Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <errno.h>
4 : : #include <fcntl.h>
5 : : #include <inttypes.h>
6 : : #include <linux/btrfs_tree.h>
7 : : #include <linux/fs.h>
8 : : #include <linux/loop.h>
9 : : #include <linux/magic.h>
10 : : #include <stddef.h>
11 : : #include <stdio.h>
12 : : #include <stdlib.h>
13 : : #include <string.h>
14 : : #include <sys/ioctl.h>
15 : : #include <sys/stat.h>
16 : : #include <sys/statfs.h>
17 : : #include <sys/sysmacros.h>
18 : : #include <unistd.h>
19 : :
20 : : #include "alloc-util.h"
21 : : #include "blockdev-util.h"
22 : : #include "btrfs-util.h"
23 : : #include "chattr-util.h"
24 : : #include "copy.h"
25 : : #include "device-nodes.h"
26 : : #include "fd-util.h"
27 : : #include "fileio.h"
28 : : #include "fs-util.h"
29 : : #include "io-util.h"
30 : : #include "macro.h"
31 : : #include "path-util.h"
32 : : #include "rm-rf.h"
33 : : #include "smack-util.h"
34 : : #include "sparse-endian.h"
35 : : #include "stat-util.h"
36 : : #include "string-util.h"
37 : : #include "time-util.h"
38 : : #include "util.h"
39 : :
40 : : /* WARNING: Be careful with file system ioctls! When we get an fd, we
41 : : * need to make sure it either refers to only a regular file or
42 : : * directory, or that it is located on btrfs, before invoking any
43 : : * btrfs ioctls. The ioctl numbers are reused by some device drivers
44 : : * (such as DRM), and hence might have bad effects when invoked on
45 : : * device nodes (that reference drivers) rather than fds to normal
46 : : * files or directories. */
47 : :
48 : 0 : static int validate_subvolume_name(const char *name) {
49 : :
50 [ # # ]: 0 : if (!filename_is_valid(name))
51 : 0 : return -EINVAL;
52 : :
53 [ # # ]: 0 : if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
54 : 0 : return -E2BIG;
55 : :
56 : 0 : return 0;
57 : : }
58 : :
59 : 0 : static int extract_subvolume_name(const char *path, const char **subvolume) {
60 : : const char *fn;
61 : : int r;
62 : :
63 [ # # ]: 0 : assert(path);
64 [ # # ]: 0 : assert(subvolume);
65 : :
66 : 0 : fn = basename(path);
67 : :
68 : 0 : r = validate_subvolume_name(fn);
69 [ # # ]: 0 : if (r < 0)
70 : 0 : return r;
71 : :
72 : 0 : *subvolume = fn;
73 : 0 : return 0;
74 : : }
75 : :
76 : 100 : int btrfs_is_filesystem(int fd) {
77 : : struct statfs sfs;
78 : :
79 [ - + ]: 100 : assert(fd >= 0);
80 : :
81 [ - + ]: 100 : if (fstatfs(fd, &sfs) < 0)
82 : 0 : return -errno;
83 : :
84 : 100 : return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
85 : : }
86 : :
87 : 0 : int btrfs_is_subvol_fd(int fd) {
88 : : struct stat st;
89 : :
90 [ # # ]: 0 : assert(fd >= 0);
91 : :
92 : : /* On btrfs subvolumes always have the inode 256 */
93 : :
94 [ # # ]: 0 : if (fstat(fd, &st) < 0)
95 : 0 : return -errno;
96 : :
97 [ # # # # ]: 0 : if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
98 : 0 : return 0;
99 : :
100 : 0 : return btrfs_is_filesystem(fd);
101 : : }
102 : :
103 : 0 : int btrfs_is_subvol(const char *path) {
104 : 0 : _cleanup_close_ int fd = -1;
105 : :
106 [ # # ]: 0 : assert(path);
107 : :
108 : 0 : fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
109 [ # # ]: 0 : if (fd < 0)
110 : 0 : return -errno;
111 : :
112 : 0 : return btrfs_is_subvol_fd(fd);
113 : : }
114 : :
115 : 0 : int btrfs_subvol_make_fd(int fd, const char *subvolume) {
116 : 0 : struct btrfs_ioctl_vol_args args = {};
117 : 0 : _cleanup_close_ int real_fd = -1;
118 : : int r;
119 : :
120 [ # # ]: 0 : assert(subvolume);
121 : :
122 : 0 : r = validate_subvolume_name(subvolume);
123 [ # # ]: 0 : if (r < 0)
124 : 0 : return r;
125 : :
126 : 0 : r = fcntl(fd, F_GETFL);
127 [ # # ]: 0 : if (r < 0)
128 : 0 : return -errno;
129 [ # # ]: 0 : if (FLAGS_SET(r, O_PATH)) {
130 : : /* An O_PATH fd was specified, let's convert here to a proper one, as btrfs ioctl's can't deal with
131 : : * O_PATH. */
132 : :
133 : 0 : real_fd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
134 [ # # ]: 0 : if (real_fd < 0)
135 : 0 : return real_fd;
136 : :
137 : 0 : fd = real_fd;
138 : : }
139 : :
140 : 0 : strncpy(args.name, subvolume, sizeof(args.name)-1);
141 : :
142 [ # # ]: 0 : if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
143 : 0 : return -errno;
144 : :
145 : 0 : return 0;
146 : : }
147 : :
148 : 0 : int btrfs_subvol_make(const char *path) {
149 : 0 : _cleanup_close_ int fd = -1;
150 : : const char *subvolume;
151 : : int r;
152 : :
153 [ # # ]: 0 : assert(path);
154 : :
155 : 0 : r = extract_subvolume_name(path, &subvolume);
156 [ # # ]: 0 : if (r < 0)
157 : 0 : return r;
158 : :
159 : 0 : fd = open_parent(path, O_CLOEXEC, 0);
160 [ # # ]: 0 : if (fd < 0)
161 : 0 : return fd;
162 : :
163 : 0 : return btrfs_subvol_make_fd(fd, subvolume);
164 : : }
165 : :
166 : 0 : int btrfs_subvol_set_read_only_fd(int fd, bool b) {
167 : : uint64_t flags, nflags;
168 : : struct stat st;
169 : :
170 [ # # ]: 0 : assert(fd >= 0);
171 : :
172 [ # # ]: 0 : if (fstat(fd, &st) < 0)
173 : 0 : return -errno;
174 : :
175 [ # # # # ]: 0 : if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
176 : 0 : return -EINVAL;
177 : :
178 [ # # ]: 0 : if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
179 : 0 : return -errno;
180 : :
181 [ # # ]: 0 : if (b)
182 : 0 : nflags = flags | BTRFS_SUBVOL_RDONLY;
183 : : else
184 : 0 : nflags = flags & ~BTRFS_SUBVOL_RDONLY;
185 : :
186 [ # # ]: 0 : if (flags == nflags)
187 : 0 : return 0;
188 : :
189 [ # # ]: 0 : if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0)
190 : 0 : return -errno;
191 : :
192 : 0 : return 0;
193 : : }
194 : :
195 : 0 : int btrfs_subvol_set_read_only(const char *path, bool b) {
196 : 0 : _cleanup_close_ int fd = -1;
197 : :
198 : 0 : fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
199 [ # # ]: 0 : if (fd < 0)
200 : 0 : return -errno;
201 : :
202 : 0 : return btrfs_subvol_set_read_only_fd(fd, b);
203 : : }
204 : :
205 : 0 : int btrfs_subvol_get_read_only_fd(int fd) {
206 : : uint64_t flags;
207 : : struct stat st;
208 : :
209 [ # # ]: 0 : assert(fd >= 0);
210 : :
211 [ # # ]: 0 : if (fstat(fd, &st) < 0)
212 : 0 : return -errno;
213 : :
214 [ # # # # ]: 0 : if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
215 : 0 : return -EINVAL;
216 : :
217 [ # # ]: 0 : if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
218 : 0 : return -errno;
219 : :
220 : 0 : return !!(flags & BTRFS_SUBVOL_RDONLY);
221 : : }
222 : :
223 : 44 : int btrfs_reflink(int infd, int outfd) {
224 : : int r;
225 : :
226 [ - + ]: 44 : assert(infd >= 0);
227 [ - + ]: 44 : assert(outfd >= 0);
228 : :
229 : : /* Make sure we invoke the ioctl on a regular file, so that no device driver accidentally gets it. */
230 : :
231 : 44 : r = fd_verify_regular(outfd);
232 [ - + ]: 44 : if (r < 0)
233 : 0 : return r;
234 : :
235 [ + - ]: 44 : if (ioctl(outfd, BTRFS_IOC_CLONE, infd) < 0)
236 : 44 : return -errno;
237 : :
238 : 0 : return 0;
239 : : }
240 : :
241 : 16 : int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) {
242 : 16 : struct btrfs_ioctl_clone_range_args args = {
243 : : .src_fd = infd,
244 : : .src_offset = in_offset,
245 : : .src_length = sz,
246 : : .dest_offset = out_offset,
247 : : };
248 : : int r;
249 : :
250 [ - + ]: 16 : assert(infd >= 0);
251 [ - + ]: 16 : assert(outfd >= 0);
252 [ - + ]: 16 : assert(sz > 0);
253 : :
254 : 16 : r = fd_verify_regular(outfd);
255 [ - + ]: 16 : if (r < 0)
256 : 0 : return r;
257 : :
258 [ + - ]: 16 : if (ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args) < 0)
259 : 16 : return -errno;
260 : :
261 : 0 : return 0;
262 : : }
263 : :
264 : 0 : int btrfs_get_block_device_fd(int fd, dev_t *dev) {
265 : 0 : struct btrfs_ioctl_fs_info_args fsi = {};
266 : : uint64_t id;
267 : : int r;
268 : :
269 [ # # ]: 0 : assert(fd >= 0);
270 [ # # ]: 0 : assert(dev);
271 : :
272 : 0 : r = btrfs_is_filesystem(fd);
273 [ # # ]: 0 : if (r < 0)
274 : 0 : return r;
275 [ # # ]: 0 : if (!r)
276 : 0 : return -ENOTTY;
277 : :
278 [ # # ]: 0 : if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
279 : 0 : return -errno;
280 : :
281 : : /* We won't do this for btrfs RAID */
282 [ # # ]: 0 : if (fsi.num_devices != 1) {
283 : 0 : *dev = 0;
284 : 0 : return 0;
285 : : }
286 : :
287 [ # # ]: 0 : for (id = 1; id <= fsi.max_id; id++) {
288 : 0 : struct btrfs_ioctl_dev_info_args di = {
289 : : .devid = id,
290 : : };
291 : : struct stat st;
292 : :
293 [ # # ]: 0 : if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
294 [ # # ]: 0 : if (errno == ENODEV)
295 : 0 : continue;
296 : :
297 : 0 : return -errno;
298 : : }
299 : :
300 [ # # ]: 0 : if (stat((char*) di.path, &st) < 0)
301 : 0 : return -errno;
302 : :
303 [ # # ]: 0 : if (!S_ISBLK(st.st_mode))
304 : 0 : return -ENODEV;
305 : :
306 [ # # ]: 0 : if (major(st.st_rdev) == 0)
307 : 0 : return -ENODEV;
308 : :
309 : 0 : *dev = st.st_rdev;
310 : 0 : return 1;
311 : : }
312 : :
313 : 0 : return -ENODEV;
314 : : }
315 : :
316 : 0 : int btrfs_get_block_device(const char *path, dev_t *dev) {
317 : 0 : _cleanup_close_ int fd = -1;
318 : :
319 [ # # ]: 0 : assert(path);
320 [ # # ]: 0 : assert(dev);
321 : :
322 : 0 : fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
323 [ # # ]: 0 : if (fd < 0)
324 : 0 : return -errno;
325 : :
326 : 0 : return btrfs_get_block_device_fd(fd, dev);
327 : : }
328 : :
329 : 0 : int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
330 : 0 : struct btrfs_ioctl_ino_lookup_args args = {
331 : : .objectid = BTRFS_FIRST_FREE_OBJECTID
332 : : };
333 : : int r;
334 : :
335 [ # # ]: 0 : assert(fd >= 0);
336 [ # # ]: 0 : assert(ret);
337 : :
338 : 0 : r = btrfs_is_filesystem(fd);
339 [ # # ]: 0 : if (r < 0)
340 : 0 : return r;
341 [ # # ]: 0 : if (!r)
342 : 0 : return -ENOTTY;
343 : :
344 [ # # ]: 0 : if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
345 : 0 : return -errno;
346 : :
347 : 0 : *ret = args.treeid;
348 : 0 : return 0;
349 : : }
350 : :
351 : 0 : int btrfs_subvol_get_id(int fd, const char *subvol, uint64_t *ret) {
352 : 0 : _cleanup_close_ int subvol_fd = -1;
353 : :
354 [ # # ]: 0 : assert(fd >= 0);
355 [ # # ]: 0 : assert(ret);
356 : :
357 : 0 : subvol_fd = openat(fd, subvol, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
358 [ # # ]: 0 : if (subvol_fd < 0)
359 : 0 : return -errno;
360 : :
361 : 0 : return btrfs_subvol_get_id_fd(subvol_fd, ret);
362 : : }
363 : :
364 : 0 : static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) {
365 [ # # ]: 0 : assert(args);
366 : :
367 : : /* the objectid, type, offset together make up the btrfs key,
368 : : * which is considered a single 136byte integer when
369 : : * comparing. This call increases the counter by one, dealing
370 : : * with the overflow between the overflows */
371 : :
372 [ # # ]: 0 : if (args->key.min_offset < (uint64_t) -1) {
373 : 0 : args->key.min_offset++;
374 : 0 : return true;
375 : : }
376 : :
377 [ # # ]: 0 : if (args->key.min_type < (uint8_t) -1) {
378 : 0 : args->key.min_type++;
379 : 0 : args->key.min_offset = 0;
380 : 0 : return true;
381 : : }
382 : :
383 [ # # ]: 0 : if (args->key.min_objectid < (uint64_t) -1) {
384 : 0 : args->key.min_objectid++;
385 : 0 : args->key.min_offset = 0;
386 : 0 : args->key.min_type = 0;
387 : 0 : return true;
388 : : }
389 : :
390 : 0 : return 0;
391 : : }
392 : :
393 : 0 : static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) {
394 [ # # ]: 0 : assert(args);
395 [ # # ]: 0 : assert(h);
396 : :
397 : 0 : args->key.min_objectid = h->objectid;
398 : 0 : args->key.min_type = h->type;
399 : 0 : args->key.min_offset = h->offset;
400 : 0 : }
401 : :
402 : 0 : static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) {
403 : : int r;
404 : :
405 [ # # ]: 0 : assert(args);
406 : :
407 : : /* Compare min and max */
408 : :
409 [ # # ]: 0 : r = CMP(args->key.min_objectid, args->key.max_objectid);
410 [ # # ]: 0 : if (r != 0)
411 : 0 : return r;
412 : :
413 [ # # ]: 0 : r = CMP(args->key.min_type, args->key.max_type);
414 [ # # ]: 0 : if (r != 0)
415 : 0 : return r;
416 : :
417 [ # # ]: 0 : return CMP(args->key.min_offset, args->key.max_offset);
418 : : }
419 : :
420 : : #define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) \
421 : : for ((i) = 0, \
422 : : (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \
423 : : (i) < (args).key.nr_items; \
424 : : (i)++, \
425 : : (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len))
426 : :
427 : : #define BTRFS_IOCTL_SEARCH_HEADER_BODY(sh) \
428 : : ((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header)))
429 : :
430 : 0 : int btrfs_subvol_get_info_fd(int fd, uint64_t subvol_id, BtrfsSubvolInfo *ret) {
431 : 0 : struct btrfs_ioctl_search_args args = {
432 : : /* Tree of tree roots */
433 : : .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
434 : :
435 : : /* Look precisely for the subvolume items */
436 : : .key.min_type = BTRFS_ROOT_ITEM_KEY,
437 : : .key.max_type = BTRFS_ROOT_ITEM_KEY,
438 : :
439 : : .key.min_offset = 0,
440 : : .key.max_offset = (uint64_t) -1,
441 : :
442 : : /* No restrictions on the other components */
443 : : .key.min_transid = 0,
444 : : .key.max_transid = (uint64_t) -1,
445 : : };
446 : :
447 : 0 : bool found = false;
448 : : int r;
449 : :
450 [ # # ]: 0 : assert(fd >= 0);
451 [ # # ]: 0 : assert(ret);
452 : :
453 [ # # ]: 0 : if (subvol_id == 0) {
454 : 0 : r = btrfs_subvol_get_id_fd(fd, &subvol_id);
455 [ # # ]: 0 : if (r < 0)
456 : 0 : return r;
457 : : } else {
458 : 0 : r = btrfs_is_filesystem(fd);
459 [ # # ]: 0 : if (r < 0)
460 : 0 : return r;
461 [ # # ]: 0 : if (!r)
462 : 0 : return -ENOTTY;
463 : : }
464 : :
465 : 0 : args.key.min_objectid = args.key.max_objectid = subvol_id;
466 : :
467 [ # # ]: 0 : while (btrfs_ioctl_search_args_compare(&args) <= 0) {
468 : : const struct btrfs_ioctl_search_header *sh;
469 : : unsigned i;
470 : :
471 : 0 : args.key.nr_items = 256;
472 [ # # ]: 0 : if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
473 : 0 : return -errno;
474 : :
475 [ # # ]: 0 : if (args.key.nr_items <= 0)
476 : 0 : break;
477 : :
478 [ # # ]: 0 : FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
479 : :
480 : : const struct btrfs_root_item *ri;
481 : :
482 : : /* Make sure we start the next search at least from this entry */
483 : 0 : btrfs_ioctl_search_args_set(&args, sh);
484 : :
485 [ # # ]: 0 : if (sh->objectid != subvol_id)
486 : 0 : continue;
487 [ # # ]: 0 : if (sh->type != BTRFS_ROOT_ITEM_KEY)
488 : 0 : continue;
489 : :
490 : : /* Older versions of the struct lacked the otime setting */
491 [ # # ]: 0 : if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
492 : 0 : continue;
493 : :
494 : 0 : ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
495 : :
496 : 0 : ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
497 : 0 : (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
498 : :
499 : 0 : ret->subvol_id = subvol_id;
500 : 0 : ret->read_only = le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY;
501 : :
502 : : assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid));
503 : 0 : memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
504 : 0 : memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));
505 : :
506 : 0 : found = true;
507 : 0 : goto finish;
508 : : }
509 : :
510 : : /* Increase search key by one, to read the next item, if we can. */
511 [ # # ]: 0 : if (!btrfs_ioctl_search_args_inc(&args))
512 : 0 : break;
513 : : }
514 : :
515 : 0 : finish:
516 [ # # ]: 0 : if (!found)
517 : 0 : return -ENODATA;
518 : :
519 : 0 : return 0;
520 : : }
521 : :
522 : 0 : int btrfs_qgroup_get_quota_fd(int fd, uint64_t qgroupid, BtrfsQuotaInfo *ret) {
523 : :
524 : 0 : struct btrfs_ioctl_search_args args = {
525 : : /* Tree of quota items */
526 : : .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
527 : :
528 : : /* The object ID is always 0 */
529 : : .key.min_objectid = 0,
530 : : .key.max_objectid = 0,
531 : :
532 : : /* Look precisely for the quota items */
533 : : .key.min_type = BTRFS_QGROUP_STATUS_KEY,
534 : : .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
535 : :
536 : : /* No restrictions on the other components */
537 : : .key.min_transid = 0,
538 : : .key.max_transid = (uint64_t) -1,
539 : : };
540 : :
541 : 0 : bool found_info = false, found_limit = false;
542 : : int r;
543 : :
544 [ # # ]: 0 : assert(fd >= 0);
545 [ # # ]: 0 : assert(ret);
546 : :
547 [ # # ]: 0 : if (qgroupid == 0) {
548 : 0 : r = btrfs_subvol_get_id_fd(fd, &qgroupid);
549 [ # # ]: 0 : if (r < 0)
550 : 0 : return r;
551 : : } else {
552 : 0 : r = btrfs_is_filesystem(fd);
553 [ # # ]: 0 : if (r < 0)
554 : 0 : return r;
555 [ # # ]: 0 : if (!r)
556 : 0 : return -ENOTTY;
557 : : }
558 : :
559 : 0 : args.key.min_offset = args.key.max_offset = qgroupid;
560 : :
561 [ # # ]: 0 : while (btrfs_ioctl_search_args_compare(&args) <= 0) {
562 : : const struct btrfs_ioctl_search_header *sh;
563 : : unsigned i;
564 : :
565 : 0 : args.key.nr_items = 256;
566 [ # # ]: 0 : if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) {
567 [ # # ]: 0 : if (errno == ENOENT) /* quota tree is missing: quota disabled */
568 : 0 : break;
569 : :
570 : 0 : return -errno;
571 : : }
572 : :
573 [ # # ]: 0 : if (args.key.nr_items <= 0)
574 : 0 : break;
575 : :
576 [ # # ]: 0 : FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
577 : :
578 : : /* Make sure we start the next search at least from this entry */
579 : 0 : btrfs_ioctl_search_args_set(&args, sh);
580 : :
581 [ # # ]: 0 : if (sh->objectid != 0)
582 : 0 : continue;
583 [ # # ]: 0 : if (sh->offset != qgroupid)
584 : 0 : continue;
585 : :
586 [ # # ]: 0 : if (sh->type == BTRFS_QGROUP_INFO_KEY) {
587 : 0 : const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
588 : :
589 : 0 : ret->referenced = le64toh(qii->rfer);
590 : 0 : ret->exclusive = le64toh(qii->excl);
591 : :
592 : 0 : found_info = true;
593 : :
594 [ # # ]: 0 : } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
595 : 0 : const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
596 : :
597 [ # # ]: 0 : if (le64toh(qli->flags) & BTRFS_QGROUP_LIMIT_MAX_RFER)
598 : 0 : ret->referenced_max = le64toh(qli->max_rfer);
599 : : else
600 : 0 : ret->referenced_max = (uint64_t) -1;
601 : :
602 [ # # ]: 0 : if (le64toh(qli->flags) & BTRFS_QGROUP_LIMIT_MAX_EXCL)
603 : 0 : ret->exclusive_max = le64toh(qli->max_excl);
604 : : else
605 : 0 : ret->exclusive_max = (uint64_t) -1;
606 : :
607 : 0 : found_limit = true;
608 : : }
609 : :
610 [ # # # # ]: 0 : if (found_info && found_limit)
611 : 0 : goto finish;
612 : : }
613 : :
614 : : /* Increase search key by one, to read the next item, if we can. */
615 [ # # ]: 0 : if (!btrfs_ioctl_search_args_inc(&args))
616 : 0 : break;
617 : : }
618 : :
619 : 0 : finish:
620 [ # # # # ]: 0 : if (!found_limit && !found_info)
621 : 0 : return -ENODATA;
622 : :
623 [ # # ]: 0 : if (!found_info) {
624 : 0 : ret->referenced = (uint64_t) -1;
625 : 0 : ret->exclusive = (uint64_t) -1;
626 : : }
627 : :
628 [ # # ]: 0 : if (!found_limit) {
629 : 0 : ret->referenced_max = (uint64_t) -1;
630 : 0 : ret->exclusive_max = (uint64_t) -1;
631 : : }
632 : :
633 : 0 : return 0;
634 : : }
635 : :
636 : 0 : int btrfs_qgroup_get_quota(const char *path, uint64_t qgroupid, BtrfsQuotaInfo *ret) {
637 : 0 : _cleanup_close_ int fd = -1;
638 : :
639 : 0 : fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
640 [ # # ]: 0 : if (fd < 0)
641 : 0 : return -errno;
642 : :
643 : 0 : return btrfs_qgroup_get_quota_fd(fd, qgroupid, ret);
644 : : }
645 : :
646 : 0 : int btrfs_subvol_find_subtree_qgroup(int fd, uint64_t subvol_id, uint64_t *ret) {
647 : 0 : uint64_t level, lowest = (uint64_t) -1, lowest_qgroupid = 0;
648 : 0 : _cleanup_free_ uint64_t *qgroups = NULL;
649 : : int r, n, i;
650 : :
651 [ # # ]: 0 : assert(fd >= 0);
652 [ # # ]: 0 : assert(ret);
653 : :
654 : : /* This finds the "subtree" qgroup for a specific
655 : : * subvolume. This only works for subvolumes that have been
656 : : * prepared with btrfs_subvol_auto_qgroup_fd() with
657 : : * insert_intermediary_qgroup=true (or equivalent). For others
658 : : * it will return the leaf qgroup instead. The two cases may
659 : : * be distuingished via the return value, which is 1 in case
660 : : * an appropriate "subtree" qgroup was found, and 0
661 : : * otherwise. */
662 : :
663 [ # # ]: 0 : if (subvol_id == 0) {
664 : 0 : r = btrfs_subvol_get_id_fd(fd, &subvol_id);
665 [ # # ]: 0 : if (r < 0)
666 : 0 : return r;
667 : : }
668 : :
669 : 0 : r = btrfs_qgroupid_split(subvol_id, &level, NULL);
670 [ # # ]: 0 : if (r < 0)
671 : 0 : return r;
672 [ # # ]: 0 : if (level != 0) /* Input must be a leaf qgroup */
673 : 0 : return -EINVAL;
674 : :
675 : 0 : n = btrfs_qgroup_find_parents(fd, subvol_id, &qgroups);
676 [ # # ]: 0 : if (n < 0)
677 : 0 : return n;
678 : :
679 [ # # ]: 0 : for (i = 0; i < n; i++) {
680 : : uint64_t id;
681 : :
682 : 0 : r = btrfs_qgroupid_split(qgroups[i], &level, &id);
683 [ # # ]: 0 : if (r < 0)
684 : 0 : return r;
685 : :
686 [ # # ]: 0 : if (id != subvol_id)
687 : 0 : continue;
688 : :
689 [ # # # # ]: 0 : if (lowest == (uint64_t) -1 || level < lowest) {
690 : 0 : lowest_qgroupid = qgroups[i];
691 : 0 : lowest = level;
692 : : }
693 : : }
694 : :
695 [ # # ]: 0 : if (lowest == (uint64_t) -1) {
696 : : /* No suitable higher-level qgroup found, let's return
697 : : * the leaf qgroup instead, and indicate that with the
698 : : * return value. */
699 : :
700 : 0 : *ret = subvol_id;
701 : 0 : return 0;
702 : : }
703 : :
704 : 0 : *ret = lowest_qgroupid;
705 : 0 : return 1;
706 : : }
707 : :
708 : 0 : int btrfs_subvol_get_subtree_quota_fd(int fd, uint64_t subvol_id, BtrfsQuotaInfo *ret) {
709 : : uint64_t qgroupid;
710 : : int r;
711 : :
712 [ # # ]: 0 : assert(fd >= 0);
713 [ # # ]: 0 : assert(ret);
714 : :
715 : : /* This determines the quota data of the qgroup with the
716 : : * lowest level, that shares the id part with the specified
717 : : * subvolume. This is useful for determining the quota data
718 : : * for entire subvolume subtrees, as long as the subtrees have
719 : : * been set up with btrfs_qgroup_subvol_auto_fd() or in a
720 : : * compatible way */
721 : :
722 : 0 : r = btrfs_subvol_find_subtree_qgroup(fd, subvol_id, &qgroupid);
723 [ # # ]: 0 : if (r < 0)
724 : 0 : return r;
725 : :
726 : 0 : return btrfs_qgroup_get_quota_fd(fd, qgroupid, ret);
727 : : }
728 : :
729 : 0 : int btrfs_subvol_get_subtree_quota(const char *path, uint64_t subvol_id, BtrfsQuotaInfo *ret) {
730 : 0 : _cleanup_close_ int fd = -1;
731 : :
732 : 0 : fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
733 [ # # ]: 0 : if (fd < 0)
734 : 0 : return -errno;
735 : :
736 : 0 : return btrfs_subvol_get_subtree_quota_fd(fd, subvol_id, ret);
737 : : }
738 : :
739 : 8 : int btrfs_defrag_fd(int fd) {
740 : : int r;
741 : :
742 [ - + ]: 8 : assert(fd >= 0);
743 : :
744 : 8 : r = fd_verify_regular(fd);
745 [ - + ]: 8 : if (r < 0)
746 : 0 : return r;
747 : :
748 [ + - ]: 8 : if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
749 : 8 : return -errno;
750 : :
751 : 0 : return 0;
752 : : }
753 : :
754 : 0 : int btrfs_defrag(const char *p) {
755 : 0 : _cleanup_close_ int fd = -1;
756 : :
757 : 0 : fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
758 [ # # ]: 0 : if (fd < 0)
759 : 0 : return -errno;
760 : :
761 : 0 : return btrfs_defrag_fd(fd);
762 : : }
763 : :
764 : 0 : int btrfs_quota_enable_fd(int fd, bool b) {
765 : 0 : struct btrfs_ioctl_quota_ctl_args args = {
766 [ # # ]: 0 : .cmd = b ? BTRFS_QUOTA_CTL_ENABLE : BTRFS_QUOTA_CTL_DISABLE,
767 : : };
768 : : int r;
769 : :
770 [ # # ]: 0 : assert(fd >= 0);
771 : :
772 : 0 : r = btrfs_is_filesystem(fd);
773 [ # # ]: 0 : if (r < 0)
774 : 0 : return r;
775 [ # # ]: 0 : if (!r)
776 : 0 : return -ENOTTY;
777 : :
778 [ # # ]: 0 : if (ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args) < 0)
779 : 0 : return -errno;
780 : :
781 : 0 : return 0;
782 : : }
783 : :
784 : 0 : int btrfs_quota_enable(const char *path, bool b) {
785 : 0 : _cleanup_close_ int fd = -1;
786 : :
787 : 0 : fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
788 [ # # ]: 0 : if (fd < 0)
789 : 0 : return -errno;
790 : :
791 : 0 : return btrfs_quota_enable_fd(fd, b);
792 : : }
793 : :
794 : 0 : int btrfs_qgroup_set_limit_fd(int fd, uint64_t qgroupid, uint64_t referenced_max) {
795 : :
796 : 0 : struct btrfs_ioctl_qgroup_limit_args args = {
797 : : .lim.max_rfer = referenced_max,
798 : : .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER,
799 : : };
800 : : unsigned c;
801 : : int r;
802 : :
803 [ # # ]: 0 : assert(fd >= 0);
804 : :
805 [ # # ]: 0 : if (qgroupid == 0) {
806 : 0 : r = btrfs_subvol_get_id_fd(fd, &qgroupid);
807 [ # # ]: 0 : if (r < 0)
808 : 0 : return r;
809 : : } else {
810 : 0 : r = btrfs_is_filesystem(fd);
811 [ # # ]: 0 : if (r < 0)
812 : 0 : return r;
813 [ # # ]: 0 : if (!r)
814 : 0 : return -ENOTTY;
815 : : }
816 : :
817 : 0 : args.qgroupid = qgroupid;
818 : :
819 : 0 : for (c = 0;; c++) {
820 [ # # ]: 0 : if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args) < 0) {
821 : :
822 [ # # # # ]: 0 : if (errno == EBUSY && c < 10) {
823 : 0 : (void) btrfs_quota_scan_wait(fd);
824 : 0 : continue;
825 : : }
826 : :
827 : 0 : return -errno;
828 : : }
829 : :
830 : 0 : break;
831 : : }
832 : :
833 : 0 : return 0;
834 : : }
835 : :
836 : 0 : int btrfs_qgroup_set_limit(const char *path, uint64_t qgroupid, uint64_t referenced_max) {
837 : 0 : _cleanup_close_ int fd = -1;
838 : :
839 : 0 : fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
840 [ # # ]: 0 : if (fd < 0)
841 : 0 : return -errno;
842 : :
843 : 0 : return btrfs_qgroup_set_limit_fd(fd, qgroupid, referenced_max);
844 : : }
845 : :
846 : 0 : int btrfs_subvol_set_subtree_quota_limit_fd(int fd, uint64_t subvol_id, uint64_t referenced_max) {
847 : : uint64_t qgroupid;
848 : : int r;
849 : :
850 [ # # ]: 0 : assert(fd >= 0);
851 : :
852 : 0 : r = btrfs_subvol_find_subtree_qgroup(fd, subvol_id, &qgroupid);
853 [ # # ]: 0 : if (r < 0)
854 : 0 : return r;
855 : :
856 : 0 : return btrfs_qgroup_set_limit_fd(fd, qgroupid, referenced_max);
857 : : }
858 : :
859 : 0 : int btrfs_subvol_set_subtree_quota_limit(const char *path, uint64_t subvol_id, uint64_t referenced_max) {
860 : 0 : _cleanup_close_ int fd = -1;
861 : :
862 : 0 : fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
863 [ # # ]: 0 : if (fd < 0)
864 : 0 : return -errno;
865 : :
866 : 0 : return btrfs_subvol_set_subtree_quota_limit_fd(fd, subvol_id, referenced_max);
867 : : }
868 : :
869 : 0 : int btrfs_qgroupid_make(uint64_t level, uint64_t id, uint64_t *ret) {
870 [ # # ]: 0 : assert(ret);
871 : :
872 [ # # ]: 0 : if (level >= (UINT64_C(1) << (64 - BTRFS_QGROUP_LEVEL_SHIFT)))
873 : 0 : return -EINVAL;
874 : :
875 [ # # ]: 0 : if (id >= (UINT64_C(1) << BTRFS_QGROUP_LEVEL_SHIFT))
876 : 0 : return -EINVAL;
877 : :
878 : 0 : *ret = (level << BTRFS_QGROUP_LEVEL_SHIFT) | id;
879 : 0 : return 0;
880 : : }
881 : :
882 : 0 : int btrfs_qgroupid_split(uint64_t qgroupid, uint64_t *level, uint64_t *id) {
883 [ # # # # ]: 0 : assert(level || id);
884 : :
885 [ # # ]: 0 : if (level)
886 : 0 : *level = qgroupid >> BTRFS_QGROUP_LEVEL_SHIFT;
887 : :
888 [ # # ]: 0 : if (id)
889 : 0 : *id = qgroupid & ((UINT64_C(1) << BTRFS_QGROUP_LEVEL_SHIFT) - 1);
890 : :
891 : 0 : return 0;
892 : : }
893 : :
894 : 0 : static int qgroup_create_or_destroy(int fd, bool b, uint64_t qgroupid) {
895 : :
896 : 0 : struct btrfs_ioctl_qgroup_create_args args = {
897 : : .create = b,
898 : : .qgroupid = qgroupid,
899 : : };
900 : : unsigned c;
901 : : int r;
902 : :
903 : 0 : r = btrfs_is_filesystem(fd);
904 [ # # ]: 0 : if (r < 0)
905 : 0 : return r;
906 [ # # ]: 0 : if (r == 0)
907 : 0 : return -ENOTTY;
908 : :
909 : 0 : for (c = 0;; c++) {
910 [ # # ]: 0 : if (ioctl(fd, BTRFS_IOC_QGROUP_CREATE, &args) < 0) {
911 : :
912 : : /* If quota is not enabled, we get EINVAL. Turn this into a recognizable error */
913 [ # # ]: 0 : if (errno == EINVAL)
914 : 0 : return -ENOPROTOOPT;
915 : :
916 [ # # # # ]: 0 : if (errno == EBUSY && c < 10) {
917 : 0 : (void) btrfs_quota_scan_wait(fd);
918 : 0 : continue;
919 : : }
920 : :
921 : 0 : return -errno;
922 : : }
923 : :
924 : 0 : break;
925 : : }
926 : :
927 : 0 : return 0;
928 : : }
929 : :
930 : 0 : int btrfs_qgroup_create(int fd, uint64_t qgroupid) {
931 : 0 : return qgroup_create_or_destroy(fd, true, qgroupid);
932 : : }
933 : :
934 : 0 : int btrfs_qgroup_destroy(int fd, uint64_t qgroupid) {
935 : 0 : return qgroup_create_or_destroy(fd, false, qgroupid);
936 : : }
937 : :
938 : 0 : int btrfs_qgroup_destroy_recursive(int fd, uint64_t qgroupid) {
939 : 0 : _cleanup_free_ uint64_t *qgroups = NULL;
940 : : uint64_t subvol_id;
941 : : int i, n, r;
942 : :
943 : : /* Destroys the specified qgroup, but unassigns it from all
944 : : * its parents first. Also, it recursively destroys all
945 : : * qgroups it is assigned to that have the same id part of the
946 : : * qgroupid as the specified group. */
947 : :
948 : 0 : r = btrfs_qgroupid_split(qgroupid, NULL, &subvol_id);
949 [ # # ]: 0 : if (r < 0)
950 : 0 : return r;
951 : :
952 : 0 : n = btrfs_qgroup_find_parents(fd, qgroupid, &qgroups);
953 [ # # ]: 0 : if (n < 0)
954 : 0 : return n;
955 : :
956 [ # # ]: 0 : for (i = 0; i < n; i++) {
957 : : uint64_t id;
958 : :
959 : 0 : r = btrfs_qgroupid_split(qgroups[i], NULL, &id);
960 [ # # ]: 0 : if (r < 0)
961 : 0 : return r;
962 : :
963 : 0 : r = btrfs_qgroup_unassign(fd, qgroupid, qgroups[i]);
964 [ # # ]: 0 : if (r < 0)
965 : 0 : return r;
966 : :
967 [ # # ]: 0 : if (id != subvol_id)
968 : 0 : continue;
969 : :
970 : : /* The parent qgroupid shares the same id part with
971 : : * us? If so, destroy it too. */
972 : :
973 : 0 : (void) btrfs_qgroup_destroy_recursive(fd, qgroups[i]);
974 : : }
975 : :
976 : 0 : return btrfs_qgroup_destroy(fd, qgroupid);
977 : : }
978 : :
979 : 0 : int btrfs_quota_scan_start(int fd) {
980 : 0 : struct btrfs_ioctl_quota_rescan_args args = {};
981 : :
982 [ # # ]: 0 : assert(fd >= 0);
983 : :
984 [ # # ]: 0 : if (ioctl(fd, BTRFS_IOC_QUOTA_RESCAN, &args) < 0)
985 : 0 : return -errno;
986 : :
987 : 0 : return 0;
988 : : }
989 : :
990 : 0 : int btrfs_quota_scan_wait(int fd) {
991 [ # # ]: 0 : assert(fd >= 0);
992 : :
993 [ # # ]: 0 : if (ioctl(fd, BTRFS_IOC_QUOTA_RESCAN_WAIT) < 0)
994 : 0 : return -errno;
995 : :
996 : 0 : return 0;
997 : : }
998 : :
999 : 0 : int btrfs_quota_scan_ongoing(int fd) {
1000 : 0 : struct btrfs_ioctl_quota_rescan_args args = {};
1001 : :
1002 [ # # ]: 0 : assert(fd >= 0);
1003 : :
1004 [ # # ]: 0 : if (ioctl(fd, BTRFS_IOC_QUOTA_RESCAN_STATUS, &args) < 0)
1005 : 0 : return -errno;
1006 : :
1007 : 0 : return !!args.flags;
1008 : : }
1009 : :
1010 : 0 : static int qgroup_assign_or_unassign(int fd, bool b, uint64_t child, uint64_t parent) {
1011 : 0 : struct btrfs_ioctl_qgroup_assign_args args = {
1012 : : .assign = b,
1013 : : .src = child,
1014 : : .dst = parent,
1015 : : };
1016 : : unsigned c;
1017 : : int r;
1018 : :
1019 : 0 : r = btrfs_is_filesystem(fd);
1020 [ # # ]: 0 : if (r < 0)
1021 : 0 : return r;
1022 [ # # ]: 0 : if (r == 0)
1023 : 0 : return -ENOTTY;
1024 : :
1025 : 0 : for (c = 0;; c++) {
1026 : 0 : r = ioctl(fd, BTRFS_IOC_QGROUP_ASSIGN, &args);
1027 [ # # ]: 0 : if (r < 0) {
1028 [ # # # # ]: 0 : if (errno == EBUSY && c < 10) {
1029 : 0 : (void) btrfs_quota_scan_wait(fd);
1030 : 0 : continue;
1031 : : }
1032 : :
1033 : 0 : return -errno;
1034 : : }
1035 : :
1036 [ # # ]: 0 : if (r == 0)
1037 : 0 : return 0;
1038 : :
1039 : : /* If the return value is > 0, we need to request a rescan */
1040 : :
1041 : 0 : (void) btrfs_quota_scan_start(fd);
1042 : 0 : return 1;
1043 : : }
1044 : : }
1045 : :
1046 : 0 : int btrfs_qgroup_assign(int fd, uint64_t child, uint64_t parent) {
1047 : 0 : return qgroup_assign_or_unassign(fd, true, child, parent);
1048 : : }
1049 : :
1050 : 0 : int btrfs_qgroup_unassign(int fd, uint64_t child, uint64_t parent) {
1051 : 0 : return qgroup_assign_or_unassign(fd, false, child, parent);
1052 : : }
1053 : :
1054 : 0 : static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol_id, BtrfsRemoveFlags flags) {
1055 : 0 : struct btrfs_ioctl_search_args args = {
1056 : : .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
1057 : :
1058 : : .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
1059 : : .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
1060 : :
1061 : : .key.min_type = BTRFS_ROOT_BACKREF_KEY,
1062 : : .key.max_type = BTRFS_ROOT_BACKREF_KEY,
1063 : :
1064 : : .key.min_transid = 0,
1065 : : .key.max_transid = (uint64_t) -1,
1066 : : };
1067 : :
1068 : 0 : struct btrfs_ioctl_vol_args vol_args = {};
1069 : 0 : _cleanup_close_ int subvol_fd = -1;
1070 : : struct stat st;
1071 : 0 : bool made_writable = false;
1072 : : int r;
1073 : :
1074 [ # # ]: 0 : assert(fd >= 0);
1075 [ # # ]: 0 : assert(subvolume);
1076 : :
1077 [ # # ]: 0 : if (fstat(fd, &st) < 0)
1078 : 0 : return -errno;
1079 : :
1080 [ # # ]: 0 : if (!S_ISDIR(st.st_mode))
1081 : 0 : return -EINVAL;
1082 : :
1083 : 0 : subvol_fd = openat(fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
1084 [ # # ]: 0 : if (subvol_fd < 0)
1085 : 0 : return -errno;
1086 : :
1087 : : /* Let's check if this is actually a subvolume. Note that this is mostly redundant, as BTRFS_IOC_SNAP_DESTROY
1088 : : * would fail anyway if it is not. However, it's a good thing to check this ahead of time so that we can return
1089 : : * ENOTTY unconditionally in this case. This is different from the ioctl() which will return EPERM/EACCES if we
1090 : : * don't have the privileges to remove subvolumes, regardless if the specified directory is actually a
1091 : : * subvolume or not. In order to make it easy for callers to cover the "this is not a btrfs subvolume" case
1092 : : * let's prefer ENOTTY over EPERM/EACCES though. */
1093 : 0 : r = btrfs_is_subvol_fd(subvol_fd);
1094 [ # # ]: 0 : if (r < 0)
1095 : 0 : return r;
1096 [ # # ]: 0 : if (r == 0) /* Not a btrfs subvolume */
1097 : 0 : return -ENOTTY;
1098 : :
1099 [ # # ]: 0 : if (subvol_id == 0) {
1100 : 0 : r = btrfs_subvol_get_id_fd(subvol_fd, &subvol_id);
1101 [ # # ]: 0 : if (r < 0)
1102 : 0 : return r;
1103 : : }
1104 : :
1105 : : /* First, try to remove the subvolume. If it happens to be
1106 : : * already empty, this will just work. */
1107 : 0 : strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
1108 [ # # ]: 0 : if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) >= 0) {
1109 : 0 : (void) btrfs_qgroup_destroy_recursive(fd, subvol_id); /* for the leaf subvolumes, the qgroup id is identical to the subvol id */
1110 : 0 : return 0;
1111 : : }
1112 [ # # # # ]: 0 : if (!(flags & BTRFS_REMOVE_RECURSIVE) || errno != ENOTEMPTY)
1113 : 0 : return -errno;
1114 : :
1115 : : /* OK, the subvolume is not empty, let's look for child
1116 : : * subvolumes, and remove them, first */
1117 : :
1118 : 0 : args.key.min_offset = args.key.max_offset = subvol_id;
1119 : :
1120 [ # # ]: 0 : while (btrfs_ioctl_search_args_compare(&args) <= 0) {
1121 : : const struct btrfs_ioctl_search_header *sh;
1122 : : unsigned i;
1123 : :
1124 : 0 : args.key.nr_items = 256;
1125 [ # # ]: 0 : if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
1126 : 0 : return -errno;
1127 : :
1128 [ # # ]: 0 : if (args.key.nr_items <= 0)
1129 : 0 : break;
1130 : :
1131 [ # # ]: 0 : FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
1132 [ # # # ]: 0 : _cleanup_free_ char *p = NULL;
1133 : : const struct btrfs_root_ref *ref;
1134 : : struct btrfs_ioctl_ino_lookup_args ino_args;
1135 : :
1136 : 0 : btrfs_ioctl_search_args_set(&args, sh);
1137 : :
1138 [ # # ]: 0 : if (sh->type != BTRFS_ROOT_BACKREF_KEY)
1139 : 0 : continue;
1140 [ # # ]: 0 : if (sh->offset != subvol_id)
1141 : 0 : continue;
1142 : :
1143 : 0 : ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
1144 : :
1145 : 0 : p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
1146 [ # # ]: 0 : if (!p)
1147 : 0 : return -ENOMEM;
1148 : :
1149 [ # # ]: 0 : zero(ino_args);
1150 : 0 : ino_args.treeid = subvol_id;
1151 : 0 : ino_args.objectid = htole64(ref->dirid);
1152 : :
1153 [ # # ]: 0 : if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
1154 : 0 : return -errno;
1155 : :
1156 [ # # ]: 0 : if (!made_writable) {
1157 : 0 : r = btrfs_subvol_set_read_only_fd(subvol_fd, false);
1158 [ # # ]: 0 : if (r < 0)
1159 : 0 : return r;
1160 : :
1161 : 0 : made_writable = true;
1162 : : }
1163 : :
1164 [ # # ]: 0 : if (isempty(ino_args.name))
1165 : : /* Subvolume is in the top-level
1166 : : * directory of the subvolume. */
1167 : 0 : r = subvol_remove_children(subvol_fd, p, sh->objectid, flags);
1168 : : else {
1169 [ # # ]: 0 : _cleanup_close_ int child_fd = -1;
1170 : :
1171 : : /* Subvolume is somewhere further down,
1172 : : * hence we need to open the
1173 : : * containing directory first */
1174 : :
1175 : 0 : child_fd = openat(subvol_fd, ino_args.name, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
1176 [ # # ]: 0 : if (child_fd < 0)
1177 : 0 : return -errno;
1178 : :
1179 : 0 : r = subvol_remove_children(child_fd, p, sh->objectid, flags);
1180 : : }
1181 [ # # ]: 0 : if (r < 0)
1182 : 0 : return r;
1183 : : }
1184 : :
1185 : : /* Increase search key by one, to read the next item, if we can. */
1186 [ # # ]: 0 : if (!btrfs_ioctl_search_args_inc(&args))
1187 : 0 : break;
1188 : : }
1189 : :
1190 : : /* OK, the child subvolumes should all be gone now, let's try
1191 : : * again to remove the subvolume */
1192 [ # # ]: 0 : if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) < 0)
1193 : 0 : return -errno;
1194 : :
1195 : 0 : (void) btrfs_qgroup_destroy_recursive(fd, subvol_id);
1196 : 0 : return 0;
1197 : : }
1198 : :
1199 : 0 : int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags) {
1200 : 0 : _cleanup_close_ int fd = -1;
1201 : : const char *subvolume;
1202 : : int r;
1203 : :
1204 [ # # ]: 0 : assert(path);
1205 : :
1206 : 0 : r = extract_subvolume_name(path, &subvolume);
1207 [ # # ]: 0 : if (r < 0)
1208 : 0 : return r;
1209 : :
1210 : 0 : fd = open_parent(path, O_CLOEXEC, 0);
1211 [ # # ]: 0 : if (fd < 0)
1212 : 0 : return fd;
1213 : :
1214 : 0 : return subvol_remove_children(fd, subvolume, 0, flags);
1215 : : }
1216 : :
1217 : 0 : int btrfs_subvol_remove_fd(int fd, const char *subvolume, BtrfsRemoveFlags flags) {
1218 : 0 : return subvol_remove_children(fd, subvolume, 0, flags);
1219 : : }
1220 : :
1221 : 0 : int btrfs_qgroup_copy_limits(int fd, uint64_t old_qgroupid, uint64_t new_qgroupid) {
1222 : :
1223 : 0 : struct btrfs_ioctl_search_args args = {
1224 : : /* Tree of quota items */
1225 : : .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
1226 : :
1227 : : /* The object ID is always 0 */
1228 : : .key.min_objectid = 0,
1229 : : .key.max_objectid = 0,
1230 : :
1231 : : /* Look precisely for the quota items */
1232 : : .key.min_type = BTRFS_QGROUP_LIMIT_KEY,
1233 : : .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
1234 : :
1235 : : /* For our qgroup */
1236 : : .key.min_offset = old_qgroupid,
1237 : : .key.max_offset = old_qgroupid,
1238 : :
1239 : : /* No restrictions on the other components */
1240 : : .key.min_transid = 0,
1241 : : .key.max_transid = (uint64_t) -1,
1242 : : };
1243 : :
1244 : : int r;
1245 : :
1246 : 0 : r = btrfs_is_filesystem(fd);
1247 [ # # ]: 0 : if (r < 0)
1248 : 0 : return r;
1249 [ # # ]: 0 : if (!r)
1250 : 0 : return -ENOTTY;
1251 : :
1252 [ # # ]: 0 : while (btrfs_ioctl_search_args_compare(&args) <= 0) {
1253 : : const struct btrfs_ioctl_search_header *sh;
1254 : : unsigned i;
1255 : :
1256 : 0 : args.key.nr_items = 256;
1257 [ # # ]: 0 : if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) {
1258 [ # # ]: 0 : if (errno == ENOENT) /* quota tree missing: quota is not enabled, hence nothing to copy */
1259 : 0 : break;
1260 : :
1261 : 0 : return -errno;
1262 : : }
1263 : :
1264 [ # # ]: 0 : if (args.key.nr_items <= 0)
1265 : 0 : break;
1266 : :
1267 [ # # ]: 0 : FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
1268 : 0 : const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
1269 : : struct btrfs_ioctl_qgroup_limit_args qargs;
1270 : : unsigned c;
1271 : :
1272 : : /* Make sure we start the next search at least from this entry */
1273 : 0 : btrfs_ioctl_search_args_set(&args, sh);
1274 : :
1275 [ # # ]: 0 : if (sh->objectid != 0)
1276 : 0 : continue;
1277 [ # # ]: 0 : if (sh->type != BTRFS_QGROUP_LIMIT_KEY)
1278 : 0 : continue;
1279 [ # # ]: 0 : if (sh->offset != old_qgroupid)
1280 : 0 : continue;
1281 : :
1282 : : /* We found the entry, now copy things over. */
1283 : :
1284 : 0 : qargs = (struct btrfs_ioctl_qgroup_limit_args) {
1285 : : .qgroupid = new_qgroupid,
1286 : :
1287 : 0 : .lim.max_rfer = le64toh(qli->max_rfer),
1288 : 0 : .lim.max_excl = le64toh(qli->max_excl),
1289 : 0 : .lim.rsv_rfer = le64toh(qli->rsv_rfer),
1290 : 0 : .lim.rsv_excl = le64toh(qli->rsv_excl),
1291 : :
1292 : 0 : .lim.flags = le64toh(qli->flags) & (BTRFS_QGROUP_LIMIT_MAX_RFER|
1293 : : BTRFS_QGROUP_LIMIT_MAX_EXCL|
1294 : : BTRFS_QGROUP_LIMIT_RSV_RFER|
1295 : : BTRFS_QGROUP_LIMIT_RSV_EXCL),
1296 : : };
1297 : :
1298 : 0 : for (c = 0;; c++) {
1299 [ # # ]: 0 : if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &qargs) < 0) {
1300 [ # # # # ]: 0 : if (errno == EBUSY && c < 10) {
1301 : 0 : (void) btrfs_quota_scan_wait(fd);
1302 : 0 : continue;
1303 : : }
1304 : 0 : return -errno;
1305 : : }
1306 : :
1307 : 0 : break;
1308 : : }
1309 : :
1310 : 0 : return 1;
1311 : : }
1312 : :
1313 : : /* Increase search key by one, to read the next item, if we can. */
1314 [ # # ]: 0 : if (!btrfs_ioctl_search_args_inc(&args))
1315 : 0 : break;
1316 : : }
1317 : :
1318 : 0 : return 0;
1319 : : }
1320 : :
1321 : 0 : static int copy_quota_hierarchy(int fd, uint64_t old_subvol_id, uint64_t new_subvol_id) {
1322 : 0 : _cleanup_free_ uint64_t *old_qgroups = NULL, *old_parent_qgroups = NULL;
1323 : 0 : bool copy_from_parent = false, insert_intermediary_qgroup = false;
1324 : : int n_old_qgroups, n_old_parent_qgroups, r, i;
1325 : : uint64_t old_parent_id;
1326 : :
1327 [ # # ]: 0 : assert(fd >= 0);
1328 : :
1329 : : /* Copies a reduced form of quota information from the old to
1330 : : * the new subvolume. */
1331 : :
1332 : 0 : n_old_qgroups = btrfs_qgroup_find_parents(fd, old_subvol_id, &old_qgroups);
1333 [ # # ]: 0 : if (n_old_qgroups <= 0) /* Nothing to copy */
1334 : 0 : return n_old_qgroups;
1335 : :
1336 : 0 : r = btrfs_subvol_get_parent(fd, old_subvol_id, &old_parent_id);
1337 [ # # ]: 0 : if (r == -ENXIO)
1338 : : /* We have no parent, hence nothing to copy. */
1339 : 0 : n_old_parent_qgroups = 0;
1340 [ # # ]: 0 : else if (r < 0)
1341 : 0 : return r;
1342 : : else {
1343 : 0 : n_old_parent_qgroups = btrfs_qgroup_find_parents(fd, old_parent_id, &old_parent_qgroups);
1344 [ # # ]: 0 : if (n_old_parent_qgroups < 0)
1345 : 0 : return n_old_parent_qgroups;
1346 : : }
1347 : :
1348 [ # # ]: 0 : for (i = 0; i < n_old_qgroups; i++) {
1349 : : uint64_t id;
1350 : : int j;
1351 : :
1352 : 0 : r = btrfs_qgroupid_split(old_qgroups[i], NULL, &id);
1353 [ # # ]: 0 : if (r < 0)
1354 : 0 : return r;
1355 : :
1356 [ # # ]: 0 : if (id == old_subvol_id) {
1357 : : /* The old subvolume was member of a qgroup
1358 : : * that had the same id, but a different level
1359 : : * as it self. Let's set up something similar
1360 : : * in the destination. */
1361 : 0 : insert_intermediary_qgroup = true;
1362 : 0 : break;
1363 : : }
1364 : :
1365 [ # # ]: 0 : for (j = 0; j < n_old_parent_qgroups; j++)
1366 [ # # ]: 0 : if (old_parent_qgroups[j] == old_qgroups[i]) {
1367 : : /* The old subvolume shared a common
1368 : : * parent qgroup with its parent
1369 : : * subvolume. Let's set up something
1370 : : * similar in the destination. */
1371 : 0 : copy_from_parent = true;
1372 : : }
1373 : : }
1374 : :
1375 [ # # # # ]: 0 : if (!insert_intermediary_qgroup && !copy_from_parent)
1376 : 0 : return 0;
1377 : :
1378 : 0 : return btrfs_subvol_auto_qgroup_fd(fd, new_subvol_id, insert_intermediary_qgroup);
1379 : : }
1380 : :
1381 : 0 : static int copy_subtree_quota_limits(int fd, uint64_t old_subvol, uint64_t new_subvol) {
1382 : : uint64_t old_subtree_qgroup, new_subtree_qgroup;
1383 : : bool changed;
1384 : : int r;
1385 : :
1386 : : /* First copy the leaf limits */
1387 : 0 : r = btrfs_qgroup_copy_limits(fd, old_subvol, new_subvol);
1388 [ # # ]: 0 : if (r < 0)
1389 : 0 : return r;
1390 : 0 : changed = r > 0;
1391 : :
1392 : : /* Then, try to copy the subtree limits, if there are any. */
1393 : 0 : r = btrfs_subvol_find_subtree_qgroup(fd, old_subvol, &old_subtree_qgroup);
1394 [ # # ]: 0 : if (r < 0)
1395 : 0 : return r;
1396 [ # # ]: 0 : if (r == 0)
1397 : 0 : return changed;
1398 : :
1399 : 0 : r = btrfs_subvol_find_subtree_qgroup(fd, new_subvol, &new_subtree_qgroup);
1400 [ # # ]: 0 : if (r < 0)
1401 : 0 : return r;
1402 [ # # ]: 0 : if (r == 0)
1403 : 0 : return changed;
1404 : :
1405 : 0 : r = btrfs_qgroup_copy_limits(fd, old_subtree_qgroup, new_subtree_qgroup);
1406 [ # # ]: 0 : if (r != 0)
1407 : 0 : return r;
1408 : :
1409 : 0 : return changed;
1410 : : }
1411 : :
1412 : 0 : static int subvol_snapshot_children(
1413 : : int old_fd,
1414 : : int new_fd,
1415 : : const char *subvolume,
1416 : : uint64_t old_subvol_id,
1417 : : BtrfsSnapshotFlags flags) {
1418 : :
1419 : 0 : struct btrfs_ioctl_search_args args = {
1420 : : .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
1421 : :
1422 : : .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
1423 : : .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
1424 : :
1425 : : .key.min_type = BTRFS_ROOT_BACKREF_KEY,
1426 : : .key.max_type = BTRFS_ROOT_BACKREF_KEY,
1427 : :
1428 : : .key.min_transid = 0,
1429 : : .key.max_transid = (uint64_t) -1,
1430 : : };
1431 : :
1432 : 0 : struct btrfs_ioctl_vol_args_v2 vol_args = {
1433 : 0 : .flags = flags & BTRFS_SNAPSHOT_READ_ONLY ? BTRFS_SUBVOL_RDONLY : 0,
1434 : : .fd = old_fd,
1435 : : };
1436 : 0 : _cleanup_close_ int subvolume_fd = -1;
1437 : : uint64_t new_subvol_id;
1438 : : int r;
1439 : :
1440 [ # # ]: 0 : assert(old_fd >= 0);
1441 [ # # ]: 0 : assert(new_fd >= 0);
1442 [ # # ]: 0 : assert(subvolume);
1443 : :
1444 : 0 : strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
1445 : :
1446 [ # # ]: 0 : if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &vol_args) < 0)
1447 : 0 : return -errno;
1448 : :
1449 [ # # ]: 0 : if (!(flags & BTRFS_SNAPSHOT_RECURSIVE) &&
1450 [ # # ]: 0 : !(flags & BTRFS_SNAPSHOT_QUOTA))
1451 : 0 : return 0;
1452 : :
1453 [ # # ]: 0 : if (old_subvol_id == 0) {
1454 : 0 : r = btrfs_subvol_get_id_fd(old_fd, &old_subvol_id);
1455 [ # # ]: 0 : if (r < 0)
1456 : 0 : return r;
1457 : : }
1458 : :
1459 : 0 : r = btrfs_subvol_get_id(new_fd, vol_args.name, &new_subvol_id);
1460 [ # # ]: 0 : if (r < 0)
1461 : 0 : return r;
1462 : :
1463 [ # # ]: 0 : if (flags & BTRFS_SNAPSHOT_QUOTA)
1464 : 0 : (void) copy_quota_hierarchy(new_fd, old_subvol_id, new_subvol_id);
1465 : :
1466 [ # # ]: 0 : if (!(flags & BTRFS_SNAPSHOT_RECURSIVE)) {
1467 : :
1468 [ # # ]: 0 : if (flags & BTRFS_SNAPSHOT_QUOTA)
1469 : 0 : (void) copy_subtree_quota_limits(new_fd, old_subvol_id, new_subvol_id);
1470 : :
1471 : 0 : return 0;
1472 : : }
1473 : :
1474 : 0 : args.key.min_offset = args.key.max_offset = old_subvol_id;
1475 : :
1476 [ # # ]: 0 : while (btrfs_ioctl_search_args_compare(&args) <= 0) {
1477 : : const struct btrfs_ioctl_search_header *sh;
1478 : : unsigned i;
1479 : :
1480 : 0 : args.key.nr_items = 256;
1481 [ # # ]: 0 : if (ioctl(old_fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
1482 : 0 : return -errno;
1483 : :
1484 [ # # ]: 0 : if (args.key.nr_items <= 0)
1485 : 0 : break;
1486 : :
1487 [ # # ]: 0 : FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
1488 [ # # # # : 0 : _cleanup_free_ char *p = NULL, *c = NULL, *np = NULL;
# # # #
# ]
1489 : : struct btrfs_ioctl_ino_lookup_args ino_args;
1490 : : const struct btrfs_root_ref *ref;
1491 [ # # # # : 0 : _cleanup_close_ int old_child_fd = -1, new_child_fd = -1;
# # ]
1492 : :
1493 : 0 : btrfs_ioctl_search_args_set(&args, sh);
1494 : :
1495 [ # # ]: 0 : if (sh->type != BTRFS_ROOT_BACKREF_KEY)
1496 : 0 : continue;
1497 : :
1498 : : /* Avoid finding the source subvolume a second
1499 : : * time */
1500 [ # # ]: 0 : if (sh->offset != old_subvol_id)
1501 : 0 : continue;
1502 : :
1503 : : /* Avoid running into loops if the new
1504 : : * subvolume is below the old one. */
1505 [ # # ]: 0 : if (sh->objectid == new_subvol_id)
1506 : 0 : continue;
1507 : :
1508 : 0 : ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
1509 : 0 : p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
1510 [ # # ]: 0 : if (!p)
1511 : 0 : return -ENOMEM;
1512 : :
1513 [ # # ]: 0 : zero(ino_args);
1514 : 0 : ino_args.treeid = old_subvol_id;
1515 : 0 : ino_args.objectid = htole64(ref->dirid);
1516 : :
1517 [ # # ]: 0 : if (ioctl(old_fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
1518 : 0 : return -errno;
1519 : :
1520 : 0 : c = path_join(ino_args.name, p);
1521 [ # # ]: 0 : if (!c)
1522 : 0 : return -ENOMEM;
1523 : :
1524 : 0 : old_child_fd = openat(old_fd, c, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
1525 [ # # ]: 0 : if (old_child_fd < 0)
1526 : 0 : return -errno;
1527 : :
1528 : 0 : np = path_join(subvolume, ino_args.name);
1529 [ # # ]: 0 : if (!np)
1530 : 0 : return -ENOMEM;
1531 : :
1532 : 0 : new_child_fd = openat(new_fd, np, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
1533 [ # # ]: 0 : if (new_child_fd < 0)
1534 : 0 : return -errno;
1535 : :
1536 [ # # ]: 0 : if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
1537 : : /* If the snapshot is read-only we
1538 : : * need to mark it writable
1539 : : * temporarily, to put the subsnapshot
1540 : : * into place. */
1541 : :
1542 [ # # ]: 0 : if (subvolume_fd < 0) {
1543 : 0 : subvolume_fd = openat(new_fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
1544 [ # # ]: 0 : if (subvolume_fd < 0)
1545 : 0 : return -errno;
1546 : : }
1547 : :
1548 : 0 : r = btrfs_subvol_set_read_only_fd(subvolume_fd, false);
1549 [ # # ]: 0 : if (r < 0)
1550 : 0 : return r;
1551 : : }
1552 : :
1553 : : /* When btrfs clones the subvolumes, child
1554 : : * subvolumes appear as empty directories. Remove
1555 : : * them, so that we can create a new snapshot
1556 : : * in their place */
1557 [ # # ]: 0 : if (unlinkat(new_child_fd, p, AT_REMOVEDIR) < 0) {
1558 : 0 : int k = -errno;
1559 : :
1560 [ # # ]: 0 : if (flags & BTRFS_SNAPSHOT_READ_ONLY)
1561 : 0 : (void) btrfs_subvol_set_read_only_fd(subvolume_fd, true);
1562 : :
1563 : 0 : return k;
1564 : : }
1565 : :
1566 : 0 : r = subvol_snapshot_children(old_child_fd, new_child_fd, p, sh->objectid, flags & ~BTRFS_SNAPSHOT_FALLBACK_COPY);
1567 : :
1568 : : /* Restore the readonly flag */
1569 [ # # ]: 0 : if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
1570 : : int k;
1571 : :
1572 : 0 : k = btrfs_subvol_set_read_only_fd(subvolume_fd, true);
1573 [ # # # # ]: 0 : if (r >= 0 && k < 0)
1574 : 0 : return k;
1575 : : }
1576 : :
1577 [ # # ]: 0 : if (r < 0)
1578 : 0 : return r;
1579 : : }
1580 : :
1581 : : /* Increase search key by one, to read the next item, if we can. */
1582 [ # # ]: 0 : if (!btrfs_ioctl_search_args_inc(&args))
1583 : 0 : break;
1584 : : }
1585 : :
1586 [ # # ]: 0 : if (flags & BTRFS_SNAPSHOT_QUOTA)
1587 : 0 : (void) copy_subtree_quota_limits(new_fd, old_subvol_id, new_subvol_id);
1588 : :
1589 : 0 : return 0;
1590 : : }
1591 : :
1592 : 0 : int btrfs_subvol_snapshot_fd_full(
1593 : : int old_fd,
1594 : : const char *new_path,
1595 : : BtrfsSnapshotFlags flags,
1596 : : copy_progress_path_t progress_path,
1597 : : copy_progress_bytes_t progress_bytes,
1598 : : void *userdata) {
1599 : :
1600 : 0 : _cleanup_close_ int new_fd = -1;
1601 : : const char *subvolume;
1602 : : int r;
1603 : :
1604 [ # # ]: 0 : assert(old_fd >= 0);
1605 [ # # ]: 0 : assert(new_path);
1606 : :
1607 : 0 : r = btrfs_is_subvol_fd(old_fd);
1608 [ # # ]: 0 : if (r < 0)
1609 : 0 : return r;
1610 [ # # ]: 0 : if (r == 0) {
1611 : 0 : bool plain_directory = false;
1612 : :
1613 : : /* If the source isn't a proper subvolume, fail unless fallback is requested */
1614 [ # # ]: 0 : if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY))
1615 : 0 : return -EISDIR;
1616 : :
1617 : 0 : r = btrfs_subvol_make(new_path);
1618 [ # # # # ]: 0 : if (r == -ENOTTY && (flags & BTRFS_SNAPSHOT_FALLBACK_DIRECTORY)) {
1619 : : /* If the destination doesn't support subvolumes, then use a plain directory, if that's requested. */
1620 [ # # ]: 0 : if (mkdir(new_path, 0755) < 0)
1621 : 0 : return -errno;
1622 : :
1623 : 0 : plain_directory = true;
1624 [ # # ]: 0 : } else if (r < 0)
1625 : 0 : return r;
1626 : :
1627 : 0 : r = copy_directory_fd_full(old_fd, new_path, COPY_MERGE|COPY_REFLINK|COPY_SAME_MOUNT|(FLAGS_SET(flags, BTRFS_SNAPSHOT_SIGINT) ? COPY_SIGINT : 0), progress_path, progress_bytes, userdata);
1628 [ # # ]: 0 : if (r < 0)
1629 : 0 : goto fallback_fail;
1630 : :
1631 [ # # ]: 0 : if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
1632 : :
1633 [ # # ]: 0 : if (plain_directory) {
1634 : : /* Plain directories have no recursive read-only flag, but something pretty close to
1635 : : * it: the IMMUTABLE bit. Let's use this here, if this is requested. */
1636 : :
1637 [ # # ]: 0 : if (flags & BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE)
1638 : 0 : (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL, NULL);
1639 : : } else {
1640 : 0 : r = btrfs_subvol_set_read_only(new_path, true);
1641 [ # # ]: 0 : if (r < 0)
1642 : 0 : goto fallback_fail;
1643 : : }
1644 : : }
1645 : :
1646 : 0 : return 0;
1647 : :
1648 : 0 : fallback_fail:
1649 : 0 : (void) rm_rf(new_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
1650 : 0 : return r;
1651 : : }
1652 : :
1653 : 0 : r = extract_subvolume_name(new_path, &subvolume);
1654 [ # # ]: 0 : if (r < 0)
1655 : 0 : return r;
1656 : :
1657 : 0 : new_fd = open_parent(new_path, O_CLOEXEC, 0);
1658 [ # # ]: 0 : if (new_fd < 0)
1659 : 0 : return new_fd;
1660 : :
1661 : 0 : return subvol_snapshot_children(old_fd, new_fd, subvolume, 0, flags);
1662 : : }
1663 : :
1664 : 0 : int btrfs_subvol_snapshot_full(
1665 : : const char *old_path,
1666 : : const char *new_path,
1667 : : BtrfsSnapshotFlags flags,
1668 : : copy_progress_path_t progress_path,
1669 : : copy_progress_bytes_t progress_bytes,
1670 : : void *userdata) {
1671 : :
1672 : 0 : _cleanup_close_ int old_fd = -1;
1673 : :
1674 [ # # ]: 0 : assert(old_path);
1675 [ # # ]: 0 : assert(new_path);
1676 : :
1677 : 0 : old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1678 [ # # ]: 0 : if (old_fd < 0)
1679 : 0 : return -errno;
1680 : :
1681 : 0 : return btrfs_subvol_snapshot_fd_full(old_fd, new_path, flags, progress_path, progress_bytes, userdata);
1682 : : }
1683 : :
1684 : 0 : int btrfs_qgroup_find_parents(int fd, uint64_t qgroupid, uint64_t **ret) {
1685 : :
1686 : 0 : struct btrfs_ioctl_search_args args = {
1687 : : /* Tree of quota items */
1688 : : .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
1689 : :
1690 : : /* Look precisely for the quota relation items */
1691 : : .key.min_type = BTRFS_QGROUP_RELATION_KEY,
1692 : : .key.max_type = BTRFS_QGROUP_RELATION_KEY,
1693 : :
1694 : : /* No restrictions on the other components */
1695 : : .key.min_offset = 0,
1696 : : .key.max_offset = (uint64_t) -1,
1697 : :
1698 : : .key.min_transid = 0,
1699 : : .key.max_transid = (uint64_t) -1,
1700 : : };
1701 : :
1702 : 0 : _cleanup_free_ uint64_t *items = NULL;
1703 : 0 : size_t n_items = 0, n_allocated = 0;
1704 : : int r;
1705 : :
1706 [ # # ]: 0 : assert(fd >= 0);
1707 [ # # ]: 0 : assert(ret);
1708 : :
1709 [ # # ]: 0 : if (qgroupid == 0) {
1710 : 0 : r = btrfs_subvol_get_id_fd(fd, &qgroupid);
1711 [ # # ]: 0 : if (r < 0)
1712 : 0 : return r;
1713 : : } else {
1714 : 0 : r = btrfs_is_filesystem(fd);
1715 [ # # ]: 0 : if (r < 0)
1716 : 0 : return r;
1717 [ # # ]: 0 : if (!r)
1718 : 0 : return -ENOTTY;
1719 : : }
1720 : :
1721 : 0 : args.key.min_objectid = args.key.max_objectid = qgroupid;
1722 : :
1723 [ # # ]: 0 : while (btrfs_ioctl_search_args_compare(&args) <= 0) {
1724 : : const struct btrfs_ioctl_search_header *sh;
1725 : : unsigned i;
1726 : :
1727 : 0 : args.key.nr_items = 256;
1728 [ # # ]: 0 : if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) {
1729 [ # # ]: 0 : if (errno == ENOENT) /* quota tree missing: quota is disabled */
1730 : 0 : break;
1731 : :
1732 : 0 : return -errno;
1733 : : }
1734 : :
1735 [ # # ]: 0 : if (args.key.nr_items <= 0)
1736 : 0 : break;
1737 : :
1738 [ # # ]: 0 : FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
1739 : :
1740 : : /* Make sure we start the next search at least from this entry */
1741 : 0 : btrfs_ioctl_search_args_set(&args, sh);
1742 : :
1743 [ # # ]: 0 : if (sh->type != BTRFS_QGROUP_RELATION_KEY)
1744 : 0 : continue;
1745 [ # # ]: 0 : if (sh->offset < sh->objectid)
1746 : 0 : continue;
1747 [ # # ]: 0 : if (sh->objectid != qgroupid)
1748 : 0 : continue;
1749 : :
1750 [ # # ]: 0 : if (!GREEDY_REALLOC(items, n_allocated, n_items+1))
1751 : 0 : return -ENOMEM;
1752 : :
1753 : 0 : items[n_items++] = sh->offset;
1754 : : }
1755 : :
1756 : : /* Increase search key by one, to read the next item, if we can. */
1757 [ # # ]: 0 : if (!btrfs_ioctl_search_args_inc(&args))
1758 : 0 : break;
1759 : : }
1760 : :
1761 [ # # ]: 0 : if (n_items <= 0) {
1762 : 0 : *ret = NULL;
1763 : 0 : return 0;
1764 : : }
1765 : :
1766 : 0 : *ret = TAKE_PTR(items);
1767 : :
1768 : 0 : return (int) n_items;
1769 : : }
1770 : :
1771 : 0 : int btrfs_subvol_auto_qgroup_fd(int fd, uint64_t subvol_id, bool insert_intermediary_qgroup) {
1772 : 0 : _cleanup_free_ uint64_t *qgroups = NULL;
1773 : : uint64_t parent_subvol;
1774 : 0 : bool changed = false;
1775 : 0 : int n = 0, r;
1776 : :
1777 [ # # ]: 0 : assert(fd >= 0);
1778 : :
1779 : : /*
1780 : : * Sets up the specified subvolume's qgroup automatically in
1781 : : * one of two ways:
1782 : : *
1783 : : * If insert_intermediary_qgroup is false, the subvolume's
1784 : : * leaf qgroup will be assigned to the same parent qgroups as
1785 : : * the subvolume's parent subvolume.
1786 : : *
1787 : : * If insert_intermediary_qgroup is true a new intermediary
1788 : : * higher-level qgroup is created, with a higher level number,
1789 : : * but reusing the id of the subvolume. The level number is
1790 : : * picked as one smaller than the lowest level qgroup the
1791 : : * parent subvolume is a member of. If the parent subvolume's
1792 : : * leaf qgroup is assigned to no higher-level qgroup a new
1793 : : * qgroup of level 255 is created instead. Either way, the new
1794 : : * qgroup is then assigned to the parent's higher-level
1795 : : * qgroup, and the subvolume itself is assigned to it.
1796 : : *
1797 : : * If the subvolume is already assigned to a higher level
1798 : : * qgroup, no operation is executed.
1799 : : *
1800 : : * Effectively this means: regardless if
1801 : : * insert_intermediary_qgroup is true or not, after this
1802 : : * function is invoked the subvolume will be accounted within
1803 : : * the same qgroups as the parent. However, if it is true, it
1804 : : * will also get its own higher-level qgroup, which may in
1805 : : * turn be used by subvolumes created beneath this subvolume
1806 : : * later on.
1807 : : *
1808 : : * This hence defines a simple default qgroup setup for
1809 : : * subvolumes, as long as this function is invoked on each
1810 : : * created subvolume: each subvolume is always accounting
1811 : : * together with its immediate parents. Optionally, if
1812 : : * insert_intermediary_qgroup is true, it will also get a
1813 : : * qgroup that then includes all its own child subvolumes.
1814 : : */
1815 : :
1816 [ # # ]: 0 : if (subvol_id == 0) {
1817 : 0 : r = btrfs_is_subvol_fd(fd);
1818 [ # # ]: 0 : if (r < 0)
1819 : 0 : return r;
1820 [ # # ]: 0 : if (!r)
1821 : 0 : return -ENOTTY;
1822 : :
1823 : 0 : r = btrfs_subvol_get_id_fd(fd, &subvol_id);
1824 [ # # ]: 0 : if (r < 0)
1825 : 0 : return r;
1826 : : }
1827 : :
1828 : 0 : n = btrfs_qgroup_find_parents(fd, subvol_id, &qgroups);
1829 [ # # ]: 0 : if (n < 0)
1830 : 0 : return n;
1831 [ # # ]: 0 : if (n > 0) /* already parent qgroups set up, let's bail */
1832 : 0 : return 0;
1833 : :
1834 : 0 : qgroups = mfree(qgroups);
1835 : :
1836 : 0 : r = btrfs_subvol_get_parent(fd, subvol_id, &parent_subvol);
1837 [ # # ]: 0 : if (r == -ENXIO)
1838 : : /* No parent, hence no qgroup memberships */
1839 : 0 : n = 0;
1840 [ # # ]: 0 : else if (r < 0)
1841 : 0 : return r;
1842 : : else {
1843 : 0 : n = btrfs_qgroup_find_parents(fd, parent_subvol, &qgroups);
1844 [ # # ]: 0 : if (n < 0)
1845 : 0 : return n;
1846 : : }
1847 : :
1848 [ # # ]: 0 : if (insert_intermediary_qgroup) {
1849 : 0 : uint64_t lowest = 256, new_qgroupid;
1850 : 0 : bool created = false;
1851 : : int i;
1852 : :
1853 : : /* Determine the lowest qgroup that the parent
1854 : : * subvolume is assigned to. */
1855 : :
1856 [ # # ]: 0 : for (i = 0; i < n; i++) {
1857 : : uint64_t level;
1858 : :
1859 : 0 : r = btrfs_qgroupid_split(qgroups[i], &level, NULL);
1860 [ # # ]: 0 : if (r < 0)
1861 : 0 : return r;
1862 : :
1863 [ # # ]: 0 : if (level < lowest)
1864 : 0 : lowest = level;
1865 : : }
1866 : :
1867 [ # # ]: 0 : if (lowest <= 1) /* There are no levels left we could use insert an intermediary qgroup at */
1868 : 0 : return -EBUSY;
1869 : :
1870 : 0 : r = btrfs_qgroupid_make(lowest - 1, subvol_id, &new_qgroupid);
1871 [ # # ]: 0 : if (r < 0)
1872 : 0 : return r;
1873 : :
1874 : : /* Create the new intermediary group, unless it already exists */
1875 : 0 : r = btrfs_qgroup_create(fd, new_qgroupid);
1876 [ # # # # ]: 0 : if (r < 0 && r != -EEXIST)
1877 : 0 : return r;
1878 [ # # ]: 0 : if (r >= 0)
1879 : 0 : changed = created = true;
1880 : :
1881 [ # # ]: 0 : for (i = 0; i < n; i++) {
1882 : 0 : r = btrfs_qgroup_assign(fd, new_qgroupid, qgroups[i]);
1883 [ # # # # ]: 0 : if (r < 0 && r != -EEXIST) {
1884 [ # # ]: 0 : if (created)
1885 : 0 : (void) btrfs_qgroup_destroy_recursive(fd, new_qgroupid);
1886 : :
1887 : 0 : return r;
1888 : : }
1889 [ # # ]: 0 : if (r >= 0)
1890 : 0 : changed = true;
1891 : : }
1892 : :
1893 : 0 : r = btrfs_qgroup_assign(fd, subvol_id, new_qgroupid);
1894 [ # # # # ]: 0 : if (r < 0 && r != -EEXIST) {
1895 [ # # ]: 0 : if (created)
1896 : 0 : (void) btrfs_qgroup_destroy_recursive(fd, new_qgroupid);
1897 : 0 : return r;
1898 : : }
1899 [ # # ]: 0 : if (r >= 0)
1900 : 0 : changed = true;
1901 : :
1902 : : } else {
1903 : : int i;
1904 : :
1905 : : /* Assign our subvolume to all the same qgroups as the parent */
1906 : :
1907 [ # # ]: 0 : for (i = 0; i < n; i++) {
1908 : 0 : r = btrfs_qgroup_assign(fd, subvol_id, qgroups[i]);
1909 [ # # # # ]: 0 : if (r < 0 && r != -EEXIST)
1910 : 0 : return r;
1911 [ # # ]: 0 : if (r >= 0)
1912 : 0 : changed = true;
1913 : : }
1914 : : }
1915 : :
1916 : 0 : return changed;
1917 : : }
1918 : :
1919 : 0 : int btrfs_subvol_auto_qgroup(const char *path, uint64_t subvol_id, bool create_intermediary_qgroup) {
1920 : 0 : _cleanup_close_ int fd = -1;
1921 : :
1922 : 0 : fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1923 [ # # ]: 0 : if (fd < 0)
1924 : 0 : return -errno;
1925 : :
1926 : 0 : return btrfs_subvol_auto_qgroup_fd(fd, subvol_id, create_intermediary_qgroup);
1927 : : }
1928 : :
1929 : 0 : int btrfs_subvol_get_parent(int fd, uint64_t subvol_id, uint64_t *ret) {
1930 : :
1931 : 0 : struct btrfs_ioctl_search_args args = {
1932 : : /* Tree of tree roots */
1933 : : .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
1934 : :
1935 : : /* Look precisely for the subvolume items */
1936 : : .key.min_type = BTRFS_ROOT_BACKREF_KEY,
1937 : : .key.max_type = BTRFS_ROOT_BACKREF_KEY,
1938 : :
1939 : : /* No restrictions on the other components */
1940 : : .key.min_offset = 0,
1941 : : .key.max_offset = (uint64_t) -1,
1942 : :
1943 : : .key.min_transid = 0,
1944 : : .key.max_transid = (uint64_t) -1,
1945 : : };
1946 : : int r;
1947 : :
1948 [ # # ]: 0 : assert(fd >= 0);
1949 [ # # ]: 0 : assert(ret);
1950 : :
1951 [ # # ]: 0 : if (subvol_id == 0) {
1952 : 0 : r = btrfs_subvol_get_id_fd(fd, &subvol_id);
1953 [ # # ]: 0 : if (r < 0)
1954 : 0 : return r;
1955 : : } else {
1956 : 0 : r = btrfs_is_filesystem(fd);
1957 [ # # ]: 0 : if (r < 0)
1958 : 0 : return r;
1959 [ # # ]: 0 : if (!r)
1960 : 0 : return -ENOTTY;
1961 : : }
1962 : :
1963 : 0 : args.key.min_objectid = args.key.max_objectid = subvol_id;
1964 : :
1965 [ # # ]: 0 : while (btrfs_ioctl_search_args_compare(&args) <= 0) {
1966 : : const struct btrfs_ioctl_search_header *sh;
1967 : : unsigned i;
1968 : :
1969 : 0 : args.key.nr_items = 256;
1970 [ # # ]: 0 : if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
1971 : 0 : return negative_errno();
1972 : :
1973 [ # # ]: 0 : if (args.key.nr_items <= 0)
1974 : 0 : break;
1975 : :
1976 [ # # ]: 0 : FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
1977 : :
1978 [ # # ]: 0 : if (sh->type != BTRFS_ROOT_BACKREF_KEY)
1979 : 0 : continue;
1980 [ # # ]: 0 : if (sh->objectid != subvol_id)
1981 : 0 : continue;
1982 : :
1983 : 0 : *ret = sh->offset;
1984 : 0 : return 0;
1985 : : }
1986 : : }
1987 : :
1988 : 0 : return -ENXIO;
1989 : : }
|