ReactOS 0.4.15-dev-6712-g46b4b55
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)
 
SetNonResidentAttributeDataLength

@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 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.
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)
 
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 WriteAttribute (PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset, const PUCHAR Buffer, ULONG Length, PULONG RealLengthWritten, PFILE_RECORD_HEADER FileRecord)
 
NTSTATUS ReadFileRecord (PDEVICE_EXTENSION Vcb, ULONGLONG index, PFILE_RECORD_HEADER file)
 
NTSTATUS UpdateFileNameRecord (PDEVICE_EXTENSION Vcb, ULONGLONG ParentMFTIndex, PUNICODE_STRING FileName, BOOLEAN DirSearch, ULONGLONG NewDataSize, ULONGLONG NewAllocationSize, BOOLEAN CaseSensitive)
 
NTSTATUS UpdateIndexEntryFileNameSize (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, ULONGLONG NewDataSize, ULONGLONG NewAllocatedSize, 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)
 
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.
NTSTATUS NtfsAddFilenameToDirectory (PDEVICE_EXTENSION DeviceExt, ULONGLONG DirectoryMftIndex, ULONGLONG FileReferenceNumber, PFILENAME_ATTRIBUTE FilenameAttribute, BOOLEAN CaseSensitive)
 
NTSTATUS AddFixupArray (PDEVICE_EXTENSION Vcb, PNTFS_RECORD_HEADER Record)
 
NTSTATUS ReadLCN (PDEVICE_EXTENSION Vcb, ULONGLONG lcn, ULONG count, PVOID buffer)
 
BOOLEAN CompareFileName (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)
 
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.
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)
 

Macro Definition Documentation

◆ NDEBUG

#define NDEBUG

Definition at line 35 of file mft.c.

Function Documentation

◆ AddFixupArray()

NTSTATUS AddFixupArray ( PDEVICE_EXTENSION  Vcb,
PNTFS_RECORD_HEADER  Record 
)

Definition at line 2603 of file mft.c.

2605{
2606 USHORT *pShortToFixUp;
2607 ULONG ArrayEntryCount = Record->UsaCount - 1;
2608 ULONG Offset = Vcb->NtfsInfo.BytesPerSector - 2;
2609 ULONG i;
2610
2611 PFIXUP_ARRAY fixupArray = (PFIXUP_ARRAY)((UCHAR*)Record + Record->UsaOffset);
2612
2613 DPRINT("AddFixupArray(%p, %p)\n fixupArray->USN: %u, ArrayEntryCount: %u\n", Vcb, Record, fixupArray->USN, ArrayEntryCount);
2614
2615 fixupArray->USN++;
2616
2617 for (i = 0; i < ArrayEntryCount; i++)
2618 {
2619 DPRINT("USN: %u\tOffset: %u\n", fixupArray->USN, Offset);
2620
2621 pShortToFixUp = (USHORT*)((PCHAR)Record + Offset);
2622 fixupArray->Array[i] = *pShortToFixUp;
2623 *pShortToFixUp = fixupArray->USN;
2624 Offset += Vcb->NtfsInfo.BytesPerSector;
2625 }
2626
2627 return STATUS_SUCCESS;
2628}
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
#define STATUS_SUCCESS
Definition: shellext.h:65
#define DPRINT
Definition: sndvol32.h:71
USHORT Array[]
Definition: ntfs.h:562
USHORT USN
Definition: ntfs.h:561
uint32_t ULONG
Definition: typedefs.h:59
char * PCHAR
Definition: typedefs.h:51
_In_ struct _KBUGCHECK_REASON_CALLBACK_RECORD * Record
Definition: ketypes.h:268
unsigned char UCHAR
Definition: xmlstorage.h:181

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

◆ AddNewMftEntry()

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

Definition at line 2022 of file mft.c.

2026{
2028 ULONGLONG MftIndex;
2030 ULONGLONG BitmapDataSize;
2031 ULONGLONG AttrBytesRead;
2034 ULONG LengthWritten;
2035 PNTFS_ATTR_CONTEXT BitmapContext;
2036 LARGE_INTEGER BitmapBits;
2037 UCHAR SystemReservedBits;
2038
2039 DPRINT1("AddNewMftEntry(%p, %p, %p, %s)\n", FileRecord, DeviceExt, DestinationIndex, CanWait ? "TRUE" : "FALSE");
2040
2041 // First, we have to read the mft's $Bitmap attribute
2042
2043 // Find the attribute
2044 Status = FindAttribute(DeviceExt, DeviceExt->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, NULL);
2045 if (!NT_SUCCESS(Status))
2046 {
2047 DPRINT1("ERROR: Couldn't find $Bitmap attribute of master file table!\n");
2048 return Status;
2049 }
2050
2051 // Get size of bitmap
2052 BitmapDataSize = AttributeDataLength(BitmapContext->pRecord);
2053
2054 // RtlInitializeBitmap wants a ULONG-aligned pointer, and wants the memory passed to it to be a ULONG-multiple
2055 // Allocate a buffer for the $Bitmap attribute plus enough to ensure we can get a ULONG-aligned pointer
2056 BitmapBuffer = ExAllocatePoolWithTag(NonPagedPool, BitmapDataSize + sizeof(ULONG), TAG_NTFS);
2057 if (!BitmapBuffer)
2058 {
2059 ReleaseAttributeContext(BitmapContext);
2061 }
2062 RtlZeroMemory(BitmapBuffer, BitmapDataSize + sizeof(ULONG));
2063
2064 // Get a ULONG-aligned pointer for the bitmap itself
2066
2067 // read $Bitmap attribute
2068 AttrBytesRead = ReadAttribute(DeviceExt, BitmapContext, 0, (PCHAR)BitmapData, BitmapDataSize);
2069
2070 if (AttrBytesRead != BitmapDataSize)
2071 {
2072 DPRINT1("ERROR: Unable to read $Bitmap attribute of master file table!\n");
2074 ReleaseAttributeContext(BitmapContext);
2076 }
2077
2078 // We need to backup the bits for records 0x10 - 0x17 (3rd byte of bitmap) and mark these records
2079 // as in-use so we don't assign files to those indices. They're reserved for the system (e.g. ChkDsk).
2080 SystemReservedBits = BitmapData[2];
2081 BitmapData[2] = 0xff;
2082
2083 // Calculate bit count
2084 BitmapBits.QuadPart = AttributeDataLength(DeviceExt->MFTContext->pRecord) /
2085 DeviceExt->NtfsInfo.BytesPerFileRecord;
2086 if (BitmapBits.HighPart != 0)
2087 {
2088 DPRINT1("\tFIXME: bitmap sizes beyond 32bits are not yet supported! (Your NTFS volume is too large)\n");
2091 ReleaseAttributeContext(BitmapContext);
2093 }
2094
2095 // convert buffer into bitmap
2097
2098 // set next available bit, preferrably after 23rd bit
2099 MftIndex = RtlFindClearBitsAndSet(&Bitmap, 1, 24);
2100 if ((LONG)MftIndex == -1)
2101 {
2102 DPRINT1("Couldn't find free space in MFT for file record, increasing MFT size.\n");
2103
2105 ReleaseAttributeContext(BitmapContext);
2106
2107 // Couldn't find a free record in the MFT, add some blank records and try again
2108 Status = IncreaseMftSize(DeviceExt, CanWait);
2109 if (!NT_SUCCESS(Status))
2110 {
2111 DPRINT1("ERROR: Couldn't find space in MFT for file or increase MFT size!\n");
2112 return Status;
2113 }
2114
2115 return AddNewMftEntry(FileRecord, DeviceExt, DestinationIndex, CanWait);
2116 }
2117
2118 DPRINT1("Creating file record at MFT index: %I64u\n", MftIndex);
2119
2120 // update file record with index
2121 FileRecord->MFTRecordNumber = MftIndex;
2122
2123 // [BitmapData should have been updated via RtlFindClearBitsAndSet()]
2124
2125 // Restore the system reserved bits
2126 BitmapData[2] = SystemReservedBits;
2127
2128 // write the bitmap back to the MFT's $Bitmap attribute
2129 Status = WriteAttribute(DeviceExt, BitmapContext, 0, BitmapData, BitmapDataSize, &LengthWritten, FileRecord);
2130 if (!NT_SUCCESS(Status))
2131 {
2132 DPRINT1("ERROR encountered when writing $Bitmap attribute!\n");
2134 ReleaseAttributeContext(BitmapContext);
2135 return Status;
2136 }
2137
2138 // update the file record (write it to disk)
2139 Status = UpdateFileRecord(DeviceExt, MftIndex, FileRecord);
2140
2141 if (!NT_SUCCESS(Status))
2142 {
2143 DPRINT1("ERROR: Unable to write file record!\n");
2145 ReleaseAttributeContext(BitmapContext);
2146 return Status;
2147 }
2148
2149 *DestinationIndex = MftIndex;
2150
2152 ReleaseAttributeContext(BitmapContext);
2153
2154 return Status;
2155}
#define ALIGN_UP_BY(size, align)
LONG NTSTATUS
Definition: precomp.h:26
#define DPRINT1
Definition: precomp.h:8
#define NULL
Definition: types.h:112
#define FALSE
Definition: types.h:117
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:32
PNTFS_GLOBAL_DATA NtfsGlobalData
Definition: ntfs.c:36
@ AttributeBitmap
Definition: ntfs.h:171
#define TAG_NTFS
Definition: ntfs.h:12
#define ExAllocatePoolWithTag(hernya, size, tag)
Definition: env_spec_w32.h:350
#define NonPagedPool
Definition: env_spec_w32.h:307
Status
Definition: gdiplustypes.h:25
static ULONG BitmapBuffer[(XMS_BLOCKS+31)/32]
Definition: himem.c:86
NTSYSAPI void WINAPI RtlInitializeBitMap(PRTL_BITMAP, PULONG, ULONG)
NTSYSAPI ULONG WINAPI RtlFindClearBitsAndSet(PRTL_BITMAP, ULONG, ULONG)
NTSTATUS UpdateFileRecord(PDEVICE_EXTENSION Vcb, ULONGLONG MftIndex, PFILE_RECORD_HEADER FileRecord)
Definition: mft.c:1931
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:1065
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:1315
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:2022
#define ExFreePoolWithTag(_P, _T)
Definition: module.h:1109
#define STATUS_NOT_IMPLEMENTED
Definition: ntstatus.h:239
#define L(x)
Definition: ntvdm.h:50
long LONG
Definition: pedump.c:60
BOOLEAN EnableWriteSupport
Definition: ntfs.h:155
ULONG MFTRecordNumber
Definition: ntfs.h:262
uint32_t * PULONG
Definition: typedefs.h:59
#define RtlZeroMemory(Destination, Length)
Definition: typedefs.h:262
uint32_t ULONG_PTR
Definition: typedefs.h:65
unsigned char * PUCHAR
Definition: typedefs.h:53
uint64_t ULONGLONG
Definition: typedefs.h:67
#define STATUS_INSUFFICIENT_RESOURCES
Definition: udferr_usr.h:158
#define STATUS_OBJECT_NAME_NOT_FOUND
Definition: udferr_usr.h:149
LONGLONG QuadPart
Definition: typedefs.h:114
ULONG LowPart
Definition: typedefs.h:106

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}
#define ATTR_RECORD_ALIGNMENT
Definition: ntfs.h:320
struct NTFS_ATTR_RECORD::@166::@168 Resident
UCHAR IsNonResident
Definition: ntfs.h:128
struct NTFS_ATTR_RECORD::@166::@169 NonResident

Referenced by NtfsGetFileSize(), NtfsGetSteamInformation(), NtfsReadFile(), NtfsSetEndOfFile(), and NtfsWriteFile().

◆ 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 2984 of file mft.c.

