Skip to content

ebrig/ewf-image

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ewf-image

A pure-Rust library for reading and writing Expert Witness Format (EWF) forensic images.

Crates.io Documentation License

ewf-image project banner

ewf-image reads and writes the EWF/E01 image formats used in digital forensics — the physical, logical, SMART, and EWF2 families — with no unsafe code and no external tool dependencies. It exposes focused APIs for raw stream access, metadata inspection, logical single-file catalogs, and EWF output creation.

Highlights

  • Full format coverage. Reads and writes EWF1 (.E01, .L01, .S01) and EWF2 (.Ex01, .Lx01), including raw, zlib, BZip2, and pattern-fill chunks.
  • Streaming reads. Immutable Image handles offer positioned reads, Read + Seek cursors, and bounded decoded-chunk caching.
  • Rich metadata. Inspect acquisition headers, stored MD5/SHA1 hashes, acquisition errors, sessions, and tracks.
  • Integrity verification. Recompute and compare stored hashes with a single Image::verify() call.
  • Flexible writing. Compression, segment splitting, secondary/shadow mirroring, authored metadata, and resume-by-rewrite.
  • Safe by construction. #![forbid(unsafe_code)], linted under clippy pedantic/nursery, and tested against external EWF tool oracles.

This release ships the Rust library. Command-line, mount, and service runtime layers are planned but not yet implemented.

Installation

[dependencies]
ewf-image = "0.2"

The default verify feature enables streamed MD5/SHA1 verification through Image::verify(). Drop it if you don't need that API:

[dependencies]
ewf-image = { version = "0.2", default-features = false }

Quick Start

Open an image, inspect its format, and read from the logical media stream:

use std::io::Read;

let image = ewf_image::Image::open("case.E01")?;
let info = image.info();

println!("{:?}: {} bytes across {} segment(s)",
    info.format, info.logical_size, info.segment_count);

// Read the first sector from the cursor...
let mut first_sector = vec![0; 512];
image.cursor().read_exact(&mut first_sector)?;

// ...or read directly at any offset.
let mut sector_at_offset = vec![0; 512];
image.read_at(&mut sector_at_offset, 4096)?;

Read forensic metadata and verify stored hashes:

let image = ewf_image::Image::open("case.E01")?;

if let Some(case_number) = image.header_value("case_number") {
    println!("case: {case_number}");
}

let result = image.verify()?;
println!("MD5 match: {:?}", result.md5_match);

Reader tuning and diagnostics

Reader caches are shared by every clone and cursor created from an Image. The decoded-chunk cache defaults to 64 chunks, and table entries use a bounded 4 MiB page cache. Configure byte limits and opt into cumulative diagnostics with OpenOptions:

let options = ewf_image::OpenOptions::default()
    .with_chunk_cache_size_bytes(32 * 1024 * 1024)
    .with_table_entry_cache_size_bytes(8 * 1024 * 1024)
    .with_maximum_open_handles(Some(32))
    .with_reader_statistics(true);

let image = ewf_image::Image::open_with_options("case.E01", options)?;
let before = image.reader_statistics().expect("statistics enabled");

// Perform the reads being measured.
let mut sector = [0; 512];
image.read_at(&mut sector, 0)?;

let delta = image
    .reader_statistics()
    .expect("statistics enabled")
    .saturating_delta(before);
println!("chunk cache misses: {}", delta.chunk_cache_misses());

let cache = image.reader_cache_info();
println!(
    "table cache: {} / {} bytes",
    cache.table_entry_cache_current_bytes(),
    cache.table_entry_cache_capacity_bytes()
);

Statistics collection is disabled by default. reader_statistics() returns None unless it was enabled when the image was opened. See Migrating to 0.2 for the OpenOptions builder migration.

Write a new compressed EWF2 image from raw bytes:

use std::fs::File;

let mut input = File::open("disk.raw")?;

let mut options = ewf_image::WriteOptions::default();
options.format = ewf_image::WriteFormat::Ewf2Physical;
options.compression = ewf_image::WriteCompression::Zlib;
options.metadata.set_header_value("case_number", "CASE-001");

let mut writer = ewf_image::EwfWriter::create("case.Ex01", options)?;
std::io::copy(&mut input, &mut writer)?;
let result = writer.finish()?;

println!("wrote {} segment(s)", result.segment_paths.len());

See examples/ for complete, runnable programs, including reading, raw export, logical inspection, and mirrored secondary output.

Supported Formats

Family Read Write Notes
EWF1 physical .E01 / EVF Segment discovery, raw/zlib chunks, metadata, hashes, acquisition errors, sessions, tracks, and split output.
EWF1 logical .L01 / LVF Logical single-file catalogs and path lookup.
EWF1 SMART .S01 SMART media profile handling.
EWF2 physical .Ex01 Raw, zlib, BZip2, and pattern-fill chunks; EWF2 metadata, memory extents, and split output.
EWF2 logical .Lx01 Logical single-file catalogs and auxiliary single-file tables.

The writer additionally supports SMART output, compression, segment splitting, secondary/shadow mirroring, authored metadata, stored hashes, incomplete EWF1 output, and resume-by-rewrite.

Limitations

  • Encrypted EWF2 images are detected and rejected; decryption is not yet implemented.
  • Encrypted writing is not yet implemented.
  • Base-plus-overlay delta/shadow images are not yet implemented.
  • EWF2 BZip2 chunks are supported locally, but some external EWF tools cannot generate or export BZip2 fixtures, so external oracle coverage for them is tracked separately.

See docs/limitations.md for details.

Testing

Compatibility is covered by local synthetic fixtures, writer round trips, and optional external EWF tool oracle tests. The routine checks are:

cargo fmt --check
cargo test --no-default-features
cargo test --all-features
cargo clippy --all-targets --all-features -- -D warnings
cargo check --examples --all-features
RUSTDOCFLAGS="-D warnings" cargo doc --all-features --no-deps

External corpus and tool oracle checks are ignored by default, since they require local fixtures and installed EWF tools. See docs/testing.md and docs/compatibility.md.

Documentation

Contributing

Contributions are welcome. See CONTRIBUTING.md to get started, and SECURITY.md for reporting security issues.

License

Licensed under the Apache License, Version 2.0. See LICENSE.

About

Pure-Rust library for reading and writing EWF/E01 forensic disk images.

Topics

Resources

License

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages