Skip to main content

secure_exec_vfs_core/posix/
usage.rs

1use 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}