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