1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include "alloc-util.h"
11 #include "fstab-util.h"
12 #include "generator.h"
14 #include "main-func.h"
16 #include "mount-setup.h"
17 #include "mount-util.h"
18 #include "mountpoint-util.h"
19 #include "parse-util.h"
20 #include "path-util.h"
21 #include "proc-cmdline.h"
23 #include "specifier.h"
24 #include "stat-util.h"
25 #include "string-util.h"
27 #include "unit-name.h"
30 #include "volatile-util.h"
32 typedef enum MountpointFlags
{
41 static const char *arg_dest
= NULL
;
42 static const char *arg_dest_late
= NULL
;
43 static bool arg_fstab_enabled
= true;
44 static bool arg_swap_enabled
= true;
45 static char *arg_root_what
= NULL
;
46 static char *arg_root_fstype
= NULL
;
47 static char *arg_root_options
= NULL
;
48 static char *arg_root_hash
= NULL
;
49 static int arg_root_rw
= -1;
50 static char *arg_usr_what
= NULL
;
51 static char *arg_usr_fstype
= NULL
;
52 static char *arg_usr_options
= NULL
;
53 static VolatileMode arg_volatile_mode
= _VOLATILE_MODE_INVALID
;
55 STATIC_DESTRUCTOR_REGISTER(arg_root_what
, freep
);
56 STATIC_DESTRUCTOR_REGISTER(arg_root_fstype
, freep
);
57 STATIC_DESTRUCTOR_REGISTER(arg_root_options
, freep
);
58 STATIC_DESTRUCTOR_REGISTER(arg_root_hash
, freep
);
59 STATIC_DESTRUCTOR_REGISTER(arg_usr_what
, freep
);
60 STATIC_DESTRUCTOR_REGISTER(arg_usr_fstype
, freep
);
61 STATIC_DESTRUCTOR_REGISTER(arg_usr_options
, freep
);
63 static int write_options(FILE *f
, const char *options
) {
64 _cleanup_free_
char *o
= NULL
;
69 if (streq(options
, "defaults"))
72 o
= specifier_escape(options
);
76 fprintf(f
, "Options=%s\n", o
);
80 static int write_what(FILE *f
, const char *what
) {
81 _cleanup_free_
char *w
= NULL
;
83 w
= specifier_escape(what
);
87 fprintf(f
, "What=%s\n", w
);
94 MountpointFlags flags
) {
96 _cleanup_free_
char *name
= NULL
;
97 _cleanup_fclose_
FILE *f
= NULL
;
103 if (!arg_swap_enabled
) {
104 log_info("Swap unit generation disabled on kernel command line, ignoring fstab swap entry for %s.", what
);
108 if (access("/proc/swaps", F_OK
) < 0) {
109 log_info("Swap not supported, ignoring fstab swap entry for %s.", what
);
113 if (detect_container() > 0) {
114 log_info("Running in a container, ignoring fstab swap entry for %s.", what
);
118 r
= unit_name_from_path(what
, ".swap", &name
);
120 return log_error_errno(r
, "Failed to generate unit name: %m");
122 r
= generator_open_unit_file(arg_dest
, fstab_path(), name
, &f
);
128 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n"
132 r
= generator_write_blockdev_dependency(f
, what
);
140 r
= write_what(f
, what
);
144 r
= write_options(f
, me
->mnt_opts
);
148 r
= fflush_and_check(f
);
150 return log_error_errno(r
, "Failed to write unit file %s: %m", name
);
152 /* use what as where, to have a nicer error message */
153 r
= generator_write_timeouts(arg_dest
, what
, what
, me
->mnt_opts
, NULL
);
157 if (flags
& MAKEFS
) {
158 r
= generator_hook_up_mkswap(arg_dest
, what
);
164 /* TODO: swap devices must be wiped and recreated */
165 log_warning("%s: growing swap devices is currently unsupported.", what
);
167 if (!(flags
& NOAUTO
)) {
168 r
= generator_add_symlink(arg_dest
, SPECIAL_SWAP_TARGET
,
169 (flags
& NOFAIL
) ? "wants" : "requires", name
);
177 static bool mount_is_network(struct mntent
*me
) {
180 return fstab_test_option(me
->mnt_opts
, "_netdev\0") ||
181 fstype_is_network(me
->mnt_type
);
184 static bool mount_in_initrd(struct mntent
*me
) {
187 return fstab_test_option(me
->mnt_opts
, "x-initrd.mount\0") ||
188 streq(me
->mnt_dir
, "/usr");
191 static int write_timeout(
196 const char *variable
) {
198 _cleanup_free_
char *timeout
= NULL
;
199 char timespan
[FORMAT_TIMESPAN_MAX
];
203 r
= fstab_filter_options(opts
, filter
, NULL
, &timeout
, NULL
);
205 return log_warning_errno(r
, "Failed to parse options: %m");
209 r
= parse_sec_fix_0(timeout
, &u
);
211 log_warning("Failed to parse timeout for %s, ignoring: %s", where
, timeout
);
215 fprintf(f
, "%s=%s\n", variable
, format_timespan(timespan
, sizeof(timespan
), u
, 0));
220 static int write_idle_timeout(FILE *f
, const char *where
, const char *opts
) {
221 return write_timeout(f
, where
, opts
,
222 "x-systemd.idle-timeout\0", "TimeoutIdleSec");
225 static int write_mount_timeout(FILE *f
, const char *where
, const char *opts
) {
226 return write_timeout(f
, where
, opts
,
227 "x-systemd.mount-timeout\0", "TimeoutSec");
230 static int write_dependency(
234 const char *format
) {
236 _cleanup_strv_free_
char **names
= NULL
, **units
= NULL
;
237 _cleanup_free_
char *res
= NULL
;
244 r
= fstab_extract_values(opts
, filter
, &names
);
246 return log_warning_errno(r
, "Failed to parse options: %m");
250 STRV_FOREACH(s
, names
) {
253 r
= unit_name_mangle_with_suffix(*s
, "as dependency", 0, ".mount", &x
);
255 return log_error_errno(r
, "Failed to generate unit name: %m");
257 r
= strv_consume(&units
, x
);
263 res
= strv_join(units
, " ");
267 DISABLE_WARNING_FORMAT_NONLITERAL
;
268 fprintf(f
, format
, res
);
275 static int write_after(FILE *f
, const char *opts
) {
276 return write_dependency(f
, opts
,
277 "x-systemd.after", "After=%1$s\n");
280 static int write_requires_after(FILE *f
, const char *opts
) {
281 return write_dependency(f
, opts
,
282 "x-systemd.requires", "After=%1$s\nRequires=%1$s\n");
285 static int write_before(FILE *f
, const char *opts
) {
286 return write_dependency(f
, opts
,
287 "x-systemd.before", "Before=%1$s\n");
290 static int write_requires_mounts_for(FILE *f
, const char *opts
) {
291 _cleanup_strv_free_
char **paths
= NULL
, **paths_escaped
= NULL
;
292 _cleanup_free_
char *res
= NULL
;
298 r
= fstab_extract_values(opts
, "x-systemd.requires-mounts-for", &paths
);
300 return log_warning_errno(r
, "Failed to parse options: %m");
304 r
= specifier_escape_strv(paths
, &paths_escaped
);
306 return log_error_errno(r
, "Failed to escape paths: %m");
308 res
= strv_join(paths_escaped
, " ");
312 fprintf(f
, "RequiresMountsFor=%s\n", res
);
317 static int write_extra_dependencies(FILE *f
, const char *opts
) {
323 r
= write_after(f
, opts
);
326 r
= write_requires_after(f
, opts
);
329 r
= write_before(f
, opts
);
332 r
= write_requires_mounts_for(f
, opts
);
340 static int add_mount(
344 const char *original_where
,
348 MountpointFlags flags
,
350 const char *source
) {
354 *automount_name
= NULL
,
356 *where_escaped
= NULL
;
357 _cleanup_strv_free_
char **wanted_by
= NULL
, **required_by
= NULL
;
358 _cleanup_fclose_
FILE *f
= NULL
;
367 if (streq_ptr(fstype
, "autofs"))
370 if (!is_path(where
)) {
371 log_warning("Mount point %s is not a valid path, ignoring.", where
);
375 if (mount_point_is_api(where
) ||
376 mount_point_ignore(where
))
379 r
= fstab_extract_values(opts
, "x-systemd.wanted-by", &wanted_by
);
383 r
= fstab_extract_values(opts
, "x-systemd.required-by", &required_by
);
387 if (path_equal(where
, "/")) {
389 log_warning("Ignoring \"noauto\" for root device");
391 log_warning("Ignoring \"nofail\" for root device");
392 if (flags
& AUTOMOUNT
)
393 log_warning("Ignoring automount option for root device");
394 if (!strv_isempty(wanted_by
))
395 log_warning("Ignoring \"x-systemd.wanted-by=\" for root device");
396 if (!strv_isempty(required_by
))
397 log_warning("Ignoring \"x-systemd.required-by=\" for root device");
399 required_by
= strv_free(required_by
);
400 wanted_by
= strv_free(wanted_by
);
401 SET_FLAG(flags
, NOAUTO
| NOFAIL
| AUTOMOUNT
, false);
404 r
= unit_name_from_path(where
, ".mount", &name
);
406 return log_error_errno(r
, "Failed to generate unit name: %m");
408 r
= generator_open_unit_file(dest
, fstab_path(), name
, &f
);
414 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n"
418 if (STRPTR_IN_SET(fstype
, "nfs", "nfs4") && !(flags
& AUTOMOUNT
) &&
419 fstab_test_yes_no_option(opts
, "bg\0" "fg\0")) {
420 /* The default retry timeout that mount.nfs uses for 'bg' mounts
421 * is 10000 minutes, where as it uses 2 minutes for 'fg' mounts.
422 * As we are making 'bg' mounts look like an 'fg' mount to
423 * mount.nfs (so systemd can manage the job-control aspects of 'bg'),
424 * we need to explicitly preserve that default, and also ensure
425 * the systemd mount-timeout doesn't interfere.
426 * By placing these options first, they can be over-ridden by
427 * settings in /etc/fstab. */
428 opts
= strjoina("x-systemd.mount-timeout=infinity,retry=10000,nofail,", opts
, ",fg");
429 SET_FLAG(flags
, NOFAIL
, true);
432 r
= write_extra_dependencies(f
, opts
);
437 r
= generator_write_fsck_deps(f
, dest
, what
, where
, fstype
);
442 r
= generator_write_blockdev_dependency(f
, what
);
451 fprintf(f
, "# Canonicalized from %s\n", original_where
);
453 where_escaped
= specifier_escape(where
);
456 fprintf(f
, "Where=%s\n", where_escaped
);
458 r
= write_what(f
, what
);
462 if (!isempty(fstype
) && !streq(fstype
, "auto")) {
463 _cleanup_free_
char *t
;
465 t
= specifier_escape(fstype
);
469 fprintf(f
, "Type=%s\n", t
);
472 r
= generator_write_timeouts(dest
, what
, where
, opts
, &filtered
);
476 r
= generator_write_device_deps(dest
, what
, where
, opts
);
480 r
= write_mount_timeout(f
, where
, opts
);
484 r
= write_options(f
, filtered
);
489 fprintf(f
, "ReadWriteOnly=yes\n");
491 r
= fflush_and_check(f
);
493 return log_error_errno(r
, "Failed to write unit file %s: %m", name
);
495 if (flags
& MAKEFS
) {
496 r
= generator_hook_up_mkfs(dest
, what
, where
, fstype
);
501 if (flags
& GROWFS
) {
502 r
= generator_hook_up_growfs(dest
, where
, post
);
507 if (!FLAGS_SET(flags
, AUTOMOUNT
)) {
508 if (!FLAGS_SET(flags
, NOAUTO
) && strv_isempty(wanted_by
) && strv_isempty(required_by
)) {
509 r
= generator_add_symlink(dest
, post
,
510 (flags
& NOFAIL
) ? "wants" : "requires", name
);
516 STRV_FOREACH(s
, wanted_by
) {
517 r
= generator_add_symlink(dest
, *s
, "wants", name
);
522 STRV_FOREACH(s
, required_by
) {
523 r
= generator_add_symlink(dest
, *s
, "requires", name
);
529 r
= unit_name_from_path(where
, ".automount", &automount_name
);
531 return log_error_errno(r
, "Failed to generate unit name: %m");
535 r
= generator_open_unit_file(dest
, fstab_path(), automount_name
, &f
);
542 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
551 r
= write_idle_timeout(f
, where
, opts
);
555 r
= fflush_and_check(f
);
557 return log_error_errno(r
, "Failed to write unit file %s: %m", automount_name
);
559 r
= generator_add_symlink(dest
, post
,
560 (flags
& NOFAIL
) ? "wants" : "requires", automount_name
);
568 static int parse_fstab(bool initrd
) {
569 _cleanup_endmntent_
FILE *f
= NULL
;
574 fstab
= initrd
? "/sysroot/etc/fstab" : fstab_path();
575 log_debug("Parsing %s...", fstab
);
577 f
= setmntent(fstab
, "re");
582 return log_error_errno(errno
, "Failed to open %s: %m", fstab
);
585 while ((me
= getmntent(f
))) {
586 _cleanup_free_
char *where
= NULL
, *what
= NULL
, *canonical_where
= NULL
;
587 bool makefs
, growfs
, noauto
, nofail
, rwonly
;
590 if (initrd
&& !mount_in_initrd(me
))
593 what
= fstab_node_to_udev_node(me
->mnt_fsname
);
597 if (is_device_path(what
) && path_is_read_only_fs("/sys") > 0) {
598 log_info("Running in a container, ignoring fstab device entry for %s.", what
);
602 where
= strdup(me
->mnt_dir
);
606 if (is_path(where
)) {
607 path_simplify(where
, false);
609 /* Follow symlinks here; see 5261ba901845c084de5a8fd06500ed09bfb0bd80 which makes sense for
610 * mount units, but causes problems since it historically worked to have symlinks in e.g.
611 * /etc/fstab. So we canonicalize here. Note that we use CHASE_NONEXISTENT to handle the case
612 * where a symlink refers to another mount target; this works assuming the sub-mountpoint
613 * target is the final directory. */
614 r
= chase_symlinks(where
, initrd
? "/sysroot" : NULL
,
615 CHASE_PREFIX_ROOT
| CHASE_NONEXISTENT
,
616 &canonical_where
, NULL
);
617 if (r
< 0) /* If we can't canonicalize we continue on as if it wasn't a symlink */
618 log_debug_errno(r
, "Failed to read symlink target for %s, ignoring: %m", where
);
619 else if (streq(canonical_where
, where
)) /* If it was fully canonicalized, suppress the change */
620 canonical_where
= mfree(canonical_where
);
622 log_debug("Canonicalized what=%s where=%s to %s", what
, where
, canonical_where
);
625 makefs
= fstab_test_option(me
->mnt_opts
, "x-systemd.makefs\0");
626 growfs
= fstab_test_option(me
->mnt_opts
, "x-systemd.growfs\0");
627 rwonly
= fstab_test_option(me
->mnt_opts
, "x-systemd.rw-only\0");
628 noauto
= fstab_test_yes_no_option(me
->mnt_opts
, "noauto\0" "auto\0");
629 nofail
= fstab_test_yes_no_option(me
->mnt_opts
, "nofail\0" "fail\0");
631 log_debug("Found entry what=%s where=%s type=%s makefs=%s growfs=%s noauto=%s nofail=%s",
632 what
, where
, me
->mnt_type
,
633 yes_no(makefs
), yes_no(growfs
),
634 yes_no(noauto
), yes_no(nofail
));
636 if (streq(me
->mnt_type
, "swap"))
637 k
= add_swap(what
, me
,
638 makefs
*MAKEFS
| growfs
*GROWFS
| noauto
*NOAUTO
| nofail
*NOFAIL
);
643 automount
= fstab_test_option(me
->mnt_opts
,
644 "comment=systemd.automount\0"
645 "x-systemd.automount\0");
647 post
= SPECIAL_INITRD_FS_TARGET
;
648 else if (mount_is_network(me
))
649 post
= SPECIAL_REMOTE_FS_TARGET
;
651 post
= SPECIAL_LOCAL_FS_TARGET
;
653 k
= add_mount(arg_dest
,
655 canonical_where
?: where
,
656 canonical_where
? where
: NULL
,
660 makefs
*MAKEFS
| growfs
*GROWFS
| noauto
*NOAUTO
| nofail
*NOFAIL
| automount
*AUTOMOUNT
| rwonly
*RWONLY
,
672 static int add_sysroot_mount(void) {
673 _cleanup_free_
char *what
= NULL
;
677 if (isempty(arg_root_what
)) {
678 log_debug("Could not find a root= entry on the kernel command line.");
682 if (streq(arg_root_what
, "gpt-auto")) {
683 /* This is handled by the gpt-auto generator */
684 log_debug("Skipping root directory handling, as gpt-auto was requested.");
688 if (path_equal(arg_root_what
, "/dev/nfs")) {
689 /* This is handled by the kernel or the initrd */
690 log_debug("Skipping root directory handling, as /dev/nfs was requested.");
694 what
= fstab_node_to_udev_node(arg_root_what
);
698 if (!arg_root_options
)
699 opts
= arg_root_rw
> 0 ? "rw" : "ro";
700 else if (arg_root_rw
>= 0 ||
701 !fstab_test_option(arg_root_options
, "ro\0" "rw\0"))
702 opts
= strjoina(arg_root_options
, ",", arg_root_rw
> 0 ? "rw" : "ro");
704 opts
= arg_root_options
;
706 log_debug("Found entry what=%s where=/sysroot type=%s", what
, strna(arg_root_fstype
));
708 if (is_device_path(what
)) {
709 r
= generator_write_initrd_root_device_deps(arg_dest
, what
);
714 return add_mount(arg_dest
,
720 is_device_path(what
) ? 1 : 0, /* passno */
721 0, /* makefs off, growfs off, noauto off, nofail off, automount off */
722 SPECIAL_INITRD_ROOT_FS_TARGET
,
726 static int add_sysroot_usr_mount(void) {
727 _cleanup_free_
char *what
= NULL
;
730 if (!arg_usr_what
&& !arg_usr_fstype
&& !arg_usr_options
)
733 if (arg_root_what
&& !arg_usr_what
) {
734 /* Copy over the root device, in case the /usr mount just differs in a mount option (consider btrfs subvolumes) */
735 arg_usr_what
= strdup(arg_root_what
);
740 if (arg_root_fstype
&& !arg_usr_fstype
) {
741 arg_usr_fstype
= strdup(arg_root_fstype
);
746 if (arg_root_options
&& !arg_usr_options
) {
747 arg_usr_options
= strdup(arg_root_options
);
748 if (!arg_usr_options
)
755 what
= fstab_node_to_udev_node(arg_usr_what
);
759 if (!arg_usr_options
)
760 opts
= arg_root_rw
> 0 ? "rw" : "ro";
761 else if (!fstab_test_option(arg_usr_options
, "ro\0" "rw\0"))
762 opts
= strjoina(arg_usr_options
, ",", arg_root_rw
> 0 ? "rw" : "ro");
764 opts
= arg_usr_options
;
766 log_debug("Found entry what=%s where=/sysroot/usr type=%s", what
, strna(arg_usr_fstype
));
767 return add_mount(arg_dest
,
773 is_device_path(what
) ? 1 : 0, /* passno */
775 SPECIAL_INITRD_FS_TARGET
,
779 static int add_volatile_root(void) {
781 /* Let's add in systemd-remount-volatile.service which will remount the root device to tmpfs if this is
782 * requested (or as an overlayfs), leaving only /usr from the root mount inside. */
784 if (!IN_SET(arg_volatile_mode
, VOLATILE_YES
, VOLATILE_OVERLAY
))
787 return generator_add_symlink(arg_dest
, SPECIAL_INITRD_ROOT_FS_TARGET
, "requires",
788 SYSTEM_DATA_UNIT_PATH
"/" SPECIAL_VOLATILE_ROOT_SERVICE
);
791 static int add_volatile_var(void) {
793 if (arg_volatile_mode
!= VOLATILE_STATE
)
796 /* If requested, mount /var as tmpfs, but do so only if there's nothing else defined for this. */
798 return add_mount(arg_dest_late
,
803 "mode=0755" TMPFS_LIMITS_VAR
,
806 SPECIAL_LOCAL_FS_TARGET
,
810 static int parse_proc_cmdline_item(const char *key
, const char *value
, void *data
) {
813 /* root=, usr=, usrfstype= and roofstype= may occur more than once, the last
814 * instance should take precedence. In the case of multiple rootflags=
815 * or usrflags= the arguments should be concatenated */
817 if (STR_IN_SET(key
, "fstab", "rd.fstab")) {
819 r
= value
? parse_boolean(value
) : 1;
821 log_warning("Failed to parse fstab switch %s. Ignoring.", value
);
823 arg_fstab_enabled
= r
;
825 } else if (streq(key
, "root")) {
827 if (proc_cmdline_value_missing(key
, value
))
830 if (free_and_strdup(&arg_root_what
, value
) < 0)
833 } else if (streq(key
, "rootfstype")) {
835 if (proc_cmdline_value_missing(key
, value
))
838 if (free_and_strdup(&arg_root_fstype
, value
) < 0)
841 } else if (streq(key
, "rootflags")) {
843 if (proc_cmdline_value_missing(key
, value
))
846 if (!strextend_with_separator(&arg_root_options
, ",", value
))
849 } else if (streq(key
, "roothash")) {
851 if (proc_cmdline_value_missing(key
, value
))
854 if (free_and_strdup(&arg_root_hash
, value
) < 0)
857 } else if (streq(key
, "mount.usr")) {
859 if (proc_cmdline_value_missing(key
, value
))
862 if (free_and_strdup(&arg_usr_what
, value
) < 0)
865 } else if (streq(key
, "mount.usrfstype")) {
867 if (proc_cmdline_value_missing(key
, value
))
870 if (free_and_strdup(&arg_usr_fstype
, value
) < 0)
873 } else if (streq(key
, "mount.usrflags")) {
875 if (proc_cmdline_value_missing(key
, value
))
878 if (!strextend_with_separator(&arg_usr_options
, ",", value
))
881 } else if (streq(key
, "rw") && !value
)
883 else if (streq(key
, "ro") && !value
)
885 else if (streq(key
, "systemd.volatile")) {
889 m
= volatile_mode_from_string(value
);
891 log_warning("Failed to parse systemd.volatile= argument: %s", value
);
893 arg_volatile_mode
= m
;
895 arg_volatile_mode
= VOLATILE_YES
;
897 } else if (streq(key
, "systemd.swap")) {
899 r
= value
? parse_boolean(value
) : 1;
901 log_warning("Failed to parse systemd.swap switch %s. Ignoring.", value
);
903 arg_swap_enabled
= r
;
909 static int determine_root(void) {
910 /* If we have a root hash but no root device then Verity is used, and we use the "root" DM device as root. */
918 arg_root_what
= strdup("/dev/mapper/root");
922 log_info("Using verity root device %s.", arg_root_what
);
927 static int run(const char *dest
, const char *dest_early
, const char *dest_late
) {
928 int r
, r2
= 0, r3
= 0;
930 assert_se(arg_dest
= dest
);
931 assert_se(arg_dest_late
= dest_late
);
933 r
= proc_cmdline_parse(parse_proc_cmdline_item
, NULL
, 0);
935 log_warning_errno(r
, "Failed to parse kernel command line, ignoring: %m");
937 (void) determine_root();
939 /* Always honour root= and usr= in the kernel command line if we are in an initrd */
941 r
= add_sysroot_mount();
943 r2
= add_sysroot_usr_mount();
945 r3
= add_volatile_root();
947 r
= add_volatile_var();
949 /* Honour /etc/fstab only when that's enabled */
950 if (arg_fstab_enabled
) {
951 /* Parse the local /etc/fstab, possibly from the initrd */
952 r2
= parse_fstab(false);
954 /* If running in the initrd also parse the /etc/fstab from the host */
956 r3
= parse_fstab(true);
958 r3
= generator_enable_remount_fs_service(arg_dest
);
961 return r
< 0 ? r
: r2
< 0 ? r2
: r3
;
964 DEFINE_MAIN_GENERATOR_FUNCTION(run
);