mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 08:33:12 -05:00
feat(libkernel): implement chflags through the guest path resolver
PS5MC (PPSA99420, Kodi 22) imports chflags (ixrw0h2tWuI) from libkernel.prx, one reference. Predicted stop once the imports ahead of it (pthread_getthreadid_np, sendfile, wcstok, pause, setrlimit and the termios calls) are covered (not a run): FAIL: unresolved ELF import ixrw0h2tWuI from libkernel.prx The NID is computed with tools/nid_names.py and matches aerolib. Follows FreeBSD 11.4 (8c639945a44588f6cfdddfb057bb5aad88735da0) sys/kern/vfs_syscalls.c kern_chflagsat and setfflags, and sys/ufs/ufs/ufs_vnops.c ufs_setattr, for a root caller: the lookup through the guest resolver gives EFAULT, ENOENT and ENOTDIR (also for a file reached through a trailing slash or used as a directory), a bit outside the 17 UFS flags of sys/sys/stat.h (VNOVAL included) gives EOPNOTSUPP, and SF_SNAPSHOT gives EPERM, since the snapshot flag cannot be toggled. No host keeps BSD file flags and stat reports st_flags 0, so flags 0 succeed without touching the file and any other flag throws with the flags named. UF_READONLY and the other Windows-attribute flags are not mapped to FILE_ATTRIBUTE_READONLY and its siblings: UFS stores them without enforcing them, and on Windows the read-only attribute is already the owner write bit of chmod. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> (cherry picked from commit 16e5b7ccd04e6467a5a282b6cfc9271304f42244)
This commit is contained in:
1 parent
9ba9ac1c8b
commit
cf419bd172
3 files changed
+69
No files matched your search
@@ -29,6 +29,7 @@
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
struct KernelIovec {
|
||||
@@ -286,6 +287,7 @@ static const struct iovec* NativeIovecs(const KernelIovec* iov) {
|
||||
}
|
||||
#endif
|
||||
|
||||
static constexpr int GUEST_EPERM = 1;
|
||||
static constexpr int GUEST_ENOENT = 2;
|
||||
static constexpr int GUEST_EIO = 5;
|
||||
static constexpr int GUEST_EBADF = 9;
|
||||
@@ -297,6 +299,7 @@ static constexpr int GUEST_ENAMETOOLONG = 63;
|
||||
static constexpr int GUEST_ENOTDIR = 20;
|
||||
static constexpr int GUEST_EISDIR = 21;
|
||||
static constexpr int GUEST_EWOULDBLOCK = 35;
|
||||
static constexpr int GUEST_EOPNOTSUPP = 45;
|
||||
static constexpr int GUEST_ENOTEMPTY = 66;
|
||||
static constexpr int GUEST_EDEADLK = 11;
|
||||
static constexpr int GUEST_ELOOP = 62;
|
||||
@@ -478,6 +481,33 @@ int File::DuplicateFrom(int fd, int start) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static int LookupError(const std::filesystem::path& native) {
|
||||
std::error_code error;
|
||||
const auto status = std::filesystem::status(native, error);
|
||||
if (std::filesystem::exists(status)) return native.has_filename() || std::filesystem::is_directory(status) ? 0 : GUEST_ENOTDIR;
|
||||
if (status.type() != std::filesystem::file_type::not_found) return SceErrorFromErrno(error.default_error_condition().value()) & 0xffff;
|
||||
const bool fileWithSeparator = !native.has_filename() && std::filesystem::exists(WithoutTrailingSeparator(native), error);
|
||||
return fileWithSeparator || HasNonDirectoryPrefix(native) ? GUEST_ENOTDIR : GUEST_ENOENT;
|
||||
}
|
||||
|
||||
static constexpr std::pair<std::uint64_t, const char*> GUEST_FILE_FLAGS[] = {
|
||||
{0x00000001, "UF_NODUMP"}, {0x00000002, "UF_IMMUTABLE"}, {0x00000004, "UF_APPEND"}, {0x00000008, "UF_OPAQUE"},
|
||||
{0x00000010, "UF_NOUNLINK"}, {0x00000080, "UF_SYSTEM"}, {0x00000100, "UF_SPARSE"}, {0x00000200, "UF_OFFLINE"},
|
||||
{0x00000400, "UF_REPARSE"}, {0x00000800, "UF_ARCHIVE"}, {0x00001000, "UF_READONLY"}, {0x00008000, "UF_HIDDEN"},
|
||||
{0x00010000, "SF_ARCHIVED"}, {0x00020000, "SF_IMMUTABLE"}, {0x00040000, "SF_APPEND"}, {0x00100000, "SF_NOUNLINK"},
|
||||
{0x00200000, "SF_SNAPSHOT"}};
|
||||
static constexpr std::uint64_t GUEST_SF_SNAPSHOT = 0x00200000;
|
||||
|
||||
static std::string FileFlagNames(std::uint64_t flags) {
|
||||
std::string names;
|
||||
for (const auto& [flag, name] : GUEST_FILE_FLAGS) {
|
||||
if ((flags & flag) == 0) continue;
|
||||
if (!names.empty()) names += '|';
|
||||
names += name;
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI chmod_nid_postfix(const char* path, int mode) {
|
||||
@@ -488,6 +518,18 @@ int APS5_VABI chmod_nid_postfix(const char* path, int mode) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI chflags_nid_postfix(const char* path, std::uint64_t flags) {
|
||||
if (const int error = PathError(path)) return PosixFailure(error);
|
||||
if (const int error = LookupError(ResolvePath_nid_no_patch(path))) return PosixFailure(error);
|
||||
std::uint64_t known = 0;
|
||||
for (const auto& entry : GUEST_FILE_FLAGS) known |= entry.first;
|
||||
if ((flags & ~known) != 0) return PosixFailure(GUEST_EOPNOTSUPP);
|
||||
if ((flags & GUEST_SF_SNAPSHOT) != 0) return PosixFailure(GUEST_EPERM);
|
||||
if (flags != 0)
|
||||
throw std::runtime_error("chflags: setting " + FileFlagNames(flags) + " on " + path + " is not supported, the host keeps no BSD file flags");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI close_nid_postfix(int d) {
|
||||
int socketError = 0;
|
||||
if (KernelCloseSocket_nid_no_patch(d, &socketError)) {
|
||||
|
||||
@@ -92,6 +92,15 @@ static void Check(bool value, int line) {
|
||||
}
|
||||
#define Require(value) Check((value), __LINE__)
|
||||
|
||||
static bool ChflagsThrows(const std::filesystem::path& path, std::uint64_t flags, const char* names) {
|
||||
try {
|
||||
chflags_nid_postfix(path.string().c_str(), flags);
|
||||
} catch (const std::runtime_error& error) {
|
||||
return std::strstr(error.what(), names) != nullptr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void TestParentTraversal() {
|
||||
const auto root = std::filesystem::path("anyps5-parent-test-" +
|
||||
std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
|
||||
@@ -314,6 +323,20 @@ int main() {
|
||||
Require(mkdir_nid_postfix(nullptr, 0700) == -1 && *__error_nid_postfix() == 14);
|
||||
const auto file = root / "file.txt";
|
||||
{ std::ofstream stream(file); stream << "retained until removal"; }
|
||||
Require(chflags_nid_postfix(nullptr, 0) == -1 && *__error_nid_postfix() == 14);
|
||||
Require(chflags_nid_postfix("", 0) == -1 && *__error_nid_postfix() == 2);
|
||||
Require(chflags_nid_postfix((root / "missing").string().c_str(), 0) == -1 && *__error_nid_postfix() == 2);
|
||||
Require(chflags_nid_postfix((root / "missing").string().c_str(), ~0ULL) == -1 && *__error_nid_postfix() == 2);
|
||||
Require(chflags_nid_postfix((file / "child").string().c_str(), 0) == -1 && *__error_nid_postfix() == 20);
|
||||
Require(chflags_nid_postfix((file.string() + "/").c_str(), 0) == -1 && *__error_nid_postfix() == 20);
|
||||
Require(chflags_nid_postfix(file.string().c_str(), 0) == 0);
|
||||
Require(chflags_nid_postfix(root.string().c_str(), 0) == 0);
|
||||
Require(chflags_nid_postfix(file.string().c_str(), 0x20) == -1 && *__error_nid_postfix() == 45);
|
||||
Require(chflags_nid_postfix(file.string().c_str(), 1ULL << 32) == -1 && *__error_nid_postfix() == 45);
|
||||
Require(chflags_nid_postfix(file.string().c_str(), ~0ULL) == -1 && *__error_nid_postfix() == 45);
|
||||
Require(chflags_nid_postfix(file.string().c_str(), 0x200000) == -1 && *__error_nid_postfix() == 1);
|
||||
Require(ChflagsThrows(file, 0x2, "UF_IMMUTABLE"));
|
||||
Require(ChflagsThrows(file, 0x1000 | 0x40000, "UF_READONLY|SF_APPEND"));
|
||||
const auto held = root / "held.txt";
|
||||
const auto heldName = held.string();
|
||||
{ std::ofstream stream(held); stream << "held"; }
|
||||
|
||||
@@ -158,4 +158,8 @@
|
||||
- [sceKernelGetEventError](../../../core/libs/prx/libkernel/Equeue/src/EventAccessors.cpp) (libkernel) returns the event's `data` as an `int` when `EV_ERROR` (0x4000) is set in `flags` and 0 otherwise, as [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/947627d10d4ef4b68ba3c0189aab6c368e70a8d2/src/kernel/eventQueue.cpp#L663) does and as FreeBSD 11's [kevent](https://github.com/freebsd/freebsd-src/blob/release/11.0.0/sys/kern/kern_event.c#L1005) reports a failed change (`flags = EV_ERROR`, errno in `data`). [fpPS4](https://github.com/red-prig/fpPS4/blob/04cefd43e6fddd1ab033e7980cd356d14c964905/kernel/ps4_queue.pas#L643) instead returns `fflags` plus `0x80020000` when `fflags` is nonzero. Whether the console reads `data` or `fflags` and returns the raw errno or an SCE error code is unverified. No event delivered by the equeue here carries `EV_ERROR`, so the call returns 0 for every event `sceKernelWaitEqueue` reports
|
||||
- [pthread_cond_reltimedwait_np](../../../core/libs/prx/libkernel/Pthread/Posix/Cond.cpp) (libkernel) takes `(cond, mutex, usec)`, a relative timeout in microseconds, and is `scePthreadCondTimedwait` with POSIX error numbers. FreeBSD 11.4 has no such function; the signature comes only from shadPS4, which registers one function for this NID and for `scePthreadCondTimedwait` ([`condvar.cpp`](https://github.com/shadps4-emu/shadPS4/blob/3a9f57265d28975658f1ca941daa5c879c7a74bd/src/core/libraries/kernel/threads/condvar.cpp#L229-L234)). shadPS4 reads a 64-bit count; here it is the 32-bit `SceKernelUseconds` of the SCE twin, as for `sem_reltimedwait_np`. Unverified on the console
|
||||
- [sysctl kern.arandom](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) serves FreeBSD 11's MIB {1, 37} (CTL_KERN, KERN_ARANDOM) and the "kern.arandom" sysctlbyname lookup through std::random_device, returning min(*oldLength, 256) random bytes. Following FreeBSD's sysctl_kern_arnd, a null buffer reports min(*oldLength, 256) without writing, a null length returns 0 without writing, and requests with a new value return EPERM (1) as the node is read-only. Cryptographic quality depends on the host's random device implementation.
|
||||
<<<<<<< HEAD
|
||||
- [sceKernelGetPageTableStats](../../../core/libs/prx/libkernel/DirectMemory/Export.cpp) (libkernel) - reports 2048 page-table entries on each side (a 4 GiB pool of 2 MiB entries, the constants of [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/main/src/kernel/memory.cpp)) and all of them available: no entries are counted as used, so a title never sees page-table pressure. KytyPS5 counts the 2 MiB entries of the committed CPU- or GPU-accessible ranges as used, which this port does not do. The four `int*` arguments follow OpenOrbis' `libkernel.h`, the Orbital type database and SharpProspero; a null pointer returns `SCE_KERNEL_ERROR_EFAULT` as in KytyPS5. None of the values is measured on a console. PPSA30601 (Resonance) calls it during start-up
|
||||
=======
|
||||
- [chflags](../../../core/libs/prx/libkernel/File/src/Stdio.cpp) (libkernel) follows FreeBSD 11.4's [`kern_chflagsat`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/vfs_syscalls.c#L2366-L2383) and UFS's [`ufs_setattr`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/ufs/ufs/ufs_vnops.c#L509-L556) for a root caller: the path lookup reports `EFAULT`, `ENOENT` and `ENOTDIR`, a flag outside the 17 UFS stores (`VNOVAL` included) `EOPNOTSUPP`, and `SF_SNAPSHOT` `EPERM`. Neither host keeps BSD file flags, so every file is taken to have none (`stat` reports `st_flags` 0): flags 0 succeed without touching the file, and any other flag throws, naming the flags. `UF_READONLY`, `UF_HIDDEN`, `UF_SYSTEM` and `UF_ARCHIVE` are FreeBSD's names for the Windows attributes but are not mapped to them: UFS stores them without enforcing them, `FILE_ATTRIBUTE_READONLY` already carries the owner write bit of `chmod` on Windows, and Linux has no equivalent a process can set without `CAP_LINUX_IMMUTABLE`. The console's filesystem types, `EROFS` on read-only mounts and the change-time update are not modelled
|
||||
>>>>>>> 50e6c3e27 (feat(libkernel): implement chflags through the guest path resolver)
|
||||
Reference in new issue
Block a user