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_path *path; 264 int ret = 0; 265 struct extent_buffer *leaf; 266 int slot; 267 u32 item_size; 268 unsigned long offset; 269 270 path = btrfs_alloc_path(); 271 if (!path) { 272 ret = -ENOMEM; 273 goto out; 274 } 275 276 key.objectid = 0; 277 key.type = 0; 278 key.offset = 0; 279 280 again_search_slot: 281 path->keep_locks = 1; 282 ret = btrfs_search_forward(root, &key, path, 0); 283 if (ret) { 284 if (ret > 0) 285 ret = 0; 286 goto out; 287 } 288 289 while (1) { 290 cond_resched(); 291 leaf = path->nodes[0]; 292 slot = path->slots[0]; 293 btrfs_item_key_to_cpu(leaf, &key, slot); 294 295 if (key.type != BTRFS_UUID_KEY_SUBVOL && 296 key.type != BTRFS_UUID_KEY_RECEIVED_SUBVOL) 297 goto skip; 298 299 offset = btrfs_item_ptr_offset(leaf, slot); 300 item_size = btrfs_item_size_nr(leaf, slot); 301 if (!IS_ALIGNED(item_size, sizeof(u64))) { 302 pr_warn("btrfs: uuid item with illegal size %lu!\n", 303 (unsigned long)item_size); 304 goto skip; 305 } 306 while (item_size) { 307 u8 uuid[BTRFS_UUID_SIZE]; 308 __le64 subid_le; 309 u64 subid_cpu; 310 311 put_unaligned_le64(key.objectid, uuid); 312 put_unaligned_le64(key.offset, uuid + sizeof(u64)); 313 read_extent_buffer(leaf, &subid_le, offset, 314 sizeof(subid_le)); 315 subid_cpu = le64_to_cpu(subid_le); 316 ret = check_func(fs_info, uuid, key.type, subid_cpu); 317 if (ret < 0) 318 goto out; 319 if (ret > 0) { 320 btrfs_release_path(path); 321 ret = btrfs_uuid_iter_rem(root, uuid, key.type, 322 subid_cpu); 323 if (ret == 0) { 324 /* 325 * this might look inefficient, but the 326 * justification is that it is an 327 * exception that check_func returns 1, 328 * and that in the regular case only one 329 * entry per UUID exists. 330 */ 331 goto again_search_slot; 332 } 333 if (ret < 0 && ret != -ENOENT) 334 goto out; 335 } 336 item_size -= sizeof(subid_le); 337 offset += sizeof(subid_le); 338 } 339 340 skip: 341 ret = btrfs_next_item(root, path); 342 if (ret == 0) 343 continue; 344 else if (ret > 0) 345 ret = 0; 346 break; 347 } 348 349 out: 350 btrfs_free_path(path); 351 if (ret) 352 pr_warn("btrfs: btrfs_uuid_tree_iterate failed %d\n", ret); 353 return 0; 354 } 355