- Implement NtQueryValueKey as a simple wrapper around CmQueryValueKey (same idea as always).

- Add cmvalche.c to deal with Value Caches. Cm implements these to quickly look up either the value list and/or the value index and/or the value data itself.  The routines fallback on non-cached access, which is what we currently do, since we don't have value caches yet.
- Implement CmQueryValueKey and CmpQueryKeyValueData based on previous code, but update to use value caches and new routines.

svn path=/trunk/; revision=26774
This commit is contained in:
Alex Ionescu 2007-05-14 05:36:25 +00:00
parent 6f9af1cef6
commit c045c30908
7 changed files with 908 additions and 254 deletions

View file

@ -48,6 +48,20 @@
#define IsNoFileHive(Hive) ((Hive)->Flags & HIVE_NO_FILE)
#define IsNoSynchHive(Hive) ((Hive)->Flags & HIVE_NO_SYNCH)
//
// Cached Child List
//
typedef struct _CACHED_CHILD_LIST
{
ULONG Count;
union
{
ULONG ValueList;
//struct _CM_KEY_CONTROL_BLOCK *RealKcb;
struct _KEY_OBJECT *RealKcb;
};
} CACHED_CHILD_LIST, *PCACHED_CHILD_LIST;
/* KEY_OBJECT.Flags */
/* When set, the key is scheduled for deletion, and all
@ -96,6 +110,8 @@ typedef struct _KEY_OBJECT
/* List entry for connected hives */
LIST_ENTRY HiveList;
CACHED_CHILD_LIST ValueCache;
} KEY_OBJECT, *PKEY_OBJECT;
/* Bits 31-22 (top 10 bits) of the cell index is the directory index */
@ -265,6 +281,15 @@ NTAPI
CmDeleteValueKey(IN PKEY_OBJECT KeyControlBlock,
IN UNICODE_STRING ValueName);
NTSTATUS
NTAPI
CmQueryValueKey(IN PKEY_OBJECT KeyObject,
IN UNICODE_STRING ValueName,
IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
IN PVOID KeyValueInformation,
IN ULONG Length,
IN PULONG ResultLength);
NTSTATUS
CmiAllocateHashTableCell(IN PEREGISTRY_HIVE RegistryHive,
OUT PHASH_TABLE_CELL *HashBlock,

View file

@ -385,6 +385,8 @@ NtCreateKey(OUT PHANDLE KeyHandle,
KeyObject->KeyCell->Parent = KeyObject->ParentKey->KeyCellOffset;
KeyObject->KeyCell->SecurityKeyOffset = KeyObject->ParentKey->KeyCell->SecurityKeyOffset;
KeyObject->ValueCache.ValueList = KeyObject->KeyCell->ValueList.List;
KeyObject->ValueCache.Count = KeyObject->KeyCell->ValueList.Count;
DPRINT("RemainingPath: %wZ\n", &RemainingPath);
@ -1536,267 +1538,57 @@ NtQueryKey(IN HANDLE KeyHandle,
return(Status);
}
NTSTATUS STDCALL
NTSTATUS
NTAPI
NtQueryValueKey(IN HANDLE KeyHandle,
IN PUNICODE_STRING ValueName,
IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
OUT PVOID KeyValueInformation,
IN ULONG Length,
OUT PULONG ResultLength)
IN PUNICODE_STRING ValueName,
IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
OUT PVOID KeyValueInformation,
IN ULONG Length,
OUT PULONG ResultLength)
{
NTSTATUS Status;
ULONG NameSize, DataSize;
PKEY_OBJECT KeyObject;
PEREGISTRY_HIVE RegistryHive;
PCM_KEY_NODE KeyCell;
PCM_KEY_VALUE ValueCell;
PVOID DataCell;
PKEY_VALUE_BASIC_INFORMATION ValueBasicInformation;
PKEY_VALUE_PARTIAL_INFORMATION ValuePartialInformation;
PKEY_VALUE_FULL_INFORMATION ValueFullInformation;
REG_QUERY_VALUE_KEY_INFORMATION QueryValueKeyInfo;
REG_POST_OPERATION_INFORMATION PostOperationInfo;
NTSTATUS Status;
PKEY_OBJECT KeyObject;
REG_QUERY_VALUE_KEY_INFORMATION QueryValueKeyInfo;
REG_POST_OPERATION_INFORMATION PostOperationInfo;
PAGED_CODE();
PAGED_CODE();
/* Verify that the handle is valid and is a registry key */
Status = ObReferenceObjectByHandle(KeyHandle,
KEY_QUERY_VALUE,
CmpKeyObjectType,
ExGetPreviousMode(),
(PVOID *)&KeyObject,
NULL);
if (!NT_SUCCESS(Status)) return Status;
DPRINT("NtQueryValueKey(KeyHandle 0x%p ValueName %S Length %x)\n",
KeyHandle, ValueName->Buffer, Length);
/* Setup the callback */
PostOperationInfo.Object = (PVOID)KeyObject;
QueryValueKeyInfo.Object = (PVOID)KeyObject;
QueryValueKeyInfo.ValueName = ValueName;
QueryValueKeyInfo.KeyValueInformationClass = KeyValueInformationClass;
QueryValueKeyInfo.Length = Length;
QueryValueKeyInfo.ResultLength = ResultLength;
/* Verify that the handle is valid and is a registry key */
Status = ObReferenceObjectByHandle(KeyHandle,
KEY_QUERY_VALUE,
CmpKeyObjectType,
ExGetPreviousMode(),
(PVOID *)&KeyObject,
NULL);
if (!NT_SUCCESS(Status))
/* Do the callback */
Status = CmiCallRegisteredCallbacks(RegNtPreQueryValueKey, &QueryValueKeyInfo);
if (NT_SUCCESS(Status))
{
DPRINT1("ObReferenceObjectByHandle() failed with status %x %p\n", Status, KeyHandle);
return Status;
}
PostOperationInfo.Object = (PVOID)KeyObject;
QueryValueKeyInfo.Object = (PVOID)KeyObject;
QueryValueKeyInfo.ValueName = ValueName;
QueryValueKeyInfo.KeyValueInformationClass = KeyValueInformationClass;
QueryValueKeyInfo.Length = Length;
QueryValueKeyInfo.ResultLength = ResultLength;
/* Call the internal API */
Status = CmQueryValueKey(KeyObject,
*ValueName,
KeyValueInformationClass,
KeyValueInformation,
Length,
ResultLength);
Status = CmiCallRegisteredCallbacks(RegNtPreQueryValueKey, &QueryValueKeyInfo);
if (!NT_SUCCESS(Status))
{
PostOperationInfo.Status = Status;
CmiCallRegisteredCallbacks(RegNtPostQueryValueKey, &PostOperationInfo);
ObDereferenceObject(KeyObject);
return Status;
/* Do the post callback */
PostOperationInfo.Status = Status;
CmiCallRegisteredCallbacks(RegNtPostQueryValueKey, &PostOperationInfo);
}
/* Acquire hive lock */
KeEnterCriticalRegion();
ExAcquireResourceSharedLite(&CmpRegistryLock, TRUE);
VERIFY_KEY_OBJECT(KeyObject);
/* Get pointer to KeyCell */
KeyCell = KeyObject->KeyCell;
RegistryHive = KeyObject->RegistryHive;
/* Get value cell by name */
Status = CmiScanKeyForValue(RegistryHive,
KeyCell,
ValueName,
&ValueCell,
NULL);
if (!NT_SUCCESS(Status))
{
DPRINT("CmiScanKeyForValue() failed with status %x\n", Status);
goto ByeBye;
}
Status = STATUS_SUCCESS;
switch (KeyValueInformationClass)
{
case KeyValueBasicInformation:
NameSize = ValueCell->NameSize;
if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
{
NameSize *= sizeof(WCHAR);
}
*ResultLength = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[0]) +
NameSize;
if (Length < FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[0]))
{
Status = STATUS_BUFFER_TOO_SMALL;
}
else
{
ValueBasicInformation = (PKEY_VALUE_BASIC_INFORMATION)
KeyValueInformation;
ValueBasicInformation->TitleIndex = 0;
ValueBasicInformation->Type = ValueCell->DataType;
ValueBasicInformation->NameLength = NameSize;
if (Length - FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[0]) <
NameSize)
{
NameSize = Length - FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[0]);
Status = STATUS_BUFFER_OVERFLOW;
CHECKPOINT;
}
if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
{
CmiCopyPackedName(ValueBasicInformation->Name,
ValueCell->Name,
NameSize / sizeof(WCHAR));
}
else
{
RtlCopyMemory(ValueBasicInformation->Name,
ValueCell->Name,
NameSize);
}
}
break;
case KeyValuePartialInformation:
DataSize = ValueCell->DataSize & REG_DATA_SIZE_MASK;
*ResultLength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[0]) +
DataSize;
if (Length < FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[0]))
{
Status = STATUS_BUFFER_TOO_SMALL;
}
else
{
ValuePartialInformation = (PKEY_VALUE_PARTIAL_INFORMATION)
KeyValueInformation;
ValuePartialInformation->TitleIndex = 0;
ValuePartialInformation->Type = ValueCell->DataType;
ValuePartialInformation->DataLength = DataSize;
if (Length - FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[0]) <
DataSize)
{
DataSize = Length - FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[0]);
Status = STATUS_BUFFER_OVERFLOW;
CHECKPOINT;
}
if (!(ValueCell->DataSize & REG_DATA_IN_OFFSET))
{
DataCell = HvGetCell (&RegistryHive->Hive, ValueCell->DataOffset);
RtlCopyMemory(ValuePartialInformation->Data,
DataCell,
DataSize);
}
else
{
RtlCopyMemory(ValuePartialInformation->Data,
&ValueCell->DataOffset,
DataSize);
}
}
break;
case KeyValueFullInformation:
NameSize = ValueCell->NameSize;
if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
{
NameSize *= sizeof(WCHAR);
}
DataSize = ValueCell->DataSize & REG_DATA_SIZE_MASK;
*ResultLength = ROUND_UP(FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION,
Name[0]) + NameSize, sizeof(PVOID)) + DataSize;
if (Length < FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name[0]))
{
Status = STATUS_BUFFER_TOO_SMALL;
}
else
{
ValueFullInformation = (PKEY_VALUE_FULL_INFORMATION)
KeyValueInformation;
ValueFullInformation->TitleIndex = 0;
ValueFullInformation->Type = ValueCell->DataType;
ValueFullInformation->NameLength = NameSize;
ValueFullInformation->DataOffset =
(ULONG_PTR)ValueFullInformation->Name -
(ULONG_PTR)ValueFullInformation +
ValueFullInformation->NameLength;
ValueFullInformation->DataOffset =
ROUND_UP(ValueFullInformation->DataOffset, sizeof(PVOID));
ValueFullInformation->DataLength = ValueCell->DataSize & REG_DATA_SIZE_MASK;
if (Length - FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name[0]) <
NameSize)
{
NameSize = Length - FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name[0]);
DataSize = 0;
Status = STATUS_BUFFER_OVERFLOW;
CHECKPOINT;
}
else if (ROUND_UP(Length - FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION,
Name[0]) - NameSize, sizeof(PVOID)) < DataSize)
{
DataSize = ROUND_UP(Length - FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION,
Name[0]) - NameSize, sizeof(PVOID));
Status = STATUS_BUFFER_OVERFLOW;
CHECKPOINT;
}
if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
{
CmiCopyPackedName(ValueFullInformation->Name,
ValueCell->Name,
NameSize / sizeof(WCHAR));
}
else
{
RtlCopyMemory(ValueFullInformation->Name,
ValueCell->Name,
NameSize);
}
if (!(ValueCell->DataSize & REG_DATA_IN_OFFSET))
{
DataCell = HvGetCell (&RegistryHive->Hive, ValueCell->DataOffset);
RtlCopyMemory((PCHAR) ValueFullInformation
+ ValueFullInformation->DataOffset,
DataCell,
DataSize);
}
else
{
RtlCopyMemory((PCHAR) ValueFullInformation
+ ValueFullInformation->DataOffset,
&ValueCell->DataOffset,
DataSize);
}
}
break;
default:
DPRINT1("Not handling 0x%x\n", KeyValueInformationClass);
Status = STATUS_INVALID_INFO_CLASS;
break;
}
ByeBye:;
ExReleaseResourceLite(&CmpRegistryLock);
KeLeaveCriticalRegion();
PostOperationInfo.Status = Status;
CmiCallRegisteredCallbacks(RegNtPostQueryValueKey, &PostOperationInfo);
ObDereferenceObject(KeyObject);
return Status;
ObDereferenceObject(KeyObject);
return Status;
}
NTSTATUS

