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..a13c14585 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 @@ -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(&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{};