Skip to main content

Exhume exFAT as a library

This document provides guidance on how to use the exhume_exfat crate as a library in your Rust projects. You will learn how to integrate the library, its basic usage, and a sample minimalist main code example. Exhume exFAT is also part of Exhume FileSystem.

Install

Create a project:

cargo new exfat_test
cd exfat_test/src

📦 Adding Exhume ExtFS as a Dependency

To use the exhume_exfat library in your Rust project, you need to add it as a dependency in your Cargo.toml file. Below is the sections you need to include:

[dependencies]
exhume_body = "=0.3.1"
exhume_exfat= ">=0.1.1"

# Required crates to log events
log = "0.4.25"
env_logger = "0.11.6"

🛠️ Basic Usage

Below is a sample code illustrating how to use exhume_exfat as a library in the newly created Rust project.

use exhume_body::{Body, BodySlice};
use exhume_exfat::ExFatFS;

fn main() {
// Connect our log env
env_logger::Builder::new()
.filter_level(log::LevelFilter::Info)
.init();

// We create an exhume body object to fetch our image.
let mut body = Body::new("/path/to/evidence.E01".to_string(), "auto");

// These values (offset & size in sectors) are typically discovered with "exhume partitions"
let offset: u64 = 0x100000; // exFAT partition start (bytes)
let part_sectors: u64 = 0x9c00000; // exFAT partition size (in sectors)
let partition_size = part_sectors * body.get_sector_size() as u64;

// Create a slice over the exFAT partition
let mut slice = match BodySlice::new(&mut body, offset, partition_size) {
Ok(sl) => sl,
Err(e) => {
eprintln!("Could not create BodySlice: {}", e);
return;
}
};

// Open the filesystem
let mut fs = match ExFatFS::new(&mut slice) {
Ok(v) => v,
Err(e) => {
eprintln!("Couldn't open exFAT: {}", e);
return;
}
};

// ---- Examples of basic usage ----

// 1) Print BPB / super info (human friendly table)
println!("{}", fs.bpb.to_string());

// 2) List root directory entries with their fake inode numbers
match fs.list_root_with_inodes() {
Ok(list) => {
println!("\n# Root directory:");
for (inode, f) in list {
println!(
"inode=0x{:016x} {:>10} cluster {:>8} {}{}",
inode,
f.size,
f.first_cluster,
f.name,
if f.is_dir() { " /" } else { "" }
);
}
}
Err(e) => eprintln!("Root listing failed: {}", e),
}

// 3) Resolve a path to an inode and print its metadata
// (change the path below to something that exists on your image)
let sample_path = "Documents/Report.txt";
match fs.resolve_path_to_inode_num(sample_path) {
Ok((ino, inode)) => {
println!("\n# Resolved '{}':", sample_path);
println!("Fake inode: 0x{:016x}", ino);
println!("{}", inode.to_string());

// 4) If it's a regular file, read its content
if inode.is_regular_file() {
match fs.read_inode(&inode) {
Ok(bytes) => {
println!("Read {} bytes from '{}'", bytes.len(), sample_path);
// Do something with `bytes` (hash, carve, save, …)
}
Err(e) => eprintln!("read_inode failed: {}", e),
}
}
}
Err(e) => eprintln!("resolve_path_to_inode_num('{}') failed: {}", sample_path, e),
}
}