Skip to content

Commit 73d6f20

Browse files
authored
Merge pull request #28487 from yuwata/statx-fixlets
util: fix error handling of statx()
2 parents adb7fe8 + 68a4fc8 commit 73d6f20

File tree

4 files changed

+57
-28
lines changed

4 files changed

+57
-28
lines changed

src/basic/fd-util.c

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -929,17 +929,18 @@ int path_is_root_at(int dir_fd, const char *path) {
929929
* $ mount --bind /tmp/x /tmp/x/y
930930
*
931931
* Note, statx() does not provide the mount ID and path_get_mnt_id_at() does not work when an old
932-
* kernel is used without /proc mounted. In that case, let's assume that we do not have such spurious
933-
* mount points in an early boot stage, and silently skip the following check. */
932+
* kernel is used. In that case, let's assume that we do not have such spurious mount points in an
933+
* early boot stage, and silently skip the following check. */
934934

935935
if (!FLAGS_SET(st.nsx.stx_mask, STATX_MNT_ID)) {
936936
int mntid;
937937

938-
r = path_get_mnt_id_at(dir_fd, "", &mntid);
939-
if (r == -ENOSYS)
940-
return true; /* skip the mount ID check */
941-
if (r < 0)
938+
r = path_get_mnt_id_at_fallback(dir_fd, "", &mntid);
939+
if (r < 0) {
940+
if (ERRNO_IS_NOT_SUPPORTED(r))
941+
return true; /* skip the mount ID check */
942942
return r;
943+
}
943944
assert(mntid >= 0);
944945

945946
st.nsx.stx_mnt_id = mntid;
@@ -949,11 +950,12 @@ int path_is_root_at(int dir_fd, const char *path) {
949950
if (!FLAGS_SET(pst.nsx.stx_mask, STATX_MNT_ID)) {
950951
int mntid;
951952

952-
r = path_get_mnt_id_at(dir_fd, "..", &mntid);
953-
if (r == -ENOSYS)
954-
return true; /* skip the mount ID check */
955-
if (r < 0)
953+
r = path_get_mnt_id_at_fallback(dir_fd, "..", &mntid);
954+
if (r < 0) {
955+
if (ERRNO_IS_NOT_SUPPORTED(r))
956+
return true; /* skip the mount ID check */
956957
return r;
958+
}
957959
assert(mntid >= 0);
958960

959961
pst.nsx.stx_mnt_id = mntid;

src/basic/mountpoint-util.c

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,9 @@ int fd_is_mount_point(int fd, const char *filename, int flags) {
223223
AT_STATX_DONT_SYNC, /* don't go to the network for this – for similar reasons */
224224
STATX_TYPE,
225225
&sx) < 0) {
226-
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
226+
if (!ERRNO_IS_NOT_SUPPORTED(errno) && /* statx() is not supported by the kernel. */
227+
!ERRNO_IS_PRIVILEGE(errno) && /* maybe filtered by seccomp. */
228+
errno != EINVAL) /* glibc's fallback method returns EINVAL when AT_STATX_DONT_SYNC is set. */
227229
return -errno;
228230

229231
/* 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) {
357359
return fd_is_mount_point(fd, last_path_component(t), flags);
358360
}
359361

362+
int path_get_mnt_id_at_fallback(int dir_fd, const char *path, int *ret) {
363+
int r;
364+
365+
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
366+
assert(ret);
367+
368+
r = name_to_handle_at_loop(dir_fd, path, NULL, ret, isempty(path) ? AT_EMPTY_PATH : 0);
369+
if (r == 0 || is_name_to_handle_at_fatal_error(r))
370+
return r;
371+
372+
return fd_fdinfo_mnt_id(dir_fd, path, isempty(path) ? AT_EMPTY_PATH : 0, ret);
373+
}
374+
360375
int path_get_mnt_id_at(int dir_fd, const char *path, int *ret) {
361376
STRUCT_NEW_STATX_DEFINE(buf);
362-
int r;
363377

364378
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
365379
assert(ret);
@@ -371,7 +385,9 @@ int path_get_mnt_id_at(int dir_fd, const char *path, int *ret) {
371385
AT_STATX_DONT_SYNC, /* don't go to the network, mnt_id is a local concept */
372386
STATX_MNT_ID,
373387
&buf.sx) < 0) {
374-
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
388+
if (!ERRNO_IS_NOT_SUPPORTED(errno) && /* statx() is not supported by the kernel. */
389+
!ERRNO_IS_PRIVILEGE(errno) && /* maybe filtered by seccomp. */
390+
errno != EINVAL) /* glibc's fallback method returns EINVAL when AT_STATX_DONT_SYNC is set. */
375391
return -errno;
376392

377393
/* 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) {
382398
return 0;
383399
}
384400

385-
r = name_to_handle_at_loop(dir_fd, path, NULL, ret, isempty(path) ? AT_EMPTY_PATH : 0);
386-
if (r == 0 || is_name_to_handle_at_fatal_error(r))
387-
return r;
388-
389-
return fd_fdinfo_mnt_id(dir_fd, path, isempty(path) ? AT_EMPTY_PATH : 0, ret);
401+
return path_get_mnt_id_at_fallback(dir_fd, path, ret);
390402
}
391403

392404
bool fstype_is_network(const char *fstype) {

src/basic/mountpoint-util.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
int name_to_handle_at_loop(int fd, const char *path, struct file_handle **ret_handle, int *ret_mnt_id, int flags);
3939

40+
int path_get_mnt_id_at_fallback(int dir_fd, const char *path, int *ret);
4041
int path_get_mnt_id_at(int dir_fd, const char *path, int *ret);
4142
static inline int path_get_mnt_id(const char *path, int *ret) {
4243
return path_get_mnt_id_at(AT_FDCWD, path, ret);

src/basic/stat-util.c

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -394,21 +394,35 @@ bool statx_mount_same(const struct new_statx *a, const struct new_statx *b) {
394394
a->stx_dev_minor == b->stx_dev_minor;
395395
}
396396

397+
static bool is_statx_fatal_error(int err, int flags) {
398+
assert(err < 0);
399+
400+
/* If statx() is not supported or if we see EPERM (which might indicate seccomp filtering or so),
401+
* let's do a fallback. Note that on EACCES we'll not fall back, since that is likely an indication of
402+
* fs access issues, which we should propagate. */
403+
if (ERRNO_IS_NOT_SUPPORTED(err) || err == -EPERM)
404+
return false;
405+
406+
/* When unsupported flags are specified, glibc's fallback function returns -EINVAL.
407+
* See statx_generic() in glibc. */
408+
if (err != -EINVAL)
409+
return true;
410+
411+
if ((flags & ~(AT_EMPTY_PATH | AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW | AT_STATX_SYNC_AS_STAT)) != 0)
412+
return false; /* Unsupported flags are specified. Let's try to use our implementation. */
413+
414+
return true;
415+
}
416+
397417
int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx) {
398418
static bool avoid_statx = false;
399419
struct stat st;
420+
int r;
400421

401422
if (!avoid_statx) {
402-
if (statx(dfd, path, flags, mask, sx) < 0) {
403-
if (!ERRNO_IS_NOT_SUPPORTED(errno) && errno != EPERM)
404-
return -errno;
405-
406-
/* If statx() is not supported or if we see EPERM (which might indicate seccomp
407-
* filtering or so), let's do a fallback. Not that on EACCES we'll not fall back,
408-
* since that is likely an indication of fs access issues, which we should
409-
* propagate */
410-
} else
411-
return 0;
423+
r = RET_NERRNO(statx(dfd, path, flags, mask, sx));
424+
if (r >= 0 || is_statx_fatal_error(r, flags))
425+
return r;
412426

413427
avoid_statx = true;
414428
}

0 commit comments

Comments
 (0)