/*++ 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" PUCHAR CmpGetValueDataFromCache( IN PHHIVE Hive, IN PPCM_CACHED_VALUE ContainingList, IN PCELL_DATA ValueKey, IN BOOLEAN ValueCached ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE,CmpGetValueDataFromCache) #pragma alloc_text(PAGE,CmpFreeValue) #pragma alloc_text(PAGE,CmpQueryKeyData) #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))) VOID CmpFreeValue( PHHIVE Hive, HCELL_INDEX Cell ) /*++ Routine Description: Free the value entry Hive.Cell refers to, including its name and data cells. Arguments: Hive - supplies a pointer to the hive control structure for the hive Cell - supplies index of value to delete Return Value: NTSTATUS - Result code from call, among the following: --*/ { PCELL_DATA value; ULONG realsize; BOOLEAN small; // map in the cell value = HvGetCell(Hive, Cell); // free data if present small = CmpIsHKeyValueSmall(realsize, value->u.KeyValue.DataLength); if ((! small) && (realsize > 0)) { HvFreeCell(Hive, value->u.KeyValue.Data); } // free the cell itself HvFreeCell(Hive, Cell); return; } // Data transfer workers NTSTATUS CmpQueryKeyData( PHHIVE Hive, PCM_KEY_NODE Node, 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. 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; pbuffer = (PKEY_INFORMATION)KeyInformation; NameLength = CmpHKeyNameLen(Node); switch (KeyInformationClass) { case KeyBasicInformation: // 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: // 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); 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; } RtlMoveMemory( pbuffer, pclass, requiredlength ); } else { pbuffer->KeyNodeInformation.ClassOffset = (ULONG)-1; } } break; case KeyFullInformation: // 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); leftlength = Length - minimumlength; requiredlength = Node->ClassLength; if (leftlength < (LONG)requiredlength) { requiredlength = leftlength; status = STATUS_BUFFER_OVERFLOW; } RtlMoveMemory( &(pbuffer->KeyFullInformation.Class[0]), pclass, requiredlength ); } 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; } return status; } PUCHAR CmpGetValueDataFromCache( IN PHHIVE Hive, IN PPCM_CACHED_VALUE ContainingList, IN PCELL_DATA ValueKey, IN BOOLEAN ValueCached ) /*++ 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. Return Value: Pointer to the Valve Index. --*/ { // Cache the data if needed. PCM_CACHED_VALUE OldEntry; PCM_CACHED_VALUE NewEntry; PUCHAR datapointer; PUCHAR Cacheddatapointer; ULONG AllocSize; ULONG CopySize; ULONG DataSize; 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) { // Data is too big to warrent caching, get it from the registry. datapointer = (PUCHAR)HvGetCell(Hive, ValueKey->u.KeyValue.Data); } 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. datapointer = (PUCHAR)HvGetCell(Hive, ValueKey->u.KeyValue.Data); DataSize = (ULONG) HvGetCellSize(Hive, datapointer); if (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 { // Trying to catch the BAD guy who writes over our pool. CmpMakeSpecialPoolReadWrite( OldEntry ); // Mark the type and do not cache it. OldEntry->DataCacheType = CM_CACHE_DATA_TOO_BIG; // Trying to catch the BAD guy who writes over our pool. CmpMakeSpecialPoolReadOnly( OldEntry ); } } } else { datapointer = (PUCHAR)HvGetCell(Hive, ValueKey->u.KeyValue.Data); } return (datapointer); } 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; 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)))) { KdPrint(("ntos/config-64 KeyValueFullInformation: " "Possible client buffer size problem.\n")); KdPrint((" Required size = %d\n", requiredlength)); KdPrint((" 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 { RtlMoveMemory( &(pbuffer->KeyValueFullInformation.Name[0]), &(pcell->u.KeyValue.Name[0]), requiredlength ); } if (realsize > 0) { if (small == TRUE) { datapointer = (PUCHAR)(&(pcell->u.KeyValue.Data)); } else { datapointer = CmpGetValueDataFromCache(Hive, ContainingList, pcell, ValueCached); } 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)); RtlMoveMemory( ((PUCHAR)pbuffer + offset), datapointer, requiredlength ); } 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 { datapointer = CmpGetValueDataFromCache(Hive, ContainingList, pcell, ValueCached); } ASSERT((small ? (requiredlength <= CM_KEY_VALUE_SMALL) : TRUE)); RtlMoveMemory((PUCHAR)&(pbuffer->KeyValuePartialInformation.Data[0]), datapointer, requiredlength); } } 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 { datapointer = CmpGetValueDataFromCache(Hive, ContainingList, pcell, ValueCached); } ASSERT((small ? (requiredlength <= CM_KEY_VALUE_SMALL) : TRUE)); RtlMoveMemory((PUCHAR)&(pbuffer->KeyValuePartialInformationAlign64.Data[0]), datapointer, requiredlength); } } break; default: status = STATUS_INVALID_PARAMETER; break; } return status; }