/*++ 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: --*/ { 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: --*/ { NTSTATUS status; PKEY_INFORMATION pbuffer; ULONG requiredlength; USHORT NameLength; 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: --*/ { 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); offset = 0; 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; }