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