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 UNICODE_STRING IndexOfFileNames
= RTL_CONSTANT_STRING(L
"$I30");
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 // Do not handle non-resident yet
109 ASSERT(!(AttrRecord
->IsNonResident
& 1));
111 ListContext
= PrepareAttributeContext(AttrRecord
);
113 ListSize
= AttributeDataLength(&ListContext
->Record
);
114 if(ListSize
<= 0xFFFFFFFF)
115 ListBuffer
= ExAllocatePoolWithTag(NonPagedPool
, (ULONG
)ListSize
, TAG_NTFS
);
121 DPRINT("Failed to allocate memory: %x\n", (ULONG
)ListSize
);
125 ListAttrRecord
= (PNTFS_ATTR_RECORD
)ListBuffer
;
126 ListAttrRecordEnd
= (PNTFS_ATTR_RECORD
)((PCHAR
)ListBuffer
+ ListSize
);
128 if (ReadAttribute(Vcb
, ListContext
, 0, ListBuffer
, (ULONG
)ListSize
) == ListSize
)
130 Context
= FindAttributeHelper(Vcb
, ListAttrRecord
, ListAttrRecordEnd
,
131 Type
, Name
, NameLength
);
133 ReleaseAttributeContext(ListContext
);
134 ExFreePoolWithTag(ListBuffer
, TAG_NTFS
);
138 DPRINT("Found context = %p\n", Context
);
144 if (AttrRecord
->Type
== Type
)
146 DPRINT("%d, %d\n", AttrRecord
->NameLength
, NameLength
);
147 if (AttrRecord
->NameLength
== NameLength
)
151 AttrName
= (PWCHAR
)((PCHAR
)AttrRecord
+ AttrRecord
->NameOffset
);
152 DPRINT("%s, %s\n", AttrName
, Name
);
153 if (RtlCompareMemory(AttrName
, Name
, NameLength
<< 1) == (NameLength
<< 1))
155 /* Found it, fill up the context and return. */
156 DPRINT("Found context\n");
157 return PrepareAttributeContext(AttrRecord
);
162 if (AttrRecord
->Length
== 0)
164 DPRINT("Null length attribute record\n");
167 AttrRecord
= (PNTFS_ATTR_RECORD
)((PCHAR
)AttrRecord
+ AttrRecord
->Length
);
176 FindAttribute(PDEVICE_EXTENSION Vcb
,
177 PFILE_RECORD_HEADER MftRecord
,
179 PUNICODE_STRING Name
,
180 PNTFS_ATTR_CONTEXT
* AttrCtx
)
182 PNTFS_ATTR_RECORD AttrRecord
;
183 PNTFS_ATTR_RECORD AttrRecordEnd
;
185 DPRINT("NtfsFindAttribute(%p, %p, %u, %s)\n", Vcb
, MftRecord
, Type
, Name
);
187 AttrRecord
= (PNTFS_ATTR_RECORD
)((PCHAR
)MftRecord
+ MftRecord
->AttributeOffset
);
188 AttrRecordEnd
= (PNTFS_ATTR_RECORD
)((PCHAR
)MftRecord
+ Vcb
->NtfsInfo
.BytesPerFileRecord
);
190 *AttrCtx
= FindAttributeHelper(Vcb
, AttrRecord
, AttrRecordEnd
, Type
, Name
->Buffer
, Name
->Length
);
191 if (*AttrCtx
== NULL
)
193 return STATUS_OBJECT_NAME_NOT_FOUND
;
196 return STATUS_SUCCESS
;
201 AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord
)
203 if (AttrRecord
->IsNonResident
)
204 return AttrRecord
->NonResident
.AllocatedSize
;
206 return AttrRecord
->Resident
.ValueLength
;
211 AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord
)
213 if (AttrRecord
->IsNonResident
)
214 return AttrRecord
->NonResident
.DataSize
;
216 return AttrRecord
->Resident
.ValueLength
;
221 ReadAttribute(PDEVICE_EXTENSION Vcb
,
222 PNTFS_ATTR_CONTEXT Context
,
229 LONGLONG DataRunOffset
;
230 ULONGLONG DataRunLength
;
231 LONGLONG DataRunStartLCN
;
232 ULONGLONG CurrentOffset
;
237 if (!Context
->Record
.IsNonResident
)
239 if (Offset
> Context
->Record
.Resident
.ValueLength
)
241 if (Offset
+ Length
> Context
->Record
.Resident
.ValueLength
)
242 Length
= (ULONG
)(Context
->Record
.Resident
.ValueLength
- Offset
);
243 RtlCopyMemory(Buffer
, (PCHAR
)&Context
->Record
+ Context
->Record
.Resident
.ValueOffset
+ Offset
, Length
);
248 * Non-resident attribute
252 * I. Find the corresponding start data run.
257 // FIXME: Cache seems to be non-working. Disable it for now
258 //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
261 DataRun
= Context
->CacheRun
;
262 LastLCN
= Context
->CacheRunLastLCN
;
263 DataRunStartLCN
= Context
->CacheRunStartLCN
;
264 DataRunLength
= Context
->CacheRunLength
;
265 CurrentOffset
= Context
->CacheRunCurrentOffset
;
270 DataRun
= (PUCHAR
)&Context
->Record
+ Context
->Record
.NonResident
.MappingPairsOffset
;
275 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
276 if (DataRunOffset
!= -1)
278 /* Normal data run. */
279 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
280 LastLCN
= DataRunStartLCN
;
284 /* Sparse data run. */
285 DataRunStartLCN
= -1;
288 if (Offset
>= CurrentOffset
&&
289 Offset
< CurrentOffset
+ (DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
))
299 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
304 * II. Go through the run list and read the data
307 ReadLength
= (ULONG
)min(DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
- (Offset
- CurrentOffset
), Length
);
308 if (DataRunStartLCN
== -1)
309 RtlZeroMemory(Buffer
, ReadLength
);
310 Status
= NtfsReadDisk(Vcb
->StorageDevice
,
311 DataRunStartLCN
* Vcb
->NtfsInfo
.BytesPerCluster
+ Offset
- CurrentOffset
,
315 if (NT_SUCCESS(Status
))
317 Length
-= ReadLength
;
318 Buffer
+= ReadLength
;
319 AlreadyRead
+= ReadLength
;
321 if (ReadLength
== DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
- (Offset
- CurrentOffset
))
323 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
324 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
325 if (DataRunLength
!= (ULONGLONG
)-1)
327 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
328 LastLCN
= DataRunStartLCN
;
331 DataRunStartLCN
= -1;
339 ReadLength
= (ULONG
)min(DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
, Length
);
340 if (DataRunStartLCN
== -1)
341 RtlZeroMemory(Buffer
, ReadLength
);
344 Status
= NtfsReadDisk(Vcb
->StorageDevice
,
345 DataRunStartLCN
* Vcb
->NtfsInfo
.BytesPerCluster
,
349 if (!NT_SUCCESS(Status
))
353 Length
-= ReadLength
;
354 Buffer
+= ReadLength
;
355 AlreadyRead
+= ReadLength
;
357 /* We finished this request, but there still data in this data run. */
358 if (Length
== 0 && ReadLength
!= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
)
362 * Go to next run in the list.
367 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
368 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
369 if (DataRunOffset
!= -1)
371 /* Normal data run. */
372 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
373 LastLCN
= DataRunStartLCN
;
377 /* Sparse data run. */
378 DataRunStartLCN
= -1;
384 Context
->CacheRun
= DataRun
;
385 Context
->CacheRunOffset
= Offset
+ AlreadyRead
;
386 Context
->CacheRunStartLCN
= DataRunStartLCN
;
387 Context
->CacheRunLength
= DataRunLength
;
388 Context
->CacheRunLastLCN
= LastLCN
;
389 Context
->CacheRunCurrentOffset
= CurrentOffset
;
396 ReadFileRecord(PDEVICE_EXTENSION Vcb
,
398 PFILE_RECORD_HEADER 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
)
467 UNICODE_STRING EntryName
;
469 EntryName
.Buffer
= IndexEntry
->FileName
.Name
;
471 EntryName
.MaximumLength
= IndexEntry
->FileName
.NameLength
;
473 return (RtlCompareUnicodeString(FileName
, &EntryName
, !!(IndexEntry
->FileName
.NameType
!= NTFS_FILE_NAME_POSIX
)) == TRUE
);
478 NtfsFindMftRecord(PDEVICE_EXTENSION Vcb
, ULONGLONG MFTIndex
, PUNICODE_STRING FileName
, ULONGLONG
*OutMFTIndex
)
480 PFILE_RECORD_HEADER MftRecord
;
482 PNTFS_ATTR_CONTEXT IndexRootCtx
;
483 PNTFS_ATTR_CONTEXT IndexBitmapCtx
;
484 PNTFS_ATTR_CONTEXT IndexAllocationCtx
;
485 PINDEX_ROOT_ATTRIBUTE IndexRoot
;
486 ULONGLONG BitmapDataSize
;
487 ULONGLONG IndexAllocationSize
;
490 PINDEX_ENTRY_ATTRIBUTE IndexEntry
, IndexEntryEnd
;
492 ULONG IndexBlockSize
;
495 MftRecord
= ExAllocatePoolWithTag(NonPagedPool
,
496 Vcb
->NtfsInfo
.BytesPerFileRecord
,
498 if (MftRecord
== NULL
)
500 return STATUS_INSUFFICIENT_RESOURCES
;
503 if (ReadFileRecord(Vcb
, MFTIndex
, MftRecord
))
505 //Magic = MftRecord->Magic;
507 Status
= FindAttribute(Vcb
, MftRecord
, AttributeIndexRoot
, &IndexOfFileNames
, &IndexRootCtx
);
508 if (!NT_SUCCESS(Status
))
510 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
514 IndexRecord
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerIndexRecord
, TAG_NTFS
);
515 if (IndexRecord
== NULL
)
517 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
518 return STATUS_INSUFFICIENT_RESOURCES
;
521 ReadAttribute(Vcb
, IndexRootCtx
, 0, IndexRecord
, Vcb
->NtfsInfo
.BytesPerIndexRecord
);
522 IndexRoot
= (PINDEX_ROOT_ATTRIBUTE
)IndexRecord
;
523 IndexEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((PCHAR
)&IndexRoot
->Header
+ IndexRoot
->Header
.FirstEntryOffset
);
524 /* Index root is always resident. */
525 IndexEntryEnd
= (PINDEX_ENTRY_ATTRIBUTE
)(IndexRecord
+ IndexRootCtx
->Record
.Resident
.ValueLength
);
526 ReleaseAttributeContext(IndexRootCtx
);
528 DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb
->NtfsInfo
.BytesPerIndexRecord
, IndexRoot
->SizeOfEntry
);
530 while (IndexEntry
< IndexEntryEnd
&&
531 !(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_END
))
533 if (CompareFileName(FileName
, IndexEntry
))
535 *OutMFTIndex
= IndexEntry
->Data
.Directory
.IndexedFile
;
536 ExFreePoolWithTag(IndexRecord
, TAG_NTFS
);
537 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
538 return STATUS_SUCCESS
;
540 IndexEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((PCHAR
)IndexEntry
+ IndexEntry
->Length
);
543 if (IndexRoot
->Header
.Flags
& INDEX_ROOT_LARGE
)
545 DPRINT("Large Index!\n");
547 IndexBlockSize
= IndexRoot
->SizeOfEntry
;
549 Status
= FindAttribute(Vcb
, MftRecord
, AttributeBitmap
, &IndexOfFileNames
, &IndexBitmapCtx
);
550 if (!NT_SUCCESS(Status
))
552 DPRINT("Corrupted filesystem!\n");
553 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
556 BitmapDataSize
= AttributeDataLength(&IndexBitmapCtx
->Record
);
557 DPRINT("BitmapDataSize: %x\n", (ULONG
)BitmapDataSize
);
558 if(BitmapDataSize
<= 0xFFFFFFFF)
559 BitmapData
= ExAllocatePoolWithTag(NonPagedPool
, (ULONG
)BitmapDataSize
, TAG_NTFS
);
563 if (BitmapData
== NULL
)
565 ExFreePoolWithTag(IndexRecord
, TAG_NTFS
);
566 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
567 return STATUS_INSUFFICIENT_RESOURCES
;
569 ReadAttribute(Vcb
, IndexBitmapCtx
, 0, BitmapData
, (ULONG
)BitmapDataSize
);
570 ReleaseAttributeContext(IndexBitmapCtx
);
572 Status
= FindAttribute(Vcb
, MftRecord
, AttributeIndexAllocation
, &IndexOfFileNames
, &IndexAllocationCtx
);
573 if (!NT_SUCCESS(Status
))
575 DPRINT("Corrupted filesystem!\n");
576 ExFreePoolWithTag(BitmapData
, TAG_NTFS
);
577 ExFreePoolWithTag(IndexRecord
, TAG_NTFS
);
578 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
581 IndexAllocationSize
= AttributeDataLength(&IndexAllocationCtx
->Record
);
587 DPRINT("RecordOffset: %x IndexAllocationSize: %x\n", RecordOffset
, IndexAllocationSize
);
588 for (; RecordOffset
< IndexAllocationSize
;)
590 UCHAR Bit
= 1 << ((RecordOffset
/ IndexBlockSize
) & 7);
591 ULONG Byte
= (RecordOffset
/ IndexBlockSize
) >> 3;
592 if ((BitmapData
[Byte
] & Bit
))
594 RecordOffset
+= IndexBlockSize
;
597 if (RecordOffset
>= IndexAllocationSize
)
602 ReadAttribute(Vcb
, IndexAllocationCtx
, RecordOffset
, IndexRecord
, IndexBlockSize
);
604 if (!FixupUpdateSequenceArray(Vcb
, &((PFILE_RECORD_HEADER
)IndexRecord
)->Ntfs
))
610 IndexEntry
= (PINDEX_ENTRY_ATTRIBUTE
)(IndexRecord
+ 0x18 + *(USHORT
*)(IndexRecord
+ 0x18));
611 IndexEntryEnd
= (PINDEX_ENTRY_ATTRIBUTE
)(IndexRecord
+ IndexBlockSize
);
613 while (IndexEntry
< IndexEntryEnd
&&
614 !(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_END
))
616 if (CompareFileName(FileName
, IndexEntry
))
618 DPRINT("File found\n");
619 *OutMFTIndex
= IndexEntry
->Data
.Directory
.IndexedFile
;
620 ExFreePoolWithTag(BitmapData
, TAG_NTFS
);
621 ExFreePoolWithTag(IndexRecord
, TAG_NTFS
);
622 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
623 ReleaseAttributeContext(IndexAllocationCtx
);
624 return STATUS_SUCCESS
;
626 IndexEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((PCHAR
)IndexEntry
+ IndexEntry
->Length
);
629 RecordOffset
+= IndexBlockSize
;
632 ReleaseAttributeContext(IndexAllocationCtx
);
633 ExFreePoolWithTag(BitmapData
, TAG_NTFS
);
636 ExFreePoolWithTag(IndexRecord
, TAG_NTFS
);
640 DPRINT("Can't read MFT record\n");
642 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
644 return STATUS_OBJECT_PATH_NOT_FOUND
;
648 NtfsLookupFileAt(PDEVICE_EXTENSION Vcb
,
649 PUNICODE_STRING PathName
,
650 PFILE_RECORD_HEADER
*FileRecord
,
651 PNTFS_ATTR_CONTEXT
*DataContext
,
652 ULONGLONG CurrentMFTIndex
)
654 UNICODE_STRING Current
, Remaining
;
657 DPRINT1("NtfsLookupFileAt(%p, %wZ, %p, %p, %I64x)\n", Vcb
, PathName
, FileRecord
, DataContext
, CurrentMFTIndex
);
659 FsRtlDissectName(*PathName
, &Current
, &Remaining
);
661 while (Current
.Length
!= 0)
663 DPRINT1("Lookup: %wZ\n", &Current
);
665 Status
= NtfsFindMftRecord(Vcb
, CurrentMFTIndex
, &Current
, &CurrentMFTIndex
);
666 if (!NT_SUCCESS(Status
))
671 FsRtlDissectName(*PathName
, &Current
, &Remaining
);
674 *FileRecord
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
675 if (*FileRecord
== NULL
)
677 DPRINT("NtfsLookupFile: Can't allocate MFT record\n");
678 return STATUS_INSUFFICIENT_RESOURCES
;
681 Status
= ReadFileRecord(Vcb
, CurrentMFTIndex
, *FileRecord
);
682 if (!NT_SUCCESS(Status
))
684 DPRINT("NtfsLookupFile: Can't read MFT record\n");
688 Status
= FindAttribute(Vcb
, *FileRecord
, AttributeData
, PathName
, DataContext
);
689 if (!NT_SUCCESS(Status
))
691 DPRINT("NtfsLookupFile: Can't find data attribute\n");
695 return STATUS_SUCCESS
;
699 NtfsLookupFile(PDEVICE_EXTENSION Vcb
,
700 PUNICODE_STRING PathName
,
701 PFILE_RECORD_HEADER
*FileRecord
,
702 PNTFS_ATTR_CONTEXT
*DataContext
)
704 return NtfsLookupFileAt(Vcb
, PathName
, FileRecord
, DataContext
, NTFS_FILE_ROOT
);