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