ReactOS 0.4.17-dev-1133-gc25e4fd
mft.c File Reference
#include "ntfs.h"
#include <ntintsafe.h>
#include <debug.h>
Include dependency graph for mft.c:

Go to the source code of this file.

Macros

#define NDEBUG
 

Functions

PNTFS_ATTR_CONTEXT PrepareAttributeContext (PNTFS_ATTR_RECORD AttrRecord)
 
VOID ReleaseAttributeContext (PNTFS_ATTR_CONTEXT Context)
 
FindAttribute

@implemented

Searches a file record for an attribute matching the given type and name.

Parameters
OffsetOptional pointer to a ULONG that will receive the offset of the found attribute from the beginning of the record. Can be set to NULL.
NTSTATUS FindAttribute (PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER MftRecord, ULONG Type, PCWSTR Name, ULONG NameLength, PNTFS_ATTR_CONTEXT *AttrCtx, PULONG Offset)
 
ULONGLONG AttributeAllocatedLength (PNTFS_ATTR_RECORD AttrRecord)
 
ULONGLONG AttributeDataLength (PNTFS_ATTR_RECORD AttrRecord)
 
IncreaseMftSize

@implemented

Increases the size of the master file table on a volume, increasing the space available for file records.

Parameters
VcbPointer to the VCB (DEVICE_EXTENSION) of the target volume.
CanWaitBoolean indicating if the function is allowed to wait for exclusive access to the master file table. This will only be relevant if the MFT doesn't have any free file records and needs to be enlarged.
Returns
STATUS_SUCCESS on success. STATUS_INSUFFICIENT_RESOURCES if an allocation fails. STATUS_INVALID_PARAMETER if there was an error reading the Mft's bitmap. STATUS_CANT_WAIT if CanWait was FALSE and the function could not get immediate, exclusive access to the MFT.
Remarks
Increases the size of the Master File Table by 64 records. Bitmap entries for the new records are cleared, and the bitmap is also enlarged if needed. Mimicking Windows' behavior when enlarging the mft is still TODO. This function will wait for exlusive access to the volume fcb.
NTSTATUS IncreaseMftSize (PDEVICE_EXTENSION Vcb, BOOLEAN CanWait)
 
MoveAttributes

@implemented

Moves a block of attributes to a new location in the file Record. The attribute at FirstAttributeToMove and every attribute after that will be moved to MoveTo.

Parameters
DeviceExtPointer to the DEVICE_EXTENSION (VCB) of the target volume.
FirstAttributeToMovePointer to the first NTFS_ATTR_RECORD that needs to be moved. This pointer must reside within a file record.
FirstAttributeOffsetOffset of FirstAttributeToMove relative to the beginning of the file record.
MoveToULONG_PTR with the memory location that will be the new location of the first attribute being moved.
Returns
The new location of the final attribute (i.e. AttributeEnd marker).
PNTFS_ATTR_RECORD MoveAttributes (PDEVICE_EXTENSION DeviceExt, PNTFS_ATTR_RECORD FirstAttributeToMove, ULONG FirstAttributeOffset, ULONG_PTR MoveTo)
 
NTSTATUS InternalSetResidentAttributeLength (PDEVICE_EXTENSION DeviceExt, PNTFS_ATTR_CONTEXT AttrContext, PFILE_RECORD_HEADER FileRecord, ULONG AttrOffset, ULONG DataSize)
 
NTSTATUS SetAttributeDataLength (PFILE_OBJECT FileObject, PNTFS_FCB Fcb, PNTFS_ATTR_CONTEXT AttrContext, ULONG AttrOffset, PFILE_RECORD_HEADER FileRecord, PLARGE_INTEGER DataSize)
 
SetFileRecordEnd

@implemented

This small function sets a new endpoint for the file record. It set's the final AttrEnd->Type to AttributeEnd and recalculates the bytes used by the file record.

Parameters
FileRecordPointer to the file record whose endpoint (length) will be set.
AttrEndPointer to section of memory that will receive the AttributeEnd marker. This must point to memory allocated for the FileRecord. Must be aligned to an 8-byte boundary (relative to FileRecord).
EndMarkerThis value will be written after AttributeEnd but isn't critical at all. When Windows resizes a file record, it preserves the final ULONG that previously ended the record, even though this value is (to my knowledge) never used. We emulate this behavior.
VOID SetFileRecordEnd (PFILE_RECORD_HEADER FileRecord, PNTFS_ATTR_RECORD AttrEnd, ULONG EndMarker)
 
NtfsExtendAllocation

@implemented

Assigns clusters to a non-resident attribute until its allocation reaches AllocationSize. Data size is left alone, so this also reserves room ahead of a growing file.

Parameters
VcbPointer to a DEVICE_EXTENSION describing the target disk.
AttrContextPNTFS_ATTR_CONTEXT describing the attribute to extend.
AttrOffsetOffset, from the beginning of the record, of the attribute.
FileRecordPointer to a complete copy of the file record containing the attribute.
AllocationSizeNew allocation in bytes. Must be a multiple of the cluster size.
Returns
STATUS_SUCCESS on success, or the error from NtfsAllocateClusters() or AddRun().
Remarks
AddRun() writes the file record for each run it adds, but the new AllocatedSize only reaches the disk with the caller's next UpdateFileRecord().
NTSTATUS NtfsExtendAllocation (_In_ PDEVICE_EXTENSION Vcb, _In_ PNTFS_ATTR_CONTEXT AttrContext, _In_ ULONG AttrOffset, _In_ PFILE_RECORD_HEADER FileRecord, _In_ ULONGLONG AllocationSize)
 
NtfsTrimAllocation

@implemented

Gives back clusters a growing file reserved past its end. Called when the last handle goes away.

Parameters
FcbFCB of the file. The caller holds its MainResource exclusively.
Returns
STATUS_SUCCESS on success, including when there's nothing to trim.
NTSTATUS NtfsTrimAllocation (_In_ PNTFS_FCB Fcb)
 
SetNonResidentAttributeDataLength

@implemented

Called by SetAttributeDataLength() and IncreaseMftSize() to set the size of a non-resident attribute. Doesn't update the file record.

Parameters
VcbPointer to a DEVICE_EXTENSION describing the target disk.
AttrContextPNTFS_ATTR_CONTEXT describing the location of the attribute whose size is being set.
AttrOffsetOffset, from the beginning of the record, of the attribute being sized.
FileRecordPointer to a file record containing the attribute to be resized. Must be a complete file record, not just the header.
DataSizePointer to a LARGE_INTEGER describing the new size of the attribute's data.
Returns
STATUS_SUCCESS on success; STATUS_INSUFFICIENT_RESOURCES if an allocation fails. STATUS_INVALID_PARAMETER if we can't find the last cluster in the data run.
Remarks
Called by SetAttributeDataLength() and IncreaseMftSize(). Use SetAttributeDataLength() unless you have a good reason to use this. Doesn't update the file record on disk. Doesn't inform the cache controller of changes with any associated files. Synchronization is the callers responsibility. Growing never gives back clusters reserved by NtfsExtendAllocation(); only a truncate does.
NTSTATUS SetNonResidentAttributeDataLength (PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT AttrContext, ULONG AttrOffset, PFILE_RECORD_HEADER FileRecord, PLARGE_INTEGER DataSize)
 
SetResidentAttributeDataLength

@implemented

Called by SetAttributeDataLength() to set the size of a non-resident attribute. Doesn't update the file record.

Parameters
VcbPointer to a DEVICE_EXTENSION describing the target disk.
AttrContextPNTFS_ATTR_CONTEXT describing the location of the attribute whose size is being set.
AttrOffsetOffset, from the beginning of the record, of the attribute being sized.
FileRecordPointer to a file record containing the attribute to be resized. Must be a complete file record, not just the header.
DataSizePointer to a LARGE_INTEGER describing the new size of the attribute's data.
Returns
STATUS_SUCCESS on success; STATUS_INSUFFICIENT_RESOURCES if an allocation fails. STATUS_INVALID_PARAMETER if AttrContext describes a non-resident attribute. STATUS_NOT_IMPLEMENTED if requested to decrease the size of an attribute that isn't the last attribute listed in the file record.
Remarks
Called by SetAttributeDataLength() and IncreaseMftSize(). Use SetAttributeDataLength() unless you have a good reason to use this. Doesn't update the file record on disk. Doesn't inform the cache controller of changes with any associated files. Synchronization is the callers responsibility.
NTSTATUS SetResidentAttributeDataLength (PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT AttrContext, ULONG AttrOffset, PFILE_RECORD_HEADER FileRecord, PLARGE_INTEGER DataSize)
 
NtfsMapAttributeRuns

@implemented

Maps a byte range of a non-resident attribute onto the volume, collecting the physically contiguous runs it spans into a run list.

Parameters
VcbVolume the attribute lives on
ContextAttribute to map. Its data run cache is updated to reflect where the mapping stopped.
OffsetByte offset into the attribute to start mapping at
LengthHow many bytes to map
RunListReceives the runs. Must have been initialized by NtfsInitIoRunList().
MappedLengthReceives how many bytes were actually mapped, which is less than Length if the attribute's allocation ends first.
Returns
STATUS_SUCCESS if the range was mapped, STATUS_END_OF_FILE if Offset lies beyond the last allocated cluster, or STATUS_INSUFFICIENT_RESOURCES.
Remarks
Holes are recorded as NTFS_SPARSE_LBO runs rather than rejected here: reads satisfy them by zeroing and writes fail on them, so the policy belongs to the caller.
static NTSTATUS NtfsMapAttributeRuns (PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset, ULONG Length, PNTFS_IO_RUN_LIST RunList, PULONG MappedLength)
 
ReadAttributeToIrp

@implemented

Reads a range of an attribute, serving it out of the given IRP where the shape of the request allows it.

Parameters
VcbVolume the attribute lives on
ContextAttribute to read from
OffsetByte offset into the attribute to start reading at
BufferSystem-space buffer receiving the data
LengthHow much to read, in bytes
IrpThe request being served, or NULL. When supplied and the range is a single aligned contiguous piece of the volume, the IRP goes straight to the storage stack and the disk transfers directly into the caller's pages.
Returns
The number of bytes read, or zero on failure.
Remarks
An IRP may only be passed when Buffer is the start of that IRP's buffer and nothing has been realigned or bounced, since a forwarded transfer lands at the beginning of the IRP's MDL.
ULONG ReadAttributeToIrp (PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset, PCHAR Buffer, ULONG Length, PIRP Irp)
 
ULONG ReadAttribute (PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset, PCHAR Buffer, ULONG Length)
 
WriteAttribute

@implemented

Writes an NTFS attribute to the disk. It presently borrows a lot of code from ReadAttribute(), and it still needs more documentation / cleaning up.

Parameters
VcbVolume Control Block indicating which volume to write the attribute to
ContextPointer to an NTFS_ATTR_CONTEXT that has information about the attribute
OffsetOffset, in bytes, from the beginning of the attribute indicating where to start writing data
BufferThe data that's being written to the device
LengthHow much data will be written, in bytes
RealLengthWrittenPointer to a ULONG which will receive how much data was written, in bytes
FileRecordOptional pointer to a FILE_RECORD_HEADER that contains a copy of the file record being written to. Can be NULL, in which case the file record will be read from disk. If not-null, WriteAttribute() will skip reading from disk, and FileRecord will be updated with the newly-written attribute before the function returns.
Returns
STATUS_SUCCESS if successful, an error code otherwise. STATUS_NOT_IMPLEMENTED if writing to a sparse file.
Remarks
Note that in this context the word "attribute" isn't referring read-only, hidden, etc. - the file's data is actually stored in an attribute in NTFS parlance.
NTSTATUS WriteAttributeFromIrp (PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset, const PUCHAR Buffer, ULONG Length, PULONG RealLengthWritten, PFILE_RECORD_HEADER FileRecord, PIRP Irp)
 
NTSTATUS WriteAttribute (PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset, const PUCHAR Buffer, ULONG Length, PULONG RealLengthWritten, PFILE_RECORD_HEADER FileRecord)
 
NtfsReadUpCaseTable

@implemented

Loads the volume's $UpCase file, which defines how NTFS folds case.

Parameters
VcbVolume to load the table for
Returns
STATUS_SUCCESS if the table was loaded, otherwise the failure that stopped us. A volume without a usable $UpCase is not a valid NTFS volume.
Remarks
Every NTFS volume carries this file at a fixed MFT index. It is one upcased code point per UCS-2 value, so exactly 128 KB.
static NTSTATUS NtfsReadUpCaseTable (PDEVICE_EXTENSION Vcb)
 
NTSTATUS NtfsLoadUpCaseTable (PDEVICE_EXTENSION Vcb)
 
NTSTATUS ReadFileRecord (PDEVICE_EXTENSION Vcb, ULONGLONG index, PFILE_RECORD_HEADER file)
 
NtfsUpdateDuplicatedInformation

@implemented

Pushes the fields NTFS keeps in more than one place out to all of them.

Parameters
VcbVolume the file lives on
FileRecordThe file's record, already holding the new values in $STANDARD_INFORMATION and its $DATA attribute
MFTIndexMFT index of the file
FlagsWhich groups of fields changed; see NTFS_FILENAME_UPDATE_*
CaseSensitiveWhether the parent's index should be searched case-sensitively
Returns
STATUS_SUCCESS once every copy agrees, or the status of the failing update.
Remarks
NTFS stores a file's sizes, timestamps and attributes three times: in $STANDARD_INFORMATION, in the record's own $FILE_NAME, and again in the parent directory's index entry. Windows funnels all three through one routine (NtfsUpdateDuplicateInfo) precisely so they cannot drift; this is that funnel. Callers change $STANDARD_INFORMATION and then say what moved.

The file record is written back here, so callers need not write it again.

The directory flag is the one field that does not come from $STANDARD_INFORMATION – NTFS masks it out of there and keeps it in the record header – so it is restored from the header on the way past.

NTSTATUS NtfsUpdateDuplicatedInformation (PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER FileRecord, ULONGLONG MFTIndex, ULONG Flags, BOOLEAN CaseSensitive)
 
static NTSTATUS UpdateFileNameRecordWorker (PDEVICE_EXTENSION Vcb, ULONGLONG ParentMFTIndex, PUNICODE_STRING FileName, BOOLEAN DirSearch, PNTFS_FILENAME_UPDATE Update, BOOLEAN CaseSensitive)
 
NTSTATUS UpdateFileNameRecord (PDEVICE_EXTENSION Vcb, ULONGLONG ParentMFTIndex, PUNICODE_STRING FileName, BOOLEAN DirSearch, PNTFS_FILENAME_UPDATE Update, BOOLEAN CaseSensitive)
 
NTSTATUS UpdateIndexEntryFileName (PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER MftRecord, PCHAR IndexRecord, ULONG IndexBlockSize, PINDEX_ENTRY_ATTRIBUTE FirstEntry, PINDEX_ENTRY_ATTRIBUTE LastEntry, PUNICODE_STRING FileName, PULONG StartEntry, PULONG CurrentEntry, BOOLEAN DirSearch, PNTFS_FILENAME_UPDATE Update, BOOLEAN CaseSensitive)
 
UpdateFileRecord

@implemented

Writes a file record to the master file table, at a given index.

Parameters
VcbPointer to the DEVICE_EXTENSION of the target drive being written to.
MftIndexTarget index in the master file table to store the file record.
FileRecordPointer to the complete file record which will be written to the master file table.
Returns
STATUS_SUCCESSFUL on success. An error passed from WriteAttribute() otherwise.
NTSTATUS UpdateFileRecord (PDEVICE_EXTENSION Vcb, ULONGLONG MftIndex, PFILE_RECORD_HEADER FileRecord)
 
NTSTATUS FixupUpdateSequenceArray (PDEVICE_EXTENSION Vcb, PNTFS_RECORD_HEADER Record)
 
AddNewMftEntry

@implemented

Adds a file record to the master file table of a given device.

Parameters
FileRecordPointer to a complete file record which will be saved to disk.
DeviceExtPointer to the DEVICE_EXTENSION of the target drive.
DestinationIndexPointer to a ULONGLONG which will receive the MFT index where the file record was stored.
CanWaitBoolean indicating if the function is allowed to wait for exclusive access to the master file table. This will only be relevant if the MFT doesn't have any free file records and needs to be enlarged.
Returns
STATUS_SUCCESS on success. STATUS_OBJECT_NAME_NOT_FOUND if we can't find the MFT's $Bitmap or if we weren't able to read the attribute. STATUS_INSUFFICIENT_RESOURCES if we can't allocate enough memory for a copy of $Bitmap. STATUS_CANT_WAIT if CanWait was FALSE and the function could not get immediate, exclusive access to the MFT.
NTSTATUS AddNewMftEntry (PFILE_RECORD_HEADER FileRecord, PDEVICE_EXTENSION DeviceExt, PULONGLONG DestinationIndex, BOOLEAN CanWait)
 
GetMaxIndexRootSize

@implemented

Calculates how many bytes of index entries the index root can hold, given the space left in the file record.

Parameters
DeviceExtPoints to the target disk's DEVICE_EXTENSION.
FileRecordPointer to the file record containing the index root.
IndexRootOffsetOffset of the index root attribute within FileRecord.
IndexRootRecordPointer to the index root attribute record.
Returns
The maximum size of the index root's entries, leaving room for any attributes that follow the index root.
static ULONG GetMaxIndexRootSize (PDEVICE_EXTENSION DeviceExt, PFILE_RECORD_HEADER FileRecord, ULONG IndexRootOffset, PNTFS_ATTR_RECORD IndexRootRecord)
 
NtfsAddFilenameToDirectory

@implemented

Adds a $FILE_NAME attribute to a given directory index.

Parameters
DeviceExtPoints to the target disk's DEVICE_EXTENSION.
DirectoryMftIndexMft index of the parent directory which will receive the file.
FileReferenceNumberFile reference of the file to be added to the directory. This is a combination of the Mft index and sequence number.
FilenameAttributePointer to the FILENAME_ATTRIBUTE of the file being added to the directory.
CaseSensitiveBoolean indicating if the function should operate in case-sensitive mode. This will be TRUE if an application created the file with the FILE_FLAG_POSIX_SEMANTICS flag.
Returns
STATUS_SUCCESS on success. STATUS_INSUFFICIENT_RESOURCES if an allocation fails. STATUS_NOT_IMPLEMENTED if target address isn't at the end of the given file record.
Remarks
WIP - Can only support a few files in a directory. One FILENAME_ATTRIBUTE is added to the directory's index for each link to that file. So, each file which contains one FILENAME_ATTRIBUTE for a long name and another for the 8.3 name, will get both attributes added to its parent directory.
static NTSTATUS AddFilenameToDirectoryWorker (PDEVICE_EXTENSION DeviceExt, ULONGLONG DirectoryMftIndex, ULONGLONG FileReferenceNumber, PFILENAME_ATTRIBUTE FilenameAttribute, BOOLEAN CaseSensitive)
 
NTSTATUS NtfsAddFilenameToDirectory (PDEVICE_EXTENSION DeviceExt, ULONGLONG DirectoryMftIndex, ULONGLONG FileReferenceNumber, PFILENAME_ATTRIBUTE FilenameAttribute, BOOLEAN CaseSensitive)
 
NtfsRemoveFilenameFromDirectory

@implemented

Removes a $FILE_NAME attribute from a given directory index.

Parameters
DeviceExtPoints to the target disk's DEVICE_EXTENSION.
DirectoryMftIndexMft index of the parent directory the file is being removed from.
FilenameAttributePointer to the FILENAME_ATTRIBUTE of the file being removed from the directory.
CaseSensitiveBoolean indicating if the function should operate in case-sensitive mode. This will be TRUE if an application created the file with the FILE_FLAG_POSIX_SEMANTICS flag.
Returns
STATUS_SUCCESS on success. STATUS_OBJECT_NAME_NOT_FOUND if the name isn't in the directory's index. STATUS_INSUFFICIENT_RESOURCES if an allocation fails.
Remarks
The inverse of NtfsAddFilenameToDirectory(). One $FILE_NAME attribute is indexed per link, so a file carrying both a long name and an 8.3 name needs one call for each.
static NTSTATUS RemoveFilenameFromDirectoryWorker (PDEVICE_EXTENSION DeviceExt, ULONGLONG DirectoryMftIndex, PFILENAME_ATTRIBUTE FilenameAttribute, BOOLEAN CaseSensitive)
 
NTSTATUS NtfsRemoveFilenameFromDirectory (PDEVICE_EXTENSION DeviceExt, ULONGLONG DirectoryMftIndex, PFILENAME_ATTRIBUTE FilenameAttribute, BOOLEAN CaseSensitive)
 
FreeMftEntry

@implemented

Releases an MFT record back to the volume, clearing its bit in the master file table's $Bitmap attribute and marking the record itself as no longer in use.

Parameters
DeviceExtPoints to the target disk's DEVICE_EXTENSION.
MftIndexMft index of the record to release.
Returns
STATUS_SUCCESS on success. STATUS_OBJECT_NAME_NOT_FOUND if the master file table has no $Bitmap attribute. STATUS_INSUFFICIENT_RESOURCES if an allocation fails.
Remarks
The record's sequence number is incremented before it's written back, so any file reference still pointing at the old incarnation can be recognised as stale.
NTSTATUS FreeMftEntry (PDEVICE_EXTENSION DeviceExt, ULONGLONG MftIndex)
 
NtfsDeleteFileRecord

@implemented

Deletes a file or empty directory: releases every cluster its non-resident attributes hold, removes each of its names from the parent index, and frees its MFT record.

Parameters
DeviceExtPoints to the target disk's DEVICE_EXTENSION.
MftIndexMft index of the file to delete.
CaseSensitiveBoolean indicating if the function should operate in case-sensitive mode. This will be TRUE if an application created the file with the FILE_FLAG_POSIX_SEMANTICS flag.
Returns
STATUS_SUCCESS on success.
Remarks
Callers must have established that the file may be deleted, in particular that a directory is empty. Clusters are released before the names, so a failure part-way through leaves a reachable file rather than orphaned allocation.
NTSTATUS NtfsDeleteFileRecord (PDEVICE_EXTENSION DeviceExt, ULONGLONG MftIndex, BOOLEAN CaseSensitive)
 
NTSTATUS AddFixupArray (PDEVICE_EXTENSION Vcb, PNTFS_RECORD_HEADER Record)
 
NTSTATUS ReadLCN (PDEVICE_EXTENSION Vcb, ULONGLONG lcn, ULONG count, PVOID buffer)
 
BOOLEAN CompareFileName (PDEVICE_EXTENSION Vcb, PUNICODE_STRING FileName, PINDEX_ENTRY_ATTRIBUTE IndexEntry, BOOLEAN DirSearch, BOOLEAN CaseSensitive)
 
UpdateMftMirror

@implemented

Backs-up the first ~4 master file table entries to the $MFTMirr file.

Parameters
VcbPointer to an NTFS_VCB for the volume whose Mft mirror is being updated.
Returns

STATUS_SUCCESS on success. STATUS_INSUFFICIENT_RESOURCES if an allocation failed. STATUS_UNSUCCESSFUL if we couldn't read the master file table.

Remarks
NTFS maintains up-to-date copies of the first several mft entries in the $MFTMirr file. Usually, the first 4 file records from the mft are stored. The exact number of entries is determined by the size of $MFTMirr's $DATA. If $MFTMirr is not up-to-date, chkdsk will reject every change it can find prior to when $MFTMirr was last updated. Therefore, it's recommended to call this function if the volume changes considerably. For instance, IncreaseMftSize() relies on this function to keep chkdsk from deleting the mft entries it creates. Note that under most instances, creating or deleting a file will not affect the first ~four mft entries, and so will not require updating the mft mirror.
NTSTATUS UpdateMftMirror (PNTFS_VCB Vcb)
 
NTSTATUS BrowseSubNodeIndexEntries (PNTFS_VCB Vcb, PFILE_RECORD_HEADER MftRecord, ULONG IndexBlockSize, PUNICODE_STRING FileName, PNTFS_ATTR_CONTEXT IndexAllocationContext, PRTL_BITMAP Bitmap, ULONGLONG VCN, PULONG StartEntry, PULONG CurrentEntry, BOOLEAN DirSearch, BOOLEAN CaseSensitive, ULONGLONG *OutMFTIndex)
 
NTSTATUS BrowseIndexEntries (PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER MftRecord, PINDEX_ROOT_ATTRIBUTE IndexRecord, ULONG IndexBlockSize, PINDEX_ENTRY_ATTRIBUTE FirstEntry, PINDEX_ENTRY_ATTRIBUTE LastEntry, PUNICODE_STRING FileName, PULONG StartEntry, PULONG CurrentEntry, BOOLEAN DirSearch, BOOLEAN CaseSensitive, ULONGLONG *OutMFTIndex)
 
NTSTATUS NtfsFindMftRecord (PDEVICE_EXTENSION Vcb, ULONGLONG MFTIndex, PUNICODE_STRING FileName, PULONG FirstEntry, BOOLEAN DirSearch, BOOLEAN CaseSensitive, ULONGLONG *OutMFTIndex)
 
NTSTATUS NtfsLookupFileAt (PDEVICE_EXTENSION Vcb, PUNICODE_STRING PathName, BOOLEAN CaseSensitive, PFILE_RECORD_HEADER *FileRecord, PULONGLONG MFTIndex, ULONGLONG CurrentMFTIndex)
 
NTSTATUS NtfsLookupFile (PDEVICE_EXTENSION Vcb, PUNICODE_STRING PathName, BOOLEAN CaseSensitive, PFILE_RECORD_HEADER *FileRecord, PULONGLONG MFTIndex)
 
void NtfsDumpData (ULONG_PTR Buffer, ULONG Length)
 
NtfsIndexNodeIsEmpty

Reads the index node at Vcn and reports whether its subtree holds any entry.

static NTSTATUS NtfsIndexNodeIsEmpty (_In_ PDEVICE_EXTENSION Vcb, _In_ PNTFS_ATTR_CONTEXT IndexAllocationContext, _In_ ULONG IndexBlockSize, _In_ ULONGLONG Vcn, _In_ ULONG Depth, _Out_ PBOOLEAN Empty)
 
NtfsIsDirectoryEmpty

@implemented

Decides whether a directory may be deleted. Unlike a name lookup, a sub-node that can't be read is an error here rather than a miss, since it may hold children.

Parameters
VcbVolume holding the directory.
MftIndexMFT record of the directory.
EmptyReceives TRUE if the index holds no entries.
Returns
STATUS_SUCCESS if Empty was set, or an error if the index couldn't be fully read.
NTSTATUS NtfsIsDirectoryEmpty (_In_ PDEVICE_EXTENSION Vcb, _In_ ULONGLONG MftIndex, _Out_ PBOOLEAN Empty)
 

NtfsDumpFileRecord

@implemented

