mode: move Parser into separate module

This commit is contained in:
Nathan Fisher 2023-01-15 12:10:36 -05:00
parent b52787280b
commit 962a9c53b6
2 changed files with 312 additions and 310 deletions

View File

@ -1,6 +1,6 @@
//! Functions for parsing and managing permissions //! Functions for parsing and managing permissions
mod parser;
use std::{error, fmt::Display, num::ParseIntError}; pub use parser::{Parser, ParseError};
pub fn get_umask() -> u32 { pub fn get_umask() -> u32 {
let mask = unsafe { libc::umask(0) }; let mask = unsafe { libc::umask(0) };
@ -23,311 +23,3 @@ pub enum Bit {
OWrite = 0o2, OWrite = 0o2,
OExec = 0o1, OExec = 0o1,
} }
#[derive(Debug, PartialEq)]
pub enum ParseError {
InvalidBit,
InvalidDigit,
OutsideRange,
ParseIntError(ParseIntError),
NoOpSet,
}
impl Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl error::Error for ParseError {}
impl From<ParseIntError> for ParseError {
fn from(value: ParseIntError) -> Self {
Self::ParseIntError(value)
}
}
#[derive(Debug, PartialEq)]
enum Op {
Add,
Remove,
Equals,
}
#[derive(Clone, Debug, PartialEq)]
enum Who {
User,
Group,
Other,
All,
}
pub struct Parser {
mode: u32,
op: Option<Op>,
who: Vec<Who>,
bits: Vec<char>,
}
impl Default for Parser {
fn default() -> Self {
let umask = get_umask();
let mut mode = 0o0777;
mode &= umask;
Self {
mode,
op: None,
who: vec![],
bits: vec![],
}
}
}
impl Parser {
pub fn new(mode: u32) -> Self {
Self {
mode,
op: None,
who: vec![],
bits: vec![],
}
}
fn parse_octal(&mut self, value: &str) -> Result<u32, ParseError> {
let m = u32::from_str_radix(value, 8)?;
if m <= 0o7777 {
Ok(m)
} else {
Err(ParseError::OutsideRange)
}
}
fn add_who(&mut self, who: Who) -> Result<(), ParseError> {
if self.op.is_some() || self.who.contains(&who) || !self.bits.is_empty() {
Err(ParseError::InvalidDigit)
} else {
self.who.push(who);
Ok(())
}
}
fn set_op(&mut self, op: Op) -> Result<(), ParseError> {
if self.op.is_some() || !self.bits.is_empty() {
Err(ParseError::InvalidDigit)
} else {
self.op = Some(op);
Ok(())
}
}
fn push_bit(&mut self, bit: char) -> Result<(), ParseError> {
match self.op {
Some(_) => if self.bits.contains(&bit) {
return Err(ParseError::InvalidDigit);
} else {
self.bits.push(bit);
},
None => return Err(ParseError::NoOpSet),
}
Ok(())
}
fn push_suid(&mut self) -> Result<(), ParseError> {
for w in &self.who {
if w == &Who::All || w == &Who::Other {
return Err(ParseError::InvalidBit);
}
}
if self.who.is_empty() {
return Err(ParseError::InvalidBit);
}
self.push_bit('s')
}
fn push_sticky(&mut self) -> Result<(), ParseError> {
for w in &self.who {
if w == &Who::All || w == &Who::User || w == &Who::Group {
return Err(ParseError::InvalidBit);
}
}
if self.who.is_empty() {
return Err(ParseError::InvalidBit);
}
self.push_bit('t')
}
fn add_user_bits(&mut self) {
for b in &self.bits {
match b {
'r' => self.mode |= Bit::URead as u32,
'w' => self.mode |= Bit::UWrite as u32,
'x' => self.mode |= Bit::UExec as u32,
's' => self.mode |= Bit::Suid as u32,
_ => unreachable!(),
}
}
}
fn remove_user_bits(&mut self) {
for b in &self.bits {
match b {
'r' => self.mode &= Bit::URead as u32,
'w' => self.mode &= Bit::UWrite as u32,
'x' => self.mode &= Bit::UExec as u32,
's' => self.mode &= Bit::Suid as u32,
_ => unreachable!(),
}
}
}
fn zero_user_bits(&mut self) {
self.mode &= 0o4444;
}
fn set_user_bits(&mut self) {
match self.op {
Some(Op::Add) => self.add_user_bits(),
Some(Op::Remove) => self.remove_user_bits(),
Some(Op::Equals) => {
self.zero_user_bits();
self.add_user_bits();
},
None => {},
}
}
fn add_group_bits(&mut self) {
for b in &self.bits {
match b {
'r' => self.mode |= Bit::GRead as u32,
'w' => self.mode |= Bit::GWrite as u32,
'x' => self.mode |= Bit::GExec as u32,
's' => self.mode |= Bit::Sgid as u32,
_ => unreachable!(),
}
}
}
fn remove_group_bits(&mut self) {
for b in &self.bits {
match b {
'r' => self.mode &= Bit::GRead as u32,
'w' => self.mode &= Bit::GWrite as u32,
'x' => self.mode &= Bit::GExec as u32,
's' => self.mode &= Bit::Sgid as u32,
_ => unreachable!(),
}
}
}
fn zero_group_bits(&mut self) {
self.mode &= 0o2222;
}
fn set_group_bits(&mut self) {
match self.op {
Some(Op::Add) => self.add_group_bits(),
Some(Op::Remove) => self.remove_group_bits(),
Some(Op::Equals) => {
self.zero_group_bits();
self.add_group_bits();
},
None => {},
}
}
fn add_other_bits(&mut self) {
for b in &self.bits {
match b {
'r' => self.mode |= Bit::ORead as u32,
'w' => self.mode |= Bit::OWrite as u32,
'x' => self.mode |= Bit::OExec as u32,
't' => self.mode |= Bit::Sticky as u32,
_ => unreachable!(),
}
}
}
fn remove_other_bits(&mut self) {
for b in &self.bits {
match b {
'r' => self.mode &= Bit::ORead as u32,
'w' => self.mode &= Bit::OWrite as u32,
'x' => self.mode &= Bit::OExec as u32,
's' => self.mode &= Bit::Sticky as u32,
_ => unreachable!(),
}
}
}
fn zero_other_bits(&mut self) {
self.mode &= 0o1111;
}
fn set_other_bits(&mut self) {
match self.op {
Some(Op::Add) => self.add_other_bits(),
Some(Op::Remove) => self.remove_other_bits(),
Some(Op::Equals) => {
self.zero_other_bits();
self.add_other_bits();
},
None => {},
}
}
fn set_bits(&mut self) {
for w in self.who.clone() {
match w {
Who::User => self.set_user_bits(),
Who::Group => self.set_group_bits(),
Who::Other => self.set_other_bits(),
Who::All => {
self.set_user_bits();
self.set_group_bits();
self.set_other_bits();
}
}
}
}
fn reset(&mut self) {
self.who.clear();
self.op = None;
self.bits.clear();
}
pub fn mode(&self) -> u32 {
self.mode
}
pub fn parse(&mut self, value: &str) -> Result<u32, ParseError> {
match self.parse_octal(value) {
Ok(mode) => {
self.mode = mode;
return Ok(mode)
},
Err(e) => if e == ParseError::OutsideRange {
return Err(e);
}
}
for c in value.chars() {
match c {
'u' => self.add_who(Who::User)?,
'g' => self.add_who(Who::Group)?,
'o' => self.add_who(Who::Other)?,
'a' => self.add_who(Who::All)?,
'-' => self.set_op(Op::Remove)?,
'+' => self.set_op(Op::Add)?,
'=' => self.set_op(Op::Equals)?,
'r' | 'w' | 'x' => self.push_bit(c)?,
's' => self.push_suid()?,
't' => self.push_sticky()?,
_ => return Err(ParseError::InvalidDigit),
}
}
self.set_bits();
self.reset();
Ok(self.mode)
}
}

310
src/mode/parser.rs Normal file
View File

@ -0,0 +1,310 @@
use std::{error, fmt::Display, num::ParseIntError};
use super::{Bit, get_umask};
#[derive(Debug, PartialEq)]
pub enum ParseError {
InvalidBit,
InvalidDigit,
OutsideRange,
ParseIntError(ParseIntError),
NoOpSet,
}
impl Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl error::Error for ParseError {}
impl From<ParseIntError> for ParseError {
fn from(value: ParseIntError) -> Self {
Self::ParseIntError(value)
}
}
#[derive(Debug, PartialEq)]
enum Op {
Add,
Remove,
Equals,
}
#[derive(Clone, Debug, PartialEq)]
enum Who {
User,
Group,
Other,
All,
}
pub struct Parser {
mode: u32,
op: Option<Op>,
who: Vec<Who>,
bits: Vec<char>,
}
impl Default for Parser {
fn default() -> Self {
let umask = get_umask();
let mut mode = 0o0777;
mode &= umask;
Self {
mode,
op: None,
who: vec![],
bits: vec![],
}
}
}
impl Parser {
pub fn new(mode: u32) -> Self {
Self {
mode,
op: None,
who: vec![],
bits: vec![],
}
}
fn parse_octal(&mut self, value: &str) -> Result<u32, ParseError> {
let m = u32::from_str_radix(value, 8)?;
if m <= 0o7777 {
Ok(m)
} else {
Err(ParseError::OutsideRange)
}
}
fn add_who(&mut self, who: Who) -> Result<(), ParseError> {
if self.op.is_some() || self.who.contains(&who) || !self.bits.is_empty() {
Err(ParseError::InvalidDigit)
} else {
self.who.push(who);
Ok(())
}
}
fn set_op(&mut self, op: Op) -> Result<(), ParseError> {
if self.op.is_some() || !self.bits.is_empty() {
Err(ParseError::InvalidDigit)
} else {
self.op = Some(op);
Ok(())
}
}
fn push_bit(&mut self, bit: char) -> Result<(), ParseError> {
match self.op {
Some(_) => if self.bits.contains(&bit) {
return Err(ParseError::InvalidDigit);
} else {
self.bits.push(bit);
},
None => return Err(ParseError::NoOpSet),
}
Ok(())
}
fn push_suid(&mut self) -> Result<(), ParseError> {
for w in &self.who {
if w == &Who::All || w == &Who::Other {
return Err(ParseError::InvalidBit);
}
}
if self.who.is_empty() {
return Err(ParseError::InvalidBit);
}
self.push_bit('s')
}
fn push_sticky(&mut self) -> Result<(), ParseError> {
for w in &self.who {
if w == &Who::All || w == &Who::User || w == &Who::Group {
return Err(ParseError::InvalidBit);
}
}
if self.who.is_empty() {
return Err(ParseError::InvalidBit);
}
self.push_bit('t')
}
fn add_user_bits(&mut self) {
for b in &self.bits {
match b {
'r' => self.mode |= Bit::URead as u32,
'w' => self.mode |= Bit::UWrite as u32,
'x' => self.mode |= Bit::UExec as u32,
's' => self.mode |= Bit::Suid as u32,
_ => unreachable!(),
}
}
}
fn remove_user_bits(&mut self) {
for b in &self.bits {
match b {
'r' => self.mode &= Bit::URead as u32,
'w' => self.mode &= Bit::UWrite as u32,
'x' => self.mode &= Bit::UExec as u32,
's' => self.mode &= Bit::Suid as u32,
_ => unreachable!(),
}
}
}
fn zero_user_bits(&mut self) {
self.mode &= 0o4444;
}
fn set_user_bits(&mut self) {
match self.op {
Some(Op::Add) => self.add_user_bits(),
Some(Op::Remove) => self.remove_user_bits(),
Some(Op::Equals) => {
self.zero_user_bits();
self.add_user_bits();
},
None => {},
}
}
fn add_group_bits(&mut self) {
for b in &self.bits {
match b {
'r' => self.mode |= Bit::GRead as u32,
'w' => self.mode |= Bit::GWrite as u32,
'x' => self.mode |= Bit::GExec as u32,
's' => self.mode |= Bit::Sgid as u32,
_ => unreachable!(),
}
}
}
fn remove_group_bits(&mut self) {
for b in &self.bits {
match b {
'r' => self.mode &= Bit::GRead as u32,
'w' => self.mode &= Bit::GWrite as u32,
'x' => self.mode &= Bit::GExec as u32,
's' => self.mode &= Bit::Sgid as u32,
_ => unreachable!(),
}
}
}
fn zero_group_bits(&mut self) {
self.mode &= 0o2222;
}
fn set_group_bits(&mut self) {
match self.op {
Some(Op::Add) => self.add_group_bits(),
Some(Op::Remove) => self.remove_group_bits(),
Some(Op::Equals) => {
self.zero_group_bits();
self.add_group_bits();
},
None => {},
}
}
fn add_other_bits(&mut self) {
for b in &self.bits {
match b {
'r' => self.mode |= Bit::ORead as u32,
'w' => self.mode |= Bit::OWrite as u32,
'x' => self.mode |= Bit::OExec as u32,
't' => self.mode |= Bit::Sticky as u32,
_ => unreachable!(),
}
}
}
fn remove_other_bits(&mut self) {
for b in &self.bits {
match b {
'r' => self.mode &= Bit::ORead as u32,
'w' => self.mode &= Bit::OWrite as u32,
'x' => self.mode &= Bit::OExec as u32,
's' => self.mode &= Bit::Sticky as u32,
_ => unreachable!(),
}
}
}
fn zero_other_bits(&mut self) {
self.mode &= 0o1111;
}
fn set_other_bits(&mut self) {
match self.op {
Some(Op::Add) => self.add_other_bits(),
Some(Op::Remove) => self.remove_other_bits(),
Some(Op::Equals) => {
self.zero_other_bits();
self.add_other_bits();
},
None => {},
}
}
fn set_bits(&mut self) {
for w in self.who.clone() {
match w {
Who::User => self.set_user_bits(),
Who::Group => self.set_group_bits(),
Who::Other => self.set_other_bits(),
Who::All => {
self.set_user_bits();
self.set_group_bits();
self.set_other_bits();
}
}
}
}
fn reset(&mut self) {
self.who.clear();
self.op = None;
self.bits.clear();
}
pub fn mode(&self) -> u32 {
self.mode
}
pub fn parse(&mut self, value: &str) -> Result<u32, ParseError> {
match self.parse_octal(value) {
Ok(mode) => {
self.mode = mode;
return Ok(mode)
},
Err(e) => if e == ParseError::OutsideRange {
return Err(e);
}
}
for c in value.chars() {
match c {
'u' => self.add_who(Who::User)?,
'g' => self.add_who(Who::Group)?,
'o' => self.add_who(Who::Other)?,
'a' => self.add_who(Who::All)?,
'-' => self.set_op(Op::Remove)?,
'+' => self.set_op(Op::Add)?,
'=' => self.set_op(Op::Equals)?,
'r' | 'w' | 'x' => self.push_bit(c)?,
's' => self.push_suid()?,
't' => self.push_sticky()?,
_ => return Err(ParseError::InvalidDigit),
}
}
self.set_bits();
self.reset();
Ok(self.mode)
}
}