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