2996{
2998 PINDEX_ENTRY_ATTRIBUTE IndexEntry;
2999 PNTFS_ATTR_CONTEXT IndexAllocationContext;
3000 PNTFS_ATTR_CONTEXT BitmapContext;
3001 PCHAR *BitmapMem;
3002 ULONG *BitmapPtr;
3004
3005 DPRINT("BrowseIndexEntries(%p, %p, %p, %lu, %p, %p, %wZ, %lu, %lu, %s, %s, %p)\n",
3006 Vcb,
3007 MftRecord,
3008 IndexRecord,
3009 IndexBlockSize,
3010 FirstEntry,
3011 LastEntry,
3012 FileName,
3013 *StartEntry,
3014 *CurrentEntry,
3015 DirSearch ? "TRUE" : "FALSE",
3016 CaseSensitive ? "TRUE" : "FALSE",
3017 OutMFTIndex);
3018
3019 // Find the $I30 index allocation, if there is one
3020 Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationContext, NULL);
3021 if (NT_SUCCESS(Status))
3022 {
3023 ULONGLONG BitmapLength;
3024 // Find the bitmap attribute for the index
3025 Status = FindAttribute(Vcb, MftRecord, AttributeBitmap, L"$I30", 4, &BitmapContext, NULL);
3026 if (!NT_SUCCESS(Status))
3027 {
3028 DPRINT1("Potential file system corruption detected!\n");
3029 ReleaseAttributeContext(IndexAllocationContext);
3030 return Status;
3031 }
3032
3033 // Get the length of the bitmap attribute
3034 BitmapLength = AttributeDataLength(BitmapContext->pRecord);
3035
3036 // Allocate memory for the bitmap, including some padding; RtlInitializeBitmap() wants a pointer
3037 // that's ULONG-aligned, and it wants the size of the memory allocated for it to be a ULONG-multiple.
3038 BitmapMem = ExAllocatePoolWithTag(NonPagedPool, BitmapLength + sizeof(ULONG), TAG_NTFS);
3039 if (!BitmapMem)
3040 {
3041 DPRINT1("Error: failed to allocate bitmap!");
3042 ReleaseAttributeContext(BitmapContext);
3043 ReleaseAttributeContext(IndexAllocationContext);
3045 }
3046
3047 RtlZeroMemory(BitmapMem, BitmapLength + sizeof(ULONG));
3048
3049 // RtlInitializeBitmap() wants a pointer that's ULONG-aligned.
3050 BitmapPtr = (PULONG)ALIGN_UP_BY((ULONG_PTR)BitmapMem, sizeof(ULONG));
3051
3052 // Read the existing bitmap data
3053 Status = ReadAttribute(Vcb, BitmapContext, 0, (PCHAR)BitmapPtr, BitmapLength);
3054 if (!NT_SUCCESS(Status))
3055 {
3056 DPRINT1("ERROR: Failed to read bitmap attribute!\n");
3057 ExFreePoolWithTag(BitmapMem, TAG_NTFS);
3058 ReleaseAttributeContext(BitmapContext);
3059 ReleaseAttributeContext(IndexAllocationContext);
3060 return Status;
3061 }
3062
3063 // Initialize bitmap
3064 RtlInitializeBitMap(&Bitmap, BitmapPtr, BitmapLength * 8);
3065 }
3066 else
3067 {
3068 // Couldn't find an index allocation
3069 IndexAllocationContext = NULL;
3070 }
3071
3072
3073 // Loop through all Index Entries of index, starting with FirstEntry
3074 IndexEntry = FirstEntry;
3075 while (IndexEntry <= LastEntry)
3076 {
3077 // Does IndexEntry have a sub-node?
3078 if (IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
3079 {
3080 if (!(IndexRecord->Header.Flags & INDEX_ROOT_LARGE) || !IndexAllocationContext)
3081 {
3082 DPRINT1("Filesystem corruption detected!\n");
3083 }
3084 else
3085 {
3087 MftRecord,
3088 IndexBlockSize,
3089 FileName,
3090 IndexAllocationContext,
3091 &Bitmap,
3092 GetIndexEntryVCN(IndexEntry),
3093 StartEntry,
3094 CurrentEntry,
3095 DirSearch,
3096 CaseSensitive,
3097 OutMFTIndex);
3098 if (NT_SUCCESS(Status))
3099 {
3100 ExFreePoolWithTag(BitmapMem, TAG_NTFS);
3101 ReleaseAttributeContext(BitmapContext);
3102 ReleaseAttributeContext(IndexAllocationContext);
3103 return Status;
3104 }
3105 }
3106 }
3107
3108 // Are we done?
3109 if (IndexEntry->Flags & NTFS_INDEX_ENTRY_END)
3110 break;
3111
3112 // If we've found a file whose index is greater than or equal to StartEntry that matches the search criteria
3113 if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) >= NTFS_FILE_FIRST_USER_FILE &&
3114 *CurrentEntry >= *StartEntry &&
3115 IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS &&
3116 CompareFileName(FileName, IndexEntry, DirSearch, CaseSensitive))
3117 {
3118 *StartEntry = *CurrentEntry;
3119 *OutMFTIndex = (IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK);
3120 if (IndexAllocationContext)
3121 {
3122 ExFreePoolWithTag(BitmapMem, TAG_NTFS);
3123 ReleaseAttributeContext(BitmapContext);
3124 ReleaseAttributeContext(IndexAllocationContext);
3125 }
3126 return STATUS_SUCCESS;
3127 }
3128
3129 // Advance to the next index entry
3130 (*CurrentEntry) += 1;
3131 ASSERT(IndexEntry->Length >= sizeof(INDEX_ENTRY_ATTRIBUTE));
3132 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
3133 }
3134
3135 if (IndexAllocationContext)
3136 {
3137 ExFreePoolWithTag(BitmapMem, TAG_NTFS);
3138 ReleaseAttributeContext(BitmapContext);
3139 ReleaseAttributeContext(IndexAllocationContext);
3140 }
3141
3143}
#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:68
#define NTFS_INDEX_ENTRY_END
Definition: ntfs.h:61
ULONGLONG GetIndexEntryVCN(PINDEX_ENTRY_ATTRIBUTE IndexEntry)
Definition: btree.c:1641
struct INDEX_ENTRY_ATTRIBUTE * PINDEX_ENTRY_ATTRIBUTE
#define NTFS_FILE_FIRST_USER_FILE
Definition: ntfs.h:196
#define INDEX_ROOT_LARGE
Definition: ntfs.h:209
@ AttributeIndexAllocation
Definition: ntfs.h:170
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:2839
BOOLEAN CompareFileName(PUNICODE_STRING FileName, PINDEX_ENTRY_ATTRIBUTE IndexEntry, BOOLEAN DirSearch, BOOLEAN CaseSensitive)
Definition: mft.c:2650
#define ASSERT(a)
Definition: mode.c:44
UCHAR NameType
Definition: ntfs.h:378
Definition: ntfs.h:409
union INDEX_ENTRY_ATTRIBUTE::@750 Data
FILENAME_ATTRIBUTE FileName
Definition: ntfs.h:427
USHORT Length
Definition: ntfs.h:423
USHORT Flags
Definition: ntfs.h:425
struct INDEX_ENTRY_ATTRIBUTE::@750::@751 Directory
INDEX_HEADER_ATTRIBUTE Header
Definition: ntfs.h:398
#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 2839 of file mft.c.

2851{
2852 PINDEX_BUFFER IndexRecord;
2855 PINDEX_ENTRY_ATTRIBUTE FirstEntry;
2856 PINDEX_ENTRY_ATTRIBUTE LastEntry;
2857 PINDEX_ENTRY_ATTRIBUTE IndexEntry;
2860
2861 DPRINT("BrowseSubNodeIndexEntries(%p, %p, %lu, %wZ, %p, %p, %I64d, %lu, %lu, %s, %s, %p)\n",
2862 Vcb,
2863 MftRecord,
2864 IndexBlockSize,
2865 FileName,
2866 IndexAllocationContext,
2867 Bitmap,
2868 VCN,
2869 *StartEntry,
2870 *CurrentEntry,
2871 "FALSE",
2872 DirSearch ? "TRUE" : "FALSE",
2873 CaseSensitive ? "TRUE" : "FALSE",
2874 OutMFTIndex);
2875
2876 // Calculate node number as VCN / Clusters per index record
2877 NodeNumber = VCN / (Vcb->NtfsInfo.BytesPerIndexRecord / Vcb->NtfsInfo.BytesPerCluster);
2878
2879 // Is the bit for this node clear in the bitmap?
2881 {
2882 DPRINT1("File system corruption detected, node with VCN %I64u is marked as deleted.\n", VCN);
2883 return STATUS_DATA_ERROR;
2884 }
2885
2886 // Allocate memory for the index record
2887 IndexRecord = ExAllocatePoolWithTag(NonPagedPool, IndexBlockSize, TAG_NTFS);
2888 if (!IndexRecord)
2889 {
2890 DPRINT1("Unable to allocate memory for index record!\n");
2892 }
2893
2894 // Calculate offset of index record
2895 Offset = VCN * Vcb->NtfsInfo.BytesPerCluster;
2896
2897 // Read the index record
2898 BytesRead = ReadAttribute(Vcb, IndexAllocationContext, Offset, (PCHAR)IndexRecord, IndexBlockSize);
2899 if (BytesRead != IndexBlockSize)
2900 {
2901 DPRINT1("Unable to read index record!\n");
2902 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
2903 return STATUS_UNSUCCESSFUL;
2904 }
2905
2906 // Assert that we're dealing with an index record here
2907 ASSERT(IndexRecord->Ntfs.Type == NRH_INDX_TYPE);
2908
2909 // Apply the fixup array to the index record
2910 Status = FixupUpdateSequenceArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs);
2911 if (!NT_SUCCESS(Status))
2912 {
2913 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
2914 DPRINT1("Failed to apply fixup array!\n");
2915 return Status;
2916 }
2917
2918 ASSERT(IndexRecord->Header.AllocatedSize + FIELD_OFFSET(INDEX_BUFFER, Header) == IndexBlockSize);
2919 FirstEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexRecord->Header + IndexRecord->Header.FirstEntryOffset);
2920 LastEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexRecord->Header + IndexRecord->Header.TotalSizeOfEntries);
2921 ASSERT(LastEntry <= (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexRecord + IndexBlockSize));
2922
2923 // Loop through all Index Entries of index, starting with FirstEntry
2924 IndexEntry = FirstEntry;
2925 while (IndexEntry <= LastEntry)
2926 {
2927 // Does IndexEntry have a sub-node?
2928 if (IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
2929 {
2930 if (!(IndexRecord->Header.Flags & INDEX_NODE_LARGE) || !IndexAllocationContext)
2931 {
2932 DPRINT1("Filesystem corruption detected!\n");
2933 }
2934 else
2935 {
2937 MftRecord,
2938 IndexBlockSize,
2939 FileName,
2940 IndexAllocationContext,
2941 Bitmap,
2942 GetIndexEntryVCN(IndexEntry),
2943 StartEntry,
2944 CurrentEntry,
2945 DirSearch,
2946 CaseSensitive,
2947 OutMFTIndex);
2948 if (NT_SUCCESS(Status))
2949 {
2950 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
2951 return Status;
2952 }
2953 }
2954 }
2955
2956 // Are we done?
2957 if (IndexEntry->Flags & NTFS_INDEX_ENTRY_END)
2958 break;
2959
2960 // If we've found a file whose index is greater than or equal to StartEntry that matches the search criteria
2961 if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) >= NTFS_FILE_FIRST_USER_FILE &&
2962 *CurrentEntry >= *StartEntry &&
2963 IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS &&
2964 CompareFileName(FileName, IndexEntry, DirSearch, CaseSensitive))
2965 {
2966 *StartEntry = *CurrentEntry;
2967 *OutMFTIndex = (IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK);
2968 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
2969 return STATUS_SUCCESS;
2970 }
2971
2972 // Advance to the next index entry
2973 (*CurrentEntry) += 1;
2974 ASSERT(IndexEntry->Length >= sizeof(INDEX_ENTRY_ATTRIBUTE));
2975 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
2976 }
2977
2978 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
2979
2981}
Definition: Header.h:9
#define INDEX_NODE_LARGE
Definition: ntfs.h:212
#define NRH_INDX_TYPE
Definition: ntfs.h:247
NTSTATUS FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb, PNTFS_RECORD_HEADER Record)
Definition: mft.c:1965
#define STATUS_DATA_ERROR
Definition: ntstatus.h:298
INDEX_HEADER_ATTRIBUTE Header
Definition: ntfs.h:405
NTFS_RECORD_HEADER Ntfs
Definition: ntfs.h:403
ULONG FirstEntryOffset
Definition: ntfs.h:384
ULONG TotalSizeOfEntries
Definition: ntfs.h:385
#define FIELD_OFFSET(t, f)
Definition: typedefs.h:255
#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:2574
#define RtlCheckBit(BMH, BP)
Definition: rtlfuncs.h:3152

Referenced by BrowseIndexEntries(), and BrowseSubNodeIndexEntries().

◆ CompareFileName()

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

Definition at line 2650 of file mft.c.

2654{
2655 BOOLEAN Ret, Alloc = FALSE;
2656 UNICODE_STRING EntryName;
2657
2658 EntryName.Buffer = IndexEntry->FileName.Name;
2659 EntryName.Length =
2660 EntryName.MaximumLength = IndexEntry->FileName.NameLength * sizeof(WCHAR);
2661
2662 if (DirSearch)
2663 {
2664 UNICODE_STRING IntFileName;
2665 if (!CaseSensitive)
2666 {
2668 Alloc = TRUE;
2669 }
2670 else
2671 {
2672 IntFileName = *FileName;
2673 }
2674
2675 Ret = FsRtlIsNameInExpression(&IntFileName, &EntryName, !CaseSensitive, NULL);
2676
2677 if (Alloc)
2678 {
2679 RtlFreeUnicodeString(&IntFileName);
2680 }
2681
2682 return Ret;
2683 }
2684 else
2685 {
2686 return (RtlCompareUnicodeString(FileName, &EntryName, !CaseSensitive) == 0);
2687 }
2688}
unsigned char BOOLEAN
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:896
NTSYSAPI VOID NTAPI RtlFreeUnicodeString(PUNICODE_STRING UnicodeString)
BOOLEAN NTAPI FsRtlIsNameInExpression(IN PUNICODE_STRING Expression, IN PUNICODE_STRING Name, IN BOOLEAN IgnoreCase, IN PWCHAR UpcaseTable OPTIONAL)
Definition: name.c:514
UCHAR NameLength
Definition: ntfs.h:377
WCHAR Name[1]
Definition: ntfs.h:379
USHORT MaximumLength
Definition: env_spec_w32.h:370
#define NT_VERIFY(exp)
Definition: rtlfuncs.h:3287
__wchar_t WCHAR
Definition: xmlstorage.h:180

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

◆ 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
NTSTATUS FindFirstAttribute(PFIND_ATTR_CONTXT Context, PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER FileRecord, BOOLEAN OnlyResident, PNTFS_ATTR_RECORD *Attribute)
Definition: attrib.c:1383
NTSTATUS FindFirstAttributeListItem(PFIND_ATTR_CONTXT Context, PNTFS_ATTRIBUTE_LIST_ITEM *Item)
Definition: attrib.c:1307
VOID FindCloseAttribute(PFIND_ATTR_CONTXT Context)
Definition: attrib.c:1465
NTSTATUS FindNextAttribute(PFIND_ATTR_CONTXT Context, PNTFS_ATTR_RECORD *Attribute)
Definition: attrib.c:1431
NTSTATUS FindNextAttributeListItem(PFIND_ATTR_CONTXT Context, PNTFS_ATTRIBUTE_LIST_ITEM *Item)
Definition: attrib.c:1321
#define RtlCompareMemory(s1, s2, l)
Definition: env_spec_w32.h:465
NTSTATUS ReadFileRecord(PDEVICE_EXTENSION Vcb, ULONGLONG index, PFILE_RECORD_HEADER file)
Definition: mft.c:1631
PNTFS_ATTR_CONTEXT PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord)
Definition: mft.c:41
ULONGLONG MFTIndex
Definition: ntfs.h:314
USHORT NameOffset
Definition: ntfs.h:130
UCHAR NameLength
Definition: ntfs.h:129
ULONG Type
Definition: ntfs.h:126
uint16_t * PWCHAR
Definition: typedefs.h:56

