Ejemplo n.º 1
0
VALUE_SEARCH_RETURN_TYPE
CmpQueryKeyValueData(
    PCM_KEY_CONTROL_BLOCK KeyControlBlock,
    PPCM_CACHED_VALUE   ContainingList,
    PCM_KEY_VALUE       ValueKey,
    BOOLEAN             ValueCached,
    KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
    PVOID               KeyValueInformation,
    ULONG               Length,
    PULONG              ResultLength,
    NTSTATUS            *status
    )
/*++

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

--*/
{
    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;
    PHHIVE      Hive;
    VALUE_SEARCH_RETURN_TYPE SearchValue = SearchSuccess;

    Hive = KeyControlBlock->KeyHive;
    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);
        offset = 0;
        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 {
                    SearchValue = CmpGetValueDataFromCache(KeyControlBlock, ContainingList, pcell, ValueCached,&datapointer,&BufferAllocated,&CellToRelease);
                    if( SearchValue != SearchSuccess ) {
                        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 ) {
                    try {
                        RtlCopyMemory(
                            ((PUCHAR)pbuffer + offset),
                            datapointer,
                            requiredlength
                            );
                    } finally {
                        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 {
                    SearchValue = CmpGetValueDataFromCache(KeyControlBlock, ContainingList, pcell, ValueCached,&datapointer,&BufferAllocated,&CellToRelease);
                    if( SearchValue != SearchSuccess ) {
                        ASSERT( datapointer == NULL );
                        ASSERT( BufferAllocated == FALSE );
                        *status = STATUS_INSUFFICIENT_RESOURCES;
                    }
                }

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

                if( datapointer != NULL ) {
                    try {
                        RtlCopyMemory((PUCHAR)&(pbuffer->KeyValuePartialInformation.Data[0]),
                                      datapointer,
                                      requiredlength);
                    } finally {
                        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 {
                    SearchValue = CmpGetValueDataFromCache(KeyControlBlock, ContainingList, pcell, ValueCached,&datapointer,&BufferAllocated,&CellToRelease);
                    if( SearchValue != SearchSuccess ) {
                        ASSERT( datapointer == NULL );
                        ASSERT( BufferAllocated == FALSE );
                        *status = STATUS_INSUFFICIENT_RESOURCES;
                    }
                }

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

        break;

    default:
        *status = STATUS_INVALID_PARAMETER;
        break;
    }
Ejemplo n.º 2
0
VALUE_SEARCH_RETURN_TYPE
NTAPI
CmpQueryKeyValueData(IN PCM_KEY_CONTROL_BLOCK Kcb,
                     IN PCM_CACHED_VALUE *CachedValue,
                     IN PCM_KEY_VALUE ValueKey,
                     IN BOOLEAN ValueIsCached,
                     IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
                     IN PVOID KeyValueInformation,
                     IN ULONG Length,
                     OUT PULONG ResultLength,
                     OUT PNTSTATUS Status)
{
    PKEY_VALUE_INFORMATION Info = (PKEY_VALUE_INFORMATION)KeyValueInformation;
    PCELL_DATA CellData;
    USHORT NameSize;
    ULONG Size, MinimumSize, SizeLeft, KeySize, AlignedData = 0, DataOffset;
    PVOID Buffer;
    BOOLEAN IsSmall, BufferAllocated = FALSE;
    HCELL_INDEX CellToRelease = HCELL_NIL;
    VALUE_SEARCH_RETURN_TYPE Result = SearchSuccess;

    /* Get the value data */
    CellData = (PCELL_DATA)ValueKey;

    /* Check if the value is compressed */
    if (CellData->u.KeyValue.Flags & VALUE_COMP_NAME)
    {
        /* Get the compressed name size */
        NameSize = CmpCompressedNameSize(CellData->u.KeyValue.Name,
                                         CellData->u.KeyValue.NameLength);
    }
    else
    {
        /* Get the real size */
        NameSize = CellData->u.KeyValue.NameLength;
    }

    /* Check what kind of information the caller is requesting */
    switch (KeyValueInformationClass)
    {
        /* Basic information */
        case KeyValueBasicInformation:

            /* This is how much size we'll need */
            Size = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name) + NameSize;

            /* This is the minimum we can work with */
            MinimumSize = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name);

            /* Return the size we'd like, and assume success */
            *ResultLength = Size;
            *Status = STATUS_SUCCESS;

            /* Check if the caller gave us below our minimum */
            if (Length < MinimumSize)
            {
                /* Then we must fail */
                *Status = STATUS_BUFFER_TOO_SMALL;
                break;
            }

            /* Fill out the basic information */
            Info->KeyValueBasicInformation.TitleIndex = 0;
            Info->KeyValueBasicInformation.Type = CellData->u.KeyValue.Type;
            Info->KeyValueBasicInformation.NameLength = NameSize;

            /* Now only the name is left */
            SizeLeft = Length - MinimumSize;
            Size = NameSize;

            /* Check if the remaining buffer is too small for the name */
            if (SizeLeft < Size)
            {
                /* Copy only as much as can fit, and tell the caller */
                Size = SizeLeft;
                *Status = STATUS_BUFFER_OVERFLOW;
            }

            /* Check if this is a compressed name */
            if (CellData->u.KeyValue.Flags & VALUE_COMP_NAME)
            {
                /* Copy as much as we can of the compressed name */
                CmpCopyCompressedName(Info->KeyValueBasicInformation.Name,
                                      Size,
                                      CellData->u.KeyValue.Name,
                                      CellData->u.KeyValue.NameLength);
            }
            else
            {
                /* Copy as much as we can of the raw name */
                RtlCopyMemory(Info->KeyValueBasicInformation.Name,
                              CellData->u.KeyValue.Name,
                              Size);
            }

            /* We're all done */
            break;

        /* Full key information */
        case KeyValueFullInformation:
        case KeyValueFullInformationAlign64:

            /* Check if this is a small key and compute key size */
            IsSmall = CmpIsKeyValueSmall(&KeySize,
                                         CellData->u.KeyValue.DataLength);

            /* Calculate the total size required */
            Size = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name) +
                   NameSize +
                   KeySize;

            /* And this is the least we can work with */
            MinimumSize = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name);

            /* Check if there's any key data */
            if (KeySize > 0)
            {
                /* Calculate the data offset */
                DataOffset = Size - KeySize;

#ifdef _WIN64
                /* On 64-bit, always align to 8 bytes */
                AlignedData = ALIGN_UP(DataOffset, ULONGLONG);
#else
                /* On 32-bit, align the offset to 4 or 8 bytes */
                if (KeyValueInformationClass == KeyValueFullInformationAlign64)
                {
                    AlignedData = ALIGN_UP(DataOffset, ULONGLONG);
                }
                else
                {
                    AlignedData = ALIGN_UP(DataOffset, ULONG);
                }
#endif
                /* If alignment was required, we'll need more space */
                if (AlignedData > DataOffset) Size += (AlignedData-DataOffset);
            }

            /* Tell the caller the size we'll finally need, and set success */
            *ResultLength = Size;
            *Status = STATUS_SUCCESS;

            /* Check if the caller is giving us too little */
            if (Length < MinimumSize)
            {
                /* Then fail right now */
                *Status = STATUS_BUFFER_TOO_SMALL;
                break;
            }

            /* Fill out the basic information */
            Info->KeyValueFullInformation.TitleIndex = 0;
            Info->KeyValueFullInformation.Type = CellData->u.KeyValue.Type;
            Info->KeyValueFullInformation.DataLength = KeySize;
            Info->KeyValueFullInformation.NameLength = NameSize;

            /* Only the name is left now */
            SizeLeft = Length - MinimumSize;
            Size = NameSize;

            /* Check if the name fits */
            if (SizeLeft < Size)
            {
                /* It doesn't, truncate what we'll copy, and tell the caller */
                Size = SizeLeft;
                *Status = STATUS_BUFFER_OVERFLOW;
            }

            /* Check if this key value is compressed */
            if (CellData->u.KeyValue.Flags & VALUE_COMP_NAME)
            {
                /* It is, copy the compressed name */
                CmpCopyCompressedName(Info->KeyValueFullInformation.Name,
                                      Size,
                                      CellData->u.KeyValue.Name,
                                      CellData->u.KeyValue.NameLength);
            }
            else
            {
                /* It's not, copy the raw name */
                RtlCopyMemory(Info->KeyValueFullInformation.Name,
                              CellData->u.KeyValue.Name,
                              Size);
            }

            /* Now check if the key had any data */
            if (KeySize > 0)
            {
                /* Was it a small key? */
                if (IsSmall)
                {
                    /* Then the data is directly into the cell */
                    Buffer = &CellData->u.KeyValue.Data;
                }
                else
                {
                    /* Otherwise, we must retrieve it from the value cache */
                    Result = CmpGetValueDataFromCache(Kcb,
                                                      CachedValue,
                                                      CellData,
                                                      ValueIsCached,
                                                      &Buffer,
                                                      &BufferAllocated,
                                                      &CellToRelease);
                    if (Result != SearchSuccess)
                    {
                        /* We failed, nothing should be allocated */
                        ASSERT(Buffer == NULL);
                        ASSERT(BufferAllocated == FALSE);
                        *Status = STATUS_INSUFFICIENT_RESOURCES;
                    }
                }

                /* Now that we know we truly have data, set its offset */
                Info->KeyValueFullInformation.DataOffset = AlignedData;

                /* Only the data remains to be copied */
                SizeLeft = (((LONG)Length - (LONG)AlignedData) < 0) ?
                           0 : (Length - AlignedData);
                Size = KeySize;

                /* Check if the caller has no space for it */
                if (SizeLeft < Size)
                {
                    /* Truncate what we'll copy, and tell the caller */
                    Size = SizeLeft;
                    *Status = STATUS_BUFFER_OVERFLOW;
                }

                /* Sanity check */
                ASSERT((IsSmall ? (Size <= CM_KEY_VALUE_SMALL) : TRUE));

                /* Make sure we have a valid buffer */
                if (Buffer)
                {
                    /* Copy the data into the aligned offset */
                    RtlCopyMemory((PVOID)((ULONG_PTR)Info + AlignedData),
                                  Buffer,
                                  Size);
                }
            }
            else
            {
                /* We don't have any data, set the offset to -1, not 0! */
                Info->KeyValueFullInformation.DataOffset = 0xFFFFFFFF;
            }

            /* We're done! */
            break;

        /* Partial information requested (no name or alignment!) */
        case KeyValuePartialInformation:

            /* Check if this is a small key and compute key size */
            IsSmall = CmpIsKeyValueSmall(&KeySize,
                                         CellData->u.KeyValue.DataLength);

            /* Calculate the total size required */
            Size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + KeySize;

            /* And this is the least we can work with */
            MinimumSize = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);

            /* Tell the caller the size we'll finally need, and set success */
            *ResultLength = Size;
            *Status = STATUS_SUCCESS;

            /* Check if the caller is giving us too little */
            if (Length < MinimumSize)
            {
                /* Then fail right now */
                *Status = STATUS_BUFFER_TOO_SMALL;
                break;
            }

            /* Fill out the basic information */
            Info->KeyValuePartialInformation.TitleIndex = 0;
            Info->KeyValuePartialInformation.Type = CellData->u.KeyValue.Type;
            Info->KeyValuePartialInformation.DataLength = KeySize;

            /* Now check if the key had any data */
            if (KeySize > 0)
            {
                /* Was it a small key? */
                if (IsSmall)
                {
                    /* Then the data is directly into the cell */
                    Buffer = &CellData->u.KeyValue.Data;
                }
                else
                {
                    /* Otherwise, we must retrieve it from the value cache */
                    Result = CmpGetValueDataFromCache(Kcb,
                                                      CachedValue,
                                                      CellData,
                                                      ValueIsCached,
                                                      &Buffer,
                                                      &BufferAllocated,
                                                      &CellToRelease);
                    if (Result != SearchSuccess)
                    {
                        /* We failed, nothing should be allocated */
                        ASSERT(Buffer == NULL);
                        ASSERT(BufferAllocated == FALSE);
                        *Status = STATUS_INSUFFICIENT_RESOURCES;
                    }
                }

                /* Only the data remains to be copied */
                SizeLeft = Length - MinimumSize;
                Size = KeySize;

                /* Check if the caller has no space for it */
                if (SizeLeft < Size)
                {
                    /* Truncate what we'll copy, and tell the caller */
                    Size = SizeLeft;
                    *Status = STATUS_BUFFER_OVERFLOW;
                }

                /* Sanity check */
                ASSERT((IsSmall ? (Size <= CM_KEY_VALUE_SMALL) : TRUE));

                /* Make sure we have a valid buffer */
                if (Buffer)
                {
                    /* Copy the data into the aligned offset */
                    RtlCopyMemory(Info->KeyValuePartialInformation.Data,
                                  Buffer,
                                  Size);
                }
            }

            /* We're done! */
            break;

        /* Other information class */
        default:

            /* We got some class that we don't support */
            DPRINT1("Caller requested unknown class: %lx\n", KeyValueInformationClass);
            *Status = STATUS_INVALID_PARAMETER;
            break;
    }

    /* Return the search result as well */
    return Result;
}
Ejemplo n.º 3
0
NTSTATUS
CmpQueryKeyData(
    PHHIVE                  Hive,
    PCM_KEY_NODE            Node,
    KEY_INFORMATION_CLASS   KeyInformationClass,
    PVOID                   KeyInformation,
    ULONG                   Length,
    PULONG                  ResultLength
    )
