]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
mountpoint-util: generalize mount_option_supported()
authorLennart Poettering <lennart@poettering.net>
Tue, 7 Mar 2023 10:19:35 +0000 (11:19 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 9 Mar 2023 20:56:42 +0000 (21:56 +0100)
src/basic/mountpoint-util.c
src/basic/mountpoint-util.h
src/core/namespace.c
src/test/test-mountpoint-util.c

index b60cf78b5ac036b12d03aee2640b15de390e027e..6ba07416aebb88a7c4b556e080d2a04831ce3292 100644 (file)
@@ -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! */
+}
index f8529030849067846165fcbc270992de26c0178e..e68fdf668523d85e8d916ff85c9b04c02aab71e1 100644 (file)
@@ -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);
index 3b0896039bc13a37895c179ab471b0b72b65f59a..252f5c9d2ce01b2f2478b297da9a73f3bb5375b4 100644 (file)
@@ -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;
         }
index 5df853a1d97e4592dd23219eb4aface18a34eda4..5feda556414d355aa81bfa93088727f6be44d754 100644 (file)
@@ -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. */