#![warn(missing_docs)]
fn type_bits(mode: u32) -> u32 {
(mode >> 12) & 0o17
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum Type {
File,
Dir,
Symlink,
Socket,
Fifo,
BlockDevice,
CharDevice,
Whiteout,
Unknown,
}
impl From<u32> for Type {
fn from(mode: u32) -> Type {
use Type::*;
match type_bits(mode) {
0o001 => Fifo,
0o002 => CharDevice,
0o004 => Dir,
0o006 => BlockDevice,
0o010 => File,
0o012 => Symlink,
0o014 => Socket,
0o016 => Whiteout,
_ => Unknown,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Accessor {
Other,
Group,
User,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Access {
Execute,
Write,
Read,
}
pub fn is_allowed(by: Accessor, ty: Access, mode: u32) -> bool {
use Access::*;
use Accessor::*;
let by = match by {
User => 2,
Group => 1,
Other => 0,
};
let bits = (mode >> (3 * by)) & 0o7;
let ty = match ty {
Read => 2,
Write => 1,
Execute => 0,
};
bits & (1 << ty) != 0
}
pub fn is_file(mode: u32) -> bool {
Type::from(mode) == Type::File
}
pub fn is_dir(mode: u32) -> bool {
Type::from(mode) == Type::Dir
}
pub fn is_symlink(mode: u32) -> bool {
Type::from(mode) == Type::Symlink
}
pub fn is_fifo(mode: u32) -> bool {
Type::from(mode) == Type::Fifo
}
pub fn is_char_device(mode: u32) -> bool {
Type::from(mode) == Type::CharDevice
}
pub fn is_block_device(mode: u32) -> bool {
Type::from(mode) == Type::BlockDevice
}
pub fn is_socket(mode: u32) -> bool {
Type::from(mode) == Type::Socket
}
pub fn is_setuid(mode: u32) -> bool {
mode & 0o4000 != 0
}
pub fn is_setgid(mode: u32) -> bool {
mode & 0o2000 != 0
}
pub fn is_sticky(mode: u32) -> bool {
mode & 0o1000 != 0
}
pub fn to_string(mode: u32) -> String {
use Access::*;
use Accessor::*;
use Type::*;
let setuid = is_setuid(mode);
let setgid = is_setgid(mode);
let sticky = is_sticky(mode);
let mut s = String::with_capacity(10);
s.push(match Type::from(mode) {
Fifo => 'p',
CharDevice => 'c',
Dir => 'd',
BlockDevice => 'b',
File => '-',
Symlink => 'l',
Socket => 's',
Whiteout => 'w',
Unknown => '?',
});
for accessor in [User, Group, Other] {
for access in [Read, Write, Execute] {
s.push(
match (access, accessor, is_allowed(accessor, access, mode)) {
(Execute, User, true) if setuid => 's',
(Execute, User, false) if setuid => 'S',
(Execute, Group, true) if setgid => 's',
(Execute, Group, false) if setgid => 'S',
(Execute, Other, true) if sticky => 't',
(Execute, Other, false) if sticky => 'T',
(Execute, _, true) => 'x',
(Write, _, true) => 'w',
(Read, _, true) => 'r',
(_, _, false) => '-',
},
);
}
}
s
}