diff --git a/src/uu/head/src/head.rs b/src/uu/head/src/head.rs index 61082204734..4a2c6f26952 100644 --- a/src/uu/head/src/head.rs +++ b/src/uu/head/src/head.rs @@ -461,7 +461,7 @@ fn uu_head(options: &HeadOptions) -> UResult<()> { writeln!(stdout)?; } write!(stdout, "==> ")?; - print_verbatim(file).unwrap(); + print_verbatim(file)?; writeln!(stdout, " <==")?; first = false; } diff --git a/tests/by-util/test_head.rs b/tests/by-util/test_head.rs index 90a9829aeb8..1ad450a1d4b 100644 --- a/tests/by-util/test_head.rs +++ b/tests/by-util/test_head.rs @@ -1050,6 +1050,31 @@ fn test_unreadable_file_prints_no_header() { .stderr_contains("cannot open 'unreadable' for reading: Permission denied"); } +/// Regression for #13887: writing the `==> filename <==` verbose header to a +/// full/closed stdout must surface the write error instead of panicking inside +/// `print_verbatim(...).unwrap()`. A filename longer than the stdout buffer +/// forces the header write to flush mid-write so the failure surfaces inside +/// the filename write rather than at the next checked one. +#[test] +#[cfg(target_os = "linux")] +#[cfg_attr(wasi_runner, ignore = "WASI sandbox: host paths (/dev) not visible")] +fn test_verbose_header_write_error_long_filename() { + use std::fs::File; + + let dev_full = + File::create("/dev/full").expect("Failed to open /dev/full - test must run on Linux"); + + let long_path = format!("/dev/{}null", "./".repeat(512)); + + new_ucmd!() + .arg("-v") + .arg(long_path) + .set_stdout(dev_full) + .fails() + .code_is(1) + .stderr_contains("No space left on device"); +} + /// Regression for #11972: head must reject directories detected on the /// open fd, not via a separate `Path::is_dir()` call. A symlink that /// resolves to a directory must still be rejected — verifying the fd