Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 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,8 @@ 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-remove = failed to remove existing file { $path }: { $error }
install-error-cannot-stat = cannot stat { $path }: { $error }

# Warning messages
install-warning-compare-ignored = the --compare (-C) option is ignored when you specify a mode with non-permission bits
Expand Down
3 changes: 2 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,8 @@ 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-remove = Échec de la suppression du fichier existant { $path }: { $error }
install-error-cannot-stat = impossible d'obtenir des informations sur le fichier. { $path }: { $error }

# 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
36 changes: 31 additions & 5 deletions src/uu/install/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@ enum InstallError {
#[error("{}", translate!("install-error-extra-operand", "operand" => .0.quote(), "usage" => .1.clone()))]
ExtraOperand(OsString, String),

#[error("{}", translate!("install-error-failed-to-remove", "path" => .0.quote(), "error" => .1.clone()))]
FailedToRemove(PathBuf, String),

#[error("{}", translate!("install-error-cannot-stat", "path" => .0.quote(), "error" => .1.clone()))]
CannotStat(PathBuf, String),

#[cfg(feature = "selinux")]
#[error("{}", .0)]
SelinuxContextFailed(String),
Expand Down Expand Up @@ -675,7 +681,19 @@ 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) {
// target.is_file does not detect special files like character/block devices
// So, in a unix environment, we need to check the file type from metadata and
// not just trust is_file().
#[cfg(unix)]
let is_file = match metadata(&target) {
Ok(meta) => !meta.file_type().is_dir(),
Err(_) => false,
};

#[cfg(not(unix))]
let is_file = target.is_file();

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

// If we don't include this check, then the `remove_file` below will fail
// and it will give an incorrect error message. However, if we check to
// see if the file exists, and it can't even be checked, this means we
// don't have permission to access the file, so we should return an error.
if let Err(to_stat) = to.try_exists() {
return Err(InstallError::CannotStat(to.to_path_buf(), to_stat.to_string()).into());
}

if to.is_dir() && !from.is_dir() {
return Err(InstallError::OverrideDirectoryFailed(
to.to_path_buf().clone(),
Expand All @@ -842,10 +868,10 @@ fn copy_file(from: &Path, to: &Path) -> UResult<()> {
// 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!(
"{}",
translate!("install-error-failed-to-remove", "path" => to.quote(), "error" => format!("{e:?}"))
);
// If we get here, then remove_file failed for some
// reason other than the file not existing. This means
// this should be a fatal error, not a warning.
return Err(InstallError::FailedToRemove(to.to_path_buf(), e.to_string()).into());
}
}

Expand Down
Loading