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
);
561 /* Follow symlinks here; see 5261ba901845c084de5a8fd06500ed09bfb0bd80 which makes sense for
562 * mount units, but causes problems since it historically worked to have symlinks in e.g.
563 * /etc/fstab. So we canonicalize here. Note that we use CHASE_NONEXISTENT to handle the case
564 * where a symlink refers to another mount target; this works assuming the sub-mountpoint
565 * target is the final directory. */
566 r
= chase_symlinks(where
, initrd
? "/sysroot" : NULL
,
567 CHASE_PREFIX_ROOT
| CHASE_NONEXISTENT
,
569 if (r
< 0) /* If we can't canonicalize we continue on as if it wasn't a symlink */
570 log_debug_errno(r
, "Failed to read symlink target for %s, ignoring: %m", where
);
571 else if (streq(canonical_where
, where
)) /* If it was fully canonicalized, suppress the change */
572 canonical_where
= mfree(canonical_where
);
574 log_debug("Canonicalized what=%s where=%s to %s", what
, where
, canonical_where
);
577 makefs
= fstab_test_option(me
->mnt_opts
, "x-systemd.makefs\0");
578 growfs
= fstab_test_option(me
->mnt_opts
, "x-systemd.growfs\0");
579 noauto
= fstab_test_yes_no_option(me
->mnt_opts
, "noauto\0" "auto\0");
580 nofail
= fstab_test_yes_no_option(me
->mnt_opts
, "nofail\0" "fail\0");
582 log_debug("Found entry what=%s where=%s type=%s makefs=%s nofail=%s noauto=%s",
583 what
, where
, me
->mnt_type
,
585 yes_no(noauto
), yes_no(nofail
));
587 if (streq(me
->mnt_type
, "swap"))
588 k
= add_swap(what
, me
,
589 makefs
*MAKEFS
| growfs
*GROWFS
| noauto
*NOAUTO
| nofail
*NOFAIL
);
594 automount
= fstab_test_option(me
->mnt_opts
,
595 "comment=systemd.automount\0"
596 "x-systemd.automount\0");
598 post
= SPECIAL_INITRD_FS_TARGET
;
599 else if (mount_is_network(me
))
600 post
= SPECIAL_REMOTE_FS_TARGET
;
602 post
= SPECIAL_LOCAL_FS_TARGET
;
604 k
= add_mount(arg_dest
,
606 canonical_where
?: where
,
607 canonical_where
? where
: NULL
,
611 makefs
*MAKEFS
| growfs
*GROWFS
| noauto
*NOAUTO
| nofail
*NOFAIL
| automount
*AUTOMOUNT
,
623 static int add_sysroot_mount(void) {
624 _cleanup_free_
char *what
= NULL
;
628 if (isempty(arg_root_what
)) {
629 log_debug("Could not find a root= entry on the kernel command line.");
633 if (streq(arg_root_what
, "gpt-auto")) {
634 /* This is handled by the gpt-auto generator */
635 log_debug("Skipping root directory handling, as gpt-auto was requested.");
639 if (path_equal(arg_root_what
, "/dev/nfs")) {
640 /* This is handled by the kernel or the initrd */
641 log_debug("Skipping root directory handling, as /dev/nfs was requested.");
645 what
= fstab_node_to_udev_node(arg_root_what
);
649 if (!arg_root_options
)
650 opts
= arg_root_rw
> 0 ? "rw" : "ro";
651 else if (arg_root_rw
>= 0 ||
652 !fstab_test_option(arg_root_options
, "ro\0" "rw\0"))
653 opts
= strjoina(arg_root_options
, ",", arg_root_rw
> 0 ? "rw" : "ro");
655 opts
= arg_root_options
;
657 log_debug("Found entry what=%s where=/sysroot type=%s", what
, strna(arg_root_fstype
));
659 if (is_device_path(what
)) {
660 r
= generator_write_initrd_root_device_deps(arg_dest
, what
);
665 return add_mount(arg_dest
,
671 is_device_path(what
) ? 1 : 0, /* passno */
672 0, /* makefs off, growfs off, noauto off, nofail off, automount off */
673 SPECIAL_INITRD_ROOT_FS_TARGET
,
677 static int add_sysroot_usr_mount(void) {
678 _cleanup_free_
char *what
= NULL
;
681 if (!arg_usr_what
&& !arg_usr_fstype
&& !arg_usr_options
)
684 if (arg_root_what
&& !arg_usr_what
) {
685 /* Copy over the root device, in case the /usr mount just differs in a mount option (consider btrfs subvolumes) */
686 arg_usr_what
= strdup(arg_root_what
);
691 if (arg_root_fstype
&& !arg_usr_fstype
) {
692 arg_usr_fstype
= strdup(arg_root_fstype
);
697 if (arg_root_options
&& !arg_usr_options
) {
698 arg_usr_options
= strdup(arg_root_options
);
699 if (!arg_usr_options
)
706 what
= fstab_node_to_udev_node(arg_usr_what
);
710 if (!arg_usr_options
)
711 opts
= arg_root_rw
> 0 ? "rw" : "ro";
712 else if (!fstab_test_option(arg_usr_options
, "ro\0" "rw\0"))
713 opts
= strjoina(arg_usr_options
, ",", arg_root_rw
> 0 ? "rw" : "ro");
715 opts
= arg_usr_options
;
717 log_debug("Found entry what=%s where=/sysroot/usr type=%s", what
, strna(arg_usr_fstype
));
718 return add_mount(arg_dest
,
724 is_device_path(what
) ? 1 : 0, /* passno */
726 SPECIAL_INITRD_FS_TARGET
,
730 static int add_volatile_root(void) {
731 const char *from
, *to
;
733 if (arg_volatile_mode
!= VOLATILE_YES
)
736 /* Let's add in systemd-remount-volatile.service which will remount the root device to tmpfs if this is
737 * requested, leaving only /usr from the root mount inside. */
739 from
= strjoina(SYSTEM_DATA_UNIT_PATH
"/systemd-volatile-root.service");
740 to
= strjoina(arg_dest
, "/" SPECIAL_INITRD_ROOT_FS_TARGET
, ".requires/systemd-volatile-root.service");
742 (void) mkdir_parents(to
, 0755);
744 if (symlink(from
, to
) < 0)
745 return log_error_errno(errno
, "Failed to hook in volatile remount service: %m");
750 static int add_volatile_var(void) {
752 if (arg_volatile_mode
!= VOLATILE_STATE
)
755 /* If requested, mount /var as tmpfs, but do so only if there's nothing else defined for this. */
757 return add_mount(arg_dest_late
,
765 SPECIAL_LOCAL_FS_TARGET
,
769 static int parse_proc_cmdline_item(const char *key
, const char *value
, void *data
) {
772 /* root=, usr=, usrfstype= and roofstype= may occur more than once, the last
773 * instance should take precedence. In the case of multiple rootflags=
774 * or usrflags= the arguments should be concatenated */
776 if (STR_IN_SET(key
, "fstab", "rd.fstab")) {
778 r
= value
? parse_boolean(value
) : 1;
780 log_warning("Failed to parse fstab switch %s. Ignoring.", value
);
782 arg_fstab_enabled
= r
;
784 } else if (streq(key
, "root")) {
786 if (proc_cmdline_value_missing(key
, value
))
789 if (free_and_strdup(&arg_root_what
, value
) < 0)
792 } else if (streq(key
, "rootfstype")) {
794 if (proc_cmdline_value_missing(key
, value
))
797 if (free_and_strdup(&arg_root_fstype
, value
) < 0)
800 } else if (streq(key
, "rootflags")) {
802 if (proc_cmdline_value_missing(key
, value
))
805 if (!strextend_with_separator(&arg_root_options
, ",", value
, NULL
))
808 } else if (streq(key
, "roothash")) {
810 if (proc_cmdline_value_missing(key
, value
))
813 if (free_and_strdup(&arg_root_hash
, value
) < 0)
816 } else if (streq(key
, "mount.usr")) {
818 if (proc_cmdline_value_missing(key
, value
))
821 if (free_and_strdup(&arg_usr_what
, value
) < 0)
824 } else if (streq(key
, "mount.usrfstype")) {
826 if (proc_cmdline_value_missing(key
, value
))
829 if (free_and_strdup(&arg_usr_fstype
, value
) < 0)
832 } else if (streq(key
, "mount.usrflags")) {
834 if (proc_cmdline_value_missing(key
, value
))
837 if (!strextend_with_separator(&arg_usr_options
, ",", value
, NULL
))
840 } else if (streq(key
, "rw") && !value
)
842 else if (streq(key
, "ro") && !value
)
844 else if (streq(key
, "systemd.volatile")) {
848 m
= volatile_mode_from_string(value
);
850 log_warning("Failed to parse systemd.volatile= argument: %s", value
);
852 arg_volatile_mode
= m
;
854 arg_volatile_mode
= VOLATILE_YES
;
860 static int determine_root(void) {
861 /* If we have a root hash but no root device then Verity is used, and we use the "root" DM device as root. */
869 arg_root_what
= strdup("/dev/mapper/root");
873 log_info("Using verity root device %s.", arg_root_what
);
878 int main(int argc
, char *argv
[]) {
881 if (argc
> 1 && argc
!= 4) {
882 log_error("This program takes three or no arguments.");
889 arg_dest_late
= argv
[3];
891 log_set_prohibit_ipc(true);
892 log_set_target(LOG_TARGET_AUTO
);
893 log_parse_environment();
898 r
= proc_cmdline_parse(parse_proc_cmdline_item
, NULL
, 0);
900 log_warning_errno(r
, "Failed to parse kernel command line, ignoring: %m");
902 (void) determine_root();
904 /* Always honour root= and usr= in the kernel command line if we are in an initrd */
908 r
= add_sysroot_mount();
910 k
= add_sysroot_usr_mount();
914 k
= add_volatile_root();
918 r
= add_volatile_var();
920 /* Honour /etc/fstab only when that's enabled */
921 if (arg_fstab_enabled
) {
924 log_debug("Parsing /etc/fstab");
926 /* Parse the local /etc/fstab, possibly from the initrd */
927 k
= parse_fstab(false);
931 /* If running in the initrd also parse the /etc/fstab from the host */
933 log_debug("Parsing /sysroot/etc/fstab");
935 k
= parse_fstab(true);
942 free(arg_root_fstype
);
943 free(arg_root_options
);
947 free(arg_usr_fstype
);
948 free(arg_usr_options
);
950 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;