From c113284a4bf6441e59d621bc1022f23d48405f85 Mon Sep 17 00:00:00 2001 From: Rayhan Faizel Date: Mon, 12 Jun 2023 23:46:33 +0530 Subject: [PATCH 1/3] uucore: modify are_hardlinks_to_same_file to check only immediate metadata --- src/uucore/src/lib/features/fs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uucore/src/lib/features/fs.rs b/src/uucore/src/lib/features/fs.rs index a97e8ac9c..e92d0977f 100644 --- a/src/uucore/src/lib/features/fs.rs +++ b/src/uucore/src/lib/features/fs.rs @@ -635,12 +635,12 @@ pub fn are_hardlinks_to_same_file(_source: &Path, _target: &Path) -> bool { /// * `bool` - Returns `true` if the paths are hard links to the same file, and `false` otherwise. #[cfg(unix)] pub fn are_hardlinks_to_same_file(source: &Path, target: &Path) -> bool { - let source_metadata = match fs::metadata(source) { + let source_metadata = match fs::symlink_metadata(source) { Ok(metadata) => metadata, Err(_) => return false, }; - let target_metadata = match fs::metadata(target) { + let target_metadata = match fs::symlink_metadata(target) { Ok(metadata) => metadata, Err(_) => return false, }; From fa11315ce7f2b47c098cb5b3741a0b0b1e11eb9e Mon Sep 17 00:00:00 2001 From: Rayhan Faizel Date: Mon, 12 Jun 2023 23:48:49 +0530 Subject: [PATCH 2/3] mv: check strictly for hard links first before checking for symlinks to catch a very special case, plus ensure only nobackupmode is considered --- src/uu/mv/src/mv.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/uu/mv/src/mv.rs b/src/uu/mv/src/mv.rs index 523183b0b..6289e79f9 100644 --- a/src/uu/mv/src/mv.rs +++ b/src/uu/mv/src/mv.rs @@ -25,7 +25,7 @@ use std::path::{Path, PathBuf}; use uucore::backup_control::{self, BackupMode}; use uucore::display::Quotable; use uucore::error::{set_exit_code, FromIo, UError, UResult, USimpleError, UUsageError}; -use uucore::fs::are_hardlinks_or_one_way_symlink_to_same_file; +use uucore::fs::{are_hardlinks_or_one_way_symlink_to_same_file, are_hardlinks_to_same_file}; use uucore::update_control::{self, UpdateMode}; use uucore::{format_usage, help_about, help_section, help_usage, prompt_yes, show}; @@ -255,8 +255,10 @@ fn handle_two_paths(source: &Path, target: &Path, b: &Behavior) -> UResult<()> { return Err(MvError::NoSuchFile(source.quote().to_string()).into()); } - if (source.eq(target) || are_hardlinks_or_one_way_symlink_to_same_file(source, target)) - && b.backup != BackupMode::SimpleBackup + if (source.eq(target) + || are_hardlinks_to_same_file(source, target) + || are_hardlinks_or_one_way_symlink_to_same_file(source, target)) + && b.backup == BackupMode::NoBackup { if source.eq(Path::new(".")) || source.ends_with("/.") || source.is_file() { return Err( From 269ffc12b47243fcd16dcda561eee558f4eb12dd Mon Sep 17 00:00:00 2001 From: Rayhan Faizel Date: Mon, 12 Jun 2023 23:49:28 +0530 Subject: [PATCH 3/3] tests/mv: add tests to check for copying symlinks onto hardlinks to symlink --- tests/by-util/test_mv.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/by-util/test_mv.rs b/tests/by-util/test_mv.rs index cef95d195..0c292c50d 100644 --- a/tests/by-util/test_mv.rs +++ b/tests/by-util/test_mv.rs @@ -465,6 +465,35 @@ fn test_mv_same_symlink() { .stderr_is(format!("mv: '{file_c}' and '{file_a}' are the same file\n",)); } +#[test] +#[cfg(all(unix, not(target_os = "android")))] +fn test_mv_hardlink_to_symlink() { + let (at, mut ucmd) = at_and_ucmd!(); + let file = "file"; + let symlink_file = "symlink"; + let hardlink_to_symlink_file = "hardlink_to_symlink"; + + at.touch(file); + at.symlink_file(file, symlink_file); + at.hard_link(symlink_file, hardlink_to_symlink_file); + + ucmd.arg(symlink_file).arg(hardlink_to_symlink_file).fails(); + + let (at2, mut ucmd2) = at_and_ucmd!(); + + at2.touch(file); + at2.symlink_file(file, symlink_file); + at2.hard_link(symlink_file, hardlink_to_symlink_file); + + ucmd2 + .arg("--backup") + .arg(symlink_file) + .arg(hardlink_to_symlink_file) + .succeeds(); + assert!(!at2.symlink_exists(symlink_file)); + assert!(at2.symlink_exists(&format!("{hardlink_to_symlink_file}~"))); +} + #[test] #[cfg(all(unix, not(target_os = "android")))] fn test_mv_same_hardlink_backup_simple() {