From 92fb444b879677edadc5588e73c755ac1303264e Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sat, 9 Mar 2024 21:17:28 +0100 Subject: [PATCH 1/2] cp: improve the support of --attributes-only --- src/uu/cp/src/cp.rs | 30 ++++++++++++++++++++-- tests/by-util/test_cp.rs | 55 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 9a3a0848342..b9da0282c1c 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -780,7 +780,13 @@ impl CopyMode { { Self::Update } else if matches.get_flag(options::ATTRIBUTES_ONLY) { - Self::AttrOnly + if matches.get_flag(options::REMOVE_DESTINATION) { + // if we pass --remove-destination --attributes-only, we want to move the copy mode + Self::Copy + } else { + // if no --remove-destination, back to the previous mode + Self::AttrOnly + } } else { Self::Copy } @@ -1709,7 +1715,13 @@ fn copy_file( fs::remove_file(dest)?; } - if file_or_link_exists(dest) { + if file_or_link_exists(dest) + && (!options.attributes_only + || matches!( + options.overwrite, + OverwriteMode::Clobber(ClobberMode::RemoveDestination) + )) + { if are_hardlinks_to_same_file(source, dest) && !options.force() && options.backup == BackupMode::NoBackup @@ -1721,6 +1733,20 @@ fn copy_file( handle_existing_dest(source, dest, options, source_in_command_line)?; } + if options.attributes_only + && source.is_symlink() + && !matches!( + options.overwrite, + OverwriteMode::Clobber(ClobberMode::RemoveDestination) + ) + { + return Err(format!( + "cannot change attribute {}: Source file is a non regular file", + dest.quote() + ) + .into()); + } + if options.preserve_hard_links() { // if we encounter a matching device/inode pair in the source tree // we can arrange to create a hard link between the corresponding names diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index d3cee58adc9..2ec1e623af6 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -3800,3 +3800,58 @@ fn test_acl_preserve() { assert!(compare_xattrs(&file, &file_target)); } + +#[test] +fn test_cp_force_remove_destination_attributes_only_with_symlink() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + at.write("file1", "1"); + at.write("file2", "2"); + at.symlink_file("file1", "sym1"); + + scene + .ucmd() + .args(&[ + "-a", + "--remove-destination", + "--attributes-only", + "sym1", + "file2", + ]) + .succeeds(); + + assert!( + at.symlink_exists("file2"), + "file2 is not a symbolic link as expected" + ); + + assert_eq!( + at.read("file1"), + at.read("file2"), + "Contents of file1 and file2 do not match" + ); +} + +#[test] +fn test_cp_no_dereference_attributes_only_with_symlink() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + at.write("file1", "1"); + at.write("file2", "2"); + at.write("file2.exp", "2"); + at.symlink_file("file1", "sym1"); + + let result = scene + .ucmd() + .args(&["--no-dereference", "--attributes-only", "sym1", "file2"]) + .fails(); + + assert_eq!(result.code(), 1, "cp command did not fail"); + + assert_eq!( + at.read("file2"), + at.read("file2.exp"), + "file2 content does not match expected" + ); +} From 503202827baab92de94f139b0ef458863725a49f Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Mon, 11 Mar 2024 07:52:22 +0100 Subject: [PATCH 2/2] remove useless comments Co-authored-by: Daniel Hofstetter --- src/uu/cp/src/cp.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index b9da0282c1c..6c060265310 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -781,10 +781,8 @@ impl CopyMode { Self::Update } else if matches.get_flag(options::ATTRIBUTES_ONLY) { if matches.get_flag(options::REMOVE_DESTINATION) { - // if we pass --remove-destination --attributes-only, we want to move the copy mode Self::Copy } else { - // if no --remove-destination, back to the previous mode Self::AttrOnly } } else {