sceKernelGetdirentries on a descriptor that is not a directory returned
ENOTDIR from the host getdents64 on Linux, and ENOTDIR for every descriptor
missing from the directory table on Windows, including invalid ones.
FreeBSD 11.4's kern_getdirentries returns EINVAL when the vnode is not a
directory.
Return EINVAL for a regular file on both hosts and EBADF for an invalid
descriptor on Windows. Test getdents and getdirentries on a regular file and
list the pipe case, which still fails with ESPIPE on Linux, in the library's
technical debt file.
Written with AI assistance (Claude).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
lstat and readlink aborted on an absolute symbolic link because only a relative reparse point was accepted. Read the link text, and still refuse reparse points that are not symbolic links.
A read-only open, stat or reachability check of a missing path listed its
directory twice: once for a case-insensitive match of the name and again for
a case-insensitive match of the relinked <name>.guest.prx (#5434). Both
names are now matched in the same listing. Precedence and the ambiguous-name
errors are unchanged.
stat of a dangling link reaches FillFileStat and throws on the CI toolchain; it is not part of this change.
Co-Authored-By: Claude <noreply@anthropic.com>
lstat of a Windows symbolic link threw, although symlink creates links
with relative targets on Windows and readlink reads them. Report such a
link as S_IFLNK with its own identity and times and the length of its
target as st_size, through the reparse reader readlink already used,
now shared from libkernel. Links with absolute targets, junctions and
other reparse points still throw, as for readlink.
Borderlands 4 (PPSA01494) loads its UI library with
sceKernelLoadStartModule("/app0/prx/libRenoirCore.PS5.prx"), but ships
the module as prx/librenoircore.ps5.prx. On Linux the call fails:
Could not load /app0/prx/libRenoirCore.PS5.prx result 0, handle result -2147352574
(0x80020002, ENOENT). Guest paths are resolved case-insensitively, but
that only finds a component that exists. With the documented layout,
app0/prx holds only the converted librenoircore.ps5.prx.guest.prx,
so the requested name resolves to itself and RelinkedModulePath then
looks for "libRenoirCore.PS5.prx.guest.prx" with its exact spelling.
Windows and macOS hosts ignore the case, so only Linux is affected.
RelinkedModulePath now falls back to the same ASCII case-insensitive
match as ResolveHostPath when the exact <name>.guest.prx is missing,
and throws for an ambiguous match as that does. dlopen,
sceKernelLoadStartModule, stat, open and sceKernelCheckReachability all
go through it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Guest path resolution trimmed the trailing slash of a path under a
mount alias (/savedata0, /download0, /app0 and the others), so the host
saw "file" for "file/": unlink, remove, sceKernelUnlink and rename
removed or moved the file, open opened it, and chmod and access
succeeded on it. The same path outside an alias already failed with
ENOTDIR, because the host got the slash.
FreeBSD 11.4 rejects it in lookup() (sys/kern/vfs_lookup.c):
/*
* If we're processing a path with a trailing slash,
* check that the end result is a directory.
*/
if ((cnp->cn_flags & TRAILINGSLASH) && dp->v_type != VDIR) {
error = ENOTDIR;
goto bad2;
}
On Linux and macOS a path under an alias now keeps its trailing slash
on the host path, as paths outside an alias do, so the host fails with
ENOTDIR for a file and directories with a trailing slash still work.
Windows keeps the previous behaviour; TechnicalDebt records it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Crimson Desert (PPSA25286) calls sceKernelStat with a null buffer at
start-up, and main throws "sceKernelStat: sb is null", which ends the
title in its first second.
FreeBSD 11.4 looks the path up first and only then copies the result
out, so a null buffer gets EFAULT for a path that exists and the lookup
error for one that does not (sys/kern/vfs_syscalls.c, sys_stat):
error = kern_statat(td, 0, AT_FDCWD, uap->path, UIO_USERSPACE,
&sb, NULL);
if (error == 0)
error = copyout(&sb, uap->ub, sizeof (sb));
sceKernelStat now does the same: the lookup and its errors are as
before, and a null buffer returns SCE_KERNEL_ERROR_EFAULT where the
status would be copied.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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)
Titles check for a plugin with sceKernelStat before loading it through the engine path. The relinker emits only X.prx.guest.prx, so the check failed and PSNCore started with the raw (0, NULL) arguments. Share the dlopen mapping with sceKernelStat, read-only sceKernelOpen and sceKernelCheckReachability when the original name is absent.
Guest descriptors are host descriptors, so a guest that moves standard
error with dup2 also moves the stream sceKernelDebugOutText writes to. A
title that forwards its standard error to the kernel log through a pipe
then reads its own debug output back and logs it again without end. Before
the guest's first dup2 onto descriptor 2, the host's standard error is kept,
and sceKernelDebugOutText writes there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
_wstat64, the FindFirstFileExW listing and std::filesystem::file_size
report the size of a file symbolic link itself (0). stat therefore gave
0 for linked files, and APR resolved them as empty, so Unity treated a
linked globalgamemanagers as empty and never read it.
stat takes the size from the handle it already opens, which follows the
link. The APR listing skips reparse points, and names missing from it
are sized through stat.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sceKernelFsync returned the host fsync or _commit result, so a failure
returned -1 instead of an SCE error code, and a guest socket descriptor
reached the host call. fsync then set the guest errno from the host
errno rather than from that result.
Handle sockets and return SCE error codes as sceKernelFdatasync does
(EINVAL for an open socket, EBADF for a closed one or an unknown
descriptor), and have fsync take the errno from sceKernelFsync's result
like the other POSIX wrappers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA