3 * Copyright (C) 2002, 2014 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19 * COPYRIGHT: See COPYING in the top level directory
20 * PROJECT: ReactOS kernel
21 * FILE: drivers/filesystem/ntfs/mft.c
22 * PURPOSE: NTFS filesystem driver
23 * PROGRAMMERS: Eric Kohl
25 * Pierre Schweitzer (pierre@reactos.org)
26 * Hervé Poussineau (hpoussin@reactos.org)
29 /* INCLUDES *****************************************************************/
36 /* FUNCTIONS ****************************************************************/
39 PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord
)
41 PNTFS_ATTR_CONTEXT Context
;
43 Context
= ExAllocatePoolWithTag(NonPagedPool
,
44 FIELD_OFFSET(NTFS_ATTR_CONTEXT
, Record
) + AttrRecord
->Length
,
46 RtlCopyMemory(&Context
->Record
, AttrRecord
, AttrRecord
->Length
);
47 if (AttrRecord
->IsNonResident
)
49 LONGLONG DataRunOffset
;
50 ULONGLONG DataRunLength
;
52 Context
->CacheRun
= (PUCHAR
)&Context
->Record
+ Context
->Record
.NonResident
.MappingPairsOffset
;
53 Context
->CacheRunOffset
= 0;
54 Context
->CacheRun
= DecodeRun(Context
->CacheRun
, &DataRunOffset
, &DataRunLength
);
55 Context
->CacheRunLength
= DataRunLength
;
56 if (DataRunOffset
!= -1)
59 Context
->CacheRunStartLCN
=
60 Context
->CacheRunLastLCN
= DataRunOffset
;
65 Context
->CacheRunStartLCN
= -1;
66 Context
->CacheRunLastLCN
= 0;
68 Context
->CacheRunCurrentOffset
= 0;
76 ReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context
)
78 ExFreePoolWithTag(Context
, TAG_NTFS
);
83 FindAttributeHelper(PDEVICE_EXTENSION Vcb
,
84 PNTFS_ATTR_RECORD AttrRecord
,
85 PNTFS_ATTR_RECORD AttrRecordEnd
,
90 DPRINT("FindAttributeHelper(%p, %p, %p, 0x%x, %S, %u)\n", Vcb
, AttrRecord
, AttrRecordEnd
, Type
, Name
, NameLength
);
92 while (AttrRecord
< AttrRecordEnd
)
94 DPRINT("AttrRecord->Type = 0x%x\n", AttrRecord
->Type
);
96 if (AttrRecord
->Type
== AttributeEnd
)
99 if (AttrRecord
->Type
== AttributeAttributeList
)
101 PNTFS_ATTR_CONTEXT Context
;
102 PNTFS_ATTR_CONTEXT ListContext
;
105 PNTFS_ATTR_RECORD ListAttrRecord
;
106 PNTFS_ATTR_RECORD ListAttrRecordEnd
;
108 ListContext
= PrepareAttributeContext(AttrRecord
);
110 ListSize
= AttributeDataLength(&ListContext
->Record
);
111 if(ListSize
<= 0xFFFFFFFF)
112 ListBuffer
= ExAllocatePoolWithTag(NonPagedPool
, (ULONG
)ListSize
, TAG_NTFS
);
118 DPRINT("Failed to allocate memory: %x\n", (ULONG
)ListSize
);
122 ListAttrRecord
= (PNTFS_ATTR_RECORD
)ListBuffer
;
123 ListAttrRecordEnd
= (PNTFS_ATTR_RECORD
)((PCHAR
)ListBuffer
+ ListSize
);
125 if (ReadAttribute(Vcb
, ListContext
, 0, ListBuffer
, (ULONG
)ListSize
) == ListSize
)
127 Context
= FindAttributeHelper(Vcb
, ListAttrRecord
, ListAttrRecordEnd
,
128 Type
, Name
, NameLength
);
130 ReleaseAttributeContext(ListContext
);
131 ExFreePoolWithTag(ListBuffer
, TAG_NTFS
);
135 if (AttrRecord
->IsNonResident
) DPRINT("Found context = %p\n", Context
);
141 if (AttrRecord
->Type
== Type
)
143 if (AttrRecord
->NameLength
== NameLength
)
147 AttrName
= (PWCHAR
)((PCHAR
)AttrRecord
+ AttrRecord
->NameOffset
);
148 DPRINT("%.*S, %.*S\n", AttrRecord
->NameLength
, AttrName
, NameLength
, Name
);
149 if (RtlCompareMemory(AttrName
, Name
, NameLength
<< 1) == (NameLength
<< 1))
151 /* Found it, fill up the context and return. */
152 DPRINT("Found context\n");
153 return PrepareAttributeContext(AttrRecord
);
158 if (AttrRecord
->Length
== 0)
160 DPRINT("Null length attribute record\n");
163 AttrRecord
= (PNTFS_ATTR_RECORD
)((PCHAR
)AttrRecord
+ AttrRecord
->Length
);
172 FindAttribute(PDEVICE_EXTENSION Vcb
,
173 PFILE_RECORD_HEADER MftRecord
,
177 PNTFS_ATTR_CONTEXT
* AttrCtx
)
179 PNTFS_ATTR_RECORD AttrRecord
;
180 PNTFS_ATTR_RECORD AttrRecordEnd
;
182 DPRINT("FindAttribute(%p, %p, 0x%x, %S, %u, %p)\n", Vcb
, MftRecord
, Type
, Name
, NameLength
, AttrCtx
);
184 AttrRecord
= (PNTFS_ATTR_RECORD
)((PCHAR
)MftRecord
+ MftRecord
->AttributeOffset
);
185 AttrRecordEnd
= (PNTFS_ATTR_RECORD
)((PCHAR
)MftRecord
+ Vcb
->NtfsInfo
.BytesPerFileRecord
);
187 *AttrCtx
= FindAttributeHelper(Vcb
, AttrRecord
, AttrRecordEnd
, Type
, Name
, NameLength
);
188 if (*AttrCtx
== NULL
)
190 return STATUS_OBJECT_NAME_NOT_FOUND
;
193 return STATUS_SUCCESS
;
198 AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord
)
200 if (AttrRecord
->IsNonResident
)
201 return AttrRecord
->NonResident
.AllocatedSize
;
203 return AttrRecord
->Resident
.ValueLength
;
208 AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord
)
210 if (AttrRecord
->IsNonResident
)
211 return AttrRecord
->NonResident
.DataSize
;
213 return AttrRecord
->Resident
.ValueLength
;
218 ReadAttribute(PDEVICE_EXTENSION Vcb
,
219 PNTFS_ATTR_CONTEXT Context
,
226 LONGLONG DataRunOffset
;
227 ULONGLONG DataRunLength
;
228 LONGLONG DataRunStartLCN
;
229 ULONGLONG CurrentOffset
;
234 if (!Context
->Record
.IsNonResident
)
236 if (Offset
> Context
->Record
.Resident
.ValueLength
)
238 if (Offset
+ Length
> Context
->Record
.Resident
.ValueLength
)
239 Length
= (ULONG
)(Context
->Record
.Resident
.ValueLength
- Offset
);
240 RtlCopyMemory(Buffer
, (PCHAR
)&Context
->Record
+ Context
->Record
.Resident
.ValueOffset
+ Offset
, Length
);
245 * Non-resident attribute
249 * I. Find the corresponding start data run.
254 // FIXME: Cache seems to be non-working. Disable it for now
255 //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
258 DataRun
= Context
->CacheRun
;
259 LastLCN
= Context
->CacheRunLastLCN
;
260 DataRunStartLCN
= Context
->CacheRunStartLCN
;
261 DataRunLength
= Context
->CacheRunLength
;
262 CurrentOffset
= Context
->CacheRunCurrentOffset
;
267 DataRun
= (PUCHAR
)&Context
->Record
+ Context
->Record
.NonResident
.MappingPairsOffset
;
272 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
273 if (DataRunOffset
!= -1)
275 /* Normal data run. */
276 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
277 LastLCN
= DataRunStartLCN
;
281 /* Sparse data run. */
282 DataRunStartLCN
= -1;
285 if (Offset
>= CurrentOffset
&&
286 Offset
< CurrentOffset
+ (DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
))
296 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
301 * II. Go through the run list and read the data
304 ReadLength
= (ULONG
)min(DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
- (Offset
- CurrentOffset
), Length
);
305 if (DataRunStartLCN
== -1)
306 RtlZeroMemory(Buffer
, ReadLength
);
307 Status
= NtfsReadDisk(Vcb
->StorageDevice
,
308 DataRunStartLCN
* Vcb
->NtfsInfo
.BytesPerCluster
+ Offset
- CurrentOffset
,
310 Vcb
->NtfsInfo
.BytesPerSector
,
313 if (NT_SUCCESS(Status
))
315 Length
-= ReadLength
;
316 Buffer
+= ReadLength
;
317 AlreadyRead
+= ReadLength
;
319 if (ReadLength
== DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
- (Offset
- CurrentOffset
))
321 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
322 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
323 if (DataRunLength
!= (ULONGLONG
)-1)
325 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
326 LastLCN
= DataRunStartLCN
;
329 DataRunStartLCN
= -1;
337 ReadLength
= (ULONG
)min(DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
, Length
);
338 if (DataRunStartLCN
== -1)
339 RtlZeroMemory(Buffer
, ReadLength
);
342 Status
= NtfsReadDisk(Vcb
->StorageDevice
,
343 DataRunStartLCN
* Vcb
->NtfsInfo
.BytesPerCluster
,
345 Vcb
->NtfsInfo
.BytesPerSector
,
348 if (!NT_SUCCESS(Status
))
352 Length
-= ReadLength
;
353 Buffer
+= ReadLength
;
354 AlreadyRead
+= ReadLength
;
356 /* We finished this request, but there still data in this data run. */
357 if (Length
== 0 && ReadLength
!= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
)
361 * Go to next run in the list.
366 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
367 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
368 if (DataRunOffset
!= -1)
370 /* Normal data run. */
371 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
372 LastLCN
= DataRunStartLCN
;
376 /* Sparse data run. */
377 DataRunStartLCN
= -1;
383 Context
->CacheRun
= DataRun
;
384 Context
->CacheRunOffset
= Offset
+ AlreadyRead
;
385 Context
->CacheRunStartLCN
= DataRunStartLCN
;
386 Context
->CacheRunLength
= DataRunLength
;
387 Context
->CacheRunLastLCN
= LastLCN
;
388 Context
->CacheRunCurrentOffset
= CurrentOffset
;
395 ReadFileRecord(PDEVICE_EXTENSION Vcb
,
397 PFILE_RECORD_HEADER file
)
401 DPRINT("ReadFileRecord(%p, %I64x, %p)\n", Vcb
, index
, file
);
403 BytesRead
= ReadAttribute(Vcb
, Vcb
->MFTContext
, index
* Vcb
->NtfsInfo
.BytesPerFileRecord
, (PCHAR
)file
, Vcb
->NtfsInfo
.BytesPerFileRecord
);
404 if (BytesRead
!= Vcb
->NtfsInfo
.BytesPerFileRecord
)
406 DPRINT1("ReadFileRecord failed: %I64u read, %u expected\n", BytesRead
, Vcb
->NtfsInfo
.BytesPerFileRecord
);
407 return STATUS_PARTIAL_COPY
;
410 /* Apply update sequence array fixups. */
411 return FixupUpdateSequenceArray(Vcb
, &file
->Ntfs
);
416 FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb
,
417 PNTFS_RECORD_HEADER Record
)
424 USA
= (USHORT
*)((PCHAR
)Record
+ Record
->UsaOffset
);
425 USANumber
= *(USA
++);
426 USACount
= Record
->UsaCount
- 1; /* Exclude the USA Number. */
427 Block
= (USHORT
*)((PCHAR
)Record
+ Vcb
->NtfsInfo
.BytesPerSector
- 2);
431 if (*Block
!= USANumber
)
433 DPRINT1("Mismatch with USA: %u read, %u expected\n" , *Block
, USANumber
);
434 return STATUS_UNSUCCESSFUL
;
437 Block
= (USHORT
*)((PCHAR
)Block
+ Vcb
->NtfsInfo
.BytesPerSector
);
441 return STATUS_SUCCESS
;
446 ReadLCN(PDEVICE_EXTENSION Vcb
,
451 LARGE_INTEGER DiskSector
;
453 DiskSector
.QuadPart
= lcn
;
455 return NtfsReadSectors(Vcb
->StorageDevice
,
456 DiskSector
.u
.LowPart
* Vcb
->NtfsInfo
.SectorsPerCluster
,
457 count
* Vcb
->NtfsInfo
.SectorsPerCluster
,
458 Vcb
->NtfsInfo
.BytesPerSector
,
465 CompareFileName(PUNICODE_STRING FileName
,
466 PINDEX_ENTRY_ATTRIBUTE IndexEntry
,
469 BOOLEAN Ret
, Alloc
= FALSE
;
470 UNICODE_STRING EntryName
;
472 EntryName
.Buffer
= IndexEntry
->FileName
.Name
;
474 EntryName
.MaximumLength
= IndexEntry
->FileName
.NameLength
* sizeof(WCHAR
);
478 UNICODE_STRING IntFileName
;
479 if (IndexEntry
->FileName
.NameType
!= NTFS_FILE_NAME_POSIX
)
481 NT_VERIFY(NT_SUCCESS(RtlUpcaseUnicodeString(&IntFileName
, FileName
, TRUE
)));
486 IntFileName
= *FileName
;
489 Ret
= FsRtlIsNameInExpression(&IntFileName
, &EntryName
, (IndexEntry
->FileName
.NameType
!= NTFS_FILE_NAME_POSIX
), NULL
);
493 RtlFreeUnicodeString(&IntFileName
);
500 return (RtlCompareUnicodeString(FileName
, &EntryName
, (IndexEntry
->FileName
.NameType
!= NTFS_FILE_NAME_POSIX
)) == 0);
506 NtfsFindMftRecord(PDEVICE_EXTENSION Vcb
,
508 PUNICODE_STRING FileName
,
511 ULONGLONG
*OutMFTIndex
)
513 PFILE_RECORD_HEADER MftRecord
;
514 PNTFS_ATTR_CONTEXT IndexRootCtx
;
515 PNTFS_ATTR_CONTEXT IndexBitmapCtx
;
516 PNTFS_ATTR_CONTEXT IndexAllocationCtx
;
517 PINDEX_ROOT_ATTRIBUTE IndexRoot
;
518 PINDEX_BUFFER IndexBuffer
;
519 ULONGLONG BitmapDataSize
;
520 ULONGLONG IndexAllocationSize
;
523 PINDEX_ENTRY_ATTRIBUTE IndexEntry
, IndexEntryEnd
;
525 ULONG IndexBlockSize
;
527 ULONG CurrentEntry
= 0;
529 DPRINT("NtfsFindMftRecord(%p, %I64d, %wZ, %p, %u, %p)\n", Vcb
, MFTIndex
, FileName
, FirstEntry
, DirSearch
, OutMFTIndex
);
531 MftRecord
= ExAllocatePoolWithTag(NonPagedPool
,
532 Vcb
->NtfsInfo
.BytesPerFileRecord
,
534 if (MftRecord
== NULL
)
536 return STATUS_INSUFFICIENT_RESOURCES
;
539 if (NT_SUCCESS(ReadFileRecord(Vcb
, MFTIndex
, MftRecord
)))
541 ASSERT(MftRecord
->Ntfs
.Type
== NRH_FILE_TYPE
);
543 Status
= FindAttribute(Vcb
, MftRecord
, AttributeIndexRoot
, L
"$I30", 4, &IndexRootCtx
);
544 if (!NT_SUCCESS(Status
))
546 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
550 IndexRecord
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerIndexRecord
, TAG_NTFS
);
551 if (IndexRecord
== NULL
)
553 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
554 return STATUS_INSUFFICIENT_RESOURCES
;
557 ReadAttribute(Vcb
, IndexRootCtx
, 0, IndexRecord
, Vcb
->NtfsInfo
.BytesPerIndexRecord
);
558 IndexRoot
= (PINDEX_ROOT_ATTRIBUTE
)IndexRecord
;
559 IndexEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((PCHAR
)&IndexRoot
->Header
+ IndexRoot
->Header
.FirstEntryOffset
);
560 /* Index root is always resident. */
561 IndexEntryEnd
= (PINDEX_ENTRY_ATTRIBUTE
)(IndexRecord
+ IndexRoot
->Header
.TotalSizeOfEntries
);
562 ReleaseAttributeContext(IndexRootCtx
);
564 DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb
->NtfsInfo
.BytesPerIndexRecord
, IndexRoot
->SizeOfEntry
);
566 while (IndexEntry
< IndexEntryEnd
&&
567 !(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_END
))
569 UNICODE_STRING EntryName
;
570 EntryName
.Buffer
= IndexEntry
->FileName
.Name
;
572 EntryName
.MaximumLength
= IndexEntry
->FileName
.NameLength
* sizeof(WCHAR
);
574 if (IndexEntry
->Flags
& NTFS_INDEX_ENTRY_NODE
)
575 DPRINT1("Warning: sub-node browsing unimplemented! (%wZ)\n", &EntryName
);
577 if ((IndexEntry
->Data
.Directory
.IndexedFile
& NTFS_MFT_MASK
) > 0x10 &&
578 CurrentEntry
>= *FirstEntry
&&
579 CompareFileName(FileName
, IndexEntry
, DirSearch
))
581 *OutMFTIndex
= (IndexEntry
->Data
.Directory
.IndexedFile
& NTFS_MFT_MASK
);
582 *FirstEntry
= CurrentEntry
;
583 ExFreePoolWithTag(IndexRecord
, TAG_NTFS
);
584 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
585 return STATUS_SUCCESS
;
589 IndexEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((PCHAR
)IndexEntry
+ IndexEntry
->Length
);
592 if (IndexRoot
->Header
.Flags
& INDEX_ROOT_LARGE
)
594 DPRINT("Large Index!\n");
596 IndexBlockSize
= IndexRoot
->SizeOfEntry
;
598 Status
= FindAttribute(Vcb
, MftRecord
, AttributeBitmap
, L
"$I30", 4, &IndexBitmapCtx
);
599 if (!NT_SUCCESS(Status
))
601 DPRINT1("Corrupted filesystem!\n");
602 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
605 BitmapDataSize
= AttributeDataLength(&IndexBitmapCtx
->Record
);
606 DPRINT("BitmapDataSize: %x\n", (ULONG
)BitmapDataSize
);
607 if(BitmapDataSize
<= 0xFFFFFFFF)
608 BitmapData
= ExAllocatePoolWithTag(NonPagedPool
, (ULONG
)BitmapDataSize
, TAG_NTFS
);
612 if (BitmapData
== NULL
)
614 ExFreePoolWithTag(IndexRecord
, TAG_NTFS
);
615 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
616 return STATUS_INSUFFICIENT_RESOURCES
;
618 ReadAttribute(Vcb
, IndexBitmapCtx
, 0, BitmapData
, (ULONG
)BitmapDataSize
);
619 ReleaseAttributeContext(IndexBitmapCtx
);
621 Status
= FindAttribute(Vcb
, MftRecord
, AttributeIndexAllocation
, L
"$I30", 4, &IndexAllocationCtx
);
622 if (!NT_SUCCESS(Status
))
624 DPRINT("Corrupted filesystem!\n");
625 ExFreePoolWithTag(BitmapData
, TAG_NTFS
);
626 ExFreePoolWithTag(IndexRecord
, TAG_NTFS
);
627 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
630 IndexAllocationSize
= AttributeDataLength(&IndexAllocationCtx
->Record
);
636 DPRINT("RecordOffset: %x IndexAllocationSize: %x\n", RecordOffset
, IndexAllocationSize
);
637 for (; RecordOffset
< IndexAllocationSize
;)
639 UCHAR Bit
= 1 << ((RecordOffset
/ IndexBlockSize
) & 7);
640 ULONG Byte
= (RecordOffset
/ IndexBlockSize
) >> 3;
641 if ((BitmapData
[Byte
] & Bit
))
643 RecordOffset
+= IndexBlockSize
;
646 if (RecordOffset
>= IndexAllocationSize
)
651 ReadAttribute(Vcb
, IndexAllocationCtx
, RecordOffset
, IndexRecord
, IndexBlockSize
);
653 if (!NT_SUCCESS(FixupUpdateSequenceArray(Vcb
, &((PFILE_RECORD_HEADER
)IndexRecord
)->Ntfs
)))
658 IndexBuffer
= (PINDEX_BUFFER
)IndexRecord
;
659 ASSERT(IndexBuffer
->Ntfs
.Type
== NRH_INDX_TYPE
);
660 ASSERT(IndexBuffer
->Header
.AllocatedSize
+ 0x18 == IndexBlockSize
);
661 IndexEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)&IndexBuffer
->Header
+ IndexBuffer
->Header
.FirstEntryOffset
);
662 IndexEntryEnd
= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)&IndexBuffer
->Header
+ IndexBuffer
->Header
.TotalSizeOfEntries
);
663 ASSERT(IndexEntryEnd
<= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)IndexBuffer
+ IndexBlockSize
));
665 while (IndexEntry
< IndexEntryEnd
&&
666 !(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_END
))
668 UNICODE_STRING EntryName
;
669 EntryName
.Buffer
= IndexEntry
->FileName
.Name
;
671 EntryName
.MaximumLength
= IndexEntry
->FileName
.NameLength
* sizeof(WCHAR
);
673 if (IndexEntry
->Flags
& NTFS_INDEX_ENTRY_NODE
)
674 DPRINT1("Warning: sub-node browsing unimplemented! (%wZ)\n", &EntryName
);
676 if ((IndexEntry
->Data
.Directory
.IndexedFile
& NTFS_MFT_MASK
) > 0x10 &&
677 CurrentEntry
>= *FirstEntry
&&
678 CompareFileName(FileName
, IndexEntry
, DirSearch
))
680 DPRINT("File found\n");
681 *OutMFTIndex
= (IndexEntry
->Data
.Directory
.IndexedFile
& NTFS_MFT_MASK
);
682 *FirstEntry
= CurrentEntry
;
683 ExFreePoolWithTag(BitmapData
, TAG_NTFS
);
684 ExFreePoolWithTag(IndexRecord
, TAG_NTFS
);
685 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
686 ReleaseAttributeContext(IndexAllocationCtx
);
687 return STATUS_SUCCESS
;
691 ASSERT(IndexEntry
->Length
>= sizeof(INDEX_ENTRY_ATTRIBUTE
));
692 IndexEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((PCHAR
)IndexEntry
+ IndexEntry
->Length
);
695 RecordOffset
+= IndexBlockSize
;
698 ReleaseAttributeContext(IndexAllocationCtx
);
699 ExFreePoolWithTag(BitmapData
, TAG_NTFS
);
702 ExFreePoolWithTag(IndexRecord
, TAG_NTFS
);
706 DPRINT("Can't read MFT record\n");
708 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
710 return STATUS_OBJECT_PATH_NOT_FOUND
;
714 NtfsLookupFileAt(PDEVICE_EXTENSION Vcb
,
715 PUNICODE_STRING PathName
,
716 PFILE_RECORD_HEADER
*FileRecord
,
717 PNTFS_ATTR_CONTEXT
*DataContext
,
719 ULONGLONG CurrentMFTIndex
)
721 UNICODE_STRING Current
, Remaining
;
723 ULONG FirstEntry
= 0;
725 DPRINT("NtfsLookupFileAt(%p, %wZ, %p, %p, %I64x)\n", Vcb
, PathName
, FileRecord
, DataContext
, CurrentMFTIndex
);
727 FsRtlDissectName(*PathName
, &Current
, &Remaining
);
729 while (Current
.Length
!= 0)
731 DPRINT("Current: %wZ\n", &Current
);
733 Status
= NtfsFindMftRecord(Vcb
, CurrentMFTIndex
, &Current
, &FirstEntry
, FALSE
, &CurrentMFTIndex
);
734 if (!NT_SUCCESS(Status
))
739 if (Remaining
.Length
== 0)
742 FsRtlDissectName(Current
, &Current
, &Remaining
);
745 *FileRecord
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
746 if (*FileRecord
== NULL
)
748 DPRINT("NtfsLookupFileAt: Can't allocate MFT record\n");
749 return STATUS_INSUFFICIENT_RESOURCES
;
752 Status
= ReadFileRecord(Vcb
, CurrentMFTIndex
, *FileRecord
);
753 if (!NT_SUCCESS(Status
))
755 DPRINT("NtfsLookupFileAt: Can't read MFT record\n");
756 ExFreePoolWithTag(*FileRecord
, TAG_NTFS
);
760 if (!((*FileRecord
)->Flags
& FRH_DIRECTORY
))
762 Status
= FindAttribute(Vcb
, *FileRecord
, AttributeData
, L
"", 0, DataContext
);
763 if (!NT_SUCCESS(Status
))
765 DPRINT("NtfsLookupFileAt: Can't find data attribute\n");
766 ExFreePoolWithTag(*FileRecord
, TAG_NTFS
);
775 *MFTIndex
= CurrentMFTIndex
;
777 return STATUS_SUCCESS
;
781 NtfsLookupFile(PDEVICE_EXTENSION Vcb
,
782 PUNICODE_STRING PathName
,
783 PFILE_RECORD_HEADER
*FileRecord
,
784 PNTFS_ATTR_CONTEXT
*DataContext
,
787 return NtfsLookupFileAt(Vcb
, PathName
, FileRecord
, DataContext
, MFTIndex
, NTFS_FILE_ROOT
);
791 NtfsFindFileAt(PDEVICE_EXTENSION Vcb
,
792 PUNICODE_STRING SearchPattern
,
794 PFILE_RECORD_HEADER
*FileRecord
,
795 PNTFS_ATTR_CONTEXT
*DataContext
,
797 ULONGLONG CurrentMFTIndex
)
801 DPRINT("NtfsFindFileAt(%p, %wZ, %p, %p, %p, %p, %I64x)\n", Vcb
, SearchPattern
, FirstEntry
, FileRecord
, DataContext
, MFTIndex
, CurrentMFTIndex
);
803 Status
= NtfsFindMftRecord(Vcb
, CurrentMFTIndex
, SearchPattern
, FirstEntry
, TRUE
, &CurrentMFTIndex
);
804 if (!NT_SUCCESS(Status
))
806 DPRINT("NtfsFindFileAt: NtfsFindMftRecord() failed with status 0x%08lx\n", Status
);
810 *FileRecord
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
811 if (*FileRecord
== NULL
)
813 DPRINT("NtfsFindFileAt: Can't allocate MFT record\n");
814 return STATUS_INSUFFICIENT_RESOURCES
;
817 Status
= ReadFileRecord(Vcb
, CurrentMFTIndex
, *FileRecord
);
818 if (!NT_SUCCESS(Status
))
820 DPRINT("NtfsFindFileAt: Can't read MFT record\n");
821 ExFreePoolWithTag(*FileRecord
, TAG_NTFS
);
825 if (!((*FileRecord
)->Flags
& FRH_DIRECTORY
))
827 Status
= FindAttribute(Vcb
, *FileRecord
, AttributeData
, L
"", 0, DataContext
);
828 if (!NT_SUCCESS(Status
))
830 DPRINT("NtfsFindFileAt: Can't find data attribute\n");
831 ExFreePoolWithTag(*FileRecord
, TAG_NTFS
);
840 *MFTIndex
= CurrentMFTIndex
;
842 return STATUS_SUCCESS
;