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_filesystem(int fd) {
91 if (fstatfs(fd, &sfs) < 0)
94 return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
97 int btrfs_is_subvol(int fd) {
102 /* On btrfs subvolumes always have the inode 256 */
104 if (fstat(fd, &st) < 0)
107 if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
110 return btrfs_is_filesystem(fd);
113 int btrfs_subvol_make(const char *path) {
114 struct btrfs_ioctl_vol_args args = {};
115 _cleanup_close_ int fd = -1;
116 const char *subvolume;
121 r = extract_subvolume_name(path, &subvolume);
125 fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
129 strncpy(args.name, subvolume, sizeof(args.name)-1);
131 if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
137 int btrfs_subvol_make_label(const char *path) {
142 r = mac_selinux_create_file_prepare(path, S_IFDIR);
146 r = btrfs_subvol_make(path);
147 mac_selinux_create_file_clear();
152 return mac_smack_fix(path, false, false);
155 int btrfs_subvol_set_read_only_fd(int fd, bool b) {
156 uint64_t flags, nflags;
161 if (fstat(fd, &st) < 0)
164 if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
167 if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
171 nflags = flags | BTRFS_SUBVOL_RDONLY;
173 nflags = flags & ~BTRFS_SUBVOL_RDONLY;
178 if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0)
184 int btrfs_subvol_set_read_only(const char *path, bool b) {
185 _cleanup_close_ int fd = -1;
187 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
191 return btrfs_subvol_set_read_only_fd(fd, b);
194 int btrfs_subvol_get_read_only_fd(int fd) {
197 if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
200 return !!(flags & BTRFS_SUBVOL_RDONLY);
203 int btrfs_reflink(int infd, int outfd) {
209 r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
216 int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) {
217 struct btrfs_ioctl_clone_range_args args = {
219 .src_offset = in_offset,
221 .dest_offset = out_offset,
229 r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args);
236 int btrfs_get_block_device_fd(int fd, dev_t *dev) {
237 struct btrfs_ioctl_fs_info_args fsi = {};
243 if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
246 /* We won't do this for btrfs RAID */
247 if (fsi.num_devices != 1)
250 for (id = 1; id <= fsi.max_id; id++) {
251 struct btrfs_ioctl_dev_info_args di = {
256 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
263 if (stat((char*) di.path, &st) < 0)
266 if (!S_ISBLK(st.st_mode))
269 if (major(st.st_rdev) == 0)
279 int btrfs_get_block_device(const char *path, dev_t *dev) {
280 _cleanup_close_ int fd = -1;
285 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
289 return btrfs_get_block_device_fd(fd, dev);
292 int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
293 struct btrfs_ioctl_ino_lookup_args args = {
294 .objectid = BTRFS_FIRST_FREE_OBJECTID
300 if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
307 static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) {
310 /* the objectid, type, offset together make up the btrfs key,
311 * which is considered a single 136byte integer when
312 * comparing. This call increases the counter by one, dealing
313 * with the overflow between the overflows */
315 if (args->key.min_offset < (uint64_t) -1) {
316 args->key.min_offset++;
320 if (args->key.min_type < (uint8_t) -1) {
321 args->key.min_type++;
322 args->key.min_offset = 0;
326 if (args->key.min_objectid < (uint64_t) -1) {
327 args->key.min_objectid++;
328 args->key.min_offset = 0;
329 args->key.min_type = 0;
336 static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) {
340 args->key.min_objectid = h->objectid;
341 args->key.min_type = h->type;
342 args->key.min_offset = h->offset;
345 static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) {
348 /* Compare min and max */
350 if (args->key.min_objectid < args->key.max_objectid)
352 if (args->key.min_objectid > args->key.max_objectid)
355 if (args->key.min_type < args->key.max_type)
357 if (args->key.min_type > args->key.max_type)
360 if (args->key.min_offset < args->key.max_offset)
362 if (args->key.min_offset > args->key.max_offset)
368 #define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) \
370 (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \
371 (i) < (args).key.nr_items; \
373 (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len))
375 #define BTRFS_IOCTL_SEARCH_HEADER_BODY(sh) \
376 ((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header)))
378 int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
379 struct btrfs_ioctl_search_args args = {
380 /* Tree of tree roots */
381 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
383 /* Look precisely for the subvolume items */
384 .key.min_type = BTRFS_ROOT_ITEM_KEY,
385 .key.max_type = BTRFS_ROOT_ITEM_KEY,
388 .key.max_offset = (uint64_t) -1,
390 /* No restrictions on the other components */
391 .key.min_transid = 0,
392 .key.max_transid = (uint64_t) -1,
402 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
406 args.key.min_objectid = args.key.max_objectid = subvol_id;
408 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
409 const struct btrfs_ioctl_search_header *sh;
412 args.key.nr_items = 256;
413 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
416 if (args.key.nr_items <= 0)
419 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
421 const struct btrfs_root_item *ri;
423 /* Make sure we start the next search at least from this entry */
424 btrfs_ioctl_search_args_set(&args, sh);
426 if (sh->objectid != subvol_id)
428 if (sh->type != BTRFS_ROOT_ITEM_KEY)
431 /* Older versions of the struct lacked the otime setting */
432 if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
435 ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
437 ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
438 (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
440 ret->subvol_id = subvol_id;
441 ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
443 assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid));
444 memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
445 memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));
451 /* Increase search key by one, to read the next item, if we can. */
452 if (!btrfs_ioctl_search_args_inc(&args))
463 int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
465 struct btrfs_ioctl_search_args args = {
466 /* Tree of quota items */
467 .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
469 /* The object ID is always 0 */
470 .key.min_objectid = 0,
471 .key.max_objectid = 0,
473 /* Look precisely for the quota items */
474 .key.min_type = BTRFS_QGROUP_STATUS_KEY,
475 .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
477 /* No restrictions on the other components */
478 .key.min_transid = 0,
479 .key.max_transid = (uint64_t) -1,
483 bool found_info = false, found_limit = false;
489 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
493 args.key.min_offset = args.key.max_offset = subvol_id;
495 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
496 const struct btrfs_ioctl_search_header *sh;
499 args.key.nr_items = 256;
500 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
503 if (args.key.nr_items <= 0)
506 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
508 /* Make sure we start the next search at least from this entry */
509 btrfs_ioctl_search_args_set(&args, sh);
511 if (sh->objectid != 0)
513 if (sh->offset != subvol_id)
516 if (sh->type == BTRFS_QGROUP_INFO_KEY) {
517 const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
519 ret->referenced = le64toh(qii->rfer);
520 ret->exclusive = le64toh(qii->excl);
524 } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
525 const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
527 ret->referenced_max = le64toh(qli->max_rfer);
528 ret->exclusive_max = le64toh(qli->max_excl);
530 if (ret->referenced_max == 0)
531 ret->referenced_max = (uint64_t) -1;
532 if (ret->exclusive_max == 0)
533 ret->exclusive_max = (uint64_t) -1;
538 if (found_info && found_limit)
542 /* Increase search key by one, to read the next item, if we can. */
543 if (!btrfs_ioctl_search_args_inc(&args))
548 if (!found_limit && !found_info)
552 ret->referenced = (uint64_t) -1;
553 ret->exclusive = (uint64_t) -1;
557 ret->referenced_max = (uint64_t) -1;
558 ret->exclusive_max = (uint64_t) -1;
564 int btrfs_defrag_fd(int fd) {
567 if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
573 int btrfs_defrag(const char *p) {
574 _cleanup_close_ int fd = -1;
576 fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
580 return btrfs_defrag_fd(fd);
583 int btrfs_quota_enable_fd(int fd, bool b) {
584 struct btrfs_ioctl_quota_ctl_args args = {
585 .cmd = b ? BTRFS_QUOTA_CTL_ENABLE : BTRFS_QUOTA_CTL_DISABLE,
590 if (ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args) < 0)
596 int btrfs_quota_enable(const char *path, bool b) {
597 _cleanup_close_ int fd = -1;
599 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
603 return btrfs_quota_enable_fd(fd, b);
606 int btrfs_quota_limit_fd(int fd, uint64_t referenced_max) {
607 struct btrfs_ioctl_qgroup_limit_args args = {
609 referenced_max == (uint64_t) -1 ? 0 :
610 referenced_max == 0 ? 1 : referenced_max,
611 .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER,
616 if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args) < 0)
622 int btrfs_quota_limit(const char *path, uint64_t referenced_max) {
623 _cleanup_close_ int fd = -1;
625 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
629 return btrfs_quota_limit_fd(fd, referenced_max);
632 int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
633 struct btrfs_ioctl_vol_args args = {};
634 _cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL;
635 _cleanup_close_ int loop_fd = -1, backing_fd = -1;
640 /* btrfs cannot handle file systems < 16M, hence use this as minimum */
641 if (new_size < 16*1024*1024)
642 new_size = 16*1024*1024;
644 r = btrfs_get_block_device_fd(fd, &dev);
650 if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0)
652 r = read_one_line_file(p, &backing);
657 if (isempty(backing) || !path_is_absolute(backing))
660 backing_fd = open(backing, O_RDWR|O_CLOEXEC|O_NOCTTY);
664 if (fstat(backing_fd, &st) < 0)
666 if (!S_ISREG(st.st_mode))
669 if (new_size == (uint64_t) st.st_size)
672 if (grow_only && new_size < (uint64_t) st.st_size)
675 if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0)
677 loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY);
681 if (snprintf(args.name, sizeof(args.name), "%" PRIu64, new_size) >= (int) sizeof(args.name))
684 if (new_size < (uint64_t) st.st_size) {
685 /* Decrease size: first decrease btrfs size, then shorten loopback */
686 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
690 if (ftruncate(backing_fd, new_size) < 0)
693 if (ioctl(loop_fd, LOOP_SET_CAPACITY, 0) < 0)
696 if (new_size > (uint64_t) st.st_size) {
697 /* Increase size: first enlarge loopback, then increase btrfs size */
698 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
702 /* Make sure the free disk space is correctly updated for both file systems */
704 (void) fsync(backing_fd);
709 int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) {
710 _cleanup_close_ int fd = -1;
712 fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
716 return btrfs_resize_loopback_fd(fd, new_size, grow_only);
719 static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol_id, bool recursive) {
720 struct btrfs_ioctl_search_args args = {
721 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
723 .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
724 .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
726 .key.min_type = BTRFS_ROOT_BACKREF_KEY,
727 .key.max_type = BTRFS_ROOT_BACKREF_KEY,
729 .key.min_transid = 0,
730 .key.max_transid = (uint64_t) -1,
733 struct btrfs_ioctl_vol_args vol_args = {};
734 _cleanup_close_ int subvol_fd = -1;
740 /* First, try to remove the subvolume. If it happens to be
741 * already empty, this will just work. */
742 strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
743 if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) >= 0)
745 if (!recursive || errno != ENOTEMPTY)
748 /* OK, the subvolume is not empty, let's look for child
749 * subvolumes, and remove them, first */
750 subvol_fd = openat(fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
754 if (subvol_id == 0) {
755 r = btrfs_subvol_get_id_fd(subvol_fd, &subvol_id);
760 args.key.min_offset = args.key.max_offset = subvol_id;
762 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
763 const struct btrfs_ioctl_search_header *sh;
766 args.key.nr_items = 256;
767 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
770 if (args.key.nr_items <= 0)
773 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
774 _cleanup_free_ char *p = NULL;
775 const struct btrfs_root_ref *ref;
776 struct btrfs_ioctl_ino_lookup_args ino_args;
778 btrfs_ioctl_search_args_set(&args, sh);
780 if (sh->type != BTRFS_ROOT_BACKREF_KEY)
782 if (sh->offset != subvol_id)
785 ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
787 p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
792 ino_args.treeid = subvol_id;
793 ino_args.objectid = htole64(ref->dirid);
795 if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
798 if (isempty(ino_args.name))
799 /* Subvolume is in the top-level
800 * directory of the subvolume. */
801 r = subvol_remove_children(subvol_fd, p, sh->objectid, recursive);
803 _cleanup_close_ int child_fd = -1;
805 /* Subvolume is somewhere further down,
806 * hence we need to open the
807 * containing directory first */
809 child_fd = openat(subvol_fd, ino_args.name, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
813 r = subvol_remove_children(child_fd, p, sh->objectid, recursive);
819 /* Increase search key by one, to read the next item, if we can. */
820 if (!btrfs_ioctl_search_args_inc(&args))
824 /* OK, the child subvolumes should all be gone now, let's try
825 * again to remove the subvolume */
826 if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) < 0)
832 int btrfs_subvol_remove(const char *path, bool recursive) {
833 _cleanup_close_ int fd = -1;
834 const char *subvolume;
839 r = extract_subvolume_name(path, &subvolume);
843 fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
847 return subvol_remove_children(fd, subvolume, 0, recursive);
850 int btrfs_subvol_remove_fd(int fd, const char *subvolume, bool recursive) {
851 return subvol_remove_children(fd, subvolume, 0, recursive);
854 static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolume, uint64_t subvol_id, BtrfsSnapshotFlags flags) {
856 struct btrfs_ioctl_search_args args = {
857 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
859 .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
860 .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
862 .key.min_type = BTRFS_ROOT_BACKREF_KEY,
863 .key.max_type = BTRFS_ROOT_BACKREF_KEY,
865 .key.min_transid = 0,
866 .key.max_transid = (uint64_t) -1,
869 struct btrfs_ioctl_vol_args_v2 vol_args = {
870 .flags = flags & BTRFS_SNAPSHOT_READ_ONLY ? BTRFS_SUBVOL_RDONLY : 0,
879 strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
880 vol_args.fd = old_fd;
882 if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &vol_args) < 0)
885 if (!(flags & BTRFS_SNAPSHOT_RECURSIVE))
888 if (subvol_id == 0) {
889 r = btrfs_subvol_get_id_fd(old_fd, &subvol_id);
894 args.key.min_offset = args.key.max_offset = subvol_id;
896 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
897 const struct btrfs_ioctl_search_header *sh;
900 args.key.nr_items = 256;
901 if (ioctl(old_fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
904 if (args.key.nr_items <= 0)
907 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
908 _cleanup_free_ char *p = NULL, *c = NULL, *np = NULL;
909 struct btrfs_ioctl_ino_lookup_args ino_args;
910 const struct btrfs_root_ref *ref;
911 _cleanup_close_ int old_child_fd = -1, new_child_fd = -1;
913 btrfs_ioctl_search_args_set(&args, sh);
915 if (sh->type != BTRFS_ROOT_BACKREF_KEY)
917 if (sh->offset != subvol_id)
920 ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
922 p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
927 ino_args.treeid = subvol_id;
928 ino_args.objectid = htole64(ref->dirid);
930 if (ioctl(old_fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
933 /* The kernel returns an empty name if the
934 * subvolume is in the top-level directory,
935 * and otherwise appends a slash, so that we
936 * can just concatenate easily here, without
938 c = strappend(ino_args.name, p);
942 old_child_fd = openat(old_fd, c, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
943 if (old_child_fd < 0)
946 np = strjoin(subvolume, "/", ino_args.name, NULL);
950 new_child_fd = openat(new_fd, np, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
951 if (new_child_fd < 0)
954 /* When btrfs clones the subvolumes, child
955 * subvolumes appear as directories. Remove
956 * them, so that we can create a new snapshot
958 if (unlinkat(new_child_fd, p, AT_REMOVEDIR) < 0)
961 r = subvol_snapshot_children(old_child_fd, new_child_fd, p, sh->objectid, flags & ~BTRFS_SNAPSHOT_FALLBACK_COPY);
966 /* Increase search key by one, to read the next item, if we can. */
967 if (!btrfs_ioctl_search_args_inc(&args))
974 int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) {
975 _cleanup_close_ int new_fd = -1;
976 const char *subvolume;
982 r = btrfs_is_subvol(old_fd);
986 if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY))
989 r = btrfs_subvol_make(new_path);
993 r = copy_directory_fd(old_fd, new_path, true);
995 btrfs_subvol_remove(new_path, false);
999 if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
1000 r = btrfs_subvol_set_read_only(new_path, true);
1002 btrfs_subvol_remove(new_path, false);
1010 r = extract_subvolume_name(new_path, &subvolume);
1014 new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1018 return subvol_snapshot_children(old_fd, new_fd, subvolume, 0, flags);
1021 int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) {
1022 _cleanup_close_ int old_fd = -1;
1027 old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1031 return btrfs_subvol_snapshot_fd(old_fd, new_path, flags);