ReactOS 0.4.16-dev-1311-g81a4d83
ntfs.c
Go to the documentation of this file.
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
30#include <debug.h>
32
33#define TAG_NTFS_CONTEXT 'CftN'
34#define TAG_NTFS_LIST 'LftN'
35#define TAG_NTFS_MFT 'MftN'
36#define TAG_NTFS_INDEX_REC 'IftN'
37#define TAG_NTFS_BITMAP 'BftN'
38#define TAG_NTFS_FILE 'FftN'
39#define TAG_NTFS_VOLUME 'VftN'
40#define TAG_NTFS_DATA 'DftN'
41
42typedef struct _NTFS_VOLUME_INFO
43{
49 /* FIXME: MFTContext is never freed. */
54
56
58{
59 if (AttrRecord->IsNonResident)
60 return AttrRecord->NonResident.DataSize;
61 else
62 return AttrRecord->Resident.ValueLength;
63}
64
65static 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
107{
109
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
141{
143}
144
146{
148 ULONG Count;
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 //
172 &Volume->TemporarySector[Offset % Volume->BootSector.BytesPerSector],
173 ReadLength);
174
175 //
176 // Move to unfilled buffer part
177 //
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 //
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
223{
224 ULONGLONG LastLCN;
225 PUCHAR DataRun;
226 LONGLONG DataRunOffset;
227 ULONGLONG DataRunLength;
228 LONGLONG DataRunStartLCN;
229 ULONGLONG CurrentOffset;
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)
306 if (DataRunStartLCN == -1 || NtfsDiskRead(Volume, DataRunStartLCN * Volume->ClusterSize + Offset - CurrentOffset, ReadLength, Buffer))
307 {
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 (DataRunOffset != (ULONGLONG)-1)
317 {
318 DataRunStartLCN = LastLCN + DataRunOffset;
319 LastLCN = DataRunStartLCN;
320 }
321 else
322 DataRunStartLCN = -1;
323 }
324
325 while (Length > 0)
326 {
327 ReadLength = (ULONG)min(DataRunLength * Volume->ClusterSize, Length);
328 if (DataRunStartLCN == -1)
330 else if (!NtfsDiskRead(Volume, DataRunStartLCN * Volume->ClusterSize, ReadLength, Buffer))
331 break;
332
335 AlreadyRead += ReadLength;
336
337 /* We finished this request, but there still data in this data run. */
338 if (Length == 0 && ReadLength != DataRunLength * Volume->ClusterSize)
339 break;
340
341 /*
342 * Go to next run in the list.
343 */
344
345 if (*DataRun == 0)
346 break;
347 CurrentOffset += DataRunLength * Volume->ClusterSize;
348 DataRun = NtfsDecodeRun(DataRun, &DataRunOffset, &DataRunLength);
349 if (DataRunOffset != -1)
350 {
351 /* Normal data run. */
352 DataRunStartLCN = LastLCN + DataRunOffset;
353 LastLCN = DataRunStartLCN;
354 }
355 else
356 {
357 /* Sparse data run. */
358 DataRunStartLCN = -1;
359 }
360 } /* while */
361
362 } /* if Disk */
363
364 Context->CacheRun = DataRun;
365 Context->CacheRunOffset = Offset + AlreadyRead;
366 Context->CacheRunStartLCN = DataRunStartLCN;
367 Context->CacheRunLength = DataRunLength;
368 Context->CacheRunLastLCN = LastLCN;
369 Context->CacheRunCurrentOffset = CurrentOffset;
370
371 return AlreadyRead;
372}
373
375{
376 while (AttrRecord < AttrRecordEnd)
377 {
378 if (AttrRecord->Type == NTFS_ATTR_TYPE_END)
379 break;
380
381 if (AttrRecord->Type == NTFS_ATTR_TYPE_ATTRIBUTE_LIST)
382 {
384 PNTFS_ATTR_CONTEXT ListContext;
385 PVOID ListBuffer;
386 ULONGLONG ListSize;
387 PNTFS_ATTR_RECORD ListAttrRecord;
388 PNTFS_ATTR_RECORD ListAttrRecordEnd;
389
390 ListContext = NtfsPrepareAttributeContext(AttrRecord);
391
392 ListSize = NtfsGetAttributeSize(&ListContext->Record);
393 if(ListSize <= 0xFFFFFFFF)
394 ListBuffer = FrLdrTempAlloc((ULONG)ListSize, TAG_NTFS_LIST);
395 else
396 ListBuffer = NULL;
397
398 if(!ListBuffer)
399 {
400 TRACE("Failed to allocate memory: %x\n", (ULONG)ListSize);
401 continue;
402 }
403
404 ListAttrRecord = (PNTFS_ATTR_RECORD)ListBuffer;
405 ListAttrRecordEnd = (PNTFS_ATTR_RECORD)((PCHAR)ListBuffer + ListSize);
406
407 if (NtfsReadAttribute(Volume, ListContext, 0, ListBuffer, (ULONG)ListSize) == ListSize)
408 {
409 Context = NtfsFindAttributeHelper(Volume, ListAttrRecord, ListAttrRecordEnd,
410 Type, Name, NameLength);
411
412 NtfsReleaseAttributeContext(ListContext);
413 FrLdrTempFree(ListBuffer, TAG_NTFS_LIST);
414
415 if (Context != NULL)
416 return Context;
417 }
418 }
419
420 if (AttrRecord->Type == Type)
421 {
422 if (AttrRecord->NameLength == NameLength)
423 {
424 PWCHAR AttrName;
425
426 AttrName = (PWCHAR)((PCHAR)AttrRecord + AttrRecord->NameOffset);
427 if (RtlEqualMemory(AttrName, Name, NameLength << 1))
428 {
429 /* Found it, fill up the context and return. */
430 return NtfsPrepareAttributeContext(AttrRecord);
431 }
432 }
433 }
434
435 if (AttrRecord->Length == 0)
436 return NULL;
437 AttrRecord = (PNTFS_ATTR_RECORD)((PCHAR)AttrRecord + AttrRecord->Length);
438 }
439
440 return NULL;
441}
442
444{
445 PNTFS_ATTR_RECORD AttrRecord;
446 PNTFS_ATTR_RECORD AttrRecordEnd;
447 ULONG NameLength;
448
449 AttrRecord = (PNTFS_ATTR_RECORD)((PCHAR)MftRecord + MftRecord->AttributesOffset);
450 AttrRecordEnd = (PNTFS_ATTR_RECORD)((PCHAR)MftRecord + Volume->MftRecordSize);
451 for (NameLength = 0; Name[NameLength] != 0; NameLength++)
452 ;
453
454 return NtfsFindAttributeHelper(Volume, AttrRecord, AttrRecordEnd, Type, Name, NameLength);
455}
456
458{
459 USHORT *USA;
460 USHORT USANumber;
461 USHORT USACount;
462 USHORT *Block;
463
464 USA = (USHORT*)((PCHAR)Record + Record->USAOffset);
465 USANumber = *(USA++);
466 USACount = Record->USACount - 1; /* Exclude the USA Number. */
467 Block = (USHORT*)((PCHAR)Record + Volume->BootSector.BytesPerSector - 2);
468
469 while (USACount)
470 {
471 if (*Block != USANumber)
472 return FALSE;
473 *Block = *(USA++);
474 Block = (USHORT*)((PCHAR)Block + Volume->BootSector.BytesPerSector);
475 USACount--;
476 }
477
478 return TRUE;
479}
480
482{
484
485 BytesRead = NtfsReadAttribute(Volume, Volume->MFTContext, MFTIndex * Volume->MftRecordSize, (PCHAR)Buffer, Volume->MftRecordSize);
486 if (BytesRead != Volume->MftRecordSize)
487 return FALSE;
488
489 /* Apply update sequence array fixups. */
491}
492
493#if DBG
494VOID NtfsPrintFile(PNTFS_INDEX_ENTRY IndexEntry)
495{
498 CHAR AnsiFileName[256];
499 UCHAR i;
500
501 FileName = IndexEntry->FileName.FileName;
502 FileNameLength = min(IndexEntry->FileName.FileNameLength, 255);
503
504 for (i = 0; i < FileNameLength; i++)
505 AnsiFileName[i] = (CHAR)FileName[i];
506 AnsiFileName[i] = 0;
507
508 TRACE("- %s (%x)\n", AnsiFileName, (IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK));
509}
510#endif
511
513{
514 PWCHAR EntryFileName;
515 UCHAR EntryFileNameLength;
516 UCHAR i;
517
518 EntryFileName = IndexEntry->FileName.FileName;
519 EntryFileNameLength = IndexEntry->FileName.FileNameLength;
520
521#if DBG
522 NtfsPrintFile(IndexEntry);
523#endif
524
525 if (strlen(FileName) != EntryFileNameLength)
526 return FALSE;
527
528 /*
529 * Always perform case-insensitive comparison for file names.
530 * This is necessary, because when modifying e.g. on Linux a Windows NTFS
531 * partition formatted with Windows itself, the NTLDR/BOOTMGR will boot
532 * normally ignoring the case of the paths.
533 */
534 for (i = 0; i < EntryFileNameLength; i++)
535 {
536 if (tolower(EntryFileName[i]) != tolower(FileName[i]))
537 return FALSE;
538 }
539
540 return TRUE;
541}
542
544{
545 PNTFS_MFT_RECORD MftRecord;
546 //ULONG Magic;
547 PNTFS_ATTR_CONTEXT IndexRootCtx;
548 PNTFS_ATTR_CONTEXT IndexBitmapCtx;
549 PNTFS_ATTR_CONTEXT IndexAllocationCtx;
550 PNTFS_INDEX_ROOT IndexRoot;
551 ULONGLONG BitmapDataSize;
552 ULONGLONG IndexAllocationSize;
554 PCHAR IndexRecord;
555 PNTFS_INDEX_ENTRY IndexEntry, IndexEntryEnd;
556 ULONG RecordOffset;
557 ULONG IndexBlockSize;
558
559 MftRecord = FrLdrTempAlloc(Volume->MftRecordSize, TAG_NTFS_MFT);
560 if (MftRecord == NULL)
561 {
562 return FALSE;
563 }
564
565 if (NtfsReadMftRecord(Volume, MFTIndex, MftRecord))
566 {
567 //Magic = MftRecord->Magic;
568
569 IndexRootCtx = NtfsFindAttribute(Volume, MftRecord, NTFS_ATTR_TYPE_INDEX_ROOT, L"$I30");
570 if (IndexRootCtx == NULL)
571 {
572 FrLdrTempFree(MftRecord, TAG_NTFS_MFT);
573 return FALSE;
574 }
575
576 IndexRecord = FrLdrTempAlloc(Volume->IndexRecordSize, TAG_NTFS_INDEX_REC);
577 if (IndexRecord == NULL)
578 {
579 FrLdrTempFree(MftRecord, TAG_NTFS_MFT);
580 return FALSE;
581 }
582
583 NtfsReadAttribute(Volume, IndexRootCtx, 0, IndexRecord, Volume->IndexRecordSize);
584 IndexRoot = (PNTFS_INDEX_ROOT)IndexRecord;
585 IndexEntry = (PNTFS_INDEX_ENTRY)((PCHAR)&IndexRoot->IndexHeader + IndexRoot->IndexHeader.EntriesOffset);
586 /* Index root is always resident. */
587 IndexEntryEnd = (PNTFS_INDEX_ENTRY)(IndexRecord + IndexRootCtx->Record.Resident.ValueLength);
588 NtfsReleaseAttributeContext(IndexRootCtx);
589
590 TRACE("IndexRecordSize: %x IndexBlockSize: %x\n", Volume->IndexRecordSize, IndexRoot->IndexBlockSize);
591
592 while (IndexEntry < IndexEntryEnd &&
593 !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
594 {
595 if (NtfsCompareFileName(FileName, IndexEntry))
596 {
597 *OutMFTIndex = (IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK);
598 FrLdrTempFree(IndexRecord, TAG_NTFS_INDEX_REC);
599 FrLdrTempFree(MftRecord, TAG_NTFS_MFT);
600 return TRUE;
601 }
602 IndexEntry = (PNTFS_INDEX_ENTRY)((PCHAR)IndexEntry + IndexEntry->Length);
603 }
604
605 if (IndexRoot->IndexHeader.Flags & NTFS_LARGE_INDEX)
606 {
607 TRACE("Large Index!\n");
608
609 IndexBlockSize = IndexRoot->IndexBlockSize;
610
611 IndexBitmapCtx = NtfsFindAttribute(Volume, MftRecord, NTFS_ATTR_TYPE_BITMAP, L"$I30");
612 if (IndexBitmapCtx == NULL)
613 {
614 TRACE("Corrupted filesystem!\n");
615 FrLdrTempFree(MftRecord, TAG_NTFS_MFT);
616 return FALSE;
617 }
618 BitmapDataSize = NtfsGetAttributeSize(&IndexBitmapCtx->Record);
619 TRACE("BitmapDataSize: %x\n", (ULONG)BitmapDataSize);
620 if(BitmapDataSize <= 0xFFFFFFFF)
622 else
624
625 if (BitmapData == NULL)
626 {
627 FrLdrTempFree(IndexRecord, TAG_NTFS_INDEX_REC);
628 FrLdrTempFree(MftRecord, TAG_NTFS_MFT);
629 return FALSE;
630 }
631 NtfsReadAttribute(Volume, IndexBitmapCtx, 0, BitmapData, (ULONG)BitmapDataSize);
632 NtfsReleaseAttributeContext(IndexBitmapCtx);
633
634 IndexAllocationCtx = NtfsFindAttribute(Volume, MftRecord, NTFS_ATTR_TYPE_INDEX_ALLOCATION, L"$I30");
635 if (IndexAllocationCtx == NULL)
636 {
637 TRACE("Corrupted filesystem!\n");
639 FrLdrTempFree(IndexRecord, TAG_NTFS_INDEX_REC);
640 FrLdrTempFree(MftRecord, TAG_NTFS_MFT);
641 return FALSE;
642 }
643 IndexAllocationSize = NtfsGetAttributeSize(&IndexAllocationCtx->Record);
644
645 RecordOffset = 0;
646
647 for (;;)
648 {
649 TRACE("RecordOffset: %x IndexAllocationSize: %x\n", RecordOffset, IndexAllocationSize);
650 for (; RecordOffset < IndexAllocationSize;)
651 {
652 UCHAR Bit = 1 << ((RecordOffset / IndexBlockSize) & 7);
653 ULONG Byte = (RecordOffset / IndexBlockSize) >> 3;
654 if ((BitmapData[Byte] & Bit))
655 break;
656 RecordOffset += IndexBlockSize;
657 }
658
659 if (RecordOffset >= IndexAllocationSize)
660 {
661 break;
662 }
663
664 NtfsReadAttribute(Volume, IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize);
665
666 if (!NtfsFixupRecord(Volume, (PNTFS_RECORD)IndexRecord))
667 {
668 break;
669 }
670
671 /* FIXME */
672 IndexEntry = (PNTFS_INDEX_ENTRY)(IndexRecord + 0x18 + *(USHORT *)(IndexRecord + 0x18));
673 IndexEntryEnd = (PNTFS_INDEX_ENTRY)(IndexRecord + IndexBlockSize);
674
675 while (IndexEntry < IndexEntryEnd &&
676 !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
677 {
678 if (NtfsCompareFileName(FileName, IndexEntry))
679 {
680 TRACE("File found\n");
681 *OutMFTIndex = (IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK);
683 FrLdrTempFree(IndexRecord, TAG_NTFS_INDEX_REC);
684 FrLdrTempFree(MftRecord, TAG_NTFS_MFT);
685 NtfsReleaseAttributeContext(IndexAllocationCtx);
686 return TRUE;
687 }
688 IndexEntry = (PNTFS_INDEX_ENTRY)((PCHAR)IndexEntry + IndexEntry->Length);
689 }
690
691 RecordOffset += IndexBlockSize;
692 }
693
694 NtfsReleaseAttributeContext(IndexAllocationCtx);
696 }
697
698 FrLdrTempFree(IndexRecord, TAG_NTFS_INDEX_REC);
699 }
700 else
701 {
702 TRACE("Can't read MFT record\n");
703 }
704 FrLdrTempFree(MftRecord, TAG_NTFS_MFT);
705
706 return FALSE;
707}
708
710{
711 ULONG NumberOfPathParts;
712 CHAR PathPart[261];
713 ULONGLONG CurrentMFTIndex;
714 UCHAR i;
715
716 TRACE("NtfsLookupFile() FileName = %s\n", FileName);
717
718 CurrentMFTIndex = NTFS_FILE_ROOT;
719
720 /* Skip leading path separator, if any */
721 if (*FileName == '\\' || *FileName == '/')
722 ++FileName;
723
724 NumberOfPathParts = FsGetNumPathParts(FileName);
725 for (i = 0; i < NumberOfPathParts; i++)
726 {
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
759{
761
764
765 return ESUCCESS;
766}
767
769{
771
773 Information->EndingAddress.QuadPart = NtfsGetAttributeSize(&FileHandle->DataContext->Record);
774 Information->CurrentAddress.QuadPart = FileHandle->Offset;
775
776 TRACE("NtfsGetFileInformation(%lu) -> FileSize = %llu, FilePointer = 0x%llx\n",
777 FileId, Information->EndingAddress.QuadPart, Information->CurrentAddress.QuadPart);
778
779 return ESUCCESS;
780}
781
783{
786 PNTFS_MFT_RECORD MftRecord;
787 ULONG DeviceId;
788
789 //
790 // Check parameters
791 //
792 if (OpenMode != OpenReadOnly)
793 return EACCES;
794
795 //
796 // Get underlying device
797 //
798 DeviceId = FsGetDeviceId(*FileId);
799 Volume = NtfsVolumes[DeviceId];
800
801 TRACE("NtfsOpen() FileName = %s\n", Path);
802
803 //
804 // Allocate file structure
805 //
806 FileHandle = FrLdrTempAlloc(sizeof(NTFS_FILE_HANDLE) + Volume->MftRecordSize,
808 if (!FileHandle)
809 {
810 return ENOMEM;
811 }
812 RtlZeroMemory(FileHandle, sizeof(NTFS_FILE_HANDLE) + Volume->MftRecordSize);
814
815 //
816 // Search file entry
817 //
818 MftRecord = (PNTFS_MFT_RECORD)(FileHandle + 1);
819 if (!NtfsLookupFile(Volume, Path, MftRecord, &FileHandle->DataContext))
820 {
822 return ENOENT;
823 }
824
826 return ESUCCESS;
827}
828
830{
832 ULONGLONG BytesRead64;
833
834 //
835 // Read file
836 //
837 BytesRead64 = NtfsReadAttribute(FileHandle->Volume, FileHandle->DataContext, FileHandle->Offset, Buffer, N);
838 FileHandle->Offset += BytesRead64;
839 *Count = (ULONG)BytesRead64;
840
841 //
842 // Check for success
843 //
844 if (BytesRead64 > 0)
845 return ESUCCESS;
846 else
847 return EIO;
848}
849
851{
853 LARGE_INTEGER NewPosition = *Position;
854
855 switch (SeekMode)
856 {
857 case SeekAbsolute:
858 break;
859 case SeekRelative:
860 NewPosition.QuadPart += FileHandle->Offset;
861 break;
862 default:
863 ASSERT(FALSE);
864 return EINVAL;
865 }
866
867 if (NewPosition.QuadPart >= NtfsGetAttributeSize(&FileHandle->DataContext->Record))
868 return EINVAL;
869
870 FileHandle->Offset = NewPosition.QuadPart;
871 return ESUCCESS;
872}
873
875{
876 NtfsClose,
878 NtfsOpen,
879 NtfsRead,
880 NtfsSeek,
881 L"ntfs",
882};
883
884const DEVVTBL* NtfsMount(ULONG DeviceId)
885{
888 ULONG Count;
890
891 TRACE("Enter NtfsMount(%lu)\n", DeviceId);
892
893 //
894 // Allocate data for volume information
895 //
897 if (!Volume)
898 return NULL;
900
901 //
902 // Read the BootSector
903 //
904 Position.QuadPart = 0;
905 Status = ArcSeek(DeviceId, &Position, SeekAbsolute);
906 if (Status != ESUCCESS)
907 {
909 return NULL;
910 }
911 Status = ArcRead(DeviceId, &Volume->BootSector, sizeof(Volume->BootSector), &Count);
912 if (Status != ESUCCESS || Count != sizeof(Volume->BootSector))
913 {
915 return NULL;
916 }
917
918 //
919 // Check if BootSector is valid. If no, return early
920 //
921 if (!RtlEqualMemory(Volume->BootSector.SystemId, "NTFS", 4))
922 {
924 return NULL;
925 }
926
927 //
928 // Calculate cluster size and MFT record size
929 //
930 Volume->ClusterSize = Volume->BootSector.SectorsPerCluster * Volume->BootSector.BytesPerSector;
931 if (Volume->BootSector.ClustersPerMftRecord > 0)
932 Volume->MftRecordSize = Volume->BootSector.ClustersPerMftRecord * Volume->ClusterSize;
933 else
934 Volume->MftRecordSize = 1 << (-Volume->BootSector.ClustersPerMftRecord);
935 if (Volume->BootSector.ClustersPerIndexRecord > 0)
936 Volume->IndexRecordSize = Volume->BootSector.ClustersPerIndexRecord * Volume->ClusterSize;
937 else
938 Volume->IndexRecordSize = 1 << (-Volume->BootSector.ClustersPerIndexRecord);
939
940 TRACE("ClusterSize: 0x%x\n", Volume->ClusterSize);
941 TRACE("ClustersPerMftRecord: %d\n", Volume->BootSector.ClustersPerMftRecord);
942 TRACE("ClustersPerIndexRecord: %d\n", Volume->BootSector.ClustersPerIndexRecord);
943 TRACE("MftRecordSize: 0x%x\n", Volume->MftRecordSize);
944 TRACE("IndexRecordSize: 0x%x\n", Volume->IndexRecordSize);
945
946 //
947 // Read MFT index
948 //
949 TRACE("Reading MFT index...\n");
950 Volume->MasterFileTable = FrLdrTempAlloc(Volume->MftRecordSize, TAG_NTFS_MFT);
951 if (!Volume->MasterFileTable)
952 {
954 return NULL;
955 }
956 Position.QuadPart = Volume->BootSector.MftLocation * Volume->ClusterSize;
957 Status = ArcSeek(DeviceId, &Position, SeekAbsolute);
958 if (Status != ESUCCESS)
959 {
960 FileSystemError("Failed to seek to Master File Table record.");
961 FrLdrTempFree(Volume->MasterFileTable, TAG_NTFS_MFT);
963 return NULL;
964 }
965 Status = ArcRead(DeviceId, Volume->MasterFileTable, Volume->MftRecordSize, &Count);
966 if (Status != ESUCCESS || Count != Volume->MftRecordSize)
967 {
968 FileSystemError("Failed to read the Master File Table record.");
969 FrLdrTempFree(Volume->MasterFileTable, TAG_NTFS_MFT);
971 return NULL;
972 }
973
974 //
975 // Keep room to read partial sectors
976 //
977 Volume->TemporarySector = FrLdrTempAlloc(Volume->BootSector.BytesPerSector, TAG_NTFS_DATA);
978 if (!Volume->TemporarySector)
979 {
980 FileSystemError("Failed to allocate memory.");
981 FrLdrTempFree(Volume->MasterFileTable, TAG_NTFS_MFT);
983 return NULL;
984 }
985
986 //
987 // Keep device id
988 //
989 Volume->DeviceId = DeviceId;
990
991 //
992 // Search DATA attribute
993 //
994 TRACE("Searching for DATA attribute...\n");
995 Volume->MFTContext = NtfsFindAttribute(Volume, Volume->MasterFileTable, NTFS_ATTR_TYPE_DATA, L"");
996 if (!Volume->MFTContext)
997 {
998 FileSystemError("Can't find data attribute for Master File Table.");
999 FrLdrTempFree(Volume->MasterFileTable, TAG_NTFS_MFT);
1001 return NULL;
1002 }
1003
1004 //
1005 // Remember NTFS volume information
1006 //
1007 NtfsVolumes[DeviceId] = Volume;
1008
1009 //
1010 // Return success
1011 //
1012 TRACE("NtfsMount(%lu) success\n", DeviceId);
1013 return &NtfsFuncTable;
1014}
1015
1016#endif
#define N
Definition: crc32.c:57
ULONG ReadLength
unsigned char BOOLEAN
PRTL_UNICODE_STRING_BUFFER Path
Type
Definition: Type.h:7
#define ENOENT
Definition: acclib.h:79
#define EINVAL
Definition: acclib.h:90
#define ENOMEM
Definition: acclib.h:84
#define EIO
Definition: acclib.h:81
ACPI_SIZE strlen(const char *String)
Definition: utclib.c:269
#define EACCES
Definition: acclib.h:85
int tolower(int c)
Definition: utclib.c:902
#define DBG_DEFAULT_CHANNEL(ch)
Definition: debug.h:106
struct NTFS_ATTR_RECORD * PNTFS_ATTR_RECORD
struct NTFS_INDEX_ENTRY * PNTFS_INDEX_ENTRY
#define NTFS_ATTR_TYPE_INDEX_ROOT
Definition: ntfs.h:44
#define NTFS_ATTR_TYPE_BITMAP
Definition: ntfs.h:46
#define NTFS_MFT_MASK
Definition: ntfs.h:68
#define NTFS_ATTR_TYPE_DATA
Definition: ntfs.h:43
#define NTFS_FILE_ROOT
Definition: ntfs.h:28
#define NTFS_INDEX_ENTRY_END
Definition: ntfs.h:61
#define NTFS_ATTR_TYPE_ATTRIBUTE_LIST
Definition: ntfs.h:37
#define NTFS_ATTR_TYPE_END
Definition: ntfs.h:50
#define NTFS_ATTR_TYPE_INDEX_ALLOCATION
Definition: ntfs.h:45
#define NTFS_LARGE_INDEX
Definition: ntfs.h:58
struct NTFS_MFT_RECORD * PNTFS_MFT_RECORD
struct NTFS_INDEX_ROOT * PNTFS_INDEX_ROOT
ARC_STATUS ArcSeek(ULONG FileId, LARGE_INTEGER *Position, SEEKMODE SeekMode)
Definition: fs.c:455
PVOID FsGetDeviceSpecific(ULONG FileId)
Definition: fs.c:632
ULONG FsGetNumPathParts(PCSTR Path)
Definition: fs.c:540
VOID FsSetDeviceSpecific(ULONG FileId, PVOID Specific)
Definition: fs.c:625
VOID FileSystemError(PCSTR ErrorString)
Definition: fs.c:471
ULONG FsGetDeviceId(ULONG FileId)
Definition: fs.c:639
#define MAX_FDS
Definition: fs.h:34
ARC_STATUS ArcRead(ULONG FileId, VOID *Buffer, ULONG N, ULONG *Count)
Definition: fs.c:448
VOID FsGetFirstNameFromPath(PCHAR Buffer, PCSTR Path)
Definition: fs.c:568
VOID FrLdrTempFree(PVOID Allocation, ULONG Tag)
Definition: heap.c:553
PVOID FrLdrTempAlloc(_In_ SIZE_T Size, _In_ ULONG Tag)
Definition: heap.c:545
static BOOLEAN NtfsDiskRead(PNTFS_VOLUME_INFO Volume, ULONGLONG Offset, ULONGLONG Length, PCHAR Buffer)
Definition: ntfs.c:145
ARC_STATUS NtfsSeek(ULONG FileId, LARGE_INTEGER *Position, SEEKMODE SeekMode)
Definition: ntfs.c:850
#define TAG_NTFS_CONTEXT
Definition: ntfs.c:33
static BOOLEAN NtfsFixupRecord(PNTFS_VOLUME_INFO Volume, PNTFS_RECORD Record)
Definition: ntfs.c:457
static BOOLEAN NtfsCompareFileName(PCHAR FileName, PNTFS_INDEX_ENTRY IndexEntry)
Definition: ntfs.c:512
PNTFS_VOLUME_INFO NtfsVolumes[MAX_FDS]
Definition: ntfs.c:55
ARC_STATUS NtfsOpen(CHAR *Path, OPENMODE OpenMode, ULONG *FileId)
Definition: ntfs.c:782
static PNTFS_ATTR_CONTEXT NtfsFindAttributeHelper(PNTFS_VOLUME_INFO Volume, PNTFS_ATTR_RECORD AttrRecord, PNTFS_ATTR_RECORD AttrRecordEnd, ULONG Type, const WCHAR *Name, ULONG NameLength)
Definition: ntfs.c:374
static BOOLEAN NtfsFindMftRecord(PNTFS_VOLUME_INFO Volume, ULONGLONG MFTIndex, PCHAR FileName, ULONGLONG *OutMFTIndex)
Definition: ntfs.c:543
ARC_STATUS NtfsGetFileInformation(ULONG FileId, FILEINFORMATION *Information)
Definition: ntfs.c:768
#define TAG_NTFS_LIST
Definition: ntfs.c:34
#define TAG_NTFS_MFT
Definition: ntfs.c:35
static VOID NtfsReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context)
Definition: ntfs.c:140
static ULONG NtfsReadAttribute(PNTFS_VOLUME_INFO Volume, PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset, PCHAR Buffer, ULONG Length)
Definition: ntfs.c:222
static PNTFS_ATTR_CONTEXT NtfsPrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord)
Definition: ntfs.c:106
const DEVVTBL NtfsFuncTable
Definition: ntfs.c:874
#define TAG_NTFS_INDEX_REC
Definition: ntfs.c:36
struct _NTFS_VOLUME_INFO NTFS_VOLUME_INFO
#define TAG_NTFS_DATA
Definition: ntfs.c:40
static BOOLEAN NtfsLookupFile(PNTFS_VOLUME_INFO Volume, PCSTR FileName, PNTFS_MFT_RECORD MftRecord, PNTFS_ATTR_CONTEXT *DataContext)
Definition: ntfs.c:709
ARC_STATUS NtfsClose(ULONG FileId)
Definition: ntfs.c:758
static PNTFS_ATTR_CONTEXT NtfsFindAttribute(PNTFS_VOLUME_INFO Volume, PNTFS_MFT_RECORD MftRecord, ULONG Type, const WCHAR *Name)
Definition: ntfs.c:443
static ULONGLONG NtfsGetAttributeSize(PNTFS_ATTR_RECORD AttrRecord)
Definition: ntfs.c:57
#define TAG_NTFS_BITMAP
Definition: ntfs.c:37
#define TAG_NTFS_FILE
Definition: ntfs.c:38
static PUCHAR NtfsDecodeRun(PUCHAR DataRun, LONGLONG *DataRunOffset, ULONGLONG *DataRunLength)
Definition: ntfs.c:65
#define TAG_NTFS_VOLUME
Definition: ntfs.c:39
static BOOLEAN NtfsReadMftRecord(PNTFS_VOLUME_INFO Volume, ULONGLONG MFTIndex, PNTFS_MFT_RECORD Buffer)
Definition: ntfs.c:481
const DEVVTBL * NtfsMount(ULONG DeviceId)
Definition: ntfs.c:884
ARC_STATUS NtfsRead(ULONG FileId, VOID *Buffer, ULONG N, ULONG *Count)
Definition: ntfs.c:829
while(CdLookupNextInitialFileDirent(IrpContext, Fcb, FileContext))
Definition: bufpool.h:45
LPWSTR Name
Definition: desk.c:124
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
unsigned char Byte
Definition: zlib.h:37
struct _FileName FileName
Definition: fatprocs.h:897
_Must_inspect_result_ _In_opt_ PFLT_INSTANCE _Out_ PHANDLE FileHandle
Definition: fltkernel.h:1231
_Must_inspect_result_ _In_ PFILE_OBJECT _In_opt_ HANDLE _In_ ULONG FileNameLength
Definition: fltkernel.h:1129
Status
Definition: gdiplustypes.h:25
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
#define RtlEqualMemory(dst, src, len)
Definition: kdvm.h:18
UNICODE_STRING Volume
Definition: fltkernel.h:1172
#define ASSERT(a)
Definition: mode.c:44
unsigned __int64 ULONG64
Definition: imports.h:198
#define min(a, b)
Definition: monoChain.cc:55
int Count
Definition: noreturn.cpp:7
_In_ ULONG _In_ ULONG Offset
Definition: ntddpcm.h:101
_In_ ULONG _In_ ULONG _In_ ULONG Length
Definition: ntddpcm.h:102
#define L(x)
Definition: ntvdm.h:50
unsigned short USHORT
Definition: pedump.c:61
@ ESUCCESS
Definition: arc.h:32
ULONG ARC_STATUS
Definition: arc.h:4
@ SeekRelative
Definition: arc.h:60
@ SeekAbsolute
Definition: arc.h:59
enum _OPENMODE OPENMODE
enum _SEEKMODE SEEKMODE
@ OpenReadOnly
Definition: arc.h:65
#define TRACE(s)
Definition: solgame.cpp:4
NTFS_ATTR_RECORD Record
Definition: ntfs.h:238
ULONG Length
Definition: ntfs.h:127
USHORT NameOffset
Definition: ntfs.h:130
struct NTFS_ATTR_RECORD::@213::@215 Resident
UCHAR NameLength
Definition: ntfs.h:129
UCHAR IsNonResident
Definition: ntfs.h:128
struct NTFS_ATTR_RECORD::@213::@216 NonResident
ULONG Type
Definition: ntfs.h:126
WCHAR FileName[1]
Definition: ntfs.h:192
UCHAR FileNameLength
Definition: ntfs.h:190
Definition: ntfs.h:208
union NTFS_INDEX_ENTRY::@217 Data
struct NTFS_INDEX_ENTRY::@217::@218 Directory
NTFS_FILE_NAME_ATTR FileName
Definition: ntfs.h:226
USHORT Flags
Definition: ntfs.h:224
USHORT Length
Definition: ntfs.h:222
UCHAR Flags
Definition: ntfs.h:164
ULONG EntriesOffset
Definition: ntfs.h:161
NTFS_INDEX_HEADER IndexHeader
Definition: ntfs.h:175
ULONG IndexBlockSize
Definition: ntfs.h:172
USHORT AttributesOffset
Definition: ntfs.h:114
PVOID Volume
Definition: fltmgrint.h:141
PUCHAR TemporarySector
Definition: ntfs.c:52
NTFS_BOOTSECTOR BootSector
Definition: ntfs.c:44
ULONG ClusterSize
Definition: ntfs.c:45
PNTFS_MFT_RECORD MasterFileTable
Definition: ntfs.c:48
ULONG MftRecordSize
Definition: ntfs.c:46
ULONG DeviceId
Definition: ntfs.c:51
PNTFS_ATTR_CONTEXT MFTContext
Definition: ntfs.c:50
ULONG IndexRecordSize
Definition: ntfs.c:47
Definition: fs.h:25
static COORD Position
Definition: mouse.c:34
int64_t LONG64
Definition: typedefs.h:68
#define FIELD_OFFSET(t, f)
Definition: typedefs.h:255
int64_t LONGLONG
Definition: typedefs.h:68
const char * PCSTR
Definition: typedefs.h:52
#define RtlCopyMemory(Destination, Source, Length)
Definition: typedefs.h:263
#define RtlZeroMemory(Destination, Length)
Definition: typedefs.h:262
uint16_t * PWCHAR
Definition: typedefs.h:56
unsigned char * PUCHAR
Definition: typedefs.h:53
uint32_t ULONG
Definition: typedefs.h:59
uint64_t ULONGLONG
Definition: typedefs.h:67
char * PCHAR
Definition: typedefs.h:51
LONGLONG QuadPart
Definition: typedefs.h:114
_Must_inspect_result_ _In_ WDFIOTARGET _In_opt_ WDFREQUEST _In_opt_ PWDF_MEMORY_DESCRIPTOR _In_opt_ PLONGLONG _In_opt_ PWDF_REQUEST_SEND_OPTIONS _Out_opt_ PULONG_PTR BytesRead
Definition: wdfiotarget.h:870
_In_ WDFREQUEST _In_ NTSTATUS _In_ ULONG_PTR Information
Definition: wdfrequest.h:1049
_In_ struct _KBUGCHECK_REASON_CALLBACK_RECORD * Record
Definition: ketypes.h:268
unsigned char UCHAR
Definition: xmlstorage.h:181
__wchar_t WCHAR
Definition: xmlstorage.h:180
char CHAR
Definition: xmlstorage.h:175