2 * FreeLoader NTFS support
3 * Copyright (C) 2004 Filip Navara <xnavara@volny.cz>
4 * Copyright (C) 2009-2010 Hervé Poussineau
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.
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.
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.
23 * - No support for compressed files.
24 * - May crash on corrupted filesystem.
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'
40 DBG_DEFAULT_CHANNEL(FILESYSTEM
);
42 typedef struct _NTFS_VOLUME_INFO
44 NTFS_BOOTSECTOR BootSector
;
47 ULONG IndexRecordSize
;
48 PNTFS_MFT_RECORD MasterFileTable
;
49 /* FIXME: MFTContext is never freed. */
50 PNTFS_ATTR_CONTEXT MFTContext
;
52 PUCHAR TemporarySector
;
55 PNTFS_VOLUME_INFO NtfsVolumes
[MAX_FDS
];
57 static ULONGLONG
NtfsGetAttributeSize(PNTFS_ATTR_RECORD AttrRecord
)
59 if (AttrRecord
->IsNonResident
)
60 return AttrRecord
->NonResident
.DataSize
;
62 return AttrRecord
->Resident
.ValueLength
;
65 static PUCHAR
NtfsDecodeRun(PUCHAR DataRun
, LONGLONG
*DataRunOffset
, ULONGLONG
*DataRunLength
)
67 UCHAR DataRunOffsetSize
;
68 UCHAR DataRunLengthSize
;
71 DataRunOffsetSize
= (*DataRun
>> 4) & 0xF;
72 DataRunLengthSize
= *DataRun
& 0xF;
76 for (i
= 0; i
< DataRunLengthSize
; i
++)
78 *DataRunLength
+= ((ULONG64
)*DataRun
) << (i
* 8);
82 /* NTFS 3+ sparse files */
83 if (DataRunOffsetSize
== 0)
89 for (i
= 0; i
< DataRunOffsetSize
- 1; i
++)
91 *DataRunOffset
+= ((ULONG64
)*DataRun
) << (i
* 8);
94 /* The last byte contains sign so we must process it different way. */
95 *DataRunOffset
= ((LONG64
)(CHAR
)(*(DataRun
++)) << (i
* 8)) + *DataRunOffset
;
98 TRACE("DataRunOffsetSize: %x\n", DataRunOffsetSize
);
99 TRACE("DataRunLengthSize: %x\n", DataRunLengthSize
);
100 TRACE("DataRunOffset: %x\n", *DataRunOffset
);
101 TRACE("DataRunLength: %x\n", *DataRunLength
);
106 static PNTFS_ATTR_CONTEXT
NtfsPrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord
)
108 PNTFS_ATTR_CONTEXT Context
;
110 Context
= FrLdrTempAlloc(FIELD_OFFSET(NTFS_ATTR_CONTEXT
, Record
) + AttrRecord
->Length
,
112 RtlCopyMemory(&Context
->Record
, AttrRecord
, AttrRecord
->Length
);
113 if (AttrRecord
->IsNonResident
)
115 LONGLONG DataRunOffset
;
116 ULONGLONG DataRunLength
;
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)
125 Context
->CacheRunStartLCN
=
126 Context
->CacheRunLastLCN
= DataRunOffset
;
131 Context
->CacheRunStartLCN
= -1;
132 Context
->CacheRunLastLCN
= 0;
134 Context
->CacheRunCurrentOffset
= 0;
140 static VOID
NtfsReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context
)
142 FrLdrTempFree(Context
, TAG_NTFS_CONTEXT
);
145 static BOOLEAN
NtfsDiskRead(PNTFS_VOLUME_INFO Volume
, ULONGLONG Offset
, ULONGLONG Length
, PCHAR Buffer
)
147 LARGE_INTEGER Position
;
152 TRACE("NtfsDiskRead - Offset: %I64d Length: %I64d\n", Offset
, Length
);
155 // I. Read partial first sector if needed
157 if (Offset
% Volume
->BootSector
.BytesPerSector
)
159 Position
.QuadPart
= Offset
& ~(Volume
->BootSector
.BytesPerSector
- 1);
160 Status
= ArcSeek(Volume
->DeviceId
, &Position
, SeekAbsolute
);
161 if (Status
!= ESUCCESS
)
163 Status
= ArcRead(Volume
->DeviceId
, Volume
->TemporarySector
, Volume
->BootSector
.BytesPerSector
, &Count
);
164 if (Status
!= ESUCCESS
|| Count
!= Volume
->BootSector
.BytesPerSector
)
166 ReadLength
= (USHORT
)min(Length
, Volume
->BootSector
.BytesPerSector
- (Offset
% Volume
->BootSector
.BytesPerSector
));
169 // Copy interesting data
171 RtlCopyMemory(Buffer
,
172 &Volume
->TemporarySector
[Offset
% Volume
->BootSector
.BytesPerSector
],
176 // Move to unfilled buffer part
178 Buffer
+= ReadLength
;
179 Length
-= ReadLength
;
180 Offset
+= ReadLength
;
184 // II. Read all complete blocks
186 if (Length
>= Volume
->BootSector
.BytesPerSector
)
188 Position
.QuadPart
= Offset
;
189 Status
= ArcSeek(Volume
->DeviceId
, &Position
, SeekAbsolute
);
190 if (Status
!= ESUCCESS
)
192 ReadLength
= Length
& ~(Volume
->BootSector
.BytesPerSector
- 1);
193 Status
= ArcRead(Volume
->DeviceId
, Buffer
, ReadLength
, &Count
);
194 if (Status
!= ESUCCESS
|| Count
!= ReadLength
)
198 // Move to unfilled buffer part
200 Buffer
+= ReadLength
;
201 Length
-= ReadLength
;
202 Offset
+= ReadLength
;
206 // III. Read the rest of data
210 Position
.QuadPart
= Offset
;
211 Status
= ArcSeek(Volume
->DeviceId
, &Position
, SeekAbsolute
);
212 if (Status
!= ESUCCESS
)
214 Status
= ArcRead(Volume
->DeviceId
, Buffer
, (ULONG
)Length
, &Count
);
215 if (Status
!= ESUCCESS
|| Count
!= Length
)
222 static ULONG
NtfsReadAttribute(PNTFS_VOLUME_INFO Volume
, PNTFS_ATTR_CONTEXT Context
, ULONGLONG Offset
, PCHAR Buffer
, ULONG Length
)
226 LONGLONG DataRunOffset
;
227 ULONGLONG DataRunLength
;
228 LONGLONG DataRunStartLCN
;
229 ULONGLONG CurrentOffset
;
233 if (!Context
->Record
.IsNonResident
)
235 if (Offset
> Context
->Record
.Resident
.ValueLength
)
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
);
244 * Non-resident attribute
248 * I. Find the corresponding start data run.
253 // FIXME: Cache seems to be non-working. Disable it for now
254 //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
257 DataRun
= Context
->CacheRun
;
258 LastLCN
= Context
->CacheRunLastLCN
;
259 DataRunStartLCN
= Context
->CacheRunStartLCN
;
260 DataRunLength
= Context
->CacheRunLength
;
261 CurrentOffset
= Context
->CacheRunCurrentOffset
;
266 DataRun
= (PUCHAR
)&Context
->Record
+ Context
->Record
.NonResident
.MappingPairsOffset
;
271 DataRun
= NtfsDecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
272 if (DataRunOffset
!= -1)
274 /* Normal data run. */
275 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
276 LastLCN
= DataRunStartLCN
;
280 /* Sparse data run. */
281 DataRunStartLCN
= -1;
284 if (Offset
>= CurrentOffset
&&
285 Offset
< CurrentOffset
+ (DataRunLength
* Volume
->ClusterSize
))
295 CurrentOffset
+= DataRunLength
* Volume
->ClusterSize
;
300 * II. Go through the run list and read the data
303 ReadLength
= (ULONG
)min(DataRunLength
* Volume
->ClusterSize
- (Offset
- CurrentOffset
), Length
);
304 if (DataRunStartLCN
== -1)
305 RtlZeroMemory(Buffer
, ReadLength
);
306 if (DataRunStartLCN
== -1 || NtfsDiskRead(Volume
, DataRunStartLCN
* Volume
->ClusterSize
+ Offset
- CurrentOffset
, ReadLength
, Buffer
))
308 Length
-= ReadLength
;
309 Buffer
+= ReadLength
;
310 AlreadyRead
+= ReadLength
;
312 if (ReadLength
== DataRunLength
* Volume
->ClusterSize
- (Offset
- CurrentOffset
))
314 CurrentOffset
+= DataRunLength
* Volume
->ClusterSize
;
315 DataRun
= NtfsDecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
316 if (DataRunOffset
!= (ULONGLONG
)-1)
318 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
319 LastLCN
= DataRunStartLCN
;
322 DataRunStartLCN
= -1;
327 ReadLength
= (ULONG
)min(DataRunLength
* Volume
->ClusterSize
, Length
);
328 if (DataRunStartLCN
== -1)
329 RtlZeroMemory(Buffer
, ReadLength
);
330 else if (!NtfsDiskRead(Volume
, DataRunStartLCN
* Volume
->ClusterSize
, ReadLength
, Buffer
))
333 Length
-= ReadLength
;
334 Buffer
+= ReadLength
;
335 AlreadyRead
+= ReadLength
;
337 /* We finished this request, but there still data in this data run. */
338 if (Length
== 0 && ReadLength
!= DataRunLength
* Volume
->ClusterSize
)
342 * Go to next run in the list.
347 CurrentOffset
+= DataRunLength
* Volume
->ClusterSize
;
348 DataRun
= NtfsDecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
349 if (DataRunOffset
!= -1)
351 /* Normal data run. */
352 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
353 LastLCN
= DataRunStartLCN
;
357 /* Sparse data run. */
358 DataRunStartLCN
= -1;
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
;
374 static PNTFS_ATTR_CONTEXT
NtfsFindAttributeHelper(PNTFS_VOLUME_INFO Volume
, PNTFS_ATTR_RECORD AttrRecord
, PNTFS_ATTR_RECORD AttrRecordEnd
, ULONG Type
, const WCHAR
*Name
, ULONG NameLength
)
376 while (AttrRecord
< AttrRecordEnd
)
378 if (AttrRecord
->Type
== NTFS_ATTR_TYPE_END
)
381 if (AttrRecord
->Type
== NTFS_ATTR_TYPE_ATTRIBUTE_LIST
)
383 PNTFS_ATTR_CONTEXT Context
;
384 PNTFS_ATTR_CONTEXT ListContext
;
387 PNTFS_ATTR_RECORD ListAttrRecord
;
388 PNTFS_ATTR_RECORD ListAttrRecordEnd
;
390 ListContext
= NtfsPrepareAttributeContext(AttrRecord
);
392 ListSize
= NtfsGetAttributeSize(&ListContext
->Record
);
393 if(ListSize
<= 0xFFFFFFFF)
394 ListBuffer
= FrLdrTempAlloc((ULONG
)ListSize
, TAG_NTFS_LIST
);
400 TRACE("Failed to allocate memory: %x\n", (ULONG
)ListSize
);
404 ListAttrRecord
= (PNTFS_ATTR_RECORD
)ListBuffer
;
405 ListAttrRecordEnd
= (PNTFS_ATTR_RECORD
)((PCHAR
)ListBuffer
+ ListSize
);
407 if (NtfsReadAttribute(Volume
, ListContext
, 0, ListBuffer
, (ULONG
)ListSize
) == ListSize
)
409 Context
= NtfsFindAttributeHelper(Volume
, ListAttrRecord
, ListAttrRecordEnd
,
410 Type
, Name
, NameLength
);
412 NtfsReleaseAttributeContext(ListContext
);
413 FrLdrTempFree(ListBuffer
, TAG_NTFS_LIST
);
420 if (AttrRecord
->Type
== Type
)
422 if (AttrRecord
->NameLength
== NameLength
)
426 AttrName
= (PWCHAR
)((PCHAR
)AttrRecord
+ AttrRecord
->NameOffset
);
427 if (RtlEqualMemory(AttrName
, Name
, NameLength
<< 1))
429 /* Found it, fill up the context and return. */
430 return NtfsPrepareAttributeContext(AttrRecord
);
435 if (AttrRecord
->Length
== 0)
437 AttrRecord
= (PNTFS_ATTR_RECORD
)((PCHAR
)AttrRecord
+ AttrRecord
->Length
);
443 static PNTFS_ATTR_CONTEXT
NtfsFindAttribute(PNTFS_VOLUME_INFO Volume
, PNTFS_MFT_RECORD MftRecord
, ULONG Type
, const WCHAR
*Name
)
445 PNTFS_ATTR_RECORD AttrRecord
;
446 PNTFS_ATTR_RECORD AttrRecordEnd
;
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
++)
454 return NtfsFindAttributeHelper(Volume
, AttrRecord
, AttrRecordEnd
, Type
, Name
, NameLength
);
457 static BOOLEAN
NtfsFixupRecord(PNTFS_VOLUME_INFO Volume
, PNTFS_RECORD Record
)
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);
471 if (*Block
!= USANumber
)
474 Block
= (USHORT
*)((PCHAR
)Block
+ Volume
->BootSector
.BytesPerSector
);
481 static BOOLEAN
NtfsReadMftRecord(PNTFS_VOLUME_INFO Volume
, ULONGLONG MFTIndex
, PNTFS_MFT_RECORD Buffer
)
485 BytesRead
= NtfsReadAttribute(Volume
, Volume
->MFTContext
, MFTIndex
* Volume
->MftRecordSize
, (PCHAR
)Buffer
, Volume
->MftRecordSize
);
486 if (BytesRead
!= Volume
->MftRecordSize
)
489 /* Apply update sequence array fixups. */
490 return NtfsFixupRecord(Volume
, (PNTFS_RECORD
)Buffer
);
494 VOID
NtfsPrintFile(PNTFS_INDEX_ENTRY IndexEntry
)
497 UCHAR FileNameLength
;
498 CHAR AnsiFileName
[256];
501 FileName
= IndexEntry
->FileName
.FileName
;
502 FileNameLength
= min(IndexEntry
->FileName
.FileNameLength
, 255);
504 for (i
= 0; i
< FileNameLength
; i
++)
505 AnsiFileName
[i
] = (CHAR
)FileName
[i
];
508 TRACE("- %s (%x)\n", AnsiFileName
, IndexEntry
->Data
.Directory
.IndexedFile
);
512 static BOOLEAN
NtfsCompareFileName(PCHAR FileName
, PNTFS_INDEX_ENTRY IndexEntry
)
514 PWCHAR EntryFileName
;
515 UCHAR EntryFileNameLength
;
518 EntryFileName
= IndexEntry
->FileName
.FileName
;
519 EntryFileNameLength
= IndexEntry
->FileName
.FileNameLength
;
522 NtfsPrintFile(IndexEntry
);
525 if (strlen(FileName
) != EntryFileNameLength
)
528 /* Do case-sensitive compares for Posix file names. */
529 if (IndexEntry
->FileName
.FileNameType
== NTFS_FILE_NAME_POSIX
)
531 for (i
= 0; i
< EntryFileNameLength
; i
++)
532 if (EntryFileName
[i
] != FileName
[i
])
537 for (i
= 0; i
< EntryFileNameLength
; i
++)
538 if (tolower(EntryFileName
[i
]) != tolower(FileName
[i
]))
545 static BOOLEAN
NtfsFindMftRecord(PNTFS_VOLUME_INFO Volume
, ULONGLONG MFTIndex
, PCHAR FileName
, ULONGLONG
*OutMFTIndex
)
547 PNTFS_MFT_RECORD MftRecord
;
549 PNTFS_ATTR_CONTEXT IndexRootCtx
;
550 PNTFS_ATTR_CONTEXT IndexBitmapCtx
;
551 PNTFS_ATTR_CONTEXT IndexAllocationCtx
;
552 PNTFS_INDEX_ROOT IndexRoot
;
553 ULONGLONG BitmapDataSize
;
554 ULONGLONG IndexAllocationSize
;
557 PNTFS_INDEX_ENTRY IndexEntry
, IndexEntryEnd
;
559 ULONG IndexBlockSize
;
561 MftRecord
= FrLdrTempAlloc(Volume
->MftRecordSize
, TAG_NTFS_MFT
);
562 if (MftRecord
== NULL
)
567 if (NtfsReadMftRecord(Volume
, MFTIndex
, MftRecord
))
569 //Magic = MftRecord->Magic;
571 IndexRootCtx
= NtfsFindAttribute(Volume
, MftRecord
, NTFS_ATTR_TYPE_INDEX_ROOT
, L
"$I30");
572 if (IndexRootCtx
== NULL
)
574 FrLdrTempFree(MftRecord
, TAG_NTFS_MFT
);
578 IndexRecord
= FrLdrTempAlloc(Volume
->IndexRecordSize
, TAG_NTFS_INDEX_REC
);
579 if (IndexRecord
== NULL
)
581 FrLdrTempFree(MftRecord
, TAG_NTFS_MFT
);
585 NtfsReadAttribute(Volume
, IndexRootCtx
, 0, IndexRecord
, Volume
->IndexRecordSize
);
586 IndexRoot
= (PNTFS_INDEX_ROOT
)IndexRecord
;
587 IndexEntry
= (PNTFS_INDEX_ENTRY
)((PCHAR
)&IndexRoot
->IndexHeader
+ IndexRoot
->IndexHeader
.EntriesOffset
);
588 /* Index root is always resident. */
589 IndexEntryEnd
= (PNTFS_INDEX_ENTRY
)(IndexRecord
+ IndexRootCtx
->Record
.Resident
.ValueLength
);
590 NtfsReleaseAttributeContext(IndexRootCtx
);
592 TRACE("IndexRecordSize: %x IndexBlockSize: %x\n", Volume
->IndexRecordSize
, IndexRoot
->IndexBlockSize
);
594 while (IndexEntry
< IndexEntryEnd
&&
595 !(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_END
))
597 if (NtfsCompareFileName(FileName
, IndexEntry
))
599 *OutMFTIndex
= IndexEntry
->Data
.Directory
.IndexedFile
;
600 FrLdrTempFree(IndexRecord
, TAG_NTFS_INDEX_REC
);
601 FrLdrTempFree(MftRecord
, TAG_NTFS_MFT
);
604 IndexEntry
= (PNTFS_INDEX_ENTRY
)((PCHAR
)IndexEntry
+ IndexEntry
->Length
);
607 if (IndexRoot
->IndexHeader
.Flags
& NTFS_LARGE_INDEX
)
609 TRACE("Large Index!\n");
611 IndexBlockSize
= IndexRoot
->IndexBlockSize
;
613 IndexBitmapCtx
= NtfsFindAttribute(Volume
, MftRecord
, NTFS_ATTR_TYPE_BITMAP
, L
"$I30");
614 if (IndexBitmapCtx
== NULL
)
616 TRACE("Corrupted filesystem!\n");
617 FrLdrTempFree(MftRecord
, TAG_NTFS_MFT
);
620 BitmapDataSize
= NtfsGetAttributeSize(&IndexBitmapCtx
->Record
);
621 TRACE("BitmapDataSize: %x\n", (ULONG
)BitmapDataSize
);
622 if(BitmapDataSize
<= 0xFFFFFFFF)
623 BitmapData
= FrLdrTempAlloc((ULONG
)BitmapDataSize
, TAG_NTFS_BITMAP
);
627 if (BitmapData
== NULL
)
629 FrLdrTempFree(IndexRecord
, TAG_NTFS_INDEX_REC
);
630 FrLdrTempFree(MftRecord
, TAG_NTFS_MFT
);
633 NtfsReadAttribute(Volume
, IndexBitmapCtx
, 0, BitmapData
, (ULONG
)BitmapDataSize
);
634 NtfsReleaseAttributeContext(IndexBitmapCtx
);
636 IndexAllocationCtx
= NtfsFindAttribute(Volume
, MftRecord
, NTFS_ATTR_TYPE_INDEX_ALLOCATION
, L
"$I30");
637 if (IndexAllocationCtx
== NULL
)
639 TRACE("Corrupted filesystem!\n");
640 FrLdrTempFree(BitmapData
, TAG_NTFS_BITMAP
);
641 FrLdrTempFree(IndexRecord
, TAG_NTFS_INDEX_REC
);
642 FrLdrTempFree(MftRecord
, TAG_NTFS_MFT
);
645 IndexAllocationSize
= NtfsGetAttributeSize(&IndexAllocationCtx
->Record
);
651 TRACE("RecordOffset: %x IndexAllocationSize: %x\n", RecordOffset
, IndexAllocationSize
);
652 for (; RecordOffset
< IndexAllocationSize
;)
654 UCHAR Bit
= 1 << ((RecordOffset
/ IndexBlockSize
) & 7);
655 ULONG Byte
= (RecordOffset
/ IndexBlockSize
) >> 3;
656 if ((BitmapData
[Byte
] & Bit
))
658 RecordOffset
+= IndexBlockSize
;
661 if (RecordOffset
>= IndexAllocationSize
)
666 NtfsReadAttribute(Volume
, IndexAllocationCtx
, RecordOffset
, IndexRecord
, IndexBlockSize
);
668 if (!NtfsFixupRecord(Volume
, (PNTFS_RECORD
)IndexRecord
))
674 IndexEntry
= (PNTFS_INDEX_ENTRY
)(IndexRecord
+ 0x18 + *(USHORT
*)(IndexRecord
+ 0x18));
675 IndexEntryEnd
= (PNTFS_INDEX_ENTRY
)(IndexRecord
+ IndexBlockSize
);
677 while (IndexEntry
< IndexEntryEnd
&&
678 !(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_END
))
680 if (NtfsCompareFileName(FileName
, IndexEntry
))
682 TRACE("File found\n");
683 *OutMFTIndex
= IndexEntry
->Data
.Directory
.IndexedFile
;
684 FrLdrTempFree(BitmapData
, TAG_NTFS_BITMAP
);
685 FrLdrTempFree(IndexRecord
, TAG_NTFS_INDEX_REC
);
686 FrLdrTempFree(MftRecord
, TAG_NTFS_MFT
);
687 NtfsReleaseAttributeContext(IndexAllocationCtx
);
690 IndexEntry
= (PNTFS_INDEX_ENTRY
)((PCHAR
)IndexEntry
+ IndexEntry
->Length
);
693 RecordOffset
+= IndexBlockSize
;
696 NtfsReleaseAttributeContext(IndexAllocationCtx
);
697 FrLdrTempFree(BitmapData
, TAG_NTFS_BITMAP
);
700 FrLdrTempFree(IndexRecord
, TAG_NTFS_INDEX_REC
);
704 TRACE("Can't read MFT record\n");
706 FrLdrTempFree(MftRecord
, TAG_NTFS_MFT
);
711 static BOOLEAN
NtfsLookupFile(PNTFS_VOLUME_INFO Volume
, PCSTR FileName
, PNTFS_MFT_RECORD MftRecord
, PNTFS_ATTR_CONTEXT
*DataContext
)
713 ULONG NumberOfPathParts
;
715 ULONGLONG CurrentMFTIndex
;
718 TRACE("NtfsLookupFile() FileName = %s\n", FileName
);
720 CurrentMFTIndex
= NTFS_FILE_ROOT
;
721 NumberOfPathParts
= FsGetNumPathParts(FileName
);
722 for (i
= 0; i
< NumberOfPathParts
; i
++)
724 FsGetFirstNameFromPath(PathPart
, FileName
);
726 for (; (*FileName
!= '\\') && (*FileName
!= '/') && (*FileName
!= '\0'); FileName
++)
730 TRACE("- Lookup: %s\n", PathPart
);
731 if (!NtfsFindMftRecord(Volume
, CurrentMFTIndex
, PathPart
, &CurrentMFTIndex
))
736 TRACE("- Lookup: %x\n", CurrentMFTIndex
);
739 if (!NtfsReadMftRecord(Volume
, CurrentMFTIndex
, MftRecord
))
741 TRACE("NtfsLookupFile: Can't read MFT record\n");
745 *DataContext
= NtfsFindAttribute(Volume
, MftRecord
, NTFS_ATTR_TYPE_DATA
, L
"");
746 if (*DataContext
== NULL
)
748 TRACE("NtfsLookupFile: Can't find data attribute\n");
755 ARC_STATUS
NtfsClose(ULONG FileId
)
757 PNTFS_FILE_HANDLE FileHandle
= FsGetDeviceSpecific(FileId
);
759 NtfsReleaseAttributeContext(FileHandle
->DataContext
);
760 FrLdrTempFree(FileHandle
, TAG_NTFS_FILE
);
765 ARC_STATUS
NtfsGetFileInformation(ULONG FileId
, FILEINFORMATION
* Information
)
767 PNTFS_FILE_HANDLE FileHandle
= FsGetDeviceSpecific(FileId
);
769 RtlZeroMemory(Information
, sizeof(FILEINFORMATION
));
770 Information
->EndingAddress
.QuadPart
= NtfsGetAttributeSize(&FileHandle
->DataContext
->Record
);
771 Information
->CurrentAddress
.QuadPart
= FileHandle
->Offset
;
773 TRACE("NtfsGetFileInformation() FileSize = %d\n",
774 Information
->EndingAddress
.LowPart
);
775 TRACE("NtfsGetFileInformation() FilePointer = %d\n",
776 Information
->CurrentAddress
.LowPart
);
781 ARC_STATUS
NtfsOpen(CHAR
* Path
, OPENMODE OpenMode
, ULONG
* FileId
)
783 PNTFS_VOLUME_INFO Volume
;
784 PNTFS_FILE_HANDLE FileHandle
;
785 PNTFS_MFT_RECORD MftRecord
;
791 if (OpenMode
!= OpenReadOnly
)
795 // Get underlying device
797 DeviceId
= FsGetDeviceId(*FileId
);
798 Volume
= NtfsVolumes
[DeviceId
];
800 TRACE("NtfsOpen() FileName = %s\n", Path
);
803 // Allocate file structure
805 FileHandle
= FrLdrTempAlloc(sizeof(NTFS_FILE_HANDLE
) + Volume
->MftRecordSize
,
811 RtlZeroMemory(FileHandle
, sizeof(NTFS_FILE_HANDLE
) + Volume
->MftRecordSize
);
812 FileHandle
->Volume
= Volume
;
817 MftRecord
= (PNTFS_MFT_RECORD
)(FileHandle
+ 1);
818 if (!NtfsLookupFile(Volume
, Path
, MftRecord
, &FileHandle
->DataContext
))
820 FrLdrTempFree(FileHandle
, TAG_NTFS_FILE
);
824 FsSetDeviceSpecific(*FileId
, FileHandle
);
828 ARC_STATUS
NtfsRead(ULONG FileId
, VOID
* Buffer
, ULONG N
, ULONG
* Count
)
830 PNTFS_FILE_HANDLE FileHandle
= FsGetDeviceSpecific(FileId
);
831 ULONGLONG BytesRead64
;
836 BytesRead64
= NtfsReadAttribute(FileHandle
->Volume
, FileHandle
->DataContext
, FileHandle
->Offset
, Buffer
, N
);
837 *Count
= (ULONG
)BytesRead64
;
848 ARC_STATUS
NtfsSeek(ULONG FileId
, LARGE_INTEGER
* Position
, SEEKMODE SeekMode
)
850 PNTFS_FILE_HANDLE FileHandle
= FsGetDeviceSpecific(FileId
);
852 TRACE("NtfsSeek() NewFilePointer = %lu\n", Position
->LowPart
);
854 if (SeekMode
!= SeekAbsolute
)
856 if (Position
->HighPart
!= 0)
858 if (Position
->LowPart
>= (ULONG
)NtfsGetAttributeSize(&FileHandle
->DataContext
->Record
))
861 FileHandle
->Offset
= Position
->LowPart
;
865 const DEVVTBL NtfsFuncTable
=
868 NtfsGetFileInformation
,
875 const DEVVTBL
* NtfsMount(ULONG DeviceId
)
877 PNTFS_VOLUME_INFO Volume
;
878 LARGE_INTEGER Position
;
883 // Allocate data for volume information
885 Volume
= FrLdrTempAlloc(sizeof(NTFS_VOLUME_INFO
), TAG_NTFS_VOLUME
);
888 RtlZeroMemory(Volume
, sizeof(NTFS_VOLUME_INFO
));
891 // Read the BootSector
893 Position
.HighPart
= 0;
894 Position
.LowPart
= 0;
895 Status
= ArcSeek(DeviceId
, &Position
, SeekAbsolute
);
896 if (Status
!= ESUCCESS
)
898 FrLdrTempFree(Volume
, TAG_NTFS_VOLUME
);
901 Status
= ArcRead(DeviceId
, &Volume
->BootSector
, sizeof(Volume
->BootSector
), &Count
);
902 if (Status
!= ESUCCESS
|| Count
!= sizeof(Volume
->BootSector
))
904 FrLdrTempFree(Volume
, TAG_NTFS_VOLUME
);
909 // Check if BootSector is valid. If no, return early
911 if (!RtlEqualMemory(Volume
->BootSector
.SystemId
, "NTFS", 4))
913 FrLdrTempFree(Volume
, TAG_NTFS_VOLUME
);
918 // Calculate cluster size and MFT record size
920 Volume
->ClusterSize
= Volume
->BootSector
.SectorsPerCluster
* Volume
->BootSector
.BytesPerSector
;
921 if (Volume
->BootSector
.ClustersPerMftRecord
> 0)
922 Volume
->MftRecordSize
= Volume
->BootSector
.ClustersPerMftRecord
* Volume
->ClusterSize
;
924 Volume
->MftRecordSize
= 1 << (-Volume
->BootSector
.ClustersPerMftRecord
);
925 if (Volume
->BootSector
.ClustersPerIndexRecord
> 0)
926 Volume
->IndexRecordSize
= Volume
->BootSector
.ClustersPerIndexRecord
* Volume
->ClusterSize
;
928 Volume
->IndexRecordSize
= 1 << (-Volume
->BootSector
.ClustersPerIndexRecord
);
930 TRACE("ClusterSize: 0x%x\n", Volume
->ClusterSize
);
931 TRACE("ClustersPerMftRecord: %d\n", Volume
->BootSector
.ClustersPerMftRecord
);
932 TRACE("ClustersPerIndexRecord: %d\n", Volume
->BootSector
.ClustersPerIndexRecord
);
933 TRACE("MftRecordSize: 0x%x\n", Volume
->MftRecordSize
);
934 TRACE("IndexRecordSize: 0x%x\n", Volume
->IndexRecordSize
);
939 TRACE("Reading MFT index...\n");
940 Volume
->MasterFileTable
= FrLdrTempAlloc(Volume
->MftRecordSize
, TAG_NTFS_MFT
);
941 if (!Volume
->MasterFileTable
)
943 FrLdrTempFree(Volume
, TAG_NTFS_VOLUME
);
946 Position
.QuadPart
= Volume
->BootSector
.MftLocation
* Volume
->ClusterSize
;
947 Status
= ArcSeek(DeviceId
, &Position
, SeekAbsolute
);
948 if (Status
!= ESUCCESS
)
950 FileSystemError("Failed to seek to Master File Table record.");
951 FrLdrTempFree(Volume
->MasterFileTable
, TAG_NTFS_MFT
);
952 FrLdrTempFree(Volume
, TAG_NTFS_VOLUME
);
955 Status
= ArcRead(DeviceId
, Volume
->MasterFileTable
, Volume
->MftRecordSize
, &Count
);
956 if (Status
!= ESUCCESS
|| Count
!= Volume
->MftRecordSize
)
958 FileSystemError("Failed to read the Master File Table record.");
959 FrLdrTempFree(Volume
->MasterFileTable
, TAG_NTFS_MFT
);
960 FrLdrTempFree(Volume
, TAG_NTFS_VOLUME
);
965 // Keep room to read partial sectors
967 Volume
->TemporarySector
= FrLdrTempAlloc(Volume
->BootSector
.BytesPerSector
, TAG_NTFS_DATA
);
968 if (!Volume
->TemporarySector
)
970 FileSystemError("Failed to allocate memory.");
971 FrLdrTempFree(Volume
->MasterFileTable
, TAG_NTFS_MFT
);
972 FrLdrTempFree(Volume
, TAG_NTFS_VOLUME
);
979 Volume
->DeviceId
= DeviceId
;
982 // Search DATA attribute
984 TRACE("Searching for DATA attribute...\n");
985 Volume
->MFTContext
= NtfsFindAttribute(Volume
, Volume
->MasterFileTable
, NTFS_ATTR_TYPE_DATA
, L
"");
986 if (!Volume
->MFTContext
)
988 FileSystemError("Can't find data attribute for Master File Table.");
989 FrLdrTempFree(Volume
->MasterFileTable
, TAG_NTFS_MFT
);
990 FrLdrTempFree(Volume
, TAG_NTFS_VOLUME
);
995 // Remember NTFS volume information
997 NtfsVolumes
[DeviceId
] = Volume
;
1002 return &NtfsFuncTable
;