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 static const char *arg_dest
= "/tmp";
51 static const char *arg_dest_late
= "/tmp";
52 static bool arg_fstab_enabled
= true;
53 static char *arg_root_what
= NULL
;
54 static char *arg_root_fstype
= NULL
;
55 static char *arg_root_options
= NULL
;
56 static char *arg_root_hash
= NULL
;
57 static int arg_root_rw
= -1;
58 static char *arg_usr_what
= NULL
;
59 static char *arg_usr_fstype
= NULL
;
60 static char *arg_usr_options
= NULL
;
61 static VolatileMode arg_volatile_mode
= _VOLATILE_MODE_INVALID
;
63 static int write_options(FILE *f
, const char *options
) {
64 _cleanup_free_
char *o
= NULL
;
69 if (streq(options
, "defaults"))
72 o
= specifier_escape(options
);
76 fprintf(f
, "Options=%s\n", o
);
80 static int write_what(FILE *f
, const char *what
) {
81 _cleanup_free_
char *w
= NULL
;
83 w
= specifier_escape(what
);
87 fprintf(f
, "What=%s\n", w
);
97 _cleanup_free_
char *name
= NULL
, *unit
= NULL
;
98 _cleanup_fclose_
FILE *f
= NULL
;
104 if (access("/proc/swaps", F_OK
) < 0) {
105 log_info("Swap not supported, ignoring fstab swap entry for %s.", what
);
109 if (detect_container() > 0) {
110 log_info("Running in a container, ignoring fstab swap entry for %s.", what
);
114 r
= unit_name_from_path(what
, ".swap", &name
);
116 return log_error_errno(r
, "Failed to generate unit name: %m");
118 unit
= strjoin(arg_dest
, "/", name
);
122 f
= fopen(unit
, "wxe");
124 return log_error_errno(errno
,
126 "Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?" :
127 "Failed to create unit file %s: %m",
130 fputs_unlocked("# 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", unit
);
148 /* use what as where, to have a nicer error message */
149 r
= generator_write_timeouts(arg_dest
, what
, what
, me
->mnt_opts
, NULL
);
154 r
= generator_add_symlink(arg_dest
, SPECIAL_SWAP_TARGET
,
155 nofail
? "wants" : "requires", name
);
163 static bool mount_is_network(struct mntent
*me
) {
166 return fstab_test_option(me
->mnt_opts
, "_netdev\0") ||
167 fstype_is_network(me
->mnt_type
);
170 static bool mount_in_initrd(struct mntent
*me
) {
173 return fstab_test_option(me
->mnt_opts
, "x-initrd.mount\0") ||
174 streq(me
->mnt_dir
, "/usr");
177 static int write_timeout(FILE *f
, const char *where
, const char *opts
,
178 const char *filter
, const char *variable
) {
179 _cleanup_free_
char *timeout
= NULL
;
180 char timespan
[FORMAT_TIMESPAN_MAX
];
184 r
= fstab_filter_options(opts
, filter
, NULL
, &timeout
, NULL
);
186 return log_warning_errno(r
, "Failed to parse options: %m");
190 r
= parse_sec_fix_0(timeout
, &u
);
192 log_warning("Failed to parse timeout for %s, ignoring: %s", where
, timeout
);
196 fprintf(f
, "%s=%s\n", variable
, format_timespan(timespan
, sizeof(timespan
), u
, 0));
201 static int write_idle_timeout(FILE *f
, const char *where
, const char *opts
) {
202 return write_timeout(f
, where
, opts
,
203 "x-systemd.idle-timeout\0", "TimeoutIdleSec");
206 static int write_mount_timeout(FILE *f
, const char *where
, const char *opts
) {
207 return write_timeout(f
, where
, opts
,
208 "x-systemd.mount-timeout\0", "TimeoutSec");
211 static int write_dependency(FILE *f
, const char *opts
,
212 const char *filter
, const char *format
) {
213 _cleanup_strv_free_
char **names
= NULL
, **units
= NULL
;
214 _cleanup_free_
char *res
= NULL
;
221 r
= fstab_extract_values(opts
, filter
, &names
);
223 return log_warning_errno(r
, "Failed to parse options: %m");
227 STRV_FOREACH(s
, names
) {
230 r
= unit_name_mangle_with_suffix(*s
, UNIT_NAME_NOGLOB
, ".mount", &x
);
232 return log_error_errno(r
, "Failed to generate unit name: %m");
233 r
= strv_consume(&units
, x
);
239 res
= strv_join(units
, " ");
242 #pragma GCC diagnostic push
243 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
244 fprintf(f
, format
, res
);
245 #pragma GCC diagnostic pop
251 static int write_after(FILE *f
, const char *opts
) {
252 return write_dependency(f
, opts
, "x-systemd.after", "After=%1$s\n");
255 static int write_requires_after(FILE *f
, const char *opts
) {
256 return write_dependency(f
, opts
,
257 "x-systemd.requires", "After=%1$s\nRequires=%1$s\n");
260 static int write_before(FILE *f
, const char *opts
) {
261 return write_dependency(f
, opts
,
262 "x-systemd.before", "Before=%1$s\n");
265 static int write_requires_mounts_for(FILE *f
, const char *opts
) {
266 _cleanup_strv_free_
char **paths
= NULL
, **paths_escaped
= NULL
;
267 _cleanup_free_
char *res
= NULL
;
273 r
= fstab_extract_values(opts
, "x-systemd.requires-mounts-for", &paths
);
275 return log_warning_errno(r
, "Failed to parse options: %m");
279 r
= specifier_escape_strv(paths
, &paths_escaped
);
281 return log_error_errno(r
, "Failed to escape paths: %m");
283 res
= strv_join(paths_escaped
, " ");
287 fprintf(f
, "RequiresMountsFor=%s\n", res
);
292 static int add_mount(
296 const char *original_where
,
304 const char *source
) {
307 *name
= NULL
, *unit
= NULL
,
308 *automount_name
= NULL
, *automount_unit
= NULL
,
310 *where_escaped
= NULL
;
311 _cleanup_fclose_
FILE *f
= NULL
;
320 if (streq_ptr(fstype
, "autofs"))
323 if (!is_path(where
)) {
324 log_warning("Mount point %s is not a valid path, ignoring.", where
);
328 if (mount_point_is_api(where
) ||
329 mount_point_ignore(where
))
332 if (path_equal(where
, "/")) {
334 log_warning("Ignoring \"noauto\" for root device");
336 log_warning("Ignoring \"nofail\" for root device");
338 log_warning("Ignoring automount option for root device");
340 noauto
= nofail
= automount
= false;
343 r
= unit_name_from_path(where
, ".mount", &name
);
345 return log_error_errno(r
, "Failed to generate unit name: %m");
347 unit
= strjoin(dest
, "/", name
);
351 f
= fopen(unit
, "wxe");
353 return log_error_errno(errno
,
355 "Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?" :
356 "Failed to create unit file %s: %m",
360 "# Automatically generated by systemd-fstab-generator\n\n"
363 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
366 if (STRPTR_IN_SET(fstype
, "nfs", "nfs4") && !automount
&&
367 fstab_test_yes_no_option(opts
, "bg\0" "fg\0")) {
368 /* The default retry timeout that mount.nfs uses for 'bg' mounts
369 * is 10000 minutes, where as it uses 2 minutes for 'fg' mounts.
370 * As we are making 'bg' mounts look like an 'fg' mount to
371 * mount.nfs (so systemd can manage the job-control aspects of 'bg'),
372 * we need to explicitly preserve that default, and also ensure
373 * the systemd mount-timeout doesn't interfere.
374 * By placing these options first, they can be over-ridden by
375 * settings in /etc/fstab. */
376 opts
= strjoina("x-systemd.mount-timeout=infinity,retry=10000,", opts
, ",fg");
380 if (!nofail
&& !automount
)
381 fprintf(f
, "Before=%s\n", post
);
383 if (!automount
&& opts
) {
384 r
= write_after(f
, opts
);
387 r
= write_requires_after(f
, opts
);
390 r
= write_before(f
, opts
);
393 r
= write_requires_mounts_for(f
, opts
);
399 r
= generator_write_fsck_deps(f
, dest
, what
, where
, fstype
);
404 fprintf(f
, "\n[Mount]\n");
406 fprintf(f
, "# Canonicalized from %s\n", original_where
);
408 where_escaped
= specifier_escape(where
);
411 fprintf(f
, "Where=%s\n", where_escaped
);
413 r
= write_what(f
, what
);
417 if (!isempty(fstype
) && !streq(fstype
, "auto")) {
418 _cleanup_free_
char *t
;
420 t
= specifier_escape(fstype
);
424 fprintf(f
, "Type=%s\n", t
);
427 r
= generator_write_timeouts(dest
, what
, where
, opts
, &filtered
);
431 r
= generator_write_device_deps(dest
, what
, where
, opts
);
435 r
= write_mount_timeout(f
, where
, opts
);
439 r
= write_options(f
, filtered
);
443 r
= fflush_and_check(f
);
445 return log_error_errno(r
, "Failed to write unit file %s: %m", unit
);
447 if (!noauto
&& !automount
) {
448 r
= generator_add_symlink(dest
, post
,
449 nofail
? "wants" : "requires", name
);
455 r
= unit_name_from_path(where
, ".automount", &automount_name
);
457 return log_error_errno(r
, "Failed to generate unit name: %m");
459 automount_unit
= strjoin(dest
, "/", automount_name
);
464 f
= fopen(automount_unit
, "wxe");
466 return log_error_errno(errno
, "Failed to create unit file %s: %m", automount_unit
);
469 "# Automatically generated by systemd-fstab-generator\n\n"
472 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
475 fprintf(f
, "Before=%s\n", post
);
478 r
= write_after(f
, opts
);
481 r
= write_requires_after(f
, opts
);
484 r
= write_before(f
, opts
);
487 r
= write_requires_mounts_for(f
, opts
);
498 r
= write_idle_timeout(f
, where
, opts
);
502 r
= fflush_and_check(f
);
504 return log_error_errno(r
, "Failed to write unit file %s: %m", automount_unit
);
506 r
= generator_add_symlink(dest
, post
,
507 nofail
? "wants" : "requires", automount_name
);
515 static int parse_fstab(bool initrd
) {
516 _cleanup_endmntent_
FILE *f
= NULL
;
517 const char *fstab_path
;
521 fstab_path
= initrd
? "/sysroot/etc/fstab" : "/etc/fstab";
522 f
= setmntent(fstab_path
, "re");
527 return log_error_errno(errno
, "Failed to open %s: %m", fstab_path
);
530 while ((me
= getmntent(f
))) {
531 _cleanup_free_
char *where
= NULL
, *what
= NULL
, *canonical_where
= NULL
;
535 if (initrd
&& !mount_in_initrd(me
))
538 what
= fstab_node_to_udev_node(me
->mnt_fsname
);
542 if (is_device_path(what
) && path_is_read_only_fs("sys") > 0) {
543 log_info("Running in a container, ignoring fstab device entry for %s.", what
);
547 where
= strdup(me
->mnt_dir
);
551 if (is_path(where
)) {
552 path_kill_slashes(where
);
553 /* Follow symlinks here; see 5261ba901845c084de5a8fd06500ed09bfb0bd80 which makes sense for
554 * mount units, but causes problems since it historically worked to have symlinks in e.g.
555 * /etc/fstab. So we canonicalize here. Note that we use CHASE_NONEXISTENT to handle the case
556 * where a symlink refers to another mount target; this works assuming the sub-mountpoint
557 * target is the final directory.
559 r
= chase_symlinks(where
, initrd
? "/sysroot" : NULL
,
560 CHASE_PREFIX_ROOT
| CHASE_NONEXISTENT
,
563 /* In this case for now we continue on as if it wasn't a symlink */
564 log_warning_errno(r
, "Failed to read symlink target for %s: %m", where
);
566 if (streq(canonical_where
, where
))
567 canonical_where
= mfree(canonical_where
);
569 log_debug("Canonicalized what=%s where=%s to %s",
570 what
, where
, canonical_where
);
574 noauto
= fstab_test_yes_no_option(me
->mnt_opts
, "noauto\0" "auto\0");
575 nofail
= fstab_test_yes_no_option(me
->mnt_opts
, "nofail\0" "fail\0");
576 log_debug("Found entry what=%s where=%s type=%s nofail=%s noauto=%s",
577 what
, where
, me
->mnt_type
,
578 yes_no(noauto
), yes_no(nofail
));
580 if (streq(me
->mnt_type
, "swap"))
581 k
= add_swap(what
, me
, noauto
, nofail
);
586 automount
= fstab_test_option(me
->mnt_opts
,
587 "comment=systemd.automount\0"
588 "x-systemd.automount\0");
590 post
= SPECIAL_INITRD_FS_TARGET
;
591 else if (mount_is_network(me
))
592 post
= SPECIAL_REMOTE_FS_TARGET
;
594 post
= SPECIAL_LOCAL_FS_TARGET
;
596 k
= add_mount(arg_dest
,
598 canonical_where
?: where
,
599 canonical_where
? where
: NULL
,
617 static int add_sysroot_mount(void) {
618 _cleanup_free_
char *what
= NULL
;
622 if (isempty(arg_root_what
)) {
623 log_debug("Could not find a root= entry on the kernel command line.");
627 if (streq(arg_root_what
, "gpt-auto")) {
628 /* This is handled by the gpt-auto generator */
629 log_debug("Skipping root directory handling, as gpt-auto was requested.");
633 if (path_equal(arg_root_what
, "/dev/nfs")) {
634 /* This is handled by the kernel or the initrd */
635 log_debug("Skipping root directory handling, as /dev/nfs was requested.");
639 what
= fstab_node_to_udev_node(arg_root_what
);
643 if (!arg_root_options
)
644 opts
= arg_root_rw
> 0 ? "rw" : "ro";
645 else if (arg_root_rw
>= 0 ||
646 !fstab_test_option(arg_root_options
, "ro\0" "rw\0"))
647 opts
= strjoina(arg_root_options
, ",", arg_root_rw
> 0 ? "rw" : "ro");
649 opts
= arg_root_options
;
651 log_debug("Found entry what=%s where=/sysroot type=%s", what
, strna(arg_root_fstype
));
653 if (is_device_path(what
)) {
654 r
= generator_write_initrd_root_device_deps(arg_dest
, what
);
659 return add_mount(arg_dest
,
665 is_device_path(what
) ? 1 : 0, /* passno */
666 false, /* noauto off */
667 false, /* nofail off */
668 false, /* automount off */
669 SPECIAL_INITRD_ROOT_FS_TARGET
,
673 static int add_sysroot_usr_mount(void) {
674 _cleanup_free_
char *what
= NULL
;
677 if (!arg_usr_what
&& !arg_usr_fstype
&& !arg_usr_options
)
680 if (arg_root_what
&& !arg_usr_what
) {
681 /* Copy over the root device, in case the /usr mount just differs in a mount option (consider btrfs subvolumes) */
682 arg_usr_what
= strdup(arg_root_what
);
687 if (arg_root_fstype
&& !arg_usr_fstype
) {
688 arg_usr_fstype
= strdup(arg_root_fstype
);
693 if (arg_root_options
&& !arg_usr_options
) {
694 arg_usr_options
= strdup(arg_root_options
);
695 if (!arg_usr_options
)
702 what
= fstab_node_to_udev_node(arg_usr_what
);
706 if (!arg_usr_options
)
707 opts
= arg_root_rw
> 0 ? "rw" : "ro";
708 else if (!fstab_test_option(arg_usr_options
, "ro\0" "rw\0"))
709 opts
= strjoina(arg_usr_options
, ",", arg_root_rw
> 0 ? "rw" : "ro");
711 opts
= arg_usr_options
;
713 log_debug("Found entry what=%s where=/sysroot/usr type=%s", what
, strna(arg_usr_fstype
));
714 return add_mount(arg_dest
,
720 is_device_path(what
) ? 1 : 0, /* passno */
721 false, /* noauto off */
722 false, /* nofail off */
723 false, /* automount off */
724 SPECIAL_INITRD_FS_TARGET
,
728 static int add_volatile_root(void) {
729 const char *from
, *to
;
731 if (arg_volatile_mode
!= VOLATILE_YES
)
734 /* Let's add in systemd-remount-volatile.service which will remount the root device to tmpfs if this is
735 * requested, leaving only /usr from the root mount inside. */
737 from
= strjoina(SYSTEM_DATA_UNIT_PATH
"/systemd-volatile-root.service");
738 to
= strjoina(arg_dest
, "/" SPECIAL_INITRD_ROOT_FS_TARGET
, ".requires/systemd-volatile-root.service");
740 (void) mkdir_parents(to
, 0755);
742 if (symlink(from
, to
) < 0)
743 return log_error_errno(errno
, "Failed to hook in volatile remount service: %m");
748 static int add_volatile_var(void) {
750 if (arg_volatile_mode
!= VOLATILE_STATE
)
753 /* If requested, mount /var as tmpfs, but do so only if there's nothing else defined for this. */
755 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")) {
803 if (proc_cmdline_value_missing(key
, value
))
806 o
= arg_root_options
?
807 strjoin(arg_root_options
, ",", value
) :
812 free(arg_root_options
);
813 arg_root_options
= o
;
814 } else if (streq(key
, "roothash")) {
816 if (proc_cmdline_value_missing(key
, value
))
819 if (free_and_strdup(&arg_root_hash
, value
) < 0)
822 } else if (streq(key
, "mount.usr")) {
824 if (proc_cmdline_value_missing(key
, value
))
827 if (free_and_strdup(&arg_usr_what
, value
) < 0)
830 } else if (streq(key
, "mount.usrfstype")) {
832 if (proc_cmdline_value_missing(key
, value
))
835 if (free_and_strdup(&arg_usr_fstype
, value
) < 0)
838 } else if (streq(key
, "mount.usrflags")) {
841 if (proc_cmdline_value_missing(key
, value
))
844 o
= arg_usr_options
?
845 strjoin(arg_usr_options
, ",", value
) :
850 free(arg_usr_options
);
853 } else if (streq(key
, "rw") && !value
)
855 else if (streq(key
, "ro") && !value
)
857 else if (streq(key
, "systemd.volatile")) {
861 m
= volatile_mode_from_string(value
);
863 log_warning("Failed to parse systemd.volatile= argument: %s", value
);
865 arg_volatile_mode
= m
;
867 arg_volatile_mode
= VOLATILE_YES
;
873 static int determine_root(void) {
874 /* If we have a root hash but no root device then Verity is used, and we use the "root" DM device as root. */
882 arg_root_what
= strdup("/dev/mapper/root");
886 log_info("Using verity root device %s.", arg_root_what
);
891 int main(int argc
, char *argv
[]) {
894 if (argc
> 1 && argc
!= 4) {
895 log_error("This program takes three or no arguments.");
902 arg_dest_late
= argv
[3];
904 log_set_target(LOG_TARGET_SAFE
);
905 log_parse_environment();
910 r
= proc_cmdline_parse(parse_proc_cmdline_item
, NULL
, 0);
912 log_warning_errno(r
, "Failed to parse kernel command line, ignoring: %m");
914 (void) determine_root();
916 /* Always honour root= and usr= in the kernel command line if we are in an initrd */
920 r
= add_sysroot_mount();
922 k
= add_sysroot_usr_mount();
926 k
= add_volatile_root();
930 r
= add_volatile_var();
932 /* Honour /etc/fstab only when that's enabled */
933 if (arg_fstab_enabled
) {
936 log_debug("Parsing /etc/fstab");
938 /* Parse the local /etc/fstab, possibly from the initrd */
939 k
= parse_fstab(false);
943 /* If running in the initrd also parse the /etc/fstab from the host */
945 log_debug("Parsing /sysroot/etc/fstab");
947 k
= parse_fstab(true);
954 free(arg_root_fstype
);
955 free(arg_root_options
);
959 free(arg_usr_fstype
);
960 free(arg_usr_options
);
962 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;