1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

cp: Support copying FIFOs with -r (#3032)

This commit is contained in:
Eli Youngs 2022-03-03 13:58:27 -08:00 committed by GitHub
parent 618a268f61
commit eace4bc907
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 51 additions and 7 deletions

View file

@ -30,6 +30,8 @@ use std::borrow::Cow;
use clap::{crate_version, App, AppSettings, Arg, ArgMatches}; use clap::{crate_version, App, AppSettings, Arg, ArgMatches};
use filetime::FileTime; use filetime::FileTime;
#[cfg(unix)]
use libc::mkfifo;
use quick_error::ResultExt; use quick_error::ResultExt;
use std::collections::HashSet; use std::collections::HashSet;
use std::env; use std::env;
@ -43,6 +45,10 @@ use std::fs::OpenOptions;
use std::io; use std::io;
use std::io::{stdin, stdout, Write}; use std::io::{stdin, stdout, Write};
use std::mem; use std::mem;
#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
#[cfg(unix)]
use std::os::unix::fs::{FileTypeExt, PermissionsExt};
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
use std::os::unix::io::AsRawFd; use std::os::unix::io::AsRawFd;
#[cfg(windows)] #[cfg(windows)]
@ -55,9 +61,6 @@ use uucore::error::{set_exit_code, ExitCode, UError, UResult};
use uucore::fs::{canonicalize, MissingHandling, ResolveMode}; use uucore::fs::{canonicalize, MissingHandling, ResolveMode};
use walkdir::WalkDir; use walkdir::WalkDir;
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
ioctl!(write ficlone with 0x94, 9; std::os::raw::c_int); ioctl!(write ficlone with 0x94, 9; std::os::raw::c_int);
@ -150,7 +153,7 @@ pub type Target = PathBuf;
pub type TargetSlice = Path; pub type TargetSlice = Path;
/// Specifies whether when overwrite files /// Specifies whether when overwrite files
#[derive(Clone, Eq, PartialEq)] #[derive(Clone, Copy, Eq, PartialEq)]
pub enum ClobberMode { pub enum ClobberMode {
Force, Force,
RemoveDestination, RemoveDestination,
@ -158,7 +161,7 @@ pub enum ClobberMode {
} }
/// Specifies whether when overwrite files /// Specifies whether when overwrite files
#[derive(Clone, Eq, PartialEq)] #[derive(Clone, Copy, Eq, PartialEq)]
pub enum OverwriteMode { pub enum OverwriteMode {
/// [Default] Always overwrite existing files /// [Default] Always overwrite existing files
Clobber(ClobberMode), Clobber(ClobberMode),
@ -1391,12 +1394,23 @@ fn copy_helper(
let parent = dest.parent().unwrap_or(dest); let parent = dest.parent().unwrap_or(dest);
fs::create_dir_all(parent)?; 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" { if source.as_os_str() == "/dev/null" {
/* workaround a limitation of fs::copy /* workaround a limitation of fs::copy
* https://github.com/rust-lang/rust/issues/79390 * https://github.com/rust-lang/rust/issues/79390
*/ */
File::create(dest).context(dest.display().to_string())?; 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 { } else if is_symlink {
copy_link(source, dest, symlinked_files)?; copy_link(source, dest, symlinked_files)?;
} else if options.reflink_mode != ReflinkMode::Never { } else if options.reflink_mode != ReflinkMode::Never {
@ -1416,6 +1430,23 @@ fn copy_helper(
Ok(()) 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( fn copy_link(
source: &Path, source: &Path,
dest: &Path, dest: &Path,
@ -1499,7 +1530,6 @@ fn copy_on_write_macos(
// Extract paths in a form suitable to be passed to a syscall. // 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 // The unwrap() is safe because they come from the command-line and so contain non nul
// character. // character.
use std::os::unix::ffi::OsStrExt;
let src = CString::new(source.as_os_str().as_bytes()).unwrap(); let src = CString::new(source.as_os_str().as_bytes()).unwrap();
let dst = CString::new(dest.as_os_str().as_bytes()).unwrap(); let dst = CString::new(dest.as_os_str().as_bytes()).unwrap();

View file

@ -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] #[test]
fn test_dir_recursive_copy() { fn test_dir_recursive_copy() {
let scene = TestScenario::new(util_name!()); let scene = TestScenario::new(util_name!());