]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
nspawn: support pivot_root()
authorChristian Brauner <brauner@kernel.org>
Thu, 24 Nov 2022 09:45:24 +0000 (10:45 +0100)
committerChristian Brauner (Microsoft) <brauner@kernel.org>
Mon, 5 Dec 2022 17:34:25 +0000 (18:34 +0100)
In order to support pivot_root() we need to move mount propagation
changes after the pivot_root(). While MS_MOVE requires the source mount
to not be a shared mount pivot_root() also requires the target mount to
not be a shared mount. This guarantees that pivot_root() doesn't leak
any mounts.

Signed-off-by: Christian Brauner (Microsoft) <brauner@kernel.org>
src/core/namespace.c
src/nspawn/nspawn.c
src/shared/mount-util.c
src/shared/mount-util.h

index c0d0cc9715f3aec892dc07429c0a88f703bd65e1..c9c2132b8ad3796af3320bba866378979adcba3b 100644 (file)
@@ -2486,7 +2486,7 @@ int setup_namespace(
                 goto finish;
 
         /* MS_MOVE does not work on MS_SHARED so the remount MS_SHARED will be done later */
-        r = mount_pivot_root(root);
+        r = mount_switch_root(root, MOUNT_ATTR_PROPAGATION_INHERIT);
         if (r == -EINVAL && root_directory) {
                 /* If we are using root_directory and we don't have privileges (ie: user manager in a user
                  * namespace) and the root_directory is already a mount point in the parent namespace,
@@ -2496,7 +2496,7 @@ int setup_namespace(
                 r = mount_nofollow_verbose(LOG_DEBUG, root, root, NULL, MS_BIND|MS_REC, NULL);
                 if (r < 0)
                         goto finish;
-                r = mount_pivot_root(root);
+                r = mount_switch_root(root, MOUNT_ATTR_PROPAGATION_INHERIT);
         }
         if (r < 0) {
                 log_debug_errno(r, "Failed to mount root with MS_MOVE: %m");
index d7b636209eb9e32203aa4184c7e2a1b20384bd23..5844674d95f426acf5694ed57d1fa5da84f6b1d5 100644 (file)
@@ -3858,19 +3858,6 @@ static int outer_child(
                 unified_cgroup_hierarchy_socket = safe_close(unified_cgroup_hierarchy_socket);
         }
 
-        /* Mark everything as shared so our mounts get propagated down. This is required to make new bind
-         * mounts available in systemd services inside the container that create a new mount namespace.  See
-         * https://github.com/systemd/systemd/issues/3860 Further submounts (such as /dev) done after this
-         * will inherit the shared propagation mode.
-         *
-         * IMPORTANT: Do not overmount the root directory anymore from now on to enable moving the root
-         * directory mount to root later on.
-         * https://github.com/systemd/systemd/issues/3847#issuecomment-562735251
-         */
-        r = mount_nofollow_verbose(LOG_ERR, NULL, directory, NULL, MS_SHARED|MS_REC, NULL);
-        if (r < 0)
-                return r;
-
         r = recursive_chown(directory, arg_uid_shift, arg_uid_range);
         if (r < 0)
                 return r;
@@ -3974,7 +3961,16 @@ static int outer_child(
                         return r;
         }
 
-        r = mount_move_root(directory);
+        /* Mark everything as shared so our mounts get propagated down. This is required to make new bind
+         * mounts available in systemd services inside the container that create a new mount namespace.  See
+         * https://github.com/systemd/systemd/issues/3860 Further submounts (such as /dev) done after this
+         * will inherit the shared propagation mode.
+         *
+         * IMPORTANT: Do not overmount the root directory anymore from now on to enable moving the root
+         * directory mount to root later on.
+         * https://github.com/systemd/systemd/issues/3847#issuecomment-562735251
+         */
+        r = mount_switch_root(directory, MOUNT_ATTR_PROPAGATION_SHARED);
         if (r < 0)
                 return log_error_errno(r, "Failed to move root directory: %m");
 
index 681d698800b1ab8477f9093b3c8a7eddd81c2787..adb6b6dd279aa15d3d13709e03d259399b16a8af 100644 (file)
@@ -36,6 +36,7 @@
 #include "set.h"
 #include "stat-util.h"
 #include "stdio-util.h"
+#include "string-table.h"
 #include "string-util.h"
 #include "strv.h"
 #include "tmpfile-util.h"
@@ -475,47 +476,41 @@ int bind_remount_one_with_mountinfo(
         return 0;
 }
 
-int mount_move_root(const char *path) {
-        assert(path);
+static const char *const mount_attr_propagation_type_table[_MOUNT_ATTR_PROPAGATION_TYPE_MAX] = {
+        [MOUNT_ATTR_PROPAGATION_INHERIT]   = "inherited",
+        [MOUNT_ATTR_PROPAGATION_PRIVATE]   = "private",
+        [MOUNT_ATTR_PROPAGATION_DEPENDENT] = "dependent",
+        [MOUNT_ATTR_PROPAGATION_SHARED]    = "shared",
+};
 
-        if (chdir(path) < 0)
-                return -errno;
+DEFINE_STRING_TABLE_LOOKUP(mount_attr_propagation_type, MountAttrPropagationType);
 
-        if (mount(path, "/", NULL, MS_MOVE, NULL) < 0)
-                return -errno;
-
-        if (chroot(".") < 0)
-                return -errno;
-
-        return RET_NERRNO(chdir("/"));
+unsigned int mount_attr_propagation_type_to_flag(MountAttrPropagationType t) {
+        switch (t) {
+        case MOUNT_ATTR_PROPAGATION_INHERIT:
+                return 0;
+        case MOUNT_ATTR_PROPAGATION_PRIVATE:
+                return MS_PRIVATE;
+        case MOUNT_ATTR_PROPAGATION_DEPENDENT:
+                return MS_SLAVE;
+        case MOUNT_ATTR_PROPAGATION_SHARED:
+                return MS_SHARED;
+        default:
+                assert_not_reached();
+        }
 }
 
-int mount_pivot_root(const char *path) {
-        _cleanup_close_ int fd_oldroot = -EBADF, fd_newroot = -EBADF;
-
-        assert(path);
-
-        /* pivot_root() isn't currently supported in the initramfs. */
-        if (in_initrd())
-                return mount_move_root(path);
+static inline int mount_switch_root_pivot(const char *path, int fd_newroot) {
+        _cleanup_close_ int fd_oldroot = -EBADF;
 
         fd_oldroot = open("/", O_PATH|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
         if (fd_oldroot < 0)
                 return log_debug_errno(errno, "Failed to open old rootfs");
 
-        fd_newroot = open(path, O_PATH|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
-        if (fd_newroot < 0)
-                return log_debug_errno(errno, "Failed to open new rootfs '%s': %m", path);
-
-        /* Change into the new rootfs. */
-        if (fchdir(fd_newroot) < 0)
-                return log_debug_errno(errno, "Failed to change into new rootfs '%s': %m", path);
-
         /* Let the kernel tuck the new root under the old one. */
         if (pivot_root(".", ".") < 0)
                 return log_debug_errno(errno, "Failed to pivot root to new rootfs '%s': %m", path);
 
-
         /* At this point the new root is tucked under the old root. If we want
          * to unmount it we cannot be fchdir()ed into it. So escape back to the
          * old root. */
@@ -535,6 +530,52 @@ int mount_pivot_root(const char *path) {
         return 0;
 }
 
+static inline int mount_switch_root_move(const char *path) {
+        if (mount(path, "/", NULL, MS_MOVE, NULL) < 0)
+                return log_debug_errno(errno, "Failed to move new rootfs '%s': %m", path);
+
+        if (chroot(".") < 0)
+                return log_debug_errno(errno, "Failed to chroot to new rootfs '%s': %m", path);
+
+        if (chdir("/"))
+                return log_debug_errno(errno, "Failed to chdir to new rootfs '%s': %m", path);
+
+        return 0;
+}
+
+int mount_switch_root(const char *path, MountAttrPropagationType type) {
+        int r;
+        _cleanup_close_ int fd_newroot = -EBADF;
+        unsigned int flags;
+
+        assert(path);
+
+        fd_newroot = open(path, O_PATH|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
+        if (fd_newroot < 0)
+                return log_debug_errno(errno, "Failed to open new rootfs '%s': %m", path);
+
+        /* Change into the new rootfs. */
+        if (fchdir(fd_newroot) < 0)
+                return log_debug_errno(errno, "Failed to change into new rootfs '%s': %m", path);
+
+        r = mount_switch_root_pivot(path, fd_newroot);
+        if (r < 0) {
+                /* Failed to pivot_root() fallback to MS_MOVE. For example, this may happen if the
+                 * rootfs is an initramfs in which case pivot_root() isn't supported. */
+                log_debug_errno(r, "Failed to pivot into new rootfs '%s': %m", path);
+                r = mount_switch_root_move(path);
+        }
+        if (r < 0)
+                return log_debug_errno(r, "Failed to switch to new rootfs '%s': %m", path);
+
+        /* Finally, let's establish the requested propagation type. */
+        flags = mount_attr_propagation_type_to_flag(type);
+        if ((flags != 0) && mount(NULL, ".", NULL, flags|MS_REC, 0) < 0)
+                return log_debug_errno(errno, "Failed to turn new rootfs '%s' into %s mount: %m",
+                                       mount_attr_propagation_type_to_string(type), path);
+
+        return 0;
+}
 
 int repeat_unmount(const char *path, int flags) {
         bool done = false;
index 29b9ed02f7ca5efda2db43b58caa09469805a581..56b1c3669c8964fdc36a2d10bf62610daf6aa3db 100644 (file)
 #include "errno-util.h"
 #include "macro.h"
 
+typedef enum MountAttrPropagationType {
+        MOUNT_ATTR_PROPAGATION_INHERIT,   /* no special MS_* propagation flags */
+        MOUNT_ATTR_PROPAGATION_PRIVATE,   /* MS_PRIVATE */
+        MOUNT_ATTR_PROPAGATION_DEPENDENT, /* MS_SLAVE */
+        MOUNT_ATTR_PROPAGATION_SHARED,    /* MS_SHARE */
+
+        _MOUNT_ATTR_PROPAGATION_TYPE_MAX,
+        _MOUNT_ATTR_PROPAGATION_TYPE_INVALID = -EINVAL,
+} MountAttrPropagationType;
+
+const char* mount_attr_propagation_type_to_string(MountAttrPropagationType t) _const_;
+MountAttrPropagationType mount_attr_propagation_type_from_string(const char *s) _pure_;
+unsigned int mount_attr_propagation_type_to_flag(MountAttrPropagationType t);
+
 /* The limit used for /dev itself. 4MB should be enough since device nodes and symlinks don't
  * consume any space and udev isn't supposed to create regular file either. There's no limit on the
  * max number of inodes since such limit is hard to guess especially on large storage array
@@ -54,8 +68,7 @@ static inline int bind_remount_recursive(const char *prefix, unsigned long new_f
 
 int bind_remount_one_with_mountinfo(const char *path, unsigned long new_flags, unsigned long flags_mask, FILE *proc_self_mountinfo);
 
-int mount_move_root(const char *path);
-int mount_pivot_root(const char *path);
+int mount_switch_root(const char *path, MountAttrPropagationType type);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(FILE*, endmntent, NULL);
 #define _cleanup_endmntent_ _cleanup_(endmntentp)