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 /* WARNING: Be careful with file system ioctls! When we get an fd, we
42 * need to make sure it either refers to only a regular file or
43 * directory, or that it is located on btrfs, before invoking any
44 * btrfs ioctls. The ioctl numbers are reused by some device drivers
45 * (such as DRM), and hence might have bad effects when invoked on
46 * device nodes (that reference drivers) rather than fds to normal
47 * files or directories. */
49 static int validate_subvolume_name(const char *name) {
51 if (!filename_is_valid(name))
54 if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
60 static int open_parent(const char *path, int flags) {
61 _cleanup_free_ char *parent = NULL;
66 r = path_get_parent(path, &parent);
70 fd = open(parent, flags);
77 static int extract_subvolume_name(const char *path, const char **subvolume) {
86 r = validate_subvolume_name(fn);
94 int btrfs_is_filesystem(int fd) {
99 if (fstatfs(fd, &sfs) < 0)
102 return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
105 int btrfs_is_subvol(int fd) {
110 /* On btrfs subvolumes always have the inode 256 */
112 if (fstat(fd, &st) < 0)
115 if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
118 return btrfs_is_filesystem(fd);
121 int btrfs_subvol_make(const char *path) {
122 struct btrfs_ioctl_vol_args args = {};
123 _cleanup_close_ int fd = -1;
124 const char *subvolume;
129 r = extract_subvolume_name(path, &subvolume);
133 fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
137 strncpy(args.name, subvolume, sizeof(args.name)-1);
139 if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
145 int btrfs_subvol_make_label(const char *path) {
150 r = mac_selinux_create_file_prepare(path, S_IFDIR);
154 r = btrfs_subvol_make(path);
155 mac_selinux_create_file_clear();
160 return mac_smack_fix(path, false, false);
163 int btrfs_subvol_set_read_only_fd(int fd, bool b) {
164 uint64_t flags, nflags;
169 if (fstat(fd, &st) < 0)
172 if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
175 if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
179 nflags = flags | BTRFS_SUBVOL_RDONLY;
181 nflags = flags & ~BTRFS_SUBVOL_RDONLY;
186 if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0)
192 int btrfs_subvol_set_read_only(const char *path, bool b) {
193 _cleanup_close_ int fd = -1;
195 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
199 return btrfs_subvol_set_read_only_fd(fd, b);
202 int btrfs_subvol_get_read_only_fd(int fd) {
208 if (fstat(fd, &st) < 0)
211 if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
214 if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
217 return !!(flags & BTRFS_SUBVOL_RDONLY);
220 int btrfs_reflink(int infd, int outfd) {
227 /* Make sure we invoke the ioctl on a regular file, so that no
228 * device driver accidentally gets it. */
230 if (fstat(outfd, &st) < 0)
233 if (!S_ISREG(st.st_mode))
236 r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
243 int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) {
244 struct btrfs_ioctl_clone_range_args args = {
246 .src_offset = in_offset,
248 .dest_offset = out_offset,
257 if (fstat(outfd, &st) < 0)
260 if (!S_ISREG(st.st_mode))
263 r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args);
270 int btrfs_get_block_device_fd(int fd, dev_t *dev) {
271 struct btrfs_ioctl_fs_info_args fsi = {};
278 r = btrfs_is_filesystem(fd);
284 if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
287 /* We won't do this for btrfs RAID */
288 if (fsi.num_devices != 1)
291 for (id = 1; id <= fsi.max_id; id++) {
292 struct btrfs_ioctl_dev_info_args di = {
297 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
304 if (stat((char*) di.path, &st) < 0)
307 if (!S_ISBLK(st.st_mode))
310 if (major(st.st_rdev) == 0)
320 int btrfs_get_block_device(const char *path, dev_t *dev) {
321 _cleanup_close_ int fd = -1;
326 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
330 return btrfs_get_block_device_fd(fd, dev);
333 int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
334 struct btrfs_ioctl_ino_lookup_args args = {
335 .objectid = BTRFS_FIRST_FREE_OBJECTID
342 r = btrfs_is_filesystem(fd);
348 if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
355 static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) {
358 /* the objectid, type, offset together make up the btrfs key,
359 * which is considered a single 136byte integer when
360 * comparing. This call increases the counter by one, dealing
361 * with the overflow between the overflows */
363 if (args->key.min_offset < (uint64_t) -1) {
364 args->key.min_offset++;
368 if (args->key.min_type < (uint8_t) -1) {
369 args->key.min_type++;
370 args->key.min_offset = 0;
374 if (args->key.min_objectid < (uint64_t) -1) {
375 args->key.min_objectid++;
376 args->key.min_offset = 0;
377 args->key.min_type = 0;
384 static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) {
388 args->key.min_objectid = h->objectid;
389 args->key.min_type = h->type;
390 args->key.min_offset = h->offset;
393 static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) {
396 /* Compare min and max */
398 if (args->key.min_objectid < args->key.max_objectid)
400 if (args->key.min_objectid > args->key.max_objectid)
403 if (args->key.min_type < args->key.max_type)
405 if (args->key.min_type > args->key.max_type)
408 if (args->key.min_offset < args->key.max_offset)
410 if (args->key.min_offset > args->key.max_offset)
416 #define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) \
418 (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \
419 (i) < (args).key.nr_items; \
421 (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len))
423 #define BTRFS_IOCTL_SEARCH_HEADER_BODY(sh) \
424 ((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header)))
426 int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
427 struct btrfs_ioctl_search_args args = {
428 /* Tree of tree roots */
429 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
431 /* Look precisely for the subvolume items */
432 .key.min_type = BTRFS_ROOT_ITEM_KEY,
433 .key.max_type = BTRFS_ROOT_ITEM_KEY,
436 .key.max_offset = (uint64_t) -1,
438 /* No restrictions on the other components */
439 .key.min_transid = 0,
440 .key.max_transid = (uint64_t) -1,
450 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
454 args.key.min_objectid = args.key.max_objectid = subvol_id;
456 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
457 const struct btrfs_ioctl_search_header *sh;
460 args.key.nr_items = 256;
461 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
464 if (args.key.nr_items <= 0)
467 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
469 const struct btrfs_root_item *ri;
471 /* Make sure we start the next search at least from this entry */
472 btrfs_ioctl_search_args_set(&args, sh);
474 if (sh->objectid != subvol_id)
476 if (sh->type != BTRFS_ROOT_ITEM_KEY)
479 /* Older versions of the struct lacked the otime setting */
480 if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
483 ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
485 ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
486 (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
488 ret->subvol_id = subvol_id;
489 ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
491 assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid));
492 memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
493 memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));
499 /* Increase search key by one, to read the next item, if we can. */
500 if (!btrfs_ioctl_search_args_inc(&args))
511 int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
513 struct btrfs_ioctl_search_args args = {
514 /* Tree of quota items */
515 .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
517 /* The object ID is always 0 */
518 .key.min_objectid = 0,
519 .key.max_objectid = 0,
521 /* Look precisely for the quota items */
522 .key.min_type = BTRFS_QGROUP_STATUS_KEY,
523 .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
525 /* No restrictions on the other components */
526 .key.min_transid = 0,
527 .key.max_transid = (uint64_t) -1,
531 bool found_info = false, found_limit = false;
537 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
541 args.key.min_offset = args.key.max_offset = subvol_id;
543 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
544 const struct btrfs_ioctl_search_header *sh;
547 args.key.nr_items = 256;
548 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
551 if (args.key.nr_items <= 0)
554 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
556 /* Make sure we start the next search at least from this entry */
557 btrfs_ioctl_search_args_set(&args, sh);
559 if (sh->objectid != 0)
561 if (sh->offset != subvol_id)
564 if (sh->type == BTRFS_QGROUP_INFO_KEY) {
565 const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
567 ret->referenced = le64toh(qii->rfer);
568 ret->exclusive = le64toh(qii->excl);
572 } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
573 const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
575 ret->referenced_max = le64toh(qli->max_rfer);
576 ret->exclusive_max = le64toh(qli->max_excl);
578 if (ret->referenced_max == 0)
579 ret->referenced_max = (uint64_t) -1;
580 if (ret->exclusive_max == 0)
581 ret->exclusive_max = (uint64_t) -1;
586 if (found_info && found_limit)
590 /* Increase search key by one, to read the next item, if we can. */
591 if (!btrfs_ioctl_search_args_inc(&args))
596 if (!found_limit && !found_info)
600 ret->referenced = (uint64_t) -1;
601 ret->exclusive = (uint64_t) -1;
605 ret->referenced_max = (uint64_t) -1;
606 ret->exclusive_max = (uint64_t) -1;
612 int btrfs_defrag_fd(int fd) {
617 if (fstat(fd, &st) < 0)
620 if (!S_ISREG(st.st_mode))
623 if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
629 int btrfs_defrag(const char *p) {
630 _cleanup_close_ int fd = -1;
632 fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
636 return btrfs_defrag_fd(fd);
639 int btrfs_quota_enable_fd(int fd, bool b) {
640 struct btrfs_ioctl_quota_ctl_args args = {
641 .cmd = b ? BTRFS_QUOTA_CTL_ENABLE : BTRFS_QUOTA_CTL_DISABLE,
647 r = btrfs_is_filesystem(fd);
653 if (ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args) < 0)
659 int btrfs_quota_enable(const char *path, bool b) {
660 _cleanup_close_ int fd = -1;
662 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
666 return btrfs_quota_enable_fd(fd, b);
669 int btrfs_quota_limit_fd(int fd, uint64_t referenced_max) {
670 struct btrfs_ioctl_qgroup_limit_args args = {
672 referenced_max == (uint64_t) -1 ? 0 :
673 referenced_max == 0 ? 1 : referenced_max,
674 .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER,
680 r = btrfs_is_filesystem(fd);
686 if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args) < 0)
692 int btrfs_quota_limit(const char *path, uint64_t referenced_max) {
693 _cleanup_close_ int fd = -1;
695 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
699 return btrfs_quota_limit_fd(fd, referenced_max);
702 int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
703 struct btrfs_ioctl_vol_args args = {};
704 _cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL;
705 _cleanup_close_ int loop_fd = -1, backing_fd = -1;
710 /* btrfs cannot handle file systems < 16M, hence use this as minimum */
711 if (new_size < 16*1024*1024)
712 new_size = 16*1024*1024;
714 r = btrfs_get_block_device_fd(fd, &dev);
720 if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0)
722 r = read_one_line_file(p, &backing);
727 if (isempty(backing) || !path_is_absolute(backing))
730 backing_fd = open(backing, O_RDWR|O_CLOEXEC|O_NOCTTY);
734 if (fstat(backing_fd, &st) < 0)
736 if (!S_ISREG(st.st_mode))
739 if (new_size == (uint64_t) st.st_size)
742 if (grow_only && new_size < (uint64_t) st.st_size)
745 if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0)
747 loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY);
751 if (snprintf(args.name, sizeof(args.name), "%" PRIu64, new_size) >= (int) sizeof(args.name))
754 if (new_size < (uint64_t) st.st_size) {
755 /* Decrease size: first decrease btrfs size, then shorten loopback */
756 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
760 if (ftruncate(backing_fd, new_size) < 0)
763 if (ioctl(loop_fd, LOOP_SET_CAPACITY, 0) < 0)
766 if (new_size > (uint64_t) st.st_size) {
767 /* Increase size: first enlarge loopback, then increase btrfs size */
768 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
772 /* Make sure the free disk space is correctly updated for both file systems */
774 (void) fsync(backing_fd);
779 int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) {
780 _cleanup_close_ int fd = -1;
782 fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
786 return btrfs_resize_loopback_fd(fd, new_size, grow_only);
789 static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol_id, bool recursive) {
790 struct btrfs_ioctl_search_args args = {
791 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
793 .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
794 .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
796 .key.min_type = BTRFS_ROOT_BACKREF_KEY,
797 .key.max_type = BTRFS_ROOT_BACKREF_KEY,
799 .key.min_transid = 0,
800 .key.max_transid = (uint64_t) -1,
803 struct btrfs_ioctl_vol_args vol_args = {};
804 _cleanup_close_ int subvol_fd = -1;
806 bool made_writable = false;
812 if (fstat(fd, &st) < 0)
815 if (!S_ISDIR(st.st_mode))
818 /* First, try to remove the subvolume. If it happens to be
819 * already empty, this will just work. */
820 strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
821 if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) >= 0)
823 if (!recursive || errno != ENOTEMPTY)
826 /* OK, the subvolume is not empty, let's look for child
827 * subvolumes, and remove them, first */
828 subvol_fd = openat(fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
832 if (subvol_id == 0) {
833 r = btrfs_subvol_get_id_fd(subvol_fd, &subvol_id);
838 args.key.min_offset = args.key.max_offset = subvol_id;
840 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
841 const struct btrfs_ioctl_search_header *sh;
844 args.key.nr_items = 256;
845 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
848 if (args.key.nr_items <= 0)
851 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
852 _cleanup_free_ char *p = NULL;
853 const struct btrfs_root_ref *ref;
854 struct btrfs_ioctl_ino_lookup_args ino_args;
856 btrfs_ioctl_search_args_set(&args, sh);
858 if (sh->type != BTRFS_ROOT_BACKREF_KEY)
860 if (sh->offset != subvol_id)
863 ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
865 p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
870 ino_args.treeid = subvol_id;
871 ino_args.objectid = htole64(ref->dirid);
873 if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
876 if (!made_writable) {
877 r = btrfs_subvol_set_read_only_fd(subvol_fd, false);
881 made_writable = true;
884 if (isempty(ino_args.name))
885 /* Subvolume is in the top-level
886 * directory of the subvolume. */
887 r = subvol_remove_children(subvol_fd, p, sh->objectid, recursive);
889 _cleanup_close_ int child_fd = -1;
891 /* Subvolume is somewhere further down,
892 * hence we need to open the
893 * containing directory first */
895 child_fd = openat(subvol_fd, ino_args.name, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
899 r = subvol_remove_children(child_fd, p, sh->objectid, recursive);
905 /* Increase search key by one, to read the next item, if we can. */
906 if (!btrfs_ioctl_search_args_inc(&args))
910 /* OK, the child subvolumes should all be gone now, let's try
911 * again to remove the subvolume */
912 if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) < 0)
918 int btrfs_subvol_remove(const char *path, bool recursive) {
919 _cleanup_close_ int fd = -1;
920 const char *subvolume;
925 r = extract_subvolume_name(path, &subvolume);
929 fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
933 return subvol_remove_children(fd, subvolume, 0, recursive);
936 int btrfs_subvol_remove_fd(int fd, const char *subvolume, bool recursive) {
937 return subvol_remove_children(fd, subvolume, 0, recursive);
940 static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolume, uint64_t subvol_id, BtrfsSnapshotFlags flags) {
942 struct btrfs_ioctl_search_args args = {
943 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
945 .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
946 .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
948 .key.min_type = BTRFS_ROOT_BACKREF_KEY,
949 .key.max_type = BTRFS_ROOT_BACKREF_KEY,
951 .key.min_transid = 0,
952 .key.max_transid = (uint64_t) -1,
955 struct btrfs_ioctl_vol_args_v2 vol_args = {
956 .flags = flags & BTRFS_SNAPSHOT_READ_ONLY ? BTRFS_SUBVOL_RDONLY : 0,
960 _cleanup_close_ int subvolume_fd = -1;
966 strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
967 vol_args.fd = old_fd;
969 if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &vol_args) < 0)
972 if (!(flags & BTRFS_SNAPSHOT_RECURSIVE))
975 if (subvol_id == 0) {
976 r = btrfs_subvol_get_id_fd(old_fd, &subvol_id);
981 args.key.min_offset = args.key.max_offset = subvol_id;
983 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
984 const struct btrfs_ioctl_search_header *sh;
987 args.key.nr_items = 256;
988 if (ioctl(old_fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
991 if (args.key.nr_items <= 0)
994 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
995 _cleanup_free_ char *p = NULL, *c = NULL, *np = NULL;
996 struct btrfs_ioctl_ino_lookup_args ino_args;
997 const struct btrfs_root_ref *ref;
998 _cleanup_close_ int old_child_fd = -1, new_child_fd = -1;
1000 btrfs_ioctl_search_args_set(&args, sh);
1002 if (sh->type != BTRFS_ROOT_BACKREF_KEY)
1004 if (sh->offset != subvol_id)
1007 ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
1009 p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
1014 ino_args.treeid = subvol_id;
1015 ino_args.objectid = htole64(ref->dirid);
1017 if (ioctl(old_fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
1020 /* The kernel returns an empty name if the
1021 * subvolume is in the top-level directory,
1022 * and otherwise appends a slash, so that we
1023 * can just concatenate easily here, without
1024 * adding a slash. */
1025 c = strappend(ino_args.name, p);
1029 old_child_fd = openat(old_fd, c, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1030 if (old_child_fd < 0)
1033 np = strjoin(subvolume, "/", ino_args.name, NULL);
1037 new_child_fd = openat(new_fd, np, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1038 if (new_child_fd < 0)
1041 if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
1042 /* If the snapshot is read-only we
1043 * need to mark it writable
1044 * temporarily, to put the subsnapshot
1047 if (subvolume_fd < 0) {
1048 subvolume_fd = openat(new_fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1049 if (subvolume_fd < 0)
1053 r = btrfs_subvol_set_read_only_fd(subvolume_fd, false);
1058 /* When btrfs clones the subvolumes, child
1059 * subvolumes appear as directories. Remove
1060 * them, so that we can create a new snapshot
1062 if (unlinkat(new_child_fd, p, AT_REMOVEDIR) < 0) {
1065 if (flags & BTRFS_SNAPSHOT_READ_ONLY)
1066 (void) btrfs_subvol_set_read_only_fd(subvolume_fd, true);
1071 r = subvol_snapshot_children(old_child_fd, new_child_fd, p, sh->objectid, flags & ~BTRFS_SNAPSHOT_FALLBACK_COPY);
1073 /* Restore the readonly flag */
1074 if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
1077 k = btrfs_subvol_set_read_only_fd(subvolume_fd, true);
1078 if (r >= 0 && k < 0)
1086 /* Increase search key by one, to read the next item, if we can. */
1087 if (!btrfs_ioctl_search_args_inc(&args))
1094 int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) {
1095 _cleanup_close_ int new_fd = -1;
1096 const char *subvolume;
1099 assert(old_fd >= 0);
1102 r = btrfs_is_subvol(old_fd);
1106 if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY))
1109 r = btrfs_subvol_make(new_path);
1113 r = copy_directory_fd(old_fd, new_path, true);
1115 btrfs_subvol_remove(new_path, false);
1119 if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
1120 r = btrfs_subvol_set_read_only(new_path, true);
1122 btrfs_subvol_remove(new_path, false);
1130 r = extract_subvolume_name(new_path, &subvolume);
1134 new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1138 return subvol_snapshot_children(old_fd, new_fd, subvolume, 0, flags);
1141 int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) {
1142 _cleanup_close_ int old_fd = -1;
1147 old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1151 return btrfs_subvol_snapshot_fd(old_fd, new_path, flags);