123 lines
5.2 KiB
C++
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;
|
|
}
|
|
}
|