mirror of
https://github.com/systemd/systemd.git
synced 2025-02-15 09:57:39 +03:00
Merge pull request #28487 from yuwata/statx-fixlets
util: fix error handling of statx()
This commit is contained in:
commit
73d6f20155
@ -929,17 +929,18 @@ int path_is_root_at(int dir_fd, const char *path) {
|
||||
* $ mount --bind /tmp/x /tmp/x/y
|
||||
*
|
||||
* Note, statx() does not provide the mount ID and path_get_mnt_id_at() does not work when an old
|
||||
* kernel is used without /proc mounted. In that case, let's assume that we do not have such spurious
|
||||
* mount points in an early boot stage, and silently skip the following check. */
|
||||
* kernel is used. In that case, let's assume that we do not have such spurious mount points in an
|
||||
* early boot stage, and silently skip the following check. */
|
||||
|
||||
if (!FLAGS_SET(st.nsx.stx_mask, STATX_MNT_ID)) {
|
||||
int mntid;
|
||||
|
||||
r = path_get_mnt_id_at(dir_fd, "", &mntid);
|
||||
if (r == -ENOSYS)
|
||||
return true; /* skip the mount ID check */
|
||||
if (r < 0)
|
||||
r = path_get_mnt_id_at_fallback(dir_fd, "", &mntid);
|
||||
if (r < 0) {
|
||||
if (ERRNO_IS_NOT_SUPPORTED(r))
|
||||
return true; /* skip the mount ID check */
|
||||
return r;
|
||||
}
|
||||
assert(mntid >= 0);
|
||||
|
||||
st.nsx.stx_mnt_id = mntid;
|
||||
@ -949,11 +950,12 @@ int path_is_root_at(int dir_fd, const char *path) {
|
||||
if (!FLAGS_SET(pst.nsx.stx_mask, STATX_MNT_ID)) {
|
||||
int mntid;
|
||||
|
||||
r = path_get_mnt_id_at(dir_fd, "..", &mntid);
|
||||
if (r == -ENOSYS)
|
||||
return true; /* skip the mount ID check */
|
||||
if (r < 0)
|
||||
r = path_get_mnt_id_at_fallback(dir_fd, "..", &mntid);
|
||||
if (r < 0) {
|
||||
if (ERRNO_IS_NOT_SUPPORTED(r))
|
||||
return true; /* skip the mount ID check */
|
||||
return r;
|
||||
}
|
||||
assert(mntid >= 0);
|
||||
|
||||
pst.nsx.stx_mnt_id = mntid;
|
||||
|
@ -223,7 +223,9 @@ int fd_is_mount_point(int fd, const char *filename, int flags) {
|
||||
AT_STATX_DONT_SYNC, /* don't go to the network for this – for similar reasons */
|
||||
STATX_TYPE,
|
||||
&sx) < 0) {
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(errno) && /* statx() is not supported by the kernel. */
|
||||
!ERRNO_IS_PRIVILEGE(errno) && /* maybe filtered by seccomp. */
|
||||
errno != EINVAL) /* glibc's fallback method returns EINVAL when AT_STATX_DONT_SYNC is set. */
|
||||
return -errno;
|
||||
|
||||
/* If statx() is not available or forbidden, fall back to name_to_handle_at() below */
|
||||
@ -357,9 +359,21 @@ int path_is_mount_point(const char *t, const char *root, int flags) {
|
||||
return fd_is_mount_point(fd, last_path_component(t), flags);
|
||||
}
|
||||
|
||||
int path_get_mnt_id_at_fallback(int dir_fd, const char *path, int *ret) {
|
||||
int r;
|
||||
|
||||
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
|
||||
assert(ret);
|
||||
|
||||
r = name_to_handle_at_loop(dir_fd, path, NULL, ret, isempty(path) ? AT_EMPTY_PATH : 0);
|
||||
if (r == 0 || is_name_to_handle_at_fatal_error(r))
|
||||
return r;
|
||||
|
||||
return fd_fdinfo_mnt_id(dir_fd, path, isempty(path) ? AT_EMPTY_PATH : 0, ret);
|
||||
}
|
||||
|
||||
int path_get_mnt_id_at(int dir_fd, const char *path, int *ret) {
|
||||
STRUCT_NEW_STATX_DEFINE(buf);
|
||||
int r;
|
||||
|
||||
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
|
||||
assert(ret);
|
||||
@ -371,7 +385,9 @@ int path_get_mnt_id_at(int dir_fd, const char *path, int *ret) {
|
||||
AT_STATX_DONT_SYNC, /* don't go to the network, mnt_id is a local concept */
|
||||
STATX_MNT_ID,
|
||||
&buf.sx) < 0) {
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(errno) && /* statx() is not supported by the kernel. */
|
||||
!ERRNO_IS_PRIVILEGE(errno) && /* maybe filtered by seccomp. */
|
||||
errno != EINVAL) /* glibc's fallback method returns EINVAL when AT_STATX_DONT_SYNC is set. */
|
||||
return -errno;
|
||||
|
||||
/* Fall back to name_to_handle_at() and then fdinfo if statx is not supported or we lack
|
||||
@ -382,11 +398,7 @@ int path_get_mnt_id_at(int dir_fd, const char *path, int *ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = name_to_handle_at_loop(dir_fd, path, NULL, ret, isempty(path) ? AT_EMPTY_PATH : 0);
|
||||
if (r == 0 || is_name_to_handle_at_fatal_error(r))
|
||||
return r;
|
||||
|
||||
return fd_fdinfo_mnt_id(dir_fd, path, isempty(path) ? AT_EMPTY_PATH : 0, ret);
|
||||
return path_get_mnt_id_at_fallback(dir_fd, path, ret);
|
||||
}
|
||||
|
||||
bool fstype_is_network(const char *fstype) {
|
||||
|
@ -37,6 +37,7 @@
|
||||
|
||||
int name_to_handle_at_loop(int fd, const char *path, struct file_handle **ret_handle, int *ret_mnt_id, int flags);
|
||||
|
||||
int path_get_mnt_id_at_fallback(int dir_fd, const char *path, int *ret);
|
||||
int path_get_mnt_id_at(int dir_fd, const char *path, int *ret);
|
||||
static inline int path_get_mnt_id(const char *path, int *ret) {
|
||||
return path_get_mnt_id_at(AT_FDCWD, path, ret);
|
||||
|
@ -394,21 +394,35 @@ bool statx_mount_same(const struct new_statx *a, const struct new_statx *b) {
|
||||
a->stx_dev_minor == b->stx_dev_minor;
|
||||
}
|
||||
|
||||
static bool is_statx_fatal_error(int err, int flags) {
|
||||
assert(err < 0);
|
||||
|
||||
/* If statx() is not supported or if we see EPERM (which might indicate seccomp filtering or so),
|
||||
* let's do a fallback. Note that on EACCES we'll not fall back, since that is likely an indication of
|
||||
* fs access issues, which we should propagate. */
|
||||
if (ERRNO_IS_NOT_SUPPORTED(err) || err == -EPERM)
|
||||
return false;
|
||||
|
||||
/* When unsupported flags are specified, glibc's fallback function returns -EINVAL.
|
||||
* See statx_generic() in glibc. */
|
||||
if (err != -EINVAL)
|
||||
return true;
|
||||
|
||||
if ((flags & ~(AT_EMPTY_PATH | AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW | AT_STATX_SYNC_AS_STAT)) != 0)
|
||||
return false; /* Unsupported flags are specified. Let's try to use our implementation. */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx) {
|
||||
static bool avoid_statx = false;
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
if (!avoid_statx) {
|
||||
if (statx(dfd, path, flags, mask, sx) < 0) {
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(errno) && errno != EPERM)
|
||||
return -errno;
|
||||
|
||||
/* If statx() is not supported or if we see EPERM (which might indicate seccomp
|
||||
* filtering or so), let's do a fallback. Not that on EACCES we'll not fall back,
|
||||
* since that is likely an indication of fs access issues, which we should
|
||||
* propagate */
|
||||
} else
|
||||
return 0;
|
||||
r = RET_NERRNO(statx(dfd, path, flags, mask, sx));
|
||||
if (r >= 0 || is_statx_fatal_error(r, flags))
|
||||
return r;
|
||||
|
||||
avoid_statx = true;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user