From 1ff61def39dedb780b00183d50a150b0ce4730a9 Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Mon, 16 Jan 2023 22:30:12 -0500 Subject: [PATCH] New trait `Mode`, currently just prints the symbolic representation of a Unix mode, as extracted from u32 --- src/mode/mod.rs | 128 ++++++++++++++++++++++++++++++++++++++++++++- src/mode/parser.rs | 1 + 2 files changed, 127 insertions(+), 2 deletions(-) diff --git a/src/mode/mod.rs b/src/mode/mod.rs index 5ccb18e..ca7840e 100644 --- a/src/mode/mod.rs +++ b/src/mode/mod.rs @@ -1,15 +1,21 @@ //! Functions for parsing and managing permissions mod parser; -use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign}; +use std::{ + fmt::{self, Write}, + ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign}, +}; pub use parser::{ParseError, Parser}; +/// Gets the umask for the current user #[must_use] pub fn get_umask() -> u32 { let mask = unsafe { libc::umask(0) }; - unsafe { libc::umask(mask) } + let _mask = unsafe { libc::umask(mask) }; + mask } +/// Unix permission bit flags #[derive(Clone, Copy, PartialEq)] pub enum Bit { Suid = 0o4000, @@ -26,6 +32,61 @@ pub enum Bit { OExec = 0o1, } +impl Bit { + 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 & Self::Suid != 0 => 's', + Self::GExec if mode & Self::Sgid != 0 => 's', + Self::OExec if mode & Self::Sticky != 0 => 't', + Self::UExec | Self::GExec | Self::OExec => 'x', + } + } else { + '-' + } + } +} + +/// Functions for extracting information about Unix modes +pub trait Mode { + /// Returns a string representing permissions in symbolic format + fn mode_string(&self) -> Result; +} + +impl Mode for u32 { + fn mode_string(&self) -> Result { + 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) + } +} + impl BitAnd for Bit { type Output = u32; @@ -69,3 +130,66 @@ impl BitOrAssign for u32 { *self = *self | rhs; } } + +#[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().unwrap(); + assert_eq!(s.as_str(), "drwxr-xr-x") + } + + #[test] + fn display_bits_char() { + let m: u32 = 0o20666; + let s = m.mode_string().unwrap(); + assert_eq!(s.as_str(), "crw-rw-rw-") + } + + #[test] + fn display_bits_block() { + let m: u32 = 0o60660; + let s = m.mode_string().unwrap(); + assert_eq!(s.as_str(), "brw-rw----") + } + + #[test] + fn display_bits_file() { + let m: u32 = 0o100644; + let s = m.mode_string().unwrap(); + assert_eq!(s.as_str(), "-rw-r--r--") + } + + #[test] + fn display_bits_suid() { + let m: u32 = 0o104755; + let s = m.mode_string().unwrap(); + assert_eq!(s.as_str(), "-rwsr-xr-x") + } + + #[test] + fn display_bits_sgid() { + let m: u32 = 0o102755; + let s = m.mode_string().unwrap(); + assert_eq!(s.as_str(), "-rwxr-sr-x") + } + + #[test] + fn display_bits_sticky() { + let m: u32 = 0o41777; + let s = m.mode_string().unwrap(); + assert_eq!(s.as_str(), "drwxrwxrwt") + } +} diff --git a/src/mode/parser.rs b/src/mode/parser.rs index 5721af9..4008678 100644 --- a/src/mode/parser.rs +++ b/src/mode/parser.rs @@ -6,6 +6,7 @@ use std::{ ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign}, }; +/// 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`