1use std::borrow::Cow;
2use std::collections::BTreeMap;
3use std::path::PathBuf;
4
5use crate::date_time::format_header_date_value;
6use crate::decode::{ChunkEncoding, decode_chunk};
7use crate::{EwfError, Result};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum Format {
12 Ewf1,
14 Ewf2,
16}
17
18#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
19pub enum FormatProfile {
21 #[default]
23 Unknown,
24 EnCase1,
26 EnCase2,
28 EnCase3,
30 EnCase4,
32 EnCase5,
34 EnCase6,
36 EnCase7,
38 Smart,
40 FtkImager,
42 Linen5,
44 Linen6,
46 Linen7,
48 LogicalEnCase5,
50 LogicalEnCase6,
52 LogicalEnCase7,
54 Ewf2EnCase7,
56 Ewf2LogicalEnCase7,
58 Ewf,
60 Ewfx,
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub enum CompressionMethod {
67 None,
69 Zlib,
71 Bzip2,
73 Unknown(u16),
75}
76
77#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
78pub enum CompressionLevel {
80 Default,
82 #[default]
84 None,
85 Fast,
87 Best,
89 Unknown(i8),
91}
92
93impl CompressionLevel {
94 pub fn as_i8(self) -> i8 {
96 match self {
97 Self::Default => -1,
98 Self::None => 0,
99 Self::Fast => 1,
100 Self::Best => 2,
101 Self::Unknown(value) => value,
102 }
103 }
104
105 pub fn from_i8(value: i8) -> Self {
107 match value {
108 -1 => Self::Default,
109 0 => Self::None,
110 1 => Self::Fast,
111 2 => Self::Best,
112 value => Self::Unknown(value),
113 }
114 }
115}
116
117#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
118pub struct CompressionFlags {
120 pub empty_block: bool,
122 pub pattern_fill: bool,
124 pub unknown_bits: u8,
126}
127
128impl CompressionFlags {
129 const EMPTY_BLOCK: u8 = 0x01;
130 const PATTERN_FILL: u8 = 0x10;
131 const KNOWN_BITS: u8 = Self::EMPTY_BLOCK | Self::PATTERN_FILL;
132
133 pub fn from_bits(bits: u8) -> Self {
135 Self {
136 empty_block: bits & Self::EMPTY_BLOCK != 0,
137 pattern_fill: bits & Self::PATTERN_FILL != 0,
138 unknown_bits: bits & !Self::KNOWN_BITS,
139 }
140 }
141
142 pub fn bits(self) -> u8 {
144 let mut bits = self.unknown_bits;
145 if self.empty_block {
146 bits |= Self::EMPTY_BLOCK;
147 }
148 if self.pattern_fill {
149 bits |= Self::PATTERN_FILL;
150 }
151 bits
152 }
153}
154
155#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
156pub struct CompressionValues {
158 pub level: CompressionLevel,
160 pub flags: CompressionFlags,
162}
163
164#[derive(Debug, Clone, Copy, PartialEq, Eq)]
165pub enum MediaType {
167 Removable,
169 Fixed,
171 Optical,
173 SingleFiles,
175 Memory,
177 Unknown(u8),
179}
180
181#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
182pub struct MediaFlags {
184 pub physical: bool,
186 pub fastbloc: bool,
188 pub tableau: bool,
190}
191
192#[derive(Debug, Clone, Copy, PartialEq, Eq)]
193pub struct SegmentFileVersion {
195 pub major: u8,
197 pub minor: u8,
199}
200
201#[derive(Debug, Clone, Default, PartialEq, Eq)]
202pub struct MediaInfo {
204 pub sectors_per_chunk: Option<u64>,
206 pub bytes_per_sector: Option<u64>,
208 pub sector_count: Option<u64>,
210 pub chunk_count: Option<u64>,
212 pub error_granularity: Option<u64>,
214 pub set_identifier: Option<[u8; 16]>,
216 pub ewf2_segment_file_version: Option<SegmentFileVersion>,
218 pub compression_method: Option<CompressionMethod>,
220 pub compression_values: CompressionValues,
222 pub media_type: Option<MediaType>,
224 pub media_flags: MediaFlags,
226}
227
228#[derive(Debug, Clone, Copy, PartialEq, Eq)]
229pub enum OpenStrictness {
231 Strict,
233 Lenient,
235}
236
237#[derive(Debug, Clone, Copy, PartialEq, Eq)]
238pub struct OpenOptions {
240 pub strictness: OpenStrictness,
242 pub chunk_cache_size: usize,
244 pub read_zero_chunk_on_error: bool,
246 pub header_codepage: HeaderCodepage,
248 pub header_values_date_format: HeaderDateFormat,
250 pub maximum_open_handles: Option<usize>,
252}
253
254impl Default for OpenOptions {
255 fn default() -> Self {
256 Self {
257 strictness: OpenStrictness::Strict,
258 chunk_cache_size: 64,
259 read_zero_chunk_on_error: false,
260 header_codepage: HeaderCodepage::Ascii,
261 header_values_date_format: HeaderDateFormat::Ctime,
262 maximum_open_handles: None,
263 }
264 }
265}
266
267#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
268pub enum HeaderCodepage {
270 #[default]
272 Ascii,
273 Windows874,
275 Windows932,
277 Windows936,
279 Windows1250,
281 Windows1251,
283 Windows1252,
285 Windows1253,
287 Windows1254,
289 Windows1255,
291 Windows1256,
293 Windows1257,
295 Windows1258,
297}
298
299impl HeaderCodepage {
300 pub fn as_i32(self) -> i32 {
302 match self {
303 Self::Ascii => 20_127,
304 Self::Windows874 => 874,
305 Self::Windows932 => 932,
306 Self::Windows936 => 936,
307 Self::Windows1250 => 1250,
308 Self::Windows1251 => 1251,
309 Self::Windows1252 => 1252,
310 Self::Windows1253 => 1253,
311 Self::Windows1254 => 1254,
312 Self::Windows1255 => 1255,
313 Self::Windows1256 => 1256,
314 Self::Windows1257 => 1257,
315 Self::Windows1258 => 1258,
316 }
317 }
318
319 pub fn from_i32(value: i32) -> Option<Self> {
321 Some(match value {
322 20_127 => Self::Ascii,
323 874 => Self::Windows874,
324 932 => Self::Windows932,
325 936 => Self::Windows936,
326 1250 => Self::Windows1250,
327 1251 => Self::Windows1251,
328 1252 => Self::Windows1252,
329 1253 => Self::Windows1253,
330 1254 => Self::Windows1254,
331 1255 => Self::Windows1255,
332 1256 => Self::Windows1256,
333 1257 => Self::Windows1257,
334 1258 => Self::Windows1258,
335 _ => return None,
336 })
337 }
338}
339
340#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
341pub enum HeaderDateFormat {
343 DayMonth,
345 MonthDay,
347 Iso8601,
349 #[default]
351 Ctime,
352}
353
354impl HeaderDateFormat {
355 pub fn as_i32(self) -> i32 {
357 match self {
358 Self::DayMonth => 1,
359 Self::MonthDay => 2,
360 Self::Iso8601 => 3,
361 Self::Ctime => 4,
362 }
363 }
364
365 pub fn from_i32(value: i32) -> Option<Self> {
367 Some(match value {
368 1 => Self::DayMonth,
369 2 => Self::MonthDay,
370 3 => Self::Iso8601,
371 4 => Self::Ctime,
372 _ => return None,
373 })
374 }
375}
376
377#[derive(Debug, Clone, Default, PartialEq, Eq)]
378pub struct EwfMetadata {
380 pub case_number: Option<String>,
382 pub evidence_number: Option<String>,
384 pub examiner: Option<String>,
386 pub description: Option<String>,
388 pub notes: Option<String>,
390 pub acquisition_software: Option<String>,
392 pub acquisition_software_version: Option<String>,
394 pub os_version: Option<String>,
396 pub acquisition_date: Option<String>,
398 pub system_date: Option<String>,
400 pub password: Option<String>,
402 pub header_values: BTreeMap<String, String>,
404}
405
406impl EwfMetadata {
407 pub fn copy_header_values_from(&mut self, source: &EwfMetadata) {
409 *self = source.clone();
410 }
411
412 pub fn header_value(&self, identifier: &str) -> Option<&str> {
414 self.standard_header_value(identifier)
415 .or_else(|| self.header_values.get(identifier).map(String::as_str))
416 }
417
418 pub fn header_value_with_date_format(
420 &self,
421 identifier: &str,
422 date_format: HeaderDateFormat,
423 ) -> Option<Cow<'_, str>> {
424 let value = self.header_value(identifier)?;
425 if matches!(identifier, "acquiry_date" | "system_date") {
426 Some(format_header_date_value(value, date_format))
427 } else {
428 Some(Cow::Borrowed(value))
429 }
430 }
431
432 pub fn set_header_value(
434 &mut self,
435 identifier: &str,
436 value: impl Into<String>,
437 ) -> Option<String> {
438 let previous = self.header_value(identifier).map(str::to_owned);
439 let value = value.into();
440
441 self.header_values.remove(identifier);
442 match identifier {
443 "case_number" => self.case_number = Some(value),
444 "description" => self.description = Some(value),
445 "examiner_name" => self.examiner = Some(value),
446 "evidence_number" => self.evidence_number = Some(value),
447 "notes" => self.notes = Some(value),
448 "acquiry_date" => self.acquisition_date = Some(value),
449 "system_date" => self.system_date = Some(value),
450 "acquiry_operating_system" => self.os_version = Some(value),
451 "acquiry_software" => self.acquisition_software = Some(value),
452 "acquiry_software_version" => self.acquisition_software_version = Some(value),
453 "password" => self.password = Some(value),
454 _ => {
455 self.header_values.insert(identifier.to_string(), value);
456 }
457 }
458
459 previous
460 }
461
462 pub fn number_of_header_values(&self) -> usize {
464 let standard_count = STANDARD_HEADER_VALUE_IDENTIFIERS
465 .iter()
466 .filter(|identifier| self.header_value(identifier).is_some())
467 .count();
468 let generic_count = self
469 .header_values
470 .keys()
471 .filter(|identifier| !is_standard_header_value_identifier(identifier))
472 .count();
473 standard_count + generic_count
474 }
475
476 pub fn header_value_identifier(&self, index: usize) -> Option<&str> {
478 let mut remaining = index;
479 for identifier in STANDARD_HEADER_VALUE_IDENTIFIERS {
480 if self.header_value(identifier).is_some() {
481 if remaining == 0 {
482 return Some(identifier);
483 }
484 remaining -= 1;
485 }
486 }
487
488 for identifier in self.header_values.keys() {
489 if is_standard_header_value_identifier(identifier) {
490 continue;
491 }
492 if remaining == 0 {
493 return Some(identifier);
494 }
495 remaining -= 1;
496 }
497
498 None
499 }
500
501 fn standard_header_value(&self, identifier: &str) -> Option<&str> {
502 match identifier {
503 "case_number" => self.case_number.as_deref(),
504 "description" => self.description.as_deref(),
505 "examiner_name" => self.examiner.as_deref(),
506 "evidence_number" => self.evidence_number.as_deref(),
507 "notes" => self.notes.as_deref(),
508 "acquiry_date" => self.acquisition_date.as_deref(),
509 "system_date" => self.system_date.as_deref(),
510 "acquiry_operating_system" => self.os_version.as_deref(),
511 "acquiry_software" => self.acquisition_software.as_deref(),
512 "acquiry_software_version" => self.acquisition_software_version.as_deref(),
513 "password" => self.password.as_deref(),
514 _ => None,
515 }
516 }
517}
518
519const STANDARD_HEADER_VALUE_IDENTIFIERS: &[&str] = &[
520 "case_number",
521 "description",
522 "examiner_name",
523 "evidence_number",
524 "notes",
525 "acquiry_date",
526 "system_date",
527 "acquiry_operating_system",
528 "acquiry_software",
529 "acquiry_software_version",
530 "password",
531 "compression_level",
532 "model",
533 "serial_number",
534];
535
536fn is_standard_header_value_identifier(identifier: &str) -> bool {
537 STANDARD_HEADER_VALUE_IDENTIFIERS.contains(&identifier)
538}
539
540#[derive(Debug, Clone, Default, PartialEq, Eq)]
541pub struct StoredHashes {
543 pub md5: Option<[u8; 16]>,
545 pub sha1: Option<[u8; 20]>,
547 pub hash_values: BTreeMap<String, String>,
549}
550
551impl StoredHashes {
552 pub fn hash_value(&self, identifier: &str) -> Option<&str> {
554 self.hash_values.get(identifier).map(String::as_str)
555 }
556
557 pub fn set_hash_value(
559 &mut self,
560 identifier: impl Into<String>,
561 value: impl Into<String>,
562 ) -> Option<String> {
563 let identifier = identifier.into();
564 let value = value.into();
565 set_typed_hash_value(&identifier, &value, &mut self.md5, &mut self.sha1);
566 self.hash_values.insert(identifier, value)
567 }
568
569 pub fn number_of_hash_values(&self) -> usize {
571 self.hash_values.len()
572 }
573
574 pub fn hash_value_identifier(&self, index: usize) -> Option<&str> {
576 self.hash_values.keys().nth(index).map(String::as_str)
577 }
578}
579
580pub(crate) fn set_typed_hash_value(
581 identifier: &str,
582 value: &str,
583 md5: &mut Option<[u8; 16]>,
584 sha1: &mut Option<[u8; 20]>,
585) {
586 if identifier.eq_ignore_ascii_case("MD5") {
587 if let Some(parsed) = parse_hex_array(value) {
588 *md5 = Some(parsed);
589 }
590 } else if identifier.eq_ignore_ascii_case("SHA1")
591 && let Some(parsed) = parse_hex_array(value)
592 {
593 *sha1 = Some(parsed);
594 }
595}
596
597fn parse_hex_array<const N: usize>(text: &str) -> Option<[u8; N]> {
598 if text.len() != N * 2 {
599 return None;
600 }
601
602 let mut bytes = [0; N];
603 for (index, pair) in text.as_bytes().chunks_exact(2).enumerate() {
604 let high = hex_nibble(pair[0])?;
605 let low = hex_nibble(pair[1])?;
606 bytes[index] = (high << 4) | low;
607 }
608 Some(bytes)
609}
610
611fn hex_nibble(value: u8) -> Option<u8> {
612 match value {
613 b'0'..=b'9' => Some(value - b'0'),
614 b'a'..=b'f' => Some(value - b'a' + 10),
615 b'A'..=b'F' => Some(value - b'A' + 10),
616 _ => None,
617 }
618}
619
620#[derive(Debug, Clone, PartialEq, Eq)]
621pub struct AcquisitionError {
623 pub first_sector: u64,
625 pub sector_count: u64,
627}
628
629#[derive(Debug, Clone, PartialEq, Eq)]
630pub struct SectorRange {
632 pub first_sector: u64,
634 pub sector_count: u64,
636}
637
638#[derive(Debug, Clone, PartialEq, Eq)]
639pub struct MemoryExtent {
641 pub start_page: u64,
643 pub page_count: u64,
645}
646
647#[derive(Debug, Clone, Copy, PartialEq, Eq)]
648pub enum DataChunkEncoding {
650 Raw,
652 Zlib,
654 Bzip2,
656 PatternFill(u64),
658}
659
660#[derive(Debug, Clone, PartialEq, Eq)]
661pub struct DataChunk {
663 pub chunk_index: u64,
665 pub logical_offset: u64,
667 pub logical_size: usize,
669 pub encoded_size: u64,
671 pub encoding: DataChunkEncoding,
673 pub corrupted: bool,
675 pub data: Vec<u8>,
677}
678
679impl DataChunk {
680 pub fn is_corrupted(&self) -> bool {
682 self.corrupted
683 }
684
685 pub fn read_buffer(&self, buffer: &mut [u8]) -> Result<usize> {
687 Ok(copy_to_buffer(&self.data, buffer))
688 }
689
690 pub fn write_buffer(&mut self, buffer: &[u8]) -> Result<usize> {
696 self.data.clear();
697 self.data.extend_from_slice(buffer);
698 self.logical_size = buffer.len();
699 self.encoded_size = u64::try_from(buffer.len())
700 .map_err(|_| EwfError::Malformed("data chunk buffer size overflow".into()))?;
701 self.encoding = DataChunkEncoding::Raw;
702 self.corrupted = false;
703 Ok(buffer.len())
704 }
705}
706
707#[derive(Debug, Clone, PartialEq, Eq)]
708pub struct EncodedDataChunk {
710 pub chunk_index: u64,
712 pub logical_offset: u64,
714 pub logical_size: usize,
716 pub encoded_size: u64,
718 pub encoding: DataChunkEncoding,
720 pub has_checksum: bool,
722 pub data: Vec<u8>,
724}
725
726impl EncodedDataChunk {
727 pub fn read_buffer(&self, buffer: &mut [u8]) -> Result<usize> {
733 if self.has_checksum && self.encoding == DataChunkEncoding::Raw {
734 validate_raw_data_chunk_checksum(&self.data, self.logical_size)?;
735 }
736
737 let decoded = decode_chunk(
738 &self.data,
739 data_chunk_encoding(self.encoding),
740 self.logical_size,
741 )?;
742 Ok(copy_to_buffer(&decoded, buffer))
743 }
744}
745
746fn copy_to_buffer(data: &[u8], buffer: &mut [u8]) -> usize {
747 let read_size = data.len().min(buffer.len());
748 buffer[..read_size].copy_from_slice(&data[..read_size]);
749 read_size
750}
751
752fn data_chunk_encoding(encoding: DataChunkEncoding) -> ChunkEncoding {
753 match encoding {
754 DataChunkEncoding::Raw => ChunkEncoding::Raw,
755 DataChunkEncoding::Zlib => ChunkEncoding::Zlib,
756 DataChunkEncoding::Bzip2 => ChunkEncoding::Bzip2,
757 DataChunkEncoding::PatternFill(pattern) => ChunkEncoding::PatternFill(pattern),
758 }
759}
760
761fn validate_raw_data_chunk_checksum(encoded: &[u8], logical_size: usize) -> Result<()> {
762 let checksum_end = logical_size
763 .checked_add(4)
764 .ok_or_else(|| EwfError::Malformed("raw chunk checksum offset overflow".into()))?;
765 if encoded.len() < checksum_end {
766 return Err(EwfError::Malformed(
767 "raw chunk checksum trailer is missing".into(),
768 ));
769 }
770
771 let stored = u32::from_le_bytes(
772 encoded[logical_size..checksum_end]
773 .try_into()
774 .expect("raw chunk checksum slice has fixed size"),
775 );
776 let calculated = adler32(&encoded[..logical_size]);
777 if stored != calculated {
778 return Err(EwfError::Malformed("raw chunk checksum mismatch".into()));
779 }
780
781 Ok(())
782}
783
784fn adler32(data: &[u8]) -> u32 {
785 const MOD_ADLER: u32 = 65_521;
786 let mut a = 1_u32;
787 let mut b = 0_u32;
788 for byte in data {
789 a = (a + u32::from(*byte)) % MOD_ADLER;
790 b = (b + a) % MOD_ADLER;
791 }
792 (b << 16) | a
793}
794
795#[derive(Debug, Clone, Copy, PartialEq, Eq)]
796pub enum SingleFileEntryType {
798 File,
800 Directory,
802 Unknown,
804}
805
806#[derive(Debug, Clone, Copy, PartialEq, Eq)]
807pub struct SingleFileExtent {
809 pub data_offset: u64,
811 pub data_size: u64,
813 pub sparse: bool,
815}
816
817#[derive(Debug, Clone, Default, PartialEq, Eq)]
818pub struct SingleFileSource {
820 pub identifier: Option<i32>,
822 pub name: Option<String>,
824 pub evidence_number: Option<String>,
826 pub location: Option<String>,
828 pub device_guid: Option<String>,
830 pub primary_device_guid: Option<String>,
832 pub drive_type: Option<char>,
834 pub manufacturer: Option<String>,
836 pub model: Option<String>,
838 pub serial_number: Option<String>,
840 pub domain: Option<String>,
842 pub ip_address: Option<String>,
844 pub mac_address: Option<String>,
846 pub size: Option<u64>,
848 pub logical_offset: Option<i64>,
850 pub physical_offset: Option<i64>,
852 pub acquisition_time: Option<i64>,
854 pub md5: Option<String>,
856 pub sha1: Option<String>,
858}
859
860#[derive(Debug, Clone, Default, PartialEq, Eq)]
861pub struct SingleFileSubject {
863 pub identifier: Option<u32>,
865 pub name: Option<String>,
867}
868
869#[derive(Debug, Clone, Default, PartialEq, Eq)]
870pub struct SingleFilePermission {
872 pub name: Option<String>,
874 pub identifier: Option<String>,
876 pub property_type: Option<u32>,
878 pub access_mask: Option<u32>,
880 pub ace_flags: Option<u32>,
882}
883
884#[derive(Debug, Clone, Default, PartialEq, Eq)]
885pub struct SingleFilePermissionGroup {
887 pub name: Option<String>,
889 pub identifier: Option<String>,
891 pub property_type: Option<u32>,
893 pub access_mask: Option<u32>,
895 pub ace_flags: Option<u32>,
897 pub permissions: Vec<SingleFilePermission>,
899}
900
901#[derive(Debug, Clone, Default, PartialEq, Eq)]
902pub struct SingleFileAttribute {
904 pub name: Option<String>,
906 pub value: Option<String>,
908}
909
910#[derive(Debug, Clone, Default, PartialEq, Eq)]
911pub struct SingleFileEntry {
913 pub identifier: Option<u64>,
915 pub file_entry_type: Option<SingleFileEntryType>,
917 pub flags: Option<u32>,
919 pub guid: Option<String>,
921 pub name: Option<String>,
923 pub short_name: Option<String>,
925 pub size: Option<u64>,
927 pub logical_offset: Option<i64>,
929 pub physical_offset: Option<i64>,
931 pub duplicate_data_offset: Option<i64>,
933 pub source_identifier: Option<i32>,
935 pub subject_identifier: Option<u32>,
937 pub permission_group_index: Option<i32>,
939 pub record_type: Option<u32>,
941 pub creation_time: Option<i64>,
943 pub modification_time: Option<i64>,
945 pub access_time: Option<i64>,
947 pub entry_modification_time: Option<i64>,
949 pub deletion_time: Option<i64>,
951 pub md5: Option<String>,
953 pub sha1: Option<String>,
955 pub extents: Vec<SingleFileExtent>,
957 pub attributes: Vec<SingleFileAttribute>,
959 pub children: Vec<SingleFileEntry>,
961}
962
963#[derive(Debug, Clone, Default, PartialEq, Eq)]
964pub struct SingleFilesInfo {
966 pub data_size: u64,
968 pub root: SingleFileEntry,
970 pub sources: Vec<SingleFileSource>,
972 pub subjects: Vec<SingleFileSubject>,
974 pub permission_groups: Vec<SingleFilePermissionGroup>,
976}
977
978#[derive(Debug, Clone, Default, PartialEq, Eq)]
979pub struct SingleFilesAuxTables {
981 pub table_0x21_entries: Vec<u64>,
983 pub md5_hashes: Vec<[u8; 16]>,
985 pub table_0x23_entries: Vec<u64>,
987}
988
989impl SingleFilesAuxTables {
990 pub fn is_empty(&self) -> bool {
992 self.table_0x21_entries.is_empty()
993 && self.md5_hashes.is_empty()
994 && self.table_0x23_entries.is_empty()
995 }
996}
997
998#[derive(Debug, Clone, PartialEq, Eq)]
999pub struct ImageInfo {
1001 pub format: Format,
1003 pub format_profile: FormatProfile,
1005 pub segment_count: usize,
1007 pub segment_paths: Vec<PathBuf>,
1009 pub chunk_size: u64,
1011 pub logical_size: u64,
1013 pub acquisition_complete: bool,
1015 pub header_codepage: HeaderCodepage,
1017 pub header_values_date_format: HeaderDateFormat,
1019 pub media: MediaInfo,
1021 pub metadata: EwfMetadata,
1023 pub stored_hashes: StoredHashes,
1025 pub acquisition_errors: Vec<AcquisitionError>,
1027 pub memory_extents: Vec<MemoryExtent>,
1029 pub single_files: Option<SingleFilesInfo>,
1031 pub ewf2_single_files_tables: SingleFilesAuxTables,
1033 pub ewf2_increment_data: Vec<Vec<u8>>,
1035 pub ewf2_final_information: Option<Vec<u8>>,
1037 pub ewf2_restart_data: Option<String>,
1039 pub ewf2_analytical_data: Option<String>,
1041 pub sessions: Vec<SectorRange>,
1043 pub tracks: Vec<SectorRange>,
1045}
1046
1047#[cfg(feature = "verify")]
1048#[derive(Debug, Clone, PartialEq, Eq)]
1049pub struct VerifyResult {
1051 pub computed_md5: Option<[u8; 16]>,
1053 pub computed_sha1: Option<[u8; 20]>,
1055 pub md5_match: Option<bool>,
1057 pub sha1_match: Option<bool>,
1059}