Merge pull request #4553 from Nixitov/feat/fchmodat
feat(libkernel_sys): implement fchmodat
This commit is contained in:
commit
d67a9e5a45
|
|
@ -10,6 +10,7 @@
|
|||
#include <string>
|
||||
#include <string_view>
|
||||
#include <system_error>
|
||||
#include <sys/stat.h>
|
||||
#ifdef __linux__
|
||||
#include <cerrno>
|
||||
#include <fcntl.h>
|
||||
|
|
@ -81,6 +82,24 @@ constexpr int PcAclExtended = 59;
|
|||
constexpr int PcAclPathMax = 60;
|
||||
constexpr int PcMacPresent = 63;
|
||||
constexpr int PcAclNfs4 = 64;
|
||||
constexpr int GuestAtSymlinkNofollow = 0x0200;
|
||||
|
||||
int GuestFileError(int error) {
|
||||
switch (error) {
|
||||
case ENOENT: return GuestEnoent;
|
||||
case EACCES: return 13;
|
||||
case ENOTDIR: return 20;
|
||||
case EINVAL: return GuestEinval;
|
||||
case EPERM: return GuestEperm;
|
||||
case EROFS: return 30;
|
||||
case ENAMETOOLONG: return GuestEnametoolong;
|
||||
case EIO: return GuestEio;
|
||||
#ifdef ELOOP
|
||||
case ELOOP: return GuestEloop;
|
||||
#endif
|
||||
default: return GuestEio;
|
||||
}
|
||||
}
|
||||
|
||||
std::int64_t Fail(int error) {
|
||||
*__error_nid_postfix() = error;
|
||||
|
|
@ -487,6 +506,41 @@ int APS5_VABI mkdirat_nid_postfix(int fd, const char* path, std::uint16_t mode)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI fchmodat_nid_postfix(int fd, const char* path, int mode, int flag) {
|
||||
if (flag != 0 && flag != GuestAtSymlinkNofollow) return static_cast<int>(Fail(GuestEinval));
|
||||
if (path == nullptr) return static_cast<int>(Fail(GuestEfault));
|
||||
if (*path == '\0') return static_cast<int>(Fail(GuestEnoent));
|
||||
std::filesystem::path native;
|
||||
if (fd == GuestAtFdcwd || *path == '/') {
|
||||
if (const int error = ResolvePathError_nid_no_patch(path, native)) return static_cast<int>(Fail(error));
|
||||
} else {
|
||||
int error = 0;
|
||||
const char* base = GetDirectoryDescriptorPath_nid_no_patch(fd, &error);
|
||||
if (base == nullptr) return static_cast<int>(Fail(error));
|
||||
native = std::filesystem::path(reinterpret_cast<const char8_t*>(base)) / std::filesystem::path(reinterpret_cast<const char8_t*>(path));
|
||||
}
|
||||
if (flag == GuestAtSymlinkNofollow) {
|
||||
#ifdef _WIN32
|
||||
const DWORD attributes = GetFileAttributesW(native.wstring().c_str());
|
||||
const bool symbolic = attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
|
||||
#else
|
||||
std::error_code status;
|
||||
const bool symbolic = std::filesystem::is_symlink(std::filesystem::symlink_status(native, status));
|
||||
#endif
|
||||
if (symbolic) {
|
||||
NotImplemented_nid_no_patch("fchmodat: AT_SYMLINK_NOFOLLOW on a symbolic link");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if (::_wchmod(native.wstring().c_str(), mode & 07777) != 0) return static_cast<int>(Fail(GuestFileError(errno)));
|
||||
#else
|
||||
if (::chmod(native.c_str(), static_cast<mode_t>(mode & 07777)) != 0) return static_cast<int>(Fail(GuestFileError(errno)));
|
||||
#endif
|
||||
RecordWrittenPath_nid_no_patch(native);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI unlink_nid_postfix(const char* path);
|
||||
int APS5_VABI rmdir_nid_postfix(const char* path);
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ int APS5_VABI unlinkat_nid_postfix(int, const char*, int);
|
|||
std::int64_t APS5_VABI readlinkat_nid_postfix(int, const char*, char*, std::size_t);
|
||||
int APS5_VABI mkdirat_nid_postfix(int, const char*, std::uint16_t);
|
||||
int APS5_VABI renameat_nid_postfix(int, const char*, int, const char*);
|
||||
int APS5_VABI fchmodat_nid_postfix(int, const char*, int, int);
|
||||
std::int64_t APS5_VABI sceKernelRead(int, void*, std::size_t);
|
||||
std::uint16_t APS5_VABI umask_nid_postfix(std::uint16_t);
|
||||
int* APS5_VABI __error_nid_postfix();
|
||||
|
|
@ -447,6 +448,22 @@ int main() {
|
|||
Require(sceKernelClose(subFd) == 0);
|
||||
Require(renameat_nid_postfix(-100, (root / "made.bin").string().c_str(), -100, (root / "final.bin").string().c_str()) == 0);
|
||||
Require(std::filesystem::is_regular_file(root / "final.bin"));
|
||||
Require(fchmodat_nid_postfix(base, "file.txt", 0600, 0x0200) == 0);
|
||||
Require(fchmodat_nid_postfix(-100, (root / "file.txt").string().c_str(), 0644, 0) == 0);
|
||||
#ifndef _WIN32
|
||||
Require((std::filesystem::status(root / "file.txt").permissions() & std::filesystem::perms::all) ==
|
||||
(std::filesystem::perms::owner_read | std::filesystem::perms::owner_write | std::filesystem::perms::group_read | std::filesystem::perms::others_read));
|
||||
#endif
|
||||
Require(fchmodat_nid_postfix(base, "missing.bin", 0600, 0) == -1 && Error() == 2);
|
||||
Require(fchmodat_nid_postfix(base, nullptr, 0600, 0) == -1 && Error() == 14);
|
||||
Require(fchmodat_nid_postfix(base, "", 0600, 0) == -1 && Error() == 2);
|
||||
Require(fchmodat_nid_postfix(base, "file.txt", 0600, 0x4000) == -1 && Error() == 22);
|
||||
Require(fchmodat_nid_postfix(-1, "file.txt", 0600, 0) == -1 && Error() == 9);
|
||||
#ifndef _WIN32
|
||||
Require(symlink_nid_postfix("loop", (root / "loop").string().c_str()) == 0);
|
||||
Require(fchmodat_nid_postfix(base, "loop/x", 0600, 0) == -1 && Error() == 62);
|
||||
Require(fchmodat_nid_postfix(base, longName.c_str(), 0600, 0) == -1 && Error() == 63);
|
||||
#endif
|
||||
Require(sceKernelClose(base) == 0);
|
||||
std::filesystem::remove_all(root);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
- [fchdir](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) changes the guest working directory to the descriptor's directory: on Windows through the path recorded when the descriptor was opened, on Linux through the `/proc/self/fd` link target. It returns `EBADF` for a descriptor that is not open and `ENOTDIR` for one that is not a directory. FreeBSD `change_dir`'s search-permission check (`EACCES`) is not modelled, and a directory renamed after it was opened is still reached through the recorded path. Console behaviour is unverified
|
||||
- [openat](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) passes `AT_FDCWD` (-100) and absolute paths to `open`. Any other relative path is joined to the descriptor's host directory, found the same way as for `fchdir`, so guest path aliases and case-insensitive name matching do not apply to it, and a `..` component is not kept inside the guest root. It returns `EBADF` for a descriptor that is not open and `ENOTDIR` for one that is not a directory. As with `open`, `O_NOFOLLOW` and `O_NONBLOCK` are ignored. PPSA99003 opens directory entries this way. Console behaviour is unverified
|
||||
- [fchmodat](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) passes `AT_FDCWD` (-100) and absolute paths to `chmod`; any other relative path is joined to the descriptor's host directory the same way as for `openat`, so guest path aliases and case-insensitive name matching do not apply to it and a `..` component is not kept inside the guest root. A flag other than `0` or `AT_SYMLINK_NOFOLLOW` (0x200) returns `EINVAL`; the mode is masked to `07777`. With `AT_SYMLINK_NOFOLLOW` a symbolic link throws, since the host cannot change a link's own mode, while a regular file is changed either way. The mode maps to the host file mode, which on Windows is only the CRT's read-only bit. PPSA99621 calls it through `std::filesystem::permissions`. Console behaviour is unverified
|
||||
- [pathconf](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) looks the path up as FreeBSD 11's `kern_pathconf` does (links followed, `ENAMETOOLONG` for a path of 1024 bytes or more or a component over 255) and returns the constants of FreeBSD 11's UFS `ufs_pathconf` for files and directories alike: `_PC_LINK_MAX` 32767, `_PC_NAME_MAX` 255, `_PC_PATH_MAX` and `_PC_SYMLINK_MAX` 1024, `_PC_PIPE_BUF` 512, `_PC_CHOWN_RESTRICTED` and `_PC_NO_TRUNC` 1, `_PC_FILESIZEBITS` 64, `_PC_REC_MAX_XFER_SIZE` -1, `_PC_PRIO_IO` and `_PC_SYNC_IO` 0. Whether the console's filesystems report the same is not verified. It throws for the names whose UFS value depends on the mount (`_PC_ALLOC_SIZE_MIN`, `_PC_REC_INCR_XFER_SIZE`, `_PC_REC_MIN_XFER_SIZE`, `_PC_MIN_HOLE_SIZE`), the kernel options (`_PC_ACL_EXTENDED`, `_PC_ACL_PATH_MAX`, `_PC_ACL_NFS4`, `_PC_MAC_PRESENT`) or the page size (`_PC_REC_XFER_ALIGN`), and on which UFS and `vop_stdpathconf` disagree (`_PC_MAX_CANON`, `_PC_MAX_INPUT`, `_PC_VDISABLE`, `_PC_ASYNC_IO`); other names fail with `EINVAL`
|
||||
- [mkdirat](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) passes `AT_FDCWD` (-100) and absolute paths to `mkdir`. Any other relative path is joined to the descriptor's host directory, found the same way as for `fchdir`, so guest path aliases and case-insensitive name matching do not apply to it, and a `..` component is not kept inside the guest root. It returns `EBADF` for a descriptor that is not open and `ENOTDIR` for one that is not a directory. On Windows, `mode` is ignored. Console behaviour is unverified
|
||||
- [readlink](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) on Windows reads a symbolic link's target from its reparse data and returns it with `/` separators, only when the target is relative, as `symlink` creates them. A symbolic link with an absolute target and a junction throw, as a host path has no guest form. Any other reparse point throws too, where FreeBSD reports `EINVAL` for a file that is not a link
|
||||
|
|
|
|||
Loading…
Reference in New Issue