Referenced by AddNewMftEntry(), AllocateIndexNode(), BrowseIndexEntries(), CreateBTreeFromIndex(), tinyxml2::XMLElement::FindAttribute(), FindAttribute(), FreeClusters(), GetVolumeBitmap(), IncreaseMftSize(), NtfsAddFilenameToDirectory(), NtfsAllocateClusters(), NtfsCreateFile(), NtfsDirFindFile(), NtfsFindMftRecord(), NtfsGetFileSize(), NtfsGetFreeClusters(), NtfsGetVolumeData(), NtfsReadFCBAttribute(), NtfsReadFile(), NtfsSetEndOfFile(), NtfsWriteFile(), tinyxml2::XMLElement::QueryBoolAttribute(), tinyxml2::XMLElement::QueryDoubleAttribute(), tinyxml2::XMLElement::QueryFloatAttribute(), tinyxml2::XMLElement::QueryIntAttribute(), tinyxml2::XMLElement::QueryUnsignedAttribute(), UpdateFileNameRecord(), UpdateIndexAllocation(), UpdateIndexEntryFileNameSize(), UpdateMftMirror(), and WriteAttribute().

◆ FixupUpdateSequenceArray()

NTSTATUS FixupUpdateSequenceArray ( PDEVICE_EXTENSION  Vcb,
PNTFS_RECORD_HEADER  Record 
)

Definition at line 1965 of file mft.c.

1967{
1968 USHORT *USA;
1969 USHORT USANumber;
1970 USHORT USACount;
1971 USHORT *Block;
1972
1973 USA = (USHORT*)((PCHAR)Record + Record->UsaOffset);
1974 USANumber = *(USA++);
1975 USACount = Record->UsaCount - 1; /* Exclude the USA Number. */
1976 Block = (USHORT*)((PCHAR)Record + Vcb->NtfsInfo.BytesPerSector - 2);
1977
1978 DPRINT("FixupUpdateSequenceArray(%p, %p)\nUSANumber: %u\tUSACount: %u\n", Vcb, Record, USANumber, USACount);
1979
1980 while (USACount)
1981 {
1982 if (*Block != USANumber)
1983 {
1984 DPRINT1("Mismatch with USA: %u read, %u expected\n" , *Block, USANumber);
1985 return STATUS_UNSUCCESSFUL;
1986 }
1987 *Block = *(USA++);
1988 Block = (USHORT*)((PCHAR)Block + Vcb->NtfsInfo.BytesPerSector);
1989 USACount--;
1990 }
1991
1992 return STATUS_SUCCESS;
1993}

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

◆ 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 DPRINT1("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:1790
PFILE_RECORD_HEADER NtfsCreateEmptyFileRecord(PDEVICE_EXTENSION DeviceExt)
Definition: create.c:818
#define ExAcquireResourceExclusiveLite(res, wait)
Definition: env_spec_w32.h:615
NTSTATUS UpdateMftMirror(PNTFS_VCB Vcb)
Definition: mft.c:2714
NTSTATUS SetNonResidentAttributeDataLength(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT AttrContext, ULONG AttrOffset, PFILE_RECORD_HEADER FileRecord, PLARGE_INTEGER DataSize)
Definition: mft.c:756
NTSTATUS SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT AttrContext, ULONG AttrOffset, PFILE_RECORD_HEADER FileRecord, PLARGE_INTEGER DataSize)
Definition: mft.c:891
_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:452
USHORT Flags
Definition: ntfs.h:256
#define max(a, b)
Definition: svc.c:63
int64_t LONGLONG
Definition: typedefs.h:68
#define STATUS_INVALID_PARAMETER
Definition: udferr_usr.h:135

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 DPRINT1("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}
struct NTFS_ATTR_RECORD * PNTFS_ATTR_RECORD
#define FILE_RECORD_END
Definition: ntfs.h:182
@ AttributeEnd
Definition: ntfs.h:177
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:3004
#define RtlCopyMemory(Destination, Source, Length)
Definition: typedefs.h:263

Referenced by NtfsAddFilenameToDirectory(), 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}
ULONG Length
Definition: ntfs.h:127
#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 2192 of file mft.c.

2197{
2199 PFILE_RECORD_HEADER ParentFileRecord;
2200 PNTFS_ATTR_CONTEXT IndexRootContext;
2201 PINDEX_ROOT_ATTRIBUTE I30IndexRoot;
2202 ULONG IndexRootOffset;
2203 ULONGLONG I30IndexRootLength;
2204 ULONG LengthWritten;
2205 PINDEX_ROOT_ATTRIBUTE NewIndexRoot;
2206 ULONG AttributeLength;
2207 PNTFS_ATTR_RECORD NextAttribute;
2208 PB_TREE NewTree;
2209 ULONG BtreeIndexLength;
2210 ULONG MaxIndexRootSize;
2211 PB_TREE_KEY NewLeftKey;
2212 PB_TREE_FILENAME_NODE NewRightHandNode;
2213 LARGE_INTEGER MinIndexRootSize;
2214 ULONG NewMaxIndexRootSize;
2215 ULONG NodeSize;
2216
2217 // Allocate memory for the parent directory
2218 ParentFileRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList);
2219 if (!ParentFileRecord)
2220 {
2221 DPRINT1("ERROR: Couldn't allocate memory for file record!\n");
2223 }
2224
2225 // Open the parent directory
2226 Status = ReadFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
2227 if (!NT_SUCCESS(Status))
2228 {
2229 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2230 DPRINT1("ERROR: Couldn't read parent directory with index %I64u\n",
2231 DirectoryMftIndex);
2232 return Status;
2233 }
2234
2235#ifndef NDEBUG
2236 DPRINT1("Dumping old parent file record:\n");
2237 NtfsDumpFileRecord(DeviceExt, ParentFileRecord);
2238#endif
2239
2240 // Find the index root attribute for the directory
2241 Status = FindAttribute(DeviceExt,
2242 ParentFileRecord,
2244 L"$I30",
2245 4,
2246 &IndexRootContext,
2247 &IndexRootOffset);
2248 if (!NT_SUCCESS(Status))
2249 {
2250 DPRINT1("ERROR: Couldn't find $I30 $INDEX_ROOT attribute for parent directory with MFT #: %I64u!\n",
2251 DirectoryMftIndex);
2252 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2253 return Status;
2254 }
2255
2256 // Find the maximum index size given what the file record can hold
2257 // First, find the max index size assuming index root is the last attribute
2258 MaxIndexRootSize = DeviceExt->NtfsInfo.BytesPerFileRecord // Start with the size of a file record
2259 - IndexRootOffset // Subtract the length of everything that comes before index root
2260 - IndexRootContext->pRecord->Resident.ValueOffset // Subtract the length of the attribute header for index root
2261 - sizeof(INDEX_ROOT_ATTRIBUTE) // Subtract the length of the index root header
2262 - (sizeof(ULONG) * 2); // Subtract the length of the file record end marker and padding
2263
2264 // Are there attributes after this one?
2265 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)ParentFileRecord + IndexRootOffset + IndexRootContext->pRecord->Length);
2266 if (NextAttribute->Type != AttributeEnd)
2267 {
2268 // Find the length of all attributes after this one, not counting the end marker
2269 ULONG LengthOfAttributes = 0;
2270 PNTFS_ATTR_RECORD CurrentAttribute = NextAttribute;
2271 while (CurrentAttribute->Type != AttributeEnd)
2272 {
2273 LengthOfAttributes += CurrentAttribute->Length;
2274 CurrentAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)CurrentAttribute + CurrentAttribute->Length);
2275 }
2276
2277 // Leave room for the existing attributes
2278 MaxIndexRootSize -= LengthOfAttributes;
2279 }
2280
2281 // Allocate memory for the index root data
2282 I30IndexRootLength = AttributeDataLength(IndexRootContext->pRecord);
2283 I30IndexRoot = ExAllocatePoolWithTag(NonPagedPool, I30IndexRootLength, TAG_NTFS);
2284 if (!I30IndexRoot)
2285 {
2286 DPRINT1("ERROR: Couldn't allocate memory for index root attribute!\n");
2287 ReleaseAttributeContext(IndexRootContext);
2288 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2290 }
2291
2292 // Read the Index Root
2293 Status = ReadAttribute(DeviceExt, IndexRootContext, 0, (PCHAR)I30IndexRoot, I30IndexRootLength);
2294 if (!NT_SUCCESS(Status))
2295 {
2296 DPRINT1("ERROR: Couln't read index root attribute for Mft index #%I64u\n", DirectoryMftIndex);
2297 ReleaseAttributeContext(IndexRootContext);
2298 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2299 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2300 return Status;
2301 }
2302
2303 // Convert the index to a B*Tree
2304 Status = CreateBTreeFromIndex(DeviceExt,
2305 ParentFileRecord,
2306 IndexRootContext,
2307 I30IndexRoot,
2308 &NewTree);
2309 if (!NT_SUCCESS(Status))
2310 {
2311 DPRINT1("ERROR: Failed to create B-Tree from Index!\n");
2312 ReleaseAttributeContext(IndexRootContext);
2313 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2314 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2315 return Status;
2316 }
2317
2318#ifndef NDEBUG
2319 DumpBTree(NewTree);
2320#endif
2321
2322 // Insert the key for the file we're adding
2323 Status = NtfsInsertKey(NewTree,
2324 FileReferenceNumber,
2325 FilenameAttribute,
2326 NewTree->RootNode,
2327 CaseSensitive,
2328 MaxIndexRootSize,
2329 I30IndexRoot->SizeOfEntry,
2330 &NewLeftKey,
2331 &NewRightHandNode);
2332 if (!NT_SUCCESS(Status))
2333 {
2334 DPRINT1("ERROR: Failed to insert key into B-Tree!\n");
2335 DestroyBTree(NewTree);
2336 ReleaseAttributeContext(IndexRootContext);
2337 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2338 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2339 return Status;
2340 }
2341
2342#ifndef NDEBUG
2343 DumpBTree(NewTree);
2344#endif
2345
2346 // The root node can't be split
2347 ASSERT(NewLeftKey == NULL);
2348 ASSERT(NewRightHandNode == NULL);
2349
2350 // Convert B*Tree back to Index
2351
2352 // Updating the index allocation can change the size available for the index root,
2353 // And if the index root is demoted, the index allocation will need to be updated again,
2354 // which may change the size available for index root... etc.
2355 // My solution is to decrease index root to the size it would be if it was demoted,
2356 // then UpdateIndexAllocation will have an accurate representation of the maximum space
2357 // it can use in the file record. There's still a chance that the act of allocating an
2358 // index node after demoting the index root will increase the size of the file record beyond
2359 // it's limit, but if that happens, an attribute-list will most definitely be needed.
2360 // This a bit hacky, but it seems to be functional.
2361
2362 // Calculate the minimum size of the index root attribute, considering one dummy key and one VCN
2363 MinIndexRootSize.QuadPart = sizeof(INDEX_ROOT_ATTRIBUTE) // size of the index root headers
2364 + 0x18; // Size of dummy key with a VCN for a subnode
2365 ASSERT(MinIndexRootSize.QuadPart % ATTR_RECORD_ALIGNMENT == 0);
2366
2367 // Temporarily shrink the index root to it's minimal size
2368 AttributeLength = MinIndexRootSize.LowPart;
2369 AttributeLength += sizeof(INDEX_ROOT_ATTRIBUTE);
2370
2371
2372 // FIXME: IndexRoot will probably be invalid until we're finished. If we fail before we finish, the directory will probably be toast.
2373 // The potential for catastrophic data-loss exists!!! :)
2374
2375 // Update the length of the attribute in the file record of the parent directory
2377 IndexRootContext,
2378 ParentFileRecord,
2379 IndexRootOffset,
2380 AttributeLength);
2381 if (!NT_SUCCESS(Status))
2382 {
2383 DPRINT1("ERROR: Unable to set length of index root!\n");
2384 DestroyBTree(NewTree);
2385 ReleaseAttributeContext(IndexRootContext);
2386 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2387 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2388 return Status;
2389 }
2390
2391 // Update the index allocation
2392 Status = UpdateIndexAllocation(DeviceExt, NewTree, I30IndexRoot->SizeOfEntry, ParentFileRecord);
2393 if (!NT_SUCCESS(Status))
2394 {
2395 DPRINT1("ERROR: Failed to update index allocation from B-Tree!\n");
2396 DestroyBTree(NewTree);
2397 ReleaseAttributeContext(IndexRootContext);
2398 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2399 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2400 return Status;
2401 }
2402
2403#ifndef NDEBUG
2404 DPRINT1("Index Allocation updated\n");
2405 DumpBTree(NewTree);
2406#endif
2407
2408 // Find the maximum index root size given what the file record can hold
2409 // First, find the max index size assuming index root is the last attribute
2410 NewMaxIndexRootSize =
2411 DeviceExt->NtfsInfo.BytesPerFileRecord // Start with the size of a file record
2412 - IndexRootOffset // Subtract the length of everything that comes before index root
2413 - IndexRootContext->pRecord->Resident.ValueOffset // Subtract the length of the attribute header for index root
2414 - sizeof(INDEX_ROOT_ATTRIBUTE) // Subtract the length of the index root header
2415 - (sizeof(ULONG) * 2); // Subtract the length of the file record end marker and padding
2416
2417 // Are there attributes after this one?
2418 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)ParentFileRecord + IndexRootOffset + IndexRootContext->pRecord->Length);
2419 if (NextAttribute->Type != AttributeEnd)
2420 {
2421 // Find the length of all attributes after this one, not counting the end marker
2422 ULONG LengthOfAttributes = 0;
2423 PNTFS_ATTR_RECORD CurrentAttribute = NextAttribute;
2424 while (CurrentAttribute->Type != AttributeEnd)
2425 {
2426 LengthOfAttributes += CurrentAttribute->Length;
2427 CurrentAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)CurrentAttribute + CurrentAttribute->Length);
2428 }
2429
2430 // Leave room for the existing attributes
2431 NewMaxIndexRootSize -= LengthOfAttributes;
2432 }
2433
2434 // The index allocation and index bitmap may have grown, leaving less room for the index root,
2435 // so now we need to double-check that index root isn't too large
2436 NodeSize = GetSizeOfIndexEntries(NewTree->RootNode);
2437 if (NodeSize > NewMaxIndexRootSize)
2438 {
2439 DPRINT1("Demoting index root.\nNodeSize: 0x%lx\nNewMaxIndexRootSize: 0x%lx\n", NodeSize, NewMaxIndexRootSize);
2440
2441 Status = DemoteBTreeRoot(NewTree);
2442 if (!NT_SUCCESS(Status))
2443 {
2444 DPRINT1("ERROR: Failed to demote index root!\n");
2445 DestroyBTree(NewTree);
2446 ReleaseAttributeContext(IndexRootContext);
2447 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2448 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2449 return Status;
2450 }
2451
2452 // We need to update the index allocation once more
2453 Status = UpdateIndexAllocation(DeviceExt, NewTree, I30IndexRoot->SizeOfEntry, ParentFileRecord);
2454 if (!NT_SUCCESS(Status))
2455 {
2456 DPRINT1("ERROR: Failed to update index allocation from B-Tree!\n");
2457 DestroyBTree(NewTree);
2458 ReleaseAttributeContext(IndexRootContext);
2459 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2460 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2461 return Status;
2462 }
2463
2464 // re-recalculate max size of index root
2465 NewMaxIndexRootSize =
2466 // Find the maximum index size given what the file record can hold
2467 // First, find the max index size assuming index root is the last attribute
2468 DeviceExt->NtfsInfo.BytesPerFileRecord // Start with the size of a file record
2469 - IndexRootOffset // Subtract the length of everything that comes before index root
2470 - IndexRootContext->pRecord->Resident.ValueOffset // Subtract the length of the attribute header for index root
2471 - sizeof(INDEX_ROOT_ATTRIBUTE) // Subtract the length of the index root header
2472 - (sizeof(ULONG) * 2); // Subtract the length of the file record end marker and padding
2473
2474 // Are there attributes after this one?
2475 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)ParentFileRecord + IndexRootOffset + IndexRootContext->pRecord->Length);
2476 if (NextAttribute->Type != AttributeEnd)
2477 {
2478 // Find the length of all attributes after this one, not counting the end marker
2479 ULONG LengthOfAttributes = 0;
2480 PNTFS_ATTR_RECORD CurrentAttribute = NextAttribute;
2481 while (CurrentAttribute->Type != AttributeEnd)
2482 {
2483 LengthOfAttributes += CurrentAttribute->Length;
2484 CurrentAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)CurrentAttribute + CurrentAttribute->Length);
2485 }
2486
2487 // Leave room for the existing attributes
2488 NewMaxIndexRootSize -= LengthOfAttributes;
2489 }
2490
2491
2492 }
2493
2494 // Create the Index Root from the B*Tree
2495 Status = CreateIndexRootFromBTree(DeviceExt, NewTree, NewMaxIndexRootSize, &NewIndexRoot, &BtreeIndexLength);
2496 if (!NT_SUCCESS(Status))
2497 {
2498 DPRINT1("ERROR: Failed to create Index root from B-Tree!\n");
2499 DestroyBTree(NewTree);
2500 ReleaseAttributeContext(IndexRootContext);
2501 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2502 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2503 return Status;
2504 }
2505
2506 // We're done with the B-Tree now
2507 DestroyBTree(NewTree);
2508
2509 // Write back the new index root attribute to the parent directory file record
2510
2511 // First, we need to resize the attribute.
2512 // CreateIndexRootFromBTree() should have verified that the index root fits within MaxIndexSize.
2513 // We can't set the size as we normally would, because $INDEX_ROOT must always be resident.
2514 AttributeLength = NewIndexRoot->Header.AllocatedSize + FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header);
2515
2516 if (AttributeLength != IndexRootContext->pRecord->Resident.ValueLength)
2517 {
2518 // Update the length of the attribute in the file record of the parent directory
2520 IndexRootContext,
2521 ParentFileRecord,
2522 IndexRootOffset,
2523 AttributeLength);
2524 if (!NT_SUCCESS(Status))
2525 {
2526 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
2527 ReleaseAttributeContext(IndexRootContext);
2528 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2529 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2530 DPRINT1("ERROR: Unable to set resident attribute length!\n");
2531 return Status;
2532 }
2533
2534 }
2535
2536 NT_ASSERT(ParentFileRecord->BytesInUse <= DeviceExt->NtfsInfo.BytesPerFileRecord);
2537
2538 Status = UpdateFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
2539 if (!NT_SUCCESS(Status))
2540 {
2541 DPRINT1("ERROR: Failed to update file record of directory with index: %llx\n", DirectoryMftIndex);
2542 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2543 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
2544 ReleaseAttributeContext(IndexRootContext);
2545 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2546 return Status;
2547 }
2548
2549 // Write the new index root to disk
2550 Status = WriteAttribute(DeviceExt,
2551 IndexRootContext,
2552 0,
2553 (PUCHAR)NewIndexRoot,
2554 AttributeLength,
2555 &LengthWritten,
2556 ParentFileRecord);
2557 if (!NT_SUCCESS(Status) || LengthWritten != AttributeLength)
2558 {
2559 DPRINT1("ERROR: Unable to write new index root attribute to parent directory!\n");
2560 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
2561 ReleaseAttributeContext(IndexRootContext);
2562 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2563 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2564 return Status;
2565 }
2566
2567 // re-read the parent file record, so we can dump it
2568 Status = ReadFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
2569 if (!NT_SUCCESS(Status))
2570 {
2571 DPRINT1("ERROR: Couldn't read parent directory after messing with it!\n");
2572 }
2573 else
2574 {
2575#ifndef NDEBUG
2576 DPRINT1("Dumping new B-Tree:\n");
2577
2578 Status = CreateBTreeFromIndex(DeviceExt, ParentFileRecord, IndexRootContext, NewIndexRoot, &NewTree);
2579 if (!NT_SUCCESS(Status))
2580 {
2581 DPRINT1("ERROR: Couldn't re-create b-tree\n");
2582 return Status;
2583 }
2584
2585 DumpBTree(NewTree);
2586
2587 DestroyBTree(NewTree);
2588
2589 NtfsDumpFileRecord(DeviceExt, ParentFileRecord);
2590#endif
2591 }
2592
2593 // Cleanup
2594 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
2595 ReleaseAttributeContext(IndexRootContext);
2596 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2597 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2598
2599 return Status;
2600}
NTSTATUS CreateBTreeFromIndex(PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER FileRecordWithIndex, PNTFS_ATTR_CONTEXT IndexRootContext, PINDEX_ROOT_ATTRIBUTE IndexRoot, PB_TREE *NewTree)
Definition: btree.c:682
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:1691
NTSTATUS UpdateIndexAllocation(PDEVICE_EXTENSION DeviceExt, PB_TREE Tree, ULONG IndexBufferSize, PFILE_RECORD_HEADER FileRecord)
Definition: btree.c:1182
VOID DestroyBTree(PB_TREE Tree)
Definition: btree.c:1542
NTSTATUS DemoteBTreeRoot(PB_TREE Tree)
Definition: btree.c:1089
ULONG GetSizeOfIndexEntries(PB_TREE_FILENAME_NODE Node)
Definition: btree.c:855
VOID DumpBTree(PB_TREE Tree)
Definition: btree.c:1622
NTSTATUS CreateIndexRootFromBTree(PDEVICE_EXTENSION DeviceExt, PB_TREE Tree, ULONG MaxIndexSize, PINDEX_ROOT_ATTRIBUTE *IndexRoot, ULONG *Length)
Definition: btree.c:910
@ AttributeIndexRoot
Definition: ntfs.h:169
VOID NtfsDumpFileRecord(PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER FileRecord)
Definition: mft.c:3334
NTSTATUS InternalSetResidentAttributeLength(PDEVICE_EXTENSION DeviceExt, PNTFS_ATTR_CONTEXT AttrContext, PFILE_RECORD_HEADER FileRecord, ULONG AttrOffset, ULONG DataSize)
Definition: mft.c:542
Definition: ntfs.h:454
PB_TREE_FILENAME_NODE RootNode
Definition: ntfs.h:455
ULONG SizeOfEntry
Definition: ntfs.h:395
ULONG BytesInUse
Definition: ntfs.h:257
#define NT_ASSERT
Definition: rtlfuncs.h:3310

