Skip to content

A write error outside the fatal class still read as the end of the body - #1417

Open
xroche wants to merge 5 commits into
masterfrom
fix-1398-nonfatal-write-eof
Open

A write error outside the fatal class still read as the end of the body#1417
xroche wants to merge 5 commits into
masterfrom
fix-1398-nonfatal-write-eof

Conversation

@xroche

@xroche xroche commented Aug 24, 2026

Copy link
Copy Markdown
Owner

http_xfread1() advances r->size before the fwrite, so a body the failing write happened to complete still satisfied the size test at the bottom and came back as a clean EOF. #1391 stopped that for the fatal class alone; the non-fatal class now has a code of its own, STATUSCODE_IO_ERROR, and the four size-based sites in back_wait() test for either. The same function also discarded fflush()'s return, and glibc's later fclose() reports nothing once that flush has taken the error, so a body with no Content-Length and no chunking (the one shape with no completion test to fall back on) was recorded as mirrored with nothing on disk, the fatal class included. The decode step is the third door onto the same bug: hts_codec_unpack() and hts_zunpack() now leave a local failure's errno behind and clear it for a body the decoder refused, which is what back_finalize() needs to stop blaming our own disk on the server.

Report rather than abort is the call for the non-fatal class, since a full disk means the next file fails too while a broken pipe is about one destination. 350 and 355 cover both classes with controls, and every guard was reverted in turn and reds.

Closes #1398

http_xfread1() advances r->size before the fwrite, so a body the failing
write happened to complete still satisfied the size test at the bottom
and came back as a clean EOF. #1391 stopped that for the fatal class
alone; every other write errno was laundered the same way, in
back_wait()'s chunk-end test, its classification of a short read and its
length check too.

The non-fatal class gets a code of its own, STATUSCODE_IO_ERROR, and the
four size-based sites test for either class. A close that fails outside
the fatal class is reported as well, and no longer leaves a slot
claiming a success it never wrote to disk.

Closes #1398

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
xroche and others added 4 commits August 24, 2026 22:59
The decode step writes ~hts-tmp/<name>.u before renaming it into place,
and a full disk there was reported as "Error when decompressing": the
same fatal error wearing the stream's clothes, with the mirror carrying
on. hts_codec_unpack() and hts_zunpack() now leave the local failure's
errno behind and clear it for a body the decoder itself refused, so
back_finalize() can tell the two apart the way every other write site
does. Both closes are checked as well, stdio holding the tail of a small
decode until then.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
http_xfread1() discarded fflush()'s return, and glibc's later fclose() returns
0 once that flush has taken the error. A body with no Content-Length and no
chunking has no completion test to fall back on, so a failed write went
unreported and the file was recorded as mirrored with nothing on disk -- the
fatal class included, where not even "Mirror aborted" was printed.

back_finalize() gated the decode's write failure on check_fatal_io_errno()
where hts_codec_unpack()'s contract is errno != 0, so a non-fatal failure on
~hts-tmp/<name>.u still read as "Error when decompressing".

Also pins the statuscode > 0 guard in back_report_write_failure(), which is
what keeps a failed decode's copy out of the update purge, and drops a dead
ternary in hts_codec_unpack().

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
@xroche
xroche enabled auto-merge (squash) August 24, 2026 22:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A non-fatal write error on the completing read is still read as end of body

1 participant