Provides diagnostic information about a file record. Prints a hex dump of the entire record (based on the size reported by FileRecord->ByesInUse), then prints a dump of each attribute.

Parameters
VcbPointer to a DEVICE_EXTENSION describing the volume.
FileRecordPointer to the file record to be analyzed.
Remarks
FileRecord must be a complete file record at least FileRecord->BytesAllocated in size, and not just the header.
#define NTFS_MAX_INDEX_DEPTH   32
 
VOID NtfsDumpFileRecord (PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER FileRecord)
 
NTSTATUS NtfsFindFileAt (PDEVICE_EXTENSION Vcb, PUNICODE_STRING SearchPattern, PULONG FirstEntry, PFILE_RECORD_HEADER *FileRecord, PULONGLONG MFTIndex, ULONGLONG CurrentMFTIndex, BOOLEAN CaseSensitive)
 
static NTSTATUS NtfsIndexEntriesAreEmpty (_In_ PDEVICE_EXTENSION Vcb, _In_ PNTFS_ATTR_CONTEXT IndexAllocationContext, _In_ ULONG IndexBlockSize, _In_ PINDEX_HEADER_ATTRIBUTE Header, _In_ ULONG Depth, _Out_ PBOOLEAN Empty)
 

Macro Definition Documentation

◆ NDEBUG

#define NDEBUG

Definition at line 35 of file mft.c.

◆ NTFS_MAX_INDEX_DEPTH

#define NTFS_MAX_INDEX_DEPTH   32

Definition at line 4280 of file mft.c.

Function Documentation

◆ AddFilenameToDirectoryWorker()

static NTSTATUS AddFilenameToDirectoryWorker ( PDEVICE_EXTENSION  DeviceExt,
ULONGLONG  DirectoryMftIndex,
ULONGLONG  FileReferenceNumber,
PFILENAME_ATTRIBUTE  FilenameAttribute,
BOOLEAN  CaseSensitive 
)
static

Definition at line 2573 of file mft.c.

2578{
2580 PFILE_RECORD_HEADER ParentFileRecord;
2581 PNTFS_ATTR_CONTEXT IndexRootContext;
2582 PINDEX_ROOT_ATTRIBUTE I30IndexRoot;
2583 ULONG IndexRootOffset;
2584 ULONGLONG I30IndexRootLength;
2585 ULONG LengthWritten;
2586 PINDEX_ROOT_ATTRIBUTE NewIndexRoot;
2587 ULONG AttributeLength;
2588 PB_TREE NewTree;
2589 ULONG BtreeIndexLength;
2590 ULONG MaxIndexRootSize;
2591 PB_TREE_KEY NewLeftKey;
2592 PB_TREE_FILENAME_NODE NewRightHandNode;
2593 LARGE_INTEGER MinIndexRootSize;
2594 ULONG NewMaxIndexRootSize;
2595 ULONG NodeSize;
2596
2597 // Allocate memory for the parent directory
2598 ParentFileRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList);
2599 if (!ParentFileRecord)
2600 {
2601 DPRINT1("ERROR: Couldn't allocate memory for file record!\n");
2603 }
2604
2605 // Open the parent directory
2606 Status = ReadFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
2607 if (!NT_SUCCESS(Status))
2608 {
2609 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2610 DPRINT1("ERROR: Couldn't read parent directory with index %I64u\n",
2611 DirectoryMftIndex);
2612 return Status;
2613 }
2614
2615#ifndef NDEBUG
2616 DPRINT1("Dumping old parent file record:\n");
2617 NtfsDumpFileRecord(DeviceExt, ParentFileRecord);
2618#endif
2619
2620 // Find the index root attribute for the directory
2621 Status = FindAttribute(DeviceExt,
2622 ParentFileRecord,
2624 L"$I30",
2625 4,
2626 &IndexRootContext,
2627 &IndexRootOffset);
2628 if (!NT_SUCCESS(Status))
2629 {
2630 DPRINT1("ERROR: Couldn't find $I30 $INDEX_ROOT attribute for parent directory with MFT #: %I64u!\n",
2631 DirectoryMftIndex);
2632 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2633 return Status;
2634 }
2635
2636 // Find the maximum index size given what the file record can hold
2637 MaxIndexRootSize = GetMaxIndexRootSize(DeviceExt, ParentFileRecord, IndexRootOffset, IndexRootContext->pRecord);
2638
2639 // Allocate memory for the index root data
2640 I30IndexRootLength = AttributeDataLength(IndexRootContext->pRecord);
2641 I30IndexRoot = ExAllocatePoolWithTag(NonPagedPool, I30IndexRootLength, TAG_NTFS);
2642 if (!I30IndexRoot)
2643 {
2644 DPRINT1("ERROR: Couldn't allocate memory for index root attribute!\n");
2645 ReleaseAttributeContext(IndexRootContext);
2646 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2648 }
2649
2650 // Read the Index Root
2651 Status = ReadAttribute(DeviceExt, IndexRootContext, 0, (PCHAR)I30IndexRoot, I30IndexRootLength);
2652 if (!NT_SUCCESS(Status))
2653 {
2654 DPRINT1("ERROR: Couln't read index root attribute for Mft index #%I64u\n", DirectoryMftIndex);
2655 ReleaseAttributeContext(IndexRootContext);
2656 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2657 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2658 return Status;
2659 }
2660
2661 // Convert the index to a B*Tree
2662 Status = CreateBTreeFromIndex(DeviceExt,
2663 ParentFileRecord,
2664 IndexRootContext,
2665 I30IndexRoot,
2666 &NewTree);
2667 if (!NT_SUCCESS(Status))
2668 {
2669 DPRINT1("ERROR: Failed to create B-Tree from Index!\n");
2670 ReleaseAttributeContext(IndexRootContext);
2671 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2672 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2673 return Status;
2674 }
2675
2676#ifndef NDEBUG
2677 DumpBTree(NewTree);
2678#endif
2679
2680 // Insert the key for the file we're adding
2681 Status = NtfsInsertKey(NewTree,
2682 FileReferenceNumber,
2683 FilenameAttribute,
2684 NewTree->RootNode,
2685 CaseSensitive,
2686 MaxIndexRootSize,
2687 I30IndexRoot->SizeOfEntry,
2688 &NewLeftKey,
2689 &NewRightHandNode);
2690 if (!NT_SUCCESS(Status))
2691 {
2692 DPRINT1("ERROR: Failed to insert '%.*S' into the index of MFT record %I64u (Status %lx)\n",
2693 FilenameAttribute->NameLength,
2694 FilenameAttribute->Name,
2695 DirectoryMftIndex,
2696 Status);
2697 DestroyBTree(NewTree);
2698 ReleaseAttributeContext(IndexRootContext);
2699 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2700 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2701 return Status;
2702 }
2703
2704#ifndef NDEBUG
2705 DumpBTree(NewTree);
2706#endif
2707
2708 // The root node can't be split
2709 ASSERT(NewLeftKey == NULL);
2710 ASSERT(NewRightHandNode == NULL);
2711
2712 // Convert B*Tree back to Index
2713
2714 // Updating the index allocation can change the size available for the index root,
2715 // And if the index root is demoted, the index allocation will need to be updated again,
2716 // which may change the size available for index root... etc.
2717 // My solution is to decrease index root to the size it would be if it was demoted,
2718 // then UpdateIndexAllocation will have an accurate representation of the maximum space
2719 // it can use in the file record. There's still a chance that the act of allocating an
2720 // index node after demoting the index root will increase the size of the file record beyond
2721 // it's limit, but if that happens, an attribute-list will most definitely be needed.
2722 // This a bit hacky, but it seems to be functional.
2723
2724 // Calculate the minimum size of the index root attribute, considering one dummy key and one VCN
2725 MinIndexRootSize.QuadPart = sizeof(INDEX_ROOT_ATTRIBUTE) // size of the index root headers
2726 + 0x18; // Size of dummy key with a VCN for a subnode
2727 ASSERT(MinIndexRootSize.QuadPart % ATTR_RECORD_ALIGNMENT == 0);
2728
2729 // Temporarily shrink the index root to it's minimal size
2730 AttributeLength = MinIndexRootSize.LowPart;
2731 AttributeLength += sizeof(INDEX_ROOT_ATTRIBUTE);
2732
2733
2734 // FIXME: IndexRoot will probably be invalid until we're finished. If we fail before we finish, the directory will probably be toast.
2735 // The potential for catastrophic data-loss exists!!! :)
2736
2737 // Update the length of the attribute in the file record of the parent directory
2739 IndexRootContext,
2740 ParentFileRecord,
2741 IndexRootOffset,
2742 AttributeLength);
2743 if (!NT_SUCCESS(Status))
2744 {
2745 DPRINT1("ERROR: Unable to set length of index root!\n");
2746 DestroyBTree(NewTree);
2747 ReleaseAttributeContext(IndexRootContext);
2748 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2749 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2750 return Status;
2751 }
2752
2753 // Update the index allocation
2754 Status = UpdateIndexAllocation(DeviceExt, NewTree, I30IndexRoot->SizeOfEntry, ParentFileRecord);
2755 if (!NT_SUCCESS(Status))
2756 {
2757 DPRINT1("ERROR: Failed to update index allocation from B-Tree!\n");
2758 DestroyBTree(NewTree);
2759 ReleaseAttributeContext(IndexRootContext);
2760 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2761 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2762 return Status;
2763 }
2764
2765#ifndef NDEBUG
2766 DPRINT1("Index Allocation updated\n");
2767 DumpBTree(NewTree);
2768#endif
2769
2770 // Find the maximum index root size given what the file record can hold
2771 NewMaxIndexRootSize = GetMaxIndexRootSize(DeviceExt, ParentFileRecord, IndexRootOffset, IndexRootContext->pRecord);
2772
2773 // The index allocation and index bitmap may have grown, leaving less room for the index root,
2774 // so now we need to double-check that index root isn't too large
2775 NodeSize = GetSizeOfIndexEntries(NewTree->RootNode);
2776 if (NodeSize > NewMaxIndexRootSize)
2777 {
2778 DPRINT("Demoting index root.\nNodeSize: 0x%lx\nNewMaxIndexRootSize: 0x%lx\n", NodeSize, NewMaxIndexRootSize);
2779
2780 Status = DemoteBTreeRoot(NewTree);
2781 if (!NT_SUCCESS(Status))
2782 {
2783 DPRINT1("ERROR: Failed to demote index root!\n");
2784 DestroyBTree(NewTree);
2785 ReleaseAttributeContext(IndexRootContext);
2786 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2787 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2788 return Status;
2789 }
2790
2791 // We need to update the index allocation once more
2792 Status = UpdateIndexAllocation(DeviceExt, NewTree, I30IndexRoot->SizeOfEntry, ParentFileRecord);
2793 if (!NT_SUCCESS(Status))
2794 {
2795 DPRINT1("ERROR: Failed to update index allocation from B-Tree!\n");
2796 DestroyBTree(NewTree);
2797 ReleaseAttributeContext(IndexRootContext);
2798 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2799 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2800 return Status;
2801 }
2802
2803 // re-recalculate max size of index root
2804 NewMaxIndexRootSize = GetMaxIndexRootSize(DeviceExt, ParentFileRecord, IndexRootOffset, IndexRootContext->pRecord);
2805 }
2806
2807 // Create the Index Root from the B*Tree
2808 Status = CreateIndexRootFromBTree(DeviceExt, NewTree, NewMaxIndexRootSize, &NewIndexRoot, &BtreeIndexLength);
2809 if (!NT_SUCCESS(Status))
2810 {
2811 DPRINT1("ERROR: Failed to create Index root from B-Tree!\n");
2812 DestroyBTree(NewTree);
2813 ReleaseAttributeContext(IndexRootContext);
2814 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2815 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2816 return Status;
2817 }
2818
2819 // We're done with the B-Tree now
2820 DestroyBTree(NewTree);
2821
2822 // Write back the new index root attribute to the parent directory file record
2823
2824 // First, we need to resize the attribute.
2825 // CreateIndexRootFromBTree() should have verified that the index root fits within MaxIndexSize.
2826 // We can't set the size as we normally would, because $INDEX_ROOT must always be resident.
2827 AttributeLength = NewIndexRoot->Header.AllocatedSize + FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header);
2828
2829 if (AttributeLength != IndexRootContext->pRecord->Resident.ValueLength)
2830 {
2831 // Update the length of the attribute in the file record of the parent directory
2833 IndexRootContext,
2834 ParentFileRecord,
2835 IndexRootOffset,
2836 AttributeLength);
2837 if (!NT_SUCCESS(Status))
2838 {
2839 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
2840 ReleaseAttributeContext(IndexRootContext);
2841 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2842 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2843 DPRINT1("ERROR: Unable to set resident attribute length!\n");
2844 return Status;
2845 }
2846
2847 }
2848
2849 NT_ASSERT(ParentFileRecord->BytesInUse <= DeviceExt->NtfsInfo.BytesPerFileRecord);
2850
2851 Status = UpdateFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
2852 if (!NT_SUCCESS(Status))
2853 {
2854 DPRINT1("ERROR: Failed to update file record of directory with index: %llx\n", DirectoryMftIndex);
2855 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2856 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
2857 ReleaseAttributeContext(IndexRootContext);
2858 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2859 return Status;
2860 }
2861
2862 // Write the new index root to disk
2863 Status = WriteAttribute(DeviceExt,
2864 IndexRootContext,
2865 0,
2866 (PUCHAR)NewIndexRoot,
2867 AttributeLength,
2868 &LengthWritten,
2869 ParentFileRecord);
2870 if (!NT_SUCCESS(Status) || LengthWritten != AttributeLength)
2871 {
2872 DPRINT1("ERROR: Unable to write new index root attribute to parent directory!\n");
2873 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
2874 ReleaseAttributeContext(IndexRootContext);
2875 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2876 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2877 return Status;
2878 }
2879
2880 // re-read the parent file record, so we can dump it
2881 Status = ReadFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
2882 if (!NT_SUCCESS(Status))
2883 {
2884 DPRINT1("ERROR: Couldn't read parent directory after messing with it!\n");
2885 }
2886 else
2887 {
2888#ifndef NDEBUG
2889 DPRINT1("Dumping new B-Tree:\n");
2890
2891 Status = CreateBTreeFromIndex(DeviceExt, ParentFileRecord, IndexRootContext, NewIndexRoot, &NewTree);
2892 if (!NT_SUCCESS(Status))
2893 {
2894 DPRINT1("ERROR: Couldn't re-create b-tree\n");
2895 return Status;
2896 }
2897
2898 DumpBTree(NewTree);
2899
2900 DestroyBTree(NewTree);
2901
2902 NtfsDumpFileRecord(DeviceExt, ParentFileRecord);
2903#endif
2904 }
2905
2906 // Cleanup
2907 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
2908 ReleaseAttributeContext(IndexRootContext);
2909 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2910 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2911
2912 return Status;
2913}
LONG NTSTATUS
Definition: precomp.h:26
#define DPRINT1
Definition: precomp.h:8
NTSTATUS CreateBTreeFromIndex(PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER FileRecordWithIndex, PNTFS_ATTR_CONTEXT IndexRootContext, PINDEX_ROOT_ATTRIBUTE IndexRoot, PB_TREE *NewTree)
Definition: btree.c:687
NTSTATUS NtfsInsertKey(PB_TREE Tree, ULONGLONG FileReference, PFILENAME_ATTRIBUTE FileNameAttribute, PB_TREE_FILENAME_NODE Node, BOOLEAN CaseSensitive, ULONG MaxIndexRootSize, ULONG IndexRecordSize, PB_TREE_KEY *MedianKey, PB_TREE_FILENAME_NODE *NewRightHandSibling)
Definition: btree.c:1696
NTSTATUS UpdateIndexAllocation(PDEVICE_EXTENSION DeviceExt, PB_TREE Tree, ULONG IndexBufferSize, PFILE_RECORD_HEADER FileRecord)
Definition: btree.c:1187
VOID DestroyBTree(PB_TREE Tree)
Definition: btree.c:1547
NTSTATUS DemoteBTreeRoot(PB_TREE Tree)
Definition: btree.c:1094
ULONG GetSizeOfIndexEntries(PB_TREE_FILENAME_NODE Node)
Definition: btree.c:860
VOID DumpBTree(PB_TREE Tree)
Definition: btree.c:1627
NTSTATUS CreateIndexRootFromBTree(PDEVICE_EXTENSION DeviceExt, PB_TREE Tree, ULONG MaxIndexSize, PINDEX_ROOT_ATTRIBUTE *IndexRoot, ULONG *Length)
Definition: btree.c:915
Definition: Header.h:9
#define NULL
Definition: types.h:112
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:33
#define L(x)
Definition: resources.c:13
@ AttributeIndexRoot
Definition: ntfs.h:196
#define TAG_NTFS
Definition: ntfs.h:12
#define ATTR_RECORD_ALIGNMENT
Definition: ntfs.h:351
#define ExAllocatePoolWithTag(hernya, size, tag)
Definition: env_spec_w32.h:350
#define NonPagedPool
Definition: env_spec_w32.h:307
Status
Definition: gdiplustypes.h:24
NTSTATUS ReadFileRecord(PDEVICE_EXTENSION Vcb, ULONGLONG index, PFILE_RECORD_HEADER file)
Definition: mft.c:1805
NTSTATUS UpdateFileRecord(PDEVICE_EXTENSION Vcb, ULONGLONG MftIndex, PFILE_RECORD_HEADER FileRecord)
Definition: mft.c:2260
static ULONG GetMaxIndexRootSize(PDEVICE_EXTENSION DeviceExt, PFILE_RECORD_HEADER FileRecord, ULONG IndexRootOffset, PNTFS_ATTR_RECORD IndexRootRecord)
Definition: mft.c:2511
VOID ReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context)
Definition: mft.c:104
ULONGLONG AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord)
Definition: mft.c:259
NTSTATUS FindAttribute(PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER MftRecord, ULONG Type, PCWSTR Name, ULONG NameLength, PNTFS_ATTR_CONTEXT *AttrCtx, PULONG Offset)
Definition: mft.c:131
ULONG ReadAttribute(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset, PCHAR Buffer, ULONG Length)
Definition: mft.c:1494
NTSTATUS WriteAttribute(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset, const PUCHAR Buffer, ULONG Length, PULONG RealLengthWritten, PFILE_RECORD_HEADER FileRecord)
Definition: mft.c:1698
VOID NtfsDumpFileRecord(PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER FileRecord)
Definition: mft.c:4211
NTSTATUS InternalSetResidentAttributeLength(PDEVICE_EXTENSION DeviceExt, PNTFS_ATTR_CONTEXT AttrContext, PFILE_RECORD_HEADER FileRecord, ULONG AttrOffset, ULONG DataSize)
Definition: mft.c:542
#define ASSERT(a)
Definition: mode.c:44
#define ExFreePoolWithTag(_P, _T)
Definition: module.h:1109
#define STATUS_SUCCESS
Definition: shellext.h:65
#define DPRINT
Definition: sndvol32.h:73
Definition: ntfs.h:485
PB_TREE_FILENAME_NODE RootNode
Definition: ntfs.h:486
UCHAR NameLength
Definition: ntfs.h:408
WCHAR Name[1]
Definition: ntfs.h:410
INDEX_HEADER_ATTRIBUTE Header
Definition: ntfs.h:429
ULONG SizeOfEntry
Definition: ntfs.h:426
ULONG BytesInUse
Definition: ntfs.h:288
#define FIELD_OFFSET(t, f)
Definition: typedefs.h:255
uint64_t ULONGLONG
Definition: typedefs.h:67
unsigned char * PUCHAR
Definition: typedefs.h:53
uint32_t ULONG
Definition: typedefs.h:59
char * PCHAR
Definition: typedefs.h:51
#define STATUS_INSUFFICIENT_RESOURCES
Definition: udferr_usr.h:158
LONGLONG QuadPart
Definition: typedefs.h:114
ULONG LowPart
Definition: typedefs.h:106
#define NT_ASSERT
Definition: rtlfuncs.h:3334

Referenced by NtfsAddFilenameToDirectory().

◆ AddFixupArray()

NTSTATUS AddFixupArray ( PDEVICE_EXTENSION  Vcb,
PNTFS_RECORD_HEADER  Record 
)

Definition at line 3444 of file mft.c.

3446{
3447 USHORT *pShortToFixUp;
3448 ULONG ArrayEntryCount = Record->UsaCount - 1;
3449 ULONG Offset = Vcb->NtfsInfo.BytesPerSector - 2;
3450 ULONG i;
3451
3452 PFIXUP_ARRAY fixupArray = (PFIXUP_ARRAY)((UCHAR*)Record + Record->UsaOffset);
3453
3454 DPRINT("AddFixupArray(%p, %p)\n fixupArray->USN: %u, ArrayEntryCount: %u\n", Vcb, Record, fixupArray->USN, ArrayEntryCount);
3455
3456 fixupArray->USN++;
3457
3458 for (i = 0; i < ArrayEntryCount; i++)
3459 {
3460 DPRINT("USN: %u\tOffset: %u\n", fixupArray->USN, Offset);
3461
3462 pShortToFixUp = (USHORT*)((PCHAR)Record + Offset);
3463 fixupArray->Array[i] = *pShortToFixUp;
3464 *pShortToFixUp = fixupArray->USN;
3465 Offset += Vcb->NtfsInfo.BytesPerSector;
3466 }
3467
3468 return STATUS_SUCCESS;
3469}
struct FIXUP_ARRAY * PFIXUP_ARRAY
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
_In_ ULONG _In_ ULONG Offset
Definition: ntddpcm.h:101
unsigned short USHORT
Definition: pedump.c:61
#define Vcb
Definition: cdprocs.h:1415
USHORT Array[]
Definition: ntfs.h:640
USHORT USN
Definition: ntfs.h:639
unsigned char UCHAR
Definition: typedefs.h:53
_In_ struct _KBUGCHECK_REASON_CALLBACK_RECORD * Record
Definition: ketypes.h:320

Referenced by CreateIndexBufferFromBTreeNode(), UpdateFileRecord(), and UpdateIndexEntryFileName().

◆ AddNewMftEntry()

NTSTATUS AddNewMftEntry ( PFILE_RECORD_HEADER  FileRecord,
PDEVICE_EXTENSION  DeviceExt,
PULONGLONG  DestinationIndex,
BOOLEAN  CanWait 
)

Definition at line 2351 of file mft.c.

2355{
2357 ULONGLONG MftIndex;
2359 ULONGLONG BitmapDataSize;
2360 ULONGLONG AttrBytesRead;
2363 ULONG LengthWritten;
2364 PNTFS_ATTR_CONTEXT BitmapContext;
2365 LARGE_INTEGER BitmapBits;
2366 UCHAR SystemReservedBits;
2367
2368 DPRINT("AddNewMftEntry(%p, %p, %p, %s)\n", FileRecord, DeviceExt, DestinationIndex, CanWait ? "TRUE" : "FALSE");
2369
2370 // First, we have to read the mft's $Bitmap attribute
2371
2372 // Find the attribute
2373 Status = FindAttribute(DeviceExt, DeviceExt->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, NULL);
2374 if (!NT_SUCCESS(Status))
2375 {
2376 DPRINT1("ERROR: Couldn't find $Bitmap attribute of master file table!\n");
2377 return Status;
2378 }
2379
2380 // Get size of bitmap
2381 BitmapDataSize = AttributeDataLength(BitmapContext->pRecord);
2382
2383 // RtlInitializeBitmap wants a ULONG-aligned pointer, and wants the memory passed to it to be a ULONG-multiple
2384 // Allocate a buffer for the $Bitmap attribute plus enough to ensure we can get a ULONG-aligned pointer
2385 BitmapBuffer = ExAllocatePoolWithTag(NonPagedPool, BitmapDataSize + sizeof(ULONG), TAG_NTFS);
2386 if (!BitmapBuffer)
2387 {
2388 ReleaseAttributeContext(BitmapContext);
2390 }
2391 RtlZeroMemory(BitmapBuffer, BitmapDataSize + sizeof(ULONG));
2392
2393 // Get a ULONG-aligned pointer for the bitmap itself
2395
2396 // read $Bitmap attribute
2397 AttrBytesRead = ReadAttribute(DeviceExt, BitmapContext, 0, (PCHAR)BitmapData, BitmapDataSize);
2398
2399 if (AttrBytesRead != BitmapDataSize)
2400 {
2401 DPRINT1("ERROR: Unable to read $Bitmap attribute of master file table!\n");
2403 ReleaseAttributeContext(BitmapContext);
2405 }
2406
2407 // We need to backup the bits for records 0x10 - 0x17 (3rd byte of bitmap) and mark these records
2408 // as in-use so we don't assign files to those indices. They're reserved for the system (e.g. ChkDsk).
2409 SystemReservedBits = BitmapData[2];
2410 BitmapData[2] = 0xff;
2411
2412 // Calculate bit count
2413 BitmapBits.QuadPart = AttributeDataLength(DeviceExt->MFTContext->pRecord) /
2414 DeviceExt->NtfsInfo.BytesPerFileRecord;
2415 if (BitmapBits.HighPart != 0)
2416 {
2417 DPRINT1("\tFIXME: bitmap sizes beyond 32bits are not yet supported! (Your NTFS volume is too large)\n");
2420 ReleaseAttributeContext(BitmapContext);
2422 }
2423
2424 // convert buffer into bitmap
2426
2427 // set next available bit, preferrably after 23rd bit
2428 MftIndex = RtlFindClearBitsAndSet(&Bitmap, 1, 24);
2429 if ((LONG)MftIndex == -1)
2430 {
2431 DPRINT1("Couldn't find free space in MFT for file record, increasing MFT size.\n");
2432
2434 ReleaseAttributeContext(BitmapContext);
2435
2436 // Couldn't find a free record in the MFT, add some blank records and try again
2437 Status = IncreaseMftSize(DeviceExt, CanWait);
2438 if (!NT_SUCCESS(Status))
2439 {
2440 DPRINT1("ERROR: Couldn't find space in MFT for file or increase MFT size!\n");
2441 return Status;
2442 }
2443
2444 return AddNewMftEntry(FileRecord, DeviceExt, DestinationIndex, CanWait);
2445 }
2446
2447 DPRINT("Creating file record at MFT index: %I64u\n", MftIndex);
2448
2449 // update file record with index
2450 FileRecord->MFTRecordNumber = MftIndex;
2451
2452 // [BitmapData should have been updated via RtlFindClearBitsAndSet()]
2453
2454 // Restore the system reserved bits
2455 BitmapData[2] = SystemReservedBits;
2456
2457 // write the bitmap back to the MFT's $Bitmap attribute
2458 Status = WriteAttribute(DeviceExt, BitmapContext, 0, BitmapData, BitmapDataSize, &LengthWritten, FileRecord);
2459 if (!NT_SUCCESS(Status))
2460 {
2461 DPRINT1("ERROR encountered when writing $Bitmap attribute!\n");
2463 ReleaseAttributeContext(BitmapContext);
2464 return Status;
2465 }
2466
2467 // update the file record (write it to disk)
2468 Status = UpdateFileRecord(DeviceExt, MftIndex, FileRecord);
2469
2470 if (!NT_SUCCESS(Status))
2471 {
2472 DPRINT1("ERROR: Unable to write file record!\n");
2474 ReleaseAttributeContext(BitmapContext);
2475 return Status;
2476 }
2477
2478 *DestinationIndex = MftIndex;
2479
2481 ReleaseAttributeContext(BitmapContext);
2482
2483 return Status;
2484}
#define ALIGN_UP_BY(size, align)
#define STATUS_NOT_IMPLEMENTED
Definition: d3dkmdt.h:42
#define RtlInitializeBitMap
Definition: dbgbitmap.h:326
#define RtlFindClearBitsAndSet
Definition: dbgbitmap.h:333
#define FALSE
Definition: types.h:117
PNTFS_GLOBAL_DATA NtfsGlobalData
Definition: ntfs.c:36
@ AttributeBitmap
Definition: ntfs.h:198
static ULONG BitmapBuffer[(XMS_BLOCKS+31)/32]
Definition: himem.c:86
NTSTATUS IncreaseMftSize(PDEVICE_EXTENSION Vcb, BOOLEAN CanWait)
Definition: mft.c:293
NTSTATUS AddNewMftEntry(PFILE_RECORD_HEADER FileRecord, PDEVICE_EXTENSION DeviceExt, PULONGLONG DestinationIndex, BOOLEAN CanWait)
Definition: mft.c:2351
long LONG
Definition: pedump.c:60
BOOLEAN EnableWriteSupport
Definition: ntfs.h:182
ULONG MFTRecordNumber
Definition: ntfs.h:293
uint32_t * PULONG
Definition: typedefs.h:59
#define RtlZeroMemory(Destination, Length)
Definition: typedefs.h:262
uint32_t ULONG_PTR
Definition: typedefs.h:65
#define STATUS_OBJECT_NAME_NOT_FOUND
Definition: udferr_usr.h:149

