Merge pull request #2898 from MarcZX100/fix/libkernel-windows-lstat-reparse

fix(libkernel): detect Windows reparse points in lstat
This commit is contained in:
Adrià 2026-10-11 01:13:25 +02:00 committed by GitHub
commit df52ac8354
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 39 additions and 3 deletions

View File

@ -44,8 +44,9 @@ static int DoFstat(int fd, NativeStat* st) {
return ReadHandleInfo(reinterpret_cast<HANDLE>(::_get_osfhandle(fd)), st);
}
static int DoLstat(const std::filesystem::path& p, NativeStat* st) {
std::error_code error;
if (std::filesystem::is_symlink(std::filesystem::symlink_status(p, error))) NotImplemented_nid_no_patch("lstat of a Windows symbolic link");
const auto attributes = GetFileAttributesW(p.c_str());
if (attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0)
NotImplemented_nid_no_patch("lstat of a Windows symbolic link");
return DoStat(p, st);
}
#else

View File

@ -1,13 +1,16 @@
#include "SceTypes.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include <algorithm>
#include <chrono>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <cstdio>
#include <cstring>
#include <stdexcept>
#ifdef _WIN32
#include <io.h>
#include <windows.h>
#else
#include <unistd.h>
#endif
@ -227,7 +230,8 @@ int main() {
Require(remove_nid_postfix(sized.string().c_str()) == 0);
const auto present = root / "present.txt";
const auto presentName = present.string();
const auto missingName = (root / "missing.txt").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{};
@ -263,6 +267,37 @@ int main() {
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);
Require(unlink_nid_postfix(link.string().c_str()) == 0 && unlink_nid_postfix(dangling.string().c_str()) == 0);
#else
const auto targetDirectory = root / "target-directory";
Require(std::filesystem::create_directory(targetDirectory));
const auto fileLink = root / "file-link";
const auto directoryLink = root / "directory-link";
const auto danglingLink = root / "dangling-link";
Require(CreateSymbolicLinkW(fileLink.c_str(), present.c_str(), SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) != 0);
Require(CreateSymbolicLinkW(directoryLink.c_str(), targetDirectory.c_str(),
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(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{};