From d6b112a241a67dd5aef3d38c6e297126e5dcd879 Mon Sep 17 00:00:00 2001 From: Kanishk Sachan Date: Fri, 17 Jul 2026 16:10:24 +0530 Subject: [PATCH 1/3] head: fix panic when -v header write fails on a long filename When head prints the "==> name <==" header with -v and the filename is longer than stdout's ~1024-byte buffer, a write error (e.g. /dev/full) was surfaced inside print_verbatim().unwrap(), causing an abort instead of a graceful error message. The two adjacent writes already use `?`, but print_verbatim() wrote to a fresh io::stdout() handle and its result was unwrapped. Replace it with stdout.write_all_os(file.as_ref())? so the filename is written through the same locked handle and any error is propagated like the surrounding writes. Fixes #13264 --- src/uu/head/src/head.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uu/head/src/head.rs b/src/uu/head/src/head.rs index 24ff9e5c04..9a1e393b53 100644 --- a/src/uu/head/src/head.rs +++ b/src/uu/head/src/head.rs @@ -17,7 +17,7 @@ use std::os::fd::AsFd; use std::path::Path; use std::path::PathBuf; use thiserror::Error; -use uucore::display::{Quotable, print_verbatim}; +use uucore::display::{OsWrite, Quotable}; use uucore::error::{FromIo, UError, UResult, USimpleError}; use uucore::line_ending::LineEnding; use uucore::show; @@ -461,7 +461,7 @@ fn uu_head(options: &HeadOptions) -> UResult<()> { writeln!(stdout)?; } write!(stdout, "==> ")?; - print_verbatim(file).unwrap(); + stdout.write_all_os(file.as_ref())?; writeln!(stdout, " <==")?; first = false; } From a5d770a580c3fbf4697bf15b97d1dfc3d3af6179 Mon Sep 17 00:00:00 2001 From: Kanishk Sachan Date: Sat, 18 Jul 2026 19:24:47 +0530 Subject: [PATCH 2/3] head: add test for -v header write error (#13264) --- tests/by-util/test_head.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/by-util/test_head.rs b/tests/by-util/test_head.rs index 0f4e9dae5d..e8d319ec19 100644 --- a/tests/by-util/test_head.rs +++ b/tests/by-util/test_head.rs @@ -897,6 +897,26 @@ fn test_all_but_last_lines() { .stdout_is_fixture("lorem_ipsum_backwards_15_lines.expected"); } +#[cfg(target_os = "linux")] +#[test] +fn test_verbose_header_write_error() { + // Before the fix, the filename in the "==> name <==" header was written + // with print_verbatim().unwrap(), which panicked on a write error. + // With -v, head always prints the header; any write failure must be + // reported gracefully rather than causing an abort (#13264). + use std::fs::OpenOptions; + let dev_full = OpenOptions::new() + .write(true) + .open("/dev/full") + .unwrap(); + new_ucmd!() + .arg("-v") + .pipe_in("") + .set_stdout(dev_full) + .fails() + .stderr_contains("No space left on device"); +} + #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))] #[test] fn test_write_to_dev_full() { From e64f32806d50c33518fc089758b172aee60a8524 Mon Sep 17 00:00:00 2001 From: Kanishk Sachan Date: Sun, 19 Jul 2026 16:40:06 +0530 Subject: [PATCH 3/3] head: fix cargo fmt style in test_verbose_header_write_error --- tests/by-util/test_head.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/by-util/test_head.rs b/tests/by-util/test_head.rs index e8d319ec19..53657065da 100644 --- a/tests/by-util/test_head.rs +++ b/tests/by-util/test_head.rs @@ -905,10 +905,7 @@ fn test_verbose_header_write_error() { // With -v, head always prints the header; any write failure must be // reported gracefully rather than causing an abort (#13264). use std::fs::OpenOptions; - let dev_full = OpenOptions::new() - .write(true) - .open("/dev/full") - .unwrap(); + let dev_full = OpenOptions::new().write(true).open("/dev/full").unwrap(); new_ucmd!() .arg("-v") .pipe_in("")