#include "prx/libc/include/General.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include #include #include #include #include extern "C" { int APS5_VABI socket_nid_postfix(int, int, int); int APS5_VABI close_nid_postfix(int); int APS5_VABI pipe_nid_postfix(int*); int APS5_VABI fcntl_nid_postfix(int, int, ...); int APS5_VABI ioctl_nid_postfix(int, std::uint64_t, void*); std::int64_t APS5_VABI read_nid_postfix(int, void*, std::uint64_t); std::int64_t APS5_VABI write_nid_postfix(int, const char*, std::int64_t); int* APS5_VABI __error_nid_postfix(); int APS5_VABI sceKernelFcntl(int, int, ...); int APS5_VABI sceKernelOpen(const char*, int, std::uint16_t); int APS5_VABI sceKernelClose(int); std::int64_t APS5_VABI sceKernelLseek(int, std::int64_t, int); } static void Require(bool value) { if (!value) std::abort(); } static constexpr int DuplicateCommand = 0; static constexpr int GetDescriptorFlags = 1; static constexpr int SetDescriptorFlags = 2; static constexpr int GetFlags = 3; static constexpr int SetFlags = 4; static constexpr int GetLock = 11; static constexpr int SetLock = 12; static constexpr int ReadAhead = 15; static constexpr int DarwinReadAhead = 16; static constexpr int DuplicateCloseOnExec = 17; static constexpr int ReadOnly = 0; static constexpr int WriteOnly = 1; static constexpr int ReadWrite = 2; static constexpr int NonBlocking = 4; static constexpr int Append = 8; static constexpr int ReadAheadFlag = 0x200; static constexpr int CreateReadWrite = 0x202; static constexpr int CreateWriteAppend = 0x209; static constexpr int GuestEbadf = 9; static constexpr int GuestEfault = 14; static constexpr int GuestEinval = 22; static constexpr int GuestEnotty = 25; static constexpr int GuestEagain = 35; static constexpr std::uint64_t NonblockingRequest = 0x8004667e; static constexpr std::uint64_t ReadableRequest = 0x4004667f; static constexpr std::uint64_t WindowSizeRequest = 0x40087468; static constexpr std::uint64_t AsyncRequest = 0x8004667d; static constexpr int SceEbadf = static_cast(0x80020009u); static constexpr int SceEinval = static_cast(0x80020016u); static constexpr int SceEnotsup = static_cast(0x8002002du); struct GuestLock { std::int64_t start; std::int64_t length; std::int32_t pid; std::int16_t type; std::int16_t whence; std::int32_t system; }; static void RequireFailure(int result, int error) { Require(result == -1); Require(*__error_nid_postfix() == error); } template static bool ThrowsWith(TCall call, const std::string& prefix) { try { call(); } catch (const std::runtime_error& error) { return std::string(error.what()).rfind(prefix, 0) == 0; } return false; } static void SocketsKeepTheirPath() { const int socket = socket_nid_postfix(2, 2, 0); Require(socket >= 0); Require(sceKernelFcntl(socket, GetFlags) == ReadWrite); Require(sceKernelFcntl(socket, SetFlags, ReadWrite | NonBlocking) == 0); Require(sceKernelFcntl(socket, GetFlags) == (ReadWrite | NonBlocking)); Require(sceKernelFcntl(socket, SetFlags, 0) == 0); Require(sceKernelFcntl(socket, GetFlags) == ReadWrite); Require(sceKernelFcntl(socket, SetFlags, 0x8000) == SceEnotsup); Require(sceKernelFcntl(socket, GetFlags) == ReadWrite); Require(sceKernelFcntl(socket, 5) == SceEinval); Require(sceKernelFcntl(socket + 4096, GetFlags) == SceEbadf); int queued = -1; Require(ioctl_nid_postfix(socket, ReadableRequest, &queued) == 0 && queued == 0); Require(close_nid_postfix(socket) == 0); Require(sceKernelFcntl(socket, GetFlags) == SceEbadf); Require(sceKernelFcntl(-1, GetFlags) == SceEbadf); Require(sceKernelFcntl(12345, GetFlags) == SceEbadf); } static void FileFlagsRoundTrip() { const int file = sceKernelOpen("kernel_fcntl_probe.bin", CreateReadWrite, 0644); Require(file >= 0); Require(sceKernelFcntl(file, GetFlags) == ReadWrite); Require(fcntl_nid_postfix(file, SetFlags, ReadWrite | NonBlocking | 0x8000) == 0); Require(fcntl_nid_postfix(file, GetFlags) == (ReadWrite | NonBlocking)); Require(fcntl_nid_postfix(file, SetFlags, 0) == 0); Require(fcntl_nid_postfix(file, GetFlags) == ReadWrite); Require(fcntl_nid_postfix(file, GetDescriptorFlags) == 0); Require(fcntl_nid_postfix(file, SetDescriptorFlags, 1) == 0); Require(fcntl_nid_postfix(file, GetDescriptorFlags) == 1); Require(sceKernelFcntl(file, SetDescriptorFlags, 0) == 0); Require(sceKernelFcntl(file, GetDescriptorFlags) == 0); Require(fcntl_nid_postfix(file, DarwinReadAhead, 1) == 0); Require(fcntl_nid_postfix(file, GetFlags) == (ReadWrite | ReadAheadFlag)); Require(fcntl_nid_postfix(file, ReadAhead, -1) == 0); Require(fcntl_nid_postfix(file, GetFlags) == ReadWrite); Require(sceKernelFcntl(file, ReadAhead, 4096) == 0); Require(fcntl_nid_postfix(file, GetFlags) == (ReadWrite | ReadAheadFlag)); Require(sceKernelFcntl(file, ReadAhead, -1) == 0); Require(sceKernelFcntl(file, GetFlags) == ReadWrite); Require(fcntl_nid_postfix(file, DarwinReadAhead, 1) == 0); const int duplicate = fcntl_nid_postfix(file, DuplicateCommand, 20); Require(duplicate >= 20); Require(fcntl_nid_postfix(duplicate, GetDescriptorFlags) == 0); Require(fcntl_nid_postfix(file, SetFlags, NonBlocking) == 0); Require(fcntl_nid_postfix(duplicate, GetFlags) == (ReadWrite | NonBlocking)); const int closing = fcntl_nid_postfix(file, DuplicateCloseOnExec, duplicate); Require(closing > duplicate); Require(fcntl_nid_postfix(closing, GetDescriptorFlags) == 1); Require(fcntl_nid_postfix(file, GetDescriptorFlags) == 0); Require(write_nid_postfix(file, "abcde", 5) == 5); Require(sceKernelLseek(file, 2, 0) == 2); int readable = -1; Require(ioctl_nid_postfix(file, ReadableRequest, &readable) == 0 && readable == 3); char size[8] = {}; RequireFailure(ioctl_nid_postfix(file, WindowSizeRequest, size), GuestEnotty); RequireFailure(ioctl_nid_postfix(file, NonblockingRequest, nullptr), GuestEfault); Require(ThrowsWith([file] { int on = 1; ioctl_nid_postfix(file, AsyncRequest, &on); }, "ioctl: request 0x8004667D on a file is not implemented")); RequireFailure(fcntl_nid_postfix(file, DuplicateCommand, -1), GuestEinval); Require(sceKernelFcntl(file, DuplicateCommand, -1) == SceEinval); RequireFailure(fcntl_nid_postfix(file, 99), GuestEinval); RequireFailure(fcntl_nid_postfix(file, GetLock, nullptr), GuestEfault); #ifdef _WIN32 GuestLock lock{0, 0, 0, 3, 0, 0}; Require(ThrowsWith([file, &lock] { fcntl_nid_postfix(file, SetLock, &lock); }, "fcntl: record locks are not implemented on Windows")); #else GuestLock lock{0, 0, 0, 3, 0, 0}; Require(fcntl_nid_postfix(file, SetLock, &lock) == 0); GuestLock probe{0, 0, 0, 1, 0, 0}; Require(fcntl_nid_postfix(file, GetLock, &probe) == 0 && probe.type == 2); lock.type = 2; Require(fcntl_nid_postfix(file, SetLock, &lock) == 0); lock.type = 7; RequireFailure(fcntl_nid_postfix(file, SetLock, &lock), GuestEinval); #endif for (const int descriptor : {closing, duplicate, file}) Require(close_nid_postfix(descriptor) == 0); RequireFailure(fcntl_nid_postfix(file, GetDescriptorFlags), GuestEbadf); RequireFailure(fcntl_nid_postfix(file, DuplicateCommand, 0), GuestEbadf); RequireFailure(ioctl_nid_postfix(file, ReadableRequest, &readable), GuestEbadf); Require(sceKernelFcntl(file, GetFlags) == SceEbadf); const int appending = sceKernelOpen("kernel_fcntl_probe.bin", CreateWriteAppend, 0644); Require(appending >= 0); Require(fcntl_nid_postfix(appending, GetFlags) == (WriteOnly | Append)); Require(fcntl_nid_postfix(appending, SetFlags, Append | NonBlocking) == 0); Require(fcntl_nid_postfix(appending, GetFlags) == (WriteOnly | Append | NonBlocking)); #ifdef _WIN32 Require(ThrowsWith([appending] { fcntl_nid_postfix(appending, SetFlags, 0); }, "fcntl: changing O_APPEND on a file is not implemented")); #else Require(fcntl_nid_postfix(appending, SetFlags, 0) == 0); Require(fcntl_nid_postfix(appending, GetFlags) == WriteOnly); #endif Require(ThrowsWith([appending] { fcntl_nid_postfix(appending, SetFlags, Append | 0x80); }, "fcntl: changing O_FSYNC on a file is not implemented")); Require(sceKernelClose(appending) == 0); std::remove("kernel_fcntl_probe.bin"); } static void PipeFlagsRoundTrip() { int pipe[2]; Require(pipe_nid_postfix(pipe) == 0); Require(fcntl_nid_postfix(pipe[0], GetFlags) == ReadOnly); Require(fcntl_nid_postfix(pipe[1], GetFlags) == WriteOnly); Require(fcntl_nid_postfix(pipe[1], SetDescriptorFlags, 1) == 0); Require(fcntl_nid_postfix(pipe[1], GetDescriptorFlags) == 1); Require(fcntl_nid_postfix(pipe[0], GetDescriptorFlags) == 0); Require(fcntl_nid_postfix(pipe[0], SetFlags, fcntl_nid_postfix(pipe[0], GetFlags) | NonBlocking) == 0); Require(fcntl_nid_postfix(pipe[0], GetFlags) == (ReadOnly | NonBlocking)); char buffer[8] = {}; RequireFailure(static_cast(read_nid_postfix(pipe[0], buffer, sizeof(buffer))), GuestEagain); Require(write_nid_postfix(pipe[1], "abc", 3) == 3); int readable = -1; Require(ioctl_nid_postfix(pipe[0], ReadableRequest, &readable) == 0 && readable == 3); Require(ioctl_nid_postfix(pipe[1], ReadableRequest, &readable) == 0 && readable == 0); Require(read_nid_postfix(pipe[0], buffer, sizeof(buffer)) == 3); RequireFailure(static_cast(read_nid_postfix(pipe[0], buffer, sizeof(buffer))), GuestEagain); int off = 0; Require(ioctl_nid_postfix(pipe[0], NonblockingRequest, &off) == 0); Require(fcntl_nid_postfix(pipe[0], GetFlags) == ReadOnly); int on = 1; Require(ioctl_nid_postfix(pipe[0], NonblockingRequest, &on) == 0); Require(fcntl_nid_postfix(pipe[0], GetFlags) == (ReadOnly | NonBlocking)); Require(fcntl_nid_postfix(pipe[1], SetFlags, NonBlocking) == 0); char block[1024] = {}; std::int64_t written = 0; std::int64_t result = 0; while (written < (1 << 22) && (result = write_nid_postfix(pipe[1], block, sizeof(block))) > 0) written += result; Require(written > 0); RequireFailure(static_cast(result), GuestEagain); RequireFailure(fcntl_nid_postfix(pipe[0], DarwinReadAhead, 1), GuestEbadf); GuestLock lock{0, 0, 0, 3, 0, 0}; RequireFailure(fcntl_nid_postfix(pipe[1], SetLock, &lock), GuestEbadf); char size[8] = {}; RequireFailure(ioctl_nid_postfix(pipe[0], WindowSizeRequest, size), GuestEnotty); Require(ThrowsWith([&pipe] { fcntl_nid_postfix(pipe[0], SetFlags, NonBlocking | 0x40); }, "fcntl: O_ASYNC on a pipe is not implemented")); for (const int descriptor : pipe) Require(close_nid_postfix(descriptor) == 0); } static void StandardInputFlags() { const int flags = fcntl_nid_postfix(0, GetFlags); Require(flags >= 0); Require((flags & 3) != WriteOnly); Require(fcntl_nid_postfix(0, SetFlags, fcntl_nid_postfix(0, GetFlags) & ~NonBlocking) == 0); Require(fcntl_nid_postfix(0, GetFlags) == (flags & ~NonBlocking)); Require(fcntl_nid_postfix(0, SetDescriptorFlags, 1) == 0); Require(fcntl_nid_postfix(0, GetDescriptorFlags) == 1); Require(fcntl_nid_postfix(0, SetDescriptorFlags, 0) == 0); Require(fcntl_nid_postfix(0, GetDescriptorFlags) == 0); } int main() { SocketsKeepTheirPath(); FileFlagsRoundTrip(); PipeFlagsRoundTrip(); StandardInputFlags(); return 0; }