]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
mount-util: make path_get_mount_info() work arbitrary inode
authorYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 7 Nov 2024 18:56:46 +0000 (03:56 +0900)
committerLennart Poettering <lennart@poettering.net>
Fri, 8 Nov 2024 12:25:17 +0000 (13:25 +0100)
Follow-up for d49d95df0a260aaca9a3fdd1e6ce535592a53bca.
Replaces 9a032ec55a9820a0424309670fe551c99203e5f1.
Fixes #35075.

src/shared/mount-util.c
src/shared/mount-util.h
src/test/test-mount-util.c

index f04235001d21ab3c7b53a70be5d64c13a3ac0df0..576f7e83aa192ab7865fe0316734e6d7427c99ae 100644 (file)
@@ -1808,63 +1808,81 @@ char* umount_and_unlink_and_free(char *p) {
         return mfree(p);
 }
 
-static int path_get_mount_info(
+static int path_get_mount_info_at(
+                int dir_fd,
                 const char *path,
                 char **ret_fstype,
                 char **ret_options) {
 
         _cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL;
-        _cleanup_free_ char *fstype = NULL, *options = NULL;
-        struct libmnt_fs *fs;
-        int r;
+        _cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL;
+        int r, mnt_id;
 
-        assert(path);
+        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
 
-        table = mnt_new_table();
-        if (!table)
-                return -ENOMEM;
+        r = path_get_mnt_id_at(dir_fd, path, &mnt_id);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to get mount ID: %m");
 
-        r = mnt_table_parse_mtab(table, /* filename = */ NULL);
+        r = libmount_parse("/proc/self/mountinfo", NULL, &table, &iter);
         if (r < 0)
-                return r;
+                return log_debug_errno(r, "Failed to parse /proc/self/mountinfo: %m");
 
-        fs = mnt_table_find_mountpoint(table, path, MNT_ITER_FORWARD);
-        if (!fs)
-                return -EINVAL;
+        for (;;) {
+                struct libmnt_fs *fs;
 
-        if (ret_fstype) {
-                fstype = strdup(strempty(mnt_fs_get_fstype(fs)));
-                if (!fstype)
-                        return -ENOMEM;
-        }
+                r = mnt_table_next_fs(table, iter, &fs);
+                if (r == 1)
+                        break; /* EOF */
+                if (r < 0)
+                        return log_debug_errno(r, "Failed to get next entry from /proc/self/mountinfo: %m");
 
-        if (ret_options) {
-                options = strdup(strempty(mnt_fs_get_options(fs)));
-                if (!options)
-                        return -ENOMEM;
-        }
+                if (mnt_fs_get_id(fs) != mnt_id)
+                        continue;
 
-        if (ret_fstype)
-                *ret_fstype = TAKE_PTR(fstype);
-        if (ret_options)
-                *ret_options = TAKE_PTR(options);
+                _cleanup_free_ char *fstype = NULL, *options = NULL;
 
-        return 0;
+                if (ret_fstype) {
+                        fstype = strdup(strempty(mnt_fs_get_fstype(fs)));
+                        if (!fstype)
+                                return log_oom_debug();
+                }
+
+                if (ret_options) {
+                        options = strdup(strempty(mnt_fs_get_options(fs)));
+                        if (!options)
+                                return log_oom_debug();
+                }
+
+                if (ret_fstype)
+                        *ret_fstype = TAKE_PTR(fstype);
+                if (ret_options)
+                        *ret_options = TAKE_PTR(options);
+
+                return 0;
+        }
+
+        return log_debug_errno(SYNTHETIC_ERRNO(ESTALE), "Cannot find mount ID %i from /proc/self/mountinfo.", mnt_id);
 }
 
-int path_is_network_fs_harder(const char *path) {
-        _cleanup_free_ char *fstype = NULL, *options = NULL;
-        int r, ret;
+int path_is_network_fs_harder_at(int dir_fd, const char *path) {
+        _cleanup_close_ int fd = -EBADF;
+        int r;
 
-        assert(path);
+        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
 
-        ret = path_is_network_fs(path);
-        if (ret > 0)
-                return true;
+        fd = xopenat(dir_fd, path, O_PATH | O_CLOEXEC | O_NOFOLLOW);
+        if (fd < 0)
+                return fd;
+
+        r = fd_is_network_fs(fd);
+        if (r != 0)
+                return r;
 
-        r = path_get_mount_info(path, &fstype, &options);
+        _cleanup_free_ char *fstype = NULL, *options = NULL;
+        r = path_get_mount_info_at(fd, /* path = */ NULL, &fstype, &options);
         if (r < 0)
-                return RET_GATHER(ret, r);
+                return r;
 
         if (fstype_is_network(fstype))
                 return true;
index 067ed0e4d942720e767e1f03ad4835d2c4e3ac53..496a95ab050eeee7b9a498a7d0ad3f335628cbdc 100644 (file)
@@ -181,4 +181,7 @@ int mount_credentials_fs(const char *path, size_t size, bool ro);
 
 int make_fsmount(int error_log_level, const char *what, const char *type, unsigned long flags, const char *options, int userns_fd);
 
-int path_is_network_fs_harder(const char *path);
+int path_is_network_fs_harder_at(int dir_fd, const char *path);
+static inline int path_is_network_fs_harder(const char *path) {
+        return path_is_network_fs_harder_at(AT_FDCWD, path);
+}
index 469aa35913baaa4fdfc8e19a8a51c45dc35cfe38..4ac8f869d6614d259c7a6f3e86966e09ee808100 100644 (file)
@@ -538,9 +538,53 @@ TEST(bind_mount_submounts) {
 }
 
 TEST(path_is_network_fs_harder) {
-        ASSERT_OK_ZERO(path_is_network_fs_harder("/dev"));
-        ASSERT_OK_ZERO(path_is_network_fs_harder("/sys"));
-        ASSERT_OK_ZERO(path_is_network_fs_harder("/run"));
+        _cleanup_close_ int dir_fd = -EBADF;
+        int r;
+
+        ASSERT_OK(dir_fd = open("/", O_PATH | O_CLOEXEC));
+        FOREACH_STRING(s,
+                       "/", "/dev/", "/proc/", "/run/", "/sys/", "/tmp/", "/usr/", "/var/tmp/",
+                       "", ".", "../../../", "/this/path/should/not/exist/for/test-mount-util/") {
+
+                r = path_is_network_fs_harder(s);
+                log_debug("path_is_network_fs_harder(%s) → %i: %s", s, r, r < 0 ? STRERROR(r) : yes_no(r));
+
+                const char *q = path_startswith(s, "/") ?: s;
+                r = path_is_network_fs_harder_at(dir_fd, q);
+                log_debug("path_is_network_fs_harder_at(root, %s) → %i: %s", q, r, r < 0 ? STRERROR(r) : yes_no(r));
+        }
+
+        if (geteuid() != 0 || have_effective_cap(CAP_SYS_ADMIN) <= 0) {
+                (void) log_tests_skipped("not running privileged");
+                return;
+        }
+
+        _cleanup_(rm_rf_physical_and_freep) char *t = NULL;
+        assert_se(mkdtemp_malloc("/tmp/test-mount-util.path_is_network_fs_harder.XXXXXXX", &t) >= 0);
+
+        r = safe_fork("(make_mount-point)",
+                      FORK_RESET_SIGNALS |
+                      FORK_CLOSE_ALL_FDS |
+                      FORK_DEATHSIG_SIGTERM |
+                      FORK_WAIT |
+                      FORK_REOPEN_LOG |
+                      FORK_LOG |
+                      FORK_NEW_MOUNTNS |
+                      FORK_MOUNTNS_SLAVE,
+                      NULL);
+        ASSERT_OK(r);
+
+        if (r == 0) {
+                ASSERT_OK(mount_nofollow_verbose(LOG_INFO, "tmpfs", t, "tmpfs", 0, NULL));
+                ASSERT_OK_ZERO(path_is_network_fs_harder(t));
+                ASSERT_OK_ERRNO(umount(t));
+
+                ASSERT_OK(mount_nofollow_verbose(LOG_INFO, "tmpfs", t, "tmpfs", 0, "x-systemd-growfs,x-systemd-automount"));
+                ASSERT_OK_ZERO(path_is_network_fs_harder(t));
+                ASSERT_OK_ERRNO(umount(t));
+
+                _exit(EXIT_SUCCESS);
+        }
 }
 
 DEFINE_TEST_MAIN(LOG_DEBUG);