ln: Restore backup to destination if linking fails. (#11355)

Co-authored-by: aweinstock <avi@zellic.io>
This commit is contained in:
Avi Weinstock
2026-03-18 17:48:31 -04:00
committed by GitHub
parent 74bf86dbea
commit 1b4f47d2bd
2 changed files with 41 additions and 14 deletions
+25 -14
View File
@@ -438,23 +438,34 @@ fn link(src: &Path, dst: &Path, settings: &Settings) -> UResult<()> {
}
}
if settings.symbolic {
symlink(&source, dst)?;
} else {
let p = if settings.logical && source.is_symlink() {
fs::canonicalize(&source)
.map_err_context(|| translate!("ln-failed-to-access", "file" => source.quote()))?
let res = (|| -> UResult<()> {
if settings.symbolic {
Ok(symlink(&source, dst)?)
} else {
source.to_path_buf()
};
if let Err(e) = fs::hard_link(&p, dst) {
if p.is_dir() {
return Err(LnError::FailedToCreateHardLinkDir(source.to_path_buf()).into());
let p = if settings.logical && source.is_symlink() {
fs::canonicalize(&source).map_err_context(
|| translate!("ln-failed-to-access", "file" => source.quote()),
)?
} else {
source.to_path_buf()
};
if let Err(e) = fs::hard_link(&p, dst) {
if p.is_dir() {
return Err(LnError::FailedToCreateHardLinkDir(source.to_path_buf()).into());
}
return Err(e).map_err_context(|| {
translate!("ln-failed-to-create-hard-link", "source" => source.quote(), "dest" => dst.quote())
});
}
return Err(e).map_err_context(|| {
translate!("ln-failed-to-create-hard-link", "source" => source.quote(), "dest" => dst.quote())
});
Ok(())
}
})();
if res.is_err() {
if let Some(ref p) = backup_path {
fs::rename(p, dst)
.map_err_context(|| translate!("ln-cannot-backup", "file" => dst.quote()))?;
}
res?;
}
if settings.verbose {
+16
View File
@@ -1065,3 +1065,19 @@ fn test_ln_no_dereference_symbolic() {
assert!(at.is_symlink("b~"));
}
}
#[test]
fn test_ln_backup_nonexistent_rollback() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("dst");
scene
.ucmd()
.args(&["--backup", "/non/existent/path", "dst"])
.fails();
assert!(!at.file_exists("dst~"));
assert!(at.file_exists("dst"));
}