use crate::attribute::{FileAttributeFlags, MftAttributeType};
use crate::entry::EntryFlags;
use crate::{MftAttribute, MftEntry, MftParser};
use serde::Serialize;
use jiff::Timestamp;
use std::io::{Read, Seek};
use std::path::PathBuf;
#[derive(Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct FlatMftEntryWithName {
pub signature: String,
pub entry_id: u64,
pub sequence: u16,
pub base_entry_id: u64,
pub base_entry_sequence: u16,
pub hard_link_count: u16,
pub flags: EntryFlags,
pub used_entry_size: u32,
pub total_entry_size: u32,
pub file_size: u64,
pub is_a_directory: bool,
pub is_deleted: bool,
pub has_alternate_data_streams: bool,
pub standard_info_flags: Option<FileAttributeFlags>,
#[serde(serialize_with = "crate::utils::serialize_option_timestamp")]
pub standard_info_last_modified: Option<Timestamp>,
#[serde(serialize_with = "crate::utils::serialize_option_timestamp")]
pub standard_info_last_access: Option<Timestamp>,
#[serde(serialize_with = "crate::utils::serialize_option_timestamp")]
pub standard_info_created: Option<Timestamp>,
pub file_name_flags: Option<FileAttributeFlags>,
#[serde(serialize_with = "crate::utils::serialize_option_timestamp")]
pub file_name_last_modified: Option<Timestamp>,
#[serde(serialize_with = "crate::utils::serialize_option_timestamp")]
pub file_name_last_access: Option<Timestamp>,
#[serde(serialize_with = "crate::utils::serialize_option_timestamp")]
pub file_name_created: Option<Timestamp>,
#[serde(serialize_with = "crate::utils::serialize_windows_path")]
pub full_path: PathBuf,
#[serde(serialize_with = "crate::utils::serialize_option_timestamp")]
pub standard_info_last_mft_modified: Option<Timestamp>,
#[serde(serialize_with = "crate::utils::serialize_option_timestamp")]
pub file_name_last_mft_modified: Option<Timestamp>,
}
impl FlatMftEntryWithName {
pub fn from_entry(
entry: &MftEntry,
parser: &mut MftParser<impl Read + Seek>,
) -> FlatMftEntryWithName {
let full_path = parser
.get_full_path_for_entry(entry)
.expect("I/O Err")
.unwrap_or_default();
let extensions = parser.get_extension_entries(entry);
Self::from_entry_with_path(entry, &extensions, full_path)
}
pub fn from_entry_with_path(
entry: &MftEntry,
extensions: &[MftEntry],
full_path: PathBuf,
) -> FlatMftEntryWithName {
let entry_attributes: Vec<MftAttribute> = std::iter::once(entry)
.chain(extensions)
.flat_map(|record| {
record.iter_attributes_matching(Some(vec![
MftAttributeType::FileName,
MftAttributeType::StandardInformation,
MftAttributeType::DATA,
]))
})
.filter_map(Result::ok)
.collect();
let file_name = entry_attributes
.iter()
.find(|a| a.header.type_code == MftAttributeType::FileName)
.and_then(|a| a.data.as_file_name())
.cloned();
let standard_info = entry_attributes
.iter()
.find(|a| a.header.type_code == MftAttributeType::StandardInformation)
.and_then(|a| a.data.as_standard_info())
.cloned();
let file_size = entry_attributes
.iter()
.filter(|a| a.header.type_code == MftAttributeType::DATA && !a.header.is_continuation())
.min_by_key(|a| !a.header.name.is_empty())
.map_or(0, |a| a.header.value_size());
let has_ads = entry_attributes
.iter()
.any(|a| a.header.type_code == MftAttributeType::DATA && !a.header.name.is_empty());
FlatMftEntryWithName {
entry_id: entry.header.record_number,
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.clone(),
used_entry_size: entry.header.used_entry_size,
total_entry_size: entry.header.total_entry_size,
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().map(|i| i.file_flags.clone()),
standard_info_last_modified: standard_info.as_ref().map(|i| i.modified),
standard_info_last_access: standard_info.as_ref().map(|i| i.accessed),
standard_info_created: standard_info.as_ref().map(|i| i.created),
file_name_flags: file_name.as_ref().map(|i| i.flags.clone()),
file_name_last_modified: file_name.as_ref().map(|i| i.modified),
file_name_last_access: file_name.as_ref().map(|i| i.accessed),
file_name_created: file_name.as_ref().map(|i| i.created),
standard_info_last_mft_modified: standard_info.as_ref().map(|i| i.mft_modified),
file_name_last_mft_modified: file_name.as_ref().map(|i| i.mft_modified),
file_size,
full_path,
}
}
}