diff --git a/src/uu/csplit/src/csplit.rs b/src/uu/csplit/src/csplit.rs index 3a4b43e881e..b73b32176e6 100644 --- a/src/uu/csplit/src/csplit.rs +++ b/src/uu/csplit/src/csplit.rs @@ -268,10 +268,12 @@ impl SplitWriter<'_> { /// /// # Errors /// - /// The creation of the split file may fail with some [`io::Error`]. - fn new_writer(&mut self) -> io::Result<()> { + /// Returns an error if creating the split file fails. + fn new_writer(&mut self) -> Result<(), CsplitError> { let file_name = self.options.split_name.get(self.counter); - let file = File::create(file_name)?; + let file = File::create(&file_name) + .map_err_context(|| file_name.clone()) + .map_err(CsplitError::from)?; self.current_writer = Some(BufWriter::new(file)); self.counter += 1; self.size = 0; diff --git a/src/uu/csplit/src/csplit_error.rs b/src/uu/csplit/src/csplit_error.rs index d73400bd7d1..497c5c5b63b 100644 --- a/src/uu/csplit/src/csplit_error.rs +++ b/src/uu/csplit/src/csplit_error.rs @@ -6,13 +6,13 @@ use std::io; use thiserror::Error; use uucore::display::Quotable; -use uucore::error::UError; +use uucore::error::{UError, strip_errno}; use uucore::translate; /// Errors thrown by the csplit command #[derive(Debug, Error)] pub enum CsplitError { - #[error("IO error: {}", _0)] + #[error("{}", strip_errno(_0))] IoError(#[from] io::Error), #[error("{}", translate!("csplit-error-line-out-of-range", "pattern" => _0.quote()))] LineOutOfRange(String), @@ -54,3 +54,19 @@ impl UError for CsplitError { } } } + +#[cfg(test)] +mod tests { + use super::CsplitError; + + #[cfg(unix)] + #[test] + fn io_error_display_is_clean() { + // GNU does not print "IO error:" nor the raw "(os error N)" suffix. + let err = CsplitError::IoError(std::io::Error::from_raw_os_error(13)); + let msg = err.to_string(); + assert_eq!(msg, "Permission denied"); + assert!(!msg.contains("IO error:")); + assert!(!msg.contains("os error")); + } +} diff --git a/tests/by-util/test_csplit.rs b/tests/by-util/test_csplit.rs index 385e2945c29..0e15fb47011 100644 --- a/tests/by-util/test_csplit.rs +++ b/tests/by-util/test_csplit.rs @@ -1582,3 +1582,22 @@ fn test_write_error_dev_full_keep_files() { assert!(at.file_exists("xx00")); assert_eq!(at.read("xx00"), "1\n"); } + +/// Test that a failed split-file creation reports the filename. +#[test] +#[cfg(unix)] +fn test_create_error_reports_filename() { + // Root can open a mode-000 file for writing, so File::create would not fail. + if rustix::process::geteuid().is_root() { + return; + } + let (at, mut ucmd) = at_and_ucmd!(); + at.write("input", "a\nb\nc\n"); + // Pre-create the first split with no write permission so File::create fails. + at.touch("xx00"); + at.set_mode("xx00", 0o000); + + ucmd.args(&["input", "2"]) + .fails() + .stderr_is("csplit: xx00: Permission denied\n"); +}