]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
fd-util: Add dir_fd_is_root() 26820/head
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Wed, 1 Feb 2023 12:14:29 +0000 (13:14 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Wed, 15 Mar 2023 10:51:08 +0000 (11:51 +0100)
src/basic/fd-util.c
src/basic/fd-util.h
src/test/test-fd-util.c

index 430db0c87907b8b61a40fad431b96aa4e0e6f1f8..d7296432650800f0b3baa645338416929bef78d6 100644 (file)
@@ -21,6 +21,7 @@
 #include "missing_fcntl.h"
 #include "missing_fs.h"
 #include "missing_syscall.h"
+#include "mountpoint-util.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "process-util.h"
@@ -864,3 +865,54 @@ int fd_get_diskseq(int fd, uint64_t *ret) {
 
         return 0;
 }
+
+int dir_fd_is_root(int dir_fd) {
+        STRUCT_NEW_STATX_DEFINE(st);
+        STRUCT_NEW_STATX_DEFINE(pst);
+        int r;
+
+        assert(dir_fd >= 0);
+
+        r = statx_fallback(dir_fd, ".", 0, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st.sx);
+        if (r == -ENOTDIR)
+                return false;
+        if (r < 0)
+                return r;
+
+        if (!FLAGS_SET(st.nsx.stx_mask, STATX_MNT_ID)) {
+                int mntid;
+
+                r = path_get_mnt_id_at(dir_fd, "", &mntid);
+                if (r < 0)
+                        return r;
+                assert(mntid >= 0);
+
+                st.nsx.stx_mnt_id = mntid;
+                st.nsx.stx_mask |= STATX_MNT_ID;
+        }
+
+        r = statx_fallback(dir_fd, "..", 0, STATX_TYPE|STATX_INO|STATX_MNT_ID, &pst.sx);
+        if (r < 0)
+                return r;
+
+        if (!FLAGS_SET(pst.nsx.stx_mask, STATX_MNT_ID)) {
+                int mntid;
+
+                r = path_get_mnt_id_at(dir_fd, "..", &mntid);
+                if (r < 0)
+                        return r;
+                assert(mntid >= 0);
+
+                pst.nsx.stx_mnt_id = mntid;
+                pst.nsx.stx_mask |= STATX_MNT_ID;
+        }
+
+        /* If the parent directory is the same inode, the fd points to the root directory "/". We also check
+         * that the mount ids are the same. Otherwise, a construct like the following could be used to trick
+         * us:
+         *
+         * $ mkdir /tmp/x /tmp/x/y
+         * $ mount --bind /tmp/x /tmp/x/y
+         */
+        return statx_inode_same(&st.sx, &pst.sx) && statx_mount_same(&st.nsx, &pst.nsx);
+}
index 76be00e4f4e2add02b4dcf9662ae94312058e677..a6353cf48e4b824eda6880a55f923bcc0e56f50d 100644 (file)
@@ -100,6 +100,8 @@ int fd_is_opath(int fd);
 int read_nr_open(void);
 int fd_get_diskseq(int fd, uint64_t *ret);
 
+int dir_fd_is_root(int dir_fd);
+
 /* The maximum length a buffer for a /proc/self/fd/<fd> path needs */
 #define PROC_FD_PATH_MAX \
         (STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int))
index 1ca9ef8bbd7a2ee5e871f2643c06745deca63f72..c5f32aa6792c543c7fd3a86dddee0a85566b3113 100644 (file)
@@ -569,4 +569,16 @@ TEST(take_fd) {
         assert_se(array[1] == -EBADF);
 }
 
+TEST(dir_fd_is_root) {
+        _cleanup_close_ int fd = -EBADF;
+
+        assert_se((fd = open("/", O_CLOEXEC|O_PATH|O_DIRECTORY|O_NOFOLLOW)) >= 0);
+        assert_se(dir_fd_is_root(fd) > 0);
+
+        fd = safe_close(fd);
+
+        assert_se((fd = open("/usr", O_CLOEXEC|O_PATH|O_DIRECTORY|O_NOFOLLOW)) >= 0);
+        assert_se(dir_fd_is_root(fd) == 0);
+}
+
 DEFINE_TEST_MAIN(LOG_DEBUG);