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