use crate::operation::Operation;
use crate::state::fsflags::FsFlags;
use crate::state::{Probe, SystemState};
use super::LayerResult;
#[must_use]
pub fn check_fs_flags(state: &SystemState) -> LayerResult {
let target_idx = match resolve_target(state) {
Ok(idx) => idx,
Err(result) => return result,
};
let component = &state.walk[target_idx];
match &component.flags {
Probe::Unknown | Probe::Inaccessible => LayerResult::Degraded {
reason: format!(
"could not read filesystem flags for {}",
component.path.display()
),
},
Probe::Known(flags) => check_operation_against_flags(state.operation, *flags, target_idx),
}
}
fn resolve_target(state: &SystemState) -> Result<usize, LayerResult> {
state
.operation
.target_component(state.walk.len())
.ok_or_else(|| LayerResult::Degraded {
reason: "path walk too short to determine target component".into(),
})
}
fn check_operation_against_flags(
op: Operation,
flags: FsFlags,
component_idx: usize,
) -> LayerResult {
match op {
Operation::Read | Operation::Stat | Operation::Execute => LayerResult::Pass {
detail: "filesystem flags do not restrict this operation".into(),
warnings: vec![],
},
Operation::Write
| Operation::Chmod
| Operation::ChownUid
| Operation::ChownGid
| Operation::SetXattr { .. } => check_write_flags(flags, component_idx),
Operation::Delete | Operation::Create => {
check_delete_create_flags(op, flags, component_idx)
}
}
}
fn check_write_flags(flags: FsFlags, component_idx: usize) -> LayerResult {
if flags.immutable {
return LayerResult::Fail {
detail: "file has immutable flag (chattr +i)".into(),
component_index: Some(component_idx),
};
}
if flags.append_only {
return LayerResult::Fail {
detail: "file has append-only flag (chattr +a)".into(),
component_index: Some(component_idx),
};
}
LayerResult::Pass {
detail: "no blocking filesystem flags set".into(),
warnings: vec![],
}
}
fn check_delete_create_flags(op: Operation, flags: FsFlags, component_idx: usize) -> LayerResult {
if flags.immutable {
return LayerResult::Fail {
detail: "parent directory has immutable flag (chattr +i), \
cannot delete or create files in it"
.into(),
component_index: Some(component_idx),
};
}
if op == Operation::Delete && flags.append_only {
return LayerResult::Fail {
detail: "parent directory has append-only flag (chattr +a), \
cannot delete files from it"
.into(),
component_index: Some(component_idx),
};
}
LayerResult::Pass {
detail: "no blocking filesystem flags set".into(),
warnings: vec![],
}
}
#[cfg(test)]
#[path = "fsflags_tests.rs"]
mod tests;