From ddcdc462b935fc210677f664d61cf556fb2abe0f Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 8 May 2023 19:45:34 +0900 Subject: [PATCH] fstab-generator: split out several functions from parse_fstab() No functional changes, just refactoring and preparation for later commits. (cherry picked from commit cfeb4d378ecd1ea50c0a0248c384e49983511fa8) Related: #2190226 --- src/fstab-generator/fstab-generator.c | 323 ++++++++++++++------------ 1 file changed, 171 insertions(+), 152 deletions(-) diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index bbd669e477..4ea95f29eb 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -99,7 +99,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; @@ -107,7 +107,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); @@ -155,7 +154,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; @@ -164,7 +163,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; @@ -188,18 +187,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( @@ -634,6 +629,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, @@ -647,11 +656,153 @@ 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_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_symlinks(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 noauto=%s nofail=%s", + what, where, strna(fstype), + yes_no(flags & MOUNT_MAKEFS), yes_no(flags & MOUNT_GROWFS), + 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(); @@ -671,146 +822,14 @@ static int parse_fstab(bool initrd) { } while ((me = getmntent(f))) { - _cleanup_free_ char *where = NULL, *what = NULL, *canonical_where = NULL; - bool makefs, growfs, 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("Running in a container, ignoring fstab entry for %s.", what); - continue; - } - - if (sysfs_check < 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"); - sysfs_check = r != 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_symlinks() learns to chase non-existent paths, use this here and - * drop the prefixing with /sysroot on error below. - */ - k = chase_symlinks(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"); - 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 noauto=%s nofail=%s", - what, where, me->mnt_type, - yes_no(makefs), yes_no(growfs), - yes_no(noauto), yes_no(nofail)); - - flags = makefs * MOUNT_MAKEFS | - growfs * MOUNT_GROWFS | - 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) {