diff --git a/core/libs/prx/libkernel_sys/Export.cpp b/core/libs/prx/libkernel_sys/Export.cpp index eb7ff910d..67c482334 100644 --- a/core/libs/prx/libkernel_sys/Export.cpp +++ b/core/libs/prx/libkernel_sys/Export.cpp @@ -363,13 +363,13 @@ int APS5_VABI link_nid_postfix(const char* existing, const char* created) { if (const int error = ResolvePathError_nid_no_patch(existing, source)) return static_cast(Fail(error)); if (const int error = ResolvePathError_nid_no_patch(created, destination)) return static_cast(Fail(error)); std::error_code error; - const auto status = std::filesystem::symlink_status(source, error); + const auto status = std::filesystem::status(source, error); if (!std::filesystem::exists(status)) return static_cast(Fail(GuestEnoent)); - if (std::filesystem::is_symlink(status)) throw std::runtime_error("link: a symbolic link as the existing path is not supported"); if (std::filesystem::is_directory(status)) return static_cast(Fail(GuestEperm)); if (std::filesystem::exists(std::filesystem::symlink_status(destination, error))) return static_cast(Fail(GuestEexist)); if (!std::filesystem::is_directory(destination.parent_path(), error)) return static_cast(Fail(GuestEnoent)); - std::filesystem::create_hard_link(source, destination, error); + const auto target = std::filesystem::is_symlink(source, error) ? std::filesystem::canonical(source, error) : source; + if (!error) std::filesystem::create_hard_link(target, destination, error); if (error == std::errc::cross_device_link) return static_cast(Fail(GuestExdev)); if (error) throw std::system_error(error, "link"); RecordWrittenPath_nid_no_patch(destination); diff --git a/core/libs/tests/GuestKernelSys.cpp b/core/libs/tests/GuestKernelSys.cpp index 5f92999d2..d16ff6789 100644 --- a/core/libs/tests/GuestKernelSys.cpp +++ b/core/libs/tests/GuestKernelSys.cpp @@ -465,6 +465,20 @@ int main() { Require(fchmodat_nid_postfix(base, longName.c_str(), 0600, 0) == -1 && Error() == 63); #endif Require(sceKernelClose(base) == 0); +#ifndef _WIN32 + if (created == 0) { + const auto throughLink = (root / "through-link.bin").string(); + Require(link_nid_postfix(symbolic.c_str(), throughLink.c_str()) == 0); + Require(!std::filesystem::is_symlink(std::filesystem::symlink_status(throughLink))); + Require(std::filesystem::equivalent(throughLink, original) && std::filesystem::hard_link_count(original) == 2); + Require(std::filesystem::remove(throughLink) && std::filesystem::hard_link_count(original) == 1); + Require(link_nid_postfix((root / "dangling").string().c_str(), throughLink.c_str()) == -1 && Error() == 2); + const auto directoryLink = (root / "directory-link").string(); + Require(symlink_nid_postfix(".", directoryLink.c_str()) == 0); + Require(link_nid_postfix(directoryLink.c_str(), throughLink.c_str()) == -1 && Error() == 1); + Require(!std::filesystem::exists(std::filesystem::symlink_status(throughLink))); + } +#endif std::filesystem::remove_all(root); return 0; }