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