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-21 15:03:09 +0300
committerOmer Ben-Amram <omerbenamram@gmail.com>2019-05-21 15:03:09 +0300
commite99b3b44bbdd1081b6963edbefdec8cce3e8188f (patch)
tree48364b48bba321d72ad97c32c8ef083ff637f942
parentb183ce8557ed741a678fb9bd18f1d332082e71a8 (diff)
added more attributes
-rw-r--r--src/attribute/mod.rs52
-rw-r--r--src/attribute/raw.rs44
-rw-r--r--src/attribute/x40.rs40
-rw-r--r--src/attribute/x80.rs31
-rw-r--r--src/attribute/x90.rs31
-rw-r--r--src/bin/mft_dump.rs3
-rw-r--r--src/entry.rs68
-rw-r--r--src/err.rs2
-rw-r--r--src/mft.rs10
9 files changed, 219 insertions, 62 deletions
diff --git a/src/attribute/mod.rs b/src/attribute/mod.rs
index eca250e..9729e38 100644
--- a/src/attribute/mod.rs
+++ b/src/attribute/mod.rs
@@ -2,18 +2,23 @@ pub mod header;
pub mod raw;
pub mod x10;
pub mod x30;
+pub mod x40;
+pub mod x80;
+pub mod x90;
-use crate::impl_serialize_for_bitflags;
+use crate::err::{self, Result};
+use crate::{impl_serialize_for_bitflags, ReadSeek};
use bitflags::bitflags;
-use num_traits::FromPrimitive;
use crate::attribute::raw::RawAttribute;
use crate::attribute::x10::StandardInfoAttr;
use crate::attribute::x30::FileNameAttr;
-use crate::attribute::header::MftAttributeHeader;
-
+use crate::attribute::header::{MftAttributeHeader, ResidentHeader};
+use crate::attribute::x40::ObjectIdAttr;
+use crate::attribute::x80::DataAttr;
+use crate::attribute::x90::IndexRootAttr;
use serde::Serialize;
#[derive(Serialize, Clone, Debug)]
@@ -22,12 +27,51 @@ pub struct MftAttribute {
pub data: MftAttributeContent,
}
+impl MftAttributeContent {
+ pub fn from_stream_resident<S: ReadSeek>(
+ stream: &mut S,
+ header: &MftAttributeHeader,
+ resident: &ResidentHeader,
+ ) -> Result<Self> {
+ match header.type_code {
+ MftAttributeType::StandardInformation => Ok(MftAttributeContent::AttrX10(
+ StandardInfoAttr::from_reader(stream)?,
+ )),
+ MftAttributeType::FileName => Ok(MftAttributeContent::AttrX30(
+ FileNameAttr::from_stream(stream)?,
+ )),
+ // Resident DATA
+ MftAttributeType::DATA => Ok(MftAttributeContent::AttrX80(DataAttr::from_stream(
+ stream,
+ resident.data_size as usize,
+ )?)),
+ // Always Resident
+ MftAttributeType::ObjectId => Ok(MftAttributeContent::AttrX40(
+ ObjectIdAttr::from_stream(stream, resident.data_size as usize)?,
+ )),
+ // Always Resident
+ MftAttributeType::IndexRoot => Ok(MftAttributeContent::AttrX90(
+ IndexRootAttr::from_stream(stream)?,
+ )),
+ // An unparsed resident attribute
+ _ => Ok(MftAttributeContent::Raw(RawAttribute::from_stream(
+ stream,
+ header.type_code.clone(),
+ resident.data_size as usize,
+ )?)),
+ }
+ }
+}
+
#[derive(Serialize, Clone, Debug)]
#[serde(untagged)]
pub enum MftAttributeContent {
Raw(RawAttribute),
+ AttrX80(DataAttr),
AttrX10(StandardInfoAttr),
AttrX30(FileNameAttr),
+ AttrX40(ObjectIdAttr),
+ AttrX90(IndexRootAttr),
/// Empty - used when data is non resident.
None,
}
diff --git a/src/attribute/raw.rs b/src/attribute/raw.rs
index bfc7fac..1e24969 100644
--- a/src/attribute/raw.rs
+++ b/src/attribute/raw.rs
@@ -1,15 +1,37 @@
-use crate::utils;
-use serde::ser;
+use crate::attribute::MftAttributeType;
+use crate::err::{self, Result};
+use crate::{utils, ReadSeek};
+use serde::{ser, Serialize};
+use snafu::ResultExt;
/// Placeholder attribute for currently unparsed attributes.
-#[derive(Clone, Debug)]
-pub struct RawAttribute(pub Vec<u8>);
-
-impl ser::Serialize for RawAttribute {
- fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
- where
- S: ser::Serializer,
- {
- serializer.serialize_str(&utils::to_hex_string(&self.0).to_string())
+#[derive(Serialize, Clone, Debug)]
+pub struct RawAttribute {
+ pub attribute_type: MftAttributeType,
+ #[serde(serialize_with = "data_as_hex")]
+ pub data: Vec<u8>,
+}
+
+impl RawAttribute {
+ pub fn from_stream<S: ReadSeek>(
+ stream: &mut S,
+ attribute_type: MftAttributeType,
+ data_size: usize,
+ ) -> Result<Self> {
+ let mut data = vec![0_u8; data_size];
+
+ stream.read_exact(&mut data).context(err::IoError)?;
+
+ Ok(RawAttribute {
+ attribute_type,
+ data,
+ })
}
}
+
+fn data_as_hex<S>(x: &Vec<u8>, s: S) -> std::result::Result<S::Ok, S::Error>
+where
+ S: ser::Serializer,
+{
+ s.serialize_str(&utils::to_hex_string(x))
+}
diff --git a/src/attribute/x40.rs b/src/attribute/x40.rs
new file mode 100644
index 0000000..beea829
--- /dev/null
+++ b/src/attribute/x40.rs
@@ -0,0 +1,40 @@
+use crate::err::{self, Result};
+use crate::ReadSeek;
+use serde::Serialize;
+use snafu::ResultExt;
+use winstructs::guid::Guid;
+
+/// $Data Attribute
+#[derive(Serialize, Clone, Debug)]
+pub struct ObjectIdAttr {
+ /// Unique Id assigned to file
+ pub object_id: Guid,
+ /// Volume where file was created
+ pub birth_volume_id: Option<Guid>,
+ /// Original Object Id of file
+ pub birth_object_id: Option<Guid>,
+ /// Domain in which object was created
+ pub domain_id: Option<Guid>,
+}
+
+impl ObjectIdAttr {
+ /// Data size should be either 16 or 64
+ pub fn from_stream<S: ReadSeek>(stream: &mut S, data_size: usize) -> Result<ObjectIdAttr> {
+ let object_id = Guid::from_stream(stream).context(err::FailedToReadGuid)?;
+ let (birth_volume_id, birth_object_id, domain_id) = if data_size == 64 {
+ let g1 = Guid::from_stream(stream).context(err::FailedToReadGuid)?;
+ let g2 = Guid::from_stream(stream).context(err::FailedToReadGuid)?;
+ let g3 = Guid::from_stream(stream).context(err::FailedToReadGuid)?;
+ (Some(g1), Some(g2), Some(g3))
+ } else {
+ (None, None, None)
+ };
+
+ Ok(ObjectIdAttr {
+ object_id,
+ birth_volume_id,
+ birth_object_id,
+ domain_id,
+ })
+ }
+}
diff --git a/src/attribute/x80.rs b/src/attribute/x80.rs
new file mode 100644
index 0000000..f194a0e
--- /dev/null
+++ b/src/attribute/x80.rs
@@ -0,0 +1,31 @@
+use crate::err::{self, Result};
+use crate::{utils, ReadSeek};
+use serde::ser;
+use snafu::ResultExt;
+
+/// $Data Attribute
+#[derive(Clone, Debug)]
+pub struct DataAttr(Vec<u8>);
+
+impl DataAttr {
+ pub fn from_stream<S: ReadSeek>(stream: &mut S, data_size: usize) -> Result<DataAttr> {
+ let mut data = vec![0_u8; data_size];
+
+ stream.read_exact(&mut data).context(err::IoError)?;
+
+ Ok(DataAttr(data))
+ }
+
+ pub fn data(&self) -> &[u8] {
+ &self.0
+ }
+}
+
+impl ser::Serialize for DataAttr {
+ fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
+ where
+ S: ser::Serializer,
+ {
+ serializer.serialize_str(&utils::to_hex_string(&self.0).to_string())
+ }
+}
diff --git a/src/attribute/x90.rs b/src/attribute/x90.rs
new file mode 100644
index 0000000..e64c33c
--- /dev/null
+++ b/src/attribute/x90.rs
@@ -0,0 +1,31 @@
+use crate::err::{self, Result};
+use crate::ReadSeek;
+use byteorder::{LittleEndian, ReadBytesExt};
+
+use serde::Serialize;
+
+/// $IndexRoot Attribute
+#[derive(Serialize, Clone, Debug)]
+pub struct IndexRootAttr {
+ /// Unique Id assigned to file
+ pub attribute_type: u32,
+ /// Collation rule used to sort the index entries.
+ /// If type is $FILENAME, this must be COLLATION_FILENAME
+ pub collation_rule: u32,
+ /// The index entry size
+ pub index_entry_size: u32,
+ /// The index entry number of cluster blocks
+ pub index_entry_number_of_cluster_blocks: u32,
+}
+
+impl IndexRootAttr {
+ /// Data size should be either 16 or 64
+ pub fn from_stream<S: ReadSeek>(stream: &mut S) -> Result<IndexRootAttr> {
+ Ok(IndexRootAttr {
+ attribute_type: stream.read_u32::<LittleEndian>()?,
+ collation_rule: stream.read_u32::<LittleEndian>()?,
+ index_entry_size: stream.read_u32::<LittleEndian>()?,
+ index_entry_number_of_cluster_blocks: stream.read_u32::<LittleEndian>()?,
+ })
+ }
+}
diff --git a/src/bin/mft_dump.rs b/src/bin/mft_dump.rs
index f8d9da0..f16ee90 100644
--- a/src/bin/mft_dump.rs
+++ b/src/bin/mft_dump.rs
@@ -93,7 +93,8 @@ impl FlatMftEntryWithName {
FlatMftEntryWithName {
entry_id: entry.header.record_number,
- signature: String::from_utf8(entry.header.signature.to_ascii_uppercase()).unwrap(),
+ signature: String::from_utf8(entry.header.signature.to_ascii_uppercase())
+ .expect("It should be either FILE or BAAD (valid utf-8)"),
sequence: entry.header.sequence,
hard_link_count: entry.header.hard_link_count,
flags: entry.header.flags,
diff --git a/src/entry.rs b/src/entry.rs
index f1d6dbc..d1aeb54 100644
--- a/src/entry.rs
+++ b/src/entry.rs
@@ -18,6 +18,9 @@ use crate::attribute::{MftAttribute, MftAttributeContent, MftAttributeType};
use crate::attribute::raw::RawAttribute;
use crate::attribute::x30::FileNameAttr;
+use crate::attribute::x40::ObjectIdAttr;
+use crate::attribute::x80::DataAttr;
+use crate::attribute::x90::IndexRootAttr;
use std::io::Read;
use std::io::SeekFrom;
use std::io::{Cursor, Seek};
@@ -206,8 +209,16 @@ impl MftEntry {
self.header.flags.bits() & 0x02 != 0
}
- /// Returns an iterator over the attributes of the entry.
+ /// Returns an iterator over all the attributes of the entry.
pub fn iter_attributes(&self) -> impl Iterator<Item = Result<MftAttribute>> + '_ {
+ self.iter_attributes_matching(None)
+ }
+
+ /// Returns an iterator over the attributes in the list given in `types`, skips other attributes.
+ pub fn iter_attributes_matching(
+ &self,
+ types: Option<&[MftAttributeType]>,
+ ) -> impl Iterator<Item = Result<MftAttribute>> + '_ {
let mut cursor = Cursor::new(&self.data);
let mut offset = u64::from(self.header.first_attribute_record_offset);
let mut exhausted = false;
@@ -232,45 +243,24 @@ impl MftEntry {
offset += u64::from(header.record_length);
// Check if the header is resident, and if it is, read the attribute content.
- match header.residential_header {
- ResidentialHeader::Resident(ref resident) => match header.type_code {
- MftAttributeType::StandardInformation => {
- match StandardInfoAttr::from_reader(&mut cursor) {
- Ok(content) => Some(Ok(MftAttribute {
- header,
- data: MftAttributeContent::AttrX10(content),
- })),
- Err(e) => Some(Err(e)),
- }
- }
- MftAttributeType::FileName => {
- match FileNameAttr::from_stream(&mut cursor) {
- Ok(content) => Some(Ok(MftAttribute {
- header,
- data: MftAttributeContent::AttrX30(content),
- })),
- Err(e) => Some(Err(e.into())),
- }
- }
- _ => {
- let mut data = vec![0_u8; resident.data_size as usize];
-
- match cursor.read_exact(&mut data).context(err::IoError) {
- Ok(_) => {}
- Err(err) => return Some(Err(err.into())),
- };
-
- Some(Ok(MftAttribute {
- header,
- data: MftAttributeContent::Raw(RawAttribute(data)),
- }))
+ let attribute_content = match header.residential_header {
+ ResidentialHeader::Resident(ref resident) => {
+ match MftAttributeContent::from_stream_resident(
+ &mut cursor,
+ &header,
+ resident,
+ ) {
+ Ok(content) => content,
+ Err(e) => return Some(Err(e)),
}
- },
- ResidentialHeader::NonResident(_) => Some(Ok(MftAttribute {
- header,
- data: MftAttributeContent::None,
- })),
- }
+ }
+ ResidentialHeader::NonResident(_) => MftAttributeContent::None,
+ };
+
+ Some(Ok(MftAttribute {
+ header,
+ data: attribute_content,
+ }))
}
None => None,
},
diff --git a/src/err.rs b/src/err.rs
index 952cc37..02bc09d 100644
--- a/src/err.rs
+++ b/src/err.rs
@@ -40,6 +40,8 @@ pub enum Error {
FailedToReadMftReference { source: winstructs::err::Error },
#[snafu(display("Failed to read WindowsTime: `{}`", source))]
FailedToReadWindowsTime { source: winstructs::err::Error },
+ #[snafu(display("Failed to read GUID: `{}`", source))]
+ FailedToReadGuid { source: winstructs::err::Error },
#[snafu(display("An unexpected error has occurred: {}", detail))]
Any { detail: String },
}
diff --git a/src/mft.rs b/src/mft.rs
index d644be8..56fa2d9 100644
--- a/src/mft.rs
+++ b/src/mft.rs
@@ -126,13 +126,9 @@ impl<T: ReadSeek> MftParser<T> {
let path = match self.get_entry(parent_entry_id).ok() {
Some(parent) => match self.get_full_path_for_entry(&parent) {
Ok(Some(path)) => path,
- _ => {
- return err::Any {
- detail: "Unexpected missing parent.\
- This is a bug, please report it at report at https://github.com/omerbenamram/mft/issues",
- }
- .fail()
- }
+ // I have a parent, which doesn't have a filename attribute.
+ // Default to root.
+ _ => PathBuf::new(),
},
// Parent is maybe corrupted or incomplete, use a sentinel instead.
None => PathBuf::from("[Unknown]"),