Skip to content
Merged
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: 3 additions & 0 deletions src/uu/install/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ 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 }

# Warning messages
install-warning-compare-ignored = the --compare (-C) option is ignored when you specify a mode with non-permission bits

# Verbose output
install-verbose-creating-directory = creating directory { $path }
install-verbose-creating-directory-step = install: creating directory { $path }
Expand Down
3 changes: 3 additions & 0 deletions src/uu/install/locales/fr-FR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ 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 }

# 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

# Sortie détaillée
install-verbose-creating-directory = création du répertoire { $path }
install-verbose-creating-directory-step = install : création du répertoire { $path }
Expand Down
51 changes: 43 additions & 8 deletions src/uu/install/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,15 @@
return Err(1.into());
}

// Check if compare is used with non-permission mode bits
if compare && specified_mode.is_some() {
let mode = specified_mode.unwrap();
let non_permission_bits = 0o7000; // setuid, setgid, sticky bits
if mode & non_permission_bits != 0 {
show_error!("{}", get_message("install-warning-compare-ignored"));
}
}

let owner = matches
.get_one::<String>(OPT_OWNER)
.map(|s| s.as_str())
Expand Down Expand Up @@ -1000,6 +1009,30 @@
Ok(())
}

/// Check if a file needs to be copied due to ownership differences when no explicit group is specified.
/// Returns true if the destination file's ownership would differ from what it should be after installation.
fn needs_copy_for_ownership(to: &Path, to_meta: &fs::Metadata) -> bool {
use std::os::unix::fs::MetadataExt;

// Check if the destination file's owner differs from the effective user ID
if to_meta.uid() != geteuid() {
return true;

Check warning on line 1019 in src/uu/install/src/install.rs

View check run for this annotation

Codecov / codecov/patch

src/uu/install/src/install.rs#L1019

Added line #L1019 was not covered by tests
}

// For group, we need to determine what the group would be after installation
// If no group is specified, the behavior depends on the directory:
// - If the directory has setgid bit, the file inherits the directory's group
// - Otherwise, the file gets the user's effective group
let expected_gid = to
.parent()
.and_then(|parent| metadata(parent).ok())
.filter(|parent_meta| parent_meta.mode() & 0o2000 != 0)
.map(|parent_meta| parent_meta.gid())
.unwrap_or(getegid());

to_meta.gid() != expected_gid
}

/// Return true if a file is necessary to copy. This is the case when:
///
/// - _from_ or _to_ is nonexistent;
Expand Down Expand Up @@ -1030,6 +1063,13 @@
return Ok(true);
};

// Check if the destination is a symlink (should always be replaced)
if let Ok(to_symlink_meta) = fs::symlink_metadata(to) {
if to_symlink_meta.file_type().is_symlink() {
return Ok(true);
}
}

Check warning on line 1071 in src/uu/install/src/install.rs

View check run for this annotation

Codecov / codecov/patch

src/uu/install/src/install.rs#L1071

Added line #L1071 was not covered by tests

// Define special file mode bits (setuid, setgid, sticky).
let extra_mode: u32 = 0o7000;
// Define all file mode bits (including permissions).
Expand All @@ -1038,7 +1078,7 @@

// Check if any special mode bits are set in the specified mode,
// source file mode, or destination file mode.
if b.specified_mode.unwrap_or(0) & extra_mode != 0
if b.mode() & extra_mode != 0
|| from_meta.mode() & extra_mode != 0
|| to_meta.mode() & extra_mode != 0
{
Expand Down Expand Up @@ -1079,13 +1119,8 @@
if group_id != to_meta.gid() {
return Ok(true);
}
} else {
#[cfg(not(target_os = "windows"))]
// Check if the destination file's owner or group
// differs from the effective user/group ID of the process.
if to_meta.uid() != geteuid() || to_meta.gid() != getegid() {
return Ok(true);
}
} else if needs_copy_for_ownership(to, &to_meta) {
return Ok(true);

Check warning on line 1123 in src/uu/install/src/install.rs

View check run for this annotation

Codecov / codecov/patch

src/uu/install/src/install.rs#L1123

Added line #L1123 was not covered by tests
}

// Check if the contents of the source and destination files differ.
Expand Down
Loading
Loading