Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 46 additions & 11 deletions common/directory/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use clap::ArgMatches;
pub use eth2_network_config::DEFAULT_HARDCODED_NETWORK;
use std::collections::VecDeque;
use std::fs;
use std::path::{Path, PathBuf};

Expand Down Expand Up @@ -64,18 +65,52 @@ pub fn parse_path_or_default_with_flag(

/// Get the approximate size of a directory and its contents.
///
/// Will skip unreadable files, and files. Not 100% accurate if files are being created and deleted
/// while this function is running.
/// - Skips unreadable entries and symlinks.
/// - Bounds traversal by depth and total entries to limit potential DoS from deeply nested trees.
/// - Not 100% accurate if files are being created and deleted while this function is running.
pub fn size_of_dir(path: &Path) -> u64 {
if let Ok(iter) = fs::read_dir(path) {
iter.filter_map(std::result::Result::ok)
.map(size_of_dir_entry)
.sum()
} else {
0
const MAX_DEPTH: usize = 64;
const MAX_TOTAL_ENTRIES: usize = 100_000;

let mut total_size = 0u64;
let mut entries_seen = 0usize;

let mut stack = VecDeque::new();
stack.push_back((path.to_path_buf(), 0usize));

while let Some((dir_path, depth)) = stack.pop_back() {
if depth > MAX_DEPTH {
continue;
}

let Ok(iter) = fs::read_dir(&dir_path) else {
continue;
};

for entry_result in iter {
if entries_seen >= MAX_TOTAL_ENTRIES {
return total_size;
}

let Ok(entry) = entry_result else {
continue;
};
entries_seen += 1;

let Ok(file_type) = entry.file_type() else {
continue;
};
if file_type.is_dir() {
if depth < MAX_DEPTH {
stack.push_back((entry.path(), depth + 1));
}
} else if file_type.is_file()
&& let Ok(metadata) = entry.metadata()
{
total_size = total_size.saturating_add(metadata.len());
}
}
}
}

fn size_of_dir_entry(dir: fs::DirEntry) -> u64 {
dir.metadata().map(|m| m.len()).unwrap_or(0)
total_size
}