diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 669a38601b2..d9fae2d189a 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1256,7 +1256,16 @@ fn handle_existing_dest(source: &Path, dest: &Path, options: &Options) -> CopyRe let backup_path = backup_control::get_backup_path(options.backup, dest, &options.backup_suffix); if let Some(backup_path) = backup_path { - backup_dest(dest, &backup_path)?; + if paths_refer_to_same_file(source, &backup_path)? { + return Err(format!( + "backing up {} might destroy source; {} not copied", + dest.quote(), + source.quote() + ) + .into()); + } else { + backup_dest(dest, &backup_path)?; + } } match options.overwrite { diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index 06134d2dd80..c246d0ef93a 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -558,6 +558,24 @@ fn test_cp_backup_simple() { ); } +#[test] +fn test_cp_backup_simple_protect_source() { + let (at, mut ucmd) = at_and_ucmd!(); + let source = format!("{}~", TEST_HELLO_WORLD_SOURCE); + at.touch(&source); + ucmd.arg("--backup=simple") + .arg(&source) + .arg(TEST_HELLO_WORLD_SOURCE) + .fails() + .stderr_only(format!( + "cp: backing up '{}' might destroy source; '{}' not copied", + TEST_HELLO_WORLD_SOURCE, source, + )); + + assert_eq!(at.read(TEST_HELLO_WORLD_SOURCE), "Hello, World!\n"); + assert_eq!(at.read(&source), ""); +} + #[test] fn test_cp_backup_never() { let (at, mut ucmd) = at_and_ucmd!();