[NTFS] - Fix some more issues, including remaining issues marked as "unresolved"...
authorTrevor Thompson <tmt256@email.vccs.edu>
Tue, 18 Jul 2017 19:59:36 +0000 (19:59 +0000)
committerThomas Faber <thomas.faber@reactos.org>
Sun, 10 Dec 2017 10:15:01 +0000 (11:15 +0100)
-Add define for indexed flag for resident attributes, RA_INDEXED.
-CreateBTreeFromIndex() - Don't try to read index entries beyond attribute length.
-Move NtfsAddFileNameToDirectory() from dirctl.c to mft.c (no changes to function).
-SetResidentAttributeDataLength() - Don't try to free AttribData if it hasn't been allocated. Cast IndexRecord to PUCHAR for WriteAttribute(), and cast BitmapData to PCHAR for ReadAttribute() (make gcc happy).
-Replace magic number 16 with a define, NTFS_FILE_FIRST_USER_FILE.

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

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

index bd070c9..a6bc9a8 100644 (file)
@@ -245,7 +245,7 @@ AddFileName(PFILE_RECORD_HEADER FileRecord,
 
     AttributeAddress->Resident.ValueLength = FIELD_OFFSET(FILENAME_ATTRIBUTE, Name) + FilenameNoPath.Length;
     AttributeAddress->Resident.ValueOffset = ResidentHeaderLength;
-    AttributeAddress->Resident.Flags = 1; // indexed
+    AttributeAddress->Resident.Flags = RA_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);
index 1791072..a7eacb3 100644 (file)
@@ -138,6 +138,7 @@ CreateBTreeFromIndex(PNTFS_ATTR_CONTEXT IndexRootContext,
     PB_TREE Tree = ExAllocatePoolWithTag(NonPagedPool, sizeof(B_TREE), TAG_NTFS);
     PB_TREE_FILENAME_NODE RootNode = ExAllocatePoolWithTag(NonPagedPool, sizeof(B_TREE_FILENAME_NODE), TAG_NTFS);
     PB_TREE_KEY CurrentKey = ExAllocatePoolWithTag(NonPagedPool, sizeof(B_TREE_KEY), TAG_NTFS);
+    ULONG CurrentOffset = IndexRoot->Header.FirstEntryOffset;
 
     DPRINT1("CreateBTreeFromIndex(%p, %p, %p)\n", IndexRootContext, IndexRoot, NewTree);
 
@@ -161,11 +162,21 @@ CreateBTreeFromIndex(PNTFS_ATTR_CONTEXT IndexRootContext,
     RootNode->FirstKey = CurrentKey;
     Tree->RootNode = RootNode;
 
-    // Create a key for each entry in the node
+    // Make sure we won't try reading past the attribute-end
+    if (FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header) + IndexRoot->Header.TotalSizeOfEntries > IndexRootContext->Record.Resident.ValueLength)
+    {
+        DPRINT1("Filesystem corruption detected!\n");
+        DestroyBTree(Tree);
+        return STATUS_FILE_CORRUPT_ERROR;
+    }
+
+    // Start at the first node entry
     CurrentNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexRoot
                                                 + FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header)
                                                 + IndexRoot->Header.FirstEntryOffset);
-    while (TRUE)
+
+    // Create a key for each entry in the node
+    while (CurrentOffset < IndexRoot->Header.TotalSizeOfEntries)
     {
         // Allocate memory for the current entry
         CurrentKey->IndexEntry = ExAllocatePoolWithTag(NonPagedPool, CurrentNodeEntry->Length, TAG_NTFS);
@@ -207,6 +218,7 @@ CreateBTreeFromIndex(PNTFS_ATTR_CONTEXT IndexRootContext,
             }
 
             // Advance to the next entry
+            CurrentOffset += CurrentNodeEntry->Length;
             CurrentNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)CurrentNodeEntry + CurrentNodeEntry->Length);
             CurrentKey = NextKey;
         }
