fd-util: introduce XAT_FDROOT fd shortcut for the root dir (#39244)
This commit is contained in:
+50
-6
@@ -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)
|
||||
|
||||
+48
-10
@@ -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);
|
||||
|
||||
+9
-2
@@ -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);
|
||||
|
||||
+23
-3
@@ -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;
|
||||
|
||||
|
||||
+58
-22
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
+19
-11
@@ -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,
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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. */
|
||||
|
||||
|
||||
+64
-5
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user