https://git.reactos.org/?p=reactos.git;a=commitdiff;h=88a7c3d14b5f74300baac…
commit 88a7c3d14b5f74300baacff3486b377901c2467f
Author: Trevor Thompson <tmt256(a)email.vccs.edu>
AuthorDate: Tue Aug 15 19:57:55 2017 +0000
[NTFS] - Allow for creating a file when the index root gets too large and needs to be moved into an index record. Add some helper functions.
+AllocateIndexNode() - Allocates a new index record in an index allocation.
+CreateDummyKey() - Creates the final B_TREE_KEY for a B_TREE_FILENAME_NODE. Also creates the associated index entry.
GetSizeOfIndexEntries() - Sums the size of each index entry in every key in a B-Tree node.
+SetIndexEntryVCN() - Sets the VCN of a given IndexEntry.
NtfsInsertKey() - Handle instance when the index root grows too large. If it does, add its contents to a new sub-node, and replace contents with a dummy-key whose child is the new node.
UpdateIndexAllocation() - Update index entry if a key has just been assigned a child allocation.
UpdateIndexNode() - Make sure the node exists on disk, and allocate an index record for it if it doesn't.
svn path=/branches/GSoC_2016/NTFS/; revision=75557
---
drivers/filesystems/ntfs/btree.c | 520 ++++++++++++++++++++++++++++++++++++---
drivers/filesystems/ntfs/mft.c | 35 ++-
drivers/filesystems/ntfs/ntfs.h | 8 +-
3 files changed, 519 insertions(+), 44 deletions(-)
diff --git a/drivers/filesystems/ntfs/btree.c b/drivers/filesystems/ntfs/btree.c
index 648111d9c0..727c147a2a 100644
--- a/drivers/filesystems/ntfs/btree.c
+++ b/drivers/filesystems/ntfs/btree.c
@@ -80,6 +80,237 @@ PrintAllVCNs(PDEVICE_EXTENSION Vcb,
ExFreePoolWithTag(Buffer, TAG_NTFS);
}
+/**
+* @name AllocateIndexNode
+* @implemented
+*
+* Allocates a new index record in an index allocation.
+*
+* @param DeviceExt
+* Pointer to the target DEVICE_EXTENSION describing the volume the node will be created on.
+*
+* @param FileRecord
+* Pointer to a copy of the file record containing the index.
+*
+* @param IndexBufferSize
+* Size of an index record for this index, in bytes. Commonly defined as 4096.
+*
+* @param IndexAllocationCtx
+* Pointer to an NTFS_ATTR_CONTEXT describing the index allocation attribute the node will be assigned to.
+*
+* @param IndexAllocationOffset
+* Offset of the index allocation attribute relative to the file record.
+*
+* @param NewVCN
+* Pointer to a ULONGLONG which will receive the VCN of the newly-assigned index record
+*
+* @returns
+* STATUS_SUCCESS in case of success.
+* STATUS_NOT_IMPLEMENTED if there's no $I30 bitmap attribute in the file record.
+*
+* @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.
+*/
+NTSTATUS
+AllocateIndexNode(PDEVICE_EXTENSION DeviceExt,
+ PFILE_RECORD_HEADER FileRecord,
+ ULONG IndexBufferSize,
+ PNTFS_ATTR_CONTEXT IndexAllocationCtx,
+ ULONG IndexAllocationOffset,
+ PULONGLONG NewVCN)
+{
+ NTSTATUS Status;
+ PNTFS_ATTR_CONTEXT BitmapCtx;
+ ULONGLONG IndexAllocationLength, BitmapLength;
+ ULONG BitmapOffset;
+ ULONGLONG NextNodeNumber;
+ PCHAR *BitmapMem;
+ ULONG *BitmapPtr;
+ RTL_BITMAP Bitmap;
+ ULONG BytesWritten;
+ ULONG BytesNeeded;
+ LARGE_INTEGER DataSize;
+
+ DPRINT1("AllocateIndexNode(%p, %p, %lu, %p, %lu, %p) called.\n", DeviceExt,
+ FileRecord,
+ IndexBufferSize,
+ IndexAllocationCtx,
+ IndexAllocationOffset,
+ NewVCN);
+
+ // Get the length of the attribute allocation
+ IndexAllocationLength = AttributeDataLength(IndexAllocationCtx->pRecord);
+
+ // Find the bitmap attribute for the index
+ Status = FindAttribute(DeviceExt,
+ FileRecord,
+ AttributeBitmap,
+ L"$I30",
+ 4,
+ &BitmapCtx,
+ &BitmapOffset);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("FIXME: Need to add bitmap attribute!\n");
+ return STATUS_NOT_IMPLEMENTED;
+ }
+
+ // Get the length of the bitmap attribute
+ BitmapLength = AttributeDataLength(BitmapCtx->pRecord);
+
+ NextNodeNumber = IndexAllocationLength / DeviceExt->NtfsInfo.BytesPerIndexRecord;
+
+ // TODO: Find unused allocation in bitmap and use that space first
+
+ // Add another bit to bitmap
+
+ // See how many bytes we need to store the amount of bits we'll have
+ BytesNeeded = NextNodeNumber / 8;
+ if (NextNodeNumber % 8 != 0)
+ 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);
+
+ // Do we need to enlarge the bitmap?
+ if (BytesNeeded > BitmapLength)
+ {
+ // 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 (!NT_SUCCESS(Status))
+ {
+ DPRINT1("ERROR: Failed to set length of bitmap attribute!\n");
+ ReleaseAttributeContext(BitmapCtx);
+ return Status;
+ }
+ }
+
+ // Enlarge Index Allocation attribute
+ DataSize.QuadPart = IndexAllocationLength + IndexBufferSize;
+ Status = SetNonResidentAttributeDataLength(DeviceExt,
+ IndexAllocationCtx,
+ IndexAllocationOffset,
+ FileRecord,
+ &DataSize);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("ERROR: Failed to set length of index allocation!\n");
+ ReleaseAttributeContext(BitmapCtx);
+ return Status;
+ }
+
+ // Update file record on disk
+ Status = UpdateFileRecord(DeviceExt, IndexAllocationCtx->FileMFTIndex, FileRecord);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("ERROR: Failed to update file record!\n");
+ 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);
+
+ // Write the new bitmap attribute
+ Status = WriteAttribute(DeviceExt,
+ BitmapCtx,
+ 0,
+ (const PUCHAR)BitmapPtr,
+ BytesNeeded,
+ &BytesWritten,
+ FileRecord);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("ERROR: Unable to write to $I30 bitmap attribute!\n");
+ }
+
+ // Calculate VCN of new node number
+ *NewVCN = NextNodeNumber * (IndexBufferSize / DeviceExt->NtfsInfo.BytesPerCluster);
+
+ ExFreePoolWithTag(BitmapMem, TAG_NTFS);
+ ReleaseAttributeContext(BitmapCtx);
+
+ return Status;
+}
+
+/**
+* @name CreateDummyKey
+* @implemented
+*
+* Creates the final B_TREE_KEY for a B_TREE_FILENAME_NODE. Also creates the associated index entry.
+*
+* @param HasChildNode
+* BOOLEAN to indicate if this key will have a LesserChild.
+*
+* @return
+* The newly-created key.
+*/
+PB_TREE_KEY
+CreateDummyKey(BOOLEAN HasChildNode)
+{
+ PINDEX_ENTRY_ATTRIBUTE NewIndexEntry;
+ PB_TREE_KEY NewDummyKey;
+
+ // Calculate max size of a dummy key
+ ULONG EntrySize = ALIGN_UP_BY(FIELD_OFFSET(INDEX_ENTRY_ATTRIBUTE, FileName), 8);
+ EntrySize += sizeof(ULONGLONG); // for VCN
+
+ // Create the index entry for the key
+ NewIndexEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_NTFS);
+ if (!NewIndexEntry)
+ {
+ DPRINT1("Couldn't allocate memory for dummy key index entry!\n");
+ return NULL;
+ }
+
+ RtlZeroMemory(NewIndexEntry, EntrySize);
+
+ if (HasChildNode)
+ {
+ NewIndexEntry->Flags = NTFS_INDEX_ENTRY_NODE | NTFS_INDEX_ENTRY_END;
+ }
+ else
+ {
+ NewIndexEntry->Flags = NTFS_INDEX_ENTRY_END;
+ EntrySize -= sizeof(ULONGLONG); // no VCN
+ }
+
+ NewIndexEntry->Length = EntrySize;
+
+ // Create the key
+ NewDummyKey = ExAllocatePoolWithTag(NonPagedPool, sizeof(B_TREE_KEY), TAG_NTFS);
+ if (!NewDummyKey)
+ {
+ DPRINT1("Unable to allocate dummy key!\n");
+ ExFreePoolWithTag(NewIndexEntry, TAG_NTFS);
+ return NULL;
+ }
+ RtlZeroMemory(NewDummyKey, sizeof(B_TREE_KEY));
+
+ NewDummyKey->IndexEntry = NewIndexEntry;
+
+ return NewDummyKey;
+}
+
/**
* @name CompareTreeKeys
* @implemented
@@ -500,6 +731,42 @@ CreateBTreeFromIndex(PDEVICE_EXTENSION Vcb,
return STATUS_SUCCESS;
}
+/**
+* @name GetSizeOfIndexEntries
+* @implemented
+*
+* Sums the size of each index entry in every key in a B-Tree node.
+*
+* @param Node
+* Pointer to a B_TREE_FILENAME_NODE. The size of this node's index entries will be returned.
+*
+* @returns
+* The sum of the sizes of every index entry for each key in the B-Tree node.
+*
+* @remarks
+* Gets only the size of the index entries; doesn't include the size of any headers that would be added to an index record.
+*/
+ULONG
+GetSizeOfIndexEntries(PB_TREE_FILENAME_NODE Node)
+{
+ // Start summing the total size of this node's entries
+ ULONG NodeSize = 0;
+
+ // Walk through the list of Node Entries
+ PB_TREE_KEY CurrentKey = Node->FirstKey;
+ ULONG i;
+ for (i = 0; i < Node->KeyCount; i++)
+ {
+ ASSERT(CurrentKey->IndexEntry->Length != 0);
+
+ // Add the length of the current node
+ NodeSize += CurrentKey->IndexEntry->Length;
+ CurrentKey = CurrentKey->NextKey;
+ }
+
+ return NodeSize;
+}
+
/**
* @name CreateIndexRootFromBTree
* @implemented
@@ -686,6 +953,33 @@ CreateIndexBufferFromBTreeNode(PDEVICE_EXTENSION DeviceExt,
return Status;
}
+/**
+* @name SetIndexEntryVCN
+* @implemented
+*
+* Sets the VCN of a given IndexEntry.
+*
+* @param IndexEntry
+* Pointer to an INDEX_ENTRY_ATTRIBUTE structure that will have its VCN set.
+*
+* @param VCN
+* VCN to store in the index entry.
+*
+* @remarks
+* The index entry must have enough memory allocated to store the VCN, and must have the NTFS_INDEX_ENTRY_NODE flag set.
+* The VCN of an index entry is stored at the very end of the structure, after the filename attribute. Since the filename
+* attribute can be a variable size, this function makes setting this member easy.
+*/
+VOID
+SetIndexEntryVCN(PINDEX_ENTRY_ATTRIBUTE IndexEntry, ULONGLONG VCN)
+{
+ PULONGLONG Destination = (PULONGLONG)((ULONG_PTR)IndexEntry + IndexEntry->Length - sizeof(ULONGLONG));
+
+ ASSERT(IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE);
+
+ *Destination = VCN;
+}
+
NTSTATUS
UpdateIndexAllocation(PDEVICE_EXTENSION DeviceExt,
PB_TREE Tree,
@@ -698,13 +992,20 @@ UpdateIndexAllocation(PDEVICE_EXTENSION DeviceExt,
NTSTATUS Status;
BOOLEAN HasIndexAllocation = FALSE;
ULONG i;
+ ULONG IndexAllocationOffset;
- DPRINT1("UpdateIndexAllocations() called.\n");
+ DPRINT1("UpdateIndexAllocation() called.\n");
- Status = FindAttribute(DeviceExt, FileRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationContext, NULL);
+ Status = FindAttribute(DeviceExt, FileRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationContext, &IndexAllocationOffset);
if (NT_SUCCESS(Status))
+ {
HasIndexAllocation = TRUE;
+ PrintAllVCNs(DeviceExt,
+ IndexAllocationContext,
+ IndexBufferSize);
+ }
+
// TODO: Handle bitmap
BitmapContext = NULL;
@@ -719,48 +1020,112 @@ UpdateIndexAllocation(PDEVICE_EXTENSION DeviceExt,
DPRINT1("FIXME: Need to add index allocation\n");
return STATUS_NOT_IMPLEMENTED;
}
- else
+
+ Status = UpdateIndexNode(DeviceExt, FileRecord, CurrentKey->LesserChild, IndexBufferSize, IndexAllocationContext, IndexAllocationOffset, BitmapContext);
+ if (!NT_SUCCESS(Status))
{
- Status = UpdateIndexNode(DeviceExt, CurrentKey->LesserChild, IndexBufferSize, IndexAllocationContext, 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?
+ if (!CurrentKey->IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
+ {
+ // Allocate memory for the larger index entry
+ PINDEX_ENTRY_ATTRIBUTE NewEntry = ExAllocatePoolWithTag(NonPagedPool,
+ CurrentKey->IndexEntry->Length + sizeof(ULONGLONG),
+ TAG_NTFS);
+ if (!NewEntry)
{
- DPRINT1("ERROR: Failed to update index node!\n");
- ReleaseAttributeContext(IndexAllocationContext);
- return Status;
+ DPRINT1("ERROR: Unable to allocate memory for new index entry!\n");
+ if (HasIndexAllocation)
+ ReleaseAttributeContext(IndexAllocationContext);
+ return STATUS_INSUFFICIENT_RESOURCES;
}
+
+ // Copy the old entry to the new one
+ RtlCopyMemory(NewEntry, CurrentKey->IndexEntry, CurrentKey->IndexEntry->Length);
+
+ NewEntry->Length += sizeof(ULONGLONG);
+
+ // Free the old memory
+ ExFreePoolWithTag(CurrentKey->IndexEntry, TAG_NTFS);
+
+ CurrentKey->IndexEntry = NewEntry;
+ CurrentKey->IndexEntry->Flags |= NTFS_INDEX_ENTRY_NODE;
}
+ // Update the VCN stored in the index entry of CurrentKey
+ SetIndexEntryVCN(CurrentKey->IndexEntry, CurrentKey->LesserChild->NodeNumber);
+
}
CurrentKey = CurrentKey->NextKey;
}
- if(HasIndexAllocation)
+ if (HasIndexAllocation)
+ {
+ PrintAllVCNs(DeviceExt,
+ IndexAllocationContext,
+ IndexBufferSize);
+
ReleaseAttributeContext(IndexAllocationContext);
+ }
return STATUS_SUCCESS;
}
NTSTATUS
UpdateIndexNode(PDEVICE_EXTENSION DeviceExt,
+ PFILE_RECORD_HEADER FileRecord,
PB_TREE_FILENAME_NODE Node,
ULONG IndexBufferSize,
PNTFS_ATTR_CONTEXT IndexAllocationContext,
+ ULONG IndexAllocationOffset,
PNTFS_ATTR_CONTEXT BitmapContext)
{
ULONG i;
PB_TREE_KEY CurrentKey = Node->FirstKey;
NTSTATUS Status;
- DPRINT1("UpdateIndexNode(%p, %p, %lu, %p, %p) called for index node with VCN %I64u\n", DeviceExt, Node, IndexBufferSize, IndexAllocationContext, BitmapContext, Node->NodeNumber);
+ DPRINT1("UpdateIndexNode(%p, %p, %p, %lu, %p, %lu, %p) called for index node with VCN %I64u\n",
+ DeviceExt,
+ FileRecord,
+ Node,
+ IndexBufferSize,
+ IndexAllocationContext,
+ IndexAllocationOffset,
+ BitmapContext,
+ Node->NodeNumber);
// Do we need to write this node to disk?
if (Node->DiskNeedsUpdating)
{
ULONGLONG NodeOffset;
ULONG LengthWritten;
+ PINDEX_BUFFER IndexBuffer;
+
+ // Does the node need to be assigned a VCN?
+ if (!Node->ExistsOnDisk)
+ {
+ // Allocate the node
+ Status = AllocateIndexNode(DeviceExt,
+ FileRecord,
+ IndexBufferSize,
+ IndexAllocationContext,
+ IndexAllocationOffset,
+ &Node->NodeNumber);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("ERROR: Failed to allocate index record in index allocation!\n");
+ return Status;
+ }
+
+ Node->ExistsOnDisk = TRUE;
+ }
// Allocate memory for an index buffer
- PINDEX_BUFFER IndexBuffer = ExAllocatePoolWithTag(NonPagedPool, IndexBufferSize, TAG_NTFS);
+ IndexBuffer = ExAllocatePoolWithTag(NonPagedPool, IndexBufferSize, TAG_NTFS);
if (!IndexBuffer)
{
DPRINT1("ERROR: Failed to allocate %lu bytes for index buffer!\n", IndexBufferSize);
@@ -780,7 +1145,7 @@ UpdateIndexNode(PDEVICE_EXTENSION DeviceExt,
NodeOffset = GetAllocationOffsetFromVCN(DeviceExt, IndexBufferSize, Node->NodeNumber);
// Write the buffer to the index allocation
- Status = WriteAttribute(DeviceExt, IndexAllocationContext, NodeOffset, (const PUCHAR)IndexBuffer, IndexBufferSize, &LengthWritten, NULL);
+ Status = WriteAttribute(DeviceExt, IndexAllocationContext, NodeOffset, (const PUCHAR)IndexBuffer, IndexBufferSize, &LengthWritten, FileRecord);
if (!NT_SUCCESS(Status) || LengthWritten != IndexBufferSize)
{
DPRINT1("ERROR: Failed to update index allocation!\n");
@@ -806,7 +1171,7 @@ UpdateIndexNode(PDEVICE_EXTENSION DeviceExt,
if (CurrentKey->LesserChild)
{
// Update the child node on disk
- Status = UpdateIndexNode(DeviceExt, CurrentKey->LesserChild, IndexBufferSize, IndexAllocationContext, BitmapContext);
+ Status = UpdateIndexNode(DeviceExt, FileRecord, CurrentKey->LesserChild, IndexBufferSize, IndexAllocationContext, IndexAllocationOffset, BitmapContext);
if (!NT_SUCCESS(Status))
{
DPRINT1("ERROR: Failed to update child node!\n");
@@ -996,6 +1361,9 @@ GetAllocationOffsetFromVCN(PDEVICE_EXTENSION DeviceExt,
*
* Inserts a FILENAME_ATTRIBUTE into a B-Tree node.
*
+* @param Tree
+* Pointer to the B_TREE the key (filename attribute) is being inserted into.
+*
* @param FileReference
* Reference number to the file being added. This will be a combination of the MFT index and update sequence number.
*
@@ -1009,27 +1377,35 @@ GetAllocationOffsetFromVCN(PDEVICE_EXTENSION DeviceExt,
* 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.
*
+* @param MaxIndexRootSize
+* The maximum size, in bytes, of node entries that can be stored in the index root before it will grow too large for
+* the file record. This number is just the size of the entries, without any headers for the attribute or index root.
+*
* @remarks
* A node is always sorted, with the least comparable filename stored first and a dummy key to mark the end.
*/
NTSTATUS
-NtfsInsertKey(ULONGLONG FileReference,
+NtfsInsertKey(PB_TREE Tree,
+ ULONGLONG FileReference,
PFILENAME_ATTRIBUTE FileNameAttribute,
PB_TREE_FILENAME_NODE Node,
- BOOLEAN CaseSensitive)
+ BOOLEAN CaseSensitive,
+ ULONG MaxIndexRootSize)
{
PB_TREE_KEY NewKey, CurrentKey, PreviousKey;
- NTSTATUS Status = STATUS_SUCCESS;
+ NTSTATUS Status = STATUS_SUCCESS;
ULONG NodeSize;
ULONG AllocatedNodeSize;
ULONG MaxNodeSizeWithoutHeader;
ULONG i;
- DPRINT1("NtfsInsertKey(0x%I64x, %p, %p, %s)\n",
+ DPRINT1("NtfsInsertKey(%p, 0x%I64x, %p, %p, %s, %lu)\n",
+ Tree,
FileReference,
FileNameAttribute,
Node,
- CaseSensitive ? "TRUE" : "FALSE");
+ CaseSensitive ? "TRUE" : "FALSE",
+ MaxIndexRootSize);
// Create the key for the filename attribute
NewKey = CreateBTreeKeyFromFilename(FileReference, FileNameAttribute);
@@ -1044,6 +1420,11 @@ NtfsInsertKey(ULONGLONG FileReference,
// Should the New Key go before the current key?
LONG Comparison = CompareTreeKeys(NewKey, CurrentKey, CaseSensitive);
+ if (Comparison == 0)
+ {
+ DPRINT1("\t\tComparison == 0: %.*S\n", NewKey->IndexEntry->FileName.NameLength, NewKey->IndexEntry->FileName.Name);
+ DPRINT1("\t\tComparison == 0: %.*S\n", CurrentKey->IndexEntry->FileName.NameLength, CurrentKey->IndexEntry->FileName.Name);
+ }
ASSERT(Comparison != 0);
// Is NewKey < CurrentKey?
@@ -1054,7 +1435,14 @@ NtfsInsertKey(ULONGLONG FileReference,
if (CurrentKey->LesserChild)
{
// Insert the key into the child node
- Status = NtfsInsertKey(FileReference, FileNameAttribute, CurrentKey->LesserChild, CaseSensitive);
+ Status = NtfsInsertKey(Tree, FileReference, FileNameAttribute, CurrentKey->LesserChild, CaseSensitive, 0);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("ERROR: Failed to insert key.\n");
+ ExFreePoolWithTag(NewKey, TAG_NTFS);
+ return Status;
+ }
+
}
else
{
@@ -1078,25 +1466,89 @@ NtfsInsertKey(ULONGLONG FileReference,
CurrentKey = CurrentKey->NextKey;
}
- // Is the node larger than its allocated size?
- NodeSize = 0;
- CurrentKey = Node->FirstKey;
- for (i = 0; i < Node->KeyCount; i++)
+ // Determine how much space the index entries will need
+ NodeSize = GetSizeOfIndexEntries(Node);
+
+ // Is Node the root node?
+ if (Node == Tree->RootNode)
{
- NodeSize += CurrentKey->IndexEntry->Length;
- CurrentKey = CurrentKey->NextKey;
- }
+ // Is the index root too large for the file record?
+ if (NodeSize > MaxIndexRootSize)
+ {
+ PB_TREE_FILENAME_NODE NewSubNode, NewIndexRoot;
+ PB_TREE_KEY DummyKey;
+
+ DPRINT1("Collapsing Index Root into sub-node.\n") ;
+
+ DumpBTree(Tree);
+
+ // Create a new node that will hold the keys currently in index root
+ NewSubNode = ExAllocatePoolWithTag(NonPagedPool, sizeof(B_TREE_FILENAME_NODE), TAG_NTFS);
+ if (!NewSubNode)
+ {
+ DPRINT1("ERROR: Couldn't allocate memory for new sub-node.\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+ RtlZeroMemory(NewSubNode, sizeof(B_TREE_FILENAME_NODE));
+
+ // Copy the applicable data from the old index root node
+ NewSubNode->KeyCount = Node->KeyCount;
+ NewSubNode->FirstKey = Node->FirstKey;
+ NewSubNode->DiskNeedsUpdating = TRUE;
+
+ // Create a new dummy key, and make the new node it's child
+ DummyKey = CreateDummyKey(TRUE);
+ if (!DummyKey)
+ {
+ DPRINT1("ERROR: Couldn't allocate memory for new root node.\n");
+ ExFreePoolWithTag(NewSubNode, TAG_NTFS);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ // Make the new node a child of the dummy key
+ DummyKey->LesserChild = NewSubNode;
+
+ // Create a new index root node
+ NewIndexRoot = ExAllocatePoolWithTag(NonPagedPool, sizeof(B_TREE_FILENAME_NODE), TAG_NTFS);
+ if (!NewIndexRoot)
+ {
+ DPRINT1("ERROR: Couldn't allocate memory for new index root.\n");
+ ExFreePoolWithTag(NewSubNode, TAG_NTFS);
+ ExFreePoolWithTag(DummyKey, TAG_NTFS);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+ RtlZeroMemory(NewIndexRoot, sizeof(B_TREE_FILENAME_NODE));
+
+ NewIndexRoot->DiskNeedsUpdating = TRUE;
+
+ // Insert the dummy key into the new node
+ NewIndexRoot->FirstKey = DummyKey;
+ NewIndexRoot->KeyCount = 1;
+ NewIndexRoot->DiskNeedsUpdating = TRUE;
+ NewIndexRoot->ExistsOnDisk = TRUE;
- // TEMPTEMP: TODO: MATH
- AllocatedNodeSize = 0xfe8;
- MaxNodeSizeWithoutHeader = AllocatedNodeSize - 0x28;
+ // Make the new node the Tree's root node
+ Tree->RootNode = NewIndexRoot;
- if (NodeSize > MaxNodeSizeWithoutHeader)
+ DumpBTree(Tree);
+
+ return STATUS_SUCCESS;
+ }
+ }
+ else
{
- DPRINT1("FIXME: Splitting a node is still a WIP!\n");
- //SplitBTreeNode(NULL, Node);
- //DumpBTree(Tree);
- return STATUS_NOT_IMPLEMENTED;
+ // TEMPTEMP: TODO: MATH
+ AllocatedNodeSize = 0xfe8;
+ MaxNodeSizeWithoutHeader = AllocatedNodeSize - 0x28;
+
+ // Has the node grown larger than its allocated size?
+ if (NodeSize > MaxNodeSizeWithoutHeader)
+ {
+ DPRINT1("FIXME: Splitting a node is still a WIP!\n");
+ //SplitBTreeNode(NULL, Node);
+ //DumpBTree(Tree);
+ return STATUS_NOT_IMPLEMENTED;
+ }
}
// NewEntry and NewKey will be destroyed later by DestroyBTree()
diff --git a/drivers/filesystems/ntfs/mft.c b/drivers/filesystems/ntfs/mft.c
index aeee447e33..0cd2767efb 100644
--- a/drivers/filesystems/ntfs/mft.c
+++ b/drivers/filesystems/ntfs/mft.c
@@ -2056,9 +2056,10 @@ NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt,
ULONG LengthWritten;
PINDEX_ROOT_ATTRIBUTE NewIndexRoot;
ULONG AttributeLength;
+ PNTFS_ATTR_RECORD NextAttribute;
PB_TREE NewTree;
ULONG BtreeIndexLength;
- ULONG MaxIndexSize;
+ ULONG MaxIndexRootSize;
// Allocate memory for the parent directory
ParentFileRecord = ExAllocatePoolWithTag(NonPagedPool,
@@ -2100,11 +2101,29 @@ NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt,
}
// Find the maximum index size given what the file record can hold
- MaxIndexSize = DeviceExt->NtfsInfo.BytesPerFileRecord
- - IndexRootOffset
- - IndexRootContext->pRecord->Resident.ValueOffset
- - FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header)
- - (sizeof(ULONG) * 2);
+ // First, find the max index size assuming index root is the last attribute
+ MaxIndexRootSize = DeviceExt->NtfsInfo.BytesPerFileRecord // Start with the size of a file record
+ - IndexRootOffset // Subtract the length of everything that comes before index root
+ - IndexRootContext->pRecord->Resident.ValueOffset // Subtract the length of the attribute header for index root
+ - FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header) // Subtract the length of the index header for index root
+ - (sizeof(ULONG) * 2); // Subtract the length of the file record end marker and padding
+
+ // Are there attributes after this one?
+ NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)ParentFileRecord + IndexRootOffset + IndexRootContext->pRecord->Length);
+ if (NextAttribute->Type != AttributeEnd)
+ {
+ // Find the length of all attributes after this one, not counting the end marker
+ ULONG LengthOfAttributes = 0;
+ PNTFS_ATTR_RECORD CurrentAttribute = NextAttribute;
+ while (CurrentAttribute->Type != AttributeEnd)
+ {
+ LengthOfAttributes += CurrentAttribute->Length;
+ CurrentAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)CurrentAttribute + CurrentAttribute->Length);
+ }
+
+ // Leave room for the existing attributes
+ MaxIndexRootSize -= LengthOfAttributes;
+ }
// Allocate memory for the index root data
I30IndexRootLength = AttributeDataLength(IndexRootContext->pRecord);
@@ -2146,7 +2165,7 @@ NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt,
DumpBTree(NewTree);
// Insert the key for the file we're adding
- Status = NtfsInsertKey(FileReferenceNumber, FilenameAttribute, NewTree->RootNode, CaseSensitive);
+ Status = NtfsInsertKey(NewTree, FileReferenceNumber, FilenameAttribute, NewTree->RootNode, CaseSensitive, MaxIndexRootSize);
if (!NT_SUCCESS(Status))
{
DPRINT1("ERROR: Failed to insert key into B-Tree!\n");
@@ -2172,7 +2191,7 @@ NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt,
}
// Create the Index Root from the B*Tree
- Status = CreateIndexRootFromBTree(DeviceExt, NewTree, MaxIndexSize, &NewIndexRoot, &BtreeIndexLength);
+ Status = CreateIndexRootFromBTree(DeviceExt, NewTree, MaxIndexRootSize, &NewIndexRoot, &BtreeIndexLength);
if (!NT_SUCCESS(Status))
{
DPRINT1("ERROR: Failed to create Index root from B-Tree!\n");
diff --git a/drivers/filesystems/ntfs/ntfs.h b/drivers/filesystems/ntfs/ntfs.h
index 4d436a699e..030b777c22 100644
--- a/drivers/filesystems/ntfs/ntfs.h
+++ b/drivers/filesystems/ntfs/ntfs.h
@@ -728,10 +728,12 @@ GetAllocationOffsetFromVCN(PDEVICE_EXTENSION DeviceExt,
ULONGLONG Vcn);
NTSTATUS
-NtfsInsertKey(ULONGLONG FileReference,
+NtfsInsertKey(PB_TREE Tree,
+ ULONGLONG FileReference,
PFILENAME_ATTRIBUTE FileNameAttribute,
PB_TREE_FILENAME_NODE Node,
- BOOLEAN CaseSensitive);
+ BOOLEAN CaseSensitive,
+ ULONG MaxIndexRootSize);
NTSTATUS
UpdateIndexAllocation(PDEVICE_EXTENSION DeviceExt,
@@ -741,9 +743,11 @@ UpdateIndexAllocation(PDEVICE_EXTENSION DeviceExt,
NTSTATUS
UpdateIndexNode(PDEVICE_EXTENSION DeviceExt,
+ PFILE_RECORD_HEADER FileRecord,
PB_TREE_FILENAME_NODE Node,
ULONG IndexBufferSize,
PNTFS_ATTR_CONTEXT IndexAllocationContext,
+ ULONG IndexAllocationOffset,
PNTFS_ATTR_CONTEXT BitmapContext);
/* close.c */
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=d484d91eba5724f885cf6…
commit d484d91eba5724f885cf6662e9e8bd87c99d561b
Author: Trevor Thompson <tmt256(a)email.vccs.edu>
AuthorDate: Tue Aug 15 19:32:20 2017 +0000
[NTFS] - Allow for resizing an attribute in the middle of a file record. Add a helper function and minor improvements:
AddRun() - Allow for resizing the size of the data runs when the attribute isn't the last in the file record. Fix some comments.
CreateIndexBufferFromBTreeNode(), CreateIndexRootFromBTree - Fix math of IndexSize when checking if the index buffer is too large.
InternalSetResidentAttributeLength() - Allow changing the length of an attribute in the middle of a file record. Adjust the position of every attribute after the one being resized.
+MoveAttributes() - Moves a block of attributes to a new location in the file Record.
PrintAllVCNs() - Add consideration for an index allocation with a size of 0.
WriteAttribute() - Add optional parameter for a pointer to the file record being written to. If passed a file record, WriteAttribute() will skip reading the file record from disk, and will update the file record in memory before returning. This helps callers that use the file record after writing an attribute to stay in-sync with what's on disk.
svn path=/branches/GSoC_2016/NTFS/; revision=75554
---
drivers/filesystems/ntfs/attrib.c | 47 +++++---
drivers/filesystems/ntfs/btree.c | 26 +++--
drivers/filesystems/ntfs/mft.c | 234 +++++++++++++++++++++++++------------
drivers/filesystems/ntfs/ntfs.h | 12 +-
drivers/filesystems/ntfs/rw.c | 2 +-
drivers/filesystems/ntfs/volinfo.c | 2 +-
6 files changed, 219 insertions(+), 104 deletions(-)
diff --git a/drivers/filesystems/ntfs/attrib.c b/drivers/filesystems/ntfs/attrib.c
index 8618862344..e6d3483553 100644
--- a/drivers/filesystems/ntfs/attrib.c
+++ b/drivers/filesystems/ntfs/attrib.c
@@ -352,18 +352,35 @@ AddRun(PNTFS_VCB Vcb,
PNTFS_ATTR_RECORD NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + NextAttributeOffset);
PNTFS_ATTR_RECORD NewRecord;
- DataRunMaxLength += Vcb->NtfsInfo.BytesPerFileRecord - NextAttributeOffset - (sizeof(ULONG) * 2);
+ // Add free space at the end of the file record to DataRunMaxLength
+ DataRunMaxLength += Vcb->NtfsInfo.BytesPerFileRecord - FileRecord->BytesInUse;
- // Can we move the end of the attribute?
- if (NextAttribute->Type != AttributeEnd || DataRunMaxLength < RunBufferSize - 1)
+ // Can we resize the attribute?
+ if (DataRunMaxLength < RunBufferSize)
{
- DPRINT1("FIXME: Need to create attribute list! Max Data Run Length available: %d\n", DataRunMaxLength);
- if (NextAttribute->Type != AttributeEnd)
- DPRINT1("There's another attribute after this one with type %0xlx\n", NextAttribute->Type);
+ DPRINT1("FIXME: Need to create attribute list! Max Data Run Length available: %d, RunBufferSize: %d\n", DataRunMaxLength, RunBufferSize);
ExFreePoolWithTag(RunBuffer, TAG_NTFS);
return STATUS_NOT_IMPLEMENTED;
}
+ // Are there more attributes after the one we're resizing?
+ if (NextAttribute->Type != AttributeEnd)
+ {
+ PNTFS_ATTR_RECORD FinalAttribute;
+
+ // Calculate where to move the trailing attributes
+ ULONG_PTR MoveTo = (ULONG_PTR)DestinationAttribute + AttrContext->pRecord->NonResident.MappingPairsOffset + RunBufferSize;
+ MoveTo = ALIGN_UP_BY(MoveTo, ATTR_RECORD_ALIGNMENT);
+
+ DPRINT1("Moving attribute(s) after this one starting with type 0x%lx\n", NextAttribute->Type);
+
+ // Move the trailing attributes; FinalAttribute will point to the end marker
+ FinalAttribute = MoveAttributes(Vcb, NextAttribute, NextAttributeOffset, MoveTo);
+
+ // set the file record end
+ SetFileRecordEnd(FileRecord, FinalAttribute, FILE_RECORD_END);
+ }
+
// calculate position of end markers
NextAttributeOffset = AttrOffset + AttrContext->pRecord->NonResident.MappingPairsOffset + RunBufferSize;
NextAttributeOffset = ALIGN_UP_BY(NextAttributeOffset, ATTR_RECORD_ALIGNMENT);
@@ -371,20 +388,24 @@ AddRun(PNTFS_VCB Vcb,
// Update the length of the destination attribute
DestinationAttribute->Length = NextAttributeOffset - AttrOffset;
- // Create a new copy of the attribute
+ // Create a new copy of the attribute record
NewRecord = ExAllocatePoolWithTag(NonPagedPool, DestinationAttribute->Length, TAG_NTFS);
RtlCopyMemory(NewRecord, AttrContext->pRecord, AttrContext->pRecord->Length);
NewRecord->Length = DestinationAttribute->Length;
- // Free the old copy of the attribute, which won't be large enough
+ // Free the old copy of the attribute record, which won't be large enough
ExFreePoolWithTag(AttrContext->pRecord, TAG_NTFS);
// Set the attribute context's record to the new copy
AttrContext->pRecord = NewRecord;
- // End the file record
- NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + NextAttributeOffset);
- SetFileRecordEnd(FileRecord, NextAttribute, FILE_RECORD_END);
+ // if NextAttribute is the AttributeEnd marker
+ if (NextAttribute->Type == AttributeEnd)
+ {
+ // End the file record
+ NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + NextAttributeOffset);
+ SetFileRecordEnd(FileRecord, NextAttribute, FILE_RECORD_END);
+ }
}
// Update HighestVCN
@@ -397,7 +418,7 @@ AddRun(PNTFS_VCB Vcb,
RunBuffer,
RunBufferSize);
- // Update the attribute copy in the attribute context
+ // Update the attribute record in the attribute context
RtlCopyMemory((PVOID)((ULONG_PTR)AttrContext->pRecord + AttrContext->pRecord->NonResident.MappingPairsOffset),
RunBuffer,
RunBufferSize);
@@ -827,7 +848,7 @@ FreeClusters(PNTFS_VCB Vcb,
}
// update $BITMAP file on disk
- Status = WriteAttribute(Vcb, DataContext, 0, BitmapData, (ULONG)BitmapDataSize, &LengthWritten);
+ Status = WriteAttribute(Vcb, DataContext, 0, BitmapData, (ULONG)BitmapDataSize, &LengthWritten, FileRecord);
if (!NT_SUCCESS(Status))
{
ReleaseAttributeContext(DataContext);
diff --git a/drivers/filesystems/ntfs/btree.c b/drivers/filesystems/ntfs/btree.c
index dbbf3f9f87..648111d9c0 100644
--- a/drivers/filesystems/ntfs/btree.c
+++ b/drivers/filesystems/ntfs/btree.c
@@ -46,6 +46,12 @@ PrintAllVCNs(PDEVICE_EXTENSION Vcb,
ULONGLONG i;
int Count = 0;
+ if (BufferSize == 0)
+ {
+ DPRINT1("Index Allocation is empty.\n");
+ return;
+ }
+
Buffer = ExAllocatePoolWithTag(NonPagedPool, BufferSize, TAG_NTFS);
BytesRead = ReadAttribute(Vcb, IndexAllocationContext, 0, (PCHAR)Buffer, BufferSize);
@@ -281,10 +287,10 @@ CreateBTreeNodeFromIndexNode(PDEVICE_EXTENSION Vcb,
{
DPRINT1("TODO: Only a node with a single-level is supported right now!\n");
// Needs debugging:
- /*CurrentKey->LesserChild = CreateBTreeNodeFromIndexNode(Vcb,
+ CurrentKey->LesserChild = CreateBTreeNodeFromIndexNode(Vcb,
IndexRoot,
IndexAllocationAttributeCtx,
- CurrentNodeEntry);*/
+ CurrentKey->IndexEntry);
}
CurrentKey = NextKey;
@@ -300,10 +306,10 @@ CreateBTreeNodeFromIndexNode(PDEVICE_EXTENSION Vcb,
{
DPRINT1("TODO: Only a node with a single-level is supported right now!\n");
// Needs debugging:
- /*CurrentKey->LesserChild = CreateBTreeNodeFromIndexNode(Vcb,
- IndexRoot,
- IndexAllocationAttributeCtx,
- CurrentNodeEntry);*/
+ CurrentKey->LesserChild = CreateBTreeNodeFromIndexNode(Vcb,
+ IndexRoot,
+ IndexAllocationAttributeCtx,
+ CurrentKey->IndexEntry);
}
break;
@@ -575,7 +581,6 @@ CreateIndexRootFromBTree(PDEVICE_EXTENSION DeviceExt,
{
// Would adding the current entry to the index increase the index size beyond the limit we've set?
ULONG IndexSize = FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header)
- + NewIndexRoot->Header.FirstEntryOffset
+ NewIndexRoot->Header.TotalSizeOfEntries
+ CurrentNodeEntry->Length;
if (IndexSize > MaxIndexSize)
@@ -649,7 +654,6 @@ CreateIndexBufferFromBTreeNode(PDEVICE_EXTENSION DeviceExt,
{
// Would adding the current entry to the index increase the node size beyond the allocation size?
ULONG IndexSize = FIELD_OFFSET(INDEX_BUFFER, Header)
- + IndexBuffer->Header.FirstEntryOffset
+ IndexBuffer->Header.TotalSizeOfEntries
+ CurrentNodeEntry->Length;
if (IndexSize > BufferSize)
@@ -776,7 +780,7 @@ UpdateIndexNode(PDEVICE_EXTENSION DeviceExt,
NodeOffset = GetAllocationOffsetFromVCN(DeviceExt, IndexBufferSize, Node->NodeNumber);
// Write the buffer to the index allocation
- Status = WriteAttribute(DeviceExt, IndexAllocationContext, NodeOffset, (const PUCHAR)IndexBuffer, IndexBufferSize, &LengthWritten);
+ Status = WriteAttribute(DeviceExt, IndexAllocationContext, NodeOffset, (const PUCHAR)IndexBuffer, IndexBufferSize, &LengthWritten, NULL);
if (!NT_SUCCESS(Status) || LengthWritten != IndexBufferSize)
{
DPRINT1("ERROR: Failed to update index allocation!\n");
@@ -945,10 +949,10 @@ DumpBTreeNode(PB_TREE_FILENAME_NODE Node, ULONG Number, ULONG Depth)
ULONG i;
for (i = 0; i < Depth; i++)
DbgPrint(" ");
- DbgPrint("Node #%d, Depth %d, has %d keys\n", Number, Depth, Node->KeyCount);
+ DbgPrint("Node #%d, Depth %d, has %d key%s\n", Number, Depth, Node->KeyCount, Node->KeyCount == 1 ? "" : "s");
CurrentKey = Node->FirstKey;
- for (i = 0; i < Node->KeyCount; i++)
+ for (i = 1; i <= Node->KeyCount; i++)
{
DumpBTreeKey(CurrentKey, i, Depth);
CurrentKey = CurrentKey->NextKey;
diff --git a/drivers/filesystems/ntfs/mft.c b/drivers/filesystems/ntfs/mft.c
index 23f802dfca..aeee447e33 100644
--- a/drivers/filesystems/ntfs/mft.c
+++ b/drivers/filesystems/ntfs/mft.c
@@ -351,7 +351,7 @@ IncreaseMftSize(PDEVICE_EXTENSION Vcb, BOOLEAN CanWait)
}
// Write out the new bitmap
- Status = WriteAttribute(Vcb, BitmapContext, BitmapOffset, BitmapBuffer, BitmapSize.LowPart, &LengthWritten);
+ Status = WriteAttribute(Vcb, BitmapContext, BitmapOffset, BitmapBuffer, BitmapSize.LowPart, &LengthWritten, Vcb->MasterFileTable);
if (!NT_SUCCESS(Status))
{
ExReleaseResourceLite(&(Vcb->DirResource));
@@ -369,17 +369,72 @@ IncreaseMftSize(PDEVICE_EXTENSION Vcb, BOOLEAN CanWait)
return STATUS_SUCCESS;
}
+/**
+* @name MoveAttributes
+* @implemented
+*
+* Moves a block of attributes to a new location in the file Record. The attribute at FirstAttributeToMove
+* and every attribute after that will be moved to MoveTo.
+*
+* @param DeviceExt
+* Pointer to the DEVICE_EXTENSION (VCB) of the target volume.
+*
+* @param FirstAttributeToMove
+* Pointer to the first NTFS_ATTR_RECORD that needs to be moved. This pointer must reside within a file record.
+*
+* @param FirstAttributeOffset
+* Offset of FirstAttributeToMove relative to the beginning of the file record.
+*
+* @param MoveTo
+* ULONG_PTR with the memory location that will be the new location of the first attribute being moved.
+*
+* @return
+* The new location of the final attribute (i.e. AttributeEnd marker).
+*/
+PNTFS_ATTR_RECORD
+MoveAttributes(PDEVICE_EXTENSION DeviceExt,
+ PNTFS_ATTR_RECORD FirstAttributeToMove,
+ ULONG FirstAttributeOffset,
+ ULONG_PTR MoveTo)
+{
+ // Get the size of all attributes after this one
+ ULONG MemBlockSize = 0;
+ PNTFS_ATTR_RECORD CurrentAttribute = FirstAttributeToMove;
+ ULONG CurrentOffset = FirstAttributeOffset;
+ PNTFS_ATTR_RECORD FinalAttribute;
+
+ while (CurrentAttribute->Type != AttributeEnd && CurrentOffset < DeviceExt->NtfsInfo.BytesPerFileRecord)
+ {
+ CurrentOffset += CurrentAttribute->Length;
+ MemBlockSize += CurrentAttribute->Length;
+ CurrentAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)CurrentAttribute + CurrentAttribute->Length);
+ }
+
+ FinalAttribute = (PNTFS_ATTR_RECORD)(MoveTo + MemBlockSize);
+ MemBlockSize += sizeof(ULONG) * 2; // Add the AttributeEnd and file record end
+
+ ASSERT(MemBlockSize % ATTR_RECORD_ALIGNMENT == 0);
+
+ // Move the attributes after this one
+ RtlMoveMemory((PCHAR)MoveTo, FirstAttributeToMove, MemBlockSize);
+
+ return FinalAttribute;
+}
+
NTSTATUS
-InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext,
+InternalSetResidentAttributeLength(PDEVICE_EXTENSION DeviceExt,
+ PNTFS_ATTR_CONTEXT AttrContext,
PFILE_RECORD_HEADER FileRecord,
ULONG AttrOffset,
ULONG DataSize)
{
PNTFS_ATTR_RECORD Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
+ PNTFS_ATTR_RECORD NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)Destination + Destination->Length);
+ PNTFS_ATTR_RECORD FinalAttribute;
+ ULONG OldAttributeLength = Destination->Length;
ULONG NextAttributeOffset;
- USHORT ValueOffset;
- DPRINT("InternalSetResidentAttributeLength( %p, %p, %lu, %lu )\n", AttrContext, FileRecord, AttrOffset, DataSize);
+ DPRINT1("InternalSetResidentAttributeLength( %p, %p, %p, %lu, %lu )\n", DeviceExt, AttrContext, FileRecord, AttrOffset, DataSize);
ASSERT(!AttrContext->pRecord->IsNonResident);
@@ -390,30 +445,46 @@ InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext,
Destination->Length = ALIGN_UP_BY(DataSize + AttrContext->pRecord->Resident.ValueOffset, ATTR_RECORD_ALIGNMENT);
NextAttributeOffset = AttrOffset + Destination->Length;
- // Ensure FileRecord has an up-to-date copy of the attribute
- ValueOffset = AttrContext->pRecord->Resident.ValueOffset;
- RtlCopyMemory((PCHAR)((ULONG_PTR)FileRecord + AttrOffset + ValueOffset),
- (PCHAR)((ULONG_PTR)AttrContext->pRecord + ValueOffset),
- min(DataSize, AttrContext->pRecord->Resident.ValueLength));
+ // Ensure NextAttributeOffset is aligned to an 8-byte boundary
+ ASSERT(NextAttributeOffset % ATTR_RECORD_ALIGNMENT == 0);
- // Free the old copy of the attribute in the context, as it will be the wrong length
- ExFreePoolWithTag(AttrContext->pRecord, TAG_NTFS);
+ // Will the new attribute be larger than the old one?
+ if (Destination->Length > OldAttributeLength)
+ {
+ // Free the old copy of the attribute in the context, as it will be the wrong length
+ ExFreePoolWithTag(AttrContext->pRecord, TAG_NTFS);
- // Create a new copy of the attribute for the context
- AttrContext->pRecord = ExAllocatePoolWithTag(NonPagedPool, Destination->Length, TAG_NTFS);
- if (!AttrContext->pRecord)
+ // Create a new copy of the attribute record for the context
+ AttrContext->pRecord = ExAllocatePoolWithTag(NonPagedPool, Destination->Length, TAG_NTFS);
+ if (!AttrContext->pRecord)
+ {
+ DPRINT1("Unable to allocate memory for attribute!\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+ RtlZeroMemory((PVOID)((ULONG_PTR)AttrContext->pRecord + OldAttributeLength), Destination->Length - OldAttributeLength);
+ RtlCopyMemory(AttrContext->pRecord, Destination, OldAttributeLength);
+ }
+
+ // Are there attributes after this one that need to be moved?
+ if (NextAttribute->Type != AttributeEnd)
{
- DPRINT1("Unable to allocate memory for attribute!\n");
- return STATUS_INSUFFICIENT_RESOURCES;
+ // Move the attributes after this one
+ FinalAttribute = MoveAttributes(DeviceExt, NextAttribute, NextAttributeOffset, (ULONG_PTR)Destination + Destination->Length);
+ }
+ else
+ {
+ // advance to the final "attribute," adjust for the changed length of the attribute we're resizing
+ FinalAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute - OldAttributeLength + Destination->Length);
}
- RtlCopyMemory(AttrContext->pRecord, Destination, Destination->Length);
- // Ensure NextAttributeOffset is aligned to an 8-byte boundary
- ASSERT(NextAttributeOffset % ATTR_RECORD_ALIGNMENT == 0);
-
- // advance Destination to the final "attribute" and set the file record end
- Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)Destination + Destination->Length);
- SetFileRecordEnd(FileRecord, Destination, FILE_RECORD_END);
+ // Update pRecord's length
+ AttrContext->pRecord->Length = Destination->Length;
+ AttrContext->pRecord->Resident.ValueLength = DataSize;
+
+ // set the file record end
+ SetFileRecordEnd(FileRecord, FinalAttribute, FILE_RECORD_END);
+
+ //NtfsDumpFileRecord(DeviceExt, FileRecord);
return STATUS_SUCCESS;
}
@@ -519,6 +590,9 @@ SetFileRecordEnd(PFILE_RECORD_HEADER FileRecord,
PNTFS_ATTR_RECORD AttrEnd,
ULONG EndMarker)
{
+ // Ensure AttrEnd is aligned on an 8-byte boundary, relative to FileRecord
+ ASSERT(((ULONG_PTR)AttrEnd - (ULONG_PTR)FileRecord) % ATTR_RECORD_ALIGNMENT == 0);
+
// mark the end of attributes
AttrEnd->Type = AttributeEnd;
@@ -841,7 +915,7 @@ SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
// restore the back-up attribute, if we made one
if (AttribDataSize.QuadPart > 0)
{
- Status = WriteAttribute(Vcb, AttrContext, 0, AttribData, AttribDataSize.QuadPart, &LengthWritten);
+ Status = WriteAttribute(Vcb, AttrContext, 0, AttribData, AttribDataSize.QuadPart, &LengthWritten, FileRecord);
if (!NT_SUCCESS(Status))
{
DPRINT1("ERROR: Unable to write attribute data to non-resident clusters during migration!\n");
@@ -855,19 +929,10 @@ SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
}
}
}
- else if (DataSize->LowPart < AttrContext->pRecord->Resident.ValueLength)
- {
- // we need to decrease the length
- if (NextAttribute->Type != AttributeEnd)
- {
- DPRINT1("FIXME: Don't know how to decrease length of resident attribute unless it's the final attribute!\n");
- return STATUS_NOT_IMPLEMENTED;
- }
- }
// set the new length of the resident attribute (if we didn't migrate it)
if (!AttrContext->pRecord->IsNonResident)
- return InternalSetResidentAttributeLength(AttrContext, FileRecord, AttrOffset, DataSize->LowPart);
+ return InternalSetResidentAttributeLength(Vcb, AttrContext, FileRecord, AttrOffset, DataSize->LowPart);
return STATUS_SUCCESS;
}
@@ -1102,6 +1167,12 @@ ReadAttribute(PDEVICE_EXTENSION Vcb,
* @param RealLengthWritten
* Pointer to a ULONG which will receive how much data was written, in bytes
*
+* @param FileRecord
+* Optional pointer to a FILE_RECORD_HEADER that contains a copy of the file record
+* being written to. Can be NULL, in which case the file record will be read from disk.
+* If not-null, WriteAttribute() will skip reading from disk, and FileRecord
+* will be updated with the newly-written attribute before the function returns.
+*
* @return
* STATUS_SUCCESS if successful, an error code otherwise. STATUS_NOT_IMPLEMENTED if
* writing to a sparse file.
@@ -1117,7 +1188,8 @@ WriteAttribute(PDEVICE_EXTENSION Vcb,
ULONGLONG Offset,
const PUCHAR Buffer,
ULONG Length,
- PULONG RealLengthWritten)
+ PULONG RealLengthWritten,
+ PFILE_RECORD_HEADER FileRecord)
{
ULONGLONG LastLCN;
PUCHAR DataRun;
@@ -1129,12 +1201,13 @@ WriteAttribute(PDEVICE_EXTENSION Vcb,
NTSTATUS Status;
PUCHAR SourceBuffer = Buffer;
LONGLONG StartingOffset;
+ BOOLEAN FileRecordAllocated = FALSE;
//TEMPTEMP
PUCHAR TempBuffer;
- DPRINT("WriteAttribute(%p, %p, %I64u, %p, %lu, %p)\n", Vcb, Context, Offset, Buffer, Length, RealLengthWritten);
+ DPRINT("WriteAttribute(%p, %p, %I64u, %p, %lu, %p, %p)\n", Vcb, Context, Offset, Buffer, Length, RealLengthWritten, FileRecord);
*RealLengthWritten = 0;
@@ -1143,7 +1216,10 @@ WriteAttribute(PDEVICE_EXTENSION Vcb,
{
ULONG AttributeOffset;
PNTFS_ATTR_CONTEXT FoundContext;
- PFILE_RECORD_HEADER FileRecord;
+ PNTFS_ATTR_RECORD Destination;
+
+ // Ensure requested data is within the bounds of the attribute
+ ASSERT(Offset + Length <= Context->pRecord->Resident.ValueLength);
if (Offset + Length > Context->pRecord->Resident.ValueLength)
{
@@ -1151,16 +1227,21 @@ WriteAttribute(PDEVICE_EXTENSION Vcb,
return STATUS_INVALID_PARAMETER;
}
- FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
-
- if (!FileRecord)
+ // Do we need to read the file record?
+ if (FileRecord == NULL)
{
- DPRINT1("Error: Couldn't allocate file record!\n");
- return STATUS_NO_MEMORY;
- }
+ FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
+ if (!FileRecord)
+ {
+ DPRINT1("Error: Couldn't allocate file record!\n");
+ return STATUS_NO_MEMORY;
+ }
+
+ FileRecordAllocated = TRUE;
- // read the file record
- ReadFileRecord(Vcb, Context->FileMFTIndex, FileRecord);
+ // read the file record
+ ReadFileRecord(Vcb, Context->FileMFTIndex, FileRecord);
+ }
// find where to write the attribute data to
Status = FindAttribute(Vcb, FileRecord,
@@ -1173,28 +1254,37 @@ WriteAttribute(PDEVICE_EXTENSION Vcb,
if (!NT_SUCCESS(Status))
{
DPRINT1("ERROR: Couldn't find matching attribute!\n");
- ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ if(FileRecordAllocated)
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
return Status;
}
+ Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttributeOffset);
+
DPRINT("Offset: %I64u, AttributeOffset: %u, ValueOffset: %u\n", Offset, AttributeOffset, Context->pRecord->Resident.ValueLength);
- Offset += AttributeOffset + Context->pRecord->Resident.ValueOffset;
-
- if (Offset + Length > Vcb->NtfsInfo.BytesPerFileRecord)
+
+ // Will we be writing past the end of the allocated file record?
+ if (Offset + Length + AttributeOffset + Context->pRecord->Resident.ValueOffset > Vcb->NtfsInfo.BytesPerFileRecord)
{
DPRINT1("DRIVER ERROR: Data being written extends past end of file record!\n");
ReleaseAttributeContext(FoundContext);
- ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ if (FileRecordAllocated)
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
return STATUS_INVALID_PARAMETER;
}
- // copy the data being written into the file record
- RtlCopyMemory((PCHAR)FileRecord + Offset, Buffer, Length);
+ // copy the data being written into the file record. We cast Offset to ULONG, which is safe because it's range has been verified.
+ RtlCopyMemory((PCHAR)((ULONG_PTR)Destination + Context->pRecord->Resident.ValueOffset + (ULONG)Offset), Buffer, Length);
Status = UpdateFileRecord(Vcb, Context->FileMFTIndex, FileRecord);
+ // Update the context's copy of the resident attribute
+ ASSERT(Context->pRecord->Length == Destination->Length);
+ RtlCopyMemory((PVOID)Context->pRecord, Destination, Context->pRecord->Length);
+
ReleaseAttributeContext(FoundContext);
- ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ if (FileRecordAllocated)
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
if (NT_SUCCESS(Status))
*RealLengthWritten = Length;
@@ -1519,7 +1609,7 @@ UpdateFileNameRecord(PDEVICE_EXTENSION Vcb,
{
// we need to write the index root attribute back to disk
ULONG LengthWritten;
- Status = WriteAttribute(Vcb, IndexRootCtx, 0, (PUCHAR)IndexRecord, AttributeDataLength(IndexRootCtx->pRecord), &LengthWritten);
+ Status = WriteAttribute(Vcb, IndexRootCtx, 0, (PUCHAR)IndexRecord, AttributeDataLength(IndexRootCtx->pRecord), &LengthWritten, MftRecord);
if (!NT_SUCCESS(Status))
{
DPRINT1("ERROR: Couldn't update Index Root!\n");
@@ -1661,7 +1751,7 @@ UpdateIndexEntryFileNameSize(PDEVICE_EXTENSION Vcb,
break;
}
- Status = WriteAttribute(Vcb, IndexAllocationCtx, RecordOffset, (const PUCHAR)IndexRecord, IndexBlockSize, &Written);
+ Status = WriteAttribute(Vcb, IndexAllocationCtx, RecordOffset, (const PUCHAR)IndexRecord, IndexBlockSize, &Written, MftRecord);
if (!NT_SUCCESS(Status))
{
DPRINT1("ERROR Performing write!\n");
@@ -1714,7 +1804,13 @@ UpdateFileRecord(PDEVICE_EXTENSION Vcb,
AddFixupArray(Vcb, &FileRecord->Ntfs);
// write the file record to the master file table
- Status = WriteAttribute(Vcb, Vcb->MFTContext, MftIndex * Vcb->NtfsInfo.BytesPerFileRecord, (const PUCHAR)FileRecord, Vcb->NtfsInfo.BytesPerFileRecord, &BytesWritten);
+ Status = WriteAttribute(Vcb,
+ Vcb->MFTContext,
+ MftIndex * Vcb->NtfsInfo.BytesPerFileRecord,
+ (const PUCHAR)FileRecord,
+ Vcb->NtfsInfo.BytesPerFileRecord,
+ &BytesWritten,
+ FileRecord);
if (!NT_SUCCESS(Status))
{
@@ -1882,7 +1978,7 @@ AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
BitmapData[2] = SystemReservedBits;
// write the bitmap back to the MFT's $Bitmap attribute
- Status = WriteAttribute(DeviceExt, BitmapContext, 0, BitmapData, BitmapDataSize, &LengthWritten);
+ Status = WriteAttribute(DeviceExt, BitmapContext, 0, BitmapData, BitmapDataSize, &LengthWritten, FileRecord);
if (!NT_SUCCESS(Status))
{
DPRINT1("ERROR encountered when writing $Bitmap attribute!\n");
@@ -1958,10 +2054,8 @@ NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt,
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;
@@ -2065,7 +2159,7 @@ NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt,
DumpBTree(NewTree);
- // Convert B*Tree back to Index
+ // Convert B*Tree back to Index, starting with the index allocation
Status = UpdateIndexAllocation(DeviceExt, NewTree, I30IndexRoot->SizeOfEntry, ParentFileRecord);
if (!NT_SUCCESS(Status))
{
@@ -2103,22 +2197,9 @@ NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt,
if (AttributeLength != IndexRootContext->pRecord->Resident.ValueLength)
{
- 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
- Status = InternalSetResidentAttributeLength(IndexRootContext,
+ Status = InternalSetResidentAttributeLength(DeviceExt,
+ IndexRootContext,
ParentFileRecord,
IndexRootOffset,
AttributeLength);
@@ -2153,7 +2234,8 @@ NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt,
0,
(PUCHAR)NewIndexRoot,
AttributeLength,
- &LengthWritten);
+ &LengthWritten,
+ ParentFileRecord);
if (!NT_SUCCESS(Status))
{
DPRINT1("ERROR: Unable to write new index root attribute to parent directory!\n");
diff --git a/drivers/filesystems/ntfs/ntfs.h b/drivers/filesystems/ntfs/ntfs.h
index 668815ddd5..4d436a699e 100644
--- a/drivers/filesystems/ntfs/ntfs.h
+++ b/drivers/filesystems/ntfs/ntfs.h
@@ -954,17 +954,25 @@ WriteAttribute(PDEVICE_EXTENSION Vcb,
ULONGLONG Offset,
const PUCHAR Buffer,
ULONG Length,
- PULONG LengthWritten);
+ PULONG LengthWritten,
+ PFILE_RECORD_HEADER FileRecord);
ULONGLONG
AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord);
NTSTATUS
-InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext,
+InternalSetResidentAttributeLength(PDEVICE_EXTENSION DeviceExt,
+ PNTFS_ATTR_CONTEXT AttrContext,
PFILE_RECORD_HEADER FileRecord,
ULONG AttrOffset,
ULONG DataSize);
+PNTFS_ATTR_RECORD
+MoveAttributes(PDEVICE_EXTENSION DeviceExt,
+ PNTFS_ATTR_RECORD FirstAttributeToMove,
+ ULONG FirstAttributeOffset,
+ ULONG_PTR MoveTo);
+
NTSTATUS
SetAttributeDataLength(PFILE_OBJECT FileObject,
PNTFS_FCB Fcb,
diff --git a/drivers/filesystems/ntfs/rw.c b/drivers/filesystems/ntfs/rw.c
index 2c21f3a449..94d05de9ae 100644
--- a/drivers/filesystems/ntfs/rw.c
+++ b/drivers/filesystems/ntfs/rw.c
@@ -478,7 +478,7 @@ NTSTATUS NtfsWriteFile(PDEVICE_EXTENSION DeviceExt,
DPRINT("Length: %lu\tWriteOffset: %lu\tStreamSize: %I64u\n", Length, WriteOffset, StreamSize);
// Write the data to the attribute
- Status = WriteAttribute(DeviceExt, DataContext, WriteOffset, Buffer, Length, LengthWritten);
+ Status = WriteAttribute(DeviceExt, DataContext, WriteOffset, Buffer, Length, LengthWritten, FileRecord);
// Did the write fail?
if (!NT_SUCCESS(Status))
diff --git a/drivers/filesystems/ntfs/volinfo.c b/drivers/filesystems/ntfs/volinfo.c
index d1efa4c269..6b7e598819 100644
--- a/drivers/filesystems/ntfs/volinfo.c
+++ b/drivers/filesystems/ntfs/volinfo.c
@@ -196,7 +196,7 @@ NtfsAllocateClusters(PDEVICE_EXTENSION DeviceExt,
}
- Status = WriteAttribute(DeviceExt, DataContext, 0, BitmapData, (ULONG)BitmapDataSize, &LengthWritten);
+ Status = WriteAttribute(DeviceExt, DataContext, 0, BitmapData, (ULONG)BitmapDataSize, &LengthWritten, BitmapRecord);
ReleaseAttributeContext(DataContext);
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=9cef425464df506c5475a…
commit 9cef425464df506c5475a47e3723f9de507f1341
Author: Trevor Thompson <tmt256(a)email.vccs.edu>
AuthorDate: Thu Jul 27 11:35:50 2017 +0000
[NTFS] - After creating a new file, update creation disposition before calling NtfsCreateFile() recursively. This fixes creating a file via right-clicking in a folder.
svn path=/branches/GSoC_2016/NTFS/; revision=75423
---
drivers/filesystems/ntfs/create.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/filesystems/ntfs/create.c b/drivers/filesystems/ntfs/create.c
index f5ca1027fc..a98afc67b7 100644
--- a/drivers/filesystems/ntfs/create.c
+++ b/drivers/filesystems/ntfs/create.c
@@ -580,8 +580,18 @@ NtfsCreateFile(PDEVICE_OBJECT DeviceObject,
return Status;
}
- // Now we should be able to open the file
- return NtfsCreateFile(DeviceObject, IrpContext);
+ // Before we open the file 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;
+
+ // Now we should be able to open the file using NtfsCreateFile()
+ Status = NtfsCreateFile(DeviceObject, IrpContext);
+ if (NT_SUCCESS(Status))
+ {
+ // We need to change Irp->IoStatus.Information to reflect creation
+ Irp->IoStatus.Information = FILE_CREATED;
+ }
+ return Status;
}
}
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=935fcd1b353a65d3af01e…
commit 935fcd1b353a65d3af01e3f9061d3784f759cdbc
Author: Trevor Thompson <tmt256(a)email.vccs.edu>
AuthorDate: Tue Jul 18 19:59:36 2017 +0000
[NTFS] - Fix some more issues, including remaining issues marked as "unresolved" from CR-123:
-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 | 2 +-
drivers/filesystems/ntfs/btree.c | 30 +++--
drivers/filesystems/ntfs/create.c | 4 +-
drivers/filesystems/ntfs/dirctl.c | 245 ------------------------------------
drivers/filesystems/ntfs/mft.c | 256 +++++++++++++++++++++++++++++++++++++-
drivers/filesystems/ntfs/ntfs.h | 18 +--
6 files changed, 284 insertions(+), 271 deletions(-)
diff --git a/drivers/filesystems/ntfs/attrib.c b/drivers/filesystems/ntfs/attrib.c
index bd070c97ad..a6bc9a8aca 100644
--- a/drivers/filesystems/ntfs/attrib.c
+++ b/drivers/filesystems/ntfs/attrib.c
@@ -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);
diff --git a/drivers/filesystems/ntfs/btree.c b/drivers/filesystems/ntfs/btree.c
index 179107272b..a7eacb3173 100644
--- a/drivers/filesystems/ntfs/btree.c
+++ b/drivers/filesystems/ntfs/btree.c
@@ -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;
diff --git a/drivers/filesystems/ntfs/create.c b/drivers/filesystems/ntfs/create.c
index 30cc921095..f5ca1027fc 100644
--- a/drivers/filesystems/ntfs/create.c
+++ b/drivers/filesystems/ntfs/create.c
@@ -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);
}
diff --git a/drivers/filesystems/ntfs/dirctl.c b/drivers/filesystems/ntfs/dirctl.c
index c41688ea2b..98294b4791 100644
--- a/drivers/filesystems/ntfs/dirctl.c
+++ b/drivers/filesystems/ntfs/dirctl.c
@@ -34,251 +34,6 @@
/* 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,
diff --git a/drivers/filesystems/ntfs/mft.c b/drivers/filesystems/ntfs/mft.c
index ed531df173..72953c0086 100644
--- a/drivers/filesystems/ntfs/mft.c
+++ b/drivers/filesystems/ntfs/mft.c
@@ -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))
diff --git a/drivers/filesystems/ntfs/ntfs.h b/drivers/filesystems/ntfs/ntfs.h
index ddb876daf9..9cbc4d5485 100644
--- a/drivers/filesystems/ntfs/ntfs.h
+++ b/drivers/filesystems/ntfs/ntfs.h
@@ -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,