1use std::io::{Read, Seek, SeekFrom};
6use std::mem::size_of;
7
8use crate::{
9 aligned_reader::open_volume,
10 api::*,
11 attribute::{DataRun, NtfsAttribute},
12 errors::{NtfsReaderError, NtfsReaderResult},
13 file::NtfsFile,
14 volume::Volume,
15};
16
17pub struct Mft {
18 pub volume: Volume,
19 pub data: Vec<u8>,
20 pub bitmap: Vec<u8>,
21 pub max_record: u64,
22}
23
24impl Mft {
25 pub fn new(volume: Volume) -> NtfsReaderResult<Self> {
26 let mut reader = open_volume(&volume.path)?;
27
28 let mft_record = Self::get_record_fs(
29 &mut reader,
30 volume.file_record_size as usize,
31 volume.mft_position,
32 )?;
33
34 let mut data =
35 Self::read_data_fs(&volume, &mut reader, &mft_record, NtfsAttributeType::Data)?
36 .ok_or_else(|| NtfsReaderError::MissingMftAttribute("Data".to_string()))?;
37 let bitmap =
38 Self::read_data_fs(&volume, &mut reader, &mft_record, NtfsAttributeType::Bitmap)?
39 .ok_or_else(|| NtfsReaderError::MissingMftAttribute("Bitmap".to_string()))?;
40
41 let max_record = (data.len() / volume.file_record_size as usize) as u64;
42
43 for number in 0..max_record {
45 let start = number as usize * volume.file_record_size as usize;
46 let end = start + volume.file_record_size as usize;
47 let data = &mut data[start..end];
48 Self::fixup_record(number, data)?;
49 }
50
51 Ok(Mft {
52 volume,
53 data,
54 bitmap,
55 max_record,
56 })
57 }
58
59 pub fn record_exists(&self, number: u64) -> bool {
60 if number >= self.max_record {
61 return false;
62 }
63
64 let bitmap_idx = (number / 8) as usize;
65 let bitmap_off = (number % 8) as u8;
66
67 if bitmap_idx >= self.bitmap.len() {
68 return false;
69 }
70
71 let bit = self.bitmap[bitmap_idx];
72 (bit & (1u8 << bitmap_off)) != 0
73 }
74
75 pub fn iterate_files<F>(&self, mut f: F)
76 where
77 F: FnMut(&NtfsFile),
78 {
79 for number in FIRST_NORMAL_RECORD..self.max_record {
80 if self.record_exists(number) {
81 if let Some(file) = self.get_record(number) {
82 if file.is_used() {
83 f(&file);
84 }
85 }
86 }
87 }
88 }
89
90 pub fn get_record_data(&self, number: u64) -> &[u8] {
91 let start = number as usize * self.volume.file_record_size as usize;
92 let end = start + self.volume.file_record_size as usize;
93 &self.data[start..end]
94 }
95
96 pub fn get_record(&self, number: u64) -> Option<NtfsFile<'_>> {
97 if number >= self.max_record {
98 return None;
99 }
100 let data = self.get_record_data(number);
101
102 if NtfsFile::is_valid(data) {
103 return Some(NtfsFile::new(number, data));
104 }
105
106 None
107 }
108
109 pub fn get_record_fs<R>(
110 fs: &mut R,
111 file_record_size: usize,
112 position: u64,
113 ) -> NtfsReaderResult<Vec<u8>>
114 where
115 R: Seek + Read,
116 {
117 let mut data = vec![0; file_record_size];
118 fs.seek(SeekFrom::Start(position))?;
119 fs.read_exact(&mut data)?;
120
121 if !NtfsFile::is_valid(&data) {
122 return Err(NtfsReaderError::InvalidMftRecord { position });
123 }
124 Self::fixup_record(0, &mut data)?;
125 Ok(data)
126 }
127
128 pub fn read_data_fs<R>(
129 volume: &Volume,
130 reader: &mut R,
131 record: &[u8],
132 attribute_type: NtfsAttributeType,
133 ) -> NtfsReaderResult<Option<Vec<u8>>>
134 where
135 R: Seek + Read,
136 {
137 let header = unsafe { &*(record.as_ptr() as *const NtfsFileRecordHeader) };
138 let mut att_offset = header.attributes_offset as usize;
139 let used = usize::min(header.used_size as usize, record.len());
140
141 while att_offset < used {
143 let slice = &record[att_offset..used];
144 let attr = match NtfsAttribute::new(slice) {
145 Some(attr) => attr,
146 None => break,
147 };
148
149 if attr.header.type_id == NtfsAttributeType::End as u32 {
150 break;
151 }
152
153 if attr.header.type_id == attribute_type as u32 {
154 return Ok(Some(Self::read_attribute_data(reader, &attr, volume)?));
155 }
156
157 let attr_len = attr.len();
158 if attr_len == 0 {
159 break;
160 }
161 att_offset = match att_offset.checked_add(attr_len) {
162 Some(next) if next <= used => next,
163 _ => break,
164 };
165 }
166
167 att_offset = header.attributes_offset as usize;
169 while att_offset < used {
170 let slice = &record[att_offset..used];
171 let attr = match NtfsAttribute::new(slice) {
172 Some(attr) => attr,
173 None => break,
174 };
175
176 if attr.header.type_id == NtfsAttributeType::End as u32 {
177 break;
178 }
179
180 if attr.header.type_id == NtfsAttributeType::AttributeList as u32 {
181 let att_list_data = if attr.header.is_non_resident != 0 {
182 Self::read_attribute_data(reader, &attr, volume)?
183 } else {
184 match attr.as_resident_data() {
185 Some(data) => data.to_vec(),
186 None => break,
187 }
188 };
189
190 let mut list_offset = 0usize;
191
192 while list_offset < att_list_data.len() {
193 let entry_slice = &att_list_data[list_offset..];
194 let entry = match parse_attribute_list_entry(entry_slice) {
195 Some(entry) => entry,
196 None => break,
197 };
198
199 let type_id = entry.type_id;
200 let reference = entry.reference();
201 let entry_len = entry.length as usize;
202
203 if type_id == attribute_type as u32 {
204 let record_position =
205 volume.mft_position + (reference * volume.file_record_size);
206 if let Ok(target_record) = Self::get_record_fs(
207 reader,
208 volume.file_record_size as usize,
209 record_position,
210 ) {
211 let target_header = unsafe {
212 &*(target_record.as_ptr() as *const NtfsFileRecordHeader)
213 };
214 let mut target_offset = target_header.attributes_offset as usize;
215 let target_used =
216 usize::min(target_header.used_size as usize, target_record.len());
217
218 while target_offset < target_used {
219 let target_slice = &target_record[target_offset..target_used];
220 let target_attr = match NtfsAttribute::new(target_slice) {
221 Some(attr) => attr,
222 None => break,
223 };
224
225 if target_attr.header.type_id == NtfsAttributeType::End as u32 {
226 break;
227 }
228
229 if target_attr.header.type_id == attribute_type as u32 {
230 return Ok(Some(Self::read_attribute_data(
231 reader,
232 &target_attr,
233 volume,
234 )?));
235 }
236
237 let len = target_attr.len();
238 if len == 0 {
239 break;
240 }
241 target_offset = match target_offset.checked_add(len) {
242 Some(next) if next <= target_used => next,
243 _ => break,
244 };
245 }
246 }
247 }
248
249 if entry_len == 0 {
250 break;
251 }
252 list_offset = match list_offset.checked_add(entry_len) {
253 Some(next) if next <= att_list_data.len() => next,
254 _ => break,
255 };
256 let align = (8 - (list_offset % 8)) % 8;
257 list_offset = match list_offset.checked_add(align) {
258 Some(next) if next <= att_list_data.len() => next,
259 _ => break,
260 };
261 }
262 }
263
264 let attr_len = attr.len();
265 if attr_len == 0 {
266 break;
267 }
268 att_offset = match att_offset.checked_add(attr_len) {
269 Some(next) if next <= used => next,
270 _ => break,
271 };
272 }
273
274 Ok(None)
275 }
276
277 fn read_attribute_data<R>(
278 reader: &mut R,
279 att: &NtfsAttribute,
280 volume: &Volume,
281 ) -> NtfsReaderResult<Vec<u8>>
282 where
283 R: Seek + Read,
284 {
285 if att.header.is_non_resident == 0 {
286 let data = att
287 .as_resident_data()
288 .ok_or(NtfsReaderError::InvalidDataRun {
289 details: "resident attribute missing value",
290 })?;
291 Ok(data.to_vec())
292 } else {
293 let (size, runs) = att.get_nonresident_data_runs(volume)?;
294 let total_size =
295 usize::try_from(size).map_err(|_| NtfsReaderError::InvalidDataRun {
296 details: "attribute size exceeds addressable memory",
297 })?;
298
299 let mut data = Vec::with_capacity(total_size);
300 let mut buffer = Vec::new();
301 let mut copied = 0usize;
302
303 for run in runs.iter() {
304 if copied >= total_size {
305 break;
306 }
307
308 match run {
309 DataRun::Data { lcn, length } => {
310 let run_len = usize::try_from(*length).map_err(|_| {
311 NtfsReaderError::InvalidDataRun {
312 details: "run length exceeds addressable memory",
313 }
314 })?;
315 let buf_size = usize::min(run_len, total_size - copied);
316 buffer.resize(buf_size, 0u8);
317
318 reader.seek(SeekFrom::Start(*lcn))?;
319 reader.read_exact(&mut buffer)?;
320
321 data.extend_from_slice(&buffer);
322 copied += buf_size;
323 }
324 DataRun::Sparse { length } => {
325 let run_len = usize::try_from(*length).map_err(|_| {
326 NtfsReaderError::InvalidDataRun {
327 details: "run length exceeds addressable memory",
328 }
329 })?;
330 let buf_size = usize::min(run_len, total_size - copied);
331 data.resize(data.len() + buf_size, 0);
332 copied += buf_size;
333 }
334 }
335 }
336
337 Ok(data)
338 }
339 }
340
341 fn fixup_record(record_number: u64, data: &mut [u8]) -> NtfsReaderResult<()> {
342 if data.len() < core::mem::size_of::<NtfsFileRecordHeader>() {
343 return Err(NtfsReaderError::CorruptMftRecord {
344 number: record_number,
345 });
346 }
347 let header =
348 unsafe { core::ptr::read_unaligned(data.as_ptr() as *const NtfsFileRecordHeader) };
349
350 let usn_start = header.update_sequence_offset as usize;
351 if usn_start + 2 > data.len() {
352 return Err(NtfsReaderError::CorruptMftRecord {
353 number: record_number,
354 });
355 }
356 let usa_start = usn_start + 2;
357 let usa_end =
358 usn_start.saturating_add((header.update_sequence_length as usize).saturating_mul(2));
359 if usa_end > data.len() {
360 return Err(NtfsReaderError::CorruptMftRecord {
361 number: record_number,
362 });
363 }
364
365 let usn0 = data[usn_start];
366 let usn1 = data[usn_start + 1];
367
368 let mut sector_off = SECTOR_SIZE - 2;
369 for usa_off in (usa_start..usa_end).step_by(2) {
370 if sector_off + 2 > data.len() {
371 break;
372 }
373
374 let mut usa = [0u8; 2];
375 usa.copy_from_slice(&data[usa_off..usa_off + 2]);
376
377 let d0 = data[sector_off];
378 let d1 = data[sector_off + 1];
379 if d0 != usn0 || d1 != usn1 {
380 return Err(NtfsReaderError::CorruptMftRecord {
381 number: record_number,
382 });
383 }
384
385 data[sector_off..sector_off + 2].copy_from_slice(&usa);
386 sector_off += SECTOR_SIZE;
387 }
388 Ok(())
389 }
390}
391
392fn parse_attribute_list_entry(data: &[u8]) -> Option<&NtfsAttributeListEntry> {
393 if data.len() < size_of::<NtfsAttributeListEntry>() {
394 return None;
395 }
396 let entry = unsafe { &*(data.as_ptr() as *const NtfsAttributeListEntry) };
397 let length = entry.length as usize;
398 if length < size_of::<NtfsAttributeListEntry>() || length > data.len() {
399 return None;
400 }
401 Some(entry)
402}