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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/uu/chmod/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ path = "src/chmod.rs"
[dependencies]
clap = { workspace = true }
libc = { workspace = true }
thiserror = { workspace = true }
uucore = { workspace = true, features = ["entries", "fs", "mode", "perms"] }

[[bin]]
Expand Down
25 changes: 25 additions & 0 deletions src/uu/chmod/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,28 @@ chmod-usage = chmod [OPTION]... MODE[,MODE]... FILE...
chmod [OPTION]... OCTAL-MODE FILE...
chmod [OPTION]... --reference=RFILE FILE...
chmod-after-help = Each MODE is of the form [ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=]?[0-7]+.
chmod-error-cannot-stat = cannot stat attributes of {$file}
chmod-error-dangling-symlink = cannot operate on dangling symlink {$file}
chmod-error-no-such-file = cannot access {$file}: No such file or directory
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 = {$file}: Permission denied
chmod-error-new-permissions = {$file}: new permissions are {$actual}, not {$expected}
chmod-error-missing-operand = missing operand

# Help messages
chmod-help-print-help = Print help information.
chmod-help-changes = like verbose but report only when a change is made
chmod-help-quiet = suppress most error messages
chmod-help-verbose = output a diagnostic for every file processed
chmod-help-no-preserve-root = do not treat '/' specially (the default)
chmod-help-preserve-root = fail to operate recursively on '/'
chmod-help-recursive = change files and directories recursively
chmod-help-reference = use RFILE's mode instead of MODE values

# Verbose messages
chmod-verbose-failed-dangling = failed to change mode of {$file} from 0000 (---------) to 1500 (r-x-----T)
chmod-verbose-neither-changed = neither symbolic link {$file} nor referent has been changed
chmod-verbose-mode-retained = mode of {$file} retained as {$mode_octal} ({$mode_display})
chmod-verbose-failed-change = failed to change mode of file {$file} from {$old_mode} ({$old_mode_display}) to {$new_mode} ({$new_mode_display})
chmod-verbose-mode-changed = mode of {$file} changed from {$old_mode} ({$old_mode_display}) to {$new_mode} ({$new_mode_display})
33 changes: 33 additions & 0 deletions src/uu/chmod/locales/fr-FR.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
chmod-about = Changer le mode de chaque FICHIER vers MODE.
Avec --reference, changer le mode de chaque FICHIER vers celui de RFICHIER.
chmod-usage = chmod [OPTION]... MODE[,MODE]... FICHIER...
chmod [OPTION]... MODE-OCTAL FICHIER...
chmod [OPTION]... --reference=RFICHIER FICHIER...
chmod-after-help = Chaque MODE est de la forme [ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=]?[0-7]+.

# Messages d'aide
chmod-help-print-help = Afficher les informations d'aide.
chmod-help-changes = comme verbeux mais rapporter seulement lors d'un changement
chmod-help-quiet = supprimer la plupart des messages d'erreur
chmod-help-verbose = afficher un diagnostic pour chaque fichier traité
chmod-help-no-preserve-root = ne pas traiter '/' spécialement (par défaut)
chmod-help-preserve-root = échouer à opérer récursivement sur '/'
chmod-help-recursive = changer les fichiers et répertoires récursivement
chmod-help-reference = utiliser le mode de RFICHIER au lieu des valeurs de MODE

# Messages d'erreur
chmod-error-cannot-stat = impossible d'obtenir les attributs de {$file}
chmod-error-dangling-symlink = impossible d'opérer sur le lien symbolique pendouillant {$file}
chmod-error-no-such-file = impossible d'accéder à {$file} : Aucun fichier ou dossier de ce type
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 = {$file} : Permission refusée
chmod-error-new-permissions = {$file} : les nouvelles permissions sont {$actual}, pas {$expected}
chmod-error-missing-operand = opérande manquant

