AnyPS5/core/libs/tests/GuestFilesystem.cpp

699 lines
46 KiB
C++

#include "SceTypes.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include "prx/libc/include/General.hpp"
#include "prx/libc/include/FileStream.hpp"
#include "prx/libkernel/File/include/File.hpp"
#include <algorithm>
#include <chrono>
#include <cerrno>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <cstdio>
#include <cstring>
#include <stdexcept>
#ifdef _WIN32
#include <io.h>
#include <windows.h>
#else
#include <unistd.h>
#endif
extern "C" {
int APS5_VABI remove_nid_postfix(const char*);
int APS5_VABI rename_nid_postfix(const char*, const char*);
int APS5_VABI sceKernelChmod_nid_postfix(const char*, unsigned short);
int APS5_VABI sceKernelFchmod(int, unsigned short);
int APS5_VABI fchmod_nid_postfix(int, int);
int APS5_VABI chmod_nid_postfix(const char*, int);
int APS5_VABI futimes_nid_postfix(int, const KernelTimeval*);
int APS5_VABI sceKernelFutimes(int, const KernelTimeval*);
int APS5_VABI socket_nid_postfix(int, int, int);
int APS5_VABI sceKernelFsync(int);
int APS5_VABI fdatasync_nid_postfix(int);
int APS5_VABI fsync_nid_postfix(int);
int APS5_VABI sceKernelFdatasync(int);
int APS5_VABI sceKernelWriteThrottlingStatus(std::uint64_t*);
int APS5_VABI sceKernelFtruncate(int, long long);
int APS5_VABI sceKernelTruncate_nid_postfix(const char*, long long);
int APS5_VABI sceKernelUtimes_nid_postfix(const char*, const void*);
int APS5_VABI open_nid_postfix(const char*, int, int);
int APS5_VABI creat_nid_postfix(const char*, int);
int APS5_VABI _open_nid_postfix(const char*, int, ...);
int APS5_VABI close_nid_postfix(int);
int APS5_VABI flock_nid_postfix(int, int);
int APS5_VABI stat_nid_postfix(const char*, FileStat*);
int APS5_VABI lstat_nid_postfix(const char*, FileStat*);
int APS5_VABI unlink_nid_postfix(const char*);
int APS5_VABI rmdir_nid_postfix(const char*);
int APS5_VABI mkdir_nid_postfix(const char*, unsigned short);
int APS5_VABI sceKernelOpen(const char*, int, unsigned short);
int APS5_VABI sceKernelClose(int);
int APS5_VABI sceKernelStat(const char*, FileStat*);
int APS5_VABI sceKernelCheckReachability(const char*);
int APS5_VABI sceKernelFstat(int, FileStat*);
int APS5_VABI sceKernelUnlink(const char*);
int APS5_VABI sceKernelRename(const char*, const char*);
int APS5_VABI sceKernelRmdir(const char*);
int APS5_VABI sceKernelMkdir(const char*, unsigned short);
int APS5_VABI sceKernelRename(const char*, const char*);
int APS5_VABI sceKernelCheckReachability(const char*);
int APS5_VABI access_nid_postfix(const char*, int);
int APS5_VABI chdir_nid_postfix(const char*);
void* APS5_VABI opendir_nid_postfix(const char*);
FileStream* APS5_VABI fopen_nid_postfix(const char*, const char*);
FileStream* APS5_VABI freopen_nid_postfix(const char*, const char*, FileStream*);
int* APS5_VABI __error_nid_postfix();
int APS5_VABI pipe_nid_postfix(int*);
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);
int APS5_VABI sceKernelDebugOutText(int, const char*);
}
static void Check(bool value, int line) {
if (!value) {
#ifdef _WIN32
const auto nativeError = ::GetLastError();
#else
const auto nativeError = errno;
#endif
const auto guestError = *__error_nid_postfix();
#ifdef _WIN32
std::fprintf(stderr, "Filesystem check failed at line %d (guest errno %d, Win32 error %lu)\n",
line, guestError, static_cast<unsigned long>(nativeError));
#else
std::fprintf(stderr, "Filesystem check failed at line %d (guest errno %d, host errno %d)\n",
line, guestError, nativeError);
#endif
std::abort();
}
}
#define Require(value) Check((value), __LINE__)
static void TestParentTraversal() {
const auto root = std::filesystem::path("anyps5-parent-test-" +
std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
Require(std::filesystem::create_directories(root / "Existing"));
const auto present = (root / "present.txt").generic_string();
{ std::ofstream file(present); file << "preserved"; }
const auto directory = (root / "victim").generic_string();
Require(std::filesystem::create_directory(directory));
const auto checkInvalid = [&](const std::string& prefix, int error) {
const auto path = prefix + "/present.txt";
const auto missingLeaf = prefix + "/new.txt";
const int kernelError = static_cast<int>(0x80020000u | static_cast<unsigned>(error));
FileStat status{};
Require(stat_nid_postfix(path.c_str(), &status) == -1 && *__error_nid_postfix() == error);
Require(lstat_nid_postfix(path.c_str(), &status) == -1 && *__error_nid_postfix() == error);
Require(sceKernelStat(path.c_str(), &status) == kernelError);
Require(open_nid_postfix(path.c_str(), 0, 0) == -1 && *__error_nid_postfix() == error);
Require(sceKernelOpen(path.c_str(), 0x601, 0600) == kernelError);
Require(sceKernelOpen(missingLeaf.c_str(), 0x601, 0600) == kernelError);
Require(access_nid_postfix(path.c_str(), 0) == -1 && *__error_nid_postfix() == error);
Require(sceKernelCheckReachability(path.c_str()) == kernelError);
Require(chmod_nid_postfix(path.c_str(), 0400) == -1 && *__error_nid_postfix() == error);
Require(sceKernelChmod_nid_postfix(path.c_str(), 0400) == kernelError);
Require(sceKernelTruncate_nid_postfix(path.c_str(), 0) == kernelError);
Require(sceKernelUtimes_nid_postfix(path.c_str(), nullptr) == kernelError);
Require(unlink_nid_postfix(path.c_str()) == -1 && *__error_nid_postfix() == error);
Require(sceKernelUnlink(path.c_str()) == kernelError);
Require(remove_nid_postfix(path.c_str()) == -1 && *__error_nid_postfix() == error);
Require(rename_nid_postfix(path.c_str(), missingLeaf.c_str()) == -1 && *__error_nid_postfix() == error);
Require(rename_nid_postfix(present.c_str(), missingLeaf.c_str()) == -1 && *__error_nid_postfix() == error);
Require(sceKernelRename(path.c_str(), missingLeaf.c_str()) == kernelError);
Require(sceKernelRename(present.c_str(), missingLeaf.c_str()) == kernelError);
const auto victim = prefix + "/victim";
Require(rmdir_nid_postfix(victim.c_str()) == -1 && *__error_nid_postfix() == error);
Require(sceKernelRmdir(victim.c_str()) == kernelError);
Require(remove_nid_postfix(victim.c_str()) == -1 && *__error_nid_postfix() == error);
Require(mkdir_nid_postfix(missingLeaf.c_str(), 0700) == -1 && *__error_nid_postfix() == error);
Require(sceKernelMkdir(missingLeaf.c_str(), 0700) == kernelError);
Require(chdir_nid_postfix(prefix.c_str()) == -1 && *__error_nid_postfix() == error);
Require(opendir_nid_postfix(prefix.c_str()) == nullptr && *__error_nid_postfix() == error);
Require(fopen_nid_postfix(path.c_str(), "wb") == nullptr && *__error_nid_postfix() == error);
auto* stream = fopen_nid_postfix(present.c_str(), "rb");
Require(stream != nullptr);
const int descriptor = stream->GuestState().descriptor;
Require(freopen_nid_postfix(path.c_str(), "wb", stream) == nullptr && *__error_nid_postfix() == error);
Require(!DescriptorIsOpen_nid_no_patch(descriptor));
std::ifstream file(present);
std::string contents;
std::getline(file, contents);
Require(contents == "preserved" && std::filesystem::is_directory(directory));
Require(!std::filesystem::exists(root / "new.txt"));
};
checkInvalid(root.generic_string() + "/missing/..", 2);
checkInvalid(root.generic_string() + "/present.txt/..", 20);
checkInvalid(root.generic_string() + "/present.txt/child/../..", 20);
checkInvalid("/" + root.generic_string() + "/missing/..", 2);
checkInvalid(root.generic_string() + "/missing/../../" + root.generic_string(), 2);
AddPathAlias_nid_no_patch("parent-test-mount", std::filesystem::absolute(root).string().c_str());
checkInvalid("/PARENT-TEST-MOUNT/missing/..", 2);
checkInvalid("/parent-test-mount/present.txt/..", 20);
FileStat status{};
Require(sceKernelStat("/parent-test-mount/existing/../present.txt", &status) == 0 && status.st_size == 9);
RemovePathAlias_nid_no_patch("parent-test-mount");
std::error_code linkError;
std::filesystem::create_directory_symlink("Existing", root / "directory-link", linkError);
if (!linkError) {
Require(sceKernelStat((root.generic_string() + "/directory-link/../present.txt").c_str(), &status) == 0 && status.st_size == 9);
std::filesystem::create_symlink("present.txt", root / "file-link");
std::filesystem::create_directory_symlink("missing", root / "dangling-link");
checkInvalid(root.generic_string() + "/file-link/..", 20);
checkInvalid(root.generic_string() + "/dangling-link/..", 2);
}
Require(chdir_nid_postfix(root.generic_string().c_str()) == 0);
Require(sceKernelStat("missing/../present.txt", &status) == static_cast<int>(0x80020002u));
Require(sceKernelStat("present.txt/..", &status) == static_cast<int>(0x80020014u));
Require(sceKernelStat("EXISTING/../present.txt", &status) == 0 && status.st_size == 9);
const int created = sceKernelOpen("Existing/../new.txt", 0x601, 0600);
Require(created >= 0 && sceKernelClose(created) == 0);
Require(chdir_nid_postfix("/") == 0);
std::filesystem::remove_all(root);
}
int main() {
TestParentTraversal();
Require(chmod_nid_postfix(nullptr, 0600) == -1 && *__error_nid_postfix() == 14);
Require(sceKernelChmod_nid_postfix(nullptr, 0600) == static_cast<int>(0x8002000eu));
Require(chmod_nid_postfix("", 0600) == -1 && *__error_nid_postfix() == 2);
Require(chmod_nid_postfix("anyps5_missing_chmod_path", 0600) == -1 && *__error_nid_postfix() == 2);
Require(sceKernelChmod_nid_postfix("anyps5_missing_chmod_path", 0600) == static_cast<int>(0x80020002u));
Require(sceKernelDebugOutText(-1, "text") == static_cast<int>(0x80020016u));
std::uint64_t throttling[4] = {1, 2, 3, 4};
Require(sceKernelWriteThrottlingStatus(throttling) == 0);
Require(throttling[0] == 0xffffffffu && throttling[1] == 0 && throttling[2] == 0 && throttling[3] == 0);
Require(sceKernelDebugOutText(0, nullptr) == static_cast<int>(0x8002000eu));
auto* captured = std::tmpfile();
Require(captured != nullptr);
#ifdef _WIN32
const int saved = ::_dup(::_fileno(stderr));
Require(saved >= 0 && ::_dup2(::_fileno(captured), ::_fileno(stderr)) == 0);
#else
const int saved = ::dup(::fileno(stderr));
Require(saved >= 0 && ::dup2(::fileno(captured), ::fileno(stderr)) == ::fileno(stderr));
#endif
const int debugResult = sceKernelDebugOutText(3, "%s%d literal\n");
#ifdef _WIN32
Require(::_dup2(saved, ::_fileno(stderr)) == 0 && ::_close(saved) == 0);
#else
Require(::dup2(saved, ::fileno(stderr)) == ::fileno(stderr) && ::close(saved) == 0);
#endif
Require(debugResult == 0);
std::rewind(captured);
char debugText[64]{};
Require(std::fread(debugText, 1, sizeof(debugText), captured) == 23);
Require(std::strcmp(debugText, "[debug:3] %s%d literal\n") == 0);
Require(std::fclose(captured) == 0);
Require(pipe_nid_postfix(nullptr) == -1 && *__error_nid_postfix() == 14);
int descriptors[2] = {-1, -1};
Require(pipe_nid_postfix(descriptors) == 0 && descriptors[0] >= 0 && descriptors[1] >= 0);
const char payload[] = {'A', '\0', '\r', '\n', '\x1a', 'Z'};
Require(write_nid_postfix(descriptors[1], payload, sizeof(payload)) == sizeof(payload));
Require(close_nid_postfix(descriptors[1]) == 0);
char received[sizeof(payload)]{};
Require(read_nid_postfix(descriptors[0], received, sizeof(received)) == sizeof(received));
Require(std::memcmp(received, payload, sizeof(payload)) == 0);
Require(read_nid_postfix(descriptors[0], received, sizeof(received)) == 0);
{
std::filesystem::remove("creat_probe.bin");
int created = creat_nid_postfix("creat_probe.bin", 0644);
Require(created >= 0 && write_nid_postfix(created, "abc", 3) == 3);
Require(close_nid_postfix(created) == 0 && std::filesystem::file_size("creat_probe.bin") == 3);
created = creat_nid_postfix("creat_probe.bin", 0644);
Require(created >= 0 && close_nid_postfix(created) == 0 && std::filesystem::file_size("creat_probe.bin") == 0);
Require(std::filesystem::remove("creat_probe.bin"));
}
Require(close_nid_postfix(descriptors[0]) == 0);
const auto root = std::filesystem::path("anyps5-filesystem-test-" +
std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
Require(mkdir_nid_postfix(root.string().c_str(), 0700) == 0);
Require(mkdir_nid_postfix(root.string().c_str(), 0700) == -1 && *__error_nid_postfix() == 17);
Require(mkdir_nid_postfix((root / "missing" / "child").string().c_str(), 0700) == -1 && *__error_nid_postfix() == 2);
Require(mkdir_nid_postfix(nullptr, 0700) == -1 && *__error_nid_postfix() == 14);
const auto file = root / "file.txt";
{ std::ofstream stream(file); stream << "retained until removal"; }
const auto held = root / "held.txt";
const auto heldName = held.string();
{ std::ofstream stream(held); stream << "held"; }
const int heldDescriptor = sceKernelOpen(heldName.c_str(), 2, 0);
Require(heldDescriptor >= 0 && sceKernelUnlink(heldName.c_str()) == 0 && !std::filesystem::exists(held));
char heldBytes[4]{};
Require(read_nid_postfix(heldDescriptor, heldBytes, sizeof(heldBytes)) == sizeof(heldBytes) && std::memcmp(heldBytes, "held", 4) == 0);
const int recreated = sceKernelOpen(heldName.c_str(), 0x0a01, 0644);
Require(recreated >= 0 && sceKernelClose(heldDescriptor) == 0 && std::filesystem::file_size(held) == 0);
const auto moved = root / "moved.txt";
Require(rename_nid_postfix(heldName.c_str(), moved.string().c_str()) == 0 && !std::filesystem::exists(held));
Require(write_nid_postfix(recreated, "moved", 5) == 5 && sceKernelClose(recreated) == 0 && std::filesystem::file_size(moved) == 5);
const int removing = open_nid_postfix(moved.string().c_str(), 0, 0);
Require(removing >= 0 && remove_nid_postfix(moved.string().c_str()) == 0 && !std::filesystem::exists(moved));
Require(close_nid_postfix(removing) == 0);
Require(remove_nid_postfix(root.string().c_str()) == -1);
Require(*__error_nid_postfix() == 66);
Require(std::filesystem::is_regular_file(file));
Require(remove_nid_postfix((file / "invalid").string().c_str()) == -1);
Require(remove_nid_postfix("") == -1 && *__error_nid_postfix() == 2);
Require(remove_nid_postfix(nullptr) == -1 && *__error_nid_postfix() == 14);
const auto readOnly = root / "read-only.txt";
{ std::ofstream stream(readOnly); stream << "removable"; }
Require(sceKernelChmod_nid_postfix(readOnly.string().c_str(), 0400) == 0);
Require(remove_nid_postfix(readOnly.string().c_str()) == 0 && !std::filesystem::exists(readOnly));
const auto renamed = root / "renamed.txt";
{ std::ofstream stream(renamed); stream << "old contents"; }
Require(rename_nid_postfix(file.string().c_str(), renamed.string().c_str()) == 0);
Require(!std::filesystem::exists(file));
{ std::ifstream stream(renamed); std::string contents; std::getline(stream, contents);
Require(contents == "retained until removal"); }
Require(rename_nid_postfix(renamed.string().c_str(), renamed.string().c_str()) == 0);
Require(rename_nid_postfix(file.string().c_str(), renamed.string().c_str()) == -1);
Require(*__error_nid_postfix() == 2);
Require(rename_nid_postfix(renamed.string().c_str(), file.string().c_str()) == 0);
{
const auto area = root / "libc_rename";
const auto sourceFile = area / "source.txt";
const auto targetFile = area / "target.txt";
const auto sourceDirectory = area / "source_directory";
const auto emptyDirectory = area / "empty_directory";
const auto fullDirectory = area / "full_directory";
Require(std::filesystem::create_directories(area));
{ std::ofstream stream(sourceFile); stream << "source"; }
{ std::ofstream stream(targetFile); stream << "target"; }
Require(std::filesystem::create_directories(sourceDirectory / "child"));
Require(std::filesystem::create_directories(emptyDirectory));
Require(std::filesystem::create_directories(fullDirectory));
{ std::ofstream stream(fullDirectory / "entry"); stream << "entry"; }
const auto name = [](const std::filesystem::path& path) { return path.string(); };
const auto missing = name(area / "missing");
const auto absentParent = name(area / "absent" / "target");
const auto fileParent = name(sourceFile / "target");
Require(rename_nid_postfix(missing.c_str(), name(targetFile).c_str()) == -1 && *__error_nid_postfix() == 2);
Require(rename_nid_postfix(name(sourceFile).c_str(), absentParent.c_str()) == -1 && *__error_nid_postfix() == 2);
Require(rename_nid_postfix(name(sourceFile).c_str(), fileParent.c_str()) == -1 && *__error_nid_postfix() == 20);
Require(rename_nid_postfix(name(sourceFile).c_str(), name(fullDirectory).c_str()) == -1 && *__error_nid_postfix() == 21);
Require(rename_nid_postfix(name(sourceDirectory).c_str(), name(targetFile).c_str()) == -1 && *__error_nid_postfix() == 20);
Require(rename_nid_postfix(name(sourceDirectory).c_str(), name(fullDirectory).c_str()) == -1 && *__error_nid_postfix() == 66);
const auto childDestination = name(sourceDirectory / "child" / "moved");
Require(rename_nid_postfix(name(sourceDirectory).c_str(), childDestination.c_str()) == -1 && *__error_nid_postfix() == 22);
const auto childDirectory = name(sourceDirectory / "child");
Require(rename_nid_postfix(name(sourceDirectory).c_str(), childDirectory.c_str()) == -1 && *__error_nid_postfix() == 22);
Require(std::filesystem::is_regular_file(sourceFile) && std::filesystem::is_regular_file(fullDirectory / "entry"));
Require(rename_nid_postfix(name(sourceFile).c_str(), name(targetFile).c_str()) == 0);
{ std::ifstream stream(targetFile); std::string contents; std::getline(stream, contents); Require(contents == "source"); }
Require(!std::filesystem::exists(sourceFile));
Require(rename_nid_postfix(name(targetFile).c_str(), name(targetFile).c_str()) == 0);
Require(rename_nid_postfix(name(sourceDirectory).c_str(), name(emptyDirectory).c_str()) == 0);
Require(!std::filesystem::exists(sourceDirectory) && std::filesystem::is_directory(emptyDirectory / "child"));
#ifndef _WIN32
const auto dangling = area / "dangling";
std::filesystem::create_symlink(area / "nonexistent", dangling);
Require(std::filesystem::is_symlink(dangling));
const auto replacement = area / "replacement";
{ std::ofstream stream(replacement); stream << "replacement"; }
Require(rename_nid_postfix(name(replacement).c_str(), name(dangling).c_str()) == 0);
Require(!std::filesystem::is_symlink(dangling) && std::filesystem::is_regular_file(dangling));
#endif
std::filesystem::remove_all(area);
}
Require(remove_nid_postfix(file.string().c_str()) == 0);
Require(!std::filesystem::exists(file));
Require(remove_nid_postfix(file.string().c_str()) == -1 && *__error_nid_postfix() == 2);
const auto sized = root / "sized.txt";
{ std::ofstream stream(sized); stream << "0123456789abcdef"; }
Require(sceKernelChmod_nid_postfix(sized.string().c_str(), 0600) == 0);
Require(sceKernelTruncate_nid_postfix(sized.string().c_str(), 6) == 0);
Require(std::filesystem::file_size(sized) == 6);
{ std::ifstream stream(sized); std::string contents; std::getline(stream, contents);
Require(contents == "012345"); }
Require(sceKernelTruncate_nid_postfix((sized / "missing").string().c_str(), 6) == static_cast<int>(0x80020002u));
Require(sceKernelUtimes_nid_postfix(sized.string().c_str(), nullptr) == 0);
std::FILE* native = std::fopen(sized.string().c_str(), "r+b");
Require(native != nullptr);
#ifdef _WIN32
const int descriptor = _fileno(native);
#else
const int descriptor = ::fileno(native);
#endif
Require(descriptor >= 0 && sceKernelFsync(descriptor) == 0);
Require(fdatasync_nid_postfix(descriptor) == 0);
Require(sceKernelFdatasync(descriptor) == 0);
const auto ownerWrite = [&] {
return (std::filesystem::status(sized).permissions() & std::filesystem::perms::owner_write) != std::filesystem::perms::none;
};
Require(sceKernelFchmod(descriptor, 0400) == 0 && !ownerWrite());
Require(fchmod_nid_postfix(descriptor, 0600) == 0 && ownerWrite());
Require(sceKernelFtruncate(descriptor, 3) == 0);
FileStat times{};
const KernelTimeval past[2]{{1000000000, 0}, {1000000000, 500000}};
Require(sceKernelFutimes(descriptor, past) == 0);
Require(stat_nid_postfix(sized.string().c_str(), &times) == 0 && times.st_mtim.tv_sec == 1000000000);
Require(futimes_nid_postfix(descriptor, nullptr) == 0);
Require(stat_nid_postfix(sized.string().c_str(), &times) == 0 && times.st_mtim.tv_sec > 1000000000);
const KernelTimeval overflow[2]{{0, 0}, {0, 1000000}};
Require(sceKernelFutimes(descriptor, overflow) == static_cast<int>(0x80020016u));
Require(futimes_nid_postfix(descriptor, overflow) == -1 && *__error_nid_postfix() == 22);
const KernelTimeval negative[2]{{0, -1}, {0, 0}};
Require(futimes_nid_postfix(descriptor, negative) == -1 && *__error_nid_postfix() == 22);
Require(std::fclose(native) == 0);
Require(std::filesystem::file_size(sized) == 3);
Require(sceKernelFchmod(descriptor, 0600) == static_cast<int>(0x80020009u));
Require(fchmod_nid_postfix(descriptor, 0600) == -1 && *__error_nid_postfix() == 9);
Require(sceKernelFutimes(descriptor, nullptr) == static_cast<int>(0x80020009u));
Require(futimes_nid_postfix(descriptor, nullptr) == -1 && *__error_nid_postfix() == 9);
Require(fdatasync_nid_postfix(descriptor) == -1 && *__error_nid_postfix() == 9);
Require(sceKernelFdatasync(descriptor) == static_cast<int>(0x80020009u));
Require(fsync_nid_postfix(descriptor) == -1 && *__error_nid_postfix() == 9);
Require(sceKernelFsync(descriptor) == static_cast<int>(0x80020009u));
const int socket = socket_nid_postfix(2, 2, 0);
Require(socket >= 0);
Require(sceKernelFchmod(socket, 0600) == static_cast<int>(0x80020016u));
Require(fchmod_nid_postfix(socket, 0600) == -1 && *__error_nid_postfix() == 22);
Require(futimes_nid_postfix(socket, nullptr) == -1 && *__error_nid_postfix() == 22);
Require(fdatasync_nid_postfix(socket) == -1 && *__error_nid_postfix() == 22);
Require(sceKernelFdatasync(socket) == static_cast<int>(0x80020016u));
Require(fsync_nid_postfix(socket) == -1 && *__error_nid_postfix() == 22);
Require(sceKernelFsync(socket) == static_cast<int>(0x80020016u));
Require(close_nid_postfix(socket) == 0);
Require(fchmod_nid_postfix(socket, 0600) == -1 && *__error_nid_postfix() == 9);
Require(futimes_nid_postfix(socket, nullptr) == -1 && *__error_nid_postfix() == 9);
Require(fdatasync_nid_postfix(socket) == -1 && *__error_nid_postfix() == 9);
Require(sceKernelFdatasync(socket) == static_cast<int>(0x80020009u));
Require(fsync_nid_postfix(socket) == -1 && *__error_nid_postfix() == 9);
Require(sceKernelFsync(socket) == static_cast<int>(0x80020009u));
Require(sceKernelFdatasync(0x7fffffff) == static_cast<int>(0x80020009u));
Require(sceKernelFsync(0x7fffffff) == static_cast<int>(0x80020009u));
Require(remove_nid_postfix(sized.string().c_str()) == 0);
const auto present = root / "present.txt";
const auto presentName = present.string();
const auto missing = root / "missing.txt";
const auto missingName = missing.string();
const auto rootName = root.string();
{ std::ofstream stream(present); stream << "posix"; }
FileStat status{};
Require(sceKernelStat("", &status) == static_cast<int>(0x80020002u));
Require(sceKernelCheckReachability("") == static_cast<int>(0x80020002u));
Require(sceKernelRename("", presentName.c_str()) == static_cast<int>(0x80020002u));
Require(sceKernelRename(presentName.c_str(), "") == static_cast<int>(0x80020002u));
Require(sceKernelRmdir("") == static_cast<int>(0x80020002u));
Require(sceKernelMkdir("", 0700) == static_cast<int>(0x80020002u));
const auto cwdPermissions = std::filesystem::status(".").permissions();
Require(sceKernelChmod_nid_postfix("", static_cast<unsigned short>(cwdPermissions) & 07777) == static_cast<int>(0x80020002u));
Require(sceKernelTruncate_nid_postfix("", 0) == static_cast<int>(0x80020002u));
const auto cwdWriteTime = std::filesystem::last_write_time(".");
const int emptyUtimes = sceKernelUtimes_nid_postfix("", nullptr);
std::error_code restoreTimeError;
std::filesystem::last_write_time(".", cwdWriteTime, restoreTimeError);
Require(emptyUtimes == static_cast<int>(0x80020002u));
Require(stat_nid_postfix(presentName.c_str(), &status) == 0 && status.st_size == 5);
Require(stat_nid_postfix(missingName.c_str(), &status) == -1 && *__error_nid_postfix() == 2);
Require(sceKernelStat(missingName.c_str(), &status) == static_cast<int>(0x80020002u));
Require(stat_nid_postfix("", &status) == -1 && *__error_nid_postfix() == 2);
Require(stat_nid_postfix((presentName + "/").c_str(), &status) == -1 && *__error_nid_postfix() == 20);
Require(sceKernelStat((presentName + "/").c_str(), &status) == static_cast<int>(0x80020014u));
Require(sceKernelStat("\xff\xfe", &status) == static_cast<int>(0x80020002u));
Require(stat_nid_postfix("\xff\xfe", &status) == -1 && *__error_nid_postfix() == 2);
FileStat dirStatus{};
Require(stat_nid_postfix((rootName + "/").c_str(), &dirStatus) == 0 && (dirStatus.st_mode & 0170000) == 0040000);
const auto viaParent = rootName + "/../" + root.filename().string() + "/present.txt";
Require(stat_nid_postfix(viaParent.c_str(), &status) == 0 && status.st_size == 5);
Require(stat_nid_postfix(nullptr, &status) == -1 && *__error_nid_postfix() == 14);
Require(stat_nid_postfix(presentName.c_str(), nullptr) == -1 && *__error_nid_postfix() == 14);
FileStat linkStatus{};
Require(lstat_nid_postfix(presentName.c_str(), &linkStatus) == 0 && linkStatus.st_size == 5);
Require((linkStatus.st_mode & 0170000) == 0100000 && linkStatus.st_ino == status.st_ino);
Require(lstat_nid_postfix(rootName.c_str(), &linkStatus) == 0 && (linkStatus.st_mode & 0170000) == 0040000);
Require(lstat_nid_postfix(missingName.c_str(), &linkStatus) == -1 && *__error_nid_postfix() == 2);
Require(lstat_nid_postfix("", &linkStatus) == -1 && *__error_nid_postfix() == 2);
Require(lstat_nid_postfix(nullptr, &linkStatus) == -1 && *__error_nid_postfix() == 14);
Require(lstat_nid_postfix(presentName.c_str(), nullptr) == -1 && *__error_nid_postfix() == 14);
#ifndef _WIN32
const auto link = root / "link";
std::filesystem::create_symlink("present.txt", link);
Require(lstat_nid_postfix(link.string().c_str(), &linkStatus) == 0);
Require((linkStatus.st_mode & 0170000) == 0120000 && linkStatus.st_size == 11);
Require(stat_nid_postfix(link.string().c_str(), &status) == 0 && (status.st_mode & 0170000) == 0100000);
const auto dangling = root / "dangling";
std::filesystem::create_symlink("missing.txt", dangling);
Require(lstat_nid_postfix(dangling.string().c_str(), &linkStatus) == 0 && (linkStatus.st_mode & 0170000) == 0120000);
Require(stat_nid_postfix(dangling.string().c_str(), &status) == -1 && *__error_nid_postfix() == 2);
Require(lstat_nid_postfix((root / "present.txt" / "child").string().c_str(), &linkStatus) == -1 && *__error_nid_postfix() == 20);
const auto viaMissing = rootName + "/missing/../present.txt";
Require(stat_nid_postfix(viaMissing.c_str(), &status) == -1 && *__error_nid_postfix() == 2);
Require(open_nid_postfix(viaMissing.c_str(), 0, 0) == -1 && *__error_nid_postfix() == 2);
Require(unlink_nid_postfix(viaMissing.c_str()) == -1 && *__error_nid_postfix() == 2 && std::filesystem::exists(present));
Require(stat_nid_postfix((presentName + "/..").c_str(), &status) == -1 && *__error_nid_postfix() == 20);
Require(sceKernelStat((presentName + "/../present.txt").c_str(), &status) == static_cast<int>(0x80020014u));
Require(unlink_nid_postfix(link.string().c_str()) == 0 && unlink_nid_postfix(dangling.string().c_str()) == 0);
#else
const auto fileLink = root / "file-link";
if (CreateSymbolicLinkW(fileLink.c_str(), present.c_str(), SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) == 0) {
Require(GetLastError() == ERROR_PRIVILEGE_NOT_HELD);
std::fprintf(stderr, "Windows lstat link checks skipped: creating symbolic links needs Developer Mode or administrator rights\n");
} else {
const auto targetDirectory = root / "target-directory";
Require(std::filesystem::create_directory(targetDirectory));
const auto directoryLink = root / "directory-link";
const auto danglingLink = root / "dangling-link";
Require(CreateSymbolicLinkW(directoryLink.c_str(), targetDirectory.c_str(),
SYMBOLIC_LINK_FLAG_DIRECTORY | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) != 0);
Require(CreateSymbolicLinkW(danglingLink.c_str(), missing.c_str(), SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) != 0);
const auto checkUnsupportedLink = [](const std::filesystem::path& link) {
FileStat untouched;
std::memset(&untouched, 0x5a, sizeof(untouched));
constexpr int sentinelError = 123;
*__error_nid_postfix() = sentinelError;
bool rejected = false;
try {
lstat_nid_postfix(link.string().c_str(), &untouched);
} catch (const std::runtime_error& error) {
rejected = std::strcmp(error.what(), "lstat of a Windows symbolic link not implemented") == 0;
}
const bool errorUnchanged = *__error_nid_postfix() == sentinelError;
const auto* bytes = reinterpret_cast<const unsigned char*>(&untouched);
const bool statusUnchanged = std::all_of(bytes, bytes + sizeof(untouched), [](unsigned char value) { return value == 0x5a; });
return rejected && errorUnchanged && statusUnchanged;
};
Require(checkUnsupportedLink(fileLink));
Require(checkUnsupportedLink(directoryLink));
Require(checkUnsupportedLink(danglingLink));
Require(std::filesystem::remove(fileLink) && std::filesystem::remove(directoryLink) &&
std::filesystem::remove(danglingLink) && std::filesystem::remove(targetDirectory));
}
#endif
FileStat identity{};
FileStat byDescriptor{};
const int identityDescriptor = open_nid_postfix(presentName.c_str(), 0, 0);
Require(identityDescriptor >= 0 && stat_nid_postfix(presentName.c_str(), &identity) == 0);
Require(sceKernelFstat(identityDescriptor, &byDescriptor) == 0 && close_nid_postfix(identityDescriptor) == 0);
Require(identity.st_ino != 0 && byDescriptor.st_dev == identity.st_dev && byDescriptor.st_ino == identity.st_ino);
const auto sibling = root / "sibling.txt";
{ std::ofstream stream(sibling); stream << "other"; }
FileStat siblingStatus{};
Require(stat_nid_postfix(sibling.string().c_str(), &siblingStatus) == 0);
Require(siblingStatus.st_dev == identity.st_dev && siblingStatus.st_ino != identity.st_ino);
Require(unlink_nid_postfix(sibling.string().c_str()) == 0);
const auto hardLink = root / "hardlink.txt";
std::error_code linkError;
std::filesystem::create_hard_link(present, hardLink, linkError);
if (!linkError) {
FileStat linked{};
Require(stat_nid_postfix(hardLink.string().c_str(), &linked) == 0);
Require(linked.st_dev == identity.st_dev && linked.st_ino == identity.st_ino && linked.st_nlink == 2);
Require(unlink_nid_postfix(hardLink.string().c_str()) == 0);
}
const int opened = open_nid_postfix(presentName.c_str(), 0, 0);
Require(opened >= 0 && close_nid_postfix(opened) == 0);
const int reopened = _open_nid_postfix(presentName.c_str(), 0);
Require(reopened >= 0 && close_nid_postfix(reopened) == 0);
const int locked = open_nid_postfix(presentName.c_str(), 0, 0);
const int other = open_nid_postfix(presentName.c_str(), 0, 0);
Require(locked >= 0 && other >= 0);
Require(flock_nid_postfix(locked, 8) == 0);
Require(flock_nid_postfix(locked, 2 | 4) == 0);
Require(flock_nid_postfix(locked, 2 | 4) == 0);
Require(flock_nid_postfix(locked, 1 | 4) == 0);
Require(flock_nid_postfix(other, 1 | 4) == 0);
Require(flock_nid_postfix(other, 8) == 0);
Require(flock_nid_postfix(locked, 1 | 4) == 0);
Require(flock_nid_postfix(locked, 2 | 4) == 0);
Require(flock_nid_postfix(locked, 8) == 0);
Require(flock_nid_postfix(locked, 8) == 0);
Require(flock_nid_postfix(other, 2 | 4) == 0);
Require(flock_nid_postfix(other, 8) == 0);
Require(flock_nid_postfix(locked, 2 | 4) == 0);
Require(close_nid_postfix(locked) == 0);
const int recycled = open_nid_postfix(presentName.c_str(), 0, 0);
Require(recycled >= 0 && flock_nid_postfix(recycled, 8) == 0);
Require(flock_nid_postfix(recycled, 2 | 4) == 0 && sceKernelClose(recycled) == 0);
const int kernelRecycled = sceKernelOpen(presentName.c_str(), 0, 0);
Require(kernelRecycled >= 0 && flock_nid_postfix(kernelRecycled, 8) == 0);
Require(flock_nid_postfix(other, 0) == -1 && *__error_nid_postfix() == 9);
Require(flock_nid_postfix(other, 4 | 0x10) == -1 && *__error_nid_postfix() == 9);
Require(flock_nid_postfix(kernelRecycled, 2 | 4 | 0x10) == 0);
Require(flock_nid_postfix(kernelRecycled, 8 | 2) == 0);
Require(flock_nid_postfix(other, 2 | 4) == 0 && flock_nid_postfix(other, 8) == 0);
Require(sceKernelClose(kernelRecycled) == 0 && close_nid_postfix(other) == 0);
const int holder = open_nid_postfix(presentName.c_str(), 0, 0);
const int contender = open_nid_postfix(presentName.c_str(), 0, 0);
Require(holder >= 0 && contender >= 0 && flock_nid_postfix(holder, 2 | 4) == 0);
Require(flock_nid_postfix(contender, 2 | 4) == -1 && *__error_nid_postfix() == 35);
Require(flock_nid_postfix(contender, 1 | 4) == -1 && *__error_nid_postfix() == 35);
Require(flock_nid_postfix(holder, 1 | 4) == 0 && flock_nid_postfix(contender, 1 | 4) == 0);
Require(flock_nid_postfix(contender, 2 | 4) == -1 && *__error_nid_postfix() == 35);
Require(flock_nid_postfix(holder, 8) == 0 && flock_nid_postfix(contender, 2 | 4) == 0);
Require(flock_nid_postfix(contender, 8) == 0);
Require(flock_nid_postfix(holder, 1 | 2 | 4 | 0x10) == 0);
Require(flock_nid_postfix(contender, 1 | 4) == -1 && *__error_nid_postfix() == 35);
Require(flock_nid_postfix(holder, 8) == 0);
Require(close_nid_postfix(contender) == 0 && close_nid_postfix(holder) == 0);
Require(open_nid_postfix(missingName.c_str(), 0, 0) == -1 && *__error_nid_postfix() == 2);
Require(_open_nid_postfix(missingName.c_str(), 0) == -1 && *__error_nid_postfix() == 2);
Require(sceKernelOpen(missingName.c_str(), 0, 0) == static_cast<int>(0x80020002u));
Require(open_nid_postfix(presentName.c_str(), 0x0a02, 0644) == -1 && *__error_nid_postfix() == 17);
Require(_open_nid_postfix(presentName.c_str(), 0x0a02, 0644) == -1 && *__error_nid_postfix() == 17);
Require(open_nid_postfix(rootName.c_str(), 0x0a02, 0644) == -1 && *__error_nid_postfix() == 17);
Require(open_nid_postfix(rootName.c_str(), 0x0a00, 0644) == -1 && *__error_nid_postfix() == 17);
Require(open_nid_postfix(rootName.c_str(), 0x0200, 0644) == -1 && *__error_nid_postfix() == 21);
Require(open_nid_postfix(rootName.c_str(), 0x0002, 0) == -1 && *__error_nid_postfix() == 21);
Require(open_nid_postfix(rootName.c_str(), 0x0001, 0) == -1 && *__error_nid_postfix() == 21);
const int syncedDirectory = open_nid_postfix(rootName.c_str(), 0, 0);
Require(syncedDirectory >= 0 && sceKernelFsync(syncedDirectory) == 0 && fdatasync_nid_postfix(syncedDirectory) == 0);
Require(sceKernelFdatasync(syncedDirectory) == 0 && close_nid_postfix(syncedDirectory) == 0);
const int syncedReader = open_nid_postfix(presentName.c_str(), 0, 0);
Require(syncedReader >= 0 && sceKernelFsync(syncedReader) == 0 && fdatasync_nid_postfix(syncedReader) == 0);
Require(sceKernelFdatasync(syncedReader) == 0 && close_nid_postfix(syncedReader) == 0);
Require(sceKernelChmod_nid_postfix(presentName.c_str(), 0400) == 0);
const int syncedLocked = open_nid_postfix(presentName.c_str(), 0, 0);
Require(syncedLocked >= 0 && sceKernelFsync(syncedLocked) == 0 && close_nid_postfix(syncedLocked) == 0);
Require(sceKernelChmod_nid_postfix(presentName.c_str(), 0600) == 0);
const int directoryDescriptor = open_nid_postfix(rootName.c_str(), 0, 0);
Require(directoryDescriptor >= 0 && close_nid_postfix(directoryDescriptor) == 0);
const int flaggedDirectory = sceKernelOpen(rootName.c_str(), 0x20000, 0);
Require(flaggedDirectory >= 0 && sceKernelClose(flaggedDirectory) == 0);
Require(open_nid_postfix(presentName.c_str(), 0x20000, 0) == -1 && *__error_nid_postfix() == 20);
Require(sceKernelOpen(presentName.c_str(), 0x20000, 0) == static_cast<int>(0x80020014u));
Require(sceKernelOpen(presentName.c_str(), 0x20402, 0) == static_cast<int>(0x80020014u));
Require(std::filesystem::file_size(present) == 5);
Require(sceKernelOpen(missingName.c_str(), 0x20000, 0) == static_cast<int>(0x80020002u));
Require(open_nid_postfix("", 0, 0) == -1 && *__error_nid_postfix() == 2);
Require(_open_nid_postfix("", 0) == -1 && *__error_nid_postfix() == 2);
Require(sceKernelOpen("", 0, 0) == static_cast<int>(0x80020002u));
Require(open_nid_postfix(nullptr, 0, 0) == -1 && *__error_nid_postfix() == 14);
Require(_open_nid_postfix(nullptr, 0) == -1 && *__error_nid_postfix() == 14);
const auto created = root / "created.txt";
const int createdDescriptor = creat_nid_postfix(created.string().c_str(), 0644);
Require(createdDescriptor >= 0);
Require(write_nid_postfix(createdDescriptor, "created", 7) == 7 && close_nid_postfix(createdDescriptor) == 0);
Require(std::filesystem::file_size(created) == 7);
const int truncatedDescriptor = creat_nid_postfix(created.string().c_str(), 0600);
Require(truncatedDescriptor >= 0 && close_nid_postfix(truncatedDescriptor) == 0);
Require(std::filesystem::file_size(created) == 0);
Require(unlink_nid_postfix(created.string().c_str()) == 0);
Require(creat_nid_postfix((root / "absent" / "child.txt").string().c_str(), 0644) == -1 &&
*__error_nid_postfix() == 2);
Require(creat_nid_postfix(rootName.c_str(), 0644) == -1 && *__error_nid_postfix() == 21);
Require(creat_nid_postfix("", 0644) == -1 && *__error_nid_postfix() == 2);
Require(creat_nid_postfix(nullptr, 0644) == -1 && *__error_nid_postfix() == 14);
Require(rmdir_nid_postfix(rootName.c_str()) == -1 && *__error_nid_postfix() == 66);
Require(sceKernelRmdir(rootName.c_str()) == static_cast<int>(0x80020042u));
Require(rmdir_nid_postfix(presentName.c_str()) == -1 && *__error_nid_postfix() == 20);
Require(rmdir_nid_postfix(missingName.c_str()) == -1 && *__error_nid_postfix() == 2);
const auto lockedFile = [&](const char* name) {
const auto locked = root / name;
{ std::ofstream stream(locked); stream << "removable"; }
Require(sceKernelChmod_nid_postfix(locked.string().c_str(), 0400) == 0);
return locked;
};
const auto lockedPosix = lockedFile("locked-posix");
Require(unlink_nid_postfix(lockedPosix.string().c_str()) == 0 && !std::filesystem::exists(lockedPosix));
const auto lockedKernel = lockedFile("locked-kernel");
Require(sceKernelUnlink(lockedKernel.string().c_str()) == 0 && !std::filesystem::exists(lockedKernel));
const auto lockedDirectory = [&](const char* name) {
const auto locked = root / name;
Require(mkdir_nid_postfix(locked.string().c_str(), 0700) == 0);
Require(sceKernelChmod_nid_postfix(locked.string().c_str(), 0555) == 0);
return locked;
};
const auto lockedDirectoryPosix = lockedDirectory("locked-directory-posix");
Require(rmdir_nid_postfix(lockedDirectoryPosix.string().c_str()) == 0 && !std::filesystem::exists(lockedDirectoryPosix));
const auto lockedDirectoryKernel = lockedDirectory("locked-directory-kernel");
Require(sceKernelRmdir(lockedDirectoryKernel.string().c_str()) == 0 && !std::filesystem::exists(lockedDirectoryKernel));
const auto underFile = presentName + "/child";
const auto deepUnderFile = presentName + "/child/leaf";
Require(mkdir_nid_postfix(underFile.c_str(), 0700) == -1 && *__error_nid_postfix() == 20);
Require(mkdir_nid_postfix(deepUnderFile.c_str(), 0700) == -1 && *__error_nid_postfix() == 20);
Require(sceKernelMkdir(underFile.c_str(), 0700) == static_cast<int>(0x80020014u));
Require(sceKernelMkdir(deepUnderFile.c_str(), 0700) == static_cast<int>(0x80020014u));
Require(rmdir_nid_postfix(underFile.c_str()) == -1 && *__error_nid_postfix() == 20);
Require(sceKernelRmdir(deepUnderFile.c_str()) == static_cast<int>(0x80020014u));
Require(rmdir_nid_postfix((presentName + "/").c_str()) == -1 && *__error_nid_postfix() == 20);
Require(rmdir_nid_postfix((missingName + "/").c_str()) == -1 && *__error_nid_postfix() == 2);
Require(sceKernelRmdir((presentName + "/").c_str()) == static_cast<int>(0x80020014u));
Require(sceKernelRmdir((missingName + "/").c_str()) == static_cast<int>(0x80020002u));
const auto dotDirectory = root / "dot-directory";
Require(std::filesystem::create_directory(dotDirectory));
const auto dotDirectoryName = dotDirectory.string() + "/.";
const auto dotDirectoryTrailingName = dotDirectoryName + "/";
Require(rmdir_nid_postfix(dotDirectoryName.c_str()) == -1 && *__error_nid_postfix() == 22);
Require(sceKernelRmdir(dotDirectoryName.c_str()) == static_cast<int>(0x80020016u));
Require(remove_nid_postfix(dotDirectoryName.c_str()) == -1 && *__error_nid_postfix() == 22);
Require(rmdir_nid_postfix(dotDirectoryTrailingName.c_str()) == -1 && *__error_nid_postfix() == 22);
Require(sceKernelRmdir(dotDirectoryTrailingName.c_str()) == static_cast<int>(0x80020016u));
Require(remove_nid_postfix(dotDirectoryTrailingName.c_str()) == -1 && *__error_nid_postfix() == 22);
Require(std::filesystem::is_directory(dotDirectory));
const auto dotFileName = presentName + "/.";
Require(rmdir_nid_postfix(dotFileName.c_str()) == -1 && *__error_nid_postfix() == 20);
Require(sceKernelRmdir(dotFileName.c_str()) == static_cast<int>(0x80020014u));
Require(remove_nid_postfix(dotFileName.c_str()) == -1 && *__error_nid_postfix() == 20);
const auto dotMissingName = missingName + "/.";
Require(rmdir_nid_postfix(dotMissingName.c_str()) == -1 && *__error_nid_postfix() == 2);
Require(sceKernelRmdir(dotMissingName.c_str()) == static_cast<int>(0x80020002u));
Require(remove_nid_postfix(dotMissingName.c_str()) == -1 && *__error_nid_postfix() == 2);
const auto missingParentLeaf = rootName + "/missing/leaf";
Require(sceKernelMkdir(missingParentLeaf.c_str(), 0700) == static_cast<int>(0x80020002u));
#ifndef _WIN32
const auto longLeaf = rootName + "/" + std::string(300, 'n');
Require(sceKernelMkdir(longLeaf.c_str(), 0700) == static_cast<int>(0x8002003fu));
Require(mkdir_nid_postfix(longLeaf.c_str(), 0700) == -1 && *__error_nid_postfix() == 63);
#endif
const auto invalidNameUnderFile = presentName + "/a:b/x";
Require(mkdir_nid_postfix(invalidNameUnderFile.c_str(), 0700) == -1 && *__error_nid_postfix() == 20);
Require(sceKernelMkdir(invalidNameUnderFile.c_str(), 0700) == static_cast<int>(0x80020014u));
const auto trailingPosix = root / "trailing_posix";
const auto trailingKernel = root / "trailing_kernel";
const auto trailingPosixName = trailingPosix.string() + "/";
const auto trailingKernelName = trailingKernel.string() + "/";
Require(mkdir_nid_postfix(trailingPosixName.c_str(), 0700) == 0 && std::filesystem::is_directory(trailingPosix));
Require(sceKernelMkdir(trailingKernelName.c_str(), 0700) == 0 && std::filesystem::is_directory(trailingKernel));
Require(rmdir_nid_postfix(trailingPosix.string().c_str()) == 0 && sceKernelRmdir(trailingKernel.string().c_str()) == 0);
Require(!std::filesystem::exists(trailingPosix) && !std::filesystem::exists(trailingKernel));
Require(std::filesystem::is_regular_file(present));
Require(rmdir_nid_postfix("") == -1 && *__error_nid_postfix() == 2);
Require(rmdir_nid_postfix(nullptr) == -1 && *__error_nid_postfix() == 14);
Require(unlink_nid_postfix(missingName.c_str()) == -1 && *__error_nid_postfix() == 2);
Require(sceKernelUnlink(missingName.c_str()) == static_cast<int>(0x80020002u));
Require(sceKernelUnlink("") == static_cast<int>(0x80020002u));
Require(unlink_nid_postfix("") == -1 && *__error_nid_postfix() == 2);
Require(unlink_nid_postfix(nullptr) == -1 && *__error_nid_postfix() == 14);
Require(unlink_nid_postfix(rootName.c_str()) == -1 && *__error_nid_postfix() == 1 && std::filesystem::is_directory(root));
Require(sceKernelUnlink(rootName.c_str()) == static_cast<int>(0x80020001u));
const int closable = sceKernelOpen(presentName.c_str(), 0, 0);
Require(closable >= 0 && sceKernelClose(closable) == 0);
Require(sceKernelClose(closable) == static_cast<int>(0x80020009u));
Require(sceKernelClose(-1) == static_cast<int>(0x80020009u));
for (const char* device : {"/dev/urandom", "/dev/random"}) {
const int random = open_nid_postfix(device, 0, 0);
Require(random >= 0);
unsigned char first[64]{};
unsigned char second[64]{};
Require(read_nid_postfix(random, first, sizeof(first)) == sizeof(first));
Require(read_nid_postfix(random, second, 3) == 3);
Require(std::memcmp(first, second, sizeof(first)) != 0 && second[3] == 0);
Require(close_nid_postfix(random) == 0);
}
Require(unlink_nid_postfix(presentName.c_str()) == 0 && !std::filesystem::exists(present));
const auto empty = root / "empty";
Require(std::filesystem::create_directory(empty));
Require(rmdir_nid_postfix(empty.string().c_str()) == 0 && !std::filesystem::exists(empty));
Require(rmdir_nid_postfix(dotDirectory.string().c_str()) == 0 && !std::filesystem::exists(dotDirectory));
Require(remove_nid_postfix(root.string().c_str()) == 0);
Require(!std::filesystem::exists(root));
}