Skip to content

Commit a9b9a50

Browse files
committed
remove symlinks at target to avoid issue with invalid symlinks at target
1 parent 32c1d61 commit a9b9a50

File tree

2 files changed

+51
-3
lines changed

2 files changed

+51
-3
lines changed

src/uu/install/src/install.rs

+20-3
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ use file_diff::diff;
1212
use filetime::{set_file_times, FileTime};
1313
use std::error::Error;
1414
use std::fmt::{Debug, Display};
15-
use std::fs;
1615
use std::fs::File;
1716
use std::os::unix::fs::MetadataExt;
1817
#[cfg(unix)]
1918
use std::os::unix::prelude::OsStrExt;
2019
use std::path::{Path, PathBuf, MAIN_SEPARATOR};
2120
use std::process;
21+
use std::{fs, io};
2222
use uucore::backup_control::{self, BackupMode};
2323
use uucore::display::Quotable;
2424
use uucore::entries::{grp2gid, usr2uid};
@@ -736,6 +736,18 @@ fn perform_backup(to: &Path, b: &Behavior) -> UResult<Option<PathBuf>> {
736736
}
737737
}
738738

739+
fn copy_file_source_not_dev_null(from: &Path, to: &Path) -> io::Result<()> {
740+
let dest_meta_res = fs::symlink_metadata(to);
741+
if let Ok(dest_meta) = dest_meta_res {
742+
if dest_meta.is_symlink() {
743+
// fs::copy fails if destination is a invalid symlink
744+
// so lets just remove all symlinks at destination before copy
745+
fs::remove_file(to)?;
746+
}
747+
}
748+
fs::copy(from, to).map(|_| ())
749+
}
750+
739751
/// Copy a file from one path to another.
740752
///
741753
/// # Parameters
@@ -757,8 +769,13 @@ fn copy_file(from: &Path, to: &Path) -> UResult<()> {
757769
InstallError::InstallFailed(from.to_path_buf(), to.to_path_buf(), err).into(),
758770
);
759771
}
760-
} else if let Err(err) = fs::copy(from, to) {
761-
return Err(InstallError::InstallFailed(from.to_path_buf(), to.to_path_buf(), err).into());
772+
} else {
773+
let result = copy_file_source_not_dev_null(from, to);
774+
if let Err(err) = result {
775+
return Err(
776+
InstallError::InstallFailed(from.to_path_buf(), to.to_path_buf(), err).into(),
777+
);
778+
}
762779
}
763780
Ok(())
764781
}

tests/by-util/test_install.rs

+31
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,37 @@ fn test_install_and_strip_with_program_hyphen() {
715715
.stdout_is("./-dest\n");
716716
}
717717

718+
#[cfg(all(unix, feature = "chmod"))]
719+
#[test]
720+
// FixME: Freebsd fails on 'No such file or directory'
721+
#[cfg(not(target_os = "freebsd"))]
722+
fn test_install_on_invalid_link_at_destination() {
723+
let scene = TestScenario::new(util_name!());
724+
725+
let at = &scene.fixtures;
726+
at.mkdir("src");
727+
at.mkdir("dest");
728+
let src_dir = at.plus("src");
729+
let dst_dir = at.plus("dest");
730+
731+
at.touch("test.sh");
732+
at.symlink_file(
733+
"/opt/FakeDestination",
734+
&dst_dir.join("test.sh").to_string_lossy(),
735+
);
736+
scene.ccmd("chmod").arg("+x").arg("test.sh").succeeds();
737+
at.symlink_file("test.sh", &src_dir.join("test.sh").to_string_lossy());
738+
739+
scene
740+
.ucmd()
741+
.current_dir(&src_dir)
742+
.arg(src_dir.join("test.sh"))
743+
.arg(dst_dir.join("test.sh"))
744+
.succeeds()
745+
.no_stderr()
746+
.no_stdout();
747+
}
748+
718749
#[test]
719750
#[cfg(not(windows))]
720751
fn test_install_and_strip_with_invalid_program() {

0 commit comments

Comments
 (0)