AnyPS5/core/libs/prx/libc/src/Filesystem.cpp

123 lines
5.2 KiB
C++

#include "prx/libc/include/FilesystemError.hpp"
#include "prx/libc/include/General.hpp"
#include <cerrno>
#include <vector>
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#else
#include <unistd.h>
#endif
extern "C" int APS5_VABI access_nid_postfix(const char* path, int mode) {
if (!path) { errno = 14; return -1; }
if (mode < 0 || (mode & ~7)) { errno = 22; return -1; }
if (!*path) { errno = 2; return -1; }
try {
const auto resolved = ResolvePath_nid_no_patch(path);
#ifdef _WIN32
const DWORD attributes = GetFileAttributesW(resolved.c_str());
if (attributes == INVALID_FILE_ATTRIBUTES) {
errno = FilesystemError(std::error_code(static_cast<int>(GetLastError()), std::system_category()));
return -1;
}
if (mode == 0) return 0;
if ((mode & 2) && !(attributes & FILE_ATTRIBUTE_DIRECTORY) && (attributes & FILE_ATTRIBUTE_READONLY)) {
errno = 13; return -1;
}
DWORD size = 0;
constexpr auto information = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
GetFileSecurityW(resolved.c_str(), information, nullptr, 0, &size);
if (!size) { errno = 13; return -1; }
std::vector<unsigned char> descriptor(size);
if (!GetFileSecurityW(resolved.c_str(), information, descriptor.data(), size, &size)) {
errno = 13; return -1;
}
struct Token {
HANDLE value = nullptr;
~Token() { if (value) CloseHandle(value); }
} source, impersonation;
if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_DUPLICATE, TRUE, &source.value)) {
if (GetLastError() != ERROR_NO_TOKEN ||
!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE, &source.value)) {
errno = 13; return -1;
}
}
if (!DuplicateToken(source.value, SecurityImpersonation, &impersonation.value)) { errno = 13; return -1; }
DWORD desired = 0;
if (mode & 4) desired |= FILE_READ_DATA;
if (mode & 2) desired |= FILE_WRITE_DATA;
if (mode & 1) desired |= FILE_EXECUTE;
GENERIC_MAPPING mapping{FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS};
PRIVILEGE_SET privileges{};
DWORD privilegeSize = sizeof(privileges), granted = 0;
BOOL allowed = FALSE;
if (!AccessCheck(descriptor.data(), impersonation.value, desired, &mapping,
&privileges, &privilegeSize, &granted, &allowed) || !allowed) {
errno = 13; return -1;
}
return 0;
#else
if (::access(resolved.c_str(), mode) == 0) return 0;
errno = FilesystemError(std::error_code(errno, std::generic_category()));
return -1;
#endif
} catch (const std::bad_alloc&) { errno = 12; return -1; }
catch (const std::filesystem::filesystem_error& error) {
errno = FilesystemError(error.code()); return -1;
}
}
extern "C" int APS5_VABI remove_nid_postfix(const char* path) {
if (!path) { errno = 14; return -1; }
if (!*path) { errno = 2; return -1; }
try {
const auto resolved = ResolvePath_nid_no_patch(path);
if (FinalPathComponentIsDot_nid_no_patch(path)) {
std::error_code statusError;
const auto status = std::filesystem::status(resolved, statusError);
if (statusError) { errno = FilesystemError(statusError); return -1; }
if (!std::filesystem::exists(status)) { errno = 2; return -1; }
if (!std::filesystem::is_directory(status)) { errno = 20; return -1; }
errno = 22;
return -1;
}
std::error_code error;
#ifdef _WIN32
const DWORD attributes = GetFileAttributesW(resolved.c_str());
bool removed = false;
if (attributes != INVALID_FILE_ATTRIBUTES) {
const DWORD writable = attributes & ~FILE_ATTRIBUTE_READONLY;
const bool unlocked = writable != attributes &&
SetFileAttributesW(resolved.c_str(), writable ? writable : FILE_ATTRIBUTE_NORMAL);
removed = (attributes & FILE_ATTRIBUTE_DIRECTORY) ?
RemoveDirectoryW(resolved.c_str()) != 0 : DeleteFileW(resolved.c_str()) != 0;
if (!removed && unlocked) {
const DWORD nativeError = GetLastError();
SetFileAttributesW(resolved.c_str(), attributes);
SetLastError(nativeError);
}
}
if (!removed) {
const DWORD nativeError = GetLastError();
if (nativeError == ERROR_DIR_NOT_EMPTY) { errno = 66; return -1; }
error = std::error_code(static_cast<int>(nativeError), std::system_category());
}
#else
const bool removed = std::filesystem::remove(resolved, error);
#endif
if (error || !removed) {
errno = error ? FilesystemError(error) : 2;
return -1;
}
RecordWrittenPath_nid_no_patch(resolved);
return 0;
} catch (const std::bad_alloc&) { errno = 12; return -1; }
catch (const std::filesystem::filesystem_error& error) {
errno = FilesystemError(error.code());
return -1;
}
}