]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
stat-util: write up XAT_FDROOT in stat-util.[ch]
authorLennart Poettering <lennart@poettering.net>
Mon, 8 Dec 2025 12:32:42 +0000 (13:32 +0100)
committerLennart Poettering <lennart@poettering.net>
Sat, 17 Jan 2026 11:37:39 +0000 (12:37 +0100)
src/basic/stat-util.c

index bb20ba50dee3e6d95565e11926d43f6ac96904c9..d1c5d7aa37b7310dec06728a093c5d6f243bdd64 100644 (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) {