From af423b4ba9c6074020a8ec1dbfe9de10020b1f19 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Wed, 1 Feb 2023 13:14:29 +0100 Subject: [PATCH] fd-util: Add dir_fd_is_root() --- src/basic/fd-util.c | 52 +++++++++++++++++++++++++++++++++++++++++ src/basic/fd-util.h | 2 ++ src/test/test-fd-util.c | 12 ++++++++++ 3 files changed, 66 insertions(+) diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index 430db0c8790..d7296432650 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -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); +} diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h index 76be00e4f4e..a6353cf48e4 100644 --- a/src/basic/fd-util.h +++ b/src/basic/fd-util.h @@ -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/ path needs */ #define PROC_FD_PATH_MAX \ (STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)) diff --git a/src/test/test-fd-util.c b/src/test/test-fd-util.c index 1ca9ef8bbd7..c5f32aa6792 100644 --- a/src/test/test-fd-util.c +++ b/src/test/test-fd-util.c @@ -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); -- 2.47.3