mirror of
https://github.com/reactos/reactos.git
synced 2025-08-05 16:32:59 +00:00
[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. svn path=/branches/GSoC_2016/NTFS/; revision=75692
This commit is contained in:
parent
c63e7e54b5
commit
b033f00f58
5 changed files with 542 additions and 113 deletions
|
@ -167,7 +167,11 @@ AddFileName(PFILE_RECORD_HEADER FileRecord,
|
||||||
FileNameAttribute->LastWriteTime = SystemTime.QuadPart;
|
FileNameAttribute->LastWriteTime = SystemTime.QuadPart;
|
||||||
FileNameAttribute->LastAccessTime = 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
|
// we need to extract the filename from the path
|
||||||
DPRINT1("Pathname: %wZ\n", &FileObject->FileName);
|
DPRINT1("Pathname: %wZ\n", &FileObject->FileName);
|
||||||
|
@ -254,6 +258,112 @@ AddFileName(PFILE_RECORD_HEADER FileRecord,
|
||||||
return Status;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name AddRun
|
* @name AddRun
|
||||||
* @implemented
|
* @implemented
|
||||||
|
|
|
@ -111,6 +111,7 @@ PrintAllVCNs(PDEVICE_EXTENSION Vcb,
|
||||||
* @remarks
|
* @remarks
|
||||||
* AllocateIndexNode() doesn't write any data to the index record it creates. Called by UpdateIndexNode().
|
* 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.
|
* 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
|
NTSTATUS
|
||||||
AllocateIndexNode(PDEVICE_EXTENSION DeviceExt,
|
AllocateIndexNode(PDEVICE_EXTENSION DeviceExt,
|
||||||
|
@ -167,11 +168,30 @@ AllocateIndexNode(PDEVICE_EXTENSION DeviceExt,
|
||||||
|
|
||||||
// See how many bytes we need to store the amount of bits we'll have
|
// See how many bytes we need to store the amount of bits we'll have
|
||||||
BytesNeeded = NextNodeNumber / 8;
|
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
|
// 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?
|
// Do we need to enlarge the bitmap?
|
||||||
if (BytesNeeded > BitmapLength)
|
if (BytesNeeded > BitmapLength)
|
||||||
|
@ -179,11 +199,22 @@ AllocateIndexNode(PDEVICE_EXTENSION DeviceExt,
|
||||||
// TODO: handle synchronization issues that could occur from changing the directory's file record
|
// TODO: handle synchronization issues that could occur from changing the directory's file record
|
||||||
// Change bitmap size
|
// Change bitmap size
|
||||||
DataSize.QuadPart = BytesNeeded;
|
DataSize.QuadPart = BytesNeeded;
|
||||||
Status = SetResidentAttributeDataLength(DeviceExt,
|
if (BitmapCtx->pRecord->IsNonResident)
|
||||||
BitmapCtx,
|
{
|
||||||
BitmapOffset,
|
Status = SetNonResidentAttributeDataLength(DeviceExt,
|
||||||
FileRecord,
|
BitmapCtx,
|
||||||
&DataSize);
|
BitmapOffset,
|
||||||
|
FileRecord,
|
||||||
|
&DataSize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Status = SetResidentAttributeDataLength(DeviceExt,
|
||||||
|
BitmapCtx,
|
||||||
|
BitmapOffset,
|
||||||
|
FileRecord,
|
||||||
|
&DataSize);
|
||||||
|
}
|
||||||
if (!NT_SUCCESS(Status))
|
if (!NT_SUCCESS(Status))
|
||||||
{
|
{
|
||||||
DPRINT1("ERROR: Failed to set length of bitmap attribute!\n");
|
DPRINT1("ERROR: Failed to set length of bitmap attribute!\n");
|
||||||
|
@ -215,18 +246,6 @@ AllocateIndexNode(PDEVICE_EXTENSION DeviceExt,
|
||||||
return Status;
|
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
|
// Set the bit for the new index record
|
||||||
RtlSetBits(&Bitmap, NextNodeNumber, 1);
|
RtlSetBits(&Bitmap, NextNodeNumber, 1);
|
||||||
|
|
||||||
|
@ -246,6 +265,8 @@ AllocateIndexNode(PDEVICE_EXTENSION DeviceExt,
|
||||||
// Calculate VCN of new node number
|
// Calculate VCN of new node number
|
||||||
*NewVCN = NextNodeNumber * (IndexBufferSize / DeviceExt->NtfsInfo.BytesPerCluster);
|
*NewVCN = NextNodeNumber * (IndexBufferSize / DeviceExt->NtfsInfo.BytesPerCluster);
|
||||||
|
|
||||||
|
DPRINT("New VCN: %I64u\n", *NewVCN);
|
||||||
|
|
||||||
ExFreePoolWithTag(BitmapMem, TAG_NTFS);
|
ExFreePoolWithTag(BitmapMem, TAG_NTFS);
|
||||||
ReleaseAttributeContext(BitmapCtx);
|
ReleaseAttributeContext(BitmapCtx);
|
||||||
|
|
||||||
|
@ -311,6 +332,63 @@ CreateDummyKey(BOOLEAN HasChildNode)
|
||||||
return NewDummyKey;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name CompareTreeKeys
|
* @name CompareTreeKeys
|
||||||
* @implemented
|
* @implemented
|
||||||
|
@ -402,7 +480,7 @@ CreateBTreeNodeFromIndexNode(PDEVICE_EXTENSION Vcb,
|
||||||
ULONG CurrentEntryOffset = 0;
|
ULONG CurrentEntryOffset = 0;
|
||||||
PINDEX_BUFFER NodeBuffer;
|
PINDEX_BUFFER NodeBuffer;
|
||||||
ULONG IndexBufferSize = Vcb->NtfsInfo.BytesPerIndexRecord;
|
ULONG IndexBufferSize = Vcb->NtfsInfo.BytesPerIndexRecord;
|
||||||
PULONGLONG NodeNumber;
|
PULONGLONG VCN;
|
||||||
PB_TREE_KEY CurrentKey;
|
PB_TREE_KEY CurrentKey;
|
||||||
NTSTATUS Status;
|
NTSTATUS Status;
|
||||||
ULONGLONG IndexNodeOffset;
|
ULONGLONG IndexNodeOffset;
|
||||||
|
@ -415,10 +493,9 @@ CreateBTreeNodeFromIndexNode(PDEVICE_EXTENSION Vcb,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the node number from the end of the node entry
|
// 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
|
// 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);
|
NewNode = ExAllocatePoolWithTag(NonPagedPool, sizeof(B_TREE_FILENAME_NODE), TAG_NTFS);
|
||||||
if (!NewNode)
|
if (!NewNode)
|
||||||
{
|
{
|
||||||
|
@ -449,7 +526,7 @@ CreateBTreeNodeFromIndexNode(PDEVICE_EXTENSION Vcb,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate offset into index allocation
|
// 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
|
// TODO: Confirm index bitmap has this node marked as in-use
|
||||||
|
|
||||||
|
@ -462,7 +539,7 @@ CreateBTreeNodeFromIndexNode(PDEVICE_EXTENSION Vcb,
|
||||||
|
|
||||||
ASSERT(BytesRead == IndexBufferSize);
|
ASSERT(BytesRead == IndexBufferSize);
|
||||||
NT_ASSERT(NodeBuffer->Ntfs.Type == NRH_INDX_TYPE);
|
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
|
// Apply the fixup array to the node buffer
|
||||||
Status = FixupUpdateSequenceArray(Vcb, &NodeBuffer->Ntfs);
|
Status = FixupUpdateSequenceArray(Vcb, &NodeBuffer->Ntfs);
|
||||||
|
@ -516,8 +593,6 @@ CreateBTreeNodeFromIndexNode(PDEVICE_EXTENSION Vcb,
|
||||||
// See if the current key has a sub-node
|
// See if the current key has a sub-node
|
||||||
if (CurrentKey->IndexEntry->Flags & NTFS_INDEX_ENTRY_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,
|
CurrentKey->LesserChild = CreateBTreeNodeFromIndexNode(Vcb,
|
||||||
IndexRoot,
|
IndexRoot,
|
||||||
IndexAllocationAttributeCtx,
|
IndexAllocationAttributeCtx,
|
||||||
|
@ -535,8 +610,6 @@ CreateBTreeNodeFromIndexNode(PDEVICE_EXTENSION Vcb,
|
||||||
// See if the current key has a sub-node
|
// See if the current key has a sub-node
|
||||||
if (CurrentKey->IndexEntry->Flags & NTFS_INDEX_ENTRY_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,
|
CurrentKey->LesserChild = CreateBTreeNodeFromIndexNode(Vcb,
|
||||||
IndexRoot,
|
IndexRoot,
|
||||||
IndexAllocationAttributeCtx,
|
IndexAllocationAttributeCtx,
|
||||||
|
@ -551,8 +624,8 @@ CreateBTreeNodeFromIndexNode(PDEVICE_EXTENSION Vcb,
|
||||||
CurrentNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)CurrentNodeEntry + CurrentNodeEntry->Length);
|
CurrentNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)CurrentNodeEntry + CurrentNodeEntry->Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
NewNode->NodeNumber = *NodeNumber;
|
NewNode->VCN = *VCN;
|
||||||
NewNode->ExistsOnDisk = TRUE;
|
NewNode->HasValidVCN = TRUE;
|
||||||
|
|
||||||
ExFreePoolWithTag(NodeBuffer, TAG_NTFS);
|
ExFreePoolWithTag(NodeBuffer, TAG_NTFS);
|
||||||
|
|
||||||
|
@ -622,8 +695,6 @@ CreateBTreeFromIndex(PDEVICE_EXTENSION Vcb,
|
||||||
NULL);
|
NULL);
|
||||||
if (!NT_SUCCESS(Status))
|
if (!NT_SUCCESS(Status))
|
||||||
IndexAllocationContext = NULL;
|
IndexAllocationContext = NULL;
|
||||||
else
|
|
||||||
PrintAllVCNs(Vcb, IndexAllocationContext, IndexRoot->SizeOfEntry);
|
|
||||||
|
|
||||||
// Setup the Tree
|
// Setup the Tree
|
||||||
RootNode->FirstKey = CurrentKey;
|
RootNode->FirstKey = CurrentKey;
|
||||||
|
@ -871,7 +942,7 @@ CreateIndexRootFromBTree(PDEVICE_EXTENSION DeviceExt,
|
||||||
NewIndexRoot->Header.Flags = INDEX_ROOT_LARGE;
|
NewIndexRoot->Header.Flags = INDEX_ROOT_LARGE;
|
||||||
|
|
||||||
// Add Length of Current Entry to Total Size of Entries
|
// 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
|
// Go to the next node entry
|
||||||
CurrentNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)CurrentNodeEntry + CurrentNodeEntry->Length);
|
CurrentNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)CurrentNodeEntry + CurrentNodeEntry->Length);
|
||||||
|
@ -905,10 +976,12 @@ CreateIndexBufferFromBTreeNode(PDEVICE_EXTENSION DeviceExt,
|
||||||
IndexBuffer->Ntfs.UsaCount = 9;
|
IndexBuffer->Ntfs.UsaCount = 9;
|
||||||
|
|
||||||
// TODO: Check bitmap for VCN
|
// TODO: Check bitmap for VCN
|
||||||
ASSERT(Node->ExistsOnDisk);
|
ASSERT(Node->HasValidVCN);
|
||||||
IndexBuffer->VCN = Node->NodeNumber;
|
IndexBuffer->VCN = Node->VCN;
|
||||||
|
|
||||||
IndexBuffer->Header.FirstEntryOffset = 0x28;
|
// 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);
|
IndexBuffer->Header.AllocatedSize = BufferSize - FIELD_OFFSET(INDEX_BUFFER, Header);
|
||||||
|
|
||||||
// Start summing the total size of this node's entries
|
// Start summing the total size of this node's entries
|
||||||
|
@ -934,9 +1007,9 @@ CreateIndexBufferFromBTreeNode(PDEVICE_EXTENSION DeviceExt,
|
||||||
// Copy the index entry
|
// Copy the index entry
|
||||||
RtlCopyMemory(CurrentNodeEntry, CurrentKey->IndexEntry, CurrentKey->IndexEntry->Length);
|
RtlCopyMemory(CurrentNodeEntry, CurrentKey->IndexEntry, CurrentKey->IndexEntry->Length);
|
||||||
|
|
||||||
DPRINT1("Index Node Entry Stream Length: %u\nIndex Node Entry Length: %u\n",
|
DPRINT("Index Node Entry Stream Length: %u\nIndex Node Entry Length: %u\n",
|
||||||
CurrentNodeEntry->KeyLength,
|
CurrentNodeEntry->KeyLength,
|
||||||
CurrentNodeEntry->Length);
|
CurrentNodeEntry->Length);
|
||||||
|
|
||||||
// Add Length of Current Entry to Total Size of Entries
|
// Add Length of Current Entry to Total Size of Entries
|
||||||
IndexBuffer->Header.TotalSizeOfEntries += CurrentNodeEntry->Length;
|
IndexBuffer->Header.TotalSizeOfEntries += CurrentNodeEntry->Length;
|
||||||
|
@ -1020,14 +1093,6 @@ UpdateIndexAllocation(PDEVICE_EXTENSION DeviceExt,
|
||||||
DPRINT1("FIXME: Need to add index allocation\n");
|
DPRINT1("FIXME: Need to add index allocation\n");
|
||||||
return STATUS_NOT_IMPLEMENTED;
|
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?
|
// Is the Index Entry large enough to store the VCN?
|
||||||
if (!CurrentKey->IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
|
if (!CurrentKey->IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
|
||||||
|
@ -1056,9 +1121,17 @@ UpdateIndexAllocation(PDEVICE_EXTENSION DeviceExt,
|
||||||
CurrentKey->IndexEntry->Flags |= NTFS_INDEX_ENTRY_NODE;
|
CurrentKey->IndexEntry->Flags |= NTFS_INDEX_ENTRY_NODE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the VCN stored in the index entry of CurrentKey
|
// Update the sub-node
|
||||||
SetIndexEntryVCN(CurrentKey->IndexEntry, CurrentKey->LesserChild->NodeNumber);
|
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->VCN);
|
||||||
}
|
}
|
||||||
CurrentKey = CurrentKey->NextKey;
|
CurrentKey = CurrentKey->NextKey;
|
||||||
}
|
}
|
||||||
|
@ -1096,7 +1169,7 @@ UpdateIndexNode(PDEVICE_EXTENSION DeviceExt,
|
||||||
IndexAllocationContext,
|
IndexAllocationContext,
|
||||||
IndexAllocationOffset,
|
IndexAllocationOffset,
|
||||||
BitmapContext,
|
BitmapContext,
|
||||||
Node->NodeNumber);
|
Node->VCN);
|
||||||
|
|
||||||
// Do we need to write this node to disk?
|
// Do we need to write this node to disk?
|
||||||
if (Node->DiskNeedsUpdating)
|
if (Node->DiskNeedsUpdating)
|
||||||
|
@ -1106,7 +1179,7 @@ UpdateIndexNode(PDEVICE_EXTENSION DeviceExt,
|
||||||
PINDEX_BUFFER IndexBuffer;
|
PINDEX_BUFFER IndexBuffer;
|
||||||
|
|
||||||
// Does the node need to be assigned a VCN?
|
// Does the node need to be assigned a VCN?
|
||||||
if (!Node->ExistsOnDisk)
|
if (!Node->HasValidVCN)
|
||||||
{
|
{
|
||||||
// Allocate the node
|
// Allocate the node
|
||||||
Status = AllocateIndexNode(DeviceExt,
|
Status = AllocateIndexNode(DeviceExt,
|
||||||
|
@ -1114,14 +1187,14 @@ UpdateIndexNode(PDEVICE_EXTENSION DeviceExt,
|
||||||
IndexBufferSize,
|
IndexBufferSize,
|
||||||
IndexAllocationContext,
|
IndexAllocationContext,
|
||||||
IndexAllocationOffset,
|
IndexAllocationOffset,
|
||||||
&Node->NodeNumber);
|
&Node->VCN);
|
||||||
if (!NT_SUCCESS(Status))
|
if (!NT_SUCCESS(Status))
|
||||||
{
|
{
|
||||||
DPRINT1("ERROR: Failed to allocate index record in index allocation!\n");
|
DPRINT1("ERROR: Failed to allocate index record in index allocation!\n");
|
||||||
return Status;
|
return Status;
|
||||||
}
|
}
|
||||||
|
|
||||||
Node->ExistsOnDisk = TRUE;
|
Node->HasValidVCN = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate memory for an index buffer
|
// Allocate memory for an index buffer
|
||||||
|
@ -1142,7 +1215,7 @@ UpdateIndexNode(PDEVICE_EXTENSION DeviceExt,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Offset of index buffer in index allocation
|
// 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
|
// Write the buffer to the index allocation
|
||||||
Status = WriteAttribute(DeviceExt, IndexAllocationContext, NodeOffset, (const PUCHAR)IndexBuffer, IndexBufferSize, &LengthWritten, FileRecord);
|
Status = WriteAttribute(DeviceExt, IndexAllocationContext, NodeOffset, (const PUCHAR)IndexBuffer, IndexBufferSize, &LengthWritten, FileRecord);
|
||||||
|
@ -1274,7 +1347,7 @@ DestroyBTree(PB_TREE Tree)
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
DumpBTreeKey(PB_TREE_KEY Key, ULONG Number, ULONG Depth)
|
DumpBTreeKey(PB_TREE Tree, PB_TREE_KEY Key, ULONG Number, ULONG Depth)
|
||||||
{
|
{
|
||||||
ULONG i;
|
ULONG i;
|
||||||
for (i = 0; i < Depth; i++)
|
for (i = 0; i < Depth; i++)
|
||||||
|
@ -1298,7 +1371,7 @@ DumpBTreeKey(PB_TREE_KEY Key, ULONG Number, ULONG Depth)
|
||||||
if (Key->IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
|
if (Key->IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
|
||||||
{
|
{
|
||||||
if (Key->LesserChild)
|
if (Key->LesserChild)
|
||||||
DumpBTreeNode(Key->LesserChild, Number, Depth + 1);
|
DumpBTreeNode(Tree, Key->LesserChild, Number, Depth + 1);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// This will be an assert once nodes with arbitrary depth are debugged
|
// This will be an assert once nodes with arbitrary depth are debugged
|
||||||
|
@ -1308,18 +1381,28 @@ DumpBTreeKey(PB_TREE_KEY Key, ULONG Number, ULONG Depth)
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID
|
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;
|
PB_TREE_KEY CurrentKey;
|
||||||
ULONG i;
|
ULONG i;
|
||||||
for (i = 0; i < Depth; i++)
|
for (i = 0; i < Depth; i++)
|
||||||
DbgPrint(" ");
|
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;
|
CurrentKey = Node->FirstKey;
|
||||||
for (i = 1; i <= Node->KeyCount; i++)
|
for (i = 0; i < Node->KeyCount; i++)
|
||||||
{
|
{
|
||||||
DumpBTreeKey(CurrentKey, i, Depth);
|
DumpBTreeKey(Tree, CurrentKey, i, Depth);
|
||||||
CurrentKey = CurrentKey->NextKey;
|
CurrentKey = CurrentKey->NextKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1340,7 +1423,7 @@ VOID
|
||||||
DumpBTree(PB_TREE Tree)
|
DumpBTree(PB_TREE Tree)
|
||||||
{
|
{
|
||||||
DbgPrint("B_TREE @ %p\n", 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
|
// Calculates start of Index Buffer relative to the index allocation, given the node's VCN
|
||||||
|
@ -1525,7 +1608,6 @@ NtfsInsertKey(PB_TREE Tree,
|
||||||
NewIndexRoot->FirstKey = DummyKey;
|
NewIndexRoot->FirstKey = DummyKey;
|
||||||
NewIndexRoot->KeyCount = 1;
|
NewIndexRoot->KeyCount = 1;
|
||||||
NewIndexRoot->DiskNeedsUpdating = TRUE;
|
NewIndexRoot->DiskNeedsUpdating = TRUE;
|
||||||
NewIndexRoot->ExistsOnDisk = TRUE;
|
|
||||||
|
|
||||||
// Make the new node the Tree's root node
|
// Make the new node the Tree's root node
|
||||||
Tree->RootNode = NewIndexRoot;
|
Tree->RootNode = NewIndexRoot;
|
||||||
|
|
|
@ -569,25 +569,31 @@ NtfsCreateFile(PDEVICE_OBJECT DeviceObject,
|
||||||
return STATUS_ACCESS_DENIED;
|
return STATUS_ACCESS_DENIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can't create directories yet
|
// Was the user trying to create a directory?
|
||||||
if (RequestedOptions & FILE_DIRECTORY_FILE)
|
if (RequestedOptions & FILE_DIRECTORY_FILE)
|
||||||
{
|
{
|
||||||
DPRINT1("FIXME: Folder creation is still TODO!\n");
|
// Create the directory on disk
|
||||||
return STATUS_NOT_IMPLEMENTED;
|
Status = NtfsCreateDirectory(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));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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))
|
if (!NT_SUCCESS(Status))
|
||||||
{
|
{
|
||||||
DPRINT1("ERROR: Couldn't create file record!\n");
|
DPRINT1("ERROR: Couldn't create file record!\n");
|
||||||
return Status;
|
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
|
// from create to open, since we already created the file
|
||||||
Stack->Parameters.Create.Options = (ULONG)FILE_OPEN << 24 | RequestedOptions;
|
Stack->Parameters.Create.Options = (ULONG)FILE_OPEN << 24 | RequestedOptions;
|
||||||
|
|
||||||
|
@ -650,6 +656,215 @@ NtfsCreate(PNTFS_IRP_CONTEXT IrpContext)
|
||||||
return Status;
|
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()
|
* @name NtfsCreateFileRecord()
|
||||||
* @implemented
|
* @implemented
|
||||||
|
@ -693,43 +908,19 @@ NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt,
|
||||||
CanWait ? "TRUE" : "FALSE");
|
CanWait ? "TRUE" : "FALSE");
|
||||||
|
|
||||||
// allocate memory for file record
|
// allocate memory for file record
|
||||||
FileRecord = ExAllocatePoolWithTag(NonPagedPool,
|
FileRecord = NtfsCreateEmptyFileRecord(DeviceExt);
|
||||||
DeviceExt->NtfsInfo.BytesPerFileRecord,
|
|
||||||
TAG_NTFS);
|
|
||||||
if (!FileRecord)
|
if (!FileRecord)
|
||||||
{
|
{
|
||||||
DPRINT1("ERROR: Unable to allocate memory for file record!\n");
|
DPRINT1("ERROR: Unable to allocate memory for file record!\n");
|
||||||
return STATUS_INSUFFICIENT_RESOURCES;
|
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
|
// find where the first attribute will be added
|
||||||
NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
|
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
|
// add first attribute, $STANDARD_INFORMATION
|
||||||
AddStandardInformation(FileRecord, NextAttribute);
|
AddStandardInformation(FileRecord, NextAttribute);
|
||||||
|
|
||||||
// advance NextAttribute pointer to the next attribute
|
// advance NextAttribute pointer to the next attribute
|
||||||
NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length);
|
NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length);
|
||||||
|
|
||||||
|
@ -745,8 +936,10 @@ NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt,
|
||||||
// add the $DATA attribute
|
// add the $DATA attribute
|
||||||
AddData(FileRecord, NextAttribute);
|
AddData(FileRecord, NextAttribute);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
// dump file record in memory (for debugging)
|
// dump file record in memory (for debugging)
|
||||||
NtfsDumpFileRecord(DeviceExt, FileRecord);
|
NtfsDumpFileRecord(DeviceExt, FileRecord);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Now that we've built the file record in memory, we need to store it in the MFT.
|
// Now that we've built the file record in memory, we need to store it in the MFT.
|
||||||
Status = AddNewMftEntry(FileRecord, DeviceExt, &FileMftIndex, CanWait);
|
Status = AddNewMftEntry(FileRecord, DeviceExt, &FileMftIndex, CanWait);
|
||||||
|
|
|
@ -1893,6 +1893,7 @@ AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
|
||||||
ULONGLONG BitmapDataSize;
|
ULONGLONG BitmapDataSize;
|
||||||
ULONGLONG AttrBytesRead;
|
ULONGLONG AttrBytesRead;
|
||||||
PUCHAR BitmapData;
|
PUCHAR BitmapData;
|
||||||
|
PUCHAR BitmapBuffer;
|
||||||
ULONG LengthWritten;
|
ULONG LengthWritten;
|
||||||
PNTFS_ATTR_CONTEXT BitmapContext;
|
PNTFS_ATTR_CONTEXT BitmapContext;
|
||||||
LARGE_INTEGER BitmapBits;
|
LARGE_INTEGER BitmapBits;
|
||||||
|
@ -1901,6 +1902,8 @@ AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
|
||||||
DPRINT1("AddNewMftEntry(%p, %p, %p, %s)\n", FileRecord, DeviceExt, DestinationIndex, CanWait ? "TRUE" : "FALSE");
|
DPRINT1("AddNewMftEntry(%p, %p, %p, %s)\n", FileRecord, DeviceExt, DestinationIndex, CanWait ? "TRUE" : "FALSE");
|
||||||
|
|
||||||
// First, we have to read the mft's $Bitmap attribute
|
// First, we have to read the mft's $Bitmap attribute
|
||||||
|
|
||||||
|
// Find the attribute
|
||||||
Status = FindAttribute(DeviceExt, DeviceExt->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, NULL);
|
Status = FindAttribute(DeviceExt, DeviceExt->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, NULL);
|
||||||
if (!NT_SUCCESS(Status))
|
if (!NT_SUCCESS(Status))
|
||||||
{
|
{
|
||||||
|
@ -1908,22 +1911,28 @@ AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
|
||||||
return Status;
|
return Status;
|
||||||
}
|
}
|
||||||
|
|
||||||
// allocate a buffer for the $Bitmap attribute
|
// Get size of bitmap
|
||||||
BitmapDataSize = AttributeDataLength(BitmapContext->pRecord);
|
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);
|
ReleaseAttributeContext(BitmapContext);
|
||||||
return STATUS_INSUFFICIENT_RESOURCES;
|
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
|
// read $Bitmap attribute
|
||||||
AttrBytesRead = ReadAttribute(DeviceExt, BitmapContext, 0, (PCHAR)BitmapData, BitmapDataSize);
|
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");
|
DPRINT1("ERROR: Unable to read $Bitmap attribute of master file table!\n");
|
||||||
ExFreePoolWithTag(BitmapData, TAG_NTFS);
|
ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
|
||||||
ReleaseAttributeContext(BitmapContext);
|
ReleaseAttributeContext(BitmapContext);
|
||||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
@ -1939,7 +1948,8 @@ AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
|
||||||
if (BitmapBits.HighPart != 0)
|
if (BitmapBits.HighPart != 0)
|
||||||
{
|
{
|
||||||
DPRINT1("\tFIXME: bitmap sizes beyond 32bits are not yet supported! (Your NTFS volume is too large)\n");
|
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);
|
ReleaseAttributeContext(BitmapContext);
|
||||||
return STATUS_NOT_IMPLEMENTED;
|
return STATUS_NOT_IMPLEMENTED;
|
||||||
}
|
}
|
||||||
|
@ -1953,7 +1963,7 @@ AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
|
||||||
{
|
{
|
||||||
DPRINT1("Couldn't find free space in MFT for file record, increasing MFT size.\n");
|
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);
|
ReleaseAttributeContext(BitmapContext);
|
||||||
|
|
||||||
// Couldn't find a free record in the MFT, add some blank records and try again
|
// Couldn't find a free record in the MFT, add some blank records and try again
|
||||||
|
@ -1982,7 +1992,7 @@ AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
|
||||||
if (!NT_SUCCESS(Status))
|
if (!NT_SUCCESS(Status))
|
||||||
{
|
{
|
||||||
DPRINT1("ERROR encountered when writing $Bitmap attribute!\n");
|
DPRINT1("ERROR encountered when writing $Bitmap attribute!\n");
|
||||||
ExFreePoolWithTag(BitmapData, TAG_NTFS);
|
ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
|
||||||
ReleaseAttributeContext(BitmapContext);
|
ReleaseAttributeContext(BitmapContext);
|
||||||
return Status;
|
return Status;
|
||||||
}
|
}
|
||||||
|
@ -1993,14 +2003,14 @@ AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
|
||||||
if (!NT_SUCCESS(Status))
|
if (!NT_SUCCESS(Status))
|
||||||
{
|
{
|
||||||
DPRINT1("ERROR: Unable to write file record!\n");
|
DPRINT1("ERROR: Unable to write file record!\n");
|
||||||
ExFreePoolWithTag(BitmapData, TAG_NTFS);
|
ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
|
||||||
ReleaseAttributeContext(BitmapContext);
|
ReleaseAttributeContext(BitmapContext);
|
||||||
return Status;
|
return Status;
|
||||||
}
|
}
|
||||||
|
|
||||||
*DestinationIndex = MftIndex;
|
*DestinationIndex = MftIndex;
|
||||||
|
|
||||||
ExFreePoolWithTag(BitmapData, TAG_NTFS);
|
ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
|
||||||
ReleaseAttributeContext(BitmapContext);
|
ReleaseAttributeContext(BitmapContext);
|
||||||
|
|
||||||
return Status;
|
return Status;
|
||||||
|
@ -2255,7 +2265,7 @@ NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt,
|
||||||
AttributeLength,
|
AttributeLength,
|
||||||
&LengthWritten,
|
&LengthWritten,
|
||||||
ParentFileRecord);
|
ParentFileRecord);
|
||||||
if (!NT_SUCCESS(Status))
|
if (!NT_SUCCESS(Status) || LengthWritten != AttributeLength)
|
||||||
{
|
{
|
||||||
DPRINT1("ERROR: Unable to write new index root attribute to parent directory!\n");
|
DPRINT1("ERROR: Unable to write new index root attribute to parent directory!\n");
|
||||||
ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
|
ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
|
||||||
|
|
|
@ -304,6 +304,12 @@ typedef struct
|
||||||
// relative to the beginning of the file record.
|
// relative to the beginning of the file record.
|
||||||
#define ATTR_RECORD_ALIGNMENT 8
|
#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
|
typedef struct
|
||||||
{
|
{
|
||||||
ULONGLONG CreationTime;
|
ULONGLONG CreationTime;
|
||||||
|
@ -423,9 +429,9 @@ typedef struct _B_TREE_KEY
|
||||||
typedef struct _B_TREE_FILENAME_NODE
|
typedef struct _B_TREE_FILENAME_NODE
|
||||||
{
|
{
|
||||||
ULONG KeyCount;
|
ULONG KeyCount;
|
||||||
BOOLEAN ExistsOnDisk;
|
BOOLEAN HasValidVCN;
|
||||||
BOOLEAN DiskNeedsUpdating;
|
BOOLEAN DiskNeedsUpdating;
|
||||||
ULONGLONG NodeNumber;
|
ULONGLONG VCN;
|
||||||
PB_TREE_KEY FirstKey;
|
PB_TREE_KEY FirstKey;
|
||||||
} B_TREE_FILENAME_NODE, *PB_TREE_FILENAME_NODE;
|
} B_TREE_FILENAME_NODE, *PB_TREE_FILENAME_NODE;
|
||||||
|
|
||||||
|
@ -570,6 +576,15 @@ AddRun(PNTFS_VCB Vcb,
|
||||||
ULONGLONG NextAssignedCluster,
|
ULONGLONG NextAssignedCluster,
|
||||||
ULONG RunLength);
|
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
|
NTSTATUS
|
||||||
AddFileName(PFILE_RECORD_HEADER FileRecord,
|
AddFileName(PFILE_RECORD_HEADER FileRecord,
|
||||||
PNTFS_ATTR_RECORD AttributeAddress,
|
PNTFS_ATTR_RECORD AttributeAddress,
|
||||||
|
@ -718,10 +733,20 @@ VOID
|
||||||
DumpBTree(PB_TREE Tree);
|
DumpBTree(PB_TREE Tree);
|
||||||
|
|
||||||
VOID
|
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 Number,
|
||||||
ULONG Depth);
|
ULONG Depth);
|
||||||
|
|
||||||
|
NTSTATUS
|
||||||
|
CreateEmptyBTree(PB_TREE *NewTree);
|
||||||
|
|
||||||
ULONGLONG
|
ULONGLONG
|
||||||
GetAllocationOffsetFromVCN(PDEVICE_EXTENSION DeviceExt,
|
GetAllocationOffsetFromVCN(PDEVICE_EXTENSION DeviceExt,
|
||||||
ULONG IndexBufferSize,
|
ULONG IndexBufferSize,
|
||||||
|
@ -771,6 +796,15 @@ NtfsClose(PNTFS_IRP_CONTEXT IrpContext);
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
NtfsCreate(PNTFS_IRP_CONTEXT IrpContext);
|
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
|
NTSTATUS
|
||||||
NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt,
|
NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt,
|
||||||
PFILE_OBJECT FileObject,
|
PFILE_OBJECT FileObject,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue