uuid-tree.c (8622B)
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(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(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(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(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(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}