[NTFS] - Add some helper functions for new features. Add some fixes. Add support for creating an index allocation, splitting a b-tree node, or "demoting" the index root. This allows for file creation without functional limitations.

+AddBitmap() - adds a $BITMAP attribute to a file record.
+AddIndexAllocation() - adds an $INDEX_ALLOCATION attribute to a file record.
+CountBTreeKeys() - Counts the number of linked B-Tree keys.
CreateIndexBufferFromBTreeNode() - Set INDEX_NODE_LARGE if the node has sub-nodes.
CreateIndexRootFromBTree() - Simplify the usage and math of MaxIndexSize; make it only account for the cumulative size of the index entries.
+DemoteBTreeRoot() - Replaces the contents of an index root with a dummy key, and puts those contents in a new node, which is made a child of the dummy key. This is done when an index root grows too large.
+GetIndexEntryVCN() - Retrieves the VCN from an index entry.
NtfsAddFilenameToDirectory() - Fix math for MaxIndexRootSize.
NtfsInsertKey() - Add support for splitting a B-Tree node. Don't check size of index root (that will be handled later).
+SplitBTreeNode() - Called when a B-Tree node grows too large.
UpdateIndexAllocation() - Create an $I30 index allocation attribute and bitmap attribute if needed.
UpdateIndexNode() - Update children before updating the current node. Store VCN of child nodes in the index entries of their respective keys.

svn path=/branches/GSoC_2016/NTFS/; revision=75707
This commit is contained in:
Trevor Thompson 2017-08-29 15:51:14 +00:00 committed by Thomas Faber
parent 5e7c11842a
commit 52c30fdf37
4 changed files with 895 additions and 134 deletions

View file

@ -36,6 +36,100 @@
/* FUNCTIONS ****************************************************************/
/**
* @name AddBitmap
* @implemented
*
* Adds a $BITMAP 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.
*
* @param AttributeAddress
* Pointer to the region of memory that will receive the $INDEX_ALLOCATION attribute.
* This address must reside within FileRecord. Must be aligned to an 8-byte boundary (relative to FileRecord).
*
* @param Name
* Pointer to a string of 16-bit Unicode characters naming the attribute. Most often 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, or if the file record isn't large enough for the attribute.
*
* @remarks
* Only adding the attribute to the end of the file record is supported; AttributeAddress must
* be of type AttributeEnd.
* This could be improved by adding an $ATTRIBUTE_LIST to the file record if there's not enough space.
*
*/
NTSTATUS
AddBitmap(PNTFS_VCB Vcb,
PFILE_RECORD_HEADER FileRecord,
PNTFS_ATTR_RECORD AttributeAddress,
PCWSTR Name,
USHORT NameLength)
{
ULONG AttributeLength;
// Calculate the header length
ULONG ResidentHeaderLength = FIELD_OFFSET(NTFS_ATTR_RECORD, Resident.Reserved) + sizeof(UCHAR);
ULONG FileRecordEnd = AttributeAddress->Length;
ULONG NameOffset;
ULONG ValueOffset;
// We'll start out with 8 bytes of bitmap data
ULONG ValueLength = 8;
ULONG BytesAvailable;
if (AttributeAddress->Type != AttributeEnd)
{
DPRINT1("FIXME: Can only add $BITMAP 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 + ValueLength;
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 = AttributeBitmap;
AttributeAddress->Length = AttributeLength;
AttributeAddress->NameLength = NameLength;
AttributeAddress->NameOffset = NameOffset;
AttributeAddress->Instance = FileRecord->NextAttributeNumber++;
AttributeAddress->Resident.ValueLength = ValueLength;
AttributeAddress->Resident.ValueOffset = ValueOffset;
// Set the name
RtlCopyMemory((PCHAR)((ULONG_PTR)AttributeAddress + NameOffset), Name, NameLength * sizeof(WCHAR));
// 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 AddData
* @implemented
@ -258,6 +352,105 @@ AddFileName(PFILE_RECORD_HEADER FileRecord,
return Status;
}
/**
* @name AddIndexAllocation
* @implemented
*
* Adds an $INDEX_ALLOCATION 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.
*
* @param AttributeAddress
* Pointer to the region of memory that will receive the $INDEX_ALLOCATION attribute.
* This address must reside within FileRecord. Must be aligned to an 8-byte boundary (relative to FileRecord).
*
* @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, or if the file record isn't large enough for the attribute.
*
* @remarks
* Only adding the attribute to the end of the file record is supported; AttributeAddress must
* be of type AttributeEnd.
* This could be improved by adding an $ATTRIBUTE_LIST to the file record if there's not enough space.
*
*/
NTSTATUS
AddIndexAllocation(PNTFS_VCB Vcb,
PFILE_RECORD_HEADER FileRecord,
PNTFS_ATTR_RECORD AttributeAddress,
PCWSTR Name,
USHORT NameLength)
{
ULONG RecordLength;
ULONG FileRecordEnd;
ULONG NameOffset;
ULONG DataRunOffset;
ULONG BytesAvailable;
if (AttributeAddress->Type != AttributeEnd)
{
DPRINT1("FIXME: Can only add $INDEX_ALLOCATION attribute to the end of a file record.\n");
return STATUS_NOT_IMPLEMENTED;
}
// Calculate the name offset
NameOffset = FIELD_OFFSET(NTFS_ATTR_RECORD, NonResident.CompressedSize);
// Calculate the offset to the first data run
DataRunOffset = (sizeof(WCHAR) * NameLength) + NameOffset;
// The data run offset must be aligned to a 4-byte boundary
DataRunOffset = ALIGN_UP_BY(DataRunOffset, DATA_RUN_ALIGNMENT);
// Calculate the length of the new attribute; the empty data run will consist of a single byte
RecordLength = DataRunOffset + 1;
// The size of the attribute itself must be aligned to an 8 - byte boundary
RecordLength = ALIGN_UP_BY(RecordLength, ATTR_RECORD_ALIGNMENT);
// Back up the last 4-bytes of the file record (even though this value doesn't matter)
FileRecordEnd = AttributeAddress->Length;
// Make sure the file record can contain the new attribute
BytesAvailable = Vcb->NtfsInfo.BytesPerFileRecord - FileRecord->BytesInUse;
if (BytesAvailable < RecordLength)
{
DPRINT1("FIXME: Not enough room in file record for index allocation attribute!\n");
return STATUS_NOT_IMPLEMENTED;
}
// Set fields of attribute header
RtlZeroMemory(AttributeAddress, RecordLength);
AttributeAddress->Type = AttributeIndexAllocation;
AttributeAddress->Length = RecordLength;
AttributeAddress->IsNonResident = TRUE;
AttributeAddress->NameLength = NameLength;
AttributeAddress->NameOffset = NameOffset;
AttributeAddress->Instance = FileRecord->NextAttributeNumber++;
AttributeAddress->NonResident.MappingPairsOffset = DataRunOffset;
AttributeAddress->NonResident.HighestVCN = (LONGLONG)-1;
// Set the name
RtlCopyMemory((PCHAR)((ULONG_PTR)AttributeAddress + NameOffset), Name, NameLength * sizeof(WCHAR));
// 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 AddIndexRoot
* @implemented