1#![deny(clippy::all)]
13#![warn(clippy::pedantic)]
14#![deny(missing_docs)]
15
16pub mod acl;
17pub mod error;
18pub mod fsflags;
19pub mod mac;
20pub mod mountinfo;
21pub mod proc;
22pub mod stat;
23pub mod statvfs;
24pub mod subject;
25
26use std::path::{Path, PathBuf};
27
28use whyno_core::operation::Operation;
29use whyno_core::state::path::PathComponent;
30use whyno_core::state::subject::ResolvedSubject;
31use whyno_core::state::SystemState;
32
33use error::GatherError;
34
35pub fn gather_state(
52 subject: &ResolvedSubject,
53 operation: Operation,
54 path: &Path,
55) -> Result<SystemState, GatherError> {
56 if !path.is_absolute() {
57 return Err(GatherError::RelativePath(path.to_owned()));
58 }
59
60 let ancestors = split_ancestors(path);
61 let components: Vec<PathComponent> = ancestors.iter().map(|p| gather_component(p)).collect();
62 let mut mounts = mountinfo::parse_mountinfo()?;
63 overlay_statvfs_flags(&mut mounts);
64 let walk = link_mounts_to_components(components, &mounts);
65
66 Ok(SystemState {
67 subject: subject.clone(),
68 walk,
69 mounts,
70 operation,
71 mac_state: mac::gather_mac_state(operation, path),
72 })
73}
74
75fn overlay_statvfs_flags(mounts: &mut whyno_core::state::mount::MountTable) {
81 for entry in &mut mounts.0 {
82 if let whyno_core::state::Probe::Known(opts) =
83 statvfs::probe_mount_options(&entry.mountpoint)
84 {
85 entry.options = opts;
86 }
87 }
88}
89
90fn split_ancestors(path: &Path) -> Vec<PathBuf> {
94 let mut ancestors: Vec<PathBuf> = path.ancestors().map(Path::to_path_buf).collect();
95 ancestors.reverse();
96 ancestors
97}
98
99fn gather_component(path: &Path) -> PathComponent {
101 PathComponent {
102 path: path.to_owned(),
103 stat: stat::stat_component(path),
104 acl: acl::get_acl(path),
105 flags: fsflags::get_fs_flags(path),
106 mount: None,
107 }
108}
109
110fn link_mounts_to_components(
116 mut components: Vec<PathComponent>,
117 mounts: &whyno_core::state::mount::MountTable,
118) -> Vec<PathComponent> {
119 for comp in &mut components {
120 comp.mount = find_best_mount(comp, mounts);
121 }
122 components
123}
124
125fn find_best_mount(
127 comp: &PathComponent,
128 mounts: &whyno_core::state::mount::MountTable,
129) -> Option<usize> {
130 let dev = comp.stat.known().map(|s| s.dev)?;
131 mounts
132 .0
133 .iter()
134 .enumerate()
135 .filter(|(_, m)| m.device == dev && comp.path.starts_with(&m.mountpoint))
136 .max_by_key(|(_, m)| m.mountpoint.as_os_str().len())
137 .map(|(idx, _)| idx)
138}
139
140#[cfg(test)]
141#[path = "lib_tests.rs"]
142mod tests;