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(fs_info, 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(fs_info, "error %d while searching for uuid item!", 191 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(fs_info, "uuid item with illegal size %lu!", 205 (unsigned long)item_size); 206 ret = -ENOENT; 207 goto out; 208 } 209 while (item_size) { 210 __le64 read_subid; 211 212 read_extent_buffer(eb, &read_subid, offset, sizeof(read_subid)); 213 if (le64_to_cpu(read_subid) == subid) 214 break; 215 offset += sizeof(read_subid); 216 item_size -= sizeof(read_subid); 217 } 218 219 if (!item_size) { 220 ret = -ENOENT; 221 goto out; 222 } 223 224 item_size = btrfs_item_size_nr(eb, slot); 225 if (item_size == sizeof(subid)) { 226 ret = btrfs_del_item(trans, uuid_root, path); 227 goto out; 228 } 229 230 move_dst = offset; 231 move_src = offset + sizeof(subid); 232 move_len = item_size - (move_src - btrfs_item_ptr_offset(eb, slot)); 233 memmove_extent_buffer(eb, move_dst, move_src, move_len); 234 btrfs_truncate_item(fs_info, path, item_size - sizeof(subid), 1); 235 236 out: 237 btrfs_free_path(path); 238 return ret; 239 } 240 241 static int btrfs_uuid_iter_rem(struct btrfs_root *uuid_root, u8 *uuid, u8 type, 242 u64 subid) 243 { 244 struct btrfs_trans_handle *trans; 245 int ret; 246 247 /* 1 - for the uuid item */ 248 trans = btrfs_start_transaction(uuid_root, 1); 249 if (IS_ERR(trans)) { 250 ret = PTR_ERR(trans); 251 goto out; 252 } 253 254 ret = btrfs_uuid_tree_rem(trans, uuid_root->fs_info, uuid, type, subid); 255 btrfs_end_transaction(trans); 256 257 out: 258 return ret; 259 } 260 261 int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info, 262 int (*check_func)(struct btrfs_fs_info *, u8 *, u8, 263 u64)) 264 { 265 struct btrfs_root *root = fs_info->uuid_root; 266 struct btrfs_key key; 267 struct btrfs_path *path; 268 int ret = 0; 269 struct extent_buffer *leaf; 270 int slot; 271 u32 item_size; 272 unsigned long offset; 273 274 path = btrfs_alloc_path(); 275 if (!path) { 276 ret = -ENOMEM; 277 goto out; 278 } 279 280 key.objectid = 0; 281 key.type = 0; 282 key.offset = 0; 283 284 again_search_slot: 285 ret = btrfs_search_forward(root, &key, path, 0); 286 if (ret) { 287 if (ret > 0) 288 ret = 0; 289 goto out; 290 } 291 292 while (1) { 293 cond_resched(); 294 leaf = path->nodes[0]; 295 slot = path->slots[0]; 296 btrfs_item_key_to_cpu(leaf, &key, slot); 297 298 if (key.type != BTRFS_UUID_KEY_SUBVOL && 299 key.type != BTRFS_UUID_KEY_RECEIVED_SUBVOL) 300 goto skip; 301 302 offset = btrfs_item_ptr_offset(leaf, slot); 303 item_size = btrfs_item_size_nr(leaf, slot); 304 if (!IS_ALIGNED(item_size, sizeof(u64))) { 305 btrfs_warn(fs_info, 306 "uuid item with illegal size %lu!", 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 return ret; 356 } 357