/*++

Routine Description:

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

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

Arguments:

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

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

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

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

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

    KeyInformation -Supplies pointer to buffer to receive the data.

    Length - Length of KeyInformation in bytes.

    ResultLength - Number of bytes actually written into KeyInformation.

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS            status;
    PCELL_DATA          pclass;
    ULONG               requiredlength;
    LONG                leftlength;
    ULONG               offset;
    ULONG               minimumlength;
    PKEY_INFORMATION    pbuffer;
    USHORT              NameLength;

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

    switch (KeyInformationClass) {

    case KeyBasicInformation:

        //
        // LastWriteTime, TitleIndex, NameLength, Name
        //

        requiredlength = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name) +
                         NameLength;

        minimumlength = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name);

        *ResultLength = requiredlength;

        status = STATUS_SUCCESS;

        if (Length < minimumlength) {

            status = STATUS_BUFFER_TOO_SMALL;

        } else {

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

            pbuffer->KeyBasicInformation.TitleIndex = 0;

            pbuffer->KeyBasicInformation.NameLength =
                NameLength;

            leftlength = Length - minimumlength;

            requiredlength = NameLength;

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

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

        break;


    case KeyNodeInformation:
        //
        // LastWriteTime, TitleIndex, ClassOffset, ClassLength
        // NameLength, Name, Class
        //
        requiredlength = FIELD_OFFSET(KEY_NODE_INFORMATION, Name) +
                         NameLength +
                         Node->ClassLength;

        minimumlength = FIELD_OFFSET(KEY_NODE_INFORMATION, Name);

        *ResultLength = requiredlength;

        status = STATUS_SUCCESS;

        if (Length < minimumlength) {

            status = STATUS_BUFFER_TOO_SMALL;

        } else {

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

            pbuffer->KeyNodeInformation.TitleIndex = 0;

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

            pbuffer->KeyNodeInformation.NameLength =
                NameLength;

            leftlength = Length - minimumlength;
            requiredlength = NameLength;

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

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

            if (Node->ClassLength > 0) {

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

                pbuffer->KeyNodeInformation.ClassOffset = offset;

                pclass = HvGetCell(Hive, Node->Class);
                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:
        //
        // 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;
    }

    return status;
}