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 14 static 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 */ 22 static 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 77 out: 78 btrfs_free_path(path); 79 return ret; 80 } 81 82 int 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 142 out: 143 btrfs_free_path(path); 144 return ret; 145 } 146 147 int 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 224 out: 225 btrfs_free_path(path); 226 return ret; 227 } 228 229 static 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 245 out: 246 return ret; 247 } 248 249 /* 250 * Check if there's an matching subvolume for given UUID 251 * 252 * Return: 253 * 0 check succeeded, the entry is not outdated 254 * > 0 if the check failed, the caller should remove the entry 255 * < 0 if an error occurred 256 */ 257 static int btrfs_check_uuid_tree_entry(struct btrfs_fs_info *fs_info, 258 u8 *uuid, u8 type, u64 subvolid) 259 { 260 int ret = 0; 261 struct btrfs_root *subvol_root; 262 263 if (type != BTRFS_UUID_KEY_SUBVOL && 264 type != BTRFS_UUID_KEY_RECEIVED_SUBVOL) 265 goto out; 266 267 subvol_root = btrfs_get_fs_root(fs_info, subvolid, true); 268 if (IS_ERR(subvol_root)) { 269 ret = PTR_ERR(subvol_root); 270 if (ret == -ENOENT) 271 ret = 1; 272 goto out; 273 } 274 275 switch (type) { 276 case BTRFS_UUID_KEY_SUBVOL: 277 if (memcmp(uuid, subvol_root->root_item.uuid, BTRFS_UUID_SIZE)) 278 ret = 1; 279 break; 280 case BTRFS_UUID_KEY_RECEIVED_SUBVOL: 281 if (memcmp(uuid, subvol_root->root_item.received_uuid, 282 BTRFS_UUID_SIZE)) 283 ret = 1; 284 break; 285 } 286 btrfs_put_root(subvol_root); 287 out: 288 return ret; 289 } 290 291 int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info) 292 { 293 struct btrfs_root *root = fs_info->uuid_root; 294 struct btrfs_key key; 295 struct btrfs_path *path; 296 int ret = 0; 297 struct extent_buffer *leaf; 298 int slot; 299 u32 item_size; 300 unsigned long offset; 301 302 path = btrfs_alloc_path(); 303 if (!path) { 304 ret = -ENOMEM; 305 goto out; 306 } 307 308 key.objectid = 0; 309 key.type = 0; 310 key.offset = 0; 311 312 again_search_slot: 313 ret = btrfs_search_forward(root, &key, path, BTRFS_OLDEST_GENERATION); 314 if (ret) { 315 if (ret > 0) 316 ret = 0; 317 goto out; 318 } 319 320 while (1) { 321 if (btrfs_fs_closing(fs_info)) { 322 ret = -EINTR; 323 goto out; 324 } 325 cond_resched(); 326 leaf = path->nodes[0]; 327 slot = path->slots[0]; 328 btrfs_item_key_to_cpu(leaf, &key, slot); 329 330 if (key.type != BTRFS_UUID_KEY_SUBVOL && 331 key.type != BTRFS_UUID_KEY_RECEIVED_SUBVOL) 332 goto skip; 333 334 offset = btrfs_item_ptr_offset(leaf, slot); 335 item_size = btrfs_item_size_nr(leaf, slot); 336 if (!IS_ALIGNED(item_size, sizeof(u64))) { 337 btrfs_warn(fs_info, 338 "uuid item with illegal size %lu!", 339 (unsigned long)item_size); 340 goto skip; 341 } 342 while (item_size) { 343 u8 uuid[BTRFS_UUID_SIZE]; 344 __le64 subid_le; 345 u64 subid_cpu; 346 347 put_unaligned_le64(key.objectid, uuid); 348 put_unaligned_le64(key.offset, uuid + sizeof(u64)); 349 read_extent_buffer(leaf, &subid_le, offset, 350 sizeof(subid_le)); 351 subid_cpu = le64_to_cpu(subid_le); 352 ret = btrfs_check_uuid_tree_entry(fs_info, uuid, 353 key.type, subid_cpu); 354 if (ret < 0) 355 goto out; 356 if (ret > 0) { 357 btrfs_release_path(path); 358 ret = btrfs_uuid_iter_rem(root, uuid, key.type, 359 subid_cpu); 360 if (ret == 0) { 361 /* 362 * this might look inefficient, but the 363 * justification is that it is an 364 * exception that check_func returns 1, 365 * and that in the regular case only one 366 * entry per UUID exists. 367 */ 368 goto again_search_slot; 369 } 370 if (ret < 0 && ret != -ENOENT) 371 goto out; 372 key.offset++; 373 goto again_search_slot; 374 } 375 item_size -= sizeof(subid_le); 376 offset += sizeof(subid_le); 377 } 378 379 skip: 380 ret = btrfs_next_item(root, path); 381 if (ret == 0) 382 continue; 383 else if (ret > 0) 384 ret = 0; 385 break; 386 } 387 388 out: 389 btrfs_free_path(path); 390 return ret; 391 } 392