pub mod data_run;
pub mod header;
pub mod non_resident_attr;
pub mod raw;
pub mod x10;
pub mod x20;
pub mod x30;
pub mod x40;
pub mod x60;
pub mod x70;
pub mod x80;
pub mod x90;
pub mod xc0;
use crate::err::Result;
use crate::impl_serialize_for_bitflags;
use std::io::Cursor;
use bitflags::bitflags;
use crate::attribute::raw::RawAttribute;
use crate::attribute::x10::StandardInfoAttr;
use crate::attribute::x20::AttributeListAttr;
use crate::attribute::x30::FileNameAttr;
use crate::attribute::data_run::decode_data_runs;
use crate::attribute::header::{MftAttributeHeader, ResidentialHeader};
use crate::attribute::non_resident_attr::NonResidentAttr;
use crate::attribute::x40::ObjectIdAttr;
use crate::attribute::x60::VolumeNameAttr;
use crate::attribute::x70::VolumeInformationAttr;
use crate::attribute::x80::DataAttr;
use crate::attribute::x90::IndexRootAttr;
use crate::attribute::xc0::ReparsePointAttr;
use serde::Serialize;
#[derive(Serialize, Clone, Debug)]
pub struct MftAttribute<'a> {
pub header: MftAttributeHeader<'a>,
pub data: MftAttributeContent<'a>,
}
impl<'a> MftAttributeContent<'a> {
pub fn from_record(record: &'a [u8], header: &MftAttributeHeader<'a>) -> Result<Self> {
match &header.residential_header {
ResidentialHeader::Resident(resident) => {
let value = resident.value(record)?;
let raw = || {
MftAttributeContent::Raw(RawAttribute::from_slice(
header.type_code.clone(),
value,
))
};
match header.type_code {
MftAttributeType::StandardInformation => Ok(MftAttributeContent::AttrX10(
StandardInfoAttr::from_slice(value)?,
)),
MftAttributeType::AttributeList => Ok(MftAttributeContent::AttrX20(
AttributeListAttr::from_slice(value)?,
)),
MftAttributeType::FileName => Ok(MftAttributeContent::AttrX30(
FileNameAttr::from_slice(value)?,
)),
MftAttributeType::DATA => {
Ok(MftAttributeContent::AttrX80(DataAttr::from_slice(value)))
}
MftAttributeType::ObjectId => Ok(MftAttributeContent::AttrX40(
ObjectIdAttr::from_stream(&mut Cursor::new(value), value.len())?,
)),
MftAttributeType::VolumeName => Ok(MftAttributeContent::AttrX60(
VolumeNameAttr::from_slice(value),
)),
MftAttributeType::VolumeInformation => {
Ok(VolumeInformationAttr::from_slice(value)
.map_or_else(|_| raw(), MftAttributeContent::AttrX70))
}
MftAttributeType::ReparsePoint => Ok(ReparsePointAttr::from_slice(value)
.map_or_else(raw, MftAttributeContent::AttrXC0)),
MftAttributeType::IndexRoot => Ok(MftAttributeContent::AttrX90(
IndexRootAttr::from_slice(value)?,
)),
_ => Ok(raw()),
}
}
ResidentialHeader::NonResident(nonresident) => {
let datarun_offset = nonresident.datarun_offset as usize;
let runs = record
.get(datarun_offset..)
.ok_or_else(|| std::io::Error::from(std::io::ErrorKind::UnexpectedEof))?;
let data_runs = if runs.is_empty() {
Vec::new()
} else {
decode_data_runs(runs).ok_or_else(|| {
crate::err::Error::FailedToDecodeDataRuns {
bad_data_runs: runs.to_vec(),
}
})?
};
Ok(MftAttributeContent::DataRun(NonResidentAttr { data_runs }))
}
}
}
pub fn as_attribute_list(&self) -> Option<&AttributeListAttr<'a>> {
match self {
MftAttributeContent::AttrX20(content) => Some(content),
_ => None,
}
}
pub fn as_index_root(&self) -> Option<&IndexRootAttr<'a>> {
match self {
MftAttributeContent::AttrX90(content) => Some(content),
_ => None,
}
}
pub fn as_object_id(&self) -> Option<&ObjectIdAttr> {
match self {
MftAttributeContent::AttrX40(content) => Some(content),
_ => None,
}
}
pub fn as_volume_name(&self) -> Option<&VolumeNameAttr<'a>> {
match self {
MftAttributeContent::AttrX60(content) => Some(content),
_ => None,
}
}
pub fn as_volume_information(&self) -> Option<&VolumeInformationAttr> {
match self {
MftAttributeContent::AttrX70(content) => Some(content),
_ => None,
}
}
pub fn as_reparse_point(&self) -> Option<&ReparsePointAttr<'a>> {
match self {
MftAttributeContent::AttrXC0(content) => Some(content),
_ => None,
}
}
pub fn as_standard_info(&self) -> Option<&StandardInfoAttr> {
match self {
MftAttributeContent::AttrX10(content) => Some(content),
_ => None,
}
}
pub fn as_data(&self) -> Option<&DataAttr<'a>> {
match self {
MftAttributeContent::AttrX80(content) => Some(content),
_ => None,
}
}
pub fn as_file_name(&self) -> Option<&FileNameAttr<'a>> {
match self {
MftAttributeContent::AttrX30(content) => Some(content),
_ => None,
}
}
pub fn as_data_runs(&self) -> Option<&NonResidentAttr> {
match self {
MftAttributeContent::DataRun(content) => Some(content),
_ => None,
}
}
}
#[derive(Serialize, Clone, Debug)]
#[serde(untagged)]
pub enum MftAttributeContent<'a> {
Raw(RawAttribute<'a>),
AttrX10(StandardInfoAttr),
AttrX20(AttributeListAttr<'a>),
AttrX30(FileNameAttr<'a>),
AttrX40(ObjectIdAttr),
AttrX60(VolumeNameAttr<'a>),
AttrX70(VolumeInformationAttr),
AttrX80(DataAttr<'a>),
AttrX90(IndexRootAttr<'a>),
AttrXC0(ReparsePointAttr<'a>),
DataRun(NonResidentAttr),
}
#[derive(Serialize, Debug, Clone, FromPrimitive, ToPrimitive, PartialOrd, PartialEq)]
#[repr(u32)]
pub enum MftAttributeType {
StandardInformation = 0x10_u32,
AttributeList = 0x20_u32,
FileName = 0x30_u32,
ObjectId = 0x40_u32,
SecurityDescriptor = 0x50_u32,
VolumeName = 0x60_u32,
VolumeInformation = 0x70_u32,
DATA = 0x80_u32,
IndexRoot = 0x90_u32,
IndexAllocation = 0xA0_u32,
BITMAP = 0xB0_u32,
ReparsePoint = 0xC0_u32,
EaInformation = 0xD0_u32,
EA = 0xE0_u32,
PropertySet = 0xF0_u32,
LoggedUtilityStream = 0x100_u32,
}
bitflags! {
#[derive(Clone, Debug, PartialEq)]
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_DIRECTORY = 0x0000_0010;
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;
const FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x0000_8000;
const FILE_ATTRIBUTE_VIRTUAL = 0x0001_0000;
const FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x0002_0000;
const FILE_ATTRIBUTE_EA = 0x0004_0000;
const FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x0004_0000;
const FILE_ATTRIBUTE_HAS_EA = 0x0004_0000;
const FILE_ATTRIBUTE_PINNED = 0x0008_0000;
const FILE_ATTRIBUTE_UNPINNED = 0x0010_0000;
const FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x0040_0000;
const FILE_ATTRIBUTE_IS_DIRECTORY = 0x1000_0000;
const FILE_ATTRIBUTE_INDEX_VIEW = 0x2000_0000;
}
}
impl_serialize_for_bitflags! {FileAttributeFlags}
bitflags! {
#[derive(Default, Clone, Debug, PartialEq)]
pub struct AttributeDataFlags: u16 {
const IS_COMPRESSED = 0x0001;
const COMPRESSION_MASK = 0x00FF;
const ENCRYPTED = 0x4000;
const SPARSE = 0x8000;
}
}
impl_serialize_for_bitflags! {AttributeDataFlags}
#[cfg(test)]
mod tests {
use super::*;
use crate::attribute::header::MftAttributeHeader;
use crate::entry::tests::resident_attribute_record;
#[test]
fn nonresident_attribute_allows_empty_mapping_pairs_section() {
let record_length: u32 = 64;
let mut record = vec![0u8; record_length as usize];
record[0..4].copy_from_slice(&(MftAttributeType::DATA as u32).to_le_bytes());
record[4..8].copy_from_slice(&record_length.to_le_bytes());
record[8] = 1; record[9] = 0; record[10..12].copy_from_slice(&0u16.to_le_bytes()); record[12..14].copy_from_slice(&0u16.to_le_bytes()); record[14..16].copy_from_slice(&0u16.to_le_bytes());
record[32..34].copy_from_slice(&(record_length as u16).to_le_bytes()); record[34..36].copy_from_slice(&0u16.to_le_bytes());
let header = MftAttributeHeader::from_slice(&record, 0)
.expect("expected header parse to succeed")
.expect("expected attribute type != $END");
let content = MftAttributeContent::from_record(&record, &header)
.expect("expected non-resident attribute to parse successfully");
match content {
MftAttributeContent::DataRun(nonresident) => {
assert!(nonresident.data_runs.is_empty());
}
other => panic!("expected DataRun content, got {other:?}"),
}
}
#[test]
fn truncated_values_stay_raw() {
for type_code in [
MftAttributeType::ReparsePoint,
MftAttributeType::VolumeInformation,
] {
let value = [0x03, 0x00, 0x00, 0xA0];
let record = resident_attribute_record(type_code as u32, &value);
let header = MftAttributeHeader::from_slice(&record, 0).unwrap().unwrap();
match MftAttributeContent::from_record(&record, &header).unwrap() {
MftAttributeContent::Raw(raw) => assert_eq!(raw.data, value),
other => panic!("expected Raw content, got {other:?}"),
}
}
}
}