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.
31 PNTFS_BOOTSECTOR NtfsBootSector
;
32 ULONG NtfsClusterSize
;
33 ULONG NtfsMftRecordSize
;
34 ULONG NtfsIndexRecordSize
;
35 ULONG NtfsDriveNumber
;
36 ULONG NtfsSectorOfClusterZero
;
37 PNTFS_MFT_RECORD NtfsMasterFileTable
;
38 /* FIXME: NtfsMFTContext is never freed. */
39 PNTFS_ATTR_CONTEXT NtfsMFTContext
;
41 static ULONGLONG
NtfsGetAttributeSize(PNTFS_ATTR_RECORD AttrRecord
)
43 if (AttrRecord
->IsNonResident
)
44 return AttrRecord
->NonResident
.DataSize
;
46 return AttrRecord
->Resident
.ValueLength
;
49 static PUCHAR
NtfsDecodeRun(PUCHAR DataRun
, LONGLONG
*DataRunOffset
, ULONGLONG
*DataRunLength
)
51 UCHAR DataRunOffsetSize
;
52 UCHAR DataRunLengthSize
;
55 DataRunOffsetSize
= (*DataRun
>> 4) & 0xF;
56 DataRunLengthSize
= *DataRun
& 0xF;
60 for (i
= 0; i
< DataRunLengthSize
; i
++)
62 *DataRunLength
+= *DataRun
<< (i
<< 3);
66 /* NTFS 3+ sparse files */
67 if (DataRunOffsetSize
== 0)
73 for (i
= 0; i
< DataRunOffsetSize
- 1; i
++)
75 *DataRunOffset
+= *DataRun
<< (i
<< 3);
78 /* The last byte contains sign so we must process it different way. */
79 *DataRunOffset
= ((CHAR
)(*(DataRun
++)) << (i
<< 3)) + *DataRunOffset
;
82 DbgPrint((DPRINT_FILESYSTEM
, "DataRunOffsetSize: %x\n", DataRunOffsetSize
));
83 DbgPrint((DPRINT_FILESYSTEM
, "DataRunLengthSize: %x\n", DataRunLengthSize
));
84 DbgPrint((DPRINT_FILESYSTEM
, "DataRunOffset: %x\n", *DataRunOffset
));
85 DbgPrint((DPRINT_FILESYSTEM
, "DataRunLength: %x\n", *DataRunLength
));
90 static PNTFS_ATTR_CONTEXT
NtfsPrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord
)
92 PNTFS_ATTR_CONTEXT Context
;
94 Context
= MmAllocateMemory(FIELD_OFFSET(NTFS_ATTR_CONTEXT
, Record
) + AttrRecord
->Length
);
95 RtlCopyMemory(&Context
->Record
, AttrRecord
, AttrRecord
->Length
);
96 if (AttrRecord
->IsNonResident
)
98 LONGLONG DataRunOffset
;
99 ULONGLONG DataRunLength
;
101 Context
->CacheRun
= (PUCHAR
)&Context
->Record
+ Context
->Record
.NonResident
.MappingPairsOffset
;
102 Context
->CacheRunOffset
= 0;
103 Context
->CacheRun
= NtfsDecodeRun(Context
->CacheRun
, &DataRunOffset
, &DataRunLength
);
104 Context
->CacheRunLength
= DataRunLength
;
105 if (DataRunOffset
!= -1)
108 Context
->CacheRunStartLCN
=
109 Context
->CacheRunLastLCN
= DataRunOffset
;
114 Context
->CacheRunStartLCN
= -1;
115 Context
->CacheRunLastLCN
= 0;
117 Context
->CacheRunCurrentOffset
= 0;
123 static VOID
NtfsReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context
)
125 MmFreeMemory(Context
);
128 /* FIXME: Optimize for multisector reads. */
129 static BOOLEAN
NtfsDiskRead(ULONGLONG Offset
, ULONGLONG Length
, PCHAR Buffer
)
133 DbgPrint((DPRINT_FILESYSTEM
, "NtfsDiskRead - Offset: %I64d Length: %I64d\n", Offset
, Length
));
134 RtlZeroMemory((PCHAR
)DISKREADBUFFER
, 0x1000);
136 /* I. Read partial first sector if needed */
137 if (Offset
% NtfsBootSector
->BytesPerSector
)
139 if (!MachDiskReadLogicalSectors(NtfsDriveNumber
, NtfsSectorOfClusterZero
+ (Offset
/ NtfsBootSector
->BytesPerSector
), 1, (PCHAR
)DISKREADBUFFER
))
141 ReadLength
= min(Length
, NtfsBootSector
->BytesPerSector
- (Offset
% NtfsBootSector
->BytesPerSector
));
142 RtlCopyMemory(Buffer
, (PCHAR
)DISKREADBUFFER
+ (Offset
% NtfsBootSector
->BytesPerSector
), ReadLength
);
143 Buffer
+= ReadLength
;
144 Length
-= ReadLength
;
145 Offset
+= ReadLength
;
148 /* II. Read all complete 64-sector blocks. */
149 while (Length
>= (ULONGLONG
)64 * (ULONGLONG
)NtfsBootSector
->BytesPerSector
)
151 if (!MachDiskReadLogicalSectors(NtfsDriveNumber
, NtfsSectorOfClusterZero
+ (Offset
/ NtfsBootSector
->BytesPerSector
), 64, (PCHAR
)DISKREADBUFFER
))
153 RtlCopyMemory(Buffer
, (PCHAR
)DISKREADBUFFER
, 64 * NtfsBootSector
->BytesPerSector
);
154 Buffer
+= 64 * NtfsBootSector
->BytesPerSector
;
155 Length
-= 64 * NtfsBootSector
->BytesPerSector
;
156 Offset
+= 64 * NtfsBootSector
->BytesPerSector
;
159 /* III. Read the rest of data */
162 ReadLength
= ((Length
+ NtfsBootSector
->BytesPerSector
- 1) / NtfsBootSector
->BytesPerSector
);
163 if (!MachDiskReadLogicalSectors(NtfsDriveNumber
, NtfsSectorOfClusterZero
+ (Offset
/ NtfsBootSector
->BytesPerSector
), ReadLength
, (PCHAR
)DISKREADBUFFER
))
165 RtlCopyMemory(Buffer
, (PCHAR
)DISKREADBUFFER
, Length
);
171 static ULONGLONG
NtfsReadAttribute(PNTFS_ATTR_CONTEXT Context
, ULONGLONG Offset
, PCHAR Buffer
, ULONGLONG Length
)
175 LONGLONG DataRunOffset
;
176 ULONGLONG DataRunLength
;
177 LONGLONG DataRunStartLCN
;
178 ULONGLONG CurrentOffset
;
179 ULONGLONG ReadLength
;
180 ULONGLONG AlreadyRead
;
182 if (!Context
->Record
.IsNonResident
)
184 if (Offset
> Context
->Record
.Resident
.ValueLength
)
186 if (Offset
+ Length
> Context
->Record
.Resident
.ValueLength
)
187 Length
= Context
->Record
.Resident
.ValueLength
- Offset
;
188 RtlCopyMemory(Buffer
, (PCHAR
)&Context
->Record
+ Context
->Record
.Resident
.ValueOffset
+ Offset
, Length
);
193 * Non-resident attribute
197 * I. Find the corresponding start data run.
200 if (Context
->CacheRunOffset
== Offset
)
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
251 ReadLength
= min(DataRunLength
* NtfsClusterSize
, Length
);
252 if (DataRunStartLCN
== -1)
253 RtlZeroMemory(Buffer
, ReadLength
);
254 else if (!NtfsDiskRead(DataRunStartLCN
* NtfsClusterSize
+ Offset
- CurrentOffset
, ReadLength
, Buffer
))
256 Length
-= ReadLength
;
257 Buffer
+= ReadLength
;
258 AlreadyRead
+= ReadLength
;
260 /* We finished this request, but there still data in this data run. */
261 if (Length
== 0 && ReadLength
!= DataRunLength
* NtfsClusterSize
)
265 * Go to next run in the list.
270 DataRun
= NtfsDecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
271 if (DataRunOffset
!= -1)
273 /* Normal data run. */
274 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
275 LastLCN
= DataRunStartLCN
;
279 /* Sparse data run. */
280 DataRunStartLCN
= -1;
282 CurrentOffset
+= DataRunLength
* NtfsClusterSize
;
285 Context
->CacheRun
= DataRun
;
286 Context
->CacheRunOffset
= Offset
+ AlreadyRead
;
287 Context
->CacheRunStartLCN
= DataRunStartLCN
;
288 Context
->CacheRunLength
= DataRunLength
;
289 Context
->CacheRunLastLCN
= LastLCN
;
290 Context
->CacheRunCurrentOffset
= CurrentOffset
;
295 static PNTFS_ATTR_CONTEXT
NtfsFindAttributeHelper(PNTFS_ATTR_RECORD AttrRecord
, PNTFS_ATTR_RECORD AttrRecordEnd
, ULONG Type
, const WCHAR
*Name
, ULONG NameLength
)
297 while (AttrRecord
< AttrRecordEnd
)
299 if (AttrRecord
->Type
== NTFS_ATTR_TYPE_END
)
302 if (AttrRecord
->Type
== NTFS_ATTR_TYPE_ATTRIBUTE_LIST
)
304 PNTFS_ATTR_CONTEXT Context
;
305 PNTFS_ATTR_CONTEXT ListContext
;
308 PNTFS_ATTR_RECORD ListAttrRecord
;
309 PNTFS_ATTR_RECORD ListAttrRecordEnd
;
311 ListContext
= NtfsPrepareAttributeContext(AttrRecord
);
313 ListSize
= NtfsGetAttributeSize(&ListContext
->Record
);
314 ListBuffer
= MmAllocateMemory(ListSize
);
316 ListAttrRecord
= (PNTFS_ATTR_RECORD
)ListBuffer
;
317 ListAttrRecordEnd
= (PNTFS_ATTR_RECORD
)((PCHAR
)ListBuffer
+ ListSize
);
319 if (NtfsReadAttribute(ListContext
, 0, ListBuffer
, ListSize
) == ListSize
)
321 Context
= NtfsFindAttributeHelper(ListAttrRecord
, ListAttrRecordEnd
,
322 Type
, Name
, NameLength
);
324 NtfsReleaseAttributeContext(ListContext
);
325 MmFreeMemory(ListBuffer
);
332 if (AttrRecord
->Type
== Type
)
334 if (AttrRecord
->NameLength
== NameLength
)
338 AttrName
= (PWCHAR
)((PCHAR
)AttrRecord
+ AttrRecord
->NameOffset
);
339 if (RtlEqualMemory(AttrName
, Name
, NameLength
<< 1))
341 /* Found it, fill up the context and return. */
342 return NtfsPrepareAttributeContext(AttrRecord
);
347 AttrRecord
= (PNTFS_ATTR_RECORD
)((PCHAR
)AttrRecord
+ AttrRecord
->Length
);
353 static PNTFS_ATTR_CONTEXT
NtfsFindAttribute(PNTFS_MFT_RECORD MftRecord
, ULONG Type
, const WCHAR
*Name
)
355 PNTFS_ATTR_RECORD AttrRecord
;
356 PNTFS_ATTR_RECORD AttrRecordEnd
;
359 AttrRecord
= (PNTFS_ATTR_RECORD
)((PCHAR
)MftRecord
+ MftRecord
->AttributesOffset
);
360 AttrRecordEnd
= (PNTFS_ATTR_RECORD
)((PCHAR
)MftRecord
+ NtfsMftRecordSize
);
361 for (NameLength
= 0; Name
[NameLength
] != 0; NameLength
++)
364 return NtfsFindAttributeHelper(AttrRecord
, AttrRecordEnd
, Type
, Name
, NameLength
);
367 static BOOLEAN
NtfsFixupRecord(PNTFS_RECORD Record
)
374 USA
= (USHORT
*)((PCHAR
)Record
+ Record
->USAOffset
);
375 USANumber
= *(USA
++);
376 USACount
= Record
->USACount
- 1; /* Exclude the USA Number. */
377 Block
= (USHORT
*)((PCHAR
)Record
+ NtfsBootSector
->BytesPerSector
- 2);
381 if (*Block
!= USANumber
)
384 Block
= (USHORT
*)((PCHAR
)Block
+ NtfsBootSector
->BytesPerSector
);
391 static BOOLEAN
NtfsReadMftRecord(ULONG MFTIndex
, PNTFS_MFT_RECORD Buffer
)
395 BytesRead
= NtfsReadAttribute(NtfsMFTContext
, MFTIndex
* NtfsMftRecordSize
, (PCHAR
)Buffer
, NtfsMftRecordSize
);
396 if (BytesRead
!= NtfsMftRecordSize
)
399 /* Apply update sequence array fixups. */
400 return NtfsFixupRecord((PNTFS_RECORD
)Buffer
);
404 VOID
NtfsPrintFile(PNTFS_INDEX_ENTRY IndexEntry
)
407 UCHAR FileNameLength
;
408 CHAR AnsiFileName
[256];
411 FileName
= IndexEntry
->FileName
.FileName
;
412 FileNameLength
= IndexEntry
->FileName
.FileNameLength
;
414 for (i
= 0; i
< FileNameLength
; i
++)
415 AnsiFileName
[i
] = FileName
[i
];
418 DbgPrint((DPRINT_FILESYSTEM
, "- %s (%x)\n", AnsiFileName
, IndexEntry
->Data
.Directory
.IndexedFile
));
422 static BOOLEAN
NtfsCompareFileName(PCHAR FileName
, PNTFS_INDEX_ENTRY IndexEntry
)
424 PWCHAR EntryFileName
;
425 UCHAR EntryFileNameLength
;
428 EntryFileName
= IndexEntry
->FileName
.FileName
;
429 EntryFileNameLength
= IndexEntry
->FileName
.FileNameLength
;
432 NtfsPrintFile(IndexEntry
);
435 if (strlen(FileName
) != EntryFileNameLength
)
438 /* Do case-sensitive compares for Posix file names. */
439 if (IndexEntry
->FileName
.FileNameType
== NTFS_FILE_NAME_POSIX
)
441 for (i
= 0; i
< EntryFileNameLength
; i
++)
442 if (EntryFileName
[i
] != FileName
[i
])
447 for (i
= 0; i
< EntryFileNameLength
; i
++)
448 if (tolower(EntryFileName
[i
]) != tolower(FileName
[i
]))
455 static BOOLEAN
NtfsFindMftRecord(ULONG MFTIndex
, PCHAR FileName
, ULONG
*OutMFTIndex
)
457 PNTFS_MFT_RECORD MftRecord
;
459 PNTFS_ATTR_CONTEXT IndexRootCtx
;
460 PNTFS_ATTR_CONTEXT IndexBitmapCtx
;
461 PNTFS_ATTR_CONTEXT IndexAllocationCtx
;
462 PNTFS_INDEX_ROOT IndexRoot
;
463 ULONGLONG BitmapDataSize
;
464 ULONGLONG IndexAllocationSize
;
467 PNTFS_INDEX_ENTRY IndexEntry
, IndexEntryEnd
;
469 ULONG IndexBlockSize
;
471 MftRecord
= MmAllocateMemory(NtfsMftRecordSize
);
472 if (MftRecord
== NULL
)
477 if (NtfsReadMftRecord(MFTIndex
, MftRecord
))
479 Magic
= MftRecord
->Magic
;
481 IndexRootCtx
= NtfsFindAttribute(MftRecord
, NTFS_ATTR_TYPE_INDEX_ROOT
, L
"$I30");
482 if (IndexRootCtx
== NULL
)
484 MmFreeMemory(MftRecord
);
488 IndexRecord
= MmAllocateMemory(NtfsIndexRecordSize
);
489 if (IndexRecord
== NULL
)
491 MmFreeMemory(MftRecord
);
495 NtfsReadAttribute(IndexRootCtx
, 0, IndexRecord
, NtfsIndexRecordSize
);
496 IndexRoot
= (PNTFS_INDEX_ROOT
)IndexRecord
;
497 IndexEntry
= (PNTFS_INDEX_ENTRY
)((PCHAR
)&IndexRoot
->IndexHeader
+ IndexRoot
->IndexHeader
.EntriesOffset
);
498 /* Index root is always resident. */
499 IndexEntryEnd
= (PNTFS_INDEX_ENTRY
)(IndexRecord
+ IndexRootCtx
->Record
.Resident
.ValueLength
);
500 NtfsReleaseAttributeContext(IndexRootCtx
);
502 DbgPrint((DPRINT_FILESYSTEM
, "NtfsIndexRecordSize: %x IndexBlockSize: %x\n", NtfsIndexRecordSize
, IndexRoot
->IndexBlockSize
));
504 while (IndexEntry
< IndexEntryEnd
&&
505 !(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_END
))
507 if (NtfsCompareFileName(FileName
, IndexEntry
))
509 *OutMFTIndex
= IndexEntry
->Data
.Directory
.IndexedFile
;
510 MmFreeMemory(IndexRecord
);
511 MmFreeMemory(MftRecord
);
514 IndexEntry
= (PNTFS_INDEX_ENTRY
)((PCHAR
)IndexEntry
+ IndexEntry
->Length
);
517 if (IndexRoot
->IndexHeader
.Flags
& NTFS_LARGE_INDEX
)
519 DbgPrint((DPRINT_FILESYSTEM
, "Large Index!\n"));
521 IndexBlockSize
= IndexRoot
->IndexBlockSize
;
523 IndexBitmapCtx
= NtfsFindAttribute(MftRecord
, NTFS_ATTR_TYPE_BITMAP
, L
"$I30");
524 if (IndexBitmapCtx
== NULL
)
526 DbgPrint((DPRINT_FILESYSTEM
, "Corrupted filesystem!\n"));
527 MmFreeMemory(MftRecord
);
530 BitmapDataSize
= NtfsGetAttributeSize(&IndexBitmapCtx
->Record
);
531 DbgPrint((DPRINT_FILESYSTEM
, "BitmapDataSize: %x\n", BitmapDataSize
));
532 BitmapData
= MmAllocateMemory(BitmapDataSize
);
533 if (BitmapData
== NULL
)
535 MmFreeMemory(IndexRecord
);
536 MmFreeMemory(MftRecord
);
539 NtfsReadAttribute(IndexBitmapCtx
, 0, BitmapData
, BitmapDataSize
);
540 NtfsReleaseAttributeContext(IndexBitmapCtx
);
542 IndexAllocationCtx
= NtfsFindAttribute(MftRecord
, NTFS_ATTR_TYPE_INDEX_ALLOCATION
, L
"$I30");
543 if (IndexAllocationCtx
== NULL
)
545 DbgPrint((DPRINT_FILESYSTEM
, "Corrupted filesystem!\n"));
546 MmFreeMemory(BitmapData
);
547 MmFreeMemory(IndexRecord
);
548 MmFreeMemory(MftRecord
);
551 IndexAllocationSize
= NtfsGetAttributeSize(&IndexAllocationCtx
->Record
);
557 DbgPrint((DPRINT_FILESYSTEM
, "RecordOffset: %x IndexAllocationSize: %x\n", RecordOffset
, IndexAllocationSize
));
558 for (; RecordOffset
< IndexAllocationSize
;)
560 UCHAR Bit
= 1 << ((RecordOffset
/ IndexBlockSize
) & 7);
561 ULONG Byte
= (RecordOffset
/ IndexBlockSize
) >> 3;
562 if ((BitmapData
[Byte
] & Bit
))
564 RecordOffset
+= IndexBlockSize
;
567 if (RecordOffset
>= IndexAllocationSize
)
572 NtfsReadAttribute(IndexAllocationCtx
, RecordOffset
, IndexRecord
, IndexBlockSize
);
574 if (!NtfsFixupRecord((PNTFS_RECORD
)IndexRecord
))
580 IndexEntry
= (PNTFS_INDEX_ENTRY
)(IndexRecord
+ 0x18 + *(USHORT
*)(IndexRecord
+ 0x18));
581 IndexEntryEnd
= (PNTFS_INDEX_ENTRY
)(IndexRecord
+ IndexBlockSize
);
583 while (IndexEntry
< IndexEntryEnd
&&
584 !(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_END
))
586 if (NtfsCompareFileName(FileName
, IndexEntry
))
588 DbgPrint((DPRINT_FILESYSTEM
, "File found\n"));
589 *OutMFTIndex
= IndexEntry
->Data
.Directory
.IndexedFile
;
590 MmFreeMemory(BitmapData
);
591 MmFreeMemory(IndexRecord
);
592 MmFreeMemory(MftRecord
);
593 NtfsReleaseAttributeContext(IndexAllocationCtx
);
596 IndexEntry
= (PNTFS_INDEX_ENTRY
)((PCHAR
)IndexEntry
+ IndexEntry
->Length
);
599 RecordOffset
+= IndexBlockSize
;
602 NtfsReleaseAttributeContext(IndexAllocationCtx
);
603 MmFreeMemory(BitmapData
);
606 MmFreeMemory(IndexRecord
);
610 DbgPrint((DPRINT_FILESYSTEM
, "Can't read MFT record\n"));
612 MmFreeMemory(MftRecord
);
617 static BOOLEAN
NtfsLookupFile(PCSTR FileName
, PNTFS_MFT_RECORD MftRecord
, PNTFS_ATTR_CONTEXT
*DataContext
)
619 ULONG NumberOfPathParts
;
621 ULONG CurrentMFTIndex
;
624 DbgPrint((DPRINT_FILESYSTEM
, "NtfsLookupFile() FileName = %s\n", FileName
));
626 CurrentMFTIndex
= NTFS_FILE_ROOT
;
627 NumberOfPathParts
= FsGetNumPathParts(FileName
);
628 for (i
= 0; i
< NumberOfPathParts
; i
++)
630 FsGetFirstNameFromPath(PathPart
, FileName
);
632 for (; (*FileName
!= '\\') && (*FileName
!= '/') && (*FileName
!= '\0'); FileName
++)
636 DbgPrint((DPRINT_FILESYSTEM
, "- Lookup: %s\n", PathPart
));
637 if (!NtfsFindMftRecord(CurrentMFTIndex
, PathPart
, &CurrentMFTIndex
))
639 DbgPrint((DPRINT_FILESYSTEM
, "- Failed\n"));
642 DbgPrint((DPRINT_FILESYSTEM
, "- Lookup: %x\n", CurrentMFTIndex
));
645 if (!NtfsReadMftRecord(CurrentMFTIndex
, MftRecord
))
647 DbgPrint((DPRINT_FILESYSTEM
, "NtfsLookupFile: Can't read MFT record\n"));
651 *DataContext
= NtfsFindAttribute(MftRecord
, NTFS_ATTR_TYPE_DATA
, L
"");
652 if (*DataContext
== NULL
)
654 DbgPrint((DPRINT_FILESYSTEM
, "NtfsLookupFile: Can't find data attribute\n"));
661 BOOLEAN
NtfsOpenVolume(ULONG DriveNumber
, ULONG VolumeStartSector
)
663 NtfsBootSector
= (PNTFS_BOOTSECTOR
)DISKREADBUFFER
;
665 DbgPrint((DPRINT_FILESYSTEM
, "NtfsOpenVolume() DriveNumber = 0x%x VolumeStartSector = 0x%x\n", DriveNumber
, VolumeStartSector
));
667 if (!MachDiskReadLogicalSectors(DriveNumber
, VolumeStartSector
, 1, (PCHAR
)DISKREADBUFFER
))
669 FileSystemError("Failed to read the boot sector.");
673 if (!RtlEqualMemory(NtfsBootSector
->SystemId
, "NTFS", 4))
675 FileSystemError("Invalid NTFS signature.");
679 NtfsBootSector
= MmAllocateMemory(NtfsBootSector
->BytesPerSector
);
680 if (NtfsBootSector
== NULL
)
685 RtlCopyMemory(NtfsBootSector
, (PCHAR
)DISKREADBUFFER
, ((PNTFS_BOOTSECTOR
)DISKREADBUFFER
)->BytesPerSector
);
687 NtfsClusterSize
= NtfsBootSector
->SectorsPerCluster
* NtfsBootSector
->BytesPerSector
;
688 if (NtfsBootSector
->ClustersPerMftRecord
> 0)
689 NtfsMftRecordSize
= NtfsBootSector
->ClustersPerMftRecord
* NtfsClusterSize
;
691 NtfsMftRecordSize
= 1 << (-NtfsBootSector
->ClustersPerMftRecord
);
692 if (NtfsBootSector
->ClustersPerIndexRecord
> 0)
693 NtfsIndexRecordSize
= NtfsBootSector
->ClustersPerIndexRecord
* NtfsClusterSize
;
695 NtfsIndexRecordSize
= 1 << (-NtfsBootSector
->ClustersPerIndexRecord
);
697 DbgPrint((DPRINT_FILESYSTEM
, "NtfsClusterSize: 0x%x\n", NtfsClusterSize
));
698 DbgPrint((DPRINT_FILESYSTEM
, "ClustersPerMftRecord: %d\n", NtfsBootSector
->ClustersPerMftRecord
));
699 DbgPrint((DPRINT_FILESYSTEM
, "ClustersPerIndexRecord: %d\n", NtfsBootSector
->ClustersPerIndexRecord
));
700 DbgPrint((DPRINT_FILESYSTEM
, "NtfsMftRecordSize: 0x%x\n", NtfsMftRecordSize
));
701 DbgPrint((DPRINT_FILESYSTEM
, "NtfsIndexRecordSize: 0x%x\n", NtfsIndexRecordSize
));
703 NtfsDriveNumber
= DriveNumber
;
704 NtfsSectorOfClusterZero
= VolumeStartSector
;
706 DbgPrint((DPRINT_FILESYSTEM
, "Reading MFT index...\n"));
707 if (!MachDiskReadLogicalSectors(DriveNumber
,
708 NtfsSectorOfClusterZero
+
709 (NtfsBootSector
->MftLocation
* NtfsBootSector
->SectorsPerCluster
),
710 NtfsMftRecordSize
/ NtfsBootSector
->BytesPerSector
, (PCHAR
)DISKREADBUFFER
))
712 FileSystemError("Failed to read the Master File Table record.");
716 NtfsMasterFileTable
= MmAllocateMemory(NtfsMftRecordSize
);
717 if (NtfsMasterFileTable
== NULL
)
719 MmFreeMemory(NtfsBootSector
);
723 RtlCopyMemory(NtfsMasterFileTable
, (PCHAR
)DISKREADBUFFER
, NtfsMftRecordSize
);
725 DbgPrint((DPRINT_FILESYSTEM
, "Searching for DATA attribute...\n"));
726 NtfsMFTContext
= NtfsFindAttribute(NtfsMasterFileTable
, NTFS_ATTR_TYPE_DATA
, L
"");
727 if (NtfsMFTContext
== NULL
)
729 FileSystemError("Can't find data attribute for Master File Table.");
736 FILE* NtfsOpenFile(PCSTR FileName
)
738 PNTFS_FILE_HANDLE FileHandle
;
739 PNTFS_MFT_RECORD MftRecord
;
741 FileHandle
= MmAllocateMemory(sizeof(NTFS_FILE_HANDLE
) + NtfsMftRecordSize
);
742 if (FileHandle
== NULL
)
747 MftRecord
= (PNTFS_MFT_RECORD
)(FileHandle
+ 1);
748 if (!NtfsLookupFile(FileName
, MftRecord
, &FileHandle
->DataContext
))
750 MmFreeMemory(FileHandle
);
754 FileHandle
->Offset
= 0;
756 return (FILE*)FileHandle
;
759 VOID
NtfsCloseFile(FILE *File
)
761 PNTFS_FILE_HANDLE FileHandle
= (PNTFS_FILE_HANDLE
)File
;
762 NtfsReleaseAttributeContext(FileHandle
->DataContext
);
763 MmFreeMemory(FileHandle
);
766 BOOLEAN
NtfsReadFile(FILE *File
, ULONG BytesToRead
, ULONG
* BytesRead
, PVOID Buffer
)
768 PNTFS_FILE_HANDLE FileHandle
= (PNTFS_FILE_HANDLE
)File
;
769 ULONGLONG BytesRead64
;
770 BytesRead64
= NtfsReadAttribute(FileHandle
->DataContext
, FileHandle
->Offset
, Buffer
, BytesToRead
);
773 *BytesRead
= (ULONG
)BytesRead64
;
774 FileHandle
->Offset
+= BytesRead64
;
780 ULONG
NtfsGetFileSize(FILE *File
)
782 PNTFS_FILE_HANDLE FileHandle
= (PNTFS_FILE_HANDLE
)File
;
783 return (ULONG
)NtfsGetAttributeSize(&FileHandle
->DataContext
->Record
);
786 VOID
NtfsSetFilePointer(FILE *File
, ULONG NewFilePointer
)
788 PNTFS_FILE_HANDLE FileHandle
= (PNTFS_FILE_HANDLE
)File
;
789 FileHandle
->Offset
= NewFilePointer
;
792 ULONG
NtfsGetFilePointer(FILE *File
)
794 PNTFS_FILE_HANDLE FileHandle
= (PNTFS_FILE_HANDLE
)File
;
795 return FileHandle
->Offset
;