Referenced by AddNewMftEntry(), NtfsCreateDirectory(), and NtfsCreateFileRecord().

◆ AttributeAllocatedLength()

ULONGLONG AttributeAllocatedLength ( PNTFS_ATTR_RECORD  AttrRecord)

Definition at line 249 of file mft.c.

250{
251 if (AttrRecord->IsNonResident)
252 return AttrRecord->NonResident.AllocatedSize;
253 else
254 return ALIGN_UP_BY(AttrRecord->Resident.ValueLength, ATTR_RECORD_ALIGNMENT);
255}
struct NTFS_ATTR_RECORD::@169::@171 Resident
UCHAR IsNonResident
Definition: ntfs.h:136
struct NTFS_ATTR_RECORD::@169::@172 NonResident

Referenced by NtfsGetFileSize(), NtfsGetStreamInformation(), and NtfsReadFile().

◆ AttributeDataLength()

◆ BrowseIndexEntries()

NTSTATUS BrowseIndexEntries ( PDEVICE_EXTENSION  Vcb,
PFILE_RECORD_HEADER  MftRecord,
PINDEX_ROOT_ATTRIBUTE  IndexRecord,
ULONG  IndexBlockSize,
PINDEX_ENTRY_ATTRIBUTE  FirstEntry,
PINDEX_ENTRY_ATTRIBUTE  LastEntry,
PUNICODE_STRING  FileName,
PULONG  StartEntry,
PULONG  CurrentEntry,
BOOLEAN  DirSearch,
BOOLEAN  CaseSensitive,
ULONGLONG *  OutMFTIndex 
)

Definition at line 3845 of file mft.c.

3857{
3859 PINDEX_ENTRY_ATTRIBUTE IndexEntry;
3860 PNTFS_ATTR_CONTEXT IndexAllocationContext;
3861 PNTFS_ATTR_CONTEXT BitmapContext;
3862 PCHAR *BitmapMem;
3863 ULONG *BitmapPtr;
3865
3866 DPRINT("BrowseIndexEntries(%p, %p, %p, %lu, %p, %p, %wZ, %lu, %lu, %s, %s, %p)\n",
3867 Vcb,
3868 MftRecord,
3869 IndexRecord,
3870 IndexBlockSize,
3871 FirstEntry,
3872 LastEntry,
3873 FileName,
3874 *StartEntry,
3875 *CurrentEntry,
3876 DirSearch ? "TRUE" : "FALSE",
3877 CaseSensitive ? "TRUE" : "FALSE",
3878 OutMFTIndex);
3879
3880 // Find the $I30 index allocation, if there is one
3881 Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationContext, NULL);
3882 if (NT_SUCCESS(Status))
3883 {
3884 ULONGLONG BitmapLength;
3885 // Find the bitmap attribute for the index
3886 Status = FindAttribute(Vcb, MftRecord, AttributeBitmap, L"$I30", 4, &BitmapContext, NULL);
3887 if (!NT_SUCCESS(Status))
3888 {
3889 DPRINT1("Potential file system corruption detected!\n");
3890 ReleaseAttributeContext(IndexAllocationContext);
3891 return Status;
3892 }
3893
3894 // Get the length of the bitmap attribute
3895 BitmapLength = AttributeDataLength(BitmapContext->pRecord);
3896
3897 // Allocate memory for the bitmap, including some padding; RtlInitializeBitmap() wants a pointer
3898 // that's ULONG-aligned, and it wants the size of the memory allocated for it to be a ULONG-multiple.
3899 BitmapMem = ExAllocatePoolWithTag(NonPagedPool, BitmapLength + sizeof(ULONG), TAG_NTFS);
3900 if (!BitmapMem)
3901 {
3902 DPRINT1("Error: failed to allocate bitmap!");
3903 ReleaseAttributeContext(BitmapContext);
3904 ReleaseAttributeContext(IndexAllocationContext);
3906 }
3907
3908 RtlZeroMemory(BitmapMem, BitmapLength + sizeof(ULONG));
3909
3910 // RtlInitializeBitmap() wants a pointer that's ULONG-aligned.
3911 BitmapPtr = (PULONG)ALIGN_UP_BY((ULONG_PTR)BitmapMem, sizeof(ULONG));
3912
3913 // Read the existing bitmap data
3914 Status = ReadAttribute(Vcb, BitmapContext, 0, (PCHAR)BitmapPtr, BitmapLength);
3915 if (!NT_SUCCESS(Status))
3916 {
3917 DPRINT1("ERROR: Failed to read bitmap attribute!\n");
3918 ExFreePoolWithTag(BitmapMem, TAG_NTFS);
3919 ReleaseAttributeContext(BitmapContext);
3920 ReleaseAttributeContext(IndexAllocationContext);
3921 return Status;
3922 }
3923
3924 // Initialize bitmap
3925 RtlInitializeBitMap(&Bitmap, BitmapPtr, BitmapLength * 8);
3926 }
3927 else
3928 {
3929 // Couldn't find an index allocation
3930 IndexAllocationContext = NULL;
3931 }
3932
3933
3934 // Loop through all Index Entries of index, starting with FirstEntry
3935 IndexEntry = FirstEntry;
3936 while (IndexEntry <= LastEntry)
3937 {
3938 // Does IndexEntry have a sub-node?
3939 if (IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
3940 {
3941 if (!(IndexRecord->Header.Flags & INDEX_ROOT_LARGE) || !IndexAllocationContext)
3942 {
3943 DPRINT1("Filesystem corruption detected!\n");
3944 }
3945 else
3946 {
3948 MftRecord,
3949 IndexBlockSize,
3950 FileName,
3951 IndexAllocationContext,
3952 &Bitmap,
3953 GetIndexEntryVCN(IndexEntry),
3954 StartEntry,
3955 CurrentEntry,
3956 DirSearch,
3957 CaseSensitive,
3958 OutMFTIndex);
3959
3960 /* Anything other than "not in this subtree" means we gave up on
3961 * a subtree that may well have held the entry */
3963 {
3964 DPRINT1("Sub-node at VCN %I64u abandoned while looking for '%wZ' (Status %lx)\n",
3965 GetIndexEntryVCN(IndexEntry), FileName, Status);
3966 }
3967
3968 if (NT_SUCCESS(Status))
3969 {
3970 ExFreePoolWithTag(BitmapMem, TAG_NTFS);
3971 ReleaseAttributeContext(BitmapContext);
3972 ReleaseAttributeContext(IndexAllocationContext);
3973 return Status;
3974 }
3975 }
3976 }
3977
3978 // Are we done?
3979 if (IndexEntry->Flags & NTFS_INDEX_ENTRY_END)
3980 break;
3981
3982 // If we've found a file whose index is greater than or equal to StartEntry that matches the search criteria
3983 if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) >= NTFS_FILE_FIRST_USER_FILE &&
3984 *CurrentEntry >= *StartEntry &&
3985 IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS &&
3986 CompareFileName(Vcb, FileName, IndexEntry, DirSearch, CaseSensitive))
3987 {
3988 *StartEntry = *CurrentEntry;
3989 *OutMFTIndex = (IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK);
3990 if (IndexAllocationContext)
3991 {
3992 ExFreePoolWithTag(BitmapMem, TAG_NTFS);
3993 ReleaseAttributeContext(BitmapContext);
3994 ReleaseAttributeContext(IndexAllocationContext);
3995 }
3996 return STATUS_SUCCESS;
3997 }
3998
3999 // Advance to the next index entry
4000 (*CurrentEntry) += 1;
4001 ASSERT(IndexEntry->Length >= sizeof(INDEX_ENTRY_ATTRIBUTE));
4002 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
4003 }
4004
4005 if (IndexAllocationContext)
4006 {
4007 DPRINT("'%wZ' not found after scanning %lu entries of MFT record %I64u "
4008 "(index allocation present)\n",
4009 FileName, *CurrentEntry, MftRecord->MFTRecordNumber & NTFS_MFT_MASK);
4010
4011 ExFreePoolWithTag(BitmapMem, TAG_NTFS);
4012 ReleaseAttributeContext(BitmapContext);
4013 ReleaseAttributeContext(IndexAllocationContext);
4014 }
4015
4017}
#define NTFS_FILE_NAME_DOS
Definition: ntfs.h:65
#define NTFS_INDEX_ENTRY_NODE
Definition: ntfs.h:60
#define NTFS_MFT_MASK
Definition: ntfs.h:76
#define NTFS_INDEX_ENTRY_END
Definition: ntfs.h:61
ULONGLONG GetIndexEntryVCN(PINDEX_ENTRY_ATTRIBUTE IndexEntry)
Definition: btree.c:1646
struct INDEX_ENTRY_ATTRIBUTE * PINDEX_ENTRY_ATTRIBUTE
#define NTFS_FILE_FIRST_USER_FILE
Definition: ntfs.h:223
#define INDEX_ROOT_LARGE
Definition: ntfs.h:236
@ AttributeIndexAllocation
Definition: ntfs.h:197
NTSTATUS BrowseSubNodeIndexEntries(PNTFS_VCB Vcb, PFILE_RECORD_HEADER MftRecord, ULONG IndexBlockSize, PUNICODE_STRING FileName, PNTFS_ATTR_CONTEXT IndexAllocationContext, PRTL_BITMAP Bitmap, ULONGLONG VCN, PULONG StartEntry, PULONG CurrentEntry, BOOLEAN DirSearch, BOOLEAN CaseSensitive, ULONGLONG *OutMFTIndex)
Definition: mft.c:3684
BOOLEAN CompareFileName(PDEVICE_EXTENSION Vcb, PUNICODE_STRING FileName, PINDEX_ENTRY_ATTRIBUTE IndexEntry, BOOLEAN DirSearch, BOOLEAN CaseSensitive)
Definition: mft.c:3491
UCHAR NameType
Definition: ntfs.h:409
Definition: ntfs.h:440
union INDEX_ENTRY_ATTRIBUTE::@969 Data
FILENAME_ATTRIBUTE FileName
Definition: ntfs.h:458
USHORT Length
Definition: ntfs.h:454
USHORT Flags
Definition: ntfs.h:456
struct INDEX_ENTRY_ATTRIBUTE::@969::@970 Directory
#define STATUS_OBJECT_PATH_NOT_FOUND
Definition: udferr_usr.h:151

Referenced by NtfsFindMftRecord().

◆ BrowseSubNodeIndexEntries()

NTSTATUS BrowseSubNodeIndexEntries ( PNTFS_VCB  Vcb,
PFILE_RECORD_HEADER  MftRecord,
ULONG  IndexBlockSize,
PUNICODE_STRING  FileName,
PNTFS_ATTR_CONTEXT  IndexAllocationContext,
PRTL_BITMAP  Bitmap,
ULONGLONG  VCN,
PULONG  StartEntry,
PULONG  CurrentEntry,
BOOLEAN  DirSearch,
BOOLEAN  CaseSensitive,
ULONGLONG *  OutMFTIndex 
)

Definition at line 3684 of file mft.c.

3696{
3697 PINDEX_BUFFER IndexRecord;
3700 PINDEX_ENTRY_ATTRIBUTE FirstEntry;
3701 PINDEX_ENTRY_ATTRIBUTE LastEntry;
3702 PINDEX_ENTRY_ATTRIBUTE IndexEntry;
3705
3706 DPRINT("BrowseSubNodeIndexEntries(%p, %p, %lu, %wZ, %p, %p, %I64d, %lu, %lu, %s, %s, %p)\n",
3707 Vcb,
3708 MftRecord,
3709 IndexBlockSize,
3710 FileName,
3711 IndexAllocationContext,
3712 Bitmap,
3713 VCN,
3714 *StartEntry,
3715 *CurrentEntry,
3716 "FALSE",
3717 DirSearch ? "TRUE" : "FALSE",
3718 CaseSensitive ? "TRUE" : "FALSE",
3719 OutMFTIndex);
3720
3721 // Calculate node number as VCN / Clusters per index record. An index record smaller than
3722 // a cluster is addressed in sectors, matching GetAllocationOffsetFromVCN().
3723 if (IndexBlockSize < Vcb->NtfsInfo.BytesPerCluster)
3724 NodeNumber = VCN / (IndexBlockSize / Vcb->NtfsInfo.BytesPerSector);
3725 else
3726 NodeNumber = VCN / (IndexBlockSize / Vcb->NtfsInfo.BytesPerCluster);
3727
3728 // Is the bit for this node clear in the bitmap?
3730 {
3731 DPRINT1("File system corruption detected, node with VCN %I64u is marked as deleted.\n", VCN);
3732 return STATUS_DATA_ERROR;
3733 }
3734
3735 // Allocate memory for the index record
3736 IndexRecord = ExAllocatePoolWithTag(NonPagedPool, IndexBlockSize, TAG_NTFS);
3737 if (!IndexRecord)
3738 {
3739 DPRINT1("Unable to allocate memory for index record!\n");
3741 }
3742
3743 // Calculate offset of index record, using the same helper the write path does
3744 Offset = GetAllocationOffsetFromVCN(Vcb, IndexBlockSize, VCN);
3745
3746 // Read the index record
3747 BytesRead = ReadAttribute(Vcb, IndexAllocationContext, Offset, (PCHAR)IndexRecord, IndexBlockSize);
3748 if (BytesRead != IndexBlockSize)
3749 {
3750 DPRINT1("Unable to read index record!\n");
3751 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
3752 return STATUS_UNSUCCESSFUL;
3753 }
3754
3755 if (IndexRecord->Ntfs.Type != NRH_INDX_TYPE)
3756 {
3757 DPRINT1("File system corruption detected, no index record at VCN %I64u of MFT record %I64u "
3758 "(offset 0x%I64x, block size %lu, cluster %lu, index record %lu, signature 0x%08lx)\n",
3759 VCN,
3760 MftRecord->MFTRecordNumber & NTFS_MFT_MASK,
3761 Offset,
3762 IndexBlockSize,
3763 Vcb->NtfsInfo.BytesPerCluster,
3764 Vcb->NtfsInfo.BytesPerIndexRecord,
3765 IndexRecord->Ntfs.Type);
3766 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
3767 return STATUS_DATA_ERROR;
3768 }
3769
3770 // Apply the fixup array to the index record
3771 Status = FixupUpdateSequenceArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs);
3772 if (!NT_SUCCESS(Status))
3773 {
3774 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
3775 DPRINT1("Failed to apply fixup array!\n");
3776 return Status;
3777 }
3778
3779 ASSERT(IndexRecord->Header.AllocatedSize + FIELD_OFFSET(INDEX_BUFFER, Header) == IndexBlockSize);
3780 FirstEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexRecord->Header + IndexRecord->Header.FirstEntryOffset);
3781 LastEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexRecord->Header + IndexRecord->Header.TotalSizeOfEntries);
3782 ASSERT(LastEntry <= (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexRecord + IndexBlockSize));
3783
3784 // Loop through all Index Entries of index, starting with FirstEntry
3785 IndexEntry = FirstEntry;
3786 while (IndexEntry <= LastEntry)
3787 {
3788 // Does IndexEntry have a sub-node?
3789 if (IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
3790 {
3791 if (!(IndexRecord->Header.Flags & INDEX_NODE_LARGE) || !IndexAllocationContext)
3792 {
3793 DPRINT1("Filesystem corruption detected!\n");
3794 }
3795 else
3796 {
3798 MftRecord,
3799 IndexBlockSize,
3800 FileName,
3801 IndexAllocationContext,
3802 Bitmap,
3803 GetIndexEntryVCN(IndexEntry),
3804 StartEntry,
3805 CurrentEntry,
3806 DirSearch,
3807 CaseSensitive,
3808 OutMFTIndex);
3809 if (NT_SUCCESS(Status))
3810 {
3811 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
3812 return Status;
3813 }
3814 }
3815 }
3816
3817 // Are we done?
3818 if (IndexEntry->Flags & NTFS_INDEX_ENTRY_END)
3819 break;
3820
3821 // If we've found a file whose index is greater than or equal to StartEntry that matches the search criteria
3822 if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) >= NTFS_FILE_FIRST_USER_FILE &&
3823 *CurrentEntry >= *StartEntry &&
3824 IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS &&
3825 CompareFileName(Vcb, FileName, IndexEntry, DirSearch, CaseSensitive))
3826 {
3827 *StartEntry = *CurrentEntry;
3828 *OutMFTIndex = (IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK);
3829 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
3830 return STATUS_SUCCESS;
3831 }
3832
3833 // Advance to the next index entry
3834 (*CurrentEntry) += 1;
3835 ASSERT(IndexEntry->Length >= sizeof(INDEX_ENTRY_ATTRIBUTE));
3836 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
3837 }
3838
3839 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
3840
3842}
ULONGLONG GetAllocationOffsetFromVCN(PDEVICE_EXTENSION DeviceExt, ULONG IndexBufferSize, ULONGLONG Vcn)
Definition: btree.c:1635
#define RtlCheckBit
Definition: dbgbitmap.h:349
#define INDEX_NODE_LARGE
Definition: ntfs.h:239
#define NRH_INDX_TYPE
Definition: ntfs.h:278
NTSTATUS FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb, PNTFS_RECORD_HEADER Record)
Definition: mft.c:2294
#define STATUS_DATA_ERROR
Definition: ntstatus.h:392
INDEX_HEADER_ATTRIBUTE Header
Definition: ntfs.h:436
NTFS_RECORD_HEADER Ntfs
Definition: ntfs.h:434
ULONG FirstEntryOffset
Definition: ntfs.h:415
ULONG TotalSizeOfEntries
Definition: ntfs.h:416
#define STATUS_UNSUCCESSFUL
Definition: udferr_usr.h:132
_Must_inspect_result_ _In_ WDFIOTARGET _In_opt_ WDFREQUEST _In_opt_ PWDF_MEMORY_DESCRIPTOR _In_opt_ PLONGLONG _In_opt_ PWDF_REQUEST_SEND_OPTIONS _Out_opt_ PULONG_PTR BytesRead
Definition: wdfiotarget.h:870
_Out_ PUSHORT NodeNumber
Definition: iofuncs.h:2545

Referenced by BrowseIndexEntries(), and BrowseSubNodeIndexEntries().

◆ CompareFileName()

BOOLEAN CompareFileName ( PDEVICE_EXTENSION  Vcb,
PUNICODE_STRING  FileName,
PINDEX_ENTRY_ATTRIBUTE  IndexEntry,
BOOLEAN  DirSearch,
BOOLEAN  CaseSensitive 
)

Definition at line 3491 of file mft.c.

3496{
3497 BOOLEAN Ret, Alloc = FALSE;
3498 UNICODE_STRING EntryName;
3499
3500 EntryName.Buffer = IndexEntry->FileName.Name;
3501 EntryName.Length =
3502 EntryName.MaximumLength = IndexEntry->FileName.NameLength * sizeof(WCHAR);
3503
3504 if (DirSearch)
3505 {
3506 UNICODE_STRING IntFileName;
3507 if (!CaseSensitive)
3508 {
3510 Alloc = TRUE;
3511 }
3512 else
3513 {
3514 IntFileName = *FileName;
3515 }
3516
3517 /* With a real upcase table the matcher folds both sides through it,
3518 * which is what NTFS means by case-insensitive. */
3519 Ret = FsRtlIsNameInExpression(&IntFileName, &EntryName, !CaseSensitive,
3520 Vcb ? Vcb->UpCaseTable : NULL);
3521
3522 if (Alloc)
3523 {
3524 RtlFreeUnicodeString(&IntFileName);
3525 }
3526
3527 return Ret;
3528 }
3529 else
3530 {
3531 return (RtlCompareUnicodeString(FileName, &EntryName, !CaseSensitive) == 0);
3532 }
3533}
unsigned char BOOLEAN
Definition: actypes.h:127
PVOID Alloc(IN DWORD dwFlags, IN SIZE_T dwBytes)
Definition: main.c:63
#define TRUE
Definition: types.h:120
ULONG RtlCompareUnicodeString(PUNICODE_STRING s1, PUNICODE_STRING s2, BOOLEAN UpCase)
Definition: string_lib.cpp:31
NTSTATUS RtlUpcaseUnicodeString(PUNICODE_STRING dst, PUNICODE_STRING src, BOOLEAN Alloc)
Definition: string_lib.cpp:46
struct _FileName FileName
Definition: fatprocs.h:897
NTSYSAPI VOID NTAPI RtlFreeUnicodeString(PUNICODE_STRING UnicodeString)
short WCHAR
Definition: pedump.c:58
USHORT MaximumLength
Definition: env_spec_w32.h:370
#define NT_VERIFY(exp)
Definition: rtlfuncs.h:3311

Referenced by BrowseIndexEntries(), BrowseSubNodeIndexEntries(), and UpdateIndexEntryFileName().

◆ FindAttribute()

NTSTATUS FindAttribute ( PDEVICE_EXTENSION  Vcb,
PFILE_RECORD_HEADER  MftRecord,
ULONG  Type,
PCWSTR  Name,
ULONG  NameLength,
PNTFS_ATTR_CONTEXT *  AttrCtx,
PULONG  Offset 
)

Definition at line 131 of file mft.c.

138{
142 PNTFS_ATTR_RECORD Attribute;
143 PNTFS_ATTRIBUTE_LIST_ITEM AttrListItem;
144
145 DPRINT("FindAttribute(%p, %p, 0x%x, %S, %lu, %p, %p)\n", Vcb, MftRecord, Type, Name, NameLength, AttrCtx, Offset);
146
147 Found = FALSE;
148 Status = FindFirstAttribute(&Context, Vcb, MftRecord, FALSE, &Attribute);
149 while (NT_SUCCESS(Status))
150 {
151 if (Attribute->Type == Type && Attribute->NameLength == NameLength)
152 {
153 if (NameLength != 0)
154 {
155 PWCHAR AttrName;
156
157 AttrName = (PWCHAR)((PCHAR)Attribute + Attribute->NameOffset);
158 DPRINT("%.*S, %.*S\n", Attribute->NameLength, AttrName, NameLength, Name);
159 if (RtlCompareMemory(AttrName, Name, NameLength * sizeof(WCHAR)) == (NameLength * sizeof(WCHAR)))
160 {
161 Found = TRUE;
162 }
163 }
164 else
165 {
166 Found = TRUE;
167 }
168
169 if (Found)
170 {
171 /* Found it, fill up the context and return. */
172 DPRINT("Found context\n");
173 *AttrCtx = PrepareAttributeContext(Attribute);
174
175 (*AttrCtx)->FileMFTIndex = MftRecord->MFTRecordNumber;
176
177 if (Offset != NULL)
178 *Offset = Context.Offset;
179
181 return STATUS_SUCCESS;
182 }
183 }
184
185 Status = FindNextAttribute(&Context, &Attribute);
186 }
187
188 /* No attribute found, check if it is referenced in another file record */
189 Status = FindFirstAttributeListItem(&Context, &AttrListItem);
190 while (NT_SUCCESS(Status))
191 {
192 if (AttrListItem->Type == Type && AttrListItem->NameLength == NameLength)
193 {
194 if (NameLength != 0)
195 {
196 PWCHAR AttrName;
197
198 AttrName = (PWCHAR)((PCHAR)AttrListItem + AttrListItem->NameOffset);
199 DPRINT("%.*S, %.*S\n", AttrListItem->NameLength, AttrName, NameLength, Name);
200 if (RtlCompareMemory(AttrName, Name, NameLength * sizeof(WCHAR)) == (NameLength * sizeof(WCHAR)))
201 {
202 Found = TRUE;
203 }
204 }
205 else
206 {
207 Found = TRUE;
208 }
209
210 if (Found == TRUE)
211 {
212 /* Get the MFT Index of attribute */
213 ULONGLONG MftIndex;
214 PFILE_RECORD_HEADER RemoteHdr;
215
216 MftIndex = AttrListItem->MFTIndex & NTFS_MFT_MASK;
217 RemoteHdr = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
218
219 if (RemoteHdr == NULL)
220 {
223 }
224
225 /* Check we are not reading ourselves */
226 if (MftRecord->MFTRecordNumber == MftIndex)
227 {
228 DPRINT1("Attribute list references missing attribute to this file entry !");
229 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, RemoteHdr);
232 }
233 /* Read the new file record */
234 ReadFileRecord(Vcb, MftIndex, RemoteHdr);
235 Status = FindAttribute(Vcb, RemoteHdr, Type, Name, NameLength, AttrCtx, Offset);
236 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, RemoteHdr);
238 return Status;
239 }
240 }
241 Status = FindNextAttributeListItem(&Context, &AttrListItem);
242 }
245}
Type
Definition: Type.h:7
return Found
Definition: dirsup.c:1270
LPWSTR Name
Definition: desk.c:124
NTSTATUS FindFirstAttribute(PFIND_ATTR_CONTXT Context, PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER FileRecord, BOOLEAN OnlyResident, PNTFS_ATTR_RECORD *Attribute)
Definition: attrib.c:1413
NTSTATUS FindFirstAttributeListItem(PFIND_ATTR_CONTXT Context, PNTFS_ATTRIBUTE_LIST_ITEM *Item)
Definition: attrib.c:1337
VOID FindCloseAttribute(PFIND_ATTR_CONTXT Context)
Definition: attrib.c:1495
NTSTATUS FindNextAttribute(PFIND_ATTR_CONTXT Context, PNTFS_ATTR_RECORD *Attribute)
Definition: attrib.c:1461
NTSTATUS FindNextAttributeListItem(PFIND_ATTR_CONTXT Context, PNTFS_ATTRIBUTE_LIST_ITEM *Item)
Definition: attrib.c:1351
#define RtlCompareMemory(s1, s2, l)
Definition: env_spec_w32.h:465
PNTFS_ATTR_CONTEXT PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord)
Definition: mft.c:41
_In_ PVOID Context
Definition: storport.h:2325
ULONGLONG MFTIndex
Definition: ntfs.h:345
USHORT NameOffset
Definition: ntfs.h:138
UCHAR NameLength
Definition: ntfs.h:137
ULONG Type
Definition: ntfs.h:134
uint16_t * PWCHAR
Definition: typedefs.h:56

