From ab42b1e5995dec8dfbf145c0229afc9a0c71d920 Mon Sep 17 00:00:00 2001 From: Rayhan Faizel Date: Sat, 10 Jun 2023 18:31:30 +0530 Subject: [PATCH 1/3] uucore: add function which checks hardlink as well as directed symlink --- src/uucore/src/lib/features/fs.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/uucore/src/lib/features/fs.rs b/src/uucore/src/lib/features/fs.rs index 797be9c2c..a97e8ac9c 100644 --- a/src/uucore/src/lib/features/fs.rs +++ b/src/uucore/src/lib/features/fs.rs @@ -648,6 +648,36 @@ pub fn are_hardlinks_to_same_file(source: &Path, target: &Path) -> bool { source_metadata.ino() == target_metadata.ino() && source_metadata.dev() == target_metadata.dev() } +#[cfg(not(unix))] +pub fn are_hardlinks_or_one_way_symlink_to_same_file(_source: &Path, _target: &Path) -> bool { + false +} + +/// Checks if either two paths are hard links to the same file or if the source path is a symbolic link which when fully resolved points to target path +/// +/// # Arguments +/// +/// * `source` - A reference to a `Path` representing the source path. +/// * `target` - A reference to a `Path` representing the target path. +/// +/// # Returns +/// +/// * `bool` - Returns `true` if either of above conditions are true, and `false` otherwise. +#[cfg(unix)] +pub fn are_hardlinks_or_one_way_symlink_to_same_file(source: &Path, target: &Path) -> bool { + let source_metadata = match fs::metadata(source) { + Ok(metadata) => metadata, + Err(_) => return false, + }; + + let target_metadata = match fs::symlink_metadata(target) { + Ok(metadata) => metadata, + Err(_) => return false, + }; + + source_metadata.ino() == target_metadata.ino() && source_metadata.dev() == target_metadata.dev() +} + #[cfg(test)] mod tests { // Note this useful idiom: importing names from outer (for mod tests) scope. From 28ad5cab9f25f15e6584ac6eb97dec301670dc31 Mon Sep 17 00:00:00 2001 From: Rayhan Faizel Date: Sat, 10 Jun 2023 18:32:09 +0530 Subject: [PATCH 2/3] mv: fix GNU test tests/mv/into-self-2.sh --- src/uu/mv/src/mv.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uu/mv/src/mv.rs b/src/uu/mv/src/mv.rs index 5ca677c69..523183b0b 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_to_same_file; +use uucore::fs::are_hardlinks_or_one_way_symlink_to_same_file; use uucore::update_control::{self, UpdateMode}; use uucore::{format_usage, help_about, help_section, help_usage, prompt_yes, show}; @@ -255,7 +255,7 @@ 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_to_same_file(source, target)) + if (source.eq(target) || are_hardlinks_or_one_way_symlink_to_same_file(source, target)) && b.backup != BackupMode::SimpleBackup { if source.eq(Path::new(".")) || source.ends_with("/.") || source.is_file() { From 6bc15aac99d6fd77d09fa8cc88b109f6a18c39d5 Mon Sep 17 00:00:00 2001 From: Rayhan Faizel Date: Sat, 10 Jun 2023 18:32:41 +0530 Subject: [PATCH 3/3] tests/mv: Test for particular edge cases when handling symlink files --- tests/by-util/test_mv.rs | 48 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/by-util/test_mv.rs b/tests/by-util/test_mv.rs index f73d3249d..cef95d195 100644 --- a/tests/by-util/test_mv.rs +++ b/tests/by-util/test_mv.rs @@ -417,6 +417,54 @@ fn test_mv_same_hardlink() { .stderr_is(format!("mv: '{file_a}' and '{file_b}' are the same file\n",)); } +#[test] +#[cfg(all(unix, not(target_os = "android")))] +fn test_mv_same_symlink() { + let (at, mut ucmd) = at_and_ucmd!(); + let file_a = "test_mv_same_file_a"; + let file_b = "test_mv_same_file_b"; + let file_c = "test_mv_same_file_c"; + + at.touch(file_a); + + at.symlink_file(file_a, file_b); + + ucmd.arg(file_b) + .arg(file_a) + .fails() + .stderr_is(format!("mv: '{file_b}' and '{file_a}' are the same file\n",)); + + let (at2, mut ucmd2) = at_and_ucmd!(); + at2.touch(file_a); + + at2.symlink_file(file_a, file_b); + ucmd2.arg(file_a).arg(file_b).succeeds(); + assert!(at2.file_exists(file_b)); + assert!(!at2.file_exists(file_a)); + + let (at3, mut ucmd3) = at_and_ucmd!(); + at3.touch(file_a); + + at3.symlink_file(file_a, file_b); + at3.symlink_file(file_b, file_c); + + ucmd3.arg(file_c).arg(file_b).succeeds(); + assert!(!at3.symlink_exists(file_c)); + assert!(at3.symlink_exists(file_b)); + + let (at4, mut ucmd4) = at_and_ucmd!(); + at4.touch(file_a); + + at4.symlink_file(file_a, file_b); + at4.symlink_file(file_b, file_c); + + ucmd4 + .arg(file_c) + .arg(file_a) + .fails() + .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_same_hardlink_backup_simple() {