fseeko now turns SEEK_CUR into ftello plus an absolute host seek, so two
threads making relative seeks on the same stream could both read the
same position and lose one of the moves. Hold the stream lock from the
target check to the end of the host seek, as the single host call did
before, and add a two-thread relative-seek test.
Co-authored-by: Alex Whitehouse <[email protected]>
Check fseeko targets on every host before the host C library sees
them, as FreeBSD 11.4's _fseeko does: a negative SEEK_SET, SEEK_CUR or
regular-file SEEK_END target fails with EINVAL and one past OFF_MAX
with EOVERFLOW, leaving the buffer, pushback and EOF state alone. This
replaces the Windows-only check for targets before the start of the
file: UCRT also dropped the read buffer for an overflowing SEEK_CUR
target, and glibc dropped pending ungetc data and returned EINVAL for
an overflowing target.
SEEK_END on a stream in write mode or on a nonregular file still goes
to the host library instead of being flushed to find its end.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
_filelengthi64 reads the size on disk and misses bytes still in the stdio write buffer, so a valid negative SEEK_END seek after an unflushed fwrite was rejected with EINVAL. Flush first, and cover it with a test that seeks from the end right after a buffered write.
Co-authored-by: Opensource55 <[email protected]>
The UCRT drops a stream's read buffer before its `lseek` to a negative target fails with
`EINVAL`, so a rejected `fseeko` or `fsetpos` left the stream at the descriptor's read-ahead
offset: after 4 of 8 buffered bytes, a rejected `fsetpos` to -1 reported the position as 8 and
the next read returned EOF. FreeBSD 11.4's `_fseeko` keeps the buffer and the position when
`_sseek` fails.
`fseeko` now rejects a target before the start of the file before the host stream is touched,
computing the base from `_ftelli64` for `SEEK_CUR` and `_filelengthi64` for `SEEK_END`, and
answers `EINVAL` with the stream unchanged. A target that is not negative still goes to
`_fseeki64`, and a host seek that fails for another reason behaves as before.
`file_position` gains the three rejected targets: `fsetpos` to -1 after 4 buffered bytes,
`fseeko` with `SEEK_CUR` past the start, and `fseeko` with `SEEK_END` past the start; each
asserts `EINVAL`, an unchanged `fgetpos` and the next byte read.
On Windows, std::tmpfile() returns NULL with errno 22 when %TEMP%
contains a character outside the ANSI code page, where GetTempPathA
returns '?' in its place. Fifteen tests that capture output through it
aborted. TemporaryFile::Open() opens a unique delete-on-close file under
temp_directory_path() with _wfopen; other hosts keep std::tmpfile().
Part of #13083
Co-Authored-By: Claude Opus 5.5 <[email protected]>
FilePosition checked fgetpos and fsetpos only on a freshly written
stream. It now reads three bytes and requires fgetpos to give 3, reads
to end of file and requires fsetpos back to 3 to clear the end-of-file
indicator (C11 7.21.9.3) so the next fgetc returns the fourth byte, and
requires fsetpos to -1 to fail with EINVAL as FreeBSD's fseeko does.
A rejected seek on Windows still moves the stream, because the UCRT
drops its read buffer before the failing lseek: recorded in libc's
technical debt instead of asserted.
Co-Authored-By: Claude Opus 5.5 <[email protected]>