Merge pull request #4038 from santhoshkumar-dev/fix/libkernel-symlink-file-size

fix(libkernel): report the target size of symbolic links on Windows
This commit is contained in:
Dean 2026-10-13 03:08:09 +13:00 committed by GitHub
commit fe66af13ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 41 additions and 6 deletions

View File

@ -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<std::uint64_t>(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<std::uint64_t>(stat.st_size);
return true;
}
int _fail(int guestErrno) {

View File

@ -68,6 +68,7 @@ static void CopyNativeStat(const NativeStat& st, FileStat* sb) {
sb->st_mode = static_cast<std::uint16_t>(st.st_mode);
sb->st_size = static_cast<std::int64_t>(st.st_size);
#ifdef _WIN32
if (st.hasIdentity) sb->st_size = static_cast<std::int64_t>((static_cast<std::uint64_t>(st.identity.nFileSizeHigh) << 32u) | st.identity.nFileSizeLow);
sb->st_dev = static_cast<std::uint32_t>(st.st_dev);
sb->st_ino = static_cast<std::uint32_t>(st.st_ino);
sb->st_nlink = static_cast<std::uint16_t>(st.st_nlink);

View File

@ -9,12 +9,16 @@
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <future>
#include <limits>
#include <stdexcept>
#include <string>
#include <utility>
#ifdef _WIN32
#include <windows.h>
#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();

View File

@ -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));