From e1a5e47f197d72758eafc62b74a8478203166a8d Mon Sep 17 00:00:00 2001 From: Christopher Dryden Date: Sat, 7 Feb 2026 18:30:21 +0000 Subject: [PATCH] tac: skip mmap for non-regular files to match GNU 9.9 behavior --- src/uu/tac/src/tac.rs | 15 +++++++-------- tests/by-util/test_tac.rs | 40 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/uu/tac/src/tac.rs b/src/uu/tac/src/tac.rs index ec8ae450344..92277768645 100644 --- a/src/uu/tac/src/tac.rs +++ b/src/uu/tac/src/tac.rs @@ -362,20 +362,19 @@ fn tac(filenames: &[OsString], before: bool, regex: bool, separator: &str) -> UR } } else { let path = Path::new(filename); - if path.is_dir() { - let e: Box = - TacError::InvalidDirectoryArgument(filename.clone()).into(); - show!(e); + let Ok(metadata) = path.metadata() else { + show!(TacError::FileNotFound(filename.clone())); continue; - } + }; - if path.metadata().is_err() { - let e: Box = TacError::FileNotFound(filename.clone()).into(); + if metadata.is_dir() { + let e: Box = + TacError::InvalidDirectoryArgument(filename.clone()).into(); show!(e); continue; } - if let Some(mmap1) = try_mmap_path(path) { + if let Some(mmap1) = metadata.is_file().then(|| try_mmap_path(path)).flatten() { mmap = mmap1; &mmap } else { diff --git a/tests/by-util/test_tac.rs b/tests/by-util/test_tac.rs index 1fc42d1c83f..6ec45d91d91 100644 --- a/tests/by-util/test_tac.rs +++ b/tests/by-util/test_tac.rs @@ -416,3 +416,43 @@ fn test_regular_end_anchor() { .succeeds() .stdout_is("\nccc\nbbaaa\nb"); } + +#[test] +#[cfg(target_os = "linux")] +fn test_non_regular_files() { + use std::process::Stdio; + + new_ucmd!().arg("/dev/null").succeeds().no_output(); + + let content = std::fs::read_to_string("/proc/version").unwrap(); + new_ucmd!() + .arg("/proc/version") + .succeeds() + .stdout_is(&content); + + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + at.mkfifo("fifo"); + let fifo_path = at.plus("fifo"); + let writer = std::thread::spawn(move || std::fs::write(fifo_path, b"a\nb\nc\n").unwrap()); + scene.ucmd().arg("fifo").succeeds().stdout_is("c\nb\na\n"); + writer.join().unwrap(); + + for dev in &["/dev/zero", "/dev/urandom"] { + let mut child = new_ucmd!() + .arg(dev) + .set_stdout(Stdio::piped()) + .run_no_wait(); + child.make_assertion_with_delay(500).is_alive(); + child.kill(); + } + + at.symlink_file("/dev/urandom", "sym_urandom"); + let mut child = scene + .ucmd() + .arg("sym_urandom") + .set_stdout(Stdio::piped()) + .run_no_wait(); + child.make_assertion_with_delay(500).is_alive(); + child.kill(); +}