windows-nt/Source/XPSP1/NT/base/ntos/config/cmsubs2.c
2020-09-26 16:20:57 +08:00

1422 lines
43 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
cmsubs2.c
Abstract:
This module various support routines for the configuration manager.
The routines in this module are independent enough to be linked into
any other program. The routines in cmsubs.c are not.
Author:
Bryan M. Willman (bryanwi) 12-Sep-1991
Revision History:
--*/
#include "cmp.h"
BOOLEAN
CmpGetValueDataFromCache(
IN PHHIVE Hive,
IN PPCM_CACHED_VALUE ContainingList,
IN PCELL_DATA ValueKey,
IN BOOLEAN ValueCached,
OUT PUCHAR *DataPointer,
OUT PBOOLEAN Allocated,
OUT PHCELL_INDEX CellToRelease
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,CmpGetValueDataFromCache)
#pragma alloc_text(PAGE,CmpQueryKeyData)
#pragma alloc_text(PAGE,CmpQueryKeyDataFromCache)
#pragma alloc_text(PAGE,CmpQueryKeyValueData)
#endif
//
// Define alignment macro.
//
#define ALIGN_OFFSET(Offset) (ULONG) \
((((ULONG)(Offset) + sizeof(ULONG)-1)) & (~(sizeof(ULONG) - 1)))
#define ALIGN_OFFSET64(Offset) (ULONG) \
((((ULONG)(Offset) + sizeof(ULONGLONG)-1)) & (~(sizeof(ULONGLONG) - 1)))
//
// Data transfer workers
//
#ifdef CMP_STATS
extern struct {
ULONG BasicInformation;
UINT64 BasicInformationTimeCounter;
UINT64 BasicInformationTimeElapsed;
ULONG NodeInformation;
UINT64 NodeInformationTimeCounter;
UINT64 NodeInformationTimeElapsed;
ULONG FullInformation;
UINT64 FullInformationTimeCounter;
UINT64 FullInformationTimeElapsed;
ULONG EnumerateKeyBasicInformation;
UINT64 EnumerateKeyBasicInformationTimeCounter;
UINT64 EnumerateKeyBasicInformationTimeElapsed;
ULONG EnumerateKeyNodeInformation;
UINT64 EnumerateKeyNodeInformationTimeCounter;
UINT64 EnumerateKeyNodeInformationTimeElapsed;
ULONG EnumerateKeyFullInformation;
UINT64 EnumerateKeyFullInformationTimeCounter;
UINT64 EnumerateKeyFullInformationTimeElapsed;
} CmpQueryKeyDataDebug;
UINT64 CmpGetTimeStamp()
{
LARGE_INTEGER CurrentTime;
LARGE_INTEGER PerfFrequency;
UINT64 Freq;
UINT64 Time;
CurrentTime = KeQueryPerformanceCounter(&PerfFrequency);
//
// Convert the perffrequency into 100ns interval.
//
Freq = 0;
Freq |= PerfFrequency.HighPart;
Freq = Freq << 32;
Freq |= PerfFrequency.LowPart;
//
// Convert from LARGE_INTEGER to UINT64
//
Time = 0;
Time |= CurrentTime.HighPart;
Time = Time << 32;
Time |= CurrentTime.LowPart;
// Normalize cycles with the frequency.
Time *= 10000000;
Time /= Freq;
return Time;
}
#endif
NTSTATUS
CmpQueryKeyData(
PHHIVE Hive,
PCM_KEY_NODE Node,
KEY_INFORMATION_CLASS KeyInformationClass,
PVOID KeyInformation,
ULONG Length,
PULONG ResultLength
#if defined(CMP_STATS) || defined(CMP_KCB_CACHE_VALIDATION)
,
PCM_KEY_CONTROL_BLOCK Kcb
#endif
)
/*++
Routine Description:
Do the actual copy of data for a key into caller's buffer.
If KeyInformation is not long enough to hold all requested data,
STATUS_BUFFER_OVERFLOW will be returned, and ResultLength will be
set to the number of bytes actually required.
Arguments:
Hive - supplies a pointer to the hive control structure for the hive
Node - Supplies pointer to node whose subkeys are to be found
KeyInformationClass - Specifies the type of information returned in
Buffer. One of the following types:
KeyBasicInformation - return last write time, title index, and name.
(see KEY_BASIC_INFORMATION structure)
KeyNodeInformation - return last write time, title index, name, class.
(see KEY_NODE_INFORMATION structure)
KeyInformation -Supplies pointer to buffer to receive the data.
Length - Length of KeyInformation in bytes.
ResultLength - Number of bytes actually written into KeyInformation.
Return Value:
NTSTATUS - Result code from call, among the following:
<TBS>
--*/
{
NTSTATUS status;
PCELL_DATA pclass;
ULONG requiredlength;
LONG leftlength;
ULONG offset;
ULONG minimumlength;
PKEY_INFORMATION pbuffer;
USHORT NameLength;
#ifdef CMP_STATS
//LARGE_INTEGER StartSystemTime;
//LARGE_INTEGER EndSystemTime;
UINT64 StartSystemTime;
UINT64 EndSystemTime;
PUINT64 TimeCounter = NULL;
PUINT64 TimeElapsed = NULL;
//KeQuerySystemTime(&StartSystemTime);
//StartSystemTime = KeQueryPerformanceCounter(NULL);
StartSystemTime = CmpGetTimeStamp();
#endif //CMP_STATS
#ifdef CMP_KCB_CACHE_VALIDATION
//
// We have cached a lot of info into the kcb; Here is some validation code
//
if( Kcb ) {
BEGIN_KCB_LOCK_GUARD;
CmpLockKCBTree();
// number of values
ASSERT( Node->ValueList.Count == Kcb->ValueCache.Count );
// number of subkeys
if( !(Kcb->ExtFlags & CM_KCB_INVALID_CACHED_INFO) ) {
// there is some cached info
ULONG SubKeyCount = Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile];
if( Kcb->ExtFlags & CM_KCB_NO_SUBKEY ) {
ASSERT( SubKeyCount == 0 );
} else if( Kcb->ExtFlags & CM_KCB_SUBKEY_ONE ) {
ASSERT( SubKeyCount == 1 );
} else if( Kcb->ExtFlags & CM_KCB_SUBKEY_HINT ) {
ASSERT( SubKeyCount == Kcb->IndexHint->Count );
} else {
ASSERT( SubKeyCount == Kcb->SubKeyCount );
}
}
// LastWriteTime
ASSERT( Node->LastWriteTime.QuadPart == Kcb->KcbLastWriteTime.QuadPart );
// MaxNameLen
ASSERT( Node->MaxNameLen == Kcb->KcbMaxNameLen );
// MaxValueNameLen
ASSERT( Node->MaxValueNameLen == Kcb->KcbMaxValueNameLen );
// MaxValueDataLen
ASSERT( Node->MaxValueDataLen == Kcb->KcbMaxValueDataLen );
CmpUnlockKCBTree();
END_KCB_LOCK_GUARD;
}
#endif //CMP_KCB_CACHE_VALIDATION
pbuffer = (PKEY_INFORMATION)KeyInformation;
NameLength = CmpHKeyNameLen(Node);
switch (KeyInformationClass) {
case KeyBasicInformation:
#ifdef CMP_STATS
if(Kcb) {
CmpQueryKeyDataDebug.BasicInformation++;
TimeCounter = &(CmpQueryKeyDataDebug.BasicInformationTimeCounter);
TimeElapsed = &(CmpQueryKeyDataDebug.BasicInformationTimeElapsed);
} else {
CmpQueryKeyDataDebug.EnumerateKeyBasicInformation++;
TimeCounter = &(CmpQueryKeyDataDebug.EnumerateKeyBasicInformationTimeCounter);
TimeElapsed = &(CmpQueryKeyDataDebug.EnumerateKeyBasicInformationTimeElapsed);
}
#endif //CMP_STATS
//
// LastWriteTime, TitleIndex, NameLength, Name
requiredlength = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name) +
NameLength;
minimumlength = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name);
*ResultLength = requiredlength;
status = STATUS_SUCCESS;
if (Length < minimumlength) {
status = STATUS_BUFFER_TOO_SMALL;
} else {
pbuffer->KeyBasicInformation.LastWriteTime =
Node->LastWriteTime;
pbuffer->KeyBasicInformation.TitleIndex = 0;
pbuffer->KeyBasicInformation.NameLength =
NameLength;
leftlength = Length - minimumlength;
requiredlength = NameLength;
if (leftlength < (LONG)requiredlength) {
requiredlength = leftlength;
status = STATUS_BUFFER_OVERFLOW;
}
if (Node->Flags & KEY_COMP_NAME) {
CmpCopyCompressedName(pbuffer->KeyBasicInformation.Name,
leftlength,
Node->Name,
Node->NameLength);
} else {
RtlCopyMemory(
&(pbuffer->KeyBasicInformation.Name[0]),
&(Node->Name[0]),
requiredlength
);
}
}
break;
case KeyNodeInformation:
#ifdef CMP_STATS
if(Kcb) {
CmpQueryKeyDataDebug.NodeInformation++;
TimeCounter = &(CmpQueryKeyDataDebug.NodeInformationTimeCounter);
TimeElapsed = &(CmpQueryKeyDataDebug.NodeInformationTimeElapsed);
} else {
CmpQueryKeyDataDebug.EnumerateKeyNodeInformation++;
TimeCounter = &(CmpQueryKeyDataDebug.EnumerateKeyNodeInformationTimeCounter);
TimeElapsed = &(CmpQueryKeyDataDebug.EnumerateKeyNodeInformationTimeElapsed);
}
#endif //CMP_STATS
//
// LastWriteTime, TitleIndex, ClassOffset, ClassLength
// NameLength, Name, Class
//
requiredlength = FIELD_OFFSET(KEY_NODE_INFORMATION, Name) +
NameLength +
Node->ClassLength;
minimumlength = FIELD_OFFSET(KEY_NODE_INFORMATION, Name);
*ResultLength = requiredlength;
status = STATUS_SUCCESS;
if (Length < minimumlength) {
status = STATUS_BUFFER_TOO_SMALL;
} else {
pbuffer->KeyNodeInformation.LastWriteTime =
Node->LastWriteTime;
pbuffer->KeyNodeInformation.TitleIndex = 0;
pbuffer->KeyNodeInformation.ClassLength =
Node->ClassLength;
pbuffer->KeyNodeInformation.NameLength =
NameLength;
leftlength = Length - minimumlength;
requiredlength = NameLength;
if (leftlength < (LONG)requiredlength) {
requiredlength = leftlength;
status = STATUS_BUFFER_OVERFLOW;
}
if (Node->Flags & KEY_COMP_NAME) {
CmpCopyCompressedName(pbuffer->KeyNodeInformation.Name,
leftlength,
Node->Name,
Node->NameLength);
} else {
RtlCopyMemory(
&(pbuffer->KeyNodeInformation.Name[0]),
&(Node->Name[0]),
requiredlength
);
}
if (Node->ClassLength > 0) {
offset = FIELD_OFFSET(KEY_NODE_INFORMATION, Name) +
NameLength;
offset = ALIGN_OFFSET(offset);
pbuffer->KeyNodeInformation.ClassOffset = offset;
pclass = HvGetCell(Hive, Node->Class);
if( pclass == NULL ) {
//
// we couldn't map this cell
//
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
pbuffer = (PKEY_INFORMATION)((PUCHAR)pbuffer + offset);
leftlength = (((LONG)Length - (LONG)offset) < 0) ?
0 :
Length - offset;
requiredlength = Node->ClassLength;
if (leftlength < (LONG)requiredlength) {
requiredlength = leftlength;
status = STATUS_BUFFER_OVERFLOW;
}
RtlCopyMemory(
pbuffer,
pclass,
requiredlength
);
HvReleaseCell(Hive,Node->Class);
} else {
pbuffer->KeyNodeInformation.ClassOffset = (ULONG)-1;
}
}
break;
case KeyFullInformation:
#ifdef CMP_STATS
if(Kcb) {
CmpQueryKeyDataDebug.FullInformation++;
TimeCounter = &(CmpQueryKeyDataDebug.FullInformationTimeCounter);
TimeElapsed = &(CmpQueryKeyDataDebug.FullInformationTimeElapsed);
} else {
CmpQueryKeyDataDebug.EnumerateKeyFullInformation++;
TimeCounter = &(CmpQueryKeyDataDebug.EnumerateKeyFullInformationTimeCounter);
TimeElapsed = &(CmpQueryKeyDataDebug.EnumerateKeyFullInformationTimeElapsed);
}
#endif //CMP_STATS
//
// LastWriteTime, TitleIndex, ClassOffset, ClassLength,
// SubKeys, MaxNameLen, MaxClassLen, Values, MaxValueNameLen,
// MaxValueDataLen, Class
//
requiredlength = FIELD_OFFSET(KEY_FULL_INFORMATION, Class) +
Node->ClassLength;
minimumlength = FIELD_OFFSET(KEY_FULL_INFORMATION, Class);
*ResultLength = requiredlength;
status = STATUS_SUCCESS;
if (Length < minimumlength) {
status = STATUS_BUFFER_TOO_SMALL;
} else {
pbuffer->KeyFullInformation.LastWriteTime =
Node->LastWriteTime;
pbuffer->KeyFullInformation.TitleIndex = 0;
pbuffer->KeyFullInformation.ClassLength =
Node->ClassLength;
if (Node->ClassLength > 0) {
pbuffer->KeyFullInformation.ClassOffset =
FIELD_OFFSET(KEY_FULL_INFORMATION, Class);
pclass = HvGetCell(Hive, Node->Class);
if( pclass == NULL ) {
//
// we couldn't map this cell
//
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
leftlength = Length - minimumlength;
requiredlength = Node->ClassLength;
if (leftlength < (LONG)requiredlength) {
requiredlength = leftlength;
status = STATUS_BUFFER_OVERFLOW;
}
RtlCopyMemory(
&(pbuffer->KeyFullInformation.Class[0]),
pclass,
requiredlength
);
HvReleaseCell(Hive,Node->Class);
} else {
pbuffer->KeyFullInformation.ClassOffset = (ULONG)-1;
}
pbuffer->KeyFullInformation.SubKeys =
Node->SubKeyCounts[Stable] +
Node->SubKeyCounts[Volatile];
pbuffer->KeyFullInformation.Values =
Node->ValueList.Count;
pbuffer->KeyFullInformation.MaxNameLen =
Node->MaxNameLen;
pbuffer->KeyFullInformation.MaxClassLen =
Node->MaxClassLen;
pbuffer->KeyFullInformation.MaxValueNameLen =
Node->MaxValueNameLen;
pbuffer->KeyFullInformation.MaxValueDataLen =
Node->MaxValueDataLen;
}
break;
default:
status = STATUS_INVALID_PARAMETER;
break;
}
#ifdef CMP_STATS
if( TimeCounter && TimeElapsed ){
//EndSystemTime = KeQueryPerformanceCounter(NULL);
//KeQuerySystemTime(&EndSystemTime);
EndSystemTime = CmpGetTimeStamp();
if( (EndSystemTime - StartSystemTime) > 0 ) {
(*TimeCounter)++;
//(*TimeElapsed) += (ULONG)(EndSystemTime.QuadPart - StartSystemTime.QuadPart);
(*TimeElapsed) += (EndSystemTime - StartSystemTime);
}
}
#endif //CMP_STATS
return status;
}
NTSTATUS
CmpQueryKeyDataFromCache(
PCM_KEY_CONTROL_BLOCK Kcb,
KEY_INFORMATION_CLASS KeyInformationClass,
PVOID KeyInformation,
ULONG Length,
PULONG ResultLength
)
/*++
Routine Description:
Do the actual copy of data for a key into caller's buffer.
If KeyInformation is not long enough to hold all requested data,
STATUS_BUFFER_OVERFLOW will be returned, and ResultLength will be
set to the number of bytes actually required.
Works only for the information cached into kcb. I.e. KeyBasicInformation
and KeyCachedInfo
Arguments:
Kcb - Supplies pointer to the kcb to be queried
KeyInformationClass - Specifies the type of information returned in
Buffer. One of the following types:
KeyBasicInformation - return last write time, title index, and name.
(see KEY_BASIC_INFORMATION structure)
KeyCachedInformation - return last write time, title index, name ....
(see KEY_CACHED_INFORMATION structure)
KeyInformation -Supplies pointer to buffer to receive the data.
Length - Length of KeyInformation in bytes.
ResultLength - Number of bytes actually written into KeyInformation.
Return Value:
NTSTATUS - Result code from call, among the following:
<TBS>
--*/
{
NTSTATUS status;
PKEY_INFORMATION pbuffer;
ULONG requiredlength;
ULONG minimumlength;
USHORT NameLength;
LONG leftlength;
PCM_KEY_NODE Node; // this is to be used only in case of cache incoherency
PAGED_CODE();
#ifdef CMP_KCB_CACHE_VALIDATION
//
// We have cached a lot of info into the kcb; Here is some validation code
//
if( Kcb ) {
BEGIN_KCB_LOCK_GUARD;
CmpLockKCBTree();
Node = (PCM_KEY_NODE)HvGetCell(Kcb->KeyHive,Kcb->KeyCell);
if( Node != NULL ) {
// number of values
ASSERT( Node->ValueList.Count == Kcb->ValueCache.Count );
// number of subkeys
if( !(Kcb->ExtFlags & CM_KCB_INVALID_CACHED_INFO) ) {
// there is some cached info
ULONG SubKeyCount = Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile];
if( Kcb->ExtFlags & CM_KCB_NO_SUBKEY ) {
ASSERT( SubKeyCount == 0 );
} else if( Kcb->ExtFlags & CM_KCB_SUBKEY_ONE ) {
ASSERT( SubKeyCount == 1 );
} else if( Kcb->ExtFlags & CM_KCB_SUBKEY_HINT ) {
ASSERT( SubKeyCount == Kcb->IndexHint->Count );
} else {
ASSERT( SubKeyCount == Kcb->SubKeyCount );
}
}
// LastWriteTime
ASSERT( Node->LastWriteTime.QuadPart == Kcb->KcbLastWriteTime.QuadPart );
// MaxNameLen
ASSERT( Node->MaxNameLen == Kcb->KcbMaxNameLen );
// MaxValueNameLen
ASSERT( Node->MaxValueNameLen == Kcb->KcbMaxValueNameLen );
// MaxValueDataLen
ASSERT( Node->MaxValueDataLen == Kcb->KcbMaxValueDataLen );
HvReleaseCell(Kcb->KeyHive,Kcb->KeyCell);
}
CmpUnlockKCBTree();
END_KCB_LOCK_GUARD;
}
#endif //CMP_KCB_CACHE_VALIDATION
//
// we cannot afford to return the kcb NameBlock as the key name
// for KeyBasicInformation as there are lots of callers expecting
// the name to be case-sensitive; KeyCachedInformation is new
// and used only by the Win32 layer, which is not case sensitive
// Note: future clients of KeyCachedInformation must be made aware
// that name is NOT case-sensitive
//
ASSERT( KeyInformationClass == KeyCachedInformation );
//
// we are going to need the nameblock; if it is NULL, bail out
//
if( Kcb->NameBlock == NULL ) {
return STATUS_INSUFFICIENT_RESOURCES;
}
pbuffer = (PKEY_INFORMATION)KeyInformation;
if (Kcb->NameBlock->Compressed) {
NameLength = CmpCompressedNameSize(Kcb->NameBlock->Name,Kcb->NameBlock->NameLength);
} else {
NameLength = Kcb->NameBlock->NameLength;
}
// Assume success
status = STATUS_SUCCESS;
switch (KeyInformationClass) {
#if 0
case KeyBasicInformation:
//
// LastWriteTime, TitleIndex, NameLength, Name
requiredlength = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name) +
NameLength;
minimumlength = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name);
*ResultLength = requiredlength;
if (Length < minimumlength) {
status = STATUS_BUFFER_TOO_SMALL;
} else {
pbuffer->KeyBasicInformation.LastWriteTime = Kcb->KcbLastWriteTime;
pbuffer->KeyBasicInformation.TitleIndex = 0;
pbuffer->KeyBasicInformation.NameLength = NameLength;
leftlength = Length - minimumlength;
requiredlength = NameLength;
if (leftlength < (LONG)requiredlength) {
requiredlength = leftlength;
status = STATUS_BUFFER_OVERFLOW;
}
if (Kcb->NameBlock->Compressed) {
CmpCopyCompressedName(pbuffer->KeyBasicInformation.Name,
leftlength,
Kcb->NameBlock->Name,
Kcb->NameBlock->NameLength);
} else {
RtlCopyMemory(
&(pbuffer->KeyBasicInformation.Name[0]),
&(Kcb->NameBlock->Name[0]),
requiredlength
);
}
}
break;
#endif
case KeyCachedInformation:
//
// LastWriteTime, TitleIndex,
// SubKeys, MaxNameLen, Values, MaxValueNameLen,
// MaxValueDataLen, Name
//
requiredlength = sizeof(KEY_CACHED_INFORMATION);
*ResultLength = requiredlength;
if (Length < requiredlength) {
status = STATUS_BUFFER_TOO_SMALL;
} else {
pbuffer->KeyCachedInformation.LastWriteTime = Kcb->KcbLastWriteTime;
pbuffer->KeyCachedInformation.TitleIndex = 0;
pbuffer->KeyCachedInformation.NameLength = NameLength;
pbuffer->KeyCachedInformation.Values = Kcb->ValueCache.Count;
pbuffer->KeyCachedInformation.MaxNameLen = Kcb->KcbMaxNameLen;
pbuffer->KeyCachedInformation.MaxValueNameLen = Kcb->KcbMaxValueNameLen;
pbuffer->KeyCachedInformation.MaxValueDataLen = Kcb->KcbMaxValueDataLen;
if( !(Kcb->ExtFlags & CM_KCB_INVALID_CACHED_INFO) ) {
// there is some cached info
if( Kcb->ExtFlags & CM_KCB_NO_SUBKEY ) {
pbuffer->KeyCachedInformation.SubKeys = 0;
} else if( Kcb->ExtFlags & CM_KCB_SUBKEY_ONE ) {
pbuffer->KeyCachedInformation.SubKeys = 1;
} else if( Kcb->ExtFlags & CM_KCB_SUBKEY_HINT ) {
pbuffer->KeyCachedInformation.SubKeys = Kcb->IndexHint->Count;
} else {
pbuffer->KeyCachedInformation.SubKeys = Kcb->SubKeyCount;
}
} else {
//
// kcb cache is not coherent; get the info from knode
//
CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"Kcb cache incoherency detected, kcb = %p\n",Kcb));
Node = (PCM_KEY_NODE)HvGetCell(Kcb->KeyHive,Kcb->KeyCell);
if( Node == NULL ) {
//
// couldn't map view for this cell
//
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
pbuffer->KeyCachedInformation.SubKeys = Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile];
HvReleaseCell(Kcb->KeyHive,Kcb->KeyCell);
}
}
break;
default:
status = STATUS_INVALID_PARAMETER;
break;
}
return status;
}
BOOLEAN
CmpGetValueDataFromCache(
IN PHHIVE Hive,
IN PPCM_CACHED_VALUE ContainingList,
IN PCELL_DATA ValueKey,
IN BOOLEAN ValueCached,
OUT PUCHAR *DataPointer,
OUT PBOOLEAN Allocated,
OUT PHCELL_INDEX CellToRelease
)
/*++
Routine Description:
Get the cached Value Data given a value node.
Arguments:
Hive - pointer to hive control structure for hive of interest
ContainingList - Address that stores the allocation address of the value node.
We need to update this when we do a re-allocate to cache
both value key and value data.
ValueKey - pointer to the Value Key
ValueCached - Indicating whether Value key is cached or not.
DataPointer - out param to receive a pointer to the data
Allocated - out param telling if the caller should free the DataPointer
Return Value:
TRUE - data was retrieved
FALSE - some error (STATUS_INSUFFICIENT_RESOURCES) occured
Note:
The caller is responsible for freeing the DataPointer when we signal it to him
by setting Allocated on TRUE;
Also we must be sure that MAXIMUM_CACHED_DATA is smaller than CM_KEY_VALUE_BIG
--*/
{
//
// Cache the data if needed.
//
PCM_CACHED_VALUE OldEntry;
PCM_CACHED_VALUE NewEntry;
PUCHAR Cacheddatapointer;
ULONG AllocSize;
ULONG CopySize;
ULONG DataSize;
ASSERT( MAXIMUM_CACHED_DATA < CM_KEY_VALUE_BIG );
//
// this routine should not be called for small data
//
ASSERT( (ValueKey->u.KeyValue.DataLength & CM_KEY_VALUE_SPECIAL_SIZE) == 0 );
//
// init out params
//
*DataPointer = NULL;
*Allocated = FALSE;
*CellToRelease = HCELL_NIL;
if (ValueCached) {
OldEntry = (PCM_CACHED_VALUE) CMP_GET_CACHED_ADDRESS(*ContainingList);
if (OldEntry->DataCacheType == CM_CACHE_DATA_CACHED) {
//
// Data is already cached, use it.
//
*DataPointer = (PUCHAR) ((ULONG_PTR) ValueKey + OldEntry->ValueKeySize);
} else {
if ((OldEntry->DataCacheType == CM_CACHE_DATA_TOO_BIG) ||
(ValueKey->u.KeyValue.DataLength > MAXIMUM_CACHED_DATA )
){
//
// Mark the type and do not cache it.
//
OldEntry->DataCacheType = CM_CACHE_DATA_TOO_BIG;
//
// Data is too big to warrent caching, get it from the registry;
// - regardless of the size; we might be forced to allocate a buffer
//
if( CmpGetValueData(Hive,&(ValueKey->u.KeyValue),&DataSize,DataPointer,Allocated,CellToRelease) == FALSE ) {
//
// insufficient resources; return NULL
//
ASSERT( *Allocated == FALSE );
ASSERT( *DataPointer == NULL );
return FALSE;
}
} else {
//
// consistency check
//
ASSERT(OldEntry->DataCacheType == CM_CACHE_DATA_NOT_CACHED);
//
// Value data is not cached.
// Check the size of value data, if it is smaller than MAXIMUM_CACHED_DATA, cache it.
//
// Anyway, the data is for sure not stored in a big data cell (see test above)
//
//
*DataPointer = (PUCHAR)HvGetCell(Hive, ValueKey->u.KeyValue.Data);
if( *DataPointer == NULL ) {
//
// we couldn't map this cell
// the caller must handle this gracefully !
//
return FALSE;
}
//
// inform the caller it has to release this cell
//
*CellToRelease = ValueKey->u.KeyValue.Data;
//
// copy only valid data; cell might be bigger
//
//DataSize = (ULONG) HvGetCellSize(Hive, datapointer);
DataSize = (ULONG)ValueKey->u.KeyValue.DataLength;
//
// consistency check
//
ASSERT(DataSize <= MAXIMUM_CACHED_DATA);
//
// Data is not cached and now we are going to do it.
// Reallocate a new cached entry for both value key and value data.
//
CopySize = DataSize + OldEntry->ValueKeySize;
AllocSize = CopySize + FIELD_OFFSET(CM_CACHED_VALUE, KeyValue);
// Dragos: Changed to catch the memory violator
// it didn't work
//NewEntry = (PCM_CACHED_VALUE) ExAllocatePoolWithTagPriority(PagedPool, AllocSize, CM_CACHE_VALUE_DATA_TAG,NormalPoolPrioritySpecialPoolUnderrun);
NewEntry = (PCM_CACHED_VALUE) ExAllocatePoolWithTag(PagedPool, AllocSize, CM_CACHE_VALUE_DATA_TAG);
if (NewEntry) {
//
// Now fill the data to the new cached entry
//
NewEntry->DataCacheType = CM_CACHE_DATA_CACHED;
NewEntry->ValueKeySize = OldEntry->ValueKeySize;
RtlCopyMemory((PVOID)&(NewEntry->KeyValue),
(PVOID)&(OldEntry->KeyValue),
NewEntry->ValueKeySize);
Cacheddatapointer = (PUCHAR) ((ULONG_PTR) &(NewEntry->KeyValue) + OldEntry->ValueKeySize);
RtlCopyMemory(Cacheddatapointer, *DataPointer, DataSize);
// Trying to catch the BAD guy who writes over our pool.
CmpMakeSpecialPoolReadWrite( OldEntry );
*ContainingList = (PCM_CACHED_VALUE) CMP_MARK_CELL_CACHED(NewEntry);
// Trying to catch the BAD guy who writes over our pool.
CmpMakeSpecialPoolReadOnly( NewEntry );
//
// Free the old entry
//
ExFreePool(OldEntry);
}
}
}
} else {
if( CmpGetValueData(Hive,&(ValueKey->u.KeyValue),&DataSize,DataPointer,Allocated,CellToRelease) == FALSE ) {
//
// insufficient resources; return NULL
//
ASSERT( *Allocated == FALSE );
ASSERT( *DataPointer == NULL );
return FALSE;
}
}
return TRUE;
}
NTSTATUS
CmpQueryKeyValueData(
PHHIVE Hive,
PPCM_CACHED_VALUE ContainingList,
PCM_KEY_VALUE ValueKey,
BOOLEAN ValueCached,
KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
PVOID KeyValueInformation,
ULONG Length,
PULONG ResultLength
)
/*++
Routine Description:
Do the actual copy of data for a key value into caller's buffer.
If KeyValueInformation is not long enough to hold all requested data,
STATUS_BUFFER_OVERFLOW will be returned, and ResultLength will be
set to the number of bytes actually required.
Arguments:
Hive - supplies a pointer to the hive control structure for the hive
Cell - supplies index of node to whose sub keys are to be found
KeyValueInformationClass - Specifies the type of information returned in
KeyValueInformation. One of the following types:
KeyValueInformation -Supplies pointer to buffer to receive the data.
Length - Length of KeyInformation in bytes.
ResultLength - Number of bytes actually written into KeyInformation.
Return Value:
NTSTATUS - Result code from call, among the following:
<TBS>
--*/
{
NTSTATUS status;
PKEY_VALUE_INFORMATION pbuffer;
PCELL_DATA pcell;
LONG leftlength;
ULONG requiredlength;
ULONG minimumlength;
ULONG offset;
ULONG base;
ULONG realsize;
PUCHAR datapointer;
BOOLEAN small;
USHORT NameLength;
BOOLEAN BufferAllocated = FALSE;
HCELL_INDEX CellToRelease = HCELL_NIL;
pbuffer = (PKEY_VALUE_INFORMATION)KeyValueInformation;
pcell = (PCELL_DATA) ValueKey;
NameLength = CmpValueNameLen(&pcell->u.KeyValue);
switch (KeyValueInformationClass) {
case KeyValueBasicInformation:
//
// TitleIndex, Type, NameLength, Name
//
requiredlength = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name) +
NameLength;
minimumlength = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name);
*ResultLength = requiredlength;
status = STATUS_SUCCESS;
if (Length < minimumlength) {
status = STATUS_BUFFER_TOO_SMALL;
} else {
pbuffer->KeyValueBasicInformation.TitleIndex = 0;
pbuffer->KeyValueBasicInformation.Type =
pcell->u.KeyValue.Type;
pbuffer->KeyValueBasicInformation.NameLength =
NameLength;
leftlength = Length - minimumlength;
requiredlength = NameLength;
if (leftlength < (LONG)requiredlength) {
requiredlength = leftlength;
status = STATUS_BUFFER_OVERFLOW;
}
if (pcell->u.KeyValue.Flags & VALUE_COMP_NAME) {
CmpCopyCompressedName(pbuffer->KeyValueBasicInformation.Name,
requiredlength,
pcell->u.KeyValue.Name,
pcell->u.KeyValue.NameLength);
} else {
RtlCopyMemory(&(pbuffer->KeyValueBasicInformation.Name[0]),
&(pcell->u.KeyValue.Name[0]),
requiredlength);
}
}
break;
case KeyValueFullInformation:
case KeyValueFullInformationAlign64:
//
// TitleIndex, Type, DataOffset, DataLength, NameLength,
// Name, Data
//
small = CmpIsHKeyValueSmall(realsize, pcell->u.KeyValue.DataLength);
requiredlength = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name) +
NameLength +
realsize;
minimumlength = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name);
if (realsize > 0) {
base = requiredlength - realsize;
#if defined(_WIN64)
offset = ALIGN_OFFSET64(base);
#else
if (KeyValueInformationClass == KeyValueFullInformationAlign64) {
offset = ALIGN_OFFSET64(base);
} else {
offset = ALIGN_OFFSET(base);
}
#endif
if (offset > base) {
requiredlength += (offset - base);
}
#if DBG && defined(_WIN64)
//
// Some clients will have passed in a structure that they "know"
// will be exactly the right size. The fact that alignment
// has changed on NT64 may cause these clients to have problems.
//
// The solution is to fix the client, but print out some debug
// spew here if it looks like this is the case. This problem
// isn't particularly easy to spot from the client end.
//
if((KeyValueInformationClass == KeyValueFullInformation) &&
(Length != minimumlength) &&
(requiredlength > Length) &&
((requiredlength - Length) <=
(ALIGN_OFFSET64(base) - ALIGN_OFFSET(base)))) {
CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"ntos/config-64 KeyValueFullInformation: "
"Possible client buffer size problem.\n"));
CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL," Required size = %d\n", requiredlength));
CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL," Supplied size = %d\n", Length));
}
#endif
}
*ResultLength = requiredlength;
status = STATUS_SUCCESS;
if (Length < minimumlength) {
status = STATUS_BUFFER_TOO_SMALL;
} else {
pbuffer->KeyValueFullInformation.TitleIndex = 0;
pbuffer->KeyValueFullInformation.Type =
pcell->u.KeyValue.Type;
pbuffer->KeyValueFullInformation.DataLength =
realsize;
pbuffer->KeyValueFullInformation.NameLength =
NameLength;
leftlength = Length - minimumlength;
requiredlength = NameLength;
if (leftlength < (LONG)requiredlength) {
requiredlength = leftlength;
status = STATUS_BUFFER_OVERFLOW;
}
if (pcell->u.KeyValue.Flags & VALUE_COMP_NAME) {
CmpCopyCompressedName(pbuffer->KeyValueFullInformation.Name,
requiredlength,
pcell->u.KeyValue.Name,
pcell->u.KeyValue.NameLength);
} else {
RtlCopyMemory(
&(pbuffer->KeyValueFullInformation.Name[0]),
&(pcell->u.KeyValue.Name[0]),
requiredlength
);
}
if (realsize > 0) {
if (small == TRUE) {
datapointer = (PUCHAR)(&(pcell->u.KeyValue.Data));
} else {
if( CmpGetValueDataFromCache(Hive, ContainingList, pcell, ValueCached,&datapointer,&BufferAllocated,&CellToRelease) == FALSE ){
//
// we couldn't map view for cell; treat it as insufficient resources problem
//
ASSERT( datapointer == NULL );
ASSERT( BufferAllocated == FALSE );
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
pbuffer->KeyValueFullInformation.DataOffset = offset;
leftlength = (((LONG)Length - (LONG)offset) < 0) ?
0 :
Length - offset;
requiredlength = realsize;
if (leftlength < (LONG)requiredlength) {
requiredlength = leftlength;
status = STATUS_BUFFER_OVERFLOW;
}
ASSERT((small ? (requiredlength <= CM_KEY_VALUE_SMALL) : TRUE));
if( datapointer != NULL ) {
RtlCopyMemory(
((PUCHAR)pbuffer + offset),
datapointer,
requiredlength
);
if( BufferAllocated == TRUE ) {
ExFreePool(datapointer);
}
if( CellToRelease != HCELL_NIL ) {
HvReleaseCell(Hive,CellToRelease);
}
}
} else {
pbuffer->KeyValueFullInformation.DataOffset = (ULONG)-1;
}
}
break;
case KeyValuePartialInformation:
//
// TitleIndex, Type, DataLength, Data
//
small = CmpIsHKeyValueSmall(realsize, pcell->u.KeyValue.DataLength);
requiredlength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) +
realsize;
minimumlength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
*ResultLength = requiredlength;
status = STATUS_SUCCESS;
if (Length < minimumlength) {
status = STATUS_BUFFER_TOO_SMALL;
} else {
pbuffer->KeyValuePartialInformation.TitleIndex = 0;
pbuffer->KeyValuePartialInformation.Type =
pcell->u.KeyValue.Type;
pbuffer->KeyValuePartialInformation.DataLength =
realsize;
leftlength = Length - minimumlength;
requiredlength = realsize;
if (leftlength < (LONG)requiredlength) {
requiredlength = leftlength;
status = STATUS_BUFFER_OVERFLOW;
}
if (realsize > 0) {
if (small == TRUE) {
datapointer = (PUCHAR)(&(pcell->u.KeyValue.Data));
} else {
if( CmpGetValueDataFromCache(Hive, ContainingList, pcell, ValueCached,&datapointer,&BufferAllocated,&CellToRelease) == FALSE ){
//
// we couldn't map view for cell; treat it as insufficient resources problem
//
ASSERT( datapointer == NULL );
ASSERT( BufferAllocated == FALSE );
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
ASSERT((small ? (requiredlength <= CM_KEY_VALUE_SMALL) : TRUE));
if( datapointer != NULL ) {
RtlCopyMemory((PUCHAR)&(pbuffer->KeyValuePartialInformation.Data[0]),
datapointer,
requiredlength);
if( BufferAllocated == TRUE ) {
ExFreePool(datapointer);
}
if(CellToRelease != HCELL_NIL) {
HvReleaseCell(Hive,CellToRelease);
}
}
}
}
break;
case KeyValuePartialInformationAlign64:
//
// TitleIndex, Type, DataLength, Data
//
small = CmpIsHKeyValueSmall(realsize, pcell->u.KeyValue.DataLength);
requiredlength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION_ALIGN64, Data) +
realsize;
minimumlength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION_ALIGN64, Data);
*ResultLength = requiredlength;
status = STATUS_SUCCESS;
if (Length < minimumlength) {
status = STATUS_BUFFER_TOO_SMALL;
} else {
pbuffer->KeyValuePartialInformationAlign64.Type =
pcell->u.KeyValue.Type;
pbuffer->KeyValuePartialInformationAlign64.DataLength =
realsize;
leftlength = Length - minimumlength;
requiredlength = realsize;
if (leftlength < (LONG)requiredlength) {
requiredlength = leftlength;
status = STATUS_BUFFER_OVERFLOW;
}
if (realsize > 0) {
if (small == TRUE) {
datapointer = (PUCHAR)(&(pcell->u.KeyValue.Data));
} else {
if( CmpGetValueDataFromCache(Hive, ContainingList, pcell, ValueCached,&datapointer,&BufferAllocated,&CellToRelease) == FALSE ){
//
// we couldn't map view for cell; treat it as insufficient resources problem
//
ASSERT( datapointer == NULL );
ASSERT( BufferAllocated == FALSE );
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
ASSERT((small ? (requiredlength <= CM_KEY_VALUE_SMALL) : TRUE));
if( datapointer != NULL ) {
RtlCopyMemory((PUCHAR)&(pbuffer->KeyValuePartialInformationAlign64.Data[0]),
datapointer,
requiredlength);
if( BufferAllocated == TRUE ) {
ExFreePool(datapointer);
}
if(CellToRelease != HCELL_NIL) {
HvReleaseCell(Hive,CellToRelease);
}
}
}
}
break;
default:
status = STATUS_INVALID_PARAMETER;
break;
}
return status;
}