Referenced by NtfsCreateDirectory(), and NtfsCreateFileRecord().

◆ NtfsDumpData()

void NtfsDumpData ( ULONG_PTR  Buffer,
ULONG  Length 
)

Definition at line 3297 of file mft.c.

3298{
3299 ULONG i, j;
3300
3301 // dump binary data, 8 bytes at a time
3302 for (i = 0; i < Length; i += 8)
3303 {
3304 // display current offset, in hex
3305 DbgPrint("\t%03x\t", i);
3306
3307 // display hex value of each of the next 8 bytes
3308 for (j = 0; j < 8; j++)
3309 DbgPrint("%02x ", *(PUCHAR)(Buffer + i + j));
3310 DbgPrint("\n");
3311 }
3312}
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 3334 of file mft.c.

3336{
3337 ULONG i, j;
3338
3339 // dump binary data, 8 bytes at a time
3340 for (i = 0; i < FileRecord->BytesInUse; i += 8)
3341 {
3342 // display current offset, in hex
3343 DbgPrint("\t%03x\t", i);
3344
3345 // display hex value of each of the next 8 bytes
3346 for (j = 0; j < 8; j++)
3347 DbgPrint("%02x ", *(PUCHAR)((ULONG_PTR)FileRecord + i + j));
3348 DbgPrint("\n");
3349 }
3350
3351 NtfsDumpFileAttributes(Vcb, FileRecord);
3352}

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

◆ NtfsFindFileAt()

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

Definition at line 3355 of file mft.c.

3362{
3364
3365 DPRINT("NtfsFindFileAt(%p, %wZ, %lu, %p, %p, %I64x, %s)\n",
3366 Vcb,
3367 SearchPattern,
3368 *FirstEntry,
3369 FileRecord,
3370 MFTIndex,
3371 CurrentMFTIndex,
3372 (CaseSensitive ? "TRUE" : "FALSE"));
3373
3374 Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, SearchPattern, FirstEntry, TRUE, CaseSensitive, &CurrentMFTIndex);
3375 if (!NT_SUCCESS(Status))
3376 {
3377 DPRINT("NtfsFindFileAt: NtfsFindMftRecord() failed with status 0x%08lx\n", Status);
3378 return Status;
3379 }
3380
3381 *FileRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
3382 if (*FileRecord == NULL)
3383 {
3384 DPRINT("NtfsFindFileAt: Can't allocate MFT record\n");
3386 }
3387
3388 Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
3389 if (!NT_SUCCESS(Status))
3390 {
3391 DPRINT("NtfsFindFileAt: Can't read MFT record\n");
3392 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, *FileRecord);
3393 return Status;
3394 }
3395
3396 *MFTIndex = CurrentMFTIndex;
3397
3398 return STATUS_SUCCESS;
3399}
NTSTATUS NtfsFindMftRecord(PDEVICE_EXTENSION Vcb, ULONGLONG MFTIndex, PUNICODE_STRING FileName, PULONG FirstEntry, BOOLEAN DirSearch, BOOLEAN CaseSensitive, ULONGLONG *OutMFTIndex)
Definition: mft.c:3146

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 3146 of file mft.c.

3153{
3154 PFILE_RECORD_HEADER MftRecord;
3155 PNTFS_ATTR_CONTEXT IndexRootCtx;
3156 PINDEX_ROOT_ATTRIBUTE IndexRoot;
3157 PCHAR IndexRecord;
3158 PINDEX_ENTRY_ATTRIBUTE IndexEntry, IndexEntryEnd;
3160 ULONG CurrentEntry = 0;
3161
3162 DPRINT("NtfsFindMftRecord(%p, %I64d, %wZ, %lu, %s, %s, %p)\n",
3163 Vcb,
3164 MFTIndex,
3165 FileName,
3166 *FirstEntry,
3167 DirSearch ? "TRUE" : "FALSE",
3168 CaseSensitive ? "TRUE" : "FALSE",
3169 OutMFTIndex);
3170
3171 MftRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
3172 if (MftRecord == NULL)
3173 {
3175 }
3176
3177 Status = ReadFileRecord(Vcb, MFTIndex, MftRecord);
3178 if (!NT_SUCCESS(Status))
3179 {
3180 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
3181 return Status;
3182 }
3183
3184 ASSERT(MftRecord->Ntfs.Type == NRH_FILE_TYPE);
3185 Status = FindAttribute(Vcb, MftRecord, AttributeIndexRoot, L"$I30", 4, &IndexRootCtx, NULL);
3186 if (!NT_SUCCESS(Status))
3187 {
3188 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
3189 return Status;
3190 }
3191
3192 IndexRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerIndexRecord, TAG_NTFS);
3193 if (IndexRecord == NULL)
3194 {
3195 ReleaseAttributeContext(IndexRootCtx);
3196 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
3198 }
3199
3200 ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, Vcb->NtfsInfo.BytesPerIndexRecord);
3201 IndexRoot = (PINDEX_ROOT_ATTRIBUTE)IndexRecord;
3202 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)&IndexRoot->Header + IndexRoot->Header.FirstEntryOffset);
3203 /* Index root is always resident. */
3204 IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)(IndexRecord + IndexRoot->Header.TotalSizeOfEntries);
3205 ReleaseAttributeContext(IndexRootCtx);
3206
3207 DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb->NtfsInfo.BytesPerIndexRecord, IndexRoot->SizeOfEntry);
3208
3210 MftRecord,
3211 (PINDEX_ROOT_ATTRIBUTE)IndexRecord,
3212 IndexRoot->SizeOfEntry,
3213 IndexEntry,
3214 IndexEntryEnd,
3215 FileName,
3216 FirstEntry,
3217 &CurrentEntry,
3218 DirSearch,
3219 CaseSensitive,
3220 OutMFTIndex);
3221
3222 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
3223 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
3224
3225 return Status;
3226}
#define NRH_FILE_TYPE
Definition: ntfs.h:246
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:2984
NTFS_RECORD_HEADER Ntfs
Definition: ntfs.h:252

