stat-util: write up XAT_FDROOT in stat-util.[ch]

This commit is contained in:
Lennart Poettering
2026-01-17 12:37:39 +01:00
parent c67abc9c43
commit 95abe4beff
+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) {