Skip to content

Commit 1b4f47d

Browse files
aweinstock314aweinstock
andauthored
ln: Restore backup to destination if linking fails. (#11355)
Co-authored-by: aweinstock <avi@zellic.io>
1 parent 74bf86d commit 1b4f47d

2 files changed

Lines changed: 41 additions & 14 deletions

File tree

src/uu/ln/src/ln.rs

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -438,23 +438,34 @@ fn link(src: &Path, dst: &Path, settings: &Settings) -> UResult<()> {
438438
}
439439
}
440440

441-
if settings.symbolic {
442-
symlink(&source, dst)?;
443-
} else {
444-
let p = if settings.logical && source.is_symlink() {
445-
fs::canonicalize(&source)
446-
.map_err_context(|| translate!("ln-failed-to-access", "file" => source.quote()))?
441+
let res = (|| -> UResult<()> {
442+
if settings.symbolic {
443+
Ok(symlink(&source, dst)?)
447444
} else {
448-
source.to_path_buf()
449-
};
450-
if let Err(e) = fs::hard_link(&p, dst) {
451-
if p.is_dir() {
452-
return Err(LnError::FailedToCreateHardLinkDir(source.to_path_buf()).into());
445+
let p = if settings.logical && source.is_symlink() {
446+
fs::canonicalize(&source).map_err_context(
447+
|| translate!("ln-failed-to-access", "file" => source.quote()),
448+
)?
449+
} else {
450+
source.to_path_buf()
451+
};
452+
if let Err(e) = fs::hard_link(&p, dst) {
453+
if p.is_dir() {
454+
return Err(LnError::FailedToCreateHardLinkDir(source.to_path_buf()).into());
455+
}
456+
return Err(e).map_err_context(|| {
457+
translate!("ln-failed-to-create-hard-link", "source" => source.quote(), "dest" => dst.quote())
458+
});
453459
}
454-
return Err(e).map_err_context(|| {
455-
translate!("ln-failed-to-create-hard-link", "source" => source.quote(), "dest" => dst.quote())
456-
});
460+
Ok(())
461+
}
462+
})();
463+
if res.is_err() {
464+
if let Some(ref p) = backup_path {
465+
fs::rename(p, dst)
466+
.map_err_context(|| translate!("ln-cannot-backup", "file" => dst.quote()))?;
457467
}
468+
res?;
458469
}
459470

460471
if settings.verbose {

tests/by-util/test_ln.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,3 +1065,19 @@ fn test_ln_no_dereference_symbolic() {
10651065
assert!(at.is_symlink("b~"));
10661066
}
10671067
}
1068+
1069+
#[test]
1070+
fn test_ln_backup_nonexistent_rollback() {
1071+
let scene = TestScenario::new(util_name!());
1072+
let at = &scene.fixtures;
1073+
1074+
at.touch("dst");
1075+
1076+
scene
1077+
.ucmd()
1078+
.args(&["--backup", "/non/existent/path", "dst"])
1079+
.fails();
1080+
1081+
assert!(!at.file_exists("dst~"));
1082+
assert!(at.file_exists("dst"));
1083+
}

0 commit comments

Comments
 (0)