Skip to content

Commit 95fcfa2

Browse files
authored
Merge pull request #3 from githubrobbi/fix-f-drive-parity
Fix LIVE MFT parity: directory sizes, missing records, CSV output
2 parents 3283242 + b2351e5 commit 95fcfa2

File tree

3 files changed

+17
-23
lines changed

3 files changed

+17
-23
lines changed

crates/uffs-cli/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ struct Cli {
237237
limit: u32,
238238

239239
/// Output format: table, json, csv, custom
240-
#[arg(short, long, default_value = "custom")]
240+
#[arg(short, long, default_value = "csv")]
241241
format: String,
242242

243243
/// Case-sensitive matching (default: off)

crates/uffs-mft/src/io/readers/parallel/to_index.rs

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -102,33 +102,22 @@ impl ParallelMftReader {
102102
);
103103

104104
// Generate read chunks with bitmap skip optimization
105-
// For NVMe/SSD, use precise chunk generation to skip unused regions entirely
105+
// CRITICAL: Use standard chunking for ALL drive types (bitmap is advisory, not
106+
// authoritative) The bitmap should be used for I/O optimization (skip
107+
// ranges, pre-allocation), NOT as an authoritative filter for which
108+
// regions to read. If the bitmap is stale (common on live filesystems),
109+
// treating it as authoritative causes record loss. Evidence: HDD path
110+
// (using advisory bitmap) has only 6 missing records vs 10K+ on NVMe/SSD.
106111
let use_direct_chunk_io = matches!(
107112
self.drive_type,
108113
crate::platform::DriveType::Nvme | crate::platform::DriveType::Ssd
109114
);
110115

111-
// For NVMe/SSD: use larger max to allow direct chunk-to-I/O mapping
112-
// For HDD: use standard io_chunk_size for predictable sequential reads
113-
const MAX_DIRECT_IO_SIZE: usize = 16 * 1024 * 1024; // 16MB max for direct I/O
114-
115-
let sorted_chunks: Vec<ReadChunk> = match (&self.drive_type, &self.bitmap) {
116-
(crate::platform::DriveType::Nvme | crate::platform::DriveType::Ssd, Some(bitmap)) => {
117-
// NVMe/SSD: Use precise chunks that skip unused regions
118-
// min_gap_records=64 means gaps smaller than 64KB are read through
119-
// Use MAX_DIRECT_IO_SIZE as the max chunk size for direct I/O
120-
let mut chunks =
121-
generate_precise_read_chunks(&self.extent_map, bitmap, MAX_DIRECT_IO_SIZE, 64);
122-
chunks.sort_by_key(|c| c.disk_offset);
123-
chunks
124-
}
125-
_ => {
126-
// HDD or no bitmap: Use standard chunk generation
127-
let mut chunks =
128-
generate_read_chunks(&self.extent_map, self.bitmap.as_ref(), self.chunk_size);
129-
chunks.sort_by_key(|c| c.disk_offset);
130-
chunks
131-
}
116+
let sorted_chunks: Vec<ReadChunk> = {
117+
let mut chunks =
118+
generate_read_chunks(&self.extent_map, self.bitmap.as_ref(), self.chunk_size);
119+
chunks.sort_by_key(|c| c.disk_offset);
120+
chunks
132121
};
133122

134123
// Build I/O operations with FRS tracking for inline parsing

crates/uffs-mft/src/reader/index_read.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,11 @@ impl MftReader {
620620

621621
let mut index = result?;
622622

623+
// Sort directory children first so tree metrics traverse in correct order.
624+
// Tree metrics computation depends on children being in sorted order to
625+
// produce accurate directory sizes and descendant counts.
626+
index.sort_directory_children();
627+
623628
// Compute tree metrics (directory sizes, descendant counts).
624629
// The legacy path gets this from `from_parsed_records()`, but the
625630
// inline path bypasses that, so we must call it explicitly.

0 commit comments

Comments
 (0)