diff --git a/src/uu/chmod/src/chmod.rs b/src/uu/chmod/src/chmod.rs index e25202fbe..68c55b4cb 100644 --- a/src/uu/chmod/src/chmod.rs +++ b/src/uu/chmod/src/chmod.rs @@ -57,7 +57,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { // Before we can parse 'args' with clap (and previously getopts), // a possible MODE prefix '-' needs to be removed (e.g. "chmod -x FILE"). - let mode_had_minus_prefix = strip_minus_from_mode(&mut args); + let mode_had_minus_prefix = mode::strip_minus_from_mode(&mut args); let usage = usage(); let after_help = get_long_usage(); @@ -180,30 +180,6 @@ pub fn uu_app() -> App<'static, 'static> { ) } -// Iterate 'args' and delete the first occurrence -// of a prefix '-' if it's associated with MODE -// e.g. "chmod -v -xw -R FILE" -> "chmod -v xw -R FILE" -pub fn strip_minus_from_mode(args: &mut Vec) -> bool { - for arg in args { - if arg == "--" { - break; - } - if arg.starts_with('-') { - if let Some(second) = arg.chars().nth(1) { - match second { - 'r' | 'w' | 'x' | 'X' | 's' | 't' | 'u' | 'g' | 'o' | '0'..='7' => { - // TODO: use strip_prefix() once minimum rust version reaches 1.45.0 - *arg = arg[1..arg.len()].to_string(); - return true; - } - _ => {} - } - } - } - } - false -} - struct Chmoder { changes: bool, quiet: bool, diff --git a/src/uu/mkdir/src/mkdir.rs b/src/uu/mkdir/src/mkdir.rs index 92c068408..9ff816dcb 100644 --- a/src/uu/mkdir/src/mkdir.rs +++ b/src/uu/mkdir/src/mkdir.rs @@ -5,15 +5,22 @@ // * For the full copyright and license information, please view the LICENSE // * file that was distributed with this source code. +// spell-checker:ignore (ToDO) ugoa cmode + #[macro_use] extern crate uucore; use clap::OsValues; -use clap::{crate_version, App, Arg}; +use clap::{crate_version, App, Arg, ArgMatches}; use std::fs; use std::path::Path; use uucore::display::Quotable; use uucore::error::{FromIo, UResult, USimpleError}; +#[cfg(not(windows))] +use uucore::mode; +use uucore::InvalidEncodingHandling; + +static DEFAULT_PERM: u32 = 0o755; static ABOUT: &str = "Create the given DIRECTORY(ies) if they do not exist"; mod options { @@ -26,29 +33,81 @@ mod options { fn usage() -> String { format!("{0} [OPTION]... [USER]", uucore::execution_phrase()) } +fn get_long_usage() -> String { + String::from("Each MODE is of the form '[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=]?[0-7]+'.") +} + +#[cfg(windows)] +fn get_mode(_matches: &ArgMatches, _mode_had_minus_prefix: bool) -> Result { + Ok(DEFAULT_PERM) +} + +#[cfg(not(windows))] +fn get_mode(matches: &ArgMatches, mode_had_minus_prefix: bool) -> Result { + let digits: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; + // Translate a ~str in octal form to u16, default to 755 + // Not tested on Windows + let mut new_mode = DEFAULT_PERM; + match matches.value_of(options::MODE) { + Some(m) => { + for mode in m.split(',') { + if mode.contains(digits) { + new_mode = mode::parse_numeric(new_mode, m, true)?; + } else { + let cmode = if mode_had_minus_prefix { + // clap parsing is finished, now put prefix back + format!("-{}", mode) + } else { + mode.to_string() + }; + new_mode = mode::parse_symbolic(new_mode, &cmode, mode::get_umask(), true)?; + } + } + Ok(new_mode) + } + None => Ok(DEFAULT_PERM), + } +} + +#[cfg(windows)] +fn strip_minus_from_mode(_args: &mut Vec) -> bool { + false +} + +#[cfg(not(windows))] +fn strip_minus_from_mode(args: &mut Vec) -> bool { + mode::strip_minus_from_mode(args) +} #[uucore_procs::gen_uumain] pub fn uumain(args: impl uucore::Args) -> UResult<()> { + let mut args = args + .collect_str(InvalidEncodingHandling::ConvertLossy) + .accept_any(); + + // Before we can parse 'args' with clap (and previously getopts), + // a possible MODE prefix '-' needs to be removed (e.g. "chmod -x FILE"). + let mode_had_minus_prefix = strip_minus_from_mode(&mut args); + let usage = usage(); + let after_help = get_long_usage(); // Linux-specific options, not implemented // opts.optflag("Z", "context", "set SELinux security context" + // " of each created directory to CTX"), - let matches = uu_app().usage(&usage[..]).get_matches_from(args); + let matches = uu_app() + .usage(&usage[..]) + .after_help(&after_help[..]) + .get_matches_from(args); let dirs = matches.values_of_os(options::DIRS).unwrap_or_default(); let verbose = matches.is_present(options::VERBOSE); let recursive = matches.is_present(options::PARENTS); - // Translate a ~str in octal form to u16, default to 755 - // Not tested on Windows - let mode: u16 = match matches.value_of(options::MODE) { - Some(m) => u16::from_str_radix(m, 8) - .map_err(|_| USimpleError::new(1, format!("invalid mode {}", m.quote())))?, - None => 0o755_u16, - }; - - exec(dirs, recursive, mode, verbose) + match get_mode(&matches, mode_had_minus_prefix) { + Ok(mode) => exec(dirs, recursive, mode, verbose), + Err(f) => Err(USimpleError::new(1, f)), + } } pub fn uu_app() -> App<'static, 'static> { @@ -86,7 +145,7 @@ pub fn uu_app() -> App<'static, 'static> { /** * Create the list of new directories */ -fn exec(dirs: OsValues, recursive: bool, mode: u16, verbose: bool) -> UResult<()> { +fn exec(dirs: OsValues, recursive: bool, mode: u32, verbose: bool) -> UResult<()> { for dir in dirs { let path = Path::new(dir); show_if_err!(mkdir(path, recursive, mode, verbose)); @@ -94,7 +153,7 @@ fn exec(dirs: OsValues, recursive: bool, mode: u16, verbose: bool) -> UResult<() Ok(()) } -fn mkdir(path: &Path, recursive: bool, mode: u16, verbose: bool) -> UResult<()> { +fn mkdir(path: &Path, recursive: bool, mode: u32, verbose: bool) -> UResult<()> { let create_dir = if recursive { fs::create_dir_all } else { @@ -115,18 +174,18 @@ fn mkdir(path: &Path, recursive: bool, mode: u16, verbose: bool) -> UResult<()> } #[cfg(any(unix, target_os = "redox"))] -fn chmod(path: &Path, mode: u16) -> UResult<()> { +fn chmod(path: &Path, mode: u32) -> UResult<()> { use std::fs::{set_permissions, Permissions}; use std::os::unix::fs::PermissionsExt; - let mode = Permissions::from_mode(u32::from(mode)); + let mode = Permissions::from_mode(mode); set_permissions(path, mode) .map_err_context(|| format!("cannot set permissions {}", path.quote())) } #[cfg(windows)] -fn chmod(_path: &Path, _mode: u16) -> UResult<()> { +fn chmod(_path: &Path, _mode: u32) -> UResult<()> { // chmod on Windows only sets the readonly flag, which isn't even honored on directories Ok(()) } diff --git a/src/uucore/src/lib/features/mode.rs b/src/uucore/src/lib/features/mode.rs index 72083e799..5e299f988 100644 --- a/src/uucore/src/lib/features/mode.rs +++ b/src/uucore/src/lib/features/mode.rs @@ -155,6 +155,30 @@ pub fn get_umask() -> u32 { mask as u32 } +// Iterate 'args' and delete the first occurrence +// of a prefix '-' if it's associated with MODE +// e.g. "chmod -v -xw -R FILE" -> "chmod -v xw -R FILE" +pub fn strip_minus_from_mode(args: &mut Vec) -> bool { + for arg in args { + if arg == "--" { + break; + } + if arg.starts_with('-') { + if let Some(second) = arg.chars().nth(1) { + match second { + 'r' | 'w' | 'x' | 'X' | 's' | 't' | 'u' | 'g' | 'o' | '0'..='7' => { + // TODO: use strip_prefix() once minimum rust version reaches 1.45.0 + *arg = arg[1..arg.len()].to_string(); + return true; + } + _ => {} + } + } + } + } + false +} + #[cfg(test)] mod test { diff --git a/tests/by-util/test_chmod.rs b/tests/by-util/test_chmod.rs index 1b8983bc3..c8348d491 100644 --- a/tests/by-util/test_chmod.rs +++ b/tests/by-util/test_chmod.rs @@ -4,7 +4,7 @@ use std::os::unix::fs::{OpenOptionsExt, PermissionsExt}; use std::sync::Mutex; extern crate libc; -use self::chmod::strip_minus_from_mode; +use uucore::mode::strip_minus_from_mode; extern crate chmod; use self::libc::umask; diff --git a/tests/by-util/test_mkdir.rs b/tests/by-util/test_mkdir.rs index 54a6fe3c8..f3451fb5e 100644 --- a/tests/by-util/test_mkdir.rs +++ b/tests/by-util/test_mkdir.rs @@ -1,4 +1,6 @@ use crate::common::util::*; +#[cfg(not(windows))] +use std::os::unix::fs::PermissionsExt; static TEST_DIR1: &str = "mkdir_test1"; static TEST_DIR2: &str = "mkdir_test2"; @@ -65,3 +67,36 @@ fn test_mkdir_dup_file() { // mkdir should fail for a file even if -p is specified. scene.ucmd().arg("-p").arg(TEST_FILE7).fails(); } + +#[test] +#[cfg(not(windows))] +fn test_symbolic_mode() { + let (at, mut ucmd) = at_and_ucmd!(); + + ucmd.arg("-m").arg("a=rwx").arg(TEST_DIR1).succeeds(); + let perms = at.metadata(TEST_DIR1).permissions().mode(); + assert_eq!(perms, 0o40777) +} + +#[test] +#[cfg(not(windows))] +fn test_symbolic_alteration() { + let (at, mut ucmd) = at_and_ucmd!(); + + ucmd.arg("-m").arg("-w").arg(TEST_DIR1).succeeds(); + let perms = at.metadata(TEST_DIR1).permissions().mode(); + assert_eq!(perms, 0o40555) +} + +#[test] +#[cfg(not(windows))] +fn test_multi_symbolic() { + let (at, mut ucmd) = at_and_ucmd!(); + + ucmd.arg("-m") + .arg("u=rwx,g=rx,o=") + .arg(TEST_DIR1) + .succeeds(); + let perms = at.metadata(TEST_DIR1).permissions().mode(); + assert_eq!(perms, 0o40750) +}