mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 08:33:12 -05:00
On Windows, sceKernelPread and sceKernelPwrite called the POSIX pread and pwrite wrappers, which throw on every failure, so a bad descriptor, a negative offset or a write to a read-only descriptor aborted the process instead of returning an error as the Linux build does. ftruncate and sceKernelFtruncate threw on any host failure on both platforms. Give the Windows sceKernelPread and sceKernelPwrite the same checks and SceErrorFromErrno returns as the Linux ones, and accept a zero-length transfer with a null buffer. Report a positioned transfer on a handle opened without the needed access as EBADF instead of EIO. Return EINVAL from ftruncate for a negative length and for a descriptor opened without write access, as FreeBSD does, and pass other host errors to the guest. Fixes #2423
181 lines
7.8 KiB
C++
181 lines
7.8 KiB
C++
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
#include "prx/libkernel/File/include/FileFlags.hpp"
|
|
#include <chrono>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <iterator>
|
|
#include <string>
|
|
#ifdef _WIN32
|
|
#include <fcntl.h>
|
|
#include <io.h>
|
|
static int MakePipe(int* ends) { return ::_pipe(ends, 64, _O_BINARY); }
|
|
static int ClosePipe(int end) { return ::_close(end); }
|
|
#else
|
|
#include <unistd.h>
|
|
static int MakePipe(int* ends) { return ::pipe(ends); }
|
|
static int ClosePipe(int end) { return ::close(end); }
|
|
#endif
|
|
|
|
struct GuestIovec {
|
|
void* base;
|
|
std::size_t length;
|
|
};
|
|
|
|
extern "C" {
|
|
int APS5_VABI sceKernelOpen(const char*, int, std::uint16_t);
|
|
int APS5_VABI sceKernelClose(int);
|
|
std::int64_t APS5_VABI sceKernelRead(int, void*, std::size_t);
|
|
std::int64_t APS5_VABI sceKernelPread(int, void*, std::size_t, std::int64_t);
|
|
int APS5_VABI sceKernelLseek(int, std::int64_t, int);
|
|
std::int64_t APS5_VABI sceKernelReadv(int, const GuestIovec*, int);
|
|
std::int64_t APS5_VABI sceKernelWritev(int, const GuestIovec*, int);
|
|
std::int64_t APS5_VABI sceKernelPreadv(int, const GuestIovec*, int, std::int64_t);
|
|
std::int64_t APS5_VABI sceKernelPwritev(int, const GuestIovec*, int, std::int64_t);
|
|
std::int64_t APS5_VABI sceKernelPread(int, void*, std::size_t, std::int64_t);
|
|
std::int64_t APS5_VABI sceKernelPwrite(int, const void*, std::size_t, std::int64_t);
|
|
int APS5_VABI sceKernelFtruncate(int, std::int64_t);
|
|
}
|
|
|
|
static void Check(bool value, int line) {
|
|
if (!value) {
|
|
std::fprintf(stderr, "File vector check failed at line %d\n", line);
|
|
std::abort();
|
|
}
|
|
}
|
|
#define Require(value) Check((value), __LINE__)
|
|
|
|
static constexpr std::int64_t ErrorEbadf = static_cast<int>(0x80020009u);
|
|
static constexpr std::int64_t ErrorEfault = static_cast<int>(0x8002000Eu);
|
|
static constexpr std::int64_t ErrorEinval = static_cast<int>(0x80020016u);
|
|
static constexpr std::int64_t ErrorEspipe = static_cast<int>(0x8002001Du);
|
|
|
|
static std::string Contents(const std::filesystem::path& path) {
|
|
std::ifstream stream(path, std::ios::binary);
|
|
return std::string(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>());
|
|
}
|
|
|
|
int main() {
|
|
const auto root = std::filesystem::path("anyps5-file-vector-test-" +
|
|
std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
|
|
Require(std::filesystem::create_directory(root));
|
|
const auto path = root / "data.bin";
|
|
{ std::ofstream stream(path, std::ios::binary); stream << "0123456789"; }
|
|
|
|
const int file = sceKernelOpen(path.string().c_str(), SCE_KERNEL_O_RDWR, 0);
|
|
Require(file >= 0);
|
|
|
|
char first[3] = {};
|
|
char second[4] = {};
|
|
GuestIovec reads[2] = {{first, sizeof(first)}, {second, sizeof(second)}};
|
|
Require(sceKernelReadv(file, reads, 2) == 7);
|
|
Require(std::memcmp(first, "012", 3) == 0 && std::memcmp(second, "3456", 4) == 0);
|
|
Require(sceKernelLseek(file, 0, 1) == 7);
|
|
Require(sceKernelReadv(file, reads, 2) == 3);
|
|
Require(std::memcmp(first, "789", 3) == 0);
|
|
Require(sceKernelReadv(file, reads, 2) == 0);
|
|
|
|
Require(sceKernelPreadv(file, reads, 2, 1) == 7);
|
|
Require(std::memcmp(first, "123", 3) == 0 && std::memcmp(second, "4567", 4) == 0);
|
|
Require(sceKernelLseek(file, 0, 1) == 10);
|
|
Require(sceKernelPreadv(file, reads, 2, 8) == 2);
|
|
Require(std::memcmp(first, "89", 2) == 0);
|
|
|
|
char ab[] = "AB";
|
|
char cde[] = "CDE";
|
|
GuestIovec writes[2] = {{ab, 2}, {cde, 3}};
|
|
Require(sceKernelPwritev(file, writes, 2, 2) == 5);
|
|
Require(sceKernelLseek(file, 0, 1) == 10);
|
|
Require(Contents(path) == "01ABCDE789");
|
|
Require(sceKernelLseek(file, 8, 0) == 8);
|
|
Require(sceKernelWritev(file, writes, 2) == 5);
|
|
Require(sceKernelLseek(file, 0, 1) == 13);
|
|
Require(Contents(path) == "01ABCDE7ABCDE");
|
|
|
|
GuestIovec gapped[3] = {{first, 2}, {nullptr, 0}, {second, 3}};
|
|
Require(sceKernelPreadv(file, gapped, 3, 0) == 5);
|
|
Require(std::memcmp(first, "01", 2) == 0 && std::memcmp(second, "ABC", 3) == 0);
|
|
Require(sceKernelLseek(file, 2, 0) == 2);
|
|
Require(sceKernelReadv(file, gapped, 3) == 5);
|
|
Require(std::memcmp(first, "AB", 2) == 0 && std::memcmp(second, "CDE", 3) == 0);
|
|
Require(sceKernelLseek(file, 0, 1) == 7);
|
|
|
|
GuestIovec empty[1] = {{nullptr, 0}};
|
|
Require(sceKernelReadv(file, empty, 1) == 0);
|
|
Require(sceKernelReadv(file, reads, 0) == 0);
|
|
Require(sceKernelWritev(file, nullptr, 0) == 0);
|
|
|
|
Require(sceKernelReadv(file, reads, -1) == ErrorEinval);
|
|
Require(sceKernelWritev(file, writes, -1) == ErrorEinval);
|
|
Require(sceKernelPreadv(file, reads, -1, 0) == ErrorEinval);
|
|
Require(sceKernelPwritev(file, writes, -1, 0) == ErrorEinval);
|
|
Require(sceKernelReadv(file, reads, 1025) == ErrorEinval);
|
|
Require(sceKernelWritev(file, writes, 1025) == ErrorEinval);
|
|
Require(sceKernelReadv(file, nullptr, 1) == ErrorEfault);
|
|
Require(sceKernelWritev(file, nullptr, 1) == ErrorEfault);
|
|
Require(sceKernelPreadv(file, nullptr, 1, 0) == ErrorEfault);
|
|
Require(sceKernelPwritev(file, nullptr, 1, 0) == ErrorEfault);
|
|
Require(sceKernelPreadv(file, reads, 2, -1) == ErrorEinval);
|
|
Require(sceKernelPwritev(file, writes, 2, -1) == ErrorEinval);
|
|
Require(Contents(path) == "01ABCDE7ABCDE");
|
|
|
|
char byte = 0;
|
|
Require(sceKernelPread(file, &byte, 1, 2) == 1 && byte == 'A');
|
|
Require(sceKernelPread(file, nullptr, 0, 0) == 0);
|
|
Require(sceKernelPwrite(file, nullptr, 0, 0) == 0);
|
|
Require(sceKernelPread(file, nullptr, 1, 0) == ErrorEfault);
|
|
Require(sceKernelPwrite(file, nullptr, 1, 0) == ErrorEfault);
|
|
Require(sceKernelPread(file, &byte, 1, -1) == ErrorEinval);
|
|
Require(sceKernelPwrite(file, &byte, 1, -1) == ErrorEinval);
|
|
Require(sceKernelFtruncate(file, -1) == ErrorEinval);
|
|
const int readOnly = sceKernelOpen(path.string().c_str(), SCE_KERNEL_O_RDONLY, 0);
|
|
Require(readOnly >= 0);
|
|
Require(sceKernelPwrite(readOnly, "X", 1, 0) == ErrorEbadf);
|
|
Require(sceKernelFtruncate(readOnly, 0) == ErrorEinval);
|
|
Require(sceKernelClose(readOnly) == 0);
|
|
const int writeOnly = sceKernelOpen(path.string().c_str(), SCE_KERNEL_O_WRONLY, 0);
|
|
Require(writeOnly >= 0);
|
|
Require(sceKernelPread(writeOnly, &byte, 1, 0) == ErrorEbadf);
|
|
Require(sceKernelClose(writeOnly) == 0);
|
|
Require(Contents(path) == "01ABCDE7ABCDE");
|
|
Require(sceKernelFtruncate(file, 5) == 0);
|
|
Require(Contents(path) == "01ABC");
|
|
|
|
Require(sceKernelClose(file) == 0);
|
|
Require(sceKernelReadv(file, reads, 2) == ErrorEbadf);
|
|
Require(sceKernelWritev(file, writes, 2) == ErrorEbadf);
|
|
Require(sceKernelPreadv(file, reads, 2, 0) == ErrorEbadf);
|
|
Require(sceKernelPwritev(file, writes, 2, 0) == ErrorEbadf);
|
|
Require(sceKernelPread(file, &byte, 1, 0) == ErrorEbadf);
|
|
Require(sceKernelPwrite(file, &byte, 1, 0) == ErrorEbadf);
|
|
Require(sceKernelFtruncate(file, 0) == ErrorEbadf);
|
|
|
|
int ends[2] = {};
|
|
Require(MakePipe(ends) == 0);
|
|
char xyz[] = "xyz";
|
|
GuestIovec message[1] = {{xyz, 3}};
|
|
Require(sceKernelWritev(ends[1], message, 1) == 3);
|
|
Require(sceKernelReadv(ends[0], reads, 2) == 3);
|
|
Require(std::memcmp(first, "xyz", 3) == 0);
|
|
Require(sceKernelPreadv(ends[0], reads, 2, 0) == ErrorEspipe);
|
|
Require(sceKernelPwritev(ends[1], message, 1, 0) == ErrorEspipe);
|
|
Require(ClosePipe(ends[0]) == 0 && ClosePipe(ends[1]) == 0);
|
|
|
|
const int random = sceKernelOpen("/dev/urandom", 0, 0);
|
|
Require(random >= 0);
|
|
unsigned char noise[48]{};
|
|
GuestIovec pieces[2] = {{noise, 16}, {noise + 16, 16}};
|
|
Require(sceKernelReadv(random, pieces, 2) == 32);
|
|
Require(sceKernelPreadv(random, pieces, 1, 1000) == 16);
|
|
Require(sceKernelPread(random, noise + 32, 16, 1000) == 16);
|
|
Require(std::memcmp(noise, noise + 16, 16) != 0 && std::memcmp(noise + 16, noise + 32, 16) != 0);
|
|
Require(sceKernelClose(random) == 0);
|
|
|
|
std::filesystem::remove_all(root);
|
|
return 0;
|
|
}
|