From 0409b3161e8789a4c9aa606616626e1237427766 Mon Sep 17 00:00:00 2001 From: Trevor Thompson Date: Sun, 16 Apr 2017 00:17:07 +0000 Subject: [PATCH] [NTFS] Add support for creating new MFT entries: +AddStandardInformation(), +AddData(), +AddFileName() - Add attributes to a file record +NtfsCreateFileRecord() - Creates a new file record and saves it to the MFT. +AddNewMftEntry() - Adds a file record to the MFT. NtfsCreateFile() - Modified to create a file record on a file-creation request (file creation is still unsupported; the created file needs to be added to the parent's directory index). +SetFileRecordEnd() - Establishes a new file record size UpdateFileRecord() - Improved documentation InternalSetResidentAttributeLength() - Updated to use SetFileRecordEnd(). svn path=/branches/GSoC_2016/NTFS/; revision=74321 --- drivers/filesystems/ntfs/attrib.c | 242 ++++++++++++++++++++++++++++++ drivers/filesystems/ntfs/create.c | 96 +++++++++++- drivers/filesystems/ntfs/mft.c | 189 +++++++++++++++++++++-- drivers/filesystems/ntfs/ntfs.h | 38 ++++- 4 files changed, 545 insertions(+), 20 deletions(-) diff --git a/drivers/filesystems/ntfs/attrib.c b/drivers/filesystems/ntfs/attrib.c index b65a6b8b50e..2a70e8c3c8f 100644 --- a/drivers/filesystems/ntfs/attrib.c +++ b/drivers/filesystems/ntfs/attrib.c @@ -35,6 +35,185 @@ /* FUNCTIONS ****************************************************************/ +/** +* @name AddData +* @implemented +* +* Adds a $DATA attribute to a given FileRecord. +* +* @param FileRecord +* Pointer to a complete file record to add the attribute to. Caller is responsible for +* ensuring FileRecord is large enough to contain $DATA. +* +* @param AttributeAddress +* Pointer to the region of memory that will receive the $DATA attribute. +* This address must reside within FileRecord. Must be aligned to an 8-byte boundary (relative to FileRecord). +* +* @return +* STATUS_SUCCESS on success. STATUS_NOT_IMPLEMENTED if target address isn't at the end +* of the given file record. +* +* @remarks +* Only adding the attribute to the end of the file record is supported; AttributeAddress must +* be of type AttributeEnd. +* As it's implemented, this function is only intended to assist in creating new file records. It +* could be made more general-purpose by considering file records with an $ATTRIBUTE_LIST. +* It's the caller's responsibility to ensure the given file record has enough memory allocated +* for the attribute. +*/ +NTSTATUS +AddData(PFILE_RECORD_HEADER FileRecord, + PNTFS_ATTR_RECORD AttributeAddress) +{ + ULONG ResidentHeaderLength = FIELD_OFFSET(NTFS_ATTR_RECORD, Resident.Reserved) + sizeof(UCHAR); + ULONG FileRecordEnd = AttributeAddress->Length; + + if (AttributeAddress->Type != AttributeEnd) + { + DPRINT1("FIXME: Can only add $DATA attribute to the end of a file record.\n"); + return STATUS_NOT_IMPLEMENTED; + } + + AttributeAddress->Type = AttributeData; + AttributeAddress->Length = ResidentHeaderLength; + AttributeAddress->Length = ALIGN_UP_BY(AttributeAddress->Length, 8); + AttributeAddress->Resident.ValueLength = 0; + AttributeAddress->Resident.ValueOffset = ResidentHeaderLength; + + // for unnamed $DATA attributes, NameOffset equals header length + AttributeAddress->NameOffset = ResidentHeaderLength; + AttributeAddress->Instance = FileRecord->NextAttributeNumber++; + + // move the attribute-end and file-record-end markers to the end of the file record + AttributeAddress = (PNTFS_ATTR_RECORD)((ULONG_PTR)AttributeAddress + AttributeAddress->Length); + SetFileRecordEnd(FileRecord, AttributeAddress, FileRecordEnd); + + return STATUS_SUCCESS; +} + +/** +* @name AddFileName +* @implemented +* +* Adds a $FILE_NAME attribute to a given FileRecord. +* +* @param FileRecord +* Pointer to a complete file record to add the attribute to. Caller is responsible for +* ensuring FileRecord is large enough to contain $FILE_NAME. +* +* @param AttributeAddress +* Pointer to the region of memory that will receive the $FILE_NAME attribute. +* This address must reside within FileRecord. Must be aligned to an 8-byte boundary (relative to FileRecord). +* +* @param DeviceExt +* Points to the target disk's DEVICE_EXTENSION. +* +* @param FileObject +* Pointer to the FILE_OBJECT which represents the new name. +* This parameter is used to determine the filename and parent directory. +* +* @return +* STATUS_SUCCESS on success. STATUS_NOT_IMPLEMENTED if target address isn't at the end +* of the given file record. +* +* @remarks +* Only adding the attribute to the end of the file record is supported; AttributeAddress must +* be of type AttributeEnd. +* As it's implemented, this function is only intended to assist in creating new file records. It +* could be made more general-purpose by considering file records with an $ATTRIBUTE_LIST. +* It's the caller's responsibility to ensure the given file record has enough memory allocated +* for the attribute. +*/ +NTSTATUS +AddFileName(PFILE_RECORD_HEADER FileRecord, + PNTFS_ATTR_RECORD AttributeAddress, + PDEVICE_EXTENSION DeviceExt, + PFILE_OBJECT FileObject) +{ + ULONG ResidentHeaderLength = FIELD_OFFSET(NTFS_ATTR_RECORD, Resident.Reserved) + sizeof(UCHAR); + PFILENAME_ATTRIBUTE FileNameAttribute; + LARGE_INTEGER SystemTime; + ULONG FileRecordEnd = AttributeAddress->Length; + ULONGLONG CurrentMFTIndex = NTFS_FILE_ROOT; + UNICODE_STRING Current, Remaining; + NTSTATUS Status = STATUS_SUCCESS; + ULONG FirstEntry = 0; + + if (AttributeAddress->Type != AttributeEnd) + { + DPRINT1("FIXME: Can only add $FILE_NAME attribute to the end of a file record.\n"); + return STATUS_NOT_IMPLEMENTED; + } + + AttributeAddress->Type = AttributeFileName; + AttributeAddress->Instance = FileRecord->NextAttributeNumber++; + + FileNameAttribute = (PFILENAME_ATTRIBUTE)((LONG_PTR)AttributeAddress + ResidentHeaderLength); + + // set timestamps + KeQuerySystemTime(&SystemTime); + FileNameAttribute->CreationTime = SystemTime.QuadPart; + FileNameAttribute->ChangeTime = SystemTime.QuadPart; + FileNameAttribute->LastWriteTime = SystemTime.QuadPart; + FileNameAttribute->LastAccessTime = SystemTime.QuadPart; + + FileNameAttribute->FileAttributes = NTFS_FILE_TYPE_ARCHIVE; + + // we need to extract the filename from the path + DPRINT1("Pathname: %wZ\n", &FileObject->FileName); + + FsRtlDissectName(FileObject->FileName, &Current, &Remaining); + + while (Current.Length != 0) + { + DPRINT1("Current: %wZ\n", &Current); + + Status = NtfsFindMftRecord(DeviceExt, CurrentMFTIndex, &Current, &FirstEntry, FALSE, &CurrentMFTIndex); + if (!NT_SUCCESS(Status)) + { + break; + } + + if (Remaining.Length == 0) + break; + + FsRtlDissectName(Current, &Current, &Remaining); + } + + DPRINT1("MFT Index of parent: %I64u\n", CurrentMFTIndex); + + // set reference to parent directory + FileNameAttribute->DirectoryFileReferenceNumber = CurrentMFTIndex; + + // The highest 2 bytes should be the sequence number, unless the parent happens to be root + if (CurrentMFTIndex == NTFS_FILE_ROOT) + FileNameAttribute->DirectoryFileReferenceNumber |= (ULONGLONG)NTFS_FILE_ROOT << 48; + else + FileNameAttribute->DirectoryFileReferenceNumber |= (ULONGLONG)FileRecord->SequenceNumber << 48; + + DPRINT1("FileNameAttribute->DirectoryFileReferenceNumber: 0x%I64x\n", FileNameAttribute->DirectoryFileReferenceNumber); + + FileNameAttribute->NameLength = Current.Length / 2; + // TODO: Get proper nametype, add DOS links as needed + FileNameAttribute->NameType = NTFS_FILE_NAME_WIN32_AND_DOS; + RtlCopyMemory(FileNameAttribute->Name, Current.Buffer, Current.Length); + FileRecord->LinkCount++; + + AttributeAddress->Length = ResidentHeaderLength + + FIELD_OFFSET(FILENAME_ATTRIBUTE, Name) + Current.Length; + AttributeAddress->Length = ALIGN_UP_BY(AttributeAddress->Length, 8); + + AttributeAddress->Resident.ValueLength = FIELD_OFFSET(FILENAME_ATTRIBUTE, Name) + Current.Length; + AttributeAddress->Resident.ValueOffset = ResidentHeaderLength; + AttributeAddress->Resident.Flags = 1; // indexed + + // move the attribute-end and file-record-end markers to the end of the file record + AttributeAddress = (PNTFS_ATTR_RECORD)((ULONG_PTR)AttributeAddress + AttributeAddress->Length); + SetFileRecordEnd(FileRecord, AttributeAddress, FileRecordEnd); + + return Status; +} + /** * @name AddRun * @implemented @@ -192,6 +371,69 @@ AddRun(PNTFS_VCB Vcb, return Status; } +/** +* @name AddStandardInformation +* @implemented +* +* Adds a $STANDARD_INFORMATION attribute to a given FileRecord. +* +* @param FileRecord +* Pointer to a complete file record to add the attribute to. Caller is responsible for +* ensuring FileRecord is large enough to contain $STANDARD_INFORMATION. +* +* @param AttributeAddress +* Pointer to the region of memory that will receive the $STANDARD_INFORMATION attribute. +* This address must reside within FileRecord. Must be aligned to an 8-byte boundary (relative to FileRecord). +* +* @return +* STATUS_SUCCESS on success. STATUS_NOT_IMPLEMENTED if target address isn't at the end +* of the given file record. +* +* @remarks +* Only adding the attribute to the end of the file record is supported; AttributeAddress must +* be of type AttributeEnd. +* As it's implemented, this function is only intended to assist in creating new file records. It +* could be made more general-purpose by considering file records with an $ATTRIBUTE_LIST. +* It's the caller's responsibility to ensure the given file record has enough memory allocated +* for the attribute. +*/ +NTSTATUS +AddStandardInformation(PFILE_RECORD_HEADER FileRecord, + PNTFS_ATTR_RECORD AttributeAddress) +{ + ULONG ResidentHeaderLength = FIELD_OFFSET(NTFS_ATTR_RECORD, Resident.Reserved) + sizeof(UCHAR); + PSTANDARD_INFORMATION StandardInfo = (PSTANDARD_INFORMATION)((LONG_PTR)AttributeAddress + ResidentHeaderLength); + LARGE_INTEGER SystemTime; + ULONG FileRecordEnd = AttributeAddress->Length; + + if (AttributeAddress->Type != AttributeEnd) + { + DPRINT1("FIXME: Can only add $STANDARD_INFORMATION attribute to the end of a file record.\n"); + return STATUS_NOT_IMPLEMENTED; + } + + AttributeAddress->Type = AttributeStandardInformation; + AttributeAddress->Length = sizeof(STANDARD_INFORMATION) + ResidentHeaderLength; + AttributeAddress->Length = ALIGN_UP_BY(AttributeAddress->Length, 8); + AttributeAddress->Resident.ValueLength = sizeof(STANDARD_INFORMATION); + AttributeAddress->Resident.ValueOffset = ResidentHeaderLength; + AttributeAddress->Instance = FileRecord->NextAttributeNumber++; + + // set dates and times + KeQuerySystemTime(&SystemTime); + StandardInfo->CreationTime = SystemTime.QuadPart; + StandardInfo->ChangeTime = SystemTime.QuadPart; + StandardInfo->LastWriteTime = SystemTime.QuadPart; + StandardInfo->LastAccessTime = SystemTime.QuadPart; + StandardInfo->FileAttribute = NTFS_FILE_TYPE_ARCHIVE; + + // move the attribute-end and file-record-end markers to the end of the file record + AttributeAddress = (PNTFS_ATTR_RECORD)((ULONG_PTR)AttributeAddress + AttributeAddress->Length); + SetFileRecordEnd(FileRecord, AttributeAddress, FileRecordEnd); + + return STATUS_SUCCESS; +} + /** * @name ConvertDataRunsToLargeMCB * @implemented diff --git a/drivers/filesystems/ntfs/create.c b/drivers/filesystems/ntfs/create.c index 344c16464ff..30312d61043 100644 --- a/drivers/filesystems/ntfs/create.c +++ b/drivers/filesystems/ntfs/create.c @@ -545,8 +545,15 @@ NtfsCreateFile(PDEVICE_OBJECT DeviceObject, RequestedDisposition == FILE_OPEN_IF || RequestedDisposition == FILE_OVERWRITE_IF || RequestedDisposition == FILE_SUPERSEDE) - { - DPRINT1("Denying file creation request on NTFS volume\n"); + { + // Create the file record on disk + Status = NtfsCreateFileRecord(DeviceExt, FileObject); + + // Update the parent directory index + // Still TODO + + // Call NtfsOpenFile() + return STATUS_CANNOT_MAKE; } } @@ -599,4 +606,89 @@ NtfsCreate(PNTFS_IRP_CONTEXT IrpContext) return Status; } +/** +* @name NtfsCreateFileRecord() +* @implemented +* +* Creates a file record and saves it to the MFT. +* +* @param DeviceExt +* Points to the target disk's DEVICE_EXTENSION +* +* @param FileObject +* Pointer to a FILE_OBJECT describing the file to be created +* +* @return +* STATUS_SUCCESS on success. +* STATUS_INSUFFICIENT_RESOURCES if unable to allocate memory for the file record. +*/ +NTSTATUS +NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt, + PFILE_OBJECT FileObject) +{ + NTSTATUS Status = STATUS_SUCCESS; + PFILE_RECORD_HEADER FileRecord; + PNTFS_ATTR_RECORD NextAttribute; + + // allocate memory for file record + FileRecord = ExAllocatePoolWithTag(NonPagedPool, + DeviceExt->NtfsInfo.BytesPerFileRecord, + TAG_NTFS); + if (!FileRecord) + { + DPRINT1("ERROR: Unable to allocate memory for file record!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory(FileRecord, DeviceExt->NtfsInfo.BytesPerFileRecord); + + FileRecord->Ntfs.Type = NRH_FILE_TYPE; + + // calculate USA offset and count + FileRecord->Ntfs.UsaOffset = FIELD_OFFSET(FILE_RECORD_HEADER, MFTRecordNumber) + sizeof(ULONG); + + // size of USA (in ULONG's) will be 1 (for USA number) + 1 for every sector the file record uses + FileRecord->BytesAllocated = DeviceExt->NtfsInfo.BytesPerFileRecord; + FileRecord->Ntfs.UsaCount = (FileRecord->BytesAllocated / DeviceExt->NtfsInfo.BytesPerSector) + 1; + + // setup other file record fields + FileRecord->SequenceNumber = 1; + FileRecord->AttributeOffset = FileRecord->Ntfs.UsaOffset + (2 * FileRecord->Ntfs.UsaCount); + FileRecord->AttributeOffset = ALIGN_UP_BY(FileRecord->AttributeOffset, 8); + FileRecord->Flags = FRH_IN_USE; + FileRecord->BytesInUse = FileRecord->AttributeOffset + sizeof(ULONG) * 2; + + // find where the first attribute will be added + NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset); + + // mark the (temporary) end of the file-record + NextAttribute->Type = AttributeEnd; + NextAttribute->Length = FILE_RECORD_END; + + // add first attribute, $STANDARD_INFORMATION + AddStandardInformation(FileRecord, NextAttribute); + + // advance NextAttribute pointer to the next attribute + NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length); + + // Add the $FILE_NAME attribute + AddFileName(FileRecord, NextAttribute, DeviceExt, FileObject); + + // advance NextAttribute pointer to the next attribute + NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length); + + // add the $DATA attribute + AddData(FileRecord, NextAttribute); + + // dump file record in memory (for debugging) + NtfsDumpFileRecord(DeviceExt, FileRecord); + + // Now that we've built the file record in memory, we need to store it in the MFT. + Status = AddNewMftEntry(FileRecord, DeviceExt); + + ExFreePoolWithTag(FileRecord, TAG_NTFS); + + return Status; +} + /* EOF */ diff --git a/drivers/filesystems/ntfs/mft.c b/drivers/filesystems/ntfs/mft.c index 30ef5f0e3ea..8b588d760b6 100644 --- a/drivers/filesystems/ntfs/mft.c +++ b/drivers/filesystems/ntfs/mft.c @@ -172,7 +172,7 @@ AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord) return AttrRecord->Resident.ValueLength; } -void +VOID InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext, PFILE_RECORD_HEADER FileRecord, ULONG AttrOffset, @@ -201,14 +201,9 @@ InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext, Destination->Length += Padding; } - // advance Destination to the final "attribute" and write the end type + // advance Destination to the final "attribute" and set the file record end Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)Destination + Destination->Length); - Destination->Type = AttributeEnd; - - // write the final marker (which shares the same offset and type as the Length field) - Destination->Length = FILE_RECORD_END; - - FileRecord->BytesInUse = NextAttributeOffset + (sizeof(ULONG) * 2); + SetFileRecordEnd(FileRecord, Destination, FILE_RECORD_END); } /** @@ -359,6 +354,41 @@ SetAttributeDataLength(PFILE_OBJECT FileObject, return STATUS_SUCCESS; } +/** +* @name 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. +* +* @param FileRecord +* Pointer to the file record whose endpoint (length) will be set. +* +* @param AttrEnd +* Pointer 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). +* +* @param EndMarker +* This 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) +{ + // mark the end of attributes + AttrEnd->Type = AttributeEnd; + + // Restore the "file-record-end marker." The value is never checked but this behavior is consistent with Win2k3. + AttrEnd->Length = EndMarker; + + // recalculate bytes in use + FileRecord->BytesInUse = (ULONG_PTR)AttrEnd - (ULONG_PTR)FileRecord + sizeof(ULONG) * 2; +} + ULONG ReadAttribute(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT Context, @@ -711,7 +741,9 @@ WriteAttribute(PDEVICE_EXTENSION Vcb, { // We reached the last assigned cluster // TODO: assign new clusters to the end of the file. - // (Presently, this code will never be reached, the write should have already failed by now) + // (Presently, this code will rarely be reached, the write will usually have already failed by now) + // [We can reach here by creating a new file record when the MFT isn't large enough] + DPRINT1("FIXME: Master File Table needs to be enlarged.\n"); return STATUS_END_OF_FILE; } @@ -1070,25 +1102,39 @@ UpdateIndexEntryFileNameSize(PDEVICE_EXTENSION Vcb, } /** -* UpdateFileRecord +* @name UpdateFileRecord * @implemented +* * Writes a file record to the master file table, at a given index. +* +* @param Vcb +* Pointer to the DEVICE_EXTENSION of the target drive being written to. +* +* @param MftIndex +* Target index in the master file table to store the file record. +* +* @param FileRecord +* Pointer to the complete file record which will be written to the master file table. +* +* @return +* STATUS_SUCCESSFUL on success. An error passed from WriteAttribute() otherwise. +* */ NTSTATUS UpdateFileRecord(PDEVICE_EXTENSION Vcb, - ULONGLONG index, - PFILE_RECORD_HEADER file) + ULONGLONG MftIndex, + PFILE_RECORD_HEADER FileRecord) { ULONG BytesWritten; NTSTATUS Status = STATUS_SUCCESS; - DPRINT("UpdateFileRecord(%p, %I64x, %p)\n", Vcb, index, file); + DPRINT("UpdateFileRecord(%p, 0x%I64x, %p)\n", Vcb, MftIndex, FileRecord); // Add the fixup array to prepare the data for writing to disk - AddFixupArray(Vcb, &file->Ntfs); + AddFixupArray(Vcb, &FileRecord->Ntfs); // write the file record to the master file table - Status = WriteAttribute(Vcb, Vcb->MFTContext, index * Vcb->NtfsInfo.BytesPerFileRecord, (const PUCHAR)file, Vcb->NtfsInfo.BytesPerFileRecord, &BytesWritten); + Status = WriteAttribute(Vcb, Vcb->MFTContext, MftIndex * Vcb->NtfsInfo.BytesPerFileRecord, (const PUCHAR)FileRecord, Vcb->NtfsInfo.BytesPerFileRecord, &BytesWritten); if (!NT_SUCCESS(Status)) { @@ -1096,7 +1142,7 @@ UpdateFileRecord(PDEVICE_EXTENSION Vcb, } // remove the fixup array (so the file record pointer can still be used) - FixupUpdateSequenceArray(Vcb, &file->Ntfs); + FixupUpdateSequenceArray(Vcb, &FileRecord->Ntfs); return Status; } @@ -1133,6 +1179,117 @@ FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb, return STATUS_SUCCESS; } +/** +* @name AddNewMftEntry +* @implemented +* +* Adds a file record to the master file table of a given device. +* +* @param FileRecord +* Pointer to a complete file record which will be saved to disk. +* +* @param DeviceExt +* Pointer to the DEVICE_EXTENSION of the target drive. +* +* @return +* 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_NOT_IMPLEMENTED if we need to increase the size of the MFT. +* +*/ +NTSTATUS +AddNewMftEntry(PFILE_RECORD_HEADER FileRecord, + PDEVICE_EXTENSION DeviceExt) +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONGLONG MftIndex; + RTL_BITMAP Bitmap; + ULONGLONG BitmapDataSize; + ULONGLONG AttrBytesRead; + PVOID BitmapData; + ULONG LengthWritten; + + // First, we have to read the mft's $Bitmap attribute + PNTFS_ATTR_CONTEXT BitmapContext; + Status = FindAttribute(DeviceExt, DeviceExt->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT1("ERROR: Couldn't find $Bitmap attribute of master file table!\n"); + return Status; + } + + // allocate a buffer for the $Bitmap attribute + BitmapDataSize = AttributeDataLength(&BitmapContext->Record); + BitmapData = ExAllocatePoolWithTag(NonPagedPool, BitmapDataSize, TAG_NTFS); + if (!BitmapData) + { + ReleaseAttributeContext(BitmapContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // read $Bitmap attribute + AttrBytesRead = ReadAttribute(DeviceExt, BitmapContext, 0, BitmapData, BitmapDataSize); + + if (AttrBytesRead == 0) + { + DPRINT1("ERROR: Unable to read $Bitmap attribute of master file table!\n"); + ExFreePoolWithTag(BitmapData, TAG_NTFS); + ReleaseAttributeContext(BitmapContext); + return STATUS_OBJECT_NAME_NOT_FOUND; + } + + // convert buffer into bitmap + RtlInitializeBitMap(&Bitmap, (PULONG)BitmapData, BitmapDataSize * 8); + + // set next available bit, preferrably after 23rd bit + MftIndex = RtlFindClearBitsAndSet(&Bitmap, 1, 24); + if ((LONG)MftIndex == -1) + { + DPRINT1("ERROR: Couldn't find free space in MFT for file record!\n"); + + ExFreePoolWithTag(BitmapData, TAG_NTFS); + ReleaseAttributeContext(BitmapContext); + + // TODO: increase mft size + return STATUS_NOT_IMPLEMENTED; + } + + DPRINT1("Creating file record at MFT index: %I64u\n", MftIndex); + + // update file record with index + FileRecord->MFTRecordNumber = MftIndex; + + // [BitmapData should have been updated via RtlFindClearBitsAndSet()] + + // write the bitmap back to the MFT's $Bitmap attribute + Status = WriteAttribute(DeviceExt, BitmapContext, 0, BitmapData, BitmapDataSize, &LengthWritten); + if (!NT_SUCCESS(Status)) + { + DPRINT1("ERROR encountered when writing $Bitmap attribute!\n"); + ExFreePoolWithTag(BitmapData, TAG_NTFS); + ReleaseAttributeContext(BitmapContext); + return Status; + } + + // update the file record (write it to disk) + Status = UpdateFileRecord(DeviceExt, MftIndex, FileRecord); + + if (!NT_SUCCESS(Status)) + { + DPRINT1("ERROR: Unable to write file record!\n"); + ExFreePoolWithTag(BitmapData, TAG_NTFS); + ReleaseAttributeContext(BitmapContext); + return Status; + } + + ExFreePoolWithTag(BitmapData, TAG_NTFS); + ReleaseAttributeContext(BitmapContext); + + return Status; +} + NTSTATUS AddFixupArray(PDEVICE_EXTENSION Vcb, PNTFS_RECORD_HEADER Record) diff --git a/drivers/filesystems/ntfs/ntfs.h b/drivers/filesystems/ntfs/ntfs.h index ce8a7df2de6..5a36bcc9fe9 100644 --- a/drivers/filesystems/ntfs/ntfs.h +++ b/drivers/filesystems/ntfs/ntfs.h @@ -516,6 +516,10 @@ NtfsMarkIrpContextForQueue(PNTFS_IRP_CONTEXT IrpContext) //VOID //NtfsDumpAttribute(PATTRIBUTE Attribute); +NTSTATUS +AddData(PFILE_RECORD_HEADER FileRecord, + PNTFS_ATTR_RECORD AttributeAddress); + NTSTATUS AddRun(PNTFS_VCB Vcb, PNTFS_ATTR_CONTEXT AttrContext, @@ -524,6 +528,16 @@ AddRun(PNTFS_VCB Vcb, ULONGLONG NextAssignedCluster, ULONG RunLength); +NTSTATUS +AddFileName(PFILE_RECORD_HEADER FileRecord, + PNTFS_ATTR_RECORD AttributeAddress, + PDEVICE_EXTENSION DeviceExt, + PFILE_OBJECT FileObject); + +NTSTATUS +AddStandardInformation(PFILE_RECORD_HEADER FileRecord, + PNTFS_ATTR_RECORD AttributeAddress); + NTSTATUS ConvertDataRunsToLargeMCB(PUCHAR DataRun, PLARGE_MCB DataRunsMCB, @@ -647,6 +661,9 @@ NtfsClose(PNTFS_IRP_CONTEXT IrpContext); NTSTATUS NtfsCreate(PNTFS_IRP_CONTEXT IrpContext); +NTSTATUS +NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt, + PFILE_OBJECT FileObject); /* devctl.c */ @@ -786,6 +803,10 @@ NtfsFileSystemControl(PNTFS_IRP_CONTEXT IrpContext); /* mft.c */ +NTSTATUS +AddNewMftEntry(PFILE_RECORD_HEADER FileRecord, + PDEVICE_EXTENSION DeviceExt); + PNTFS_ATTR_CONTEXT PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord); @@ -818,6 +839,11 @@ SetAttributeDataLength(PFILE_OBJECT FileObject, PFILE_RECORD_HEADER FileRecord, PLARGE_INTEGER DataSize); +VOID +SetFileRecordEnd(PFILE_RECORD_HEADER FileRecord, + PNTFS_ATTR_RECORD AttrEnd, + ULONG EndMarker); + ULONGLONG AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord); @@ -855,8 +881,8 @@ UpdateFileNameRecord(PDEVICE_EXTENSION Vcb, NTSTATUS UpdateFileRecord(PDEVICE_EXTENSION Vcb, - ULONGLONG index, - PFILE_RECORD_HEADER file); + ULONGLONG MftIndex, + PFILE_RECORD_HEADER FileRecord); NTSTATUS FindAttribute(PDEVICE_EXTENSION Vcb, @@ -919,6 +945,14 @@ NtfsFindFileAt(PDEVICE_EXTENSION Vcb, PULONGLONG MFTIndex, ULONGLONG CurrentMFTIndex); +NTSTATUS +NtfsFindMftRecord(PDEVICE_EXTENSION Vcb, + ULONGLONG MFTIndex, + PUNICODE_STRING FileName, + PULONG FirstEntry, + BOOLEAN DirSearch, + ULONGLONG *OutMFTIndex); + /* misc.c */ BOOLEAN -- 2.17.1