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 26 static 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 */ 34 static 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 pr_warn("btrfs: uuid item with illegal size %lu!\n", 73 (unsigned long)item_size); 74 goto out; 75 } 76 while (item_size) { 77 __le64 data; 78 79 read_extent_buffer(eb, &data, offset, sizeof(data)); 80 if (le64_to_cpu(data) == subid) { 81 ret = 0; 82 break; 83 } 84 offset += sizeof(data); 85 item_size -= sizeof(data); 86 } 87 88 out: 89 btrfs_free_path(path); 90 return ret; 91 } 92 93 int btrfs_uuid_tree_add(struct btrfs_trans_handle *trans, 94 struct btrfs_root *uuid_root, u8 *uuid, u8 type, 95 u64 subid_cpu) 96 { 97 int ret; 98 struct btrfs_path *path = NULL; 99 struct btrfs_key key; 100 struct extent_buffer *eb; 101 int slot; 102 unsigned long offset; 103 __le64 subid_le; 104 105 ret = btrfs_uuid_tree_lookup(uuid_root, uuid, type, subid_cpu); 106 if (ret != -ENOENT) 107 return ret; 108 109 if (WARN_ON_ONCE(!uuid_root)) { 110 ret = -EINVAL; 111 goto out; 112 } 113 114 btrfs_uuid_to_key(uuid, type, &key); 115 116 path = btrfs_alloc_path(); 117 if (!path) { 118 ret = -ENOMEM; 119 goto out; 120 } 121 122 ret = btrfs_insert_empty_item(trans, uuid_root, path, &key, 123 sizeof(subid_le)); 124 if (ret >= 0) { 125 /* Add an item for the type for the first time */ 126 eb = path->nodes[0]; 127 slot = path->slots[0]; 128 offset = btrfs_item_ptr_offset(eb, slot); 129 } else if (ret == -EEXIST) { 130 /* 131 * An item with that type already exists. 132 * Extend the item and store the new subid at the end. 133 */ 134 btrfs_extend_item(uuid_root, path, sizeof(subid_le)); 135 eb = path->nodes[0]; 136 slot = path->slots[0]; 137 offset = btrfs_item_ptr_offset(eb, slot); 138 offset += btrfs_item_size_nr(eb, slot) - sizeof(subid_le); 139 } else if (ret < 0) { 140 pr_warn("btrfs: insert uuid item failed %d (0x%016llx, 0x%016llx) type %u!\n", 141 ret, (unsigned long long)key.objectid, 142 (unsigned long long)key.offset, type); 143 goto out; 144 } 145 146 ret = 0; 147 subid_le = cpu_to_le64(subid_cpu); 148 write_extent_buffer(eb, &subid_le, offset, sizeof(subid_le)); 149 btrfs_mark_buffer_dirty(eb); 150 151 out: 152 btrfs_free_path(path); 153 return ret; 154 } 155 156 int btrfs_uuid_tree_rem(struct btrfs_trans_handle *trans, 157 struct btrfs_root *uuid_root, u8 *uuid, u8 type, 158 u64 subid) 159 { 160 int ret; 161 struct btrfs_path *path = NULL; 162 struct btrfs_key key; 163 struct extent_buffer *eb; 164 int slot; 165 unsigned long offset; 166 u32 item_size; 167 unsigned long move_dst; 168 unsigned long move_src; 169 unsigned long move_len; 170 171 if (WARN_ON_ONCE(!uuid_root)) { 172 ret = -EINVAL; 173 goto out; 174 } 175 176 btrfs_uuid_to_key(uuid, type, &key); 177 178 path = btrfs_alloc_path(); 179 if (!path) { 180 ret = -ENOMEM; 181 goto out; 182 } 183 184 ret = btrfs_search_slot(trans, uuid_root, &key, path, -1, 1); 185 if (ret < 0) { 186 pr_warn("btrfs: error %d while searching for uuid item!\n", 187 ret); 188 goto out; 189 } 190 if (ret > 0) { 191 ret = -ENOENT; 192 goto out; 193 } 194 195 eb = path->nodes[0]; 196 slot = path->slots[0]; 197 offset = btrfs_item_ptr_offset(eb, slot); 198 item_size = btrfs_item_size_nr(eb, slot); 199 if (!IS_ALIGNED(item_size, sizeof(u64))) { 200 pr_warn("btrfs: uuid item with illegal size %lu!\n", 201 (unsigned long)item_size); 202 ret = -ENOENT; 203 goto out; 204 } 205 while (item_size) { 206 __le64 read_subid; 207 208 read_extent_buffer(eb, &read_subid, offset, sizeof(read_subid)); 209 if (le64_to_cpu(read_subid) == subid) 210 break; 211 offset += sizeof(read_subid); 212 item_size -= sizeof(read_subid); 213 } 214 215 if (!item_size) { 216 ret = -ENOENT; 217 goto out; 218 } 219 220 item_size = btrfs_item_size_nr(eb, slot); 221 if (item_size == sizeof(subid)) { 222 ret = btrfs_del_item(trans, uuid_root, path); 223 goto out; 224 } 225 226 move_dst = offset; 227 move_src = offset + sizeof(subid); 228 move_len = item_size - (move_src - btrfs_item_ptr_offset(eb, slot)); 229 memmove_extent_buffer(eb, move_dst, move_src, move_len); 230 btrfs_truncate_item(uuid_root, path, item_size - sizeof(subid), 1); 231 232 out: 233 btrfs_free_path(path); 234 return ret; 235 } 236 237 static int btrfs_uuid_iter_rem(struct btrfs_root *uuid_root, u8 *uuid, u8 type, 238 u64 subid) 239 { 240 struct btrfs_trans_handle *trans; 241 int ret; 242 243 /* 1 - for the uuid item */ 244 trans = btrfs_start_transaction(uuid_root, 1); 245 if (IS_ERR(trans)) { 246 ret = PTR_ERR(trans); 247 goto out; 248 } 249 250 ret = btrfs_uuid_tree_rem(trans, uuid_root, uuid, type, subid); 251 btrfs_end_transaction(trans, uuid_root); 252 253 out: 254 return ret; 255 } 256 257 int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info, 258 int (*check_func)(struct btrfs_fs_info *, u8 *, u8, 259 u64)) 260 { 261 struct btrfs_root *root = fs_info->uuid_root; 262 struct btrfs_key key; 263 struct btrfs_key max_key; 264 struct btrfs_path *path; 265 int ret = 0; 266 struct extent_buffer *leaf; 267 int slot; 268 u32 item_size; 269 unsigned long offset; 270 271 path = btrfs_alloc_path(); 272 if (!path) { 273 ret = -ENOMEM; 274 goto out; 275 } 276 277 key.objectid = 0; 278 key.type = 0; 279 key.offset = 0; 280 max_key.objectid = (u64)-1; 281 max_key.type = (u8)-1; 282 max_key.offset = (u64)-1; 283 284 again_search_slot: 285 path->keep_locks = 1; 286 ret = btrfs_search_forward(root, &key, &max_key, path, 0); 287 if (ret) { 288 if (ret > 0) 289 ret = 0; 290 goto out; 291 } 292 293 while (1) { 294 cond_resched(); 295 leaf = path->nodes[0]; 296 slot = path->slots[0]; 297 btrfs_item_key_to_cpu(leaf, &key, slot); 298 299 if (key.type != BTRFS_UUID_KEY_SUBVOL && 300 key.type != BTRFS_UUID_KEY_RECEIVED_SUBVOL) 301 goto skip; 302 303 offset = btrfs_item_ptr_offset(leaf, slot); 304 item_size = btrfs_item_size_nr(leaf, slot); 305 if (!IS_ALIGNED(item_size, sizeof(u64))) { 306 pr_warn("btrfs: uuid item with illegal size %lu!\n", 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 344 skip: 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 353 out: 354 btrfs_free_path(path); 355 if (ret) 356 pr_warn("btrfs: btrfs_uuid_tree_iterate failed %d\n", ret); 357 return 0; 358 } 359