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