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