Windows2000/private/ntos/config/cmsubs2.c

902 lines
24 KiB
C
Raw Normal View History

2001-01-01 00:00:00 +01:00
/*++
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"
PUCHAR
CmpGetValueDataFromCache(
IN PHHIVE Hive,
IN PPCM_CACHED_VALUE ContainingList,
IN PCELL_DATA ValueKey,
IN BOOLEAN ValueCached
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,CmpGetValueDataFromCache)
#pragma alloc_text(PAGE,CmpFreeValue)
#pragma alloc_text(PAGE,CmpQueryKeyData)
#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)))
VOID
CmpFreeValue(
PHHIVE Hive,
HCELL_INDEX Cell
)
/*++
Routine Description:
Free the value entry Hive.Cell refers to, including
its name and data cells.
Arguments:
Hive - supplies a pointer to the hive control structure for the hive
Cell - supplies index of value to delete
Return Value:
NTSTATUS - Result code from call, among the following:
<TBS>
--*/
{
PCELL_DATA value;
ULONG realsize;
BOOLEAN small;
// map in the cell
value = HvGetCell(Hive, Cell);
// free data if present
small = CmpIsHKeyValueSmall(realsize, value->u.KeyValue.DataLength);
if ((! small) && (realsize > 0))
{
HvFreeCell(Hive, value->u.KeyValue.Data);
}
// free the cell itself
HvFreeCell(Hive, Cell);
return;
}
// Data transfer workers
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 - 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;
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);
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;
}
RtlMoveMemory(
pbuffer,
pclass,
requiredlength
);
} 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);
leftlength = Length - minimumlength;
requiredlength = Node->ClassLength;
if (leftlength < (LONG)requiredlength) {
requiredlength = leftlength;
status = STATUS_BUFFER_OVERFLOW;
}
RtlMoveMemory(
&(pbuffer->KeyFullInformation.Class[0]),
pclass,
requiredlength
);
} 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;
}
PUCHAR
CmpGetValueDataFromCache(
IN PHHIVE Hive,
IN PPCM_CACHED_VALUE ContainingList,
IN PCELL_DATA ValueKey,
IN BOOLEAN ValueCached
)
/*++
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.
Return Value:
Pointer to the Valve Index.
--*/
{
// Cache the data if needed.
PCM_CACHED_VALUE OldEntry;
PCM_CACHED_VALUE NewEntry;
PUCHAR datapointer;
PUCHAR Cacheddatapointer;
ULONG AllocSize;
ULONG CopySize;
ULONG DataSize;
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) {
// Data is too big to warrent caching, get it from the registry.
datapointer = (PUCHAR)HvGetCell(Hive, ValueKey->u.KeyValue.Data);
} 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.
datapointer = (PUCHAR)HvGetCell(Hive, ValueKey->u.KeyValue.Data);
DataSize = (ULONG) HvGetCellSize(Hive, datapointer);
if (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 {
// Trying to catch the BAD guy who writes over our pool.
CmpMakeSpecialPoolReadWrite( OldEntry );
// Mark the type and do not cache it.
OldEntry->DataCacheType = CM_CACHE_DATA_TOO_BIG;
// Trying to catch the BAD guy who writes over our pool.
CmpMakeSpecialPoolReadOnly( OldEntry );
}
}
} else {
datapointer = (PUCHAR)HvGetCell(Hive, ValueKey->u.KeyValue.Data);
}
return (datapointer);
}
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;
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)))) {
KdPrint(("ntos/config-64 KeyValueFullInformation: "
"Possible client buffer size problem.\n"));
KdPrint((" Required size = %d\n", requiredlength));
KdPrint((" 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 {
RtlMoveMemory(
&(pbuffer->KeyValueFullInformation.Name[0]),
&(pcell->u.KeyValue.Name[0]),
requiredlength
);
}
if (realsize > 0) {
if (small == TRUE) {
datapointer = (PUCHAR)(&(pcell->u.KeyValue.Data));
} else {
datapointer = CmpGetValueDataFromCache(Hive, ContainingList, pcell, ValueCached);
}
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));
RtlMoveMemory(
((PUCHAR)pbuffer + offset),
datapointer,
requiredlength
);
} 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 {
datapointer = CmpGetValueDataFromCache(Hive, ContainingList, pcell, ValueCached);
}
ASSERT((small ? (requiredlength <= CM_KEY_VALUE_SMALL) : TRUE));
RtlMoveMemory((PUCHAR)&(pbuffer->KeyValuePartialInformation.Data[0]),
datapointer,
requiredlength);
}
}
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 {
datapointer = CmpGetValueDataFromCache(Hive, ContainingList, pcell, ValueCached);
}
ASSERT((small ? (requiredlength <= CM_KEY_VALUE_SMALL) : TRUE));
RtlMoveMemory((PUCHAR)&(pbuffer->KeyValuePartialInformationAlign64.Data[0]),
datapointer,
requiredlength);
}
}
break;
default:
status = STATUS_INVALID_PARAMETER;
break;
}
return status;
}