/*++

Copyright (c) 1991  Microsoft Corporation

Module Name:

    cmsubs2.c

Abstract:

    This module various support routines for the configuration manager.

    The routines in this module are independent enough to be linked into
    any other program.  The routines in cmsubs.c are not.

Author:

    Bryan M. Willman (bryanwi) 12-Sep-1991

Revision History:

--*/

#include    "cmp.h"

BOOLEAN
CmpGetValueDataFromCache(
    IN PHHIVE               Hive,
    IN PPCM_CACHED_VALUE    ContainingList,
    IN PCELL_DATA           ValueKey,
    IN BOOLEAN              ValueCached,
    OUT PUCHAR              *DataPointer,
    OUT PBOOLEAN            Allocated,
    OUT PHCELL_INDEX        CellToRelease
);

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,CmpGetValueDataFromCache)
#pragma alloc_text(PAGE,CmpQueryKeyData)
#pragma alloc_text(PAGE,CmpQueryKeyDataFromCache)
#pragma alloc_text(PAGE,CmpQueryKeyValueData)
#endif

//
// Define alignment macro.
//

#define ALIGN_OFFSET(Offset) (ULONG) \
        ((((ULONG)(Offset) + sizeof(ULONG)-1)) & (~(sizeof(ULONG) - 1)))

#define ALIGN_OFFSET64(Offset) (ULONG) \
        ((((ULONG)(Offset) + sizeof(ULONGLONG)-1)) & (~(sizeof(ULONGLONG) - 1)))

//
// Data transfer workers
//


#ifdef CMP_STATS

extern struct {
    ULONG   BasicInformation;
    UINT64  BasicInformationTimeCounter;
    UINT64  BasicInformationTimeElapsed;

    ULONG   NodeInformation;
    UINT64  NodeInformationTimeCounter;
    UINT64  NodeInformationTimeElapsed;

    ULONG   FullInformation;
    UINT64  FullInformationTimeCounter;
    UINT64  FullInformationTimeElapsed;

    ULONG   EnumerateKeyBasicInformation;
    UINT64  EnumerateKeyBasicInformationTimeCounter;
    UINT64  EnumerateKeyBasicInformationTimeElapsed;

    ULONG   EnumerateKeyNodeInformation;
    UINT64  EnumerateKeyNodeInformationTimeCounter;
    UINT64  EnumerateKeyNodeInformationTimeElapsed;

    ULONG   EnumerateKeyFullInformation;
    UINT64  EnumerateKeyFullInformationTimeCounter;
    UINT64  EnumerateKeyFullInformationTimeElapsed;
} CmpQueryKeyDataDebug;


UINT64  CmpGetTimeStamp()
{
                
    LARGE_INTEGER   CurrentTime;
    LARGE_INTEGER   PerfFrequency;
    UINT64          Freq;
    UINT64          Time;

    CurrentTime = KeQueryPerformanceCounter(&PerfFrequency);

    //
    // Convert the perffrequency into 100ns interval.
    //
    Freq = 0;
    Freq |= PerfFrequency.HighPart;
    Freq = Freq << 32;
    Freq |= PerfFrequency.LowPart;


    //
    // Convert from LARGE_INTEGER to UINT64
    //
    Time = 0;
    Time |= CurrentTime.HighPart;
    Time = Time << 32;
    Time |= CurrentTime.LowPart;

    // Normalize cycles with the frequency.
    Time *= 10000000;
    Time /= Freq;

    return Time;
}   
#endif

NTSTATUS
CmpQueryKeyData(
    PHHIVE                  Hive,
    PCM_KEY_NODE            Node,
    KEY_INFORMATION_CLASS   KeyInformationClass,
    PVOID                   KeyInformation,
    ULONG                   Length,
    PULONG                  ResultLength
#if defined(CMP_STATS) || defined(CMP_KCB_CACHE_VALIDATION)
    ,
    PCM_KEY_CONTROL_BLOCK   Kcb
#endif
    )
