1use std::io::{Read, Seek, SeekFrom};
6
7use crate::{
8 aligned_reader::open_volume,
9 api::*,
10 attribute::NtfsAttribute,
11 errors::{NtfsReaderError, NtfsReaderResult},
12 file::NtfsFile,
13 volume::Volume,
14};
15
16pub struct Mft {
17 pub volume: Volume,
18 pub data: Vec<u8>,
19 pub bitmap: Vec<u8>,
20 pub max_record: u64,
21}
22
23impl Mft {
24 pub fn new(volume: Volume) -> NtfsReaderResult<Self> {
25 let mut reader = open_volume(&volume.path)?;
26
27 let mft_record = Self::get_record_fs(
28 &mut reader,
29 volume.file_record_size as usize,
30 volume.mft_position,
31 );
32
33 let mut data =
34 Self::read_data_fs(&volume, &mut reader, &mft_record, NtfsAttributeType::Data)
35 .ok_or_else(|| NtfsReaderError::MissingMftAttribute("Data".to_string()))?;
36 let bitmap =
37 Self::read_data_fs(&volume, &mut reader, &mft_record, NtfsAttributeType::Bitmap)
38 .ok_or_else(|| NtfsReaderError::MissingMftAttribute("Bitmap".to_string()))?;
39
40 let max_record = (data.len() / volume.file_record_size as usize) as u64;
41
42 for number in 0..max_record {
55 let start = number as usize * volume.file_record_size as usize;
56 let end = start + volume.file_record_size as usize;
57 let data = &mut data[start..end];
58 Self::fixup_record(data);
59 }
60
61 Ok(Mft {
62 volume,
63 data,
64 bitmap,
65 max_record,
66 })
67 }
68
69 pub fn record_exists(&self, number: u64) -> bool {
70 if number > self.max_record {
71 return false;
72 }
73
74 let bitmap_idx = (number / 8) as usize;
75 let bitmap_off = number % 8;
76
77 if bitmap_idx >= self.bitmap.len() {
78 return false;
79 }
80
81 let bit = self.bitmap[bitmap_idx];
82 bit & (1 << bitmap_off) != 0
83 }
84
85 pub fn iterate_files<F>(&self, mut f: F)
86 where
87 F: FnMut(&NtfsFile),
88 {
89 for number in FIRST_NORMAL_RECORD..self.max_record {
90 if self.record_exists(number) {
91 if let Some(file) = self.get_record(number) {
92 if file.is_used() {
93 f(&file);
94 }
95 }
96 }
97 }
98 }
99
100 pub fn get_record_data(&self, number: u64) -> &[u8] {
101 let start = number as usize * self.volume.file_record_size as usize;
102 let end = start + self.volume.file_record_size as usize;
103 &self.data[start..end]
104 }
105
106 pub fn get_record(&self, number: u64) -> Option<NtfsFile<'_>> {
107 if number >= self.max_record {
108 return None;
109 }
110 let data = self.get_record_data(number);
111
112 if NtfsFile::is_valid(data) {
113 return Some(NtfsFile::new(number, data));
114 }
115
116 None
117 }
118
119 pub fn get_record_fs<R>(fs: &mut R, file_record_size: usize, position: u64) -> Vec<u8>
120 where
121 R: Seek + Read,
122 {
123 let mut data = vec![0; file_record_size];
124 let _ = fs.seek(SeekFrom::Start(position));
125 let _ = fs.read_exact(&mut data);
126
127 if NtfsFile::is_valid(&data) {
128 Self::fixup_record(&mut data);
129 data
130 } else {
131 Vec::new()
132 }
133 }
134
135 pub fn read_data_fs<R>(
136 volume: &Volume,
137 reader: &mut R,
138 record: &[u8],
139 attribute_type: NtfsAttributeType,
140 ) -> Option<Vec<u8>>
141 where
142 R: Seek + Read,
143 {
144 let header = unsafe { &*(record.as_ptr() as *const NtfsFileRecordHeader) };
145 let mut att_offset = header.attributes_offset as usize;
146
147 loop {
149 if att_offset >= header.used_size as usize {
150 break;
151 }
152
153 let att = NtfsAttribute::new(&record[att_offset..]);
154 if att.header.type_id == NtfsAttributeType::End as u32 {
155 break;
156 }
157
158 if att.header.type_id == attribute_type as u32 {
159 return Some(Self::read_attribute_data(reader, &att, volume));
160 }
161
162 att_offset += att.header.length as usize;
163 }
164
165 att_offset = header.attributes_offset as usize;
167 loop {
168 if att_offset >= header.used_size as usize {
169 break;
170 }
171
172 let att = NtfsAttribute::new(&record[att_offset..]);
173 if att.header.type_id == NtfsAttributeType::End as u32 {
174 break;
175 }
176
177 if att.header.type_id == NtfsAttributeType::AttributeList as u32 {
178 let att_list_data = if att.header.is_non_resident != 0 {
179 Self::read_attribute_data(reader, &att, volume)
180 } else {
181 let header = unsafe {
182 &*(record[att_offset..].as_ptr() as *const NtfsResidentAttributeHeader)
183 };
184 let value_length = header.value_length;
185 record[att_offset + header.value_offset as usize
186 ..att_offset + header.value_offset as usize + value_length as usize]
187 .to_vec()
188 };
189
190 let mut list_offset = 0;
191
192 while list_offset < att_list_data.len() {
193 let entry = unsafe {
194 &*(att_list_data[list_offset..].as_ptr() as *const NtfsAttributeListEntry)
195 };
196
197 let type_id = entry.type_id;
198 let reference = entry.reference();
199
200 if type_id == attribute_type as u32 {
201 let record_position =
202 volume.mft_position + (reference * volume.file_record_size);
203 let target_record = Self::get_record_fs(
204 reader,
205 volume.file_record_size as usize,
206 record_position,
207 );
208
209 if !target_record.is_empty() {
210 let header = unsafe {
212 &*(target_record.as_ptr() as *const NtfsFileRecordHeader)
213 };
214 let mut att_offset = header.attributes_offset as usize;
215
216 loop {
217 if att_offset >= header.used_size as usize {
218 break;
219 }
220
221 let att = NtfsAttribute::new(&target_record[att_offset..]);
222 if att.header.type_id == NtfsAttributeType::End as u32 {
223 break;
224 }
225
226 if att.header.type_id == attribute_type as u32 {
227 return Some(Self::read_attribute_data(reader, &att, volume));
228 }
229
230 att_offset += att.header.length as usize;
231 }
232 }
233 }
234
235 list_offset += entry.length as usize;
236 list_offset += (8 - (list_offset % 8)) % 8;
238 }
239 }
240
241 att_offset += att.header.length as usize;
242 }
243
244 None
245 }
246
247 fn read_attribute_data<R>(reader: &mut R, att: &NtfsAttribute, volume: &Volume) -> Vec<u8>
248 where
249 R: Seek + Read,
250 {
251 let mut data = Vec::<u8>::new();
252
253 if att.header.is_non_resident == 0 {
254 data.copy_from_slice(att.as_resident_data());
255 } else {
256 let mut buffer = Vec::new();
257 let (size, runs) = att.get_nonresident_data_runs(volume);
258 data.reserve_exact(size);
259 let mut copied = 0usize;
260
261 for run in runs.iter() {
262 if copied >= size {
263 break;
264 }
265
266 let buf_size = usize::min(run.len(), size - copied);
267 buffer.resize(buf_size, 0u8);
268
269 let _ = reader.seek(SeekFrom::Start(run.start as u64));
270 let _ = reader.read_exact(&mut buffer);
271
272 data.append(&mut buffer.clone());
273 copied += buf_size;
274 }
275 }
276
277 data
278 }
279
280 fn fixup_record(data: &mut [u8]) {
281 let header = unsafe { &*(data.as_ptr() as *const NtfsFileRecordHeader) };
282
283 let usn_start = header.update_sequence_offset as usize;
285 let usa_start = usn_start + 2;
287 let usa_end = usn_start + header.update_sequence_length as usize * 2;
288
289 let mut sector_off = SECTOR_SIZE - 2;
293 for usa_off in (usa_start..usa_end).step_by(2) {
294 let mut usa = [0u8; 2];
295 usa.clone_from_slice(&data[usa_off..usa_off + 2]);
296
297 data[sector_off..sector_off + 2].copy_from_slice(&usa);
303 sector_off += SECTOR_SIZE;
304 }
305 }
306}
307
308#[cfg(test)]
309mod tests {
310 use crate::{
311 errors::NtfsReaderResult, mft::Mft, test_utils::TEST_VOLUME_LETTER, volume::Volume,
312 };
313
314 #[test]
315 fn test_mft_creation() -> NtfsReaderResult<()> {
316 let vol = Volume::new(format!("\\\\.\\{}:", TEST_VOLUME_LETTER))?;
317 let mft = Mft::new(vol)?;
318
319 assert!(mft.max_record > 0);
320 assert!(!mft.data.is_empty());
321 assert!(!mft.bitmap.is_empty());
322
323 Ok(())
324 }
325
326 #[test]
327 fn test_record_exists() -> NtfsReaderResult<()> {
328 let vol = Volume::new(format!("\\\\.\\{}:", TEST_VOLUME_LETTER))?;
329 let mft = Mft::new(vol)?;
330
331 assert!(mft.record_exists(0));
333
334 assert!(!mft.record_exists(u64::MAX));
336 assert!(!mft.record_exists(mft.max_record + 1));
337
338 Ok(())
339 }
340
341 #[test]
342 fn test_get_record() -> NtfsReaderResult<()> {
343 let vol = Volume::new(format!("\\\\.\\{}:", TEST_VOLUME_LETTER))?;
344 let mft = Mft::new(vol)?;
345
346 let record = mft.get_record(0);
348 assert!(record.is_some());
349
350 let invalid = mft.get_record(mft.max_record + 1);
352 assert!(invalid.is_none());
353
354 Ok(())
355 }
356
357 #[test]
358 fn test_iterate_files() -> NtfsReaderResult<()> {
359 let vol = Volume::new(format!("\\\\.\\{}:", TEST_VOLUME_LETTER))?;
360 let mft = Mft::new(vol)?;
361
362 let mut count = 0;
363 mft.iterate_files(|_file| {
364 count += 1;
365 });
366
367 assert!(count > 0, "Should iterate over at least some files");
368
369 Ok(())
370 }
371}