fd-util: introduce XAT_FDROOT fd shortcut for the root dir (#39244)

This commit is contained in:
Daan De Meyer
2026-01-18 11:57:11 +01:00
committed by GitHub
10 changed files with 306 additions and 63 deletions
+50 -6
View File
@@ -128,6 +128,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
unsigned max_follow = CHASE_MAX; /* how many symlinks to follow before giving up and returning ELOOP */
bool exists = true, append_trail_slash = false;
struct stat st; /* stat obtained from fd */
bool need_absolute = false; /* allocate early to avoid compiler warnings around goto */
const char *todo;
int r;
@@ -135,7 +136,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
assert(!FLAGS_SET(flags, CHASE_MUST_BE_DIRECTORY|CHASE_MUST_BE_REGULAR));
assert(!FLAGS_SET(flags, CHASE_STEP|CHASE_EXTRACT_FILENAME));
assert(!FLAGS_SET(flags, CHASE_NO_AUTOFS|CHASE_TRIGGER_AUTOFS));
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(dir_fd >= 0 || IN_SET(dir_fd, AT_FDCWD, XAT_FDROOT));
if (FLAGS_SET(flags, CHASE_STEP))
assert(!ret_fd);
@@ -218,7 +219,45 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
* the mount point is emitted. CHASE_WARN cannot be used in PID 1.
*/
if (FLAGS_SET(flags, CHASE_AT_RESOLVE_IN_ROOT)) {
_cleanup_close_ int _dir_fd = -EBADF;
r = dir_fd_is_root(dir_fd);
if (r < 0)
return r;
if (r > 0) {
/* Shortcut the common case where no root dir is specified, and no special flags are given to
* a regular open() */
if (!ret_path &&
(flags & (CHASE_STEP|CHASE_NO_AUTOFS|CHASE_NONEXISTENT|CHASE_SAFE|CHASE_WARN|CHASE_PROHIBIT_SYMLINKS|CHASE_PARENT|CHASE_MKDIR_0755)) == 0) {
_cleanup_free_ char *slash_path = NULL;
if (!path_is_absolute(path)) {
slash_path = strjoin("/", path);
if (!slash_path)
return -ENOMEM;
}
/* We use open_tree() rather than regular open() here, because it gives us direct
* control over automount behaviour, and otherwise is equivalent to open() with
* O_PATH */
fd = open_tree(-EBADF, slash_path ?: path, OPEN_TREE_CLOEXEC|(FLAGS_SET(flags, CHASE_TRIGGER_AUTOFS) ? 0 : AT_NO_AUTOMOUNT));
if (fd < 0)
return -errno;
if (fstat(fd, &st) < 0)
return -errno;
exists = true;
goto success;
}
_dir_fd = open("/", O_DIRECTORY|O_RDONLY|O_CLOEXEC);
if (_dir_fd < 0)
return -errno;
dir_fd = _dir_fd;
flags &= ~CHASE_AT_RESOLVE_IN_ROOT;
} else if (FLAGS_SET(flags, CHASE_AT_RESOLVE_IN_ROOT)) {
/* If we get AT_FDCWD or dir_fd points to "/", then we always resolve symlinks relative to
* the host's root. Hence, CHASE_AT_RESOLVE_IN_ROOT is meaningless. */
@@ -251,7 +290,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
if (r < 0)
return r;
bool need_absolute = r;
need_absolute = r;
if (need_absolute) {
done = strdup("/");
if (!done)
@@ -514,6 +553,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
close_and_replace(fd, child);
}
success:
if (exists) {
if (FLAGS_SET(flags, CHASE_MUST_BE_DIRECTORY)) {
r = stat_verify_directory(&st);
@@ -656,9 +696,13 @@ int chase(const char *path, const char *root, ChaseFlags flags, char **ret_path,
"Specified path '%s' is outside of specified root directory '%s', refusing to resolve.",
absolute, root);
fd = open(root, O_CLOEXEC|O_DIRECTORY|O_PATH);
if (fd < 0)
return -errno;
if (empty_or_root(root))
fd = XAT_FDROOT;
else {
fd = open(root, O_CLOEXEC|O_DIRECTORY|O_PATH);
if (fd < 0)
return -errno;
}
r = chaseat(fd, path, flags & ~CHASE_PREFIX_ROOT, ret_path ? &p : NULL, ret_fd ? &pfd : NULL);
if (r < 0)
+48 -10
View File
@@ -572,10 +572,12 @@ bool fdname_is_valid(const char *s) {
int fd_get_path(int fd, char **ret) {
int r;
assert(fd >= 0 || fd == AT_FDCWD);
assert(fd >= 0 || IN_SET(fd, AT_FDCWD, XAT_FDROOT));
if (fd == AT_FDCWD)
return safe_getcwd(ret);
if (fd == XAT_FDROOT)
return strdup_to(ret, "/");
r = readlink_malloc(FORMAT_PROC_FD_PATH(fd), ret);
if (r == -ENOENT)
@@ -770,7 +772,7 @@ finish:
}
int fd_reopen(int fd, int flags) {
assert(fd >= 0 || fd == AT_FDCWD);
assert(fd >= 0 || IN_SET(fd, AT_FDCWD, XAT_FDROOT));
assert(!FLAGS_SET(flags, O_CREAT));
/* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to
@@ -782,6 +784,8 @@ int fd_reopen(int fd, int flags) {
*
* If AT_FDCWD is specified as file descriptor gets an fd to the current cwd.
*
* If XAT_FDROOT is specified as fd get an fd to the root directory.
*
* If the specified file descriptor refers to a symlink via O_PATH, then this function cannot be used
* to follow that symlink. Because we cannot have non-O_PATH fds to symlinks reopening it without
* O_PATH will always result in -ELOOP. Or in other words: if you have an O_PATH fd to a symlink you
@@ -795,6 +799,9 @@ int fd_reopen(int fd, int flags) {
* the same way as the non-O_DIRECTORY case. */
return -ELOOP;
if (fd == XAT_FDROOT)
return RET_NERRNO(open("/", flags | O_DIRECTORY));
if (FLAGS_SET(flags, O_DIRECTORY) || fd == AT_FDCWD)
/* If we shall reopen the fd as directory we can just go via "." and thus bypass the whole
* magic /proc/ directory, and make ourselves independent of that being mounted. */
@@ -1025,19 +1032,36 @@ int fd_get_diskseq(int fd, uint64_t *ret) {
return 0;
}
static bool is_literal_root(const char *p) {
if (!p)
return false;
/* Check if string consists of at least one '/', and possibly more, but nothing else */
size_t n = strspn(p, "/");
return n >= 1 && p[n] == 0;
}
int path_is_root_at(int dir_fd, const char *path) {
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(dir_fd >= 0 || IN_SET(dir_fd, AT_FDCWD, XAT_FDROOT));
if (dir_fd == XAT_FDROOT && isempty(path))
return true;
if (IN_SET(dir_fd, XAT_FDROOT, AT_FDCWD) && is_literal_root(path))
return true;
_cleanup_close_ int fd = -EBADF;
if (!isempty(path)) {
fd = openat(dir_fd, path, O_PATH|O_DIRECTORY|O_CLOEXEC);
fd = xopenat(dir_fd, path, O_PATH|O_DIRECTORY|O_CLOEXEC);
if (fd == -ENOTDIR)
return false; /* the root dir must be a dir */
if (fd < 0)
return errno == ENOTDIR ? false : -errno;
return fd;
dir_fd = fd;
}
_cleanup_close_ int root_fd = openat(AT_FDCWD, "/", O_PATH|O_DIRECTORY|O_CLOEXEC);
_cleanup_close_ int root_fd = open("/", O_PATH|O_DIRECTORY|O_CLOEXEC);
if (root_fd < 0)
return -errno;
@@ -1055,13 +1079,27 @@ int path_is_root_at(int dir_fd, const char *path) {
int fds_are_same_mount(int fd1, int fd2) {
struct statx sx1 = {}, sx2 = {}; /* explicitly initialize the struct to make msan silent. */
assert(fd1 >= 0);
assert(fd2 >= 0);
assert(fd1 >= 0 || IN_SET(fd1, AT_FDCWD, XAT_FDROOT));
assert(fd2 >= 0 || IN_SET(fd2, AT_FDCWD, XAT_FDROOT));
if (statx(fd1, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sx1) < 0)
const char *fn1;
if (fd1 == XAT_FDROOT) {
fd1 = AT_FDCWD;
fn1 = "/";
} else
fn1 = "";
if (statx(fd1, fn1, AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sx1) < 0)
return -errno;
if (statx(fd2, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sx2) < 0)
const char *fn2;
if (fd2 == XAT_FDROOT) {
fd2 = AT_FDCWD;
fn2 = "/";
} else
fn2 = "";
if (statx(fd2, fn2, AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sx2) < 0)
return -errno;
return statx_inode_same(&sx1, &sx2) && statx_mount_same(&sx1, &sx2);
+9 -2
View File
@@ -57,6 +57,13 @@
#define NR_OPEN_MINIMUM ((unsigned) (sizeof(long) * 8))
#define NR_OPEN_MAXIMUM ((unsigned) (CONST_MIN((size_t) INT_MAX, SIZE_MAX / __SIZEOF_POINTER__) & ~(sizeof(long) * 8 - 1)))
/* A special fd that can be passed in various helpers instead of an fd indicating the root dir. Inspired by,
* and an alternative to AT_FDCWD. We use specific negative value that is outside of the negative errno
* range, to avoid any potential ambiguities. */
#define XAT_FDROOT -8192
assert_cc(XAT_FDROOT != AT_FDCWD);
assert_cc(XAT_FDROOT < -ERRNO_MAX);
int close_nointr(int fd);
int safe_close(int fd);
void safe_close_pair(int p[static 2]);
@@ -166,10 +173,10 @@ static inline int path_is_root(const char *path) {
return path_is_root_at(AT_FDCWD, path);
}
static inline int dir_fd_is_root(int dir_fd) {
return path_is_root_at(dir_fd, NULL);
return dir_fd == XAT_FDROOT ? true : path_is_root_at(dir_fd, NULL);
}
static inline int dir_fd_is_root_or_cwd(int dir_fd) {
return dir_fd == AT_FDCWD ? true : path_is_root_at(dir_fd, NULL);
return IN_SET(dir_fd, AT_FDCWD, XAT_FDROOT) ? true : path_is_root_at(dir_fd, NULL);
}
int fds_are_same_mount(int fd1, int fd2);
+23 -3
View File
@@ -713,10 +713,15 @@ char* unlink_and_free(char *p) {
}
int access_fd(int fd, int mode) {
assert(fd >= 0);
/* Like access() but operates on an already open fd */
if (fd == AT_FDCWD)
return RET_NERRNO(access(".", mode));
if (fd == XAT_FDROOT)
return RET_NERRNO(access("/", mode));
assert(fd >= 0);
if (faccessat(fd, "", mode, AT_EMPTY_PATH) >= 0)
return 0;
if (errno != EINVAL)
@@ -1160,7 +1165,7 @@ int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_
bool made_dir = false, made_file = false;
int r;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(dir_fd >= 0 || IN_SET(dir_fd, AT_FDCWD, XAT_FDROOT));
/* An inode cannot be both a directory and a regular file at the same time. */
assert(!(FLAGS_SET(open_flags, O_DIRECTORY) && FLAGS_SET(xopen_flags, XO_REGULAR)));
@@ -1179,8 +1184,23 @@ int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_
* • if XO_REGULAR is specified will return an error if inode is not a regular file.
*
* • If mode is specified as MODE_INVALID, we'll use 0755 for dirs, and 0644 for regular files.
*
* • The dir fd can be passed as XAT_FDROOT, in which case any relative paths will be taken relative to the root fs.
*/
_cleanup_close_ int _dir_fd = -EBADF;
if (dir_fd == XAT_FDROOT) {
if (path_is_absolute(path))
dir_fd = AT_FDCWD;
else {
_dir_fd = open("/", O_CLOEXEC|O_DIRECTORY|O_RDONLY);
if (_dir_fd < 0)
return -errno;
dir_fd = _dir_fd;
}
}
if (mode == MODE_INVALID)
mode = (open_flags & O_DIRECTORY) ? 0755 : 0644;
+58 -22
View File
@@ -31,10 +31,25 @@ static int verify_stat_at(
struct stat st;
int r;
assert(fd >= 0 || fd == AT_FDCWD);
assert(fd >= 0 || IN_SET(fd, AT_FDCWD, XAT_FDROOT));
assert(!isempty(path) || !follow);
assert(verify_func);
_cleanup_free_ char *p = NULL;
if (fd == XAT_FDROOT) {
fd = AT_FDCWD;
if (isempty(path))
path = "/";
else if (!path_is_absolute(path)) {
p = strjoin("/", path);
if (!p)
return -ENOMEM;
path = p;
}
}
if (fstatat(fd, strempty(path), &st,
(isempty(path) ? AT_EMPTY_PATH : 0) | (follow ? 0 : AT_SYMLINK_NOFOLLOW)) < 0)
return -errno;
@@ -66,8 +81,10 @@ int verify_regular_at(int fd, const char *path, bool follow) {
}
int fd_verify_regular(int fd) {
assert(fd >= 0);
return verify_regular_at(fd, NULL, false);
if (IN_SET(fd, AT_FDCWD, XAT_FDROOT))
return -EISDIR;
return verify_regular_at(fd, /* path= */ NULL, /* follow= */ false);
}
int stat_verify_directory(const struct stat *st) {
@@ -83,7 +100,9 @@ int stat_verify_directory(const struct stat *st) {
}
int fd_verify_directory(int fd) {
assert(fd >= 0);
if (IN_SET(fd, AT_FDCWD, XAT_FDROOT))
return 0;
return verify_stat_at(fd, NULL, false, stat_verify_directory, true);
}
@@ -127,7 +146,10 @@ int stat_verify_linked(const struct stat *st) {
}
int fd_verify_linked(int fd) {
assert(fd >= 0);
if (fd == XAT_FDROOT)
return 0;
return verify_stat_at(fd, NULL, false, stat_verify_linked, true);
}
@@ -223,23 +245,33 @@ int null_or_empty_path_with_root(const char *fn, const char *root) {
return null_or_empty(&st);
}
int fd_is_read_only_fs(int fd) {
struct statfs st;
static int xfstatfs(int fd, struct statfs *ret) {
assert(ret);
if (fd == AT_FDCWD)
return RET_NERRNO(statfs(".", ret));
if (fd == XAT_FDROOT)
return RET_NERRNO(statfs("/", ret));
assert(fd >= 0);
return RET_NERRNO(fstatfs(fd, ret));
}
if (fstatfs(fd, &st) < 0)
return -errno;
int fd_is_read_only_fs(int fd) {
int r;
struct statfs st;
r = xfstatfs(fd, &st);
if (r < 0)
return r;
if (st.f_flags & ST_RDONLY)
return true;
if (is_network_fs(&st)) {
if (is_network_fs(&st))
/* On NFS, fstatfs() might not reflect whether we can actually write to the remote share.
* Let's try again with access(W_OK) which is more reliable, at least sometimes. */
if (access_fd(fd, W_OK) == -EROFS)
return true;
}
return access_fd(fd, W_OK) == -EROFS;
return false;
}
@@ -364,9 +396,9 @@ bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
}
int is_fs_type_at(int dir_fd, const char *path, statfs_f_type_t magic_value) {
struct statfs s;
int r;
struct statfs s;
r = xstatfsat(dir_fd, path, &s);
if (r < 0)
return r;
@@ -383,19 +415,23 @@ bool is_network_fs(const struct statfs *s) {
}
int fd_is_temporary_fs(int fd) {
struct statfs s;
int r;
if (fstatfs(fd, &s) < 0)
return -errno;
struct statfs s;
r = xfstatfs(fd, &s);
if (r < 0)
return r;
return is_temporary_fs(&s);
}
int fd_is_network_fs(int fd) {
struct statfs s;
int r;
if (fstatfs(fd, &s) < 0)
return -errno;
struct statfs s;
r = xfstatfs(fd, &s);
if (r < 0)
return r;
return is_network_fs(&s);
}
@@ -489,7 +525,7 @@ bool statx_mount_same(const struct statx *a, const struct statx *b) {
int xstatfsat(int dir_fd, const char *path, struct statfs *ret) {
_cleanup_close_ int fd = -EBADF;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(dir_fd >= 0 || IN_SET(dir_fd, AT_FDCWD, XAT_FDROOT));
assert(ret);
if (!isempty(path)) {
@@ -499,7 +535,7 @@ int xstatfsat(int dir_fd, const char *path, struct statfs *ret) {
dir_fd = fd;
}
return RET_NERRNO(fstatfs(dir_fd, ret));
return RET_NERRNO(xfstatfs(dir_fd, ret));
}
usec_t statx_timestamp_load(const struct statx_timestamp *ts) {
+1 -1
View File
@@ -140,7 +140,7 @@ int id128_read_fd(int fd, Id128Flag f, sd_id128_t *ret) {
int id128_read_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t *ret) {
_cleanup_close_ int fd = -EBADF;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(dir_fd >= 0 || IN_SET(dir_fd, AT_FDCWD, XAT_FDROOT));
assert(path);
fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+19 -11
View File
@@ -332,7 +332,7 @@ static int verify_esp(
dev_t devid = 0;
int r;
assert(rfd >= 0 || rfd == AT_FDCWD);
assert(rfd >= 0 || IN_SET(rfd, AT_FDCWD, XAT_FDROOT));
assert(path);
/* This logs about all errors, except:
@@ -442,14 +442,14 @@ int find_esp_and_warn_at(
VerifyESPFlags flags;
int r;
assert(rfd >= 0 || IN_SET(rfd, AT_FDCWD, XAT_FDROOT));
/* This logs about all errors except:
*
* -ENOKEY → when we can't find the partition
* -EACCESS → when unprivileged_mode is true, and we can't access something
*/
assert(rfd >= 0 || rfd == AT_FDCWD);
flags = verify_esp_flags_init(unprivileged_mode, "SYSTEMD_RELAX_ESP_CHECKS");
if (path)
@@ -527,9 +527,13 @@ int find_esp_and_warn(
dev_t devid;
int r;
rfd = open(empty_to_root(root), O_PATH|O_DIRECTORY|O_CLOEXEC);
if (rfd < 0)
return -errno;
if (empty_or_root(root))
rfd = XAT_FDROOT;
else {
rfd = open(root, O_PATH|O_DIRECTORY|O_CLOEXEC);
if (rfd < 0)
return -errno;
}
r = find_esp_and_warn_at(
rfd,
@@ -737,7 +741,7 @@ static int verify_xbootldr(
dev_t devid = 0;
int r;
assert(rfd >= 0 || rfd == AT_FDCWD);
assert(rfd >= 0 || IN_SET(rfd, AT_FDCWD, XAT_FDROOT));
assert(path);
r = chaseat(rfd, path, CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT|CHASE_TRIGGER_AUTOFS, &p, &pfd);
@@ -800,7 +804,7 @@ int find_xbootldr_and_warn_at(
/* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */
assert(rfd >= 0 || rfd == AT_FDCWD);
assert(rfd >= 0 || IN_SET(rfd, AT_FDCWD, XAT_FDROOT));
flags = verify_esp_flags_init(unprivileged_mode, "SYSTEMD_RELAX_XBOOTLDR_CHECKS");
@@ -862,9 +866,13 @@ int find_xbootldr_and_warn(
dev_t devid;
int r;
rfd = open(empty_to_root(root), O_PATH|O_DIRECTORY|O_CLOEXEC);
if (rfd < 0)
return -errno;
if (empty_or_root(root))
rfd = XAT_FDROOT;
else {
rfd = open(root, O_PATH|O_DIRECTORY|O_CLOEXEC);
if (rfd < 0)
return -errno;
}
r = find_xbootldr_and_warn_at(
rfd,
+4 -3
View File
@@ -3,6 +3,7 @@
#include "alloc-util.h"
#include "env-file.h"
#include "fd-util.h"
#include "fs-util.h"
#include "kernel-image.h"
#include "log.h"
#include "pe-binary.h"
@@ -134,12 +135,12 @@ int inspect_kernel(
_cleanup_close_ int fd = -EBADF;
int r;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(dir_fd >= 0 || IN_SET(dir_fd, AT_FDCWD, XAT_FDROOT));
assert(filename);
fd = openat(dir_fd, filename, O_RDONLY|O_CLOEXEC);
fd = xopenat(dir_fd, filename, O_RDONLY|O_CLOEXEC);
if (fd < 0)
return log_error_errno(errno, "Failed to open kernel image file '%s': %m", filename);
return log_error_errno(fd, "Failed to open kernel image file '%s': %m", filename);
r = pe_load_headers(fd, &dos_header, &pe_header);
if (r == -EBADMSG) /* not a valid PE file */
+30
View File
@@ -499,6 +499,36 @@ TEST(chaseat) {
fd = safe_close(fd);
/* Same but with XAT_FDROOT */
_cleanup_close_ int found_fd1 = -EBADF;
ASSERT_OK(chaseat(XAT_FDROOT, p, 0, &result, &found_fd1));
ASSERT_STREQ(result, "/usr");
result = mfree(result);
_cleanup_close_ int found_fd2 = -EBADF;
ASSERT_OK(chaseat(XAT_FDROOT, p, CHASE_AT_RESOLVE_IN_ROOT, &result, &found_fd2));
ASSERT_STREQ(result, "/usr");
result = mfree(result);
assert(fd_inode_same(found_fd1, found_fd2) > 0);
/* Do the same XAT_FDROOT tests again, this time without querying the path, so that the open_tree()
* shortcut can work */
_cleanup_close_ int found_fd3 = -EBADF;
ASSERT_OK(chaseat(XAT_FDROOT, p, 0, NULL, &found_fd3));
assert(fd_inode_same(found_fd1, found_fd3) > 0);
assert(fd_inode_same(found_fd2, found_fd3) > 0);
_cleanup_close_ int found_fd4 = -EBADF;
ASSERT_OK(chaseat(XAT_FDROOT, p, CHASE_AT_RESOLVE_IN_ROOT, NULL, &found_fd4));
assert(fd_inode_same(found_fd1, found_fd4) > 0);
assert(fd_inode_same(found_fd2, found_fd4) > 0);
assert(fd_inode_same(found_fd3, found_fd4) > 0);
found_fd1 = safe_close(found_fd1);
found_fd2 = safe_close(found_fd2);
found_fd3 = safe_close(found_fd3);
found_fd4 = safe_close(found_fd4);
/* If the file descriptor does not point to the root directory, the result will be relative
* unless the result is outside of the specified file descriptor. */
+64 -5
View File
@@ -819,11 +819,6 @@ TEST(linkat_replace) {
assert_se(inode_same_at(fd1, NULL, fd2_check, NULL, AT_EMPTY_PATH) > 0);
}
static int intro(void) {
arg_test_dir = saved_argv[1];
return EXIT_SUCCESS;
}
TEST(readlinkat_malloc) {
_cleanup_(rm_rf_physical_and_freep) char *t = NULL;
_cleanup_close_ int tfd = -EBADF, fd = -EBADF;
@@ -859,4 +854,68 @@ TEST(readlinkat_malloc) {
q = mfree(q);
}
TEST(xat_fdroot) {
_cleanup_free_ char *p = NULL;
ASSERT_OK(fd_get_path(XAT_FDROOT, &p));
ASSERT_STREQ(p, "/");
_cleanup_close_ int fd = -EBADF;
fd = fd_reopen(XAT_FDROOT, O_CLOEXEC);
ASSERT_OK(fd);
ASSERT_OK_POSITIVE(path_is_root_at(XAT_FDROOT, NULL));
ASSERT_OK_POSITIVE(path_is_root_at(XAT_FDROOT, "."));
ASSERT_OK_POSITIVE(path_is_root_at(XAT_FDROOT, "/"));
ASSERT_OK_POSITIVE(path_is_root_at(fd, NULL));
ASSERT_OK_POSITIVE(path_is_root_at(fd, "."));
ASSERT_OK_POSITIVE(path_is_root_at(fd, "/"));
ASSERT_OK_POSITIVE(fds_are_same_mount(fd, fd));
ASSERT_OK_POSITIVE(fds_are_same_mount(XAT_FDROOT, XAT_FDROOT));
ASSERT_OK_POSITIVE(fds_are_same_mount(fd, XAT_FDROOT));
ASSERT_OK_POSITIVE(fds_are_same_mount(XAT_FDROOT, fd));
ASSERT_OK_POSITIVE(dir_fd_is_root(XAT_FDROOT));
ASSERT_OK_POSITIVE(dir_fd_is_root(fd));
ASSERT_OK_POSITIVE(dir_fd_is_root_or_cwd(XAT_FDROOT));
ASSERT_OK_POSITIVE(dir_fd_is_root_or_cwd(AT_FDCWD));
ASSERT_OK_POSITIVE(dir_fd_is_root_or_cwd(fd));
ASSERT_OK(access_fd(XAT_FDROOT, F_OK));
ASSERT_OK(access_fd(fd, F_OK));
fd = safe_close(fd);
fd = xopenat(XAT_FDROOT, ".", O_RDONLY);
ASSERT_OK(fd);
fd = safe_close(fd);
fd = xopenat(XAT_FDROOT, "/", O_RDONLY);
ASSERT_OK(fd);
ASSERT_OK(fd_verify_directory(fd));
ASSERT_OK(fd_verify_directory(XAT_FDROOT));
ASSERT_OK(fd_verify_linked(fd));
ASSERT_OK(fd_verify_linked(XAT_FDROOT));
int a = fd_is_read_only_fs(fd);
ASSERT_OK(a);
int b = fd_is_read_only_fs(XAT_FDROOT);
ASSERT_OK(b);
ASSERT_EQ(a, b);
a = fd_is_temporary_fs(fd);
ASSERT_OK(a);
b = fd_is_temporary_fs(XAT_FDROOT);
ASSERT_OK(b);
ASSERT_EQ(a, b);
}
static int intro(void) {
arg_test_dir = saved_argv[1];
return EXIT_SUCCESS;
}
DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro);