507 lines
12 KiB
C
507 lines
12 KiB
C
/*++
|
||
|
||
Copyright (c) 1989-1997 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
heap.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the code to manipulate the value heap.
|
||
|
||
|
||
--*/
|
||
|
||
#include <viewprop.h> // needs propset.h and ntfsprop.h
|
||
|
||
#define Dbg DEBUG_TRACE_PROP_FSCTL
|
||
|
||
//
|
||
// Global illegal value
|
||
//
|
||
|
||
PROPID IllegalId = PID_ILLEGAL;
|
||
|
||
|
||
ULONG
|
||
FindStringInHeap(
|
||
IN PPROPERTY_CONTEXT Context,
|
||
IN PCOUNTED_STRING Name
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routines looks up a property by name in the property set heap.
|
||
|
||
Arguments:
|
||
|
||
Context - Context of heap to scan
|
||
|
||
Name - UNICODE string property to search for
|
||
|
||
Return Value:
|
||
|
||
Offset to property value. If name is not found, 0 is returned
|
||
|
||
--*/
|
||
|
||
{
|
||
PPROPERTY_HEAP_ENTRY HeapEntry = FIRST_HEAP_ENTRY( Context->HeapHeader );
|
||
UNICODE_STRING First, Second;
|
||
|
||
First.Buffer = COUNTED_STRING_TEXT( Name );
|
||
First.Length = Second.Length = COUNTED_STRING_LENGTH( Name );
|
||
|
||
//
|
||
// While there are more heap records
|
||
//
|
||
|
||
while (!IS_LAST_HEAP_ENTRY( Context->HeapHeader, HeapEntry )) {
|
||
|
||
//
|
||
// if a name doesn't have the same length, skip it
|
||
//
|
||
|
||
if (HeapEntry->PropertyNameLength != Name->Length) {
|
||
|
||
NOTHING;
|
||
|
||
//
|
||
// Same length, test for case-insignificant match
|
||
//
|
||
|
||
} else {
|
||
Second.Buffer = &HeapEntry->PropertyName[0];
|
||
|
||
if (RtlCompareUnicodeString( &First, &Second, TRUE ) == 0) {
|
||
|
||
//
|
||
// return pointer to name
|
||
//
|
||
|
||
DebugTrace( 0, Dbg, ("FindStringInHeap %.*ws %x\n",
|
||
COUNTED_STRING_LENGTH( Name ) / sizeof( WCHAR ),
|
||
COUNTED_STRING_TEXT( Name ),
|
||
HeapOffset( Context, HeapEntry )) );
|
||
|
||
return HeapOffset( Context, HeapEntry );
|
||
}
|
||
}
|
||
|
||
//
|
||
// advance to next heap record
|
||
//
|
||
|
||
HeapEntry = NEXT_HEAP_ENTRY( HeapEntry );
|
||
}
|
||
|
||
//
|
||
// return not-found
|
||
//
|
||
|
||
DebugTrace( 0, Dbg, ("FindStringInHeap %.*ws not found\n",
|
||
COUNTED_STRING_LENGTH( Name ) / sizeof( WCHAR ),
|
||
COUNTED_STRING_TEXT( Name )) );
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
PPROPERTY_HEAP_ENTRY GrowHeap (
|
||
IN PPROPERTY_CONTEXT Context,
|
||
IN ULONG Length
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routines grows the heap to accommodate a value of a particular length.
|
||
There is no guarantee about any "extra space" present.
|
||
|
||
Arguments:
|
||
|
||
Context - context of property set
|
||
|
||
Length - minimum growth needed
|
||
|
||
|
||
Return Value:
|
||
|
||
Pointer to new free item at end of heap
|
||
|
||
--*/
|
||
|
||
{
|
||
PPROPERTY_HEAP_ENTRY HeapEntry;
|
||
|
||
//
|
||
// Determine true growth. For now, we allocate 2X the size of the property
|
||
// up to a max of 1K. After that, we just allocate the requested size
|
||
//
|
||
|
||
if (Length <= 512) {
|
||
Length = max( 2 * Length, Length + PROPERTY_HEAP_ENTRY_SIZE( 10, 8 ));
|
||
}
|
||
|
||
//
|
||
// Resize stream to account for new growth
|
||
//
|
||
|
||
|
||
DebugTrace( 0, Dbg, ("Growing heap to length %x\n", Length + NtOfsQueryLength( Context->Attribute )));
|
||
|
||
NtOfsSetLength (
|
||
Context->IrpContext,
|
||
Context->Attribute,
|
||
Length + NtOfsQueryLength( Context->Attribute ));
|
||
|
||
//
|
||
// Set up new header for new growth, Id and Length.
|
||
//
|
||
|
||
HeapEntry = (PPROPERTY_HEAP_ENTRY) Add2Ptr( Context->HeapHeader, Context->HeapHeader->PropertyHeapLength );
|
||
|
||
LogFileFullFailCheck( Context->IrpContext );
|
||
NtOfsPutData( Context->IrpContext,
|
||
Context->Attribute,
|
||
ContextOffset( Context, &HeapEntry->PropertyId ),
|
||
sizeof( PROPID ),
|
||
&IllegalId );
|
||
|
||
LogFileFullFailCheck( Context->IrpContext );
|
||
NtOfsPutData( Context->IrpContext,
|
||
Context->Attribute,
|
||
ContextOffset( Context, &HeapEntry->PropertyValueLength ),
|
||
sizeof( Length ),
|
||
&Length );
|
||
|
||
//
|
||
// Resize heap
|
||
//
|
||
|
||
Length+= Context->HeapHeader->PropertyHeapLength;
|
||
|
||
LogFileFullFailCheck( Context->IrpContext );
|
||
NtOfsPutData( Context->IrpContext,
|
||
Context->Attribute,
|
||
ContextOffset( Context, &Context->HeapHeader->PropertyHeapLength ),
|
||
sizeof( Length ),
|
||
&Length );
|
||
|
||
return HeapEntry;
|
||
}
|
||
|
||
VOID
|
||
SetValueInHeap(
|
||
IN PPROPERTY_CONTEXT Context,
|
||
IN PPROPERTY_HEAP_ENTRY HeapEntry,
|
||
IN PROPID Id,
|
||
IN USHORT NameLength,
|
||
IN PWCHAR Name,
|
||
IN ULONG ValueLength,
|
||
IN SERIALIZEDPROPERTYVALUE *Value
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routines sets a specific value in the heap.
|
||
|
||
Arguments:
|
||
|
||
Context - context of property set
|
||
|
||
HeapEntry - pointer to header that will be modified
|
||
|
||
Id - PROPID to set
|
||
|
||
NameLength - length of name in BYTES
|
||
|
||
Name - pointer to name, may be NULL
|
||
|
||
ValueLength - count of bytes in value
|
||
|
||
Value - Serialized property value
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PROPASSERT( Id != PID_ILLEGAL );
|
||
|
||
//
|
||
// Set PropertyId
|
||
//
|
||
|
||
LogFileFullFailCheck( Context->IrpContext );
|
||
NtOfsPutData( Context->IrpContext,
|
||
Context->Attribute,
|
||
ContextOffset( Context, &HeapEntry->PropertyId ),
|
||
sizeof( PROPID ),
|
||
&Id );
|
||
|
||
//
|
||
// Set name length
|
||
//
|
||
|
||
LogFileFullFailCheck( Context->IrpContext );
|
||
NtOfsPutData( Context->IrpContext,
|
||
Context->Attribute,
|
||
ContextOffset( Context, &HeapEntry->PropertyNameLength ),
|
||
sizeof( USHORT ),
|
||
&NameLength );
|
||
|
||
//
|
||
// Set name if present
|
||
//
|
||
|
||
if (Name != NULL) {
|
||
LogFileFullFailCheck( Context->IrpContext );
|
||
NtOfsPutData( Context->IrpContext,
|
||
Context->Attribute,
|
||
ContextOffset( Context, &HeapEntry->PropertyName[0] ),
|
||
NameLength,
|
||
Name );
|
||
}
|
||
|
||
//
|
||
// Set property value
|
||
//
|
||
|
||
LogFileFullFailCheck( Context->IrpContext );
|
||
NtOfsPutData( Context->IrpContext,
|
||
Context->Attribute,
|
||
ContextOffset( Context, PROPERTY_HEAP_ENTRY_VALUE( HeapEntry )),
|
||
ValueLength,
|
||
Value );
|
||
|
||
}
|
||
|
||
|
||
ULONG
|
||
AddValueToHeap(
|
||
IN PPROPERTY_CONTEXT Context,
|
||
IN PROPID Id,
|
||
IN ULONG Length,
|
||
IN USHORT NameLength,
|
||
IN PWCHAR Name OPTIONAL,
|
||
IN ULONG ValueLength,
|
||
IN SERIALIZEDPROPERTYVALUE *Value
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routines adds a value to the heap. We walk the heap looking for a free
|
||
header (PID_ILLEGAL) whose size is sufficient to contain the name and value.
|
||
If one is not found, the heap and attribute are grown.
|
||
|
||
Arguments:
|
||
|
||
Context - context of property set
|
||
|
||
Id - PROPID to add
|
||
|
||
Length - length of entire propery value (including name) in bytes
|
||
|
||
NameLength - length of name in BYTES
|
||
|
||
Name - pointer to name, may be NULL
|
||
|
||
ValueLength - count of bytes in value
|
||
|
||
Value - Serialized property value
|
||
|
||
Return Value:
|
||
|
||
Offset to property value stored in heap
|
||
|
||
--*/
|
||
|
||
{
|
||
PPROPERTY_HEAP_ENTRY HeapEntry = FIRST_HEAP_ENTRY( Context->HeapHeader );
|
||
|
||
PROPASSERT( Id != PID_ILLEGAL );
|
||
|
||
DebugTrace( +1, Dbg, ("AddValueToHeap(%x '%.*ws' %x len %x)\n",
|
||
Id, NameLength / sizeof( WCHAR ), Name, Value, ValueLength) );
|
||
DebugTrace( 0, Dbg, ("property value length is %x\n", Length) );
|
||
|
||
//
|
||
// Walk through the heap until we reach the end or until we have
|
||
// a free block that contains enough room for this property. We also
|
||
// will break out if the block we find has enough room for the property
|
||
// and can be split to allow for a property that has 5 chars of name
|
||
// and 8 bytes of serialized value.
|
||
//
|
||
|
||
while (TRUE) {
|
||
//
|
||
// If this is not a valid header, break
|
||
//
|
||
|
||
if (IS_LAST_HEAP_ENTRY( Context->HeapHeader, HeapEntry )) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// if this is a free header
|
||
//
|
||
|
||
if (HeapEntry->PropertyId == PID_ILLEGAL) {
|
||
|
||
//
|
||
// If the value length matches exactly, break
|
||
//
|
||
|
||
if (HeapEntry->PropertyValueLength == Length) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// If the value length allows for an extra value, break
|
||
//
|
||
|
||
if (HeapEntry->PropertyValueLength >=
|
||
Length + PROPERTY_HEAP_ENTRY_SIZE( 10, 8 )) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Go to next block
|
||
//
|
||
|
||
HeapEntry = NEXT_HEAP_ENTRY( HeapEntry );
|
||
}
|
||
|
||
//
|
||
// If we are at the end of the heap, then we must grow the heap. In this case
|
||
// we grow the heap by a large block and set thing up as if we had found
|
||
// a free block of the right size. We then fall into the normal reuse-free-
|
||
// block code.
|
||
//
|
||
|
||
if (IS_LAST_HEAP_ENTRY( Context->HeapHeader, HeapEntry )) {
|
||
HeapEntry = GrowHeap( Context, Length );
|
||
}
|
||
|
||
//
|
||
// HeapEntry points to a free heap block that is large enough to contain
|
||
// the new length, possibly splitting. First, split if necessary.
|
||
//
|
||
|
||
if (HeapEntry->PropertyValueLength != Length) {
|
||
PPROPERTY_HEAP_ENTRY SecondHeader;
|
||
ULONG SecondLength = HeapEntry->PropertyValueLength - Length;
|
||
|
||
PROPASSERT( Length + PROPERTY_HEAP_ENTRY_SIZE( 10, 8 ) <= HeapEntry->PropertyValueLength);
|
||
|
||
//
|
||
// Get address of second block header
|
||
//
|
||
|
||
SecondHeader = (PPROPERTY_HEAP_ENTRY) Add2Ptr( HeapEntry, Length );
|
||
|
||
//
|
||
// set up second block header: length and propid
|
||
//
|
||
|
||
LogFileFullFailCheck( Context->IrpContext );
|
||
NtOfsPutData( Context->IrpContext,
|
||
Context->Attribute,
|
||
ContextOffset( Context, &SecondHeader->PropertyId ),
|
||
sizeof( PROPID ),
|
||
&IllegalId );
|
||
|
||
LogFileFullFailCheck( Context->IrpContext );
|
||
NtOfsPutData( Context->IrpContext,
|
||
Context->Attribute,
|
||
ContextOffset( Context, &SecondHeader->PropertyValueLength ),
|
||
sizeof( SecondLength ),
|
||
&SecondLength );
|
||
|
||
|
||
//
|
||
// set up value header length to reflect split
|
||
//
|
||
|
||
LogFileFullFailCheck( Context->IrpContext );
|
||
NtOfsPutData( Context->IrpContext,
|
||
Context->Attribute,
|
||
ContextOffset( Context, &HeapEntry->PropertyValueLength ),
|
||
sizeof( Length ),
|
||
&Length );
|
||
|
||
}
|
||
|
||
//
|
||
// HeapEntry points at a free block with exactly the right amount of space
|
||
// for this name/value
|
||
//
|
||
|
||
PROPASSERT( Length == HeapEntry->PropertyValueLength );
|
||
|
||
SetValueInHeap( Context, HeapEntry, Id, NameLength, Name, ValueLength, Value );
|
||
|
||
DebugTrace( -1, Dbg, ("value offset is at %x\n",
|
||
HeapOffset( Context, HeapEntry )) );
|
||
|
||
return HeapOffset( Context, HeapEntry );
|
||
}
|
||
|
||
|
||
VOID
|
||
DeleteFromHeap(
|
||
IN PPROPERTY_CONTEXT Context,
|
||
IN PPROPERTY_HEAP_ENTRY HeapEntry
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routines deletes a value from the heap. The present implementation
|
||
marks the header as being for PID_ILLEGAL.
|
||
|
||
Arguments:
|
||
|
||
Context - Property set context
|
||
|
||
HeapEntry - entry to be deleted
|
||
|
||
Return Value:
|
||
|
||
Nothing
|
||
|
||
--*/
|
||
|
||
{
|
||
DebugTrace( 0, Dbg, ("DeleteFromHeap at %x\n", HeapEntry) );
|
||
|
||
//
|
||
// Delete by setting the value to be PID_ILLEGAL
|
||
//
|
||
|
||
LogFileFullFailCheck( Context->IrpContext );
|
||
NtOfsPutData( Context->IrpContext,
|
||
Context->Attribute,
|
||
ContextOffset( Context, &HeapEntry->PropertyId),
|
||
sizeof( IllegalId ),
|
||
&IllegalId );
|
||
}
|
||
|