/* SPDX-License-Identifier: LGPL-2.1+ */
#include <errno.h>
-#include <mntent.h>
#include <stdio.h>
-#include <string.h>
#include <unistd.h>
-#include <stdio_ext.h>
#include "alloc-util.h"
#include "fd-util.h"
AUTOMOUNT = 1 << 2,
MAKEFS = 1 << 3,
GROWFS = 1 << 4,
+ RWONLY = 1 << 5,
} MountpointFlags;
static const char *arg_dest = NULL;
static const char *arg_dest_late = NULL;
static bool arg_fstab_enabled = true;
+static bool arg_swap_enabled = true;
static char *arg_root_what = NULL;
static char *arg_root_fstype = NULL;
static char *arg_root_options = NULL;
assert(what);
assert(me);
+ if (!arg_swap_enabled) {
+ log_info("Swap unit generation disabled on kernel command line, ignoring fstab swap entry for %s.", what);
+ return 0;
+ }
+
if (access("/proc/swaps", F_OK) < 0) {
log_info("Swap not supported, ignoring fstab swap entry for %s.", what);
return 0;
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
- r = generator_open_unit_file(arg_dest, "/etc/fstab", name, &f);
+ r = generator_open_unit_file(arg_dest, fstab_path(), name, &f);
+ if (r < 0)
+ return r;
+
+ fprintf(f,
+ "[Unit]\n"
+ "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n"
+ "SourcePath=%s\n",
+ fstab_path());
+
+ r = generator_write_blockdev_dependency(f, what);
if (r < 0)
return r;
- fputs("# Automatically generated by systemd-fstab-generator\n\n"
- "[Unit]\n"
- "SourcePath=/etc/fstab\n"
- "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n"
- "[Swap]\n", f);
+ fprintf(f,
+ "\n"
+ "[Swap]\n");
r = write_what(f, what);
if (r < 0)
streq(me->mnt_dir, "/usr");
}
-static int write_timeout(FILE *f, const char *where, const char *opts,
- const char *filter, const char *variable) {
+static int write_timeout(
+ FILE *f,
+ const char *where,
+ const char *opts,
+ const char *filter,
+ const char *variable) {
+
_cleanup_free_ char *timeout = NULL;
char timespan[FORMAT_TIMESPAN_MAX];
usec_t u;
"x-systemd.mount-timeout\0", "TimeoutSec");
}
-static int write_dependency(FILE *f, const char *opts,
- const char *filter, const char *format) {
+static int write_dependency(
+ FILE *f,
+ const char *opts,
+ const char *filter,
+ const char *format) {
+
_cleanup_strv_free_ char **names = NULL, **units = NULL;
_cleanup_free_ char *res = NULL;
char **s;
STRV_FOREACH(s, names) {
char *x;
- r = unit_name_mangle_with_suffix(*s, 0, ".mount", &x);
+ r = unit_name_mangle_with_suffix(*s, "as dependency", 0, ".mount", &x);
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
+
r = strv_consume(&units, x);
if (r < 0)
return log_oom();
}
static int write_after(FILE *f, const char *opts) {
- return write_dependency(f, opts, "x-systemd.after", "After=%1$s\n");
+ return write_dependency(f, opts,
+ "x-systemd.after", "After=%1$s\n");
}
static int write_requires_after(FILE *f, const char *opts) {
*automount_name = NULL,
*filtered = NULL,
*where_escaped = NULL;
+ _cleanup_strv_free_ char **wanted_by = NULL, **required_by = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
mount_point_ignore(where))
return 0;
+ r = fstab_extract_values(opts, "x-systemd.wanted-by", &wanted_by);
+ if (r < 0)
+ return r;
+
+ r = fstab_extract_values(opts, "x-systemd.required-by", &required_by);
+ if (r < 0)
+ return r;
+
if (path_equal(where, "/")) {
if (flags & NOAUTO)
log_warning("Ignoring \"noauto\" for root device");
log_warning("Ignoring \"nofail\" for root device");
if (flags & AUTOMOUNT)
log_warning("Ignoring automount option for root device");
+ if (!strv_isempty(wanted_by))
+ log_warning("Ignoring \"x-systemd.wanted-by=\" for root device");
+ if (!strv_isempty(required_by))
+ log_warning("Ignoring \"x-systemd.required-by=\" for root device");
+ required_by = strv_free(required_by);
+ wanted_by = strv_free(wanted_by);
SET_FLAG(flags, NOAUTO | NOFAIL | AUTOMOUNT, false);
}
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
- r = generator_open_unit_file(dest, "/etc/fstab", name, &f);
+ r = generator_open_unit_file(dest, fstab_path(), name, &f);
if (r < 0)
return r;
fprintf(f,
"[Unit]\n"
- "SourcePath=%s\n"
- "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
+ "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n"
+ "SourcePath=%s\n",
source);
+ /* All mounts under /sysroot need to happen later, at initrd-fs.target time. IOW, it's not
+ * technically part of the basic initrd filesystem itself, and so shouldn't inherit the default
+ * Before=local-fs.target dependency. */
+ if (in_initrd() && path_startswith(where, "/sysroot"))
+ fprintf(f, "DefaultDependencies=no\n");
+
if (STRPTR_IN_SET(fstype, "nfs", "nfs4") && !(flags & AUTOMOUNT) &&
fstab_test_yes_no_option(opts, "bg\0" "fg\0")) {
/* The default retry timeout that mount.nfs uses for 'bg' mounts
return r;
}
- fprintf(f, "\n[Mount]\n");
+ r = generator_write_blockdev_dependency(f, what);
+ if (r < 0)
+ return r;
+
+ fprintf(f,
+ "\n"
+ "[Mount]\n");
+
if (original_where)
fprintf(f, "# Canonicalized from %s\n", original_where);
if (r < 0)
return r;
+ if (flags & RWONLY)
+ fprintf(f, "ReadWriteOnly=yes\n");
+
r = fflush_and_check(f);
if (r < 0)
return log_error_errno(r, "Failed to write unit file %s: %m", name);
return r;
}
- if (!(flags & NOAUTO) && !(flags & AUTOMOUNT)) {
- r = generator_add_symlink(dest, post,
- (flags & NOFAIL) ? "wants" : "requires", name);
- if (r < 0)
- return r;
- }
-
- if (flags & AUTOMOUNT) {
+ if (!FLAGS_SET(flags, AUTOMOUNT)) {
+ if (!FLAGS_SET(flags, NOAUTO) && strv_isempty(wanted_by) && strv_isempty(required_by)) {
+ r = generator_add_symlink(dest, post,
+ (flags & NOFAIL) ? "wants" : "requires", name);
+ if (r < 0)
+ return r;
+ } else {
+ char **s;
+
+ STRV_FOREACH(s, wanted_by) {
+ r = generator_add_symlink(dest, *s, "wants", name);
+ if (r < 0)
+ return r;
+ }
+
+ STRV_FOREACH(s, required_by) {
+ r = generator_add_symlink(dest, *s, "requires", name);
+ if (r < 0)
+ return r;
+ }
+ }
+ } else {
r = unit_name_from_path(where, ".automount", &automount_name);
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
- fclose(f);
+ f = safe_fclose(f);
- r = generator_open_unit_file(dest, "/etc/fstab", automount_name, &f);
+ r = generator_open_unit_file(dest, fstab_path(), automount_name, &f);
if (r < 0)
return r;
static int parse_fstab(bool initrd) {
_cleanup_endmntent_ FILE *f = NULL;
- const char *fstab_path;
+ const char *fstab;
struct mntent *me;
int r = 0;
- fstab_path = initrd ? "/sysroot/etc/fstab" : "/etc/fstab";
- f = setmntent(fstab_path, "re");
+ fstab = initrd ? "/sysroot/etc/fstab" : fstab_path();
+ log_debug("Parsing %s...", fstab);
+
+ f = setmntent(fstab, "re");
if (!f) {
if (errno == ENOENT)
return 0;
- return log_error_errno(errno, "Failed to open %s: %m", fstab_path);
+ return log_error_errno(errno, "Failed to open %s: %m", fstab);
}
while ((me = getmntent(f))) {
_cleanup_free_ char *where = NULL, *what = NULL, *canonical_where = NULL;
- bool makefs, growfs, noauto, nofail;
+ bool makefs, growfs, noauto, nofail, rwonly;
int k;
if (initrd && !mount_in_initrd(me))
* target is the final directory. */
r = chase_symlinks(where, initrd ? "/sysroot" : NULL,
CHASE_PREFIX_ROOT | CHASE_NONEXISTENT,
- &canonical_where);
+ &canonical_where, NULL);
if (r < 0) /* If we can't canonicalize we continue on as if it wasn't a symlink */
log_debug_errno(r, "Failed to read symlink target for %s, ignoring: %m", where);
else if (streq(canonical_where, where)) /* If it was fully canonicalized, suppress the change */
makefs = fstab_test_option(me->mnt_opts, "x-systemd.makefs\0");
growfs = fstab_test_option(me->mnt_opts, "x-systemd.growfs\0");
+ rwonly = fstab_test_option(me->mnt_opts, "x-systemd.rw-only\0");
noauto = fstab_test_yes_no_option(me->mnt_opts, "noauto\0" "auto\0");
nofail = fstab_test_yes_no_option(me->mnt_opts, "nofail\0" "fail\0");
- log_debug("Found entry what=%s where=%s type=%s makefs=%s nofail=%s noauto=%s",
+ log_debug("Found entry what=%s where=%s type=%s makefs=%s growfs=%s noauto=%s nofail=%s",
what, where, me->mnt_type,
- yes_no(makefs),
+ yes_no(makefs), yes_no(growfs),
yes_no(noauto), yes_no(nofail));
if (streq(me->mnt_type, "swap"))
me->mnt_type,
me->mnt_opts,
me->mnt_passno,
- makefs*MAKEFS | growfs*GROWFS | noauto*NOAUTO | nofail*NOFAIL | automount*AUTOMOUNT,
+ makefs*MAKEFS | growfs*GROWFS | noauto*NOAUTO | nofail*NOFAIL | automount*AUTOMOUNT | rwonly*RWONLY,
post,
- fstab_path);
+ fstab);
}
if (r >= 0 && k < 0)
}
static int add_volatile_root(void) {
- const char *from, *to;
-
- if (arg_volatile_mode != VOLATILE_YES)
- return 0;
/* Let's add in systemd-remount-volatile.service which will remount the root device to tmpfs if this is
- * requested, leaving only /usr from the root mount inside. */
-
- from = strjoina(SYSTEM_DATA_UNIT_PATH "/systemd-volatile-root.service");
- to = strjoina(arg_dest, "/" SPECIAL_INITRD_ROOT_FS_TARGET, ".requires/systemd-volatile-root.service");
-
- (void) mkdir_parents(to, 0755);
+ * requested (or as an overlayfs), leaving only /usr from the root mount inside. */
- if (symlink(from, to) < 0)
- return log_error_errno(errno, "Failed to hook in volatile remount service: %m");
+ if (!IN_SET(arg_volatile_mode, VOLATILE_YES, VOLATILE_OVERLAY))
+ return 0;
- return 0;
+ return generator_add_symlink(arg_dest, SPECIAL_INITRD_ROOT_FS_TARGET, "requires",
+ SYSTEM_DATA_UNIT_PATH "/" SPECIAL_VOLATILE_ROOT_SERVICE);
}
static int add_volatile_var(void) {
arg_volatile_mode = m;
} else
arg_volatile_mode = VOLATILE_YES;
+
+ } else if (streq(key, "systemd.swap")) {
+
+ r = value ? parse_boolean(value) : 1;
+ if (r < 0)
+ log_warning("Failed to parse systemd.swap switch %s. Ignoring.", value);
+ else
+ arg_swap_enabled = r;
}
return 0;
}
static int run(const char *dest, const char *dest_early, const char *dest_late) {
- int r;
+ int r, r2 = 0, r3 = 0;
assert_se(arg_dest = dest);
assert_se(arg_dest_late = dest_late);
/* Always honour root= and usr= in the kernel command line if we are in an initrd */
if (in_initrd()) {
- int k;
-
r = add_sysroot_mount();
- k = add_sysroot_usr_mount();
- if (k < 0)
- r = k;
+ r2 = add_sysroot_usr_mount();
- k = add_volatile_root();
- if (k < 0)
- r = k;
+ r3 = add_volatile_root();
} else
r = add_volatile_var();
/* Honour /etc/fstab only when that's enabled */
if (arg_fstab_enabled) {
- int k;
-
- log_debug("Parsing /etc/fstab");
-
/* Parse the local /etc/fstab, possibly from the initrd */
- k = parse_fstab(false);
- if (k < 0)
- r = k;
+ r2 = parse_fstab(false);
/* If running in the initrd also parse the /etc/fstab from the host */
- if (in_initrd()) {
- log_debug("Parsing /sysroot/etc/fstab");
-
- k = parse_fstab(true);
- if (k < 0)
- r = k;
- }
+ if (in_initrd())
+ r3 = parse_fstab(true);
+ else
+ r3 = generator_enable_remount_fs_service(arg_dest);
}
- return r;
+ return r < 0 ? r : r2 < 0 ? r2 : r3;
}
DEFINE_MAIN_GENERATOR_FUNCTION(run);