diff --git a/src/mode/mod.rs b/src/mode/mod.rs index f681ecb..d250d67 100644 --- a/src/mode/mod.rs +++ b/src/mode/mod.rs @@ -1,5 +1,7 @@ //! Functions for parsing and managing permissions +use std::{error, fmt::Display, num::ParseIntError}; + pub fn get_umask() -> u32 { let mask = unsafe { libc::umask(0) }; let umask = unsafe {libc::umask(mask) }; @@ -7,7 +9,7 @@ pub fn get_umask() -> u32 { } #[derive(Debug, PartialEq)] -pub enum Bits { +pub enum Bit { Suid = 0o4000, Sgid = 0o2000, Sticky = 0o1000, @@ -23,14 +25,37 @@ pub enum Bits { } #[derive(Debug, PartialEq)] -pub enum Op { +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 for ParseError { + fn from(value: ParseIntError) -> Self { + Self::ParseIntError(value) + } +} + +#[derive(Debug, PartialEq)] +enum Op { Add, Remove, Equals, } -#[derive(Debug, PartialEq)] -pub enum Who { +#[derive(Clone, Debug, PartialEq)] +enum Who { User, Group, Other, @@ -40,7 +65,8 @@ pub enum Who { pub struct Parser { mode: u32, op: Option, - who: Option, + who: Vec, + bits: Vec, } impl Default for Parser { @@ -51,17 +77,257 @@ impl Default for Parser { Self { mode, op: None, - who: None, + who: vec![], + bits: vec![], } } } impl Parser { - fn new(mode: u32) -> Self { + pub fn new(mode: u32) -> Self { Self { mode, op: None, - who: None, + who: vec![], + bits: vec![], } } + + fn parse_octal(&mut self, value: &str) -> Result { + 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 { + 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) + } }