1use crate::types::{
2 SingleFileAttribute, SingleFileEntry, SingleFileEntryType, SingleFileExtent,
3 SingleFilePermission, SingleFilePermissionGroup, SingleFileSource, SingleFileSubject,
4 SingleFilesInfo,
5};
6use crate::{EwfError, Result};
7
8pub const SINGLE_FILE_PATH_SEPARATOR: char = '\t';
13const EXTENDED_ATTRIBUTES_HEADER: &[u8; 37] = &[
14 0x00, 0x00, 0x00, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x41, 0x00, 0x74,
15 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x62, 0x00, 0x75, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73,
16 0x00, 0x00, 0x00, 0x00, 0x00,
17];
18
19impl SingleFilesInfo {
20 pub fn entry_by_path(&self, path: &str) -> Result<Option<&SingleFileEntry>> {
26 self.root.child_by_path(path)
27 }
28
29 pub fn source_by_identifier(&self, identifier: i32) -> Option<&SingleFileSource> {
31 self.sources
32 .iter()
33 .find(|source| source.identifier == Some(identifier))
34 }
35
36 pub fn source_by_index(&self, index: i32) -> Option<&SingleFileSource> {
38 usize::try_from(index)
39 .ok()
40 .and_then(|index| self.sources.get(index))
41 }
42
43 pub fn source_for_entry(&self, entry: &SingleFileEntry) -> Option<&SingleFileSource> {
45 let identifier = entry.source_identifier?;
46 if identifier < 1 {
47 return None;
48 }
49 self.source_by_index(identifier)
50 }
51
52 pub fn subject_by_identifier(&self, identifier: u32) -> Option<&SingleFileSubject> {
54 self.subjects
55 .iter()
56 .find(|subject| subject.identifier == Some(identifier))
57 }
58
59 pub fn subject_for_entry(&self, entry: &SingleFileEntry) -> Option<&SingleFileSubject> {
61 self.subject_by_identifier(entry.subject_identifier?)
62 }
63
64 pub fn number_of_permission_groups(&self) -> usize {
66 self.permission_groups.len()
67 }
68
69 pub fn permission_group_by_index(&self, index: i32) -> Option<&SingleFilePermissionGroup> {
71 usize::try_from(index)
72 .ok()
73 .and_then(|index| self.permission_groups.get(index))
74 }
75
76 pub fn permission_group_for_entry(
78 &self,
79 entry: &SingleFileEntry,
80 ) -> Option<&SingleFilePermissionGroup> {
81 self.permission_group_by_index(entry.permission_group_index?)
82 }
83
84 pub fn access_control_entries_for_entry(
86 &self,
87 entry: &SingleFileEntry,
88 ) -> &[SingleFilePermission] {
89 self.permission_group_for_entry(entry)
90 .map_or(&[], |group| group.permissions.as_slice())
91 }
92
93 pub fn number_of_access_control_entries_for_entry(&self, entry: &SingleFileEntry) -> usize {
95 self.access_control_entries_for_entry(entry).len()
96 }
97
98 pub fn access_control_entry_for_entry(
100 &self,
101 entry: &SingleFileEntry,
102 index: usize,
103 ) -> Option<&SingleFilePermission> {
104 self.access_control_entries_for_entry(entry).get(index)
105 }
106}
107
108impl SingleFileSource {
109 pub fn identifier(&self) -> Option<i32> {
111 self.identifier
112 }
113
114 pub fn name(&self) -> Option<&str> {
116 self.name.as_deref()
117 }
118
119 pub fn evidence_number(&self) -> Option<&str> {
121 self.evidence_number.as_deref()
122 }
123
124 pub fn location(&self) -> Option<&str> {
126 self.location.as_deref()
127 }
128
129 pub fn device_guid(&self) -> Option<&str> {
131 self.device_guid.as_deref()
132 }
133
134 pub fn primary_device_guid(&self) -> Option<&str> {
136 self.primary_device_guid.as_deref()
137 }
138
139 pub fn manufacturer(&self) -> Option<&str> {
141 self.manufacturer.as_deref()
142 }
143
144 pub fn model(&self) -> Option<&str> {
146 self.model.as_deref()
147 }
148
149 pub fn serial_number(&self) -> Option<&str> {
151 self.serial_number.as_deref()
152 }
153
154 pub fn domain(&self) -> Option<&str> {
156 self.domain.as_deref()
157 }
158
159 pub fn ip_address(&self) -> Option<&str> {
161 self.ip_address.as_deref()
162 }
163
164 pub fn mac_address(&self) -> Option<&str> {
166 self.mac_address.as_deref()
167 }
168
169 pub fn size(&self) -> Option<u64> {
171 self.size
172 }
173
174 pub fn drive_type(&self) -> Option<char> {
176 self.drive_type
177 }
178
179 pub fn logical_offset(&self) -> Option<i64> {
181 self.logical_offset
182 }
183
184 pub fn physical_offset(&self) -> Option<i64> {
186 self.physical_offset
187 }
188
189 pub fn acquisition_time(&self) -> Option<i64> {
191 self.acquisition_time
192 }
193
194 pub fn md5_hash(&self) -> Option<&str> {
196 self.md5.as_deref()
197 }
198
199 pub fn sha1_hash(&self) -> Option<&str> {
201 self.sha1.as_deref()
202 }
203}
204
205impl SingleFileEntry {
206 pub fn identifier(&self) -> Option<u64> {
208 self.identifier
209 }
210
211 pub fn entry_type(&self) -> Option<SingleFileEntryType> {
213 self.file_entry_type
214 }
215
216 pub fn flags(&self) -> Option<u32> {
218 self.flags
219 }
220
221 pub fn guid(&self) -> Option<&str> {
223 self.guid.as_deref()
224 }
225
226 pub fn media_data_offset(&self) -> Option<i64> {
228 self.logical_offset
229 }
230
231 pub fn media_data_size(&self) -> Option<u64> {
233 self.size
234 }
235
236 pub fn physical_offset(&self) -> Option<i64> {
238 self.physical_offset
239 }
240
241 pub fn duplicate_media_data_offset(&self) -> Option<i64> {
243 self.duplicate_data_offset
244 }
245
246 pub fn source_identifier(&self) -> Option<i32> {
248 self.source_identifier
249 }
250
251 pub fn subject_identifier(&self) -> Option<u32> {
253 self.subject_identifier
254 }
255
256 pub fn permission_group_index(&self) -> Option<i32> {
258 self.permission_group_index
259 }
260
261 pub fn record_type(&self) -> Option<u32> {
263 self.record_type
264 }
265
266 pub fn name(&self) -> Option<&str> {
268 self.name.as_deref()
269 }
270
271 pub fn short_name(&self) -> Option<&str> {
273 self.short_name.as_deref()
274 }
275
276 pub fn size(&self) -> Option<u64> {
278 self.size
279 }
280
281 pub fn creation_time(&self) -> Option<i64> {
283 self.creation_time
284 }
285
286 pub fn modification_time(&self) -> Option<i64> {
288 self.modification_time
289 }
290
291 pub fn access_time(&self) -> Option<i64> {
293 self.access_time
294 }
295
296 pub fn entry_modification_time(&self) -> Option<i64> {
298 self.entry_modification_time
299 }
300
301 pub fn deletion_time(&self) -> Option<i64> {
303 self.deletion_time
304 }
305
306 pub fn md5_hash(&self) -> Option<&str> {
308 self.md5.as_deref()
309 }
310
311 pub fn sha1_hash(&self) -> Option<&str> {
313 self.sha1.as_deref()
314 }
315
316 pub fn number_of_sub_file_entries(&self) -> usize {
318 self.children.len()
319 }
320
321 pub fn sub_file_entry(&self, index: usize) -> Option<&SingleFileEntry> {
323 self.children.get(index)
324 }
325
326 pub fn sub_file_entry_by_name(&self, name: &str) -> Option<&SingleFileEntry> {
328 self.child_by_name(name)
329 }
330
331 pub fn sub_file_entry_by_path(&self, path: &str) -> Result<Option<&SingleFileEntry>> {
337 self.child_by_path(path)
338 }
339
340 pub fn number_of_extents(&self) -> usize {
342 self.extents.len()
343 }
344
345 pub fn extent(&self, index: usize) -> Option<&SingleFileExtent> {
347 self.extents.get(index)
348 }
349
350 pub fn number_of_attributes(&self) -> usize {
352 self.attributes.len()
353 }
354
355 pub fn attribute(&self, index: usize) -> Option<&SingleFileAttribute> {
357 self.attributes.get(index)
358 }
359
360 pub fn child_by_name(&self, name: &str) -> Option<&SingleFileEntry> {
362 self.children
363 .iter()
364 .find(|child| child.name.as_deref() == Some(name))
365 }
366
367 pub fn child_by_path(&self, path: &str) -> Result<Option<&SingleFileEntry>> {
373 entry_by_path(self, path)
374 }
375}
376
377impl SingleFilePermission {
378 pub fn name(&self) -> Option<&str> {
380 self.name.as_deref()
381 }
382
383 pub fn identifier(&self) -> Option<&str> {
385 self.identifier.as_deref()
386 }
387
388 pub fn property_type(&self) -> Option<u32> {
390 self.property_type
391 }
392
393 pub fn access_mask(&self) -> Option<u32> {
395 self.access_mask
396 }
397
398 pub fn flags(&self) -> Option<u32> {
400 self.ace_flags
401 }
402}
403
404impl SingleFilePermissionGroup {
405 pub fn name(&self) -> Option<&str> {
407 self.name.as_deref()
408 }
409
410 pub fn identifier(&self) -> Option<&str> {
412 self.identifier.as_deref()
413 }
414
415 pub fn property_type(&self) -> Option<u32> {
417 self.property_type
418 }
419
420 pub fn access_mask(&self) -> Option<u32> {
422 self.access_mask
423 }
424
425 pub fn flags(&self) -> Option<u32> {
427 self.ace_flags
428 }
429
430 pub fn number_of_entries(&self) -> usize {
432 self.permissions.len()
433 }
434
435 pub fn entry_by_index(&self, index: i32) -> Option<&SingleFilePermission> {
437 usize::try_from(index)
438 .ok()
439 .and_then(|index| self.permissions.get(index))
440 }
441}
442
443impl SingleFileAttribute {
444 pub fn name(&self) -> Option<&str> {
446 self.name.as_deref()
447 }
448
449 pub fn value(&self) -> Option<&str> {
451 self.value.as_deref()
452 }
453}
454
455pub(crate) fn parse_ewf2_single_files_data(data: &[u8]) -> Result<SingleFilesInfo> {
456 let lines = decode_utf16le_lines(data)?;
457 if lines.first().map(String::as_str) != Some("5") {
458 return Err(EwfError::Malformed(
459 "EWF2 single files data has unsupported category count".into(),
460 ));
461 }
462
463 let mut category_index = 1;
464
465 let (data_size, next_category_index) = parse_record_category(&lines, category_index)?;
466 category_index = next_category_index;
467 let (permission_groups, next_category_index) =
468 parse_permission_groups_category(&lines, category_index)?;
469 category_index = next_category_index;
470 let (sources, next_category_index) = parse_sources_category(&lines, category_index)?;
471 category_index = next_category_index;
472 let (subjects, entry_index) = parse_subjects_category(&lines, category_index)?;
473
474 require_category_at(&lines, entry_index, "entry", "entry category")?;
475 let types_line_index = entry_index
476 .checked_add(2)
477 .ok_or_else(|| EwfError::Malformed("EWF2 single files entry index overflow".into()))?;
478 let mut cursor = entry_index
479 .checked_add(3)
480 .ok_or_else(|| EwfError::Malformed("EWF2 single files entry index overflow".into()))?;
481 if types_line_index >= lines.len() {
482 return Err(EwfError::Malformed(
483 "EWF2 single files entry category is truncated".into(),
484 ));
485 }
486
487 parse_category_entry_count(&lines[entry_index + 1], "EWF2 single files entry count")?;
488 let types = parse_type_line(
489 &lines[types_line_index],
490 "EWF2 single files entry category types",
491 )?;
492
493 let data_size = data_size.unwrap_or(
494 u64::try_from(data.len())
495 .map_err(|_| EwfError::Malformed("single files data size overflow".into()))?,
496 );
497 let root = parse_entry(&lines, &types, &mut cursor)?;
498 require_empty_category_terminator(
499 &lines,
500 cursor,
501 "EWF2 single files entry category terminator",
502 )?;
503 Ok(SingleFilesInfo {
504 data_size,
505 root,
506 sources,
507 subjects,
508 permission_groups,
509 })
510}
511
512fn entry_by_path<'a>(
513 mut entry: &'a SingleFileEntry,
514 path: &str,
515) -> Result<Option<&'a SingleFileEntry>> {
516 let mut rest = path
517 .strip_prefix(SINGLE_FILE_PATH_SEPARATOR)
518 .unwrap_or(path);
519 if rest.is_empty() {
520 return Ok(Some(entry));
521 }
522
523 while !rest.is_empty() {
524 let (segment, next_rest) = match rest.find([SINGLE_FILE_PATH_SEPARATOR, '\0']) {
525 Some(separator_index) => {
526 let separator_size = rest[separator_index..]
527 .chars()
528 .next()
529 .expect("separator index points to a character")
530 .len_utf8();
531 (
532 &rest[..separator_index],
533 &rest[separator_index + separator_size..],
534 )
535 }
536 None => (rest, ""),
537 };
538 if segment.is_empty() {
539 return Err(EwfError::Malformed(
540 "single file path is missing entry name".into(),
541 ));
542 }
543
544 entry = match entry.child_by_name(segment) {
545 Some(child) => child,
546 None => return Ok(None),
547 };
548 rest = next_rest;
549 }
550
551 Ok(Some(entry))
552}
553
554fn parse_sources_category(
555 lines: &[String],
556 category_index: usize,
557) -> Result<(Vec<SingleFileSource>, usize)> {
558 require_category_at(lines, category_index, "srce", "source category")?;
559 let (types, entry_count, mut cursor) = parse_category_header(lines, category_index, "srce")?;
560
561 let root = parse_source_row(&types, get_line(lines, cursor, "EWF2 source root")?)?;
562 cursor += 1;
563 let mut sources = Vec::with_capacity(entry_count.saturating_add(1));
564 sources.push(root);
565
566 for source_index in 0..entry_count {
567 let child_count = parse_child_entry_count(
568 get_line(lines, cursor, "EWF2 source child count")?,
569 "EWF2 source child count",
570 )?;
571 if child_count != 0 {
572 return Err(EwfError::Malformed(
573 "EWF2 source entries cannot have children".into(),
574 ));
575 }
576 cursor += 1;
577
578 let source = parse_source_row(&types, get_line(lines, cursor, "EWF2 source row")?)?;
579 let expected_identifier = source_index
580 .checked_add(1)
581 .and_then(|identifier| i32::try_from(identifier).ok())
582 .ok_or_else(|| EwfError::Malformed("EWF2 source identifier index overflow".into()))?;
583 if source.identifier != Some(expected_identifier) {
584 return Err(EwfError::Malformed(
585 "EWF2 source identifier does not match source index".into(),
586 ));
587 }
588 sources.push(source);
589 cursor += 1;
590 }
591 require_empty_category_terminator(
592 lines,
593 cursor,
594 "EWF2 single files source category terminator",
595 )?;
596 Ok((
597 sources,
598 next_category_index(cursor, "EWF2 source category terminator")?,
599 ))
600}
601
602fn parse_subjects_category(
603 lines: &[String],
604 category_index: usize,
605) -> Result<(Vec<SingleFileSubject>, usize)> {
606 require_category_at(lines, category_index, "sub", "subject category")?;
607 let (types, entry_count, mut cursor) = parse_category_header(lines, category_index, "sub")?;
608
609 let root = parse_subject_row(&types, get_line(lines, cursor, "EWF2 subject root")?)?;
610 cursor += 1;
611 let mut subjects = Vec::with_capacity(entry_count.saturating_add(1));
612 subjects.push(root);
613
614 for _ in 0..entry_count {
615 let child_count = parse_child_entry_count(
616 get_line(lines, cursor, "EWF2 subject child count")?,
617 "EWF2 subject child count",
618 )?;
619 if child_count != 0 {
620 return Err(EwfError::Malformed(
621 "EWF2 subject entries cannot have children".into(),
622 ));
623 }
624 cursor += 1;
625
626 let subject = parse_subject_row(&types, get_line(lines, cursor, "EWF2 subject row")?)?;
627 subjects.push(subject);
628 cursor += 1;
629 }
630 require_empty_category_terminator(
631 lines,
632 cursor,
633 "EWF2 single files subject category terminator",
634 )?;
635 Ok((
636 subjects,
637 next_category_index(cursor, "EWF2 subject category terminator")?,
638 ))
639}
640
641fn parse_permission_groups_category(
642 lines: &[String],
643 category_index: usize,
644) -> Result<(Vec<SingleFilePermissionGroup>, usize)> {
645 require_category_at(lines, category_index, "perm", "permission category")?;
646 let (types, entry_count, mut cursor) = parse_category_header(lines, category_index, "perm")?;
647
648 let root_permission = parse_permission_row(
649 &types,
650 get_line(lines, cursor, "EWF2 permission category root")?,
651 )?;
652 require_permission_group_type(&root_permission, "EWF2 permission category root")?;
653 cursor += 1;
654 let mut groups = Vec::with_capacity(entry_count);
655
656 for _ in 0..entry_count {
657 let permission_count = parse_child_entry_count(
658 get_line(lines, cursor, "EWF2 permission group child count")?,
659 "EWF2 permission group child count",
660 )?;
661 cursor += 1;
662
663 let group_permission = parse_permission_row(
664 &types,
665 get_line(lines, cursor, "EWF2 permission group row")?,
666 )?;
667 require_permission_group_type(&group_permission, "EWF2 permission group row")?;
668 cursor += 1;
669 let mut group = permission_group_from_permission(group_permission, permission_count);
670
671 for _ in 0..permission_count {
672 let child_count = parse_child_entry_count(
673 get_line(lines, cursor, "EWF2 permission child count")?,
674 "EWF2 permission child count",
675 )?;
676 if child_count != 0 {
677 return Err(EwfError::Malformed(
678 "EWF2 permission entries cannot have children".into(),
679 ));
680 }
681 cursor += 1;
682
683 let permission =
684 parse_permission_row(&types, get_line(lines, cursor, "EWF2 permission row")?)?;
685 group.permissions.push(permission);
686 cursor += 1;
687 }
688 groups.push(group);
689 }
690 require_empty_category_terminator(
691 lines,
692 cursor,
693 "EWF2 single files permission category terminator",
694 )?;
695 Ok((
696 groups,
697 next_category_index(cursor, "EWF2 permission category terminator")?,
698 ))
699}
700
701fn require_category_at(lines: &[String], index: usize, name: &str, label: &str) -> Result<()> {
702 let line = get_line(lines, index, &format!("EWF2 single files {label}"))?;
703 if line != name {
704 return Err(EwfError::Malformed(format!(
705 "EWF2 single files {label} is missing or out of order"
706 )));
707 }
708 Ok(())
709}
710
711fn next_category_index(index: usize, label: &str) -> Result<usize> {
712 index
713 .checked_add(1)
714 .ok_or_else(|| EwfError::Malformed(format!("{label} index overflow")))
715}
716
717fn parse_record_category(lines: &[String], category_index: usize) -> Result<(Option<u64>, usize)> {
718 require_category_at(lines, category_index, "rec", "record category")?;
719 let types_line_index = category_index
720 .checked_add(1)
721 .ok_or_else(|| EwfError::Malformed("EWF2 record category index overflow".into()))?;
722 let values_line_index = category_index
723 .checked_add(2)
724 .ok_or_else(|| EwfError::Malformed("EWF2 record category index overflow".into()))?;
725 let types = parse_type_line(
726 get_line(lines, types_line_index, "EWF2 record category types")?,
727 "EWF2 record category types",
728 )?;
729 let values: Vec<&str> = get_line(lines, values_line_index, "EWF2 record category values")?
730 .split('\t')
731 .collect();
732 let terminator_index = category_index
733 .checked_add(3)
734 .ok_or_else(|| EwfError::Malformed("EWF2 record category index overflow".into()))?;
735
736 require_empty_category_terminator(
737 lines,
738 terminator_index,
739 "EWF2 single files record category terminator",
740 )?;
741 let next_category_index =
742 next_category_index(terminator_index, "EWF2 record category terminator")?;
743
744 for (index, value_type) in types.iter().enumerate() {
745 if *value_type != "tb" {
746 continue;
747 }
748 let Some(value) = values.get(index).copied().filter(|value| !value.is_empty()) else {
749 return Ok((None, next_category_index));
750 };
751 return Ok((
752 parse_u64(value, "record data size").map(Some)?,
753 next_category_index,
754 ));
755 }
756 Ok((None, next_category_index))
757}
758
759fn parse_category_header<'a>(
760 lines: &'a [String],
761 category_index: usize,
762 label: &str,
763) -> Result<(Vec<&'a str>, usize, usize)> {
764 let count_line_index = category_index
765 .checked_add(1)
766 .ok_or_else(|| EwfError::Malformed(format!("EWF2 {label} category index overflow")))?;
767 let types_line_index = category_index
768 .checked_add(2)
769 .ok_or_else(|| EwfError::Malformed(format!("EWF2 {label} category index overflow")))?;
770 let copy_count_line_index = category_index
771 .checked_add(3)
772 .ok_or_else(|| EwfError::Malformed(format!("EWF2 {label} category index overflow")))?;
773 let root_line_index = category_index
774 .checked_add(4)
775 .ok_or_else(|| EwfError::Malformed(format!("EWF2 {label} category index overflow")))?;
776
777 let entry_count = parse_category_entry_count(
778 get_line(lines, count_line_index, "EWF2 category count")?,
779 "EWF2 category count",
780 )?;
781 let types = parse_type_line(
782 get_line(lines, types_line_index, "EWF2 category types")?,
783 &format!("EWF2 {label} category types"),
784 )?;
785 let copy_entry_count = parse_child_entry_count(
786 get_line(lines, copy_count_line_index, "EWF2 category copy count")?,
787 "EWF2 category copy count",
788 )?;
789 if copy_entry_count != entry_count {
790 return Err(EwfError::Malformed(format!(
791 "EWF2 {label} category entry count mismatch"
792 )));
793 }
794 Ok((types, entry_count, root_line_index))
795}
796
797fn parse_source_row(types: &[&str], row: &str) -> Result<SingleFileSource> {
798 let values = split_exact_typed_values(types, row, "EWF2 source row")?;
799 let mut source = SingleFileSource::default();
800 for (value_type, value) in types.iter().zip(values.iter()) {
801 if value.is_empty() {
802 continue;
803 }
804 match *value_type {
805 "ah" => source.md5 = parse_serialized_base16_string(value, "source MD5 hash")?,
806 "aq" => source.acquisition_time = Some(parse_i64(value, "source acquisition time")?),
807 "do" => source.domain = Some((*value).to_owned()),
808 "dt" => source.drive_type = Some(parse_single_char(value, "source drive type")?),
809 "ev" => source.evidence_number = Some((*value).to_owned()),
810 "gu" => {
811 source.device_guid = parse_serialized_base16_string(value, "source device GUID")?;
812 }
813 "id" => source.identifier = Some(parse_non_negative_i32(value, "source identifier")?),
814 "ip" => source.ip_address = Some((*value).to_owned()),
815 "lo" => source.logical_offset = Some(parse_i64(value, "source logical offset")?),
816 "loc" => source.location = Some((*value).to_owned()),
817 "ma" => {
818 source.mac_address = parse_serialized_base16_string(value, "source MAC address")?;
819 }
820 "mfr" => source.manufacturer = Some((*value).to_owned()),
821 "mo" => source.model = Some((*value).to_owned()),
822 "n" => source.name = Some((*value).to_owned()),
823 "pgu" => {
824 source.primary_device_guid =
825 parse_serialized_base16_string(value, "source primary device GUID")?;
826 }
827 "po" => source.physical_offset = Some(parse_i64(value, "source physical offset")?),
828 "se" => source.serial_number = Some((*value).to_owned()),
829 "sh" => source.sha1 = parse_serialized_base16_string(value, "source SHA1 hash")?,
830 "tb" => source.size = Some(parse_u64(value, "source size")?),
831 _ => {}
832 }
833 }
834 Ok(source)
835}
836
837fn parse_subject_row(types: &[&str], row: &str) -> Result<SingleFileSubject> {
838 let values = split_exact_typed_values(types, row, "EWF2 subject row")?;
839 let mut subject = SingleFileSubject::default();
840 for (value_type, value) in types.iter().zip(values.iter()) {
841 if value.is_empty() {
842 continue;
843 }
844 match *value_type {
845 "id" => subject.identifier = Some(parse_u32(value, "subject identifier")?),
846 "n" => subject.name = Some((*value).to_owned()),
847 _ => {}
848 }
849 }
850 Ok(subject)
851}
852
853fn parse_permission_row(types: &[&str], row: &str) -> Result<SingleFilePermission> {
854 let values = split_exact_typed_values(types, row, "EWF2 permission row")?;
855 let mut permission = SingleFilePermission::default();
856 for (value_type, value) in types.iter().zip(values.iter()) {
857 if value.is_empty() {
858 continue;
859 }
860 match *value_type {
861 "n" => permission.name = Some((*value).to_owned()),
862 "nta" => permission.access_mask = Some(parse_u32(value, "permission access mask")?),
863 "nti" => permission.ace_flags = Some(parse_u32(value, "permission ACE flags")?),
864 "pr" | "pt" => {
865 permission.property_type = Some(parse_u32(value, "permission property type")?);
866 }
867 "s" => permission.identifier = Some((*value).to_owned()),
868 _ => {}
869 }
870 }
871 Ok(permission)
872}
873
874fn require_permission_group_type(permission: &SingleFilePermission, label: &str) -> Result<()> {
875 if permission.property_type != Some(10) {
876 return Err(EwfError::Malformed(format!(
877 "{label} has unsupported permission group type"
878 )));
879 }
880 Ok(())
881}
882
883fn permission_group_from_permission(
884 permission: SingleFilePermission,
885 permission_count: usize,
886) -> SingleFilePermissionGroup {
887 SingleFilePermissionGroup {
888 name: permission.name,
889 identifier: permission.identifier,
890 property_type: permission.property_type,
891 access_mask: permission.access_mask,
892 ace_flags: permission.ace_flags,
893 permissions: Vec::with_capacity(permission_count),
894 }
895}
896
897fn split_typed_values<'a>(types: &[&str], row: &'a str) -> Vec<&'a str> {
898 let mut values: Vec<&str> = row.split('\t').collect();
899 values.truncate(types.len());
900 values.resize(types.len(), "");
901 values
902}
903
904fn split_exact_typed_values<'a>(types: &[&str], row: &'a str, label: &str) -> Result<Vec<&'a str>> {
905 let values: Vec<&str> = row.split('\t').collect();
906 if values.len() != types.len() {
907 return Err(EwfError::Malformed(format!(
908 "{label} value count does not match type count"
909 )));
910 }
911 Ok(values)
912}
913
914fn parse_type_line<'a>(line: &'a str, label: &str) -> Result<Vec<&'a str>> {
915 let types: Vec<&str> = line.split('\t').collect();
916 if types.is_empty() || types.iter().any(|value_type| value_type.is_empty()) {
917 return Err(EwfError::Malformed(format!("{label} has empty type")));
918 }
919 Ok(types)
920}
921
922fn parse_category_entry_count(line: &str, label: &str) -> Result<usize> {
923 let (first, second) = parse_number_pair(line, label)?;
924 if second != 1 {
925 return Err(EwfError::Malformed(format!(
926 "{label} has unsupported category count shape"
927 )));
928 }
929 usize::try_from(first).map_err(|_| EwfError::Malformed(format!("{label} does not fit usize")))
930}
931
932fn parse_child_entry_count(line: &str, label: &str) -> Result<usize> {
933 let (first, second) = parse_number_pair(line, label)?;
934 if first != 0 {
935 return Err(EwfError::Malformed(format!(
936 "{label} has unsupported child count shape"
937 )));
938 }
939 Ok(second)
940}
941
942fn parse_file_entry_child_count(line: &str, label: &str) -> Result<usize> {
943 let (parent_value, child_count) = parse_number_pair(line, label)?;
944 if parent_value != 0 && parent_value != 26 {
945 return Err(EwfError::Malformed(format!(
946 "{label} has unsupported parent value"
947 )));
948 }
949 Ok(child_count)
950}
951
952fn get_line<'a>(lines: &'a [String], index: usize, label: &str) -> Result<&'a str> {
953 lines
954 .get(index)
955 .map(String::as_str)
956 .ok_or_else(|| EwfError::Malformed(format!("{label} is truncated")))
957}
958
959fn require_empty_category_terminator(lines: &[String], cursor: usize, label: &str) -> Result<()> {
960 if !get_line(lines, cursor, label)?.is_empty() {
961 return Err(EwfError::Malformed(format!("{label} is not empty")));
962 }
963 Ok(())
964}
965
966fn parse_single_char(value: &str, label: &str) -> Result<char> {
967 let mut characters = value.chars();
968 let character = characters
969 .next()
970 .ok_or_else(|| EwfError::Malformed(format!("{label} is empty")))?;
971 if characters.next().is_some() {
972 return Err(EwfError::Malformed(format!(
973 "{label} has more than one character"
974 )));
975 }
976 Ok(character)
977}
978
979fn decode_utf16le_lines(data: &[u8]) -> Result<Vec<String>> {
980 if !data.len().is_multiple_of(2) {
981 return Err(EwfError::Malformed(
982 "EWF2 single files data has odd UTF-16 size".into(),
983 ));
984 }
985
986 let units: Vec<u16> = data
987 .chunks_exact(2)
988 .map(|chunk| u16::from_le_bytes(chunk.try_into().expect("chunk size checked")))
989 .collect();
990 let text = String::from_utf16(&units)
991 .map_err(|_| EwfError::Malformed("EWF2 single files data is not valid UTF-16LE".into()))?;
992 let text = text.strip_prefix('\u{feff}').unwrap_or(&text);
993
994 Ok(text
995 .split('\n')
996 .map(|line| line.strip_suffix('\r').unwrap_or(line).to_owned())
997 .collect())
998}
999
1000fn parse_entry(lines: &[String], types: &[&str], cursor: &mut usize) -> Result<SingleFileEntry> {
1001 let count_line = lines
1002 .get(*cursor)
1003 .ok_or_else(|| EwfError::Malformed("EWF2 single file entry missing count".into()))?;
1004 let child_count =
1005 parse_file_entry_child_count(count_line, "EWF2 single file entry child count")?;
1006 *cursor = cursor
1007 .checked_add(1)
1008 .ok_or_else(|| EwfError::Malformed("EWF2 single file entry index overflow".into()))?;
1009
1010 let value_line = lines
1011 .get(*cursor)
1012 .ok_or_else(|| EwfError::Malformed("EWF2 single file entry missing values".into()))?;
1013 let values = split_typed_values(types, value_line);
1014 *cursor = cursor
1015 .checked_add(1)
1016 .ok_or_else(|| EwfError::Malformed("EWF2 single file entry index overflow".into()))?;
1017
1018 let mut entry = SingleFileEntry::default();
1019 for (value_type, value) in types.iter().zip(values.iter()) {
1020 apply_entry_value(&mut entry, value_type, value)?;
1021 }
1022
1023 for _ in 0..child_count {
1024 let child = parse_entry(lines, types, cursor)?;
1025 entry.children.push(child);
1026 }
1027 Ok(entry)
1028}
1029
1030fn apply_entry_value(entry: &mut SingleFileEntry, value_type: &str, value: &str) -> Result<()> {
1031 if value.is_empty() {
1032 return Ok(());
1033 }
1034
1035 match value_type {
1036 "ac" => entry.access_time = Some(parse_i64(value, "access time")?),
1037 "be" => entry.extents = parse_binary_extents(value)?,
1038 "cid" => entry.record_type = Some(parse_u32(value, "record type")?),
1039 "cr" => entry.creation_time = Some(parse_i64(value, "creation time")?),
1040 "dl" => entry.deletion_time = Some(parse_i64(value, "deletion time")?),
1041 "du" => entry.duplicate_data_offset = Some(parse_i64(value, "duplicate data offset")?),
1042 "ea" => entry.attributes = parse_extended_attributes(value)?,
1043 "ha" => entry.md5 = parse_serialized_base16_string(value, "MD5 hash")?,
1044 "id" => entry.identifier = Some(parse_u64(value, "identifier")?),
1045 "lo" => entry.logical_offset = Some(parse_i64(value, "logical offset")?),
1046 "ls" => entry.size = Some(parse_u64(value, "file size")?),
1047 "mid" => entry.guid = parse_serialized_base16_string(value, "GUID")?,
1048 "mo" => entry.entry_modification_time = Some(parse_i64(value, "entry modification time")?),
1049 "n" => entry.name = Some(value.to_owned()),
1050 "opr" => entry.flags = Some(parse_u32(value, "entry flags")?),
1051 "p" => {
1052 entry.file_entry_type = Some(match value {
1053 "f" => SingleFileEntryType::File,
1054 "d" => SingleFileEntryType::Directory,
1055 _ => SingleFileEntryType::Unknown,
1056 });
1057 }
1058 "pm" => entry.permission_group_index = Some(parse_i32(value, "permission group index")?),
1059 "po" => entry.physical_offset = Some(parse_i64(value, "physical offset")?),
1060 "sha" => entry.sha1 = parse_serialized_base16_string(value, "SHA1 hash")?,
1061 "snh" => entry.short_name = Some(parse_short_name(value)?),
1062 "src" => {
1063 entry.source_identifier = Some(parse_non_negative_i32(value, "source identifier")?);
1064 }
1065 "sub" => entry.subject_identifier = Some(parse_u32(value, "subject identifier")?),
1066 "wr" => entry.modification_time = Some(parse_i64(value, "modification time")?),
1067 _ => {}
1068 }
1069 Ok(())
1070}
1071
1072fn parse_binary_extents(value: &str) -> Result<Vec<SingleFileExtent>> {
1073 let mut parts = value.split(' ').filter(|part| !part.is_empty()).peekable();
1074 let extent_count = parts
1075 .next()
1076 .ok_or_else(|| EwfError::Malformed("EWF2 single files extents missing count".into()))
1077 .and_then(|part| parse_hex_usize(part, "extent count"))?;
1078 let mut extents = Vec::with_capacity(extent_count);
1079
1080 while parts.peek().is_some() {
1081 let sparse = if parts.peek() == Some(&"S") {
1082 parts.next();
1083 true
1084 } else {
1085 false
1086 };
1087 let data_offset = parts
1088 .next()
1089 .ok_or_else(|| EwfError::Malformed("EWF2 single files extent missing offset".into()))
1090 .and_then(|part| parse_hex_u64(part, "extent offset"))?;
1091 let data_size = parts
1092 .next()
1093 .ok_or_else(|| EwfError::Malformed("EWF2 single files extent missing size".into()))
1094 .and_then(|part| parse_hex_u64(part, "extent size"))?;
1095 extents.push(SingleFileExtent {
1096 data_offset,
1097 data_size,
1098 sparse,
1099 });
1100 }
1101
1102 if extents.len() != extent_count {
1103 return Err(EwfError::Malformed(
1104 "EWF2 single files extent count mismatch".into(),
1105 ));
1106 }
1107 Ok(extents)
1108}
1109
1110fn parse_short_name(value: &str) -> Result<String> {
1111 let mut parts = value.split(' ');
1112 let declared_size = parts
1113 .next()
1114 .ok_or_else(|| EwfError::Malformed("short name is missing size".into()))?;
1115 let short_name = parts
1116 .next()
1117 .ok_or_else(|| EwfError::Malformed("short name is missing value".into()))?;
1118 if parts.next().is_some() {
1119 return Err(EwfError::Malformed(
1120 "short name has unsupported value count".into(),
1121 ));
1122 }
1123 let declared_size = parse_u64(declared_size, "short name size")?;
1124 let actual_size = short_name
1125 .len()
1126 .checked_add(1)
1127 .ok_or_else(|| EwfError::Malformed("short name size overflow".into()))?;
1128 let actual_size = u64::try_from(actual_size)
1129 .map_err(|_| EwfError::Malformed("short name size does not fit u64".into()))?;
1130 if declared_size != actual_size {
1131 return Err(EwfError::Malformed(
1132 "short name size does not match value size".into(),
1133 ));
1134 }
1135 Ok(short_name.to_owned())
1136}
1137
1138fn parse_extended_attributes(value: &str) -> Result<Vec<SingleFileAttribute>> {
1139 let data = parse_hex_byte_stream(value, "extended attributes")?;
1140 if data.is_empty() {
1141 return Ok(Vec::new());
1142 }
1143 if data.len() < EXTENDED_ATTRIBUTES_HEADER.len()
1144 || &data[..EXTENDED_ATTRIBUTES_HEADER.len()] != EXTENDED_ATTRIBUTES_HEADER
1145 {
1146 return Err(EwfError::Malformed(
1147 "EWF2 single files extended attributes header is unsupported".into(),
1148 ));
1149 }
1150
1151 let mut attributes = Vec::new();
1152 let mut offset = 0_usize;
1153 while offset < data.len() {
1154 let (attribute, read_size) = parse_extended_attribute_record(&data[offset..])?;
1155 offset = offset
1156 .checked_add(read_size)
1157 .ok_or_else(|| EwfError::Malformed("extended attribute offset overflow".into()))?;
1158 if !attribute.is_branch {
1159 attributes.push(SingleFileAttribute {
1160 name: attribute.name,
1161 value: attribute.value,
1162 });
1163 }
1164 }
1165 Ok(attributes)
1166}
1167
1168struct ParsedExtendedAttribute {
1169 name: Option<String>,
1170 value: Option<String>,
1171 is_branch: bool,
1172}
1173
1174fn parse_extended_attribute_record(data: &[u8]) -> Result<(ParsedExtendedAttribute, usize)> {
1175 if data.len() < 13 {
1176 return Err(EwfError::Malformed(
1177 "EWF2 single files extended attribute is truncated".into(),
1178 ));
1179 }
1180
1181 let is_branch = data[4] != 0;
1182 let name_units = u32::from_le_bytes(data[5..9].try_into().expect("slice length checked"));
1183 let value_units = u32::from_le_bytes(data[9..13].try_into().expect("slice length checked"));
1184 let name_size = utf16_byte_size(name_units, "extended attribute name")?;
1185 let value_size = utf16_byte_size(value_units, "extended attribute value")?;
1186 let value_offset = 13_usize
1187 .checked_add(name_size)
1188 .ok_or_else(|| EwfError::Malformed("extended attribute value offset overflow".into()))?;
1189 let end_offset = value_offset
1190 .checked_add(value_size)
1191 .ok_or_else(|| EwfError::Malformed("extended attribute end offset overflow".into()))?;
1192 if data.len() < end_offset {
1193 return Err(EwfError::Malformed(
1194 "EWF2 single files extended attribute is truncated".into(),
1195 ));
1196 }
1197
1198 let name = decode_optional_utf16le_string(&data[13..value_offset], "extended attribute name")?;
1199 let value = decode_optional_utf16le_string(
1200 &data[value_offset..end_offset],
1201 "extended attribute value",
1202 )?;
1203
1204 Ok((
1205 ParsedExtendedAttribute {
1206 name,
1207 value,
1208 is_branch,
1209 },
1210 end_offset,
1211 ))
1212}
1213
1214fn utf16_byte_size(units: u32, label: &str) -> Result<usize> {
1215 let units = usize::try_from(units)
1216 .map_err(|_| EwfError::Malformed(format!("{label} size does not fit usize")))?;
1217 units
1218 .checked_mul(2)
1219 .ok_or_else(|| EwfError::Malformed(format!("{label} size overflow")))
1220}
1221
1222fn decode_optional_utf16le_string(data: &[u8], label: &str) -> Result<Option<String>> {
1223 if data.is_empty() {
1224 return Ok(None);
1225 }
1226 if !data.len().is_multiple_of(2) {
1227 return Err(EwfError::Malformed(format!("{label} has odd UTF-16 size")));
1228 }
1229
1230 let units: Vec<u16> = data
1231 .chunks_exact(2)
1232 .map(|chunk| u16::from_le_bytes(chunk.try_into().expect("chunk size checked")))
1233 .collect();
1234 let mut text = String::from_utf16(&units)
1235 .map_err(|_| EwfError::Malformed(format!("{label} is not valid UTF-16LE")))?;
1236 if text.ends_with('\0') {
1237 text.pop();
1238 }
1239 if text.is_empty() {
1240 Ok(None)
1241 } else {
1242 Ok(Some(text))
1243 }
1244}
1245
1246fn parse_hex_byte_stream(value: &str, label: &str) -> Result<Vec<u8>> {
1247 let hex: String = value
1248 .chars()
1249 .filter(|character| !character.is_whitespace())
1250 .collect();
1251 if !hex.len().is_multiple_of(2) {
1252 return Err(EwfError::Malformed(format!(
1253 "EWF2 single files {label} has odd hexadecimal size"
1254 )));
1255 }
1256
1257 (0..hex.len())
1258 .step_by(2)
1259 .map(|index| {
1260 u8::from_str_radix(&hex[index..index + 2], 16).map_err(|_| {
1261 EwfError::Malformed(format!(
1262 "invalid EWF2 single files {label} hexadecimal value"
1263 ))
1264 })
1265 })
1266 .collect()
1267}
1268
1269fn parse_serialized_base16_string(value: &str, label: &str) -> Result<Option<String>> {
1270 if !value.bytes().all(|byte| byte.is_ascii_hexdigit()) {
1271 return Err(EwfError::Malformed(format!(
1272 "invalid EWF2 single files {label} hexadecimal value"
1273 )));
1274 }
1275 if value.bytes().all(|byte| byte == b'0') {
1276 Ok(None)
1277 } else {
1278 Ok(Some(value.to_ascii_lowercase()))
1279 }
1280}
1281
1282fn parse_number_pair(line: &str, label: &str) -> Result<(u64, usize)> {
1283 let mut parts = line.split('\t');
1284 let first = parts
1285 .next()
1286 .ok_or_else(|| EwfError::Malformed(format!("{label} is missing first value")))?;
1287 let second = parts
1288 .next()
1289 .ok_or_else(|| EwfError::Malformed(format!("{label} is missing second value")))?;
1290 if parts.next().is_some() {
1291 return Err(EwfError::Malformed(format!("{label} has too many values")));
1292 }
1293 let first = parse_u64(first, label)?;
1294 let second = parse_u64(second, label)?;
1295 let second = usize::try_from(second)
1296 .map_err(|_| EwfError::Malformed(format!("{label} does not fit usize")))?;
1297 Ok((first, second))
1298}
1299
1300fn parse_hex_usize(value: &str, label: &str) -> Result<usize> {
1301 let value = parse_hex_u64(value, label)?;
1302 usize::try_from(value)
1303 .map_err(|_| EwfError::Malformed(format!("EWF2 single files {label} out of bounds")))
1304}
1305
1306fn parse_hex_u64(value: &str, label: &str) -> Result<u64> {
1307 u64::from_str_radix(value, 16)
1308 .map_err(|_| EwfError::Malformed(format!("invalid EWF2 single files {label} value")))
1309}
1310
1311fn parse_u64(value: &str, label: &str) -> Result<u64> {
1312 value
1313 .parse()
1314 .map_err(|_| EwfError::Malformed(format!("invalid EWF2 single files {label} value")))
1315}
1316
1317fn parse_u32(value: &str, label: &str) -> Result<u32> {
1318 parse_u64(value, label).and_then(|value| {
1319 u32::try_from(value)
1320 .map_err(|_| EwfError::Malformed(format!("EWF2 single files {label} out of bounds")))
1321 })
1322}
1323
1324fn parse_i64(value: &str, label: &str) -> Result<i64> {
1325 value
1326 .parse()
1327 .map_err(|_| EwfError::Malformed(format!("invalid EWF2 single files {label} value")))
1328}
1329
1330fn parse_i32(value: &str, label: &str) -> Result<i32> {
1331 parse_i64(value, label).and_then(|value| {
1332 i32::try_from(value)
1333 .map_err(|_| EwfError::Malformed(format!("EWF2 single files {label} out of bounds")))
1334 })
1335}
1336
1337fn parse_non_negative_i32(value: &str, label: &str) -> Result<i32> {
1338 let value = parse_i32(value, label)?;
1339 if value < 0 {
1340 return Err(EwfError::Malformed(format!(
1341 "EWF2 single files {label} out of bounds"
1342 )));
1343 }
1344 Ok(value)
1345}