From 0701f535ac415b4b45d8e34e220a9e0c3c7e2e21 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Wed, 27 Dec 2023 15:39:54 +0100 Subject: [PATCH] cp: fix backup of destination symlink --- src/uu/cp/src/cp.rs | 6 +++++- tests/by-util/test_cp.rs | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 6e88dcbb37..97afdee1e0 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1496,7 +1496,11 @@ fn context_for(src: &Path, dest: &Path) -> String { /// Implements a simple backup copy for the destination file. /// TODO: for the backup, should this function be replaced by `copy_file(...)`? fn backup_dest(dest: &Path, backup_path: &Path) -> CopyResult { - fs::copy(dest, backup_path)?; + if dest.is_symlink() { + fs::rename(dest, backup_path)?; + } else { + fs::copy(dest, backup_path)?; + } Ok(backup_path.into()) } diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index 16eed035aa..2636df28a3 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -678,6 +678,27 @@ fn test_cp_arg_backup() { ); } +#[test] +fn test_cp_arg_backup_with_dest_a_symlink() { + let (at, mut ucmd) = at_and_ucmd!(); + let source = "source"; + let source_content = "content"; + let symlink = "symlink"; + let original = "original"; + let backup = "symlink~"; + + at.write(source, source_content); + at.write(original, "original"); + at.symlink_file(original, symlink); + + ucmd.arg("-b").arg(source).arg(symlink).succeeds(); + + assert!(!at.symlink_exists(symlink)); + assert_eq!(source_content, at.read(symlink)); + assert!(at.symlink_exists(backup)); + assert_eq!(original, at.resolve_link(backup)); +} + #[test] fn test_cp_arg_backup_with_other_args() { let (at, mut ucmd) = at_and_ucmd!();