-/*\r
- * PROJECT: ReactOS Kernel\r
- * LICENSE: GPL - See COPYING in the top level directory\r
- * FILE: ntoskrnl/config/cmapi.c\r
- * PURPOSE: Configuration Manager - Internal Registry APIs\r
- * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)\r
- */\r
-\r
-/* INCLUDES ******************************************************************/\r
-\r
-#include "ntoskrnl.h"\r
-#include "cm.h"\r
-#define NDEBUG\r
-#include "debug.h"\r
-\r
-/* FUNCTIONS *****************************************************************/\r
-\r
-NTSTATUS\r
-NTAPI\r
-CmpSetValueKeyNew(IN PHHIVE Hive,\r
- IN PCM_KEY_NODE Parent,\r
- IN PUNICODE_STRING ValueName,\r
- IN ULONG Index,\r
- IN ULONG Type,\r
- IN PVOID Data,\r
- IN ULONG DataSize,\r
- IN ULONG StorageType,\r
- IN ULONG SmallData)\r
-{\r
- PCELL_DATA CellData;\r
- HCELL_INDEX ValueCell;\r
- NTSTATUS Status;\r
-\r
- /* Check if we already have a value list */\r
- if (Parent->ValueList.Count)\r
- {\r
- /* Then make sure it's valid and dirty it */\r
- ASSERT(Parent->ValueList.List != HCELL_NIL);\r
- HvMarkCellDirty(Hive, Parent->ValueList.List);\r
- }\r
-\r
- /* Allocate avalue cell */\r
- ValueCell = HvAllocateCell(Hive,\r
- FIELD_OFFSET(CM_KEY_VALUE, Name) +\r
- CmpNameSize(Hive, ValueName),\r
- StorageType);\r
- if (ValueCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;\r
-\r
- /* Get the actual data for it */\r
- CellData = HvGetCell(Hive, ValueCell);\r
- if (!CellData) ASSERT(FALSE);\r
-\r
- /* Now we can release it, make sure it's also dirty */\r
- HvReleaseCell(Hive, ValueCell);\r
- ASSERT(HvIsCellDirty(Hive, ValueCell));\r
-\r
- /* Set it up and copy the name */\r
- CellData->u.KeyValue.Signature = CM_KEY_VALUE_SIGNATURE;\r
- CellData->u.KeyValue.Flags = 0;\r
- CellData->u.KeyValue.Type = Type;\r
- CellData->u.KeyValue.NameLength = CmpCopyName(Hive,\r
- CellData->u.KeyValue.Name,\r
- ValueName);\r
- if (CellData->u.KeyValue.NameLength < ValueName->Length)\r
- {\r
- /* This is a compressed name */\r
- CellData->u.KeyValue.Flags = VALUE_COMP_NAME;\r
- }\r
-\r
- /* Check if this is a normal key */\r
- if (DataSize > CM_KEY_VALUE_SMALL)\r
- {\r
- /* Build a data cell for it */\r
- Status = CmpSetValueDataNew(Hive,\r
- Data,\r
- DataSize,\r
- StorageType,\r
- ValueCell,\r
- &CellData->u.KeyValue.Data);\r
- if (!NT_SUCCESS(Status))\r
- {\r
- /* We failed, free the cell */\r
- HvFreeCell(Hive, ValueCell);\r
- return Status;\r
- }\r
-\r
- /* Otherwise, set the data length, and make sure the data is dirty */\r
- CellData->u.KeyValue.DataLength = DataSize;\r
- ASSERT(HvIsCellDirty(Hive, CellData->u.KeyValue.Data));\r
- }\r
- else\r
- {\r
- /* This is a small key, set the data directly inside */\r
- CellData->u.KeyValue.DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE;\r
- CellData->u.KeyValue.Data = SmallData;\r
- }\r
-\r
- /* Add this value cell to the child list */\r
- Status = CmpAddValueToList(Hive,\r
- ValueCell,\r
- Index,\r
- StorageType,\r
- &Parent->ValueList);\r
-\r
- /* If we failed, free the entire cell, including the data */\r
- if (!NT_SUCCESS(Status)) CmpFreeValue(Hive, ValueCell);\r
-\r
- /* Return Status */\r
- return Status;\r
-}\r
-\r
-NTSTATUS\r
-NTAPI\r
-CmpSetValueKeyExisting(IN PHHIVE Hive,\r
- IN HCELL_INDEX OldChild,\r
- IN PCM_KEY_VALUE Value,\r
- IN ULONG Type,\r
- IN PVOID Data,\r
- IN ULONG DataSize,\r
- IN ULONG StorageType,\r
- IN ULONG TempData)\r
-{\r
- HCELL_INDEX DataCell, NewCell;\r
- PCELL_DATA CellData;\r
- ULONG Length;\r
- BOOLEAN WasSmall, IsSmall;\r
-\r
- /* Mark the old child cell dirty */\r
- HvMarkCellDirty(Hive, OldChild);\r
-\r
- /* See if this is a small or normal key */\r
- WasSmall = CmpIsKeyValueSmall(&Length, Value->DataLength);\r
-\r
- /* See if our new data can fit in a small key */\r
- IsSmall = (DataSize <= CM_KEY_VALUE_SMALL) ? TRUE: FALSE;\r
-\r
- /* Big keys are unsupported */\r
- ASSERT_VALUE_BIG(Hive, Length);\r
- ASSERT_VALUE_BIG(Hive, DataSize);\r
-\r
- /* Mark the old value dirty */\r
- CmpMarkValueDataDirty(Hive, Value);\r
-\r
- /* Check if we have a small key */\r
- if (IsSmall)\r
- {\r
- /* Check if we had a normal key with some data in it */\r
- if (!(WasSmall) && (Length > 0))\r
- {\r
- /* Free the previous data */\r
- CmpFreeValueData(Hive, Value->Data, Length);\r
- }\r
-\r
- /* Write our data directly */\r
- Value->DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE;\r
- Value->Data = TempData;\r
- Value->Type = Type;\r
- return STATUS_SUCCESS;\r
- }\r
- else\r
- {\r
- /* We have a normal key. Was the old cell also normal and had data? */\r
- if (!(WasSmall) && (Length > 0))\r
- {\r
- /* Get the current data cell and actual data inside it */\r
- DataCell = Value->Data;\r
- ASSERT(DataCell != HCELL_NIL);\r
- CellData = HvGetCell(Hive, DataCell);\r
- if (!CellData) return STATUS_INSUFFICIENT_RESOURCES;\r
-\r
- /* Immediately release the cell */\r
- HvReleaseCell(Hive, DataCell);\r
-\r
- /* Make sure that the data cell actually has a size */\r
- ASSERT(HvGetCellSize(Hive, CellData) > 0);\r
-\r
- /* Check if the previous data cell could fit our new data */\r
- if (DataSize <= (ULONG)(HvGetCellSize(Hive, CellData)))\r
- {\r
- /* Re-use it then */\r
- NewCell = DataCell;\r
- }\r
- else\r
- {\r
- /* Otherwise, re-allocate the current data cell */\r
- NewCell = HvReallocateCell(Hive, DataCell, DataSize);\r
- if (NewCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;\r
- }\r
- }\r
- else\r
- {\r
- /* This was a small key, or a key with no data, allocate a cell */\r
- NewCell = HvAllocateCell(Hive, DataSize, StorageType);\r
- if (NewCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;\r
- }\r
-\r
- /* Now get the actual data for our data cell */\r
- CellData = HvGetCell(Hive, NewCell);\r
- if (!CellData) ASSERT(FALSE);\r
-\r
- /* Release it immediately */\r
- HvReleaseCell(Hive, NewCell);\r
-\r
- /* Copy our data into the data cell's buffer, and set up the value */\r
- RtlCopyMemory(CellData, Data, DataSize);\r
- Value->Data = NewCell;\r
- Value->DataLength = DataSize;\r
- Value->Type = Type;\r
-\r
- /* Return success */\r
- ASSERT(HvIsCellDirty(Hive, NewCell));\r
- return STATUS_SUCCESS;\r
- }\r
-}\r
-\r
-NTSTATUS\r
-NTAPI\r
-CmSetValueKey(IN PKEY_OBJECT KeyObject,\r
- IN PUNICODE_STRING ValueName,\r
- IN ULONG Type,\r
- IN PVOID Data,\r
- IN ULONG DataLength)\r
-{\r
- PHHIVE Hive;\r
- PCM_KEY_NODE Parent;\r
- PCM_KEY_VALUE Value = NULL;\r
- HCELL_INDEX CurrentChild, Cell;\r
- NTSTATUS Status;\r
- BOOLEAN Found, Result;\r
- ULONG Count, ChildIndex, SmallData, Storage;\r
-\r
- /* Acquire hive lock exclusively */\r
- KeEnterCriticalRegion();\r
- ExAcquireResourceExclusiveLite(&CmpRegistryLock, TRUE);\r
-\r
- /* Get pointer to key cell */\r
- Parent = KeyObject->KeyCell;\r
- Hive = &KeyObject->RegistryHive->Hive;\r
- Cell = KeyObject->KeyCellOffset;\r
-\r
- /* Prepare to scan the key node */\r
- Count = Parent->ValueList.Count;\r
- Found = FALSE;\r
- if (Count > 0)\r
- {\r
- /* Try to find the existing name */\r
- Result = CmpFindNameInList(Hive,\r
- &Parent->ValueList,\r
- ValueName,\r
- &ChildIndex,\r
- &CurrentChild);\r
- if (!Result)\r
- {\r
- /* Fail */\r
- Status = STATUS_INSUFFICIENT_RESOURCES;\r
- goto Quickie;\r
- }\r
-\r
- /* Check if we found something */\r
- if (CurrentChild != HCELL_NIL)\r
- {\r
- /* Get its value */\r
- Value = (PCM_KEY_VALUE)HvGetCell(Hive, CurrentChild);\r
- if (!Value)\r
- {\r
- /* Fail */\r
- Status = STATUS_INSUFFICIENT_RESOURCES;\r
- goto Quickie;\r
- }\r
-\r
- /* Remember that we found it */\r
- Found = TRUE;\r
- }\r
- }\r
- else\r
- {\r
- /* No child list, we'll need to add it */\r
- ChildIndex = 0;\r
- }\r
-\r
- /* Mark the cell dirty */\r
- HvMarkCellDirty(Hive, Cell);\r
-\r
- /* Get the storage type */\r
- Storage = HvGetCellType(Cell);\r
-\r
- /* Check if this is small data */\r
- SmallData = 0;\r
- if ((DataLength <= CM_KEY_VALUE_SMALL) && (DataLength > 0))\r
- {\r
- /* Copy it */\r
- RtlCopyMemory(&SmallData, Data, DataLength);\r
- }\r
-\r
- /* Check if we didn't find a matching key */\r
- if (!Found)\r
- {\r
- /* Call the internal routine */\r
- Status = CmpSetValueKeyNew(Hive,\r
- Parent,\r
- ValueName,\r
- ChildIndex,\r
- Type,\r
- Data,\r
- DataLength,\r
- Storage,\r
- SmallData);\r
- }\r
- else\r
- {\r
- /* Call the internal routine */\r
- Status = CmpSetValueKeyExisting(Hive,\r
- CurrentChild,\r
- Value,\r
- Type,\r
- Data,\r
- DataLength,\r
- Storage,\r
- SmallData);\r
- }\r
-\r
- /* Mark link key */\r
- if ((Type == REG_LINK) &&\r
- (_wcsicmp(ValueName->Buffer, L"SymbolicLinkValue") == 0))\r
- {\r
- Parent->Flags |= KEY_SYM_LINK;\r
- }\r
-\r
- /* Check for success */\r
-Quickie:\r
- if (NT_SUCCESS(Status))\r
- {\r
- /* Save the write time */\r
- KeQuerySystemTime(&Parent->LastWriteTime);\r
- }\r
-\r
- /* Release the lock */\r
- ExReleaseResourceLite(&CmpRegistryLock);\r
- KeLeaveCriticalRegion();\r
- return Status;\r
-}\r
-\r
-NTSTATUS\r
-NTAPI\r
-CmDeleteValueKey(IN PKEY_OBJECT KeyObject,\r
- IN UNICODE_STRING ValueName)\r
-{\r
- NTSTATUS Status = STATUS_OBJECT_NAME_NOT_FOUND;\r
- PHHIVE Hive;\r
- PCM_KEY_NODE Parent;\r
- HCELL_INDEX ChildCell, Cell;\r
- PCHILD_LIST ChildList;\r
- PCM_KEY_VALUE Value = NULL;\r
- ULONG ChildIndex;\r
- BOOLEAN Result;\r
-\r
- /* Acquire hive lock */\r
- KeEnterCriticalRegion();\r
- ExAcquireResourceExclusiveLite(&CmpRegistryLock, TRUE);\r
-\r
- /* Get the hive and the cell index */\r
- Hive = &KeyObject->RegistryHive->Hive;\r
- Cell = KeyObject->KeyCellOffset;\r
-\r
- /* Get the parent key node */\r
- Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell);\r
- if (!Parent)\r
- {\r
- /* Fail */\r
- Status = STATUS_INSUFFICIENT_RESOURCES;\r
- goto Quickie;\r
- }\r
-\r
- /* Get the value list and check if it has any entries */\r
- ChildList = &Parent->ValueList;\r
- if (ChildList->Count)\r
- {\r
- /* Try to find this value */\r
- Result = CmpFindNameInList(Hive,\r
- ChildList,\r
- &ValueName,\r
- &ChildIndex,\r
- &ChildCell);\r
- if (!Result)\r
- {\r
- /* Fail */\r
- Status = STATUS_INSUFFICIENT_RESOURCES;\r
- goto Quickie;\r
- }\r
-\r
- /* Value not found, return error */\r
- if (ChildCell == HCELL_NIL) goto Quickie;\r
-\r
- /* We found the value, mark all relevant cells dirty */\r
- HvMarkCellDirty(Hive, Cell);\r
- HvMarkCellDirty(Hive, Parent->ValueList.List);\r
- HvMarkCellDirty(Hive, ChildCell);\r
-\r
- /* Get the key value */\r
- Value = (PCM_KEY_VALUE)HvGetCell(Hive,ChildCell);\r
- if (!Value) ASSERT(FALSE);\r
-\r
- /* Mark it and all related data as dirty */\r
- CmpMarkValueDataDirty(Hive, Value);\r
-\r
- /* Ssanity checks */\r
- ASSERT(HvIsCellDirty(Hive, Parent->ValueList.List));\r
- ASSERT(HvIsCellDirty(Hive, ChildCell));\r
-\r
- /* Remove the value from the child list */\r
- Status = CmpRemoveValueFromList(Hive, ChildIndex, ChildList);\r
- if(!NT_SUCCESS(Status)) goto Quickie;\r
-\r
- /* Remove the value and its data itself */\r
- if (!CmpFreeValue(Hive, ChildCell))\r
- {\r
- /* Failed to free the value, fail */\r
- Status = STATUS_INSUFFICIENT_RESOURCES;\r
- goto Quickie;\r
- }\r
-\r
- /* Set the last write time */\r
- KeQuerySystemTime(&Parent->LastWriteTime);\r
-\r
- /* Sanity check */\r
- ASSERT(HvIsCellDirty(Hive, Cell));\r
-\r
- /* Check if the value list is empty now */\r
- if (!Parent->ValueList.Count)\r
- {\r
- /* Then clear key node data */\r
- Parent->MaxValueNameLen = 0;\r
- Parent->MaxValueDataLen = 0;\r
- }\r
-\r
- /* Change default Status to success */\r
- Status = STATUS_SUCCESS;\r
- }\r
-\r
-Quickie:\r
- /* Release the parent cell, if any */\r
- if (Parent) HvReleaseCell(Hive, Cell);\r
-\r
- /* Check if we had a value */\r
- if (Value)\r
- {\r
- /* Release the child cell */\r
- ASSERT(ChildCell != HCELL_NIL);\r
- HvReleaseCell(Hive, ChildCell);\r
- }\r
-\r
- /* Release hive lock */\r
- ExReleaseResourceLite(&CmpRegistryLock);\r
- KeLeaveCriticalRegion();\r
- return Status;\r
-}\r
-\r
-NTSTATUS\r
-NTAPI\r
-CmQueryValueKey(IN PKEY_OBJECT KeyObject,\r
- IN UNICODE_STRING ValueName,\r
- IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,\r
- IN PVOID KeyValueInformation,\r
- IN ULONG Length,\r
- IN PULONG ResultLength)\r
-{\r
- NTSTATUS Status;\r
- PCM_KEY_VALUE ValueData;\r
- ULONG Index;\r
- BOOLEAN ValueCached = FALSE;\r
- PCM_CACHED_VALUE *CachedValue;\r
- HCELL_INDEX CellToRelease;\r
- VALUE_SEARCH_RETURN_TYPE Result;\r
- PHHIVE Hive;\r
- PAGED_CODE();\r
-\r
- /* Acquire hive lock */\r
- KeEnterCriticalRegion();\r
- ExAcquireResourceExclusiveLite(&CmpRegistryLock, TRUE);\r
-\r
- /* Get the hive */\r
- Hive = &KeyObject->RegistryHive->Hive;\r
-\r
- /* Find the key value */\r
- Result = CmpFindValueByNameFromCache(KeyObject,\r
- &ValueName,\r
- &CachedValue,\r
- &Index,\r
- &ValueData,\r
- &ValueCached,\r
- &CellToRelease);\r
- if (Result == SearchSuccess)\r
- {\r
- /* Sanity check */\r
- ASSERT(ValueData != NULL);\r
-\r
- /* Query the information requested */\r
- Result = CmpQueryKeyValueData(KeyObject,\r
- CachedValue,\r
- ValueData,\r
- ValueCached,\r
- KeyValueInformationClass,\r
- KeyValueInformation,\r
- Length,\r
- ResultLength,\r
- &Status);\r
- }\r
- else\r
- {\r
- /* Failed to find the value */\r
- Status = STATUS_OBJECT_NAME_NOT_FOUND;\r
- }\r
-\r
- /* If we have a cell to release, do so */\r
- if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);\r
-\r
- /* Release hive lock */\r
- ExReleaseResourceLite(&CmpRegistryLock);\r
- KeLeaveCriticalRegion();\r
- return Status;\r
-}\r
-\r
-NTSTATUS\r
-NTAPI\r
-CmEnumerateValueKey(IN PKEY_OBJECT KeyObject,\r
- IN ULONG Index,\r
- IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,\r
- IN PVOID KeyValueInformation,\r
- IN ULONG Length,\r
- IN PULONG ResultLength)\r
-{\r
- NTSTATUS Status;\r
- PHHIVE Hive;\r
- PCM_KEY_NODE Parent;\r
- HCELL_INDEX CellToRelease = HCELL_NIL, CellToRelease2 = HCELL_NIL;\r
- VALUE_SEARCH_RETURN_TYPE Result;\r
- BOOLEAN IndexIsCached, ValueIsCached = FALSE;\r
- PCELL_DATA CellData;\r
- PCM_CACHED_VALUE *CachedValue;\r
- PCM_KEY_VALUE ValueData;\r
- PAGED_CODE();\r
-\r
- /* Acquire hive lock */\r
- KeEnterCriticalRegion();\r
- ExAcquireResourceExclusiveLite(&CmpRegistryLock, TRUE);\r
-\r
- /* Get the hive and parent */\r
- Hive = &KeyObject->RegistryHive->Hive;\r
- Parent = (PCM_KEY_NODE)HvGetCell(Hive, KeyObject->KeyCellOffset);\r
- if (!Parent)\r
- {\r
- /* Fail */\r
- Status = STATUS_INSUFFICIENT_RESOURCES;\r
- goto Quickie;\r
- }\r
-\r
- /* Make sure the index is valid */\r
- //if (Index >= KeyObject->ValueCache.Count)\r
- if (Index >= KeyObject->KeyCell->ValueList.Count)\r
- {\r
- /* Release the cell and fail */\r
- HvReleaseCell(Hive, KeyObject->KeyCellOffset);\r
- Status = STATUS_NO_MORE_ENTRIES;\r
- goto Quickie;\r
- }\r
-\r
- /* Find the value list */\r
- Result = CmpGetValueListFromCache(KeyObject,\r
- &CellData,\r
- &IndexIsCached,\r
- &CellToRelease);\r
- if (Result != SearchSuccess)\r
- {\r
- /* Sanity check */\r
- ASSERT(CellData == NULL);\r
-\r
- /* Release the cell and fail */\r
- Status = STATUS_INSUFFICIENT_RESOURCES;\r
- goto Quickie;\r
- }\r
-\r
- /* Now get the key value */\r
- Result = CmpGetValueKeyFromCache(KeyObject,\r
- CellData,\r
- Index,\r
- &CachedValue,\r
- &ValueData,\r
- IndexIsCached,\r
- &ValueIsCached,\r
- &CellToRelease2);\r
- if (Result != SearchSuccess)\r
- {\r
- /* Sanity check */\r
- ASSERT(CellToRelease2 == HCELL_NIL);\r
-\r
- /* Release the cells and fail */\r
- Status = STATUS_INSUFFICIENT_RESOURCES;\r
- goto Quickie;\r
- }\r
-\r
- /* Query the information requested */\r
- Result = CmpQueryKeyValueData(KeyObject,\r
- CachedValue,\r
- ValueData,\r
- ValueIsCached,\r
- KeyValueInformationClass,\r
- KeyValueInformation,\r
- Length,\r
- ResultLength,\r
- &Status);\r
-\r
-Quickie:\r
- /* If we have a cell to release, do so */\r
- if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);\r
-\r
- /* Release the parent cell */\r
- HvReleaseCell(Hive, KeyObject->KeyCellOffset);\r
-\r
- /* If we have a cell to release, do so */\r
- if (CellToRelease2 != HCELL_NIL) HvReleaseCell(Hive, CellToRelease2);\r
-\r
- /* Release hive lock */\r
- ExReleaseResourceLite(&CmpRegistryLock);\r
- KeLeaveCriticalRegion();\r
- return Status;\r
-}\r
-\r
-NTSTATUS\r
-NTAPI\r
-CmpQueryKeyData(IN PHHIVE Hive,\r
- IN PCM_KEY_NODE Node,\r
- IN KEY_INFORMATION_CLASS KeyInformationClass,\r
- IN OUT PVOID KeyInformation,\r
- IN ULONG Length,\r
- IN OUT PULONG ResultLength)\r
-{\r
- NTSTATUS Status;\r
- ULONG Size, SizeLeft, MinimumSize;\r
- PKEY_INFORMATION Info = (PKEY_INFORMATION)KeyInformation;\r
- USHORT NameLength;\r
-\r
- /* Check if the value is compressed */\r
- if (Node->Flags & KEY_COMP_NAME)\r
- {\r
- /* Get the compressed name size */\r
- NameLength = CmpCompressedNameSize(Node->Name, Node->NameLength);\r
- }\r
- else\r
- {\r
- /* Get the real size */\r
- NameLength = Node->NameLength;\r
- }\r
-\r
- /* Check what kind of information is being requested */\r
- switch (KeyInformationClass)\r
- {\r
- /* Basic information */\r
- case KeyBasicInformation:\r
-\r
- /* This is the size we need */\r
- Size = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name) + NameLength;\r
-\r
- /* And this is the minimum we can work with */\r
- MinimumSize = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name);\r
-\r
- /* Let the caller know and assume success */\r
- *ResultLength = Size;\r
- Status = STATUS_SUCCESS;\r
-\r
- /* Check if the bufer we got is too small */\r
- if (Length < MinimumSize)\r
- {\r
- /* Let the caller know and fail */\r
- Status = STATUS_BUFFER_TOO_SMALL;\r
- break;\r
- }\r
-\r
- /* Copy the basic information */\r
- Info->KeyBasicInformation.LastWriteTime = Node->LastWriteTime;\r
- Info->KeyBasicInformation.TitleIndex = 0;\r
- Info->KeyBasicInformation.NameLength = NameLength;\r
-\r
- /* Only the name is left */\r
- SizeLeft = Length - MinimumSize;\r
- Size = NameLength;\r
-\r
- /* Check if we don't have enough space for the name */\r
- if (SizeLeft < Size)\r
- {\r
- /* Truncate the name we'll return, and tell the caller */\r
- Size = SizeLeft;\r
- Status = STATUS_BUFFER_OVERFLOW;\r
- }\r
-\r
- /* Check if this is a compressed key */\r
- if (Node->Flags & KEY_COMP_NAME)\r
- {\r
- /* Copy the compressed name */\r
- CmpCopyCompressedName(Info->KeyBasicInformation.Name,\r
- SizeLeft,\r
- Node->Name,\r
- Node->NameLength);\r
- }\r
- else\r
- {\r
- /* Otherwise, copy the raw name */\r
- RtlCopyMemory(Info->KeyBasicInformation.Name,\r
- Node->Name,\r
- Size);\r
- }\r
- break;\r
-\r
- /* Node information */\r
- case KeyNodeInformation:\r
-\r
- /* Calculate the size we need */\r
- Size = FIELD_OFFSET(KEY_NODE_INFORMATION, Name) +\r
- NameLength +\r
- Node->ClassLength;\r
-\r
- /* And the minimum size we can support */\r
- MinimumSize = FIELD_OFFSET(KEY_NODE_INFORMATION, Name);\r
-\r
- /* Return the size to the caller and assume succes */\r
- *ResultLength = Size;\r
- Status = STATUS_SUCCESS;\r
-\r
- /* Check if the caller's buffer is too small */\r
- if (Length < MinimumSize)\r
- {\r
- /* Let them know, and fail */\r
- Status = STATUS_BUFFER_TOO_SMALL;\r
- break;\r
- }\r
-\r
- /* Copy the basic information */\r
- Info->KeyNodeInformation.LastWriteTime = Node->LastWriteTime;\r
- Info->KeyNodeInformation.TitleIndex = 0;\r
- Info->KeyNodeInformation.ClassLength = Node->ClassLength;\r
- Info->KeyNodeInformation.NameLength = NameLength;\r
-\r
- /* Now the name is left */\r
- SizeLeft = Length - MinimumSize;\r
- Size = NameLength;\r
-\r
- /* Check if the name can fit entirely */\r
- if (SizeLeft < Size)\r
- {\r
- /* It can't, we'll have to truncate. Tell the caller */\r
- Size = SizeLeft;\r
- Status = STATUS_BUFFER_OVERFLOW;\r
- }\r
-\r
- /* Check if the key node name is compressed */\r
- if (Node->Flags & KEY_COMP_NAME)\r
- {\r
- /* Copy the compressed name */\r
- CmpCopyCompressedName(Info->KeyNodeInformation.Name,\r
- SizeLeft,\r
- Node->Name,\r
- Node->NameLength);\r
- }\r
- else\r
- {\r
- /* It isn't, so copy the raw name */\r
- RtlCopyMemory(Info->KeyNodeInformation.Name,\r
- Node->Name,\r
- Size);\r
- }\r
-\r
- /* Check if the node has a class */\r
- if (Node->ClassLength > 0)\r
- {\r
- /* It does. We don't support these yet */\r
- ASSERTMSG("Classes not supported\n", FALSE);\r
- }\r
- else\r
- {\r
- /* It doesn't, so set offset to -1, not 0! */\r
- Info->KeyNodeInformation.ClassOffset = 0xFFFFFFFF;\r
- }\r
- break;\r
-\r
- /* Full information requsted */\r
- case KeyFullInformation:\r
-\r
- /* This is the size we need */\r
- Size = FIELD_OFFSET(KEY_FULL_INFORMATION, Class) +\r
- Node->ClassLength;\r
-\r
- /* This is what we can work with */\r
- MinimumSize = FIELD_OFFSET(KEY_FULL_INFORMATION, Class);\r
-\r
- /* Return it to caller and assume success */\r
- *ResultLength = Size;\r
- Status = STATUS_SUCCESS;\r
-\r
- /* Check if the caller's buffer is to small */\r
- if (Length < MinimumSize)\r
- {\r
- /* Let them know and fail */\r
- Status = STATUS_BUFFER_TOO_SMALL;\r
- break;\r
- }\r
-\r
- /* Now copy all the basic information */\r
- Info->KeyFullInformation.LastWriteTime = Node->LastWriteTime;\r
- Info->KeyFullInformation.TitleIndex = 0;\r
- Info->KeyFullInformation.ClassLength = Node->ClassLength;\r
- Info->KeyFullInformation.SubKeys = Node->SubKeyCounts[HvStable] +\r
- Node->SubKeyCounts[HvVolatile];\r
- Info->KeyFullInformation.Values = Node->ValueList.Count;\r
- Info->KeyFullInformation.MaxNameLen = CmiGetMaxNameLength(Hive, Node);\r
- Info->KeyFullInformation.MaxClassLen = CmiGetMaxClassLength(Hive, Node);\r
- Info->KeyFullInformation.MaxValueNameLen = CmiGetMaxValueNameLength(Hive, Node);\r
- Info->KeyFullInformation.MaxValueDataLen = CmiGetMaxValueDataLength(Hive, Node);\r
- DPRINT("%d %d %d %d\n",\r
- CmiGetMaxNameLength(Hive, Node),\r
- CmiGetMaxValueDataLength(Hive, Node),\r
- CmiGetMaxValueNameLength(Hive, Node),\r
- CmiGetMaxClassLength(Hive, Node));\r
- //Info->KeyFullInformation.MaxNameLen = Node->MaxNameLen;\r
- //Info->KeyFullInformation.MaxClassLen = Node->MaxClassLen;\r
- //Info->KeyFullInformation.MaxValueNameLen = Node->MaxValueNameLen;\r
- //Info->KeyFullInformation.MaxValueDataLen = Node->MaxValueDataLen;\r
-\r
- /* Check if we have a class */\r
- if (Node->ClassLength > 0)\r
- {\r
- /* We do, but we currently don't support this */\r
- ASSERTMSG("Classes not supported\n", FALSE);\r
- }\r
- else\r
- {\r
- /* We don't have a class, so set offset to -1, not 0! */\r
- Info->KeyNodeInformation.ClassOffset = 0xFFFFFFFF;\r
- }\r
- break;\r
-\r
- /* Any other class that got sent here is invalid! */\r
- default:\r
-\r
- /* Set failure code */\r
- Status = STATUS_INVALID_PARAMETER;\r
- break;\r
- }\r
-\r
- /* Return status */\r
- return Status;\r
-}\r
-\r
-NTSTATUS\r
-NTAPI\r
-CmQueryKey(IN PKEY_OBJECT KeyObject,\r
- IN KEY_INFORMATION_CLASS KeyInformationClass,\r
- IN PVOID KeyInformation,\r
- IN ULONG Length,\r
- IN PULONG ResultLength)\r
-{\r
- NTSTATUS Status;\r
- PHHIVE Hive;\r
- PCM_KEY_NODE Parent;\r
-\r
- /* Acquire hive lock */\r
- KeEnterCriticalRegion();\r
- ExAcquireResourceExclusiveLite(&CmpRegistryLock, TRUE);\r
-\r
- /* Get the hive and parent */\r
- Hive = &KeyObject->RegistryHive->Hive;\r
- Parent = (PCM_KEY_NODE)HvGetCell(Hive, KeyObject->KeyCellOffset);\r
- if (!Parent)\r
- {\r
- /* Fail */\r
- Status = STATUS_INSUFFICIENT_RESOURCES;\r
- goto Quickie;\r
- }\r
-\r
- /* Check what class we got */\r
- switch (KeyInformationClass)\r
- {\r
- /* Typical information */\r
- case KeyFullInformation:\r
- case KeyBasicInformation:\r
- case KeyNodeInformation:\r
-\r
- /* Call the internal API */\r
- Status = CmpQueryKeyData(Hive,\r
- Parent,\r
- KeyInformationClass,\r
- KeyInformation,\r
- Length,\r
- ResultLength);\r
- break;\r
-\r
- /* Unsupported classes for now */\r
- case KeyNameInformation:\r
- case KeyCachedInformation:\r
- case KeyFlagsInformation:\r
-\r
- /* Print message and fail */\r
- DPRINT1("Unsupported class: %d!\n", KeyInformationClass);\r
- Status = STATUS_NOT_IMPLEMENTED;\r
- break;\r
-\r
- /* Illegal classes */\r
- default:\r
-\r
- /* Print message and fail */\r
- DPRINT1("Unsupported class: %d!\n", KeyInformationClass);\r
- Status = STATUS_INVALID_INFO_CLASS;\r
- break;\r
- }\r
-\r
-Quickie:\r
- /* Release hive lock */\r
- ExReleaseResourceLite(&CmpRegistryLock);\r
- KeLeaveCriticalRegion();\r
- return Status;\r
-}\r
-\r
-NTSTATUS\r
-NTAPI\r
-CmEnumerateKey(IN PKEY_OBJECT KeyObject,\r
- IN ULONG Index,\r
- IN KEY_INFORMATION_CLASS KeyInformationClass,\r
- IN PVOID KeyInformation,\r
- IN ULONG Length,\r
- IN PULONG ResultLength)\r
-{\r
- NTSTATUS Status;\r
- PHHIVE Hive;\r
- PCM_KEY_NODE Parent, Child;\r
- HCELL_INDEX ChildCell;\r
-\r
- /* Acquire hive lock */\r
- KeEnterCriticalRegion();\r
- ExAcquireResourceExclusiveLite(&CmpRegistryLock, TRUE);\r
-\r
- /* Get the hive and parent */\r
- Hive = &KeyObject->RegistryHive->Hive;\r
- Parent = (PCM_KEY_NODE)HvGetCell(Hive, KeyObject->KeyCellOffset);\r
- if (!Parent)\r
- {\r
- /* Fail */\r
- Status = STATUS_INSUFFICIENT_RESOURCES;\r
- goto Quickie;\r
- }\r
-\r
- /* Get the child cell */\r
- ChildCell = CmpFindSubKeyByNumber(Hive, Parent, Index);\r
-\r
- /* Release the parent cell */\r
- HvReleaseCell(Hive, KeyObject->KeyCellOffset);\r
-\r
- /* Check if we found the child */\r
- if (ChildCell == HCELL_NIL)\r
- {\r
- /* We didn't, fail */\r
- Status = STATUS_NO_MORE_ENTRIES;\r
- goto Quickie;\r
- }\r
-\r
- /* Now get the actual child node */\r
- Child = (PCM_KEY_NODE)HvGetCell(Hive, ChildCell);\r
- if (!Child)\r
- {\r
- /* Fail */\r
- Status = STATUS_INSUFFICIENT_RESOURCES;\r
- goto Quickie;\r
- }\r
-\r
- /* Query the data requested */\r
- Status = CmpQueryKeyData(Hive,\r
- Child,\r
- KeyInformationClass,\r
- KeyInformation,\r
- Length,\r
- ResultLength);\r
-\r
-Quickie:\r
- /* Release hive lock */\r
- ExReleaseResourceLite(&CmpRegistryLock);\r
- KeLeaveCriticalRegion();\r
- return Status;\r
-}\r
+/*
+ * PROJECT: ReactOS Kernel
+ * LICENSE: GPL - See COPYING in the top level directory
+ * FILE: ntoskrnl/config/cmapi.c
+ * PURPOSE: Configuration Manager - Internal Registry APIs
+ * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
+ * Eric Kohl
+ */
+
+/* INCLUDES ******************************************************************/
+
+#include "ntoskrnl.h"
+#define NDEBUG
+#include "debug.h"
+
+/* FUNCTIONS *****************************************************************/
+
+BOOLEAN
+NTAPI
+CmpIsHiveAlreadyLoaded(IN HANDLE KeyHandle,
+ IN POBJECT_ATTRIBUTES SourceFile,
+ OUT PCMHIVE *CmHive)
+{
+ NTSTATUS Status;
+ PCM_KEY_BODY KeyBody;
+ PCMHIVE Hive;
+ BOOLEAN Loaded = FALSE;
+ PAGED_CODE();
+
+ /* Sanity check */
+ CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK();
+
+ /* Reference the handle */
+ Status = ObReferenceObjectByHandle(KeyHandle,
+ 0,
+ CmpKeyObjectType,
+ KernelMode,
+ (PVOID)&KeyBody,
+ NULL);
+ if (!NT_SUCCESS(Status)) return Loaded;
+
+ /* Don't touch deleted KCBs */
+ if (KeyBody->KeyControlBlock->Delete) return Loaded;
+
+ Hive = CONTAINING_RECORD(KeyBody->KeyControlBlock->KeyHive, CMHIVE, Hive);
+
+ /* Must be the root key */
+ if (!(KeyBody->KeyControlBlock->Flags & KEY_HIVE_ENTRY) ||
+ !(Hive->FileUserName.Buffer))
+ {
+ /* It isn't */
+ ObDereferenceObject(KeyBody);
+ return Loaded;
+ }
+
+ /* Now compare the name of the file */
+ if (!RtlCompareUnicodeString(&Hive->FileUserName,
+ SourceFile->ObjectName,
+ TRUE))
+ {
+ /* Same file found */
+ Loaded = TRUE;
+ *CmHive = Hive;
+
+ /* If the hive is frozen, not sure what to do */
+ if (Hive->Frozen)
+ {
+ /* FIXME: TODO */
+ DPRINT1("ERROR: Hive is frozen\n");
+ while (TRUE);
+ }
+ }
+
+ /* Dereference and return result */
+ ObDereferenceObject(KeyBody);
+ return Loaded;
+ }
+
+BOOLEAN
+NTAPI
+CmpDoFlushAll(IN BOOLEAN ForceFlush)
+{
+ PLIST_ENTRY NextEntry;
+ PCMHIVE Hive;
+ NTSTATUS Status;
+ BOOLEAN Result = TRUE;
+
+ /* Make sure that the registry isn't read-only now */
+ if (CmpNoWrite) return TRUE;
+
+ /* Otherwise, acquire the hive list lock and disable force flush */
+ CmpForceForceFlush = FALSE;
+ ExAcquirePushLockShared(&CmpHiveListHeadLock);
+
+ /* Loop the hive list */
+ NextEntry = CmpHiveListHead.Flink;
+ while (NextEntry != &CmpHiveListHead)
+ {
+ /* Get the hive */
+ Hive = CONTAINING_RECORD(NextEntry, CMHIVE, HiveList);
+ if (!(Hive->Hive.HiveFlags & HIVE_NOLAZYFLUSH))
+ {
+ /* Acquire the flusher lock */
+ CmpLockHiveFlusherExclusive(Hive);
+
+ /* Check for illegal state */
+ if ((ForceFlush) && (Hive->UseCount))
+ {
+ /* Registry needs to be locked down */
+ CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK();
+ DPRINT1("FIXME: Hive is damaged and needs fixup\n");
+ while (TRUE);
+ }
+
+ /* Only sync if we are forced to or if it won't cause a hive shrink */
+ if ((ForceFlush) || (!HvHiveWillShrink(&Hive->Hive)))
+ {
+ /* Do the sync */
+ Status = HvSyncHive(&Hive->Hive);
+
+ /* If something failed - set the flag and continue looping */
+ if (!NT_SUCCESS(Status)) Result = FALSE;
+ }
+ else
+ {
+ /* We won't flush if the hive might shrink */
+ Result = FALSE;
+ CmpForceForceFlush = TRUE;
+ }
+
+ /* Release the flusher lock */
+ CmpUnlockHiveFlusher(Hive);
+ }
+
+ /* Try the next entry */
+ NextEntry = NextEntry->Flink;
+ }
+
+ /* Release lock and return */
+ ExReleasePushLock(&CmpHiveListHeadLock);
+ return Result;
+}
+
+NTSTATUS
+NTAPI
+CmpSetValueKeyNew(IN PHHIVE Hive,
+ IN PCM_KEY_NODE Parent,
+ IN PUNICODE_STRING ValueName,
+ IN ULONG Index,
+ IN ULONG Type,
+ IN PVOID Data,
+ IN ULONG DataSize,
+ IN ULONG StorageType,
+ IN ULONG SmallData)
+{
+ PCELL_DATA CellData;
+ HCELL_INDEX ValueCell;
+ NTSTATUS Status;
+
+ /* Check if we already have a value list */
+ if (Parent->ValueList.Count)
+ {
+ /* Then make sure it's valid and dirty it */
+ ASSERT(Parent->ValueList.List != HCELL_NIL);
+ if (!HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE))
+ {
+ /* Fail if we're out of space for log changes */
+ return STATUS_NO_LOG_SPACE;
+ }
+ }
+
+ /* Allocate a value cell */
+ ValueCell = HvAllocateCell(Hive,
+ FIELD_OFFSET(CM_KEY_VALUE, Name) +
+ CmpNameSize(Hive, ValueName),
+ StorageType,
+ HCELL_NIL);
+ if (ValueCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;
+
+ /* Get the actual data for it */
+ CellData = HvGetCell(Hive, ValueCell);
+ if (!CellData) ASSERT(FALSE);
+
+ /* Now we can release it, make sure it's also dirty */
+ HvReleaseCell(Hive, ValueCell);
+ ASSERT(HvIsCellDirty(Hive, ValueCell));
+
+ /* Set it up and copy the name */
+ CellData->u.KeyValue.Signature = CM_KEY_VALUE_SIGNATURE;
+ _SEH2_TRY
+ {
+ /* This can crash since the name is coming from user-mode */
+ CellData->u.KeyValue.NameLength = CmpCopyName(Hive,
+ CellData->u.KeyValue.Name,
+ ValueName);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* Fail */
+ DPRINT1("Invalid user data!\n");
+ HvFreeCell(Hive, ValueCell);
+ _SEH2_YIELD(return _SEH2_GetExceptionCode());
+ }
+ _SEH2_END;
+
+ /* Check for compressed name */
+ if (CellData->u.KeyValue.NameLength < ValueName->Length)
+ {
+ /* This is a compressed name */
+ CellData->u.KeyValue.Flags = VALUE_COMP_NAME;
+ }
+ else
+ {
+ /* No flags to set */
+ CellData->u.KeyValue.Flags = 0;
+ }
+
+ /* Check if this is a normal key */
+ if (DataSize > CM_KEY_VALUE_SMALL)
+ {
+ /* Build a data cell for it */
+ Status = CmpSetValueDataNew(Hive,
+ Data,
+ DataSize,
+ StorageType,
+ ValueCell,
+ &CellData->u.KeyValue.Data);
+ if (!NT_SUCCESS(Status))
+ {
+ /* We failed, free the cell */
+ HvFreeCell(Hive, ValueCell);
+ return Status;
+ }
+
+ /* Otherwise, set the data length, and make sure the data is dirty */
+ CellData->u.KeyValue.DataLength = DataSize;
+ ASSERT(HvIsCellDirty(Hive, CellData->u.KeyValue.Data));
+ }
+ else
+ {
+ /* This is a small key, set the data directly inside */
+ CellData->u.KeyValue.DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE;
+ CellData->u.KeyValue.Data = SmallData;
+ }
+
+ /* Set the type now */
+ CellData->u.KeyValue.Type = Type;
+
+ /* Add this value cell to the child list */
+ Status = CmpAddValueToList(Hive,
+ ValueCell,
+ Index,
+ StorageType,
+ &Parent->ValueList);
+
+ /* If we failed, free the entire cell, including the data */
+ if (!NT_SUCCESS(Status))
+ {
+ /* Overwrite the status with a known one */
+ CmpFreeValue(Hive, ValueCell);
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ /* Return Status */
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+CmpSetValueKeyExisting(IN PHHIVE Hive,
+ IN HCELL_INDEX OldChild,
+ IN PCM_KEY_VALUE Value,
+ IN ULONG Type,
+ IN PVOID Data,
+ IN ULONG DataSize,
+ IN ULONG StorageType,
+ IN ULONG TempData)
+{
+ HCELL_INDEX DataCell, NewCell;
+ PCELL_DATA CellData;
+ ULONG Length;
+ BOOLEAN WasSmall, IsSmall;
+
+ /* Registry writes must be blocked */
+ CMP_ASSERT_FLUSH_LOCK(Hive);
+
+ /* Mark the old child cell dirty */
+ if (!HvMarkCellDirty(Hive, OldChild, FALSE)) return STATUS_NO_LOG_SPACE;
+
+ /* See if this is a small or normal key */
+ WasSmall = CmpIsKeyValueSmall(&Length, Value->DataLength);
+
+ /* See if our new data can fit in a small key */
+ IsSmall = (DataSize <= CM_KEY_VALUE_SMALL) ? TRUE: FALSE;
+
+ /* Big keys are unsupported */
+ ASSERT_VALUE_BIG(Hive, Length);
+ ASSERT_VALUE_BIG(Hive, DataSize);
+
+ /* Mark the old value dirty */
+ if (!CmpMarkValueDataDirty(Hive, Value)) return STATUS_NO_LOG_SPACE;
+
+ /* Check if we have a small key */
+ if (IsSmall)
+ {
+ /* Check if we had a normal key with some data in it */
+ if (!(WasSmall) && (Length > 0))
+ {
+ /* Free the previous data */
+ CmpFreeValueData(Hive, Value->Data, Length);
+ }
+
+ /* Write our data directly */
+ Value->DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE;
+ Value->Data = TempData;
+ Value->Type = Type;
+ return STATUS_SUCCESS;
+ }
+
+ /* We have a normal key. Was the old cell also normal and had data? */
+ if (!(WasSmall) && (Length > 0))
+ {
+ /* Get the current data cell and actual data inside it */
+ DataCell = Value->Data;
+ ASSERT(DataCell != HCELL_NIL);
+ CellData = HvGetCell(Hive, DataCell);
+ if (!CellData) return STATUS_INSUFFICIENT_RESOURCES;
+
+ /* Immediately release the cell */
+ HvReleaseCell(Hive, DataCell);
+
+ /* Make sure that the data cell actually has a size */
+ ASSERT(HvGetCellSize(Hive, CellData) > 0);
+
+ /* Check if the previous data cell could fit our new data */
+ if (DataSize <= (ULONG)(HvGetCellSize(Hive, CellData)))
+ {
+ /* Re-use it then */
+ NewCell = DataCell;
+ }
+ else
+ {
+ /* Otherwise, re-allocate the current data cell */
+ NewCell = HvReallocateCell(Hive, DataCell, DataSize);
+ if (NewCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
+ else
+ {
+ /* This was a small key, or a key with no data, allocate a cell */
+ NewCell = HvAllocateCell(Hive, DataSize, StorageType, HCELL_NIL);
+ if (NewCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ /* Now get the actual data for our data cell */
+ CellData = HvGetCell(Hive, NewCell);
+ if (!CellData) ASSERT(FALSE);
+
+ /* Release it immediately */
+ HvReleaseCell(Hive, NewCell);
+
+ /* Copy our data into the data cell's buffer, and set up the value */
+ RtlCopyMemory(CellData, Data, DataSize);
+ Value->Data = NewCell;
+ Value->DataLength = DataSize;
+ Value->Type = Type;
+
+ /* Return success */
+ ASSERT(HvIsCellDirty(Hive, NewCell));
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+CmpQueryKeyData(IN PHHIVE Hive,
+ IN PCM_KEY_NODE Node,
+ IN KEY_INFORMATION_CLASS KeyInformationClass,
+ IN OUT PVOID KeyInformation,
+ IN ULONG Length,
+ IN OUT PULONG ResultLength)
+{
+ NTSTATUS Status;
+ ULONG Size, SizeLeft, MinimumSize;
+ PKEY_INFORMATION Info = (PKEY_INFORMATION)KeyInformation;
+ USHORT NameLength;
+
+ /* Check if the value is compressed */
+ if (Node->Flags & KEY_COMP_NAME)
+ {
+ /* Get the compressed name size */
+ NameLength = CmpCompressedNameSize(Node->Name, Node->NameLength);
+ }
+ else
+ {
+ /* Get the real size */
+ NameLength = Node->NameLength;
+ }
+
+ /* Check what kind of information is being requested */
+ switch (KeyInformationClass)
+ {
+ /* Basic information */
+ case KeyBasicInformation:
+
+ /* This is the size we need */
+ Size = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name) + NameLength;
+
+ /* And this is the minimum we can work with */
+ MinimumSize = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name);
+
+ /* Let the caller know and assume success */
+ *ResultLength = Size;
+ Status = STATUS_SUCCESS;
+
+ /* Check if the bufer we got is too small */
+ if (Length < MinimumSize)
+ {
+ /* Let the caller know and fail */
+ Status = STATUS_BUFFER_TOO_SMALL;
+ break;
+ }
+
+ /* Copy the basic information */
+ Info->KeyBasicInformation.LastWriteTime = Node->LastWriteTime;
+ Info->KeyBasicInformation.TitleIndex = 0;
+ Info->KeyBasicInformation.NameLength = NameLength;
+
+ /* Only the name is left */
+ SizeLeft = Length - MinimumSize;
+ Size = NameLength;
+
+ /* Check if we don't have enough space for the name */
+ if (SizeLeft < Size)
+ {
+ /* Truncate the name we'll return, and tell the caller */
+ Size = SizeLeft;
+ Status = STATUS_BUFFER_OVERFLOW;
+ }
+
+ /* Check if this is a compressed key */
+ if (Node->Flags & KEY_COMP_NAME)
+ {
+ /* Copy the compressed name */
+ CmpCopyCompressedName(Info->KeyBasicInformation.Name,
+ SizeLeft,
+ Node->Name,
+ Node->NameLength);
+ }
+ else
+ {
+ /* Otherwise, copy the raw name */
+ RtlCopyMemory(Info->KeyBasicInformation.Name,
+ Node->Name,
+ Size);
+ }
+ break;
+
+ /* Node information */
+ case KeyNodeInformation:
+
+ /* Calculate the size we need */
+ Size = FIELD_OFFSET(KEY_NODE_INFORMATION, Name) +
+ NameLength +
+ Node->ClassLength;
+
+ /* And the minimum size we can support */
+ MinimumSize = FIELD_OFFSET(KEY_NODE_INFORMATION, Name);
+
+ /* Return the size to the caller and assume succes */
+ *ResultLength = Size;
+ Status = STATUS_SUCCESS;
+
+ /* Check if the caller's buffer is too small */
+ if (Length < MinimumSize)
+ {
+ /* Let them know, and fail */
+ Status = STATUS_BUFFER_TOO_SMALL;
+ break;
+ }
+
+ /* Copy the basic information */
+ Info->KeyNodeInformation.LastWriteTime = Node->LastWriteTime;
+ Info->KeyNodeInformation.TitleIndex = 0;
+ Info->KeyNodeInformation.ClassLength = Node->ClassLength;
+ Info->KeyNodeInformation.NameLength = NameLength;
+
+ /* Now the name is left */
+ SizeLeft = Length - MinimumSize;
+ Size = NameLength;
+
+ /* Check if the name can fit entirely */
+ if (SizeLeft < Size)
+ {
+ /* It can't, we'll have to truncate. Tell the caller */
+ Size = SizeLeft;
+ Status = STATUS_BUFFER_OVERFLOW;
+ }
+
+ /* Check if the key node name is compressed */
+ if (Node->Flags & KEY_COMP_NAME)
+ {
+ /* Copy the compressed name */
+ CmpCopyCompressedName(Info->KeyNodeInformation.Name,
+ SizeLeft,
+ Node->Name,
+ Node->NameLength);
+ }
+ else
+ {
+ /* It isn't, so copy the raw name */
+ RtlCopyMemory(Info->KeyNodeInformation.Name,
+ Node->Name,
+ Size);
+ }
+
+ /* Check if the node has a class */
+ if (Node->ClassLength > 0)
+ {
+ /* It does. We don't support these yet */
+ ASSERTMSG("Classes not supported\n", FALSE);
+ }
+ else
+ {
+ /* It doesn't, so set offset to -1, not 0! */
+ Info->KeyNodeInformation.ClassOffset = 0xFFFFFFFF;
+ }
+ break;
+
+ /* Full information requsted */
+ case KeyFullInformation:
+
+ /* This is the size we need */
+ Size = FIELD_OFFSET(KEY_FULL_INFORMATION, Class) +
+ Node->ClassLength;
+
+ /* This is what we can work with */
+ MinimumSize = FIELD_OFFSET(KEY_FULL_INFORMATION, Class);
+
+ /* Return it to caller and assume success */
+ *ResultLength = Size;
+ Status = STATUS_SUCCESS;
+
+ /* Check if the caller's buffer is to small */
+ if (Length < MinimumSize)
+ {
+ /* Let them know and fail */
+ Status = STATUS_BUFFER_TOO_SMALL;
+ break;
+ }
+
+ /* Now copy all the basic information */
+ Info->KeyFullInformation.LastWriteTime = Node->LastWriteTime;
+ Info->KeyFullInformation.TitleIndex = 0;
+ Info->KeyFullInformation.ClassLength = Node->ClassLength;
+ Info->KeyFullInformation.SubKeys = Node->SubKeyCounts[Stable] +
+ Node->SubKeyCounts[Volatile];
+ Info->KeyFullInformation.Values = Node->ValueList.Count;
+ Info->KeyFullInformation.MaxNameLen = Node->MaxNameLen;
+ Info->KeyFullInformation.MaxClassLen = Node->MaxClassLen;
+ Info->KeyFullInformation.MaxValueNameLen = Node->MaxValueNameLen;
+ Info->KeyFullInformation.MaxValueDataLen = Node->MaxValueDataLen;
+
+ /* Check if we have a class */
+ if (Node->ClassLength > 0)
+ {
+ /* We do, but we currently don't support this */
+ ASSERTMSG("Classes not supported\n", FALSE);
+ }
+ else
+ {
+ /* We don't have a class, so set offset to -1, not 0! */
+ Info->KeyNodeInformation.ClassOffset = 0xFFFFFFFF;
+ }
+ break;
+
+ /* Any other class that got sent here is invalid! */
+ default:
+
+ /* Set failure code */
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ /* Return status */
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+CmSetValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
+ IN PUNICODE_STRING ValueName,
+ IN ULONG Type,
+ IN PVOID Data,
+ IN ULONG DataLength)
+{
+ PHHIVE Hive = NULL;
+ PCM_KEY_NODE Parent;
+ PCM_KEY_VALUE Value = NULL;
+ HCELL_INDEX CurrentChild, Cell;
+ NTSTATUS Status;
+ BOOLEAN Found, Result;
+ ULONG Count, ChildIndex, SmallData, Storage;
+ VALUE_SEARCH_RETURN_TYPE SearchResult;
+ BOOLEAN FirstTry = TRUE, FlusherLocked = FALSE;
+ HCELL_INDEX ParentCell = HCELL_NIL, ChildCell = HCELL_NIL;
+
+ /* Acquire hive and KCB lock */
+ CmpLockRegistry();
+ CmpAcquireKcbLockShared(Kcb);
+
+ /* Sanity check */
+ ASSERT(sizeof(ULONG) == CM_KEY_VALUE_SMALL);
+
+ /* Don't touch deleted KCBs */
+DoAgain:
+ if (Kcb->Delete)
+ {
+ /* Fail */
+ Status = STATUS_KEY_DELETED;
+ goto Quickie;
+ }
+
+ /* Don't let anyone mess with symlinks */
+ if ((Kcb->Flags & KEY_SYM_LINK) &&
+ ((Type != REG_LINK) ||
+ !(ValueName) ||
+ !(RtlEqualUnicodeString(&CmSymbolicLinkValueName, ValueName, TRUE))))
+ {
+ /* Invalid modification of a symlink key */
+ Status = STATUS_ACCESS_DENIED;
+ goto Quickie;
+ }
+
+ /* Check if this is the first attempt */
+ if (FirstTry)
+ {
+ /* Search for the value in the cache */
+ SearchResult = CmpCompareNewValueDataAgainstKCBCache(Kcb,
+ ValueName,
+ Type,
+ Data,
+ DataLength);
+ if (SearchResult == SearchNeedExclusiveLock)
+ {
+ /* Try again with the exclusive lock */
+ CmpConvertKcbSharedToExclusive(Kcb);
+ goto DoAgain;
+ }
+ else if (SearchResult == SearchSuccess)
+ {
+ /* We don't actually need to do anything! */
+ Status = STATUS_SUCCESS;
+ goto Quickie;
+ }
+
+ /* We need the exclusive KCB lock now */
+ if (!(CmpIsKcbLockedExclusive(Kcb)) &&
+ !(CmpTryToConvertKcbSharedToExclusive(Kcb)))
+ {
+ /* Acquire exclusive lock */
+ CmpConvertKcbSharedToExclusive(Kcb);
+ }
+
+ /* Cache lookup failed, so don't try it next time */
+ FirstTry = FALSE;
+
+ /* Now grab the flush lock since the key will be modified */
+ ASSERT(FlusherLocked == FALSE);
+ CmpLockHiveFlusherShared((PCMHIVE)Kcb->KeyHive);
+ FlusherLocked = TRUE;
+ goto DoAgain;
+ }
+ else
+ {
+ /* Get pointer to key cell */
+ Hive = Kcb->KeyHive;
+ Cell = Kcb->KeyCell;
+
+ /* Get the parent */
+ Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
+ ASSERT(Parent);
+ ParentCell = Cell;
+
+ /* Prepare to scan the key node */
+ Count = Parent->ValueList.Count;
+ Found = FALSE;
+ if (Count > 0)
+ {
+ /* Try to find the existing name */
+ Result = CmpFindNameInList(Hive,
+ &Parent->ValueList,
+ ValueName,
+ &ChildIndex,
+ &CurrentChild);
+ if (!Result)
+ {
+ /* Fail */
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Quickie;
+ }
+
+ /* Check if we found something */
+ if (CurrentChild != HCELL_NIL)
+ {
+ /* Release existing child */
+ if (ChildCell != HCELL_NIL)
+ {
+ HvReleaseCell(Hive, ChildCell);
+ ChildCell = HCELL_NIL;
+ }
+
+ /* Get its value */
+ Value = (PCM_KEY_VALUE)HvGetCell(Hive, CurrentChild);
+ if (!Value)
+ {
+ /* Fail */
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Quickie;
+ }
+
+ /* Remember that we found it */
+ ChildCell = CurrentChild;
+ Found = TRUE;
+ }
+ }
+ else
+ {
+ /* No child list, we'll need to add it */
+ ChildIndex = 0;
+ }
+ }
+
+ /* Should only get here on the second pass */
+ ASSERT(FirstTry == FALSE);
+
+ /* The KCB must be locked exclusive at this point */
+ CMP_ASSERT_KCB_LOCK(Kcb);
+
+ /* Mark the cell dirty */
+ if (!HvMarkCellDirty(Hive, Cell, FALSE))
+ {
+ /* Not enough log space, fail */
+ Status = STATUS_NO_LOG_SPACE;
+ goto Quickie;
+ }
+
+ /* Get the storage type */
+ Storage = HvGetCellType(Cell);
+
+ /* Check if this is small data */
+ SmallData = 0;
+ if ((DataLength <= CM_KEY_VALUE_SMALL) && (DataLength > 0))
+ {
+ /* Need SEH because user data may be invalid */
+ _SEH2_TRY
+ {
+ /* Copy it */
+ RtlCopyMemory(&SmallData, Data, DataLength);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* Return failure code */
+ Status = _SEH2_GetExceptionCode();
+ _SEH2_YIELD(goto Quickie);
+ }
+ _SEH2_END;
+ }
+
+ /* Check if we didn't find a matching key */
+ if (!Found)
+ {
+ /* Call the internal routine */
+ Status = CmpSetValueKeyNew(Hive,
+ Parent,
+ ValueName,
+ ChildIndex,
+ Type,
+ Data,
+ DataLength,
+ Storage,
+ SmallData);
+ }
+ else
+ {
+ /* Call the internal routine */
+ Status = CmpSetValueKeyExisting(Hive,
+ CurrentChild,
+ Value,
+ Type,
+ Data,
+ DataLength,
+ Storage,
+ SmallData);
+ }
+
+ /* Check for success */
+ if (NT_SUCCESS(Status))
+ {
+ /* Check if the maximum value name length changed */
+ ASSERT(Parent->MaxValueNameLen == Kcb->KcbMaxValueNameLen);
+ if (Parent->MaxValueNameLen < ValueName->Length)
+ {
+ /* Set the new values */
+ Parent->MaxValueNameLen = ValueName->Length;
+ Kcb->KcbMaxValueNameLen = ValueName->Length;
+ }
+
+ /* Check if the maximum data length changed */
+ ASSERT(Parent->MaxValueDataLen == Kcb->KcbMaxValueDataLen);
+ if (Parent->MaxValueDataLen < DataLength)
+ {
+ /* Update it */
+ Parent->MaxValueDataLen = DataLength;
+ Kcb->KcbMaxValueDataLen = Parent->MaxValueDataLen;
+ }
+
+ /* Save the write time */
+ KeQuerySystemTime(&Parent->LastWriteTime);
+ Kcb->KcbLastWriteTime = Parent->LastWriteTime;
+
+ /* Check if the cell is cached */
+ if ((Found) && (CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)))
+ {
+ /* Shouldn't happen */
+ ASSERT(FALSE);
+ }
+ else
+ {
+ /* Cleanup the value cache */
+ CmpCleanUpKcbValueCache(Kcb);
+
+ /* Sanity checks */
+ ASSERT(!(CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)));
+ ASSERT(!(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND));
+
+ /* Set the value cache */
+ Kcb->ValueCache.Count = Parent->ValueList.Count;
+ Kcb->ValueCache.ValueList = Parent->ValueList.List;
+ }
+
+ /* Notify registered callbacks */
+ CmpReportNotify(Kcb,
+ Hive,
+ Kcb->KeyCell,
+ REG_NOTIFY_CHANGE_LAST_SET);
+ }
+
+ /* Release the cells */
+Quickie:
+ if ((ParentCell != HCELL_NIL) && (Hive)) HvReleaseCell(Hive, ParentCell);
+ if ((ChildCell != HCELL_NIL) && (Hive)) HvReleaseCell(Hive, ChildCell);
+
+ /* Release the locks */
+ if (FlusherLocked) CmpUnlockHiveFlusher((PCMHIVE)Hive);
+ CmpReleaseKcbLock(Kcb);
+ CmpUnlockRegistry();
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+CmDeleteValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
+ IN UNICODE_STRING ValueName)
+{
+ NTSTATUS Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ PHHIVE Hive;
+ PCM_KEY_NODE Parent;
+ HCELL_INDEX ChildCell, Cell;
+ PCHILD_LIST ChildList;
+ PCM_KEY_VALUE Value = NULL;
+ ULONG ChildIndex;
+ BOOLEAN Result;
+
+ /* Acquire hive lock */
+ CmpLockRegistry();
+
+ /* Lock KCB exclusively */
+ CmpAcquireKcbLockExclusive(Kcb);
+
+ /* Don't touch deleted keys */
+ if (Kcb->Delete)
+ {
+ /* Undo everything */
+ CmpReleaseKcbLock(Kcb);
+ CmpUnlockRegistry();
+ return STATUS_KEY_DELETED;
+ }
+
+ /* Get the hive and the cell index */
+ Hive = Kcb->KeyHive;
+ Cell = Kcb->KeyCell;
+
+ /* Lock flushes */
+ CmpLockHiveFlusherShared((PCMHIVE)Hive);
+
+ /* Get the parent key node */
+ Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
+ ASSERT(Parent);
+
+ /* Get the value list and check if it has any entries */
+ ChildList = &Parent->ValueList;
+ if (ChildList->Count)
+ {
+ /* Try to find this value */
+ Result = CmpFindNameInList(Hive,
+ ChildList,
+ &ValueName,
+ &ChildIndex,
+ &ChildCell);
+ if (!Result)
+ {
+ /* Fail */
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Quickie;
+ }
+
+ /* Value not found, return error */
+ if (ChildCell == HCELL_NIL) goto Quickie;
+
+ /* We found the value, mark all relevant cells dirty */
+ if (!((HvMarkCellDirty(Hive, Cell, FALSE)) &&
+ (HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE)) &&
+ (HvMarkCellDirty(Hive, ChildCell, FALSE))))
+ {
+ /* Not enough log space, fail */
+ Status = STATUS_NO_LOG_SPACE;
+ goto Quickie;
+ }
+
+ /* Get the key value */
+ Value = (PCM_KEY_VALUE)HvGetCell(Hive,ChildCell);
+ ASSERT(Value);
+
+ /* Mark it and all related data as dirty */
+ if (!CmpMarkValueDataDirty(Hive, Value))
+ {
+ /* Not enough log space, fail */
+ Status = STATUS_NO_LOG_SPACE;
+ goto Quickie;
+ }
+
+ /* Ssanity checks */
+ ASSERT(HvIsCellDirty(Hive, Parent->ValueList.List));
+ ASSERT(HvIsCellDirty(Hive, ChildCell));
+
+ /* Remove the value from the child list */
+ Status = CmpRemoveValueFromList(Hive, ChildIndex, ChildList);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Set known error */
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Quickie;
+ }
+
+ /* Remove the value and its data itself */
+ if (!CmpFreeValue(Hive, ChildCell))
+ {
+ /* Failed to free the value, fail */
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Quickie;
+ }
+
+ /* Set the last write time */
+ KeQuerySystemTime(&Parent->LastWriteTime);
+ Kcb->KcbLastWriteTime = Parent->LastWriteTime;
+
+ /* Sanity check */
+ ASSERT(Parent->MaxValueNameLen == Kcb->KcbMaxValueNameLen);
+ ASSERT(Parent->MaxValueDataLen == Kcb->KcbMaxValueDataLen);
+ ASSERT(HvIsCellDirty(Hive, Cell));
+
+ /* Check if the value list is empty now */
+ if (!Parent->ValueList.Count)
+ {
+ /* Then clear key node data */
+ Parent->MaxValueNameLen = 0;
+ Parent->MaxValueDataLen = 0;
+ Kcb->KcbMaxValueNameLen = 0;
+ Kcb->KcbMaxValueDataLen = 0;
+ }
+
+ /* Cleanup the value cache */
+ CmpCleanUpKcbValueCache(Kcb);
+
+ /* Sanity checks */
+ ASSERT(!(CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)));
+ ASSERT(!(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND));
+
+ /* Set the value cache */
+ Kcb->ValueCache.Count = ChildList->Count;
+ Kcb->ValueCache.ValueList = ChildList->List;
+
+ /* Notify registered callbacks */
+ CmpReportNotify(Kcb, Hive, Cell, REG_NOTIFY_CHANGE_LAST_SET);
+
+ /* Change default Status to success */
+ Status = STATUS_SUCCESS;
+ }
+
+Quickie:
+ /* Release the parent cell, if any */
+ if (Parent) HvReleaseCell(Hive, Cell);
+
+ /* Check if we had a value */
+ if (Value)
+ {
+ /* Release the child cell */
+ ASSERT(ChildCell != HCELL_NIL);
+ HvReleaseCell(Hive, ChildCell);
+ }
+
+ /* Release locks */
+ CmpUnlockHiveFlusher((PCMHIVE)Hive);
+ CmpReleaseKcbLock(Kcb);
+ CmpUnlockRegistry();
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+CmQueryValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
+ 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 */
+ CmpLockRegistry();
+
+ /* Lock the KCB shared */
+ CmpAcquireKcbLockShared(Kcb);
+
+ /* Don't touch deleted keys */
+DoAgain:
+ if (Kcb->Delete)
+ {
+ /* Undo everything */
+ CmpReleaseKcbLock(Kcb);
+ CmpUnlockRegistry();
+ return STATUS_KEY_DELETED;
+ }
+
+ /* We don't deal with this yet */
+ if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)
+ {
+ /* Shouldn't happen */
+ ASSERT(FALSE);
+ }
+
+ /* Get the hive */
+ Hive = Kcb->KeyHive;
+
+ /* Find the key value */
+ Result = CmpFindValueByNameFromCache(Kcb,
+ &ValueName,
+ &CachedValue,
+ &Index,
+ &ValueData,
+ &ValueCached,
+ &CellToRelease);
+ if (Result == SearchNeedExclusiveLock)
+ {
+ /* Check if we need an exclusive lock */
+ ASSERT(CellToRelease == HCELL_NIL);
+ ASSERT(ValueData == NULL);
+
+ /* Try with exclusive KCB lock */
+ CmpConvertKcbSharedToExclusive(Kcb);
+ goto DoAgain;
+ }
+
+ if (Result == SearchSuccess)
+ {
+ /* Sanity check */
+ ASSERT(ValueData != NULL);
+
+ /* User data, protect against exceptions */
+ _SEH2_TRY
+ {
+ /* Query the information requested */
+ Result = CmpQueryKeyValueData(Kcb,
+ CachedValue,
+ ValueData,
+ ValueCached,
+ KeyValueInformationClass,
+ KeyValueInformation,
+ Length,
+ ResultLength,
+ &Status);
+ if (Result == SearchNeedExclusiveLock)
+ {
+ /* Release the value cell */
+ if (CellToRelease != HCELL_NIL)
+ {
+ HvReleaseCell(Hive, CellToRelease);
+ CellToRelease = HCELL_NIL;
+ }
+
+ /* Try with exclusive KCB lock */
+ CmpConvertKcbSharedToExclusive(Kcb);
+ goto DoAgain;
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
+ }
+ 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 locks */
+ CmpReleaseKcbLock(Kcb);
+ CmpUnlockRegistry();
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+CmEnumerateValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
+ IN ULONG Index,
+ IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
+ IN PVOID KeyValueInformation,
+ IN ULONG Length,
+ IN PULONG ResultLength)
+{
+ NTSTATUS Status;
+ PHHIVE Hive;
+ PCM_KEY_NODE Parent;
+ HCELL_INDEX CellToRelease = HCELL_NIL, CellToRelease2 = HCELL_NIL;
+ VALUE_SEARCH_RETURN_TYPE Result;
+ BOOLEAN IndexIsCached, ValueIsCached = FALSE;
+ PCELL_DATA CellData;
+ PCM_CACHED_VALUE *CachedValue;
+ PCM_KEY_VALUE ValueData = NULL;
+ PAGED_CODE();
+
+ /* Acquire hive lock */
+ CmpLockRegistry();
+
+ /* Lock the KCB shared */
+ CmpAcquireKcbLockShared(Kcb);
+
+ /* Don't touch deleted keys */
+DoAgain:
+ if (Kcb->Delete)
+ {
+ /* Undo everything */
+ CmpReleaseKcbLock(Kcb);
+ CmpUnlockRegistry();
+ return STATUS_KEY_DELETED;
+ }
+
+ /* Get the hive and parent */
+ Hive = Kcb->KeyHive;
+ Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
+ ASSERT(Parent);
+
+ /* FIXME: Lack of cache? */
+ if (Kcb->ValueCache.Count != Parent->ValueList.Count)
+ {
+ DPRINT1("HACK: Overriding value cache count\n");
+ Kcb->ValueCache.Count = Parent->ValueList.Count;
+ }
+
+ /* Make sure the index is valid */
+ if (Index >= Kcb->ValueCache.Count)
+ {
+ /* Release the cell and fail */
+ HvReleaseCell(Hive, Kcb->KeyCell);
+ Status = STATUS_NO_MORE_ENTRIES;
+ goto Quickie;
+ }
+
+ /* We don't deal with this yet */
+ if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)
+ {
+ /* Shouldn't happen */
+ ASSERT(FALSE);
+ }
+
+ /* Find the value list */
+ Result = CmpGetValueListFromCache(Kcb,
+ &CellData,
+ &IndexIsCached,
+ &CellToRelease);
+ if (Result == SearchNeedExclusiveLock)
+ {
+ /* Check if we need an exclusive lock */
+ ASSERT(CellToRelease == HCELL_NIL);
+ HvReleaseCell(Hive, Kcb->KeyCell);
+
+ /* Try with exclusive KCB lock */
+ CmpConvertKcbSharedToExclusive(Kcb);
+ goto DoAgain;
+ }
+ else if (Result != SearchSuccess)
+ {
+ /* Sanity check */
+ ASSERT(CellData == NULL);
+
+ /* Release the cell and fail */
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Quickie;
+ }
+
+ /* Now get the key value */
+ Result = CmpGetValueKeyFromCache(Kcb,
+ CellData,
+ Index,
+ &CachedValue,
+ &ValueData,
+ IndexIsCached,
+ &ValueIsCached,
+ &CellToRelease2);
+ if (Result == SearchNeedExclusiveLock)
+ {
+ /* Cleanup state */
+ ASSERT(CellToRelease2 == HCELL_NIL);
+ if (CellToRelease)
+ {
+ HvReleaseCell(Hive, CellToRelease);
+ CellToRelease = HCELL_NIL;
+ }
+ HvReleaseCell(Hive, Kcb->KeyCell);
+
+ /* Try with exclusive KCB lock */
+ CmpConvertKcbSharedToExclusive(Kcb);
+ goto DoAgain;
+ }
+ else if (Result != SearchSuccess)
+ {
+ /* Sanity check */
+ ASSERT(ValueData == NULL);
+
+ /* Release the cells and fail */
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Quickie;
+ }
+
+ /* User data, need SEH */
+ _SEH2_TRY
+ {
+ /* Query the information requested */
+ Result = CmpQueryKeyValueData(Kcb,
+ CachedValue,
+ ValueData,
+ ValueIsCached,
+ KeyValueInformationClass,
+ KeyValueInformation,
+ Length,
+ ResultLength,
+ &Status);
+ if (Result == SearchNeedExclusiveLock)
+ {
+ /* Cleanup state */
+ if (CellToRelease2) HvReleaseCell(Hive, CellToRelease2);
+ HvReleaseCell(Hive, Kcb->KeyCell);
+ if (CellToRelease) HvReleaseCell(Hive, CellToRelease);
+
+ /* Try with exclusive KCB lock */
+ CmpConvertKcbSharedToExclusive(Kcb);
+ goto DoAgain;
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* Get exception code */
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
+
+Quickie:
+ /* If we have a cell to release, do so */
+ if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);
+
+ /* Release the parent cell */
+ HvReleaseCell(Hive, Kcb->KeyCell);
+
+ /* If we have a cell to release, do so */
+ if (CellToRelease2 != HCELL_NIL) HvReleaseCell(Hive, CellToRelease2);
+
+ /* Release locks */
+ CmpReleaseKcbLock(Kcb);
+ CmpUnlockRegistry();
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+CmQueryKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
+ IN KEY_INFORMATION_CLASS KeyInformationClass,
+ IN PVOID KeyInformation,
+ IN ULONG Length,
+ IN PULONG ResultLength)
+{
+ NTSTATUS Status;
+ PHHIVE Hive;
+ PCM_KEY_NODE Parent;
+ HV_TRACK_CELL_REF CellReferences = {0};
+
+ /* Acquire hive lock */
+ CmpLockRegistry();
+
+ /* Lock KCB shared */
+ CmpAcquireKcbLockShared(Kcb);
+
+ /* Don't touch deleted keys */
+ if (Kcb->Delete)
+ {
+ /* Fail */
+ Status = STATUS_KEY_DELETED;
+ goto Quickie;
+ }
+
+ /* Check what class we got */
+ switch (KeyInformationClass)
+ {
+ /* Typical information */
+ case KeyFullInformation:
+ case KeyBasicInformation:
+ case KeyNodeInformation:
+
+ /* Get the hive and parent */
+ Hive = Kcb->KeyHive;
+ Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
+ ASSERT(Parent);
+
+ /* Track cell references */
+ if (!HvTrackCellRef(&CellReferences, Hive, Kcb->KeyCell))
+ {
+ /* Not enough memory to track references */
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ else
+ {
+ /* Call the internal API */
+ Status = CmpQueryKeyData(Hive,
+ Parent,
+ KeyInformationClass,
+ KeyInformation,
+ Length,
+ ResultLength);
+ }
+ break;
+
+ /* Unsupported classes for now */
+ case KeyNameInformation:
+ case KeyCachedInformation:
+ case KeyFlagsInformation:
+
+ /* Print message and fail */
+ DPRINT1("Unsupported class: %d!\n", KeyInformationClass);
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ /* Illegal classes */
+ default:
+
+ /* Print message and fail */
+ DPRINT1("Unsupported class: %d!\n", KeyInformationClass);
+ Status = STATUS_INVALID_INFO_CLASS;
+ break;
+ }
+
+Quickie:
+ /* Release references */
+ HvReleaseFreeCellRefArray(&CellReferences);
+
+ /* Release locks */
+ CmpReleaseKcbLock(Kcb);
+ CmpUnlockRegistry();
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+CmEnumerateKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
+ IN ULONG Index,
+ IN KEY_INFORMATION_CLASS KeyInformationClass,
+ IN PVOID KeyInformation,
+ IN ULONG Length,
+ IN PULONG ResultLength)
+{
+ NTSTATUS Status;
+ PHHIVE Hive;
+ PCM_KEY_NODE Parent, Child;
+ HCELL_INDEX ChildCell;
+ HV_TRACK_CELL_REF CellReferences = {0};
+
+ /* Acquire hive lock */
+ CmpLockRegistry();
+
+ /* Lock the KCB shared */
+ CmpAcquireKcbLockShared(Kcb);
+
+ /* Don't touch deleted keys */
+ if (Kcb->Delete)
+ {
+ /* Undo everything */
+ Status = STATUS_KEY_DELETED;
+ goto Quickie;
+ }
+
+ /* Get the hive and parent */
+ Hive = Kcb->KeyHive;
+ Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
+ ASSERT(Parent);
+
+ /* Get the child cell */
+ ChildCell = CmpFindSubKeyByNumber(Hive, Parent, Index);
+
+ /* Release the parent cell */
+ HvReleaseCell(Hive, Kcb->KeyCell);
+
+ /* Check if we found the child */
+ if (ChildCell == HCELL_NIL)
+ {
+ /* We didn't, fail */
+ Status = STATUS_NO_MORE_ENTRIES;
+ goto Quickie;
+ }
+
+ /* Now get the actual child node */
+ Child = (PCM_KEY_NODE)HvGetCell(Hive, ChildCell);
+ ASSERT(Child);
+
+ /* Track references */
+ if (!HvTrackCellRef(&CellReferences, Hive, ChildCell))
+ {
+ /* Can't allocate memory for tracking */
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Quickie;
+ }
+
+ /* Data can be user-mode, use SEH */
+ _SEH2_TRY
+ {
+ /* Query the data requested */
+ Status = CmpQueryKeyData(Hive,
+ Child,
+ KeyInformationClass,
+ KeyInformation,
+ Length,
+ ResultLength);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* Fail with exception code */
+ Status = _SEH2_GetExceptionCode();
+ _SEH2_YIELD(goto Quickie);
+ }
+ _SEH2_END;
+
+Quickie:
+ /* Release references */
+ HvReleaseFreeCellRefArray(&CellReferences);
+
+ /* Release locks */
+ CmpReleaseKcbLock(Kcb);
+ CmpUnlockRegistry();
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+CmDeleteKey(IN PCM_KEY_BODY KeyBody)
+{
+ NTSTATUS Status;
+ PHHIVE Hive;
+ PCM_KEY_NODE Node, Parent;
+ HCELL_INDEX Cell, ParentCell;
+ PCM_KEY_CONTROL_BLOCK Kcb;
+
+ /* Acquire hive lock */
+ CmpLockRegistry();
+
+ /* Get the kcb */
+ Kcb = KeyBody->KeyControlBlock;
+
+ /* Don't allow deleting the root */
+ if (!Kcb->ParentKcb)
+ {
+ /* Fail */
+ CmpUnlockRegistry();
+ return STATUS_CANNOT_DELETE;
+ }
+
+ /* Lock parent and child */
+ CmpAcquireTwoKcbLocksExclusiveByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey);
+
+ /* Check if we're already being deleted */
+ if (Kcb->Delete)
+ {
+ /* Don't do it twice */
+ Status = STATUS_SUCCESS;
+ goto Quickie2;
+ }
+
+ /* Get the hive and node */
+ Hive = Kcb->KeyHive;
+ Cell = Kcb->KeyCell;
+
+ /* Lock flushes */
+ CmpLockHiveFlusherShared((PCMHIVE)Hive);
+
+ /* Get the key node */
+ Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
+ ASSERT(Node);
+
+ /* Sanity check */
+ ASSERT(Node->Flags == Kcb->Flags);
+
+ /* Check if we don't have any children */
+ if (!(Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile]) &&
+ !(Node->Flags & KEY_NO_DELETE))
+ {
+ /* Send notification to registered callbacks */
+ CmpReportNotify(Kcb, Hive, Cell, REG_NOTIFY_CHANGE_NAME);
+
+ /* Get the parent and free the cell */
+ ParentCell = Node->Parent;
+ Status = CmpFreeKeyByCell(Hive, Cell, TRUE);
+ if (NT_SUCCESS(Status))
+ {
+ /* Flush any notifications */
+ CmpFlushNotifiesOnKeyBodyList(Kcb, FALSE);
+
+ /* Clean up information we have on the subkey */
+ CmpCleanUpSubKeyInfo(Kcb->ParentKcb);
+
+ /* Get the parent node */
+ Parent = (PCM_KEY_NODE)HvGetCell(Hive, ParentCell);
+ if (Parent)
+ {
+ /* Update the maximum name length */
+ Kcb->ParentKcb->KcbMaxNameLen = Parent->MaxNameLen;
+
+ /* Make sure we're dirty */
+ ASSERT(HvIsCellDirty(Hive, ParentCell));
+
+ /* Update the write time */
+ KeQuerySystemTime(&Parent->LastWriteTime);
+ Kcb->ParentKcb->KcbLastWriteTime = Parent->LastWriteTime;
+
+ /* Release the cell */
+ HvReleaseCell(Hive, ParentCell);
+ }
+
+ /* Set the KCB in delete mode and remove it */
+ Kcb->Delete = TRUE;
+ CmpRemoveKeyControlBlock(Kcb);
+
+ /* Clear the cell */
+ Kcb->KeyCell = HCELL_NIL;
+ }
+ }
+ else
+ {
+ /* Fail */
+ Status = STATUS_CANNOT_DELETE;
+ }
+
+ /* Release the cell */
+ HvReleaseCell(Hive, Cell);
+
+ /* Release flush lock */
+ CmpUnlockHiveFlusher((PCMHIVE)Hive);
+
+ /* Release the KCB locks */
+Quickie2:
+ CmpReleaseTwoKcbLockByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey);
+
+ /* Release hive lock */
+ CmpUnlockRegistry();
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+CmFlushKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
+ IN BOOLEAN ExclusiveLock)
+{
+ PCMHIVE CmHive;
+ NTSTATUS Status = STATUS_SUCCESS;
+ PHHIVE Hive;
+
+ /* Ignore flushes until we're ready */
+ if (CmpNoWrite) return STATUS_SUCCESS;
+
+ /* Get the hives */
+ Hive = Kcb->KeyHive;
+ CmHive = (PCMHIVE)Hive;
+
+ /* Check if this is the master hive */
+ if (CmHive == CmiVolatileHive)
+ {
+ /* Flush all the hives instead */
+ CmpDoFlushAll(FALSE);
+ }
+ else
+ {
+ /* Don't touch the hive */
+ CmpLockHiveFlusherExclusive(CmHive);
+ ASSERT(CmHive->ViewLock);
+ KeAcquireGuardedMutex(CmHive->ViewLock);
+ CmHive->ViewLockOwner = KeGetCurrentThread();
+
+ /* Will the hive shrink? */
+ if (HvHiveWillShrink(Hive))
+ {
+ /* I don't believe the current Hv does shrinking */
+ ASSERT(FALSE);
+ }
+ else
+ {
+ /* Now we can release views */
+ ASSERT(CmHive->ViewLock);
+ CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK_OR_LOADING(CmHive);
+ ASSERT(KeGetCurrentThread() == CmHive->ViewLockOwner);
+ KeReleaseGuardedMutex(CmHive->ViewLock);
+ }
+
+ /* Flush only this hive */
+ if (!HvSyncHive(Hive))
+ {
+ /* Fail */
+ Status = STATUS_REGISTRY_IO_FAILED;
+ }
+
+ /* Release the flush lock */
+ CmpUnlockHiveFlusher((PCMHIVE)Hive);
+ }
+
+ /* Return the status */
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+CmLoadKey(IN POBJECT_ATTRIBUTES TargetKey,
+ IN POBJECT_ATTRIBUTES SourceFile,
+ IN ULONG Flags,
+ IN PCM_KEY_BODY KeyBody)
+{
+ SECURITY_QUALITY_OF_SERVICE ServiceQos;
+ SECURITY_CLIENT_CONTEXT ClientSecurityContext;
+ HANDLE KeyHandle;
+ BOOLEAN Allocate = TRUE;
+ PCMHIVE CmHive, LoadedHive;
+ NTSTATUS Status;
+ CM_PARSE_CONTEXT ParseContext;
+
+ /* Check if we have a trust key */
+ if (KeyBody)
+ {
+ /* Fail */
+ DPRINT1("Trusted classes not yet supported\n");
+ return STATUS_NOT_IMPLEMENTED;
+ }
+
+ /* Build a service QoS for a security context */
+ ServiceQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ ServiceQos.ImpersonationLevel = SecurityImpersonation;
+ ServiceQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ ServiceQos.EffectiveOnly = TRUE;
+ Status = SeCreateClientSecurity(PsGetCurrentThread(),
+ &ServiceQos,
+ FALSE,
+ &ClientSecurityContext);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Fail */
+ DPRINT1("Security context failed\n");
+ return Status;
+ }
+
+ /* Open the target key */
+#if 0
+ Status = ZwOpenKey(&KeyHandle, KEY_READ, TargetKey);
+#else
+ RtlZeroMemory(&ParseContext, sizeof(ParseContext));
+ ParseContext.CreateOperation = FALSE;
+ Status = ObOpenObjectByName(TargetKey,
+ CmpKeyObjectType,
+ KernelMode,
+ NULL,
+ KEY_READ,
+ &ParseContext,
+ &KeyHandle);
+#endif
+ if (!NT_SUCCESS(Status)) KeyHandle = NULL;
+
+ /* Open the hive */
+ Status = CmpCmdHiveOpen(SourceFile,
+ &ClientSecurityContext,
+ &Allocate,
+ &CmHive,
+ 0);
+
+ /* Get rid of the security context */
+ SeDeleteClientSecurity(&ClientSecurityContext);
+
+ /* See if we failed */
+ if (!NT_SUCCESS(Status))
+ {
+ /* See if the target already existed */
+ if (KeyHandle)
+ {
+ /* Lock the registry */
+ CmpLockRegistryExclusive();
+
+ /* Check if we are already loaded */
+ if (CmpIsHiveAlreadyLoaded(KeyHandle, SourceFile, &LoadedHive))
+ {
+ /* That's okay then */
+ ASSERT(LoadedHive);
+ Status = STATUS_SUCCESS;
+ }
+
+ /* Release the registry */
+ CmpUnlockRegistry();
+ }
+
+ /* Close the key handle if we had one */
+ if (KeyHandle) ZwClose(KeyHandle);
+ return Status;
+ }
+
+ /* Lock the registry shared */
+ CmpLockRegistry();
+
+ /* Lock loading */
+ ExAcquirePushLockExclusive(&CmpLoadHiveLock);
+
+ /* Lock the hive to this thread */
+ CmHive->Hive.HiveFlags |= HIVE_IS_UNLOADING;
+ CmHive->CreatorOwner = KeGetCurrentThread();
+
+ /* Set flag */
+ if (Flags & REG_NO_LAZY_FLUSH) CmHive->Hive.HiveFlags |= HIVE_NOLAZYFLUSH;
+
+ /* Link the hive */
+ Status = CmpLinkHiveToMaster(TargetKey->ObjectName,
+ TargetKey->RootDirectory,
+ CmHive,
+ Allocate,
+ TargetKey->SecurityDescriptor);
+ if (NT_SUCCESS(Status))
+ {
+ /* Add to HiveList key */
+ CmpAddToHiveFileList(CmHive);
+
+ /* Sync the hive if necessary */
+ if (Allocate)
+ {
+ /* Sync it under the flusher lock */
+ CmpLockHiveFlusherExclusive(CmHive);
+ HvSyncHive(&CmHive->Hive);
+ CmpUnlockHiveFlusher(CmHive);
+ }
+
+ /* Release the hive */
+ CmHive->Hive.HiveFlags &= ~HIVE_IS_UNLOADING;
+ CmHive->CreatorOwner = NULL;
+
+ /* Allow loads */
+ ExReleasePushLock(&CmpLoadHiveLock);
+ }
+ else
+ {
+ /* FIXME: TODO */
+ ASSERT(FALSE);
+ }
+
+ /* Is this first profile load? */
+ if (!(CmpProfileLoaded) && !(CmpWasSetupBoot))
+ {
+ /* User is now logged on, set quotas */
+ CmpProfileLoaded = TRUE;
+ CmpSetGlobalQuotaAllowed();
+ }
+
+ /* Unlock the registry */
+ CmpUnlockRegistry();
+
+ /* Close handle and return */
+ if (KeyHandle) ZwClose(KeyHandle);
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+CmUnloadKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
+ IN ULONG Flags)
+{
+ UNIMPLEMENTED;
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+ULONG
+NTAPI
+CmCountOpenSubKeys(IN PCM_KEY_CONTROL_BLOCK RootKcb,
+ IN BOOLEAN RemoveEmptyCacheEntries)
+{
+ PCM_KEY_HASH Entry;
+ PCM_KEY_CONTROL_BLOCK CachedKcb;
+ PCM_KEY_CONTROL_BLOCK ParentKcb;
+ USHORT ParentKeyCount;
+ USHORT j;
+ ULONG i;
+ ULONG SubKeys = 0;
+
+ DPRINT("CmCountOpenSubKeys() called\n");
+
+ /* The root key is the only referenced key. There are no refereced sub keys. */
+ if (RootKcb->RefCount == 1)
+ {
+ DPRINT("open sub keys: 0\n");
+ return 0;
+ }
+
+ /* Enumerate all hash lists */
+ for (i = 0; i < CmpHashTableSize; i++)
+ {
+ /* Get the first cache entry */
+ Entry = CmpCacheTable[i].Entry;
+
+ /* Enumerate all cache entries */
+ while (Entry)
+ {
+ /* Get the KCB of the current cache entry */
+ CachedKcb = CONTAINING_RECORD(Entry, CM_KEY_CONTROL_BLOCK, KeyHash);
+
+ /* Check keys only that are subkeys to our root key */
+ if (CachedKcb->TotalLevels > RootKcb->TotalLevels)
+ {
+ /* Calculate the number of parent keys to the root key */
+ ParentKeyCount = CachedKcb->TotalLevels - RootKcb->TotalLevels;
+
+ /* Find a parent key that could be the root key */
+ ParentKcb = CachedKcb;
+ for (j = 0; j < ParentKeyCount; j++)
+ {
+ ParentKcb = ParentKcb->ParentKcb;
+ }
+
+ /* Check whether the parent is the root key */
+ if (ParentKcb == RootKcb)
+ {
+ DPRINT("Found a sub key \n");
+ DPRINT("RefCount = %u\n", CachedKcb->RefCount);
+
+ if (CachedKcb->RefCount > 0)
+ {
+ /* Count the current hash entry if it is in use */
+ SubKeys++;
+ }
+ else if ((CachedKcb->RefCount == 0) && (RemoveEmptyCacheEntries == TRUE))
+ {
+ /* Remove the current key from the delayed close list */
+ CmpRemoveFromDelayedClose(CachedKcb);
+
+ /* Remove the current cache entry */
+ CmpCleanUpKcbCacheWithLock(CachedKcb, TRUE);
+
+ /* Restart, because the hash list has changed */
+ Entry = CmpCacheTable[i].Entry;
+ continue;
+ }
+ }
+ }
+
+ /* Get the next cache entry */
+ Entry = Entry->NextHash;
+ }
+ }
+
+ DPRINT("open sub keys: %u\n", SubKeys);
+
+ return SubKeys;
+}