diff --git a/core/libs/prx/libc/include/FilesystemError.hpp b/core/libs/prx/libc/include/FilesystemError.hpp index bb50c8d70e60..2d2abfa5291f 100644 --- a/core/libs/prx/libc/include/FilesystemError.hpp +++ b/core/libs/prx/libc/include/FilesystemError.hpp @@ -33,6 +33,15 @@ inline bool HasNonDirectoryPrefix_nid_no_patch(const std::filesystem::path& nati return false; } +inline bool NamesNonDirectoryWithSlash_nid_no_patch(const std::filesystem::path& native) { + if (native.has_filename()) return false; + auto named = native; + while (!named.has_filename() && named.has_relative_path()) named = named.parent_path(); + std::error_code error; + const auto status = std::filesystem::status(named, error); + return std::filesystem::exists(status) && !std::filesystem::is_directory(status); +} + inline int NotFoundError_nid_no_patch(const std::filesystem::path& native) { return HasNonDirectoryPrefix_nid_no_patch(native) ? 20 : 2; } diff --git a/core/libs/prx/libc/src/Filesystem.cpp b/core/libs/prx/libc/src/Filesystem.cpp index 510477eb7ae0..1f3723cc0748 100644 --- a/core/libs/prx/libc/src/Filesystem.cpp +++ b/core/libs/prx/libc/src/Filesystem.cpp @@ -85,6 +85,7 @@ extern "C" int APS5_VABI remove_nid_postfix(const char* path) { errno = 22; return -1; } + if (NamesNonDirectoryWithSlash_nid_no_patch(resolved)) { errno = 20; return -1; } std::error_code error; #ifdef _WIN32 const DWORD attributes = GetFileAttributesW(resolved.c_str()); diff --git a/core/libs/prx/libc/src/General.cpp b/core/libs/prx/libc/src/General.cpp index d3c8d0753cd6..058673bf4b63 100644 --- a/core/libs/prx/libc/src/General.cpp +++ b/core/libs/prx/libc/src/General.cpp @@ -178,13 +178,11 @@ std::optional ResolveAlias(const std::string& guestPath, const auto result = std::filesystem::path(host).make_preferred(); return module ? PathOrRelinkedModule(result) : result; } -#ifndef _WIN32 if (guestPath.back() == '/') { auto result = ResolveHostPath(host, SplitNames(std::string_view(relative).substr(prefix.size() + 1))); result /= ""; return module ? PathOrRelinkedModule(result) : result; } -#endif return ResolveHostPath(host, SplitNames(std::string_view(relative).substr(prefix.size() + 1)), module).make_preferred(); } return std::nullopt; diff --git a/core/libs/prx/libc/src/Rename.cpp b/core/libs/prx/libc/src/Rename.cpp index 973c142ff6ef..27d823d94a31 100644 --- a/core/libs/prx/libc/src/Rename.cpp +++ b/core/libs/prx/libc/src/Rename.cpp @@ -115,6 +115,7 @@ extern "C" int RenameGuestPath_nid_no_patch(const char* from, const char* to) { return -1; } const auto destination = ResolvePath_nid_no_patch(to); + if (NamesNonDirectoryWithSlash_nid_no_patch(source) || (std::filesystem::exists(source) && NamesNonDirectoryWithSlash_nid_no_patch(destination))) { errno = 20; return -1; } int error = RenamePath_nid_no_patch(source, destination); if (error == 2) { std::error_code status; diff --git a/core/libs/prx/libkernel/File/src/Open.cpp b/core/libs/prx/libkernel/File/src/Open.cpp index 186ce63ff3b6..44759b28ac4c 100644 --- a/core/libs/prx/libkernel/File/src/Open.cpp +++ b/core/libs/prx/libkernel/File/src/Open.cpp @@ -270,6 +270,7 @@ int APS5_VABI sceKernelOpen(const char* path, int flags, std::uint16_t mode) { APS5_LOG_ERR("open failed path=%s resolveError=%d result=0x%08X", path != nullptr ? path : "", error, static_cast(result)); return result; } + if (NamesNonDirectoryWithSlash_nid_no_patch(native)) return SceErrorFromErrno(ENOTDIR); const int result = OpenNative(native, flags, mode); if (result < 0) { const auto resolved = native.u8string(); @@ -416,6 +417,7 @@ int APS5_VABI sceKernelUnlink(const char* path) { std::filesystem::path native; if (const int error = ResolvePathError_nid_no_patch(path, native)) return SceKernelError(error); constexpr int GuestEperm = 1; + if (NamesNonDirectoryWithSlash_nid_no_patch(native)) return SceErrorFromErrno(20); std::error_code error; if (std::filesystem::is_directory(std::filesystem::symlink_status(native, error))) return SceErrorFromErrno(GuestEperm); if (NativeUnlink(native) != 0) { diff --git a/core/libs/tests/GuestFilesystem.cpp b/core/libs/tests/GuestFilesystem.cpp index 07b86670f3a8..9ddde71df8d5 100644 --- a/core/libs/tests/GuestFilesystem.cpp +++ b/core/libs/tests/GuestFilesystem.cpp @@ -238,19 +238,21 @@ static void TestParentTraversal() { checkInvalid("/parent-test-mount/present.txt/..", 20); FileStat status{}; Require(sceKernelStat("/parent-test-mount/existing/../present.txt", &status) == 0 && status.st_size == 9); -#ifndef _WIN32 for (const char* name : {"slash-unlink", "slash-remove", "slash-rename", "slash-open", "slash-kernel"}) std::ofstream(root / name) << "kept"; Require(unlink_nid_postfix("/parent-test-mount/slash-unlink/") == -1 && *__error_nid_postfix() == 20); Require(remove_nid_postfix("/parent-test-mount/slash-remove/") == -1 && *__error_nid_postfix() == 20); Require(rename_nid_postfix("/parent-test-mount/slash-rename/", "/parent-test-mount/slash-renamed") == -1 && *__error_nid_postfix() == 20); Require(open_nid_postfix("/parent-test-mount/slash-open/", 0, 0) == -1 && *__error_nid_postfix() == 20); Require(sceKernelUnlink("/parent-test-mount/slash-kernel/") == static_cast(0x80020014u)); + Require(unlink_nid_postfix("/parent-test-mount/slash-unlink/child/") == -1 && *__error_nid_postfix() == 20); + Require(remove_nid_postfix("/parent-test-mount/slash-remove/child/") == -1 && *__error_nid_postfix() == 20); + Require(open_nid_postfix("/parent-test-mount/slash-open/child/", 0, 0) == -1 && *__error_nid_postfix() == 20); + Require(sceKernelUnlink("/parent-test-mount/slash-kernel/child/") == static_cast(0x80020014u)); for (const char* name : {"slash-unlink", "slash-remove", "slash-rename", "slash-open", "slash-kernel"}) Require(std::filesystem::is_regular_file(root / name)); Require(!std::filesystem::exists(root / "slash-renamed")); Require(sceKernelStat("/parent-test-mount/Existing/", &status) == 0 && (status.st_mode & 0170000) == 0040000); Require(mkdir_nid_postfix("/parent-test-mount/slash-directory/", 0755) == 0 && std::filesystem::is_directory(root / "slash-directory")); Require(rmdir_nid_postfix("/parent-test-mount/slash-directory/") == 0 && !std::filesystem::exists(root / "slash-directory")); -#endif RemovePathAlias_nid_no_patch("parent-test-mount"); std::error_code linkError; std::filesystem::create_directory_symlink("Existing", root / "directory-link", linkError);