]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/btrfs-util.c
Add SPDX license identifiers to source files under the LGPL
[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"
3ffd4af2 45#include "fd-util.h"
07630cea 46#include "fileio.h"
a90fb858 47#include "io-util.h"
07630cea 48#include "macro.h"
d7c7c334 49#include "missing.h"
d7c7c334 50#include "path-util.h"
17cbb288 51#include "rm-rf.h"
d7b8eec7
LP
52#include "selinux-util.h"
53#include "smack-util.h"
11c3a366 54#include "sparse-endian.h"
872a590e 55#include "stat-util.h"
07630cea 56#include "string-util.h"
93cc7779 57#include "time-util.h"
07630cea 58#include "util.h"
d7c7c334 59
62572894
LP
60/* WARNING: Be careful with file system ioctls! When we get an fd, we
61 * need to make sure it either refers to only a regular file or
62 * directory, or that it is located on btrfs, before invoking any
63 * btrfs ioctls. The ioctl numbers are reused by some device drivers
64 * (such as DRM), and hence might have bad effects when invoked on
65 * device nodes (that reference drivers) rather than fds to normal
66 * files or directories. */
67
d7c7c334
LP
68static int validate_subvolume_name(const char *name) {
69
70 if (!filename_is_valid(name))
71 return -EINVAL;
72
73 if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
74 return -E2BIG;
75
76 return 0;
77}
78
79static int open_parent(const char *path, int flags) {
80 _cleanup_free_ char *parent = NULL;
5f311f8c 81 int fd;
d7c7c334
LP
82
83 assert(path);
84
5f311f8c
LP
85 parent = dirname_malloc(path);
86 if (!parent)
87 return -ENOMEM;
d7c7c334
LP
88
89 fd = open(parent, flags);
90 if (fd < 0)
91 return -errno;
92
93 return fd;
94}
95
96static int extract_subvolume_name(const char *path, const char **subvolume) {
97 const char *fn;
98 int r;
99
100 assert(path);
101 assert(subvolume);
102
103 fn = basename(path);
104
105 r = validate_subvolume_name(fn);
106 if (r < 0)
107 return r;
108
109 *subvolume = fn;
110 return 0;
111}
112
21222ea5 113int btrfs_is_filesystem(int fd) {
d7c7c334
LP
114 struct statfs sfs;
115
21222ea5
LP
116 assert(fd >= 0);
117
118 if (fstatfs(fd, &sfs) < 0)
119 return -errno;
120
121 return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
122}
123
2904e949 124int btrfs_is_subvol_fd(int fd) {
21222ea5
LP
125 struct stat st;
126
127 assert(fd >= 0);
128
cd61c3bf
LP
129 /* On btrfs subvolumes always have the inode 256 */
130
131 if (fstat(fd, &st) < 0)
d7c7c334
LP
132 return -errno;
133
cd61c3bf 134 if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
d7c7c334
LP
135 return 0;
136
21222ea5 137 return btrfs_is_filesystem(fd);
d7c7c334
LP
138}
139
2904e949
LP
140int btrfs_is_subvol(const char *path) {
141 _cleanup_close_ int fd = -1;
142
143 assert(path);
144
145 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
146 if (fd < 0)
147 return -errno;
148
149 return btrfs_is_subvol_fd(fd);
150}
151
d7c7c334
LP
152int btrfs_subvol_make(const char *path) {
153 struct btrfs_ioctl_vol_args args = {};
154 _cleanup_close_ int fd = -1;
155 const char *subvolume;
156 int r;
157
158 assert(path);
159
160 r = extract_subvolume_name(path, &subvolume);
161 if (r < 0)
162 return r;
163
164 fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
165 if (fd < 0)
166 return fd;
167
168 strncpy(args.name, subvolume, sizeof(args.name)-1);
169
170 if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
171 return -errno;
172
173 return 0;
174}
175
d7b8eec7
LP
176int btrfs_subvol_make_label(const char *path) {
177 int r;
178
179 assert(path);
180
181 r = mac_selinux_create_file_prepare(path, S_IFDIR);
182 if (r < 0)
183 return r;
184
185 r = btrfs_subvol_make(path);
186 mac_selinux_create_file_clear();
187
188 if (r < 0)
189 return r;
190
191 return mac_smack_fix(path, false, false);
192}
193
0d6e763b 194int btrfs_subvol_set_read_only_fd(int fd, bool b) {
d7c7c334 195 uint64_t flags, nflags;
0d6e763b 196 struct stat st;
d7c7c334 197
0d6e763b
LP
198 assert(fd >= 0);
199
200 if (fstat(fd, &st) < 0)
d7c7c334
LP
201 return -errno;
202
0d6e763b
LP
203 if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
204 return -EINVAL;
205
d7c7c334
LP
206 if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
207 return -errno;
208
209 if (b)
210 nflags = flags | BTRFS_SUBVOL_RDONLY;
211 else
212 nflags = flags & ~BTRFS_SUBVOL_RDONLY;
213
214 if (flags == nflags)
215 return 0;
216
217 if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0)
218 return -errno;
219
220 return 0;
221}
222
0d6e763b
LP
223int btrfs_subvol_set_read_only(const char *path, bool b) {
224 _cleanup_close_ int fd = -1;
225
226 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
227 if (fd < 0)
228 return -errno;
229
230 return btrfs_subvol_set_read_only_fd(fd, b);
231}
232
10f9c755 233int btrfs_subvol_get_read_only_fd(int fd) {
cd61c3bf 234 uint64_t flags;
62572894
LP
235 struct stat st;
236
237 assert(fd >= 0);
238
239 if (fstat(fd, &st) < 0)
240 return -errno;
241
242 if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
243 return -EINVAL;
cd61c3bf
LP
244
245 if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
246 return -errno;
247
248 return !!(flags & BTRFS_SUBVOL_RDONLY);
249}
250
d7c7c334 251int btrfs_reflink(int infd, int outfd) {
62572894 252 struct stat st;
d7c7c334
LP
253 int r;
254
255 assert(infd >= 0);
256 assert(outfd >= 0);
257
62572894
LP
258 /* Make sure we invoke the ioctl on a regular file, so that no
259 * device driver accidentally gets it. */
260
261 if (fstat(outfd, &st) < 0)
262 return -errno;
263
264 if (!S_ISREG(st.st_mode))
265 return -EINVAL;
266
d7c7c334
LP
267 r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
268 if (r < 0)
269 return -errno;
270
271 return 0;
272}
273
1c7dd825
LP
274int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) {
275 struct btrfs_ioctl_clone_range_args args = {
276 .src_fd = infd,
277 .src_offset = in_offset,
278 .src_length = sz,
279 .dest_offset = out_offset,
280 };
62572894 281 struct stat st;
1c7dd825
LP
282 int r;
283
284 assert(infd >= 0);
285 assert(outfd >= 0);
286 assert(sz > 0);
287
62572894
LP
288 if (fstat(outfd, &st) < 0)
289 return -errno;
290
291 if (!S_ISREG(st.st_mode))
292 return -EINVAL;
293
1c7dd825
LP
294 r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args);
295 if (r < 0)
296 return -errno;
297
298 return 0;
299}
300
efe02862 301int btrfs_get_block_device_fd(int fd, dev_t *dev) {
d7c7c334 302 struct btrfs_ioctl_fs_info_args fsi = {};
d7c7c334 303 uint64_t id;
62572894 304 int r;
d7c7c334 305
efe02862 306 assert(fd >= 0);
d7c7c334
LP
307 assert(dev);
308
62572894
LP
309 r = btrfs_is_filesystem(fd);
310 if (r < 0)
311 return r;
312 if (!r)
313 return -ENOTTY;
314
d7c7c334
LP
315 if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
316 return -errno;
317
318 /* We won't do this for btrfs RAID */
319 if (fsi.num_devices != 1)
320 return 0;
321
322 for (id = 1; id <= fsi.max_id; id++) {
323 struct btrfs_ioctl_dev_info_args di = {
324 .devid = id,
325 };
326 struct stat st;
327
328 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
329 if (errno == ENODEV)
330 continue;
331
332 return -errno;
333 }
334
335 if (stat((char*) di.path, &st) < 0)
336 return -errno;
337
338 if (!S_ISBLK(st.st_mode))
339 return -ENODEV;
340
341 if (major(st.st_rdev) == 0)
342 return -ENODEV;
343
344 *dev = st.st_rdev;
345 return 1;
346 }
347
348 return -ENODEV;
349}
10f9c755 350
efe02862
LP
351int btrfs_get_block_device(const char *path, dev_t *dev) {
352 _cleanup_close_ int fd = -1;
353
354 assert(path);
355 assert(dev);
356
357 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
358 if (fd < 0)
359 return -errno;
360
361 return btrfs_get_block_device_fd(fd, dev);
362}
363
10f9c755
LP
364int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
365 struct btrfs_ioctl_ino_lookup_args args = {
366 .objectid = BTRFS_FIRST_FREE_OBJECTID
367 };
62572894 368 int r;
10f9c755
LP
369
370 assert(fd >= 0);
371 assert(ret);
372
62572894
LP
373 r = btrfs_is_filesystem(fd);
374 if (r < 0)
375 return r;
376 if (!r)
377 return -ENOTTY;
378
10f9c755
LP
379 if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
380 return -errno;
381
382 *ret = args.treeid;
383 return 0;
384}
385
90578cbd
LP
386int btrfs_subvol_get_id(int fd, const char *subvol, uint64_t *ret) {
387 _cleanup_close_ int subvol_fd = -1;
388
389 assert(fd >= 0);
390 assert(ret);
391
392 subvol_fd = openat(fd, subvol, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
393 if (subvol_fd < 0)
394 return -errno;
395
396 return btrfs_subvol_get_id_fd(subvol_fd, ret);
397}
398
5743a585
LP
399static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) {
400 assert(args);
401
402 /* the objectid, type, offset together make up the btrfs key,
403 * which is considered a single 136byte integer when
404 * comparing. This call increases the counter by one, dealing
405 * with the overflow between the overflows */
406
407 if (args->key.min_offset < (uint64_t) -1) {
408 args->key.min_offset++;
409 return true;
410 }
411
412 if (args->key.min_type < (uint8_t) -1) {
413 args->key.min_type++;
414 args->key.min_offset = 0;
415 return true;
416 }
417
418 if (args->key.min_objectid < (uint64_t) -1) {
419 args->key.min_objectid++;
420 args->key.min_offset = 0;
421 args->key.min_type = 0;
422 return true;
423 }
424
425 return 0;
426}
427
428static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) {
429 assert(args);
430 assert(h);
431
432 args->key.min_objectid = h->objectid;
433 args->key.min_type = h->type;
434 args->key.min_offset = h->offset;
435}
436
437static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) {
438 assert(args);
439
440 /* Compare min and max */
441
442 if (args->key.min_objectid < args->key.max_objectid)
443 return -1;
444 if (args->key.min_objectid > args->key.max_objectid)
445 return 1;
446
447 if (args->key.min_type < args->key.max_type)
448 return -1;
449 if (args->key.min_type > args->key.max_type)
450 return 1;
451
452 if (args->key.min_offset < args->key.max_offset)
453 return -1;
454 if (args->key.min_offset > args->key.max_offset)
455 return 1;
456
457 return 0;
458}
459
460#define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) \
461 for ((i) = 0, \
462 (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \
463 (i) < (args).key.nr_items; \
464 (i)++, \
465 (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len))
466
467#define BTRFS_IOCTL_SEARCH_HEADER_BODY(sh) \
468 ((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header)))
469
5bcd08db 470int btrfs_subvol_get_info_fd(int fd, uint64_t subvol_id, BtrfsSubvolInfo *ret) {
10f9c755
LP
471 struct btrfs_ioctl_search_args args = {
472 /* Tree of tree roots */
b6b18498 473 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
10f9c755
LP
474
475 /* Look precisely for the subvolume items */
476 .key.min_type = BTRFS_ROOT_ITEM_KEY,
477 .key.max_type = BTRFS_ROOT_ITEM_KEY,
478
10f9c755
LP
479 .key.min_offset = 0,
480 .key.max_offset = (uint64_t) -1,
5743a585
LP
481
482 /* No restrictions on the other components */
10f9c755
LP
483 .key.min_transid = 0,
484 .key.max_transid = (uint64_t) -1,
10f9c755
LP
485 };
486
b6b18498 487 bool found = false;
10f9c755
LP
488 int r;
489
490 assert(fd >= 0);
491 assert(ret);
492
5bcd08db
LP
493 if (subvol_id == 0) {
494 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
495 if (r < 0)
496 return r;
497 } else {
498 r = btrfs_is_filesystem(fd);
499 if (r < 0)
500 return r;
501 if (!r)
502 return -ENOTTY;
503 }
10f9c755
LP
504
505 args.key.min_objectid = args.key.max_objectid = subvol_id;
10f9c755 506
5743a585 507 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
b6b18498
LP
508 const struct btrfs_ioctl_search_header *sh;
509 unsigned i;
510
511 args.key.nr_items = 256;
512 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
513 return -errno;
514
515 if (args.key.nr_items <= 0)
516 break;
10f9c755 517
5743a585 518 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
10f9c755 519
b6b18498
LP
520 const struct btrfs_root_item *ri;
521
5743a585
LP
522 /* Make sure we start the next search at least from this entry */
523 btrfs_ioctl_search_args_set(&args, sh);
524
b6b18498
LP
525 if (sh->objectid != subvol_id)
526 continue;
527 if (sh->type != BTRFS_ROOT_ITEM_KEY)
528 continue;
5743a585
LP
529
530 /* Older versions of the struct lacked the otime setting */
b6b18498
LP
531 if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
532 continue;
10f9c755 533
5743a585 534 ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
10f9c755 535
b6b18498
LP
536 ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
537 (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
10f9c755 538
b6b18498
LP
539 ret->subvol_id = subvol_id;
540 ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
10f9c755 541
b6b18498
LP
542 assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid));
543 memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
544 memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));
545
546 found = true;
547 goto finish;
548 }
549
5743a585
LP
550 /* Increase search key by one, to read the next item, if we can. */
551 if (!btrfs_ioctl_search_args_inc(&args))
b6b18498
LP
552 break;
553 }
554
555finish:
556 if (!found)
557 return -ENODATA;
558
559 return 0;
560}
561
5bcd08db 562int btrfs_qgroup_get_quota_fd(int fd, uint64_t qgroupid, BtrfsQuotaInfo *ret) {
b6b18498
LP
563
564 struct btrfs_ioctl_search_args args = {
565 /* Tree of quota items */
566 .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
567
5743a585
LP
568 /* The object ID is always 0 */
569 .key.min_objectid = 0,
570 .key.max_objectid = 0,
571
b6b18498
LP
572 /* Look precisely for the quota items */
573 .key.min_type = BTRFS_QGROUP_STATUS_KEY,
574 .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
575
b6b18498
LP
576 /* No restrictions on the other components */
577 .key.min_transid = 0,
578 .key.max_transid = (uint64_t) -1,
579 };
580
b6b18498
LP
581 bool found_info = false, found_limit = false;
582 int r;
583
584 assert(fd >= 0);
585 assert(ret);
586
5bcd08db
LP
587 if (qgroupid == 0) {
588 r = btrfs_subvol_get_id_fd(fd, &qgroupid);
589 if (r < 0)
590 return r;
591 } else {
592 r = btrfs_is_filesystem(fd);
593 if (r < 0)
594 return r;
595 if (!r)
596 return -ENOTTY;
597 }
b6b18498 598
5bcd08db 599 args.key.min_offset = args.key.max_offset = qgroupid;
b6b18498 600
5743a585 601 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
b6b18498
LP
602 const struct btrfs_ioctl_search_header *sh;
603 unsigned i;
604
605 args.key.nr_items = 256;
12ee6186
LP
606 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) {
607 if (errno == ENOENT) /* quota tree is missing: quota disabled */
608 break;
609
b6b18498 610 return -errno;
12ee6186 611 }
b6b18498
LP
612
613 if (args.key.nr_items <= 0)
614 break;
615
5743a585 616 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
b6b18498 617
5743a585
LP
618 /* Make sure we start the next search at least from this entry */
619 btrfs_ioctl_search_args_set(&args, sh);
b6b18498
LP
620
621 if (sh->objectid != 0)
622 continue;
5bcd08db 623 if (sh->offset != qgroupid)
b6b18498
LP
624 continue;
625
b6b18498 626 if (sh->type == BTRFS_QGROUP_INFO_KEY) {
5743a585 627 const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
b6b18498 628
cb81cd80 629 ret->referenced = le64toh(qii->rfer);
b6b18498
LP
630 ret->exclusive = le64toh(qii->excl);
631
632 found_info = true;
633
634 } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
5743a585 635 const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
b6b18498 636
5bcd08db
LP
637 if (le64toh(qli->flags) & BTRFS_QGROUP_LIMIT_MAX_RFER)
638 ret->referenced_max = le64toh(qli->max_rfer);
639 else
cb81cd80 640 ret->referenced_max = (uint64_t) -1;
5bcd08db
LP
641
642 if (le64toh(qli->flags) & BTRFS_QGROUP_LIMIT_MAX_EXCL)
643 ret->exclusive_max = le64toh(qli->max_excl);
644 else
b6b18498
LP
645 ret->exclusive_max = (uint64_t) -1;
646
647 found_limit = true;
648 }
649
650 if (found_info && found_limit)
651 goto finish;
652 }
653
5743a585
LP
654 /* Increase search key by one, to read the next item, if we can. */
655 if (!btrfs_ioctl_search_args_inc(&args))
b6b18498
LP
656 break;
657 }
658
659finish:
660 if (!found_limit && !found_info)
661 return -ENODATA;
662
663 if (!found_info) {
cb81cd80 664 ret->referenced = (uint64_t) -1;
b6b18498
LP
665 ret->exclusive = (uint64_t) -1;
666 }
667
668 if (!found_limit) {
cb81cd80 669 ret->referenced_max = (uint64_t) -1;
b6b18498
LP
670 ret->exclusive_max = (uint64_t) -1;
671 }
10f9c755
LP
672
673 return 0;
674}
f27a3864 675
5bcd08db
LP
676int btrfs_qgroup_get_quota(const char *path, uint64_t qgroupid, BtrfsQuotaInfo *ret) {
677 _cleanup_close_ int fd = -1;
678
679 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
680 if (fd < 0)
681 return -errno;
682
683 return btrfs_qgroup_get_quota_fd(fd, qgroupid, ret);
684}
685
686int btrfs_subvol_find_subtree_qgroup(int fd, uint64_t subvol_id, uint64_t *ret) {
687 uint64_t level, lowest = (uint64_t) -1, lowest_qgroupid = 0;
688 _cleanup_free_ uint64_t *qgroups = NULL;
689 int r, n, i;
690
691 assert(fd >= 0);
692 assert(ret);
693
694 /* This finds the "subtree" qgroup for a specific
695 * subvolume. This only works for subvolumes that have been
696 * prepared with btrfs_subvol_auto_qgroup_fd() with
697 * insert_intermediary_qgroup=true (or equivalent). For others
698 * it will return the leaf qgroup instead. The two cases may
699 * be distuingished via the return value, which is 1 in case
700 * an appropriate "subtree" qgroup was found, and 0
701 * otherwise. */
702
703 if (subvol_id == 0) {
704 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
705 if (r < 0)
706 return r;
707 }
708
709 r = btrfs_qgroupid_split(subvol_id, &level, NULL);
710 if (r < 0)
711 return r;
712 if (level != 0) /* Input must be a leaf qgroup */
713 return -EINVAL;
714
715 n = btrfs_qgroup_find_parents(fd, subvol_id, &qgroups);
716 if (n < 0)
717 return n;
718
719 for (i = 0; i < n; i++) {
720 uint64_t id;
721
722 r = btrfs_qgroupid_split(qgroups[i], &level, &id);
723 if (r < 0)
724 return r;
725
726 if (id != subvol_id)
727 continue;
728
729 if (lowest == (uint64_t) -1 || level < lowest) {
730 lowest_qgroupid = qgroups[i];
731 lowest = level;
732 }
733 }
734
735 if (lowest == (uint64_t) -1) {
736 /* No suitable higher-level qgroup found, let's return
737 * the leaf qgroup instead, and indicate that with the
738 * return value. */
739
740 *ret = subvol_id;
741 return 0;
742 }
743
744 *ret = lowest_qgroupid;
745 return 1;
746}
747
748int btrfs_subvol_get_subtree_quota_fd(int fd, uint64_t subvol_id, BtrfsQuotaInfo *ret) {
749 uint64_t qgroupid;
750 int r;
751
752 assert(fd >= 0);
753 assert(ret);
754
755 /* This determines the quota data of the qgroup with the
756 * lowest level, that shares the id part with the specified
757 * subvolume. This is useful for determining the quota data
758 * for entire subvolume subtrees, as long as the subtrees have
759 * been set up with btrfs_qgroup_subvol_auto_fd() or in a
760 * compatible way */
761
762 r = btrfs_subvol_find_subtree_qgroup(fd, subvol_id, &qgroupid);
763 if (r < 0)
764 return r;
765
766 return btrfs_qgroup_get_quota_fd(fd, qgroupid, ret);
767}
768
769int btrfs_subvol_get_subtree_quota(const char *path, uint64_t subvol_id, BtrfsQuotaInfo *ret) {
770 _cleanup_close_ int fd = -1;
771
772 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
773 if (fd < 0)
774 return -errno;
775
776 return btrfs_subvol_get_subtree_quota_fd(fd, subvol_id, ret);
777}
778
f27a3864 779int btrfs_defrag_fd(int fd) {
62572894
LP
780 struct stat st;
781
f27a3864
LP
782 assert(fd >= 0);
783
62572894
LP
784 if (fstat(fd, &st) < 0)
785 return -errno;
786
787 if (!S_ISREG(st.st_mode))
788 return -EINVAL;
789
f27a3864
LP
790 if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
791 return -errno;
792
793 return 0;
794}
795
796int btrfs_defrag(const char *p) {
797 _cleanup_close_ int fd = -1;
798
799 fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
800 if (fd < 0)
801 return -errno;
802
803 return btrfs_defrag_fd(fd);
804}
754061ce
LP
805
806int btrfs_quota_enable_fd(int fd, bool b) {
807 struct btrfs_ioctl_quota_ctl_args args = {
808 .cmd = b ? BTRFS_QUOTA_CTL_ENABLE : BTRFS_QUOTA_CTL_DISABLE,
809 };
62572894 810 int r;
754061ce
LP
811
812 assert(fd >= 0);
813
62572894
LP
814 r = btrfs_is_filesystem(fd);
815 if (r < 0)
816 return r;
817 if (!r)
818 return -ENOTTY;
819
754061ce
LP
820 if (ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args) < 0)
821 return -errno;
822
823 return 0;
824}
825
826int btrfs_quota_enable(const char *path, bool b) {
827 _cleanup_close_ int fd = -1;
828
829 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
830 if (fd < 0)
831 return -errno;
832
833 return btrfs_quota_enable_fd(fd, b);
834}
d6ce17c7 835
5bcd08db
LP
836int btrfs_qgroup_set_limit_fd(int fd, uint64_t qgroupid, uint64_t referenced_max) {
837
d6ce17c7 838 struct btrfs_ioctl_qgroup_limit_args args = {
5bcd08db 839 .lim.max_rfer = referenced_max,
d6ce17c7
LP
840 .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER,
841 };
5bcd08db 842 unsigned c;
62572894 843 int r;
d6ce17c7
LP
844
845 assert(fd >= 0);
846
5bcd08db
LP
847 if (qgroupid == 0) {
848 r = btrfs_subvol_get_id_fd(fd, &qgroupid);
849 if (r < 0)
850 return r;
851 } else {
852 r = btrfs_is_filesystem(fd);
853 if (r < 0)
854 return r;
855 if (!r)
856 return -ENOTTY;
857 }
62572894 858
5bcd08db
LP
859 args.qgroupid = qgroupid;
860
861 for (c = 0;; c++) {
862 if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args) < 0) {
863
864 if (errno == EBUSY && c < 10) {
865 (void) btrfs_quota_scan_wait(fd);
866 continue;
867 }
868
869 return -errno;
870 }
871
872 break;
873 }
d6ce17c7
LP
874
875 return 0;
876}
877
5bcd08db
LP
878int btrfs_qgroup_set_limit(const char *path, uint64_t qgroupid, uint64_t referenced_max) {
879 _cleanup_close_ int fd = -1;
880
881 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
882 if (fd < 0)
883 return -errno;
884
885 return btrfs_qgroup_set_limit_fd(fd, qgroupid, referenced_max);
886}
887
888int btrfs_subvol_set_subtree_quota_limit_fd(int fd, uint64_t subvol_id, uint64_t referenced_max) {
889 uint64_t qgroupid;
890 int r;
891
892 assert(fd >= 0);
893
894 r = btrfs_subvol_find_subtree_qgroup(fd, subvol_id, &qgroupid);
895 if (r < 0)
896 return r;
897
898 return btrfs_qgroup_set_limit_fd(fd, qgroupid, referenced_max);
899}
900
901int btrfs_subvol_set_subtree_quota_limit(const char *path, uint64_t subvol_id, uint64_t referenced_max) {
d6ce17c7
LP
902 _cleanup_close_ int fd = -1;
903
904 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
905 if (fd < 0)
906 return -errno;
907
5bcd08db 908 return btrfs_subvol_set_subtree_quota_limit_fd(fd, subvol_id, referenced_max);
d6ce17c7 909}
efe02862 910
26166c88 911int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
efe02862
LP
912 struct btrfs_ioctl_vol_args args = {};
913 _cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL;
914 _cleanup_close_ int loop_fd = -1, backing_fd = -1;
915 struct stat st;
a7f7d1bd 916 dev_t dev = 0;
efe02862
LP
917 int r;
918
a90fb858
LP
919 /* In contrast to btrfs quota ioctls ftruncate() cannot make sense of "infinity" or file sizes > 2^31 */
920 if (!FILE_SIZE_VALID(new_size))
921 return -EINVAL;
922
efe02862
LP
923 /* btrfs cannot handle file systems < 16M, hence use this as minimum */
924 if (new_size < 16*1024*1024)
925 new_size = 16*1024*1024;
926
927 r = btrfs_get_block_device_fd(fd, &dev);
928 if (r < 0)
929 return r;
930 if (r == 0)
931 return -ENODEV;
932
933 if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0)
934 return -ENOMEM;
935 r = read_one_line_file(p, &backing);
26166c88
LP
936 if (r == -ENOENT)
937 return -ENODEV;
efe02862
LP
938 if (r < 0)
939 return r;
940 if (isempty(backing) || !path_is_absolute(backing))
941 return -ENODEV;
942
943 backing_fd = open(backing, O_RDWR|O_CLOEXEC|O_NOCTTY);
944 if (backing_fd < 0)
945 return -errno;
946
947 if (fstat(backing_fd, &st) < 0)
948 return -errno;
949 if (!S_ISREG(st.st_mode))
950 return -ENODEV;
951
952 if (new_size == (uint64_t) st.st_size)
953 return 0;
954
26166c88
LP
955 if (grow_only && new_size < (uint64_t) st.st_size)
956 return -EINVAL;
957
efe02862
LP
958 if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0)
959 return -ENOMEM;
960 loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY);
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}