mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 08:33:12 -05:00
Guest path resolution trimmed the trailing slash of a path under a
mount alias (/savedata0, /download0, /app0 and the others), so the host
saw "file" for "file/": unlink, remove, sceKernelUnlink and rename
removed or moved the file, open opened it, and chmod and access
succeeded on it. The same path outside an alias already failed with
ENOTDIR, because the host got the slash.
FreeBSD 11.4 rejects it in lookup() (sys/kern/vfs_lookup.c):
/*
* If we're processing a path with a trailing slash,
* check that the end result is a directory.
*/
if ((cnp->cn_flags & TRAILINGSLASH) && dp->v_type != VDIR) {
error = ENOTDIR;
goto bad2;
}
On Linux and macOS a path under an alias now keeps its trailing slash
on the host path, as paths outside an alias do, so the host fails with
ENOTDIR for a file and directories with a trailing slash still work.
Windows keeps the previous behaviour; TechnicalDebt records it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
917 lines
61 KiB
C++
917 lines
61 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>
|
|
#include <string>
|
|
#include <vector>
|
|
#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 chflags_nid_postfix(const char*, 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 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);
|
|
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*);
|
|
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*);
|
|
}
|
|
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 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));
|
|
{ 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);
|
|
}
|
|
|
|
int main() {
|
|
TestParentTraversal();
|
|
TestRelinkedModuleNames();
|
|
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(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);
|
|
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 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"));
|
|
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::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(), ×) == 0 && times.st_mtim.tv_sec == 1000000000);
|
|
Require(futimes_nid_postfix(descriptor, nullptr) == 0);
|
|
Require(stat_nid_postfix(sized.string().c_str(), ×) == 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);
|
|
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);
|
|
#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);
|
|
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);
|
|
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));
|
|
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));
|
|
#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);
|
|
#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));
|
|
}
|