]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
fstab-generator: support creating bind mounts via root= kernel cmdline switches
authorLennart Poettering <lennart@poettering.net>
Mon, 10 Feb 2025 11:56:08 +0000 (12:56 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 21 Feb 2025 09:03:32 +0000 (10:03 +0100)
This is useful for bind mounting a freshly downloaded and unpacked tar
disk images to /sysroot to mount into.

Specifically, with a kernel command line like this one:

  rd.systemd.pull=verify=no,machine,tar:root:http://_gateway:8081/image.tar root=bind:/run/machines/root ip=any

The first parameter downloads the root image, the second one then binds
it to /sysroot so that we can boot into it.

man/systemd-fstab-generator.xml
man/systemd-import-generator.xml
src/fstab-generator/fstab-generator.c
test/test-fstab-generator/test-12-dev-sdx.expected/sysroot.mount
test/test-fstab-generator/test-13-label.expected/sysroot.mount
test/test-fstab-generator/test-14-uuid.expected/sysroot.mount
test/test-fstab-generator/test-15-partuuid.expected/sysroot.mount
test/test-fstab-generator/test-16-tmpfs.expected/sysroot.mount

index 42c6d27bfc50d780a0edef39a7db1364a5e0abc9..f04334b9851d29a459021a0e7518bb4ced1f34c1 100644 (file)
         separate, immutable <filename>/usr/</filename> file system. Also see
         <varname>systemd.volatile=</varname> below.</para>
 
+        <para>Use <literal>bind:…</literal> to bind mount another directory as operating system root
+        filesystems (added in v258). Expects an absolute path name referencing an existing directory within the initrd's file
+        hierarchy to boot into.</para>
+
         <xi:include href="version-info.xml" xpointer="v217"/></listitem>
       </varlistentry>
 
index cea015e6f8aad5c2ec9884c071a4586de1164a7a..dfadf5db5fc40f4223bb4c6bb6a828661760a642 100644 (file)
       <literal>http://example.com/image.efi</literal> this would result in a root disk being downloaded from
       <literal>http://example.com/image.raw.xz</literal>.</para>
     </example>
+
+    <example>
+      <title>Boot into disk image (tar), with URL derived from UEFI HTTP network booting</title>
+
+      <programlisting>systemd.pull=tar,machine,verify=no,bootorigin:root:image.tar.xz root=bind:/run/machines/root</programlisting>
+
+      <para>This is similar to the previous example, but instead of a raw (i.e. block device based) disk
+      image the system boots into a tarball that is downloaded from the originating UEFI network
+      server.</para>
+    </example>
   </refsect1>
 
   <refsect1>
index 4dfe9fea89785a9a76ef1dcc034cb0f4d0a21220..475981f36d06e840131df0476006227c8d9dda06 100644 (file)
@@ -494,7 +494,8 @@ static int add_mount(
                 const char *opts,
                 int passno,
                 MountPointFlags flags,
-                const char *target_unit) {
+                const char *target_unit,
+                const char *extra_after) {
 
         _cleanup_free_ char *name = NULL, *automount_name = NULL, *filtered = NULL, *where_escaped = NULL,
                 *opts_root_filtered = NULL;
@@ -596,6 +597,9 @@ static int add_mount(
         if (target_unit && !FLAGS_SET(flags, MOUNT_NOFAIL))
                 fprintf(f, "Before=%s\n", target_unit);
 
+        if (extra_after)
+                fprintf(f, "After=%s\n", extra_after);
+
         if (passno != 0) {
                 r = generator_write_fsck_deps(f, dest, what, where, fstype);
                 if (r < 0)
@@ -814,15 +818,16 @@ static bool sysfs_check(void) {
 
 static int add_sysusr_sysroot_usr_bind_mount(const char *source) {
         return add_mount(source,
-                        arg_dest,
-                        "/sysusr/usr",
-                        "/sysroot/usr",
-                        NULL,
-                        NULL,
-                        "bind",
-                        0,
-                        0,
-                        SPECIAL_INITRD_FS_TARGET);
+                         arg_dest,
+                         "/sysusr/usr",
+                         "/sysroot/usr",
+                         /* original_where= */ NULL,
+                         /* fstype= */ NULL,
+                         "bind",
+                         /* passno= */ 0,
+                         /* flags= */ 0,
+                         SPECIAL_INITRD_FS_TARGET,
+                         /* extra_after= */ NULL);
 }
 
 static MountPointFlags fstab_options_to_flags(const char *options, bool is_swap) {
@@ -1000,7 +1005,8 @@ static int parse_fstab_one(
                       options,
                       passno,
                       flags,
-                      target_unit);
+                      target_unit,
+                      /* extra_after= */ NULL);
         if (r <= 0)
                 return r;
 
@@ -1105,8 +1111,8 @@ static int sysroot_is_nfsroot(void) {
 
 static int add_sysroot_mount(void) {
         _cleanup_free_ char *what = NULL;
-        const char *opts, *fstype;
-        bool default_rw, makefs;
+        const char *extra_opts = NULL, *fstype = NULL;
+        bool default_rw = true, makefs = false;
         MountPointFlags flags;
         int r;
 
@@ -1149,7 +1155,20 @@ static int add_sysroot_mount(void) {
                 return 0;
         }
 
-        if (streq(arg_root_what, "tmpfs")) {
+        const char *bind = startswith(arg_root_what, "bind:");
+        if (bind) {
+                if (!path_is_valid(bind) || !path_is_absolute(bind)) {
+                        log_debug("Invalid root=bind: source path, ignoring: %s", bind);
+                        return 0;
+                }
+
+                what = strdup(bind);
+                if (!what)
+                        return log_oom();
+
+                extra_opts = "bind";
+
+        } else if (streq(arg_root_what, "tmpfs")) {
                 /* If root=tmpfs is specified, then take this as shortcut for a writable tmpfs mount as root */
 
                 what = strdup("rootfs"); /* just a pretty name, to show up in /proc/self/mountinfo */
@@ -1157,8 +1176,6 @@ static int add_sysroot_mount(void) {
                         return log_oom();
 
                 fstype = arg_root_fstype ?: "tmpfs"; /* tmpfs, unless overridden */
-
-                default_rw = true; /* writable, unless overridden */;
         } else {
 
                 what = fstab_node_to_udev_node(arg_root_what);
@@ -1166,40 +1183,44 @@ static int add_sysroot_mount(void) {
                         return log_oom();
 
                 fstype = arg_root_fstype; /* if not specified explicitly, don't default to anything here */
-
                 default_rw = false; /* read-only, unless overridden */
         }
 
-        if (!arg_root_options)
-                opts = arg_root_rw > 0 || (arg_root_rw < 0 && default_rw) ? "rw" : "ro";
-        else if (arg_root_rw >= 0 ||
-                 !fstab_test_option(arg_root_options, "ro\0" "rw\0"))
-                opts = strjoina(arg_root_options, ",", arg_root_rw > 0 ? "rw" : "ro");
-        else
-                opts = arg_root_options;
+        _cleanup_free_ char *combined_options = NULL;
+        if (strdup_to(&combined_options, arg_root_options) < 0)
+                return log_oom();
+
+        if (arg_root_rw >= 0 || !fstab_test_option(combined_options, "ro\0" "rw\0"))
+                if (!strextend_with_separator(&combined_options, ",", arg_root_rw > 0 || (arg_root_rw < 0 && default_rw) ? "rw" : "ro"))
+                        return log_oom();
+
+        if (extra_opts)
+                if (!strextend_with_separator(&combined_options, ",", extra_opts))
+                        return log_oom();
 
-        log_debug("Found entry what=%s where=/sysroot type=%s opts=%s", what, strna(arg_root_fstype), strempty(opts));
+        log_debug("Found entry what=%s where=/sysroot type=%s opts=%s", what, strna(arg_root_fstype), strempty(combined_options));
 
-        makefs = fstab_test_option(opts, "x-systemd.makefs\0");
+        makefs = fstab_test_option(combined_options, "x-systemd.makefs\0");
         flags = makefs * MOUNT_MAKEFS;
 
         return add_mount("/proc/cmdline",
                          arg_dest,
                          what,
                          "/sysroot",
-                         NULL,
+                         /* original_where= */ NULL,
                          fstype,
-                         opts,
-                         is_device_path(what) ? 1 : 0, /* passno */
-                         flags,                        /* makefs off, pcrfs off, quota off, noauto off, nofail off, automount off */
-                         SPECIAL_INITRD_ROOT_FS_TARGET);
+                         combined_options,
+                         /* passno= */ is_device_path(what) ? 1 : 0,
+                         flags,
+                         SPECIAL_INITRD_ROOT_FS_TARGET,
+                         "imports.target");
 }
 
 static int add_sysroot_usr_mount(void) {
         _cleanup_free_ char *what = NULL;
-        const char *opts;
-        bool makefs;
+        const char *extra_opts = NULL;
         MountPointFlags flags;
+        bool makefs;
         int r;
 
         /* Returns 0 if we didn't do anything, > 0 if we either generated a unit for the /usr/ mount, or we
@@ -1250,16 +1271,35 @@ static int add_sysroot_usr_mount(void) {
                 return 1; /* As above, report that NFS code will create the unit */
         }
 
-        what = fstab_node_to_udev_node(arg_usr_what);
-        if (!what)
+        const char *bind = startswith(arg_usr_what, "bind:");
+        if (bind) {
+                if (!path_is_valid(bind) || !path_is_absolute(bind)) {
+                        log_debug("Invalid mount.usr=bind: source path, ignoring: %s", bind);
+                        return 0;
+                }
+
+                what = strdup(bind);
+                if (!what)
+                        return log_oom();
+
+                extra_opts = "bind";
+        } else {
+                what = fstab_node_to_udev_node(arg_usr_what);
+                if (!what)
+                        return log_oom();
+        }
+
+        _cleanup_free_ char *combined_options = NULL;
+        if (strdup_to(&combined_options, arg_usr_options) < 0)
                 return log_oom();
 
-        if (!arg_usr_options)
-                opts = arg_root_rw > 0 ? "rw" : "ro";
-        else if (!fstab_test_option(arg_usr_options, "ro\0" "rw\0"))
-                opts = strjoina(arg_usr_options, ",", arg_root_rw > 0 ? "rw" : "ro");
-        else
-                opts = arg_usr_options;
+        if (!fstab_test_option(combined_options, "ro\0" "rw\0"))
+                if (!strextend_with_separator(&combined_options, ",", arg_root_rw > 0 ? "rw" : "ro"))
+                        return log_oom();
+
+        if (extra_opts)
+                if (!strextend_with_separator(&combined_options, ",", extra_opts))
+                        return log_oom();
 
         /* When mounting /usr from the initrd, we add an extra level of indirection: we first mount the /usr/
          * partition to /sysusr/usr/, and then afterwards bind mount that to /sysroot/usr/. We do this so
@@ -1268,21 +1308,22 @@ static int add_sysroot_usr_mount(void) {
          * this should order itself after initrd-usr-fs.target and before initrd-fs.target; and it should
          * look into both /sysusr/ and /sysroot/ for the configuration data to apply. */
 
-        log_debug("Found entry what=%s where=/sysusr/usr type=%s opts=%s", what, strna(arg_usr_fstype), strempty(opts));
+        log_debug("Found entry what=%s where=/sysusr/usr type=%s opts=%s", what, strna(arg_usr_fstype), strempty(combined_options));
 
-        makefs = fstab_test_option(opts, "x-systemd.makefs\0");
+        makefs = fstab_test_option(combined_options, "x-systemd.makefs\0");
         flags = makefs * MOUNT_MAKEFS;
 
         r = add_mount("/proc/cmdline",
                       arg_dest,
                       what,
                       "/sysusr/usr",
-                      NULL,
+                      /* original_where= */ NULL,
                       arg_usr_fstype,
-                      opts,
-                      is_device_path(what) ? 1 : 0, /* passno */
+                      combined_options,
+                      /* passno= */ is_device_path(what) ? 1 : 0,
                       flags,
-                      SPECIAL_INITRD_USR_FS_TARGET);
+                      SPECIAL_INITRD_USR_FS_TARGET,
+                      "imports.target");
         if (r < 0)
                 return r;
 
@@ -1336,12 +1377,13 @@ static int add_volatile_var(void) {
                          arg_dest_late,
                          "tmpfs",
                          "/var",
-                         NULL,
+                         /* original_where= */ NULL,
                          "tmpfs",
                          "mode=0755" TMPFS_LIMITS_VAR,
-                         0,
-                         0,
-                         SPECIAL_LOCAL_FS_TARGET);
+                         /* passno= */ 0,
+                         /* flags= */ 0,
+                         SPECIAL_LOCAL_FS_TARGET,
+                         /* extra_after= */ NULL);
 }
 
 static int add_mounts_from_cmdline(void) {
index 8f8ef486173227de927c28b49a148c9ea9cb34d4..92eed56423aaff6479854a80a885363906b1cf9d 100644 (file)
@@ -4,6 +4,7 @@
 Documentation=man:fstab(5) man:systemd-fstab-generator(8)
 SourcePath=/proc/cmdline
 Before=initrd-root-fs.target
+After=imports.target
 Requires=systemd-fsck-root.service
 After=systemd-fsck-root.service
 After=blockdev@dev-sdx1.target
index 98698d5968accbdcacd59256dd106180c9e23cf5..8b0f6cce2571c44a6fde1abefddb69dc1118cdf5 100644 (file)
@@ -4,6 +4,7 @@
 Documentation=man:fstab(5) man:systemd-fstab-generator(8)
 SourcePath=/proc/cmdline
 Before=initrd-root-fs.target
+After=imports.target
 Requires=systemd-fsck-root.service
 After=systemd-fsck-root.service
 After=blockdev@dev-disk-by\x2dlabel-Root.target
index 999acb0b23e2311d6bce75273c8ae52e14cec8f0..096da67f1e4f800f110d3acf63a30eec35704157 100644 (file)
@@ -4,6 +4,7 @@
 Documentation=man:fstab(5) man:systemd-fstab-generator(8)
 SourcePath=/proc/cmdline
 Before=initrd-root-fs.target
+After=imports.target
 Requires=systemd-fsck-root.service
 After=systemd-fsck-root.service
 After=blockdev@dev-disk-by\x2duuid-3f5ad593\x2d4546\x2d4a94\x2da374\x2dbcfb68aa11f7.target
index d10fb6ef76136effa261ad0a64cde37f067159e7..6d948b91c29e4dd13322d500cb734b0d6dcdda79 100644 (file)
@@ -4,6 +4,7 @@
 Documentation=man:fstab(5) man:systemd-fstab-generator(8)
 SourcePath=/proc/cmdline
 Before=initrd-root-fs.target
+After=imports.target
 Requires=systemd-fsck-root.service
 After=systemd-fsck-root.service
 After=blockdev@dev-disk-by\x2dpartuuid-3f5ad593\x2d4546\x2d4a94\x2da374\x2dbcfb68aa11f7.target
index 6bd9a07f2c406dc87b374eee610bd9c1bd33bfc4..99728fd0ca3c6150a3f051548fafafe1eac69358 100644 (file)
@@ -4,6 +4,7 @@
 Documentation=man:fstab(5) man:systemd-fstab-generator(8)
 SourcePath=/proc/cmdline
 Before=initrd-root-fs.target
+After=imports.target
 
 [Mount]
 What=rootfs