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