VOID CmpCleanUpKcbValueCache( PCM_KEY_CONTROL_BLOCK KeyControlBlock ) /*++ Routine Description: Clean up cached value/data that are associated to this key. Arguments: KeyControlBlock - pointer to a key control block. Return Value: NONE. --*/ { ULONG i; PULONG_PTR CachedList; PCELL_DATA pcell; ULONG realsize; BOOLEAN small; if (CMP_IS_CELL_CACHED(KeyControlBlock->ValueCache.ValueList)) { CachedList = (PULONG_PTR) CMP_GET_CACHED_CELLDATA(KeyControlBlock->ValueCache.ValueList); for (i = 0; i < KeyControlBlock->ValueCache.Count; i++) { if (CMP_IS_CELL_CACHED(CachedList[i])) { // Trying to catch the BAD guy who writes over our pool. CmpMakeSpecialPoolReadWrite( CMP_GET_CACHED_ADDRESS(CachedList[i]) ); ExFreePool((PVOID) CMP_GET_CACHED_ADDRESS(CachedList[i])); } } // Trying to catch the BAD guy who writes over our pool. CmpMakeSpecialPoolReadWrite( CMP_GET_CACHED_ADDRESS(KeyControlBlock->ValueCache.ValueList) ); ExFreePool((PVOID) CMP_GET_CACHED_ADDRESS(KeyControlBlock->ValueCache.ValueList)); // Mark the ValueList as NULL KeyControlBlock->ValueCache.ValueList = HCELL_NIL; } else if (KeyControlBlock->ExtFlags & CM_KCB_SYM_LINK_FOUND) { // // This is a symbolic link key with symbolic name resolved. // Dereference to its real kcb and clear the bit. // if ((KeyControlBlock->ValueCache.RealKcb->RefCount == 1) && !(KeyControlBlock->ValueCache.RealKcb->Delete)) { KeyControlBlock->ValueCache.RealKcb->ExtFlags |= CM_KCB_NO_DELAY_CLOSE; } CmpDereferenceKeyControlBlockWithLock(KeyControlBlock->ValueCache.RealKcb); KeyControlBlock->ExtFlags &= ~CM_KCB_SYM_LINK_FOUND; } }
VALUE_SEARCH_RETURN_TYPE CmpFindValueByNameFromCache( IN PCM_KEY_CONTROL_BLOCK KeyControlBlock, IN PUNICODE_STRING Name, OUT PPCM_CACHED_VALUE *ContainingList, OUT ULONG *Index, OUT PCM_KEY_VALUE *Value, OUT BOOLEAN *ValueCached, OUT PHCELL_INDEX CellToRelease ) /*++ Routine Description: Find a value node given a value list array and a value name. It sequentially walk through each value node to look for a match. If the array and value nodes touched are not already cached, cache them. Arguments: Hive - pointer to hive control structure for hive of interest ChildList - pointer/index to the Value Index array Name - name of value to find ContainlingList - The address of the entry that will receive the found cached value. Index - pointer to variable to receive index for child ValueCached - Indicate if the value node is cached. Return Value: HCELL_INDEX for the found cell HCELL_NIL if not found Notes: New hives (Minor >= 4) have ValueList sorted; this implies ValueCache is sorted too; So, we can do a binary search here! --*/ { UNICODE_STRING Candidate; LONG Result; PCELL_DATA List; BOOLEAN IndexCached; ULONG Current; HCELL_INDEX ValueListToRelease = HCELL_NIL; ULONG HashKey = 0; PHHIVE Hive = KeyControlBlock->KeyHive; PCACHED_CHILD_LIST ChildList = &(KeyControlBlock->ValueCache); VALUE_SEARCH_RETURN_TYPE ret = SearchFail; ASSERT_KCB_LOCKED(KeyControlBlock); *CellToRelease = HCELL_NIL; *Value = NULL; if (ChildList->Count != 0) { ret = CmpGetValueListFromCache(KeyControlBlock,&List, &IndexCached,&ValueListToRelease); if( ret != SearchSuccess ) { // // retry with exclusive lock, since we need to update the cache // or fail altogether // ASSERT( (ret == SearchFail) || (CmpIsKCBLockedExclusive(KeyControlBlock) == FALSE) ); ASSERT( ValueListToRelease == HCELL_NIL ); return ret; } if( IndexCached ) { try { HashKey = CmpComputeHashKey(0,Name #if DBG , TRUE #endif ); } except (EXCEPTION_EXECUTE_HANDLER) { ret = SearchFail; goto Exit; } } // // old plain hive; simulate a for // Current = 0; while( TRUE ) { if( *CellToRelease != HCELL_NIL ) { HvReleaseCell(Hive,*CellToRelease); *CellToRelease = HCELL_NIL; } ret = CmpGetValueKeyFromCache(KeyControlBlock, List, Current, ContainingList, Value, IndexCached, ValueCached, CellToRelease); if( ret != SearchSuccess ) { // // retry with exclusive lock, since we need to update the cache // or fail altogether // ASSERT( (ret == SearchFail) || (CmpIsKCBLockedExclusive(KeyControlBlock) == FALSE) ); ASSERT( *CellToRelease == HCELL_NIL ); return ret; } // // only compare names when hash matches. // if( IndexCached && (*ValueCached) && (HashKey != ((PCM_CACHED_VALUE)((CMP_GET_CACHED_ADDRESS(**ContainingList))))->HashKey) ) { // // no hash match; skip it // #if DBG try { // // Name has user-mode buffer. // if ((*Value)->Flags & VALUE_COMP_NAME) { Result = CmpCompareCompressedName( Name, (*Value)->Name, (*Value)->NameLength, 0); } else { Candidate.Length = (*Value)->NameLength; Candidate.MaximumLength = Candidate.Length; Candidate.Buffer = (*Value)->Name; Result = RtlCompareUnicodeString( Name, &Candidate, TRUE); } } except (EXCEPTION_EXECUTE_HANDLER) { CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"CmpFindValueByNameFromCache: code:%08lx\n", GetExceptionCode())); // // the caller will bail out. Some ,alicious caller altered the Name buffer since we probed it. // *Value = NULL; ret = SearchFail; goto Exit; } ASSERT( Result != 0 ); #endif Result = 1; } else { try { // // Name has user-mode buffer. // if( (*Value)->Flags & VALUE_COMP_NAME) { Result = CmpCompareCompressedName( Name, (*Value)->Name, (*Value)->NameLength, 0); } else { Candidate.Length = (*Value)->NameLength; Candidate.MaximumLength = Candidate.Length; Candidate.Buffer = (*Value)->Name; Result = RtlCompareUnicodeString( Name, &Candidate, TRUE); } } except (EXCEPTION_EXECUTE_HANDLER) { CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"CmpFindValueByNameFromCache: code:%08lx\n", GetExceptionCode())); // // the caller will bail out. Some ,alicious caller altered the Name buffer since we probed it. // *Value = NULL; ret = SearchFail; goto Exit; } } if (Result == 0) { // // Success, fill the index, return data to caller and exit // *Index = Current; ret = SearchSuccess; goto Exit; } // // compute the next index to try: old'n plain hive; go on // Current++; if( Current == ChildList->Count ) { // // we've reached the end of the list; nicely return // (*Value) = NULL; ret = SearchFail; goto Exit; } } // while(TRUE)
VALUE_SEARCH_RETURN_TYPE CmpGetValueKeyFromCache( IN PCM_KEY_CONTROL_BLOCK KeyControlBlock, IN PCELL_DATA List, IN ULONG Index, OUT PPCM_CACHED_VALUE *ContainingList, OUT PCM_KEY_VALUE *Value, IN BOOLEAN IndexCached, OUT BOOLEAN *ValueCached, OUT PHCELL_INDEX CellToRelease ) /*++ Routine Description: Get the Valve Node. Check if it is already cached, if not but the index is cached, cache and return the value node. Arguments: Hive - pointer to hive control structure for hive of interest. List - pointer to the Value Index Array (of ULONG_PTR if cached and ULONG if non-cached) Index - Index in the Value index array ContainlingList - The address of the entry that will receive the found cached value. IndexCached - Indicate if the index list is cached. If not, everything is from the original registry data. ValueCached - Indicating whether Value is cached or not. Return Value: Pointer to the Value Node. NULL when we couldn't map a view --*/ { PULONG_PTR CachedList; ULONG AllocSize; ULONG CopySize; PCM_CACHED_VALUE CachedValue; PHHIVE Hive; *CellToRelease = HCELL_NIL; Hive = KeyControlBlock->KeyHive; *Value = NULL; if (IndexCached) { // // The index array is cached, so List is pointing to an array of ULONG_PTR. // Use CachedList. // CachedList = (PULONG_PTR) List; *ValueCached = TRUE; if (CMP_IS_CELL_CACHED(CachedList[Index])) { *Value = CMP_GET_CACHED_KEYVALUE(CachedList[Index]); *ContainingList = &((PCM_CACHED_VALUE) CachedList[Index]); } else { // // ensure exclusive lock. // if( (CmpIsKCBLockedExclusive(KeyControlBlock) == FALSE) && (CmpTryConvertKCBLockSharedToExclusive(KeyControlBlock) == FALSE) ) { // // need to upgrade lock to exclusive // return SearchNeedExclusiveLock; } *Value = (PCM_KEY_VALUE) HvGetCell(Hive, List->u.KeyList[Index]); if( *Value == NULL ) { // // we couldn't map a view for this cell // just return NULL; the caller must handle it gracefully // return SearchFail; } *CellToRelease = List->u.KeyList[Index]; // // Allocate a PagedPool to cache the value node. // CopySize = (ULONG) HvGetCellSize(Hive, *Value); AllocSize = CopySize + FIELD_OFFSET(CM_CACHED_VALUE, KeyValue); CachedValue = (PCM_CACHED_VALUE) ExAllocatePoolWithTag(PagedPool, AllocSize, CM_CACHE_VALUE_TAG); if (CachedValue) { // // Set the information for later use if we need to cache data as well. // if ((*Value)->Flags & VALUE_COMP_NAME) { CachedValue->HashKey = CmpComputeHashKeyForCompressedName(0,(*Value)->Name,(*Value)->NameLength); } else { UNICODE_STRING TmpStr; TmpStr.Length = (*Value)->NameLength; TmpStr.Buffer = (*Value)->Name; CachedValue->HashKey = CmpComputeHashKey(0,&TmpStr #if DBG , TRUE #endif ); } CachedValue->DataCacheType = CM_CACHE_DATA_NOT_CACHED; CachedValue->ValueKeySize = (USHORT) CopySize; RtlCopyMemory((PVOID)&(CachedValue->KeyValue), *Value, CopySize); // Trying to catch the BAD guy who writes over our pool. CmpMakeSpecialPoolReadWrite( CMP_GET_CACHED_ADDRESS(CachedList) ); CachedList[Index] = CMP_MARK_CELL_CACHED(CachedValue); // Trying to catch the BAD guy who writes over our pool. CmpMakeSpecialPoolReadOnly( CMP_GET_CACHED_ADDRESS(CachedList) ); // Trying to catch the BAD guy who writes over our pool. CmpMakeSpecialPoolReadOnly(CachedValue); *ContainingList = &((PCM_CACHED_VALUE) CachedList[Index]); // // Now we have the stuff cached, use the cache data. // (*Value) = CMP_GET_CACHED_KEYVALUE(CachedValue); } else { // // If the allocation fails, just do not cache it. continue. // *ValueCached = FALSE; } } } else { // // The Valve Index Array is from the registry hive, just get the cell and move on. // (*Value) = (PCM_KEY_VALUE) HvGetCell(Hive, List->u.KeyList[Index]); *ValueCached = FALSE; if( *Value == NULL ) { // // we couldn't map a view for this cell // just return NULL; the caller must handle it gracefully // OBS: we may remove this as we return pchild anyway; just for clarity // return SearchFail; } *CellToRelease = List->u.KeyList[Index]; } return SearchSuccess; }