1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
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.
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.
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/>.
26 #ifdef HAVE_LINUX_BTRFS_H
27 #include <linux/btrfs.h>
32 #include "path-util.h"
35 #include "selinux-util.h"
36 #include "smack-util.h"
38 #include "btrfs-ctree.h"
39 #include "btrfs-util.h"
41 static int validate_subvolume_name(const char *name) {
43 if (!filename_is_valid(name))
46 if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
52 static int open_parent(const char *path, int flags) {
53 _cleanup_free_ char *parent = NULL;
58 r = path_get_parent(path, &parent);
62 fd = open(parent, flags);
69 static int extract_subvolume_name(const char *path, const char **subvolume) {
78 r = validate_subvolume_name(fn);
86 int btrfs_is_snapshot(int fd) {
90 /* On btrfs subvolumes always have the inode 256 */
92 if (fstat(fd, &st) < 0)
95 if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
98 if (fstatfs(fd, &sfs) < 0)
101 return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
104 int btrfs_subvol_make(const char *path) {
105 struct btrfs_ioctl_vol_args args = {};
106 _cleanup_close_ int fd = -1;
107 const char *subvolume;
112 r = extract_subvolume_name(path, &subvolume);
116 fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
120 strncpy(args.name, subvolume, sizeof(args.name)-1);
122 if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
128 int btrfs_subvol_make_label(const char *path) {
133 r = mac_selinux_create_file_prepare(path, S_IFDIR);
137 r = btrfs_subvol_make(path);
138 mac_selinux_create_file_clear();
143 return mac_smack_fix(path, false, false);
146 int btrfs_subvol_set_read_only_fd(int fd, bool b) {
147 uint64_t flags, nflags;
152 if (fstat(fd, &st) < 0)
155 if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
158 if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
162 nflags = flags | BTRFS_SUBVOL_RDONLY;
164 nflags = flags & ~BTRFS_SUBVOL_RDONLY;
169 if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0)
175 int btrfs_subvol_set_read_only(const char *path, bool b) {
176 _cleanup_close_ int fd = -1;
178 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
182 return btrfs_subvol_set_read_only_fd(fd, b);
185 int btrfs_subvol_get_read_only_fd(int fd) {
188 if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
191 return !!(flags & BTRFS_SUBVOL_RDONLY);
194 int btrfs_reflink(int infd, int outfd) {
200 r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
207 int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) {
208 struct btrfs_ioctl_clone_range_args args = {
210 .src_offset = in_offset,
212 .dest_offset = out_offset,
220 r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args);
227 int btrfs_get_block_device_fd(int fd, dev_t *dev) {
228 struct btrfs_ioctl_fs_info_args fsi = {};
234 if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
237 /* We won't do this for btrfs RAID */
238 if (fsi.num_devices != 1)
241 for (id = 1; id <= fsi.max_id; id++) {
242 struct btrfs_ioctl_dev_info_args di = {
247 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
254 if (stat((char*) di.path, &st) < 0)
257 if (!S_ISBLK(st.st_mode))
260 if (major(st.st_rdev) == 0)
270 int btrfs_get_block_device(const char *path, dev_t *dev) {
271 _cleanup_close_ int fd = -1;
276 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
280 return btrfs_get_block_device_fd(fd, dev);
283 int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
284 struct btrfs_ioctl_ino_lookup_args args = {
285 .objectid = BTRFS_FIRST_FREE_OBJECTID
291 if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
298 static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) {
301 /* the objectid, type, offset together make up the btrfs key,
302 * which is considered a single 136byte integer when
303 * comparing. This call increases the counter by one, dealing
304 * with the overflow between the overflows */
306 if (args->key.min_offset < (uint64_t) -1) {
307 args->key.min_offset++;
311 if (args->key.min_type < (uint8_t) -1) {
312 args->key.min_type++;
313 args->key.min_offset = 0;
317 if (args->key.min_objectid < (uint64_t) -1) {
318 args->key.min_objectid++;
319 args->key.min_offset = 0;
320 args->key.min_type = 0;
327 static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) {
331 args->key.min_objectid = h->objectid;
332 args->key.min_type = h->type;
333 args->key.min_offset = h->offset;
336 static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) {
339 /* Compare min and max */
341 if (args->key.min_objectid < args->key.max_objectid)
343 if (args->key.min_objectid > args->key.max_objectid)
346 if (args->key.min_type < args->key.max_type)
348 if (args->key.min_type > args->key.max_type)
351 if (args->key.min_offset < args->key.max_offset)
353 if (args->key.min_offset > args->key.max_offset)
359 #define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) \
361 (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \
362 (i) < (args).key.nr_items; \
364 (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len))
366 #define BTRFS_IOCTL_SEARCH_HEADER_BODY(sh) \
367 ((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header)))
369 int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
370 struct btrfs_ioctl_search_args args = {
371 /* Tree of tree roots */
372 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
374 /* Look precisely for the subvolume items */
375 .key.min_type = BTRFS_ROOT_ITEM_KEY,
376 .key.max_type = BTRFS_ROOT_ITEM_KEY,
379 .key.max_offset = (uint64_t) -1,
381 /* No restrictions on the other components */
382 .key.min_transid = 0,
383 .key.max_transid = (uint64_t) -1,
393 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
397 args.key.min_objectid = args.key.max_objectid = subvol_id;
399 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
400 const struct btrfs_ioctl_search_header *sh;
403 args.key.nr_items = 256;
404 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
407 if (args.key.nr_items <= 0)
410 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
412 const struct btrfs_root_item *ri;
414 /* Make sure we start the next search at least from this entry */
415 btrfs_ioctl_search_args_set(&args, sh);
417 if (sh->objectid != subvol_id)
419 if (sh->type != BTRFS_ROOT_ITEM_KEY)
422 /* Older versions of the struct lacked the otime setting */
423 if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
426 ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
428 ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
429 (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
431 ret->subvol_id = subvol_id;
432 ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
434 assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid));
435 memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
436 memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));
442 /* Increase search key by one, to read the next item, if we can. */
443 if (!btrfs_ioctl_search_args_inc(&args))
454 int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
456 struct btrfs_ioctl_search_args args = {
457 /* Tree of quota items */
458 .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
460 /* The object ID is always 0 */
461 .key.min_objectid = 0,
462 .key.max_objectid = 0,
464 /* Look precisely for the quota items */
465 .key.min_type = BTRFS_QGROUP_STATUS_KEY,
466 .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
468 /* No restrictions on the other components */
469 .key.min_transid = 0,
470 .key.max_transid = (uint64_t) -1,
474 bool found_info = false, found_limit = false;
480 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
484 args.key.min_offset = args.key.max_offset = subvol_id;
486 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
487 const struct btrfs_ioctl_search_header *sh;
490 args.key.nr_items = 256;
491 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
494 if (args.key.nr_items <= 0)
497 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
499 /* Make sure we start the next search at least from this entry */
500 btrfs_ioctl_search_args_set(&args, sh);
502 if (sh->objectid != 0)
504 if (sh->offset != subvol_id)
507 if (sh->type == BTRFS_QGROUP_INFO_KEY) {
508 const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
510 ret->referenced = le64toh(qii->rfer);
511 ret->exclusive = le64toh(qii->excl);
515 } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
516 const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
518 ret->referenced_max = le64toh(qli->max_rfer);
519 ret->exclusive_max = le64toh(qli->max_excl);
521 if (ret->referenced_max == 0)
522 ret->referenced_max = (uint64_t) -1;
523 if (ret->exclusive_max == 0)
524 ret->exclusive_max = (uint64_t) -1;
529 if (found_info && found_limit)
533 /* Increase search key by one, to read the next item, if we can. */
534 if (!btrfs_ioctl_search_args_inc(&args))
539 if (!found_limit && !found_info)
543 ret->referenced = (uint64_t) -1;
544 ret->exclusive = (uint64_t) -1;
548 ret->referenced_max = (uint64_t) -1;
549 ret->exclusive_max = (uint64_t) -1;
555 int btrfs_defrag_fd(int fd) {
558 if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
564 int btrfs_defrag(const char *p) {
565 _cleanup_close_ int fd = -1;
567 fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
571 return btrfs_defrag_fd(fd);
574 int btrfs_quota_enable_fd(int fd, bool b) {
575 struct btrfs_ioctl_quota_ctl_args args = {
576 .cmd = b ? BTRFS_QUOTA_CTL_ENABLE : BTRFS_QUOTA_CTL_DISABLE,
581 if (ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args) < 0)
587 int btrfs_quota_enable(const char *path, bool b) {
588 _cleanup_close_ int fd = -1;
590 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
594 return btrfs_quota_enable_fd(fd, b);
597 int btrfs_quota_limit_fd(int fd, uint64_t referenced_max) {
598 struct btrfs_ioctl_qgroup_limit_args args = {
600 referenced_max == (uint64_t) -1 ? 0 :
601 referenced_max == 0 ? 1 : referenced_max,
602 .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER,
607 if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args) < 0)
613 int btrfs_quota_limit(const char *path, uint64_t referenced_max) {
614 _cleanup_close_ int fd = -1;
616 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
620 return btrfs_quota_limit_fd(fd, referenced_max);
623 int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
624 struct btrfs_ioctl_vol_args args = {};
625 _cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL;
626 _cleanup_close_ int loop_fd = -1, backing_fd = -1;
631 /* btrfs cannot handle file systems < 16M, hence use this as minimum */
632 if (new_size < 16*1024*1024)
633 new_size = 16*1024*1024;
635 r = btrfs_get_block_device_fd(fd, &dev);
641 if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0)
643 r = read_one_line_file(p, &backing);
648 if (isempty(backing) || !path_is_absolute(backing))
651 backing_fd = open(backing, O_RDWR|O_CLOEXEC|O_NOCTTY);
655 if (fstat(backing_fd, &st) < 0)
657 if (!S_ISREG(st.st_mode))
660 if (new_size == (uint64_t) st.st_size)
663 if (grow_only && new_size < (uint64_t) st.st_size)
666 if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0)
668 loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY);
672 if (snprintf(args.name, sizeof(args.name), "%" PRIu64, new_size) >= (int) sizeof(args.name))
675 if (new_size < (uint64_t) st.st_size) {
676 /* Decrease size: first decrease btrfs size, then shorten loopback */
677 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
681 if (ftruncate(backing_fd, new_size) < 0)
684 if (ioctl(loop_fd, LOOP_SET_CAPACITY, 0) < 0)
687 if (new_size > (uint64_t) st.st_size) {
688 /* Increase size: first enlarge loopback, then increase btrfs size */
689 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
693 /* Make sure the free disk space is correctly updated for both file systems */
695 (void) fsync(backing_fd);
700 int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) {
701 _cleanup_close_ int fd = -1;
703 fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
707 return btrfs_resize_loopback_fd(fd, new_size, grow_only);
710 static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol_id, bool recursive) {
711 struct btrfs_ioctl_search_args args = {
712 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
714 .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
715 .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
717 .key.min_type = BTRFS_ROOT_BACKREF_KEY,
718 .key.max_type = BTRFS_ROOT_BACKREF_KEY,
720 .key.min_transid = 0,
721 .key.max_transid = (uint64_t) -1,
724 struct btrfs_ioctl_vol_args vol_args = {};
725 _cleanup_close_ int subvol_fd = -1;
731 /* First, try to remove the subvolume. If it happens to be
732 * already empty, this will just work. */
733 strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
734 if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) >= 0)
736 if (!recursive || errno != ENOTEMPTY)
739 /* OK, the subvolume is not empty, let's look for child
740 * subvolumes, and remove them, first */
741 subvol_fd = openat(fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
745 if (subvol_id == 0) {
746 r = btrfs_subvol_get_id_fd(subvol_fd, &subvol_id);
751 args.key.min_offset = args.key.max_offset = subvol_id;
753 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
754 const struct btrfs_ioctl_search_header *sh;
757 args.key.nr_items = 256;
758 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
761 if (args.key.nr_items <= 0)
764 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
765 _cleanup_free_ char *p = NULL;
766 const struct btrfs_root_ref *ref;
767 struct btrfs_ioctl_ino_lookup_args ino_args;
769 btrfs_ioctl_search_args_set(&args, sh);
771 if (sh->type != BTRFS_ROOT_BACKREF_KEY)
773 if (sh->offset != subvol_id)
776 ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
778 p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
783 ino_args.treeid = subvol_id;
784 ino_args.objectid = htole64(ref->dirid);
786 if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
789 if (isempty(ino_args.name))
790 /* Subvolume is in the top-level
791 * directory of the subvolume. */
792 r = subvol_remove_children(subvol_fd, p, sh->objectid, recursive);
794 _cleanup_close_ int child_fd = -1;
796 /* Subvolume is somewhere further down,
797 * hence we need to open the
798 * containing directory first */
800 child_fd = openat(subvol_fd, ino_args.name, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
804 r = subvol_remove_children(child_fd, p, sh->objectid, recursive);
810 /* Increase search key by one, to read the next item, if we can. */
811 if (!btrfs_ioctl_search_args_inc(&args))
815 /* OK, the child subvolumes should all be gone now, let's try
816 * again to remove the subvolume */
817 if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) < 0)
823 int btrfs_subvol_remove(const char *path, bool recursive) {
824 _cleanup_close_ int fd = -1;
825 const char *subvolume;
830 r = extract_subvolume_name(path, &subvolume);
834 fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
838 return subvol_remove_children(fd, subvolume, 0, recursive);
841 int btrfs_subvol_remove_fd(int fd, const char *subvolume, bool recursive) {
842 return subvol_remove_children(fd, subvolume, 0, recursive);
845 static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolume, uint64_t subvol_id, BtrfsSnapshotFlags flags) {
847 struct btrfs_ioctl_search_args args = {
848 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
850 .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
851 .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
853 .key.min_type = BTRFS_ROOT_BACKREF_KEY,
854 .key.max_type = BTRFS_ROOT_BACKREF_KEY,
856 .key.min_transid = 0,
857 .key.max_transid = (uint64_t) -1,
860 struct btrfs_ioctl_vol_args_v2 vol_args = {
861 .flags = flags & BTRFS_SNAPSHOT_READ_ONLY ? BTRFS_SUBVOL_RDONLY : 0,
870 strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
871 vol_args.fd = old_fd;
873 if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &vol_args) < 0)
876 if (!(flags & BTRFS_SNAPSHOT_RECURSIVE))
879 if (subvol_id == 0) {
880 r = btrfs_subvol_get_id_fd(old_fd, &subvol_id);
885 args.key.min_offset = args.key.max_offset = subvol_id;
887 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
888 const struct btrfs_ioctl_search_header *sh;
891 args.key.nr_items = 256;
892 if (ioctl(old_fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
895 if (args.key.nr_items <= 0)
898 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
899 _cleanup_free_ char *p = NULL, *c = NULL, *np = NULL;
900 struct btrfs_ioctl_ino_lookup_args ino_args;
901 const struct btrfs_root_ref *ref;
902 _cleanup_close_ int old_child_fd = -1, new_child_fd = -1;
904 btrfs_ioctl_search_args_set(&args, sh);
906 if (sh->type != BTRFS_ROOT_BACKREF_KEY)
908 if (sh->offset != subvol_id)
911 ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
913 p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
918 ino_args.treeid = subvol_id;
919 ino_args.objectid = htole64(ref->dirid);
921 if (ioctl(old_fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
924 /* The kernel returns an empty name if the
925 * subvolume is in the top-level directory,
926 * and otherwise appends a slash, so that we
927 * can just concatenate easily here, without
929 c = strappend(ino_args.name, p);
933 old_child_fd = openat(old_fd, c, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
934 if (old_child_fd < 0)
937 np = strjoin(subvolume, "/", ino_args.name, NULL);
941 new_child_fd = openat(new_fd, np, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
942 if (new_child_fd < 0)
945 /* When btrfs clones the subvolumes, child
946 * subvolumes appear as directories. Remove
947 * them, so that we can create a new snapshot
949 if (unlinkat(new_child_fd, p, AT_REMOVEDIR) < 0)
952 r = subvol_snapshot_children(old_child_fd, new_child_fd, p, sh->objectid, flags & ~BTRFS_SNAPSHOT_FALLBACK_COPY);
957 /* Increase search key by one, to read the next item, if we can. */
958 if (!btrfs_ioctl_search_args_inc(&args))
965 int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) {
966 _cleanup_close_ int new_fd = -1;
967 const char *subvolume;
973 r = btrfs_is_snapshot(old_fd);
977 if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY))
980 r = btrfs_subvol_make(new_path);
984 r = copy_directory_fd(old_fd, new_path, true);
986 btrfs_subvol_remove(new_path, false);
990 if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
991 r = btrfs_subvol_set_read_only(new_path, true);
993 btrfs_subvol_remove(new_path, false);
1001 r = extract_subvolume_name(new_path, &subvolume);
1005 new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1009 return subvol_snapshot_children(old_fd, new_fd, subvolume, 0, flags);
1012 int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) {
1013 _cleanup_close_ int old_fd = -1;
1018 old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1022 return btrfs_subvol_snapshot_fd(old_fd, new_path, flags);