linux/fs/btrfs/uuid-tree.c
<<
>>
Prefs
   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
  14static 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 */
  22static 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
  77out:
  78        btrfs_free_path(path);
  79        return ret;
  80}
  81
  82int 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
 142out:
 143        btrfs_free_path(path);
 144        return ret;
 145}
 146
 147int 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
 224out:
 225        btrfs_free_path(path);
 226        return ret;
 227}
 228
 229static 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
 245out:
 246        return ret;
 247}
 248
 249int 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
 272again_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                        }
 328                        item_size -= sizeof(subid_le);
 329                        offset += sizeof(subid_le);
 330                }
 331
 332skip:
 333                ret = btrfs_next_item(root, path);
 334                if (ret == 0)
 335                        continue;
 336                else if (ret > 0)
 337                        ret = 0;
 338                break;
 339        }
 340
 341out:
 342        btrfs_free_path(path);
 343        return ret;
 344}
 345