mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-30 04:27:45 +00:00
Merge pull request #4208 from jfinkels/cp-preserve-link
cp: preserve hard links when target already exists
This commit is contained in:
commit
2facffa489
3 changed files with 51 additions and 0 deletions
|
@ -918,6 +918,23 @@ fn preserve_hardlinks(
|
|||
|
||||
for hard_link in hard_links.iter() {
|
||||
if hard_link.1 == inode {
|
||||
// Consider the following files:
|
||||
//
|
||||
// * `src/f` - a regular file
|
||||
// * `src/link` - a hard link to `src/f`
|
||||
// * `dest/src/f` - a different regular file
|
||||
//
|
||||
// In this scenario, if we do `cp -a src/ dest/`, it is
|
||||
// possible that the order of traversal causes `src/link`
|
||||
// to get copied first (to `dest/src/link`). In that case,
|
||||
// in order to make sure `dest/src/link` is a hard link to
|
||||
// `dest/src/f` and `dest/src/f` has the contents of
|
||||
// `src/f`, we delete the existing file to allow the hard
|
||||
// linking.
|
||||
if file_or_link_exists(dest) && file_or_link_exists(Path::new(&hard_link.0)) {
|
||||
std::fs::remove_file(dest)?;
|
||||
}
|
||||
|
||||
std::fs::hard_link(hard_link.0.clone(), dest).unwrap();
|
||||
*found_hard_link = true;
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue