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)
}
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
*
* 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
* 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. */
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;
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);