/*++

Routine Description:

    Do the actual copy of data for a key into caller's buffer.

    If KeyInformation is not long enough to hold all requested data,
    STATUS_BUFFER_OVERFLOW will be returned, and ResultLength will be
    set to the number of bytes actually required.

Arguments:

    Hive - supplies a pointer to the hive control structure for the hive

    Node - Supplies pointer to node whose subkeys are to be found

    KeyInformationClass - Specifies the type of information returned in
        Buffer.  One of the following types:

        KeyBasicInformation - return last write time, title index, and name.
            (see KEY_BASIC_INFORMATION structure)

        KeyNodeInformation - return last write time, title index, name, class.
            (see KEY_NODE_INFORMATION structure)

    KeyInformation -Supplies pointer to buffer to receive the data.

    Length - Length of KeyInformation in bytes.

    ResultLength - Number of bytes actually written into KeyInformation.

Return Value:

    NTSTATUS - Result code from call, among the following:

        <TBS>

--*/
{
    NTSTATUS            status;
    PCELL_DATA          pclass;
    ULONG               requiredlength;
    LONG                leftlength;
    ULONG               offset;
    ULONG               minimumlength;
    PKEY_INFORMATION    pbuffer;
    USHORT              NameLength;
#ifdef CMP_STATS
    //LARGE_INTEGER       StartSystemTime;
    //LARGE_INTEGER       EndSystemTime;
    UINT64              StartSystemTime;
    UINT64              EndSystemTime;
    PUINT64             TimeCounter = NULL;
    PUINT64             TimeElapsed = NULL;

    //KeQuerySystemTime(&StartSystemTime);
    //StartSystemTime = KeQueryPerformanceCounter(NULL);
    StartSystemTime = CmpGetTimeStamp();
#endif //CMP_STATS


#ifdef CMP_KCB_CACHE_VALIDATION
    //
    // We have cached a lot of info into the kcb; Here is some validation code 
    //
    if( Kcb ) {
        BEGIN_KCB_LOCK_GUARD;                             
        CmpLockKCBTree();

        // number of values
        ASSERT( Node->ValueList.Count == Kcb->ValueCache.Count );

        // number of subkeys
        if( !(Kcb->ExtFlags & CM_KCB_INVALID_CACHED_INFO) ) {
            // there is some cached info
            ULONG   SubKeyCount = Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile];

            if( Kcb->ExtFlags & CM_KCB_NO_SUBKEY ) {
                ASSERT( SubKeyCount == 0 );
            } else if( Kcb->ExtFlags & CM_KCB_SUBKEY_ONE ) {
                ASSERT( SubKeyCount == 1 );
            } else if( Kcb->ExtFlags & CM_KCB_SUBKEY_HINT ) {
                ASSERT( SubKeyCount == Kcb->IndexHint->Count );
            } else {
                ASSERT( SubKeyCount == Kcb->SubKeyCount );
            }
        }

        // LastWriteTime
        ASSERT( Node->LastWriteTime.QuadPart == Kcb->KcbLastWriteTime.QuadPart );

        // MaxNameLen
        ASSERT( Node->MaxNameLen == Kcb->KcbMaxNameLen );

        // MaxValueNameLen
        ASSERT( Node->MaxValueNameLen == Kcb->KcbMaxValueNameLen );

        // MaxValueDataLen
        ASSERT( Node->MaxValueDataLen == Kcb->KcbMaxValueDataLen );

        CmpUnlockKCBTree();
        END_KCB_LOCK_GUARD;                             
    }

#endif //CMP_KCB_CACHE_VALIDATION

    pbuffer = (PKEY_INFORMATION)KeyInformation;
    NameLength = CmpHKeyNameLen(Node);

    switch (KeyInformationClass) {

    case KeyBasicInformation:

#ifdef CMP_STATS
        if(Kcb) {
            CmpQueryKeyDataDebug.BasicInformation++;
            TimeCounter = &(CmpQueryKeyDataDebug.BasicInformationTimeCounter);
            TimeElapsed = &(CmpQueryKeyDataDebug.BasicInformationTimeElapsed);
        } else {
            CmpQueryKeyDataDebug.EnumerateKeyBasicInformation++;
            TimeCounter = &(CmpQueryKeyDataDebug.EnumerateKeyBasicInformationTimeCounter);
            TimeElapsed = &(CmpQueryKeyDataDebug.EnumerateKeyBasicInformationTimeElapsed);
        }
#endif //CMP_STATS

        //
        // LastWriteTime, TitleIndex, NameLength, Name

        requiredlength = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name) +
                         NameLength;

        minimumlength = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name);

        *ResultLength = requiredlength;

        status = STATUS_SUCCESS;

        if (Length < minimumlength) {

            status = STATUS_BUFFER_TOO_SMALL;

        } else {

            pbuffer->KeyBasicInformation.LastWriteTime =
                Node->LastWriteTime;

            pbuffer->KeyBasicInformation.TitleIndex = 0;

            pbuffer->KeyBasicInformation.NameLength =
                NameLength;

            leftlength = Length - minimumlength;

            requiredlength = NameLength;

            if (leftlength < (LONG)requiredlength) {
                requiredlength = leftlength;
                status = STATUS_BUFFER_OVERFLOW;
            }

            if (Node->Flags & KEY_COMP_NAME) {
                CmpCopyCompressedName(pbuffer->KeyBasicInformation.Name,
                                      leftlength,
                                      Node->Name,
                                      Node->NameLength);
            } else {
                RtlCopyMemory(
                    &(pbuffer->KeyBasicInformation.Name[0]),
                    &(Node->Name[0]),
                    requiredlength
                    );
            }
        }

        break;


    case KeyNodeInformation:

#ifdef CMP_STATS
        if(Kcb) {
            CmpQueryKeyDataDebug.NodeInformation++;
            TimeCounter = &(CmpQueryKeyDataDebug.NodeInformationTimeCounter);
            TimeElapsed = &(CmpQueryKeyDataDebug.NodeInformationTimeElapsed);
        } else {
            CmpQueryKeyDataDebug.EnumerateKeyNodeInformation++;
            TimeCounter = &(CmpQueryKeyDataDebug.EnumerateKeyNodeInformationTimeCounter);
            TimeElapsed = &(CmpQueryKeyDataDebug.EnumerateKeyNodeInformationTimeElapsed);
        }
