From: Lennart Poettering Date: Fri, 23 Jun 2023 15:50:17 +0000 (+0200) Subject: mount-util: add helper that can bind mount submounts of one dir to another X-Git-Tag: v254-rc1~126^2~2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=1b618bf1414a72b1b4c77a2762bc07fca47d572d;p=thirdparty%2Fsystemd.git mount-util: add helper that can bind mount submounts of one dir to another --- diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c index 657ab9d9fb9..425840ffe91 100644 --- a/src/shared/mount-util.c +++ b/src/shared/mount-util.c @@ -1251,7 +1251,11 @@ static void sub_mount_drop(SubMount *s, size_t n) { } } -static int get_sub_mounts(const char *prefix, SubMount **ret_mounts, size_t *ret_n_mounts) { +static int get_sub_mounts( + const char *prefix, + bool clone_tree, + SubMount **ret_mounts, + size_t *ret_n_mounts) { _cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL; _cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL; SubMount *mounts = NULL; @@ -1301,12 +1305,15 @@ static int get_sub_mounts(const char *prefix, SubMount **ret_mounts, size_t *ret continue; } - mount_fd = open_tree(AT_FDCWD, path, OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC | AT_RECURSIVE); + if (clone_tree) + mount_fd = open_tree(AT_FDCWD, path, OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC | AT_RECURSIVE); + else + mount_fd = open(path, O_CLOEXEC|O_PATH); if (mount_fd < 0) { if (errno == ENOENT) /* The path may be hidden by another over-mount or already unmounted. */ continue; - return log_debug_errno(errno, "Failed to open tree of mounted filesystem '%s': %m", path); + return log_debug_errno(errno, "Failed to open subtree of mounted filesystem '%s': %m", path); } p = strdup(path); @@ -1374,7 +1381,7 @@ int remount_and_move_sub_mounts( return mount_nofollow_verbose(LOG_DEBUG, what, where, type, flags, options); /* Get the list of sub-mounts and duplicate them. */ - r = get_sub_mounts(where, &mounts, &n); + r = get_sub_mounts(where, /* clone_tree= */ true, &mounts, &n); if (r < 0) return r; @@ -1390,6 +1397,57 @@ int remount_and_move_sub_mounts( return move_sub_mounts(mounts, n); } +int bind_mount_submounts( + const char *source, + const char *target) { + + SubMount *mounts = NULL; + size_t n = 0; + int ret = 0, r; + + /* Bind mounts all child mounts of 'source' to 'target'. Useful when setting up a new procfs instance + * with new mount options to copy the original submounts over. */ + + assert(source); + assert(target); + + CLEANUP_ARRAY(mounts, n, sub_mount_array_free); + + r = get_sub_mounts(source, /* clone_tree= */ false, &mounts, &n); + if (r < 0) + return r; + + FOREACH_ARRAY(m, mounts, n) { + _cleanup_free_ char *t = NULL; + const char *suffix; + + if (isempty(m->path)) + continue; + + assert_se(suffix = path_startswith(m->path, source)); + + t = path_join(target, suffix); + if (!t) + return -ENOMEM; + + r = path_is_mount_point(t, NULL, 0); + if (r < 0) { + log_debug_errno(r, "Failed to detect if '%s' already is a mount point, ignoring: %m", t); + continue; + } + if (r > 0) { + log_debug("Not bind mounting '%s' from '%s' to '%s', since there's already a mountpoint.", suffix, source, target); + continue; + } + + r = mount_follow_verbose(LOG_DEBUG, FORMAT_PROC_FD_PATH(m->mount_fd), t, NULL, MS_BIND|MS_REC, NULL); + if (r < 0 && ret == 0) + ret = r; + } + + return ret; +} + int remount_sysfs(const char *where) { return remount_and_move_sub_mounts("sysfs", where, "sysfs", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL); } diff --git a/src/shared/mount-util.h b/src/shared/mount-util.h index 7214cf1e52e..42a15b58008 100644 --- a/src/shared/mount-util.h +++ b/src/shared/mount-util.h @@ -132,6 +132,10 @@ int remount_and_move_sub_mounts( const char *options); int remount_sysfs(const char *where); +int bind_mount_submounts( + const char *source, + const char *target); + /* Creates a mount point (not parents) based on the source path or stat - ie, a file or a directory */ int make_mount_point_inode_from_stat(const struct stat *st, const char *dest, mode_t mode); int make_mount_point_inode_from_path(const char *source, const char *dest, mode_t mode); diff --git a/src/test/test-mount-util.c b/src/test/test-mount-util.c index 536819e0fc2..f0fc0f3e738 100644 --- a/src/test/test-mount-util.c +++ b/src/test/test-mount-util.c @@ -568,6 +568,77 @@ TEST(fd_make_mount_point) { } } +TEST(bind_mount_submounts) { + _cleanup_(rmdir_and_freep) char *a = NULL, *b = NULL; + _cleanup_free_ char *x = NULL; + int r; + + assert_se(mkdtemp_malloc(NULL, &a) >= 0); + r = mount_nofollow_verbose(LOG_INFO, "tmpfs", a, "tmpfs", 0, NULL); + if (r < 0 && ERRNO_IS_PRIVILEGE(r)) { + (void) log_tests_skipped("Skipping bind_mount_submounts() test, lacking privileges"); + return; + } + assert_se(r >= 0); + + assert_se(x = path_join(a, "foo")); + assert_se(touch(x) >= 0); + free(x); + + assert_se(x = path_join(a, "x")); + assert_se(mkdir(x, 0755) >= 0); + assert_se(mount_nofollow_verbose(LOG_INFO, "tmpfs", x, "tmpfs", 0, NULL) >= 0); + free(x); + + assert_se(x = path_join(a, "x/xx")); + assert_se(touch(x) >= 0); + free(x); + + assert_se(x = path_join(a, "y")); + assert_se(mkdir(x, 0755) >= 0); + assert_se(mount_nofollow_verbose(LOG_INFO, "tmpfs", x, "tmpfs", 0, NULL) >= 0); + free(x); + + assert_se(x = path_join(a, "y/yy")); + assert_se(touch(x) >= 0); + free(x); + + assert_se(mkdtemp_malloc(NULL, &b) >= 0); + assert_se(mount_nofollow_verbose(LOG_INFO, "tmpfs", b, "tmpfs", 0, NULL) >= 0); + + assert_se(x = path_join(b, "x")); + assert_se(mkdir(x, 0755) >= 0); + free(x); + + assert_se(x = path_join(b, "y")); + assert_se(mkdir(x, 0755) >= 0); + free(x); + + assert_se(bind_mount_submounts(a, b) >= 0); + + assert_se(x = path_join(b, "foo")); + assert_se(access(x, F_OK) < 0 && errno == ENOENT); + free(x); + + assert_se(x = path_join(b, "x/xx")); + assert_se(access(x, F_OK) >= 0); + free(x); + + assert_se(x = path_join(b, "y/yy")); + assert_se(access(x, F_OK) >= 0); + free(x); + + assert_se(x = path_join(b, "x")); + assert_se(path_is_mount_point(x, NULL, 0) > 0); + free(x); + + assert_se(x = path_join(b, "y")); + assert_se(path_is_mount_point(x, NULL, 0) > 0); + + assert_se(umount_recursive(a, 0) >= 0); + assert_se(umount_recursive(b, 0) >= 0); +} + static int intro(void) { /* Create a dummy network interface for testing remount_sysfs(). */ (void) system("ip link add dummy-test-mnt type dummy");