AnyPS5/core/libs/tests/GuestPosixScalarIo.cpp

180 lines
8.5 KiB
C++

#include "SceTypes.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include "prx/libkernel/File/include/FileFlags.hpp"
#include <cerrno>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <exception>
#include <filesystem>
#include <fstream>
#include <limits>
#include <stdexcept>
#include <string>
#ifdef _WIN32
#include <io.h>
#else
#include <fcntl.h>
#include <unistd.h>
#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 <typename Operation>
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::int64_t>(std::numeric_limits<unsigned int>::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<std::int32_t>(0x80020009u));
Require(sceKernelWrite(-1, direct, 1) == static_cast<std::int32_t>(0x80020009u));
Require(sceKernelLseek(-1, 0, 0) == static_cast<std::int32_t>(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<std::int32_t>(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;
}