Referenced by AddFilenameToDirectoryWorker(), AddNewMftEntry(), AllocateIndexNode(), BrowseIndexEntries(), CreateBTreeFromIndex(), tinyxml2::XMLElement::FindAttribute(), FindAttribute(), FreeMftEntry(), IncreaseMftSize(), NtfsCreateFile(), NtfsDeleteFileRecord(), NtfsDirFindFile(), NtfsFindMftRecord(), NtfsGetFileSize(), NtfsGetVolumeData(), NtfsIsDirectoryEmpty(), NtfsLoadVolumeBitmap(), NtfsReadFCBAttribute(), NtfsReadFile(), NtfsReadUpCaseTable(), NtfsSetEndOfFile(), NtfsTrimAllocation(), NtfsWriteFile(), tinyxml2::XMLElement::QueryBoolAttribute(), tinyxml2::XMLElement::QueryDoubleAttribute(), tinyxml2::XMLElement::QueryFloatAttribute(), tinyxml2::XMLElement::QueryIntAttribute(), tinyxml2::XMLElement::QueryUnsignedAttribute(), RemoveFilenameFromDirectoryWorker(), UpdateFileNameRecordWorker(), UpdateIndexAllocation(), UpdateIndexEntryFileName(), UpdateMftMirror(), and WriteAttributeFromIrp().

◆ FixupUpdateSequenceArray()

NTSTATUS FixupUpdateSequenceArray ( PDEVICE_EXTENSION  Vcb,
PNTFS_RECORD_HEADER  Record 
)

Definition at line 2294 of file mft.c.

2296{
2297 USHORT *USA;
2298 USHORT USANumber;
2299 USHORT USACount;
2300 USHORT *Block;
2301
2302 USA = (USHORT*)((PCHAR)Record + Record->UsaOffset);
2303 USANumber = *(USA++);
2304 USACount = Record->UsaCount - 1; /* Exclude the USA Number. */
2305 Block = (USHORT*)((PCHAR)Record + Vcb->NtfsInfo.BytesPerSector - 2);
2306
2307 DPRINT("FixupUpdateSequenceArray(%p, %p)\nUSANumber: %u\tUSACount: %u\n", Vcb, Record, USANumber, USACount);
2308
2309 while (USACount)
2310 {
2311 if (*Block != USANumber)
2312 {
2313 DPRINT1("Mismatch with USA: %u read, %u expected\n" , *Block, USANumber);
2314 return STATUS_UNSUCCESSFUL;
2315 }
2316 *Block = *(USA++);
2317 Block = (USHORT*)((PCHAR)Block + Vcb->NtfsInfo.BytesPerSector);
2318 USACount--;
2319 }
2320
2321 return STATUS_SUCCESS;
2322}

Referenced by BrowseSubNodeIndexEntries(), CreateBTreeNodeFromIndexNode(), NtfsIndexNodeIsEmpty(), PrintAllVCNs(), ReadFileRecord(), UpdateFileRecord(), and UpdateIndexEntryFileName().

◆ FreeMftEntry()

NTSTATUS FreeMftEntry ( PDEVICE_EXTENSION  DeviceExt,
ULONGLONG  MftIndex 
)

Definition at line 3205 of file mft.c.

3207{
3210 ULONGLONG BitmapDataSize;
3211 ULONGLONG AttrBytesRead;
3214 ULONG LengthWritten;
3215 PNTFS_ATTR_CONTEXT BitmapContext;
3216 LARGE_INTEGER BitmapBits;
3217 PFILE_RECORD_HEADER FileRecord;
3218
3219 DPRINT("FreeMftEntry(%p, %I64u)\n", DeviceExt, MftIndex);
3220
3221 // Mark the record itself as free first, so a bitmap bit is never handed out for a record
3222 // that still looks in use.
3223 FileRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList);
3224 if (!FileRecord)
3226
3227 Status = ReadFileRecord(DeviceExt, MftIndex, FileRecord);
3228 if (!NT_SUCCESS(Status))
3229 {
3230 DPRINT1("ERROR: Couldn't read file record %I64u to free it!\n", MftIndex);
3231 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
3232 return Status;
3233 }
3234
3235 FileRecord->Flags &= ~FRH_IN_USE;
3236 FileRecord->SequenceNumber++;
3237
3238 // Sequence number zero is reserved to mean "no reference"
3239 if (FileRecord->SequenceNumber == 0)
3240 FileRecord->SequenceNumber = 1;
3241
3242 Status = UpdateFileRecord(DeviceExt, MftIndex, FileRecord);
3243 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
3244
3245 if (!NT_SUCCESS(Status))
3246 {
3247 DPRINT1("ERROR: Couldn't write file record %I64u while freeing it!\n", MftIndex);
3248 return Status;
3249 }
3250
3251 Status = FindAttribute(DeviceExt, DeviceExt->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, NULL);
3252 if (!NT_SUCCESS(Status))
3253 {
3254 DPRINT1("ERROR: Couldn't find $Bitmap attribute of master file table!\n");
3255 return Status;
3256 }
3257
3258 BitmapDataSize = AttributeDataLength(BitmapContext->pRecord);
3259
3260 // RtlInitializeBitmap wants a ULONG-aligned pointer and a ULONG-multiple of memory
3261 BitmapBuffer = ExAllocatePoolWithTag(NonPagedPool, BitmapDataSize + sizeof(ULONG), TAG_NTFS);
3262 if (!BitmapBuffer)
3263 {
3264 ReleaseAttributeContext(BitmapContext);
3266 }
3267 RtlZeroMemory(BitmapBuffer, BitmapDataSize + sizeof(ULONG));
3268
3270
3271 AttrBytesRead = ReadAttribute(DeviceExt, BitmapContext, 0, (PCHAR)BitmapData, BitmapDataSize);
3272 if (AttrBytesRead != BitmapDataSize)
3273 {
3274 DPRINT1("ERROR: Unable to read $Bitmap attribute of master file table!\n");
3276 ReleaseAttributeContext(BitmapContext);
3278 }
3279
3280 BitmapBits.QuadPart = AttributeDataLength(DeviceExt->MFTContext->pRecord) /
3281 DeviceExt->NtfsInfo.BytesPerFileRecord;
3282 if (BitmapBits.HighPart != 0)
3283 {
3284 DPRINT1("\tFIXME: bitmap sizes beyond 32bits are not yet supported! (Your NTFS volume is too large)\n");
3286 ReleaseAttributeContext(BitmapContext);
3288 }
3289
3290 if (MftIndex >= BitmapBits.QuadPart)
3291 {
3292 DPRINT1("ERROR: Mft index %I64u is beyond the end of the master file table!\n", MftIndex);
3294 ReleaseAttributeContext(BitmapContext);
3296 }
3297
3299 RtlClearBits(&Bitmap, (ULONG)MftIndex, 1);
3300
3301 Status = WriteAttribute(DeviceExt, BitmapContext, 0, BitmapData, BitmapDataSize, &LengthWritten, NULL);
3302 if (!NT_SUCCESS(Status))
3303 {
3304 DPRINT1("ERROR encountered when writing $Bitmap attribute!\n");
3305 }
3306
3308 ReleaseAttributeContext(BitmapContext);
3309
3310 return Status;
3311}
#define RtlClearBits
Definition: dbgbitmap.h:331
USHORT SequenceNumber
Definition: ntfs.h:284
USHORT Flags
Definition: ntfs.h:287
#define STATUS_INVALID_PARAMETER
Definition: udferr_usr.h:135

Referenced by NtfsDeleteFileRecord().

◆ GetMaxIndexRootSize()

static ULONG GetMaxIndexRootSize ( PDEVICE_EXTENSION  DeviceExt,
PFILE_RECORD_HEADER  FileRecord,
ULONG  IndexRootOffset,
PNTFS_ATTR_RECORD  IndexRootRecord 
)
static

Definition at line 2511 of file mft.c.

2515{
2516 ULONG MaxIndexRootSize;
2517 PNTFS_ATTR_RECORD CurrentAttribute;
2518
2519 // First, find the max index size assuming index root is the last attribute
2520 MaxIndexRootSize = DeviceExt->NtfsInfo.BytesPerFileRecord // Start with the size of a file record
2521 - IndexRootOffset // Subtract the length of everything that comes before index root
2522 - IndexRootRecord->Resident.ValueOffset // Subtract the length of the attribute header for index root
2523 - sizeof(INDEX_ROOT_ATTRIBUTE) // Subtract the length of the index root header
2524 - (sizeof(ULONG) * 2); // Subtract the length of the file record end marker and padding
2525
2526 // Leave room for any attributes after this one, not counting the end marker
2527 CurrentAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + IndexRootOffset + IndexRootRecord->Length);
2528 while (CurrentAttribute->Type != AttributeEnd)
2529 {
2530 MaxIndexRootSize -= CurrentAttribute->Length;
2531 CurrentAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)CurrentAttribute + CurrentAttribute->Length);
2532 }
2533
2534 return MaxIndexRootSize;
2535}
struct NTFS_ATTR_RECORD * PNTFS_ATTR_RECORD
@ AttributeEnd
Definition: ntfs.h:204
ULONG Length
Definition: ntfs.h:135

Referenced by AddFilenameToDirectoryWorker(), and RemoveFilenameFromDirectoryWorker().

◆ IncreaseMftSize()

NTSTATUS IncreaseMftSize ( PDEVICE_EXTENSION  Vcb,
BOOLEAN  CanWait 
)

Definition at line 293 of file mft.c.

294{
295 PNTFS_ATTR_CONTEXT BitmapContext;
296 LARGE_INTEGER BitmapSize;
298 LONGLONG BitmapSizeDifference;
299 ULONG NewRecords = ATTR_RECORD_ALIGNMENT * 8; // Allocate one new record for every bit of every byte we'll be adding to the bitmap
300 ULONG DataSizeDifference = Vcb->NtfsInfo.BytesPerFileRecord * NewRecords;
301 ULONG BitmapOffset;
303 ULONGLONG BitmapBytes;
304 ULONGLONG NewBitmapSize;
305 ULONGLONG FirstNewMftIndex;
307 ULONG LengthWritten;
308 PFILE_RECORD_HEADER BlankFileRecord;
309 ULONG i;
311
312 DPRINT("IncreaseMftSize(%p, %s)\n", Vcb, CanWait ? "TRUE" : "FALSE");
313
314 // We need exclusive access to the mft while we change its size
315 if (!ExAcquireResourceExclusiveLite(&(Vcb->DirResource), CanWait))
316 {
317 return STATUS_CANT_WAIT;
318 }
319
320 // Create a blank file record that will be used later
321 BlankFileRecord = NtfsCreateEmptyFileRecord(Vcb);
322 if (!BlankFileRecord)
323 {
324 DPRINT1("Error: Unable to create empty file record!\n");
326 }
327
328 // Clear the flags (file record is not in use)
329 BlankFileRecord->Flags = 0;
330
331 // Find the bitmap attribute of master file table
332 Status = FindAttribute(Vcb, Vcb->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, NULL);
333 if (!NT_SUCCESS(Status))
334 {
335 DPRINT1("ERROR: Couldn't find $BITMAP attribute of Mft!\n");
336 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
337 ExReleaseResourceLite(&(Vcb->DirResource));
338 return Status;
339 }
340
341 // Get size of Bitmap Attribute
342 BitmapSize.QuadPart = AttributeDataLength(BitmapContext->pRecord);
343
344 // Calculate the new mft size
345 DataSize.QuadPart = AttributeDataLength(Vcb->MFTContext->pRecord) + DataSizeDifference;
346
347 // Find the index of the first Mft entry that will be created
348 FirstNewMftIndex = AttributeDataLength(Vcb->MFTContext->pRecord) / Vcb->NtfsInfo.BytesPerFileRecord;
349
350 // Determine how many bytes will make up the bitmap
351 BitmapBytes = DataSize.QuadPart / Vcb->NtfsInfo.BytesPerFileRecord / 8;
352 if ((DataSize.QuadPart / Vcb->NtfsInfo.BytesPerFileRecord) % 8 != 0)
353 BitmapBytes++;
354
355 // Windows will always keep the number of bytes in a bitmap as a multiple of 8, so no bytes are wasted on slack
356 BitmapBytes = ALIGN_UP_BY(BitmapBytes, ATTR_RECORD_ALIGNMENT);
357
358 // Determine how much we need to adjust the bitmap size (it's possible we don't)
359 BitmapSizeDifference = BitmapBytes - BitmapSize.QuadPart;
360 NewBitmapSize = max(BitmapSize.QuadPart + BitmapSizeDifference, BitmapSize.QuadPart);
361
362 // Allocate memory for the bitmap
364 if (!BitmapBuffer)
365 {
366 DPRINT1("ERROR: Unable to allocate memory for bitmap attribute!\n");
367 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
368 ExReleaseResourceLite(&(Vcb->DirResource));
369 ReleaseAttributeContext(BitmapContext);
371 }
372
373 // Zero the bytes we'll be adding
374 RtlZeroMemory(BitmapBuffer, NewBitmapSize);
375
376 // Read the bitmap attribute
378 BitmapContext,
379 0,
381 BitmapSize.LowPart);
382 if (BytesRead != BitmapSize.LowPart)
383 {
384 DPRINT1("ERROR: Bytes read != Bitmap size!\n");
385 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
386 ExReleaseResourceLite(&(Vcb->DirResource));
388 ReleaseAttributeContext(BitmapContext);
390 }
391
392 // Increase the mft size
393 Status = SetNonResidentAttributeDataLength(Vcb, Vcb->MFTContext, Vcb->MftDataOffset, Vcb->MasterFileTable, &DataSize);
394 if (!NT_SUCCESS(Status))
395 {
396 DPRINT1("ERROR: Failed to set size of $MFT data attribute!\n");
397 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
398 ExReleaseResourceLite(&(Vcb->DirResource));
400 ReleaseAttributeContext(BitmapContext);
401 return Status;
402 }
403
404 // We'll need to find the bitmap again, because its offset will have changed after resizing the data attribute
405 ReleaseAttributeContext(BitmapContext);
406 Status = FindAttribute(Vcb, Vcb->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, &BitmapOffset);
407 if (!NT_SUCCESS(Status))
408 {
409 DPRINT1("ERROR: Couldn't find $BITMAP attribute of Mft!\n");
410 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
411 ExReleaseResourceLite(&(Vcb->DirResource));
412 return Status;
413 }
414
415 // If the bitmap grew
416 if (BitmapSizeDifference > 0)
417 {
418 // Set the new bitmap size
419 BitmapSize.QuadPart = NewBitmapSize;
420 if (BitmapContext->pRecord->IsNonResident)
421 Status = SetNonResidentAttributeDataLength(Vcb, BitmapContext, BitmapOffset, Vcb->MasterFileTable, &BitmapSize);
422 else
423 Status = SetResidentAttributeDataLength(Vcb, BitmapContext, BitmapOffset, Vcb->MasterFileTable, &BitmapSize);
424
425 if (!NT_SUCCESS(Status))
426 {
427 DPRINT1("ERROR: Failed to set size of bitmap attribute!\n");
428 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
429 ExReleaseResourceLite(&(Vcb->DirResource));
431 ReleaseAttributeContext(BitmapContext);
432 return Status;
433 }
434 }
435
436 NtfsDumpFileAttributes(Vcb, Vcb->MasterFileTable);
437
438 // Update the file record with the new attribute sizes
439 Status = UpdateFileRecord(Vcb, Vcb->VolumeFcb->MFTIndex, Vcb->MasterFileTable);
440 if (!NT_SUCCESS(Status))
441 {
442 DPRINT1("ERROR: Failed to update $MFT file record!\n");
443 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
444 ExReleaseResourceLite(&(Vcb->DirResource));
446 ReleaseAttributeContext(BitmapContext);
447 return Status;
448 }
449
450 // Write out the new bitmap
451 Status = WriteAttribute(Vcb, BitmapContext, 0, BitmapBuffer, NewBitmapSize, &LengthWritten, Vcb->MasterFileTable);
452 if (!NT_SUCCESS(Status))
453 {
454 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
455 ExReleaseResourceLite(&(Vcb->DirResource));
457 ReleaseAttributeContext(BitmapContext);
458 DPRINT1("ERROR: Couldn't write to bitmap attribute of $MFT!\n");
459 return Status;
460 }
461
462 // Create blank records for the new file record entries.
463 for (i = 0; i < NewRecords; i++)
464 {
465 Status = UpdateFileRecord(Vcb, FirstNewMftIndex + i, BlankFileRecord);
466 if (!NT_SUCCESS(Status))
467 {
468 DPRINT1("ERROR: Failed to write blank file record!\n");
469 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
470 ExReleaseResourceLite(&(Vcb->DirResource));
472 ReleaseAttributeContext(BitmapContext);
473 return Status;
474 }
475 }
476
477 // Update the mft mirror
479
480 // Cleanup
481 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
482 ExReleaseResourceLite(&(Vcb->DirResource));
484 ReleaseAttributeContext(BitmapContext);
485
486 return Status;
487}
VOID NtfsDumpFileAttributes(PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER FileRecord)
Definition: attrib.c:1820
PFILE_RECORD_HEADER NtfsCreateEmptyFileRecord(PDEVICE_EXTENSION DeviceExt)
Definition: create.c:1026
#define ExAcquireResourceExclusiveLite(res, wait)
Definition: env_spec_w32.h:615
NTSTATUS UpdateMftMirror(PNTFS_VCB Vcb)
Definition: mft.c:3559
NTSTATUS SetNonResidentAttributeDataLength(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT AttrContext, ULONG AttrOffset, PFILE_RECORD_HEADER FileRecord, PLARGE_INTEGER DataSize)
Definition: mft.c:937
NTSTATUS SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT AttrContext, ULONG AttrOffset, PFILE_RECORD_HEADER FileRecord, PLARGE_INTEGER DataSize)
Definition: mft.c:1020
_In_ NDIS_STATUS _In_ ULONG _In_ USHORT _In_opt_ PVOID _In_ ULONG DataSize
Definition: ndis.h:4755
VOID FASTCALL ExReleaseResourceLite(IN PERESOURCE Resource)
Definition: resource.c:1822
#define STATUS_CANT_WAIT
Definition: ntstatus.h:546
#define max(a, b)
Definition: svc.c:63
int64_t LONGLONG
Definition: typedefs.h:68

Referenced by AddNewMftEntry().

◆ InternalSetResidentAttributeLength()

NTSTATUS InternalSetResidentAttributeLength ( PDEVICE_EXTENSION  DeviceExt,
PNTFS_ATTR_CONTEXT  AttrContext,
PFILE_RECORD_HEADER  FileRecord,
ULONG  AttrOffset,
ULONG  DataSize 
)

Definition at line 542 of file mft.c.

547{
548 PNTFS_ATTR_RECORD Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
550 PNTFS_ATTR_RECORD FinalAttribute;
551 ULONG OldAttributeLength = Destination->Length;
552 ULONG NextAttributeOffset;
553
554 DPRINT("InternalSetResidentAttributeLength( %p, %p, %p, %lu, %lu )\n", DeviceExt, AttrContext, FileRecord, AttrOffset, DataSize);
555
556 ASSERT(!AttrContext->pRecord->IsNonResident);
557
558 // Update ValueLength Field
559 Destination->Resident.ValueLength = DataSize;
560
561 // Calculate the record length and end marker offset
562 Destination->Length = ALIGN_UP_BY(DataSize + AttrContext->pRecord->Resident.ValueOffset, ATTR_RECORD_ALIGNMENT);
563 NextAttributeOffset = AttrOffset + Destination->Length;
564
565 // Ensure NextAttributeOffset is aligned to an 8-byte boundary
566 ASSERT(NextAttributeOffset % ATTR_RECORD_ALIGNMENT == 0);
567
568 // Will the new attribute be larger than the old one?
569 if (Destination->Length > OldAttributeLength)
570 {
571 // Free the old copy of the attribute in the context, as it will be the wrong length
572 ExFreePoolWithTag(AttrContext->pRecord, TAG_NTFS);
573
574 // Create a new copy of the attribute record for the context
576 if (!AttrContext->pRecord)
577 {
578 DPRINT1("Unable to allocate memory for attribute!\n");
580 }
581 RtlZeroMemory((PVOID)((ULONG_PTR)AttrContext->pRecord + OldAttributeLength), Destination->Length - OldAttributeLength);
582 RtlCopyMemory(AttrContext->pRecord, Destination, OldAttributeLength);
583 }
584
585 // Are there attributes after this one that need to be moved?
586 if (NextAttribute->Type != AttributeEnd)
587 {
588 // Move the attributes after this one
589 FinalAttribute = MoveAttributes(DeviceExt, NextAttribute, NextAttributeOffset, (ULONG_PTR)Destination + Destination->Length);
590 }
591 else
592 {
593 // advance to the final "attribute," adjust for the changed length of the attribute we're resizing
594 FinalAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute - OldAttributeLength + Destination->Length);
595 }
596
597 // Update pRecord's length
598 AttrContext->pRecord->Length = Destination->Length;
599 AttrContext->pRecord->Resident.ValueLength = DataSize;
600
601 // set the file record end
602 SetFileRecordEnd(FileRecord, FinalAttribute, FILE_RECORD_END);
603
604 //NtfsDumpFileRecord(DeviceExt, FileRecord);
605
606 return STATUS_SUCCESS;
607}
#define FILE_RECORD_END
Definition: ntfs.h:209
VOID SetFileRecordEnd(PFILE_RECORD_HEADER FileRecord, PNTFS_ATTR_RECORD AttrEnd, ULONG EndMarker)
Definition: mft.c:706
PNTFS_ATTR_RECORD MoveAttributes(PDEVICE_EXTENSION DeviceExt, PNTFS_ATTR_RECORD FirstAttributeToMove, ULONG FirstAttributeOffset, ULONG_PTR MoveTo)
Definition: mft.c:512
_In_ PUNICODE_STRING _Inout_ PUNICODE_STRING Destination
Definition: rtlfuncs.h:3051
#define RtlCopyMemory(Destination, Source, Length)
Definition: typedefs.h:263

Referenced by AddFilenameToDirectoryWorker(), RemoveFilenameFromDirectoryWorker(), and SetResidentAttributeDataLength().

◆ MoveAttributes()

PNTFS_ATTR_RECORD MoveAttributes ( PDEVICE_EXTENSION  DeviceExt,
PNTFS_ATTR_RECORD  FirstAttributeToMove,
ULONG  FirstAttributeOffset,
ULONG_PTR  MoveTo 
)

Definition at line 512 of file mft.c.

516{
517 // Get the size of all attributes after this one
518 ULONG MemBlockSize = 0;
519 PNTFS_ATTR_RECORD CurrentAttribute = FirstAttributeToMove;
520 ULONG CurrentOffset = FirstAttributeOffset;
521 PNTFS_ATTR_RECORD FinalAttribute;
522
523 while (CurrentAttribute->Type != AttributeEnd && CurrentOffset < DeviceExt->NtfsInfo.BytesPerFileRecord)
524 {
525 CurrentOffset += CurrentAttribute->Length;
526 MemBlockSize += CurrentAttribute->Length;
527 CurrentAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)CurrentAttribute + CurrentAttribute->Length);
528 }
529
530 FinalAttribute = (PNTFS_ATTR_RECORD)(MoveTo + MemBlockSize);
531 MemBlockSize += sizeof(ULONG) * 2; // Add the AttributeEnd and file record end
532
533 ASSERT(MemBlockSize % ATTR_RECORD_ALIGNMENT == 0);
534
535 // Move the attributes after this one
536 RtlMoveMemory((PCHAR)MoveTo, FirstAttributeToMove, MemBlockSize);
537
538 return FinalAttribute;
539}
#define RtlMoveMemory(Destination, Source, Length)
Definition: typedefs.h:264

Referenced by AddRun(), and InternalSetResidentAttributeLength().

◆ NtfsAddFilenameToDirectory()

NTSTATUS NtfsAddFilenameToDirectory ( PDEVICE_EXTENSION  DeviceExt,
ULONGLONG  DirectoryMftIndex,
ULONGLONG  FileReferenceNumber,
PFILENAME_ATTRIBUTE  FilenameAttribute,
BOOLEAN  CaseSensitive 
)

Definition at line 2918 of file mft.c.

2923{
2925
2927 ExAcquireResourceExclusiveLite(&DeviceExt->IndexResource, TRUE);
2928 Status = AddFilenameToDirectoryWorker(DeviceExt, DirectoryMftIndex, FileReferenceNumber, FilenameAttribute, CaseSensitive);
2929 ExReleaseResourceLite(&DeviceExt->IndexResource);
2931
2932 return Status;
2933}
#define KeLeaveCriticalRegion()
Definition: ke_x.h:119
#define KeEnterCriticalRegion()
Definition: ke_x.h:88
static NTSTATUS AddFilenameToDirectoryWorker(PDEVICE_EXTENSION DeviceExt, ULONGLONG DirectoryMftIndex, ULONGLONG FileReferenceNumber, PFILENAME_ATTRIBUTE FilenameAttribute, BOOLEAN CaseSensitive)
Definition: mft.c:2573

Referenced by NtfsCommitRename(), NtfsCreateDirectory(), NtfsCreateFileRecord(), and NtfsRelinkFileNames().

◆ NtfsDeleteFileRecord()

NTSTATUS NtfsDeleteFileRecord ( PDEVICE_EXTENSION  DeviceExt,
ULONGLONG  MftIndex,
BOOLEAN  CaseSensitive 
)

Definition at line 3339 of file mft.c.

