#![allow(dead_code)] use md5::{Digest as _, Md5};
pub const EVF_SIGNATURE: [u8; 8] = [0x45, 0x56, 0x46, 0x09, 0x0d, 0x0a, 0xff, 0x00];
pub const FILE_HEADER_SIZE: usize = 13;
pub const SECTION_DESCRIPTOR_SIZE: usize = 76;
pub const VOLUME_DATA_SIZE: usize = 94;
pub const HASH_DATA_SIZE: usize = 16;
pub fn adler32(data: &[u8]) -> u32 {
const MOD: u32 = 65521;
let mut s1: u32 = 1;
let mut s2: u32 = 0;
for &b in data {
s1 = (s1 + u32::from(b)) % MOD;
s2 = (s2 + s1) % MOD;
}
(s2 << 16) | s1
}
pub fn make_section_descriptor(section_type: &str, next: u64, size: u64) -> Vec<u8> {
let mut buf = vec![0u8; SECTION_DESCRIPTOR_SIZE];
let name = section_type.as_bytes();
let copy_len = name.len().min(16);
buf[..copy_len].copy_from_slice(&name[..copy_len]);
buf[16..24].copy_from_slice(&next.to_le_bytes());
buf[24..32].copy_from_slice(&size.to_le_bytes());
let crc = adler32(&buf[..72]);
buf[72..76].copy_from_slice(&crc.to_le_bytes());
buf
}
pub struct E01Builder {
pub virtual_disk_size: u64,
pub sectors_per_chunk: u32,
pub bytes_per_sector: u32,
pub segment_number: u16,
pub signature_override: Option<[u8; 8]>,
pub corrupt_volume_crc: bool,
pub omit_done: bool,
pub insert_gap: bool,
pub md5_override: Option<[u8; 16]>,
pub table_chunk_count_override: Option<u32>,
pub volume_sectors_per_chunk_override: Option<u32>,
pub volume_bytes_per_sector_override: Option<u32>,
pub volume_sector_count_override: Option<u64>,
pub omit_volume: bool,
pub break_chain: bool,
pub volume_type_override: Option<String>,
pub segment_number_override: Option<u16>,
pub omit_hash: bool,
pub table_base_offset_override: Option<u64>,
pub insert_zero_gap: bool,
}
impl E01Builder {
pub fn new(virtual_disk_size: u64) -> Self {
Self {
virtual_disk_size,
sectors_per_chunk: 64,
bytes_per_sector: 512,
segment_number: 1,
signature_override: None,
corrupt_volume_crc: false,
omit_done: false,
insert_gap: false,
md5_override: None,
table_chunk_count_override: None,
volume_sectors_per_chunk_override: None,
volume_bytes_per_sector_override: None,
volume_sector_count_override: None,
omit_volume: false,
break_chain: false,
volume_type_override: None,
segment_number_override: None,
omit_hash: false,
table_base_offset_override: None,
insert_zero_gap: false,
}
}
pub fn with_signature(mut self, sig: [u8; 8]) -> Self {
self.signature_override = Some(sig);
self
}
pub fn with_segment_number(mut self, n: u16) -> Self {
self.segment_number_override = Some(n);
self
}
pub fn with_corrupt_volume_crc(mut self) -> Self {
self.corrupt_volume_crc = true;
self
}
pub fn with_broken_chain(mut self) -> Self {
self.break_chain = true;
self
}
pub fn with_gap(mut self) -> Self {
self.insert_gap = true;
self
}
pub fn with_omit_volume(mut self) -> Self {
self.omit_volume = true;
self
}
pub fn with_volume_type(mut self, t: &str) -> Self {
self.volume_type_override = Some(t.to_string());
self
}
pub fn with_omit_done(mut self) -> Self {
self.omit_done = true;
self
}
pub fn with_volume_sectors_per_chunk(mut self, spc: u32) -> Self {
self.volume_sectors_per_chunk_override = Some(spc);
self
}
pub fn with_volume_sector_count(mut self, sc: u64) -> Self {
self.volume_sector_count_override = Some(sc);
self
}
pub fn with_volume_bytes_per_sector(mut self, bps: u32) -> Self {
self.volume_bytes_per_sector_override = Some(bps);
self
}
pub fn with_table_chunk_count(mut self, n: u32) -> Self {
self.table_chunk_count_override = Some(n);
self
}
pub fn with_md5(mut self, md5: [u8; 16]) -> Self {
self.md5_override = Some(md5);
self
}
pub fn with_omit_hash(mut self) -> Self {
self.omit_hash = true;
self
}
pub fn with_table_base_offset(mut self, offset: u64) -> Self {
self.table_base_offset_override = Some(offset);
self
}
pub fn with_zero_gap(mut self) -> Self {
self.insert_zero_gap = true;
self
}
pub fn build(self) -> Vec<u8> {
let spc = self.sectors_per_chunk;
let bps = self.bytes_per_sector;
let chunk_size = u64::from(spc) * u64::from(bps);
let chunk_count = self.virtual_disk_size.div_ceil(chunk_size) as u32;
let sector_count = u64::from(chunk_count) * u64::from(spc);
let header_section_data_size: u64 = 1; let volume_section_size: u64 = (SECTION_DESCRIPTOR_SIZE + VOLUME_DATA_SIZE) as u64;
let table_data_size: u64 = 24 + 4 * u64::from(chunk_count);
let table_section_size = SECTION_DESCRIPTOR_SIZE as u64 + table_data_size;
let sectors_data_size = u64::from(chunk_count) * chunk_size;
let sectors_section_size = SECTION_DESCRIPTOR_SIZE as u64 + sectors_data_size;
let hash_section_size: u64 = (SECTION_DESCRIPTOR_SIZE + HASH_DATA_SIZE) as u64;
let done_section_size: u64 = SECTION_DESCRIPTOR_SIZE as u64;
let mut pos: u64 = FILE_HEADER_SIZE as u64;
let _ewf_header_desc_off = pos;
let ewf_header_section_size = SECTION_DESCRIPTOR_SIZE as u64 + header_section_data_size;
pos += ewf_header_section_size;
let volume_desc_off = pos;
if !self.omit_volume {
pos += volume_section_size;
}
let table_desc_off = pos;
pos += table_section_size;
let sectors_desc_off = pos;
pos += sectors_section_size;
if self.insert_gap {
pos += 16;
}
if self.insert_zero_gap {
pos += 16;
}
let hash_desc_off = pos;
if !self.omit_hash {
pos += hash_section_size;
}
let done_desc_off = pos;
let mut buf: Vec<u8> = Vec::new();
let sig = self.signature_override.unwrap_or(EVF_SIGNATURE);
buf.extend_from_slice(&sig);
buf.push(1u8); let seg = self.segment_number_override.unwrap_or(self.segment_number);
buf.extend_from_slice(&seg.to_le_bytes());
buf.extend_from_slice(&0u16.to_le_bytes());
let next_after_ewf_header = if !self.omit_volume {
volume_desc_off
} else {
table_desc_off
};
buf.extend_from_slice(&make_section_descriptor(
"header",
next_after_ewf_header,
ewf_header_section_size,
));
buf.push(0u8);
if !self.omit_volume {
let vol_type = self.volume_type_override.as_deref().unwrap_or("volume");
let mut desc = make_section_descriptor(vol_type, table_desc_off, volume_section_size);
if self.corrupt_volume_crc {
desc[72] ^= 0xFF;
}
buf.extend_from_slice(&desc);
let mut vol = vec![0u8; VOLUME_DATA_SIZE];
vol[0..4].copy_from_slice(&1u32.to_le_bytes());
vol[4..8].copy_from_slice(&chunk_count.to_le_bytes());
let spc_vol = self.volume_sectors_per_chunk_override.unwrap_or(spc);
vol[8..12].copy_from_slice(&spc_vol.to_le_bytes());
let bps_vol = self.volume_bytes_per_sector_override.unwrap_or(bps);
vol[12..16].copy_from_slice(&bps_vol.to_le_bytes());
let sc_vol = self.volume_sector_count_override.unwrap_or(sector_count);
vol[16..24].copy_from_slice(&sc_vol.to_le_bytes());
buf.extend_from_slice(&vol);
}
let table_chunk_count = self.table_chunk_count_override.unwrap_or(chunk_count);
buf.extend_from_slice(&make_section_descriptor(
"table",
sectors_desc_off,
table_section_size,
));
let mut tbl_hdr = vec![0u8; 24];
tbl_hdr[0..4].copy_from_slice(&table_chunk_count.to_le_bytes()); let sectors_data_start = sectors_desc_off + SECTION_DESCRIPTOR_SIZE as u64;
let tbl_base = self
.table_base_offset_override
.unwrap_or(sectors_data_start);
tbl_hdr[8..16].copy_from_slice(&tbl_base.to_le_bytes());
let tbl_crc = adler32(&tbl_hdr[..16]);
tbl_hdr[16..20].copy_from_slice(&tbl_crc.to_le_bytes());
buf.extend_from_slice(&tbl_hdr);
for i in 0..table_chunk_count {
let offset = i as u64 * chunk_size;
let entry = (offset as u32) & 0x7FFF_FFFF; buf.extend_from_slice(&entry.to_le_bytes());
}
let sectors_next = if self.omit_hash {
done_desc_off
} else if self.insert_gap {
hash_desc_off + 16
} else {
hash_desc_off
};
buf.extend_from_slice(&make_section_descriptor(
"sectors",
sectors_next,
sectors_section_size,
));
let sectors_data = vec![0u8; sectors_data_size as usize];
buf.extend_from_slice(§ors_data);
if self.insert_gap {
buf.extend_from_slice(&[
0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE, 0x13, 0x37, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01,
]);
}
if self.insert_zero_gap {
buf.extend_from_slice(&[0u8; 16]);
}
if !self.omit_hash {
let next_after_hash = if self.omit_done {
0u64
} else if self.break_chain {
buf.len() as u64 + 0x0010_0000 } else {
done_desc_off
};
buf.extend_from_slice(&make_section_descriptor(
"hash",
next_after_hash,
hash_section_size,
));
let md5 = compute_md5(§ors_data);
let stored_md5 = self.md5_override.unwrap_or(md5);
buf.extend_from_slice(&stored_md5);
}
if !self.omit_done {
let done_next = done_desc_off;
buf.extend_from_slice(&make_section_descriptor(
"done",
done_next,
done_section_size,
));
}
buf
}
}
fn compute_md5(data: &[u8]) -> [u8; 16] {
Md5::digest(data).into()
}