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 * - No attribute list support.
24 * - May crash on corrupted filesystem.
39 PNTFS_BOOTSECTOR NtfsBootSector
;
40 ULONG NtfsClusterSize
;
41 ULONG NtfsMftRecordSize
;
42 ULONG NtfsIndexRecordSize
;
43 ULONG NtfsDriveNumber
;
44 ULONG NtfsSectorOfClusterZero
;
45 PNTFS_MFT_RECORD NtfsMasterFileTable
;
46 NTFS_ATTR_CONTEXT NtfsMFTContext
;
48 PUCHAR
NtfsDecodeRun(PUCHAR DataRun
, LONGLONG
*DataRunOffset
, ULONGLONG
*DataRunLength
)
50 UCHAR DataRunOffsetSize
;
51 UCHAR DataRunLengthSize
;
54 DataRunOffsetSize
= (*DataRun
>> 4) & 0xF;
55 DataRunLengthSize
= *DataRun
& 0xF;
59 for (i
= 0; i
< DataRunLengthSize
; i
++)
61 *DataRunLength
+= *DataRun
<< (i
<< 3);
65 /* NTFS 3+ sparse files */
66 if (DataRunOffsetSize
== 0)
72 for (i
= 0; i
< DataRunOffsetSize
- 1; i
++)
74 *DataRunOffset
+= *DataRun
<< (i
<< 3);
77 /* The last byte contains sign so we must process it different way. */
78 *DataRunOffset
= ((CHAR
)(*(DataRun
++)) << (i
<< 3)) + *DataRunOffset
;
81 DbgPrint((DPRINT_FILESYSTEM
, "DataRunOffsetSize: %x\n", DataRunOffsetSize
));
82 DbgPrint((DPRINT_FILESYSTEM
, "DataRunLengthSize: %x\n", DataRunLengthSize
));
83 DbgPrint((DPRINT_FILESYSTEM
, "DataRunOffset: %x\n", *DataRunOffset
));
84 DbgPrint((DPRINT_FILESYSTEM
, "DataRunLength: %x\n", *DataRunLength
));
89 /* FIXME: Add support for attribute lists! */
90 BOOL
NtfsFindAttribute(PNTFS_ATTR_CONTEXT Context
, PNTFS_MFT_RECORD MftRecord
, ULONG Type
, PWCHAR Name
)
92 PNTFS_ATTR_RECORD AttrRecord
;
93 PNTFS_ATTR_RECORD AttrRecordEnd
;
97 AttrRecord
= (PNTFS_ATTR_RECORD
)((PCHAR
)MftRecord
+ MftRecord
->AttributesOffset
);
98 AttrRecordEnd
= (PNTFS_ATTR_RECORD
)((PCHAR
)MftRecord
+ NtfsMftRecordSize
);
99 for (NameLength
= 0; Name
[NameLength
] != 0; NameLength
++)
102 while (AttrRecord
< AttrRecordEnd
)
104 if (AttrRecord
->Type
== NTFS_ATTR_TYPE_END
)
107 if (AttrRecord
->Type
== Type
)
109 if (AttrRecord
->NameLength
== NameLength
)
111 AttrName
= (PWCHAR
)((PCHAR
)AttrRecord
+ AttrRecord
->NameOffset
);
112 if (!RtlCompareMemory(AttrName
, Name
, NameLength
<< 1))
114 /* Found it, fill up the context and return. */
115 Context
->Record
= AttrRecord
;
116 if (AttrRecord
->IsNonResident
)
118 LONGLONG DataRunOffset
;
119 ULONGLONG DataRunLength
;
121 Context
->CacheRun
= (PUCHAR
)Context
->Record
+ Context
->Record
->NonResident
.MappingPairsOffset
;
122 Context
->CacheRunOffset
= 0;
123 Context
->CacheRun
= NtfsDecodeRun(Context
->CacheRun
, &DataRunOffset
, &DataRunLength
);
124 Context
->CacheRunLength
= DataRunLength
;
125 if (DataRunOffset
!= -1)
128 Context
->CacheRunStartLCN
=
129 Context
->CacheRunLastLCN
= DataRunOffset
;
134 Context
->CacheRunStartLCN
= -1;
135 Context
->CacheRunLastLCN
= 0;
137 Context
->CacheRunCurrentOffset
= 0;
144 AttrRecord
= (PNTFS_ATTR_RECORD
)((PCHAR
)AttrRecord
+ AttrRecord
->Length
);
150 /* FIXME: Optimize for multisector reads. */
151 BOOL
NtfsDiskRead(ULONGLONG Offset
, ULONGLONG Length
, PCHAR Buffer
)
155 DbgPrint((DPRINT_FILESYSTEM
, "NtfsDiskRead - Offset: %I64d Length: %I64d\n", Offset
, Length
));
156 RtlZeroMemory((PCHAR
)DISKREADBUFFER
, 0x1000);
158 /* I. Read partial first sector if needed */
159 if (Offset
% NtfsBootSector
->BytesPerSector
)
161 if (!MachDiskReadLogicalSectors(NtfsDriveNumber
, NtfsSectorOfClusterZero
+ (Offset
/ NtfsBootSector
->BytesPerSector
), 1, (PCHAR
)DISKREADBUFFER
))
163 ReadLength
= min(Length
, NtfsBootSector
->BytesPerSector
- (Offset
% NtfsBootSector
->BytesPerSector
));
164 RtlCopyMemory(Buffer
, (PCHAR
)DISKREADBUFFER
+ (Offset
% NtfsBootSector
->BytesPerSector
), ReadLength
);
165 Buffer
+= ReadLength
;
166 Length
-= ReadLength
;
167 Offset
+= ReadLength
;
170 /* II. Read all complete 64-sector blocks. */
171 while (Length
>= 64 * NtfsBootSector
->BytesPerSector
)
173 if (!MachDiskReadLogicalSectors(NtfsDriveNumber
, NtfsSectorOfClusterZero
+ (Offset
/ NtfsBootSector
->BytesPerSector
), 64, (PCHAR
)DISKREADBUFFER
))
175 RtlCopyMemory(Buffer
, (PCHAR
)DISKREADBUFFER
, 64 * NtfsBootSector
->BytesPerSector
);
176 Buffer
+= 64 * NtfsBootSector
->BytesPerSector
;
177 Length
-= 64 * NtfsBootSector
->BytesPerSector
;
178 Offset
+= 64 * NtfsBootSector
->BytesPerSector
;
181 /* III. Read the rest of data */
184 ReadLength
= ((Length
+ NtfsBootSector
->BytesPerSector
- 1) / NtfsBootSector
->BytesPerSector
);
185 if (!MachDiskReadLogicalSectors(NtfsDriveNumber
, NtfsSectorOfClusterZero
+ (Offset
/ NtfsBootSector
->BytesPerSector
), ReadLength
, (PCHAR
)DISKREADBUFFER
))
187 RtlCopyMemory(Buffer
, (PCHAR
)DISKREADBUFFER
, Length
);
193 ULONGLONG
NtfsReadAttribute(PNTFS_ATTR_CONTEXT Context
, ULONGLONG Offset
, PCHAR Buffer
, ULONGLONG Length
)
197 LONGLONG DataRunOffset
;
198 ULONGLONG DataRunLength
;
199 LONGLONG DataRunStartLCN
;
200 ULONGLONG CurrentOffset
;
201 ULONGLONG ReadLength
;
202 ULONGLONG AlreadyRead
;
204 if (!Context
->Record
->IsNonResident
)
206 if (Offset
> Context
->Record
->Resident
.ValueLength
)
208 if (Offset
+ Length
> Context
->Record
->Resident
.ValueLength
)
209 Length
= Context
->Record
->Resident
.ValueLength
- Offset
;
210 RtlCopyMemory(Buffer
, (PCHAR
)Context
->Record
+ Context
->Record
->Resident
.ValueOffset
+ Offset
, Length
);
215 * Non-resident attribute
219 * I. Find the corresponding start data run.
222 if (Context
->CacheRunOffset
== Offset
)
224 DataRun
= Context
->CacheRun
;
225 LastLCN
= Context
->CacheRunLastLCN
;
226 DataRunStartLCN
= Context
->CacheRunStartLCN
;
227 DataRunLength
= Context
->CacheRunLength
;
228 CurrentOffset
= Context
->CacheRunCurrentOffset
;
233 DataRun
= (PUCHAR
)Context
->Record
+ Context
->Record
->NonResident
.MappingPairsOffset
;
238 DataRun
= NtfsDecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
239 if (DataRunOffset
!= -1)
241 /* Normal data run. */
242 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
243 LastLCN
= DataRunStartLCN
;
247 /* Sparse data run. */
248 DataRunStartLCN
= -1;
251 if (Offset
>= CurrentOffset
&&
252 Offset
< CurrentOffset
+ (DataRunLength
* NtfsClusterSize
))
262 CurrentOffset
+= DataRunLength
* NtfsClusterSize
;
267 * II. Go through the run list and read the data
273 ReadLength
= min(DataRunLength
* NtfsClusterSize
, Length
);
274 if (DataRunStartLCN
== -1)
275 RtlZeroMemory(Buffer
, ReadLength
);
276 else if (!NtfsDiskRead(DataRunStartLCN
* NtfsClusterSize
+ Offset
- CurrentOffset
, ReadLength
, Buffer
))
278 Length
-= ReadLength
;
279 Buffer
+= ReadLength
;
280 AlreadyRead
+= ReadLength
;
282 /* We finished this request, but there still data in this data run. */
283 if (Length
== 0 && ReadLength
!= DataRunLength
* NtfsClusterSize
)
287 * Go to next run in the list.
292 DataRun
= NtfsDecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
293 if (DataRunOffset
!= -1)
295 /* Normal data run. */
296 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
297 LastLCN
= DataRunStartLCN
;
301 /* Sparse data run. */
302 DataRunStartLCN
= -1;
304 CurrentOffset
+= DataRunLength
* NtfsClusterSize
;
307 Context
->CacheRun
= DataRun
;
308 Context
->CacheRunOffset
= Offset
+ AlreadyRead
;
309 Context
->CacheRunStartLCN
= DataRunStartLCN
;
310 Context
->CacheRunLength
= DataRunLength
;
311 Context
->CacheRunLastLCN
= LastLCN
;
312 Context
->CacheRunCurrentOffset
= CurrentOffset
;
317 BOOL
NtfsFixupRecord(PNTFS_RECORD Record
)
324 USA
= (USHORT
*)((PCHAR
)Record
+ Record
->USAOffset
);
325 USANumber
= *(USA
++);
326 USACount
= Record
->USACount
- 1; /* Exclude the USA Number. */
327 Block
= (USHORT
*)((PCHAR
)Record
+ NtfsBootSector
->BytesPerSector
- 2);
331 if (*Block
!= USANumber
)
334 Block
= (USHORT
*)((PCHAR
)Block
+ NtfsBootSector
->BytesPerSector
);
341 BOOL
NtfsReadMftRecord(ULONG MFTIndex
, PNTFS_MFT_RECORD Buffer
)
345 BytesRead
= NtfsReadAttribute(&NtfsMFTContext
, MFTIndex
* NtfsMftRecordSize
, (PCHAR
)Buffer
, NtfsMftRecordSize
);
346 if (BytesRead
!= NtfsMftRecordSize
)
349 /* Apply update sequence array fixups. */
350 return NtfsFixupRecord((PNTFS_RECORD
)Buffer
);
354 VOID
NtfsPrintFile(PNTFS_INDEX_ENTRY IndexEntry
)
357 UCHAR FileNameLength
;
358 CHAR AnsiFileName
[256];
361 FileName
= IndexEntry
->FileName
.FileName
;
362 FileNameLength
= IndexEntry
->FileName
.FileNameLength
;
364 for (i
= 0; i
< FileNameLength
; i
++)
365 AnsiFileName
[i
] = FileName
[i
];
368 DbgPrint((DPRINT_FILESYSTEM
, "- %s (%x)\n", AnsiFileName
, IndexEntry
->Data
.Directory
.IndexedFile
));
372 BOOL
NtfsCompareFileName(PCHAR FileName
, PNTFS_INDEX_ENTRY IndexEntry
)
374 PWCHAR EntryFileName
;
375 UCHAR EntryFileNameLength
;
378 EntryFileName
= IndexEntry
->FileName
.FileName
;
379 EntryFileNameLength
= IndexEntry
->FileName
.FileNameLength
;
382 NtfsPrintFile(IndexEntry
);
385 if (strlen(FileName
) != EntryFileNameLength
)
388 /* Do case-sensitive compares for Posix file names. */
389 if (IndexEntry
->FileName
.FileNameType
== NTFS_FILE_NAME_POSIX
)
391 for (i
= 0; i
< EntryFileNameLength
; i
++)
392 if (EntryFileName
[i
] != FileName
[i
])
397 for (i
= 0; i
< EntryFileNameLength
; i
++)
398 if (tolower(EntryFileName
[i
]) != tolower(FileName
[i
]))
405 BOOL
NtfsFindMftRecord(ULONG MFTIndex
, PCHAR FileName
, ULONG
*OutMFTIndex
)
407 PNTFS_MFT_RECORD MftRecord
;
409 NTFS_ATTR_CONTEXT IndexRootCtx
;
410 NTFS_ATTR_CONTEXT IndexBitmapCtx
;
411 NTFS_ATTR_CONTEXT IndexAllocationCtx
;
412 PNTFS_INDEX_ROOT IndexRoot
;
413 ULONGLONG BitmapDataSize
;
414 ULONGLONG IndexAllocationSize
;
417 PNTFS_INDEX_ENTRY IndexEntry
, IndexEntryEnd
;
419 ULONG IndexBlockSize
;
421 MftRecord
= MmAllocateMemory(NtfsMftRecordSize
);
422 if (MftRecord
== NULL
)
427 if (NtfsReadMftRecord(MFTIndex
, MftRecord
))
429 Magic
= MftRecord
->Magic
;
431 if (!NtfsFindAttribute(&IndexRootCtx
, MftRecord
, NTFS_ATTR_TYPE_INDEX_ROOT
, L
"$I30"))
433 MmFreeMemory(MftRecord
);
437 IndexRecord
= MmAllocateMemory(NtfsIndexRecordSize
);
438 if (IndexRecord
== NULL
)
440 MmFreeMemory(MftRecord
);
444 NtfsReadAttribute(&IndexRootCtx
, 0, IndexRecord
, NtfsIndexRecordSize
);
445 IndexRoot
= (PNTFS_INDEX_ROOT
)IndexRecord
;
446 IndexEntry
= (PNTFS_INDEX_ENTRY
)((PCHAR
)&IndexRoot
->IndexHeader
+ IndexRoot
->IndexHeader
.EntriesOffset
);
447 /* Index root is always resident. */
448 IndexEntryEnd
= (PNTFS_INDEX_ENTRY
)(IndexRecord
+ IndexRootCtx
.Record
->Resident
.ValueLength
);
450 DbgPrint((DPRINT_FILESYSTEM
, "NtfsIndexRecordSize: %x IndexBlockSize: %x\n", NtfsIndexRecordSize
, IndexRoot
->IndexBlockSize
));
452 while (IndexEntry
< IndexEntryEnd
&&
453 !(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_END
))
455 if (NtfsCompareFileName(FileName
, IndexEntry
))
457 *OutMFTIndex
= IndexEntry
->Data
.Directory
.IndexedFile
;
458 MmFreeMemory(IndexRecord
);
459 MmFreeMemory(MftRecord
);
462 IndexEntry
= (PNTFS_INDEX_ENTRY
)((PCHAR
)IndexEntry
+ IndexEntry
->Length
);
465 if (IndexRoot
->IndexHeader
.Flags
& NTFS_LARGE_INDEX
)
467 DbgPrint((DPRINT_FILESYSTEM
, "Large Index!\n"));
469 IndexBlockSize
= IndexRoot
->IndexBlockSize
;
471 if (!NtfsFindAttribute(&IndexBitmapCtx
, MftRecord
, NTFS_ATTR_TYPE_BITMAP
, L
"$I30"))
473 DbgPrint((DPRINT_FILESYSTEM
, "Corrupted filesystem!\n"));
474 MmFreeMemory(MftRecord
);
477 if (IndexBitmapCtx
.Record
->IsNonResident
)
478 BitmapDataSize
= IndexBitmapCtx
.Record
->NonResident
.DataSize
;
480 BitmapDataSize
= IndexBitmapCtx
.Record
->Resident
.ValueLength
;
481 DbgPrint((DPRINT_FILESYSTEM
, "BitmapDataSize: %x\n", BitmapDataSize
));
482 BitmapData
= MmAllocateMemory(BitmapDataSize
);
483 if (BitmapData
== NULL
)
485 MmFreeMemory(IndexRecord
);
486 MmFreeMemory(MftRecord
);
489 NtfsReadAttribute(&IndexBitmapCtx
, 0, BitmapData
, BitmapDataSize
);
491 if (!NtfsFindAttribute(&IndexAllocationCtx
, MftRecord
, NTFS_ATTR_TYPE_INDEX_ALLOCATION
, L
"$I30"))
493 DbgPrint((DPRINT_FILESYSTEM
, "Corrupted filesystem!\n"));
494 MmFreeMemory(BitmapData
);
495 MmFreeMemory(IndexRecord
);
496 MmFreeMemory(MftRecord
);
499 if (IndexAllocationCtx
.Record
->IsNonResident
)
500 IndexAllocationSize
= IndexAllocationCtx
.Record
->NonResident
.DataSize
;
502 IndexAllocationSize
= IndexAllocationCtx
.Record
->Resident
.ValueLength
;
508 DbgPrint((DPRINT_FILESYSTEM
, "RecordOffset: %x IndexAllocationSize: %x\n", RecordOffset
, IndexAllocationSize
));
509 for (; RecordOffset
< IndexAllocationSize
;)
511 UCHAR Bit
= 1 << ((RecordOffset
/ IndexBlockSize
) & 7);
512 ULONG Byte
= (RecordOffset
/ IndexBlockSize
) >> 3;
513 if ((BitmapData
[Byte
] & Bit
))
515 RecordOffset
+= IndexBlockSize
;
518 if (RecordOffset
>= IndexAllocationSize
)
523 NtfsReadAttribute(&IndexAllocationCtx
, RecordOffset
, IndexRecord
, IndexBlockSize
);
525 if (!NtfsFixupRecord((PNTFS_RECORD
)IndexRecord
))
531 IndexEntry
= (PNTFS_INDEX_ENTRY
)(IndexRecord
+ 0x18 + *(USHORT
*)(IndexRecord
+ 0x18));
532 IndexEntryEnd
= (PNTFS_INDEX_ENTRY
)(IndexRecord
+ IndexBlockSize
);
534 while (IndexEntry
< IndexEntryEnd
&&
535 !(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_END
))
537 if (NtfsCompareFileName(FileName
, IndexEntry
))
539 DbgPrint((DPRINT_FILESYSTEM
, "File found\n"));
540 *OutMFTIndex
= IndexEntry
->Data
.Directory
.IndexedFile
;
541 MmFreeMemory(BitmapData
);
542 MmFreeMemory(IndexRecord
);
543 MmFreeMemory(MftRecord
);
546 IndexEntry
= (PNTFS_INDEX_ENTRY
)((PCHAR
)IndexEntry
+ IndexEntry
->Length
);
549 RecordOffset
+= IndexBlockSize
;
552 MmFreeMemory(BitmapData
);
555 MmFreeMemory(IndexRecord
);
559 DbgPrint((DPRINT_FILESYSTEM
, "Can't read MFT record\n"));
561 MmFreeMemory(MftRecord
);
566 BOOL
NtfsLookupFile(PUCHAR FileName
, PNTFS_MFT_RECORD MftRecord
, PNTFS_ATTR_CONTEXT DataContext
)
568 ULONG NumberOfPathParts
;
570 ULONG CurrentMFTIndex
;
573 DbgPrint((DPRINT_FILESYSTEM
, "NtfsLookupFile() FileName = %s\n", FileName
));
575 CurrentMFTIndex
= NTFS_FILE_ROOT
;
576 NumberOfPathParts
= FsGetNumPathParts(FileName
);
577 for (i
= 0; i
< NumberOfPathParts
; i
++)
579 FsGetFirstNameFromPath(PathPart
, FileName
);
581 for (; (*FileName
!= '\\') && (*FileName
!= '/') && (*FileName
!= '\0'); FileName
++)
585 DbgPrint((DPRINT_FILESYSTEM
, "- Lookup: %s\n", PathPart
));
586 if (!NtfsFindMftRecord(CurrentMFTIndex
, PathPart
, &CurrentMFTIndex
))
588 DbgPrint((DPRINT_FILESYSTEM
, "- Failed\n"));
591 DbgPrint((DPRINT_FILESYSTEM
, "- Lookup: %x\n", CurrentMFTIndex
));
594 if (!NtfsReadMftRecord(CurrentMFTIndex
, MftRecord
))
596 DbgPrint((DPRINT_FILESYSTEM
, "NtfsLookupFile: Can't read MFT record\n"));
600 if (!NtfsFindAttribute(DataContext
, MftRecord
, NTFS_ATTR_TYPE_DATA
, L
""))
602 DbgPrint((DPRINT_FILESYSTEM
, "NtfsLookupFile: Can't find data attribute\n"));
609 BOOL
NtfsOpenVolume(ULONG DriveNumber
, ULONG VolumeStartSector
)
611 NtfsBootSector
= (PNTFS_BOOTSECTOR
)DISKREADBUFFER
;
613 DbgPrint((DPRINT_FILESYSTEM
, "NtfsOpenVolume() DriveNumber = 0x%x VolumeStartSector = 0x%x\n", DriveNumber
, VolumeStartSector
));
615 if (!MachDiskReadLogicalSectors(DriveNumber
, VolumeStartSector
, 1, (PCHAR
)DISKREADBUFFER
))
617 FileSystemError("Failed to read the boot sector.");
621 if (RtlCompareMemory(NtfsBootSector
->SystemId
, "NTFS", 4))
623 FileSystemError("Invalid NTFS signature.");
627 NtfsBootSector
= MmAllocateMemory(NtfsBootSector
->BytesPerSector
);
628 if (NtfsBootSector
== NULL
)
633 RtlCopyMemory(NtfsBootSector
, (PCHAR
)DISKREADBUFFER
, ((PNTFS_BOOTSECTOR
)DISKREADBUFFER
)->BytesPerSector
);
635 NtfsClusterSize
= NtfsBootSector
->SectorsPerCluster
* NtfsBootSector
->BytesPerSector
;
636 if (NtfsBootSector
->ClustersPerMftRecord
> 0)
637 NtfsMftRecordSize
= NtfsBootSector
->ClustersPerMftRecord
* NtfsClusterSize
;
639 NtfsMftRecordSize
= 1 << (-NtfsBootSector
->ClustersPerMftRecord
);
640 if (NtfsBootSector
->ClustersPerIndexRecord
> 0)
641 NtfsIndexRecordSize
= NtfsBootSector
->ClustersPerIndexRecord
* NtfsClusterSize
;
643 NtfsIndexRecordSize
= 1 << (-NtfsBootSector
->ClustersPerIndexRecord
);
645 DbgPrint((DPRINT_FILESYSTEM
, "NtfsClusterSize: 0x%x\n", NtfsClusterSize
));
646 DbgPrint((DPRINT_FILESYSTEM
, "ClustersPerMftRecord: %d\n", NtfsBootSector
->ClustersPerMftRecord
));
647 DbgPrint((DPRINT_FILESYSTEM
, "ClustersPerIndexRecord: %d\n", NtfsBootSector
->ClustersPerIndexRecord
));
648 DbgPrint((DPRINT_FILESYSTEM
, "NtfsMftRecordSize: 0x%x\n", NtfsMftRecordSize
));
649 DbgPrint((DPRINT_FILESYSTEM
, "NtfsIndexRecordSize: 0x%x\n", NtfsIndexRecordSize
));
651 NtfsDriveNumber
= DriveNumber
;
652 NtfsSectorOfClusterZero
= VolumeStartSector
;
654 DbgPrint((DPRINT_FILESYSTEM
, "Reading MFT index...\n"));
655 if (!MachDiskReadLogicalSectors(DriveNumber
,
656 NtfsSectorOfClusterZero
+
657 (NtfsBootSector
->MftLocation
* NtfsBootSector
->SectorsPerCluster
),
658 NtfsMftRecordSize
/ NtfsBootSector
->BytesPerSector
, (PCHAR
)DISKREADBUFFER
))
660 FileSystemError("Failed to read the Master File Table record.");
664 NtfsMasterFileTable
= MmAllocateMemory(NtfsMftRecordSize
);
665 if (NtfsMasterFileTable
== NULL
)
667 MmFreeMemory(NtfsBootSector
);
671 RtlCopyMemory(NtfsMasterFileTable
, (PCHAR
)DISKREADBUFFER
, NtfsMftRecordSize
);
673 DbgPrint((DPRINT_FILESYSTEM
, "Searching for DATA attribute...\n"));
674 if (!NtfsFindAttribute(&NtfsMFTContext
, NtfsMasterFileTable
, NTFS_ATTR_TYPE_DATA
, L
""))
676 FileSystemError("Can't find data attribute for Master File Table.");
683 FILE* NtfsOpenFile(PUCHAR FileName
)
685 PNTFS_FILE_HANDLE FileHandle
;
686 PNTFS_MFT_RECORD MftRecord
;
688 FileHandle
= MmAllocateMemory(sizeof(NTFS_FILE_HANDLE
) + NtfsMftRecordSize
);
689 if (FileHandle
== NULL
)
694 MftRecord
= (PNTFS_MFT_RECORD
)(FileHandle
+ 1);
695 if (!NtfsLookupFile(FileName
, MftRecord
, &FileHandle
->DataContext
))
697 MmFreeMemory(FileHandle
);
701 FileHandle
->Offset
= 0;
703 return (FILE*)FileHandle
;
706 BOOL
NtfsReadFile(FILE *File
, ULONG BytesToRead
, ULONG
* BytesRead
, PVOID Buffer
)
708 PNTFS_FILE_HANDLE FileHandle
= (PNTFS_FILE_HANDLE
)File
;
709 ULONGLONG BytesRead64
;
710 BytesRead64
= NtfsReadAttribute(&FileHandle
->DataContext
, FileHandle
->Offset
, Buffer
, BytesToRead
);
713 *BytesRead
= (ULONG
)BytesRead64
;
714 FileHandle
->Offset
+= BytesRead64
;
720 ULONG
NtfsGetFileSize(FILE *File
)
722 PNTFS_FILE_HANDLE FileHandle
= (PNTFS_FILE_HANDLE
)File
;
723 if (FileHandle
->DataContext
.Record
->IsNonResident
)
724 return (ULONG
)FileHandle
->DataContext
.Record
->NonResident
.DataSize
;
726 return (ULONG
)FileHandle
->DataContext
.Record
->Resident
.ValueLength
;
729 VOID
NtfsSetFilePointer(FILE *File
, ULONG NewFilePointer
)
731 PNTFS_FILE_HANDLE FileHandle
= (PNTFS_FILE_HANDLE
)File
;
732 FileHandle
->Offset
= NewFilePointer
;
735 ULONG
NtfsGetFilePointer(FILE *File
)
737 PNTFS_FILE_HANDLE FileHandle
= (PNTFS_FILE_HANDLE
)File
;
738 return FileHandle
->Offset
;