@@ -336,7 +348,7 @@ CreateIndexRootFromBTree(PDEVICE_EXTENSION DeviceExt,
         // Add Length of Current Entry to Total Size of Entries
         NewIndexRoot->Header.TotalSizeOfEntries += CurrentNodeEntry->Length;
 
-        // Go to the next node
+        // Go to the next node entry
         CurrentNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)CurrentNodeEntry + CurrentNodeEntry->Length);
 
         CurrentKey = CurrentKey->NextKey;
@@ -533,16 +545,12 @@ NtfsInsertKey(ULONGLONG FileReference,
     {
         // Should the New Key go before the current key?
         LONG Comparison = CompareTreeKeys(NewKey, CurrentKey, CaseSensitive);
-        if (Comparison == 0)
-        {
-            DPRINT1("DRIVER ERROR: Asked to insert key into tree that already has it!\n");
-            ExFreePoolWithTag(NewKey, TAG_NTFS);
-            ExFreePoolWithTag(NewEntry, TAG_NTFS);
-            return STATUS_INVALID_PARAMETER;
-        }
+
+        ASSERT(Comparison != 0);
+
+        // Is NewKey < CurrentKey?
         if (Comparison < 0)
         {
-            // NewKey is < CurrentKey
             // Insert New Key before Current Key
             NewKey->NextKey = CurrentKey;
 
index 30cc921..f5ca102 100644 (file)
@@ -185,7 +185,7 @@ NtfsOpenFileById(PDEVICE_EXTENSION DeviceExt,
 
     DPRINT1("NtfsOpenFileById(%p, %p, %I64x, %p)\n", DeviceExt, FileObject, MftId, FoundFCB);
 
-    ASSERT(MftId < 0x10);
+    ASSERT(MftId < NTFS_FILE_FIRST_USER_FILE);
     if (MftId > 0xb) /* No entries are used yet beyond this */
     {
         return STATUS_OBJECT_NAME_NOT_FOUND;
@@ -375,7 +375,7 @@ NtfsCreateFile(PDEVICE_OBJECT DeviceObject,
             return STATUS_INVALID_PARAMETER;
 
         MFTId = (*(PULONGLONG)FileObject->FileName.Buffer) & NTFS_MFT_MASK;
-        if (MFTId < 0x10)
+        if (MFTId < NTFS_FILE_FIRST_USER_FILE)
         {
             Status = NtfsOpenFileById(DeviceExt, FileObject, MFTId, &Fcb);
         }
index c41688e..98294b4 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.
-*
-* @param CaseSensitive
-* Boolean 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.
-*
-* @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 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 Status = STATUS_SUCCESS;
-    PFILE_RECORD_HEADER ParentFileRecord;
-    PNTFS_ATTR_CONTEXT IndexRootContext;
-    PINDEX_ROOT_ATTRIBUTE I30IndexRoot;
-    ULONG IndexRootOffset;
-    ULONGLONG I30IndexRootLength;
-    ULONG LengthWritten;
-    PNTFS_ATTR_RECORD DestinationAttribute;
-    PINDEX_ROOT_ATTRIBUTE NewIndexRoot;
-    ULONG AttributeLength;
-    PNTFS_ATTR_RECORD NextAttribute;
-    PB_TREE NewTree;
-    ULONG BtreeIndexLength;
-    ULONG MaxIndexSize;
-
-    // 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,
-                           &IndexRootContext,
-                           &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;
-    }
-
-    // Find the maximum index size given what the file record can hold
-    MaxIndexSize = DeviceExt->NtfsInfo.BytesPerFileRecord
-                   - IndexRootOffset
-                   - IndexRootContext->Record.Resident.ValueOffset
-                   - FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header)
-                   - (sizeof(ULONG) * 2);
-
-    // Allocate memory for the index root data
-    I30IndexRootLength = AttributeDataLength(&IndexRootContext->Record);
-    I30IndexRoot = ExAllocatePoolWithTag(NonPagedPool, I30IndexRootLength, TAG_NTFS);
-    if (!I30IndexRoot)
-    {
-        DPRINT1("ERROR: Couldn't allocate memory for index root attribute!\n");
-        ReleaseAttributeContext(IndexRootContext);
-        ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-
-    // Read the Index Root
-    Status = ReadAttribute(DeviceExt, IndexRootContext, 0, (PCHAR)I30IndexRoot, I30IndexRootLength);
-    if (!NT_SUCCESS(Status))
-    {
-        DPRINT1("ERROR: Couln't read index root attribute for Mft index #%I64u\n", DirectoryMftIndex);
-        ReleaseAttributeContext(IndexRootContext);
-        ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
-        ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
-        return Status;
-    }
-
-    // Convert the index to a B*Tree
-    Status = CreateBTreeFromIndex(IndexRootContext, I30IndexRoot, &NewTree);
-    if (!NT_SUCCESS(Status))
-    {
-        DPRINT1("ERROR: Failed to create B-Tree from Index!\n");
-        ReleaseAttributeContext(IndexRootContext);
-        ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
-        ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
-        return Status;
-    }
-
-    DumpBTree(NewTree);
-
-    // Insert the key for the file we're adding
-    Status = NtfsInsertKey(FileReferenceNumber, FilenameAttribute, NewTree->RootNode, CaseSensitive);
-    if (!NT_SUCCESS(Status))
-    {
-        DPRINT1("ERROR: Failed to insert key into B-Tree!\n");
-        DestroyBTree(NewTree);
-        ReleaseAttributeContext(IndexRootContext);
-        ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
-        ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
-        return Status;
-    }
-
-    DumpBTree(NewTree);
-    
-    // Convert B*Tree back to Index Root
-    Status = CreateIndexRootFromBTree(DeviceExt, NewTree, MaxIndexSize, &NewIndexRoot, &BtreeIndexLength);
-    if (!NT_SUCCESS(Status))
-    {
-        DPRINT1("ERROR: Failed to create Index root from B-Tree!\n");
-        DestroyBTree(NewTree);
-        ReleaseAttributeContext(IndexRootContext);
-        ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
-        ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
-        return Status;
-    }
-
-    // We're done with the B-Tree now
-    DestroyBTree(NewTree);
-
-    // Write back the new index root attribute to the parent directory file record
-
-    // First, we need to resize the attribute.
-    // CreateIndexRootFromBTree() should have verified that the index root fits within MaxIndexSize.
-    // 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);
-
-    // Find the attribute (or attribute-end marker) after the index root
-    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(IndexRootContext);
-        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(IndexRootContext,
-                                       ParentFileRecord,
-                                       IndexRootOffset,
-                                       AttributeLength);
-
-    NT_ASSERT(ParentFileRecord->BytesInUse <= DeviceExt->NtfsInfo.BytesPerFileRecord);
-
-    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);
-        ReleaseAttributeContext(IndexRootContext);
-        ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
-        return Status;
-    }
-
-    // Write the new index root to disk
-    Status = WriteAttribute(DeviceExt,
-                            IndexRootContext,
-                            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(IndexRootContext);
-        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(IndexRootContext);
-    ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
-    ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
-
-    return Status;
-}
-
 ULONGLONG
 NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt,
                 PFILE_RECORD_HEADER FileRecord,
index ed531df..72953c0 100644 (file)
@@ -750,7 +750,8 @@ SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 
                 {
                     DPRINT1("Unable to create LargeMcb!\n");
-                    ExFreePoolWithTag(AttribData, TAG_NTFS);
+                    if (AttribDataSize.QuadPart > 0)
+                        ExFreePoolWithTag(AttribData, TAG_NTFS);
                     _SEH2_YIELD(return _SEH2_GetExceptionCode());
                 } _SEH2_END;
 
@@ -1452,7 +1453,7 @@ UpdateFileNameRecord(PDEVICE_EXTENSION Vcb,
     {
         // we need to write the index root attribute back to disk
         ULONG LengthWritten;
-        Status = WriteAttribute(Vcb, IndexRootCtx, 0, IndexRecord, AttributeDataLength(&IndexRootCtx->Record), &LengthWritten);
+        Status = WriteAttribute(Vcb, IndexRootCtx, 0, (PUCHAR)IndexRecord, AttributeDataLength(&IndexRootCtx->Record), &LengthWritten);
         if (!NT_SUCCESS(Status))
         {
             DPRINT1("ERROR: Couldn't update Index Root!\n");
@@ -1514,7 +1515,7 @@ UpdateIndexEntryFileNameSize(PDEVICE_EXTENSION Vcb,
     while (IndexEntry < LastEntry &&
            !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
     {
-        if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) > 0x10 &&
+        if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) > NTFS_FILE_FIRST_USER_FILE &&
             *CurrentEntry >= *StartEntry &&
             IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS &&
             CompareFileName(FileName, IndexEntry, DirSearch, CaseSensitive))
@@ -1755,7 +1756,7 @@ AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
     }
 
     // read $Bitmap attribute
-    AttrBytesRead = ReadAttribute(DeviceExt, BitmapContext, 0, BitmapData, BitmapDataSize);
+    AttrBytesRead = ReadAttribute(DeviceExt, BitmapContext, 0, (PCHAR)BitmapData, BitmapDataSize);
 
     if (AttrBytesRead == 0)
     {
@@ -1843,6 +1844,251 @@ AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
     return Status;
 }
 
+/**
+* @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.
+*
+* @param CaseSensitive
+* Boolean 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.
+*
+* @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 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 Status = STATUS_SUCCESS;
+    PFILE_RECORD_HEADER ParentFileRecord;
+    PNTFS_ATTR_CONTEXT IndexRootContext;
+    PINDEX_ROOT_ATTRIBUTE I30IndexRoot;
+    ULONG IndexRootOffset;
+    ULONGLONG I30IndexRootLength;
+    ULONG LengthWritten;
+    PNTFS_ATTR_RECORD DestinationAttribute;
+    PINDEX_ROOT_ATTRIBUTE NewIndexRoot;
+    ULONG AttributeLength;
+    PNTFS_ATTR_RECORD NextAttribute;
+    PB_TREE NewTree;
+    ULONG BtreeIndexLength;
+    ULONG MaxIndexSize;
+
+    // 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,
+                           &IndexRootContext,
+                           &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;
+    }
+
+    // Find the maximum index size given what the file record can hold
+    MaxIndexSize = DeviceExt->NtfsInfo.BytesPerFileRecord
+        - IndexRootOffset
+        - IndexRootContext->Record.Resident.ValueOffset
+        - FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header)
+        - (sizeof(ULONG) * 2);
+
+    // Allocate memory for the index root data
+    I30IndexRootLength = AttributeDataLength(&IndexRootContext->Record);
+    I30IndexRoot = ExAllocatePoolWithTag(NonPagedPool, I30IndexRootLength, TAG_NTFS);
+    if (!I30IndexRoot)
+    {
+        DPRINT1("ERROR: Couldn't allocate memory for index root attribute!\n");
+        ReleaseAttributeContext(IndexRootContext);
+        ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    // Read the Index Root
+    Status = ReadAttribute(DeviceExt, IndexRootContext, 0, (PCHAR)I30IndexRoot, I30IndexRootLength);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("ERROR: Couln't read index root attribute for Mft index #%I64u\n", DirectoryMftIndex);
+        ReleaseAttributeContext(IndexRootContext);
+        ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
+        ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
+        return Status;
+    }
+
+    // Convert the index to a B*Tree
+    Status = CreateBTreeFromIndex(IndexRootContext, I30IndexRoot, &NewTree);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("ERROR: Failed to create B-Tree from Index!\n");
+        ReleaseAttributeContext(IndexRootContext);
+        ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
+        ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
+        return Status;
+    }
+
+    DumpBTree(NewTree);
+
+    // Insert the key for the file we're adding
+    Status = NtfsInsertKey(FileReferenceNumber, FilenameAttribute, NewTree->RootNode, CaseSensitive);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("ERROR: Failed to insert key into B-Tree!\n");
+        DestroyBTree(NewTree);
+        ReleaseAttributeContext(IndexRootContext);
+        ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
+        ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
+        return Status;
+    }
+
+    DumpBTree(NewTree);
+
+    // Convert B*Tree back to Index Root
+    Status = CreateIndexRootFromBTree(DeviceExt, NewTree, MaxIndexSize, &NewIndexRoot, &BtreeIndexLength);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("ERROR: Failed to create Index root from B-Tree!\n");
+        DestroyBTree(NewTree);
+        ReleaseAttributeContext(IndexRootContext);
+        ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
+        ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
+        return Status;
+    }
+
+    // We're done with the B-Tree now
+    DestroyBTree(NewTree);
+
+    // Write back the new index root attribute to the parent directory file record
+
+    // First, we need to resize the attribute.
+    // CreateIndexRootFromBTree() should have verified that the index root fits within MaxIndexSize.
+    // 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);
+
+    // Find the attribute (or attribute-end marker) after the index root
+    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(IndexRootContext);
+        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(IndexRootContext,
+                                       ParentFileRecord,
+                                       IndexRootOffset,
+                                       AttributeLength);
+
+    NT_ASSERT(ParentFileRecord->BytesInUse <= DeviceExt->NtfsInfo.BytesPerFileRecord);
+
+    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);
+        ReleaseAttributeContext(IndexRootContext);
+        ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
+        return Status;
+    }
+
+    // Write the new index root to disk
+    Status = WriteAttribute(DeviceExt,
+                            IndexRootContext,
+                            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(IndexRootContext);
+        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(IndexRootContext);
+    ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
+    ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
+
+    return Status;
+}
+
 NTSTATUS
 AddFixupArray(PDEVICE_EXTENSION Vcb,
               PNTFS_RECORD_HEADER Record)
@@ -1995,7 +2241,7 @@ BrowseIndexEntries(PDEVICE_EXTENSION Vcb,
     while (IndexEntry < LastEntry &&
            !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
     {
-        if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) >= 0x10 &&
+        if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) >= NTFS_FILE_FIRST_USER_FILE &&
             *CurrentEntry >= *StartEntry &&
             IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS &&
             CompareFileName(FileName, IndexEntry, DirSearch, CaseSensitive))
index ddb876d..9cbc4d5 100644 (file)
@@ -193,6 +193,7 @@ typedef enum
 #define NTFS_FILE_QUOTA                9
 #define NTFS_FILE_UPCASE            10
 #define NTFS_FILE_EXTEND            11
+#define NTFS_FILE_FIRST_USER_FILE   16
 
 #define NTFS_MFT_MASK 0x0000FFFFFFFFFFFFULL
 
@@ -223,6 +224,9 @@ typedef enum
 #define NTFS_FILE_TYPE_COMPRESSED 0x800
 #define NTFS_FILE_TYPE_DIRECTORY  0x10000000
 
+/* Indexed Flag in Resident attributes - still somewhat speculative */
+#define RA_INDEXED    0x01
+
 typedef struct
 {
     ULONG Type;             /* Magic number 'FILE' */
@@ -740,13 +744,6 @@ NtfsDeviceControl(PNTFS_IRP_CONTEXT IrpContext);
 
 /* dirctl.c */
 
-NTSTATUS
-NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt,
-                           ULONGLONG DirectoryMftIndex,
-                           ULONGLONG FileReferenceNumber,
-                           PFILENAME_ATTRIBUTE FilenameAttribute,
-                           BOOLEAN CaseSensitive);
-
 ULONGLONG
 NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt,
                 PFILE_RECORD_HEADER FileRecord,
@@ -888,6 +885,13 @@ NtfsFileSystemControl(PNTFS_IRP_CONTEXT IrpContext);
 
 
 /* mft.c */
+NTSTATUS
+NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt,
+                           ULONGLONG DirectoryMftIndex,
+                           ULONGLONG FileReferenceNumber,
+                           PFILENAME_ATTRIBUTE FilenameAttribute,
+                           BOOLEAN CaseSensitive);
+
 NTSTATUS
 AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
                PDEVICE_EXTENSION DeviceExt,