diff --git a/src/find/mod.rs b/src/find/mod.rs index ff047c82..eafae005 100644 --- a/src/find/mod.rs +++ b/src/find/mod.rs @@ -15,6 +15,7 @@ use std::io::{self, stderr, stdout, BufRead, BufReader, Write}; use std::path::PathBuf; use std::rc::Rc; use std::time::SystemTime; +use uucore::error; use walkdir::WalkDir; pub struct Config { @@ -160,6 +161,8 @@ struct ParsedInfo { /// struct Files0Paths { reader: Box, + /// Kept so that read errors can be reported GNU-style with the offending file name. + name: String, } impl Files0Paths { @@ -168,11 +171,19 @@ impl Files0Paths { let reader: Box = if name == "-" { Box::new(BufReader::new(io::stdin())) } else { - let file = std::fs::File::open(name) - .map_err(|e| format!("cannot open '{}' for reading: {}", name, e))?; + let file = std::fs::File::open(name).map_err(|e| { + format!( + "cannot open ‘{}’ for reading: {}", + name, + error::strip_errno(&e) + ) + })?; Box::new(BufReader::new(file)) }; - Ok(Self { reader }) + Ok(Self { + reader, + name: name.to_string(), + }) } } @@ -183,7 +194,14 @@ impl Iterator for Files0Paths { loop { let mut buffer = Vec::new(); match self.reader.read_until(0, &mut buffer) { - Err(e) => return Some(Err(e.into())), + Err(e) => { + return Some(Err(format!( + "‘{}’: read error: {}", + self.name, + error::strip_errno(&e) + ) + .into())); + } Ok(0) => return None, Ok(_) => {} } diff --git a/tests/test_find.rs b/tests/test_find.rs index 0fab96dd..7285f850 100644 --- a/tests/test_find.rs +++ b/tests/test_find.rs @@ -1631,3 +1631,23 @@ fn find_exits_cleanly_on_broken_pipe() { "find panicked instead of exiting cleanly on a broken pipe:\n{stderr}" ); } + +#[test] +#[cfg(target_os = "linux")] +fn files0_from_special_file_read_error() { + for path in &["/dev/vhost-net", "/dev/vhost-vsock"] { + if !Path::new(path).exists() { + continue; + } + + let file_list_failure = ucmd().arg("-files0-from").arg(path).fails(); + + let error_output = file_list_failure.no_stdout().stderr_str(); + assert!( + error_output.contains("read error") + || (error_output.contains("cannot open") + && error_output.contains("Permission denied")), + "unexpected stderr for {path}: {error_output}" + ); + } +}