From 7322a3b9f8e651d5add1e149f2a10cec1fd8301e Mon Sep 17 00:00:00 2001 From: Gruzzberry Date: Sun, 25 Oct 2026 23:50:43 +0000 Subject: [PATCH] fix(libkernel_sys): fchmodat follows a symbolic link like chmod Co-Authored-By: Claude Sonnet 5.5 --- .../File/include/DirectoryDescriptor.hpp | 1 + core/libs/prx/libkernel/File/src/Stdio.cpp | 4 ++++ core/libs/prx/libkernel_sys/Export.cpp | 6 +---- core/libs/tests/GuestKernelSys.cpp | 23 +++++++++++++++++++ 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libkernel/File/include/DirectoryDescriptor.hpp b/core/libs/prx/libkernel/File/include/DirectoryDescriptor.hpp index 6978a7998a41..0a7a74fa9b8d 100644 --- a/core/libs/prx/libkernel/File/include/DirectoryDescriptor.hpp +++ b/core/libs/prx/libkernel/File/include/DirectoryDescriptor.hpp @@ -11,6 +11,7 @@ extern "C" const char* GetDirectoryDescriptorPath_nid_no_patch(int fd, int* erro extern "C" std::uint64_t GetDirectoryDescriptorPosition_nid_no_patch(int fd); extern "C" int OpenInDirectory_nid_no_patch(int directory, const char* path, int flags, std::uint16_t mode); extern "C" int StatInDirectory_nid_no_patch(int directory, const char* path, FileStat* sb, bool follow); +extern "C" int ChmodNative_nid_no_patch(const std::filesystem::path& native, int mode); namespace File { diff --git a/core/libs/prx/libkernel/File/src/Stdio.cpp b/core/libs/prx/libkernel/File/src/Stdio.cpp index adce983db2bd..1f02d9a27c29 100644 --- a/core/libs/prx/libkernel/File/src/Stdio.cpp +++ b/core/libs/prx/libkernel/File/src/Stdio.cpp @@ -719,6 +719,10 @@ int APS5_VABI chmod_nid_postfix(const char* path, int mode) { return 0; } +int ChmodNative_nid_no_patch(const std::filesystem::path& native, int mode) { + return NativeChmod(native, mode); +} + int APS5_VABI chflags_nid_postfix(const char* path, std::uint64_t flags) { std::string copy; if (const int error = PathError(path, copy)) return PosixFailure(error); diff --git a/core/libs/prx/libkernel_sys/Export.cpp b/core/libs/prx/libkernel_sys/Export.cpp index 4aa5f49a80eb..8fea6925321a 100644 --- a/core/libs/prx/libkernel_sys/Export.cpp +++ b/core/libs/prx/libkernel_sys/Export.cpp @@ -694,11 +694,7 @@ int APS5_VABI fchmodat_nid_postfix(int fd, const char* path, int mode, int flag) return 0; } } -#ifdef _WIN32 - if (::_wchmod(native.wstring().c_str(), mode & 07777) != 0) return static_cast(Fail(errno == ENOENT && HasNonDirectoryPrefix(native) ? GuestEnotdir : GuestFileError(errno))); -#else - if (::chmod(native.c_str(), static_cast(mode & 07777)) != 0) return static_cast(Fail(errno == ENOENT && HasNonDirectoryPrefix(native) ? GuestEnotdir : GuestFileError(errno))); -#endif + if (ChmodNative_nid_no_patch(native, mode & 07777) != 0) return static_cast(Fail(errno == ENOENT && HasNonDirectoryPrefix(native) ? GuestEnotdir : GuestFileError(errno))); RecordWrittenPath_nid_no_patch(native); return 0; } diff --git a/core/libs/tests/GuestKernelSys.cpp b/core/libs/tests/GuestKernelSys.cpp index ffe0e3140917..c12a3a60f704 100644 --- a/core/libs/tests/GuestKernelSys.cpp +++ b/core/libs/tests/GuestKernelSys.cpp @@ -1099,6 +1099,29 @@ int main() { Require((std::filesystem::status(modeFile).permissions() & std::filesystem::perms::all) == readable); Require(RemoveSymlink(modeLink)); #endif + const auto chmodTarget = (root / "chmod-target.txt").string(); + { std::ofstream file(chmodTarget); file << "x"; } + const auto chmodLink = (root / "chmod-link.txt").string(); + const auto chmodDangling = (root / "chmod-dangling.txt").string(); + const auto chmodLoopA = (root / "chmod-loop-a").string(); + const auto chmodLoopB = (root / "chmod-loop-b").string(); + if (symlink_nid_postfix("chmod-target.txt", chmodLink.c_str()) == 0) { + Require(symlink_nid_postfix("chmod-missing.txt", chmodDangling.c_str()) == 0); + Require(symlink_nid_postfix("chmod-loop-b", chmodLoopA.c_str()) == 0 && symlink_nid_postfix("chmod-loop-a", chmodLoopB.c_str()) == 0); + const auto writable = [&] { + FileStat linked{}; + Require(stat_nid_postfix(chmodTarget.c_str(), &linked) == 0); + return (linked.st_mode & 0222) != 0; + }; + Require(writable()); + Require(fchmodat_nid_postfix(base, "chmod-link.txt", 0400, 0) == 0 && !writable()); + Require(fchmodat_nid_postfix(-100, chmodLink.c_str(), 0600, 0) == 0 && writable()); + Require(fchmodat_nid_postfix(base, "chmod-dangling.txt", 0600, 0) == -1 && Error() == 2); + Require(fchmodat_nid_postfix(-100, chmodDangling.c_str(), 0600, 0) == -1 && Error() == 2); + Require(fchmodat_nid_postfix(base, "chmod-loop-a", 0600, 0) == -1 && Error() == 62); + Require(fchmodat_nid_postfix(base, "chmod-link.txt/child", 0600, 0) == -1 && Error() == 20); + Require(RemoveSymlink(chmodLink) && RemoveSymlink(chmodDangling) && RemoveSymlink(chmodLoopA) && RemoveSymlink(chmodLoopB)); + } Require(fchown_nid_postfix(base, 0, 0) == 0); Require(fchown_nid_postfix(base, 0xFFFFFFFFu, 0xFFFFFFFFu) == 0); Require(fchown_nid_postfix(-1, 0, 0) == -1 && Error() == 9);