vfscanf, which vscanf and scanf also go through, read without switching
the stream to reading first. On Windows a vfscanf directly after fwrite
on an r+ stream returned bytes of the write buffer, as fscanf did before
a7e6e597d. It now calls SwitchToReading as fscanf does.
FreeBSD 11 fopen opens the path with _open, and vn_open_vnode refuses a
directory only for FWRITE or O_TRUNC, so fopen of a directory in a read
mode succeeds; Linux hosts already open it. On Windows CreateFileW
without FILE_FLAG_BACKUP_SEMANTICS refused it with EACCES.
fopen now opens such a path as a libkernel directory descriptor, which
libkernel registers in libc when it loads, and fclose and freopen remove
it from libkernel's table. Reads on the stream fail with EISDIR and set
the error indicator, as on a Linux host and FreeBSD's tmpfs: the Windows
CRT fails them already but reports EINVAL. Writes fail with EBADF, and
fseek, ftell and ungetc work, as on any read-only stream.
The console's fread returns EINVAL and 0 for an overflowing size * count
before it reads the stream, so a null or closed stream with an overflowing
request answers EINVAL instead of throwing or EBADF. Move the check ahead
of the open check and the handle lookup, keeping the zero size and zero
count result, and use EINVAL instead of the literal 22.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
On Windows, freopen reopened a stream through _wfreopen, which opens the file without FILE_SHARE_DELETE, so remove or rename of the file failed while the stream was open, unlike fopen streams and POSIX unlink/rename of an open file. freopen now closes the previous stream and opens the new file through the same CreateFileW path fopen uses, so the reopened stream shares delete access. Host standard streams and an empty filename keep the _wfreopen path. Reference: FreeBSD 11 unlink(2) and rename(2) succeed on a file that is open.
The console's libc.prx (read from PPSA21564 by the maintainer) returns from
fread with EINVAL before it touches the stream, so ferror stays clear, and
its fwrite has no overflow check at all. fread no longer sets the stream
error flag, fwrite keeps throwing, and the test follows.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
On Windows the UCRT does not switch an r+, w+ or a+ stream from writing
to reading without fflush or fseek: fread after fwrite returned bytes of
the write buffer and the next flush wrote them over the file, and ungetc
failed on an update stream that was not already reading. On Linux glibc
does not switch either for ungetc, which pushes the byte back over the
pending write, drops the write and can corrupt the heap on the next read,
or for an fread of at least the buffer size, which drops the write and
reads from the old offset.
fread, fgetc, getc_unlocked, fgets, ungetc, ungetwc and fscanf now flush
pending output and mark the stream as reading first, as FreeBSD 11.4's
__srefill and __ungetc do, through _IO_switch_to_get_mode on glibc.
Writes directly after reads still go through the UCRT on Windows; the
gap is recorded in the libc technical debt.
FreeBSD 11.4 fread and fwrite return 0 with errno EINVAL and the error
flag set when size * count does not fit size_t (lib/libc/stdio/fread.c:78-86,
fwrite.c:67-75). The guest threw std::overflow_error, which ends the title.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
fflush threw when the host flush failed, which ends the title on an
ordinary write error such as a full disk. FreeBSD's __sflush sets the
stream's error flag and returns EOF, as C17 7.21.5.2 asks. fflush now
returns the host result and keeps the guest stream's error flag in sync,
as fwrite, fputs and fclose already do since #4380 and #3926.
fopen opened its stream with _wfopen, which shares read and write
access but not delete access. While the guest held a stream open,
rename of its file failed with EIO and remove with EBUSY. POSIX rename
and remove work on open files, as on Linux and FreeBSD, and since
#3677 the descriptors sceKernelOpen returns share delete access.
fopen now creates the handle with CreateFileW sharing read, write and
delete, with the access and creation disposition of the mode, and
wraps it with _open_osfhandle and _wfdopen, keeping append mode. A
failed CreateFileW sets errno through the error mapping #3677 added to
libkernel's NativeOpen, which moves to libc as
WindowsOpenError_nid_no_patch so that both use it. freopen still
reopens through _wfreopen; TechnicalDebt records it.