From 050b5b0c9b6746d99be1c63d5845a3e4ea62ed11 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Wed, 30 Mar 2022 00:11:27 +0200 Subject: [PATCH] ln: make the tests/ln/hard-backup.sh test work We haven't a great error message with hard link on the same file + Update the GNU error message to match ours --- src/uu/ln/src/ln.rs | 22 +++++++++++++++++++++- tests/by-util/test_ln.rs | 9 +++++++++ util/build-gnu.sh | 3 +++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/uu/ln/src/ln.rs b/src/uu/ln/src/ln.rs index 2fc478a23..c09f7a606 100644 --- a/src/uu/ln/src/ln.rs +++ b/src/uu/ln/src/ln.rs @@ -54,6 +54,7 @@ enum LnError { TargetIsDirectory(PathBuf), SomeLinksFailed, FailedToLink(String), + SameFile(PathBuf, PathBuf), MissingDestination(PathBuf), ExtraOperand(OsString), } @@ -63,6 +64,12 @@ impl Display for LnError { match self { Self::TargetIsDirectory(s) => write!(f, "target {} is not a directory", s.quote()), Self::FailedToLink(e) => write!(f, "failed to link: {}", e), + Self::SameFile(e, e2) => write!( + f, + "'{}' and '{}' are the same file", + e2.display(), + e.display() + ), Self::SomeLinksFailed => write!(f, "some links failed to create"), Self::MissingDestination(s) => { write!(f, "missing destination file operand after {}", s.quote()) @@ -85,6 +92,7 @@ impl UError for LnError { Self::TargetIsDirectory(_) | Self::SomeLinksFailed | Self::FailedToLink(_) + | Self::SameFile(_, _) | Self::MissingDestination(_) | Self::ExtraOperand(_) => 1, } @@ -381,7 +389,7 @@ fn relative_path<'a>(src: &Path, dst: &Path) -> Result> { Ok(result.into()) } -fn link(src: &Path, dst: &Path, settings: &Settings) -> Result<()> { +fn link(src: &Path, dst: &Path, settings: &Settings) -> UResult<()> { let mut backup_path = None; let source: Cow<'_, Path> = if settings.relative { relative_path(src, dst)? @@ -408,6 +416,18 @@ fn link(src: &Path, dst: &Path, settings: &Settings) -> Result<()> { BackupMode::NumberedBackup => Some(numbered_backup_path(dst)), BackupMode::ExistingBackup => Some(existing_backup_path(dst, &settings.suffix)), }; + if settings.backup == BackupMode::ExistingBackup && !settings.symbolic { + // when ln --backup f f, it should detect that it is the same file + let dst_abs = canonicalize(dst, MissingHandling::Normal, ResolveMode::Logical)?; + let source_abs = canonicalize( + source.clone(), + MissingHandling::Normal, + ResolveMode::Logical, + )?; + if dst_abs == source_abs { + return Err(LnError::SameFile(dst.to_path_buf(), source.to_path_buf()).into()); + } + } if let Some(ref p) = backup_path { fs::rename(dst, p)?; } diff --git a/tests/by-util/test_ln.rs b/tests/by-util/test_ln.rs index a2a31464f..9bda5d412 100644 --- a/tests/by-util/test_ln.rs +++ b/tests/by-util/test_ln.rs @@ -615,3 +615,12 @@ fn test_relative_recursive() { ucmd.args(&["-sr", "dir", "dir/recursive"]).succeeds(); assert_eq!(at.resolve_link("dir/recursive"), "."); } + +#[test] +fn test_backup_same_file() { + let (at, mut ucmd) = at_and_ucmd!(); + at.touch("file1"); + ucmd.args(&["--backup", "file1", "./file1"]) + .fails() + .stderr_contains("'file1' and './file1' are the same file"); +} diff --git a/util/build-gnu.sh b/util/build-gnu.sh index 0cb0af301..bbfb497f7 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -158,3 +158,6 @@ sed -i -e "s~ sed -n \"1s/'\\\/'/'OPT'/p\" < err >> pat || framework_failure_~ # And change the default error code to 2 # see issue #3331 sed -i -e "s/rcexp=1$/rcexp=2\n case \"\$prg\" in chcon|dir|runcon|vdir) return;; esac/" tests/misc/usage_vs_getopt.sh + +# Update the GNU error message to match ours +sed -i -e "s/ln: 'f' and 'f' are the same file/ln: failed to link: 'f' and 'f' are the same file/g" tests/ln/hard-backup.sh