View file

@ -157,6 +157,11 @@
#define CMP_CREATE_FAKE_KCB 0x1
#define CMP_LOCK_HASHES_FOR_KCB 0x2
//
// Maximum size of Value Cache
//
#define MAXIMUM_CACHED_DATA 2 * PAGE_SIZE
//
// Number of items that can fit inside an Allocation Page
//
@ -167,6 +172,16 @@
#ifndef __INCLUDE_CM_H
//
// Value Search Results
//
typedef enum _VALUE_SEARCH_RETURN_TYPE
{
SearchSuccess,
SearchNeedExclusiveLock,
SearchFail
} VALUE_SEARCH_RETURN_TYPE;
//
// Key Hash
//
@ -238,7 +253,8 @@ typedef struct _CACHED_CHILD_LIST
union
{
ULONG ValueList;
struct _CM_KEY_CONTROL_BLOCK *RealKcb;
//struct _CM_KEY_CONTROL_BLOCK *RealKcb;
struct _KEY_OBJECT *RealKcb;
};
} CACHED_CHILD_LIST, *PCACHED_CHILD_LIST;
@ -595,6 +611,7 @@ typedef struct _CM_CACHED_VALUE
{
USHORT DataCacheType;
USHORT ValueKeySize;
ULONG HashKey;
CM_KEY_VALUE KeyValue;
} CM_CACHED_VALUE, *PCM_CACHED_VALUE;
@ -644,6 +661,20 @@ typedef struct _CM_SYSTEM_CONTROL_VECTOR
PULONG Type;
} CM_SYSTEM_CONTROL_VECTOR, *PCM_SYSTEM_CONTROL_VECTOR;
//
// Structure for CmpQueryValueDataFromCache
//
typedef struct _KEY_VALUE_INFORMATION
{
union
{
KEY_VALUE_BASIC_INFORMATION KeyValueBasicInformation;
KEY_VALUE_FULL_INFORMATION KeyValueFullInformation;
KEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInformation;
KEY_VALUE_PARTIAL_INFORMATION_ALIGN64 KeyValuePartialInformationAlign64;
};
} KEY_VALUE_INFORMATION, *PKEY_VALUE_INFORMATION;
///////////////////////////////////////////////////////////////////////////////
//
// BUGBUG Old Hive Stuff for Temporary Support
@ -679,6 +710,7 @@ typedef struct _KEY_OBJECT
struct _KEY_OBJECT **SubKeys;
ULONG TimeStamp;
LIST_ENTRY HiveList;
CACHED_CHILD_LIST ValueCache;
} KEY_OBJECT, *PKEY_OBJECT;
extern PEREGISTRY_HIVE CmiVolatileHive;
extern LIST_ENTRY CmiKeyObjectListHead, CmiConnectedHiveList;
@ -708,6 +740,35 @@ CmpInitSecurityCache(
IN PCMHIVE Hive
);
//
// Value Cache Functions
//
VALUE_SEARCH_RETURN_TYPE
NTAPI
CmpFindValueByNameFromCache(
IN PKEY_OBJECT KeyObject,
IN PUNICODE_STRING Name,
OUT PCM_CACHED_VALUE **CachedValue,
OUT ULONG *Index,
OUT PCM_KEY_VALUE *Value,
OUT BOOLEAN *ValueIsCached,
OUT PHCELL_INDEX CellToRelease
);
VALUE_SEARCH_RETURN_TYPE
NTAPI
CmpQueryKeyValueData(
IN PKEY_OBJECT KeyObject,
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
);
//
// Registry Validation Functions
//
@ -953,6 +1014,22 @@ CmpNameSize(
IN PUNICODE_STRING Name
);
USHORT
NTAPI
CmpCompressedNameSize(
IN PWCHAR Name,
IN ULONG Length
);
VOID
NTAPI
CmpCopyCompressedName(
IN PWCHAR Destination,
IN ULONG DestinationLength,
IN PWCHAR Source,
IN ULONG SourceLength
);
USHORT
NTAPI
CmpCopyName(
@ -1002,6 +1079,14 @@ CmpFindSubKeyByName(
IN PUNICODE_STRING SearchName
);
ULONG
NTAPI
CmpComputeHashKey(
IN ULONG Hash,
IN PUNICODE_STRING Name,
IN BOOLEAN AllowSeparators
);
//
// Cell Value Routines
//
@ -1072,6 +1157,17 @@ CmpRemoveValueFromList(
IN OUT PCHILD_LIST ChildList
);
BOOLEAN
NTAPI
CmpGetValueData(
IN PHHIVE Hive,
IN PCM_KEY_VALUE Value,
IN PULONG Length,
OUT PVOID *Buffer,
OUT PBOOLEAN BufferAllocated,
OUT PHCELL_INDEX CellToRelease
);
//
// Boot Routines
//

View file

@ -433,7 +433,7 @@ CmDeleteValueKey(IN PKEY_OBJECT KeyObject,
Parent->MaxValueDataLen = 0;
}
/* Change default status to success */
/* Change default Status to success */
Status = STATUS_SUCCESS;
}
@ -454,3 +454,68 @@ Quickie:
KeLeaveCriticalRegion();
return Status;
}
NTSTATUS
NTAPI
CmQueryValueKey(IN PKEY_OBJECT KeyObject,
IN UNICODE_STRING ValueName,
IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
IN PVOID KeyValueInformation,
IN ULONG Length,
IN PULONG ResultLength)
{
NTSTATUS Status;
PCM_KEY_VALUE ValueData;
ULONG Index;
BOOLEAN ValueCached = FALSE;
PCM_CACHED_VALUE *CachedValue;
HCELL_INDEX CellToRelease;
VALUE_SEARCH_RETURN_TYPE Result;
PHHIVE Hive;
PAGED_CODE();
/* Acquire hive lock */
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&CmpRegistryLock, TRUE);
/* Get the hive */
Hive = &KeyObject->RegistryHive->Hive;
/* Find the key value */
Result = CmpFindValueByNameFromCache(KeyObject,
&ValueName,
&CachedValue,
&Index,
&ValueData,
&ValueCached,
&CellToRelease);
if (Result == SearchSuccess)
{
/* Sanity check */
ASSERT(ValueData != NULL);
/* Query the information requested */
Result = CmpQueryKeyValueData(KeyObject,
CachedValue,
ValueData,
ValueCached,
KeyValueInformationClass,
KeyValueInformation,
Length,
ResultLength,
&Status);
}
else
{
/* Failed to find the value */
Status = STATUS_OBJECT_NAME_NOT_FOUND;
}
/* If we have a cell to release, do so */
if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);
/* Release hive lock */
ExReleaseResourceLite(&CmpRegistryLock);
KeLeaveCriticalRegion();
return Status;
}

