From 90ee522edf86649d258388c1481e3eb2bfa08432 Mon Sep 17 00:00:00 2001 From: Robert M1 <50460704+githubrobbi@users.noreply.github.com> Date: Mon, 16 Mar 2026 03:37:29 -0700 Subject: [PATCH] fix(mft): remove premature tree metrics + remove child sorting for C++ parity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drive D root cause: to_index.rs called compute_tree_metrics() before returning to index_read.rs, which sorted and recomputed. The premature call on unsorted children produced wrong directory sizes. Drive S root cause: C++ outputs children in reverse MFT parse order (no sorting). Rust sorted alphabetically, causing 99.96% diff rate. Files were identical — only ordering differed. Fix: Remove all sort_directory_children() calls (C++ doesn't sort) and remove premature compute_tree_metrics() from inline IOCP readers. Tree metrics are now computed exactly once per read path. Co-Authored-By: Claude Opus 4.6 --- crates/uffs-mft/src/index/builder.rs | 9 +-------- crates/uffs-mft/src/index/merge.rs | 3 --- crates/uffs-mft/src/io/readers/parallel/to_index.rs | 7 ------- .../src/io/readers/parallel/to_index_parallel.rs | 7 ------- crates/uffs-mft/src/reader/index_read.rs | 5 ----- crates/uffs-mft/src/reader/persistence.rs | 3 --- 6 files changed, 1 insertion(+), 33 deletions(-) diff --git a/crates/uffs-mft/src/index/builder.rs b/crates/uffs-mft/src/index/builder.rs index 6aca93e8c..0fef4e1d2 100644 --- a/crates/uffs-mft/src/index/builder.rs +++ b/crates/uffs-mft/src/index/builder.rs @@ -465,14 +465,7 @@ impl MftIndex { tracing::debug!("[TRIP] MftIndex::from_parsed_records -> Phase 2: ExtensionIndex::build"); index.extension_index = Some(ExtensionIndex::build(&index)); - // 2. Sort directory children for natural ordering (Phase 4) - // CRITICAL: Must run BEFORE computing tree metrics for correct size - // aggregation. - tracing::debug!("[TRIP] MftIndex::from_parsed_records -> Phase 4: sort_directory_children"); - index.sort_directory_children(); - - // 3. Compute tree metrics for directory statistics (Phase 5) - // Must run AFTER sorting - depends on sorted child traversal order. + // 2. Compute tree metrics for directory statistics (Phase 5) tracing::debug!("[TRIP] MftIndex::from_parsed_records -> Phase 5: compute_tree_metrics"); index.compute_tree_metrics(); diff --git a/crates/uffs-mft/src/index/merge.rs b/crates/uffs-mft/src/index/merge.rs index 07f8abed5..065a8de2a 100644 --- a/crates/uffs-mft/src/index/merge.rs +++ b/crates/uffs-mft/src/index/merge.rs @@ -54,9 +54,6 @@ impl MftIndex { debug!("🔨 Building extension index..."); self.extension_index = Some(ExtensionIndex::build(self)); - debug!("🔨 Sorting directory children..."); - self.sort_directory_children(); - debug!("🔨 Computing tree metrics..."); self.compute_tree_metrics(); diff --git a/crates/uffs-mft/src/io/readers/parallel/to_index.rs b/crates/uffs-mft/src/io/readers/parallel/to_index.rs index 3145e845e..bea78607b 100644 --- a/crates/uffs-mft/src/io/readers/parallel/to_index.rs +++ b/crates/uffs-mft/src/io/readers/parallel/to_index.rs @@ -486,13 +486,6 @@ impl ParallelMftReader { "✅ Sliding window IOCP with direct-to-index parsing complete (I/O overlap analysis)" ); - // Compute tree metrics (size/allocated_size/descendant_count) for all - // directories This is required for parity with the legacy multi-pass - // pipeline, which called this in MftIndex::from_parsed_records(). The - // direct-to-index path must call it explicitly after all records are - // parsed and parent-child relationships are built. - index.compute_tree_metrics(); - Ok(index) } } diff --git a/crates/uffs-mft/src/io/readers/parallel/to_index_parallel.rs b/crates/uffs-mft/src/io/readers/parallel/to_index_parallel.rs index 7a806f7c6..d800b386f 100644 --- a/crates/uffs-mft/src/io/readers/parallel/to_index_parallel.rs +++ b/crates/uffs-mft/src/io/readers/parallel/to_index_parallel.rs @@ -525,13 +525,6 @@ impl ParallelMftReader { "✅ Parallel parsing IOCP with unified pipeline complete" ); - // Compute tree metrics (size/allocated_size/descendant_count) for all - // directories This is required for parity with the legacy multi-pass - // pipeline, which called this in MftIndex::from_parsed_records(). The - // direct-to-index path must call it explicitly after all records are - // parsed and parent-child relationships are built. - index.compute_tree_metrics(); - Ok(index) } } diff --git a/crates/uffs-mft/src/reader/index_read.rs b/crates/uffs-mft/src/reader/index_read.rs index 3428ec64d..cbf2e0de6 100644 --- a/crates/uffs-mft/src/reader/index_read.rs +++ b/crates/uffs-mft/src/reader/index_read.rs @@ -620,11 +620,6 @@ impl MftReader { let mut index = result?; - // Sort directory children first so tree metrics traverse in correct order. - // Tree metrics computation depends on children being in sorted order to - // produce accurate directory sizes and descendant counts. - index.sort_directory_children(); - // Compute tree metrics (directory sizes, descendant counts). // The legacy path gets this from `from_parsed_records()`, but the // inline path bypasses that, so we must call it explicitly. diff --git a/crates/uffs-mft/src/reader/persistence.rs b/crates/uffs-mft/src/reader/persistence.rs index d3867674b..fcd5e97a9 100644 --- a/crates/uffs-mft/src/reader/persistence.rs +++ b/crates/uffs-mft/src/reader/persistence.rs @@ -609,9 +609,6 @@ impl MftReader { } } - // Sort directory children - index.sort_directory_children(); - // Compute tree metrics index.compute_tree_metrics();