mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 00:22:57 -05:00
The case that removes search permission from a directory and expects fchdir to fail with EACCES, leave the working directory unchanged and succeed again once search is allowed ran only on Linux. It now runs on every host but Windows, with the POSIX headers it needs.
485 lines
32 KiB
C++
485 lines
32 KiB
C++
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
#include "prx/libkernel/File/include/FileFlags.hpp"
|
|
#include <chrono>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <source_location>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
|
|
#define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE 0x2
|
|
#endif
|
|
#endif
|
|
#ifndef _WIN32
|
|
#include <cerrno>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
extern "C" {
|
|
int APS5_VABI fstatfs_nid_postfix(int, void*);
|
|
int APS5_VABI _fstatfs_nid_postfix(int, void*);
|
|
int APS5_VABI open_nid_postfix(const char*, int, int);
|
|
int APS5_VABI close_nid_postfix(int);
|
|
std::int64_t APS5_VABI readlink_nid_postfix(const char*, char*, std::size_t);
|
|
std::int64_t APS5_VABI pathconf_nid_postfix(const char*, int);
|
|
int APS5_VABI fchdir_nid_postfix(int);
|
|
int APS5_VABI chdir_nid_postfix(const char*);
|
|
int APS5_VABI chroot_nid_postfix(const char*);
|
|
char* APS5_VABI getcwd_nid_postfix(char*, std::size_t);
|
|
int APS5_VABI sceKernelOpen(const char*, int, std::uint16_t);
|
|
int APS5_VABI sceKernelClose(int);
|
|
int APS5_VABI setsid_nid_postfix(void);
|
|
int APS5_VABI link_nid_postfix(const char*, const char*);
|
|
int APS5_VABI symlink_nid_postfix(const char*, const char*);
|
|
int APS5_VABI openat_nid_postfix(int, const char*, int, ...);
|
|
int APS5_VABI unlinkat_nid_postfix(int, const char*, int);
|
|
std::int64_t APS5_VABI readlinkat_nid_postfix(int, const char*, char*, std::size_t);
|
|
int APS5_VABI mkdirat_nid_postfix(int, const char*, std::uint16_t);
|
|
int APS5_VABI renameat_nid_postfix(int, const char*, int, const char*);
|
|
int APS5_VABI fchmodat_nid_postfix(int, const char*, int, int);
|
|
std::int64_t APS5_VABI sceKernelRead(int, void*, std::size_t);
|
|
std::uint16_t APS5_VABI umask_nid_postfix(std::uint16_t);
|
|
int* APS5_VABI __error_nid_postfix();
|
|
}
|
|
|
|
void Require(bool condition, std::source_location location = std::source_location::current()) {
|
|
if (!condition) {
|
|
std::fprintf(stderr, "Kernel sys check failed at line %u (guest errno %d)\n", location.line(), *__error_nid_postfix());
|
|
std::abort();
|
|
}
|
|
}
|
|
|
|
int Error() {
|
|
return *__error_nid_postfix();
|
|
}
|
|
|
|
bool PathconfThrows(const char* path, int name) {
|
|
try {
|
|
pathconf_nid_postfix(path, name);
|
|
} catch (const std::runtime_error&) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ChrootThrows(const std::string& path) {
|
|
try {
|
|
chroot_nid_postfix(path.c_str());
|
|
} catch (const std::runtime_error& error) {
|
|
return std::strstr(error.what(), path.c_str()) != nullptr;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CheckPathconf(const std::filesystem::path& root, const std::string& file) {
|
|
const auto directory = root.generic_string();
|
|
for (const auto& path : {file, directory}) {
|
|
Require(pathconf_nid_postfix(path.c_str(), 1) == 32767);
|
|
Require(pathconf_nid_postfix(path.c_str(), 4) == 255);
|
|
Require(pathconf_nid_postfix(path.c_str(), 5) == 1024);
|
|
Require(pathconf_nid_postfix(path.c_str(), 6) == 512);
|
|
Require(pathconf_nid_postfix(path.c_str(), 7) == 1);
|
|
Require(pathconf_nid_postfix(path.c_str(), 8) == 1);
|
|
Require(pathconf_nid_postfix(path.c_str(), 12) == 64);
|
|
*__error_nid_postfix() = 0;
|
|
Require(pathconf_nid_postfix(path.c_str(), 15) == -1 && Error() == 0);
|
|
Require(pathconf_nid_postfix(path.c_str(), 18) == 1024);
|
|
Require(pathconf_nid_postfix(path.c_str(), 54) == 0);
|
|
Require(pathconf_nid_postfix(path.c_str(), 55) == 0);
|
|
}
|
|
for (const int name : {0, 11, 61, 62, 65, -1})
|
|
Require(pathconf_nid_postfix(file.c_str(), name) == -1 && Error() == 22);
|
|
for (const int name : {2, 3, 9, 10, 14, 16, 17, 21, 53, 59, 60, 63, 64})
|
|
Require(PathconfThrows(file.c_str(), name));
|
|
const auto missing = (root / "missing").string();
|
|
Require(pathconf_nid_postfix(missing.c_str(), 4) == -1 && Error() == 2);
|
|
Require(pathconf_nid_postfix(missing.c_str(), 0) == -1 && Error() == 2);
|
|
Require(pathconf_nid_postfix((root / "missing" / "file").string().c_str(), 4) == -1 && Error() == 2);
|
|
Require(pathconf_nid_postfix("", 4) == -1 && Error() == 2);
|
|
Require(pathconf_nid_postfix(nullptr, 4) == -1 && Error() == 14);
|
|
const auto longName = directory + "/" + std::string(256, 'n');
|
|
Require(pathconf_nid_postfix(longName.c_str(), 4) == -1 && Error() == 63);
|
|
Require(pathconf_nid_postfix((directory + "/missing/" + std::string(256, 'n')).c_str(), 4) == -1 && Error() == 2);
|
|
const auto longPath = directory + "/" + std::string(1024, 'p');
|
|
Require(pathconf_nid_postfix(longPath.c_str(), 4) == -1 && Error() == 63);
|
|
}
|
|
|
|
int main() {
|
|
Require(setsid_nid_postfix() == -1 && Error() == 1);
|
|
Require(umask_nid_postfix(0) == 022);
|
|
Require(umask_nid_postfix(07777) == 0);
|
|
Require(umask_nid_postfix(022) == 07777);
|
|
const auto root = std::filesystem::path("anyps5-kernel-sys-test-" +
|
|
std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
|
|
Require(std::filesystem::create_directory(root));
|
|
const auto file = (root / "file.txt").string();
|
|
std::ofstream(file).put('x');
|
|
CheckPathconf(root, file);
|
|
const int descriptor = open_nid_postfix(file.c_str(), 0, 0);
|
|
Require(descriptor >= 0);
|
|
unsigned char viaPublic[472] = {};
|
|
unsigned char viaSyscall[472] = {};
|
|
Require(fstatfs_nid_postfix(descriptor, viaPublic) == 0 && _fstatfs_nid_postfix(descriptor, viaSyscall) == 0);
|
|
Require(std::memcmp(viaPublic, viaSyscall, 4) == 0 && std::memcmp(viaPublic + 16, viaSyscall + 16, 8) == 0);
|
|
Require(std::memcmp(viaPublic + 280, viaSyscall + 280, 16) == 0);
|
|
Require(fstatfs_nid_postfix(descriptor, nullptr) == -1 && Error() == 14);
|
|
Require(close_nid_postfix(descriptor) == 0);
|
|
Require(fstatfs_nid_postfix(-1, viaPublic) == -1 && Error() == 9);
|
|
char buffer[64];
|
|
for (const auto& component : {std::string("missing"), std::string("file.txt")}) {
|
|
const auto prefix = root.generic_string() + "/" + component + "/../";
|
|
const auto invalid = prefix + "file.txt";
|
|
const auto created = prefix + "new.txt";
|
|
const int expected = component == "missing" ? 2 : 20;
|
|
Require(readlink_nid_postfix(invalid.c_str(), buffer, sizeof(buffer)) == -1 && Error() == expected);
|
|
Require(link_nid_postfix(invalid.c_str(), created.c_str()) == -1 && Error() == expected);
|
|
Require(link_nid_postfix(file.c_str(), created.c_str()) == -1 && Error() == expected);
|
|
Require(symlink_nid_postfix("file.txt", created.c_str()) == -1 && Error() == expected);
|
|
Require(std::filesystem::hard_link_count(file) == 1 && !std::filesystem::exists(root / "new.txt"));
|
|
}
|
|
Require(readlink_nid_postfix(file.c_str(), buffer, sizeof(buffer)) == -1 && Error() == 22);
|
|
Require(readlink_nid_postfix(root.string().c_str(), buffer, sizeof(buffer)) == -1 && Error() == 22);
|
|
Require(readlink_nid_postfix((root / "missing").string().c_str(), buffer, sizeof(buffer)) == -1 && Error() == 2);
|
|
Require(readlink_nid_postfix("", buffer, sizeof(buffer)) == -1 && Error() == 2);
|
|
Require(readlink_nid_postfix(nullptr, buffer, sizeof(buffer)) == -1 && Error() == 14);
|
|
#ifndef _WIN32
|
|
Require(readlink_nid_postfix((root / std::string(300, 'n')).string().c_str(), buffer, sizeof(buffer)) == -1 && Error() == 63);
|
|
#endif
|
|
std::error_code error;
|
|
const auto link = (root / "link").string();
|
|
std::filesystem::create_symlink("sub/target.txt", link, error);
|
|
if (!error) {
|
|
std::memset(buffer, '#', sizeof(buffer));
|
|
Require(readlink_nid_postfix(link.c_str(), buffer, sizeof(buffer)) == 14);
|
|
Require(std::memcmp(buffer, "sub/target.txt", 14) == 0 && buffer[14] == '#');
|
|
std::memset(buffer, '#', sizeof(buffer));
|
|
Require(readlink_nid_postfix(link.c_str(), buffer, 3) == 3);
|
|
Require(std::memcmp(buffer, "sub", 3) == 0 && buffer[3] == '#');
|
|
Require(readlink_nid_postfix(link.c_str(), nullptr, 0) == 0);
|
|
Require(readlink_nid_postfix(link.c_str(), nullptr, sizeof(buffer)) == -1 && Error() == 14);
|
|
Require(readlink_nid_postfix((root / "LINK").string().c_str(), buffer, sizeof(buffer)) == 14);
|
|
const int linkDir = sceKernelOpen(root.string().c_str(), SCE_KERNEL_O_RDONLY | SCE_KERNEL_O_DIRECTORY, 0);
|
|
Require(linkDir >= 0);
|
|
std::memset(buffer, '#', sizeof(buffer));
|
|
Require(readlinkat_nid_postfix(linkDir, "link", buffer, sizeof(buffer)) == 14);
|
|
Require(std::memcmp(buffer, "sub/target.txt", 14) == 0 && buffer[14] == '#');
|
|
Require(readlinkat_nid_postfix(linkDir, "link", buffer, 3) == 3 && std::memcmp(buffer, "sub", 3) == 0);
|
|
Require(readlinkat_nid_postfix(linkDir, "link", nullptr, 0) == 0);
|
|
Require(readlinkat_nid_postfix(linkDir, "link", nullptr, sizeof(buffer)) == -1 && Error() == 14);
|
|
const auto guestLink = "/" + root.filename().string() + "/link";
|
|
Require(readlinkat_nid_postfix(-1, guestLink.c_str(), buffer, sizeof(buffer)) == 14 && std::memcmp(buffer, "sub/target.txt", 14) == 0);
|
|
Require(readlinkat_nid_postfix(-100, link.c_str(), buffer, sizeof(buffer)) == 14 && std::memcmp(buffer, "sub/target.txt", 14) == 0);
|
|
Require(sceKernelClose(linkDir) == 0);
|
|
#ifndef _WIN32
|
|
std::filesystem::create_symlink("loopB", root / "loopA");
|
|
std::filesystem::create_symlink("loopA", root / "loopB");
|
|
Require(readlink_nid_postfix((root / "loopA" / "inner").string().c_str(), buffer, sizeof(buffer)) == -1 && Error() == 62);
|
|
#endif
|
|
} else {
|
|
std::fprintf(stderr, "symbolic links unavailable: the link cases not tested\n");
|
|
}
|
|
const auto original = (root / "original.bin").string();
|
|
const auto linked = (root / "linked.bin").string();
|
|
std::ofstream(original) << "abc";
|
|
Require(link_nid_postfix(original.c_str(), linked.c_str()) == 0);
|
|
Require(std::filesystem::hard_link_count(original) == 2 && std::filesystem::file_size(linked) == 3);
|
|
std::ofstream(linked, std::ios::trunc).close();
|
|
Require(std::filesystem::file_size(original) == 0);
|
|
Require(link_nid_postfix(original.c_str(), linked.c_str()) == -1 && Error() == 17);
|
|
Require(link_nid_postfix((root / "missing").string().c_str(), (root / "other").string().c_str()) == -1 && Error() == 2);
|
|
Require(link_nid_postfix(original.c_str(), (root / "missing" / "linked.bin").string().c_str()) == -1 && Error() == 2);
|
|
Require(link_nid_postfix(root.string().c_str(), (root / "directory").string().c_str()) == -1 && Error() == 1);
|
|
Require(link_nid_postfix(nullptr, linked.c_str()) == -1 && Error() == 14);
|
|
Require(link_nid_postfix(original.c_str(), "") == -1 && Error() == 2);
|
|
Require(std::filesystem::remove(linked) && std::filesystem::hard_link_count(original) == 1);
|
|
const int directory = sceKernelOpen(root.string().c_str(), SCE_KERNEL_O_RDONLY | SCE_KERNEL_O_DIRECTORY, 0);
|
|
Require(directory >= 0);
|
|
const int fileDescriptor = sceKernelOpen(file.c_str(), SCE_KERNEL_O_RDONLY, 0);
|
|
Require(fileDescriptor >= 0);
|
|
Require(fchdir_nid_postfix(-1) == -1 && Error() == 9);
|
|
Require(fchdir_nid_postfix(fileDescriptor) == -1 && Error() == 20);
|
|
Require(fchdir_nid_postfix(directory) == 0);
|
|
char directoryPath[1024];
|
|
Require(getcwd_nid_postfix(directoryPath, sizeof(directoryPath)) == directoryPath);
|
|
Require(std::strcmp(directoryPath, ("/" + root.filename().string()).c_str()) == 0);
|
|
#ifndef _WIN32
|
|
Require(chdir_nid_postfix("/") == 0);
|
|
const auto originalPermissions = std::filesystem::status(root).permissions();
|
|
std::filesystem::permissions(root, std::filesystem::perms::owner_read | std::filesystem::perms::owner_write);
|
|
const bool searchDenied = ::access(root.c_str(), X_OK) == -1 && errno == EACCES;
|
|
const int deniedResult = searchDenied ? fchdir_nid_postfix(directory) : 0;
|
|
const int deniedError = Error();
|
|
const bool cwdUnchanged = getcwd_nid_postfix(directoryPath, sizeof(directoryPath)) == directoryPath && std::strcmp(directoryPath, "/") == 0;
|
|
std::filesystem::permissions(root, std::filesystem::perms::owner_exec);
|
|
const int searchOnlyResult = fchdir_nid_postfix(directory);
|
|
std::filesystem::permissions(root, originalPermissions);
|
|
if (searchDenied) Require(deniedResult == -1 && deniedError == EACCES && cwdUnchanged);
|
|
Require(searchOnlyResult == 0);
|
|
#endif
|
|
Require(sceKernelClose(fileDescriptor) == 0);
|
|
Require(sceKernelClose(directory) == 0);
|
|
Require(fchdir_nid_postfix(directory) == -1 && Error() == 9);
|
|
Require(chdir_nid_postfix("/") == 0);
|
|
Require(chroot_nid_postfix(nullptr) == -1 && Error() == 14);
|
|
Require(chroot_nid_postfix("") == -1 && Error() == 2);
|
|
Require(chroot_nid_postfix((root / "missing").string().c_str()) == -1 && Error() == 2);
|
|
Require(chroot_nid_postfix(file.c_str()) == -1 && Error() == 20);
|
|
Require(chroot_nid_postfix((root / "file.txt" / "child").string().c_str()) == -1 && Error() == 20);
|
|
Require(chroot_nid_postfix((file + "/").c_str()) == -1 && Error() == 20);
|
|
Require(chroot_nid_postfix((root / "file.txt" / "..").string().c_str()) == -1 && Error() == 20);
|
|
Require(chroot_nid_postfix((root / std::string(256, 'n')).string().c_str()) == -1 && Error() == 63);
|
|
Require(chroot_nid_postfix(("/" + std::string(1024, 'p')).c_str()) == -1 && Error() == 63);
|
|
Require(ChrootThrows(root.string()));
|
|
Require(ChrootThrows("/" + root.filename().string() + "/."));
|
|
const auto guestRoot = "/" + root.filename().string();
|
|
const auto chrootFile = guestRoot + "/file.txt";
|
|
const int top = sceKernelOpen("/", SCE_KERNEL_O_RDONLY | SCE_KERNEL_O_DIRECTORY, 0);
|
|
Require(top >= 0);
|
|
Require(chdir_nid_postfix(guestRoot.c_str()) == 0);
|
|
Require(fchdir_nid_postfix(top) == 0 && chroot_nid_postfix(".") == 0);
|
|
Require(getcwd_nid_postfix(directoryPath, sizeof(directoryPath)) == directoryPath && std::strcmp(directoryPath, "/") == 0);
|
|
Require(sceKernelClose(top) == 0);
|
|
Require(chroot_nid_postfix("/") == 0 && chroot_nid_postfix("/..") == 0 && chroot_nid_postfix("//./") == 0);
|
|
Require(chdir_nid_postfix(guestRoot.c_str()) == 0);
|
|
Require(chroot_nid_postfix("..") == 0 && chroot_nid_postfix("../..") == 0);
|
|
Require(getcwd_nid_postfix(directoryPath, sizeof(directoryPath)) == directoryPath && guestRoot == directoryPath);
|
|
Require(ChrootThrows("."));
|
|
for (const auto& path : {std::string("file.txt"), chrootFile, "/../../.." + chrootFile, "../.." + chrootFile}) {
|
|
const int opened = sceKernelOpen(path.c_str(), SCE_KERNEL_O_RDONLY, 0);
|
|
char byte = 0;
|
|
Require(opened >= 0 && sceKernelRead(opened, &byte, 1) == 1 && byte == 'x');
|
|
Require(sceKernelClose(opened) == 0);
|
|
}
|
|
Require(chdir_nid_postfix("/") == 0);
|
|
Require(getcwd_nid_postfix(directoryPath, sizeof(directoryPath)) == directoryPath && std::strcmp(directoryPath, "/") == 0);
|
|
Require(readlink_nid_postfix(file.c_str(), buffer, sizeof(buffer)) == -1 && Error() == 22);
|
|
const auto symbolic = (root / "symbolic").string();
|
|
const int created = symlink_nid_postfix("original.bin", symbolic.c_str());
|
|
if (created == 0) {
|
|
const auto dangling = (root / "dangling").string();
|
|
#ifdef _WIN32
|
|
Require((GetFileAttributesW(std::filesystem::path(symbolic).wstring().c_str()) & FILE_ATTRIBUTE_REPARSE_POINT) != 0);
|
|
Require(symlink_nid_postfix("original.bin", symbolic.c_str()) == -1 && Error() == 17);
|
|
Require(symlink_nid_postfix("missing.bin", dangling.c_str()) == 0);
|
|
Require((GetFileAttributesW(std::filesystem::path(dangling).wstring().c_str()) & FILE_ATTRIBUTE_REPARSE_POINT) != 0);
|
|
Require(symlink_nid_postfix("missing.bin", dangling.c_str()) == -1 && Error() == 17);
|
|
Require(readlink_nid_postfix(symbolic.c_str(), buffer, sizeof(buffer)) == 12 && std::memcmp(buffer, "original.bin", 12) == 0);
|
|
Require(readlink_nid_postfix(dangling.c_str(), buffer, sizeof(buffer)) == 11 && std::memcmp(buffer, "missing.bin", 11) == 0);
|
|
const auto nested = (root / "nested").string();
|
|
Require(symlink_nid_postfix("sub/target.txt", nested.c_str()) == 0);
|
|
std::memset(buffer, '#', sizeof(buffer));
|
|
Require(readlink_nid_postfix(nested.c_str(), buffer, sizeof(buffer)) == 14);
|
|
Require(std::memcmp(buffer, "sub/target.txt", 14) == 0 && buffer[14] == '#');
|
|
Require(readlink_nid_postfix(nested.c_str(), buffer, 3) == 3 && std::memcmp(buffer, "sub", 3) == 0);
|
|
Require(readlink_nid_postfix(nested.c_str(), nullptr, 0) == 0);
|
|
Require(readlink_nid_postfix(nested.c_str(), nullptr, sizeof(buffer)) == -1 && Error() == 14);
|
|
const auto accented = (root / "accented").string();
|
|
Require(symlink_nid_postfix("a\xc3\xa7\xc3\xa3o.bin", accented.c_str()) == 0);
|
|
Require(readlink_nid_postfix(accented.c_str(), buffer, sizeof(buffer)) == 10 && std::memcmp(buffer, "a\xc3\xa7\xc3\xa3o.bin", 10) == 0);
|
|
Require(std::filesystem::create_directory(root / "sub"));
|
|
const auto directoryLink = (root / "directory-link").string();
|
|
Require(symlink_nid_postfix("sub", directoryLink.c_str()) == 0);
|
|
Require(readlink_nid_postfix(directoryLink.c_str(), buffer, sizeof(buffer)) == 3 && std::memcmp(buffer, "sub", 3) == 0);
|
|
const auto absolute = std::filesystem::absolute(root / "absolute");
|
|
Require(CreateSymbolicLinkW(absolute.wstring().c_str(), std::filesystem::absolute(original).wstring().c_str(),
|
|
SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) != 0);
|
|
bool refused = false;
|
|
try {
|
|
readlink_nid_postfix((root / "absolute").string().c_str(), buffer, sizeof(buffer));
|
|
} catch (const std::runtime_error& failure) {
|
|
refused = std::string(failure.what()) == "readlink of a Windows reparse point other than a relative symbolic link not implemented";
|
|
}
|
|
Require(refused);
|
|
#else
|
|
Require(std::filesystem::is_symlink(std::filesystem::symlink_status(symbolic)));
|
|
Require(readlink_nid_postfix(symbolic.c_str(), buffer, sizeof(buffer)) == 12 && std::memcmp(buffer, "original.bin", 12) == 0);
|
|
Require(std::filesystem::file_size(symbolic) == std::filesystem::file_size(original));
|
|
Require(symlink_nid_postfix("original.bin", symbolic.c_str()) == -1 && Error() == 17);
|
|
Require(symlink_nid_postfix("missing.bin", dangling.c_str()) == 0);
|
|
Require(readlink_nid_postfix(dangling.c_str(), buffer, sizeof(buffer)) == 11 && !std::filesystem::exists(dangling));
|
|
Require(symlink_nid_postfix("missing.bin", dangling.c_str()) == -1 && Error() == 17);
|
|
#endif
|
|
} else {
|
|
Require(created == -1 && Error() == 1);
|
|
std::fprintf(stderr, "symbolic links unavailable: the symlink success cases not tested\n");
|
|
}
|
|
Require(symlink_nid_postfix("original.bin", (root / "missing" / "symbolic").string().c_str()) == -1 && Error() == 2);
|
|
Require(symlink_nid_postfix(nullptr, symbolic.c_str()) == -1 && Error() == 14);
|
|
Require(symlink_nid_postfix("original.bin", "") == -1 && Error() == 2);
|
|
const int base = sceKernelOpen(root.string().c_str(), SCE_KERNEL_O_RDONLY | SCE_KERNEL_O_DIRECTORY, 0);
|
|
Require(base >= 0);
|
|
const int relative = openat_nid_postfix(base, "file.txt", SCE_KERNEL_O_RDONLY);
|
|
char byte = 0;
|
|
Require(relative >= 0 && sceKernelRead(relative, &byte, 1) == 1 && byte == 'x');
|
|
Require(sceKernelClose(relative) == 0);
|
|
const int made = openat_nid_postfix(base, "made.bin", SCE_KERNEL_O_WRONLY | SCE_KERNEL_O_CREAT | SCE_KERNEL_O_EXCL, 0644);
|
|
Require(made >= 0 && std::filesystem::is_regular_file(root / "made.bin"));
|
|
Require(sceKernelClose(made) == 0);
|
|
Require(openat_nid_postfix(base, "made.bin", SCE_KERNEL_O_WRONLY | SCE_KERNEL_O_CREAT | SCE_KERNEL_O_EXCL, 0644) == -1 && Error() == 17);
|
|
Require(openat_nid_postfix(base, "missing.bin", SCE_KERNEL_O_RDONLY) == -1 && Error() == 2);
|
|
const auto guestFile = "/" + root.filename().string() + "/file.txt";
|
|
const int absolute = openat_nid_postfix(-1, guestFile.c_str(), SCE_KERNEL_O_RDONLY);
|
|
Require(absolute >= 0 && sceKernelClose(absolute) == 0);
|
|
const int current = openat_nid_postfix(-100, file.c_str(), SCE_KERNEL_O_RDONLY);
|
|
Require(current >= 0 && sceKernelClose(current) == 0);
|
|
const int plain = sceKernelOpen(file.c_str(), SCE_KERNEL_O_RDONLY, 0);
|
|
Require(plain >= 0);
|
|
Require(openat_nid_postfix(plain, "file.txt", SCE_KERNEL_O_RDONLY) == -1 && Error() == 20);
|
|
Require(readlinkat_nid_postfix(base, "file.txt", buffer, sizeof(buffer)) == -1 && Error() == 22);
|
|
Require(readlinkat_nid_postfix(base, "missing.bin", buffer, sizeof(buffer)) == -1 && Error() == 2);
|
|
Require(readlinkat_nid_postfix(plain, "file.txt", buffer, sizeof(buffer)) == -1 && Error() == 20);
|
|
Require(readlinkat_nid_postfix(-1, "file.txt", buffer, sizeof(buffer)) == -1 && Error() == 9);
|
|
Require(readlinkat_nid_postfix(base, nullptr, buffer, sizeof(buffer)) == -1 && Error() == 14);
|
|
Require(readlinkat_nid_postfix(base, "", buffer, sizeof(buffer)) == -1 && Error() == 2);
|
|
Require(readlinkat_nid_postfix(-1, guestFile.c_str(), buffer, sizeof(buffer)) == -1 && Error() == 22);
|
|
Require(readlinkat_nid_postfix(-100, file.c_str(), buffer, sizeof(buffer)) == -1 && Error() == 22);
|
|
Require(mkdirat_nid_postfix(base, "dir", 0700) == 0 && std::filesystem::is_directory(root / "dir"));
|
|
Require(mkdirat_nid_postfix(base, "dir", 0700) == -1 && Error() == 17);
|
|
Require(mkdirat_nid_postfix(base, "file.txt", 0700) == -1 && Error() == 17);
|
|
Require(mkdirat_nid_postfix(base, "missing/child", 0700) == -1 && Error() == 2);
|
|
Require(mkdirat_nid_postfix(base, "file.txt/child", 0700) == -1 && Error() == 20);
|
|
Require(mkdirat_nid_postfix(base, "slash_dir/", 0700) == 0 && std::filesystem::is_directory(root / "slash_dir"));
|
|
const auto guestSubdir = "/" + root.filename().string() + "/abs_dir";
|
|
Require(mkdirat_nid_postfix(-1, guestSubdir.c_str(), 0700) == 0 && std::filesystem::is_directory(root / "abs_dir"));
|
|
const auto cwdDir = (root / "cwd_dir").string();
|
|
Require(mkdirat_nid_postfix(-100, cwdDir.c_str(), 0700) == 0 && std::filesystem::is_directory(root / "cwd_dir"));
|
|
Require(mkdirat_nid_postfix(plain, "child", 0700) == -1 && Error() == 20);
|
|
Require(mkdirat_nid_postfix(-1, "child", 0700) == -1 && Error() == 9);
|
|
Require(mkdirat_nid_postfix(base, nullptr, 0700) == -1 && Error() == 14);
|
|
Require(mkdirat_nid_postfix(base, "", 0700) == -1 && Error() == 2);
|
|
const std::string longName(256, 'a');
|
|
Require(mkdirat_nid_postfix(base, longName.c_str(), 0700) == -1 && Error() == 63);
|
|
Require(mkdirat_nid_postfix(base, "dir/../dotdot_dir", 0700) == 0 && std::filesystem::is_directory(root / "dotdot_dir"));
|
|
Require(unlinkat_nid_postfix(plain, "file.txt", 0) == -1 && Error() == 20);
|
|
Require(unlinkat_nid_postfix(-1, "file.txt", 0) == -1 && Error() == 9);
|
|
Require(sceKernelClose(plain) == 0);
|
|
Require(unlinkat_nid_postfix(base, "file.txt", 1) == -1 && Error() == 22);
|
|
Require(unlinkat_nid_postfix(base, nullptr, 1) == -1 && Error() == 22);
|
|
Require(unlinkat_nid_postfix(base, nullptr, 0) == -1 && Error() == 14);
|
|
Require(unlinkat_nid_postfix(base, "", 0) == -1 && Error() == 2);
|
|
Require(unlinkat_nid_postfix(base, "missing.bin", 0) == -1 && Error() == 2);
|
|
Require(unlinkat_nid_postfix(base, "missing.bin", 0x800) == -1 && Error() == 2);
|
|
Require(unlinkat_nid_postfix(base, "missing/child", 0) == -1 && Error() == 2);
|
|
Require(unlinkat_nid_postfix(base, "file.txt/child", 0) == -1 && Error() == 20);
|
|
Require(unlinkat_nid_postfix(base, "file.txt/child", 0x800) == -1 && Error() == 20);
|
|
Require(unlinkat_nid_postfix(base, "file.txt/", 0) == -1 && Error() == 20);
|
|
Require(unlinkat_nid_postfix(base, longName.c_str(), 0) == -1 && Error() == 63);
|
|
Require(unlinkat_nid_postfix(base, "file.txt", 0x800) == -1 && Error() == 20);
|
|
Require(unlinkat_nid_postfix(base, ".", 0x800) == -1 && Error() == 22);
|
|
Require(unlinkat_nid_postfix(base, "..", 0x800) == -1 && Error() == 22);
|
|
Require(unlinkat_nid_postfix(base, ".", 0) == -1 && Error() == 1);
|
|
std::filesystem::create_directory(root / "usub");
|
|
Require(unlinkat_nid_postfix(base, "usub", 0) == -1 && Error() == 1);
|
|
std::filesystem::create_directory(root / "nonempty");
|
|
{ std::ofstream out(root / "nonempty" / "f.bin"); out << 'x'; }
|
|
Require(unlinkat_nid_postfix(base, "nonempty", 0x800) == -1 && Error() == 66);
|
|
Require(unlinkat_nid_postfix(base, "nonempty/f.bin", 0) == 0 && !std::filesystem::exists(root / "nonempty" / "f.bin"));
|
|
Require(unlinkat_nid_postfix(base, "nonempty", 0x800) == 0 && !std::filesystem::exists(root / "nonempty"));
|
|
{ std::ofstream out(root / "to_unlink.bin"); out << 'x'; }
|
|
Require(unlinkat_nid_postfix(base, "to_unlink.bin", 0) == 0 && !std::filesystem::exists(root / "to_unlink.bin"));
|
|
{ std::ofstream out(root / "dotdot.bin"); out << 'x'; }
|
|
Require(unlinkat_nid_postfix(base, "usub/../dotdot.bin", 0) == 0 && !std::filesystem::exists(root / "dotdot.bin"));
|
|
std::filesystem::create_directory(root / "uslash_sub");
|
|
Require(unlinkat_nid_postfix(base, "uslash_sub/", 0x800) == 0 && !std::filesystem::exists(root / "uslash_sub"));
|
|
Require(unlinkat_nid_postfix(base, "usub", 0x800) == 0 && !std::filesystem::exists(root / "usub"));
|
|
{ std::ofstream out(root / "abs.bin"); out << 'x'; }
|
|
const auto guestAbsFile = "/" + root.filename().string() + "/abs.bin";
|
|
Require(unlinkat_nid_postfix(-1, guestAbsFile.c_str(), 0) == 0 && !std::filesystem::exists(root / "abs.bin"));
|
|
std::filesystem::create_directory(root / "unlink_abs_dir");
|
|
const auto guestAbsDir = "/" + root.filename().string() + "/unlink_abs_dir";
|
|
Require(unlinkat_nid_postfix(-1, guestAbsDir.c_str(), 0x800) == 0 && !std::filesystem::exists(root / "unlink_abs_dir"));
|
|
{ std::ofstream out(root / "cwd.bin"); out << 'x'; }
|
|
Require(unlinkat_nid_postfix(-100, (root / "cwd.bin").string().c_str(), 0) == 0 && !std::filesystem::exists(root / "cwd.bin"));
|
|
std::filesystem::create_directory(root / "unlink_cwd_dir");
|
|
Require(unlinkat_nid_postfix(-100, (root / "unlink_cwd_dir").string().c_str(), 0x800) == 0 && !std::filesystem::exists(root / "unlink_cwd_dir"));
|
|
Require(openat_nid_postfix(-1, "file.txt", SCE_KERNEL_O_RDONLY) == -1 && Error() == 9);
|
|
Require(openat_nid_postfix(base, nullptr, SCE_KERNEL_O_RDONLY) == -1 && Error() == 14);
|
|
Require(openat_nid_postfix(base, "", SCE_KERNEL_O_RDONLY) == -1 && Error() == 2);
|
|
Require(renameat_nid_postfix(base, nullptr, base, "renamed.txt") == -1 && Error() == 14);
|
|
Require(renameat_nid_postfix(base, "made.bin", base, nullptr) == -1 && Error() == 14);
|
|
Require(renameat_nid_postfix(base, "", base, "renamed.txt") == -1 && Error() == 2);
|
|
Require(renameat_nid_postfix(base, "made.bin", base, "") == -1 && Error() == 2);
|
|
const int bad = sceKernelOpen(file.c_str(), SCE_KERNEL_O_RDONLY, 0);
|
|
Require(bad >= 0);
|
|
Require(renameat_nid_postfix(-1, "made.bin", base, "renamed.txt") == -1 && Error() == 9);
|
|
Require(renameat_nid_postfix(base, "made.bin", -1, "renamed.txt") == -1 && Error() == 9);
|
|
Require(renameat_nid_postfix(bad, "made.bin", base, "renamed.txt") == -1 && Error() == 20);
|
|
Require(renameat_nid_postfix(base, "made.bin", bad, "renamed.txt") == -1 && Error() == 20);
|
|
Require(sceKernelClose(bad) == 0);
|
|
const auto longPart = std::string(256, 'y');
|
|
Require(renameat_nid_postfix(base, longPart.c_str(), base, "renamed.txt") == -1 && Error() == 63);
|
|
Require(renameat_nid_postfix(base, "made.bin", base, longPart.c_str()) == -1 && Error() == 63);
|
|
Require(renameat_nid_postfix(base, ".", base, "renamed.txt") == -1 && Error() == 22);
|
|
Require(renameat_nid_postfix(base, "..", base, "renamed.txt") == -1 && Error() == 22);
|
|
Require(renameat_nid_postfix(base, "made.bin", base, ".") == -1 && Error() == 22);
|
|
Require(renameat_nid_postfix(base, "made.bin", base, "..") == -1 && Error() == 22);
|
|
Require(renameat_nid_postfix(base, "missing.bin", base, "renamed.txt") == -1 && Error() == 2);
|
|
Require(renameat_nid_postfix(base, "made.bin", base, "renamed.bin") == 0);
|
|
Require(!std::filesystem::exists(root / "made.bin"));
|
|
Require(std::filesystem::is_regular_file(root / "renamed.bin"));
|
|
Require(renameat_nid_postfix(base, "renamed.bin", base, "renamed.bin") == 0);
|
|
const auto subPath = root / "sub_dir";
|
|
std::filesystem::create_directory(subPath);
|
|
Require(renameat_nid_postfix(base, "renamed.bin", base, "sub_dir") == -1 && Error() == 21);
|
|
Require(renameat_nid_postfix(base, "sub_dir", base, "renamed.bin") == -1 && Error() == 20);
|
|
Require(renameat_nid_postfix(base, "sub_dir", base, "sub_renamed") == 0);
|
|
Require(!std::filesystem::exists(subPath));
|
|
Require(std::filesystem::is_directory(root / "sub_renamed"));
|
|
const int subFd = sceKernelOpen((root / "sub_renamed").string().c_str(), SCE_KERNEL_O_RDONLY | SCE_KERNEL_O_DIRECTORY, 0);
|
|
Require(subFd >= 0);
|
|
Require(renameat_nid_postfix(base, "renamed.bin", subFd, "nested.bin") == 0);
|
|
Require(!std::filesystem::exists(root / "renamed.bin"));
|
|
Require(std::filesystem::is_regular_file(root / "sub_renamed" / "nested.bin"));
|
|
Require(renameat_nid_postfix(base, "sub_renamed", subFd, "child") == -1 && Error() == 22);
|
|
const auto otherDir = root / "other_dir";
|
|
std::filesystem::create_directory(otherDir);
|
|
{ std::ofstream stream(otherDir / "item.txt"); }
|
|
Require(renameat_nid_postfix(base, "sub_renamed", base, "other_dir") == -1 && Error() == 66);
|
|
Require(renameat_nid_postfix(subFd, "nested.bin/", base, "back.bin") == -1 && Error() == 20);
|
|
Require(renameat_nid_postfix(subFd, "nested.bin", base, "nonexistent/") == -1 && Error() == 20);
|
|
Require(renameat_nid_postfix(subFd, "nested.bin", base, "made.bin") == 0);
|
|
Require(std::filesystem::is_regular_file(root / "made.bin"));
|
|
Require(sceKernelClose(subFd) == 0);
|
|
Require(renameat_nid_postfix(-100, (root / "made.bin").string().c_str(), -100, (root / "final.bin").string().c_str()) == 0);
|
|
Require(std::filesystem::is_regular_file(root / "final.bin"));
|
|
Require(fchmodat_nid_postfix(base, "file.txt", 0600, 0x0200) == 0);
|
|
Require(fchmodat_nid_postfix(-100, (root / "file.txt").string().c_str(), 0644, 0) == 0);
|
|
#ifndef _WIN32
|
|
Require((std::filesystem::status(root / "file.txt").permissions() & std::filesystem::perms::all) ==
|
|
(std::filesystem::perms::owner_read | std::filesystem::perms::owner_write | std::filesystem::perms::group_read | std::filesystem::perms::others_read));
|
|
#endif
|
|
Require(fchmodat_nid_postfix(base, "missing.bin", 0600, 0) == -1 && Error() == 2);
|
|
Require(fchmodat_nid_postfix(base, nullptr, 0600, 0) == -1 && Error() == 14);
|
|
Require(fchmodat_nid_postfix(base, "", 0600, 0) == -1 && Error() == 2);
|
|
Require(fchmodat_nid_postfix(base, "file.txt", 0600, 0x4000) == -1 && Error() == 22);
|
|
Require(fchmodat_nid_postfix(-1, "file.txt", 0600, 0) == -1 && Error() == 9);
|
|
#ifndef _WIN32
|
|
Require(symlink_nid_postfix("loop", (root / "loop").string().c_str()) == 0);
|
|
Require(fchmodat_nid_postfix(base, "loop/x", 0600, 0) == -1 && Error() == 62);
|
|
Require(fchmodat_nid_postfix(base, longName.c_str(), 0600, 0) == -1 && Error() == 63);
|
|
#endif
|
|
Require(sceKernelClose(base) == 0);
|
|
#ifndef _WIN32
|
|
if (created == 0) {
|
|
const auto throughLink = (root / "through-link.bin").string();
|
|
Require(link_nid_postfix(symbolic.c_str(), throughLink.c_str()) == 0);
|
|
Require(!std::filesystem::is_symlink(std::filesystem::symlink_status(throughLink)));
|
|
Require(std::filesystem::equivalent(throughLink, original) && std::filesystem::hard_link_count(original) == 2);
|
|
Require(std::filesystem::remove(throughLink) && std::filesystem::hard_link_count(original) == 1);
|
|
Require(link_nid_postfix((root / "dangling").string().c_str(), throughLink.c_str()) == -1 && Error() == 2);
|
|
const auto directoryLink = (root / "directory-link").string();
|
|
Require(symlink_nid_postfix(".", directoryLink.c_str()) == 0);
|
|
Require(link_nid_postfix(directoryLink.c_str(), throughLink.c_str()) == -1 && Error() == 1);
|
|
Require(!std::filesystem::exists(std::filesystem::symlink_status(throughLink)));
|
|
}
|
|
#endif
|
|
std::filesystem::remove_all(root);
|
|
return 0;
|
|
}
|