[NTFS] - Add the most basic support for file creation. Expand diagnostic output,...
authorTrevor Thompson <tmt256@email.vccs.edu>
Fri, 9 Jun 2017 03:14:30 +0000 (03:14 +0000)
committerThomas Faber <thomas.faber@reactos.org>
Sun, 10 Dec 2017 10:14:26 +0000 (11:14 +0100)
AddFileName() - Add a parameter to receive the Mft index of the parent directory. Fix so the name of the file will be stored in the attribute, not the name of the directory.
NtfsCreateFile() - Open a file that was successfully created, instead of assuming failure.
NtfsCreateFileRecord() - Add the filename attribute of the created file to the parent directory's index.
+NtfsAddFilenameToDirectory() - Adds a $FILE_NAME attribute to a given directory index. Presently, a file can be created in an empty directory only.
AddNewMftEntry() - Add a parameter to receive the mft index where the new entry was stored.

svn path=/branches/GSoC_2016/NTFS/; revision=74970

drivers/filesystems/ntfs/attrib.c
drivers/filesystems/ntfs/create.c
drivers/filesystems/ntfs/dirctl.c
drivers/filesystems/ntfs/mft.c
drivers/filesystems/ntfs/ntfs.h

index bb678e3..eeed581 100644 (file)
@@ -112,6 +112,9 @@ AddData(PFILE_RECORD_HEADER FileRecord,
 * Pointer to the FILE_OBJECT which represents the new name.
 * This parameter is used to determine the filename and parent directory.
 *
+* @param ParentMftIndex
+* Pointer to a ULONGLONG which will receive the index of the parent directory.
+*
 * @return
 * STATUS_SUCCESS on success. STATUS_NOT_IMPLEMENTED if target address isn't at the end
 * of the given file record.
@@ -128,16 +131,18 @@ NTSTATUS
 AddFileName(PFILE_RECORD_HEADER FileRecord,
             PNTFS_ATTR_RECORD AttributeAddress,
             PDEVICE_EXTENSION DeviceExt,
-            PFILE_OBJECT FileObject)
+            PFILE_OBJECT FileObject,
+            PULONGLONG ParentMftIndex)
 {
     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;
+    UNICODE_STRING Current, Remaining, FilenameNoPath;
     NTSTATUS Status = STATUS_SUCCESS;
     ULONG FirstEntry = 0;
+    WCHAR Buffer[MAX_PATH];
 
     if (AttributeAddress->Type != AttributeEnd)
     {
@@ -162,20 +167,29 @@ AddFileName(PFILE_RECORD_HEADER FileRecord,
     // we need to extract the filename from the path
     DPRINT1("Pathname: %wZ\n", &FileObject->FileName);
 
+    RtlZeroMemory(&FilenameNoPath, sizeof(UNICODE_STRING));
+    FilenameNoPath.Buffer = Buffer;
+    FilenameNoPath.MaximumLength = MAX_PATH;
+
     FsRtlDissectName(FileObject->FileName, &Current, &Remaining);
 
     while (Current.Length != 0)
     {
         DPRINT1("Current: %wZ\n", &Current);
 
+        if(Remaining.Length != 0)
+            RtlCopyUnicodeString(&FilenameNoPath, &Remaining);
+
         Status = NtfsFindMftRecord(DeviceExt, CurrentMFTIndex, &Current, &FirstEntry, FALSE, &CurrentMFTIndex);
         if (!NT_SUCCESS(Status))
-        {
             break;
-        }
 
-        if (Remaining.Length == 0)
+        if (Remaining.Length == 0 )
+        {
+            if(Current.Length != 0)
+                RtlCopyUnicodeString(&FilenameNoPath, &Current);
             break;
+        }
 
         FsRtlDissectName(Current, &Current, &Remaining);
     }
@@ -184,6 +198,9 @@ AddFileName(PFILE_RECORD_HEADER FileRecord,
 
     // set reference to parent directory
     FileNameAttribute->DirectoryFileReferenceNumber = CurrentMFTIndex;
+    *ParentMftIndex = CurrentMFTIndex;
+
+    DPRINT1("SequenceNumber: 0x%02x\n", FileRecord->SequenceNumber);
 
     // The highest 2 bytes should be the sequence number, unless the parent happens to be root
     if (CurrentMFTIndex == NTFS_FILE_ROOT)
@@ -191,19 +208,19 @@ AddFileName(PFILE_RECORD_HEADER FileRecord,
     else
         FileNameAttribute->DirectoryFileReferenceNumber |= (ULONGLONG)FileRecord->SequenceNumber << 48;
 
-    DPRINT1("FileNameAttribute->DirectoryFileReferenceNumber: 0x%I64x\n", FileNameAttribute->DirectoryFileReferenceNumber);
+    DPRINT1("FileNameAttribute->DirectoryFileReferenceNumber: 0x%016I64x\n", FileNameAttribute->DirectoryFileReferenceNumber);
 
-    FileNameAttribute->NameLength = Current.Length / 2;
+    FileNameAttribute->NameLength = FilenameNoPath.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);
+    RtlCopyMemory(FileNameAttribute->Name, FilenameNoPath.Buffer, FilenameNoPath.Length);
     FileRecord->LinkCount++;
 
     AttributeAddress->Length = ResidentHeaderLength +
-        FIELD_OFFSET(FILENAME_ATTRIBUTE, Name) + Current.Length;
+        FIELD_OFFSET(FILENAME_ATTRIBUTE, Name) + FilenameNoPath.Length;
     AttributeAddress->Length = ALIGN_UP_BY(AttributeAddress->Length, 8);
 
-    AttributeAddress->Resident.ValueLength = FIELD_OFFSET(FILENAME_ATTRIBUTE, Name) + Current.Length;
+    AttributeAddress->Resident.ValueLength = FIELD_OFFSET(FILENAME_ATTRIBUTE, Name) + FilenameNoPath.Length;
     AttributeAddress->Resident.ValueOffset = ResidentHeaderLength;
     AttributeAddress->Resident.Flags = 1; // indexed
 
@@ -1054,6 +1071,7 @@ NtfsDumpFileNameAttribute(PNTFS_ATTR_RECORD Attribute)
     DbgPrint(" (%x) '%.*S' ", FileNameAttr->NameType, FileNameAttr->NameLength, FileNameAttr->Name);
     DbgPrint(" '%x' \n", FileNameAttr->FileAttributes);
     DbgPrint(" AllocatedSize: %I64u\nDataSize: %I64u\n", FileNameAttr->AllocatedSize, FileNameAttr->DataSize);
+    DbgPrint(" File reference: 0x%016I64x\n", FileNameAttr->DirectoryFileReferenceNumber);
 }
 
 
@@ -1110,23 +1128,73 @@ VOID
 NtfsDumpIndexRootAttribute(PNTFS_ATTR_RECORD Attribute)
 {
     PINDEX_ROOT_ATTRIBUTE IndexRootAttr;
+    ULONG currentOffset;
+    ULONG currentNode;
 
     IndexRootAttr = (PINDEX_ROOT_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
 
     if (IndexRootAttr->AttributeType == AttributeFileName)
         ASSERT(IndexRootAttr->CollationRule == COLLATION_FILE_NAME);
 
-    DbgPrint("  $INDEX_ROOT (%uB, %u) ", IndexRootAttr->SizeOfEntry, IndexRootAttr->ClustersPerIndexRecord);
+    DbgPrint("  $INDEX_ROOT (%u bytes per index record, %u clusters) ", IndexRootAttr->SizeOfEntry, IndexRootAttr->ClustersPerIndexRecord);
 
     if (IndexRootAttr->Header.Flags == INDEX_ROOT_SMALL)
     {
-        DbgPrint(" (small) ");
+        DbgPrint(" (small)\n");
     }
     else
     {
         ASSERT(IndexRootAttr->Header.Flags == INDEX_ROOT_LARGE);
-        DbgPrint(" (large) ");
+        DbgPrint(" (large)\n");
     }
+
+    DbgPrint("   Offset to first index: 0x%lx\n   Total size of index entries: 0x%lx\n   Allocated size of node: 0x%lx\n",
+             IndexRootAttr->Header.FirstEntryOffset,
+             IndexRootAttr->Header.TotalSizeOfEntries,
+             IndexRootAttr->Header.AllocatedSize);
+    currentOffset = IndexRootAttr->Header.FirstEntryOffset;
+    currentNode = 0;
+    // print details of every node in the index
+    while (currentOffset < IndexRootAttr->Header.TotalSizeOfEntries)
+    {
+        PINDEX_ENTRY_ATTRIBUTE currentIndexExtry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexRootAttr + 0x10 + currentOffset);
+        DbgPrint("   Index Node Entry %u", currentNode++);
+        if (currentIndexExtry->Flags & NTFS_INDEX_ENTRY_NODE)
+            DbgPrint(" (Branch)");
+        else
+            DbgPrint(" (Leaf)");
+        if((currentIndexExtry->Flags & NTFS_INDEX_ENTRY_END))
+        {
+            DbgPrint(" (Dummy Key)");
+        }
+        DbgPrint("\n    File Reference: 0x%016I64x\n", currentIndexExtry->Data.Directory.IndexedFile);
+        DbgPrint("    Index Entry Length: 0x%x\n", currentIndexExtry->Length);
+        DbgPrint("    Index Key Length: 0x%x\n", currentIndexExtry->KeyLength);
+
+        // if this isn't the final (dummy) node, print info about the key (Filename attribute)
+        if (!(currentIndexExtry->Flags & NTFS_INDEX_ENTRY_END))
+        {
+            UNICODE_STRING Name;
+            DbgPrint("     Parent File Reference: 0x%016I64x\n", currentIndexExtry->FileName.DirectoryFileReferenceNumber);
+            DbgPrint("     $FILENAME indexed: ");
+            Name.Length = currentIndexExtry->FileName.NameLength * sizeof(WCHAR);
+            Name.MaximumLength = Name.Length;
+            Name.Buffer = currentIndexExtry->FileName.Name;
+            DbgPrint("'%wZ'\n", &Name);
+        }
+
+        // if this node has a sub-node beneath it
+        if (currentIndexExtry->Flags & NTFS_INDEX_ENTRY_NODE)
+        {
+            // Print the VCN of the sub-node
+            PULONGLONG SubNodeVCN = (PULONGLONG)((ULONG_PTR)currentIndexExtry + currentIndexExtry->Length - 8);
+            DbgPrint("    VCN of sub-node: 0x%llx\n", *SubNodeVCN);
+        }
+        
+        currentOffset += currentIndexExtry->Length;
+        ASSERT(currentIndexExtry->Length);
+    }
+
 }
 
 
index 6513827..ef39b44 100644 (file)
@@ -523,7 +523,7 @@ NtfsCreateFile(PDEVICE_OBJECT DeviceObject,
            
         DoneOverwriting:
             if (fileRecord)
-                ExFreePool(fileRecord);
+                ExFreePoolWithTag(fileRecord, TAG_NTFS);
             if (dataContext)
                 ReleaseAttributeContext(dataContext);
 
@@ -562,13 +562,14 @@ NtfsCreateFile(PDEVICE_OBJECT DeviceObject,
 
             // Create the file record on disk
             Status = NtfsCreateFileRecord(DeviceExt, FileObject);
+            if (!NT_SUCCESS(Status))
+            {
+                DPRINT1("ERROR: Couldn't create file record!\n");
+                return Status;
+            }
 
-            // Update the parent directory index
-            // Still TODO
-
-            // Call NtfsOpenFile()
-
-            return STATUS_CANNOT_MAKE;
+            // Now we should be able to open the file
+            return NtfsCreateFile(DeviceObject, Irp);
         }
     }
 
@@ -624,7 +625,8 @@ NtfsCreate(PNTFS_IRP_CONTEXT IrpContext)
 * @name NtfsCreateFileRecord()
 * @implemented
 *
-* Creates a file record and saves it to the MFT.
+* Creates a file record and saves it to the MFT. Adds the filename attribute of the
+* created file to the parent directory's index.
 *
 * @param DeviceExt
 * Points to the target disk's DEVICE_EXTENSION
@@ -643,6 +645,9 @@ NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt,
     NTSTATUS Status = STATUS_SUCCESS;
     PFILE_RECORD_HEADER FileRecord;
     PNTFS_ATTR_RECORD NextAttribute;
+    PFILENAME_ATTRIBUTE FilenameAttribute;
+    ULONGLONG ParentMftIndex;
+    ULONGLONG FileMftIndex;
 
     // allocate memory for file record
     FileRecord = ExAllocatePoolWithTag(NonPagedPool,
@@ -686,7 +691,10 @@ NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt,
     NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length);
 
     // Add the $FILE_NAME attribute
-    AddFileName(FileRecord, NextAttribute, DeviceExt, FileObject);
+    AddFileName(FileRecord, NextAttribute, DeviceExt, FileObject, &ParentMftIndex);
+
+    // save a pointer to the filename attribute
+    FilenameAttribute = (PFILENAME_ATTRIBUTE)((ULONG_PTR)NextAttribute + NextAttribute->Resident.ValueOffset);
 
     // advance NextAttribute pointer to the next attribute
     NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length);
@@ -698,7 +706,23 @@ NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt,
     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);
+    Status = AddNewMftEntry(FileRecord, DeviceExt, &FileMftIndex);
+    if (NT_SUCCESS(Status))
+    {
+        // The highest 2 bytes should be the sequence number, unless the parent happens to be root
+        if (FileMftIndex == NTFS_FILE_ROOT)
+            FileMftIndex = FileMftIndex + ((ULONGLONG)NTFS_FILE_ROOT << 48);
+        else
+            FileMftIndex = FileMftIndex + ((ULONGLONG)FileRecord->SequenceNumber << 48);
+
+        DPRINT1("New File Reference: 0x%016I64x\n", FileMftIndex);
+
+        // Add the filename attribute to the filename-index of the parent directory
+        Status = NtfsAddFilenameToDirectory(DeviceExt,
+                                            ParentMftIndex,
+                                            FileMftIndex,
+                                            FilenameAttribute);
+    }
 
     ExFreePoolWithTag(FileRecord, TAG_NTFS);
 
index aaa6dfc..91f0bc5 100644 (file)
 
 /* FUNCTIONS ****************************************************************/
 
+/**
+* @name NtfsAddFilenameToDirectory
+* @implemented
+*
+* Adds a $FILE_NAME attribute to a given directory index.
+*
+* @param DeviceExt
+* Points to the target disk's DEVICE_EXTENSION.
+*
+* @param DirectoryMftIndex
+* Mft index of the parent directory which will receive the file.
+*
+* @param FileReferenceNumber
+* File reference of the file to be added to the directory. This is a combination of the 
+* Mft index and sequence number.
+*
+* @param FilenameAttribute
+* Pointer to the FILENAME_ATTRIBUTE of the file being added to the directory.
+*
+* @return
+* 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 an empty 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)
+{
+    NTSTATUS Status = STATUS_SUCCESS;
+    PFILE_RECORD_HEADER ParentFileRecord;
+    PNTFS_ATTR_CONTEXT DirectoryContext;
+    PINDEX_ROOT_ATTRIBUTE I30IndexRoot;
+    ULONG IndexRootOffset;
+    ULONGLONG I30IndexRootLength;
+    PINDEX_ENTRY_ATTRIBUTE IndexNodeEntry;
+    ULONG LengthWritten;
+    PNTFS_ATTR_RECORD DestinationAttribute;
+    PINDEX_ROOT_ATTRIBUTE NewIndexRoot;
+    ULONG AttributeLength;
+    PNTFS_ATTR_RECORD NextAttribute;
+
+    // Allocate memory for the parent directory
+    ParentFileRecord = ExAllocatePoolWithTag(NonPagedPool,
+                                             DeviceExt->NtfsInfo.BytesPerFileRecord,
+                                             TAG_NTFS);
+    if (!ParentFileRecord)
+    {
+        DPRINT1("ERROR: Couldn't allocate memory for file record!\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    // Open the parent directory
+    Status = ReadFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
+    if (!NT_SUCCESS(Status))
+    {
+        ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
+        DPRINT1("ERROR: Couldn't read parent directory with index %I64u\n",
+                DirectoryMftIndex);
+        return Status;
+    }
+
+    DPRINT1("Dumping old parent file record:\n");
+    NtfsDumpFileRecord(DeviceExt, ParentFileRecord);
+
+    // Find the index root attribute for the directory
+    Status = FindAttribute(DeviceExt,
+                           ParentFileRecord,
+                           AttributeIndexRoot,
+                           L"$I30",
+                           4,
+                           &DirectoryContext,
+                           &IndexRootOffset);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("ERROR: Couldn't find $I30 $INDEX_ROOT attribute for parent directory with MFT #: %I64u!\n",
+                DirectoryMftIndex);
+        ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
+        return Status;
+    }
+
+    I30IndexRootLength = AttributeDataLength(&DirectoryContext->Record);
+
+    // Allocate memory for the index root data
+    I30IndexRoot = (PINDEX_ROOT_ATTRIBUTE)ExAllocatePoolWithTag(NonPagedPool, I30IndexRootLength, TAG_NTFS);
+    if (!I30IndexRoot)
+    {
+        DPRINT1("ERROR: Couldn't allocate memory for index root attribute!\n");
+        ReleaseAttributeContext(DirectoryContext);
+        ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
+    }
+
+    // Read the Index Root
+    Status = ReadAttribute(DeviceExt, DirectoryContext, 0, (PCHAR)I30IndexRoot, I30IndexRootLength);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("ERROR: Couln't read index root attribute for Mft index #%I64u\n", DirectoryMftIndex);
+        ReleaseAttributeContext(DirectoryContext);
+        ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
+        ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
+        return Status;
+    }
+
+    // Make sure it's empty (temporarily)
+    IndexNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)I30IndexRoot + I30IndexRoot->Header.FirstEntryOffset + 0x10);
+    if (IndexNodeEntry->Data.Directory.IndexedFile != 0 || IndexNodeEntry->Flags != 2)
+    {
+        DPRINT1("FIXME: File-creation is only supported in empty directories right now! Be patient! :)\n");
+        ReleaseAttributeContext(DirectoryContext);
+        ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
+        ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
+        return STATUS_NOT_IMPLEMENTED;
+    }
+    
+    // Now we need to setup a new index root attribute to replace the old one
+    NewIndexRoot = ExAllocatePoolWithTag(NonPagedPool, DeviceExt->NtfsInfo.BytesPerIndexRecord, TAG_NTFS);
+    if (!NewIndexRoot)
+    {
+        DPRINT1("ERROR: Unable to allocate memory for new index root attribute!\n");
+        ReleaseAttributeContext(DirectoryContext);
+        ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
+        ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    // Setup the new index record
+    RtlZeroMemory(NewIndexRoot, DeviceExt->NtfsInfo.BytesPerIndexRecord);   // shouldn't be necessary but aids in debugging
+
+    NewIndexRoot->AttributeType = AttributeFileName;
+    NewIndexRoot->CollationRule = COLLATION_FILE_NAME;
+    NewIndexRoot->SizeOfEntry = DeviceExt->NtfsInfo.BytesPerIndexRecord;
+    // If Bytes per index record is less than cluster size, clusters per index record becomes sectors per index
+    if(NewIndexRoot->SizeOfEntry < DeviceExt->NtfsInfo.BytesPerCluster)
+        NewIndexRoot->ClustersPerIndexRecord = NewIndexRoot->SizeOfEntry / DeviceExt->NtfsInfo.BytesPerSector;
+    else    
+        NewIndexRoot->ClustersPerIndexRecord = NewIndexRoot->SizeOfEntry / DeviceExt->NtfsInfo.BytesPerCluster;
+
+    // Setup the Index node header
+    NewIndexRoot->Header.FirstEntryOffset = 0x10;
+    NewIndexRoot->Header.Flags = INDEX_ROOT_SMALL;
+    // still need to calculate sizes
+
+    // The first index node entry will be for the filename we're adding
+    IndexNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)NewIndexRoot + NewIndexRoot->Header.FirstEntryOffset + 0x10);
+    IndexNodeEntry->Data.Directory.IndexedFile = FileReferenceNumber;
+    IndexNodeEntry->Flags = INDEX_ROOT_SMALL;
+    IndexNodeEntry->KeyLength = FIELD_OFFSET(FILENAME_ATTRIBUTE, Name) + (2 * FilenameAttribute->NameLength);
+    IndexNodeEntry->Length = ALIGN_UP_BY(IndexNodeEntry->KeyLength, 8) + FIELD_OFFSET(INDEX_ENTRY_ATTRIBUTE, FileName);
+
+    // Now we can calculate the Node length (temp logic)
+    NewIndexRoot->Header.TotalSizeOfEntries = NewIndexRoot->Header.FirstEntryOffset + IndexNodeEntry->Length + 0x10;
+    NewIndexRoot->Header.AllocatedSize = NewIndexRoot->Header.TotalSizeOfEntries;
+
+    DPRINT1("New Index Node Entry Stream Length: %u\nNew Inde Node Entry Length: %u\n",
+            IndexNodeEntry->KeyLength,
+            IndexNodeEntry->Length);
+
+    // copy over the attribute proper
+    RtlCopyMemory(&IndexNodeEntry->FileName, FilenameAttribute, IndexNodeEntry->KeyLength);
+
+    // Now setup the dummy key
+    IndexNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexNodeEntry + IndexNodeEntry->Length);
+
+    IndexNodeEntry->Data.Directory.IndexedFile = 0;
+    IndexNodeEntry->Length = 0x10;
+    IndexNodeEntry->KeyLength = 0;
+    IndexNodeEntry->Flags = NTFS_INDEX_ENTRY_END;
+
+    // This is when we'd normally setup the length (already done above)
+
+    // Write back the new index root attribute to the parent directory file record
+
+    // First, we need to make sure the attribute is large enough.
+    // We can't set the size as we normally would, because if we extend past the file record, 
+    // we must create an index allocation and index bitmap (TODO). Also TODO: support file records with
+    // $ATTRIBUTE_LIST's.
+    AttributeLength = NewIndexRoot->Header.AllocatedSize + FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header);
+    DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)ParentFileRecord + IndexRootOffset);
+
+    NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)DestinationAttribute + DestinationAttribute->Length);
+    if (NextAttribute->Type != AttributeEnd)
+    {
+        DPRINT1("FIXME: For now, only resizing index root at the end of a file record is supported!\n");
+        ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
+        ReleaseAttributeContext(DirectoryContext);
+        ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
+        ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
+        return STATUS_NOT_IMPLEMENTED;
+    }
+    
+    // Update the length of the attribute in the file record of the parent directory
+    InternalSetResidentAttributeLength(DirectoryContext,
+                                       ParentFileRecord,
+                                       IndexRootOffset,
+                                       AttributeLength);
+
+    Status = UpdateFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("ERROR: Failed to update file record of directory with index: %llx\n", DirectoryMftIndex);
+        ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
+        ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
+        ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
+        return Status;
+    }
+
+    // Update the parent directory with the new index root
+    Status = WriteAttribute(DeviceExt,
+                            DirectoryContext,
+                            0,
+                            (PUCHAR)NewIndexRoot,
+                            AttributeLength,
+                            &LengthWritten);
+    if (!NT_SUCCESS(Status) )
+    {
+        DPRINT1("ERROR: Unable to write new index root attribute to parent directory!\n");
+        ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
+        ReleaseAttributeContext(DirectoryContext);
+        ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
+        ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
+        return Status;
+    }
+    
+    // re-read the parent file record, so we can dump it
+    Status = ReadFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("ERROR: Couldn't read parent directory after messing with it!\n");
+    }
+    else
+    {
+        DPRINT1("Dumping new parent file record:\n");
+        NtfsDumpFileRecord(DeviceExt, ParentFileRecord);
+    }
+
+    // Cleanup
+    ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
+    ReleaseAttributeContext(DirectoryContext);
+    ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
+    ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
+
+    return Status;
+}
 
 ULONGLONG
 NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt,
index 19ae0d1..5b9b291 100644 (file)
@@ -1361,6 +1361,9 @@ FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb,
 * @param DeviceExt
 * Pointer to the DEVICE_EXTENSION of the target drive.
 *
+* @param DestinationIndex
+* Pointer to a ULONGLONG which will receive the MFT index where the file record was stored.
+*
 * @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 
@@ -1371,7 +1374,8 @@ FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb,
 */
 NTSTATUS
 AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
-               PDEVICE_EXTENSION DeviceExt)
+               PDEVICE_EXTENSION DeviceExt,
+               PULONGLONG DestinationIndex)
 {
     NTSTATUS Status = STATUS_SUCCESS;
     ULONGLONG MftIndex;
@@ -1454,6 +1458,8 @@ AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
         return Status;
     }
 
