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-18 14:15:27 +0300
committerOmer Ben-Amram <omerbenamram@gmail.com>2019-05-18 14:15:27 +0300
commitc7642a52e77ac7420e2a20f92be538a1ae891e71 (patch)
treed18ad3a6aff64b4a778c22471de669359f70b7e0
parent30c6d49a0ec4518d565c818dd69cff4f7b69d29d (diff)
more progress
-rw-r--r--src/attribute/mod.rs20
-rw-r--r--src/attribute/x10.rs21
-rw-r--r--src/attribute/x30.rs38
-rw-r--r--src/bin/mft_dump.rs58
-rw-r--r--src/lib.rs2
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")
diff --git a/src/lib.rs b/src/lib.rs
index 779d8c7..7dff17e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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};