Skip to content

Commit

Permalink
add CSV features
Browse files Browse the repository at this point in the history
  • Loading branch information
omerbenamram committed Jun 6, 2019
1 parent b44ee90 commit eabf393
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 20 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
- `mft_dump` can now dump only a specific range of entries with `-r`.
- CSV output now shows `FileSize`, `IsDeleted` as separate columns.

### Fixed
- Fixed an issue with debug-logs
Expand Down
53 changes: 34 additions & 19 deletions src/csv.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::attribute::x30::FileNamespace;
use crate::attribute::{FileAttributeFlags, MftAttributeContent, MftAttributeType};
use crate::attribute::header::ResidentialHeader;

use crate::attribute::{FileAttributeFlags, MftAttributeType};
use crate::entry::EntryFlags;
use crate::{MftAttribute, MftEntry, MftParser, ReadSeek};

Expand Down Expand Up @@ -27,8 +28,14 @@ pub struct FlatMftEntryWithName {
pub used_entry_size: u32,
pub total_entry_size: u32,

/// The size of the file, if available, from the X80 attribute.
/// Will be 0 if no $DATA attribute is found.
pub file_size: u64,

/// Indicates whether the record is a directory.
pub is_a_directory: bool,
/// Indicates whether the record has the `ALLOCATED` bit turned off.
pub is_deleted: bool,

/// Indicates whether the record has alternate data streams.
pub has_alternate_data_streams: bool,
Expand Down Expand Up @@ -61,27 +68,33 @@ impl FlatMftEntryWithName {
.filter_map(Result::ok)
.collect();

let mut file_name = None;
let mut standard_info = None;
let file_name = entry_attributes
.iter()
.find(|a| a.header.type_code == MftAttributeType::FileName)
.and_then(|a| a.data.clone().into_file_name());

for attr in entry_attributes.iter() {
if let MftAttributeContent::AttrX30(data) = &attr.data {
if [FileNamespace::Win32, FileNamespace::Win32AndDos].contains(&data.namespace) {
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;
}
}
let standard_info = entry_attributes
.iter()
.find(|a| a.header.type_code == MftAttributeType::StandardInformation)
.and_then(|a| a.data.clone().into_standard_info());

let data_attr = entry_attributes
.iter()
.find(|a| a.header.type_code == MftAttributeType::DATA);

let file_size = match data_attr {
Some(attr) => match &attr.header.residential_header {
ResidentialHeader::Resident(r) => u64::from(r.data_size),
ResidentialHeader::NonResident(nr) => nr.file_size,
},
_ => 0,
};

let has_ads = entry_attributes
.iter()
.any(|a| a.header.type_code == MftAttributeType::DATA && a.header.name_size > 0);
.filter(|a| a.header.type_code == MftAttributeType::DATA)
.count()
> 1;

FlatMftEntryWithName {
entry_id: entry.header.record_number,
Expand All @@ -95,6 +108,7 @@ impl FlatMftEntryWithName {
base_entry_id: entry.header.base_reference.entry,
base_entry_sequence: entry.header.base_reference.sequence,
is_a_directory: entry.is_dir(),
is_deleted: !entry.header.flags.contains(EntryFlags::ALLOCATED),
has_alternate_data_streams: has_ads,
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)),
Expand All @@ -104,6 +118,7 @@ impl FlatMftEntryWithName {
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)),
file_size,
full_path: parser
.get_full_path_for_entry(entry)
.expect("I/O Err")
Expand Down
2 changes: 1 addition & 1 deletion src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ impl MftEntry {
/// Will prefer `Win32` file name attributes, and fallback to `Dos` paths.
pub fn find_best_name_attribute(&self) -> Option<FileNameAttr> {
let file_name_attributes: Vec<FileNameAttr> = self
.iter_attributes()
.iter_attributes_matching(Some(vec![MftAttributeType::FileName]))
.filter_map(Result::ok)
.filter_map(|a| a.data.into_file_name())
.collect();
Expand Down

0 comments on commit eabf393

Please sign in to comment.