Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/libs/prx/libkernel/File/src/ArchivedFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ void FillNode(const AppArchiveNode& node, FileStat* sb) {
sb->st_nlink = node.Directory ? 2 : 1;
sb->st_size = static_cast<std::int64_t>(node.Size);
sb->st_blocks = static_cast<std::int64_t>((node.Size + 511) / 512);
if (node.Directory) sb->st_blksize = 0x10000;
}

}
Expand Down
1 change: 1 addition & 0 deletions core/libs/prx/libkernel/File/src/NativeStat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ static void CopyNativeStat(const NativeStat& st, FileStat* sb) {
sb->st_birthtim.tv_nsec = static_cast<std::int64_t>(st.st_birthtim.tv_nsec);
#endif
#endif
if ((sb->st_mode & 0170000) == 0040000) sb->st_blksize = 0x10000;
}

namespace File {
Expand Down
16 changes: 15 additions & 1 deletion core/libs/tests/GuestFilesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
#include "prx/libkernel/File/include/NativeStat.hpp"
#include "prx/libkernel/KernelErrors.hpp"
#include "tests/TemporaryFile.hpp"
#ifndef _WIN32
#include <sys/stat.h>
#endif
#include <algorithm>
#include <array>
#include <chrono>
Expand Down Expand Up @@ -1434,7 +1437,18 @@ int main() {
Require(close_nid_postfix(plainFile) == 0);
Require(close_nid_postfix(directoryDescriptor) == 0);
const int flaggedDirectory = sceKernelOpen(rootName.c_str(), 0x20000, 0);
Require(flaggedDirectory >= 0 && sceKernelClose(flaggedDirectory) == 0);
FileStat directoryStatus{};
Require(flaggedDirectory >= 0 && sceKernelFstat(flaggedDirectory, &directoryStatus) == 0 && directoryStatus.st_blksize == 0x10000);
Require(sceKernelStat(rootName.c_str(), &directoryStatus) == 0 && directoryStatus.st_blksize == 0x10000);
FileStat fileStatus{};
#ifdef _WIN32
Require(sceKernelStat(presentName.c_str(), &fileStatus) == 0 && fileStatus.st_blksize == 512);
#else
struct stat presentHostStat {};
Require(::stat(presentName.c_str(), &presentHostStat) == 0);
Require(sceKernelStat(presentName.c_str(), &fileStatus) == 0 && fileStatus.st_blksize == static_cast<decltype(fileStatus.st_blksize)>(presentHostStat.st_blksize));
#endif
Require(sceKernelClose(flaggedDirectory) == 0);
std::vector<char> entries(4096);
for (const auto& directoryName : {std::string("/"), std::string("."), rootName + "/", rootName + "/.."}) {
const int listed = sceKernelOpen(directoryName.c_str(), 0x20000, 0);
Expand Down
2 changes: 1 addition & 1 deletion core/libs/tests/GuestKernelAppArchive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ void Status() {
FileStat status{};
Check(sceKernelStat("/app0/Data/Small.txt", &status) == 0);
Check((status.st_mode & TypeMask) == RegularType && (status.st_mode & 0222) == 0 && status.st_size == 3 && status.st_blocks == 1);
Check(sceKernelStat("/app0/data", &status) == 0 && (status.st_mode & TypeMask) == DirectoryType);
Check(sceKernelStat("/app0/data", &status) == 0 && (status.st_mode & TypeMask) == DirectoryType && status.st_blksize == 0x10000);
Check(sceKernelStat("/app0/Data/", &status) == 0);
Check(sceKernelStat("/app0/Data/Small.txt/", &status) == SCE_KERNEL_ERROR_ENOTDIR);
Check(sceKernelStat("/app0/Data/Missing.txt", &status) == SCE_KERNEL_ERROR_ENOENT);
Expand Down
1 change: 1 addition & 0 deletions docs/dev/debt/libkernel.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@
- [Windows file timestamps](../../../core/libs/prx/libkernel/File/include/WindowsFileTime.hpp) use the host filesystem's timestamp range and resolution. `utimes`, `futimes`, and `lutimes` reject timestamps outside nonnegative signed 64-bit Windows file-time ticks with `EINVAL`, including exactly 1601-01-01 00:00:00 UTC, whose zero value means "leave unchanged" to `SetFileTime`. Directory timestamp updates resolve the current path of the opened handle. Filesystems with coarser timestamp resolution cannot retain every guest microsecond. Birth-time clamping is not atomic with concurrent host timestamp updates and has not been measured on a console
- [EVFILT_VNODE on macOS](../../../core/libs/prx/libkernel/Equeue/src/FileEvent.cpp) (libkernel) registers `EVFILT_VNODE` on a duplicate of the descriptor in a host kqueue, whose notes have FreeBSD's values, and drops the `NOTE_LINK` that XNU adds to `NOTE_DELETE` when the watched file or directory is removed, since FreeBSD's `vop_remove_post` and `vop_rmdir_post` report `NOTE_DELETE` alone. As on Windows, a directory registered with `NOTE_EXTEND` is accepted but never receives it: XNU reports a move between directories as `NOTE_WRITE` alone, with `NOTE_LINK` for a subdirectory
- [Windows file identity](../../../core/libs/prx/libkernel/File/src/NativeStat.cpp) (libkernel) - `stat` and `fstat` report the volume serial number as `st_dev` and the 64-bit file index folded into the guest's 32-bit `st_ino` (never 0), so two different files can in principle share an `st_ino`; the fold is not taken from a reference. `st_nlink` is clamped to 65535, the width of the guest field. The call keeps the `_wstat64` values when `GetFileInformationByHandle` fails
- [Directory st_blksize](../../../core/libs/prx/libkernel/File/src/NativeStat.cpp) (libkernel) - `stat` and `fstat` report `st_blksize` 0x10000 for every directory, app0.zar directories included, as a PS4 Pro (firmware 10.71) reports for `/app0`, `/app0/sce_sys` and the system library directory. Titles size one `getdents` buffer from it: CUSA18659 reads `st_blksize` + 264 bytes once. The console reports 0x8000 for `/data` directories, which are not told apart here, and files keep the host's value (512 on Windows) where `/app0` files report 0x10000
- [lstat of a Windows symbolic link](../../../core/libs/prx/libkernel/File/src/NativeStat.cpp) (libkernel) reports a link whose target is relative, as `symlink` creates them, with the link's own times, `st_dev` and `st_ino`, mode `S_IFLNK | 0777` and the length of the target `readlink` returns as `st_size`. FreeBSD gives a new link `0777` less the creating process's umask, which the host does not keep, so the permission bits are not the console's. A link with an absolute target is reported the same way, using its print name, or its substitute name when the print name is empty, without a `\??\` or `\\?\` prefix. A directory junction is treated as a link only when `ReadJunction` in [WindowsSymbolicLink.hpp](../../../core/libs/prx/libkernel/File/include/WindowsSymbolicLink.hpp) accepts a drive-path target (`X:\...`) after stripping `\??\` / `\\?\`; volume mounts and other non-drive substitutes are not. Consumers of that drive-target path: [NativeStat.cpp](../../../core/libs/prx/libkernel/File/src/NativeStat.cpp) (`lstat`/`readlink`), [Open.cpp](../../../core/libs/prx/libkernel/File/src/Open.cpp) (`O_NOFOLLOW` → `EMLINK`, and `unlink` removing the junction), [Stdio.cpp](../../../core/libs/prx/libkernel/File/src/Stdio.cpp) (`rmdir` → `ENOTDIR`), and [Export.cpp](../../../core/libs/prx/libkernel_sys/Export.cpp) (`unlinkat` with and without `AT_REMOVEDIR`). `unlink` / plain `unlinkat` leave the target, including when it is not empty
- [lstat of a Windows reparse point that is not a link](../../../core/libs/prx/libkernel/File/src/NativeStat.cpp) (libkernel) reports a mounted volume (`\??\Volume{GUID}\`) or another name surrogate as the reparse point itself, not followed, with the mode of its attributes, as CPython's `os.lstat` does, and a reparse point without the name surrogate bit (deduplication, cloud files) as `stat` reports it
- [sceKernelMapDirectMemory with execute](../../../core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp) (libkernel) - a protection with CPU execute (bit 0x4) returns `EINVAL` (0x80020016) and maps nothing, and `sceKernelMprotect` grants execute afterwards, as [PS5_PayloadSDK measured](https://github.com/mihawk-99/PS5_PayloadSDK/blob/3e31e0b1b68ada28beca68adc480cf0c3d17b9df/platform/docs/PROBE.md#L28-L34) in a title on firmware 12.09 for read-execute and read-write-execute. `sceKernelMapDirectMemory2`, `sceKernelMapNamedDirectMemory`, and `sceKernelBatchMap` and `sceKernelBatchMap2` with operation 0 share the check here, which is not measured for them, nor for a title with more privileges than a homebrew one. Of 40 `sceKernelMapDirectMemory` calls in ten titles checked, 35 pass a constant protection without execute and five read it from a register or field (one each in PPSA01494, PPSA21564 and PPSA30839, two in PPSA18194).
Expand Down
Loading