use crate::attribute::{AttributeDataFlags, MftAttributeType};
use crate::err::{Error, Result};
use crate::utils::read_utf16_string;
use byteorder::{LittleEndian, ReadBytesExt};
use num_traits::FromPrimitive;
use serde::Serialize;
use std::io::{Read, Seek, SeekFrom};
#[derive(Serialize, Clone, Debug)]
pub struct MftAttributeHeader {
pub type_code: MftAttributeType,
pub record_length: u32,
pub form_code: u8,
pub residential_header: ResidentialHeader,
pub name_size: u8,
pub name_offset: Option<u16>,
pub data_flags: AttributeDataFlags,
pub instance: u16,
pub name: String,
pub start_offset: u64,
}
#[derive(Serialize, Clone, Debug)]
#[serde(untagged)]
pub enum ResidentialHeader {
Resident(ResidentHeader),
NonResident(NonResidentHeader),
}
impl MftAttributeHeader {
pub fn from_stream<S: Read + Seek>(stream: &mut S) -> Result<Option<MftAttributeHeader>> {
let attribute_header_start_offset = stream.stream_position()?;
let type_code_value = stream.read_u32::<LittleEndian>()?;
if type_code_value == 0xFFFF_FFFF {
return Ok(None);
}
let type_code = match MftAttributeType::from_u32(type_code_value) {
Some(attribute_type) => attribute_type,
None => {
return Err(Error::UnknownAttributeType {
attribute_type: type_code_value,
});
}
};
let attribute_size = stream.read_u32::<LittleEndian>()?;
let resident_flag = stream.read_u8()?;
let name_size = stream.read_u8()?;
let name_offset = {
let value = stream.read_u16::<LittleEndian>()?;
if name_size > 0 { Some(value) } else { None }
};
let data_flags = AttributeDataFlags::from_bits_truncate(stream.read_u16::<LittleEndian>()?);
let id = stream.read_u16::<LittleEndian>()?;
let residential_header = match resident_flag {
0 => ResidentialHeader::Resident(ResidentHeader::from_stream(stream)?),
1 => ResidentialHeader::NonResident(NonResidentHeader::from_stream(stream)?),
_ => {
return Err(Error::UnhandledResidentFlag {
flag: resident_flag,
offset: stream.stream_position()?,
});
}
};
let name = if name_size > 0 {
stream.seek(SeekFrom::Start(
attribute_header_start_offset
+ u64::from(name_offset.expect("name_size > 0 is invariant")),
))?;
read_utf16_string(stream, Some(name_size as usize))?
} else {
String::new()
};
Ok(Some(MftAttributeHeader {
type_code,
record_length: attribute_size,
form_code: resident_flag,
name_size,
name_offset,
data_flags,
instance: id,
name,
residential_header,
start_offset: attribute_header_start_offset,
}))
}
}
#[derive(Serialize, Clone, Debug)]
pub struct ResidentHeader {
#[serde(skip_serializing)]
pub data_size: u32,
#[serde(skip_serializing)]
pub data_offset: u16,
pub index_flag: u8,
pub padding: u8,
}
impl ResidentHeader {
pub fn from_stream<R: Read>(reader: &mut R) -> Result<ResidentHeader> {
Ok(ResidentHeader {
data_size: reader.read_u32::<LittleEndian>()?,
data_offset: reader.read_u16::<LittleEndian>()?,
index_flag: reader.read_u8()?,
padding: reader.read_u8()?,
})
}
}
#[derive(Serialize, Clone, Debug)]
pub struct NonResidentHeader {
pub vnc_first: u64,
pub vnc_last: u64,
#[serde(skip_serializing)]
pub datarun_offset: u16,
pub unit_compression_size: u16,
#[serde(skip_serializing)]
pub padding: u32,
pub allocated_length: u64,
pub file_size: u64,
pub valid_data_length: u64,
pub total_allocated: Option<u64>,
}
impl NonResidentHeader {
pub fn from_stream<R: Read>(reader: &mut R) -> Result<NonResidentHeader> {
let vnc_first = reader.read_u64::<LittleEndian>()?;
let vnc_last = reader.read_u64::<LittleEndian>()?;
let datarun_offset = reader.read_u16::<LittleEndian>()?;
let unit_compression_size = reader.read_u16::<LittleEndian>()?;
let padding = reader.read_u32::<LittleEndian>()?;
let allocated_length = reader.read_u64::<LittleEndian>()?;
let file_size = reader.read_u64::<LittleEndian>()?;
let valid_data_length = reader.read_u64::<LittleEndian>()?;
let total_allocated = if unit_compression_size > 0 {
Some(reader.read_u64::<LittleEndian>()?)
} else {
None
};
Ok(NonResidentHeader {
vnc_first,
vnc_last,
datarun_offset,
unit_compression_size,
padding,
allocated_length,
file_size,
valid_data_length,
total_allocated,
})
}
}
#[cfg(test)]
mod tests {
use super::MftAttributeHeader;
use crate::attribute::MftAttributeType;
use std::io::Cursor;
#[test]
fn attribute_test_01_resident() {
let raw: &[u8] = &[
0x10, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
];
let mut cursor = Cursor::new(raw);
let attribute_header = MftAttributeHeader::from_stream(&mut cursor)
.expect("Should not be $End")
.expect("Shold parse correctly");
assert_eq!(
attribute_header.type_code,
MftAttributeType::StandardInformation
);
assert_eq!(attribute_header.record_length, 96);
assert_eq!(attribute_header.form_code, 0);
assert_eq!(attribute_header.name_size, 0);
assert_eq!(attribute_header.name_offset, None);
}
#[test]
fn attribute_test_01_nonresident() {
let raw: &[u8] = &[
0x80, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x01, 0x00, 0x40, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x1E, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xEC, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x11, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xEC, 0x11, 0x00, 0x00, 0x00, 0x00, 0x33, 0x20, 0xC8, 0x00, 0x00, 0x00,
0x0C, 0x32, 0xA0, 0x56, 0xE3, 0xE6, 0x24, 0x00, 0xFF, 0xFF,
];
let mut cursor = Cursor::new(raw);
let attribute_header = MftAttributeHeader::from_stream(&mut cursor)
.expect("Should not be $End")
.expect("Shold parse correctly");
assert_eq!(attribute_header.type_code, MftAttributeType::DATA);
assert_eq!(attribute_header.record_length, 80);
assert_eq!(attribute_header.form_code, 1);
assert_eq!(attribute_header.name_size, 0);
assert_eq!(attribute_header.name_offset, None);
}
}