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/>.
27 #include "alloc-util.h"
31 #include "fstab-util.h"
32 #include "generator.h"
35 #include "mount-setup.h"
36 #include "mount-util.h"
37 #include "parse-util.h"
38 #include "path-util.h"
39 #include "proc-cmdline.h"
41 #include "specifier.h"
42 #include "stat-util.h"
43 #include "string-util.h"
45 #include "unit-name.h"
48 #include "volatile-util.h"
50 typedef enum MountpointFlags
{
58 static const char *arg_dest
= "/tmp";
59 static const char *arg_dest_late
= "/tmp";
60 static bool arg_fstab_enabled
= true;
61 static char *arg_root_what
= NULL
;
62 static char *arg_root_fstype
= NULL
;
63 static char *arg_root_options
= NULL
;
64 static char *arg_root_hash
= NULL
;
65 static int arg_root_rw
= -1;
66 static char *arg_usr_what
= NULL
;
67 static char *arg_usr_fstype
= NULL
;
68 static char *arg_usr_options
= NULL
;
69 static VolatileMode arg_volatile_mode
= _VOLATILE_MODE_INVALID
;
71 static int write_options(FILE *f
, const char *options
) {
72 _cleanup_free_
char *o
= NULL
;
77 if (streq(options
, "defaults"))
80 o
= specifier_escape(options
);
84 fprintf(f
, "Options=%s\n", o
);
88 static int write_what(FILE *f
, const char *what
) {
89 _cleanup_free_
char *w
= NULL
;
91 w
= specifier_escape(what
);
95 fprintf(f
, "What=%s\n", w
);
102 MountpointFlags flags
) {
104 _cleanup_free_
char *name
= NULL
, *unit
= NULL
;
105 _cleanup_fclose_
FILE *f
= NULL
;
111 if (access("/proc/swaps", F_OK
) < 0) {
112 log_info("Swap not supported, ignoring fstab swap entry for %s.", what
);
116 if (detect_container() > 0) {
117 log_info("Running in a container, ignoring fstab swap entry for %s.", what
);
121 r
= unit_name_from_path(what
, ".swap", &name
);
123 return log_error_errno(r
, "Failed to generate unit name: %m");
125 unit
= strjoin(arg_dest
, "/", name
);
129 f
= fopen(unit
, "wxe");
131 return log_error_errno(errno
,
133 "Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?" :
134 "Failed to create unit file %s: %m",
137 fputs_unlocked("# Automatically generated by systemd-fstab-generator\n\n"
139 "SourcePath=/etc/fstab\n"
140 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n"
143 r
= write_what(f
, what
);
147 r
= write_options(f
, me
->mnt_opts
);
151 r
= fflush_and_check(f
);
153 return log_error_errno(r
, "Failed to write unit file %s: %m", unit
);
155 /* use what as where, to have a nicer error message */
156 r
= generator_write_timeouts(arg_dest
, what
, what
, me
->mnt_opts
, NULL
);
160 if (flags
& MAKEFS
) {
161 r
= generator_hook_up_mkswap(arg_dest
, what
);
167 /* TODO: swap devices must be wiped and recreated */
168 log_warning("%s: growing swap devices is currently unsupported.", what
);
170 if (!(flags
& NOAUTO
)) {
171 r
= generator_add_symlink(arg_dest
, SPECIAL_SWAP_TARGET
,
172 (flags
& NOFAIL
) ? "wants" : "requires", name
);
180 static bool mount_is_network(struct mntent
*me
) {
183 return fstab_test_option(me
->mnt_opts
, "_netdev\0") ||
184 fstype_is_network(me
->mnt_type
);
187 static bool mount_in_initrd(struct mntent
*me
) {
190 return fstab_test_option(me
->mnt_opts
, "x-initrd.mount\0") ||
191 streq(me
->mnt_dir
, "/usr");
194 static int write_timeout(FILE *f
, const char *where
, const char *opts
,
195 const char *filter
, const char *variable
) {
196 _cleanup_free_
char *timeout
= NULL
;
197 char timespan
[FORMAT_TIMESPAN_MAX
];
201 r
= fstab_filter_options(opts
, filter
, NULL
, &timeout
, NULL
);
203 return log_warning_errno(r
, "Failed to parse options: %m");
207 r
= parse_sec_fix_0(timeout
, &u
);
209 log_warning("Failed to parse timeout for %s, ignoring: %s", where
, timeout
);
213 fprintf(f
, "%s=%s\n", variable
, format_timespan(timespan
, sizeof(timespan
), u
, 0));
218 static int write_idle_timeout(FILE *f
, const char *where
, const char *opts
) {
219 return write_timeout(f
, where
, opts
,
220 "x-systemd.idle-timeout\0", "TimeoutIdleSec");
223 static int write_mount_timeout(FILE *f
, const char *where
, const char *opts
) {
224 return write_timeout(f
, where
, opts
,
225 "x-systemd.mount-timeout\0", "TimeoutSec");
228 static int write_dependency(FILE *f
, const char *opts
,
229 const char *filter
, const char *format
) {
230 _cleanup_strv_free_
char **names
= NULL
, **units
= NULL
;
231 _cleanup_free_
char *res
= NULL
;
238 r
= fstab_extract_values(opts
, filter
, &names
);
240 return log_warning_errno(r
, "Failed to parse options: %m");
244 STRV_FOREACH(s
, names
) {
247 r
= unit_name_mangle_with_suffix(*s
, UNIT_NAME_NOGLOB
, ".mount", &x
);
249 return log_error_errno(r
, "Failed to generate unit name: %m");
250 r
= strv_consume(&units
, x
);
256 res
= strv_join(units
, " ");
259 #pragma GCC diagnostic push
260 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
261 fprintf(f
, format
, res
);
262 #pragma GCC diagnostic pop
268 static int write_after(FILE *f
, const char *opts
) {
269 return write_dependency(f
, opts
, "x-systemd.after", "After=%1$s\n");
272 static int write_requires_after(FILE *f
, const char *opts
) {
273 return write_dependency(f
, opts
,
274 "x-systemd.requires", "After=%1$s\nRequires=%1$s\n");
277 static int write_before(FILE *f
, const char *opts
) {
278 return write_dependency(f
, opts
,
279 "x-systemd.before", "Before=%1$s\n");
282 static int write_requires_mounts_for(FILE *f
, const char *opts
) {
283 _cleanup_strv_free_
char **paths
= NULL
, **paths_escaped
= NULL
;
284 _cleanup_free_
char *res
= NULL
;
290 r
= fstab_extract_values(opts
, "x-systemd.requires-mounts-for", &paths
);
292 return log_warning_errno(r
, "Failed to parse options: %m");
296 r
= specifier_escape_strv(paths
, &paths_escaped
);
298 return log_error_errno(r
, "Failed to escape paths: %m");
300 res
= strv_join(paths_escaped
, " ");
304 fprintf(f
, "RequiresMountsFor=%s\n", res
);
309 static int add_mount(
313 const char *original_where
,
317 MountpointFlags flags
,
319 const char *source
) {
323 *automount_name
= NULL
, *automount_unit
= NULL
,
325 *where_escaped
= NULL
;
327 _cleanup_fclose_
FILE *f
= NULL
;
336 if (streq_ptr(fstype
, "autofs"))
339 if (!is_path(where
)) {
340 log_warning("Mount point %s is not a valid path, ignoring.", where
);
344 if (mount_point_is_api(where
) ||
345 mount_point_ignore(where
))
348 if (path_equal(where
, "/")) {
350 log_warning("Ignoring \"noauto\" for root device");
352 log_warning("Ignoring \"nofail\" for root device");
353 if (flags
& AUTOMOUNT
)
354 log_warning("Ignoring automount option for root device");
356 SET_FLAG(flags
, NOAUTO
| NOFAIL
| AUTOMOUNT
, false);
359 r
= unit_name_from_path(where
, ".mount", &name
);
361 return log_error_errno(r
, "Failed to generate unit name: %m");
363 unit
= strjoina(dest
, "/", name
);
365 f
= fopen(unit
, "wxe");
367 return log_error_errno(errno
,
369 "Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?" :
370 "Failed to create unit file %s: %m",
374 "# Automatically generated by systemd-fstab-generator\n\n"
377 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
380 if (STRPTR_IN_SET(fstype
, "nfs", "nfs4") && !(flags
& AUTOMOUNT
) &&
381 fstab_test_yes_no_option(opts
, "bg\0" "fg\0")) {
382 /* The default retry timeout that mount.nfs uses for 'bg' mounts
383 * is 10000 minutes, where as it uses 2 minutes for 'fg' mounts.
384 * As we are making 'bg' mounts look like an 'fg' mount to
385 * mount.nfs (so systemd can manage the job-control aspects of 'bg'),
386 * we need to explicitly preserve that default, and also ensure
387 * the systemd mount-timeout doesn't interfere.
388 * By placing these options first, they can be over-ridden by
389 * settings in /etc/fstab. */
390 opts
= strjoina("x-systemd.mount-timeout=infinity,retry=10000,", opts
, ",fg");
391 SET_FLAG(flags
, NOFAIL
, true);
394 if (!(flags
& NOFAIL
) && !(flags
& AUTOMOUNT
))
395 fprintf(f
, "Before=%s\n", post
);
397 if (!(flags
& AUTOMOUNT
) && opts
) {
398 r
= write_after(f
, opts
);
401 r
= write_requires_after(f
, opts
);
404 r
= write_before(f
, opts
);
407 r
= write_requires_mounts_for(f
, opts
);
413 r
= generator_write_fsck_deps(f
, dest
, what
, where
, fstype
);
418 fprintf(f
, "\n[Mount]\n");
420 fprintf(f
, "# Canonicalized from %s\n", original_where
);
422 where_escaped
= specifier_escape(where
);
425 fprintf(f
, "Where=%s\n", where_escaped
);
427 r
= write_what(f
, what
);
431 if (!isempty(fstype
) && !streq(fstype
, "auto")) {
432 _cleanup_free_
char *t
;
434 t
= specifier_escape(fstype
);
438 fprintf(f
, "Type=%s\n", t
);
441 r
= generator_write_timeouts(dest
, what
, where
, opts
, &filtered
);
445 r
= generator_write_device_deps(dest
, what
, where
, opts
);
449 r
= write_mount_timeout(f
, where
, opts
);
453 r
= write_options(f
, filtered
);
457 r
= fflush_and_check(f
);
459 return log_error_errno(r
, "Failed to write unit file %s: %m", unit
);
461 if (flags
& MAKEFS
) {
462 r
= generator_hook_up_mkfs(dest
, what
, where
, fstype
);
467 if (flags
& GROWFS
) {
468 r
= generator_hook_up_growfs(dest
, where
, post
);
473 if (!(flags
& NOAUTO
) && !(flags
& AUTOMOUNT
)) {
474 r
= generator_add_symlink(dest
, post
,
475 (flags
& NOFAIL
) ? "wants" : "requires", name
);
480 if (flags
& AUTOMOUNT
) {
481 r
= unit_name_from_path(where
, ".automount", &automount_name
);
483 return log_error_errno(r
, "Failed to generate unit name: %m");
485 automount_unit
= strjoin(dest
, "/", automount_name
);
490 f
= fopen(automount_unit
, "wxe");
492 return log_error_errno(errno
, "Failed to create unit file %s: %m", automount_unit
);
495 "# Automatically generated by systemd-fstab-generator\n\n"
498 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
501 fprintf(f
, "Before=%s\n", post
);
504 r
= write_after(f
, opts
);
507 r
= write_requires_after(f
, opts
);
510 r
= write_before(f
, opts
);
513 r
= write_requires_mounts_for(f
, opts
);
524 r
= write_idle_timeout(f
, where
, opts
);
528 r
= fflush_and_check(f
);
530 return log_error_errno(r
, "Failed to write unit file %s: %m", automount_unit
);
532 r
= generator_add_symlink(dest
, post
,
533 (flags
& NOFAIL
) ? "wants" : "requires", automount_name
);
541 static int parse_fstab(bool initrd
) {
542 _cleanup_endmntent_
FILE *f
= NULL
;
543 const char *fstab_path
;
547 fstab_path
= initrd
? "/sysroot/etc/fstab" : "/etc/fstab";
548 f
= setmntent(fstab_path
, "re");
553 return log_error_errno(errno
, "Failed to open %s: %m", fstab_path
);
556 while ((me
= getmntent(f
))) {
557 _cleanup_free_
char *where
= NULL
, *what
= NULL
, *canonical_where
= NULL
;
558 bool makefs
, growfs
, noauto
, nofail
;
561 if (initrd
&& !mount_in_initrd(me
))
564 what
= fstab_node_to_udev_node(me
->mnt_fsname
);
568 if (is_device_path(what
) && path_is_read_only_fs("sys") > 0) {
569 log_info("Running in a container, ignoring fstab device entry for %s.", what
);
573 where
= strdup(me
->mnt_dir
);
577 if (is_path(where
)) {
578 path_kill_slashes(where
);
579 /* Follow symlinks here; see 5261ba901845c084de5a8fd06500ed09bfb0bd80 which makes sense for
580 * mount units, but causes problems since it historically worked to have symlinks in e.g.
581 * /etc/fstab. So we canonicalize here. Note that we use CHASE_NONEXISTENT to handle the case
582 * where a symlink refers to another mount target; this works assuming the sub-mountpoint
583 * target is the final directory.
585 r
= chase_symlinks(where
, initrd
? "/sysroot" : NULL
,
586 CHASE_PREFIX_ROOT
| CHASE_NONEXISTENT
,
589 /* In this case for now we continue on as if it wasn't a symlink */
590 log_warning_errno(r
, "Failed to read symlink target for %s: %m", where
);
592 if (streq(canonical_where
, where
))
593 canonical_where
= mfree(canonical_where
);
595 log_debug("Canonicalized what=%s where=%s to %s",
596 what
, where
, canonical_where
);
600 makefs
= fstab_test_option(me
->mnt_opts
, "x-systemd.makefs\0");
601 growfs
= fstab_test_option(me
->mnt_opts
, "x-systemd.growfs\0");
602 noauto
= fstab_test_yes_no_option(me
->mnt_opts
, "noauto\0" "auto\0");
603 nofail
= fstab_test_yes_no_option(me
->mnt_opts
, "nofail\0" "fail\0");
604 log_debug("Found entry what=%s where=%s type=%s makefs=%s nofail=%s noauto=%s",
605 what
, where
, me
->mnt_type
,
607 yes_no(noauto
), yes_no(nofail
));
609 if (streq(me
->mnt_type
, "swap"))
610 k
= add_swap(what
, me
,
611 makefs
*MAKEFS
| growfs
*GROWFS
| noauto
*NOAUTO
| nofail
*NOFAIL
);
616 automount
= fstab_test_option(me
->mnt_opts
,
617 "comment=systemd.automount\0"
618 "x-systemd.automount\0");
620 post
= SPECIAL_INITRD_FS_TARGET
;
621 else if (mount_is_network(me
))
622 post
= SPECIAL_REMOTE_FS_TARGET
;
624 post
= SPECIAL_LOCAL_FS_TARGET
;
626 k
= add_mount(arg_dest
,
628 canonical_where
?: where
,
629 canonical_where
? where
: NULL
,
633 makefs
*MAKEFS
| growfs
*GROWFS
| noauto
*NOAUTO
| nofail
*NOFAIL
| automount
*AUTOMOUNT
,
645 static int add_sysroot_mount(void) {
646 _cleanup_free_
char *what
= NULL
;
650 if (isempty(arg_root_what
)) {
651 log_debug("Could not find a root= entry on the kernel command line.");
655 if (streq(arg_root_what
, "gpt-auto")) {
656 /* This is handled by the gpt-auto generator */
657 log_debug("Skipping root directory handling, as gpt-auto was requested.");
661 if (path_equal(arg_root_what
, "/dev/nfs")) {
662 /* This is handled by the kernel or the initrd */
663 log_debug("Skipping root directory handling, as /dev/nfs was requested.");
667 what
= fstab_node_to_udev_node(arg_root_what
);
671 if (!arg_root_options
)
672 opts
= arg_root_rw
> 0 ? "rw" : "ro";
673 else if (arg_root_rw
>= 0 ||
674 !fstab_test_option(arg_root_options
, "ro\0" "rw\0"))
675 opts
= strjoina(arg_root_options
, ",", arg_root_rw
> 0 ? "rw" : "ro");
677 opts
= arg_root_options
;
679 log_debug("Found entry what=%s where=/sysroot type=%s", what
, strna(arg_root_fstype
));
681 if (is_device_path(what
)) {
682 r
= generator_write_initrd_root_device_deps(arg_dest
, what
);
687 return add_mount(arg_dest
,
693 is_device_path(what
) ? 1 : 0, /* passno */
694 0, /* makefs off, growfs off, noauto off, nofail off, automount off */
695 SPECIAL_INITRD_ROOT_FS_TARGET
,
699 static int add_sysroot_usr_mount(void) {
700 _cleanup_free_
char *what
= NULL
;
703 if (!arg_usr_what
&& !arg_usr_fstype
&& !arg_usr_options
)
706 if (arg_root_what
&& !arg_usr_what
) {
707 /* Copy over the root device, in case the /usr mount just differs in a mount option (consider btrfs subvolumes) */
708 arg_usr_what
= strdup(arg_root_what
);
713 if (arg_root_fstype
&& !arg_usr_fstype
) {
714 arg_usr_fstype
= strdup(arg_root_fstype
);
719 if (arg_root_options
&& !arg_usr_options
) {
720 arg_usr_options
= strdup(arg_root_options
);
721 if (!arg_usr_options
)
728 what
= fstab_node_to_udev_node(arg_usr_what
);
732 if (!arg_usr_options
)
733 opts
= arg_root_rw
> 0 ? "rw" : "ro";
734 else if (!fstab_test_option(arg_usr_options
, "ro\0" "rw\0"))
735 opts
= strjoina(arg_usr_options
, ",", arg_root_rw
> 0 ? "rw" : "ro");
737 opts
= arg_usr_options
;
739 log_debug("Found entry what=%s where=/sysroot/usr type=%s", what
, strna(arg_usr_fstype
));
740 return add_mount(arg_dest
,
746 is_device_path(what
) ? 1 : 0, /* passno */
748 SPECIAL_INITRD_FS_TARGET
,
752 static int add_volatile_root(void) {
753 const char *from
, *to
;
755 if (arg_volatile_mode
!= VOLATILE_YES
)
758 /* Let's add in systemd-remount-volatile.service which will remount the root device to tmpfs if this is
759 * requested, leaving only /usr from the root mount inside. */
761 from
= strjoina(SYSTEM_DATA_UNIT_PATH
"/systemd-volatile-root.service");
762 to
= strjoina(arg_dest
, "/" SPECIAL_INITRD_ROOT_FS_TARGET
, ".requires/systemd-volatile-root.service");
764 (void) mkdir_parents(to
, 0755);
766 if (symlink(from
, to
) < 0)
767 return log_error_errno(errno
, "Failed to hook in volatile remount service: %m");
772 static int add_volatile_var(void) {
774 if (arg_volatile_mode
!= VOLATILE_STATE
)
777 /* If requested, mount /var as tmpfs, but do so only if there's nothing else defined for this. */
779 return add_mount(arg_dest_late
,
787 SPECIAL_LOCAL_FS_TARGET
,
791 static int parse_proc_cmdline_item(const char *key
, const char *value
, void *data
) {
794 /* root=, usr=, usrfstype= and roofstype= may occur more than once, the last
795 * instance should take precedence. In the case of multiple rootflags=
796 * or usrflags= the arguments should be concatenated */
798 if (STR_IN_SET(key
, "fstab", "rd.fstab")) {
800 r
= value
? parse_boolean(value
) : 1;
802 log_warning("Failed to parse fstab switch %s. Ignoring.", value
);
804 arg_fstab_enabled
= r
;
806 } else if (streq(key
, "root")) {
808 if (proc_cmdline_value_missing(key
, value
))
811 if (free_and_strdup(&arg_root_what
, value
) < 0)
814 } else if (streq(key
, "rootfstype")) {
816 if (proc_cmdline_value_missing(key
, value
))
819 if (free_and_strdup(&arg_root_fstype
, value
) < 0)
822 } else if (streq(key
, "rootflags")) {
824 if (proc_cmdline_value_missing(key
, value
))
827 if (!strextend_with_separator(&arg_root_options
, ",", value
, NULL
))
830 } else if (streq(key
, "roothash")) {
832 if (proc_cmdline_value_missing(key
, value
))
835 if (free_and_strdup(&arg_root_hash
, value
) < 0)
838 } else if (streq(key
, "mount.usr")) {
840 if (proc_cmdline_value_missing(key
, value
))
843 if (free_and_strdup(&arg_usr_what
, value
) < 0)
846 } else if (streq(key
, "mount.usrfstype")) {
848 if (proc_cmdline_value_missing(key
, value
))
851 if (free_and_strdup(&arg_usr_fstype
, value
) < 0)
854 } else if (streq(key
, "mount.usrflags")) {
856 if (proc_cmdline_value_missing(key
, value
))
859 if (!strextend_with_separator(&arg_usr_options
, ",", value
, NULL
))
862 } else if (streq(key
, "rw") && !value
)
864 else if (streq(key
, "ro") && !value
)
866 else if (streq(key
, "systemd.volatile")) {
870 m
= volatile_mode_from_string(value
);
872 log_warning("Failed to parse systemd.volatile= argument: %s", value
);
874 arg_volatile_mode
= m
;
876 arg_volatile_mode
= VOLATILE_YES
;
882 static int determine_root(void) {
883 /* If we have a root hash but no root device then Verity is used, and we use the "root" DM device as root. */
891 arg_root_what
= strdup("/dev/mapper/root");
895 log_info("Using verity root device %s.", arg_root_what
);
900 int main(int argc
, char *argv
[]) {
903 if (argc
> 1 && argc
!= 4) {
904 log_error("This program takes three or no arguments.");
911 arg_dest_late
= argv
[3];
913 log_set_target(LOG_TARGET_SAFE
);
914 log_parse_environment();
919 r
= proc_cmdline_parse(parse_proc_cmdline_item
, NULL
, 0);
921 log_warning_errno(r
, "Failed to parse kernel command line, ignoring: %m");
923 (void) determine_root();
925 /* Always honour root= and usr= in the kernel command line if we are in an initrd */
929 r
= add_sysroot_mount();
931 k
= add_sysroot_usr_mount();
935 k
= add_volatile_root();
939 r
= add_volatile_var();
941 /* Honour /etc/fstab only when that's enabled */
942 if (arg_fstab_enabled
) {
945 log_debug("Parsing /etc/fstab");
947 /* Parse the local /etc/fstab, possibly from the initrd */
948 k
= parse_fstab(false);
952 /* If running in the initrd also parse the /etc/fstab from the host */
954 log_debug("Parsing /sysroot/etc/fstab");
956 k
= parse_fstab(true);
963 free(arg_root_fstype
);
964 free(arg_root_options
);
968 free(arg_usr_fstype
);
969 free(arg_usr_options
);
971 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;