ReactOS  0.4.14-dev-614-gbfd8a84
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 
30 #include <debug.h>
31 DBG_DEFAULT_CHANNEL(FILESYSTEM);
32 
33 #define TAG_NTFS_CONTEXT 'CftN'
34 #define TAG_NTFS_LIST 'LftN'
35 #define TAG_NTFS_MFT 'MftN'
36 #define TAG_NTFS_INDEX_REC 'IftN'
37 #define TAG_NTFS_BITMAP 'BftN'
38 #define TAG_NTFS_FILE 'FftN'
39 #define TAG_NTFS_VOLUME 'VftN'
40 #define TAG_NTFS_DATA 'DftN'
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(%lu) -> FileSize = %llu, FilePointer = 0x%llx\n",
774  FileId, Information->EndingAddress.QuadPart, Information->CurrentAddress.QuadPart);
775 
776  return ESUCCESS;
777 }
778 
780 {
783  PNTFS_MFT_RECORD MftRecord;
784  ULONG DeviceId;
785 
786  //
787  // Check parameters
788  //
789  if (OpenMode != OpenReadOnly)
790  return EACCES;
791 
792  //
793  // Get underlying device
794  //
795  DeviceId = FsGetDeviceId(*FileId);
796  Volume = NtfsVolumes[DeviceId];
797 
798  TRACE("NtfsOpen() FileName = %s\n", Path);
799 
800  //
801  // Allocate file structure
802  //
803  FileHandle = FrLdrTempAlloc(sizeof(NTFS_FILE_HANDLE) + Volume->MftRecordSize,
804  TAG_NTFS_FILE);
805  if (!FileHandle)
806  {
807  return ENOMEM;
808  }
809  RtlZeroMemory(FileHandle, sizeof(NTFS_FILE_HANDLE) + Volume->MftRecordSize);
810  FileHandle->Volume = Volume;
811 
812  //
813  // Search file entry
814  //
815  MftRecord = (PNTFS_MFT_RECORD)(FileHandle + 1);
816  if (!NtfsLookupFile(Volume, Path, MftRecord, &FileHandle->DataContext))
817  {
819  return ENOENT;
820  }
821 
823  return ESUCCESS;
824 }
825 
827 {
829  ULONGLONG BytesRead64;
830 
831  //
832  // Read file
833  //
834  BytesRead64 = NtfsReadAttribute(FileHandle->Volume, FileHandle->DataContext, FileHandle->Offset, Buffer, N);
835  FileHandle->Offset += BytesRead64;
836  *Count = (ULONG)BytesRead64;
837 
838  //
839  // Check for success
840  //
841  if (BytesRead64 > 0)
842  return ESUCCESS;
843  else
844  return EIO;
845 }
846 
848 {
850  LARGE_INTEGER NewPosition = *Position;
851 
852  switch (SeekMode)
853  {
854  case SeekAbsolute:
855  break;
856  case SeekRelative:
857  NewPosition.QuadPart += FileHandle->Offset;
858  break;
859  default:
860  ASSERT(FALSE);
861  return EINVAL;
862  }
863 
864  if (NewPosition.QuadPart >= NtfsGetAttributeSize(&FileHandle->DataContext->Record))
865  return EINVAL;
866 
867  FileHandle->Offset = NewPosition.QuadPart;
868  return ESUCCESS;
869 }
870 
872 {
873  NtfsClose,
875  NtfsOpen,
876  NtfsRead,
877  NtfsSeek,
878  L"ntfs",
879 };
880 
881 const DEVVTBL* NtfsMount(ULONG DeviceId)
882 {
885  ULONG Count;
887 
888  TRACE("Enter NtfsMount(%lu)\n", DeviceId);
889 
890  //
891  // Allocate data for volume information
892  //
894  if (!Volume)
895  return NULL;
897 
898  //
899  // Read the BootSector
900  //
901  Position.QuadPart = 0;
902  Status = ArcSeek(DeviceId, &Position, SeekAbsolute);
903  if (Status != ESUCCESS)
904  {
906  return NULL;
907  }
908  Status = ArcRead(DeviceId, &Volume->BootSector, sizeof(Volume->BootSector), &Count);
909  if (Status != ESUCCESS || Count != sizeof(Volume->BootSector))
910  {
912  return NULL;
913  }
914 
915  //
916  // Check if BootSector is valid. If no, return early
917  //
918  if (!RtlEqualMemory(Volume->BootSector.SystemId, "NTFS", 4))
919  {
921  return NULL;
922  }
923 
924  //
925  // Calculate cluster size and MFT record size
926  //
927  Volume->ClusterSize = Volume->BootSector.SectorsPerCluster * Volume->BootSector.BytesPerSector;
928  if (Volume->BootSector.ClustersPerMftRecord > 0)
929  Volume->MftRecordSize = Volume->BootSector.ClustersPerMftRecord * Volume->ClusterSize;
930  else
931  Volume->MftRecordSize = 1 << (-Volume->BootSector.ClustersPerMftRecord);
932  if (Volume->BootSector.ClustersPerIndexRecord > 0)
933  Volume->IndexRecordSize = Volume->BootSector.ClustersPerIndexRecord * Volume->ClusterSize;
934  else
935  Volume->IndexRecordSize = 1 << (-Volume->BootSector.ClustersPerIndexRecord);
936 
937  TRACE("ClusterSize: 0x%x\n", Volume->ClusterSize);
938  TRACE("ClustersPerMftRecord: %d\n", Volume->BootSector.ClustersPerMftRecord);
939  TRACE("ClustersPerIndexRecord: %d\n", Volume->BootSector.ClustersPerIndexRecord);
940  TRACE("MftRecordSize: 0x%x\n", Volume->MftRecordSize);
941  TRACE("IndexRecordSize: 0x%x\n", Volume->IndexRecordSize);
942 
943  //
944  // Read MFT index
945  //
946  TRACE("Reading MFT index...\n");
947  Volume->MasterFileTable = FrLdrTempAlloc(Volume->MftRecordSize, TAG_NTFS_MFT);
948  if (!Volume->MasterFileTable)
949  {
951  return NULL;
952  }
953  Position.QuadPart = Volume->BootSector.MftLocation * Volume->ClusterSize;
954  Status = ArcSeek(DeviceId, &Position, SeekAbsolute);
955  if (Status != ESUCCESS)
956  {
957  FileSystemError("Failed to seek to Master File Table record.");
958  FrLdrTempFree(Volume->MasterFileTable, TAG_NTFS_MFT);
960  return NULL;
961  }
962  Status = ArcRead(DeviceId, Volume->MasterFileTable, Volume->MftRecordSize, &Count);
963  if (Status != ESUCCESS || Count != Volume->MftRecordSize)
964  {
965  FileSystemError("Failed to read the Master File Table record.");
966  FrLdrTempFree(Volume->MasterFileTable, TAG_NTFS_MFT);
968  return NULL;
969  }
970 
971  //
972  // Keep room to read partial sectors
973  //
974  Volume->TemporarySector = FrLdrTempAlloc(Volume->BootSector.BytesPerSector, TAG_NTFS_DATA);
975  if (!Volume->TemporarySector)
976  {
977  FileSystemError("Failed to allocate memory.");
978  FrLdrTempFree(Volume->MasterFileTable, TAG_NTFS_MFT);
980  return NULL;
981  }
982 
983  //
984  // Keep device id
985  //
986  Volume->DeviceId = DeviceId;
987 
988  //
989  // Search DATA attribute
990  //
991  TRACE("Searching for DATA attribute...\n");
992  Volume->MFTContext = NtfsFindAttribute(Volume, Volume->MasterFileTable, NTFS_ATTR_TYPE_DATA, L"");
993  if (!Volume->MFTContext)
994  {
995  FileSystemError("Can't find data attribute for Master File Table.");
996  FrLdrTempFree(Volume->MasterFileTable, TAG_NTFS_MFT);
998  return NULL;
999  }
1000 
1001  //
1002  // Remember NTFS volume information
1003  //
1004  NtfsVolumes[DeviceId] = Volume;
1005 
1006  //
1007  // Return success
1008  //
1009  TRACE("NtfsMount(%lu) success\n", DeviceId);
1010  return &NtfsFuncTable;
1011 }
1012 
1013 #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:36
#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:356
Type
Definition: Type.h:6
ARC_STATUS NtfsOpen(CHAR *Path, OPENMODE OpenMode, ULONG *FileId)
Definition: ntfs.c:779
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:24
USHORT AttributesOffset
Definition: ntfs.h:112
ULONG FsGetNumPathParts(PCSTR Path)
Definition: fs.c:328
_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:38
#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:416
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:847
PNTFS_MFT_RECORD MasterFileTable
Definition: ntfs.c:48
const DEVVTBL * NtfsMount(ULONG DeviceId)
Definition: ntfs.c:881
PUCHAR TemporarySector
Definition: ntfs.c:52
ARC_STATUS NtfsRead(ULONG FileId, VOID *Buffer, ULONG N, ULONG *Count)
Definition: ntfs.c:826
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:259
#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:37
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
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:33
#define TRACE(s)
Definition: solgame.cpp:4
#define TAG_NTFS_LIST
Definition: ntfs.c:34
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
uint64_t ULONGLONG
Definition: typedefs.h:65
#define TAG_NTFS_VOLUME
Definition: ntfs.c:39
#define NTFS_ATTR_TYPE_END
Definition: ntfs.h:50
struct BitmapData BitmapData
VOID FsSetDeviceSpecific(ULONG FileId, VOID *Specific)
Definition: fs.c:409
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
#define MAX_FDS
Definition: fs.h:34
ASSERT((InvokeOnSuccess||InvokeOnError||InvokeOnCancel) ?(CompletionRoutine !=NULL) :TRUE)
ARC_STATUS ArcRead(ULONG FileId, VOID *Buffer, ULONG N, ULONG *Count)
Definition: fs.c:236
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:243
static ULONGLONG NtfsGetAttributeSize(PNTFS_ATTR_RECORD AttrRecord)
Definition: ntfs.c:57
NTFS_BOOTSECTOR BootSector
Definition: ntfs.c:44
struct NTFS_INDEX_ENTRY::@169::@170 Directory
_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:40
unsigned short USHORT
Definition: pedump.c:61
struct NTFS_ATTR_RECORD::@165::@168 NonResident
#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:423
struct tagContext Context
Definition: acpixf.h:1030
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:35
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
struct NTFS_ATTR_RECORD::@165::@167 Resident
enum _OPENMODE OPENMODE
int tolower(int c)
Definition: utclib.c:902
const DEVVTBL NtfsFuncTable
Definition: ntfs.c:871
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)
LONGLONG QuadPart
Definition: typedefs.h:112
#define NTFS_FILE_ROOT
Definition: ntfs.h:28
ULONG IndexBlockSize
Definition: ntfs.h:168
union NTFS_INDEX_ENTRY::@169 Data
FORCEINLINE VOID FrLdrTempFree(PVOID Allocation, ULONG Tag)
Definition: mm.h:186