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 btrfs_warn(uuid_root->fs_info, 73 "uuid item with illegal size %lu!", 74 (unsigned long)item_size); 75 goto out; 76 } 77 while (item_size) { 78 __le64 data; 79 80 read_extent_buffer(eb, &data, offset, sizeof(data)); 81 if (le64_to_cpu(data) == subid) { 82 ret = 0; 83 break; 84 } 85 offset += sizeof(data); 86 item_size -= sizeof(data); 87 } 88 89 out: 90 btrfs_free_path(path); 91 return ret; 92 } 93 94 int btrfs_uuid_tree_add(struct btrfs_trans_handle *trans, 95 struct btrfs_fs_info *fs_info, u8 *uuid, u8 type, 96 u64 subid_cpu) 97 { 98 struct btrfs_root *uuid_root = fs_info->uuid_root; 99 int ret; 100 struct btrfs_path *path = NULL; 101 struct btrfs_key key; 102 struct extent_buffer *eb; 103 int slot; 104 unsigned long offset; 105 __le64 subid_le; 106 107 ret = btrfs_uuid_tree_lookup(uuid_root, uuid, type, subid_cpu); 108 if (ret != -ENOENT) 109 return ret; 110 111 if (WARN_ON_ONCE(!uuid_root)) { 112 ret = -EINVAL; 113 goto out; 114 } 115 116 btrfs_uuid_to_key(uuid, type, &key); 117 118 path = btrfs_alloc_path(); 119 if (!path) { 120 ret = -ENOMEM; 121 goto out; 122 } 123 124 ret = btrfs_insert_empty_item(trans, uuid_root, path, &key, 125 sizeof(subid_le)); 126 if (ret >= 0) { 127 /* Add an item for the type for the first time */ 128 eb = path->nodes[0]; 129 slot = path->slots[0]; 130 offset = btrfs_item_ptr_offset(eb, slot); 131 } else if (ret == -EEXIST) { 132 /* 133 * An item with that type already exists. 134 * Extend the item and store the new subid at the end. 135 */ 136 btrfs_extend_item(uuid_root, path, sizeof(subid_le)); 137 eb = path->nodes[0]; 138 slot = path->slots[0]; 139 offset = btrfs_item_ptr_offset(eb, slot); 140 offset += btrfs_item_size_nr(eb, slot) - sizeof(subid_le); 141 } else if (ret < 0) { 142 btrfs_warn(fs_info, 143 "insert uuid item failed %d (0x%016llx, 0x%016llx) type %u!", 144 ret, (unsigned long long)key.objectid, 145 (unsigned long long)key.offset, type); 146 goto out; 147 } 148 149 ret = 0; 150 subid_le = cpu_to_le64(subid_cpu); 151 write_extent_buffer(eb, &subid_le, offset, sizeof(subid_le)); 152 btrfs_mark_buffer_dirty(eb); 153 154 out: 155 btrfs_free_path(path); 156 return ret; 157 } 158 159 int btrfs_uuid_tree_rem(struct btrfs_trans_handle *trans, 160 struct btrfs_fs_info *fs_info, u8 *uuid, u8 type, 161 u64 subid) 162 { 163 struct btrfs_root *uuid_root = fs_info->uuid_root; 164 int ret; 165 struct btrfs_path *path = NULL; 166 struct btrfs_key key; 167 struct extent_buffer *eb; 168 int slot; 169 unsigned long offset; 170 u32 item_size; 171 unsigned long move_dst; 172 unsigned long move_src; 173 unsigned long move_len; 174 175 if (WARN_ON_ONCE(!uuid_root)) { 176 ret = -EINVAL; 177 goto out; 178 } 179 180 btrfs_uuid_to_key(uuid, type, &key); 181 182 path = btrfs_alloc_path(); 183 if (!path) { 184 ret = -ENOMEM; 185 goto out; 186 } 187 188 ret = btrfs_search_slot(trans, uuid_root, &key, path, -1, 1); 189 if (ret < 0) { 190 btrfs_warn(uuid_root->fs_info, 191 "error %d while searching for uuid item!", ret); 192 goto out; 193 } 194 if (ret > 0) { 195 ret = -ENOENT; 196 goto out; 197 } 198 199 eb = path->nodes[0]; 200 slot = path->slots[0]; 201 offset = btrfs_item_ptr_offset(eb, slot); 202 item_size = btrfs_item_size_nr(eb, slot); 203 if (!IS_ALIGNED(item_size, sizeof(u64))) { 204 btrfs_warn(uuid_root->fs_info, 205 "uuid item with illegal size %lu!", 206 (unsigned long)item_size); 207 ret = -ENOENT; 208 goto out; 209 } 210 while (item_size) { 211 __le64 read_subid; 212 213 read_extent_buffer(eb, &read_subid, offset, sizeof(read_subid)); 214 if (le64_to_cpu(read_subid) == subid) 215 break; 216 offset += sizeof(read_subid); 217 item_size -= sizeof(read_subid); 218 } 219 220 if (!item_size) { 221 ret = -ENOENT; 222 goto out; 223 } 224 225 item_size = btrfs_item_size_nr(eb, slot); 226 if (item_size == sizeof(subid)) { 227 ret = btrfs_del_item(trans, uuid_root, path); 228 goto out; 229 } 230 231 move_dst = offset; 232 move_src = offset + sizeof(subid); 233 move_len = item_size - (move_src - btrfs_item_ptr_offset(eb, slot)); 234 memmove_extent_buffer(eb, move_dst, move_src, move_len); 235 btrfs_truncate_item(uuid_root, path, item_size - sizeof(subid), 1); 236 237 out: 238 btrfs_free_path(path); 239 return ret; 240 } 241 242 static int btrfs_uuid_iter_rem(struct btrfs_root *uuid_root, u8 *uuid, u8 type, 243 u64 subid) 244 { 245 struct btrfs_trans_handle *trans; 246 int ret; 247 248 /* 1 - for the uuid item */ 249 trans = btrfs_start_transaction(uuid_root, 1); 250 if (IS_ERR(trans)) { 251 ret = PTR_ERR(trans); 252 goto out; 253 } 254 255 ret = btrfs_uuid_tree_rem(trans, uuid_root->fs_info, uuid, type, subid); 256 btrfs_end_transaction(trans, uuid_root); 257 258 out: 259 return ret; 260 } 261 262 int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info, 263 int (*check_func)(struct btrfs_fs_info *, u8 *, u8, 264 u64)) 265 { 266 struct btrfs_root *root = fs_info->uuid_root; 267 struct btrfs_key key; 268 struct btrfs_path *path; 269 int ret = 0; 270 struct extent_buffer *leaf; 271 int slot; 272 u32 item_size; 273 unsigned long offset; 274 275 path = btrfs_alloc_path(); 276 if (!path) { 277 ret = -ENOMEM; 278 goto out; 279 } 280 281 key.objectid = 0; 282 key.type = 0; 283 key.offset = 0; 284 285 again_search_slot: 286 ret = btrfs_search_forward(root, &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 btrfs_warn(fs_info, 307 "uuid item with illegal size %lu!", 308 (unsigned long)item_size); 309 goto skip; 310 } 311 while (item_size) { 312 u8 uuid[BTRFS_UUID_SIZE]; 313 __le64 subid_le; 314 u64 subid_cpu; 315 316 put_unaligned_le64(key.objectid, uuid); 317 put_unaligned_le64(key.offset, uuid + sizeof(u64)); 318 read_extent_buffer(leaf, &subid_le, offset, 319 sizeof(subid_le)); 320 subid_cpu = le64_to_cpu(subid_le); 321 ret = check_func(fs_info, uuid, key.type, subid_cpu); 322 if (ret < 0) 323 goto out; 324 if (ret > 0) { 325 btrfs_release_path(path); 326 ret = btrfs_uuid_iter_rem(root, uuid, key.type, 327 subid_cpu); 328 if (ret == 0) { 329 /* 330 * this might look inefficient, but the 331 * justification is that it is an 332 * exception that check_func returns 1, 333 * and that in the regular case only one 334 * entry per UUID exists. 335 */ 336 goto again_search_slot; 337 } 338 if (ret < 0 && ret != -ENOENT) 339 goto out; 340 } 341 item_size -= sizeof(subid_le); 342 offset += sizeof(subid_le); 343 } 344 345 skip: 346 ret = btrfs_next_item(root, path); 347 if (ret == 0) 348 continue; 349 else if (ret > 0) 350 ret = 0; 351 break; 352 } 353 354 out: 355 btrfs_free_path(path); 356 if (ret) 357 btrfs_warn(fs_info, "btrfs_uuid_tree_iterate failed %d", ret); 358 return 0; 359 } 360