https://git.reactos.org/?p=reactos.git;a=commitdiff;h=0409b3161e8789a4c9aa606616626e1237427766

commit 0409b3161e8789a4c9aa606616626e1237427766
Author: Trevor Thompson <[email protected]>
AuthorDate: Sun Apr 16 00:17:07 2017 +0000

    [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 b65a6b8b50..2a70e8c3c8 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 344c16464f..30312d6104 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 30ef5f0e3e..8b588d760b 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 ce8a7df2de..5a36bcc9fe 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

Reply via email to