Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 30 additions & 4 deletions core/libs/prx/libc/src/Filesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,42 @@
#include <unistd.h>
#endif

#ifdef _WIN32
static std::filesystem::path FollowedPath(const std::filesystem::path& path) {
const DWORD attributes = GetFileAttributesW(path.c_str());
if (attributes == INVALID_FILE_ATTRIBUTES || (attributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) return path;
const HANDLE handle = CreateFileW(path.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
if (handle == INVALID_HANDLE_VALUE) return {};
std::wstring target(MAX_PATH, L'\0');
DWORD length = GetFinalPathNameByHandleW(handle, target.data(), static_cast<DWORD>(target.size()), FILE_NAME_NORMALIZED);
if (length >= target.size()) {
target.resize(length);
length = GetFinalPathNameByHandleW(handle, target.data(), length, FILE_NAME_NORMALIZED);
}
const DWORD error = GetLastError();
CloseHandle(handle);
if (length == 0 || length >= target.size()) {
SetLastError(length == 0 ? error : ERROR_FILENAME_EXCED_RANGE);
return {};
}
target.resize(length);
return target;
}
#endif

extern "C" int APS5_VABI access_nid_postfix(const char* path, int mode) {
if (mode < 0 || (mode & ~7)) { errno = 22; return -1; }
if (!path) { errno = 14; 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());
const auto target = FollowedPath(resolved);
const DWORD attributes = target.empty() ? INVALID_FILE_ATTRIBUTES : GetFileAttributesW(target.c_str());
if (attributes == INVALID_FILE_ATTRIBUTES) {
errno = FilesystemError(std::error_code(static_cast<int>(GetLastError()), std::system_category()));
const DWORD failure = GetLastError();
errno = failure == ERROR_CANT_RESOLVE_FILENAME ? 62 : FilesystemError(std::error_code(static_cast<int>(failure), std::system_category()));
if (errno == 2) errno = NotFoundError_nid_no_patch(resolved);
return -1;
}
Expand All @@ -30,10 +56,10 @@ extern "C" int APS5_VABI access_nid_postfix(const char* path, int mode) {
}
DWORD size = 0;
constexpr auto information = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
GetFileSecurityW(resolved.c_str(), information, nullptr, 0, &size);
GetFileSecurityW(target.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)) {
if (!GetFileSecurityW(target.c_str(), information, descriptor.data(), size, &size)) {
errno = 13; return -1;
}
struct Token {
Expand Down
41 changes: 41 additions & 0 deletions core/libs/tests/GuestAccess.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "prx/libc/include/General.hpp"
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#ifdef _WIN32
Expand Down Expand Up @@ -76,6 +77,46 @@ int main() {
Require(access_nid_postfix("sample.txt", 1) == -1 && *__error_nid_postfix() == 13);
std::filesystem::permissions(file, std::filesystem::perms::owner_exec, std::filesystem::perm_options::add);
Require(access_nid_postfix("sample.txt", 1) == 0);
#endif
const auto dangling = directory / "dangling.link";
const auto pointing = directory / "sample.link";
const auto loop = directory / "loop.link";
#ifdef _WIN32
const DWORD linkFlags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
const bool linked = CreateSymbolicLinkW(dangling.c_str(), L"missing.bin", linkFlags) &&
CreateSymbolicLinkW(pointing.c_str(), L"sample.txt", linkFlags) && CreateSymbolicLinkW(loop.c_str(), L"loop.link", linkFlags);
#else
std::error_code linkError;
std::filesystem::create_symlink("missing.bin", dangling, linkError);
if (!linkError) std::filesystem::create_symlink("sample.txt", pointing, linkError);
if (!linkError) std::filesystem::create_symlink("loop.link", loop, linkError);
const bool linked = !linkError;
#endif
if (linked) {
Require(access_nid_postfix("dangling.link", 0) == -1 && *__error_nid_postfix() == 2);
Require(access_nid_postfix("dangling.link", 4) == -1 && *__error_nid_postfix() == 2);
Require(access_nid_postfix("loop.link", 0) == -1 && *__error_nid_postfix() == 62);
Require(access_nid_postfix("sample.link", 6) == 0);
#ifdef _WIN32
Require(SetFileAttributesW(file.c_str(), FILE_ATTRIBUTE_READONLY));
Require(access_nid_postfix("sample.link", 4) == 0);
Require(access_nid_postfix("sample.link", 2) == -1 && *__error_nid_postfix() == 13);
Require(SetFileAttributesW(file.c_str(), FILE_ATTRIBUTE_NORMAL));
Require(SetFileAttributesW(pointing.c_str(), FILE_ATTRIBUTE_READONLY));
Require(access_nid_postfix("sample.link", 2) == 0);
Require(SetFileAttributesW(pointing.c_str(), FILE_ATTRIBUTE_NORMAL));
#endif
} else {
std::fprintf(stderr, "symbolic links unavailable: the access link cases not tested\n");
}
#ifdef _WIN32
DeleteFileW(dangling.c_str());
DeleteFileW(pointing.c_str());
DeleteFileW(loop.c_str());
#else
std::filesystem::remove(dangling, linkError);
std::filesystem::remove(pointing, linkError);
std::filesystem::remove(loop, linkError);
#endif
Require(chdir_nid_postfix("/") == 0);
std::filesystem::remove(file);
Expand Down
Loading