Summary
With -v (or when heading multiple files), head prints a ==> <filename> <== header for each file. The filename is written with print_verbatim(file).unwrap(). When the underlying stdout write fails for a reason other than a broken pipe (e.g. the output device is full, or the redirect target is no longer writable), that .unwrap() turns the io::Error into a panic.
Unlike the plain -v > /dev/full write-error class seen in other utilities, this one only fires when the filename argument is longer than the stdout buffer (~1024 bytes). The two neighbouring header writes use ? and fail gracefully; only the filename write uses unwrap, and a short filename stays buffered (its error surfaces later at the ?-checked flush). A filename longer than the buffer forces write_all to flush mid-write, so the failure is raised inside the un-propagated unwrap — hence the long path is required to trigger it.
Steps to reproduce
Redirect stdout to /dev/full (every write fails with ENOSPC) and pass a file argument whose path is longer than ~1024 bytes. A path made of repeated ./ segments resolves to a normal file while printing verbatim as a long string:
$ LONG="/dev/$(python3 -c "print('./'*512)")null" # ~1033-byte path to /dev/null
$ head -v "$LONG" > /dev/full
thread 'main' panicked at src/uu/head/src/head.rs:463:42:
called `Result::unwrap()` on an `Err` value: Os { code: 28, kind: StorageFull, message: "No space left on device" }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Aborted (core dumped)
$ echo $?
134
The threshold is sharp — a filename at/below the buffer size fails gracefully, above it aborts:
$ head -v /dev/null > /dev/full ; echo $? # short name → graceful
head: No space left on device
1
Expected behavior
Match GNU: report the write error and exit non-zero, without panicking — regardless of filename length.
$ /usr/bin/head -v "$LONG" > /dev/full ; echo $?
/usr/bin/head: write error: No space left on device
1
Root cause
In the verbose header block of uu_head (src/uu/head/src/head.rs), the two literal writes propagate their error but the filename write unwraps it:
write!(stdout, "==> ")?;
print_verbatim(file).unwrap(); // <-- panics on write error
writeln!(stdout, " <==")?;
print_verbatim (src/uucore/src/lib/mods/display.rs) writes directly to the
process-global stdout:
pub fn print_verbatim<S: AsRef<OsStr>>(text: S) -> io::Result<()> {
io::stdout().write_all_os(text.as_ref())
}
io::stdout() is a LineWriter with a ~1024-byte buffer. For a short filename the bytes stay buffered and the error only surfaces at the next ?-checked flush (graceful); for a filename larger than the buffer, write_all_os flushes mid-write and the ENOSPC/write error is returned to the unwrap, which panics.
Found by our static analysis tooling.
Summary
With
-v(or when heading multiple files),headprints a==> <filename> <==header for each file. The filename is written withprint_verbatim(file).unwrap(). When the underlying stdout write fails for a reason other than a broken pipe (e.g. the output device is full, or the redirect target is no longer writable), that.unwrap()turns theio::Errorinto a panic.Unlike the plain
-v > /dev/fullwrite-error class seen in other utilities, this one only fires when the filename argument is longer than the stdout buffer (~1024 bytes). The two neighbouring header writes use?and fail gracefully; only the filename write usesunwrap, and a short filename stays buffered (its error surfaces later at the?-checked flush). A filename longer than the buffer forceswrite_allto flush mid-write, so the failure is raised inside the un-propagatedunwrap— hence the long path is required to trigger it.Steps to reproduce
Redirect stdout to
/dev/full(every write fails withENOSPC) and pass a file argument whose path is longer than ~1024 bytes. A path made of repeated./segments resolves to a normal file while printing verbatim as a long string:The threshold is sharp — a filename at/below the buffer size fails gracefully, above it aborts:
Expected behavior
Match GNU: report the write error and exit non-zero, without panicking — regardless of filename length.
Root cause
In the verbose header block of
uu_head(src/uu/head/src/head.rs), the two literal writes propagate their error but the filename write unwraps it:print_verbatim(src/uucore/src/lib/mods/display.rs) writes directly to theprocess-global stdout:
io::stdout()is aLineWriterwith a ~1024-byte buffer. For a short filename the bytes stay buffered and the error only surfaces at the next?-checked flush (graceful); for a filename larger than the buffer,write_all_osflushes mid-write and theENOSPC/write error is returned to theunwrap, which panics.Found by our static analysis tooling.