# Messages verbeux/de statut
chmod-verbose-failed-dangling = échec du changement de mode de {$file} de 0000 (---------) vers 1500 (r-x-----T)
chmod-verbose-neither-changed = ni le lien symbolique {$file} ni la référence n'ont été changés
chmod-verbose-mode-retained = mode de {$file} conservé comme {$mode_octal} ({$mode_display})
chmod-verbose-failed-change = échec du changement de mode du fichier {$file} de {$old_mode} ({$old_mode_display}) vers {$new_mode} ({$new_mode_display})
chmod-verbose-mode-changed = mode de {$file} changé de {$old_mode} ({$old_mode_display}) vers {$new_mode} ({$new_mode_display})
102 changes: 52 additions & 50 deletions src/uu/chmod/src/chmod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,40 @@
// spell-checker:ignore (ToDO) Chmoder cmode fmode fperm fref ugoa RFILE RFILE's

use clap::{Arg, ArgAction, Command};
use std::collections::HashMap;
use std::ffi::OsString;
use std::fs;
use std::os::unix::fs::{MetadataExt, PermissionsExt};
use std::path::Path;
use thiserror::Error;
use uucore::display::Quotable;
use uucore::error::{ExitCode, UResult, USimpleError, UUsageError, set_exit_code};
use uucore::error::{ExitCode, UError, UResult, USimpleError, UUsageError, set_exit_code};
use uucore::fs::display_permissions_unix;
use uucore::libc::mode_t;
#[cfg(not(windows))]
use uucore::mode;
use uucore::perms::{TraverseSymlinks, configure_symlink_and_recursion};
use uucore::{format_usage, show, show_error};

use uucore::locale::get_message;
use uucore::locale::{get_message, get_message_with_args};

#[derive(Debug, Error)]
enum ChmodError {
#[error("{}", get_message_with_args("chmod-error-cannot-stat", HashMap::from([("file".to_string(), _0.quote().to_string())])))]
CannotStat(String),
#[error("{}", get_message_with_args("chmod-error-dangling-symlink", HashMap::from([("file".to_string(), _0.quote().to_string())])))]
DanglingSymlink(String),
#[error("{}", get_message_with_args("chmod-error-no-such-file", HashMap::from([("file".to_string(), _0.quote().to_string())])))]
NoSuchFile(String),
#[error("{}", get_message_with_args("chmod-error-preserve-root", HashMap::from([("file".to_string(), _0.quote().to_string())])))]
PreserveRoot(String),
#[error("{}", get_message_with_args("chmod-error-permission-denied", HashMap::from([("file".to_string(), _0.quote().to_string())])))]
PermissionDenied(String),
#[error("{}", get_message_with_args("chmod-error-new-permissions", HashMap::from([("file".to_string(), _0.clone()), ("actual".to_string(), _1.clone()), ("expected".to_string(), _2.clone())])))]
NewPermissions(String, String, String),
}

impl UError for ChmodError {}

