diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 359f23572..c03eb25a5 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -32,8 +32,8 @@ use platform::copy_on_write; use uucore::display::Quotable; use uucore::error::{set_exit_code, UClapError, UError, UResult, UUsageError}; use uucore::fs::{ - canonicalize, is_symlink_loop, paths_refer_to_same_file, FileInformation, MissingHandling, - ResolveMode, + are_hardlinks_to_same_file, canonicalize, is_symlink_loop, paths_refer_to_same_file, + FileInformation, MissingHandling, ResolveMode, }; use uucore::{backup_control, update_control}; // These are exposed for projects (e.g. nushell) that want to create an `Options` value, which @@ -1672,6 +1672,15 @@ fn copy_file( } } + if are_hardlinks_to_same_file(source, dest) + && matches!( + options.overwrite, + OverwriteMode::Clobber(ClobberMode::RemoveDestination) + ) + { + fs::remove_file(dest)?; + } + if file_or_link_exists(dest) { handle_existing_dest(source, dest, options, source_in_command_line)?; } diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index 8a20443a4..dfd20c9c2 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -2881,6 +2881,24 @@ fn test_cp_mode_hardlink_no_dereference() { assert_eq!(at.read_symlink("z"), "slink"); } +#[cfg(not(any(windows, target_os = "android")))] +#[test] +fn test_remove_destination_with_destination_being_a_hardlink_to_source() { + let (at, mut ucmd) = at_and_ucmd!(); + let file = "file"; + let hardlink = "hardlink"; + + at.touch(file); + at.hard_link(file, hardlink); + + ucmd.args(&["--remove-destination", file, hardlink]) + .succeeds(); + + assert_eq!("", at.resolve_link(hardlink)); + assert!(at.file_exists(file)); + assert!(at.file_exists(hardlink)); +} + #[test] fn test_remove_destination_with_destination_being_a_symlink_to_source() { let (at, mut ucmd) = at_and_ucmd!();