Merge pull request #4688 from eliyarson/fix/kernel-stat-relinked-prx

fix(libkernel): resolve original .prx names to relinked .guest.prx in stat, open and reachability
This commit is contained in:
Dean 2026-10-13 17:48:42 +13:00 committed by GitHub
commit 88f48fddef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 48 additions and 9 deletions

View File

@ -18,6 +18,7 @@ extern "C" int HostStandardError_nid_no_patch();
#endif
extern "C" std::filesystem::path ResolvePath_nid_no_patch(const char* path);
extern "C" std::filesystem::path RelinkedModulePath_nid_no_patch(const std::filesystem::path& path);
extern "C" int SetWorkingDirectory_nid_no_patch(const char* resolved);
extern "C" bool FinalPathComponentIsDot_nid_no_patch(const char* path);
// Guest path aliases: a guest prefix such as "/savedata0" (a save-data mount point) resolves to a host

View File

@ -379,6 +379,13 @@ extern "C" std::filesystem::path ResolvePath_nid_no_patch(const char* path) {
return Resolve(state, path);
}
extern "C" std::filesystem::path RelinkedModulePath_nid_no_patch(const std::filesystem::path& path) {
auto relinked = path;
relinked += ".guest.prx";
std::error_code error;
return std::filesystem::is_regular_file(relinked, error) ? relinked : path;
}
extern "C" int SetWorkingDirectory_nid_no_patch(const char* resolved) {
if (resolved == nullptr) return 14;
auto& state = Directories();

View File

@ -6,6 +6,7 @@
namespace File {
std::filesystem::path PathOrRelinkedModule(const std::filesystem::path& nativePath);
void FillFileStat(const std::filesystem::path& nativePath, FileStat* sb);
void FillFileStat(int nativeDescriptor, FileStat* sb);
bool FillFileStatFromDescriptor(int fd, FileStat* sb);

View File

@ -125,6 +125,12 @@ static void CopyNativeStat(const NativeStat& st, FileStat* sb) {
namespace File {
std::filesystem::path PathOrRelinkedModule(const std::filesystem::path& nativePath) {
std::error_code error;
if (std::filesystem::exists(std::filesystem::symlink_status(nativePath, error))) return nativePath;
return RelinkedModulePath_nid_no_patch(nativePath);
}
void FillFileStat(const std::filesystem::path& nativePath, FileStat* sb) {
NativeStat st{};
if (DoStat(nativePath, &st) != 0) {

View File

@ -210,6 +210,7 @@ int APS5_VABI sceKernelOpen(const char* path, int flags, std::uint16_t mode) {
}
std::filesystem::path native;
if (const int error = ResolvePathError_nid_no_patch(path, native)) return SceKernelError(error);
if ((flags & (SCE_KERNEL_O_ACCMODE | SCE_KERNEL_O_CREAT)) == SCE_KERNEL_O_RDONLY) native = File::PathOrRelinkedModule(native);
return OpenNative(native, flags, mode);
}
@ -291,6 +292,7 @@ int APS5_VABI sceKernelStat(const char* path, FileStat* sb) {
if (*path == '\0') return SceErrorFromErrno(2);
std::filesystem::path native;
if (const int error = ResolvePathError_nid_no_patch(path, native)) return SceKernelError(error);
native = File::PathOrRelinkedModule(native);
std::error_code error;
constexpr int GuestEnotdir = 20;
const auto status = std::filesystem::status(native, error);

View File

@ -795,6 +795,7 @@ int APS5_VABI sceKernelCheckReachability(const char* path) {
if (std::strlen(path) > 255) return SCE_KERNEL_ERROR_ENAMETOOLONG;
std::filesystem::path native;
if (const int error = ResolvePathError_nid_no_patch(path, native)) return SceKernelError(error);
native = File::PathOrRelinkedModule(native);
std::error_code error;
return std::filesystem::exists(native, error) ? 0 : SCE_KERNEL_ERROR_ENOENT;
}

View File

@ -162,13 +162,6 @@ static void InitializeDeferredModule(HMODULE native) {
}
#endif
static std::filesystem::path RelinkedModulePath(const std::filesystem::path& path) {
auto relinked = path;
relinked += ".guest.prx";
std::error_code error;
return std::filesystem::is_regular_file(relinked, error) ? relinked : path;
}
void* APS5_VABI dlopen_nid_postfix(const char* path, int flags) {
if ((flags & ~0x103) || (flags & 3) == 0 || (flags & 3) == 3) {
Error("dlopen: unsupported flags"); return nullptr;
@ -182,7 +175,7 @@ void* APS5_VABI dlopen_nid_postfix(const char* path, int flags) {
module->owned = false;
} else {
if (!*path) { Error("dlopen: empty module path"); return nullptr; }
const auto resolved = RelinkedModulePath(ResolvePath_nid_no_patch(path));
const auto resolved = RelinkedModulePath_nid_no_patch(ResolvePath_nid_no_patch(path));
module->native = LoadLibraryExW(resolved.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
if (module->native) InitializeDeferredModule(static_cast<HMODULE>(module->native));
}
@ -192,7 +185,7 @@ void* APS5_VABI dlopen_nid_postfix(const char* path, int flags) {
Error(message); return nullptr;
}
#else
const auto resolved = path ? RelinkedModulePath(ResolvePath_nid_no_patch(path)).string() : std::string{};
const auto resolved = path ? RelinkedModulePath_nid_no_patch(ResolvePath_nid_no_patch(path)).string() : std::string{};
const int nativeFlags = ((flags & 3) == 1 ? RTLD_LAZY : RTLD_NOW) |
((flags & 0x100) ? RTLD_GLOBAL : RTLD_LOCAL);
module->native = ::dlopen(path ? resolved.c_str() : nullptr, nativeFlags);

View File

@ -174,8 +174,36 @@ static void TestParentTraversal() {
std::filesystem::remove_all(root);
}
static void TestRelinkedModuleNames() {
const auto root = std::filesystem::path("anyps5-relinked-names-" +
std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
Require(std::filesystem::create_directories(root));
const auto original = (root / "Plugin.prx").generic_string();
const auto relinked = original + ".guest.prx";
FileStat status{};
Require(sceKernelStat(original.c_str(), &status) == static_cast<int>(0x80020002u));
Require(sceKernelCheckReachability(original.c_str()) == static_cast<int>(0x80020002u));
Require(sceKernelOpen(original.c_str(), 0, 0) == static_cast<int>(0x80020002u));
{ std::ofstream file(relinked); file << "relinked"; }
Require(sceKernelStat(original.c_str(), &status) == 0 && status.st_size == 8);
Require(stat_nid_postfix(original.c_str(), &status) == 0 && status.st_size == 8);
Require(sceKernelCheckReachability(original.c_str()) == 0);
const int opened = sceKernelOpen(original.c_str(), 0, 0);
Require(opened >= 0 && sceKernelClose(opened) == 0);
Require(sceKernelStat((root / "Other.prx").generic_string().c_str(), &status) == static_cast<int>(0x80020002u));
Require(!std::filesystem::exists(original));
{ std::ofstream file(original); file << "original-bytes"; }
Require(sceKernelStat(original.c_str(), &status) == 0 && status.st_size == 14);
Require(std::filesystem::remove(original));
const int created = sceKernelOpen(original.c_str(), 0x601, 0600);
Require(created >= 0 && sceKernelClose(created) == 0);
Require(std::filesystem::file_size(original) == 0 && std::filesystem::file_size(relinked) == 8);
std::filesystem::remove_all(root);
}
int main() {
TestParentTraversal();
TestRelinkedModuleNames();
Require(chmod_nid_postfix(nullptr, 0600) == -1 && *__error_nid_postfix() == 14);
Require(sceKernelChmod_nid_postfix(nullptr, 0600) == static_cast<int>(0x8002000eu));
Require(chmod_nid_postfix("", 0600) == -1 && *__error_nid_postfix() == 2);