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, key.objectid, key.offset, type);
 133                goto out;
 134        }
 135
 136        ret = 0;
 137        subid_le = cpu_to_le64(subid_cpu);
 138        write_extent_buffer(eb, &subid_le, offset, sizeof(subid_le));
 139        btrfs_mark_buffer_dirty(eb);
 140
 141out:
 142        btrfs_free_path(path);
 143        return ret;
 144}
 145
 146int btrfs_uuid_tree_remove(struct btrfs_trans_handle *trans, u8 *uuid, u8 type,
 147                        u64 subid)
 148{
 149        struct btrfs_fs_info *fs_info = trans->fs_info;
 150        struct btrfs_root *uuid_root = fs_info->uuid_root;
 151        int ret;
 152        struct btrfs_path *path = NULL;
 153        struct btrfs_key key;
 154        struct extent_buffer *eb;
 155        int slot;
 156        unsigned long offset;
 157        u32 item_size;
 158        unsigned long move_dst;
 159        unsigned long move_src;
 160        unsigned long move_len;
 161
 162        if (WARN_ON_ONCE(!uuid_root)) {
 163                ret = -EINVAL;
 164                goto out;
 165        }
 166
 167        btrfs_uuid_to_key(uuid, type, &key);
 168
 169        path = btrfs_alloc_path();
 170        if (!path) {
 171                ret = -ENOMEM;
 172                goto out;
 173        }
 174
 175        ret = btrfs_search_slot(trans, uuid_root, &key, path, -1, 1);
 176        if (ret < 0) {
 177                btrfs_warn(fs_info, "error %d while searching for uuid item!",
 178                           ret);
 179                goto out;
 180        }
 181        if (ret > 0) {
 182                ret = -ENOENT;
 183                goto out;
 184        }
 185
 186        eb = path->nodes[0];
 187        slot = path->slots[0];
 188        offset = btrfs_item_ptr_offset(eb, slot);
 189        item_size = btrfs_item_size_nr(eb, slot);
 190        if (!IS_ALIGNED(item_size, sizeof(u64))) {
 191                btrfs_warn(fs_info, "uuid item with illegal size %lu!",
 192                           (unsigned long)item_size);
 193                ret = -ENOENT;
 194                goto out;
 195        }
 196        while (item_size) {
 197                __le64 read_subid;
 198
 199                read_extent_buffer(eb, &read_subid, offset, sizeof(read_subid));
 200                if (le64_to_cpu(read_subid) == subid)
 201                        break;
 202                offset += sizeof(read_subid);
 203                item_size -= sizeof(read_subid);
 204        }
 205
 206        if (!item_size) {
 207                ret = -ENOENT;
 208                goto out;
 209        }
 210
 211        item_size = btrfs_item_size_nr(eb, slot);
 212        if (item_size == sizeof(subid)) {
 213                ret = btrfs_del_item(trans, uuid_root, path);
 214                goto out;
 215        }
 216
 217        move_dst = offset;
 218        move_src = offset + sizeof(subid);
 219        move_len = item_size - (move_src - btrfs_item_ptr_offset(eb, slot));
 220        memmove_extent_buffer(eb, move_dst, move_src, move_len);
 221        btrfs_truncate_item(path, item_size - sizeof(subid), 1);
 222
 223out:
 224        btrfs_free_path(path);
 225        return ret;
 226}
 227
 228static int btrfs_uuid_iter_rem(struct btrfs_root *uuid_root, u8 *uuid, u8 type,
 229                               u64 subid)
 230{
 231        struct btrfs_trans_handle *trans;
 232        int ret;
 233
 234        /* 1 - for the uuid item */
 235        trans = btrfs_start_transaction(uuid_root, 1);
 236        if (IS_ERR(trans)) {
 237                ret = PTR_ERR(trans);
 238                goto out;
 239        }
 240
 241        ret = btrfs_uuid_tree_remove(trans, uuid, type, subid);
 242        btrfs_end_transaction(trans);
 243
 244out:
 245        return ret;
 246}
 247
 248/*
 249 * Check if there's an matching subvolume for given UUID
 250 *
 251 * Return:
 252 * 0    check succeeded, the entry is not outdated
 253 * > 0  if the check failed, the caller should remove the entry
 254 * < 0  if an error occurred
 255 */
 256static int btrfs_check_uuid_tree_entry(struct btrfs_fs_info *fs_info,
 257                                       u8 *uuid, u8 type, u64 subvolid)
 258{
 259        int ret = 0;
 260        struct btrfs_root *subvol_root;
 261
 262        if (type != BTRFS_UUID_KEY_SUBVOL &&
 263            type != BTRFS_UUID_KEY_RECEIVED_SUBVOL)
 264                goto out;
 265
 266        subvol_root = btrfs_get_fs_root(fs_info, subvolid, true);
 267        if (IS_ERR(subvol_root)) {
 268                ret = PTR_ERR(subvol_root);
 269                if (ret == -ENOENT)
 270                        ret = 1;
 271                goto out;
 272        }
 273
 274        switch (type) {
 275        case BTRFS_UUID_KEY_SUBVOL:
 276                if (memcmp(uuid, subvol_root->root_item.uuid, BTRFS_UUID_SIZE))
 277                        ret = 1;
 278                break;
 279        case BTRFS_UUID_KEY_RECEIVED_SUBVOL:
 280                if (memcmp(uuid, subvol_root->root_item.received_uuid,
 281                           BTRFS_UUID_SIZE))
 282                        ret = 1;
 283                break;
 284        }
 285        btrfs_put_root(subvol_root);
 286out:
 287        return ret;
 288}
 289
 290int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info)
 291{
 292        struct btrfs_root *root = fs_info->uuid_root;
 293        struct btrfs_key key;
 294        struct btrfs_path *path;
 295        int ret = 0;
 296        struct extent_buffer *leaf;
 297        int slot;
 298        u32 item_size;
 299        unsigned long offset;
 300
 301        path = btrfs_alloc_path();
 302        if (!path) {
 303                ret = -ENOMEM;
 304                goto out;
 305        }
 306
 307        key.objectid = 0;
 308        key.type = 0;
 309        key.offset = 0;
 310
 311again_search_slot:
 312        ret = btrfs_search_forward(root, &key, path, BTRFS_OLDEST_GENERATION);
 313        if (ret) {
 314                if (ret > 0)
 315                        ret = 0;
 316                goto out;
 317        }
 318
 319        while (1) {
 320                if (btrfs_fs_closing(fs_info)) {
 321                        ret = -EINTR;
 322                        goto out;
 323                }
 324                cond_resched();
 325                leaf = path->nodes[0];
 326                slot = path->slots[0];
 327                btrfs_item_key_to_cpu(leaf, &key, slot);
 328
 329                if (key.type != BTRFS_UUID_KEY_SUBVOL &&
 330                    key.type != BTRFS_UUID_KEY_RECEIVED_SUBVOL)
 331                        goto skip;
 332
 333                offset = btrfs_item_ptr_offset(leaf, slot);
 334                item_size = btrfs_item_size_nr(leaf, slot);
 335                if (!IS_ALIGNED(item_size, sizeof(u64))) {
 336                        btrfs_warn(fs_info,
 337                                   "uuid item with illegal size %lu!",
 338                                   (unsigned long)item_size);
 339                        goto skip;
 340                }
 341                while (item_size) {
 342                        u8 uuid[BTRFS_UUID_SIZE];
 343                        __le64 subid_le;
 344                        u64 subid_cpu;
 345
 346                        put_unaligned_le64(key.objectid, uuid);
 347                        put_unaligned_le64(key.offset, uuid + sizeof(u64));
 348                        read_extent_buffer(leaf, &subid_le, offset,
 349                                           sizeof(subid_le));
 350                        subid_cpu = le64_to_cpu(subid_le);
 351                        ret = btrfs_check_uuid_tree_entry(fs_info, uuid,
 352                                                          key.type, subid_cpu);
 353                        if (ret < 0)
 354                                goto out;
 355                        if (ret > 0) {
 356                                btrfs_release_path(path);
 357                                ret = btrfs_uuid_iter_rem(root, uuid, key.type,
 358                                                          subid_cpu);
 359                                if (ret == 0) {
 360                                        /*
 361                                         * this might look inefficient, but the
 362                                         * justification is that it is an
 363                                         * exception that check_func returns 1,
 364                                         * and that in the regular case only one
 365                                         * entry per UUID exists.
 366                                         */
 367                                        goto again_search_slot;
 368                                }
 369                                if (ret < 0 && ret != -ENOENT)
 370                                        goto out;
 371                                key.offset++;
 372                                goto again_search_slot;
 373                        }
 374                        item_size -= sizeof(subid_le);
 375                        offset += sizeof(subid_le);
 376                }
 377
 378skip:
 379                ret = btrfs_next_item(root, path);
 380                if (ret == 0)
 381                        continue;
 382                else if (ret > 0)
 383                        ret = 0;
 384                break;
 385        }
 386
 387out:
 388        btrfs_free_path(path);
 389        return ret;
 390}
 391