#endif //CMP_STATS
        //
        // LastWriteTime, TitleIndex, ClassOffset, ClassLength
        // NameLength, Name, Class
        //
        requiredlength = FIELD_OFFSET(KEY_NODE_INFORMATION, Name) +
                         NameLength +
                         Node->ClassLength;

        minimumlength = FIELD_OFFSET(KEY_NODE_INFORMATION, Name);

        *ResultLength = requiredlength;

        status = STATUS_SUCCESS;

        if (Length < minimumlength) {

            status = STATUS_BUFFER_TOO_SMALL;

        } else {

            pbuffer->KeyNodeInformation.LastWriteTime =
                Node->LastWriteTime;

            pbuffer->KeyNodeInformation.TitleIndex = 0;

            pbuffer->KeyNodeInformation.ClassLength =
                Node->ClassLength;

            pbuffer->KeyNodeInformation.NameLength =
                NameLength;

            leftlength = Length - minimumlength;
            requiredlength = NameLength;

            if (leftlength < (LONG)requiredlength) {
                requiredlength = leftlength;
                status = STATUS_BUFFER_OVERFLOW;
            }

            if (Node->Flags & KEY_COMP_NAME) {
                CmpCopyCompressedName(pbuffer->KeyNodeInformation.Name,
                                      leftlength,
                                      Node->Name,
                                      Node->NameLength);
            } else {
                RtlCopyMemory(
                    &(pbuffer->KeyNodeInformation.Name[0]),
                    &(Node->Name[0]),
                    requiredlength
                    );
            }

            if (Node->ClassLength > 0) {

                offset = FIELD_OFFSET(KEY_NODE_INFORMATION, Name) +
                            NameLength;
                offset = ALIGN_OFFSET(offset);

                pbuffer->KeyNodeInformation.ClassOffset = offset;

                pclass = HvGetCell(Hive, Node->Class);
                if( pclass == NULL ) {
                    //
                    // we couldn't map this cell
                    //
                    status = STATUS_INSUFFICIENT_RESOURCES;
                    break;
                }

                pbuffer = (PKEY_INFORMATION)((PUCHAR)pbuffer + offset);

                leftlength = (((LONG)Length - (LONG)offset) < 0) ?
                                    0 :
                                    Length - offset;

                requiredlength = Node->ClassLength;

                if (leftlength < (LONG)requiredlength) {
                    requiredlength = leftlength;
                    status = STATUS_BUFFER_OVERFLOW;
                }

                RtlCopyMemory(
                    pbuffer,
                    pclass,
                    requiredlength
                    );

                HvReleaseCell(Hive,Node->Class);

            } else {
                pbuffer->KeyNodeInformation.ClassOffset = (ULONG)-1;
            }
        }

        break;


    case KeyFullInformation:

#ifdef CMP_STATS
        if(Kcb) {
            CmpQueryKeyDataDebug.FullInformation++;
            TimeCounter = &(CmpQueryKeyDataDebug.FullInformationTimeCounter);
            TimeElapsed = &(CmpQueryKeyDataDebug.FullInformationTimeElapsed);
        } else {
            CmpQueryKeyDataDebug.EnumerateKeyFullInformation++;
            TimeCounter = &(CmpQueryKeyDataDebug.EnumerateKeyFullInformationTimeCounter);
            TimeElapsed = &(CmpQueryKeyDataDebug.EnumerateKeyFullInformationTimeElapsed);
        }
#endif //CMP_STATS

        //
        // LastWriteTime, TitleIndex, ClassOffset, ClassLength,
        // SubKeys, MaxNameLen, MaxClassLen, Values, MaxValueNameLen,
        // MaxValueDataLen, Class
        //
        requiredlength = FIELD_OFFSET(KEY_FULL_INFORMATION, Class) +
                         Node->ClassLength;

        minimumlength = FIELD_OFFSET(KEY_FULL_INFORMATION, Class);

        *ResultLength = requiredlength;

        status = STATUS_SUCCESS;

        if (Length < minimumlength) {

            status = STATUS_BUFFER_TOO_SMALL;

        } else {

            pbuffer->KeyFullInformation.LastWriteTime =
                Node->LastWriteTime;

            pbuffer->KeyFullInformation.TitleIndex = 0;

            pbuffer->KeyFullInformation.ClassLength =
                Node->ClassLength;

            if (Node->ClassLength > 0) {

                pbuffer->KeyFullInformation.ClassOffset =
                        FIELD_OFFSET(KEY_FULL_INFORMATION, Class);

                pclass = HvGetCell(Hive, Node->Class);
                if( pclass == NULL ) {
                    //
                    // we couldn't map this cell
                    //
                    status = STATUS_INSUFFICIENT_RESOURCES;
                    break;
                }

                leftlength = Length - minimumlength;
                requiredlength = Node->ClassLength;

                if (leftlength < (LONG)requiredlength) {
                    requiredlength = leftlength;
                    status = STATUS_BUFFER_OVERFLOW;
                }

                RtlCopyMemory(
                    &(pbuffer->KeyFullInformation.Class[0]),
                    pclass,
                    requiredlength
                    );

                HvReleaseCell(Hive,Node->Class);

            } else {
                pbuffer->KeyFullInformation.ClassOffset = (ULONG)-1;
            }

            pbuffer->KeyFullInformation.SubKeys =
                Node->SubKeyCounts[Stable] +
                Node->SubKeyCounts[Volatile];

            pbuffer->KeyFullInformation.Values =
                Node->ValueList.Count;

            pbuffer->KeyFullInformation.MaxNameLen =
                Node->MaxNameLen;

            pbuffer->KeyFullInformation.MaxClassLen =
                Node->MaxClassLen;

            pbuffer->KeyFullInformation.MaxValueNameLen =
                Node->MaxValueNameLen;

            pbuffer->KeyFullInformation.MaxValueDataLen =
                Node->MaxValueDataLen;

        }

        break;


    default:
        status = STATUS_INVALID_PARAMETER;
        break;
    }

