3 * Copyright (C) 2002 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 * PROGRAMMER: Eric Kohl
24 * Updated by Valentin Verkhovsky 2003/09/12
27 /* INCLUDES *****************************************************************/
34 /* FUNCTIONS ****************************************************************/
37 PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord
)
39 PNTFS_ATTR_CONTEXT Context
;
41 Context
= ExAllocatePoolWithTag(NonPagedPool
,
42 FIELD_OFFSET(NTFS_ATTR_CONTEXT
, Record
) + AttrRecord
->Length
,
44 RtlCopyMemory(&Context
->Record
, AttrRecord
, AttrRecord
->Length
);
45 if (AttrRecord
->IsNonResident
)
47 LONGLONG DataRunOffset
;
48 ULONGLONG DataRunLength
;
50 Context
->CacheRun
= (PUCHAR
)&Context
->Record
+ Context
->Record
.NonResident
.MappingPairsOffset
;
51 Context
->CacheRunOffset
= 0;
52 Context
->CacheRun
= DecodeRun(Context
->CacheRun
, &DataRunOffset
, &DataRunLength
);
53 Context
->CacheRunLength
= DataRunLength
;
54 if (DataRunOffset
!= -1)
57 Context
->CacheRunStartLCN
=
58 Context
->CacheRunLastLCN
= DataRunOffset
;
63 Context
->CacheRunStartLCN
= -1;
64 Context
->CacheRunLastLCN
= 0;
66 Context
->CacheRunCurrentOffset
= 0;
74 ReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context
)
76 ExFreePoolWithTag(Context
, TAG_NTFS
);
81 FindAttributeHelper(PDEVICE_EXTENSION Vcb
,
82 PNTFS_ATTR_RECORD AttrRecord
,
83 PNTFS_ATTR_RECORD AttrRecordEnd
,
88 DPRINT1("FindAttributeHelper(%p, %p, %p, 0x%x, %S, %u)\n", Vcb
, AttrRecord
, AttrRecordEnd
, Type
, Name
, NameLength
);
90 while (AttrRecord
< AttrRecordEnd
)
92 DPRINT("AttrRecord->Type = 0x%x\n", AttrRecord
->Type
);
94 if (AttrRecord
->Type
== AttributeEnd
)
97 if (AttrRecord
->Type
== AttributeAttributeList
)
99 PNTFS_ATTR_CONTEXT Context
;
100 PNTFS_ATTR_CONTEXT ListContext
;
103 PNTFS_ATTR_RECORD ListAttrRecord
;
104 PNTFS_ATTR_RECORD ListAttrRecordEnd
;
106 // Do not handle non-resident yet
107 ASSERT(!(AttrRecord
->IsNonResident
& 1));
109 ListContext
= PrepareAttributeContext(AttrRecord
);
111 ListSize
= AttributeDataLength(&ListContext
->Record
);
112 if(ListSize
<= 0xFFFFFFFF)
113 ListBuffer
= ExAllocatePoolWithTag(NonPagedPool
, (ULONG
)ListSize
, TAG_NTFS
);
119 DPRINT("Failed to allocate memory: %x\n", (ULONG
)ListSize
);
123 ListAttrRecord
= (PNTFS_ATTR_RECORD
)ListBuffer
;
124 ListAttrRecordEnd
= (PNTFS_ATTR_RECORD
)((PCHAR
)ListBuffer
+ ListSize
);
126 if (ReadAttribute(Vcb
, ListContext
, 0, ListBuffer
, (ULONG
)ListSize
) == ListSize
)
128 Context
= FindAttributeHelper(Vcb
, ListAttrRecord
, ListAttrRecordEnd
,
129 Type
, Name
, NameLength
);
131 ReleaseAttributeContext(ListContext
);
132 ExFreePoolWithTag(ListBuffer
, TAG_NTFS
);
136 DPRINT("Found context = %p\n", Context
);
142 if (AttrRecord
->Type
== Type
)
144 if (AttrRecord
->NameLength
== NameLength
)
148 AttrName
= (PWCHAR
)((PCHAR
)AttrRecord
+ AttrRecord
->NameOffset
);
149 DPRINT("%s, %s\n", AttrName
, Name
);
150 if (RtlCompareMemory(AttrName
, Name
, NameLength
<< 1) == (NameLength
<< 1))
152 /* Found it, fill up the context and return. */
153 DPRINT("Found context\n");
154 return PrepareAttributeContext(AttrRecord
);
159 if (AttrRecord
->Length
== 0)
161 DPRINT("Null length attribute record\n");
164 AttrRecord
= (PNTFS_ATTR_RECORD
)((PCHAR
)AttrRecord
+ AttrRecord
->Length
);
173 FindAttribute(PDEVICE_EXTENSION Vcb
,
174 PFILE_RECORD_HEADER MftRecord
,
178 PNTFS_ATTR_CONTEXT
* AttrCtx
)
180 PNTFS_ATTR_RECORD AttrRecord
;
181 PNTFS_ATTR_RECORD AttrRecordEnd
;
183 DPRINT1("NtfsFindAttribute(%p, %p, %u, %S, %u, %p)\n", Vcb
, MftRecord
, Type
, Name
, NameLength
, AttrCtx
);
185 AttrRecord
= (PNTFS_ATTR_RECORD
)((PCHAR
)MftRecord
+ MftRecord
->AttributeOffset
);
186 AttrRecordEnd
= (PNTFS_ATTR_RECORD
)((PCHAR
)MftRecord
+ Vcb
->NtfsInfo
.BytesPerFileRecord
);
188 *AttrCtx
= FindAttributeHelper(Vcb
, AttrRecord
, AttrRecordEnd
, Type
, Name
, NameLength
);
189 if (*AttrCtx
== NULL
)
191 return STATUS_OBJECT_NAME_NOT_FOUND
;
194 return STATUS_SUCCESS
;
199 AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord
)
201 if (AttrRecord
->IsNonResident
)
202 return AttrRecord
->NonResident
.AllocatedSize
;
204 return AttrRecord
->Resident
.ValueLength
;
209 AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord
)
211 if (AttrRecord
->IsNonResident
)
212 return AttrRecord
->NonResident
.DataSize
;
214 return AttrRecord
->Resident
.ValueLength
;
219 ReadAttribute(PDEVICE_EXTENSION Vcb
,
220 PNTFS_ATTR_CONTEXT Context
,
227 LONGLONG DataRunOffset
;
228 ULONGLONG DataRunLength
;
229 LONGLONG DataRunStartLCN
;
230 ULONGLONG CurrentOffset
;
235 if (!Context
->Record
.IsNonResident
)
237 if (Offset
> Context
->Record
.Resident
.ValueLength
)
239 if (Offset
+ Length
> Context
->Record
.Resident
.ValueLength
)
240 Length
= (ULONG
)(Context
->Record
.Resident
.ValueLength
- Offset
);
241 RtlCopyMemory(Buffer
, (PCHAR
)&Context
->Record
+ Context
->Record
.Resident
.ValueOffset
+ Offset
, Length
);
246 * Non-resident attribute
250 * I. Find the corresponding start data run.
255 // FIXME: Cache seems to be non-working. Disable it for now
256 //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
259 DataRun
= Context
->CacheRun
;
260 LastLCN
= Context
->CacheRunLastLCN
;
261 DataRunStartLCN
= Context
->CacheRunStartLCN
;
262 DataRunLength
= Context
->CacheRunLength
;
263 CurrentOffset
= Context
->CacheRunCurrentOffset
;
268 DataRun
= (PUCHAR
)&Context
->Record
+ Context
->Record
.NonResident
.MappingPairsOffset
;
273 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
274 if (DataRunOffset
!= -1)
276 /* Normal data run. */
277 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
278 LastLCN
= DataRunStartLCN
;
282 /* Sparse data run. */
283 DataRunStartLCN
= -1;
286 if (Offset
>= CurrentOffset
&&
287 Offset
< CurrentOffset
+ (DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
))
297 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
302 * II. Go through the run list and read the data
305 ReadLength
= (ULONG
)min(DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
- (Offset
- CurrentOffset
), Length
);
306 if (DataRunStartLCN
== -1)
307 RtlZeroMemory(Buffer
, ReadLength
);
308 Status
= NtfsReadDisk(Vcb
->StorageDevice
,
309 DataRunStartLCN
* Vcb
->NtfsInfo
.BytesPerCluster
+ Offset
- CurrentOffset
,
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
,
347 if (!NT_SUCCESS(Status
))
351 Length
-= ReadLength
;
352 Buffer
+= ReadLength
;
353 AlreadyRead
+= ReadLength
;
355 /* We finished this request, but there still data in this data run. */
356 if (Length
== 0 && ReadLength
!= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
)
360 * Go to next run in the list.
365 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
366 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
367 if (DataRunOffset
!= -1)
369 /* Normal data run. */
370 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
371 LastLCN
= DataRunStartLCN
;
375 /* Sparse data run. */
376 DataRunStartLCN
= -1;
382 Context
->CacheRun
= DataRun
;
383 Context
->CacheRunOffset
= Offset
+ AlreadyRead
;
384 Context
->CacheRunStartLCN
= DataRunStartLCN
;
385 Context
->CacheRunLength
= DataRunLength
;
386 Context
->CacheRunLastLCN
= LastLCN
;
387 Context
->CacheRunCurrentOffset
= CurrentOffset
;
394 ReadFileRecord(PDEVICE_EXTENSION Vcb
,
396 PFILE_RECORD_HEADER file
)
400 DPRINT1("ReadFileRecord(%p, %I64x, %p)\n", Vcb
, index
, file
);
402 BytesRead
= ReadAttribute(Vcb
, Vcb
->MFTContext
, index
* Vcb
->NtfsInfo
.BytesPerFileRecord
, (PCHAR
)file
, Vcb
->NtfsInfo
.BytesPerFileRecord
);
403 if (BytesRead
!= Vcb
->NtfsInfo
.BytesPerFileRecord
)
405 DPRINT1("ReadFileRecord failed: %u read, %u expected\n", BytesRead
, Vcb
->NtfsInfo
.BytesPerFileRecord
);
406 return STATUS_PARTIAL_COPY
;
409 /* Apply update sequence array fixups. */
410 return FixupUpdateSequenceArray(Vcb
, &file
->Ntfs
);
415 FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb
,
416 PNTFS_RECORD_HEADER Record
)
423 USA
= (USHORT
*)((PCHAR
)Record
+ Record
->UsaOffset
);
424 USANumber
= *(USA
++);
425 USACount
= Record
->UsaCount
- 1; /* Exclude the USA Number. */
426 Block
= (USHORT
*)((PCHAR
)Record
+ Vcb
->NtfsInfo
.BytesPerSector
- 2);
430 if (*Block
!= USANumber
)
432 DPRINT1("Mismatch with USA: %u read, %u expected\n" , *Block
, USANumber
);
433 return STATUS_UNSUCCESSFUL
;
436 Block
= (USHORT
*)((PCHAR
)Block
+ Vcb
->NtfsInfo
.BytesPerSector
);
440 return STATUS_SUCCESS
;
445 ReadLCN(PDEVICE_EXTENSION Vcb
,
450 LARGE_INTEGER DiskSector
;
452 DiskSector
.QuadPart
= lcn
;
454 return NtfsReadSectors(Vcb
->StorageDevice
,
455 DiskSector
.u
.LowPart
* Vcb
->NtfsInfo
.SectorsPerCluster
,
456 count
* Vcb
->NtfsInfo
.SectorsPerCluster
,
457 Vcb
->NtfsInfo
.BytesPerSector
,
464 CompareFileName(PUNICODE_STRING FileName
,
465 PINDEX_ENTRY_ATTRIBUTE IndexEntry
,
468 UNICODE_STRING EntryName
;
470 EntryName
.Buffer
= IndexEntry
->FileName
.Name
;
472 EntryName
.MaximumLength
= IndexEntry
->FileName
.NameLength
;
476 return FsRtlIsNameInExpression(FileName
, &EntryName
, (IndexEntry
->FileName
.NameType
!= NTFS_FILE_NAME_POSIX
), NULL
);
480 return (RtlCompareUnicodeString(FileName
, &EntryName
, (IndexEntry
->FileName
.NameType
!= NTFS_FILE_NAME_POSIX
)) == TRUE
);
486 NtfsFindMftRecord(PDEVICE_EXTENSION Vcb
,
488 PUNICODE_STRING FileName
,
491 ULONGLONG
*OutMFTIndex
,
494 PFILE_RECORD_HEADER MftRecord
;
496 PNTFS_ATTR_CONTEXT IndexRootCtx
;
497 PNTFS_ATTR_CONTEXT IndexBitmapCtx
;
498 PNTFS_ATTR_CONTEXT IndexAllocationCtx
;
499 PINDEX_ROOT_ATTRIBUTE IndexRoot
;
500 PINDEX_BUFFER IndexBuffer
;
501 ULONGLONG BitmapDataSize
;
502 ULONGLONG IndexAllocationSize
;
505 PINDEX_ENTRY_ATTRIBUTE IndexEntry
, IndexEntryEnd
;
507 ULONG IndexBlockSize
;
509 ULONG CurrentEntry
= 0;
511 DPRINT1("NtfsFindMftRecord(%p, %I64d, %wZ, %p, %u, %p, %p)\n", Vcb
, MFTIndex
, FileName
, FirstEntry
, DirSearch
, OutMFTIndex
, OutName
);
513 MftRecord
= ExAllocatePoolWithTag(NonPagedPool
,
514 Vcb
->NtfsInfo
.BytesPerFileRecord
,
516 if (MftRecord
== NULL
)
518 return STATUS_INSUFFICIENT_RESOURCES
;
521 if (NT_SUCCESS(ReadFileRecord(Vcb
, MFTIndex
, MftRecord
)))
523 //Magic = MftRecord->Magic;
525 Status
= FindAttribute(Vcb
, MftRecord
, AttributeIndexRoot
, L
"$I30", 4, &IndexRootCtx
);
526 if (!NT_SUCCESS(Status
))
528 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
532 IndexRecord
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerIndexRecord
, TAG_NTFS
);
533 if (IndexRecord
== NULL
)
535 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
536 return STATUS_INSUFFICIENT_RESOURCES
;
539 ReadAttribute(Vcb
, IndexRootCtx
, 0, IndexRecord
, Vcb
->NtfsInfo
.BytesPerIndexRecord
);
540 IndexRoot
= (PINDEX_ROOT_ATTRIBUTE
)IndexRecord
;
541 IndexEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((PCHAR
)&IndexRoot
->Header
+ IndexRoot
->Header
.FirstEntryOffset
);
542 /* Index root is always resident. */
543 IndexEntryEnd
= (PINDEX_ENTRY_ATTRIBUTE
)(IndexRecord
+ IndexRootCtx
->Record
.Resident
.ValueLength
);
544 ReleaseAttributeContext(IndexRootCtx
);
546 DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb
->NtfsInfo
.BytesPerIndexRecord
, IndexRoot
->SizeOfEntry
);
548 while (IndexEntry
< IndexEntryEnd
&&
549 !(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_END
))
551 if (CurrentEntry
>= *FirstEntry
&& CompareFileName(FileName
, IndexEntry
, DirSearch
))
553 *OutMFTIndex
= IndexEntry
->Data
.Directory
.IndexedFile
;
554 *FirstEntry
= CurrentEntry
;
555 RtlCopyMemory(OutName
, IndexEntry
->FileName
.Name
, IndexEntry
->FileName
.NameLength
);
556 OutName
[IndexEntry
->FileName
.NameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
557 ExFreePoolWithTag(IndexRecord
, TAG_NTFS
);
558 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
559 return STATUS_SUCCESS
;
563 IndexEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((PCHAR
)IndexEntry
+ IndexEntry
->Length
);
566 if (IndexRoot
->Header
.Flags
& INDEX_ROOT_LARGE
)
568 DPRINT("Large Index!\n");
570 IndexBlockSize
= IndexRoot
->SizeOfEntry
;
572 Status
= FindAttribute(Vcb
, MftRecord
, AttributeBitmap
, L
"$I30", 4, &IndexBitmapCtx
);
573 if (!NT_SUCCESS(Status
))
575 DPRINT1("Corrupted filesystem!\n");
576 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
579 BitmapDataSize
= AttributeDataLength(&IndexBitmapCtx
->Record
);
580 DPRINT("BitmapDataSize: %x\n", (ULONG
)BitmapDataSize
);
581 if(BitmapDataSize
<= 0xFFFFFFFF)
582 BitmapData
= ExAllocatePoolWithTag(NonPagedPool
, (ULONG
)BitmapDataSize
, TAG_NTFS
);
586 if (BitmapData
== NULL
)
588 ExFreePoolWithTag(IndexRecord
, TAG_NTFS
);
589 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
590 return STATUS_INSUFFICIENT_RESOURCES
;
592 ReadAttribute(Vcb
, IndexBitmapCtx
, 0, BitmapData
, (ULONG
)BitmapDataSize
);
593 ReleaseAttributeContext(IndexBitmapCtx
);
595 Status
= FindAttribute(Vcb
, MftRecord
, AttributeIndexAllocation
, L
"$I30", 4, &IndexAllocationCtx
);
596 if (!NT_SUCCESS(Status
))
598 DPRINT("Corrupted filesystem!\n");
599 ExFreePoolWithTag(BitmapData
, TAG_NTFS
);
600 ExFreePoolWithTag(IndexRecord
, TAG_NTFS
);
601 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
604 IndexAllocationSize
= AttributeDataLength(&IndexAllocationCtx
->Record
);
610 DPRINT("RecordOffset: %x IndexAllocationSize: %x\n", RecordOffset
, IndexAllocationSize
);
611 for (; RecordOffset
< IndexAllocationSize
;)
613 UCHAR Bit
= 1 << ((RecordOffset
/ IndexBlockSize
) & 7);
614 ULONG Byte
= (RecordOffset
/ IndexBlockSize
) >> 3;
615 if ((BitmapData
[Byte
] & Bit
))
617 RecordOffset
+= IndexBlockSize
;
620 if (RecordOffset
>= IndexAllocationSize
)
625 ReadAttribute(Vcb
, IndexAllocationCtx
, RecordOffset
, IndexRecord
, IndexBlockSize
);
627 if (!NT_SUCCESS(FixupUpdateSequenceArray(Vcb
, &((PFILE_RECORD_HEADER
)IndexRecord
)->Ntfs
)))
632 IndexBuffer
= (PINDEX_BUFFER
)IndexRecord
;
633 ASSERT(IndexBuffer
->Ntfs
.Type
== 'XDNI');
634 ASSERT(IndexBuffer
->Header
.AllocatedSize
+ 0x18 == IndexBlockSize
);
635 IndexEntry
= (PINDEX_ENTRY_ATTRIBUTE
)(&IndexBuffer
->Header
+ IndexBuffer
->Header
.FirstEntryOffset
);
636 IndexEntryEnd
= (PINDEX_ENTRY_ATTRIBUTE
)(&IndexBuffer
->Header
+ IndexBuffer
->Header
.TotalSizeOfEntries
);
637 //ASSERT(IndexEntryEnd <= (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexBuffer + IndexBlockSize)); FIXME: Why doesn't it work?
639 while (IndexEntry
< IndexEntryEnd
&&
640 !(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_END
))
642 if (CurrentEntry
>= *FirstEntry
&& CompareFileName(FileName
, IndexEntry
, DirSearch
))
644 DPRINT("File found\n");
645 *OutMFTIndex
= IndexEntry
->Data
.Directory
.IndexedFile
;
646 *FirstEntry
= CurrentEntry
;
647 RtlCopyMemory(OutName
, IndexEntry
->FileName
.Name
, IndexEntry
->FileName
.NameLength
);
648 OutName
[IndexEntry
->FileName
.NameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
649 ExFreePoolWithTag(BitmapData
, TAG_NTFS
);
650 ExFreePoolWithTag(IndexRecord
, TAG_NTFS
);
651 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
652 ReleaseAttributeContext(IndexAllocationCtx
);
653 return STATUS_SUCCESS
;
657 IndexEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((PCHAR
)IndexEntry
+ IndexEntry
->Length
);
660 RecordOffset
+= IndexBlockSize
;
663 ReleaseAttributeContext(IndexAllocationCtx
);
664 ExFreePoolWithTag(BitmapData
, TAG_NTFS
);
667 ExFreePoolWithTag(IndexRecord
, TAG_NTFS
);
671 DPRINT("Can't read MFT record\n");
673 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
675 return STATUS_OBJECT_PATH_NOT_FOUND
;
679 NtfsLookupFileAt(PDEVICE_EXTENSION Vcb
,
680 PUNICODE_STRING PathName
,
681 PFILE_RECORD_HEADER
*FileRecord
,
682 PNTFS_ATTR_CONTEXT
*DataContext
,
684 ULONGLONG CurrentMFTIndex
)
686 UNICODE_STRING Current
, Remaining
;
688 WCHAR FoundName
[MAX_PATH
+ 1];
689 ULONG FirstEntry
= 0, Length
;
691 DPRINT1("NtfsLookupFileAt(%p, %wZ, %p, %p, %I64x)\n", Vcb
, PathName
, FileRecord
, DataContext
, CurrentMFTIndex
);
693 FsRtlDissectName(*PathName
, &Current
, &Remaining
);
695 while (Current
.Length
!= 0)
697 DPRINT1("Lookup: %wZ\n", &Current
);
699 Status
= NtfsFindMftRecord(Vcb
, CurrentMFTIndex
, &Current
, &FirstEntry
, FALSE
, &CurrentMFTIndex
, FoundName
);
700 if (!NT_SUCCESS(Status
))
705 FsRtlDissectName(*PathName
, &Current
, &Remaining
);
708 *FileRecord
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
709 if (*FileRecord
== NULL
)
711 DPRINT("NtfsLookupFileAt: Can't allocate MFT record\n");
712 return STATUS_INSUFFICIENT_RESOURCES
;
715 Status
= ReadFileRecord(Vcb
, CurrentMFTIndex
, *FileRecord
);
716 if (!NT_SUCCESS(Status
))
718 DPRINT("NtfsLookupFileAt: Can't read MFT record\n");
719 ExFreePoolWithTag(*FileRecord
, TAG_NTFS
);
723 Length
= wcslen(FoundName
) * sizeof(WCHAR
);
725 Status
= FindAttribute(Vcb
, *FileRecord
, AttributeData
, FoundName
, Length
, DataContext
);
726 if (!NT_SUCCESS(Status
))
728 DPRINT("NtfsLookupFileAt: Can't find data attribute\n");
729 ExFreePoolWithTag(*FileRecord
, TAG_NTFS
);
733 *MFTIndex
= CurrentMFTIndex
;
735 return STATUS_SUCCESS
;
739 NtfsLookupFile(PDEVICE_EXTENSION Vcb
,
740 PUNICODE_STRING PathName
,
741 PFILE_RECORD_HEADER
*FileRecord
,
742 PNTFS_ATTR_CONTEXT
*DataContext
,
745 return NtfsLookupFileAt(Vcb
, PathName
, FileRecord
, DataContext
, MFTIndex
, NTFS_FILE_ROOT
);
749 NtfsFindFileAt(PDEVICE_EXTENSION Vcb
,
750 PUNICODE_STRING SearchPattern
,
752 PFILE_RECORD_HEADER
*FileRecord
,
753 PNTFS_ATTR_CONTEXT
*DataContext
,
755 ULONGLONG CurrentMFTIndex
)
758 WCHAR FoundName
[MAX_PATH
+ 1];
761 DPRINT1("NtfsFindFileAt(%p, %wZ, %p, %p, %p, %p, %I64x)\n", Vcb
, SearchPattern
, FirstEntry
, FileRecord
, DataContext
, MFTIndex
, CurrentMFTIndex
);
763 Status
= NtfsFindMftRecord(Vcb
, CurrentMFTIndex
, SearchPattern
, FirstEntry
, TRUE
, &CurrentMFTIndex
, FoundName
);
764 if (!NT_SUCCESS(Status
))
769 *FileRecord
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
770 if (*FileRecord
== NULL
)
772 DPRINT("NtfsFindFileAt: Can't allocate MFT record\n");
773 return STATUS_INSUFFICIENT_RESOURCES
;
776 Status
= ReadFileRecord(Vcb
, CurrentMFTIndex
, *FileRecord
);
777 if (!NT_SUCCESS(Status
))
779 DPRINT("NtfsFindFileAt: Can't read MFT record\n");
780 ExFreePoolWithTag(*FileRecord
, TAG_NTFS
);
784 Length
= wcslen(FoundName
) * sizeof(WCHAR
);
786 Status
= FindAttribute(Vcb
, *FileRecord
, AttributeData
, FoundName
, Length
, DataContext
);
787 if (!NT_SUCCESS(Status
))
789 DPRINT("NtfsFindFileAt: Can't find data attribute\n");
790 ExFreePoolWithTag(*FileRecord
, TAG_NTFS
);
794 *MFTIndex
= CurrentMFTIndex
;
796 return STATUS_SUCCESS
;