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 or if we fail to allocate a
66 * buffer for the new data runs.
67 * STATUS_INSUFFICIENT_RESOURCES or STATUS_UNSUCCESSFUL if FsRtlAddLargeMcbEntry() fails.
68 * STATUS_BUFFER_TOO_SMALL if ConvertLargeMCBToDataRuns() fails.
69 * STATUS_NOT_IMPLEMENTED if we need to migrate the attribute to an attribute list (TODO).
72 * Clusters should have been allocated previously with NtfsAllocateClusters().
78 PNTFS_ATTR_CONTEXT AttrContext
,
80 PFILE_RECORD_HEADER FileRecord
,
81 ULONGLONG NextAssignedCluster
,
85 PUCHAR DataRun
= (PUCHAR
)&AttrContext
->Record
+ AttrContext
->Record
.NonResident
.MappingPairsOffset
;
87 PNTFS_ATTR_RECORD DestinationAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ AttrOffset
);
88 LARGE_MCB DataRunsMCB
;
89 ULONG NextAttributeOffset
= AttrOffset
+ AttrContext
->Record
.Length
;
90 ULONGLONG NextVBN
= AttrContext
->Record
.NonResident
.LowestVCN
;
92 // Allocate some memory for the RunBuffer
94 ULONG RunBufferOffset
= 0;
96 if (!AttrContext
->Record
.IsNonResident
)
97 return STATUS_INVALID_PARAMETER
;
99 RunBuffer
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
102 DPRINT1("ERROR: Couldn't allocate memory for data runs!\n");
103 return STATUS_INSUFFICIENT_RESOURCES
;
106 // Convert the data runs to a map control block
107 Status
= ConvertDataRunsToLargeMCB(DataRun
, &DataRunsMCB
, &NextVBN
);
108 if (!NT_SUCCESS(Status
))
110 DPRINT1("Unable to convert data runs to MCB (probably ran out of memory)!\n");
111 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
115 // Add newly-assigned clusters to mcb
117 if (!FsRtlAddLargeMcbEntry(&DataRunsMCB
,
122 ExRaiseStatus(STATUS_UNSUCCESSFUL
);
124 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
125 FsRtlUninitializeLargeMcb(&DataRunsMCB
);
126 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
127 _SEH2_YIELD(_SEH2_GetExceptionCode());
131 // Convert the map control block back to encoded data runs
132 ConvertLargeMCBToDataRuns(&DataRunsMCB
, RunBuffer
, Vcb
->NtfsInfo
.BytesPerCluster
, &RunBufferOffset
);
134 // Get the amount of free space between the start of the of the first data run and the attribute end
135 DataRunMaxLength
= AttrContext
->Record
.Length
- AttrContext
->Record
.NonResident
.MappingPairsOffset
;
137 // Do we need to extend the attribute (or convert to attribute list)?
138 if (DataRunMaxLength
< RunBufferOffset
)
140 PNTFS_ATTR_RECORD NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ NextAttributeOffset
);
141 DataRunMaxLength
+= Vcb
->NtfsInfo
.BytesPerFileRecord
- NextAttributeOffset
- (sizeof(ULONG
) * 2);
143 // Can we move the end of the attribute?
144 if (NextAttribute
->Type
!= AttributeEnd
|| DataRunMaxLength
< RunBufferOffset
- 1)
146 DPRINT1("FIXME: Need to create attribute list! Max Data Run Length available: %d\n", DataRunMaxLength
);
147 if (NextAttribute
->Type
!= AttributeEnd
)
148 DPRINT1("There's another attribute after this one with type %0xlx\n", NextAttribute
->Type
);
149 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
150 FsRtlUninitializeLargeMcb(&DataRunsMCB
);
151 return STATUS_NOT_IMPLEMENTED
;
154 // calculate position of end markers
155 NextAttributeOffset
= AttrOffset
+ AttrContext
->Record
.NonResident
.MappingPairsOffset
+ RunBufferOffset
;
156 NextAttributeOffset
= ALIGN_UP_BY(NextAttributeOffset
, 8);
158 // Write the end markers
159 NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ NextAttributeOffset
);
160 NextAttribute
->Type
= AttributeEnd
;
161 NextAttribute
->Length
= FILE_RECORD_END
;
164 DestinationAttribute
->Length
= NextAttributeOffset
- AttrOffset
;
165 AttrContext
->Record
.Length
= DestinationAttribute
->Length
;
167 // We need to increase the FileRecord size
168 FileRecord
->BytesInUse
= NextAttributeOffset
+ (sizeof(ULONG
) * 2);
171 // NOTE: from this point on the original attribute record will contain invalid data in it's runbuffer
172 // TODO: Elegant fix? Could we free the old Record and allocate a new one without issue?
175 DestinationAttribute
->NonResident
.HighestVCN
=
176 AttrContext
->Record
.NonResident
.HighestVCN
= max(NextVBN
- 1 + RunLength
,
177 AttrContext
->Record
.NonResident
.HighestVCN
);
179 // Write data runs to destination attribute
180 RtlCopyMemory((PVOID
)((ULONG_PTR
)DestinationAttribute
+ DestinationAttribute
->NonResident
.MappingPairsOffset
),
184 // Update the file record
185 Status
= UpdateFileRecord(Vcb
, AttrContext
->FileMFTIndex
, FileRecord
);
187 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
188 FsRtlUninitializeLargeMcb(&DataRunsMCB
);
190 NtfsDumpDataRuns((PUCHAR
)((ULONG_PTR
)DestinationAttribute
+ DestinationAttribute
->NonResident
.MappingPairsOffset
), 0);
196 * @name ConvertDataRunsToLargeMCB
199 * Converts binary data runs to a map control block.
202 * Pointer to the run data
205 * Pointer to an unitialized LARGE_MCB structure.
208 * STATUS_SUCCESS on success, STATUS_INSUFFICIENT_RESOURCES or STATUS_UNSUCCESSFUL if we fail to
209 * initialize the mcb or add an entry.
212 * Initializes the LARGE_MCB pointed to by DataRunsMCB. If this function succeeds, you
213 * need to call FsRtlUninitializeLargeMcb() when you're done with DataRunsMCB. This
214 * function will ensure the LargeMCB has been unitialized in case of failure.
218 ConvertDataRunsToLargeMCB(PUCHAR DataRun
,
219 PLARGE_MCB DataRunsMCB
,
222 LONGLONG DataRunOffset
;
223 ULONGLONG DataRunLength
;
224 LONGLONG DataRunStartLCN
;
225 ULONGLONG LastLCN
= 0;
227 // Initialize the MCB, potentially catch an exception
229 FsRtlInitializeLargeMcb(DataRunsMCB
, NonPagedPool
);
230 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
231 _SEH2_YIELD(return _SEH2_GetExceptionCode());
234 while (*DataRun
!= 0)
236 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
238 if (DataRunOffset
!= -1)
241 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
242 LastLCN
= DataRunStartLCN
;
245 if (!FsRtlAddLargeMcbEntry(DataRunsMCB
,
250 ExRaiseStatus(STATUS_UNSUCCESSFUL
);
252 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
253 FsRtlUninitializeLargeMcb(DataRunsMCB
);
254 _SEH2_YIELD(return _SEH2_GetExceptionCode());
259 *pNextVBN
+= DataRunLength
;
262 return STATUS_SUCCESS
;
266 * @name ConvertLargeMCBToDataRuns
269 * Converts a map control block to a series of encoded data runs (used by non-resident attributes).
272 * Pointer to a LARGE_MCB structure describing the data runs.
275 * Pointer to the buffer that will receive the encoded data runs.
277 * @param MaxBufferSize
278 * Size of RunBuffer, in bytes.
280 * @param UsedBufferSize
281 * Pointer to a ULONG that will receive the size of the data runs in bytes. Can't be NULL.
284 * STATUS_SUCCESS on success, STATUS_BUFFER_TOO_SMALL if RunBuffer is too small to contain the
289 ConvertLargeMCBToDataRuns(PLARGE_MCB DataRunsMCB
,
292 PULONG UsedBufferSize
)
294 NTSTATUS Status
= STATUS_SUCCESS
;
295 ULONG RunBufferOffset
= 0;
296 LONGLONG DataRunOffset
;
297 ULONGLONG LastLCN
= 0;
298 LONGLONG Vbn
, Lbn
, Count
;
302 DPRINT("\t[Vbn, Lbn, Count]\n");
304 // convert each mcb entry to a data run
305 for (i
= 0; FsRtlGetNextLargeMcbEntry(DataRunsMCB
, i
, &Vbn
, &Lbn
, &Count
); i
++)
307 UCHAR DataRunOffsetSize
= 0;
308 UCHAR DataRunLengthSize
= 0;
309 UCHAR ControlByte
= 0;
312 DPRINT("\t[%I64d, %I64d,%I64d]\n", Vbn
, Lbn
, Count
);
314 // TODO: check for holes and convert to sparse runs
315 DataRunOffset
= Lbn
- LastLCN
;
318 // now we need to determine how to represent DataRunOffset with the minimum number of bytes
319 DPRINT("Determining how many bytes needed to represent %I64x\n", DataRunOffset
);
320 DataRunOffsetSize
= GetPackedByteCount(DataRunOffset
, TRUE
);
321 DPRINT("%d bytes needed.\n", DataRunOffsetSize
);
323 // determine how to represent DataRunLengthSize with the minimum number of bytes
324 DPRINT("Determining how many bytes needed to represent %I64x\n", Count
);
325 DataRunLengthSize
= GetPackedByteCount(Count
, TRUE
);
326 DPRINT("%d bytes needed.\n", DataRunLengthSize
);
328 // ensure the next data run + end marker would be > Max buffer size
329 if (RunBufferOffset
+ 2 + DataRunLengthSize
+ DataRunOffsetSize
> MaxBufferSize
)
331 Status
= STATUS_BUFFER_TOO_SMALL
;
332 DPRINT1("FIXME: Ran out of room in buffer for data runs!\n");
336 // pack and copy the control byte
337 ControlByte
= (DataRunOffsetSize
<< 4) + DataRunLengthSize
;
338 RunBuffer
[RunBufferOffset
++] = ControlByte
;
340 // copy DataRunLength
341 RtlCopyMemory(RunBuffer
+ RunBufferOffset
, &Count
, DataRunLengthSize
);
342 RunBufferOffset
+= DataRunLengthSize
;
344 // copy DataRunOffset
345 RtlCopyMemory(RunBuffer
+ RunBufferOffset
, &DataRunOffset
, DataRunOffsetSize
);
346 RunBufferOffset
+= DataRunOffsetSize
;
350 RunBuffer
[RunBufferOffset
++] = 0;
352 *UsedBufferSize
= RunBufferOffset
;
353 DPRINT("New Size of DataRuns: %ld\n", *UsedBufferSize
);
359 DecodeRun(PUCHAR DataRun
,
360 LONGLONG
*DataRunOffset
,
361 ULONGLONG
*DataRunLength
)
363 UCHAR DataRunOffsetSize
;
364 UCHAR DataRunLengthSize
;
367 DataRunOffsetSize
= (*DataRun
>> 4) & 0xF;
368 DataRunLengthSize
= *DataRun
& 0xF;
372 for (i
= 0; i
< DataRunLengthSize
; i
++)
374 *DataRunLength
+= ((ULONG64
)*DataRun
) << (i
* 8);
378 /* NTFS 3+ sparse files */
379 if (DataRunOffsetSize
== 0)
385 for (i
= 0; i
< DataRunOffsetSize
- 1; i
++)
387 *DataRunOffset
+= ((ULONG64
)*DataRun
) << (i
* 8);
390 /* The last byte contains sign so we must process it different way. */
391 *DataRunOffset
= ((LONG64
)(CHAR
)(*(DataRun
++)) << (i
* 8)) + *DataRunOffset
;
394 DPRINT("DataRunOffsetSize: %x\n", DataRunOffsetSize
);
395 DPRINT("DataRunLengthSize: %x\n", DataRunLengthSize
);
396 DPRINT("DataRunOffset: %x\n", *DataRunOffset
);
397 DPRINT("DataRunLength: %x\n", *DataRunLength
);
403 FindRun(PNTFS_ATTR_RECORD NresAttr
,
408 if (vcn
< NresAttr
->NonResident
.LowestVCN
|| vcn
> NresAttr
->NonResident
.HighestVCN
)
411 DecodeRun((PUCHAR
)((ULONG_PTR
)NresAttr
+ NresAttr
->NonResident
.MappingPairsOffset
), (PLONGLONG
)lcn
, count
);
420 * Shrinks the allocation size of a non-resident attribute by a given number of clusters.
421 * Frees the clusters from the volume's $BITMAP file as well as the attribute's data runs.
424 * Pointer to an NTFS_VCB for the destination volume.
427 * Pointer to an NTFS_ATTR_CONTEXT describing the attribute from which the clusters will be freed.
430 * Byte offset of the destination attribute relative to its file record.
433 * Pointer to a complete copy of the file record containing the attribute. Must be at least
434 * Vcb->NtfsInfo.BytesPerFileRecord bytes long.
436 * @param ClustersToFree
437 * Number of clusters that should be freed from the end of the data stream. Must be no more
438 * Than the number of clusters assigned to the attribute (HighestVCN + 1).
441 * STATUS_SUCCESS on success. STATUS_INVALID_PARAMETER if AttrContext describes a resident attribute,
442 * or if the caller requested more clusters be freed than the attribute has been allocated.
443 * STATUS_INSUFFICIENT_RESOURCES if allocating a buffer for the data runs fails or
444 * if ConvertDataRunsToLargeMCB() fails.
445 * STATUS_BUFFER_TOO_SMALL if ConvertLargeMCBToDataRuns() fails.
450 FreeClusters(PNTFS_VCB Vcb
,
451 PNTFS_ATTR_CONTEXT AttrContext
,
453 PFILE_RECORD_HEADER FileRecord
,
454 ULONG ClustersToFree
)
456 NTSTATUS Status
= STATUS_SUCCESS
;
457 ULONG ClustersLeftToFree
= ClustersToFree
;
459 // convert data runs to mcb
460 PUCHAR DataRun
= (PUCHAR
)&AttrContext
->Record
+ AttrContext
->Record
.NonResident
.MappingPairsOffset
;
461 PNTFS_ATTR_RECORD DestinationAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ AttrOffset
);
462 LARGE_MCB DataRunsMCB
;
463 ULONG NextAttributeOffset
= AttrOffset
+ AttrContext
->Record
.Length
;
464 PNTFS_ATTR_RECORD NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ NextAttributeOffset
);
465 ULONGLONG NextVBN
= AttrContext
->Record
.NonResident
.LowestVCN
;
467 // Allocate some memory for the RunBuffer
469 ULONG RunBufferOffset
= 0;
471 PFILE_RECORD_HEADER BitmapRecord
;
472 PNTFS_ATTR_CONTEXT DataContext
;
473 ULONGLONG BitmapDataSize
;
478 if (!AttrContext
->Record
.IsNonResident
)
480 return STATUS_INVALID_PARAMETER
;
483 RunBuffer
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
486 DPRINT1("ERROR: Couldn't allocate memory for data runs!\n");
487 return STATUS_INSUFFICIENT_RESOURCES
;
490 // Convert the data runs to a map control block
491 Status
= ConvertDataRunsToLargeMCB(DataRun
, &DataRunsMCB
, &NextVBN
);
492 if (!NT_SUCCESS(Status
))
494 DPRINT1("Unable to convert data runs to MCB (probably ran out of memory)!\n");
495 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
499 BitmapRecord
= ExAllocatePoolWithTag(NonPagedPool
,
500 Vcb
->NtfsInfo
.BytesPerFileRecord
,
502 if (BitmapRecord
== NULL
)
504 DPRINT1("Error: Unable to allocate memory for bitmap file record!\n");
505 FsRtlUninitializeLargeMcb(&DataRunsMCB
);
506 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
507 return STATUS_NO_MEMORY
;
510 Status
= ReadFileRecord(Vcb
, NTFS_FILE_BITMAP
, BitmapRecord
);
511 if (!NT_SUCCESS(Status
))
513 DPRINT1("Error: Unable to read file record for bitmap!\n");
514 FsRtlUninitializeLargeMcb(&DataRunsMCB
);
515 ExFreePoolWithTag(BitmapRecord
, TAG_NTFS
);
516 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
520 Status
= FindAttribute(Vcb
, BitmapRecord
, AttributeData
, L
"", 0, &DataContext
, NULL
);
521 if (!NT_SUCCESS(Status
))
523 DPRINT1("Error: Unable to find data attribute for bitmap file!\n");
524 FsRtlUninitializeLargeMcb(&DataRunsMCB
);
525 ExFreePoolWithTag(BitmapRecord
, TAG_NTFS
);
526 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
530 BitmapDataSize
= AttributeDataLength(&DataContext
->Record
);
531 BitmapDataSize
= min(BitmapDataSize
, 0xffffffff);
532 ASSERT((BitmapDataSize
* 8) >= Vcb
->NtfsInfo
.ClusterCount
);
533 BitmapData
= ExAllocatePoolWithTag(NonPagedPool
, ROUND_UP(BitmapDataSize
, Vcb
->NtfsInfo
.BytesPerSector
), TAG_NTFS
);
534 if (BitmapData
== NULL
)
536 DPRINT1("Error: Unable to allocate memory for bitmap file data!\n");
537 ReleaseAttributeContext(DataContext
);
538 FsRtlUninitializeLargeMcb(&DataRunsMCB
);
539 ExFreePoolWithTag(BitmapRecord
, TAG_NTFS
);
540 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
544 ReadAttribute(Vcb
, DataContext
, 0, (PCHAR
)BitmapData
, (ULONG
)BitmapDataSize
);
546 RtlInitializeBitMap(&Bitmap
, (PULONG
)BitmapData
, Vcb
->NtfsInfo
.ClusterCount
);
548 // free clusters in $BITMAP file
549 while (ClustersLeftToFree
> 0)
551 LONGLONG LargeVbn
, LargeLbn
;
553 if (!FsRtlLookupLastLargeMcbEntry(&DataRunsMCB
, &LargeVbn
, &LargeLbn
))
555 Status
= STATUS_INVALID_PARAMETER
;
556 DPRINT1("DRIVER ERROR: FreeClusters called to free %lu clusters, which is %lu more clusters than are assigned to attribute!",
564 // deallocate this cluster
565 RtlClearBits(&Bitmap
, LargeLbn
, 1);
567 FsRtlTruncateLargeMcb(&DataRunsMCB
, AttrContext
->Record
.NonResident
.HighestVCN
);
568 AttrContext
->Record
.NonResident
.HighestVCN
= min(AttrContext
->Record
.NonResident
.HighestVCN
, AttrContext
->Record
.NonResident
.HighestVCN
- 1);
569 ClustersLeftToFree
--;
572 // update $BITMAP file on disk
573 Status
= WriteAttribute(Vcb
, DataContext
, 0, BitmapData
, (ULONG
)BitmapDataSize
, &LengthWritten
);
574 if (!NT_SUCCESS(Status
))
576 ReleaseAttributeContext(DataContext
);
577 FsRtlUninitializeLargeMcb(&DataRunsMCB
);
578 ExFreePoolWithTag(BitmapData
, TAG_NTFS
);
579 ExFreePoolWithTag(BitmapRecord
, TAG_NTFS
);
580 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
584 ReleaseAttributeContext(DataContext
);
585 ExFreePoolWithTag(BitmapData
, TAG_NTFS
);
586 ExFreePoolWithTag(BitmapRecord
, TAG_NTFS
);
588 // Convert the map control block back to encoded data runs
589 ConvertLargeMCBToDataRuns(&DataRunsMCB
, RunBuffer
, Vcb
->NtfsInfo
.BytesPerCluster
, &RunBufferOffset
);
592 DestinationAttribute
->NonResident
.HighestVCN
= AttrContext
->Record
.NonResident
.HighestVCN
;
594 // Write data runs to destination attribute
595 RtlCopyMemory((PVOID
)((ULONG_PTR
)DestinationAttribute
+ DestinationAttribute
->NonResident
.MappingPairsOffset
),
599 if (NextAttribute
->Type
== AttributeEnd
)
601 // update attribute length
602 AttrContext
->Record
.Length
= ALIGN_UP_BY(AttrContext
->Record
.NonResident
.MappingPairsOffset
+ RunBufferOffset
, 8);
603 DestinationAttribute
->Length
= AttrContext
->Record
.Length
;
606 NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)DestinationAttribute
+ DestinationAttribute
->Length
);
607 NextAttribute
->Type
= AttributeEnd
;
608 NextAttribute
->Length
= FILE_RECORD_END
;
610 // update file record length
611 FileRecord
->BytesInUse
= AttrOffset
+ DestinationAttribute
->Length
+ (sizeof(ULONG
) * 2);
614 // Update the file record
615 Status
= UpdateFileRecord(Vcb
, AttrContext
->FileMFTIndex
, FileRecord
);
617 FsRtlUninitializeLargeMcb(&DataRunsMCB
);
618 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
620 NtfsDumpDataRuns((PUCHAR
)((ULONG_PTR
)DestinationAttribute
+ DestinationAttribute
->NonResident
.MappingPairsOffset
), 0);
627 InternalReadNonResidentAttributes(PFIND_ATTR_CONTXT Context
)
630 PNTFS_ATTR_RECORD Attribute
;
631 PNTFS_ATTR_CONTEXT ListContext
;
633 DPRINT("InternalReadNonResidentAttributes(%p)\n", Context
);
635 Attribute
= Context
->CurrAttr
;
636 ASSERT(Attribute
->Type
== AttributeAttributeList
);
638 if (Context
->OnlyResident
)
640 Context
->NonResidentStart
= NULL
;
641 Context
->NonResidentEnd
= NULL
;
642 return STATUS_SUCCESS
;
645 if (Context
->NonResidentStart
!= NULL
)
647 return STATUS_FILE_CORRUPT_ERROR
;
650 ListContext
= PrepareAttributeContext(Attribute
);
651 ListSize
= AttributeDataLength(&ListContext
->Record
);
652 if (ListSize
> 0xFFFFFFFF)
654 ReleaseAttributeContext(ListContext
);
655 return STATUS_BUFFER_OVERFLOW
;
658 Context
->NonResidentStart
= ExAllocatePoolWithTag(NonPagedPool
, (ULONG
)ListSize
, TAG_NTFS
);
659 if (Context
->NonResidentStart
== NULL
)
661 ReleaseAttributeContext(ListContext
);
662 return STATUS_INSUFFICIENT_RESOURCES
;
665 if (ReadAttribute(Context
->Vcb
, ListContext
, 0, (PCHAR
)Context
->NonResidentStart
, (ULONG
)ListSize
) != ListSize
)
667 ExFreePoolWithTag(Context
->NonResidentStart
, TAG_NTFS
);
668 Context
->NonResidentStart
= NULL
;
669 ReleaseAttributeContext(ListContext
);
670 return STATUS_FILE_CORRUPT_ERROR
;
673 ReleaseAttributeContext(ListContext
);
674 Context
->NonResidentEnd
= (PNTFS_ATTR_RECORD
)((PCHAR
)Context
->NonResidentStart
+ ListSize
);
675 return STATUS_SUCCESS
;
680 InternalGetNextAttribute(PFIND_ATTR_CONTXT Context
)
682 PNTFS_ATTR_RECORD NextAttribute
;
684 if (Context
->CurrAttr
== (PVOID
)-1)
689 if (Context
->CurrAttr
>= Context
->FirstAttr
&&
690 Context
->CurrAttr
< Context
->LastAttr
)
692 if (Context
->CurrAttr
->Length
== 0)
694 DPRINT1("Broken length!\n");
695 Context
->CurrAttr
= (PVOID
)-1;
699 NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)Context
->CurrAttr
+ Context
->CurrAttr
->Length
);
701 if (NextAttribute
> Context
->LastAttr
|| NextAttribute
< Context
->FirstAttr
)
703 DPRINT1("Broken length: 0x%lx!\n", Context
->CurrAttr
->Length
);
704 Context
->CurrAttr
= (PVOID
)-1;
708 Context
->Offset
+= ((ULONG_PTR
)NextAttribute
- (ULONG_PTR
)Context
->CurrAttr
);
709 Context
->CurrAttr
= NextAttribute
;
711 if (Context
->CurrAttr
< Context
->LastAttr
&&
712 Context
->CurrAttr
->Type
!= AttributeEnd
)
714 return Context
->CurrAttr
;
718 if (Context
->NonResidentStart
== NULL
)
720 Context
->CurrAttr
= (PVOID
)-1;
724 if (Context
->CurrAttr
< Context
->NonResidentStart
||
725 Context
->CurrAttr
>= Context
->NonResidentEnd
)
727 Context
->CurrAttr
= Context
->NonResidentStart
;
729 else if (Context
->CurrAttr
->Length
!= 0)
731 NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)Context
->CurrAttr
+ Context
->CurrAttr
->Length
);
732 Context
->Offset
+= ((ULONG_PTR
)NextAttribute
- (ULONG_PTR
)Context
->CurrAttr
);
733 Context
->CurrAttr
= NextAttribute
;
737 DPRINT1("Broken length!\n");
738 Context
->CurrAttr
= (PVOID
)-1;
742 if (Context
->CurrAttr
< Context
->NonResidentEnd
&&
743 Context
->CurrAttr
->Type
!= AttributeEnd
)
745 return Context
->CurrAttr
;
748 Context
->CurrAttr
= (PVOID
)-1;
753 FindFirstAttribute(PFIND_ATTR_CONTXT Context
,
754 PDEVICE_EXTENSION Vcb
,
755 PFILE_RECORD_HEADER FileRecord
,
756 BOOLEAN OnlyResident
,
757 PNTFS_ATTR_RECORD
* Attribute
)
761 DPRINT("FindFistAttribute(%p, %p, %p, %p, %u, %p)\n", Context
, Vcb
, FileRecord
, OnlyResident
, Attribute
);
764 Context
->OnlyResident
= OnlyResident
;
765 Context
->FirstAttr
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ FileRecord
->AttributeOffset
);
766 Context
->CurrAttr
= Context
->FirstAttr
;
767 Context
->LastAttr
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ FileRecord
->BytesInUse
);
768 Context
->NonResidentStart
= NULL
;
769 Context
->NonResidentEnd
= NULL
;
770 Context
->Offset
= FileRecord
->AttributeOffset
;
772 if (Context
->FirstAttr
->Type
== AttributeEnd
)
774 Context
->CurrAttr
= (PVOID
)-1;
775 return STATUS_END_OF_FILE
;
777 else if (Context
->FirstAttr
->Type
== AttributeAttributeList
)
779 Status
= InternalReadNonResidentAttributes(Context
);
780 if (!NT_SUCCESS(Status
))
785 *Attribute
= InternalGetNextAttribute(Context
);
786 if (*Attribute
== NULL
)
788 return STATUS_END_OF_FILE
;
793 *Attribute
= Context
->CurrAttr
;
794 Context
->Offset
= (UCHAR
*)Context
->CurrAttr
- (UCHAR
*)FileRecord
;
797 return STATUS_SUCCESS
;
801 FindNextAttribute(PFIND_ATTR_CONTXT Context
,
802 PNTFS_ATTR_RECORD
* Attribute
)
806 DPRINT("FindNextAttribute(%p, %p)\n", Context
, Attribute
);
808 *Attribute
= InternalGetNextAttribute(Context
);
809 if (*Attribute
== NULL
)
811 return STATUS_END_OF_FILE
;
814 if (Context
->CurrAttr
->Type
!= AttributeAttributeList
)
816 return STATUS_SUCCESS
;
819 Status
= InternalReadNonResidentAttributes(Context
);
820 if (!NT_SUCCESS(Status
))
825 *Attribute
= InternalGetNextAttribute(Context
);
826 if (*Attribute
== NULL
)
828 return STATUS_END_OF_FILE
;
831 return STATUS_SUCCESS
;
835 FindCloseAttribute(PFIND_ATTR_CONTXT Context
)
837 if (Context
->NonResidentStart
!= NULL
)
839 ExFreePoolWithTag(Context
->NonResidentStart
, TAG_NTFS
);
840 Context
->NonResidentStart
= NULL
;
846 NtfsDumpFileNameAttribute(PNTFS_ATTR_RECORD Attribute
)
848 PFILENAME_ATTRIBUTE FileNameAttr
;
850 DbgPrint(" $FILE_NAME ");
852 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
854 FileNameAttr
= (PFILENAME_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
855 DbgPrint(" (%x) '%.*S' ", FileNameAttr
->NameType
, FileNameAttr
->NameLength
, FileNameAttr
->Name
);
856 DbgPrint(" '%x' \n", FileNameAttr
->FileAttributes
);
857 DbgPrint(" AllocatedSize: %I64u\nDataSize: %I64u\n", FileNameAttr
->AllocatedSize
, FileNameAttr
->DataSize
);
863 NtfsDumpStandardInformationAttribute(PNTFS_ATTR_RECORD Attribute
)
865 PSTANDARD_INFORMATION StandardInfoAttr
;
867 DbgPrint(" $STANDARD_INFORMATION ");
869 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
871 StandardInfoAttr
= (PSTANDARD_INFORMATION
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
872 DbgPrint(" '%x' ", StandardInfoAttr
->FileAttribute
);
878 NtfsDumpVolumeNameAttribute(PNTFS_ATTR_RECORD Attribute
)
882 DbgPrint(" $VOLUME_NAME ");
884 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
886 VolumeName
= (PWCHAR
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
887 DbgPrint(" '%.*S' ", Attribute
->Resident
.ValueLength
/ sizeof(WCHAR
), VolumeName
);
893 NtfsDumpVolumeInformationAttribute(PNTFS_ATTR_RECORD Attribute
)
895 PVOLINFO_ATTRIBUTE VolInfoAttr
;
897 DbgPrint(" $VOLUME_INFORMATION ");
899 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
901 VolInfoAttr
= (PVOLINFO_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
902 DbgPrint(" NTFS Version %u.%u Flags 0x%04hx ",
903 VolInfoAttr
->MajorVersion
,
904 VolInfoAttr
->MinorVersion
,
911 NtfsDumpIndexRootAttribute(PNTFS_ATTR_RECORD Attribute
)
913 PINDEX_ROOT_ATTRIBUTE IndexRootAttr
;
915 IndexRootAttr
= (PINDEX_ROOT_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
917 if (IndexRootAttr
->AttributeType
== AttributeFileName
)
918 ASSERT(IndexRootAttr
->CollationRule
== COLLATION_FILE_NAME
);
920 DbgPrint(" $INDEX_ROOT (%uB, %u) ", IndexRootAttr
->SizeOfEntry
, IndexRootAttr
->ClustersPerIndexRecord
);
922 if (IndexRootAttr
->Header
.Flags
== INDEX_ROOT_SMALL
)
924 DbgPrint(" (small) ");
928 ASSERT(IndexRootAttr
->Header
.Flags
== INDEX_ROOT_LARGE
);
929 DbgPrint(" (large) ");
936 NtfsDumpAttribute(PDEVICE_EXTENSION Vcb
,
937 PNTFS_ATTR_RECORD Attribute
)
942 ULONGLONG runcount
= 0;
944 switch (Attribute
->Type
)
946 case AttributeFileName
:
947 NtfsDumpFileNameAttribute(Attribute
);
950 case AttributeStandardInformation
:
951 NtfsDumpStandardInformationAttribute(Attribute
);
954 case AttributeObjectId
:
955 DbgPrint(" $OBJECT_ID ");
958 case AttributeSecurityDescriptor
:
959 DbgPrint(" $SECURITY_DESCRIPTOR ");
962 case AttributeVolumeName
:
963 NtfsDumpVolumeNameAttribute(Attribute
);
966 case AttributeVolumeInformation
:
967 NtfsDumpVolumeInformationAttribute(Attribute
);
972 //DataBuf = ExAllocatePool(NonPagedPool,AttributeLengthAllocated(Attribute));
975 case AttributeIndexRoot
:
976 NtfsDumpIndexRootAttribute(Attribute
);
979 case AttributeIndexAllocation
:
980 DbgPrint(" $INDEX_ALLOCATION ");
983 case AttributeBitmap
:
984 DbgPrint(" $BITMAP ");
987 case AttributeReparsePoint
:
988 DbgPrint(" $REPARSE_POINT ");
991 case AttributeEAInformation
:
992 DbgPrint(" $EA_INFORMATION ");
999 case AttributePropertySet
:
1000 DbgPrint(" $PROPERTY_SET ");
1003 case AttributeLoggedUtilityStream
:
1004 DbgPrint(" $LOGGED_UTILITY_STREAM ");
1008 DbgPrint(" Attribute %lx ",
1013 if (Attribute
->Type
!= AttributeAttributeList
)
1015 if (Attribute
->NameLength
!= 0)
1017 Name
.Length
= Attribute
->NameLength
* sizeof(WCHAR
);
1018 Name
.MaximumLength
= Name
.Length
;
1019 Name
.Buffer
= (PWCHAR
)((ULONG_PTR
)Attribute
+ Attribute
->NameOffset
);
1021 DbgPrint("'%wZ' ", &Name
);
1025 Attribute
->IsNonResident
? "non-resident" : "resident");
1027 if (Attribute
->IsNonResident
)
1029 FindRun(Attribute
,0,&lcn
, &runcount
);
1031 DbgPrint(" AllocatedSize %I64u DataSize %I64u InitilizedSize %I64u\n",
1032 Attribute
->NonResident
.AllocatedSize
, Attribute
->NonResident
.DataSize
, Attribute
->NonResident
.InitializedSize
);
1033 DbgPrint(" logical clusters: %I64u - %I64u\n",
1034 lcn
, lcn
+ runcount
- 1);
1037 DbgPrint(" %u bytes of data\n", Attribute
->Resident
.ValueLength
);
1042 VOID
NtfsDumpDataRunData(PUCHAR DataRun
)
1044 UCHAR DataRunOffsetSize
;
1045 UCHAR DataRunLengthSize
;
1048 DbgPrint("%02x ", *DataRun
);
1053 DataRunOffsetSize
= (*DataRun
>> 4) & 0xF;
1054 DataRunLengthSize
= *DataRun
& 0xF;
1057 for (i
= 0; i
< DataRunLengthSize
; i
++)
1059 DbgPrint("%02x ", *DataRun
);
1063 for (i
= 0; i
< DataRunOffsetSize
; i
++)
1065 DbgPrint("%02x ", *DataRun
);
1069 NtfsDumpDataRunData(DataRun
);
1074 NtfsDumpDataRuns(PVOID StartOfRun
,
1075 ULONGLONG CurrentLCN
)
1077 PUCHAR DataRun
= StartOfRun
;
1078 LONGLONG DataRunOffset
;
1079 ULONGLONG DataRunLength
;
1081 if (CurrentLCN
== 0)
1083 DPRINT1("Dumping data runs.\n\tData:\n\t\t");
1084 NtfsDumpDataRunData(StartOfRun
);
1085 DbgPrint("\n\tRuns:\n\t\tOff\t\tLCN\t\tLength\n");
1088 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
1090 if (DataRunOffset
!= -1)
1091 CurrentLCN
+= DataRunOffset
;
1093 DbgPrint("\t\t%I64d\t", DataRunOffset
);
1094 if (DataRunOffset
< 99999)
1096 DbgPrint("%I64u\t", CurrentLCN
);
1097 if (CurrentLCN
< 99999)
1099 DbgPrint("%I64u\n", DataRunLength
);
1102 DbgPrint("\t\t00\n");
1104 NtfsDumpDataRuns(DataRun
, CurrentLCN
);
1109 NtfsDumpFileAttributes(PDEVICE_EXTENSION Vcb
,
1110 PFILE_RECORD_HEADER FileRecord
)
1113 FIND_ATTR_CONTXT Context
;
1114 PNTFS_ATTR_RECORD Attribute
;
1116 Status
= FindFirstAttribute(&Context
, Vcb
, FileRecord
, FALSE
, &Attribute
);
1117 while (NT_SUCCESS(Status
))
1119 NtfsDumpAttribute(Vcb
, Attribute
);
1121 Status
= FindNextAttribute(&Context
, &Attribute
);
1124 FindCloseAttribute(&Context
);
1128 GetFileNameFromRecord(PDEVICE_EXTENSION Vcb
,
1129 PFILE_RECORD_HEADER FileRecord
,
1132 FIND_ATTR_CONTXT Context
;
1133 PNTFS_ATTR_RECORD Attribute
;
1134 PFILENAME_ATTRIBUTE Name
;
1137 Status
= FindFirstAttribute(&Context
, Vcb
, FileRecord
, FALSE
, &Attribute
);
1138 while (NT_SUCCESS(Status
))
1140 if (Attribute
->Type
== AttributeFileName
)
1142 Name
= (PFILENAME_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
1143 if (Name
->NameType
== NameType
||
1144 (Name
->NameType
== NTFS_FILE_NAME_WIN32_AND_DOS
&& NameType
== NTFS_FILE_NAME_WIN32
) ||
1145 (Name
->NameType
== NTFS_FILE_NAME_WIN32_AND_DOS
&& NameType
== NTFS_FILE_NAME_DOS
))
1147 FindCloseAttribute(&Context
);
1152 Status
= FindNextAttribute(&Context
, &Attribute
);
1155 FindCloseAttribute(&Context
);
1160 * GetPackedByteCount
1161 * Returns the minimum number of bytes needed to represent the value of a
1162 * 64-bit number. Used to encode data runs.
1165 GetPackedByteCount(LONGLONG NumberToPack
,
1171 if (NumberToPack
>= 0x0100000000000000)
1173 if (NumberToPack
>= 0x0001000000000000)
1175 if (NumberToPack
>= 0x0000010000000000)
1177 if (NumberToPack
>= 0x0000000100000000)
1179 if (NumberToPack
>= 0x0000000001000000)
1181 if (NumberToPack
>= 0x0000000000010000)
1183 if (NumberToPack
>= 0x0000000000000100)
1188 if (NumberToPack
> 0)
1190 // we have to make sure the number that gets encoded won't be interpreted as negative
1191 if (NumberToPack
>= 0x0080000000000000)
1193 if (NumberToPack
>= 0x0000800000000000)
1195 if (NumberToPack
>= 0x0000008000000000)
1197 if (NumberToPack
>= 0x0000000080000000)
1199 if (NumberToPack
>= 0x0000000000800000)
1201 if (NumberToPack
>= 0x0000000000008000)
1203 if (NumberToPack
>= 0x0000000000000080)
1210 if (NumberToPack
<= 0xff80000000000000)
1212 if (NumberToPack
<= 0xffff800000000000)
1214 if (NumberToPack
<= 0xffffff8000000000)
1216 if (NumberToPack
<= 0xffffffff80000000)
1218 if (NumberToPack
<= 0xffffffffff800000)
1220 if (NumberToPack
<= 0xffffffffffff8000)
1222 if (NumberToPack
<= 0xffffffffffffff80)
1230 GetLastClusterInDataRun(PDEVICE_EXTENSION Vcb
, PNTFS_ATTR_RECORD Attribute
, PULONGLONG LastCluster
)
1232 LONGLONG DataRunOffset
;
1233 ULONGLONG DataRunLength
;
1234 LONGLONG DataRunStartLCN
;
1236 ULONGLONG LastLCN
= 0;
1237 PUCHAR DataRun
= (PUCHAR
)Attribute
+ Attribute
->NonResident
.MappingPairsOffset
;
1239 if (!Attribute
->IsNonResident
)
1240 return STATUS_INVALID_PARAMETER
;
1244 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
1246 if (DataRunOffset
!= -1)
1249 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
1250 LastLCN
= DataRunStartLCN
;
1251 *LastCluster
= LastLCN
+ DataRunLength
- 1;
1258 return STATUS_SUCCESS
;
1261 PSTANDARD_INFORMATION
1262 GetStandardInformationFromRecord(PDEVICE_EXTENSION Vcb
,
1263 PFILE_RECORD_HEADER FileRecord
)
1266 FIND_ATTR_CONTXT Context
;
1267 PNTFS_ATTR_RECORD Attribute
;
1268 PSTANDARD_INFORMATION StdInfo
;
1270 Status
= FindFirstAttribute(&Context
, Vcb
, FileRecord
, FALSE
, &Attribute
);
1271 while (NT_SUCCESS(Status
))
1273 if (Attribute
->Type
== AttributeStandardInformation
)
1275 StdInfo
= (PSTANDARD_INFORMATION
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
1276 FindCloseAttribute(&Context
);
1280 Status
= FindNextAttribute(&Context
, &Attribute
);
1283 FindCloseAttribute(&Context
);
1288 GetBestFileNameFromRecord(PDEVICE_EXTENSION Vcb
,
1289 PFILE_RECORD_HEADER FileRecord
)
1291 PFILENAME_ATTRIBUTE FileName
;
1293 FileName
= GetFileNameFromRecord(Vcb
, FileRecord
, NTFS_FILE_NAME_POSIX
);
1294 if (FileName
== NULL
)
1296 FileName
= GetFileNameFromRecord(Vcb
, FileRecord
, NTFS_FILE_NAME_WIN32
);
1297 if (FileName
== NULL
)
1299 FileName
= GetFileNameFromRecord(Vcb
, FileRecord
, NTFS_FILE_NAME_DOS
);