From 1fd723f21f736febf78da5a53bf9e32482a1dc14 Mon Sep 17 00:00:00 2001 From: Doston Abdullayev Date: Sat, 10 Oct 2026 09:29:09 +0500 Subject: [PATCH] fix(libkernel): return errors from file operation failures On Windows, sceKernelPread and sceKernelPwrite called the POSIX pread and pwrite wrappers, which throw on every failure, so a bad descriptor, a negative offset or a write to a read-only descriptor aborted the process instead of returning an error as the Linux build does. ftruncate and sceKernelFtruncate threw on any host failure on both platforms. Give the Windows sceKernelPread and sceKernelPwrite the same checks and SceErrorFromErrno returns as the Linux ones, and accept a zero-length transfer with a null buffer. Report a positioned transfer on a handle opened without the needed access as EBADF instead of EIO. Return EINVAL from ftruncate for a negative length and for a descriptor opened without write access, as FreeBSD does, and pass other host errors to the guest. Fixes #2423 --- core/libs/prx/libkernel/File/src/Stdio.cpp | 25 +++++++++++++------ core/libs/tests/GuestFileVector.cpp | 28 ++++++++++++++++++++++ 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/core/libs/prx/libkernel/File/src/Stdio.cpp b/core/libs/prx/libkernel/File/src/Stdio.cpp index d53803786..1143f0ebb 100644 --- a/core/libs/prx/libkernel/File/src/Stdio.cpp +++ b/core/libs/prx/libkernel/File/src/Stdio.cpp @@ -138,7 +138,7 @@ std::int64_t NativePositioned_nid_no_patch(int descriptor, void* buf, std::size_ } if (!ok) { if (!write && error == ERROR_HANDLE_EOF) return 0; - errno = EIO; + errno = error == ERROR_ACCESS_DENIED ? EBADF : EIO; return -1; } return done; @@ -313,23 +313,24 @@ int64_t APS5_VABI fstat_nid_disambig1_nid_postfix(int d, FileStat* sb) { int APS5_VABI ftruncate_nid_postfix(int d, int64_t length) { if (length < 0) { - APS5_INVALID_ARG_EX; + return PosixFailure(GUEST_EINVAL); } #ifdef _WIN32 int error = NativeFtruncate(d, length); if (error != 0) { - throw std::runtime_error(std::string(__func__) + ": ftruncate failed, fd=" + std::to_string(d) + ", error=" + std::to_string(error)); + if (error == EACCES) error = GUEST_EINVAL; + return PosixFailure(SceErrorFromErrno(error) & 0xffff); } #else if (NativeFtruncate(d, length) != 0) { - throw std::runtime_error(std::string(__func__) + ": ftruncate failed, fd=" + std::to_string(d) + ", errno=" + std::to_string(errno)); + return PosixFailure(SceErrorFromErrno(errno) & 0xffff); } #endif return 0; } int APS5_VABI sceKernelFtruncate(int d, int64_t length) { - return ftruncate_nid_postfix(d, length); + return ftruncate_nid_postfix(d, length) == 0 ? 0 : SceErrorFromErrno(*__error_nid_postfix()); } int64_t APS5_VABI lseek_nid_postfix(int d, int64_t offset, int whence) { @@ -577,11 +578,21 @@ static bool OpenIovecs(const KernelIovec* iov, int iovcnt, std::deque= 0); + Require(sceKernelPwrite(readOnly, "X", 1, 0) == ErrorEbadf); + Require(sceKernelFtruncate(readOnly, 0) == ErrorEinval); + Require(sceKernelClose(readOnly) == 0); + const int writeOnly = sceKernelOpen(path.string().c_str(), SCE_KERNEL_O_WRONLY, 0); + Require(writeOnly >= 0); + Require(sceKernelPread(writeOnly, &byte, 1, 0) == ErrorEbadf); + Require(sceKernelClose(writeOnly) == 0); + Require(Contents(path) == "01ABCDE7ABCDE"); + Require(sceKernelFtruncate(file, 5) == 0); + Require(Contents(path) == "01ABC"); + Require(sceKernelClose(file) == 0); Require(sceKernelReadv(file, reads, 2) == ErrorEbadf); Require(sceKernelWritev(file, writes, 2) == ErrorEbadf); Require(sceKernelPreadv(file, reads, 2, 0) == ErrorEbadf); Require(sceKernelPwritev(file, writes, 2, 0) == ErrorEbadf); + Require(sceKernelPread(file, &byte, 1, 0) == ErrorEbadf); + Require(sceKernelPwrite(file, &byte, 1, 0) == ErrorEbadf); + Require(sceKernelFtruncate(file, 0) == ErrorEbadf); int ends[2] = {}; Require(MakePipe(ends) == 0);