Referenced by NtfsFindFileAt(), and NtfsLookupFileAt().

◆ NtfsLookupFile()

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

Definition at line 3287 of file mft.c.

3292{
3293 return NtfsLookupFileAt(Vcb, PathName, CaseSensitive, FileRecord, MFTIndex, NTFS_FILE_ROOT);
3294}
#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:3229

◆ NtfsLookupFileAt()

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

Definition at line 3229 of file mft.c.

3235{
3236 UNICODE_STRING Current, Remaining;
3238 ULONG FirstEntry = 0;
3239
3240 DPRINT("NtfsLookupFileAt(%p, %wZ, %s, %p, %p, %I64x)\n",
3241 Vcb,
3242 PathName,
3243 CaseSensitive ? "TRUE" : "FALSE",
3244 FileRecord,
3245 MFTIndex,
3246 CurrentMFTIndex);
3247
3248 FsRtlDissectName(*PathName, &Current, &Remaining);
3249
3250 while (Current.Length != 0)
3251 {
3252 DPRINT("Current: %wZ\n", &Current);
3253
3254 Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, &Current, &FirstEntry, FALSE, CaseSensitive, &CurrentMFTIndex);
3255 if (!NT_SUCCESS(Status))
3256 {
3257 return Status;
3258 }
3259
3260 if (Remaining.Length == 0)
3261 break;
3262
3263 FsRtlDissectName(Current, &Current, &Remaining);
3264 }
3265
3266 *FileRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
3267 if (*FileRecord == NULL)
3268 {
3269 DPRINT("NtfsLookupFileAt: Can't allocate MFT record\n");
3271 }
3272
3273 Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
3274 if (!NT_SUCCESS(Status))
3275 {
3276 DPRINT("NtfsLookupFileAt: Can't read MFT record\n");
3277 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, *FileRecord);
3278 return Status;
3279 }
3280
3281 *MFTIndex = CurrentMFTIndex;
3282
3283 return STATUS_SUCCESS;
3284}
VOID NTAPI FsRtlDissectName(IN UNICODE_STRING Name, OUT PUNICODE_STRING FirstPart, OUT PUNICODE_STRING RemainingPart)
Definition: name.c:398

Referenced by NtfsDirFindFile(), and NtfsLookupFile().

◆ 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}
PUCHAR DecodeRun(PUCHAR DataRun, LONGLONG *DataRunOffset, ULONGLONG *DataRunLength)
Definition: attrib.c:966
NTSTATUS ConvertDataRunsToLargeMCB(PUCHAR DataRun, PLARGE_MCB DataRunsMCB, PULONGLONG pNextVBN)
Definition: attrib.c:825
NPAGED_LOOKASIDE_LIST AttrCtxtLookasideList
Definition: ntfs.h:154

Referenced by FindAttribute(), and InternalReadNonResidentAttributes().

◆ ReadAttribute()

ULONG ReadAttribute ( PDEVICE_EXTENSION  Vcb,
PNTFS_ATTR_CONTEXT  Context,
ULONGLONG  Offset,
PCHAR  Buffer,
ULONG  Length 
)

Definition at line 1065 of file mft.c.

1070{
1071 ULONGLONG LastLCN;
1072 PUCHAR DataRun;
1073 LONGLONG DataRunOffset;
1074 ULONGLONG DataRunLength;
1075 LONGLONG DataRunStartLCN;
1076 ULONGLONG CurrentOffset;
1078 ULONG AlreadyRead;
1080
1081 //TEMPTEMP
1082 PUCHAR TempBuffer;
1083
1084 if (!Context->pRecord->IsNonResident)
1085 {
1086 // We need to truncate Offset to a ULONG for pointer arithmetic
1087 // The check below should ensure that Offset is well within the range of 32 bits
1088 ULONG LittleOffset = (ULONG)Offset;
1089
1090 // Ensure that offset isn't beyond the end of the attribute
1091 if (Offset > Context->pRecord->Resident.ValueLength)
1092 return 0;
1093 if (Offset + Length > Context->pRecord->Resident.ValueLength)
1094 Length = (ULONG)(Context->pRecord->Resident.ValueLength - Offset);
1095
1096 RtlCopyMemory(Buffer, (PVOID)((ULONG_PTR)Context->pRecord + Context->pRecord->Resident.ValueOffset + LittleOffset), Length);
1097 return Length;
1098 }
1099
1100 /*
1101 * Non-resident attribute
1102 */
1103
1104 /*
1105 * I. Find the corresponding start data run.
1106 */
1107
1108 AlreadyRead = 0;
1109
1110 // FIXME: Cache seems to be non-working. Disable it for now
1111 //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
1112 if (0)
1113 {
1114 DataRun = Context->CacheRun;
1115 LastLCN = Context->CacheRunLastLCN;
1116 DataRunStartLCN = Context->CacheRunStartLCN;
1117 DataRunLength = Context->CacheRunLength;
1118 CurrentOffset = Context->CacheRunCurrentOffset;
1119 }
1120 else
1121 {
1122 //TEMPTEMP
1123 ULONG UsedBufferSize;
1124 TempBuffer = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
1125 if (TempBuffer == NULL)
1126 {
1128 }
1129
1130 LastLCN = 0;
1131 CurrentOffset = 0;
1132
1133 // This will be rewritten in the next iteration to just use the DataRuns MCB directly
1134 ConvertLargeMCBToDataRuns(&Context->DataRunsMCB,
1135 TempBuffer,
1136 Vcb->NtfsInfo.BytesPerFileRecord,
1137 &UsedBufferSize);
1138
1139 DataRun = TempBuffer;
1140
1141 while (1)
1142 {
1143 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
1144 if (DataRunOffset != -1)
1145 {
1146 /* Normal data run. */
1147 DataRunStartLCN = LastLCN + DataRunOffset;
1148 LastLCN = DataRunStartLCN;
1149 }
1150 else
1151 {
1152 /* Sparse data run. */
1153 DataRunStartLCN = -1;
1154 }
1155
1156 if (Offset >= CurrentOffset &&
1157 Offset < CurrentOffset + (DataRunLength * Vcb->NtfsInfo.BytesPerCluster))
1158 {
1159 break;
1160 }
1161
1162 if (*DataRun == 0)
1163 {
1164 ExFreePoolWithTag(TempBuffer, TAG_NTFS);
1165 return AlreadyRead;
1166 }
1167
1168 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
1169 }
1170 }
1171
1172 /*
1173 * II. Go through the run list and read the data
1174 */
1175
1176 ReadLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset), Length);
1177 if (DataRunStartLCN == -1)
1178 {
1181 }
1182 else
1183 {
1184 Status = NtfsReadDisk(Vcb->StorageDevice,
1185 DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster + Offset - CurrentOffset,
1186 ReadLength,
1187 Vcb->NtfsInfo.BytesPerSector,
1188 (PVOID)Buffer,
1189 FALSE);
1190 }
1191 if (NT_SUCCESS(Status))
1192 {
1193 Length -= ReadLength;
1194 Buffer += ReadLength;
1195 AlreadyRead += ReadLength;
1196
1197 if (ReadLength == DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset))
1198 {
1199 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
1200 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
1201 if (DataRunOffset != (ULONGLONG)-1)
1202 {
1203 DataRunStartLCN = LastLCN + DataRunOffset;
1204 LastLCN = DataRunStartLCN;
1205 }
1206 else
1207 DataRunStartLCN = -1;
1208 }
1209
1210 while (Length > 0)
1211 {
1212 ReadLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster, Length);
1213 if (DataRunStartLCN == -1)
1215 else
1216 {
1217 Status = NtfsReadDisk(Vcb->StorageDevice,
1218 DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster,
1219 ReadLength,
1220 Vcb->NtfsInfo.BytesPerSector,
1221 (PVOID)Buffer,
1222 FALSE);
1223 if (!NT_SUCCESS(Status))
1224 break;
1225 }
1226
1227 Length -= ReadLength;
1228 Buffer += ReadLength;
1229 AlreadyRead += ReadLength;
1230
1231 /* We finished this request, but there still data in this data run. */
1232 if (Length == 0 && ReadLength != DataRunLength * Vcb->NtfsInfo.BytesPerCluster)
1233 break;
1234
1235 /*
1236 * Go to next run in the list.
1237 */
1238
1239 if (*DataRun == 0)
1240 break;
1241 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
1242 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
1243 if (DataRunOffset != -1)
1244 {
1245 /* Normal data run. */
1246 DataRunStartLCN = LastLCN + DataRunOffset;
1247 LastLCN = DataRunStartLCN;
1248 }
1249 else
1250 {
1251 /* Sparse data run. */
1252 DataRunStartLCN = -1;
1253 }
1254 } /* while */
1255
1256 } /* if Disk */
1257
1258 // TEMPTEMP
1259 if (Context->pRecord->IsNonResident)
1260 ExFreePoolWithTag(TempBuffer, TAG_NTFS);
1261
1262 Context->CacheRun = DataRun;
1263 Context->CacheRunOffset = Offset + AlreadyRead;
1264 Context->CacheRunStartLCN = DataRunStartLCN;
1265 Context->CacheRunLength = DataRunLength;
1266 Context->CacheRunLastLCN = LastLCN;
1267 Context->CacheRunCurrentOffset = CurrentOffset;
1268
1269 return AlreadyRead;
1270}
ULONG ReadLength
NTSTATUS ConvertLargeMCBToDataRuns(PLARGE_MCB DataRunsMCB, PUCHAR RunBuffer, ULONG MaxBufferSize, PULONG UsedBufferSize)
Definition: attrib.c:896
#define min(a, b)
Definition: monoChain.cc:55
NTSTATUS NtfsReadDisk(IN PDEVICE_OBJECT DeviceObject, IN LONGLONG StartingOffset, IN ULONG Length, IN ULONG SectorSize, IN OUT PUCHAR Buffer, IN BOOLEAN Override)
Definition: blockdev.c:37

Referenced by AddNewMftEntry(), AllocateIndexNode(), BrowseIndexEntries(), BrowseSubNodeIndexEntries(), CreateBTreeNodeFromIndexNode(), FreeClusters(), GetVolumeBitmap(), IncreaseMftSize(), InternalReadNonResidentAttributes(), NtfsAddFilenameToDirectory(), NtfsAllocateClusters(), NtfsFindMftRecord(), NtfsGetFreeClusters(), NtfsReadFCBAttribute(), NtfsReadFile(), PrintAllVCNs(), ReadFileRecord(), SetResidentAttributeDataLength(), UpdateFileNameRecord(), UpdateIndexEntryFileNameSize(), and UpdateMftMirror().

◆ ReadFileRecord()

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

Definition at line 1631 of file mft.c.

1634{
1636
1637 DPRINT("ReadFileRecord(%p, %I64x, %p)\n", Vcb, index, file);
1638
1639 BytesRead = ReadAttribute(Vcb, Vcb->MFTContext, index * Vcb->NtfsInfo.BytesPerFileRecord, (PCHAR)file, Vcb->NtfsInfo.BytesPerFileRecord);
1640 if (BytesRead != Vcb->NtfsInfo.BytesPerFileRecord)
1641 {
1642 DPRINT1("ReadFileRecord failed: %I64u read, %lu expected\n", BytesRead, Vcb->NtfsInfo.BytesPerFileRecord);
1643 return STATUS_PARTIAL_COPY;
1644 }
1645
1646 /* Apply update sequence array fixups. */
1647 DPRINT("Sequence number: %u\n", file->SequenceNumber);
1648 return FixupUpdateSequenceArray(Vcb, &file->Ntfs);
1649}
GLuint index
Definition: glext.h:6031
#define STATUS_PARTIAL_COPY
Definition: ntstatus.h:193
Definition: fci.c:127

Referenced by FindAttribute(), FreeClusters(), GetNtfsFileRecord(), GetVolumeBitmap(), NtfsAddFilenameToDirectory(), NtfsAllocateClusters(), NtfsCreateFile(), NtfsFindFileAt(), NtfsFindMftRecord(), NtfsGetFreeClusters(), NtfsGetSteamInformation(), NtfsGetVolumeData(), NtfsLookupFileAt(), NtfsMakeRootFCB(), NtfsMoonWalkID(), NtfsOpenFileById(), NtfsReadFCBAttribute(), NtfsReadFile(), NtfsSetEndOfFile(), NtfsWriteFile(), UpdateFileNameRecord(), UpdateMftMirror(), and WriteAttribute().

◆ ReadLCN()

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

Definition at line 2631 of file mft.c.

2635{
2636 LARGE_INTEGER DiskSector;
2637
2638 DiskSector.QuadPart = lcn;
2639
2640 return NtfsReadSectors(Vcb->StorageDevice,
2641 DiskSector.u.LowPart * Vcb->NtfsInfo.SectorsPerCluster,
2642 count * Vcb->NtfsInfo.SectorsPerCluster,
2643 Vcb->NtfsInfo.BytesPerSector,
2644 buffer,
2645 FALSE);
2646}
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:308
struct _LARGE_INTEGER::@2274 u

◆ ReleaseAttributeContext()

