ReactOS  0.4.13-dev-464-g6b95727
ntfs.c
Go to the documentation of this file.
1 /*
2  * FreeLoader NTFS support
3  * Copyright (C) 2004 Filip Navara <xnavara@volny.cz>
4  * Copyright (C) 2009-2010 Hervé Poussineau
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20 
21 /*
22  * Limitations:
23  * - No support for compressed files.
24  * - May crash on corrupted filesystem.
25  */
26 
27 #ifndef _M_ARM
28 #include <freeldr.h>
29 #include <debug.h>
30 
31 #define TAG_NTFS_CONTEXT 'CftN'
32 #define TAG_NTFS_LIST 'LftN'
33 #define TAG_NTFS_MFT 'MftN'
34 #define TAG_NTFS_INDEX_REC 'IftN'
35 #define TAG_NTFS_BITMAP 'BftN'
36 #define TAG_NTFS_FILE 'FftN'
37 #define TAG_NTFS_VOLUME 'VftN'
38 #define TAG_NTFS_DATA 'DftN'
39 
40 DBG_DEFAULT_CHANNEL(FILESYSTEM);
41 
42 typedef struct _NTFS_VOLUME_INFO
43 {
49  /* FIXME: MFTContext is never freed. */
54 
56 
58 {
59  if (AttrRecord->IsNonResident)
60  return AttrRecord->NonResident.DataSize;
61  else
62  return AttrRecord->Resident.ValueLength;
63 }
64 
65 static PUCHAR NtfsDecodeRun(PUCHAR DataRun, LONGLONG *DataRunOffset, ULONGLONG *DataRunLength)
66 {
67  UCHAR DataRunOffsetSize;
68  UCHAR DataRunLengthSize;
69  CHAR i;
70 
71  DataRunOffsetSize = (*DataRun >> 4) & 0xF;
72  DataRunLengthSize = *DataRun & 0xF;
73  *DataRunOffset = 0;
74  *DataRunLength = 0;
75  DataRun++;
76  for (i = 0; i < DataRunLengthSize; i++)
77  {
78  *DataRunLength += ((ULONG64)*DataRun) << (i * 8);
79  DataRun++;
80  }
81 
82  /* NTFS 3+ sparse files */
83  if (DataRunOffsetSize == 0)
84  {
85  *DataRunOffset = -1;
86  }
87  else
88  {
89  for (i = 0; i < DataRunOffsetSize - 1; i++)
90  {
91  *DataRunOffset += ((ULONG64)*DataRun) << (i * 8);
92  DataRun++;
93  }
94  /* The last byte contains sign so we must process it different way. */
95  *DataRunOffset = ((LONG64)(CHAR)(*(DataRun++)) << (i * 8)) + *DataRunOffset;
96  }
97 
98  TRACE("DataRunOffsetSize: %x\n", DataRunOffsetSize);
99  TRACE("DataRunLengthSize: %x\n", DataRunLengthSize);
100  TRACE("DataRunOffset: %x\n", *DataRunOffset);
101  TRACE("DataRunLength: %x\n", *DataRunLength);
102 
103  return DataRun;
104 }
105 
107 {
109 
112  RtlCopyMemory(&Context->Record, AttrRecord, AttrRecord->Length);
113  if (AttrRecord->IsNonResident)
114  {
115  LONGLONG DataRunOffset;
116  ULONGLONG DataRunLength;
117 
118  Context->CacheRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset;
119  Context->CacheRunOffset = 0;
120  Context->CacheRun = NtfsDecodeRun(Context->CacheRun, &DataRunOffset, &DataRunLength);
121  Context->CacheRunLength = DataRunLength;
122  if (DataRunOffset != -1)
123  {
124  /* Normal run. */
125  Context->CacheRunStartLCN =
126  Context->CacheRunLastLCN = DataRunOffset;
127  }
128  else
129  {
130  /* Sparse run. */
131  Context->CacheRunStartLCN = -1;
132  Context->CacheRunLastLCN = 0;
133  }
134  Context->CacheRunCurrentOffset = 0;
135  }
136 
137  return Context;
138 }
139 
141 {
143 }
144 
146 {
148  ULONG Count;
149  ULONG ReadLength;
151 
152  TRACE("NtfsDiskRead - Offset: %I64d Length: %I64d\n", Offset, Length);
153 
154  //
155  // I. Read partial first sector if needed
156  //
157  if (Offset % Volume->BootSector.BytesPerSector)
158  {
159  Position.QuadPart = Offset & ~(Volume->BootSector.BytesPerSector - 1);
160  Status = ArcSeek(Volume->DeviceId, &Position, SeekAbsolute);
161  if (Status != ESUCCESS)
162  return FALSE;
163  Status = ArcRead(Volume->DeviceId, Volume->TemporarySector, Volume->BootSector.BytesPerSector, &Count);
164  if (Status != ESUCCESS || Count != Volume->BootSector.BytesPerSector)
165  return FALSE;
166  ReadLength = (USHORT)min(Length, Volume->BootSector.BytesPerSector - (Offset % Volume->BootSector.BytesPerSector));
167 
168  //
169  // Copy interesting data
170  //
172  &Volume->TemporarySector[Offset % Volume->BootSector.BytesPerSector],
173  ReadLength);
174 
175  //
176  // Move to unfilled buffer part
177  //
178  Buffer += ReadLength;
179  Length -= ReadLength;
180  Offset += ReadLength;
181  }
182 
183  //
184  // II. Read all complete blocks
185  //
186  if (Length >= Volume->BootSector.BytesPerSector)
187  {
188  Position.QuadPart = Offset;
189  Status = ArcSeek(Volume->DeviceId, &Position, SeekAbsolute);
190  if (Status != ESUCCESS)
191  return FALSE;
192  ReadLength = Length & ~(Volume->BootSector.BytesPerSector - 1);
193  Status = ArcRead(Volume->DeviceId, Buffer, ReadLength, &Count);
194  if (Status != ESUCCESS || Count != ReadLength)
195  return FALSE;
196 
197  //
198  // Move to unfilled buffer part
199  //
200  Buffer += ReadLength;
201  Length -= ReadLength;
202  Offset += ReadLength;
203  }
204 
205  //
206  // III. Read the rest of data
207  //
208  if (Length)
209  {
210  Position.QuadPart = Offset;
211  Status = ArcSeek(Volume->DeviceId, &Position, SeekAbsolute);
212  if (Status != ESUCCESS)
213  return FALSE;
214  Status = ArcRead(Volume->DeviceId, Buffer, (ULONG)Length, &Count);
215  if (Status != ESUCCESS || Count != Length)
216  return FALSE;
217  }
218 
219  return TRUE;
220 }
221 
223 {
224  ULONGLONG LastLCN;
225  PUCHAR DataRun;
226  LONGLONG DataRunOffset;
227  ULONGLONG DataRunLength;
228  LONGLONG DataRunStartLCN;
229  ULONGLONG CurrentOffset;
230  ULONG ReadLength;
231  ULONG AlreadyRead;
232 
233  if (!Context->Record.IsNonResident)
234  {
235  if (Offset > Context->Record.Resident.ValueLength)
236  return 0;
237  if (Offset + Length > Context->Record.Resident.ValueLength)
238  Length = (ULONG)(Context->Record.Resident.ValueLength - Offset);
239  RtlCopyMemory(Buffer, (PCHAR)&Context->Record + Context->Record.Resident.ValueOffset + Offset, Length);
240  return Length;
241  }
242 
243  /*
244  * Non-resident attribute
245  */
246 
247  /*
248  * I. Find the corresponding start data run.
249  */
250 
251  AlreadyRead = 0;
252 
253  // FIXME: Cache seems to be non-working. Disable it for now
254  //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
255  if (0)
256  {
257  DataRun = Context->CacheRun;
258  LastLCN = Context->CacheRunLastLCN;
259  DataRunStartLCN = Context->CacheRunStartLCN;
260  DataRunLength = Context->CacheRunLength;
261  CurrentOffset = Context->CacheRunCurrentOffset;
262  }
263  else
264  {
265  LastLCN = 0;
266  DataRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset;
267  CurrentOffset = 0;
268 
269  while (1)
270  {
271  DataRun = NtfsDecodeRun(DataRun, &DataRunOffset, &DataRunLength);
272  if (DataRunOffset != -1)
273  {
274  /* Normal data run. */
275  DataRunStartLCN = LastLCN + DataRunOffset;
276  LastLCN = DataRunStartLCN;
277  }
278  else
279  {
280  /* Sparse data run. */
281  DataRunStartLCN = -1;
282  }
283 
284  if (Offset >= CurrentOffset &&
285  Offset < CurrentOffset + (DataRunLength * Volume->ClusterSize))
286  {
287  break;
288  }
289 
290  if (*DataRun == 0)
291  {
292  return AlreadyRead;
293  }
294 
295  CurrentOffset += DataRunLength * Volume->ClusterSize;
296  }
297  }
298 
299  /*
300  * II. Go through the run list and read the data
301  */
302 
303  ReadLength = (ULONG)min(DataRunLength * Volume->ClusterSize - (Offset - CurrentOffset), Length);
304  if (DataRunStartLCN == -1)
305  RtlZeroMemory(Buffer, ReadLength);
306  if (DataRunStartLCN == -1 || NtfsDiskRead(Volume, DataRunStartLCN * Volume->ClusterSize + Offset - CurrentOffset, ReadLength, Buffer))
307  {
308  Length -= ReadLength;
309  Buffer += ReadLength;
310  AlreadyRead += ReadLength;
311 
312  if (ReadLength == DataRunLength * Volume->ClusterSize - (Offset - CurrentOffset))
313  {
314  CurrentOffset += DataRunLength * Volume->ClusterSize;
315  DataRun = NtfsDecodeRun(DataRun, &DataRunOffset, &DataRunLength);
316  if (DataRunOffset != (ULONGLONG)-1)
317  {
318  DataRunStartLCN = LastLCN + DataRunOffset;
319  LastLCN = DataRunStartLCN;
320  }
321  else
322  DataRunStartLCN = -1;
323  }
324 
325  while (Length > 0)
326  {
327  ReadLength = (ULONG)min(DataRunLength * Volume->ClusterSize, Length);
328  if (DataRunStartLCN == -1)
329  RtlZeroMemory(Buffer, ReadLength);
330  else if (!NtfsDiskRead(Volume, DataRunStartLCN * Volume->ClusterSize, ReadLength, Buffer))
331  break;
332 
333  Length -= ReadLength;
334  Buffer += ReadLength;
335  AlreadyRead += ReadLength;
336 
337  /* We finished this request, but there still data in this data run. */
338  if (Length == 0 && ReadLength != DataRunLength * Volume->ClusterSize)
339  break;
340 
341  /*
342  * Go to next run in the list.
343  */
344 
345  if (*DataRun == 0)
346  break;
347  CurrentOffset += DataRunLength * Volume->ClusterSize;
348  DataRun = NtfsDecodeRun(DataRun, &DataRunOffset, &DataRunLength);
349  if (DataRunOffset != -1)
350  {
351  /* Normal data run. */
352  DataRunStartLCN = LastLCN + DataRunOffset;
353  LastLCN = DataRunStartLCN;
354  }
355  else
356  {
357  /* Sparse data run. */
358  DataRunStartLCN = -1;
359  }
360  } /* while */
361 
362  } /* if Disk */
363 
364  Context->CacheRun = DataRun;
365  Context->CacheRunOffset = Offset + AlreadyRead;
366  Context->CacheRunStartLCN = DataRunStartLCN;
367  Context->CacheRunLength = DataRunLength;
368  Context->CacheRunLastLCN = LastLCN;
369  Context->CacheRunCurrentOffset = CurrentOffset;
370 
371  return AlreadyRead;
372 }
373 
375 {
376  while (AttrRecord < AttrRecordEnd)
377  {
378  if (AttrRecord->Type == NTFS_ATTR_TYPE_END)
379  break;
380 
381  if (AttrRecord->Type == NTFS_ATTR_TYPE_ATTRIBUTE_LIST)
382  {
384  PNTFS_ATTR_CONTEXT ListContext;
385  PVOID ListBuffer;
386  ULONGLONG ListSize;
387  PNTFS_ATTR_RECORD ListAttrRecord;
388  PNTFS_ATTR_RECORD ListAttrRecordEnd;
389 
390  ListContext = NtfsPrepareAttributeContext(AttrRecord);
391 
392  ListSize = NtfsGetAttributeSize(&ListContext->Record);
393  if(ListSize <= 0xFFFFFFFF)
394  ListBuffer = FrLdrTempAlloc((ULONG)ListSize, TAG_NTFS_LIST);
395  else
396  ListBuffer = NULL;
397 
398  if(!ListBuffer)
399  {
400  TRACE("Failed to allocate memory: %x\n", (ULONG)ListSize);
401  continue;
402  }
403 
404  ListAttrRecord = (PNTFS_ATTR_RECORD)ListBuffer;
405  ListAttrRecordEnd = (PNTFS_ATTR_RECORD)((PCHAR)ListBuffer + ListSize);
406 
407  if (NtfsReadAttribute(Volume, ListContext, 0, ListBuffer, (ULONG)ListSize) == ListSize)
408  {
409  Context = NtfsFindAttributeHelper(Volume, ListAttrRecord, ListAttrRecordEnd,
410  Type, Name, NameLength);
411 
412  NtfsReleaseAttributeContext(ListContext);
413  FrLdrTempFree(ListBuffer, TAG_NTFS_LIST);
414 
415  if (Context != NULL)
416  return Context;
417  }
418  }
419 
420  if (AttrRecord->Type == Type)
421  {
422  if (AttrRecord->NameLength == NameLength)
423  {
424  PWCHAR AttrName;
425 
426  AttrName = (PWCHAR)((PCHAR)AttrRecord + AttrRecord->NameOffset);
427  if (RtlEqualMemory(AttrName, Name, NameLength << 1))
428  {
429  /* Found it, fill up the context and return. */
430  return NtfsPrepareAttributeContext(AttrRecord);
431  }
432  }
433  }
434 
435  if (AttrRecord->Length == 0)
436  return NULL;
437  AttrRecord = (PNTFS_ATTR_RECORD)((PCHAR)AttrRecord + AttrRecord->Length);
438  }
439 
440  return NULL;
441 }
442 
444 {
445  PNTFS_ATTR_RECORD AttrRecord;
446  PNTFS_ATTR_RECORD AttrRecordEnd;
447  ULONG NameLength;
448 
449  AttrRecord = (PNTFS_ATTR_RECORD)((PCHAR)MftRecord + MftRecord->AttributesOffset);
450  AttrRecordEnd = (PNTFS_ATTR_RECORD)((PCHAR)MftRecord + Volume->MftRecordSize);
451  for (NameLength = 0; Name[NameLength] != 0; NameLength++)
452  ;
453 
454  return NtfsFindAttributeHelper(Volume, AttrRecord, AttrRecordEnd, Type, Name, NameLength);
455 }
456 
458 {
459  USHORT *USA;
460  USHORT USANumber;
461  USHORT USACount;
462  USHORT *Block;
463 
464  USA = (USHORT*)((PCHAR)Record + Record->USAOffset);
465  USANumber = *(USA++);
466  USACount = Record->USACount - 1; /* Exclude the USA Number. */
467  Block = (USHORT*)((PCHAR)Record + Volume->BootSector.BytesPerSector - 2);
468 
469  while (USACount)
470  {
471  if (*Block != USANumber)
472  return FALSE;
473  *Block = *(USA++);
474  Block = (USHORT*)((PCHAR)Block + Volume->BootSector.BytesPerSector);
475  USACount--;
476  }
477 
478  return TRUE;
479 }
480 
482 {
484 
485  BytesRead = NtfsReadAttribute(Volume, Volume->MFTContext, MFTIndex * Volume->MftRecordSize, (PCHAR)Buffer, Volume->MftRecordSize);
486  if (BytesRead != Volume->MftRecordSize)
487  return FALSE;
488 
489  /* Apply update sequence array fixups. */
491 }
492 
493 #if DBG
494 VOID NtfsPrintFile(PNTFS_INDEX_ENTRY IndexEntry)
495 {
498  CHAR AnsiFileName[256];
499  UCHAR i;
500 
501  FileName = IndexEntry->FileName.FileName;
502  FileNameLength = min(IndexEntry->FileName.FileNameLength, 255);
503 
504  for (i = 0; i < FileNameLength; i++)
505  AnsiFileName[i] = (CHAR)FileName[i];
506  AnsiFileName[i] = 0;
507 
508  TRACE("- %s (%x)\n", AnsiFileName, IndexEntry->Data.Directory.IndexedFile);
509 }
510 #endif
511 
513 {
514  PWCHAR EntryFileName;
515  UCHAR EntryFileNameLength;
516  UCHAR i;
517 
518  EntryFileName = IndexEntry->FileName.FileName;
519  EntryFileNameLength = IndexEntry->FileName.FileNameLength;
520 
521 #if DBG
522  NtfsPrintFile(IndexEntry);
523 #endif
524 
525  if (strlen(FileName) != EntryFileNameLength)
526  return FALSE;
527 
528  /* Do case-sensitive compares for Posix file names. */
529  if (IndexEntry->FileName.FileNameType == NTFS_FILE_NAME_POSIX)
530  {
531  for (i = 0; i < EntryFileNameLength; i++)
532  if (EntryFileName[i] != FileName[i])
533  return FALSE;
534  }
535  else
536  {
537  for (i = 0; i < EntryFileNameLength; i++)
538  if (tolower(EntryFileName[i]) != tolower(FileName[i]))
539  return FALSE;
540  }
541 
542  return TRUE;
543 }
544 
546 {
547  PNTFS_MFT_RECORD MftRecord;
548  //ULONG Magic;
549  PNTFS_ATTR_CONTEXT IndexRootCtx;
550  PNTFS_ATTR_CONTEXT IndexBitmapCtx;
551  PNTFS_ATTR_CONTEXT IndexAllocationCtx;
552  PNTFS_INDEX_ROOT IndexRoot;
553  ULONGLONG BitmapDataSize;
554  ULONGLONG IndexAllocationSize;
556  PCHAR IndexRecord;
557  PNTFS_INDEX_ENTRY IndexEntry, IndexEntryEnd;
558  ULONG RecordOffset;
559  ULONG IndexBlockSize;
560 
561  MftRecord = FrLdrTempAlloc(Volume->MftRecordSize, TAG_NTFS_MFT);
562  if (MftRecord == NULL)
563  {
564  return FALSE;
565  }
566 
567  if (NtfsReadMftRecord(Volume, MFTIndex, MftRecord))
568  {
569  //Magic = MftRecord->Magic;
570 
571  IndexRootCtx = NtfsFindAttribute(Volume, MftRecord, NTFS_ATTR_TYPE_INDEX_ROOT, L"$I30");
572  if (IndexRootCtx == NULL)
573  {
574  FrLdrTempFree(MftRecord, TAG_NTFS_MFT);
575  return FALSE;
576  }
577 
578  IndexRecord = FrLdrTempAlloc(Volume->IndexRecordSize, TAG_NTFS_INDEX_REC);
579  if (IndexRecord == NULL)
580  {
581  FrLdrTempFree(MftRecord, TAG_NTFS_MFT);
582  return FALSE;
583  }
584 
585  NtfsReadAttribute(Volume, IndexRootCtx, 0, IndexRecord, Volume->IndexRecordSize);
586  IndexRoot = (PNTFS_INDEX_ROOT)IndexRecord;
587  IndexEntry = (PNTFS_INDEX_ENTRY)((PCHAR)&IndexRoot->IndexHeader + IndexRoot->IndexHeader.EntriesOffset);
588  /* Index root is always resident. */
589  IndexEntryEnd = (PNTFS_INDEX_ENTRY)(IndexRecord + IndexRootCtx->Record.Resident.ValueLength);
590  NtfsReleaseAttributeContext(IndexRootCtx);
591 
592  TRACE("IndexRecordSize: %x IndexBlockSize: %x\n", Volume->IndexRecordSize, IndexRoot->IndexBlockSize);
593 
594  while (IndexEntry < IndexEntryEnd &&
595  !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
596  {
597  if (NtfsCompareFileName(FileName, IndexEntry))
598  {
599  *OutMFTIndex = IndexEntry->Data.Directory.IndexedFile;
600  FrLdrTempFree(IndexRecord, TAG_NTFS_INDEX_REC);
601  FrLdrTempFree(MftRecord, TAG_NTFS_MFT);
602  return TRUE;
603  }
604  IndexEntry = (PNTFS_INDEX_ENTRY)((PCHAR)IndexEntry + IndexEntry->Length);
605  }
606 
607  if (IndexRoot->IndexHeader.Flags & NTFS_LARGE_INDEX)
608  {
609  TRACE("Large Index!\n");
610 
611  IndexBlockSize = IndexRoot->IndexBlockSize;
612 
613  IndexBitmapCtx = NtfsFindAttribute(Volume, MftRecord, NTFS_ATTR_TYPE_BITMAP, L"$I30");
614  if (IndexBitmapCtx == NULL)
615  {
616  TRACE("Corrupted filesystem!\n");
617  FrLdrTempFree(MftRecord, TAG_NTFS_MFT);
618  return FALSE;
619  }
620  BitmapDataSize = NtfsGetAttributeSize(&IndexBitmapCtx->Record);
621  TRACE("BitmapDataSize: %x\n", (ULONG)BitmapDataSize);
622  if(BitmapDataSize <= 0xFFFFFFFF)
623  BitmapData = FrLdrTempAlloc((ULONG)BitmapDataSize, TAG_NTFS_BITMAP);
624  else
625  BitmapData = NULL;
626 
627  if (BitmapData == NULL)
628  {
629  FrLdrTempFree(IndexRecord, TAG_NTFS_INDEX_REC);
630  FrLdrTempFree(MftRecord, TAG_NTFS_MFT);
631  return FALSE;
632  }
633  NtfsReadAttribute(Volume, IndexBitmapCtx, 0, BitmapData, (ULONG)BitmapDataSize);
634  NtfsReleaseAttributeContext(IndexBitmapCtx);
635 
636  IndexAllocationCtx = NtfsFindAttribute(Volume, MftRecord, NTFS_ATTR_TYPE_INDEX_ALLOCATION, L"$I30");
637  if (IndexAllocationCtx == NULL)
638  {
639  TRACE("Corrupted filesystem!\n");
641  FrLdrTempFree(IndexRecord, TAG_NTFS_INDEX_REC);
642  FrLdrTempFree(MftRecord, TAG_NTFS_MFT);
643  return FALSE;
644  }
645  IndexAllocationSize = NtfsGetAttributeSize(&IndexAllocationCtx->Record);
646 
647  RecordOffset = 0;
648 
649  for (;;)
650  {
651  TRACE("RecordOffset: %x IndexAllocationSize: %x\n", RecordOffset, IndexAllocationSize);
652  for (; RecordOffset < IndexAllocationSize;)
653  {
654  UCHAR Bit = 1 << ((RecordOffset / IndexBlockSize) & 7);
655  ULONG Byte = (RecordOffset / IndexBlockSize) >> 3;
656  if ((BitmapData[Byte] & Bit))
657  break;
658  RecordOffset += IndexBlockSize;
659  }
660 
661  if (RecordOffset >= IndexAllocationSize)
662  {
663  break;
664  }
665 
666  NtfsReadAttribute(Volume, IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize);
667 
668  if (!NtfsFixupRecord(Volume, (PNTFS_RECORD)IndexRecord))
669  {
670  break;
671  }
672 
673  /* FIXME */
674  IndexEntry = (PNTFS_INDEX_ENTRY)(IndexRecord + 0x18 + *(USHORT *)(IndexRecord + 0x18));
675  IndexEntryEnd = (PNTFS_INDEX_ENTRY)(IndexRecord + IndexBlockSize);
676 
677  while (IndexEntry < IndexEntryEnd &&
678  !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
679  {
680  if (NtfsCompareFileName(FileName, IndexEntry))
681  {
682  TRACE("File found\n");
683  *OutMFTIndex = IndexEntry->Data.Directory.IndexedFile;
685  FrLdrTempFree(IndexRecord, TAG_NTFS_INDEX_REC);
686  FrLdrTempFree(MftRecord, TAG_NTFS_MFT);
687  NtfsReleaseAttributeContext(IndexAllocationCtx);
688  return TRUE;
689  }
690  IndexEntry = (PNTFS_INDEX_ENTRY)((PCHAR)IndexEntry + IndexEntry->Length);
691  }
692 
693  RecordOffset += IndexBlockSize;
694  }
695 
696  NtfsReleaseAttributeContext(IndexAllocationCtx);
698  }
699 
700  FrLdrTempFree(IndexRecord, TAG_NTFS_INDEX_REC);
701  }
702  else
703  {
704  TRACE("Can't read MFT record\n");
705  }
706  FrLdrTempFree(MftRecord, TAG_NTFS_MFT);
707 
708  return FALSE;
709 }
710 
712 {
713  ULONG NumberOfPathParts;
714  CHAR PathPart[261];
715  ULONGLONG CurrentMFTIndex;
716  UCHAR i;
717 
718  TRACE("NtfsLookupFile() FileName = %s\n", FileName);
719 
720  CurrentMFTIndex = NTFS_FILE_ROOT;
721  NumberOfPathParts = FsGetNumPathParts(FileName);
722  for (i = 0; i < NumberOfPathParts; i++)
723  {
724  FsGetFirstNameFromPath(PathPart, FileName);
725 
726  for (; (*FileName != '\\') && (*FileName != '/') && (*FileName != '\0'); FileName++)
727  ;
728  FileName++;
729 
730  TRACE("- Lookup: %s\n", PathPart);
731  if (!NtfsFindMftRecord(Volume, CurrentMFTIndex, PathPart, &CurrentMFTIndex))
732  {
733  TRACE("- Failed\n");
734  return FALSE;
735  }
736  TRACE("- Lookup: %x\n", CurrentMFTIndex);
737  }
738 
739  if (!NtfsReadMftRecord(Volume, CurrentMFTIndex, MftRecord))
740  {
741  TRACE("NtfsLookupFile: Can't read MFT record\n");
742  return FALSE;
743  }
744 
745  *DataContext = NtfsFindAttribute(Volume, MftRecord, NTFS_ATTR_TYPE_DATA, L"");
746  if (*DataContext == NULL)
747  {
748  TRACE("NtfsLookupFile: Can't find data attribute\n");
749  return FALSE;
750  }
751 
752  return TRUE;
753 }
754 
756 {
758 
761 
762  return ESUCCESS;
763 }
764 
766 {
768 
770  Information->EndingAddress.QuadPart = NtfsGetAttributeSize(&FileHandle->DataContext->Record);
771  Information->CurrentAddress.QuadPart = FileHandle->Offset;
772 
773  TRACE("NtfsGetFileInformation() FileSize = %d\n",
774  Information->EndingAddress.LowPart);
775  TRACE("NtfsGetFileInformation() FilePointer = %d\n",
776  Information->CurrentAddress.LowPart);
777 
778  return ESUCCESS;
779 }
780 
782 {
785  PNTFS_MFT_RECORD MftRecord;
786  ULONG DeviceId;
787 
788  //
789  // Check parameters
790  //
791  if (OpenMode != OpenReadOnly)
792  return EACCES;
793 
794  //
795  // Get underlying device
796  //
797  DeviceId = FsGetDeviceId(*FileId);
798  Volume = NtfsVolumes[DeviceId];
799 
800  TRACE("NtfsOpen() FileName = %s\n", Path);
801 
802  //
803  // Allocate file structure
804  //
805  FileHandle = FrLdrTempAlloc(sizeof(NTFS_FILE_HANDLE) + Volume->MftRecordSize,
806  TAG_NTFS_FILE);
807  if (!FileHandle)
808  {
809  return ENOMEM;
810  }
811  RtlZeroMemory(FileHandle, sizeof(NTFS_FILE_HANDLE) + Volume->MftRecordSize);
812  FileHandle->Volume = Volume;
813 
814  //
815  // Search file entry
816  //
817  MftRecord = (PNTFS_MFT_RECORD)(FileHandle + 1);
818  if (!NtfsLookupFile(Volume, Path, MftRecord, &FileHandle->DataContext))
819  {
821  return ENOENT;
822  }
823 
825  return ESUCCESS;
826 }
827 
829 {
831  ULONGLONG BytesRead64;
832 
833  //
834  // Read file
835  //
836  BytesRead64 = NtfsReadAttribute(FileHandle->Volume, FileHandle->DataContext, FileHandle->Offset, Buffer, N);
837  *Count = (ULONG)BytesRead64;
838 
839  //
840  // Check for success
841  //
842  if (BytesRead64 > 0)
843  return ESUCCESS;
844  else
845  return EIO;
846 }
847 
849 {
851 
852  TRACE("NtfsSeek() NewFilePointer = %lu\n", Position->LowPart);
853 
854  if (SeekMode != SeekAbsolute)
855  return EINVAL;
856  if (Position->HighPart != 0)
857  return EINVAL;
858  if (Position->LowPart >= (ULONG)NtfsGetAttributeSize(&FileHandle->DataContext->Record))
859  return EINVAL;
860 
861  FileHandle->Offset = Position->LowPart;
862  return ESUCCESS;
863 }
864 
866 {
867  NtfsClose,
869  NtfsOpen,
870  NtfsRead,
871  NtfsSeek,
872  L"ntfs",
873 };
874 
875 const DEVVTBL* NtfsMount(ULONG DeviceId)
876 {
879  ULONG Count;
881 
882  //
883  // Allocate data for volume information
884  //
886  if (!Volume)
887  return NULL;
889 
890  //
891  // Read the BootSector
892  //
893  Position.HighPart = 0;
894  Position.LowPart = 0;
895  Status = ArcSeek(DeviceId, &Position, SeekAbsolute);
896  if (Status != ESUCCESS)
897  {
899  return NULL;
900  }
901  Status = ArcRead(DeviceId, &Volume->BootSector, sizeof(Volume->BootSector), &Count);
902  if (Status != ESUCCESS || Count != sizeof(Volume->BootSector))
903  {
905  return NULL;
906  }
907 
908  //
909  // Check if BootSector is valid. If no, return early
910  //
911  if (!RtlEqualMemory(Volume->BootSector.SystemId, "NTFS", 4))
912  {
914  return NULL;
915  }
916 
917  //
918  // Calculate cluster size and MFT record size
919  //
920  Volume->ClusterSize = Volume->BootSector.SectorsPerCluster * Volume->BootSector.BytesPerSector;
921  if (Volume->BootSector.ClustersPerMftRecord > 0)
922  Volume->MftRecordSize = Volume->BootSector.ClustersPerMftRecord * Volume->ClusterSize;
923  else
924  Volume->MftRecordSize = 1 << (-Volume->BootSector.ClustersPerMftRecord);
925  if (Volume->BootSector.ClustersPerIndexRecord > 0)
926  Volume->IndexRecordSize = Volume->BootSector.ClustersPerIndexRecord * Volume->ClusterSize;
927  else
928  Volume->IndexRecordSize = 1 << (-Volume->BootSector.ClustersPerIndexRecord);
929 
930  TRACE("ClusterSize: 0x%x\n", Volume->ClusterSize);
931  TRACE("ClustersPerMftRecord: %d\n", Volume->BootSector.ClustersPerMftRecord);
932  TRACE("ClustersPerIndexRecord: %d\n", Volume->BootSector.ClustersPerIndexRecord);
933  TRACE("MftRecordSize: 0x%x\n", Volume->MftRecordSize);
934  TRACE("IndexRecordSize: 0x%x\n", Volume->IndexRecordSize);
935 
936  //
937  // Read MFT index
938  //
939  TRACE("Reading MFT index...\n");
940  Volume->MasterFileTable = FrLdrTempAlloc(Volume->MftRecordSize, TAG_NTFS_MFT);
941  if (!Volume->MasterFileTable)
942  {
944  return NULL;
945  }
946  Position.QuadPart = Volume->BootSector.MftLocation * Volume->ClusterSize;
947  Status = ArcSeek(DeviceId, &Position, SeekAbsolute);
948  if (Status != ESUCCESS)
949  {
950  FileSystemError("Failed to seek to Master File Table record.");
951  FrLdrTempFree(Volume->MasterFileTable, TAG_NTFS_MFT);
953  return NULL;
954  }
955  Status = ArcRead(DeviceId, Volume->MasterFileTable, Volume->MftRecordSize, &Count);
956  if (Status != ESUCCESS || Count != Volume->MftRecordSize)
957  {
958  FileSystemError("Failed to read the Master File Table record.");
959  FrLdrTempFree(Volume->MasterFileTable, TAG_NTFS_MFT);
961  return NULL;
962  }
963 
964  //
965  // Keep room to read partial sectors
966  //
967  Volume->TemporarySector = FrLdrTempAlloc(Volume->BootSector.BytesPerSector, TAG_NTFS_DATA);
968  if (!Volume->TemporarySector)
969  {
970  FileSystemError("Failed to allocate memory.");
971  FrLdrTempFree(Volume->MasterFileTable, TAG_NTFS_MFT);
973  return NULL;
974  }
975 
976  //
977  // Keep device id
978  //
979  Volume->DeviceId = DeviceId;
980 
981  //
982  // Search DATA attribute
983  //
984  TRACE("Searching for DATA attribute...\n");
985  Volume->MFTContext = NtfsFindAttribute(Volume, Volume->MasterFileTable, NTFS_ATTR_TYPE_DATA, L"");
986  if (!Volume->MFTContext)
987  {
988  FileSystemError("Can't find data attribute for Master File Table.");
989  FrLdrTempFree(Volume->MasterFileTable, TAG_NTFS_MFT);
991  return NULL;
992  }
993 
994  //
995  // Remember NTFS volume information
996  //
997  NtfsVolumes[DeviceId] = Volume;
998 
999  //
1000  // Return success
1001  //
1002  return &NtfsFuncTable;
1003 }
1004 
1005 #endif
ULONG IndexRecordSize
Definition: ntfs.c:47
signed char * PCHAR
Definition: retypes.h:7
UCHAR Flags
Definition: ntfs.h:160
UCHAR FileNameType
Definition: ntfs.h:187
#define TRUE
Definition: types.h:120
NTSYSAPI VOID NTAPI RtlCopyMemory(VOID UNALIGNED *Destination, CONST VOID UNALIGNED *Source, ULONG Length)
#define TAG_NTFS_INDEX_REC
Definition: ntfs.c:34
#define NTFS_ATTR_TYPE_ATTRIBUTE_LIST
Definition: ntfs.h:37
struct NTFS_MFT_RECORD * PNTFS_MFT_RECORD
VOID FsGetFirstNameFromPath(PCHAR Buffer, PCSTR Path)
Definition: fs.c:414
Type
Definition: Type.h:6
ARC_STATUS NtfsOpen(CHAR *Path, OPENMODE OpenMode, ULONG *FileId)
Definition: ntfs.c:781
USHORT NameOffset
Definition: ntfs.h:126
Definition: arc.h:32
ACPI_SIZE strlen(const char *String)
Definition: utclib.c:269
unsigned char Byte
Definition: zconf.h:391
struct NTFS_ATTR_RECORD * PNTFS_ATTR_RECORD
NTFS_INDEX_HEADER IndexHeader
Definition: ntfs.h:171
Definition: arc.h:39
static COORD Position
Definition: mouse.c:34
unsigned char * PUCHAR
Definition: retypes.h:3
char CHAR
Definition: xmlstorage.h:175
ARC_STATUS NtfsGetFileInformation(ULONG FileId, FILEINFORMATION *Information)
Definition: ntfs.c:765
NTFS_ATTR_RECORD Record
Definition: ntfs.h:234
Definition: fs.h:22
struct NTFS_INDEX_ENTRY::@168::@169 Directory
USHORT AttributesOffset
Definition: ntfs.h:112
ULONG FsGetNumPathParts(PCSTR Path)
Definition: fs.c:386
_Inout_ __drv_aliasesMem PSLIST_ENTRY _Inout_ PSLIST_ENTRY _In_ ULONG Count
Definition: exfuncs.h:1015
ULONG ARC_STATUS
Definition: arc.h:4
ULONG Type
Definition: ntfs.h:122
ULONG EntriesOffset
Definition: ntfs.h:157
static PNTFS_ATTR_CONTEXT NtfsFindAttributeHelper(PNTFS_VOLUME_INFO Volume, PNTFS_ATTR_RECORD AttrRecord, PNTFS_ATTR_RECORD AttrRecordEnd, ULONG Type, const WCHAR *Name, ULONG NameLength)
Definition: ntfs.c:374
#define NTFS_ATTR_TYPE_DATA
Definition: ntfs.h:43
uint16_t * PWCHAR
Definition: typedefs.h:54
#define TAG_NTFS_FILE
Definition: ntfs.c:36
#define NTFS_ATTR_TYPE_INDEX_ALLOCATION
Definition: ntfs.h:45
#define NTFS_INDEX_ENTRY_END
Definition: ntfs.h:61
VOID * FsGetDeviceSpecific(ULONG FileId)
Definition: fs.c:474
Definition: arc.h:48
NTFS_FILE_NAME_ATTR FileName
Definition: ntfs.h:222
ARC_STATUS NtfsSeek(ULONG FileId, LARGE_INTEGER *Position, SEEKMODE SeekMode)
Definition: ntfs.c:848
PNTFS_MFT_RECORD MasterFileTable
Definition: ntfs.c:48
const DEVVTBL * NtfsMount(ULONG DeviceId)
Definition: ntfs.c:875
PUCHAR TemporarySector
Definition: ntfs.c:52
ARC_STATUS NtfsRead(ULONG FileId, VOID *Buffer, ULONG N, ULONG *Count)
Definition: ntfs.c:828
WCHAR FileName[0]
Definition: ntfs.h:188
struct _NTFS_VOLUME_INFO NTFS_VOLUME_INFO
HANDLE FileHandle
Definition: stats.c:38
_In_ struct _KBUGCHECK_REASON_CALLBACK_RECORD * Record
Definition: ketypes.h:256
while(1)
Definition: macro.lex.yy.c:740
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
_In_ ULONG _In_ ULONG Offset
Definition: ntddpcm.h:101
ULONG Length
Definition: ntfs.h:123
VOID FileSystemError(PCSTR ErrorString)
Definition: fs.c:260
#define NTFS_FILE_NAME_POSIX
Definition: ntfs.h:63
ARC_STATUS NtfsClose(ULONG FileId)
Definition: ntfs.c:755
#define TAG_NTFS_BITMAP
Definition: ntfs.c:35
FORCEINLINE PVOID FrLdrTempAlloc(_In_ SIZE_T Size, _In_ ULONG Tag)
Definition: mm.h:177
static BOOLEAN NtfsReadMftRecord(PNTFS_VOLUME_INFO Volume, ULONGLONG MFTIndex, PNTFS_MFT_RECORD Buffer)
Definition: ntfs.c:481
unsigned char BOOLEAN
smooth NULL
Definition: ftsmooth.c:416
#define NTFS_ATTR_TYPE_BITMAP
Definition: ntfs.h:46
#define NTFS_LARGE_INDEX
Definition: ntfs.h:58
static PNTFS_ATTR_CONTEXT NtfsPrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord)
Definition: ntfs.c:106
enum _SEEKMODE SEEKMODE
Definition: bufpool.h:45
struct NTFS_ATTR_RECORD::@164::@167 NonResident
ULONG ClusterSize
Definition: ntfs.c:45
int64_t LONGLONG
Definition: typedefs.h:66
int64_t LONG64
Definition: typedefs.h:66
static BOOLEAN NtfsDiskRead(PNTFS_VOLUME_INFO Volume, ULONGLONG Offset, ULONGLONG Length, PCHAR Buffer)
Definition: ntfs.c:145
USHORT Flags
Definition: ntfs.h:220
#define TAG_NTFS_CONTEXT
Definition: ntfs.c:31
#define TRACE(s)
Definition: solgame.cpp:4
#define TAG_NTFS_LIST
Definition: ntfs.c:32
struct NTFS_INDEX_ROOT * PNTFS_INDEX_ROOT
NTSYSAPI ULONG NTAPI RtlEqualMemory(CONST VOID *Source1, CONST VOID *Source2, ULONG Length)
static BOOLEAN NtfsLookupFile(PNTFS_VOLUME_INFO Volume, PCSTR FileName, PNTFS_MFT_RECORD MftRecord, PNTFS_ATTR_CONTEXT *DataContext)
Definition: ntfs.c:711
static PNTFS_ATTR_CONTEXT NtfsFindAttribute(PNTFS_VOLUME_INFO Volume, PNTFS_MFT_RECORD MftRecord, ULONG Type, const WCHAR *Name)
Definition: ntfs.c:443
__wchar_t WCHAR
Definition: xmlstorage.h:180
union NTFS_INDEX_ENTRY::@168 Data
uint64_t ULONGLONG
Definition: typedefs.h:65
#define TAG_NTFS_VOLUME
Definition: ntfs.c:37
#define NTFS_ATTR_TYPE_END
Definition: ntfs.h:50
struct BitmapData BitmapData
VOID FsSetDeviceSpecific(ULONG FileId, VOID *Specific)
Definition: fs.c:467
static BOOLEAN NtfsFixupRecord(PNTFS_VOLUME_INFO Volume, PNTFS_RECORD Record)
Definition: ntfs.c:457
_In_ ULONG _In_ ULONG _In_ ULONG Length
Definition: ntddpcm.h:101
struct NTFS_ATTR_RECORD::@164::@166 Resident
#define MAX_FDS
Definition: fs.h:62
ARC_STATUS ArcRead(ULONG FileId, VOID *Buffer, ULONG N, ULONG *Count)
Definition: fs.c:237
unsigned __int64 ULONG64
Definition: imports.h:198
unsigned char UCHAR
Definition: xmlstorage.h:181
Definition: arc.h:34
static const WCHAR L[]
Definition: oid.c:1250
static VOID NtfsReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context)
Definition: ntfs.c:140
Definition: ntfs.h:203
PNTFS_VOLUME_INFO NtfsVolumes[MAX_FDS]
Definition: ntfs.c:55
UCHAR IsNonResident
Definition: ntfs.h:124
ARC_STATUS ArcSeek(ULONG FileId, LARGE_INTEGER *Position, SEEKMODE SeekMode)
Definition: fs.c:244
static ULONGLONG NtfsGetAttributeSize(PNTFS_ATTR_RECORD AttrRecord)
Definition: ntfs.c:57
NTFS_BOOTSECTOR BootSector
Definition: ntfs.c:44
_Must_inspect_result_ _In_ PFILE_OBJECT _In_opt_ HANDLE _In_ ULONG FileNameLength
Definition: fltkernel.h:1129
Status
Definition: gdiplustypes.h:24
#define NTFS_ATTR_TYPE_INDEX_ROOT
Definition: ntfs.h:44
static PUCHAR NtfsDecodeRun(PUCHAR DataRun, LONGLONG *DataRunOffset, ULONGLONG *DataRunLength)
Definition: ntfs.c:65
static ULONG NtfsReadAttribute(PNTFS_VOLUME_INFO Volume, PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset, PCHAR Buffer, ULONG Length)
Definition: ntfs.c:222
PNTFS_ATTR_CONTEXT MFTContext
Definition: ntfs.c:50
UCHAR NameLength
Definition: ntfs.h:125
struct _FileName FileName
Definition: fatprocs.h:884
PRTL_UNICODE_STRING_BUFFER Path
#define TAG_NTFS_DATA
Definition: ntfs.c:38
unsigned short USHORT
Definition: pedump.c:61
#define FIELD_OFFSET(t, f)
Definition: typedefs.h:254
#define min(a, b)
Definition: monoChain.cc:55
Definition: arc.h:46
Definition: arc.h:40
ULONG DeviceId
Definition: ntfs.c:51
ULONG FsGetDeviceId(ULONG FileId)
Definition: fs.c:481
struct tagContext Context
Definition: acpixf.h:1012
unsigned int ULONG
Definition: retypes.h:1
static BOOLEAN NtfsCompareFileName(PCHAR FileName, PNTFS_INDEX_ENTRY IndexEntry)
Definition: ntfs.c:512
#define RtlZeroMemory(Destination, Length)
Definition: typedefs.h:261
#define TAG_NTFS_MFT
Definition: ntfs.c:33
ULONG MftRecordSize
Definition: ntfs.c:46
const char * PCSTR
Definition: typedefs.h:51
static BOOLEAN NtfsFindMftRecord(PNTFS_VOLUME_INFO Volume, ULONGLONG MFTIndex, PCHAR FileName, ULONGLONG *OutMFTIndex)
Definition: ntfs.c:545
struct NTFS_INDEX_ENTRY * PNTFS_INDEX_ENTRY
UCHAR FileNameLength
Definition: ntfs.h:186
enum _OPENMODE OPENMODE
int tolower(int c)
Definition: utclib.c:902
const DEVVTBL NtfsFuncTable
Definition: ntfs.c:865
USHORT Length
Definition: ntfs.h:218
_Must_inspect_result_ _In_ PFILE_OBJECT _In_opt_ PLARGE_INTEGER _In_ ULONG _In_ FLT_IO_OPERATION_FLAGS _Out_opt_ PULONG BytesRead
Definition: fltkernel.h:1255
Iosb Information
Definition: create.c:4377
DBG_DEFAULT_CHANNEL(FILESYSTEM)
#define NTFS_FILE_ROOT
Definition: ntfs.h:28
ULONG IndexBlockSize
Definition: ntfs.h:168
FORCEINLINE VOID FrLdrTempFree(PVOID Allocation, ULONG Tag)
Definition: mm.h:186