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.
116 * STATUS_SUCCESS on success. STATUS_NOT_IMPLEMENTED if target address isn't at the end
117 * of the given file record.
120 * Only adding the attribute to the end of the file record is supported; AttributeAddress must
121 * be of type AttributeEnd.
122 * As it's implemented, this function is only intended to assist in creating new file records. It
123 * could be made more general-purpose by considering file records with an $ATTRIBUTE_LIST.
124 * It's the caller's responsibility to ensure the given file record has enough memory allocated
128 AddFileName(PFILE_RECORD_HEADER FileRecord
,
129 PNTFS_ATTR_RECORD AttributeAddress
,
130 PDEVICE_EXTENSION DeviceExt
,
131 PFILE_OBJECT FileObject
)
133 ULONG ResidentHeaderLength
= FIELD_OFFSET(NTFS_ATTR_RECORD
, Resident
.Reserved
) + sizeof(UCHAR
);
134 PFILENAME_ATTRIBUTE FileNameAttribute
;
135 LARGE_INTEGER SystemTime
;
136 ULONG FileRecordEnd
= AttributeAddress
->Length
;
137 ULONGLONG CurrentMFTIndex
= NTFS_FILE_ROOT
;
138 UNICODE_STRING Current
, Remaining
;
139 NTSTATUS Status
= STATUS_SUCCESS
;
140 ULONG FirstEntry
= 0;
142 if (AttributeAddress
->Type
!= AttributeEnd
)
144 DPRINT1("FIXME: Can only add $FILE_NAME attribute to the end of a file record.\n");
145 return STATUS_NOT_IMPLEMENTED
;
148 AttributeAddress
->Type
= AttributeFileName
;
149 AttributeAddress
->Instance
= FileRecord
->NextAttributeNumber
++;
151 FileNameAttribute
= (PFILENAME_ATTRIBUTE
)((LONG_PTR
)AttributeAddress
+ ResidentHeaderLength
);
154 KeQuerySystemTime(&SystemTime
);
155 FileNameAttribute
->CreationTime
= SystemTime
.QuadPart
;
156 FileNameAttribute
->ChangeTime
= SystemTime
.QuadPart
;
157 FileNameAttribute
->LastWriteTime
= SystemTime
.QuadPart
;
158 FileNameAttribute
->LastAccessTime
= SystemTime
.QuadPart
;
160 FileNameAttribute
->FileAttributes
= NTFS_FILE_TYPE_ARCHIVE
;
162 // we need to extract the filename from the path
163 DPRINT1("Pathname: %wZ\n", &FileObject
->FileName
);
165 FsRtlDissectName(FileObject
->FileName
, &Current
, &Remaining
);
167 while (Current
.Length
!= 0)
169 DPRINT1("Current: %wZ\n", &Current
);
171 Status
= NtfsFindMftRecord(DeviceExt
, CurrentMFTIndex
, &Current
, &FirstEntry
, FALSE
, &CurrentMFTIndex
);
172 if (!NT_SUCCESS(Status
))
177 if (Remaining
.Length
== 0)
180 FsRtlDissectName(Current
, &Current
, &Remaining
);
183 DPRINT1("MFT Index of parent: %I64u\n", CurrentMFTIndex
);
185 // set reference to parent directory
186 FileNameAttribute
->DirectoryFileReferenceNumber
= CurrentMFTIndex
;
188 // The highest 2 bytes should be the sequence number, unless the parent happens to be root
189 if (CurrentMFTIndex
== NTFS_FILE_ROOT
)
190 FileNameAttribute
->DirectoryFileReferenceNumber
|= (ULONGLONG
)NTFS_FILE_ROOT
<< 48;
192 FileNameAttribute
->DirectoryFileReferenceNumber
|= (ULONGLONG
)FileRecord
->SequenceNumber
<< 48;
194 DPRINT1("FileNameAttribute->DirectoryFileReferenceNumber: 0x%I64x\n", FileNameAttribute
->DirectoryFileReferenceNumber
);
196 FileNameAttribute
->NameLength
= Current
.Length
/ 2;
197 // TODO: Get proper nametype, add DOS links as needed
198 FileNameAttribute
->NameType
= NTFS_FILE_NAME_WIN32_AND_DOS
;
199 RtlCopyMemory(FileNameAttribute
->Name
, Current
.Buffer
, Current
.Length
);
200 FileRecord
->LinkCount
++;
202 AttributeAddress
->Length
= ResidentHeaderLength
+
203 FIELD_OFFSET(FILENAME_ATTRIBUTE
, Name
) + Current
.Length
;
204 AttributeAddress
->Length
= ALIGN_UP_BY(AttributeAddress
->Length
, 8);
206 AttributeAddress
->Resident
.ValueLength
= FIELD_OFFSET(FILENAME_ATTRIBUTE
, Name
) + Current
.Length
;
207 AttributeAddress
->Resident
.ValueOffset
= ResidentHeaderLength
;
208 AttributeAddress
->Resident
.Flags
= 1; // indexed
210 // move the attribute-end and file-record-end markers to the end of the file record
211 AttributeAddress
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)AttributeAddress
+ AttributeAddress
->Length
);
212 SetFileRecordEnd(FileRecord
, AttributeAddress
, FileRecordEnd
);
221 * Adds a run of allocated clusters to a non-resident attribute.
224 * Pointer to an NTFS_VCB for the destination volume.
227 * Pointer to an NTFS_ATTR_CONTEXT describing the destination attribute.
230 * Byte offset of the destination attribute relative to its file record.
233 * Pointer to a complete copy of the file record containing the destination attribute. Must be at least
234 * Vcb->NtfsInfo.BytesPerFileRecord bytes long.
236 * @param NextAssignedCluster
237 * Logical cluster number of the start of the data run being added.
240 * How many clusters are in the data run being added. Can't be 0.
243 * STATUS_SUCCESS on success. STATUS_INVALID_PARAMETER if AttrContext describes a resident attribute.
244 * STATUS_INSUFFICIENT_RESOURCES if ConvertDataRunsToLargeMCB() fails or if we fail to allocate a
245 * buffer for the new data runs.
246 * STATUS_INSUFFICIENT_RESOURCES or STATUS_UNSUCCESSFUL if FsRtlAddLargeMcbEntry() fails.
247 * STATUS_BUFFER_TOO_SMALL if ConvertLargeMCBToDataRuns() fails.
248 * STATUS_NOT_IMPLEMENTED if we need to migrate the attribute to an attribute list (TODO).
251 * Clusters should have been allocated previously with NtfsAllocateClusters().
256 AddRun(PNTFS_VCB Vcb
,
257 PNTFS_ATTR_CONTEXT AttrContext
,
259 PFILE_RECORD_HEADER FileRecord
,
260 ULONGLONG NextAssignedCluster
,
264 PUCHAR DataRun
= (PUCHAR
)&AttrContext
->Record
+ AttrContext
->Record
.NonResident
.MappingPairsOffset
;
265 int DataRunMaxLength
;
266 PNTFS_ATTR_RECORD DestinationAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ AttrOffset
);
267 LARGE_MCB DataRunsMCB
;
268 ULONG NextAttributeOffset
= AttrOffset
+ AttrContext
->Record
.Length
;
269 ULONGLONG NextVBN
= AttrContext
->Record
.NonResident
.LowestVCN
;
271 // Allocate some memory for the RunBuffer
273 ULONG RunBufferOffset
= 0;
275 if (!AttrContext
->Record
.IsNonResident
)
276 return STATUS_INVALID_PARAMETER
;
278 RunBuffer
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
281 DPRINT1("ERROR: Couldn't allocate memory for data runs!\n");
282 return STATUS_INSUFFICIENT_RESOURCES
;
285 // Convert the data runs to a map control block
286 Status
= ConvertDataRunsToLargeMCB(DataRun
, &DataRunsMCB
, &NextVBN
);
287 if (!NT_SUCCESS(Status
))
289 DPRINT1("Unable to convert data runs to MCB (probably ran out of memory)!\n");
290 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
294 // Add newly-assigned clusters to mcb
296 if (!FsRtlAddLargeMcbEntry(&DataRunsMCB
,
301 ExRaiseStatus(STATUS_UNSUCCESSFUL
);
303 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
304 FsRtlUninitializeLargeMcb(&DataRunsMCB
);
305 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
306 _SEH2_YIELD(_SEH2_GetExceptionCode());
310 // Convert the map control block back to encoded data runs
311 ConvertLargeMCBToDataRuns(&DataRunsMCB
, RunBuffer
, Vcb
->NtfsInfo
.BytesPerCluster
, &RunBufferOffset
);
313 // Get the amount of free space between the start of the of the first data run and the attribute end
314 DataRunMaxLength
= AttrContext
->Record
.Length
- AttrContext
->Record
.NonResident
.MappingPairsOffset
;
316 // Do we need to extend the attribute (or convert to attribute list)?
317 if (DataRunMaxLength
< RunBufferOffset
)
319 PNTFS_ATTR_RECORD NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ NextAttributeOffset
);
320 DataRunMaxLength
+= Vcb
->NtfsInfo
.BytesPerFileRecord
- NextAttributeOffset
- (sizeof(ULONG
) * 2);
322 // Can we move the end of the attribute?
323 if (NextAttribute
->Type
!= AttributeEnd
|| DataRunMaxLength
< RunBufferOffset
- 1)
325 DPRINT1("FIXME: Need to create attribute list! Max Data Run Length available: %d\n", DataRunMaxLength
);
326 if (NextAttribute
->Type
!= AttributeEnd
)
327 DPRINT1("There's another attribute after this one with type %0xlx\n", NextAttribute
->Type
);
328 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
329 FsRtlUninitializeLargeMcb(&DataRunsMCB
);
330 return STATUS_NOT_IMPLEMENTED
;
333 // calculate position of end markers
334 NextAttributeOffset
= AttrOffset
+ AttrContext
->Record
.NonResident
.MappingPairsOffset
+ RunBufferOffset
;
335 NextAttributeOffset
= ALIGN_UP_BY(NextAttributeOffset
, 8);
337 // Write the end markers
338 NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ NextAttributeOffset
);
339 NextAttribute
->Type
= AttributeEnd
;
340 NextAttribute
->Length
= FILE_RECORD_END
;
343 DestinationAttribute
->Length
= NextAttributeOffset
- AttrOffset
;
344 AttrContext
->Record
.Length
= DestinationAttribute
->Length
;
346 // We need to increase the FileRecord size
347 FileRecord
->BytesInUse
= NextAttributeOffset
+ (sizeof(ULONG
) * 2);
350 // NOTE: from this point on the original attribute record will contain invalid data in it's runbuffer
351 // TODO: Elegant fix? Could we free the old Record and allocate a new one without issue?
354 DestinationAttribute
->NonResident
.HighestVCN
=
355 AttrContext
->Record
.NonResident
.HighestVCN
= max(NextVBN
- 1 + RunLength
,
356 AttrContext
->Record
.NonResident
.HighestVCN
);
358 // Write data runs to destination attribute
359 RtlCopyMemory((PVOID
)((ULONG_PTR
)DestinationAttribute
+ DestinationAttribute
->NonResident
.MappingPairsOffset
),
363 // Update the file record
364 Status
= UpdateFileRecord(Vcb
, AttrContext
->FileMFTIndex
, FileRecord
);
366 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
367 FsRtlUninitializeLargeMcb(&DataRunsMCB
);
369 NtfsDumpDataRuns((PUCHAR
)((ULONG_PTR
)DestinationAttribute
+ DestinationAttribute
->NonResident
.MappingPairsOffset
), 0);
375 * @name AddStandardInformation
378 * Adds a $STANDARD_INFORMATION attribute to a given FileRecord.
381 * Pointer to a complete file record to add the attribute to. Caller is responsible for
382 * ensuring FileRecord is large enough to contain $STANDARD_INFORMATION.
384 * @param AttributeAddress
385 * Pointer to the region of memory that will receive the $STANDARD_INFORMATION attribute.
386 * This address must reside within FileRecord. Must be aligned to an 8-byte boundary (relative to FileRecord).
389 * STATUS_SUCCESS on success. STATUS_NOT_IMPLEMENTED if target address isn't at the end
390 * of the given file record.
393 * Only adding the attribute to the end of the file record is supported; AttributeAddress must
394 * be of type AttributeEnd.
395 * As it's implemented, this function is only intended to assist in creating new file records. It
396 * could be made more general-purpose by considering file records with an $ATTRIBUTE_LIST.
397 * It's the caller's responsibility to ensure the given file record has enough memory allocated
401 AddStandardInformation(PFILE_RECORD_HEADER FileRecord
,
402 PNTFS_ATTR_RECORD AttributeAddress
)
404 ULONG ResidentHeaderLength
= FIELD_OFFSET(NTFS_ATTR_RECORD
, Resident
.Reserved
) + sizeof(UCHAR
);
405 PSTANDARD_INFORMATION StandardInfo
= (PSTANDARD_INFORMATION
)((LONG_PTR
)AttributeAddress
+ ResidentHeaderLength
);
406 LARGE_INTEGER SystemTime
;
407 ULONG FileRecordEnd
= AttributeAddress
->Length
;
409 if (AttributeAddress
->Type
!= AttributeEnd
)
411 DPRINT1("FIXME: Can only add $STANDARD_INFORMATION attribute to the end of a file record.\n");
412 return STATUS_NOT_IMPLEMENTED
;
415 AttributeAddress
->Type
= AttributeStandardInformation
;
416 AttributeAddress
->Length
= sizeof(STANDARD_INFORMATION
) + ResidentHeaderLength
;
417 AttributeAddress
->Length
= ALIGN_UP_BY(AttributeAddress
->Length
, 8);
418 AttributeAddress
->Resident
.ValueLength
= sizeof(STANDARD_INFORMATION
);
419 AttributeAddress
->Resident
.ValueOffset
= ResidentHeaderLength
;
420 AttributeAddress
->Instance
= FileRecord
->NextAttributeNumber
++;
422 // set dates and times
423 KeQuerySystemTime(&SystemTime
);
424 StandardInfo
->CreationTime
= SystemTime
.QuadPart
;
425 StandardInfo
->ChangeTime
= SystemTime
.QuadPart
;
426 StandardInfo
->LastWriteTime
= SystemTime
.QuadPart
;
427 StandardInfo
->LastAccessTime
= SystemTime
.QuadPart
;
428 StandardInfo
->FileAttribute
= NTFS_FILE_TYPE_ARCHIVE
;
430 // move the attribute-end and file-record-end markers to the end of the file record
431 AttributeAddress
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)AttributeAddress
+ AttributeAddress
->Length
);
432 SetFileRecordEnd(FileRecord
, AttributeAddress
, FileRecordEnd
);
434 return STATUS_SUCCESS
;
438 * @name ConvertDataRunsToLargeMCB
441 * Converts binary data runs to a map control block.
444 * Pointer to the run data
447 * Pointer to an unitialized LARGE_MCB structure.
450 * STATUS_SUCCESS on success, STATUS_INSUFFICIENT_RESOURCES or STATUS_UNSUCCESSFUL if we fail to
451 * initialize the mcb or add an entry.
454 * Initializes the LARGE_MCB pointed to by DataRunsMCB. If this function succeeds, you
455 * need to call FsRtlUninitializeLargeMcb() when you're done with DataRunsMCB. This
456 * function will ensure the LargeMCB has been unitialized in case of failure.
460 ConvertDataRunsToLargeMCB(PUCHAR DataRun
,
461 PLARGE_MCB DataRunsMCB
,
464 LONGLONG DataRunOffset
;
465 ULONGLONG DataRunLength
;
466 LONGLONG DataRunStartLCN
;
467 ULONGLONG LastLCN
= 0;
469 // Initialize the MCB, potentially catch an exception
471 FsRtlInitializeLargeMcb(DataRunsMCB
, NonPagedPool
);
472 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
473 _SEH2_YIELD(return _SEH2_GetExceptionCode());
476 while (*DataRun
!= 0)
478 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
480 if (DataRunOffset
!= -1)
483 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
484 LastLCN
= DataRunStartLCN
;
487 if (!FsRtlAddLargeMcbEntry(DataRunsMCB
,
492 ExRaiseStatus(STATUS_UNSUCCESSFUL
);
494 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
495 FsRtlUninitializeLargeMcb(DataRunsMCB
);
496 _SEH2_YIELD(return _SEH2_GetExceptionCode());
501 *pNextVBN
+= DataRunLength
;
504 return STATUS_SUCCESS
;
508 * @name ConvertLargeMCBToDataRuns
511 * Converts a map control block to a series of encoded data runs (used by non-resident attributes).
514 * Pointer to a LARGE_MCB structure describing the data runs.
517 * Pointer to the buffer that will receive the encoded data runs.
519 * @param MaxBufferSize
520 * Size of RunBuffer, in bytes.
522 * @param UsedBufferSize
523 * Pointer to a ULONG that will receive the size of the data runs in bytes. Can't be NULL.
526 * STATUS_SUCCESS on success, STATUS_BUFFER_TOO_SMALL if RunBuffer is too small to contain the
531 ConvertLargeMCBToDataRuns(PLARGE_MCB DataRunsMCB
,
534 PULONG UsedBufferSize
)
536 NTSTATUS Status
= STATUS_SUCCESS
;
537 ULONG RunBufferOffset
= 0;
538 LONGLONG DataRunOffset
;
539 ULONGLONG LastLCN
= 0;
540 LONGLONG Vbn
, Lbn
, Count
;
544 DPRINT("\t[Vbn, Lbn, Count]\n");
546 // convert each mcb entry to a data run
547 for (i
= 0; FsRtlGetNextLargeMcbEntry(DataRunsMCB
, i
, &Vbn
, &Lbn
, &Count
); i
++)
549 UCHAR DataRunOffsetSize
= 0;
550 UCHAR DataRunLengthSize
= 0;
551 UCHAR ControlByte
= 0;
554 DPRINT("\t[%I64d, %I64d,%I64d]\n", Vbn
, Lbn
, Count
);
556 // TODO: check for holes and convert to sparse runs
557 DataRunOffset
= Lbn
- LastLCN
;
560 // now we need to determine how to represent DataRunOffset with the minimum number of bytes
561 DPRINT("Determining how many bytes needed to represent %I64x\n", DataRunOffset
);
562 DataRunOffsetSize
= GetPackedByteCount(DataRunOffset
, TRUE
);
563 DPRINT("%d bytes needed.\n", DataRunOffsetSize
);
565 // determine how to represent DataRunLengthSize with the minimum number of bytes
566 DPRINT("Determining how many bytes needed to represent %I64x\n", Count
);
567 DataRunLengthSize
= GetPackedByteCount(Count
, TRUE
);
568 DPRINT("%d bytes needed.\n", DataRunLengthSize
);
570 // ensure the next data run + end marker would be > Max buffer size
571 if (RunBufferOffset
+ 2 + DataRunLengthSize
+ DataRunOffsetSize
> MaxBufferSize
)
573 Status
= STATUS_BUFFER_TOO_SMALL
;
574 DPRINT1("FIXME: Ran out of room in buffer for data runs!\n");
578 // pack and copy the control byte
579 ControlByte
= (DataRunOffsetSize
<< 4) + DataRunLengthSize
;
580 RunBuffer
[RunBufferOffset
++] = ControlByte
;
582 // copy DataRunLength
583 RtlCopyMemory(RunBuffer
+ RunBufferOffset
, &Count
, DataRunLengthSize
);
584 RunBufferOffset
+= DataRunLengthSize
;
586 // copy DataRunOffset
587 RtlCopyMemory(RunBuffer
+ RunBufferOffset
, &DataRunOffset
, DataRunOffsetSize
);
588 RunBufferOffset
+= DataRunOffsetSize
;
592 RunBuffer
[RunBufferOffset
++] = 0;
594 *UsedBufferSize
= RunBufferOffset
;
595 DPRINT("New Size of DataRuns: %ld\n", *UsedBufferSize
);
601 DecodeRun(PUCHAR DataRun
,
602 LONGLONG
*DataRunOffset
,
603 ULONGLONG
*DataRunLength
)
605 UCHAR DataRunOffsetSize
;
606 UCHAR DataRunLengthSize
;
609 DataRunOffsetSize
= (*DataRun
>> 4) & 0xF;
610 DataRunLengthSize
= *DataRun
& 0xF;
614 for (i
= 0; i
< DataRunLengthSize
; i
++)
616 *DataRunLength
+= ((ULONG64
)*DataRun
) << (i
* 8);
620 /* NTFS 3+ sparse files */
621 if (DataRunOffsetSize
== 0)
627 for (i
= 0; i
< DataRunOffsetSize
- 1; i
++)
629 *DataRunOffset
+= ((ULONG64
)*DataRun
) << (i
* 8);
632 /* The last byte contains sign so we must process it different way. */
633 *DataRunOffset
= ((LONG64
)(CHAR
)(*(DataRun
++)) << (i
* 8)) + *DataRunOffset
;
636 DPRINT("DataRunOffsetSize: %x\n", DataRunOffsetSize
);
637 DPRINT("DataRunLengthSize: %x\n", DataRunLengthSize
);
638 DPRINT("DataRunOffset: %x\n", *DataRunOffset
);
639 DPRINT("DataRunLength: %x\n", *DataRunLength
);
645 FindRun(PNTFS_ATTR_RECORD NresAttr
,
650 if (vcn
< NresAttr
->NonResident
.LowestVCN
|| vcn
> NresAttr
->NonResident
.HighestVCN
)
653 DecodeRun((PUCHAR
)((ULONG_PTR
)NresAttr
+ NresAttr
->NonResident
.MappingPairsOffset
), (PLONGLONG
)lcn
, count
);
662 * Shrinks the allocation size of a non-resident attribute by a given number of clusters.
663 * Frees the clusters from the volume's $BITMAP file as well as the attribute's data runs.
666 * Pointer to an NTFS_VCB for the destination volume.
669 * Pointer to an NTFS_ATTR_CONTEXT describing the attribute from which the clusters will be freed.
672 * Byte offset of the destination attribute relative to its file record.
675 * Pointer to a complete copy of the file record containing the attribute. Must be at least
676 * Vcb->NtfsInfo.BytesPerFileRecord bytes long.
678 * @param ClustersToFree
679 * Number of clusters that should be freed from the end of the data stream. Must be no more
680 * Than the number of clusters assigned to the attribute (HighestVCN + 1).
683 * STATUS_SUCCESS on success. STATUS_INVALID_PARAMETER if AttrContext describes a resident attribute,
684 * or if the caller requested more clusters be freed than the attribute has been allocated.
685 * STATUS_INSUFFICIENT_RESOURCES if allocating a buffer for the data runs fails or
686 * if ConvertDataRunsToLargeMCB() fails.
687 * STATUS_BUFFER_TOO_SMALL if ConvertLargeMCBToDataRuns() fails.
692 FreeClusters(PNTFS_VCB Vcb
,
693 PNTFS_ATTR_CONTEXT AttrContext
,
695 PFILE_RECORD_HEADER FileRecord
,
696 ULONG ClustersToFree
)
698 NTSTATUS Status
= STATUS_SUCCESS
;
699 ULONG ClustersLeftToFree
= ClustersToFree
;
701 // convert data runs to mcb
702 PUCHAR DataRun
= (PUCHAR
)&AttrContext
->Record
+ AttrContext
->Record
.NonResident
.MappingPairsOffset
;
703 PNTFS_ATTR_RECORD DestinationAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ AttrOffset
);
704 LARGE_MCB DataRunsMCB
;
705 ULONG NextAttributeOffset
= AttrOffset
+ AttrContext
->Record
.Length
;
706 PNTFS_ATTR_RECORD NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ NextAttributeOffset
);
707 ULONGLONG NextVBN
= AttrContext
->Record
.NonResident
.LowestVCN
;
709 // Allocate some memory for the RunBuffer
711 ULONG RunBufferOffset
= 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 RunBuffer
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
728 DPRINT1("ERROR: Couldn't allocate memory for data runs!\n");
729 return STATUS_INSUFFICIENT_RESOURCES
;
732 // Convert the data runs to a map control block
733 Status
= ConvertDataRunsToLargeMCB(DataRun
, &DataRunsMCB
, &NextVBN
);
734 if (!NT_SUCCESS(Status
))
736 DPRINT1("Unable to convert data runs to MCB (probably ran out of memory)!\n");
737 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
741 BitmapRecord
= ExAllocatePoolWithTag(NonPagedPool
,
742 Vcb
->NtfsInfo
.BytesPerFileRecord
,
744 if (BitmapRecord
== NULL
)
746 DPRINT1("Error: Unable to allocate memory for bitmap file record!\n");
747 FsRtlUninitializeLargeMcb(&DataRunsMCB
);
748 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
749 return STATUS_NO_MEMORY
;
752 Status
= ReadFileRecord(Vcb
, NTFS_FILE_BITMAP
, BitmapRecord
);
753 if (!NT_SUCCESS(Status
))
755 DPRINT1("Error: Unable to read file record for bitmap!\n");
756 FsRtlUninitializeLargeMcb(&DataRunsMCB
);
757 ExFreePoolWithTag(BitmapRecord
, TAG_NTFS
);
758 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
762 Status
= FindAttribute(Vcb
, BitmapRecord
, AttributeData
, L
"", 0, &DataContext
, NULL
);
763 if (!NT_SUCCESS(Status
))
765 DPRINT1("Error: Unable to find data attribute for bitmap file!\n");
766 FsRtlUninitializeLargeMcb(&DataRunsMCB
);
767 ExFreePoolWithTag(BitmapRecord
, TAG_NTFS
);
768 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
772 BitmapDataSize
= AttributeDataLength(&DataContext
->Record
);
773 BitmapDataSize
= min(BitmapDataSize
, 0xffffffff);
774 ASSERT((BitmapDataSize
* 8) >= Vcb
->NtfsInfo
.ClusterCount
);
775 BitmapData
= ExAllocatePoolWithTag(NonPagedPool
, ROUND_UP(BitmapDataSize
, Vcb
->NtfsInfo
.BytesPerSector
), TAG_NTFS
);
776 if (BitmapData
== NULL
)
778 DPRINT1("Error: Unable to allocate memory for bitmap file data!\n");
779 ReleaseAttributeContext(DataContext
);
780 FsRtlUninitializeLargeMcb(&DataRunsMCB
);
781 ExFreePoolWithTag(BitmapRecord
, TAG_NTFS
);
782 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
786 ReadAttribute(Vcb
, DataContext
, 0, (PCHAR
)BitmapData
, (ULONG
)BitmapDataSize
);
788 RtlInitializeBitMap(&Bitmap
, (PULONG
)BitmapData
, Vcb
->NtfsInfo
.ClusterCount
);
790 // free clusters in $BITMAP file
791 while (ClustersLeftToFree
> 0)
793 LONGLONG LargeVbn
, LargeLbn
;
795 if (!FsRtlLookupLastLargeMcbEntry(&DataRunsMCB
, &LargeVbn
, &LargeLbn
))
797 Status
= STATUS_INVALID_PARAMETER
;
798 DPRINT1("DRIVER ERROR: FreeClusters called to free %lu clusters, which is %lu more clusters than are assigned to attribute!",
806 // deallocate this cluster
807 RtlClearBits(&Bitmap
, LargeLbn
, 1);
809 FsRtlTruncateLargeMcb(&DataRunsMCB
, AttrContext
->Record
.NonResident
.HighestVCN
);
810 AttrContext
->Record
.NonResident
.HighestVCN
= min(AttrContext
->Record
.NonResident
.HighestVCN
, AttrContext
->Record
.NonResident
.HighestVCN
- 1);
811 ClustersLeftToFree
--;
814 // update $BITMAP file on disk
815 Status
= WriteAttribute(Vcb
, DataContext
, 0, BitmapData
, (ULONG
)BitmapDataSize
, &LengthWritten
);
816 if (!NT_SUCCESS(Status
))
818 ReleaseAttributeContext(DataContext
);
819 FsRtlUninitializeLargeMcb(&DataRunsMCB
);
820 ExFreePoolWithTag(BitmapData
, TAG_NTFS
);
821 ExFreePoolWithTag(BitmapRecord
, TAG_NTFS
);
822 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
826 ReleaseAttributeContext(DataContext
);
827 ExFreePoolWithTag(BitmapData
, TAG_NTFS
);
828 ExFreePoolWithTag(BitmapRecord
, TAG_NTFS
);
830 // Convert the map control block back to encoded data runs
831 ConvertLargeMCBToDataRuns(&DataRunsMCB
, RunBuffer
, Vcb
->NtfsInfo
.BytesPerCluster
, &RunBufferOffset
);
834 DestinationAttribute
->NonResident
.HighestVCN
= AttrContext
->Record
.NonResident
.HighestVCN
;
836 // Write data runs to destination attribute
837 RtlCopyMemory((PVOID
)((ULONG_PTR
)DestinationAttribute
+ DestinationAttribute
->NonResident
.MappingPairsOffset
),
841 if (NextAttribute
->Type
== AttributeEnd
)
843 // update attribute length
844 AttrContext
->Record
.Length
= ALIGN_UP_BY(AttrContext
->Record
.NonResident
.MappingPairsOffset
+ RunBufferOffset
, 8);
845 DestinationAttribute
->Length
= AttrContext
->Record
.Length
;
848 NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)DestinationAttribute
+ DestinationAttribute
->Length
);
849 NextAttribute
->Type
= AttributeEnd
;
850 NextAttribute
->Length
= FILE_RECORD_END
;
852 // update file record length
853 FileRecord
->BytesInUse
= AttrOffset
+ DestinationAttribute
->Length
+ (sizeof(ULONG
) * 2);
856 // Update the file record
857 Status
= UpdateFileRecord(Vcb
, AttrContext
->FileMFTIndex
, FileRecord
);
859 FsRtlUninitializeLargeMcb(&DataRunsMCB
);
860 ExFreePoolWithTag(RunBuffer
, TAG_NTFS
);
862 NtfsDumpDataRuns((PUCHAR
)((ULONG_PTR
)DestinationAttribute
+ DestinationAttribute
->NonResident
.MappingPairsOffset
), 0);
869 InternalReadNonResidentAttributes(PFIND_ATTR_CONTXT Context
)
872 PNTFS_ATTR_RECORD Attribute
;
873 PNTFS_ATTR_CONTEXT ListContext
;
875 DPRINT("InternalReadNonResidentAttributes(%p)\n", Context
);
877 Attribute
= Context
->CurrAttr
;
878 ASSERT(Attribute
->Type
== AttributeAttributeList
);
880 if (Context
->OnlyResident
)
882 Context
->NonResidentStart
= NULL
;
883 Context
->NonResidentEnd
= NULL
;
884 return STATUS_SUCCESS
;
887 if (Context
->NonResidentStart
!= NULL
)
889 return STATUS_FILE_CORRUPT_ERROR
;
892 ListContext
= PrepareAttributeContext(Attribute
);
893 ListSize
= AttributeDataLength(&ListContext
->Record
);
894 if (ListSize
> 0xFFFFFFFF)
896 ReleaseAttributeContext(ListContext
);
897 return STATUS_BUFFER_OVERFLOW
;
900 Context
->NonResidentStart
= ExAllocatePoolWithTag(NonPagedPool
, (ULONG
)ListSize
, TAG_NTFS
);
901 if (Context
->NonResidentStart
== NULL
)
903 ReleaseAttributeContext(ListContext
);
904 return STATUS_INSUFFICIENT_RESOURCES
;
907 if (ReadAttribute(Context
->Vcb
, ListContext
, 0, (PCHAR
)Context
->NonResidentStart
, (ULONG
)ListSize
) != ListSize
)
909 ExFreePoolWithTag(Context
->NonResidentStart
, TAG_NTFS
);
910 Context
->NonResidentStart
= NULL
;
911 ReleaseAttributeContext(ListContext
);
912 return STATUS_FILE_CORRUPT_ERROR
;
915 ReleaseAttributeContext(ListContext
);
916 Context
->NonResidentEnd
= (PNTFS_ATTR_RECORD
)((PCHAR
)Context
->NonResidentStart
+ ListSize
);
917 return STATUS_SUCCESS
;
922 InternalGetNextAttribute(PFIND_ATTR_CONTXT Context
)
924 PNTFS_ATTR_RECORD NextAttribute
;
926 if (Context
->CurrAttr
== (PVOID
)-1)
931 if (Context
->CurrAttr
>= Context
->FirstAttr
&&
932 Context
->CurrAttr
< Context
->LastAttr
)
934 if (Context
->CurrAttr
->Length
== 0)
936 DPRINT1("Broken length!\n");
937 Context
->CurrAttr
= (PVOID
)-1;
941 NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)Context
->CurrAttr
+ Context
->CurrAttr
->Length
);
943 if (NextAttribute
> Context
->LastAttr
|| NextAttribute
< Context
->FirstAttr
)
945 DPRINT1("Broken length: 0x%lx!\n", Context
->CurrAttr
->Length
);
946 Context
->CurrAttr
= (PVOID
)-1;
950 Context
->Offset
+= ((ULONG_PTR
)NextAttribute
- (ULONG_PTR
)Context
->CurrAttr
);
951 Context
->CurrAttr
= NextAttribute
;
953 if (Context
->CurrAttr
< Context
->LastAttr
&&
954 Context
->CurrAttr
->Type
!= AttributeEnd
)
956 return Context
->CurrAttr
;
960 if (Context
->NonResidentStart
== NULL
)
962 Context
->CurrAttr
= (PVOID
)-1;
966 if (Context
->CurrAttr
< Context
->NonResidentStart
||
967 Context
->CurrAttr
>= Context
->NonResidentEnd
)
969 Context
->CurrAttr
= Context
->NonResidentStart
;
971 else if (Context
->CurrAttr
->Length
!= 0)
973 NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)Context
->CurrAttr
+ Context
->CurrAttr
->Length
);
974 Context
->Offset
+= ((ULONG_PTR
)NextAttribute
- (ULONG_PTR
)Context
->CurrAttr
);
975 Context
->CurrAttr
= NextAttribute
;
979 DPRINT1("Broken length!\n");
980 Context
->CurrAttr
= (PVOID
)-1;
984 if (Context
->CurrAttr
< Context
->NonResidentEnd
&&
985 Context
->CurrAttr
->Type
!= AttributeEnd
)
987 return Context
->CurrAttr
;
990 Context
->CurrAttr
= (PVOID
)-1;
995 FindFirstAttribute(PFIND_ATTR_CONTXT Context
,
996 PDEVICE_EXTENSION Vcb
,
997 PFILE_RECORD_HEADER FileRecord
,
998 BOOLEAN OnlyResident
,
999 PNTFS_ATTR_RECORD
* Attribute
)
1003 DPRINT("FindFistAttribute(%p, %p, %p, %p, %u, %p)\n", Context
, Vcb
, FileRecord
, OnlyResident
, Attribute
);
1006 Context
->OnlyResident
= OnlyResident
;
1007 Context
->FirstAttr
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ FileRecord
->AttributeOffset
);
1008 Context
->CurrAttr
= Context
->FirstAttr
;
1009 Context
->LastAttr
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ FileRecord
->BytesInUse
);
1010 Context
->NonResidentStart
= NULL
;
1011 Context
->NonResidentEnd
= NULL
;
1012 Context
->Offset
= FileRecord
->AttributeOffset
;
1014 if (Context
->FirstAttr
->Type
== AttributeEnd
)
1016 Context
->CurrAttr
= (PVOID
)-1;
1017 return STATUS_END_OF_FILE
;
1019 else if (Context
->FirstAttr
->Type
== AttributeAttributeList
)
1021 Status
= InternalReadNonResidentAttributes(Context
);
1022 if (!NT_SUCCESS(Status
))
1027 *Attribute
= InternalGetNextAttribute(Context
);
1028 if (*Attribute
== NULL
)
1030 return STATUS_END_OF_FILE
;
1035 *Attribute
= Context
->CurrAttr
;
1036 Context
->Offset
= (UCHAR
*)Context
->CurrAttr
- (UCHAR
*)FileRecord
;
1039 return STATUS_SUCCESS
;
1043 FindNextAttribute(PFIND_ATTR_CONTXT Context
,
1044 PNTFS_ATTR_RECORD
* Attribute
)
1048 DPRINT("FindNextAttribute(%p, %p)\n", Context
, Attribute
);
1050 *Attribute
= InternalGetNextAttribute(Context
);
1051 if (*Attribute
== NULL
)
1053 return STATUS_END_OF_FILE
;
1056 if (Context
->CurrAttr
->Type
!= AttributeAttributeList
)
1058 return STATUS_SUCCESS
;
1061 Status
= InternalReadNonResidentAttributes(Context
);
1062 if (!NT_SUCCESS(Status
))
1067 *Attribute
= InternalGetNextAttribute(Context
);
1068 if (*Attribute
== NULL
)
1070 return STATUS_END_OF_FILE
;
1073 return STATUS_SUCCESS
;
1077 FindCloseAttribute(PFIND_ATTR_CONTXT Context
)
1079 if (Context
->NonResidentStart
!= NULL
)
1081 ExFreePoolWithTag(Context
->NonResidentStart
, TAG_NTFS
);
1082 Context
->NonResidentStart
= NULL
;
1088 NtfsDumpFileNameAttribute(PNTFS_ATTR_RECORD Attribute
)
1090 PFILENAME_ATTRIBUTE FileNameAttr
;
1092 DbgPrint(" $FILE_NAME ");
1094 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
1096 FileNameAttr
= (PFILENAME_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
1097 DbgPrint(" (%x) '%.*S' ", FileNameAttr
->NameType
, FileNameAttr
->NameLength
, FileNameAttr
->Name
);
1098 DbgPrint(" '%x' \n", FileNameAttr
->FileAttributes
);
1099 DbgPrint(" AllocatedSize: %I64u\nDataSize: %I64u\n", FileNameAttr
->AllocatedSize
, FileNameAttr
->DataSize
);
1105 NtfsDumpStandardInformationAttribute(PNTFS_ATTR_RECORD Attribute
)
1107 PSTANDARD_INFORMATION StandardInfoAttr
;
1109 DbgPrint(" $STANDARD_INFORMATION ");
1111 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
1113 StandardInfoAttr
= (PSTANDARD_INFORMATION
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
1114 DbgPrint(" '%x' ", StandardInfoAttr
->FileAttribute
);
1120 NtfsDumpVolumeNameAttribute(PNTFS_ATTR_RECORD Attribute
)
1124 DbgPrint(" $VOLUME_NAME ");
1126 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
1128 VolumeName
= (PWCHAR
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
1129 DbgPrint(" '%.*S' ", Attribute
->Resident
.ValueLength
/ sizeof(WCHAR
), VolumeName
);
1135 NtfsDumpVolumeInformationAttribute(PNTFS_ATTR_RECORD Attribute
)
1137 PVOLINFO_ATTRIBUTE VolInfoAttr
;
1139 DbgPrint(" $VOLUME_INFORMATION ");
1141 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
1143 VolInfoAttr
= (PVOLINFO_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
1144 DbgPrint(" NTFS Version %u.%u Flags 0x%04hx ",
1145 VolInfoAttr
->MajorVersion
,
1146 VolInfoAttr
->MinorVersion
,
1147 VolInfoAttr
->Flags
);
1153 NtfsDumpIndexRootAttribute(PNTFS_ATTR_RECORD Attribute
)
1155 PINDEX_ROOT_ATTRIBUTE IndexRootAttr
;
1157 IndexRootAttr
= (PINDEX_ROOT_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
1159 if (IndexRootAttr
->AttributeType
== AttributeFileName
)
1160 ASSERT(IndexRootAttr
->CollationRule
== COLLATION_FILE_NAME
);
1162 DbgPrint(" $INDEX_ROOT (%uB, %u) ", IndexRootAttr
->SizeOfEntry
, IndexRootAttr
->ClustersPerIndexRecord
);
1164 if (IndexRootAttr
->Header
.Flags
== INDEX_ROOT_SMALL
)
1166 DbgPrint(" (small) ");
1170 ASSERT(IndexRootAttr
->Header
.Flags
== INDEX_ROOT_LARGE
);
1171 DbgPrint(" (large) ");
1178 NtfsDumpAttribute(PDEVICE_EXTENSION Vcb
,
1179 PNTFS_ATTR_RECORD Attribute
)
1181 UNICODE_STRING Name
;
1184 ULONGLONG runcount
= 0;
1186 switch (Attribute
->Type
)
1188 case AttributeFileName
:
1189 NtfsDumpFileNameAttribute(Attribute
);
1192 case AttributeStandardInformation
:
1193 NtfsDumpStandardInformationAttribute(Attribute
);
1196 case AttributeObjectId
:
1197 DbgPrint(" $OBJECT_ID ");
1200 case AttributeSecurityDescriptor
:
1201 DbgPrint(" $SECURITY_DESCRIPTOR ");
1204 case AttributeVolumeName
:
1205 NtfsDumpVolumeNameAttribute(Attribute
);
1208 case AttributeVolumeInformation
:
1209 NtfsDumpVolumeInformationAttribute(Attribute
);
1213 DbgPrint(" $DATA ");
1214 //DataBuf = ExAllocatePool(NonPagedPool,AttributeLengthAllocated(Attribute));
1217 case AttributeIndexRoot
:
1218 NtfsDumpIndexRootAttribute(Attribute
);
1221 case AttributeIndexAllocation
:
1222 DbgPrint(" $INDEX_ALLOCATION ");
1225 case AttributeBitmap
:
1226 DbgPrint(" $BITMAP ");
1229 case AttributeReparsePoint
:
1230 DbgPrint(" $REPARSE_POINT ");
1233 case AttributeEAInformation
:
1234 DbgPrint(" $EA_INFORMATION ");
1241 case AttributePropertySet
:
1242 DbgPrint(" $PROPERTY_SET ");
1245 case AttributeLoggedUtilityStream
:
1246 DbgPrint(" $LOGGED_UTILITY_STREAM ");
1250 DbgPrint(" Attribute %lx ",
1255 if (Attribute
->Type
!= AttributeAttributeList
)
1257 if (Attribute
->NameLength
!= 0)
1259 Name
.Length
= Attribute
->NameLength
* sizeof(WCHAR
);
1260 Name
.MaximumLength
= Name
.Length
;
1261 Name
.Buffer
= (PWCHAR
)((ULONG_PTR
)Attribute
+ Attribute
->NameOffset
);
1263 DbgPrint("'%wZ' ", &Name
);
1267 Attribute
->IsNonResident
? "non-resident" : "resident");
1269 if (Attribute
->IsNonResident
)
1271 FindRun(Attribute
,0,&lcn
, &runcount
);
1273 DbgPrint(" AllocatedSize %I64u DataSize %I64u InitilizedSize %I64u\n",
1274 Attribute
->NonResident
.AllocatedSize
, Attribute
->NonResident
.DataSize
, Attribute
->NonResident
.InitializedSize
);
1275 DbgPrint(" logical clusters: %I64u - %I64u\n",
1276 lcn
, lcn
+ runcount
- 1);
1279 DbgPrint(" %u bytes of data\n", Attribute
->Resident
.ValueLength
);
1284 VOID
NtfsDumpDataRunData(PUCHAR DataRun
)
1286 UCHAR DataRunOffsetSize
;
1287 UCHAR DataRunLengthSize
;
1290 DbgPrint("%02x ", *DataRun
);
1295 DataRunOffsetSize
= (*DataRun
>> 4) & 0xF;
1296 DataRunLengthSize
= *DataRun
& 0xF;
1299 for (i
= 0; i
< DataRunLengthSize
; i
++)
1301 DbgPrint("%02x ", *DataRun
);
1305 for (i
= 0; i
< DataRunOffsetSize
; i
++)
1307 DbgPrint("%02x ", *DataRun
);
1311 NtfsDumpDataRunData(DataRun
);
1316 NtfsDumpDataRuns(PVOID StartOfRun
,
1317 ULONGLONG CurrentLCN
)
1319 PUCHAR DataRun
= StartOfRun
;
1320 LONGLONG DataRunOffset
;
1321 ULONGLONG DataRunLength
;
1323 if (CurrentLCN
== 0)
1325 DPRINT1("Dumping data runs.\n\tData:\n\t\t");
1326 NtfsDumpDataRunData(StartOfRun
);
1327 DbgPrint("\n\tRuns:\n\t\tOff\t\tLCN\t\tLength\n");
1330 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
1332 if (DataRunOffset
!= -1)
1333 CurrentLCN
+= DataRunOffset
;
1335 DbgPrint("\t\t%I64d\t", DataRunOffset
);
1336 if (DataRunOffset
< 99999)
1338 DbgPrint("%I64u\t", CurrentLCN
);
1339 if (CurrentLCN
< 99999)
1341 DbgPrint("%I64u\n", DataRunLength
);
1344 DbgPrint("\t\t00\n");
1346 NtfsDumpDataRuns(DataRun
, CurrentLCN
);
1351 NtfsDumpFileAttributes(PDEVICE_EXTENSION Vcb
,
1352 PFILE_RECORD_HEADER FileRecord
)
1355 FIND_ATTR_CONTXT Context
;
1356 PNTFS_ATTR_RECORD Attribute
;
1358 Status
= FindFirstAttribute(&Context
, Vcb
, FileRecord
, FALSE
, &Attribute
);
1359 while (NT_SUCCESS(Status
))
1361 NtfsDumpAttribute(Vcb
, Attribute
);
1363 Status
= FindNextAttribute(&Context
, &Attribute
);
1366 FindCloseAttribute(&Context
);
1370 GetFileNameFromRecord(PDEVICE_EXTENSION Vcb
,
1371 PFILE_RECORD_HEADER FileRecord
,
1374 FIND_ATTR_CONTXT Context
;
1375 PNTFS_ATTR_RECORD Attribute
;
1376 PFILENAME_ATTRIBUTE Name
;
1379 Status
= FindFirstAttribute(&Context
, Vcb
, FileRecord
, FALSE
, &Attribute
);
1380 while (NT_SUCCESS(Status
))
1382 if (Attribute
->Type
== AttributeFileName
)
1384 Name
= (PFILENAME_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
1385 if (Name
->NameType
== NameType
||
1386 (Name
->NameType
== NTFS_FILE_NAME_WIN32_AND_DOS
&& NameType
== NTFS_FILE_NAME_WIN32
) ||
1387 (Name
->NameType
== NTFS_FILE_NAME_WIN32_AND_DOS
&& NameType
== NTFS_FILE_NAME_DOS
))
1389 FindCloseAttribute(&Context
);
1394 Status
= FindNextAttribute(&Context
, &Attribute
);
1397 FindCloseAttribute(&Context
);
1402 * GetPackedByteCount
1403 * Returns the minimum number of bytes needed to represent the value of a
1404 * 64-bit number. Used to encode data runs.
1407 GetPackedByteCount(LONGLONG NumberToPack
,
1413 if (NumberToPack
>= 0x0100000000000000)
1415 if (NumberToPack
>= 0x0001000000000000)
1417 if (NumberToPack
>= 0x0000010000000000)
1419 if (NumberToPack
>= 0x0000000100000000)
1421 if (NumberToPack
>= 0x0000000001000000)
1423 if (NumberToPack
>= 0x0000000000010000)
1425 if (NumberToPack
>= 0x0000000000000100)
1430 if (NumberToPack
> 0)
1432 // we have to make sure the number that gets encoded won't be interpreted as negative
1433 if (NumberToPack
>= 0x0080000000000000)
1435 if (NumberToPack
>= 0x0000800000000000)
1437 if (NumberToPack
>= 0x0000008000000000)
1439 if (NumberToPack
>= 0x0000000080000000)
1441 if (NumberToPack
>= 0x0000000000800000)
1443 if (NumberToPack
>= 0x0000000000008000)
1445 if (NumberToPack
>= 0x0000000000000080)
1452 if (NumberToPack
<= 0xff80000000000000)
1454 if (NumberToPack
<= 0xffff800000000000)
1456 if (NumberToPack
<= 0xffffff8000000000)
1458 if (NumberToPack
<= 0xffffffff80000000)
1460 if (NumberToPack
<= 0xffffffffff800000)
1462 if (NumberToPack
<= 0xffffffffffff8000)
1464 if (NumberToPack
<= 0xffffffffffffff80)
1472 GetLastClusterInDataRun(PDEVICE_EXTENSION Vcb
, PNTFS_ATTR_RECORD Attribute
, PULONGLONG LastCluster
)
1474 LONGLONG DataRunOffset
;
1475 ULONGLONG DataRunLength
;
1476 LONGLONG DataRunStartLCN
;
1478 ULONGLONG LastLCN
= 0;
1479 PUCHAR DataRun
= (PUCHAR
)Attribute
+ Attribute
->NonResident
.MappingPairsOffset
;
1481 if (!Attribute
->IsNonResident
)
1482 return STATUS_INVALID_PARAMETER
;
1486 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
1488 if (DataRunOffset
!= -1)
1491 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
1492 LastLCN
= DataRunStartLCN
;
1493 *LastCluster
= LastLCN
+ DataRunLength
- 1;
1500 return STATUS_SUCCESS
;
1503 PSTANDARD_INFORMATION
1504 GetStandardInformationFromRecord(PDEVICE_EXTENSION Vcb
,
1505 PFILE_RECORD_HEADER FileRecord
)
1508 FIND_ATTR_CONTXT Context
;
1509 PNTFS_ATTR_RECORD Attribute
;
1510 PSTANDARD_INFORMATION StdInfo
;
1512 Status
= FindFirstAttribute(&Context
, Vcb
, FileRecord
, FALSE
, &Attribute
);
1513 while (NT_SUCCESS(Status
))
1515 if (Attribute
->Type
== AttributeStandardInformation
)
1517 StdInfo
= (PSTANDARD_INFORMATION
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
1518 FindCloseAttribute(&Context
);
1522 Status
= FindNextAttribute(&Context
, &Attribute
);
1525 FindCloseAttribute(&Context
);
1530 GetBestFileNameFromRecord(PDEVICE_EXTENSION Vcb
,
1531 PFILE_RECORD_HEADER FileRecord
)
1533 PFILENAME_ATTRIBUTE FileName
;
1535 FileName
= GetFileNameFromRecord(Vcb
, FileRecord
, NTFS_FILE_NAME_POSIX
);
1536 if (FileName
== NULL
)
1538 FileName
= GetFileNameFromRecord(Vcb
, FileRecord
, NTFS_FILE_NAME_WIN32
);
1539 if (FileName
== NULL
)
1541 FileName
= GetFileNameFromRecord(Vcb
, FileRecord
, NTFS_FILE_NAME_DOS
);