Index: sys/compat/linux/linux.h =================================================================== --- sys/compat/linux/linux.h +++ sys/compat/linux/linux.h @@ -205,4 +205,46 @@ int bsd_to_linux_errno(int error); void linux_check_errtbl(void); +#define STATX_BASIC_STATS 0x07ff +#define STATX_BTIME 0x0800 +#define STATX_ALL 0x0fff + +#define STATX_ATTR_COMPRESSED 0x0004 +#define STATX_ATTR_IMMUTABLE 0x0010 +#define STATX_ATTR_APPEND 0x0020 +#define STATX_ATTR_NODUMP 0x0040 +#define STATX_ATTR_ENCRYPTED 0x0800 +#define STATX_ATTR_AUTOMOUNT 0x1000 + +struct l_statx_timestamp { + int64_t tv_sec; + int32_t tv_nsec; + int32_t __spare0; +}; + +struct l_statx { + uint32_t stx_mask; + uint32_t stx_blksize; + uint64_t stx_attributes; + uint32_t stx_nlink; + uint32_t stx_uid; + uint32_t stx_gid; + uint16_t stx_mode; + uint16_t __spare0[1]; + uint64_t stx_ino; + uint64_t stx_size; + uint64_t stx_blocks; + uint64_t stx_attributes_mask; + struct l_statx_timestamp stx_atime; + struct l_statx_timestamp stx_btime; + struct l_statx_timestamp stx_ctime; + struct l_statx_timestamp stx_mtime; + uint32_t stx_rdev_major; + uint32_t stx_rdev_minor; + uint32_t stx_dev_major; + uint32_t stx_dev_minor; + uint64_t stx_mnt_id; + uint64_t __spare2[13]; +}; + #endif /* _LINUX_MI_H_ */ Index: sys/compat/linux/linux_dummy.c =================================================================== --- sys/compat/linux/linux_dummy.c +++ sys/compat/linux/linux_dummy.c @@ -147,8 +147,6 @@ DUMMY(process_madvise); DUMMY(epoll_pwait2); DUMMY(mount_setattr); -/* Linux 4.11: */ -DUMMY(statx); /* Linux 4.18: */ DUMMY(io_pgetevents); DUMMY(rseq); Index: sys/compat/linux/linux_stats.c =================================================================== --- sys/compat/linux/linux_stats.c +++ sys/compat/linux/linux_stats.c @@ -196,6 +196,40 @@ return (copyout(&tbuf, ubuf, sizeof(tbuf))); } +static int +statx_copyout(struct stat *buf, void *ubuf) +{ + struct l_statx tbuf; + + bzero(&tbuf, sizeof(tbuf)); + tbuf.stx_mask = STATX_ALL; + tbuf.stx_blksize = buf->st_blksize; + tbuf.stx_attributes = 0; + tbuf.stx_nlink = buf->st_nlink; + tbuf.stx_uid = buf->st_uid; + tbuf.stx_gid = buf->st_gid; + tbuf.stx_mode = buf->st_mode; + tbuf.stx_ino = buf->st_ino; + tbuf.stx_size = buf->st_size; + tbuf.stx_blocks = buf->st_blocks; + + tbuf.stx_atime.tv_sec = buf->st_atim.tv_sec; + tbuf.stx_atime.tv_nsec = buf->st_atim.tv_nsec; + tbuf.stx_btime.tv_sec = buf->st_birthtim.tv_sec; + tbuf.stx_btime.tv_nsec = buf->st_birthtim.tv_nsec; + tbuf.stx_ctime.tv_sec = buf->st_ctim.tv_sec; + tbuf.stx_ctime.tv_nsec = buf->st_ctim.tv_nsec; + tbuf.stx_mtime.tv_sec = buf->st_mtim.tv_sec; + tbuf.stx_mtime.tv_nsec = buf->st_mtim.tv_nsec; + + tbuf.stx_rdev_major = buf->st_rdev >> 8; + tbuf.stx_rdev_minor = buf->st_rdev & 0xff; + tbuf.stx_dev_major = buf->st_dev >> 8; + tbuf.stx_dev_minor = buf->st_dev & 0xff; + + return (copyout(&tbuf, ubuf, sizeof(tbuf))); +} + #ifdef LINUX_LEGACY_SYSCALLS int linux_newstat(struct thread *td, struct linux_newstat_args *args) @@ -730,3 +764,36 @@ vrele(vp); return (error); } + +int +linux_statx(struct thread *td, struct linux_statx_args *args) +{ + char *path; + int error, dirfd, flags; + struct stat buf; + + if (args->flags & ~(LINUX_AT_SYMLINK_NOFOLLOW | LINUX_AT_EMPTY_PATH)) { + linux_msg(td, "statx unsupported flags 0x%x", args->flags); + return (EINVAL); + } + + flags = (args->flags & LINUX_AT_SYMLINK_NOFOLLOW) ? + AT_SYMLINK_NOFOLLOW : 0; + flags |= (args->flags & LINUX_AT_EMPTY_PATH) ? + AT_EMPTY_PATH : 0; + + dirfd = (args->dirfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dirfd; + if (!LUSECONVPATH(td)) { + error = linux_kern_statat(td, flags, dirfd, args->pathname, + UIO_USERSPACE, &buf); + } else { + LCONVPATHEXIST_AT(td, args->pathname, &path, dirfd); + error = linux_kern_statat(td, flags, dirfd, path, UIO_SYSSPACE, &buf); + LFREEPATH(path); + } + if (error == 0) + error = statx_copyout(&buf, args->statxbuf); + + return (error); +} +