f2930515291c48e1849d65acb10c9483d8b1cf5d
[reactos.git] / reactos / boot / freeldr / freeldr / fs / ntfs.c
1 /*
2 * FreeLoader NTFS support
3 * Copyright (C) 2004 Filip Navara <xnavara@volny.cz>
4 * Copyright (C) 2009-2010 Hervé Poussineau
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 /*
22 * Limitations:
23 * - No support for compressed files.
24 * - May crash on corrupted filesystem.
25 */
26
27 #ifndef _M_ARM
28 #include <freeldr.h>
29 #include <debug.h>
30
31 #define TAG_NTFS_CONTEXT 'CftN'
32 #define TAG_NTFS_LIST 'LftN'
33 #define TAG_NTFS_MFT 'MftN'
34 #define TAG_NTFS_INDEX_REC 'IftN'
35 #define TAG_NTFS_BITMAP 'BftN'
36 #define TAG_NTFS_FILE 'FftN'
37 #define TAG_NTFS_VOLUME 'VftN'
38 #define TAG_NTFS_DATA 'DftN'
39
40 DBG_DEFAULT_CHANNEL(FILESYSTEM);
41
42 typedef struct _NTFS_VOLUME_INFO
43 {
44 NTFS_BOOTSECTOR BootSector;
45 ULONG ClusterSize;
46 ULONG MftRecordSize;
47 ULONG IndexRecordSize;
48 PNTFS_MFT_RECORD MasterFileTable;
49 /* FIXME: MFTContext is never freed. */
50 PNTFS_ATTR_CONTEXT MFTContext;
51 ULONG DeviceId;
52 PUCHAR TemporarySector;
53 } NTFS_VOLUME_INFO;
54
55 PNTFS_VOLUME_INFO NtfsVolumes[MAX_FDS];
56
57 static ULONGLONG NtfsGetAttributeSize(PNTFS_ATTR_RECORD AttrRecord)
58 {
59 if (AttrRecord->IsNonResident)
60 return AttrRecord->NonResident.DataSize;
61 else
62 return AttrRecord->Resident.ValueLength;
63 }
64
65 static PUCHAR NtfsDecodeRun(PUCHAR DataRun, LONGLONG *DataRunOffset, ULONGLONG *DataRunLength)
66 {
67 UCHAR DataRunOffsetSize;
68 UCHAR DataRunLengthSize;
69 CHAR i;
70
71 DataRunOffsetSize = (*DataRun >> 4) & 0xF;
72 DataRunLengthSize = *DataRun & 0xF;
73 *DataRunOffset = 0;
74 *DataRunLength = 0;
75 DataRun++;
76 for (i = 0; i < DataRunLengthSize; i++)
77 {
78 *DataRunLength += ((ULONG64)*DataRun) << (i * 8);
79 DataRun++;
80 }
81
82 /* NTFS 3+ sparse files */
83 if (DataRunOffsetSize == 0)
84 {
85 *DataRunOffset = -1;
86 }
87 else
88 {
89 for (i = 0; i < DataRunOffsetSize - 1; i++)
90 {
91 *DataRunOffset += ((ULONG64)*DataRun) << (i * 8);
92 DataRun++;
93 }
94 /* The last byte contains sign so we must process it different way. */
95 *DataRunOffset = ((LONG64)(CHAR)(*(DataRun++)) << (i * 8)) + *DataRunOffset;
96 }
97
98 TRACE("DataRunOffsetSize: %x\n", DataRunOffsetSize);
99 TRACE("DataRunLengthSize: %x\n", DataRunLengthSize);
100 TRACE("DataRunOffset: %x\n", *DataRunOffset);
101 TRACE("DataRunLength: %x\n", *DataRunLength);
102
103 return DataRun;
104 }
105
106 static PNTFS_ATTR_CONTEXT NtfsPrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord)
107 {
108 PNTFS_ATTR_CONTEXT Context;
109
110 Context = FrLdrTempAlloc(FIELD_OFFSET(NTFS_ATTR_CONTEXT, Record) + AttrRecord->Length,
111 TAG_NTFS_CONTEXT);
112 RtlCopyMemory(&Context->Record, AttrRecord, AttrRecord->Length);
113 if (AttrRecord->IsNonResident)
114 {
115 LONGLONG DataRunOffset;
116 ULONGLONG DataRunLength;
117
118 Context->CacheRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset;
119 Context->CacheRunOffset = 0;
120 Context->CacheRun = NtfsDecodeRun(Context->CacheRun, &DataRunOffset, &DataRunLength);
121 Context->CacheRunLength = DataRunLength;
122 if (DataRunOffset != -1)
123 {
124 /* Normal run. */
125 Context->CacheRunStartLCN =
126 Context->CacheRunLastLCN = DataRunOffset;
127 }
128 else
129 {
130 /* Sparse run. */
131 Context->CacheRunStartLCN = -1;
132 Context->CacheRunLastLCN = 0;
133 }
134 Context->CacheRunCurrentOffset = 0;
135 }
136
137 return Context;
138 }
139
140 static VOID NtfsReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context)
141 {
142 FrLdrTempFree(Context, TAG_NTFS_CONTEXT);
143 }
144
145 static BOOLEAN NtfsDiskRead(PNTFS_VOLUME_INFO Volume, ULONGLONG Offset, ULONGLONG Length, PCHAR Buffer)
146 {
147 LARGE_INTEGER Position;
148 ULONG Count;
149 ULONG ReadLength;
150 ARC_STATUS Status;
151
152 TRACE("NtfsDiskRead - Offset: %I64d Length: %I64d\n", Offset, Length);
153
154 //
155 // I. Read partial first sector if needed
156 //
157 if (Offset % Volume->BootSector.BytesPerSector)
158 {
159 Position.QuadPart = Offset & ~(Volume->BootSector.BytesPerSector - 1);
160 Status = ArcSeek(Volume->DeviceId, &Position, SeekAbsolute);
161 if (Status != ESUCCESS)
162 return FALSE;
163 Status = ArcRead(Volume->DeviceId, Volume->TemporarySector, Volume->BootSector.BytesPerSector, &Count);
164 if (Status != ESUCCESS || Count != Volume->BootSector.BytesPerSector)
165 return FALSE;
166 ReadLength = (USHORT)min(Length, Volume->BootSector.BytesPerSector - (Offset % Volume->BootSector.BytesPerSector));
167
168 //
169 // Copy interesting data
170 //
171 RtlCopyMemory(Buffer,
172 &Volume->TemporarySector[Offset % Volume->BootSector.BytesPerSector],
173 ReadLength);
174
175 //
176 // Move to unfilled buffer part
177 //
178 Buffer += ReadLength;
179 Length -= ReadLength;
180 Offset += ReadLength;
181 }
182
183 //
184 // II. Read all complete blocks
185 //
186 if (Length >= Volume->BootSector.BytesPerSector)
187 {
188 Position.QuadPart = Offset;
189 Status = ArcSeek(Volume->DeviceId, &Position, SeekAbsolute);
190 if (Status != ESUCCESS)
191 return FALSE;
192 ReadLength = Length & ~(Volume->BootSector.BytesPerSector - 1);
193 Status = ArcRead(Volume->DeviceId, Buffer, ReadLength, &Count);
194 if (Status != ESUCCESS || Count != ReadLength)
195 return FALSE;
196
197 //
198 // Move to unfilled buffer part
199 //
200 Buffer += ReadLength;
201 Length -= ReadLength;
202 Offset += ReadLength;
203 }
204
205 //
206 // III. Read the rest of data
207 //
208 if (Length)
209 {
210 Position.QuadPart = Offset;
211 Status = ArcSeek(Volume->DeviceId, &Position, SeekAbsolute);
212 if (Status != ESUCCESS)
213 return FALSE;
214 Status = ArcRead(Volume->DeviceId, Buffer, (ULONG)Length, &Count);
215 if (Status != ESUCCESS || Count != Length)
216 return FALSE;
217 }
218
219 return TRUE;
220 }
221
222 static ULONG NtfsReadAttribute(PNTFS_VOLUME_INFO Volume, PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset, PCHAR Buffer, ULONG Length)
223 {
224 ULONGLONG LastLCN;
225 PUCHAR DataRun;
226 LONGLONG DataRunOffset;
227 ULONGLONG DataRunLength;
228 LONGLONG DataRunStartLCN;
229 ULONGLONG CurrentOffset;
230 ULONG ReadLength;
231 ULONG AlreadyRead;
232
233 if (!Context->Record.IsNonResident)
234 {
235 if (Offset > Context->Record.Resident.ValueLength)
236 return 0;
237 if (Offset + Length > Context->Record.Resident.ValueLength)
238 Length = (ULONG)(Context->Record.Resident.ValueLength - Offset);
239 RtlCopyMemory(Buffer, (PCHAR)&Context->Record + Context->Record.Resident.ValueOffset + Offset, Length);
240 return Length;
241 }
242
243 /*
244 * Non-resident attribute
245 */
246
247 /*
248 * I. Find the corresponding start data run.
249 */
250
251 AlreadyRead = 0;
252
253 // FIXME: Cache seems to be non-working. Disable it for now
254 //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
255 if (0)
256 {
257 DataRun = Context->CacheRun;
258 LastLCN = Context->CacheRunLastLCN;
259 DataRunStartLCN = Context->CacheRunStartLCN;
260 DataRunLength = Context->CacheRunLength;
261 CurrentOffset = Context->CacheRunCurrentOffset;
262 }
263 else
264 {
265 LastLCN = 0;
266 DataRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset;
267 CurrentOffset = 0;
268
269 while (1)
270 {
271 DataRun = NtfsDecodeRun(DataRun, &DataRunOffset, &DataRunLength);
272 if (DataRunOffset != -1)
273 {
274 /* Normal data run. */
275 DataRunStartLCN = LastLCN + DataRunOffset;
276 LastLCN = DataRunStartLCN;
277 }
278 else
279 {
280 /* Sparse data run. */
281 DataRunStartLCN = -1;
282 }
283
284 if (Offset >= CurrentOffset &&
285 Offset < CurrentOffset + (DataRunLength * Volume->ClusterSize))
286 {
287 break;
288 }
289
290 if (*DataRun == 0)
291 {
292 return AlreadyRead;
293 }
294
295 CurrentOffset += DataRunLength * Volume->ClusterSize;
296 }
297 }
298
299 /*
300 * II. Go through the run list and read the data
301 */
302
303 ReadLength = (ULONG)min(DataRunLength * Volume->ClusterSize - (Offset - CurrentOffset), Length);
304 if (DataRunStartLCN == -1)
305 RtlZeroMemory(Buffer, ReadLength);
306 if (NtfsDiskRead(Volume, DataRunStartLCN * Volume->ClusterSize + Offset - CurrentOffset, ReadLength, Buffer))
307 {
308 Length -= ReadLength;
309 Buffer += ReadLength;
310 AlreadyRead += ReadLength;
311
312 if (ReadLength == DataRunLength * Volume->ClusterSize - (Offset - CurrentOffset))
313 {
314 CurrentOffset += DataRunLength * Volume->ClusterSize;
315 DataRun = NtfsDecodeRun(DataRun, &DataRunOffset, &DataRunLength);
316 if (DataRunLength != (ULONGLONG)-1)
317 {
318 DataRunStartLCN = LastLCN + DataRunOffset;
319 LastLCN = DataRunStartLCN;
320 }
321 else
322 DataRunStartLCN = -1;
323
324 if (*DataRun == 0)
325 return AlreadyRead;
326 }
327
328 while (Length > 0)
329 {
330 ReadLength = (ULONG)min(DataRunLength * Volume->ClusterSize, Length);
331 if (DataRunStartLCN == -1)
332 RtlZeroMemory(Buffer, ReadLength);
333 else if (!NtfsDiskRead(Volume, DataRunStartLCN * Volume->ClusterSize, ReadLength, Buffer))
334 break;
335
336 Length -= ReadLength;
337 Buffer += ReadLength;
338 AlreadyRead += ReadLength;
339
340 /* We finished this request, but there still data in this data run. */
341 if (Length == 0 && ReadLength != DataRunLength * Volume->ClusterSize)
342 break;
343
344 /*
345 * Go to next run in the list.
346 */
347
348 if (*DataRun == 0)
349 break;
350 CurrentOffset += DataRunLength * Volume->ClusterSize;
351 DataRun = NtfsDecodeRun(DataRun, &DataRunOffset, &DataRunLength);
352 if (DataRunOffset != -1)
353 {
354 /* Normal data run. */
355 DataRunStartLCN = LastLCN + DataRunOffset;
356 LastLCN = DataRunStartLCN;
357 }
358 else
359 {
360 /* Sparse data run. */
361 DataRunStartLCN = -1;
362 }
363 } /* while */
364
365 } /* if Disk */
366
367 Context->CacheRun = DataRun;
368 Context->CacheRunOffset = Offset + AlreadyRead;
369 Context->CacheRunStartLCN = DataRunStartLCN;
370 Context->CacheRunLength = DataRunLength;
371 Context->CacheRunLastLCN = LastLCN;
372 Context->CacheRunCurrentOffset = CurrentOffset;
373
374 return AlreadyRead;
375 }
376
377 static PNTFS_ATTR_CONTEXT NtfsFindAttributeHelper(PNTFS_VOLUME_INFO Volume, PNTFS_ATTR_RECORD AttrRecord, PNTFS_ATTR_RECORD AttrRecordEnd, ULONG Type, const WCHAR *Name, ULONG NameLength)
378 {
379 while (AttrRecord < AttrRecordEnd)
380 {
381 if (AttrRecord->Type == NTFS_ATTR_TYPE_END)
382 break;
383
384 if (AttrRecord->Type == NTFS_ATTR_TYPE_ATTRIBUTE_LIST)
385 {
386 PNTFS_ATTR_CONTEXT Context;
387 PNTFS_ATTR_CONTEXT ListContext;
388 PVOID ListBuffer;
389 ULONGLONG ListSize;
390 PNTFS_ATTR_RECORD ListAttrRecord;
391 PNTFS_ATTR_RECORD ListAttrRecordEnd;
392
393 ListContext = NtfsPrepareAttributeContext(AttrRecord);
394
395 ListSize = NtfsGetAttributeSize(&ListContext->Record);
396 if(ListSize <= 0xFFFFFFFF)
397 ListBuffer = FrLdrTempAlloc((ULONG)ListSize, TAG_NTFS_LIST);
398 else
399 ListBuffer = NULL;
400
401 if(!ListBuffer)
402 {
403 TRACE("Failed to allocate memory: %x\n", (ULONG)ListSize);
404 continue;
405 }
406
407 ListAttrRecord = (PNTFS_ATTR_RECORD)ListBuffer;
408 ListAttrRecordEnd = (PNTFS_ATTR_RECORD)((PCHAR)ListBuffer + ListSize);
409
410 if (NtfsReadAttribute(Volume, ListContext, 0, ListBuffer, (ULONG)ListSize) == ListSize)
411 {
412 Context = NtfsFindAttributeHelper(Volume, ListAttrRecord, ListAttrRecordEnd,
413 Type, Name, NameLength);
414
415 NtfsReleaseAttributeContext(ListContext);
416 FrLdrTempFree(ListBuffer, TAG_NTFS_LIST);
417
418 if (Context != NULL)
419 return Context;
420 }
421 }
422
423 if (AttrRecord->Type == Type)
424 {
425 if (AttrRecord->NameLength == NameLength)
426 {
427 PWCHAR AttrName;
428
429 AttrName = (PWCHAR)((PCHAR)AttrRecord + AttrRecord->NameOffset);
430 if (RtlEqualMemory(AttrName, Name, NameLength << 1))
431 {
432 /* Found it, fill up the context and return. */
433 return NtfsPrepareAttributeContext(AttrRecord);
434 }
435 }
436 }
437
438 if (AttrRecord->Length == 0)
439 return NULL;
440 AttrRecord = (PNTFS_ATTR_RECORD)((PCHAR)AttrRecord + AttrRecord->Length);
441 }
442
443 return NULL;
444 }
445
446 static PNTFS_ATTR_CONTEXT NtfsFindAttribute(PNTFS_VOLUME_INFO Volume, PNTFS_MFT_RECORD MftRecord, ULONG Type, const WCHAR *Name)
447 {
448 PNTFS_ATTR_RECORD AttrRecord;
449 PNTFS_ATTR_RECORD AttrRecordEnd;
450 ULONG NameLength;
451
452 AttrRecord = (PNTFS_ATTR_RECORD)((PCHAR)MftRecord + MftRecord->AttributesOffset);
453 AttrRecordEnd = (PNTFS_ATTR_RECORD)((PCHAR)MftRecord + Volume->MftRecordSize);
454 for (NameLength = 0; Name[NameLength] != 0; NameLength++)
455 ;
456
457 return NtfsFindAttributeHelper(Volume, AttrRecord, AttrRecordEnd, Type, Name, NameLength);
458 }
459
460 static BOOLEAN NtfsFixupRecord(PNTFS_VOLUME_INFO Volume, PNTFS_RECORD Record)
461 {
462 USHORT *USA;
463 USHORT USANumber;
464 USHORT USACount;
465 USHORT *Block;
466
467 USA = (USHORT*)((PCHAR)Record + Record->USAOffset);
468 USANumber = *(USA++);
469 USACount = Record->USACount - 1; /* Exclude the USA Number. */
470 Block = (USHORT*)((PCHAR)Record + Volume->BootSector.BytesPerSector - 2);
471
472 while (USACount)
473 {
474 if (*Block != USANumber)
475 return FALSE;
476 *Block = *(USA++);
477 Block = (USHORT*)((PCHAR)Block + Volume->BootSector.BytesPerSector);
478 USACount--;
479 }
480
481 return TRUE;
482 }
483
484 static BOOLEAN NtfsReadMftRecord(PNTFS_VOLUME_INFO Volume, ULONGLONG MFTIndex, PNTFS_MFT_RECORD Buffer)
485 {
486 ULONGLONG BytesRead;
487
488 BytesRead = NtfsReadAttribute(Volume, Volume->MFTContext, MFTIndex * Volume->MftRecordSize, (PCHAR)Buffer, Volume->MftRecordSize);
489 if (BytesRead != Volume->MftRecordSize)
490 return FALSE;
491
492 /* Apply update sequence array fixups. */
493 return NtfsFixupRecord(Volume, (PNTFS_RECORD)Buffer);
494 }
495
496 #if DBG
497 VOID NtfsPrintFile(PNTFS_INDEX_ENTRY IndexEntry)
498 {
499 PWCHAR FileName;
500 UCHAR FileNameLength;
501 CHAR AnsiFileName[256];
502 UCHAR i;
503
504 FileName = IndexEntry->FileName.FileName;
505 FileNameLength = min(IndexEntry->FileName.FileNameLength, 255);
506
507 for (i = 0; i < FileNameLength; i++)
508 AnsiFileName[i] = (CHAR)FileName[i];
509 AnsiFileName[i] = 0;
510
511 TRACE("- %s (%x)\n", AnsiFileName, IndexEntry->Data.Directory.IndexedFile);
512 }
513 #endif
514
515 static BOOLEAN NtfsCompareFileName(PCHAR FileName, PNTFS_INDEX_ENTRY IndexEntry)
516 {
517 PWCHAR EntryFileName;
518 UCHAR EntryFileNameLength;
519 UCHAR i;
520
521 EntryFileName = IndexEntry->FileName.FileName;
522 EntryFileNameLength = IndexEntry->FileName.FileNameLength;
523
524 #if DBG
525 NtfsPrintFile(IndexEntry);
526 #endif
527
528 if (strlen(FileName) != EntryFileNameLength)
529 return FALSE;
530
531 /* Do case-sensitive compares for Posix file names. */
532 if (IndexEntry->FileName.FileNameType == NTFS_FILE_NAME_POSIX)
533 {
534 for (i = 0; i < EntryFileNameLength; i++)
535 if (EntryFileName[i] != FileName[i])
536 return FALSE;
537 }
538 else
539 {
540 for (i = 0; i < EntryFileNameLength; i++)
541 if (tolower(EntryFileName[i]) != tolower(FileName[i]))
542 return FALSE;
543 }
544
545 return TRUE;
546 }
547
548 static BOOLEAN NtfsFindMftRecord(PNTFS_VOLUME_INFO Volume, ULONGLONG MFTIndex, PCHAR FileName, ULONGLONG *OutMFTIndex)
549 {
550 PNTFS_MFT_RECORD MftRecord;
551 //ULONG Magic;
552 PNTFS_ATTR_CONTEXT IndexRootCtx;
553 PNTFS_ATTR_CONTEXT IndexBitmapCtx;
554 PNTFS_ATTR_CONTEXT IndexAllocationCtx;
555 PNTFS_INDEX_ROOT IndexRoot;
556 ULONGLONG BitmapDataSize;
557 ULONGLONG IndexAllocationSize;
558 PCHAR BitmapData;
559 PCHAR IndexRecord;
560 PNTFS_INDEX_ENTRY IndexEntry, IndexEntryEnd;
561 ULONG RecordOffset;
562 ULONG IndexBlockSize;
563
564 MftRecord = FrLdrTempAlloc(Volume->MftRecordSize, TAG_NTFS_MFT);
565 if (MftRecord == NULL)
566 {
567 return FALSE;
568 }
569
570 if (NtfsReadMftRecord(Volume, MFTIndex, MftRecord))
571 {
572 //Magic = MftRecord->Magic;
573
574 IndexRootCtx = NtfsFindAttribute(Volume, MftRecord, NTFS_ATTR_TYPE_INDEX_ROOT, L"$I30");
575 if (IndexRootCtx == NULL)
576 {
577 FrLdrTempFree(MftRecord, TAG_NTFS_MFT);
578 return FALSE;
579 }
580
581 IndexRecord = FrLdrTempAlloc(Volume->IndexRecordSize, TAG_NTFS_INDEX_REC);
582 if (IndexRecord == NULL)
583 {
584 FrLdrTempFree(MftRecord, TAG_NTFS_MFT);
585 return FALSE;
586 }
587
588 NtfsReadAttribute(Volume, IndexRootCtx, 0, IndexRecord, Volume->IndexRecordSize);
589 IndexRoot = (PNTFS_INDEX_ROOT)IndexRecord;
590 IndexEntry = (PNTFS_INDEX_ENTRY)((PCHAR)&IndexRoot->IndexHeader + IndexRoot->IndexHeader.EntriesOffset);
591 /* Index root is always resident. */
592 IndexEntryEnd = (PNTFS_INDEX_ENTRY)(IndexRecord + IndexRootCtx->Record.Resident.ValueLength);
593 NtfsReleaseAttributeContext(IndexRootCtx);
594
595 TRACE("IndexRecordSize: %x IndexBlockSize: %x\n", Volume->IndexRecordSize, IndexRoot->IndexBlockSize);
596
597 while (IndexEntry < IndexEntryEnd &&
598 !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
599 {
600 if (NtfsCompareFileName(FileName, IndexEntry))
601 {
602 *OutMFTIndex = IndexEntry->Data.Directory.IndexedFile;
603 FrLdrTempFree(IndexRecord, TAG_NTFS_INDEX_REC);
604 FrLdrTempFree(MftRecord, TAG_NTFS_MFT);
605 return TRUE;
606 }
607 IndexEntry = (PNTFS_INDEX_ENTRY)((PCHAR)IndexEntry + IndexEntry->Length);
608 }
609
610 if (IndexRoot->IndexHeader.Flags & NTFS_LARGE_INDEX)
611 {
612 TRACE("Large Index!\n");
613
614 IndexBlockSize = IndexRoot->IndexBlockSize;
615
616 IndexBitmapCtx = NtfsFindAttribute(Volume, MftRecord, NTFS_ATTR_TYPE_BITMAP, L"$I30");
617 if (IndexBitmapCtx == NULL)
618 {
619 TRACE("Corrupted filesystem!\n");
620 FrLdrTempFree(MftRecord, TAG_NTFS_MFT);
621 return FALSE;
622 }
623 BitmapDataSize = NtfsGetAttributeSize(&IndexBitmapCtx->Record);
624 TRACE("BitmapDataSize: %x\n", (ULONG)BitmapDataSize);
625 if(BitmapDataSize <= 0xFFFFFFFF)
626 BitmapData = FrLdrTempAlloc((ULONG)BitmapDataSize, TAG_NTFS_BITMAP);
627 else
628 BitmapData = NULL;
629
630 if (BitmapData == NULL)
631 {
632 FrLdrTempFree(IndexRecord, TAG_NTFS_INDEX_REC);
633 FrLdrTempFree(MftRecord, TAG_NTFS_MFT);
634 return FALSE;
635 }
636 NtfsReadAttribute(Volume, IndexBitmapCtx, 0, BitmapData, (ULONG)BitmapDataSize);
637 NtfsReleaseAttributeContext(IndexBitmapCtx);
638
639 IndexAllocationCtx = NtfsFindAttribute(Volume, MftRecord, NTFS_ATTR_TYPE_INDEX_ALLOCATION, L"$I30");
640 if (IndexAllocationCtx == NULL)
641 {
642 TRACE("Corrupted filesystem!\n");
643 FrLdrTempFree(BitmapData, TAG_NTFS_BITMAP);
644 FrLdrTempFree(IndexRecord, TAG_NTFS_INDEX_REC);
645 FrLdrTempFree(MftRecord, TAG_NTFS_MFT);
646 return FALSE;
647 }
648 IndexAllocationSize = NtfsGetAttributeSize(&IndexAllocationCtx->Record);
649
650 RecordOffset = 0;
651
652 for (;;)
653 {
654 TRACE("RecordOffset: %x IndexAllocationSize: %x\n", RecordOffset, IndexAllocationSize);
655 for (; RecordOffset < IndexAllocationSize;)
656 {
657 UCHAR Bit = 1 << ((RecordOffset / IndexBlockSize) & 7);
658 ULONG Byte = (RecordOffset / IndexBlockSize) >> 3;
659 if ((BitmapData[Byte] & Bit))
660 break;
661 RecordOffset += IndexBlockSize;
662 }
663
664 if (RecordOffset >= IndexAllocationSize)
665 {
666 break;
667 }
668
669 NtfsReadAttribute(Volume, IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize);
670
671 if (!NtfsFixupRecord(Volume, (PNTFS_RECORD)IndexRecord))
672 {
673 break;
674 }
675
676 /* FIXME */
677 IndexEntry = (PNTFS_INDEX_ENTRY)(IndexRecord + 0x18 + *(USHORT *)(IndexRecord + 0x18));
678 IndexEntryEnd = (PNTFS_INDEX_ENTRY)(IndexRecord + IndexBlockSize);
679
680 while (IndexEntry < IndexEntryEnd &&
681 !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
682 {
683 if (NtfsCompareFileName(FileName, IndexEntry))
684 {
685 TRACE("File found\n");
686 *OutMFTIndex = IndexEntry->Data.Directory.IndexedFile;
687 FrLdrTempFree(BitmapData, TAG_NTFS_BITMAP);
688 FrLdrTempFree(IndexRecord, TAG_NTFS_INDEX_REC);
689 FrLdrTempFree(MftRecord, TAG_NTFS_MFT);
690 NtfsReleaseAttributeContext(IndexAllocationCtx);
691 return TRUE;
692 }
693 IndexEntry = (PNTFS_INDEX_ENTRY)((PCHAR)IndexEntry + IndexEntry->Length);
694 }
695
696 RecordOffset += IndexBlockSize;
697 }
698
699 NtfsReleaseAttributeContext(IndexAllocationCtx);
700 FrLdrTempFree(BitmapData, TAG_NTFS_BITMAP);
701 }
702
703 FrLdrTempFree(IndexRecord, TAG_NTFS_INDEX_REC);
704 }
705 else
706 {
707 TRACE("Can't read MFT record\n");
708 }
709 FrLdrTempFree(MftRecord, TAG_NTFS_MFT);
710
711 return FALSE;
712 }
713
714 static BOOLEAN NtfsLookupFile(PNTFS_VOLUME_INFO Volume, PCSTR FileName, PNTFS_MFT_RECORD MftRecord, PNTFS_ATTR_CONTEXT *DataContext)
715 {
716 ULONG NumberOfPathParts;
717 CHAR PathPart[261];
718 ULONGLONG CurrentMFTIndex;
719 UCHAR i;
720
721 TRACE("NtfsLookupFile() FileName = %s\n", FileName);
722
723 CurrentMFTIndex = NTFS_FILE_ROOT;
724 NumberOfPathParts = FsGetNumPathParts(FileName);
725 for (i = 0; i < NumberOfPathParts; i++)
726 {
727 FsGetFirstNameFromPath(PathPart, FileName);
728
729 for (; (*FileName != '\\') && (*FileName != '/') && (*FileName != '\0'); FileName++)
730 ;
731 FileName++;
732
733 TRACE("- Lookup: %s\n", PathPart);
734 if (!NtfsFindMftRecord(Volume, CurrentMFTIndex, PathPart, &CurrentMFTIndex))
735 {
736 TRACE("- Failed\n");
737 return FALSE;
738 }
739 TRACE("- Lookup: %x\n", CurrentMFTIndex);
740 }
741
742 if (!NtfsReadMftRecord(Volume, CurrentMFTIndex, MftRecord))
743 {
744 TRACE("NtfsLookupFile: Can't read MFT record\n");
745 return FALSE;
746 }
747
748 *DataContext = NtfsFindAttribute(Volume, MftRecord, NTFS_ATTR_TYPE_DATA, L"");
749 if (*DataContext == NULL)
750 {
751 TRACE("NtfsLookupFile: Can't find data attribute\n");
752 return FALSE;
753 }
754
755 return TRUE;
756 }
757
758 ARC_STATUS NtfsClose(ULONG FileId)
759 {
760 PNTFS_FILE_HANDLE FileHandle = FsGetDeviceSpecific(FileId);
761
762 NtfsReleaseAttributeContext(FileHandle->DataContext);
763 FrLdrTempFree(FileHandle, TAG_NTFS_FILE);
764
765 return ESUCCESS;
766 }
767
768 ARC_STATUS NtfsGetFileInformation(ULONG FileId, FILEINFORMATION* Information)
769 {
770 PNTFS_FILE_HANDLE FileHandle = FsGetDeviceSpecific(FileId);
771
772 RtlZeroMemory(Information, sizeof(FILEINFORMATION));
773 Information->EndingAddress.QuadPart = NtfsGetAttributeSize(&FileHandle->DataContext->Record);
774 Information->CurrentAddress.QuadPart = FileHandle->Offset;
775
776 TRACE("NtfsGetFileInformation() FileSize = %d\n",
777 Information->EndingAddress.LowPart);
778 TRACE("NtfsGetFileInformation() FilePointer = %d\n",
779 Information->CurrentAddress.LowPart);
780
781 return ESUCCESS;
782 }
783
784 ARC_STATUS NtfsOpen(CHAR* Path, OPENMODE OpenMode, ULONG* FileId)
785 {
786 PNTFS_VOLUME_INFO Volume;
787 PNTFS_FILE_HANDLE FileHandle;
788 PNTFS_MFT_RECORD MftRecord;
789 ULONG DeviceId;
790
791 //
792 // Check parameters
793 //
794 if (OpenMode != OpenReadOnly)
795 return EACCES;
796
797 //
798 // Get underlying device
799 //
800 DeviceId = FsGetDeviceId(*FileId);
801 Volume = NtfsVolumes[DeviceId];
802
803 TRACE("NtfsOpen() FileName = %s\n", Path);
804
805 //
806 // Allocate file structure
807 //
808 FileHandle = FrLdrTempAlloc(sizeof(NTFS_FILE_HANDLE) + Volume->MftRecordSize,
809 TAG_NTFS_FILE);
810 if (!FileHandle)
811 {
812 return ENOMEM;
813 }
814 RtlZeroMemory(FileHandle, sizeof(NTFS_FILE_HANDLE) + Volume->MftRecordSize);
815 FileHandle->Volume = Volume;
816
817 //
818 // Search file entry
819 //
820 MftRecord = (PNTFS_MFT_RECORD)(FileHandle + 1);
821 if (!NtfsLookupFile(Volume, Path, MftRecord, &FileHandle->DataContext))
822 {
823 FrLdrTempFree(FileHandle, TAG_NTFS_FILE);
824 return ENOENT;
825 }
826
827 FsSetDeviceSpecific(*FileId, FileHandle);
828 return ESUCCESS;
829 }
830
831 ARC_STATUS NtfsRead(ULONG FileId, VOID* Buffer, ULONG N, ULONG* Count)
832 {
833 PNTFS_FILE_HANDLE FileHandle = FsGetDeviceSpecific(FileId);
834 ULONGLONG BytesRead64;
835
836 //
837 // Read file
838 //
839 BytesRead64 = NtfsReadAttribute(FileHandle->Volume, FileHandle->DataContext, FileHandle->Offset, Buffer, N);
840 *Count = (ULONG)BytesRead64;
841
842 //
843 // Check for success
844 //
845 if (BytesRead64 > 0)
846 return ESUCCESS;
847 else
848 return EIO;
849 }
850
851 ARC_STATUS NtfsSeek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode)
852 {
853 PNTFS_FILE_HANDLE FileHandle = FsGetDeviceSpecific(FileId);
854
855 TRACE("NtfsSeek() NewFilePointer = %lu\n", Position->LowPart);
856
857 if (SeekMode != SeekAbsolute)
858 return EINVAL;
859 if (Position->HighPart != 0)
860 return EINVAL;
861 if (Position->LowPart >= (ULONG)NtfsGetAttributeSize(&FileHandle->DataContext->Record))
862 return EINVAL;
863
864 FileHandle->Offset = Position->LowPart;
865 return ESUCCESS;
866 }
867
868 const DEVVTBL NtfsFuncTable =
869 {
870 NtfsClose,
871 NtfsGetFileInformation,
872 NtfsOpen,
873 NtfsRead,
874 NtfsSeek,
875 L"ntfs",
876 };
877
878 const DEVVTBL* NtfsMount(ULONG DeviceId)
879 {
880 PNTFS_VOLUME_INFO Volume;
881 LARGE_INTEGER Position;
882 ULONG Count;
883 ARC_STATUS Status;
884
885 //
886 // Allocate data for volume information
887 //
888 Volume = FrLdrTempAlloc(sizeof(NTFS_VOLUME_INFO), TAG_NTFS_VOLUME);
889 if (!Volume)
890 return NULL;
891 RtlZeroMemory(Volume, sizeof(NTFS_VOLUME_INFO));
892
893 //
894 // Read the BootSector
895 //
896 Position.HighPart = 0;
897 Position.LowPart = 0;
898 Status = ArcSeek(DeviceId, &Position, SeekAbsolute);
899 if (Status != ESUCCESS)
900 {
901 FrLdrTempFree(Volume, TAG_NTFS_VOLUME);
902 return NULL;
903 }
904 Status = ArcRead(DeviceId, &Volume->BootSector, sizeof(Volume->BootSector), &Count);
905 if (Status != ESUCCESS || Count != sizeof(Volume->BootSector))
906 {
907 FrLdrTempFree(Volume, TAG_NTFS_VOLUME);
908 return NULL;
909 }
910
911 //
912 // Check if BootSector is valid. If no, return early
913 //
914 if (!RtlEqualMemory(Volume->BootSector.SystemId, "NTFS", 4))
915 {
916 FrLdrTempFree(Volume, TAG_NTFS_VOLUME);
917 return NULL;
918 }
919
920 //
921 // Calculate cluster size and MFT record size
922 //
923 Volume->ClusterSize = Volume->BootSector.SectorsPerCluster * Volume->BootSector.BytesPerSector;
924 if (Volume->BootSector.ClustersPerMftRecord > 0)
925 Volume->MftRecordSize = Volume->BootSector.ClustersPerMftRecord * Volume->ClusterSize;
926 else
927 Volume->MftRecordSize = 1 << (-Volume->BootSector.ClustersPerMftRecord);
928 if (Volume->BootSector.ClustersPerIndexRecord > 0)
929 Volume->IndexRecordSize = Volume->BootSector.ClustersPerIndexRecord * Volume->ClusterSize;
930 else
931 Volume->IndexRecordSize = 1 << (-Volume->BootSector.ClustersPerIndexRecord);
932
933 TRACE("ClusterSize: 0x%x\n", Volume->ClusterSize);
934 TRACE("ClustersPerMftRecord: %d\n", Volume->BootSector.ClustersPerMftRecord);
935 TRACE("ClustersPerIndexRecord: %d\n", Volume->BootSector.ClustersPerIndexRecord);
936 TRACE("MftRecordSize: 0x%x\n", Volume->MftRecordSize);
937 TRACE("IndexRecordSize: 0x%x\n", Volume->IndexRecordSize);
938
939 //
940 // Read MFT index
941 //
942 TRACE("Reading MFT index...\n");
943 Volume->MasterFileTable = FrLdrTempAlloc(Volume->MftRecordSize, TAG_NTFS_MFT);
944 if (!Volume->MasterFileTable)
945 {
946 FrLdrTempFree(Volume, TAG_NTFS_VOLUME);
947 return NULL;
948 }
949 Position.QuadPart = Volume->BootSector.MftLocation * Volume->ClusterSize;
950 Status = ArcSeek(DeviceId, &Position, SeekAbsolute);
951 if (Status != ESUCCESS)
952 {
953 FileSystemError("Failed to seek to Master File Table record.");
954 FrLdrTempFree(Volume->MasterFileTable, TAG_NTFS_MFT);
955 FrLdrTempFree(Volume, TAG_NTFS_VOLUME);
956 return NULL;
957 }
958 Status = ArcRead(DeviceId, Volume->MasterFileTable, Volume->MftRecordSize, &Count);
959 if (Status != ESUCCESS || Count != Volume->MftRecordSize)
960 {
961 FileSystemError("Failed to read the Master File Table record.");
962 FrLdrTempFree(Volume->MasterFileTable, TAG_NTFS_MFT);
963 FrLdrTempFree(Volume, TAG_NTFS_VOLUME);
964 return NULL;
965 }
966
967 //
968 // Keep room to read partial sectors
969 //
970 Volume->TemporarySector = FrLdrTempAlloc(Volume->BootSector.BytesPerSector, TAG_NTFS_DATA);
971 if (!Volume->TemporarySector)
972 {
973 FileSystemError("Failed to allocate memory.");
974 FrLdrTempFree(Volume->MasterFileTable, TAG_NTFS_MFT);
975 FrLdrTempFree(Volume, TAG_NTFS_VOLUME);
976 return NULL;
977 }
978
979 //
980 // Keep device id
981 //
982 Volume->DeviceId = DeviceId;
983
984 //
985 // Search DATA attribute
986 //
987 TRACE("Searching for DATA attribute...\n");
988 Volume->MFTContext = NtfsFindAttribute(Volume, Volume->MasterFileTable, NTFS_ATTR_TYPE_DATA, L"");
989 if (!Volume->MFTContext)
990 {
991 FileSystemError("Can't find data attribute for Master File Table.");
992 FrLdrTempFree(Volume->MasterFileTable, TAG_NTFS_MFT);
993 FrLdrTempFree(Volume, TAG_NTFS_VOLUME);
994 return NULL;
995 }
996
997 //
998 // Remember NTFS volume information
999 //
1000 NtfsVolumes[DeviceId] = Volume;
1001
1002 //
1003 // Return success
1004 //
1005 return &NtfsFuncTable;
1006 }
1007
1008 #endif