Problem: Block and undo writers buffer serialized data, then rely on BufferedWriter's destructor to write the remaining bytes.
fwrite() can report fewer bytes written than requested after a local storage error, e.g. if the filesystem fills up while a block is being written (my 1 TB benchmarking servers have recently been filling up, so I've been seeing these failures more often).
AutoFile::write_buffer() converts that result into an exception and because the destructor is implicitly non-throwing, the exception invokes std::terminate instead of reaching normal storage-error handling.
Calling flush() before destruction is insufficient on its own because a failed flush leaves the same bytes pending, so the destructor retries the write while the original exception unwinds.
I introduced this bug in #31551 when adding the BufferedWriter.
Fix: BufferedWriter now clears the pending byte count before writing, preventing a destructor retry after a failed explicit flush.
Block and undo writers flush before destruction, so write failures follow normal storage-error handling instead of terminating the process.
<details> <summary>Manual short-write reproducer</summary>
sed -i '' '122s/src.size()/0/' src/streams.cpp
cmake -B build && cmake --build build -j && build/bin/bitcoind -regtest -datadir="$(mktemp -d)"; echo "exit=$?"
With implicit flushing, bitcoind aborts:
libc++abi: terminating due to uncaught exception of type std::__1::ios_base::failure: AutoFile::write_buffer: write failed: unspecified iostream_category error zsh: abort build/bin/bitcoind -regtest -datadir="$(mktemp -d)" exit=134
With explicit flushing, it reports Failed to write genesis block and exits 1 instead of terminating the process:
Shutdown done exit=1
</details>