1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) STRATO AG 2013. All rights reserved.
6 #include <linux/uuid.h>
7 #include <asm/unaligned.h>
10 #include "transaction.h"
12 #include "print-tree.h"
14 #include "accessors.h"
15 #include "uuid-tree.h"
17 static void btrfs_uuid_to_key(u8 *uuid, u8 type, struct btrfs_key *key)
20 key->objectid = get_unaligned_le64(uuid);
21 key->offset = get_unaligned_le64(uuid + sizeof(u64));
24 /* return -ENOENT for !found, < 0 for errors, or 0 if an item was found */
25 static int btrfs_uuid_tree_lookup(struct btrfs_root *uuid_root, u8 *uuid,
29 struct btrfs_path *path = NULL;
30 struct extent_buffer *eb;
36 if (WARN_ON_ONCE(!uuid_root)) {
41 path = btrfs_alloc_path();
47 btrfs_uuid_to_key(uuid, type, &key);
48 ret = btrfs_search_slot(NULL, uuid_root, &key, path, 0, 0);
57 slot = path->slots[0];
58 item_size = btrfs_item_size(eb, slot);
59 offset = btrfs_item_ptr_offset(eb, slot);
62 if (!IS_ALIGNED(item_size, sizeof(u64))) {
63 btrfs_warn(uuid_root->fs_info,
64 "uuid item with illegal size %lu!",
65 (unsigned long)item_size);
71 read_extent_buffer(eb, &data, offset, sizeof(data));
72 if (le64_to_cpu(data) == subid) {
76 offset += sizeof(data);
77 item_size -= sizeof(data);
81 btrfs_free_path(path);
85 int btrfs_uuid_tree_add(struct btrfs_trans_handle *trans, u8 *uuid, u8 type,
88 struct btrfs_fs_info *fs_info = trans->fs_info;
89 struct btrfs_root *uuid_root = fs_info->uuid_root;
91 struct btrfs_path *path = NULL;
93 struct extent_buffer *eb;
98 ret = btrfs_uuid_tree_lookup(uuid_root, uuid, type, subid_cpu);
102 if (WARN_ON_ONCE(!uuid_root)) {
107 btrfs_uuid_to_key(uuid, type, &key);
109 path = btrfs_alloc_path();
115 ret = btrfs_insert_empty_item(trans, uuid_root, path, &key,
118 /* Add an item for the type for the first time */
120 slot = path->slots[0];
121 offset = btrfs_item_ptr_offset(eb, slot);
122 } else if (ret == -EEXIST) {
124 * An item with that type already exists.
125 * Extend the item and store the new subid at the end.
127 btrfs_extend_item(path, sizeof(subid_le));
129 slot = path->slots[0];
130 offset = btrfs_item_ptr_offset(eb, slot);
131 offset += btrfs_item_size(eb, slot) - sizeof(subid_le);
134 "insert uuid item failed %d (0x%016llx, 0x%016llx) type %u!",
135 ret, key.objectid, key.offset, type);
140 subid_le = cpu_to_le64(subid_cpu);
141 write_extent_buffer(eb, &subid_le, offset, sizeof(subid_le));
142 btrfs_mark_buffer_dirty(eb);
145 btrfs_free_path(path);
149 int btrfs_uuid_tree_remove(struct btrfs_trans_handle *trans, u8 *uuid, u8 type,
152 struct btrfs_fs_info *fs_info = trans->fs_info;
153 struct btrfs_root *uuid_root = fs_info->uuid_root;
155 struct btrfs_path *path = NULL;
156 struct btrfs_key key;
157 struct extent_buffer *eb;
159 unsigned long offset;
161 unsigned long move_dst;
162 unsigned long move_src;
163 unsigned long move_len;
165 if (WARN_ON_ONCE(!uuid_root)) {
170 btrfs_uuid_to_key(uuid, type, &key);
172 path = btrfs_alloc_path();
178 ret = btrfs_search_slot(trans, uuid_root, &key, path, -1, 1);
180 btrfs_warn(fs_info, "error %d while searching for uuid item!",
190 slot = path->slots[0];
191 offset = btrfs_item_ptr_offset(eb, slot);
192 item_size = btrfs_item_size(eb, slot);
193 if (!IS_ALIGNED(item_size, sizeof(u64))) {
194 btrfs_warn(fs_info, "uuid item with illegal size %lu!",
195 (unsigned long)item_size);
202 read_extent_buffer(eb, &read_subid, offset, sizeof(read_subid));
203 if (le64_to_cpu(read_subid) == subid)
205 offset += sizeof(read_subid);
206 item_size -= sizeof(read_subid);
214 item_size = btrfs_item_size(eb, slot);
215 if (item_size == sizeof(subid)) {
216 ret = btrfs_del_item(trans, uuid_root, path);
221 move_src = offset + sizeof(subid);
222 move_len = item_size - (move_src - btrfs_item_ptr_offset(eb, slot));
223 memmove_extent_buffer(eb, move_dst, move_src, move_len);
224 btrfs_truncate_item(path, item_size - sizeof(subid), 1);
227 btrfs_free_path(path);
231 static int btrfs_uuid_iter_rem(struct btrfs_root *uuid_root, u8 *uuid, u8 type,
234 struct btrfs_trans_handle *trans;
237 /* 1 - for the uuid item */
238 trans = btrfs_start_transaction(uuid_root, 1);
240 ret = PTR_ERR(trans);
244 ret = btrfs_uuid_tree_remove(trans, uuid, type, subid);
245 btrfs_end_transaction(trans);
252 * Check if there's an matching subvolume for given UUID
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
259 static int btrfs_check_uuid_tree_entry(struct btrfs_fs_info *fs_info,
260 u8 *uuid, u8 type, u64 subvolid)
263 struct btrfs_root *subvol_root;
265 if (type != BTRFS_UUID_KEY_SUBVOL &&
266 type != BTRFS_UUID_KEY_RECEIVED_SUBVOL)
269 subvol_root = btrfs_get_fs_root(fs_info, subvolid, true);
270 if (IS_ERR(subvol_root)) {
271 ret = PTR_ERR(subvol_root);
278 case BTRFS_UUID_KEY_SUBVOL:
279 if (memcmp(uuid, subvol_root->root_item.uuid, BTRFS_UUID_SIZE))
282 case BTRFS_UUID_KEY_RECEIVED_SUBVOL:
283 if (memcmp(uuid, subvol_root->root_item.received_uuid,
288 btrfs_put_root(subvol_root);
293 int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info)
295 struct btrfs_root *root = fs_info->uuid_root;
296 struct btrfs_key key;
297 struct btrfs_path *path;
299 struct extent_buffer *leaf;
302 unsigned long offset;
304 path = btrfs_alloc_path();
315 ret = btrfs_search_forward(root, &key, path, BTRFS_OLDEST_GENERATION);
323 if (btrfs_fs_closing(fs_info)) {
328 leaf = path->nodes[0];
329 slot = path->slots[0];
330 btrfs_item_key_to_cpu(leaf, &key, slot);
332 if (key.type != BTRFS_UUID_KEY_SUBVOL &&
333 key.type != BTRFS_UUID_KEY_RECEIVED_SUBVOL)
336 offset = btrfs_item_ptr_offset(leaf, slot);
337 item_size = btrfs_item_size(leaf, slot);
338 if (!IS_ALIGNED(item_size, sizeof(u64))) {
340 "uuid item with illegal size %lu!",
341 (unsigned long)item_size);
345 u8 uuid[BTRFS_UUID_SIZE];
349 put_unaligned_le64(key.objectid, uuid);
350 put_unaligned_le64(key.offset, uuid + sizeof(u64));
351 read_extent_buffer(leaf, &subid_le, offset,
353 subid_cpu = le64_to_cpu(subid_le);
354 ret = btrfs_check_uuid_tree_entry(fs_info, uuid,
355 key.type, subid_cpu);
359 btrfs_release_path(path);
360 ret = btrfs_uuid_iter_rem(root, uuid, key.type,
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.
370 goto again_search_slot;
372 if (ret < 0 && ret != -ENOENT)
375 goto again_search_slot;
377 item_size -= sizeof(subid_le);
378 offset += sizeof(subid_le);
382 ret = btrfs_next_item(root, path);
391 btrfs_free_path(path);