3342{
3344 PFILE_RECORD_HEADER FileRecord;
3345 PNTFS_ATTR_RECORD Attribute;
3346 PNTFS_ATTR_CONTEXT AttributeContext;
3347 ULONG AttributeOffset;
3348
3349 DPRINT("NtfsDeleteFileRecord(%p, %I64u, %s)\n", DeviceExt, MftIndex, CaseSensitive ? "TRUE" : "FALSE");
3350
3351 FileRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList);
3352 if (!FileRecord)
3354
3355 Status = ReadFileRecord(DeviceExt, MftIndex, FileRecord);
3356 if (!NT_SUCCESS(Status))
3357 {
3358 DPRINT1("ERROR: Couldn't read file record %I64u to delete it!\n", MftIndex);
3359 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
3360 return Status;
3361 }
3362
3363 // Remove every name this file has from its parent's index before anything is freed, so a
3364 // failure leaves the file whole instead of an entry pointing at a released record. A file
3365 // with both a long name and an 8.3 name is indexed twice, and both parents are named by
3366 // the attributes themselves.
3367 Attribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
3368 AttributeOffset = FileRecord->AttributeOffset;
3369
3370 while (AttributeOffset < DeviceExt->NtfsInfo.BytesPerFileRecord &&
3371 Attribute->Type != AttributeEnd)
3372 {
3373 if (Attribute->Type == AttributeFileName && !Attribute->IsNonResident)
3374 {
3376 (PFILENAME_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
3377
3379 FileName->DirectoryFileReferenceNumber & NTFS_MFT_MASK,
3380 FileName,
3381 CaseSensitive);
3382 if (!NT_SUCCESS(Status))
3383 {
3384 DPRINT1("ERROR: Failed to remove '%.*S' from directory %I64u!\n",
3385 FileName->NameLength,
3386 FileName->Name,
3387 FileName->DirectoryFileReferenceNumber & NTFS_MFT_MASK);
3388 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
3389 return Status;
3390 }
3391 }
3392
3393 if (Attribute->Length == 0)
3394 break;
3395
3396 AttributeOffset += Attribute->Length;
3397 Attribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttributeOffset);
3398 }
3399
3400 // Release the clusters held by every non-resident attribute
3401 Attribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
3402 AttributeOffset = FileRecord->AttributeOffset;
3403
3404 while (AttributeOffset < DeviceExt->NtfsInfo.BytesPerFileRecord &&
3405 Attribute->Type != AttributeEnd)
3406 {
3407 if (Attribute->IsNonResident)
3408 {
3409 Status = FindAttribute(DeviceExt,
3410 FileRecord,
3411 Attribute->Type,
3412 (PCWSTR)((ULONG_PTR)Attribute + Attribute->NameOffset),
3413 Attribute->NameLength,
3414 &AttributeContext,
3415 NULL);
3416 if (NT_SUCCESS(Status))
3417 {
3418 ULONGLONG ClusterCount = Attribute->NonResident.HighestVCN - Attribute->NonResident.LowestVCN + 1;
3419
3420 Status = FreeClusters(DeviceExt, AttributeContext, AttributeOffset, FileRecord, (ULONG)ClusterCount);
3421 if (!NT_SUCCESS(Status))
3422 {
3423 DPRINT1("ERROR: Failed to free clusters of attribute 0x%lx of file %I64u!\n",
3424 Attribute->Type, MftIndex);
3425 }
3426
3427 ReleaseAttributeContext(AttributeContext);
3428 }
3429 }
3430
3431 if (Attribute->Length == 0)
3432 break;
3433
3434 AttributeOffset += Attribute->Length;
3435 Attribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttributeOffset);
3436 }
3437
3438 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
3439
3440 return FreeMftEntry(DeviceExt, MftIndex);
3441}
NTSTATUS FreeClusters(PNTFS_VCB Vcb, PNTFS_ATTR_CONTEXT AttrContext, ULONG AttrOffset, PFILE_RECORD_HEADER FileRecord, ULONG ClustersToFree)
Definition: attrib.c:1101
struct FILENAME_ATTRIBUTE * PFILENAME_ATTRIBUTE
@ AttributeFileName
Definition: ntfs.h:190
NTSTATUS NtfsRemoveFilenameFromDirectory(PDEVICE_EXTENSION DeviceExt, ULONGLONG DirectoryMftIndex, PFILENAME_ATTRIBUTE FilenameAttribute, BOOLEAN CaseSensitive)
Definition: mft.c:3166
NTSTATUS FreeMftEntry(PDEVICE_EXTENSION DeviceExt, ULONGLONG MftIndex)
Definition: mft.c:3205
USHORT AttributeOffset
Definition: ntfs.h:286
TCHAR Name[MAX_PATH]
Definition: filecomp.c:349
const uint16_t * PCWSTR
Definition: typedefs.h:57

Referenced by NtfsCleanupFile(), and NtfsClearRenameTarget().

◆ NtfsDumpData()

void NtfsDumpData ( ULONG_PTR  Buffer,
ULONG  Length 
)

Definition at line 4174 of file mft.c.

4175{
4176 ULONG i, j;
4177
4178 // dump binary data, 8 bytes at a time
4179 for (i = 0; i < Length; i += 8)
4180 {
4181 // display current offset, in hex
4182 DbgPrint("\t%03x\t", i);
4183
4184 // display hex value of each of the next 8 bytes
4185 for (j = 0; j < 8; j++)
4186 DbgPrint("%02x ", *(PUCHAR)(Buffer + i + j));
4187 DbgPrint("\n");
4188 }
4189}
Definition: bufpool.h:45
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint GLint GLint j
Definition: glfuncs.h:250
#define DbgPrint
Definition: hal.h:12
_In_ ULONG _In_ ULONG _In_ ULONG Length
Definition: ntddpcm.h:102

◆ NtfsDumpFileRecord()

VOID NtfsDumpFileRecord ( PDEVICE_EXTENSION  Vcb,
PFILE_RECORD_HEADER  FileRecord 
)

Definition at line 4211 of file mft.c.

4213{
4214 ULONG i, j;
4215
4216 // dump binary data, 8 bytes at a time
4217 for (i = 0; i < FileRecord->BytesInUse; i += 8)
4218 {
4219 // display current offset, in hex
4220 DbgPrint("\t%03x\t", i);
4221
4222 // display hex value of each of the next 8 bytes
4223 for (j = 0; j < 8; j++)
4224 DbgPrint("%02x ", *(PUCHAR)((ULONG_PTR)FileRecord + i + j));
4225 DbgPrint("\n");
4226 }
4227
4228 NtfsDumpFileAttributes(Vcb, FileRecord);
4229}

Referenced by AddFilenameToDirectoryWorker(), NtfsCreateDirectory(), and NtfsCreateFileRecord().

◆ NtfsExtendAllocation()

NTSTATUS NtfsExtendAllocation ( _In_ PDEVICE_EXTENSION  Vcb,
_In_ PNTFS_ATTR_CONTEXT  AttrContext,
_In_ ULONG  AttrOffset,
_In_ PFILE_RECORD_HEADER  FileRecord,
_In_ ULONGLONG  AllocationSize 
)

Definition at line 753 of file mft.c.

759{
761 ULONG BytesPerCluster = Vcb->NtfsInfo.BytesPerCluster;
762 PNTFS_ATTR_RECORD DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
763 ULONGLONG ExistingClusters = AttrContext->pRecord->NonResident.AllocatedSize / BytesPerCluster;
764 ULONG ClustersNeeded;
765 ULONG DesiredCluster = 0;
766 LONGLONG LastVbn;
767 LONGLONG LastLbn;
768 ULONG NextAssignedCluster;
769 ULONG AssignedClusters;
770
771 ASSERT(AttrContext->pRecord->IsNonResident);
772 ASSERT(AllocationSize % BytesPerCluster == 0);
773
774 if (AttrContext->pRecord->NonResident.AllocatedSize >= AllocationSize)
775 return STATUS_SUCCESS;
776
777 ClustersNeeded = (ULONG)(AllocationSize / BytesPerCluster - ExistingClusters);
778
779 /* Ask for the clusters right after the last one the stream owns. The
780 * tail of a sparse stream is a hole, so look at the last mapping, not
781 * at HighestVCN. */
782 if (ExistingClusters != 0 &&
783 FsRtlLookupLastLargeMcbEntry(&AttrContext->DataRunsMCB, &LastVbn, &LastLbn) &&
784 LastLbn != -1)
785 {
786 DesiredCluster = (ULONG)LastLbn + 1;
787 }
788
789 DPRINT("DesiredCluster: %lu\n", DesiredCluster);
790 DPRINT("Highest VCN of record: %I64u\n", AttrContext->pRecord->NonResident.HighestVCN);
791
792 while (ClustersNeeded > 0)
793 {
795 DesiredCluster,
796 ClustersNeeded,
797 &NextAssignedCluster,
798 &AssignedClusters);
799
800 if (!NT_SUCCESS(Status))
801 {
802 DPRINT1("Error: Unable to allocate requested clusters!\n");
803 return Status;
804 }
805
806 // now we need to add the clusters we allocated to the data run
807 Status = AddRun(Vcb, AttrContext, AttrOffset, FileRecord, NextAssignedCluster, AssignedClusters);
808 if (!NT_SUCCESS(Status))
809 {
810 DPRINT1("Error: Unable to add data run!\n");
811 return Status;
812 }
813
814 /* AddRun() places the next run after HighestVCN only once the
815 * attribute owns clusters, so count these before looping */
816 AttrContext->pRecord->NonResident.AllocatedSize += (ULONGLONG)AssignedClusters * BytesPerCluster;
817
818 ClustersNeeded -= AssignedClusters;
819 DesiredCluster = NextAssignedCluster + AssignedClusters;
820 }
821
822 DestinationAttribute->NonResident.AllocatedSize = AttrContext->pRecord->NonResident.AllocatedSize;
823 DestinationAttribute->NonResident.HighestVCN = AttrContext->pRecord->NonResident.HighestVCN;
824
825 return STATUS_SUCCESS;
826}
_In_ ULONG AllocationSize
Definition: dispmprt.h:712
NTSTATUS AddRun(PNTFS_VCB Vcb, PNTFS_ATTR_CONTEXT AttrContext, ULONG AttrOffset, PFILE_RECORD_HEADER FileRecord, ULONGLONG NextAssignedCluster, ULONG RunLength)
Definition: attrib.c:591
NTSTATUS NtfsAllocateClusters(PDEVICE_EXTENSION DeviceExt, ULONG FirstDesiredCluster, ULONG DesiredClusters, PULONG FirstAssignedCluster, PULONG AssignedClusters)
Definition: volinfo.c:213
BOOLEAN NTAPI FsRtlLookupLastLargeMcbEntry(IN PLARGE_MCB Mcb, OUT PLONGLONG Vbn, OUT PLONGLONG Lbn)
Definition: largemcb.c:718

Referenced by NtfsWriteFile(), and SetNonResidentAttributeDataLength().

◆ NtfsFindFileAt()

NTSTATUS NtfsFindFileAt ( PDEVICE_EXTENSION  Vcb,
PUNICODE_STRING  SearchPattern,
PULONG  FirstEntry,
PFILE_RECORD_HEADER *  FileRecord,
PULONGLONG  MFTIndex,
ULONGLONG  CurrentMFTIndex,
BOOLEAN  CaseSensitive 
)

Definition at line 4232 of file mft.c.

4239{
4241
4242 DPRINT("NtfsFindFileAt(%p, %wZ, %lu, %p, %p, %I64x, %s)\n",
4243 Vcb,
4244 SearchPattern,
4245 *FirstEntry,
4246 FileRecord,
4247 MFTIndex,
4248 CurrentMFTIndex,
4249 (CaseSensitive ? "TRUE" : "FALSE"));
4250
4251 Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, SearchPattern, FirstEntry, TRUE, CaseSensitive, &CurrentMFTIndex);
4252 if (!NT_SUCCESS(Status))
4253 {
4254 DPRINT("NtfsFindFileAt: NtfsFindMftRecord() failed with status 0x%08lx\n", Status);
4255 return Status;
4256 }
4257
4258 *FileRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
4259 if (*FileRecord == NULL)
4260 {
4261 DPRINT("NtfsFindFileAt: Can't allocate MFT record\n");
4263 }
4264
4265 Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
4266 if (!NT_SUCCESS(Status))
4267 {
4268 DPRINT("NtfsFindFileAt: Can't read MFT record\n");
4269 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, *FileRecord);
4270 return Status;
4271 }
4272
4273 *MFTIndex = CurrentMFTIndex;
4274
4275 return STATUS_SUCCESS;
4276}
NTSTATUS NtfsFindMftRecord(PDEVICE_EXTENSION Vcb, ULONGLONG MFTIndex, PUNICODE_STRING FileName, PULONG FirstEntry, BOOLEAN DirSearch, BOOLEAN CaseSensitive, ULONGLONG *OutMFTIndex)
Definition: mft.c:4020

Referenced by NtfsQueryDirectory().

◆ NtfsFindMftRecord()

NTSTATUS NtfsFindMftRecord ( PDEVICE_EXTENSION  Vcb,
ULONGLONG  MFTIndex,
PUNICODE_STRING  FileName,
PULONG  FirstEntry,
BOOLEAN  DirSearch,
BOOLEAN  CaseSensitive,
ULONGLONG *  OutMFTIndex 
)

Definition at line 4020 of file mft.c.

4027{
4028 PFILE_RECORD_HEADER MftRecord;
4029 PNTFS_ATTR_CONTEXT IndexRootCtx;
4030 PINDEX_ROOT_ATTRIBUTE IndexRoot;
4031 PCHAR IndexRecord;
4032 PINDEX_ENTRY_ATTRIBUTE IndexEntry, IndexEntryEnd;
4034 ULONG CurrentEntry = 0;
4035
4036 DPRINT("NtfsFindMftRecord(%p, %I64d, %wZ, %lu, %s, %s, %p)\n",
4037 Vcb,
4038 MFTIndex,
4039 FileName,
4040 *FirstEntry,
4041 DirSearch ? "TRUE" : "FALSE",
4042 CaseSensitive ? "TRUE" : "FALSE",
4043 OutMFTIndex);
4044
4045 MftRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
4046 if (MftRecord == NULL)
4047 {
4049 }
4050
4051 Status = ReadFileRecord(Vcb, MFTIndex, MftRecord);
4052 if (!NT_SUCCESS(Status))
4053 {
4054 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
4055 return Status;
4056 }
4057
4058 ASSERT(MftRecord->Ntfs.Type == NRH_FILE_TYPE);
4059 Status = FindAttribute(Vcb, MftRecord, AttributeIndexRoot, L"$I30", 4, &IndexRootCtx, NULL);
4060 if (!NT_SUCCESS(Status))
4061 {
4062 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
4063 return Status;
4064 }
4065
4066 IndexRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerIndexRecord, TAG_NTFS);
4067 if (IndexRecord == NULL)
4068 {
4069 ReleaseAttributeContext(IndexRootCtx);
4070 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
4072 }
4073
4074 ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, Vcb->NtfsInfo.BytesPerIndexRecord);
4075 IndexRoot = (PINDEX_ROOT_ATTRIBUTE)IndexRecord;
4076 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)&IndexRoot->Header + IndexRoot->Header.FirstEntryOffset);
4077 /* Index root is always resident. */
4078 /* TotalSizeOfEntries is measured from the index header, not from the
4079 * start of the attribute, and FirstEntryOffset above already is. */
4080 IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexRoot->Header + IndexRoot->Header.TotalSizeOfEntries);
4081 ReleaseAttributeContext(IndexRootCtx);
4082
4083 DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb->NtfsInfo.BytesPerIndexRecord, IndexRoot->SizeOfEntry);
4084
4086 MftRecord,
4087 (PINDEX_ROOT_ATTRIBUTE)IndexRecord,
4088 IndexRoot->SizeOfEntry,
4089 IndexEntry,
4090 IndexEntryEnd,
4091 FileName,
4092 FirstEntry,
4093 &CurrentEntry,
4094 DirSearch,
4095 CaseSensitive,
4096 OutMFTIndex);
4097
4098 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
4099 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
4100
4101 return Status;
4102}
#define NRH_FILE_TYPE
Definition: ntfs.h:277
struct INDEX_ROOT_ATTRIBUTE * PINDEX_ROOT_ATTRIBUTE
NTSTATUS BrowseIndexEntries(PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER MftRecord, PINDEX_ROOT_ATTRIBUTE IndexRecord, ULONG IndexBlockSize, PINDEX_ENTRY_ATTRIBUTE FirstEntry, PINDEX_ENTRY_ATTRIBUTE LastEntry, PUNICODE_STRING FileName, PULONG StartEntry, PULONG CurrentEntry, BOOLEAN DirSearch, BOOLEAN CaseSensitive, ULONGLONG *OutMFTIndex)
Definition: mft.c:3845
NTFS_RECORD_HEADER Ntfs
Definition: ntfs.h:283

Referenced by NtfsFindFileAt(), and NtfsLookupFileAt().

◆ NtfsIndexEntriesAreEmpty()

static NTSTATUS NtfsIndexEntriesAreEmpty ( _In_ PDEVICE_EXTENSION  Vcb,
_In_ PNTFS_ATTR_CONTEXT  IndexAllocationContext,
_In_ ULONG  IndexBlockSize,
_In_ PINDEX_HEADER_ATTRIBUTE  Header,
_In_ ULONG  Depth,
_Out_ PBOOLEAN  Empty 
)
static

Definition at line 4360 of file mft.c.

4367{
4368 PINDEX_ENTRY_ATTRIBUTE IndexEntry;
4369
4370 /* A terminator has no key, so only the fixed header is guaranteed */
4371 if (Header->FirstEntryOffset + FIELD_OFFSET(INDEX_ENTRY_ATTRIBUTE, FileName) > Header->TotalSizeOfEntries)
4373
4374 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)Header + Header->FirstEntryOffset);
4375
4376 if (Header->FirstEntryOffset + IndexEntry->Length > Header->TotalSizeOfEntries)
4378
4379 if (!(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
4380 {
4381 *Empty = FALSE;
4382 return STATUS_SUCCESS;
4383 }
4384
4385 if (IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
4386 {
4387 /* The child's VCN sits in the last 8 bytes of the entry */
4388 if (IndexEntry->Length < FIELD_OFFSET(INDEX_ENTRY_ATTRIBUTE, FileName) + sizeof(ULONGLONG))
4390
4392 IndexAllocationContext,
4393 IndexBlockSize,
4394 GetIndexEntryVCN(IndexEntry),
4395 Depth,
4396 Empty);
4397 }
4398
4399 *Empty = TRUE;
4400 return STATUS_SUCCESS;
4401}
UINT Depth
Definition: d3d12.idl:4644
@ Empty
Definition: npfs.h:126
static NTSTATUS NtfsIndexNodeIsEmpty(_In_ PDEVICE_EXTENSION Vcb, _In_ PNTFS_ATTR_CONTEXT IndexAllocationContext, _In_ ULONG IndexBlockSize, _In_ ULONGLONG Vcn, _In_ ULONG Depth, _Out_ PBOOLEAN Empty)
Definition: mft.c:4299
#define STATUS_FILE_CORRUPT_ERROR
Definition: udferr_usr.h:168

Referenced by NtfsIndexNodeIsEmpty(), and NtfsIsDirectoryEmpty().

◆ NtfsIndexNodeIsEmpty()

static NTSTATUS NtfsIndexNodeIsEmpty ( _In_ PDEVICE_EXTENSION  Vcb,
_In_ PNTFS_ATTR_CONTEXT  IndexAllocationContext,
_In_ ULONG  IndexBlockSize,
_In_ ULONGLONG  Vcn,
_In_ ULONG  Depth,
_Out_ PBOOLEAN  Empty 
)
static

Definition at line 4299 of file mft.c.

4306{
4307 PINDEX_BUFFER IndexBuffer;
4310
4311 if (IndexAllocationContext == NULL || Depth >= NTFS_MAX_INDEX_DEPTH)
4313
4314 IndexBuffer = ExAllocatePoolWithTag(NonPagedPool, IndexBlockSize, TAG_NTFS);
4315 if (IndexBuffer == NULL)
4317
4319 IndexAllocationContext,
4320 GetAllocationOffsetFromVCN(Vcb, IndexBlockSize, Vcn),
4321 (PCHAR)IndexBuffer,
4322 IndexBlockSize);
4323 if (BytesRead != IndexBlockSize || IndexBuffer->Ntfs.Type != NRH_INDX_TYPE)
4324 {
4325 DPRINT1("Unable to read index node at VCN %I64u\n", Vcn);
4326 ExFreePoolWithTag(IndexBuffer, TAG_NTFS);
4328 }
4329
4330 Status = FixupUpdateSequenceArray(Vcb, &((PFILE_RECORD_HEADER)IndexBuffer)->Ntfs);
4331 if (NT_SUCCESS(Status) &&
4332 FIELD_OFFSET(INDEX_BUFFER, Header) + IndexBuffer->Header.TotalSizeOfEntries > IndexBlockSize)
4333 {
4335 }
4336
4337 if (NT_SUCCESS(Status))
4338 {
4340 IndexAllocationContext,
4341 IndexBlockSize,
4342 &IndexBuffer->Header,
4343 Depth + 1,
4344 Empty);
4345 }
4346
4347 ExFreePoolWithTag(IndexBuffer, TAG_NTFS);
4348 return Status;
4349}
static NTSTATUS NtfsIndexEntriesAreEmpty(_In_ PDEVICE_EXTENSION Vcb, _In_ PNTFS_ATTR_CONTEXT IndexAllocationContext, _In_ ULONG IndexBlockSize, _In_ PINDEX_HEADER_ATTRIBUTE Header, _In_ ULONG Depth, _Out_ PBOOLEAN Empty)
Definition: mft.c:4360
#define NTFS_MAX_INDEX_DEPTH
Definition: mft.c:4280

Referenced by NtfsIndexEntriesAreEmpty().

◆ NtfsIsDirectoryEmpty()

NTSTATUS NtfsIsDirectoryEmpty ( _In_ PDEVICE_EXTENSION  Vcb,
_In_ ULONGLONG  MftIndex,
_Out_ PBOOLEAN  Empty 
)

Definition at line 4423 of file mft.c.

4427{
4428 PFILE_RECORD_HEADER FileRecord;
4429 PNTFS_ATTR_CONTEXT IndexRootContext = NULL;
4430 PNTFS_ATTR_CONTEXT IndexAllocationContext = NULL;
4431 PINDEX_ROOT_ATTRIBUTE IndexRoot = NULL;
4432 ULONG IndexRootLength;
4434
4435 *Empty = FALSE;
4436
4437 FileRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
4438 if (FileRecord == NULL)
4440
4441 Status = ReadFileRecord(Vcb, MftIndex, FileRecord);
4442 if (!NT_SUCCESS(Status))
4443 goto Quit;
4444
4445 Status = FindAttribute(Vcb, FileRecord, AttributeIndexRoot, L"$I30", 4, &IndexRootContext, NULL);
4446 if (!NT_SUCCESS(Status))
4447 goto Quit;
4448
4449 IndexRootLength = (ULONG)AttributeDataLength(IndexRootContext->pRecord);
4450 if (IndexRootLength < sizeof(*IndexRoot))
4451 {
4453 goto Quit;
4454 }
4455
4456 IndexRoot = ExAllocatePoolWithTag(NonPagedPool, IndexRootLength, TAG_NTFS);
4457 if (IndexRoot == NULL)
4458 {
4460 goto Quit;
4461 }
4462
4463 if (ReadAttribute(Vcb, IndexRootContext, 0, (PCHAR)IndexRoot, IndexRootLength) != IndexRootLength)
4464 {
4466 goto Quit;
4467 }
4468
4469 if (FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header) + IndexRoot->Header.TotalSizeOfEntries > IndexRootLength)
4470 {
4472 goto Quit;
4473 }
4474
4475 /* Only a root that points down needs the allocation */
4476 if (IndexRoot->Header.Flags & INDEX_ROOT_LARGE)
4477 {
4478 Status = FindAttribute(Vcb, FileRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationContext, NULL);
4479 if (!NT_SUCCESS(Status))
4480 {
4482 goto Quit;
4483 }
4484 }
4485
4487 IndexAllocationContext,
4488 IndexRoot->SizeOfEntry,
4489 &IndexRoot->Header,
4490 0,
4491 Empty);
4492
4493Quit:
4494 if (IndexRoot != NULL)
4495 ExFreePoolWithTag(IndexRoot, TAG_NTFS);
4496 if (IndexAllocationContext != NULL)
4497 ReleaseAttributeContext(IndexAllocationContext);
4498 if (IndexRootContext != NULL)
4499 ReleaseAttributeContext(IndexRootContext);
4500 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
4501
4502 return Status;
4503}

Referenced by NtfsCleanupFile(), and NtfsSetDispositionInformation().

◆ NtfsLoadUpCaseTable()

NTSTATUS NtfsLoadUpCaseTable ( PDEVICE_EXTENSION  Vcb)

Definition at line 1799 of file mft.c.

1800{
1801 return NtfsReadUpCaseTable(Vcb);
1802}
static NTSTATUS NtfsReadUpCaseTable(PDEVICE_EXTENSION Vcb)
Definition: mft.c:1735

Referenced by NtfsGetVolumeData().

◆ NtfsLookupFile()

NTSTATUS NtfsLookupFile ( PDEVICE_EXTENSION  Vcb,
PUNICODE_STRING  PathName,
BOOLEAN  CaseSensitive,
PFILE_RECORD_HEADER *  FileRecord,
PULONGLONG  MFTIndex 
)

Definition at line 4164 of file mft.c.

4169{
4170 return NtfsLookupFileAt(Vcb, PathName, CaseSensitive, FileRecord, MFTIndex, NTFS_FILE_ROOT);
4171}
#define NTFS_FILE_ROOT
Definition: ntfs.h:28
NTSTATUS NtfsLookupFileAt(PDEVICE_EXTENSION Vcb, PUNICODE_STRING PathName, BOOLEAN CaseSensitive, PFILE_RECORD_HEADER *FileRecord, PULONGLONG MFTIndex, ULONGLONG CurrentMFTIndex)
Definition: mft.c:4105

◆ NtfsLookupFileAt()

NTSTATUS NtfsLookupFileAt ( PDEVICE_EXTENSION  Vcb,
PUNICODE_STRING  PathName,
BOOLEAN  CaseSensitive,
PFILE_RECORD_HEADER *  FileRecord,
PULONGLONG  MFTIndex,
ULONGLONG  CurrentMFTIndex 
)

Definition at line 4105 of file mft.c.

