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
1 change: 1 addition & 0 deletions src/uu/chmod/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ chmod-error-preserve-root = it is dangerous to operate recursively on {$file}
chmod: use --no-preserve-root to override this failsafe
chmod-error-permission-denied = cannot access {$file}: Permission denied
chmod-error-new-permissions = {$file}: new permissions are {$actual}, not {$expected}
chmod-error-changing-permissions = changing permissions of {$file}: {$err}
chmod-error-missing-operand = missing operand

# Help messages
Expand Down
1 change: 1 addition & 0 deletions src/uu/chmod/locales/fr-FR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ chmod-error-preserve-root = il est dangereux d'opérer récursivement sur {$file
chmod: utiliser --no-preserve-root pour outrepasser cette protection
chmod-error-permission-denied = impossible d'accéder à {$file} : Permission refusée
chmod-error-new-permissions = {$file} : les nouvelles permissions sont {$actual}, pas {$expected}
chmod-error-changing-permissions = changement des permissions de {$file} : {$err}
chmod-error-missing-operand = opérande manquant

# Messages verbeux/de statut
Expand Down
17 changes: 10 additions & 7 deletions src/uu/chmod/src/chmod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ use std::os::unix::fs::{MetadataExt, PermissionsExt};
use std::path::{Path, PathBuf};
use thiserror::Error;
use uucore::display::Quotable;
use uucore::error::{ExitCode, UError, UResult, USimpleError, UUsageError, set_exit_code};
use uucore::error::{
ExitCode, UError, UResult, USimpleError, UUsageError, set_exit_code, strip_errno,
};
use uucore::fs::{FileInformation, display_permissions_unix};
use uucore::mode;
use uucore::perms::{TraverseSymlinks, configure_symlink_and_recursion};
Expand All @@ -38,6 +40,8 @@ enum ChmodError {
PermissionDenied(PathBuf),
#[error("{}", translate!("chmod-error-new-permissions", "file" => _0.maybe_quote(), "actual" => _1.clone(), "expected" => _2.clone()))]
NewPermissions(PathBuf, String, String),
#[error("{}", translate!("chmod-error-changing-permissions", "file" => _0.quote(), "err" => strip_errno(_1)))]
ChangingPermissions(PathBuf, std::io::Error),
}

impl UError for ChmodError {}
Expand Down Expand Up @@ -772,13 +776,12 @@ impl Chmoder {
}

fn change_file(&self, fperm: u32, mode: u32, file: &Path) -> Result<(), i32> {
if fperm == mode {
// Use the helper method for consistent reporting
self.report_permission_change(file, fperm, mode);
Ok(())
} else if let Err(err) = fs::set_permissions(file, fs::Permissions::from_mode(mode)) {
// Always issue the chmod(2) call, even when the bits are unchanged: the
// syscall can still fail (e.g. lacking permission on the file) and that
// failure must be reported, matching GNU.
if let Err(err) = fs::set_permissions(file, fs::Permissions::from_mode(mode)) {
if !self.quiet {
show_error!("{err}");
show_error!("{}", ChmodError::ChangingPermissions(file.into(), err));
}
if self.verbose {
println!(
Expand Down
27 changes: 26 additions & 1 deletion tests/by-util/test_chmod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// spell-checker:ignore (words) dirfd subdirs openat FDCWD rwxr

use std::fs::{OpenOptions, Permissions, metadata, set_permissions};
use std::os::unix::fs::{OpenOptionsExt, PermissionsExt};
use std::os::unix::fs::{MetadataExt, OpenOptionsExt, PermissionsExt};
use uutests::at_and_ucmd;
use uutests::util::{AtPath, TestScenario, UCommand};

Expand Down Expand Up @@ -1409,6 +1409,31 @@ fn test_chmod_recursive_uses_dirfd_for_subdirs() {
);
}

#[test]
fn test_chmod_operator_only_still_calls_syscall() {
use uucore::process::geteuid;

// An operator with no permission letters ('+', '-', '=') leaves the mode
// bits unchanged, yet chmod must still issue the chmod(2) call so that a
// lack of permission is reported instead of silently succeeding. As a
// non-root user, '/' (owned by root) is a file we cannot chmod.
if geteuid() == 0 {
return;
}
if metadata("/").map_or(0, |m| m.uid()) != 0 {
return; // '/' is not root-owned in this environment
}

for op in ["+", "-", "="] {
new_ucmd!()
.arg(op)
.arg("/")
.fails()
.code_is(1)
.stderr_contains("changing permissions of '/'");
}
}

#[test]
fn test_chmod_colored_output() {
// Test colored help message
Expand Down
Loading