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
42 changes: 41 additions & 1 deletion src/aggregate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,9 @@ fn aggregate_inner(
let total = aggregates.iter().map(|aggregate| aggregate.bytes).sum();
res.num_errors = aggregates.iter().map(|aggregate| aggregate.errors).sum();

if stats.entries_traversed == 0 {
// Entries whose metadata could not be read never contribute a size, so counting them is not
// enough to know that the minimum was ever set - only the sentinel still being in place is.
if stats.smallest_file_in_bytes == u128::MAX {
stats.smallest_file_in_bytes = 0;
}

Expand Down Expand Up @@ -810,6 +812,44 @@ mod tests {
assert_eq!(result.num_errors, 1);
}

#[cfg(unix)]
#[test]
fn unreadable_roots_do_not_leak_the_smallest_file_sentinel() {
let dir = tempfile::tempdir().unwrap();
let root = dir.path().join("missing");

let (result, statistics) = aggregate(
Vec::new(),
None::<Vec<u8>>,
WalkOptions {
threads: 1,
count_hard_links: true,
apparent_size: true,
// Crossing filesystems keeps the root out of `crossdev::init`, so it fails while
// being walked and is counted as a traversed entry without a size.
cross_filesystems: true,
ignore_dirs: std::collections::BTreeSet::default(),
ignore_patterns: None,
metadata_options: crate::TraversalOptions::default(),
},
false,
true,
ByteFormat::Bytes,
vec![root],
)
.unwrap();

assert_eq!(result.num_errors, 1);
assert_eq!(
statistics.entries_traversed, 1,
"the failing root is seen, even though its size is unknown"
);
assert_eq!(
statistics.smallest_file_in_bytes, 0,
"no file size was ever observed, so the sentinel must not be reported as a size"
);
}

#[test]
fn ignored_patterns_are_left_out_of_the_reported_size() {
let dir = tempfile::tempdir().unwrap();
Expand Down
Loading