@@ -1499,10 +1499,11 @@ fn context_for(src: &Path, dest: &Path) -> String {
14991499 format ! ( "{} -> {}" , src. quote( ) , dest. quote( ) )
15001500}
15011501
1502- /// Implements a simple backup copy for the destination file.
1502+ /// Implements a simple backup copy for the destination file .
1503+ /// if is_dest_symlink flag is set to true dest will be renamed to backup_path
15031504/// TODO: for the backup, should this function be replaced by `copy_file(...)`?
1504- fn backup_dest ( dest : & Path , backup_path : & Path ) -> CopyResult < PathBuf > {
1505- if dest . is_symlink ( ) {
1505+ fn backup_dest ( dest : & Path , backup_path : & Path , is_dest_symlink : bool ) -> CopyResult < PathBuf > {
1506+ if is_dest_symlink {
15061507 fs:: rename ( dest, backup_path) ?;
15071508 } else {
15081509 fs:: copy ( dest, backup_path) ?;
@@ -1536,6 +1537,7 @@ fn handle_existing_dest(
15361537 dest : & Path ,
15371538 options : & Options ,
15381539 source_in_command_line : bool ,
1540+ copied_files : & mut HashMap < FileInformation , PathBuf > ,
15391541) -> CopyResult < ( ) > {
15401542 // Disallow copying a file to itself, unless `--force` and
15411543 // `--backup` are both specified.
@@ -1545,6 +1547,7 @@ fn handle_existing_dest(
15451547
15461548 options. overwrite . verify ( dest) ?;
15471549
1550+ let mut is_dest_removed = false ;
15481551 let backup_path = backup_control:: get_backup_path ( options. backup , dest, & options. backup_suffix ) ;
15491552 if let Some ( backup_path) = backup_path {
15501553 if paths_refer_to_same_file ( source, & backup_path, true ) {
@@ -1555,13 +1558,16 @@ fn handle_existing_dest(
15551558 )
15561559 . into ( ) ) ;
15571560 } else {
1558- backup_dest ( dest, & backup_path) ?;
1561+ is_dest_removed = dest. is_symlink ( ) ;
1562+ backup_dest ( dest, & backup_path, is_dest_removed) ?;
15591563 }
15601564 }
15611565 match options. overwrite {
15621566 // FIXME: print that the file was removed if --verbose is enabled
15631567 OverwriteMode :: Clobber ( ClobberMode :: Force ) => {
1564- if is_symlink_loop ( dest) || fs:: metadata ( dest) ?. permissions ( ) . readonly ( ) {
1568+ if !is_dest_removed
1569+ && ( is_symlink_loop ( dest) || fs:: metadata ( dest) ?. permissions ( ) . readonly ( ) )
1570+ {
15651571 fs:: remove_file ( dest) ?;
15661572 }
15671573 }
@@ -1582,7 +1588,18 @@ fn handle_existing_dest(
15821588 // `dest/src/f` and `dest/src/f` has the contents of
15831589 // `src/f`, we delete the existing file to allow the hard
15841590 // linking.
1585- if options. preserve_hard_links ( ) {
1591+
1592+ if options. preserve_hard_links ( )
1593+ // only try to remove dest file only if the current source is hardlink to a file that is already copied
1594+ && copied_files. contains_key (
1595+ & FileInformation :: from_path (
1596+ source,
1597+ options. dereference ( source_in_command_line) ,
1598+ )
1599+ . context ( format ! ( "cannot stat {}" , source. quote( ) ) ) ?,
1600+ )
1601+ && !is_dest_removed
1602+ {
15861603 fs:: remove_file ( dest) ?;
15871604 }
15881605 }
@@ -1708,7 +1725,7 @@ fn handle_copy_mode(
17081725 let backup_path =
17091726 backup_control:: get_backup_path ( options. backup , dest, & options. backup_suffix ) ;
17101727 if let Some ( backup_path) = backup_path {
1711- backup_dest ( dest, & backup_path) ?;
1728+ backup_dest ( dest, & backup_path, dest . is_symlink ( ) ) ?;
17121729 fs:: remove_file ( dest) ?;
17131730 }
17141731 if options. overwrite == OverwriteMode :: Clobber ( ClobberMode :: Force ) {
@@ -1935,7 +1952,7 @@ fn copy_file(
19351952 {
19361953 return Ok ( ( ) ) ;
19371954 }
1938- handle_existing_dest ( source, dest, options, source_in_command_line) ?;
1955+ handle_existing_dest ( source, dest, options, source_in_command_line, copied_files ) ?;
19391956 }
19401957
19411958 if options. attributes_only
0 commit comments