mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1421 lines
43 KiB
1421 lines
43 KiB
/*++
|
|
|
|
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;
|
|
}
|