btrfs-forensic¶
A from-scratch btrfs reader and a graded anomaly auditor — walk the chunk tree, B-trees, and FS-tree of a btrfs image over any byte source, then turn its copy-on-write history into evidence: stale backup roots, csum-mismatched tree blocks, orphaned inodes, and deleted files still carvable from an older generation's FS-tree.
Two crates, one workspace:
btrfs-core— the reader: superblock +sys_chunk_arraybootstrap, chunk-tree logical→physical mapping, B-tree node navigation, FS-tree inode / directory / path resolution, andEXTENT_DATA→ file content with always-compiled zlib / LZO / zstd decompression, over any byte slice. Nounsafe, no C bindings.btrfs-forensic— the auditor: turns parsed btrfs structures into severity-gradedforensicnomicon::report::Findings, and recovers CoW-deleted files, so a btrfs volume's anomalies aggregate uniformly with the partition and container layers.
Audit a btrfs image in 30 seconds¶
use btrfs_forensic::audit_findings;
// Feed it the raw image bytes; get back graded findings.
for finding in audit_findings(&image_bytes, "btrfs") {
println!("[{:?}] {} — {}", finding.severity, finding.code, finding.note);
// e.g. [Some(High)] BTRFS-BACKUP-ROOT-DIVERGENCE — backup root gen 42 > committed 41 …
}
audit_findings parses the superblock, chunk tree, and FS-tree in place and grades what it finds. A structurally invalid image yields no findings (corruption is surfaced as its own finding or by the carver, never a panic).
The anomaly codes¶
Each finding is an observation ("consistent with …"); the examiner draws the conclusions. Codes are a stable, published contract.
| Code | Severity | What it observes |
|---|---|---|
BTRFS-SUPERBLOCK-CRC-MISMATCH |
High | The superblock's own crc32c does not verify — corruption or post-write tampering |
BTRFS-CRC-MISMATCH |
High | A metadata node/leaf whose stored crc32c does not verify |
BTRFS-BACKUP-ROOT-DIVERGENCE |
High | A btrfs_root_backup entry inconsistent with the committed generation — consistent with rollback / tampered backup roots |
BTRFS-IMPOSSIBLE-GEOMETRY |
High | A root/geometry field beyond what the image can hold — an allocation-bomb / corruption guard |
BTRFS-ORPHANED-INODE |
Medium | An ORPHAN_ITEM in the FS_TREE — an inode unlinked while open / pending cleanup, a recovery lead |
Deleted-file recovery is separate: recover_deleted(&image) walks an older generation's FS_TREE (reached through a btrfs_root_backup entry), diffs it against the current FS_TREE, and returns each carved RecoveredFile (name, inode, generation, size, content, and the content's sha256 recovery gate).
The reader: navigate an image¶
btrfs-core reads a btrfs image over any byte slice:
use btrfs_core::{Superblock, ChunkMap, read_by_path_content};
let sb = Superblock::parse(&image[65536..65536 + 4096])?;
let map = ChunkMap::walk(&image, &sb)?;
let bytes = read_by_path_content(&image, &sb, &map, "etc/hostname")?;
# Ok::<(), btrfs_core::BtrfsError>(())
The bare crate name btrfs on crates.io is an unrelated live-FS ioctl wrapper, so this on-disk reader publishes as btrfs-core and imports as btrfs_core.
Trust but verify¶
#![forbid(unsafe_code)]in both crates — nounsafe, no C bindings.- Panic-free — every integer/length/offset field is read through bounds-checked helpers; a malformed image degrades to an empty/typed result, never a panic.
- Fuzzed — one
cargo-fuzztarget per parsed structure (superblock, node, chunk, fstree, extent, crc) plus afuzz_forensictarget driving the fullaudit_image/recover_deletedpipeline. See Validation. - Tier-1 validated — the reader is checked against a real Fedora Cloud btrfs filesystem whose ground truth comes from btrfs-progs' own
dump-super -f/dump-tree, a wholly separate implementation. See Validation.
Privacy Policy · Terms of Service · © 2026 Security Ronin Ltd.