]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/btrfs-util.c
Merge pull request #1907 from evverx/fix-pam-install-on-debian
[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;
12ee6186
LP
578 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) {
579 if (errno == ENOENT) /* quota tree is missing: quota disabled */
580 break;
581
b6b18498 582 return -errno;
12ee6186 583 }
b6b18498
LP
584
585 if (args.key.nr_items <= 0)
586 break;
587
5743a585 588 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
b6b18498 589
5743a585
LP
590 /* Make sure we start the next search at least from this entry */
591 btrfs_ioctl_search_args_set(&args, sh);
b6b18498
LP
592
593 if (sh->objectid != 0)
594 continue;
5bcd08db 595 if (sh->offset != qgroupid)
b6b18498
LP
596 continue;
597
b6b18498 598 if (sh->type == BTRFS_QGROUP_INFO_KEY) {
5743a585 599 const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
b6b18498 600
cb81cd80 601 ret->referenced = le64toh(qii->rfer);
b6b18498
LP
602 ret->exclusive = le64toh(qii->excl);
603
604 found_info = true;
605
606 } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
5743a585 607 const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
b6b18498 608
5bcd08db
LP
609 if (le64toh(qli->flags) & BTRFS_QGROUP_LIMIT_MAX_RFER)
610 ret->referenced_max = le64toh(qli->max_rfer);
611 else
cb81cd80 612 ret->referenced_max = (uint64_t) -1;
5bcd08db
LP
613
614 if (le64toh(qli->flags) & BTRFS_QGROUP_LIMIT_MAX_EXCL)
615 ret->exclusive_max = le64toh(qli->max_excl);
616 else
b6b18498
LP
617 ret->exclusive_max = (uint64_t) -1;
618
619 found_limit = true;
620 }
621
622 if (found_info && found_limit)
623 goto finish;
624 }
625
5743a585
LP
626 /* Increase search key by one, to read the next item, if we can. */
627 if (!btrfs_ioctl_search_args_inc(&args))
b6b18498
LP
628 break;
629 }
630
631finish:
632 if (!found_limit && !found_info)
633 return -ENODATA;
634
635 if (!found_info) {
cb81cd80 636 ret->referenced = (uint64_t) -1;
b6b18498
LP
637 ret->exclusive = (uint64_t) -1;
638 }
639
640 if (!found_limit) {
cb81cd80 641 ret->referenced_max = (uint64_t) -1;
b6b18498
LP
642 ret->exclusive_max = (uint64_t) -1;
643 }
10f9c755
LP
644
645 return 0;
646}
f27a3864 647
5bcd08db
LP
648int btrfs_qgroup_get_quota(const char *path, uint64_t qgroupid, BtrfsQuotaInfo *ret) {
649 _cleanup_close_ int fd = -1;
650
651 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
652 if (fd < 0)
653 return -errno;
654
655 return btrfs_qgroup_get_quota_fd(fd, qgroupid, ret);
656}
657
658int btrfs_subvol_find_subtree_qgroup(int fd, uint64_t subvol_id, uint64_t *ret) {
659 uint64_t level, lowest = (uint64_t) -1, lowest_qgroupid = 0;
660 _cleanup_free_ uint64_t *qgroups = NULL;
661 int r, n, i;
662
663 assert(fd >= 0);
664 assert(ret);
665
666 /* This finds the "subtree" qgroup for a specific
667 * subvolume. This only works for subvolumes that have been
668 * prepared with btrfs_subvol_auto_qgroup_fd() with
669 * insert_intermediary_qgroup=true (or equivalent). For others
670 * it will return the leaf qgroup instead. The two cases may
671 * be distuingished via the return value, which is 1 in case
672 * an appropriate "subtree" qgroup was found, and 0
673 * otherwise. */
674
675 if (subvol_id == 0) {
676 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
677 if (r < 0)
678 return r;
679 }
680
681 r = btrfs_qgroupid_split(subvol_id, &level, NULL);
682 if (r < 0)
683 return r;
684 if (level != 0) /* Input must be a leaf qgroup */
685 return -EINVAL;
686
687 n = btrfs_qgroup_find_parents(fd, subvol_id, &qgroups);
688 if (n < 0)
689 return n;
690
691 for (i = 0; i < n; i++) {
692 uint64_t id;
693
694 r = btrfs_qgroupid_split(qgroups[i], &level, &id);
695 if (r < 0)
696 return r;
697
698 if (id != subvol_id)
699 continue;
700
701 if (lowest == (uint64_t) -1 || level < lowest) {
702 lowest_qgroupid = qgroups[i];
703 lowest = level;
704 }
705 }
706
707 if (lowest == (uint64_t) -1) {
708 /* No suitable higher-level qgroup found, let's return
709 * the leaf qgroup instead, and indicate that with the
710 * return value. */
711
712 *ret = subvol_id;
713 return 0;
714 }
715
716 *ret = lowest_qgroupid;
717 return 1;
718}
719
720int btrfs_subvol_get_subtree_quota_fd(int fd, uint64_t subvol_id, BtrfsQuotaInfo *ret) {
721 uint64_t qgroupid;
722 int r;
723
724 assert(fd >= 0);
725 assert(ret);
726
727 /* This determines the quota data of the qgroup with the
728 * lowest level, that shares the id part with the specified
729 * subvolume. This is useful for determining the quota data
730 * for entire subvolume subtrees, as long as the subtrees have
731 * been set up with btrfs_qgroup_subvol_auto_fd() or in a
732 * compatible way */
733
734 r = btrfs_subvol_find_subtree_qgroup(fd, subvol_id, &qgroupid);
735 if (r < 0)
736 return r;
737
738 return btrfs_qgroup_get_quota_fd(fd, qgroupid, ret);
739}
740
741int btrfs_subvol_get_subtree_quota(const char *path, uint64_t subvol_id, BtrfsQuotaInfo *ret) {
742 _cleanup_close_ int fd = -1;
743
744 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
745 if (fd < 0)
746 return -errno;
747
748 return btrfs_subvol_get_subtree_quota_fd(fd, subvol_id, ret);
749}
750
f27a3864 751int btrfs_defrag_fd(int fd) {
62572894
LP
752 struct stat st;
753
f27a3864
LP
754 assert(fd >= 0);
755
62572894
LP
756 if (fstat(fd, &st) < 0)
757 return -errno;
758
759 if (!S_ISREG(st.st_mode))
760 return -EINVAL;
761
f27a3864
LP
762 if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
763 return -errno;
764
765 return 0;
766}
767
768int btrfs_defrag(const char *p) {
769 _cleanup_close_ int fd = -1;
770
771 fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
772 if (fd < 0)
773 return -errno;
774
775 return btrfs_defrag_fd(fd);
776}
754061ce
LP
777
778int btrfs_quota_enable_fd(int fd, bool b) {
779 struct btrfs_ioctl_quota_ctl_args args = {
780 .cmd = b ? BTRFS_QUOTA_CTL_ENABLE : BTRFS_QUOTA_CTL_DISABLE,
781 };
62572894 782 int r;
754061ce
LP
783
784 assert(fd >= 0);
785
62572894
LP
786 r = btrfs_is_filesystem(fd);
787 if (r < 0)
788 return r;
789 if (!r)
790 return -ENOTTY;
791
754061ce
LP
792 if (ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args) < 0)
793 return -errno;
794
795 return 0;
796}
797
798int btrfs_quota_enable(const char *path, bool b) {
799 _cleanup_close_ int fd = -1;
800
801 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
802 if (fd < 0)
803 return -errno;
804
805 return btrfs_quota_enable_fd(fd, b);
806}
d6ce17c7 807
5bcd08db
LP
808int btrfs_qgroup_set_limit_fd(int fd, uint64_t qgroupid, uint64_t referenced_max) {
809
d6ce17c7 810 struct btrfs_ioctl_qgroup_limit_args args = {
5bcd08db 811 .lim.max_rfer = referenced_max,
d6ce17c7
LP
812 .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER,
813 };
5bcd08db 814 unsigned c;
62572894 815 int r;
d6ce17c7
LP
816
817 assert(fd >= 0);
818
5bcd08db
LP
819 if (qgroupid == 0) {
820 r = btrfs_subvol_get_id_fd(fd, &qgroupid);
821 if (r < 0)
822 return r;
823 } else {
824 r = btrfs_is_filesystem(fd);
825 if (r < 0)
826 return r;
827 if (!r)
828 return -ENOTTY;
829 }
62572894 830
5bcd08db
LP
831 args.qgroupid = qgroupid;
832
833 for (c = 0;; c++) {
834 if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args) < 0) {
835
836 if (errno == EBUSY && c < 10) {
837 (void) btrfs_quota_scan_wait(fd);
838 continue;
839 }
840
841 return -errno;
842 }
843
844 break;
845 }
d6ce17c7
LP
846
847 return 0;
848}
849
5bcd08db
LP
850int btrfs_qgroup_set_limit(const char *path, uint64_t qgroupid, uint64_t referenced_max) {
851 _cleanup_close_ int fd = -1;
852
853 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
854 if (fd < 0)
855 return -errno;
856
857 return btrfs_qgroup_set_limit_fd(fd, qgroupid, referenced_max);
858}
859
860int btrfs_subvol_set_subtree_quota_limit_fd(int fd, uint64_t subvol_id, uint64_t referenced_max) {
861 uint64_t qgroupid;
862 int r;
863
864 assert(fd >= 0);
865
866 r = btrfs_subvol_find_subtree_qgroup(fd, subvol_id, &qgroupid);
867 if (r < 0)
868 return r;
869
870 return btrfs_qgroup_set_limit_fd(fd, qgroupid, referenced_max);
871}
872
873int btrfs_subvol_set_subtree_quota_limit(const char *path, uint64_t subvol_id, uint64_t referenced_max) {
d6ce17c7
LP
874 _cleanup_close_ int fd = -1;
875
876 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
877 if (fd < 0)
878 return -errno;
879
5bcd08db 880 return btrfs_subvol_set_subtree_quota_limit_fd(fd, subvol_id, referenced_max);
d6ce17c7 881}
efe02862 882
26166c88 883int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
efe02862
LP
884 struct btrfs_ioctl_vol_args args = {};
885 _cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL;
886 _cleanup_close_ int loop_fd = -1, backing_fd = -1;
887 struct stat st;
a7f7d1bd 888 dev_t dev = 0;
efe02862
LP
889 int r;
890
891 /* btrfs cannot handle file systems < 16M, hence use this as minimum */
892 if (new_size < 16*1024*1024)
893 new_size = 16*1024*1024;
894
895 r = btrfs_get_block_device_fd(fd, &dev);
896 if (r < 0)
897 return r;
898 if (r == 0)
899 return -ENODEV;
900
901 if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0)
902 return -ENOMEM;
903 r = read_one_line_file(p, &backing);
26166c88
LP
904 if (r == -ENOENT)
905 return -ENODEV;
efe02862
LP
906 if (r < 0)
907 return r;
908 if (isempty(backing) || !path_is_absolute(backing))
909 return -ENODEV;
910
911 backing_fd = open(backing, O_RDWR|O_CLOEXEC|O_NOCTTY);
912 if (backing_fd < 0)
913 return -errno;
914
915 if (fstat(backing_fd, &st) < 0)
916 return -errno;
917 if (!S_ISREG(st.st_mode))
918 return -ENODEV;
919
920 if (new_size == (uint64_t) st.st_size)
921 return 0;
922
26166c88
LP
923 if (grow_only && new_size < (uint64_t) st.st_size)
924 return -EINVAL;
925
efe02862
LP
926 if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0)
927 return -ENOMEM;
928 loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY);
929 if (loop_fd < 0)
930 return -errno;
931
932 if (snprintf(args.name, sizeof(args.name), "%" PRIu64, new_size) >= (int) sizeof(args.name))
933 return -EINVAL;
934
935 if (new_size < (uint64_t) st.st_size) {
936 /* Decrease size: first decrease btrfs size, then shorten loopback */
937 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
938 return -errno;
939 }
940
941 if (ftruncate(backing_fd, new_size) < 0)
942 return -errno;
943
944 if (ioctl(loop_fd, LOOP_SET_CAPACITY, 0) < 0)
945 return -errno;
946
947 if (new_size > (uint64_t) st.st_size) {
948 /* Increase size: first enlarge loopback, then increase btrfs size */
949 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
950 return -errno;
951 }
952
26166c88
LP
953 /* Make sure the free disk space is correctly updated for both file systems */
954 (void) fsync(fd);
955 (void) fsync(backing_fd);
956
957 return 1;
efe02862
LP
958}
959
26166c88 960int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) {
efe02862
LP
961 _cleanup_close_ int fd = -1;
962
963 fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
964 if (fd < 0)
965 return -errno;
966
26166c88 967 return btrfs_resize_loopback_fd(fd, new_size, grow_only);
efe02862 968}
d9e2daaf 969
5bcd08db 970int btrfs_qgroupid_make(uint64_t level, uint64_t id, uint64_t *ret) {
3f952f92
LP
971 assert(ret);
972
973 if (level >= (UINT64_C(1) << (64 - BTRFS_QGROUP_LEVEL_SHIFT)))
974 return -EINVAL;
975
976 if (id >= (UINT64_C(1) << BTRFS_QGROUP_LEVEL_SHIFT))
977 return -EINVAL;
978
979 *ret = (level << BTRFS_QGROUP_LEVEL_SHIFT) | id;
980 return 0;
981}
982
5bcd08db
LP
983int btrfs_qgroupid_split(uint64_t qgroupid, uint64_t *level, uint64_t *id) {
984 assert(level || id);
985
986 if (level)
987 *level = qgroupid >> BTRFS_QGROUP_LEVEL_SHIFT;
988
989 if (id)
990 *id = qgroupid & ((UINT64_C(1) << BTRFS_QGROUP_LEVEL_SHIFT) - 1);
991
992 return 0;
993}
994
995static int qgroup_create_or_destroy(int fd, bool b, uint64_t qgroupid) {
3f952f92
LP
996
997 struct btrfs_ioctl_qgroup_create_args args = {
998 .create = b,
5bcd08db 999 .qgroupid = qgroupid,
3f952f92 1000 };
5bcd08db 1001 unsigned c;
3f952f92
LP
1002 int r;
1003
5bcd08db 1004 r = btrfs_is_filesystem(fd);
3f952f92
LP
1005 if (r < 0)
1006 return r;
5bcd08db
LP
1007 if (r == 0)
1008 return -ENOTTY;
1009
1010 for (c = 0;; c++) {
1011 if (ioctl(fd, BTRFS_IOC_QGROUP_CREATE, &args) < 0) {
1012
be6d467c
LP
1013 /* If quota is not enabled, we get EINVAL. Turn this into a recognizable error */
1014 if (errno == EINVAL)
1015 return -ENOPROTOOPT;
1016
5bcd08db
LP
1017 if (errno == EBUSY && c < 10) {
1018 (void) btrfs_quota_scan_wait(fd);
1019 continue;
1020 }
1021
1022 return -errno;
1023 }
3f952f92 1024
5bcd08db
LP
1025 break;
1026 }
1027
1028 return 0;
1029}
1030
1031int btrfs_qgroup_create(int fd, uint64_t qgroupid) {
1032 return qgroup_create_or_destroy(fd, true, qgroupid);
1033}
1034
1035int btrfs_qgroup_destroy(int fd, uint64_t qgroupid) {
1036 return qgroup_create_or_destroy(fd, false, qgroupid);
1037}
1038
1039int btrfs_qgroup_destroy_recursive(int fd, uint64_t qgroupid) {
1040 _cleanup_free_ uint64_t *qgroups = NULL;
1041 uint64_t subvol_id;
1042 int i, n, r;
1043
1044 /* Destroys the specified qgroup, but unassigns it from all
1045 * its parents first. Also, it recursively destroys all
1046 * qgroups it is assgined to that have the same id part of the
1047 * qgroupid as the specified group. */
1048
1049 r = btrfs_qgroupid_split(qgroupid, NULL, &subvol_id);
1050 if (r < 0)
1051 return r;
1052
1053 n = btrfs_qgroup_find_parents(fd, qgroupid, &qgroups);
1054 if (n < 0)
1055 return n;
1056
1057 for (i = 0; i < n; i++) {
1058 uint64_t id;
1059
1060 r = btrfs_qgroupid_split(qgroups[i], NULL, &id);
1061 if (r < 0)
1062 return r;
1063
1064 r = btrfs_qgroup_unassign(fd, qgroupid, qgroups[i]);
1065 if (r < 0)
1066 return r;
1067
1068 if (id != subvol_id)
1069 continue;
1070
1071 /* The parent qgroupid shares the same id part with
1072 * us? If so, destroy it too. */
1073
1074 (void) btrfs_qgroup_destroy_recursive(fd, qgroups[i]);
1075 }
1076
1077 return btrfs_qgroup_destroy(fd, qgroupid);
1078}
1079
1080int btrfs_quota_scan_start(int fd) {
1081 struct btrfs_ioctl_quota_rescan_args args = {};
1082
1083 assert(fd >= 0);
1084
1085 if (ioctl(fd, BTRFS_IOC_QUOTA_RESCAN, &args) < 0)
3f952f92
LP
1086 return -errno;
1087
1088 return 0;
1089}
1090
5bcd08db
LP
1091int btrfs_quota_scan_wait(int fd) {
1092 assert(fd >= 0);
1093
1094 if (ioctl(fd, BTRFS_IOC_QUOTA_RESCAN_WAIT) < 0)
1095 return -errno;
1096
1097 return 0;
3f952f92
LP
1098}
1099
5bcd08db
LP
1100int btrfs_quota_scan_ongoing(int fd) {
1101 struct btrfs_ioctl_quota_rescan_args args = {};
1102
1103 assert(fd >= 0);
1104
1105 if (ioctl(fd, BTRFS_IOC_QUOTA_RESCAN_STATUS, &args) < 0)
1106 return -errno;
1107
1108 return !!args.flags;
3f952f92
LP
1109}
1110
5bcd08db
LP
1111static int qgroup_assign_or_unassign(int fd, bool b, uint64_t child, uint64_t parent) {
1112 struct btrfs_ioctl_qgroup_assign_args args = {
1113 .assign = b,
1114 .src = child,
1115 .dst = parent,
1116 };
1117 unsigned c;
1118 int r;
1119
1120 r = btrfs_is_filesystem(fd);
1121 if (r < 0)
1122 return r;
1123 if (r == 0)
1124 return -ENOTTY;
1125
1126 for (c = 0;; c++) {
1127 r = ioctl(fd, BTRFS_IOC_QGROUP_ASSIGN, &args);
1128 if (r < 0) {
1129 if (errno == EBUSY && c < 10) {
1130 (void) btrfs_quota_scan_wait(fd);
1131 continue;
1132 }
1133
1134 return -errno;
1135 }
1136
1137 if (r == 0)
1138 return 0;
1139
1140 /* If the return value is > 0, we need to request a rescan */
1141
1142 (void) btrfs_quota_scan_start(fd);
1143 return 1;
1144 }
1145}
1146
1147int btrfs_qgroup_assign(int fd, uint64_t child, uint64_t parent) {
1148 return qgroup_assign_or_unassign(fd, true, child, parent);
1149}
1150
1151int btrfs_qgroup_unassign(int fd, uint64_t child, uint64_t parent) {
1152 return qgroup_assign_or_unassign(fd, false, child, parent);
1153}
1154
1155static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol_id, BtrfsRemoveFlags flags) {
d9e2daaf
LP
1156 struct btrfs_ioctl_search_args args = {
1157 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
1158
1159 .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
1160 .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
1161
1162 .key.min_type = BTRFS_ROOT_BACKREF_KEY,
1163 .key.max_type = BTRFS_ROOT_BACKREF_KEY,
1164
1165 .key.min_transid = 0,
1166 .key.max_transid = (uint64_t) -1,
1167 };
1168
1169 struct btrfs_ioctl_vol_args vol_args = {};
1170 _cleanup_close_ int subvol_fd = -1;
62572894 1171 struct stat st;
3986b258 1172 bool made_writable = false;
d9e2daaf
LP
1173 int r;
1174
1175 assert(fd >= 0);
1176 assert(subvolume);
1177
62572894
LP
1178 if (fstat(fd, &st) < 0)
1179 return -errno;
1180
1181 if (!S_ISDIR(st.st_mode))
1182 return -EINVAL;
1183
d9e2daaf
LP
1184 subvol_fd = openat(fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1185 if (subvol_fd < 0)
1186 return -errno;
1187
1188 if (subvol_id == 0) {
1189 r = btrfs_subvol_get_id_fd(subvol_fd, &subvol_id);
1190 if (r < 0)
1191 return r;
1192 }
1193
3f952f92
LP
1194 /* First, try to remove the subvolume. If it happens to be
1195 * already empty, this will just work. */
1196 strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
1197 if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) >= 0) {
5bcd08db 1198 (void) btrfs_qgroup_destroy_recursive(fd, subvol_id); /* for the leaf subvolumes, the qgroup id is identical to the subvol id */
3f952f92
LP
1199 return 0;
1200 }
5bcd08db 1201 if (!(flags & BTRFS_REMOVE_RECURSIVE) || errno != ENOTEMPTY)
3f952f92
LP
1202 return -errno;
1203
1204 /* OK, the subvolume is not empty, let's look for child
1205 * subvolumes, and remove them, first */
1206
d9e2daaf
LP
1207 args.key.min_offset = args.key.max_offset = subvol_id;
1208
1209 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
1210 const struct btrfs_ioctl_search_header *sh;
1211 unsigned i;
1212
1213 args.key.nr_items = 256;
1214 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
1215 return -errno;
1216
1217 if (args.key.nr_items <= 0)
1218 break;
1219
1220 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
1221 _cleanup_free_ char *p = NULL;
1222 const struct btrfs_root_ref *ref;
1223 struct btrfs_ioctl_ino_lookup_args ino_args;
1224
1225 btrfs_ioctl_search_args_set(&args, sh);
1226
1227 if (sh->type != BTRFS_ROOT_BACKREF_KEY)
1228 continue;
1229 if (sh->offset != subvol_id)
1230 continue;
1231
1232 ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
1233
1234 p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
1235 if (!p)
1236 return -ENOMEM;
1237
1238 zero(ino_args);
1239 ino_args.treeid = subvol_id;
cbf21ecc 1240 ino_args.objectid = htole64(ref->dirid);
d9e2daaf
LP
1241
1242 if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
1243 return -errno;
1244
3986b258
LP
1245 if (!made_writable) {
1246 r = btrfs_subvol_set_read_only_fd(subvol_fd, false);
1247 if (r < 0)
1248 return r;
1249
1250 made_writable = true;
1251 }
1252
d9e2daaf
LP
1253 if (isempty(ino_args.name))
1254 /* Subvolume is in the top-level
1255 * directory of the subvolume. */
5bcd08db 1256 r = subvol_remove_children(subvol_fd, p, sh->objectid, flags);
d9e2daaf
LP
1257 else {
1258 _cleanup_close_ int child_fd = -1;
1259
1260 /* Subvolume is somewhere further down,
1261 * hence we need to open the
1262 * containing directory first */
1263
1264 child_fd = openat(subvol_fd, ino_args.name, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1265 if (child_fd < 0)
1266 return -errno;
1267
5bcd08db 1268 r = subvol_remove_children(child_fd, p, sh->objectid, flags);
d9e2daaf
LP
1269 }
1270 if (r < 0)
1271 return r;
1272 }
1273
1274 /* Increase search key by one, to read the next item, if we can. */
1275 if (!btrfs_ioctl_search_args_inc(&args))
1276 break;
1277 }
1278
1279 /* OK, the child subvolumes should all be gone now, let's try
1280 * again to remove the subvolume */
1281 if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) < 0)
1282 return -errno;
1283
5bcd08db 1284 (void) btrfs_qgroup_destroy_recursive(fd, subvol_id);
d9e2daaf
LP
1285 return 0;
1286}
1287
5bcd08db 1288int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags) {
d9e2daaf
LP
1289 _cleanup_close_ int fd = -1;
1290 const char *subvolume;
1291 int r;
1292
1293 assert(path);
1294
1295 r = extract_subvolume_name(path, &subvolume);
1296 if (r < 0)
1297 return r;
1298
1299 fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1300 if (fd < 0)
1301 return fd;
1302
5bcd08db
LP
1303 return subvol_remove_children(fd, subvolume, 0, flags);
1304}
1305
1306int btrfs_subvol_remove_fd(int fd, const char *subvolume, BtrfsRemoveFlags flags) {
1307 return subvol_remove_children(fd, subvolume, 0, flags);
1308}
1309
1310int btrfs_qgroup_copy_limits(int fd, uint64_t old_qgroupid, uint64_t new_qgroupid) {
1311
1312 struct btrfs_ioctl_search_args args = {
1313 /* Tree of quota items */
1314 .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
1315
1316 /* The object ID is always 0 */
1317 .key.min_objectid = 0,
1318 .key.max_objectid = 0,
1319
1320 /* Look precisely for the quota items */
1321 .key.min_type = BTRFS_QGROUP_LIMIT_KEY,
1322 .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
1323
1324 /* For our qgroup */
1325 .key.min_offset = old_qgroupid,
1326 .key.max_offset = old_qgroupid,
1327
1328 /* No restrictions on the other components */
1329 .key.min_transid = 0,
1330 .key.max_transid = (uint64_t) -1,
1331 };
1332
1333 int r;
1334
1335 r = btrfs_is_filesystem(fd);
1336 if (r < 0)
1337 return r;
1338 if (!r)
1339 return -ENOTTY;
1340
1341 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
1342 const struct btrfs_ioctl_search_header *sh;
1343 unsigned i;
1344
1345 args.key.nr_items = 256;
12ee6186
LP
1346 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) {
1347 if (errno == ENOENT) /* quota tree missing: quota is not enabled, hence nothing to copy */
1348 break;
1349
5bcd08db 1350 return -errno;
12ee6186 1351 }
5bcd08db
LP
1352
1353 if (args.key.nr_items <= 0)
1354 break;
1355
1356 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
1357 const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
1358 struct btrfs_ioctl_qgroup_limit_args qargs;
1359 unsigned c;
1360
1361 /* Make sure we start the next search at least from this entry */
1362 btrfs_ioctl_search_args_set(&args, sh);
1363
1364 if (sh->objectid != 0)
1365 continue;
1366 if (sh->type != BTRFS_QGROUP_LIMIT_KEY)
1367 continue;
1368 if (sh->offset != old_qgroupid)
1369 continue;
1370
1371 /* We found the entry, now copy things over. */
1372
1373 qargs = (struct btrfs_ioctl_qgroup_limit_args) {
1374 .qgroupid = new_qgroupid,
1375
1376 .lim.max_rfer = le64toh(qli->max_rfer),
1377 .lim.max_excl = le64toh(qli->max_excl),
1378 .lim.rsv_rfer = le64toh(qli->rsv_rfer),
1379 .lim.rsv_excl = le64toh(qli->rsv_excl),
1380
1381 .lim.flags = le64toh(qli->flags) & (BTRFS_QGROUP_LIMIT_MAX_RFER|
1382 BTRFS_QGROUP_LIMIT_MAX_EXCL|
1383 BTRFS_QGROUP_LIMIT_RSV_RFER|
1384 BTRFS_QGROUP_LIMIT_RSV_EXCL),
1385 };
1386
1387 for (c = 0;; c++) {
1388 if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &qargs) < 0) {
1389 if (errno == EBUSY && c < 10) {
1390 (void) btrfs_quota_scan_wait(fd);
1391 continue;
1392 }
1393 return -errno;
1394 }
1395
1396 break;
1397 }
1398
1399 return 1;
1400 }
1401
1402 /* Increase search key by one, to read the next item, if we can. */
1403 if (!btrfs_ioctl_search_args_inc(&args))
1404 break;
1405 }
1406
1407 return 0;
d9e2daaf
LP
1408}
1409
5bcd08db
LP
1410static int copy_quota_hierarchy(int fd, uint64_t old_subvol_id, uint64_t new_subvol_id) {
1411 _cleanup_free_ uint64_t *old_qgroups = NULL, *old_parent_qgroups = NULL;
1412 bool copy_from_parent = false, insert_intermediary_qgroup = false;
1413 int n_old_qgroups, n_old_parent_qgroups, r, i;
1414 uint64_t old_parent_id;
1415
1416 assert(fd >= 0);
1417
1418 /* Copies a reduced form of quota information from the old to
1419 * the new subvolume. */
1420
1421 n_old_qgroups = btrfs_qgroup_find_parents(fd, old_subvol_id, &old_qgroups);
1422 if (n_old_qgroups <= 0) /* Nothing to copy */
1423 return n_old_qgroups;
1424
1425 r = btrfs_subvol_get_parent(fd, old_subvol_id, &old_parent_id);
1426 if (r < 0)
1427 return r;
1428
1429 n_old_parent_qgroups = btrfs_qgroup_find_parents(fd, old_parent_id, &old_parent_qgroups);
1430 if (n_old_parent_qgroups < 0)
1431 return n_old_parent_qgroups;
1432
1433 for (i = 0; i < n_old_qgroups; i++) {
1434 uint64_t id;
1435 int j;
1436
1437 r = btrfs_qgroupid_split(old_qgroups[i], NULL, &id);
1438 if (r < 0)
1439 return r;
1440
1441 if (id == old_subvol_id) {
1442 /* The old subvolume was member of a qgroup
1443 * that had the same id, but a different level
1444 * as it self. Let's set up something similar
1445 * in the destination. */
1446 insert_intermediary_qgroup = true;
1447 break;
1448 }
1449
1450 for (j = 0; j < n_old_parent_qgroups; j++)
1451 if (old_parent_qgroups[j] == old_qgroups[i]) {
1452 /* The old subvolume shared a common
1453 * parent qgroup with its parent
1454 * subvolume. Let's set up something
1455 * similar in the destination. */
1456 copy_from_parent = true;
1457 }
1458 }
1459
1460 if (!insert_intermediary_qgroup && !copy_from_parent)
1461 return 0;
1462
1463 return btrfs_subvol_auto_qgroup_fd(fd, new_subvol_id, insert_intermediary_qgroup);
1464}
1465
1466static int copy_subtree_quota_limits(int fd, uint64_t old_subvol, uint64_t new_subvol) {
1467 uint64_t old_subtree_qgroup, new_subtree_qgroup;
1468 bool changed;
1469 int r;
1470
1471 /* First copy the leaf limits */
1472 r = btrfs_qgroup_copy_limits(fd, old_subvol, new_subvol);
1473 if (r < 0)
1474 return r;
1475 changed = r > 0;
1476
1477 /* Then, try to copy the subtree limits, if there are any. */
1478 r = btrfs_subvol_find_subtree_qgroup(fd, old_subvol, &old_subtree_qgroup);
1479 if (r < 0)
1480 return r;
1481 if (r == 0)
1482 return changed;
1483
1484 r = btrfs_subvol_find_subtree_qgroup(fd, new_subvol, &new_subtree_qgroup);
1485 if (r < 0)
1486 return r;
1487 if (r == 0)
1488 return changed;
1489
1490 r = btrfs_qgroup_copy_limits(fd, old_subtree_qgroup, new_subtree_qgroup);
1491 if (r != 0)
1492 return r;
1493
1494 return changed;
d9e2daaf 1495}
f70a17f8 1496
90578cbd 1497static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolume, uint64_t old_subvol_id, BtrfsSnapshotFlags flags) {
f70a17f8
LP
1498
1499 struct btrfs_ioctl_search_args args = {
1500 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
1501
1502 .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
1503 .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
1504
1505 .key.min_type = BTRFS_ROOT_BACKREF_KEY,
1506 .key.max_type = BTRFS_ROOT_BACKREF_KEY,
1507
1508 .key.min_transid = 0,
1509 .key.max_transid = (uint64_t) -1,
1510 };
1511
1512 struct btrfs_ioctl_vol_args_v2 vol_args = {
1513 .flags = flags & BTRFS_SNAPSHOT_READ_ONLY ? BTRFS_SUBVOL_RDONLY : 0,
1514 .fd = old_fd,
1515 };
ffb296b2 1516 _cleanup_close_ int subvolume_fd = -1;
90578cbd
LP
1517 uint64_t new_subvol_id;
1518 int r;
f70a17f8
LP
1519
1520 assert(old_fd >= 0);
1521 assert(new_fd >= 0);
1522 assert(subvolume);
1523
1524 strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
f70a17f8
LP
1525
1526 if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &vol_args) < 0)
1527 return -errno;
1528
5bcd08db
LP
1529 if (!(flags & BTRFS_SNAPSHOT_RECURSIVE) &&
1530 !(flags & BTRFS_SNAPSHOT_QUOTA))
f70a17f8
LP
1531 return 0;
1532
90578cbd
LP
1533 if (old_subvol_id == 0) {
1534 r = btrfs_subvol_get_id_fd(old_fd, &old_subvol_id);
f70a17f8
LP
1535 if (r < 0)
1536 return r;
1537 }
1538
90578cbd
LP
1539 r = btrfs_subvol_get_id(new_fd, vol_args.name, &new_subvol_id);
1540 if (r < 0)
1541 return r;
1542
5bcd08db
LP
1543 if (flags & BTRFS_SNAPSHOT_QUOTA)
1544 (void) copy_quota_hierarchy(new_fd, old_subvol_id, new_subvol_id);
1545
1546 if (!(flags & BTRFS_SNAPSHOT_RECURSIVE)) {
1547
1548 if (flags & BTRFS_SNAPSHOT_QUOTA)
1549 (void) copy_subtree_quota_limits(new_fd, old_subvol_id, new_subvol_id);
1550
1551 return 0;
1552 }
1553
90578cbd 1554 args.key.min_offset = args.key.max_offset = old_subvol_id;
f70a17f8
LP
1555
1556 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
1557 const struct btrfs_ioctl_search_header *sh;
1558 unsigned i;
1559
1560 args.key.nr_items = 256;
1561 if (ioctl(old_fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
1562 return -errno;
1563
1564 if (args.key.nr_items <= 0)
1565 break;
1566
1567 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
1568 _cleanup_free_ char *p = NULL, *c = NULL, *np = NULL;
1569 struct btrfs_ioctl_ino_lookup_args ino_args;
1570 const struct btrfs_root_ref *ref;
1571 _cleanup_close_ int old_child_fd = -1, new_child_fd = -1;
1572
1573 btrfs_ioctl_search_args_set(&args, sh);
1574
1575 if (sh->type != BTRFS_ROOT_BACKREF_KEY)
1576 continue;
90578cbd
LP
1577
1578 /* Avoid finding the source subvolume a second
1579 * time */
1580 if (sh->offset != old_subvol_id)
f70a17f8
LP
1581 continue;
1582
90578cbd
LP
1583 /* Avoid running into loops if the new
1584 * subvolume is below the old one. */
1585 if (sh->objectid == new_subvol_id)
1586 continue;
f70a17f8 1587
90578cbd 1588 ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
f70a17f8
LP
1589 p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
1590 if (!p)
1591 return -ENOMEM;
1592
1593 zero(ino_args);
90578cbd 1594 ino_args.treeid = old_subvol_id;
f70a17f8
LP
1595 ino_args.objectid = htole64(ref->dirid);
1596
1597 if (ioctl(old_fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
1598 return -errno;
1599
1600 /* The kernel returns an empty name if the
1601 * subvolume is in the top-level directory,
1602 * and otherwise appends a slash, so that we
1603 * can just concatenate easily here, without
1604 * adding a slash. */
1605 c = strappend(ino_args.name, p);
1606 if (!c)
1607 return -ENOMEM;
1608
1609 old_child_fd = openat(old_fd, c, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1610 if (old_child_fd < 0)
1611 return -errno;
1612
1613 np = strjoin(subvolume, "/", ino_args.name, NULL);
1614 if (!np)
1615 return -ENOMEM;
1616
1617 new_child_fd = openat(new_fd, np, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1618 if (new_child_fd < 0)
1619 return -errno;
1620
ffb296b2
LP
1621 if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
1622 /* If the snapshot is read-only we
1623 * need to mark it writable
1624 * temporarily, to put the subsnapshot
1625 * into place. */
1626
1627 if (subvolume_fd < 0) {
1628 subvolume_fd = openat(new_fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1629 if (subvolume_fd < 0)
1630 return -errno;
1631 }
1632
1633 r = btrfs_subvol_set_read_only_fd(subvolume_fd, false);
1634 if (r < 0)
1635 return r;
1636 }
1637
f70a17f8 1638 /* When btrfs clones the subvolumes, child
90578cbd 1639 * subvolumes appear as empty directories. Remove
f70a17f8
LP
1640 * them, so that we can create a new snapshot
1641 * in their place */
ffb296b2
LP
1642 if (unlinkat(new_child_fd, p, AT_REMOVEDIR) < 0) {
1643 int k = -errno;
1644
1645 if (flags & BTRFS_SNAPSHOT_READ_ONLY)
1646 (void) btrfs_subvol_set_read_only_fd(subvolume_fd, true);
1647
1648 return k;
1649 }
f70a17f8
LP
1650
1651 r = subvol_snapshot_children(old_child_fd, new_child_fd, p, sh->objectid, flags & ~BTRFS_SNAPSHOT_FALLBACK_COPY);
ffb296b2
LP
1652
1653 /* Restore the readonly flag */
1654 if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
1655 int k;
1656
1657 k = btrfs_subvol_set_read_only_fd(subvolume_fd, true);
1658 if (r >= 0 && k < 0)
1659 return k;
1660 }
1661
f70a17f8
LP
1662 if (r < 0)
1663 return r;
1664 }
1665
1666 /* Increase search key by one, to read the next item, if we can. */
1667 if (!btrfs_ioctl_search_args_inc(&args))
1668 break;
1669 }
1670
5bcd08db
LP
1671 if (flags & BTRFS_SNAPSHOT_QUOTA)
1672 (void) copy_subtree_quota_limits(new_fd, old_subvol_id, new_subvol_id);
1673
f70a17f8
LP
1674 return 0;
1675}
1676
1677int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) {
1678 _cleanup_close_ int new_fd = -1;
1679 const char *subvolume;
1680 int r;
1681
1682 assert(old_fd >= 0);
1683 assert(new_path);
1684
21222ea5 1685 r = btrfs_is_subvol(old_fd);
f70a17f8
LP
1686 if (r < 0)
1687 return r;
1688 if (r == 0) {
1689 if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY))
1690 return -EISDIR;
1691
1692 r = btrfs_subvol_make(new_path);
1693 if (r < 0)
1694 return r;
1695
1696 r = copy_directory_fd(old_fd, new_path, true);
1697 if (r < 0) {
5bcd08db 1698 (void) btrfs_subvol_remove(new_path, BTRFS_REMOVE_QUOTA);
f70a17f8
LP
1699 return r;
1700 }
1701
1702 if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
1703 r = btrfs_subvol_set_read_only(new_path, true);
1704 if (r < 0) {
5bcd08db 1705 (void) btrfs_subvol_remove(new_path, BTRFS_REMOVE_QUOTA);
f70a17f8
LP
1706 return r;
1707 }
1708 }
1709
1710 return 0;
1711 }
1712
1713 r = extract_subvolume_name(new_path, &subvolume);
1714 if (r < 0)
1715 return r;
1716
1717 new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1718 if (new_fd < 0)
1719 return new_fd;
1720
1721 return subvol_snapshot_children(old_fd, new_fd, subvolume, 0, flags);
1722}
1723
1724int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) {
1725 _cleanup_close_ int old_fd = -1;
1726
1727 assert(old_path);
1728 assert(new_path);
1729
1730 old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1731 if (old_fd < 0)
1732 return -errno;
1733
1734 return btrfs_subvol_snapshot_fd(old_fd, new_path, flags);
1735}
5bcd08db
LP
1736
1737int btrfs_qgroup_find_parents(int fd, uint64_t qgroupid, uint64_t **ret) {
1738
1739 struct btrfs_ioctl_search_args args = {
1740 /* Tree of quota items */
1741 .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
1742
1743 /* Look precisely for the quota relation items */
1744 .key.min_type = BTRFS_QGROUP_RELATION_KEY,
1745 .key.max_type = BTRFS_QGROUP_RELATION_KEY,
1746
1747 /* No restrictions on the other components */
1748 .key.min_offset = 0,
1749 .key.max_offset = (uint64_t) -1,
1750
1751 .key.min_transid = 0,
1752 .key.max_transid = (uint64_t) -1,
1753 };
1754
1755 _cleanup_free_ uint64_t *items = NULL;
1756 size_t n_items = 0, n_allocated = 0;
1757 int r;
1758
1759 assert(fd >= 0);
1760 assert(ret);
1761
1762 if (qgroupid == 0) {
1763 r = btrfs_subvol_get_id_fd(fd, &qgroupid);
1764 if (r < 0)
1765 return r;
1766 } else {
1767 r = btrfs_is_filesystem(fd);
1768 if (r < 0)
1769 return r;
1770 if (!r)
1771 return -ENOTTY;
1772 }
1773
1774 args.key.min_objectid = args.key.max_objectid = qgroupid;
1775
1776 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
1777 const struct btrfs_ioctl_search_header *sh;
1778 unsigned i;
1779
1780 args.key.nr_items = 256;
12ee6186
LP
1781 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) {
1782 if (errno == ENOENT) /* quota tree missing: quota is disabled */
1783 break;
1784
5bcd08db 1785 return -errno;
12ee6186 1786 }
5bcd08db
LP
1787
1788 if (args.key.nr_items <= 0)
1789 break;
1790
1791 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
1792
1793 /* Make sure we start the next search at least from this entry */
1794 btrfs_ioctl_search_args_set(&args, sh);
1795
1796 if (sh->type != BTRFS_QGROUP_RELATION_KEY)
1797 continue;
1798 if (sh->offset < sh->objectid)
1799 continue;
1800 if (sh->objectid != qgroupid)
1801 continue;
1802
1803 if (!GREEDY_REALLOC(items, n_allocated, n_items+1))
1804 return -ENOMEM;
1805
1806 items[n_items++] = sh->offset;
1807 }
1808
1809 /* Increase search key by one, to read the next item, if we can. */
1810 if (!btrfs_ioctl_search_args_inc(&args))
1811 break;
1812 }
1813
1814 if (n_items <= 0) {
1815 *ret = NULL;
1816 return 0;
1817 }
1818
1819 *ret = items;
1820 items = NULL;
1821
1822 return (int) n_items;
1823}
1824
1825int btrfs_subvol_auto_qgroup_fd(int fd, uint64_t subvol_id, bool insert_intermediary_qgroup) {
1826 _cleanup_free_ uint64_t *qgroups = NULL;
1827 uint64_t parent_subvol;
1828 bool changed = false;
1829 int n = 0, r;
1830
1831 assert(fd >= 0);
1832
1833 /*
1834 * Sets up the specified subvolume's qgroup automatically in
1835 * one of two ways:
1836 *
1837 * If insert_intermediary_qgroup is false, the subvolume's
1838 * leaf qgroup will be assigned to the same parent qgroups as
1839 * the subvolume's parent subvolume.
1840 *
1841 * If insert_intermediary_qgroup is true a new intermediary
1842 * higher-level qgroup is created, with a higher level number,
1843 * but reusing the id of the subvolume. The level number is
1844 * picked as one smaller than the lowest level qgroup the
1845 * parent subvolume is a member of. If the parent subvolume's
1846 * leaf qgroup is assigned to no higher-level qgroup a new
1847 * qgroup of level 255 is created instead. Either way, the new
1848 * qgroup is then assigned to the parent's higher-level
1849 * qgroup, and the subvolume itself is assigned to it.
1850 *
1851 * If the subvolume is already assigned to a higher level
1852 * qgroup, no operation is executed.
1853 *
1854 * Effectively this means: regardless if
1855 * insert_intermediary_qgroup is true or not, after this
1856 * function is invoked the subvolume will be accounted within
1857 * the same qgroups as the parent. However, if it is true, it
1858 * will also get its own higher-level qgroup, which may in
1859 * turn be used by subvolumes created beneath this subvolume
1860 * later on.
1861 *
1862 * This hence defines a simple default qgroup setup for
1863 * subvolumes, as long as this function is invoked on each
1864 * created subvolume: each subvolume is always accounting
1865 * together with its immediate parents. Optionally, if
1866 * insert_intermediary_qgroup is true, it will also get a
1867 * qgroup that then includes all its own child subvolumes.
1868 */
1869
1870 if (subvol_id == 0) {
1871 r = btrfs_is_subvol(fd);
1872 if (r < 0)
1873 return r;
1874 if (!r)
1875 return -ENOTTY;
1876
1877 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
1878 if (r < 0)
1879 return r;
1880 }
1881
1882 n = btrfs_qgroup_find_parents(fd, subvol_id, &qgroups);
1883 if (n < 0)
1884 return n;
1885 if (n > 0) /* already parent qgroups set up, let's bail */
1886 return 0;
1887
1888 r = btrfs_subvol_get_parent(fd, subvol_id, &parent_subvol);
1889 if (r < 0)
1890 return r;
1891
1892 qgroups = mfree(qgroups);
1893 n = btrfs_qgroup_find_parents(fd, parent_subvol, &qgroups);
1894 if (n < 0)
1895 return n;
1896
1897 if (insert_intermediary_qgroup) {
1898 uint64_t lowest = 256, new_qgroupid;
1899 bool created = false;
1900 int i;
1901
1902 /* Determine the lowest qgroup that the parent
1903 * subvolume is assigned to. */
1904
1905 for (i = 0; i < n; i++) {
1906 uint64_t level;
1907
1908 r = btrfs_qgroupid_split(qgroups[i], &level, NULL);
1909 if (r < 0)
1910 return r;
1911
1912 if (level < lowest)
1913 lowest = level;
1914 }
1915
1916 if (lowest <= 1) /* There are no levels left we could use insert an intermediary qgroup at */
1917 return -EBUSY;
1918
1919 r = btrfs_qgroupid_make(lowest - 1, subvol_id, &new_qgroupid);
1920 if (r < 0)
1921 return r;
1922
1923 /* Create the new intermediary group, unless it already exists */
1924 r = btrfs_qgroup_create(fd, new_qgroupid);
1925 if (r < 0 && r != -EEXIST)
1926 return r;
1927 if (r >= 0)
1928 changed = created = true;
1929
1930 for (i = 0; i < n; i++) {
1931 r = btrfs_qgroup_assign(fd, new_qgroupid, qgroups[i]);
1932 if (r < 0 && r != -EEXIST) {
1933 if (created)
1934 (void) btrfs_qgroup_destroy_recursive(fd, new_qgroupid);
1935
1936 return r;
1937 }
1938 if (r >= 0)
1939 changed = true;
1940 }
1941
1942 r = btrfs_qgroup_assign(fd, subvol_id, new_qgroupid);
1943 if (r < 0 && r != -EEXIST) {
1944 if (created)
1945 (void) btrfs_qgroup_destroy_recursive(fd, new_qgroupid);
1946 return r;
1947 }
1948 if (r >= 0)
1949 changed = true;
1950
1951 } else {
1952 int i;
1953
1954 /* Assign our subvolume to all the same qgroups as the parent */
1955
1956 for (i = 0; i < n; i++) {
1957 r = btrfs_qgroup_assign(fd, subvol_id, qgroups[i]);
1958 if (r < 0 && r != -EEXIST)
1959 return r;
1960 if (r >= 0)
1961 changed = true;
1962 }
1963 }
1964
1965 return changed;
1966}
1967
1968int btrfs_subvol_auto_qgroup(const char *path, uint64_t subvol_id, bool create_intermediary_qgroup) {
1969 _cleanup_close_ int fd = -1;
1970
1971 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1972 if (fd < 0)
1973 return -errno;
1974
1975 return btrfs_subvol_auto_qgroup_fd(fd, subvol_id, create_intermediary_qgroup);
1976}
1977
1978int btrfs_subvol_get_parent(int fd, uint64_t subvol_id, uint64_t *ret) {
1979
1980 struct btrfs_ioctl_search_args args = {
1981 /* Tree of tree roots */
1982 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
1983
1984 /* Look precisely for the subvolume items */
1985 .key.min_type = BTRFS_ROOT_BACKREF_KEY,
1986 .key.max_type = BTRFS_ROOT_BACKREF_KEY,
1987
1988 /* No restrictions on the other components */
1989 .key.min_offset = 0,
1990 .key.max_offset = (uint64_t) -1,
1991
1992 .key.min_transid = 0,
1993 .key.max_transid = (uint64_t) -1,
1994 };
1995 int r;
1996
1997 assert(fd >= 0);
1998 assert(ret);
1999
2000 if (subvol_id == 0) {
2001 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
2002 if (r < 0)
2003 return r;
2004 } else {
2005 r = btrfs_is_filesystem(fd);
2006 if (r < 0)
2007 return r;
2008 if (!r)
2009 return -ENOTTY;
2010 }
2011
2012 args.key.min_objectid = args.key.max_objectid = subvol_id;
2013
2014 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
2015 const struct btrfs_ioctl_search_header *sh;
2016 unsigned i;
2017
2018 args.key.nr_items = 256;
2019 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
2020 return -errno;
2021
2022 if (args.key.nr_items <= 0)
2023 break;
2024
2025 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
2026
2027 if (sh->type != BTRFS_ROOT_BACKREF_KEY)
2028 continue;
2029 if (sh->objectid != subvol_id)
2030 continue;
2031
2032 *ret = sh->offset;
2033 return 0;
2034 }
2035 }
2036
2037 return -ENXIO;
2038}