Files
AnyPS5/core/libs/tests/GuestFilesystem.cpp
T
Scott SchusterandClaude Opus 5.5 2585eeb7ee fix(libkernel): keep sceKernelDebugOutText off a redirected guest stderr
Guest descriptors are host descriptors, so a guest that moves standard
error with dup2 also moves the stream sceKernelDebugOutText writes to. A
title that forwards its standard error to the kernel log through a pipe
then reads its own debug output back and logs it again without end. Before
the guest's first dup2 onto descriptor 2, the host's standard error is kept,
and sceKernelDebugOutText writes there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 17:53:41 +03:00

724 lines
47 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*);
int APS5_VABI dup2_nid_postfix(int, 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);
auto* hostCapture = std::tmpfile();
Require(hostCapture != nullptr && pipe_nid_postfix(descriptors) == 0);
#ifdef _WIN32
const int hostError = ::_dup(2);
Require(hostError >= 0 && ::_dup2(::_fileno(hostCapture), 2) == 0);
#else
const int hostError = ::dup(2);
Require(hostError >= 0 && ::dup2(::fileno(hostCapture), 2) == 2);
#endif
Require(dup2_nid_postfix(descriptors[1], 2) == 2);
Require(sceKernelDebugOutText(1, "kept\n") == 0);
Require(write_nid_postfix(2, "P", 1) == 1);
#ifdef _WIN32
Require(::_dup2(hostError, 2) == 0 && ::_close(hostError) == 0);
#else
Require(::dup2(hostError, 2) == 2 && ::close(hostError) == 0);
#endif
Require(close_nid_postfix(descriptors[1]) == 0);
Require(read_nid_postfix(descriptors[0], received, sizeof(received)) == 1 && received[0] == 'P');
Require(close_nid_postfix(descriptors[0]) == 0);
std::rewind(hostCapture);
Require(std::fread(debugText, 1, sizeof(debugText), hostCapture) == 15 && std::memcmp(debugText, "[debug:1] kept\n", 15) == 0);
Require(std::fclose(hostCapture) == 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 && status.st_size == 5);
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(), L"present.txt", 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(), L"target-directory",
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(stat_nid_postfix(fileLink.string().c_str(), &status) == 0 && status.st_size == 5);
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));
}