0001
0002
0003
0004
0005
0006 #include <linux/uuid.h>
0007 #include <asm/unaligned.h>
0008 #include "ctree.h"
0009 #include "transaction.h"
0010 #include "disk-io.h"
0011 #include "print-tree.h"
0012
0013
0014 static void btrfs_uuid_to_key(u8 *uuid, u8 type, struct btrfs_key *key)
0015 {
0016 key->type = type;
0017 key->objectid = get_unaligned_le64(uuid);
0018 key->offset = get_unaligned_le64(uuid + sizeof(u64));
0019 }
0020
0021
0022 static int btrfs_uuid_tree_lookup(struct btrfs_root *uuid_root, u8 *uuid,
0023 u8 type, u64 subid)
0024 {
0025 int ret;
0026 struct btrfs_path *path = NULL;
0027 struct extent_buffer *eb;
0028 int slot;
0029 u32 item_size;
0030 unsigned long offset;
0031 struct btrfs_key key;
0032
0033 if (WARN_ON_ONCE(!uuid_root)) {
0034 ret = -ENOENT;
0035 goto out;
0036 }
0037
0038 path = btrfs_alloc_path();
0039 if (!path) {
0040 ret = -ENOMEM;
0041 goto out;
0042 }
0043
0044 btrfs_uuid_to_key(uuid, type, &key);
0045 ret = btrfs_search_slot(NULL, uuid_root, &key, path, 0, 0);
0046 if (ret < 0) {
0047 goto out;
0048 } else if (ret > 0) {
0049 ret = -ENOENT;
0050 goto out;
0051 }
0052
0053 eb = path->nodes[0];
0054 slot = path->slots[0];
0055 item_size = btrfs_item_size(eb, slot);
0056 offset = btrfs_item_ptr_offset(eb, slot);
0057 ret = -ENOENT;
0058
0059 if (!IS_ALIGNED(item_size, sizeof(u64))) {
0060 btrfs_warn(uuid_root->fs_info,
0061 "uuid item with illegal size %lu!",
0062 (unsigned long)item_size);
0063 goto out;
0064 }
0065 while (item_size) {
0066 __le64 data;
0067
0068 read_extent_buffer(eb, &data, offset, sizeof(data));
0069 if (le64_to_cpu(data) == subid) {
0070 ret = 0;
0071 break;
0072 }
0073 offset += sizeof(data);
0074 item_size -= sizeof(data);
0075 }
0076
0077 out:
0078 btrfs_free_path(path);
0079 return ret;
0080 }
0081
0082 int btrfs_uuid_tree_add(struct btrfs_trans_handle *trans, u8 *uuid, u8 type,
0083 u64 subid_cpu)
0084 {
0085 struct btrfs_fs_info *fs_info = trans->fs_info;
0086 struct btrfs_root *uuid_root = fs_info->uuid_root;
0087 int ret;
0088 struct btrfs_path *path = NULL;
0089 struct btrfs_key key;
0090 struct extent_buffer *eb;
0091 int slot;
0092 unsigned long offset;
0093 __le64 subid_le;
0094
0095 ret = btrfs_uuid_tree_lookup(uuid_root, uuid, type, subid_cpu);
0096 if (ret != -ENOENT)
0097 return ret;
0098
0099 if (WARN_ON_ONCE(!uuid_root)) {
0100 ret = -EINVAL;
0101 goto out;
0102 }
0103
0104 btrfs_uuid_to_key(uuid, type, &key);
0105
0106 path = btrfs_alloc_path();
0107 if (!path) {
0108 ret = -ENOMEM;
0109 goto out;
0110 }
0111
0112 ret = btrfs_insert_empty_item(trans, uuid_root, path, &key,
0113 sizeof(subid_le));
0114 if (ret >= 0) {
0115
0116 eb = path->nodes[0];
0117 slot = path->slots[0];
0118 offset = btrfs_item_ptr_offset(eb, slot);
0119 } else if (ret == -EEXIST) {
0120
0121
0122
0123
0124 btrfs_extend_item(path, sizeof(subid_le));
0125 eb = path->nodes[0];
0126 slot = path->slots[0];
0127 offset = btrfs_item_ptr_offset(eb, slot);
0128 offset += btrfs_item_size(eb, slot) - sizeof(subid_le);
0129 } else {
0130 btrfs_warn(fs_info,
0131 "insert uuid item failed %d (0x%016llx, 0x%016llx) type %u!",
0132 ret, key.objectid, key.offset, type);
0133 goto out;
0134 }
0135
0136 ret = 0;
0137 subid_le = cpu_to_le64(subid_cpu);
0138 write_extent_buffer(eb, &subid_le, offset, sizeof(subid_le));
0139 btrfs_mark_buffer_dirty(eb);
0140
0141 out:
0142 btrfs_free_path(path);
0143 return ret;
0144 }
0145
0146 int btrfs_uuid_tree_remove(struct btrfs_trans_handle *trans, u8 *uuid, u8 type,
0147 u64 subid)
0148 {
0149 struct btrfs_fs_info *fs_info = trans->fs_info;
0150 struct btrfs_root *uuid_root = fs_info->uuid_root;
0151 int ret;
0152 struct btrfs_path *path = NULL;
0153 struct btrfs_key key;
0154 struct extent_buffer *eb;
0155 int slot;
0156 unsigned long offset;
0157 u32 item_size;
0158 unsigned long move_dst;
0159 unsigned long move_src;
0160 unsigned long move_len;
0161
0162 if (WARN_ON_ONCE(!uuid_root)) {
0163 ret = -EINVAL;
0164 goto out;
0165 }
0166
0167 btrfs_uuid_to_key(uuid, type, &key);
0168
0169 path = btrfs_alloc_path();
0170 if (!path) {
0171 ret = -ENOMEM;
0172 goto out;
0173 }
0174
0175 ret = btrfs_search_slot(trans, uuid_root, &key, path, -1, 1);
0176 if (ret < 0) {
0177 btrfs_warn(fs_info, "error %d while searching for uuid item!",
0178 ret);
0179 goto out;
0180 }
0181 if (ret > 0) {
0182 ret = -ENOENT;
0183 goto out;
0184 }
0185
0186 eb = path->nodes[0];
0187 slot = path->slots[0];
0188 offset = btrfs_item_ptr_offset(eb, slot);
0189 item_size = btrfs_item_size(eb, slot);
0190 if (!IS_ALIGNED(item_size, sizeof(u64))) {
0191 btrfs_warn(fs_info, "uuid item with illegal size %lu!",
0192 (unsigned long)item_size);
0193 ret = -ENOENT;
0194 goto out;
0195 }
0196 while (item_size) {
0197 __le64 read_subid;
0198
0199 read_extent_buffer(eb, &read_subid, offset, sizeof(read_subid));
0200 if (le64_to_cpu(read_subid) == subid)
0201 break;
0202 offset += sizeof(read_subid);
0203 item_size -= sizeof(read_subid);
0204 }
0205
0206 if (!item_size) {
0207 ret = -ENOENT;
0208 goto out;
0209 }
0210
0211 item_size = btrfs_item_size(eb, slot);
0212 if (item_size == sizeof(subid)) {
0213 ret = btrfs_del_item(trans, uuid_root, path);
0214 goto out;
0215 }
0216
0217 move_dst = offset;
0218 move_src = offset + sizeof(subid);
0219 move_len = item_size - (move_src - btrfs_item_ptr_offset(eb, slot));
0220 memmove_extent_buffer(eb, move_dst, move_src, move_len);
0221 btrfs_truncate_item(path, item_size - sizeof(subid), 1);
0222
0223 out:
0224 btrfs_free_path(path);
0225 return ret;
0226 }
0227
0228 static int btrfs_uuid_iter_rem(struct btrfs_root *uuid_root, u8 *uuid, u8 type,
0229 u64 subid)
0230 {
0231 struct btrfs_trans_handle *trans;
0232 int ret;
0233
0234
0235 trans = btrfs_start_transaction(uuid_root, 1);
0236 if (IS_ERR(trans)) {
0237 ret = PTR_ERR(trans);
0238 goto out;
0239 }
0240
0241 ret = btrfs_uuid_tree_remove(trans, uuid, type, subid);
0242 btrfs_end_transaction(trans);
0243
0244 out:
0245 return ret;
0246 }
0247
0248
0249
0250
0251
0252
0253
0254
0255
0256 static int btrfs_check_uuid_tree_entry(struct btrfs_fs_info *fs_info,
0257 u8 *uuid, u8 type, u64 subvolid)
0258 {
0259 int ret = 0;
0260 struct btrfs_root *subvol_root;
0261
0262 if (type != BTRFS_UUID_KEY_SUBVOL &&
0263 type != BTRFS_UUID_KEY_RECEIVED_SUBVOL)
0264 goto out;
0265
0266 subvol_root = btrfs_get_fs_root(fs_info, subvolid, true);
0267 if (IS_ERR(subvol_root)) {
0268 ret = PTR_ERR(subvol_root);
0269 if (ret == -ENOENT)
0270 ret = 1;
0271 goto out;
0272 }
0273
0274 switch (type) {
0275 case BTRFS_UUID_KEY_SUBVOL:
0276 if (memcmp(uuid, subvol_root->root_item.uuid, BTRFS_UUID_SIZE))
0277 ret = 1;
0278 break;
0279 case BTRFS_UUID_KEY_RECEIVED_SUBVOL:
0280 if (memcmp(uuid, subvol_root->root_item.received_uuid,
0281 BTRFS_UUID_SIZE))
0282 ret = 1;
0283 break;
0284 }
0285 btrfs_put_root(subvol_root);
0286 out:
0287 return ret;
0288 }
0289
0290 int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info)
0291 {
0292 struct btrfs_root *root = fs_info->uuid_root;
0293 struct btrfs_key key;
0294 struct btrfs_path *path;
0295 int ret = 0;
0296 struct extent_buffer *leaf;
0297 int slot;
0298 u32 item_size;
0299 unsigned long offset;
0300
0301 path = btrfs_alloc_path();
0302 if (!path) {
0303 ret = -ENOMEM;
0304 goto out;
0305 }
0306
0307 key.objectid = 0;
0308 key.type = 0;
0309 key.offset = 0;
0310
0311 again_search_slot:
0312 ret = btrfs_search_forward(root, &key, path, BTRFS_OLDEST_GENERATION);
0313 if (ret) {
0314 if (ret > 0)
0315 ret = 0;
0316 goto out;
0317 }
0318
0319 while (1) {
0320 if (btrfs_fs_closing(fs_info)) {
0321 ret = -EINTR;
0322 goto out;
0323 }
0324 cond_resched();
0325 leaf = path->nodes[0];
0326 slot = path->slots[0];
0327 btrfs_item_key_to_cpu(leaf, &key, slot);
0328
0329 if (key.type != BTRFS_UUID_KEY_SUBVOL &&
0330 key.type != BTRFS_UUID_KEY_RECEIVED_SUBVOL)
0331 goto skip;
0332
0333 offset = btrfs_item_ptr_offset(leaf, slot);
0334 item_size = btrfs_item_size(leaf, slot);
0335 if (!IS_ALIGNED(item_size, sizeof(u64))) {
0336 btrfs_warn(fs_info,
0337 "uuid item with illegal size %lu!",
0338 (unsigned long)item_size);
0339 goto skip;
0340 }
0341 while (item_size) {
0342 u8 uuid[BTRFS_UUID_SIZE];
0343 __le64 subid_le;
0344 u64 subid_cpu;
0345
0346 put_unaligned_le64(key.objectid, uuid);
0347 put_unaligned_le64(key.offset, uuid + sizeof(u64));
0348 read_extent_buffer(leaf, &subid_le, offset,
0349 sizeof(subid_le));
0350 subid_cpu = le64_to_cpu(subid_le);
0351 ret = btrfs_check_uuid_tree_entry(fs_info, uuid,
0352 key.type, subid_cpu);
0353 if (ret < 0)
0354 goto out;
0355 if (ret > 0) {
0356 btrfs_release_path(path);
0357 ret = btrfs_uuid_iter_rem(root, uuid, key.type,
0358 subid_cpu);
0359 if (ret == 0) {
0360
0361
0362
0363
0364
0365
0366
0367 goto again_search_slot;
0368 }
0369 if (ret < 0 && ret != -ENOENT)
0370 goto out;
0371 key.offset++;
0372 goto again_search_slot;
0373 }
0374 item_size -= sizeof(subid_le);
0375 offset += sizeof(subid_le);
0376 }
0377
0378 skip:
0379 ret = btrfs_next_item(root, path);
0380 if (ret == 0)
0381 continue;
0382 else if (ret > 0)
0383 ret = 0;
0384 break;
0385 }
0386
0387 out:
0388 btrfs_free_path(path);
0389 return ret;
0390 }