Merge pull request #26820 from DaanDeMeyer/dir-fd-is-root

fd-util: Add dir_fd_is_root()
This commit is contained in:
Daan De Meyer
2023-03-15 12:48:14 +01:00
committed by GitHub
5 changed files with 82 additions and 5 deletions

View 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);
}

View 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))

View File

@@ -355,11 +355,19 @@ int path_is_mount_point(const char *t, const char *root, int flags) {
return fd_is_mount_point(fd, last_path_component(t), flags);
}
int path_get_mnt_id(const char *path, int *ret) {
int path_get_mnt_id_at(int dir_fd, const char *path, int *ret) {
STRUCT_NEW_STATX_DEFINE(buf);
int r;
if (statx(AT_FDCWD, path, AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT, STATX_MNT_ID, &buf.sx) < 0) {
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(path);
assert(ret);
if (statx(dir_fd,
path,
AT_NO_AUTOMOUNT|(isempty(path) ? AT_EMPTY_PATH : AT_SYMLINK_NOFOLLOW),
STATX_MNT_ID,
&buf.sx) < 0) {
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
return -errno;
@@ -371,11 +379,11 @@ int path_get_mnt_id(const char *path, int *ret) {
return 0;
}
r = name_to_handle_at_loop(AT_FDCWD, path, NULL, ret, 0);
r = name_to_handle_at_loop(dir_fd, path, NULL, ret, isempty(path) ? AT_EMPTY_PATH : 0);
if (r == 0 || is_name_to_handle_at_fatal_error(r))
return r;
return fd_fdinfo_mnt_id(AT_FDCWD, path, 0, ret);
return fd_fdinfo_mnt_id(dir_fd, path, isempty(path) ? AT_EMPTY_PATH : 0, ret);
}
bool fstype_is_network(const char *fstype) {

View File

@@ -37,7 +37,10 @@
int name_to_handle_at_loop(int fd, const char *path, struct file_handle **ret_handle, int *ret_mnt_id, int flags);
int path_get_mnt_id(const char *path, int *ret);
int path_get_mnt_id_at(int dir_fd, const char *path, int *ret);
static inline int path_get_mnt_id(const char *path, int *ret) {
return path_get_mnt_id_at(AT_FDCWD, path, ret);
}
int fd_is_mount_point(int fd, const char *filename, int flags);
int path_is_mount_point(const char *path, const char *root, int flags);

View 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);