]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
fd-util: wire up XAT_FDROOT in various really basic fd-util.h calls
authorLennart Poettering <lennart@poettering.net>
Mon, 8 Dec 2025 12:30:41 +0000 (13:30 +0100)
committerLennart Poettering <lennart@poettering.net>
Sat, 17 Jan 2026 11:37:39 +0000 (12:37 +0100)
src/basic/fd-util.c
src/basic/fd-util.h

index e08ad7d32426adeb121f06182973414ea1f7270b..f80ce0ae470e12e8f423c041f26c037ef48450ca 100644 (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));
+
+        const char *fn1;
+        if (fd1 == XAT_FDROOT) {
+                fd1 = AT_FDCWD;
+                fn1 = "/";
+        } else
+                fn1 = "";
 
-        if (statx(fd1, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sx1) < 0)
+        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);
index 2663ff8aac3ca09c234360b47edcb04f2106b43c..707445d73d89c4d37213b43d896e5301c4c4bf93 100644 (file)
@@ -173,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);