diff --git a/core/libs/prx/libkernel/Apr/src/Apr.cpp b/core/libs/prx/libkernel/Apr/src/Apr.cpp index 6910a43eb..631f5553e 100644 --- a/core/libs/prx/libkernel/Apr/src/Apr.cpp +++ b/core/libs/prx/libkernel/Apr/src/Apr.cpp @@ -77,7 +77,7 @@ bool _fileSize(const std::filesystem::path& path, std::uint64_t& bytes) { const HANDLE find = FindFirstFileExW((path.parent_path() / L"*").c_str(), FindExInfoBasic, &entry, FindExSearchNameMatch, nullptr, FIND_FIRST_EX_LARGE_FETCH); if (find != INVALID_HANDLE_VALUE) { do { - if ((entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) continue; + if ((entry.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)) != 0) continue; const auto size = (static_cast(entry.nFileSizeHigh) << 32u) | entry.nFileSizeLow; sizes.emplace(_foldCase(std::filesystem::path(entry.cFileName).string()), size); } while (FindNextFileW(find, &entry)); @@ -101,8 +101,11 @@ bool _fileSize(const std::filesystem::path& path, std::uint64_t& bytes) { } // Not in the listing (created since, or an unusual name): ask the file system directly. std::error_code error; - bytes = std::filesystem::file_size(path, error); - return !error; + if (!std::filesystem::is_regular_file(path, error)) return false; + FileStat stat{}; + File::FillFileStat(path, &stat); + bytes = static_cast(stat.st_size); + return true; } int _fail(int guestErrno) { diff --git a/core/libs/prx/libkernel/File/src/NativeStat.cpp b/core/libs/prx/libkernel/File/src/NativeStat.cpp index 5454db47d..4495fb59f 100644 --- a/core/libs/prx/libkernel/File/src/NativeStat.cpp +++ b/core/libs/prx/libkernel/File/src/NativeStat.cpp @@ -68,6 +68,7 @@ static void CopyNativeStat(const NativeStat& st, FileStat* sb) { sb->st_mode = static_cast(st.st_mode); sb->st_size = static_cast(st.st_size); #ifdef _WIN32 + if (st.hasIdentity) sb->st_size = static_cast((static_cast(st.identity.nFileSizeHigh) << 32u) | st.identity.nFileSizeLow); sb->st_dev = static_cast(st.st_dev); sb->st_ino = static_cast(st.st_ino); sb->st_nlink = static_cast(st.st_nlink); diff --git a/core/libs/tests/GuestAmpr.cpp b/core/libs/tests/GuestAmpr.cpp index 3b366edd4..37bc4c9df 100644 --- a/core/libs/tests/GuestAmpr.cpp +++ b/core/libs/tests/GuestAmpr.cpp @@ -9,12 +9,16 @@ #include #include #include +#include #include #include #include #include #include #include +#ifdef _WIN32 +#include +#endif extern "C" { int APS5_VABI sceAmprCommandBufferConstructor(Apr::CommandBufferObject*); @@ -671,6 +675,31 @@ void TestMalformedCommandSizes() { Require(rejected); } +void TestResolveSymbolicLink() { + const std::filesystem::path directory = "ampr_resolve_links"; + std::filesystem::remove_all(directory); + Require(std::filesystem::create_directory(directory)); + { std::ofstream file(directory / "target.bin", std::ios::binary); file << "linked data"; } + const auto link = directory / "link.bin"; +#ifdef _WIN32 + if (CreateSymbolicLinkW(link.c_str(), L"target.bin", SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) == 0) { + Require(GetLastError() == ERROR_PRIVILEGE_NOT_HELD); + std::fprintf(stderr, "APR symbolic link check skipped: creating symbolic links needs Developer Mode or administrator rights\n"); + std::filesystem::remove_all(directory); + return; + } +#else + std::filesystem::create_symlink("target.bin", link); +#endif + const std::string linkPath = link.generic_string(); + const char* path = linkPath.c_str(); + std::uint32_t id = 0; + std::uint64_t size = 0; + std::uint32_t failed = 0; + Require(sceKernelAprResolveFilepathsToIdsAndFileSizes(&path, 1, &id, &size, &failed) == 0 && size == 11); + std::filesystem::remove_all(directory); +} + void TestGatherScatter() { const char* path = "ampr_gather_scatter.bin"; { @@ -1230,6 +1259,7 @@ int main() { TestZeroFilledBuffer(); TestResolveFailures(); TestMalformedCommandSizes(); + TestResolveSymbolicLink(); TestGatherScatter(); TestAmmRangesAboveHintedMaps(); TestAmm(); diff --git a/core/libs/tests/GuestFilesystem.cpp b/core/libs/tests/GuestFilesystem.cpp index f759e12d3..f531c7ba4 100644 --- a/core/libs/tests/GuestFilesystem.cpp +++ b/core/libs/tests/GuestFilesystem.cpp @@ -428,7 +428,7 @@ int main() { std::filesystem::create_symlink("present.txt", link); Require(lstat_nid_postfix(link.string().c_str(), &linkStatus) == 0); Require((linkStatus.st_mode & 0170000) == 0120000 && linkStatus.st_size == 11); - Require(stat_nid_postfix(link.string().c_str(), &status) == 0 && (status.st_mode & 0170000) == 0100000); + Require(stat_nid_postfix(link.string().c_str(), &status) == 0 && (status.st_mode & 0170000) == 0100000 && status.st_size == 5); const auto dangling = root / "dangling"; std::filesystem::create_symlink("missing.txt", dangling); Require(lstat_nid_postfix(dangling.string().c_str(), &linkStatus) == 0 && (linkStatus.st_mode & 0170000) == 0120000); @@ -443,7 +443,7 @@ int main() { Require(unlink_nid_postfix(link.string().c_str()) == 0 && unlink_nid_postfix(dangling.string().c_str()) == 0); #else const auto fileLink = root / "file-link"; - if (CreateSymbolicLinkW(fileLink.c_str(), present.c_str(), SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) == 0) { + if (CreateSymbolicLinkW(fileLink.c_str(), L"present.txt", SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) == 0) { Require(GetLastError() == ERROR_PRIVILEGE_NOT_HELD); std::fprintf(stderr, "Windows lstat link checks skipped: creating symbolic links needs Developer Mode or administrator rights\n"); } else { @@ -451,7 +451,7 @@ int main() { Require(std::filesystem::create_directory(targetDirectory)); const auto directoryLink = root / "directory-link"; const auto danglingLink = root / "dangling-link"; - Require(CreateSymbolicLinkW(directoryLink.c_str(), targetDirectory.c_str(), + Require(CreateSymbolicLinkW(directoryLink.c_str(), L"target-directory", 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) { @@ -470,6 +470,7 @@ int main() { const bool statusUnchanged = std::all_of(bytes, bytes + sizeof(untouched), [](unsigned char value) { return value == 0x5a; }); return rejected && errorUnchanged && statusUnchanged; }; + Require(stat_nid_postfix(fileLink.string().c_str(), &status) == 0 && status.st_size == 5); Require(checkUnsupportedLink(fileLink)); Require(checkUnsupportedLink(directoryLink)); Require(checkUnsupportedLink(danglingLink));