4111{
4112 UNICODE_STRING Current, Remaining;
4114 ULONG FirstEntry = 0;
4115
4116 DPRINT("NtfsLookupFileAt(%p, %wZ, %s, %p, %p, %I64x)\n",
4117 Vcb,
4118 PathName,
4119 CaseSensitive ? "TRUE" : "FALSE",
4120 FileRecord,
4121 MFTIndex,
4122 CurrentMFTIndex);
4123
4124 FsRtlDissectName(*PathName, &Current, &Remaining);
4125
4126 while (Current.Length != 0)
4127 {
4128 DPRINT("Current: %wZ\n", &Current);
4129
4130 Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, &Current, &FirstEntry, FALSE, CaseSensitive, &CurrentMFTIndex);
4131 if (!NT_SUCCESS(Status))
4132 {
4133 return Status;
4134 }
4135
4136 if (Remaining.Length == 0)
4137 break;
4138
4139 /* Split what is left of the path, not the component we just resolved */
4140 FsRtlDissectName(Remaining, &Current, &Remaining);
4141 }
4142
4143 *FileRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
4144 if (*FileRecord == NULL)
4145 {
4146 DPRINT("NtfsLookupFileAt: Can't allocate MFT record\n");
4148 }
4149
4150 Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
4151 if (!NT_SUCCESS(Status))
4152 {
4153 DPRINT("NtfsLookupFileAt: Can't read MFT record\n");
4154 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, *FileRecord);
4155 return Status;
4156 }
4157
4158 *MFTIndex = CurrentMFTIndex;
4159
4160 return STATUS_SUCCESS;
4161}
VOID NTAPI FsRtlDissectName(IN UNICODE_STRING Name, OUT PUNICODE_STRING FirstPart, OUT PUNICODE_STRING RemainingPart)
Definition: name.c:147

Referenced by NtfsDirFindFile(), and NtfsLookupFile().

◆ NtfsMapAttributeRuns()

static NTSTATUS NtfsMapAttributeRuns ( PDEVICE_EXTENSION  Vcb,
PNTFS_ATTR_CONTEXT  Context,
ULONGLONG  Offset,
ULONG  Length,
PNTFS_IO_RUN_LIST  RunList,
PULONG  MappedLength 
)
static

Definition at line 1231 of file mft.c.

1237{
1238 ULONGLONG LastLCN = 0;
1239 ULONGLONG CurrentOffset = 0;
1240 PUCHAR DataRun;
1241 LONGLONG DataRunOffset;
1242 ULONGLONG DataRunLength = 0;
1243 LONGLONG DataRunStartLCN = -1;
1244 ULONG RunLength;
1245 ULONG Mapped = 0;
1246 ULONG UsedBufferSize;
1248
1249 //TEMPTEMP
1250 PUCHAR TempBuffer;
1251
1252 *MappedLength = 0;
1253
1254 // This will be rewritten in the next iteration to just use the DataRuns MCB directly
1255 TempBuffer = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
1256 if (TempBuffer == NULL)
1257 {
1258 DPRINT1("Not enough memory!\n");
1260 }
1261 RtlZeroMemory(TempBuffer, Vcb->NtfsInfo.BytesPerFileRecord);
1262
1263 ConvertLargeMCBToDataRuns(&Context->DataRunsMCB,
1264 Context->pRecord->NonResident.HighestVCN + 1,
1265 TempBuffer,
1266 Vcb->NtfsInfo.BytesPerFileRecord,
1267 &UsedBufferSize);
1268
1269 DataRun = TempBuffer;
1270
1271 // I. Walk forward to the data run that holds Offset.
1272 while (1)
1273 {
1274 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
1275 if (DataRunOffset != (LONGLONG)-1)
1276 {
1277 /* Normal data run. */
1278 DataRunStartLCN = LastLCN + DataRunOffset;
1279 LastLCN = DataRunStartLCN;
1280 }
1281 else
1282 {
1283 /* Sparse data run. */
1284 DataRunStartLCN = -1;
1285 }
1286
1287 if (Offset >= CurrentOffset &&
1288 Offset < CurrentOffset + (DataRunLength * Vcb->NtfsInfo.BytesPerCluster))
1289 {
1290 break;
1291 }
1292
1293 if (*DataRun == 0)
1294 {
1295 /* Offset is past the last cluster the attribute has allocated. */
1297 goto Cleanup;
1298 }
1299
1300 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
1301 }
1302
1303 // II. Collect the runs. The first is special only in that the request
1304 // usually starts partway into it.
1305
1306 RunLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset), Length);
1307
1308 Status = NtfsAddIoRun(RunList,
1309 (DataRunStartLCN == -1) ? NTFS_SPARSE_LBO :
1310 DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster + Offset - CurrentOffset,
1311 RunLength);
1312 if (!NT_SUCCESS(Status))
1313 goto Cleanup;
1314
1315 Length -= RunLength;
1316 Mapped += RunLength;
1317
1318 /* Did that consume the rest of this data run? */
1319 if (RunLength == DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset))
1320 {
1321 /* Never decode past the terminator; the caller gets a short map */
1322 if (*DataRun == 0)
1323 goto Cleanup;
1324
1325 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
1326 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
1327 if (DataRunOffset != (LONGLONG)-1)
1328 {
1329 DataRunStartLCN = LastLCN + DataRunOffset;
1330 LastLCN = DataRunStartLCN;
1331 }
1332 else
1333 DataRunStartLCN = -1;
1334 }
1335
1336 while (Length > 0)
1337 {
1338 RunLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster, Length);
1339
1340 Status = NtfsAddIoRun(RunList,
1341 (DataRunStartLCN == -1) ? NTFS_SPARSE_LBO :
1342 DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster,
1343 RunLength);
1344 if (!NT_SUCCESS(Status))
1345 goto Cleanup;
1346
1347 Length -= RunLength;
1348 Mapped += RunLength;
1349
1350 /* We satisfied the request, but there is still data in this run. */
1351 if (Length == 0 && RunLength != DataRunLength * Vcb->NtfsInfo.BytesPerCluster)
1352 break;
1353
1354 /* Last run the attribute has, so the caller gets a short map */
1355 if (*DataRun == 0)
1356 break;
1357
1358 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
1359 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
1360 if (DataRunOffset != (LONGLONG)-1)
1361 {
1362 /* Normal data run. */
1363 DataRunStartLCN = LastLCN + DataRunOffset;
1364 LastLCN = DataRunStartLCN;
1365 }
1366 else
1367 {
1368 /* Sparse data run. */
1369 DataRunStartLCN = -1;
1370 }
1371 }
1372
1373Cleanup:
1374 Context->CacheRun = DataRun;
1375 Context->CacheRunOffset = Offset + Mapped;
1376 Context->CacheRunStartLCN = DataRunStartLCN;
1377 Context->CacheRunLength = DataRunLength;
1378 Context->CacheRunLastLCN = LastLCN;
1379 Context->CacheRunCurrentOffset = CurrentOffset;
1380
1381 //TEMPTEMP
1382 ExFreePoolWithTag(TempBuffer, TAG_NTFS);
1383
1384 *MappedLength = Mapped;
1385
1386 return Status;
1387}
static const WCHAR Cleanup[]
Definition: register.c:80
NTSTATUS ConvertLargeMCBToDataRuns(PLARGE_MCB DataRunsMCB, ULONGLONG VcnCount, PUCHAR RunBuffer, ULONG MaxBufferSize, PULONG UsedBufferSize)
Definition: attrib.c:917
PUCHAR DecodeRun(PUCHAR DataRun, LONGLONG *DataRunOffset, ULONGLONG *DataRunLength)
Definition: attrib.c:1010
#define NTFS_SPARSE_LBO
Definition: ntfs.h:510
#define min(a, b)
Definition: monoChain.cc:55
NTSTATUS NtfsAddIoRun(IN OUT PNTFS_IO_RUN_LIST RunList, IN LONGLONG Lbo, IN ULONG ByteCount)
Definition: blockdev.c:74
#define STATUS_END_OF_FILE
Definition: shellext.h:67

Referenced by ReadAttributeToIrp(), and WriteAttributeFromIrp().

◆ NtfsReadUpCaseTable()

static NTSTATUS NtfsReadUpCaseTable ( PDEVICE_EXTENSION  Vcb)
static

Definition at line 1735 of file mft.c.

1736{
1737 PFILE_RECORD_HEADER FileRecord;
1738 PNTFS_ATTR_CONTEXT DataContext;
1741 ULONG Read;
1742
1743 FileRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
1744 if (FileRecord == NULL)
1745 {
1747 }
1748
1749 Status = ReadFileRecord(Vcb, NTFS_FILE_UPCASE, FileRecord);
1750 if (!NT_SUCCESS(Status))
1751 {
1752 DPRINT1("Couldn't read the $UpCase file record!\n");
1753 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
1754 return Status;
1755 }
1756
1757 Status = FindAttribute(Vcb, FileRecord, AttributeData, L"", 0, &DataContext, NULL);
1758 if (!NT_SUCCESS(Status))
1759 {
1760 DPRINT1("$UpCase has no data stream!\n");
1761 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
1762 return Status;
1763 }
1764
1765 Size = AttributeDataLength(DataContext->pRecord);
1766 if (Size != (0x10000 * sizeof(WCHAR)))
1767 {
1768 DPRINT1("$UpCase is %I64u bytes, expected %u\n", Size, 0x10000 * sizeof(WCHAR));
1769 ReleaseAttributeContext(DataContext);
1770 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
1772 }
1773
1775 if (Vcb->UpCaseTable == NULL)
1776 {
1777 ReleaseAttributeContext(DataContext);
1778 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
1780 }
1781
1782 Read = ReadAttribute(Vcb, DataContext, 0, (PCHAR)Vcb->UpCaseTable, (ULONG)Size);
1783
1784 ReleaseAttributeContext(DataContext);
1785 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
1786
1787 if (Read != Size)
1788 {
1789 DPRINT1("Short read of $UpCase: %lu of %I64u bytes\n", Read, Size);
1790 ExFreePoolWithTag(Vcb->UpCaseTable, TAG_NTFS);
1791 Vcb->UpCaseTable = NULL;
1792 return STATUS_UNSUCCESSFUL;
1793 }
1794
1795 return STATUS_SUCCESS;
1796}
#define NTFS_FILE_UPCASE
Definition: ntfs.h:33
@ AttributeData
Definition: ntfs.h:195
_In_ BOOLEAN Read
Definition: strmini.h:479
#define STATUS_UNRECOGNIZED_VOLUME
Definition: udferr_usr.h:173
_Must_inspect_result_ _In_ WDFDEVICE _In_ PWDF_DEVICE_PROPERTY_DATA _In_ DEVPROPTYPE _In_ ULONG Size
Definition: wdfdevice.h:4539

Referenced by NtfsLoadUpCaseTable().

◆ NtfsRemoveFilenameFromDirectory()

NTSTATUS NtfsRemoveFilenameFromDirectory ( PDEVICE_EXTENSION  DeviceExt,
ULONGLONG  DirectoryMftIndex,
PFILENAME_ATTRIBUTE  FilenameAttribute,
BOOLEAN  CaseSensitive 
)

Definition at line 3166 of file mft.c.

3170{
3172
3174 ExAcquireResourceExclusiveLite(&DeviceExt->IndexResource, TRUE);
3175 Status = RemoveFilenameFromDirectoryWorker(DeviceExt, DirectoryMftIndex, FilenameAttribute, CaseSensitive);
3176 ExReleaseResourceLite(&DeviceExt->IndexResource);
3178
3179 return Status;
3180}
static NTSTATUS RemoveFilenameFromDirectoryWorker(PDEVICE_EXTENSION DeviceExt, ULONGLONG DirectoryMftIndex, PFILENAME_ATTRIBUTE FilenameAttribute, BOOLEAN CaseSensitive)
Definition: mft.c:2965

Referenced by NtfsDeleteFileRecord(), and NtfsUnlinkFileNames().

◆ NtfsTrimAllocation()

NTSTATUS NtfsTrimAllocation ( _In_ PNTFS_FCB  Fcb)

Definition at line 842 of file mft.c.

844{
846 ULONG BytesPerCluster = Vcb->NtfsInfo.BytesPerCluster;
847 PFILE_RECORD_HEADER FileRecord;
848 PNTFS_ATTR_CONTEXT DataContext;
849 PNTFS_ATTR_RECORD DestinationAttribute;
850 ULONG AttributeOffset;
851 ULONGLONG NeededSize = 0;
853
854 FileRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
855 if (FileRecord == NULL)
857
858 /* Paging writes look the runs up again; keep them out while the tail goes */
860
861 Status = ReadFileRecord(Vcb, Fcb->MFTIndex, FileRecord);
862 if (!NT_SUCCESS(Status))
863 goto Quit;
864
865 Status = FindAttribute(Vcb, FileRecord, AttributeData, Fcb->Stream, wcslen(Fcb->Stream), &DataContext, &AttributeOffset);
866 if (!NT_SUCCESS(Status))
867 goto Quit;
868
869 if (DataContext->pRecord->IsNonResident)
870 NeededSize = ROUND_UP(DataContext->pRecord->NonResident.DataSize, BytesPerCluster);
871
872 /* Compressed and sparse streams round their allocation differently; leave them be */
873 if (DataContext->pRecord->IsNonResident &&
874 !(DataContext->pRecord->Flags & (NTFS_ATTR_COMPRESSION_MASK | NTFS_ATTR_SPARSE)) &&
875 DataContext->pRecord->NonResident.AllocatedSize > NeededSize)
876 {
878 DataContext,
879 AttributeOffset,
880 FileRecord,
881 (ULONG)((DataContext->pRecord->NonResident.AllocatedSize - NeededSize) / BytesPerCluster));
882 if (NT_SUCCESS(Status))
883 {
884 DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttributeOffset);
885 DestinationAttribute->NonResident.AllocatedSize = NeededSize;
886
887 Status = UpdateFileRecord(Vcb, Fcb->MFTIndex, FileRecord);
888 if (NT_SUCCESS(Status))
889 Fcb->RFCB.AllocationSize.QuadPart = NeededSize;
890 }
891 }
892
893 ReleaseAttributeContext(DataContext);
894
895Quit:
897 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
898
899 return Status;
900}
_In_ PFCB Fcb
Definition: cdprocs.h:159
_ACRTIMP size_t __cdecl wcslen(const wchar_t *)
Definition: wcs.c:2988
#define NTFS_ATTR_SPARSE
Definition: ntfs.h:263
#define NTFS_ATTR_COMPRESSION_MASK
Definition: ntfs.h:262
#define ROUND_UP(n, align)
Definition: eventvwr.h:34
PVCB Vcb
Definition: cdstruc.h:933
FSRTL_COMMON_FCB_HEADER RFCB
Definition: ntfs.h:594
ULONGLONG MFTIndex
Definition: ntfs.h:617
WCHAR Stream[MAX_PATH]
Definition: ntfs.h:600
ERESOURCE PagingIoResource
Definition: ntfs.h:604
LARGE_INTEGER AllocationSize
Definition: env_spec_w32.h:755

Referenced by NtfsCleanupFile().

◆ NtfsUpdateDuplicatedInformation()

NTSTATUS NtfsUpdateDuplicatedInformation ( PDEVICE_EXTENSION  Vcb,
PFILE_RECORD_HEADER  FileRecord,
ULONGLONG  MFTIndex,
ULONG  Flags,
BOOLEAN  CaseSensitive 
)

Definition at line 1864 of file mft.c.

1869{
1871 PSTANDARD_INFORMATION StdInfo;
1874 ULONGLONG ParentMFTIndex;
1875 ULONG DirFlag;
1877
1878 if (Flags == 0)
1879 return STATUS_SUCCESS;
1880
1881 StdInfo = GetStandardInformationFromRecord(Vcb, FileRecord);
1882 FileName = GetBestFileNameFromRecord(Vcb, FileRecord);
1883 if (StdInfo == NULL || FileName == NULL)
1884 {
1885 DPRINT1("Record %I64u is missing $STANDARD_INFORMATION or $FILE_NAME\n", MFTIndex);
1887 }
1888
1889 DirFlag = (FileRecord->Flags & FRH_DIRECTORY) ? NTFS_FILE_TYPE_DIRECTORY : 0;
1890
1891 RtlZeroMemory(&Update, sizeof(Update));
1892 Update.Flags = Flags;
1893
1895 {
1896 Update.CreationTime = StdInfo->CreationTime;
1897 Update.LastWriteTime = StdInfo->LastWriteTime;
1898 Update.ChangeTime = StdInfo->ChangeTime;
1899 Update.LastAccessTime = StdInfo->LastAccessTime;
1900
1901 FileName->CreationTime = StdInfo->CreationTime;
1902 FileName->LastWriteTime = StdInfo->LastWriteTime;
1903 FileName->ChangeTime = StdInfo->ChangeTime;
1904 FileName->LastAccessTime = StdInfo->LastAccessTime;
1905 }
1906
1908 {
1909 Update.FileAttributes = StdInfo->FileAttribute | DirFlag;
1910 FileName->FileAttributes = Update.FileAttributes;
1911 }
1912
1914 {
1915 /* Sizes come from the unnamed $DATA attribute, the same way the times
1916 * come from $STANDARD_INFORMATION: one source of truth per field. */
1917 Update.DataSize = NtfsGetFileSize(Vcb, FileRecord, L"", 0, &Update.AllocatedSize);
1918
1919 FileName->DataSize = Update.DataSize;
1920 FileName->AllocatedSize = Update.AllocatedSize;
1921 }
1922
1923 /* Persist the $FILE_NAME just modified. Callers have no reason to write the record again,
1924 * and leaving it unwritten would let the copy in the parent index disagree with it. */
1925 Status = UpdateFileRecord(Vcb, MFTIndex, FileRecord);
1926 if (!NT_SUCCESS(Status))
1927 {
1928 DPRINT1("Failed to write file record %I64u (Status %lx)\n", MFTIndex, Status);
1929 return Status;
1930 }
1931
1932 /* Now the parent's index entry, which carries the same values */
1933 ParentMFTIndex = FileName->DirectoryFileReferenceNumber & NTFS_MFT_MASK;
1934
1935 Name.Buffer = FileName->Name;
1936 Name.Length = FileName->NameLength * sizeof(WCHAR);
1937 Name.MaximumLength = Name.Length;
1938
1939 return UpdateFileNameRecord(Vcb, ParentMFTIndex, &Name, FALSE, &Update, CaseSensitive);
1940}
@ Update
Definition: registry.c:565
_In_ D3DDDI_VIDEO_PRESENT_TARGET_ID _In_ ULONG _In_ ULONG Flags
Definition: dispmprt.h:245
PSTANDARD_INFORMATION GetStandardInformationFromRecord(PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER FileRecord)
Definition: attrib.c:1970
PFILENAME_ATTRIBUTE GetBestFileNameFromRecord(PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER FileRecord)
Definition: attrib.c:2015
#define NTFS_FILENAME_UPDATE_SIZES
Definition: ntfs.h:1308
#define NTFS_FILENAME_UPDATE_ATTRS
Definition: ntfs.h:1310
#define FRH_DIRECTORY
Definition: ntfs.h:299
#define NTFS_FILE_TYPE_DIRECTORY
Definition: ntfs.h:259
#define NTFS_FILENAME_UPDATE_TIMES
Definition: ntfs.h:1309
NTSTATUS UpdateFileNameRecord(PDEVICE_EXTENSION Vcb, ULONGLONG ParentMFTIndex, PUNICODE_STRING FileName, BOOLEAN DirSearch, PNTFS_FILENAME_UPDATE Update, BOOLEAN CaseSensitive)
Definition: mft.c:2060
ULONGLONG NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt, PFILE_RECORD_HEADER FileRecord, PCWSTR Stream, ULONG StreamLength, PULONGLONG AllocatedSize)
Definition: dirctl.c:38
ULONGLONG LastAccessTime
Definition: ntfs.h:364
ULONGLONG ChangeTime
Definition: ntfs.h:363
ULONGLONG LastWriteTime
Definition: ntfs.h:362
ULONG FileAttribute
Definition: ntfs.h:365
ULONGLONG CreationTime
Definition: ntfs.h:361

Referenced by NtfsSetBasicInformation(), NtfsSetEndOfFile(), and NtfsWriteFile().

◆ PrepareAttributeContext()

PNTFS_ATTR_CONTEXT PrepareAttributeContext ( PNTFS_ATTR_RECORD  AttrRecord)

Definition at line 41 of file mft.c.

42{
44
45 Context = ExAllocateFromNPagedLookasideList(&NtfsGlobalData->AttrCtxtLookasideList);
46 if(!Context)
47 {
48 DPRINT1("Error: Unable to allocate memory for context!\n");
49 return NULL;
50 }
51
52 // Allocate memory for a copy of the attribute
54 if(!Context->pRecord)
55 {
56 DPRINT1("Error: Unable to allocate memory for attribute record!\n");
57 ExFreeToNPagedLookasideList(&NtfsGlobalData->AttrCtxtLookasideList, Context);
58 return NULL;
59 }
60
61 // Copy the attribute
62 RtlCopyMemory(Context->pRecord, AttrRecord, AttrRecord->Length);
63
64 if (AttrRecord->IsNonResident)
65 {
66 LONGLONG DataRunOffset;
67 ULONGLONG DataRunLength;
68 ULONGLONG NextVBN = 0;
69 PUCHAR DataRun = (PUCHAR)((ULONG_PTR)Context->pRecord + Context->pRecord->NonResident.MappingPairsOffset);
70
71 Context->CacheRun = DataRun;
72 Context->CacheRunOffset = 0;
73 Context->CacheRun = DecodeRun(Context->CacheRun, &DataRunOffset, &DataRunLength);
74 Context->CacheRunLength = DataRunLength;
75 if (DataRunOffset != -1)
76 {
77 /* Normal run. */
78 Context->CacheRunStartLCN =
79 Context->CacheRunLastLCN = DataRunOffset;
80 }
81 else
82 {
83 /* Sparse run. */
84 Context->CacheRunStartLCN = -1;
85 Context->CacheRunLastLCN = 0;
86 }
87 Context->CacheRunCurrentOffset = 0;
88
89 // Convert the data runs to a map control block
90 if (!NT_SUCCESS(ConvertDataRunsToLargeMCB(DataRun, &Context->DataRunsMCB, &NextVBN)))
91 {
92 DPRINT1("Unable to convert data runs to MCB!\n");
94 ExFreeToNPagedLookasideList(&NtfsGlobalData->AttrCtxtLookasideList, Context);
95 return NULL;
96 }
97 }
98
99 return Context;
100}
NTSTATUS ConvertDataRunsToLargeMCB(PUCHAR DataRun, PLARGE_MCB DataRunsMCB, PULONGLONG pNextVBN)
Definition: attrib.c:842
NPAGED_LOOKASIDE_LIST AttrCtxtLookasideList
Definition: ntfs.h:181

Referenced by FindAttribute(), and InternalReadNonResidentAttributes().

◆ ReadAttribute()

◆ ReadAttributeToIrp()

ULONG ReadAttributeToIrp ( PDEVICE_EXTENSION  Vcb,
PNTFS_ATTR_CONTEXT  Context,
ULONGLONG  Offset,
PCHAR  Buffer,
ULONG  Length,
PIRP  Irp 
)

Definition at line 1425 of file mft.c.

1431{
1432 ULONG AlreadyRead = 0;
1433 ULONG Transferred = 0;
1435 NTFS_IO_RUN_LIST RunList;
1436
1437 if (!Context->pRecord->IsNonResident)
1438 {
1439 // We need to truncate Offset to a ULONG for pointer arithmetic
1440 // The check below should ensure that Offset is well within the range of 32 bits
1441 ULONG LittleOffset = (ULONG)Offset;
1442
1443 // Ensure that offset isn't beyond the end of the attribute
1444 if (Offset > Context->pRecord->Resident.ValueLength)
1445 return 0;
1446 if (Offset + Length > Context->pRecord->Resident.ValueLength)
1447 Length = (ULONG)(Context->pRecord->Resident.ValueLength - Offset);
1448
1449 RtlCopyMemory(Buffer, (PVOID)((ULONG_PTR)Context->pRecord + Context->pRecord->Resident.ValueOffset + LittleOffset), Length);
1450 return Length;
1451 }
1452
1453 /*
1454 * Non-resident attribute: map the requested range onto the volume, then
1455 * issue every run it spans in a single parallel batch.
1456 */
1457
1458 NtfsInitIoRunList(&RunList);
1459
1460 Status = NtfsMapAttributeRuns(Vcb, Context, Offset, Length, &RunList, &AlreadyRead);
1461
1462 if (NT_SUCCESS(Status) && AlreadyRead != 0)
1463 {
1464 Status = NtfsPerformIrpIoRuns(Vcb->StorageDevice,
1466 Vcb->NtfsInfo.BytesPerSector,
1467 Irp,
1468 (PUCHAR)Buffer,
1469 &RunList,
1470 FALSE,
1471 &Transferred);
1472 }
1473
1474 NtfsFreeIoRunList(&RunList);
1475
1476 if (!NT_SUCCESS(Status))
1477 {
1478 /* Our callers read the return value as a byte count and treat zero as
1479 * failure, so we cannot report a partial transfer here. */
1480 DPRINT1("Read failure (Status %lx)\n", Status);
1481 return 0;
1482 }
1483
1484 if (Transferred < AlreadyRead)
1485 {
1486 DPRINT1("Short read: expected %lu bytes, got %lu\n", AlreadyRead, Transferred);
1487 AlreadyRead = Transferred;
1488 }
1489
1490 return AlreadyRead;
1491}
_In_ PIRP Irp
Definition: csq.h:116
static NTSTATUS NtfsMapAttributeRuns(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset, ULONG Length, PNTFS_IO_RUN_LIST RunList, PULONG MappedLength)
Definition: mft.c:1231
VOID NtfsFreeIoRunList(IN OUT PNTFS_IO_RUN_LIST RunList)
Definition: blockdev.c:140
VOID NtfsInitIoRunList(OUT PNTFS_IO_RUN_LIST RunList)
Definition: blockdev.c:40
NTSTATUS NtfsPerformIrpIoRuns(IN PDEVICE_OBJECT DeviceObject, IN UCHAR MajorFunction, IN ULONG SectorSize, IN PIRP Irp, IN OUT PUCHAR Buffer, IN PNTFS_IO_RUN_LIST RunList, IN BOOLEAN Override, OUT PULONG BytesTransferred)
Definition: blockdev.c:743
#define IRP_MJ_READ
Definition: rdpdr.c:46

Referenced by NtfsReadFile(), and ReadAttribute().

◆ ReadFileRecord()

NTSTATUS ReadFileRecord ( PDEVICE_EXTENSION  Vcb,
ULONGLONG  index,
PFILE_RECORD_HEADER  file 
)

Definition at line 1805 of file mft.c.

