Skip to content

Commit

Permalink
cp: Support copying FIFOs with -r (#3032)
Browse files Browse the repository at this point in the history
  • Loading branch information
water-ghosts authored Mar 3, 2022
1 parent 618a268 commit eace4bc
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 7 deletions.
44 changes: 37 additions & 7 deletions src/uu/cp/src/cp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,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;
Expand All @@ -43,6 +45,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)]
Expand All @@ -55,9 +61,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);

Expand Down Expand Up @@ -150,15 +153,15 @@ 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,
Standard,
}

/// Specifies whether when overwrite files
#[derive(Clone, Eq, PartialEq)]
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum OverwriteMode {
/// [Default] Always overwrite existing files
Clobber(ClobberMode),
Expand Down Expand Up @@ -1391,12 +1394,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 {
Expand All @@ -1416,6 +1430,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) };
if err == -1 {
return Err(format!("cannot create fifo {}: File exists", dest.quote()).into());
}
Ok(())
}

fn copy_link(
source: &Path,
dest: &Path,
Expand Down Expand Up @@ -1499,7 +1530,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();

Expand Down
14 changes: 14 additions & 0 deletions tests/by-util/test_cp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1463,6 +1463,20 @@ fn test_cp_archive_on_nonexistent_file() {
);
}

#[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"));
}

#[test]
fn test_dir_recursive_copy() {
let scene = TestScenario::new(util_name!());
Expand Down

0 comments on commit eace4bc

Please sign in to comment.