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
{
56 static const char *arg_dest
= "/tmp";
57 static const char *arg_dest_late
= "/tmp";
58 static bool arg_fstab_enabled
= true;
59 static char *arg_root_what
= NULL
;
60 static char *arg_root_fstype
= NULL
;
61 static char *arg_root_options
= NULL
;
62 static char *arg_root_hash
= NULL
;
63 static int arg_root_rw
= -1;
64 static char *arg_usr_what
= NULL
;
65 static char *arg_usr_fstype
= NULL
;
66 static char *arg_usr_options
= NULL
;
67 static VolatileMode arg_volatile_mode
= _VOLATILE_MODE_INVALID
;
69 static int write_options(FILE *f
, const char *options
) {
70 _cleanup_free_
char *o
= NULL
;
75 if (streq(options
, "defaults"))
78 o
= specifier_escape(options
);
82 fprintf(f
, "Options=%s\n", o
);
86 static int write_what(FILE *f
, const char *what
) {
87 _cleanup_free_
char *w
= NULL
;
89 w
= specifier_escape(what
);
93 fprintf(f
, "What=%s\n", w
);
100 MountpointFlags flags
) {
102 _cleanup_free_
char *name
= NULL
, *unit
= NULL
;
103 _cleanup_fclose_
FILE *f
= NULL
;
109 if (access("/proc/swaps", F_OK
) < 0) {
110 log_info("Swap not supported, ignoring fstab swap entry for %s.", what
);
114 if (detect_container() > 0) {
115 log_info("Running in a container, ignoring fstab swap entry for %s.", what
);
119 r
= unit_name_from_path(what
, ".swap", &name
);
121 return log_error_errno(r
, "Failed to generate unit name: %m");
123 unit
= strjoin(arg_dest
, "/", name
);
127 f
= fopen(unit
, "wxe");
129 return log_error_errno(errno
,
131 "Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?" :
132 "Failed to create unit file %s: %m",
135 fputs_unlocked("# Automatically generated by systemd-fstab-generator\n\n"
137 "SourcePath=/etc/fstab\n"
138 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n"
141 r
= write_what(f
, what
);
145 r
= write_options(f
, me
->mnt_opts
);
149 r
= fflush_and_check(f
);
151 return log_error_errno(r
, "Failed to write unit file %s: %m", unit
);
153 /* use what as where, to have a nicer error message */
154 r
= generator_write_timeouts(arg_dest
, what
, what
, me
->mnt_opts
, NULL
);
158 if (!(flags
& NOAUTO
)) {
159 r
= generator_add_symlink(arg_dest
, SPECIAL_SWAP_TARGET
,
160 (flags
& NOFAIL
) ? "wants" : "requires", name
);
168 static bool mount_is_network(struct mntent
*me
) {
171 return fstab_test_option(me
->mnt_opts
, "_netdev\0") ||
172 fstype_is_network(me
->mnt_type
);
175 static bool mount_in_initrd(struct mntent
*me
) {
178 return fstab_test_option(me
->mnt_opts
, "x-initrd.mount\0") ||
179 streq(me
->mnt_dir
, "/usr");
182 static int write_timeout(FILE *f
, const char *where
, const char *opts
,
183 const char *filter
, const char *variable
) {
184 _cleanup_free_
char *timeout
= NULL
;
185 char timespan
[FORMAT_TIMESPAN_MAX
];
189 r
= fstab_filter_options(opts
, filter
, NULL
, &timeout
, NULL
);
191 return log_warning_errno(r
, "Failed to parse options: %m");
195 r
= parse_sec_fix_0(timeout
, &u
);
197 log_warning("Failed to parse timeout for %s, ignoring: %s", where
, timeout
);
201 fprintf(f
, "%s=%s\n", variable
, format_timespan(timespan
, sizeof(timespan
), u
, 0));
206 static int write_idle_timeout(FILE *f
, const char *where
, const char *opts
) {
207 return write_timeout(f
, where
, opts
,
208 "x-systemd.idle-timeout\0", "TimeoutIdleSec");
211 static int write_mount_timeout(FILE *f
, const char *where
, const char *opts
) {
212 return write_timeout(f
, where
, opts
,
213 "x-systemd.mount-timeout\0", "TimeoutSec");
216 static int write_dependency(FILE *f
, const char *opts
,
217 const char *filter
, const char *format
) {
218 _cleanup_strv_free_
char **names
= NULL
, **units
= NULL
;
219 _cleanup_free_
char *res
= NULL
;
226 r
= fstab_extract_values(opts
, filter
, &names
);
228 return log_warning_errno(r
, "Failed to parse options: %m");
232 STRV_FOREACH(s
, names
) {
235 r
= unit_name_mangle_with_suffix(*s
, UNIT_NAME_NOGLOB
, ".mount", &x
);
237 return log_error_errno(r
, "Failed to generate unit name: %m");
238 r
= strv_consume(&units
, x
);
244 res
= strv_join(units
, " ");
247 #pragma GCC diagnostic push
248 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
249 fprintf(f
, format
, res
);
250 #pragma GCC diagnostic pop
256 static int write_after(FILE *f
, const char *opts
) {
257 return write_dependency(f
, opts
, "x-systemd.after", "After=%1$s\n");
260 static int write_requires_after(FILE *f
, const char *opts
) {
261 return write_dependency(f
, opts
,
262 "x-systemd.requires", "After=%1$s\nRequires=%1$s\n");
265 static int write_before(FILE *f
, const char *opts
) {
266 return write_dependency(f
, opts
,
267 "x-systemd.before", "Before=%1$s\n");
270 static int write_requires_mounts_for(FILE *f
, const char *opts
) {
271 _cleanup_strv_free_
char **paths
= NULL
, **paths_escaped
= NULL
;
272 _cleanup_free_
char *res
= NULL
;
278 r
= fstab_extract_values(opts
, "x-systemd.requires-mounts-for", &paths
);
280 return log_warning_errno(r
, "Failed to parse options: %m");
284 r
= specifier_escape_strv(paths
, &paths_escaped
);
286 return log_error_errno(r
, "Failed to escape paths: %m");
288 res
= strv_join(paths_escaped
, " ");
292 fprintf(f
, "RequiresMountsFor=%s\n", res
);
297 static int add_mount(
301 const char *original_where
,
305 MountpointFlags flags
,
307 const char *source
) {
310 *name
= NULL
, *unit
= NULL
,
311 *automount_name
= NULL
, *automount_unit
= NULL
,
313 *where_escaped
= NULL
;
314 _cleanup_fclose_
FILE *f
= NULL
;
323 if (streq_ptr(fstype
, "autofs"))
326 if (!is_path(where
)) {
327 log_warning("Mount point %s is not a valid path, ignoring.", where
);
331 if (mount_point_is_api(where
) ||
332 mount_point_ignore(where
))
335 if (path_equal(where
, "/")) {
337 log_warning("Ignoring \"noauto\" for root device");
339 log_warning("Ignoring \"nofail\" for root device");
340 if (flags
& AUTOMOUNT
)
341 log_warning("Ignoring automount option for root device");
343 SET_FLAG(flags
, NOAUTO
| NOFAIL
| AUTOMOUNT
, false);
346 r
= unit_name_from_path(where
, ".mount", &name
);
348 return log_error_errno(r
, "Failed to generate unit name: %m");
350 unit
= strjoin(dest
, "/", name
);
354 f
= fopen(unit
, "wxe");
356 return log_error_errno(errno
,
358 "Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?" :
359 "Failed to create unit file %s: %m",
363 "# Automatically generated by systemd-fstab-generator\n\n"
366 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
369 if (STRPTR_IN_SET(fstype
, "nfs", "nfs4") && !(flags
& AUTOMOUNT
) &&
370 fstab_test_yes_no_option(opts
, "bg\0" "fg\0")) {
371 /* The default retry timeout that mount.nfs uses for 'bg' mounts
372 * is 10000 minutes, where as it uses 2 minutes for 'fg' mounts.
373 * As we are making 'bg' mounts look like an 'fg' mount to
374 * mount.nfs (so systemd can manage the job-control aspects of 'bg'),
375 * we need to explicitly preserve that default, and also ensure
376 * the systemd mount-timeout doesn't interfere.
377 * By placing these options first, they can be over-ridden by
378 * settings in /etc/fstab. */
379 opts
= strjoina("x-systemd.mount-timeout=infinity,retry=10000,", opts
, ",fg");
380 SET_FLAG(flags
, NOFAIL
, true);
383 if (!(flags
& NOFAIL
) && !(flags
& AUTOMOUNT
))
384 fprintf(f
, "Before=%s\n", post
);
386 if (!(flags
& AUTOMOUNT
) && opts
) {
387 r
= write_after(f
, opts
);
390 r
= write_requires_after(f
, opts
);
393 r
= write_before(f
, opts
);
396 r
= write_requires_mounts_for(f
, opts
);
402 r
= generator_write_fsck_deps(f
, dest
, what
, where
, fstype
);
407 fprintf(f
, "\n[Mount]\n");
409 fprintf(f
, "# Canonicalized from %s\n", original_where
);
411 where_escaped
= specifier_escape(where
);
414 fprintf(f
, "Where=%s\n", where_escaped
);
416 r
= write_what(f
, what
);
420 if (!isempty(fstype
) && !streq(fstype
, "auto")) {
421 _cleanup_free_
char *t
;
423 t
= specifier_escape(fstype
);
427 fprintf(f
, "Type=%s\n", t
);
430 r
= generator_write_timeouts(dest
, what
, where
, opts
, &filtered
);
434 r
= generator_write_device_deps(dest
, what
, where
, opts
);
438 r
= write_mount_timeout(f
, where
, opts
);
442 r
= write_options(f
, filtered
);
446 r
= fflush_and_check(f
);
448 return log_error_errno(r
, "Failed to write unit file %s: %m", unit
);
450 if (!(flags
& NOAUTO
) && !(flags
& AUTOMOUNT
)) {
451 r
= generator_add_symlink(dest
, post
,
452 (flags
& NOFAIL
) ? "wants" : "requires", name
);
457 if (flags
& AUTOMOUNT
) {
458 r
= unit_name_from_path(where
, ".automount", &automount_name
);
460 return log_error_errno(r
, "Failed to generate unit name: %m");
462 automount_unit
= strjoin(dest
, "/", automount_name
);
467 f
= fopen(automount_unit
, "wxe");
469 return log_error_errno(errno
, "Failed to create unit file %s: %m", automount_unit
);
472 "# Automatically generated by systemd-fstab-generator\n\n"
475 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
478 fprintf(f
, "Before=%s\n", post
);
481 r
= write_after(f
, opts
);
484 r
= write_requires_after(f
, opts
);
487 r
= write_before(f
, opts
);
490 r
= write_requires_mounts_for(f
, opts
);
501 r
= write_idle_timeout(f
, where
, opts
);
505 r
= fflush_and_check(f
);
507 return log_error_errno(r
, "Failed to write unit file %s: %m", automount_unit
);
509 r
= generator_add_symlink(dest
, post
,
510 (flags
& NOFAIL
) ? "wants" : "requires", automount_name
);
518 static int parse_fstab(bool initrd
) {
519 _cleanup_endmntent_
FILE *f
= NULL
;
520 const char *fstab_path
;
524 fstab_path
= initrd
? "/sysroot/etc/fstab" : "/etc/fstab";
525 f
= setmntent(fstab_path
, "re");
530 return log_error_errno(errno
, "Failed to open %s: %m", fstab_path
);
533 while ((me
= getmntent(f
))) {
534 _cleanup_free_
char *where
= NULL
, *what
= NULL
, *canonical_where
= NULL
;
538 if (initrd
&& !mount_in_initrd(me
))
541 what
= fstab_node_to_udev_node(me
->mnt_fsname
);
545 if (is_device_path(what
) && path_is_read_only_fs("sys") > 0) {
546 log_info("Running in a container, ignoring fstab device entry for %s.", what
);
550 where
= strdup(me
->mnt_dir
);
554 if (is_path(where
)) {
555 path_kill_slashes(where
);
556 /* Follow symlinks here; see 5261ba901845c084de5a8fd06500ed09bfb0bd80 which makes sense for
557 * mount units, but causes problems since it historically worked to have symlinks in e.g.
558 * /etc/fstab. So we canonicalize here. Note that we use CHASE_NONEXISTENT to handle the case
559 * where a symlink refers to another mount target; this works assuming the sub-mountpoint
560 * target is the final directory.
562 r
= chase_symlinks(where
, initrd
? "/sysroot" : NULL
,
563 CHASE_PREFIX_ROOT
| CHASE_NONEXISTENT
,
566 /* In this case for now we continue on as if it wasn't a symlink */
567 log_warning_errno(r
, "Failed to read symlink target for %s: %m", where
);
569 if (streq(canonical_where
, where
))
570 canonical_where
= mfree(canonical_where
);
572 log_debug("Canonicalized what=%s where=%s to %s",
573 what
, where
, canonical_where
);
577 noauto
= fstab_test_yes_no_option(me
->mnt_opts
, "noauto\0" "auto\0");
578 nofail
= fstab_test_yes_no_option(me
->mnt_opts
, "nofail\0" "fail\0");
579 log_debug("Found entry what=%s where=%s type=%s nofail=%s noauto=%s",
580 what
, where
, me
->mnt_type
,
581 yes_no(noauto
), yes_no(nofail
));
583 if (streq(me
->mnt_type
, "swap"))
584 k
= add_swap(what
, me
,
585 noauto
*NOAUTO
| nofail
*NOFAIL
);
590 automount
= fstab_test_option(me
->mnt_opts
,
591 "comment=systemd.automount\0"
592 "x-systemd.automount\0");
594 post
= SPECIAL_INITRD_FS_TARGET
;
595 else if (mount_is_network(me
))
596 post
= SPECIAL_REMOTE_FS_TARGET
;
598 post
= SPECIAL_LOCAL_FS_TARGET
;
600 k
= add_mount(arg_dest
,
602 canonical_where
?: where
,
603 canonical_where
? where
: NULL
,
607 noauto
*NOAUTO
| nofail
*NOFAIL
| automount
*AUTOMOUNT
,
619 static int add_sysroot_mount(void) {
620 _cleanup_free_
char *what
= NULL
;
624 if (isempty(arg_root_what
)) {
625 log_debug("Could not find a root= entry on the kernel command line.");
629 if (streq(arg_root_what
, "gpt-auto")) {
630 /* This is handled by the gpt-auto generator */
631 log_debug("Skipping root directory handling, as gpt-auto was requested.");
635 if (path_equal(arg_root_what
, "/dev/nfs")) {
636 /* This is handled by the kernel or the initrd */
637 log_debug("Skipping root directory handling, as /dev/nfs was requested.");
641 what
= fstab_node_to_udev_node(arg_root_what
);
645 if (!arg_root_options
)
646 opts
= arg_root_rw
> 0 ? "rw" : "ro";
647 else if (arg_root_rw
>= 0 ||
648 !fstab_test_option(arg_root_options
, "ro\0" "rw\0"))
649 opts
= strjoina(arg_root_options
, ",", arg_root_rw
> 0 ? "rw" : "ro");
651 opts
= arg_root_options
;
653 log_debug("Found entry what=%s where=/sysroot type=%s", what
, strna(arg_root_fstype
));
655 if (is_device_path(what
)) {
656 r
= generator_write_initrd_root_device_deps(arg_dest
, what
);
661 return add_mount(arg_dest
,
667 is_device_path(what
) ? 1 : 0, /* passno */
668 0, /* noauto off, nofail off, 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 */
722 SPECIAL_INITRD_FS_TARGET
,
726 static int add_volatile_root(void) {
727 const char *from
, *to
;
729 if (arg_volatile_mode
!= VOLATILE_YES
)
732 /* Let's add in systemd-remount-volatile.service which will remount the root device to tmpfs if this is
733 * requested, leaving only /usr from the root mount inside. */
735 from
= strjoina(SYSTEM_DATA_UNIT_PATH
"/systemd-volatile-root.service");
736 to
= strjoina(arg_dest
, "/" SPECIAL_INITRD_ROOT_FS_TARGET
, ".requires/systemd-volatile-root.service");
738 (void) mkdir_parents(to
, 0755);
740 if (symlink(from
, to
) < 0)
741 return log_error_errno(errno
, "Failed to hook in volatile remount service: %m");
746 static int add_volatile_var(void) {
748 if (arg_volatile_mode
!= VOLATILE_STATE
)
751 /* If requested, mount /var as tmpfs, but do so only if there's nothing else defined for this. */
753 return add_mount(arg_dest_late
,
761 SPECIAL_LOCAL_FS_TARGET
,
765 static int parse_proc_cmdline_item(const char *key
, const char *value
, void *data
) {
768 /* root=, usr=, usrfstype= and roofstype= may occur more than once, the last
769 * instance should take precedence. In the case of multiple rootflags=
770 * or usrflags= the arguments should be concatenated */
772 if (STR_IN_SET(key
, "fstab", "rd.fstab")) {
774 r
= value
? parse_boolean(value
) : 1;
776 log_warning("Failed to parse fstab switch %s. Ignoring.", value
);
778 arg_fstab_enabled
= r
;
780 } else if (streq(key
, "root")) {
782 if (proc_cmdline_value_missing(key
, value
))
785 if (free_and_strdup(&arg_root_what
, value
) < 0)
788 } else if (streq(key
, "rootfstype")) {
790 if (proc_cmdline_value_missing(key
, value
))
793 if (free_and_strdup(&arg_root_fstype
, value
) < 0)
796 } else if (streq(key
, "rootflags")) {
799 if (proc_cmdline_value_missing(key
, value
))
802 o
= arg_root_options
?
803 strjoin(arg_root_options
, ",", value
) :
808 free(arg_root_options
);
809 arg_root_options
= o
;
810 } else if (streq(key
, "roothash")) {
812 if (proc_cmdline_value_missing(key
, value
))
815 if (free_and_strdup(&arg_root_hash
, value
) < 0)
818 } else if (streq(key
, "mount.usr")) {
820 if (proc_cmdline_value_missing(key
, value
))
823 if (free_and_strdup(&arg_usr_what
, value
) < 0)
826 } else if (streq(key
, "mount.usrfstype")) {
828 if (proc_cmdline_value_missing(key
, value
))
831 if (free_and_strdup(&arg_usr_fstype
, value
) < 0)
834 } else if (streq(key
, "mount.usrflags")) {
837 if (proc_cmdline_value_missing(key
, value
))
840 o
= arg_usr_options
?
841 strjoin(arg_usr_options
, ",", value
) :
846 free(arg_usr_options
);
849 } else if (streq(key
, "rw") && !value
)
851 else if (streq(key
, "ro") && !value
)
853 else if (streq(key
, "systemd.volatile")) {
857 m
= volatile_mode_from_string(value
);
859 log_warning("Failed to parse systemd.volatile= argument: %s", value
);
861 arg_volatile_mode
= m
;
863 arg_volatile_mode
= VOLATILE_YES
;
869 static int determine_root(void) {
870 /* If we have a root hash but no root device then Verity is used, and we use the "root" DM device as root. */
878 arg_root_what
= strdup("/dev/mapper/root");
882 log_info("Using verity root device %s.", arg_root_what
);
887 int main(int argc
, char *argv
[]) {
890 if (argc
> 1 && argc
!= 4) {
891 log_error("This program takes three or no arguments.");
898 arg_dest_late
= argv
[3];
900 log_set_target(LOG_TARGET_SAFE
);
901 log_parse_environment();
906 r
= proc_cmdline_parse(parse_proc_cmdline_item
, NULL
, 0);
908 log_warning_errno(r
, "Failed to parse kernel command line, ignoring: %m");
910 (void) determine_root();
912 /* Always honour root= and usr= in the kernel command line if we are in an initrd */
916 r
= add_sysroot_mount();
918 k
= add_sysroot_usr_mount();
922 k
= add_volatile_root();
926 r
= add_volatile_var();
928 /* Honour /etc/fstab only when that's enabled */
929 if (arg_fstab_enabled
) {
932 log_debug("Parsing /etc/fstab");
934 /* Parse the local /etc/fstab, possibly from the initrd */
935 k
= parse_fstab(false);
939 /* If running in the initrd also parse the /etc/fstab from the host */
941 log_debug("Parsing /sysroot/etc/fstab");
943 k
= parse_fstab(true);
950 free(arg_root_fstype
);
951 free(arg_root_options
);
955 free(arg_usr_fstype
);
956 free(arg_usr_options
);
958 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;