mod options {
pub const HELP: &str = "help";
Expand Down Expand Up @@ -103,11 +123,8 @@
let fmode = match matches.get_one::<String>(options::REFERENCE) {
Some(fref) => match fs::metadata(fref) {
Ok(meta) => Some(meta.mode() & 0o7777),
Err(err) => {
return Err(USimpleError::new(
1,
format!("cannot stat attributes of {}: {err}", fref.quote()),
));
Err(_) => {
return Err(ChmodError::CannotStat(fref.to_string()).into());

Check warning on line 127 in src/uu/chmod/src/chmod.rs

View check run for this annotation

Codecov / codecov/patch

src/uu/chmod/src/chmod.rs#L127

Added line #L127 was not covered by tests
}
},
None => None,
Expand Down Expand Up @@ -135,7 +152,10 @@
};

if files.is_empty() {
return Err(UUsageError::new(1, "missing operand".to_string()));
return Err(UUsageError::new(
1,
get_message("chmod-error-missing-operand"),
));
}

let (recursive, dereference, traverse_symlinks) =
Expand Down Expand Up @@ -168,55 +188,55 @@
.arg(
Arg::new(options::HELP)
.long(options::HELP)
.help("Print help information.")
.help(get_message("chmod-help-print-help"))
.action(ArgAction::Help),
)
.arg(
Arg::new(options::CHANGES)
.long(options::CHANGES)
.short('c')
.help("like verbose but report only when a change is made")
.help(get_message("chmod-help-changes"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::QUIET)
.long(options::QUIET)
.visible_alias("silent")
.short('f')
.help("suppress most error messages")
.help(get_message("chmod-help-quiet"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::VERBOSE)
.long(options::VERBOSE)
.short('v')
.help("output a diagnostic for every file processed")
.help(get_message("chmod-help-verbose"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::NO_PRESERVE_ROOT)
.long(options::NO_PRESERVE_ROOT)
.help("do not treat '/' specially (the default)")
.help(get_message("chmod-help-no-preserve-root"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::PRESERVE_ROOT)
.long(options::PRESERVE_ROOT)
.help("fail to operate recursively on '/'")
.help(get_message("chmod-help-preserve-root"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::RECURSIVE)
.long(options::RECURSIVE)
.short('R')
.help("change files and directories recursively")
.help(get_message("chmod-help-recursive"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::REFERENCE)
.long("reference")
.value_hint(clap::ValueHint::FilePath)
.help("use RFILE's mode instead of MODE values"),
.help(get_message("chmod-help-reference")),
)
.arg(
Arg::new(options::MODE).required_unless_present(options::REFERENCE),
Expand Down Expand Up @@ -265,27 +285,21 @@
}

if !self.quiet {
show!(USimpleError::new(
1,
format!("cannot operate on dangling symlink {}", filename.quote()),
));
show!(ChmodError::DanglingSymlink(filename.to_string()));
set_exit_code(1);
}

if self.verbose {
println!(
"failed to change mode of {} from 0000 (---------) to 1500 (r-x-----T)",
filename.quote()
"{}",
get_message_with_args(
"chmod-verbose-failed-dangling",
HashMap::from([("file".to_string(), filename.quote().to_string())])
)
);
}
} else if !self.quiet {
show!(USimpleError::new(
1,
format!(
"cannot access {}: No such file or directory",
filename.quote()
)
));
show!(ChmodError::NoSuchFile(filename.to_string()));
}
// GNU exits with exit code 1 even if -q or --quiet are passed
// So we set the exit code, because it hasn't been set yet if `self.quiet` is true.
Expand All @@ -298,13 +312,7 @@
continue;
}
if self.recursive && self.preserve_root && filename == "/" {
return Err(USimpleError::new(
1,
format!(
"it is dangerous to operate recursively on {}\nchmod: use --no-preserve-root to override this failsafe",
filename.quote()
),
));
return Err(ChmodError::PreserveRoot(filename.to_string()).into());
}
if self.recursive {
r = self.walk_dir(file);
Expand Down Expand Up @@ -368,12 +376,9 @@
} else if err.kind() == std::io::ErrorKind::PermissionDenied {
// These two filenames would normally be conditionally
// quoted, but GNU's tests expect them to always be quoted
Err(USimpleError::new(
1,
format!("{}: Permission denied", file.quote()),
))
Err(ChmodError::PermissionDenied(file.to_string_lossy().to_string()).into())
} else {
Err(USimpleError::new(1, format!("{}: {err}", file.quote())))
Err(ChmodError::CannotStat(file.to_string_lossy().to_string()).into())

Check warning on line 381 in src/uu/chmod/src/chmod.rs

View check run for this annotation

Codecov / codecov/patch

src/uu/chmod/src/chmod.rs#L381

Added line #L381 was not covered by tests
};
}
};
Expand Down Expand Up @@ -420,15 +425,12 @@
self.change_file(fperm, new_mode, file)?;
// if a permission would have been removed if umask was 0, but it wasn't because umask was not 0, print an error and fail
if (new_mode & !naively_expected_new_mode) != 0 {
return Err(USimpleError::new(
1,
format!(
"{}: new permissions are {}, not {}",
file.maybe_quote(),
display_permissions_unix(new_mode as mode_t, false),
display_permissions_unix(naively_expected_new_mode as mode_t, false)
),
));
return Err(ChmodError::NewPermissions(
file.to_string_lossy().to_string(),
display_permissions_unix(new_mode as mode_t, false),
display_permissions_unix(naively_expected_new_mode as mode_t, false),
)
.into());
}
}
}
Expand Down
Loading