]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
generators: hook in validatefs on gpt-auto and fstab generator mounts
authorLennart Poettering <lennart@poettering.net>
Wed, 12 Mar 2025 08:49:52 +0000 (09:49 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 31 Mar 2025 13:14:28 +0000 (15:14 +0200)
Let's turn on validatefs automatically for all auto-discovered
partitions.

Let's add an x-systemd.validatefs option to optionally turn this on for
fstab listed file systems.

man/systemd-gpt-auto-generator.xml
man/systemd-validatefs@.service.xml
man/systemd.mount.xml
src/basic/special.h
src/fstab-generator/fstab-generator.c
src/gpt-auto-generator/gpt-auto-generator.c
src/shared/generator.c
src/shared/generator.h

index d3724e7e9dd620a114fb983443f5c9589dc47ec7..af450f9fbe615c6fe4f5275c64a73e367ab56918 100644 (file)
     automatically measured into PCR 15 on activation, via
     <citerefentry><refentrytitle>systemd-pcrfs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
 
+    <para>Mount constraint metadata contained in the file systems is validated by pulling in
+    <citerefentry><refentrytitle>systemd-validatefs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    for generated mounts.</para>
+
     <para><filename>systemd-gpt-auto-generator</filename> implements
     <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
   </refsect1>
       <member><citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
       <member><citerefentry><refentrytitle>systemd-cryptsetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
       <member><citerefentry><refentrytitle>systemd-pcrfs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>systemd-validatefs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
       <member><citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
       <member><citerefentry project='die-net'><refentrytitle>cryptsetup</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
       <member><citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
index 6535ed463b80bd0c30eb378922242a2d0af23a6c..b7c8adf43f329b6ef89e2ea2c47c20f3f1321fa1 100644 (file)
       GPT partition type UUID formatted as string. It is compared with the partition type UUID of the
       partition this file system is located on, and if different the validation will fail.</para></listitem>
     </orderedlist>
+
+    <para>The <filename>systemd-validatefs@.service</filename> unit is automatically pulled into the initial
+    transaction by
+    <citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    for all file systems it discovers and generates mounts
+    for. <citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    will do this for all mounts with the <option>x-systemd.validatefs</option> mount option in
+    <filename>/etc/fstab</filename>.</para>
   </refsect1>
 
   <refsect1>
index 26bc116f975d6c6ab59fbfccb886a2e824247d7a..c3d06d696aa1b66d70c9425c0ae44993dbebe141 100644 (file)
         <xi:include href="version-info.xml" xpointer="v253"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>x-systemd.validatefs</option></term>
+
+        <listitem><para>Validates mount constraint metadata of the mounted file system after mounting
+        it. This ensures the
+        <citerefentry><refentrytitle>systemd-validatefs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+        service is pulled in by the mount unit.</para>
+
+        <para>Note that this option can only be used in <filename>/etc/fstab</filename>, and will be ignored
+        when part of the <varname>Options=</varname> setting in a unit file. It is also implied for all
+        partitions discovered by
+        <citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+
+        <xi:include href="version-info.xml" xpointer="v258"/></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>x-systemd.rw-only</option></term>
 
index 166737a9ca815455f289a59bf2601bbe6eabca54..a5cdfebae57beb112a959757d7c8c6d3cf5c1032 100644 (file)
@@ -95,6 +95,7 @@
 #define SPECIAL_GROWFS_ROOT_SERVICE "systemd-growfs-root.service"
 #define SPECIAL_PCRFS_SERVICE "systemd-pcrfs@.service"
 #define SPECIAL_PCRFS_ROOT_SERVICE "systemd-pcrfs-root.service"
+#define SPECIAL_VALIDATEFS_SERVICE "systemd-validatefs@.service"
 #define SPECIAL_HIBERNATE_RESUME_SERVICE "systemd-hibernate-resume.service"
 
 /* Services systemd relies on */
index 7e84be499d7a079c5b7fe0fdcd2f9b2b0adffae3..33c4c941f048d35b69f24e102bb694186e67c229 100644 (file)
 #include "volatile-util.h"
 
 typedef enum MountPointFlags {
-        MOUNT_NOAUTO    = 1 << 0,
-        MOUNT_NOFAIL    = 1 << 1,
-        MOUNT_AUTOMOUNT = 1 << 2,
-        MOUNT_MAKEFS    = 1 << 3,
-        MOUNT_GROWFS    = 1 << 4,
-        MOUNT_RW_ONLY   = 1 << 5,
-        MOUNT_PCRFS     = 1 << 6,
-        MOUNT_QUOTA     = 1 << 7,
+        MOUNT_NOAUTO     = 1 << 0,
+        MOUNT_NOFAIL     = 1 << 1,
+        MOUNT_AUTOMOUNT  = 1 << 2,
+        MOUNT_MAKEFS     = 1 << 3,
+        MOUNT_GROWFS     = 1 << 4,
+        MOUNT_RW_ONLY    = 1 << 5,
+        MOUNT_PCRFS      = 1 << 6,
+        MOUNT_QUOTA      = 1 << 7,
+        MOUNT_VALIDATEFS = 1 << 8,
 } MountPointFlags;
 
 typedef struct Mount {
@@ -249,9 +250,10 @@ static int add_swap(
                 return true;
         }
 
-        log_debug("Found swap entry what=%s makefs=%s growfs=%s pcrfs=%s noauto=%s nofail=%s",
+        log_debug("Found swap entry what=%s makefs=%s growfs=%s pcrfs=%s validatefs=%s noauto=%s nofail=%s",
                   what,
-                  yes_no(flags & MOUNT_MAKEFS), yes_no(flags & MOUNT_GROWFS), yes_no(flags & MOUNT_PCRFS),
+                  yes_no(flags & MOUNT_MAKEFS), yes_no(flags & MOUNT_GROWFS),
+                  yes_no(flags & MOUNT_PCRFS), yes_no(flags & MOUNT_VALIDATEFS),
                   yes_no(flags & MOUNT_NOAUTO), yes_no(flags & MOUNT_NOFAIL));
 
         r = unit_name_from_path(what, ".swap", &name);
@@ -304,6 +306,8 @@ static int add_swap(
                 log_warning("%s: growing swap devices is currently unsupported.", what);
         if (flags & MOUNT_PCRFS)
                 log_warning("%s: measuring swap devices is currently unsupported.", what);
+        if (flags & MOUNT_VALIDATEFS)
+                log_warning("%s: validating swap devices is currently unsupported.", what);
 
         if (!(flags & MOUNT_NOAUTO)) {
                 r = generator_add_symlink(arg_dest, SPECIAL_SWAP_TARGET,
@@ -688,6 +692,12 @@ static int add_mount(
                 }
         }
 
+        if (flags & MOUNT_VALIDATEFS) {
+                r = generator_hook_up_validatefs(dest, where, target_unit);
+                if (r < 0)
+                        return r;
+        }
+
         if (flags & MOUNT_QUOTA) {
                 r = generator_hook_up_quotacheck(dest, what, where, target_unit, fstype);
                 if (r < 0) {
@@ -842,6 +852,8 @@ static MountPointFlags fstab_options_to_flags(const char *options, bool is_swap)
                 flags |= MOUNT_GROWFS;
         if (fstab_test_option(options, "x-systemd.pcrfs\0"))
                 flags |= MOUNT_PCRFS;
+        if (fstab_test_option(options, "x-systemd.validatefs\0"))
+                flags |= MOUNT_VALIDATEFS;
         if (fstab_test_option(options, "usrquota\0" "grpquota\0" "quota\0" "usrjquota\0" "grpjquota\0" "prjquota\0"))
                 flags |= MOUNT_QUOTA;
         if (fstab_test_yes_no_option(options, "noauto\0" "auto\0"))
@@ -970,9 +982,10 @@ static int parse_fstab_one(
                 free_and_replace(what, p);
         }
 
-        log_debug("Found entry what=%s where=%s type=%s makefs=%s growfs=%s pcrfs=%s noauto=%s nofail=%s",
+        log_debug("Found entry what=%s where=%s type=%s makefs=%s growfs=%s pcrfs=%s validatefs=%s noauto=%s nofail=%s",
                   what, where, strna(fstype),
-                  yes_no(flags & MOUNT_MAKEFS), yes_no(flags & MOUNT_GROWFS), yes_no(flags & MOUNT_PCRFS),
+                  yes_no(flags & MOUNT_MAKEFS), yes_no(flags & MOUNT_GROWFS),
+                  yes_no(flags & MOUNT_PCRFS), yes_no(flags & MOUNT_VALIDATEFS),
                   yes_no(flags & MOUNT_NOAUTO), yes_no(flags & MOUNT_NOFAIL));
 
         bool is_sysroot = in_initrd() && path_equal(where, "/sysroot");
index 0addb5a82c985b5b5db5c9560725b8eebf2fd731..54f674040be3168bdde139c90678b5b1e55e3837 100644 (file)
 #include "unit-name.h"
 #include "virt.h"
 
+typedef enum MountPointFlags {
+        MOUNT_RW         = 1 << 0,
+        MOUNT_GROWFS     = 1 << 1,
+        MOUNT_MEASURE    = 1 << 2,
+        MOUNT_VALIDATEFS = 1 << 3,
+} MountPointFlags;
+
 static const char *arg_dest = NULL;
 static bool arg_enabled = true;
 static GptAutoRoot arg_auto_root = _GPT_AUTO_ROOT_INVALID;
@@ -57,9 +64,8 @@ static int add_cryptsetup(
                 const char *id,
                 const char *what,
                 const char *mount_opts,
-                bool rw,
+                MountPointFlags flags,
                 bool require,
-                bool measure,
                 char **ret_device) {
 
 #if HAVE_LIBCRYPTSETUP
@@ -97,7 +103,7 @@ static int add_cryptsetup(
                 "After=%s\n",
                 d, d);
 
-        if (!rw) {
+        if (!FLAGS_SET(flags, MOUNT_RW)) {
                 options = strdup("read-only");
                 if (!options)
                         return log_oom();
@@ -109,7 +115,7 @@ static int add_cryptsetup(
                 if (!strextend_with_separator(&options, ",", "tpm2-device=auto"))
                         return log_oom();
 
-        if (measure) {
+        if (FLAGS_SET(flags, MOUNT_MEASURE)) {
                 /* We only measure the root volume key into PCR 15 if we are booted with sd-stub (i.e. in a
                  * UKI), and sd-stub measured the UKI. We do this in order not to step into people's own PCR
                  * assignment, under the assumption that people who are fine to use sd-stub with its PCR
@@ -178,9 +184,7 @@ static int add_mount(
                 const char *what,
                 const char *where,
                 const char *fstype,
-                bool rw,
-                bool growfs,
-                bool measure,
+                MountPointFlags flags,
                 const char *options,
                 const char *description,
                 const char *post) {
@@ -203,7 +207,7 @@ static int add_mount(
         if (streq_ptr(fstype, "crypto_LUKS")) {
                 /* Mount options passed are determined by partition_pick_mount_options(), whose result
                  * is known to not contain timeout options. */
-                r = add_cryptsetup(id, what, /* mount_opts = */ NULL, rw, /* require= */ true, measure, &crypto_what);
+                r = add_cryptsetup(id, what, /* mount_opts = */ NULL, flags, /* require= */ true, &crypto_what);
                 if (r < 0)
                         return r;
 
@@ -270,13 +274,19 @@ static int add_mount(
         if (r < 0)
                 return log_error_errno(r, "Failed to write unit %s: %m", unit);
 
-        if (growfs) {
+        if (FLAGS_SET(flags, MOUNT_VALIDATEFS)) {
+                r = generator_hook_up_validatefs(arg_dest, where, post);
+                if (r < 0)
+                        return r;
+        }
+
+        if (FLAGS_SET(flags, MOUNT_GROWFS)) {
                 r = generator_hook_up_growfs(arg_dest, where, post);
                 if (r < 0)
                         return r;
         }
 
-        if (measure) {
+        if (FLAGS_SET(flags, MOUNT_MEASURE)) {
                 r = generator_hook_up_pcrfs(arg_dest, where, post);
                 if (r < 0)
                         return r;
@@ -353,9 +363,10 @@ static int add_partition_mount(
                         p->node,
                         where,
                         p->fstype,
-                        p->rw,
-                        p->growfs,
-                        /* measure= */ STR_IN_SET(id, "root", "var"), /* by default measure rootfs and /var, since they contain the "identity" of the system */
+                        (p->rw ? MOUNT_RW : 0) |
+                        MOUNT_VALIDATEFS |
+                        (p->growfs ? MOUNT_GROWFS : 0) |
+                        (STR_IN_SET(id, "root", "var") ? MOUNT_MEASURE : 0), /* by default measure rootfs and /var, since they contain the "identity" of the system */
                         options,
                         description,
                         SPECIAL_LOCAL_FS_TARGET);
@@ -383,7 +394,7 @@ static int add_partition_swap(DissectedPartition *p) {
         }
 
         if (streq_ptr(p->fstype, "crypto_LUKS")) {
-                r = add_cryptsetup("swap", p->node, /* mount_opts = */ NULL, /* rw= */ true, /* require= */ true, /* measure= */ false, &crypto_what);
+                r = add_cryptsetup("swap", p->node, /* mount_opts = */ NULL, MOUNT_RW, /* require= */ true, &crypto_what);
                 if (r < 0)
                         return r;
                 what = crypto_what;
@@ -427,8 +438,7 @@ static int add_automount(
                 const char *what,
                 const char *where,
                 const char *fstype,
-                bool rw,
-                bool growfs,
+                MountPointFlags flags,
                 const char *options,
                 const char *description,
                 usec_t timeout) {
@@ -445,12 +455,10 @@ static int add_automount(
                       what,
                       where,
                       fstype,
-                      rw,
-                      growfs,
-                      /* measure= */ false,
+                      flags,
                       options,
                       description,
-                      NULL);
+                      /* post= */ NULL);
         if (r < 0)
                 return r;
 
@@ -508,8 +516,7 @@ static int add_partition_xbootldr(DissectedPartition *p) {
                         p->node,
                         "/boot",
                         p->fstype,
-                        /* rw= */ true,
-                        /* growfs= */ false,
+                        MOUNT_RW,
                         options,
                         "Boot Loader Partition",
                         LOADER_PARTITION_IDLE_USEC);
@@ -571,8 +578,7 @@ static int add_partition_esp(DissectedPartition *p, bool has_xbootldr) {
                         p->node,
                         esp_path,
                         p->fstype,
-                        /* rw= */ true,
-                        /* growfs= */ false,
+                        MOUNT_RW,
                         options,
                         "EFI System Partition Automount",
                         LOADER_PARTITION_IDLE_USEC);
@@ -665,7 +671,7 @@ static int add_root_cryptsetup(void) {
                         bdev = "/dev/gpt-auto-root-luks-ignore-factory-reset";
         }
 
-        return add_cryptsetup("root", bdev, arg_root_options, /* rw= */ true, /* require= */ false, /* measure= */ true, NULL);
+        return add_cryptsetup("root", bdev, arg_root_options, MOUNT_RW|MOUNT_MEASURE, /* require= */ false, NULL);
 #else
         return 0;
 #endif
@@ -752,9 +758,9 @@ static int add_root_mount(void) {
                         bdev,
                         in_initrd() ? "/sysroot" : "/",
                         arg_root_fstype,
-                        /* rw= */ arg_root_rw > 0,
-                        /* growfs= */ false,
-                        /* measure= */ true,
+                        (arg_root_rw > 0 ? MOUNT_RW : 0) |
+                        (in_initrd() ? MOUNT_VALIDATEFS : 0) |
+                        MOUNT_MEASURE,
                         options,
                         "Root Partition",
                         in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
index 0bec2a2a27772d782c7743d9cf74c4ea29f815a7..70fcd6e935d69c26bb50c0a92b0b4eefcd0b1adf 100644 (file)
@@ -764,6 +764,43 @@ int generator_hook_up_pcrfs(
         return generator_add_symlink_full(dir, where_unit, "wants", pcrfs_unit_path, instance);
 }
 
+int generator_hook_up_validatefs(
+                const char *dir,
+                const char *where,
+                const char *target) {
+
+        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
+        const char *validatefs_unit, *validatefs_unit_path;
+        int r;
+
+        assert(dir);
+        assert(where);
+
+        /* never hook this in for the actual root fs, because it's too late then, we already are running from
+         * the root fs, it makes no sense to validate it anymore */
+        if (empty_or_root(where))
+                return 0;
+
+        r = unit_name_from_path(where, ".mount", &where_unit);
+        if (r < 0)
+                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
+
+        validatefs_unit = SPECIAL_VALIDATEFS_SERVICE;
+        validatefs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_VALIDATEFS_SERVICE;
+
+        r = unit_name_path_escape(where, &instance);
+        if (r < 0)
+                return log_error_errno(r, "Failed to escape path '%s': %m", where);
+
+        if (target) {
+                r = generator_add_ordering(dir, target, "After", validatefs_unit, instance);
+                if (r < 0)
+                        return r;
+        }
+
+        return generator_add_symlink_full(dir, where_unit, "wants", validatefs_unit_path, instance);
+}
+
 int generator_hook_up_quotacheck(
                 const char *dir,
                 const char *what,
index 4738fe90afc07632fe289e9a35a8e9952de61cac..47cccd9c592047218bc52f6541123e6e17963dfa 100644 (file)
@@ -67,6 +67,10 @@ int generator_hook_up_pcrfs(
         const char *dir,
         const char *where,
         const char *target);
+int generator_hook_up_validatefs(
+        const char *dir,
+        const char *where,
+        const char *target);
 int generator_hook_up_quotacheck(
         const char *dir,
         const char *what,