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
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 * - No support for compressed files.
23 * - May crash on corrupted filesystem.
29 PNTFS_BOOTSECTOR NtfsBootSector
;
30 ULONG NtfsClusterSize
;
31 ULONG NtfsMftRecordSize
;
32 ULONG NtfsIndexRecordSize
;
33 ULONG NtfsDriveNumber
;
34 ULONG NtfsSectorOfClusterZero
;
35 PNTFS_MFT_RECORD NtfsMasterFileTable
;
36 /* FIXME: NtfsMFTContext is never freed. */
37 PNTFS_ATTR_CONTEXT NtfsMFTContext
;
39 static ULONGLONG
NtfsGetAttributeSize(PNTFS_ATTR_RECORD AttrRecord
)
41 if (AttrRecord
->IsNonResident
)
42 return AttrRecord
->NonResident
.DataSize
;
44 return AttrRecord
->Resident
.ValueLength
;
47 static PUCHAR
NtfsDecodeRun(PUCHAR DataRun
, LONGLONG
*DataRunOffset
, ULONGLONG
*DataRunLength
)
49 UCHAR DataRunOffsetSize
;
50 UCHAR DataRunLengthSize
;
53 DataRunOffsetSize
= (*DataRun
>> 4) & 0xF;
54 DataRunLengthSize
= *DataRun
& 0xF;
58 for (i
= 0; i
< DataRunLengthSize
; i
++)
60 *DataRunLength
+= *DataRun
<< (i
<< 3);
64 /* NTFS 3+ sparse files */
65 if (DataRunOffsetSize
== 0)
71 for (i
= 0; i
< DataRunOffsetSize
- 1; i
++)
73 *DataRunOffset
+= *DataRun
<< (i
<< 3);
76 /* The last byte contains sign so we must process it different way. */
77 *DataRunOffset
= ((CHAR
)(*(DataRun
++)) << (i
<< 3)) + *DataRunOffset
;
80 DPRINTM(DPRINT_FILESYSTEM
, "DataRunOffsetSize: %x\n", DataRunOffsetSize
);
81 DPRINTM(DPRINT_FILESYSTEM
, "DataRunLengthSize: %x\n", DataRunLengthSize
);
82 DPRINTM(DPRINT_FILESYSTEM
, "DataRunOffset: %x\n", *DataRunOffset
);
83 DPRINTM(DPRINT_FILESYSTEM
, "DataRunLength: %x\n", *DataRunLength
);
88 static PNTFS_ATTR_CONTEXT
NtfsPrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord
)
90 PNTFS_ATTR_CONTEXT Context
;
92 Context
= MmHeapAlloc(FIELD_OFFSET(NTFS_ATTR_CONTEXT
, Record
) + AttrRecord
->Length
);
93 RtlCopyMemory(&Context
->Record
, AttrRecord
, AttrRecord
->Length
);
94 if (AttrRecord
->IsNonResident
)
96 LONGLONG DataRunOffset
;
97 ULONGLONG DataRunLength
;
99 Context
->CacheRun
= (PUCHAR
)&Context
->Record
+ Context
->Record
.NonResident
.MappingPairsOffset
;
100 Context
->CacheRunOffset
= 0;
101 Context
->CacheRun
= NtfsDecodeRun(Context
->CacheRun
, &DataRunOffset
, &DataRunLength
);
102 Context
->CacheRunLength
= DataRunLength
;
103 if (DataRunOffset
!= -1)
106 Context
->CacheRunStartLCN
=
107 Context
->CacheRunLastLCN
= DataRunOffset
;
112 Context
->CacheRunStartLCN
= -1;
113 Context
->CacheRunLastLCN
= 0;
115 Context
->CacheRunCurrentOffset
= 0;
121 static VOID
NtfsReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context
)
126 /* FIXME: Optimize for multisector reads. */
127 static BOOLEAN
NtfsDiskRead(ULONGLONG Offset
, ULONGLONG Length
, PCHAR Buffer
)
131 DPRINTM(DPRINT_FILESYSTEM
, "NtfsDiskRead - Offset: %I64d Length: %I64d\n", Offset
, Length
);
132 RtlZeroMemory((PCHAR
)DISKREADBUFFER
, 0x1000);
134 /* I. Read partial first sector if needed */
135 if (Offset
% NtfsBootSector
->BytesPerSector
)
137 if (!MachDiskReadLogicalSectors(NtfsDriveNumber
, NtfsSectorOfClusterZero
+ (Offset
/ NtfsBootSector
->BytesPerSector
), 1, (PCHAR
)DISKREADBUFFER
))
139 ReadLength
= min(Length
, NtfsBootSector
->BytesPerSector
- (Offset
% NtfsBootSector
->BytesPerSector
));
140 RtlCopyMemory(Buffer
, (PCHAR
)DISKREADBUFFER
+ (Offset
% NtfsBootSector
->BytesPerSector
), ReadLength
);
141 Buffer
+= ReadLength
;
142 Length
-= ReadLength
;
143 Offset
+= ReadLength
;
146 /* II. Read all complete 64-sector blocks. */
147 while (Length
>= (ULONGLONG
)64 * (ULONGLONG
)NtfsBootSector
->BytesPerSector
)
149 if (!MachDiskReadLogicalSectors(NtfsDriveNumber
, NtfsSectorOfClusterZero
+ (Offset
/ NtfsBootSector
->BytesPerSector
), 64, (PCHAR
)DISKREADBUFFER
))
151 RtlCopyMemory(Buffer
, (PCHAR
)DISKREADBUFFER
, 64 * NtfsBootSector
->BytesPerSector
);
152 Buffer
+= 64 * NtfsBootSector
->BytesPerSector
;
153 Length
-= 64 * NtfsBootSector
->BytesPerSector
;
154 Offset
+= 64 * NtfsBootSector
->BytesPerSector
;
157 /* III. Read the rest of data */
160 ReadLength
= ((Length
+ NtfsBootSector
->BytesPerSector
- 1) / NtfsBootSector
->BytesPerSector
);
161 if (!MachDiskReadLogicalSectors(NtfsDriveNumber
, NtfsSectorOfClusterZero
+ (Offset
/ NtfsBootSector
->BytesPerSector
), ReadLength
, (PCHAR
)DISKREADBUFFER
))
163 RtlCopyMemory(Buffer
, (PCHAR
)DISKREADBUFFER
, Length
);
169 static ULONGLONG
NtfsReadAttribute(PNTFS_ATTR_CONTEXT Context
, ULONGLONG Offset
, PCHAR Buffer
, ULONGLONG Length
)
173 LONGLONG DataRunOffset
;
174 ULONGLONG DataRunLength
;
175 LONGLONG DataRunStartLCN
;
176 ULONGLONG CurrentOffset
;
177 ULONGLONG ReadLength
;
178 ULONGLONG AlreadyRead
;
180 if (!Context
->Record
.IsNonResident
)
182 if (Offset
> Context
->Record
.Resident
.ValueLength
)
184 if (Offset
+ Length
> Context
->Record
.Resident
.ValueLength
)
185 Length
= Context
->Record
.Resident
.ValueLength
- Offset
;
186 RtlCopyMemory(Buffer
, (PCHAR
)&Context
->Record
+ Context
->Record
.Resident
.ValueOffset
+ Offset
, Length
);
191 * Non-resident attribute
195 * I. Find the corresponding start data run.
200 if(Context
->CacheRunOffset
<= Offset
&& Offset
< Context
->CacheRunOffset
+ Context
->CacheRunLength
* NtfsClusterSize
)
202 DataRun
= Context
->CacheRun
;
203 LastLCN
= Context
->CacheRunLastLCN
;
204 DataRunStartLCN
= Context
->CacheRunStartLCN
;
205 DataRunLength
= Context
->CacheRunLength
;
206 CurrentOffset
= Context
->CacheRunCurrentOffset
;
211 DataRun
= (PUCHAR
)&Context
->Record
+ Context
->Record
.NonResident
.MappingPairsOffset
;
216 DataRun
= NtfsDecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
217 if (DataRunOffset
!= -1)
219 /* Normal data run. */
220 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
221 LastLCN
= DataRunStartLCN
;
225 /* Sparse data run. */
226 DataRunStartLCN
= -1;
229 if (Offset
>= CurrentOffset
&&
230 Offset
< CurrentOffset
+ (DataRunLength
* NtfsClusterSize
))
240 CurrentOffset
+= DataRunLength
* NtfsClusterSize
;
245 * II. Go through the run list and read the data
248 ReadLength
= min(DataRunLength
* NtfsClusterSize
- (Offset
- CurrentOffset
), Length
);
249 if (DataRunStartLCN
== -1)
250 RtlZeroMemory(Buffer
, ReadLength
);
251 if (NtfsDiskRead(DataRunStartLCN
* NtfsClusterSize
+ Offset
- CurrentOffset
, ReadLength
, Buffer
))
253 Length
-= ReadLength
;
254 Buffer
+= ReadLength
;
255 AlreadyRead
+= ReadLength
;
257 if (ReadLength
== DataRunLength
* NtfsClusterSize
- (Offset
- CurrentOffset
))
259 CurrentOffset
+= DataRunLength
* NtfsClusterSize
;
260 DataRun
= NtfsDecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
261 if (DataRunLength
!= (ULONGLONG
)-1)
263 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
264 LastLCN
= DataRunStartLCN
;
267 DataRunStartLCN
= -1;
275 ReadLength
= min(DataRunLength
* NtfsClusterSize
, Length
);
276 if (DataRunStartLCN
== -1)
277 RtlZeroMemory(Buffer
, ReadLength
);
278 else if (!NtfsDiskRead(DataRunStartLCN
* NtfsClusterSize
, ReadLength
, Buffer
))
281 Length
-= ReadLength
;
282 Buffer
+= ReadLength
;
283 AlreadyRead
+= ReadLength
;
285 /* We finished this request, but there still data in this data run. */
286 if (Length
== 0 && ReadLength
!= DataRunLength
* NtfsClusterSize
)
290 * Go to next run in the list.
295 CurrentOffset
+= DataRunLength
* NtfsClusterSize
;
296 DataRun
= NtfsDecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
297 if (DataRunOffset
!= -1)
299 /* Normal data run. */
300 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
301 LastLCN
= DataRunStartLCN
;
305 /* Sparse data run. */
306 DataRunStartLCN
= -1;
312 Context
->CacheRun
= DataRun
;
313 Context
->CacheRunOffset
= Offset
+ AlreadyRead
;
314 Context
->CacheRunStartLCN
= DataRunStartLCN
;
315 Context
->CacheRunLength
= DataRunLength
;
316 Context
->CacheRunLastLCN
= LastLCN
;
317 Context
->CacheRunCurrentOffset
= CurrentOffset
;
322 static PNTFS_ATTR_CONTEXT
NtfsFindAttributeHelper(PNTFS_ATTR_RECORD AttrRecord
, PNTFS_ATTR_RECORD AttrRecordEnd
, ULONG Type
, const WCHAR
*Name
, ULONG NameLength
)
324 while (AttrRecord
< AttrRecordEnd
)
326 if (AttrRecord
->Type
== NTFS_ATTR_TYPE_END
)
329 if (AttrRecord
->Type
== NTFS_ATTR_TYPE_ATTRIBUTE_LIST
)
331 PNTFS_ATTR_CONTEXT Context
;
332 PNTFS_ATTR_CONTEXT ListContext
;
335 PNTFS_ATTR_RECORD ListAttrRecord
;
336 PNTFS_ATTR_RECORD ListAttrRecordEnd
;
338 ListContext
= NtfsPrepareAttributeContext(AttrRecord
);
340 ListSize
= NtfsGetAttributeSize(&ListContext
->Record
);
341 ListBuffer
= MmHeapAlloc(ListSize
);
343 ListAttrRecord
= (PNTFS_ATTR_RECORD
)ListBuffer
;
344 ListAttrRecordEnd
= (PNTFS_ATTR_RECORD
)((PCHAR
)ListBuffer
+ ListSize
);
346 if (NtfsReadAttribute(ListContext
, 0, ListBuffer
, ListSize
) == ListSize
)
348 Context
= NtfsFindAttributeHelper(ListAttrRecord
, ListAttrRecordEnd
,
349 Type
, Name
, NameLength
);
351 NtfsReleaseAttributeContext(ListContext
);
352 MmHeapFree(ListBuffer
);
359 if (AttrRecord
->Type
== Type
)
361 if (AttrRecord
->NameLength
== NameLength
)
365 AttrName
= (PWCHAR
)((PCHAR
)AttrRecord
+ AttrRecord
->NameOffset
);
366 if (RtlEqualMemory(AttrName
, Name
, NameLength
<< 1))
368 /* Found it, fill up the context and return. */
369 return NtfsPrepareAttributeContext(AttrRecord
);
374 AttrRecord
= (PNTFS_ATTR_RECORD
)((PCHAR
)AttrRecord
+ AttrRecord
->Length
);
380 static PNTFS_ATTR_CONTEXT
NtfsFindAttribute(PNTFS_MFT_RECORD MftRecord
, ULONG Type
, const WCHAR
*Name
)
382 PNTFS_ATTR_RECORD AttrRecord
;
383 PNTFS_ATTR_RECORD AttrRecordEnd
;
386 AttrRecord
= (PNTFS_ATTR_RECORD
)((PCHAR
)MftRecord
+ MftRecord
->AttributesOffset
);
387 AttrRecordEnd
= (PNTFS_ATTR_RECORD
)((PCHAR
)MftRecord
+ NtfsMftRecordSize
);
388 for (NameLength
= 0; Name
[NameLength
] != 0; NameLength
++)
391 return NtfsFindAttributeHelper(AttrRecord
, AttrRecordEnd
, Type
, Name
, NameLength
);
394 static BOOLEAN
NtfsFixupRecord(PNTFS_RECORD Record
)
401 USA
= (USHORT
*)((PCHAR
)Record
+ Record
->USAOffset
);
402 USANumber
= *(USA
++);
403 USACount
= Record
->USACount
- 1; /* Exclude the USA Number. */
404 Block
= (USHORT
*)((PCHAR
)Record
+ NtfsBootSector
->BytesPerSector
- 2);
408 if (*Block
!= USANumber
)
411 Block
= (USHORT
*)((PCHAR
)Block
+ NtfsBootSector
->BytesPerSector
);
418 static BOOLEAN
NtfsReadMftRecord(ULONG MFTIndex
, PNTFS_MFT_RECORD Buffer
)
422 BytesRead
= NtfsReadAttribute(NtfsMFTContext
, MFTIndex
* NtfsMftRecordSize
, (PCHAR
)Buffer
, NtfsMftRecordSize
);
423 if (BytesRead
!= NtfsMftRecordSize
)
426 /* Apply update sequence array fixups. */
427 return NtfsFixupRecord((PNTFS_RECORD
)Buffer
);
431 VOID
NtfsPrintFile(PNTFS_INDEX_ENTRY IndexEntry
)
434 UCHAR FileNameLength
;
435 CHAR AnsiFileName
[256];
438 FileName
= IndexEntry
->FileName
.FileName
;
439 FileNameLength
= IndexEntry
->FileName
.FileNameLength
;
441 for (i
= 0; i
< FileNameLength
; i
++)
442 AnsiFileName
[i
] = FileName
[i
];
445 DPRINTM(DPRINT_FILESYSTEM
, "- %s (%x)\n", AnsiFileName
, IndexEntry
->Data
.Directory
.IndexedFile
);
449 static BOOLEAN
NtfsCompareFileName(PCHAR FileName
, PNTFS_INDEX_ENTRY IndexEntry
)
451 PWCHAR EntryFileName
;
452 UCHAR EntryFileNameLength
;
455 EntryFileName
= IndexEntry
->FileName
.FileName
;
456 EntryFileNameLength
= IndexEntry
->FileName
.FileNameLength
;
459 NtfsPrintFile(IndexEntry
);
462 if (strlen(FileName
) != EntryFileNameLength
)
465 /* Do case-sensitive compares for Posix file names. */
466 if (IndexEntry
->FileName
.FileNameType
== NTFS_FILE_NAME_POSIX
)
468 for (i
= 0; i
< EntryFileNameLength
; i
++)
469 if (EntryFileName
[i
] != FileName
[i
])
474 for (i
= 0; i
< EntryFileNameLength
; i
++)
475 if (tolower(EntryFileName
[i
]) != tolower(FileName
[i
]))
482 static BOOLEAN
NtfsFindMftRecord(ULONG MFTIndex
, PCHAR FileName
, ULONG
*OutMFTIndex
)
484 PNTFS_MFT_RECORD MftRecord
;
486 PNTFS_ATTR_CONTEXT IndexRootCtx
;
487 PNTFS_ATTR_CONTEXT IndexBitmapCtx
;
488 PNTFS_ATTR_CONTEXT IndexAllocationCtx
;
489 PNTFS_INDEX_ROOT IndexRoot
;
490 ULONGLONG BitmapDataSize
;
491 ULONGLONG IndexAllocationSize
;
494 PNTFS_INDEX_ENTRY IndexEntry
, IndexEntryEnd
;
496 ULONG IndexBlockSize
;
498 MftRecord
= MmHeapAlloc(NtfsMftRecordSize
);
499 if (MftRecord
== NULL
)
504 if (NtfsReadMftRecord(MFTIndex
, MftRecord
))
506 Magic
= MftRecord
->Magic
;
508 IndexRootCtx
= NtfsFindAttribute(MftRecord
, NTFS_ATTR_TYPE_INDEX_ROOT
, L
"$I30");
509 if (IndexRootCtx
== NULL
)
511 MmHeapFree(MftRecord
);
515 IndexRecord
= MmHeapAlloc(NtfsIndexRecordSize
);
516 if (IndexRecord
== NULL
)
518 MmHeapFree(MftRecord
);
522 NtfsReadAttribute(IndexRootCtx
, 0, IndexRecord
, NtfsIndexRecordSize
);
523 IndexRoot
= (PNTFS_INDEX_ROOT
)IndexRecord
;
524 IndexEntry
= (PNTFS_INDEX_ENTRY
)((PCHAR
)&IndexRoot
->IndexHeader
+ IndexRoot
->IndexHeader
.EntriesOffset
);
525 /* Index root is always resident. */
526 IndexEntryEnd
= (PNTFS_INDEX_ENTRY
)(IndexRecord
+ IndexRootCtx
->Record
.Resident
.ValueLength
);
527 NtfsReleaseAttributeContext(IndexRootCtx
);
529 DPRINTM(DPRINT_FILESYSTEM
, "NtfsIndexRecordSize: %x IndexBlockSize: %x\n", NtfsIndexRecordSize
, IndexRoot
->IndexBlockSize
);
531 while (IndexEntry
< IndexEntryEnd
&&
532 !(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_END
))
534 if (NtfsCompareFileName(FileName
, IndexEntry
))
536 *OutMFTIndex
= IndexEntry
->Data
.Directory
.IndexedFile
;
537 MmHeapFree(IndexRecord
);
538 MmHeapFree(MftRecord
);
541 IndexEntry
= (PNTFS_INDEX_ENTRY
)((PCHAR
)IndexEntry
+ IndexEntry
->Length
);
544 if (IndexRoot
->IndexHeader
.Flags
& NTFS_LARGE_INDEX
)
546 DPRINTM(DPRINT_FILESYSTEM
, "Large Index!\n");
548 IndexBlockSize
= IndexRoot
->IndexBlockSize
;
550 IndexBitmapCtx
= NtfsFindAttribute(MftRecord
, NTFS_ATTR_TYPE_BITMAP
, L
"$I30");
551 if (IndexBitmapCtx
== NULL
)
553 DPRINTM(DPRINT_FILESYSTEM
, "Corrupted filesystem!\n");
554 MmHeapFree(MftRecord
);
557 BitmapDataSize
= NtfsGetAttributeSize(&IndexBitmapCtx
->Record
);
558 DPRINTM(DPRINT_FILESYSTEM
, "BitmapDataSize: %x\n", BitmapDataSize
);
559 BitmapData
= MmHeapAlloc(BitmapDataSize
);
560 if (BitmapData
== NULL
)
562 MmHeapFree(IndexRecord
);
563 MmHeapFree(MftRecord
);
566 NtfsReadAttribute(IndexBitmapCtx
, 0, BitmapData
, BitmapDataSize
);
567 NtfsReleaseAttributeContext(IndexBitmapCtx
);
569 IndexAllocationCtx
= NtfsFindAttribute(MftRecord
, NTFS_ATTR_TYPE_INDEX_ALLOCATION
, L
"$I30");
570 if (IndexAllocationCtx
== NULL
)
572 DPRINTM(DPRINT_FILESYSTEM
, "Corrupted filesystem!\n");
573 MmHeapFree(BitmapData
);
574 MmHeapFree(IndexRecord
);
575 MmHeapFree(MftRecord
);
578 IndexAllocationSize
= NtfsGetAttributeSize(&IndexAllocationCtx
->Record
);
584 DPRINTM(DPRINT_FILESYSTEM
, "RecordOffset: %x IndexAllocationSize: %x\n", RecordOffset
, IndexAllocationSize
);
585 for (; RecordOffset
< IndexAllocationSize
;)
587 UCHAR Bit
= 1 << ((RecordOffset
/ IndexBlockSize
) & 7);
588 ULONG Byte
= (RecordOffset
/ IndexBlockSize
) >> 3;
589 if ((BitmapData
[Byte
] & Bit
))
591 RecordOffset
+= IndexBlockSize
;
594 if (RecordOffset
>= IndexAllocationSize
)
599 NtfsReadAttribute(IndexAllocationCtx
, RecordOffset
, IndexRecord
, IndexBlockSize
);
601 if (!NtfsFixupRecord((PNTFS_RECORD
)IndexRecord
))
607 IndexEntry
= (PNTFS_INDEX_ENTRY
)(IndexRecord
+ 0x18 + *(USHORT
*)(IndexRecord
+ 0x18));
608 IndexEntryEnd
= (PNTFS_INDEX_ENTRY
)(IndexRecord
+ IndexBlockSize
);
610 while (IndexEntry
< IndexEntryEnd
&&
611 !(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_END
))
613 if (NtfsCompareFileName(FileName
, IndexEntry
))
615 DPRINTM(DPRINT_FILESYSTEM
, "File found\n");
616 *OutMFTIndex
= IndexEntry
->Data
.Directory
.IndexedFile
;
617 MmHeapFree(BitmapData
);
618 MmHeapFree(IndexRecord
);
619 MmHeapFree(MftRecord
);
620 NtfsReleaseAttributeContext(IndexAllocationCtx
);
623 IndexEntry
= (PNTFS_INDEX_ENTRY
)((PCHAR
)IndexEntry
+ IndexEntry
->Length
);
626 RecordOffset
+= IndexBlockSize
;
629 NtfsReleaseAttributeContext(IndexAllocationCtx
);
630 MmHeapFree(BitmapData
);
633 MmHeapFree(IndexRecord
);
637 DPRINTM(DPRINT_FILESYSTEM
, "Can't read MFT record\n");
639 MmHeapFree(MftRecord
);
644 static BOOLEAN
NtfsLookupFile(PCSTR FileName
, PNTFS_MFT_RECORD MftRecord
, PNTFS_ATTR_CONTEXT
*DataContext
)
646 ULONG NumberOfPathParts
;
648 ULONG CurrentMFTIndex
;
651 DPRINTM(DPRINT_FILESYSTEM
, "NtfsLookupFile() FileName = %s\n", FileName
);
653 CurrentMFTIndex
= NTFS_FILE_ROOT
;
654 NumberOfPathParts
= FsGetNumPathParts(FileName
);
655 for (i
= 0; i
< NumberOfPathParts
; i
++)
657 FsGetFirstNameFromPath(PathPart
, FileName
);
659 for (; (*FileName
!= '\\') && (*FileName
!= '/') && (*FileName
!= '\0'); FileName
++)
663 DPRINTM(DPRINT_FILESYSTEM
, "- Lookup: %s\n", PathPart
);
664 if (!NtfsFindMftRecord(CurrentMFTIndex
, PathPart
, &CurrentMFTIndex
))
666 DPRINTM(DPRINT_FILESYSTEM
, "- Failed\n");
669 DPRINTM(DPRINT_FILESYSTEM
, "- Lookup: %x\n", CurrentMFTIndex
);
672 if (!NtfsReadMftRecord(CurrentMFTIndex
, MftRecord
))
674 DPRINTM(DPRINT_FILESYSTEM
, "NtfsLookupFile: Can't read MFT record\n");
678 *DataContext
= NtfsFindAttribute(MftRecord
, NTFS_ATTR_TYPE_DATA
, L
"");
679 if (*DataContext
== NULL
)
681 DPRINTM(DPRINT_FILESYSTEM
, "NtfsLookupFile: Can't find data attribute\n");
688 BOOLEAN
NtfsOpenVolume(UCHAR DriveNumber
, ULONGLONG VolumeStartSector
, ULONGLONG PartitionSectorCount
)
690 NtfsBootSector
= (PNTFS_BOOTSECTOR
)DISKREADBUFFER
;
692 DPRINTM(DPRINT_FILESYSTEM
, "NtfsOpenVolume() DriveNumber = 0x%x VolumeStartSector = 0x%x\n", DriveNumber
, VolumeStartSector
);
694 if (!MachDiskReadLogicalSectors(DriveNumber
, VolumeStartSector
, 1, (PCHAR
)DISKREADBUFFER
))
696 FileSystemError("Failed to read the boot sector.");
700 if (!RtlEqualMemory(NtfsBootSector
->SystemId
, "NTFS", 4))
702 FileSystemError("Invalid NTFS signature.");
706 NtfsBootSector
= MmHeapAlloc(NtfsBootSector
->BytesPerSector
);
707 if (NtfsBootSector
== NULL
)
712 RtlCopyMemory(NtfsBootSector
, (PCHAR
)DISKREADBUFFER
, ((PNTFS_BOOTSECTOR
)DISKREADBUFFER
)->BytesPerSector
);
714 NtfsClusterSize
= NtfsBootSector
->SectorsPerCluster
* NtfsBootSector
->BytesPerSector
;
715 if (NtfsBootSector
->ClustersPerMftRecord
> 0)
716 NtfsMftRecordSize
= NtfsBootSector
->ClustersPerMftRecord
* NtfsClusterSize
;
718 NtfsMftRecordSize
= 1 << (-NtfsBootSector
->ClustersPerMftRecord
);
719 if (NtfsBootSector
->ClustersPerIndexRecord
> 0)
720 NtfsIndexRecordSize
= NtfsBootSector
->ClustersPerIndexRecord
* NtfsClusterSize
;
722 NtfsIndexRecordSize
= 1 << (-NtfsBootSector
->ClustersPerIndexRecord
);
724 DPRINTM(DPRINT_FILESYSTEM
, "NtfsClusterSize: 0x%x\n", NtfsClusterSize
);
725 DPRINTM(DPRINT_FILESYSTEM
, "ClustersPerMftRecord: %d\n", NtfsBootSector
->ClustersPerMftRecord
);
726 DPRINTM(DPRINT_FILESYSTEM
, "ClustersPerIndexRecord: %d\n", NtfsBootSector
->ClustersPerIndexRecord
);
727 DPRINTM(DPRINT_FILESYSTEM
, "NtfsMftRecordSize: 0x%x\n", NtfsMftRecordSize
);
728 DPRINTM(DPRINT_FILESYSTEM
, "NtfsIndexRecordSize: 0x%x\n", NtfsIndexRecordSize
);
730 NtfsDriveNumber
= DriveNumber
;
731 NtfsSectorOfClusterZero
= VolumeStartSector
;
733 DPRINTM(DPRINT_FILESYSTEM
, "Reading MFT index...\n");
734 if (!MachDiskReadLogicalSectors(DriveNumber
,
735 NtfsSectorOfClusterZero
+
736 (NtfsBootSector
->MftLocation
* NtfsBootSector
->SectorsPerCluster
),
737 NtfsMftRecordSize
/ NtfsBootSector
->BytesPerSector
, (PCHAR
)DISKREADBUFFER
))
739 FileSystemError("Failed to read the Master File Table record.");
743 NtfsMasterFileTable
= MmHeapAlloc(NtfsMftRecordSize
);
744 if (NtfsMasterFileTable
== NULL
)
746 MmHeapFree(NtfsBootSector
);
750 RtlCopyMemory(NtfsMasterFileTable
, (PCHAR
)DISKREADBUFFER
, NtfsMftRecordSize
);
752 DPRINTM(DPRINT_FILESYSTEM
, "Searching for DATA attribute...\n");
753 NtfsMFTContext
= NtfsFindAttribute(NtfsMasterFileTable
, NTFS_ATTR_TYPE_DATA
, L
"");
754 if (NtfsMFTContext
== NULL
)
756 FileSystemError("Can't find data attribute for Master File Table.");
763 FILE* NtfsOpenFile(PCSTR FileName
)
765 PNTFS_FILE_HANDLE FileHandle
;
766 PNTFS_MFT_RECORD MftRecord
;
768 FileHandle
= MmHeapAlloc(sizeof(NTFS_FILE_HANDLE
) + NtfsMftRecordSize
);
769 if (FileHandle
== NULL
)
774 MftRecord
= (PNTFS_MFT_RECORD
)(FileHandle
+ 1);
775 if (!NtfsLookupFile(FileName
, MftRecord
, &FileHandle
->DataContext
))
777 MmHeapFree(FileHandle
);
781 FileHandle
->Offset
= 0;
783 return (FILE*)FileHandle
;
786 VOID
NtfsCloseFile(FILE *File
)
788 PNTFS_FILE_HANDLE FileHandle
= (PNTFS_FILE_HANDLE
)File
;
789 NtfsReleaseAttributeContext(FileHandle
->DataContext
);
790 MmHeapFree(FileHandle
);
793 BOOLEAN
NtfsReadFile(FILE *File
, ULONG BytesToRead
, ULONG
* BytesRead
, PVOID Buffer
)
795 PNTFS_FILE_HANDLE FileHandle
= (PNTFS_FILE_HANDLE
)File
;
796 ULONGLONG BytesRead64
;
797 BytesRead64
= NtfsReadAttribute(FileHandle
->DataContext
, FileHandle
->Offset
, Buffer
, BytesToRead
);
800 *BytesRead
= (ULONG
)BytesRead64
;
801 FileHandle
->Offset
+= BytesRead64
;
807 ULONG
NtfsGetFileSize(FILE *File
)
809 PNTFS_FILE_HANDLE FileHandle
= (PNTFS_FILE_HANDLE
)File
;
810 return (ULONG
)NtfsGetAttributeSize(&FileHandle
->DataContext
->Record
);
813 VOID
NtfsSetFilePointer(FILE *File
, ULONG NewFilePointer
)
815 PNTFS_FILE_HANDLE FileHandle
= (PNTFS_FILE_HANDLE
)File
;
816 FileHandle
->Offset
= NewFilePointer
;
819 ULONG
NtfsGetFilePointer(FILE *File
)
821 PNTFS_FILE_HANDLE FileHandle
= (PNTFS_FILE_HANDLE
)File
;
822 return FileHandle
->Offset
;
825 const FS_VTBL NtfsVtbl
= {