From 599bc462daf4dc3615e090347359f01630e67a28 Mon Sep 17 00:00:00 2001 From: Mason Bartle Date: Wed, 2 Dec 2020 23:22:36 -0500 Subject: [PATCH] Convert all paths from PathBuf to String. We're not using any PathBuf methods except join, and can use string formatting instead, so MFT paths don't differ from OS to OS. --- src/bin/mft_dump.rs | 2 +- src/csv.rs | 3 +-- src/mft.rs | 41 ++++++++++++++++++++++++++--------------- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/bin/mft_dump.rs b/src/bin/mft_dump.rs index ff8516b..a339bcb 100644 --- a/src/bin/mft_dump.rs +++ b/src/bin/mft_dump.rs @@ -313,7 +313,7 @@ impl MftDump { if let Some(data_streams_dir) = &self.data_streams_output { if let Ok(Some(path)) = parser.get_full_path_for_entry(&entry) { - let sanitized_path = sanitized(&path.to_string_lossy()); + let sanitized_path = sanitized(&path.to_string()); for (i, (name, stream)) in entry .iter_attributes() diff --git a/src/csv.rs b/src/csv.rs index 8f37922..a83d4f3 100644 --- a/src/csv.rs +++ b/src/csv.rs @@ -8,7 +8,6 @@ use serde::Serialize; use chrono::{DateTime, Utc}; use std::io::{Read, Seek}; -use std::path::PathBuf; /// Used for CSV output #[derive(Serialize)] @@ -52,7 +51,7 @@ pub struct FlatMftEntryWithName { pub file_name_last_access: Option>, pub file_name_created: Option>, - pub full_path: PathBuf, + pub full_path: String, } impl FlatMftEntryWithName { diff --git a/src/mft.rs b/src/mft.rs index 43cb4ec..47b3d53 100644 --- a/src/mft.rs +++ b/src/mft.rs @@ -8,7 +8,7 @@ use lru::LruCache; use std::fs::{self, File}; use std::io::{BufReader, Cursor, Read, Seek, SeekFrom}; use std::num::NonZeroUsize; -use std::path::{Path, PathBuf}; +use std::path::Path; pub struct MftParser { data: T, @@ -16,7 +16,7 @@ pub struct MftParser { /// Instead this will be guessed by the entry size of the first entry. entry_size: u32, size: u64, - entries_cache: LruCache, + entries_cache: LruCache, } impl MftParser> { @@ -43,6 +43,15 @@ impl MftParser>> { } } +fn join_paths(path: String, name: &str) -> String { + // Ignore empty parents + if path == "" { + String::from(name) + } else { + String::from(format!("{}/{}", path, name)) + } +} + impl MftParser { pub fn from_read_seek(mut data: T, size: Option) -> Result { // We use the first entry to guess the entry size for all the other records. @@ -87,32 +96,33 @@ impl MftParser { (0..total_entries).map(move |i| self.get_entry(i)) } - fn inner_get_entry(&mut self, parent_entry_id: u64, entry_name: Option<&str>) -> PathBuf { + + fn inner_get_entry(&mut self, parent_entry_id: u64, entry_name: Option<&str>) -> String { let cached_entry = self.entries_cache.get(&parent_entry_id); // If my parent path is known, then my path is parent's full path + my name. // Else, retrieve and cache my parent's path. if let Some(cached_parent_path) = cached_entry { match entry_name { - Some(name) => cached_parent_path.clone().join(name), + Some(name) => join_paths(String::from(cached_parent_path), name), None => cached_parent_path.clone(), } } else { let path = match self.get_entry(parent_entry_id).ok() { Some(parent) => match self.get_full_path_for_entry(&parent) { Ok(Some(path)) if parent.is_dir() => path, - Ok(Some(_)) => PathBuf::from("[Unknown]"), + Ok(Some(_)) => String::from("[Unknown]"), // I have a parent, which doesn't have a filename attribute. // Default to root. - _ => PathBuf::new(), + _ => String::new(), }, // Parent is maybe corrupted or incomplete, use a sentinel instead. - None => PathBuf::from("[Unknown]"), + None => String::from("[Unknown]"), }; self.entries_cache.put(parent_entry_id, path.clone()); match entry_name { - Some(name) => path.join(name), + Some(name) => join_paths(path, name), None => path, } } @@ -120,7 +130,7 @@ impl MftParser { /// Gets the full path for an entry. /// Caches computations. - pub fn get_full_path_for_entry(&mut self, entry: &MftEntry) -> Result> { + pub fn get_full_path_for_entry(&mut self, entry: &MftEntry) -> Result> { let entry_id = entry.header.record_number; match entry.find_best_name_attribute() { Some(filename_header) => { @@ -128,7 +138,7 @@ impl MftParser { // MFT entry 5 is the root path. if parent_entry_id == 5 { - return Ok(Some(PathBuf::from(filename_header.name))); + return Ok(Some(String::from(filename_header.name))); } if parent_entry_id == entry_id { @@ -136,7 +146,7 @@ impl MftParser { "Found self-referential file path, for entry ID {}", entry_id ); - return Ok(Some(PathBuf::from("[Orphaned]").join(filename_header.name))); + return Ok(Some(String::from(format!("[Orphaned]/{}", filename_header.name)))); } if parent_entry_id > 0 { @@ -147,7 +157,7 @@ impl MftParser { } else { trace!("Found orphaned entry ID {}", entry_id); - let orphan = PathBuf::from("[Orphaned]").join(filename_header.name); + let orphan = String::from(format!("[Orphaned]/{}", filename_header.name)); self.entries_cache .put(entry.header.record_number, orphan.clone()); @@ -156,7 +166,7 @@ impl MftParser { } } None => match entry.header.base_reference.entry { - // I don't have a parent reference, and no X30 attribute. Though luck. + // I don't have a parent reference, and no X30 attribute. Tough luck. 0 => Ok(None), parent_entry_id => Ok(Some(self.inner_get_entry(parent_entry_id, None))), }, @@ -212,7 +222,8 @@ mod tests { let sample = mft_sample(); let mut parser = MftParser::from_path(sample).unwrap(); - let e = parser.get_entry(5).unwrap(); - parser.get_full_path_for_entry(&e).unwrap(); + let e = parser.get_entry(500).unwrap(); + let path = parser.get_full_path_for_entry(&e).unwrap().unwrap(); + assert_eq!(path, "WINDOWS/system32/devmgmt.msc") } }