1808{
1810
1811 DPRINT("ReadFileRecord(%p, %I64x, %p)\n", Vcb, index, file);
1812
1813 BytesRead = ReadAttribute(Vcb, Vcb->MFTContext, index * Vcb->NtfsInfo.BytesPerFileRecord, (PCHAR)file, Vcb->NtfsInfo.BytesPerFileRecord);
1814 if (BytesRead != Vcb->NtfsInfo.BytesPerFileRecord)
1815 {
1816 DPRINT1("ReadFileRecord failed: %I64u read, %lu expected\n", BytesRead, Vcb->NtfsInfo.BytesPerFileRecord);
1817 return STATUS_PARTIAL_COPY;
1818 }
1819
1820 /* Apply update sequence array fixups. */
1821 DPRINT("Sequence number: %u\n", file->SequenceNumber);
1822 return FixupUpdateSequenceArray(Vcb, &file->Ntfs);
1823}
GLuint index
Definition: glext.h:6031
#define STATUS_PARTIAL_COPY
Definition: ntstatus.h:273
Definition: fci.c:123

Referenced by AddFilenameToDirectoryWorker(), FindAttribute(), FreeMftEntry(), GetNtfsFileRecord(), NtfsBuildRenamedFileName(), NtfsClearRenameTarget(), NtfsCreateFile(), NtfsDeleteFileRecord(), NtfsFindFileAt(), NtfsFindMftRecord(), NtfsGetDotEntry(), NtfsGetParentMftIndex(), NtfsGetStreamInformation(), NtfsGetVolumeData(), NtfsIsDirectoryEmpty(), NtfsLoadVolumeBitmap(), NtfsLookupFileAt(), NtfsMakeRootFCB(), NtfsMoonWalkID(), NtfsOpenFileById(), NtfsReadFCBAttribute(), NtfsReadFile(), NtfsReadUpCaseTable(), NtfsSetBasicInformation(), NtfsSetEndOfFile(), NtfsSetRenameInformation(), NtfsTrimAllocation(), NtfsWriteFile(), RemoveFilenameFromDirectoryWorker(), UpdateFileNameRecordWorker(), UpdateMftMirror(), and WriteAttributeFromIrp().

◆ ReadLCN()

NTSTATUS ReadLCN ( PDEVICE_EXTENSION  Vcb,
ULONGLONG  lcn,
ULONG  count,
PVOID  buffer 
)

Definition at line 3472 of file mft.c.

3476{
3477 LARGE_INTEGER DiskSector;
3478
3479 DiskSector.QuadPart = lcn;
3480
3481 return NtfsReadSectors(Vcb->StorageDevice,
3482 DiskSector.u.LowPart * Vcb->NtfsInfo.SectorsPerCluster,
3483 count * Vcb->NtfsInfo.SectorsPerCluster,
3484 Vcb->NtfsInfo.BytesPerSector,
3485 buffer,
3486 FALSE);
3487}
GLuint GLuint GLsizei count
Definition: gl.h:1545
GLuint buffer
Definition: glext.h:5915
NTSTATUS NtfsReadSectors(IN PDEVICE_OBJECT DeviceObject, IN ULONG DiskSector, IN ULONG SectorCount, IN ULONG SectorSize, IN OUT PUCHAR Buffer, IN BOOLEAN Override)
Definition: blockdev.c:1011
struct _LARGE_INTEGER::@2750 u

◆ ReleaseAttributeContext()

◆ RemoveFilenameFromDirectoryWorker()

static NTSTATUS RemoveFilenameFromDirectoryWorker ( PDEVICE_EXTENSION  DeviceExt,
ULONGLONG  DirectoryMftIndex,
PFILENAME_ATTRIBUTE  FilenameAttribute,
BOOLEAN  CaseSensitive 
)
static

Definition at line 2965 of file mft.c.

2969{
2971 PFILE_RECORD_HEADER ParentFileRecord;
2972 PNTFS_ATTR_CONTEXT IndexRootContext;
2973 PINDEX_ROOT_ATTRIBUTE I30IndexRoot;
2974 PINDEX_ROOT_ATTRIBUTE NewIndexRoot;
2975 ULONG IndexRootOffset;
2976 ULONGLONG I30IndexRootLength;
2977 ULONG LengthWritten;
2978 ULONG AttributeLength;
2979 ULONG BtreeIndexLength;
2980 ULONG MaxIndexRootSize;
2981 PB_TREE Tree;
2982 PB_TREE_KEY SearchKey;
2983
2984 DPRINT("NtfsRemoveFilenameFromDirectory(%p, %I64u, %p, %s)\n",
2985 DeviceExt, DirectoryMftIndex, FilenameAttribute, CaseSensitive ? "TRUE" : "FALSE");
2986
2987 ParentFileRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList);
2988 if (!ParentFileRecord)
2989 {
2990 DPRINT1("ERROR: Couldn't allocate memory for file record!\n");
2992 }
2993
2994 Status = ReadFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
2995 if (!NT_SUCCESS(Status))
2996 {
2997 DPRINT1("ERROR: Couldn't read parent directory with index %I64u\n", DirectoryMftIndex);
2998 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2999 return Status;
3000 }
3001
3002 Status = FindAttribute(DeviceExt,
3003 ParentFileRecord,
3005 L"$I30",
3006 4,
3007 &IndexRootContext,
3008 &IndexRootOffset);
3009 if (!NT_SUCCESS(Status))
3010 {
3011 DPRINT1("ERROR: Couldn't find $I30 $INDEX_ROOT attribute for parent directory with MFT #: %I64u!\n",
3012 DirectoryMftIndex);
3013 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3014 return Status;
3015 }
3016
3017 I30IndexRootLength = AttributeDataLength(IndexRootContext->pRecord);
3018 I30IndexRoot = ExAllocatePoolWithTag(NonPagedPool, I30IndexRootLength, TAG_NTFS);
3019 if (!I30IndexRoot)
3020 {
3021 DPRINT1("ERROR: Couldn't allocate memory for index root attribute!\n");
3022 ReleaseAttributeContext(IndexRootContext);
3023 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3025 }
3026
3027 Status = ReadAttribute(DeviceExt, IndexRootContext, 0, (PCHAR)I30IndexRoot, I30IndexRootLength);
3028 if (!NT_SUCCESS(Status))
3029 {
3030 DPRINT1("ERROR: Couldn't read index root attribute for Mft index #%I64u\n", DirectoryMftIndex);
3031 ReleaseAttributeContext(IndexRootContext);
3032 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
3033 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3034 return Status;
3035 }
3036
3037 Status = CreateBTreeFromIndex(DeviceExt, ParentFileRecord, IndexRootContext, I30IndexRoot, &Tree);
3038 if (!NT_SUCCESS(Status))
3039 {
3040 DPRINT1("ERROR: Failed to create B-Tree from Index!\n");
3041 ReleaseAttributeContext(IndexRootContext);
3042 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
3043 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3044 return Status;
3045 }
3046
3047 // NtfsRemoveKey() compares filenames, so the file reference here is only a placeholder
3048 SearchKey = CreateBTreeKeyFromFilename(0, FilenameAttribute);
3049 if (!SearchKey)
3050 {
3052 ReleaseAttributeContext(IndexRootContext);
3053 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
3054 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3056 }
3057
3058 Status = NtfsRemoveKey(Tree->RootNode, SearchKey, CaseSensitive);
3059
3060 DestroyBTreeKey(SearchKey);
3061
3062 if (!NT_SUCCESS(Status))
3063 {
3064 DPRINT1("ERROR: Failed to remove '%.*S' from the index of MFT record %I64u (Status %lx)\n",
3065 FilenameAttribute->NameLength,
3066 FilenameAttribute->Name,
3067 DirectoryMftIndex,
3068 Status);
3070 ReleaseAttributeContext(IndexRootContext);
3071 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
3072 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3073 return Status;
3074 }
3075
3076 // Push the tree back out to the index allocation before sizing the index root, for the same
3077 // reason the insert path does: growing or shrinking the allocation changes how much of the
3078 // file record is left for the root.
3079 Status = UpdateIndexAllocation(DeviceExt, Tree, I30IndexRoot->SizeOfEntry, ParentFileRecord);
3080 if (!NT_SUCCESS(Status))
3081 {
3082 DPRINT1("ERROR: Failed to update index allocation from B-Tree!\n");
3084 ReleaseAttributeContext(IndexRootContext);
3085 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
3086 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3087 return Status;
3088 }
3089
3090 // Find the maximum index root size given what the file record can hold
3091 MaxIndexRootSize = GetMaxIndexRootSize(DeviceExt, ParentFileRecord, IndexRootOffset, IndexRootContext->pRecord);
3092
3093 Status = CreateIndexRootFromBTree(DeviceExt, Tree, MaxIndexRootSize, &NewIndexRoot, &BtreeIndexLength);
3094 if (!NT_SUCCESS(Status))
3095 {
3096 DPRINT1("ERROR: Failed to create Index root from B-Tree!\n");
3098 ReleaseAttributeContext(IndexRootContext);
3099 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
3100 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3101 return Status;
3102 }
3103
3105
3106 // $INDEX_ROOT must always be resident, so its length is set directly rather than through
3107 // the usual attribute-resizing path.
3108 AttributeLength = NewIndexRoot->Header.AllocatedSize + FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header);
3109
3110 if (AttributeLength != IndexRootContext->pRecord->Resident.ValueLength)
3111 {
3113 IndexRootContext,
3114 ParentFileRecord,
3115 IndexRootOffset,
3116 AttributeLength);
3117 if (!NT_SUCCESS(Status))
3118 {
3119 DPRINT1("ERROR: Unable to set resident attribute length!\n");
3120 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
3121 ReleaseAttributeContext(IndexRootContext);
3122 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
3123 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3124 return Status;
3125 }
3126 }
3127
3128 NT_ASSERT(ParentFileRecord->BytesInUse <= DeviceExt->NtfsInfo.BytesPerFileRecord);
3129
3130 Status = UpdateFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
3131 if (!NT_SUCCESS(Status))
3132 {
3133 DPRINT1("ERROR: Failed to update file record of directory with index: %I64x\n", DirectoryMftIndex);
3134 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
3135 ReleaseAttributeContext(IndexRootContext);
3136 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
3137 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3138 return Status;
3139 }
3140
3141 Status = WriteAttribute(DeviceExt,
3142 IndexRootContext,
3143 0,
3144 (PUCHAR)NewIndexRoot,
3145 AttributeLength,
3146 &LengthWritten,
3147 ParentFileRecord);
3148 if (!NT_SUCCESS(Status) || LengthWritten != AttributeLength)
3149 {
3150 DPRINT1("ERROR: Unable to write new index root attribute to parent directory!\n");
3151 if (NT_SUCCESS(Status))
3153 }
3154
3155 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
3156 ReleaseAttributeContext(IndexRootContext);
3157 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
3158 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3159
3160 return Status;
3161}
CRegistryTree Tree
NTSTATUS NtfsRemoveKey(PB_TREE_FILENAME_NODE Node, PB_TREE_KEY SearchKey, BOOLEAN CaseSensitive)
Definition: btree.c:2065
VOID DestroyBTreeKey(PB_TREE_KEY Key)
Definition: btree.c:1504
PB_TREE_KEY CreateBTreeKeyFromFilename(ULONGLONG FileReference, PFILENAME_ATTRIBUTE FileNameAttribute)
Definition: btree.c:1466

Referenced by NtfsRemoveFilenameFromDirectory().

◆ SetAttributeDataLength()

NTSTATUS SetAttributeDataLength ( PFILE_OBJECT  FileObject,
PNTFS_FCB  Fcb,
PNTFS_ATTR_CONTEXT  AttrContext,
ULONG  AttrOffset,
PFILE_RECORD_HEADER  FileRecord,
PLARGE_INTEGER  DataSize 
)

@parameter FileRecord Pointer to a file record. Must be a full record at least Fcb->Vcb->NtfsInfo.BytesPerFileRecord bytes large, not just the header.

Definition at line 615 of file mft.c.

621{
623
624 DPRINT("SetAttributeDataLength(%p, %p, %p, %lu, %p, %I64u)\n",
626 Fcb,
627 AttrContext,
628 AttrOffset,
629 FileRecord,
630 DataSize->QuadPart);
631
632 // are we truncating the file?
633 if (DataSize->QuadPart < AttributeDataLength(AttrContext->pRecord))
634 {
635 if (!MmCanFileBeTruncated(FileObject->SectionObjectPointer, DataSize))
636 {
637 DPRINT1("Can't truncate a memory-mapped file!\n");
639 }
640 }
641
642 if (AttrContext->pRecord->IsNonResident)
643 {
645 AttrContext,
646 AttrOffset,
647 FileRecord,
648 DataSize);
649 }
650 else
651 {
652 // resident attribute
654 AttrContext,
655 AttrOffset,
656 FileRecord,
657 DataSize);
658 }
659
660 if (!NT_SUCCESS(Status))
661 {
662 DPRINT1("ERROR: Failed to set size of attribute!\n");
663 return Status;
664 }
665
666 //NtfsDumpFileAttributes(Fcb->Vcb, FileRecord);
667
668 // write the updated file record back to disk
669 Status = UpdateFileRecord(Fcb->Vcb, Fcb->MFTIndex, FileRecord);
670
671 if (NT_SUCCESS(Status))
672 {
673 if (AttrContext->pRecord->IsNonResident)
674 Fcb->RFCB.AllocationSize.QuadPart = AttrContext->pRecord->NonResident.AllocatedSize;
675 else
680 }
681
682 return STATUS_SUCCESS;
683}
VOID NTAPI CcSetFileSizes(IN PFILE_OBJECT FileObject, IN PCC_FILE_SIZES FileSizes)
Definition: fssup.c:356
#define STATUS_USER_MAPPED_FILE
Definition: ntstatus.h:833
BOOLEAN NTAPI MmCanFileBeTruncated(_In_ PSECTION_OBJECT_POINTERS SectionObjectPointer, _In_opt_ PLARGE_INTEGER NewFileSize)
Definition: section.c:4312
LARGE_INTEGER ValidDataLength
Definition: env_spec_w32.h:757
_In_ WDFREQUEST _In_ WDFFILEOBJECT FileObject
Definition: wdfdevice.h:550

Referenced by NtfsCreateFile(), NtfsSetEndOfFile(), and NtfsWriteFile().

◆ SetFileRecordEnd()

VOID SetFileRecordEnd ( PFILE_RECORD_HEADER  FileRecord,
PNTFS_ATTR_RECORD  AttrEnd,
ULONG  EndMarker 
)

Definition at line 706 of file mft.c.

709{
710 // Ensure AttrEnd is aligned on an 8-byte boundary, relative to FileRecord
711 ASSERT(((ULONG_PTR)AttrEnd - (ULONG_PTR)FileRecord) % ATTR_RECORD_ALIGNMENT == 0);
712
713 // mark the end of attributes
714 AttrEnd->Type = AttributeEnd;
715
716 // Restore the "file-record-end marker." The value is never checked but this behavior is consistent with Win2k3.
717 AttrEnd->Length = EndMarker;
718
719 // recalculate bytes in use
720 FileRecord->BytesInUse = (ULONG_PTR)AttrEnd - (ULONG_PTR)FileRecord + sizeof(ULONG) * 2;
721}
#define ULONG_PTR
Definition: config.h:101

Referenced by AddBitmap(), AddData(), AddFileName(), AddIndexAllocation(), AddIndexRoot(), AddRun(), AddStandardInformation(), FreeClusters(), InternalSetResidentAttributeLength(), NtfsSetFileNameAttribute(), and SetResidentAttributeDataLength().

◆ SetNonResidentAttributeDataLength()

NTSTATUS SetNonResidentAttributeDataLength ( PDEVICE_EXTENSION  Vcb,
PNTFS_ATTR_CONTEXT  AttrContext,
ULONG  AttrOffset,
PFILE_RECORD_HEADER  FileRecord,
PLARGE_INTEGER  DataSize 
)

Definition at line 937 of file mft.c.

942{
944 ULONG BytesPerCluster = Vcb->NtfsInfo.BytesPerCluster;
945 ULONGLONG AllocationSize = ROUND_UP(DataSize->QuadPart, BytesPerCluster);
946 PNTFS_ATTR_RECORD DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
947 ULONG ExistingClusters = AttrContext->pRecord->NonResident.AllocatedSize / BytesPerCluster;
948
949 ASSERT(AttrContext->pRecord->IsNonResident);
950
951 // do we need to increase the allocation size?
952 if (AttrContext->pRecord->NonResident.AllocatedSize < AllocationSize)
953 {
954 Status = NtfsExtendAllocation(Vcb, AttrContext, AttrOffset, FileRecord, AllocationSize);
955 if (!NT_SUCCESS(Status))
956 return Status;
957 }
958 else if (AttrContext->pRecord->NonResident.AllocatedSize > AllocationSize &&
959 (ULONGLONG)DataSize->QuadPart < AttrContext->pRecord->NonResident.DataSize)
960 {
961 // shrink allocation size
962 ULONG ClustersToFree = ExistingClusters - (AllocationSize / BytesPerCluster);
963 Status = FreeClusters(Vcb, AttrContext, AttrOffset, FileRecord, ClustersToFree);
964 AttrContext->pRecord->NonResident.AllocatedSize = AllocationSize;
965 }
966
967 // TODO: is the file compressed, encrypted, or sparse?
968
969 AttrContext->pRecord->NonResident.DataSize = DataSize->QuadPart;
970 AttrContext->pRecord->NonResident.InitializedSize = DataSize->QuadPart;
971
972 DestinationAttribute->NonResident.AllocatedSize = AttrContext->pRecord->NonResident.AllocatedSize;
973 DestinationAttribute->NonResident.DataSize = DataSize->QuadPart;
974 DestinationAttribute->NonResident.InitializedSize = DataSize->QuadPart;
975
976 /* AddRun() and FreeClusters() keep HighestVCN on the last VCN the runs
977 * describe, holes included, so it's carried over rather than recomputed */
978 DestinationAttribute->NonResident.HighestVCN = AttrContext->pRecord->NonResident.HighestVCN;
979
980 DPRINT("Allocated Size: %I64u\n", DestinationAttribute->NonResident.AllocatedSize);
981
982 return Status;
983}
NTSTATUS NtfsExtendAllocation(_In_ PDEVICE_EXTENSION Vcb, _In_ PNTFS_ATTR_CONTEXT AttrContext, _In_ ULONG AttrOffset, _In_ PFILE_RECORD_HEADER FileRecord, _In_ ULONGLONG AllocationSize)
Definition: mft.c:753

Referenced by AllocateIndexNode(), IncreaseMftSize(), SetAttributeDataLength(), and SetResidentAttributeDataLength().

◆ SetResidentAttributeDataLength()

NTSTATUS SetResidentAttributeDataLength ( PDEVICE_EXTENSION  Vcb,
PNTFS_ATTR_CONTEXT  AttrContext,
ULONG  AttrOffset,
PFILE_RECORD_HEADER  FileRecord,
PLARGE_INTEGER  DataSize 
)

Definition at line 1020 of file mft.c.

1025{
1027
1028 // find the next attribute
1029 ULONG NextAttributeOffset = AttrOffset + AttrContext->pRecord->Length;
1030 PNTFS_ATTR_RECORD NextAttribute = (PNTFS_ATTR_RECORD)((PCHAR)FileRecord + NextAttributeOffset);
1031
1032 ASSERT(!AttrContext->pRecord->IsNonResident);
1033
1034 //NtfsDumpFileAttributes(Vcb, FileRecord);
1035
1036 // Do we need to increase the data length?
1037 if (DataSize->QuadPart > AttrContext->pRecord->Resident.ValueLength)
1038 {
1039 // There's usually padding at the end of a record. Do we need to extend past it?
1040 ULONG MaxValueLength = AttrContext->pRecord->Length - AttrContext->pRecord->Resident.ValueOffset;
1041 if (MaxValueLength < DataSize->LowPart)
1042 {
1043 // If this is the last attribute, we could move the end marker to the very end of the file record
1044 MaxValueLength += Vcb->NtfsInfo.BytesPerFileRecord - NextAttributeOffset - (sizeof(ULONG) * 2);
1045
1046 if (MaxValueLength < DataSize->LowPart || NextAttribute->Type != AttributeEnd)
1047 {
1048 // convert attribute to non-resident
1049 PNTFS_ATTR_RECORD Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
1050 PNTFS_ATTR_RECORD NewRecord;
1051 LARGE_INTEGER AttribDataSize;
1052 PVOID AttribData;
1053 ULONG NewRecordLength;
1054 ULONG EndAttributeOffset;
1055 ULONG LengthWritten;
1056
1057 DPRINT("Converting attribute to non-resident.\n");
1058
1059 AttribDataSize.QuadPart = AttrContext->pRecord->Resident.ValueLength;
1060
1061 // Is there existing data we need to back-up?
1062 if (AttribDataSize.QuadPart > 0)
1063 {
1064 AttribData = ExAllocatePoolWithTag(NonPagedPool, AttribDataSize.QuadPart, TAG_NTFS);
1065 if (AttribData == NULL)
1066 {
1067 DPRINT1("ERROR: Couldn't allocate memory for attribute data. Can't migrate to non-resident!\n");
1069 }
1070
1071 // read data to temp buffer
1072 Status = ReadAttribute(Vcb, AttrContext, 0, AttribData, AttribDataSize.QuadPart);
1073 if (!NT_SUCCESS(Status))
1074 {
1075 DPRINT1("ERROR: Unable to read attribute before migrating!\n");
1076 ExFreePoolWithTag(AttribData, TAG_NTFS);
1077 return Status;
1078 }
1079 }
1080
1081 // Start by turning this attribute into a 0-length, non-resident attribute, then enlarge it.
1082
1083 // The size of a 0-length, non-resident attribute will be 0x41 + the size of the attribute name, aligned to an 8-byte boundary
1084 NewRecordLength = ALIGN_UP_BY(0x41 + (AttrContext->pRecord->NameLength * sizeof(WCHAR)), ATTR_RECORD_ALIGNMENT);
1085
1086 // Create a new attribute record that will store the 0-length, non-resident attribute
1087 NewRecord = ExAllocatePoolWithTag(NonPagedPool, NewRecordLength, TAG_NTFS);
1088
1089 // Zero out the NonResident structure
1090 RtlZeroMemory(NewRecord, NewRecordLength);
1091
1092 // Copy the data that's common to both non-resident and resident attributes
1093 RtlCopyMemory(NewRecord, AttrContext->pRecord, FIELD_OFFSET(NTFS_ATTR_RECORD, Resident.ValueLength));
1094
1095 // if there's a name
1096 if (AttrContext->pRecord->NameLength != 0)
1097 {
1098 // copy the name
1099 // An attribute name will be located at offset 0x18 for a resident attribute, 0x40 for non-resident
1100 RtlCopyMemory((PCHAR)((ULONG_PTR)NewRecord + 0x40),
1101 (PCHAR)((ULONG_PTR)AttrContext->pRecord + 0x18),
1102 AttrContext->pRecord->NameLength * sizeof(WCHAR));
1103 }
1104
1105 // update the mapping pairs offset, which will be 0x40 (size of a non-resident header) + length in bytes of the name
1106 NewRecord->NonResident.MappingPairsOffset = 0x40 + (AttrContext->pRecord->NameLength * sizeof(WCHAR));
1107
1108 // update the end of the file record
1109 // calculate position of end markers (1 byte for empty data run)
1110 EndAttributeOffset = AttrOffset + NewRecord->NonResident.MappingPairsOffset + 1;
1111 EndAttributeOffset = ALIGN_UP_BY(EndAttributeOffset, ATTR_RECORD_ALIGNMENT);
1112
1113 // Update the length
1114 NewRecord->Length = EndAttributeOffset - AttrOffset;
1115
1116 ASSERT(NewRecord->Length == NewRecordLength);
1117
1118 // Copy the new attribute record into the file record
1119 RtlCopyMemory(Destination, NewRecord, NewRecord->Length);
1120
1121 // Update the file record end
1122 SetFileRecordEnd(FileRecord,
1123 (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + EndAttributeOffset),
1125
1126 // Initialize the MCB, potentially catch an exception
1127 _SEH2_TRY
1128 {
1129 FsRtlInitializeLargeMcb(&AttrContext->DataRunsMCB, NonPagedPool);
1130 }
1132 {
1133 DPRINT1("Unable to create LargeMcb!\n");
1134 if (AttribDataSize.QuadPart > 0)
1135 ExFreePoolWithTag(AttribData, TAG_NTFS);
1136 ExFreePoolWithTag(NewRecord, TAG_NTFS);
1138 } _SEH2_END;
1139
1140 // Mark the attribute as non-resident (we wait until after we know the LargeMcb was initialized)
1141 NewRecord->IsNonResident = Destination->IsNonResident = 1;
1142
1143 // Update file record on disk
1144 Status = UpdateFileRecord(Vcb, AttrContext->FileMFTIndex, FileRecord);
1145 if (!NT_SUCCESS(Status))
1146 {
1147 DPRINT1("ERROR: Couldn't update file record to continue migration!\n");
1148 if (AttribDataSize.QuadPart > 0)
1149 ExFreePoolWithTag(AttribData, TAG_NTFS);
1150 ExFreePoolWithTag(NewRecord, TAG_NTFS);
1151 return Status;
1152 }
1153
1154 // Now we need to free the old copy of the attribute record in the context and replace it with the new one
1155 ExFreePoolWithTag(AttrContext->pRecord, TAG_NTFS);
1156 AttrContext->pRecord = NewRecord;
1157
1158 // Now we can treat the attribute as non-resident and enlarge it normally
1159 Status = SetNonResidentAttributeDataLength(Vcb, AttrContext, AttrOffset, FileRecord, DataSize);
1160 if (!NT_SUCCESS(Status))
1161 {
1162 DPRINT1("ERROR: Unable to migrate resident attribute!\n");
1163 if (AttribDataSize.QuadPart > 0)
1164 ExFreePoolWithTag(AttribData, TAG_NTFS);
1165 return Status;
1166 }
1167
1168 // restore the back-up attribute, if we made one
1169 if (AttribDataSize.QuadPart > 0)
1170 {
1171 Status = WriteAttribute(Vcb, AttrContext, 0, AttribData, AttribDataSize.QuadPart, &LengthWritten, FileRecord);
1172 if (!NT_SUCCESS(Status))
1173 {
1174 DPRINT1("ERROR: Unable to write attribute data to non-resident clusters during migration!\n");
1175 // TODO: Reverse migration so no data is lost
1176 ExFreePoolWithTag(AttribData, TAG_NTFS);
1177 return Status;
1178 }
1179
1180 ExFreePoolWithTag(AttribData, TAG_NTFS);
1181 }
1182 }
1183 }
1184 }
1185
1186 // set the new length of the resident attribute (if we didn't migrate it)
1187 if (!AttrContext->pRecord->IsNonResident)
1188 return InternalSetResidentAttributeLength(Vcb, AttrContext, FileRecord, AttrOffset, DataSize->LowPart);
1189
1190 return STATUS_SUCCESS;
1191}
@ Resident
Definition: data.h:23
#define EXCEPTION_EXECUTE_HANDLER
Definition: excpt.h:90
VOID NTAPI FsRtlInitializeLargeMcb(IN PLARGE_MCB Mcb, IN POOL_TYPE PoolType)
Definition: largemcb.c:451
#define _SEH2_GetExceptionCode()
Definition: pseh2_64.h:204
#define _SEH2_EXCEPT(...)
Definition: pseh2_64.h:104
#define _SEH2_END
Definition: pseh2_64.h:194
#define _SEH2_TRY
Definition: pseh2_64.h:93
#define _SEH2_YIELD(__stmt)
Definition: pseh2_64.h:207

Referenced by AllocateIndexNode(), IncreaseMftSize(), and SetAttributeDataLength().

◆ UpdateFileNameRecord()

NTSTATUS UpdateFileNameRecord ( PDEVICE_EXTENSION  Vcb,
ULONGLONG  ParentMFTIndex,
PUNICODE_STRING  FileName,
BOOLEAN  DirSearch,
PNTFS_FILENAME_UPDATE  Update,
BOOLEAN  CaseSensitive 
)

Definition at line 2060 of file mft.c.

2066{
2068
2070 ExAcquireResourceExclusiveLite(&Vcb->IndexResource, TRUE);
2071 Status = UpdateFileNameRecordWorker(Vcb, ParentMFTIndex, FileName, DirSearch, Update, CaseSensitive);
2072 ExReleaseResourceLite(&Vcb->IndexResource);
2074
2075 return Status;
2076}
static NTSTATUS UpdateFileNameRecordWorker(PDEVICE_EXTENSION Vcb, ULONGLONG ParentMFTIndex, PUNICODE_STRING FileName, BOOLEAN DirSearch, PNTFS_FILENAME_UPDATE Update, BOOLEAN CaseSensitive)
Definition: mft.c:1951

Referenced by NtfsUpdateDuplicatedInformation().

◆ UpdateFileNameRecordWorker()

static NTSTATUS UpdateFileNameRecordWorker ( PDEVICE_EXTENSION  Vcb,
ULONGLONG  ParentMFTIndex,
PUNICODE_STRING  FileName,
BOOLEAN  DirSearch,
PNTFS_FILENAME_UPDATE  Update,
BOOLEAN  CaseSensitive 
)
static

Searches a file's parent directory (given the parent's index in the mft) for the given file. Upon finding an index entry for that file, refreshes the fields of that entry's $FILE_NAME attribute named by Update->Flags.

