@@ -8002,7 +8002,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
abi_long ret;
#if defined(TARGET_NR_stat) || defined(TARGET_NR_stat64) \
|| defined(TARGET_NR_lstat) || defined(TARGET_NR_lstat64) \
- || defined(TARGET_NR_fstat) || defined(TARGET_NR_fstat64)
+ || defined(TARGET_NR_fstat) || defined(TARGET_NR_fstat64) \
+ || defined(TARGET_NR_statx)
struct stat st;
#endif
#if defined(TARGET_NR_statfs) || defined(TARGET_NR_statfs64) \
@@ -10025,6 +10026,124 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
}
break;
#endif
+#if defined(TARGET_NR_statx)
+ case TARGET_NR_statx:
+ {
+ struct target_statx *target_stx;
+ int dirfd = tswap32(arg1);
+ int flags = tswap32(arg3);
+
+ p = lock_user_string(arg2);
+ if (p == NULL) {
+ goto efault;
+ }
+#if defined(__NR_statx)
+ {
+ /* We assume that struct statx is arhitecture independent */
+ struct target_statx host_stx;
+ int mask = tswap32(arg4);
+
+ ret = get_errno(syscall(__NR_statx, dirfd, p, flags, mask,
+ &host_stx));
+ if (!is_error(ret)) {
+ unlock_user(p, arg2, 0);
+ if (!lock_user_struct(VERIFY_WRITE, target_stx, arg5, 0)) {
+ goto efault;
+ }
+ memset(target_stx, 0, sizeof(*target_stx));
+
+ __put_user(host_stx.stx_mask, &target_stx->stx_mask);
+ __put_user(host_stx.stx_blksize, &target_stx->stx_blksize);
+ __put_user(host_stx.stx_attributes,
+ &target_stx->stx_attributes);
+ __put_user(host_stx.stx_nlink, &target_stx->stx_nlink);
+ __put_user(host_stx.stx_uid, &target_stx->stx_uid);
+ __put_user(host_stx.stx_gid, &target_stx->stx_gid);
+ __put_user(host_stx.stx_mode, &target_stx->stx_mode);
+ __put_user(host_stx.stx_ino, &target_stx->stx_ino);
+ __put_user(host_stx.stx_size, &target_stx->stx_size);
+ __put_user(host_stx.stx_blocks, &target_stx->stx_blocks);
+ __put_user(host_stx.stx_attributes_mask,
+ &target_stx->stx_attributes_mask);
+ __put_user(host_stx.stx_atime.tv_sec,
+ &target_stx->stx_atime.tv_sec);
+ __put_user(host_stx.stx_atime.tv_nsec,
+ &target_stx->stx_atime.tv_nsec);
+ __put_user(host_stx.stx_btime.tv_sec,
+ &target_stx->stx_atime.tv_sec);
+ __put_user(host_stx.stx_btime.tv_nsec,
+ &target_stx->stx_atime.tv_nsec);
+ __put_user(host_stx.stx_ctime.tv_sec,
+ &target_stx->stx_atime.tv_sec);
+ __put_user(host_stx.stx_ctime.tv_nsec,
+ &target_stx->stx_atime.tv_nsec);
+ __put_user(host_stx.stx_mtime.tv_sec,
+ &target_stx->stx_atime.tv_sec);
+ __put_user(host_stx.stx_mtime.tv_nsec,
+ &target_stx->stx_atime.tv_nsec);
+ __put_user(host_stx.stx_rdev_major,
+ &target_stx->stx_rdev_major);
+ __put_user(host_stx.stx_rdev_minor,
+ &target_stx->stx_rdev_minor);
+ __put_user(host_stx.stx_dev_major,
+ &target_stx->stx_dev_major);
+ __put_user(host_stx.stx_dev_minor,
+ &target_stx->stx_dev_minor);
+ }
+
+ if (ret != TARGET_ENOSYS) {
+ break;
+ }
+ }
+#endif
+ if ((p == NULL) || (*((char *)p) == 0)) {
+ /* By file descriptor */
+ ret = get_errno(fstat(dirfd, &st));
+ unlock_user(p, arg2, 0);
+ } else if (*((char *)p) == '/') {
+ /* Absolute pathname */
+ ret = get_errno(stat(path(p), &st));
+ unlock_user(p, arg2, 0);
+ } else {
+ if (dirfd == AT_FDCWD) {
+ /* Pathname relative to the current working directory */
+ ret = get_errno(stat(path(p), &st));
+ unlock_user(p, arg2, 0);
+ } else {
+ /*
+ * Pathname relative to the directory referred to by the
+ * file descriptor dirfd
+ */
+ ret = get_errno(fstatat(dirfd, path(p), &st, flags));
+ unlock_user(p, arg2, 0);
+ }
+ }
+
+ if (!is_error(ret)) {
+ if (!lock_user_struct(VERIFY_WRITE, target_stx, arg5, 0)) {
+ goto efault;
+ }
+ memset(target_stx, 0, sizeof(*target_stx));
+ __put_user(major(st.st_dev), &target_stx->stx_dev_major);
+ __put_user(minor(st.st_dev), &target_stx->stx_dev_minor);
+ __put_user(st.st_ino, &target_stx->stx_ino);
+ __put_user(st.st_mode, &target_stx->stx_mode);
+ __put_user(st.st_uid, &target_stx->stx_uid);
+ __put_user(st.st_gid, &target_stx->stx_gid);
+ __put_user(st.st_nlink, &target_stx->stx_nlink);
+ __put_user(major(st.st_rdev), &target_stx->stx_rdev_major);
+ __put_user(minor(st.st_rdev), &target_stx->stx_rdev_minor);
+ __put_user(st.st_size, &target_stx->stx_size);
+ __put_user(st.st_blksize, &target_stx->stx_blksize);
+ __put_user(st.st_blocks, &target_stx->stx_blocks);
+ __put_user(st.st_atime, &target_stx->stx_atime.tv_sec);
+ __put_user(st.st_mtime, &target_stx->stx_mtime.tv_sec);
+ __put_user(st.st_ctime, &target_stx->stx_ctime.tv_sec);
+ unlock_user_struct(target_stx, arg5, 1);
+ }
+ }
+ break;
+#endif
#ifdef TARGET_NR_olduname
case TARGET_NR_olduname:
goto unimplemented;
@@ -2530,4 +2530,42 @@ struct target_user_cap_data {
/* Return size of the log buffer */
#define TARGET_SYSLOG_ACTION_SIZE_BUFFER 10
+struct target_statx_timestamp {
+ int64_t tv_sec;
+ uint32_t tv_nsec;
+ int32_t __reserved;
+};
+
+struct target_statx {
+ /* 0x00 */
+ uint32_t stx_mask; /* What results were written [uncond] */
+ uint32_t stx_blksize; /* Preferred general I/O size [uncond] */
+ uint64_t stx_attributes; /* Flags conveying information about the file */
+ /* 0x10 */
+ uint32_t stx_nlink; /* Number of hard links */
+ uint32_t stx_uid; /* User ID of owner */
+ uint32_t stx_gid; /* Group ID of owner */
+ uint16_t stx_mode; /* File mode */
+ uint16_t __spare0[1];
+ /* 0x20 */
+ uint64_t stx_ino; /* Inode number */
+ uint64_t stx_size; /* File size */
+ uint64_t stx_blocks; /* Number of 512-byte blocks allocated */
+ uint64_t stx_attributes_mask; /* Mask to show what's supported in
+ stx_attributes */
+ /* 0x40 */
+ struct target_statx_timestamp stx_atime; /* Last access time */
+ struct target_statx_timestamp stx_btime; /* File creation time */
+ struct target_statx_timestamp stx_ctime; /* Last attribute change time */
+ struct target_statx_timestamp stx_mtime; /* Last data modification time */
+ /* 0x80 */
+ uint32_t stx_rdev_major; /* Device ID of special file [if bdev/cdev] */
+ uint32_t stx_rdev_minor;
+ uint32_t stx_dev_major; /* ID of device containing file [uncond] */
+ uint32_t stx_dev_minor;
+ /* 0x90 */
+ uint64_t __spare2[14]; /* Spare space for future expansion */
+ /* 0x100 */
+};
+
#endif