#include "SceTypes.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include "prx/libkernel/File/include/FileFlags.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #else #include #include #endif extern "C" { int APS5_VABI open_nid_postfix(const char*, int, int); int APS5_VABI close_nid_postfix(int); int APS5_VABI pipe_nid_postfix(int*); int* APS5_VABI __error_nid_postfix(); 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); std::int64_t APS5_VABI _read_nid_postfix(int, void*, std::size_t); std::int64_t APS5_VABI _write_nid_postfix(int, const void*, std::size_t); std::int64_t APS5_VABI lseek_nid_postfix(int, std::int64_t, int); std::int64_t APS5_VABI sceKernelRead(int, void*, std::size_t); std::int64_t APS5_VABI sceKernelWrite(int, const void*, std::size_t); std::int64_t APS5_VABI sceKernelLseek(int, std::int64_t, int); } static void Check(bool value, int line) { if (!value) { std::fprintf(stderr, "POSIX scalar I/O check failed at line %d (guest errno %d)\n", line, *__error_nid_postfix()); std::abort(); } } #define Require(value) Check((value), __LINE__) #ifndef _WIN32 static constexpr int GuestEagain = 35; static constexpr int GuestEisdir = 21; #endif template static void CheckPosixFailure(Operation operation, int expectedError, int line) { std::int64_t result = 0; try { result = operation(); } catch (const std::exception& error) { std::fprintf(stderr, "POSIX syscall threw instead of returning -1: %s\n", error.what()); Check(false, line); return; } Check(result == -1 && *__error_nid_postfix() == expectedError, line); } #define RequirePosixFailure(operation, error) CheckPosixFailure((operation), (error), __LINE__) int main() { const auto root = std::filesystem::path("anyps5-posix-scalar-io-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count())); Require(std::filesystem::create_directory(root)); const auto file = root / "scalar.txt"; { std::ofstream stream(file, std::ios::binary); stream << "posix"; } const auto fileName = file.string(); const int descriptor = open_nid_postfix(fileName.c_str(), SCE_KERNEL_O_RDWR, 0); Require(descriptor >= 0); int* guestErrno = __error_nid_postfix(); char buffer[3]{}; *guestErrno = EACCES; Require(lseek_nid_postfix(descriptor, 0, SEEK_SET) == 0); Require(*guestErrno == EACCES); *guestErrno = EACCES; Require(read_nid_postfix(descriptor, buffer, 1) == 1 && buffer[0] == 'p'); Require(*guestErrno == EACCES); *guestErrno = EACCES; Require(_read_nid_postfix(descriptor, buffer + 1, 1) == 1 && buffer[1] == 'o'); Require(*guestErrno == EACCES); constexpr std::int64_t sparseOffset = (std::int64_t{1} << 32) + 17; *guestErrno = EACCES; Require(lseek_nid_postfix(descriptor, sparseOffset, SEEK_SET) == sparseOffset); Require(*guestErrno == EACCES); *guestErrno = EACCES; Require(lseek_nid_postfix(descriptor, 0, SEEK_SET) == 0); Require(*guestErrno == EACCES); const char payload[] = {'P', 'S'}; *guestErrno = EACCES; Require(write_nid_postfix(descriptor, payload, 1) == 1); Require(*guestErrno == EACCES); *guestErrno = EACCES; Require(_write_nid_postfix(descriptor, payload + 1, 1) == 1); Require(*guestErrno == EACCES); *guestErrno = EACCES; Require(lseek_nid_postfix(descriptor, 0, SEEK_SET) == 0); Require(*guestErrno == EACCES); Require(_read_nid_postfix(descriptor, buffer, 2) == 2 && buffer[0] == 'P' && buffer[1] == 'S'); RequirePosixFailure([&] { return read_nid_postfix(-1, buffer, 1); }, EBADF); RequirePosixFailure([&] { return _read_nid_postfix(-1, buffer, 1); }, EBADF); RequirePosixFailure([&] { return write_nid_postfix(-1, payload, 1); }, EBADF); RequirePosixFailure([&] { return _write_nid_postfix(-1, payload, 1); }, EBADF); RequirePosixFailure([&] { return lseek_nid_postfix(-1, 0, SEEK_SET); }, EBADF); RequirePosixFailure([&] { return lseek_nid_postfix(descriptor, 0, -1); }, EINVAL); RequirePosixFailure([&] { return lseek_nid_postfix(descriptor, 0, 3); }, EINVAL); RequirePosixFailure([&] { return read_nid_postfix(descriptor, nullptr, 1); }, EFAULT); RequirePosixFailure([&] { return _read_nid_postfix(descriptor, nullptr, 1); }, EFAULT); RequirePosixFailure([&] { return write_nid_postfix(descriptor, nullptr, 1); }, EFAULT); RequirePosixFailure([&] { return _write_nid_postfix(descriptor, nullptr, 1); }, EFAULT); Require(read_nid_postfix(descriptor, nullptr, 0) == 0); Require(_read_nid_postfix(descriptor, nullptr, 0) == 0); Require(write_nid_postfix(descriptor, nullptr, 0) == 0); Require(_write_nid_postfix(descriptor, nullptr, 0) == 0); RequirePosixFailure([&] { return read_nid_postfix(-1, nullptr, 0); }, EBADF); RequirePosixFailure([&] { return _read_nid_postfix(-1, nullptr, 0); }, EBADF); RequirePosixFailure([&] { return write_nid_postfix(-1, nullptr, 0); }, EBADF); RequirePosixFailure([&] { return _write_nid_postfix(-1, nullptr, 0); }, EBADF); #ifdef _WIN32 errno = EACCES; bool limitDiagnosticPreserved = false; try { write_nid_postfix(descriptor, payload, static_cast(std::numeric_limits::max()) + 1); } catch (const std::runtime_error& error) { limitDiagnosticPreserved = std::string(error.what()).find("nbytes exceeds platform limit") != std::string::npos; } Require(limitDiagnosticPreserved && errno == EACCES); #endif char direct[1]{}; Require(sceKernelRead(-1, direct, 1) == static_cast(0x80020009u)); Require(sceKernelWrite(-1, direct, 1) == static_cast(0x80020009u)); Require(sceKernelLseek(-1, 0, 0) == static_cast(0x80020009u)); Require(close_nid_postfix(descriptor) == 0); const int readOnly = open_nid_postfix(fileName.c_str(), SCE_KERNEL_O_RDONLY, 0); Require(readOnly >= 0); RequirePosixFailure([&] { return write_nid_postfix(readOnly, payload, 1); }, EBADF); RequirePosixFailure([&] { return _write_nid_postfix(readOnly, payload, 1); }, EBADF); Require(close_nid_postfix(readOnly) == 0); const int writeOnly = open_nid_postfix(fileName.c_str(), SCE_KERNEL_O_WRONLY, 0); Require(writeOnly >= 0); RequirePosixFailure([&] { return read_nid_postfix(writeOnly, buffer, 1); }, EBADF); RequirePosixFailure([&] { return _read_nid_postfix(writeOnly, buffer, 1); }, EBADF); Require(close_nid_postfix(writeOnly) == 0); int descriptors[2] = {-1, -1}; Require(pipe_nid_postfix(descriptors) == 0); const char pipePayload[] = {'p', 'i', 'p', 'e'}; Require(write_nid_postfix(descriptors[1], pipePayload, sizeof(pipePayload)) == sizeof(pipePayload)); Require(close_nid_postfix(descriptors[1]) == 0); char pipeBuffer[sizeof(pipePayload)]{}; Require(_read_nid_postfix(descriptors[0], pipeBuffer, sizeof(pipeBuffer)) == sizeof(pipeBuffer)); Require(std::memcmp(pipeBuffer, pipePayload, sizeof(pipePayload)) == 0); Require(close_nid_postfix(descriptors[0]) == 0); #ifndef _WIN32 int nonBlockingPipe[2] = {-1, -1}; Require(pipe_nid_postfix(nonBlockingPipe) == 0); const int pipeFlags = fcntl(nonBlockingPipe[0], F_GETFL); Require(pipeFlags >= 0 && fcntl(nonBlockingPipe[0], F_SETFL, pipeFlags | O_NONBLOCK) == 0); RequirePosixFailure([&] { return read_nid_postfix(nonBlockingPipe[0], buffer, 1); }, GuestEagain); RequirePosixFailure([&] { return _read_nid_postfix(nonBlockingPipe[0], buffer, 1); }, GuestEagain); Require(sceKernelRead(nonBlockingPipe[0], direct, 1) == static_cast(0x80020000u | GuestEagain)); Require(close_nid_postfix(nonBlockingPipe[0]) == 0); Require(close_nid_postfix(nonBlockingPipe[1]) == 0); #endif #ifndef _WIN32 const std::string rootName = root.string(); const int directory = open_nid_postfix(rootName.c_str(), SCE_KERNEL_O_RDONLY, 0); Require(directory >= 0); RequirePosixFailure([&] { return read_nid_postfix(directory, buffer, 1); }, GuestEisdir); RequirePosixFailure([&] { return _read_nid_postfix(directory, buffer, 1); }, GuestEisdir); Require(close_nid_postfix(directory) == 0); #endif Require(std::filesystem::remove_all(root) == 2); return 0; }