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)
30 /* INCLUDES *****************************************************************/
38 /* FUNCTIONS ****************************************************************/
41 PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord
)
43 PNTFS_ATTR_CONTEXT Context
;
45 Context
= ExAllocatePoolWithTag(NonPagedPool
,
46 FIELD_OFFSET(NTFS_ATTR_CONTEXT
, Record
) + AttrRecord
->Length
,
48 RtlCopyMemory(&Context
->Record
, AttrRecord
, AttrRecord
->Length
);
49 if (AttrRecord
->IsNonResident
)
51 LONGLONG DataRunOffset
;
52 ULONGLONG DataRunLength
;
54 Context
->CacheRun
= (PUCHAR
)&Context
->Record
+ Context
->Record
.NonResident
.MappingPairsOffset
;
55 Context
->CacheRunOffset
= 0;
56 Context
->CacheRun
= DecodeRun(Context
->CacheRun
, &DataRunOffset
, &DataRunLength
);
57 Context
->CacheRunLength
= DataRunLength
;
58 if (DataRunOffset
!= -1)
61 Context
->CacheRunStartLCN
=
62 Context
->CacheRunLastLCN
= DataRunOffset
;
67 Context
->CacheRunStartLCN
= -1;
68 Context
->CacheRunLastLCN
= 0;
70 Context
->CacheRunCurrentOffset
= 0;
78 ReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context
)
80 ExFreePoolWithTag(Context
, TAG_NTFS
);
85 FindAttribute(PDEVICE_EXTENSION Vcb
,
86 PFILE_RECORD_HEADER MftRecord
,
90 PNTFS_ATTR_CONTEXT
* AttrCtx
)
94 FIND_ATTR_CONTXT Context
;
95 PNTFS_ATTR_RECORD Attribute
;
97 DPRINT("FindAttribute(%p, %p, 0x%x, %S, %u, %p)\n", Vcb
, MftRecord
, Type
, Name
, NameLength
, AttrCtx
);
100 Status
= FindFirstAttribute(&Context
, Vcb
, MftRecord
, FALSE
, &Attribute
);
101 while (NT_SUCCESS(Status
))
103 if (Attribute
->Type
== Type
&& Attribute
->NameLength
== NameLength
)
109 AttrName
= (PWCHAR
)((PCHAR
)Attribute
+ Attribute
->NameOffset
);
110 DPRINT("%.*S, %.*S\n", Attribute
->NameLength
, AttrName
, NameLength
, Name
);
111 if (RtlCompareMemory(AttrName
, Name
, NameLength
<< 1) == (NameLength
<< 1))
123 /* Found it, fill up the context and return. */
124 DPRINT("Found context\n");
125 *AttrCtx
= PrepareAttributeContext(Attribute
);
126 FindCloseAttribute(&Context
);
127 return STATUS_SUCCESS
;
131 Status
= FindNextAttribute(&Context
, &Attribute
);
134 FindCloseAttribute(&Context
);
135 return STATUS_OBJECT_NAME_NOT_FOUND
;
140 AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord
)
142 if (AttrRecord
->IsNonResident
)
143 return AttrRecord
->NonResident
.AllocatedSize
;
145 return AttrRecord
->Resident
.ValueLength
;
150 AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord
)
152 if (AttrRecord
->IsNonResident
)
153 return AttrRecord
->NonResident
.DataSize
;
155 return AttrRecord
->Resident
.ValueLength
;
160 ReadAttribute(PDEVICE_EXTENSION Vcb
,
161 PNTFS_ATTR_CONTEXT Context
,
168 LONGLONG DataRunOffset
;
169 ULONGLONG DataRunLength
;
170 LONGLONG DataRunStartLCN
;
171 ULONGLONG CurrentOffset
;
176 if (!Context
->Record
.IsNonResident
)
178 if (Offset
> Context
->Record
.Resident
.ValueLength
)
180 if (Offset
+ Length
> Context
->Record
.Resident
.ValueLength
)
181 Length
= (ULONG
)(Context
->Record
.Resident
.ValueLength
- Offset
);
182 RtlCopyMemory(Buffer
, (PCHAR
)&Context
->Record
+ Context
->Record
.Resident
.ValueOffset
+ Offset
, Length
);
187 * Non-resident attribute
191 * I. Find the corresponding start data run.
196 // FIXME: Cache seems to be non-working. Disable it for now
197 //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
200 DataRun
= Context
->CacheRun
;
201 LastLCN
= Context
->CacheRunLastLCN
;
202 DataRunStartLCN
= Context
->CacheRunStartLCN
;
203 DataRunLength
= Context
->CacheRunLength
;
204 CurrentOffset
= Context
->CacheRunCurrentOffset
;
209 DataRun
= (PUCHAR
)&Context
->Record
+ Context
->Record
.NonResident
.MappingPairsOffset
;
214 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
215 if (DataRunOffset
!= -1)
217 /* Normal data run. */
218 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
219 LastLCN
= DataRunStartLCN
;
223 /* Sparse data run. */
224 DataRunStartLCN
= -1;
227 if (Offset
>= CurrentOffset
&&
228 Offset
< CurrentOffset
+ (DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
))
238 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
243 * II. Go through the run list and read the data
246 ReadLength
= (ULONG
)min(DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
- (Offset
- CurrentOffset
), Length
);
247 if (DataRunStartLCN
== -1)
249 RtlZeroMemory(Buffer
, ReadLength
);
250 Status
= STATUS_SUCCESS
;
254 Status
= NtfsReadDisk(Vcb
->StorageDevice
,
255 DataRunStartLCN
* Vcb
->NtfsInfo
.BytesPerCluster
+ Offset
- CurrentOffset
,
257 Vcb
->NtfsInfo
.BytesPerSector
,
261 if (NT_SUCCESS(Status
))
263 Length
-= ReadLength
;
264 Buffer
+= ReadLength
;
265 AlreadyRead
+= ReadLength
;
267 if (ReadLength
== DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
- (Offset
- CurrentOffset
))
269 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
270 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
271 if (DataRunOffset
!= (ULONGLONG
)-1)
273 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
274 LastLCN
= DataRunStartLCN
;
277 DataRunStartLCN
= -1;
282 ReadLength
= (ULONG
)min(DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
, Length
);
283 if (DataRunStartLCN
== -1)
284 RtlZeroMemory(Buffer
, ReadLength
);
287 Status
= NtfsReadDisk(Vcb
->StorageDevice
,
288 DataRunStartLCN
* Vcb
->NtfsInfo
.BytesPerCluster
,
290 Vcb
->NtfsInfo
.BytesPerSector
,
293 if (!NT_SUCCESS(Status
))
297 Length
-= ReadLength
;
298 Buffer
+= ReadLength
;
299 AlreadyRead
+= ReadLength
;
301 /* We finished this request, but there still data in this data run. */
302 if (Length
== 0 && ReadLength
!= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
)
306 * Go to next run in the list.
311 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
312 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
313 if (DataRunOffset
!= -1)
315 /* Normal data run. */
316 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
317 LastLCN
= DataRunStartLCN
;
321 /* Sparse data run. */
322 DataRunStartLCN
= -1;
328 Context
->CacheRun
= DataRun
;
329 Context
->CacheRunOffset
= Offset
+ AlreadyRead
;
330 Context
->CacheRunStartLCN
= DataRunStartLCN
;
331 Context
->CacheRunLength
= DataRunLength
;
332 Context
->CacheRunLastLCN
= LastLCN
;
333 Context
->CacheRunCurrentOffset
= CurrentOffset
;
340 * @name WriteAttribute
343 * Writes an NTFS attribute to the disk. It presently borrows a lot of code from ReadAttribute(),
344 * and it still needs more documentation / cleaning up.
347 * Volume Control Block indicating which volume to write the attribute to
350 * Pointer to an NTFS_ATTR_CONTEXT that has information about the attribute
353 * Offset, in bytes, from the beginning of the attribute indicating where to start
357 * The data that's being written to the device
360 * How much data will be written, in bytes
362 * @param RealLengthWritten
363 * Pointer to a ULONG which will receive how much data was written, in bytes
366 * STATUS_SUCCESS if successful, an error code otherwise. STATUS_NOT_IMPLEMENTED if
367 * writing to a sparse file.
369 * @remarks Note that in this context the word "attribute" isn't referring read-only, hidden,
370 * etc. - the file's data is actually stored in an attribute in NTFS parlance.
375 WriteAttribute(PDEVICE_EXTENSION Vcb
,
376 PNTFS_ATTR_CONTEXT Context
,
380 PULONG RealLengthWritten
)
384 LONGLONG DataRunOffset
;
385 ULONGLONG DataRunLength
;
386 LONGLONG DataRunStartLCN
;
387 ULONGLONG CurrentOffset
;
390 PUCHAR SourceBuffer
= Buffer
;
391 LONGLONG StartingOffset
;
393 DPRINT("WriteAttribute(%p, %p, %I64U, %p, %lu)\n", Vcb
, Context
, Offset
, Buffer
, Length
);
395 // is this a resident attribute?
396 if (!Context
->Record
.IsNonResident
)
398 DPRINT1("FIXME: Writing to resident NTFS records (small files) is not supported at this time.\n");
399 // (TODO: This should be really easy to implement)
401 /* LeftOver code from ReadAttribute(), may be helpful:
402 if (Offset > Context->Record.Resident.ValueLength)
404 if (Offset + Length > Context->Record.Resident.ValueLength)
405 Length = (ULONG)(Context->Record.Resident.ValueLength - Offset);
406 RtlCopyMemory(Buffer, (PCHAR)&Context->Record + Context->Record.Resident.ValueOffset + Offset, Length);
409 return STATUS_NOT_IMPLEMENTED
; // until we implement it
412 // This is a non-resident attribute.
414 // I. Find the corresponding start data run.
416 *RealLengthWritten
= 0;
418 // FIXME: Cache seems to be non-working. Disable it for now
419 //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
422 DataRun = Context->CacheRun;
423 LastLCN = Context->CacheRunLastLCN;
424 DataRunStartLCN = Context->CacheRunStartLCN;
425 DataRunLength = Context->CacheRunLength;
426 CurrentOffset = Context->CacheRunCurrentOffset;
431 DataRun
= (PUCHAR
)&Context
->Record
+ Context
->Record
.NonResident
.MappingPairsOffset
;
436 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
437 if (DataRunOffset
!= -1)
440 // DPRINT1("Writing to normal data run, LastLCN %I64u DataRunOffset %I64d\n", LastLCN, DataRunOffset);
441 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
442 LastLCN
= DataRunStartLCN
;
446 // Sparse data run. We can't support writing to sparse files yet
447 // (it may require increasing the allocation size).
448 DataRunStartLCN
= -1;
449 DPRINT1("FIXME: Writing to sparse files is not supported yet!\n");
450 return STATUS_NOT_IMPLEMENTED
;
453 // Have we reached the data run we're trying to write to?
454 if (Offset
>= CurrentOffset
&&
455 Offset
< CurrentOffset
+ (DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
))
462 // We reached the last assigned cluster
463 // TODO: assign new clusters to the end of the file.
464 // (Presently, this code will never be reached, the write should have already failed by now)
465 return STATUS_END_OF_FILE
;
468 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
472 // II. Go through the run list and write the data
474 /* REVIEWME -- As adapted from NtfsReadAttribute():
475 We seem to be making a special case for the first applicable data run, but I'm not sure why.
476 Does it have something to do with (not) caching? Is this strategy equally applicable to writing? */
478 WriteLength
= (ULONG
)min(DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
- (Offset
- CurrentOffset
), Length
);
480 StartingOffset
= DataRunStartLCN
* Vcb
->NtfsInfo
.BytesPerCluster
+ Offset
- CurrentOffset
;
482 // Write the data to the disk
483 Status
= NtfsWriteDisk(Vcb
->StorageDevice
,
486 Vcb
->NtfsInfo
.BytesPerSector
,
487 (PVOID
)SourceBuffer
);
489 // Did the write fail?
490 if (!NT_SUCCESS(Status
))
492 Context
->CacheRun
= DataRun
;
493 Context
->CacheRunOffset
= Offset
;
494 Context
->CacheRunStartLCN
= DataRunStartLCN
;
495 Context
->CacheRunLength
= DataRunLength
;
496 Context
->CacheRunLastLCN
= LastLCN
;
497 Context
->CacheRunCurrentOffset
= CurrentOffset
;
502 Length
-= WriteLength
;
503 SourceBuffer
+= WriteLength
;
504 *RealLengthWritten
+= WriteLength
;
506 // Did we write to the end of the data run?
507 if (WriteLength
== DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
- (Offset
- CurrentOffset
))
509 // Advance to the next data run
510 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
511 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
513 if (DataRunOffset
!= (ULONGLONG
)-1)
515 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
516 LastLCN
= DataRunStartLCN
;
519 DataRunStartLCN
= -1;
522 // Do we have more data to write?
525 // Make sure we don't write past the end of the current data run
526 WriteLength
= (ULONG
)min(DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
, Length
);
528 // Are we dealing with a sparse data run?
529 if (DataRunStartLCN
== -1)
531 DPRINT1("FIXME: Don't know how to write to sparse files yet! (DataRunStartLCN == -1)\n");
532 return STATUS_NOT_IMPLEMENTED
;
536 // write the data to the disk
537 Status
= NtfsWriteDisk(Vcb
->StorageDevice
,
538 DataRunStartLCN
* Vcb
->NtfsInfo
.BytesPerCluster
,
540 Vcb
->NtfsInfo
.BytesPerSector
,
541 (PVOID
)SourceBuffer
);
542 if (!NT_SUCCESS(Status
))
546 Length
-= WriteLength
;
547 SourceBuffer
+= WriteLength
;
548 *RealLengthWritten
+= WriteLength
;
550 // We finished this request, but there's still data in this data run.
551 if (Length
== 0 && WriteLength
!= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
)
554 // Go to next run in the list.
558 // that was the last run
561 // Failed sanity check.
562 DPRINT1("Encountered EOF before expected!\n");
563 return STATUS_END_OF_FILE
;
569 // Advance to the next data run
570 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
571 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
572 if (DataRunOffset
!= -1)
575 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
576 LastLCN
= DataRunStartLCN
;
581 DataRunStartLCN
= -1;
583 } // end while (Length > 0) [more data to write]
585 Context
->CacheRun
= DataRun
;
586 Context
->CacheRunOffset
= Offset
+ *RealLengthWritten
;
587 Context
->CacheRunStartLCN
= DataRunStartLCN
;
588 Context
->CacheRunLength
= DataRunLength
;
589 Context
->CacheRunLastLCN
= LastLCN
;
590 Context
->CacheRunCurrentOffset
= CurrentOffset
;
596 ReadFileRecord(PDEVICE_EXTENSION Vcb
,
598 PFILE_RECORD_HEADER file
)
602 DPRINT("ReadFileRecord(%p, %I64x, %p)\n", Vcb
, index
, file
);
604 BytesRead
= ReadAttribute(Vcb
, Vcb
->MFTContext
, index
* Vcb
->NtfsInfo
.BytesPerFileRecord
, (PCHAR
)file
, Vcb
->NtfsInfo
.BytesPerFileRecord
);
605 if (BytesRead
!= Vcb
->NtfsInfo
.BytesPerFileRecord
)
607 DPRINT1("ReadFileRecord failed: %I64u read, %u expected\n", BytesRead
, Vcb
->NtfsInfo
.BytesPerFileRecord
);
608 return STATUS_PARTIAL_COPY
;
611 /* Apply update sequence array fixups. */
612 return FixupUpdateSequenceArray(Vcb
, &file
->Ntfs
);
617 FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb
,
618 PNTFS_RECORD_HEADER Record
)
625 USA
= (USHORT
*)((PCHAR
)Record
+ Record
->UsaOffset
);
626 USANumber
= *(USA
++);
627 USACount
= Record
->UsaCount
- 1; /* Exclude the USA Number. */
628 Block
= (USHORT
*)((PCHAR
)Record
+ Vcb
->NtfsInfo
.BytesPerSector
- 2);
632 if (*Block
!= USANumber
)
634 DPRINT1("Mismatch with USA: %u read, %u expected\n" , *Block
, USANumber
);
635 return STATUS_UNSUCCESSFUL
;
638 Block
= (USHORT
*)((PCHAR
)Block
+ Vcb
->NtfsInfo
.BytesPerSector
);
642 return STATUS_SUCCESS
;
647 ReadLCN(PDEVICE_EXTENSION Vcb
,
652 LARGE_INTEGER DiskSector
;
654 DiskSector
.QuadPart
= lcn
;
656 return NtfsReadSectors(Vcb
->StorageDevice
,
657 DiskSector
.u
.LowPart
* Vcb
->NtfsInfo
.SectorsPerCluster
,
658 count
* Vcb
->NtfsInfo
.SectorsPerCluster
,
659 Vcb
->NtfsInfo
.BytesPerSector
,
666 CompareFileName(PUNICODE_STRING FileName
,
667 PINDEX_ENTRY_ATTRIBUTE IndexEntry
,
670 BOOLEAN Ret
, Alloc
= FALSE
;
671 UNICODE_STRING EntryName
;
673 EntryName
.Buffer
= IndexEntry
->FileName
.Name
;
675 EntryName
.MaximumLength
= IndexEntry
->FileName
.NameLength
* sizeof(WCHAR
);
679 UNICODE_STRING IntFileName
;
680 if (IndexEntry
->FileName
.NameType
!= NTFS_FILE_NAME_POSIX
)
682 NT_VERIFY(NT_SUCCESS(RtlUpcaseUnicodeString(&IntFileName
, FileName
, TRUE
)));
687 IntFileName
= *FileName
;
690 Ret
= FsRtlIsNameInExpression(&IntFileName
, &EntryName
, (IndexEntry
->FileName
.NameType
!= NTFS_FILE_NAME_POSIX
), NULL
);
694 RtlFreeUnicodeString(&IntFileName
);
701 return (RtlCompareUnicodeString(FileName
, &EntryName
, (IndexEntry
->FileName
.NameType
!= NTFS_FILE_NAME_POSIX
)) == 0);
708 DumpIndexEntry(PINDEX_ENTRY_ATTRIBUTE IndexEntry
)
710 DPRINT1("Entry: %p\n", IndexEntry
);
711 DPRINT1("\tData.Directory.IndexedFile: %I64x\n", IndexEntry
->Data
.Directory
.IndexedFile
);
712 DPRINT1("\tLength: %u\n", IndexEntry
->Length
);
713 DPRINT1("\tKeyLength: %u\n", IndexEntry
->KeyLength
);
714 DPRINT1("\tFlags: %x\n", IndexEntry
->Flags
);
715 DPRINT1("\tReserved: %x\n", IndexEntry
->Reserved
);
716 DPRINT1("\t\tDirectoryFileReferenceNumber: %I64x\n", IndexEntry
->FileName
.DirectoryFileReferenceNumber
);
717 DPRINT1("\t\tCreationTime: %I64u\n", IndexEntry
->FileName
.CreationTime
);
718 DPRINT1("\t\tChangeTime: %I64u\n", IndexEntry
->FileName
.ChangeTime
);
719 DPRINT1("\t\tLastWriteTime: %I64u\n", IndexEntry
->FileName
.LastWriteTime
);
720 DPRINT1("\t\tLastAccessTime: %I64u\n", IndexEntry
->FileName
.LastAccessTime
);
721 DPRINT1("\t\tAllocatedSize: %I64u\n", IndexEntry
->FileName
.AllocatedSize
);
722 DPRINT1("\t\tDataSize: %I64u\n", IndexEntry
->FileName
.DataSize
);
723 DPRINT1("\t\tFileAttributes: %x\n", IndexEntry
->FileName
.FileAttributes
);
724 DPRINT1("\t\tNameLength: %u\n", IndexEntry
->FileName
.NameLength
);
725 DPRINT1("\t\tNameType: %x\n", IndexEntry
->FileName
.NameType
);
726 DPRINT1("\t\tName: %.*S\n", IndexEntry
->FileName
.NameLength
, IndexEntry
->FileName
.Name
);
731 BrowseIndexEntries(PDEVICE_EXTENSION Vcb
,
732 PFILE_RECORD_HEADER MftRecord
,
734 ULONG IndexBlockSize
,
735 PINDEX_ENTRY_ATTRIBUTE FirstEntry
,
736 PINDEX_ENTRY_ATTRIBUTE LastEntry
,
737 PUNICODE_STRING FileName
,
741 ULONGLONG
*OutMFTIndex
)
745 PINDEX_ENTRY_ATTRIBUTE IndexEntry
;
746 PNTFS_ATTR_CONTEXT IndexAllocationCtx
;
747 ULONGLONG IndexAllocationSize
;
748 PINDEX_BUFFER IndexBuffer
;
750 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
);
752 IndexEntry
= FirstEntry
;
753 while (IndexEntry
< LastEntry
&&
754 !(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_END
))
756 if ((IndexEntry
->Data
.Directory
.IndexedFile
& NTFS_MFT_MASK
) > 0x10 &&
757 *CurrentEntry
>= *StartEntry
&&
758 IndexEntry
->FileName
.NameType
!= NTFS_FILE_NAME_DOS
&&
759 CompareFileName(FileName
, IndexEntry
, DirSearch
))
761 *StartEntry
= *CurrentEntry
;
762 *OutMFTIndex
= (IndexEntry
->Data
.Directory
.IndexedFile
& NTFS_MFT_MASK
);
763 return STATUS_SUCCESS
;
766 (*CurrentEntry
) += 1;
767 ASSERT(IndexEntry
->Length
>= sizeof(INDEX_ENTRY_ATTRIBUTE
));
768 IndexEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((PCHAR
)IndexEntry
+ IndexEntry
->Length
);
771 /* If we're already browsing a subnode */
772 if (IndexRecord
== NULL
)
774 return STATUS_OBJECT_PATH_NOT_FOUND
;
777 /* If there's no subnode */
778 if (!(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_NODE
))
780 return STATUS_OBJECT_PATH_NOT_FOUND
;
783 Status
= FindAttribute(Vcb
, MftRecord
, AttributeIndexAllocation
, L
"$I30", 4, &IndexAllocationCtx
);
784 if (!NT_SUCCESS(Status
))
786 DPRINT("Corrupted filesystem!\n");
790 IndexAllocationSize
= AttributeDataLength(&IndexAllocationCtx
->Record
);
791 Status
= STATUS_OBJECT_PATH_NOT_FOUND
;
792 for (RecordOffset
= 0; RecordOffset
< IndexAllocationSize
; RecordOffset
+= IndexBlockSize
)
794 ReadAttribute(Vcb
, IndexAllocationCtx
, RecordOffset
, IndexRecord
, IndexBlockSize
);
795 Status
= FixupUpdateSequenceArray(Vcb
, &((PFILE_RECORD_HEADER
)IndexRecord
)->Ntfs
);
796 if (!NT_SUCCESS(Status
))
801 IndexBuffer
= (PINDEX_BUFFER
)IndexRecord
;
802 ASSERT(IndexBuffer
->Ntfs
.Type
== NRH_INDX_TYPE
);
803 ASSERT(IndexBuffer
->Header
.AllocatedSize
+ FIELD_OFFSET(INDEX_BUFFER
, Header
) == IndexBlockSize
);
804 FirstEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)&IndexBuffer
->Header
+ IndexBuffer
->Header
.FirstEntryOffset
);
805 LastEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)&IndexBuffer
->Header
+ IndexBuffer
->Header
.TotalSizeOfEntries
);
806 ASSERT(LastEntry
<= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)IndexBuffer
+ IndexBlockSize
));
808 Status
= BrowseIndexEntries(NULL
, NULL
, NULL
, 0, FirstEntry
, LastEntry
, FileName
, StartEntry
, CurrentEntry
, DirSearch
, OutMFTIndex
);
809 if (NT_SUCCESS(Status
))
815 ReleaseAttributeContext(IndexAllocationCtx
);
820 NtfsFindMftRecord(PDEVICE_EXTENSION Vcb
,
822 PUNICODE_STRING FileName
,
825 ULONGLONG
*OutMFTIndex
)
827 PFILE_RECORD_HEADER MftRecord
;
828 PNTFS_ATTR_CONTEXT IndexRootCtx
;
829 PINDEX_ROOT_ATTRIBUTE IndexRoot
;
831 PINDEX_ENTRY_ATTRIBUTE IndexEntry
, IndexEntryEnd
;
833 ULONG CurrentEntry
= 0;
835 DPRINT("NtfsFindMftRecord(%p, %I64d, %wZ, %u, %u, %p)\n", Vcb
, MFTIndex
, FileName
, *FirstEntry
, DirSearch
, OutMFTIndex
);
837 MftRecord
= ExAllocatePoolWithTag(NonPagedPool
,
838 Vcb
->NtfsInfo
.BytesPerFileRecord
,
840 if (MftRecord
== NULL
)
842 return STATUS_INSUFFICIENT_RESOURCES
;
845 Status
= ReadFileRecord(Vcb
, MFTIndex
, MftRecord
);
846 if (!NT_SUCCESS(Status
))
848 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
852 ASSERT(MftRecord
->Ntfs
.Type
== NRH_FILE_TYPE
);
853 Status
= FindAttribute(Vcb
, MftRecord
, AttributeIndexRoot
, L
"$I30", 4, &IndexRootCtx
);
854 if (!NT_SUCCESS(Status
))
856 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
860 IndexRecord
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerIndexRecord
, TAG_NTFS
);
861 if (IndexRecord
== NULL
)
863 ReleaseAttributeContext(IndexRootCtx
);
864 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
865 return STATUS_INSUFFICIENT_RESOURCES
;
868 ReadAttribute(Vcb
, IndexRootCtx
, 0, IndexRecord
, Vcb
->NtfsInfo
.BytesPerIndexRecord
);
869 IndexRoot
= (PINDEX_ROOT_ATTRIBUTE
)IndexRecord
;
870 IndexEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((PCHAR
)&IndexRoot
->Header
+ IndexRoot
->Header
.FirstEntryOffset
);
871 /* Index root is always resident. */
872 IndexEntryEnd
= (PINDEX_ENTRY_ATTRIBUTE
)(IndexRecord
+ IndexRoot
->Header
.TotalSizeOfEntries
);
873 ReleaseAttributeContext(IndexRootCtx
);
875 DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb
->NtfsInfo
.BytesPerIndexRecord
, IndexRoot
->SizeOfEntry
);
877 Status
= BrowseIndexEntries(Vcb
, MftRecord
, IndexRecord
, IndexRoot
->SizeOfEntry
, IndexEntry
, IndexEntryEnd
, FileName
, FirstEntry
, &CurrentEntry
, DirSearch
, OutMFTIndex
);
879 ExFreePoolWithTag(IndexRecord
, TAG_NTFS
);
880 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
886 NtfsLookupFileAt(PDEVICE_EXTENSION Vcb
,
887 PUNICODE_STRING PathName
,
888 PFILE_RECORD_HEADER
*FileRecord
,
890 ULONGLONG CurrentMFTIndex
)
892 UNICODE_STRING Current
, Remaining
;
894 ULONG FirstEntry
= 0;
896 DPRINT("NtfsLookupFileAt(%p, %wZ, %p, %I64x)\n", Vcb
, PathName
, FileRecord
, CurrentMFTIndex
);
898 FsRtlDissectName(*PathName
, &Current
, &Remaining
);
900 while (Current
.Length
!= 0)
902 DPRINT("Current: %wZ\n", &Current
);
904 Status
= NtfsFindMftRecord(Vcb
, CurrentMFTIndex
, &Current
, &FirstEntry
, FALSE
, &CurrentMFTIndex
);
905 if (!NT_SUCCESS(Status
))
910 if (Remaining
.Length
== 0)
913 FsRtlDissectName(Current
, &Current
, &Remaining
);
916 *FileRecord
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
917 if (*FileRecord
== NULL
)
919 DPRINT("NtfsLookupFileAt: Can't allocate MFT record\n");
920 return STATUS_INSUFFICIENT_RESOURCES
;
923 Status
= ReadFileRecord(Vcb
, CurrentMFTIndex
, *FileRecord
);
924 if (!NT_SUCCESS(Status
))
926 DPRINT("NtfsLookupFileAt: Can't read MFT record\n");
927 ExFreePoolWithTag(*FileRecord
, TAG_NTFS
);
931 *MFTIndex
= CurrentMFTIndex
;
933 return STATUS_SUCCESS
;
937 NtfsLookupFile(PDEVICE_EXTENSION Vcb
,
938 PUNICODE_STRING PathName
,
939 PFILE_RECORD_HEADER
*FileRecord
,
942 return NtfsLookupFileAt(Vcb
, PathName
, FileRecord
, MFTIndex
, NTFS_FILE_ROOT
);
946 NtfsFindFileAt(PDEVICE_EXTENSION Vcb
,
947 PUNICODE_STRING SearchPattern
,
949 PFILE_RECORD_HEADER
*FileRecord
,
951 ULONGLONG CurrentMFTIndex
)
955 DPRINT("NtfsFindFileAt(%p, %wZ, %u, %p, %p, %I64x)\n", Vcb
, SearchPattern
, *FirstEntry
, FileRecord
, MFTIndex
, CurrentMFTIndex
);
957 Status
= NtfsFindMftRecord(Vcb
, CurrentMFTIndex
, SearchPattern
, FirstEntry
, TRUE
, &CurrentMFTIndex
);
958 if (!NT_SUCCESS(Status
))
960 DPRINT("NtfsFindFileAt: NtfsFindMftRecord() failed with status 0x%08lx\n", Status
);
964 *FileRecord
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
965 if (*FileRecord
== NULL
)
967 DPRINT("NtfsFindFileAt: Can't allocate MFT record\n");
968 return STATUS_INSUFFICIENT_RESOURCES
;
971 Status
= ReadFileRecord(Vcb
, CurrentMFTIndex
, *FileRecord
);
972 if (!NT_SUCCESS(Status
))
974 DPRINT("NtfsFindFileAt: Can't read MFT record\n");
975 ExFreePoolWithTag(*FileRecord
, TAG_NTFS
);
979 *MFTIndex
= CurrentMFTIndex
;
981 return STATUS_SUCCESS
;