mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-27 11:07:44 +00:00
Merge pull request #6965 from DaringCuteSeal/install-stream
install: implement copying from streams
This commit is contained in:
commit
438c3c93b2
4 changed files with 91 additions and 14 deletions
|
@ -23,6 +23,7 @@ file_diff = { workspace = true }
|
|||
libc = { workspace = true }
|
||||
uucore = { workspace = true, features = [
|
||||
"backup-control",
|
||||
"buf-copy",
|
||||
"fs",
|
||||
"mode",
|
||||
"perms",
|
||||
|
|
|
@ -12,14 +12,12 @@ use file_diff::diff;
|
|||
use filetime::{set_file_times, FileTime};
|
||||
use std::error::Error;
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::prelude::OsStrExt;
|
||||
use std::fs::{self, metadata};
|
||||
use std::path::{Path, PathBuf, MAIN_SEPARATOR};
|
||||
use std::process;
|
||||
use uucore::backup_control::{self, BackupMode};
|
||||
use uucore::buf_copy::copy_stream;
|
||||
use uucore::display::Quotable;
|
||||
use uucore::entries::{grp2gid, usr2uid};
|
||||
use uucore::error::{FromIo, UError, UIoError, UResult, UUsageError};
|
||||
|
@ -29,6 +27,11 @@ use uucore::perms::{wrap_chown, Verbosity, VerbosityLevel};
|
|||
use uucore::process::{getegid, geteuid};
|
||||
use uucore::{format_usage, help_about, help_usage, show, show_error, show_if_err, uio_error};
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::{FileTypeExt, MetadataExt};
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::prelude::OsStrExt;
|
||||
|
||||
const DEFAULT_MODE: u32 = 0o755;
|
||||
const DEFAULT_STRIP_PROGRAM: &str = "strip";
|
||||
|
||||
|
@ -736,7 +739,24 @@ fn perform_backup(to: &Path, b: &Behavior) -> UResult<Option<PathBuf>> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Copy a file from one path to another.
|
||||
/// Copy a non-special file using std::fs::copy.
|
||||
///
|
||||
/// # Parameters
|
||||
/// * `from` - The source file path.
|
||||
/// * `to` - The destination file path.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns an empty Result or an error in case of failure.
|
||||
fn copy_normal_file(from: &Path, to: &Path) -> UResult<()> {
|
||||
if let Err(err) = fs::copy(from, to) {
|
||||
return Err(InstallError::InstallFailed(from.to_path_buf(), to.to_path_buf(), err).into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Copy a file from one path to another. Handles the certain cases of special
|
||||
/// files (e.g character specials).
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
|
@ -760,18 +780,26 @@ fn copy_file(from: &Path, to: &Path) -> UResult<()> {
|
|||
}
|
||||
}
|
||||
|
||||
if from.as_os_str() == "/dev/null" {
|
||||
/* workaround a limitation of fs::copy
|
||||
* https://github.com/rust-lang/rust/issues/79390
|
||||
*/
|
||||
if let Err(err) = File::create(to) {
|
||||
let ft = match metadata(from) {
|
||||
Ok(ft) => ft.file_type(),
|
||||
Err(err) => {
|
||||
return Err(
|
||||
InstallError::InstallFailed(from.to_path_buf(), to.to_path_buf(), err).into(),
|
||||
);
|
||||
}
|
||||
} else if let Err(err) = fs::copy(from, to) {
|
||||
return Err(InstallError::InstallFailed(from.to_path_buf(), to.to_path_buf(), err).into());
|
||||
};
|
||||
|
||||
// Stream-based copying to get around the limitations of std::fs::copy
|
||||
#[cfg(unix)]
|
||||
if ft.is_char_device() || ft.is_block_device() || ft.is_fifo() {
|
||||
let mut handle = File::open(from)?;
|
||||
let mut dest = File::create(to)?;
|
||||
copy_stream(&mut handle, &mut dest)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
copy_normal_file(from, to)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -58,8 +58,6 @@ impl From<nix::Error> for Error {
|
|||
///
|
||||
/// Result of operation and bytes successfully written (as a `u64`) when
|
||||
/// operation is successful.
|
||||
///
|
||||
|
||||
pub fn copy_stream<R, S>(src: &mut R, dest: &mut S) -> UResult<u64>
|
||||
where
|
||||
R: Read + AsFd + AsRawFd,
|
||||
|
|
|
@ -1717,3 +1717,53 @@ fn test_install_root_combined() {
|
|||
run_and_check(&["-Cv", "c", "d"], "d", 0, 0);
|
||||
run_and_check(&["-Cv", "c", "d"], "d", 0, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_install_from_fifo() {
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
use std::thread;
|
||||
|
||||
let pipe_name = "pipe";
|
||||
let target_name = "target";
|
||||
let test_string = "Hello, world!\n";
|
||||
|
||||
let s = TestScenario::new(util_name!());
|
||||
s.fixtures.mkfifo(pipe_name);
|
||||
assert!(s.fixtures.is_fifo(pipe_name));
|
||||
|
||||
let proc = s.ucmd().arg(pipe_name).arg(target_name).run_no_wait();
|
||||
|
||||
let pipe_path = s.fixtures.plus(pipe_name);
|
||||
let thread = thread::spawn(move || {
|
||||
let mut pipe = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(false)
|
||||
.open(pipe_path)
|
||||
.unwrap();
|
||||
pipe.write_all(test_string.as_bytes()).unwrap();
|
||||
});
|
||||
|
||||
proc.wait().unwrap();
|
||||
thread.join().unwrap();
|
||||
|
||||
assert!(s.fixtures.file_exists(target_name));
|
||||
assert_eq!(s.fixtures.read(target_name), test_string);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_install_from_stdin() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
let target = "target";
|
||||
let test_string = "Hello, World!\n";
|
||||
|
||||
ucmd.arg("/dev/fd/0")
|
||||
.arg(target)
|
||||
.pipe_in(test_string)
|
||||
.succeeds();
|
||||
|
||||
assert!(at.file_exists(target));
|
||||
assert_eq!(at.read(target), test_string);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue