use compact_str::CompactString;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Directive {
CreateFile {
truncate_if_exists: bool,
mode: Option<Mode>,
user: Id,
group: Id,
contents: Option<Box<[u8]>>,
},
WriteToFile {
append: bool,
contents: Box<[u8]>,
},
CreateDirectory {
remove_if_exists: bool,
mode: Option<Mode>,
user: Id,
group: Id,
cleanup_age: Option<Age>,
},
AdjustPermissionsAndTmpFiles {
mode: Option<Mode>,
user: Id,
group: Id,
cleanup_age: Option<Age>,
},
CreateSubvolume {
quota: Option<SubvolumeQuota>,
mode: Option<Mode>,
user: Id,
group: Id,
cleanup_age: Option<Age>,
},
CreateFifo {
replace_if_exists: bool,
mode: Option<Mode>,
user: Id,
group: Id,
},
CreateSymlink {
replace_if_exists: bool,
target: Option<Box<[u8]>>,
},
CreateCharDeviceNode {
replace_if_exists: bool,
mode: Option<Mode>,
user: Id,
group: Id,
device_specifier: DeviceNode,
},
CreateBlockDeviceNode {
replace_if_exists: bool,
mode: Option<Mode>,
user: Id,
group: Id,
device_specifier: DeviceNode,
},
RecursiveCopy {
recursive_if_exists: bool,
cleanup_age: Option<Age>,
source: Option<CompactString>,
},
IgnorePathDuringCleaning { cleanup_age: Option<Age> },
IgnoreDirectoryDuringCleaning { cleanup_age: Option<Age> },
RemoveFile { recursive: bool },
AdjustAccess {
recursive: bool,
mode: Option<Mode>,
user: Id,
group: Id,
},
SetExtendedAttributes {
recursive: bool,
attributes: Box<[u8]>,
},
SetAttributes {
recursive: bool,
attributes: Box<[u8]>,
},
SetAcl {
recursive: bool,
append: bool,
acls: Box<[u8]>,
},
}
impl Directive {
#[must_use]
pub fn mode(&self) -> Option<&Mode> {
match self {
Self::CreateFile { mode, .. } => mode.as_ref(),
Self::WriteToFile { .. } => None,
Self::CreateDirectory { mode, .. } => mode.as_ref(),
Self::AdjustPermissionsAndTmpFiles { mode, .. } => mode.as_ref(),
Self::CreateSubvolume { mode, .. } => mode.as_ref(),
Self::CreateFifo { mode, .. } => mode.as_ref(),
Self::CreateSymlink { .. } => None,
Self::CreateCharDeviceNode { mode, .. } => mode.as_ref(),
Self::CreateBlockDeviceNode { mode, .. } => mode.as_ref(),
Self::RecursiveCopy { .. } => None,
Self::IgnorePathDuringCleaning { .. } => None,
Self::IgnoreDirectoryDuringCleaning { .. } => None,
Self::RemoveFile { .. } => None,
Self::AdjustAccess { mode, .. } => mode.as_ref(),
Self::SetExtendedAttributes { .. } => None,
Self::SetAttributes { .. } => None,
Self::SetAcl { .. } => None,
}
}
#[must_use]
pub fn user(&self) -> Option<&Id> {
match self {
Self::CreateFile { user, .. } => Some(user),
Self::WriteToFile { .. } => None,
Self::CreateDirectory { user, .. } => Some(user),
Self::AdjustPermissionsAndTmpFiles { user, .. } => Some(user),
Self::CreateSubvolume { user, .. } => Some(user),
Self::CreateFifo { user, .. } => Some(user),
Self::CreateSymlink { .. } => None,
Self::CreateCharDeviceNode { user, .. } => Some(user),
Self::CreateBlockDeviceNode { user, .. } => Some(user),
Self::RecursiveCopy { .. } => None,
Self::IgnorePathDuringCleaning { .. } => None,
Self::IgnoreDirectoryDuringCleaning { .. } => None,
Self::RemoveFile { .. } => None,
Self::AdjustAccess { user, .. } => Some(user),
Self::SetExtendedAttributes { .. } => None,
Self::SetAttributes { .. } => None,
Self::SetAcl { .. } => None,
}
}
#[must_use]
pub fn group(&self) -> Option<&Id> {
match self {
Self::CreateFile { group, .. } => Some(group),
Self::WriteToFile { .. } => None,
Self::CreateDirectory { group, .. } => Some(group),
Self::AdjustPermissionsAndTmpFiles { group, .. } => Some(group),
Self::CreateSubvolume { group, .. } => Some(group),
Self::CreateFifo { group, .. } => Some(group),
Self::CreateSymlink { .. } => None,
Self::CreateCharDeviceNode { group, .. } => Some(group),
Self::CreateBlockDeviceNode { group, .. } => Some(group),
Self::RecursiveCopy { .. } => None,
Self::IgnorePathDuringCleaning { .. } => None,
Self::IgnoreDirectoryDuringCleaning { .. } => None,
Self::RemoveFile { .. } => None,
Self::AdjustAccess { group, .. } => Some(group),
Self::SetExtendedAttributes { .. } => None,
Self::SetAttributes { .. } => None,
Self::SetAcl { .. } => None,
}
}
fn can_be_glob(&self) -> bool {
match self {
Self::CreateFile { .. } => false,
Self::WriteToFile { .. } => true,
Self::CreateDirectory { .. } => false,
Self::AdjustPermissionsAndTmpFiles { .. } => true,
Self::CreateSubvolume { .. } => false,
Self::CreateFifo { .. } => false,
Self::CreateSymlink { .. } => false,
Self::CreateCharDeviceNode { .. } => false,
Self::CreateBlockDeviceNode { .. } => false,
Self::RecursiveCopy { .. } => false,
Self::IgnorePathDuringCleaning { .. } => true,
Self::IgnoreDirectoryDuringCleaning { .. } => true,
Self::RemoveFile { .. } => true,
Self::AdjustAccess { .. } => true,
Self::SetExtendedAttributes { .. } => true,
Self::SetAttributes { .. } => true,
Self::SetAcl { .. } => true,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct Entry {
pub(crate) path: CompactString,
pub(crate) directive: Directive,
pub(crate) flags: EntryFlags,
}
impl Entry {
#[must_use]
pub fn path(&self) -> &str {
self.path.as_str()
}
#[must_use]
pub fn path_is_glob(&self) -> bool {
self.directive.can_be_glob() && self.path().contains(['*', '?', '['])
}
#[must_use]
pub fn directive(&self) -> &Directive {
&self.directive
}
#[must_use]
pub fn flags(&self) -> EntryFlags {
self.flags
}
}
bitflags::bitflags! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct EntryFlags: u8 {
const BOOT_ONLY = 0b01;
const ERRORS_OK_ON_CREATE = 0b10;
const REMOVE_NONMATCHING = 0b100;
const ARG_BASE64 = 0b1000;
const ARG_CREDENTIAL = 0b10000;
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Mode {
Set {
mode: libc::mode_t,
new_only: bool,
masked: bool,
},
}
impl Mode {
#[must_use]
pub fn new_only(&self) -> bool {
match self {
Self::Set { new_only, .. } => *new_only,
}
}
#[must_use]
pub fn mode(&self) -> libc::mode_t {
match self {
Self::Set { mode, .. } => *mode,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
#[allow(variant_size_differences)]
pub enum Id {
Caller { new_only: bool },
Numeric { id: libc::uid_t, new_only: bool },
Name { name: CompactString, new_only: bool },
}
impl Default for Id {
fn default() -> Self {
const {
assert!(size_of::<libc::uid_t>() == size_of::<libc::gid_t>());
};
Self::Caller { new_only: false }
}
}
impl Id {
#[must_use]
pub fn new_only(&self) -> bool {
match self {
Self::Caller { new_only } => *new_only,
Self::Numeric { new_only, .. } => *new_only,
Self::Name { new_only, .. } => *new_only,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct Age {
pub(crate) specifier: CompactString,
}
impl Age {
#[must_use]
pub fn raw(&self) -> &str {
self.specifier.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum SubvolumeQuota {
Inherit,
New,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DeviceNode {
pub major: libc::dev_t,
pub minor: libc::dev_t,
}
impl DeviceNode {
pub(crate) fn try_from_bytes(value: &[u8]) -> Option<Self> {
let value = std::str::from_utf8(value).ok()?;
let parts = value.split_once(':')?;
let major = parts.0.parse().ok()?;
let minor = parts.1.parse().ok()?;
Some(Self { major, minor })
}
}