use nix::errno::Errno;
use nix::sys::stat::{Mode, SFlag};
use nix::unistd::AccessFlags;
#[derive(Default, Debug, PartialEq, Copy, Clone, Eq)]
pub struct TypePerm(pub u16);
lazy_static::lazy_static! {
pub static ref SPECIAL_BITS: Mode = Mode::S_ISUID | Mode::S_ISGID | Mode::S_ISVTX;
pub static ref PERMISSIONS_MASK: Mode = Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO;
}
#[allow(unused)]
impl TypePerm {
pub fn remove_mode(&mut self, mode: Mode) {
let new_mode = self.0 & !mode.bits() as u16;
self.0 = new_mode;
}
pub fn insert_mode(&mut self, mode: Mode) {
let new_mode = self.0 | mode.bits() as u16;
self.0 = new_mode;
}
pub fn extract_type(self) -> SFlag {
let mask = SFlag::S_IFMT;
SFlag::from_bits_truncate(self.0 as u32) & mask
}
pub fn is_typed(&self) -> bool {
!self.extract_type().is_empty()
}
pub fn is_character_device(&self) -> bool {
SFlag::from_bits_truncate(self.0 as u32) == SFlag::S_IFCHR
}
pub fn is_fifo(&self) -> bool {
SFlag::from_bits_truncate(self.0 as u32) == SFlag::S_IFIFO
}
pub fn is_regular(&self) -> bool {
SFlag::from_bits_truncate(self.0 as u32) == SFlag::S_IFREG
}
pub fn is_directory(&self) -> bool {
SFlag::from_bits_truncate(self.0 as u32) == SFlag::S_IFDIR
}
pub fn is_symlink(&self) -> bool {
SFlag::from_bits_truncate(self.0 as u32) == SFlag::S_IFLNK
}
pub fn is_socket(&self) -> bool {
SFlag::from_bits_truncate(self.0 as u32) == SFlag::S_IFSOCK
}
pub fn is_block_device(&self) -> bool {
SFlag::from_bits_truncate(self.0 as u32) == SFlag::S_IFBLK
}
pub fn owner_access(&self) -> AccessFlags {
let mask = Mode::S_IRWXU;
AccessFlags::from_bits(((self.0 as libc::mode_t & mask.bits()) >> 6) as i32)
.expect("bits should be valid")
}
pub fn group_access(&self) -> AccessFlags {
let mask = Mode::S_IRWXG;
AccessFlags::from_bits(((self.0 as libc::mode_t & mask.bits()) >> 3) as i32)
.expect("bits should be valid")
}
pub fn other_access(&self) -> AccessFlags {
let mask = Mode::S_IRWXO;
AccessFlags::from_bits((self.0 as libc::mode_t & mask.bits()) as i32)
.expect("bits should be valid")
}
pub fn class_access(&self, class: PermissionClass) -> AccessFlags {
match class {
PermissionClass::Owner => self.owner_access(),
PermissionClass::Group => self.group_access(),
PermissionClass::Other => self.other_access(),
}
}
pub fn is_pure_mode(&self) -> bool {
let mask = *SPECIAL_BITS | *PERMISSIONS_MASK;
let excess_bits = self.0 & !mask.bits() as u16;
excess_bits == 0
}
pub fn extract_pure_mode(self) -> Self {
let mask = *SPECIAL_BITS | *PERMISSIONS_MASK;
Self(self.0 & mask.bits() as u16)
}
}
impl TryFrom<(libc::mode_t, SFlag)> for TypePerm {
type Error = Errno;
fn try_from(values: (libc::mode_t, SFlag)) -> Result<Self, Self::Error> {
let (mode, filetype) = values;
let mode = Mode::from_bits(mode).ok_or(Errno::EINVAL)?;
Ok(TypePerm(mode.bits() as u16 | filetype.bits() as u16))
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[allow(unused)]
pub enum PermissionClass {
Owner,
Group,
Other,
}