diff --git a/core/libs/prx/libkernel/File/src/Stdio.cpp b/core/libs/prx/libkernel/File/src/Stdio.cpp index 4d597c117..fa2d30cfc 100644 --- a/core/libs/prx/libkernel/File/src/Stdio.cpp +++ b/core/libs/prx/libkernel/File/src/Stdio.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #ifdef _WIN32 @@ -32,6 +33,30 @@ static int NativeMkdir(const std::filesystem::path& path, std::uint16_t mode) { static int NativeChmod(const std::filesystem::path& path, int mode) { return ::_wchmod(path.wstring().c_str(), mode); } +static std::optional NativeDescriptorPath(int descriptor) { + if (auto directory = File::DirectoryDescriptorPath(descriptor)) return directory; + const auto handle = reinterpret_cast(::_get_osfhandle(descriptor)); + if (handle == INVALID_HANDLE_VALUE) { + errno = EBADF; + return std::nullopt; + } + std::wstring path(MAX_PATH, L'\0'); + auto length = ::GetFinalPathNameByHandleW(handle, path.data(), static_cast(path.size()), FILE_NAME_NORMALIZED); + if (length >= path.size()) { + path.resize(length); + length = ::GetFinalPathNameByHandleW(handle, path.data(), length, FILE_NAME_NORMALIZED); + } + if (length == 0 || length >= path.size()) { + errno = EINVAL; + return std::nullopt; + } + path.resize(length); + return path; +} +static int NativeFchmod(int descriptor, int mode) { + const auto path = NativeDescriptorPath(descriptor); + return path ? NativeChmod(*path, mode) : -1; +} static int NativeFtruncate(int descriptor, std::int64_t length) { return static_cast(::_chsize_s(descriptor, length)); } @@ -99,6 +124,9 @@ static int NativeMkdir(const std::filesystem::path& path, std::uint16_t mode) { static int NativeChmod(const std::filesystem::path& path, int mode) { return ::chmod(path.c_str(), static_cast(mode)); } +static int NativeFchmod(int descriptor, int mode) { + return ::fchmod(descriptor, static_cast(mode)); +} static int NativeFtruncate(int descriptor, std::int64_t length) { return ::ftruncate(descriptor, static_cast(length)); } @@ -121,6 +149,7 @@ static std::int64_t NativePwrite(int descriptor, const void* buf, std::size_t nb static constexpr int GUEST_ENOENT = 2; static constexpr int GUEST_EIO = 5; +static constexpr int GUEST_EBADF = 9; static constexpr int GUEST_EFAULT = 14; static constexpr int GUEST_EEXIST = 17; static constexpr int GUEST_EINVAL = 22; @@ -469,6 +498,16 @@ int APS5_VABI sceKernelChmod_nid_postfix(const char* path, std::uint16_t mode) { return chmod_nid_postfix(path, mode); } +int APS5_VABI sceKernelFchmod(int d, std::uint16_t mode) { + if (d >= GuestSockets::FirstDescriptor) return SceErrorFromErrno(GuestSockets::IsOpen(d) ? GUEST_EINVAL : GUEST_EBADF); + if (NativeFchmod(d, mode & 07777) != 0) return SceErrorFromErrno(errno); + return 0; +} + +int APS5_VABI fchmod_nid_postfix(int d, int mode) { + return PosixResult(sceKernelFchmod(d, static_cast(mode))); +} + int APS5_VABI sceKernelTruncate_nid_postfix(const char* path, std::int64_t length) { if (path == nullptr) throw std::invalid_argument("sceKernelTruncate: path is null"); if (length < 0) return SceErrorFromErrno(GUEST_EINVAL); diff --git a/core/libs/prx/libkernel/Socket/include/SocketRuntime.hpp b/core/libs/prx/libkernel/Socket/include/SocketRuntime.hpp index ee0899030..0ef89de10 100644 --- a/core/libs/prx/libkernel/Socket/include/SocketRuntime.hpp +++ b/core/libs/prx/libkernel/Socket/include/SocketRuntime.hpp @@ -2,4 +2,5 @@ namespace GuestSockets { constexpr int FirstDescriptor = 0x10000000; int Close(int descriptor); +bool IsOpen(int descriptor); } diff --git a/core/libs/prx/libkernel/Socket/src/Udp.cpp b/core/libs/prx/libkernel/Socket/src/Udp.cpp index bffca9598..ee5931b22 100644 --- a/core/libs/prx/libkernel/Socket/src/Udp.cpp +++ b/core/libs/prx/libkernel/Socket/src/Udp.cpp @@ -158,6 +158,11 @@ int GuestSockets::Close(int descriptor) { return sockets.erase(descriptor) ? 0 : Fail(9); } +bool GuestSockets::IsOpen(int descriptor) { + std::lock_guard lock(socketsMutex); + return sockets.contains(descriptor); +} + extern "C" { int APS5_VABI fcntl_nid_postfix(int descriptor, int command, ...) { const auto socket = Lookup(descriptor); diff --git a/core/libs/prx/libkernel/Unimplemented.cpp b/core/libs/prx/libkernel/Unimplemented.cpp index 6c5ee71eb..fd6b61a57 100644 --- a/core/libs/prx/libkernel/Unimplemented.cpp +++ b/core/libs/prx/libkernel/Unimplemented.cpp @@ -27,16 +27,6 @@ int APS5_VABI pthread_cancel_nid_postfix(void) { NotImplemented_nid_no_patch("0D4-FVvEikw"); return 0; } -int APS5_VABI sceKernelFchmod(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI fchmod_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI futimes_nid_postfix(void) { NotImplemented_nid_no_patch(__func__); return 0; diff --git a/core/libs/tests/GuestFilesystem.cpp b/core/libs/tests/GuestFilesystem.cpp index d99ae96d3..9f17aa3e1 100644 --- a/core/libs/tests/GuestFilesystem.cpp +++ b/core/libs/tests/GuestFilesystem.cpp @@ -9,6 +9,9 @@ extern "C" { int APS5_VABI remove_nid_postfix(const char*); int APS5_VABI rename_nid_postfix(const char*, const char*); int APS5_VABI sceKernelChmod_nid_postfix(const char*, unsigned short); +int APS5_VABI sceKernelFchmod(int, unsigned short); +int APS5_VABI fchmod_nid_postfix(int, int); +int APS5_VABI socket_nid_postfix(int, int, int); int APS5_VABI sceKernelFsync(int); int APS5_VABI sceKernelFtruncate(int, long long); int APS5_VABI sceKernelTruncate_nid_postfix(const char*, long long); @@ -74,9 +77,24 @@ int main() { const int descriptor = ::fileno(native); #endif Require(descriptor >= 0 && sceKernelFsync(descriptor) == 0); + const auto ownerWrite = [&] { + return (std::filesystem::status(sized).permissions() & std::filesystem::perms::owner_write) != std::filesystem::perms::none; + }; + Require(sceKernelFchmod(descriptor, 0400) == 0 && !ownerWrite()); + Require(fchmod_nid_postfix(descriptor, 0600) == 0 && ownerWrite()); Require(sceKernelFtruncate(descriptor, 3) == 0); Require(std::fclose(native) == 0); Require(std::filesystem::file_size(sized) == 3); +#ifndef _WIN32 + Require(sceKernelFchmod(descriptor, 0600) == static_cast(0x80020009u)); + Require(fchmod_nid_postfix(descriptor, 0600) == -1 && *__error_nid_postfix() == 9); +#endif + const int socket = socket_nid_postfix(2, 2, 0); + Require(socket >= 0); + Require(sceKernelFchmod(socket, 0600) == static_cast(0x80020016u)); + Require(fchmod_nid_postfix(socket, 0600) == -1 && *__error_nid_postfix() == 22); + Require(close_nid_postfix(socket) == 0); + Require(fchmod_nid_postfix(socket, 0600) == -1 && *__error_nid_postfix() == 9); Require(remove_nid_postfix(sized.string().c_str()) == 0); const auto present = root / "present.txt"; const auto presentName = present.string();