#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EffectiveAccess {
pub read: bool,
pub write: bool,
pub execute: bool,
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PathAccess {
pub readable: bool,
pub writable: bool,
pub executable: bool,
}
impl PathAccess {
pub fn resolve(mode: Option<u32>, mount_read_only: bool) -> Self {
Self {
readable: mode.is_none_or(|p| p & 0o444 != 0),
writable: !mount_read_only && mode.is_some_and(|p| p & 0o222 != 0),
executable: mode.is_some_and(|p| p & 0o111 != 0),
}
}
pub fn from_effective_access(access: EffectiveAccess, mount_read_only: bool) -> Self {
Self {
readable: access.read,
writable: !mount_read_only && access.write,
executable: access.execute,
}
}
}
#[cfg(test)]
mod tests {
use super::PathAccess;
#[test]
fn absent_mode_reads_and_nothing_else() {
for mount_read_only in [false, true] {
let access = PathAccess::resolve(None, mount_read_only);
assert!(access.readable, "read-only is about writes");
assert!(!access.writable, "absent mode means read-only backend");
assert!(!access.executable, "nothing here to hand exec(2)");
}
}
#[test]
fn directory_modes_separate_searchable_from_writable() {
let memory_dir = PathAccess::resolve(Some(0o777), false);
assert!(memory_dir.writable);
assert!(memory_dir.executable, "0o111 on a directory is searchable");
let dev_dir = PathAccess::resolve(Some(0o555), false);
assert!(!dev_dir.writable, "/dev accepts no mkdir");
assert!(dev_dir.executable);
assert!(dev_dir.readable);
}
#[test]
fn mode_bits_decide_on_a_writable_mount() {
assert!(PathAccess::resolve(Some(0o644), false).writable);
assert!(!PathAccess::resolve(Some(0o444), false).writable);
assert!(PathAccess::resolve(Some(0o755), false).executable);
assert!(!PathAccess::resolve(Some(0o644), false).executable);
assert!(!PathAccess::resolve(Some(0o000), false).readable);
}
#[test]
fn read_only_mount_overrides_a_writable_mode() {
let access = PathAccess::resolve(Some(0o755), true);
assert!(!access.writable, "the mount's read-only state must win");
assert!(access.readable, "read-only is about writes");
assert!(access.executable, "read-only says nothing about exec");
}
}