]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
mount-util: add helper that can bind mount submounts of one dir to another
authorLennart Poettering <lennart@poettering.net>
Fri, 23 Jun 2023 15:50:17 +0000 (17:50 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 23 Jun 2023 16:30:59 +0000 (18:30 +0200)
src/shared/mount-util.c
src/shared/mount-util.h
src/test/test-mount-util.c

index 657ab9d9fb9e5bff96bbb019eb1046e59c6f62b0..425840ffe914c9d20440656caabe53e90344529f 100644 (file)
@@ -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);
 }
index 7214cf1e52e61a9dce23952ce6cea12d9a06ae88..42a15b58008bbd5c6e9c5d66ee857c8b3b19237c 100644 (file)
@@ -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);
index 536819e0fc23cb9eff01c52c2e15396caf6c3c75..f0fc0f3e7383f1a4b7ad4fdc9dba7d17f8de9706 100644 (file)
@@ -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");