From 4946478409a045733b33752485d295f0d9068249 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Wed, 1 Nov 2023 10:42:21 +0100 Subject: [PATCH] cp: -l don't fail if dest is hardlink to source --- src/uu/cp/src/cp.rs | 6 ++++++ tests/by-util/test_cp.rs | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index c03eb25a5c..ebd865dc35 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1682,6 +1682,12 @@ fn copy_file( } if file_or_link_exists(dest) { + if are_hardlinks_to_same_file(source, dest) + && !options.force() + && options.backup == BackupMode::NoBackup + { + return Ok(()); + } 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 dfd20c9c26..261057cf3a 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -531,6 +531,25 @@ fn test_cp_arg_link() { assert_eq!(at.metadata(TEST_HELLO_WORLD_SOURCE).st_nlink(), 2); } +#[test] +#[cfg(target_os = "linux")] +fn test_cp_arg_link_with_dest_hardlink_to_source() { + use std::os::linux::fs::MetadataExt; + + let (at, mut ucmd) = at_and_ucmd!(); + let file = "file"; + let hardlink = "hardlink"; + + at.touch(file); + at.hard_link(file, hardlink); + + ucmd.args(&["--link", file, hardlink]).succeeds(); + + assert_eq!(at.metadata(file).st_nlink(), 2); + assert!(at.file_exists(file)); + assert!(at.file_exists(hardlink)); +} + #[test] fn test_cp_arg_symlink() { let (at, mut ucmd) = at_and_ucmd!();