GNU Linux-libre 5.19-rc6-gnu
[releases.git] / fs / btrfs / uuid-tree.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) STRATO AG 2013.  All rights reserved.
4  */
5
6 #include <linux/uuid.h>
7 #include <asm/unaligned.h>
8 #include "ctree.h"
9 #include "transaction.h"
10 #include "disk-io.h"
11 #include "print-tree.h"
12
13
14 static void btrfs_uuid_to_key(u8 *uuid, u8 type, struct btrfs_key *key)
15 {
16         key->type = type;
17         key->objectid = get_unaligned_le64(uuid);
18         key->offset = get_unaligned_le64(uuid + sizeof(u64));
19 }
20
21 /* return -ENOENT for !found, < 0 for errors, or 0 if an item was found */
22 static int btrfs_uuid_tree_lookup(struct btrfs_root *uuid_root, u8 *uuid,
23                                   u8 type, u64 subid)
24 {
25         int ret;
26         struct btrfs_path *path = NULL;
27         struct extent_buffer *eb;
28         int slot;
29         u32 item_size;
30         unsigned long offset;
31         struct btrfs_key key;
32
33         if (WARN_ON_ONCE(!uuid_root)) {
34                 ret = -ENOENT;
35                 goto out;
36         }
37
38         path = btrfs_alloc_path();
39         if (!path) {
40                 ret = -ENOMEM;
41                 goto out;
42         }
43
44         btrfs_uuid_to_key(uuid, type, &key);
45         ret = btrfs_search_slot(NULL, uuid_root, &key, path, 0, 0);
46         if (ret < 0) {
47                 goto out;
48         } else if (ret > 0) {
49                 ret = -ENOENT;
50                 goto out;
51         }
52
53         eb = path->nodes[0];
54         slot = path->slots[0];
55         item_size = btrfs_item_size(eb, slot);
56         offset = btrfs_item_ptr_offset(eb, slot);
57         ret = -ENOENT;
58
59         if (!IS_ALIGNED(item_size, sizeof(u64))) {
60                 btrfs_warn(uuid_root->fs_info,
61                            "uuid item with illegal size %lu!",
62                            (unsigned long)item_size);
63                 goto out;
64         }
65         while (item_size) {
66                 __le64 data;
67
68                 read_extent_buffer(eb, &data, offset, sizeof(data));
69                 if (le64_to_cpu(data) == subid) {
70                         ret = 0;
71                         break;
72                 }
73                 offset += sizeof(data);
74                 item_size -= sizeof(data);
75         }
76
77 out:
78         btrfs_free_path(path);
79         return ret;
80 }
81
82 int btrfs_uuid_tree_add(struct btrfs_trans_handle *trans, u8 *uuid, u8 type,
83                         u64 subid_cpu)
84 {
85         struct btrfs_fs_info *fs_info = trans->fs_info;
86         struct btrfs_root *uuid_root = fs_info->uuid_root;
87         int ret;
88         struct btrfs_path *path = NULL;
89         struct btrfs_key key;
90         struct extent_buffer *eb;
91         int slot;
92         unsigned long offset;
93         __le64 subid_le;
94
95         ret = btrfs_uuid_tree_lookup(uuid_root, uuid, type, subid_cpu);
96         if (ret != -ENOENT)
97                 return ret;
98
99         if (WARN_ON_ONCE(!uuid_root)) {
100                 ret = -EINVAL;
101                 goto out;
102         }
103
104         btrfs_uuid_to_key(uuid, type, &key);
105
106         path = btrfs_alloc_path();
107         if (!path) {
108                 ret = -ENOMEM;
109                 goto out;
110         }
111
112         ret = btrfs_insert_empty_item(trans, uuid_root, path, &key,
113                                       sizeof(subid_le));
114         if (ret >= 0) {
115                 /* Add an item for the type for the first time */
116                 eb = path->nodes[0];
117                 slot = path->slots[0];
118                 offset = btrfs_item_ptr_offset(eb, slot);
119         } else if (ret == -EEXIST) {
120                 /*
121                  * An item with that type already exists.
122                  * Extend the item and store the new subid at the end.
123                  */
124                 btrfs_extend_item(path, sizeof(subid_le));
125                 eb = path->nodes[0];
126                 slot = path->slots[0];
127                 offset = btrfs_item_ptr_offset(eb, slot);
128                 offset += btrfs_item_size(eb, slot) - sizeof(subid_le);
129         } else {
130                 btrfs_warn(fs_info,
131                            "insert uuid item failed %d (0x%016llx, 0x%016llx) type %u!",
132                            ret, key.objectid, key.offset, type);
133                 goto out;
134         }
135
136         ret = 0;
137         subid_le = cpu_to_le64(subid_cpu);
138         write_extent_buffer(eb, &subid_le, offset, sizeof(subid_le));
139         btrfs_mark_buffer_dirty(eb);
140
141 out:
142         btrfs_free_path(path);
143         return ret;
144 }
145
146 int btrfs_uuid_tree_remove(struct btrfs_trans_handle *trans, u8 *uuid, u8 type,
147                         u64 subid)
148 {
149         struct btrfs_fs_info *fs_info = trans->fs_info;
150         struct btrfs_root *uuid_root = fs_info->uuid_root;
151         int ret;
152         struct btrfs_path *path = NULL;
153         struct btrfs_key key;
154         struct extent_buffer *eb;
155         int slot;
156         unsigned long offset;
157         u32 item_size;
158         unsigned long move_dst;
159         unsigned long move_src;
160         unsigned long move_len;
161
162         if (WARN_ON_ONCE(!uuid_root)) {
163                 ret = -EINVAL;
164                 goto out;
165         }
166
167         btrfs_uuid_to_key(uuid, type, &key);
168
169         path = btrfs_alloc_path();
170         if (!path) {
171                 ret = -ENOMEM;
172                 goto out;
173         }
174
175         ret = btrfs_search_slot(trans, uuid_root, &key, path, -1, 1);
176         if (ret < 0) {
177                 btrfs_warn(fs_info, "error %d while searching for uuid item!",
178                            ret);
179                 goto out;
180         }
181         if (ret > 0) {
182                 ret = -ENOENT;
183                 goto out;
184         }
185
186         eb = path->nodes[0];
187         slot = path->slots[0];
188         offset = btrfs_item_ptr_offset(eb, slot);
189         item_size = btrfs_item_size(eb, slot);
190         if (!IS_ALIGNED(item_size, sizeof(u64))) {
191                 btrfs_warn(fs_info, "uuid item with illegal size %lu!",
192                            (unsigned long)item_size);
193                 ret = -ENOENT;
194                 goto out;
195         }
196         while (item_size) {
197                 __le64 read_subid;
198
199                 read_extent_buffer(eb, &read_subid, offset, sizeof(read_subid));
200                 if (le64_to_cpu(read_subid) == subid)
201                         break;
202                 offset += sizeof(read_subid);
203                 item_size -= sizeof(read_subid);
204         }
205
206         if (!item_size) {
207                 ret = -ENOENT;
208                 goto out;
209         }
210
211         item_size = btrfs_item_size(eb, slot);
212         if (item_size == sizeof(subid)) {
213                 ret = btrfs_del_item(trans, uuid_root, path);
214                 goto out;
215         }
216
217         move_dst = offset;
218         move_src = offset + sizeof(subid);
219         move_len = item_size - (move_src - btrfs_item_ptr_offset(eb, slot));
220         memmove_extent_buffer(eb, move_dst, move_src, move_len);
221         btrfs_truncate_item(path, item_size - sizeof(subid), 1);
222
223 out:
224         btrfs_free_path(path);
225         return ret;
226 }
227
228 static int btrfs_uuid_iter_rem(struct btrfs_root *uuid_root, u8 *uuid, u8 type,
229                                u64 subid)
230 {
231         struct btrfs_trans_handle *trans;
232         int ret;
233
234         /* 1 - for the uuid item */
235         trans = btrfs_start_transaction(uuid_root, 1);
236         if (IS_ERR(trans)) {
237                 ret = PTR_ERR(trans);
238                 goto out;
239         }
240
241         ret = btrfs_uuid_tree_remove(trans, uuid, type, subid);
242         btrfs_end_transaction(trans);
243
244 out:
245         return ret;
246 }
247
248 /*
249  * Check if there's an matching subvolume for given UUID
250  *
251  * Return:
252  * 0    check succeeded, the entry is not outdated
253  * > 0  if the check failed, the caller should remove the entry
254  * < 0  if an error occurred
255  */
256 static int btrfs_check_uuid_tree_entry(struct btrfs_fs_info *fs_info,
257                                        u8 *uuid, u8 type, u64 subvolid)
258 {
259         int ret = 0;
260         struct btrfs_root *subvol_root;
261
262         if (type != BTRFS_UUID_KEY_SUBVOL &&
263             type != BTRFS_UUID_KEY_RECEIVED_SUBVOL)
264                 goto out;
265
266         subvol_root = btrfs_get_fs_root(fs_info, subvolid, true);
267         if (IS_ERR(subvol_root)) {
268                 ret = PTR_ERR(subvol_root);
269                 if (ret == -ENOENT)
270                         ret = 1;
271                 goto out;
272         }
273
274         switch (type) {
275         case BTRFS_UUID_KEY_SUBVOL:
276                 if (memcmp(uuid, subvol_root->root_item.uuid, BTRFS_UUID_SIZE))
277                         ret = 1;
278                 break;
279         case BTRFS_UUID_KEY_RECEIVED_SUBVOL:
280                 if (memcmp(uuid, subvol_root->root_item.received_uuid,
281                            BTRFS_UUID_SIZE))
282                         ret = 1;
283                 break;
284         }
285         btrfs_put_root(subvol_root);
286 out:
287         return ret;
288 }
289
290 int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info)
291 {
292         struct btrfs_root *root = fs_info->uuid_root;
293         struct btrfs_key key;
294         struct btrfs_path *path;
295         int ret = 0;
296         struct extent_buffer *leaf;
297         int slot;
298         u32 item_size;
299         unsigned long offset;
300
301         path = btrfs_alloc_path();
302         if (!path) {
303                 ret = -ENOMEM;
304                 goto out;
305         }
306
307         key.objectid = 0;
308         key.type = 0;
309         key.offset = 0;
310
311 again_search_slot:
312         ret = btrfs_search_forward(root, &key, path, BTRFS_OLDEST_GENERATION);
313         if (ret) {
314                 if (ret > 0)
315                         ret = 0;
316                 goto out;
317         }
318
319         while (1) {
320                 if (btrfs_fs_closing(fs_info)) {
321                         ret = -EINTR;
322                         goto out;
323                 }
324                 cond_resched();
325                 leaf = path->nodes[0];
326                 slot = path->slots[0];
327                 btrfs_item_key_to_cpu(leaf, &key, slot);
328
329                 if (key.type != BTRFS_UUID_KEY_SUBVOL &&
330                     key.type != BTRFS_UUID_KEY_RECEIVED_SUBVOL)
331                         goto skip;
332
333                 offset = btrfs_item_ptr_offset(leaf, slot);
334                 item_size = btrfs_item_size(leaf, slot);
335                 if (!IS_ALIGNED(item_size, sizeof(u64))) {
336                         btrfs_warn(fs_info,
337                                    "uuid item with illegal size %lu!",
338                                    (unsigned long)item_size);
339                         goto skip;
340                 }
341                 while (item_size) {
342                         u8 uuid[BTRFS_UUID_SIZE];
343                         __le64 subid_le;
344                         u64 subid_cpu;
345
346                         put_unaligned_le64(key.objectid, uuid);
347                         put_unaligned_le64(key.offset, uuid + sizeof(u64));
348                         read_extent_buffer(leaf, &subid_le, offset,
349                                            sizeof(subid_le));
350                         subid_cpu = le64_to_cpu(subid_le);
351                         ret = btrfs_check_uuid_tree_entry(fs_info, uuid,
352                                                           key.type, subid_cpu);
353                         if (ret < 0)
354                                 goto out;
355                         if (ret > 0) {
356                                 btrfs_release_path(path);
357                                 ret = btrfs_uuid_iter_rem(root, uuid, key.type,
358                                                           subid_cpu);
359                                 if (ret == 0) {
360                                         /*
361                                          * this might look inefficient, but the
362                                          * justification is that it is an
363                                          * exception that check_func returns 1,
364                                          * and that in the regular case only one
365                                          * entry per UUID exists.
366                                          */
367                                         goto again_search_slot;
368                                 }
369                                 if (ret < 0 && ret != -ENOENT)
370                                         goto out;
371                                 key.offset++;
372                                 goto again_search_slot;
373                         }
374                         item_size -= sizeof(subid_le);
375                         offset += sizeof(subid_le);
376                 }
377
378 skip:
379                 ret = btrfs_next_item(root, path);
380                 if (ret == 0)
381                         continue;
382                 else if (ret > 0)
383                         ret = 0;
384                 break;
385         }
386
387 out:
388         btrfs_free_path(path);
389         return ret;
390 }