fstab-generator: split out several functions from parse_fstab()

No functional changes, just refactoring and preparation for later
commits.
This commit is contained in:
Yu Watanabe
2023-05-24 08:23:22 +09:00
parent 82c29dbee0
commit cfeb4d378e
+173 -153
View File
@@ -102,7 +102,7 @@ static int write_what(FILE *f, const char *what) {
static int add_swap(
const char *source,
const char *what,
struct mntent *me,
const char *options,
MountPointFlags flags) {
_cleanup_free_ char *name = NULL;
@@ -110,7 +110,6 @@ static int add_swap(
int r;
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);
@@ -158,7 +157,7 @@ static int add_swap(
if (r < 0)
return r;
r = write_options(f, me->mnt_opts);
r = write_options(f, options);
if (r < 0)
return r;
@@ -167,7 +166,7 @@ static int add_swap(
return log_error_errno(r, "Failed to write unit file %s: %m", name);
/* use what as where, to have a nicer error message */
r = generator_write_timeouts(arg_dest, what, what, me->mnt_opts, NULL);
r = generator_write_timeouts(arg_dest, what, what, options, NULL);
if (r < 0)
return r;
@@ -193,18 +192,14 @@ static int add_swap(
return true;
}
static bool mount_is_network(struct mntent *me) {
assert(me);
return fstab_test_option(me->mnt_opts, "_netdev\0") ||
fstype_is_network(me->mnt_type);
static bool mount_is_network(const char *fstype, const char *options) {
return fstab_test_option(options, "_netdev\0") ||
(fstype && fstype_is_network(fstype));
}
static bool mount_in_initrd(struct mntent *me) {
assert(me);
return fstab_test_option(me->mnt_opts, "x-initrd.mount\0") ||
path_equal(me->mnt_dir, "/usr");
static bool mount_in_initrd(const char *where, const char *options) {
return fstab_test_option(options, "x-initrd.mount\0") ||
(where && path_equal(where, "/usr"));
}
static int write_timeout(
@@ -643,6 +638,20 @@ static const char* sysroot_fstab_path(void) {
return getenv("SYSTEMD_SYSROOT_FSTAB") ?: "/sysroot/etc/fstab";
}
static bool sysfs_check(void) {
static int cached = -1;
int r;
if (cached < 0) {
r = getenv_bool_secure("SYSTEMD_SYSFS_CHECK");
if (r < 0 && r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_SYSFS_CHECK, ignoring: %m");
cached = r != 0;
}
return cached;
}
static int add_sysusr_sysroot_usr_bind_mount(const char *source) {
return add_mount(source,
arg_dest,
@@ -656,11 +665,155 @@ static int add_sysusr_sysroot_usr_bind_mount(const char *source) {
SPECIAL_INITRD_FS_TARGET);
}
static MountPointFlags fstab_options_to_flags(const char *options, bool is_swap) {
MountPointFlags flags = 0;
if (fstab_test_option(options, "x-systemd.makefs\0"))
flags |= MOUNT_MAKEFS;
if (fstab_test_option(options, "x-systemd.growfs\0"))
flags |= MOUNT_GROWFS;
if (fstab_test_option(options, "x-systemd.pcrfs\0"))
flags |= MOUNT_PCRFS;
if (fstab_test_yes_no_option(options, "noauto\0" "auto\0"))
flags |= MOUNT_NOAUTO;
if (fstab_test_yes_no_option(options, "nofail\0" "fail\0"))
flags |= MOUNT_NOFAIL;
if (!is_swap) {
if (fstab_test_option(options, "x-systemd.rw-only\0"))
flags |= MOUNT_RW_ONLY;
if (fstab_test_option(options,
"comment=systemd.automount\0"
"x-systemd.automount\0"))
flags |= MOUNT_AUTOMOUNT;
}
return flags;
}
static int parse_fstab_one(
const char *source,
const char *what_original,
const char *where_original,
const char *fstype,
const char *options,
int passno,
bool initrd) {
_cleanup_free_ char *what = NULL, *where = NULL, *canonical_where = NULL;
MountPointFlags flags;
int r;
assert(what_original);
assert(where_original);
assert(fstype);
assert(options);
if (initrd && !mount_in_initrd(where_original, options))
return 0;
what = fstab_node_to_udev_node(what_original);
if (!what)
return log_oom();
if (path_is_read_only_fs("/sys") > 0 &&
(streq(what, "sysfs") ||
(sysfs_check() && is_device_path(what)))) {
log_info("/sys/ is read-only (running in a container?), ignoring mount for %s.", what);
return 0;
}
where = strdup(where_original);
if (!where)
return log_oom();
if (is_path(where)) {
path_simplify(where);
/* Follow symlinks here; see 5261ba901845c084de5a8fd06500ed09bfb0bd80 which makes sense for
* mount units, but causes problems since it historically worked to have symlinks in e.g.
* /etc/fstab. So we canonicalize here. Note that we use CHASE_NONEXISTENT to handle the case
* where a symlink refers to another mount target; this works assuming the sub-mountpoint
* target is the final directory.
*
* FIXME: when chase() learns to chase non-existent paths, use this here and
* drop the prefixing with /sysroot on error below.
*/
r = chase(where, initrd ? "/sysroot" : NULL, CHASE_PREFIX_ROOT | CHASE_NONEXISTENT,
&canonical_where, NULL);
if (r < 0) {
/* If we can't canonicalize, continue as if it wasn't a symlink */
log_debug_errno(r, "Failed to read symlink target for %s, using as-is: %m", where);
if (initrd) {
canonical_where = path_join("/sysroot", where);
if (!canonical_where)
return log_oom();
}
} else if (streq(canonical_where, where)) /* If it was fully canonicalized, suppress the change */
canonical_where = mfree(canonical_where);
else
log_debug("Canonicalized what=%s where=%s to %s", what, where, canonical_where);
}
flags = fstab_options_to_flags(options, streq_ptr(fstype, "swap"));
log_debug("Found entry what=%s where=%s type=%s makefs=%s growfs=%s pcrfs=%s noauto=%s nofail=%s",
what, where, strna(fstype),
yes_no(flags & MOUNT_MAKEFS), yes_no(flags & MOUNT_GROWFS), yes_no(flags & MOUNT_PCRFS),
yes_no(flags & MOUNT_NOAUTO), yes_no(flags & MOUNT_NOFAIL));
if (streq_ptr(fstype, "swap"))
return add_swap(source, what, options, flags);
bool is_sysroot = in_initrd() && path_equal(where, "/sysroot");
/* See comment from add_sysroot_usr_mount() about the need for extra indirection in case /usr needs
* to be mounted in order for the root fs to be synthesized based on configuration included in /usr/,
* e.g. systemd-repart. */
bool is_sysroot_usr = in_initrd() && path_equal(where, "/sysroot/usr");
const char *target_unit =
initrd ? SPECIAL_INITRD_FS_TARGET :
is_sysroot ? SPECIAL_INITRD_ROOT_FS_TARGET :
is_sysroot_usr ? SPECIAL_INITRD_USR_FS_TARGET :
mount_is_network(fstype, options) ? SPECIAL_REMOTE_FS_TARGET :
SPECIAL_LOCAL_FS_TARGET;
if (is_sysroot && is_device_path(what)) {
r = generator_write_initrd_root_device_deps(arg_dest, what);
if (r < 0)
return r;
}
r = add_mount(source,
arg_dest,
what,
is_sysroot_usr ? "/sysusr/usr" : canonical_where ?: where,
!is_sysroot_usr && canonical_where ? where : NULL,
fstype,
options,
passno,
flags,
target_unit);
if (r <= 0)
return r;
if (is_sysroot_usr) {
log_debug("Synthesizing fstab entry what=/sysusr/usr where=/sysroot/usr opts=bind");
r = add_sysusr_sysroot_usr_bind_mount(source);
if (r < 0)
return r;
}
return true;
}
static int parse_fstab(bool initrd) {
_cleanup_endmntent_ FILE *f = NULL;
const char *fstab;
struct mntent *me;
int r = 0, sysfs_check = -1;
int r, ret = 0;
if (initrd)
fstab = sysroot_fstab_path();
@@ -680,147 +833,14 @@ static int parse_fstab(bool initrd) {
}
while ((me = getmntent(f))) {
_cleanup_free_ char *where = NULL, *what = NULL, *canonical_where = NULL;
bool makefs, growfs, pcrfs, noauto, nofail;
MountPointFlags flags;
int k;
if (initrd && !mount_in_initrd(me))
continue;
what = fstab_node_to_udev_node(me->mnt_fsname);
if (!what)
return log_oom();
if (path_is_read_only_fs("/sys") > 0) {
if (streq(what, "sysfs")) {
log_info("/sys/ is read-only (running in a container?), ignoring fstab entry for %s.", me->mnt_dir);
continue;
}
if (sysfs_check < 0) {
k = getenv_bool_secure("SYSTEMD_SYSFS_CHECK");
if (k < 0 && k != -ENXIO)
log_debug_errno(k, "Failed to parse $SYSTEMD_SYSFS_CHECK, ignoring: %m");
sysfs_check = k != 0;
}
if (sysfs_check && is_device_path(what)) {
log_info("/sys/ is read-only (running in a container?), ignoring fstab device entry for %s.", what);
continue;
}
}
where = strdup(me->mnt_dir);
if (!where)
return log_oom();
if (is_path(where)) {
path_simplify(where);
/* Follow symlinks here; see 5261ba901845c084de5a8fd06500ed09bfb0bd80 which makes sense for
* mount units, but causes problems since it historically worked to have symlinks in e.g.
* /etc/fstab. So we canonicalize here. Note that we use CHASE_NONEXISTENT to handle the case
* where a symlink refers to another mount target; this works assuming the sub-mountpoint
* target is the final directory.
*
* FIXME: when chase() learns to chase non-existent paths, use this here and
* drop the prefixing with /sysroot on error below.
*/
k = chase(where, initrd ? "/sysroot" : NULL, CHASE_PREFIX_ROOT | CHASE_NONEXISTENT,
&canonical_where, NULL);
if (k < 0) {
/* If we can't canonicalize, continue as if it wasn't a symlink */
log_debug_errno(k, "Failed to read symlink target for %s, using as-is: %m", where);
if (initrd) {
canonical_where = path_join("/sysroot", where);
if (!canonical_where)
return log_oom();
}
} else if (streq(canonical_where, where)) /* If it was fully canonicalized, suppress the change */
canonical_where = mfree(canonical_where);
else
log_debug("Canonicalized what=%s where=%s to %s", what, where, canonical_where);
}
makefs = fstab_test_option(me->mnt_opts, "x-systemd.makefs\0");
growfs = fstab_test_option(me->mnt_opts, "x-systemd.growfs\0");
pcrfs = fstab_test_option(me->mnt_opts, "x-systemd.pcrfs\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 growfs=%s pcrfs=%s noauto=%s nofail=%s",
what, where, me->mnt_type,
yes_no(makefs), yes_no(growfs), yes_no(pcrfs),
yes_no(noauto), yes_no(nofail));
flags = makefs * MOUNT_MAKEFS |
growfs * MOUNT_GROWFS |
pcrfs * MOUNT_PCRFS |
noauto * MOUNT_NOAUTO |
nofail * MOUNT_NOFAIL;
if (streq(me->mnt_type, "swap"))
k = add_swap(fstab, what, me, flags);
else {
bool rw_only, automount, is_sysroot, is_sysroot_usr;
rw_only = fstab_test_option(me->mnt_opts, "x-systemd.rw-only\0");
automount = fstab_test_option(me->mnt_opts,
"comment=systemd.automount\0"
"x-systemd.automount\0");
flags |= rw_only * MOUNT_RW_ONLY |
automount * MOUNT_AUTOMOUNT;
is_sysroot = in_initrd() && path_equal(where, "/sysroot");
/* See comment from add_sysroot_usr_mount about the need for extra indirection
* in case /usr needs to be mounted in order for the root fs to be synthesized
* based on configuration included in /usr/, e.g. systemd-repart. */
is_sysroot_usr = in_initrd() && path_equal(where, "/sysroot/usr");
const char *target_unit =
initrd ? SPECIAL_INITRD_FS_TARGET :
is_sysroot ? SPECIAL_INITRD_ROOT_FS_TARGET :
is_sysroot_usr ? SPECIAL_INITRD_USR_FS_TARGET :
mount_is_network(me) ? SPECIAL_REMOTE_FS_TARGET :
SPECIAL_LOCAL_FS_TARGET;
if (is_sysroot && is_device_path(what)) {
r = generator_write_initrd_root_device_deps(arg_dest, what);
if (r < 0)
return r;
}
k = add_mount(fstab,
arg_dest,
what,
is_sysroot_usr ? "/sysusr/usr" : canonical_where ?: where,
!is_sysroot_usr && canonical_where ? where : NULL,
me->mnt_type,
me->mnt_opts,
me->mnt_passno,
flags,
target_unit);
if (is_sysroot_usr && k >= 0) {
log_debug("Synthesizing fstab entry what=/sysusr/usr where=/sysroot/usr opts=bind");
r = add_sysusr_sysroot_usr_bind_mount(fstab);
if (r != 0)
k = r;
}
}
if (arg_sysroot_check && k > 0)
r = parse_fstab_one(fstab, me->mnt_fsname, me->mnt_dir, me->mnt_type, me->mnt_opts, me->mnt_passno, initrd);
if (r < 0 && ret >= 0)
ret = r;
if (arg_sysroot_check && r > 0)
return true; /* We found a mount or swap that would be started… */
if (r >= 0 && k < 0)
r = k;
}
return r;
return ret;
}
static int sysroot_is_nfsroot(void) {