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-djwa @ Mon Jul 31 20:08:17 PDT 2023 Lines: 0 239 0.0 %
Date: 2023-07-31 20:08:17 Functions: 0 6 0.0 %

          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           0 :         key->type = type;
      20           0 :         key->objectid = get_unaligned_le64(uuid);
      21           0 :         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           0 : static int btrfs_uuid_tree_lookup(struct btrfs_root *uuid_root, u8 *uuid,
      26             :                                   u8 type, u64 subid)
      27             : {
      28           0 :         int ret;
      29           0 :         struct btrfs_path *path = NULL;
      30           0 :         struct extent_buffer *eb;
      31           0 :         int slot;
      32           0 :         u32 item_size;
      33           0 :         unsigned long offset;
      34           0 :         struct btrfs_key key;
      35             : 
      36           0 :         if (WARN_ON_ONCE(!uuid_root)) {
      37           0 :                 ret = -ENOENT;
      38           0 :                 goto out;
      39             :         }
      40             : 
      41           0 :         path = btrfs_alloc_path();
      42           0 :         if (!path) {
      43           0 :                 ret = -ENOMEM;
      44           0 :                 goto out;
      45             :         }
      46             : 
      47           0 :         btrfs_uuid_to_key(uuid, type, &key);
      48           0 :         ret = btrfs_search_slot(NULL, uuid_root, &key, path, 0, 0);
      49           0 :         if (ret < 0) {
      50           0 :                 goto out;
      51           0 :         } else if (ret > 0) {
      52           0 :                 ret = -ENOENT;
      53           0 :                 goto out;
      54             :         }
      55             : 
      56           0 :         eb = path->nodes[0];
      57           0 :         slot = path->slots[0];
      58           0 :         item_size = btrfs_item_size(eb, slot);
      59           0 :         offset = btrfs_item_ptr_offset(eb, slot);
      60           0 :         ret = -ENOENT;
      61             : 
      62           0 :         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           0 :         while (item_size) {
      69           0 :                 __le64 data;
      70             : 
      71           0 :                 read_extent_buffer(eb, &data, offset, sizeof(data));
      72           0 :                 if (le64_to_cpu(data) == subid) {
      73           0 :                         ret = 0;
      74           0 :                         break;
      75             :                 }
      76           0 :                 offset += sizeof(data);
      77           0 :                 item_size -= sizeof(data);
      78             :         }
      79             : 
      80           0 : out:
      81           0 :         btrfs_free_path(path);
      82           0 :         return ret;
      83             : }
      84             : 
      85           0 : int btrfs_uuid_tree_add(struct btrfs_trans_handle *trans, u8 *uuid, u8 type,
      86             :                         u64 subid_cpu)
      87             : {
      88           0 :         struct btrfs_fs_info *fs_info = trans->fs_info;
      89           0 :         struct btrfs_root *uuid_root = fs_info->uuid_root;
      90           0 :         int ret;
      91           0 :         struct btrfs_path *path = NULL;
      92           0 :         struct btrfs_key key;
      93           0 :         struct extent_buffer *eb;
      94           0 :         int slot;
      95           0 :         unsigned long offset;
      96           0 :         __le64 subid_le;
      97             : 
      98           0 :         ret = btrfs_uuid_tree_lookup(uuid_root, uuid, type, subid_cpu);
      99           0 :         if (ret != -ENOENT)
     100             :                 return ret;
     101             : 
     102           0 :         if (WARN_ON_ONCE(!uuid_root)) {
     103           0 :                 ret = -EINVAL;
     104           0 :                 goto out;
     105             :         }
     106             : 
     107           0 :         btrfs_uuid_to_key(uuid, type, &key);
     108             : 
     109           0 :         path = btrfs_alloc_path();
     110           0 :         if (!path) {
     111           0 :                 ret = -ENOMEM;
     112           0 :                 goto out;
     113             :         }
     114             : 
     115           0 :         ret = btrfs_insert_empty_item(trans, uuid_root, path, &key,
     116             :                                       sizeof(subid_le));
     117           0 :         if (ret >= 0) {
     118             :                 /* Add an item for the type for the first time */
     119           0 :                 eb = path->nodes[0];
     120           0 :                 slot = path->slots[0];
     121           0 :                 offset = btrfs_item_ptr_offset(eb, slot);
     122           0 :         } 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           0 :                 btrfs_extend_item(path, sizeof(subid_le));
     128           0 :                 eb = path->nodes[0];
     129           0 :                 slot = path->slots[0];
     130           0 :                 offset = btrfs_item_ptr_offset(eb, slot);
     131           0 :                 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           0 :         ret = 0;
     140           0 :         subid_le = cpu_to_le64(subid_cpu);
     141           0 :         write_extent_buffer(eb, &subid_le, offset, sizeof(subid_le));
     142           0 :         btrfs_mark_buffer_dirty(eb);
     143             : 
     144           0 : out:
     145           0 :         btrfs_free_path(path);
     146           0 :         return ret;
     147             : }
     148             : 
     149           0 : int btrfs_uuid_tree_remove(struct btrfs_trans_handle *trans, u8 *uuid, u8 type,
     150             :                         u64 subid)
     151             : {
     152           0 :         struct btrfs_fs_info *fs_info = trans->fs_info;
     153           0 :         struct btrfs_root *uuid_root = fs_info->uuid_root;
     154           0 :         int ret;
     155           0 :         struct btrfs_path *path = NULL;
     156           0 :         struct btrfs_key key;
     157           0 :         struct extent_buffer *eb;
     158           0 :         int slot;
     159           0 :         unsigned long offset;
     160           0 :         u32 item_size;
     161           0 :         unsigned long move_dst;
     162           0 :         unsigned long move_src;
     163           0 :         unsigned long move_len;
     164             : 
     165           0 :         if (WARN_ON_ONCE(!uuid_root)) {
     166           0 :                 ret = -EINVAL;
     167           0 :                 goto out;
     168             :         }
     169             : 
     170           0 :         btrfs_uuid_to_key(uuid, type, &key);
     171             : 
     172           0 :         path = btrfs_alloc_path();
     173           0 :         if (!path) {
     174           0 :                 ret = -ENOMEM;
     175           0 :                 goto out;
     176             :         }
     177             : 
     178           0 :         ret = btrfs_search_slot(trans, uuid_root, &key, path, -1, 1);
     179           0 :         if (ret < 0) {
     180           0 :                 btrfs_warn(fs_info, "error %d while searching for uuid item!",
     181             :                            ret);
     182           0 :                 goto out;
     183             :         }
     184           0 :         if (ret > 0) {
     185           0 :                 ret = -ENOENT;
     186           0 :                 goto out;
     187             :         }
     188             : 
     189           0 :         eb = path->nodes[0];
     190           0 :         slot = path->slots[0];
     191           0 :         offset = btrfs_item_ptr_offset(eb, slot);
     192           0 :         item_size = btrfs_item_size(eb, slot);
     193           0 :         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           0 :         while (item_size) {
     200           0 :                 __le64 read_subid;
     201             : 
     202           0 :                 read_extent_buffer(eb, &read_subid, offset, sizeof(read_subid));
     203           0 :                 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           0 :         if (!item_size) {
     210           0 :                 ret = -ENOENT;
     211           0 :                 goto out;
     212             :         }
     213             : 
     214           0 :         item_size = btrfs_item_size(eb, slot);
     215           0 :         if (item_size == sizeof(subid)) {
     216           0 :                 ret = btrfs_del_item(trans, uuid_root, path);
     217           0 :                 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           0 : out:
     227           0 :         btrfs_free_path(path);
     228           0 :         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           0 : static int btrfs_check_uuid_tree_entry(struct btrfs_fs_info *fs_info,
     260             :                                        u8 *uuid, u8 type, u64 subvolid)
     261             : {
     262           0 :         int ret = 0;
     263           0 :         struct btrfs_root *subvol_root;
     264             : 
     265           0 :         if (type != BTRFS_UUID_KEY_SUBVOL &&
     266             :             type != BTRFS_UUID_KEY_RECEIVED_SUBVOL)
     267           0 :                 goto out;
     268             : 
     269           0 :         subvol_root = btrfs_get_fs_root(fs_info, subvolid, true);
     270           0 :         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           0 :         switch (type) {
     278           0 :         case BTRFS_UUID_KEY_SUBVOL:
     279           0 :                 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           0 :         btrfs_put_root(subvol_root);
     289           0 : out:
     290           0 :         return ret;
     291             : }
     292             : 
     293           0 : int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info)
     294             : {
     295           0 :         struct btrfs_root *root = fs_info->uuid_root;
     296           0 :         struct btrfs_key key;
     297           0 :         struct btrfs_path *path;
     298           0 :         int ret = 0;
     299           0 :         struct extent_buffer *leaf;
     300           0 :         int slot;
     301           0 :         u32 item_size;
     302           0 :         unsigned long offset;
     303             : 
     304           0 :         path = btrfs_alloc_path();
     305           0 :         if (!path) {
     306           0 :                 ret = -ENOMEM;
     307           0 :                 goto out;
     308             :         }
     309             : 
     310           0 :         key.objectid = 0;
     311           0 :         key.type = 0;
     312           0 :         key.offset = 0;
     313             : 
     314           0 : again_search_slot:
     315           0 :         ret = btrfs_search_forward(root, &key, path, BTRFS_OLDEST_GENERATION);
     316           0 :         if (ret) {
     317           0 :                 if (ret > 0)
     318             :                         ret = 0;
     319           0 :                 goto out;
     320             :         }
     321             : 
     322           0 :         while (1) {
     323           0 :                 if (btrfs_fs_closing(fs_info)) {
     324           0 :                         ret = -EINTR;
     325           0 :                         goto out;
     326             :                 }
     327           0 :                 cond_resched();
     328           0 :                 leaf = path->nodes[0];
     329           0 :                 slot = path->slots[0];
     330           0 :                 btrfs_item_key_to_cpu(leaf, &key, slot);
     331             : 
     332           0 :                 if (key.type != BTRFS_UUID_KEY_SUBVOL &&
     333             :                     key.type != BTRFS_UUID_KEY_RECEIVED_SUBVOL)
     334           0 :                         goto skip;
     335             : 
     336           0 :                 offset = btrfs_item_ptr_offset(leaf, slot);
     337           0 :                 item_size = btrfs_item_size(leaf, slot);
     338           0 :                 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           0 :                 while (item_size) {
     345           0 :                         u8 uuid[BTRFS_UUID_SIZE];
     346           0 :                         __le64 subid_le;
     347           0 :                         u64 subid_cpu;
     348             : 
     349           0 :                         put_unaligned_le64(key.objectid, uuid);
     350           0 :                         put_unaligned_le64(key.offset, uuid + sizeof(u64));
     351           0 :                         read_extent_buffer(leaf, &subid_le, offset,
     352             :                                            sizeof(subid_le));
     353           0 :                         subid_cpu = le64_to_cpu(subid_le);
     354           0 :                         ret = btrfs_check_uuid_tree_entry(fs_info, uuid,
     355             :                                                           key.type, subid_cpu);
     356           0 :                         if (ret < 0)
     357           0 :                                 goto out;
     358           0 :                         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           0 :                         item_size -= sizeof(subid_le);
     378           0 :                         offset += sizeof(subid_le);
     379             :                 }
     380             : 
     381           0 : skip:
     382           0 :                 ret = btrfs_next_item(root, path);
     383           0 :                 if (ret == 0)
     384           0 :                         continue;
     385           0 :                 else if (ret > 0)
     386             :                         ret = 0;
     387             :                 break;
     388             :         }
     389             : 
     390           0 : out:
     391           0 :         btrfs_free_path(path);
     392           0 :         return ret;
     393             : }

Generated by: LCOV version 1.14