From fbefc2bd9956596c9e6f986a7420132cf5be8603 Mon Sep 17 00:00:00 2001 From: Lorenzo Rossi <65499789+rossilor95@users.noreply.github.com> Date: Sun, 19 Apr 2026 13:39:40 +0200 Subject: [PATCH] tail: fix panic on write error in bounded_tail (#11886) * tail: fix panic on write error in bounded_tail * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * tail: increase test file size to ensure bounded_tail path --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- src/uu/tail/src/tail.rs | 14 ++++++++------ tests/by-util/test_tail.rs | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/uu/tail/src/tail.rs b/src/uu/tail/src/tail.rs index 4c3cbee25..509a5619d 100644 --- a/src/uu/tail/src/tail.rs +++ b/src/uu/tail/src/tail.rs @@ -174,7 +174,7 @@ fn tail_file( && file.is_seekable(if input.is_stdin() { offset } else { 0 }) && (!st.is_file() || st.len() > blksize_limit) { - bounded_tail(&mut file, settings); + bounded_tail(&mut file, settings)?; reader = BufReader::new(file); } else { reader = BufReader::new(file); @@ -460,7 +460,7 @@ fn backwards_thru_file(file: &mut File, num_delimiters: u64, delimiter: u8) { /// end of the file, and then read the file "backwards" in blocks of size /// `BLOCK_SIZE` until we find the location of the first line/byte. This ends up /// being a nice performance win for very large files. -fn bounded_tail(file: &mut File, settings: &Settings) { +fn bounded_tail(file: &mut File, settings: &Settings) -> UResult<()> { debug_assert!(!settings.presume_input_pipe); let mut limit = None; @@ -493,7 +493,8 @@ fn bounded_tail(file: &mut File, settings: &Settings) { _ => {} } - print_target_section(file, limit); + print_target_section(file, limit)?; + Ok(()) } fn unbounded_tail(reader: &mut BufReader, settings: &Settings) -> UResult<()> { @@ -574,7 +575,7 @@ fn unbounded_tail(reader: &mut BufReader, settings: &Settings) -> UR Ok(()) } -fn print_target_section(file: &mut R, limit: Option) +fn print_target_section(file: &mut R, limit: Option) -> UResult<()> where R: Read + ?Sized, { @@ -583,10 +584,11 @@ where let mut stdout = stdout.lock(); if let Some(limit) = limit { let mut reader = file.take(limit); - io::copy(&mut reader, &mut stdout).unwrap(); + io::copy(&mut reader, &mut stdout)?; } else { - io::copy(file, &mut stdout).unwrap(); + io::copy(file, &mut stdout)?; } + Ok(()) } #[cfg(test)] diff --git a/tests/by-util/test_tail.rs b/tests/by-util/test_tail.rs index c1b755bda..d46ff112c 100644 --- a/tests/by-util/test_tail.rs +++ b/tests/by-util/test_tail.rs @@ -5086,6 +5086,21 @@ fn test_failed_write_is_reported() { .stderr_is("tail: No space left on device\n"); } +#[cfg(target_os = "linux")] +#[test] +fn test_failed_write_is_reported_on_seekable_input() { + let ts = TestScenario::new("tail"); + let at = &ts.fixtures; + + at.write("bigfile", &"x\n".repeat(1_100_000)); + + ts.ucmd() + .arg("bigfile") + .set_stdout(File::create("/dev/full").unwrap()) + .fails() + .stderr_is("tail: No space left on device\n"); +} + #[test] #[cfg(target_os = "linux")] fn test_dev_zero() {