#include "prx/libkernel/include/Api.hpp" #include "prx/libkernel/File/include/File.hpp" #include "prx/libc/include/Api.hpp" #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 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); constexpr auto oversized = static_cast(std::numeric_limits::max()) + 1; RequirePosixFailure([&] { return read_nid_postfix(descriptor, buffer, oversized); }, EINVAL); RequirePosixFailure([&] { return _read_nid_postfix(descriptor, buffer, oversized); }, EINVAL); RequirePosixFailure([&] { return write_nid_postfix(descriptor, payload, -1); }, EINVAL); RequirePosixFailure([&] { return write_nid_postfix(descriptor, payload, std::numeric_limits::min()); }, EINVAL); RequirePosixFailure([&] { return _write_nid_postfix(descriptor, payload, oversized); }, EINVAL); RequirePosixFailure([&] { return pread_nid_postfix(descriptor, buffer, oversized, 0); }, EINVAL); RequirePosixFailure([&] { return pwrite_nid_disambig1_nid_postfix(descriptor, payload, oversized, 0); }, EINVAL); RequirePosixFailure([&] { return read_nid_postfix(-1, buffer, oversized); }, EINVAL); RequirePosixFailure([&] { return write_nid_postfix(-1, payload, -1); }, EINVAL); #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(sceKernelRead(descriptor, nullptr, 1) == static_cast(0x8002000eu)); Require(sceKernelWrite(descriptor, nullptr, 1) == static_cast(0x8002000eu)); Require(sceKernelRead(descriptor, nullptr, 0) == 0); Require(sceKernelWrite(descriptor, nullptr, 0) == 0); Require(sceKernelRead(-1, nullptr, 0) == static_cast(0x80020009u)); Require(sceKernelWrite(-1, nullptr, 0) == static_cast(0x80020009u)); FileStat nullStat{}; Require(sceKernelStat(nullptr, &nullStat) == static_cast(0x8002000eu)); Require(sceKernelStat(nullptr, nullptr) == static_cast(0x8002000eu)); Require(sceKernelUnlink(nullptr) == static_cast(0x8002000eu)); Require(sceKernelTruncate_nid_postfix(nullptr, 0) == static_cast(0x8002000eu)); Require(sceKernelUtimes_nid_postfix(nullptr, nullptr) == static_cast(0x8002000eu)); Require(sceKernelMkdir(nullptr, 0777) == static_cast(0x8002000eu)); Require(sceKernelRmdir(nullptr) == static_cast(0x8002000eu)); Require(sceKernelRename(nullptr, "dest") == static_cast(0x8002000eu)); Require(sceKernelRename("src", nullptr) == static_cast(0x80020002u)); Require(sceKernelRename(nullptr, nullptr) == static_cast(0x8002000eu)); 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); int capacityDescriptors[2] = {-1, -1}; Require(pipe_nid_postfix(capacityDescriptors) == 0); std::string largePayload(8192, 'x'); Require(write_nid_postfix(capacityDescriptors[1], largePayload.data(), largePayload.size()) == static_cast(largePayload.size())); Require(close_nid_postfix(capacityDescriptors[1]) == 0); std::string largeBuffer(8192, '\0'); Require(_read_nid_postfix(capacityDescriptors[0], largeBuffer.data(), largeBuffer.size()) == static_cast(largeBuffer.size())); Require(largeBuffer == largePayload); Require(close_nid_postfix(capacityDescriptors[0]) == 0); #ifdef _WIN32 constexpr int GuestEpipe = 32; int brokenPipe[2] = {-1, -1}; Require(pipe_nid_postfix(brokenPipe) == 0); Require(close_nid_postfix(brokenPipe[0]) == 0); RequirePosixFailure([&] { return write_nid_postfix(brokenPipe[1], pipePayload, 1); }, GuestEpipe); RequirePosixFailure([&] { return _write_nid_postfix(brokenPipe[1], pipePayload, 1); }, GuestEpipe); Require(sceKernelWrite(brokenPipe[1], pipePayload, 1) == static_cast(0x80020000u | GuestEpipe)); char brokenByte = 'b'; const KernelIovec brokenVector{&brokenByte, 1}; RequirePosixFailure([&] { return writev_nid_postfix(brokenPipe[1], &brokenVector, 1); }, GuestEpipe); Require(sceKernelWritev(brokenPipe[1], &brokenVector, 1) == static_cast(0x80020000u | GuestEpipe)); Require(close_nid_postfix(brokenPipe[1]) == 0); #endif #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; }