]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
nspawn: rework how /run/host/ is set up
authorLennart Poettering <lennart@poettering.net>
Thu, 23 Jul 2020 14:49:13 +0000 (16:49 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 23 Jul 2020 16:47:38 +0000 (18:47 +0200)
Let's find the right os-release file on the host side, and only mount
the one that matters, i.e. /etc/os-release if it exists and
/usr/lib/os-release otherwise. Use the fixed path /run/host/os-release
for that.

Let's also mount /run/host as a bind mount on itself before we set up
/run/host, and let's mount it MS_RDONLY after we are done, so that it
remains immutable as a whole.

src/nspawn/nspawn-mount.c
src/nspawn/nspawn-mount.h
test/units/testsuite-13.sh

index 4687ac4c18d88cde8268f2879666b0d8826da9db..a48c7f62da2f54109967336e8ff04e8fbfd4a8f4 100644 (file)
@@ -563,15 +563,16 @@ int mount_all(const char *dest,
                   MOUNT_FATAL|MOUNT_MKDIR },
                 { "tmpfs",                  "/run",                         "tmpfs", "mode=755" TMPFS_LIMITS_RUN,      MS_NOSUID|MS_NODEV|MS_STRICTATIME,
                   MOUNT_FATAL|MOUNT_MKDIR },
-                { "/usr/lib/os-release",    "/run/host/usr/lib/os-release", NULL,    NULL,                             MS_BIND,
-                  MOUNT_FATAL|MOUNT_MKDIR|MOUNT_TOUCH }, /* As per kernel interface requirements, bind mount first (creating mount points) and make read-only later */
-                { NULL,                     "/run/host/usr/lib/os-release", NULL,    NULL,                             MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT,
-                  0 },
-                { "/etc/os-release",        "/run/host/etc/os-release",     NULL,    NULL,                             MS_BIND,
-                  MOUNT_MKDIR|MOUNT_TOUCH },
-                { NULL,                     "/run/host/etc/os-release",     NULL,    NULL,                             MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT,
-                  0 },
-
+                { "/run/host",              "/run/host",                    NULL,    NULL,                             MS_BIND,
+                  MOUNT_FATAL|MOUNT_MKDIR|MOUNT_PREFIX_ROOT }, /* Prepare this so that we can make it read-only when we are done */
+                { "/etc/os-release",        "/run/host/os-release",         NULL,    NULL,                             MS_BIND,
+                  MOUNT_TOUCH }, /* As per kernel interface requirements, bind mount first (creating mount points) and make read-only later */
+                { "/usr/lib/os-release",    "/run/host/os-release",         NULL,    NULL,                             MS_BIND,
+                  MOUNT_FATAL }, /* If /etc/os-release doesn't exist use the version in /usr/lib as fallback */
+                { NULL,                     "/run/host/os-release",         NULL,    NULL,                             MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT,
+                  MOUNT_FATAL },
+                { NULL,                     "/run/host",                    NULL,    NULL,                             MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT,
+                  MOUNT_FATAL|MOUNT_IN_USERNS },
 #if HAVE_SELINUX
                 { "/sys/fs/selinux",        "/sys/fs/selinux",              NULL,    NULL,                             MS_BIND,
                   MOUNT_MKDIR },  /* Bind mount first (mkdir/chown the mount point in case /sys/ is mounted as minimal skeleton tmpfs) */
@@ -589,9 +590,9 @@ int mount_all(const char *dest,
         int r;
 
         for (k = 0; k < ELEMENTSOF(mount_table); k++) {
-                _cleanup_free_ char *where = NULL, *options = NULL;
-                const char *o;
+                _cleanup_free_ char *where = NULL, *options = NULL, *prefixed = NULL;
                 bool fatal = FLAGS_SET(mount_table[k].mount_settings, MOUNT_FATAL);
+                const char *o;
 
                 if (in_userns != FLAGS_SET(mount_table[k].mount_settings, MOUNT_IN_USERNS))
                         continue;
@@ -616,20 +617,9 @@ int mount_all(const char *dest,
                                 return log_error_errno(r, "Failed to detect whether %s is a mount point: %m", where);
                         if (r > 0)
                                 continue;
-
-                        /* Shortcut for optional bind mounts: if the source can't be found skip ahead to avoid creating
-                         * empty and unused directories. */
-                        if (!fatal && FLAGS_SET(mount_table[k].mount_settings, MOUNT_MKDIR) && FLAGS_SET(mount_table[k].flags, MS_BIND)) {
-                                r = access(mount_table[k].what, F_OK);
-                                if (r < 0) {
-                                        if (errno == ENOENT)
-                                                continue;
-                                        return log_error_errno(errno, "Failed to stat %s: %m", mount_table[k].what);
-                                }
-                        }
                 }
 
-                if (FLAGS_SET(mount_table[k].mount_settings, MOUNT_MKDIR)) {
+                if ((mount_table[k].mount_settings & (MOUNT_MKDIR|MOUNT_TOUCH)) != 0) {
                         uid_t u = (use_userns && !in_userns) ? uid_shift : UID_INVALID;
 
                         if (FLAGS_SET(mount_table[k].mount_settings, MOUNT_TOUCH))
@@ -647,13 +637,17 @@ int mount_all(const char *dest,
                                 if (r != -EROFS)
                                         continue;
                         }
-                        if (FLAGS_SET(mount_table[k].mount_settings, MOUNT_TOUCH)) {
-                                r = touch(where);
-                                if (r < 0 && r != -EEXIST) {
-                                        if (fatal)
-                                                return log_error_errno(r, "Failed to create mount point %s: %m", where);
-                                        log_debug_errno(r, "Failed to create mount point %s: %m", where);
-                                }
+                }
+
+                if (FLAGS_SET(mount_table[k].mount_settings, MOUNT_TOUCH)) {
+                        r = touch(where);
+                        if (r < 0 && r != -EEXIST) {
+                                if (fatal && r != -EROFS)
+                                        return log_error_errno(r, "Failed to create file %s: %m", where);
+
+                                log_debug_errno(r, "Failed to create file %s: %m", where);
+                                if (r != -EROFS)
+                                        continue;
                         }
                 }
 
@@ -666,8 +660,18 @@ int mount_all(const char *dest,
                                 o = options;
                 }
 
+                if (FLAGS_SET(mount_table[k].mount_settings, MOUNT_PREFIX_ROOT)) {
+                        /* Optionally prefix the mount source with the root dir. This is useful in bind
+                         * mounts to be created within the container image before we transition into it. Note
+                         * that MOUNT_IN_USERNS is run after we transitioned hence prefixing is not ncessary
+                         * for those. */
+                        r = chase_symlinks(mount_table[k].what, dest, CHASE_PREFIX_ROOT, &prefixed, NULL);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, mount_table[k].what);
+                }
+
                 r = mount_verbose(fatal ? LOG_ERR : LOG_DEBUG,
-                                  mount_table[k].what,
+                                  prefixed ?: mount_table[k].what,
                                   where,
                                   mount_table[k].type,
                                   mount_table[k].flags,
index 062ed8b57daa768af8209fc102bc211614fc6f73..3898c74f18eaaf0897d769288df3b064ad882c33 100644 (file)
@@ -18,6 +18,7 @@ typedef enum MountSettingsMask {
         MOUNT_NON_ROOT_ONLY      = 1 << 7, /* if set, only non-root mounts are mounted */
         MOUNT_MKDIR              = 1 << 8, /* if set, make directory to mount over first */
         MOUNT_TOUCH              = 1 << 9, /* if set, touch file to mount over first */
+        MOUNT_PREFIX_ROOT        = 1 << 10,/* if set, prefix the source path with the container's root directory */
 } MountSettingsMask;
 
 typedef enum CustomMountType {
index 0ad75ac8b0576a7b6fa3beb0622c20fe196d433f..b8fbf00ea65bb79003eb9b2304259cc40949a76c 100755 (executable)
@@ -66,12 +66,25 @@ if [ -n "${ID:+set}" ] && [ "${ID}" != "${container_host_id}" ]; then exit 1; fi
 if [ -n "${VERSION_ID:+set}" ] && [ "${VERSION_ID}" != "${container_host_version_id}" ]; then exit 1; fi
 if [ -n "${BUILD_ID:+set}" ] && [ "${BUILD_ID}" != "${container_host_build_id}" ]; then exit 1; fi
 if [ -n "${VARIANT_ID:+set}" ] && [ "${VARIANT_ID}" != "${container_host_variant_id}" ]; then exit 1; fi
-cd /tmp; (cd /run/host/usr/lib; md5sum os-release) | md5sum -c
-if echo test >> /run/host/usr/lib/os-release; then exit 1; fi
-if echo test >> /run/host/etc/os-release; then exit 1; fi
+cd /tmp; (cd /run/host; md5sum os-release) | md5sum -c
+if echo test >> /run/host/os-release; then exit 1; fi
 '
 
-    systemd-nspawn --register=no -D /testsuite-13.nc-container --bind=/etc/os-release:/tmp/os-release /bin/sh -x -e -c "$_cmd"
+    local _os_release_source="/etc/os-release"
+    if [ ! -r "${_os_release_source}" ]; then
+        _os_release_source="/usr/lib/os-release"
+    elif [ -L "${_os_release_source}" ] && rm /etc/os-release; then
+        # Ensure that /etc always wins if available
+        cp /usr/lib/os-release /etc
+        echo MARKER=1 >> /etc/os-release
+    fi
+
+    systemd-nspawn --register=no -D /testsuite-13.nc-container --bind="${_os_release_source}":/tmp/os-release /bin/sh -x -e -c "$_cmd"
+
+    if grep -q MARKER /etc/os-release; then
+        rm /etc/os-release
+        ln -s ../usr/lib/os-release /etc/os-release
+    fi
 }
 
 function run {