cp: preserve hard links when target already exists

Prevent a panic in `cp -a` when the target of a hard link already
exists in the target directory structure.

For example,

    $ mkdir -p src dest/src
    $ touch src/f dest/src/f
    $ ln src/f src/link
    $ cp -a src dest

The `cp` command now succeeds without error.
This commit is contained in:
Jeffrey Finkelstein 2022-12-02 23:46:48 -05:00
parent 3ca6139e0f
commit 66ee373373
3 changed files with 51 additions and 0 deletions

View file

@ -2363,3 +2363,32 @@ fn test_reflink_never_sparse_always() {
assert_eq!(src_metadata.blocks(), dest_metadata.blocks());
assert_eq!(dest_metadata.len(), 1024 * 1024);
}
/// Test for preserving attributes of a hard link in a directory.
#[test]
fn test_preserve_hardlink_attributes_in_directory() {
let (at, mut ucmd) = at_and_ucmd!();
// The source directory tree.
at.mkdir("src");
at.touch("src/f");
at.hard_link("src/f", "src/link");
// The destination directory tree.
//
// The file `f` already exists, but the `link` does not.
at.mkdir_all("dest/src");
at.touch("dest/src/f");
ucmd.args(&["-a", "src", "dest"]).succeeds().no_output();
// The hard link should now appear in the destination directory tree.
//
// A hard link should have the same inode as the target file.
at.file_exists("dest/src/link");
#[cfg(unix)]
assert_eq!(
at.metadata("dest/src/f").ino(),
at.metadata("dest/src/link").ino()
);
}

View file

@ -228,6 +228,11 @@ impl CmdResult {
self
}
/// Assert that there is output to neither stderr nor stdout.
pub fn no_output(&self) -> &Self {
self.no_stdout().no_stderr()
}
/// asserts that the command resulted in stdout stream output that equals the
/// passed in value, trailing whitespace are kept to force strict comparison (#1235)
/// stdout_only is a better choice unless stderr may or will be non-empty