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 $DATA attribute to a given FileRecord.
45 * Pointer to a complete file record to add the attribute to. Caller is responsible for
46 * ensuring FileRecord is large enough to contain $DATA.
48 * @param AttributeAddress
49 * Pointer to the region of memory that will receive the $DATA attribute.
50 * This address must reside within FileRecord. Must be aligned to an 8-byte boundary (relative to FileRecord).
53 * STATUS_SUCCESS on success. STATUS_NOT_IMPLEMENTED if target address isn't at the end
54 * of the given file record.
57 * Only adding the attribute to the end of the file record is supported; AttributeAddress must
58 * be of type AttributeEnd.
59 * As it's implemented, this function is only intended to assist in creating new file records. It
60 * could be made more general-purpose by considering file records with an $ATTRIBUTE_LIST.
61 * It's the caller's responsibility to ensure the given file record has enough memory allocated
65 AddData(PFILE_RECORD_HEADER FileRecord
,
66 PNTFS_ATTR_RECORD AttributeAddress
)
68 ULONG ResidentHeaderLength
= FIELD_OFFSET(NTFS_ATTR_RECORD
, Resident
.Reserved
) + sizeof(UCHAR
);
69 ULONG FileRecordEnd
= AttributeAddress
->Length
;
71 if (AttributeAddress
->Type
!= AttributeEnd
)
73 DPRINT1("FIXME: Can only add $DATA attribute to the end of a file record.\n");
74 return STATUS_NOT_IMPLEMENTED
;
77 AttributeAddress
->Type
= AttributeData
;
78 AttributeAddress
->Length
= ResidentHeaderLength
;
79 AttributeAddress
->Length
= ALIGN_UP_BY(AttributeAddress
->Length
, 8);
80 AttributeAddress
->Resident
.ValueLength
= 0;
81 AttributeAddress
->Resident
.ValueOffset
= ResidentHeaderLength
;
83 // for unnamed $DATA attributes, NameOffset equals header length
84 AttributeAddress
->NameOffset
= ResidentHeaderLength
;
85 AttributeAddress
->Instance
= FileRecord
->NextAttributeNumber
++;
87 // move the attribute-end and file-record-end markers to the end of the file record
88 AttributeAddress
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)AttributeAddress
+ AttributeAddress
->Length
);
89 SetFileRecordEnd(FileRecord
, AttributeAddress
, FileRecordEnd
);
91 return STATUS_SUCCESS
;
98 * Adds a $FILE_NAME attribute to a given FileRecord.
101 * Pointer to a complete file record to add the attribute to. Caller is responsible for
102 * ensuring FileRecord is large enough to contain $FILE_NAME.
104 * @param AttributeAddress
105 * Pointer to the region of memory that will receive the $FILE_NAME attribute.
106 * This address must reside within FileRecord. Must be aligned to an 8-byte boundary (relative to FileRecord).
109 * Points to the target disk's DEVICE_EXTENSION.
112 * Pointer to the FILE_OBJECT which represents the new name.
113 * This parameter is used to determine the filename and parent directory.
115 * @param CaseSensitive
116 * Boolean indicating if the function should operate in case-sensitive mode. This will be TRUE
117 * if an application opened the file with the FILE_FLAG_POSIX_SEMANTICS flag.
119 * @param ParentMftIndex
120 * Pointer to a ULONGLONG which will receive the index of the parent directory.
123 * STATUS_SUCCESS on success. STATUS_NOT_IMPLEMENTED if target address isn't at the end
124 * of the given file record.
127 * Only adding the attribute to the end of the file record is supported; AttributeAddress must
128 * be of type AttributeEnd.
129 * As it's implemented, this function is only intended to assist in creating new file records. It
130 * could be made more general-purpose by considering file records with an $ATTRIBUTE_LIST.
131 * It's the caller's responsibility to ensure the given file record has enough memory allocated
135 AddFileName(PFILE_RECORD_HEADER FileRecord
,
136 PNTFS_ATTR_RECORD AttributeAddress
,
137 PDEVICE_EXTENSION DeviceExt
,
138 PFILE_OBJECT FileObject
,
139 BOOLEAN CaseSensitive
,
140 PULONGLONG ParentMftIndex
)
142 ULONG ResidentHeaderLength
= FIELD_OFFSET(NTFS_ATTR_RECORD
, Resident
.Reserved
) + sizeof(UCHAR
);
143 PFILENAME_ATTRIBUTE FileNameAttribute
;
144 LARGE_INTEGER SystemTime
;
145 ULONG FileRecordEnd
= AttributeAddress
->Length
;
146 ULONGLONG CurrentMFTIndex
= NTFS_FILE_ROOT
;
147 UNICODE_STRING Current
, Remaining
, FilenameNoPath
;
148 NTSTATUS Status
= STATUS_SUCCESS
;
149 ULONG FirstEntry
= 0;
150 WCHAR Buffer
[MAX_PATH
];
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 RtlInitEmptyUnicodeString(&FilenameNoPath
, Buffer
, MAX_PATH
);
177 FsRtlDissectName(FileObject
->FileName
, &Current
, &Remaining
);
179 while (Current
.Length
!= 0)
181 DPRINT1("Current: %wZ\n", &Current
);
183 if(Remaining
.Length
!= 0)
184 RtlCopyUnicodeString(&FilenameNoPath
, &Remaining
);
186 Status
= NtfsFindMftRecord(DeviceExt
,
193 if (!NT_SUCCESS(Status
))
196 if (Remaining
.Length
== 0 )
198 if(Current
.Length
!= 0)
199 RtlCopyUnicodeString(&FilenameNoPath
, &Current
);
203 FsRtlDissectName(Current
, &Current
, &Remaining
);
206 DPRINT1("MFT Index of parent: %I64u\n", CurrentMFTIndex
);
208 // set reference to parent directory
209 FileNameAttribute
->DirectoryFileReferenceNumber
= CurrentMFTIndex
;
210 *ParentMftIndex
= CurrentMFTIndex
;
212 DPRINT1("SequenceNumber: 0x%02x\n", FileRecord
->SequenceNumber
);
214 // The highest 2 bytes should be the sequence number, unless the parent happens to be root
215 if (CurrentMFTIndex
== NTFS_FILE_ROOT
)
216 FileNameAttribute
->DirectoryFileReferenceNumber
|= (ULONGLONG
)NTFS_FILE_ROOT
<< 48;
218 FileNameAttribute
->DirectoryFileReferenceNumber
|= (ULONGLONG
)FileRecord
->SequenceNumber
<< 48;
220 DPRINT1("FileNameAttribute->DirectoryFileReferenceNumber: 0x%016I64x\n", FileNameAttribute
->DirectoryFileReferenceNumber
);
222 FileNameAttribute
->NameLength
= FilenameNoPath
.Length
/ sizeof(WCHAR
);
223 // TODO: Get proper nametype, add DOS links as needed
224 FileNameAttribute
->NameType
= NTFS_FILE_NAME_WIN32_AND_DOS
;
225 RtlCopyMemory(FileNameAttribute
->Name
, FilenameNoPath
.Buffer
, FilenameNoPath
.Length
);
226 FileRecord
->LinkCount
++;
228 AttributeAddress
->Length
= ResidentHeaderLength
+
229 FIELD_OFFSET(FILENAME_ATTRIBUTE
, Name
) + FilenameNoPath
.Length
;
230 AttributeAddress
->Length
= ALIGN_UP_BY(AttributeAddress
->Length
, 8);
232 AttributeAddress
->Resident
.ValueLength
= FIELD_OFFSET(FILENAME_ATTRIBUTE
, Name
) + FilenameNoPath
.Length
;
233 AttributeAddress
->Resident
.ValueOffset
= ResidentHeaderLength
;
234 AttributeAddress
->Resident
.Flags
= 1; // indexed
236 // move the attribute-end and file-record-end markers to the end of the file record
237 AttributeAddress
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)AttributeAddress
+ AttributeAddress
->Length
);
238 SetFileRecordEnd(FileRecord
, AttributeAddress
, FileRecordEnd
);
247 * Adds a run of allocated clusters to a non-resident attribute.
250 * Pointer to an NTFS_VCB for the destination volume.
253 * Pointer to an NTFS_ATTR_CONTEXT describing the destination attribute.
256 * Byte offset of the destination attribute relative to its file record.
259 * Pointer to a complete copy of the file record containing the destination attribute. Must be at least
260 * Vcb->NtfsInfo.BytesPerFileRecord bytes long.
262 * @param NextAssignedCluster
263 * Logical cluster number of the start of the data run being added.
266 * How many clusters are in the data run being added. Can't be 0.
269 * STATUS_SUCCESS on success. STATUS_INVALID_PARAMETER if AttrContext describes a resident attribute.
270 * STATUS_INSUFFICIENT_RESOURCES if ConvertDataRunsToLargeMCB() fails or if we fail to allocate a
271 * buffer for the new data runs.
272 * STATUS_INSUFFICIENT_RESOURCES or STATUS_UNSUCCESSFUL if FsRtlAddLargeMcbEntry() fails.
273 * STATUS_BUFFER_TOO_SMALL if ConvertLargeMCBToDataRuns() fails.
274 * STATUS_NOT_IMPLEMENTED if we need to migrate the attribute to an attribute list (TODO).
277 * Clusters should have been allocated previously with NtfsAllocateClusters().
282 AddRun(PNTFS_VCB Vcb
,
283 PNTFS_ATTR_CONTEXT AttrContext
,
285 PFILE_RECORD_HEADER FileRecord
,
286 ULONGLONG NextAssignedCluster
,
290 int DataRunMaxLength
;
291 PNTFS_ATTR_RECORD DestinationAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ AttrOffset
);
292 ULONG NextAttributeOffset
= AttrOffset
+ AttrContext
->Record
.Length
;
293 ULONGLONG NextVBN
= 0;
298 if (!AttrContext
->Record
.IsNonResident
)
299 return STATUS_INVALID_PARAMETER
;
301 if (AttrContext
->Record
.NonResident
.AllocatedSize
!= 0)
302 NextVBN
= AttrContext
->Record
.NonResident
.HighestVCN
+ 1;
304 // Add newly-assigned clusters to mcb
306 if (!FsRtlAddLargeMcbEntry(&AttrContext
->DataRunsMCB
,
311 ExRaiseStatus(STATUS_UNSUCCESSFUL
);
313 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
314 _SEH2_YIELD(_SEH2_GetExceptionCode());
317 RunBuffer
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
320 DPRINT1("ERROR: Couldn't allocate memory for data runs!\n");
321 return STATUS_INSUFFICIENT_RESOURCES
;
324 // Convert the map control block back to encoded data runs
325 ConvertLargeMCBToDataRuns(&AttrContext
->DataRunsMCB
, RunBuffer
, Vcb
->NtfsInfo
.BytesPerCluster
, &RunBufferSize
);
327 // Get the amount of free space between the start of the of the first data run and the attribute end
328 DataRunMaxLength
= AttrContext
->Record
.Length
- AttrContext
->Record
.NonResident
.MappingPairsOffset
;
330 // Do we need to extend the attribute (or convert to attribute list)?
331 if (DataRunMaxLength
< RunBufferSize
)
333 PNTFS_ATTR_RECORD NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ NextAttributeOffset
);
334 DataRunMaxLength
+= Vcb
->NtfsInfo
.BytesPerFileRecord
- NextAttributeOffset
- (sizeof(ULONG
) * 2);
336 // Can we move the end of the attribute?
337 if (NextAttribute
->Type
!= AttributeEnd
|| DataRunMaxLength
< RunBufferSize
- 1)
339 DPRINT1("FIXME: Need to create attribute list! Max Data Run Length available: %d\n", DataRunMaxLength
);
340 if (NextAttribute
->Type
!= AttributeEnd
)
341 DPRINT1("There's another attribute after this one with type %0xlx\n", NextAttribute
->Type
);
342 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
343 return STATUS_NOT_IMPLEMENTED
;
346 // calculate position of end markers
347 NextAttributeOffset
= AttrOffset
+ AttrContext
->Record
.NonResident
.MappingPairsOffset
+ RunBufferSize
;
348 NextAttributeOffset
= ALIGN_UP_BY(NextAttributeOffset
, 8);
351 DestinationAttribute
->Length
= NextAttributeOffset
- AttrOffset
;
352 AttrContext
->Record
.Length
= DestinationAttribute
->Length
;
354 // End the file record
355 NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ NextAttributeOffset
);
356 SetFileRecordEnd(FileRecord
, NextAttribute
, FILE_RECORD_END
);
360 DestinationAttribute
->NonResident
.HighestVCN
=
361 AttrContext
->Record
.NonResident
.HighestVCN
= max(NextVBN
- 1 + RunLength
,
362 AttrContext
->Record
.NonResident
.HighestVCN
);
364 // Write data runs to destination attribute
365 RtlCopyMemory((PVOID
)((ULONG_PTR
)DestinationAttribute
+ DestinationAttribute
->NonResident
.MappingPairsOffset
),
369 // Update the file record
370 Status
= UpdateFileRecord(Vcb
, AttrContext
->FileMFTIndex
, FileRecord
);
372 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
374 NtfsDumpDataRuns((PUCHAR
)((ULONG_PTR
)DestinationAttribute
+ DestinationAttribute
->NonResident
.MappingPairsOffset
), 0);
380 * @name AddStandardInformation
383 * Adds a $STANDARD_INFORMATION attribute to a given FileRecord.
386 * Pointer to a complete file record to add the attribute to. Caller is responsible for
387 * ensuring FileRecord is large enough to contain $STANDARD_INFORMATION.
389 * @param AttributeAddress
390 * Pointer to the region of memory that will receive the $STANDARD_INFORMATION attribute.
391 * This address must reside within FileRecord. Must be aligned to an 8-byte boundary (relative to FileRecord).
394 * STATUS_SUCCESS on success. STATUS_NOT_IMPLEMENTED if target address isn't at the end
395 * of the given file record.
398 * Only adding the attribute to the end of the file record is supported; AttributeAddress must
399 * be of type AttributeEnd.
400 * As it's implemented, this function is only intended to assist in creating new file records. It
401 * could be made more general-purpose by considering file records with an $ATTRIBUTE_LIST.
402 * It's the caller's responsibility to ensure the given file record has enough memory allocated
406 AddStandardInformation(PFILE_RECORD_HEADER FileRecord
,
407 PNTFS_ATTR_RECORD AttributeAddress
)
409 ULONG ResidentHeaderLength
= FIELD_OFFSET(NTFS_ATTR_RECORD
, Resident
.Reserved
) + sizeof(UCHAR
);
410 PSTANDARD_INFORMATION StandardInfo
= (PSTANDARD_INFORMATION
)((LONG_PTR
)AttributeAddress
+ ResidentHeaderLength
);
411 LARGE_INTEGER SystemTime
;
412 ULONG FileRecordEnd
= AttributeAddress
->Length
;
414 if (AttributeAddress
->Type
!= AttributeEnd
)
416 DPRINT1("FIXME: Can only add $STANDARD_INFORMATION attribute to the end of a file record.\n");
417 return STATUS_NOT_IMPLEMENTED
;
420 AttributeAddress
->Type
= AttributeStandardInformation
;
421 AttributeAddress
->Length
= sizeof(STANDARD_INFORMATION
) + ResidentHeaderLength
;
422 AttributeAddress
->Length
= ALIGN_UP_BY(AttributeAddress
->Length
, 8);
423 AttributeAddress
->Resident
.ValueLength
= sizeof(STANDARD_INFORMATION
);
424 AttributeAddress
->Resident
.ValueOffset
= ResidentHeaderLength
;
425 AttributeAddress
->Instance
= FileRecord
->NextAttributeNumber
++;
427 // set dates and times
428 KeQuerySystemTime(&SystemTime
);
429 StandardInfo
->CreationTime
= SystemTime
.QuadPart
;
430 StandardInfo
->ChangeTime
= SystemTime
.QuadPart
;
431 StandardInfo
->LastWriteTime
= SystemTime
.QuadPart
;
432 StandardInfo
->LastAccessTime
= SystemTime
.QuadPart
;
433 StandardInfo
->FileAttribute
= NTFS_FILE_TYPE_ARCHIVE
;
435 // move the attribute-end and file-record-end markers to the end of the file record
436 AttributeAddress
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)AttributeAddress
+ AttributeAddress
->Length
);
437 SetFileRecordEnd(FileRecord
, AttributeAddress
, FileRecordEnd
);
439 return STATUS_SUCCESS
;
443 * @name ConvertDataRunsToLargeMCB
446 * Converts binary data runs to a map control block.
449 * Pointer to the run data
452 * Pointer to an unitialized LARGE_MCB structure.
455 * STATUS_SUCCESS on success, STATUS_INSUFFICIENT_RESOURCES or STATUS_UNSUCCESSFUL if we fail to
456 * initialize the mcb or add an entry.
459 * Initializes the LARGE_MCB pointed to by DataRunsMCB. If this function succeeds, you
460 * need to call FsRtlUninitializeLargeMcb() when you're done with DataRunsMCB. This
461 * function will ensure the LargeMCB has been unitialized in case of failure.
465 ConvertDataRunsToLargeMCB(PUCHAR DataRun
,
466 PLARGE_MCB DataRunsMCB
,
469 LONGLONG DataRunOffset
;
470 ULONGLONG DataRunLength
;
471 LONGLONG DataRunStartLCN
;
472 ULONGLONG LastLCN
= 0;
474 // Initialize the MCB, potentially catch an exception
476 FsRtlInitializeLargeMcb(DataRunsMCB
, NonPagedPool
);
477 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
478 _SEH2_YIELD(return _SEH2_GetExceptionCode());
481 while (*DataRun
!= 0)
483 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
485 if (DataRunOffset
!= -1)
488 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
489 LastLCN
= DataRunStartLCN
;
492 if (!FsRtlAddLargeMcbEntry(DataRunsMCB
,
497 ExRaiseStatus(STATUS_UNSUCCESSFUL
);
499 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
500 FsRtlUninitializeLargeMcb(DataRunsMCB
);
501 _SEH2_YIELD(return _SEH2_GetExceptionCode());
506 *pNextVBN
+= DataRunLength
;
509 return STATUS_SUCCESS
;
513 * @name ConvertLargeMCBToDataRuns
516 * Converts a map control block to a series of encoded data runs (used by non-resident attributes).
519 * Pointer to a LARGE_MCB structure describing the data runs.
522 * Pointer to the buffer that will receive the encoded data runs.
524 * @param MaxBufferSize
525 * Size of RunBuffer, in bytes.
527 * @param UsedBufferSize
528 * Pointer to a ULONG that will receive the size of the data runs in bytes. Can't be NULL.
531 * STATUS_SUCCESS on success, STATUS_BUFFER_TOO_SMALL if RunBuffer is too small to contain the
536 ConvertLargeMCBToDataRuns(PLARGE_MCB DataRunsMCB
,
539 PULONG UsedBufferSize
)
541 NTSTATUS Status
= STATUS_SUCCESS
;
542 ULONG RunBufferOffset
= 0;
543 LONGLONG DataRunOffset
;
544 ULONGLONG LastLCN
= 0;
545 LONGLONG Vbn
, Lbn
, Count
;
549 DPRINT("\t[Vbn, Lbn, Count]\n");
551 // convert each mcb entry to a data run
552 for (i
= 0; FsRtlGetNextLargeMcbEntry(DataRunsMCB
, i
, &Vbn
, &Lbn
, &Count
); i
++)
554 UCHAR DataRunOffsetSize
= 0;
555 UCHAR DataRunLengthSize
= 0;
556 UCHAR ControlByte
= 0;
559 DPRINT("\t[%I64d, %I64d,%I64d]\n", Vbn
, Lbn
, Count
);
561 // TODO: check for holes and convert to sparse runs
562 DataRunOffset
= Lbn
- LastLCN
;
565 // now we need to determine how to represent DataRunOffset with the minimum number of bytes
566 DPRINT("Determining how many bytes needed to represent %I64x\n", DataRunOffset
);
567 DataRunOffsetSize
= GetPackedByteCount(DataRunOffset
, TRUE
);
568 DPRINT("%d bytes needed.\n", DataRunOffsetSize
);
570 // determine how to represent DataRunLengthSize with the minimum number of bytes
571 DPRINT("Determining how many bytes needed to represent %I64x\n", Count
);
572 DataRunLengthSize
= GetPackedByteCount(Count
, TRUE
);
573 DPRINT("%d bytes needed.\n", DataRunLengthSize
);
575 // ensure the next data run + end marker would be <= Max buffer size
576 if (RunBufferOffset
+ 2 + DataRunLengthSize
+ DataRunOffsetSize
> MaxBufferSize
)
578 Status
= STATUS_BUFFER_TOO_SMALL
;
579 DPRINT1("FIXME: Ran out of room in buffer for data runs!\n");
583 // pack and copy the control byte
584 ControlByte
= (DataRunOffsetSize
<< 4) + DataRunLengthSize
;
585 RunBuffer
[RunBufferOffset
++] = ControlByte
;
587 // copy DataRunLength
588 RtlCopyMemory(RunBuffer
+ RunBufferOffset
, &Count
, DataRunLengthSize
);
589 RunBufferOffset
+= DataRunLengthSize
;
591 // copy DataRunOffset
592 RtlCopyMemory(RunBuffer
+ RunBufferOffset
, &DataRunOffset
, DataRunOffsetSize
);
593 RunBufferOffset
+= DataRunOffsetSize
;
597 RunBuffer
[RunBufferOffset
++] = 0;
599 *UsedBufferSize
= RunBufferOffset
;
600 DPRINT("New Size of DataRuns: %ld\n", *UsedBufferSize
);
606 DecodeRun(PUCHAR DataRun
,
607 LONGLONG
*DataRunOffset
,
608 ULONGLONG
*DataRunLength
)
610 UCHAR DataRunOffsetSize
;
611 UCHAR DataRunLengthSize
;
614 DataRunOffsetSize
= (*DataRun
>> 4) & 0xF;
615 DataRunLengthSize
= *DataRun
& 0xF;
619 for (i
= 0; i
< DataRunLengthSize
; i
++)
621 *DataRunLength
+= ((ULONG64
)*DataRun
) << (i
* 8);
625 /* NTFS 3+ sparse files */
626 if (DataRunOffsetSize
== 0)
632 for (i
= 0; i
< DataRunOffsetSize
- 1; i
++)
634 *DataRunOffset
+= ((ULONG64
)*DataRun
) << (i
* 8);
637 /* The last byte contains sign so we must process it different way. */
638 *DataRunOffset
= ((LONG64
)(CHAR
)(*(DataRun
++)) << (i
* 8)) + *DataRunOffset
;
641 DPRINT("DataRunOffsetSize: %x\n", DataRunOffsetSize
);
642 DPRINT("DataRunLengthSize: %x\n", DataRunLengthSize
);
643 DPRINT("DataRunOffset: %x\n", *DataRunOffset
);
644 DPRINT("DataRunLength: %x\n", *DataRunLength
);
650 FindRun(PNTFS_ATTR_RECORD NresAttr
,
655 if (vcn
< NresAttr
->NonResident
.LowestVCN
|| vcn
> NresAttr
->NonResident
.HighestVCN
)
658 DecodeRun((PUCHAR
)((ULONG_PTR
)NresAttr
+ NresAttr
->NonResident
.MappingPairsOffset
), (PLONGLONG
)lcn
, count
);
667 * Shrinks the allocation size of a non-resident attribute by a given number of clusters.
668 * Frees the clusters from the volume's $BITMAP file as well as the attribute's data runs.
671 * Pointer to an NTFS_VCB for the destination volume.
674 * Pointer to an NTFS_ATTR_CONTEXT describing the attribute from which the clusters will be freed.
677 * Byte offset of the destination attribute relative to its file record.
680 * Pointer to a complete copy of the file record containing the attribute. Must be at least
681 * Vcb->NtfsInfo.BytesPerFileRecord bytes long.
683 * @param ClustersToFree
684 * Number of clusters that should be freed from the end of the data stream. Must be no more
685 * Than the number of clusters assigned to the attribute (HighestVCN + 1).
688 * STATUS_SUCCESS on success. STATUS_INVALID_PARAMETER if AttrContext describes a resident attribute,
689 * or if the caller requested more clusters be freed than the attribute has been allocated.
690 * STATUS_INSUFFICIENT_RESOURCES if allocating a buffer for the data runs fails or
691 * if ConvertDataRunsToLargeMCB() fails.
692 * STATUS_BUFFER_TOO_SMALL if ConvertLargeMCBToDataRuns() fails.
697 FreeClusters(PNTFS_VCB Vcb
,
698 PNTFS_ATTR_CONTEXT AttrContext
,
700 PFILE_RECORD_HEADER FileRecord
,
701 ULONG ClustersToFree
)
703 NTSTATUS Status
= STATUS_SUCCESS
;
704 ULONG ClustersLeftToFree
= ClustersToFree
;
706 PNTFS_ATTR_RECORD DestinationAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ AttrOffset
);
707 ULONG NextAttributeOffset
= AttrOffset
+ AttrContext
->Record
.Length
;
708 PNTFS_ATTR_RECORD NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ NextAttributeOffset
);
711 ULONG RunBufferSize
= 0;
713 PFILE_RECORD_HEADER BitmapRecord
;
714 PNTFS_ATTR_CONTEXT DataContext
;
715 ULONGLONG BitmapDataSize
;
720 if (!AttrContext
->Record
.IsNonResident
)
722 return STATUS_INVALID_PARAMETER
;
725 // Read the $Bitmap file
726 BitmapRecord
= ExAllocatePoolWithTag(NonPagedPool
,
727 Vcb
->NtfsInfo
.BytesPerFileRecord
,
729 if (BitmapRecord
== NULL
)
731 DPRINT1("Error: Unable to allocate memory for bitmap file record!\n");
732 return STATUS_NO_MEMORY
;
735 Status
= ReadFileRecord(Vcb
, NTFS_FILE_BITMAP
, BitmapRecord
);
736 if (!NT_SUCCESS(Status
))
738 DPRINT1("Error: Unable to read file record for bitmap!\n");
739 ExFreePoolWithTag(BitmapRecord
, TAG_NTFS
);
743 Status
= FindAttribute(Vcb
, BitmapRecord
, AttributeData
, L
"", 0, &DataContext
, NULL
);
744 if (!NT_SUCCESS(Status
))
746 DPRINT1("Error: Unable to find data attribute for bitmap file!\n");
747 ExFreePoolWithTag(BitmapRecord
, TAG_NTFS
);
751 BitmapDataSize
= AttributeDataLength(&DataContext
->Record
);
752 BitmapDataSize
= min(BitmapDataSize
, 0xffffffff);
753 ASSERT((BitmapDataSize
* 8) >= Vcb
->NtfsInfo
.ClusterCount
);
754 BitmapData
= ExAllocatePoolWithTag(NonPagedPool
, ROUND_UP(BitmapDataSize
, Vcb
->NtfsInfo
.BytesPerSector
), TAG_NTFS
);
755 if (BitmapData
== NULL
)
757 DPRINT1("Error: Unable to allocate memory for bitmap file data!\n");
758 ReleaseAttributeContext(DataContext
);
759 ExFreePoolWithTag(BitmapRecord
, TAG_NTFS
);
763 ReadAttribute(Vcb
, DataContext
, 0, (PCHAR
)BitmapData
, (ULONG
)BitmapDataSize
);
765 RtlInitializeBitMap(&Bitmap
, (PULONG
)BitmapData
, Vcb
->NtfsInfo
.ClusterCount
);
767 // free clusters in $BITMAP file
768 while (ClustersLeftToFree
> 0)
770 LONGLONG LargeVbn
, LargeLbn
;
772 if (!FsRtlLookupLastLargeMcbEntry(&AttrContext
->DataRunsMCB
, &LargeVbn
, &LargeLbn
))
774 Status
= STATUS_INVALID_PARAMETER
;
775 DPRINT1("DRIVER ERROR: FreeClusters called to free %lu clusters, which is %lu more clusters than are assigned to attribute!",
783 // deallocate this cluster
784 RtlClearBits(&Bitmap
, LargeLbn
, 1);
786 FsRtlTruncateLargeMcb(&AttrContext
->DataRunsMCB
, AttrContext
->Record
.NonResident
.HighestVCN
);
788 // decrement HighestVCN, but don't let it go below 0
789 AttrContext
->Record
.NonResident
.HighestVCN
= min(AttrContext
->Record
.NonResident
.HighestVCN
, AttrContext
->Record
.NonResident
.HighestVCN
- 1);
790 ClustersLeftToFree
--;
793 // update $BITMAP file on disk
794 Status
= WriteAttribute(Vcb
, DataContext
, 0, BitmapData
, (ULONG
)BitmapDataSize
, &LengthWritten
);
795 if (!NT_SUCCESS(Status
))
797 ReleaseAttributeContext(DataContext
);
798 ExFreePoolWithTag(BitmapData
, TAG_NTFS
);
799 ExFreePoolWithTag(BitmapRecord
, TAG_NTFS
);
803 ReleaseAttributeContext(DataContext
);
804 ExFreePoolWithTag(BitmapData
, TAG_NTFS
);
805 ExFreePoolWithTag(BitmapRecord
, TAG_NTFS
);
807 // Save updated data runs to file record
809 // Allocate some memory for a new RunBuffer
810 RunBuffer
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
813 DPRINT1("ERROR: Couldn't allocate memory for data runs!\n");
814 return STATUS_INSUFFICIENT_RESOURCES
;
817 // Convert the map control block back to encoded data runs
818 ConvertLargeMCBToDataRuns(&AttrContext
->DataRunsMCB
, RunBuffer
, Vcb
->NtfsInfo
.BytesPerCluster
, &RunBufferSize
);
821 DestinationAttribute
->NonResident
.HighestVCN
= AttrContext
->Record
.NonResident
.HighestVCN
;
823 // Write data runs to destination attribute
824 RtlCopyMemory((PVOID
)((ULONG_PTR
)DestinationAttribute
+ DestinationAttribute
->NonResident
.MappingPairsOffset
),
828 // Is DestinationAttribute the last attribute in the file record?
829 if (NextAttribute
->Type
== AttributeEnd
)
831 // update attribute length
832 AttrContext
->Record
.Length
= ALIGN_UP_BY(AttrContext
->Record
.NonResident
.MappingPairsOffset
+ RunBufferSize
, 8);
833 DestinationAttribute
->Length
= AttrContext
->Record
.Length
;
836 NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)DestinationAttribute
+ DestinationAttribute
->Length
);
837 SetFileRecordEnd(FileRecord
, NextAttribute
, FILE_RECORD_END
);
840 // Update the file record
841 Status
= UpdateFileRecord(Vcb
, AttrContext
->FileMFTIndex
, FileRecord
);
843 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
845 NtfsDumpDataRuns((PUCHAR
)((ULONG_PTR
)DestinationAttribute
+ DestinationAttribute
->NonResident
.MappingPairsOffset
), 0);
852 InternalReadNonResidentAttributes(PFIND_ATTR_CONTXT Context
)
855 PNTFS_ATTR_RECORD Attribute
;
856 PNTFS_ATTR_CONTEXT ListContext
;
858 DPRINT("InternalReadNonResidentAttributes(%p)\n", Context
);
860 Attribute
= Context
->CurrAttr
;
861 ASSERT(Attribute
->Type
== AttributeAttributeList
);
863 if (Context
->OnlyResident
)
865 Context
->NonResidentStart
= NULL
;
866 Context
->NonResidentEnd
= NULL
;
867 return STATUS_SUCCESS
;
870 if (Context
->NonResidentStart
!= NULL
)
872 return STATUS_FILE_CORRUPT_ERROR
;
875 ListContext
= PrepareAttributeContext(Attribute
);
876 ListSize
= AttributeDataLength(&ListContext
->Record
);
877 if (ListSize
> 0xFFFFFFFF)
879 ReleaseAttributeContext(ListContext
);
880 return STATUS_BUFFER_OVERFLOW
;
883 Context
->NonResidentStart
= ExAllocatePoolWithTag(NonPagedPool
, (ULONG
)ListSize
, TAG_NTFS
);
884 if (Context
->NonResidentStart
== NULL
)
886 ReleaseAttributeContext(ListContext
);
887 return STATUS_INSUFFICIENT_RESOURCES
;
890 if (ReadAttribute(Context
->Vcb
, ListContext
, 0, (PCHAR
)Context
->NonResidentStart
, (ULONG
)ListSize
) != ListSize
)
892 ExFreePoolWithTag(Context
->NonResidentStart
, TAG_NTFS
);
893 Context
->NonResidentStart
= NULL
;
894 ReleaseAttributeContext(ListContext
);
895 return STATUS_FILE_CORRUPT_ERROR
;
898 ReleaseAttributeContext(ListContext
);
899 Context
->NonResidentEnd
= (PNTFS_ATTR_RECORD
)((PCHAR
)Context
->NonResidentStart
+ ListSize
);
900 return STATUS_SUCCESS
;
905 InternalGetNextAttribute(PFIND_ATTR_CONTXT Context
)
907 PNTFS_ATTR_RECORD NextAttribute
;
909 if (Context
->CurrAttr
== (PVOID
)-1)
914 if (Context
->CurrAttr
>= Context
->FirstAttr
&&
915 Context
->CurrAttr
< Context
->LastAttr
)
917 if (Context
->CurrAttr
->Length
== 0)
919 DPRINT1("Broken length!\n");
920 Context
->CurrAttr
= (PVOID
)-1;
924 NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)Context
->CurrAttr
+ Context
->CurrAttr
->Length
);
926 if (NextAttribute
> Context
->LastAttr
|| NextAttribute
< Context
->FirstAttr
)
928 DPRINT1("Broken length: 0x%lx!\n", Context
->CurrAttr
->Length
);
929 Context
->CurrAttr
= (PVOID
)-1;
933 Context
->Offset
+= ((ULONG_PTR
)NextAttribute
- (ULONG_PTR
)Context
->CurrAttr
);
934 Context
->CurrAttr
= NextAttribute
;
936 if (Context
->CurrAttr
< Context
->LastAttr
&&
937 Context
->CurrAttr
->Type
!= AttributeEnd
)
939 return Context
->CurrAttr
;
943 if (Context
->NonResidentStart
== NULL
)
945 Context
->CurrAttr
= (PVOID
)-1;
949 if (Context
->CurrAttr
< Context
->NonResidentStart
||
950 Context
->CurrAttr
>= Context
->NonResidentEnd
)
952 Context
->CurrAttr
= Context
->NonResidentStart
;
954 else if (Context
->CurrAttr
->Length
!= 0)
956 NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)Context
->CurrAttr
+ Context
->CurrAttr
->Length
);
957 Context
->Offset
+= ((ULONG_PTR
)NextAttribute
- (ULONG_PTR
)Context
->CurrAttr
);
958 Context
->CurrAttr
= NextAttribute
;
962 DPRINT1("Broken length!\n");
963 Context
->CurrAttr
= (PVOID
)-1;
967 if (Context
->CurrAttr
< Context
->NonResidentEnd
&&
968 Context
->CurrAttr
->Type
!= AttributeEnd
)
970 return Context
->CurrAttr
;
973 Context
->CurrAttr
= (PVOID
)-1;
978 FindFirstAttribute(PFIND_ATTR_CONTXT Context
,
979 PDEVICE_EXTENSION Vcb
,
980 PFILE_RECORD_HEADER FileRecord
,
981 BOOLEAN OnlyResident
,
982 PNTFS_ATTR_RECORD
* Attribute
)
986 DPRINT("FindFistAttribute(%p, %p, %p, %p, %u, %p)\n", Context
, Vcb
, FileRecord
, OnlyResident
, Attribute
);
989 Context
->OnlyResident
= OnlyResident
;
990 Context
->FirstAttr
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ FileRecord
->AttributeOffset
);
991 Context
->CurrAttr
= Context
->FirstAttr
;
992 Context
->LastAttr
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ FileRecord
->BytesInUse
);
993 Context
->NonResidentStart
= NULL
;
994 Context
->NonResidentEnd
= NULL
;
995 Context
->Offset
= FileRecord
->AttributeOffset
;
997 if (Context
->FirstAttr
->Type
== AttributeEnd
)
999 Context
->CurrAttr
= (PVOID
)-1;
1000 return STATUS_END_OF_FILE
;
1002 else if (Context
->FirstAttr
->Type
== AttributeAttributeList
)
1004 Status
= InternalReadNonResidentAttributes(Context
);
1005 if (!NT_SUCCESS(Status
))
1010 *Attribute
= InternalGetNextAttribute(Context
);
1011 if (*Attribute
== NULL
)
1013 return STATUS_END_OF_FILE
;
1018 *Attribute
= Context
->CurrAttr
;
1019 Context
->Offset
= (UCHAR
*)Context
->CurrAttr
- (UCHAR
*)FileRecord
;
1022 return STATUS_SUCCESS
;
1026 FindNextAttribute(PFIND_ATTR_CONTXT Context
,
1027 PNTFS_ATTR_RECORD
* Attribute
)
1031 DPRINT("FindNextAttribute(%p, %p)\n", Context
, Attribute
);
1033 *Attribute
= InternalGetNextAttribute(Context
);
1034 if (*Attribute
== NULL
)
1036 return STATUS_END_OF_FILE
;
1039 if (Context
->CurrAttr
->Type
!= AttributeAttributeList
)
1041 return STATUS_SUCCESS
;
1044 Status
= InternalReadNonResidentAttributes(Context
);
1045 if (!NT_SUCCESS(Status
))
1050 *Attribute
= InternalGetNextAttribute(Context
);
1051 if (*Attribute
== NULL
)
1053 return STATUS_END_OF_FILE
;
1056 return STATUS_SUCCESS
;
1060 FindCloseAttribute(PFIND_ATTR_CONTXT Context
)
1062 if (Context
->NonResidentStart
!= NULL
)
1064 ExFreePoolWithTag(Context
->NonResidentStart
, TAG_NTFS
);
1065 Context
->NonResidentStart
= NULL
;
1071 NtfsDumpFileNameAttribute(PNTFS_ATTR_RECORD Attribute
)
1073 PFILENAME_ATTRIBUTE FileNameAttr
;
1075 DbgPrint(" $FILE_NAME ");
1077 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
1079 FileNameAttr
= (PFILENAME_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
1080 DbgPrint(" (%x) '%.*S' ", FileNameAttr
->NameType
, FileNameAttr
->NameLength
, FileNameAttr
->Name
);
1081 DbgPrint(" '%x' \n", FileNameAttr
->FileAttributes
);
1082 DbgPrint(" AllocatedSize: %I64u\nDataSize: %I64u\n", FileNameAttr
->AllocatedSize
, FileNameAttr
->DataSize
);
1083 DbgPrint(" File reference: 0x%016I64x\n", FileNameAttr
->DirectoryFileReferenceNumber
);
1089 NtfsDumpStandardInformationAttribute(PNTFS_ATTR_RECORD Attribute
)
1091 PSTANDARD_INFORMATION StandardInfoAttr
;
1093 DbgPrint(" $STANDARD_INFORMATION ");
1095 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
1097 StandardInfoAttr
= (PSTANDARD_INFORMATION
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
1098 DbgPrint(" '%x' ", StandardInfoAttr
->FileAttribute
);
1104 NtfsDumpVolumeNameAttribute(PNTFS_ATTR_RECORD Attribute
)
1108 DbgPrint(" $VOLUME_NAME ");
1110 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
1112 VolumeName
= (PWCHAR
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
1113 DbgPrint(" '%.*S' ", Attribute
->Resident
.ValueLength
/ sizeof(WCHAR
), VolumeName
);
1119 NtfsDumpVolumeInformationAttribute(PNTFS_ATTR_RECORD Attribute
)
1121 PVOLINFO_ATTRIBUTE VolInfoAttr
;
1123 DbgPrint(" $VOLUME_INFORMATION ");
1125 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
1127 VolInfoAttr
= (PVOLINFO_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
1128 DbgPrint(" NTFS Version %u.%u Flags 0x%04hx ",
1129 VolInfoAttr
->MajorVersion
,
1130 VolInfoAttr
->MinorVersion
,
1131 VolInfoAttr
->Flags
);
1137 NtfsDumpIndexRootAttribute(PNTFS_ATTR_RECORD Attribute
)
1139 PINDEX_ROOT_ATTRIBUTE IndexRootAttr
;
1140 ULONG currentOffset
;
1143 IndexRootAttr
= (PINDEX_ROOT_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
1145 if (IndexRootAttr
->AttributeType
== AttributeFileName
)
1146 ASSERT(IndexRootAttr
->CollationRule
== COLLATION_FILE_NAME
);
1148 DbgPrint(" $INDEX_ROOT (%u bytes per index record, %u clusters) ", IndexRootAttr
->SizeOfEntry
, IndexRootAttr
->ClustersPerIndexRecord
);
1150 if (IndexRootAttr
->Header
.Flags
== INDEX_ROOT_SMALL
)
1152 DbgPrint(" (small)\n");
1156 ASSERT(IndexRootAttr
->Header
.Flags
== INDEX_ROOT_LARGE
);
1157 DbgPrint(" (large)\n");
1160 DbgPrint(" Offset to first index: 0x%lx\n Total size of index entries: 0x%lx\n Allocated size of node: 0x%lx\n",
1161 IndexRootAttr
->Header
.FirstEntryOffset
,
1162 IndexRootAttr
->Header
.TotalSizeOfEntries
,
1163 IndexRootAttr
->Header
.AllocatedSize
);
1164 currentOffset
= IndexRootAttr
->Header
.FirstEntryOffset
;
1166 // print details of every node in the index
1167 while (currentOffset
< IndexRootAttr
->Header
.TotalSizeOfEntries
)
1169 PINDEX_ENTRY_ATTRIBUTE currentIndexExtry
= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)IndexRootAttr
+ 0x10 + currentOffset
);
1170 DbgPrint(" Index Node Entry %u", currentNode
++);
1171 if (currentIndexExtry
->Flags
& NTFS_INDEX_ENTRY_NODE
)
1172 DbgPrint(" (Branch)");
1174 DbgPrint(" (Leaf)");
1175 if((currentIndexExtry
->Flags
& NTFS_INDEX_ENTRY_END
))
1177 DbgPrint(" (Dummy Key)");
1179 DbgPrint("\n File Reference: 0x%016I64x\n", currentIndexExtry
->Data
.Directory
.IndexedFile
);
1180 DbgPrint(" Index Entry Length: 0x%x\n", currentIndexExtry
->Length
);
1181 DbgPrint(" Index Key Length: 0x%x\n", currentIndexExtry
->KeyLength
);
1183 // if this isn't the final (dummy) node, print info about the key (Filename attribute)
1184 if (!(currentIndexExtry
->Flags
& NTFS_INDEX_ENTRY_END
))
1186 UNICODE_STRING Name
;
1187 DbgPrint(" Parent File Reference: 0x%016I64x\n", currentIndexExtry
->FileName
.DirectoryFileReferenceNumber
);
1188 DbgPrint(" $FILENAME indexed: ");
1189 Name
.Length
= currentIndexExtry
->FileName
.NameLength
* sizeof(WCHAR
);
1190 Name
.MaximumLength
= Name
.Length
;
1191 Name
.Buffer
= currentIndexExtry
->FileName
.Name
;
1192 DbgPrint("'%wZ'\n", &Name
);
1195 // if this node has a sub-node beneath it
1196 if (currentIndexExtry
->Flags
& NTFS_INDEX_ENTRY_NODE
)
1198 // Print the VCN of the sub-node
1199 PULONGLONG SubNodeVCN
= (PULONGLONG
)((ULONG_PTR
)currentIndexExtry
+ currentIndexExtry
->Length
- 8);
1200 DbgPrint(" VCN of sub-node: 0x%llx\n", *SubNodeVCN
);
1203 currentOffset
+= currentIndexExtry
->Length
;
1204 ASSERT(currentIndexExtry
->Length
);
1212 NtfsDumpAttribute(PDEVICE_EXTENSION Vcb
,
1213 PNTFS_ATTR_RECORD Attribute
)
1215 UNICODE_STRING Name
;
1218 ULONGLONG runcount
= 0;
1220 switch (Attribute
->Type
)
1222 case AttributeFileName
:
1223 NtfsDumpFileNameAttribute(Attribute
);
1226 case AttributeStandardInformation
:
1227 NtfsDumpStandardInformationAttribute(Attribute
);
1230 case AttributeObjectId
:
1231 DbgPrint(" $OBJECT_ID ");
1234 case AttributeSecurityDescriptor
:
1235 DbgPrint(" $SECURITY_DESCRIPTOR ");
1238 case AttributeVolumeName
:
1239 NtfsDumpVolumeNameAttribute(Attribute
);
1242 case AttributeVolumeInformation
:
1243 NtfsDumpVolumeInformationAttribute(Attribute
);
1247 DbgPrint(" $DATA ");
1248 //DataBuf = ExAllocatePool(NonPagedPool,AttributeLengthAllocated(Attribute));
1251 case AttributeIndexRoot
:
1252 NtfsDumpIndexRootAttribute(Attribute
);
1255 case AttributeIndexAllocation
:
1256 DbgPrint(" $INDEX_ALLOCATION ");
1259 case AttributeBitmap
:
1260 DbgPrint(" $BITMAP ");
1263 case AttributeReparsePoint
:
1264 DbgPrint(" $REPARSE_POINT ");
1267 case AttributeEAInformation
:
1268 DbgPrint(" $EA_INFORMATION ");
1275 case AttributePropertySet
:
1276 DbgPrint(" $PROPERTY_SET ");
1279 case AttributeLoggedUtilityStream
:
1280 DbgPrint(" $LOGGED_UTILITY_STREAM ");
1284 DbgPrint(" Attribute %lx ",
1289 if (Attribute
->Type
!= AttributeAttributeList
)
1291 if (Attribute
->NameLength
!= 0)
1293 Name
.Length
= Attribute
->NameLength
* sizeof(WCHAR
);
1294 Name
.MaximumLength
= Name
.Length
;
1295 Name
.Buffer
= (PWCHAR
)((ULONG_PTR
)Attribute
+ Attribute
->NameOffset
);
1297 DbgPrint("'%wZ' ", &Name
);
1301 Attribute
->IsNonResident
? "non-resident" : "resident");
1303 if (Attribute
->IsNonResident
)
1305 FindRun(Attribute
,0,&lcn
, &runcount
);
1307 DbgPrint(" AllocatedSize %I64u DataSize %I64u InitilizedSize %I64u\n",
1308 Attribute
->NonResident
.AllocatedSize
, Attribute
->NonResident
.DataSize
, Attribute
->NonResident
.InitializedSize
);
1309 DbgPrint(" logical clusters: %I64u - %I64u\n",
1310 lcn
, lcn
+ runcount
- 1);
1313 DbgPrint(" %u bytes of data\n", Attribute
->Resident
.ValueLength
);
1318 VOID
NtfsDumpDataRunData(PUCHAR DataRun
)
1320 UCHAR DataRunOffsetSize
;
1321 UCHAR DataRunLengthSize
;
1324 DbgPrint("%02x ", *DataRun
);
1329 DataRunOffsetSize
= (*DataRun
>> 4) & 0xF;
1330 DataRunLengthSize
= *DataRun
& 0xF;
1333 for (i
= 0; i
< DataRunLengthSize
; i
++)
1335 DbgPrint("%02x ", *DataRun
);
1339 for (i
= 0; i
< DataRunOffsetSize
; i
++)
1341 DbgPrint("%02x ", *DataRun
);
1345 NtfsDumpDataRunData(DataRun
);
1350 NtfsDumpDataRuns(PVOID StartOfRun
,
1351 ULONGLONG CurrentLCN
)
1353 PUCHAR DataRun
= StartOfRun
;
1354 LONGLONG DataRunOffset
;
1355 ULONGLONG DataRunLength
;
1357 if (CurrentLCN
== 0)
1359 DPRINT1("Dumping data runs.\n\tData:\n\t\t");
1360 NtfsDumpDataRunData(StartOfRun
);
1361 DbgPrint("\n\tRuns:\n\t\tOff\t\tLCN\t\tLength\n");
1364 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
1366 if (DataRunOffset
!= -1)
1367 CurrentLCN
+= DataRunOffset
;
1369 DbgPrint("\t\t%I64d\t", DataRunOffset
);
1370 if (DataRunOffset
< 99999)
1372 DbgPrint("%I64u\t", CurrentLCN
);
1373 if (CurrentLCN
< 99999)
1375 DbgPrint("%I64u\n", DataRunLength
);
1378 DbgPrint("\t\t00\n");
1380 NtfsDumpDataRuns(DataRun
, CurrentLCN
);
1385 NtfsDumpFileAttributes(PDEVICE_EXTENSION Vcb
,
1386 PFILE_RECORD_HEADER FileRecord
)
1389 FIND_ATTR_CONTXT Context
;
1390 PNTFS_ATTR_RECORD Attribute
;
1392 Status
= FindFirstAttribute(&Context
, Vcb
, FileRecord
, FALSE
, &Attribute
);
1393 while (NT_SUCCESS(Status
))
1395 NtfsDumpAttribute(Vcb
, Attribute
);
1397 Status
= FindNextAttribute(&Context
, &Attribute
);
1400 FindCloseAttribute(&Context
);
1404 GetFileNameFromRecord(PDEVICE_EXTENSION Vcb
,
1405 PFILE_RECORD_HEADER FileRecord
,
1408 FIND_ATTR_CONTXT Context
;
1409 PNTFS_ATTR_RECORD Attribute
;
1410 PFILENAME_ATTRIBUTE Name
;
1413 Status
= FindFirstAttribute(&Context
, Vcb
, FileRecord
, FALSE
, &Attribute
);
1414 while (NT_SUCCESS(Status
))
1416 if (Attribute
->Type
== AttributeFileName
)
1418 Name
= (PFILENAME_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
1419 if (Name
->NameType
== NameType
||
1420 (Name
->NameType
== NTFS_FILE_NAME_WIN32_AND_DOS
&& NameType
== NTFS_FILE_NAME_WIN32
) ||
1421 (Name
->NameType
== NTFS_FILE_NAME_WIN32_AND_DOS
&& NameType
== NTFS_FILE_NAME_DOS
))
1423 FindCloseAttribute(&Context
);
1428 Status
= FindNextAttribute(&Context
, &Attribute
);
1431 FindCloseAttribute(&Context
);
1436 * GetPackedByteCount
1437 * Returns the minimum number of bytes needed to represent the value of a
1438 * 64-bit number. Used to encode data runs.
1441 GetPackedByteCount(LONGLONG NumberToPack
,
1447 if (NumberToPack
>= 0x0100000000000000)
1449 if (NumberToPack
>= 0x0001000000000000)
1451 if (NumberToPack
>= 0x0000010000000000)
1453 if (NumberToPack
>= 0x0000000100000000)
1455 if (NumberToPack
>= 0x0000000001000000)
1457 if (NumberToPack
>= 0x0000000000010000)
1459 if (NumberToPack
>= 0x0000000000000100)
1464 if (NumberToPack
> 0)
1466 // we have to make sure the number that gets encoded won't be interpreted as negative
1467 if (NumberToPack
>= 0x0080000000000000)
1469 if (NumberToPack
>= 0x0000800000000000)
1471 if (NumberToPack
>= 0x0000008000000000)
1473 if (NumberToPack
>= 0x0000000080000000)
1475 if (NumberToPack
>= 0x0000000000800000)
1477 if (NumberToPack
>= 0x0000000000008000)
1479 if (NumberToPack
>= 0x0000000000000080)
1486 if (NumberToPack
<= 0xff80000000000000)
1488 if (NumberToPack
<= 0xffff800000000000)
1490 if (NumberToPack
<= 0xffffff8000000000)
1492 if (NumberToPack
<= 0xffffffff80000000)
1494 if (NumberToPack
<= 0xffffffffff800000)
1496 if (NumberToPack
<= 0xffffffffffff8000)
1498 if (NumberToPack
<= 0xffffffffffffff80)
1506 GetLastClusterInDataRun(PDEVICE_EXTENSION Vcb
, PNTFS_ATTR_RECORD Attribute
, PULONGLONG LastCluster
)
1508 LONGLONG DataRunOffset
;
1509 ULONGLONG DataRunLength
;
1510 LONGLONG DataRunStartLCN
;
1512 ULONGLONG LastLCN
= 0;
1513 PUCHAR DataRun
= (PUCHAR
)Attribute
+ Attribute
->NonResident
.MappingPairsOffset
;
1515 if (!Attribute
->IsNonResident
)
1516 return STATUS_INVALID_PARAMETER
;
1520 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
1522 if (DataRunOffset
!= -1)
1525 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
1526 LastLCN
= DataRunStartLCN
;
1527 *LastCluster
= LastLCN
+ DataRunLength
- 1;
1534 return STATUS_SUCCESS
;
1537 PSTANDARD_INFORMATION
1538 GetStandardInformationFromRecord(PDEVICE_EXTENSION Vcb
,
1539 PFILE_RECORD_HEADER FileRecord
)
1542 FIND_ATTR_CONTXT Context
;
1543 PNTFS_ATTR_RECORD Attribute
;
1544 PSTANDARD_INFORMATION StdInfo
;
1546 Status
= FindFirstAttribute(&Context
, Vcb
, FileRecord
, FALSE
, &Attribute
);
1547 while (NT_SUCCESS(Status
))
1549 if (Attribute
->Type
== AttributeStandardInformation
)
1551 StdInfo
= (PSTANDARD_INFORMATION
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
1552 FindCloseAttribute(&Context
);
1556 Status
= FindNextAttribute(&Context
, &Attribute
);
1559 FindCloseAttribute(&Context
);
1564 GetBestFileNameFromRecord(PDEVICE_EXTENSION Vcb
,
1565 PFILE_RECORD_HEADER FileRecord
)
1567 PFILENAME_ATTRIBUTE FileName
;
1569 FileName
= GetFileNameFromRecord(Vcb
, FileRecord
, NTFS_FILE_NAME_POSIX
);
1570 if (FileName
== NULL
)
1572 FileName
= GetFileNameFromRecord(Vcb
, FileRecord
, NTFS_FILE_NAME_WIN32
);
1573 if (FileName
== NULL
)
1575 FileName
= GetFileNameFromRecord(Vcb
, FileRecord
, NTFS_FILE_NAME_DOS
);