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
);
88 * Searches a file record for an attribute matching the given type and name.
91 * Optional pointer to a ULONG that will receive the offset of the found attribute
92 * from the beginning of the record. Can be set to NULL.
95 FindAttribute(PDEVICE_EXTENSION Vcb
,
96 PFILE_RECORD_HEADER MftRecord
,
100 PNTFS_ATTR_CONTEXT
* AttrCtx
,
105 FIND_ATTR_CONTXT Context
;
106 PNTFS_ATTR_RECORD Attribute
;
108 DPRINT("FindAttribute(%p, %p, 0x%x, %S, %u, %p)\n", Vcb
, MftRecord
, Type
, Name
, NameLength
, AttrCtx
);
111 Status
= FindFirstAttribute(&Context
, Vcb
, MftRecord
, FALSE
, &Attribute
);
112 while (NT_SUCCESS(Status
))
114 if (Attribute
->Type
== Type
&& Attribute
->NameLength
== NameLength
)
120 AttrName
= (PWCHAR
)((PCHAR
)Attribute
+ Attribute
->NameOffset
);
121 DPRINT("%.*S, %.*S\n", Attribute
->NameLength
, AttrName
, NameLength
, Name
);
122 if (RtlCompareMemory(AttrName
, Name
, NameLength
<< 1) == (NameLength
<< 1))
134 /* Found it, fill up the context and return. */
135 DPRINT("Found context\n");
136 *AttrCtx
= PrepareAttributeContext(Attribute
);
139 *Offset
= Context
.Offset
;
141 FindCloseAttribute(&Context
);
142 return STATUS_SUCCESS
;
146 Status
= FindNextAttribute(&Context
, &Attribute
);
149 FindCloseAttribute(&Context
);
150 return STATUS_OBJECT_NAME_NOT_FOUND
;
155 AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord
)
157 if (AttrRecord
->IsNonResident
)
158 return AttrRecord
->NonResident
.AllocatedSize
;
160 return AttrRecord
->Resident
.ValueLength
;
165 AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord
)
167 if (AttrRecord
->IsNonResident
)
168 return AttrRecord
->NonResident
.DataSize
;
170 return AttrRecord
->Resident
.ValueLength
;
175 SetAttributeDataLength(PFILE_OBJECT FileObject
,
177 PNTFS_ATTR_CONTEXT AttrContext
,
179 PFILE_RECORD_HEADER FileRecord
,
180 PLARGE_INTEGER DataSize
)
182 if (AttrContext
->Record
.IsNonResident
)
184 ULONG BytesPerCluster
= Fcb
->Vcb
->NtfsInfo
.BytesPerCluster
;
185 ULONGLONG AllocationSize
= ROUND_UP(DataSize
->QuadPart
, BytesPerCluster
);
187 // do we need to increase the allocation size?
188 if (AttrContext
->Record
.NonResident
.AllocatedSize
< AllocationSize
)
190 ULONG ExistingClusters
= AttrContext
->Record
.NonResident
.AllocatedSize
/ BytesPerCluster
;
191 ULONG ClustersNeeded
= (AllocationSize
/ BytesPerCluster
) - ExistingClusters
;
192 LARGE_INTEGER LastClusterInDataRun
;
193 ULONG NextAssignedCluster
;
194 ULONG AssignedClusters
;
196 NTSTATUS Status
= GetLastClusterInDataRun(Fcb
->Vcb
, &AttrContext
->Record
, &LastClusterInDataRun
.QuadPart
);
198 DPRINT1("GetLastClusterInDataRun returned: %I64u\n", LastClusterInDataRun
.QuadPart
);
199 DPRINT1("Highest VCN of record: %I64u\n", AttrContext
->Record
.NonResident
.HighestVCN
);
201 while (ClustersNeeded
> 0)
203 Status
= NtfsAllocateClusters(Fcb
->Vcb
,
204 LastClusterInDataRun
.LowPart
+ 1,
206 &NextAssignedCluster
,
209 if (!NT_SUCCESS(Status
))
211 DPRINT1("Error: Unable to allocate requested clusters!\n");
215 // now we need to add the clusters we allocated to the data run
216 Status
= AddRun(AttrContext
, NextAssignedCluster
, AssignedClusters
);
217 if (!NT_SUCCESS(Status
))
219 DPRINT1("Error: Unable to add data run!\n");
223 ClustersNeeded
-= AssignedClusters
;
224 LastClusterInDataRun
.LowPart
= NextAssignedCluster
+ AssignedClusters
- 1;
227 DPRINT1("FixMe: Increasing allocation size is unimplemented!\n");
228 return STATUS_NOT_IMPLEMENTED
;
231 // TODO: is the file compressed, encrypted, or sparse?
233 // NOTE: we need to have acquired the main resource exclusively, as well as(?) the PagingIoResource
235 // TODO: update the allocated size on-disk
236 DPRINT("Allocated Size: %I64u\n", AttrContext
->Record
.NonResident
.AllocatedSize
);
238 AttrContext
->Record
.NonResident
.DataSize
= DataSize
->QuadPart
;
239 AttrContext
->Record
.NonResident
.InitializedSize
= DataSize
->QuadPart
;
241 Fcb
->RFCB
.FileSize
= *DataSize
;
242 Fcb
->RFCB
.ValidDataLength
= *DataSize
;
244 DPRINT("Data Size: %I64u\n", Fcb
->RFCB
.FileSize
.QuadPart
);
246 //NtfsDumpFileAttributes(Fcb->Vcb, FileRecord);
248 // copy the attribute back into the FileRecord
249 RtlCopyMemory((PCHAR
)FileRecord
+ AttrOffset
, &AttrContext
->Record
, AttrContext
->Record
.Length
);
251 //NtfsDumpFileAttributes(Fcb->Vcb, FileRecord);
253 // write the updated file record back to disk
254 UpdateFileRecord(Fcb
->Vcb
, Fcb
->MFTIndex
, FileRecord
);
256 CcSetFileSizes(FileObject
, (PCC_FILE_SIZES
)&Fcb
->RFCB
.AllocationSize
);
260 // we can't yet handle resident attributes
261 DPRINT1("FixMe: Can't handle increasing length of resident attribute\n");
262 return STATUS_NOT_IMPLEMENTED
;
265 return STATUS_SUCCESS
;
269 ReadAttribute(PDEVICE_EXTENSION Vcb
,
270 PNTFS_ATTR_CONTEXT Context
,
277 LONGLONG DataRunOffset
;
278 ULONGLONG DataRunLength
;
279 LONGLONG DataRunStartLCN
;
280 ULONGLONG CurrentOffset
;
285 if (!Context
->Record
.IsNonResident
)
287 if (Offset
> Context
->Record
.Resident
.ValueLength
)
289 if (Offset
+ Length
> Context
->Record
.Resident
.ValueLength
)
290 Length
= (ULONG
)(Context
->Record
.Resident
.ValueLength
- Offset
);
291 RtlCopyMemory(Buffer
, (PCHAR
)&Context
->Record
+ Context
->Record
.Resident
.ValueOffset
+ Offset
, Length
);
296 * Non-resident attribute
300 * I. Find the corresponding start data run.
305 // FIXME: Cache seems to be non-working. Disable it for now
306 //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
309 DataRun
= Context
->CacheRun
;
310 LastLCN
= Context
->CacheRunLastLCN
;
311 DataRunStartLCN
= Context
->CacheRunStartLCN
;
312 DataRunLength
= Context
->CacheRunLength
;
313 CurrentOffset
= Context
->CacheRunCurrentOffset
;
318 DataRun
= (PUCHAR
)&Context
->Record
+ Context
->Record
.NonResident
.MappingPairsOffset
;
323 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
324 if (DataRunOffset
!= -1)
326 /* Normal data run. */
327 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
328 LastLCN
= DataRunStartLCN
;
332 /* Sparse data run. */
333 DataRunStartLCN
= -1;
336 if (Offset
>= CurrentOffset
&&
337 Offset
< CurrentOffset
+ (DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
))
347 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
352 * II. Go through the run list and read the data
355 ReadLength
= (ULONG
)min(DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
- (Offset
- CurrentOffset
), Length
);
356 if (DataRunStartLCN
== -1)
358 RtlZeroMemory(Buffer
, ReadLength
);
359 Status
= STATUS_SUCCESS
;
363 Status
= NtfsReadDisk(Vcb
->StorageDevice
,
364 DataRunStartLCN
* Vcb
->NtfsInfo
.BytesPerCluster
+ Offset
- CurrentOffset
,
366 Vcb
->NtfsInfo
.BytesPerSector
,
370 if (NT_SUCCESS(Status
))
372 Length
-= ReadLength
;
373 Buffer
+= ReadLength
;
374 AlreadyRead
+= ReadLength
;
376 if (ReadLength
== DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
- (Offset
- CurrentOffset
))
378 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
379 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
380 if (DataRunOffset
!= (ULONGLONG
)-1)
382 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
383 LastLCN
= DataRunStartLCN
;
386 DataRunStartLCN
= -1;
391 ReadLength
= (ULONG
)min(DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
, Length
);
392 if (DataRunStartLCN
== -1)
393 RtlZeroMemory(Buffer
, ReadLength
);
396 Status
= NtfsReadDisk(Vcb
->StorageDevice
,
397 DataRunStartLCN
* Vcb
->NtfsInfo
.BytesPerCluster
,
399 Vcb
->NtfsInfo
.BytesPerSector
,
402 if (!NT_SUCCESS(Status
))
406 Length
-= ReadLength
;
407 Buffer
+= ReadLength
;
408 AlreadyRead
+= ReadLength
;
410 /* We finished this request, but there still data in this data run. */
411 if (Length
== 0 && ReadLength
!= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
)
415 * Go to next run in the list.
420 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
421 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
422 if (DataRunOffset
!= -1)
424 /* Normal data run. */
425 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
426 LastLCN
= DataRunStartLCN
;
430 /* Sparse data run. */
431 DataRunStartLCN
= -1;
437 Context
->CacheRun
= DataRun
;
438 Context
->CacheRunOffset
= Offset
+ AlreadyRead
;
439 Context
->CacheRunStartLCN
= DataRunStartLCN
;
440 Context
->CacheRunLength
= DataRunLength
;
441 Context
->CacheRunLastLCN
= LastLCN
;
442 Context
->CacheRunCurrentOffset
= CurrentOffset
;
449 * @name WriteAttribute
452 * Writes an NTFS attribute to the disk. It presently borrows a lot of code from ReadAttribute(),
453 * and it still needs more documentation / cleaning up.
456 * Volume Control Block indicating which volume to write the attribute to
459 * Pointer to an NTFS_ATTR_CONTEXT that has information about the attribute
462 * Offset, in bytes, from the beginning of the attribute indicating where to start
466 * The data that's being written to the device
469 * How much data will be written, in bytes
471 * @param RealLengthWritten
472 * Pointer to a ULONG which will receive how much data was written, in bytes
475 * STATUS_SUCCESS if successful, an error code otherwise. STATUS_NOT_IMPLEMENTED if
476 * writing to a sparse file.
478 * @remarks Note that in this context the word "attribute" isn't referring read-only, hidden,
479 * etc. - the file's data is actually stored in an attribute in NTFS parlance.
484 WriteAttribute(PDEVICE_EXTENSION Vcb
,
485 PNTFS_ATTR_CONTEXT Context
,
489 PULONG RealLengthWritten
)
493 LONGLONG DataRunOffset
;
494 ULONGLONG DataRunLength
;
495 LONGLONG DataRunStartLCN
;
496 ULONGLONG CurrentOffset
;
499 PUCHAR SourceBuffer
= Buffer
;
500 LONGLONG StartingOffset
;
502 DPRINT("WriteAttribute(%p, %p, %I64u, %p, %lu, %p)\n", Vcb
, Context
, Offset
, Buffer
, Length
, RealLengthWritten
);
504 // is this a resident attribute?
505 if (!Context
->Record
.IsNonResident
)
507 DPRINT1("FIXME: Writing to resident NTFS records (small files) is not supported at this time.\n");
508 // (TODO: This should be really easy to implement)
510 /* LeftOver code from ReadAttribute(), may be helpful:
511 if (Offset > Context->Record.Resident.ValueLength)
513 if (Offset + Length > Context->Record.Resident.ValueLength)
514 Length = (ULONG)(Context->Record.Resident.ValueLength - Offset);
515 RtlCopyMemory(Buffer, (PCHAR)&Context->Record + Context->Record.Resident.ValueOffset + Offset, Length);
518 return STATUS_NOT_IMPLEMENTED
; // until we implement it
521 // This is a non-resident attribute.
523 // I. Find the corresponding start data run.
525 *RealLengthWritten
= 0;
527 // FIXME: Cache seems to be non-working. Disable it for now
528 //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
531 DataRun = Context->CacheRun;
532 LastLCN = Context->CacheRunLastLCN;
533 DataRunStartLCN = Context->CacheRunStartLCN;
534 DataRunLength = Context->CacheRunLength;
535 CurrentOffset = Context->CacheRunCurrentOffset;
540 DataRun
= (PUCHAR
)&Context
->Record
+ Context
->Record
.NonResident
.MappingPairsOffset
;
545 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
546 if (DataRunOffset
!= -1)
549 // DPRINT1("Writing to normal data run, LastLCN %I64u DataRunOffset %I64d\n", LastLCN, DataRunOffset);
550 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
551 LastLCN
= DataRunStartLCN
;
555 // Sparse data run. We can't support writing to sparse files yet
556 // (it may require increasing the allocation size).
557 DataRunStartLCN
= -1;
558 DPRINT1("FIXME: Writing to sparse files is not supported yet!\n");
559 return STATUS_NOT_IMPLEMENTED
;
562 // Have we reached the data run we're trying to write to?
563 if (Offset
>= CurrentOffset
&&
564 Offset
< CurrentOffset
+ (DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
))
571 // We reached the last assigned cluster
572 // TODO: assign new clusters to the end of the file.
573 // (Presently, this code will never be reached, the write should have already failed by now)
574 return STATUS_END_OF_FILE
;
577 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
581 // II. Go through the run list and write the data
583 /* REVIEWME -- As adapted from NtfsReadAttribute():
584 We seem to be making a special case for the first applicable data run, but I'm not sure why.
585 Does it have something to do with (not) caching? Is this strategy equally applicable to writing? */
587 WriteLength
= (ULONG
)min(DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
- (Offset
- CurrentOffset
), Length
);
589 StartingOffset
= DataRunStartLCN
* Vcb
->NtfsInfo
.BytesPerCluster
+ Offset
- CurrentOffset
;
591 // Write the data to the disk
592 Status
= NtfsWriteDisk(Vcb
->StorageDevice
,
595 Vcb
->NtfsInfo
.BytesPerSector
,
596 (PVOID
)SourceBuffer
);
598 // Did the write fail?
599 if (!NT_SUCCESS(Status
))
601 Context
->CacheRun
= DataRun
;
602 Context
->CacheRunOffset
= Offset
;
603 Context
->CacheRunStartLCN
= DataRunStartLCN
;
604 Context
->CacheRunLength
= DataRunLength
;
605 Context
->CacheRunLastLCN
= LastLCN
;
606 Context
->CacheRunCurrentOffset
= CurrentOffset
;
611 Length
-= WriteLength
;
612 SourceBuffer
+= WriteLength
;
613 *RealLengthWritten
+= WriteLength
;
615 // Did we write to the end of the data run?
616 if (WriteLength
== DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
- (Offset
- CurrentOffset
))
618 // Advance to the next data run
619 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
620 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
622 if (DataRunOffset
!= (ULONGLONG
)-1)
624 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
625 LastLCN
= DataRunStartLCN
;
628 DataRunStartLCN
= -1;
631 // Do we have more data to write?
634 // Make sure we don't write past the end of the current data run
635 WriteLength
= (ULONG
)min(DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
, Length
);
637 // Are we dealing with a sparse data run?
638 if (DataRunStartLCN
== -1)
640 DPRINT1("FIXME: Don't know how to write to sparse files yet! (DataRunStartLCN == -1)\n");
641 return STATUS_NOT_IMPLEMENTED
;
645 // write the data to the disk
646 Status
= NtfsWriteDisk(Vcb
->StorageDevice
,
647 DataRunStartLCN
* Vcb
->NtfsInfo
.BytesPerCluster
,
649 Vcb
->NtfsInfo
.BytesPerSector
,
650 (PVOID
)SourceBuffer
);
651 if (!NT_SUCCESS(Status
))
655 Length
-= WriteLength
;
656 SourceBuffer
+= WriteLength
;
657 *RealLengthWritten
+= WriteLength
;
659 // We finished this request, but there's still data in this data run.
660 if (Length
== 0 && WriteLength
!= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
)
663 // Go to next run in the list.
667 // that was the last run
670 // Failed sanity check.
671 DPRINT1("Encountered EOF before expected!\n");
672 return STATUS_END_OF_FILE
;
678 // Advance to the next data run
679 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
680 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
681 if (DataRunOffset
!= -1)
684 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
685 LastLCN
= DataRunStartLCN
;
690 DataRunStartLCN
= -1;
692 } // end while (Length > 0) [more data to write]
694 Context
->CacheRun
= DataRun
;
695 Context
->CacheRunOffset
= Offset
+ *RealLengthWritten
;
696 Context
->CacheRunStartLCN
= DataRunStartLCN
;
697 Context
->CacheRunLength
= DataRunLength
;
698 Context
->CacheRunLastLCN
= LastLCN
;
699 Context
->CacheRunCurrentOffset
= CurrentOffset
;
705 ReadFileRecord(PDEVICE_EXTENSION Vcb
,
707 PFILE_RECORD_HEADER file
)
711 DPRINT("ReadFileRecord(%p, %I64x, %p)\n", Vcb
, index
, file
);
713 BytesRead
= ReadAttribute(Vcb
, Vcb
->MFTContext
, index
* Vcb
->NtfsInfo
.BytesPerFileRecord
, (PCHAR
)file
, Vcb
->NtfsInfo
.BytesPerFileRecord
);
714 if (BytesRead
!= Vcb
->NtfsInfo
.BytesPerFileRecord
)
716 DPRINT1("ReadFileRecord failed: %I64u read, %u expected\n", BytesRead
, Vcb
->NtfsInfo
.BytesPerFileRecord
);
717 return STATUS_PARTIAL_COPY
;
720 /* Apply update sequence array fixups. */
721 return FixupUpdateSequenceArray(Vcb
, &file
->Ntfs
);
726 * Searches a file's parent directory (given the parent's index in the mft)
727 * for the given file. Upon finding an index entry for that file, updates
728 * Data Size and Allocated Size values in the $FILE_NAME attribute of that entry.
730 * (Most of this code was copied from NtfsFindMftRecord)
733 UpdateFileNameRecord(PDEVICE_EXTENSION Vcb
,
734 ULONGLONG ParentMFTIndex
,
735 PUNICODE_STRING FileName
,
737 ULONGLONG NewDataSize
,
738 ULONGLONG NewAllocationSize
)
740 PFILE_RECORD_HEADER MftRecord
;
741 PNTFS_ATTR_CONTEXT IndexRootCtx
;
742 PINDEX_ROOT_ATTRIBUTE IndexRoot
;
744 PINDEX_ENTRY_ATTRIBUTE IndexEntry
, IndexEntryEnd
;
746 ULONG CurrentEntry
= 0;
748 DPRINT("UpdateFileNameRecord(%p, %I64d, %wZ, %u, %I64u, %I64u)\n", Vcb
, ParentMFTIndex
, FileName
, DirSearch
, NewDataSize
, NewAllocationSize
);
750 MftRecord
= ExAllocatePoolWithTag(NonPagedPool
,
751 Vcb
->NtfsInfo
.BytesPerFileRecord
,
753 if (MftRecord
== NULL
)
755 return STATUS_INSUFFICIENT_RESOURCES
;
758 Status
= ReadFileRecord(Vcb
, ParentMFTIndex
, MftRecord
);
759 if (!NT_SUCCESS(Status
))
761 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
765 ASSERT(MftRecord
->Ntfs
.Type
== NRH_FILE_TYPE
);
766 Status
= FindAttribute(Vcb
, MftRecord
, AttributeIndexRoot
, L
"$I30", 4, &IndexRootCtx
, NULL
);
767 if (!NT_SUCCESS(Status
))
769 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
773 IndexRecord
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerIndexRecord
, TAG_NTFS
);
774 if (IndexRecord
== NULL
)
776 ReleaseAttributeContext(IndexRootCtx
);
777 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
778 return STATUS_INSUFFICIENT_RESOURCES
;
781 ReadAttribute(Vcb
, IndexRootCtx
, 0, IndexRecord
, Vcb
->NtfsInfo
.BytesPerIndexRecord
);
782 IndexRoot
= (PINDEX_ROOT_ATTRIBUTE
)IndexRecord
;
783 IndexEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((PCHAR
)&IndexRoot
->Header
+ IndexRoot
->Header
.FirstEntryOffset
);
784 // Index root is always resident.
785 IndexEntryEnd
= (PINDEX_ENTRY_ATTRIBUTE
)(IndexRecord
+ IndexRoot
->Header
.TotalSizeOfEntries
);
787 DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb
->NtfsInfo
.BytesPerIndexRecord
, IndexRoot
->SizeOfEntry
);
789 Status
= UpdateIndexEntryFileNameSize(Vcb
,
792 IndexRoot
->SizeOfEntry
,
802 ReleaseAttributeContext(IndexRootCtx
);
803 ExFreePoolWithTag(IndexRecord
, TAG_NTFS
);
804 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
810 * Recursively searches directory index and applies the size update to the $FILE_NAME attribute of the
811 * proper index entry.
812 * (Heavily based on BrowseIndexEntries)
815 UpdateIndexEntryFileNameSize(PDEVICE_EXTENSION Vcb
,
816 PFILE_RECORD_HEADER MftRecord
,
818 ULONG IndexBlockSize
,
819 PINDEX_ENTRY_ATTRIBUTE FirstEntry
,
820 PINDEX_ENTRY_ATTRIBUTE LastEntry
,
821 PUNICODE_STRING FileName
,
825 ULONGLONG NewDataSize
,
826 ULONGLONG NewAllocatedSize
)
830 PINDEX_ENTRY_ATTRIBUTE IndexEntry
;
831 PNTFS_ATTR_CONTEXT IndexAllocationCtx
;
832 ULONGLONG IndexAllocationSize
;
833 PINDEX_BUFFER IndexBuffer
;
835 DPRINT("UpdateIndexEntrySize(%p, %p, %p, %u, %p, %p, %wZ, %u, %u, %u, %I64u, %I64u)\n", Vcb
, MftRecord
, IndexRecord
, IndexBlockSize
, FirstEntry
, LastEntry
, FileName
, *StartEntry
, *CurrentEntry
, DirSearch
, NewDataSize
, NewAllocatedSize
);
837 // find the index entry responsible for the file we're trying to update
838 IndexEntry
= FirstEntry
;
839 while (IndexEntry
< LastEntry
&&
840 !(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_END
))
842 if ((IndexEntry
->Data
.Directory
.IndexedFile
& NTFS_MFT_MASK
) > 0x10 &&
843 *CurrentEntry
>= *StartEntry
&&
844 IndexEntry
->FileName
.NameType
!= NTFS_FILE_NAME_DOS
&&
845 CompareFileName(FileName
, IndexEntry
, DirSearch
))
847 *StartEntry
= *CurrentEntry
;
848 IndexEntry
->FileName
.DataSize
= NewDataSize
;
849 IndexEntry
->FileName
.AllocatedSize
= NewAllocatedSize
;
850 // indicate that the caller will still need to write the structure to the disk
851 return STATUS_PENDING
;
854 (*CurrentEntry
) += 1;
855 ASSERT(IndexEntry
->Length
>= sizeof(INDEX_ENTRY_ATTRIBUTE
));
856 IndexEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((PCHAR
)IndexEntry
+ IndexEntry
->Length
);
859 /* If we're already browsing a subnode */
860 if (IndexRecord
== NULL
)
862 return STATUS_OBJECT_PATH_NOT_FOUND
;
865 /* If there's no subnode */
866 if (!(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_NODE
))
868 return STATUS_OBJECT_PATH_NOT_FOUND
;
871 Status
= FindAttribute(Vcb
, MftRecord
, AttributeIndexAllocation
, L
"$I30", 4, &IndexAllocationCtx
, NULL
);
872 if (!NT_SUCCESS(Status
))
874 DPRINT("Corrupted filesystem!\n");
878 IndexAllocationSize
= AttributeDataLength(&IndexAllocationCtx
->Record
);
879 Status
= STATUS_OBJECT_PATH_NOT_FOUND
;
880 for (RecordOffset
= 0; RecordOffset
< IndexAllocationSize
; RecordOffset
+= IndexBlockSize
)
882 ReadAttribute(Vcb
, IndexAllocationCtx
, RecordOffset
, IndexRecord
, IndexBlockSize
);
883 Status
= FixupUpdateSequenceArray(Vcb
, &((PFILE_RECORD_HEADER
)IndexRecord
)->Ntfs
);
884 if (!NT_SUCCESS(Status
))
889 IndexBuffer
= (PINDEX_BUFFER
)IndexRecord
;
890 ASSERT(IndexBuffer
->Ntfs
.Type
== NRH_INDX_TYPE
);
891 ASSERT(IndexBuffer
->Header
.AllocatedSize
+ FIELD_OFFSET(INDEX_BUFFER
, Header
) == IndexBlockSize
);
892 FirstEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)&IndexBuffer
->Header
+ IndexBuffer
->Header
.FirstEntryOffset
);
893 LastEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)&IndexBuffer
->Header
+ IndexBuffer
->Header
.TotalSizeOfEntries
);
894 ASSERT(LastEntry
<= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)IndexBuffer
+ IndexBlockSize
));
896 Status
= UpdateIndexEntryFileNameSize(NULL
, NULL
, NULL
, 0, FirstEntry
, LastEntry
, FileName
, StartEntry
, CurrentEntry
, DirSearch
, NewDataSize
, NewAllocatedSize
);
897 if (Status
== STATUS_PENDING
)
899 // write the index record back to disk
902 // first we need to update the fixup values for the index block
903 Status
= AddFixupArray(Vcb
, &((PFILE_RECORD_HEADER
)IndexRecord
)->Ntfs
);
904 if (!NT_SUCCESS(Status
))
906 DPRINT1("Error: Failed to update fixup sequence array!\n");
910 Status
= WriteAttribute(Vcb
, IndexAllocationCtx
, RecordOffset
, (const PUCHAR
)IndexRecord
, IndexBlockSize
, &Written
);
911 if (!NT_SUCCESS(Status
))
913 DPRINT1("ERROR Performing write!\n");
917 Status
= STATUS_SUCCESS
;
920 if (NT_SUCCESS(Status
))
926 ReleaseAttributeContext(IndexAllocationCtx
);
933 * Writes a file record to the master file table, at a given index.
936 UpdateFileRecord(PDEVICE_EXTENSION Vcb
,
938 PFILE_RECORD_HEADER file
)
941 NTSTATUS Status
= STATUS_SUCCESS
;
943 DPRINT("UpdateFileRecord(%p, %I64x, %p)\n", Vcb
, index
, file
);
945 // Add the fixup array to prepare the data for writing to disk
946 AddFixupArray(Vcb
, &file
->Ntfs
);
948 // write the file record to the master file table
949 Status
= WriteAttribute(Vcb
, Vcb
->MFTContext
, index
* Vcb
->NtfsInfo
.BytesPerFileRecord
, (const PUCHAR
)file
, Vcb
->NtfsInfo
.BytesPerFileRecord
, &BytesWritten
);
951 // TODO: Update MFT mirror
953 if (!NT_SUCCESS(Status
))
955 DPRINT1("UpdateFileRecord failed: %I64u written, %u expected\n", BytesWritten
, Vcb
->NtfsInfo
.BytesPerFileRecord
);
963 FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb
,
964 PNTFS_RECORD_HEADER Record
)
971 USA
= (USHORT
*)((PCHAR
)Record
+ Record
->UsaOffset
);
972 USANumber
= *(USA
++);
973 USACount
= Record
->UsaCount
- 1; /* Exclude the USA Number. */
974 Block
= (USHORT
*)((PCHAR
)Record
+ Vcb
->NtfsInfo
.BytesPerSector
- 2);
976 DPRINT("FixupUpdateSequenceArray(%p, %p)\nUSANumber: %u\tUSACount: %u\n", Vcb
, Record
, USANumber
, USACount
);
980 if (*Block
!= USANumber
)
982 DPRINT1("Mismatch with USA: %u read, %u expected\n" , *Block
, USANumber
);
983 return STATUS_UNSUCCESSFUL
;
986 Block
= (USHORT
*)((PCHAR
)Block
+ Vcb
->NtfsInfo
.BytesPerSector
);
990 return STATUS_SUCCESS
;
994 AddFixupArray(PDEVICE_EXTENSION Vcb
,
995 PNTFS_RECORD_HEADER Record
)
997 USHORT
*pShortToFixUp
;
998 unsigned int ArrayEntryCount
= Record
->UsaCount
- 1;
999 unsigned int Offset
= Vcb
->NtfsInfo
.BytesPerSector
- 2;
1002 PFIXUP_ARRAY fixupArray
= (PFIXUP_ARRAY
)((UCHAR
*)Record
+ Record
->UsaOffset
);
1004 DPRINT("AddFixupArray(%p, %p)\n fixupArray->USN: %u, ArrayEntryCount: %u\n", Vcb
, Record
, fixupArray
->USN
, ArrayEntryCount
);
1008 for (i
= 0; i
< ArrayEntryCount
; i
++)
1010 DPRINT("USN: %u\tOffset: %u\n", fixupArray
->USN
, Offset
);
1012 pShortToFixUp
= (USHORT
*)((PCHAR
)Record
+ Offset
);
1013 fixupArray
->Array
[i
] = *pShortToFixUp
;
1014 *pShortToFixUp
= fixupArray
->USN
;
1015 Offset
+= Vcb
->NtfsInfo
.BytesPerSector
;
1018 return STATUS_SUCCESS
;
1022 ReadLCN(PDEVICE_EXTENSION Vcb
,
1027 LARGE_INTEGER DiskSector
;
1029 DiskSector
.QuadPart
= lcn
;
1031 return NtfsReadSectors(Vcb
->StorageDevice
,
1032 DiskSector
.u
.LowPart
* Vcb
->NtfsInfo
.SectorsPerCluster
,
1033 count
* Vcb
->NtfsInfo
.SectorsPerCluster
,
1034 Vcb
->NtfsInfo
.BytesPerSector
,
1041 CompareFileName(PUNICODE_STRING FileName
,
1042 PINDEX_ENTRY_ATTRIBUTE IndexEntry
,
1045 BOOLEAN Ret
, Alloc
= FALSE
;
1046 UNICODE_STRING EntryName
;
1048 EntryName
.Buffer
= IndexEntry
->FileName
.Name
;
1050 EntryName
.MaximumLength
= IndexEntry
->FileName
.NameLength
* sizeof(WCHAR
);
1054 UNICODE_STRING IntFileName
;
1055 if (IndexEntry
->FileName
.NameType
!= NTFS_FILE_NAME_POSIX
)
1057 NT_VERIFY(NT_SUCCESS(RtlUpcaseUnicodeString(&IntFileName
, FileName
, TRUE
)));
1062 IntFileName
= *FileName
;
1065 Ret
= FsRtlIsNameInExpression(&IntFileName
, &EntryName
, (IndexEntry
->FileName
.NameType
!= NTFS_FILE_NAME_POSIX
), NULL
);
1069 RtlFreeUnicodeString(&IntFileName
);
1076 return (RtlCompareUnicodeString(FileName
, &EntryName
, (IndexEntry
->FileName
.NameType
!= NTFS_FILE_NAME_POSIX
)) == 0);
1083 DumpIndexEntry(PINDEX_ENTRY_ATTRIBUTE IndexEntry
)
1085 DPRINT1("Entry: %p\n", IndexEntry
);
1086 DPRINT1("\tData.Directory.IndexedFile: %I64x\n", IndexEntry
->Data
.Directory
.IndexedFile
);
1087 DPRINT1("\tLength: %u\n", IndexEntry
->Length
);
1088 DPRINT1("\tKeyLength: %u\n", IndexEntry
->KeyLength
);
1089 DPRINT1("\tFlags: %x\n", IndexEntry
->Flags
);
1090 DPRINT1("\tReserved: %x\n", IndexEntry
->Reserved
);
1091 DPRINT1("\t\tDirectoryFileReferenceNumber: %I64x\n", IndexEntry
->FileName
.DirectoryFileReferenceNumber
);
1092 DPRINT1("\t\tCreationTime: %I64u\n", IndexEntry
->FileName
.CreationTime
);
1093 DPRINT1("\t\tChangeTime: %I64u\n", IndexEntry
->FileName
.ChangeTime
);
1094 DPRINT1("\t\tLastWriteTime: %I64u\n", IndexEntry
->FileName
.LastWriteTime
);
1095 DPRINT1("\t\tLastAccessTime: %I64u\n", IndexEntry
->FileName
.LastAccessTime
);
1096 DPRINT1("\t\tAllocatedSize: %I64u\n", IndexEntry
->FileName
.AllocatedSize
);
1097 DPRINT1("\t\tDataSize: %I64u\n", IndexEntry
->FileName
.DataSize
);
1098 DPRINT1("\t\tFileAttributes: %x\n", IndexEntry
->FileName
.FileAttributes
);
1099 DPRINT1("\t\tNameLength: %u\n", IndexEntry
->FileName
.NameLength
);
1100 DPRINT1("\t\tNameType: %x\n", IndexEntry
->FileName
.NameType
);
1101 DPRINT1("\t\tName: %.*S\n", IndexEntry
->FileName
.NameLength
, IndexEntry
->FileName
.Name
);
1106 BrowseIndexEntries(PDEVICE_EXTENSION Vcb
,
1107 PFILE_RECORD_HEADER MftRecord
,
1109 ULONG IndexBlockSize
,
1110 PINDEX_ENTRY_ATTRIBUTE FirstEntry
,
1111 PINDEX_ENTRY_ATTRIBUTE LastEntry
,
1112 PUNICODE_STRING FileName
,
1114 PULONG CurrentEntry
,
1116 ULONGLONG
*OutMFTIndex
)
1120 PINDEX_ENTRY_ATTRIBUTE IndexEntry
;
1121 PNTFS_ATTR_CONTEXT IndexAllocationCtx
;
1122 ULONGLONG IndexAllocationSize
;
1123 PINDEX_BUFFER IndexBuffer
;
1125 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
);
1127 IndexEntry
= FirstEntry
;
1128 while (IndexEntry
< LastEntry
&&
1129 !(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_END
))
1131 if ((IndexEntry
->Data
.Directory
.IndexedFile
& NTFS_MFT_MASK
) > 0x10 &&
1132 *CurrentEntry
>= *StartEntry
&&
1133 IndexEntry
->FileName
.NameType
!= NTFS_FILE_NAME_DOS
&&
1134 CompareFileName(FileName
, IndexEntry
, DirSearch
))
1136 *StartEntry
= *CurrentEntry
;
1137 *OutMFTIndex
= (IndexEntry
->Data
.Directory
.IndexedFile
& NTFS_MFT_MASK
);
1138 return STATUS_SUCCESS
;
1141 (*CurrentEntry
) += 1;
1142 ASSERT(IndexEntry
->Length
>= sizeof(INDEX_ENTRY_ATTRIBUTE
));
1143 IndexEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((PCHAR
)IndexEntry
+ IndexEntry
->Length
);
1146 /* If we're already browsing a subnode */
1147 if (IndexRecord
== NULL
)
1149 return STATUS_OBJECT_PATH_NOT_FOUND
;
1152 /* If there's no subnode */
1153 if (!(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_NODE
))
1155 return STATUS_OBJECT_PATH_NOT_FOUND
;
1158 Status
= FindAttribute(Vcb
, MftRecord
, AttributeIndexAllocation
, L
"$I30", 4, &IndexAllocationCtx
, NULL
);
1159 if (!NT_SUCCESS(Status
))
1161 DPRINT("Corrupted filesystem!\n");
1165 IndexAllocationSize
= AttributeDataLength(&IndexAllocationCtx
->Record
);
1166 Status
= STATUS_OBJECT_PATH_NOT_FOUND
;
1167 for (RecordOffset
= 0; RecordOffset
< IndexAllocationSize
; RecordOffset
+= IndexBlockSize
)
1169 ReadAttribute(Vcb
, IndexAllocationCtx
, RecordOffset
, IndexRecord
, IndexBlockSize
);
1170 Status
= FixupUpdateSequenceArray(Vcb
, &((PFILE_RECORD_HEADER
)IndexRecord
)->Ntfs
);
1171 if (!NT_SUCCESS(Status
))
1176 IndexBuffer
= (PINDEX_BUFFER
)IndexRecord
;
1177 ASSERT(IndexBuffer
->Ntfs
.Type
== NRH_INDX_TYPE
);
1178 ASSERT(IndexBuffer
->Header
.AllocatedSize
+ FIELD_OFFSET(INDEX_BUFFER
, Header
) == IndexBlockSize
);
1179 FirstEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)&IndexBuffer
->Header
+ IndexBuffer
->Header
.FirstEntryOffset
);
1180 LastEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)&IndexBuffer
->Header
+ IndexBuffer
->Header
.TotalSizeOfEntries
);
1181 ASSERT(LastEntry
<= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)IndexBuffer
+ IndexBlockSize
));
1183 Status
= BrowseIndexEntries(NULL
, NULL
, NULL
, 0, FirstEntry
, LastEntry
, FileName
, StartEntry
, CurrentEntry
, DirSearch
, OutMFTIndex
);
1184 if (NT_SUCCESS(Status
))
1190 ReleaseAttributeContext(IndexAllocationCtx
);
1195 NtfsFindMftRecord(PDEVICE_EXTENSION Vcb
,
1197 PUNICODE_STRING FileName
,
1200 ULONGLONG
*OutMFTIndex
)
1202 PFILE_RECORD_HEADER MftRecord
;
1203 PNTFS_ATTR_CONTEXT IndexRootCtx
;
1204 PINDEX_ROOT_ATTRIBUTE IndexRoot
;
1206 PINDEX_ENTRY_ATTRIBUTE IndexEntry
, IndexEntryEnd
;
1208 ULONG CurrentEntry
= 0;
1210 DPRINT("NtfsFindMftRecord(%p, %I64d, %wZ, %u, %u, %p)\n", Vcb
, MFTIndex
, FileName
, *FirstEntry
, DirSearch
, OutMFTIndex
);
1212 MftRecord
= ExAllocatePoolWithTag(NonPagedPool
,
1213 Vcb
->NtfsInfo
.BytesPerFileRecord
,
1215 if (MftRecord
== NULL
)
1217 return STATUS_INSUFFICIENT_RESOURCES
;
1220 Status
= ReadFileRecord(Vcb
, MFTIndex
, MftRecord
);
1221 if (!NT_SUCCESS(Status
))
1223 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
1227 ASSERT(MftRecord
->Ntfs
.Type
== NRH_FILE_TYPE
);
1228 Status
= FindAttribute(Vcb
, MftRecord
, AttributeIndexRoot
, L
"$I30", 4, &IndexRootCtx
, NULL
);
1229 if (!NT_SUCCESS(Status
))
1231 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
1235 IndexRecord
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerIndexRecord
, TAG_NTFS
);
1236 if (IndexRecord
== NULL
)
1238 ReleaseAttributeContext(IndexRootCtx
);
1239 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
1240 return STATUS_INSUFFICIENT_RESOURCES
;
1243 ReadAttribute(Vcb
, IndexRootCtx
, 0, IndexRecord
, Vcb
->NtfsInfo
.BytesPerIndexRecord
);
1244 IndexRoot
= (PINDEX_ROOT_ATTRIBUTE
)IndexRecord
;
1245 IndexEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((PCHAR
)&IndexRoot
->Header
+ IndexRoot
->Header
.FirstEntryOffset
);
1246 /* Index root is always resident. */
1247 IndexEntryEnd
= (PINDEX_ENTRY_ATTRIBUTE
)(IndexRecord
+ IndexRoot
->Header
.TotalSizeOfEntries
);
1248 ReleaseAttributeContext(IndexRootCtx
);
1250 DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb
->NtfsInfo
.BytesPerIndexRecord
, IndexRoot
->SizeOfEntry
);
1252 Status
= BrowseIndexEntries(Vcb
, MftRecord
, IndexRecord
, IndexRoot
->SizeOfEntry
, IndexEntry
, IndexEntryEnd
, FileName
, FirstEntry
, &CurrentEntry
, DirSearch
, OutMFTIndex
);
1254 ExFreePoolWithTag(IndexRecord
, TAG_NTFS
);
1255 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
1261 NtfsLookupFileAt(PDEVICE_EXTENSION Vcb
,
1262 PUNICODE_STRING PathName
,
1263 PFILE_RECORD_HEADER
*FileRecord
,
1264 PULONGLONG MFTIndex
,
1265 ULONGLONG CurrentMFTIndex
)
1267 UNICODE_STRING Current
, Remaining
;
1269 ULONG FirstEntry
= 0;
1271 DPRINT("NtfsLookupFileAt(%p, %wZ, %p, %I64x)\n", Vcb
, PathName
, FileRecord
, CurrentMFTIndex
);
1273 FsRtlDissectName(*PathName
, &Current
, &Remaining
);
1275 while (Current
.Length
!= 0)
1277 DPRINT("Current: %wZ\n", &Current
);
1279 Status
= NtfsFindMftRecord(Vcb
, CurrentMFTIndex
, &Current
, &FirstEntry
, FALSE
, &CurrentMFTIndex
);
1280 if (!NT_SUCCESS(Status
))
1285 if (Remaining
.Length
== 0)
1288 FsRtlDissectName(Current
, &Current
, &Remaining
);
1291 *FileRecord
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
1292 if (*FileRecord
== NULL
)
1294 DPRINT("NtfsLookupFileAt: Can't allocate MFT record\n");
1295 return STATUS_INSUFFICIENT_RESOURCES
;
1298 Status
= ReadFileRecord(Vcb
, CurrentMFTIndex
, *FileRecord
);
1299 if (!NT_SUCCESS(Status
))
1301 DPRINT("NtfsLookupFileAt: Can't read MFT record\n");
1302 ExFreePoolWithTag(*FileRecord
, TAG_NTFS
);
1306 *MFTIndex
= CurrentMFTIndex
;
1308 return STATUS_SUCCESS
;
1312 NtfsLookupFile(PDEVICE_EXTENSION Vcb
,
1313 PUNICODE_STRING PathName
,
1314 PFILE_RECORD_HEADER
*FileRecord
,
1315 PULONGLONG MFTIndex
)
1317 return NtfsLookupFileAt(Vcb
, PathName
, FileRecord
, MFTIndex
, NTFS_FILE_ROOT
);
1321 NtfsFindFileAt(PDEVICE_EXTENSION Vcb
,
1322 PUNICODE_STRING SearchPattern
,
1324 PFILE_RECORD_HEADER
*FileRecord
,
1325 PULONGLONG MFTIndex
,
1326 ULONGLONG CurrentMFTIndex
)
1330 DPRINT("NtfsFindFileAt(%p, %wZ, %u, %p, %p, %I64x)\n", Vcb
, SearchPattern
, *FirstEntry
, FileRecord
, MFTIndex
, CurrentMFTIndex
);
1332 Status
= NtfsFindMftRecord(Vcb
, CurrentMFTIndex
, SearchPattern
, FirstEntry
, TRUE
, &CurrentMFTIndex
);
1333 if (!NT_SUCCESS(Status
))
1335 DPRINT("NtfsFindFileAt: NtfsFindMftRecord() failed with status 0x%08lx\n", Status
);
1339 *FileRecord
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
1340 if (*FileRecord
== NULL
)
1342 DPRINT("NtfsFindFileAt: Can't allocate MFT record\n");
1343 return STATUS_INSUFFICIENT_RESOURCES
;
1346 Status
= ReadFileRecord(Vcb
, CurrentMFTIndex
, *FileRecord
);
1347 if (!NT_SUCCESS(Status
))
1349 DPRINT("NtfsFindFileAt: Can't read MFT record\n");
1350 ExFreePoolWithTag(*FileRecord
, TAG_NTFS
);
1354 *MFTIndex
= CurrentMFTIndex
;
1356 return STATUS_SUCCESS
;