diff --git a/src/uu/install/src/install.rs b/src/uu/install/src/install.rs index 9955be7b2..331a50f67 100644 --- a/src/uu/install/src/install.rs +++ b/src/uu/install/src/install.rs @@ -748,6 +748,18 @@ fn perform_backup(to: &Path, b: &Behavior) -> UResult> { /// Returns an empty Result or an error in case of failure. /// fn copy_file(from: &Path, to: &Path) -> UResult<()> { + // fs::copy fails if destination is a invalid symlink. + // so lets just remove all existing files at destination before copy. + if let Err(e) = fs::remove_file(to) { + if e.kind() != std::io::ErrorKind::NotFound { + show_error!( + "Failed to remove existing file {}. Error: {:?}", + to.display(), + e + ); + } + } + if from.as_os_str() == "/dev/null" { /* workaround a limitation of fs::copy * https://github.com/rust-lang/rust/issues/79390 diff --git a/tests/by-util/test_install.rs b/tests/by-util/test_install.rs index 6b1d76e55..5790c685f 100644 --- a/tests/by-util/test_install.rs +++ b/tests/by-util/test_install.rs @@ -8,7 +8,7 @@ use crate::common::util::{is_ci, run_ucmd_as_root, TestScenario}; use filetime::FileTime; use std::fs; use std::os::unix::fs::{MetadataExt, PermissionsExt}; -#[cfg(not(any(windows, target_os = "freebsd")))] +#[cfg(not(windows))] use std::process::Command; #[cfg(any(target_os = "linux", target_os = "android"))] use std::thread::sleep; @@ -610,13 +610,17 @@ fn test_install_copy_then_compare_file_with_extra_mode() { } const STRIP_TARGET_FILE: &str = "helloworld_installed"; -#[cfg(not(any(windows, target_os = "freebsd")))] +#[cfg(all(not(windows), not(target_os = "freebsd")))] const SYMBOL_DUMP_PROGRAM: &str = "objdump"; -#[cfg(not(any(windows, target_os = "freebsd")))] +#[cfg(target_os = "freebsd")] +const SYMBOL_DUMP_PROGRAM: &str = "llvm-objdump"; +#[cfg(not(windows))] const STRIP_SOURCE_FILE_SYMBOL: &str = "main"; fn strip_source_file() -> &'static str { - if cfg!(target_os = "macos") { + if cfg!(target_os = "freebsd") { + "helloworld_freebsd" + } else if cfg!(target_os = "macos") { "helloworld_macos" } else if cfg!(target_arch = "arm") || cfg!(target_arch = "aarch64") { "helloworld_android" @@ -626,8 +630,7 @@ fn strip_source_file() -> &'static str { } #[test] -// FixME: Freebsd fails on 'No such file or directory' -#[cfg(not(any(windows, target_os = "freebsd")))] +#[cfg(not(windows))] fn test_install_and_strip() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; @@ -650,8 +653,7 @@ fn test_install_and_strip() { } #[test] -// FixME: Freebsd fails on 'No such file or directory' -#[cfg(not(any(windows, target_os = "freebsd")))] +#[cfg(not(windows))] fn test_install_and_strip_with_program() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; @@ -677,8 +679,6 @@ fn test_install_and_strip_with_program() { #[cfg(all(unix, feature = "chmod"))] #[test] -// FixME: Freebsd fails on 'No such file or directory' -#[cfg(not(target_os = "freebsd"))] fn test_install_and_strip_with_program_hyphen() { let scene = TestScenario::new(util_name!()); @@ -715,6 +715,64 @@ fn test_install_and_strip_with_program_hyphen() { .stdout_is("./-dest\n"); } +#[cfg(all(unix, feature = "chmod"))] +#[test] +fn test_install_on_invalid_link_at_destination() { + let scene = TestScenario::new(util_name!()); + + let at = &scene.fixtures; + at.mkdir("src"); + at.mkdir("dest"); + let src_dir = at.plus("src"); + let dst_dir = at.plus("dest"); + + at.touch("test.sh"); + at.symlink_file( + "/opt/FakeDestination", + &dst_dir.join("test.sh").to_string_lossy(), + ); + scene.ccmd("chmod").arg("+x").arg("test.sh").succeeds(); + at.symlink_file("test.sh", &src_dir.join("test.sh").to_string_lossy()); + + scene + .ucmd() + .current_dir(&src_dir) + .arg(src_dir.join("test.sh")) + .arg(dst_dir.join("test.sh")) + .succeeds() + .no_stderr() + .no_stdout(); +} + +#[cfg(all(unix, feature = "chmod"))] +#[test] +fn test_install_on_invalid_link_at_destination_and_dev_null_at_source() { + let scene = TestScenario::new(util_name!()); + + let at = &scene.fixtures; + at.mkdir("src"); + at.mkdir("dest"); + let src_dir = at.plus("src"); + let dst_dir = at.plus("dest"); + + at.touch("test.sh"); + at.symlink_file( + "/opt/FakeDestination", + &dst_dir.join("test.sh").to_string_lossy(), + ); + scene.ccmd("chmod").arg("+x").arg("test.sh").succeeds(); + at.symlink_file("test.sh", &src_dir.join("test.sh").to_string_lossy()); + + scene + .ucmd() + .current_dir(&src_dir) + .arg("/dev/null") + .arg(dst_dir.join("test.sh")) + .succeeds() + .no_stderr() + .no_stdout(); +} + #[test] #[cfg(not(windows))] fn test_install_and_strip_with_invalid_program() { diff --git a/tests/fixtures/install/helloworld_freebsd b/tests/fixtures/install/helloworld_freebsd new file mode 100755 index 000000000..bfd788630 Binary files /dev/null and b/tests/fixtures/install/helloworld_freebsd differ