diff options
author | Omer Ben-Amram <omerbenamram@gmail.com> | 2019-05-18 14:15:27 +0300 |
---|---|---|
committer | Omer Ben-Amram <omerbenamram@gmail.com> | 2019-05-18 14:15:27 +0300 |
commit | c7642a52e77ac7420e2a20f92be538a1ae891e71 (patch) | |
tree | d18ad3a6aff64b4a778c22471de669359f70b7e0 | |
parent | 30c6d49a0ec4518d565c818dd69cff4f7b69d29d (diff) |
more progress
-rw-r--r-- | src/attribute/mod.rs | 20 | ||||
-rw-r--r-- | src/attribute/x10.rs | 21 | ||||
-rw-r--r-- | src/attribute/x30.rs | 38 | ||||
-rw-r--r-- | src/bin/mft_dump.rs | 58 | ||||
-rw-r--r-- | src/lib.rs | 2 |
5 files changed, 89 insertions, 50 deletions
diff --git a/src/attribute/mod.rs b/src/attribute/mod.rs index 2b890a6..eca250e 100644 --- a/src/attribute/mod.rs +++ b/src/attribute/mod.rs @@ -63,6 +63,26 @@ pub enum MftAttributeType { } bitflags! { + pub struct FileAttributeFlags: u32 { + const FILE_ATTRIBUTE_READONLY = 0x0000_0001; + const FILE_ATTRIBUTE_HIDDEN = 0x0000_0002; + const FILE_ATTRIBUTE_SYSTEM = 0x0000_0004; + const FILE_ATTRIBUTE_ARCHIVE = 0x0000_0020; + const FILE_ATTRIBUTE_DEVICE = 0x0000_0040; + const FILE_ATTRIBUTE_NORMAL = 0x0000_0080; + const FILE_ATTRIBUTE_TEMPORARY = 0x0000_0100; + const FILE_ATTRIBUTE_SPARSE_FILE = 0x0000_0200; + const FILE_ATTRIBUTE_REPARSE_POINT = 0x0000_0400; + const FILE_ATTRIBUTE_COMPRESSED = 0x0000_0800; + const FILE_ATTRIBUTE_OFFLINE = 0x0000_1000; + const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x0000_2000; + const FILE_ATTRIBUTE_ENCRYPTED = 0x0000_4000; + } +} + +impl_serialize_for_bitflags! {FileAttributeFlags} + +bitflags! { #[derive(Default)] pub struct AttributeDataFlags: u16 { const IS_COMPRESSED = 0x0001; diff --git a/src/attribute/x10.rs b/src/attribute/x10.rs index 7e08ec0..ce7a610 100644 --- a/src/attribute/x10.rs +++ b/src/attribute/x10.rs @@ -1,6 +1,7 @@ +use crate::attribute::FileAttributeFlags; use crate::err::{self, Result}; - use crate::ReadSeek; + use byteorder::{LittleEndian, ReadBytesExt}; use chrono::{DateTime, Utc}; use log::trace; @@ -14,7 +15,8 @@ pub struct StandardInfoAttr { pub modified: DateTime<Utc>, pub mft_modified: DateTime<Utc>, pub accessed: DateTime<Utc>, - pub file_flags: u32, + /// DOS File Permissions + pub file_flags: FileAttributeFlags, pub max_version: u32, pub version: u32, pub class_id: u32, @@ -33,8 +35,8 @@ impl StandardInfoAttr { /// /// ``` /// use mft::attribute::x10::StandardInfoAttr; + /// use mft::attribute::FileAttributeFlags; /// # use std::io::Cursor; - /// # fn test_standard_information() { /// let attribute_buffer: &[u8] = &[ /// 0x2F,0x6D,0xB6,0x6F,0x0C,0x97,0xCE,0x01,0x56,0xCD,0x1A,0x75,0x73,0xB5,0xCE,0x01, /// 0x56,0xCD,0x1A,0x75,0x73,0xB5,0xCE,0x01,0x56,0xCD,0x1A,0x75,0x73,0xB5,0xCE,0x01, @@ -45,18 +47,17 @@ impl StandardInfoAttr { /// /// let attribute = StandardInfoAttr::from_reader(&mut Cursor::new(attribute_buffer)).unwrap(); /// - /// assert_eq!(attribute.created.timestamp(), 130207518909951279); - /// assert_eq!(attribute.modified.timestamp(), 130240946730880342); - /// assert_eq!(attribute.mft_modified.timestamp(), 130240946730880342); - /// assert_eq!(attribute.accessed.timestamp(), 130240946730880342); - /// assert_eq!(attribute.file_flags, 32); + /// assert_eq!(attribute.created.timestamp(), 1376278290); + /// assert_eq!(attribute.modified.timestamp(), 1379621073); + /// assert_eq!(attribute.mft_modified.timestamp(), 1379621073); + /// assert_eq!(attribute.accessed.timestamp(), 1379621073); + /// assert_eq!(attribute.file_flags.bits(), 32); /// assert_eq!(attribute.max_version, 0); /// assert_eq!(attribute.version, 0); /// assert_eq!(attribute.class_id, 0); /// assert_eq!(attribute.security_id, 1456); /// assert_eq!(attribute.quota, 0); /// assert_eq!(attribute.usn, 8768215144); - /// # } /// ``` pub fn from_reader<S: ReadSeek>(reader: &mut S) -> Result<StandardInfoAttr> { trace!("Offset {}: StandardInfoAttr", reader.tell()?); @@ -78,7 +79,7 @@ impl StandardInfoAttr { modified, mft_modified, accessed, - file_flags: reader.read_u32::<LittleEndian>()?, + file_flags: FileAttributeFlags::from_bits_truncate(reader.read_u32::<LittleEndian>()?), max_version: reader.read_u32::<LittleEndian>()?, version: reader.read_u32::<LittleEndian>()?, class_id: reader.read_u32::<LittleEndian>()?, diff --git a/src/attribute/x30.rs b/src/attribute/x30.rs index 9fa4453..f073b9e 100644 --- a/src/attribute/x30.rs +++ b/src/attribute/x30.rs @@ -1,8 +1,8 @@ +use crate::attribute::FileAttributeFlags; use crate::err::{self, Result}; -use crate::{impl_serialize_for_bitflags, ReadSeek}; +use crate::ReadSeek; use log::trace; -use bitflags::bitflags; use byteorder::{LittleEndian, ReadBytesExt}; use encoding::all::UTF_16LE; use encoding::{DecoderTrap, Encoding}; @@ -30,26 +30,6 @@ pub struct FileNameAttr { pub name: String, } -bitflags! { - pub struct FileAttributeFlags: u32 { - const FILE_ATTRIBUTE_READONLY = 0x0000_0001; - const FILE_ATTRIBUTE_HIDDEN = 0x0000_0002; - const FILE_ATTRIBUTE_SYSTEM = 0x0000_0004; - const FILE_ATTRIBUTE_ARCHIVE = 0x0000_0020; - const FILE_ATTRIBUTE_DEVICE = 0x0000_0040; - const FILE_ATTRIBUTE_NORMAL = 0x0000_0080; - const FILE_ATTRIBUTE_TEMPORARY = 0x0000_0100; - const FILE_ATTRIBUTE_SPARSE_FILE = 0x0000_0200; - const FILE_ATTRIBUTE_REPARSE_POINT = 0x0000_0400; - const FILE_ATTRIBUTE_COMPRESSED = 0x0000_0800; - const FILE_ATTRIBUTE_OFFLINE = 0x0000_1000; - const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x0000_2000; - const FILE_ATTRIBUTE_ENCRYPTED = 0x0000_4000; - } -} - -impl_serialize_for_bitflags! {FileAttributeFlags} - impl FileNameAttr { /// Parse a Filename attrbiute buffer. /// @@ -60,7 +40,6 @@ impl FileNameAttr { /// ``` /// use mft::attribute::x30::FileNameAttr; /// # use std::io::Cursor; - /// # fn test_filename_attribute() { /// let attribute_buffer: &[u8] = &[ /// 0x05,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0xD5,0x2D,0x48,0x58,0x43,0x5F,0xCE,0x01, /// 0xD5,0x2D,0x48,0x58,0x43,0x5F,0xCE,0x01,0xD5,0x2D,0x48,0x58,0x43,0x5F,0xCE,0x01, @@ -72,19 +51,18 @@ impl FileNameAttr { /// /// let attribute = FileNameAttr::from_stream(&mut Cursor::new(attribute_buffer)).unwrap(); /// - /// assert_eq!(attribute.parent.entry, 1407374883553285); - /// assert_eq!(attribute.created.timestamp(), 130146182088895957); - /// assert_eq!(attribute.modified.timestamp(), 130146182088895957); - /// assert_eq!(attribute.mft_modified.timestamp(), 130146182088895957); - /// assert_eq!(attribute.accessed.timestamp(), 130146182088895957); + /// assert_eq!(attribute.parent.entry, 5); + /// assert_eq!(attribute.created.timestamp(), 1370144608); + /// assert_eq!(attribute.modified.timestamp(), 1370144608); + /// assert_eq!(attribute.mft_modified.timestamp(), 1370144608); + /// assert_eq!(attribute.accessed.timestamp(), 1370144608); /// assert_eq!(attribute.logical_size, 67108864); /// assert_eq!(attribute.physical_size, 67108864); - /// assert_eq!(attribute.flags, 6); + /// assert_eq!(attribute.flags.bits(), 6); /// assert_eq!(attribute.reparse_value, 0); /// assert_eq!(attribute.name_length, 8); /// assert_eq!(attribute.namespace, 3); /// assert_eq!(attribute.name, "$LogFile"); - /// # } /// ``` pub fn from_stream<S: ReadSeek>(stream: &mut S) -> Result<FileNameAttr> { trace!("Offset {}: FilenameAttr", stream.tell()?); diff --git a/src/bin/mft_dump.rs b/src/bin/mft_dump.rs index eacce18..32bfdaa 100644 --- a/src/bin/mft_dump.rs +++ b/src/bin/mft_dump.rs @@ -3,12 +3,14 @@ use env_logger; use log::info; use mft::err::Result; -use mft::attribute::{MftAttributeContent, MftAttributeType}; +use mft::attribute::FileAttributeFlags; +use mft::attribute::MftAttributeContent; use mft::entry::EntryFlags; use mft::mft::MftParser; use mft::{MftAttribute, MftEntry, ReadSeek}; use serde::Serialize; +use chrono::{DateTime, Utc}; use std::io; use std::io::Write; use std::path::PathBuf; @@ -30,21 +32,42 @@ impl OutputFormat { /// Used for CSV output #[derive(Serialize)] +#[serde(rename_all = "PascalCase")] pub struct FlatMftEntryWithName { pub signature: String, pub logfile_sequence_number: u64, pub sequence: u16, pub hard_link_count: u16, pub flags: EntryFlags, + + /// The size of the file, in bytes. pub used_entry_size: u32, pub total_entry_size: u32, + pub parent_reference_entry: u64, pub parent_reference_sequence: u16, + pub record_number: u64, pub entry_reference_entry: u64, + + /// Indicates whether the record is free or not. + // TODO: implement + pub in_use: bool, + /// Indicates whether the record is a directory. + pub is_a_directory: bool, + /// The file extension (only for non-directories entries) pub entry_reference_sequence: u16, - // TODO: figure out a name to make this play nice with CSV output. - // pub file_name_records: Vec<FileNameAttr>, + /// All of these fields are present for entries that have an 0x10 attribute. + pub standard_info_flags: Option<FileAttributeFlags>, + pub standard_info_last_modified: Option<DateTime<Utc>>, + pub standard_info_last_access: Option<DateTime<Utc>>, + pub standard_info_created: Option<DateTime<Utc>>, + /// All of these fields are present for entries that have an 0x30 attribute. + pub file_name_flags: Option<FileAttributeFlags>, + pub file_name_last_modified: Option<DateTime<Utc>>, + pub file_name_last_access: Option<DateTime<Utc>>, + pub file_name_created: Option<DateTime<Utc>>, + pub full_path: PathBuf, } @@ -56,15 +79,24 @@ impl FlatMftEntryWithName { let entry_attributes: Vec<MftAttribute> = entry.iter_attributes().filter_map(Result::ok).collect(); - let mut file_name_attributes = vec![]; + let mut file_name = None; + let mut standard_info = None; - for attr in entry_attributes.iter().cloned() { - if let MftAttributeContent::AttrX30(data) = attr.data { - file_name_attributes.push(data) + for attr in entry_attributes.iter() { + if let MftAttributeContent::AttrX30(data) = &attr.data { + file_name = Some(data.clone()); + break; + } + } + for attr in entry_attributes.iter() { + if let MftAttributeContent::AttrX10(data) = &attr.data { + standard_info = Some(data.clone()); + break; } } FlatMftEntryWithName { + record_number: entry.header.record_number, signature: String::from_utf8(entry.header.signature.to_ascii_uppercase()).unwrap(), logfile_sequence_number: entry.header.logfile_sequence_number, sequence: entry.header.sequence, @@ -75,9 +107,17 @@ impl FlatMftEntryWithName { parent_reference_entry: entry.header.base_reference.entry, parent_reference_sequence: entry.header.base_reference.sequence, entry_reference_entry: entry.header.entry_reference.entry, + in_use: false, + is_a_directory: entry.is_dir(), entry_reference_sequence: entry.header.entry_reference.sequence, - // file_name_records: file_name_attributes, - record_number: entry.header.record_number, + standard_info_flags: standard_info.as_ref().and_then(|i| Some(i.file_flags)), + standard_info_last_modified: standard_info.as_ref().and_then(|i| Some(i.modified)), + standard_info_last_access: standard_info.as_ref().and_then(|i| Some(i.accessed)), + standard_info_created: standard_info.as_ref().and_then(|i| Some(i.created)), + file_name_flags: file_name.as_ref().and_then(|i| Some(i.flags)), + file_name_last_modified: file_name.as_ref().and_then(|i| Some(i.modified)), + file_name_last_access: file_name.as_ref().and_then(|i| Some(i.accessed)), + file_name_created: file_name.as_ref().and_then(|i| Some(i.created)), full_path: parser .get_full_path_for_entry(entry) .expect("I/O Err") @@ -5,8 +5,8 @@ pub use attribute::x10::StandardInfoAttr; pub use attribute::x30::FileNameAttr; pub use attribute::MftAttribute; +pub use crate::mft::MftParser; pub use entry::{EntryHeader, MftEntry}; -pub use mft::MftParser; use std::io::{self, Read, Seek, SeekFrom}; |