1 /* SPDX-License-Identifier: LGPL-2.1+ */
13 #include <sys/ioctl.h>
15 #include <sys/types.h>
23 #include "memory_utils.h"
29 #include "include/strlcpy.h"
33 #include "include/strlcat.h"
36 lxc_log_define(btrfs
, lxc
);
39 * Return the full path of objid under dirid. Let's say dirid is
40 * /lxc/c1/rootfs, and objid is /lxc/c1/rootfs/a/b/c. Then we will
41 * return a/b/c. If instead objid is for /lxc/c1/rootfs/a, we will
44 char *get_btrfs_subvol_path(int fd
, u64 dir_id
, u64 objid
, char *name
,
47 struct btrfs_ioctl_ino_lookup_args args
;
52 memset(&args
, 0, sizeof(args
));
54 args
.objectid
= objid
;
56 ret
= ioctl(fd
, BTRFS_IOC_INO_LOOKUP
, &args
);
58 SYSERROR("Failed to lookup path for %llu %llu %s",
59 (unsigned long long) dir_id
,
60 (unsigned long long) objid
,
64 INFO("Got path for %llu %llu - %s",
65 (unsigned long long) objid
, (unsigned long long) dir_id
,
70 * we're in a subdirectory of ref_tree, the kernel ioctl
71 * puts a / in there for us
73 len
= strlen(args
.name
) + name_len
+ 2;
74 retpath
= malloc(len
);
78 (void)strlcpy(retpath
, args
.name
, len
);
79 (void)strlcat(retpath
, "/", len
);
81 retlen
= strlcat(retpath
, name
, len
);
83 ERROR("Failed to append name - %s", name
);
88 /* we're at the root of ref_tree */
90 retpath
= malloc(len
);
96 retlen
= strlcat(retpath
, name
, len
);
98 ERROR("Failed to append name - %s", name
);
107 int btrfs_list_get_path_rootid(int fd
, u64
*treeid
)
110 struct btrfs_ioctl_ino_lookup_args args
;
112 memset(&args
, 0, sizeof(args
));
113 args
.objectid
= BTRFS_FIRST_FREE_OBJECTID
;
115 ret
= ioctl(fd
, BTRFS_IOC_INO_LOOKUP
, &args
);
117 SYSWARN("Can't perform the search");
121 *treeid
= args
.treeid
;
125 bool is_btrfs_fs(const char *path
)
128 struct btrfs_ioctl_space_args sargs
;
130 /* Make sure this is a btrfs filesystem. */
131 fd
= open(path
, O_RDONLY
);
135 sargs
.space_slots
= 0;
136 sargs
.total_spaces
= 0;
137 ret
= ioctl(fd
, BTRFS_IOC_SPACE_INFO
, &sargs
);
146 * Taken from btrfs toolsuite. Test if path is a subvolume.
147 * return 0; path exists but it is not a subvolume
148 * return 1; path exists and it is a subvolume
151 int is_btrfs_subvol(const char *path
)
157 ret
= stat(path
, &st
);
161 if (st
.st_ino
!= BTRFS_FIRST_FREE_OBJECTID
|| !S_ISDIR(st
.st_mode
))
164 ret
= statfs(path
, &stfs
);
168 return stfs
.f_type
== BTRFS_SUPER_MAGIC
;
171 bool btrfs_detect(const char *path
)
176 if (!strncmp(path
, "btrfs:", 6))
179 if (!is_btrfs_fs(path
))
182 /* make sure it's a subvolume */
183 ret
= stat(path
, &st
);
187 if (st
.st_ino
== 256 && S_ISDIR(st
.st_mode
))
193 int btrfs_mount(struct lxc_storage
*bdev
)
195 unsigned long mntflags
= 0;
196 char *mntdata
= NULL
;
200 if (strcmp(bdev
->type
, "btrfs"))
203 if (!bdev
->src
|| !bdev
->dest
)
206 if (parse_mntopts(bdev
->mntopts
, &mntflags
, &mntdata
) < 0) {
211 src
= lxc_storage_get_path(bdev
->src
, "btrfs");
213 ret
= mount(src
, bdev
->dest
, "bind", MS_BIND
| MS_REC
| mntflags
, mntdata
);
218 int btrfs_umount(struct lxc_storage
*bdev
)
220 if (strcmp(bdev
->type
, "btrfs"))
223 if (!bdev
->src
|| !bdev
->dest
)
226 return umount(bdev
->dest
);
229 static int btrfs_subvolume_create(const char *path
)
231 int ret
, saved_errno
;
233 struct btrfs_ioctl_vol_args args
;
237 newfull
= strdup(path
);
243 p
= strrchr(newfull
, '/');
251 fd
= open(newfull
, O_RDONLY
);
257 memset(&args
, 0, sizeof(args
));
258 retlen
= strlcpy(args
.name
, p
+ 1, BTRFS_SUBVOL_NAME_MAX
);
259 if (retlen
>= BTRFS_SUBVOL_NAME_MAX
) {
265 ret
= ioctl(fd
, BTRFS_IOC_SUBVOL_CREATE
, &args
);
274 int btrfs_same_fs(const char *orig
, const char *new)
276 int fd_orig
= -1, fd_new
= -1, ret
= -1;
277 struct btrfs_ioctl_fs_info_args orig_args
, new_args
;
279 fd_orig
= open(orig
, O_RDONLY
);
281 SYSERROR("Failed to open original rootfs %s", orig
);
285 ret
= ioctl(fd_orig
, BTRFS_IOC_FS_INFO
, &orig_args
);
287 SYSERROR("BTRFS_IOC_FS_INFO %s", orig
);
291 fd_new
= open(new, O_RDONLY
);
293 SYSERROR("Failed to open new container dir %s", new);
298 ret
= ioctl(fd_new
, BTRFS_IOC_FS_INFO
, &new_args
);
300 SYSERROR("BTRFS_IOC_FS_INFO %s", new);
304 if (strncmp(orig_args
.fsid
, new_args
.fsid
, BTRFS_FSID_SIZE
) != 0) {
321 int btrfs_snapshot(const char *orig
, const char *new)
324 struct btrfs_ioctl_vol_args_v2 args
;
325 char *newdir
, *newname
;
326 char *newfull
= NULL
;
327 int saved_errno
= -1;
328 int fd
= -1, fddst
= -1, ret
= -1;
330 newfull
= strdup(new);
334 ret
= rmdir(newfull
);
335 if (ret
< 0 && errno
!= ENOENT
)
338 newname
= basename(newfull
);
339 fd
= open(orig
, O_RDONLY
);
343 newdir
= dirname(newfull
);
344 fddst
= open(newdir
, O_RDONLY
);
348 memset(&args
, 0, sizeof(args
));
350 retlen
= strlcpy(args
.name
, newname
, BTRFS_SUBVOL_NAME_MAX
);
351 if (retlen
>= BTRFS_SUBVOL_NAME_MAX
)
354 ret
= ioctl(fddst
, BTRFS_IOC_SNAP_CREATE_V2
, &args
);
366 if (saved_errno
>= 0)
372 int btrfs_snapshot_wrapper(void *data
)
375 struct rsync_data_char
*arg
= data
;
377 (void)lxc_setgroups(0, NULL
);
380 ERROR("Failed to setgid to 0");
385 ERROR("Failed to setuid to 0");
389 src
= lxc_storage_get_path(arg
->src
, "btrfs");
390 return btrfs_snapshot(src
, arg
->dest
);
393 int btrfs_clonepaths(struct lxc_storage
*orig
, struct lxc_storage
*new,
394 const char *oldname
, const char *cname
,
395 const char *oldpath
, const char *lxcpath
, int snap
,
396 uint64_t newsize
, struct lxc_conf
*conf
)
400 if (!orig
->dest
|| !orig
->src
)
403 if (strcmp(orig
->type
, "btrfs") && snap
) {
404 ERROR("btrfs snapshot from %s backing store is not supported",
409 new->src
= lxc_string_join(
411 (const char *[]){"btrfs:", *lxcpath
!= '/' ? lxcpath
: ++lxcpath
,
412 cname
, "rootfs", NULL
},
415 ERROR("Failed to create new rootfs path");
418 TRACE("Constructed new rootfs path \"%s\"", new->src
);
420 src
= lxc_storage_get_path(new->src
, "btrfs");
421 new->dest
= strdup(src
);
423 ERROR("Failed to duplicate string \"%s\"", src
);
428 new->mntopts
= strdup(orig
->mntopts
);
430 ERROR("Failed to duplicate string \"%s\"",
439 bool btrfs_create_clone(struct lxc_conf
*conf
, struct lxc_storage
*orig
,
440 struct lxc_storage
*new, uint64_t newsize
)
443 struct rsync_data data
= {0, 0};
444 char cmd_output
[PATH_MAX
] = {0};
446 ret
= rmdir(new->dest
);
447 if (ret
< 0 && errno
!= ENOENT
)
450 ret
= btrfs_subvolume_create(new->dest
);
452 SYSERROR("Failed to create btrfs subvolume \"%s\"", new->dest
);
456 /* rsync the contents from source to target */
460 if (am_guest_unpriv()) {
461 ret
= userns_exec_full(conf
, lxc_storage_rsync_exec_wrapper
,
462 &data
, "lxc_storage_rsync_exec_wrapper");
464 ERROR("Failed to rsync from \"%s\" into \"%s\"",
465 orig
->dest
, new->dest
);
472 ret
= run_command(cmd_output
, sizeof(cmd_output
),
473 lxc_storage_rsync_exec_wrapper
, (void *)&data
);
475 ERROR("Failed to rsync from \"%s\" into \"%s\": %s", orig
->dest
,
476 new->dest
, cmd_output
);
483 bool btrfs_create_snapshot(struct lxc_conf
*conf
, struct lxc_storage
*orig
,
484 struct lxc_storage
*new, uint64_t newsize
)
488 ret
= rmdir(new->dest
);
489 if (ret
< 0 && errno
!= ENOENT
)
492 if (am_guest_unpriv()) {
493 struct rsync_data_char args
;
495 args
.src
= orig
->src
;
496 args
.dest
= new->dest
;
498 ret
= userns_exec_1(conf
, btrfs_snapshot_wrapper
, &args
,
499 "btrfs_snapshot_wrapper");
501 ERROR("Failed to run \"btrfs_snapshot_wrapper\"");
505 TRACE("Created btrfs snapshot \"%s\" from \"%s\"", new->dest
,
510 ret
= btrfs_snapshot(orig
->src
, new->dest
);
512 SYSERROR("Failed to create btrfs snapshot \"%s\" from \"%s\"",
513 new->dest
, orig
->dest
);
517 TRACE("Created btrfs snapshot \"%s\" from \"%s\"", new->dest
, orig
->dest
);
521 static int btrfs_do_destroy_subvol(const char *path
)
525 struct btrfs_ioctl_vol_args args
;
526 char *p
, *newfull
= strdup(path
);
529 ERROR("Out of memory");
533 p
= strrchr(newfull
, '/');
535 ERROR("Invalid path: %s", path
);
541 fd
= open(newfull
, O_RDONLY
);
543 SYSERROR("Failed to open %s", newfull
);
548 memset(&args
, 0, sizeof(args
));
549 retlen
= strlcpy(args
.name
, p
+1, BTRFS_SUBVOL_NAME_MAX
);
550 if (retlen
>= BTRFS_SUBVOL_NAME_MAX
) {
556 ret
= ioctl(fd
, BTRFS_IOC_SNAP_DESTROY
, &args
);
557 INFO("IOCTL for destroying snapshot returned %d for %s", ret
, path
);
558 if (ret
< 0 && errno
== EPERM
)
559 ERROR("Is the rootfs mounted with -o user_subvol_rm_allowed?");
566 static int get_btrfs_tree_idx(struct my_btrfs_tree
*tree
, u64 id
)
573 for (i
= 0; i
< tree
->num
; i
++)
574 if (tree
->nodes
[i
].objid
== id
)
580 static struct my_btrfs_tree
*create_my_btrfs_tree(u64 id
, const char *path
,
583 struct my_btrfs_tree
*tree
;
585 tree
= malloc(sizeof(struct my_btrfs_tree
));
589 tree
->nodes
= malloc(sizeof(struct mytree_node
));
596 tree
->nodes
[0].dirname
= NULL
;
597 tree
->nodes
[0].name
= strdup(path
);
598 if (!tree
->nodes
[0].name
) {
604 tree
->nodes
[0].parentid
= 0;
605 tree
->nodes
[0].objid
= id
;
609 static bool update_tree_node(struct mytree_node
*n
, u64 id
, u64 parent
,
610 char *name
, u16 name_len
, char *dirname
)
616 n
->parentid
= parent
;
619 n
->name
= malloc(name_len
+ 1);
623 (void)strlcpy(n
->name
, name
, name_len
+ 1);
629 len
= strlen(dirname
);
630 n
->dirname
= malloc(len
+ 1);
636 (void)strlcpy(n
->dirname
, dirname
, len
+ 1);
642 static bool add_btrfs_tree_node(struct my_btrfs_tree
*tree
, u64 id
, u64 parent
,
643 char *name
, u16 name_len
, char *dirname
)
645 struct mytree_node
*tmp
;
647 int i
= get_btrfs_tree_idx(tree
, id
);
649 return update_tree_node(&tree
->nodes
[i
], id
, parent
, name
,
652 tmp
= realloc(tree
->nodes
, (tree
->num
+1) * sizeof(struct mytree_node
));
657 memset(&tree
->nodes
[tree
->num
], 0, sizeof(struct mytree_node
));
659 if (!update_tree_node(&tree
->nodes
[tree
->num
], id
, parent
, name
,
667 static void free_btrfs_tree(struct my_btrfs_tree
*tree
)
674 for (i
= 0; i
< tree
->num
; i
++) {
675 free(tree
->nodes
[i
].name
);
676 free(tree
->nodes
[i
].dirname
);
684 * Given a @tree of subvolumes under @path, ask btrfs to remove each
687 static bool do_remove_btrfs_children(struct my_btrfs_tree
*tree
, u64 root_id
,
694 for (i
= 0; i
< tree
->num
; i
++) {
695 if (tree
->nodes
[i
].parentid
== root_id
) {
696 if (!tree
->nodes
[i
].dirname
) {
697 WARN("Odd condition: child objid with no name under %s", path
);
701 len
= strlen(path
) + strlen(tree
->nodes
[i
].dirname
) + 2;
702 newpath
= malloc(len
);
704 ERROR("Out of memory");
708 ret
= snprintf(newpath
, len
, "%s/%s", path
, tree
->nodes
[i
].dirname
);
709 if (ret
< 0 || ret
>= len
) {
714 if (!do_remove_btrfs_children(tree
, tree
->nodes
[i
].objid
, newpath
)) {
715 ERROR("Failed to prune %s", tree
->nodes
[i
].name
);
720 if (btrfs_do_destroy_subvol(newpath
) != 0) {
721 ERROR("Failed to remove %s", newpath
);
733 static int btrfs_recursive_destroy(const char *path
)
737 struct btrfs_ioctl_search_args args
;
738 struct btrfs_ioctl_search_key
*sk
= &args
.key
;
739 struct btrfs_ioctl_search_header sh
;
740 struct btrfs_root_ref
*ref
;
741 struct my_btrfs_tree
*tree
;
743 unsigned long off
= 0;
747 fd
= open(path
, O_RDONLY
);
749 ERROR("Failed to open %s", path
);
753 if (btrfs_list_get_path_rootid(fd
, &root_id
)) {
756 if (e
== EPERM
|| e
== EACCES
) {
757 WARN("Will simply try removing");
764 tree
= create_my_btrfs_tree(root_id
, path
, strlen(path
));
766 ERROR("Out of memory");
771 /* Walk all subvols looking for any under this id */
772 memset(&args
, 0, sizeof(args
));
774 /* search in the tree of tree roots */
776 sk
->max_type
= BTRFS_ROOT_REF_KEY
;
777 sk
->min_type
= BTRFS_ROOT_ITEM_KEY
;
778 sk
->min_objectid
= 0;
779 sk
->max_objectid
= (u64
)-1;
780 sk
->max_offset
= (u64
)-1;
782 sk
->max_transid
= (u64
)-1;
786 ret
= ioctl(fd
, BTRFS_IOC_TREE_SEARCH
, &args
);
790 free_btrfs_tree(tree
);
791 if (e
== EPERM
|| e
== EACCES
) {
792 WARN("Can't perform the search under %s. "
793 "Will simply try removing", path
);
797 ERROR("Can't perform the search under %s", path
);
801 if (sk
->nr_items
== 0)
805 for (i
= 0; i
< sk
->nr_items
; i
++) {
806 memcpy(&sh
, args
.buf
+ off
, sizeof(sh
));
810 * A backref key with the name and dirid of the parent
811 * comes followed by the root ref key which has the
812 * name of the child subvol in question.
814 if (sh
.objectid
!= root_id
&& sh
.type
== BTRFS_ROOT_BACKREF_KEY
) {
815 __do_free
char *name
= NULL
, *tmppath
= NULL
;
818 ref
= (struct btrfs_root_ref
*)(args
.buf
+ off
);
819 name_len
= btrfs_stack_root_ref_name_len(ref
);
820 tmp
= (char *)(ref
+ 1);
822 name
= malloc(name_len
+ 1);
824 ERROR("Out of memory");
825 free_btrfs_tree(tree
);
829 memcpy(name
, tmp
, name_len
);
830 name
[name_len
] = '\0';
831 dir_id
= btrfs_stack_root_ref_dirid(ref
);
832 tmppath
= get_btrfs_subvol_path(fd
, sh
.offset
,
833 dir_id
, name
, name_len
);
835 if (!add_btrfs_tree_node(tree
, sh
.objectid
,
837 name_len
, tmppath
)) {
838 ERROR("Out of memory");
839 free_btrfs_tree(tree
);
848 * record the mins in sk so we can make sure the
849 * next search doesn't repeat this root
851 sk
->min_objectid
= sh
.objectid
;
852 sk
->min_type
= sh
.type
;
853 sk
->min_offset
= sh
.offset
;
864 if (sk
->min_type
> BTRFS_ROOT_BACKREF_KEY
) {
865 sk
->min_type
= BTRFS_ROOT_ITEM_KEY
;
871 if (sk
->min_objectid
>= sk
->max_objectid
)
877 /* now actually remove them */
878 if (!do_remove_btrfs_children(tree
, root_id
, path
)) {
879 free_btrfs_tree(tree
);
880 ERROR("Failed to prune");
884 free_btrfs_tree(tree
);
886 /* All child subvols have been removed, now remove this one */
888 return btrfs_do_destroy_subvol(path
);
891 bool btrfs_try_remove_subvol(const char *path
)
893 if (!btrfs_detect(path
))
896 return btrfs_recursive_destroy(path
) == 0;
899 int btrfs_destroy(struct lxc_storage
*orig
)
903 src
= lxc_storage_get_path(orig
->src
, "btrfs");
905 return btrfs_recursive_destroy(src
);
908 int btrfs_create(struct lxc_storage
*bdev
, const char *dest
, const char *n
,
909 struct bdev_specs
*specs
, const struct lxc_conf
*conf
)
915 len
= strlen(dest
) + 1;
916 /* strlen("btrfs:") */
919 bdev
->src
= malloc(len
);
921 ERROR("Failed to allocate memory");
925 ret
= snprintf(bdev
->src
, len
, "btrfs:%s", dest
);
926 if (ret
< 0 || (size_t)ret
>= len
) {
927 ERROR("Failed to create string");
931 bdev
->dest
= strdup(dest
);
933 ERROR("Failed to duplicate string \"%s\"", dest
);
937 ret
= btrfs_subvolume_create(bdev
->dest
);
939 SYSERROR("Failed to create btrfs subvolume \"%s\"", bdev
->dest
);