Author: fireball
Date: Sat Apr 3 22:22:32 2010
New Revision: 46702
URL:
http://svn.reactos.org/svn/reactos?rev=46702&view=rev
Log:
[NTOSKRNL/CONFIG]
- Flusher lock fixes: wrong kind of lock,total mess (and the wrong kind of lock). Properly
fixed throughout cmapi.c, but still missing in many other places.
- Add support for detecting loading of an already loaded hive.
- Start adding calls to CmpReportNotify to support registry callbacks.
- Do work needed to flush notifications for a deleted node (but CmpFlushNotify not yet
implemented).
- Add support for adding each newly loaded hive to the HiveList key in the registry (but
CmpAddHiveToFileList not yet implemented).
- Add some ViewLock acquire/releases where needed.
- Load the key in a faster way (Ob vs Zw)
- Add checks everywhere for HvMarkCellDirty success. In future (when log/backup file is
enabled), it can return FALSE (e.g. when we are out of space).
- Change logic in CmpDoFlushAll to only flush a hive if it won't shrink (in the
future, flushing may lead to hive shrinkage for efficiency).
- Add SEH2 protection to all CmApis that may deal with user-mode data.
- Add HvTrackCellRef/HvReleaseCellRef for tracking cell references in scenarios where we
might need many GetCell/ReleaseCell calls. For now stubbed to only work with up to 4
static references.
- Properly unlock/free in some failure paths in some of the CM APIs.
- Add some missing HvReleaseCell in paths where it was missing.
- Try to fix hack in enumerate key.
- Fix wrong usage of KeQuerySystemTime. It was called twice to save it in 2 different
places. Instead, there should be only one call, and then duplicate the value across.
- Fix logic in CmpSetValueExistingData/Key.
Tested with winetests and .NET framework 1.1 installation which fully completes.
Modified:
trunk/reactos/lib/cmlib/cmlib.h
trunk/reactos/lib/cmlib/hivecell.c
trunk/reactos/lib/cmlib/hivewrt.c
trunk/reactos/ntoskrnl/config/cmapi.c
trunk/reactos/ntoskrnl/config/cmhvlist.c
trunk/reactos/ntoskrnl/config/cminit.c
trunk/reactos/ntoskrnl/config/cmkcbncb.c
trunk/reactos/ntoskrnl/config/cmparse.c
Modified: trunk/reactos/lib/cmlib/cmlib.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/lib/cmlib/cmlib.h?rev=4670…
==============================================================================
--- trunk/reactos/lib/cmlib/cmlib.h [iso-8859-1] (original)
+++ trunk/reactos/lib/cmlib/cmlib.h [iso-8859-1] Sat Apr 3 22:22:32 2010
@@ -198,6 +198,22 @@
#endif
+typedef struct _HV_HIVE_CELL_PAIR
+{
+ PHHIVE Hive;
+ HCELL_INDEX Cell;
+} HV_HIVE_CELL_PAIR, *PHV_HIVE_CELL_PAIR;
+
+#define STATIC_CELL_PAIR_COUNT 4
+typedef struct _HV_TRACK_CELL_REF
+{
+ USHORT Count;
+ USHORT Max;
+ PHV_HIVE_CELL_PAIR CellArray;
+ HV_HIVE_CELL_PAIR StaticArray[STATIC_CELL_PAIR_COUNT];
+ USHORT StaticCount;
+} HV_TRACK_CELL_REF, *PHV_TRACK_CELL_REF;
+
extern ULONG CmlibTraceLevel;
/*
@@ -272,6 +288,12 @@
IN HCELL_INDEX Cell
);
+BOOLEAN
+CMAPI
+HvHiveWillShrink(
+ IN PHHIVE RegistryHive
+);
+
BOOLEAN CMAPI
HvSyncHive(
PHHIVE RegistryHive);
@@ -288,6 +310,21 @@
VOID CMAPI
CmPrepareHive(
PHHIVE RegistryHive);
+
+
+BOOLEAN
+CMAPI
+HvTrackCellRef(
+ PHV_TRACK_CELL_REF CellRef,
+ PHHIVE Hive,
+ HCELL_INDEX Cell
+);
+
+VOID
+CMAPI
+HvReleaseFreeCellRefArray(
+ PHV_TRACK_CELL_REF CellRef
+);
/*
* Private functions.
Modified: trunk/reactos/lib/cmlib/hivecell.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/lib/cmlib/hivecell.c?rev=4…
==============================================================================
--- trunk/reactos/lib/cmlib/hivecell.c [iso-8859-1] (original)
+++ trunk/reactos/lib/cmlib/hivecell.c [iso-8859-1] Sat Apr 3 22:22:32 2010
@@ -113,7 +113,7 @@
__FUNCTION__, RegistryHive, CellIndex, HoldingLock);
if ((CellIndex & HCELL_TYPE_MASK) >> HCELL_TYPE_SHIFT != Stable)
- return FALSE;
+ return TRUE;
CellBlock = (CellIndex & HCELL_BLOCK_MASK) >> HCELL_BLOCK_SHIFT;
CellLastBlock = ((CellIndex + HV_BLOCK_SIZE - 1) & HCELL_BLOCK_MASK) >>
HCELL_BLOCK_SHIFT;
@@ -525,3 +525,56 @@
if (CellType == Stable)
HvMarkCellDirty(RegistryHive, CellIndex, FALSE);
}
+
+BOOLEAN
+CMAPI
+HvTrackCellRef(PHV_TRACK_CELL_REF CellRef,
+ PHHIVE Hive,
+ HCELL_INDEX Cell)
+{
+ /* Sanity checks */
+ ASSERT(CellRef);
+ ASSERT(Hive );
+ ASSERT(Cell != HCELL_NIL);
+
+ /* Less than 4? */
+ if (CellRef->StaticCount < STATIC_CELL_PAIR_COUNT)
+ {
+ /* Add reference */
+ CellRef->StaticArray[CellRef->StaticCount].Hive = Hive;
+ CellRef->StaticArray[CellRef->StaticCount].Cell = Cell;
+ CellRef->StaticCount++;
+ return TRUE;
+ }
+
+ /* FIXME: TODO */
+ DPRINT1("ERROR: Too many references\n");
+ while (TRUE);
+ return FALSE;
+}
+
+VOID
+CMAPI
+HvReleaseFreeCellRefArray(PHV_TRACK_CELL_REF CellRef)
+{
+ ULONG i;
+ ASSERT(CellRef);
+
+ /* Any references? */
+ if (CellRef->StaticCount > 0)
+ {
+ /* Sanity check */
+ ASSERT(CellRef->StaticCount <= STATIC_CELL_PAIR_COUNT);
+
+ /* Loop them */
+ for (i = 0; i < CellRef->StaticCount;i++)
+ {
+ /* Release them */
+ HvReleaseCell(CellRef->StaticArray[i].Hive,
+ CellRef->StaticArray[i].Cell);
+ }
+
+ /* Free again */
+ CellRef->StaticCount = 0;
+ }
+}
Modified: trunk/reactos/lib/cmlib/hivewrt.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/lib/cmlib/hivewrt.c?rev=46…
==============================================================================
--- trunk/reactos/lib/cmlib/hivewrt.c [iso-8859-1] (original)
+++ trunk/reactos/lib/cmlib/hivewrt.c [iso-8859-1] Sat Apr 3 22:22:32 2010
@@ -265,6 +265,14 @@
return TRUE;
}
+BOOLEAN
+CMAPI
+HvHiveWillShrink(IN PHHIVE RegistryHive)
+{
+ /* No shrinking yet */
+ return FALSE;
+}
+
BOOLEAN CMAPI
HvWriteHive(
PHHIVE RegistryHive)
Modified: trunk/reactos/ntoskrnl/config/cmapi.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/config/cmapi.c?re…
==============================================================================
--- trunk/reactos/ntoskrnl/config/cmapi.c [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/config/cmapi.c [iso-8859-1] Sat Apr 3 22:22:32 2010
@@ -14,6 +14,67 @@
/* 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)
@@ -39,16 +100,35 @@
if (!(Hive->Hive.HiveFlags & HIVE_NOLAZYFLUSH))
{
/* Acquire the flusher lock */
- ExAcquirePushLockExclusive((PVOID)&Hive->FlusherLock);
-
- /* Do the sync */
- Status = HvSyncHive(&Hive->Hive);
-
- /* If something failed - set the flag and continue looping*/
- if (!NT_SUCCESS(Status)) Result = FALSE;
+ 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 */
- ExReleasePushLock((PVOID)&Hive->FlusherLock);
+ CmpUnlockHiveFlusher(Hive);
}
/* Try the next entry */
@@ -81,10 +161,14 @@
{
/* Then make sure it's valid and dirty it */
ASSERT(Parent->ValueList.List != HCELL_NIL);
- HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE);
- }
-
- /* Allocate avalue cell */
+ 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),
@@ -102,15 +186,32 @@
/* Set it up and copy the name */
CellData->u.KeyValue.Signature = CM_KEY_VALUE_SIGNATURE;
- CellData->u.KeyValue.Flags = 0;
- CellData->u.KeyValue.Type = Type;
- CellData->u.KeyValue.NameLength = CmpCopyName(Hive,
- CellData->u.KeyValue.Name,
- ValueName);
+ _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 */
@@ -140,6 +241,9 @@
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,
@@ -149,7 +253,12 @@
&Parent->ValueList);
/* If we failed, free the entire cell, including the data */
- if (!NT_SUCCESS(Status)) CmpFreeValue(Hive, ValueCell);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Overwrite the status with a known one */
+ CmpFreeValue(Hive, ValueCell);
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
/* Return Status */
return Status;
@@ -170,9 +279,12 @@
PCELL_DATA CellData;
ULONG Length;
BOOLEAN WasSmall, IsSmall;
+
+ /* Registry writes must be blocked */
+ CMP_ASSERT_FLUSH_LOCK(Hive);
/* Mark the old child cell dirty */
- HvMarkCellDirty(Hive, OldChild, FALSE);
+ if (!HvMarkCellDirty(Hive, OldChild, FALSE)) return STATUS_NO_LOG_SPACE;
/* See if this is a small or normal key */
WasSmall = CmpIsKeyValueSmall(&Length, Value->DataLength);
@@ -185,7 +297,7 @@
ASSERT_VALUE_BIG(Hive, DataSize);
/* Mark the old value dirty */
- CmpMarkValueDataDirty(Hive, Value);
+ if (!CmpMarkValueDataDirty(Hive, Value)) return STATUS_NO_LOG_SPACE;
/* Check if we have a small key */
if (IsSmall)
@@ -203,60 +315,273 @@
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
{
- /* 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;
+ /* 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, 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;
- }
+ /* 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
@@ -267,7 +592,7 @@
IN PVOID Data,
IN ULONG DataLength)
{
- PHHIVE Hive;
+ PHHIVE Hive = NULL;
PCM_KEY_NODE Parent;
PCM_KEY_VALUE Value = NULL;
HCELL_INDEX CurrentChild, Cell;
@@ -275,8 +600,10 @@
BOOLEAN Found, Result;
ULONG Count, ChildIndex, SmallData, Storage;
VALUE_SEARCH_RETURN_TYPE SearchResult;
-
- /* Acquire hive lock */
+ BOOLEAN FirstTry = TRUE, FlusherLocked = FALSE;
+ HCELL_INDEX ParentCell = HCELL_NIL, ChildCell = HCELL_NIL;
+
+ /* Acquire hive and KCB lock */
CmpLockRegistry();
CmpAcquireKcbLockShared(Kcb);
@@ -302,84 +629,119 @@
Status = STATUS_ACCESS_DENIED;
goto Quickie;
}
-
- /* Search for the value */
- SearchResult = CmpCompareNewValueDataAgainstKCBCache(Kcb,
- ValueName,
- Type,
- Data,
- DataLength);
- if (SearchResult == SearchNeedExclusiveLock)
- {
- /* Try again with the exclusive lock */
- CmpConvertKcbSharedToExclusive(Kcb);
+
+ /* 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 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);
- }
-
- /* Get pointer to key cell */
- Hive = Kcb->KeyHive;
- Cell = Kcb->KeyCell;
-
- /* Prepare to scan the key node */
- Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
- 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)
- {
- /* Get its value */
- Value = (PCM_KEY_VALUE)HvGetCell(Hive, CurrentChild);
- if (!Value)
+ 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;
}
- /* Remember that we found it */
- Found = TRUE;
- }
- }
- else
- {
- /* No child list, we'll need to add it */
- ChildIndex = 0;
- }
+ /* 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 */
- ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
- (CmpTestRegistryLockExclusive() == TRUE));
+ CMP_ASSERT_KCB_LOCK(Kcb);
/* Mark the cell dirty */
- HvMarkCellDirty(Hive, Cell, FALSE);
+ if (!HvMarkCellDirty(Hive, Cell, FALSE))
+ {
+ /* Not enough log space, fail */
+ Status = STATUS_NO_LOG_SPACE;
+ goto Quickie;
+ }
/* Get the storage type */
Storage = HvGetCellType(Cell);
@@ -388,8 +750,19 @@
SmallData = 0;
if ((DataLength <= CM_KEY_VALUE_SMALL) && (DataLength > 0))
{
- /* Copy it */
- RtlCopyMemory(&SmallData, Data, DataLength);
+ /* 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 */
@@ -442,7 +815,7 @@
/* Save the write time */
KeQuerySystemTime(&Parent->LastWriteTime);
- KeQuerySystemTime(&Kcb->KcbLastWriteTime);
+ Kcb->KcbLastWriteTime = Parent->LastWriteTime;
/* Check if the cell is cached */
if ((Found) && (CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)))
@@ -463,10 +836,21 @@
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;
@@ -504,15 +888,13 @@
/* 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);
- if (!Parent)
- {
- /* Fail */
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto Quickie;
- }
+ ASSERT(Parent);
/* Get the value list and check if it has any entries */
ChildList = &Parent->ValueList;
@@ -535,16 +917,26 @@
if (ChildCell == HCELL_NIL) goto Quickie;
/* We found the value, mark all relevant cells dirty */
- HvMarkCellDirty(Hive, Cell, FALSE);
- HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE);
- HvMarkCellDirty(Hive, ChildCell, FALSE);
+ 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);
- if (!Value) ASSERT(FALSE);
+ ASSERT(Value);
/* Mark it and all related data as dirty */
- CmpMarkValueDataDirty(Hive, Value);
+ if (!CmpMarkValueDataDirty(Hive, Value))
+ {
+ /* Not enough log space, fail */
+ Status = STATUS_NO_LOG_SPACE;
+ goto Quickie;
+ }
/* Ssanity checks */
ASSERT(HvIsCellDirty(Hive, Parent->ValueList.List));
@@ -552,7 +944,12 @@
/* Remove the value from the child list */
Status = CmpRemoveValueFromList(Hive, ChildIndex, ChildList);
- if(!NT_SUCCESS(Status)) goto Quickie;
+ if (!NT_SUCCESS(Status))
+ {
+ /* Set known error */
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Quickie;
+ }
/* Remove the value and its data itself */
if (!CmpFreeValue(Hive, ChildCell))
@@ -564,7 +961,7 @@
/* Set the last write time */
KeQuerySystemTime(&Parent->LastWriteTime);
- KeQuerySystemTime(&Kcb->KcbLastWriteTime);
+ Kcb->KcbLastWriteTime = Parent->LastWriteTime;
/* Sanity check */
ASSERT(Parent->MaxValueNameLen == Kcb->KcbMaxValueNameLen);
@@ -591,6 +988,9 @@
/* 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;
@@ -609,6 +1009,7 @@
}
/* Release locks */
+ CmpUnlockHiveFlusher((PCMHIVE)Hive);
CmpReleaseKcbLock(Kcb);
CmpUnlockRegistry();
return Status;
@@ -683,22 +1084,38 @@
/* Sanity check */
ASSERT(ValueData != NULL);
- /* Query the information requested */
- Result = CmpQueryKeyValueData(Kcb,
- CachedValue,
- ValueData,
- ValueCached,
- KeyValueInformationClass,
- KeyValueInformation,
- Length,
- ResultLength,
- &Status);
- if (Result == SearchNeedExclusiveLock)
- {
- /* Try with exclusive KCB lock */
- CmpConvertKcbSharedToExclusive(Kcb);
- goto DoAgain;
- }
+ /* 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
{
@@ -754,16 +1171,17 @@
/* Get the hive and parent */
Hive = Kcb->KeyHive;
Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
- if (!Parent)
- {
- /* Fail */
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto Quickie;
- }
-
- /* Make sure the index is valid */
- //if (Index >= Kcb->ValueCache.Count)
- if (Index >= Parent->ValueList.Count)
+ 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);
@@ -787,7 +1205,7 @@
{
/* Check if we need an exclusive lock */
ASSERT(CellToRelease == HCELL_NIL);
- ASSERT(ValueData == NULL);
+ HvReleaseCell(Hive, Kcb->KeyCell);
/* Try with exclusive KCB lock */
CmpConvertKcbSharedToExclusive(Kcb);
@@ -814,6 +1232,15 @@
&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;
@@ -827,23 +1254,38 @@
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Quickie;
}
-
- /* Query the information requested */
- Result = CmpQueryKeyValueData(Kcb,
- CachedValue,
- ValueData,
- ValueIsCached,
- KeyValueInformationClass,
- KeyValueInformation,
- Length,
- ResultLength,
- &Status);
- if (Result == SearchNeedExclusiveLock)
- {
- /* Try with exclusive KCB lock */
- CmpConvertKcbSharedToExclusive(Kcb);
- goto DoAgain;
- }
+
+ /* 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 */
@@ -858,221 +1300,6 @@
/* Release locks */
CmpReleaseKcbLock(Kcb);
CmpUnlockRegistry();
- return Status;
-}
-
-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;
}
@@ -1087,6 +1314,7 @@
NTSTATUS Status;
PHHIVE Hive;
PCM_KEY_NODE Parent;
+ HV_TRACK_CELL_REF CellReferences = {0};
/* Acquire hive lock */
CmpLockRegistry();
@@ -1094,16 +1322,6 @@
/* Lock KCB shared */
CmpAcquireKcbLockShared(Kcb);
- /* Get the hive and parent */
- Hive = Kcb->KeyHive;
- Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
- if (!Parent)
- {
- /* Fail */
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto Quickie;
- }
-
/* Don't touch deleted keys */
if (Kcb->Delete)
{
@@ -1120,13 +1338,27 @@
case KeyBasicInformation:
case KeyNodeInformation:
- /* Call the internal API */
- Status = CmpQueryKeyData(Hive,
- Parent,
- KeyInformationClass,
- KeyInformation,
- Length,
- ResultLength);
+ /* 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 */
@@ -1149,6 +1381,9 @@
}
Quickie:
+ /* Release references */
+ HvReleaseFreeCellRefArray(&CellReferences);
+
/* Release locks */
CmpReleaseKcbLock(Kcb);
CmpUnlockRegistry();
@@ -1168,6 +1403,7 @@
PHHIVE Hive;
PCM_KEY_NODE Parent, Child;
HCELL_INDEX ChildCell;
+ HV_TRACK_CELL_REF CellReferences = {0};
/* Acquire hive lock */
CmpLockRegistry();
@@ -1179,20 +1415,14 @@
if (Kcb->Delete)
{
/* Undo everything */
- CmpReleaseKcbLock(Kcb);
- CmpUnlockRegistry();
- return STATUS_KEY_DELETED;
+ Status = STATUS_KEY_DELETED;
+ goto Quickie;
}
/* Get the hive and parent */
Hive = Kcb->KeyHive;
Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
- if (!Parent)
- {
- /* Fail */
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto Quickie;
- }
+ ASSERT(Parent);
/* Get the child cell */
ChildCell = CmpFindSubKeyByNumber(Hive, Parent, Index);
@@ -1210,22 +1440,39 @@
/* Now get the actual child node */
Child = (PCM_KEY_NODE)HvGetCell(Hive, ChildCell);
- if (!Child)
- {
- /* Fail */
+ ASSERT(Child);
+
+ /* Track references */
+ if (!HvTrackCellRef(&CellReferences, Hive, ChildCell))
+ {
+ /* Can't allocate memory for tracking */
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Quickie;
}
- /* Query the data requested */
- Status = CmpQueryKeyData(Hive,
- Child,
- KeyInformationClass,
- KeyInformation,
- Length,
- ResultLength);
+ /* 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();
@@ -1271,14 +1518,12 @@
Hive = Kcb->KeyHive;
Cell = Kcb->KeyCell;
+ /* Lock flushes */
+ CmpLockHiveFlusherShared((PCMHIVE)Hive);
+
/* Get the key node */
Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
- if (!Node)
- {
- /* Fail */
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto Quickie;
- }
+ ASSERT(Node);
/* Sanity check */
ASSERT(Node->Flags == Kcb->Flags);
@@ -1287,11 +1532,17 @@
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);
@@ -1327,10 +1578,12 @@
Status = STATUS_CANNOT_DELETE;
}
-Quickie:
/* Release the cell */
HvReleaseCell(Hive, Cell);
-
+
+ /* Release flush lock */
+ CmpUnlockHiveFlusher((PCMHIVE)Hive);
+
/* Release the KCB locks */
Quickie2:
CmpReleaseTwoKcbLockByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey);
@@ -1364,12 +1617,36 @@
}
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 */
@@ -1387,8 +1664,9 @@
SECURITY_CLIENT_CONTEXT ClientSecurityContext;
HANDLE KeyHandle;
BOOLEAN Allocate = TRUE;
- PCMHIVE CmHive;
+ PCMHIVE CmHive, LoadedHive;
NTSTATUS Status;
+ CM_PARSE_CONTEXT ParseContext;
/* Check if we have a trust key */
if (KeyBody)
@@ -1415,9 +1693,21 @@
}
/* 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,
@@ -1437,20 +1727,28 @@
/* Lock the registry */
CmpLockRegistryExclusive();
- /* FIXME: Check if we are already loaded */
-
+ /* 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);
- DPRINT1("Failed: %lx\n", Status);
return Status;
}
/* Lock the registry shared */
CmpLockRegistry();
+
+ /* Lock loading */
+ ExAcquirePushLockExclusive(&CmpLoadHiveLock);
/* Lock the hive to this thread */
CmHive->Hive.HiveFlags |= HIVE_IS_UNLOADING;
@@ -1467,23 +1765,37 @@
TargetKey->SecurityDescriptor);
if (NT_SUCCESS(Status))
{
- /* FIXME: Add to HiveList key */
+ /* Add to HiveList key */
+ CmpAddToHiveFileList(CmHive);
/* Sync the hive if necessary */
if (Allocate)
{
- /* Sync it */
+ /* 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 */
Modified: trunk/reactos/ntoskrnl/config/cmhvlist.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/config/cmhvlist.c…
==============================================================================
--- trunk/reactos/ntoskrnl/config/cmhvlist.c [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/config/cmhvlist.c [iso-8859-1] Sat Apr 3 22:22:32 2010
@@ -14,4 +14,11 @@
/* FUNCTIONS *****************************************************************/
+NTSTATUS
+NTAPI
+CmpAddToHiveFileList(IN PCMHIVE Hive)
+{
+ return STATUS_SUCCESS;
+}
+
/* EOF */
Modified: trunk/reactos/ntoskrnl/config/cminit.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/config/cminit.c?r…
==============================================================================
--- trunk/reactos/ntoskrnl/config/cminit.c [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/config/cminit.c [iso-8859-1] Sat Apr 3 22:22:32 2010
@@ -119,12 +119,10 @@
if (!Hive->ViewLock) return STATUS_INSUFFICIENT_RESOURCES;
/* Allocate the flush lock */
-#if 0
Hive->FlusherLock = ExAllocatePoolWithTag(NonPagedPool,
sizeof(ERESOURCE),
TAG_CM);
if (!Hive->FlusherLock) return STATUS_INSUFFICIENT_RESOURCES;
-#endif
/* Setup the handles */
Hive->FileHandles[HFILE_TYPE_PRIMARY] = Primary;
@@ -136,7 +134,7 @@
Hive->ViewLockOwner = NULL;
/* Initialize the flush lock */
- ExInitializePushLock((PULONG_PTR)&Hive->FlusherLock);
+ ExInitializeResourceLite(Hive->FlusherLock);
/* Setup hive locks */
ExInitializePushLock((PULONG_PTR)&Hive->HiveLock);
@@ -193,9 +191,7 @@
{
/* Clear allocations and fail */
ExFreePool(Hive->ViewLock);
-#if 0
ExFreePool(Hive->FlusherLock);
-#endif
ExFreePool(Hive);
return Status;
}
@@ -211,9 +207,7 @@
{
/* Free all alocations */
ExFreePool(Hive->ViewLock);
-#if 0
ExFreePool(Hive->FlusherLock);
-#endif
ExFreePool(Hive);
return STATUS_REGISTRY_CORRUPT;
}
Modified: trunk/reactos/ntoskrnl/config/cmkcbncb.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/config/cmkcbncb.c…
==============================================================================
--- trunk/reactos/ntoskrnl/config/cmkcbncb.c [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/config/cmkcbncb.c [iso-8859-1] Sat Apr 3 22:22:32 2010
@@ -1135,3 +1135,62 @@
/* Unlock it it if we did a manual lock */
if (!LockHeld) CmpReleaseKcbLock(KeyBody->KeyControlBlock);
}
+
+VOID
+NTAPI
+CmpFlushNotifiesOnKeyBodyList(IN PCM_KEY_CONTROL_BLOCK Kcb,
+ IN BOOLEAN LockHeld)
+{
+ PLIST_ENTRY NextEntry, ListHead;
+ PCM_KEY_BODY KeyBody;
+
+ /* Sanity check */
+ LockHeld ? CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK() : CmpIsKcbLockedExclusive(Kcb);
+ while (TRUE)
+ {
+ /* Is the list empty? */
+ ListHead = &Kcb->KeyBodyListHead;
+ if (!IsListEmpty(ListHead))
+ {
+ /* Loop the list */
+ NextEntry = ListHead->Flink;
+ while (NextEntry != ListHead)
+ {
+ /* Get the key body */
+ KeyBody = CONTAINING_RECORD(NextEntry, CM_KEY_BODY, KeyBodyList);
+ ASSERT(KeyBody->Type == '20yk');
+
+ /* Check for notifications */
+ if (KeyBody->NotifyBlock)
+ {
+ /* Is the lock held? */
+ if (LockHeld)
+ {
+ /* Flush it */
+ CmpFlushNotify(KeyBody, LockHeld);
+ ASSERT(KeyBody->NotifyBlock == NULL);
+ continue;
+ }
+
+ /* Lock isn't held, so we need to take a reference */
+ if (ObReferenceObjectSafe(KeyBody))
+ {
+ /* Now we can flush */
+ CmpFlushNotify(KeyBody, LockHeld);
+ ASSERT(KeyBody->NotifyBlock == NULL);
+
+ /* Release the reference we took */
+ ObDereferenceObjectDeferDelete(KeyBody);
+ continue;
+ }
+ }
+
+ /* Try the next entry */
+ NextEntry = NextEntry->Flink;
+ }
+ }
+
+ /* List has been parsed, exit */
+ break;
+ }
+}
Modified: trunk/reactos/ntoskrnl/config/cmparse.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/config/cmparse.c?…
==============================================================================
--- trunk/reactos/ntoskrnl/config/cmparse.c [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/config/cmparse.c [iso-8859-1] Sat Apr 3 22:22:32 2010
@@ -414,15 +414,6 @@
LARGE_INTEGER TimeStamp;
PCM_KEY_NODE KeyNode;
- /* Sanity check */
-#if 0
- ASSERT((CmpIsKcbLockedExclusive(ParentKcb) == TRUE) ||
- (CmpTestRegistryLockExclusive() == TRUE));
-#endif
-
- /* Acquire the flusher lock */
- ExAcquirePushLockShared((PVOID)&((PCMHIVE)Hive)->FlusherLock);
-
/* Check if the parent is being deleted */
if (ParentKcb->Delete)
{
@@ -555,7 +546,6 @@
Exit:
/* Release the flusher lock and return status */
- ExReleasePushLock((PVOID)&((PCMHIVE)Hive)->FlusherLock);
return Status;
}
@@ -747,9 +737,6 @@
LARGE_INTEGER TimeStamp;
PCM_KEY_NODE KeyNode;
PCM_KEY_CONTROL_BLOCK Kcb = ParentKcb;
-#if 0
- CMP_ASSERT_REGISTRY_LOCK();
-#endif
/* Link nodes only allowed on the master */
if (Hive != &CmiVolatileHive->Hive)
@@ -759,10 +746,6 @@
return STATUS_ACCESS_DENIED;
}
- /* Acquire the flusher locks */
- ExAcquirePushLockShared((PVOID)&((PCMHIVE)Hive)->FlusherLock);
-
ExAcquirePushLockShared((PVOID)&((PCMHIVE)Context->ChildHive.KeyHive)->FlusherLock);
-
/* Check if the parent is being deleted */
if (ParentKcb->Delete)
{
@@ -964,8 +947,6 @@
Exit:
/* Release the flusher locks and return status */
-
ExReleasePushLock((PVOID)&((PCMHIVE)Context->ChildHive.KeyHive)->FlusherLock);
- ExReleasePushLock((PVOID)&((PCMHIVE)Hive)->FlusherLock);
return Status;
}