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