◆ 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 DPRINT1("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}
_In_ PFCB Fcb
Definition: cdprocs.h:159
VOID NTAPI CcSetFileSizes(IN PFILE_OBJECT FileObject, IN PCC_FILE_SIZES FileSizes)
Definition: fssup.c:356
#define STATUS_USER_MAPPED_FILE
Definition: ntstatus.h:711
BOOLEAN NTAPI MmCanFileBeTruncated(_In_ PSECTION_OBJECT_POINTERS SectionObjectPointer, _In_opt_ PLARGE_INTEGER NewFileSize)
Definition: section.c:4242
PVCB Vcb
Definition: cdstruc.h:933
FSRTL_COMMON_FCB_HEADER RFCB
Definition: ntfs.h:517
ULONGLONG MFTIndex
Definition: ntfs.h:539
LARGE_INTEGER AllocationSize
Definition: env_spec_w32.h:755
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(), and SetResidentAttributeDataLength().

◆ SetNonResidentAttributeDataLength()

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

Definition at line 756 of file mft.c.

761{
763 ULONG BytesPerCluster = Vcb->NtfsInfo.BytesPerCluster;
764 ULONGLONG AllocationSize = ROUND_UP(DataSize->QuadPart, BytesPerCluster);
765 PNTFS_ATTR_RECORD DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
766 ULONG ExistingClusters = AttrContext->pRecord->NonResident.AllocatedSize / BytesPerCluster;
767
768 ASSERT(AttrContext->pRecord->IsNonResident);
769
770 // do we need to increase the allocation size?
771 if (AttrContext->pRecord->NonResident.AllocatedSize < AllocationSize)
772 {
773 ULONG ClustersNeeded = (AllocationSize / BytesPerCluster) - ExistingClusters;
774 LARGE_INTEGER LastClusterInDataRun;
775 ULONG NextAssignedCluster;
776 ULONG AssignedClusters;
777
778 if (ExistingClusters == 0)
779 {
780 LastClusterInDataRun.QuadPart = 0;
781 }
782 else
783 {
784 if (!FsRtlLookupLargeMcbEntry(&AttrContext->DataRunsMCB,
785 (LONGLONG)AttrContext->pRecord->NonResident.HighestVCN,
786 (PLONGLONG)&LastClusterInDataRun.QuadPart,
787 NULL,
788 NULL,
789 NULL,
790 NULL))
791 {
792 DPRINT1("Error looking up final large MCB entry!\n");
793
794 // Most likely, HighestVCN went above the largest mapping
795 DPRINT1("Highest VCN of record: %I64u\n", AttrContext->pRecord->NonResident.HighestVCN);
797 }
798 }
799
800 DPRINT("LastClusterInDataRun: %I64u\n", LastClusterInDataRun.QuadPart);
801 DPRINT("Highest VCN of record: %I64u\n", AttrContext->pRecord->NonResident.HighestVCN);
802
803 while (ClustersNeeded > 0)
804 {
806 LastClusterInDataRun.LowPart + 1,
807 ClustersNeeded,
808 &NextAssignedCluster,
809 &AssignedClusters);
810
811 if (!NT_SUCCESS(Status))
812 {
813 DPRINT1("Error: Unable to allocate requested clusters!\n");
814 return Status;
815 }
816
817 // now we need to add the clusters we allocated to the data run
818 Status = AddRun(Vcb, AttrContext, AttrOffset, FileRecord, NextAssignedCluster, AssignedClusters);
819 if (!NT_SUCCESS(Status))
820 {
821 DPRINT1("Error: Unable to add data run!\n");
822 return Status;
823 }
824
825 ClustersNeeded -= AssignedClusters;
826 LastClusterInDataRun.LowPart = NextAssignedCluster + AssignedClusters - 1;
827 }
828 }
829 else if (AttrContext->pRecord->NonResident.AllocatedSize > AllocationSize)
830 {
831 // shrink allocation size
832 ULONG ClustersToFree = ExistingClusters - (AllocationSize / BytesPerCluster);
833 Status = FreeClusters(Vcb, AttrContext, AttrOffset, FileRecord, ClustersToFree);
834 }
835
836 // TODO: is the file compressed, encrypted, or sparse?
837
838 AttrContext->pRecord->NonResident.AllocatedSize = AllocationSize;
839 AttrContext->pRecord->NonResident.DataSize = DataSize->QuadPart;
840 AttrContext->pRecord->NonResident.InitializedSize = DataSize->QuadPart;
841
842 DestinationAttribute->NonResident.AllocatedSize = AllocationSize;
843 DestinationAttribute->NonResident.DataSize = DataSize->QuadPart;
844 DestinationAttribute->NonResident.InitializedSize = DataSize->QuadPart;
845
846 // HighestVCN seems to be set incorrectly somewhere. Apply a hack-fix to reset it.
847 // HACKHACK FIXME: Fix for sparse files; this math won't work in that case.
848 AttrContext->pRecord->NonResident.HighestVCN = ((ULONGLONG)AllocationSize / Vcb->NtfsInfo.BytesPerCluster) - 1;
849 DestinationAttribute->NonResident.HighestVCN = AttrContext->pRecord->NonResident.HighestVCN;
850
851 DPRINT("Allocated Size: %I64u\n", DestinationAttribute->NonResident.AllocatedSize);
852
853 return Status;
854}
NTSTATUS AddRun(PNTFS_VCB Vcb, PNTFS_ATTR_CONTEXT AttrContext, ULONG AttrOffset, PFILE_RECORD_HEADER FileRecord, ULONGLONG NextAssignedCluster, ULONG RunLength)
Definition: attrib.c:599
NTSTATUS FreeClusters(PNTFS_VCB Vcb, PNTFS_ATTR_CONTEXT AttrContext, ULONG AttrOffset, PFILE_RECORD_HEADER FileRecord, ULONG ClustersToFree)
Definition: attrib.c:1057
NTSTATUS NtfsAllocateClusters(PDEVICE_EXTENSION DeviceExt, ULONG FirstDesiredCluster, ULONG DesiredClusters, PULONG FirstAssignedCluster, PULONG AssignedClusters)
Definition: volinfo.c:105
#define ROUND_UP(n, align)
Definition: eventvwr.h:34
IN PFCB IN PFILE_OBJECT FileObject IN ULONG AllocationSize
Definition: fatprocs.h:322
BOOLEAN NTAPI FsRtlLookupLargeMcbEntry(IN PLARGE_MCB Mcb, IN LONGLONG Vbn, OUT PLONGLONG Lbn OPTIONAL, OUT PLONGLONG SectorCountFromLbn OPTIONAL, OUT PLONGLONG StartingLbn OPTIONAL, OUT PLONGLONG SectorCountFromStartingLbn OPTIONAL, OUT PULONG Index OPTIONAL)
Definition: largemcb.c:560
__GNU_EXTENSION typedef __int64 * PLONGLONG
Definition: ntbasedef.h:382

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 891 of file mft.c.

896{
898
899 // find the next attribute
900 ULONG NextAttributeOffset = AttrOffset + AttrContext->pRecord->Length;
901 PNTFS_ATTR_RECORD NextAttribute = (PNTFS_ATTR_RECORD)((PCHAR)FileRecord + NextAttributeOffset);
902
903 ASSERT(!AttrContext->pRecord->IsNonResident);
904
905 //NtfsDumpFileAttributes(Vcb, FileRecord);
906
907 // Do we need to increase the data length?
908 if (DataSize->QuadPart > AttrContext->pRecord->Resident.ValueLength)
909 {
910 // There's usually padding at the end of a record. Do we need to extend past it?
911 ULONG MaxValueLength = AttrContext->pRecord->Length - AttrContext->pRecord->Resident.ValueOffset;
912 if (MaxValueLength < DataSize->LowPart)
913 {
914 // If this is the last attribute, we could move the end marker to the very end of the file record
915 MaxValueLength += Vcb->NtfsInfo.BytesPerFileRecord - NextAttributeOffset - (sizeof(ULONG) * 2);
916
917 if (MaxValueLength < DataSize->LowPart || NextAttribute->Type != AttributeEnd)
918 {
919 // convert attribute to non-resident
920 PNTFS_ATTR_RECORD Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
921 PNTFS_ATTR_RECORD NewRecord;
922 LARGE_INTEGER AttribDataSize;
923 PVOID AttribData;
924 ULONG NewRecordLength;
925 ULONG EndAttributeOffset;
926 ULONG LengthWritten;
927
928 DPRINT1("Converting attribute to non-resident.\n");
929
930 AttribDataSize.QuadPart = AttrContext->pRecord->Resident.ValueLength;
931
932 // Is there existing data we need to back-up?
933 if (AttribDataSize.QuadPart > 0)
934 {
935 AttribData = ExAllocatePoolWithTag(NonPagedPool, AttribDataSize.QuadPart, TAG_NTFS);
936 if (AttribData == NULL)
937 {
938 DPRINT1("ERROR: Couldn't allocate memory for attribute data. Can't migrate to non-resident!\n");
940 }
941
942 // read data to temp buffer
943 Status = ReadAttribute(Vcb, AttrContext, 0, AttribData, AttribDataSize.QuadPart);
944 if (!NT_SUCCESS(Status))
945 {
946 DPRINT1("ERROR: Unable to read attribute before migrating!\n");
947 ExFreePoolWithTag(AttribData, TAG_NTFS);
948 return Status;
949 }
950 }
951
952 // Start by turning this attribute into a 0-length, non-resident attribute, then enlarge it.
953
954 // The size of a 0-length, non-resident attribute will be 0x41 + the size of the attribute name, aligned to an 8-byte boundary
955 NewRecordLength = ALIGN_UP_BY(0x41 + (AttrContext->pRecord->NameLength * sizeof(WCHAR)), ATTR_RECORD_ALIGNMENT);
956
957 // Create a new attribute record that will store the 0-length, non-resident attribute
958 NewRecord = ExAllocatePoolWithTag(NonPagedPool, NewRecordLength, TAG_NTFS);
959
960 // Zero out the NonResident structure
961 RtlZeroMemory(NewRecord, NewRecordLength);
962
963 // Copy the data that's common to both non-resident and resident attributes
964 RtlCopyMemory(NewRecord, AttrContext->pRecord, FIELD_OFFSET(NTFS_ATTR_RECORD, Resident.ValueLength));
965
966 // if there's a name
967 if (AttrContext->pRecord->NameLength != 0)
968 {
969 // copy the name
970 // An attribute name will be located at offset 0x18 for a resident attribute, 0x40 for non-resident
971 RtlCopyMemory((PCHAR)((ULONG_PTR)NewRecord + 0x40),
972 (PCHAR)((ULONG_PTR)AttrContext->pRecord + 0x18),
973 AttrContext->pRecord->NameLength * sizeof(WCHAR));
974 }
975
976 // update the mapping pairs offset, which will be 0x40 (size of a non-resident header) + length in bytes of the name
977 NewRecord->NonResident.MappingPairsOffset = 0x40 + (AttrContext->pRecord->NameLength * sizeof(WCHAR));
978
979 // update the end of the file record
980 // calculate position of end markers (1 byte for empty data run)
981 EndAttributeOffset = AttrOffset + NewRecord->NonResident.MappingPairsOffset + 1;
982 EndAttributeOffset = ALIGN_UP_BY(EndAttributeOffset, ATTR_RECORD_ALIGNMENT);
983
984 // Update the length
985 NewRecord->Length = EndAttributeOffset - AttrOffset;
986
987 ASSERT(NewRecord->Length == NewRecordLength);
988
989 // Copy the new attribute record into the file record
990 RtlCopyMemory(Destination, NewRecord, NewRecord->Length);
991
992 // Update the file record end
993 SetFileRecordEnd(FileRecord,
994 (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + EndAttributeOffset),
996
997 // Initialize the MCB, potentially catch an exception
999 {
1000 FsRtlInitializeLargeMcb(&AttrContext->DataRunsMCB, NonPagedPool);
1001 }
1003 {
1004 DPRINT1("Unable to create LargeMcb!\n");
1005 if (AttribDataSize.QuadPart > 0)
1006 ExFreePoolWithTag(AttribData, TAG_NTFS);
1007 ExFreePoolWithTag(NewRecord, TAG_NTFS);
1009 } _SEH2_END;
1010
1011 // Mark the attribute as non-resident (we wait until after we know the LargeMcb was initialized)
1012 NewRecord->IsNonResident = Destination->IsNonResident = 1;
1013
1014 // Update file record on disk
1015 Status = UpdateFileRecord(Vcb, AttrContext->FileMFTIndex, FileRecord);
1016 if (!NT_SUCCESS(Status))
1017 {
1018 DPRINT1("ERROR: Couldn't update file record to continue migration!\n");
1019 if (AttribDataSize.QuadPart > 0)
1020 ExFreePoolWithTag(AttribData, TAG_NTFS);
1021 ExFreePoolWithTag(NewRecord, TAG_NTFS);
1022 return Status;
1023 }
1024
1025 // Now we need to free the old copy of the attribute record in the context and replace it with the new one
1026 ExFreePoolWithTag(AttrContext->pRecord, TAG_NTFS);
1027 AttrContext->pRecord = NewRecord;
1028
1029 // Now we can treat the attribute as non-resident and enlarge it normally
1030 Status = SetNonResidentAttributeDataLength(Vcb, AttrContext, AttrOffset, FileRecord, DataSize);
1031 if (!NT_SUCCESS(Status))
1032 {
1033 DPRINT1("ERROR: Unable to migrate resident attribute!\n");
1034 if (AttribDataSize.QuadPart > 0)
1035 ExFreePoolWithTag(AttribData, TAG_NTFS);
1036 return Status;
1037 }
1038
1039 // restore the back-up attribute, if we made one
1040 if (AttribDataSize.QuadPart > 0)
1041 {
1042 Status = WriteAttribute(Vcb, AttrContext, 0, AttribData, AttribDataSize.QuadPart, &LengthWritten, FileRecord);
1043 if (!NT_SUCCESS(Status))
1044 {
1045 DPRINT1("ERROR: Unable to write attribute data to non-resident clusters during migration!\n");
1046 // TODO: Reverse migration so no data is lost
1047 ExFreePoolWithTag(AttribData, TAG_NTFS);
1048 return Status;
1049 }
1050
1051 ExFreePoolWithTag(AttribData, TAG_NTFS);
1052 }
1053 }
1054 }
1055 }
1056
1057 // set the new length of the resident attribute (if we didn't migrate it)
1058 if (!AttrContext->pRecord->IsNonResident)
1059 return InternalSetResidentAttributeLength(Vcb, AttrContext, FileRecord, AttrOffset, DataSize->LowPart);
1060
1061 return STATUS_SUCCESS;
1062}
#define _SEH2_END
Definition: filesup.c:22
#define _SEH2_TRY
Definition: filesup.c:19
#define EXCEPTION_EXECUTE_HANDLER
Definition: excpt.h:85
VOID NTAPI FsRtlInitializeLargeMcb(IN PLARGE_MCB Mcb, IN POOL_TYPE PoolType)
Definition: largemcb.c:451
#define _SEH2_GetExceptionCode()
Definition: pseh2_64.h:159
#define _SEH2_EXCEPT(...)
Definition: pseh2_64.h:34
#define _SEH2_YIELD(__stmt)
Definition: pseh2_64.h:162
ULONG ValueLength
Definition: ntfs.h:138

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

◆ UpdateFileNameRecord()

NTSTATUS UpdateFileNameRecord ( PDEVICE_EXTENSION  Vcb,
ULONGLONG  ParentMFTIndex,
PUNICODE_STRING  FileName,
BOOLEAN  DirSearch,
ULONGLONG  NewDataSize,
ULONGLONG  NewAllocationSize,
BOOLEAN  CaseSensitive 
)

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, updates Data Size and Allocated Size values in the $FILE_NAME attribute of that entry.

(Most of this code was copied from NtfsFindMftRecord)

Definition at line 1660 of file mft.c.

1667{
1668 PFILE_RECORD_HEADER MftRecord;
1669 PNTFS_ATTR_CONTEXT IndexRootCtx;
1670 PINDEX_ROOT_ATTRIBUTE IndexRoot;
1671 PCHAR IndexRecord;
1672 PINDEX_ENTRY_ATTRIBUTE IndexEntry, IndexEntryEnd;
1674 ULONG CurrentEntry = 0;
1675
1676 DPRINT("UpdateFileNameRecord(%p, %I64d, %wZ, %s, %I64u, %I64u, %s)\n",
1677 Vcb,
1678 ParentMFTIndex,
1679 FileName,
1680 DirSearch ? "TRUE" : "FALSE",
1681 NewDataSize,
1682 NewAllocationSize,
1683 CaseSensitive ? "TRUE" : "FALSE");
1684
1685 MftRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
1686 if (MftRecord == NULL)
1687 {
1689 }
1690
1691 Status = ReadFileRecord(Vcb, ParentMFTIndex, MftRecord);
1692 if (!NT_SUCCESS(Status))
1693 {
1694 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
1695 return Status;
1696 }
1697
1698 ASSERT(MftRecord->Ntfs.Type == NRH_FILE_TYPE);
1699 Status = FindAttribute(Vcb, MftRecord, AttributeIndexRoot, L"$I30", 4, &IndexRootCtx, NULL);
1700 if (!NT_SUCCESS(Status))
1701 {
1702 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
1703 return Status;
1704 }
1705
1706 IndexRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerIndexRecord, TAG_NTFS);
1707 if (IndexRecord == NULL)
1708 {
1709 ReleaseAttributeContext(IndexRootCtx);
1710 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
1712 }
1713
1714 Status = ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, AttributeDataLength(IndexRootCtx->pRecord));
1715 if (!NT_SUCCESS(Status))
1716 {
1717 DPRINT1("ERROR: Failed to read Index Root!\n");
1718 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
1719 ReleaseAttributeContext(IndexRootCtx);
1720 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
1721 return Status;
1722 }
1723
1724 IndexRoot = (PINDEX_ROOT_ATTRIBUTE)IndexRecord;
1725 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)&IndexRoot->Header + IndexRoot->Header.FirstEntryOffset);
1726 // Index root is always resident.
1727 IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)(IndexRecord + IndexRoot->Header.TotalSizeOfEntries);
1728
1729 DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb->NtfsInfo.BytesPerIndexRecord, IndexRoot->SizeOfEntry);
1730
1732 MftRecord,
1733 IndexRecord,
1734 IndexRoot->SizeOfEntry,
1735 IndexEntry,
1736 IndexEntryEnd,
1737 FileName,
1738 &CurrentEntry,
1739 &CurrentEntry,
1740 DirSearch,
1741 NewDataSize,
1742 NewAllocationSize,
1743 CaseSensitive);
1744
1745 if (Status == STATUS_PENDING)
1746 {
1747 // we need to write the index root attribute back to disk
1748 ULONG LengthWritten;
1749 Status = WriteAttribute(Vcb, IndexRootCtx, 0, (PUCHAR)IndexRecord, AttributeDataLength(IndexRootCtx->pRecord), &LengthWritten, MftRecord);
1750 if (!NT_SUCCESS(Status))
1751 {
1752 DPRINT1("ERROR: Couldn't update Index Root!\n");
1753 }
1754
1755 }
1756
1757 ReleaseAttributeContext(IndexRootCtx);
1758 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
1759 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
1760
1761 return Status;
1762}
NTSTATUS UpdateIndexEntryFileNameSize(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, ULONGLONG NewDataSize, ULONGLONG NewAllocatedSize, BOOLEAN CaseSensitive)
Definition: mft.c:1770
#define STATUS_PENDING
Definition: ntstatus.h:82

