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 extension_records: Vec<(u64, u64)>,
23}
24
25impl Mft {
26 pub fn new(volume: Volume) -> NtfsReaderResult<Self> {
27 let mut reader = open_volume(&volume.path)?;
28
29 let mft_record =
30 Self::get_record_fs(&mut reader, volume.file_record_size, volume.mft_position)?;
31
32 let mut data =
33 Self::read_data_fs(&volume, &mut reader, &mft_record, NtfsAttributeType::Data)?
34 .ok_or_else(|| NtfsReaderError::MissingMftAttribute("Data".to_string()))?;
35 let bitmap =
36 Self::read_data_fs(&volume, &mut reader, &mft_record, NtfsAttributeType::Bitmap)?
37 .ok_or_else(|| NtfsReaderError::MissingMftAttribute("Bitmap".to_string()))?;
38
39 let max_record = data.len() as u64 / volume.file_record_size;
40
41 for number in 0..max_record {
43 let start = number * volume.file_record_size;
44 let end = start + volume.file_record_size;
45 let (start, end) = (start as usize, end as usize);
46 let data = &mut data[start..end];
47 Self::fixup_record(number, data)?;
48 }
49
50 let mut mft = Mft {
51 volume,
52 data,
53 bitmap,
54 max_record,
55 extension_records: Vec::new(),
56 };
57 mft.index_extension_records();
58 Ok(mft)
59 }
60
61 pub fn record_exists(&self, number: u64) -> bool {
62 if number >= self.max_record {
63 return false;
64 }
65
66 let bitmap_idx = number / 8;
67 let bitmap_off = (number % 8) as u8;
68
69 if bitmap_idx >= self.bitmap.len() as u64 {
70 return false;
71 }
72
73 let bit = self.bitmap[bitmap_idx as usize];
74 (bit & (1u8 << bitmap_off)) != 0
75 }
76
77 pub fn files(&self) -> impl Iterator<Item = NtfsFile<'_>> {
78 (FIRST_NORMAL_RECORD..self.max_record)
79 .filter(|&n| self.record_exists(n))
80 .filter_map(|n| self.get_record(n))
81 .filter(|f| f.is_used() && !f.is_extension())
82 }
83
84 pub fn file_records<'a>(
87 &'a self,
88 file: &NtfsFile<'_>,
89 ) -> impl Iterator<Item = NtfsFile<'a>> + 'a {
90 let base_number = file.base_record_number().unwrap_or(file.number());
91 let base_reference = self
92 .get_record(base_number)
93 .map(|base| base.reference_number());
94 let start = self
95 .extension_records
96 .partition_point(|(base, _)| *base < base_number);
97 let end = self
98 .extension_records
99 .partition_point(|(base, _)| *base <= base_number);
100
101 std::iter::once(base_number)
102 .chain(
103 self.extension_records[start..end]
104 .iter()
105 .map(|(_, extension)| *extension),
106 )
107 .filter(move |number| self.record_exists(*number))
108 .filter_map(move |number| self.get_record(number))
109 .filter(move |record| {
110 record.is_used()
111 && (record.number() == base_number
112 || record.base_reference_number() == base_reference)
113 })
114 }
115
116 #[deprecated(since = "0.4.5", note = "use `files()` iterator instead")]
117 pub fn iterate_files<F>(&self, mut f: F)
118 where
119 F: FnMut(&NtfsFile),
120 {
121 for file in self.files() {
122 f(&file);
123 }
124 }
125
126 fn get_record_data(&self, number: u64) -> &[u8] {
127 let start = number * self.volume.file_record_size;
128 let end = start + self.volume.file_record_size;
129 &self.data[start as usize..end as usize]
130 }
131
132 pub fn get_record(&self, number: u64) -> Option<NtfsFile<'_>> {
133 if number >= self.max_record {
134 return None;
135 }
136 let data = self.get_record_data(number);
137
138 if NtfsFile::is_valid(data) {
139 return Some(NtfsFile::new(number, data));
140 }
141
142 None
143 }
144
145 fn index_extension_records(&mut self) {
146 self.extension_records = (0..self.max_record)
147 .filter(|&number| self.record_exists(number))
148 .filter_map(|number| {
149 let record = self.get_record(number)?;
150 if !record.is_used() {
151 return None;
152 }
153 record
154 .base_record_number()
155 .map(|base| (base, record.number()))
156 })
157 .collect();
158 self.extension_records.sort_unstable();
159 }
160
161 pub fn get_record_fs<R>(
162 fs: &mut R,
163 file_record_size: u64,
164 position: u64,
165 ) -> NtfsReaderResult<Vec<u8>>
166 where
167 R: Seek + Read,
168 {
169 let mut data = vec![0; file_record_size as usize];
170 fs.seek(SeekFrom::Start(position))?;
171 fs.read_exact(&mut data)?;
172
173 if !NtfsFile::is_valid(&data) {
174 return Err(NtfsReaderError::InvalidMftRecord { position });
175 }
176 Self::fixup_record(0, &mut data)?;
177 Ok(data)
178 }
179
180 pub fn read_data_fs<R>(
181 volume: &Volume,
182 reader: &mut R,
183 record: &[u8],
184 attribute_type: NtfsAttributeType,
185 ) -> NtfsReaderResult<Option<Vec<u8>>>
186 where
187 R: Seek + Read,
188 {
189 let header = unsafe { &*(record.as_ptr() as *const NtfsFileRecordHeader) };
190 let mut att_offset = header.attributes_offset as usize;
191 let used = usize::min(header.used_size as usize, record.len());
192
193 while att_offset < used {
195 let slice = &record[att_offset..used];
196 let attr = match NtfsAttribute::new(slice) {
197 Some(attr) => attr,
198 None => break,
199 };
200
201 if attr.header.type_id == NtfsAttributeType::End as u32 {
202 break;
203 }
204
205 if attr.header.type_id == attribute_type as u32 {
206 return Ok(Some(Self::read_attribute_data(reader, &attr, volume)?));
207 }
208
209 let attr_len = attr.len();
210 if attr_len == 0 {
211 break;
212 }
213 att_offset = match att_offset.checked_add(attr_len) {
214 Some(next) if next <= used => next,
215 _ => break,
216 };
217 }
218
219 att_offset = header.attributes_offset as usize;
221 while att_offset < used {
222 let slice = &record[att_offset..used];
223 let attr = match NtfsAttribute::new(slice) {
224 Some(attr) => attr,
225 None => break,
226 };
227
228 if attr.header.type_id == NtfsAttributeType::End as u32 {
229 break;
230 }
231
232 if attr.header.type_id == NtfsAttributeType::AttributeList as u32 {
233 let att_list_data = if attr.header.is_non_resident != 0 {
234 Self::read_attribute_data(reader, &attr, volume)?
235 } else {
236 match attr.as_resident_data() {
237 Some(data) => data.to_vec(),
238 None => break,
239 }
240 };
241
242 let mut list_offset = 0usize;
243
244 while list_offset < att_list_data.len() {
245 let entry_slice = &att_list_data[list_offset..];
246 let entry = match parse_attribute_list_entry(entry_slice) {
247 Some(entry) => entry,
248 None => break,
249 };
250
251 let type_id = entry.type_id;
252 let reference = entry.reference();
253 let entry_len = entry.length as usize;
254
255 if type_id == attribute_type as u32 {
256 let record_position =
257 volume.mft_position + (reference * volume.file_record_size);
258 if let Ok(target_record) =
259 Self::get_record_fs(reader, volume.file_record_size, record_position)
260 {
261 let target_header = unsafe {
262 &*(target_record.as_ptr() as *const NtfsFileRecordHeader)
263 };
264 let mut target_offset = target_header.attributes_offset as usize;
265 let target_used =
266 usize::min(target_header.used_size as usize, target_record.len());
267
268 while target_offset < target_used {
269 let target_slice = &target_record[target_offset..target_used];
270 let target_attr = match NtfsAttribute::new(target_slice) {
271 Some(attr) => attr,
272 None => break,
273 };
274
275 if target_attr.header.type_id == NtfsAttributeType::End as u32 {
276 break;
277 }
278
279 if target_attr.header.type_id == attribute_type as u32 {
280 return Ok(Some(Self::read_attribute_data(
281 reader,
282 &target_attr,
283 volume,
284 )?));
285 }
286
287 let len = target_attr.len();
288 if len == 0 {
289 break;
290 }
291 target_offset = match target_offset.checked_add(len) {
292 Some(next) if next <= target_used => next,
293 _ => break,
294 };
295 }
296 }
297 }
298
299 if entry_len == 0 {
300 break;
301 }
302 list_offset = match list_offset.checked_add(entry_len) {
303 Some(next) if next <= att_list_data.len() => next,
304 _ => break,
305 };
306 let align = (8 - (list_offset % 8)) % 8;
307 list_offset = match list_offset.checked_add(align) {
308 Some(next) if next <= att_list_data.len() => next,
309 _ => break,
310 };
311 }
312 }
313
314 let attr_len = attr.len();
315 if attr_len == 0 {
316 break;
317 }
318 att_offset = match att_offset.checked_add(attr_len) {
319 Some(next) if next <= used => next,
320 _ => break,
321 };
322 }
323
324 Ok(None)
325 }
326
327 fn read_attribute_data<R>(
328 reader: &mut R,
329 att: &NtfsAttribute,
330 volume: &Volume,
331 ) -> NtfsReaderResult<Vec<u8>>
332 where
333 R: Seek + Read,
334 {
335 if att.header.is_non_resident == 0 {
336 let data = att
337 .as_resident_data()
338 .ok_or(NtfsReaderError::InvalidDataRun {
339 details: "resident attribute missing value",
340 })?;
341 Ok(data.to_vec())
342 } else {
343 let (size, runs) = att.get_nonresident_data_runs(volume)?;
344 let total_size =
345 usize::try_from(size).map_err(|_| NtfsReaderError::AllocationTooLarge { size })?;
346
347 let mut data = Vec::new();
348 data.try_reserve(total_size)
349 .map_err(|_| NtfsReaderError::AllocationTooLarge { size })?;
350 let mut copied = 0u64;
351
352 for run in runs.iter() {
353 if copied >= size {
354 break;
355 }
356
357 let buf_size = match run {
358 DataRun::Data { lcn, length } => {
359 let buf_size = u64::min(*length, size - copied);
360 let start = data.len();
361 data.resize(start + buf_size as usize, 0u8);
362
363 reader.seek(SeekFrom::Start(*lcn))?;
364 reader.read_exact(&mut data[start..])?;
365 buf_size
366 }
367 DataRun::Sparse { length } => {
368 let buf_size = u64::min(*length, size - copied);
369 data.resize(data.len() + buf_size as usize, 0);
370 buf_size
371 }
372 };
373 copied += buf_size;
374 }
375
376 Ok(data)
377 }
378 }
379
380 fn fixup_record(record_number: u64, data: &mut [u8]) -> NtfsReaderResult<()> {
381 if data.len() < core::mem::size_of::<NtfsFileRecordHeader>() {
382 return Err(NtfsReaderError::CorruptMftRecord {
383 number: record_number,
384 });
385 }
386 let header =
387 unsafe { core::ptr::read_unaligned(data.as_ptr() as *const NtfsFileRecordHeader) };
388
389 let usn_start = header.update_sequence_offset as usize;
390 if usn_start + 2 > data.len() {
391 return Err(NtfsReaderError::CorruptMftRecord {
392 number: record_number,
393 });
394 }
395 let usa_start = usn_start + 2;
396 let usa_end =
397 usn_start.saturating_add((header.update_sequence_length as usize).saturating_mul(2));
398 if usa_end > data.len() {
399 return Err(NtfsReaderError::CorruptMftRecord {
400 number: record_number,
401 });
402 }
403
404 let usn0 = data[usn_start];
405 let usn1 = data[usn_start + 1];
406
407 let mut sector_off = SECTOR_SIZE - 2;
408 for usa_off in (usa_start..usa_end).step_by(2) {
409 if sector_off + 2 > data.len() {
410 break;
411 }
412
413 let mut usa = [0u8; 2];
414 usa.copy_from_slice(&data[usa_off..usa_off + 2]);
415
416 let d0 = data[sector_off];
417 let d1 = data[sector_off + 1];
418 if d0 != usn0 || d1 != usn1 {
419 return Err(NtfsReaderError::CorruptMftRecord {
420 number: record_number,
421 });
422 }
423
424 data[sector_off..sector_off + 2].copy_from_slice(&usa);
425 sector_off += SECTOR_SIZE;
426 }
427 Ok(())
428 }
429}
430
431fn parse_attribute_list_entry(data: &[u8]) -> Option<&NtfsAttributeListEntry> {
432 if data.len() < size_of::<NtfsAttributeListEntry>() {
433 return None;
434 }
435 let entry = unsafe { &*(data.as_ptr() as *const NtfsAttributeListEntry) };
436 let length = entry.length as usize;
437 if length < size_of::<NtfsAttributeListEntry>() || length > data.len() {
438 return None;
439 }
440 Some(entry)
441}
442
443#[cfg(test)]
444mod tests {
445 use std::path::PathBuf;
446
447 use super::*;
448 use crate::file_info::FileInfo;
449
450 const RECORD_SIZE: usize = 1024;
451 const ATTRIBUTES_OFFSET: usize = 48;
452
453 #[test]
454 fn combines_base_and_extension_records_into_one_file() {
455 let attributes = NtfsFileNameFlags::Hidden as u32
456 | NtfsFileNameFlags::System as u32
457 | NtfsFileNameFlags::Archive as u32
458 | NtfsFileNameFlags::SparseFile as u32;
459 let file_size = 34_359_738_368u64;
460
461 let mut base = new_record(24, 7, 0);
462 let mut offset = ATTRIBUTES_OFFSET;
463 offset = add_standard_information(&mut base, offset, attributes);
464 offset = add_nonresident_data(&mut base, offset, file_size);
465 finish_record(&mut base, offset);
466
467 let base_reference = (7u64 << 48) | 24;
468 let mut extension = new_record(25, 3, base_reference);
469 let offset = add_file_name(
470 &mut extension,
471 ATTRIBUTES_OFFSET,
472 "large-fragmented.rar",
473 attributes,
474 );
475 finish_record(&mut extension, offset);
476
477 let mut data = vec![0u8; 26 * RECORD_SIZE];
478 data[24 * RECORD_SIZE..25 * RECORD_SIZE].copy_from_slice(&base);
479 data[25 * RECORD_SIZE..26 * RECORD_SIZE].copy_from_slice(&extension);
480
481 let boot_sector = unsafe { std::mem::zeroed() };
482 let volume = Volume {
483 path: PathBuf::from(r"\\.\T:"),
484 boot_sector,
485 cluster_size: 4096,
486 volume_size: 0,
487 file_record_size: RECORD_SIZE as u64,
488 mft_position: 0,
489 };
490 let mut mft = Mft {
491 volume,
492 data,
493 bitmap: vec![0, 0, 0, 0b0000_0011],
494 max_record: 26,
495 extension_records: Vec::new(),
496 };
497 mft.index_extension_records();
498
499 let files: Vec<_> = mft.files().collect();
500 assert_eq!(files.len(), 1, "extension record must not be a second file");
501 assert_eq!(files[0].number(), 24);
502 assert_eq!(mft.file_records(&files[0]).count(), 2);
503
504 let info = FileInfo::new(&mft, &files[0]);
505 assert_eq!(info.name, "large-fragmented.rar");
506 assert_eq!(info.size, file_size);
507 assert_eq!(info.file_attributes, attributes);
508 }
509
510 fn new_record(number: u64, sequence: u16, base_reference: u64) -> Vec<u8> {
511 let mut record = vec![0u8; RECORD_SIZE];
512 record[0..4].copy_from_slice(FILE_RECORD_SIGNATURE);
513 write_u16(&mut record, 4, 42);
514 write_u16(&mut record, 6, 1);
515 write_u16(&mut record, 16, sequence);
516 write_u16(&mut record, 18, 1);
517 write_u16(&mut record, 20, ATTRIBUTES_OFFSET as u16);
518 write_u16(&mut record, 22, NtfsFileFlags::InUse as u16);
519 write_u32(&mut record, 28, RECORD_SIZE as u32);
520 write_u64(&mut record, 32, base_reference);
521 write_u16(&mut record, 40, number as u16);
522 record
523 }
524
525 fn add_standard_information(record: &mut [u8], offset: usize, attributes: u32) -> usize {
526 let mut value = vec![0u8; size_of::<NtfsStandardInformation>()];
527 write_u64(&mut value, 0, EPOCH_DIFFERENCE + 10_000_000);
528 write_u64(&mut value, 8, EPOCH_DIFFERENCE + 20_000_000);
529 write_u64(&mut value, 24, EPOCH_DIFFERENCE + 30_000_000);
530 write_u32(&mut value, 32, attributes);
531 add_resident_attribute(
532 record,
533 offset,
534 NtfsAttributeType::StandardInformation,
535 0,
536 &value,
537 )
538 }
539
540 fn add_file_name(record: &mut [u8], offset: usize, name: &str, attributes: u32) -> usize {
541 let encoded: Vec<u16> = name.encode_utf16().collect();
542 let mut value = vec![0u8; size_of::<NtfsFileNameHeader>() + encoded.len() * 2];
543 write_u64(&mut value, 0, ROOT_RECORD);
544 write_u64(&mut value, 40, 34_359_738_368);
545 write_u64(&mut value, 48, 34_359_738_368);
546 write_u32(&mut value, 56, attributes);
547 value[64] = encoded.len() as u8;
548 value[65] = NtfsFileNamespace::Win32 as u8;
549 for (index, character) in encoded.into_iter().enumerate() {
550 write_u16(
551 &mut value,
552 size_of::<NtfsFileNameHeader>() + index * 2,
553 character,
554 );
555 }
556 add_resident_attribute(record, offset, NtfsAttributeType::FileName, 1, &value)
557 }
558
559 fn add_resident_attribute(
560 record: &mut [u8],
561 offset: usize,
562 attribute_type: NtfsAttributeType,
563 id: u16,
564 value: &[u8],
565 ) -> usize {
566 const VALUE_OFFSET: usize = 24;
567 let length = align_to_eight(VALUE_OFFSET + value.len());
568 write_u32(record, offset, attribute_type as u32);
569 write_u32(record, offset + 4, length as u32);
570 write_u16(record, offset + 14, id);
571 write_u32(record, offset + 16, value.len() as u32);
572 write_u16(record, offset + 20, VALUE_OFFSET as u16);
573 record[offset + VALUE_OFFSET..offset + VALUE_OFFSET + value.len()].copy_from_slice(value);
574 offset + length
575 }
576
577 fn add_nonresident_data(record: &mut [u8], offset: usize, size: u64) -> usize {
578 let length = size_of::<NtfsNonResidentAttributeHeader>();
579 write_u32(record, offset, NtfsAttributeType::Data as u32);
580 write_u32(record, offset + 4, length as u32);
581 record[offset + 8] = 1;
582 write_u16(record, offset + 14, 2);
583 write_u64(record, offset + 16, 0);
584 write_u64(record, offset + 40, size);
585 write_u64(record, offset + 48, size);
586 write_u64(record, offset + 56, size);
587 offset + length
588 }
589
590 fn finish_record(record: &mut [u8], used_size: usize) {
591 write_u32(record, 24, used_size as u32);
592 }
593
594 fn align_to_eight(value: usize) -> usize {
595 (value + 7) & !7
596 }
597
598 fn write_u16(data: &mut [u8], offset: usize, value: u16) {
599 data[offset..offset + 2].copy_from_slice(&value.to_le_bytes());
600 }
601
602 fn write_u32(data: &mut [u8], offset: usize, value: u32) {
603 data[offset..offset + 4].copy_from_slice(&value.to_le_bytes());
604 }
605
606 fn write_u64(data: &mut [u8], offset: usize, value: u64) {
607 data[offset..offset + 8].copy_from_slice(&value.to_le_bytes());
608 }
609}