Add unix-mode (previously a submodule)
This commit is contained in:
parent
ebebf2b4e5
commit
dca08c34bb
@ -1 +0,0 @@
|
|||||||
Subproject commit 0bbf1aed9ad583017b90aa2c03d3f7870538a914
|
|
13
unix-mode/Cargo.toml
Normal file
13
unix-mode/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "unix-mode"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libc = { workspace = true }
|
||||||
|
|
||||||
|
[dependencies.bitflags]
|
||||||
|
package = "bitflags-mini"
|
||||||
|
path = "../bitflags-mini"
|
98
unix-mode/src/bit.rs
Normal file
98
unix-mode/src/bit.rs
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
use bitflags::BitFlags;
|
||||||
|
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign};
|
||||||
|
|
||||||
|
/// Unix permission bit flags
|
||||||
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
pub enum Bit {
|
||||||
|
Suid = 0o4000,
|
||||||
|
Sgid = 0o2000,
|
||||||
|
Sticky = 0o1000,
|
||||||
|
URead = 0o400,
|
||||||
|
UWrite = 0o200,
|
||||||
|
UExec = 0o100,
|
||||||
|
GRead = 0o40,
|
||||||
|
GWrite = 0o20,
|
||||||
|
GExec = 0o10,
|
||||||
|
ORead = 0o4,
|
||||||
|
OWrite = 0o2,
|
||||||
|
OExec = 0o1,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitAnd<u32> for Bit {
|
||||||
|
type Output = u32;
|
||||||
|
|
||||||
|
fn bitand(self, rhs: u32) -> Self::Output {
|
||||||
|
self as u32 & rhs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitAnd<Bit> for u32 {
|
||||||
|
type Output = u32;
|
||||||
|
|
||||||
|
fn bitand(self, rhs: Bit) -> Self::Output {
|
||||||
|
self & rhs as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitAnd for Bit {
|
||||||
|
type Output = u32;
|
||||||
|
|
||||||
|
fn bitand(self, rhs: Self) -> Self::Output {
|
||||||
|
self as u32 & rhs as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitAndAssign<Bit> for u32 {
|
||||||
|
fn bitand_assign(&mut self, rhs: Bit) {
|
||||||
|
*self = *self & rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitOr<u32> for Bit {
|
||||||
|
type Output = u32;
|
||||||
|
|
||||||
|
fn bitor(self, rhs: u32) -> Self::Output {
|
||||||
|
self as u32 | rhs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitOr<Bit> for u32 {
|
||||||
|
type Output = u32;
|
||||||
|
|
||||||
|
fn bitor(self, rhs: Bit) -> Self::Output {
|
||||||
|
self | rhs as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitOr for Bit {
|
||||||
|
type Output = u32;
|
||||||
|
|
||||||
|
fn bitor(self, rhs: Self) -> Self::Output {
|
||||||
|
self as u32 | rhs as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitOrAssign<Bit> for u32 {
|
||||||
|
fn bitor_assign(&mut self, rhs: Bit) {
|
||||||
|
*self = *self | rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bit {
|
||||||
|
pub fn as_char(&self, mode: u32) -> char {
|
||||||
|
if mode & *self != 0 {
|
||||||
|
match self {
|
||||||
|
Self::Suid | Self::Sgid => 's',
|
||||||
|
Self::Sticky => 't',
|
||||||
|
Self::URead | Self::GRead | Self::ORead => 'r',
|
||||||
|
Self::UWrite | Self::GWrite | Self::OWrite => 'w',
|
||||||
|
Self::UExec if mode.contains(Self::Suid) => 's',
|
||||||
|
Self::GExec if mode.contains(Self::Sgid) => 's',
|
||||||
|
Self::OExec if mode.contains(Self::Sticky) => 't',
|
||||||
|
Self::UExec | Self::GExec | Self::OExec => 'x',
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
'-'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
141
unix-mode/src/lib.rs
Normal file
141
unix-mode/src/lib.rs
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
//! Functions for parsing and managing permissions
|
||||||
|
mod bit;
|
||||||
|
mod parser;
|
||||||
|
mod who;
|
||||||
|
use std::fmt::{self, Write};
|
||||||
|
|
||||||
|
pub use {
|
||||||
|
bit::Bit,
|
||||||
|
parser::{ParseError, Parser},
|
||||||
|
who::Who,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Gets the umask for the current user
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_umask() -> u32 {
|
||||||
|
let mask = unsafe { libc::umask(0) };
|
||||||
|
let _mask = unsafe { libc::umask(mask) };
|
||||||
|
mask
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Functions for extracting information about Unix modes
|
||||||
|
pub trait Mode {
|
||||||
|
/// Returns a string representing permissions in symbolic format
|
||||||
|
/// including file type
|
||||||
|
fn mode_string_full(&self) -> Result<String, fmt::Error>;
|
||||||
|
|
||||||
|
/// Returns a string representing permissions in symbolic format
|
||||||
|
/// minus file type
|
||||||
|
fn mode_string(&self) -> Result<String, fmt::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mode for u32 {
|
||||||
|
fn mode_string_full(&self) -> Result<String, fmt::Error> {
|
||||||
|
let b = if self & 0o40000 != 0 && self & 0o20000 != 0 {
|
||||||
|
'b'
|
||||||
|
} else if self & 0o40000 != 0 {
|
||||||
|
'd'
|
||||||
|
} else if self & 0o20000 != 0 {
|
||||||
|
'c'
|
||||||
|
} else {
|
||||||
|
'-'
|
||||||
|
};
|
||||||
|
let mut s = String::new();
|
||||||
|
write!(s, "{b}")?;
|
||||||
|
[
|
||||||
|
Bit::URead,
|
||||||
|
Bit::UWrite,
|
||||||
|
Bit::UExec,
|
||||||
|
Bit::GRead,
|
||||||
|
Bit::GWrite,
|
||||||
|
Bit::GExec,
|
||||||
|
Bit::ORead,
|
||||||
|
Bit::OWrite,
|
||||||
|
Bit::OExec,
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.try_for_each(|b| write!(s, "{}", b.as_char(*self)))?;
|
||||||
|
Ok(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mode_string(&self) -> Result<String, fmt::Error> {
|
||||||
|
let mut s = String::new();
|
||||||
|
[
|
||||||
|
Bit::URead,
|
||||||
|
Bit::UWrite,
|
||||||
|
Bit::UExec,
|
||||||
|
Bit::GRead,
|
||||||
|
Bit::GWrite,
|
||||||
|
Bit::GExec,
|
||||||
|
Bit::ORead,
|
||||||
|
Bit::OWrite,
|
||||||
|
Bit::OExec,
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.try_for_each(|b| write!(s, "{}", b.as_char(*self)))?;
|
||||||
|
Ok(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn getumask() {
|
||||||
|
let mask = unsafe { libc::umask(0o22) };
|
||||||
|
assert_eq!(get_umask(), 0o022);
|
||||||
|
unsafe {
|
||||||
|
libc::umask(mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn display_bits_dir() {
|
||||||
|
let m: u32 = 0o40755;
|
||||||
|
let s = m.mode_string_full().unwrap();
|
||||||
|
assert_eq!(s.as_str(), "drwxr-xr-x")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn display_bits_char() {
|
||||||
|
let m: u32 = 0o20666;
|
||||||
|
let s = m.mode_string_full().unwrap();
|
||||||
|
assert_eq!(s.as_str(), "crw-rw-rw-")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn display_bits_block() {
|
||||||
|
let m: u32 = 0o60660;
|
||||||
|
let s = m.mode_string_full().unwrap();
|
||||||
|
assert_eq!(s.as_str(), "brw-rw----")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn display_bits_file() {
|
||||||
|
let m: u32 = 0o100644;
|
||||||
|
let s = m.mode_string_full().unwrap();
|
||||||
|
assert_eq!(s.as_str(), "-rw-r--r--")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn display_bits_suid() {
|
||||||
|
let m: u32 = 0o104755;
|
||||||
|
let s = m.mode_string_full().unwrap();
|
||||||
|
assert_eq!(s.as_str(), "-rwsr-xr-x")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn display_bits_sgid() {
|
||||||
|
let m: u32 = 0o102755;
|
||||||
|
let s = m.mode_string_full().unwrap();
|
||||||
|
assert_eq!(s.as_str(), "-rwxr-sr-x")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn display_bits_sticky() {
|
||||||
|
let m: u32 = 0o41777;
|
||||||
|
let s = m.mode_string_full().unwrap();
|
||||||
|
assert_eq!(s.as_str(), "drwxrwxrwt")
|
||||||
|
}
|
||||||
|
}
|
386
unix-mode/src/parser.rs
Normal file
386
unix-mode/src/parser.rs
Normal file
@ -0,0 +1,386 @@
|
|||||||
|
use super::{get_umask, Bit, Who};
|
||||||
|
use bitflags::BitFlags;
|
||||||
|
use std::{error, fmt::Display, num::ParseIntError};
|
||||||
|
|
||||||
|
/// Errors which might occur when parsing Unix permissions from a string
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum ParseError {
|
||||||
|
/// the given `Bit` cannot be set for the given `Who`
|
||||||
|
InvalidBit,
|
||||||
|
/// the character is not recognized
|
||||||
|
InvalidChar,
|
||||||
|
/// the specified octal mode is invalid
|
||||||
|
OutsideRange,
|
||||||
|
ParseIntError(ParseIntError),
|
||||||
|
/// no `Op` is set when parsing a `Bit`
|
||||||
|
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(PartialEq)]
|
||||||
|
/// Operations which can be performed to add, remove, or set explicitly the given
|
||||||
|
/// `Bit` for the given `Who`
|
||||||
|
enum Op {
|
||||||
|
/// `Bit`s will be added for `Who`
|
||||||
|
Add,
|
||||||
|
/// `Bit`s will be remoed for `Who`
|
||||||
|
Remove,
|
||||||
|
/// `Bit`s will be set to exactly the specified arrangement
|
||||||
|
Equals,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
||||||
|
mode: u32,
|
||||||
|
op: Option<Op>,
|
||||||
|
who: u32,
|
||||||
|
bits: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Parser {
|
||||||
|
fn default() -> Self {
|
||||||
|
let umask = get_umask();
|
||||||
|
let mut mode = 0o0666;
|
||||||
|
mode &= umask;
|
||||||
|
Self {
|
||||||
|
mode,
|
||||||
|
op: None,
|
||||||
|
who: 0,
|
||||||
|
bits: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parser {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(mode: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
mode,
|
||||||
|
op: None,
|
||||||
|
who: 0,
|
||||||
|
bits: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_octal(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.bits == 0 {
|
||||||
|
Err(ParseError::InvalidChar)
|
||||||
|
} else {
|
||||||
|
self.who |= who;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_op(&mut self, op: Op) -> Result<(), ParseError> {
|
||||||
|
if self.op.is_some() || !self.bits == 0 {
|
||||||
|
Err(ParseError::InvalidChar)
|
||||||
|
} else {
|
||||||
|
self.op = Some(op);
|
||||||
|
if self.who == 0 {
|
||||||
|
self.who |= 0o111;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_read_bits(&mut self) -> Result<(), ParseError> {
|
||||||
|
if self.op.is_none() {
|
||||||
|
Err(ParseError::NoOpSet)
|
||||||
|
} else {
|
||||||
|
if self.who.contains(Who::User) {
|
||||||
|
self.bits |= Bit::URead;
|
||||||
|
}
|
||||||
|
if self.who.contains(Who::Group) {
|
||||||
|
self.bits |= Bit::GRead;
|
||||||
|
}
|
||||||
|
if self.who.contains(Who::Other) {
|
||||||
|
self.bits |= Bit::ORead;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_write_bits(&mut self) -> Result<(), ParseError> {
|
||||||
|
if self.op.is_none() {
|
||||||
|
Err(ParseError::NoOpSet)
|
||||||
|
} else {
|
||||||
|
if self.who.contains(Who::User) {
|
||||||
|
self.bits |= Bit::UWrite;
|
||||||
|
}
|
||||||
|
if self.who.contains(Who::Group) {
|
||||||
|
self.bits |= Bit::GWrite;
|
||||||
|
}
|
||||||
|
if self.who.contains(Who::Other) {
|
||||||
|
self.bits |= Bit::OWrite;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_exec_bits(&mut self) -> Result<(), ParseError> {
|
||||||
|
if self.op.is_none() {
|
||||||
|
Err(ParseError::NoOpSet)
|
||||||
|
} else {
|
||||||
|
if self.who.contains(Who::User) {
|
||||||
|
self.bits |= Bit::UExec;
|
||||||
|
}
|
||||||
|
if self.who.contains(Who::Group) {
|
||||||
|
self.bits |= Bit::GExec;
|
||||||
|
}
|
||||||
|
if self.who.contains(Who::Other) {
|
||||||
|
self.bits |= Bit::OExec;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_suid_sgid(&mut self) -> Result<(), ParseError> {
|
||||||
|
if self.who == 0 || self.who.contains(Who::Other) {
|
||||||
|
return Err(ParseError::InvalidBit);
|
||||||
|
} else if self.op.is_none() {
|
||||||
|
return Err(ParseError::NoOpSet);
|
||||||
|
}
|
||||||
|
if self.who.contains(Who::User) {
|
||||||
|
self.bits |= Bit::Suid;
|
||||||
|
}
|
||||||
|
if self.who.contains(Who::Group) {
|
||||||
|
self.bits |= Bit::Sgid;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_sticky(&mut self) -> Result<(), ParseError> {
|
||||||
|
if self.who == 0 || self.who.contains(Who::User) || self.who.contains(Who::Group) {
|
||||||
|
return Err(ParseError::InvalidBit);
|
||||||
|
} else if self.op.is_none() {
|
||||||
|
return Err(ParseError::NoOpSet);
|
||||||
|
}
|
||||||
|
if self.who.contains(Who::Other) {
|
||||||
|
self.bits |= Bit::Sticky;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_bits(&mut self) {
|
||||||
|
self.mode |= self.bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_bits(&mut self) {
|
||||||
|
self.mode &= !self.bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_bits(&mut self) -> Result<(), ParseError> {
|
||||||
|
match self.op {
|
||||||
|
Some(Op::Add) => self.add_bits(),
|
||||||
|
Some(Op::Remove) => self.remove_bits(),
|
||||||
|
Some(Op::Equals) => {
|
||||||
|
if self.who.contains(Who::User) {
|
||||||
|
self.mode &= !(0o4700);
|
||||||
|
}
|
||||||
|
if self.who.contains(Who::Group) {
|
||||||
|
self.mode &= !(0o2070);
|
||||||
|
}
|
||||||
|
if self.who.contains(Who::Other) {
|
||||||
|
self.mode &= !(0o1007);
|
||||||
|
}
|
||||||
|
self.add_bits();
|
||||||
|
}
|
||||||
|
None => return Err(ParseError::NoOpSet),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
self.who = 0;
|
||||||
|
self.op = None;
|
||||||
|
self.bits = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn mode(&self) -> u32 {
|
||||||
|
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> {
|
||||||
|
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::User)?;
|
||||||
|
self.add_who(Who::Group)?;
|
||||||
|
self.add_who(Who::Other)?;
|
||||||
|
}
|
||||||
|
'-' => self.set_op(Op::Remove)?,
|
||||||
|
'+' => self.set_op(Op::Add)?,
|
||||||
|
'=' => self.set_op(Op::Equals)?,
|
||||||
|
'r' => self.push_read_bits()?,
|
||||||
|
'w' => self.push_write_bits()?,
|
||||||
|
'x' => self.push_exec_bits()?,
|
||||||
|
's' => self.push_suid_sgid()?,
|
||||||
|
't' => self.push_sticky()?,
|
||||||
|
',' => {
|
||||||
|
self.set_bits()?;
|
||||||
|
self.reset();
|
||||||
|
}
|
||||||
|
_ => return Err(ParseError::InvalidChar),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.set_bits()?;
|
||||||
|
self.reset();
|
||||||
|
Ok(self.mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add() {
|
||||||
|
let mut parser = Parser::new(0o644);
|
||||||
|
let mode = parser.parse("ug+x");
|
||||||
|
assert_eq!(mode, Ok(0o754));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn remove() {
|
||||||
|
let mut parser = Parser::new(0o777);
|
||||||
|
let mode = parser.parse("go-wx");
|
||||||
|
assert_eq!(mode, Ok(0o744));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn octal() {
|
||||||
|
let mut parser = Parser::default();
|
||||||
|
let mode = parser.parse("4755");
|
||||||
|
assert_eq!(mode, Ok(0o4755));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn equals() {
|
||||||
|
let mut parser = Parser::new(0o701);
|
||||||
|
let mode = parser.parse("ugo=rw");
|
||||||
|
assert_eq!(mode, Ok(0o666));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn o_equals() {
|
||||||
|
let mut parser = Parser::new(0o752);
|
||||||
|
let mode = parser.parse("o=rx");
|
||||||
|
assert_eq!(mode, Ok(0o755));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_who() {
|
||||||
|
let mut parser = Parser::new(0o644);
|
||||||
|
let mode = parser.parse("+x");
|
||||||
|
assert_eq!(mode, Ok(0o755));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn compound_ops() {
|
||||||
|
let mut parser = Parser::new(0o666);
|
||||||
|
let mode = parser.parse("u+x,g-w,o-r");
|
||||||
|
assert_eq!(mode, Ok(0o742));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn compount_ops2() {
|
||||||
|
let mut parser = Parser::new(0o4444);
|
||||||
|
let mode = parser.parse("u+w,ug+x,o=rx,u-s");
|
||||||
|
assert_eq!(mode, Ok(0o755));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_sticky_bit() {
|
||||||
|
let mut parser = Parser::default();
|
||||||
|
let mode = parser.parse("u+t");
|
||||||
|
assert_eq!(mode, Err(ParseError::InvalidBit));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_s() {
|
||||||
|
let mut parser = Parser::default();
|
||||||
|
let mode = parser.parse("+s");
|
||||||
|
assert_eq!(mode, Err(ParseError::InvalidBit));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn outside_range() {
|
||||||
|
let mut parser = Parser::default();
|
||||||
|
let mode = parser.parse("10000");
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
48
unix-mode/src/who.rs
Normal file
48
unix-mode/src/who.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign};
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
/// The granularity of the given permissions
|
||||||
|
pub enum Who {
|
||||||
|
/// applies for the current user
|
||||||
|
User = 0o100,
|
||||||
|
/// applies for the current group
|
||||||
|
Group = 0o10,
|
||||||
|
/// applies for everyone else
|
||||||
|
Other = 0o1,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitAnd<Who> for u32 {
|
||||||
|
type Output = u32;
|
||||||
|
|
||||||
|
fn bitand(self, rhs: Who) -> Self::Output {
|
||||||
|
self & rhs as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitAnd<u32> for Who {
|
||||||
|
type Output = u32;
|
||||||
|
|
||||||
|
fn bitand(self, rhs: u32) -> Self::Output {
|
||||||
|
self as u32 & rhs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitAndAssign<Who> for u32 {
|
||||||
|
fn bitand_assign(&mut self, rhs: Who) {
|
||||||
|
*self = *self & rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitOr<Who> for u32 {
|
||||||
|
type Output = u32;
|
||||||
|
|
||||||
|
fn bitor(self, rhs: Who) -> Self::Output {
|
||||||
|
self | rhs as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitOrAssign<Who> for u32 {
|
||||||
|
fn bitor_assign(&mut self, rhs: Who) {
|
||||||
|
*self = *self | rhs;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user