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 *****************************************************************/
32 #include <ntintsafe.h>
37 /* FUNCTIONS ****************************************************************/
43 * Adds a $DATA attribute to a given FileRecord.
46 * Pointer to a complete file record to add the attribute to. Caller is responsible for
47 * ensuring FileRecord is large enough to contain $DATA.
49 * @param AttributeAddress
50 * Pointer to the region of memory that will receive the $DATA attribute.
51 * This address must reside within FileRecord. Must be aligned to an 8-byte boundary (relative to FileRecord).
54 * STATUS_SUCCESS on success. STATUS_NOT_IMPLEMENTED if target address isn't at the end
55 * of the given file record.
58 * Only adding the attribute to the end of the file record is supported; AttributeAddress must
59 * be of type AttributeEnd.
60 * As it's implemented, this function is only intended to assist in creating new file records. It
61 * could be made more general-purpose by considering file records with an $ATTRIBUTE_LIST.
62 * It's the caller's responsibility to ensure the given file record has enough memory allocated
66 AddData(PFILE_RECORD_HEADER FileRecord
,
67 PNTFS_ATTR_RECORD AttributeAddress
)
69 ULONG ResidentHeaderLength
= FIELD_OFFSET(NTFS_ATTR_RECORD
, Resident
.Reserved
) + sizeof(UCHAR
);
70 ULONG FileRecordEnd
= AttributeAddress
->Length
;
72 if (AttributeAddress
->Type
!= AttributeEnd
)
74 DPRINT1("FIXME: Can only add $DATA attribute to the end of a file record.\n");
75 return STATUS_NOT_IMPLEMENTED
;
78 AttributeAddress
->Type
= AttributeData
;
79 AttributeAddress
->Length
= ResidentHeaderLength
;
80 AttributeAddress
->Length
= ALIGN_UP_BY(AttributeAddress
->Length
, ATTR_RECORD_ALIGNMENT
);
81 AttributeAddress
->Resident
.ValueLength
= 0;
82 AttributeAddress
->Resident
.ValueOffset
= ResidentHeaderLength
;
84 // for unnamed $DATA attributes, NameOffset equals header length
85 AttributeAddress
->NameOffset
= ResidentHeaderLength
;
86 AttributeAddress
->Instance
= FileRecord
->NextAttributeNumber
++;
88 // move the attribute-end and file-record-end markers to the end of the file record
89 AttributeAddress
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)AttributeAddress
+ AttributeAddress
->Length
);
90 SetFileRecordEnd(FileRecord
, AttributeAddress
, FileRecordEnd
);
92 return STATUS_SUCCESS
;
99 * Adds a $FILE_NAME attribute to a given FileRecord.
102 * Pointer to a complete file record to add the attribute to. Caller is responsible for
103 * ensuring FileRecord is large enough to contain $FILE_NAME.
105 * @param AttributeAddress
106 * Pointer to the region of memory that will receive the $FILE_NAME attribute.
107 * This address must reside within FileRecord. Must be aligned to an 8-byte boundary (relative to FileRecord).
110 * Points to the target disk's DEVICE_EXTENSION.
113 * Pointer to the FILE_OBJECT which represents the new name.
114 * This parameter is used to determine the filename and parent directory.
116 * @param CaseSensitive
117 * Boolean indicating if the function should operate in case-sensitive mode. This will be TRUE
118 * if an application opened the file with the FILE_FLAG_POSIX_SEMANTICS flag.
120 * @param ParentMftIndex
121 * Pointer to a ULONGLONG which will receive the index of the parent directory.
124 * STATUS_SUCCESS on success. STATUS_NOT_IMPLEMENTED if target address isn't at the end
125 * of the given file record.
128 * Only adding the attribute to the end of the file record is supported; AttributeAddress must
129 * be of type AttributeEnd.
130 * As it's implemented, this function is only intended to assist in creating new file records. It
131 * could be made more general-purpose by considering file records with an $ATTRIBUTE_LIST.
132 * It's the caller's responsibility to ensure the given file record has enough memory allocated
136 AddFileName(PFILE_RECORD_HEADER FileRecord
,
137 PNTFS_ATTR_RECORD AttributeAddress
,
138 PDEVICE_EXTENSION DeviceExt
,
139 PFILE_OBJECT FileObject
,
140 BOOLEAN CaseSensitive
,
141 PULONGLONG ParentMftIndex
)
143 ULONG ResidentHeaderLength
= FIELD_OFFSET(NTFS_ATTR_RECORD
, Resident
.Reserved
) + sizeof(UCHAR
);
144 PFILENAME_ATTRIBUTE FileNameAttribute
;
145 LARGE_INTEGER SystemTime
;
146 ULONG FileRecordEnd
= AttributeAddress
->Length
;
147 ULONGLONG CurrentMFTIndex
= NTFS_FILE_ROOT
;
148 UNICODE_STRING Current
, Remaining
, FilenameNoPath
;
149 NTSTATUS Status
= STATUS_SUCCESS
;
152 if (AttributeAddress
->Type
!= AttributeEnd
)
154 DPRINT1("FIXME: Can only add $FILE_NAME attribute to the end of a file record.\n");
155 return STATUS_NOT_IMPLEMENTED
;
158 AttributeAddress
->Type
= AttributeFileName
;
159 AttributeAddress
->Instance
= FileRecord
->NextAttributeNumber
++;
161 FileNameAttribute
= (PFILENAME_ATTRIBUTE
)((LONG_PTR
)AttributeAddress
+ ResidentHeaderLength
);
164 KeQuerySystemTime(&SystemTime
);
165 FileNameAttribute
->CreationTime
= SystemTime
.QuadPart
;
166 FileNameAttribute
->ChangeTime
= SystemTime
.QuadPart
;
167 FileNameAttribute
->LastWriteTime
= SystemTime
.QuadPart
;
168 FileNameAttribute
->LastAccessTime
= SystemTime
.QuadPart
;
170 FileNameAttribute
->FileAttributes
= NTFS_FILE_TYPE_ARCHIVE
;
172 // we need to extract the filename from the path
173 DPRINT1("Pathname: %wZ\n", &FileObject
->FileName
);
175 FilenameNoPath
.Buffer
= FileObject
->FileName
.Buffer
;
176 FilenameNoPath
.MaximumLength
= FilenameNoPath
.Length
= FileObject
->FileName
.Length
;
178 FsRtlDissectName(FileObject
->FileName
, &Current
, &Remaining
);
180 while (Current
.Length
!= 0)
182 DPRINT1("Current: %wZ\n", &Current
);
184 if (Remaining
.Length
!= 0)
186 FilenameNoPath
.Buffer
= Remaining
.Buffer
;
187 FilenameNoPath
.Length
= FilenameNoPath
.MaximumLength
= Remaining
.Length
;
191 Status
= NtfsFindMftRecord(DeviceExt
,
198 if (!NT_SUCCESS(Status
))
201 if (Remaining
.Length
== 0 )
203 if (Current
.Length
!= 0)
205 FilenameNoPath
.Buffer
= Current
.Buffer
;
206 FilenameNoPath
.Length
= FilenameNoPath
.MaximumLength
= Current
.Length
;
211 FsRtlDissectName(Remaining
, &Current
, &Remaining
);
214 DPRINT1("MFT Index of parent: %I64u\n", CurrentMFTIndex
);
216 // set reference to parent directory
217 FileNameAttribute
->DirectoryFileReferenceNumber
= CurrentMFTIndex
;
218 *ParentMftIndex
= CurrentMFTIndex
;
220 DPRINT1("SequenceNumber: 0x%02x\n", FileRecord
->SequenceNumber
);
222 // The highest 2 bytes should be the sequence number, unless the parent happens to be root
223 if (CurrentMFTIndex
== NTFS_FILE_ROOT
)
224 FileNameAttribute
->DirectoryFileReferenceNumber
|= (ULONGLONG
)NTFS_FILE_ROOT
<< 48;
226 FileNameAttribute
->DirectoryFileReferenceNumber
|= (ULONGLONG
)FileRecord
->SequenceNumber
<< 48;
228 DPRINT1("FileNameAttribute->DirectoryFileReferenceNumber: 0x%016I64x\n", FileNameAttribute
->DirectoryFileReferenceNumber
);
230 FileNameAttribute
->NameLength
= FilenameNoPath
.Length
/ sizeof(WCHAR
);
231 RtlCopyMemory(FileNameAttribute
->Name
, FilenameNoPath
.Buffer
, FilenameNoPath
.Length
);
233 // For now, we're emulating the way Windows behaves when 8.3 name generation is disabled
234 // TODO: add DOS Filename as needed
235 if (RtlIsNameLegalDOS8Dot3(&FilenameNoPath
, NULL
, NULL
))
236 FileNameAttribute
->NameType
= NTFS_FILE_NAME_WIN32_AND_DOS
;
238 FileNameAttribute
->NameType
= NTFS_FILE_NAME_POSIX
;
240 FileRecord
->LinkCount
++;
242 AttributeAddress
->Length
= ResidentHeaderLength
+
243 FIELD_OFFSET(FILENAME_ATTRIBUTE
, Name
) + FilenameNoPath
.Length
;
244 AttributeAddress
->Length
= ALIGN_UP_BY(AttributeAddress
->Length
, ATTR_RECORD_ALIGNMENT
);
246 AttributeAddress
->Resident
.ValueLength
= FIELD_OFFSET(FILENAME_ATTRIBUTE
, Name
) + FilenameNoPath
.Length
;
247 AttributeAddress
->Resident
.ValueOffset
= ResidentHeaderLength
;
248 AttributeAddress
->Resident
.Flags
= RA_INDEXED
;
250 // move the attribute-end and file-record-end markers to the end of the file record
251 AttributeAddress
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)AttributeAddress
+ AttributeAddress
->Length
);
252 SetFileRecordEnd(FileRecord
, AttributeAddress
, FileRecordEnd
);
261 * Adds a run of allocated clusters to a non-resident attribute.
264 * Pointer to an NTFS_VCB for the destination volume.
267 * Pointer to an NTFS_ATTR_CONTEXT describing the destination attribute.
270 * Byte offset of the destination attribute relative to its file record.
273 * Pointer to a complete copy of the file record containing the destination attribute. Must be at least
274 * Vcb->NtfsInfo.BytesPerFileRecord bytes long.
276 * @param NextAssignedCluster
277 * Logical cluster number of the start of the data run being added.
280 * How many clusters are in the data run being added. Can't be 0.
283 * STATUS_SUCCESS on success. STATUS_INVALID_PARAMETER if AttrContext describes a resident attribute.
284 * STATUS_INSUFFICIENT_RESOURCES if ConvertDataRunsToLargeMCB() fails or if we fail to allocate a
285 * buffer for the new data runs.
286 * STATUS_INSUFFICIENT_RESOURCES or STATUS_UNSUCCESSFUL if FsRtlAddLargeMcbEntry() fails.
287 * STATUS_BUFFER_TOO_SMALL if ConvertLargeMCBToDataRuns() fails.
288 * STATUS_NOT_IMPLEMENTED if we need to migrate the attribute to an attribute list (TODO).
291 * Clusters should have been allocated previously with NtfsAllocateClusters().
296 AddRun(PNTFS_VCB Vcb
,
297 PNTFS_ATTR_CONTEXT AttrContext
,
299 PFILE_RECORD_HEADER FileRecord
,
300 ULONGLONG NextAssignedCluster
,
304 int DataRunMaxLength
;
305 PNTFS_ATTR_RECORD DestinationAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ AttrOffset
);
306 ULONG NextAttributeOffset
= AttrOffset
+ AttrContext
->pRecord
->Length
;
307 ULONGLONG NextVBN
= 0;
312 if (!AttrContext
->pRecord
->IsNonResident
)
313 return STATUS_INVALID_PARAMETER
;
315 if (AttrContext
->pRecord
->NonResident
.AllocatedSize
!= 0)
316 NextVBN
= AttrContext
->pRecord
->NonResident
.HighestVCN
+ 1;
318 // Add newly-assigned clusters to mcb
321 if (!FsRtlAddLargeMcbEntry(&AttrContext
->DataRunsMCB
,
326 ExRaiseStatus(STATUS_UNSUCCESSFUL
);
329 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
331 DPRINT1("Failed to add LargeMcb Entry!\n");
332 _SEH2_YIELD(return _SEH2_GetExceptionCode());
336 RunBuffer
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
339 DPRINT1("ERROR: Couldn't allocate memory for data runs!\n");
340 return STATUS_INSUFFICIENT_RESOURCES
;
343 // Convert the map control block back to encoded data runs
344 ConvertLargeMCBToDataRuns(&AttrContext
->DataRunsMCB
, RunBuffer
, Vcb
->NtfsInfo
.BytesPerCluster
, &RunBufferSize
);
346 // Get the amount of free space between the start of the of the first data run and the attribute end
347 DataRunMaxLength
= AttrContext
->pRecord
->Length
- AttrContext
->pRecord
->NonResident
.MappingPairsOffset
;
349 // Do we need to extend the attribute (or convert to attribute list)?
350 if (DataRunMaxLength
< RunBufferSize
)
352 PNTFS_ATTR_RECORD NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ NextAttributeOffset
);
353 PNTFS_ATTR_RECORD NewRecord
;
355 // Add free space at the end of the file record to DataRunMaxLength
356 DataRunMaxLength
+= Vcb
->NtfsInfo
.BytesPerFileRecord
- FileRecord
->BytesInUse
;
358 // Can we resize the attribute?
359 if (DataRunMaxLength
< RunBufferSize
)
361 DPRINT1("FIXME: Need to create attribute list! Max Data Run Length available: %d, RunBufferSize: %d\n", DataRunMaxLength
, RunBufferSize
);
362 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
363 return STATUS_NOT_IMPLEMENTED
;
366 // Are there more attributes after the one we're resizing?
367 if (NextAttribute
->Type
!= AttributeEnd
)
369 PNTFS_ATTR_RECORD FinalAttribute
;
371 // Calculate where to move the trailing attributes
372 ULONG_PTR MoveTo
= (ULONG_PTR
)DestinationAttribute
+ AttrContext
->pRecord
->NonResident
.MappingPairsOffset
+ RunBufferSize
;
373 MoveTo
= ALIGN_UP_BY(MoveTo
, ATTR_RECORD_ALIGNMENT
);
375 DPRINT1("Moving attribute(s) after this one starting with type 0x%lx\n", NextAttribute
->Type
);
377 // Move the trailing attributes; FinalAttribute will point to the end marker
378 FinalAttribute
= MoveAttributes(Vcb
, NextAttribute
, NextAttributeOffset
, MoveTo
);
380 // set the file record end
381 SetFileRecordEnd(FileRecord
, FinalAttribute
, FILE_RECORD_END
);
384 // calculate position of end markers
385 NextAttributeOffset
= AttrOffset
+ AttrContext
->pRecord
->NonResident
.MappingPairsOffset
+ RunBufferSize
;
386 NextAttributeOffset
= ALIGN_UP_BY(NextAttributeOffset
, ATTR_RECORD_ALIGNMENT
);
388 // Update the length of the destination attribute
389 DestinationAttribute
->Length
= NextAttributeOffset
- AttrOffset
;
391 // Create a new copy of the attribute record
392 NewRecord
= ExAllocatePoolWithTag(NonPagedPool
, DestinationAttribute
->Length
, TAG_NTFS
);
393 RtlCopyMemory(NewRecord
, AttrContext
->pRecord
, AttrContext
->pRecord
->Length
);
394 NewRecord
->Length
= DestinationAttribute
->Length
;
396 // Free the old copy of the attribute record, which won't be large enough
397 ExFreePoolWithTag(AttrContext
->pRecord
, TAG_NTFS
);
399 // Set the attribute context's record to the new copy
400 AttrContext
->pRecord
= NewRecord
;
402 // if NextAttribute is the AttributeEnd marker
403 if (NextAttribute
->Type
== AttributeEnd
)
405 // End the file record
406 NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ NextAttributeOffset
);
407 SetFileRecordEnd(FileRecord
, NextAttribute
, FILE_RECORD_END
);
412 DestinationAttribute
->NonResident
.HighestVCN
=
413 AttrContext
->pRecord
->NonResident
.HighestVCN
= max(NextVBN
- 1 + RunLength
,
414 AttrContext
->pRecord
->NonResident
.HighestVCN
);
416 // Write data runs to destination attribute
417 RtlCopyMemory((PVOID
)((ULONG_PTR
)DestinationAttribute
+ DestinationAttribute
->NonResident
.MappingPairsOffset
),
421 // Update the attribute record in the attribute context
422 RtlCopyMemory((PVOID
)((ULONG_PTR
)AttrContext
->pRecord
+ AttrContext
->pRecord
->NonResident
.MappingPairsOffset
),
426 // Update the file record
427 Status
= UpdateFileRecord(Vcb
, AttrContext
->FileMFTIndex
, FileRecord
);
429 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
431 NtfsDumpDataRuns((PUCHAR
)((ULONG_PTR
)DestinationAttribute
+ DestinationAttribute
->NonResident
.MappingPairsOffset
), 0);
437 * @name AddStandardInformation
440 * Adds a $STANDARD_INFORMATION attribute to a given FileRecord.
443 * Pointer to a complete file record to add the attribute to. Caller is responsible for
444 * ensuring FileRecord is large enough to contain $STANDARD_INFORMATION.
446 * @param AttributeAddress
447 * Pointer to the region of memory that will receive the $STANDARD_INFORMATION attribute.
448 * This address must reside within FileRecord. Must be aligned to an 8-byte boundary (relative to FileRecord).
451 * STATUS_SUCCESS on success. STATUS_NOT_IMPLEMENTED if target address isn't at the end
452 * of the given file record.
455 * Only adding the attribute to the end of the file record is supported; AttributeAddress must
456 * be of type AttributeEnd.
457 * As it's implemented, this function is only intended to assist in creating new file records. It
458 * could be made more general-purpose by considering file records with an $ATTRIBUTE_LIST.
459 * It's the caller's responsibility to ensure the given file record has enough memory allocated
463 AddStandardInformation(PFILE_RECORD_HEADER FileRecord
,
464 PNTFS_ATTR_RECORD AttributeAddress
)
466 ULONG ResidentHeaderLength
= FIELD_OFFSET(NTFS_ATTR_RECORD
, Resident
.Reserved
) + sizeof(UCHAR
);
467 PSTANDARD_INFORMATION StandardInfo
= (PSTANDARD_INFORMATION
)((LONG_PTR
)AttributeAddress
+ ResidentHeaderLength
);
468 LARGE_INTEGER SystemTime
;
469 ULONG FileRecordEnd
= AttributeAddress
->Length
;
471 if (AttributeAddress
->Type
!= AttributeEnd
)
473 DPRINT1("FIXME: Can only add $STANDARD_INFORMATION attribute to the end of a file record.\n");
474 return STATUS_NOT_IMPLEMENTED
;
477 AttributeAddress
->Type
= AttributeStandardInformation
;
478 AttributeAddress
->Length
= sizeof(STANDARD_INFORMATION
) + ResidentHeaderLength
;
479 AttributeAddress
->Length
= ALIGN_UP_BY(AttributeAddress
->Length
, ATTR_RECORD_ALIGNMENT
);
480 AttributeAddress
->Resident
.ValueLength
= sizeof(STANDARD_INFORMATION
);
481 AttributeAddress
->Resident
.ValueOffset
= ResidentHeaderLength
;
482 AttributeAddress
->Instance
= FileRecord
->NextAttributeNumber
++;
484 // set dates and times
485 KeQuerySystemTime(&SystemTime
);
486 StandardInfo
->CreationTime
= SystemTime
.QuadPart
;
487 StandardInfo
->ChangeTime
= SystemTime
.QuadPart
;
488 StandardInfo
->LastWriteTime
= SystemTime
.QuadPart
;
489 StandardInfo
->LastAccessTime
= SystemTime
.QuadPart
;
490 StandardInfo
->FileAttribute
= NTFS_FILE_TYPE_ARCHIVE
;
492 // move the attribute-end and file-record-end markers to the end of the file record
493 AttributeAddress
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)AttributeAddress
+ AttributeAddress
->Length
);
494 SetFileRecordEnd(FileRecord
, AttributeAddress
, FileRecordEnd
);
496 return STATUS_SUCCESS
;
500 * @name ConvertDataRunsToLargeMCB
503 * Converts binary data runs to a map control block.
506 * Pointer to the run data
509 * Pointer to an unitialized LARGE_MCB structure.
512 * STATUS_SUCCESS on success, STATUS_INSUFFICIENT_RESOURCES or STATUS_UNSUCCESSFUL if we fail to
513 * initialize the mcb or add an entry.
516 * Initializes the LARGE_MCB pointed to by DataRunsMCB. If this function succeeds, you
517 * need to call FsRtlUninitializeLargeMcb() when you're done with DataRunsMCB. This
518 * function will ensure the LargeMCB has been unitialized in case of failure.
522 ConvertDataRunsToLargeMCB(PUCHAR DataRun
,
523 PLARGE_MCB DataRunsMCB
,
526 LONGLONG DataRunOffset
;
527 ULONGLONG DataRunLength
;
528 LONGLONG DataRunStartLCN
;
529 ULONGLONG LastLCN
= 0;
531 // Initialize the MCB, potentially catch an exception
533 FsRtlInitializeLargeMcb(DataRunsMCB
, NonPagedPool
);
534 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
535 _SEH2_YIELD(return _SEH2_GetExceptionCode());
538 while (*DataRun
!= 0)
540 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
542 if (DataRunOffset
!= -1)
545 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
546 LastLCN
= DataRunStartLCN
;
549 if (!FsRtlAddLargeMcbEntry(DataRunsMCB
,
554 ExRaiseStatus(STATUS_UNSUCCESSFUL
);
556 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
557 FsRtlUninitializeLargeMcb(DataRunsMCB
);
558 _SEH2_YIELD(return _SEH2_GetExceptionCode());
563 *pNextVBN
+= DataRunLength
;
566 return STATUS_SUCCESS
;
570 * @name ConvertLargeMCBToDataRuns
573 * Converts a map control block to a series of encoded data runs (used by non-resident attributes).
576 * Pointer to a LARGE_MCB structure describing the data runs.
579 * Pointer to the buffer that will receive the encoded data runs.
581 * @param MaxBufferSize
582 * Size of RunBuffer, in bytes.
584 * @param UsedBufferSize
585 * Pointer to a ULONG that will receive the size of the data runs in bytes. Can't be NULL.
588 * STATUS_SUCCESS on success, STATUS_BUFFER_TOO_SMALL if RunBuffer is too small to contain the
593 ConvertLargeMCBToDataRuns(PLARGE_MCB DataRunsMCB
,
596 PULONG UsedBufferSize
)
598 NTSTATUS Status
= STATUS_SUCCESS
;
599 ULONG RunBufferOffset
= 0;
600 LONGLONG DataRunOffset
;
601 ULONGLONG LastLCN
= 0;
602 LONGLONG Vbn
, Lbn
, Count
;
606 DPRINT("\t[Vbn, Lbn, Count]\n");
608 // convert each mcb entry to a data run
609 for (i
= 0; FsRtlGetNextLargeMcbEntry(DataRunsMCB
, i
, &Vbn
, &Lbn
, &Count
); i
++)
611 UCHAR DataRunOffsetSize
= 0;
612 UCHAR DataRunLengthSize
= 0;
613 UCHAR ControlByte
= 0;
616 DPRINT("\t[%I64d, %I64d,%I64d]\n", Vbn
, Lbn
, Count
);
618 // TODO: check for holes and convert to sparse runs
619 DataRunOffset
= Lbn
- LastLCN
;
622 // now we need to determine how to represent DataRunOffset with the minimum number of bytes
623 DPRINT("Determining how many bytes needed to represent %I64x\n", DataRunOffset
);
624 DataRunOffsetSize
= GetPackedByteCount(DataRunOffset
, TRUE
);
625 DPRINT("%d bytes needed.\n", DataRunOffsetSize
);
627 // determine how to represent DataRunLengthSize with the minimum number of bytes
628 DPRINT("Determining how many bytes needed to represent %I64x\n", Count
);
629 DataRunLengthSize
= GetPackedByteCount(Count
, TRUE
);
630 DPRINT("%d bytes needed.\n", DataRunLengthSize
);
632 // ensure the next data run + end marker would be <= Max buffer size
633 if (RunBufferOffset
+ 2 + DataRunLengthSize
+ DataRunOffsetSize
> MaxBufferSize
)
635 Status
= STATUS_BUFFER_TOO_SMALL
;
636 DPRINT1("FIXME: Ran out of room in buffer for data runs!\n");
640 // pack and copy the control byte
641 ControlByte
= (DataRunOffsetSize
<< 4) + DataRunLengthSize
;
642 RunBuffer
[RunBufferOffset
++] = ControlByte
;
644 // copy DataRunLength
645 RtlCopyMemory(RunBuffer
+ RunBufferOffset
, &Count
, DataRunLengthSize
);
646 RunBufferOffset
+= DataRunLengthSize
;
648 // copy DataRunOffset
649 RtlCopyMemory(RunBuffer
+ RunBufferOffset
, &DataRunOffset
, DataRunOffsetSize
);
650 RunBufferOffset
+= DataRunOffsetSize
;
654 RunBuffer
[RunBufferOffset
++] = 0;
656 *UsedBufferSize
= RunBufferOffset
;
657 DPRINT("New Size of DataRuns: %ld\n", *UsedBufferSize
);
663 DecodeRun(PUCHAR DataRun
,
664 LONGLONG
*DataRunOffset
,
665 ULONGLONG
*DataRunLength
)
667 UCHAR DataRunOffsetSize
;
668 UCHAR DataRunLengthSize
;
671 DataRunOffsetSize
= (*DataRun
>> 4) & 0xF;
672 DataRunLengthSize
= *DataRun
& 0xF;
676 for (i
= 0; i
< DataRunLengthSize
; i
++)
678 *DataRunLength
+= ((ULONG64
)*DataRun
) << (i
* 8);
682 /* NTFS 3+ sparse files */
683 if (DataRunOffsetSize
== 0)
689 for (i
= 0; i
< DataRunOffsetSize
- 1; i
++)
691 *DataRunOffset
+= ((ULONG64
)*DataRun
) << (i
* 8);
694 /* The last byte contains sign so we must process it different way. */
695 *DataRunOffset
= ((LONG64
)(CHAR
)(*(DataRun
++)) << (i
* 8)) + *DataRunOffset
;
698 DPRINT("DataRunOffsetSize: %x\n", DataRunOffsetSize
);
699 DPRINT("DataRunLengthSize: %x\n", DataRunLengthSize
);
700 DPRINT("DataRunOffset: %x\n", *DataRunOffset
);
701 DPRINT("DataRunLength: %x\n", *DataRunLength
);
707 FindRun(PNTFS_ATTR_RECORD NresAttr
,
712 if (vcn
< NresAttr
->NonResident
.LowestVCN
|| vcn
> NresAttr
->NonResident
.HighestVCN
)
715 DecodeRun((PUCHAR
)((ULONG_PTR
)NresAttr
+ NresAttr
->NonResident
.MappingPairsOffset
), (PLONGLONG
)lcn
, count
);
724 * Shrinks the allocation size of a non-resident attribute by a given number of clusters.
725 * Frees the clusters from the volume's $BITMAP file as well as the attribute's data runs.
728 * Pointer to an NTFS_VCB for the destination volume.
731 * Pointer to an NTFS_ATTR_CONTEXT describing the attribute from which the clusters will be freed.
734 * Byte offset of the destination attribute relative to its file record.
737 * Pointer to a complete copy of the file record containing the attribute. Must be at least
738 * Vcb->NtfsInfo.BytesPerFileRecord bytes long.
740 * @param ClustersToFree
741 * Number of clusters that should be freed from the end of the data stream. Must be no more
742 * Than the number of clusters assigned to the attribute (HighestVCN + 1).
745 * STATUS_SUCCESS on success. STATUS_INVALID_PARAMETER if AttrContext describes a resident attribute,
746 * or if the caller requested more clusters be freed than the attribute has been allocated.
747 * STATUS_INSUFFICIENT_RESOURCES if allocating a buffer for the data runs fails or
748 * if ConvertDataRunsToLargeMCB() fails.
749 * STATUS_BUFFER_TOO_SMALL if ConvertLargeMCBToDataRuns() fails.
754 FreeClusters(PNTFS_VCB Vcb
,
755 PNTFS_ATTR_CONTEXT AttrContext
,
757 PFILE_RECORD_HEADER FileRecord
,
758 ULONG ClustersToFree
)
760 NTSTATUS Status
= STATUS_SUCCESS
;
761 ULONG ClustersLeftToFree
= ClustersToFree
;
763 PNTFS_ATTR_RECORD DestinationAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ AttrOffset
);
764 ULONG NextAttributeOffset
= AttrOffset
+ AttrContext
->pRecord
->Length
;
765 PNTFS_ATTR_RECORD NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ NextAttributeOffset
);
768 ULONG RunBufferSize
= 0;
770 PFILE_RECORD_HEADER BitmapRecord
;
771 PNTFS_ATTR_CONTEXT DataContext
;
772 ULONGLONG BitmapDataSize
;
777 if (!AttrContext
->pRecord
->IsNonResident
)
779 return STATUS_INVALID_PARAMETER
;
782 // Read the $Bitmap file
783 BitmapRecord
= ExAllocatePoolWithTag(NonPagedPool
,
784 Vcb
->NtfsInfo
.BytesPerFileRecord
,
786 if (BitmapRecord
== NULL
)
788 DPRINT1("Error: Unable to allocate memory for bitmap file record!\n");
789 return STATUS_NO_MEMORY
;
792 Status
= ReadFileRecord(Vcb
, NTFS_FILE_BITMAP
, BitmapRecord
);
793 if (!NT_SUCCESS(Status
))
795 DPRINT1("Error: Unable to read file record for bitmap!\n");
796 ExFreePoolWithTag(BitmapRecord
, TAG_NTFS
);
800 Status
= FindAttribute(Vcb
, BitmapRecord
, AttributeData
, L
"", 0, &DataContext
, NULL
);
801 if (!NT_SUCCESS(Status
))
803 DPRINT1("Error: Unable to find data attribute for bitmap file!\n");
804 ExFreePoolWithTag(BitmapRecord
, TAG_NTFS
);
808 BitmapDataSize
= AttributeDataLength(DataContext
->pRecord
);
809 BitmapDataSize
= min(BitmapDataSize
, ULONG_MAX
);
810 ASSERT((BitmapDataSize
* 8) >= Vcb
->NtfsInfo
.ClusterCount
);
811 BitmapData
= ExAllocatePoolWithTag(NonPagedPool
, ROUND_UP(BitmapDataSize
, Vcb
->NtfsInfo
.BytesPerSector
), TAG_NTFS
);
812 if (BitmapData
== NULL
)
814 DPRINT1("Error: Unable to allocate memory for bitmap file data!\n");
815 ReleaseAttributeContext(DataContext
);
816 ExFreePoolWithTag(BitmapRecord
, TAG_NTFS
);
820 ReadAttribute(Vcb
, DataContext
, 0, (PCHAR
)BitmapData
, (ULONG
)BitmapDataSize
);
822 RtlInitializeBitMap(&Bitmap
, (PULONG
)BitmapData
, Vcb
->NtfsInfo
.ClusterCount
);
824 // free clusters in $BITMAP file
825 while (ClustersLeftToFree
> 0)
827 LONGLONG LargeVbn
, LargeLbn
;
829 if (!FsRtlLookupLastLargeMcbEntry(&AttrContext
->DataRunsMCB
, &LargeVbn
, &LargeLbn
))
831 Status
= STATUS_INVALID_PARAMETER
;
832 DPRINT1("DRIVER ERROR: FreeClusters called to free %lu clusters, which is %lu more clusters than are assigned to attribute!",
840 // deallocate this cluster
841 RtlClearBits(&Bitmap
, LargeLbn
, 1);
843 FsRtlTruncateLargeMcb(&AttrContext
->DataRunsMCB
, AttrContext
->pRecord
->NonResident
.HighestVCN
);
845 // decrement HighestVCN, but don't let it go below 0
846 AttrContext
->pRecord
->NonResident
.HighestVCN
= min(AttrContext
->pRecord
->NonResident
.HighestVCN
, AttrContext
->pRecord
->NonResident
.HighestVCN
- 1);
847 ClustersLeftToFree
--;
850 // update $BITMAP file on disk
851 Status
= WriteAttribute(Vcb
, DataContext
, 0, BitmapData
, (ULONG
)BitmapDataSize
, &LengthWritten
, FileRecord
);
852 if (!NT_SUCCESS(Status
))
854 ReleaseAttributeContext(DataContext
);
855 ExFreePoolWithTag(BitmapData
, TAG_NTFS
);
856 ExFreePoolWithTag(BitmapRecord
, TAG_NTFS
);
860 ReleaseAttributeContext(DataContext
);
861 ExFreePoolWithTag(BitmapData
, TAG_NTFS
);
862 ExFreePoolWithTag(BitmapRecord
, TAG_NTFS
);
864 // Save updated data runs to file record
866 // Allocate some memory for a new RunBuffer
867 RunBuffer
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
870 DPRINT1("ERROR: Couldn't allocate memory for data runs!\n");
871 return STATUS_INSUFFICIENT_RESOURCES
;
874 // Convert the map control block back to encoded data runs
875 ConvertLargeMCBToDataRuns(&AttrContext
->DataRunsMCB
, RunBuffer
, Vcb
->NtfsInfo
.BytesPerCluster
, &RunBufferSize
);
878 DestinationAttribute
->NonResident
.HighestVCN
= AttrContext
->pRecord
->NonResident
.HighestVCN
;
880 // Write data runs to destination attribute
881 RtlCopyMemory((PVOID
)((ULONG_PTR
)DestinationAttribute
+ DestinationAttribute
->NonResident
.MappingPairsOffset
),
885 // Is DestinationAttribute the last attribute in the file record?
886 if (NextAttribute
->Type
== AttributeEnd
)
888 // update attribute length
889 DestinationAttribute
->Length
= ALIGN_UP_BY(AttrContext
->pRecord
->NonResident
.MappingPairsOffset
+ RunBufferSize
,
890 ATTR_RECORD_ALIGNMENT
);
892 ASSERT(DestinationAttribute
->Length
<= AttrContext
->pRecord
->Length
);
894 AttrContext
->pRecord
->Length
= DestinationAttribute
->Length
;
897 NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)DestinationAttribute
+ DestinationAttribute
->Length
);
898 SetFileRecordEnd(FileRecord
, NextAttribute
, FILE_RECORD_END
);
901 // Update the file record
902 Status
= UpdateFileRecord(Vcb
, AttrContext
->FileMFTIndex
, FileRecord
);
904 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
906 NtfsDumpDataRuns((PUCHAR
)((ULONG_PTR
)DestinationAttribute
+ DestinationAttribute
->NonResident
.MappingPairsOffset
), 0);
913 InternalReadNonResidentAttributes(PFIND_ATTR_CONTXT Context
)
916 PNTFS_ATTR_RECORD Attribute
;
917 PNTFS_ATTR_CONTEXT ListContext
;
919 DPRINT("InternalReadNonResidentAttributes(%p)\n", Context
);
921 Attribute
= Context
->CurrAttr
;
922 ASSERT(Attribute
->Type
== AttributeAttributeList
);
924 if (Context
->OnlyResident
)
926 Context
->NonResidentStart
= NULL
;
927 Context
->NonResidentEnd
= NULL
;
928 return STATUS_SUCCESS
;
931 if (Context
->NonResidentStart
!= NULL
)
933 return STATUS_FILE_CORRUPT_ERROR
;
936 ListContext
= PrepareAttributeContext(Attribute
);
937 ListSize
= AttributeDataLength(ListContext
->pRecord
);
938 if (ListSize
> 0xFFFFFFFF)
940 ReleaseAttributeContext(ListContext
);
941 return STATUS_BUFFER_OVERFLOW
;
944 Context
->NonResidentStart
= ExAllocatePoolWithTag(NonPagedPool
, (ULONG
)ListSize
, TAG_NTFS
);
945 if (Context
->NonResidentStart
== NULL
)
947 ReleaseAttributeContext(ListContext
);
948 return STATUS_INSUFFICIENT_RESOURCES
;
951 if (ReadAttribute(Context
->Vcb
, ListContext
, 0, (PCHAR
)Context
->NonResidentStart
, (ULONG
)ListSize
) != ListSize
)
953 ExFreePoolWithTag(Context
->NonResidentStart
, TAG_NTFS
);
954 Context
->NonResidentStart
= NULL
;
955 ReleaseAttributeContext(ListContext
);
956 return STATUS_FILE_CORRUPT_ERROR
;
959 ReleaseAttributeContext(ListContext
);
960 Context
->NonResidentEnd
= (PNTFS_ATTR_RECORD
)((PCHAR
)Context
->NonResidentStart
+ ListSize
);
961 return STATUS_SUCCESS
;
966 InternalGetNextAttribute(PFIND_ATTR_CONTXT Context
)
968 PNTFS_ATTR_RECORD NextAttribute
;
970 if (Context
->CurrAttr
== (PVOID
)-1)
975 if (Context
->CurrAttr
>= Context
->FirstAttr
&&
976 Context
->CurrAttr
< Context
->LastAttr
)
978 if (Context
->CurrAttr
->Length
== 0)
980 DPRINT1("Broken length!\n");
981 Context
->CurrAttr
= (PVOID
)-1;
985 NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)Context
->CurrAttr
+ Context
->CurrAttr
->Length
);
987 if (NextAttribute
> Context
->LastAttr
|| NextAttribute
< Context
->FirstAttr
)
989 DPRINT1("Broken length: 0x%lx!\n", Context
->CurrAttr
->Length
);
990 Context
->CurrAttr
= (PVOID
)-1;
994 Context
->Offset
+= ((ULONG_PTR
)NextAttribute
- (ULONG_PTR
)Context
->CurrAttr
);
995 Context
->CurrAttr
= NextAttribute
;
997 if (Context
->CurrAttr
< Context
->LastAttr
&&
998 Context
->CurrAttr
->Type
!= AttributeEnd
)
1000 return Context
->CurrAttr
;
1004 if (Context
->NonResidentStart
== NULL
)
1006 Context
->CurrAttr
= (PVOID
)-1;
1010 if (Context
->CurrAttr
< Context
->NonResidentStart
||
1011 Context
->CurrAttr
>= Context
->NonResidentEnd
)
1013 Context
->CurrAttr
= Context
->NonResidentStart
;
1015 else if (Context
->CurrAttr
->Length
!= 0)
1017 NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)Context
->CurrAttr
+ Context
->CurrAttr
->Length
);
1018 Context
->Offset
+= ((ULONG_PTR
)NextAttribute
- (ULONG_PTR
)Context
->CurrAttr
);
1019 Context
->CurrAttr
= NextAttribute
;
1023 DPRINT1("Broken length!\n");
1024 Context
->CurrAttr
= (PVOID
)-1;
1028 if (Context
->CurrAttr
< Context
->NonResidentEnd
&&
1029 Context
->CurrAttr
->Type
!= AttributeEnd
)
1031 return Context
->CurrAttr
;
1034 Context
->CurrAttr
= (PVOID
)-1;
1039 FindFirstAttribute(PFIND_ATTR_CONTXT Context
,
1040 PDEVICE_EXTENSION Vcb
,
1041 PFILE_RECORD_HEADER FileRecord
,
1042 BOOLEAN OnlyResident
,
1043 PNTFS_ATTR_RECORD
* Attribute
)
1047 DPRINT("FindFistAttribute(%p, %p, %p, %p, %u, %p)\n", Context
, Vcb
, FileRecord
, OnlyResident
, Attribute
);
1050 Context
->OnlyResident
= OnlyResident
;
1051 Context
->FirstAttr
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ FileRecord
->AttributeOffset
);
1052 Context
->CurrAttr
= Context
->FirstAttr
;
1053 Context
->LastAttr
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ FileRecord
->BytesInUse
);
1054 Context
->NonResidentStart
= NULL
;
1055 Context
->NonResidentEnd
= NULL
;
1056 Context
->Offset
= FileRecord
->AttributeOffset
;
1058 if (Context
->FirstAttr
->Type
== AttributeEnd
)
1060 Context
->CurrAttr
= (PVOID
)-1;
1061 return STATUS_END_OF_FILE
;
1063 else if (Context
->FirstAttr
->Type
== AttributeAttributeList
)
1065 Status
= InternalReadNonResidentAttributes(Context
);
1066 if (!NT_SUCCESS(Status
))
1071 *Attribute
= InternalGetNextAttribute(Context
);
1072 if (*Attribute
== NULL
)
1074 return STATUS_END_OF_FILE
;
1079 *Attribute
= Context
->CurrAttr
;
1080 Context
->Offset
= (UCHAR
*)Context
->CurrAttr
- (UCHAR
*)FileRecord
;
1083 return STATUS_SUCCESS
;
1087 FindNextAttribute(PFIND_ATTR_CONTXT Context
,
1088 PNTFS_ATTR_RECORD
* Attribute
)
1092 DPRINT("FindNextAttribute(%p, %p)\n", Context
, Attribute
);
1094 *Attribute
= InternalGetNextAttribute(Context
);
1095 if (*Attribute
== NULL
)
1097 return STATUS_END_OF_FILE
;
1100 if (Context
->CurrAttr
->Type
!= AttributeAttributeList
)
1102 return STATUS_SUCCESS
;
1105 Status
= InternalReadNonResidentAttributes(Context
);
1106 if (!NT_SUCCESS(Status
))
1111 *Attribute
= InternalGetNextAttribute(Context
);
1112 if (*Attribute
== NULL
)
1114 return STATUS_END_OF_FILE
;
1117 return STATUS_SUCCESS
;
1121 FindCloseAttribute(PFIND_ATTR_CONTXT Context
)
1123 if (Context
->NonResidentStart
!= NULL
)
1125 ExFreePoolWithTag(Context
->NonResidentStart
, TAG_NTFS
);
1126 Context
->NonResidentStart
= NULL
;
1132 NtfsDumpFileNameAttribute(PNTFS_ATTR_RECORD Attribute
)
1134 PFILENAME_ATTRIBUTE FileNameAttr
;
1136 DbgPrint(" $FILE_NAME ");
1138 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
1140 FileNameAttr
= (PFILENAME_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
1141 DbgPrint(" (%x) '%.*S' ", FileNameAttr
->NameType
, FileNameAttr
->NameLength
, FileNameAttr
->Name
);
1142 DbgPrint(" '%x' \n", FileNameAttr
->FileAttributes
);
1143 DbgPrint(" AllocatedSize: %I64u\nDataSize: %I64u\n", FileNameAttr
->AllocatedSize
, FileNameAttr
->DataSize
);
1144 DbgPrint(" File reference: 0x%016I64x\n", FileNameAttr
->DirectoryFileReferenceNumber
);
1150 NtfsDumpStandardInformationAttribute(PNTFS_ATTR_RECORD Attribute
)
1152 PSTANDARD_INFORMATION StandardInfoAttr
;
1154 DbgPrint(" $STANDARD_INFORMATION ");
1156 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
1158 StandardInfoAttr
= (PSTANDARD_INFORMATION
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
1159 DbgPrint(" '%x' ", StandardInfoAttr
->FileAttribute
);
1165 NtfsDumpVolumeNameAttribute(PNTFS_ATTR_RECORD Attribute
)
1169 DbgPrint(" $VOLUME_NAME ");
1171 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
1173 VolumeName
= (PWCHAR
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
1174 DbgPrint(" '%.*S' ", Attribute
->Resident
.ValueLength
/ sizeof(WCHAR
), VolumeName
);
1180 NtfsDumpVolumeInformationAttribute(PNTFS_ATTR_RECORD Attribute
)
1182 PVOLINFO_ATTRIBUTE VolInfoAttr
;
1184 DbgPrint(" $VOLUME_INFORMATION ");
1186 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
1188 VolInfoAttr
= (PVOLINFO_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
1189 DbgPrint(" NTFS Version %u.%u Flags 0x%04hx ",
1190 VolInfoAttr
->MajorVersion
,
1191 VolInfoAttr
->MinorVersion
,
1192 VolInfoAttr
->Flags
);
1198 NtfsDumpIndexRootAttribute(PNTFS_ATTR_RECORD Attribute
)
1200 PINDEX_ROOT_ATTRIBUTE IndexRootAttr
;
1201 ULONG CurrentOffset
;
1204 IndexRootAttr
= (PINDEX_ROOT_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
1206 if (IndexRootAttr
->AttributeType
== AttributeFileName
)
1207 ASSERT(IndexRootAttr
->CollationRule
== COLLATION_FILE_NAME
);
1209 DbgPrint(" $INDEX_ROOT (%u bytes per index record, %u clusters) ", IndexRootAttr
->SizeOfEntry
, IndexRootAttr
->ClustersPerIndexRecord
);
1211 if (IndexRootAttr
->Header
.Flags
== INDEX_ROOT_SMALL
)
1213 DbgPrint(" (small)\n");
1217 ASSERT(IndexRootAttr
->Header
.Flags
== INDEX_ROOT_LARGE
);
1218 DbgPrint(" (large)\n");
1221 DbgPrint(" Offset to first index: 0x%lx\n Total size of index entries: 0x%lx\n Allocated size of node: 0x%lx\n",
1222 IndexRootAttr
->Header
.FirstEntryOffset
,
1223 IndexRootAttr
->Header
.TotalSizeOfEntries
,
1224 IndexRootAttr
->Header
.AllocatedSize
);
1225 CurrentOffset
= IndexRootAttr
->Header
.FirstEntryOffset
;
1227 // print details of every node in the index
1228 while (CurrentOffset
< IndexRootAttr
->Header
.TotalSizeOfEntries
)
1230 PINDEX_ENTRY_ATTRIBUTE currentIndexExtry
= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)IndexRootAttr
+ 0x10 + CurrentOffset
);
1231 DbgPrint(" Index Node Entry %lu", CurrentNode
++);
1232 if (BooleanFlagOn(currentIndexExtry
->Flags
, NTFS_INDEX_ENTRY_NODE
))
1233 DbgPrint(" (Branch)");
1235 DbgPrint(" (Leaf)");
1236 if (BooleanFlagOn(currentIndexExtry
->Flags
, NTFS_INDEX_ENTRY_END
))
1238 DbgPrint(" (Dummy Key)");
1240 DbgPrint("\n File Reference: 0x%016I64x\n", currentIndexExtry
->Data
.Directory
.IndexedFile
);
1241 DbgPrint(" Index Entry Length: 0x%x\n", currentIndexExtry
->Length
);
1242 DbgPrint(" Index Key Length: 0x%x\n", currentIndexExtry
->KeyLength
);
1244 // if this isn't the final (dummy) node, print info about the key (Filename attribute)
1245 if (!(currentIndexExtry
->Flags
& NTFS_INDEX_ENTRY_END
))
1247 UNICODE_STRING Name
;
1248 DbgPrint(" Parent File Reference: 0x%016I64x\n", currentIndexExtry
->FileName
.DirectoryFileReferenceNumber
);
1249 DbgPrint(" $FILENAME indexed: ");
1250 Name
.Length
= currentIndexExtry
->FileName
.NameLength
* sizeof(WCHAR
);
1251 Name
.MaximumLength
= Name
.Length
;
1252 Name
.Buffer
= currentIndexExtry
->FileName
.Name
;
1253 DbgPrint("'%wZ'\n", &Name
);
1256 // if this node has a sub-node beneath it
1257 if (currentIndexExtry
->Flags
& NTFS_INDEX_ENTRY_NODE
)
1259 // Print the VCN of the sub-node
1260 PULONGLONG SubNodeVCN
= (PULONGLONG
)((ULONG_PTR
)currentIndexExtry
+ currentIndexExtry
->Length
- sizeof(ULONGLONG
));
1261 DbgPrint(" VCN of sub-node: 0x%llx\n", *SubNodeVCN
);
1264 CurrentOffset
+= currentIndexExtry
->Length
;
1265 ASSERT(currentIndexExtry
->Length
);
1273 NtfsDumpAttribute(PDEVICE_EXTENSION Vcb
,
1274 PNTFS_ATTR_RECORD Attribute
)
1276 UNICODE_STRING Name
;
1279 ULONGLONG runcount
= 0;
1281 switch (Attribute
->Type
)
1283 case AttributeFileName
:
1284 NtfsDumpFileNameAttribute(Attribute
);
1287 case AttributeStandardInformation
:
1288 NtfsDumpStandardInformationAttribute(Attribute
);
1291 case AttributeObjectId
:
1292 DbgPrint(" $OBJECT_ID ");
1295 case AttributeSecurityDescriptor
:
1296 DbgPrint(" $SECURITY_DESCRIPTOR ");
1299 case AttributeVolumeName
:
1300 NtfsDumpVolumeNameAttribute(Attribute
);
1303 case AttributeVolumeInformation
:
1304 NtfsDumpVolumeInformationAttribute(Attribute
);
1308 DbgPrint(" $DATA ");
1309 //DataBuf = ExAllocatePool(NonPagedPool,AttributeLengthAllocated(Attribute));
1312 case AttributeIndexRoot
:
1313 NtfsDumpIndexRootAttribute(Attribute
);
1316 case AttributeIndexAllocation
:
1317 DbgPrint(" $INDEX_ALLOCATION ");
1320 case AttributeBitmap
:
1321 DbgPrint(" $BITMAP ");
1324 case AttributeReparsePoint
:
1325 DbgPrint(" $REPARSE_POINT ");
1328 case AttributeEAInformation
:
1329 DbgPrint(" $EA_INFORMATION ");
1336 case AttributePropertySet
:
1337 DbgPrint(" $PROPERTY_SET ");
1340 case AttributeLoggedUtilityStream
:
1341 DbgPrint(" $LOGGED_UTILITY_STREAM ");
1345 DbgPrint(" Attribute %lx ",
1350 if (Attribute
->Type
!= AttributeAttributeList
)
1352 if (Attribute
->NameLength
!= 0)
1354 Name
.Length
= Attribute
->NameLength
* sizeof(WCHAR
);
1355 Name
.MaximumLength
= Name
.Length
;
1356 Name
.Buffer
= (PWCHAR
)((ULONG_PTR
)Attribute
+ Attribute
->NameOffset
);
1358 DbgPrint("'%wZ' ", &Name
);
1362 Attribute
->IsNonResident
? "non-resident" : "resident");
1364 if (Attribute
->IsNonResident
)
1366 FindRun(Attribute
,0,&lcn
, &runcount
);
1368 DbgPrint(" AllocatedSize %I64u DataSize %I64u InitilizedSize %I64u\n",
1369 Attribute
->NonResident
.AllocatedSize
, Attribute
->NonResident
.DataSize
, Attribute
->NonResident
.InitializedSize
);
1370 DbgPrint(" logical clusters: %I64u - %I64u\n",
1371 lcn
, lcn
+ runcount
- 1);
1374 DbgPrint(" %u bytes of data\n", Attribute
->Resident
.ValueLength
);
1379 VOID
NtfsDumpDataRunData(PUCHAR DataRun
)
1381 UCHAR DataRunOffsetSize
;
1382 UCHAR DataRunLengthSize
;
1385 DbgPrint("%02x ", *DataRun
);
1390 DataRunOffsetSize
= (*DataRun
>> 4) & 0xF;
1391 DataRunLengthSize
= *DataRun
& 0xF;
1394 for (i
= 0; i
< DataRunLengthSize
; i
++)
1396 DbgPrint("%02x ", *DataRun
);
1400 for (i
= 0; i
< DataRunOffsetSize
; i
++)
1402 DbgPrint("%02x ", *DataRun
);
1406 NtfsDumpDataRunData(DataRun
);
1411 NtfsDumpDataRuns(PVOID StartOfRun
,
1412 ULONGLONG CurrentLCN
)
1414 PUCHAR DataRun
= StartOfRun
;
1415 LONGLONG DataRunOffset
;
1416 ULONGLONG DataRunLength
;
1418 if (CurrentLCN
== 0)
1420 DPRINT1("Dumping data runs.\n\tData:\n\t\t");
1421 NtfsDumpDataRunData(StartOfRun
);
1422 DbgPrint("\n\tRuns:\n\t\tOff\t\tLCN\t\tLength\n");
1425 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
1427 if (DataRunOffset
!= -1)
1428 CurrentLCN
+= DataRunOffset
;
1430 DbgPrint("\t\t%I64d\t", DataRunOffset
);
1431 if (DataRunOffset
< 99999)
1433 DbgPrint("%I64u\t", CurrentLCN
);
1434 if (CurrentLCN
< 99999)
1436 DbgPrint("%I64u\n", DataRunLength
);
1439 DbgPrint("\t\t00\n");
1441 NtfsDumpDataRuns(DataRun
, CurrentLCN
);
1446 NtfsDumpFileAttributes(PDEVICE_EXTENSION Vcb
,
1447 PFILE_RECORD_HEADER FileRecord
)
1450 FIND_ATTR_CONTXT Context
;
1451 PNTFS_ATTR_RECORD Attribute
;
1453 Status
= FindFirstAttribute(&Context
, Vcb
, FileRecord
, FALSE
, &Attribute
);
1454 while (NT_SUCCESS(Status
))
1456 NtfsDumpAttribute(Vcb
, Attribute
);
1458 Status
= FindNextAttribute(&Context
, &Attribute
);
1461 FindCloseAttribute(&Context
);
1465 GetFileNameFromRecord(PDEVICE_EXTENSION Vcb
,
1466 PFILE_RECORD_HEADER FileRecord
,
1469 FIND_ATTR_CONTXT Context
;
1470 PNTFS_ATTR_RECORD Attribute
;
1471 PFILENAME_ATTRIBUTE Name
;
1474 Status
= FindFirstAttribute(&Context
, Vcb
, FileRecord
, FALSE
, &Attribute
);
1475 while (NT_SUCCESS(Status
))
1477 if (Attribute
->Type
== AttributeFileName
)
1479 Name
= (PFILENAME_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
1480 if (Name
->NameType
== NameType
||
1481 (Name
->NameType
== NTFS_FILE_NAME_WIN32_AND_DOS
&& NameType
== NTFS_FILE_NAME_WIN32
) ||
1482 (Name
->NameType
== NTFS_FILE_NAME_WIN32_AND_DOS
&& NameType
== NTFS_FILE_NAME_DOS
))
1484 FindCloseAttribute(&Context
);
1489 Status
= FindNextAttribute(&Context
, &Attribute
);
1492 FindCloseAttribute(&Context
);
1497 * GetPackedByteCount
1498 * Returns the minimum number of bytes needed to represent the value of a
1499 * 64-bit number. Used to encode data runs.
1502 GetPackedByteCount(LONGLONG NumberToPack
,
1507 if (NumberToPack
>= 0x0100000000000000)
1509 if (NumberToPack
>= 0x0001000000000000)
1511 if (NumberToPack
>= 0x0000010000000000)
1513 if (NumberToPack
>= 0x0000000100000000)
1515 if (NumberToPack
>= 0x0000000001000000)
1517 if (NumberToPack
>= 0x0000000000010000)
1519 if (NumberToPack
>= 0x0000000000000100)
1524 if (NumberToPack
> 0)
1526 // we have to make sure the number that gets encoded won't be interpreted as negative
1527 if (NumberToPack
>= 0x0080000000000000)
1529 if (NumberToPack
>= 0x0000800000000000)
1531 if (NumberToPack
>= 0x0000008000000000)
1533 if (NumberToPack
>= 0x0000000080000000)
1535 if (NumberToPack
>= 0x0000000000800000)
1537 if (NumberToPack
>= 0x0000000000008000)
1539 if (NumberToPack
>= 0x0000000000000080)
1545 if (NumberToPack
<= 0xff80000000000000)
1547 if (NumberToPack
<= 0xffff800000000000)
1549 if (NumberToPack
<= 0xffffff8000000000)
1551 if (NumberToPack
<= 0xffffffff80000000)
1553 if (NumberToPack
<= 0xffffffffff800000)
1555 if (NumberToPack
<= 0xffffffffffff8000)
1557 if (NumberToPack
<= 0xffffffffffffff80)
1564 GetLastClusterInDataRun(PDEVICE_EXTENSION Vcb
, PNTFS_ATTR_RECORD Attribute
, PULONGLONG LastCluster
)
1566 LONGLONG DataRunOffset
;
1567 ULONGLONG DataRunLength
;
1568 LONGLONG DataRunStartLCN
;
1570 ULONGLONG LastLCN
= 0;
1571 PUCHAR DataRun
= (PUCHAR
)Attribute
+ Attribute
->NonResident
.MappingPairsOffset
;
1573 if (!Attribute
->IsNonResident
)
1574 return STATUS_INVALID_PARAMETER
;
1578 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
1580 if (DataRunOffset
!= -1)
1583 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
1584 LastLCN
= DataRunStartLCN
;
1585 *LastCluster
= LastLCN
+ DataRunLength
- 1;
1592 return STATUS_SUCCESS
;
1595 PSTANDARD_INFORMATION
1596 GetStandardInformationFromRecord(PDEVICE_EXTENSION Vcb
,
1597 PFILE_RECORD_HEADER FileRecord
)
1600 FIND_ATTR_CONTXT Context
;
1601 PNTFS_ATTR_RECORD Attribute
;
1602 PSTANDARD_INFORMATION StdInfo
;
1604 Status
= FindFirstAttribute(&Context
, Vcb
, FileRecord
, FALSE
, &Attribute
);
1605 while (NT_SUCCESS(Status
))
1607 if (Attribute
->Type
== AttributeStandardInformation
)
1609 StdInfo
= (PSTANDARD_INFORMATION
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
1610 FindCloseAttribute(&Context
);
1614 Status
= FindNextAttribute(&Context
, &Attribute
);
1617 FindCloseAttribute(&Context
);
1622 * @name GetFileNameAttributeLength
1625 * Returns the size of a given FILENAME_ATTRIBUTE, in bytes.
1627 * @param FileNameAttribute
1628 * Pointer to a FILENAME_ATTRIBUTE to determine the size of.
1631 * The length of a FILENAME_ATTRIBUTE is variable and is dependent on the length of the file name stored at the end.
1632 * This function operates on the FILENAME_ATTRIBUTE proper, so don't try to pass it a PNTFS_ATTR_RECORD.
1634 ULONG
GetFileNameAttributeLength(PFILENAME_ATTRIBUTE FileNameAttribute
)
1636 ULONG Length
= FIELD_OFFSET(FILENAME_ATTRIBUTE
, Name
) + (FileNameAttribute
->NameLength
* sizeof(WCHAR
));
1641 GetBestFileNameFromRecord(PDEVICE_EXTENSION Vcb
,
1642 PFILE_RECORD_HEADER FileRecord
)
1644 PFILENAME_ATTRIBUTE FileName
;
1646 FileName
= GetFileNameFromRecord(Vcb
, FileRecord
, NTFS_FILE_NAME_POSIX
);
1647 if (FileName
== NULL
)
1649 FileName
= GetFileNameFromRecord(Vcb
, FileRecord
, NTFS_FILE_NAME_WIN32
);
1650 if (FileName
== NULL
)
1652 FileName
= GetFileNameFromRecord(Vcb
, FileRecord
, NTFS_FILE_NAME_DOS
);