/*++ Copyright (c) 1999 Microsoft Corporation Module Name: cmvalue.c Abstract: This module contains cm routines for operating on (sorted) value list. Insertion, Deletion,Searching ... Routines to deal with a KeyValue data; whether it is small, big - new hives format - , or normal Author: Dragos C. Sambotin (dragoss) 12-Aug-1999 Revision History: --*/ #include "cmp.h" #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE,CmpFindValueByName) #pragma alloc_text(PAGE,CmpFindNameInList) #pragma alloc_text(PAGE,CmpAddValueToList) #pragma alloc_text(PAGE,CmpRemoveValueFromList) #pragma alloc_text(PAGE,CmpGetValueData) #pragma alloc_text(PAGE,CmpMarkValueDataDirty) #pragma alloc_text(PAGE,CmpFreeValue) #pragma alloc_text(PAGE,CmpSetValueDataNew) #pragma alloc_text(PAGE,CmpSetValueDataExisting) #pragma alloc_text(PAGE,CmpFreeValueData) #pragma alloc_text(PAGE,CmpValueToData) #endif HCELL_INDEX CmpFindValueByName( PHHIVE Hive, PCM_KEY_NODE KeyNode, PUNICODE_STRING Name ) /*++ Routine Description: Underlying CmpFindNameInList was changed to return an error code; Had to make it a function instead of a macro Arguments: Hive - pointer to hive control structure for hive of interest Return Value: HCELL_INDEX or HCELL_NIL on error --*/ { HCELL_INDEX CellIndex; #ifndef _CM_LDR_ PAGED_CODE(); #endif //_CM_LDR_ if( CmpFindNameInList(Hive,&((KeyNode)->ValueList),Name,NULL,&CellIndex) == FALSE ) { // // above should set this right // ASSERT( CellIndex == HCELL_NIL ); } return CellIndex; } BOOLEAN CmpFindNameInList( IN PHHIVE Hive, IN PCHILD_LIST ChildList, IN PUNICODE_STRING Name, IN OPTIONAL PULONG ChildIndex, OUT PHCELL_INDEX CellIndex ) /*++ Routine Description: Find a child object in an object list. Child List must be sorted based on the name. (for new hives format) Arguments: Hive - pointer to hive control structure for hive of interest List - pointer to mapped in list structure Count - number of elements in list structure Name - name of child object to find ChildIndex - pointer to variable to receive index for child; CellIndex - pointer to receive the index of the child. On return, this is: HCELL_INDEX for the found cell HCELL_NIL if not found Return Value: TRUE - success FALSE - error, insufficient resources Notes: ChildIndex is always filled with the position where Name should be in the list. The difference whether Name is in the list or not is made upon CellIndex - CellIndex == HCELL_NIL ==> Name not found in the list - CellIndex <> HCELL_NIL ==> Name already exists in the list --*/ { PCM_KEY_VALUE pchild; UNICODE_STRING Candidate; LONG Result; PCELL_DATA List = NULL; ULONG Current; HCELL_INDEX CellToRelease = HCELL_NIL; BOOLEAN ReturnValue = FALSE; #ifndef _CM_LDR_ PAGED_CODE(); #endif //_CM_LDR_ if (ChildList->Count != 0) { List = (PCELL_DATA)HvGetCell(Hive,ChildList->List); if( List == NULL ) { // // we could not map the view containing the cell // *CellIndex = HCELL_NIL; return FALSE; } // // old plain hive; simulate a for // Current = 0; while( TRUE ) { if( CellToRelease != HCELL_NIL ) { HvReleaseCell(Hive,CellToRelease); CellToRelease = HCELL_NIL; } pchild = (PCM_KEY_VALUE)HvGetCell(Hive, List->u.KeyList[Current]); if( pchild == NULL ) { // // we could not map the view containing the cell // *CellIndex = HCELL_NIL; ReturnValue = FALSE; goto JustReturn; } CellToRelease = List->u.KeyList[Current]; if (pchild->Flags & VALUE_COMP_NAME) { Result = CmpCompareCompressedName(Name, pchild->Name, pchild->NameLength, 0); } else { Candidate.Length = pchild->NameLength; Candidate.MaximumLength = Candidate.Length; Candidate.Buffer = pchild->Name; Result = RtlCompareUnicodeString(Name, &Candidate, TRUE); } if (Result == 0) { // // Success, return data to caller and exit // if (ARGUMENT_PRESENT(ChildIndex)) { *ChildIndex = Current; } *CellIndex = List->u.KeyList[Current]; ReturnValue = TRUE; goto JustReturn; } // // 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 // if (ARGUMENT_PRESENT(ChildIndex)) { *ChildIndex = Current; } // // nicely return // *CellIndex = HCELL_NIL; ReturnValue = TRUE; goto JustReturn; } } } // // in the new design we shouldn't get here; we should exit the while loop with return // ASSERT( ChildList->Count == 0 ); // add it first; as it's the only one if (ARGUMENT_PRESENT(ChildIndex)) { *ChildIndex = 0; } *CellIndex = HCELL_NIL; return TRUE; JustReturn: if( List != NULL ) { HvReleaseCell(Hive,ChildList->List); } if( CellToRelease != HCELL_NIL ) { HvReleaseCell(Hive,CellToRelease); } return ReturnValue; } BOOLEAN CmpGetValueData(IN PHHIVE Hive, IN PCM_KEY_VALUE Value, OUT PULONG realsize, IN OUT PVOID *Buffer, OUT PBOOLEAN Allocated, OUT PHCELL_INDEX CellToRelease ) /*++ Routine Description: Retrieves the real valueData, given the key value. Arguments: Hive - pointer to hive control structure for hive of interest Value - CM_KEY_VALUE to retrieve the data for. realsize - the actual size of the data (in bytes) Buffer - pointer to the data; if the cell is a BIG_CELL we should allocate a buffer Allocated - here we signal the caller that he has to free the buffer on return; TRUE - a new buffer was allocated to gather together the BIG_CELL data FALSE - Buffer points directly in the hive, the caller shouldn't free it CellToRelease - Cell to release after finishing work with Buffer Return Value: TRUE - success FALSE - not enough resources available; (to map a cell or to allocate the buffer) Notes: The caller is responsible to remove the buffer, when Allocated is set on TRUE on return; --*/ { #ifndef _CM_LDR_ PAGED_CODE(); #endif //_CM_LDR_ ASSERT_KEY_VALUE(Value); // // normally we don't allocate buffer // *Allocated = FALSE; *Buffer = NULL; *CellToRelease = HCELL_NIL; // // check for small values // if( CmpIsHKeyValueSmall(*realsize, Value->DataLength) == TRUE ) { // // data is stored inside the cell // *Buffer = &Value->Data; return TRUE; } #ifndef _CM_LDR_ // // check for big values // if( CmpIsHKeyValueBig(Hive,*realsize) == TRUE ) { // // // PCM_BIG_DATA BigData = NULL; PUCHAR WorkBuffer = NULL; ULONG Length; USHORT i; PUCHAR PartialData; PHCELL_INDEX Plist = NULL; BOOLEAN bRet = TRUE; #ifndef _CM_LDR_ try { #endif //_CM_LDR_ BigData = (PCM_BIG_DATA)HvGetCell(Hive,Value->Data); if( BigData == NULL ) { // // cannot map view containing the cell; bail out // bRet = FALSE; #ifndef _CM_LDR_ leave; #else return bRet; #endif //_CM_LDR_ } ASSERT_BIG_DATA(BigData); Plist = (PHCELL_INDEX)HvGetCell(Hive,BigData->List); if( Plist == NULL ) { // // cannot map view containing the cell; bail out // bRet = FALSE; #ifndef _CM_LDR_ leave; #else return bRet; #endif //_CM_LDR_ } Length = Value->DataLength; // // sanity check // ASSERT( Length <= (ULONG)(BigData->Count * CM_KEY_VALUE_BIG) ); // // allocate a buffer to merge bring all the pieces together // WorkBuffer = (PUCHAR)ExAllocatePoolWithTag(PagedPool, Length, CM_POOL_TAG); if( WorkBuffer == NULL ){ bRet = FALSE; #ifndef _CM_LDR_ leave; #else return bRet; #endif //_CM_LDR_ } for(i=0;iCount;i++) { // // sanity check // ASSERT( Length > 0 ); PartialData = (PUCHAR)HvGetCell(Hive,Plist[i]); if( PartialData == NULL ){ // // cannot map view containing the cell; bail out // ExFreePool(WorkBuffer); bRet = FALSE; #ifndef _CM_LDR_ leave; #else return bRet; #endif //_CM_LDR_ } // // copy this piece of data to the work buffer // RtlCopyMemory(WorkBuffer + CM_KEY_VALUE_BIG*i,PartialData,(Length>CM_KEY_VALUE_BIG)?CM_KEY_VALUE_BIG:Length); HvReleaseCell(Hive,Plist[i]); // // adjust the data still to copy. // All cells in Plist should be of size CM_KEY_VALUE_BIG, except the last one, which is the remaining // Length -= CM_KEY_VALUE_BIG; } #ifndef _CM_LDR_ } finally { if( BigData != NULL ) { HvReleaseCell(Hive,Value->Data); if( Plist != NULL ) { HvReleaseCell(Hive,BigData->List); } } } #endif //_CM_LDR_ if( !bRet ) { return FALSE; } // // if we are here; we successfuly have copied all data into WorkBuffer. // update the return buffer and return; Caller is responsible to free the return buffer // We signal the caller by setting Allocated on TRUE // *Buffer = WorkBuffer; *Allocated = TRUE; return TRUE; } #endif //_CM_LDR_ // // normal, old plain case // *Buffer = HvGetCell(Hive,Value->Data); if( *Buffer == NULL ) { // // insufficient resources to map the view containing this cell // return FALSE; } // // signal to the caller to release this cell after finishing with buffer // *CellToRelease = Value->Data; return TRUE; } PCELL_DATA CmpValueToData(IN PHHIVE Hive, IN PCM_KEY_VALUE Value, OUT PULONG realsize ) /*++ Routine Description: Retrieves the real valueData, given the key value. Arguments: Hive - pointer to hive control structure for hive of interest Value - CM_KEY_VALUE to retrieve the data for. realsize - the actual size of the data (in bytes) Return Value: pointer to the value data; NULL if any error (insuficient resources) Notes: This function doesn't support big cells; It is intended to be called just by the loader, which doesn't store large data. It'll bugcheck if big cell is queried. --*/ { PCELL_DATA Buffer; BOOLEAN BufferAllocated; HCELL_INDEX CellToRelease; #ifndef _CM_LDR_ PAGED_CODE(); #endif //_CM_LDR_ ASSERT( Hive->ReleaseCellRoutine == NULL ); if( CmpGetValueData(Hive,Value,realsize,&Buffer,&BufferAllocated,&CellToRelease) == FALSE ) { // // insufficient resources; return NULL // ASSERT( BufferAllocated == FALSE ); ASSERT( Buffer == NULL ); return NULL; } // // we specificallly ignore CellToRelease as this is not a mapped view // if( BufferAllocated == TRUE ) { // // this function is not intended for big cells; // #ifndef _CM_LDR_ ExFreePool( Buffer ); #endif //_CM_LDR_ CM_BUGCHECK( REGISTRY_ERROR,BIG_CELL_ERROR,0,Hive,Value); #ifdef _CM_LDR_ return NULL; #endif } // // success // return Buffer; } #ifndef _CM_LDR_ NTSTATUS CmpAddValueToList( IN PHHIVE Hive, IN HCELL_INDEX ValueCell, IN ULONG Index, IN ULONG Type, IN OUT PCHILD_LIST ChildList ) /*++ Routine Description: Adds a value to the value list, keeping the list sorted (for new hives format) Arguments: Hive - pointer to hive control structure for hive of interest ValueCell - value index Index - index at which to add the value ChildList - pointer to the list of values Return Value: STATUS_SUCCESS - success STATUS_INSUFFICIENT_RESOURCES - an error occured --*/ { HCELL_INDEX NewCell; ULONG count; ULONG AllocateSize; ULONG i; PCELL_DATA pdata; PAGED_CODE(); // // we have the lock exclusive or nobody is operating inside this hive // //ASSERT_CM_LOCK_OWNED_EXCLUSIVE(); ASSERT_CM_EXCLUSIVE_HIVE_ACCESS(Hive); // // sanity check for index range // ASSERT( (((LONG)Index) >= 0) && (Index <= ChildList->Count) ); count = ChildList->Count; count++; if (count > 1) { ASSERT_CELL_DIRTY(Hive,ChildList->List); if (count < CM_MAX_REASONABLE_VALUES) { // // A reasonable number of values, allocate just enough // space. // AllocateSize = count * sizeof(HCELL_INDEX); } else { // // An excessive number of values, pad the allocation out // to avoid fragmentation. (if there's this many values, // there'll probably be more pretty soon) // AllocateSize = ROUND_UP(count, CM_MAX_REASONABLE_VALUES) * sizeof(HCELL_INDEX); if (AllocateSize > HBLOCK_SIZE) { AllocateSize = ROUND_UP(AllocateSize, HBLOCK_SIZE); } } NewCell = HvReallocateCell( Hive, ChildList->List, AllocateSize ); } else { NewCell = HvAllocateCell(Hive, sizeof(HCELL_INDEX), Type,ValueCell); } // // put ourselves on the list // if (NewCell != HCELL_NIL) { // sanity ChildList->List = NewCell; pdata = HvGetCell(Hive, NewCell); if( pdata == NULL ) { // // we couldn't map a view for the bin containing this cell // // // normally this shouldn't happen as we just allocated ValueCell // i.e. the bin containing NewCell should be mapped in memory at this point. // ASSERT( FALSE ); return STATUS_INSUFFICIENT_RESOURCES; } // // make room for the new cell; move values in the reverse order ! // adding at the end makes this a nop // for( i = count - 1; i > Index; i-- ) { pdata->u.KeyList[i] = pdata->u.KeyList[i-1]; } pdata->u.KeyList[Index] = ValueCell; ChildList->Count = count; HvReleaseCell(Hive,NewCell); // sanity ASSERT_CELL_DIRTY(Hive,ValueCell); } else { return STATUS_INSUFFICIENT_RESOURCES; } return STATUS_SUCCESS; } NTSTATUS CmpRemoveValueFromList( IN PHHIVE Hive, IN ULONG Index, IN OUT PCHILD_LIST ChildList ) /*++ Routine Description: Removes the value at the specified index from the value list Arguments: Hive - pointer to hive control structure for hive of interest Index - index at which to add the value ChildList - pointer to the list of values Return Value: STATUS_SUCCESS - success STATUS_INSUFFICIENT_RESOURCES - an error occured Notes: The caller is responsible for freeing the removed value --*/ { ULONG newcount; HCELL_INDEX newcell; PAGED_CODE(); ASSERT_CM_LOCK_OWNED_EXCLUSIVE(); // // sanity check for index range // ASSERT( (((LONG)Index) >= 0) && (Index <= ChildList->Count) ); newcount = ChildList->Count - 1; if (newcount > 0) { PCELL_DATA pvector; // // more than one entry list, squeeze // pvector = HvGetCell(Hive, ChildList->List); if( pvector == NULL ) { // // we couldn't map a view for the bin containing this cell // return STATUS_INSUFFICIENT_RESOURCES; } // release the cell here as the reglock is held exclusive HvReleaseCell(Hive,ChildList->List); // sanity ASSERT_CELL_DIRTY(Hive,ChildList->List); ASSERT_CELL_DIRTY(Hive,pvector->u.KeyList[Index]); for ( ; Index < newcount; Index++) { pvector->u.KeyList[ Index ] = pvector->u.KeyList[ Index + 1 ]; } newcell = HvReallocateCell( Hive, ChildList->List, newcount * sizeof(HCELL_INDEX) ); ASSERT(newcell != HCELL_NIL); ChildList->List = newcell; } else { // // list is empty, free it // HvFreeCell(Hive, ChildList->List); ChildList->List = HCELL_NIL; } ChildList->Count = newcount; return STATUS_SUCCESS; } BOOLEAN CmpMarkValueDataDirty( IN PHHIVE Hive, IN PCM_KEY_VALUE Value ) /*++ Routine Description: Marks the cell(s) storing the value data as dirty; Knows how to deal with bigcells Arguments: Hive - pointer to hive control structure for hive of interest Value - CM_KEY_VALUE to retrieve the data for. Return Value: TRUE - success FALSE - failure to mark all the cells involved; --*/ { ULONG realsize; PAGED_CODE(); // // we have the lock exclusive or nobody is operating inside this hive // //ASSERT_CM_LOCK_OWNED_EXCLUSIVE(); ASSERT_CM_EXCLUSIVE_HIVE_ACCESS(Hive); ASSERT_KEY_VALUE(Value); if( Value->Data != HCELL_NIL ) { // // Could be that value was just partially initialized (CmpSetValueKeyNew case) // // // check for small values // if( CmpIsHKeyValueSmall(realsize, Value->DataLength) == TRUE ) { // // data is stored inside the cell // return TRUE; } // // check for big values // if( CmpIsHKeyValueBig(Hive,realsize) == TRUE ) { // // // PCM_BIG_DATA BigData; PHCELL_INDEX Plist; USHORT i; BigData = (PCM_BIG_DATA)HvGetCell(Hive,Value->Data); if( BigData == NULL ) { // // cannot map view containing the cell; bail out // return FALSE; } ASSERT_BIG_DATA(BigData); if( BigData->List != HCELL_NIL ) { Plist = (PHCELL_INDEX)HvGetCell(Hive,BigData->List); if( Plist == NULL ) { // // cannot map view containing the cell; bail out // HvReleaseCell(Hive,Value->Data); return FALSE; } for(i=0;iCount;i++) { // // mark this chunk dirty // if( Plist[i] != HCELL_NIL ) { if (! HvMarkCellDirty(Hive, Plist[i])) { HvReleaseCell(Hive,Value->Data); HvReleaseCell(Hive,BigData->List); return FALSE; } } } // // mark the list as dirty // if (! HvMarkCellDirty(Hive, BigData->List)) { HvReleaseCell(Hive,Value->Data); HvReleaseCell(Hive,BigData->List); return FALSE; } // // we can safely remove it here as it is now dirty/pinned // HvReleaseCell(Hive,BigData->List); } // // we don't need this cell anymore // HvReleaseCell(Hive,Value->Data); // // fall through to mark the cell itself as dirty // } // // Data is a HCELL_INDEX; mark it dirty // if (! HvMarkCellDirty(Hive, Value->Data)) { return FALSE; } } return TRUE; } BOOLEAN CmpFreeValueData( PHHIVE Hive, HCELL_INDEX DataCell, ULONG DataLength ) /*++ Routine Description: Free the Value Data DataCell carries with. Arguments: Hive - supplies a pointer to the hive control structure for the hive DataCell - supplies index of value who's data to free DataLength - length of the data; used to detect the type of the cell Return Value: TRUE: Success FALSE: Error Notes: Knows how to deal with big cell(s) --*/ { ULONG realsize; PAGED_CODE(); ASSERT_CM_LOCK_OWNED_EXCLUSIVE(); // // check for small values // if( CmpIsHKeyValueSmall(realsize, DataLength) == TRUE ) { // // data is stored inside the cell; this is a nop // } else { // // Could be that value was just partially initialized (CmpSetValueKeyNew case) // if( DataCell == HCELL_NIL ) { return TRUE; } ASSERT(HvIsCellAllocated(Hive,DataCell)); // // check for big values // if( CmpIsHKeyValueBig(Hive,realsize) == TRUE ) { // // // PCM_BIG_DATA BigData; PHCELL_INDEX Plist; USHORT i; BigData = (PCM_BIG_DATA)HvGetCell(Hive,DataCell); if( BigData == NULL ) { // // cannot map view containing the cell; bail out // // This shouldn't happen as this cell is marked ditry by // this time (i.e. its view is pinned in memory) // ASSERT( FALSE ); return FALSE; } // release the cell here as the reglock is held exclusive HvReleaseCell(Hive,DataCell); ASSERT_BIG_DATA(BigData); if( BigData->List != HCELL_NIL ) { Plist = (PHCELL_INDEX)HvGetCell(Hive,BigData->List); if( Plist == NULL ) { // // cannot map view containing the cell; bail out // // // This shouldn't happen as this cell is marked ditry by // this time (i.e. its view is pinned in memory) // ASSERT( FALSE ); return FALSE; } // release the cell here as the reglock is held exclusive HvReleaseCell(Hive,BigData->List); for(i=0;iCount;i++) { // // mark this chunk dirty // if( Plist[i] != HCELL_NIL ) { HvFreeCell(Hive, Plist[i]); } } // // mark the list as dirty // HvFreeCell(Hive, BigData->List); } // // fall through to free the cell data itself // } // // normal case free the Data cell // HvFreeCell(Hive, DataCell); } return TRUE; } BOOLEAN 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: TRUE: Success FALSE: Error --*/ { PCM_KEY_VALUE Value; PAGED_CODE(); ASSERT_CM_LOCK_OWNED_EXCLUSIVE(); // // map in the cell // Value = (PCM_KEY_VALUE)HvGetCell(Hive, Cell); if( Value == NULL ) { // // we couldn't map the bin containing this cell // sorry we cannot free value // // This shouldn't happen as the value is marked ditry by // this time (i.e. its view is pinned in memory) // ASSERT( FALSE ); return FALSE; } // release the cell here as the reglock is held exclusive HvReleaseCell(Hive,Cell); if( CmpFreeValueData(Hive,Value->Data,Value->DataLength) == FALSE ) { return FALSE; } // // free the cell itself // HvFreeCell(Hive, Cell); return TRUE; } NTSTATUS CmpSetValueDataNew( IN PHHIVE Hive, IN PVOID Data, IN ULONG DataSize, IN ULONG StorageType, IN HCELL_INDEX ValueCell, OUT PHCELL_INDEX DataCell ) /*++ Routine Description: Allocates a new cell (or big data cell) to accomodate DataSize; Initialize and copy information from Data to the new cell; Arguments: Hive - supplies a pointer to the hive control structure for the hive Data - data buffer (possibly from user-mode) DataSize - size of the buffer StorageType - Stable or Volatile ValueCell - The value setting the data for (locality purposes). DataCell - return value:HCELL_INDEX of the new cell; HCELL_NIL on some error Return Value: Status of the operation (STATUS_SUCCESS or the exception code - if any) Notes: Knows how to deal with big cell(s) Data buffer comes from user mode, so it should be guarded by a try-except --*/ { PCELL_DATA pdata; PAGED_CODE(); ASSERT_CM_EXCLUSIVE_HIVE_ACCESS(Hive); // // bogus args; we don't deal with small values here! // ASSERT(DataSize > CM_KEY_VALUE_SMALL); if( CmpIsHKeyValueBig(Hive,DataSize) == TRUE ) { // // request for a big data value // PCM_BIG_DATA BigData = NULL; USHORT Count; PHCELL_INDEX Plist = NULL; NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES; // // allocate the embedding cell // *DataCell = HvAllocateCell(Hive, sizeof(CM_BIG_DATA), StorageType,ValueCell); if (*DataCell == HCELL_NIL) { return status; } // // init the BIG_DATA cell // BigData = (PCM_BIG_DATA)HvGetCell(Hive,*DataCell); if( BigData == NULL) { // // couldn't map view for this cell // this shouldn't happen as we just allocated this cell // (i.e. its view should be pinned in memory) // ASSERT( FALSE ); goto Cleanup; } // release the cell here as the reglock is held exclusive HvReleaseCell(Hive,*DataCell); BigData->Signature = CM_BIG_DATA_SIGNATURE; BigData->Count = 0; BigData->List = HCELL_NIL; // // Compute the number of cells needed // Count = (USHORT)((DataSize + CM_KEY_VALUE_BIG - 1) / CM_KEY_VALUE_BIG); // // allocate the embeded list // BigData->List = HvAllocateCell(Hive, Count * sizeof(HCELL_INDEX), StorageType,*DataCell); if( BigData->List == HCELL_NIL ) { goto Cleanup; } Plist = (PHCELL_INDEX)HvGetCell(Hive,BigData->List); if( Plist == NULL ) { // // cannot map view containing the cell; bail out // // // This shouldn't happen as this cell is marked ditry by // this time (i.e. its view is pinned in memory) // ASSERT( FALSE ); goto Cleanup; } // release the cell here as the reglock is held exclusive HvReleaseCell(Hive,BigData->List); // // allocate each chunk and copy the data; if we fail part through, we'll free the already allocated values // for( ;BigData->Count < Count;(BigData->Count)++) { // // allocate this chunk // Plist[BigData->Count] = HvAllocateCell(Hive, CM_KEY_VALUE_BIG, StorageType,BigData->List); if( Plist[BigData->Count] == HCELL_NIL ) { goto Cleanup; } pdata = HvGetCell(Hive,Plist[BigData->Count]); if( pdata == NULL ) { // // cannot map view containing the cell; bail out // // // This shouldn't happen as this cell is marked ditry by // this time (i.e. its view is pinned in memory) // ASSERT( FALSE ); goto Cleanup; } // release the cell here as the reglock is held exclusive HvReleaseCell(Hive,Plist[BigData->Count]); // // now, copy this chunk data // try { RtlCopyMemory(pdata, (PUCHAR)Data, (DataSize>CM_KEY_VALUE_BIG)?CM_KEY_VALUE_BIG:DataSize); } except (EXCEPTION_EXECUTE_HANDLER) { CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!CmpSetValueDataNew: code:%08lx\n", GetExceptionCode())); status = GetExceptionCode(); goto Cleanup; } // // update the data pointer and the remaining size // Data = (PVOID)((PCHAR)Data + CM_KEY_VALUE_BIG); DataSize -= CM_KEY_VALUE_BIG; } ASSERT( Count == BigData->Count ); return STATUS_SUCCESS; Cleanup: // // free what we already allocated // if( BigData != NULL) { if( Plist != NULL ) { for(;BigData->Count;BigData->Count--) { if( Plist[BigData->Count] != HCELL_NIL ) { HvFreeCell(Hive, Plist[BigData->Count]); } } } else { ASSERT( BigData->Count == 0 ); } if( BigData->List != HCELL_NIL ) { HvFreeCell(Hive, BigData->List); } } HvFreeCell(Hive, *DataCell); *DataCell = HCELL_NIL; return status; } else { // // normal old'n plain value // *DataCell = HvAllocateCell(Hive, DataSize, StorageType,ValueCell); if (*DataCell == HCELL_NIL) { return STATUS_INSUFFICIENT_RESOURCES; } pdata = HvGetCell(Hive, *DataCell); if( pdata == NULL ) { // // we couldn't map a view for the bin containing this cell // // // normally this shouldn't happen as we just allocated ValueCell // i.e. the bin containing DataCell should be mapped in memory at this point. // ASSERT( FALSE ); if (*DataCell != HCELL_NIL) { HvFreeCell(Hive, *DataCell); *DataCell = HCELL_NIL; } return STATUS_INSUFFICIENT_RESOURCES; } // release the cell here as the reglock is held exclusive HvReleaseCell(Hive,*DataCell); // // copy the actual data, guarding the buffer as it may be a user-mode buffer // try { RtlCopyMemory(pdata, Data, DataSize); } except (EXCEPTION_EXECUTE_HANDLER) { CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!CmpSetValueDataNew: code:%08lx\n", GetExceptionCode())); // // We have bombed out loading user data, clean up and exit. // if (*DataCell != HCELL_NIL) { HvFreeCell(Hive, *DataCell); *DataCell = HCELL_NIL; } return GetExceptionCode(); } } return STATUS_SUCCESS; } NTSTATUS CmpSetValueDataExisting( IN PHHIVE Hive, IN PVOID Data, IN ULONG DataSize, IN ULONG StorageType, IN HCELL_INDEX OldDataCell ) /*++ Routine Description: Grows an existing big data cell and copies the new data into it. Arguments: Hive - supplies a pointer to the hive control structure for the hive Data - data buffer (possibly from user-mode) DataSize - size of the buffer StorageType - Stable or Volatile OldDataCell - old big data cell NewDataCell - return value:HCELL_INDEX of the new cell; HCELL_NIL on some error Return Value: Status of the operation (STATUS_SUCCESS or the exception code - if any) Notes: Knows how to deal with big cell(s) Data buffer is secured by the time this function is called --*/ { PCELL_DATA pdata; PCM_BIG_DATA BigData = NULL; USHORT NewCount,i; PHCELL_INDEX Plist = NULL; HCELL_INDEX NewList; PAGED_CODE(); ASSERT_CM_LOCK_OWNED_EXCLUSIVE(); // // bogus args; we deal only with big data cells! // ASSERT(DataSize > CM_KEY_VALUE_BIG ); BigData = (PCM_BIG_DATA)HvGetCell(Hive,OldDataCell); if( BigData == NULL) { // // couldn't map view for this cell // this shouldn't happen as we just marked it as dirty // (i.e. its view should be pinned in memory) // ASSERT( FALSE ); return STATUS_INSUFFICIENT_RESOURCES; } // release the cell here as the reglock is held exclusive HvReleaseCell(Hive,OldDataCell); ASSERT_BIG_DATA(BigData); Plist = (PHCELL_INDEX)HvGetCell(Hive,BigData->List); if( Plist == NULL ) { // // cannot map view containing the cell; bail out // this shouldn't happen as we just marked it as dirty // (i.e. its view should be pinned in memory) // ASSERT(FALSE); return STATUS_INSUFFICIENT_RESOURCES; } // release the cell here as the reglock is held exclusive HvReleaseCell(Hive,BigData->List); // // what's the new size? // NewCount = (USHORT)((DataSize + CM_KEY_VALUE_BIG - 1) / CM_KEY_VALUE_BIG); if( NewCount > BigData->Count ) { // // grow the list and allocate additional cells to it // NewList = HvReallocateCell(Hive,BigData->List,NewCount * sizeof(HCELL_INDEX)); if( NewList == HCELL_NIL ) { return STATUS_INSUFFICIENT_RESOURCES; } // // we can now safely alter the list; if allocating the aditional cells below fails // we'll end up with some wasted space, but we'll be safe // BigData->List = NewList; // // read the new list // Plist = (PHCELL_INDEX)HvGetCell(Hive,NewList); if( Plist == NULL ) { // // cannot map view containing the cell; bail out // this shouldn't happen as we just reallocated the cell // (i.e. its view should be pinned in memory) // ASSERT(FALSE); return STATUS_INSUFFICIENT_RESOURCES; } // release the cell here as the reglock is held exclusive HvReleaseCell(Hive,NewList); for(i= BigData->Count;iCount ) { // // shrink the list and free additional unneccessary cells // for(i=NewCount;iCount;i++) { // // this CANNOT fail as the cell is already marked dirty (i.e. pinned in memory). // HvFreeCell(Hive,Plist[i]); } // // this WON'T fail, 'cause it's a shrink // NewList = HvReallocateCell(Hive,BigData->List,NewCount * sizeof(HCELL_INDEX)); if( NewList == HCELL_NIL ) { ASSERT( FALSE ); return STATUS_INSUFFICIENT_RESOURCES; } // // read the new list (in the current implementation we don't shrink cells, // so this is not really needed - just to be consistent) // Plist = (PHCELL_INDEX)HvGetCell(Hive,NewList); if( Plist == NULL ) { // // cannot map view containing the cell; bail out // this shouldn't happen as we just reallocated the cell // (i.e. its view should be pinned in memory) // ASSERT(FALSE); return STATUS_INSUFFICIENT_RESOURCES; } // release the cell here as the reglock is held exclusive HvReleaseCell(Hive,NewList); // // we can now safely alter the list // BigData->List = NewList; } // // if we came to this point, we have successfully grown the list and // allocated the additional space; nothing should go wrong further // // // go on and fill in the data onto the (new) big data cell // for( i=0;iCM_KEY_VALUE_BIG)?CM_KEY_VALUE_BIG:DataSize); // // update the data pointer and the remaining size // Data = (PVOID)((PCHAR)Data + CM_KEY_VALUE_BIG); DataSize -= CM_KEY_VALUE_BIG; } BigData->Count = NewCount; return STATUS_SUCCESS; } #endif //_CM_LDR_