From aa9b59e88115a70458af68a46b165711f76b1390 Mon Sep 17 00:00:00 2001 From: Eli Youngs Date: Tue, 1 Feb 2022 19:40:04 -0800 Subject: [PATCH 1/5] cp: Support FIFOs --- src/uu/cp/src/cp.rs | 43 ++++++++++++++++++++++++++++++++++------ tests/by-util/test_cp.rs | 14 +++++++++++++ 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index f8ce6f24137..b65238db65a 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -29,6 +29,8 @@ use std::borrow::Cow; use clap::{crate_version, App, AppSettings, Arg, ArgMatches}; use filetime::FileTime; +#[cfg(unix)] +use libc::mkfifo; use quick_error::ResultExt; use std::collections::HashSet; use std::env; @@ -42,6 +44,10 @@ use std::fs::OpenOptions; use std::io; use std::io::{stdin, stdout, Write}; use std::mem; +#[cfg(unix)] +use std::os::unix::ffi::OsStrExt; +#[cfg(unix)] +use std::os::unix::fs::{FileTypeExt, PermissionsExt}; #[cfg(target_os = "linux")] use std::os::unix::io::AsRawFd; #[cfg(windows)] @@ -54,9 +60,6 @@ use uucore::error::{set_exit_code, ExitCode, UError, UResult}; use uucore::fs::{canonicalize, MissingHandling, ResolveMode}; use walkdir::WalkDir; -#[cfg(unix)] -use std::os::unix::fs::PermissionsExt; - #[cfg(target_os = "linux")] ioctl!(write ficlone with 0x94, 9; std::os::raw::c_int); @@ -149,7 +152,7 @@ pub type Target = PathBuf; pub type TargetSlice = Path; /// Specifies whether when overwrite files -#[derive(Clone, Eq, PartialEq)] +#[derive(Clone, Copy, Eq, PartialEq)] pub enum ClobberMode { Force, RemoveDestination, @@ -157,7 +160,7 @@ pub enum ClobberMode { } /// Specifies whether when overwrite files -#[derive(Clone, Eq, PartialEq)] +#[derive(Clone, Copy, Eq, PartialEq)] pub enum OverwriteMode { /// [Default] Always overwrite existing files Clobber(ClobberMode), @@ -1376,12 +1379,23 @@ fn copy_helper( let parent = dest.parent().unwrap_or(dest); fs::create_dir_all(parent)?; } - let is_symlink = fs::symlink_metadata(&source)?.file_type().is_symlink(); + + let file_type = fs::symlink_metadata(&source)?.file_type(); + let is_symlink = file_type.is_symlink(); + + #[cfg(unix)] + let is_fifo = file_type.is_fifo(); + #[cfg(not(unix))] + let is_fifo = false; + if source.as_os_str() == "/dev/null" { /* workaround a limitation of fs::copy * https://github.com/rust-lang/rust/issues/79390 */ File::create(dest).context(dest.display().to_string())?; + } else if is_fifo && options.recursive { + #[cfg(unix)] + copy_fifo(dest, options.overwrite)?; } else if is_symlink { copy_link(source, dest, symlinked_files)?; } else if options.reflink_mode != ReflinkMode::Never { @@ -1401,6 +1415,23 @@ fn copy_helper( Ok(()) } +// "Copies" a FIFO by creating a new one. This workaround is because Rust's +// built-in fs::copy does not handle FIFOs (see rust-lang/rust/issues/79390). +#[cfg(unix)] +fn copy_fifo(dest: &Path, overwrite: OverwriteMode) -> CopyResult<()> { + if dest.exists() { + overwrite.verify(&dest)?; + fs::remove_file(&dest)?; + } + + let name = CString::new(dest.as_os_str().as_bytes()).unwrap(); + let err = unsafe { mkfifo(name.as_ptr(), 0o666 as libc::mode_t) }; + if err == -1 { + return Err(format!("cannot create fifo {}: File exists", dest.quote()).into()); + } + Ok(()) +} + fn copy_link( source: &Path, dest: &Path, diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index 92637dfbe3e..940a6d7333b 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -1444,3 +1444,17 @@ fn test_cp_archive_on_nonexistent_file() { "cp: cannot stat 'nonexistent_file.txt': No such file or directory (os error 2)", ); } + +#[test] +#[cfg(unix)] +fn test_cp_fifo() { + let (at, mut ucmd) = at_and_ucmd!(); + at.mkfifo("fifo"); + ucmd.arg("-r") + .arg("fifo") + .arg("fifo2") + .succeeds() + .no_stderr() + .no_stdout(); + assert!(at.is_fifo("fifo2")); +} From f90c0f85668b084f220ce35f6b0cbf0611849148 Mon Sep 17 00:00:00 2001 From: Eli Youngs Date: Tue, 1 Feb 2022 23:21:29 -0800 Subject: [PATCH 2/5] Fix clippy warnings --- src/uu/cp/src/cp.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index b65238db65a..eb3daf4fbf5 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1420,12 +1420,12 @@ fn copy_helper( #[cfg(unix)] fn copy_fifo(dest: &Path, overwrite: OverwriteMode) -> CopyResult<()> { if dest.exists() { - overwrite.verify(&dest)?; + overwrite.verify(dest)?; fs::remove_file(&dest)?; } let name = CString::new(dest.as_os_str().as_bytes()).unwrap(); - let err = unsafe { mkfifo(name.as_ptr(), 0o666 as libc::mode_t) }; + let err = unsafe { mkfifo(name.as_ptr(), 0o666_u32) }; if err == -1 { return Err(format!("cannot create fifo {}: File exists", dest.quote()).into()); } From 9886435225d4abd1fbd3a65a4fa2e18f6fdf36de Mon Sep 17 00:00:00 2001 From: Eli Youngs Date: Wed, 2 Feb 2022 00:01:42 -0800 Subject: [PATCH 3/5] Fix clippy warnings for Mac --- src/uu/cp/src/cp.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index eb3daf4fbf5..fca1865a60c 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1425,7 +1425,7 @@ fn copy_fifo(dest: &Path, overwrite: OverwriteMode) -> CopyResult<()> { } let name = CString::new(dest.as_os_str().as_bytes()).unwrap(); - let err = unsafe { mkfifo(name.as_ptr(), 0o666_u32) }; + let err = unsafe { mkfifo(name.as_ptr(), 0o666) }; if err == -1 { return Err(format!("cannot create fifo {}: File exists", dest.quote()).into()); } @@ -1515,7 +1515,6 @@ fn copy_on_write_macos( // Extract paths in a form suitable to be passed to a syscall. // The unwrap() is safe because they come from the command-line and so contain non nul // character. - use std::os::unix::ffi::OsStrExt; let src = CString::new(source.as_os_str().as_bytes()).unwrap(); let dst = CString::new(dest.as_os_str().as_bytes()).unwrap(); From 9353e16887b8a6c247f9419d2cefe51e425a9c73 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 20 Feb 2022 10:35:13 +0100 Subject: [PATCH 4/5] Add missing } --- tests/by-util/test_cp.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index 982110e6a56..152e62642b9 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -1475,6 +1475,7 @@ fn test_cp_fifo() { .no_stderr() .no_stdout(); assert!(at.is_fifo("fifo2")); +} #[test] fn test_cp_dir_vs_file() { From ca0f3903441a006d30baf7fb96fe11bb8c309615 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Mon, 28 Feb 2022 14:47:11 +0100 Subject: [PATCH 5/5] Remove trailing whitespaces --- tests/by-util/test_cp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index a0fddd91fe1..8ae358b313e 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -1477,7 +1477,7 @@ fn test_cp_fifo() { assert!(at.is_fifo("fifo2")); } -#[test] +#[test] fn test_dir_recursive_copy() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures;