Files
AnyPS5/core/libs/tests/GuestFilesystem.cpp
T
bluandClaude Fable 5.1 338e2cbe97 test(libkernel): skip the reparse point checks when setting one is refused for rights
Both fixtures required FSCTL_SET_REPARSE_POINT to succeed; a user without the right would have
aborted guest_filesystem and guest_kernel_sys. They now print that the checks are skipped and carry
on, as the symbolic link checks do without Developer Mode. On this PC the control succeeds at
medium integrity, so the checks ran both elevated and unelevated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-20 18:46:51 -07:00

1387 lines
90 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 "prx/libkernel/File/include/NativeStat.hpp"
#include "prx/libkernel/KernelErrors.hpp"
#include <algorithm>
#include <array>
#include <chrono>
#include <cerrno>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <cstdio>
#include <cstring>
#include <functional>
#include <stdexcept>
#include <string>
#include <thread>
#include <vector>
#ifdef _WIN32
#include <io.h>
#include <windows.h>
#include <winioctl.h>
#else
#include <unistd.h>
#endif
#ifdef __linux__
#include <sys/inotify.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 chflags_nid_postfix(const char*, std::uint64_t);
int APS5_VABI fchflags_nid_postfix(int, std::uint64_t);
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 truncate_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 sceKernelGetdents(int, char*, int);
int APS5_VABI getdents_nid_postfix(int, char*, int);
int APS5_VABI getdirentries_nid_postfix(int, char*, int, int64_t*);
std::int64_t APS5_VABI sceKernelRead(int, void*, std::size_t);
std::int64_t APS5_VABI sceKernelReadv(int, const void*, int);
std::int64_t APS5_VABI sceKernelPread(int, void*, std::size_t, std::int64_t);
std::int64_t APS5_VABI sceKernelPreadv(int, const void*, int, std::int64_t);
std::int64_t APS5_VABI sceKernelPwrite(int, const void*, std::size_t, std::int64_t);
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*);
std::size_t APS5_VABI fread_nid_postfix(void*, std::size_t, std::size_t, FileStream*);
int APS5_VABI fclose_nid_postfix(FileStream*);
int APS5_VABI fgetc_nid_postfix(FileStream*);
int APS5_VABI feof_nid_postfix(FileStream*);
int APS5_VABI ferror_nid_postfix(FileStream*);
int APS5_VABI setvbuf_nid_postfix(FileStream*, char*, int, std::size_t);
int APS5_VABI fflush_nid_postfix(FileStream*);
void APS5_VABI flockfile_nid_postfix(FileStream*);
void APS5_VABI funlockfile_nid_postfix(FileStream*);
int APS5_VABI ftrylockfile_nid_postfix(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 lseek_nid_postfix(int, std::int64_t, 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*);
}
#ifdef _WIN32
static bool g_invalidParameter = false;
static void RecordInvalidParameter(const wchar_t*, const wchar_t*, const wchar_t*, unsigned, std::uintptr_t) { g_invalidParameter = true; }
#endif
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 bool ChflagsThrows(const std::filesystem::path& path, std::uint64_t flags, const char* names) {
try {
chflags_nid_postfix(path.string().c_str(), flags);
} catch (const std::runtime_error& error) {
return std::strstr(error.what(), names) != nullptr;
}
return false;
}
static bool FchflagsThrows(int descriptor, std::uint64_t flags, const char* names) {
try {
fchflags_nid_postfix(descriptor, flags);
} catch (const std::runtime_error& error) {
return std::strstr(error.what(), names) != nullptr;
}
return false;
}
static std::string CaptureStderr(const std::function<void()>& action) {
std::FILE* capture = std::tmpfile();
Require(capture != nullptr);
std::fflush(stderr);
#ifdef _WIN32
const int stream = ::_fileno(stderr);
const int saved = ::_dup(stream);
Require(saved >= 0 && ::_dup2(::_fileno(capture), stream) == 0);
#else
const int stream = ::fileno(stderr);
const int saved = ::dup(stream);
Require(saved >= 0 && ::dup2(::fileno(capture), stream) >= 0);
#endif
action();
std::fflush(stderr);
#ifdef _WIN32
Require(::_dup2(saved, stream) == 0);
::_close(saved);
#else
Require(::dup2(saved, stream) >= 0);
::close(saved);
#endif
Require(std::fseek(capture, 0, SEEK_SET) == 0);
std::string output;
char buffer[512];
for (;;) {
const auto bytes = std::fread(buffer, 1, sizeof(buffer), capture);
output.append(buffer, bytes);
if (bytes != sizeof(buffer)) break;
}
std::fclose(capture);
return output;
}
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);
#ifndef _WIN32
for (const char* name : {"slash-unlink", "slash-remove", "slash-rename", "slash-open", "slash-kernel"}) std::ofstream(root / name) << "kept";
Require(unlink_nid_postfix("/parent-test-mount/slash-unlink/") == -1 && *__error_nid_postfix() == 20);
Require(remove_nid_postfix("/parent-test-mount/slash-remove/") == -1 && *__error_nid_postfix() == 20);
Require(rename_nid_postfix("/parent-test-mount/slash-rename/", "/parent-test-mount/slash-renamed") == -1 && *__error_nid_postfix() == 20);
Require(open_nid_postfix("/parent-test-mount/slash-open/", 0, 0) == -1 && *__error_nid_postfix() == 20);
Require(sceKernelUnlink("/parent-test-mount/slash-kernel/") == static_cast<int>(0x80020014u));
for (const char* name : {"slash-unlink", "slash-remove", "slash-rename", "slash-open", "slash-kernel"}) Require(std::filesystem::is_regular_file(root / name));
Require(!std::filesystem::exists(root / "slash-renamed"));
Require(sceKernelStat("/parent-test-mount/Existing/", &status) == 0 && (status.st_mode & 0170000) == 0040000);
Require(mkdir_nid_postfix("/parent-test-mount/slash-directory/", 0755) == 0 && std::filesystem::is_directory(root / "slash-directory"));
Require(rmdir_nid_postfix("/parent-test-mount/slash-directory/") == 0 && !std::filesystem::exists(root / "slash-directory"));
#endif
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);
}
static void TestRelinkedModuleNames() {
const auto root = std::filesystem::path("anyps5-relinked-names-" +
std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
Require(std::filesystem::create_directories(root));
const auto original = (root / "Plugin.prx").generic_string();
const auto relinked = original + ".guest.prx";
FileStat status{};
Require(sceKernelStat(original.c_str(), &status) == static_cast<int>(0x80020002u));
Require(sceKernelCheckReachability(original.c_str()) == static_cast<int>(0x80020002u));
Require(sceKernelOpen(original.c_str(), 0, 0) == static_cast<int>(0x80020002u));
const auto missing = (root / "missing-content.pak").string();
int openResult = 0;
const auto openLog = CaptureStderr([&] { openResult = sceKernelOpen(missing.c_str(), 0, 0); });
Require(openResult == static_cast<int>(0x80020002u));
Require(openLog.find("path=" + missing) != std::string::npos);
Require(openLog.find("resolvedPath=") != std::string::npos && openLog.find("missing-content.pak") != std::string::npos);
Require(openLog.find("result=0x80020002") != std::string::npos);
{ std::ofstream file(relinked); file << "relinked"; }
Require(sceKernelStat(original.c_str(), &status) == 0 && status.st_size == 8);
Require(stat_nid_postfix(original.c_str(), &status) == 0 && status.st_size == 8);
Require(sceKernelCheckReachability(original.c_str()) == 0);
const int opened = sceKernelOpen(original.c_str(), 0, 0);
Require(opened >= 0 && sceKernelClose(opened) == 0);
Require(sceKernelStat((root / "Other.prx").generic_string().c_str(), &status) == static_cast<int>(0x80020002u));
Require(!std::filesystem::exists(original));
{ std::ofstream file(original); file << "original-bytes"; }
Require(sceKernelStat(original.c_str(), &status) == 0 && status.st_size == 14);
Require(std::filesystem::remove(original));
const int created = sceKernelOpen(original.c_str(), 0x601, 0600);
Require(created >= 0 && sceKernelClose(created) == 0);
Require(std::filesystem::file_size(original) == 0 && std::filesystem::file_size(relinked) == 8);
std::filesystem::remove_all(root);
}
static void TestRelinkedModuleNameCase() {
const auto root = std::filesystem::path("anyps5-relinked-case-" +
std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
Require(std::filesystem::create_directories(root));
{ std::ofstream file(root / "librenoircore.ps5.prx.guest.prx"); file << "relinked"; }
const auto requested = (root / "libRenoirCore.PS5.prx").generic_string();
FileStat status{};
Require(sceKernelStat(requested.c_str(), &status) == 0 && status.st_size == 8);
Require(stat_nid_postfix(requested.c_str(), &status) == 0 && status.st_size == 8);
Require(sceKernelCheckReachability(requested.c_str()) == 0);
const int opened = sceKernelOpen(requested.c_str(), 0, 0);
Require(opened >= 0 && sceKernelClose(opened) == 0);
Require(sceKernelStat((root / "libRenoirCore.PS5.prx2").generic_string().c_str(), &status) == static_cast<int>(0x80020002u));
std::filesystem::remove_all(root);
}
#ifdef _WIN32
static std::filesystem::path ExtendedPath(const std::filesystem::path& path) {
const auto& native = path.native();
if (native.starts_with(L"\\\\?\\") || native.starts_with(L"\\\\.\\")) return path;
return native.starts_with(L"\\\\") ? std::filesystem::path(L"\\\\?\\UNC\\" + native.substr(2))
: std::filesystem::path(L"\\\\?\\" + native);
}
static void TestLongHostPathStat() {
auto root = std::filesystem::absolute(std::filesystem::temp_directory_path()).lexically_normal();
if (root.filename().empty()) root = root.parent_path();
root /= L"anyps5-stat-long-" + std::to_wstring(std::chrono::steady_clock::now().time_since_epoch().count());
auto deep = root;
while (deep.native().size() < MAX_PATH + 24) deep /= std::wstring(40, L'd');
Require(::CreateDirectoryW(ExtendedPath(root).c_str(), nullptr) != 0);
auto directory = root;
for (const auto& component : std::filesystem::path(deep).lexically_relative(root)) {
directory /= component;
Require(::CreateDirectoryW(ExtendedPath(directory).c_str(), nullptr) != 0);
}
const auto file = deep / L"payload.bin";
const auto handle = ::CreateFileW(ExtendedPath(file).c_str(), GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
Require(handle != INVALID_HANDLE_VALUE);
DWORD written = 0;
const bool wrote = ::WriteFile(handle, "metadata", 8, &written, nullptr) != 0 && written == 8;
::CloseHandle(handle);
Require(wrote);
const auto host = deep.string();
AddPathAlias_nid_no_patch("/long-stat-host", host.c_str());
FileStat status{};
Require(ResolvePath_nid_no_patch("/long-stat-host/payload.bin").native().size() > MAX_PATH);
Require(sceKernelStat("/long-stat-host/payload.bin", &status) == 0 && status.st_size == 8);
Require(sceKernelStat("/long-stat-host/missing.bin", &status) == static_cast<int>(0x80020002u));
Require(sceKernelStat("/long-stat-host/", &status) == 0 && (status.st_mode & 0170000) == 0040000);
RemovePathAlias_nid_no_patch("/long-stat-host");
const auto extendedHost = ExtendedPath(deep).string();
AddPathAlias_nid_no_patch("/long-stat-extended", extendedHost.c_str());
Require(ResolvePath_nid_no_patch("/long-stat-extended/payload.bin").native().starts_with(L"\\\\?\\"));
Require(sceKernelStat("/long-stat-extended/payload.bin", &status) == 0 && status.st_size == 8);
Require(sceKernelStat("/long-stat-extended/missing.bin", &status) == static_cast<int>(0x80020002u));
RemovePathAlias_nid_no_patch("/long-stat-extended");
::DeleteFileW(ExtendedPath(file).c_str());
for (auto path = deep; ; path = path.parent_path()) {
::RemoveDirectoryW(ExtendedPath(path).c_str());
if (path == root) break;
}
}
#endif
#ifdef __linux__
static int DirectoryOpens(int watch) {
alignas(inotify_event) char buffer[4096];
int opens = 0;
for (ssize_t size; (size = read(watch, buffer, sizeof buffer)) > 0;) {
for (char* event = buffer; event < buffer + size; event += sizeof(inotify_event) + reinterpret_cast<inotify_event*>(event)->len)
if (reinterpret_cast<inotify_event*>(event)->mask & IN_OPEN) ++opens;
}
return opens;
}
static void TestMissingPathListsDirectoryOnce() {
const auto root = std::filesystem::path("anyps5-missing-listing-" +
std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
Require(std::filesystem::create_directories(root));
{ std::ofstream file(root / "present.txt"); file << "present"; }
const int watch = inotify_init1(IN_NONBLOCK);
Require(watch >= 0 && inotify_add_watch(watch, root.c_str(), IN_OPEN | IN_CLOSE_NOWRITE) >= 0);
const auto missing = (root / "Missing.txt").generic_string();
FileStat status{};
Require(sceKernelStat(missing.c_str(), &status) == static_cast<int>(0x80020002u) && DirectoryOpens(watch) == 1);
Require(sceKernelCheckReachability(missing.c_str()) == static_cast<int>(0x80020002u) && DirectoryOpens(watch) == 1);
Require(sceKernelOpen(missing.c_str(), 0, 0) == static_cast<int>(0x80020002u) && DirectoryOpens(watch) == 1);
close(watch);
{ std::ofstream file(root / "plugin.prx.guest.prx"); file << "lower"; }
{ std::ofstream file(root / "Plugin.prx.guest.prx"); file << "upper"; }
bool ambiguous = false;
try {
sceKernelStat((root / "PLUGIN.prx").generic_string().c_str(), &status);
} catch (const std::runtime_error&) {
ambiguous = true;
}
Require(ambiguous);
std::filesystem::remove_all(root);
}
#endif
int main() {
TestParentTraversal();
TestRelinkedModuleNames();
TestRelinkedModuleNameCase();
#ifdef _WIN32
TestLongHostPathStat();
#endif
#ifdef __linux__
TestMissingPathListsDirectoryOnce();
#endif
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(sceKernelStat("anyps5_missing_chmod_path", nullptr) == static_cast<int>(0x80020002u));
Require(sceKernelStat(".", nullptr) == static_cast<int>(0x8002000eu));
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(sceKernelWriteThrottlingStatus(nullptr) == static_cast<int>(0x8002000eu));
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);
{
int unseekable[2] = {-1, -1};
Require(pipe_nid_postfix(unseekable) == 0 && write_nid_postfix(unseekable[1], "pipe", 4) == 4);
for (const int end : unseekable) {
for (int whence = 0; whence < 3; ++whence) {
Require(lseek_nid_postfix(end, 0, whence) == -1 && *__error_nid_postfix() == 29);
Require(sceKernelLseek(end, 1, whence) == static_cast<int>(0x8002001du));
}
}
char piped[8]{};
Require(read_nid_postfix(unseekable[0], piped, sizeof(piped)) == 4 && std::memcmp(piped, "pipe", 4) == 0);
Require(close_nid_postfix(unseekable[0]) == 0 && close_nid_postfix(unseekable[1]) == 0);
Require(lseek_nid_postfix(unseekable[0], 0, 1) == -1 && *__error_nid_postfix() == 9);
}
{
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"));
}
{
int ends[2] = {-1, -1};
Require(pipe_nid_postfix(ends) == 0 && write_nid_postfix(ends[1], "abc", 3) == 3);
for (const int end : ends) {
Require(sceKernelFtruncate(end, 0) == static_cast<int>(0x80020016u) && sceKernelFtruncate(end, 8) == static_cast<int>(0x80020016u));
}
char kept[4]{};
Require(read_nid_postfix(ends[0], kept, sizeof(kept)) == 3 && std::memcmp(kept, "abc", 3) == 0);
Require(close_nid_postfix(ends[0]) == 0 && close_nid_postfix(ends[1]) == 0);
for (const char* name : {".", "/dev/urandom"}) {
const int opened = open_nid_postfix(name, 0, 0);
Require(opened >= 0 && sceKernelFtruncate(opened, 0) == static_cast<int>(0x80020016u) && close_nid_postfix(opened) == 0);
}
}
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);
#ifdef _WIN32
for (const bool reopen : {false, true}) {
auto* hostCapture = std::tmpfile();
const int hostError = ::_dup(2);
Require(hostCapture != nullptr && hostError >= 0);
int redirect[2] = {-1, -1};
Require(reopen || pipe_nid_postfix(redirect) == 0);
const bool captured = ::_dup2(::_fileno(hostCapture), 2) == 0 && SetStdHandle(STD_ERROR_HANDLE, reinterpret_cast<HANDLE>(::_get_osfhandle(2)));
const bool redirected = reopen ? freopen_nid_postfix("stderr_probe.txt", "w", &_Stderr_nid_postfix) == &_Stderr_nid_postfix : dup2_nid_postfix(redirect[1], 2) == 2;
const HANDLE kept = GetStdHandle(STD_ERROR_HANDLE);
DWORD count = 0;
const bool separate = kept != reinterpret_cast<HANDLE>(::_get_osfhandle(2)) && WriteFile(kept, "kept", 4, &count, nullptr) && count == 4;
const bool written = write_nid_postfix(2, "G", 1) == 1;
const bool restored = ::_dup2(hostError, 2) == 0 && ::_close(hostError) == 0 && SetStdHandle(STD_ERROR_HANDLE, reinterpret_cast<HANDLE>(::_get_osfhandle(2)));
Require(captured && redirected && separate && written && restored && CloseHandle(kept));
std::rewind(hostCapture);
char keptText[8]{};
Require(std::fread(keptText, 1, sizeof(keptText), hostCapture) == 4 && std::memcmp(keptText, "kept", 4) == 0);
Require(std::fclose(hostCapture) == 0);
char guestText = 0;
if (reopen) {
Require(std::filesystem::file_size("stderr_probe.txt") == 1 && std::filesystem::remove("stderr_probe.txt"));
} else {
Require(close_nid_postfix(redirect[1]) == 0 && read_nid_postfix(redirect[0], &guestText, 1) == 1 && guestText == 'G');
Require(close_nid_postfix(redirect[0]) == 0);
}
}
#endif
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);
for (const char* openMode : {"w", "wb", "r+", "a"}) {
Require(fopen_nid_postfix(root.string().c_str(), openMode) == nullptr && *__error_nid_postfix() == 21);
}
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"; }
Require(chflags_nid_postfix(nullptr, 0) == -1 && *__error_nid_postfix() == 14);
Require(chflags_nid_postfix("", 0) == -1 && *__error_nid_postfix() == 2);
Require(chflags_nid_postfix((root / "missing").string().c_str(), 0) == -1 && *__error_nid_postfix() == 2);
Require(chflags_nid_postfix((root / "missing").string().c_str(), ~0ULL) == -1 && *__error_nid_postfix() == 2);
Require(chflags_nid_postfix((file / "child").string().c_str(), 0) == -1 && *__error_nid_postfix() == 20);
Require(chflags_nid_postfix((file.string() + "/").c_str(), 0) == -1 && *__error_nid_postfix() == 20);
Require(chflags_nid_postfix(file.string().c_str(), 0) == 0);
Require(chflags_nid_postfix(root.string().c_str(), 0) == 0);
Require(chflags_nid_postfix(file.string().c_str(), 0x20) == -1 && *__error_nid_postfix() == 45);
Require(chflags_nid_postfix(file.string().c_str(), 1ULL << 32) == -1 && *__error_nid_postfix() == 45);
Require(chflags_nid_postfix(file.string().c_str(), ~0ULL) == -1 && *__error_nid_postfix() == 45);
Require(chflags_nid_postfix(file.string().c_str(), 0x200000) == -1 && *__error_nid_postfix() == 1);
Require(ChflagsThrows(file, 0x2, "UF_IMMUTABLE"));
Require(ChflagsThrows(file, 0x1000 | 0x40000, "UF_READONLY|SF_APPEND"));
const int flagged = open_nid_postfix(file.string().c_str(), 0, 0);
Require(flagged >= 0 && fchflags_nid_postfix(flagged, 0) == 0);
Require(fchflags_nid_postfix(flagged, 0x20) == -1 && *__error_nid_postfix() == 45);
Require(fchflags_nid_postfix(flagged, 1ULL << 32) == -1 && *__error_nid_postfix() == 45);
Require(fchflags_nid_postfix(flagged, ~0ULL) == -1 && *__error_nid_postfix() == 45);
Require(fchflags_nid_postfix(flagged, 0x200000) == -1 && *__error_nid_postfix() == 1);
Require(FchflagsThrows(flagged, 0x2, "UF_IMMUTABLE"));
Require(FchflagsThrows(flagged, 0x1000 | 0x40000, "UF_READONLY|SF_APPEND"));
Require(close_nid_postfix(flagged) == 0);
Require(fchflags_nid_postfix(flagged, 0) == -1 && *__error_nid_postfix() == 9);
Require(fchflags_nid_postfix(flagged, ~0ULL) == -1 && *__error_nid_postfix() == 9);
Require(fchflags_nid_postfix(-1, 0) == -1 && *__error_nid_postfix() == 9);
const int flaggedRoot = sceKernelOpen(root.string().c_str(), 0x20000, 0);
Require(flaggedRoot >= 0 && fchflags_nid_postfix(flaggedRoot, 0) == 0 && sceKernelClose(flaggedRoot) == 0);
int flaggedPipe[2];
Require(pipe_nid_postfix(flaggedPipe) == 0);
Require(fchflags_nid_postfix(flaggedPipe[0], 0) == -1 && *__error_nid_postfix() == 22);
Require(fchflags_nid_postfix(flaggedPipe[1], ~0ULL) == -1 && *__error_nid_postfix() == 22);
Require(close_nid_postfix(flaggedPipe[0]) == 0 && close_nid_postfix(flaggedPipe[1]) == 0);
const int flaggedSocket = socket_nid_postfix(2, 2, 0);
Require(flaggedSocket >= 0 && fchflags_nid_postfix(flaggedSocket, 0) == -1 && *__error_nid_postfix() == 22);
Require(close_nid_postfix(flaggedSocket) == 0);
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"));
const auto kernelSource = area / "kernel_source";
const auto kernelTarget = area / "kernel_target";
Require(std::filesystem::create_directories(kernelSource / "child"));
Require(std::filesystem::create_directory(kernelTarget));
{ std::ofstream stream(kernelSource / "marker"); stream << "kernel directory contents"; }
const int directoryResult = sceKernelRename(name(kernelSource).c_str(), name(kernelTarget).c_str());
if (directoryResult != 0)
std::fprintf(stderr, "kernel empty-directory replacement result=0x%08x, expected=0\n", static_cast<unsigned>(directoryResult));
Require(directoryResult == 0);
Require(!std::filesystem::exists(kernelSource) && std::filesystem::is_directory(kernelTarget / "child"));
{ std::ifstream stream(kernelTarget / "marker"); std::string contents; std::getline(stream, contents);
Require(contents == "kernel directory contents"); }
const int parentResult = sceKernelRename(name(targetFile).c_str(), absentParent.c_str());
Require(parentResult == static_cast<int>(0x80020002u));
Require(std::filesystem::is_regular_file(targetFile) && !std::filesystem::exists(area / "absent"));
const int prefixResult = sceKernelRename(name(targetFile).c_str(), name(targetFile / "target").c_str());
Require(prefixResult == static_cast<int>(0x80020014u));
Require(std::filesystem::is_regular_file(targetFile));
const int fileDirectoryResult = sceKernelRename(name(targetFile).c_str(), name(fullDirectory).c_str());
Require(fileDirectoryResult == static_cast<int>(0x80020015u));
Require(std::filesystem::is_regular_file(targetFile) && std::filesystem::is_regular_file(fullDirectory / "entry"));
const int directoryFileResult = sceKernelRename(name(kernelTarget).c_str(), name(targetFile).c_str());
Require(directoryFileResult == static_cast<int>(0x80020014u));
Require(std::filesystem::is_regular_file(kernelTarget / "marker") && std::filesystem::is_regular_file(targetFile));
const int nonemptyResult = sceKernelRename(name(kernelTarget).c_str(), name(fullDirectory).c_str());
Require(nonemptyResult == static_cast<int>(0x80020042u));
Require(std::filesystem::is_regular_file(kernelTarget / "marker") && std::filesystem::is_regular_file(fullDirectory / "entry"));
const auto descendant = kernelTarget / "child" / "moved";
const int descendantResult = sceKernelRename(name(kernelTarget).c_str(), name(descendant).c_str());
Require(descendantResult == static_cast<int>(0x80020016u));
Require(std::filesystem::is_directory(kernelTarget / "child") && !std::filesystem::exists(descendant));
{ std::ifstream stream(targetFile); std::string contents; std::getline(stream, contents); Require(contents == "source"); }
{ std::ifstream stream(fullDirectory / "entry"); std::string contents; std::getline(stream, contents); Require(contents == "entry"); }
{ std::ifstream stream(kernelTarget / "marker"); std::string contents; std::getline(stream, contents);
Require(contents == "kernel directory contents"); }
#ifdef _WIN32
for (const bool kernel : {true, false}) {
const auto lockedSource = area / (kernel ? "locked_kernel_source" : "locked_libc_source");
const auto preservedTarget = area / (kernel ? "preserved_kernel_target" : "preserved_libc_target");
Require(std::filesystem::create_directory(lockedSource));
Require(std::filesystem::create_directory(preservedTarget));
const auto marker = lockedSource / "marker";
{ std::ofstream stream(marker); stream << "preserved source contents"; }
const HANDLE held = CreateFileW(marker.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
Require(held != INVALID_HANDLE_VALUE);
const int result = kernel ? sceKernelRename(name(lockedSource).c_str(), name(preservedTarget).c_str()) :
rename_nid_postfix(name(lockedSource).c_str(), name(preservedTarget).c_str());
Require(CloseHandle(held) != 0);
Require(result != 0 && std::filesystem::is_regular_file(marker));
if (!std::filesystem::is_directory(preservedTarget))
std::fprintf(stderr, "failed %s directory rename removed its destination\n", kernel ? "kernel" : "libc");
Require(std::filesystem::is_directory(preservedTarget) && std::filesystem::is_empty(preservedTarget));
{ std::ifstream stream(marker); std::string contents; std::getline(stream, contents);
Require(contents == "preserved source contents"); }
}
#endif
for (const bool kernel : {false, true}) {
const auto incoming = name(area / "incoming.txt");
const auto occupied = name(area / "occupied.txt");
const auto replace = [&] { return kernel ? sceKernelRename(incoming.c_str(), occupied.c_str()) : rename_nid_postfix(incoming.c_str(), occupied.c_str()); };
const auto contents = [&] { std::ifstream stream(occupied); std::string text; std::getline(stream, text); return text; };
{ std::ofstream stream(incoming); stream << "incoming"; }
{ std::ofstream stream(occupied); stream << "occupied"; }
const int occupiedDescriptor = sceKernelOpen(occupied.c_str(), 2, 0);
Require(occupiedDescriptor >= 0 && replace() == 0 && !std::filesystem::exists(incoming) && contents() == "incoming");
char occupiedBytes[8]{};
Require(read_nid_postfix(occupiedDescriptor, occupiedBytes, sizeof(occupiedBytes)) == sizeof(occupiedBytes) && std::memcmp(occupiedBytes, "occupied", 8) == 0);
Require(sceKernelClose(occupiedDescriptor) == 0 && contents() == "incoming");
{ std::ofstream stream(incoming); stream << "replaced"; }
Require(sceKernelChmod_nid_postfix(occupied.c_str(), 0400) == 0);
Require(replace() == 0 && !std::filesystem::exists(incoming) && contents() == "replaced");
Require(remove_nid_postfix(occupied.c_str()) == 0);
}
#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::error_code cleanupError;
for (int attempt = 0; attempt < 10; ++attempt) {
std::filesystem::remove_all(area, cleanupError);
if (!cleanupError) break;
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
Require(!cleanupError);
}
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>(0x80020014u));
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));
int syncPipe[2] = {-1, -1};
Require(pipe_nid_postfix(syncPipe) == 0);
for (const int end : syncPipe) {
Require(fsync_nid_postfix(end) == -1 && *__error_nid_postfix() == 22);
Require(fdatasync_nid_postfix(end) == -1 && *__error_nid_postfix() == 22);
Require(sceKernelFsync(end) == static_cast<int>(0x80020016u) && sceKernelFdatasync(end) == static_cast<int>(0x80020016u));
}
Require(close_nid_postfix(syncPipe[0]) == 0 && close_nid_postfix(syncPipe[1]) == 0);
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));
#ifdef _WIN32
const auto previousParameterHandler = _set_invalid_parameter_handler(RecordInvalidParameter);
#endif
FileStat closedStatus{};
Require(sceKernelFstat(descriptor, &closedStatus) == static_cast<int>(0x80020009u));
Require(sceKernelFstat(socket, &closedStatus) == static_cast<int>(0x80020009u));
Require(sceKernelFstat(0x7fffffff, &closedStatus) == static_cast<int>(0x80020009u));
#ifdef _WIN32
_set_invalid_parameter_handler(previousParameterHandler);
Require(!g_invalidParameter);
#endif
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(sceKernelRename(missingName.c_str(), nullptr) == static_cast<int>(0x80020002u));
Require(sceKernelRename("", nullptr) == static_cast<int>(0x80020002u));
Require(sceKernelRename(".", nullptr) == static_cast<int>(0x80020016u));
Require(sceKernelRename(missingName.c_str(), ".") == static_cast<int>(0x80020002u));
Require(sceKernelRename(presentName.c_str(), nullptr) == static_cast<int>(0x8002000Eu));
Require(sceKernelRename(nullptr, presentName.c_str()) == static_cast<int>(0x8002000Eu));
Require(rename_nid_postfix(missingName.c_str(), nullptr) == -1 && *__error_nid_postfix() == 2);
Require(rename_nid_postfix(presentName.c_str(), nullptr) == -1 && *__error_nid_postfix() == 14);
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));
Require(sceKernelTruncate_nid_postfix(nullptr, -1) == static_cast<int>(0x80020016u));
Require(sceKernelTruncate_nid_postfix("", -1) == static_cast<int>(0x80020016u));
Require(truncate_nid_postfix(nullptr, -1) == -1 && *__error_nid_postfix() == 22);
Require(truncate_nid_postfix("", -1) == -1 && *__error_nid_postfix() == 22);
Require(sceKernelTruncate_nid_postfix(nullptr, 0) == static_cast<int>(0x8002000Eu));
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);
const auto statLoop = root / "stat-loop";
std::filesystem::create_symlink("stat-loop", statLoop);
errno = 0;
Require(!File::TryFillFileStat(statLoop, &status) && errno == ELOOP);
Require(sceKernelStat(statLoop.generic_string().c_str(), &status) == SCE_KERNEL_ERROR_ELOOP);
Require(lstat_nid_postfix(dangling.string().c_str(), &linkStatus) == 0 && (linkStatus.st_mode & 0170000) == 0120000);
constexpr int noFollow = 0x100;
Require(sceKernelOpen(link.string().c_str(), noFollow, 0) == static_cast<int>(0x8002001Fu));
Require(open_nid_postfix(link.string().c_str(), noFollow, 0) == -1 && *__error_nid_postfix() == 31);
Require(open_nid_postfix(dangling.string().c_str(), noFollow | 0x201, 0600) == -1 && *__error_nid_postfix() == 31);
Require(!std::filesystem::exists(root / "missing.txt"));
Require(open_nid_postfix(dangling.string().c_str(), noFollow | 0xA01, 0600) == -1 && *__error_nid_postfix() == 17);
const int followed = open_nid_postfix(link.string().c_str(), 0, 0);
Require(followed >= 0 && close_nid_postfix(followed) == 0);
const int plain = open_nid_postfix(presentName.c_str(), noFollow, 0);
Require(plain >= 0 && close_nid_postfix(plain) == 0);
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 &&
unlink_nid_postfix(statLoop.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";
const auto absoluteLink = root / "absolute-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(), L"missing.txt", SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) != 0);
Require(CreateSymbolicLinkW(absoluteLink.c_str(), std::filesystem::absolute(present).c_str(),
SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) != 0);
const auto statLoop = root / "stat-loop";
Require(CreateSymbolicLinkW(statLoop.c_str(), L"stat-loop", SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) != 0);
Require(sceKernelStat(statLoop.generic_string().c_str(), &status) == static_cast<int>(0x8002003eu));
const auto checkRelativeLink = [](const std::filesystem::path& link, std::int64_t targetLength) {
FileStat own{};
return lstat_nid_postfix(link.string().c_str(), &own) == 0 && (own.st_mode & 0170000) == 0120000 &&
own.st_size == targetLength && own.st_nlink == 1;
};
Require(checkRelativeLink(fileLink, 11));
Require(checkRelativeLink(directoryLink, 16));
Require(checkRelativeLink(danglingLink, 11));
FileStat followed{};
Require(lstat_nid_postfix(fileLink.string().c_str(), &linkStatus) == 0);
Require(stat_nid_postfix(fileLink.string().c_str(), &followed) == 0 && (followed.st_mode & 0170000) == 0100000);
Require(followed.st_ino == status.st_ino && linkStatus.st_ino != status.st_ino && linkStatus.st_dev == status.st_dev);
Require(stat_nid_postfix(directoryLink.string().c_str(), &followed) == 0 && (followed.st_mode & 0170000) == 0040000);
Require(stat_nid_postfix(fileLink.string().c_str(), &status) == 0 && status.st_size == 5);
FileStat absoluteStatus{};
Require(lstat_nid_postfix(absoluteLink.string().c_str(), &absoluteStatus) == 0);
Require((absoluteStatus.st_mode & 0170000) == 0120000 && absoluteStatus.st_size > 0 && absoluteStatus.st_nlink == 1);
Require(stat_nid_postfix(absoluteLink.string().c_str(), &followed) == 0 && followed.st_ino == status.st_ino);
Require(std::filesystem::remove(fileLink) && std::filesystem::remove(directoryLink) && DeleteFileW(danglingLink.c_str()) != 0 &&
DeleteFileW(statLoop.c_str()) != 0 &&
std::filesystem::remove(absoluteLink) && std::filesystem::remove(targetDirectory));
}
const auto junctionTarget = root / "junction-target";
const auto junction = root / "junction";
Require(std::filesystem::create_directory(junctionTarget));
const auto junctionCommand = L"cmd /c mklink /J \"" + junction.wstring() + L"\" \"" + junctionTarget.wstring() + L"\"";
Require(_wsystem(junctionCommand.c_str()) == 0);
FileStat junctionStatus{};
Require(lstat_nid_postfix(junction.string().c_str(), &junctionStatus) == 0);
Require((junctionStatus.st_mode & 0170000) == 0120000 && junctionStatus.st_size > 0);
FileStat junctionFollowed{};
Require(stat_nid_postfix(junction.string().c_str(), &junctionFollowed) == 0 && (junctionFollowed.st_mode & 0170000) == 0040000);
constexpr int noFollow = 0x100;
Require(sceKernelOpen(junction.string().c_str(), noFollow, 0) == static_cast<int>(0x8002001Fu));
Require(open_nid_postfix(junction.string().c_str(), noFollow, 0) == -1 && *__error_nid_postfix() == 31);
const int followedJunction = sceKernelOpen(junction.string().c_str(), 0, 0);
Require(followedJunction >= 0 && sceKernelClose(followedJunction) == 0);
Require(std::filesystem::remove(junction) && std::filesystem::remove(junctionTarget));
const auto tagged = root / "tagged.bin";
{ std::ofstream stream(tagged, std::ios::binary); stream << std::string(1u << 18, 'a'); }
const HANDLE taggedHandle = CreateFileW(tagged.c_str(), GENERIC_WRITE, 0, nullptr, OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nullptr);
Require(taggedHandle != INVALID_HANDLE_VALUE);
std::array<std::byte, REPARSE_GUID_DATA_BUFFER_HEADER_SIZE + 4> reparseData{};
REPARSE_GUID_DATA_BUFFER reparseHeader{};
reparseHeader.ReparseTag = 0x00000ABC;
reparseHeader.ReparseDataLength = 4;
reparseHeader.ReparseGuid = {0x6a1e3d0b, 0x1f6c, 0x4c1e, {0x9b, 0x52, 0x3d, 0x8e, 0x41, 0x07, 0x5c, 0x2a}};
std::memcpy(reparseData.data(), &reparseHeader, REPARSE_GUID_DATA_BUFFER_HEADER_SIZE);
DWORD returned = 0;
const bool taggedSet = DeviceIoControl(taggedHandle, FSCTL_SET_REPARSE_POINT, reparseData.data(),
static_cast<DWORD>(reparseData.size()), nullptr, 0, &returned, nullptr) != 0;
if (!taggedSet) Require(GetLastError() == ERROR_PRIVILEGE_NOT_HELD || GetLastError() == ERROR_ACCESS_DENIED);
Require(CloseHandle(taggedHandle) != 0);
if (!taggedSet) {
Require(std::filesystem::remove(tagged));
std::fprintf(stderr, "Windows reparse point checks skipped: setting a reparse point on a file needs rights this user lacks\n");
} else {
Require((GetFileAttributesW(tagged.c_str()) & FILE_ATTRIBUTE_REPARSE_POINT) != 0);
FileStat taggedStatus{};
FileStat taggedFollowed{};
const int taggedResult = lstat_nid_postfix(tagged.string().c_str(), &taggedStatus);
const int taggedError = *__error_nid_postfix();
Require(stat_nid_postfix(tagged.string().c_str(), &taggedFollowed) == taggedResult);
Require(taggedResult == 0 ? (taggedStatus.st_ino == taggedFollowed.st_ino && taggedStatus.st_mode == taggedFollowed.st_mode)
: *__error_nid_postfix() == taggedError);
const HANDLE taggedCleanup = CreateFileW(tagged.c_str(), GENERIC_WRITE, 0, nullptr, OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nullptr);
Require(taggedCleanup != INVALID_HANDLE_VALUE);
reparseHeader.ReparseDataLength = 0;
Require(DeviceIoControl(taggedCleanup, FSCTL_DELETE_REPARSE_POINT, &reparseHeader, REPARSE_GUID_DATA_BUFFER_HEADER_SIZE,
nullptr, 0, &returned, nullptr) != 0);
Require(CloseHandle(taggedCleanup) != 0 && std::filesystem::remove(tagged));
}
#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(flock_nid_postfix(-1, 2 | 4) == -1 && *__error_nid_postfix() == 9);
Require(flock_nid_postfix(0x7fffffff, 1) == -1 && *__error_nid_postfix() == 9);
int flockPipe[2] = {-1, -1};
Require(pipe_nid_postfix(flockPipe) == 0);
Require(flock_nid_postfix(flockPipe[0], 2 | 4) == -1 && *__error_nid_postfix() == 45);
Require(flock_nid_postfix(flockPipe[1], 8) == -1 && *__error_nid_postfix() == 45);
Require(close_nid_postfix(flockPipe[0]) == 0 && close_nid_postfix(flockPipe[1]) == 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);
char directoryBytes[8]{};
struct { void* base; std::size_t length; } directoryVector[2] = {{directoryBytes, 3}, {directoryBytes + 3, 5}};
Require(read_nid_postfix(directoryDescriptor, directoryBytes, sizeof(directoryBytes)) == -1 && *__error_nid_postfix() == 21);
Require(sceKernelRead(directoryDescriptor, directoryBytes, sizeof(directoryBytes)) == static_cast<int>(0x80020015u));
Require(sceKernelReadv(directoryDescriptor, directoryVector, 2) == static_cast<int>(0x80020015u));
Require(sceKernelPread(directoryDescriptor, directoryBytes, sizeof(directoryBytes), 0) == static_cast<int>(0x80020015u));
Require(sceKernelPreadv(directoryDescriptor, directoryVector, 2, 0) == static_cast<int>(0x80020015u));
Require(sceKernelPwrite(directoryDescriptor, "x", 1, 0) == static_cast<int>(0x80020009u));
std::vector<char> listing(4096);
Require(sceKernelGetdents(directoryDescriptor, listing.data(), static_cast<int>(listing.size())) > 0);
const int plainFile = open_nid_postfix(presentName.c_str(), 0, 0);
Require(plainFile >= 0);
Require(sceKernelGetdents(plainFile, listing.data(), static_cast<int>(listing.size())) == static_cast<int>(0x80020016u));
Require(getdents_nid_postfix(plainFile, listing.data(), static_cast<int>(listing.size())) == -1 && *__error_nid_postfix() == 22);
int64_t plainBase = 0;
Require(getdirentries_nid_postfix(plainFile, listing.data(), static_cast<int>(listing.size()), &plainBase) == -1 && *__error_nid_postfix() == 22);
Require(close_nid_postfix(plainFile) == 0);
Require(close_nid_postfix(directoryDescriptor) == 0);
const int flaggedDirectory = sceKernelOpen(rootName.c_str(), 0x20000, 0);
Require(flaggedDirectory >= 0 && sceKernelClose(flaggedDirectory) == 0);
std::vector<char> entries(4096);
for (const auto& directoryName : {std::string("/"), std::string("."), rootName + "/", rootName + "/.."}) {
const int listed = sceKernelOpen(directoryName.c_str(), 0x20000, 0);
Require(listed >= 0);
int read = sceKernelGetdents(listed, entries.data(), static_cast<int>(entries.size()));
Require(read > 0);
while (read > 0) read = sceKernelGetdents(listed, entries.data(), static_cast<int>(entries.size()));
Require(read == 0 && sceKernelClose(listed) == 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));
const auto invalidFlagsName = (root / "invalid-flags.txt").string();
Require(sceKernelOpen(presentName.c_str(), 3, 0) == static_cast<int>(0x80020016u));
Require(sceKernelOpen(missingName.c_str(), 0x203, 0600) == static_cast<int>(0x80020016u));
Require(open_nid_postfix(presentName.c_str(), 3, 0) == -1 && *__error_nid_postfix() == 22);
Require(_open_nid_postfix(presentName.c_str(), 0x403) == -1 && *__error_nid_postfix() == 22);
Require(sceKernelOpen(invalidFlagsName.c_str(), 0x20a02, 0600) == static_cast<int>(0x80020016u));
Require(open_nid_postfix(invalidFlagsName.c_str(), 0x20a00, 0600) == -1 && *__error_nid_postfix() == 22);
Require(!std::filesystem::exists(invalidFlagsName));
Require(open_nid_postfix(nullptr, 3, 0) == -1 && *__error_nid_postfix() == 22);
Require(open_nid_postfix("", 0x20a00, 0600) == -1 && *__error_nid_postfix() == 22);
Require(_open_nid_postfix(nullptr, 0x403) == -1 && *__error_nid_postfix() == 22);
Require(_open_nid_postfix("", 3) == -1 && *__error_nid_postfix() == 22);
Require(std::filesystem::file_size(present) == 5);
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 missingChild = missingName + "/child";
for (const auto* blocked : {&underFile, &deepUnderFile, &missingChild}) {
const int expected = blocked == &missingChild ? 2 : 20;
FileStat blockedStatus{};
Require(open_nid_postfix(blocked->c_str(), 0, 0) == -1 && *__error_nid_postfix() == expected);
Require(open_nid_postfix(blocked->c_str(), 0x202, 0644) == -1 && *__error_nid_postfix() == expected);
Require(stat_nid_postfix(blocked->c_str(), &blockedStatus) == -1 && *__error_nid_postfix() == expected);
Require(lstat_nid_postfix(blocked->c_str(), &blockedStatus) == -1 && *__error_nid_postfix() == expected);
Require(access_nid_postfix(blocked->c_str(), 0) == -1 && *__error_nid_postfix() == expected);
Require(unlink_nid_postfix(blocked->c_str()) == -1 && *__error_nid_postfix() == expected);
Require(chmod_nid_postfix(blocked->c_str(), 0644) == -1 && *__error_nid_postfix() == expected);
Require(sceKernelTruncate_nid_postfix(blocked->c_str(), 0) == static_cast<int>(0x80020000u | static_cast<unsigned>(expected)));
Require(sceKernelUtimes_nid_postfix(blocked->c_str(), nullptr) == static_cast<int>(0x80020000u | static_cast<unsigned>(expected)));
Require(rename_nid_postfix(blocked->c_str(), (root / "renamed-target").string().c_str()) == -1 && *__error_nid_postfix() == expected);
if (blocked != &missingChild) Require(rename_nid_postfix(presentName.c_str(), blocked->c_str()) == -1 && *__error_nid_postfix() == expected);
Require(remove_nid_postfix(blocked->c_str()) == -1 && *__error_nid_postfix() == expected);
Require(opendir_nid_postfix(blocked->c_str()) == nullptr && *__error_nid_postfix() == expected);
Require(chdir_nid_postfix(blocked->c_str()) == -1 && *__error_nid_postfix() == expected);
for (const char* openMode : {"r", "w"}) {
Require(fopen_nid_postfix(blocked->c_str(), openMode) == nullptr && *__error_nid_postfix() == expected);
}
}
#ifndef _WIN32
const auto linkedDirectory = root / "linked-directory";
const auto directoryLink = root / "directory-link";
const auto danglingDirectoryLink = root / "dangling-directory-link";
Require(std::filesystem::create_directory(linkedDirectory));
std::filesystem::create_directory_symlink("linked-directory", directoryLink);
std::filesystem::create_directory_symlink("missing-directory", danglingDirectoryLink);
Require(rmdir_nid_postfix(directoryLink.string().c_str()) == -1 && *__error_nid_postfix() == 20);
Require(sceKernelRmdir(directoryLink.string().c_str()) == static_cast<int>(0x80020014u));
Require(rmdir_nid_postfix(danglingDirectoryLink.string().c_str()) == -1 && *__error_nid_postfix() == 20);
Require(std::filesystem::is_symlink(directoryLink) && std::filesystem::is_directory(linkedDirectory));
Require(unlink_nid_postfix(directoryLink.string().c_str()) == 0 && unlink_nid_postfix(danglingDirectoryLink.string().c_str()) == 0);
Require(rmdir_nid_postfix(linkedDirectory.string().c_str()) == 0 && !std::filesystem::exists(linkedDirectory));
#endif
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);
FileStat unreachable{};
Require(sceKernelOpen(longLeaf.c_str(), 0, 0) == static_cast<int>(0x8002003fu));
Require(sceKernelOpen(longLeaf.c_str(), 0x201, 0600) == static_cast<int>(0x8002003fu));
Require(open_nid_postfix(longLeaf.c_str(), 0, 0) == -1 && *__error_nid_postfix() == 63);
Require(sceKernelUnlink(longLeaf.c_str()) == static_cast<int>(0x8002003fu));
Require(unlink_nid_postfix(longLeaf.c_str()) == -1 && *__error_nid_postfix() == 63);
Require(sceKernelStat(longLeaf.c_str(), &unreachable) == static_cast<int>(0x8002003fu));
Require(stat_nid_postfix(longLeaf.c_str(), &unreachable) == -1 && *__error_nid_postfix() == 63);
const auto loop = root / "loop";
std::filesystem::create_symlink("loop", loop);
const auto loopName = loop.string();
const auto underLoop = loopName + "/leaf";
Require(sceKernelOpen(loopName.c_str(), 0, 0) == static_cast<int>(0x8002003eu));
Require(open_nid_postfix(loopName.c_str(), 0, 0) == -1 && *__error_nid_postfix() == 62);
Require(sceKernelStat(loopName.c_str(), &unreachable) == static_cast<int>(0x8002003eu));
Require(stat_nid_postfix(loopName.c_str(), &unreachable) == -1 && *__error_nid_postfix() == 62);
Require(sceKernelUnlink(underLoop.c_str()) == static_cast<int>(0x8002003eu));
Require(unlink_nid_postfix(underLoop.c_str()) == -1 && *__error_nid_postfix() == 62);
Require(unlink_nid_postfix(loopName.c_str()) == 0 && !std::filesystem::is_symlink(loop));
#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);
auto* stream = fopen_nid_postfix(device, "rb");
Require(stream != nullptr);
Require(setvbuf_nid_postfix(stream, nullptr, 2, 0) == 0);
std::memset(first, 0, sizeof(first));
std::memset(second, 0, sizeof(second));
Require(fread_nid_postfix(first, 1, sizeof(first), stream) == sizeof(first));
Require(fread_nid_postfix(second, 8, 8, stream) == 8);
Require(std::memcmp(first, second, sizeof(first)) != 0);
Require(feof_nid_postfix(stream) == 0 && ferror_nid_postfix(stream) == 0);
flockfile_nid_postfix(stream);
Require(fread_nid_postfix(first, 1, 4, stream) == 4);
funlockfile_nid_postfix(stream);
Require(ftrylockfile_nid_postfix(stream) == 0);
funlockfile_nid_postfix(stream);
Require(fflush_nid_postfix(stream) == 0);
bool unsupported = false;
try {
fgetc_nid_postfix(stream);
} catch (const std::runtime_error&) {
unsupported = true;
}
Require(unsupported);
Require(fclose_nid_postfix(stream) == 0);
for (const char* mode : {"wb", "ab", "r+b"}) {
bool rejected = false;
try {
fopen_nid_postfix(device, mode);
} catch (const std::runtime_error&) {
rejected = true;
}
Require(rejected);
}
}
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));
}