Improve docs

This commit is contained in:
Nathan Fisher 2023-01-16 18:28:37 -05:00
parent e9819b6228
commit 7da63bb1b5
2 changed files with 127 additions and 51 deletions

View File

@ -2,15 +2,15 @@
mod parser; mod parser;
use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign}; use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign};
pub use parser::{Parser, ParseError}; pub use parser::{ParseError, Parser};
#[must_use]
pub fn get_umask() -> u32 { pub fn get_umask() -> u32 {
let mask = unsafe { libc::umask(0) }; let mask = unsafe { libc::umask(0) };
let umask = unsafe {libc::umask(mask) }; unsafe { libc::umask(mask) }
umask
} }
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, PartialEq)]
pub enum Bit { pub enum Bit {
Suid = 0o4000, Suid = 0o4000,
Sgid = 0o2000, Sgid = 0o2000,

View File

@ -1,12 +1,21 @@
use std::{error, fmt::Display, num::ParseIntError, ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign}}; use super::{get_umask, Bit};
use super::{Bit, get_umask}; use std::{
error,
fmt::Display,
num::ParseIntError,
ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign},
};
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum ParseError { pub enum ParseError {
/// the given `Bit` cannot be set for the given `Who`
InvalidBit, InvalidBit,
InvalidDigit, /// the character is not recognized
InvalidChar,
/// the specified octal mode is invalid
OutsideRange, OutsideRange,
ParseIntError(ParseIntError), ParseIntError(ParseIntError),
/// no `Op` is set when parsing a `Bit`
NoOpSet, NoOpSet,
} }
@ -24,17 +33,26 @@ impl From<ParseIntError> for ParseError {
} }
} }
#[derive(Debug, PartialEq)] #[derive(PartialEq)]
/// Operations which can be performed to add, remove, or set explicitly the given
/// `Bit` for the given `Who`
enum Op { enum Op {
/// `Bit`s will be added for `Who`
Add, Add,
/// `Bit`s will be remoed for `Who`
Remove, Remove,
/// `Bit`s will be set to exactly the specified arrangement
Equals, Equals,
} }
#[derive(Clone, Debug, PartialEq)] #[derive(PartialEq)]
/// The granularity of the given permissions
enum Who { enum Who {
/// applies for the current user
User = 0o100, User = 0o100,
/// applies for the current group
Group = 0o10, Group = 0o10,
/// applies for everyone else
Other = 0o1, Other = 0o1,
} }
@ -66,6 +84,12 @@ impl BitOrAssign<Who> for u32 {
} }
} }
/// A parser for octal and symbolic permissions. `Parser::default` creates an
/// instance which applies the given operations to the default setting for the
/// current user's umask. `Parser::new` creates a parser which applies the given
/// operations to the specified beginning set of permissions. Therefore, when
/// creating new files or directories use `Parser::default`, while `Parser::new`
/// should be used for setting permissions of already existing files or directories.
pub struct Parser { pub struct Parser {
mode: u32, mode: u32,
op: Option<Op>, op: Option<Op>,
@ -88,6 +112,7 @@ impl Default for Parser {
} }
impl Parser { impl Parser {
#[must_use]
pub fn new(mode: u32) -> Self { pub fn new(mode: u32) -> Self {
Self { Self {
mode, mode,
@ -97,7 +122,7 @@ impl Parser {
} }
} }
fn parse_octal(&mut self, value: &str) -> Result<u32, ParseError> { fn parse_octal(value: &str) -> Result<u32, ParseError> {
let m = u32::from_str_radix(value, 8)?; let m = u32::from_str_radix(value, 8)?;
if m <= 0o7777 { if m <= 0o7777 {
Ok(m) Ok(m)
@ -108,7 +133,7 @@ impl Parser {
fn add_who(&mut self, who: Who) -> Result<(), ParseError> { fn add_who(&mut self, who: Who) -> Result<(), ParseError> {
if self.op.is_some() || !self.bits == 0 { if self.op.is_some() || !self.bits == 0 {
Err(ParseError::InvalidDigit) Err(ParseError::InvalidChar)
} else { } else {
self.who |= who; self.who |= who;
Ok(()) Ok(())
@ -117,7 +142,7 @@ impl Parser {
fn set_op(&mut self, op: Op) -> Result<(), ParseError> { fn set_op(&mut self, op: Op) -> Result<(), ParseError> {
if self.op.is_some() || !self.bits == 0 { if self.op.is_some() || !self.bits == 0 {
Err(ParseError::InvalidDigit) Err(ParseError::InvalidChar)
} else { } else {
self.op = Some(op); self.op = Some(op);
if self.who == 0 { if self.who == 0 {
@ -128,6 +153,9 @@ impl Parser {
} }
fn push_read_bits(&mut self) -> Result<(), ParseError> { fn push_read_bits(&mut self) -> Result<(), ParseError> {
if self.op.is_none() {
Err(ParseError::NoOpSet)
} else {
if self.who & 0o100 != 0 { if self.who & 0o100 != 0 {
self.bits |= Bit::URead; self.bits |= Bit::URead;
} }
@ -139,8 +167,12 @@ impl Parser {
} }
Ok(()) Ok(())
} }
}
fn push_write_bits(&mut self) -> Result<(), ParseError> { fn push_write_bits(&mut self) -> Result<(), ParseError> {
if self.op.is_none() {
Err(ParseError::NoOpSet)
} else {
if self.who & 0o100 != 0 { if self.who & 0o100 != 0 {
self.bits |= Bit::UWrite; self.bits |= Bit::UWrite;
} }
@ -152,8 +184,12 @@ impl Parser {
} }
Ok(()) Ok(())
} }
}
fn push_exec_bits(&mut self) -> Result<(), ParseError> { fn push_exec_bits(&mut self) -> Result<(), ParseError> {
if self.op.is_none() {
Err(ParseError::NoOpSet)
} else {
if self.who & 0o100 != 0 { if self.who & 0o100 != 0 {
self.bits |= Bit::UExec; self.bits |= Bit::UExec;
} }
@ -165,15 +201,18 @@ impl Parser {
} }
Ok(()) Ok(())
} }
}
fn push_suid_sgid(&mut self) -> Result<(), ParseError> { fn push_suid_sgid(&mut self) -> Result<(), ParseError> {
if self.who == 0 || self.who & 0o1 != 0{ if self.who == 0 || self.who & 0o1 != 0 {
return Err(ParseError::InvalidBit); return Err(ParseError::InvalidBit);
} else if self.op.is_none() {
return Err(ParseError::NoOpSet);
} }
if self.who & 0o100 != 0 { if self.who & 0o100 != 0 {
self.bits |= Bit::Suid; self.bits |= Bit::Suid;
} }
if self.who &0o10 != 0 { if self.who & 0o10 != 0 {
self.bits |= Bit::Sgid; self.bits |= Bit::Sgid;
} }
Ok(()) Ok(())
@ -182,6 +221,8 @@ impl Parser {
fn push_sticky(&mut self) -> Result<(), ParseError> { fn push_sticky(&mut self) -> Result<(), ParseError> {
if self.who == 0 || self.who & 0o100 != 0 || self.who & 0o10 != 0 { if self.who == 0 || self.who & 0o100 != 0 || self.who & 0o10 != 0 {
return Err(ParseError::InvalidBit); return Err(ParseError::InvalidBit);
} else if self.op.is_none() {
return Err(ParseError::NoOpSet);
} }
if self.who & 0o1 != 0 { if self.who & 0o1 != 0 {
self.bits |= Bit::Sticky; self.bits |= Bit::Sticky;
@ -208,11 +249,11 @@ impl Parser {
if self.who & 0o10 != 0 { if self.who & 0o10 != 0 {
self.mode &= !(0o2070); self.mode &= !(0o2070);
} }
if self.who &0o1 != 0 { if self.who & 0o1 != 0 {
self.mode &= !(0o1007); self.mode &= !(0o1007);
} }
self.add_bits(); self.add_bits();
}, }
None => return Err(ParseError::NoOpSet), None => return Err(ParseError::NoOpSet),
} }
Ok(()) Ok(())
@ -224,20 +265,34 @@ impl Parser {
self.bits = 0; self.bits = 0;
} }
#[must_use]
pub fn mode(&self) -> u32 { pub fn mode(&self) -> u32 {
self.mode self.mode
} }
/// Parses a numerical mode from either an octal string or symbolic representation
/// and applies those permissions to the starting set of permissions.
/// # Errors
/// Returns `ParseError` if:
/// - invalid digit
/// - no operation specified
/// - more than one operation specified at a time (multiple operations can
/// be specified separated by comma)
/// - the specified bit cannot be applied to the specified `Who`
/// - bits are specified before operations
/// - the specified octal mode is greater than 0o7777
pub fn parse(&mut self, value: &str) -> Result<u32, ParseError> { pub fn parse(&mut self, value: &str) -> Result<u32, ParseError> {
match self.parse_octal(value) { match Self::parse_octal(value) {
Ok(mode) => { Ok(mode) => {
self.mode = mode; self.mode = mode;
return Ok(mode) return Ok(mode);
}, }
Err(e) => if e == ParseError::OutsideRange { Err(e) => {
if e == ParseError::OutsideRange {
return Err(e); return Err(e);
} }
} }
}
for c in value.chars() { for c in value.chars() {
match c { match c {
'u' => self.add_who(Who::User)?, 'u' => self.add_who(Who::User)?,
@ -247,7 +302,7 @@ impl Parser {
self.add_who(Who::User)?; self.add_who(Who::User)?;
self.add_who(Who::Group)?; self.add_who(Who::Group)?;
self.add_who(Who::Other)?; self.add_who(Who::Other)?;
}, }
'-' => self.set_op(Op::Remove)?, '-' => self.set_op(Op::Remove)?,
'+' => self.set_op(Op::Add)?, '+' => self.set_op(Op::Add)?,
'=' => self.set_op(Op::Equals)?, '=' => self.set_op(Op::Equals)?,
@ -259,8 +314,8 @@ impl Parser {
',' => { ',' => {
self.set_bits()?; self.set_bits()?;
self.reset(); self.reset();
}, }
_ => return Err(ParseError::InvalidDigit), _ => return Err(ParseError::InvalidChar),
} }
} }
self.set_bits()?; self.set_bits()?;
@ -349,4 +404,25 @@ mod test {
let mode = parser.parse("10000"); let mode = parser.parse("10000");
assert_eq!(mode, Err(ParseError::OutsideRange)) assert_eq!(mode, Err(ParseError::OutsideRange))
} }
#[test]
fn no_op() {
let mut parser = Parser::default();
let mode = parser.parse("rws");
assert_eq!(mode, Err(ParseError::NoOpSet));
}
#[test]
fn ordering_error() {
let mut parser = Parser::default();
let mode = parser.parse("ux+s");
assert_eq!(mode, Err(ParseError::NoOpSet));
}
#[test]
fn ordering_error1() {
let mut parser = Parser::default();
let mode = parser.parse("x+s");
assert_eq!(mode, Err(ParseError::NoOpSet));
}
} }