Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/windirstat/mft.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOmer Ben-Amram <omerbenamram@gmail.com>2019-05-15 23:28:59 +0300
committerOmer Ben-Amram <omerbenamram@gmail.com>2019-05-15 23:28:59 +0300
commit6a3c23feadc5290b20a4e6f55775f0a4ac91e662 (patch)
treee2a64f2b462c0b6fa7f89cbeb8343952b13a1515
parent93551bb9acce8d71004f5485ffd7e896292c87a4 (diff)
Implement get full pathget-full-path
-rw-r--r--src/entry.rs4
-rw-r--r--src/lib.rs3
-rw-r--r--src/mft.rs65
-rw-r--r--src/tests/fixtures.rs13
-rw-r--r--src/tests/mod.rs1
5 files changed, 61 insertions, 25 deletions
diff --git a/src/entry.rs b/src/entry.rs
index afe19d7..a19cad6 100644
--- a/src/entry.rs
+++ b/src/entry.rs
@@ -25,7 +25,7 @@ use std::io::{Cursor, Seek};
const SEQUENCE_NUMBER_STRIDE: usize = 512;
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct MftEntry {
pub header: EntryHeader,
pub data: Vec<u8>,
@@ -46,7 +46,7 @@ impl ser::Serialize for MftEntry {
/// https://docs.microsoft.com/en-us/windows/desktop/devnotes/file-record-segment-header
/// The MFT entry can be filled entirely with 0-byte values.
-#[derive(Serialize, Debug)]
+#[derive(Serialize, Debug, Clone)]
pub struct EntryHeader {
/// MULTI_SECTOR_HEADER
/// The signature. This value is a convenience to the user.
diff --git a/src/lib.rs b/src/lib.rs
index 5e4a217..78c8c4b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -18,6 +18,9 @@ pub mod mft;
pub(crate) mod macros;
pub(crate) mod utils;
+#[cfg(test)]
+pub(crate) mod tests;
+
pub trait ReadSeek: Read + Seek {
fn tell(&mut self) -> io::Result<u64> {
self.seek(SeekFrom::Current(0))
diff --git a/src/mft.rs b/src/mft.rs
index 02b2194..88635bb 100644
--- a/src/mft.rs
+++ b/src/mft.rs
@@ -2,7 +2,7 @@ use crate::entry::MftEntry;
use crate::err::{self, Result};
use crate::{EntryHeader, ReadSeek};
-use log::debug;
+use log::{debug, trace};
use snafu::ResultExt;
use crate::attribute::MftAttributeContent::AttrX30;
@@ -93,28 +93,43 @@ impl<T: ReadSeek> MftParser<T> {
}
/// Gets the full path for an entry.
+ /// Cached computations.
pub fn get_full_path_for_entry(&mut self, entry: &MftEntry) -> Result<Option<PathBuf>> {
+ let entry_id = entry.header.entry_reference.entry;
+
for attribute in entry.iter_attributes().filter_map(|a| a.ok()) {
if let AttrX30(filename_header) = attribute.data {
let parent_entry_id = filename_header.parent.entry;
- if parent_entry_id > 0 {
- // If my parent path is known, then my path is parent's path + my name.
- // Else, look and cache my parent's path.
- let parent_entry = self.get_entry(parent_entry_id).ok();
- let parent_path =
- self.entries_cache
- .entry(parent_entry_id)
- .or_insert_with(|| match parent_entry {
- Some(e) => self
- .get_full_path_for_entry(&e)
- .expect("We've checked that parent_entry > 0")
- .unwrap(),
+ if parent_entry_id > 0 {
+ // If i'm my own parent, I'm the root path.
+ if parent_entry_id == entry_id {
+ return Ok(Some(PathBuf::from(filename_header.name)));
+ }
- None => PathBuf::from("[Unknown]"),
- });
+ let cached_entry = self.entries_cache.get(&parent_entry_id);
- return Ok(Some(parent_path.clone().join(filename_header.name)));
+ // If my parent path is known, then my path is parent's path + my name.
+ // Else, look and cache my parent's path.
+ if let Some(cached_parent_path) = cached_entry {
+ return Ok(Some(cached_parent_path.clone().join(filename_header.name)));
+ } else {
+ let path = match self.get_entry(parent_entry_id).ok() {
+ Some(parent) => self
+ .get_full_path_for_entry(&parent)
+ .expect("We've checked that parent_entry > 0")
+ .unwrap(),
+ None => PathBuf::from("[Unknown]"),
+ };
+
+ self.entries_cache.insert(parent_entry_id, path.clone());
+ return Ok(Some(path.join(filename_header.name)));
+ }
+ } else {
+ let root = PathBuf::from(filename_header.name);
+ self.entries_cache
+ .insert(entry.header.entry_reference.entry, root.clone());
+ return Ok(Some(root));
}
}
}
@@ -125,19 +140,14 @@ impl<T: ReadSeek> MftParser<T> {
#[cfg(test)]
mod tests {
+ use crate::tests::fixtures::mft_sample;
use crate::MftParser;
use std::path::PathBuf;
// entrypoint for clion profiler.
#[test]
fn test_process_90_mft_entries() {
- let sample = PathBuf::from(file!())
- .parent()
- .unwrap()
- .parent()
- .unwrap()
- .join("samples")
- .join("MFT");
+ let sample = mft_sample();
let mut parser = MftParser::from_path(sample).unwrap();
@@ -148,4 +158,13 @@ mod tests {
}
}
}
+
+ #[test]
+ fn test_get_full_name() {
+ 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();
+ }
}
diff --git a/src/tests/fixtures.rs b/src/tests/fixtures.rs
new file mode 100644
index 0000000..38e338f
--- /dev/null
+++ b/src/tests/fixtures.rs
@@ -0,0 +1,13 @@
+use std::path::PathBuf;
+
+pub fn mft_sample() -> PathBuf {
+ PathBuf::from(file!())
+ .parent()
+ .unwrap()
+ .parent()
+ .unwrap()
+ .parent()
+ .unwrap()
+ .join("samples")
+ .join("MFT")
+}
diff --git a/src/tests/mod.rs b/src/tests/mod.rs
new file mode 100644
index 0000000..d066349
--- /dev/null
+++ b/src/tests/mod.rs
@@ -0,0 +1 @@
+pub mod fixtures;