View file

@ -52,6 +52,24 @@ CmpCopyName(IN PHHIVE Hive,
return Source->Length / sizeof(WCHAR);
}
VOID
NTAPI
CmpCopyCompressedName(IN PWCHAR Destination,
IN ULONG DestinationLength,
IN PWCHAR Source,
IN ULONG SourceLength)
{
ULONG i, Length;
/* Get the actual length to copy */
Length = min(DestinationLength / sizeof(WCHAR), SourceLength);
for (i = 0; i < Length; i++)
{
/* Copy each character */
Destination[i] = (WCHAR)((PCHAR)Source)[i];
}
}
USHORT
NTAPI
CmpNameSize(IN PHHIVE Hive,
@ -73,6 +91,20 @@ CmpNameSize(IN PHHIVE Hive,
return Name->Length / sizeof(WCHAR);
}
USHORT
NTAPI
CmpCompressedNameSize(IN PWCHAR Name,
IN ULONG Length)
{
/*
* Don't remove this: compressed names are "opaque" and just because
* the current implementation turns them into ansi-names doesn't mean
* that it will remain that way forever, so -never- assume this code
* below internally!
*/
return Length * sizeof(WCHAR);
}
LONG
NTAPI
CmpCompareCompressedName(IN PUNICODE_STRING SearchName,

View file

@ -0,0 +1,643 @@
/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/config/cmvalche.c
* PURPOSE: Configuration Manager - Value Cell Cache
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
*/
/* INCLUDES ******************************************************************/
#include "ntoskrnl.h"
#include "cm.h"
#define NDEBUG
#include "debug.h"
FORCEINLINE
BOOLEAN
CmpIsValueCached(IN HCELL_INDEX CellIndex)
{
/* Make sure that the cell is valid in the first place */
if (CellIndex == HCELL_NIL) return FALSE;
/*Is this cell actually a pointer to the cached value data? */
if (CellIndex & 1) return TRUE;
/* This is a regular cell */
return FALSE;
}
FORCEINLINE
VOID
CmpSetValueCached(IN PHCELL_INDEX CellIndex)
{
/* Set the cached bit */
*CellIndex |= 1;
}
#define ASSERT_VALUE_CACHE() \
ASSERTMSG("Cached Values Not Yet Supported!", FALSE);
/* FUNCTIONS *****************************************************************/
VALUE_SEARCH_RETURN_TYPE
NTAPI
CmpGetValueListFromCache(IN PKEY_OBJECT KeyObject,
OUT PCELL_DATA *CellData,
OUT BOOLEAN *IndexIsCached,
OUT PHCELL_INDEX ValueListToRelease)
{
PHHIVE Hive;
PCACHED_CHILD_LIST ChildList;
HCELL_INDEX CellToRelease;
/* Set defaults */
*ValueListToRelease = HCELL_NIL;
*IndexIsCached = FALSE;
/* Get the hive */
Hive = &KeyObject->RegistryHive->Hive;
/* Get the child value cache */
//ChildList = &KeyObject->ValueCache;
ChildList = (PCACHED_CHILD_LIST)&KeyObject->KeyCell->ValueList;
/* Check if the value is cached */
if (CmpIsValueCached(ChildList->ValueList))
{
/* It is: we don't expect this yet! */
ASSERT_VALUE_CACHE();
*IndexIsCached = TRUE;
*CellData = NULL;
}
else
{
/* Select the value list as our cell, and get the actual list array */
CellToRelease = ChildList->ValueList;
*CellData = (PCELL_DATA)HvGetCell(Hive, CellToRelease);
if (!(*CellData)) return SearchFail;
/* Return the cell to be released */
*ValueListToRelease = CellToRelease;
}
/* If we got here, then the value list was found */
return SearchSuccess;
}
VALUE_SEARCH_RETURN_TYPE
NTAPI
CmpGetValueKeyFromCache(IN PKEY_OBJECT KeyObject,
IN PCELL_DATA CellData,
IN ULONG Index,
OUT PCM_CACHED_VALUE **CachedValue,
OUT PCM_KEY_VALUE *Value,
IN BOOLEAN IndexIsCached,
OUT BOOLEAN *ValueIsCached,
OUT PHCELL_INDEX CellToRelease)
{
PHHIVE Hive;
PCM_KEY_VALUE KeyValue;
HCELL_INDEX Cell;
/* Set defaults */
*CellToRelease = HCELL_NIL;
*Value = NULL;
*ValueIsCached = FALSE;
/* Get the hive */
Hive = &KeyObject->RegistryHive->Hive;
/* Check if the index was cached */
if (IndexIsCached)
{
/* Not expected yet! */
ASSERT_VALUE_CACHE();
*ValueIsCached = TRUE;
}
else
{
/* Get the cell index and the key value associated to it */
Cell = CellData->u.KeyList[Index];
KeyValue = (PCM_KEY_VALUE)HvGetCell(Hive, Cell);
if (!KeyValue) return SearchFail;
/* Return the cell and the actual key value */
*CellToRelease = Cell;
*Value = KeyValue;
}
/* If we got here, then we found the key value */
return SearchSuccess;
}
VALUE_SEARCH_RETURN_TYPE
NTAPI
CmpGetValueDataFromCache(IN PKEY_OBJECT KeyObject,
IN PCM_CACHED_VALUE *CachedValue,
IN PCELL_DATA ValueKey,
IN BOOLEAN ValueIsCached,
OUT PVOID *DataPointer,
OUT PBOOLEAN Allocated,
OUT PHCELL_INDEX CellToRelease)
{
PHHIVE Hive;
ULONG Length;
/* Sanity checks */
ASSERT(MAXIMUM_CACHED_DATA < CM_KEY_VALUE_BIG);
ASSERT((ValueKey->u.KeyValue.DataLength & CM_KEY_VALUE_SPECIAL_SIZE) == 0);
/* Set defaults */
*DataPointer = NULL;
*Allocated = FALSE;
*CellToRelease = HCELL_NIL;
/* Get the hive */
Hive = &KeyObject->RegistryHive->Hive;
/* Check it the value is cached */
if (ValueIsCached)
{
/* This isn't expected! */
ASSERT_VALUE_CACHE();
}
else
{
/* It's not, get the value data using the typical routine */
if (!CmpGetValueData(Hive,
&ValueKey->u.KeyValue,
&Length,
DataPointer,
Allocated,
CellToRelease))
{
/* Nothing found: make sure no data was allocated */
ASSERT(*Allocated == FALSE);
ASSERT(*DataPointer == NULL);
return SearchFail;
}
}
/* We found the actual data, return success */
return SearchSuccess;
}
VALUE_SEARCH_RETURN_TYPE
NTAPI
CmpFindValueByNameFromCache(IN PKEY_OBJECT KeyObject,
IN PUNICODE_STRING Name,
OUT PCM_CACHED_VALUE **CachedValue,
OUT ULONG *Index,
OUT PCM_KEY_VALUE *Value,
OUT BOOLEAN *ValueIsCached,
OUT PHCELL_INDEX CellToRelease)
{
PHHIVE Hive;
VALUE_SEARCH_RETURN_TYPE SearchResult = SearchFail;
LONG Result;
UNICODE_STRING SearchName;
PCELL_DATA CellData;
PCACHED_CHILD_LIST ChildList;
PCM_KEY_VALUE KeyValue;
BOOLEAN IndexIsCached;
ULONG i = 0;
HCELL_INDEX Cell = HCELL_NIL;
/* Set defaults */
*CellToRelease = HCELL_NIL;
*Value = NULL;
/* Get the hive and child list */
Hive = &KeyObject->RegistryHive->Hive;
//ChildList = &KeyObject->ValueCache;
ChildList = (PCACHED_CHILD_LIST)&KeyObject->KeyCell->ValueList;
/* Check if the child list has any entries */
if (ChildList->Count != 0)
{
/* Get the value list associated to this child list */
SearchResult = CmpGetValueListFromCache(KeyObject,
&CellData,
&IndexIsCached,
&Cell);
if (SearchResult != SearchSuccess) return SearchResult;
/* The index shouldn't be cached right now */
if (IndexIsCached) ASSERT_VALUE_CACHE();
/* Loop every value */
while (TRUE)
{
/* Check if there's any cell to release */
if (*CellToRelease != HCELL_NIL)
{
/* Release it now */
HvReleaseCell(Hive, *CellToRelease);
*CellToRelease = HCELL_NIL;
}
/* Get the key value for this index */
SearchResult = CmpGetValueKeyFromCache(KeyObject,
CellData,
i,
CachedValue,
Value,
IndexIsCached,
ValueIsCached,
CellToRelease);
if (SearchResult != SearchSuccess) return SearchResult;
/* Check if the both the index and the value are cached */
if ((IndexIsCached) && (*ValueIsCached))
{
/* We don't expect this yet */
ASSERT_VALUE_CACHE();
Result = -1;
}
else
{
/* No cache, so try to compare the name. Is it compressed? */
KeyValue = *Value;
if (KeyValue->Flags & VALUE_COMP_NAME)
{
/* It is, do a compressed name comparison */
Result = CmpCompareCompressedName(Name,
KeyValue->Name,
KeyValue->NameLength);
}
else
{
/* It's not compressed, so do a standard comparison */
SearchName.Length = KeyValue->NameLength;
SearchName.MaximumLength = SearchName.Length;
SearchName.Buffer = KeyValue->Name;
Result = RtlCompareUnicodeString(Name, &SearchName, TRUE);
}
}
/* Check if we found the value data */
if (!Result)
{
/* We have, return the index of the value and success */
*Index = i;
SearchResult = SearchSuccess;
goto Quickie;
}
/* We didn't find it, try the next entry */
if (++i == ChildList->Count)
{
/* The entire list was parsed, fail */
*Value = NULL;
SearchResult = SearchFail;
goto Quickie;
}
}
}
/* We should only get here if the child list is empty */
ASSERT(ChildList->Count == 0);
Quickie:
/* Release the value list cell if required, and return search result */
if (Cell != HCELL_NIL) HvReleaseCell(Hive, Cell);
return SearchResult;
}
VALUE_SEARCH_RETURN_TYPE
NTAPI
CmpQueryKeyValueData(IN PKEY_OBJECT KeyObject,
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)
{
PHHIVE Hive;
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 hive and cell data */
Hive = &KeyObject->RegistryHive->Hive;
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:
/* 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;
/* Align the offset to 4 bytes */
AlignedData = ALIGN_UP(DataOffset, ULONG);
/* 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(KeyObject,
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 = max(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(KeyObject,
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 */
*Status = STATUS_INVALID_PARAMETER;
break;
}
/* Return the search result as well */
return Result;
}

View file

@ -106,6 +106,7 @@
<file>cmsecach.c</file>
<file>cmsysini.c</file>
<file>cmvalue.c</file>
<file>cmvalche.c</file>
<file>cmwraprs.c</file>
</directory>
<directory name="cm">