2 * FreeLoader NTFS support
3 * Copyright (C) 2004 Filip Navara <xnavara@volny.cz>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 * - No support for compressed files.
23 * - May crash on corrupted filesystem.
29 typedef struct _NTFS_VOLUME_INFO
31 NTFS_BOOTSECTOR BootSector
;
34 ULONG IndexRecordSize
;
35 PNTFS_MFT_RECORD MasterFileTable
;
36 /* FIXME: MFTContext is never freed. */
37 PNTFS_ATTR_CONTEXT MFTContext
;
41 PNTFS_VOLUME_INFO NtfsVolumes
[MAX_FDS
];
43 static ULONGLONG
NtfsGetAttributeSize(PNTFS_ATTR_RECORD AttrRecord
)
45 if (AttrRecord
->IsNonResident
)
46 return AttrRecord
->NonResident
.DataSize
;
48 return AttrRecord
->Resident
.ValueLength
;
51 static PUCHAR
NtfsDecodeRun(PUCHAR DataRun
, LONGLONG
*DataRunOffset
, ULONGLONG
*DataRunLength
)
53 UCHAR DataRunOffsetSize
;
54 UCHAR DataRunLengthSize
;
57 DataRunOffsetSize
= (*DataRun
>> 4) & 0xF;
58 DataRunLengthSize
= *DataRun
& 0xF;
62 for (i
= 0; i
< DataRunLengthSize
; i
++)
64 *DataRunLength
+= *DataRun
<< (i
<< 3);
68 /* NTFS 3+ sparse files */
69 if (DataRunOffsetSize
== 0)
75 for (i
= 0; i
< DataRunOffsetSize
- 1; i
++)
77 *DataRunOffset
+= *DataRun
<< (i
<< 3);
80 /* The last byte contains sign so we must process it different way. */
81 *DataRunOffset
= ((CHAR
)(*(DataRun
++)) << (i
<< 3)) + *DataRunOffset
;
84 DPRINTM(DPRINT_FILESYSTEM
, "DataRunOffsetSize: %x\n", DataRunOffsetSize
);
85 DPRINTM(DPRINT_FILESYSTEM
, "DataRunLengthSize: %x\n", DataRunLengthSize
);
86 DPRINTM(DPRINT_FILESYSTEM
, "DataRunOffset: %x\n", *DataRunOffset
);
87 DPRINTM(DPRINT_FILESYSTEM
, "DataRunLength: %x\n", *DataRunLength
);
92 static PNTFS_ATTR_CONTEXT
NtfsPrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord
)
94 PNTFS_ATTR_CONTEXT Context
;
96 Context
= MmHeapAlloc(FIELD_OFFSET(NTFS_ATTR_CONTEXT
, Record
) + AttrRecord
->Length
);
97 RtlCopyMemory(&Context
->Record
, AttrRecord
, AttrRecord
->Length
);
98 if (AttrRecord
->IsNonResident
)
100 LONGLONG DataRunOffset
;
101 ULONGLONG DataRunLength
;
103 Context
->CacheRun
= (PUCHAR
)&Context
->Record
+ Context
->Record
.NonResident
.MappingPairsOffset
;
104 Context
->CacheRunOffset
= 0;
105 Context
->CacheRun
= NtfsDecodeRun(Context
->CacheRun
, &DataRunOffset
, &DataRunLength
);
106 Context
->CacheRunLength
= DataRunLength
;
107 if (DataRunOffset
!= -1)
110 Context
->CacheRunStartLCN
=
111 Context
->CacheRunLastLCN
= DataRunOffset
;
116 Context
->CacheRunStartLCN
= -1;
117 Context
->CacheRunLastLCN
= 0;
119 Context
->CacheRunCurrentOffset
= 0;
125 static VOID
NtfsReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context
)
130 static BOOLEAN
NtfsDiskRead(PNTFS_VOLUME_INFO Volume
, ULONGLONG Offset
, ULONGLONG Length
, PCHAR Buffer
)
132 LARGE_INTEGER Position
;
137 DPRINTM(DPRINT_FILESYSTEM
, "NtfsDiskRead - Offset: %I64d Length: %I64d\n", Offset
, Length
);
140 // I. Read partial first sector if needed
142 if (Offset
% Volume
->BootSector
.BytesPerSector
)
144 Position
.HighPart
= 0;
145 Position
.LowPart
= Offset
& ~(Volume
->BootSector
.BytesPerSector
- 1);
146 ret
= ArcSeek(Volume
->DeviceId
, &Position
, SeekAbsolute
);
149 ReadLength
= min(Length
, Volume
->BootSector
.BytesPerSector
- (Offset
% Volume
->BootSector
.BytesPerSector
));
150 ret
= ArcRead(Volume
->DeviceId
, Buffer
, ReadLength
, &Count
);
151 if (ret
!= ESUCCESS
|| Count
!= ReadLength
)
155 // Move to unfilled buffer part
157 Buffer
+= ReadLength
;
158 Length
-= ReadLength
;
159 Offset
+= ReadLength
;
163 // II. Read all complete blocks
165 if (Length
>= Volume
->BootSector
.BytesPerSector
)
167 Position
.HighPart
= 0;
168 Position
.LowPart
= Offset
;
169 ret
= ArcSeek(Volume
->DeviceId
, &Position
, SeekAbsolute
);
172 ReadLength
= Length
& ~(Volume
->BootSector
.BytesPerSector
- 1);
173 ret
= ArcRead(Volume
->DeviceId
, Buffer
, ReadLength
, &Count
);
174 if (ret
!= ESUCCESS
|| Count
!= ReadLength
)
178 // Move to unfilled buffer part
180 Buffer
+= ReadLength
;
181 Length
-= ReadLength
;
182 Offset
+= ReadLength
;
186 // III. Read the rest of data
190 Position
.HighPart
= 0;
191 Position
.LowPart
= Offset
;
192 ret
= ArcSeek(Volume
->DeviceId
, &Position
, SeekAbsolute
);
195 ret
= ArcRead(Volume
->DeviceId
, Buffer
, Length
, &Count
);
196 if (ret
!= ESUCCESS
|| Count
!= Length
)
203 static ULONGLONG
NtfsReadAttribute(PNTFS_VOLUME_INFO Volume
, PNTFS_ATTR_CONTEXT Context
, ULONGLONG Offset
, PCHAR Buffer
, ULONGLONG Length
)
207 LONGLONG DataRunOffset
;
208 ULONGLONG DataRunLength
;
209 LONGLONG DataRunStartLCN
;
210 ULONGLONG CurrentOffset
;
211 ULONGLONG ReadLength
;
212 ULONGLONG AlreadyRead
;
214 if (!Context
->Record
.IsNonResident
)
216 if (Offset
> Context
->Record
.Resident
.ValueLength
)
218 if (Offset
+ Length
> Context
->Record
.Resident
.ValueLength
)
219 Length
= Context
->Record
.Resident
.ValueLength
- Offset
;
220 RtlCopyMemory(Buffer
, (PCHAR
)&Context
->Record
+ Context
->Record
.Resident
.ValueOffset
+ Offset
, Length
);
225 * Non-resident attribute
229 * I. Find the corresponding start data run.
234 if(Context
->CacheRunOffset
<= Offset
&& Offset
< Context
->CacheRunOffset
+ Context
->CacheRunLength
* Volume
->ClusterSize
)
236 DataRun
= Context
->CacheRun
;
237 LastLCN
= Context
->CacheRunLastLCN
;
238 DataRunStartLCN
= Context
->CacheRunStartLCN
;
239 DataRunLength
= Context
->CacheRunLength
;
240 CurrentOffset
= Context
->CacheRunCurrentOffset
;
245 DataRun
= (PUCHAR
)&Context
->Record
+ Context
->Record
.NonResident
.MappingPairsOffset
;
250 DataRun
= NtfsDecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
251 if (DataRunOffset
!= -1)
253 /* Normal data run. */
254 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
255 LastLCN
= DataRunStartLCN
;
259 /* Sparse data run. */
260 DataRunStartLCN
= -1;
263 if (Offset
>= CurrentOffset
&&
264 Offset
< CurrentOffset
+ (DataRunLength
* Volume
->ClusterSize
))
274 CurrentOffset
+= DataRunLength
* Volume
->ClusterSize
;
279 * II. Go through the run list and read the data
282 ReadLength
= min(DataRunLength
* Volume
->ClusterSize
- (Offset
- CurrentOffset
), Length
);
283 if (DataRunStartLCN
== -1)
284 RtlZeroMemory(Buffer
, ReadLength
);
285 if (NtfsDiskRead(Volume
, DataRunStartLCN
* Volume
->ClusterSize
+ Offset
- CurrentOffset
, ReadLength
, Buffer
))
287 Length
-= ReadLength
;
288 Buffer
+= ReadLength
;
289 AlreadyRead
+= ReadLength
;
291 if (ReadLength
== DataRunLength
* Volume
->ClusterSize
- (Offset
- CurrentOffset
))
293 CurrentOffset
+= DataRunLength
* Volume
->ClusterSize
;
294 DataRun
= NtfsDecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
295 if (DataRunLength
!= (ULONGLONG
)-1)
297 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
298 LastLCN
= DataRunStartLCN
;
301 DataRunStartLCN
= -1;
309 ReadLength
= min(DataRunLength
* Volume
->ClusterSize
, Length
);
310 if (DataRunStartLCN
== -1)
311 RtlZeroMemory(Buffer
, ReadLength
);
312 else if (!NtfsDiskRead(Volume
, DataRunStartLCN
* Volume
->ClusterSize
, ReadLength
, Buffer
))
315 Length
-= ReadLength
;
316 Buffer
+= ReadLength
;
317 AlreadyRead
+= ReadLength
;
319 /* We finished this request, but there still data in this data run. */
320 if (Length
== 0 && ReadLength
!= DataRunLength
* Volume
->ClusterSize
)
324 * Go to next run in the list.
329 CurrentOffset
+= DataRunLength
* Volume
->ClusterSize
;
330 DataRun
= NtfsDecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
331 if (DataRunOffset
!= -1)
333 /* Normal data run. */
334 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
335 LastLCN
= DataRunStartLCN
;
339 /* Sparse data run. */
340 DataRunStartLCN
= -1;
346 Context
->CacheRun
= DataRun
;
347 Context
->CacheRunOffset
= Offset
+ AlreadyRead
;
348 Context
->CacheRunStartLCN
= DataRunStartLCN
;
349 Context
->CacheRunLength
= DataRunLength
;
350 Context
->CacheRunLastLCN
= LastLCN
;
351 Context
->CacheRunCurrentOffset
= CurrentOffset
;
356 static PNTFS_ATTR_CONTEXT
NtfsFindAttributeHelper(PNTFS_VOLUME_INFO Volume
, PNTFS_ATTR_RECORD AttrRecord
, PNTFS_ATTR_RECORD AttrRecordEnd
, ULONG Type
, const WCHAR
*Name
, ULONG NameLength
)
358 while (AttrRecord
< AttrRecordEnd
)
360 if (AttrRecord
->Type
== NTFS_ATTR_TYPE_END
)
363 if (AttrRecord
->Type
== NTFS_ATTR_TYPE_ATTRIBUTE_LIST
)
365 PNTFS_ATTR_CONTEXT Context
;
366 PNTFS_ATTR_CONTEXT ListContext
;
369 PNTFS_ATTR_RECORD ListAttrRecord
;
370 PNTFS_ATTR_RECORD ListAttrRecordEnd
;
372 ListContext
= NtfsPrepareAttributeContext(AttrRecord
);
374 ListSize
= NtfsGetAttributeSize(&ListContext
->Record
);
375 ListBuffer
= MmHeapAlloc(ListSize
);
377 ListAttrRecord
= (PNTFS_ATTR_RECORD
)ListBuffer
;
378 ListAttrRecordEnd
= (PNTFS_ATTR_RECORD
)((PCHAR
)ListBuffer
+ ListSize
);
380 if (NtfsReadAttribute(Volume
, ListContext
, 0, ListBuffer
, ListSize
) == ListSize
)
382 Context
= NtfsFindAttributeHelper(Volume
, ListAttrRecord
, ListAttrRecordEnd
,
383 Type
, Name
, NameLength
);
385 NtfsReleaseAttributeContext(ListContext
);
386 MmHeapFree(ListBuffer
);
393 if (AttrRecord
->Type
== Type
)
395 if (AttrRecord
->NameLength
== NameLength
)
399 AttrName
= (PWCHAR
)((PCHAR
)AttrRecord
+ AttrRecord
->NameOffset
);
400 if (RtlEqualMemory(AttrName
, Name
, NameLength
<< 1))
402 /* Found it, fill up the context and return. */
403 return NtfsPrepareAttributeContext(AttrRecord
);
408 if (AttrRecord
->Length
== 0)
410 AttrRecord
= (PNTFS_ATTR_RECORD
)((PCHAR
)AttrRecord
+ AttrRecord
->Length
);
416 static PNTFS_ATTR_CONTEXT
NtfsFindAttribute(PNTFS_VOLUME_INFO Volume
, PNTFS_MFT_RECORD MftRecord
, ULONG Type
, const WCHAR
*Name
)
418 PNTFS_ATTR_RECORD AttrRecord
;
419 PNTFS_ATTR_RECORD AttrRecordEnd
;
422 AttrRecord
= (PNTFS_ATTR_RECORD
)((PCHAR
)MftRecord
+ MftRecord
->AttributesOffset
);
423 AttrRecordEnd
= (PNTFS_ATTR_RECORD
)((PCHAR
)MftRecord
+ Volume
->MftRecordSize
);
424 for (NameLength
= 0; Name
[NameLength
] != 0; NameLength
++)
427 return NtfsFindAttributeHelper(Volume
, AttrRecord
, AttrRecordEnd
, Type
, Name
, NameLength
);
430 static BOOLEAN
NtfsFixupRecord(PNTFS_VOLUME_INFO Volume
, PNTFS_RECORD Record
)
437 USA
= (USHORT
*)((PCHAR
)Record
+ Record
->USAOffset
);
438 USANumber
= *(USA
++);
439 USACount
= Record
->USACount
- 1; /* Exclude the USA Number. */
440 Block
= (USHORT
*)((PCHAR
)Record
+ Volume
->BootSector
.BytesPerSector
- 2);
444 if (*Block
!= USANumber
)
447 Block
= (USHORT
*)((PCHAR
)Block
+ Volume
->BootSector
.BytesPerSector
);
454 static BOOLEAN
NtfsReadMftRecord(PNTFS_VOLUME_INFO Volume
, ULONG MFTIndex
, PNTFS_MFT_RECORD Buffer
)
458 BytesRead
= NtfsReadAttribute(Volume
, Volume
->MFTContext
, MFTIndex
* Volume
->MftRecordSize
, (PCHAR
)Buffer
, Volume
->MftRecordSize
);
459 if (BytesRead
!= Volume
->MftRecordSize
)
462 /* Apply update sequence array fixups. */
463 return NtfsFixupRecord(Volume
, (PNTFS_RECORD
)Buffer
);
467 VOID
NtfsPrintFile(PNTFS_INDEX_ENTRY IndexEntry
)
470 UCHAR FileNameLength
;
471 CHAR AnsiFileName
[256];
474 FileName
= IndexEntry
->FileName
.FileName
;
475 FileNameLength
= IndexEntry
->FileName
.FileNameLength
;
477 for (i
= 0; i
< FileNameLength
; i
++)
478 AnsiFileName
[i
] = FileName
[i
];
481 DPRINTM(DPRINT_FILESYSTEM
, "- %s (%x)\n", AnsiFileName
, IndexEntry
->Data
.Directory
.IndexedFile
);
485 static BOOLEAN
NtfsCompareFileName(PCHAR FileName
, PNTFS_INDEX_ENTRY IndexEntry
)
487 PWCHAR EntryFileName
;
488 UCHAR EntryFileNameLength
;
491 EntryFileName
= IndexEntry
->FileName
.FileName
;
492 EntryFileNameLength
= IndexEntry
->FileName
.FileNameLength
;
495 NtfsPrintFile(IndexEntry
);
498 if (strlen(FileName
) != EntryFileNameLength
)
501 /* Do case-sensitive compares for Posix file names. */
502 if (IndexEntry
->FileName
.FileNameType
== NTFS_FILE_NAME_POSIX
)
504 for (i
= 0; i
< EntryFileNameLength
; i
++)
505 if (EntryFileName
[i
] != FileName
[i
])
510 for (i
= 0; i
< EntryFileNameLength
; i
++)
511 if (tolower(EntryFileName
[i
]) != tolower(FileName
[i
]))
518 static BOOLEAN
NtfsFindMftRecord(PNTFS_VOLUME_INFO Volume
, ULONG MFTIndex
, PCHAR FileName
, ULONG
*OutMFTIndex
)
520 PNTFS_MFT_RECORD MftRecord
;
522 PNTFS_ATTR_CONTEXT IndexRootCtx
;
523 PNTFS_ATTR_CONTEXT IndexBitmapCtx
;
524 PNTFS_ATTR_CONTEXT IndexAllocationCtx
;
525 PNTFS_INDEX_ROOT IndexRoot
;
526 ULONGLONG BitmapDataSize
;
527 ULONGLONG IndexAllocationSize
;
530 PNTFS_INDEX_ENTRY IndexEntry
, IndexEntryEnd
;
532 ULONG IndexBlockSize
;
534 MftRecord
= MmHeapAlloc(Volume
->MftRecordSize
);
535 if (MftRecord
== NULL
)
540 if (NtfsReadMftRecord(Volume
, MFTIndex
, MftRecord
))
542 Magic
= MftRecord
->Magic
;
544 IndexRootCtx
= NtfsFindAttribute(Volume
, MftRecord
, NTFS_ATTR_TYPE_INDEX_ROOT
, L
"$I30");
545 if (IndexRootCtx
== NULL
)
547 MmHeapFree(MftRecord
);
551 IndexRecord
= MmHeapAlloc(Volume
->IndexRecordSize
);
552 if (IndexRecord
== NULL
)
554 MmHeapFree(MftRecord
);
558 NtfsReadAttribute(Volume
, IndexRootCtx
, 0, IndexRecord
, Volume
->IndexRecordSize
);
559 IndexRoot
= (PNTFS_INDEX_ROOT
)IndexRecord
;
560 IndexEntry
= (PNTFS_INDEX_ENTRY
)((PCHAR
)&IndexRoot
->IndexHeader
+ IndexRoot
->IndexHeader
.EntriesOffset
);
561 /* Index root is always resident. */
562 IndexEntryEnd
= (PNTFS_INDEX_ENTRY
)(IndexRecord
+ IndexRootCtx
->Record
.Resident
.ValueLength
);
563 NtfsReleaseAttributeContext(IndexRootCtx
);
565 DPRINTM(DPRINT_FILESYSTEM
, "IndexRecordSize: %x IndexBlockSize: %x\n", Volume
->IndexRecordSize
, IndexRoot
->IndexBlockSize
);
567 while (IndexEntry
< IndexEntryEnd
&&
568 !(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_END
))
570 if (NtfsCompareFileName(FileName
, IndexEntry
))
572 *OutMFTIndex
= IndexEntry
->Data
.Directory
.IndexedFile
;
573 MmHeapFree(IndexRecord
);
574 MmHeapFree(MftRecord
);
577 IndexEntry
= (PNTFS_INDEX_ENTRY
)((PCHAR
)IndexEntry
+ IndexEntry
->Length
);
580 if (IndexRoot
->IndexHeader
.Flags
& NTFS_LARGE_INDEX
)
582 DPRINTM(DPRINT_FILESYSTEM
, "Large Index!\n");
584 IndexBlockSize
= IndexRoot
->IndexBlockSize
;
586 IndexBitmapCtx
= NtfsFindAttribute(Volume
, MftRecord
, NTFS_ATTR_TYPE_BITMAP
, L
"$I30");
587 if (IndexBitmapCtx
== NULL
)
589 DPRINTM(DPRINT_FILESYSTEM
, "Corrupted filesystem!\n");
590 MmHeapFree(MftRecord
);
593 BitmapDataSize
= NtfsGetAttributeSize(&IndexBitmapCtx
->Record
);
594 DPRINTM(DPRINT_FILESYSTEM
, "BitmapDataSize: %x\n", BitmapDataSize
);
595 BitmapData
= MmHeapAlloc(BitmapDataSize
);
596 if (BitmapData
== NULL
)
598 MmHeapFree(IndexRecord
);
599 MmHeapFree(MftRecord
);
602 NtfsReadAttribute(Volume
, IndexBitmapCtx
, 0, BitmapData
, BitmapDataSize
);
603 NtfsReleaseAttributeContext(IndexBitmapCtx
);
605 IndexAllocationCtx
= NtfsFindAttribute(Volume
, MftRecord
, NTFS_ATTR_TYPE_INDEX_ALLOCATION
, L
"$I30");
606 if (IndexAllocationCtx
== NULL
)
608 DPRINTM(DPRINT_FILESYSTEM
, "Corrupted filesystem!\n");
609 MmHeapFree(BitmapData
);
610 MmHeapFree(IndexRecord
);
611 MmHeapFree(MftRecord
);
614 IndexAllocationSize
= NtfsGetAttributeSize(&IndexAllocationCtx
->Record
);
620 DPRINTM(DPRINT_FILESYSTEM
, "RecordOffset: %x IndexAllocationSize: %x\n", RecordOffset
, IndexAllocationSize
);
621 for (; RecordOffset
< IndexAllocationSize
;)
623 UCHAR Bit
= 1 << ((RecordOffset
/ IndexBlockSize
) & 7);
624 ULONG Byte
= (RecordOffset
/ IndexBlockSize
) >> 3;
625 if ((BitmapData
[Byte
] & Bit
))
627 RecordOffset
+= IndexBlockSize
;
630 if (RecordOffset
>= IndexAllocationSize
)
635 NtfsReadAttribute(Volume
, IndexAllocationCtx
, RecordOffset
, IndexRecord
, IndexBlockSize
);
637 if (!NtfsFixupRecord(Volume
, (PNTFS_RECORD
)IndexRecord
))
643 IndexEntry
= (PNTFS_INDEX_ENTRY
)(IndexRecord
+ 0x18 + *(USHORT
*)(IndexRecord
+ 0x18));
644 IndexEntryEnd
= (PNTFS_INDEX_ENTRY
)(IndexRecord
+ IndexBlockSize
);
646 while (IndexEntry
< IndexEntryEnd
&&
647 !(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_END
))
649 if (NtfsCompareFileName(FileName
, IndexEntry
))
651 DPRINTM(DPRINT_FILESYSTEM
, "File found\n");
652 *OutMFTIndex
= IndexEntry
->Data
.Directory
.IndexedFile
;
653 MmHeapFree(BitmapData
);
654 MmHeapFree(IndexRecord
);
655 MmHeapFree(MftRecord
);
656 NtfsReleaseAttributeContext(IndexAllocationCtx
);
659 IndexEntry
= (PNTFS_INDEX_ENTRY
)((PCHAR
)IndexEntry
+ IndexEntry
->Length
);
662 RecordOffset
+= IndexBlockSize
;
665 NtfsReleaseAttributeContext(IndexAllocationCtx
);
666 MmHeapFree(BitmapData
);
669 MmHeapFree(IndexRecord
);
673 DPRINTM(DPRINT_FILESYSTEM
, "Can't read MFT record\n");
675 MmHeapFree(MftRecord
);
680 static BOOLEAN
NtfsLookupFile(PNTFS_VOLUME_INFO Volume
, PCSTR FileName
, PNTFS_MFT_RECORD MftRecord
, PNTFS_ATTR_CONTEXT
*DataContext
)
682 ULONG NumberOfPathParts
;
684 ULONG CurrentMFTIndex
;
687 DPRINTM(DPRINT_FILESYSTEM
, "NtfsLookupFile() FileName = %s\n", FileName
);
689 CurrentMFTIndex
= NTFS_FILE_ROOT
;
690 NumberOfPathParts
= FsGetNumPathParts(FileName
);
691 for (i
= 0; i
< NumberOfPathParts
; i
++)
693 FsGetFirstNameFromPath(PathPart
, FileName
);
695 for (; (*FileName
!= '\\') && (*FileName
!= '/') && (*FileName
!= '\0'); FileName
++)
699 DPRINTM(DPRINT_FILESYSTEM
, "- Lookup: %s\n", PathPart
);
700 if (!NtfsFindMftRecord(Volume
, CurrentMFTIndex
, PathPart
, &CurrentMFTIndex
))
702 DPRINTM(DPRINT_FILESYSTEM
, "- Failed\n");
705 DPRINTM(DPRINT_FILESYSTEM
, "- Lookup: %x\n", CurrentMFTIndex
);
708 if (!NtfsReadMftRecord(Volume
, CurrentMFTIndex
, MftRecord
))
710 DPRINTM(DPRINT_FILESYSTEM
, "NtfsLookupFile: Can't read MFT record\n");
714 *DataContext
= NtfsFindAttribute(Volume
, MftRecord
, NTFS_ATTR_TYPE_DATA
, L
"");
715 if (*DataContext
== NULL
)
717 DPRINTM(DPRINT_FILESYSTEM
, "NtfsLookupFile: Can't find data attribute\n");
724 LONG
NtfsClose(ULONG FileId
)
726 PNTFS_FILE_HANDLE FileHandle
= FsGetDeviceSpecific(FileId
);
728 NtfsReleaseAttributeContext(FileHandle
->DataContext
);
729 MmHeapFree(FileHandle
);
734 LONG
NtfsGetFileInformation(ULONG FileId
, FILEINFORMATION
* Information
)
736 PNTFS_FILE_HANDLE FileHandle
= FsGetDeviceSpecific(FileId
);
738 RtlZeroMemory(Information
, sizeof(FILEINFORMATION
));
739 Information
->EndingAddress
.LowPart
= (ULONG
)NtfsGetAttributeSize(&FileHandle
->DataContext
->Record
);
740 Information
->CurrentAddress
.LowPart
= FileHandle
->Offset
;
742 DPRINTM(DPRINT_FILESYSTEM
, "NtfsGetFileInformation() FileSize = %d\n",
743 Information
->EndingAddress
.LowPart
);
744 DPRINTM(DPRINT_FILESYSTEM
, "NtfsGetFileInformation() FilePointer = %d\n",
745 Information
->CurrentAddress
.LowPart
);
750 LONG
NtfsOpen(CHAR
* Path
, OPENMODE OpenMode
, ULONG
* FileId
)
752 PNTFS_VOLUME_INFO Volume
;
753 PNTFS_FILE_HANDLE FileHandle
;
754 PNTFS_MFT_RECORD MftRecord
;
760 if (OpenMode
!= OpenReadOnly
)
764 // Get underlying device
766 DeviceId
= FsGetDeviceId(*FileId
);
767 Volume
= NtfsVolumes
[DeviceId
];
769 DPRINTM(DPRINT_FILESYSTEM
, "NtfsOpen() FileName = %s\n", Path
);
772 // Allocate file structure
774 FileHandle
= MmHeapAlloc(sizeof(NTFS_FILE_HANDLE
) + Volume
->MftRecordSize
);
779 RtlZeroMemory(FileHandle
, sizeof(NTFS_FILE_HANDLE
) + Volume
->MftRecordSize
);
780 FileHandle
->Volume
= Volume
;
785 MftRecord
= (PNTFS_MFT_RECORD
)(FileHandle
+ 1);
786 if (!NtfsLookupFile(Volume
, Path
, MftRecord
, &FileHandle
->DataContext
))
788 MmHeapFree(FileHandle
);
795 LONG
NtfsRead(ULONG FileId
, VOID
* Buffer
, ULONG N
, ULONG
* Count
)
797 PNTFS_FILE_HANDLE FileHandle
= FsGetDeviceSpecific(FileId
);
798 ULONGLONG BytesRead64
;
803 BytesRead64
= NtfsReadAttribute(FileHandle
->Volume
, FileHandle
->DataContext
, FileHandle
->Offset
, Buffer
, N
);
804 *Count
= (ULONG
)BytesRead64
;
815 LONG
NtfsSeek(ULONG FileId
, LARGE_INTEGER
* Position
, SEEKMODE SeekMode
)
817 PNTFS_FILE_HANDLE FileHandle
= FsGetDeviceSpecific(FileId
);
819 DPRINTM(DPRINT_FILESYSTEM
, "NtfsSeek() NewFilePointer = %lu\n", Position
->LowPart
);
821 if (SeekMode
!= SeekAbsolute
)
823 if (Position
->HighPart
!= 0)
825 if (Position
->LowPart
>= (ULONG
)NtfsGetAttributeSize(&FileHandle
->DataContext
->Record
))
828 FileHandle
->Offset
= Position
->LowPart
;
832 const DEVVTBL NtfsFuncTable
=
835 NtfsGetFileInformation
,
842 const DEVVTBL
* NtfsMount(ULONG DeviceId
)
844 PNTFS_VOLUME_INFO Volume
;
845 LARGE_INTEGER Position
;
850 // Allocate data for volume information
852 Volume
= MmHeapAlloc(sizeof(NTFS_VOLUME_INFO
));
855 RtlZeroMemory(Volume
, sizeof(NTFS_VOLUME_INFO
));
858 // Read the BootSector
860 Position
.HighPart
= 0;
861 Position
.LowPart
= 0;
862 ret
= ArcSeek(DeviceId
, &Position
, SeekAbsolute
);
868 ret
= ArcRead(DeviceId
, &Volume
->BootSector
, sizeof(Volume
->BootSector
), &Count
);
869 if (ret
!= ESUCCESS
|| Count
!= sizeof(Volume
->BootSector
))
876 // Check if BootSector is valid. If no, return early
878 if (!RtlEqualMemory(Volume
->BootSector
.SystemId
, "NTFS", 4))
885 // Calculate cluster size and MFT record size
887 Volume
->ClusterSize
= Volume
->BootSector
.SectorsPerCluster
* Volume
->BootSector
.BytesPerSector
;
888 if (Volume
->BootSector
.ClustersPerMftRecord
> 0)
889 Volume
->MftRecordSize
= Volume
->BootSector
.ClustersPerMftRecord
* Volume
->ClusterSize
;
891 Volume
->MftRecordSize
= 1 << (-Volume
->BootSector
.ClustersPerMftRecord
);
892 if (Volume
->BootSector
.ClustersPerIndexRecord
> 0)
893 Volume
->IndexRecordSize
= Volume
->BootSector
.ClustersPerIndexRecord
* Volume
->ClusterSize
;
895 Volume
->IndexRecordSize
= 1 << (-Volume
->BootSector
.ClustersPerIndexRecord
);
897 DPRINTM(DPRINT_FILESYSTEM
, "ClusterSize: 0x%x\n", Volume
->ClusterSize
);
898 DPRINTM(DPRINT_FILESYSTEM
, "ClustersPerMftRecord: %d\n", Volume
->BootSector
.ClustersPerMftRecord
);
899 DPRINTM(DPRINT_FILESYSTEM
, "ClustersPerIndexRecord: %d\n", Volume
->BootSector
.ClustersPerIndexRecord
);
900 DPRINTM(DPRINT_FILESYSTEM
, "MftRecordSize: 0x%x\n", Volume
->MftRecordSize
);
901 DPRINTM(DPRINT_FILESYSTEM
, "IndexRecordSize: 0x%x\n", Volume
->IndexRecordSize
);
906 DPRINTM(DPRINT_FILESYSTEM
, "Reading MFT index...\n");
907 Volume
->MasterFileTable
= MmHeapAlloc(Volume
->MftRecordSize
);
908 if (!Volume
->MasterFileTable
)
913 Position
.HighPart
= 0;
914 Position
.LowPart
= Volume
->BootSector
.MftLocation
* Volume
->ClusterSize
;
915 ret
= ArcSeek(DeviceId
, &Position
, SeekAbsolute
);
918 FileSystemError("Failed to seek to Master File Table record.");
919 MmHeapFree(Volume
->MasterFileTable
);
923 ret
= ArcRead(DeviceId
, Volume
->MasterFileTable
, Volume
->MftRecordSize
, &Count
);
924 if (ret
!= ESUCCESS
|| Count
!= Volume
->MftRecordSize
)
926 FileSystemError("Failed to read the Master File Table record.");
927 MmHeapFree(Volume
->MasterFileTable
);
935 Volume
->DeviceId
= DeviceId
;
938 // Search DATA attribute
940 DPRINTM(DPRINT_FILESYSTEM
, "Searching for DATA attribute...\n");
941 Volume
->MFTContext
= NtfsFindAttribute(Volume
, Volume
->MasterFileTable
, NTFS_ATTR_TYPE_DATA
, L
"");
942 if (!Volume
->MFTContext
)
944 FileSystemError("Can't find data attribute for Master File Table.");
945 MmHeapFree(Volume
->MasterFileTable
);
951 // Remember NTFS volume information
953 NtfsVolumes
[DeviceId
] = Volume
;
958 return &NtfsFuncTable
;