diff --git a/src/basic/chase.c b/src/basic/chase.c index 8030c74079..2243b12972 100644 --- a/src/basic/chase.c +++ b/src/basic/chase.c @@ -379,11 +379,20 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int * root tree we operate in is allowed, where an inode is moved up the tree while we * look at it, and thus get the current path wrong and think we are deeper down than * we actually are. */ - if (FLAGS_SET(flags, CHASE_AT_RESOLVE_IN_ROOT) && - (empty_or_root(done) || (statx_inode_same(&stx, &root_stx) && statx_mount_same(&stx, &root_stx)))) { - if (FLAGS_SET(flags, CHASE_STEP)) - goto chased_one; - continue; + if (FLAGS_SET(flags, CHASE_AT_RESOLVE_IN_ROOT)) { + bool is_root = empty_or_root(done); + if (!is_root && statx_inode_same(&stx, &root_stx)) { + r = statx_mount_same(&stx, &root_stx); + if (r < 0) + return r; + + is_root = r > 0; + } + if (is_root) { + if (FLAGS_SET(flags, CHASE_STEP)) + goto chased_one; + continue; + } } fd_parent = openat(fd, "..", O_CLOEXEC|O_NOFOLLOW|O_PATH|O_DIRECTORY); diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index e032bb62ac..6fb4e78c2f 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -1094,7 +1094,11 @@ int fds_inode_and_mount_same(int fd1, int fd2) { if (r < 0) return r; - return statx_inode_same(&sx1, &sx2) && statx_mount_same(&sx1, &sx2); + r = statx_mount_same(&sx1, &sx2); + if (r <= 0) + return r; + + return statx_inode_same(&sx1, &sx2); } int resolve_xat_fdroot(int *fd, const char **path, char **ret_buffer) { diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index c5702ca6ab..7f065b5c7f 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -675,14 +675,15 @@ bool statx_inode_same(const struct statx *a, const struct statx *b) { a->stx_ino == b->stx_ino; } -bool statx_mount_same(const struct statx *a, const struct statx *b) { +int statx_mount_same(const struct statx *a, const struct statx *b) { if (!statx_is_set(a) || !statx_is_set(b)) return false; - assert(FLAGS_SET(a->stx_mask, STATX_MNT_ID)); - assert(FLAGS_SET(b->stx_mask, STATX_MNT_ID)); + if ((FLAGS_SET(a->stx_mask, STATX_MNT_ID) && FLAGS_SET(b->stx_mask, STATX_MNT_ID)) || + (FLAGS_SET(a->stx_mask, STATX_MNT_ID_UNIQUE) && FLAGS_SET(b->stx_mask, STATX_MNT_ID_UNIQUE))) + return a->stx_mnt_id == b->stx_mnt_id; - return a->stx_mnt_id == b->stx_mnt_id; + return -ENODATA; } usec_t statx_timestamp_load(const struct statx_timestamp *ts) { diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h index 2336c775e9..230535aad4 100644 --- a/src/basic/stat-util.h +++ b/src/basic/stat-util.h @@ -110,7 +110,7 @@ bool stat_inode_same(const struct stat *a, const struct stat *b); bool stat_inode_unmodified(const struct stat *a, const struct stat *b); bool statx_inode_same(const struct statx *a, const struct statx *b); -bool statx_mount_same(const struct statx *a, const struct statx *b); +int statx_mount_same(const struct statx *a, const struct statx *b); int xstatfsat(int dir_fd, const char *path, struct statfs *ret); diff --git a/src/mountfsd/mountwork.c b/src/mountfsd/mountwork.c index 2e75ac532f..54d92b5585 100644 --- a/src/mountfsd/mountwork.c +++ b/src/mountfsd/mountwork.c @@ -947,7 +947,10 @@ static DirectoryOwnership validate_directory_fd( return DIRECTORY_IS_OTHERWISE_OWNED; } - if (!statx_mount_same(&stx, &new_stx)) { + r = statx_mount_same(&stx, &new_stx); + if (r < 0) + return log_debug_errno(r, "Failed to compare mount IDs: %m"); + if (!r) { /* NB, this check is probably redundant, given we also check * STATX_ATTR_MOUNT_ROOT. The only reason we have it here is to provide extra safety * in case the mount tree is rearranged concurrently with our traversal, so that