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 "messages.h" 9 #include "ctree.h" 10 #include "transaction.h" 11 #include "disk-io.h" 12 #include "print-tree.h" 13 #include "fs.h" 14 #include "accessors.h" 15 #include "uuid-tree.h" 16 17 static void btrfs_uuid_to_key(u8 *uuid, u8 type, struct btrfs_key *key) 18 { 19 key->type = type; 20 key->objectid = get_unaligned_le64(uuid); 21 key->offset = get_unaligned_le64(uuid + sizeof(u64)); 22 } 23 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, 26 u8 type, u64 subid) 27 { 28 int ret; 29 struct btrfs_path *path = NULL; 30 struct extent_buffer *eb; 31 int slot; 32 u32 item_size; 33 unsigned long offset; 34 struct btrfs_key key; 35 36 if (WARN_ON_ONCE(!uuid_root)) { 37 ret = -ENOENT; 38 goto out; 39 } 40 41 path = btrfs_alloc_path(); 42 if (!path) { 43 ret = -ENOMEM; 44 goto out; 45 } 46 47 btrfs_uuid_to_key(uuid, type, &key); 48 ret = btrfs_search_slot(NULL, uuid_root, &key, path, 0, 0); 49 if (ret < 0) { 50 goto out; 51 } else if (ret > 0) { 52 ret = -ENOENT; 53 goto out; 54 } 55 56 eb = path->nodes[0]; 57 slot = path->slots[0]; 58 item_size = btrfs_item_size(eb, slot); 59 offset = btrfs_item_ptr_offset(eb, slot); 60 ret = -ENOENT; 61 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); 66 goto out; 67 } 68 while (item_size) { 69 __le64 data; 70 71 read_extent_buffer(eb, &data, offset, sizeof(data)); 72 if (le64_to_cpu(data) == subid) { 73 ret = 0; 74 break; 75 } 76 offset += sizeof(data); 77 item_size -= sizeof(data); 78 } 79 80 out: 81 btrfs_free_path(path); 82 return ret; 83 } 84 85 int btrfs_uuid_tree_add(struct btrfs_trans_handle *trans, u8 *uuid, u8 type, 86 u64 subid_cpu) 87 { 88 struct btrfs_fs_info *fs_info = trans->fs_info; 89 struct btrfs_root *uuid_root = fs_info->uuid_root; 90 int ret; 91 struct btrfs_path *path = NULL; 92 struct btrfs_key key; 93 struct extent_buffer *eb; 94 int slot; 95 unsigned long offset; 96 __le64 subid_le; 97 98 ret = btrfs_uuid_tree_lookup(uuid_root, uuid, type, subid_cpu); 99 if (ret != -ENOENT) 100 return ret; 101 102 if (WARN_ON_ONCE(!uuid_root)) { 103 ret = -EINVAL; 104 goto out; 105 } 106 107 btrfs_uuid_to_key(uuid, type, &key); 108 109 path = btrfs_alloc_path(); 110 if (!path) { 111 ret = -ENOMEM; 112 goto out; 113 } 114 115 ret = btrfs_insert_empty_item(trans, uuid_root, path, &key, 116 sizeof(subid_le)); 117 if (ret >= 0) { 118 /* Add an item for the type for the first time */ 119 eb = path->nodes[0]; 120 slot = path->slots[0]; 121 offset = btrfs_item_ptr_offset(eb, slot); 122 } else if (ret == -EEXIST) { 123 /* 124 * An item with that type already exists. 125 * Extend the item and store the new subid at the end. 126 */ 127 btrfs_extend_item(trans, path, sizeof(subid_le)); 128 eb = path->nodes[0]; 129 slot = path->slots[0]; 130 offset = btrfs_item_ptr_offset(eb, slot); 131 offset += btrfs_item_size(eb, slot) - sizeof(subid_le); 132 } else { 133 btrfs_warn(fs_info, 134 "insert uuid item failed %d (0x%016llx, 0x%016llx) type %u!", 135 ret, key.objectid, key.offset, type); 136 goto out; 137 } 138 139 ret = 0; 140 subid_le = cpu_to_le64(subid_cpu); 141 write_extent_buffer(eb, &subid_le, offset, sizeof(subid_le)); 142 btrfs_mark_buffer_dirty(trans, eb); 143 144 out: 145 btrfs_free_path(path); 146 return ret; 147 } 148 149 int btrfs_uuid_tree_remove(struct btrfs_trans_handle *trans, u8 *uuid, u8 type, 150 u64 subid) 151 { 152 struct btrfs_fs_info *fs_info = trans->fs_info; 153 struct btrfs_root *uuid_root = fs_info->uuid_root; 154 int ret; 155 struct btrfs_path *path = NULL; 156 struct btrfs_key key; 157 struct extent_buffer *eb; 158 int slot; 159 unsigned long offset; 160 u32 item_size; 161 unsigned long move_dst; 162 unsigned long move_src; 163 unsigned long move_len; 164 165 if (WARN_ON_ONCE(!uuid_root)) { 166 ret = -EINVAL; 167 goto out; 168 } 169 170 btrfs_uuid_to_key(uuid, type, &key); 171 172 path = btrfs_alloc_path(); 173 if (!path) { 174 ret = -ENOMEM; 175 goto out; 176 } 177 178 ret = btrfs_search_slot(trans, uuid_root, &key, path, -1, 1); 179 if (ret < 0) { 180 btrfs_warn(fs_info, "error %d while searching for uuid item!", 181 ret); 182 goto out; 183 } 184 if (ret > 0) { 185 ret = -ENOENT; 186 goto out; 187 } 188 189 eb = path->nodes[0]; 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); 196 ret = -ENOENT; 197 goto out; 198 } 199 while (item_size) { 200 __le64 read_subid; 201 202 read_extent_buffer(eb, &read_subid, offset, sizeof(read_subid)); 203 if (le64_to_cpu(read_subid) == subid) 204 break; 205 offset += sizeof(read_subid); 206 item_size -= sizeof(read_subid); 207 } 208 209 if (!item_size) { 210 ret = -ENOENT; 211 goto out; 212 } 213 214 item_size = btrfs_item_size(eb, slot); 215 if (item_size == sizeof(subid)) { 216 ret = btrfs_del_item(trans, uuid_root, path); 217 goto out; 218 } 219 220 move_dst = offset; 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(trans, path, item_size - sizeof(subid), 1); 225 226 out: 227 btrfs_free_path(path); 228 return ret; 229 } 230 231 static int btrfs_uuid_iter_rem(struct btrfs_root *uuid_root, u8 *uuid, u8 type, 232 u64 subid) 233 { 234 struct btrfs_trans_handle *trans; 235 int ret; 236 237 /* 1 - for the uuid item */ 238 trans = btrfs_start_transaction(uuid_root, 1); 239 if (IS_ERR(trans)) { 240 ret = PTR_ERR(trans); 241 goto out; 242 } 243 244 ret = btrfs_uuid_tree_remove(trans, uuid, type, subid); 245 btrfs_end_transaction(trans); 246 247 out: 248 return ret; 249 } 250 251 /* 252 * Check if there's an matching subvolume for given UUID 253 * 254 * Return: 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 258 */ 259 static int btrfs_check_uuid_tree_entry(struct btrfs_fs_info *fs_info, 260 u8 *uuid, u8 type, u64 subvolid) 261 { 262 int ret = 0; 263 struct btrfs_root *subvol_root; 264 265 if (type != BTRFS_UUID_KEY_SUBVOL && 266 type != BTRFS_UUID_KEY_RECEIVED_SUBVOL) 267 goto out; 268 269 subvol_root = btrfs_get_fs_root(fs_info, subvolid, true); 270 if (IS_ERR(subvol_root)) { 271 ret = PTR_ERR(subvol_root); 272 if (ret == -ENOENT) 273 ret = 1; 274 goto out; 275 } 276 277 switch (type) { 278 case BTRFS_UUID_KEY_SUBVOL: 279 if (memcmp(uuid, subvol_root->root_item.uuid, BTRFS_UUID_SIZE)) 280 ret = 1; 281 break; 282 case BTRFS_UUID_KEY_RECEIVED_SUBVOL: 283 if (memcmp(uuid, subvol_root->root_item.received_uuid, 284 BTRFS_UUID_SIZE)) 285 ret = 1; 286 break; 287 } 288 btrfs_put_root(subvol_root); 289 out: 290 return ret; 291 } 292 293 int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info) 294 { 295 struct btrfs_root *root = fs_info->uuid_root; 296 struct btrfs_key key; 297 struct btrfs_path *path; 298 int ret = 0; 299 struct extent_buffer *leaf; 300 int slot; 301 u32 item_size; 302 unsigned long offset; 303 304 path = btrfs_alloc_path(); 305 if (!path) { 306 ret = -ENOMEM; 307 goto out; 308 } 309 310 key.objectid = 0; 311 key.type = 0; 312 key.offset = 0; 313 314 again_search_slot: 315 ret = btrfs_search_forward(root, &key, path, BTRFS_OLDEST_GENERATION); 316 if (ret) { 317 if (ret > 0) 318 ret = 0; 319 goto out; 320 } 321 322 while (1) { 323 if (btrfs_fs_closing(fs_info)) { 324 ret = -EINTR; 325 goto out; 326 } 327 cond_resched(); 328 leaf = path->nodes[0]; 329 slot = path->slots[0]; 330 btrfs_item_key_to_cpu(leaf, &key, slot); 331 332 if (key.type != BTRFS_UUID_KEY_SUBVOL && 333 key.type != BTRFS_UUID_KEY_RECEIVED_SUBVOL) 334 goto skip; 335 336 offset = btrfs_item_ptr_offset(leaf, slot); 337 item_size = btrfs_item_size(leaf, slot); 338 if (!IS_ALIGNED(item_size, sizeof(u64))) { 339 btrfs_warn(fs_info, 340 "uuid item with illegal size %lu!", 341 (unsigned long)item_size); 342 goto skip; 343 } 344 while (item_size) { 345 u8 uuid[BTRFS_UUID_SIZE]; 346 __le64 subid_le; 347 u64 subid_cpu; 348 349 put_unaligned_le64(key.objectid, uuid); 350 put_unaligned_le64(key.offset, uuid + sizeof(u64)); 351 read_extent_buffer(leaf, &subid_le, offset, 352 sizeof(subid_le)); 353 subid_cpu = le64_to_cpu(subid_le); 354 ret = btrfs_check_uuid_tree_entry(fs_info, uuid, 355 key.type, subid_cpu); 356 if (ret < 0) 357 goto out; 358 if (ret > 0) { 359 btrfs_release_path(path); 360 ret = btrfs_uuid_iter_rem(root, uuid, key.type, 361 subid_cpu); 362 if (ret == 0) { 363 /* 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. 369 */ 370 goto again_search_slot; 371 } 372 if (ret < 0 && ret != -ENOENT) 373 goto out; 374 key.offset++; 375 goto again_search_slot; 376 } 377 item_size -= sizeof(subid_le); 378 offset += sizeof(subid_le); 379 } 380 381 skip: 382 ret = btrfs_next_item(root, path); 383 if (ret == 0) 384 continue; 385 else if (ret > 0) 386 ret = 0; 387 break; 388 } 389 390 out: 391 btrfs_free_path(path); 392 return ret; 393 } 394