#ifdef CMP_STATS
    if( TimeCounter && TimeElapsed ){
        //EndSystemTime = KeQueryPerformanceCounter(NULL);
        //KeQuerySystemTime(&EndSystemTime);
        EndSystemTime = CmpGetTimeStamp();
        if( (EndSystemTime - StartSystemTime) > 0 ) {
            (*TimeCounter)++;
            //(*TimeElapsed) += (ULONG)(EndSystemTime.QuadPart - StartSystemTime.QuadPart);
            (*TimeElapsed) += (EndSystemTime - StartSystemTime);
        }
    }
#endif //CMP_STATS

    return status;
}

NTSTATUS
CmpQueryKeyDataFromCache(
    PCM_KEY_CONTROL_BLOCK   Kcb,
    KEY_INFORMATION_CLASS   KeyInformationClass,
    PVOID                   KeyInformation,
    ULONG                   Length,
    PULONG                  ResultLength
    )
/*++

Routine Description:

    Do the actual copy of data for a key into caller's buffer.

    If KeyInformation is not long enough to hold all requested data,
    STATUS_BUFFER_OVERFLOW will be returned, and ResultLength will be
    set to the number of bytes actually required.

    Works only for the information cached into kcb. I.e. KeyBasicInformation
    and KeyCachedInfo


Arguments:

    Kcb - Supplies pointer to the kcb to be queried

    KeyInformationClass - Specifies the type of information returned in
        Buffer.  One of the following types:

        KeyBasicInformation - return last write time, title index, and name.
            (see KEY_BASIC_INFORMATION structure)

        KeyCachedInformation - return last write time, title index, name ....
            (see KEY_CACHED_INFORMATION structure)

    KeyInformation -Supplies pointer to buffer to receive the data.

    Length - Length of KeyInformation in bytes.

    ResultLength - Number of bytes actually written into KeyInformation.

Return Value:

    NTSTATUS - Result code from call, among the following:

        <TBS>

--*/
{
    NTSTATUS            status;
    PKEY_INFORMATION    pbuffer;
    ULONG               requiredlength;
    ULONG               minimumlength;
    USHORT              NameLength;
    LONG                leftlength;
    PCM_KEY_NODE        Node; // this is to be used only in case of cache incoherency

    PAGED_CODE();

#ifdef CMP_KCB_CACHE_VALIDATION
    //
    // We have cached a lot of info into the kcb; Here is some validation code 
    //
    if( Kcb ) {
        BEGIN_KCB_LOCK_GUARD;                             
        CmpLockKCBTree();

        Node = (PCM_KEY_NODE)HvGetCell(Kcb->KeyHive,Kcb->KeyCell);
        if( Node != NULL ) {
            // number of values
            ASSERT( Node->ValueList.Count == Kcb->ValueCache.Count );

            // number of subkeys
            if( !(Kcb->ExtFlags & CM_KCB_INVALID_CACHED_INFO) ) {
                // there is some cached info
                ULONG   SubKeyCount = Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile];

                if( Kcb->ExtFlags & CM_KCB_NO_SUBKEY ) {
                    ASSERT( SubKeyCount == 0 );
                } else if( Kcb->ExtFlags & CM_KCB_SUBKEY_ONE ) {
                    ASSERT( SubKeyCount == 1 );
                } else if( Kcb->ExtFlags & CM_KCB_SUBKEY_HINT ) {
                    ASSERT( SubKeyCount == Kcb->IndexHint->Count );
                } else {
                    ASSERT( SubKeyCount == Kcb->SubKeyCount );
                }
            }

            // LastWriteTime
            ASSERT( Node->LastWriteTime.QuadPart == Kcb->KcbLastWriteTime.QuadPart );

            // MaxNameLen
            ASSERT( Node->MaxNameLen == Kcb->KcbMaxNameLen );

            // MaxValueNameLen
            ASSERT( Node->MaxValueNameLen == Kcb->KcbMaxValueNameLen );

            // MaxValueDataLen
            ASSERT( Node->MaxValueDataLen == Kcb->KcbMaxValueDataLen );
            HvReleaseCell(Kcb->KeyHive,Kcb->KeyCell);
        }
        
        CmpUnlockKCBTree();
        END_KCB_LOCK_GUARD;                             
    }

#endif //CMP_KCB_CACHE_VALIDATION

    //
    // we cannot afford to return the kcb NameBlock as the key name
    // for KeyBasicInformation as there are lots of callers expecting
    // the name to be case-sensitive; KeyCachedInformation is new
    // and used only by the Win32 layer, which is not case sensitive
    // Note: future clients of KeyCachedInformation must be made aware 
    // that name is NOT case-sensitive
    //
    ASSERT( KeyInformationClass == KeyCachedInformation );

    // 
    // we are going to need the nameblock; if it is NULL, bail out
    //
    if( Kcb->NameBlock == NULL ) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    pbuffer = (PKEY_INFORMATION)KeyInformation;
    
    if (Kcb->NameBlock->Compressed) {
        NameLength = CmpCompressedNameSize(Kcb->NameBlock->Name,Kcb->NameBlock->NameLength);
    } else {
        NameLength = Kcb->NameBlock->NameLength;
    }
    
    // Assume success
    status = STATUS_SUCCESS;

    switch (KeyInformationClass) {

#if 0
    case KeyBasicInformation:

        //
        // LastWriteTime, TitleIndex, NameLength, Name

        requiredlength = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name) +
                         NameLength;

        minimumlength = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name);

        *ResultLength = requiredlength;

        if (Length < minimumlength) {

            status = STATUS_BUFFER_TOO_SMALL;

        } else {

            pbuffer->KeyBasicInformation.LastWriteTime = Kcb->KcbLastWriteTime;

            pbuffer->KeyBasicInformation.TitleIndex = 0;

            pbuffer->KeyBasicInformation.NameLength = NameLength;

            leftlength = Length - minimumlength;

            requiredlength = NameLength;

            if (leftlength < (LONG)requiredlength) {
                requiredlength = leftlength;
                status = STATUS_BUFFER_OVERFLOW;
            }

            if (Kcb->NameBlock->Compressed) {
                CmpCopyCompressedName(pbuffer->KeyBasicInformation.Name,
                                      leftlength,
                                      Kcb->NameBlock->Name,
                                      Kcb->NameBlock->NameLength);
            } else {
                RtlCopyMemory(
                    &(pbuffer->KeyBasicInformation.Name[0]),
                    &(Kcb->NameBlock->Name[0]),
                    requiredlength
                    );
            }
        }

        break;
