From dd9e0cec2cf4488977c5b2cf2283f7a116929124 Mon Sep 17 00:00:00 2001 From: VXNCXNX Date: Sun, 16 Aug 2026 08:46:34 +0000 Subject: [PATCH] fix: --stats printed the u128 sentinel as the smallest file size The sentinel reset was guarded by entries_traversed == 0, but an entry whose metadata cannot be read still counts as traversed while never contributing a size. Reset when the sentinel is still in place instead. --- src/aggregate.rs | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/aggregate.rs b/src/aggregate.rs index 552c433e..c40d5e50 100644 --- a/src/aggregate.rs +++ b/src/aggregate.rs @@ -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; } @@ -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::>, + 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();