From 903f50a3b4745353711b005cf9335cd42a78628e Mon Sep 17 00:00:00 2001 From: Hannah Lyons <7751607+babyhuey@users.noreply.github.com> Date: Thu, 8 Oct 2026 11:11:17 -0400 Subject: [PATCH] feat(libkernel): implement lstat --- .../prx/libkernel/File/include/NativeStat.hpp | 1 + .../prx/libkernel/File/src/NativeStat.cpp | 16 ++++++++++++++ core/libs/prx/libkernel/File/src/Stdio.cpp | 7 ++++++ core/libs/tests/GuestFilesystem.cpp | 22 +++++++++++++++++++ 4 files changed, 46 insertions(+) diff --git a/core/libs/prx/libkernel/File/include/NativeStat.hpp b/core/libs/prx/libkernel/File/include/NativeStat.hpp index f5b8642f53d4..5715f0211980 100644 --- a/core/libs/prx/libkernel/File/include/NativeStat.hpp +++ b/core/libs/prx/libkernel/File/include/NativeStat.hpp @@ -9,6 +9,7 @@ namespace File { void FillFileStat(const std::filesystem::path& nativePath, FileStat* sb); void FillFileStat(int nativeDescriptor, FileStat* sb); bool FillFileStatFromDescriptor(int fd, FileStat* sb); +bool FillLinkStat(const std::filesystem::path& nativePath, FileStat* sb); } diff --git a/core/libs/prx/libkernel/File/src/NativeStat.cpp b/core/libs/prx/libkernel/File/src/NativeStat.cpp index cc1c0af371ec..048443cce87e 100644 --- a/core/libs/prx/libkernel/File/src/NativeStat.cpp +++ b/core/libs/prx/libkernel/File/src/NativeStat.cpp @@ -1,5 +1,6 @@ #include "prx/libkernel/File/include/NativeStat.hpp" #include "prx/libkernel/File/include/DirectoryDescriptor.hpp" +#include "prx/libc/include/General.hpp" #include #include @@ -15,6 +16,11 @@ static int DoFstat(int fd, NativeStat* st) { if (const auto directory = File::DirectoryDescriptorPath(fd)) return DoStat(*directory, st); return _fstat64(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"); + return DoStat(p, st); +} #else #include using NativeStat = struct stat; @@ -24,6 +30,9 @@ static int DoStat(const std::filesystem::path& p, NativeStat* st) { static int DoFstat(int fd, NativeStat* st) { return ::fstat(fd, st); } +static int DoLstat(const std::filesystem::path& p, NativeStat* st) { + return ::lstat(p.c_str(), st); +} #endif static void CopyNativeStat(const NativeStat& st, FileStat* sb) { @@ -99,4 +108,11 @@ bool FillFileStatFromDescriptor(int fd, FileStat* sb) { return true; } +bool FillLinkStat(const std::filesystem::path& nativePath, FileStat* sb) { + NativeStat st{}; + if (DoLstat(nativePath, &st) != 0) return false; + CopyNativeStat(st, sb); + return true; +} + } diff --git a/core/libs/prx/libkernel/File/src/Stdio.cpp b/core/libs/prx/libkernel/File/src/Stdio.cpp index 2516713c6359..f42f622500b5 100644 --- a/core/libs/prx/libkernel/File/src/Stdio.cpp +++ b/core/libs/prx/libkernel/File/src/Stdio.cpp @@ -400,6 +400,13 @@ int APS5_VABI stat_nid_postfix(const char* path, FileStat* sb) { return PosixResult(sceKernelStat(path, sb)); } +int APS5_VABI lstat_nid_postfix(const char* path, FileStat* sb) { + if (sb == nullptr) return PosixFailure(GUEST_EFAULT); + if (const int error = PathError(path)) return PosixFailure(error); + if (!File::FillLinkStat(ResolvePath_nid_no_patch(path), sb)) return PosixResult(SceErrorFromErrno(errno)); + return 0; +} + int APS5_VABI unlink_nid_postfix(const char* path) { if (const int error = PathError(path)) return PosixFailure(error); return PosixResult(sceKernelUnlink(path)); diff --git a/core/libs/tests/GuestFilesystem.cpp b/core/libs/tests/GuestFilesystem.cpp index bd6a113c66f1..c73661bfc528 100644 --- a/core/libs/tests/GuestFilesystem.cpp +++ b/core/libs/tests/GuestFilesystem.cpp @@ -28,6 +28,7 @@ int APS5_VABI open_nid_postfix(const char*, int, int); int APS5_VABI _open_nid_postfix(const char*, int, ...); int APS5_VABI close_nid_postfix(int); int APS5_VABI stat_nid_postfix(const char*, FileStat*); +int APS5_VABI lstat_nid_postfix(const char*, FileStat*); int APS5_VABI unlink_nid_postfix(const char*); int APS5_VABI rmdir_nid_postfix(const char*); int APS5_VABI mkdir_nid_postfix(const char*, unsigned short); @@ -175,6 +176,27 @@ int main() { Require(stat_nid_postfix("", &status) == -1 && *__error_nid_postfix() == 2); Require(stat_nid_postfix(nullptr, &status) == -1 && *__error_nid_postfix() == 14); Require(stat_nid_postfix(presentName.c_str(), nullptr) == -1 && *__error_nid_postfix() == 14); + FileStat linkStatus{}; + Require(lstat_nid_postfix(presentName.c_str(), &linkStatus) == 0 && linkStatus.st_size == 5); + Require((linkStatus.st_mode & 0170000) == 0100000 && linkStatus.st_ino == status.st_ino); + Require(lstat_nid_postfix(rootName.c_str(), &linkStatus) == 0 && (linkStatus.st_mode & 0170000) == 0040000); + Require(lstat_nid_postfix(missingName.c_str(), &linkStatus) == -1 && *__error_nid_postfix() == 2); + Require(lstat_nid_postfix("", &linkStatus) == -1 && *__error_nid_postfix() == 2); + Require(lstat_nid_postfix(nullptr, &linkStatus) == -1 && *__error_nid_postfix() == 14); + Require(lstat_nid_postfix(presentName.c_str(), nullptr) == -1 && *__error_nid_postfix() == 14); +#ifndef _WIN32 + const auto link = root / "link"; + 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); + 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); + 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); +#endif const int opened = open_nid_postfix(presentName.c_str(), 0, 0); Require(opened >= 0 && close_nid_postfix(opened) == 0); const int reopened = _open_nid_postfix(presentName.c_str(), 0);