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:
parent
618a268f61
commit
eace4bc907
2 changed files with 51 additions and 7 deletions
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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!());
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue