Author: tthompson
Date: Sun Aug 27 14:37:17 2017
New Revision: 75692

URL: http://svn.reactos.org/svn/reactos?rev=75692&view=rev
Log:
[NTFS] - Add support for directory creation. Add some helper functions, some 
comments, and some fixes.
+AddIndexRoot() - Creates an $INDEX_ROOT attribute and adds it to a file record.
AddNewMftEntry() - Make sure the buffer used by RtlInitializeBitmap() is 
ULONG-aligned, and a ULONG-multiple in size, per MSDN.
AllocateIndexNode() - Calculate BytesNeeded correctly. Read $BITMAP attribute 
before increasing its length, in anticipation of a future commit that will 
check for a free bit before assigning a new index record to the end of the 
allocation. Use appropriate Set*AttributeDataLength() function, as $BITMAP can 
be resident or non-resident.
B_TREE_FILENAME_NODE - Give two members more accurate names: change 
"ExistsOnDisk" member to "HasValidVCN" and rename "NodeNumber" member "VCN."
+CreateEmptyBTree() - Creates a B-Tree to represent an empty directory (for 
AddIndexRoot).
+NtfsCreateEmptyFileRecord() - Creates an empty file record in memory, with no 
attributes.
CreateIndexRootFromBTree() - Fix TotalSizeOfEntries calculation.
+NtfsCreateDirectory() - Creates a file record for an empty directory and adds 
it to the mft.

Modified:
    branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/attrib.c
    branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/btree.c
    branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/create.c
    branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/mft.c
    branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/ntfs.h

Modified: branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/attrib.c
URL: 
http://svn.reactos.org/svn/reactos/branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/attrib.c?rev=75692&r1=75691&r2=75692&view=diff
==============================================================================
--- branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/attrib.c   [iso-8859-1] 
(original)
+++ branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/attrib.c   [iso-8859-1] 
Sun Aug 27 14:37:17 2017
@@ -167,7 +167,11 @@
     FileNameAttribute->LastWriteTime = SystemTime.QuadPart;
     FileNameAttribute->LastAccessTime = SystemTime.QuadPart;
 
-    FileNameAttribute->FileAttributes = NTFS_FILE_TYPE_ARCHIVE;
+    // Is this a directory?
+    if(FileRecord->Flags & FRH_DIRECTORY)
+        FileNameAttribute->FileAttributes = NTFS_FILE_TYPE_DIRECTORY;
+    else
+        FileNameAttribute->FileAttributes = NTFS_FILE_TYPE_ARCHIVE;
 
     // we need to extract the filename from the path
     DPRINT1("Pathname: %wZ\n", &FileObject->FileName);
@@ -252,6 +256,112 @@
     SetFileRecordEnd(FileRecord, AttributeAddress, FileRecordEnd);
 
     return Status;
