- Remove some header duplication.
[reactos.git] / reactos / boot / freeldr / freeldr / fs / ntfs.c
1 /*
2 * FreeLoader NTFS support
3 * Copyright (C) 2004 Filip Navara <xnavara@volny.cz>
4 *
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.
9 *
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.
14 *
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.
18 */
19
20 /*
21 * Limitations:
22 * - No support for compressed files.
23 * - No attribute list support.
24 * - May crash on corrupted filesystem.
25 */
26
27 #include <freeldr.h>
28
29 #define NDEBUG
30 #include <debug.h>
31
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;
40
41 static PUCHAR NtfsDecodeRun(PUCHAR DataRun, LONGLONG *DataRunOffset, ULONGLONG *DataRunLength)
42 {
43 UCHAR DataRunOffsetSize;
44 UCHAR DataRunLengthSize;
45 CHAR i;
46
47 DataRunOffsetSize = (*DataRun >> 4) & 0xF;
48 DataRunLengthSize = *DataRun & 0xF;
49 *DataRunOffset = 0;
50 *DataRunLength = 0;
51 DataRun++;
52 for (i = 0; i < DataRunLengthSize; i++)
53 {
54 *DataRunLength += *DataRun << (i << 3);
55 DataRun++;
56 }
57
58 /* NTFS 3+ sparse files */
59 if (DataRunOffsetSize == 0)
60 {
61 *DataRunOffset = -1;
62 }
63 else
64 {
65 for (i = 0; i < DataRunOffsetSize - 1; i++)
66 {
67 *DataRunOffset += *DataRun << (i << 3);
68 DataRun++;
69 }
70 /* The last byte contains sign so we must process it different way. */
71 *DataRunOffset = ((CHAR)(*(DataRun++)) << (i << 3)) + *DataRunOffset;
72 }
73
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));
78
79 return DataRun;
80 }
81
82 /* FIXME: Add support for attribute lists! */
83 static BOOL NtfsFindAttribute(PNTFS_ATTR_CONTEXT Context, PNTFS_MFT_RECORD MftRecord, ULONG Type, const WCHAR *Name)
84 {
85 PNTFS_ATTR_RECORD AttrRecord;
86 PNTFS_ATTR_RECORD AttrRecordEnd;
87 ULONG NameLength;
88 PWCHAR AttrName;
89
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++)
93 ;
94
95 while (AttrRecord < AttrRecordEnd)
96 {
97 if (AttrRecord->Type == NTFS_ATTR_TYPE_END)
98 break;
99
100 if (AttrRecord->Type == Type)
101 {
102 if (AttrRecord->NameLength == NameLength)
103 {
104 AttrName = (PWCHAR)((PCHAR)AttrRecord + AttrRecord->NameOffset);
105 if (RtlEqualMemory(AttrName, Name, NameLength << 1))
106 {
107 /* Found it, fill up the context and return. */
108 Context->Record = AttrRecord;
109 if (AttrRecord->IsNonResident)
110 {
111 LONGLONG DataRunOffset;
112 ULONGLONG DataRunLength;
113
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)
119 {
120 /* Normal run. */
121 Context->CacheRunStartLCN =
122 Context->CacheRunLastLCN = DataRunOffset;
123 }
124 else
125 {
126 /* Sparse run. */
127 Context->CacheRunStartLCN = -1;
128 Context->CacheRunLastLCN = 0;
129 }
130 Context->CacheRunCurrentOffset = 0;
131 }
132 return TRUE;
133 }
134 }
135 }
136
137 AttrRecord = (PNTFS_ATTR_RECORD)((PCHAR)AttrRecord + AttrRecord->Length);
138 }
139
140 return FALSE;
141 }
142
143 /* FIXME: Optimize for multisector reads. */
144 static BOOL NtfsDiskRead(ULONGLONG Offset, ULONGLONG Length, PCHAR Buffer)
145 {
146 USHORT ReadLength;
147
148 DbgPrint((DPRINT_FILESYSTEM, "NtfsDiskRead - Offset: %I64d Length: %I64d\n", Offset, Length));
149 RtlZeroMemory((PCHAR)DISKREADBUFFER, 0x1000);
150
151 /* I. Read partial first sector if needed */
152 if (Offset % NtfsBootSector->BytesPerSector)
153 {
154 if (!MachDiskReadLogicalSectors(NtfsDriveNumber, NtfsSectorOfClusterZero + (Offset / NtfsBootSector->BytesPerSector), 1, (PCHAR)DISKREADBUFFER))
155 return FALSE;
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;
161 }
162
163 /* II. Read all complete 64-sector blocks. */
164 while (Length >= (ULONGLONG)64 * (ULONGLONG)NtfsBootSector->BytesPerSector)
165 {
166 if (!MachDiskReadLogicalSectors(NtfsDriveNumber, NtfsSectorOfClusterZero + (Offset / NtfsBootSector->BytesPerSector), 64, (PCHAR)DISKREADBUFFER))
167 return FALSE;
168 RtlCopyMemory(Buffer, (PCHAR)DISKREADBUFFER, 64 * NtfsBootSector->BytesPerSector);
169 Buffer += 64 * NtfsBootSector->BytesPerSector;
170 Length -= 64 * NtfsBootSector->BytesPerSector;
171 Offset += 64 * NtfsBootSector->BytesPerSector;
172 }
173
174 /* III. Read the rest of data */
175 if (Length)
176 {
177 ReadLength = ((Length + NtfsBootSector->BytesPerSector - 1) / NtfsBootSector->BytesPerSector);
178 if (!MachDiskReadLogicalSectors(NtfsDriveNumber, NtfsSectorOfClusterZero + (Offset / NtfsBootSector->BytesPerSector), ReadLength, (PCHAR)DISKREADBUFFER))
179 return FALSE;
180 RtlCopyMemory(Buffer, (PCHAR)DISKREADBUFFER, Length);
181 }
182
183 return TRUE;
184 }
185
186 static ULONGLONG NtfsReadAttribute(PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset, PCHAR Buffer, ULONGLONG Length)
187 {
188 ULONGLONG LastLCN;
189 PUCHAR DataRun;
190 LONGLONG DataRunOffset;
191 ULONGLONG DataRunLength;
192 LONGLONG DataRunStartLCN;
193 ULONGLONG CurrentOffset;
194 ULONGLONG ReadLength;
195 ULONGLONG AlreadyRead;
196
197 if (!Context->Record->IsNonResident)
198 {
199 if (Offset > Context->Record->Resident.ValueLength)
200 return 0;
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);
204 return Length;
205 }
206
207 /*
208 * Non-resident attribute
209 */
210
211 /*
212 * I. Find the corresponding start data run.
213 */
214
215 if (Context->CacheRunOffset == Offset)
216 {
217 DataRun = Context->CacheRun;
218 LastLCN = Context->CacheRunLastLCN;
219 DataRunStartLCN = Context->CacheRunStartLCN;
220 DataRunLength = Context->CacheRunLength;
221 CurrentOffset = Context->CacheRunCurrentOffset;
222 }
223 else
224 {
225 LastLCN = 0;
226 DataRun = (PUCHAR)Context->Record + Context->Record->NonResident.MappingPairsOffset;
227 CurrentOffset = 0;
228
229 while (1)
230 {
231 DataRun = NtfsDecodeRun(DataRun, &DataRunOffset, &DataRunLength);
232 if (DataRunOffset != -1)
233 {
234 /* Normal data run. */
235 DataRunStartLCN = LastLCN + DataRunOffset;
236 LastLCN = DataRunStartLCN;
237 }
238 else
239 {
240 /* Sparse data run. */
241 DataRunStartLCN = -1;
242 }
243
244 if (Offset >= CurrentOffset &&
245 Offset < CurrentOffset + (DataRunLength * NtfsClusterSize))
246 {
247 break;
248 }
249
250 if (*DataRun == 0)
251 {
252 return 0;
253 }
254
255 CurrentOffset += DataRunLength * NtfsClusterSize;
256 }
257 }
258
259 /*
260 * II. Go through the run list and read the data
261 */
262
263 AlreadyRead = 0;
264 while (Length > 0)
265 {
266 ReadLength = min(DataRunLength * NtfsClusterSize, Length);
267 if (DataRunStartLCN == -1)
268 RtlZeroMemory(Buffer, ReadLength);
269 else if (!NtfsDiskRead(DataRunStartLCN * NtfsClusterSize + Offset - CurrentOffset, ReadLength, Buffer))
270 break;
271 Length -= ReadLength;
272 Buffer += ReadLength;
273 AlreadyRead += ReadLength;
274
275 /* We finished this request, but there still data in this data run. */
276 if (Length == 0 && ReadLength != DataRunLength * NtfsClusterSize)
277 break;
278
279 /*
280 * Go to next run in the list.
281 */
282
283 if (*DataRun == 0)
284 break;
285 DataRun = NtfsDecodeRun(DataRun, &DataRunOffset, &DataRunLength);
286 if (DataRunOffset != -1)
287 {
288 /* Normal data run. */
289 DataRunStartLCN = LastLCN + DataRunOffset;
290 LastLCN = DataRunStartLCN;
291 }
292 else
293 {
294 /* Sparse data run. */
295 DataRunStartLCN = -1;
296 }
297 CurrentOffset += DataRunLength * NtfsClusterSize;
298 }
299
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;
306
307 return AlreadyRead;
308 }
309
310 static BOOL NtfsFixupRecord(PNTFS_RECORD Record)
311 {
312 USHORT *USA;
313 USHORT USANumber;
314 USHORT USACount;
315 USHORT *Block;
316
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);
321
322 while (USACount)
323 {
324 if (*Block != USANumber)
325 return FALSE;
326 *Block = *(USA++);
327 Block = (USHORT*)((PCHAR)Block + NtfsBootSector->BytesPerSector);
328 USACount--;
329 }
330
331 return TRUE;
332 }
333
334 static BOOL NtfsReadMftRecord(ULONG MFTIndex, PNTFS_MFT_RECORD Buffer)
335 {
336 ULONGLONG BytesRead;
337
338 BytesRead = NtfsReadAttribute(&NtfsMFTContext, MFTIndex * NtfsMftRecordSize, (PCHAR)Buffer, NtfsMftRecordSize);
339 if (BytesRead != NtfsMftRecordSize)
340 return FALSE;
341
342 /* Apply update sequence array fixups. */
343 return NtfsFixupRecord((PNTFS_RECORD)Buffer);
344 }
345
346 #ifdef DEBUG
347 VOID NtfsPrintFile(PNTFS_INDEX_ENTRY IndexEntry)
348 {
349 PWCHAR FileName;
350 UCHAR FileNameLength;
351 CHAR AnsiFileName[256];
352 UCHAR i;
353
354 FileName = IndexEntry->FileName.FileName;
355 FileNameLength = IndexEntry->FileName.FileNameLength;
356
357 for (i = 0; i < FileNameLength; i++)
358 AnsiFileName[i] = FileName[i];
359 AnsiFileName[i] = 0;
360
361 DbgPrint((DPRINT_FILESYSTEM, "- %s (%x)\n", AnsiFileName, IndexEntry->Data.Directory.IndexedFile));
362 }
363 #endif
364
365 static BOOL NtfsCompareFileName(PCHAR FileName, PNTFS_INDEX_ENTRY IndexEntry)
366 {
367 PWCHAR EntryFileName;
368 UCHAR EntryFileNameLength;
369 UCHAR i;
370
371 EntryFileName = IndexEntry->FileName.FileName;
372 EntryFileNameLength = IndexEntry->FileName.FileNameLength;
373
374 #ifdef DEBUG
375 NtfsPrintFile(IndexEntry);
376 #endif
377
378 if (strlen(FileName) != EntryFileNameLength)
379 return FALSE;
380
381 /* Do case-sensitive compares for Posix file names. */
382 if (IndexEntry->FileName.FileNameType == NTFS_FILE_NAME_POSIX)
383 {
384 for (i = 0; i < EntryFileNameLength; i++)
385 if (EntryFileName[i] != FileName[i])
386 return FALSE;
387 }
388 else
389 {
390 for (i = 0; i < EntryFileNameLength; i++)
391 if (tolower(EntryFileName[i]) != tolower(FileName[i]))
392 return FALSE;
393 }
394
395 return TRUE;
396 }
397
398 static BOOL NtfsFindMftRecord(ULONG MFTIndex, PCHAR FileName, ULONG *OutMFTIndex)
399 {
400 PNTFS_MFT_RECORD MftRecord;
401 ULONG Magic;
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;
408 PCHAR BitmapData;
409 PCHAR IndexRecord;
410 PNTFS_INDEX_ENTRY IndexEntry, IndexEntryEnd;
411 ULONG RecordOffset;
412 ULONG IndexBlockSize;
413
414 MftRecord = MmAllocateMemory(NtfsMftRecordSize);
415 if (MftRecord == NULL)
416 {
417 return FALSE;
418 }
419
420 if (NtfsReadMftRecord(MFTIndex, MftRecord))
421 {
422 Magic = MftRecord->Magic;
423
424 if (!NtfsFindAttribute(&IndexRootCtx, MftRecord, NTFS_ATTR_TYPE_INDEX_ROOT, L"$I30"))
425 {
426 MmFreeMemory(MftRecord);
427 return FALSE;
428 }
429
430 IndexRecord = MmAllocateMemory(NtfsIndexRecordSize);
431 if (IndexRecord == NULL)
432 {
433 MmFreeMemory(MftRecord);
434 return FALSE;
435 }
436
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);
442
443 DbgPrint((DPRINT_FILESYSTEM, "NtfsIndexRecordSize: %x IndexBlockSize: %x\n", NtfsIndexRecordSize, IndexRoot->IndexBlockSize));
444
445 while (IndexEntry < IndexEntryEnd &&
446 !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
447 {
448 if (NtfsCompareFileName(FileName, IndexEntry))
449 {
450 *OutMFTIndex = IndexEntry->Data.Directory.IndexedFile;
451 MmFreeMemory(IndexRecord);
452 MmFreeMemory(MftRecord);
453 return TRUE;
454 }
455 IndexEntry = (PNTFS_INDEX_ENTRY)((PCHAR)IndexEntry + IndexEntry->Length);
456 }
457
458 if (IndexRoot->IndexHeader.Flags & NTFS_LARGE_INDEX)
459 {
460 DbgPrint((DPRINT_FILESYSTEM, "Large Index!\n"));
461
462 IndexBlockSize = IndexRoot->IndexBlockSize;
463
464 if (!NtfsFindAttribute(&IndexBitmapCtx, MftRecord, NTFS_ATTR_TYPE_BITMAP, L"$I30"))
465 {
466 DbgPrint((DPRINT_FILESYSTEM, "Corrupted filesystem!\n"));
467 MmFreeMemory(MftRecord);
468 return FALSE;
469 }
470 if (IndexBitmapCtx.Record->IsNonResident)
471 BitmapDataSize = IndexBitmapCtx.Record->NonResident.DataSize;
472 else
473 BitmapDataSize = IndexBitmapCtx.Record->Resident.ValueLength;
474 DbgPrint((DPRINT_FILESYSTEM, "BitmapDataSize: %x\n", BitmapDataSize));
475 BitmapData = MmAllocateMemory(BitmapDataSize);
476 if (BitmapData == NULL)
477 {
478 MmFreeMemory(IndexRecord);
479 MmFreeMemory(MftRecord);
480 return FALSE;
481 }
482 NtfsReadAttribute(&IndexBitmapCtx, 0, BitmapData, BitmapDataSize);
483
484 if (!NtfsFindAttribute(&IndexAllocationCtx, MftRecord, NTFS_ATTR_TYPE_INDEX_ALLOCATION, L"$I30"))
485 {
486 DbgPrint((DPRINT_FILESYSTEM, "Corrupted filesystem!\n"));
487 MmFreeMemory(BitmapData);
488 MmFreeMemory(IndexRecord);
489 MmFreeMemory(MftRecord);
490 return FALSE;
491 }
492 if (IndexAllocationCtx.Record->IsNonResident)
493 IndexAllocationSize = IndexAllocationCtx.Record->NonResident.DataSize;
494 else
495 IndexAllocationSize = IndexAllocationCtx.Record->Resident.ValueLength;
496
497 RecordOffset = 0;
498
499 for (;;)
500 {
501 DbgPrint((DPRINT_FILESYSTEM, "RecordOffset: %x IndexAllocationSize: %x\n", RecordOffset, IndexAllocationSize));
502 for (; RecordOffset < IndexAllocationSize;)
503 {
504 UCHAR Bit = 1 << ((RecordOffset / IndexBlockSize) & 7);
505 ULONG Byte = (RecordOffset / IndexBlockSize) >> 3;
506 if ((BitmapData[Byte] & Bit))
507 break;
508 RecordOffset += IndexBlockSize;
509 }
510
511 if (RecordOffset >= IndexAllocationSize)
512 {
513 break;
514 }
515
516 NtfsReadAttribute(&IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize);
517
518 if (!NtfsFixupRecord((PNTFS_RECORD)IndexRecord))
519 {
520 break;
521 }
522
523 /* FIXME */
524 IndexEntry = (PNTFS_INDEX_ENTRY)(IndexRecord + 0x18 + *(USHORT *)(IndexRecord + 0x18));
525 IndexEntryEnd = (PNTFS_INDEX_ENTRY)(IndexRecord + IndexBlockSize);
526
527 while (IndexEntry < IndexEntryEnd &&
528 !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
529 {
530 if (NtfsCompareFileName(FileName, IndexEntry))
531 {
532 DbgPrint((DPRINT_FILESYSTEM, "File found\n"));
533 *OutMFTIndex = IndexEntry->Data.Directory.IndexedFile;
534 MmFreeMemory(BitmapData);
535 MmFreeMemory(IndexRecord);
536 MmFreeMemory(MftRecord);
537 return TRUE;
538 }
539 IndexEntry = (PNTFS_INDEX_ENTRY)((PCHAR)IndexEntry + IndexEntry->Length);
540 }
541
542 RecordOffset += IndexBlockSize;
543 }
544
545 MmFreeMemory(BitmapData);
546 }
547
548 MmFreeMemory(IndexRecord);
549 }
550 else
551 {
552 DbgPrint((DPRINT_FILESYSTEM, "Can't read MFT record\n"));
553 }
554 MmFreeMemory(MftRecord);
555
556 return FALSE;
557 }
558
559 static BOOL NtfsLookupFile(PCSTR FileName, PNTFS_MFT_RECORD MftRecord, PNTFS_ATTR_CONTEXT DataContext)
560 {
561 ULONG NumberOfPathParts;
562 CHAR PathPart[261];
563 ULONG CurrentMFTIndex;
564 UCHAR i;
565
566 DbgPrint((DPRINT_FILESYSTEM, "NtfsLookupFile() FileName = %s\n", FileName));
567
568 CurrentMFTIndex = NTFS_FILE_ROOT;
569 NumberOfPathParts = FsGetNumPathParts(FileName);
570 for (i = 0; i < NumberOfPathParts; i++)
571 {
572 FsGetFirstNameFromPath(PathPart, FileName);
573
574 for (; (*FileName != '\\') && (*FileName != '/') && (*FileName != '\0'); FileName++)
575 ;
576 FileName++;
577
578 DbgPrint((DPRINT_FILESYSTEM, "- Lookup: %s\n", PathPart));
579 if (!NtfsFindMftRecord(CurrentMFTIndex, PathPart, &CurrentMFTIndex))
580 {
581 DbgPrint((DPRINT_FILESYSTEM, "- Failed\n"));
582 return FALSE;
583 }
584 DbgPrint((DPRINT_FILESYSTEM, "- Lookup: %x\n", CurrentMFTIndex));
585 }
586
587 if (!NtfsReadMftRecord(CurrentMFTIndex, MftRecord))
588 {
589 DbgPrint((DPRINT_FILESYSTEM, "NtfsLookupFile: Can't read MFT record\n"));
590 return FALSE;
591 }
592
593 if (!NtfsFindAttribute(DataContext, MftRecord, NTFS_ATTR_TYPE_DATA, L""))
594 {
595 DbgPrint((DPRINT_FILESYSTEM, "NtfsLookupFile: Can't find data attribute\n"));
596 return FALSE;
597 }
598
599 return TRUE;
600 }
601
602 BOOL NtfsOpenVolume(ULONG DriveNumber, ULONG VolumeStartSector)
603 {
604 NtfsBootSector = (PNTFS_BOOTSECTOR)DISKREADBUFFER;
605
606 DbgPrint((DPRINT_FILESYSTEM, "NtfsOpenVolume() DriveNumber = 0x%x VolumeStartSector = 0x%x\n", DriveNumber, VolumeStartSector));
607
608 if (!MachDiskReadLogicalSectors(DriveNumber, VolumeStartSector, 1, (PCHAR)DISKREADBUFFER))
609 {
610 FileSystemError("Failed to read the boot sector.");
611 return FALSE;
612 }
613
614 if (!RtlEqualMemory(NtfsBootSector->SystemId, "NTFS", 4))
615 {
616 FileSystemError("Invalid NTFS signature.");
617 return FALSE;
618 }
619
620 NtfsBootSector = MmAllocateMemory(NtfsBootSector->BytesPerSector);
621 if (NtfsBootSector == NULL)
622 {
623 return FALSE;
624 }
625
626 RtlCopyMemory(NtfsBootSector, (PCHAR)DISKREADBUFFER, ((PNTFS_BOOTSECTOR)DISKREADBUFFER)->BytesPerSector);
627
628 NtfsClusterSize = NtfsBootSector->SectorsPerCluster * NtfsBootSector->BytesPerSector;
629 if (NtfsBootSector->ClustersPerMftRecord > 0)
630 NtfsMftRecordSize = NtfsBootSector->ClustersPerMftRecord * NtfsClusterSize;
631 else
632 NtfsMftRecordSize = 1 << (-NtfsBootSector->ClustersPerMftRecord);
633 if (NtfsBootSector->ClustersPerIndexRecord > 0)
634 NtfsIndexRecordSize = NtfsBootSector->ClustersPerIndexRecord * NtfsClusterSize;
635 else
636 NtfsIndexRecordSize = 1 << (-NtfsBootSector->ClustersPerIndexRecord);
637
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));
643
644 NtfsDriveNumber = DriveNumber;
645 NtfsSectorOfClusterZero = VolumeStartSector;
646
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))
652 {
653 FileSystemError("Failed to read the Master File Table record.");
654 return FALSE;
655 }
656
657 NtfsMasterFileTable = MmAllocateMemory(NtfsMftRecordSize);
658 if (NtfsMasterFileTable == NULL)
659 {
660 MmFreeMemory(NtfsBootSector);
661 return FALSE;
662 }
663
664 RtlCopyMemory(NtfsMasterFileTable, (PCHAR)DISKREADBUFFER, NtfsMftRecordSize);
665
666 DbgPrint((DPRINT_FILESYSTEM, "Searching for DATA attribute...\n"));
667 if (!NtfsFindAttribute(&NtfsMFTContext, NtfsMasterFileTable, NTFS_ATTR_TYPE_DATA, L""))
668 {
669 FileSystemError("Can't find data attribute for Master File Table.");
670 return FALSE;
671 }
672
673 return TRUE;
674 }
675
676 FILE* NtfsOpenFile(PCSTR FileName)
677 {
678 PNTFS_FILE_HANDLE FileHandle;
679 PNTFS_MFT_RECORD MftRecord;
680
681 FileHandle = MmAllocateMemory(sizeof(NTFS_FILE_HANDLE) + NtfsMftRecordSize);
682 if (FileHandle == NULL)
683 {
684 return NULL;
685 }
686
687 MftRecord = (PNTFS_MFT_RECORD)(FileHandle + 1);
688 if (!NtfsLookupFile(FileName, MftRecord, &FileHandle->DataContext))
689 {
690 MmFreeMemory(FileHandle);
691 return NULL;
692 }
693
694 FileHandle->Offset = 0;
695
696 return (FILE*)FileHandle;
697 }
698
699 BOOL NtfsReadFile(FILE *File, ULONG BytesToRead, ULONG* BytesRead, PVOID Buffer)
700 {
701 PNTFS_FILE_HANDLE FileHandle = (PNTFS_FILE_HANDLE)File;
702 ULONGLONG BytesRead64;
703 BytesRead64 = NtfsReadAttribute(&FileHandle->DataContext, FileHandle->Offset, Buffer, BytesToRead);
704 if (BytesRead64)
705 {
706 *BytesRead = (ULONG)BytesRead64;
707 FileHandle->Offset += BytesRead64;
708 return TRUE;
709 }
710 return FALSE;
711 }
712
713 ULONG NtfsGetFileSize(FILE *File)
714 {
715 PNTFS_FILE_HANDLE FileHandle = (PNTFS_FILE_HANDLE)File;
716 if (FileHandle->DataContext.Record->IsNonResident)
717 return (ULONG)FileHandle->DataContext.Record->NonResident.DataSize;
718 else
719 return (ULONG)FileHandle->DataContext.Record->Resident.ValueLength;
720 }
721
722 VOID NtfsSetFilePointer(FILE *File, ULONG NewFilePointer)
723 {
724 PNTFS_FILE_HANDLE FileHandle = (PNTFS_FILE_HANDLE)File;
725 FileHandle->Offset = NewFilePointer;
726 }
727
728 ULONG NtfsGetFilePointer(FILE *File)
729 {
730 PNTFS_FILE_HANDLE FileHandle = (PNTFS_FILE_HANDLE)File;
731 return FileHandle->Offset;
732 }