root/fs/btrfs/uuid-tree.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. btrfs_uuid_to_key
  2. btrfs_uuid_tree_lookup
  3. btrfs_uuid_tree_add
  4. btrfs_uuid_tree_remove
  5. btrfs_uuid_iter_rem
  6. btrfs_uuid_tree_iterate

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

/* [<][>][^][v][top][bottom][index][help] */