Skip to content
Open
4 changes: 3 additions & 1 deletion src/uu/install/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ install-error-mutually-exclusive-compare-preserve = Options --compare and --pres
install-error-mutually-exclusive-compare-strip = Options --compare and --strip are mutually exclusive
install-error-missing-file-operand = missing file operand
install-error-missing-destination-operand = missing destination file operand after { $path }
install-error-failed-to-remove = Failed to remove existing file { $path }. Error: { $error }
install-error-failed-to-access = failed to access { $path }
install-error-failed-to-remove = cannot remove { $path }
install-error-cannot-create-file = cannot create regular file { $path }

# Warning messages
install-warning-compare-ignored = the --compare (-C) option is ignored when you specify a mode with non-permission bits
Expand Down
4 changes: 3 additions & 1 deletion src/uu/install/locales/fr-FR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ install-error-mutually-exclusive-compare-preserve = Les options --compare et --p
install-error-mutually-exclusive-compare-strip = Les options --compare et --strip sont mutuellement exclusives
install-error-missing-file-operand = opérande de fichier manquant
install-error-missing-destination-operand = opérande de fichier de destination manquant après { $path }
install-error-failed-to-remove = Échec de la suppression du fichier existant { $path }. Erreur : { $error }
install-error-failed-to-access = impossible d'accéder à { $path }
install-error-failed-to-remove = impossible de supprimer { $path }
install-error-cannot-create-file = impossible de créer le fichier { $path }

# Messages d'avertissement
install-warning-compare-ignored = l'option --compare (-C) est ignorée quand un mode est indiqué avec des bits non liés à des droits
Expand Down
18 changes: 12 additions & 6 deletions src/uu/install/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ fn standard(mut paths: Vec<OsString>, b: &Behavior) -> UResult<()> {
return copy_files_into_dir(sources, &target, b);
}

if target.is_file() || is_new_file_path(&target) {
if (target.exists() && !target.is_dir()) || is_new_file_path(&target) {
copy(source, &target, b)
} else {
Err(InstallError::InvalidTarget(target).into())
Expand Down Expand Up @@ -820,6 +820,12 @@ fn copy_file(from: &Path, to: &Path) -> UResult<()> {
}
}

if let Err(e) = to.try_exists() {
return Err(e.map_err_context(
|| translate!("install-error-failed-to-access", "path" => to.quote()),
));
}

if to.is_dir() && !from.is_dir() {
return Err(InstallError::OverrideDirectoryFailed(
to.to_path_buf().clone(),
Expand All @@ -833,10 +839,9 @@ fn copy_file(from: &Path, to: &Path) -> UResult<()> {
// appears at this path between the remove and create, it will fail safely
if let Err(e) = fs::remove_file(to) {
if e.kind() != std::io::ErrorKind::NotFound {
show_error!(
"{}",
translate!("install-error-failed-to-remove", "path" => to.quote(), "error" => format!("{e:?}"))
);
return Err(e.map_err_context(
|| translate!("install-error-failed-to-remove", "path" => to.quote()),
));
}
}

Expand All @@ -846,7 +851,8 @@ fn copy_file(from: &Path, to: &Path) -> UResult<()> {
.write(true)
.create_new(true)
.mode(0o600)
.open(to)?;
.open(to)
.map_err_context(|| translate!("install-error-cannot-create-file", "path" => to.quote()))?;

copy_stream(&mut handle, &mut dest).map_err(|err| {
InstallError::InstallFailed(from.to_path_buf(), to.to_path_buf(), err.to_string())
Expand Down
24 changes: 24 additions & 0 deletions tests/by-util/test_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,30 @@ fn test_install_ancestors_mode_directories() {
assert_eq!(0o40_200_u32, at.metadata(target_dir).permissions().mode());
}

#[test]
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The files that are being used to test do not exist on macos

#[cfg(target_os = "linux")]
fn test_install_cannot_remove_destination() {
if geteuid() == 0 {
return;
}
new_ucmd!()
.args(&["/dev/null", "/dev/full"])
.fails()
.stderr_only("install: cannot remove '/dev/full': Permission denied\n");
}

#[test]
#[cfg(target_os = "linux")]
fn test_install_cannot_create_destination() {
if geteuid() == 0 {
return;
}
new_ucmd!()
.args(&["/dev/null", "/root/file"])
.fails()
.stderr_only("install: cannot create regular file '/root/file': Permission denied\n");
}

#[test]
fn test_install_ancestors_mode_directories_with_file() {
let (at, mut ucmd) = at_and_ucmd!();
Expand Down
Loading