#endif

    case KeyCachedInformation:

        //
        // LastWriteTime, TitleIndex, 
        // SubKeys, MaxNameLen, Values, MaxValueNameLen,
        // MaxValueDataLen, Name
        //
        requiredlength = sizeof(KEY_CACHED_INFORMATION);

        *ResultLength = requiredlength;

        if (Length < requiredlength) {

            status = STATUS_BUFFER_TOO_SMALL;

        } else {

            pbuffer->KeyCachedInformation.LastWriteTime = Kcb->KcbLastWriteTime;

            pbuffer->KeyCachedInformation.TitleIndex = 0;

            pbuffer->KeyCachedInformation.NameLength = NameLength;

            pbuffer->KeyCachedInformation.Values = Kcb->ValueCache.Count;
            
            pbuffer->KeyCachedInformation.MaxNameLen = Kcb->KcbMaxNameLen;
            
            pbuffer->KeyCachedInformation.MaxValueNameLen = Kcb->KcbMaxValueNameLen;
            
            pbuffer->KeyCachedInformation.MaxValueDataLen = Kcb->KcbMaxValueDataLen;

            if( !(Kcb->ExtFlags & CM_KCB_INVALID_CACHED_INFO) ) {
                // there is some cached info
                if( Kcb->ExtFlags & CM_KCB_NO_SUBKEY ) {
                    pbuffer->KeyCachedInformation.SubKeys = 0;
                } else if( Kcb->ExtFlags & CM_KCB_SUBKEY_ONE ) {
                    pbuffer->KeyCachedInformation.SubKeys = 1;
                } else if( Kcb->ExtFlags & CM_KCB_SUBKEY_HINT ) {
                    pbuffer->KeyCachedInformation.SubKeys = Kcb->IndexHint->Count;
                } else {
                    pbuffer->KeyCachedInformation.SubKeys = Kcb->SubKeyCount;
                }
            } else {
                //
                // kcb cache is not coherent; get the info from knode
                // 
                CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"Kcb cache incoherency detected, kcb = %p\n",Kcb));

                Node = (PCM_KEY_NODE)HvGetCell(Kcb->KeyHive,Kcb->KeyCell);
                if( Node == NULL ) {
                    //
                    // couldn't map view for this cell
                    //
                    status = STATUS_INSUFFICIENT_RESOURCES;
                    break;
                }

                pbuffer->KeyCachedInformation.SubKeys = Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile];
                HvReleaseCell(Kcb->KeyHive,Kcb->KeyCell);

            }

        }

        break;

    default:
        status = STATUS_INVALID_PARAMETER;
        break;
    }

    return status;
}


