]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/btrfs-util.c
btrfs: support recursively removing btrfs snapshots
[thirdparty/systemd.git] / src / shared / btrfs-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdlib.h>
23 #include <sys/vfs.h>
24 #include <sys/stat.h>
25
26 #ifdef HAVE_LINUX_BTRFS_H
27 #include <linux/btrfs.h>
28 #endif
29
30 #include "missing.h"
31 #include "util.h"
32 #include "path-util.h"
33 #include "macro.h"
34 #include "copy.h"
35 #include "selinux-util.h"
36 #include "smack-util.h"
37 #include "fileio.h"
38 #include "btrfs-ctree.h"
39 #include "btrfs-util.h"
40
41 static int validate_subvolume_name(const char *name) {
42
43 if (!filename_is_valid(name))
44 return -EINVAL;
45
46 if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
47 return -E2BIG;
48
49 return 0;
50 }
51
52 static int open_parent(const char *path, int flags) {
53 _cleanup_free_ char *parent = NULL;
54 int r, fd;
55
56 assert(path);
57
58 r = path_get_parent(path, &parent);
59 if (r < 0)
60 return r;
61
62 fd = open(parent, flags);
63 if (fd < 0)
64 return -errno;
65
66 return fd;
67 }
68
69 static int extract_subvolume_name(const char *path, const char **subvolume) {
70 const char *fn;
71 int r;
72
73 assert(path);
74 assert(subvolume);
75
76 fn = basename(path);
77
78 r = validate_subvolume_name(fn);
79 if (r < 0)
80 return r;
81
82 *subvolume = fn;
83 return 0;
84 }
85
86 int btrfs_is_snapshot(int fd) {
87 struct stat st;
88 struct statfs sfs;
89
90 /* On btrfs subvolumes always have the inode 256 */
91
92 if (fstat(fd, &st) < 0)
93 return -errno;
94
95 if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
96 return 0;
97
98 if (fstatfs(fd, &sfs) < 0)
99 return -errno;
100
101 return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
102 }
103
104 int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, bool read_only, bool fallback_copy) {
105 struct btrfs_ioctl_vol_args_v2 args = {
106 .flags = read_only ? BTRFS_SUBVOL_RDONLY : 0,
107 };
108 _cleanup_close_ int new_fd = -1;
109 const char *subvolume;
110 int r;
111
112 assert(new_path);
113
114 r = btrfs_is_snapshot(old_fd);
115 if (r < 0)
116 return r;
117 if (r == 0) {
118 if (!fallback_copy)
119 return -EISDIR;
120
121 r = btrfs_subvol_make(new_path);
122 if (r < 0)
123 return r;
124
125 r = copy_directory_fd(old_fd, new_path, true);
126 if (r < 0) {
127 btrfs_subvol_remove(new_path, false);
128 return r;
129 }
130
131 if (read_only) {
132 r = btrfs_subvol_set_read_only(new_path, true);
133 if (r < 0) {
134 btrfs_subvol_remove(new_path, false);
135 return r;
136 }
137 }
138
139 return 0;
140 }
141
142 r = extract_subvolume_name(new_path, &subvolume);
143 if (r < 0)
144 return r;
145
146 new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
147 if (new_fd < 0)
148 return new_fd;
149
150 strncpy(args.name, subvolume, sizeof(args.name)-1);
151 args.fd = old_fd;
152
153 if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &args) < 0)
154 return -errno;
155
156 return 0;
157 }
158
159 int btrfs_subvol_snapshot(const char *old_path, const char *new_path, bool read_only, bool fallback_copy) {
160 _cleanup_close_ int old_fd = -1;
161
162 assert(old_path);
163 assert(new_path);
164
165 old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
166 if (old_fd < 0)
167 return -errno;
168
169 return btrfs_subvol_snapshot_fd(old_fd, new_path, read_only, fallback_copy);
170 }
171
172 int btrfs_subvol_make(const char *path) {
173 struct btrfs_ioctl_vol_args args = {};
174 _cleanup_close_ int fd = -1;
175 const char *subvolume;
176 int r;
177
178 assert(path);
179
180 r = extract_subvolume_name(path, &subvolume);
181 if (r < 0)
182 return r;
183
184 fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
185 if (fd < 0)
186 return fd;
187
188 strncpy(args.name, subvolume, sizeof(args.name)-1);
189
190 if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
191 return -errno;
192
193 return 0;
194 }
195
196 int btrfs_subvol_make_label(const char *path) {
197 int r;
198
199 assert(path);
200
201 r = mac_selinux_create_file_prepare(path, S_IFDIR);
202 if (r < 0)
203 return r;
204
205 r = btrfs_subvol_make(path);
206 mac_selinux_create_file_clear();
207
208 if (r < 0)
209 return r;
210
211 return mac_smack_fix(path, false, false);
212 }
213
214 int btrfs_subvol_set_read_only_fd(int fd, bool b) {
215 uint64_t flags, nflags;
216 struct stat st;
217
218 assert(fd >= 0);
219
220 if (fstat(fd, &st) < 0)
221 return -errno;
222
223 if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
224 return -EINVAL;
225
226 if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
227 return -errno;
228
229 if (b)
230 nflags = flags | BTRFS_SUBVOL_RDONLY;
231 else
232 nflags = flags & ~BTRFS_SUBVOL_RDONLY;
233
234 if (flags == nflags)
235 return 0;
236
237 if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0)
238 return -errno;
239
240 return 0;
241 }
242
243 int btrfs_subvol_set_read_only(const char *path, bool b) {
244 _cleanup_close_ int fd = -1;
245
246 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
247 if (fd < 0)
248 return -errno;
249
250 return btrfs_subvol_set_read_only_fd(fd, b);
251 }
252
253 int btrfs_subvol_get_read_only_fd(int fd) {
254 uint64_t flags;
255
256 if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
257 return -errno;
258
259 return !!(flags & BTRFS_SUBVOL_RDONLY);
260 }
261
262 int btrfs_reflink(int infd, int outfd) {
263 int r;
264
265 assert(infd >= 0);
266 assert(outfd >= 0);
267
268 r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
269 if (r < 0)
270 return -errno;
271
272 return 0;
273 }
274
275 int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) {
276 struct btrfs_ioctl_clone_range_args args = {
277 .src_fd = infd,
278 .src_offset = in_offset,
279 .src_length = sz,
280 .dest_offset = out_offset,
281 };
282 int r;
283
284 assert(infd >= 0);
285 assert(outfd >= 0);
286 assert(sz > 0);
287
288 r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args);
289 if (r < 0)
290 return -errno;
291
292 return 0;
293 }
294
295 int btrfs_get_block_device_fd(int fd, dev_t *dev) {
296 struct btrfs_ioctl_fs_info_args fsi = {};
297 uint64_t id;
298
299 assert(fd >= 0);
300 assert(dev);
301
302 if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
303 return -errno;
304
305 /* We won't do this for btrfs RAID */
306 if (fsi.num_devices != 1)
307 return 0;
308
309 for (id = 1; id <= fsi.max_id; id++) {
310 struct btrfs_ioctl_dev_info_args di = {
311 .devid = id,
312 };
313 struct stat st;
314
315 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
316 if (errno == ENODEV)
317 continue;
318
319 return -errno;
320 }
321
322 if (stat((char*) di.path, &st) < 0)
323 return -errno;
324
325 if (!S_ISBLK(st.st_mode))
326 return -ENODEV;
327
328 if (major(st.st_rdev) == 0)
329 return -ENODEV;
330
331 *dev = st.st_rdev;
332 return 1;
333 }
334
335 return -ENODEV;
336 }
337
338 int btrfs_get_block_device(const char *path, dev_t *dev) {
339 _cleanup_close_ int fd = -1;
340
341 assert(path);
342 assert(dev);
343
344 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
345 if (fd < 0)
346 return -errno;
347
348 return btrfs_get_block_device_fd(fd, dev);
349 }
350
351 int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
352 struct btrfs_ioctl_ino_lookup_args args = {
353 .objectid = BTRFS_FIRST_FREE_OBJECTID
354 };
355
356 assert(fd >= 0);
357 assert(ret);
358
359 if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
360 return -errno;
361
362 *ret = args.treeid;
363 return 0;
364 }
365
366 static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) {
367 assert(args);
368
369 /* the objectid, type, offset together make up the btrfs key,
370 * which is considered a single 136byte integer when
371 * comparing. This call increases the counter by one, dealing
372 * with the overflow between the overflows */
373
374 if (args->key.min_offset < (uint64_t) -1) {
375 args->key.min_offset++;
376 return true;
377 }
378
379 if (args->key.min_type < (uint8_t) -1) {
380 args->key.min_type++;
381 args->key.min_offset = 0;
382 return true;
383 }
384
385 if (args->key.min_objectid < (uint64_t) -1) {
386 args->key.min_objectid++;
387 args->key.min_offset = 0;
388 args->key.min_type = 0;
389 return true;
390 }
391
392 return 0;
393 }
394
395 static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) {
396 assert(args);
397 assert(h);
398
399 args->key.min_objectid = h->objectid;
400 args->key.min_type = h->type;
401 args->key.min_offset = h->offset;
402 }
403
404 static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) {
405 assert(args);
406
407 /* Compare min and max */
408
409 if (args->key.min_objectid < args->key.max_objectid)
410 return -1;
411 if (args->key.min_objectid > args->key.max_objectid)
412 return 1;
413
414 if (args->key.min_type < args->key.max_type)
415 return -1;
416 if (args->key.min_type > args->key.max_type)
417 return 1;
418
419 if (args->key.min_offset < args->key.max_offset)
420 return -1;
421 if (args->key.min_offset > args->key.max_offset)
422 return 1;
423
424 return 0;
425 }
426
427 #define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) \
428 for ((i) = 0, \
429 (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \
430 (i) < (args).key.nr_items; \
431 (i)++, \
432 (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len))
433
434 #define BTRFS_IOCTL_SEARCH_HEADER_BODY(sh) \
435 ((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header)))
436
437 int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
438 struct btrfs_ioctl_search_args args = {
439 /* Tree of tree roots */
440 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
441
442 /* Look precisely for the subvolume items */
443 .key.min_type = BTRFS_ROOT_ITEM_KEY,
444 .key.max_type = BTRFS_ROOT_ITEM_KEY,
445
446 .key.min_offset = 0,
447 .key.max_offset = (uint64_t) -1,
448
449 /* No restrictions on the other components */
450 .key.min_transid = 0,
451 .key.max_transid = (uint64_t) -1,
452 };
453
454 uint64_t subvol_id;
455 bool found = false;
456 int r;
457
458 assert(fd >= 0);
459 assert(ret);
460
461 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
462 if (r < 0)
463 return r;
464
465 args.key.min_objectid = args.key.max_objectid = subvol_id;
466
467 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
468 const struct btrfs_ioctl_search_header *sh;
469 unsigned i;
470
471 args.key.nr_items = 256;
472 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
473 return -errno;
474
475 if (args.key.nr_items <= 0)
476 break;
477
478 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 btrfs_ioctl_search_args_set(&args, sh);
484
485 if (sh->objectid != subvol_id)
486 continue;
487 if (sh->type != BTRFS_ROOT_ITEM_KEY)
488 continue;
489
490 /* Older versions of the struct lacked the otime setting */
491 if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
492 continue;
493
494 ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
495
496 ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
497 (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
498
499 ret->subvol_id = subvol_id;
500 ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
501
502 assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid));
503 memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
504 memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));
505
506 found = true;
507 goto finish;
508 }
509
510 /* Increase search key by one, to read the next item, if we can. */
511 if (!btrfs_ioctl_search_args_inc(&args))
512 break;
513 }
514
515 finish:
516 if (!found)
517 return -ENODATA;
518
519 return 0;
520 }
521
522 int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
523
524 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 uint64_t subvol_id;
542 bool found_info = false, found_limit = false;
543 int r;
544
545 assert(fd >= 0);
546 assert(ret);
547
548 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
549 if (r < 0)
550 return r;
551
552 args.key.min_offset = args.key.max_offset = subvol_id;
553
554 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
555 const struct btrfs_ioctl_search_header *sh;
556 unsigned i;
557
558 args.key.nr_items = 256;
559 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
560 return -errno;
561
562 if (args.key.nr_items <= 0)
563 break;
564
565 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
566
567 /* Make sure we start the next search at least from this entry */
568 btrfs_ioctl_search_args_set(&args, sh);
569
570 if (sh->objectid != 0)
571 continue;
572 if (sh->offset != subvol_id)
573 continue;
574
575 if (sh->type == BTRFS_QGROUP_INFO_KEY) {
576 const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
577
578 ret->referenced = le64toh(qii->rfer);
579 ret->exclusive = le64toh(qii->excl);
580
581 found_info = true;
582
583 } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
584 const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
585
586 ret->referenced_max = le64toh(qli->max_rfer);
587 ret->exclusive_max = le64toh(qli->max_excl);
588
589 if (ret->referenced_max == 0)
590 ret->referenced_max = (uint64_t) -1;
591 if (ret->exclusive_max == 0)
592 ret->exclusive_max = (uint64_t) -1;
593
594 found_limit = true;
595 }
596
597 if (found_info && found_limit)
598 goto finish;
599 }
600
601 /* Increase search key by one, to read the next item, if we can. */
602 if (!btrfs_ioctl_search_args_inc(&args))
603 break;
604 }
605
606 finish:
607 if (!found_limit && !found_info)
608 return -ENODATA;
609
610 if (!found_info) {
611 ret->referenced = (uint64_t) -1;
612 ret->exclusive = (uint64_t) -1;
613 }
614
615 if (!found_limit) {
616 ret->referenced_max = (uint64_t) -1;
617 ret->exclusive_max = (uint64_t) -1;
618 }
619
620 return 0;
621 }
622
623 int btrfs_defrag_fd(int fd) {
624 assert(fd >= 0);
625
626 if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
627 return -errno;
628
629 return 0;
630 }
631
632 int btrfs_defrag(const char *p) {
633 _cleanup_close_ int fd = -1;
634
635 fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
636 if (fd < 0)
637 return -errno;
638
639 return btrfs_defrag_fd(fd);
640 }
641
642 int btrfs_quota_enable_fd(int fd, bool b) {
643 struct btrfs_ioctl_quota_ctl_args args = {
644 .cmd = b ? BTRFS_QUOTA_CTL_ENABLE : BTRFS_QUOTA_CTL_DISABLE,
645 };
646
647 assert(fd >= 0);
648
649 if (ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args) < 0)
650 return -errno;
651
652 return 0;
653 }
654
655 int btrfs_quota_enable(const char *path, bool b) {
656 _cleanup_close_ int fd = -1;
657
658 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
659 if (fd < 0)
660 return -errno;
661
662 return btrfs_quota_enable_fd(fd, b);
663 }
664
665 int btrfs_quota_limit_fd(int fd, uint64_t referenced_max) {
666 struct btrfs_ioctl_qgroup_limit_args args = {
667 .lim.max_rfer =
668 referenced_max == (uint64_t) -1 ? 0 :
669 referenced_max == 0 ? 1 : referenced_max,
670 .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER,
671 };
672
673 assert(fd >= 0);
674
675 if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args) < 0)
676 return -errno;
677
678 return 0;
679 }
680
681 int btrfs_quota_limit(const char *path, uint64_t referenced_max) {
682 _cleanup_close_ int fd = -1;
683
684 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
685 if (fd < 0)
686 return -errno;
687
688 return btrfs_quota_limit_fd(fd, referenced_max);
689 }
690
691 int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
692 struct btrfs_ioctl_vol_args args = {};
693 _cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL;
694 _cleanup_close_ int loop_fd = -1, backing_fd = -1;
695 struct stat st;
696 dev_t dev = 0;
697 int r;
698
699 /* btrfs cannot handle file systems < 16M, hence use this as minimum */
700 if (new_size < 16*1024*1024)
701 new_size = 16*1024*1024;
702
703 r = btrfs_get_block_device_fd(fd, &dev);
704 if (r < 0)
705 return r;
706 if (r == 0)
707 return -ENODEV;
708
709 if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0)
710 return -ENOMEM;
711 r = read_one_line_file(p, &backing);
712 if (r == -ENOENT)
713 return -ENODEV;
714 if (r < 0)
715 return r;
716 if (isempty(backing) || !path_is_absolute(backing))
717 return -ENODEV;
718
719 backing_fd = open(backing, O_RDWR|O_CLOEXEC|O_NOCTTY);
720 if (backing_fd < 0)
721 return -errno;
722
723 if (fstat(backing_fd, &st) < 0)
724 return -errno;
725 if (!S_ISREG(st.st_mode))
726 return -ENODEV;
727
728 if (new_size == (uint64_t) st.st_size)
729 return 0;
730
731 if (grow_only && new_size < (uint64_t) st.st_size)
732 return -EINVAL;
733
734 if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0)
735 return -ENOMEM;
736 loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY);
737 if (loop_fd < 0)
738 return -errno;
739
740 if (snprintf(args.name, sizeof(args.name), "%" PRIu64, new_size) >= (int) sizeof(args.name))
741 return -EINVAL;
742
743 if (new_size < (uint64_t) st.st_size) {
744 /* Decrease size: first decrease btrfs size, then shorten loopback */
745 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
746 return -errno;
747 }
748
749 if (ftruncate(backing_fd, new_size) < 0)
750 return -errno;
751
752 if (ioctl(loop_fd, LOOP_SET_CAPACITY, 0) < 0)
753 return -errno;
754
755 if (new_size > (uint64_t) st.st_size) {
756 /* Increase size: first enlarge loopback, then increase btrfs size */
757 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
758 return -errno;
759 }
760
761 /* Make sure the free disk space is correctly updated for both file systems */
762 (void) fsync(fd);
763 (void) fsync(backing_fd);
764
765 return 1;
766 }
767
768 int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) {
769 _cleanup_close_ int fd = -1;
770
771 fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
772 if (fd < 0)
773 return -errno;
774
775 return btrfs_resize_loopback_fd(fd, new_size, grow_only);
776 }
777
778 static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol_id, bool recursive) {
779 struct btrfs_ioctl_search_args args = {
780 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
781
782 .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
783 .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
784
785 .key.min_type = BTRFS_ROOT_BACKREF_KEY,
786 .key.max_type = BTRFS_ROOT_BACKREF_KEY,
787
788 .key.min_transid = 0,
789 .key.max_transid = (uint64_t) -1,
790 };
791
792 struct btrfs_ioctl_vol_args vol_args = {};
793 _cleanup_close_ int subvol_fd = -1;
794 int r;
795
796 assert(fd >= 0);
797 assert(subvolume);
798
799 /* First, try to remove the subvolume. If it happens to be
800 * already empty, this will just work. */
801 strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
802 if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) >= 0)
803 return 0;
804 if (!recursive || errno != ENOTEMPTY)
805 return -errno;
806
807 /* OK, the subvolume is not empty, let's look for child
808 * subvolumes, and remove them, first */
809 subvol_fd = openat(fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
810 if (subvol_fd < 0)
811 return -errno;
812
813 if (subvol_id == 0) {
814 r = btrfs_subvol_get_id_fd(subvol_fd, &subvol_id);
815 if (r < 0)
816 return r;
817 }
818
819 args.key.min_offset = args.key.max_offset = subvol_id;
820
821 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
822 const struct btrfs_ioctl_search_header *sh;
823 unsigned i;
824
825 args.key.nr_items = 256;
826 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
827 return -errno;
828
829 if (args.key.nr_items <= 0)
830 break;
831
832 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
833 _cleanup_free_ char *p = NULL;
834 const struct btrfs_root_ref *ref;
835 struct btrfs_ioctl_ino_lookup_args ino_args;
836
837 btrfs_ioctl_search_args_set(&args, sh);
838
839 if (sh->type != BTRFS_ROOT_BACKREF_KEY)
840 continue;
841 if (sh->offset != subvol_id)
842 continue;
843
844 ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
845
846 p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
847 if (!p)
848 return -ENOMEM;
849
850 zero(ino_args);
851 ino_args.treeid = subvol_id;
852 ino_args.objectid = ref->dirid;
853
854 if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
855 return -errno;
856
857 if (isempty(ino_args.name))
858 /* Subvolume is in the top-level
859 * directory of the subvolume. */
860 r = subvol_remove_children(subvol_fd, p, sh->objectid, recursive);
861 else {
862 _cleanup_close_ int child_fd = -1;
863
864 /* Subvolume is somewhere further down,
865 * hence we need to open the
866 * containing directory first */
867
868 child_fd = openat(subvol_fd, ino_args.name, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
869 if (child_fd < 0)
870 return -errno;
871
872 r = subvol_remove_children(child_fd, p, sh->objectid, recursive);
873 }
874 if (r < 0)
875 return r;
876 }
877
878 /* Increase search key by one, to read the next item, if we can. */
879 if (!btrfs_ioctl_search_args_inc(&args))
880 break;
881 }
882
883 /* OK, the child subvolumes should all be gone now, let's try
884 * again to remove the subvolume */
885 if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) < 0)
886 return -errno;
887
888 return 0;
889 }
890
891 int btrfs_subvol_remove(const char *path, bool recursive) {
892 _cleanup_close_ int fd = -1;
893 const char *subvolume;
894 int r;
895
896 assert(path);
897
898 r = extract_subvolume_name(path, &subvolume);
899 if (r < 0)
900 return r;
901
902 fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
903 if (fd < 0)
904 return fd;
905
906 return subvol_remove_children(fd, subvolume, 0, recursive);
907 }
908
909 int btrfs_subvol_remove_fd(int fd, const char *subvolume, bool recursive) {
910 return subvol_remove_children(fd, subvolume, 0, recursive);
911 }