mod internal;
mod mac;
use std::path::PathBuf;
use crate::operation::Operation;
use crate::state::acl::PosixAcl;
use crate::state::fsflags::FsFlags;
use crate::state::mac::MacState;
use crate::state::mount::{MountEntry, MountTable};
use crate::state::path::{FileType, PathComponent};
use crate::state::subject::ResolvedSubject;
use crate::state::{Probe, SystemState};
use internal::{link_mounts, make_component, parse_mount_options};
#[derive(Debug)]
pub struct StateBuilder {
subject: ResolvedSubject,
operation: Operation,
components: Vec<PathComponent>,
mounts: Vec<MountEntry>,
next_mount_id: u32,
next_device: u64,
mac_state: MacState,
}
impl Default for StateBuilder {
fn default() -> Self {
Self::new()
}
}
impl StateBuilder {
#[must_use]
pub fn new() -> Self {
Self {
subject: ResolvedSubject {
uid: 0,
gid: 0,
groups: vec![],
capabilities: Probe::Unknown,
},
operation: Operation::Read,
components: Vec::new(),
mounts: Vec::new(),
next_mount_id: 1,
next_device: 1,
mac_state: MacState::default(),
}
}
#[must_use]
pub fn subject(mut self, uid: u32, gid: u32, groups: Vec<u32>) -> Self {
self.subject = ResolvedSubject {
uid,
gid,
groups,
capabilities: Probe::Unknown,
};
self
}
#[must_use]
pub fn with_capabilities(mut self, bitmask: u64) -> Self {
self.subject.capabilities = Probe::Known(bitmask);
self
}
#[must_use]
pub fn operation(mut self, op: Operation) -> Self {
self.operation = op;
self
}
#[must_use]
pub fn component(mut self, path: &str, uid: u32, gid: u32, mode: u32) -> Self {
self.components
.push(make_component(path, uid, gid, mode, FileType::Directory, 2));
self
}
#[must_use]
pub fn component_file(mut self, path: &str, uid: u32, gid: u32, mode: u32) -> Self {
self.components
.push(make_component(path, uid, gid, mode, FileType::Regular, 1));
self
}
#[must_use]
pub fn component_with_acl(
mut self,
path: &str,
uid: u32,
gid: u32,
mode: u32,
acl_entries: Vec<crate::state::acl::AclEntry>,
) -> Self {
let mut comp = make_component(path, uid, gid, mode, FileType::Directory, 2);
comp.acl = Probe::Known(PosixAcl(acl_entries));
self.components.push(comp);
self
}
#[must_use]
pub fn component_file_with_acl(
mut self,
path: &str,
uid: u32,
gid: u32,
mode: u32,
acl_entries: Vec<crate::state::acl::AclEntry>,
) -> Self {
let mut comp = make_component(path, uid, gid, mode, FileType::Regular, 1);
comp.acl = Probe::Known(PosixAcl(acl_entries));
self.components.push(comp);
self
}
#[must_use]
pub fn component_with_flags(
mut self,
path: &str,
uid: u32,
gid: u32,
mode: u32,
flags: FsFlags,
) -> Self {
let mut comp = make_component(path, uid, gid, mode, FileType::Directory, 2);
comp.flags = Probe::Known(flags);
self.components.push(comp);
self
}
#[must_use]
pub fn component_file_with_flags(
mut self,
path: &str,
uid: u32,
gid: u32,
mode: u32,
flags: FsFlags,
) -> Self {
let mut comp = make_component(path, uid, gid, mode, FileType::Regular, 1);
comp.flags = Probe::Known(flags);
self.components.push(comp);
self
}
#[must_use]
pub fn component_inaccessible(mut self, path: &str) -> Self {
self.components.push(PathComponent {
path: PathBuf::from(path),
stat: Probe::Inaccessible,
acl: Probe::Inaccessible,
flags: Probe::Inaccessible,
mount: None,
});
self
}
#[must_use]
pub fn component_unknown(mut self, path: &str) -> Self {
self.components.push(PathComponent {
path: PathBuf::from(path),
stat: Probe::Unknown,
acl: Probe::Unknown,
flags: Probe::Unknown,
mount: None,
});
self
}
#[must_use]
pub fn mount(mut self, mountpoint: &str, fs_type: &str, options_str: &str) -> Self {
let options = parse_mount_options(options_str);
let device = self.next_device;
self.mounts.push(MountEntry {
mount_id: self.next_mount_id,
device,
mountpoint: PathBuf::from(mountpoint),
fs_type: fs_type.to_owned(),
options,
});
self.next_mount_id += 1;
self.next_device += 1;
self
}
#[must_use]
pub fn build(mut self) -> SystemState {
assert!(
!self.components.is_empty(),
"StateBuilder: at least one path component is required"
);
link_mounts(&mut self.components, &self.mounts);
SystemState {
subject: self.subject,
walk: self.components,
mounts: MountTable(self.mounts),
operation: self.operation,
mac_state: self.mac_state,
}
}
}
#[cfg(test)]
mod tests;