BOOLEAN
CmpGetValueDataFromCache(
    IN PHHIVE               Hive,
    IN PPCM_CACHED_VALUE    ContainingList,
    IN PCELL_DATA           ValueKey,
    IN BOOLEAN              ValueCached,
    OUT PUCHAR              *DataPointer,
    OUT PBOOLEAN            Allocated,
    OUT PHCELL_INDEX        CellToRelease
)
/*++

Routine Description:

    Get the cached Value Data given a value node.

Arguments:

    Hive - pointer to hive control structure for hive of interest

    ContainingList - Address that stores the allocation address of the value node.
                     We need to update this when we do a re-allocate to cache
                     both value key and value data.

    ValueKey - pointer to the Value Key

    ValueCached - Indicating whether Value key is cached or not.

    DataPointer - out param to receive a pointer to the data

    Allocated - out param telling if the caller should free the DataPointer

Return Value:

    TRUE - data was retrieved
    FALSE - some error (STATUS_INSUFFICIENT_RESOURCES) occured

Note:
    
    The caller is responsible for freeing the DataPointer when we signal it to him
    by setting Allocated on TRUE;

    Also we must be sure that MAXIMUM_CACHED_DATA is smaller than CM_KEY_VALUE_BIG
--*/
{
    //
    // Cache the data if needed.
    //
    PCM_CACHED_VALUE OldEntry;
    PCM_CACHED_VALUE NewEntry;
    PUCHAR      Cacheddatapointer;
    ULONG       AllocSize;
    ULONG       CopySize;
    ULONG       DataSize;

    ASSERT( MAXIMUM_CACHED_DATA < CM_KEY_VALUE_BIG );

    //
    // this routine should not be called for small data
    //
    ASSERT( (ValueKey->u.KeyValue.DataLength & CM_KEY_VALUE_SPECIAL_SIZE) == 0 );
    
    //
    // init out params
    //
    *DataPointer = NULL;
    *Allocated = FALSE;
    *CellToRelease = HCELL_NIL;

    if (ValueCached) {
        OldEntry = (PCM_CACHED_VALUE) CMP_GET_CACHED_ADDRESS(*ContainingList);
        if (OldEntry->DataCacheType == CM_CACHE_DATA_CACHED) {
            //
            // Data is already cached, use it.
            //
            *DataPointer = (PUCHAR) ((ULONG_PTR) ValueKey + OldEntry->ValueKeySize);
        } else {
            if ((OldEntry->DataCacheType == CM_CACHE_DATA_TOO_BIG) ||
                (ValueKey->u.KeyValue.DataLength > MAXIMUM_CACHED_DATA ) 
               ){
                //
                // Mark the type and do not cache it.
                //
                OldEntry->DataCacheType = CM_CACHE_DATA_TOO_BIG;

                //
                // Data is too big to warrent caching, get it from the registry; 
                // - regardless of the size; we might be forced to allocate a buffer
                //
                if( CmpGetValueData(Hive,&(ValueKey->u.KeyValue),&DataSize,DataPointer,Allocated,CellToRelease) == FALSE ) {
                    //
                    // insufficient resources; return NULL
                    //
                    ASSERT( *Allocated == FALSE );
                    ASSERT( *DataPointer == NULL );
                    return FALSE;
                }

            } else {
                //
                // consistency check
                //
                ASSERT(OldEntry->DataCacheType == CM_CACHE_DATA_NOT_CACHED);

                //
                // Value data is not cached.
                // Check the size of value data, if it is smaller than MAXIMUM_CACHED_DATA, cache it.
                //
                // Anyway, the data is for sure not stored in a big data cell (see test above)
                //
                //
                *DataPointer = (PUCHAR)HvGetCell(Hive, ValueKey->u.KeyValue.Data);
                if( *DataPointer == NULL ) {
                    //
                    // we couldn't map this cell
                    // the caller must handle this gracefully !
                    //
                    return FALSE;
                }
                //
                // inform the caller it has to release this cell
                //
                *CellToRelease = ValueKey->u.KeyValue.Data;
                
                //
                // copy only valid data; cell might be bigger
                //
                //DataSize = (ULONG) HvGetCellSize(Hive, datapointer);
                DataSize = (ULONG)ValueKey->u.KeyValue.DataLength;

                //
                // consistency check
                //
                ASSERT(DataSize <= MAXIMUM_CACHED_DATA);

                //
                // Data is not cached and now we are going to do it.
                // Reallocate a new cached entry for both value key and value data.
                //
                CopySize = DataSize + OldEntry->ValueKeySize;
                AllocSize = CopySize + FIELD_OFFSET(CM_CACHED_VALUE, KeyValue);

                // Dragos: Changed to catch the memory violator
                // it didn't work
                //NewEntry = (PCM_CACHED_VALUE) ExAllocatePoolWithTagPriority(PagedPool, AllocSize, CM_CACHE_VALUE_DATA_TAG,NormalPoolPrioritySpecialPoolUnderrun);
                NewEntry = (PCM_CACHED_VALUE) ExAllocatePoolWithTag(PagedPool, AllocSize, CM_CACHE_VALUE_DATA_TAG);

                if (NewEntry) {
                    //
                    // Now fill the data to the new cached entry
                    //
                    NewEntry->DataCacheType = CM_CACHE_DATA_CACHED;
                    NewEntry->ValueKeySize = OldEntry->ValueKeySize;

                    RtlCopyMemory((PVOID)&(NewEntry->KeyValue),
                                  (PVOID)&(OldEntry->KeyValue),
                                  NewEntry->ValueKeySize);

                    Cacheddatapointer = (PUCHAR) ((ULONG_PTR) &(NewEntry->KeyValue) + OldEntry->ValueKeySize);
                    RtlCopyMemory(Cacheddatapointer, *DataPointer, DataSize);

                    // Trying to catch the BAD guy who writes over our pool.
                    CmpMakeSpecialPoolReadWrite( OldEntry );

                    *ContainingList = (PCM_CACHED_VALUE) CMP_MARK_CELL_CACHED(NewEntry);

                    // Trying to catch the BAD guy who writes over our pool.
                    CmpMakeSpecialPoolReadOnly( NewEntry );

                    //
                    // Free the old entry
                    //
                    ExFreePool(OldEntry);

                } 
            }
        }
    } else {
        if( CmpGetValueData(Hive,&(ValueKey->u.KeyValue),&DataSize,DataPointer,Allocated,CellToRelease) == FALSE ) {
            //
            // insufficient resources; return NULL
            //
            ASSERT( *Allocated == FALSE );
            ASSERT( *DataPointer == NULL );
            return FALSE;
        }
    }

    return TRUE;
}



NTSTATUS
CmpQueryKeyValueData(
    PHHIVE Hive,
    PPCM_CACHED_VALUE ContainingList,
    PCM_KEY_VALUE ValueKey,
    BOOLEAN     ValueCached,
    KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
    PVOID KeyValueInformation,
    ULONG Length,
    PULONG ResultLength
    )