+    *DestinationIndex = MftIndex;
+
     ExFreePoolWithTag(BitmapData, TAG_NTFS);
     ReleaseAttributeContext(BitmapContext);
 
index 2177f1d..a3265db 100644 (file)
@@ -534,7 +534,8 @@ NTSTATUS
 AddFileName(PFILE_RECORD_HEADER FileRecord,
             PNTFS_ATTR_RECORD AttributeAddress,
             PDEVICE_EXTENSION DeviceExt,
-            PFILE_OBJECT FileObject);
+            PFILE_OBJECT FileObject,
+            PULONGLONG ParentMftIndex);
 
 NTSTATUS
 AddStandardInformation(PFILE_RECORD_HEADER FileRecord,
@@ -675,6 +676,12 @@ NtfsDeviceControl(PNTFS_IRP_CONTEXT IrpContext);
 
 /* dirctl.c */
 
+NTSTATUS
+NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt,
+                           ULONGLONG DirectoryMftIndex,
+                           ULONGLONG FileMftIndex,
+                           PFILENAME_ATTRIBUTE FilenameAttribute);
+
 ULONGLONG
 NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt,
                 PFILE_RECORD_HEADER FileRecord,
@@ -816,7 +823,8 @@ NtfsFileSystemControl(PNTFS_IRP_CONTEXT IrpContext);
 /* mft.c */
 NTSTATUS
 AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
-               PDEVICE_EXTENSION DeviceExt);
+               PDEVICE_EXTENSION DeviceExt,
+               PULONGLONG DestinationIndex);
 
 PNTFS_ATTR_CONTEXT
 PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord);
@@ -842,6 +850,12 @@ WriteAttribute(PDEVICE_EXTENSION Vcb,
 ULONGLONG
 AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord);
 
+VOID
+InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext,
+                                   PFILE_RECORD_HEADER FileRecord,
+                                   ULONG AttrOffset,
+                                   ULONG DataSize);
+
 NTSTATUS
 SetAttributeDataLength(PFILE_OBJECT FileObject,
                        PNTFS_FCB Fcb,