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 );
|
|||
|
}
|
|||
|
|