/*++

Routine Description:

    Do the actual copy of data for a key value into caller's buffer.

    If KeyValueInformation is not long enough to hold all requested data,
    STATUS_BUFFER_OVERFLOW will be returned, and ResultLength will be
    set to the number of bytes actually required.

Arguments:

    Hive - supplies a pointer to the hive control structure for the hive

    Cell - supplies index of node to whose sub keys are to be found

    KeyValueInformationClass - Specifies the type of information returned in
        KeyValueInformation.  One of the following types:

    KeyValueInformation -Supplies pointer to buffer to receive the data.

    Length - Length of KeyInformation in bytes.

    ResultLength - Number of bytes actually written into KeyInformation.

Return Value:

    NTSTATUS - Result code from call, among the following:

        <TBS>

--*/
{
    NTSTATUS    status;
    PKEY_VALUE_INFORMATION pbuffer;
    PCELL_DATA  pcell;
    LONG        leftlength;
    ULONG       requiredlength;
    ULONG       minimumlength;
    ULONG       offset;
    ULONG       base;
    ULONG       realsize;
    PUCHAR      datapointer;
    BOOLEAN     small;
    USHORT      NameLength;
    BOOLEAN     BufferAllocated = FALSE;
    HCELL_INDEX CellToRelease = HCELL_NIL;

    pbuffer = (PKEY_VALUE_INFORMATION)KeyValueInformation;

    pcell = (PCELL_DATA) ValueKey;
    NameLength = CmpValueNameLen(&pcell->u.KeyValue);

    switch (KeyValueInformationClass) {

    case KeyValueBasicInformation:

        //
        // TitleIndex, Type, NameLength, Name
        //
        requiredlength = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name) +
                         NameLength;

        minimumlength = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name);

        *ResultLength = requiredlength;

        status = STATUS_SUCCESS;

        if (Length < minimumlength) {

            status = STATUS_BUFFER_TOO_SMALL;

        } else {

            pbuffer->KeyValueBasicInformation.TitleIndex = 0;

            pbuffer->KeyValueBasicInformation.Type =
                pcell->u.KeyValue.Type;

            pbuffer->KeyValueBasicInformation.NameLength =
                NameLength;

            leftlength = Length - minimumlength;
            requiredlength = NameLength;

            if (leftlength < (LONG)requiredlength) {
                requiredlength = leftlength;
                status = STATUS_BUFFER_OVERFLOW;
            }

            if (pcell->u.KeyValue.Flags & VALUE_COMP_NAME) {
                CmpCopyCompressedName(pbuffer->KeyValueBasicInformation.Name,
                                      requiredlength,
                                      pcell->u.KeyValue.Name,
                                      pcell->u.KeyValue.NameLength);
            } else {
                RtlCopyMemory(&(pbuffer->KeyValueBasicInformation.Name[0]),
                              &(pcell->u.KeyValue.Name[0]),
                              requiredlength);
            }
        }

        break;



    case KeyValueFullInformation:
    case KeyValueFullInformationAlign64:

        //
        // TitleIndex, Type, DataOffset, DataLength, NameLength,
        // Name, Data
        //
        small = CmpIsHKeyValueSmall(realsize, pcell->u.KeyValue.DataLength);

        requiredlength = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name) +
                         NameLength +
                         realsize;

        minimumlength = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name);
        if (realsize > 0) {
            base = requiredlength - realsize;

#if defined(_WIN64)

            offset = ALIGN_OFFSET64(base);

#else

            if (KeyValueInformationClass == KeyValueFullInformationAlign64) {
                offset = ALIGN_OFFSET64(base);

            } else {
                offset = ALIGN_OFFSET(base);
            }

#endif

            if (offset > base) {
                requiredlength += (offset - base);
            }

#if DBG && defined(_WIN64)

            //
            // Some clients will have passed in a structure that they "know"
            // will be exactly the right size.  The fact that alignment
            // has changed on NT64 may cause these clients to have problems.
            //
            // The solution is to fix the client, but print out some debug
            // spew here if it looks like this is the case.  This problem
            // isn't particularly easy to spot from the client end.
            //

            if((KeyValueInformationClass == KeyValueFullInformation) &&
                (Length != minimumlength) &&
                (requiredlength > Length) &&
                ((requiredlength - Length) <=
                                (ALIGN_OFFSET64(base) - ALIGN_OFFSET(base)))) {

                CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"ntos/config-64 KeyValueFullInformation: "
                                                                 "Possible client buffer size problem.\n"));

                CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"    Required size = %d\n", requiredlength));
                CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"    Supplied size = %d\n", Length));
            }

