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
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct FileSystemStats {
15 pub total_bytes: u64,
16 pub used_bytes: u64,
17 pub available_bytes: u64,
18 pub total_inodes: u64,
19 pub free_inodes: u64,
20}
21
22pub trait RootFilesystemResourceLimits {
23 fn max_filesystem_bytes(&self) -> Option<u64>;
24 fn max_inode_count(&self) -> Option<usize>;
25}
26
27pub fn measure_filesystem_usage<F: VirtualFileSystem>(
28 filesystem: &mut F,
29) -> VfsResult<FileSystemUsage> {
30 let mut visited = BTreeSet::new();
31 measure_path_usage(filesystem, "/", &mut visited)
32}
33
34fn measure_path_usage<F: VirtualFileSystem>(
35 filesystem: &mut F,
36 path: &str,
37 visited: &mut BTreeSet<(u64, u64)>,
38) -> VfsResult<FileSystemUsage> {
39 let stat = filesystem.lstat(path)?;
40 let mut usage = FileSystemUsage::default();
41
42 if visited.insert((stat.dev, stat.ino)) {
43 usage.inode_count += 1;
44 if !stat.is_directory {
45 usage.total_bytes = usage.total_bytes.saturating_add(stat.size);
46 }
47 }
48
49 if !stat.is_directory || stat.is_symbolic_link {
50 return Ok(usage);
51 }
52
53 for entry in filesystem.read_dir_with_types(path)? {
54 if matches!(entry.name.as_str(), "." | "..") {
55 continue;
56 }
57
58 let child_path = if path == "/" {
59 format!("/{}", entry.name)
60 } else {
61 format!("{path}/{}", entry.name)
62 };
63 let child_usage = measure_path_usage(filesystem, &child_path, visited)?;
64 usage.total_bytes = usage.total_bytes.saturating_add(child_usage.total_bytes);
65 usage.inode_count = usage.inode_count.saturating_add(child_usage.inode_count);
66 }
67
68 Ok(usage)
69}