LCOV - code coverage report
Current view: top level - fs/btrfs - uuid-tree.c (source / functions) Hit Total Coverage
Test: fstests of 6.5.0-rc3-achx @ Mon Jul 31 20:08:12 PDT 2023 Lines: 175 240 72.9 %
Date: 2023-07-31 20:08:12 Functions: 5 6 83.3 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0
       2             : /*
       3             :  * Copyright (C) STRATO AG 2013.  All rights reserved.
       4             :  */
       5             : 
       6             : #include <linux/uuid.h>
       7             : #include <asm/unaligned.h>
       8             : #include "messages.h"
       9             : #include "ctree.h"
      10             : #include "transaction.h"
      11             : #include "disk-io.h"
      12             : #include "print-tree.h"
      13             : #include "fs.h"
      14             : #include "accessors.h"
      15             : #include "uuid-tree.h"
      16             : 
      17             : static void btrfs_uuid_to_key(u8 *uuid, u8 type, struct btrfs_key *key)
      18             : {
      19        4109 :         key->type = type;
      20        4109 :         key->objectid = get_unaligned_le64(uuid);
      21        4109 :         key->offset = get_unaligned_le64(uuid + sizeof(u64));
      22             : }
      23             : 
      24             : /* return -ENOENT for !found, < 0 for errors, or 0 if an item was found */
      25        2466 : static int btrfs_uuid_tree_lookup(struct btrfs_root *uuid_root, u8 *uuid,
      26             :                                   u8 type, u64 subid)
      27             : {
      28        2466 :         int ret;
      29        2466 :         struct btrfs_path *path = NULL;
      30        2466 :         struct extent_buffer *eb;
      31        2466 :         int slot;
      32        2466 :         u32 item_size;
      33        2466 :         unsigned long offset;
      34        2466 :         struct btrfs_key key;
      35             : 
      36        2466 :         if (WARN_ON_ONCE(!uuid_root)) {
      37           0 :                 ret = -ENOENT;
      38           0 :                 goto out;
      39             :         }
      40             : 
      41        2466 :         path = btrfs_alloc_path();
      42        2466 :         if (!path) {
      43           0 :                 ret = -ENOMEM;
      44           0 :                 goto out;
      45             :         }
      46             : 
      47        2466 :         btrfs_uuid_to_key(uuid, type, &key);
      48        2466 :         ret = btrfs_search_slot(NULL, uuid_root, &key, path, 0, 0);
      49        2466 :         if (ret < 0) {
      50           0 :                 goto out;
      51        2466 :         } else if (ret > 0) {
      52        1452 :                 ret = -ENOENT;
      53        1452 :                 goto out;
      54             :         }
      55             : 
      56        1014 :         eb = path->nodes[0];
      57        1014 :         slot = path->slots[0];
      58        1014 :         item_size = btrfs_item_size(eb, slot);
      59        1014 :         offset = btrfs_item_ptr_offset(eb, slot);
      60        1014 :         ret = -ENOENT;
      61             : 
      62        1014 :         if (!IS_ALIGNED(item_size, sizeof(u64))) {
      63           0 :                 btrfs_warn(uuid_root->fs_info,
      64             :                            "uuid item with illegal size %lu!",
      65             :                            (unsigned long)item_size);
      66           0 :                 goto out;
      67             :         }
      68        1040 :         while (item_size) {
      69        1017 :                 __le64 data;
      70             : 
      71        1017 :                 read_extent_buffer(eb, &data, offset, sizeof(data));
      72        1017 :                 if (le64_to_cpu(data) == subid) {
      73         991 :                         ret = 0;
      74         991 :                         break;
      75             :                 }
      76          26 :                 offset += sizeof(data);
      77          26 :                 item_size -= sizeof(data);
      78             :         }
      79             : 
      80          23 : out:
      81        2466 :         btrfs_free_path(path);
      82        2466 :         return ret;
      83             : }
      84             : 
      85        2466 : int btrfs_uuid_tree_add(struct btrfs_trans_handle *trans, u8 *uuid, u8 type,
      86             :                         u64 subid_cpu)
      87             : {
      88        2466 :         struct btrfs_fs_info *fs_info = trans->fs_info;
      89        2466 :         struct btrfs_root *uuid_root = fs_info->uuid_root;
      90        2466 :         int ret;
      91        2466 :         struct btrfs_path *path = NULL;
      92        2466 :         struct btrfs_key key;
      93        2466 :         struct extent_buffer *eb;
      94        2466 :         int slot;
      95        2466 :         unsigned long offset;
      96        2466 :         __le64 subid_le;
      97             : 
      98        2466 :         ret = btrfs_uuid_tree_lookup(uuid_root, uuid, type, subid_cpu);
      99        2466 :         if (ret != -ENOENT)
     100             :                 return ret;
     101             : 
     102        1475 :         if (WARN_ON_ONCE(!uuid_root)) {
     103           0 :                 ret = -EINVAL;
     104           0 :                 goto out;
     105             :         }
     106             : 
     107        1475 :         btrfs_uuid_to_key(uuid, type, &key);
     108             : 
     109        1475 :         path = btrfs_alloc_path();
     110        1475 :         if (!path) {
     111           0 :                 ret = -ENOMEM;
     112           0 :                 goto out;
     113             :         }
     114             : 
     115        1475 :         ret = btrfs_insert_empty_item(trans, uuid_root, path, &key,
     116             :                                       sizeof(subid_le));
     117        1475 :         if (ret >= 0) {
     118             :                 /* Add an item for the type for the first time */
     119        1452 :                 eb = path->nodes[0];
     120        1452 :                 slot = path->slots[0];
     121        1452 :                 offset = btrfs_item_ptr_offset(eb, slot);
     122          23 :         } else if (ret == -EEXIST) {
     123             :                 /*
     124             :                  * An item with that type already exists.
     125             :                  * Extend the item and store the new subid at the end.
     126             :                  */
     127          23 :                 btrfs_extend_item(path, sizeof(subid_le));
     128          23 :                 eb = path->nodes[0];
     129          23 :                 slot = path->slots[0];
     130          23 :                 offset = btrfs_item_ptr_offset(eb, slot);
     131          23 :                 offset += btrfs_item_size(eb, slot) - sizeof(subid_le);
     132             :         } else {
     133           0 :                 btrfs_warn(fs_info,
     134             :                            "insert uuid item failed %d (0x%016llx, 0x%016llx) type %u!",
     135             :                            ret, key.objectid, key.offset, type);
     136           0 :                 goto out;
     137             :         }
     138             : 
     139        1475 :         ret = 0;
     140        1475 :         subid_le = cpu_to_le64(subid_cpu);
     141        1475 :         write_extent_buffer(eb, &subid_le, offset, sizeof(subid_le));
     142        1475 :         btrfs_mark_buffer_dirty(eb);
     143             : 
     144        1475 : out:
     145        1475 :         btrfs_free_path(path);
     146        1475 :         return ret;
     147             : }
     148             : 
     149         168 : int btrfs_uuid_tree_remove(struct btrfs_trans_handle *trans, u8 *uuid, u8 type,
     150             :                         u64 subid)
     151             : {
     152         168 :         struct btrfs_fs_info *fs_info = trans->fs_info;
     153         168 :         struct btrfs_root *uuid_root = fs_info->uuid_root;
     154         168 :         int ret;
     155         168 :         struct btrfs_path *path = NULL;
     156         168 :         struct btrfs_key key;
     157         168 :         struct extent_buffer *eb;
     158         168 :         int slot;
     159         168 :         unsigned long offset;
     160         168 :         u32 item_size;
     161         168 :         unsigned long move_dst;
     162         168 :         unsigned long move_src;
     163         168 :         unsigned long move_len;
     164             : 
     165         168 :         if (WARN_ON_ONCE(!uuid_root)) {
     166           0 :                 ret = -EINVAL;
     167           0 :                 goto out;
     168             :         }
     169             : 
     170         168 :         btrfs_uuid_to_key(uuid, type, &key);
     171             : 
     172         168 :         path = btrfs_alloc_path();
     173         168 :         if (!path) {
     174           0 :                 ret = -ENOMEM;
     175           0 :                 goto out;
     176             :         }
     177             : 
     178         168 :         ret = btrfs_search_slot(trans, uuid_root, &key, path, -1, 1);
     179         168 :         if (ret < 0) {
     180           0 :                 btrfs_warn(fs_info, "error %d while searching for uuid item!",
     181             :                            ret);
     182           0 :                 goto out;
     183             :         }
     184         168 :         if (ret > 0) {
     185           1 :                 ret = -ENOENT;
     186           1 :                 goto out;
     187             :         }
     188             : 
     189         167 :         eb = path->nodes[0];
     190         167 :         slot = path->slots[0];
     191         167 :         offset = btrfs_item_ptr_offset(eb, slot);
     192         167 :         item_size = btrfs_item_size(eb, slot);
     193         167 :         if (!IS_ALIGNED(item_size, sizeof(u64))) {
     194           0 :                 btrfs_warn(fs_info, "uuid item with illegal size %lu!",
     195             :                            (unsigned long)item_size);
     196           0 :                 ret = -ENOENT;
     197           0 :                 goto out;
     198             :         }
     199         167 :         while (item_size) {
     200         167 :                 __le64 read_subid;
     201             : 
     202         167 :                 read_extent_buffer(eb, &read_subid, offset, sizeof(read_subid));
     203         167 :                 if (le64_to_cpu(read_subid) == subid)
     204             :                         break;
     205           0 :                 offset += sizeof(read_subid);
     206           0 :                 item_size -= sizeof(read_subid);
     207             :         }
     208             : 
     209         167 :         if (!item_size) {
     210           0 :                 ret = -ENOENT;
     211           0 :                 goto out;
     212             :         }
     213             : 
     214         167 :         item_size = btrfs_item_size(eb, slot);
     215         167 :         if (item_size == sizeof(subid)) {
     216         167 :                 ret = btrfs_del_item(trans, uuid_root, path);
     217         167 :                 goto out;
     218             :         }
     219             : 
     220           0 :         move_dst = offset;
     221           0 :         move_src = offset + sizeof(subid);
     222           0 :         move_len = item_size - (move_src - btrfs_item_ptr_offset(eb, slot));
     223           0 :         memmove_extent_buffer(eb, move_dst, move_src, move_len);
     224           0 :         btrfs_truncate_item(path, item_size - sizeof(subid), 1);
     225             : 
     226         168 : out:
     227         168 :         btrfs_free_path(path);
     228         168 :         return ret;
     229             : }
     230             : 
     231           0 : static int btrfs_uuid_iter_rem(struct btrfs_root *uuid_root, u8 *uuid, u8 type,
     232             :                                u64 subid)
     233             : {
     234           0 :         struct btrfs_trans_handle *trans;
     235           0 :         int ret;
     236             : 
     237             :         /* 1 - for the uuid item */
     238           0 :         trans = btrfs_start_transaction(uuid_root, 1);
     239           0 :         if (IS_ERR(trans)) {
     240           0 :                 ret = PTR_ERR(trans);
     241           0 :                 goto out;
     242             :         }
     243             : 
     244           0 :         ret = btrfs_uuid_tree_remove(trans, uuid, type, subid);
     245           0 :         btrfs_end_transaction(trans);
     246             : 
     247           0 : out:
     248           0 :         return ret;
     249             : }
     250             : 
     251             : /*
     252             :  * Check if there's an matching subvolume for given UUID
     253             :  *
     254             :  * Return:
     255             :  * 0    check succeeded, the entry is not outdated
     256             :  * > 0       if the check failed, the caller should remove the entry
     257             :  * < 0       if an error occurred
     258             :  */
     259         991 : static int btrfs_check_uuid_tree_entry(struct btrfs_fs_info *fs_info,
     260             :                                        u8 *uuid, u8 type, u64 subvolid)
     261             : {
     262         991 :         int ret = 0;
     263         991 :         struct btrfs_root *subvol_root;
     264             : 
     265         991 :         if (type != BTRFS_UUID_KEY_SUBVOL &&
     266             :             type != BTRFS_UUID_KEY_RECEIVED_SUBVOL)
     267           0 :                 goto out;
     268             : 
     269         991 :         subvol_root = btrfs_get_fs_root(fs_info, subvolid, true);
     270         991 :         if (IS_ERR(subvol_root)) {
     271           0 :                 ret = PTR_ERR(subvol_root);
     272           0 :                 if (ret == -ENOENT)
     273           0 :                         ret = 1;
     274           0 :                 goto out;
     275             :         }
     276             : 
     277         991 :         switch (type) {
     278         991 :         case BTRFS_UUID_KEY_SUBVOL:
     279        1982 :                 if (memcmp(uuid, subvol_root->root_item.uuid, BTRFS_UUID_SIZE))
     280           0 :                         ret = 1;
     281             :                 break;
     282           0 :         case BTRFS_UUID_KEY_RECEIVED_SUBVOL:
     283           0 :                 if (memcmp(uuid, subvol_root->root_item.received_uuid,
     284             :                            BTRFS_UUID_SIZE))
     285           0 :                         ret = 1;
     286             :                 break;
     287             :         }
     288         991 :         btrfs_put_root(subvol_root);
     289         991 : out:
     290         991 :         return ret;
     291             : }
     292             : 
     293         993 : int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info)
     294             : {
     295         993 :         struct btrfs_root *root = fs_info->uuid_root;
     296         993 :         struct btrfs_key key;
     297         993 :         struct btrfs_path *path;
     298         993 :         int ret = 0;
     299         993 :         struct extent_buffer *leaf;
     300         993 :         int slot;
     301         993 :         u32 item_size;
     302         993 :         unsigned long offset;
     303             : 
     304         993 :         path = btrfs_alloc_path();
     305         993 :         if (!path) {
     306           0 :                 ret = -ENOMEM;
     307           0 :                 goto out;
     308             :         }
     309             : 
     310         993 :         key.objectid = 0;
     311         993 :         key.type = 0;
     312         993 :         key.offset = 0;
     313             : 
     314         993 : again_search_slot:
     315         993 :         ret = btrfs_search_forward(root, &key, path, BTRFS_OLDEST_GENERATION);
     316         993 :         if (ret) {
     317           3 :                 if (ret > 0)
     318             :                         ret = 0;
     319           3 :                 goto out;
     320             :         }
     321             : 
     322         994 :         while (1) {
     323         992 :                 if (btrfs_fs_closing(fs_info)) {
     324           1 :                         ret = -EINTR;
     325           1 :                         goto out;
     326             :                 }
     327         991 :                 cond_resched();
     328         991 :                 leaf = path->nodes[0];
     329         991 :                 slot = path->slots[0];
     330         991 :                 btrfs_item_key_to_cpu(leaf, &key, slot);
     331             : 
     332         991 :                 if (key.type != BTRFS_UUID_KEY_SUBVOL &&
     333             :                     key.type != BTRFS_UUID_KEY_RECEIVED_SUBVOL)
     334           0 :                         goto skip;
     335             : 
     336         991 :                 offset = btrfs_item_ptr_offset(leaf, slot);
     337         991 :                 item_size = btrfs_item_size(leaf, slot);
     338         991 :                 if (!IS_ALIGNED(item_size, sizeof(u64))) {
     339           0 :                         btrfs_warn(fs_info,
     340             :                                    "uuid item with illegal size %lu!",
     341             :                                    (unsigned long)item_size);
     342           0 :                         goto skip;
     343             :                 }
     344        1982 :                 while (item_size) {
     345         991 :                         u8 uuid[BTRFS_UUID_SIZE];
     346         991 :                         __le64 subid_le;
     347         991 :                         u64 subid_cpu;
     348             : 
     349         991 :                         put_unaligned_le64(key.objectid, uuid);
     350         991 :                         put_unaligned_le64(key.offset, uuid + sizeof(u64));
     351         991 :                         read_extent_buffer(leaf, &subid_le, offset,
     352             :                                            sizeof(subid_le));
     353         991 :                         subid_cpu = le64_to_cpu(subid_le);
     354         991 :                         ret = btrfs_check_uuid_tree_entry(fs_info, uuid,
     355         991 :                                                           key.type, subid_cpu);
     356         991 :                         if (ret < 0)
     357           0 :                                 goto out;
     358         991 :                         if (ret > 0) {
     359           0 :                                 btrfs_release_path(path);
     360           0 :                                 ret = btrfs_uuid_iter_rem(root, uuid, key.type,
     361             :                                                           subid_cpu);
     362           0 :                                 if (ret == 0) {
     363             :                                         /*
     364             :                                          * this might look inefficient, but the
     365             :                                          * justification is that it is an
     366             :                                          * exception that check_func returns 1,
     367             :                                          * and that in the regular case only one
     368             :                                          * entry per UUID exists.
     369             :                                          */
     370           0 :                                         goto again_search_slot;
     371             :                                 }
     372           0 :                                 if (ret < 0 && ret != -ENOENT)
     373           0 :                                         goto out;
     374           0 :                                 key.offset++;
     375           0 :                                 goto again_search_slot;
     376             :                         }
     377         991 :                         item_size -= sizeof(subid_le);
     378         991 :                         offset += sizeof(subid_le);
     379             :                 }
     380             : 
     381         991 : skip:
     382         991 :                 ret = btrfs_next_item(root, path);
     383         991 :                 if (ret == 0)
     384           2 :                         continue;
     385         989 :                 else if (ret > 0)
     386             :                         ret = 0;
     387             :                 break;
     388             :         }
     389             : 
     390         993 : out:
     391         993 :         btrfs_free_path(path);
     392         993 :         return ret;
     393             : }

Generated by: LCOV version 1.14