Referenced by NtfsSetEndOfFile(), and NtfsWriteFile().

◆ UpdateFileRecord()

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

Definition at line 1931 of file mft.c.

1934{
1937
1938 DPRINT("UpdateFileRecord(%p, 0x%I64x, %p)\n", Vcb, MftIndex, FileRecord);
1939
1940 // Add the fixup array to prepare the data for writing to disk
1941 AddFixupArray(Vcb, &FileRecord->Ntfs);
1942
1943 // write the file record to the master file table
1945 Vcb->MFTContext,
1946 MftIndex * Vcb->NtfsInfo.BytesPerFileRecord,
1947 (const PUCHAR)FileRecord,
1948 Vcb->NtfsInfo.BytesPerFileRecord,
1949 &BytesWritten,
1950 FileRecord);
1951
1952 if (!NT_SUCCESS(Status))
1953 {
1954 DPRINT1("UpdateFileRecord failed: %lu written, %lu expected\n", BytesWritten, Vcb->NtfsInfo.BytesPerFileRecord);
1955 }
1956
1957 // remove the fixup array (so the file record pointer can still be used)
1958 FixupUpdateSequenceArray(Vcb, &FileRecord->Ntfs);
1959
1960 return Status;
1961}
NTSTATUS AddFixupArray(PDEVICE_EXTENSION Vcb, PNTFS_RECORD_HEADER Record)
Definition: mft.c:2603
_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 AddNewMftEntry(), AddRun(), AllocateIndexNode(), FreeClusters(), IncreaseMftSize(), NtfsAddFilenameToDirectory(), SetAttributeDataLength(), SetResidentAttributeDataLength(), and WriteAttribute().

◆ UpdateIndexEntryFileNameSize()

NTSTATUS UpdateIndexEntryFileNameSize ( 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,
ULONGLONG  NewDataSize,
ULONGLONG  NewAllocatedSize,
BOOLEAN  CaseSensitive 
)

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

Definition at line 1770 of file mft.c.

1783{
1785 ULONG RecordOffset;
1786 PINDEX_ENTRY_ATTRIBUTE IndexEntry;
1787 PNTFS_ATTR_CONTEXT IndexAllocationCtx;
1788 ULONGLONG IndexAllocationSize;
1789 PINDEX_BUFFER IndexBuffer;
1790
1791 DPRINT("UpdateIndexEntrySize(%p, %p, %p, %lu, %p, %p, %wZ, %lu, %lu, %s, %I64u, %I64u, %s)\n",
1792 Vcb,
1793 MftRecord,
1794 IndexRecord,
1795 IndexBlockSize,
1796 FirstEntry,
1797 LastEntry,
1798 FileName,
1799 *StartEntry,
1800 *CurrentEntry,
1801 DirSearch ? "TRUE" : "FALSE",
1802 NewDataSize,
1803 NewAllocatedSize,
1804 CaseSensitive ? "TRUE" : "FALSE");
1805
1806 // find the index entry responsible for the file we're trying to update
1807 IndexEntry = FirstEntry;
1808 while (IndexEntry < LastEntry &&
1809 !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
1810 {
1811 if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) > NTFS_FILE_FIRST_USER_FILE &&
1812 *CurrentEntry >= *StartEntry &&
1813 IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS &&
1814 CompareFileName(FileName, IndexEntry, DirSearch, CaseSensitive))
1815 {
1816 *StartEntry = *CurrentEntry;
1817 IndexEntry->FileName.DataSize = NewDataSize;
1818 IndexEntry->FileName.AllocatedSize = NewAllocatedSize;
1819 // indicate that the caller will still need to write the structure to the disk
1820 return STATUS_PENDING;
1821 }
1822
1823 (*CurrentEntry) += 1;
1824 ASSERT(IndexEntry->Length >= sizeof(INDEX_ENTRY_ATTRIBUTE));
1825 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
1826 }
1827
1828 /* If we're already browsing a subnode */
1829 if (IndexRecord == NULL)
1830 {
1832 }
1833
1834 /* If there's no subnode */
1835 if (!(IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE))
1836 {
1838 }
1839
1840 Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationCtx, NULL);
1841 if (!NT_SUCCESS(Status))
1842 {
1843 DPRINT("Corrupted filesystem!\n");
1844 return Status;
1845 }
1846
1847 IndexAllocationSize = AttributeDataLength(IndexAllocationCtx->pRecord);
1849 for (RecordOffset = 0; RecordOffset < IndexAllocationSize; RecordOffset += IndexBlockSize)
1850 {
1851 ReadAttribute(Vcb, IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize);
1852 Status = FixupUpdateSequenceArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs);
1853 if (!NT_SUCCESS(Status))
1854 {
1855 break;
1856 }
1857
1858 IndexBuffer = (PINDEX_BUFFER)IndexRecord;
1859 ASSERT(IndexBuffer->Ntfs.Type == NRH_INDX_TYPE);
1860 ASSERT(IndexBuffer->Header.AllocatedSize + FIELD_OFFSET(INDEX_BUFFER, Header) == IndexBlockSize);
1861 FirstEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.FirstEntryOffset);
1862 LastEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.TotalSizeOfEntries);
1863 ASSERT(LastEntry <= (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexBuffer + IndexBlockSize));
1864
1866 NULL,
1867 NULL,
1868 0,
1869 FirstEntry,
1870 LastEntry,
1871 FileName,
1872 StartEntry,
1873 CurrentEntry,
1874 DirSearch,
1875 NewDataSize,
1876 NewAllocatedSize,
1877 CaseSensitive);
1878 if (Status == STATUS_PENDING)
1879 {
1880 // write the index record back to disk
1881 ULONG Written;
1882
1883 // first we need to update the fixup values for the index block
1884 Status = AddFixupArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs);
1885 if (!NT_SUCCESS(Status))
1886 {
1887 DPRINT1("Error: Failed to update fixup sequence array!\n");
1888 break;
1889 }
1890
1891 Status = WriteAttribute(Vcb, IndexAllocationCtx, RecordOffset, (const PUCHAR)IndexRecord, IndexBlockSize, &Written, MftRecord);
1892 if (!NT_SUCCESS(Status))
1893 {
1894 DPRINT1("ERROR Performing write!\n");
1895 break;
1896 }
1897
1899 break;
1900 }
1901 if (NT_SUCCESS(Status))
1902 {
1903 break;
1904 }
1905 }
1906
1907 ReleaseAttributeContext(IndexAllocationCtx);
1908 return Status;
1909}
struct INDEX_BUFFER * PINDEX_BUFFER
ULONGLONG DataSize
Definition: ntfs.h:366
ULONGLONG AllocatedSize
Definition: ntfs.h:365

Referenced by UpdateFileNameRecord(), and UpdateIndexEntryFileNameSize().

◆ UpdateMftMirror()

NTSTATUS UpdateMftMirror ( PNTFS_VCB  Vcb)

Definition at line 2714 of file mft.c.

