From 9d27286214894954619f59dd425bb53e5228c6de Mon Sep 17 00:00:00 2001 From: nikhilnrr Date: Fri, 28 Aug 2026 14:36:45 +0530 Subject: [PATCH 1/2] test-fstab-generator: fix inverted /mnt symlink guard The test-18-options.fstab.input scenario was guarded by "$(readlink /mnt)" != "/mnt", which is true on any normal system since readlink prints nothing for a plain directory. This meant the scenario was always skipped, and its fixtures were never actually compared against generator output. Fix the guard to check whether /mnt is a symlink directly, and repair the pre-existing fixtures that had drifted while the scenario was dead: - mnt-wantedby-automount.mount had a stale Where= value and was missing the escaping the generator emits for the hyphen in /mnt/wantedby-automount. - mnt-requires.mount had Requires=/After= in the wrong order. Also extend the backslash-stripping workaround loop (used because the .deb packager dislikes files named with a backslash) to cover .automount units in addition to .mount/.swap, and fix an ordering bug where -f was checked before -L, which could leave a dangling symlink when both a unit and its drop-in symlink needed stripping. --- test/test-fstab-generator.sh | 11 +++++------ .../mnt-wantedby-automount.automount | 1 - .../mnt-wantedbyx2dautomount.automount | 1 + .../mnt-requires.mount | 2 +- ...t.automount => mnt-wantedbyx2dautomount.automount} | 0 ...automount.mount => mnt-wantedbyx2dautomount.mount} | 2 +- .../mnt-wantedby-automount.automount | 1 - .../mnt-wantedbyx2dautomount.automount | 1 + .../test-18-options.fstab.expected/mnt-requires.mount | 2 +- ...t.automount => mnt-wantedbyx2dautomount.automount} | 0 ...automount.mount => mnt-wantedbyx2dautomount.mount} | 2 +- 11 files changed, 11 insertions(+), 12 deletions(-) delete mode 120000 test/test-fstab-generator/test-18-options.fstab.expected.sysroot/foo.service.wants/mnt-wantedby-automount.automount create mode 120000 test/test-fstab-generator/test-18-options.fstab.expected.sysroot/foo.service.wants/mnt-wantedbyx2dautomount.automount rename test/test-fstab-generator/test-18-options.fstab.expected.sysroot/{mnt-wantedby-automount.automount => mnt-wantedbyx2dautomount.automount} (100%) rename test/test-fstab-generator/test-18-options.fstab.expected.sysroot/{mnt-wantedby-automount.mount => mnt-wantedbyx2dautomount.mount} (91%) delete mode 120000 test/test-fstab-generator/test-18-options.fstab.expected/foo.service.wants/mnt-wantedby-automount.automount create mode 120000 test/test-fstab-generator/test-18-options.fstab.expected/foo.service.wants/mnt-wantedbyx2dautomount.automount rename test/test-fstab-generator/test-18-options.fstab.expected/{mnt-wantedby-automount.automount => mnt-wantedbyx2dautomount.automount} (100%) rename test/test-fstab-generator/test-18-options.fstab.expected/{mnt-wantedby-automount.mount => mnt-wantedbyx2dautomount.mount} (91%) diff --git a/test/test-fstab-generator.sh b/test/test-fstab-generator.sh index 24a2533f16..f6173a0310 100755 --- a/test/test-fstab-generator.sh +++ b/test/test-fstab-generator.sh @@ -67,15 +67,15 @@ test_one() ( # .deb packager seems to dislike files named with backslash. So, as a workaround, we store files # without backslash in .expected. - for i in "$out"/**/*\\*.{mount,swap}; do + for i in "$out"/**/*\\*.{mount,swap,automount}; do k="${i//\\/}" if [[ "$i" != "$k" ]]; then - if [[ -f "$i" ]]; then - mv "$i" "$k" - elif [[ -L "$i" ]]; then + if [[ -L "$i" ]]; then dest=$(readlink "$i") rm "$i" ln -s "${dest//\\/}" "$k" + elif [[ -f "$i" ]]; then + mv "$i" "$k" fi fi done @@ -130,8 +130,7 @@ test_one() ( for f in "$src"/test-*.input; do # If /mnt is a symlink, then the expected output from this # test scenario will not match the actual output - if test "$f" = "$src/test-18-options.fstab.input" -a "$(readlink /mnt)" != "/mnt" - then + if [[ "$f" == "$src/test-18-options.fstab.input" && -L /mnt ]]; then echo "Skip $f because /mnt is a symlink" continue fi diff --git a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/foo.service.wants/mnt-wantedby-automount.automount b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/foo.service.wants/mnt-wantedby-automount.automount deleted file mode 120000 index 14fd395672..0000000000 --- a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/foo.service.wants/mnt-wantedby-automount.automount +++ /dev/null @@ -1 +0,0 @@ -../mnt-wantedby-automount.automount \ No newline at end of file diff --git a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/foo.service.wants/mnt-wantedbyx2dautomount.automount b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/foo.service.wants/mnt-wantedbyx2dautomount.automount new file mode 120000 index 0000000000..97b5f69861 --- /dev/null +++ b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/foo.service.wants/mnt-wantedbyx2dautomount.automount @@ -0,0 +1 @@ +../mnt-wantedbyx2dautomount.automount \ No newline at end of file diff --git a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-requires.mount b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-requires.mount index 8386616593..f2aa83939b 100644 --- a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-requires.mount +++ b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-requires.mount @@ -3,8 +3,8 @@ [Unit] Documentation=man:fstab(5) man:systemd-fstab-generator(8) SourcePath=/etc/fstab -After=foo.service Requires=foo.service +After=foo.service Before=local-fs.target After=blockdev@dev-sdx5.target diff --git a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.automount b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedbyx2dautomount.automount similarity index 100% rename from test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.automount rename to test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedbyx2dautomount.automount diff --git a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.mount b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedbyx2dautomount.mount similarity index 91% rename from test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.mount rename to test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedbyx2dautomount.mount index d9094767f2..40375bf010 100644 --- a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.mount +++ b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedbyx2dautomount.mount @@ -10,5 +10,5 @@ After=blockdev@dev-sdx17.target [Mount] What=/dev/sdx17 -Where=/mnt/wantedby +Where=/mnt/wantedby-automount Options=x-systemd.wanted-by=foo.service,x-systemd.automount diff --git a/test/test-fstab-generator/test-18-options.fstab.expected/foo.service.wants/mnt-wantedby-automount.automount b/test/test-fstab-generator/test-18-options.fstab.expected/foo.service.wants/mnt-wantedby-automount.automount deleted file mode 120000 index 14fd395672..0000000000 --- a/test/test-fstab-generator/test-18-options.fstab.expected/foo.service.wants/mnt-wantedby-automount.automount +++ /dev/null @@ -1 +0,0 @@ -../mnt-wantedby-automount.automount \ No newline at end of file diff --git a/test/test-fstab-generator/test-18-options.fstab.expected/foo.service.wants/mnt-wantedbyx2dautomount.automount b/test/test-fstab-generator/test-18-options.fstab.expected/foo.service.wants/mnt-wantedbyx2dautomount.automount new file mode 120000 index 0000000000..97b5f69861 --- /dev/null +++ b/test/test-fstab-generator/test-18-options.fstab.expected/foo.service.wants/mnt-wantedbyx2dautomount.automount @@ -0,0 +1 @@ +../mnt-wantedbyx2dautomount.automount \ No newline at end of file diff --git a/test/test-fstab-generator/test-18-options.fstab.expected/mnt-requires.mount b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-requires.mount index 8386616593..f2aa83939b 100644 --- a/test/test-fstab-generator/test-18-options.fstab.expected/mnt-requires.mount +++ b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-requires.mount @@ -3,8 +3,8 @@ [Unit] Documentation=man:fstab(5) man:systemd-fstab-generator(8) SourcePath=/etc/fstab -After=foo.service Requires=foo.service +After=foo.service Before=local-fs.target After=blockdev@dev-sdx5.target diff --git a/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.automount b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedbyx2dautomount.automount similarity index 100% rename from test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.automount rename to test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedbyx2dautomount.automount diff --git a/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.mount b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedbyx2dautomount.mount similarity index 91% rename from test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.mount rename to test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedbyx2dautomount.mount index d9094767f2..40375bf010 100644 --- a/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.mount +++ b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedbyx2dautomount.mount @@ -10,5 +10,5 @@ After=blockdev@dev-sdx17.target [Mount] What=/dev/sdx17 -Where=/mnt/wantedby +Where=/mnt/wantedby-automount Options=x-systemd.wanted-by=foo.service,x-systemd.automount From b7041b73cfe7418b27b76c51f0de03a95999fdf9 Mon Sep 17 00:00:00 2001 From: nikhilnrr Date: Fri, 28 Aug 2026 14:37:26 +0530 Subject: [PATCH 2/2] fstab-generator: support for x-systemd.onfailure Add support for the x-systemd.onfailure fstab option in fstab-generator. This allows generated mount units to include the systemd OnFailure= dependency specified through /etc/fstab. x-systemd.onfailure= is handled by generalizing the existing write_dependency() helper (already used for After=/Before=/Requires=/ Wants=) to take an optional unit-name suffix. When no suffix is given, unit names are mangled without forcing a ".mount" suffix, since OnFailure= targets are not necessarily mount units. write_on_failure() is now a thin wrapper around write_dependency(). Example: /dev/sda1 /data ext4 defaults,x-systemd.onfailure=emergency.target 0 2 generates: OnFailure=emergency.target This makes it possible to attach recovery targets or services to mount failures directly from fstab configuration. Add documentation and generator tests. --- man/systemd.mount.xml | 19 +++++++++++++++ src/fstab-generator/fstab-generator.c | 24 +++++++++++++++---- .../mnt-onfailure.mount | 1 + .../mnt-onfailuremulti.mount | 1 + .../mnt-onfailure.mount | 13 ++++++++++ .../mnt-onfailuremulti.mount | 13 ++++++++++ .../mnt-onfailure.mount | 1 + .../mnt-onfailuremulti.mount | 1 + .../mnt-onfailure.mount | 13 ++++++++++ .../mnt-onfailuremulti.mount | 13 ++++++++++ .../test-18-options.fstab.input | 2 ++ 11 files changed, 96 insertions(+), 5 deletions(-) create mode 120000 test/test-fstab-generator/test-18-options.fstab.expected.sysroot/local-fs.target.requires/mnt-onfailure.mount create mode 120000 test/test-fstab-generator/test-18-options.fstab.expected.sysroot/local-fs.target.requires/mnt-onfailuremulti.mount create mode 100644 test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-onfailure.mount create mode 100644 test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-onfailuremulti.mount create mode 120000 test/test-fstab-generator/test-18-options.fstab.expected/local-fs.target.requires/mnt-onfailure.mount create mode 120000 test/test-fstab-generator/test-18-options.fstab.expected/local-fs.target.requires/mnt-onfailuremulti.mount create mode 100644 test/test-fstab-generator/test-18-options.fstab.expected/mnt-onfailure.mount create mode 100644 test/test-fstab-generator/test-18-options.fstab.expected/mnt-onfailuremulti.mount diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml index 035069437b..107c07aca9 100644 --- a/man/systemd.mount.xml +++ b/man/systemd.mount.xml @@ -284,6 +284,25 @@ + + + + In the created mount unit, configures an + OnFailure= dependency. The argument is a unit name, see + above. This option may be specified more than + once. See OnFailure= in + systemd.unit5 + for details. + + Note that this option always applies to the created mount + unit only, regardless of whether + has been specified. + + Note that this option is silently ignored on swap entries. + + + + diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index b723868f51..82bbcbcc4d 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -341,6 +341,7 @@ static int write_dependency( const char *where, const char *opts, const char *filter, + const char *suffix, const char* const *unit_settings) { _cleanup_strv_free_ char **unit_names = NULL; @@ -360,7 +361,9 @@ static int write_dependency( STRV_FOREACH(s, unit_names) { _cleanup_free_ char *mangled = NULL; - r = unit_name_mangle_with_suffix(*s, "as dependency", 0, ".mount", &mangled); + r = suffix ? + unit_name_mangle_with_suffix(*s, "as dependency", 0, suffix, &mangled) : + unit_name_mangle(*s, 0, &mangled); if (r < 0) return log_error_errno(r, "Failed to generate dependency unit name for '%s': %m", where); @@ -376,22 +379,24 @@ static int write_dependency( static int write_after(FILE *f, const char *where, const char *opts) { return write_dependency(f, where, opts, - "x-systemd.after\0", STRV_MAKE_CONST("After")); + "x-systemd.after\0", /* suffix= */ ".mount", STRV_MAKE_CONST("After")); } static int write_requires_after(FILE *f, const char *where, const char *opts) { return write_dependency(f, where, opts, - "x-systemd.requires\0", STRV_MAKE_CONST("Requires", "After")); + "x-systemd.requires\0", /* suffix= */ ".mount", + STRV_MAKE_CONST("Requires", "After")); } static int write_wants_after(FILE *f, const char *where, const char *opts) { return write_dependency(f, where, opts, - "x-systemd.wants\0", STRV_MAKE_CONST("Wants", "After")); + "x-systemd.wants\0", /* suffix= */ ".mount", + STRV_MAKE_CONST("Wants", "After")); } static int write_before(FILE *f, const char *where, const char *opts) { return write_dependency(f, where, opts, - "x-systemd.before\0", STRV_MAKE_CONST("Before")); + "x-systemd.before\0", /* suffix= */ ".mount", STRV_MAKE_CONST("Before")); } static int write_mounts_for( @@ -426,6 +431,11 @@ static int write_mounts_for( return 0; } +static int write_on_failure(FILE *f, const char *where, const char *opts) { + return write_dependency(f, where, opts, + "x-systemd.onfailure\0", /* suffix= */ NULL, STRV_MAKE_CONST("OnFailure")); +} + static int write_extra_dependencies(FILE *f, const char *where, const char *opts) { int r; @@ -460,6 +470,10 @@ static int write_extra_dependencies(FILE *f, const char *where, const char *opts if (r < 0) return r; + r = write_on_failure(f, where, opts); + if (r < 0) + return r; + return 0; } diff --git a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/local-fs.target.requires/mnt-onfailure.mount b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/local-fs.target.requires/mnt-onfailure.mount new file mode 120000 index 0000000000..3802a7f058 --- /dev/null +++ b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/local-fs.target.requires/mnt-onfailure.mount @@ -0,0 +1 @@ +../mnt-onfailure.mount \ No newline at end of file diff --git a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/local-fs.target.requires/mnt-onfailuremulti.mount b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/local-fs.target.requires/mnt-onfailuremulti.mount new file mode 120000 index 0000000000..7d31474405 --- /dev/null +++ b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/local-fs.target.requires/mnt-onfailuremulti.mount @@ -0,0 +1 @@ +../mnt-onfailuremulti.mount \ No newline at end of file diff --git a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-onfailure.mount b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-onfailure.mount new file mode 100644 index 0000000000..65aaf7c45b --- /dev/null +++ b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-onfailure.mount @@ -0,0 +1,13 @@ +# Automatically generated by systemd-fstab-generator + +[Unit] +Documentation=man:fstab(5) man:systemd-fstab-generator(8) +SourcePath=/etc/fstab +OnFailure=foo.service +Before=local-fs.target +After=blockdev@dev-sdx18.target + +[Mount] +What=/dev/sdx18 +Where=/mnt/onfailure +Options=x-systemd.onfailure=foo.service diff --git a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-onfailuremulti.mount b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-onfailuremulti.mount new file mode 100644 index 0000000000..387cb02f46 --- /dev/null +++ b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-onfailuremulti.mount @@ -0,0 +1,13 @@ +# Automatically generated by systemd-fstab-generator + +[Unit] +Documentation=man:fstab(5) man:systemd-fstab-generator(8) +SourcePath=/etc/fstab +OnFailure=foo.service bar.service +Before=local-fs.target +After=blockdev@dev-sdx19.target + +[Mount] +What=/dev/sdx19 +Where=/mnt/onfailuremulti +Options=x-systemd.onfailure=foo.service,x-systemd.onfailure=bar.service diff --git a/test/test-fstab-generator/test-18-options.fstab.expected/local-fs.target.requires/mnt-onfailure.mount b/test/test-fstab-generator/test-18-options.fstab.expected/local-fs.target.requires/mnt-onfailure.mount new file mode 120000 index 0000000000..3802a7f058 --- /dev/null +++ b/test/test-fstab-generator/test-18-options.fstab.expected/local-fs.target.requires/mnt-onfailure.mount @@ -0,0 +1 @@ +../mnt-onfailure.mount \ No newline at end of file diff --git a/test/test-fstab-generator/test-18-options.fstab.expected/local-fs.target.requires/mnt-onfailuremulti.mount b/test/test-fstab-generator/test-18-options.fstab.expected/local-fs.target.requires/mnt-onfailuremulti.mount new file mode 120000 index 0000000000..7d31474405 --- /dev/null +++ b/test/test-fstab-generator/test-18-options.fstab.expected/local-fs.target.requires/mnt-onfailuremulti.mount @@ -0,0 +1 @@ +../mnt-onfailuremulti.mount \ No newline at end of file diff --git a/test/test-fstab-generator/test-18-options.fstab.expected/mnt-onfailure.mount b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-onfailure.mount new file mode 100644 index 0000000000..65aaf7c45b --- /dev/null +++ b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-onfailure.mount @@ -0,0 +1,13 @@ +# Automatically generated by systemd-fstab-generator + +[Unit] +Documentation=man:fstab(5) man:systemd-fstab-generator(8) +SourcePath=/etc/fstab +OnFailure=foo.service +Before=local-fs.target +After=blockdev@dev-sdx18.target + +[Mount] +What=/dev/sdx18 +Where=/mnt/onfailure +Options=x-systemd.onfailure=foo.service diff --git a/test/test-fstab-generator/test-18-options.fstab.expected/mnt-onfailuremulti.mount b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-onfailuremulti.mount new file mode 100644 index 0000000000..387cb02f46 --- /dev/null +++ b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-onfailuremulti.mount @@ -0,0 +1,13 @@ +# Automatically generated by systemd-fstab-generator + +[Unit] +Documentation=man:fstab(5) man:systemd-fstab-generator(8) +SourcePath=/etc/fstab +OnFailure=foo.service bar.service +Before=local-fs.target +After=blockdev@dev-sdx19.target + +[Mount] +What=/dev/sdx19 +Where=/mnt/onfailuremulti +Options=x-systemd.onfailure=foo.service,x-systemd.onfailure=bar.service diff --git a/test/test-fstab-generator/test-18-options.fstab.input b/test/test-fstab-generator/test-18-options.fstab.input index 2112d68828..d5b48f3c0e 100644 --- a/test/test-fstab-generator/test-18-options.fstab.input +++ b/test/test-fstab-generator/test-18-options.fstab.input @@ -15,3 +15,5 @@ /dev/sdx15 /mnt/noauto auto noauto 0 0 /dev/sdx16 /mnt/nofail auto nofail 0 0 /dev/sdx17 /mnt/wantedby-automount auto x-systemd.wanted-by=foo.service,x-systemd.automount 0 0 +/dev/sdx18 /mnt/onfailure auto x-systemd.onfailure=foo.service 0 0 +/dev/sdx19 /mnt/onfailuremulti auto x-systemd.onfailure=foo.service,x-systemd.onfailure=bar.service 0 0