diff --git a/Cargo.lock b/Cargo.lock index c0f7fda..d12afb0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1681,6 +1681,7 @@ dependencies = [ "clap", "regex", "tar", + "tempfile", "thiserror", "uucore", ] diff --git a/src/uu/tar/Cargo.toml b/src/uu/tar/Cargo.toml index 3b341c7..c9e4375 100644 --- a/src/uu/tar/Cargo.toml +++ b/src/uu/tar/Cargo.toml @@ -26,3 +26,6 @@ path = "src/tar.rs" [[bin]] name = "tar" path = "src/main.rs" + +[dev-dependencies] +tempfile = { workspace = true } diff --git a/src/uu/tar/src/errors.rs b/src/uu/tar/src/errors.rs index 5630a95..de098bf 100644 --- a/src/uu/tar/src/errors.rs +++ b/src/uu/tar/src/errors.rs @@ -54,6 +54,10 @@ pub enum TarError { /// Cannot finalize the archive #[error("tar: Cannot finalize archive: {0}")] CannotFinalizeArchive(io::Error), + + /// Refusing to write archive contents to terminal + #[error("tar: Refusing to write archive contents to terminal (missing -f option?)")] + RefuseWriteArchiveToTerminal, } impl TarError { diff --git a/src/uu/tar/src/operations/create.rs b/src/uu/tar/src/operations/create.rs index bcdbfee..c79b144 100644 --- a/src/uu/tar/src/operations/create.rs +++ b/src/uu/tar/src/operations/create.rs @@ -77,8 +77,8 @@ pub fn create_archive( .iter() .collect(); writeln!( - status_output, - "Removing leading `{}' from member names", + std::io::stderr(), + "tar: Removing leading `{}' from member names", removed.display() ) .map_err(TarError::Io)?; @@ -146,3 +146,32 @@ fn normalize_path(path: &Path, allow_absolute: bool) -> Option { None } } +#[cfg(test)] +mod tests { + use super::*; + use std::io::{self, Write}; + use tempfile::TempDir; + + struct FailFlushWriter; + impl Write for FailFlushWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + fn flush(&mut self) -> io::Result<()> { + Err(io::Error::other("flush failed")) + } + } + + #[test] + fn test_create_archive_flush_failed() { + let dir = TempDir::new().unwrap(); + let file_path = dir.path().join("test.txt"); + fs::write(&file_path, "hello").unwrap(); + + let output = FailFlushWriter; + let status_output = io::sink(); + + let res = create_archive(output, status_output, &[file_path.as_path()], false, false); + assert!(res.is_err()); + } +} diff --git a/src/uu/tar/src/tar.rs b/src/uu/tar/src/tar.rs index 1d97510..61113f6 100644 --- a/src/uu/tar/src/tar.rs +++ b/src/uu/tar/src/tar.rs @@ -9,7 +9,7 @@ pub mod operations; use crate::errors::TarError; use clap::{arg, crate_version, ArgAction, Command}; use std::fs::File; -use std::io; +use std::io::{self, IsTerminal}; use std::path::{Path, PathBuf}; use uucore::error::UResult; use uucore::format_usage; @@ -171,21 +171,25 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let output_is_stdout = archive_path == Path::new("-"); return if output_is_stdout { - let output = io::stdout(); - let status_output = io::stderr(); - operations::create::create_archive( - output, - status_output, - &files, - allow_absolute, - verbose, - ) + if io::stdout().is_terminal() { + Err(TarError::RefuseWriteArchiveToTerminal.into()) + } else { + let output = io::stdout().lock(); + let status_output = io::stderr(); + operations::create::create_archive( + output, + status_output, + &files, + allow_absolute, + verbose, + ) + } } else { let output = File::create(archive_path).map_err(|e| TarError::CannotCreateArchive { path: archive_path.clone(), source: e, })?; - let status_output = io::stdout(); + let status_output = io::stdout().lock(); operations::create::create_archive( output, status_output, diff --git a/tests/by-util/test_tar.rs b/tests/by-util/test_tar.rs index 80196e6..a426e1b 100644 --- a/tests/by-util/test_tar.rs +++ b/tests/by-util/test_tar.rs @@ -190,7 +190,7 @@ fn test_create_absolute_path() { &file_abs_path.display().to_string(), ]) .succeeds() - .stdout_contains("Removing leading"); + .stderr_contains("tar: Removing leading"); assert!(at.file_exists("archive-trimed.tar")); @@ -868,6 +868,10 @@ fn test_create_verbose_to_stdout_keeps_stdout_as_tar_data() { "tar header should contain filename" ); assert_eq!(&bytes[..8], b"file.txt"); + assert_eq!( + bytes[8], 0, + "stdout should not contain verbose output prefix" + ); } #[test]