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 FindAttribute(PDEVICE_EXTENSION Vcb
,
84 PFILE_RECORD_HEADER MftRecord
,
88 PNTFS_ATTR_CONTEXT
* AttrCtx
)
92 FIND_ATTR_CONTXT Context
;
93 PNTFS_ATTR_RECORD Attribute
;
95 DPRINT("FindAttribute(%p, %p, 0x%x, %S, %u, %p)\n", Vcb
, MftRecord
, Type
, Name
, NameLength
, AttrCtx
);
98 Status
= FindFirstAttribute(&Context
, Vcb
, MftRecord
, FALSE
, &Attribute
);
99 while (NT_SUCCESS(Status
))
101 if (Attribute
->Type
== Type
&& Attribute
->NameLength
== NameLength
)
107 AttrName
= (PWCHAR
)((PCHAR
)Attribute
+ Attribute
->NameOffset
);
108 DPRINT("%.*S, %.*S\n", Attribute
->NameLength
, AttrName
, NameLength
, Name
);
109 if (RtlCompareMemory(AttrName
, Name
, NameLength
<< 1) == (NameLength
<< 1))
121 /* Found it, fill up the context and return. */
122 DPRINT("Found context\n");
123 *AttrCtx
= PrepareAttributeContext(Attribute
);
124 FindCloseAttribute(&Context
);
125 return STATUS_SUCCESS
;
129 Status
= FindNextAttribute(&Context
, &Attribute
);
132 FindCloseAttribute(&Context
);
133 return STATUS_OBJECT_NAME_NOT_FOUND
;
138 AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord
)
140 if (AttrRecord
->IsNonResident
)
141 return AttrRecord
->NonResident
.AllocatedSize
;
143 return AttrRecord
->Resident
.ValueLength
;
148 AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord
)
150 if (AttrRecord
->IsNonResident
)
151 return AttrRecord
->NonResident
.DataSize
;
153 return AttrRecord
->Resident
.ValueLength
;
158 ReadAttribute(PDEVICE_EXTENSION Vcb
,
159 PNTFS_ATTR_CONTEXT Context
,
166 LONGLONG DataRunOffset
;
167 ULONGLONG DataRunLength
;
168 LONGLONG DataRunStartLCN
;
169 ULONGLONG CurrentOffset
;
174 if (!Context
->Record
.IsNonResident
)
176 if (Offset
> Context
->Record
.Resident
.ValueLength
)
178 if (Offset
+ Length
> Context
->Record
.Resident
.ValueLength
)
179 Length
= (ULONG
)(Context
->Record
.Resident
.ValueLength
- Offset
);
180 RtlCopyMemory(Buffer
, (PCHAR
)&Context
->Record
+ Context
->Record
.Resident
.ValueOffset
+ Offset
, Length
);
185 * Non-resident attribute
189 * I. Find the corresponding start data run.
194 // FIXME: Cache seems to be non-working. Disable it for now
195 //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
198 DataRun
= Context
->CacheRun
;
199 LastLCN
= Context
->CacheRunLastLCN
;
200 DataRunStartLCN
= Context
->CacheRunStartLCN
;
201 DataRunLength
= Context
->CacheRunLength
;
202 CurrentOffset
= Context
->CacheRunCurrentOffset
;
207 DataRun
= (PUCHAR
)&Context
->Record
+ Context
->Record
.NonResident
.MappingPairsOffset
;
212 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
213 if (DataRunOffset
!= -1)
215 /* Normal data run. */
216 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
217 LastLCN
= DataRunStartLCN
;
221 /* Sparse data run. */
222 DataRunStartLCN
= -1;
225 if (Offset
>= CurrentOffset
&&
226 Offset
< CurrentOffset
+ (DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
))
236 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
241 * II. Go through the run list and read the data
244 ReadLength
= (ULONG
)min(DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
- (Offset
- CurrentOffset
), Length
);
245 if (DataRunStartLCN
== -1)
247 RtlZeroMemory(Buffer
, ReadLength
);
248 Status
= STATUS_SUCCESS
;
252 Status
= NtfsReadDisk(Vcb
->StorageDevice
,
253 DataRunStartLCN
* Vcb
->NtfsInfo
.BytesPerCluster
+ Offset
- CurrentOffset
,
255 Vcb
->NtfsInfo
.BytesPerSector
,
259 if (NT_SUCCESS(Status
))
261 Length
-= ReadLength
;
262 Buffer
+= ReadLength
;
263 AlreadyRead
+= ReadLength
;
265 if (ReadLength
== DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
- (Offset
- CurrentOffset
))
267 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
268 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
269 if (DataRunOffset
!= (ULONGLONG
)-1)
271 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
272 LastLCN
= DataRunStartLCN
;
275 DataRunStartLCN
= -1;
280 ReadLength
= (ULONG
)min(DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
, Length
);
281 if (DataRunStartLCN
== -1)
282 RtlZeroMemory(Buffer
, ReadLength
);
285 Status
= NtfsReadDisk(Vcb
->StorageDevice
,
286 DataRunStartLCN
* Vcb
->NtfsInfo
.BytesPerCluster
,
288 Vcb
->NtfsInfo
.BytesPerSector
,
291 if (!NT_SUCCESS(Status
))
295 Length
-= ReadLength
;
296 Buffer
+= ReadLength
;
297 AlreadyRead
+= ReadLength
;
299 /* We finished this request, but there still data in this data run. */
300 if (Length
== 0 && ReadLength
!= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
)
304 * Go to next run in the list.
309 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
310 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
311 if (DataRunOffset
!= -1)
313 /* Normal data run. */
314 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
315 LastLCN
= DataRunStartLCN
;
319 /* Sparse data run. */
320 DataRunStartLCN
= -1;
326 Context
->CacheRun
= DataRun
;
327 Context
->CacheRunOffset
= Offset
+ AlreadyRead
;
328 Context
->CacheRunStartLCN
= DataRunStartLCN
;
329 Context
->CacheRunLength
= DataRunLength
;
330 Context
->CacheRunLastLCN
= LastLCN
;
331 Context
->CacheRunCurrentOffset
= CurrentOffset
;
338 ReadFileRecord(PDEVICE_EXTENSION Vcb
,
340 PFILE_RECORD_HEADER file
)
344 DPRINT("ReadFileRecord(%p, %I64x, %p)\n", Vcb
, index
, file
);
346 BytesRead
= ReadAttribute(Vcb
, Vcb
->MFTContext
, index
* Vcb
->NtfsInfo
.BytesPerFileRecord
, (PCHAR
)file
, Vcb
->NtfsInfo
.BytesPerFileRecord
);
347 if (BytesRead
!= Vcb
->NtfsInfo
.BytesPerFileRecord
)
349 DPRINT1("ReadFileRecord failed: %I64u read, %u expected\n", BytesRead
, Vcb
->NtfsInfo
.BytesPerFileRecord
);
350 return STATUS_PARTIAL_COPY
;
353 /* Apply update sequence array fixups. */
354 return FixupUpdateSequenceArray(Vcb
, &file
->Ntfs
);
359 FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb
,
360 PNTFS_RECORD_HEADER Record
)
367 USA
= (USHORT
*)((PCHAR
)Record
+ Record
->UsaOffset
);
368 USANumber
= *(USA
++);
369 USACount
= Record
->UsaCount
- 1; /* Exclude the USA Number. */
370 Block
= (USHORT
*)((PCHAR
)Record
+ Vcb
->NtfsInfo
.BytesPerSector
- 2);
374 if (*Block
!= USANumber
)
376 DPRINT1("Mismatch with USA: %u read, %u expected\n" , *Block
, USANumber
);
377 return STATUS_UNSUCCESSFUL
;
380 Block
= (USHORT
*)((PCHAR
)Block
+ Vcb
->NtfsInfo
.BytesPerSector
);
384 return STATUS_SUCCESS
;
389 ReadLCN(PDEVICE_EXTENSION Vcb
,
394 LARGE_INTEGER DiskSector
;
396 DiskSector
.QuadPart
= lcn
;
398 return NtfsReadSectors(Vcb
->StorageDevice
,
399 DiskSector
.u
.LowPart
* Vcb
->NtfsInfo
.SectorsPerCluster
,
400 count
* Vcb
->NtfsInfo
.SectorsPerCluster
,
401 Vcb
->NtfsInfo
.BytesPerSector
,
408 CompareFileName(PUNICODE_STRING FileName
,
409 PINDEX_ENTRY_ATTRIBUTE IndexEntry
,
412 BOOLEAN Ret
, Alloc
= FALSE
;
413 UNICODE_STRING EntryName
;
415 EntryName
.Buffer
= IndexEntry
->FileName
.Name
;
417 EntryName
.MaximumLength
= IndexEntry
->FileName
.NameLength
* sizeof(WCHAR
);
421 UNICODE_STRING IntFileName
;
422 if (IndexEntry
->FileName
.NameType
!= NTFS_FILE_NAME_POSIX
)
424 NT_VERIFY(NT_SUCCESS(RtlUpcaseUnicodeString(&IntFileName
, FileName
, TRUE
)));
429 IntFileName
= *FileName
;
432 Ret
= FsRtlIsNameInExpression(&IntFileName
, &EntryName
, (IndexEntry
->FileName
.NameType
!= NTFS_FILE_NAME_POSIX
), NULL
);
436 RtlFreeUnicodeString(&IntFileName
);
443 return (RtlCompareUnicodeString(FileName
, &EntryName
, (IndexEntry
->FileName
.NameType
!= NTFS_FILE_NAME_POSIX
)) == 0);
450 DumpIndexEntry(PINDEX_ENTRY_ATTRIBUTE IndexEntry
)
452 DPRINT1("Entry: %p\n", IndexEntry
);
453 DPRINT1("\tData.Directory.IndexedFile: %I64x\n", IndexEntry
->Data
.Directory
.IndexedFile
);
454 DPRINT1("\tLength: %u\n", IndexEntry
->Length
);
455 DPRINT1("\tKeyLength: %u\n", IndexEntry
->KeyLength
);
456 DPRINT1("\tFlags: %x\n", IndexEntry
->Flags
);
457 DPRINT1("\tReserved: %x\n", IndexEntry
->Reserved
);
458 DPRINT1("\t\tDirectoryFileReferenceNumber: %I64x\n", IndexEntry
->FileName
.DirectoryFileReferenceNumber
);
459 DPRINT1("\t\tCreationTime: %I64u\n", IndexEntry
->FileName
.CreationTime
);
460 DPRINT1("\t\tChangeTime: %I64u\n", IndexEntry
->FileName
.ChangeTime
);
461 DPRINT1("\t\tLastWriteTime: %I64u\n", IndexEntry
->FileName
.LastWriteTime
);
462 DPRINT1("\t\tLastAccessTime: %I64u\n", IndexEntry
->FileName
.LastAccessTime
);
463 DPRINT1("\t\tAllocatedSize: %I64u\n", IndexEntry
->FileName
.AllocatedSize
);
464 DPRINT1("\t\tDataSize: %I64u\n", IndexEntry
->FileName
.DataSize
);
465 DPRINT1("\t\tFileAttributes: %x\n", IndexEntry
->FileName
.FileAttributes
);
466 DPRINT1("\t\tNameLength: %u\n", IndexEntry
->FileName
.NameLength
);
467 DPRINT1("\t\tNameType: %x\n", IndexEntry
->FileName
.NameType
);
468 DPRINT1("\t\tName: %.*S\n", IndexEntry
->FileName
.NameLength
, IndexEntry
->FileName
.Name
);
473 BrowseIndexEntries(PDEVICE_EXTENSION Vcb
,
474 PFILE_RECORD_HEADER MftRecord
,
476 ULONG IndexBlockSize
,
477 PINDEX_ENTRY_ATTRIBUTE FirstEntry
,
478 PINDEX_ENTRY_ATTRIBUTE LastEntry
,
479 PUNICODE_STRING FileName
,
483 ULONGLONG
*OutMFTIndex
)
487 PINDEX_ENTRY_ATTRIBUTE IndexEntry
;
488 PNTFS_ATTR_CONTEXT IndexAllocationCtx
;
489 ULONGLONG IndexAllocationSize
;
490 PINDEX_BUFFER IndexBuffer
;
492 DPRINT("BrowseIndexEntries(%p, %p, %p, %u, %p, %p, %wZ, %u, %u, %u, %p)\n", Vcb
, MftRecord
, IndexRecord
, IndexBlockSize
, FirstEntry
, LastEntry
, FileName
, *StartEntry
, *CurrentEntry
, DirSearch
, OutMFTIndex
);
494 IndexEntry
= FirstEntry
;
495 while (IndexEntry
< LastEntry
&&
496 !(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_END
))
498 if ((IndexEntry
->Data
.Directory
.IndexedFile
& NTFS_MFT_MASK
) > 0x10 &&
499 *CurrentEntry
>= *StartEntry
&&
500 IndexEntry
->FileName
.NameType
!= NTFS_FILE_NAME_DOS
&&
501 CompareFileName(FileName
, IndexEntry
, DirSearch
))
503 *StartEntry
= *CurrentEntry
;
504 *OutMFTIndex
= (IndexEntry
->Data
.Directory
.IndexedFile
& NTFS_MFT_MASK
);
505 return STATUS_SUCCESS
;
508 (*CurrentEntry
) += 1;
509 ASSERT(IndexEntry
->Length
>= sizeof(INDEX_ENTRY_ATTRIBUTE
));
510 IndexEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((PCHAR
)IndexEntry
+ IndexEntry
->Length
);
513 /* If we're already browsing a subnode */
514 if (IndexRecord
== NULL
)
516 return STATUS_OBJECT_PATH_NOT_FOUND
;
519 /* If there's no subnode */
520 if (!(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_NODE
))
522 return STATUS_OBJECT_PATH_NOT_FOUND
;
525 Status
= FindAttribute(Vcb
, MftRecord
, AttributeIndexAllocation
, L
"$I30", 4, &IndexAllocationCtx
);
526 if (!NT_SUCCESS(Status
))
528 DPRINT("Corrupted filesystem!\n");
532 IndexAllocationSize
= AttributeDataLength(&IndexAllocationCtx
->Record
);
533 Status
= STATUS_OBJECT_PATH_NOT_FOUND
;
534 for (RecordOffset
= 0; RecordOffset
< IndexAllocationSize
; RecordOffset
+= IndexBlockSize
)
536 ReadAttribute(Vcb
, IndexAllocationCtx
, RecordOffset
, IndexRecord
, IndexBlockSize
);
537 Status
= FixupUpdateSequenceArray(Vcb
, &((PFILE_RECORD_HEADER
)IndexRecord
)->Ntfs
);
538 if (!NT_SUCCESS(Status
))
543 IndexBuffer
= (PINDEX_BUFFER
)IndexRecord
;
544 ASSERT(IndexBuffer
->Ntfs
.Type
== NRH_INDX_TYPE
);
545 ASSERT(IndexBuffer
->Header
.AllocatedSize
+ FIELD_OFFSET(INDEX_BUFFER
, Header
) == IndexBlockSize
);
546 FirstEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)&IndexBuffer
->Header
+ IndexBuffer
->Header
.FirstEntryOffset
);
547 LastEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)&IndexBuffer
->Header
+ IndexBuffer
->Header
.TotalSizeOfEntries
);
548 ASSERT(LastEntry
<= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)IndexBuffer
+ IndexBlockSize
));
550 Status
= BrowseIndexEntries(NULL
, NULL
, NULL
, 0, FirstEntry
, LastEntry
, FileName
, StartEntry
, CurrentEntry
, DirSearch
, OutMFTIndex
);
551 if (NT_SUCCESS(Status
))
557 ReleaseAttributeContext(IndexAllocationCtx
);
562 NtfsFindMftRecord(PDEVICE_EXTENSION Vcb
,
564 PUNICODE_STRING FileName
,
567 ULONGLONG
*OutMFTIndex
)
569 PFILE_RECORD_HEADER MftRecord
;
570 PNTFS_ATTR_CONTEXT IndexRootCtx
;
571 PINDEX_ROOT_ATTRIBUTE IndexRoot
;
573 PINDEX_ENTRY_ATTRIBUTE IndexEntry
, IndexEntryEnd
;
575 ULONG CurrentEntry
= 0;
577 DPRINT("NtfsFindMftRecord(%p, %I64d, %wZ, %u, %u, %p)\n", Vcb
, MFTIndex
, FileName
, *FirstEntry
, DirSearch
, OutMFTIndex
);
579 MftRecord
= ExAllocatePoolWithTag(NonPagedPool
,
580 Vcb
->NtfsInfo
.BytesPerFileRecord
,
582 if (MftRecord
== NULL
)
584 return STATUS_INSUFFICIENT_RESOURCES
;
587 Status
= ReadFileRecord(Vcb
, MFTIndex
, MftRecord
);
588 if (!NT_SUCCESS(Status
))
590 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
594 ASSERT(MftRecord
->Ntfs
.Type
== NRH_FILE_TYPE
);
595 Status
= FindAttribute(Vcb
, MftRecord
, AttributeIndexRoot
, L
"$I30", 4, &IndexRootCtx
);
596 if (!NT_SUCCESS(Status
))
598 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
602 IndexRecord
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerIndexRecord
, TAG_NTFS
);
603 if (IndexRecord
== NULL
)
605 ReleaseAttributeContext(IndexRootCtx
);
606 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
607 return STATUS_INSUFFICIENT_RESOURCES
;
610 ReadAttribute(Vcb
, IndexRootCtx
, 0, IndexRecord
, Vcb
->NtfsInfo
.BytesPerIndexRecord
);
611 IndexRoot
= (PINDEX_ROOT_ATTRIBUTE
)IndexRecord
;
612 IndexEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((PCHAR
)&IndexRoot
->Header
+ IndexRoot
->Header
.FirstEntryOffset
);
613 /* Index root is always resident. */
614 IndexEntryEnd
= (PINDEX_ENTRY_ATTRIBUTE
)(IndexRecord
+ IndexRoot
->Header
.TotalSizeOfEntries
);
615 ReleaseAttributeContext(IndexRootCtx
);
617 DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb
->NtfsInfo
.BytesPerIndexRecord
, IndexRoot
->SizeOfEntry
);
619 Status
= BrowseIndexEntries(Vcb
, MftRecord
, IndexRecord
, IndexRoot
->SizeOfEntry
, IndexEntry
, IndexEntryEnd
, FileName
, FirstEntry
, &CurrentEntry
, DirSearch
, OutMFTIndex
);
621 ExFreePoolWithTag(IndexRecord
, TAG_NTFS
);
622 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
628 NtfsLookupFileAt(PDEVICE_EXTENSION Vcb
,
629 PUNICODE_STRING PathName
,
630 PFILE_RECORD_HEADER
*FileRecord
,
632 ULONGLONG CurrentMFTIndex
)
634 UNICODE_STRING Current
, Remaining
;
636 ULONG FirstEntry
= 0;
638 DPRINT("NtfsLookupFileAt(%p, %wZ, %p, %I64x)\n", Vcb
, PathName
, FileRecord
, CurrentMFTIndex
);
640 FsRtlDissectName(*PathName
, &Current
, &Remaining
);
642 while (Current
.Length
!= 0)
644 DPRINT("Current: %wZ\n", &Current
);
646 Status
= NtfsFindMftRecord(Vcb
, CurrentMFTIndex
, &Current
, &FirstEntry
, FALSE
, &CurrentMFTIndex
);
647 if (!NT_SUCCESS(Status
))
652 if (Remaining
.Length
== 0)
655 FsRtlDissectName(Current
, &Current
, &Remaining
);
658 *FileRecord
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
659 if (*FileRecord
== NULL
)
661 DPRINT("NtfsLookupFileAt: Can't allocate MFT record\n");
662 return STATUS_INSUFFICIENT_RESOURCES
;
665 Status
= ReadFileRecord(Vcb
, CurrentMFTIndex
, *FileRecord
);
666 if (!NT_SUCCESS(Status
))
668 DPRINT("NtfsLookupFileAt: Can't read MFT record\n");
669 ExFreePoolWithTag(*FileRecord
, TAG_NTFS
);
673 *MFTIndex
= CurrentMFTIndex
;
675 return STATUS_SUCCESS
;
679 NtfsLookupFile(PDEVICE_EXTENSION Vcb
,
680 PUNICODE_STRING PathName
,
681 PFILE_RECORD_HEADER
*FileRecord
,
684 return NtfsLookupFileAt(Vcb
, PathName
, FileRecord
, MFTIndex
, NTFS_FILE_ROOT
);
688 NtfsFindFileAt(PDEVICE_EXTENSION Vcb
,
689 PUNICODE_STRING SearchPattern
,
691 PFILE_RECORD_HEADER
*FileRecord
,
693 ULONGLONG CurrentMFTIndex
)
697 DPRINT("NtfsFindFileAt(%p, %wZ, %u, %p, %p, %I64x)\n", Vcb
, SearchPattern
, *FirstEntry
, FileRecord
, MFTIndex
, CurrentMFTIndex
);
699 Status
= NtfsFindMftRecord(Vcb
, CurrentMFTIndex
, SearchPattern
, FirstEntry
, TRUE
, &CurrentMFTIndex
);
700 if (!NT_SUCCESS(Status
))
702 DPRINT("NtfsFindFileAt: NtfsFindMftRecord() failed with status 0x%08lx\n", Status
);
706 *FileRecord
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
707 if (*FileRecord
== NULL
)
709 DPRINT("NtfsFindFileAt: Can't allocate MFT record\n");
710 return STATUS_INSUFFICIENT_RESOURCES
;
713 Status
= ReadFileRecord(Vcb
, CurrentMFTIndex
, *FileRecord
);
714 if (!NT_SUCCESS(Status
))
716 DPRINT("NtfsFindFileAt: Can't read MFT record\n");
717 ExFreePoolWithTag(*FileRecord
, TAG_NTFS
);
721 *MFTIndex
= CurrentMFTIndex
;
723 return STATUS_SUCCESS
;