1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2012 Lennart Poettering
13 #include <stdio_ext.h>
15 #include "alloc-util.h"
19 #include "fstab-util.h"
20 #include "generator.h"
23 #include "mount-setup.h"
24 #include "mount-util.h"
25 #include "parse-util.h"
26 #include "path-util.h"
27 #include "proc-cmdline.h"
29 #include "specifier.h"
30 #include "stat-util.h"
31 #include "string-util.h"
33 #include "unit-name.h"
36 #include "volatile-util.h"
38 typedef enum MountpointFlags
{
46 static const char *arg_dest
= "/tmp";
47 static const char *arg_dest_late
= "/tmp";
48 static bool arg_fstab_enabled
= true;
49 static char *arg_root_what
= NULL
;
50 static char *arg_root_fstype
= NULL
;
51 static char *arg_root_options
= NULL
;
52 static char *arg_root_hash
= NULL
;
53 static int arg_root_rw
= -1;
54 static char *arg_usr_what
= NULL
;
55 static char *arg_usr_fstype
= NULL
;
56 static char *arg_usr_options
= NULL
;
57 static VolatileMode arg_volatile_mode
= _VOLATILE_MODE_INVALID
;
59 static int write_options(FILE *f
, const char *options
) {
60 _cleanup_free_
char *o
= NULL
;
65 if (streq(options
, "defaults"))
68 o
= specifier_escape(options
);
72 fprintf(f
, "Options=%s\n", o
);
76 static int write_what(FILE *f
, const char *what
) {
77 _cleanup_free_
char *w
= NULL
;
79 w
= specifier_escape(what
);
83 fprintf(f
, "What=%s\n", w
);
90 MountpointFlags flags
) {
92 _cleanup_free_
char *name
= NULL
;
93 _cleanup_fclose_
FILE *f
= NULL
;
99 if (access("/proc/swaps", F_OK
) < 0) {
100 log_info("Swap not supported, ignoring fstab swap entry for %s.", what
);
104 if (detect_container() > 0) {
105 log_info("Running in a container, ignoring fstab swap entry for %s.", what
);
109 r
= unit_name_from_path(what
, ".swap", &name
);
111 return log_error_errno(r
, "Failed to generate unit name: %m");
113 r
= generator_open_unit_file(arg_dest
, "/etc/fstab", name
, &f
);
117 fputs("# Automatically generated by systemd-fstab-generator\n\n"
119 "SourcePath=/etc/fstab\n"
120 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n"
123 r
= write_what(f
, what
);
127 r
= write_options(f
, me
->mnt_opts
);
131 r
= fflush_and_check(f
);
133 return log_error_errno(r
, "Failed to write unit file %s: %m", name
);
135 /* use what as where, to have a nicer error message */
136 r
= generator_write_timeouts(arg_dest
, what
, what
, me
->mnt_opts
, NULL
);
140 if (flags
& MAKEFS
) {
141 r
= generator_hook_up_mkswap(arg_dest
, what
);
147 /* TODO: swap devices must be wiped and recreated */
148 log_warning("%s: growing swap devices is currently unsupported.", what
);
150 if (!(flags
& NOAUTO
)) {
151 r
= generator_add_symlink(arg_dest
, SPECIAL_SWAP_TARGET
,
152 (flags
& NOFAIL
) ? "wants" : "requires", name
);
160 static bool mount_is_network(struct mntent
*me
) {
163 return fstab_test_option(me
->mnt_opts
, "_netdev\0") ||
164 fstype_is_network(me
->mnt_type
);
167 static bool mount_in_initrd(struct mntent
*me
) {
170 return fstab_test_option(me
->mnt_opts
, "x-initrd.mount\0") ||
171 streq(me
->mnt_dir
, "/usr");
174 static int write_timeout(FILE *f
, const char *where
, const char *opts
,
175 const char *filter
, const char *variable
) {
176 _cleanup_free_
char *timeout
= NULL
;
177 char timespan
[FORMAT_TIMESPAN_MAX
];
181 r
= fstab_filter_options(opts
, filter
, NULL
, &timeout
, NULL
);
183 return log_warning_errno(r
, "Failed to parse options: %m");
187 r
= parse_sec_fix_0(timeout
, &u
);
189 log_warning("Failed to parse timeout for %s, ignoring: %s", where
, timeout
);
193 fprintf(f
, "%s=%s\n", variable
, format_timespan(timespan
, sizeof(timespan
), u
, 0));
198 static int write_idle_timeout(FILE *f
, const char *where
, const char *opts
) {
199 return write_timeout(f
, where
, opts
,
200 "x-systemd.idle-timeout\0", "TimeoutIdleSec");
203 static int write_mount_timeout(FILE *f
, const char *where
, const char *opts
) {
204 return write_timeout(f
, where
, opts
,
205 "x-systemd.mount-timeout\0", "TimeoutSec");
208 static int write_dependency(FILE *f
, const char *opts
,
209 const char *filter
, const char *format
) {
210 _cleanup_strv_free_
char **names
= NULL
, **units
= NULL
;
211 _cleanup_free_
char *res
= NULL
;
218 r
= fstab_extract_values(opts
, filter
, &names
);
220 return log_warning_errno(r
, "Failed to parse options: %m");
224 STRV_FOREACH(s
, names
) {
227 r
= unit_name_mangle_with_suffix(*s
, 0, ".mount", &x
);
229 return log_error_errno(r
, "Failed to generate unit name: %m");
230 r
= strv_consume(&units
, x
);
236 res
= strv_join(units
, " ");
239 #pragma GCC diagnostic push
240 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
241 fprintf(f
, format
, res
);
242 #pragma GCC diagnostic pop
248 static int write_after(FILE *f
, const char *opts
) {
249 return write_dependency(f
, opts
, "x-systemd.after", "After=%1$s\n");
252 static int write_requires_after(FILE *f
, const char *opts
) {
253 return write_dependency(f
, opts
,
254 "x-systemd.requires", "After=%1$s\nRequires=%1$s\n");
257 static int write_before(FILE *f
, const char *opts
) {
258 return write_dependency(f
, opts
,
259 "x-systemd.before", "Before=%1$s\n");
262 static int write_requires_mounts_for(FILE *f
, const char *opts
) {
263 _cleanup_strv_free_
char **paths
= NULL
, **paths_escaped
= NULL
;
264 _cleanup_free_
char *res
= NULL
;
270 r
= fstab_extract_values(opts
, "x-systemd.requires-mounts-for", &paths
);
272 return log_warning_errno(r
, "Failed to parse options: %m");
276 r
= specifier_escape_strv(paths
, &paths_escaped
);
278 return log_error_errno(r
, "Failed to escape paths: %m");
280 res
= strv_join(paths_escaped
, " ");
284 fprintf(f
, "RequiresMountsFor=%s\n", res
);
289 static int add_mount(
293 const char *original_where
,
297 MountpointFlags flags
,
299 const char *source
) {
303 *automount_name
= NULL
,
305 *where_escaped
= NULL
;
306 _cleanup_fclose_
FILE *f
= NULL
;
315 if (streq_ptr(fstype
, "autofs"))
318 if (!is_path(where
)) {
319 log_warning("Mount point %s is not a valid path, ignoring.", where
);
323 if (mount_point_is_api(where
) ||
324 mount_point_ignore(where
))
327 if (path_equal(where
, "/")) {
329 log_warning("Ignoring \"noauto\" for root device");
331 log_warning("Ignoring \"nofail\" for root device");
332 if (flags
& AUTOMOUNT
)
333 log_warning("Ignoring automount option for root device");
335 SET_FLAG(flags
, NOAUTO
| NOFAIL
| AUTOMOUNT
, false);
338 r
= unit_name_from_path(where
, ".mount", &name
);
340 return log_error_errno(r
, "Failed to generate unit name: %m");
342 r
= generator_open_unit_file(dest
, "/etc/fstab", name
, &f
);
349 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
352 if (STRPTR_IN_SET(fstype
, "nfs", "nfs4") && !(flags
& AUTOMOUNT
) &&
353 fstab_test_yes_no_option(opts
, "bg\0" "fg\0")) {
354 /* The default retry timeout that mount.nfs uses for 'bg' mounts
355 * is 10000 minutes, where as it uses 2 minutes for 'fg' mounts.
356 * As we are making 'bg' mounts look like an 'fg' mount to
357 * mount.nfs (so systemd can manage the job-control aspects of 'bg'),
358 * we need to explicitly preserve that default, and also ensure
359 * the systemd mount-timeout doesn't interfere.
360 * By placing these options first, they can be over-ridden by
361 * settings in /etc/fstab. */
362 opts
= strjoina("x-systemd.mount-timeout=infinity,retry=10000,", opts
, ",fg");
363 SET_FLAG(flags
, NOFAIL
, true);
366 if (!(flags
& NOFAIL
) && !(flags
& AUTOMOUNT
))
367 fprintf(f
, "Before=%s\n", post
);
369 if (!(flags
& AUTOMOUNT
) && opts
) {
370 r
= write_after(f
, opts
);
373 r
= write_requires_after(f
, opts
);
376 r
= write_before(f
, opts
);
379 r
= write_requires_mounts_for(f
, opts
);
385 r
= generator_write_fsck_deps(f
, dest
, what
, where
, fstype
);
390 fprintf(f
, "\n[Mount]\n");
392 fprintf(f
, "# Canonicalized from %s\n", original_where
);
394 where_escaped
= specifier_escape(where
);
397 fprintf(f
, "Where=%s\n", where_escaped
);
399 r
= write_what(f
, what
);
403 if (!isempty(fstype
) && !streq(fstype
, "auto")) {
404 _cleanup_free_
char *t
;
406 t
= specifier_escape(fstype
);
410 fprintf(f
, "Type=%s\n", t
);
413 r
= generator_write_timeouts(dest
, what
, where
, opts
, &filtered
);
417 r
= generator_write_device_deps(dest
, what
, where
, opts
);
421 r
= write_mount_timeout(f
, where
, opts
);
425 r
= write_options(f
, filtered
);
429 r
= fflush_and_check(f
);
431 return log_error_errno(r
, "Failed to write unit file %s: %m", name
);
433 if (flags
& MAKEFS
) {
434 r
= generator_hook_up_mkfs(dest
, what
, where
, fstype
);
439 if (flags
& GROWFS
) {
440 r
= generator_hook_up_growfs(dest
, where
, post
);
445 if (!(flags
& NOAUTO
) && !(flags
& AUTOMOUNT
)) {
446 r
= generator_add_symlink(dest
, post
,
447 (flags
& NOFAIL
) ? "wants" : "requires", name
);
452 if (flags
& AUTOMOUNT
) {
453 r
= unit_name_from_path(where
, ".automount", &automount_name
);
455 return log_error_errno(r
, "Failed to generate unit name: %m");
459 r
= generator_open_unit_file(dest
, "/etc/fstab", automount_name
, &f
);
466 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
469 fprintf(f
, "Before=%s\n", post
);
472 r
= write_after(f
, opts
);
475 r
= write_requires_after(f
, opts
);
478 r
= write_before(f
, opts
);
481 r
= write_requires_mounts_for(f
, opts
);
492 r
= write_idle_timeout(f
, where
, opts
);
496 r
= fflush_and_check(f
);
498 return log_error_errno(r
, "Failed to write unit file %s: %m", automount_name
);
500 r
= generator_add_symlink(dest
, post
,
501 (flags
& NOFAIL
) ? "wants" : "requires", automount_name
);
509 static int parse_fstab(bool initrd
) {
510 _cleanup_endmntent_
FILE *f
= NULL
;
511 const char *fstab_path
;
515 fstab_path
= initrd
? "/sysroot/etc/fstab" : "/etc/fstab";
516 f
= setmntent(fstab_path
, "re");
521 return log_error_errno(errno
, "Failed to open %s: %m", fstab_path
);
524 while ((me
= getmntent(f
))) {
525 _cleanup_free_
char *where
= NULL
, *what
= NULL
, *canonical_where
= NULL
;
526 bool makefs
, growfs
, noauto
, nofail
;
529 if (initrd
&& !mount_in_initrd(me
))
532 what
= fstab_node_to_udev_node(me
->mnt_fsname
);
536 if (is_device_path(what
) && path_is_read_only_fs("sys") > 0) {
537 log_info("Running in a container, ignoring fstab device entry for %s.", what
);
541 where
= strdup(me
->mnt_dir
);
545 if (is_path(where
)) {
546 path_kill_slashes(where
);
548 /* Follow symlinks here; see 5261ba901845c084de5a8fd06500ed09bfb0bd80 which makes sense for
549 * mount units, but causes problems since it historically worked to have symlinks in e.g.
550 * /etc/fstab. So we canonicalize here. Note that we use CHASE_NONEXISTENT to handle the case
551 * where a symlink refers to another mount target; this works assuming the sub-mountpoint
552 * target is the final directory. */
553 r
= chase_symlinks(where
, initrd
? "/sysroot" : NULL
,
554 CHASE_PREFIX_ROOT
| CHASE_NONEXISTENT
,
556 if (r
< 0) /* If we can't canonicalize we continue on as if it wasn't a symlink */
557 log_debug_errno(r
, "Failed to read symlink target for %s, ignoring: %m", where
);
558 else if (streq(canonical_where
, where
)) /* If it was fully canonicalized, suppress the change */
559 canonical_where
= mfree(canonical_where
);
561 log_debug("Canonicalized what=%s where=%s to %s", what
, where
, canonical_where
);
564 makefs
= fstab_test_option(me
->mnt_opts
, "x-systemd.makefs\0");
565 growfs
= fstab_test_option(me
->mnt_opts
, "x-systemd.growfs\0");
566 noauto
= fstab_test_yes_no_option(me
->mnt_opts
, "noauto\0" "auto\0");
567 nofail
= fstab_test_yes_no_option(me
->mnt_opts
, "nofail\0" "fail\0");
569 log_debug("Found entry what=%s where=%s type=%s makefs=%s nofail=%s noauto=%s",
570 what
, where
, me
->mnt_type
,
572 yes_no(noauto
), yes_no(nofail
));
574 if (streq(me
->mnt_type
, "swap"))
575 k
= add_swap(what
, me
,
576 makefs
*MAKEFS
| growfs
*GROWFS
| noauto
*NOAUTO
| nofail
*NOFAIL
);
581 automount
= fstab_test_option(me
->mnt_opts
,
582 "comment=systemd.automount\0"
583 "x-systemd.automount\0");
585 post
= SPECIAL_INITRD_FS_TARGET
;
586 else if (mount_is_network(me
))
587 post
= SPECIAL_REMOTE_FS_TARGET
;
589 post
= SPECIAL_LOCAL_FS_TARGET
;
591 k
= add_mount(arg_dest
,
593 canonical_where
?: where
,
594 canonical_where
? where
: NULL
,
598 makefs
*MAKEFS
| growfs
*GROWFS
| noauto
*NOAUTO
| nofail
*NOFAIL
| automount
*AUTOMOUNT
,
610 static int add_sysroot_mount(void) {
611 _cleanup_free_
char *what
= NULL
;
615 if (isempty(arg_root_what
)) {
616 log_debug("Could not find a root= entry on the kernel command line.");
620 if (streq(arg_root_what
, "gpt-auto")) {
621 /* This is handled by the gpt-auto generator */
622 log_debug("Skipping root directory handling, as gpt-auto was requested.");
626 if (path_equal(arg_root_what
, "/dev/nfs")) {
627 /* This is handled by the kernel or the initrd */
628 log_debug("Skipping root directory handling, as /dev/nfs was requested.");
632 what
= fstab_node_to_udev_node(arg_root_what
);
636 if (!arg_root_options
)
637 opts
= arg_root_rw
> 0 ? "rw" : "ro";
638 else if (arg_root_rw
>= 0 ||
639 !fstab_test_option(arg_root_options
, "ro\0" "rw\0"))
640 opts
= strjoina(arg_root_options
, ",", arg_root_rw
> 0 ? "rw" : "ro");
642 opts
= arg_root_options
;
644 log_debug("Found entry what=%s where=/sysroot type=%s", what
, strna(arg_root_fstype
));
646 if (is_device_path(what
)) {
647 r
= generator_write_initrd_root_device_deps(arg_dest
, what
);
652 return add_mount(arg_dest
,
658 is_device_path(what
) ? 1 : 0, /* passno */
659 0, /* makefs off, growfs off, noauto off, nofail off, automount off */
660 SPECIAL_INITRD_ROOT_FS_TARGET
,
664 static int add_sysroot_usr_mount(void) {
665 _cleanup_free_
char *what
= NULL
;
668 if (!arg_usr_what
&& !arg_usr_fstype
&& !arg_usr_options
)
671 if (arg_root_what
&& !arg_usr_what
) {
672 /* Copy over the root device, in case the /usr mount just differs in a mount option (consider btrfs subvolumes) */
673 arg_usr_what
= strdup(arg_root_what
);
678 if (arg_root_fstype
&& !arg_usr_fstype
) {
679 arg_usr_fstype
= strdup(arg_root_fstype
);
684 if (arg_root_options
&& !arg_usr_options
) {
685 arg_usr_options
= strdup(arg_root_options
);
686 if (!arg_usr_options
)
693 what
= fstab_node_to_udev_node(arg_usr_what
);
697 if (!arg_usr_options
)
698 opts
= arg_root_rw
> 0 ? "rw" : "ro";
699 else if (!fstab_test_option(arg_usr_options
, "ro\0" "rw\0"))
700 opts
= strjoina(arg_usr_options
, ",", arg_root_rw
> 0 ? "rw" : "ro");
702 opts
= arg_usr_options
;
704 log_debug("Found entry what=%s where=/sysroot/usr type=%s", what
, strna(arg_usr_fstype
));
705 return add_mount(arg_dest
,
711 is_device_path(what
) ? 1 : 0, /* passno */
713 SPECIAL_INITRD_FS_TARGET
,
717 static int add_volatile_root(void) {
718 const char *from
, *to
;
720 if (arg_volatile_mode
!= VOLATILE_YES
)
723 /* Let's add in systemd-remount-volatile.service which will remount the root device to tmpfs if this is
724 * requested, leaving only /usr from the root mount inside. */
726 from
= strjoina(SYSTEM_DATA_UNIT_PATH
"/systemd-volatile-root.service");
727 to
= strjoina(arg_dest
, "/" SPECIAL_INITRD_ROOT_FS_TARGET
, ".requires/systemd-volatile-root.service");
729 (void) mkdir_parents(to
, 0755);
731 if (symlink(from
, to
) < 0)
732 return log_error_errno(errno
, "Failed to hook in volatile remount service: %m");
737 static int add_volatile_var(void) {
739 if (arg_volatile_mode
!= VOLATILE_STATE
)
742 /* If requested, mount /var as tmpfs, but do so only if there's nothing else defined for this. */
744 return add_mount(arg_dest_late
,
752 SPECIAL_LOCAL_FS_TARGET
,
756 static int parse_proc_cmdline_item(const char *key
, const char *value
, void *data
) {
759 /* root=, usr=, usrfstype= and roofstype= may occur more than once, the last
760 * instance should take precedence. In the case of multiple rootflags=
761 * or usrflags= the arguments should be concatenated */
763 if (STR_IN_SET(key
, "fstab", "rd.fstab")) {
765 r
= value
? parse_boolean(value
) : 1;
767 log_warning("Failed to parse fstab switch %s. Ignoring.", value
);
769 arg_fstab_enabled
= r
;
771 } else if (streq(key
, "root")) {
773 if (proc_cmdline_value_missing(key
, value
))
776 if (free_and_strdup(&arg_root_what
, value
) < 0)
779 } else if (streq(key
, "rootfstype")) {
781 if (proc_cmdline_value_missing(key
, value
))
784 if (free_and_strdup(&arg_root_fstype
, value
) < 0)
787 } else if (streq(key
, "rootflags")) {
789 if (proc_cmdline_value_missing(key
, value
))
792 if (!strextend_with_separator(&arg_root_options
, ",", value
, NULL
))
795 } else if (streq(key
, "roothash")) {
797 if (proc_cmdline_value_missing(key
, value
))
800 if (free_and_strdup(&arg_root_hash
, value
) < 0)
803 } else if (streq(key
, "mount.usr")) {
805 if (proc_cmdline_value_missing(key
, value
))
808 if (free_and_strdup(&arg_usr_what
, value
) < 0)
811 } else if (streq(key
, "mount.usrfstype")) {
813 if (proc_cmdline_value_missing(key
, value
))
816 if (free_and_strdup(&arg_usr_fstype
, value
) < 0)
819 } else if (streq(key
, "mount.usrflags")) {
821 if (proc_cmdline_value_missing(key
, value
))
824 if (!strextend_with_separator(&arg_usr_options
, ",", value
, NULL
))
827 } else if (streq(key
, "rw") && !value
)
829 else if (streq(key
, "ro") && !value
)
831 else if (streq(key
, "systemd.volatile")) {
835 m
= volatile_mode_from_string(value
);
837 log_warning("Failed to parse systemd.volatile= argument: %s", value
);
839 arg_volatile_mode
= m
;
841 arg_volatile_mode
= VOLATILE_YES
;
847 static int determine_root(void) {
848 /* If we have a root hash but no root device then Verity is used, and we use the "root" DM device as root. */
856 arg_root_what
= strdup("/dev/mapper/root");
860 log_info("Using verity root device %s.", arg_root_what
);
865 int main(int argc
, char *argv
[]) {
868 if (argc
> 1 && argc
!= 4) {
869 log_error("This program takes three or no arguments.");
876 arg_dest_late
= argv
[3];
878 log_set_prohibit_ipc(true);
879 log_set_target(LOG_TARGET_AUTO
);
880 log_parse_environment();
885 r
= proc_cmdline_parse(parse_proc_cmdline_item
, NULL
, 0);
887 log_warning_errno(r
, "Failed to parse kernel command line, ignoring: %m");
889 (void) determine_root();
891 /* Always honour root= and usr= in the kernel command line if we are in an initrd */
895 r
= add_sysroot_mount();
897 k
= add_sysroot_usr_mount();
901 k
= add_volatile_root();
905 r
= add_volatile_var();
907 /* Honour /etc/fstab only when that's enabled */
908 if (arg_fstab_enabled
) {
911 log_debug("Parsing /etc/fstab");
913 /* Parse the local /etc/fstab, possibly from the initrd */
914 k
= parse_fstab(false);
918 /* If running in the initrd also parse the /etc/fstab from the host */
920 log_debug("Parsing /sysroot/etc/fstab");
922 k
= parse_fstab(true);
929 free(arg_root_fstype
);
930 free(arg_root_options
);
934 free(arg_usr_fstype
);
935 free(arg_usr_options
);
937 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;