2715{
2716 PFILE_RECORD_HEADER MirrorFileRecord;
2717 PNTFS_ATTR_CONTEXT MirrDataContext;
2718 PNTFS_ATTR_CONTEXT MftDataContext;
2719 PCHAR DataBuffer;
2723 ULONG LengthWritten;
2724
2725 // Allocate memory for the Mft mirror file record
2726 MirrorFileRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
2727 if (!MirrorFileRecord)
2728 {
2729 DPRINT1("Error: Failed to allocate memory for $MFTMirr!\n");
2731 }
2732
2733 // Read the Mft Mirror file record
2734 Status = ReadFileRecord(Vcb, NTFS_FILE_MFTMIRR, MirrorFileRecord);
2735 if (!NT_SUCCESS(Status))
2736 {
2737 DPRINT1("ERROR: Failed to read $MFTMirr!\n");
2738 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MirrorFileRecord);
2739 return Status;
2740 }
2741
2742 // Find the $DATA attribute of $MFTMirr
2743 Status = FindAttribute(Vcb, MirrorFileRecord, AttributeData, L"", 0, &MirrDataContext, NULL);
2744 if (!NT_SUCCESS(Status))
2745 {
2746 DPRINT1("ERROR: Couldn't find $DATA attribute!\n");
2747 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MirrorFileRecord);
2748 return Status;
2749 }
2750
2751 // Find the $DATA attribute of $MFT
2752 Status = FindAttribute(Vcb, Vcb->MasterFileTable, AttributeData, L"", 0, &MftDataContext, NULL);
2753 if (!NT_SUCCESS(Status))
2754 {
2755 DPRINT1("ERROR: Couldn't find $DATA attribute!\n");
2756 ReleaseAttributeContext(MirrDataContext);
2757 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MirrorFileRecord);
2758 return Status;
2759 }
2760
2761 // Get the size of the mirror's $DATA attribute
2762 DataLength = AttributeDataLength(MirrDataContext->pRecord);
2763
2764 ASSERT(DataLength % Vcb->NtfsInfo.BytesPerFileRecord == 0);
2765
2766 // Create buffer for the mirror's $DATA attribute
2768 if (!DataBuffer)
2769 {
2770 DPRINT1("Error: Couldn't allocate memory for $DATA buffer!\n");
2771 ReleaseAttributeContext(MftDataContext);
2772 ReleaseAttributeContext(MirrDataContext);
2773 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MirrorFileRecord);
2775 }
2776
2778
2779 // Back up the first several entries of the Mft's $DATA Attribute
2780 BytesRead = ReadAttribute(Vcb, MftDataContext, 0, DataBuffer, (ULONG)DataLength);
2781 if (BytesRead != (ULONG)DataLength)
2782 {
2783 DPRINT1("Error: Failed to read $DATA for $MFTMirr!\n");
2784 ReleaseAttributeContext(MftDataContext);
2785 ReleaseAttributeContext(MirrDataContext);
2786 ExFreePoolWithTag(DataBuffer, TAG_NTFS);
2787 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MirrorFileRecord);
2788 return STATUS_UNSUCCESSFUL;
2789 }
2790
2791 // Write the mirror's $DATA attribute
2793 MirrDataContext,
2794 0,
2795 (PUCHAR)DataBuffer,
2796 DataLength,
2797 &LengthWritten,
2798 MirrorFileRecord);
2799 if (!NT_SUCCESS(Status))
2800 {
2801 DPRINT1("ERROR: Failed to write $DATA attribute of $MFTMirr!\n");
2802 }
2803
2804 // Cleanup
2805 ReleaseAttributeContext(MftDataContext);
2806 ReleaseAttributeContext(MirrDataContext);
2807 ExFreePoolWithTag(DataBuffer, TAG_NTFS);
2808 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MirrorFileRecord);
2809
2810 return Status;
2811}
#define NTFS_FILE_MFTMIRR
Definition: ntfs.h:24
_In_ ULONG _In_opt_ WDFREQUEST _In_opt_ PVOID _In_ size_t _In_ PVOID _In_ size_t _Out_ size_t * DataLength
Definition: cdrom.h:1444
@ AttributeData
Definition: ntfs.h:168
#define ULONG_MAX
Definition: limits.h:44

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 1315 of file mft.c.

1322{
1323 ULONGLONG LastLCN;
1324 PUCHAR DataRun;
1325 LONGLONG DataRunOffset;
1326 ULONGLONG DataRunLength;
1327 LONGLONG DataRunStartLCN;
1328 ULONGLONG CurrentOffset;
1331 PUCHAR SourceBuffer = Buffer;
1333 BOOLEAN FileRecordAllocated = FALSE;
1334
1335 //TEMPTEMP
1336 PUCHAR TempBuffer;
1337
1338
1339 DPRINT("WriteAttribute(%p, %p, %I64u, %p, %lu, %p, %p)\n", Vcb, Context, Offset, Buffer, Length, RealLengthWritten, FileRecord);
1340
1341 *RealLengthWritten = 0;
1342
1343 // is this a resident attribute?
1344 if (!Context->pRecord->IsNonResident)
1345 {
1346 ULONG AttributeOffset;
1347 PNTFS_ATTR_CONTEXT FoundContext;
1349
1350 // Ensure requested data is within the bounds of the attribute
1351 ASSERT(Offset + Length <= Context->pRecord->Resident.ValueLength);
1352
1353 if (Offset + Length > Context->pRecord->Resident.ValueLength)
1354 {
1355 DPRINT1("DRIVER ERROR: Attribute is too small!\n");
1357 }
1358
1359 // Do we need to read the file record?
1360 if (FileRecord == NULL)
1361 {
1362 FileRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
1363 if (!FileRecord)
1364 {
1365 DPRINT1("Error: Couldn't allocate file record!\n");
1366 return STATUS_NO_MEMORY;
1367 }
1368
1369 FileRecordAllocated = TRUE;
1370
1371 // read the file record
1372 ReadFileRecord(Vcb, Context->FileMFTIndex, FileRecord);
1373 }
1374
1375 // find where to write the attribute data to
1376 Status = FindAttribute(Vcb, FileRecord,
1377 Context->pRecord->Type,
1378 (PCWSTR)((ULONG_PTR)Context->pRecord + Context->pRecord->NameOffset),
1379 Context->pRecord->NameLength,
1380 &FoundContext,
1381 &AttributeOffset);
1382
1383 if (!NT_SUCCESS(Status))
1384 {
1385 DPRINT1("ERROR: Couldn't find matching attribute!\n");
1386 if(FileRecordAllocated)
1387 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
1388 return Status;
1389 }
1390
1391 Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttributeOffset);
1392
1393 DPRINT("Offset: %I64u, AttributeOffset: %u, ValueOffset: %u\n", Offset, AttributeOffset, Context->pRecord->Resident.ValueLength);
1394
1395 // Will we be writing past the end of the allocated file record?
1396 if (Offset + Length + AttributeOffset + Context->pRecord->Resident.ValueOffset > Vcb->NtfsInfo.BytesPerFileRecord)
1397 {
1398 DPRINT1("DRIVER ERROR: Data being written extends past end of file record!\n");
1399 ReleaseAttributeContext(FoundContext);
1400 if (FileRecordAllocated)
1401 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
1403 }
1404
1405 // copy the data being written into the file record. We cast Offset to ULONG, which is safe because it's range has been verified.
1406 RtlCopyMemory((PCHAR)((ULONG_PTR)Destination + Context->pRecord->Resident.ValueOffset + (ULONG)Offset), Buffer, Length);
1407
1408 Status = UpdateFileRecord(Vcb, Context->FileMFTIndex, FileRecord);
1409
1410 // Update the context's copy of the resident attribute
1411 ASSERT(Context->pRecord->Length == Destination->Length);
1412 RtlCopyMemory((PVOID)Context->pRecord, Destination, Context->pRecord->Length);
1413
1414 ReleaseAttributeContext(FoundContext);
1415 if (FileRecordAllocated)
1416 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
1417
1418 if (NT_SUCCESS(Status))
1419 *RealLengthWritten = Length;
1420
1421 return Status;
1422 }
1423
1424 // This is a non-resident attribute.
1425
1426 // I. Find the corresponding start data run.
1427
1428 // FIXME: Cache seems to be non-working. Disable it for now
1429 //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
1430 /*if (0)
1431 {
1432 DataRun = Context->CacheRun;
1433 LastLCN = Context->CacheRunLastLCN;
1434 DataRunStartLCN = Context->CacheRunStartLCN;
1435 DataRunLength = Context->CacheRunLength;
1436 CurrentOffset = Context->CacheRunCurrentOffset;
1437 }
1438 else*/
1439 {
1440 ULONG UsedBufferSize;
1441 LastLCN = 0;
1442 CurrentOffset = 0;
1443
1444 // This will be rewritten in the next iteration to just use the DataRuns MCB directly
1445 TempBuffer = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
1446 if (TempBuffer == NULL)
1447 {
1449 }
1450
1451 ConvertLargeMCBToDataRuns(&Context->DataRunsMCB,
1452 TempBuffer,
1453 Vcb->NtfsInfo.BytesPerFileRecord,
1454 &UsedBufferSize);
1455
1456 DataRun = TempBuffer;
1457
1458 while (1)
1459 {
1460 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
1461 if (DataRunOffset != -1)
1462 {
1463 // Normal data run.
1464 // DPRINT1("Writing to normal data run, LastLCN %I64u DataRunOffset %I64d\n", LastLCN, DataRunOffset);
1465 DataRunStartLCN = LastLCN + DataRunOffset;
1466 LastLCN = DataRunStartLCN;
1467 }
1468 else
1469 {
1470 // Sparse data run. We can't support writing to sparse files yet
1471 // (it may require increasing the allocation size).
1472 DataRunStartLCN = -1;
1473 DPRINT1("FIXME: Writing to sparse files is not supported yet!\n");
1475 goto Cleanup;
1476 }
1477
1478 // Have we reached the data run we're trying to write to?
1479 if (Offset >= CurrentOffset &&
1480 Offset < CurrentOffset + (DataRunLength * Vcb->NtfsInfo.BytesPerCluster))
1481 {
1482 break;
1483 }
1484
1485 if (*DataRun == 0)
1486 {
1487 // We reached the last assigned cluster
1488 // TODO: assign new clusters to the end of the file.
1489 // (Presently, this code will rarely be reached, the write will usually have already failed by now)
1490 // [We can reach here by creating a new file record when the MFT isn't large enough]
1491 DPRINT1("FIXME: Master File Table needs to be enlarged.\n");
1493 goto Cleanup;
1494 }
1495
1496 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
1497 }
1498 }
1499
1500 // II. Go through the run list and write the data
1501
1502 /* REVIEWME -- As adapted from NtfsReadAttribute():
1503 We seem to be making a special case for the first applicable data run, but I'm not sure why.
1504 Does it have something to do with (not) caching? Is this strategy equally applicable to writing? */
1505
1506 WriteLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset), Length);
1507
1508 StartingOffset = DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster + Offset - CurrentOffset;
1509
1510 // Write the data to the disk
1511 Status = NtfsWriteDisk(Vcb->StorageDevice,
1514 Vcb->NtfsInfo.BytesPerSector,
1515 (PVOID)SourceBuffer);
1516
1517 // Did the write fail?
1518 if (!NT_SUCCESS(Status))
1519 {
1520 Context->CacheRun = DataRun;
1521 Context->CacheRunOffset = Offset;
1522 Context->CacheRunStartLCN = DataRunStartLCN;
1523 Context->CacheRunLength = DataRunLength;
1524 Context->CacheRunLastLCN = LastLCN;
1525 Context->CacheRunCurrentOffset = CurrentOffset;
1526
1527 goto Cleanup;
1528 }
1529
1531 SourceBuffer += WriteLength;
1532 *RealLengthWritten += WriteLength;
1533
1534 // Did we write to the end of the data run?
1535 if (WriteLength == DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset))
1536 {
1537 // Advance to the next data run
1538 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
1539 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
1540
1541 if (DataRunOffset != (ULONGLONG)-1)
1542 {
1543 DataRunStartLCN = LastLCN + DataRunOffset;
1544 LastLCN = DataRunStartLCN;
1545 }
1546 else
1547 DataRunStartLCN = -1;
1548 }
1549
1550 // Do we have more data to write?
1551 while (Length > 0)
1552 {
1553 // Make sure we don't write past the end of the current data run
1554 WriteLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster, Length);
1555
1556 // Are we dealing with a sparse data run?
1557 if (DataRunStartLCN == -1)
1558 {
1559 DPRINT1("FIXME: Don't know how to write to sparse files yet! (DataRunStartLCN == -1)\n");
1561 goto Cleanup;
1562 }
1563 else
1564 {
1565 // write the data to the disk
1566 Status = NtfsWriteDisk(Vcb->StorageDevice,
1567 DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster,
1569 Vcb->NtfsInfo.BytesPerSector,
1570 (PVOID)SourceBuffer);
1571 if (!NT_SUCCESS(Status))
1572 break;
1573 }
1574
1576 SourceBuffer += WriteLength;
1577 *RealLengthWritten += WriteLength;
1578
1579 // We finished this request, but there's still data in this data run.
1580 if (Length == 0 && WriteLength != DataRunLength * Vcb->NtfsInfo.BytesPerCluster)
1581 break;
1582
1583 // Go to next run in the list.
1584
1585 if (*DataRun == 0)
1586 {
1587 // that was the last run
1588 if (Length > 0)
1589 {
1590 // Failed sanity check.
1591 DPRINT1("Encountered EOF before expected!\n");
1593 goto Cleanup;
1594 }
1595
1596 break;
1597 }
1598
1599 // Advance to the next data run
1600 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
1601 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
1602 if (DataRunOffset != -1)
1603 {
1604 // Normal data run.
1605 DataRunStartLCN = LastLCN + DataRunOffset;
1606 LastLCN = DataRunStartLCN;
1607 }
1608 else
1609 {
1610 // Sparse data run.
1611 DataRunStartLCN = -1;
1612 }
1613 } // end while (Length > 0) [more data to write]
1614
1615 Context->CacheRun = DataRun;
1616 Context->CacheRunOffset = Offset + *RealLengthWritten;
1617 Context->CacheRunStartLCN = DataRunStartLCN;
1618 Context->CacheRunLength = DataRunLength;
1619 Context->CacheRunLastLCN = LastLCN;
1620 Context->CacheRunCurrentOffset = CurrentOffset;
1621
1622Cleanup:
1623 // TEMPTEMP
1624 if (Context->pRecord->IsNonResident)
1625 ExFreePoolWithTag(TempBuffer, TAG_NTFS);
1626
1627 return Status;
1628}
ULONG WriteLength
Definition: CcPinRead_drv.c:40
_In_ PFCB _In_ LONGLONG StartingOffset
Definition: cdprocs.h:291
static const WCHAR Cleanup[]
Definition: register.c:80
NTSTATUS NtfsWriteDisk(IN PDEVICE_OBJECT DeviceObject, IN LONGLONG StartingOffset, IN ULONG Length, IN ULONG SectorSize, IN const PUCHAR Buffer)
Definition: blockdev.c:163
#define STATUS_NO_MEMORY
Definition: ntstatus.h:260
#define STATUS_END_OF_FILE
Definition: shellext.h:67
const uint16_t * PCWSTR
Definition: typedefs.h:57

Referenced by AddNewMftEntry(), AllocateIndexNode(), FreeClusters(), IncreaseMftSize(), NtfsAddFilenameToDirectory(), NtfsAllocateClusters(), NtfsWriteFile(), SetResidentAttributeDataLength(), UpdateFileNameRecord(), UpdateFileRecord(), UpdateIndexEntryFileNameSize(), UpdateIndexNode(), and UpdateMftMirror().