mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-27 11:07:44 +00:00
Add symbolic mode support to mkdir
This commit is contained in:
parent
20becf8166
commit
0b2483452a
5 changed files with 136 additions and 42 deletions
|
@ -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<String>) -> 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,
|
||||
|
|
|
@ -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<u32, String> {
|
||||
Ok(DEFAULT_PERM)
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn get_mode(matches: &ArgMatches, mode_had_minus_prefix: bool) -> Result<u32, String> {
|
||||
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<String>) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn strip_minus_from_mode(args: &mut Vec<String>) -> 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(())
|
||||
}
|
||||
|
|
|
@ -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<String>) -> 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 {
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue