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