mft/attribute/
header.rs

1use crate::attribute::{AttributeDataFlags, MftAttributeType};
2use crate::err::{Error, Result};
3use crate::utils::read_utf16_string;
4
5use byteorder::{LittleEndian, ReadBytesExt};
6use num_traits::FromPrimitive;
7use serde::Serialize;
8use std::io::{Read, Seek, SeekFrom};
9
10/// Represents the union defined in
11/// <https://docs.microsoft.com/en-us/windows/desktop/devnotes/attribute-record-header>
12#[derive(Serialize, Clone, Debug)]
13pub struct MftAttributeHeader {
14    pub type_code: MftAttributeType,
15    /// The size of the attribute record, in bytes.
16    /// This value reflects the required size for the record variant and is always rounded to the nearest quadword boundary.
17    pub record_length: u32,
18    /// If the FormCode member is RESIDENT_FORM (0x00), the union is a Resident structure.
19    /// If FormCode is NONRESIDENT_FORM (0x01), the union is a Nonresident structure.
20    pub form_code: u8,
21    pub residential_header: ResidentialHeader,
22    /// The size of the optional attribute name, in characters, or 0 if there is no attribute name.
23    /// The maximum attribute name length is 255 characters.
24    pub name_size: u8,
25    /// The offset of the attribute name from the start of the attribute record, in bytes.
26    /// If the NameLength member is 0, this member is undefined.
27    pub name_offset: Option<u16>,
28    pub data_flags: AttributeDataFlags,
29    /// The unique instance for this attribute in the file record.
30    pub instance: u16,
31    pub name: String,
32}
33
34#[derive(Serialize, Clone, Debug)]
35#[serde(untagged)]
36pub enum ResidentialHeader {
37    Resident(ResidentHeader),
38    NonResident(NonResidentHeader),
39}
40
41impl MftAttributeHeader {
42    /// Tries to read an AttributeHeader from the stream.
43    /// Will return `None` if the type code is $END.
44    pub fn from_stream<S: Read + Seek>(stream: &mut S) -> Result<Option<MftAttributeHeader>> {
45        let attribute_header_start_offset = stream.stream_position()?;
46
47        let type_code_value = stream.read_u32::<LittleEndian>()?;
48
49        if type_code_value == 0xFFFF_FFFF {
50            return Ok(None);
51        }
52
53        let type_code = match MftAttributeType::from_u32(type_code_value) {
54            Some(attribute_type) => attribute_type,
55            None => {
56                return Err(Error::UnknownAttributeType {
57                    attribute_type: type_code_value,
58                })
59            }
60        };
61
62        let attribute_size = stream.read_u32::<LittleEndian>()?;
63        let resident_flag = stream.read_u8()?;
64        let name_size = stream.read_u8()?;
65        let name_offset = {
66            // We always read the two bytes to advance the stream.
67            let value = stream.read_u16::<LittleEndian>()?;
68            if name_size > 0 {
69                Some(value)
70            } else {
71                None
72            }
73        };
74
75        let data_flags = AttributeDataFlags::from_bits_truncate(stream.read_u16::<LittleEndian>()?);
76        let id = stream.read_u16::<LittleEndian>()?;
77
78        let residential_header = match resident_flag {
79            0 => ResidentialHeader::Resident(ResidentHeader::from_stream(stream)?),
80            1 => ResidentialHeader::NonResident(NonResidentHeader::from_stream(stream)?),
81            _ => {
82                return Err(Error::UnhandledResidentFlag {
83                    flag: resident_flag,
84                    offset: stream.stream_position()?,
85                })
86            }
87        };
88
89        // Name is optional, and will not be present if size == 0.
90        let name = if name_size > 0 {
91            stream.seek(SeekFrom::Start(
92                attribute_header_start_offset
93                    + u64::from(name_offset.expect("name_size > 0 is invariant")),
94            ))?;
95            read_utf16_string(stream, Some(name_size as usize))?
96        } else {
97            String::new()
98        };
99
100        Ok(Some(MftAttributeHeader {
101            type_code,
102            record_length: attribute_size,
103            form_code: resident_flag,
104            name_size,
105            name_offset,
106            data_flags,
107            instance: id,
108            name,
109            residential_header,
110        }))
111    }
112}
113
114#[derive(Serialize, Clone, Debug)]
115pub struct ResidentHeader {
116    #[serde(skip_serializing)]
117    /// The size of the attribute value, in bytes.
118    pub data_size: u32,
119    #[serde(skip_serializing)]
120    /// The offset to the value from the start of the attribute record, in bytes.
121    pub data_offset: u16,
122    pub index_flag: u8,
123    #[serde(skip_serializing)]
124    pub padding: u8,
125}
126
127impl ResidentHeader {
128    pub fn from_stream<R: Read>(reader: &mut R) -> Result<ResidentHeader> {
129        Ok(ResidentHeader {
130            data_size: reader.read_u32::<LittleEndian>()?,
131            data_offset: reader.read_u16::<LittleEndian>()?,
132            index_flag: reader.read_u8()?,
133            padding: reader.read_u8()?,
134        })
135    }
136}
137
138#[derive(Serialize, Clone, Debug)]
139pub struct NonResidentHeader {
140    /// The lowest virtual cluster number (VCN) covered by this attribute record.
141    pub vnc_first: u64,
142    /// The highest VCN covered by this attribute record.
143    pub vnc_last: u64,
144    #[serde(skip_serializing)]
145    /// The offset to the mapping pairs array from the start of the attribute record, in bytes. For more information, see Remarks.
146    pub datarun_offset: u16,
147    /// Reserved UCHAR\[6]
148    pub unit_compression_size: u16,
149    #[serde(skip_serializing)]
150    pub padding: u32,
151
152    /// The allocated size of the file, in bytes.
153    /// This value is an even multiple of the cluster size.
154    /// This member is not valid if the LowestVcn member is nonzero.
155    pub allocated_length: u64,
156    pub file_size: u64,
157    ///  Contains the valid data size in number of bytes.
158    /// This value is not valid if the first VCN is nonzero.
159    pub valid_data_length: u64,
160    pub total_allocated: Option<u64>,
161}
162
163impl NonResidentHeader {
164    pub fn from_stream<R: Read>(reader: &mut R) -> Result<NonResidentHeader> {
165        let vnc_first = reader.read_u64::<LittleEndian>()?;
166        let vnc_last = reader.read_u64::<LittleEndian>()?;
167        let datarun_offset = reader.read_u16::<LittleEndian>()?;
168        let unit_compression_size = reader.read_u16::<LittleEndian>()?;
169        let padding = reader.read_u32::<LittleEndian>()?;
170        let allocated_length = reader.read_u64::<LittleEndian>()?;
171        let file_size = reader.read_u64::<LittleEndian>()?;
172        let valid_data_length = reader.read_u64::<LittleEndian>()?;
173
174        let total_allocated = if unit_compression_size > 0 {
175            Some(reader.read_u64::<LittleEndian>()?)
176        } else {
177            None
178        };
179
180        Ok(NonResidentHeader {
181            vnc_first,
182            vnc_last,
183            datarun_offset,
184            unit_compression_size,
185            padding,
186            allocated_length,
187            file_size,
188            valid_data_length,
189            total_allocated,
190        })
191    }
192}
193
194#[cfg(test)]
195mod tests {
196    use super::MftAttributeHeader;
197    use crate::attribute::MftAttributeType;
198    use std::io::Cursor;
199
200    #[test]
201    fn attribute_test_01_resident() {
202        let raw: &[u8] = &[
203            0x10, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
204            0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
205        ];
206
207        let mut cursor = Cursor::new(raw);
208
209        let attribute_header = MftAttributeHeader::from_stream(&mut cursor)
210            .expect("Should not be $End")
211            .expect("Shold parse correctly");
212
213        assert_eq!(
214            attribute_header.type_code,
215            MftAttributeType::StandardInformation
216        );
217        assert_eq!(attribute_header.record_length, 96);
218        assert_eq!(attribute_header.form_code, 0);
219        assert_eq!(attribute_header.name_size, 0);
220        assert_eq!(attribute_header.name_offset, None);
221    }
222
223    #[test]
224    fn attribute_test_01_nonresident() {
225        let raw: &[u8] = &[
226            0x80, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x01, 0x00, 0x40, 0x00, 0x00, 0x00,
227            0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x1E, 0x01, 0x00,
228            0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
229            0xEC, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x11, 0x00, 0x00, 0x00, 0x00,
230            0x00, 0x00, 0xEC, 0x11, 0x00, 0x00, 0x00, 0x00, 0x33, 0x20, 0xC8, 0x00, 0x00, 0x00,
231            0x0C, 0x32, 0xA0, 0x56, 0xE3, 0xE6, 0x24, 0x00, 0xFF, 0xFF,
232        ];
233
234        let mut cursor = Cursor::new(raw);
235
236        let attribute_header = MftAttributeHeader::from_stream(&mut cursor)
237            .expect("Should not be $End")
238            .expect("Shold parse correctly");
239
240        assert_eq!(attribute_header.type_code, MftAttributeType::DATA);
241        assert_eq!(attribute_header.record_length, 80);
242        assert_eq!(attribute_header.form_code, 1);
243        assert_eq!(attribute_header.name_size, 0);
244        assert_eq!(attribute_header.name_offset, None);
245    }
246}