On Windows std::filesystem::status reports a symbolic link that resolves
to itself as missing, because the CRT maps ERROR_CANT_RESOLVE_FILENAME to
ENOENT, so sceKernelStat returned 0x80020002 where the console and the
Linux build return SCE_KERNEL_ERROR_ELOOP. When the status says the path
does not exist, open it once following links and return ELOOP when Windows
fails with ERROR_CANT_RESOLVE_FILENAME.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A path whose directory part is a regular file, such as file/child, failed
with ENOENT in chmod, truncate, utimes and rename on Windows, where FreeBSD
11.4 fails it with ENOTDIR (vfs_cache_lookup, sys/kern/vfs_cache.c). The
not-found error of these calls now asks whether the nearest existing
ancestor is not a directory, as open, stat, lstat, access and unlink do.
For rename the source ancestor is checked first and the destination ancestor
only while the source exists. A test line that expected ENOENT for truncate
of a path below a regular file now expects ENOTDIR.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A path whose directory part is a regular file, such as file/child, failed
with ENOENT in open, stat, lstat, access and unlink on Windows, where
FreeBSD 11.4 lookup fails it with ENOTDIR (vfs_cache_lookup, sys/kern/vfs_cache.c).
mkdir and rmdir already did. A not-found error of these calls now asks whether the
nearest existing ancestor is not a directory and reports ENOTDIR then.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
ftruncate, truncate and sceKernelFtruncate extended a file through _chsize_s,
which writes the whole new length as zero bytes. FreeBSD extends the file
without writing data (kern_ftruncate -> ffs_truncate) and fails a length the
file system cannot hold with EFBIG. NativeFtruncate now sets the end of file
with SetFileInformationByHandle and maps ERROR_DISK_FULL to ENOSPC and
ERROR_FILE_TOO_LARGE/ERROR_INVALID_PARAMETER to EFBIG.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
On Windows fsync and fdatasync of a pipe descriptor answered EBADF for the
read end and 0 for the write end. FreeBSD 11.4 kern_fsync reaches the
descriptor through getvnode, which fails a descriptor that is not a vnode
with EINVAL. NativeSync now refuses a pipe handle with EINVAL before it
commits.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
flock on a descriptor that is not open threw a runtime_error from the failed
Windows lock call, ending the title. FreeBSD 11.4 sys_flock fails it with
EBADF (fget) and a descriptor that is not a vnode, such as a pipe, with
EOPNOTSUPP. The guest flock now checks the descriptor first, on Windows
through the OS handle and elsewhere through fstat.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
fdopendir listed the directory from its first entry whatever position the
descriptor had, and left FD_CLOEXEC unchanged. FreeBSD 11.4 fdopendir sets
FD_CLOEXEC on the descriptor and preserves the initial seek position (lib/libc/gen/opendir.c).
The Windows directory handle now skips the entries the descriptor already
consumed, the Linux handle duplicates the descriptor, and the test requires both.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
FreeBSD 11.4 kern_truncate returns EINVAL for a negative length before it
looks up the path, and getutimes rejects an out-of-range tv_usec before
namei in utimes and lutimes. The guest calls checked a NULL or empty path
first, so truncate(NULL, -1) and utimes("", {usec -1}) returned EFAULT or
ENOENT where FreeBSD returns EINVAL.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
rename and sceKernelRename resolved "dir/." to "dir" and renamed the directory. FreeBSD 11.4's lookup fails a last component of "." for DELETE and RENAME with EINVAL, for the source and the destination alike. Check the guest string before it is resolved: a last component of "." (also under trailing slashes) fails with EINVAL when its parent exists, ENOENT when it does not and ENOTDIR when it is not a directory.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
FreeBSD's sys_ioctl rejects a request with no direction bit, an output request with no length, or a void request whose length is not an int before it reads the argument or resolves the descriptor. ioctl threw on such a request for a file, pipe or directory, failed with EBADF for a closed descriptor and with EFAULT for a socket given no argument.
(cherry picked from commit 501518f321ac5d0b45ce1090af39c680927edf49)
(cherry picked from commit 3eb7edd9cf)
FreeBSD 11.4 rejects access mode 3 in kern_openat and
O_CREAT|O_EXCL|O_DIRECTORY in vn_open_cred before namei reads the path
or resolves the directory descriptor. open, _open and openat checked a
NULL or empty path first and returned EFAULT or ENOENT for those flags.
InvalidOpenFlags moves to FileFlags.hpp and runs first in all three.
getrlimit(RLIMIT_NOFILE) reports the host's limits, and libkernel
raised the soft limit to the hard one at load only on Linux. On macOS
the soft limit stayed below an unlimited hard limit (1048576 and
infinity in a shell here, 256 and infinity by default), so the two
differed. XPSemu (PPSA97358) then raises the soft limit to the hard one
in QEMU's os_setup_limits, and setrlimit refuses the change.
macOS fails with EMFILE at kern.maxfilesperproc (10240 here) whatever
the soft limit, even an unlimited one. At load, libkernel now sets both
limits to the smaller of the hard limit and kern.maxfilesperproc, so
getrlimit reports the descriptor count after which opening fails. The
Linux path still raises the soft limit to the hard one.
sceKernelFstat and fstat passed any descriptor to the UCRT's _fstat64, which
raises the invalid-parameter handler for one that is not open. The crash
report installs that handler as fatal, so the calling thread died where the
console returns EBADF. Dying Light 2 (PPSA02262) hits it on a loading thread
right after sign-in.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Windows, pipe_nid_postfix configured the anonymous pipe with a 4096-byte buffer. When guest executables redirect standard error to a pipe via dup2 and host logging writes to standard error, the pipe buffer fills during startup before or between guest reader drains, blocking writer threads in ZwWriteFile and deadlocking the title. Increase the Windows pipe buffer capacity to 65536 bytes to match FreeBSD BIG_PIPE_SIZE used by the PS5 kernel, and cover multi-page single-shot pipe writes in guest posix scalar IO tests.
The C runtime allows 512 stdio streams by default, so after about 509
guest fopen/fdopen streams every further stream failed with EMFILE,
including the std::ifstream APR opens for each read, while getrlimit
reports 8192 open files. Raise the limit to 8192 when libkernel loads,
as the Linux build raises RLIMIT_NOFILE.
Open.cpp passed host errno values 1 to 34 through and turned every
other one into EIO. On Linux, ENAMETOOLONG (36) and ELOOP (40) are above
that range, so sceKernelOpen and sceKernelUnlink returned EIO for a name
longer than 255 bytes or a symbolic link loop, where FreeBSD returns
ENAMETOOLONG (63) and ELOOP (62). sceKernelStat returned ENOENT for
both. sceKernelMkdir already reported them through the mapping in
Stdio.cpp.
Values above 34 now go through FilesystemError, the std::errc mapping
that path resolution already uses. sceKernelStat returns ENAMETOOLONG
and ELOOP when the status query fails with them. The POSIX open, unlink
and stat wrappers set the same errno values.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
sceKernelGetdirentries/sceKernelGetdents wrote up to nbytes into buf
and 8 bytes into *basep with only null checks, unlike sceKernelRead,
pread and readv which all gate on GuestArena::HostWrite. Validate both
buffers the same way so a forged pointer fails with EFAULT instead of
writing to an arbitrary host address.