+}
+
+/**
+* @name AddIndexRoot
+* @implemented
+*
+* Adds an $INDEX_ROOT attribute to a given FileRecord.
+*
+* @param Vcb
+* Pointer to an NTFS_VCB for the destination volume.
+*
+* @param FileRecord
+* Pointer to a complete file record to add the attribute to. Caller is 
responsible for
+* ensuring FileRecord is large enough to contain $INDEX_ROOT.
+*
+* @param AttributeAddress
+* Pointer to the region of memory that will receive the $INDEX_ROOT attribute.
+* This address must reside within FileRecord. Must be aligned to an 8-byte 
boundary (relative to FileRecord).
+*
+* @param NewIndexRoot
+* Pointer to an INDEX_ROOT_ATTRIBUTE containing the index root that will be 
copied to the new attribute.
+*
+* @param RootLength
+* The length of NewIndexRoot, in bytes.
+*
+* @param Name
+* Pointer to a string of 16-bit Unicode characters naming the attribute. Most 
often, this will be L"$I30".
+*
+* @param NameLength
+* The number of wide-characters in the name. L"$I30" Would use 4 here.
+*
+* @return
+* STATUS_SUCCESS on success. STATUS_NOT_IMPLEMENTED if target address isn't at 
the end
+* of the given file record.
+*
+* @remarks
+* This function is intended to assist in creating new folders.
+* Only adding the attribute to the end of the file record is supported; 
AttributeAddress must
+* be of type AttributeEnd.
+* It's the caller's responsibility to ensure the given file record has enough 
memory allocated
+* for the attribute, and this memory must have been zeroed.
+*/
+NTSTATUS
+AddIndexRoot(PNTFS_VCB Vcb,
+             PFILE_RECORD_HEADER FileRecord,
+             PNTFS_ATTR_RECORD AttributeAddress,
+             PINDEX_ROOT_ATTRIBUTE NewIndexRoot,
+             ULONG RootLength,
+             PCWSTR Name,
+             USHORT NameLength)
+{
+    ULONG AttributeLength;
+    // Calculate the header length
+    ULONG ResidentHeaderLength = FIELD_OFFSET(NTFS_ATTR_RECORD, 
Resident.Reserved) + sizeof(UCHAR);
+    // Back up the file record's final ULONG (even though it doesn't matter)
+    ULONG FileRecordEnd = AttributeAddress->Length;
+    ULONG NameOffset;
+    ULONG ValueOffset;
+    ULONG BytesAvailable;
+
+    if (AttributeAddress->Type != AttributeEnd)
+    {
+        DPRINT1("FIXME: Can only add $DATA attribute to the end of a file 
record.\n");
+        return STATUS_NOT_IMPLEMENTED;
+    }
+
+    NameOffset = ResidentHeaderLength;
+
+    // Calculate ValueOffset, which will be aligned to a 4-byte boundary
+    ValueOffset = ALIGN_UP_BY(NameOffset + (sizeof(WCHAR) * NameLength), 
VALUE_OFFSET_ALIGNMENT);
+
+    // Calculate length of attribute
+    AttributeLength = ValueOffset + RootLength;
+    AttributeLength = ALIGN_UP_BY(AttributeLength, ATTR_RECORD_ALIGNMENT);
+
+    // Make sure the file record is large enough for the new attribute
+    BytesAvailable = Vcb->NtfsInfo.BytesPerFileRecord - FileRecord->BytesInUse;
+    if (BytesAvailable < AttributeLength)
+    {
+        DPRINT1("FIXME: Not enough room in file record for index allocation 
attribute!\n");
+        return STATUS_NOT_IMPLEMENTED;
+    }
+
+    // Set Attribute fields
+    RtlZeroMemory(AttributeAddress, AttributeLength);
+
+    AttributeAddress->Type = AttributeIndexRoot;
+    AttributeAddress->Length = AttributeLength;
+    AttributeAddress->NameLength = NameLength;
+    AttributeAddress->NameOffset = NameOffset;
+    AttributeAddress->Instance = FileRecord->NextAttributeNumber++;
+
+    AttributeAddress->Resident.ValueLength = RootLength;
+    AttributeAddress->Resident.ValueOffset = ValueOffset;
+
+    // Set the name
+    RtlCopyMemory((PCHAR)((ULONG_PTR)AttributeAddress + NameOffset), Name, 
NameLength * sizeof(WCHAR));
+
+    // Copy the index root attribute
+    RtlCopyMemory((PCHAR)((ULONG_PTR)AttributeAddress + ValueOffset), 
NewIndexRoot, RootLength);
+
+    // 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;
 }
 
 /**

Modified: branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/btree.c
URL: 
http://svn.reactos.org/svn/reactos/branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/btree.c?rev=75692&r1=75691&r2=75692&view=diff
==============================================================================
--- branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/btree.c    [iso-8859-1] 
(original)
+++ branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/btree.c    [iso-8859-1] 
Sun Aug 27 14:37:17 2017
@@ -111,6 +111,7 @@
 * @remarks
 * AllocateIndexNode() doesn't write any data to the index record it creates. 
Called by UpdateIndexNode().
 * Don't call PrintAllVCNs() or NtfsDumpFileRecord() after calling 
AllocateIndexNode() before UpdateIndexNode() finishes.
+* Possible TODO: Create an empty node and write it to the allocated index 
node, so the index allocation is always valid.
 */
 NTSTATUS
 AllocateIndexNode(PDEVICE_EXTENSION DeviceExt,
@@ -167,11 +168,30 @@
 
     // See how many bytes we need to store the amount of bits we'll have
     BytesNeeded = NextNodeNumber / 8;
-    if (NextNodeNumber % 8 != 0)
-        BytesNeeded++;
+    BytesNeeded++;
 
     // Windows seems to allocate the bitmap in 8-byte chunks to keep any bytes 
from being wasted on padding
-    ALIGN_UP(BytesNeeded, ATTR_RECORD_ALIGNMENT);
+    BytesNeeded = ALIGN_UP(BytesNeeded, ATTR_RECORD_ALIGNMENT);
+
+    // Allocate memory for the bitmap, including some padding; 
RtlInitializeBitmap() wants a pointer 
+    // that's ULONG-aligned, and it wants the size of the memory allocated for 
it to be a ULONG-multiple.
+    BitmapMem = ExAllocatePoolWithTag(NonPagedPool, BytesNeeded + 
sizeof(ULONG), TAG_NTFS);
+    if (!BitmapMem)
+    {
+        DPRINT1("Error: failed to allocate bitmap!");
+        ReleaseAttributeContext(BitmapCtx);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    // RtlInitializeBitmap() wants a pointer that's ULONG-aligned.
+    BitmapPtr = (PULONG)ALIGN_UP_BY((ULONG_PTR)BitmapMem, sizeof(ULONG));
+
+    RtlZeroMemory(BitmapPtr, BytesNeeded);
+
+    // Read the existing bitmap data
+    Status = ReadAttribute(DeviceExt, BitmapCtx, 0, (PCHAR)BitmapPtr, 
BitmapLength);
+
+    // Initialize bitmap
+    RtlInitializeBitMap(&Bitmap, BitmapPtr, NextNodeNumber);
 
     // Do we need to enlarge the bitmap?
     if (BytesNeeded > BitmapLength)
@@ -179,11 +199,22 @@
         // TODO: handle synchronization issues that could occur from changing 
the directory's file record
         // Change bitmap size
         DataSize.QuadPart = BytesNeeded;
-        Status = SetResidentAttributeDataLength(DeviceExt,
-                                                BitmapCtx,
-                                                BitmapOffset,
-                                                FileRecord,
-                                                &DataSize);
+        if (BitmapCtx->pRecord->IsNonResident)
+        {
+            Status = SetNonResidentAttributeDataLength(DeviceExt,
+                                                       BitmapCtx,
+                                                       BitmapOffset,
+                                                       FileRecord,
+                                                       &DataSize);
+        }
+        else
+        {
+            Status = SetResidentAttributeDataLength(DeviceExt,
+                                                    BitmapCtx,
+                                                    BitmapOffset,
+                                                    FileRecord,
+                                                    &DataSize);
+        }
         if (!NT_SUCCESS(Status))
         {
             DPRINT1("ERROR: Failed to set length of bitmap attribute!\n");
@@ -214,18 +245,6 @@
         ReleaseAttributeContext(BitmapCtx);
         return Status;
     }
-
-    // Allocate memory for the bitmap. RtlInitializeBitmap() wants a pointer 
that's ULONG-aligned
-    BitmapMem = ExAllocatePoolWithTag(NonPagedPool, BytesNeeded + 
sizeof(ULONG) - 1, TAG_NTFS);
-    BitmapPtr = (PULONG)ALIGN_UP_BY((ULONG_PTR)BitmapMem, sizeof(ULONG));
-
-    RtlZeroMemory(BitmapPtr, BytesNeeded);
-
-    // Read the existing bitmap data
-    Status = ReadAttribute(DeviceExt, BitmapCtx, 0, (PCHAR)BitmapPtr, 
BitmapLength);
-
-    // Initialize bitmap
-    RtlInitializeBitMap(&Bitmap, BitmapPtr, BytesNeeded);
 
     // Set the bit for the new index record
     RtlSetBits(&Bitmap, NextNodeNumber, 1);
@@ -246,6 +265,8 @@
     // Calculate VCN of new node number
     *NewVCN = NextNodeNumber * (IndexBufferSize / 
DeviceExt->NtfsInfo.BytesPerCluster);
 
+    DPRINT("New VCN: %I64u\n", *NewVCN);
+
     ExFreePoolWithTag(BitmapMem, TAG_NTFS);
     ReleaseAttributeContext(BitmapCtx);
 
@@ -309,6 +330,63 @@
     NewDummyKey->IndexEntry = NewIndexEntry;
 
     return NewDummyKey;
+}
+
+/**
+* @name CreateEmptyBTree
+* @implemented
+*
+* Creates an empty B-Tree, which will contain a single root node which will 
contain a single dummy key.
+*
+* @param NewTree
+* Pointer to a PB_TREE that will receive the pointer of the newly-created 
B-Tree.
+*
+* @return
+* STATUS_SUCCESS on success. STATUS_INSUFFICIENT_RESOURCES if an allocation 
fails.
+*/
+NTSTATUS
+CreateEmptyBTree(PB_TREE *NewTree)
+{
+    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 DummyKey;
+
+    DPRINT1("CreateEmptyBTree(%p) called\n", NewTree);
+
+    if (!Tree || !RootNode)
+    {
+        DPRINT1("Couldn't allocate enough memory for B-Tree!\n");
+        if (Tree)
+            ExFreePoolWithTag(Tree, TAG_NTFS);
+        if (RootNode)
+            ExFreePoolWithTag(RootNode, TAG_NTFS);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    // Create the dummy key
+    DummyKey = CreateDummyKey(FALSE);
+    if (!DummyKey)
+    {
+        DPRINT1("ERROR: Failed to create dummy key!\n");
+        ExFreePoolWithTag(Tree, TAG_NTFS);
+        ExFreePoolWithTag(RootNode, TAG_NTFS);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    RtlZeroMemory(Tree, sizeof(B_TREE));
+    RtlZeroMemory(RootNode, sizeof(B_TREE_FILENAME_NODE));
+
+    // Setup the Tree
+    RootNode->FirstKey = DummyKey;
+    RootNode->KeyCount = 1;
+    RootNode->DiskNeedsUpdating = TRUE;
+    Tree->RootNode = RootNode;
+
+    *NewTree = Tree;
+
+    // Memory will be freed when DestroyBTree() is called
+
+    return STATUS_SUCCESS;
 }
 
 /**
@@ -402,7 +480,7 @@
     ULONG CurrentEntryOffset = 0;
     PINDEX_BUFFER NodeBuffer;
     ULONG IndexBufferSize = Vcb->NtfsInfo.BytesPerIndexRecord;
-    PULONGLONG NodeNumber;
+    PULONGLONG VCN;
     PB_TREE_KEY CurrentKey;
     NTSTATUS Status;
     ULONGLONG IndexNodeOffset;
@@ -415,10 +493,9 @@
     }
 
     // Get the node number from the end of the node entry
-    NodeNumber = (PULONGLONG)((ULONG_PTR)NodeEntry + NodeEntry->Length - 
sizeof(ULONGLONG));
+    VCN = (PULONGLONG)((ULONG_PTR)NodeEntry + NodeEntry->Length - 
sizeof(ULONGLONG));
 
     // Create the new tree node
-    DPRINT1("About to allocate %ld for NewNode\n", 
sizeof(B_TREE_FILENAME_NODE));
     NewNode = ExAllocatePoolWithTag(NonPagedPool, 
sizeof(B_TREE_FILENAME_NODE), TAG_NTFS);
     if (!NewNode)
     {
@@ -449,7 +526,7 @@
     }
 
     // Calculate offset into index allocation
-    IndexNodeOffset = GetAllocationOffsetFromVCN(Vcb, IndexBufferSize, 
*NodeNumber);
+    IndexNodeOffset = GetAllocationOffsetFromVCN(Vcb, IndexBufferSize, *VCN);
 
     // TODO: Confirm index bitmap has this node marked as in-use
 
@@ -462,7 +539,7 @@
 
     ASSERT(BytesRead == IndexBufferSize);
     NT_ASSERT(NodeBuffer->Ntfs.Type == NRH_INDX_TYPE);
-    NT_ASSERT(NodeBuffer->VCN == *NodeNumber);
+    NT_ASSERT(NodeBuffer->VCN == *VCN);
 
     // Apply the fixup array to the node buffer
     Status = FixupUpdateSequenceArray(Vcb, &NodeBuffer->Ntfs);
@@ -516,8 +593,6 @@
             // See if the current key has a sub-node
             if (CurrentKey->IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
             {
-                DPRINT1("TODO: Only a node with a single-level is supported 
right now!\n");
-                // Needs debugging:
                 CurrentKey->LesserChild = CreateBTreeNodeFromIndexNode(Vcb,
                                                                        
IndexRoot,
                                                                        
IndexAllocationAttributeCtx,
@@ -535,8 +610,6 @@
             // See if the current key has a sub-node
             if (CurrentKey->IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
             {
-                DPRINT1("TODO: Only a node with a single-level is supported 
right now!\n");
-                // Needs debugging:
                 CurrentKey->LesserChild = CreateBTreeNodeFromIndexNode(Vcb,
                                                                        
IndexRoot,
                                                                        
IndexAllocationAttributeCtx,
@@ -551,8 +624,8 @@
         CurrentNodeEntry = 
(PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)CurrentNodeEntry + 
CurrentNodeEntry->Length);
     }
 
-    NewNode->NodeNumber = *NodeNumber;
-    NewNode->ExistsOnDisk = TRUE;
+    NewNode->VCN = *VCN;
+    NewNode->HasValidVCN = TRUE;
 
     ExFreePoolWithTag(NodeBuffer, TAG_NTFS);
 
@@ -622,8 +695,6 @@
                            NULL);
     if (!NT_SUCCESS(Status))
         IndexAllocationContext = NULL;
-    else
-        PrintAllVCNs(Vcb, IndexAllocationContext, IndexRoot->SizeOfEntry);
 
     // Setup the Tree
     RootNode->FirstKey = CurrentKey;
@@ -871,7 +942,7 @@
             NewIndexRoot->Header.Flags = INDEX_ROOT_LARGE;
 
         // Add Length of Current Entry to Total Size of Entries
-        NewIndexRoot->Header.TotalSizeOfEntries += CurrentNodeEntry->Length;
+        NewIndexRoot->Header.TotalSizeOfEntries += 
CurrentKey->IndexEntry->Length;
 
         // Go to the next node entry
         CurrentNodeEntry = 
(PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)CurrentNodeEntry + 
CurrentNodeEntry->Length);
@@ -905,10 +976,12 @@
     IndexBuffer->Ntfs.UsaCount = 9;
 
     // TODO: Check bitmap for VCN
-    ASSERT(Node->ExistsOnDisk);
-    IndexBuffer->VCN = Node->NodeNumber;
-
-    IndexBuffer->Header.FirstEntryOffset = 0x28;
+    ASSERT(Node->HasValidVCN);
+    IndexBuffer->VCN = Node->VCN;
+
+    // Windows seems to alternate between using 0x28 and 0x40 for the first 
entry offset of each index buffer.
+    // Interestingly, neither Windows nor chkdsk seem to mind if we just use 
0x28 for every index record.
+    IndexBuffer->Header.FirstEntryOffset = 0x28; 
     IndexBuffer->Header.AllocatedSize = BufferSize - 
FIELD_OFFSET(INDEX_BUFFER, Header);
 
     // Start summing the total size of this node's entries
@@ -934,9 +1007,9 @@
         // Copy the index entry
         RtlCopyMemory(CurrentNodeEntry, CurrentKey->IndexEntry, 
CurrentKey->IndexEntry->Length);
 
-        DPRINT1("Index Node Entry Stream Length: %u\nIndex Node Entry Length: 
%u\n",
-                CurrentNodeEntry->KeyLength,
-                CurrentNodeEntry->Length);
+        DPRINT("Index Node Entry Stream Length: %u\nIndex Node Entry Length: 
%u\n",
+               CurrentNodeEntry->KeyLength,
+               CurrentNodeEntry->Length);
 
         // Add Length of Current Entry to Total Size of Entries
         IndexBuffer->Header.TotalSizeOfEntries += CurrentNodeEntry->Length;
@@ -1019,14 +1092,6 @@
             {
                 DPRINT1("FIXME: Need to add index allocation\n");
                 return STATUS_NOT_IMPLEMENTED;
-            }
-           
-            Status = UpdateIndexNode(DeviceExt, FileRecord, 
CurrentKey->LesserChild, IndexBufferSize, IndexAllocationContext, 
IndexAllocationOffset, BitmapContext);
-            if (!NT_SUCCESS(Status))
-            {
-                DPRINT1("ERROR: Failed to update index node!\n");
-                ReleaseAttributeContext(IndexAllocationContext);
-                return Status;
             }
 
             // Is the Index Entry large enough to store the VCN?
@@ -1056,9 +1121,17 @@
                 CurrentKey->IndexEntry->Flags |= NTFS_INDEX_ENTRY_NODE;
             }
 
+            // Update the sub-node
+            Status = UpdateIndexNode(DeviceExt, FileRecord, 
CurrentKey->LesserChild, IndexBufferSize, IndexAllocationContext, 
IndexAllocationOffset, BitmapContext);
+            if (!NT_SUCCESS(Status))
+            {
+                DPRINT1("ERROR: Failed to update index node!\n");
+                ReleaseAttributeContext(IndexAllocationContext);
+                return Status;
+            }
+
             // Update the VCN stored in the index entry of CurrentKey
-            SetIndexEntryVCN(CurrentKey->IndexEntry, 
CurrentKey->LesserChild->NodeNumber);
-
+            SetIndexEntryVCN(CurrentKey->IndexEntry, 
CurrentKey->LesserChild->VCN);
         }
         CurrentKey = CurrentKey->NextKey;
     }
@@ -1096,7 +1169,7 @@
             IndexAllocationContext,
             IndexAllocationOffset,
             BitmapContext,
-            Node->NodeNumber);
+            Node->VCN);
 
     // Do we need to write this node to disk?
     if (Node->DiskNeedsUpdating)
@@ -1106,7 +1179,7 @@
         PINDEX_BUFFER IndexBuffer;
 
         // Does the node need to be assigned a VCN?
-        if (!Node->ExistsOnDisk)
+        if (!Node->HasValidVCN)
         {
             // Allocate the node
             Status = AllocateIndexNode(DeviceExt,
@@ -1114,14 +1187,14 @@
                                        IndexBufferSize,
                                        IndexAllocationContext,
                                        IndexAllocationOffset,
-                                       &Node->NodeNumber);
+                                       &Node->VCN);
             if (!NT_SUCCESS(Status))
             {
                 DPRINT1("ERROR: Failed to allocate index record in index 
allocation!\n");
                 return Status;
             }
 
-            Node->ExistsOnDisk = TRUE;
+            Node->HasValidVCN = TRUE;
         }
 
         // Allocate memory for an index buffer
@@ -1142,7 +1215,7 @@
         }
 
         // Get Offset of index buffer in index allocation
-        NodeOffset = GetAllocationOffsetFromVCN(DeviceExt, IndexBufferSize, 
Node->NodeNumber);
+        NodeOffset = GetAllocationOffsetFromVCN(DeviceExt, IndexBufferSize, 
Node->VCN);
 
         // Write the buffer to the index allocation
         Status = WriteAttribute(DeviceExt, IndexAllocationContext, NodeOffset, 
(const PUCHAR)IndexBuffer, IndexBufferSize, &LengthWritten, FileRecord);
@@ -1274,7 +1347,7 @@
 }
 
 VOID
-DumpBTreeKey(PB_TREE_KEY Key, ULONG Number, ULONG Depth)
+DumpBTreeKey(PB_TREE Tree, PB_TREE_KEY Key, ULONG Number, ULONG Depth)
 {
     ULONG i;
     for (i = 0; i < Depth; i++)
@@ -1298,7 +1371,7 @@
     if (Key->IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
     {
         if (Key->LesserChild)
-            DumpBTreeNode(Key->LesserChild, Number, Depth + 1);
+            DumpBTreeNode(Tree, Key->LesserChild, Number, Depth + 1);
         else
         {
             // This will be an assert once nodes with arbitrary depth are 
debugged
@@ -1308,18 +1381,28 @@
 }
 
 VOID
-DumpBTreeNode(PB_TREE_FILENAME_NODE Node, ULONG Number, ULONG Depth)
+DumpBTreeNode(PB_TREE Tree,
+              PB_TREE_FILENAME_NODE Node,
+              ULONG Number,
+              ULONG Depth)
 {
     PB_TREE_KEY CurrentKey;
     ULONG i;
     for (i = 0; i < Depth; i++)
         DbgPrint(" ");
-    DbgPrint("Node #%d, Depth %d, has %d key%s\n", Number, Depth, 
Node->KeyCount, Node->KeyCount == 1 ? "" : "s");
+    DbgPrint("Node #%d, Depth %d, has %d key%s", Number, Depth, 
Node->KeyCount, Node->KeyCount == 1 ? "" : "s");
+
+    if (Node->HasValidVCN)
+        DbgPrint(" VCN: %I64u\n", Node->VCN);
+    else if (Tree->RootNode == Node)
+        DbgPrint(" Index Root");
+    else
+        DbgPrint(" NOT ASSIGNED VCN YET\n");
 
     CurrentKey = Node->FirstKey;
-    for (i = 1; i <= Node->KeyCount; i++)
-    {
-        DumpBTreeKey(CurrentKey, i, Depth);
+    for (i = 0; i < Node->KeyCount; i++)
+    {
+        DumpBTreeKey(Tree, CurrentKey, i, Depth);
         CurrentKey = CurrentKey->NextKey;
     }
 }
@@ -1340,7 +1423,7 @@
 DumpBTree(PB_TREE Tree)
 {
     DbgPrint("B_TREE @ %p\n", Tree);
-    DumpBTreeNode(Tree->RootNode, 0, 0);
+    DumpBTreeNode(Tree, Tree->RootNode, 0, 0);
 }
 
 // Calculates start of Index Buffer relative to the index allocation, given 
the node's VCN
@@ -1525,7 +1608,6 @@
             NewIndexRoot->FirstKey = DummyKey;
             NewIndexRoot->KeyCount = 1;
             NewIndexRoot->DiskNeedsUpdating = TRUE;
-            NewIndexRoot->ExistsOnDisk = TRUE;
 
             // Make the new node the Tree's root node
             Tree->RootNode = NewIndexRoot;

Modified: branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/create.c
URL: 
http://svn.reactos.org/svn/reactos/branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/create.c?rev=75692&r1=75691&r2=75692&view=diff
==============================================================================
--- branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/create.c   [iso-8859-1] 
(original)
+++ branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/create.c   [iso-8859-1] 
Sun Aug 27 14:37:17 2017
@@ -569,25 +569,31 @@
                 return STATUS_ACCESS_DENIED;
             }
 
-            // We can't create directories yet
+            // Was the user trying to create a directory?
             if (RequestedOptions & FILE_DIRECTORY_FILE)
             {
-                DPRINT1("FIXME: Folder creation is still TODO!\n");
-                return STATUS_NOT_IMPLEMENTED;
+                // Create the directory on disk
+                Status = NtfsCreateDirectory(DeviceExt,
+                                             FileObject,
+                                             BooleanFlagOn(Stack->Flags, 
SL_CASE_SENSITIVE),
+                                             BooleanFlagOn(IrpContext->Flags, 
IRPCONTEXT_CANWAIT));
             }
-
-            // Create the file record on disk
-            Status = NtfsCreateFileRecord(DeviceExt,
-                                          FileObject,
-                                          BooleanFlagOn(Stack->Flags, 
SL_CASE_SENSITIVE),
-                                          
BooleanFlagOn(IrpContext->Flags,IRPCONTEXT_CANWAIT));
+            else
+            {
+                // Create the file record on disk
+                Status = NtfsCreateFileRecord(DeviceExt,
+                                              FileObject,
+                                              BooleanFlagOn(Stack->Flags, 
SL_CASE_SENSITIVE),
+                                              BooleanFlagOn(IrpContext->Flags, 
IRPCONTEXT_CANWAIT));
+            }
+
             if (!NT_SUCCESS(Status))
             {
                 DPRINT1("ERROR: Couldn't create file record!\n");
                 return Status;
             }
 
-            // Before we open the file we just created, we need to change the 
disposition (upper 8 bits of ULONG)
+            // Before we open the file/directory we just created, we need to 
change the disposition (upper 8 bits of ULONG)
             // from create to open, since we already created the file
             Stack->Parameters.Create.Options = (ULONG)FILE_OPEN << 24 | 
RequestedOptions;
 
@@ -649,6 +655,215 @@
 
     return Status;
 }
+
+/**
+* @name NtfsCreateDirectory()
+* @implemented
+*
+* Creates a file record for a new directory and saves it to the MFT. Adds the 
filename attribute of the
+* created directory to the parent directory's index.
+*
+* @param DeviceExt
+* Points to the target disk's DEVICE_EXTENSION
+*
+* @param FileObject
+* Pointer to a FILE_OBJECT describing the directory to be created
+*
+* @param CaseSensitive
+* Boolean indicating if the function should operate in case-sensitive mode. 
This will be TRUE
+* if an application created the folder with the FILE_FLAG_POSIX_SEMANTICS flag.
+*
+* @param CanWait
+* Boolean indicating if the function is allowed to wait for exclusive access 
to the master file table.
+* This will only be relevant if the MFT doesn't have any free file records and 
needs to be enlarged.
+*
+* @return
+* STATUS_SUCCESS on success.
+* STATUS_INSUFFICIENT_RESOURCES if unable to allocate memory for the file 
record.
+* STATUS_CANT_WAIT if CanWait was FALSE and the function needed to resize the 
MFT but
+* couldn't get immediate, exclusive access to it.
+*/
+NTSTATUS
+NtfsCreateDirectory(PDEVICE_EXTENSION DeviceExt,
+                    PFILE_OBJECT FileObject,
+                    BOOLEAN CaseSensitive,
+                    BOOLEAN CanWait)
+{
+
+    NTSTATUS Status = STATUS_SUCCESS;
+    PFILE_RECORD_HEADER FileRecord;
+    PNTFS_ATTR_RECORD NextAttribute;
+    PFILENAME_ATTRIBUTE FilenameAttribute;
+    ULONGLONG ParentMftIndex;
+    ULONGLONG FileMftIndex;
+    PB_TREE Tree;
+    PINDEX_ROOT_ATTRIBUTE NewIndexRoot;
+    ULONG RootLength;
+
+    DPRINT1("NtfsCreateFileRecord(%p, %p, %s, %s)\n",
+            DeviceExt,
+            FileObject,
+            CaseSensitive ? "TRUE" : "FALSE",
+            CanWait ? "TRUE" : "FALSE");
+
+    // Start with an empty file record
+    FileRecord = NtfsCreateEmptyFileRecord(DeviceExt);
+    if (!FileRecord)
+    {
+        DPRINT1("ERROR: Unable to allocate memory for file record!\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    // Set the directory flag
+    FileRecord->Flags |= FRH_DIRECTORY;
+
+    // find where the first attribute will be added
+    NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + 
FileRecord->AttributeOffset);
+
+    // 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, 
CaseSensitive, &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);
+
+    // Create an empty b-tree to represent our new index
+    Status = CreateEmptyBTree(&Tree);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("ERROR: Failed to create empty B-Tree!\n");
+        ExFreePoolWithTag(FileRecord, TAG_NTFS);
+        return Status;
+    }
+
+    // Calculate maximum size of index root
+    ULONG MaxIndexRootSize = DeviceExt->NtfsInfo.BytesPerFileRecord 
+                             - ((ULONG_PTR)NextAttribute - 
(ULONG_PTR)FileRecord)
+                             - sizeof(ULONG) * 2;
+
+    // Create a new index record from the tree
+    Status = CreateIndexRootFromBTree(DeviceExt,
+                                      Tree,
+                                      MaxIndexRootSize,
+                                      &NewIndexRoot,
+                                      &RootLength);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("ERROR: Unable to create empty index root!\n");
+        DestroyBTree(Tree);
+        ExFreePoolWithTag(FileRecord, TAG_NTFS);
+        return Status;
+    }
+
+    // We're done with the B-Tree
+    DestroyBTree(Tree);
+
+    // add the $INDEX_ROOT attribute
+    Status = AddIndexRoot(DeviceExt, FileRecord, NextAttribute, NewIndexRoot, 
RootLength, L"$I30", 4);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("ERROR: Failed to add index root to new file record!\n");
+        ExFreePoolWithTag(FileRecord, TAG_NTFS);
+        return Status;
+    }
+
+
+#ifndef NDEBUG
+    NtfsDumpFileRecord(DeviceExt, FileRecord);
+#endif
+
+    // Now that we've built the file record in memory, we need to store it in 
the MFT.
+    Status = AddNewMftEntry(FileRecord, DeviceExt, &FileMftIndex, CanWait);
+    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,
+                                            CaseSensitive);
+    }
+
+    ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
+    ExFreePoolWithTag(FileRecord, TAG_NTFS);
+
+    return Status;
+}
+
+/**
+* @name NtfsCreateEmptyFileRecord
+* @implemented
+*
+* Creates a new, empty file record, with no attributes.
+*
+* @param DeviceExt
+* Pointer to the DEVICE_EXTENSION of the target volume the file record will be 
stored on.
+* 
+* @return
+* A pointer to the newly-created FILE_RECORD_HEADER if the function succeeds, 
NULL otherwise.
+*/
+PFILE_RECORD_HEADER
+NtfsCreateEmptyFileRecord(PDEVICE_EXTENSION DeviceExt)
+{
+    PFILE_RECORD_HEADER FileRecord;
+    PNTFS_ATTR_RECORD NextAttribute;
+
+    DPRINT1("NtfsCreateEmptyFileRecord(%p)\n", DeviceExt);
+
+    // 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 NULL;
+    }
+
+    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, 
ATTR_RECORD_ALIGNMENT);
+    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;
+
+    return FileRecord;
+}
+
 
 /**
 * @name NtfsCreateFileRecord()
@@ -693,43 +908,19 @@
             CanWait ? "TRUE" : "FALSE");
 
     // allocate memory for file record
-    FileRecord = ExAllocatePoolWithTag(NonPagedPool,
-                                       DeviceExt->NtfsInfo.BytesPerFileRecord,
-                                       TAG_NTFS);
+    FileRecord = NtfsCreateEmptyFileRecord(DeviceExt);
     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, 
ATTR_RECORD_ALIGNMENT);
-    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);
 
@@ -745,8 +936,10 @@
     // add the $DATA attribute
     AddData(FileRecord, NextAttribute);
 
+#ifndef NDEBUG
     // dump file record in memory (for debugging)
     NtfsDumpFileRecord(DeviceExt, FileRecord);
+#endif
 
     // Now that we've built the file record in memory, we need to store it in 
the MFT.
     Status = AddNewMftEntry(FileRecord, DeviceExt, &FileMftIndex, CanWait);

Modified: branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/mft.c
URL: 
http://svn.reactos.org/svn/reactos/branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/mft.c?rev=75692&r1=75691&r2=75692&view=diff
==============================================================================
--- branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/mft.c      [iso-8859-1] 
(original)
+++ branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/mft.c      [iso-8859-1] 
Sun Aug 27 14:37:17 2017
@@ -1893,6 +1893,7 @@
     ULONGLONG BitmapDataSize;
     ULONGLONG AttrBytesRead;
     PUCHAR BitmapData;
+    PUCHAR BitmapBuffer;
     ULONG LengthWritten;
     PNTFS_ATTR_CONTEXT BitmapContext;
     LARGE_INTEGER BitmapBits;
@@ -1901,6 +1902,8 @@
     DPRINT1("AddNewMftEntry(%p, %p, %p, %s)\n", FileRecord, DeviceExt, 
DestinationIndex, CanWait ? "TRUE" : "FALSE");
 
     // First, we have to read the mft's $Bitmap attribute
+
+    // Find the attribute
     Status = FindAttribute(DeviceExt, DeviceExt->MasterFileTable, 
AttributeBitmap, L"", 0, &BitmapContext, NULL);
     if (!NT_SUCCESS(Status))
     {
@@ -1908,22 +1911,28 @@
         return Status;
     }
 
-    // allocate a buffer for the $Bitmap attribute
+    // Get size of bitmap
     BitmapDataSize = AttributeDataLength(BitmapContext->pRecord);
-    BitmapData = ExAllocatePoolWithTag(NonPagedPool, BitmapDataSize, TAG_NTFS);
-    if (!BitmapData)
+
+    // RtlInitializeBitmap wants a ULONG-aligned pointer, and wants the memory 
passed to it to be a ULONG-multiple
+    // Allocate a buffer for the $Bitmap attribute plus enough to ensure we 
can get a ULONG-aligned pointer
+    BitmapBuffer = ExAllocatePoolWithTag(NonPagedPool, BitmapDataSize + 
sizeof(ULONG), TAG_NTFS);
+    if (!BitmapBuffer)
     {
         ReleaseAttributeContext(BitmapContext);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
+    // Get a ULONG-aligned pointer for the bitmap itself
+    BitmapData = (PUCHAR)ALIGN_UP_BY((ULONG_PTR)BitmapBuffer, sizeof(ULONG));
+
     // read $Bitmap attribute
     AttrBytesRead = ReadAttribute(DeviceExt, BitmapContext, 0, 
(PCHAR)BitmapData, BitmapDataSize);
 
-    if (AttrBytesRead == 0)
+    if (AttrBytesRead != BitmapDataSize)
     {
         DPRINT1("ERROR: Unable to read $Bitmap attribute of master file 
table!\n");
-        ExFreePoolWithTag(BitmapData, TAG_NTFS);
+        ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
         ReleaseAttributeContext(BitmapContext);
         return STATUS_OBJECT_NAME_NOT_FOUND;
     }
@@ -1939,7 +1948,8 @@
     if (BitmapBits.HighPart != 0)
     {
         DPRINT1("\tFIXME: bitmap sizes beyond 32bits are not yet supported! 
(Your NTFS volume is too large)\n");
-        ExFreePoolWithTag(BitmapData, TAG_NTFS);
+        NtfsGlobalData->EnableWriteSupport = FALSE;
+        ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
         ReleaseAttributeContext(BitmapContext);
         return STATUS_NOT_IMPLEMENTED;
     }
@@ -1953,7 +1963,7 @@
     {
         DPRINT1("Couldn't find free space in MFT for file record, increasing 
MFT size.\n");
 
-        ExFreePoolWithTag(BitmapData, TAG_NTFS);
+        ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
         ReleaseAttributeContext(BitmapContext);
 
         // Couldn't find a free record in the MFT, add some blank records and 
try again
@@ -1982,7 +1992,7 @@
     if (!NT_SUCCESS(Status))
     {
         DPRINT1("ERROR encountered when writing $Bitmap attribute!\n");
-        ExFreePoolWithTag(BitmapData, TAG_NTFS);
+        ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
         ReleaseAttributeContext(BitmapContext);
         return Status;
     }
@@ -1993,14 +2003,14 @@
     if (!NT_SUCCESS(Status))
     {
         DPRINT1("ERROR: Unable to write file record!\n");
-        ExFreePoolWithTag(BitmapData, TAG_NTFS);
+        ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
         ReleaseAttributeContext(BitmapContext);
         return Status;
     }
 
     *DestinationIndex = MftIndex;
 
-    ExFreePoolWithTag(BitmapData, TAG_NTFS);
+    ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
     ReleaseAttributeContext(BitmapContext);
 
     return Status;
@@ -2255,7 +2265,7 @@
                             AttributeLength,
                             &LengthWritten,
                             ParentFileRecord);
-    if (!NT_SUCCESS(Status))
+    if (!NT_SUCCESS(Status) || LengthWritten != AttributeLength)
     {
         DPRINT1("ERROR: Unable to write new index root attribute to parent 
directory!\n");
         ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);

Modified: branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/ntfs.h
URL: 
http://svn.reactos.org/svn/reactos/branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/ntfs.h?rev=75692&r1=75691&r2=75692&view=diff
==============================================================================
--- branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/ntfs.h     [iso-8859-1] 
(original)
+++ branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/ntfs.h     [iso-8859-1] 
Sun Aug 27 14:37:17 2017
@@ -304,6 +304,12 @@
 // relative to the beginning of the file record.
 #define ATTR_RECORD_ALIGNMENT 8
 
+// Data runs are aligned to a 4-byte boundary, relative to the start of the 
attribute record
+#define DATA_RUN_ALIGNMENT  4
+
+// Value offset is aligned to a 4-byte boundary, relative to the start of the 
attribute record
+#define VALUE_OFFSET_ALIGNMENT  4
+
 typedef struct
 {
     ULONGLONG CreationTime;
@@ -423,9 +429,9 @@
 typedef struct _B_TREE_FILENAME_NODE
 {
     ULONG KeyCount;
-    BOOLEAN ExistsOnDisk;
+    BOOLEAN HasValidVCN;
     BOOLEAN DiskNeedsUpdating;
-    ULONGLONG NodeNumber;
+    ULONGLONG VCN;
     PB_TREE_KEY FirstKey;
 } B_TREE_FILENAME_NODE, *PB_TREE_FILENAME_NODE;
 
@@ -571,6 +577,15 @@
        ULONG RunLength);
 
 NTSTATUS
+AddIndexRoot(PNTFS_VCB Vcb,
+             PFILE_RECORD_HEADER FileRecord,
+             PNTFS_ATTR_RECORD AttributeAddress,
+             PINDEX_ROOT_ATTRIBUTE NewIndexRoot,
+             ULONG RootLength,
+             PCWSTR Name,
+             USHORT NameLength);
+
+NTSTATUS
 AddFileName(PFILE_RECORD_HEADER FileRecord,
             PNTFS_ATTR_RECORD AttributeAddress,
             PDEVICE_EXTENSION DeviceExt,
@@ -718,9 +733,19 @@
 DumpBTree(PB_TREE Tree);
 
 VOID
-DumpBTreeNode(PB_TREE_FILENAME_NODE Node,
+DumpBTreeKey(PB_TREE Tree,
+             PB_TREE_KEY Key,
+             ULONG Number,
+             ULONG Depth);
+
+VOID
+DumpBTreeNode(PB_TREE Tree,
+              PB_TREE_FILENAME_NODE Node,
               ULONG Number,
               ULONG Depth);
+
+NTSTATUS
+CreateEmptyBTree(PB_TREE *NewTree);
 
 ULONGLONG
 GetAllocationOffsetFromVCN(PDEVICE_EXTENSION DeviceExt,
@@ -770,6 +795,15 @@
 
 NTSTATUS
 NtfsCreate(PNTFS_IRP_CONTEXT IrpContext);
+
+NTSTATUS
+NtfsCreateDirectory(PDEVICE_EXTENSION DeviceExt,
+                    PFILE_OBJECT FileObject,
+                    BOOLEAN CaseSensitive,
+                    BOOLEAN CanWait);
+
+PFILE_RECORD_HEADER
+NtfsCreateEmptyFileRecord(PDEVICE_EXTENSION DeviceExt);
 
 NTSTATUS
 NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt,


Reply via email to