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, "uuid item with illegal size %lu!", 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 btrfs_warn(uuid_root->fs_info, "insert uuid item failed %d " 141 "(0x%016llx, 0x%016llx) type %u!", 142 ret, (unsigned long long)key.objectid, 143 (unsigned long long)key.offset, type); 144 goto out; 145 } 146 147 ret = 0; 148 subid_le = cpu_to_le64(subid_cpu); 149 write_extent_buffer(eb, &subid_le, offset, sizeof(subid_le)); 150 btrfs_mark_buffer_dirty(eb); 151 152 out: 153 btrfs_free_path(path); 154 return ret; 155 } 156 157 int btrfs_uuid_tree_rem(struct btrfs_trans_handle *trans, 158 struct btrfs_root *uuid_root, u8 *uuid, u8 type, 159 u64 subid) 160 { 161 int ret; 162 struct btrfs_path *path = NULL; 163 struct btrfs_key key; 164 struct extent_buffer *eb; 165 int slot; 166 unsigned long offset; 167 u32 item_size; 168 unsigned long move_dst; 169 unsigned long move_src; 170 unsigned long move_len; 171 172 if (WARN_ON_ONCE(!uuid_root)) { 173 ret = -EINVAL; 174 goto out; 175 } 176 177 btrfs_uuid_to_key(uuid, type, &key); 178 179 path = btrfs_alloc_path(); 180 if (!path) { 181 ret = -ENOMEM; 182 goto out; 183 } 184 185 ret = btrfs_search_slot(trans, uuid_root, &key, path, -1, 1); 186 if (ret < 0) { 187 btrfs_warn(uuid_root->fs_info, "error %d while searching for uuid item!", 188 ret); 189 goto out; 190 } 191 if (ret > 0) { 192 ret = -ENOENT; 193 goto out; 194 } 195 196 eb = path->nodes[0]; 197 slot = path->slots[0]; 198 offset = btrfs_item_ptr_offset(eb, slot); 199 item_size = btrfs_item_size_nr(eb, slot); 200 if (!IS_ALIGNED(item_size, sizeof(u64))) { 201 btrfs_warn(uuid_root->fs_info, "uuid item with illegal size %lu!", 202 (unsigned long)item_size); 203 ret = -ENOENT; 204 goto out; 205 } 206 while (item_size) { 207 __le64 read_subid; 208 209 read_extent_buffer(eb, &read_subid, offset, sizeof(read_subid)); 210 if (le64_to_cpu(read_subid) == subid) 211 break; 212 offset += sizeof(read_subid); 213 item_size -= sizeof(read_subid); 214 } 215 216 if (!item_size) { 217 ret = -ENOENT; 218 goto out; 219 } 220 221 item_size = btrfs_item_size_nr(eb, slot); 222 if (item_size == sizeof(subid)) { 223 ret = btrfs_del_item(trans, uuid_root, path); 224 goto out; 225 } 226 227 move_dst = offset; 228 move_src = offset + sizeof(subid); 229 move_len = item_size - (move_src - btrfs_item_ptr_offset(eb, slot)); 230 memmove_extent_buffer(eb, move_dst, move_src, move_len); 231 btrfs_truncate_item(uuid_root, path, item_size - sizeof(subid), 1); 232 233 out: 234 btrfs_free_path(path); 235 return ret; 236 } 237 238 static int btrfs_uuid_iter_rem(struct btrfs_root *uuid_root, u8 *uuid, u8 type, 239 u64 subid) 240 { 241 struct btrfs_trans_handle *trans; 242 int ret; 243 244 /* 1 - for the uuid item */ 245 trans = btrfs_start_transaction(uuid_root, 1); 246 if (IS_ERR(trans)) { 247 ret = PTR_ERR(trans); 248 goto out; 249 } 250 251 ret = btrfs_uuid_tree_rem(trans, uuid_root, uuid, type, subid); 252 btrfs_end_transaction(trans, uuid_root); 253 254 out: 255 return ret; 256 } 257 258 int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info, 259 int (*check_func)(struct btrfs_fs_info *, u8 *, u8, 260 u64)) 261 { 262 struct btrfs_root *root = fs_info->uuid_root; 263 struct btrfs_key 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 281 again_search_slot: 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 btrfs_warn(fs_info, "uuid item with illegal size %lu!", 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 btrfs_warn(fs_info, "btrfs_uuid_tree_iterate failed %d", ret); 353 return 0; 354 } 355