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