diff --git a/src/basic/chase.c b/src/basic/chase.c index 0199f5dd69..f360cc26a3 100644 --- a/src/basic/chase.c +++ b/src/basic/chase.c @@ -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) diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index e08ad7d324..f80ce0ae47 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -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); diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h index bb9669a84e..707445d73d 100644 --- a/src/basic/fd-util.h +++ b/src/basic/fd-util.h @@ -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); diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index d668416ff8..d3712622f1 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -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; diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index bb20ba50de..d1c5d7aa37 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -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) { diff --git a/src/libsystemd/sd-id128/id128-util.c b/src/libsystemd/sd-id128/id128-util.c index 86a2938af4..0cae92b728 100644 --- a/src/libsystemd/sd-id128/id128-util.c +++ b/src/libsystemd/sd-id128/id128-util.c @@ -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); diff --git a/src/shared/find-esp.c b/src/shared/find-esp.c index 8e1737eebb..cf4eddbbfa 100644 --- a/src/shared/find-esp.c +++ b/src/shared/find-esp.c @@ -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, diff --git a/src/shared/kernel-image.c b/src/shared/kernel-image.c index ed8fde1fb2..d3e18d19f4 100644 --- a/src/shared/kernel-image.c +++ b/src/shared/kernel-image.c @@ -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 */ diff --git a/src/test/test-chase.c b/src/test/test-chase.c index 89705b4ffd..129ea19b72 100644 --- a/src/test/test-chase.c +++ b/src/test/test-chase.c @@ -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. */ diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index 51b7b146fe..a56f8f92ad 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -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);