// Stolen from https://github.com/Peltoche/lsd/blob/master/src/meta/permissions.rs use serde::{Deserialize, Serialize}; use std::{fmt::Display, fs::Metadata}; pub type RWX = (char, char, char, char, char, char, char, char, char); pub type Octal = (u8, u8, u8, u8); #[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Serialize, Deserialize, Hash)] pub struct Permissions { #[serde(default)] pub user_read: bool, #[serde(default)] pub user_write: bool, #[serde(default)] pub user_execute: bool, #[serde(default)] pub group_read: bool, #[serde(default)] pub group_write: bool, #[serde(default)] pub group_execute: bool, #[serde(default)] pub other_read: bool, #[serde(default)] pub other_write: bool, #[serde(default)] pub other_execute: bool, #[serde(default)] pub sticky: bool, #[serde(default)] pub setgid: bool, #[serde(default)] pub setuid: bool, } impl Permissions {} impl<'a> From<&'a Metadata> for Permissions { #[cfg(unix)] fn from(meta: &Metadata) -> Self { use std::os::unix::fs::PermissionsExt; let bits = meta.permissions().mode(); let has_bit = |bit| bits & bit == bit; Self { user_read: has_bit(modes::USER_READ), user_write: has_bit(modes::USER_WRITE), user_execute: has_bit(modes::USER_EXECUTE), group_read: has_bit(modes::GROUP_READ), group_write: has_bit(modes::GROUP_WRITE), group_execute: has_bit(modes::GROUP_EXECUTE), other_read: has_bit(modes::OTHER_READ), other_write: has_bit(modes::OTHER_WRITE), other_execute: has_bit(modes::OTHER_EXECUTE), sticky: has_bit(modes::STICKY), setgid: has_bit(modes::SETGID), setuid: has_bit(modes::SETUID), } } #[cfg(windows)] fn from(_: &Metadata) -> Self { panic!("Cannot get permissions from metadata on Windows") } } impl Display for Permissions { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let (ur, uw, ux, gr, gw, gx, or, ow, ox) = (*self).into(); write!(f, "{ur}{uw}{ux}{gr}{gw}{gx}{or}{ow}{ox}") } } impl Into for Permissions { fn into(self) -> RWX { let bit = |bit: bool, chr: char| { if bit { chr } else { '-' } }; let ur = bit(self.user_read, 'r'); let uw = bit(self.user_write, 'w'); let ux = match (self.user_execute, self.setuid) { (true, true) => 's', (true, false) => 'x', (false, true) => 'S', (false, false) => '-', }; let gr = bit(self.group_read, 'r'); let gw = bit(self.group_write, 'w'); let gx = match (self.group_execute, self.setgid) { (true, true) => 's', (true, false) => 'x', (false, true) => 'S', (false, false) => '-', }; let or = bit(self.other_read, 'r'); let ow = bit(self.other_write, 'w'); let ox = match (self.other_execute, self.sticky) { (true, true) => 't', (true, false) => 'x', (false, true) => 'T', (false, false) => '-', }; (ur, uw, ux, gr, gw, gx, or, ow, ox) } } impl Into for Permissions { fn into(self) -> Octal { let bits_to_octal = |r: bool, w: bool, x: bool| (r as u8) * 4 + (w as u8) * 2 + (x as u8); ( bits_to_octal(self.setuid, self.setgid, self.sticky), bits_to_octal(self.user_read, self.user_write, self.user_execute), bits_to_octal(self.group_read, self.group_write, self.group_execute), bits_to_octal(self.other_read, self.other_write, self.other_execute), ) } } // More readable aliases for the permission bits exposed by libc. #[allow(trivial_numeric_casts)] #[cfg(unix)] mod modes { pub type Mode = u32; // The `libc::mode_t` type’s actual type varies, but the value returned // from `metadata.permissions().mode()` is always `u32`. pub const USER_READ: Mode = libc::S_IRUSR as Mode; pub const USER_WRITE: Mode = libc::S_IWUSR as Mode; pub const USER_EXECUTE: Mode = libc::S_IXUSR as Mode; pub const GROUP_READ: Mode = libc::S_IRGRP as Mode; pub const GROUP_WRITE: Mode = libc::S_IWGRP as Mode; pub const GROUP_EXECUTE: Mode = libc::S_IXGRP as Mode; pub const OTHER_READ: Mode = libc::S_IROTH as Mode; pub const OTHER_WRITE: Mode = libc::S_IWOTH as Mode; pub const OTHER_EXECUTE: Mode = libc::S_IXOTH as Mode; pub const STICKY: Mode = libc::S_ISVTX as Mode; pub const SETGID: Mode = libc::S_ISGID as Mode; pub const SETUID: Mode = libc::S_ISUID as Mode; }