mirror of
https://github.com/uutils/coreutils.git
synced 2025-12-23 08:47:37 +00:00
Fix ln -f handling when source and destination are the same entry (#8838)
* fix(ln): enhance same-file detection with canonical paths Improved the `link` function in `ln.rs` to use canonical path resolution for accurate same-file detection when forcing overwrites, preventing incorrect errors for equivalent paths. Added tests to verify behavior for self-linking and hard link relinking scenarios. Co-authored-by: Sylvestre Ledru <sylvestre@debian.org>
This commit is contained in:
parent
3d3c21afab
commit
1c29075b01
2 changed files with 57 additions and 1 deletions
|
|
@ -410,7 +410,17 @@ fn link(src: &Path, dst: &Path, settings: &Settings) -> UResult<()> {
|
|||
}
|
||||
OverwriteMode::Force => {
|
||||
if !dst.is_symlink() && paths_refer_to_same_file(src, dst, true) {
|
||||
return Err(LnError::SameFile(src.to_owned(), dst.to_owned()).into());
|
||||
// Even in force overwrite mode, verify we are not targeting the same entry and return a SameFile error if so
|
||||
let same_entry = match (
|
||||
canonicalize(src, MissingHandling::Missing, ResolveMode::Physical),
|
||||
canonicalize(dst, MissingHandling::Missing, ResolveMode::Physical),
|
||||
) {
|
||||
(Ok(src), Ok(dst)) => src == dst,
|
||||
_ => true,
|
||||
};
|
||||
if same_entry {
|
||||
return Err(LnError::SameFile(src.to_owned(), dst.to_owned()).into());
|
||||
}
|
||||
}
|
||||
if fs::remove_file(dst).is_ok() {}
|
||||
// In case of error, don't do anything
|
||||
|
|
|
|||
|
|
@ -793,6 +793,52 @@ fn test_symlink_remove_existing_same_src_and_dest() {
|
|||
assert_eq!(at.read("a"), "sample");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_force_same_file_detected_after_canonicalization() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
|
||||
at.write("file", "hello");
|
||||
|
||||
ucmd.args(&["-f", "file", "./file"])
|
||||
.fails_with_code(1)
|
||||
.stderr_contains("are the same file");
|
||||
|
||||
assert!(at.file_exists("file"));
|
||||
assert_eq!(at.read("file"), "hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_os = "android"))]
|
||||
fn test_force_ln_existing_hard_link_entry() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
at.write("file", "hardlink\n");
|
||||
at.mkdir("dir");
|
||||
|
||||
scene.ucmd().args(&["file", "dir"]).succeeds().no_stderr();
|
||||
assert!(at.file_exists("dir/file"));
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.args(&["-f", "file", "dir"])
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
|
||||
assert!(at.file_exists("file"));
|
||||
assert!(at.file_exists("dir/file"));
|
||||
assert_eq!(at.read("file"), "hardlink\n");
|
||||
assert_eq!(at.read("dir/file"), "hardlink\n");
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
let source_inode = at.metadata("file").ino();
|
||||
let target_inode = at.metadata("dir/file").ino();
|
||||
assert_eq!(source_inode, target_inode);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_os = "android"))]
|
||||
fn test_ln_seen_file() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue