1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2012 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include <stdio_ext.h>
28 #include "alloc-util.h"
32 #include "fstab-util.h"
33 #include "generator.h"
36 #include "mount-setup.h"
37 #include "mount-util.h"
38 #include "parse-util.h"
39 #include "path-util.h"
40 #include "proc-cmdline.h"
42 #include "specifier.h"
43 #include "stat-util.h"
44 #include "string-util.h"
46 #include "unit-name.h"
49 #include "volatile-util.h"
51 typedef enum MountpointFlags
{
59 static const char *arg_dest
= "/tmp";
60 static const char *arg_dest_late
= "/tmp";
61 static bool arg_fstab_enabled
= true;
62 static char *arg_root_what
= NULL
;
63 static char *arg_root_fstype
= NULL
;
64 static char *arg_root_options
= NULL
;
65 static char *arg_root_hash
= NULL
;
66 static int arg_root_rw
= -1;
67 static char *arg_usr_what
= NULL
;
68 static char *arg_usr_fstype
= NULL
;
69 static char *arg_usr_options
= NULL
;
70 static VolatileMode arg_volatile_mode
= _VOLATILE_MODE_INVALID
;
72 static int write_options(FILE *f
, const char *options
) {
73 _cleanup_free_
char *o
= NULL
;
78 if (streq(options
, "defaults"))
81 o
= specifier_escape(options
);
85 fprintf(f
, "Options=%s\n", o
);
89 static int write_what(FILE *f
, const char *what
) {
90 _cleanup_free_
char *w
= NULL
;
92 w
= specifier_escape(what
);
96 fprintf(f
, "What=%s\n", w
);
103 MountpointFlags flags
) {
105 _cleanup_free_
char *name
= NULL
, *unit
= NULL
;
106 _cleanup_fclose_
FILE *f
= NULL
;
112 if (access("/proc/swaps", F_OK
) < 0) {
113 log_info("Swap not supported, ignoring fstab swap entry for %s.", what
);
117 if (detect_container() > 0) {
118 log_info("Running in a container, ignoring fstab swap entry for %s.", what
);
122 r
= unit_name_from_path(what
, ".swap", &name
);
124 return log_error_errno(r
, "Failed to generate unit name: %m");
126 unit
= strjoin(arg_dest
, "/", name
);
130 f
= fopen(unit
, "wxe");
132 return log_error_errno(errno
,
134 "Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?" :
135 "Failed to create unit file %s: %m",
138 (void) __fsetlocking(f
, FSETLOCKING_BYCALLER
);
140 fputs("# Automatically generated by systemd-fstab-generator\n\n"
142 "SourcePath=/etc/fstab\n"
143 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n"
146 r
= write_what(f
, what
);
150 r
= write_options(f
, me
->mnt_opts
);
154 r
= fflush_and_check(f
);
156 return log_error_errno(r
, "Failed to write unit file %s: %m", unit
);
158 /* use what as where, to have a nicer error message */
159 r
= generator_write_timeouts(arg_dest
, what
, what
, me
->mnt_opts
, NULL
);
163 if (flags
& MAKEFS
) {
164 r
= generator_hook_up_mkswap(arg_dest
, what
);
170 /* TODO: swap devices must be wiped and recreated */
171 log_warning("%s: growing swap devices is currently unsupported.", what
);
173 if (!(flags
& NOAUTO
)) {
174 r
= generator_add_symlink(arg_dest
, SPECIAL_SWAP_TARGET
,
175 (flags
& NOFAIL
) ? "wants" : "requires", name
);
183 static bool mount_is_network(struct mntent
*me
) {
186 return fstab_test_option(me
->mnt_opts
, "_netdev\0") ||
187 fstype_is_network(me
->mnt_type
);
190 static bool mount_in_initrd(struct mntent
*me
) {
193 return fstab_test_option(me
->mnt_opts
, "x-initrd.mount\0") ||
194 streq(me
->mnt_dir
, "/usr");
197 static int write_timeout(FILE *f
, const char *where
, const char *opts
,
198 const char *filter
, const char *variable
) {
199 _cleanup_free_
char *timeout
= NULL
;
200 char timespan
[FORMAT_TIMESPAN_MAX
];
204 r
= fstab_filter_options(opts
, filter
, NULL
, &timeout
, NULL
);
206 return log_warning_errno(r
, "Failed to parse options: %m");
210 r
= parse_sec_fix_0(timeout
, &u
);
212 log_warning("Failed to parse timeout for %s, ignoring: %s", where
, timeout
);
216 fprintf(f
, "%s=%s\n", variable
, format_timespan(timespan
, sizeof(timespan
), u
, 0));
221 static int write_idle_timeout(FILE *f
, const char *where
, const char *opts
) {
222 return write_timeout(f
, where
, opts
,
223 "x-systemd.idle-timeout\0", "TimeoutIdleSec");
226 static int write_mount_timeout(FILE *f
, const char *where
, const char *opts
) {
227 return write_timeout(f
, where
, opts
,
228 "x-systemd.mount-timeout\0", "TimeoutSec");
231 static int write_dependency(FILE *f
, const char *opts
,
232 const char *filter
, const char *format
) {
233 _cleanup_strv_free_
char **names
= NULL
, **units
= NULL
;
234 _cleanup_free_
char *res
= NULL
;
241 r
= fstab_extract_values(opts
, filter
, &names
);
243 return log_warning_errno(r
, "Failed to parse options: %m");
247 STRV_FOREACH(s
, names
) {
250 r
= unit_name_mangle_with_suffix(*s
, UNIT_NAME_NOGLOB
, ".mount", &x
);
252 return log_error_errno(r
, "Failed to generate unit name: %m");
253 r
= strv_consume(&units
, x
);
259 res
= strv_join(units
, " ");
262 #pragma GCC diagnostic push
263 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
264 fprintf(f
, format
, res
);
265 #pragma GCC diagnostic pop
271 static int write_after(FILE *f
, const char *opts
) {
272 return write_dependency(f
, opts
, "x-systemd.after", "After=%1$s\n");
275 static int write_requires_after(FILE *f
, const char *opts
) {
276 return write_dependency(f
, opts
,
277 "x-systemd.requires", "After=%1$s\nRequires=%1$s\n");
280 static int write_before(FILE *f
, const char *opts
) {
281 return write_dependency(f
, opts
,
282 "x-systemd.before", "Before=%1$s\n");
285 static int write_requires_mounts_for(FILE *f
, const char *opts
) {
286 _cleanup_strv_free_
char **paths
= NULL
, **paths_escaped
= NULL
;
287 _cleanup_free_
char *res
= NULL
;
293 r
= fstab_extract_values(opts
, "x-systemd.requires-mounts-for", &paths
);
295 return log_warning_errno(r
, "Failed to parse options: %m");
299 r
= specifier_escape_strv(paths
, &paths_escaped
);
301 return log_error_errno(r
, "Failed to escape paths: %m");
303 res
= strv_join(paths_escaped
, " ");
307 fprintf(f
, "RequiresMountsFor=%s\n", res
);
312 static int add_mount(
316 const char *original_where
,
320 MountpointFlags flags
,
322 const char *source
) {
326 *automount_name
= NULL
, *automount_unit
= NULL
,
328 *where_escaped
= NULL
;
330 _cleanup_fclose_
FILE *f
= NULL
;
339 if (streq_ptr(fstype
, "autofs"))
342 if (!is_path(where
)) {
343 log_warning("Mount point %s is not a valid path, ignoring.", where
);
347 if (mount_point_is_api(where
) ||
348 mount_point_ignore(where
))
351 if (path_equal(where
, "/")) {
353 log_warning("Ignoring \"noauto\" for root device");
355 log_warning("Ignoring \"nofail\" for root device");
356 if (flags
& AUTOMOUNT
)
357 log_warning("Ignoring automount option for root device");
359 SET_FLAG(flags
, NOAUTO
| NOFAIL
| AUTOMOUNT
, false);
362 r
= unit_name_from_path(where
, ".mount", &name
);
364 return log_error_errno(r
, "Failed to generate unit name: %m");
366 unit
= strjoina(dest
, "/", name
);
368 f
= fopen(unit
, "wxe");
370 return log_error_errno(errno
,
372 "Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?" :
373 "Failed to create unit file %s: %m",
376 (void) __fsetlocking(f
, FSETLOCKING_BYCALLER
);
379 "# Automatically generated by systemd-fstab-generator\n\n"
382 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
385 if (STRPTR_IN_SET(fstype
, "nfs", "nfs4") && !(flags
& AUTOMOUNT
) &&
386 fstab_test_yes_no_option(opts
, "bg\0" "fg\0")) {
387 /* The default retry timeout that mount.nfs uses for 'bg' mounts
388 * is 10000 minutes, where as it uses 2 minutes for 'fg' mounts.
389 * As we are making 'bg' mounts look like an 'fg' mount to
390 * mount.nfs (so systemd can manage the job-control aspects of 'bg'),
391 * we need to explicitly preserve that default, and also ensure
392 * the systemd mount-timeout doesn't interfere.
393 * By placing these options first, they can be over-ridden by
394 * settings in /etc/fstab. */
395 opts
= strjoina("x-systemd.mount-timeout=infinity,retry=10000,", opts
, ",fg");
396 SET_FLAG(flags
, NOFAIL
, true);
399 if (!(flags
& NOFAIL
) && !(flags
& AUTOMOUNT
))
400 fprintf(f
, "Before=%s\n", post
);
402 if (!(flags
& AUTOMOUNT
) && opts
) {
403 r
= write_after(f
, opts
);
406 r
= write_requires_after(f
, opts
);
409 r
= write_before(f
, opts
);
412 r
= write_requires_mounts_for(f
, opts
);
418 r
= generator_write_fsck_deps(f
, dest
, what
, where
, fstype
);
423 fprintf(f
, "\n[Mount]\n");
425 fprintf(f
, "# Canonicalized from %s\n", original_where
);
427 where_escaped
= specifier_escape(where
);
430 fprintf(f
, "Where=%s\n", where_escaped
);
432 r
= write_what(f
, what
);
436 if (!isempty(fstype
) && !streq(fstype
, "auto")) {
437 _cleanup_free_
char *t
;
439 t
= specifier_escape(fstype
);
443 fprintf(f
, "Type=%s\n", t
);
446 r
= generator_write_timeouts(dest
, what
, where
, opts
, &filtered
);
450 r
= generator_write_device_deps(dest
, what
, where
, opts
);
454 r
= write_mount_timeout(f
, where
, opts
);
458 r
= write_options(f
, filtered
);
462 r
= fflush_and_check(f
);
464 return log_error_errno(r
, "Failed to write unit file %s: %m", unit
);
466 if (flags
& MAKEFS
) {
467 r
= generator_hook_up_mkfs(dest
, what
, where
, fstype
);
472 if (flags
& GROWFS
) {
473 r
= generator_hook_up_growfs(dest
, where
, post
);
478 if (!(flags
& NOAUTO
) && !(flags
& AUTOMOUNT
)) {
479 r
= generator_add_symlink(dest
, post
,
480 (flags
& NOFAIL
) ? "wants" : "requires", name
);
485 if (flags
& AUTOMOUNT
) {
486 r
= unit_name_from_path(where
, ".automount", &automount_name
);
488 return log_error_errno(r
, "Failed to generate unit name: %m");
490 automount_unit
= strjoin(dest
, "/", automount_name
);
495 f
= fopen(automount_unit
, "wxe");
497 return log_error_errno(errno
, "Failed to create unit file %s: %m", automount_unit
);
499 (void) __fsetlocking(f
, FSETLOCKING_BYCALLER
);
502 "# Automatically generated by systemd-fstab-generator\n\n"
505 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
508 fprintf(f
, "Before=%s\n", post
);
511 r
= write_after(f
, opts
);
514 r
= write_requires_after(f
, opts
);
517 r
= write_before(f
, opts
);
520 r
= write_requires_mounts_for(f
, opts
);
531 r
= write_idle_timeout(f
, where
, opts
);
535 r
= fflush_and_check(f
);
537 return log_error_errno(r
, "Failed to write unit file %s: %m", automount_unit
);
539 r
= generator_add_symlink(dest
, post
,
540 (flags
& NOFAIL
) ? "wants" : "requires", automount_name
);
548 static int parse_fstab(bool initrd
) {
549 _cleanup_endmntent_
FILE *f
= NULL
;
550 const char *fstab_path
;
554 fstab_path
= initrd
? "/sysroot/etc/fstab" : "/etc/fstab";
555 f
= setmntent(fstab_path
, "re");
560 return log_error_errno(errno
, "Failed to open %s: %m", fstab_path
);
563 while ((me
= getmntent(f
))) {
564 _cleanup_free_
char *where
= NULL
, *what
= NULL
, *canonical_where
= NULL
;
565 bool makefs
, growfs
, noauto
, nofail
;
568 if (initrd
&& !mount_in_initrd(me
))
571 what
= fstab_node_to_udev_node(me
->mnt_fsname
);
575 if (is_device_path(what
) && path_is_read_only_fs("sys") > 0) {
576 log_info("Running in a container, ignoring fstab device entry for %s.", what
);
580 where
= strdup(me
->mnt_dir
);
584 if (is_path(where
)) {
585 path_kill_slashes(where
);
586 /* Follow symlinks here; see 5261ba901845c084de5a8fd06500ed09bfb0bd80 which makes sense for
587 * mount units, but causes problems since it historically worked to have symlinks in e.g.
588 * /etc/fstab. So we canonicalize here. Note that we use CHASE_NONEXISTENT to handle the case
589 * where a symlink refers to another mount target; this works assuming the sub-mountpoint
590 * target is the final directory.
592 r
= chase_symlinks(where
, initrd
? "/sysroot" : NULL
,
593 CHASE_PREFIX_ROOT
| CHASE_NONEXISTENT
,
596 /* In this case for now we continue on as if it wasn't a symlink */
597 log_warning_errno(r
, "Failed to read symlink target for %s: %m", where
);
599 if (streq(canonical_where
, where
))
600 canonical_where
= mfree(canonical_where
);
602 log_debug("Canonicalized what=%s where=%s to %s",
603 what
, where
, canonical_where
);
607 makefs
= fstab_test_option(me
->mnt_opts
, "x-systemd.makefs\0");
608 growfs
= fstab_test_option(me
->mnt_opts
, "x-systemd.growfs\0");
609 noauto
= fstab_test_yes_no_option(me
->mnt_opts
, "noauto\0" "auto\0");
610 nofail
= fstab_test_yes_no_option(me
->mnt_opts
, "nofail\0" "fail\0");
611 log_debug("Found entry what=%s where=%s type=%s makefs=%s nofail=%s noauto=%s",
612 what
, where
, me
->mnt_type
,
614 yes_no(noauto
), yes_no(nofail
));
616 if (streq(me
->mnt_type
, "swap"))
617 k
= add_swap(what
, me
,
618 makefs
*MAKEFS
| growfs
*GROWFS
| noauto
*NOAUTO
| nofail
*NOFAIL
);
623 automount
= fstab_test_option(me
->mnt_opts
,
624 "comment=systemd.automount\0"
625 "x-systemd.automount\0");
627 post
= SPECIAL_INITRD_FS_TARGET
;
628 else if (mount_is_network(me
))
629 post
= SPECIAL_REMOTE_FS_TARGET
;
631 post
= SPECIAL_LOCAL_FS_TARGET
;
633 k
= add_mount(arg_dest
,
635 canonical_where
?: where
,
636 canonical_where
? where
: NULL
,
640 makefs
*MAKEFS
| growfs
*GROWFS
| noauto
*NOAUTO
| nofail
*NOFAIL
| automount
*AUTOMOUNT
,
652 static int add_sysroot_mount(void) {
653 _cleanup_free_
char *what
= NULL
;
657 if (isempty(arg_root_what
)) {
658 log_debug("Could not find a root= entry on the kernel command line.");
662 if (streq(arg_root_what
, "gpt-auto")) {
663 /* This is handled by the gpt-auto generator */
664 log_debug("Skipping root directory handling, as gpt-auto was requested.");
668 if (path_equal(arg_root_what
, "/dev/nfs")) {
669 /* This is handled by the kernel or the initrd */
670 log_debug("Skipping root directory handling, as /dev/nfs was requested.");
674 what
= fstab_node_to_udev_node(arg_root_what
);
678 if (!arg_root_options
)
679 opts
= arg_root_rw
> 0 ? "rw" : "ro";
680 else if (arg_root_rw
>= 0 ||
681 !fstab_test_option(arg_root_options
, "ro\0" "rw\0"))
682 opts
= strjoina(arg_root_options
, ",", arg_root_rw
> 0 ? "rw" : "ro");
684 opts
= arg_root_options
;
686 log_debug("Found entry what=%s where=/sysroot type=%s", what
, strna(arg_root_fstype
));
688 if (is_device_path(what
)) {
689 r
= generator_write_initrd_root_device_deps(arg_dest
, what
);
694 return add_mount(arg_dest
,
700 is_device_path(what
) ? 1 : 0, /* passno */
701 0, /* makefs off, growfs off, noauto off, nofail off, automount off */
702 SPECIAL_INITRD_ROOT_FS_TARGET
,
706 static int add_sysroot_usr_mount(void) {
707 _cleanup_free_
char *what
= NULL
;
710 if (!arg_usr_what
&& !arg_usr_fstype
&& !arg_usr_options
)
713 if (arg_root_what
&& !arg_usr_what
) {
714 /* Copy over the root device, in case the /usr mount just differs in a mount option (consider btrfs subvolumes) */
715 arg_usr_what
= strdup(arg_root_what
);
720 if (arg_root_fstype
&& !arg_usr_fstype
) {
721 arg_usr_fstype
= strdup(arg_root_fstype
);
726 if (arg_root_options
&& !arg_usr_options
) {
727 arg_usr_options
= strdup(arg_root_options
);
728 if (!arg_usr_options
)
735 what
= fstab_node_to_udev_node(arg_usr_what
);
739 if (!arg_usr_options
)
740 opts
= arg_root_rw
> 0 ? "rw" : "ro";
741 else if (!fstab_test_option(arg_usr_options
, "ro\0" "rw\0"))
742 opts
= strjoina(arg_usr_options
, ",", arg_root_rw
> 0 ? "rw" : "ro");
744 opts
= arg_usr_options
;
746 log_debug("Found entry what=%s where=/sysroot/usr type=%s", what
, strna(arg_usr_fstype
));
747 return add_mount(arg_dest
,
753 is_device_path(what
) ? 1 : 0, /* passno */
755 SPECIAL_INITRD_FS_TARGET
,
759 static int add_volatile_root(void) {
760 const char *from
, *to
;
762 if (arg_volatile_mode
!= VOLATILE_YES
)
765 /* Let's add in systemd-remount-volatile.service which will remount the root device to tmpfs if this is
766 * requested, leaving only /usr from the root mount inside. */
768 from
= strjoina(SYSTEM_DATA_UNIT_PATH
"/systemd-volatile-root.service");
769 to
= strjoina(arg_dest
, "/" SPECIAL_INITRD_ROOT_FS_TARGET
, ".requires/systemd-volatile-root.service");
771 (void) mkdir_parents(to
, 0755);
773 if (symlink(from
, to
) < 0)
774 return log_error_errno(errno
, "Failed to hook in volatile remount service: %m");
779 static int add_volatile_var(void) {
781 if (arg_volatile_mode
!= VOLATILE_STATE
)
784 /* If requested, mount /var as tmpfs, but do so only if there's nothing else defined for this. */
786 return add_mount(arg_dest_late
,
794 SPECIAL_LOCAL_FS_TARGET
,
798 static int parse_proc_cmdline_item(const char *key
, const char *value
, void *data
) {
801 /* root=, usr=, usrfstype= and roofstype= may occur more than once, the last
802 * instance should take precedence. In the case of multiple rootflags=
803 * or usrflags= the arguments should be concatenated */
805 if (STR_IN_SET(key
, "fstab", "rd.fstab")) {
807 r
= value
? parse_boolean(value
) : 1;
809 log_warning("Failed to parse fstab switch %s. Ignoring.", value
);
811 arg_fstab_enabled
= r
;
813 } else if (streq(key
, "root")) {
815 if (proc_cmdline_value_missing(key
, value
))
818 if (free_and_strdup(&arg_root_what
, value
) < 0)
821 } else if (streq(key
, "rootfstype")) {
823 if (proc_cmdline_value_missing(key
, value
))
826 if (free_and_strdup(&arg_root_fstype
, value
) < 0)
829 } else if (streq(key
, "rootflags")) {
831 if (proc_cmdline_value_missing(key
, value
))
834 if (!strextend_with_separator(&arg_root_options
, ",", value
, NULL
))
837 } else if (streq(key
, "roothash")) {
839 if (proc_cmdline_value_missing(key
, value
))
842 if (free_and_strdup(&arg_root_hash
, value
) < 0)
845 } else if (streq(key
, "mount.usr")) {
847 if (proc_cmdline_value_missing(key
, value
))
850 if (free_and_strdup(&arg_usr_what
, value
) < 0)
853 } else if (streq(key
, "mount.usrfstype")) {
855 if (proc_cmdline_value_missing(key
, value
))
858 if (free_and_strdup(&arg_usr_fstype
, value
) < 0)
861 } else if (streq(key
, "mount.usrflags")) {
863 if (proc_cmdline_value_missing(key
, value
))
866 if (!strextend_with_separator(&arg_usr_options
, ",", value
, NULL
))
869 } else if (streq(key
, "rw") && !value
)
871 else if (streq(key
, "ro") && !value
)
873 else if (streq(key
, "systemd.volatile")) {
877 m
= volatile_mode_from_string(value
);
879 log_warning("Failed to parse systemd.volatile= argument: %s", value
);
881 arg_volatile_mode
= m
;
883 arg_volatile_mode
= VOLATILE_YES
;
889 static int determine_root(void) {
890 /* If we have a root hash but no root device then Verity is used, and we use the "root" DM device as root. */
898 arg_root_what
= strdup("/dev/mapper/root");
902 log_info("Using verity root device %s.", arg_root_what
);
907 int main(int argc
, char *argv
[]) {
910 if (argc
> 1 && argc
!= 4) {
911 log_error("This program takes three or no arguments.");
918 arg_dest_late
= argv
[3];
920 log_set_target(LOG_TARGET_SAFE
);
921 log_parse_environment();
926 r
= proc_cmdline_parse(parse_proc_cmdline_item
, NULL
, 0);
928 log_warning_errno(r
, "Failed to parse kernel command line, ignoring: %m");
930 (void) determine_root();
932 /* Always honour root= and usr= in the kernel command line if we are in an initrd */
936 r
= add_sysroot_mount();
938 k
= add_sysroot_usr_mount();
942 k
= add_volatile_root();
946 r
= add_volatile_var();
948 /* Honour /etc/fstab only when that's enabled */
949 if (arg_fstab_enabled
) {
952 log_debug("Parsing /etc/fstab");
954 /* Parse the local /etc/fstab, possibly from the initrd */
955 k
= parse_fstab(false);
959 /* If running in the initrd also parse the /etc/fstab from the host */
961 log_debug("Parsing /sysroot/etc/fstab");
963 k
= parse_fstab(true);
970 free(arg_root_fstype
);
971 free(arg_root_options
);
975 free(arg_usr_fstype
);
976 free(arg_usr_options
);
978 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;