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.
32 PNTFS_BOOTSECTOR NtfsBootSector
;
33 ULONG NtfsClusterSize
;
34 ULONG NtfsMftRecordSize
;
35 ULONG NtfsIndexRecordSize
;
36 ULONG NtfsDriveNumber
;
37 ULONG NtfsSectorOfClusterZero
;
38 PNTFS_MFT_RECORD NtfsMasterFileTable
;
39 NTFS_ATTR_CONTEXT NtfsMFTContext
;
41 static PUCHAR
NtfsDecodeRun(PUCHAR DataRun
, LONGLONG
*DataRunOffset
, ULONGLONG
*DataRunLength
)
43 UCHAR DataRunOffsetSize
;
44 UCHAR DataRunLengthSize
;
47 DataRunOffsetSize
= (*DataRun
>> 4) & 0xF;
48 DataRunLengthSize
= *DataRun
& 0xF;
52 for (i
= 0; i
< DataRunLengthSize
; i
++)
54 *DataRunLength
+= *DataRun
<< (i
<< 3);
58 /* NTFS 3+ sparse files */
59 if (DataRunOffsetSize
== 0)
65 for (i
= 0; i
< DataRunOffsetSize
- 1; i
++)
67 *DataRunOffset
+= *DataRun
<< (i
<< 3);
70 /* The last byte contains sign so we must process it different way. */
71 *DataRunOffset
= ((CHAR
)(*(DataRun
++)) << (i
<< 3)) + *DataRunOffset
;
74 DbgPrint((DPRINT_FILESYSTEM
, "DataRunOffsetSize: %x\n", DataRunOffsetSize
));
75 DbgPrint((DPRINT_FILESYSTEM
, "DataRunLengthSize: %x\n", DataRunLengthSize
));
76 DbgPrint((DPRINT_FILESYSTEM
, "DataRunOffset: %x\n", *DataRunOffset
));
77 DbgPrint((DPRINT_FILESYSTEM
, "DataRunLength: %x\n", *DataRunLength
));
82 /* FIXME: Add support for attribute lists! */
83 static BOOL
NtfsFindAttribute(PNTFS_ATTR_CONTEXT Context
, PNTFS_MFT_RECORD MftRecord
, ULONG Type
, const WCHAR
*Name
)
85 PNTFS_ATTR_RECORD AttrRecord
;
86 PNTFS_ATTR_RECORD AttrRecordEnd
;
90 AttrRecord
= (PNTFS_ATTR_RECORD
)((PCHAR
)MftRecord
+ MftRecord
->AttributesOffset
);
91 AttrRecordEnd
= (PNTFS_ATTR_RECORD
)((PCHAR
)MftRecord
+ NtfsMftRecordSize
);
92 for (NameLength
= 0; Name
[NameLength
] != 0; NameLength
++)
95 while (AttrRecord
< AttrRecordEnd
)
97 if (AttrRecord
->Type
== NTFS_ATTR_TYPE_END
)
100 if (AttrRecord
->Type
== Type
)
102 if (AttrRecord
->NameLength
== NameLength
)
104 AttrName
= (PWCHAR
)((PCHAR
)AttrRecord
+ AttrRecord
->NameOffset
);
105 if (RtlEqualMemory(AttrName
, Name
, NameLength
<< 1))
107 /* Found it, fill up the context and return. */
108 Context
->Record
= AttrRecord
;
109 if (AttrRecord
->IsNonResident
)
111 LONGLONG DataRunOffset
;
112 ULONGLONG DataRunLength
;
114 Context
->CacheRun
= (PUCHAR
)Context
->Record
+ Context
->Record
->NonResident
.MappingPairsOffset
;
115 Context
->CacheRunOffset
= 0;
116 Context
->CacheRun
= NtfsDecodeRun(Context
->CacheRun
, &DataRunOffset
, &DataRunLength
);
117 Context
->CacheRunLength
= DataRunLength
;
118 if (DataRunOffset
!= -1)
121 Context
->CacheRunStartLCN
=
122 Context
->CacheRunLastLCN
= DataRunOffset
;
127 Context
->CacheRunStartLCN
= -1;
128 Context
->CacheRunLastLCN
= 0;
130 Context
->CacheRunCurrentOffset
= 0;
137 AttrRecord
= (PNTFS_ATTR_RECORD
)((PCHAR
)AttrRecord
+ AttrRecord
->Length
);
143 /* FIXME: Optimize for multisector reads. */
144 static BOOL
NtfsDiskRead(ULONGLONG Offset
, ULONGLONG Length
, PCHAR Buffer
)
148 DbgPrint((DPRINT_FILESYSTEM
, "NtfsDiskRead - Offset: %I64d Length: %I64d\n", Offset
, Length
));
149 RtlZeroMemory((PCHAR
)DISKREADBUFFER
, 0x1000);
151 /* I. Read partial first sector if needed */
152 if (Offset
% NtfsBootSector
->BytesPerSector
)
154 if (!MachDiskReadLogicalSectors(NtfsDriveNumber
, NtfsSectorOfClusterZero
+ (Offset
/ NtfsBootSector
->BytesPerSector
), 1, (PCHAR
)DISKREADBUFFER
))
156 ReadLength
= min(Length
, NtfsBootSector
->BytesPerSector
- (Offset
% NtfsBootSector
->BytesPerSector
));
157 RtlCopyMemory(Buffer
, (PCHAR
)DISKREADBUFFER
+ (Offset
% NtfsBootSector
->BytesPerSector
), ReadLength
);
158 Buffer
+= ReadLength
;
159 Length
-= ReadLength
;
160 Offset
+= ReadLength
;
163 /* II. Read all complete 64-sector blocks. */
164 while (Length
>= (ULONGLONG
)64 * (ULONGLONG
)NtfsBootSector
->BytesPerSector
)
166 if (!MachDiskReadLogicalSectors(NtfsDriveNumber
, NtfsSectorOfClusterZero
+ (Offset
/ NtfsBootSector
->BytesPerSector
), 64, (PCHAR
)DISKREADBUFFER
))
168 RtlCopyMemory(Buffer
, (PCHAR
)DISKREADBUFFER
, 64 * NtfsBootSector
->BytesPerSector
);
169 Buffer
+= 64 * NtfsBootSector
->BytesPerSector
;
170 Length
-= 64 * NtfsBootSector
->BytesPerSector
;
171 Offset
+= 64 * NtfsBootSector
->BytesPerSector
;
174 /* III. Read the rest of data */
177 ReadLength
= ((Length
+ NtfsBootSector
->BytesPerSector
- 1) / NtfsBootSector
->BytesPerSector
);
178 if (!MachDiskReadLogicalSectors(NtfsDriveNumber
, NtfsSectorOfClusterZero
+ (Offset
/ NtfsBootSector
->BytesPerSector
), ReadLength
, (PCHAR
)DISKREADBUFFER
))
180 RtlCopyMemory(Buffer
, (PCHAR
)DISKREADBUFFER
, Length
);
186 static ULONGLONG
NtfsReadAttribute(PNTFS_ATTR_CONTEXT Context
, ULONGLONG Offset
, PCHAR Buffer
, ULONGLONG Length
)
190 LONGLONG DataRunOffset
;
191 ULONGLONG DataRunLength
;
192 LONGLONG DataRunStartLCN
;
193 ULONGLONG CurrentOffset
;
194 ULONGLONG ReadLength
;
195 ULONGLONG AlreadyRead
;
197 if (!Context
->Record
->IsNonResident
)
199 if (Offset
> Context
->Record
->Resident
.ValueLength
)
201 if (Offset
+ Length
> Context
->Record
->Resident
.ValueLength
)
202 Length
= Context
->Record
->Resident
.ValueLength
- Offset
;
203 RtlCopyMemory(Buffer
, (PCHAR
)Context
->Record
+ Context
->Record
->Resident
.ValueOffset
+ Offset
, Length
);
208 * Non-resident attribute
212 * I. Find the corresponding start data run.
215 if (Context
->CacheRunOffset
== Offset
)
217 DataRun
= Context
->CacheRun
;
218 LastLCN
= Context
->CacheRunLastLCN
;
219 DataRunStartLCN
= Context
->CacheRunStartLCN
;
220 DataRunLength
= Context
->CacheRunLength
;
221 CurrentOffset
= Context
->CacheRunCurrentOffset
;
226 DataRun
= (PUCHAR
)Context
->Record
+ Context
->Record
->NonResident
.MappingPairsOffset
;
231 DataRun
= NtfsDecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
232 if (DataRunOffset
!= -1)
234 /* Normal data run. */
235 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
236 LastLCN
= DataRunStartLCN
;
240 /* Sparse data run. */
241 DataRunStartLCN
= -1;
244 if (Offset
>= CurrentOffset
&&
245 Offset
< CurrentOffset
+ (DataRunLength
* NtfsClusterSize
))
255 CurrentOffset
+= DataRunLength
* NtfsClusterSize
;
260 * II. Go through the run list and read the data
266 ReadLength
= min(DataRunLength
* NtfsClusterSize
, Length
);
267 if (DataRunStartLCN
== -1)
268 RtlZeroMemory(Buffer
, ReadLength
);
269 else if (!NtfsDiskRead(DataRunStartLCN
* NtfsClusterSize
+ Offset
- CurrentOffset
, ReadLength
, Buffer
))
271 Length
-= ReadLength
;
272 Buffer
+= ReadLength
;
273 AlreadyRead
+= ReadLength
;
275 /* We finished this request, but there still data in this data run. */
276 if (Length
== 0 && ReadLength
!= DataRunLength
* NtfsClusterSize
)
280 * Go to next run in the list.
285 DataRun
= NtfsDecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
286 if (DataRunOffset
!= -1)
288 /* Normal data run. */
289 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
290 LastLCN
= DataRunStartLCN
;
294 /* Sparse data run. */
295 DataRunStartLCN
= -1;
297 CurrentOffset
+= DataRunLength
* NtfsClusterSize
;
300 Context
->CacheRun
= DataRun
;
301 Context
->CacheRunOffset
= Offset
+ AlreadyRead
;
302 Context
->CacheRunStartLCN
= DataRunStartLCN
;
303 Context
->CacheRunLength
= DataRunLength
;
304 Context
->CacheRunLastLCN
= LastLCN
;
305 Context
->CacheRunCurrentOffset
= CurrentOffset
;
310 static BOOL
NtfsFixupRecord(PNTFS_RECORD Record
)
317 USA
= (USHORT
*)((PCHAR
)Record
+ Record
->USAOffset
);
318 USANumber
= *(USA
++);
319 USACount
= Record
->USACount
- 1; /* Exclude the USA Number. */
320 Block
= (USHORT
*)((PCHAR
)Record
+ NtfsBootSector
->BytesPerSector
- 2);
324 if (*Block
!= USANumber
)
327 Block
= (USHORT
*)((PCHAR
)Block
+ NtfsBootSector
->BytesPerSector
);
334 static BOOL
NtfsReadMftRecord(ULONG MFTIndex
, PNTFS_MFT_RECORD Buffer
)
338 BytesRead
= NtfsReadAttribute(&NtfsMFTContext
, MFTIndex
* NtfsMftRecordSize
, (PCHAR
)Buffer
, NtfsMftRecordSize
);
339 if (BytesRead
!= NtfsMftRecordSize
)
342 /* Apply update sequence array fixups. */
343 return NtfsFixupRecord((PNTFS_RECORD
)Buffer
);
347 VOID
NtfsPrintFile(PNTFS_INDEX_ENTRY IndexEntry
)
350 UCHAR FileNameLength
;
351 CHAR AnsiFileName
[256];
354 FileName
= IndexEntry
->FileName
.FileName
;
355 FileNameLength
= IndexEntry
->FileName
.FileNameLength
;
357 for (i
= 0; i
< FileNameLength
; i
++)
358 AnsiFileName
[i
] = FileName
[i
];
361 DbgPrint((DPRINT_FILESYSTEM
, "- %s (%x)\n", AnsiFileName
, IndexEntry
->Data
.Directory
.IndexedFile
));
365 static BOOL
NtfsCompareFileName(PCHAR FileName
, PNTFS_INDEX_ENTRY IndexEntry
)
367 PWCHAR EntryFileName
;
368 UCHAR EntryFileNameLength
;
371 EntryFileName
= IndexEntry
->FileName
.FileName
;
372 EntryFileNameLength
= IndexEntry
->FileName
.FileNameLength
;
375 NtfsPrintFile(IndexEntry
);
378 if (strlen(FileName
) != EntryFileNameLength
)
381 /* Do case-sensitive compares for Posix file names. */
382 if (IndexEntry
->FileName
.FileNameType
== NTFS_FILE_NAME_POSIX
)
384 for (i
= 0; i
< EntryFileNameLength
; i
++)
385 if (EntryFileName
[i
] != FileName
[i
])
390 for (i
= 0; i
< EntryFileNameLength
; i
++)
391 if (tolower(EntryFileName
[i
]) != tolower(FileName
[i
]))
398 static BOOL
NtfsFindMftRecord(ULONG MFTIndex
, PCHAR FileName
, ULONG
*OutMFTIndex
)
400 PNTFS_MFT_RECORD MftRecord
;
402 NTFS_ATTR_CONTEXT IndexRootCtx
;
403 NTFS_ATTR_CONTEXT IndexBitmapCtx
;
404 NTFS_ATTR_CONTEXT IndexAllocationCtx
;
405 PNTFS_INDEX_ROOT IndexRoot
;
406 ULONGLONG BitmapDataSize
;
407 ULONGLONG IndexAllocationSize
;
410 PNTFS_INDEX_ENTRY IndexEntry
, IndexEntryEnd
;
412 ULONG IndexBlockSize
;
414 MftRecord
= MmAllocateMemory(NtfsMftRecordSize
);
415 if (MftRecord
== NULL
)
420 if (NtfsReadMftRecord(MFTIndex
, MftRecord
))
422 Magic
= MftRecord
->Magic
;
424 if (!NtfsFindAttribute(&IndexRootCtx
, MftRecord
, NTFS_ATTR_TYPE_INDEX_ROOT
, L
"$I30"))
426 MmFreeMemory(MftRecord
);
430 IndexRecord
= MmAllocateMemory(NtfsIndexRecordSize
);
431 if (IndexRecord
== NULL
)
433 MmFreeMemory(MftRecord
);
437 NtfsReadAttribute(&IndexRootCtx
, 0, IndexRecord
, NtfsIndexRecordSize
);
438 IndexRoot
= (PNTFS_INDEX_ROOT
)IndexRecord
;
439 IndexEntry
= (PNTFS_INDEX_ENTRY
)((PCHAR
)&IndexRoot
->IndexHeader
+ IndexRoot
->IndexHeader
.EntriesOffset
);
440 /* Index root is always resident. */
441 IndexEntryEnd
= (PNTFS_INDEX_ENTRY
)(IndexRecord
+ IndexRootCtx
.Record
->Resident
.ValueLength
);
443 DbgPrint((DPRINT_FILESYSTEM
, "NtfsIndexRecordSize: %x IndexBlockSize: %x\n", NtfsIndexRecordSize
, IndexRoot
->IndexBlockSize
));
445 while (IndexEntry
< IndexEntryEnd
&&
446 !(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_END
))
448 if (NtfsCompareFileName(FileName
, IndexEntry
))
450 *OutMFTIndex
= IndexEntry
->Data
.Directory
.IndexedFile
;
451 MmFreeMemory(IndexRecord
);
452 MmFreeMemory(MftRecord
);
455 IndexEntry
= (PNTFS_INDEX_ENTRY
)((PCHAR
)IndexEntry
+ IndexEntry
->Length
);
458 if (IndexRoot
->IndexHeader
.Flags
& NTFS_LARGE_INDEX
)
460 DbgPrint((DPRINT_FILESYSTEM
, "Large Index!\n"));
462 IndexBlockSize
= IndexRoot
->IndexBlockSize
;
464 if (!NtfsFindAttribute(&IndexBitmapCtx
, MftRecord
, NTFS_ATTR_TYPE_BITMAP
, L
"$I30"))
466 DbgPrint((DPRINT_FILESYSTEM
, "Corrupted filesystem!\n"));
467 MmFreeMemory(MftRecord
);
470 if (IndexBitmapCtx
.Record
->IsNonResident
)
471 BitmapDataSize
= IndexBitmapCtx
.Record
->NonResident
.DataSize
;
473 BitmapDataSize
= IndexBitmapCtx
.Record
->Resident
.ValueLength
;
474 DbgPrint((DPRINT_FILESYSTEM
, "BitmapDataSize: %x\n", BitmapDataSize
));
475 BitmapData
= MmAllocateMemory(BitmapDataSize
);
476 if (BitmapData
== NULL
)
478 MmFreeMemory(IndexRecord
);
479 MmFreeMemory(MftRecord
);
482 NtfsReadAttribute(&IndexBitmapCtx
, 0, BitmapData
, BitmapDataSize
);
484 if (!NtfsFindAttribute(&IndexAllocationCtx
, MftRecord
, NTFS_ATTR_TYPE_INDEX_ALLOCATION
, L
"$I30"))
486 DbgPrint((DPRINT_FILESYSTEM
, "Corrupted filesystem!\n"));
487 MmFreeMemory(BitmapData
);
488 MmFreeMemory(IndexRecord
);
489 MmFreeMemory(MftRecord
);
492 if (IndexAllocationCtx
.Record
->IsNonResident
)
493 IndexAllocationSize
= IndexAllocationCtx
.Record
->NonResident
.DataSize
;
495 IndexAllocationSize
= IndexAllocationCtx
.Record
->Resident
.ValueLength
;
501 DbgPrint((DPRINT_FILESYSTEM
, "RecordOffset: %x IndexAllocationSize: %x\n", RecordOffset
, IndexAllocationSize
));
502 for (; RecordOffset
< IndexAllocationSize
;)
504 UCHAR Bit
= 1 << ((RecordOffset
/ IndexBlockSize
) & 7);
505 ULONG Byte
= (RecordOffset
/ IndexBlockSize
) >> 3;
506 if ((BitmapData
[Byte
] & Bit
))
508 RecordOffset
+= IndexBlockSize
;
511 if (RecordOffset
>= IndexAllocationSize
)
516 NtfsReadAttribute(&IndexAllocationCtx
, RecordOffset
, IndexRecord
, IndexBlockSize
);
518 if (!NtfsFixupRecord((PNTFS_RECORD
)IndexRecord
))
524 IndexEntry
= (PNTFS_INDEX_ENTRY
)(IndexRecord
+ 0x18 + *(USHORT
*)(IndexRecord
+ 0x18));
525 IndexEntryEnd
= (PNTFS_INDEX_ENTRY
)(IndexRecord
+ IndexBlockSize
);
527 while (IndexEntry
< IndexEntryEnd
&&
528 !(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_END
))
530 if (NtfsCompareFileName(FileName
, IndexEntry
))
532 DbgPrint((DPRINT_FILESYSTEM
, "File found\n"));
533 *OutMFTIndex
= IndexEntry
->Data
.Directory
.IndexedFile
;
534 MmFreeMemory(BitmapData
);
535 MmFreeMemory(IndexRecord
);
536 MmFreeMemory(MftRecord
);
539 IndexEntry
= (PNTFS_INDEX_ENTRY
)((PCHAR
)IndexEntry
+ IndexEntry
->Length
);
542 RecordOffset
+= IndexBlockSize
;
545 MmFreeMemory(BitmapData
);
548 MmFreeMemory(IndexRecord
);
552 DbgPrint((DPRINT_FILESYSTEM
, "Can't read MFT record\n"));
554 MmFreeMemory(MftRecord
);
559 static BOOL
NtfsLookupFile(PCSTR FileName
, PNTFS_MFT_RECORD MftRecord
, PNTFS_ATTR_CONTEXT DataContext
)
561 ULONG NumberOfPathParts
;
563 ULONG CurrentMFTIndex
;
566 DbgPrint((DPRINT_FILESYSTEM
, "NtfsLookupFile() FileName = %s\n", FileName
));
568 CurrentMFTIndex
= NTFS_FILE_ROOT
;
569 NumberOfPathParts
= FsGetNumPathParts(FileName
);
570 for (i
= 0; i
< NumberOfPathParts
; i
++)
572 FsGetFirstNameFromPath(PathPart
, FileName
);
574 for (; (*FileName
!= '\\') && (*FileName
!= '/') && (*FileName
!= '\0'); FileName
++)
578 DbgPrint((DPRINT_FILESYSTEM
, "- Lookup: %s\n", PathPart
));
579 if (!NtfsFindMftRecord(CurrentMFTIndex
, PathPart
, &CurrentMFTIndex
))
581 DbgPrint((DPRINT_FILESYSTEM
, "- Failed\n"));
584 DbgPrint((DPRINT_FILESYSTEM
, "- Lookup: %x\n", CurrentMFTIndex
));
587 if (!NtfsReadMftRecord(CurrentMFTIndex
, MftRecord
))
589 DbgPrint((DPRINT_FILESYSTEM
, "NtfsLookupFile: Can't read MFT record\n"));
593 if (!NtfsFindAttribute(DataContext
, MftRecord
, NTFS_ATTR_TYPE_DATA
, L
""))
595 DbgPrint((DPRINT_FILESYSTEM
, "NtfsLookupFile: Can't find data attribute\n"));
602 BOOL
NtfsOpenVolume(ULONG DriveNumber
, ULONG VolumeStartSector
)
604 NtfsBootSector
= (PNTFS_BOOTSECTOR
)DISKREADBUFFER
;
606 DbgPrint((DPRINT_FILESYSTEM
, "NtfsOpenVolume() DriveNumber = 0x%x VolumeStartSector = 0x%x\n", DriveNumber
, VolumeStartSector
));
608 if (!MachDiskReadLogicalSectors(DriveNumber
, VolumeStartSector
, 1, (PCHAR
)DISKREADBUFFER
))
610 FileSystemError("Failed to read the boot sector.");
614 if (!RtlEqualMemory(NtfsBootSector
->SystemId
, "NTFS", 4))
616 FileSystemError("Invalid NTFS signature.");
620 NtfsBootSector
= MmAllocateMemory(NtfsBootSector
->BytesPerSector
);
621 if (NtfsBootSector
== NULL
)
626 RtlCopyMemory(NtfsBootSector
, (PCHAR
)DISKREADBUFFER
, ((PNTFS_BOOTSECTOR
)DISKREADBUFFER
)->BytesPerSector
);
628 NtfsClusterSize
= NtfsBootSector
->SectorsPerCluster
* NtfsBootSector
->BytesPerSector
;
629 if (NtfsBootSector
->ClustersPerMftRecord
> 0)
630 NtfsMftRecordSize
= NtfsBootSector
->ClustersPerMftRecord
* NtfsClusterSize
;
632 NtfsMftRecordSize
= 1 << (-NtfsBootSector
->ClustersPerMftRecord
);
633 if (NtfsBootSector
->ClustersPerIndexRecord
> 0)
634 NtfsIndexRecordSize
= NtfsBootSector
->ClustersPerIndexRecord
* NtfsClusterSize
;
636 NtfsIndexRecordSize
= 1 << (-NtfsBootSector
->ClustersPerIndexRecord
);
638 DbgPrint((DPRINT_FILESYSTEM
, "NtfsClusterSize: 0x%x\n", NtfsClusterSize
));
639 DbgPrint((DPRINT_FILESYSTEM
, "ClustersPerMftRecord: %d\n", NtfsBootSector
->ClustersPerMftRecord
));
640 DbgPrint((DPRINT_FILESYSTEM
, "ClustersPerIndexRecord: %d\n", NtfsBootSector
->ClustersPerIndexRecord
));
641 DbgPrint((DPRINT_FILESYSTEM
, "NtfsMftRecordSize: 0x%x\n", NtfsMftRecordSize
));
642 DbgPrint((DPRINT_FILESYSTEM
, "NtfsIndexRecordSize: 0x%x\n", NtfsIndexRecordSize
));
644 NtfsDriveNumber
= DriveNumber
;
645 NtfsSectorOfClusterZero
= VolumeStartSector
;
647 DbgPrint((DPRINT_FILESYSTEM
, "Reading MFT index...\n"));
648 if (!MachDiskReadLogicalSectors(DriveNumber
,
649 NtfsSectorOfClusterZero
+
650 (NtfsBootSector
->MftLocation
* NtfsBootSector
->SectorsPerCluster
),
651 NtfsMftRecordSize
/ NtfsBootSector
->BytesPerSector
, (PCHAR
)DISKREADBUFFER
))
653 FileSystemError("Failed to read the Master File Table record.");
657 NtfsMasterFileTable
= MmAllocateMemory(NtfsMftRecordSize
);
658 if (NtfsMasterFileTable
== NULL
)
660 MmFreeMemory(NtfsBootSector
);
664 RtlCopyMemory(NtfsMasterFileTable
, (PCHAR
)DISKREADBUFFER
, NtfsMftRecordSize
);
666 DbgPrint((DPRINT_FILESYSTEM
, "Searching for DATA attribute...\n"));
667 if (!NtfsFindAttribute(&NtfsMFTContext
, NtfsMasterFileTable
, NTFS_ATTR_TYPE_DATA
, L
""))
669 FileSystemError("Can't find data attribute for Master File Table.");
676 FILE* NtfsOpenFile(PCSTR FileName
)
678 PNTFS_FILE_HANDLE FileHandle
;
679 PNTFS_MFT_RECORD MftRecord
;
681 FileHandle
= MmAllocateMemory(sizeof(NTFS_FILE_HANDLE
) + NtfsMftRecordSize
);
682 if (FileHandle
== NULL
)
687 MftRecord
= (PNTFS_MFT_RECORD
)(FileHandle
+ 1);
688 if (!NtfsLookupFile(FileName
, MftRecord
, &FileHandle
->DataContext
))
690 MmFreeMemory(FileHandle
);
694 FileHandle
->Offset
= 0;
696 return (FILE*)FileHandle
;
699 BOOL
NtfsReadFile(FILE *File
, ULONG BytesToRead
, ULONG
* BytesRead
, PVOID Buffer
)
701 PNTFS_FILE_HANDLE FileHandle
= (PNTFS_FILE_HANDLE
)File
;
702 ULONGLONG BytesRead64
;
703 BytesRead64
= NtfsReadAttribute(&FileHandle
->DataContext
, FileHandle
->Offset
, Buffer
, BytesToRead
);
706 *BytesRead
= (ULONG
)BytesRead64
;
707 FileHandle
->Offset
+= BytesRead64
;
713 ULONG
NtfsGetFileSize(FILE *File
)
715 PNTFS_FILE_HANDLE FileHandle
= (PNTFS_FILE_HANDLE
)File
;
716 if (FileHandle
->DataContext
.Record
->IsNonResident
)
717 return (ULONG
)FileHandle
->DataContext
.Record
->NonResident
.DataSize
;
719 return (ULONG
)FileHandle
->DataContext
.Record
->Resident
.ValueLength
;
722 VOID
NtfsSetFilePointer(FILE *File
, ULONG NewFilePointer
)
724 PNTFS_FILE_HANDLE FileHandle
= (PNTFS_FILE_HANDLE
)File
;
725 FileHandle
->Offset
= NewFilePointer
;
728 ULONG
NtfsGetFilePointer(FILE *File
)
730 PNTFS_FILE_HANDLE FileHandle
= (PNTFS_FILE_HANDLE
)File
;
731 return FileHandle
->Offset
;