243 lines
11 KiB
C++
243 lines
11 KiB
C++
#include "prx/libc/include/General.hpp"
|
|
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
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<int>(0x80020009u);
|
|
static constexpr int SceEinval = static_cast<int>(0x80020016u);
|
|
static constexpr int SceEnotsup = static_cast<int>(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 <typename TCall> 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<int>(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<int>(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<int>(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;
|
|
}
|