From: Lennart Poettering Date: Tue, 7 Mar 2023 10:19:35 +0000 (+0100) Subject: mountpoint-util: generalize mount_option_supported() X-Git-Tag: v254-rc1~1073^2~4 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=117e7034470251a7da4061c83cb49df32c0defe0;p=thirdparty%2Fsystemd.git mountpoint-util: generalize mount_option_supported() --- diff --git a/src/basic/mountpoint-util.c b/src/basic/mountpoint-util.c index b60cf78b5ac..6ba07416aeb 100644 --- a/src/basic/mountpoint-util.c +++ b/src/basic/mountpoint-util.c @@ -664,3 +664,54 @@ not_supported: cache = false; return 0; } + +int mount_option_supported(const char *fstype, const char *key, const char *value) { + _cleanup_close_ int fd = -EBADF; + int r; + + /* Checks if the specified file system supports a mount option. Returns > 0 if it suppors it, == 0 if + * it does not. Return -EAGAIN if we can't determine it. And any other error otherwise. */ + + assert(fstype); + assert(key); + + fd = fsopen(fstype, FSOPEN_CLOEXEC); + if (fd < 0) { + if (ERRNO_IS_NOT_SUPPORTED(errno)) + return -EAGAIN; /* new mount API not available → don't know */ + + return log_debug_errno(errno, "Failed to open superblock context for '%s': %m", fstype); + } + + /* Various file systems have not been converted to the new mount API yet. For such file systems + * fsconfig() with FSCONFIG_SET_STRING/FSCONFIG_SET_FLAG never fail. Which sucks, because we want to + * use it for testing support, after all. Let's hence do a check if the file system got converted yet + * first. */ + if (fsconfig(fd, FSCONFIG_SET_FD, "adefinitelynotexistingmountoption", NULL, fd) < 0) { + /* If FSCONFIG_SET_FD is not supported for the fs, then the file system was not converted to + * the new mount API yet. If it returns EINVAL the mount option doesn't exist, but the fstype + * is converted. */ + if (errno == EOPNOTSUPP) + return -EAGAIN; /* FSCONFIG_SET_FD not supported on the fs, hence not converted to new mount API → don't know */ + if (errno != EINVAL) + return log_debug_errno(errno, "Failed to check if file system has been converted to new mount API: %m"); + + /* So FSCONFIG_SET_FD worked, but the option didn't exist (we got EINVAL), this means the fs + * is converted. Let's now ask the actual question we wonder about. */ + } else + return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN), "FSCONFIG_SET_FD worked unexpectedly for '%s', whoa!", fstype); + + if (value) + r = fsconfig(fd, FSCONFIG_SET_STRING, key, value, 0); + else + r = fsconfig(fd, FSCONFIG_SET_FLAG, key, NULL, 0); + if (r < 0) { + if (errno == EINVAL) + return false; /* EINVAL means option not supported. */ + + return log_debug_errno(errno, "Failed to set '%s%s%s' on '%s' superblock context: %m", + key, value ? "=" : "", strempty(value), fstype); + } + + return true; /* works! */ +} diff --git a/src/basic/mountpoint-util.h b/src/basic/mountpoint-util.h index f8529030849..e68fdf66852 100644 --- a/src/basic/mountpoint-util.h +++ b/src/basic/mountpoint-util.h @@ -60,3 +60,5 @@ int mount_propagation_flag_from_string(const char *name, unsigned long *ret); bool mount_propagation_flag_is_valid(unsigned long flag); unsigned long ms_nosymfollow_supported(void); + +int mount_option_supported(const char *fstype, const char *key, const char *value); diff --git a/src/core/namespace.c b/src/core/namespace.c index 3b0896039bc..252f5c9d2ce 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -1099,27 +1099,6 @@ static int mount_bind_sysfs(const MountEntry *m) { return 1; } -static bool mount_option_supported(const char *fstype, const char *key, const char *value) { - _cleanup_close_ int fd = -EBADF; - int r; - - /* This function assumes support by default. Only if the fsconfig() call fails with -EINVAL/-EOPNOTSUPP - * will it report that the option/value is not supported. */ - - fd = fsopen(fstype, FSOPEN_CLOEXEC); - if (fd < 0) { - if (errno != ENOSYS) - log_debug_errno(errno, "Failed to open superblock context for '%s': %m", fstype); - return true; /* If fsopen() fails for whatever reason, assume the value is supported. */ - } - - r = fsconfig(fd, FSCONFIG_SET_STRING, key, value, 0); - if (r < 0 && !IN_SET(errno, EINVAL, EOPNOTSUPP, ENOSYS)) - log_debug_errno(errno, "Failed to set '%s=%s' on '%s' superblock context: %m", key, value, fstype); - - return r >= 0 || !IN_SET(errno, EINVAL, EOPNOTSUPP); -} - static int mount_procfs(const MountEntry *m, const NamespaceInfo *ns_info) { _cleanup_free_ char *opts = NULL; const char *entry_path; @@ -1147,13 +1126,14 @@ static int mount_procfs(const MountEntry *m, const NamespaceInfo *ns_info) { * fsopen()/fsconfig() was also backported on some distros which allows us to detect * hidepid=/subset= support in even more scenarios. */ - if (mount_option_supported("proc", "hidepid", hpv)) { + if (mount_option_supported("proc", "hidepid", hpv) != 0) { opts = strjoin("hidepid=", hpv); if (!opts) return -ENOMEM; } - if (ns_info->proc_subset == PROC_SUBSET_PID && mount_option_supported("proc", "subset", "pid")) + if (ns_info->proc_subset == PROC_SUBSET_PID && + mount_option_supported("proc", "subset", "pid") != 0) if (!strextend_with_separator(&opts, ",", "subset=pid")) return -ENOMEM; } diff --git a/src/test/test-mountpoint-util.c b/src/test/test-mountpoint-util.c index 5df853a1d97..5feda556414 100644 --- a/src/test/test-mountpoint-util.c +++ b/src/test/test-mountpoint-util.c @@ -325,6 +325,30 @@ TEST(ms_nosymfollow_supported) { log_info("MS_NOSYMFOLLOW supported: %s", yes_no(ms_nosymfollow_supported())); } +TEST(mount_option_supported) { + int r; + + r = mount_option_supported("tmpfs", "size", "64M"); + log_info("tmpfs supports size=64M: %s (%i)", r < 0 ? "dont know" : yes_no(r), r); + assert_se(r > 0 || (r < 0 && ERRNO_IS_PRIVILEGE(r))); + + r = mount_option_supported("ext4", "discard", NULL); + log_info("ext4 supports discard: %s (%i)", r < 0 ? "dont know" : yes_no(r), r); + assert_se(r > 0 || r == -EAGAIN || (r < 0 && ERRNO_IS_PRIVILEGE(r))); + + r = mount_option_supported("tmpfs", "idontexist", "64M"); + log_info("tmpfs supports idontexist: %s (%i)", r < 0 ? "dont know" : yes_no(r), r); + assert_se(r == 0 || (r < 0 && ERRNO_IS_PRIVILEGE(r))); + + r = mount_option_supported("tmpfs", "ialsodontexist", NULL); + log_info("tmpfs supports ialsodontexist: %s (%i)", r < 0 ? "dont know" : yes_no(r), r); + assert_se(r == 0 || (r < 0 && ERRNO_IS_PRIVILEGE(r))); + + r = mount_option_supported("proc", "hidepid", "1"); + log_info("proc supports hidepid=1: %s (%i)", r < 0 ? "dont know" : yes_no(r), r); + assert_se(r >= 0 || (r < 0 && ERRNO_IS_PRIVILEGE(r))); +} + static int intro(void) { /* let's move into our own mount namespace with all propagation from the host turned off, so * that /proc/self/mountinfo is static and constant for the whole time our test runs. */