From f0bd170996deff926be62d39846791084ed8eae7 Mon Sep 17 00:00:00 2001 From: Alex Lyon Date: Sun, 10 Dec 2017 20:11:05 -0800 Subject: [PATCH] chmod, install: move mode parsing into uucore --- src/uucore/Cargo.toml | 3 +- src/uucore/lib.rs | 2 + src/uucore/mode.rs | 129 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 src/uucore/mode.rs diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index eba667645..236bf7860 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -17,13 +17,14 @@ fs = ["libc"] utf8 = [] encoding = ["data-encoding"] parse_time = [] +mode = ["libc"] utmpx = ["time", "libc"] process = ["libc"] signals = [] entries = ["libc"] wide = [] utsname = ["libc"] -default = ["fs", "libc", "utf8", "utsname", "encoding", "parse_time", "utmpx", "process", "entries", "signals", "wide"] +default = ["fs", "libc", "utf8", "utsname", "encoding", "parse_time", "mode", "utmpx", "process", "entries", "signals", "wide"] [lib] path = "lib.rs" diff --git a/src/uucore/lib.rs b/src/uucore/lib.rs index 3c6314d8a..2bbca4b30 100644 --- a/src/uucore/lib.rs +++ b/src/uucore/lib.rs @@ -17,6 +17,8 @@ pub mod utf8; pub mod encoding; #[cfg(feature = "parse_time")] pub mod parse_time; +#[cfg(feature = "mode")] +pub mod mode; #[cfg(all(unix, not(target_os = "fuchsia"), feature = "utmpx"))] pub mod utmpx; diff --git a/src/uucore/mode.rs b/src/uucore/mode.rs new file mode 100644 index 000000000..f8d2123d2 --- /dev/null +++ b/src/uucore/mode.rs @@ -0,0 +1,129 @@ +// This file is part of the uutils coreutils package. +// +// (c) Alex Lyon +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. +// + +use std::error::Error; + +pub fn parse_numeric(fperm: u32, mut mode: &str) -> Result { + let (op, pos) = parse_op(mode, Some('='))?; + mode = mode[pos..].trim_left_matches('0'); + if mode.len() > 4 { + Err(format!("mode is too large ({} > 7777)", mode)) + } else { + match u32::from_str_radix(mode, 8) { + Ok(change) => { + Ok(match op { + '+' => fperm | change, + '-' => fperm & !change, + '=' => change, + _ => unreachable!() + }) + } + Err(err) => Err(err.description().to_owned()) + } + } +} + +pub fn parse_symbolic(mut fperm: u32, mut mode: &str, considering_dir: bool) -> Result { + #[cfg(unix)] + use libc::umask; + + #[cfg(target_os = "redox")] + unsafe fn umask(_mask: u32) -> u32 { + // XXX Redox does not currently have umask + 0 + } + + let (mask, pos) = parse_levels(mode); + if pos == mode.len() { + return Err(format!("invalid mode ({})", mode)); + } + let respect_umask = pos == 0; + let last_umask = unsafe { + umask(0) + }; + mode = &mode[pos..]; + while mode.len() > 0 { + let (op, pos) = parse_op(mode, None)?; + mode = &mode[pos..]; + let (mut srwx, pos) = parse_change(mode, fperm, considering_dir); + if respect_umask { + srwx &= !(last_umask as u32); + } + mode = &mode[pos..]; + match op { + '+' => fperm |= srwx & mask, + '-' => fperm &= !(srwx & mask), + '=' => fperm = (fperm & !mask) | (srwx & mask), + _ => unreachable!() + } + } + unsafe { + umask(last_umask); + } + Ok(fperm) +} + +fn parse_levels(mode: &str) -> (u32, usize) { + let mut mask = 0; + let mut pos = 0; + for ch in mode.chars() { + mask |= match ch { + 'u' => 0o7700, + 'g' => 0o7070, + 'o' => 0o7007, + 'a' => 0o7777, + _ => break + }; + pos += 1; + } + if pos == 0 { + mask = 0o7777; // default to 'a' + } + (mask, pos) +} + +fn parse_op(mode: &str, default: Option) -> Result<(char, usize), String> { + match mode.chars().next() { + Some(ch) => match ch { + '+' | '-' | '=' => Ok((ch, 1)), + _ => match default { + Some(ch) => Ok((ch, 0)), + None => Err(format!("invalid operator (expected +, -, or =, but found {})", ch)) + } + }, + None => Err("unexpected end of mode".to_owned()) + } +} + +fn parse_change(mode: &str, fperm: u32, considering_dir: bool) -> (u32, usize) { + let mut srwx = fperm & 0o7000; + let mut pos = 0; + for ch in mode.chars() { + match ch { + 'r' => srwx |= 0o444, + 'w' => srwx |= 0o222, + 'x' => srwx |= 0o111, + 'X' => { + if considering_dir || (fperm & 0o0111) != 0 { + srwx |= 0o111 + } + } + 's' => srwx |= 0o4000 | 0o2000, + 't' => srwx |= 0o1000, + 'u' => srwx = (fperm & 0o700) | ((fperm >> 3) & 0o070) | ((fperm >> 6) & 0o007), + 'g' => srwx = ((fperm << 3) & 0o700) | (fperm & 0o070) | ((fperm >> 3) & 0o007), + 'o' => srwx = ((fperm << 6) & 0o700) | ((fperm << 3) & 0o070) | (fperm & 0o007), + _ => break + }; + pos += 1; + } + if pos == 0 { + srwx = 0; + } + (srwx, pos) +} \ No newline at end of file