diff --git a/reactos/ntoskrnl/cm/cm.h b/reactos/ntoskrnl/cm/cm.h index b87a37c880f..b232c112100 100644 --- a/reactos/ntoskrnl/cm/cm.h +++ b/reactos/ntoskrnl/cm/cm.h @@ -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, diff --git a/reactos/ntoskrnl/cm/ntfunc.c b/reactos/ntoskrnl/cm/ntfunc.c index de08756e043..066442a6d0e 100644 --- a/reactos/ntoskrnl/cm/ntfunc.c +++ b/reactos/ntoskrnl/cm/ntfunc.c @@ -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 diff --git a/reactos/ntoskrnl/config/cm.h b/reactos/ntoskrnl/config/cm.h index 00c97690c9d..b899c71c4b0 100644 --- a/reactos/ntoskrnl/config/cm.h +++ b/reactos/ntoskrnl/config/cm.h @@ -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 // diff --git a/reactos/ntoskrnl/config/cmapi.c b/reactos/ntoskrnl/config/cmapi.c index 8369cf50a71..f690045460e 100644 --- a/reactos/ntoskrnl/config/cmapi.c +++ b/reactos/ntoskrnl/config/cmapi.c @@ -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; +} diff --git a/reactos/ntoskrnl/config/cmname.c b/reactos/ntoskrnl/config/cmname.c index 39bf2be38e0..4b8e2024a30 100644 --- a/reactos/ntoskrnl/config/cmname.c +++ b/reactos/ntoskrnl/config/cmname.c @@ -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, diff --git a/reactos/ntoskrnl/config/cmvalche.c b/reactos/ntoskrnl/config/cmvalche.c new file mode 100644 index 00000000000..2ff2e284b57 --- /dev/null +++ b/reactos/ntoskrnl/config/cmvalche.c @@ -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; +} diff --git a/reactos/ntoskrnl/ntoskrnl.rbuild b/reactos/ntoskrnl/ntoskrnl.rbuild index 1a90add0f38..ba595fc6fa9 100644 --- a/reactos/ntoskrnl/ntoskrnl.rbuild +++ b/reactos/ntoskrnl/ntoskrnl.rbuild @@ -106,6 +106,7 @@ cmsecach.c cmsysini.c cmvalue.c + cmvalche.c cmwraprs.c