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
;
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 r
= generator_open_unit_file(arg_dest
, "/etc/fstab", name
, &f
);
130 fputs("# Automatically generated by systemd-fstab-generator\n\n"
132 "SourcePath=/etc/fstab\n"
133 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n"
136 r
= write_what(f
, what
);
140 r
= write_options(f
, me
->mnt_opts
);
144 r
= fflush_and_check(f
);
146 return log_error_errno(r
, "Failed to write unit file %s: %m", name
);
148 /* use what as where, to have a nicer error message */
149 r
= generator_write_timeouts(arg_dest
, what
, what
, me
->mnt_opts
, NULL
);
153 if (flags
& MAKEFS
) {
154 r
= generator_hook_up_mkswap(arg_dest
, what
);
160 /* TODO: swap devices must be wiped and recreated */
161 log_warning("%s: growing swap devices is currently unsupported.", what
);
163 if (!(flags
& NOAUTO
)) {
164 r
= generator_add_symlink(arg_dest
, SPECIAL_SWAP_TARGET
,
165 (flags
& NOFAIL
) ? "wants" : "requires", name
);
173 static bool mount_is_network(struct mntent
*me
) {
176 return fstab_test_option(me
->mnt_opts
, "_netdev\0") ||
177 fstype_is_network(me
->mnt_type
);
180 static bool mount_in_initrd(struct mntent
*me
) {
183 return fstab_test_option(me
->mnt_opts
, "x-initrd.mount\0") ||
184 streq(me
->mnt_dir
, "/usr");
187 static int write_timeout(FILE *f
, const char *where
, const char *opts
,
188 const char *filter
, const char *variable
) {
189 _cleanup_free_
char *timeout
= NULL
;
190 char timespan
[FORMAT_TIMESPAN_MAX
];
194 r
= fstab_filter_options(opts
, filter
, NULL
, &timeout
, NULL
);
196 return log_warning_errno(r
, "Failed to parse options: %m");
200 r
= parse_sec_fix_0(timeout
, &u
);
202 log_warning("Failed to parse timeout for %s, ignoring: %s", where
, timeout
);
206 fprintf(f
, "%s=%s\n", variable
, format_timespan(timespan
, sizeof(timespan
), u
, 0));
211 static int write_idle_timeout(FILE *f
, const char *where
, const char *opts
) {
212 return write_timeout(f
, where
, opts
,
213 "x-systemd.idle-timeout\0", "TimeoutIdleSec");
216 static int write_mount_timeout(FILE *f
, const char *where
, const char *opts
) {
217 return write_timeout(f
, where
, opts
,
218 "x-systemd.mount-timeout\0", "TimeoutSec");
221 static int write_dependency(FILE *f
, const char *opts
,
222 const char *filter
, const char *format
) {
223 _cleanup_strv_free_
char **names
= NULL
, **units
= NULL
;
224 _cleanup_free_
char *res
= NULL
;
231 r
= fstab_extract_values(opts
, filter
, &names
);
233 return log_warning_errno(r
, "Failed to parse options: %m");
237 STRV_FOREACH(s
, names
) {
240 r
= unit_name_mangle_with_suffix(*s
, UNIT_NAME_NOGLOB
, ".mount", &x
);
242 return log_error_errno(r
, "Failed to generate unit name: %m");
243 r
= strv_consume(&units
, x
);
249 res
= strv_join(units
, " ");
252 #pragma GCC diagnostic push
253 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
254 fprintf(f
, format
, res
);
255 #pragma GCC diagnostic pop
261 static int write_after(FILE *f
, const char *opts
) {
262 return write_dependency(f
, opts
, "x-systemd.after", "After=%1$s\n");
265 static int write_requires_after(FILE *f
, const char *opts
) {
266 return write_dependency(f
, opts
,
267 "x-systemd.requires", "After=%1$s\nRequires=%1$s\n");
270 static int write_before(FILE *f
, const char *opts
) {
271 return write_dependency(f
, opts
,
272 "x-systemd.before", "Before=%1$s\n");
275 static int write_requires_mounts_for(FILE *f
, const char *opts
) {
276 _cleanup_strv_free_
char **paths
= NULL
, **paths_escaped
= NULL
;
277 _cleanup_free_
char *res
= NULL
;
283 r
= fstab_extract_values(opts
, "x-systemd.requires-mounts-for", &paths
);
285 return log_warning_errno(r
, "Failed to parse options: %m");
289 r
= specifier_escape_strv(paths
, &paths_escaped
);
291 return log_error_errno(r
, "Failed to escape paths: %m");
293 res
= strv_join(paths_escaped
, " ");
297 fprintf(f
, "RequiresMountsFor=%s\n", res
);
302 static int add_mount(
306 const char *original_where
,
310 MountpointFlags flags
,
312 const char *source
) {
316 *automount_name
= NULL
,
318 *where_escaped
= NULL
;
319 _cleanup_fclose_
FILE *f
= NULL
;
328 if (streq_ptr(fstype
, "autofs"))
331 if (!is_path(where
)) {
332 log_warning("Mount point %s is not a valid path, ignoring.", where
);
336 if (mount_point_is_api(where
) ||
337 mount_point_ignore(where
))
340 if (path_equal(where
, "/")) {
342 log_warning("Ignoring \"noauto\" for root device");
344 log_warning("Ignoring \"nofail\" for root device");
345 if (flags
& AUTOMOUNT
)
346 log_warning("Ignoring automount option for root device");
348 SET_FLAG(flags
, NOAUTO
| NOFAIL
| AUTOMOUNT
, false);
351 r
= unit_name_from_path(where
, ".mount", &name
);
353 return log_error_errno(r
, "Failed to generate unit name: %m");
355 r
= generator_open_unit_file(dest
, "/etc/fstab", name
, &f
);
362 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
365 if (STRPTR_IN_SET(fstype
, "nfs", "nfs4") && !(flags
& AUTOMOUNT
) &&
366 fstab_test_yes_no_option(opts
, "bg\0" "fg\0")) {
367 /* The default retry timeout that mount.nfs uses for 'bg' mounts
368 * is 10000 minutes, where as it uses 2 minutes for 'fg' mounts.
369 * As we are making 'bg' mounts look like an 'fg' mount to
370 * mount.nfs (so systemd can manage the job-control aspects of 'bg'),
371 * we need to explicitly preserve that default, and also ensure
372 * the systemd mount-timeout doesn't interfere.
373 * By placing these options first, they can be over-ridden by
374 * settings in /etc/fstab. */
375 opts
= strjoina("x-systemd.mount-timeout=infinity,retry=10000,", opts
, ",fg");
376 SET_FLAG(flags
, NOFAIL
, true);
379 if (!(flags
& NOFAIL
) && !(flags
& AUTOMOUNT
))
380 fprintf(f
, "Before=%s\n", post
);
382 if (!(flags
& AUTOMOUNT
) && opts
) {
383 r
= write_after(f
, opts
);
386 r
= write_requires_after(f
, opts
);
389 r
= write_before(f
, opts
);
392 r
= write_requires_mounts_for(f
, opts
);
398 r
= generator_write_fsck_deps(f
, dest
, what
, where
, fstype
);
403 fprintf(f
, "\n[Mount]\n");
405 fprintf(f
, "# Canonicalized from %s\n", original_where
);
407 where_escaped
= specifier_escape(where
);
410 fprintf(f
, "Where=%s\n", where_escaped
);
412 r
= write_what(f
, what
);
416 if (!isempty(fstype
) && !streq(fstype
, "auto")) {
417 _cleanup_free_
char *t
;
419 t
= specifier_escape(fstype
);
423 fprintf(f
, "Type=%s\n", t
);
426 r
= generator_write_timeouts(dest
, what
, where
, opts
, &filtered
);
430 r
= generator_write_device_deps(dest
, what
, where
, opts
);
434 r
= write_mount_timeout(f
, where
, opts
);
438 r
= write_options(f
, filtered
);
442 r
= fflush_and_check(f
);
444 return log_error_errno(r
, "Failed to write unit file %s: %m", name
);
446 if (flags
& MAKEFS
) {
447 r
= generator_hook_up_mkfs(dest
, what
, where
, fstype
);
452 if (flags
& GROWFS
) {
453 r
= generator_hook_up_growfs(dest
, where
, post
);
458 if (!(flags
& NOAUTO
) && !(flags
& AUTOMOUNT
)) {
459 r
= generator_add_symlink(dest
, post
,
460 (flags
& NOFAIL
) ? "wants" : "requires", name
);
465 if (flags
& AUTOMOUNT
) {
466 r
= unit_name_from_path(where
, ".automount", &automount_name
);
468 return log_error_errno(r
, "Failed to generate unit name: %m");
472 r
= generator_open_unit_file(dest
, "/etc/fstab", automount_name
, &f
);
479 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
482 fprintf(f
, "Before=%s\n", post
);
485 r
= write_after(f
, opts
);
488 r
= write_requires_after(f
, opts
);
491 r
= write_before(f
, opts
);
494 r
= write_requires_mounts_for(f
, opts
);
505 r
= write_idle_timeout(f
, where
, opts
);
509 r
= fflush_and_check(f
);
511 return log_error_errno(r
, "Failed to write unit file %s: %m", automount_name
);
513 r
= generator_add_symlink(dest
, post
,
514 (flags
& NOFAIL
) ? "wants" : "requires", automount_name
);
522 static int parse_fstab(bool initrd
) {
523 _cleanup_endmntent_
FILE *f
= NULL
;
524 const char *fstab_path
;
528 fstab_path
= initrd
? "/sysroot/etc/fstab" : "/etc/fstab";
529 f
= setmntent(fstab_path
, "re");
534 return log_error_errno(errno
, "Failed to open %s: %m", fstab_path
);
537 while ((me
= getmntent(f
))) {
538 _cleanup_free_
char *where
= NULL
, *what
= NULL
, *canonical_where
= NULL
;
539 bool makefs
, growfs
, noauto
, nofail
;
542 if (initrd
&& !mount_in_initrd(me
))
545 what
= fstab_node_to_udev_node(me
->mnt_fsname
);
549 if (is_device_path(what
) && path_is_read_only_fs("sys") > 0) {
550 log_info("Running in a container, ignoring fstab device entry for %s.", what
);
554 where
= strdup(me
->mnt_dir
);
558 if (is_path(where
)) {
559 path_kill_slashes(where
);
560 /* Follow symlinks here; see 5261ba901845c084de5a8fd06500ed09bfb0bd80 which makes sense for
561 * mount units, but causes problems since it historically worked to have symlinks in e.g.
562 * /etc/fstab. So we canonicalize here. Note that we use CHASE_NONEXISTENT to handle the case
563 * where a symlink refers to another mount target; this works assuming the sub-mountpoint
564 * target is the final directory.
566 r
= chase_symlinks(where
, initrd
? "/sysroot" : NULL
,
567 CHASE_PREFIX_ROOT
| CHASE_NONEXISTENT
,
570 /* In this case for now we continue on as if it wasn't a symlink */
571 log_warning_errno(r
, "Failed to read symlink target for %s: %m", where
);
573 if (streq(canonical_where
, where
))
574 canonical_where
= mfree(canonical_where
);
576 log_debug("Canonicalized what=%s where=%s to %s",
577 what
, where
, canonical_where
);
581 makefs
= fstab_test_option(me
->mnt_opts
, "x-systemd.makefs\0");
582 growfs
= fstab_test_option(me
->mnt_opts
, "x-systemd.growfs\0");
583 noauto
= fstab_test_yes_no_option(me
->mnt_opts
, "noauto\0" "auto\0");
584 nofail
= fstab_test_yes_no_option(me
->mnt_opts
, "nofail\0" "fail\0");
585 log_debug("Found entry what=%s where=%s type=%s makefs=%s nofail=%s noauto=%s",
586 what
, where
, me
->mnt_type
,
588 yes_no(noauto
), yes_no(nofail
));
590 if (streq(me
->mnt_type
, "swap"))
591 k
= add_swap(what
, me
,
592 makefs
*MAKEFS
| growfs
*GROWFS
| noauto
*NOAUTO
| nofail
*NOFAIL
);
597 automount
= fstab_test_option(me
->mnt_opts
,
598 "comment=systemd.automount\0"
599 "x-systemd.automount\0");
601 post
= SPECIAL_INITRD_FS_TARGET
;
602 else if (mount_is_network(me
))
603 post
= SPECIAL_REMOTE_FS_TARGET
;
605 post
= SPECIAL_LOCAL_FS_TARGET
;
607 k
= add_mount(arg_dest
,
609 canonical_where
?: where
,
610 canonical_where
? where
: NULL
,
614 makefs
*MAKEFS
| growfs
*GROWFS
| noauto
*NOAUTO
| nofail
*NOFAIL
| automount
*AUTOMOUNT
,
626 static int add_sysroot_mount(void) {
627 _cleanup_free_
char *what
= NULL
;
631 if (isempty(arg_root_what
)) {
632 log_debug("Could not find a root= entry on the kernel command line.");
636 if (streq(arg_root_what
, "gpt-auto")) {
637 /* This is handled by the gpt-auto generator */
638 log_debug("Skipping root directory handling, as gpt-auto was requested.");
642 if (path_equal(arg_root_what
, "/dev/nfs")) {
643 /* This is handled by the kernel or the initrd */
644 log_debug("Skipping root directory handling, as /dev/nfs was requested.");
648 what
= fstab_node_to_udev_node(arg_root_what
);
652 if (!arg_root_options
)
653 opts
= arg_root_rw
> 0 ? "rw" : "ro";
654 else if (arg_root_rw
>= 0 ||
655 !fstab_test_option(arg_root_options
, "ro\0" "rw\0"))
656 opts
= strjoina(arg_root_options
, ",", arg_root_rw
> 0 ? "rw" : "ro");
658 opts
= arg_root_options
;
660 log_debug("Found entry what=%s where=/sysroot type=%s", what
, strna(arg_root_fstype
));
662 if (is_device_path(what
)) {
663 r
= generator_write_initrd_root_device_deps(arg_dest
, what
);
668 return add_mount(arg_dest
,
674 is_device_path(what
) ? 1 : 0, /* passno */
675 0, /* makefs off, growfs off, noauto off, nofail off, automount off */
676 SPECIAL_INITRD_ROOT_FS_TARGET
,
680 static int add_sysroot_usr_mount(void) {
681 _cleanup_free_
char *what
= NULL
;
684 if (!arg_usr_what
&& !arg_usr_fstype
&& !arg_usr_options
)
687 if (arg_root_what
&& !arg_usr_what
) {
688 /* Copy over the root device, in case the /usr mount just differs in a mount option (consider btrfs subvolumes) */
689 arg_usr_what
= strdup(arg_root_what
);
694 if (arg_root_fstype
&& !arg_usr_fstype
) {
695 arg_usr_fstype
= strdup(arg_root_fstype
);
700 if (arg_root_options
&& !arg_usr_options
) {
701 arg_usr_options
= strdup(arg_root_options
);
702 if (!arg_usr_options
)
709 what
= fstab_node_to_udev_node(arg_usr_what
);
713 if (!arg_usr_options
)
714 opts
= arg_root_rw
> 0 ? "rw" : "ro";
715 else if (!fstab_test_option(arg_usr_options
, "ro\0" "rw\0"))
716 opts
= strjoina(arg_usr_options
, ",", arg_root_rw
> 0 ? "rw" : "ro");
718 opts
= arg_usr_options
;
720 log_debug("Found entry what=%s where=/sysroot/usr type=%s", what
, strna(arg_usr_fstype
));
721 return add_mount(arg_dest
,
727 is_device_path(what
) ? 1 : 0, /* passno */
729 SPECIAL_INITRD_FS_TARGET
,
733 static int add_volatile_root(void) {
734 const char *from
, *to
;
736 if (arg_volatile_mode
!= VOLATILE_YES
)
739 /* Let's add in systemd-remount-volatile.service which will remount the root device to tmpfs if this is
740 * requested, leaving only /usr from the root mount inside. */
742 from
= strjoina(SYSTEM_DATA_UNIT_PATH
"/systemd-volatile-root.service");
743 to
= strjoina(arg_dest
, "/" SPECIAL_INITRD_ROOT_FS_TARGET
, ".requires/systemd-volatile-root.service");
745 (void) mkdir_parents(to
, 0755);
747 if (symlink(from
, to
) < 0)
748 return log_error_errno(errno
, "Failed to hook in volatile remount service: %m");
753 static int add_volatile_var(void) {
755 if (arg_volatile_mode
!= VOLATILE_STATE
)
758 /* If requested, mount /var as tmpfs, but do so only if there's nothing else defined for this. */
760 return add_mount(arg_dest_late
,
768 SPECIAL_LOCAL_FS_TARGET
,
772 static int parse_proc_cmdline_item(const char *key
, const char *value
, void *data
) {
775 /* root=, usr=, usrfstype= and roofstype= may occur more than once, the last
776 * instance should take precedence. In the case of multiple rootflags=
777 * or usrflags= the arguments should be concatenated */
779 if (STR_IN_SET(key
, "fstab", "rd.fstab")) {
781 r
= value
? parse_boolean(value
) : 1;
783 log_warning("Failed to parse fstab switch %s. Ignoring.", value
);
785 arg_fstab_enabled
= r
;
787 } else if (streq(key
, "root")) {
789 if (proc_cmdline_value_missing(key
, value
))
792 if (free_and_strdup(&arg_root_what
, value
) < 0)
795 } else if (streq(key
, "rootfstype")) {
797 if (proc_cmdline_value_missing(key
, value
))
800 if (free_and_strdup(&arg_root_fstype
, value
) < 0)
803 } else if (streq(key
, "rootflags")) {
805 if (proc_cmdline_value_missing(key
, value
))
808 if (!strextend_with_separator(&arg_root_options
, ",", value
, NULL
))
811 } else if (streq(key
, "roothash")) {
813 if (proc_cmdline_value_missing(key
, value
))
816 if (free_and_strdup(&arg_root_hash
, value
) < 0)
819 } else if (streq(key
, "mount.usr")) {
821 if (proc_cmdline_value_missing(key
, value
))
824 if (free_and_strdup(&arg_usr_what
, value
) < 0)
827 } else if (streq(key
, "mount.usrfstype")) {
829 if (proc_cmdline_value_missing(key
, value
))
832 if (free_and_strdup(&arg_usr_fstype
, value
) < 0)
835 } else if (streq(key
, "mount.usrflags")) {
837 if (proc_cmdline_value_missing(key
, value
))
840 if (!strextend_with_separator(&arg_usr_options
, ",", value
, NULL
))
843 } else if (streq(key
, "rw") && !value
)
845 else if (streq(key
, "ro") && !value
)
847 else if (streq(key
, "systemd.volatile")) {
851 m
= volatile_mode_from_string(value
);
853 log_warning("Failed to parse systemd.volatile= argument: %s", value
);
855 arg_volatile_mode
= m
;
857 arg_volatile_mode
= VOLATILE_YES
;
863 static int determine_root(void) {
864 /* If we have a root hash but no root device then Verity is used, and we use the "root" DM device as root. */
872 arg_root_what
= strdup("/dev/mapper/root");
876 log_info("Using verity root device %s.", arg_root_what
);
881 int main(int argc
, char *argv
[]) {
884 if (argc
> 1 && argc
!= 4) {
885 log_error("This program takes three or no arguments.");
892 arg_dest_late
= argv
[3];
894 log_set_target(LOG_TARGET_SAFE
);
895 log_parse_environment();
900 r
= proc_cmdline_parse(parse_proc_cmdline_item
, NULL
, 0);
902 log_warning_errno(r
, "Failed to parse kernel command line, ignoring: %m");
904 (void) determine_root();
906 /* Always honour root= and usr= in the kernel command line if we are in an initrd */
910 r
= add_sysroot_mount();
912 k
= add_sysroot_usr_mount();
916 k
= add_volatile_root();
920 r
= add_volatile_var();
922 /* Honour /etc/fstab only when that's enabled */
923 if (arg_fstab_enabled
) {
926 log_debug("Parsing /etc/fstab");
928 /* Parse the local /etc/fstab, possibly from the initrd */
929 k
= parse_fstab(false);
933 /* If running in the initrd also parse the /etc/fstab from the host */
935 log_debug("Parsing /sysroot/etc/fstab");
937 k
= parse_fstab(true);
944 free(arg_root_fstype
);
945 free(arg_root_options
);
949 free(arg_usr_fstype
);
950 free(arg_usr_options
);
952 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;