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