mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-27 19:17:43 +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),
|
// Before we can parse 'args' with clap (and previously getopts),
|
||||||
// a possible MODE prefix '-' needs to be removed (e.g. "chmod -x FILE").
|
// 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 usage = usage();
|
||||||
let after_help = get_long_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 {
|
struct Chmoder {
|
||||||
changes: bool,
|
changes: bool,
|
||||||
quiet: bool,
|
quiet: bool,
|
||||||
|
|
|
@ -5,15 +5,22 @@
|
||||||
// * For the full copyright and license information, please view the LICENSE
|
// * For the full copyright and license information, please view the LICENSE
|
||||||
// * file that was distributed with this source code.
|
// * file that was distributed with this source code.
|
||||||
|
|
||||||
|
// spell-checker:ignore (ToDO) ugoa cmode
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
|
|
||||||
use clap::OsValues;
|
use clap::OsValues;
|
||||||
use clap::{crate_version, App, Arg};
|
use clap::{crate_version, App, Arg, ArgMatches};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::{FromIo, UResult, USimpleError};
|
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";
|
static ABOUT: &str = "Create the given DIRECTORY(ies) if they do not exist";
|
||||||
mod options {
|
mod options {
|
||||||
|
@ -26,29 +33,81 @@ mod options {
|
||||||
fn usage() -> String {
|
fn usage() -> String {
|
||||||
format!("{0} [OPTION]... [USER]", uucore::execution_phrase())
|
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]
|
#[uucore_procs::gen_uumain]
|
||||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
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 usage = usage();
|
||||||
|
let after_help = get_long_usage();
|
||||||
|
|
||||||
// Linux-specific options, not implemented
|
// Linux-specific options, not implemented
|
||||||
// opts.optflag("Z", "context", "set SELinux security context" +
|
// opts.optflag("Z", "context", "set SELinux security context" +
|
||||||
// " of each created directory to CTX"),
|
// " 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 dirs = matches.values_of_os(options::DIRS).unwrap_or_default();
|
||||||
let verbose = matches.is_present(options::VERBOSE);
|
let verbose = matches.is_present(options::VERBOSE);
|
||||||
let recursive = matches.is_present(options::PARENTS);
|
let recursive = matches.is_present(options::PARENTS);
|
||||||
|
|
||||||
// Translate a ~str in octal form to u16, default to 755
|
match get_mode(&matches, mode_had_minus_prefix) {
|
||||||
// Not tested on Windows
|
Ok(mode) => exec(dirs, recursive, mode, verbose),
|
||||||
let mode: u16 = match matches.value_of(options::MODE) {
|
Err(f) => Err(USimpleError::new(1, f)),
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uu_app() -> App<'static, 'static> {
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
@ -86,7 +145,7 @@ pub fn uu_app() -> App<'static, 'static> {
|
||||||
/**
|
/**
|
||||||
* Create the list of new directories
|
* 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 {
|
for dir in dirs {
|
||||||
let path = Path::new(dir);
|
let path = Path::new(dir);
|
||||||
show_if_err!(mkdir(path, recursive, mode, verbose));
|
show_if_err!(mkdir(path, recursive, mode, verbose));
|
||||||
|
@ -94,7 +153,7 @@ fn exec(dirs: OsValues, recursive: bool, mode: u16, verbose: bool) -> UResult<()
|
||||||
Ok(())
|
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 {
|
let create_dir = if recursive {
|
||||||
fs::create_dir_all
|
fs::create_dir_all
|
||||||
} else {
|
} else {
|
||||||
|
@ -115,18 +174,18 @@ fn mkdir(path: &Path, recursive: bool, mode: u16, verbose: bool) -> UResult<()>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(unix, target_os = "redox"))]
|
#[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::fs::{set_permissions, Permissions};
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
let mode = Permissions::from_mode(u32::from(mode));
|
let mode = Permissions::from_mode(mode);
|
||||||
|
|
||||||
set_permissions(path, mode)
|
set_permissions(path, mode)
|
||||||
.map_err_context(|| format!("cannot set permissions {}", path.quote()))
|
.map_err_context(|| format!("cannot set permissions {}", path.quote()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[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
|
// chmod on Windows only sets the readonly flag, which isn't even honored on directories
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,6 +155,30 @@ pub fn get_umask() -> u32 {
|
||||||
mask as 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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::os::unix::fs::{OpenOptionsExt, PermissionsExt};
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
use self::chmod::strip_minus_from_mode;
|
use uucore::mode::strip_minus_from_mode;
|
||||||
extern crate chmod;
|
extern crate chmod;
|
||||||
use self::libc::umask;
|
use self::libc::umask;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
use crate::common::util::*;
|
use crate::common::util::*;
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
static TEST_DIR1: &str = "mkdir_test1";
|
static TEST_DIR1: &str = "mkdir_test1";
|
||||||
static TEST_DIR2: &str = "mkdir_test2";
|
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.
|
// mkdir should fail for a file even if -p is specified.
|
||||||
scene.ucmd().arg("-p").arg(TEST_FILE7).fails();
|
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