AnyPS5/core/libs/tests/GuestDirectoryEntries.cpp

151 lines
7.4 KiB
C++

#include "prx/libc/include/general/VabiMacros.hpp"
#include "prx/libkernel/File/include/FileFlags.hpp"
#include <array>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <map>
#include <string>
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);
int APS5_VABI sceKernelLseek(int, std::int64_t, int);
int APS5_VABI sceKernelGetdents(int, char*, int);
int APS5_VABI sceKernelGetdirentries(int, char*, int, std::int64_t*);
int APS5_VABI getdents_nid_postfix(int, char*, int);
int APS5_VABI getdirentries_nid_postfix(int, char*, int, std::int64_t*);
int* APS5_VABI __error_nid_postfix();
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);
}
static void Check(bool value, int line) {
if (!value) {
std::fprintf(stderr, "Directory entry check failed at line %d\n", line);
std::abort();
}
}
#define Require(value) Check((value), __LINE__)
static constexpr int ErrorEinval = static_cast<int>(0x80020016u);
static constexpr int ErrorEfault = static_cast<int>(0x8002000Eu);
static constexpr int ErrorEbadf = static_cast<int>(0x80020009u);
struct Entry {
std::uint8_t type;
std::uint16_t recordLength;
};
static int Collect(const char* buffer, int bytes, std::map<std::string, Entry>& entries) {
int records = 0;
for (int offset = 0; offset < bytes; ++records) {
std::uint16_t recordLength = 0;
std::memcpy(&recordLength, buffer + offset + 4, sizeof(recordLength));
const auto nameLength = static_cast<std::uint8_t>(buffer[offset + 7]);
const std::string name(buffer + offset + 8);
Require(name.size() == nameLength);
Require(recordLength == ((8 + nameLength + 1 + 3) & ~3));
Require(offset + recordLength <= bytes);
Require(entries.emplace(name, Entry{static_cast<std::uint8_t>(buffer[offset + 6]), recordLength}).second);
offset += recordLength;
}
return records;
}
int main() {
const auto root = std::filesystem::path("anyps5-directory-entries-test-" +
std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
Require(std::filesystem::create_directory(root));
const std::string longName = "a-much-longer-entry-name-0123456789";
{ std::ofstream stream(root / "data.bin", std::ios::binary); stream << "0123456789"; }
{ std::ofstream stream(root / "b"); }
{ std::ofstream stream(root / longName); }
Require(std::filesystem::create_directory(root / "sub"));
const int directory = sceKernelOpen(root.string().c_str(), SCE_KERNEL_O_RDONLY | SCE_KERNEL_O_DIRECTORY, 0);
Require(directory >= 0);
std::array<char, 48> small{};
std::map<std::string, Entry> entries;
int calls = 0;
std::int64_t previousBase = -1;
for (;;) {
std::int64_t base = -1;
const int read = sceKernelGetdirentries(directory, small.data(), static_cast<int>(small.size()), &base);
Require(read >= 0 && read <= static_cast<int>(small.size()));
Require(base >= 0 && base != previousBase);
if (calls == 0) Require(base == 0);
previousBase = base;
if (read == 0) break;
Require(Collect(small.data(), read, entries) > 0);
++calls;
}
Require(calls >= 3);
Require(entries.size() == 6);
for (const char* name : {".", "..", "data.bin", "b", "sub"}) Require(entries.contains(name));
Require(entries.contains(longName) && entries[longName].recordLength == 44);
Require(entries["b"].recordLength == 12);
Require(entries["sub"].type == 4 || entries["sub"].type == 0);
Require(entries["data.bin"].type == 8 || entries["data.bin"].type == 0);
Require(sceKernelLseek(directory, 0, 0) == 0);
std::array<char, 8> tooSmall{};
Require(sceKernelGetdents(directory, tooSmall.data(), static_cast<int>(tooSmall.size())) == ErrorEinval);
Require(sceKernelGetdents(directory, nullptr, 64) == ErrorEfault);
Require(sceKernelGetdents(directory, small.data(), 0) == ErrorEinval);
std::array<char, 4096> large{};
std::map<std::string, Entry> all;
const int readAll = sceKernelGetdents(directory, large.data(), static_cast<int>(large.size()));
Require(readAll > 0 && Collect(large.data(), readAll, all) == 6);
Require(sceKernelGetdents(directory, large.data(), static_cast<int>(large.size())) == 0);
Require(sceKernelLseek(directory, 0, 0) == 0);
Require(getdents_nid_postfix(directory, tooSmall.data(), static_cast<int>(tooSmall.size())) == -1 && *__error_nid_postfix() == 22);
*__error_nid_postfix() = 0;
Require(getdents_nid_postfix(directory, nullptr, 64) == -1 && *__error_nid_postfix() == 14);
*__error_nid_postfix() = 0;
Require(getdirentries_nid_postfix(directory, small.data(), 0, nullptr) == -1 && *__error_nid_postfix() == 22);
std::int64_t posixBase = -1;
*__error_nid_postfix() = 1234;
const int firstPart = getdirentries_nid_postfix(directory, small.data(), static_cast<int>(small.size()), &posixBase);
Require(firstPart > 0 && posixBase == 0 && *__error_nid_postfix() == 1234);
const int secondPart = getdirentries_nid_postfix(directory, small.data(), static_cast<int>(small.size()), &posixBase);
Require(secondPart > 0 && posixBase > 0 && *__error_nid_postfix() == 1234);
Require(sceKernelLseek(directory, 0, 0) == 0);
std::map<std::string, Entry> posix;
const int posixRead = getdirentries_nid_postfix(directory, large.data(), static_cast<int>(large.size()), &posixBase);
Require(posixRead == readAll && posixBase == 0 && Collect(large.data(), posixRead, posix) == 6);
Require(getdents_nid_postfix(directory, large.data(), static_cast<int>(large.size())) == 0);
Require(sceKernelLseek(directory, 0, 0) == 0);
std::map<std::string, Entry> dents;
const int dentsRead = getdents_nid_postfix(directory, large.data(), static_cast<int>(large.size()));
Require(dentsRead == readAll && Collect(large.data(), dentsRead, dents) == 6);
Require(sceKernelClose(directory) == 0);
Require(getdents_nid_postfix(directory, large.data(), static_cast<int>(large.size())) == -1 && *__error_nid_postfix() == 9);
*__error_nid_postfix() = 0;
Require(getdirentries_nid_postfix(directory, large.data(), static_cast<int>(large.size()), nullptr) == -1 && *__error_nid_postfix() == 9);
const int file = sceKernelOpen((root / "data.bin").string().c_str(), SCE_KERNEL_O_RDWR, 0);
Require(file >= 0);
Require(sceKernelLseek(file, 2, 0) == 2);
char bytes[16] = {};
Require(sceKernelPread(file, bytes, 4, 5) == 4 && std::memcmp(bytes, "5678", 4) == 0);
Require(sceKernelLseek(file, 0, 1) == 2);
Require(sceKernelPwrite(file, "XY", 2, 8) == 2);
Require(sceKernelLseek(file, 0, 1) == 2);
Require(sceKernelRead(file, bytes, 3) == 3 && std::memcmp(bytes, "234", 3) == 0);
Require(sceKernelPread(file, bytes, sizeof(bytes), 0) == 10 && std::memcmp(bytes, "01234567XY", 10) == 0);
Require(sceKernelPread(file, bytes, sizeof(bytes), 10) == 0);
Require(sceKernelPread(file, bytes, 1, -1) == ErrorEinval);
Require(sceKernelPwrite(file, nullptr, 1, 0) == ErrorEfault);
Require(sceKernelClose(file) == 0);
Require(sceKernelPread(file, bytes, 1, 0) == ErrorEbadf);
std::filesystem::remove_all(root);
}