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)]
65#[non_exhaustive]
66pub enum CompressionMethod {
68 None,
70 Zlib,
72 Zstd,
74 Bzip2,
76 Unknown(u16),
78}
79
80#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
81pub enum CompressionLevel {
83 Default,
85 #[default]
87 None,
88 Fast,
90 Best,
92 Unknown(i8),
94}
95
96impl CompressionLevel {
97 pub fn as_i8(self) -> i8 {
99 match self {
100 Self::Default => -1,
101 Self::None => 0,
102 Self::Fast => 1,
103 Self::Best => 2,
104 Self::Unknown(value) => value,
105 }
106 }
107
108 pub fn from_i8(value: i8) -> Self {
110 match value {
111 -1 => Self::Default,
112 0 => Self::None,
113 1 => Self::Fast,
114 2 => Self::Best,
115 value => Self::Unknown(value),
116 }
117 }
118}
119
120#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
121pub struct CompressionFlags {
123 pub empty_block: bool,
125 pub pattern_fill: bool,
127 pub unknown_bits: u8,
129}
130
131impl CompressionFlags {
132 const EMPTY_BLOCK: u8 = 0x01;
133 const PATTERN_FILL: u8 = 0x10;
134 const KNOWN_BITS: u8 = Self::EMPTY_BLOCK | Self::PATTERN_FILL;
135
136 pub fn from_bits(bits: u8) -> Self {
138 Self {
139 empty_block: bits & Self::EMPTY_BLOCK != 0,
140 pattern_fill: bits & Self::PATTERN_FILL != 0,
141 unknown_bits: bits & !Self::KNOWN_BITS,
142 }
143 }
144
145 pub fn bits(self) -> u8 {
147 let mut bits = self.unknown_bits;
148 if self.empty_block {
149 bits |= Self::EMPTY_BLOCK;
150 }
151 if self.pattern_fill {
152 bits |= Self::PATTERN_FILL;
153 }
154 bits
155 }
156}
157
158#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
159pub struct CompressionValues {
161 pub level: CompressionLevel,
163 pub flags: CompressionFlags,
165}
166
167#[derive(Debug, Clone, Copy, PartialEq, Eq)]
168pub enum MediaType {
170 Removable,
172 Fixed,
174 Optical,
176 SingleFiles,
178 Memory,
180 Unknown(u8),
182}
183
184#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
185pub struct MediaFlags {
187 pub physical: bool,
189 pub fastbloc: bool,
191 pub tableau: bool,
193}
194
195#[derive(Debug, Clone, Copy, PartialEq, Eq)]
196pub struct SegmentFileVersion {
198 pub major: u8,
200 pub minor: u8,
202}
203
204#[derive(Debug, Clone, Default, PartialEq, Eq)]
205pub struct MediaInfo {
207 pub sectors_per_chunk: Option<u64>,
209 pub bytes_per_sector: Option<u64>,
211 pub sector_count: Option<u64>,
213 pub chunk_count: Option<u64>,
215 pub error_granularity: Option<u64>,
217 pub set_identifier: Option<[u8; 16]>,
219 pub ewf2_segment_file_version: Option<SegmentFileVersion>,
221 pub compression_method: Option<CompressionMethod>,
223 pub compression_values: CompressionValues,
225 pub media_type: Option<MediaType>,
227 pub media_flags: MediaFlags,
229}
230
231#[derive(Debug, Clone, Copy, PartialEq, Eq)]
232pub enum OpenStrictness {
234 Strict,
236 Lenient,
238}
239
240#[derive(Debug, Clone, Copy, PartialEq, Eq)]
241pub struct OpenOptions {
245 strictness: OpenStrictness,
247 chunk_cache_capacity: ChunkCacheCapacity,
249 table_entry_cache_size_bytes: usize,
251 reader_statistics: bool,
253 read_zero_chunk_on_error: bool,
255 header_codepage: HeaderCodepage,
257 header_values_date_format: HeaderDateFormat,
259 maximum_open_handles: Option<usize>,
261}
262
263#[derive(Debug, Clone, Copy, PartialEq, Eq)]
264pub enum ChunkCacheCapacity {
266 Chunks(usize),
268 Bytes(usize),
270}
271
272impl Default for OpenOptions {
273 fn default() -> Self {
274 Self {
275 strictness: OpenStrictness::Strict,
276 chunk_cache_capacity: ChunkCacheCapacity::Chunks(64),
277 table_entry_cache_size_bytes: 4 * 1024 * 1024,
278 reader_statistics: false,
279 read_zero_chunk_on_error: false,
280 header_codepage: HeaderCodepage::Ascii,
281 header_values_date_format: HeaderDateFormat::Ctime,
282 maximum_open_handles: None,
283 }
284 }
285}
286
287impl OpenOptions {
288 pub fn strictness(&self) -> OpenStrictness {
290 self.strictness
291 }
292
293 #[must_use]
295 pub fn with_strictness(mut self, strictness: OpenStrictness) -> Self {
296 self.strictness = strictness;
297 self
298 }
299
300 pub fn chunk_cache_capacity(&self) -> ChunkCacheCapacity {
302 self.chunk_cache_capacity
303 }
304
305 #[must_use]
307 pub fn with_chunk_cache_size(mut self, chunk_count: usize) -> Self {
308 self.chunk_cache_capacity = ChunkCacheCapacity::Chunks(chunk_count);
309 self
310 }
311
312 #[must_use]
317 pub fn with_chunk_cache_size_bytes(mut self, bytes: usize) -> Self {
318 self.chunk_cache_capacity = ChunkCacheCapacity::Bytes(bytes);
319 self
320 }
321
322 pub fn table_entry_cache_size_bytes(&self) -> usize {
324 self.table_entry_cache_size_bytes
325 }
326
327 #[must_use]
331 pub fn with_table_entry_cache_size_bytes(mut self, bytes: usize) -> Self {
332 self.table_entry_cache_size_bytes = bytes;
333 self
334 }
335
336 pub fn reader_statistics_enabled(&self) -> bool {
338 self.reader_statistics
339 }
340
341 #[must_use]
343 pub fn with_reader_statistics(mut self, enabled: bool) -> Self {
344 self.reader_statistics = enabled;
345 self
346 }
347
348 pub fn read_zero_chunk_on_error(&self) -> bool {
350 self.read_zero_chunk_on_error
351 }
352
353 #[must_use]
355 pub fn with_read_zero_chunk_on_error(mut self, enabled: bool) -> Self {
356 self.read_zero_chunk_on_error = enabled;
357 self
358 }
359
360 pub fn header_codepage(&self) -> HeaderCodepage {
362 self.header_codepage
363 }
364
365 #[must_use]
367 pub fn with_header_codepage(mut self, codepage: HeaderCodepage) -> Self {
368 self.header_codepage = codepage;
369 self
370 }
371
372 pub fn header_values_date_format(&self) -> HeaderDateFormat {
374 self.header_values_date_format
375 }
376
377 #[must_use]
379 pub fn with_header_values_date_format(mut self, format: HeaderDateFormat) -> Self {
380 self.header_values_date_format = format;
381 self
382 }
383
384 pub fn maximum_open_handles(&self) -> Option<usize> {
386 self.maximum_open_handles
387 }
388
389 #[must_use]
391 pub fn with_maximum_open_handles(mut self, maximum: Option<usize>) -> Self {
392 self.maximum_open_handles = maximum;
393 self
394 }
395}
396
397#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
398pub enum HeaderCodepage {
400 #[default]
402 Ascii,
403 Windows874,
405 Windows932,
407 Windows936,
409 Windows1250,
411 Windows1251,
413 Windows1252,
415 Windows1253,
417 Windows1254,
419 Windows1255,
421 Windows1256,
423 Windows1257,
425 Windows1258,
427}
428
429impl HeaderCodepage {
430 pub fn as_i32(self) -> i32 {
432 match self {
433 Self::Ascii => 20_127,
434 Self::Windows874 => 874,
435 Self::Windows932 => 932,
436 Self::Windows936 => 936,
437 Self::Windows1250 => 1250,
438 Self::Windows1251 => 1251,
439 Self::Windows1252 => 1252,
440 Self::Windows1253 => 1253,
441 Self::Windows1254 => 1254,
442 Self::Windows1255 => 1255,
443 Self::Windows1256 => 1256,
444 Self::Windows1257 => 1257,
445 Self::Windows1258 => 1258,
446 }
447 }
448
449 pub fn from_i32(value: i32) -> Option<Self> {
451 Some(match value {
452 20_127 => Self::Ascii,
453 874 => Self::Windows874,
454 932 => Self::Windows932,
455 936 => Self::Windows936,
456 1250 => Self::Windows1250,
457 1251 => Self::Windows1251,
458 1252 => Self::Windows1252,
459 1253 => Self::Windows1253,
460 1254 => Self::Windows1254,
461 1255 => Self::Windows1255,
462 1256 => Self::Windows1256,
463 1257 => Self::Windows1257,
464 1258 => Self::Windows1258,
465 _ => return None,
466 })
467 }
468}
469
470#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
471pub enum HeaderDateFormat {
473 DayMonth,
475 MonthDay,
477 Iso8601,
479 #[default]
481 Ctime,
482}
483
484impl HeaderDateFormat {
485 pub fn as_i32(self) -> i32 {
487 match self {
488 Self::DayMonth => 1,
489 Self::MonthDay => 2,
490 Self::Iso8601 => 3,
491 Self::Ctime => 4,
492 }
493 }
494
495 pub fn from_i32(value: i32) -> Option<Self> {
497 Some(match value {
498 1 => Self::DayMonth,
499 2 => Self::MonthDay,
500 3 => Self::Iso8601,
501 4 => Self::Ctime,
502 _ => return None,
503 })
504 }
505}
506
507#[derive(Debug, Clone, Default, PartialEq, Eq)]
508pub struct EwfMetadata {
510 pub case_number: Option<String>,
512 pub evidence_number: Option<String>,
514 pub examiner: Option<String>,
516 pub description: Option<String>,
518 pub notes: Option<String>,
520 pub acquisition_software: Option<String>,
522 pub acquisition_software_version: Option<String>,
524 pub os_version: Option<String>,
526 pub acquisition_date: Option<String>,
528 pub system_date: Option<String>,
530 pub password: Option<String>,
532 pub header_values: BTreeMap<String, String>,
534}
535
536impl EwfMetadata {
537 pub fn copy_header_values_from(&mut self, source: &EwfMetadata) {
539 *self = source.clone();
540 }
541
542 pub fn header_value(&self, identifier: &str) -> Option<&str> {
544 self.standard_header_value(identifier)
545 .or_else(|| self.header_values.get(identifier).map(String::as_str))
546 }
547
548 pub fn header_value_with_date_format(
550 &self,
551 identifier: &str,
552 date_format: HeaderDateFormat,
553 ) -> Option<Cow<'_, str>> {
554 let value = self.header_value(identifier)?;
555 if matches!(identifier, "acquiry_date" | "system_date") {
556 Some(format_header_date_value(value, date_format))
557 } else {
558 Some(Cow::Borrowed(value))
559 }
560 }
561
562 pub fn set_header_value(
564 &mut self,
565 identifier: &str,
566 value: impl Into<String>,
567 ) -> Option<String> {
568 let previous = self.header_value(identifier).map(str::to_owned);
569 let value = value.into();
570
571 self.header_values.remove(identifier);
572 match identifier {
573 "case_number" => self.case_number = Some(value),
574 "description" => self.description = Some(value),
575 "examiner_name" => self.examiner = Some(value),
576 "evidence_number" => self.evidence_number = Some(value),
577 "notes" => self.notes = Some(value),
578 "acquiry_date" => self.acquisition_date = Some(value),
579 "system_date" => self.system_date = Some(value),
580 "acquiry_operating_system" => self.os_version = Some(value),
581 "acquiry_software" => self.acquisition_software = Some(value),
582 "acquiry_software_version" => self.acquisition_software_version = Some(value),
583 "password" => self.password = Some(value),
584 _ => {
585 self.header_values.insert(identifier.to_string(), value);
586 }
587 }
588
589 previous
590 }
591
592 pub fn number_of_header_values(&self) -> usize {
594 let standard_count = STANDARD_HEADER_VALUE_IDENTIFIERS
595 .iter()
596 .filter(|identifier| self.header_value(identifier).is_some())
597 .count();
598 let generic_count = self
599 .header_values
600 .keys()
601 .filter(|identifier| !is_standard_header_value_identifier(identifier))
602 .count();
603 standard_count + generic_count
604 }
605
606 pub fn header_value_identifier(&self, index: usize) -> Option<&str> {
608 let mut remaining = index;
609 for identifier in STANDARD_HEADER_VALUE_IDENTIFIERS {
610 if self.header_value(identifier).is_some() {
611 if remaining == 0 {
612 return Some(identifier);
613 }
614 remaining -= 1;
615 }
616 }
617
618 for identifier in self.header_values.keys() {
619 if is_standard_header_value_identifier(identifier) {
620 continue;
621 }
622 if remaining == 0 {
623 return Some(identifier);
624 }
625 remaining -= 1;
626 }
627
628 None
629 }
630
631 fn standard_header_value(&self, identifier: &str) -> Option<&str> {
632 match identifier {
633 "case_number" => self.case_number.as_deref(),
634 "description" => self.description.as_deref(),
635 "examiner_name" => self.examiner.as_deref(),
636 "evidence_number" => self.evidence_number.as_deref(),
637 "notes" => self.notes.as_deref(),
638 "acquiry_date" => self.acquisition_date.as_deref(),
639 "system_date" => self.system_date.as_deref(),
640 "acquiry_operating_system" => self.os_version.as_deref(),
641 "acquiry_software" => self.acquisition_software.as_deref(),
642 "acquiry_software_version" => self.acquisition_software_version.as_deref(),
643 "password" => self.password.as_deref(),
644 _ => None,
645 }
646 }
647}
648
649const STANDARD_HEADER_VALUE_IDENTIFIERS: &[&str] = &[
650 "case_number",
651 "description",
652 "examiner_name",
653 "evidence_number",
654 "notes",
655 "acquiry_date",
656 "system_date",
657 "acquiry_operating_system",
658 "acquiry_software",
659 "acquiry_software_version",
660 "password",
661 "compression_level",
662 "model",
663 "serial_number",
664];
665
666fn is_standard_header_value_identifier(identifier: &str) -> bool {
667 STANDARD_HEADER_VALUE_IDENTIFIERS.contains(&identifier)
668}
669
670#[derive(Debug, Clone, Default, PartialEq, Eq)]
671pub struct StoredHashes {
673 pub md5: Option<[u8; 16]>,
675 pub sha1: Option<[u8; 20]>,
677 pub hash_values: BTreeMap<String, String>,
679}
680
681impl StoredHashes {
682 pub fn hash_value(&self, identifier: &str) -> Option<&str> {
684 self.hash_values.get(identifier).map(String::as_str)
685 }
686
687 pub fn set_hash_value(
689 &mut self,
690 identifier: impl Into<String>,
691 value: impl Into<String>,
692 ) -> Option<String> {
693 let identifier = identifier.into();
694 let value = value.into();
695 set_typed_hash_value(&identifier, &value, &mut self.md5, &mut self.sha1);
696 self.hash_values.insert(identifier, value)
697 }
698
699 pub fn number_of_hash_values(&self) -> usize {
701 self.hash_values.len()
702 }
703
704 pub fn hash_value_identifier(&self, index: usize) -> Option<&str> {
706 self.hash_values.keys().nth(index).map(String::as_str)
707 }
708}
709
710pub(crate) fn set_typed_hash_value(
711 identifier: &str,
712 value: &str,
713 md5: &mut Option<[u8; 16]>,
714 sha1: &mut Option<[u8; 20]>,
715) {
716 if identifier.eq_ignore_ascii_case("MD5") {
717 if let Some(parsed) = parse_hex_array(value) {
718 *md5 = Some(parsed);
719 }
720 } else if identifier.eq_ignore_ascii_case("SHA1")
721 && let Some(parsed) = parse_hex_array(value)
722 {
723 *sha1 = Some(parsed);
724 }
725}
726
727pub(crate) fn parse_hex_array<const N: usize>(text: &str) -> Option<[u8; N]> {
728 if text.len() != N * 2 {
729 return None;
730 }
731
732 let mut bytes = [0; N];
733 for (index, pair) in text.as_bytes().as_chunks::<2>().0.iter().enumerate() {
734 let high = hex_nibble(pair[0])?;
735 let low = hex_nibble(pair[1])?;
736 bytes[index] = (high << 4) | low;
737 }
738 Some(bytes)
739}
740
741fn hex_nibble(value: u8) -> Option<u8> {
742 match value {
743 b'0'..=b'9' => Some(value - b'0'),
744 b'a'..=b'f' => Some(value - b'a' + 10),
745 b'A'..=b'F' => Some(value - b'A' + 10),
746 _ => None,
747 }
748}
749
750#[derive(Debug, Clone, PartialEq, Eq)]
751pub struct AcquisitionError {
753 pub first_sector: u64,
755 pub sector_count: u64,
757}
758
759#[derive(Debug, Clone, PartialEq, Eq)]
760pub struct SectorRange {
762 pub first_sector: u64,
764 pub sector_count: u64,
766}
767
768#[derive(Debug, Clone, PartialEq, Eq)]
769pub struct MemoryExtent {
771 pub start_page: u64,
773 pub page_count: u64,
775}
776
777#[derive(Debug, Clone, Copy, PartialEq, Eq)]
778#[non_exhaustive]
779pub enum DataChunkEncoding {
781 Raw,
783 Zlib,
785 Zstd,
787 Bzip2,
789 PatternFill(u64),
791}
792
793#[derive(Debug, Clone, PartialEq, Eq)]
794pub struct DataChunk {
796 pub chunk_index: u64,
798 pub logical_offset: u64,
800 pub logical_size: usize,
802 pub encoded_size: u64,
804 pub encoding: DataChunkEncoding,
806 pub corrupted: bool,
808 pub data: Vec<u8>,
810}
811
812impl DataChunk {
813 pub fn is_corrupted(&self) -> bool {
815 self.corrupted
816 }
817
818 pub fn read_buffer(&self, buffer: &mut [u8]) -> Result<usize> {
820 Ok(copy_to_buffer(&self.data, buffer))
821 }
822
823 pub fn write_buffer(&mut self, buffer: &[u8]) -> Result<usize> {
829 self.data.clear();
830 self.data.extend_from_slice(buffer);
831 self.logical_size = buffer.len();
832 self.encoded_size = u64::try_from(buffer.len())
833 .map_err(|_| EwfError::Malformed("data chunk buffer size overflow".into()))?;
834 self.encoding = DataChunkEncoding::Raw;
835 self.corrupted = false;
836 Ok(buffer.len())
837 }
838}
839
840#[derive(Debug, Clone, PartialEq, Eq)]
841pub struct EncodedDataChunk {
843 pub chunk_index: u64,
845 pub logical_offset: u64,
847 pub logical_size: usize,
849 pub encoded_size: u64,
851 pub encoding: DataChunkEncoding,
853 pub has_checksum: bool,
855 pub data: Vec<u8>,
857}
858
859impl EncodedDataChunk {
860 pub fn read_buffer(&self, buffer: &mut [u8]) -> Result<usize> {
866 if self.has_checksum && self.encoding == DataChunkEncoding::Raw {
867 validate_raw_data_chunk_checksum(&self.data, self.logical_size)?;
868 }
869
870 let decoded = decode_chunk(
871 &self.data,
872 data_chunk_encoding(self.encoding),
873 self.logical_size,
874 )?;
875 Ok(copy_to_buffer(&decoded, buffer))
876 }
877}
878
879fn copy_to_buffer(data: &[u8], buffer: &mut [u8]) -> usize {
880 let read_size = data.len().min(buffer.len());
881 buffer[..read_size].copy_from_slice(&data[..read_size]);
882 read_size
883}
884
885fn data_chunk_encoding(encoding: DataChunkEncoding) -> ChunkEncoding {
886 match encoding {
887 DataChunkEncoding::Raw => ChunkEncoding::Raw,
888 DataChunkEncoding::Zlib => ChunkEncoding::Zlib,
889 DataChunkEncoding::Zstd => ChunkEncoding::Zstd,
890 DataChunkEncoding::Bzip2 => ChunkEncoding::Bzip2,
891 DataChunkEncoding::PatternFill(pattern) => ChunkEncoding::PatternFill(pattern),
892 }
893}
894
895fn validate_raw_data_chunk_checksum(encoded: &[u8], logical_size: usize) -> Result<()> {
896 let checksum_end = logical_size
897 .checked_add(4)
898 .ok_or_else(|| EwfError::Malformed("raw chunk checksum offset overflow".into()))?;
899 if encoded.len() < checksum_end {
900 return Err(EwfError::Malformed(
901 "raw chunk checksum trailer is missing".into(),
902 ));
903 }
904
905 let stored = u32::from_le_bytes(
906 encoded[logical_size..checksum_end]
907 .try_into()
908 .expect("raw chunk checksum slice has fixed size"),
909 );
910 let calculated = adler32(&encoded[..logical_size]);
911 if stored != calculated {
912 return Err(EwfError::Malformed("raw chunk checksum mismatch".into()));
913 }
914
915 Ok(())
916}
917
918fn adler32(data: &[u8]) -> u32 {
919 const MOD_ADLER: u32 = 65_521;
920 let mut a = 1_u32;
921 let mut b = 0_u32;
922 for byte in data {
923 a = (a + u32::from(*byte)) % MOD_ADLER;
924 b = (b + a) % MOD_ADLER;
925 }
926 (b << 16) | a
927}
928
929#[derive(Debug, Clone, Copy, PartialEq, Eq)]
930pub enum SingleFileEntryType {
932 File,
934 Directory,
936 Unknown,
938}
939
940#[derive(Debug, Clone, Copy, PartialEq, Eq)]
941pub struct SingleFileExtent {
943 pub data_offset: u64,
945 pub data_size: u64,
947 pub sparse: bool,
949}
950
951#[derive(Debug, Clone, Default, PartialEq, Eq)]
952pub struct SingleFileSource {
954 pub identifier: Option<i32>,
956 pub name: Option<String>,
958 pub evidence_number: Option<String>,
960 pub location: Option<String>,
962 pub device_guid: Option<String>,
964 pub primary_device_guid: Option<String>,
966 pub drive_type: Option<char>,
968 pub manufacturer: Option<String>,
970 pub model: Option<String>,
972 pub serial_number: Option<String>,
974 pub domain: Option<String>,
976 pub ip_address: Option<String>,
978 pub mac_address: Option<String>,
980 pub size: Option<u64>,
982 pub logical_offset: Option<i64>,
984 pub physical_offset: Option<i64>,
986 pub acquisition_time: Option<i64>,
988 pub md5: Option<String>,
990 pub sha1: Option<String>,
992}
993
994#[derive(Debug, Clone, Default, PartialEq, Eq)]
995pub struct SingleFileSubject {
997 pub identifier: Option<u32>,
999 pub name: Option<String>,
1001}
1002
1003#[derive(Debug, Clone, Default, PartialEq, Eq)]
1004pub struct SingleFilePermission {
1006 pub name: Option<String>,
1008 pub identifier: Option<String>,
1010 pub property_type: Option<u32>,
1012 pub access_mask: Option<u32>,
1014 pub ace_flags: Option<u32>,
1016}
1017
1018#[derive(Debug, Clone, Default, PartialEq, Eq)]
1019pub struct SingleFilePermissionGroup {
1021 pub name: Option<String>,
1023 pub identifier: Option<String>,
1025 pub property_type: Option<u32>,
1027 pub access_mask: Option<u32>,
1029 pub ace_flags: Option<u32>,
1031 pub permissions: Vec<SingleFilePermission>,
1033}
1034
1035#[derive(Debug, Clone, Default, PartialEq, Eq)]
1036pub struct SingleFileAttribute {
1038 pub name: Option<String>,
1040 pub value: Option<String>,
1042}
1043
1044#[derive(Debug, Clone, Default, PartialEq, Eq)]
1045pub struct SingleFileEntry {
1047 pub identifier: Option<u64>,
1049 pub file_entry_type: Option<SingleFileEntryType>,
1051 pub flags: Option<u32>,
1053 pub guid: Option<String>,
1055 pub name: Option<String>,
1057 pub name_utf16: Option<Vec<u16>>,
1061 pub short_name: Option<String>,
1063 pub size: Option<u64>,
1065 pub logical_offset: Option<i64>,
1067 pub physical_offset: Option<i64>,
1069 pub duplicate_data_offset: Option<i64>,
1071 pub source_identifier: Option<i32>,
1073 pub subject_identifier: Option<u32>,
1075 pub permission_group_index: Option<i32>,
1077 pub record_type: Option<u32>,
1079 pub creation_time: Option<i64>,
1081 pub modification_time: Option<i64>,
1083 pub access_time: Option<i64>,
1085 pub entry_modification_time: Option<i64>,
1087 pub deletion_time: Option<i64>,
1089 pub md5: Option<String>,
1091 pub sha1: Option<String>,
1093 pub extents: Vec<SingleFileExtent>,
1095 pub attributes: Vec<SingleFileAttribute>,
1097 pub children: Vec<SingleFileEntry>,
1099}
1100
1101#[derive(Debug, Clone, Default, PartialEq, Eq)]
1102pub struct SingleFilesInfo {
1104 pub data_size: u64,
1106 pub root: SingleFileEntry,
1108 pub sources: Vec<SingleFileSource>,
1110 pub subjects: Vec<SingleFileSubject>,
1112 pub permission_groups: Vec<SingleFilePermissionGroup>,
1114}
1115
1116#[derive(Debug, Clone, Default, PartialEq, Eq)]
1117pub struct SingleFilesAuxTables {
1119 pub table_0x21_entries: Vec<u64>,
1121 pub md5_hashes: Vec<[u8; 16]>,
1123 pub table_0x23_entries: Vec<u64>,
1125}
1126
1127impl SingleFilesAuxTables {
1128 pub fn is_empty(&self) -> bool {
1130 self.table_0x21_entries.is_empty()
1131 && self.md5_hashes.is_empty()
1132 && self.table_0x23_entries.is_empty()
1133 }
1134}
1135
1136#[derive(Debug, Clone, PartialEq, Eq)]
1137pub struct ImageInfo {
1139 pub format: Format,
1141 pub format_profile: FormatProfile,
1143 pub segment_count: usize,
1145 pub segment_paths: Vec<PathBuf>,
1147 pub chunk_size: u64,
1149 pub logical_size: u64,
1151 pub acquisition_complete: bool,
1153 pub header_codepage: HeaderCodepage,
1155 pub header_values_date_format: HeaderDateFormat,
1157 pub media: MediaInfo,
1159 pub metadata: EwfMetadata,
1161 pub stored_hashes: StoredHashes,
1163 pub acquisition_errors: Vec<AcquisitionError>,
1165 pub memory_extents: Vec<MemoryExtent>,
1167 pub single_files: Option<SingleFilesInfo>,
1169 pub ewf2_single_files_tables: SingleFilesAuxTables,
1171 pub ewf2_increment_data: Vec<Vec<u8>>,
1173 pub ewf2_final_information: Option<Vec<u8>>,
1175 pub ewf2_restart_data: Option<String>,
1177 pub ewf2_analytical_data: Option<String>,
1179 pub sessions: Vec<SectorRange>,
1181 pub tracks: Vec<SectorRange>,
1183}
1184
1185#[cfg(feature = "verify")]
1186#[derive(Debug, Clone, PartialEq, Eq)]
1187pub struct VerifyResult {
1189 pub computed_md5: Option<[u8; 16]>,
1191 pub computed_sha1: Option<[u8; 20]>,
1193 pub computed_sha256: Option<[u8; 32]>,
1195 pub md5_match: Option<bool>,
1197 pub sha1_match: Option<bool>,
1199 pub sha256_match: Option<bool>,
1201}