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 "stat-util.h"
42 #include "string-util.h"
44 #include "unit-name.h"
47 #include "volatile-util.h"
49 static const char *arg_dest
= "/tmp";
50 static const char *arg_dest_late
= "/tmp";
51 static bool arg_fstab_enabled
= true;
52 static char *arg_root_what
= NULL
;
53 static char *arg_root_fstype
= NULL
;
54 static char *arg_root_options
= NULL
;
55 static char *arg_root_hash
= NULL
;
56 static int arg_root_rw
= -1;
57 static char *arg_usr_what
= NULL
;
58 static char *arg_usr_fstype
= NULL
;
59 static char *arg_usr_options
= NULL
;
60 static VolatileMode arg_volatile_mode
= _VOLATILE_MODE_INVALID
;
62 static int write_options(FILE *f
, const char *options
) {
63 _cleanup_free_
char *o
= NULL
;
68 if (streq(options
, "defaults"))
71 o
= strreplace(options
, "%", "%%");
75 fprintf(f
, "Options=%s\n", o
);
79 static int write_what(FILE *f
, const char *what
) {
80 _cleanup_free_
char *w
= NULL
;
82 w
= strreplace(what
, "%", "%%");
86 fprintf(f
, "What=%s\n", w
);
96 _cleanup_free_
char *name
= NULL
, *unit
= NULL
;
97 _cleanup_fclose_
FILE *f
= NULL
;
103 if (access("/proc/swaps", F_OK
) < 0) {
104 log_info("Swap not supported, ignoring fstab swap entry for %s.", what
);
108 if (detect_container() > 0) {
109 log_info("Running in a container, ignoring fstab swap entry for %s.", what
);
113 r
= unit_name_from_path(what
, ".swap", &name
);
115 return log_error_errno(r
, "Failed to generate unit name: %m");
117 unit
= strjoin(arg_dest
, "/", name
);
121 f
= fopen(unit
, "wxe");
123 return log_error_errno(errno
,
125 "Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?" :
126 "Failed to create unit file %s: %m",
129 fputs_unlocked("# Automatically generated by systemd-fstab-generator\n\n"
131 "SourcePath=/etc/fstab\n"
132 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n"
135 r
= write_what(f
, what
);
139 r
= write_options(f
, me
->mnt_opts
);
143 r
= fflush_and_check(f
);
145 return log_error_errno(r
, "Failed to write unit file %s: %m", unit
);
147 /* use what as where, to have a nicer error message */
148 r
= generator_write_timeouts(arg_dest
, what
, what
, me
->mnt_opts
, NULL
);
153 r
= generator_add_symlink(arg_dest
, SPECIAL_SWAP_TARGET
,
154 nofail
? "wants" : "requires", name
);
162 static bool mount_is_network(struct mntent
*me
) {
165 return fstab_test_option(me
->mnt_opts
, "_netdev\0") ||
166 fstype_is_network(me
->mnt_type
);
169 static bool mount_in_initrd(struct mntent
*me
) {
172 return fstab_test_option(me
->mnt_opts
, "x-initrd.mount\0") ||
173 streq(me
->mnt_dir
, "/usr");
176 static int write_timeout(FILE *f
, const char *where
, const char *opts
,
177 const char *filter
, const char *variable
) {
178 _cleanup_free_
char *timeout
= NULL
;
179 char timespan
[FORMAT_TIMESPAN_MAX
];
183 r
= fstab_filter_options(opts
, filter
, NULL
, &timeout
, NULL
);
185 return log_warning_errno(r
, "Failed to parse options: %m");
189 r
= parse_sec_fix_0(timeout
, &u
);
191 log_warning("Failed to parse timeout for %s, ignoring: %s", where
, timeout
);
195 fprintf(f
, "%s=%s\n", variable
, format_timespan(timespan
, sizeof(timespan
), u
, 0));
200 static int write_idle_timeout(FILE *f
, const char *where
, const char *opts
) {
201 return write_timeout(f
, where
, opts
,
202 "x-systemd.idle-timeout\0", "TimeoutIdleSec");
205 static int write_mount_timeout(FILE *f
, const char *where
, const char *opts
) {
206 return write_timeout(f
, where
, opts
,
207 "x-systemd.mount-timeout\0", "TimeoutSec");
210 static int write_dependency(FILE *f
, const char *opts
,
211 const char *filter
, const char *format
) {
212 _cleanup_strv_free_
char **names
= NULL
, **units
= NULL
;
213 _cleanup_free_
char *res
= NULL
;
220 r
= fstab_extract_values(opts
, filter
, &names
);
222 return log_warning_errno(r
, "Failed to parse options: %m");
226 STRV_FOREACH(s
, names
) {
229 r
= unit_name_mangle_with_suffix(*s
, UNIT_NAME_NOGLOB
, ".mount", &x
);
231 return log_error_errno(r
, "Failed to generate unit name: %m");
232 r
= strv_consume(&units
, x
);
238 res
= strv_join(units
, " ");
241 #pragma GCC diagnostic push
242 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
243 fprintf(f
, format
, res
);
244 #pragma GCC diagnostic pop
250 static int write_after(FILE *f
, const char *opts
) {
251 return write_dependency(f
, opts
, "x-systemd.after", "After=%1$s\n");
254 static int write_requires_after(FILE *f
, const char *opts
) {
255 return write_dependency(f
, opts
,
256 "x-systemd.requires", "After=%1$s\nRequires=%1$s\n");
259 static int write_before(FILE *f
, const char *opts
) {
260 return write_dependency(f
, opts
,
261 "x-systemd.before", "Before=%1$s\n");
264 static int write_requires_mounts_for(FILE *f
, const char *opts
) {
265 _cleanup_strv_free_
char **paths
= NULL
;
266 _cleanup_free_
char *res
= NULL
;
272 r
= fstab_extract_values(opts
, "x-systemd.requires-mounts-for", &paths
);
274 return log_warning_errno(r
, "Failed to parse options: %m");
278 res
= strv_join(paths
, " ");
282 fprintf(f
, "RequiresMountsFor=%s\n", res
);
287 static int add_mount(
291 const char *original_where
,
299 const char *source
) {
302 *name
= NULL
, *unit
= NULL
,
303 *automount_name
= NULL
, *automount_unit
= NULL
,
305 _cleanup_fclose_
FILE *f
= NULL
;
314 if (streq_ptr(fstype
, "autofs"))
317 if (!is_path(where
)) {
318 log_warning("Mount point %s is not a valid path, ignoring.", where
);
322 if (mount_point_is_api(where
) ||
323 mount_point_ignore(where
))
326 if (path_equal(where
, "/")) {
328 log_warning("Ignoring \"noauto\" for root device");
330 log_warning("Ignoring \"nofail\" for root device");
332 log_warning("Ignoring automount option for root device");
334 noauto
= nofail
= automount
= false;
337 r
= unit_name_from_path(where
, ".mount", &name
);
339 return log_error_errno(r
, "Failed to generate unit name: %m");
341 unit
= strjoin(dest
, "/", name
);
345 f
= fopen(unit
, "wxe");
347 return log_error_errno(errno
,
349 "Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?" :
350 "Failed to create unit file %s: %m",
354 "# Automatically generated by systemd-fstab-generator\n\n"
357 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
360 if (STRPTR_IN_SET(fstype
, "nfs", "nfs4") && !automount
&&
361 fstab_test_yes_no_option(opts
, "bg\0" "fg\0")) {
362 /* The default retry timeout that mount.nfs uses for 'bg' mounts
363 * is 10000 minutes, where as it uses 2 minutes for 'fg' mounts.
364 * As we are making 'bg' mounts look like an 'fg' mount to
365 * mount.nfs (so systemd can manage the job-control aspects of 'bg'),
366 * we need to explicitly preserve that default, and also ensure
367 * the systemd mount-timeout doesn't interfere.
368 * By placing these options first, they can be over-ridden by
369 * settings in /etc/fstab. */
370 opts
= strjoina("x-systemd.mount-timeout=infinity,retry=10000,", opts
, ",fg");
374 if (!nofail
&& !automount
)
375 fprintf(f
, "Before=%s\n", post
);
377 if (!automount
&& opts
) {
378 r
= write_after(f
, opts
);
381 r
= write_requires_after(f
, opts
);
384 r
= write_before(f
, opts
);
387 r
= write_requires_mounts_for(f
, opts
);
393 r
= generator_write_fsck_deps(f
, dest
, what
, where
, fstype
);
398 fprintf(f
, "\n[Mount]\n");
400 fprintf(f
, "# Canonicalized from %s\n", original_where
);
401 fprintf(f
, "Where=%s\n", where
);
403 r
= write_what(f
, what
);
407 if (!isempty(fstype
) && !streq(fstype
, "auto"))
408 fprintf(f
, "Type=%s\n", fstype
);
410 r
= generator_write_timeouts(dest
, what
, where
, opts
, &filtered
);
414 r
= generator_write_device_deps(dest
, what
, where
, opts
);
418 r
= write_mount_timeout(f
, where
, opts
);
422 r
= write_options(f
, filtered
);
426 r
= fflush_and_check(f
);
428 return log_error_errno(r
, "Failed to write unit file %s: %m", unit
);
430 if (!noauto
&& !automount
) {
431 r
= generator_add_symlink(dest
, post
,
432 nofail
? "wants" : "requires", name
);
438 r
= unit_name_from_path(where
, ".automount", &automount_name
);
440 return log_error_errno(r
, "Failed to generate unit name: %m");
442 automount_unit
= strjoin(dest
, "/", automount_name
);
447 f
= fopen(automount_unit
, "wxe");
449 return log_error_errno(errno
, "Failed to create unit file %s: %m", automount_unit
);
452 "# Automatically generated by systemd-fstab-generator\n\n"
455 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
458 fprintf(f
, "Before=%s\n", post
);
461 r
= write_after(f
, opts
);
464 r
= write_requires_after(f
, opts
);
467 r
= write_before(f
, opts
);
470 r
= write_requires_mounts_for(f
, opts
);
481 r
= write_idle_timeout(f
, where
, opts
);
485 r
= fflush_and_check(f
);
487 return log_error_errno(r
, "Failed to write unit file %s: %m", automount_unit
);
489 r
= generator_add_symlink(dest
, post
,
490 nofail
? "wants" : "requires", automount_name
);
498 static int parse_fstab(bool initrd
) {
499 _cleanup_endmntent_
FILE *f
= NULL
;
500 const char *fstab_path
;
504 fstab_path
= initrd
? "/sysroot/etc/fstab" : "/etc/fstab";
505 f
= setmntent(fstab_path
, "re");
510 return log_error_errno(errno
, "Failed to open %s: %m", fstab_path
);
513 while ((me
= getmntent(f
))) {
514 _cleanup_free_
char *where
= NULL
, *what
= NULL
, *canonical_where
= NULL
;
518 if (initrd
&& !mount_in_initrd(me
))
521 what
= fstab_node_to_udev_node(me
->mnt_fsname
);
525 if (is_device_path(what
) && path_is_read_only_fs("sys") > 0) {
526 log_info("Running in a container, ignoring fstab device entry for %s.", what
);
530 where
= strdup(me
->mnt_dir
);
534 if (is_path(where
)) {
535 path_kill_slashes(where
);
536 /* Follow symlinks here; see 5261ba901845c084de5a8fd06500ed09bfb0bd80 which makes sense for
537 * mount units, but causes problems since it historically worked to have symlinks in e.g.
538 * /etc/fstab. So we canonicalize here. Note that we use CHASE_NONEXISTENT to handle the case
539 * where a symlink refers to another mount target; this works assuming the sub-mountpoint
540 * target is the final directory.
542 r
= chase_symlinks(where
, initrd
? "/sysroot" : NULL
,
543 CHASE_PREFIX_ROOT
| CHASE_NONEXISTENT
,
546 /* In this case for now we continue on as if it wasn't a symlink */
547 log_warning_errno(r
, "Failed to read symlink target for %s: %m", where
);
549 if (streq(canonical_where
, where
))
550 canonical_where
= mfree(canonical_where
);
552 log_debug("Canonicalized what=%s where=%s to %s",
553 what
, where
, canonical_where
);
557 noauto
= fstab_test_yes_no_option(me
->mnt_opts
, "noauto\0" "auto\0");
558 nofail
= fstab_test_yes_no_option(me
->mnt_opts
, "nofail\0" "fail\0");
559 log_debug("Found entry what=%s where=%s type=%s nofail=%s noauto=%s",
560 what
, where
, me
->mnt_type
,
561 yes_no(noauto
), yes_no(nofail
));
563 if (streq(me
->mnt_type
, "swap"))
564 k
= add_swap(what
, me
, noauto
, nofail
);
569 automount
= fstab_test_option(me
->mnt_opts
,
570 "comment=systemd.automount\0"
571 "x-systemd.automount\0");
573 post
= SPECIAL_INITRD_FS_TARGET
;
574 else if (mount_is_network(me
))
575 post
= SPECIAL_REMOTE_FS_TARGET
;
577 post
= SPECIAL_LOCAL_FS_TARGET
;
579 k
= add_mount(arg_dest
,
581 canonical_where
?: where
,
582 canonical_where
? where
: NULL
,
600 static int add_sysroot_mount(void) {
601 _cleanup_free_
char *what
= NULL
;
605 if (isempty(arg_root_what
)) {
606 log_debug("Could not find a root= entry on the kernel command line.");
610 if (streq(arg_root_what
, "gpt-auto")) {
611 /* This is handled by the gpt-auto generator */
612 log_debug("Skipping root directory handling, as gpt-auto was requested.");
616 if (path_equal(arg_root_what
, "/dev/nfs")) {
617 /* This is handled by the kernel or the initrd */
618 log_debug("Skipping root directory handling, as /dev/nfs was requested.");
622 what
= fstab_node_to_udev_node(arg_root_what
);
626 if (!arg_root_options
)
627 opts
= arg_root_rw
> 0 ? "rw" : "ro";
628 else if (arg_root_rw
>= 0 ||
629 !fstab_test_option(arg_root_options
, "ro\0" "rw\0"))
630 opts
= strjoina(arg_root_options
, ",", arg_root_rw
> 0 ? "rw" : "ro");
632 opts
= arg_root_options
;
634 log_debug("Found entry what=%s where=/sysroot type=%s", what
, strna(arg_root_fstype
));
636 if (is_device_path(what
)) {
637 r
= generator_write_initrd_root_device_deps(arg_dest
, what
);
642 return add_mount(arg_dest
,
648 is_device_path(what
) ? 1 : 0, /* passno */
649 false, /* noauto off */
650 false, /* nofail off */
651 false, /* automount off */
652 SPECIAL_INITRD_ROOT_FS_TARGET
,
656 static int add_sysroot_usr_mount(void) {
657 _cleanup_free_
char *what
= NULL
;
660 if (!arg_usr_what
&& !arg_usr_fstype
&& !arg_usr_options
)
663 if (arg_root_what
&& !arg_usr_what
) {
664 /* Copy over the root device, in case the /usr mount just differs in a mount option (consider btrfs subvolumes) */
665 arg_usr_what
= strdup(arg_root_what
);
670 if (arg_root_fstype
&& !arg_usr_fstype
) {
671 arg_usr_fstype
= strdup(arg_root_fstype
);
676 if (arg_root_options
&& !arg_usr_options
) {
677 arg_usr_options
= strdup(arg_root_options
);
678 if (!arg_usr_options
)
685 what
= fstab_node_to_udev_node(arg_usr_what
);
689 if (!arg_usr_options
)
690 opts
= arg_root_rw
> 0 ? "rw" : "ro";
691 else if (!fstab_test_option(arg_usr_options
, "ro\0" "rw\0"))
692 opts
= strjoina(arg_usr_options
, ",", arg_root_rw
> 0 ? "rw" : "ro");
694 opts
= arg_usr_options
;
696 log_debug("Found entry what=%s where=/sysroot/usr type=%s", what
, strna(arg_usr_fstype
));
697 return add_mount(arg_dest
,
703 is_device_path(what
) ? 1 : 0, /* passno */
704 false, /* noauto off */
705 false, /* nofail off */
706 false, /* automount off */
707 SPECIAL_INITRD_FS_TARGET
,
711 static int add_volatile_root(void) {
712 const char *from
, *to
;
714 if (arg_volatile_mode
!= VOLATILE_YES
)
717 /* Let's add in systemd-remount-volatile.service which will remount the root device to tmpfs if this is
718 * requested, leaving only /usr from the root mount inside. */
720 from
= strjoina(SYSTEM_DATA_UNIT_PATH
"/systemd-volatile-root.service");
721 to
= strjoina(arg_dest
, "/" SPECIAL_INITRD_ROOT_FS_TARGET
, ".requires/systemd-volatile-root.service");
723 (void) mkdir_parents(to
, 0755);
725 if (symlink(from
, to
) < 0)
726 return log_error_errno(errno
, "Failed to hook in volatile remount service: %m");
731 static int add_volatile_var(void) {
733 if (arg_volatile_mode
!= VOLATILE_STATE
)
736 /* If requested, mount /var as tmpfs, but do so only if there's nothing else defined for this. */
738 return add_mount(arg_dest_late
,
748 SPECIAL_LOCAL_FS_TARGET
,
752 static int parse_proc_cmdline_item(const char *key
, const char *value
, void *data
) {
755 /* root=, usr=, usrfstype= and roofstype= may occur more than once, the last
756 * instance should take precedence. In the case of multiple rootflags=
757 * or usrflags= the arguments should be concatenated */
759 if (STR_IN_SET(key
, "fstab", "rd.fstab")) {
761 r
= value
? parse_boolean(value
) : 1;
763 log_warning("Failed to parse fstab switch %s. Ignoring.", value
);
765 arg_fstab_enabled
= r
;
767 } else if (streq(key
, "root")) {
769 if (proc_cmdline_value_missing(key
, value
))
772 if (free_and_strdup(&arg_root_what
, value
) < 0)
775 } else if (streq(key
, "rootfstype")) {
777 if (proc_cmdline_value_missing(key
, value
))
780 if (free_and_strdup(&arg_root_fstype
, value
) < 0)
783 } else if (streq(key
, "rootflags")) {
786 if (proc_cmdline_value_missing(key
, value
))
789 o
= arg_root_options
?
790 strjoin(arg_root_options
, ",", value
) :
795 free(arg_root_options
);
796 arg_root_options
= o
;
797 } else if (streq(key
, "roothash")) {
799 if (proc_cmdline_value_missing(key
, value
))
802 if (free_and_strdup(&arg_root_hash
, value
) < 0)
805 } else if (streq(key
, "mount.usr")) {
807 if (proc_cmdline_value_missing(key
, value
))
810 if (free_and_strdup(&arg_usr_what
, value
) < 0)
813 } else if (streq(key
, "mount.usrfstype")) {
815 if (proc_cmdline_value_missing(key
, value
))
818 if (free_and_strdup(&arg_usr_fstype
, value
) < 0)
821 } else if (streq(key
, "mount.usrflags")) {
824 if (proc_cmdline_value_missing(key
, value
))
827 o
= arg_usr_options
?
828 strjoin(arg_usr_options
, ",", value
) :
833 free(arg_usr_options
);
836 } else if (streq(key
, "rw") && !value
)
838 else if (streq(key
, "ro") && !value
)
840 else if (streq(key
, "systemd.volatile")) {
844 m
= volatile_mode_from_string(value
);
846 log_warning("Failed to parse systemd.volatile= argument: %s", value
);
848 arg_volatile_mode
= m
;
850 arg_volatile_mode
= VOLATILE_YES
;
856 static int determine_root(void) {
857 /* If we have a root hash but no root device then Verity is used, and we use the "root" DM device as root. */
865 arg_root_what
= strdup("/dev/mapper/root");
869 log_info("Using verity root device %s.", arg_root_what
);
874 int main(int argc
, char *argv
[]) {
877 if (argc
> 1 && argc
!= 4) {
878 log_error("This program takes three or no arguments.");
885 arg_dest_late
= argv
[3];
887 log_set_target(LOG_TARGET_SAFE
);
888 log_parse_environment();
893 r
= proc_cmdline_parse(parse_proc_cmdline_item
, NULL
, 0);
895 log_warning_errno(r
, "Failed to parse kernel command line, ignoring: %m");
897 (void) determine_root();
899 /* Always honour root= and usr= in the kernel command line if we are in an initrd */
903 r
= add_sysroot_mount();
905 k
= add_sysroot_usr_mount();
909 k
= add_volatile_root();
913 r
= add_volatile_var();
915 /* Honour /etc/fstab only when that's enabled */
916 if (arg_fstab_enabled
) {
919 log_debug("Parsing /etc/fstab");
921 /* Parse the local /etc/fstab, possibly from the initrd */
922 k
= parse_fstab(false);
926 /* If running in the initrd also parse the /etc/fstab from the host */
928 log_debug("Parsing /sysroot/etc/fstab");
930 k
= parse_fstab(true);
937 free(arg_root_fstype
);
938 free(arg_root_options
);
942 free(arg_usr_fstype
);
943 free(arg_usr_options
);
945 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;