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 {
242 strictness: OpenStrictness,
244 chunk_cache_capacity: ChunkCacheCapacity,
246 table_entry_cache_size_bytes: usize,
248 reader_statistics: bool,
250 read_zero_chunk_on_error: bool,
252 header_codepage: HeaderCodepage,
254 header_values_date_format: HeaderDateFormat,
256 maximum_open_handles: Option<usize>,
258}
259
260#[derive(Debug, Clone, Copy, PartialEq, Eq)]
261pub enum ChunkCacheCapacity {
263 Chunks(usize),
265 Bytes(usize),
267}
268
269impl Default for OpenOptions {
270 fn default() -> Self {
271 Self {
272 strictness: OpenStrictness::Strict,
273 chunk_cache_capacity: ChunkCacheCapacity::Chunks(64),
274 table_entry_cache_size_bytes: 4 * 1024 * 1024,
275 reader_statistics: false,
276 read_zero_chunk_on_error: false,
277 header_codepage: HeaderCodepage::Ascii,
278 header_values_date_format: HeaderDateFormat::Ctime,
279 maximum_open_handles: None,
280 }
281 }
282}
283
284impl OpenOptions {
285 pub fn strictness(&self) -> OpenStrictness {
287 self.strictness
288 }
289
290 #[must_use]
292 pub fn with_strictness(mut self, strictness: OpenStrictness) -> Self {
293 self.strictness = strictness;
294 self
295 }
296
297 pub fn chunk_cache_capacity(&self) -> ChunkCacheCapacity {
299 self.chunk_cache_capacity
300 }
301
302 #[must_use]
304 pub fn with_chunk_cache_size(mut self, chunk_count: usize) -> Self {
305 self.chunk_cache_capacity = ChunkCacheCapacity::Chunks(chunk_count);
306 self
307 }
308
309 #[must_use]
314 pub fn with_chunk_cache_size_bytes(mut self, bytes: usize) -> Self {
315 self.chunk_cache_capacity = ChunkCacheCapacity::Bytes(bytes);
316 self
317 }
318
319 pub fn table_entry_cache_size_bytes(&self) -> usize {
321 self.table_entry_cache_size_bytes
322 }
323
324 #[must_use]
328 pub fn with_table_entry_cache_size_bytes(mut self, bytes: usize) -> Self {
329 self.table_entry_cache_size_bytes = bytes;
330 self
331 }
332
333 pub fn reader_statistics_enabled(&self) -> bool {
335 self.reader_statistics
336 }
337
338 #[must_use]
340 pub fn with_reader_statistics(mut self, enabled: bool) -> Self {
341 self.reader_statistics = enabled;
342 self
343 }
344
345 pub fn read_zero_chunk_on_error(&self) -> bool {
347 self.read_zero_chunk_on_error
348 }
349
350 #[must_use]
352 pub fn with_read_zero_chunk_on_error(mut self, enabled: bool) -> Self {
353 self.read_zero_chunk_on_error = enabled;
354 self
355 }
356
357 pub fn header_codepage(&self) -> HeaderCodepage {
359 self.header_codepage
360 }
361
362 #[must_use]
364 pub fn with_header_codepage(mut self, codepage: HeaderCodepage) -> Self {
365 self.header_codepage = codepage;
366 self
367 }
368
369 pub fn header_values_date_format(&self) -> HeaderDateFormat {
371 self.header_values_date_format
372 }
373
374 #[must_use]
376 pub fn with_header_values_date_format(mut self, format: HeaderDateFormat) -> Self {
377 self.header_values_date_format = format;
378 self
379 }
380
381 pub fn maximum_open_handles(&self) -> Option<usize> {
383 self.maximum_open_handles
384 }
385
386 #[must_use]
388 pub fn with_maximum_open_handles(mut self, maximum: Option<usize>) -> Self {
389 self.maximum_open_handles = maximum;
390 self
391 }
392}
393
394#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
395pub enum HeaderCodepage {
397 #[default]
399 Ascii,
400 Windows874,
402 Windows932,
404 Windows936,
406 Windows1250,
408 Windows1251,
410 Windows1252,
412 Windows1253,
414 Windows1254,
416 Windows1255,
418 Windows1256,
420 Windows1257,
422 Windows1258,
424}
425
426impl HeaderCodepage {
427 pub fn as_i32(self) -> i32 {
429 match self {
430 Self::Ascii => 20_127,
431 Self::Windows874 => 874,
432 Self::Windows932 => 932,
433 Self::Windows936 => 936,
434 Self::Windows1250 => 1250,
435 Self::Windows1251 => 1251,
436 Self::Windows1252 => 1252,
437 Self::Windows1253 => 1253,
438 Self::Windows1254 => 1254,
439 Self::Windows1255 => 1255,
440 Self::Windows1256 => 1256,
441 Self::Windows1257 => 1257,
442 Self::Windows1258 => 1258,
443 }
444 }
445
446 pub fn from_i32(value: i32) -> Option<Self> {
448 Some(match value {
449 20_127 => Self::Ascii,
450 874 => Self::Windows874,
451 932 => Self::Windows932,
452 936 => Self::Windows936,
453 1250 => Self::Windows1250,
454 1251 => Self::Windows1251,
455 1252 => Self::Windows1252,
456 1253 => Self::Windows1253,
457 1254 => Self::Windows1254,
458 1255 => Self::Windows1255,
459 1256 => Self::Windows1256,
460 1257 => Self::Windows1257,
461 1258 => Self::Windows1258,
462 _ => return None,
463 })
464 }
465}
466
467#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
468pub enum HeaderDateFormat {
470 DayMonth,
472 MonthDay,
474 Iso8601,
476 #[default]
478 Ctime,
479}
480
481impl HeaderDateFormat {
482 pub fn as_i32(self) -> i32 {
484 match self {
485 Self::DayMonth => 1,
486 Self::MonthDay => 2,
487 Self::Iso8601 => 3,
488 Self::Ctime => 4,
489 }
490 }
491
492 pub fn from_i32(value: i32) -> Option<Self> {
494 Some(match value {
495 1 => Self::DayMonth,
496 2 => Self::MonthDay,
497 3 => Self::Iso8601,
498 4 => Self::Ctime,
499 _ => return None,
500 })
501 }
502}
503
504#[derive(Debug, Clone, Default, PartialEq, Eq)]
505pub struct EwfMetadata {
507 pub case_number: Option<String>,
509 pub evidence_number: Option<String>,
511 pub examiner: Option<String>,
513 pub description: Option<String>,
515 pub notes: Option<String>,
517 pub acquisition_software: Option<String>,
519 pub acquisition_software_version: Option<String>,
521 pub os_version: Option<String>,
523 pub acquisition_date: Option<String>,
525 pub system_date: Option<String>,
527 pub password: Option<String>,
529 pub header_values: BTreeMap<String, String>,
531}
532
533impl EwfMetadata {
534 pub fn copy_header_values_from(&mut self, source: &EwfMetadata) {
536 *self = source.clone();
537 }
538
539 pub fn header_value(&self, identifier: &str) -> Option<&str> {
541 self.standard_header_value(identifier)
542 .or_else(|| self.header_values.get(identifier).map(String::as_str))
543 }
544
545 pub fn header_value_with_date_format(
547 &self,
548 identifier: &str,
549 date_format: HeaderDateFormat,
550 ) -> Option<Cow<'_, str>> {
551 let value = self.header_value(identifier)?;
552 if matches!(identifier, "acquiry_date" | "system_date") {
553 Some(format_header_date_value(value, date_format))
554 } else {
555 Some(Cow::Borrowed(value))
556 }
557 }
558
559 pub fn set_header_value(
561 &mut self,
562 identifier: &str,
563 value: impl Into<String>,
564 ) -> Option<String> {
565 let previous = self.header_value(identifier).map(str::to_owned);
566 let value = value.into();
567
568 self.header_values.remove(identifier);
569 match identifier {
570 "case_number" => self.case_number = Some(value),
571 "description" => self.description = Some(value),
572 "examiner_name" => self.examiner = Some(value),
573 "evidence_number" => self.evidence_number = Some(value),
574 "notes" => self.notes = Some(value),
575 "acquiry_date" => self.acquisition_date = Some(value),
576 "system_date" => self.system_date = Some(value),
577 "acquiry_operating_system" => self.os_version = Some(value),
578 "acquiry_software" => self.acquisition_software = Some(value),
579 "acquiry_software_version" => self.acquisition_software_version = Some(value),
580 "password" => self.password = Some(value),
581 _ => {
582 self.header_values.insert(identifier.to_string(), value);
583 }
584 }
585
586 previous
587 }
588
589 pub fn number_of_header_values(&self) -> usize {
591 let standard_count = STANDARD_HEADER_VALUE_IDENTIFIERS
592 .iter()
593 .filter(|identifier| self.header_value(identifier).is_some())
594 .count();
595 let generic_count = self
596 .header_values
597 .keys()
598 .filter(|identifier| !is_standard_header_value_identifier(identifier))
599 .count();
600 standard_count + generic_count
601 }
602
603 pub fn header_value_identifier(&self, index: usize) -> Option<&str> {
605 let mut remaining = index;
606 for identifier in STANDARD_HEADER_VALUE_IDENTIFIERS {
607 if self.header_value(identifier).is_some() {
608 if remaining == 0 {
609 return Some(identifier);
610 }
611 remaining -= 1;
612 }
613 }
614
615 for identifier in self.header_values.keys() {
616 if is_standard_header_value_identifier(identifier) {
617 continue;
618 }
619 if remaining == 0 {
620 return Some(identifier);
621 }
622 remaining -= 1;
623 }
624
625 None
626 }
627
628 fn standard_header_value(&self, identifier: &str) -> Option<&str> {
629 match identifier {
630 "case_number" => self.case_number.as_deref(),
631 "description" => self.description.as_deref(),
632 "examiner_name" => self.examiner.as_deref(),
633 "evidence_number" => self.evidence_number.as_deref(),
634 "notes" => self.notes.as_deref(),
635 "acquiry_date" => self.acquisition_date.as_deref(),
636 "system_date" => self.system_date.as_deref(),
637 "acquiry_operating_system" => self.os_version.as_deref(),
638 "acquiry_software" => self.acquisition_software.as_deref(),
639 "acquiry_software_version" => self.acquisition_software_version.as_deref(),
640 "password" => self.password.as_deref(),
641 _ => None,
642 }
643 }
644}
645
646const STANDARD_HEADER_VALUE_IDENTIFIERS: &[&str] = &[
647 "case_number",
648 "description",
649 "examiner_name",
650 "evidence_number",
651 "notes",
652 "acquiry_date",
653 "system_date",
654 "acquiry_operating_system",
655 "acquiry_software",
656 "acquiry_software_version",
657 "password",
658 "compression_level",
659 "model",
660 "serial_number",
661];
662
663fn is_standard_header_value_identifier(identifier: &str) -> bool {
664 STANDARD_HEADER_VALUE_IDENTIFIERS.contains(&identifier)
665}
666
667#[derive(Debug, Clone, Default, PartialEq, Eq)]
668pub struct StoredHashes {
670 pub md5: Option<[u8; 16]>,
672 pub sha1: Option<[u8; 20]>,
674 pub hash_values: BTreeMap<String, String>,
676}
677
678impl StoredHashes {
679 pub fn hash_value(&self, identifier: &str) -> Option<&str> {
681 self.hash_values.get(identifier).map(String::as_str)
682 }
683
684 pub fn set_hash_value(
686 &mut self,
687 identifier: impl Into<String>,
688 value: impl Into<String>,
689 ) -> Option<String> {
690 let identifier = identifier.into();
691 let value = value.into();
692 set_typed_hash_value(&identifier, &value, &mut self.md5, &mut self.sha1);
693 self.hash_values.insert(identifier, value)
694 }
695
696 pub fn number_of_hash_values(&self) -> usize {
698 self.hash_values.len()
699 }
700
701 pub fn hash_value_identifier(&self, index: usize) -> Option<&str> {
703 self.hash_values.keys().nth(index).map(String::as_str)
704 }
705}
706
707pub(crate) fn set_typed_hash_value(
708 identifier: &str,
709 value: &str,
710 md5: &mut Option<[u8; 16]>,
711 sha1: &mut Option<[u8; 20]>,
712) {
713 if identifier.eq_ignore_ascii_case("MD5") {
714 if let Some(parsed) = parse_hex_array(value) {
715 *md5 = Some(parsed);
716 }
717 } else if identifier.eq_ignore_ascii_case("SHA1")
718 && let Some(parsed) = parse_hex_array(value)
719 {
720 *sha1 = Some(parsed);
721 }
722}
723
724fn parse_hex_array<const N: usize>(text: &str) -> Option<[u8; N]> {
725 if text.len() != N * 2 {
726 return None;
727 }
728
729 let mut bytes = [0; N];
730 for (index, pair) in text.as_bytes().chunks_exact(2).enumerate() {
731 let high = hex_nibble(pair[0])?;
732 let low = hex_nibble(pair[1])?;
733 bytes[index] = (high << 4) | low;
734 }
735 Some(bytes)
736}
737
738fn hex_nibble(value: u8) -> Option<u8> {
739 match value {
740 b'0'..=b'9' => Some(value - b'0'),
741 b'a'..=b'f' => Some(value - b'a' + 10),
742 b'A'..=b'F' => Some(value - b'A' + 10),
743 _ => None,
744 }
745}
746
747#[derive(Debug, Clone, PartialEq, Eq)]
748pub struct AcquisitionError {
750 pub first_sector: u64,
752 pub sector_count: u64,
754}
755
756#[derive(Debug, Clone, PartialEq, Eq)]
757pub struct SectorRange {
759 pub first_sector: u64,
761 pub sector_count: u64,
763}
764
765#[derive(Debug, Clone, PartialEq, Eq)]
766pub struct MemoryExtent {
768 pub start_page: u64,
770 pub page_count: u64,
772}
773
774#[derive(Debug, Clone, Copy, PartialEq, Eq)]
775pub enum DataChunkEncoding {
777 Raw,
779 Zlib,
781 Bzip2,
783 PatternFill(u64),
785}
786
787#[derive(Debug, Clone, PartialEq, Eq)]
788pub struct DataChunk {
790 pub chunk_index: u64,
792 pub logical_offset: u64,
794 pub logical_size: usize,
796 pub encoded_size: u64,
798 pub encoding: DataChunkEncoding,
800 pub corrupted: bool,
802 pub data: Vec<u8>,
804}
805
806impl DataChunk {
807 pub fn is_corrupted(&self) -> bool {
809 self.corrupted
810 }
811
812 pub fn read_buffer(&self, buffer: &mut [u8]) -> Result<usize> {
814 Ok(copy_to_buffer(&self.data, buffer))
815 }
816
817 pub fn write_buffer(&mut self, buffer: &[u8]) -> Result<usize> {
823 self.data.clear();
824 self.data.extend_from_slice(buffer);
825 self.logical_size = buffer.len();
826 self.encoded_size = u64::try_from(buffer.len())
827 .map_err(|_| EwfError::Malformed("data chunk buffer size overflow".into()))?;
828 self.encoding = DataChunkEncoding::Raw;
829 self.corrupted = false;
830 Ok(buffer.len())
831 }
832}
833
834#[derive(Debug, Clone, PartialEq, Eq)]
835pub struct EncodedDataChunk {
837 pub chunk_index: u64,
839 pub logical_offset: u64,
841 pub logical_size: usize,
843 pub encoded_size: u64,
845 pub encoding: DataChunkEncoding,
847 pub has_checksum: bool,
849 pub data: Vec<u8>,
851}
852
853impl EncodedDataChunk {
854 pub fn read_buffer(&self, buffer: &mut [u8]) -> Result<usize> {
860 if self.has_checksum && self.encoding == DataChunkEncoding::Raw {
861 validate_raw_data_chunk_checksum(&self.data, self.logical_size)?;
862 }
863
864 let decoded = decode_chunk(
865 &self.data,
866 data_chunk_encoding(self.encoding),
867 self.logical_size,
868 )?;
869 Ok(copy_to_buffer(&decoded, buffer))
870 }
871}
872
873fn copy_to_buffer(data: &[u8], buffer: &mut [u8]) -> usize {
874 let read_size = data.len().min(buffer.len());
875 buffer[..read_size].copy_from_slice(&data[..read_size]);
876 read_size
877}
878
879fn data_chunk_encoding(encoding: DataChunkEncoding) -> ChunkEncoding {
880 match encoding {
881 DataChunkEncoding::Raw => ChunkEncoding::Raw,
882 DataChunkEncoding::Zlib => ChunkEncoding::Zlib,
883 DataChunkEncoding::Bzip2 => ChunkEncoding::Bzip2,
884 DataChunkEncoding::PatternFill(pattern) => ChunkEncoding::PatternFill(pattern),
885 }
886}
887
888fn validate_raw_data_chunk_checksum(encoded: &[u8], logical_size: usize) -> Result<()> {
889 let checksum_end = logical_size
890 .checked_add(4)
891 .ok_or_else(|| EwfError::Malformed("raw chunk checksum offset overflow".into()))?;
892 if encoded.len() < checksum_end {
893 return Err(EwfError::Malformed(
894 "raw chunk checksum trailer is missing".into(),
895 ));
896 }
897
898 let stored = u32::from_le_bytes(
899 encoded[logical_size..checksum_end]
900 .try_into()
901 .expect("raw chunk checksum slice has fixed size"),
902 );
903 let calculated = adler32(&encoded[..logical_size]);
904 if stored != calculated {
905 return Err(EwfError::Malformed("raw chunk checksum mismatch".into()));
906 }
907
908 Ok(())
909}
910
911fn adler32(data: &[u8]) -> u32 {
912 const MOD_ADLER: u32 = 65_521;
913 let mut a = 1_u32;
914 let mut b = 0_u32;
915 for byte in data {
916 a = (a + u32::from(*byte)) % MOD_ADLER;
917 b = (b + a) % MOD_ADLER;
918 }
919 (b << 16) | a
920}
921
922#[derive(Debug, Clone, Copy, PartialEq, Eq)]
923pub enum SingleFileEntryType {
925 File,
927 Directory,
929 Unknown,
931}
932
933#[derive(Debug, Clone, Copy, PartialEq, Eq)]
934pub struct SingleFileExtent {
936 pub data_offset: u64,
938 pub data_size: u64,
940 pub sparse: bool,
942}
943
944#[derive(Debug, Clone, Default, PartialEq, Eq)]
945pub struct SingleFileSource {
947 pub identifier: Option<i32>,
949 pub name: Option<String>,
951 pub evidence_number: Option<String>,
953 pub location: Option<String>,
955 pub device_guid: Option<String>,
957 pub primary_device_guid: Option<String>,
959 pub drive_type: Option<char>,
961 pub manufacturer: Option<String>,
963 pub model: Option<String>,
965 pub serial_number: Option<String>,
967 pub domain: Option<String>,
969 pub ip_address: Option<String>,
971 pub mac_address: Option<String>,
973 pub size: Option<u64>,
975 pub logical_offset: Option<i64>,
977 pub physical_offset: Option<i64>,
979 pub acquisition_time: Option<i64>,
981 pub md5: Option<String>,
983 pub sha1: Option<String>,
985}
986
987#[derive(Debug, Clone, Default, PartialEq, Eq)]
988pub struct SingleFileSubject {
990 pub identifier: Option<u32>,
992 pub name: Option<String>,
994}
995
996#[derive(Debug, Clone, Default, PartialEq, Eq)]
997pub struct SingleFilePermission {
999 pub name: Option<String>,
1001 pub identifier: Option<String>,
1003 pub property_type: Option<u32>,
1005 pub access_mask: Option<u32>,
1007 pub ace_flags: Option<u32>,
1009}
1010
1011#[derive(Debug, Clone, Default, PartialEq, Eq)]
1012pub struct SingleFilePermissionGroup {
1014 pub name: Option<String>,
1016 pub identifier: Option<String>,
1018 pub property_type: Option<u32>,
1020 pub access_mask: Option<u32>,
1022 pub ace_flags: Option<u32>,
1024 pub permissions: Vec<SingleFilePermission>,
1026}
1027
1028#[derive(Debug, Clone, Default, PartialEq, Eq)]
1029pub struct SingleFileAttribute {
1031 pub name: Option<String>,
1033 pub value: Option<String>,
1035}
1036
1037#[derive(Debug, Clone, Default, PartialEq, Eq)]
1038pub struct SingleFileEntry {
1040 pub identifier: Option<u64>,
1042 pub file_entry_type: Option<SingleFileEntryType>,
1044 pub flags: Option<u32>,
1046 pub guid: Option<String>,
1048 pub name: Option<String>,
1050 pub short_name: Option<String>,
1052 pub size: Option<u64>,
1054 pub logical_offset: Option<i64>,
1056 pub physical_offset: Option<i64>,
1058 pub duplicate_data_offset: Option<i64>,
1060 pub source_identifier: Option<i32>,
1062 pub subject_identifier: Option<u32>,
1064 pub permission_group_index: Option<i32>,
1066 pub record_type: Option<u32>,
1068 pub creation_time: Option<i64>,
1070 pub modification_time: Option<i64>,
1072 pub access_time: Option<i64>,
1074 pub entry_modification_time: Option<i64>,
1076 pub deletion_time: Option<i64>,
1078 pub md5: Option<String>,
1080 pub sha1: Option<String>,
1082 pub extents: Vec<SingleFileExtent>,
1084 pub attributes: Vec<SingleFileAttribute>,
1086 pub children: Vec<SingleFileEntry>,
1088}
1089
1090#[derive(Debug, Clone, Default, PartialEq, Eq)]
1091pub struct SingleFilesInfo {
1093 pub data_size: u64,
1095 pub root: SingleFileEntry,
1097 pub sources: Vec<SingleFileSource>,
1099 pub subjects: Vec<SingleFileSubject>,
1101 pub permission_groups: Vec<SingleFilePermissionGroup>,
1103}
1104
1105#[derive(Debug, Clone, Default, PartialEq, Eq)]
1106pub struct SingleFilesAuxTables {
1108 pub table_0x21_entries: Vec<u64>,
1110 pub md5_hashes: Vec<[u8; 16]>,
1112 pub table_0x23_entries: Vec<u64>,
1114}
1115
1116impl SingleFilesAuxTables {
1117 pub fn is_empty(&self) -> bool {
1119 self.table_0x21_entries.is_empty()
1120 && self.md5_hashes.is_empty()
1121 && self.table_0x23_entries.is_empty()
1122 }
1123}
1124
1125#[derive(Debug, Clone, PartialEq, Eq)]
1126pub struct ImageInfo {
1128 pub format: Format,
1130 pub format_profile: FormatProfile,
1132 pub segment_count: usize,
1134 pub segment_paths: Vec<PathBuf>,
1136 pub chunk_size: u64,
1138 pub logical_size: u64,
1140 pub acquisition_complete: bool,
1142 pub header_codepage: HeaderCodepage,
1144 pub header_values_date_format: HeaderDateFormat,
1146 pub media: MediaInfo,
1148 pub metadata: EwfMetadata,
1150 pub stored_hashes: StoredHashes,
1152 pub acquisition_errors: Vec<AcquisitionError>,
1154 pub memory_extents: Vec<MemoryExtent>,
1156 pub single_files: Option<SingleFilesInfo>,
1158 pub ewf2_single_files_tables: SingleFilesAuxTables,
1160 pub ewf2_increment_data: Vec<Vec<u8>>,
1162 pub ewf2_final_information: Option<Vec<u8>>,
1164 pub ewf2_restart_data: Option<String>,
1166 pub ewf2_analytical_data: Option<String>,
1168 pub sessions: Vec<SectorRange>,
1170 pub tracks: Vec<SectorRange>,
1172}
1173
1174#[cfg(feature = "verify")]
1175#[derive(Debug, Clone, PartialEq, Eq)]
1176pub struct VerifyResult {
1178 pub computed_md5: Option<[u8; 16]>,
1180 pub computed_sha1: Option<[u8; 20]>,
1182 pub md5_match: Option<bool>,
1184 pub sha1_match: Option<bool>,
1186}