(Most of this code was copied from NtfsFindMftRecord)

Definition at line 1951 of file mft.c.

1957{
1958 PFILE_RECORD_HEADER MftRecord;
1959 PNTFS_ATTR_CONTEXT IndexRootCtx;
1960 PINDEX_ROOT_ATTRIBUTE IndexRoot;
1961 PCHAR IndexRecord;
1962 PINDEX_ENTRY_ATTRIBUTE IndexEntry, IndexEntryEnd;
1964 ULONG CurrentEntry = 0;
1965
1966 DPRINT("UpdateFileNameRecord(%p, %I64d, %wZ, %s, %lx, %s)\n",
1967 Vcb,
1968 ParentMFTIndex,
1969 FileName,
1970 DirSearch ? "TRUE" : "FALSE",
1971 Update->Flags,
1972 CaseSensitive ? "TRUE" : "FALSE");
1973
1974 if (Update->Flags == 0)
1975 return STATUS_SUCCESS;
1976
1977 MftRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
1978 if (MftRecord == NULL)
1979 {
1981 }
1982
1983 Status = ReadFileRecord(Vcb, ParentMFTIndex, MftRecord);
1984 if (!NT_SUCCESS(Status))
1985 {
1986 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
1987 return Status;
1988 }
1989
1990 ASSERT(MftRecord->Ntfs.Type == NRH_FILE_TYPE);
1991 Status = FindAttribute(Vcb, MftRecord, AttributeIndexRoot, L"$I30", 4, &IndexRootCtx, NULL);
1992 if (!NT_SUCCESS(Status))
1993 {
1994 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
1995 return Status;
1996 }
1997
1998 IndexRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerIndexRecord, TAG_NTFS);
1999 if (IndexRecord == NULL)
2000 {
2001 ReleaseAttributeContext(IndexRootCtx);
2002 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
2004 }
2005
2006 Status = ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, AttributeDataLength(IndexRootCtx->pRecord));
2007 if (!NT_SUCCESS(Status))
2008 {
2009 DPRINT1("ERROR: Failed to read Index Root!\n");
2010 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
2011 ReleaseAttributeContext(IndexRootCtx);
2012 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
2013 return Status;
2014 }
2015
2016 IndexRoot = (PINDEX_ROOT_ATTRIBUTE)IndexRecord;
2017 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)&IndexRoot->Header + IndexRoot->Header.FirstEntryOffset);
2018 // Index root is always resident.
2019 /* TotalSizeOfEntries is measured from the index header, not from the
2020 * start of the attribute, and FirstEntryOffset above already is. */
2021 IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexRoot->Header + IndexRoot->Header.TotalSizeOfEntries);
2022
2023 DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb->NtfsInfo.BytesPerIndexRecord, IndexRoot->SizeOfEntry);
2024
2026 MftRecord,
2027 IndexRecord,
2028 IndexRoot->SizeOfEntry,
2029 IndexEntry,
2030 IndexEntryEnd,
2031 FileName,
2032 &CurrentEntry,
2033 &CurrentEntry,
2034 DirSearch,
2035 Update,
2036 CaseSensitive);
2037
2038 if (Status == STATUS_PENDING)
2039 {
2040 // we need to write the index root attribute back to disk
2041 ULONG LengthWritten;
2042 Status = WriteAttribute(Vcb, IndexRootCtx, 0, (PUCHAR)IndexRecord, AttributeDataLength(IndexRootCtx->pRecord), &LengthWritten, MftRecord);
2043 if (!NT_SUCCESS(Status))
2044 {
2045 DPRINT1("ERROR: Couldn't update Index Root!\n");
2046 }
2047
2048 }
2049
2050 ReleaseAttributeContext(IndexRootCtx);
2051 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
2052 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
2053
2054 return Status;
2055}
NTSTATUS UpdateIndexEntryFileName(PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER MftRecord, PCHAR IndexRecord, ULONG IndexBlockSize, PINDEX_ENTRY_ATTRIBUTE FirstEntry, PINDEX_ENTRY_ATTRIBUTE LastEntry, PUNICODE_STRING FileName, PULONG StartEntry, PULONG CurrentEntry, BOOLEAN DirSearch, PNTFS_FILENAME_UPDATE Update, BOOLEAN CaseSensitive)
Definition: mft.c:2084
#define STATUS_PENDING
Definition: telnetd.h:14

Referenced by UpdateFileNameRecord().

◆ UpdateFileRecord()

NTSTATUS UpdateFileRecord ( PDEVICE_EXTENSION  Vcb,
ULONGLONG  MftIndex,
PFILE_RECORD_HEADER  FileRecord 
)

Definition at line 2260 of file mft.c.

2263{
2266
2267 DPRINT("UpdateFileRecord(%p, 0x%I64x, %p)\n", Vcb, MftIndex, FileRecord);
2268
2269 // Add the fixup array to prepare the data for writing to disk
2270 AddFixupArray(Vcb, &FileRecord->Ntfs);
2271
2272 // write the file record to the master file table
2274 Vcb->MFTContext,
2275 MftIndex * Vcb->NtfsInfo.BytesPerFileRecord,
2276 (const PUCHAR)FileRecord,
2277 Vcb->NtfsInfo.BytesPerFileRecord,
2278 &BytesWritten,
2279 FileRecord);
2280
2281 if (!NT_SUCCESS(Status))
2282 {
2283 DPRINT1("UpdateFileRecord failed: %lu written, %lu expected\n", BytesWritten, Vcb->NtfsInfo.BytesPerFileRecord);
2284 }
2285
2286 // remove the fixup array (so the file record pointer can still be used)
2287 FixupUpdateSequenceArray(Vcb, &FileRecord->Ntfs);
2288
2289 return Status;
2290}
NTSTATUS AddFixupArray(PDEVICE_EXTENSION Vcb, PNTFS_RECORD_HEADER Record)
Definition: mft.c:3444
_Must_inspect_result_ _In_ WDFIOTARGET _In_opt_ WDFREQUEST _In_opt_ PWDF_MEMORY_DESCRIPTOR _In_opt_ PLONGLONG _In_opt_ PWDF_REQUEST_SEND_OPTIONS _Out_opt_ PULONG_PTR BytesWritten
Definition: wdfiotarget.h:960

Referenced by AddFilenameToDirectoryWorker(), AddNewMftEntry(), AddRun(), AllocateIndexNode(), FreeClusters(), FreeMftEntry(), IncreaseMftSize(), NtfsCommitRename(), NtfsTrimAllocation(), NtfsUpdateDuplicatedInformation(), RemoveFilenameFromDirectoryWorker(), SetAttributeDataLength(), SetResidentAttributeDataLength(), and WriteAttributeFromIrp().

◆ UpdateIndexEntryFileName()

NTSTATUS UpdateIndexEntryFileName ( PDEVICE_EXTENSION  Vcb,
PFILE_RECORD_HEADER  MftRecord,
PCHAR  IndexRecord,
ULONG  IndexBlockSize,
PINDEX_ENTRY_ATTRIBUTE  FirstEntry,
PINDEX_ENTRY_ATTRIBUTE  LastEntry,
PUNICODE_STRING  FileName,
PULONG  StartEntry,
PULONG  CurrentEntry,
BOOLEAN  DirSearch,
PNTFS_FILENAME_UPDATE  Update,
BOOLEAN  CaseSensitive 
)

Recursively searches a directory index and applies the requested update to the $FILE_NAME attribute of the proper index entry. (Heavily based on BrowseIndexEntries)

Definition at line 2084 of file mft.c.

2096{
2098 ULONG RecordOffset;
2099 PINDEX_ENTRY_ATTRIBUTE IndexEntry;
2100 PNTFS_ATTR_CONTEXT IndexAllocationCtx;
2101 ULONGLONG IndexAllocationSize;
2102 PINDEX_BUFFER IndexBuffer;
2103
2104 DPRINT("UpdateIndexEntryFileName(%p, %p, %p, %lu, %p, %p, %wZ, %lu, %lu, %s, %lx, %s)\n",
2105 Vcb,
2106 MftRecord,
2107 IndexRecord,
2108 IndexBlockSize,
2109 FirstEntry,
2110 LastEntry,
2111 FileName,
2112 *StartEntry,
2113 *CurrentEntry,
2114 DirSearch ? "TRUE" : "FALSE",
2115 Update->Flags,
2116 CaseSensitive ? "TRUE" : "FALSE");
2117
2118 // find the index entry responsible for the file we're trying to update
2119 IndexEntry = FirstEntry;
2120 while (IndexEntry < LastEntry &&
2121 !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
2122 {
2123 if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) > NTFS_FILE_FIRST_USER_FILE &&
2124 *CurrentEntry >= *StartEntry &&
2125 IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS &&
2126 CompareFileName(Vcb, FileName, IndexEntry, DirSearch, CaseSensitive))
2127 {
2128 *StartEntry = *CurrentEntry;
2129
2131 {
2132 IndexEntry->FileName.DataSize = Update->DataSize;
2133 IndexEntry->FileName.AllocatedSize = Update->AllocatedSize;
2134 }
2135
2137 {
2138 IndexEntry->FileName.CreationTime = Update->CreationTime;
2139 IndexEntry->FileName.ChangeTime = Update->ChangeTime;
2140 IndexEntry->FileName.LastWriteTime = Update->LastWriteTime;
2141 IndexEntry->FileName.LastAccessTime = Update->LastAccessTime;
2142 }
2143
2145 {
2146 IndexEntry->FileName.FileAttributes = Update->FileAttributes;
2147 }
2148
2149 // indicate that the caller will still need to write the structure to the disk
2150 return STATUS_PENDING;
2151 }
2152
2153 (*CurrentEntry) += 1;
2154 ASSERT(IndexEntry->Length >= sizeof(INDEX_ENTRY_ATTRIBUTE));
2155 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
2156 }
2157
2158 /* If we're already browsing a subnode */
2159 if (IndexRecord == NULL)
2160 {
2162 }
2163
2164 /* If there's no subnode */
2165 if (!(IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE))
2166 {
2168 }
2169
2170 Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationCtx, NULL);
2171 if (!NT_SUCCESS(Status))
2172 {
2173 DPRINT("Corrupted filesystem!\n");
2174 return Status;
2175 }
2176
2177 IndexAllocationSize = AttributeDataLength(IndexAllocationCtx->pRecord);
2179 for (RecordOffset = 0; RecordOffset < IndexAllocationSize; RecordOffset += IndexBlockSize)
2180 {
2181 ReadAttribute(Vcb, IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize);
2182 Status = FixupUpdateSequenceArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs);
2183 if (!NT_SUCCESS(Status))
2184 {
2185 break;
2186 }
2187
2188 IndexBuffer = (PINDEX_BUFFER)IndexRecord;
2189 ASSERT(IndexBuffer->Ntfs.Type == NRH_INDX_TYPE);
2190 ASSERT(IndexBuffer->Header.AllocatedSize + FIELD_OFFSET(INDEX_BUFFER, Header) == IndexBlockSize);
2191 FirstEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.FirstEntryOffset);
2192 LastEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.TotalSizeOfEntries);
2193 ASSERT(LastEntry <= (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexBuffer + IndexBlockSize));
2194
2196 NULL,
2197 NULL,
2198 0,
2199 FirstEntry,
2200 LastEntry,
2201 FileName,
2202 StartEntry,
2203 CurrentEntry,
2204 DirSearch,
2205 Update,
2206 CaseSensitive);
2207 if (Status == STATUS_PENDING)
2208 {
2209 // write the index record back to disk
2210 ULONG Written;
2211
2212 // first we need to update the fixup values for the index block
2213 Status = AddFixupArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs);
2214 if (!NT_SUCCESS(Status))
2215 {
2216 DPRINT1("Error: Failed to update fixup sequence array!\n");
2217 break;
2218 }
2219
2220 Status = WriteAttribute(Vcb, IndexAllocationCtx, RecordOffset, (const PUCHAR)IndexRecord, IndexBlockSize, &Written, MftRecord);
2221 if (!NT_SUCCESS(Status))
2222 {
2223 DPRINT1("ERROR Performing write!\n");
2224 break;
2225 }
2226
2228 break;
2229 }
2230 if (NT_SUCCESS(Status))
2231 {
2232 break;
2233 }
2234 }
2235
2236 ReleaseAttributeContext(IndexAllocationCtx);
2237 return Status;
2238}
struct INDEX_BUFFER * PINDEX_BUFFER
ULONGLONG LastWriteTime
Definition: ntfs.h:393
ULONGLONG DataSize
Definition: ntfs.h:397
ULONGLONG AllocatedSize
Definition: ntfs.h:396
ULONG FileAttributes
Definition: ntfs.h:398
ULONGLONG LastAccessTime
Definition: ntfs.h:395
ULONGLONG CreationTime
Definition: ntfs.h:392
ULONGLONG ChangeTime
Definition: ntfs.h:394

Referenced by UpdateFileNameRecordWorker(), and UpdateIndexEntryFileName().

◆ UpdateMftMirror()

NTSTATUS UpdateMftMirror ( PNTFS_VCB  Vcb)

Definition at line 3559 of file mft.c.

3560{
3561 PFILE_RECORD_HEADER MirrorFileRecord;
3562 PNTFS_ATTR_CONTEXT MirrDataContext;
3563 PNTFS_ATTR_CONTEXT MftDataContext;
3564 PCHAR DataBuffer;
3568 ULONG LengthWritten;
3569
3570 // Allocate memory for the Mft mirror file record
3571 MirrorFileRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
3572 if (!MirrorFileRecord)
3573 {
3574 DPRINT1("Error: Failed to allocate memory for $MFTMirr!\n");
3576 }
3577
3578 // Read the Mft Mirror file record
3579 Status = ReadFileRecord(Vcb, NTFS_FILE_MFTMIRR, MirrorFileRecord);
3580 if (!NT_SUCCESS(Status))
3581 {
3582 DPRINT1("ERROR: Failed to read $MFTMirr!\n");
3583 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MirrorFileRecord);
3584 return Status;
3585 }
3586
3587 // Find the $DATA attribute of $MFTMirr
3588 Status = FindAttribute(Vcb, MirrorFileRecord, AttributeData, L"", 0, &MirrDataContext, NULL);
3589 if (!NT_SUCCESS(Status))
3590 {
3591 DPRINT1("ERROR: Couldn't find $DATA attribute!\n");
3592 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MirrorFileRecord);
3593 return Status;
3594 }
3595
3596 // Find the $DATA attribute of $MFT
3597 Status = FindAttribute(Vcb, Vcb->MasterFileTable, AttributeData, L"", 0, &MftDataContext, NULL);
3598 if (!NT_SUCCESS(Status))
3599 {
3600 DPRINT1("ERROR: Couldn't find $DATA attribute!\n");
3601 ReleaseAttributeContext(MirrDataContext);
3602 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MirrorFileRecord);
3603 return Status;
3604 }
3605
3606 // Get the size of the mirror's $DATA attribute
3607 DataLength = AttributeDataLength(MirrDataContext->pRecord);
3608
3609 ASSERT(DataLength % Vcb->NtfsInfo.BytesPerFileRecord == 0);
3610
3611 // Create buffer for the mirror's $DATA attribute
3613 if (!DataBuffer)
3614 {
3615 DPRINT1("Error: Couldn't allocate memory for $DATA buffer!\n");
3616 ReleaseAttributeContext(MftDataContext);
3617 ReleaseAttributeContext(MirrDataContext);
3618 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MirrorFileRecord);
3620 }
3621
3623
3624 // Back up the first several entries of the Mft's $DATA Attribute
3625 BytesRead = ReadAttribute(Vcb, MftDataContext, 0, DataBuffer, (ULONG)DataLength);
3626 if (BytesRead != (ULONG)DataLength)
3627 {
3628 DPRINT1("Error: Failed to read $DATA for $MFTMirr!\n");
3629 ReleaseAttributeContext(MftDataContext);
3630 ReleaseAttributeContext(MirrDataContext);
3631 ExFreePoolWithTag(DataBuffer, TAG_NTFS);
3632 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MirrorFileRecord);
3633 return STATUS_UNSUCCESSFUL;
3634 }
3635
3636 // Write the mirror's $DATA attribute
3638 MirrDataContext,
3639 0,
3640 (PUCHAR)DataBuffer,
3641 DataLength,
3642 &LengthWritten,
3643 MirrorFileRecord);
3644 if (!NT_SUCCESS(Status))
3645 {
3646 DPRINT1("ERROR: Failed to write $DATA attribute of $MFTMirr!\n");
3647 }
3648
3649 // Cleanup
3650 ReleaseAttributeContext(MftDataContext);
3651 ReleaseAttributeContext(MirrDataContext);
3652 ExFreePoolWithTag(DataBuffer, TAG_NTFS);
3653 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MirrorFileRecord);
3654
3655 return Status;
3656}
#define NTFS_FILE_MFTMIRR
Definition: ntfs.h:24
_In_ D3DDDI_VIDEO_PRESENT_TARGET_ID _In_ ULONG _In_ ULONG DataLength
Definition: dispmprt.h:233
#define ULONG_MAX
Definition: limits.h:31

Referenced by IncreaseMftSize().

◆ WriteAttribute()

NTSTATUS WriteAttribute ( PDEVICE_EXTENSION  Vcb,
PNTFS_ATTR_CONTEXT  Context,
ULONGLONG  Offset,
const PUCHAR  Buffer,
ULONG  Length,
PULONG  RealLengthWritten,
PFILE_RECORD_HEADER  FileRecord 
)

Definition at line 1698 of file mft.c.

1705{
1707 Context,
1708 Offset,
1709 Buffer,
1710 Length,
1711 RealLengthWritten,
1712 FileRecord,
1713 NULL);
1714}
NTSTATUS WriteAttributeFromIrp(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset, const PUCHAR Buffer, ULONG Length, PULONG RealLengthWritten, PFILE_RECORD_HEADER FileRecord, PIRP Irp)
Definition: mft.c:1546

Referenced by AddFilenameToDirectoryWorker(), AddNewMftEntry(), AllocateIndexNode(), FreeMftEntry(), IncreaseMftSize(), NtfsWriteVolumeBitmap(), RemoveFilenameFromDirectoryWorker(), SetResidentAttributeDataLength(), UpdateFileNameRecordWorker(), UpdateFileRecord(), UpdateIndexEntryFileName(), UpdateIndexNode(), and UpdateMftMirror().

◆ WriteAttributeFromIrp()

NTSTATUS WriteAttributeFromIrp ( PDEVICE_EXTENSION  Vcb,
PNTFS_ATTR_CONTEXT  Context,
ULONGLONG  Offset,
const PUCHAR  Buffer,
ULONG  Length,
PULONG  RealLengthWritten,
PFILE_RECORD_HEADER  FileRecord,
PIRP  Irp 
)

Definition at line 1546 of file mft.c.

1554{
1555 ULONG MappedLength = 0;
1556 ULONG Transferred = 0;
1558 BOOLEAN FileRecordAllocated = FALSE;
1559 NTFS_IO_RUN_LIST RunList;
1560
1561
1562 DPRINT("WriteAttribute(%p, %p, %I64u, %p, %lu, %p, %p)\n", Vcb, Context, Offset, Buffer, Length, RealLengthWritten, FileRecord);
1563
1564 *RealLengthWritten = 0;
1565 NtfsInitIoRunList(&RunList);
1566
1567 // is this a resident attribute?
1568 if (!Context->pRecord->IsNonResident)
1569 {
1570 ULONG AttributeOffset;
1571 PNTFS_ATTR_CONTEXT FoundContext;
1573
1574 // Ensure requested data is within the bounds of the attribute
1575 ASSERT(Offset + Length <= Context->pRecord->Resident.ValueLength);
1576
1577 if (Offset + Length > Context->pRecord->Resident.ValueLength)
1578 {
1579 DPRINT1("DRIVER ERROR: Attribute is too small!\n");
1581 }
1582
1583 // Do we need to read the file record?
1584 if (FileRecord == NULL)
1585 {
1586 FileRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
1587 if (!FileRecord)
1588 {
1589 DPRINT1("Error: Couldn't allocate file record!\n");
1590 return STATUS_NO_MEMORY;
1591 }
1592
1593 FileRecordAllocated = TRUE;
1594
1595 // read the file record
1596 ReadFileRecord(Vcb, Context->FileMFTIndex, FileRecord);
1597 }
1598
1599 // find where to write the attribute data to
1600 Status = FindAttribute(Vcb, FileRecord,
1601 Context->pRecord->Type,
1602 (PCWSTR)((ULONG_PTR)Context->pRecord + Context->pRecord->NameOffset),
1603 Context->pRecord->NameLength,
1604 &FoundContext,
1605 &AttributeOffset);
1606
1607 if (!NT_SUCCESS(Status))
1608 {
1609 DPRINT1("ERROR: Couldn't find matching attribute!\n");
1610 if(FileRecordAllocated)
1611 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
1612 return Status;
1613 }
1614
1615 Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttributeOffset);
1616
1617 DPRINT("Offset: %I64u, AttributeOffset: %u, ValueOffset: %u\n", Offset, AttributeOffset, Context->pRecord->Resident.ValueLength);
1618
1619 // Will we be writing past the end of the allocated file record?
1620 if (Offset + Length + AttributeOffset + Context->pRecord->Resident.ValueOffset > Vcb->NtfsInfo.BytesPerFileRecord)
1621 {
1622 DPRINT1("DRIVER ERROR: Data being written extends past end of file record!\n");
1623 ReleaseAttributeContext(FoundContext);
1624 if (FileRecordAllocated)
1625 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
1627 }
1628
1629 // copy the data being written into the file record. We cast Offset to ULONG, which is safe because it's range has been verified.
1630 RtlCopyMemory((PCHAR)((ULONG_PTR)Destination + Context->pRecord->Resident.ValueOffset + (ULONG)Offset), Buffer, Length);
1631
1632 Status = UpdateFileRecord(Vcb, Context->FileMFTIndex, FileRecord);
1633
1634 // Update the context's copy of the resident attribute
1635 ASSERT(Context->pRecord->Length == Destination->Length);
1636 RtlCopyMemory((PVOID)Context->pRecord, Destination, Context->pRecord->Length);
1637
1638 ReleaseAttributeContext(FoundContext);
1639 if (FileRecordAllocated)
1640 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
1641
1642 if (NT_SUCCESS(Status))
1643 *RealLengthWritten = Length;
1644
1645 return Status;
1646 }
1647
1648 // This is a non-resident attribute: map the requested range onto the
1649 // volume, then issue every run it spans in a single parallel batch.
1650
1651 Status = NtfsMapAttributeRuns(Vcb, Context, Offset, Length, &RunList, &MappedLength);
1652
1653 if (NT_SUCCESS(Status) && MappedLength < Length)
1654 {
1655 // The attribute's allocation ends before the request does.
1656 // TODO: assign new clusters to the end of the file.
1657 // [We can reach here by creating a new file record when the MFT isn't large enough]
1658 DPRINT1("FIXME: Attribute needs to be enlarged (mapped %lu of %lu bytes).\n",
1659 MappedLength, Length);
1661 }
1662
1663 if (NT_SUCCESS(Status))
1664 {
1665 // Writing into a hole would mean allocating clusters, which we cannot
1666 // do here; NtfsPerformIrpIoRuns() rejects sparse runs on IRP_MJ_WRITE.
1667 Status = NtfsPerformIrpIoRuns(Vcb->StorageDevice,
1669 Vcb->NtfsInfo.BytesPerSector,
1670 Irp,
1671 Buffer,
1672 &RunList,
1673 FALSE,
1674 &Transferred);
1675 }
1676
1677 NtfsFreeIoRunList(&RunList);
1678
1679 if (!NT_SUCCESS(Status))
1680 {
1681 DPRINT1("Write failure (Status %lx)\n", Status);
1682 *RealLengthWritten = 0;
1683 return Status;
1684 }
1685
1686 *RealLengthWritten = Transferred;
1687
1688 if (Transferred < Length)
1689 {
1690 DPRINT1("Short write: expected %lu bytes, wrote %lu\n", Length, Transferred);
1692 }
1693
1694 return Status;
1695}
#define STATUS_NO_MEMORY
Definition: d3dkmdt.h:51
#define STATUS_UNEXPECTED_IO_ERROR
Definition: ntstatus.h:563
#define IRP_MJ_WRITE
Definition: rdpdr.c:47

Referenced by NtfsWriteFile(), and WriteAttribute().