From 7f2a88262a87d78e34af93d8e662a627090fb804 Mon Sep 17 00:00:00 2001 From: MarcZX100 Date: Sat, 10 Oct 2026 18:07:49 +0000 Subject: [PATCH 1/3] fix(libkernel): detect Windows reparse points in lstat --- .../prx/libkernel/File/src/NativeStat.cpp | 5 ++-- core/libs/tests/GuestFilesystem.cpp | 30 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libkernel/File/src/NativeStat.cpp b/core/libs/prx/libkernel/File/src/NativeStat.cpp index 1c0ada2a6..5454db47d 100644 --- a/core/libs/prx/libkernel/File/src/NativeStat.cpp +++ b/core/libs/prx/libkernel/File/src/NativeStat.cpp @@ -44,8 +44,9 @@ static int DoFstat(int fd, NativeStat* st) { return ReadHandleInfo(reinterpret_cast(::_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 diff --git a/core/libs/tests/GuestFilesystem.cpp b/core/libs/tests/GuestFilesystem.cpp index c42534124..21b489394 100644 --- a/core/libs/tests/GuestFilesystem.cpp +++ b/core/libs/tests/GuestFilesystem.cpp @@ -1,13 +1,16 @@ #include "SceTypes.hpp" #include "prx/libc/include/general/VabiMacros.hpp" +#include #include #include #include #include #include #include +#include #ifdef _WIN32 #include +#include #else #include #endif @@ -263,6 +266,33 @@ 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"; + 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); + 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(&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(std::filesystem::remove(fileLink) && std::filesystem::remove(directoryLink)); #endif FileStat identity{}; FileStat byDescriptor{}; From 314c372285d67d84a2a4c5e2443b78872fb69fb4 Mon Sep 17 00:00:00 2001 From: MarcZX100 Date: Sat, 10 Oct 2026 18:08:27 +0000 Subject: [PATCH 2/3] test(libkernel): cover dangling Windows lstat links --- core/libs/tests/GuestFilesystem.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/libs/tests/GuestFilesystem.cpp b/core/libs/tests/GuestFilesystem.cpp index 21b489394..e53c45718 100644 --- a/core/libs/tests/GuestFilesystem.cpp +++ b/core/libs/tests/GuestFilesystem.cpp @@ -271,9 +271,11 @@ int main() { 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(), missingName.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)); @@ -292,7 +294,8 @@ int main() { }; Require(checkUnsupportedLink(fileLink)); Require(checkUnsupportedLink(directoryLink)); - Require(std::filesystem::remove(fileLink) && std::filesystem::remove(directoryLink)); + Require(checkUnsupportedLink(danglingLink)); + Require(std::filesystem::remove(fileLink) && std::filesystem::remove(directoryLink) && std::filesystem::remove(danglingLink)); #endif FileStat identity{}; FileStat byDescriptor{}; From a22206bfbdd70978a72291262be759d369e4156b Mon Sep 17 00:00:00 2001 From: MarcZX100 Date: Sat, 10 Oct 2026 20:10:19 +0000 Subject: [PATCH 3/3] test(libkernel): fix Windows lstat fixture cleanup --- core/libs/tests/GuestFilesystem.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/core/libs/tests/GuestFilesystem.cpp b/core/libs/tests/GuestFilesystem.cpp index e53c45718..a13c14585 100644 --- a/core/libs/tests/GuestFilesystem.cpp +++ b/core/libs/tests/GuestFilesystem.cpp @@ -230,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{}; @@ -275,7 +276,7 @@ int main() { 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(), missingName.c_str(), 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)); @@ -295,7 +296,8 @@ int main() { Require(checkUnsupportedLink(fileLink)); Require(checkUnsupportedLink(directoryLink)); Require(checkUnsupportedLink(danglingLink)); - Require(std::filesystem::remove(fileLink) && std::filesystem::remove(directoryLink) && std::filesystem::remove(danglingLink)); + Require(std::filesystem::remove(fileLink) && std::filesystem::remove(directoryLink) && + std::filesystem::remove(danglingLink) && std::filesystem::remove(targetDirectory)); #endif FileStat identity{}; FileStat byDescriptor{};