diff --git a/src/arch/x86/linux/linux.hh b/src/arch/x86/linux/linux.hh index b9598224a6..2b2031cc3c 100644 --- a/src/arch/x86/linux/linux.hh +++ b/src/arch/x86/linux/linux.hh @@ -119,6 +119,49 @@ class X86Linux64 : public X86Linux, public OpenFlagTable int64_t unused0[3]; }; + struct tgt_statx + { + /* 0x00 */ + uint32_t stx_mask; + uint32_t stx_blksize; + uint64_t stx_attributes; + /* 0x10 */ + uint32_t stx_nlink; + uint32_t stx_uid; + uint32_t stx_gid; + uint16_t stx_mode; + uint16_t stx_spare0; + /* 0x20 */ + uint64_t stx_ino; + uint64_t stx_size; + uint64_t stx_blocks; + uint64_t stx_attributes_mask; + /* 0x40 */ + uint64_t stx_atimeX; + uint32_t stx_atime_nsec; + int32_t stx_atime_reserved; + uint64_t stx_btimeX; + uint32_t stx_btime_nsec; + int32_t stx_btime_reserved; + uint64_t stx_ctimeX; + uint32_t stx_ctime_nsec; + int32_t stx_ctime_reserved; + uint64_t stx_mtimeX; + uint32_t stx_mtime_nsec; + int32_t stx_mtime_reserved; + /* 0x80 */ + uint32_t stx_rdev_major; + uint32_t stx_rdev_minor; + uint32_t stx_dev_major; + uint32_t stx_dev_minor; + /* 0x90 */ + uint64_t stx_mnt_id; + uint64_t stx_spare2; + /* 0xa0 */ + uint64_t stx_spare3[12]; + /* 0x100 */ + }; + struct tgt_fsid { long val[2]; diff --git a/src/arch/x86/linux/syscall_tbl64.cc b/src/arch/x86/linux/syscall_tbl64.cc index 26299d884b..1b3dfaf020 100644 --- a/src/arch/x86/linux/syscall_tbl64.cc +++ b/src/arch/x86/linux/syscall_tbl64.cc @@ -375,7 +375,7 @@ SyscallDescTable EmuLinux::syscallDescs64 = { { 329, "pkey_mprotect" }, { 330, "pkey_alloc" }, { 331, "pkey_free" }, - { 332, "statx" }, + { 332, "statx", statxFunc }, { 333, "io_pgetevents" }, { 334, "rseq", ignoreFunc }, { 424, "pidfd_send_signal" }, diff --git a/src/sim/syscall_emul.hh b/src/sim/syscall_emul.hh index 97749f325d..57d6bd8a5b 100644 --- a/src/sim/syscall_emul.hh +++ b/src/sim/syscall_emul.hh @@ -57,6 +57,7 @@ /// application on the host machine. #if defined(__linux__) +#include #include #include #include @@ -677,6 +678,73 @@ copyOutStatfsBuf(TgtStatPtr tgt, HostStatPtr host) #endif } +template +void +copyOutStatxBuf(TgtStatPtr tgt, HostStatPtr host, bool fakeTTY = false) +{ + constexpr ByteOrder bo = OS::byteOrder; + + if (fakeTTY) { + tgt->stx_dev_major = MAJOR(0xA); + tgt->stx_dev_minor = MINOR(0xA); + } else { + tgt->stx_dev_major = host->stx_dev_major; + tgt->stx_dev_minor = host->stx_dev_minor; + } + tgt->stx_dev_major = htog(tgt->stx_dev_major, bo); + tgt->stx_dev_minor = htog(tgt->stx_dev_minor, bo); + tgt->stx_ino = host->stx_ino; + tgt->stx_ino = htog(tgt->stx_ino, bo); + tgt->stx_mode = host->stx_mode; + if (fakeTTY) { + // Claim to be character device. + tgt->stx_mode &= ~S_IFMT; + tgt->stx_mode |= S_IFCHR; + } + tgt->stx_mode = htog(tgt->stx_mode, bo); + tgt->stx_nlink = host->stx_nlink; + tgt->stx_nlink = htog(tgt->stx_nlink, bo); + tgt->stx_uid = host->stx_uid; + tgt->stx_uid = htog(tgt->stx_uid, bo); + tgt->stx_gid = host->stx_gid; + tgt->stx_gid = htog(tgt->stx_gid, bo); + if (fakeTTY) { + tgt->stx_rdev_major = MAJOR(0x880d); + tgt->stx_rdev_minor = MINOR(0x880d); + } else { + tgt->stx_rdev_major = host->stx_rdev_major; + tgt->stx_rdev_minor = host->stx_rdev_minor; + } + tgt->stx_rdev_major = htog(tgt->stx_rdev_major, bo); + tgt->stx_rdev_minor = htog(tgt->stx_rdev_minor, bo); + tgt->stx_size = host->stx_size; + tgt->stx_size = htog(tgt->stx_size, bo); +#define statx_copy_time(out, in) \ + out##X = in.tv_sec; \ + out##X = htog(out##X, bo); \ + out##_nsec = in.tv_sec; \ + out##_nsec = htog(out##_nsec, bo); + statx_copy_time(tgt->stx_atime, host->stx_atime); + statx_copy_time(tgt->stx_btime, host->stx_btime); + statx_copy_time(tgt->stx_ctime, host->stx_ctime); + statx_copy_time(tgt->stx_mtime, host->stx_mtime); +#undef statx_copy_time + // Force the block size to be 8KB. This helps to ensure buffered io works + // consistently across different hosts. + tgt->stx_blksize = 0x2000; + tgt->stx_blksize = htog(tgt->stx_blksize, bo); + tgt->stx_blocks = host->stx_blocks; + tgt->stx_blocks = htog(tgt->stx_blocks, bo); + tgt->stx_mask = host->stx_mask; + tgt->stx_mask = htog(tgt->stx_mask, bo); + tgt->stx_attributes = host->stx_attributes; + tgt->stx_attributes = htog(tgt->stx_attributes, bo); + tgt->stx_attributes_mask = host->stx_attributes_mask; + tgt->stx_attributes_mask = htog(tgt->stx_attributes_mask, bo); + tgt->stx_mnt_id = host->stx_mnt_id; + tgt->stx_mnt_id = htog(tgt->stx_mnt_id, bo); +} + /// Target ioctl() handler. For the most part, programs call ioctl() /// only to find out if their stdout is a tty, to determine whether to /// do line or block buffering. We always claim that output fds are @@ -1456,6 +1524,46 @@ stat64Func(SyscallDesc *desc, ThreadContext *tc, return fstatat64Func(desc, tc, OS::TGT_AT_FDCWD, pathname, tgt_stat); } +/// Target statx() handler. +template +SyscallReturn +statxFunc(SyscallDesc *desc, ThreadContext *tc, + int dirfd, VPtr<> pathname, int flags, + unsigned int mask, VPtr tgt_statx) +{ + std::string path; + + if (!SETranslatingPortProxy(tc).tryReadString(path, pathname)) + return -EFAULT; + + if (path.empty() && !(flags & OS::TGT_AT_EMPTY_PATH)) + return -ENOENT; + flags = flags & ~OS::TGT_AT_EMPTY_PATH; + + warn_if(flags != 0, "statx: Flag bits %#x not supported.", flags); + + // Modifying path from the directory descriptor + if (auto res = atSyscallPath(tc, dirfd, path); !res.successful()) { + return res; + } + + auto p = tc->getProcessPtr(); + + // Adjust path for cwd and redirection + path = p->checkPathRedirect(path); + + struct statx host_buf; + std::memset(&host_buf, 0, sizeof host_buf); + int result = statx(AT_FDCWD, path.c_str(), flags, mask, &host_buf); + + if (result < 0) + return -errno; + + copyOutStatxBuf(tgt_statx, &host_buf); + + return 0; +} + /// Target fstat64() handler. template SyscallReturn