Skip to content

Commit 5d5b430

Browse files
committed
remove symlinks at target to avoid issue with invalid symlinks at target
1 parent 55b7b2f commit 5d5b430

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
@@ -702,6 +702,37 @@ fn test_install_and_strip_with_program_hyphen() {
702702
.no_stderr();
703703
}
704704

705+
#[cfg(all(unix, feature = "chmod"))]
706+
#[test]
707+
// FixME: Freebsd fails on 'No such file or directory'
708+
#[cfg(not(target_os = "freebsd"))]
709+
fn test_install_on_invalid_link_at_destination() {
710+
let scene = TestScenario::new(util_name!());
711+
712+
let at = &scene.fixtures;
713+
at.mkdir("src");
714+
at.mkdir("dest");
715+
let src_dir = at.plus("src");
716+
let dst_dir = at.plus("dest");
717+
718+
at.touch("test.sh");
719+
at.symlink_file(
720+
"/opt/FakeDestination",
721+
&dst_dir.join("test.sh").to_string_lossy(),
722+
);
723+
scene.ccmd("chmod").arg("+x").arg("test.sh").succeeds();
724+
at.symlink_file("test.sh", &src_dir.join("test.sh").to_string_lossy());
725+
726+
scene
727+
.ucmd()
728+
.current_dir(&src_dir)
729+
.arg(src_dir.join("test.sh"))
730+
.arg(dst_dir.join("test.sh"))
731+
.succeeds()
732+
.no_stderr()
733+
.no_stdout();
734+
}
735+
705736
#[test]
706737
#[cfg(not(windows))]
707738
fn test_install_and_strip_with_invalid_program() {

0 commit comments

Comments
 (0)