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