/*++

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

--*/
{
    NTSTATUS        status;
    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;
        ULONG           Length;
        USHORT          i;
        PUCHAR          PartialData;
        PHCELL_INDEX    Plist = NULL;
        
#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
                //
                return FALSE;
            }

            ASSERT_BIG_DATA(BigData);

            Plist = (PHCELL_INDEX)HvGetCell(Hive,BigData->List);
            if( Plist == NULL ) {
                //
                // cannot map view containing the cell; bail out
                //
                return FALSE;
            }

            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 ){
                return FALSE;
            }
        
            for(i=0;i<BigData->Count;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);
                    return FALSE;
                }
            
                //
                // 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 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);

        return NULL;
    }
    
    //
    // 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;i<BigData->Count;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;i<BigData->Count;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;
    ULONG           realsize;

    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
            //
            ((PUCHAR)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;i<NewCount;i++) {
            Plist[i] = HvAllocateCell(Hive, CM_KEY_VALUE_BIG, StorageType,NewList);
            if( Plist[i] == HCELL_NIL ) {
                return STATUS_INSUFFICIENT_RESOURCES;
            }
        }
    } else if( NewCount < BigData->Count ) {
        //
        // shrink the list and free additional unneccessary cells
        //
        for(i=NewCount;i<BigData->Count;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;i<NewCount;i++) {
        pdata = HvGetCell(Hive,Plist[i]);
        if( pdata == NULL ) {
            //
            // cannot map view containing the cell; bail out
            //
            // 
            // This shouldn't happen as this cell is marked dirty by
            // this time - or is a new allocated cell 
            // (i.e. its view is pinned in memory)
            //
            ASSERT( FALSE );
            return STATUS_INSUFFICIENT_RESOURCES;
        }

        // release the cell here as the reglock is held exclusive
        HvReleaseCell(Hive,Plist[i]);

        //
        // now, copy this chunk data
        //
        RtlCopyMemory(pdata, (PUCHAR)Data, (DataSize>CM_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_