Now both our required baseline of glibc and kernel support statx.
int dirent_ensure_type(int dir_fd, struct dirent *de) {
struct statx sx;
- int r;
assert(dir_fd >= 0);
assert(de);
}
/* Let's ask only for the type, nothing else. */
- r = statx_fallback(dir_fd, de->d_name, AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT, STATX_TYPE, &sx);
- if (r < 0)
- return r;
+ if (statx(dir_fd, de->d_name, AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT, STATX_TYPE, &sx) < 0)
+ return -errno;
assert(FLAGS_SET(sx.stx_mask, STATX_TYPE));
de->d_type = IFTODT(sx.stx_mode);
assert(fd1 >= 0);
assert(fd2 >= 0);
- r = statx_fallback(fd1, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sx1);
- if (r < 0)
- return r;
+ if (statx(fd1, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sx1) < 0)
+ return -errno;
- r = statx_fallback(fd2, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sx2);
- if (r < 0)
- return r;
+ if (statx(fd2, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sx2) < 0)
+ return -errno;
/* First, compare inode. If these are different, the fd does not point to the root directory "/". */
if (!statx_inode_same(&sx1, &sx2))
if (FLAGS_SET(flags, RECURSE_DIR_TOPLEVEL)) {
if (statx_mask != 0) {
- r = statx_fallback(dir_fd, "", AT_EMPTY_PATH, statx_mask, &root_sx);
- if (r < 0)
- return r;
+ if (statx(dir_fd, "", AT_EMPTY_PATH, statx_mask, &root_sx) < 0)
+ return -errno;
}
r = func(RECURSE_DIR_ENTER,
de->entries[i]->d_type = DT_DIR;
if (statx_mask != 0 || (flags & RECURSE_DIR_SAME_MOUNT)) {
- r = statx_fallback(subdir_fd, "", AT_EMPTY_PATH, statx_mask, &sx);
- if (r < 0)
- return r;
+ if (statx(subdir_fd, "", AT_EMPTY_PATH, statx_mask, &sx) < 0)
+ return -errno;
sx_valid = true;
}
* assume. Let's guarantee that we never pass statx data of a directory where
* caller expects a non-directory */
- r = statx_fallback(inode_fd, "", AT_EMPTY_PATH, statx_mask | STATX_TYPE, &sx);
- if (r < 0)
- return r;
+ if (statx(inode_fd, "", AT_EMPTY_PATH, statx_mask | STATX_TYPE, &sx) < 0)
+ return -errno;
assert(sx.stx_mask & STATX_TYPE);
sx_valid = true;
} else if (statx_mask != 0 || (de->entries[i]->d_type == DT_UNKNOWN && (flags & RECURSE_DIR_ENSURE_TYPE))) {
- r = statx_fallback(dir_fd, de->entries[i]->d_name, AT_SYMLINK_NOFOLLOW, statx_mask | STATX_TYPE, &sx);
- if (r == -ENOENT) /* Vanished by now? Go for next file immediately */
- continue;
- if (r < 0) {
- log_debug_errno(r, "Failed to stat directory entry '%s': %m", p);
+ if (statx(dir_fd, de->entries[i]->d_name, AT_SYMLINK_NOFOLLOW, statx_mask | STATX_TYPE, &sx) < 0) {
+ if (errno == ENOENT) /* Vanished by now? Go for next file immediately */
+ continue;
+
+ log_debug_errno(errno, "Failed to stat directory entry '%s': %m", p);
- assert(-r <= RECURSE_DIR_SKIP_STAT_INODE_ERROR_MAX - RECURSE_DIR_SKIP_STAT_INODE_ERROR_BASE);
+ assert(errno <= RECURSE_DIR_SKIP_STAT_INODE_ERROR_MAX - RECURSE_DIR_SKIP_STAT_INODE_ERROR_BASE);
- r = func(RECURSE_DIR_SKIP_STAT_INODE_ERROR_BASE + -r,
+ r = func(RECURSE_DIR_SKIP_STAT_INODE_ERROR_BASE + errno,
p,
dir_fd,
-1,
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) {
- r = RET_NERRNO(statx(dfd, path, flags, mask, sx));
- if (r >= 0 || is_statx_fatal_error(r, flags))
- return r;
-
- avoid_statx = true;
- }
-
- /* Only do fallback if fstatat() supports the flag too, or if it's one of the sync flags, which are
- * OK to ignore */
- if ((flags & ~(AT_EMPTY_PATH|AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW|
- AT_STATX_SYNC_AS_STAT|AT_STATX_FORCE_SYNC|AT_STATX_DONT_SYNC)) != 0)
- return -EOPNOTSUPP;
-
- if (fstatat(dfd, path, &st, flags & (AT_EMPTY_PATH|AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW)) < 0)
- return -errno;
-
- *sx = (struct statx) {
- .stx_mask = STATX_TYPE|STATX_MODE|
- STATX_NLINK|STATX_UID|STATX_GID|
- STATX_ATIME|STATX_MTIME|STATX_CTIME|
- STATX_INO|STATX_SIZE|STATX_BLOCKS,
- .stx_blksize = st.st_blksize,
- .stx_nlink = st.st_nlink,
- .stx_uid = st.st_uid,
- .stx_gid = st.st_gid,
- .stx_mode = st.st_mode,
- .stx_ino = st.st_ino,
- .stx_size = st.st_size,
- .stx_blocks = st.st_blocks,
- .stx_rdev_major = major(st.st_rdev),
- .stx_rdev_minor = minor(st.st_rdev),
- .stx_dev_major = major(st.st_dev),
- .stx_dev_minor = minor(st.st_dev),
- .stx_atime.tv_sec = st.st_atim.tv_sec,
- .stx_atime.tv_nsec = st.st_atim.tv_nsec,
- .stx_mtime.tv_sec = st.st_mtim.tv_sec,
- .stx_mtime.tv_nsec = st.st_mtim.tv_nsec,
- .stx_ctime.tv_sec = st.st_ctim.tv_sec,
- .stx_ctime.tv_nsec = st.st_ctim.tv_nsec,
- };
-
- return 0;
-}
-
int xstatfsat(int dir_fd, const char *path, struct statfs *ret) {
_cleanup_close_ int fd = -EBADF;
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_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx);
-
int xstatfsat(int dir_fd, const char *path, struct statfs *ret);
static inline usec_t statx_timestamp_load(const struct statx_timestamp *ts) {
if (r < 0 && r != -EADDRNOTAVAIL)
return log_error_errno(r, "Failed to extract filename of %s: %m", path);
- r = statx_fallback(dir_fd, strempty(f), AT_SYMLINK_NOFOLLOW|(isempty(f) ? AT_EMPTY_PATH : 0),
- STATX_TYPE|STATX_INO|STATX_MNT_ID, &sxa);
- if (r < 0)
- return log_full_errno((searching && r == -ENOENT) ||
- (unprivileged_mode && ERRNO_IS_PRIVILEGE(r)) ? LOG_DEBUG : LOG_ERR, r,
+ if (statx(dir_fd, strempty(f),
+ AT_SYMLINK_NOFOLLOW|(isempty(f) ? AT_EMPTY_PATH : 0),
+ STATX_TYPE|STATX_INO|STATX_MNT_ID, &sxa) < 0)
+ return log_full_errno((searching && errno == ENOENT) ||
+ (unprivileged_mode && ERRNO_IS_PRIVILEGE(errno)) ? LOG_DEBUG : LOG_ERR, errno,
"Failed to determine block device node of \"%s\": %m", path);
assert(S_ISDIR(sxa.stx_mode)); /* We used O_DIRECTORY above, when opening, so this must hold */
}
/* Now let's look at the parent */
- r = statx_fallback(dir_fd, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sxb);
- if (r < 0)
- return log_full_errno(unprivileged_mode && ERRNO_IS_PRIVILEGE(r) ? LOG_DEBUG : LOG_ERR, r,
+ if (statx(dir_fd, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sxb) < 0)
+ return log_full_errno(unprivileged_mode && ERRNO_IS_PRIVILEGE(errno) ? LOG_DEBUG : LOG_ERR, errno,
"Failed to determine block device node of parent of \"%s\": %m", path);
if (statx_inode_same(&sxa, &sxb)) /* for the root dir inode nr for both inodes will be the same */
return 0;
}
- r = statx_fallback(dirfd(d), "", AT_EMPTY_PATH, STATX_MODE|STATX_INO|STATX_ATIME|STATX_MTIME, &sx1);
- if (r < 0)
- return log_error_errno(r, "statx(%s) failed: %m", path);
+ if (statx(dirfd(d), "", AT_EMPTY_PATH, STATX_MODE|STATX_INO|STATX_ATIME|STATX_MTIME, &sx1) < 0)
+ return log_error_errno(errno, "statx(%s) failed: %m", path);
if (FLAGS_SET(sx1.stx_attributes_mask, STATX_ATTR_MOUNT_ROOT))
*ret_mountpoint = FLAGS_SET(sx1.stx_attributes, STATX_ATTR_MOUNT_ROOT);
else {
struct statx sx2;
- r = statx_fallback(dirfd(d), "..", 0, STATX_INO, &sx2);
- if (r < 0)
- return log_error_errno(r, "statx(%s/..) failed: %m", path);
+ if (statx(dirfd(d), "..", 0, STATX_INO, &sx2) < 0)
+ return log_error_errno(errno, "statx(%s/..) failed: %m", path);
*ret_mountpoint = !statx_mount_same(&sx1, &sx2);
}
* st_dev. */
struct statx sx;
- r = statx_fallback(
- dirfd(d), de->d_name,
- AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT,
- STATX_TYPE|STATX_MODE|STATX_UID|STATX_ATIME|STATX_MTIME|STATX_CTIME|STATX_BTIME,
- &sx);
- if (r == -ENOENT)
- continue;
- if (r < 0) {
+ if (statx(dirfd(d), de->d_name,
+ AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT,
+ STATX_TYPE|STATX_MODE|STATX_UID|STATX_ATIME|STATX_MTIME|STATX_CTIME|STATX_BTIME,
+ &sx) < 0) {
+ if (errno == ENOENT)
+ continue;
+
/* FUSE, NFS mounts, SELinux might return EACCES */
- log_full_errno(r == -EACCES ? LOG_DEBUG : LOG_ERR, r,
+ log_full_errno(errno == EACCES ? LOG_DEBUG : LOG_ERR, errno,
"statx(%s/%s) failed: %m", p, de->d_name);
continue;
}