mirror of
https://github.com/reactos/reactos.git
synced 2024-11-18 21:13:52 +00:00
7d6bda16c6
NB: Not touching calls in "third-party" code: class and classpnp.
812 lines
28 KiB
C
812 lines
28 KiB
C
/*
|
|
* 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"
|
|
#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!\n", FALSE);
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
VALUE_SEARCH_RETURN_TYPE
|
|
NTAPI
|
|
CmpGetValueListFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb,
|
|
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 and value cache */
|
|
Hive = Kcb->KeyHive;
|
|
ChildList = &Kcb->ValueCache;
|
|
|
|
/* 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
|
|
{
|
|
/* Make sure the KCB is locked exclusive */
|
|
if (!(CmpIsKcbLockedExclusive(Kcb)) &&
|
|
!(CmpTryToConvertKcbSharedToExclusive(Kcb)))
|
|
{
|
|
/* We need the exclusive lock */
|
|
return SearchNeedExclusiveLock;
|
|
}
|
|
|
|
/* 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;
|
|
|
|
/* FIXME: Here we would cache the value */
|
|
|
|
/* 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 PCM_KEY_CONTROL_BLOCK Kcb,
|
|
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 = Kcb->KeyHive;
|
|
|
|
/* 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 PCM_KEY_CONTROL_BLOCK Kcb,
|
|
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 = Kcb->KeyHive;
|
|
|
|
/* 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 PCM_KEY_CONTROL_BLOCK Kcb,
|
|
IN PCUNICODE_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 = Kcb->KeyHive;
|
|
ChildList = &Kcb->ValueCache;
|
|
|
|
/* Check if the child list has any entries */
|
|
if (ChildList->Count != 0)
|
|
{
|
|
/* Get the value list associated to this child list */
|
|
SearchResult = CmpGetValueListFromCache(Kcb,
|
|
&CellData,
|
|
&IndexIsCached,
|
|
&Cell);
|
|
if (SearchResult != SearchSuccess)
|
|
{
|
|
/* We either failed or need the exclusive lock */
|
|
ASSERT((SearchResult == SearchFail) || !(CmpIsKcbLockedExclusive(Kcb)));
|
|
ASSERT(Cell == HCELL_NIL);
|
|
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(Kcb,
|
|
CellData,
|
|
i,
|
|
CachedValue,
|
|
Value,
|
|
IndexIsCached,
|
|
ValueIsCached,
|
|
CellToRelease);
|
|
if (SearchResult != SearchSuccess)
|
|
{
|
|
/* We either failed or need the exclusive lock */
|
|
ASSERT((SearchResult == SearchFail) || !(CmpIsKcbLockedExclusive(Kcb)));
|
|
ASSERT(Cell == HCELL_NIL);
|
|
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 PCM_KEY_CONTROL_BLOCK Kcb,
|
|
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)
|
|
{
|
|
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 value data */
|
|
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:
|
|
case KeyValueFullInformationAlign64:
|
|
|
|
/* 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;
|
|
|
|
#ifdef _WIN64
|
|
/* On 64-bit, always align to 8 bytes */
|
|
AlignedData = ALIGN_UP(DataOffset, ULONGLONG);
|
|
#else
|
|
/* On 32-bit, align the offset to 4 or 8 bytes */
|
|
if (KeyValueInformationClass == KeyValueFullInformationAlign64)
|
|
{
|
|
AlignedData = ALIGN_UP(DataOffset, ULONGLONG);
|
|
}
|
|
else
|
|
{
|
|
AlignedData = ALIGN_UP(DataOffset, ULONG);
|
|
}
|
|
#endif
|
|
/* 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(Kcb,
|
|
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 = (((LONG)Length - (LONG)AlignedData) < 0) ?
|
|
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(Kcb,
|
|
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 */
|
|
DPRINT1("Caller requested unknown class: %lx\n", KeyValueInformationClass);
|
|
*Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
/* Return the search result as well */
|
|
return Result;
|
|
}
|
|
|
|
VALUE_SEARCH_RETURN_TYPE
|
|
NTAPI
|
|
CmpCompareNewValueDataAgainstKCBCache(IN PCM_KEY_CONTROL_BLOCK Kcb,
|
|
IN PUNICODE_STRING ValueName,
|
|
IN ULONG Type,
|
|
IN PVOID Data,
|
|
IN ULONG DataSize)
|
|
{
|
|
VALUE_SEARCH_RETURN_TYPE SearchResult;
|
|
PCM_KEY_NODE KeyNode;
|
|
PCM_CACHED_VALUE *CachedValue;
|
|
ULONG Index;
|
|
PCM_KEY_VALUE Value;
|
|
BOOLEAN ValueCached, BufferAllocated = FALSE;
|
|
PVOID Buffer;
|
|
HCELL_INDEX ValueCellToRelease = HCELL_NIL, CellToRelease = HCELL_NIL;
|
|
BOOLEAN IsSmall;
|
|
ULONG_PTR CompareResult;
|
|
PAGED_CODE();
|
|
|
|
/* Check if this is a symlink */
|
|
if (Kcb->Flags & KEY_SYM_LINK)
|
|
{
|
|
/* We need the exclusive lock */
|
|
if (!(CmpIsKcbLockedExclusive(Kcb)) &&
|
|
!(CmpTryToConvertKcbSharedToExclusive(Kcb)))
|
|
{
|
|
/* We need the exclusive lock */
|
|
return SearchNeedExclusiveLock;
|
|
}
|
|
|
|
/* Otherwise, get the key node */
|
|
KeyNode = (PCM_KEY_NODE)HvGetCell(Kcb->KeyHive, Kcb->KeyCell);
|
|
if (!KeyNode) return SearchFail;
|
|
|
|
/* Cleanup the KCB 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 = KeyNode->ValueList.Count;
|
|
Kcb->ValueCache.ValueList = KeyNode->ValueList.List;
|
|
|
|
/* Release the cell */
|
|
HvReleaseCell(Kcb->KeyHive, Kcb->KeyCell);
|
|
}
|
|
|
|
/* Do the search */
|
|
SearchResult = CmpFindValueByNameFromCache(Kcb,
|
|
ValueName,
|
|
&CachedValue,
|
|
&Index,
|
|
&Value,
|
|
&ValueCached,
|
|
&ValueCellToRelease);
|
|
if (SearchResult == SearchNeedExclusiveLock)
|
|
{
|
|
/* We need the exclusive lock */
|
|
ASSERT(!CmpIsKcbLockedExclusive(Kcb));
|
|
ASSERT(ValueCellToRelease == HCELL_NIL);
|
|
ASSERT(Value == NULL);
|
|
goto Quickie;
|
|
}
|
|
else if (SearchResult == SearchSuccess)
|
|
{
|
|
/* Sanity check */
|
|
ASSERT(Value);
|
|
|
|
/* First of all, check if the key size and type matches */
|
|
if ((Type == Value->Type) &&
|
|
(DataSize == (Value->DataLength & ~CM_KEY_VALUE_SPECIAL_SIZE)))
|
|
{
|
|
/* Check if this is a small key */
|
|
IsSmall = (DataSize <= CM_KEY_VALUE_SMALL) ? TRUE: FALSE;
|
|
if (IsSmall)
|
|
{
|
|
/* Compare against the data directly */
|
|
Buffer = &Value->Data;
|
|
}
|
|
else
|
|
{
|
|
/* Do a search */
|
|
SearchResult = CmpGetValueDataFromCache(Kcb,
|
|
CachedValue,
|
|
(PCELL_DATA)Value,
|
|
ValueCached,
|
|
&Buffer,
|
|
&BufferAllocated,
|
|
&CellToRelease);
|
|
if (SearchResult != SearchSuccess)
|
|
{
|
|
/* Sanity checks */
|
|
ASSERT(Buffer == NULL);
|
|
ASSERT(BufferAllocated == FALSE);
|
|
goto Quickie;
|
|
}
|
|
}
|
|
|
|
/* Now check the data size */
|
|
if (DataSize)
|
|
{
|
|
/* Do the compare */
|
|
CompareResult = RtlCompareMemory(Buffer,
|
|
Data,
|
|
DataSize &
|
|
~CM_KEY_VALUE_SPECIAL_SIZE);
|
|
}
|
|
else
|
|
{
|
|
/* It's equal */
|
|
CompareResult = 0;
|
|
}
|
|
|
|
/* Now check if the compare wasn't equal */
|
|
if (CompareResult != DataSize) SearchResult = SearchFail;
|
|
}
|
|
else
|
|
{
|
|
/* The length or type isn't equal */
|
|
SearchResult = SearchFail;
|
|
}
|
|
}
|
|
|
|
Quickie:
|
|
/* Release the value cell */
|
|
if (ValueCellToRelease) HvReleaseCell(Kcb->KeyHive, ValueCellToRelease);
|
|
|
|
/* Free the buffer */
|
|
if (BufferAllocated) CmpFree(Buffer, 0);
|
|
|
|
/* Free the cell */
|
|
if (CellToRelease) HvReleaseCell(Kcb->KeyHive, CellToRelease);
|
|
|
|
/* Return the search result */
|
|
return SearchResult;
|
|
}
|