#endif

        }

        *ResultLength = requiredlength;

        status = STATUS_SUCCESS;

        if (Length < minimumlength) {

            status = STATUS_BUFFER_TOO_SMALL;

        } else {

            pbuffer->KeyValueFullInformation.TitleIndex = 0;

            pbuffer->KeyValueFullInformation.Type =
                pcell->u.KeyValue.Type;

            pbuffer->KeyValueFullInformation.DataLength =
                realsize;

            pbuffer->KeyValueFullInformation.NameLength =
                NameLength;

            leftlength = Length - minimumlength;
            requiredlength = NameLength;

            if (leftlength < (LONG)requiredlength) {
                requiredlength = leftlength;
                status = STATUS_BUFFER_OVERFLOW;
            }

            if (pcell->u.KeyValue.Flags & VALUE_COMP_NAME) {
                CmpCopyCompressedName(pbuffer->KeyValueFullInformation.Name,
                                      requiredlength,
                                      pcell->u.KeyValue.Name,
                                      pcell->u.KeyValue.NameLength);
            } else {
                RtlCopyMemory(
                    &(pbuffer->KeyValueFullInformation.Name[0]),
                    &(pcell->u.KeyValue.Name[0]),
                    requiredlength
                    );
            }

            if (realsize > 0) {

                if (small == TRUE) {
                    datapointer = (PUCHAR)(&(pcell->u.KeyValue.Data));
                } else {
                    if( CmpGetValueDataFromCache(Hive, ContainingList, pcell, ValueCached,&datapointer,&BufferAllocated,&CellToRelease) == FALSE ){
                        //
                        // we couldn't map view for cell; treat it as insufficient resources problem
                        //
                        ASSERT( datapointer == NULL );
                        ASSERT( BufferAllocated == FALSE );
                        status = STATUS_INSUFFICIENT_RESOURCES;
                    }
                }

                pbuffer->KeyValueFullInformation.DataOffset = offset;

                leftlength = (((LONG)Length - (LONG)offset) < 0) ?
                                    0 :
                                    Length - offset;

                requiredlength = realsize;

                if (leftlength < (LONG)requiredlength) {
                    requiredlength = leftlength;
                    status = STATUS_BUFFER_OVERFLOW;
                }

                ASSERT((small ? (requiredlength <= CM_KEY_VALUE_SMALL) : TRUE));

                if( datapointer != NULL ) {
                    RtlCopyMemory(
                        ((PUCHAR)pbuffer + offset),
                        datapointer,
                        requiredlength
                        );
                    if( BufferAllocated == TRUE ) {
                        ExFreePool(datapointer);
                    }
                    if( CellToRelease != HCELL_NIL ) {
                        HvReleaseCell(Hive,CellToRelease);
                    }
                }

            } else {
                pbuffer->KeyValueFullInformation.DataOffset = (ULONG)-1;
            }
        }

        break;


    case KeyValuePartialInformation:

        //
        // TitleIndex, Type, DataLength, Data
        //
        small = CmpIsHKeyValueSmall(realsize, pcell->u.KeyValue.DataLength);
        requiredlength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) +
                         realsize;

        minimumlength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);

        *ResultLength = requiredlength;

        status = STATUS_SUCCESS;

        if (Length < minimumlength) {

            status = STATUS_BUFFER_TOO_SMALL;

        } else {

            pbuffer->KeyValuePartialInformation.TitleIndex = 0;

            pbuffer->KeyValuePartialInformation.Type =
                pcell->u.KeyValue.Type;

            pbuffer->KeyValuePartialInformation.DataLength =
                realsize;

            leftlength = Length - minimumlength;
            requiredlength = realsize;

            if (leftlength < (LONG)requiredlength) {
                requiredlength = leftlength;
                status = STATUS_BUFFER_OVERFLOW;
            }

            if (realsize > 0) {

                if (small == TRUE) {
                    datapointer = (PUCHAR)(&(pcell->u.KeyValue.Data));
                } else {
                    if( CmpGetValueDataFromCache(Hive, ContainingList, pcell, ValueCached,&datapointer,&BufferAllocated,&CellToRelease) == FALSE ){
                        //
                        // we couldn't map view for cell; treat it as insufficient resources problem
                        //
                        ASSERT( datapointer == NULL );
                        ASSERT( BufferAllocated == FALSE );
                        status = STATUS_INSUFFICIENT_RESOURCES;
                    }
                }

                ASSERT((small ? (requiredlength <= CM_KEY_VALUE_SMALL) : TRUE));

                if( datapointer != NULL ) {
                    RtlCopyMemory((PUCHAR)&(pbuffer->KeyValuePartialInformation.Data[0]),
                                  datapointer,
                                  requiredlength);
                    if( BufferAllocated == TRUE ) {
                        ExFreePool(datapointer);
                    }
                    if(CellToRelease != HCELL_NIL) {
                        HvReleaseCell(Hive,CellToRelease);
                    }
                }
            }
        }

        break;
    case KeyValuePartialInformationAlign64:

        //
        // TitleIndex, Type, DataLength, Data
        //
        small = CmpIsHKeyValueSmall(realsize, pcell->u.KeyValue.DataLength);
        requiredlength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION_ALIGN64, Data) +
                         realsize;

        minimumlength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION_ALIGN64, Data);

        *ResultLength = requiredlength;

        status = STATUS_SUCCESS;

        if (Length < minimumlength) {

            status = STATUS_BUFFER_TOO_SMALL;

        } else {

            pbuffer->KeyValuePartialInformationAlign64.Type =
                pcell->u.KeyValue.Type;

            pbuffer->KeyValuePartialInformationAlign64.DataLength =
                realsize;

            leftlength = Length - minimumlength;
            requiredlength = realsize;

            if (leftlength < (LONG)requiredlength) {
                requiredlength = leftlength;
                status = STATUS_BUFFER_OVERFLOW;
            }

            if (realsize > 0) {

                if (small == TRUE) {
                    datapointer = (PUCHAR)(&(pcell->u.KeyValue.Data));
                } else {
                    if( CmpGetValueDataFromCache(Hive, ContainingList, pcell, ValueCached,&datapointer,&BufferAllocated,&CellToRelease) == FALSE ){
                        //
                        // we couldn't map view for cell; treat it as insufficient resources problem
                        //
                        ASSERT( datapointer == NULL );
                        ASSERT( BufferAllocated == FALSE );
                        status = STATUS_INSUFFICIENT_RESOURCES;
                    }
                }

                ASSERT((small ? (requiredlength <= CM_KEY_VALUE_SMALL) : TRUE));
                if( datapointer != NULL ) {
                    RtlCopyMemory((PUCHAR)&(pbuffer->KeyValuePartialInformationAlign64.Data[0]),
                                  datapointer,
                                  requiredlength);
                    if( BufferAllocated == TRUE ) {
                        ExFreePool(datapointer);
                    }
                    if(CellToRelease != HCELL_NIL) {
                        HvReleaseCell(Hive,CellToRelease);
                    }

                }
            }
        }

        break;

    default:
        status = STATUS_INVALID_PARAMETER;
        break;
    }
    return status;
}