feat(libkernel): implement fchmod and sceKernelFchmod

This commit is contained in:
Nahuel Genchi 2026-10-05 20:45:01 -03:00
parent 964dd3c919
commit eded1e010f
5 changed files with 63 additions and 10 deletions

View File

@ -14,6 +14,7 @@
#include <cstring>
#include <cstdarg>
#include <filesystem>
#include <optional>
#include <stdexcept>
#include <string>
#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<std::filesystem::path> NativeDescriptorPath(int descriptor) {
if (auto directory = File::DirectoryDescriptorPath(descriptor)) return directory;
const auto handle = reinterpret_cast<HANDLE>(::_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<DWORD>(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<int>(::_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_t>(mode));
}
static int NativeFchmod(int descriptor, int mode) {
return ::fchmod(descriptor, static_cast<mode_t>(mode));
}
static int NativeFtruncate(int descriptor, std::int64_t length) {
return ::ftruncate(descriptor, static_cast<off_t>(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<std::uint16_t>(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);

View File

@ -2,4 +2,5 @@
namespace GuestSockets {
constexpr int FirstDescriptor = 0x10000000;
int Close(int descriptor);
bool IsOpen(int descriptor);
}

View File

@ -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);

View File

@ -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;

View File

@ -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<int>(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<int>(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();