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
);
138 (*AttrCtx
)->FileMFTIndex
= MftRecord
->MFTRecordNumber
;
141 *Offset
= Context
.Offset
;
143 FindCloseAttribute(&Context
);
144 return STATUS_SUCCESS
;
148 Status
= FindNextAttribute(&Context
, &Attribute
);
151 FindCloseAttribute(&Context
);
152 return STATUS_OBJECT_NAME_NOT_FOUND
;
157 AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord
)
159 if (AttrRecord
->IsNonResident
)
160 return AttrRecord
->NonResident
.AllocatedSize
;
162 return AttrRecord
->Resident
.ValueLength
;
167 AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord
)
169 if (AttrRecord
->IsNonResident
)
170 return AttrRecord
->NonResident
.DataSize
;
172 return AttrRecord
->Resident
.ValueLength
;
176 InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext
,
177 PFILE_RECORD_HEADER FileRecord
,
181 PNTFS_ATTR_RECORD Destination
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ AttrOffset
);
182 ULONG NextAttributeOffset
;
184 DPRINT("InternalSetResidentAttributeLength( %p, %p, %lu, %lu )\n", AttrContext
, FileRecord
, AttrOffset
, DataSize
);
186 // update ValueLength Field
187 AttrContext
->Record
.Resident
.ValueLength
=
188 Destination
->Resident
.ValueLength
= DataSize
;
190 // calculate the record length and end marker offset
191 AttrContext
->Record
.Length
=
192 Destination
->Length
= DataSize
+ AttrContext
->Record
.Resident
.ValueOffset
;
193 NextAttributeOffset
= AttrOffset
+ AttrContext
->Record
.Length
;
195 // Ensure NextAttributeOffset is aligned to an 8-byte boundary
196 if (NextAttributeOffset
% 8 != 0)
198 USHORT Padding
= 8 - (NextAttributeOffset
% 8);
199 NextAttributeOffset
+= Padding
;
200 AttrContext
->Record
.Length
+= Padding
;
201 Destination
->Length
+= Padding
;
204 // advance Destination to the final "attribute" and write the end type
205 Destination
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)Destination
+ Destination
->Length
);
206 Destination
->Type
= AttributeEnd
;
208 // write the final marker (which shares the same offset and type as the Length field)
209 Destination
->Length
= FILE_RECORD_END
;
211 FileRecord
->BytesInUse
= NextAttributeOffset
+ (sizeof(ULONG
) * 2);
215 SetAttributeDataLength(PFILE_OBJECT FileObject
,
217 PNTFS_ATTR_CONTEXT AttrContext
,
219 PFILE_RECORD_HEADER FileRecord
,
220 PLARGE_INTEGER DataSize
)
222 NTSTATUS Status
= STATUS_SUCCESS
;
224 // are we truncating the file?
225 if (DataSize
->QuadPart
< AttributeDataLength(&AttrContext
->Record
))
227 if (!MmCanFileBeTruncated(FileObject
->SectionObjectPointer
, DataSize
))
229 DPRINT1("Can't truncate a memory-mapped file!\n");
230 return STATUS_USER_MAPPED_FILE
;
234 if (AttrContext
->Record
.IsNonResident
)
236 ULONG BytesPerCluster
= Fcb
->Vcb
->NtfsInfo
.BytesPerCluster
;
237 ULONGLONG AllocationSize
= ROUND_UP(DataSize
->QuadPart
, BytesPerCluster
);
239 // do we need to increase the allocation size?
240 if (AttrContext
->Record
.NonResident
.AllocatedSize
< AllocationSize
)
242 ULONG ExistingClusters
= AttrContext
->Record
.NonResident
.AllocatedSize
/ BytesPerCluster
;
243 ULONG ClustersNeeded
= (AllocationSize
/ BytesPerCluster
) - ExistingClusters
;
244 LARGE_INTEGER LastClusterInDataRun
;
245 ULONG NextAssignedCluster
;
246 ULONG AssignedClusters
;
248 NTSTATUS Status
= GetLastClusterInDataRun(Fcb
->Vcb
, &AttrContext
->Record
, (PULONGLONG
)&LastClusterInDataRun
.QuadPart
);
250 DPRINT1("GetLastClusterInDataRun returned: %I64u\n", LastClusterInDataRun
.QuadPart
);
251 DPRINT1("Highest VCN of record: %I64u\n", AttrContext
->Record
.NonResident
.HighestVCN
);
253 while (ClustersNeeded
> 0)
255 Status
= NtfsAllocateClusters(Fcb
->Vcb
,
256 LastClusterInDataRun
.LowPart
+ 1,
258 &NextAssignedCluster
,
261 if (!NT_SUCCESS(Status
))
263 DPRINT1("Error: Unable to allocate requested clusters!\n");
267 // now we need to add the clusters we allocated to the data run
268 Status
= AddRun(AttrContext
, NextAssignedCluster
, AssignedClusters
);
269 if (!NT_SUCCESS(Status
))
271 DPRINT1("Error: Unable to add data run!\n");
275 ClustersNeeded
-= AssignedClusters
;
276 LastClusterInDataRun
.LowPart
= NextAssignedCluster
+ AssignedClusters
- 1;
279 DPRINT1("FixMe: Increasing allocation size is unimplemented!\n");
280 return STATUS_NOT_IMPLEMENTED
;
283 // TODO: is the file compressed, encrypted, or sparse?
285 // NOTE: we need to have acquired the main resource exclusively, as well as(?) the PagingIoResource
287 // TODO: update the allocated size on-disk
288 DPRINT("Allocated Size: %I64u\n", AttrContext
->Record
.NonResident
.AllocatedSize
);
290 AttrContext
->Record
.NonResident
.DataSize
= DataSize
->QuadPart
;
291 AttrContext
->Record
.NonResident
.InitializedSize
= DataSize
->QuadPart
;
293 // copy the attribute record back into the FileRecord
294 RtlCopyMemory((PCHAR
)FileRecord
+ AttrOffset
, &AttrContext
->Record
, AttrContext
->Record
.Length
);
298 // resident attribute
300 // find the next attribute
301 ULONG NextAttributeOffset
= AttrOffset
+ AttrContext
->Record
.Length
;
302 PNTFS_ATTR_RECORD NextAttribute
= (PNTFS_ATTR_RECORD
)((PCHAR
)FileRecord
+ NextAttributeOffset
);
304 //NtfsDumpFileAttributes(Fcb->Vcb, FileRecord);
306 // Do we need to increase the data length?
307 if (DataSize
->QuadPart
> AttrContext
->Record
.Resident
.ValueLength
)
309 // There's usually padding at the end of a record. Do we need to extend past it?
310 ULONG MaxValueLength
= AttrContext
->Record
.Length
- AttrContext
->Record
.Resident
.ValueOffset
;
311 if (MaxValueLength
< DataSize
->LowPart
)
313 // If this is the last attribute, we could move the end marker to the very end of the file record
314 MaxValueLength
+= Fcb
->Vcb
->NtfsInfo
.BytesPerFileRecord
- NextAttributeOffset
- (sizeof(ULONG
) * 2);
316 if (MaxValueLength
< DataSize
->LowPart
|| NextAttribute
->Type
!= AttributeEnd
)
318 DPRINT1("FIXME: Need to convert attribute to non-resident!\n");
319 return STATUS_NOT_IMPLEMENTED
;
323 else if (DataSize
->LowPart
< AttrContext
->Record
.Resident
.ValueLength
)
325 // we need to decrease the length
326 if (NextAttribute
->Type
!= AttributeEnd
)
328 DPRINT1("FIXME: Don't know how to decrease length of resident attribute unless it's the final attribute!\n");
329 return STATUS_NOT_IMPLEMENTED
;
333 InternalSetResidentAttributeLength(AttrContext
, FileRecord
, AttrOffset
, DataSize
->LowPart
);
336 //NtfsDumpFileAttributes(Fcb->Vcb, FileRecord);
338 // write the updated file record back to disk
339 Status
= UpdateFileRecord(Fcb
->Vcb
, Fcb
->MFTIndex
, FileRecord
);
341 if (NT_SUCCESS(Status
))
343 Fcb
->RFCB
.FileSize
= *DataSize
;
344 Fcb
->RFCB
.ValidDataLength
= *DataSize
;
345 CcSetFileSizes(FileObject
, (PCC_FILE_SIZES
)&Fcb
->RFCB
.AllocationSize
);
348 return STATUS_SUCCESS
;
352 ReadAttribute(PDEVICE_EXTENSION Vcb
,
353 PNTFS_ATTR_CONTEXT Context
,
360 LONGLONG DataRunOffset
;
361 ULONGLONG DataRunLength
;
362 LONGLONG DataRunStartLCN
;
363 ULONGLONG CurrentOffset
;
368 if (!Context
->Record
.IsNonResident
)
370 if (Offset
> Context
->Record
.Resident
.ValueLength
)
372 if (Offset
+ Length
> Context
->Record
.Resident
.ValueLength
)
373 Length
= (ULONG
)(Context
->Record
.Resident
.ValueLength
- Offset
);
374 RtlCopyMemory(Buffer
, (PCHAR
)&Context
->Record
+ Context
->Record
.Resident
.ValueOffset
+ Offset
, Length
);
379 * Non-resident attribute
383 * I. Find the corresponding start data run.
388 // FIXME: Cache seems to be non-working. Disable it for now
389 //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
392 DataRun
= Context
->CacheRun
;
393 LastLCN
= Context
->CacheRunLastLCN
;
394 DataRunStartLCN
= Context
->CacheRunStartLCN
;
395 DataRunLength
= Context
->CacheRunLength
;
396 CurrentOffset
= Context
->CacheRunCurrentOffset
;
401 DataRun
= (PUCHAR
)&Context
->Record
+ Context
->Record
.NonResident
.MappingPairsOffset
;
406 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
407 if (DataRunOffset
!= -1)
409 /* Normal data run. */
410 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
411 LastLCN
= DataRunStartLCN
;
415 /* Sparse data run. */
416 DataRunStartLCN
= -1;
419 if (Offset
>= CurrentOffset
&&
420 Offset
< CurrentOffset
+ (DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
))
430 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
435 * II. Go through the run list and read the data
438 ReadLength
= (ULONG
)min(DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
- (Offset
- CurrentOffset
), Length
);
439 if (DataRunStartLCN
== -1)
441 RtlZeroMemory(Buffer
, ReadLength
);
442 Status
= STATUS_SUCCESS
;
446 Status
= NtfsReadDisk(Vcb
->StorageDevice
,
447 DataRunStartLCN
* Vcb
->NtfsInfo
.BytesPerCluster
+ Offset
- CurrentOffset
,
449 Vcb
->NtfsInfo
.BytesPerSector
,
453 if (NT_SUCCESS(Status
))
455 Length
-= ReadLength
;
456 Buffer
+= ReadLength
;
457 AlreadyRead
+= ReadLength
;
459 if (ReadLength
== DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
- (Offset
- CurrentOffset
))
461 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
462 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
463 if (DataRunOffset
!= (ULONGLONG
)-1)
465 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
466 LastLCN
= DataRunStartLCN
;
469 DataRunStartLCN
= -1;
474 ReadLength
= (ULONG
)min(DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
, Length
);
475 if (DataRunStartLCN
== -1)
476 RtlZeroMemory(Buffer
, ReadLength
);
479 Status
= NtfsReadDisk(Vcb
->StorageDevice
,
480 DataRunStartLCN
* Vcb
->NtfsInfo
.BytesPerCluster
,
482 Vcb
->NtfsInfo
.BytesPerSector
,
485 if (!NT_SUCCESS(Status
))
489 Length
-= ReadLength
;
490 Buffer
+= ReadLength
;
491 AlreadyRead
+= ReadLength
;
493 /* We finished this request, but there still data in this data run. */
494 if (Length
== 0 && ReadLength
!= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
)
498 * Go to next run in the list.
503 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
504 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
505 if (DataRunOffset
!= -1)
507 /* Normal data run. */
508 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
509 LastLCN
= DataRunStartLCN
;
513 /* Sparse data run. */
514 DataRunStartLCN
= -1;
520 Context
->CacheRun
= DataRun
;
521 Context
->CacheRunOffset
= Offset
+ AlreadyRead
;
522 Context
->CacheRunStartLCN
= DataRunStartLCN
;
523 Context
->CacheRunLength
= DataRunLength
;
524 Context
->CacheRunLastLCN
= LastLCN
;
525 Context
->CacheRunCurrentOffset
= CurrentOffset
;
532 * @name WriteAttribute
535 * Writes an NTFS attribute to the disk. It presently borrows a lot of code from ReadAttribute(),
536 * and it still needs more documentation / cleaning up.
539 * Volume Control Block indicating which volume to write the attribute to
542 * Pointer to an NTFS_ATTR_CONTEXT that has information about the attribute
545 * Offset, in bytes, from the beginning of the attribute indicating where to start
549 * The data that's being written to the device
552 * How much data will be written, in bytes
554 * @param RealLengthWritten
555 * Pointer to a ULONG which will receive how much data was written, in bytes
558 * STATUS_SUCCESS if successful, an error code otherwise. STATUS_NOT_IMPLEMENTED if
559 * writing to a sparse file.
561 * @remarks Note that in this context the word "attribute" isn't referring read-only, hidden,
562 * etc. - the file's data is actually stored in an attribute in NTFS parlance.
567 WriteAttribute(PDEVICE_EXTENSION Vcb
,
568 PNTFS_ATTR_CONTEXT Context
,
572 PULONG RealLengthWritten
)
576 LONGLONG DataRunOffset
;
577 ULONGLONG DataRunLength
;
578 LONGLONG DataRunStartLCN
;
579 ULONGLONG CurrentOffset
;
582 PUCHAR SourceBuffer
= Buffer
;
583 LONGLONG StartingOffset
;
585 DPRINT("WriteAttribute(%p, %p, %I64u, %p, %lu, %p)\n", Vcb
, Context
, Offset
, Buffer
, Length
, RealLengthWritten
);
587 *RealLengthWritten
= 0;
589 // is this a resident attribute?
590 if (!Context
->Record
.IsNonResident
)
592 ULONG AttributeOffset
;
593 PNTFS_ATTR_CONTEXT FoundContext
;
594 PFILE_RECORD_HEADER FileRecord
;
596 if (Offset
+ Length
> Context
->Record
.Resident
.ValueLength
)
598 DPRINT1("DRIVER ERROR: Attribute is too small!\n");
599 return STATUS_INVALID_PARAMETER
;
602 FileRecord
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
606 DPRINT1("Error: Couldn't allocate file record!\n");
607 return STATUS_NO_MEMORY
;
610 // read the file record
611 ReadFileRecord(Vcb
, Context
->FileMFTIndex
, FileRecord
);
613 // find where to write the attribute data to
614 Status
= FindAttribute(Vcb
, FileRecord
,
615 Context
->Record
.Type
,
616 (PCWSTR
)((PCHAR
)&Context
->Record
+ Context
->Record
.NameOffset
),
617 Context
->Record
.NameLength
,
621 if (!NT_SUCCESS(Status
))
623 DPRINT1("ERROR: Couldn't find matching attribute!\n");
624 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
628 DPRINT("Offset: %I64u, AttributeOffset: %u, ValueOffset: %u\n", Offset
, AttributeOffset
, Context
->Record
.Resident
.ValueLength
);
629 Offset
+= AttributeOffset
+ Context
->Record
.Resident
.ValueOffset
;
631 if (Offset
+ Length
> Vcb
->NtfsInfo
.BytesPerFileRecord
)
633 DPRINT1("DRIVER ERROR: Data being written extends past end of file record!\n");
634 ReleaseAttributeContext(FoundContext
);
635 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
636 return STATUS_INVALID_PARAMETER
;
639 // copy the data being written into the file record
640 RtlCopyMemory((PCHAR
)FileRecord
+ Offset
, Buffer
, Length
);
642 Status
= UpdateFileRecord(Vcb
, Context
->FileMFTIndex
, FileRecord
);
644 ReleaseAttributeContext(FoundContext
);
645 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
647 if (NT_SUCCESS(Status
))
648 *RealLengthWritten
= Length
;
653 // This is a non-resident attribute.
655 // I. Find the corresponding start data run.
657 // FIXME: Cache seems to be non-working. Disable it for now
658 //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
661 DataRun = Context->CacheRun;
662 LastLCN = Context->CacheRunLastLCN;
663 DataRunStartLCN = Context->CacheRunStartLCN;
664 DataRunLength = Context->CacheRunLength;
665 CurrentOffset = Context->CacheRunCurrentOffset;
670 DataRun
= (PUCHAR
)&Context
->Record
+ Context
->Record
.NonResident
.MappingPairsOffset
;
675 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
676 if (DataRunOffset
!= -1)
679 // DPRINT1("Writing to normal data run, LastLCN %I64u DataRunOffset %I64d\n", LastLCN, DataRunOffset);
680 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
681 LastLCN
= DataRunStartLCN
;
685 // Sparse data run. We can't support writing to sparse files yet
686 // (it may require increasing the allocation size).
687 DataRunStartLCN
= -1;
688 DPRINT1("FIXME: Writing to sparse files is not supported yet!\n");
689 return STATUS_NOT_IMPLEMENTED
;
692 // Have we reached the data run we're trying to write to?
693 if (Offset
>= CurrentOffset
&&
694 Offset
< CurrentOffset
+ (DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
))
701 // We reached the last assigned cluster
702 // TODO: assign new clusters to the end of the file.
703 // (Presently, this code will never be reached, the write should have already failed by now)
704 return STATUS_END_OF_FILE
;
707 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
711 // II. Go through the run list and write the data
713 /* REVIEWME -- As adapted from NtfsReadAttribute():
714 We seem to be making a special case for the first applicable data run, but I'm not sure why.
715 Does it have something to do with (not) caching? Is this strategy equally applicable to writing? */
717 WriteLength
= (ULONG
)min(DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
- (Offset
- CurrentOffset
), Length
);
719 StartingOffset
= DataRunStartLCN
* Vcb
->NtfsInfo
.BytesPerCluster
+ Offset
- CurrentOffset
;
721 // Write the data to the disk
722 Status
= NtfsWriteDisk(Vcb
->StorageDevice
,
725 Vcb
->NtfsInfo
.BytesPerSector
,
726 (PVOID
)SourceBuffer
);
728 // Did the write fail?
729 if (!NT_SUCCESS(Status
))
731 Context
->CacheRun
= DataRun
;
732 Context
->CacheRunOffset
= Offset
;
733 Context
->CacheRunStartLCN
= DataRunStartLCN
;
734 Context
->CacheRunLength
= DataRunLength
;
735 Context
->CacheRunLastLCN
= LastLCN
;
736 Context
->CacheRunCurrentOffset
= CurrentOffset
;
741 Length
-= WriteLength
;
742 SourceBuffer
+= WriteLength
;
743 *RealLengthWritten
+= WriteLength
;
745 // Did we write to the end of the data run?
746 if (WriteLength
== DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
- (Offset
- CurrentOffset
))
748 // Advance to the next data run
749 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
750 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
752 if (DataRunOffset
!= (ULONGLONG
)-1)
754 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
755 LastLCN
= DataRunStartLCN
;
758 DataRunStartLCN
= -1;
761 // Do we have more data to write?
764 // Make sure we don't write past the end of the current data run
765 WriteLength
= (ULONG
)min(DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
, Length
);
767 // Are we dealing with a sparse data run?
768 if (DataRunStartLCN
== -1)
770 DPRINT1("FIXME: Don't know how to write to sparse files yet! (DataRunStartLCN == -1)\n");
771 return STATUS_NOT_IMPLEMENTED
;
775 // write the data to the disk
776 Status
= NtfsWriteDisk(Vcb
->StorageDevice
,
777 DataRunStartLCN
* Vcb
->NtfsInfo
.BytesPerCluster
,
779 Vcb
->NtfsInfo
.BytesPerSector
,
780 (PVOID
)SourceBuffer
);
781 if (!NT_SUCCESS(Status
))
785 Length
-= WriteLength
;
786 SourceBuffer
+= WriteLength
;
787 *RealLengthWritten
+= WriteLength
;
789 // We finished this request, but there's still data in this data run.
790 if (Length
== 0 && WriteLength
!= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
)
793 // Go to next run in the list.
797 // that was the last run
800 // Failed sanity check.
801 DPRINT1("Encountered EOF before expected!\n");
802 return STATUS_END_OF_FILE
;
808 // Advance to the next data run
809 CurrentOffset
+= DataRunLength
* Vcb
->NtfsInfo
.BytesPerCluster
;
810 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
811 if (DataRunOffset
!= -1)
814 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
815 LastLCN
= DataRunStartLCN
;
820 DataRunStartLCN
= -1;
822 } // end while (Length > 0) [more data to write]
824 Context
->CacheRun
= DataRun
;
825 Context
->CacheRunOffset
= Offset
+ *RealLengthWritten
;
826 Context
->CacheRunStartLCN
= DataRunStartLCN
;
827 Context
->CacheRunLength
= DataRunLength
;
828 Context
->CacheRunLastLCN
= LastLCN
;
829 Context
->CacheRunCurrentOffset
= CurrentOffset
;
835 ReadFileRecord(PDEVICE_EXTENSION Vcb
,
837 PFILE_RECORD_HEADER file
)
841 DPRINT("ReadFileRecord(%p, %I64x, %p)\n", Vcb
, index
, file
);
843 BytesRead
= ReadAttribute(Vcb
, Vcb
->MFTContext
, index
* Vcb
->NtfsInfo
.BytesPerFileRecord
, (PCHAR
)file
, Vcb
->NtfsInfo
.BytesPerFileRecord
);
844 if (BytesRead
!= Vcb
->NtfsInfo
.BytesPerFileRecord
)
846 DPRINT1("ReadFileRecord failed: %I64u read, %u expected\n", BytesRead
, Vcb
->NtfsInfo
.BytesPerFileRecord
);
847 return STATUS_PARTIAL_COPY
;
850 /* Apply update sequence array fixups. */
851 DPRINT("Sequence number: %u\n", file
->SequenceNumber
);
852 return FixupUpdateSequenceArray(Vcb
, &file
->Ntfs
);
857 * Searches a file's parent directory (given the parent's index in the mft)
858 * for the given file. Upon finding an index entry for that file, updates
859 * Data Size and Allocated Size values in the $FILE_NAME attribute of that entry.
861 * (Most of this code was copied from NtfsFindMftRecord)
864 UpdateFileNameRecord(PDEVICE_EXTENSION Vcb
,
865 ULONGLONG ParentMFTIndex
,
866 PUNICODE_STRING FileName
,
868 ULONGLONG NewDataSize
,
869 ULONGLONG NewAllocationSize
)
871 PFILE_RECORD_HEADER MftRecord
;
872 PNTFS_ATTR_CONTEXT IndexRootCtx
;
873 PINDEX_ROOT_ATTRIBUTE IndexRoot
;
875 PINDEX_ENTRY_ATTRIBUTE IndexEntry
, IndexEntryEnd
;
877 ULONG CurrentEntry
= 0;
879 DPRINT("UpdateFileNameRecord(%p, %I64d, %wZ, %u, %I64u, %I64u)\n", Vcb
, ParentMFTIndex
, FileName
, DirSearch
, NewDataSize
, NewAllocationSize
);
881 MftRecord
= ExAllocatePoolWithTag(NonPagedPool
,
882 Vcb
->NtfsInfo
.BytesPerFileRecord
,
884 if (MftRecord
== NULL
)
886 return STATUS_INSUFFICIENT_RESOURCES
;
889 Status
= ReadFileRecord(Vcb
, ParentMFTIndex
, MftRecord
);
890 if (!NT_SUCCESS(Status
))
892 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
896 ASSERT(MftRecord
->Ntfs
.Type
== NRH_FILE_TYPE
);
897 Status
= FindAttribute(Vcb
, MftRecord
, AttributeIndexRoot
, L
"$I30", 4, &IndexRootCtx
, NULL
);
898 if (!NT_SUCCESS(Status
))
900 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
904 IndexRecord
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerIndexRecord
, TAG_NTFS
);
905 if (IndexRecord
== NULL
)
907 ReleaseAttributeContext(IndexRootCtx
);
908 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
909 return STATUS_INSUFFICIENT_RESOURCES
;
912 ReadAttribute(Vcb
, IndexRootCtx
, 0, IndexRecord
, Vcb
->NtfsInfo
.BytesPerIndexRecord
);
913 IndexRoot
= (PINDEX_ROOT_ATTRIBUTE
)IndexRecord
;
914 IndexEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((PCHAR
)&IndexRoot
->Header
+ IndexRoot
->Header
.FirstEntryOffset
);
915 // Index root is always resident.
916 IndexEntryEnd
= (PINDEX_ENTRY_ATTRIBUTE
)(IndexRecord
+ IndexRoot
->Header
.TotalSizeOfEntries
);
918 DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb
->NtfsInfo
.BytesPerIndexRecord
, IndexRoot
->SizeOfEntry
);
920 Status
= UpdateIndexEntryFileNameSize(Vcb
,
923 IndexRoot
->SizeOfEntry
,
933 ReleaseAttributeContext(IndexRootCtx
);
934 ExFreePoolWithTag(IndexRecord
, TAG_NTFS
);
935 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
941 * Recursively searches directory index and applies the size update to the $FILE_NAME attribute of the
942 * proper index entry.
943 * (Heavily based on BrowseIndexEntries)
946 UpdateIndexEntryFileNameSize(PDEVICE_EXTENSION Vcb
,
947 PFILE_RECORD_HEADER MftRecord
,
949 ULONG IndexBlockSize
,
950 PINDEX_ENTRY_ATTRIBUTE FirstEntry
,
951 PINDEX_ENTRY_ATTRIBUTE LastEntry
,
952 PUNICODE_STRING FileName
,
956 ULONGLONG NewDataSize
,
957 ULONGLONG NewAllocatedSize
)
961 PINDEX_ENTRY_ATTRIBUTE IndexEntry
;
962 PNTFS_ATTR_CONTEXT IndexAllocationCtx
;
963 ULONGLONG IndexAllocationSize
;
964 PINDEX_BUFFER IndexBuffer
;
966 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
);
968 // find the index entry responsible for the file we're trying to update
969 IndexEntry
= FirstEntry
;
970 while (IndexEntry
< LastEntry
&&
971 !(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_END
))
973 if ((IndexEntry
->Data
.Directory
.IndexedFile
& NTFS_MFT_MASK
) > 0x10 &&
974 *CurrentEntry
>= *StartEntry
&&
975 IndexEntry
->FileName
.NameType
!= NTFS_FILE_NAME_DOS
&&
976 CompareFileName(FileName
, IndexEntry
, DirSearch
))
978 *StartEntry
= *CurrentEntry
;
979 IndexEntry
->FileName
.DataSize
= NewDataSize
;
980 IndexEntry
->FileName
.AllocatedSize
= NewAllocatedSize
;
981 // indicate that the caller will still need to write the structure to the disk
982 return STATUS_PENDING
;
985 (*CurrentEntry
) += 1;
986 ASSERT(IndexEntry
->Length
>= sizeof(INDEX_ENTRY_ATTRIBUTE
));
987 IndexEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((PCHAR
)IndexEntry
+ IndexEntry
->Length
);
990 /* If we're already browsing a subnode */
991 if (IndexRecord
== NULL
)
993 return STATUS_OBJECT_PATH_NOT_FOUND
;
996 /* If there's no subnode */
997 if (!(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_NODE
))
999 return STATUS_OBJECT_PATH_NOT_FOUND
;
1002 Status
= FindAttribute(Vcb
, MftRecord
, AttributeIndexAllocation
, L
"$I30", 4, &IndexAllocationCtx
, NULL
);
1003 if (!NT_SUCCESS(Status
))
1005 DPRINT("Corrupted filesystem!\n");
1009 IndexAllocationSize
= AttributeDataLength(&IndexAllocationCtx
->Record
);
1010 Status
= STATUS_OBJECT_PATH_NOT_FOUND
;
1011 for (RecordOffset
= 0; RecordOffset
< IndexAllocationSize
; RecordOffset
+= IndexBlockSize
)
1013 ReadAttribute(Vcb
, IndexAllocationCtx
, RecordOffset
, IndexRecord
, IndexBlockSize
);
1014 Status
= FixupUpdateSequenceArray(Vcb
, &((PFILE_RECORD_HEADER
)IndexRecord
)->Ntfs
);
1015 if (!NT_SUCCESS(Status
))
1020 IndexBuffer
= (PINDEX_BUFFER
)IndexRecord
;
1021 ASSERT(IndexBuffer
->Ntfs
.Type
== NRH_INDX_TYPE
);
1022 ASSERT(IndexBuffer
->Header
.AllocatedSize
+ FIELD_OFFSET(INDEX_BUFFER
, Header
) == IndexBlockSize
);
1023 FirstEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)&IndexBuffer
->Header
+ IndexBuffer
->Header
.FirstEntryOffset
);
1024 LastEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)&IndexBuffer
->Header
+ IndexBuffer
->Header
.TotalSizeOfEntries
);
1025 ASSERT(LastEntry
<= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)IndexBuffer
+ IndexBlockSize
));
1027 Status
= UpdateIndexEntryFileNameSize(NULL
, NULL
, NULL
, 0, FirstEntry
, LastEntry
, FileName
, StartEntry
, CurrentEntry
, DirSearch
, NewDataSize
, NewAllocatedSize
);
1028 if (Status
== STATUS_PENDING
)
1030 // write the index record back to disk
1033 // first we need to update the fixup values for the index block
1034 Status
= AddFixupArray(Vcb
, &((PFILE_RECORD_HEADER
)IndexRecord
)->Ntfs
);
1035 if (!NT_SUCCESS(Status
))
1037 DPRINT1("Error: Failed to update fixup sequence array!\n");
1041 Status
= WriteAttribute(Vcb
, IndexAllocationCtx
, RecordOffset
, (const PUCHAR
)IndexRecord
, IndexBlockSize
, &Written
);
1042 if (!NT_SUCCESS(Status
))
1044 DPRINT1("ERROR Performing write!\n");
1048 Status
= STATUS_SUCCESS
;
1051 if (NT_SUCCESS(Status
))
1057 ReleaseAttributeContext(IndexAllocationCtx
);
1064 * Writes a file record to the master file table, at a given index.
1067 UpdateFileRecord(PDEVICE_EXTENSION Vcb
,
1069 PFILE_RECORD_HEADER file
)
1072 NTSTATUS Status
= STATUS_SUCCESS
;
1074 DPRINT("UpdateFileRecord(%p, %I64x, %p)\n", Vcb
, index
, file
);
1076 // Add the fixup array to prepare the data for writing to disk
1077 AddFixupArray(Vcb
, &file
->Ntfs
);
1079 // write the file record to the master file table
1080 Status
= WriteAttribute(Vcb
, Vcb
->MFTContext
, index
* Vcb
->NtfsInfo
.BytesPerFileRecord
, (const PUCHAR
)file
, Vcb
->NtfsInfo
.BytesPerFileRecord
, &BytesWritten
);
1082 if (!NT_SUCCESS(Status
))
1084 DPRINT1("UpdateFileRecord failed: %I64u written, %u expected\n", BytesWritten
, Vcb
->NtfsInfo
.BytesPerFileRecord
);
1087 // remove the fixup array (so the file record pointer can still be used)
1088 FixupUpdateSequenceArray(Vcb
, &file
->Ntfs
);
1095 FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb
,
1096 PNTFS_RECORD_HEADER Record
)
1103 USA
= (USHORT
*)((PCHAR
)Record
+ Record
->UsaOffset
);
1104 USANumber
= *(USA
++);
1105 USACount
= Record
->UsaCount
- 1; /* Exclude the USA Number. */
1106 Block
= (USHORT
*)((PCHAR
)Record
+ Vcb
->NtfsInfo
.BytesPerSector
- 2);
1108 DPRINT("FixupUpdateSequenceArray(%p, %p)\nUSANumber: %u\tUSACount: %u\n", Vcb
, Record
, USANumber
, USACount
);
1112 if (*Block
!= USANumber
)
1114 DPRINT1("Mismatch with USA: %u read, %u expected\n" , *Block
, USANumber
);
1115 return STATUS_UNSUCCESSFUL
;
1118 Block
= (USHORT
*)((PCHAR
)Block
+ Vcb
->NtfsInfo
.BytesPerSector
);
1122 return STATUS_SUCCESS
;
1126 AddFixupArray(PDEVICE_EXTENSION Vcb
,
1127 PNTFS_RECORD_HEADER Record
)
1129 USHORT
*pShortToFixUp
;
1130 unsigned int ArrayEntryCount
= Record
->UsaCount
- 1;
1131 unsigned int Offset
= Vcb
->NtfsInfo
.BytesPerSector
- 2;
1134 PFIXUP_ARRAY fixupArray
= (PFIXUP_ARRAY
)((UCHAR
*)Record
+ Record
->UsaOffset
);
1136 DPRINT("AddFixupArray(%p, %p)\n fixupArray->USN: %u, ArrayEntryCount: %u\n", Vcb
, Record
, fixupArray
->USN
, ArrayEntryCount
);
1140 for (i
= 0; i
< ArrayEntryCount
; i
++)
1142 DPRINT("USN: %u\tOffset: %u\n", fixupArray
->USN
, Offset
);
1144 pShortToFixUp
= (USHORT
*)((PCHAR
)Record
+ Offset
);
1145 fixupArray
->Array
[i
] = *pShortToFixUp
;
1146 *pShortToFixUp
= fixupArray
->USN
;
1147 Offset
+= Vcb
->NtfsInfo
.BytesPerSector
;
1150 return STATUS_SUCCESS
;
1154 ReadLCN(PDEVICE_EXTENSION Vcb
,
1159 LARGE_INTEGER DiskSector
;
1161 DiskSector
.QuadPart
= lcn
;
1163 return NtfsReadSectors(Vcb
->StorageDevice
,
1164 DiskSector
.u
.LowPart
* Vcb
->NtfsInfo
.SectorsPerCluster
,
1165 count
* Vcb
->NtfsInfo
.SectorsPerCluster
,
1166 Vcb
->NtfsInfo
.BytesPerSector
,
1173 CompareFileName(PUNICODE_STRING FileName
,
1174 PINDEX_ENTRY_ATTRIBUTE IndexEntry
,
1177 BOOLEAN Ret
, Alloc
= FALSE
;
1178 UNICODE_STRING EntryName
;
1180 EntryName
.Buffer
= IndexEntry
->FileName
.Name
;
1182 EntryName
.MaximumLength
= IndexEntry
->FileName
.NameLength
* sizeof(WCHAR
);
1186 UNICODE_STRING IntFileName
;
1187 if (IndexEntry
->FileName
.NameType
!= NTFS_FILE_NAME_POSIX
)
1189 NT_VERIFY(NT_SUCCESS(RtlUpcaseUnicodeString(&IntFileName
, FileName
, TRUE
)));
1194 IntFileName
= *FileName
;
1197 Ret
= FsRtlIsNameInExpression(&IntFileName
, &EntryName
, (IndexEntry
->FileName
.NameType
!= NTFS_FILE_NAME_POSIX
), NULL
);
1201 RtlFreeUnicodeString(&IntFileName
);
1208 return (RtlCompareUnicodeString(FileName
, &EntryName
, (IndexEntry
->FileName
.NameType
!= NTFS_FILE_NAME_POSIX
)) == 0);
1215 DumpIndexEntry(PINDEX_ENTRY_ATTRIBUTE IndexEntry
)
1217 DPRINT1("Entry: %p\n", IndexEntry
);
1218 DPRINT1("\tData.Directory.IndexedFile: %I64x\n", IndexEntry
->Data
.Directory
.IndexedFile
);
1219 DPRINT1("\tLength: %u\n", IndexEntry
->Length
);
1220 DPRINT1("\tKeyLength: %u\n", IndexEntry
->KeyLength
);
1221 DPRINT1("\tFlags: %x\n", IndexEntry
->Flags
);
1222 DPRINT1("\tReserved: %x\n", IndexEntry
->Reserved
);
1223 DPRINT1("\t\tDirectoryFileReferenceNumber: %I64x\n", IndexEntry
->FileName
.DirectoryFileReferenceNumber
);
1224 DPRINT1("\t\tCreationTime: %I64u\n", IndexEntry
->FileName
.CreationTime
);
1225 DPRINT1("\t\tChangeTime: %I64u\n", IndexEntry
->FileName
.ChangeTime
);
1226 DPRINT1("\t\tLastWriteTime: %I64u\n", IndexEntry
->FileName
.LastWriteTime
);
1227 DPRINT1("\t\tLastAccessTime: %I64u\n", IndexEntry
->FileName
.LastAccessTime
);
1228 DPRINT1("\t\tAllocatedSize: %I64u\n", IndexEntry
->FileName
.AllocatedSize
);
1229 DPRINT1("\t\tDataSize: %I64u\n", IndexEntry
->FileName
.DataSize
);
1230 DPRINT1("\t\tFileAttributes: %x\n", IndexEntry
->FileName
.FileAttributes
);
1231 DPRINT1("\t\tNameLength: %u\n", IndexEntry
->FileName
.NameLength
);
1232 DPRINT1("\t\tNameType: %x\n", IndexEntry
->FileName
.NameType
);
1233 DPRINT1("\t\tName: %.*S\n", IndexEntry
->FileName
.NameLength
, IndexEntry
->FileName
.Name
);
1238 BrowseIndexEntries(PDEVICE_EXTENSION Vcb
,
1239 PFILE_RECORD_HEADER MftRecord
,
1241 ULONG IndexBlockSize
,
1242 PINDEX_ENTRY_ATTRIBUTE FirstEntry
,
1243 PINDEX_ENTRY_ATTRIBUTE LastEntry
,
1244 PUNICODE_STRING FileName
,
1246 PULONG CurrentEntry
,
1248 ULONGLONG
*OutMFTIndex
)
1252 PINDEX_ENTRY_ATTRIBUTE IndexEntry
;
1253 PNTFS_ATTR_CONTEXT IndexAllocationCtx
;
1254 ULONGLONG IndexAllocationSize
;
1255 PINDEX_BUFFER IndexBuffer
;
1257 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
);
1259 IndexEntry
= FirstEntry
;
1260 while (IndexEntry
< LastEntry
&&
1261 !(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_END
))
1263 if ((IndexEntry
->Data
.Directory
.IndexedFile
& NTFS_MFT_MASK
) > 0x10 &&
1264 *CurrentEntry
>= *StartEntry
&&
1265 IndexEntry
->FileName
.NameType
!= NTFS_FILE_NAME_DOS
&&
1266 CompareFileName(FileName
, IndexEntry
, DirSearch
))
1268 *StartEntry
= *CurrentEntry
;
1269 *OutMFTIndex
= (IndexEntry
->Data
.Directory
.IndexedFile
& NTFS_MFT_MASK
);
1270 return STATUS_SUCCESS
;
1273 (*CurrentEntry
) += 1;
1274 ASSERT(IndexEntry
->Length
>= sizeof(INDEX_ENTRY_ATTRIBUTE
));
1275 IndexEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((PCHAR
)IndexEntry
+ IndexEntry
->Length
);
1278 /* If we're already browsing a subnode */
1279 if (IndexRecord
== NULL
)
1281 return STATUS_OBJECT_PATH_NOT_FOUND
;
1284 /* If there's no subnode */
1285 if (!(IndexEntry
->Flags
& NTFS_INDEX_ENTRY_NODE
))
1287 return STATUS_OBJECT_PATH_NOT_FOUND
;
1290 Status
= FindAttribute(Vcb
, MftRecord
, AttributeIndexAllocation
, L
"$I30", 4, &IndexAllocationCtx
, NULL
);
1291 if (!NT_SUCCESS(Status
))
1293 DPRINT("Corrupted filesystem!\n");
1297 IndexAllocationSize
= AttributeDataLength(&IndexAllocationCtx
->Record
);
1298 Status
= STATUS_OBJECT_PATH_NOT_FOUND
;
1299 for (RecordOffset
= 0; RecordOffset
< IndexAllocationSize
; RecordOffset
+= IndexBlockSize
)
1301 ReadAttribute(Vcb
, IndexAllocationCtx
, RecordOffset
, IndexRecord
, IndexBlockSize
);
1302 Status
= FixupUpdateSequenceArray(Vcb
, &((PFILE_RECORD_HEADER
)IndexRecord
)->Ntfs
);
1303 if (!NT_SUCCESS(Status
))
1308 IndexBuffer
= (PINDEX_BUFFER
)IndexRecord
;
1309 ASSERT(IndexBuffer
->Ntfs
.Type
== NRH_INDX_TYPE
);
1310 ASSERT(IndexBuffer
->Header
.AllocatedSize
+ FIELD_OFFSET(INDEX_BUFFER
, Header
) == IndexBlockSize
);
1311 FirstEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)&IndexBuffer
->Header
+ IndexBuffer
->Header
.FirstEntryOffset
);
1312 LastEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)&IndexBuffer
->Header
+ IndexBuffer
->Header
.TotalSizeOfEntries
);
1313 ASSERT(LastEntry
<= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)IndexBuffer
+ IndexBlockSize
));
1315 Status
= BrowseIndexEntries(NULL
, NULL
, NULL
, 0, FirstEntry
, LastEntry
, FileName
, StartEntry
, CurrentEntry
, DirSearch
, OutMFTIndex
);
1316 if (NT_SUCCESS(Status
))
1322 ReleaseAttributeContext(IndexAllocationCtx
);
1327 NtfsFindMftRecord(PDEVICE_EXTENSION Vcb
,
1329 PUNICODE_STRING FileName
,
1332 ULONGLONG
*OutMFTIndex
)
1334 PFILE_RECORD_HEADER MftRecord
;
1335 PNTFS_ATTR_CONTEXT IndexRootCtx
;
1336 PINDEX_ROOT_ATTRIBUTE IndexRoot
;
1338 PINDEX_ENTRY_ATTRIBUTE IndexEntry
, IndexEntryEnd
;
1340 ULONG CurrentEntry
= 0;
1342 DPRINT("NtfsFindMftRecord(%p, %I64d, %wZ, %u, %u, %p)\n", Vcb
, MFTIndex
, FileName
, *FirstEntry
, DirSearch
, OutMFTIndex
);
1344 MftRecord
= ExAllocatePoolWithTag(NonPagedPool
,
1345 Vcb
->NtfsInfo
.BytesPerFileRecord
,
1347 if (MftRecord
== NULL
)
1349 return STATUS_INSUFFICIENT_RESOURCES
;
1352 Status
= ReadFileRecord(Vcb
, MFTIndex
, MftRecord
);
1353 if (!NT_SUCCESS(Status
))
1355 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
1359 ASSERT(MftRecord
->Ntfs
.Type
== NRH_FILE_TYPE
);
1360 Status
= FindAttribute(Vcb
, MftRecord
, AttributeIndexRoot
, L
"$I30", 4, &IndexRootCtx
, NULL
);
1361 if (!NT_SUCCESS(Status
))
1363 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
1367 IndexRecord
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerIndexRecord
, TAG_NTFS
);
1368 if (IndexRecord
== NULL
)
1370 ReleaseAttributeContext(IndexRootCtx
);
1371 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
1372 return STATUS_INSUFFICIENT_RESOURCES
;
1375 ReadAttribute(Vcb
, IndexRootCtx
, 0, IndexRecord
, Vcb
->NtfsInfo
.BytesPerIndexRecord
);
1376 IndexRoot
= (PINDEX_ROOT_ATTRIBUTE
)IndexRecord
;
1377 IndexEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((PCHAR
)&IndexRoot
->Header
+ IndexRoot
->Header
.FirstEntryOffset
);
1378 /* Index root is always resident. */
1379 IndexEntryEnd
= (PINDEX_ENTRY_ATTRIBUTE
)(IndexRecord
+ IndexRoot
->Header
.TotalSizeOfEntries
);
1380 ReleaseAttributeContext(IndexRootCtx
);
1382 DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb
->NtfsInfo
.BytesPerIndexRecord
, IndexRoot
->SizeOfEntry
);
1384 Status
= BrowseIndexEntries(Vcb
, MftRecord
, IndexRecord
, IndexRoot
->SizeOfEntry
, IndexEntry
, IndexEntryEnd
, FileName
, FirstEntry
, &CurrentEntry
, DirSearch
, OutMFTIndex
);
1386 ExFreePoolWithTag(IndexRecord
, TAG_NTFS
);
1387 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
1393 NtfsLookupFileAt(PDEVICE_EXTENSION Vcb
,
1394 PUNICODE_STRING PathName
,
1395 PFILE_RECORD_HEADER
*FileRecord
,
1396 PULONGLONG MFTIndex
,
1397 ULONGLONG CurrentMFTIndex
)
1399 UNICODE_STRING Current
, Remaining
;
1401 ULONG FirstEntry
= 0;
1403 DPRINT("NtfsLookupFileAt(%p, %wZ, %p, %I64x)\n", Vcb
, PathName
, FileRecord
, CurrentMFTIndex
);
1405 FsRtlDissectName(*PathName
, &Current
, &Remaining
);
1407 while (Current
.Length
!= 0)
1409 DPRINT("Current: %wZ\n", &Current
);
1411 Status
= NtfsFindMftRecord(Vcb
, CurrentMFTIndex
, &Current
, &FirstEntry
, FALSE
, &CurrentMFTIndex
);
1412 if (!NT_SUCCESS(Status
))
1417 if (Remaining
.Length
== 0)
1420 FsRtlDissectName(Current
, &Current
, &Remaining
);
1423 *FileRecord
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
1424 if (*FileRecord
== NULL
)
1426 DPRINT("NtfsLookupFileAt: Can't allocate MFT record\n");
1427 return STATUS_INSUFFICIENT_RESOURCES
;
1430 Status
= ReadFileRecord(Vcb
, CurrentMFTIndex
, *FileRecord
);
1431 if (!NT_SUCCESS(Status
))
1433 DPRINT("NtfsLookupFileAt: Can't read MFT record\n");
1434 ExFreePoolWithTag(*FileRecord
, TAG_NTFS
);
1438 *MFTIndex
= CurrentMFTIndex
;
1440 return STATUS_SUCCESS
;
1444 NtfsLookupFile(PDEVICE_EXTENSION Vcb
,
1445 PUNICODE_STRING PathName
,
1446 PFILE_RECORD_HEADER
*FileRecord
,
1447 PULONGLONG MFTIndex
)
1449 return NtfsLookupFileAt(Vcb
, PathName
, FileRecord
, MFTIndex
, NTFS_FILE_ROOT
);
1453 NtfsFindFileAt(PDEVICE_EXTENSION Vcb
,
1454 PUNICODE_STRING SearchPattern
,
1456 PFILE_RECORD_HEADER
*FileRecord
,
1457 PULONGLONG MFTIndex
,
1458 ULONGLONG CurrentMFTIndex
)
1462 DPRINT("NtfsFindFileAt(%p, %wZ, %u, %p, %p, %I64x)\n", Vcb
, SearchPattern
, *FirstEntry
, FileRecord
, MFTIndex
, CurrentMFTIndex
);
1464 Status
= NtfsFindMftRecord(Vcb
, CurrentMFTIndex
, SearchPattern
, FirstEntry
, TRUE
, &CurrentMFTIndex
);
1465 if (!NT_SUCCESS(Status
))
1467 DPRINT("NtfsFindFileAt: NtfsFindMftRecord() failed with status 0x%08lx\n", Status
);
1471 *FileRecord
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
1472 if (*FileRecord
== NULL
)
1474 DPRINT("NtfsFindFileAt: Can't allocate MFT record\n");
1475 return STATUS_INSUFFICIENT_RESOURCES
;
1478 Status
= ReadFileRecord(Vcb
, CurrentMFTIndex
, *FileRecord
);
1479 if (!NT_SUCCESS(Status
))
1481 DPRINT("NtfsFindFileAt: Can't read MFT record\n");
1482 ExFreePoolWithTag(*FileRecord
, TAG_NTFS
);
1486 *MFTIndex
= CurrentMFTIndex
;
1488 return STATUS_SUCCESS
;