Follow-up to #7652. A raise on a thread running host code waited up to the
1 s RetryLimit before falling back, so Windows guest_raise_exception (about
220 host-resident raises) went from ~15 s to 17-30 s. Host code now has its
own HostCodeLimit of 50 ms; RetryLimit still guards NtContinue and winpthreads.
In a payload process on a PS5 (firmware 12.70), sysctlbyname returns -1
with ENOENT for thirteen names that threw here, and leaves the buffer and
the length alone, for a read, a size query, a null length and a null
buffer with a null length. sysctl does the same for the fixed MIBs of
seven of them. sysctlnametomib gives those seven MIBs and returns ENOENT
for hw.cacheline_size and hw.usermem, which threw too.
machdep.cacheline_size already returned ENOENT and is now tested with
the others. A write to the new names and MIBs throws, as the console's
answer to a write was not tried. The hw.realmem and vm.overcommit debt
entries now say that a payload process gets ENOENT for them as well.
AI-assisted: yes
Co-Authored-By: Claude Opus 5.5 <[email protected]>
kevent threw for EVFILT_WRITE on any descriptor that is not a socket. On the write end of a pipe it now follows FreeBSD 11's filt_pipewrite: data is the free buffer space, and once the read end is closed the event reports EV_EOF with data 0. Adding the event after the read end is closed fails with EPIPE, as in pipe_kqfilter. EVFILT_WRITE on the read end still throws, because FreeBSD's pipe ends are bidirectional and host pipes are not.
GuestSockets::Ready only knew the read end of a pipe, and WaitAny asked the host for POLLIN on every descriptor that is not a socket, so a write interest on a pipe could never be reported or wake a wait. PipeWriteState gives the free space of the host buffer (F_GETPIPE_SZ less FIONREAD on Linux, the write quota on Windows), EOF once the read end is closed, and whether the host takes a write now; Ready reports the write end when it is writable or at EOF, and WaitAny asks for POLLOUT on it. macOS has no call for the capacity of a pipe and throws.
sceKernelTruncate's status query fails with too_many_symbolic_link_levels for a link loop and filename_too_long for an over-long name, and the call answered ENOENT for both. It now returns ELOOP or ENAMETOOLONG through PathLookupFailure, and truncate sets the same errno.
#6648 taught open, unlink and stat to report a symbolic link loop and an over-long name, but sceKernelMkdir and sceKernelRmdir still lost them: their std::filesystem queries fail with filename_too_long or too_many_symbolic_link_levels and the code went on to ENOENT, EIO or ENOTDIR. A PathLookupFailure check after those queries now returns the guest error (ELOOP 62, ENAMETOOLONG 63) as FreeBSD's namei does, and the POSIX wrappers set the same errno. guest_path_errors covers the open, unlink, stat, rename, mkdir and rmdir paths with a self-referencing link, a two-link cycle, a 300-byte name and each as a path prefix; it is UNIX-only because it creates symbolic links.
FreeBSD 11.4 kern_renameat looks up the source first (namei on fromnd),
so a missing or dot source fails with ENOENT or EINVAL before the new
name is touched. The guest call rejected a NULL, empty or dot destination
first, so rename(missing, NULL) returned EFAULT and rename(missing, ".")
returned EINVAL where FreeBSD returns ENOENT.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Host logging opens its own FILE on the saved descriptor. The guest FILE stays on the replaced descriptor, so guest printf, puts and fprintf still reach the pipe.
readdir and readdir_r returned d_fileno 0 for every entry on Windows,
because MinGW's _wreaddir reports no inode. FreeBSD 11.4's
_readdir_unlocked (lib/libc/gen/readdir.c) never returns an entry whose
d_ino is 0, and d_fileno is the number lstat reports as st_ino.
The Windows directory stream now reads the listing from the directory
handle with GetFileInformationByHandleEx(FileIdBothDirectoryInfo), so
each entry carries the file ID of the entry itself, symbolic links
included, without opening it. The ID is folded as stat and lstat fold
nFileIndexHigh:nFileIndexLow; the fold moves to WindowsDescriptor.hpp so
both libraries use it. rewinddir restarts the scan, telldir and seekdir
count entries as _wtelldir and _wseekdir did, and opendir fails with the
open error of the directory. A file system that lists no file ID gets a
nonzero number hashed from the name. Linux truncates the host inode to
32 bits as stat does instead of reporting 0 above 2^32.
guest_directory checks that every entry has a nonzero file number, that
the numbers of a file, a subdirectory and symbolic links to both equal
lstat's st_ino, that an entry created or removed while the directory is
open shows up after rewinddir, that a delete-pending entry no lstat can
open is still listed, and that seekdir returns the same number.
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>