3 * Copyright (C) 2002,2003 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/attrib.c
22 * PURPOSE: NTFS filesystem driver
23 * PROGRAMMERS: Eric Kohl
25 * Hervé Poussineau (hpoussin@reactos.org)
26 * Pierre Schweitzer (pierre@reactos.org)
29 /* INCLUDES *****************************************************************/
36 /* FUNCTIONS ****************************************************************/
42 * Adds a run of allocated clusters to a non-resident attribute.
45 * Pointer to an NTFS_VCB for the destination volume.
48 * Pointer to an NTFS_ATTR_CONTEXT describing the destination attribute.
51 * Byte offset of the destination attribute relative to its file record.
54 * Pointer to a complete copy of the file record containing the destination attribute. Must be at least
55 * Vcb->NtfsInfo.BytesPerFileRecord bytes long.
57 * @param NextAssignedCluster
58 * Logical cluster number of the start of the data run being added.
61 * How many clusters are in the data run being added. Can't be 0.
64 * STATUS_SUCCESS on success. STATUS_INVALID_PARAMETER if AttrContext describes a resident attribute.
65 * STATUS_INSUFFICIENT_RESOURCES if ConvertDataRunsToLargeMCB() fails.
66 * STATUS_BUFFER_TOO_SMALL if ConvertLargeMCBToDataRuns() fails.
67 * STATUS_NOT_IMPLEMENTED if we need to migrate the attribute to an attribute list (TODO).
70 * Clusters should have been allocated previously with NtfsAllocateClusters().
76 PNTFS_ATTR_CONTEXT AttrContext
,
78 PFILE_RECORD_HEADER FileRecord
,
79 ULONGLONG NextAssignedCluster
,
83 PUCHAR DataRun
= (PUCHAR
)&AttrContext
->Record
+ AttrContext
->Record
.NonResident
.MappingPairsOffset
;
85 PNTFS_ATTR_RECORD DestinationAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ AttrOffset
);
86 LARGE_MCB DataRunsMCB
;
87 ULONG NextAttributeOffset
= AttrOffset
+ AttrContext
->Record
.Length
;
88 ULONGLONG NextVBN
= AttrContext
->Record
.NonResident
.LowestVCN
;
90 // Allocate some memory for the RunBuffer
92 ULONG RunBufferOffset
= 0;
94 if (!AttrContext
->Record
.IsNonResident
)
95 return STATUS_INVALID_PARAMETER
;
97 RunBuffer
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
99 // Convert the data runs to a map control block
100 Status
= ConvertDataRunsToLargeMCB(DataRun
, &DataRunsMCB
, &NextVBN
);
101 if (!NT_SUCCESS(Status
))
103 DPRINT1("Unable to convert data runs to MCB (probably ran out of memory)!\n");
104 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
108 // Add newly-assigned clusters to mcb
110 if (!FsRtlAddLargeMcbEntry(&DataRunsMCB
,
115 FsRtlUninitializeLargeMcb(&DataRunsMCB
);
116 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
117 return STATUS_INSUFFICIENT_RESOURCES
;
119 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
120 FsRtlUninitializeLargeMcb(&DataRunsMCB
);
121 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
122 _SEH2_YIELD(return STATUS_INSUFFICIENT_RESOURCES
);
126 // Convert the map control block back to encoded data runs
127 ConvertLargeMCBToDataRuns(&DataRunsMCB
, RunBuffer
, Vcb
->NtfsInfo
.BytesPerCluster
, &RunBufferOffset
);
129 // Get the amount of free space between the start of the of the first data run and the attribute end
130 DataRunMaxLength
= AttrContext
->Record
.Length
- AttrContext
->Record
.NonResident
.MappingPairsOffset
;
132 // Do we need to extend the attribute (or convert to attribute list)?
133 if (DataRunMaxLength
< RunBufferOffset
)
135 PNTFS_ATTR_RECORD NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ NextAttributeOffset
);
136 DataRunMaxLength
+= Vcb
->NtfsInfo
.BytesPerFileRecord
- NextAttributeOffset
- (sizeof(ULONG
) * 2);
138 // Can we move the end of the attribute?
139 if (NextAttribute
->Type
!= AttributeEnd
|| DataRunMaxLength
< RunBufferOffset
- 1)
141 DPRINT1("FIXME: Need to create attribute list! Max Data Run Length available: %d\n", DataRunMaxLength
);
142 if (NextAttribute
->Type
!= AttributeEnd
)
143 DPRINT1("There's another attribute after this one with type %0xlx\n", NextAttribute
->Type
);
144 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
145 FsRtlUninitializeLargeMcb(&DataRunsMCB
);
146 return STATUS_NOT_IMPLEMENTED
;
149 // calculate position of end markers
150 NextAttributeOffset
= AttrOffset
+ AttrContext
->Record
.NonResident
.MappingPairsOffset
+ RunBufferOffset
;
151 NextAttributeOffset
= ALIGN_UP_BY(NextAttributeOffset
, 8);
153 // Write the end markers
154 NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ NextAttributeOffset
);
155 NextAttribute
->Type
= AttributeEnd
;
156 NextAttribute
->Length
= FILE_RECORD_END
;
159 DestinationAttribute
->Length
= NextAttributeOffset
- AttrOffset
;
160 AttrContext
->Record
.Length
= DestinationAttribute
->Length
;
162 // We need to increase the FileRecord size
163 FileRecord
->BytesInUse
= NextAttributeOffset
+ (sizeof(ULONG
) * 2);
166 // NOTE: from this point on the original attribute record will contain invalid data in it's runbuffer
167 // TODO: Elegant fix? Could we free the old Record and allocate a new one without issue?
170 DestinationAttribute
->NonResident
.HighestVCN
=
171 AttrContext
->Record
.NonResident
.HighestVCN
= max(NextVBN
- 1 + RunLength
,
172 AttrContext
->Record
.NonResident
.HighestVCN
);
174 // Write data runs to destination attribute
175 RtlCopyMemory((PVOID
)((ULONG_PTR
)DestinationAttribute
+ DestinationAttribute
->NonResident
.MappingPairsOffset
),
179 // Update the file record
180 Status
= UpdateFileRecord(Vcb
, AttrContext
->FileMFTIndex
, FileRecord
);
182 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
183 FsRtlUninitializeLargeMcb(&DataRunsMCB
);
185 NtfsDumpDataRuns((PUCHAR
)((ULONG_PTR
)DestinationAttribute
+ DestinationAttribute
->NonResident
.MappingPairsOffset
), 0);
191 * @name ConvertDataRunsToLargeMCB
194 * Converts binary data runs to a map control block.
197 * Pointer to the run data
200 * Pointer to an unitialized LARGE_MCB structure.
203 * STATUS_SUCCESS on success, STATUS_INSUFFICIENT_RESOURCES if we fail to
204 * initialize the mcb or add an entry.
207 * Initializes the LARGE_MCB pointed to by DataRunsMCB. If this function succeeds, you
208 * need to call FsRtlUninitializeLargeMcb() when you're done with DataRunsMCB. This
209 * function will ensure the LargeMCB has been unitialized in case of failure.
213 ConvertDataRunsToLargeMCB(PUCHAR DataRun
,
214 PLARGE_MCB DataRunsMCB
,
217 LONGLONG DataRunOffset
;
218 ULONGLONG DataRunLength
;
219 LONGLONG DataRunStartLCN
;
220 ULONGLONG LastLCN
= 0;
222 // Initialize the MCB, potentially catch an exception
224 FsRtlInitializeLargeMcb(DataRunsMCB
, NonPagedPool
);
225 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
226 _SEH2_YIELD(return STATUS_INSUFFICIENT_RESOURCES
);
229 while (*DataRun
!= 0)
231 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
233 if (DataRunOffset
!= -1)
236 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
237 LastLCN
= DataRunStartLCN
;
240 if (!FsRtlAddLargeMcbEntry(DataRunsMCB
,
245 FsRtlUninitializeLargeMcb(DataRunsMCB
);
246 return STATUS_INSUFFICIENT_RESOURCES
;
248 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
249 FsRtlUninitializeLargeMcb(DataRunsMCB
);
250 _SEH2_YIELD(return STATUS_INSUFFICIENT_RESOURCES
);
255 *pNextVBN
+= DataRunLength
;
258 return STATUS_SUCCESS
;
262 * @name ConvertLargeMCBToDataRuns
265 * Converts a map control block to a series of encoded data runs (used by non-resident attributes).
268 * Pointer to a LARGE_MCB structure describing the data runs.
271 * Pointer to the buffer that will receive the encoded data runs.
273 * @param MaxBufferSize
274 * Size of RunBuffer, in bytes.
276 * @param UsedBufferSize
277 * Pointer to a ULONG that will receive the size of the data runs in bytes. Can't be NULL.
280 * STATUS_SUCCESS on success, STATUS_BUFFER_TOO_SMALL if RunBuffer is too small to contain the
285 ConvertLargeMCBToDataRuns(PLARGE_MCB DataRunsMCB
,
288 PULONG UsedBufferSize
)
290 NTSTATUS Status
= STATUS_SUCCESS
;
291 ULONG RunBufferOffset
= 0;
292 LONGLONG DataRunOffset
;
293 ULONGLONG LastLCN
= 0;
294 LONGLONG Vbn
, Lbn
, Count
;
298 DPRINT("\t[Vbn, Lbn, Count]\n");
300 // convert each mcb entry to a data run
301 for (i
= 0; FsRtlGetNextLargeMcbEntry(DataRunsMCB
, i
, &Vbn
, &Lbn
, &Count
); i
++)
303 UCHAR DataRunOffsetSize
= 0;
304 UCHAR DataRunLengthSize
= 0;
305 UCHAR ControlByte
= 0;
308 DPRINT("\t[%I64d, %I64d,%I64d]\n", Vbn
, Lbn
, Count
);
310 // TODO: check for holes and convert to sparse runs
311 DataRunOffset
= Lbn
- LastLCN
;
314 // now we need to determine how to represent DataRunOffset with the minimum number of bytes
315 DPRINT("Determining how many bytes needed to represent %I64x\n", DataRunOffset
);
316 DataRunOffsetSize
= GetPackedByteCount(DataRunOffset
, TRUE
);
317 DPRINT("%d bytes needed.\n", DataRunOffsetSize
);
319 // determine how to represent DataRunLengthSize with the minimum number of bytes
320 DPRINT("Determining how many bytes needed to represent %I64x\n", Count
);
321 DataRunLengthSize
= GetPackedByteCount(Count
, TRUE
);
322 DPRINT("%d bytes needed.\n", DataRunLengthSize
);
324 // ensure the next data run + end marker would be > Max buffer size
325 if (RunBufferOffset
+ 2 + DataRunLengthSize
+ DataRunOffsetSize
> MaxBufferSize
)
327 Status
= STATUS_BUFFER_TOO_SMALL
;
328 DPRINT1("FIXME: Ran out of room in buffer for data runs!\n");
332 // pack and copy the control byte
333 ControlByte
= (DataRunOffsetSize
<< 4) + DataRunLengthSize
;
334 RunBuffer
[RunBufferOffset
++] = ControlByte
;
336 // copy DataRunLength
337 RtlCopyMemory(RunBuffer
+ RunBufferOffset
, &Count
, DataRunLengthSize
);
338 RunBufferOffset
+= DataRunLengthSize
;
340 // copy DataRunOffset
341 RtlCopyMemory(RunBuffer
+ RunBufferOffset
, &DataRunOffset
, DataRunOffsetSize
);
342 RunBufferOffset
+= DataRunOffsetSize
;
346 RunBuffer
[RunBufferOffset
++] = 0;
348 *UsedBufferSize
= RunBufferOffset
;
349 DPRINT("New Size of DataRuns: %ld\n", *UsedBufferSize
);
355 DecodeRun(PUCHAR DataRun
,
356 LONGLONG
*DataRunOffset
,
357 ULONGLONG
*DataRunLength
)
359 UCHAR DataRunOffsetSize
;
360 UCHAR DataRunLengthSize
;
363 DataRunOffsetSize
= (*DataRun
>> 4) & 0xF;
364 DataRunLengthSize
= *DataRun
& 0xF;
368 for (i
= 0; i
< DataRunLengthSize
; i
++)
370 *DataRunLength
+= ((ULONG64
)*DataRun
) << (i
* 8);
374 /* NTFS 3+ sparse files */
375 if (DataRunOffsetSize
== 0)
381 for (i
= 0; i
< DataRunOffsetSize
- 1; i
++)
383 *DataRunOffset
+= ((ULONG64
)*DataRun
) << (i
* 8);
386 /* The last byte contains sign so we must process it different way. */
387 *DataRunOffset
= ((LONG64
)(CHAR
)(*(DataRun
++)) << (i
* 8)) + *DataRunOffset
;
390 DPRINT("DataRunOffsetSize: %x\n", DataRunOffsetSize
);
391 DPRINT("DataRunLengthSize: %x\n", DataRunLengthSize
);
392 DPRINT("DataRunOffset: %x\n", *DataRunOffset
);
393 DPRINT("DataRunLength: %x\n", *DataRunLength
);
399 FindRun(PNTFS_ATTR_RECORD NresAttr
,
404 if (vcn
< NresAttr
->NonResident
.LowestVCN
|| vcn
> NresAttr
->NonResident
.HighestVCN
)
407 DecodeRun((PUCHAR
)((ULONG_PTR
)NresAttr
+ NresAttr
->NonResident
.MappingPairsOffset
), (PLONGLONG
)lcn
, count
);
414 InternalReadNonResidentAttributes(PFIND_ATTR_CONTXT Context
)
417 PNTFS_ATTR_RECORD Attribute
;
418 PNTFS_ATTR_CONTEXT ListContext
;
420 DPRINT("InternalReadNonResidentAttributes(%p)\n", Context
);
422 Attribute
= Context
->CurrAttr
;
423 ASSERT(Attribute
->Type
== AttributeAttributeList
);
425 if (Context
->OnlyResident
)
427 Context
->NonResidentStart
= NULL
;
428 Context
->NonResidentEnd
= NULL
;
429 return STATUS_SUCCESS
;
432 if (Context
->NonResidentStart
!= NULL
)
434 return STATUS_FILE_CORRUPT_ERROR
;
437 ListContext
= PrepareAttributeContext(Attribute
);
438 ListSize
= AttributeDataLength(&ListContext
->Record
);
439 if (ListSize
> 0xFFFFFFFF)
441 ReleaseAttributeContext(ListContext
);
442 return STATUS_BUFFER_OVERFLOW
;
445 Context
->NonResidentStart
= ExAllocatePoolWithTag(NonPagedPool
, (ULONG
)ListSize
, TAG_NTFS
);
446 if (Context
->NonResidentStart
== NULL
)
448 ReleaseAttributeContext(ListContext
);
449 return STATUS_INSUFFICIENT_RESOURCES
;
452 if (ReadAttribute(Context
->Vcb
, ListContext
, 0, (PCHAR
)Context
->NonResidentStart
, (ULONG
)ListSize
) != ListSize
)
454 ExFreePoolWithTag(Context
->NonResidentStart
, TAG_NTFS
);
455 Context
->NonResidentStart
= NULL
;
456 ReleaseAttributeContext(ListContext
);
457 return STATUS_FILE_CORRUPT_ERROR
;
460 ReleaseAttributeContext(ListContext
);
461 Context
->NonResidentEnd
= (PNTFS_ATTR_RECORD
)((PCHAR
)Context
->NonResidentStart
+ ListSize
);
462 return STATUS_SUCCESS
;
467 InternalGetNextAttribute(PFIND_ATTR_CONTXT Context
)
469 PNTFS_ATTR_RECORD NextAttribute
;
471 if (Context
->CurrAttr
== (PVOID
)-1)
476 if (Context
->CurrAttr
>= Context
->FirstAttr
&&
477 Context
->CurrAttr
< Context
->LastAttr
)
479 if (Context
->CurrAttr
->Length
== 0)
481 DPRINT1("Broken length!\n");
482 Context
->CurrAttr
= (PVOID
)-1;
486 NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)Context
->CurrAttr
+ Context
->CurrAttr
->Length
);
488 if (NextAttribute
> Context
->LastAttr
|| NextAttribute
< Context
->FirstAttr
)
490 DPRINT1("Broken length: 0x%lx!\n", Context
->CurrAttr
->Length
);
491 Context
->CurrAttr
= (PVOID
)-1;
495 Context
->Offset
+= ((ULONG_PTR
)NextAttribute
- (ULONG_PTR
)Context
->CurrAttr
);
496 Context
->CurrAttr
= NextAttribute
;
498 if (Context
->CurrAttr
< Context
->LastAttr
&&
499 Context
->CurrAttr
->Type
!= AttributeEnd
)
501 return Context
->CurrAttr
;
505 if (Context
->NonResidentStart
== NULL
)
507 Context
->CurrAttr
= (PVOID
)-1;
511 if (Context
->CurrAttr
< Context
->NonResidentStart
||
512 Context
->CurrAttr
>= Context
->NonResidentEnd
)
514 Context
->CurrAttr
= Context
->NonResidentStart
;
516 else if (Context
->CurrAttr
->Length
!= 0)
518 NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)Context
->CurrAttr
+ Context
->CurrAttr
->Length
);
519 Context
->Offset
+= ((ULONG_PTR
)NextAttribute
- (ULONG_PTR
)Context
->CurrAttr
);
520 Context
->CurrAttr
= NextAttribute
;
524 DPRINT1("Broken length!\n");
525 Context
->CurrAttr
= (PVOID
)-1;
529 if (Context
->CurrAttr
< Context
->NonResidentEnd
&&
530 Context
->CurrAttr
->Type
!= AttributeEnd
)
532 return Context
->CurrAttr
;
535 Context
->CurrAttr
= (PVOID
)-1;
540 FindFirstAttribute(PFIND_ATTR_CONTXT Context
,
541 PDEVICE_EXTENSION Vcb
,
542 PFILE_RECORD_HEADER FileRecord
,
543 BOOLEAN OnlyResident
,
544 PNTFS_ATTR_RECORD
* Attribute
)
548 DPRINT("FindFistAttribute(%p, %p, %p, %p, %u, %p)\n", Context
, Vcb
, FileRecord
, OnlyResident
, Attribute
);
551 Context
->OnlyResident
= OnlyResident
;
552 Context
->FirstAttr
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ FileRecord
->AttributeOffset
);
553 Context
->CurrAttr
= Context
->FirstAttr
;
554 Context
->LastAttr
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ FileRecord
->BytesInUse
);
555 Context
->NonResidentStart
= NULL
;
556 Context
->NonResidentEnd
= NULL
;
557 Context
->Offset
= FileRecord
->AttributeOffset
;
559 if (Context
->FirstAttr
->Type
== AttributeEnd
)
561 Context
->CurrAttr
= (PVOID
)-1;
562 return STATUS_END_OF_FILE
;
564 else if (Context
->FirstAttr
->Type
== AttributeAttributeList
)
566 Status
= InternalReadNonResidentAttributes(Context
);
567 if (!NT_SUCCESS(Status
))
572 *Attribute
= InternalGetNextAttribute(Context
);
573 if (*Attribute
== NULL
)
575 return STATUS_END_OF_FILE
;
580 *Attribute
= Context
->CurrAttr
;
581 Context
->Offset
= (UCHAR
*)Context
->CurrAttr
- (UCHAR
*)FileRecord
;
584 return STATUS_SUCCESS
;
588 FindNextAttribute(PFIND_ATTR_CONTXT Context
,
589 PNTFS_ATTR_RECORD
* Attribute
)
593 DPRINT("FindNextAttribute(%p, %p)\n", Context
, Attribute
);
595 *Attribute
= InternalGetNextAttribute(Context
);
596 if (*Attribute
== NULL
)
598 return STATUS_END_OF_FILE
;
601 if (Context
->CurrAttr
->Type
!= AttributeAttributeList
)
603 return STATUS_SUCCESS
;
606 Status
= InternalReadNonResidentAttributes(Context
);
607 if (!NT_SUCCESS(Status
))
612 *Attribute
= InternalGetNextAttribute(Context
);
613 if (*Attribute
== NULL
)
615 return STATUS_END_OF_FILE
;
618 return STATUS_SUCCESS
;
622 FindCloseAttribute(PFIND_ATTR_CONTXT Context
)
624 if (Context
->NonResidentStart
!= NULL
)
626 ExFreePoolWithTag(Context
->NonResidentStart
, TAG_NTFS
);
627 Context
->NonResidentStart
= NULL
;
633 NtfsDumpFileNameAttribute(PNTFS_ATTR_RECORD Attribute
)
635 PFILENAME_ATTRIBUTE FileNameAttr
;
637 DbgPrint(" $FILE_NAME ");
639 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
641 FileNameAttr
= (PFILENAME_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
642 DbgPrint(" (%x) '%.*S' ", FileNameAttr
->NameType
, FileNameAttr
->NameLength
, FileNameAttr
->Name
);
643 DbgPrint(" '%x' \n", FileNameAttr
->FileAttributes
);
644 DbgPrint(" AllocatedSize: %I64u\nDataSize: %I64u\n", FileNameAttr
->AllocatedSize
, FileNameAttr
->DataSize
);
650 NtfsDumpStandardInformationAttribute(PNTFS_ATTR_RECORD Attribute
)
652 PSTANDARD_INFORMATION StandardInfoAttr
;
654 DbgPrint(" $STANDARD_INFORMATION ");
656 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
658 StandardInfoAttr
= (PSTANDARD_INFORMATION
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
659 DbgPrint(" '%x' ", StandardInfoAttr
->FileAttribute
);
665 NtfsDumpVolumeNameAttribute(PNTFS_ATTR_RECORD Attribute
)
669 DbgPrint(" $VOLUME_NAME ");
671 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
673 VolumeName
= (PWCHAR
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
674 DbgPrint(" '%.*S' ", Attribute
->Resident
.ValueLength
/ sizeof(WCHAR
), VolumeName
);
680 NtfsDumpVolumeInformationAttribute(PNTFS_ATTR_RECORD Attribute
)
682 PVOLINFO_ATTRIBUTE VolInfoAttr
;
684 DbgPrint(" $VOLUME_INFORMATION ");
686 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
688 VolInfoAttr
= (PVOLINFO_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
689 DbgPrint(" NTFS Version %u.%u Flags 0x%04hx ",
690 VolInfoAttr
->MajorVersion
,
691 VolInfoAttr
->MinorVersion
,
698 NtfsDumpIndexRootAttribute(PNTFS_ATTR_RECORD Attribute
)
700 PINDEX_ROOT_ATTRIBUTE IndexRootAttr
;
702 IndexRootAttr
= (PINDEX_ROOT_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
704 if (IndexRootAttr
->AttributeType
== AttributeFileName
)
705 ASSERT(IndexRootAttr
->CollationRule
== COLLATION_FILE_NAME
);
707 DbgPrint(" $INDEX_ROOT (%uB, %u) ", IndexRootAttr
->SizeOfEntry
, IndexRootAttr
->ClustersPerIndexRecord
);
709 if (IndexRootAttr
->Header
.Flags
== INDEX_ROOT_SMALL
)
711 DbgPrint(" (small) ");
715 ASSERT(IndexRootAttr
->Header
.Flags
== INDEX_ROOT_LARGE
);
716 DbgPrint(" (large) ");
723 NtfsDumpAttribute(PDEVICE_EXTENSION Vcb
,
724 PNTFS_ATTR_RECORD Attribute
)
729 ULONGLONG runcount
= 0;
731 switch (Attribute
->Type
)
733 case AttributeFileName
:
734 NtfsDumpFileNameAttribute(Attribute
);
737 case AttributeStandardInformation
:
738 NtfsDumpStandardInformationAttribute(Attribute
);
741 case AttributeObjectId
:
742 DbgPrint(" $OBJECT_ID ");
745 case AttributeSecurityDescriptor
:
746 DbgPrint(" $SECURITY_DESCRIPTOR ");
749 case AttributeVolumeName
:
750 NtfsDumpVolumeNameAttribute(Attribute
);
753 case AttributeVolumeInformation
:
754 NtfsDumpVolumeInformationAttribute(Attribute
);
759 //DataBuf = ExAllocatePool(NonPagedPool,AttributeLengthAllocated(Attribute));
762 case AttributeIndexRoot
:
763 NtfsDumpIndexRootAttribute(Attribute
);
766 case AttributeIndexAllocation
:
767 DbgPrint(" $INDEX_ALLOCATION ");
770 case AttributeBitmap
:
771 DbgPrint(" $BITMAP ");
774 case AttributeReparsePoint
:
775 DbgPrint(" $REPARSE_POINT ");
778 case AttributeEAInformation
:
779 DbgPrint(" $EA_INFORMATION ");
786 case AttributePropertySet
:
787 DbgPrint(" $PROPERTY_SET ");
790 case AttributeLoggedUtilityStream
:
791 DbgPrint(" $LOGGED_UTILITY_STREAM ");
795 DbgPrint(" Attribute %lx ",
800 if (Attribute
->Type
!= AttributeAttributeList
)
802 if (Attribute
->NameLength
!= 0)
804 Name
.Length
= Attribute
->NameLength
* sizeof(WCHAR
);
805 Name
.MaximumLength
= Name
.Length
;
806 Name
.Buffer
= (PWCHAR
)((ULONG_PTR
)Attribute
+ Attribute
->NameOffset
);
808 DbgPrint("'%wZ' ", &Name
);
812 Attribute
->IsNonResident
? "non-resident" : "resident");
814 if (Attribute
->IsNonResident
)
816 FindRun(Attribute
,0,&lcn
, &runcount
);
818 DbgPrint(" AllocatedSize %I64u DataSize %I64u InitilizedSize %I64u\n",
819 Attribute
->NonResident
.AllocatedSize
, Attribute
->NonResident
.DataSize
, Attribute
->NonResident
.InitializedSize
);
820 DbgPrint(" logical clusters: %I64u - %I64u\n",
821 lcn
, lcn
+ runcount
- 1);
824 DbgPrint(" %u bytes of data\n", Attribute
->Resident
.ValueLength
);
829 VOID
NtfsDumpDataRunData(PUCHAR DataRun
)
831 UCHAR DataRunOffsetSize
;
832 UCHAR DataRunLengthSize
;
835 DbgPrint("%02x ", *DataRun
);
840 DataRunOffsetSize
= (*DataRun
>> 4) & 0xF;
841 DataRunLengthSize
= *DataRun
& 0xF;
844 for (i
= 0; i
< DataRunLengthSize
; i
++)
846 DbgPrint("%02x ", *DataRun
);
850 for (i
= 0; i
< DataRunOffsetSize
; i
++)
852 DbgPrint("%02x ", *DataRun
);
856 NtfsDumpDataRunData(DataRun
);
861 NtfsDumpDataRuns(PVOID StartOfRun
,
862 ULONGLONG CurrentLCN
)
864 PUCHAR DataRun
= StartOfRun
;
865 LONGLONG DataRunOffset
;
866 ULONGLONG DataRunLength
;
870 DPRINT1("Dumping data runs.\n\tData:\n\t\t");
871 NtfsDumpDataRunData(StartOfRun
);
872 DbgPrint("\n\tRuns:\n\t\tOff\t\tLCN\t\tLength\n");
875 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
877 if (DataRunOffset
!= -1)
878 CurrentLCN
+= DataRunOffset
;
880 DbgPrint("\t\t%I64d\t", DataRunOffset
);
881 if (DataRunOffset
< 99999)
883 DbgPrint("%I64u\t", CurrentLCN
);
884 if (CurrentLCN
< 99999)
886 DbgPrint("%I64u\n", DataRunLength
);
889 DbgPrint("\t\t00\n");
891 NtfsDumpDataRuns(DataRun
, CurrentLCN
);
896 NtfsDumpFileAttributes(PDEVICE_EXTENSION Vcb
,
897 PFILE_RECORD_HEADER FileRecord
)
900 FIND_ATTR_CONTXT Context
;
901 PNTFS_ATTR_RECORD Attribute
;
903 Status
= FindFirstAttribute(&Context
, Vcb
, FileRecord
, FALSE
, &Attribute
);
904 while (NT_SUCCESS(Status
))
906 NtfsDumpAttribute(Vcb
, Attribute
);
908 Status
= FindNextAttribute(&Context
, &Attribute
);
911 FindCloseAttribute(&Context
);
915 GetFileNameFromRecord(PDEVICE_EXTENSION Vcb
,
916 PFILE_RECORD_HEADER FileRecord
,
919 FIND_ATTR_CONTXT Context
;
920 PNTFS_ATTR_RECORD Attribute
;
921 PFILENAME_ATTRIBUTE Name
;
924 Status
= FindFirstAttribute(&Context
, Vcb
, FileRecord
, FALSE
, &Attribute
);
925 while (NT_SUCCESS(Status
))
927 if (Attribute
->Type
== AttributeFileName
)
929 Name
= (PFILENAME_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
930 if (Name
->NameType
== NameType
||
931 (Name
->NameType
== NTFS_FILE_NAME_WIN32_AND_DOS
&& NameType
== NTFS_FILE_NAME_WIN32
) ||
932 (Name
->NameType
== NTFS_FILE_NAME_WIN32_AND_DOS
&& NameType
== NTFS_FILE_NAME_DOS
))
934 FindCloseAttribute(&Context
);
939 Status
= FindNextAttribute(&Context
, &Attribute
);
942 FindCloseAttribute(&Context
);
948 * Returns the minimum number of bytes needed to represent the value of a
949 * 64-bit number. Used to encode data runs.
952 GetPackedByteCount(LONGLONG NumberToPack
,
958 if (NumberToPack
>= 0x0100000000000000)
960 if (NumberToPack
>= 0x0001000000000000)
962 if (NumberToPack
>= 0x0000010000000000)
964 if (NumberToPack
>= 0x0000000100000000)
966 if (NumberToPack
>= 0x0000000001000000)
968 if (NumberToPack
>= 0x0000000000010000)
970 if (NumberToPack
>= 0x0000000000000100)
975 if (NumberToPack
> 0)
977 // we have to make sure the number that gets encoded won't be interpreted as negative
978 if (NumberToPack
>= 0x0080000000000000)
980 if (NumberToPack
>= 0x0000800000000000)
982 if (NumberToPack
>= 0x0000008000000000)
984 if (NumberToPack
>= 0x0000000080000000)
986 if (NumberToPack
>= 0x0000000000800000)
988 if (NumberToPack
>= 0x0000000000008000)
990 if (NumberToPack
>= 0x0000000000000080)
997 if (NumberToPack
<= 0xff80000000000000)
999 if (NumberToPack
<= 0xffff800000000000)
1001 if (NumberToPack
<= 0xffffff8000000000)
1003 if (NumberToPack
<= 0xffffffff80000000)
1005 if (NumberToPack
<= 0xffffffffff800000)
1007 if (NumberToPack
<= 0xffffffffffff8000)
1009 if (NumberToPack
<= 0xffffffffffffff80)
1017 GetLastClusterInDataRun(PDEVICE_EXTENSION Vcb
, PNTFS_ATTR_RECORD Attribute
, PULONGLONG LastCluster
)
1019 LONGLONG DataRunOffset
;
1020 ULONGLONG DataRunLength
;
1021 LONGLONG DataRunStartLCN
;
1023 ULONGLONG LastLCN
= 0;
1024 PUCHAR DataRun
= (PUCHAR
)Attribute
+ Attribute
->NonResident
.MappingPairsOffset
;
1026 if (!Attribute
->IsNonResident
)
1027 return STATUS_INVALID_PARAMETER
;
1031 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
1033 if (DataRunOffset
!= -1)
1036 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
1037 LastLCN
= DataRunStartLCN
;
1038 *LastCluster
= LastLCN
+ DataRunLength
- 1;
1045 return STATUS_SUCCESS
;
1048 PSTANDARD_INFORMATION
1049 GetStandardInformationFromRecord(PDEVICE_EXTENSION Vcb
,
1050 PFILE_RECORD_HEADER FileRecord
)
1053 FIND_ATTR_CONTXT Context
;
1054 PNTFS_ATTR_RECORD Attribute
;
1055 PSTANDARD_INFORMATION StdInfo
;
1057 Status
= FindFirstAttribute(&Context
, Vcb
, FileRecord
, FALSE
, &Attribute
);
1058 while (NT_SUCCESS(Status
))
1060 if (Attribute
->Type
== AttributeStandardInformation
)
1062 StdInfo
= (PSTANDARD_INFORMATION
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
1063 FindCloseAttribute(&Context
);
1067 Status
= FindNextAttribute(&Context
, &Attribute
);
1070 FindCloseAttribute(&Context
);
1075 GetBestFileNameFromRecord(PDEVICE_EXTENSION Vcb
,
1076 PFILE_RECORD_HEADER FileRecord
)
1078 PFILENAME_ATTRIBUTE FileName
;
1080 FileName
= GetFileNameFromRecord(Vcb
, FileRecord
, NTFS_FILE_NAME_POSIX
);
1081 if (FileName
== NULL
)
1083 FileName
= GetFileNameFromRecord(Vcb
, FileRecord
, NTFS_FILE_NAME_WIN32
);
1084 if (FileName
== NULL
)
1086 FileName
= GetFileNameFromRecord(Vcb
, FileRecord
, NTFS_FILE_NAME_DOS
);