diff --git a/core/libs/prx/libkernel_sys/Export.cpp b/core/libs/prx/libkernel_sys/Export.cpp index 048d457629..94c339ffac 100644 --- a/core/libs/prx/libkernel_sys/Export.cpp +++ b/core/libs/prx/libkernel_sys/Export.cpp @@ -436,17 +436,21 @@ static int FollowLinkPath(const std::filesystem::path& source, std::filesystem:: } #endif -static int LinkNative(const std::filesystem::path& source, const std::filesystem::path& destination) { +static int LinkSourceError(const std::filesystem::path& source) { std::error_code error; const auto status = std::filesystem::status(source, error); if (!std::filesystem::exists(status)) { #ifdef _WIN32 std::filesystem::path target; - if (const int followError = FollowLinkPath(source, target)) return static_cast(Fail(followError)); + if (const int followError = FollowLinkPath(source, target)) return followError; #endif - return static_cast(Fail(HasNonDirectoryPrefix(source) ? GuestEnotdir : GuestEnoent)); + return HasNonDirectoryPrefix(source) ? GuestEnotdir : GuestEnoent; } - if (std::filesystem::is_directory(status)) return static_cast(Fail(GuestEperm)); + return std::filesystem::is_directory(status) ? GuestEperm : 0; +} + +static int LinkNative(const std::filesystem::path& source, const std::filesystem::path& destination) { + std::error_code error; 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(HasNonDirectoryPrefix(destination) ? GuestEnotdir : GuestEnoent)); #ifdef _WIN32 @@ -469,6 +473,7 @@ int APS5_VABI link_nid_postfix(const char* existing, const char* created) { std::filesystem::path source, destination; 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)); + if (const int error = LinkSourceError(source)) return static_cast(Fail(error)); return LinkNative(source, destination); } @@ -484,12 +489,15 @@ static int ResolveAtPath(int fd, const char* path, std::filesystem::path& native int APS5_VABI linkat_nid_postfix(int fd1, const char* path1, int fd2, const char* path2, int flag) { if (flag & ~GuestAtSymlinkFollow) return static_cast(Fail(GuestEinval)); - if (path1 == nullptr || path2 == nullptr) return static_cast(Fail(GuestEfault)); - if (*path1 == '\0' || *path2 == '\0') return static_cast(Fail(GuestEnoent)); + if (path1 == nullptr) return static_cast(Fail(GuestEfault)); + if (*path1 == '\0') return static_cast(Fail(GuestEnoent)); std::filesystem::path source, destination; if (const int error = ResolveAtPath(fd1, path1, source)) return static_cast(Fail(error)); - if (const int error = ResolveAtPath(fd2, path2, destination)) return static_cast(Fail(error)); if (flag == 0 && IsSymbolicLink(source)) NotImplemented_nid_no_patch("linkat: a hard link to a symbolic link itself"); + if (const int error = LinkSourceError(source)) return static_cast(Fail(error)); + if (path2 == nullptr) return static_cast(Fail(GuestEfault)); + if (*path2 == '\0') return static_cast(Fail(GuestEnoent)); + if (const int error = ResolveAtPath(fd2, path2, destination)) return static_cast(Fail(error)); return LinkNative(source, destination); } diff --git a/core/libs/tests/GuestKernelSys.cpp b/core/libs/tests/GuestKernelSys.cpp index 4f5aaa08a9..dee944c4fd 100644 --- a/core/libs/tests/GuestKernelSys.cpp +++ b/core/libs/tests/GuestKernelSys.cpp @@ -171,6 +171,12 @@ void OwnershipAndLinkTests(const std::filesystem::path& root) { Require(linkat_nid_postfix(directory, "missing.txt", directory, "absent.txt", 0) == -1 && Error() == 2); Require(linkat_nid_postfix(4321, "owned.txt", directory, "absent.txt", 0) == -1 && Error() == 9); Require(linkat_nid_postfix(directory, "owned.txt", 4321, "absent.txt", 0) == -1 && Error() == 9); + Require(linkat_nid_postfix(directory, "missing.txt", 4321, "absent.txt", 0) == -1 && Error() == 2); + Require(linkat_nid_postfix(directory, "missing.txt", -100, nullptr, 0) == -1 && Error() == 2); + Require(linkat_nid_postfix(-100, root.string().c_str(), 4321, "absent.txt", 0) == -1 && Error() == 1); + Require(linkat_nid_postfix(-100, root.string().c_str(), -100, "", 0) == -1 && Error() == 1); + Require(linkat_nid_postfix(directory, "owned.txt", -100, nullptr, 0) == -1 && Error() == 14); + Require(linkat_nid_postfix(directory, "owned.txt", -100, "", 0) == -1 && Error() == 2); const int plain = sceKernelOpen(file.c_str(), SCE_KERNEL_O_RDONLY, 0); Require(plain >= 0); Require(linkat_nid_postfix(plain, "owned.txt", directory, "absent.txt", 0) == -1 && Error() == 20);