secure_exec_vfs_core/posix/
usage.rs1use super::vfs::{VfsResult, VirtualFileSystem};
2use std::collections::BTreeSet;
3
4pub const DEFAULT_MAX_FILESYSTEM_BYTES: u64 = 64 * 1024 * 1024;
5pub const DEFAULT_MAX_INODE_COUNT: usize = 16_384;
6
7#[derive(Debug, Clone, PartialEq, Eq, Default)]
8pub struct FileSystemUsage {
9 pub total_bytes: u64,
10 pub inode_count: usize,
11}
12
13pub trait RootFilesystemResourceLimits {
14 fn max_filesystem_bytes(&self) -> Option<u64>;
15 fn max_inode_count(&self) -> Option<usize>;
16}
17
18pub fn measure_filesystem_usage<F: VirtualFileSystem>(
19 filesystem: &mut F,
20) -> VfsResult<FileSystemUsage> {
21 let mut visited = BTreeSet::new();
22 measure_path_usage(filesystem, "/", &mut visited)
23}
24
25fn measure_path_usage<F: VirtualFileSystem>(
26 filesystem: &mut F,
27 path: &str,
28 visited: &mut BTreeSet<u64>,
29) -> VfsResult<FileSystemUsage> {
30 let stat = filesystem.lstat(path)?;
31 let mut usage = FileSystemUsage::default();
32
33 if visited.insert(stat.ino) {
34 usage.inode_count += 1;
35 if !stat.is_directory {
36 usage.total_bytes = usage.total_bytes.saturating_add(stat.size);
37 }
38 }
39
40 if !stat.is_directory || stat.is_symbolic_link {
41 return Ok(usage);
42 }
43
44 for entry in filesystem.read_dir_with_types(path)? {
45 if matches!(entry.name.as_str(), "." | "..") {
46 continue;
47 }
48
49 let child_path = if path == "/" {
50 format!("/{}", entry.name)
51 } else {
52 format!("{path}/{}", entry.name)
53 };
54 let child_usage = measure_path_usage(filesystem, &child_path, visited)?;
55 usage.total_bytes = usage.total_bytes.saturating_add(child_usage.total_bytes);
56 usage.inode_count = usage.inode_count.saturating_add(child_usage.inode_count);
57 }
58
59 Ok(usage)
60}