1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 19:47:45 +00:00

feature(cp) implement -P/--no-deference

This commit is contained in:
Sylvestre Ledru 2020-05-26 21:35:30 +02:00
parent 4b4d11b61a
commit 280fafed8a
2 changed files with 74 additions and 8 deletions

View file

@ -207,6 +207,7 @@ pub struct Options {
copy_contents: bool,
copy_mode: CopyMode,
dereference: bool,
no_dereference: bool,
no_target_dir: bool,
one_file_system: bool,
overwrite: OverwriteMode,
@ -414,6 +415,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.value_name("ATTR_LIST")
.conflicts_with_all(&[OPT_PRESERVE_DEFAULT_ATTRIBUTES, OPT_PRESERVE, OPT_ARCHIVE])
.help("don't preserve the specified attributes"))
.arg(Arg::with_name(OPT_NO_DEREFERENCE)
.short("-P")
.long(OPT_NO_DEREFERENCE)
.conflicts_with(OPT_DEREFERENCE)
.help("never follow symbolic links in SOURCE"))
// TODO: implement the following args
.arg(Arg::with_name(OPT_ARCHIVE)
@ -433,11 +439,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.long(OPT_DEREFERENCE)
.conflicts_with(OPT_NO_DEREFERENCE)
.help("NotImplemented: always follow symbolic links in SOURCE"))
.arg(Arg::with_name(OPT_NO_DEREFERENCE)
.short("-P")
.long(OPT_NO_DEREFERENCE)
.conflicts_with(OPT_DEREFERENCE)
.help("NotImplemented: never follow symbolic links in SOURCE"))
.arg(Arg::with_name(OPT_PARENTS)
.long(OPT_PARENTS)
.help("NotImplemented: use full source file name under DIRECTORY"))
@ -565,7 +566,6 @@ impl Options {
OPT_COPY_CONTENTS,
OPT_NO_DEREFERENCE_PRESERVE_LINKS,
OPT_DEREFERENCE,
OPT_NO_DEREFERENCE,
OPT_PARENTS,
OPT_SPARSE,
OPT_STRIP_TRAILING_SLASHES,
@ -627,6 +627,7 @@ impl Options {
copy_contents: matches.is_present(OPT_COPY_CONTENTS),
copy_mode: CopyMode::from_matches(matches),
dereference: matches.is_present(OPT_DEREFERENCE),
no_dereference: matches.is_present(OPT_NO_DEREFERENCE),
one_file_system: matches.is_present(OPT_ONE_FILE_SYSTEM),
overwrite: OverwriteMode::from_matches(matches),
parents: matches.is_present(OPT_PARENTS),
@ -794,6 +795,8 @@ fn copy(sources: &[Source], target: &Target, options: &Options) -> CopyResult<()
}
}
let dont_follow_symbolic_links = options.no_dereference;
let mut hard_links: Vec<(String, u64)> = vec![];
let mut non_fatal_errors = false;
@ -807,7 +810,20 @@ fn copy(sources: &[Source], target: &Target, options: &Options) -> CopyResult<()
let dest = construct_dest_path(source, target, &target_type, options)?;
preserve_hardlinks(&mut hard_links, source, dest, &mut found_hard_link).unwrap();
}
if !found_hard_link {
if dont_follow_symbolic_links && fs::symlink_metadata(&source)?.file_type().is_symlink()
{
// Here, we will copy the symlink itself (actually, just recreate it)
let link = fs::read_link(&source)?;
let dest = if target.is_dir() {
// the target is a directory, we need to keep the filename
let p = Path::new(source.file_name().unwrap());
target.join(p)
} else {
target.clone()
};
symlink_file(&link, &dest, &*context_for(&link, target))?;
} else if !found_hard_link {
if let Err(error) = copy_source(source, target, &target_type, options) {
show_error!("{}", error);
match error {
@ -1070,7 +1086,6 @@ fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> {
}
}
}
match options.copy_mode {
CopyMode::Link => {
fs::hard_link(source, dest).context(&*context_for(source, dest))?;

View file

@ -2,8 +2,15 @@ use crate::common::util::*;
#[cfg(not(windows))]
use std::fs::set_permissions;
#[cfg(not(windows))]
use std::os::unix::fs;
#[cfg(windows)]
use std::os::windows::fs::symlink_file;
static TEST_EXISTING_FILE: &str = "existing_file.txt";
static TEST_HELLO_WORLD_SOURCE: &str = "hello_world.txt";
static TEST_HELLO_WORLD_SOURCE_SYMLINK: &str = "hello_world.txt.link";
static TEST_HELLO_WORLD_DEST: &str = "copy_of_hello_world.txt";
static TEST_HOW_ARE_YOU_SOURCE: &str = "how_are_you.txt";
static TEST_HOW_ARE_YOU_DEST: &str = "hello_dir/how_are_you.txt";
@ -328,3 +335,47 @@ fn test_cp_arg_suffix() {
"How are you?\n"
);
}
#[test]
fn test_cp_no_deref() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
#[cfg(not(windows))]
let _r = fs::symlink(
TEST_HELLO_WORLD_SOURCE,
at.subdir.join(TEST_HELLO_WORLD_SOURCE_SYMLINK),
);
#[cfg(windows)]
let _r = symlink_file(
TEST_HELLO_WORLD_SOURCE,
at.subdir.join(TEST_HELLO_WORLD_SOURCE_SYMLINK),
);
//using -t option
let result = scene
.ucmd()
.arg("-P")
.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HELLO_WORLD_SOURCE_SYMLINK)
.arg(TEST_COPY_TO_FOLDER)
.run();
// Check that the exit code represents a successful copy.
let exit_success = result.success;
assert!(exit_success);
let path_to_new_symlink = at
.subdir
.join(TEST_COPY_TO_FOLDER)
.join(TEST_HELLO_WORLD_SOURCE_SYMLINK);
assert!(at.is_symlink(
&path_to_new_symlink
.clone()
.into_os_string()
.into_string()
.unwrap()
));
// Check the content of the destination file that was copied.
assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n");
let path_to_check = path_to_new_symlink.to_str().unwrap();
assert_eq!(at.read(&path_to_check), "Hello, World!\n");
}