[NTOSKRNL/CONFIG]
authorAleksey Bragin <aleksey@reactos.org>
Sat, 3 Apr 2010 20:22:32 +0000 (20:22 +0000)
committerAleksey Bragin <aleksey@reactos.org>
Sat, 3 Apr 2010 20:22:32 +0000 (20:22 +0000)
- 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.

svn path=/trunk/; revision=46702

reactos/lib/cmlib/cmlib.h
reactos/lib/cmlib/hivecell.c
reactos/lib/cmlib/hivewrt.c
reactos/ntoskrnl/config/cmapi.c
reactos/ntoskrnl/config/cmhvlist.c
reactos/ntoskrnl/config/cminit.c
reactos/ntoskrnl/config/cmkcbncb.c
reactos/ntoskrnl/config/cmparse.c

index fae0495..c6823b0 100644 (file)
@@ -198,6 +198,22 @@ typedef struct _CMHIVE
 
 #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 @@ HvIsCellDirty(
     IN HCELL_INDEX Cell
 );
 
+BOOLEAN
+CMAPI
+HvHiveWillShrink(
+    IN PHHIVE RegistryHive
+);
+
 BOOLEAN CMAPI
 HvSyncHive(
    PHHIVE RegistryHive);
@@ -288,6 +310,21 @@ CmCreateRootNode(
 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.
index 994ca98..8151651 100644 (file)
@@ -113,7 +113,7 @@ HvMarkCellDirty(
        __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 @@ HvFreeCell(
    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;
+    }
+}
\ No newline at end of file
index 5cc20cf..7ecadff 100644 (file)
@@ -265,6 +265,14 @@ HvSyncHive(
    return TRUE;
 }
 
+BOOLEAN
+CMAPI
+HvHiveWillShrink(IN PHHIVE RegistryHive)
+{
+    /* No shrinking yet */
+    return FALSE;
+}
+
 BOOLEAN CMAPI
 HvWriteHive(
    PHHIVE RegistryHive)
index 85f2ed9..538c3e5 100644 (file)
 
 /* 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 @@ CmpDoFlushAll(IN BOOLEAN ForceFlush)
         if (!(Hive->Hive.HiveFlags & HIVE_NOLAZYFLUSH))
         {
             /* Acquire the flusher lock */
-            ExAcquirePushLockExclusive((PVOID)&Hive->FlusherLock);
-
-            /* Do the sync */
-            Status = HvSyncHive(&Hive->Hive);
+            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;
+                /* 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 @@ CmpSetValueKeyNew(IN PHHIVE Hive,
     {
         /* Then make sure it's valid and dirty it */
         ASSERT(Parent->ValueList.List != HCELL_NIL);
-        HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE);
+        if (!HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE))
+        {
+            /* Fail if we're out of space for log changes */
+            return STATUS_NO_LOG_SPACE;
+        }
     }
 
-    /* Allocate  avalue cell */
+    /* Allocate value cell */
     ValueCell = HvAllocateCell(Hive,
                                FIELD_OFFSET(CM_KEY_VALUE, Name) +
                                CmpNameSize(Hive, ValueName),
@@ -102,16 +186,33 @@ CmpSetValueKeyNew(IN PHHIVE Hive,
 
     /* 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 */
     if (DataSize > CM_KEY_VALUE_SMALL)
@@ -140,6 +241,9 @@ CmpSetValueKeyNew(IN PHHIVE Hive,
         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 @@ CmpSetValueKeyNew(IN PHHIVE Hive,
                                &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 @@ CmpSetValueKeyExisting(IN PHHIVE Hive,
     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 @@ CmpSetValueKeyExisting(IN PHHIVE Hive,
     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,246 +315,507 @@ CmpSetValueKeyExisting(IN PHHIVE Hive,
         Value->Type = Type;
         return STATUS_SUCCESS;
     }
-    else
+    
+    /* We have a normal key. Was the old cell also normal and had data? */
+    if (!(WasSmall) && (Length > 0))
     {
-        /* 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;
+        /* 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);
+        /* Immediately release the cell */
+        HvReleaseCell(Hive, DataCell);
 
-            /* Make sure that the data cell actually has a size */
-            ASSERT(HvGetCellSize(Hive, CellData) > 0);
+        /* 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;
-            }
+        /* Check if the previous data cell could fit our new data */
+        if (DataSize <= (ULONG)(HvGetCellSize(Hive, CellData)))
+        {
+            /* Re-use it then */
+            NewCell = DataCell;
         }
         else
         {
-            /* This was a small key, or a key with no data, allocate a cell */
-            NewCell = HvAllocateCell(Hive, DataSize, StorageType, HCELL_NIL);
+            /* 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);
+    /* Now get the actual data for our data cell */
+    CellData = HvGetCell(Hive, NewCell);
+    if (!CellData) ASSERT(FALSE);
 
-        /* Release it immediately */
-        HvReleaseCell(Hive, NewCell);
+    /* 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;
+    /* 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;
-    }
+    /* Return success */
+    ASSERT(HvIsCellDirty(Hive, NewCell));
+    return STATUS_SUCCESS;
 }
 
 NTSTATUS
 NTAPI
-CmSetValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
-              IN PUNICODE_STRING ValueName,
-              IN ULONG Type,
-              IN PVOID Data,
-              IN ULONG DataLength)
+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)
 {
-    PHHIVE Hive;
-    PCM_KEY_NODE Parent;
-    PCM_KEY_VALUE Value = NULL;
-    HCELL_INDEX CurrentChild, Cell;
     NTSTATUS Status;
-    BOOLEAN Found, Result;
-    ULONG Count, ChildIndex, SmallData, Storage;
-    VALUE_SEARCH_RETURN_TYPE SearchResult;
+    ULONG Size, SizeLeft, MinimumSize;
+    PKEY_INFORMATION Info = (PKEY_INFORMATION)KeyInformation;
+    USHORT NameLength;
 
-    /* Acquire hive lock */
-    CmpLockRegistry();
-    CmpAcquireKcbLockShared(Kcb);
-    
-    /* Sanity check */
-    ASSERT(sizeof(ULONG) == CM_KEY_VALUE_SMALL);
-    
-    /* Don't touch deleted KCBs */
-DoAgain:
-    if (Kcb->Delete)
-    {
-        /* Fail */
-        Status = STATUS_KEY_DELETED;
-        goto Quickie;
-    }
-    
-    /* Don't let anyone mess with symlinks */
-    if ((Kcb->Flags & KEY_SYM_LINK) &&
-        ((Type != REG_LINK) ||
-         !(ValueName) ||
-         !(RtlEqualUnicodeString(&CmSymbolicLinkValueName, ValueName, TRUE))))
-    {
-        /* Invalid modification of a symlink key */
-        Status = STATUS_ACCESS_DENIED;
-        goto Quickie;
-    }
-    
-    /* Search for the value */
-    SearchResult = CmpCompareNewValueDataAgainstKCBCache(Kcb,
-                                                         ValueName,
-                                                         Type,
-                                                         Data,
-                                                         DataLength);
-    if (SearchResult == SearchNeedExclusiveLock)
+    /* Check if the value is compressed */
+    if (Node->Flags & KEY_COMP_NAME)
     {
-        /* Try again with the exclusive lock */
-        CmpConvertKcbSharedToExclusive(Kcb);
-        goto DoAgain;
+        /* Get the compressed name size */
+        NameLength = CmpCompressedNameSize(Node->Name, Node->NameLength);
     }
-    else if (SearchResult == SearchSuccess)
+    else
     {
-        /* We don't actually need to do anything! */
-        Status = STATUS_SUCCESS;
-        goto Quickie;
+        /* Get the real size */
+        NameLength = Node->NameLength;
     }
 
-    /* We need the exclusive KCB lock now */
-    if (!(CmpIsKcbLockedExclusive(Kcb)) && !(CmpTryToConvertKcbSharedToExclusive(Kcb)))
+    /* Check what kind of information is being requested */
+    switch (KeyInformationClass)
     {
-        /* Acquire exclusive lock */
-        CmpConvertKcbSharedToExclusive(Kcb);
-    }
+        /* Basic information */
+        case KeyBasicInformation:
 
-    /* Get pointer to key cell */
-    Hive = Kcb->KeyHive;
-    Cell = Kcb->KeyCell;
+            /* This is the size we need */
+            Size = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name) + NameLength;
 
-    /* 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;
-        }
+            /* And this is the minimum we can work with */
+            MinimumSize = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name);
 
-        /* Check if we found something */
-        if (CurrentChild != HCELL_NIL)
-        {
-            /* Get its value */
-            Value = (PCM_KEY_VALUE)HvGetCell(Hive, CurrentChild);
-            if (!Value)
+            /* Let the caller know and assume success */
+            *ResultLength = Size;
+            Status = STATUS_SUCCESS;
+
+            /* Check if the bufer we got is too small */
+            if (Length < MinimumSize)
             {
-                /* Fail */
-                Status = STATUS_INSUFFICIENT_RESOURCES;
-                goto Quickie;
+                /* Let the caller know and fail */
+                Status = STATUS_BUFFER_TOO_SMALL;
+                break;
             }
 
-            /* Remember that we found it */
-            Found = TRUE;
-        }
-    }
-    else
-    {
-        /* No child list, we'll need to add it */
-        ChildIndex = 0;
-    }
-    
-    /* The KCB must be locked exclusive at this point */
-    ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
-           (CmpTestRegistryLockExclusive() == TRUE));
-    
-    /* Mark the cell dirty */
-    HvMarkCellDirty(Hive, Cell, FALSE);
+            /* Copy the basic information */
+            Info->KeyBasicInformation.LastWriteTime = Node->LastWriteTime;
+            Info->KeyBasicInformation.TitleIndex = 0;
+            Info->KeyBasicInformation.NameLength = NameLength;
 
-    /* Get the storage type */
-    Storage = HvGetCellType(Cell);
+            /* Only the name is left */
+            SizeLeft = Length - MinimumSize;
+            Size = NameLength;
 
-    /* Check if this is small data */
-    SmallData = 0;
-    if ((DataLength <= CM_KEY_VALUE_SMALL) && (DataLength > 0))
-    {
-        /* Copy it */
-        RtlCopyMemory(&SmallData, Data, DataLength);
-    }
+            /* 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 we didn't find a matching key */
-    if (!Found)
-    {
-        /* Call the internal routine */
-        Status = CmpSetValueKeyNew(Hive,
-                                   Parent,
-                                   ValueName,
-                                   ChildIndex,
-                                   Type,
-                                   Data,
-                                   DataLength,
-                                   Storage,
-                                   SmallData);
-    }
-    else
-    {
-        /* Call the internal routine */
-        Status = CmpSetValueKeyExisting(Hive,
-                                        CurrentChild,
-                                        Value,
-                                        Type,
-                                        Data,
-                                        DataLength,
-                                        Storage,
-                                        SmallData);
-    }
+            /* Check 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;
 
-    /* Check for success */
-    if (NT_SUCCESS(Status))
-    {
-        /* Check if the maximum value name length changed */
-        ASSERT(Parent->MaxValueNameLen == Kcb->KcbMaxValueNameLen);
-        if (Parent->MaxValueNameLen < ValueName->Length)
-        {
-            /* Set the new values */
-            Parent->MaxValueNameLen = ValueName->Length;
-            Kcb->KcbMaxValueNameLen = ValueName->Length;
-        }
-    
-        /* Check if the maximum data length changed */
-        ASSERT(Parent->MaxValueDataLen == Kcb->KcbMaxValueDataLen);
-        if (Parent->MaxValueDataLen < DataLength)
-        {
-            /* Update it */
-            Parent->MaxValueDataLen = DataLength;
-            Kcb->KcbMaxValueDataLen = Parent->MaxValueDataLen;
-        }
+        /* Node information */
+        case KeyNodeInformation:
+
+            /* Calculate the size we need */
+            Size = FIELD_OFFSET(KEY_NODE_INFORMATION, Name) +
+                   NameLength +
+                   Node->ClassLength;
+
+            /* And the minimum size we can support */
+            MinimumSize = FIELD_OFFSET(KEY_NODE_INFORMATION, Name);
+
+            /* Return the size to the caller and assume succes */
+            *ResultLength = Size;
+            Status = STATUS_SUCCESS;
+
+            /* Check if the caller's buffer is too small */
+            if (Length < MinimumSize)
+            {
+                /* Let them know, and fail */
+                Status = STATUS_BUFFER_TOO_SMALL;
+                break;
+            }
+
+            /* Copy the basic information */
+            Info->KeyNodeInformation.LastWriteTime = Node->LastWriteTime;
+            Info->KeyNodeInformation.TitleIndex = 0;
+            Info->KeyNodeInformation.ClassLength = Node->ClassLength;
+            Info->KeyNodeInformation.NameLength = NameLength;
+
+            /* Now the name is left */
+            SizeLeft = Length - MinimumSize;
+            Size = NameLength;
+
+            /* Check if the name can fit entirely */
+            if (SizeLeft < Size)
+            {
+                /* It can't, we'll have to truncate. Tell the caller */
+                Size = SizeLeft;
+                Status = STATUS_BUFFER_OVERFLOW;
+            }
+
+            /* Check if the key node name is compressed */
+            if (Node->Flags & KEY_COMP_NAME)
+            {
+                /* Copy the compressed name */
+                CmpCopyCompressedName(Info->KeyNodeInformation.Name,
+                                      SizeLeft,
+                                      Node->Name,
+                                      Node->NameLength);
+            }
+            else
+            {
+                /* It isn't, so copy the raw name */
+                RtlCopyMemory(Info->KeyNodeInformation.Name,
+                              Node->Name,
+                              Size);
+            }
+
+            /* Check if the node has a class */
+            if (Node->ClassLength > 0)
+            {
+                /* It does. We don't support these yet */
+                ASSERTMSG("Classes not supported\n", FALSE);
+            }
+            else
+            {
+                /* It doesn't, so set offset to -1, not 0! */
+                Info->KeyNodeInformation.ClassOffset = 0xFFFFFFFF;
+            }
+            break;
+
+        /* Full information requsted */
+        case KeyFullInformation:
+
+            /* This is the size we need */
+            Size = FIELD_OFFSET(KEY_FULL_INFORMATION, Class) +
+                   Node->ClassLength;
+
+            /* This is what we can work with */
+            MinimumSize = FIELD_OFFSET(KEY_FULL_INFORMATION, Class);
+
+            /* Return it to caller and assume success */
+            *ResultLength = Size;
+            Status = STATUS_SUCCESS;
+
+            /* Check if the caller's buffer is to small */
+            if (Length < MinimumSize)
+            {
+                /* Let them know and fail */
+                Status = STATUS_BUFFER_TOO_SMALL;
+                break;
+            }
+
+            /* Now copy all the basic information */
+            Info->KeyFullInformation.LastWriteTime = Node->LastWriteTime;
+            Info->KeyFullInformation.TitleIndex = 0;
+            Info->KeyFullInformation.ClassLength = Node->ClassLength;
+            Info->KeyFullInformation.SubKeys = Node->SubKeyCounts[Stable] +
+                                               Node->SubKeyCounts[Volatile];
+            Info->KeyFullInformation.Values = Node->ValueList.Count;
+            Info->KeyFullInformation.MaxNameLen = Node->MaxNameLen;
+            Info->KeyFullInformation.MaxClassLen = Node->MaxClassLen;
+            Info->KeyFullInformation.MaxValueNameLen = Node->MaxValueNameLen;
+            Info->KeyFullInformation.MaxValueDataLen = Node->MaxValueDataLen;
+
+            /* Check if we have a class */
+            if (Node->ClassLength > 0)
+            {
+                /* We do, but we currently don't support this */
+                ASSERTMSG("Classes not supported\n", FALSE);
+            }
+            else
+            {
+                /* We don't have a class, so set offset to -1, not 0! */
+                Info->KeyNodeInformation.ClassOffset = 0xFFFFFFFF;
+            }
+            break;
+
+        /* Any other class that got sent here is invalid! */
+        default:
+
+            /* Set failure code */
+            Status = STATUS_INVALID_PARAMETER;
+            break;
+    }
+
+    /* Return status */
+    return Status;
+}
+
+NTSTATUS
+NTAPI
+CmSetValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
+              IN PUNICODE_STRING ValueName,
+              IN ULONG Type,
+              IN PVOID Data,
+              IN ULONG DataLength)
+{
+    PHHIVE Hive = NULL;
+    PCM_KEY_NODE Parent;
+    PCM_KEY_VALUE Value = NULL;
+    HCELL_INDEX CurrentChild, Cell;
+    NTSTATUS Status;
+    BOOLEAN Found, Result;
+    ULONG Count, ChildIndex, SmallData, Storage;
+    VALUE_SEARCH_RETURN_TYPE SearchResult;
+    BOOLEAN FirstTry = TRUE, FlusherLocked = FALSE;
+    HCELL_INDEX ParentCell = HCELL_NIL, ChildCell = HCELL_NIL;
+
+    /* Acquire hive and KCB lock */
+    CmpLockRegistry();
+    CmpAcquireKcbLockShared(Kcb);
+    
+    /* Sanity check */
+    ASSERT(sizeof(ULONG) == CM_KEY_VALUE_SMALL);
+    
+    /* Don't touch deleted KCBs */
+DoAgain:
+    if (Kcb->Delete)
+    {
+        /* Fail */
+        Status = STATUS_KEY_DELETED;
+        goto Quickie;
+    }
+    
+    /* Don't let anyone mess with symlinks */
+    if ((Kcb->Flags & KEY_SYM_LINK) &&
+        ((Type != REG_LINK) ||
+         !(ValueName) ||
+         !(RtlEqualUnicodeString(&CmSymbolicLinkValueName, ValueName, TRUE))))
+    {
+        /* Invalid modification of a symlink key */
+        Status = STATUS_ACCESS_DENIED;
+        goto Quickie;
+    }
+
+    /* Check if this is the first attempt */
+    if (FirstTry)
+    {
+        /* Search for the value in the cache */
+        SearchResult = CmpCompareNewValueDataAgainstKCBCache(Kcb,
+                                                             ValueName,
+                                                             Type,
+                                                             Data,
+                                                             DataLength);
+        if (SearchResult == SearchNeedExclusiveLock)
+        {
+            /* Try again with the exclusive lock */
+            CmpConvertKcbSharedToExclusive(Kcb);
+            goto DoAgain;
+        }
+        else if (SearchResult == SearchSuccess)
+        {
+            /* We don't actually need to do anything! */
+            Status = STATUS_SUCCESS;
+            goto Quickie;
+        }
+
+        /* We need the exclusive KCB lock now */
+        if (!(CmpIsKcbLockedExclusive(Kcb)) &&
+            !(CmpTryToConvertKcbSharedToExclusive(Kcb)))
+        {
+            /* Acquire exclusive lock */
+            CmpConvertKcbSharedToExclusive(Kcb);
+        }
+        
+        /* Cache lookup failed, so don't try it next time */
+        FirstTry = FALSE;
+        
+        /* Now grab the flush lock since the key will be modified */
+        ASSERT(FlusherLocked == FALSE);
+        CmpLockHiveFlusherShared((PCMHIVE)Kcb->KeyHive);
+        FlusherLocked = TRUE;
+        goto DoAgain;
+    }
+    else
+    {
+        /* Get pointer to key cell */
+        Hive = Kcb->KeyHive;
+        Cell = Kcb->KeyCell;
+
+        /* Get the parent */
+        Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
+        ASSERT(Parent);
+        ParentCell = Cell;
+        
+        /* Prepare to scan the key node */
+        Count = Parent->ValueList.Count;
+        Found = FALSE;
+        if (Count > 0)
+        {
+            /* Try to find the existing name */
+            Result = CmpFindNameInList(Hive,
+                                       &Parent->ValueList,
+                                       ValueName,
+                                       &ChildIndex,
+                                       &CurrentChild);
+            if (!Result)
+            {
+                /* Fail */
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto Quickie;
+            }
+
+            /* Check if we found something */
+            if (CurrentChild != HCELL_NIL)
+            {
+                /* Release existing child */
+                if (ChildCell != HCELL_NIL)
+                {
+                    HvReleaseCell(Hive, ChildCell);
+                    ChildCell = HCELL_NIL;
+                }
+                
+                /* Get its value */
+                Value = (PCM_KEY_VALUE)HvGetCell(Hive, CurrentChild);
+                if (!Value)
+                {
+                    /* Fail */
+                    Status = STATUS_INSUFFICIENT_RESOURCES;
+                    goto Quickie;
+                }
+
+                /* Remember that we found it */
+                ChildCell = CurrentChild;
+                Found = TRUE;
+            }
+        }
+        else
+        {
+            /* No child list, we'll need to add it */
+            ChildIndex = 0;
+        }
+    }
+    
+    /* Should only get here on the second pass */
+    ASSERT(FirstTry == FALSE);
+    
+    /* The KCB must be locked exclusive at this point */
+    CMP_ASSERT_KCB_LOCK(Kcb);
+    
+    /* Mark the cell dirty */
+    if (!HvMarkCellDirty(Hive, Cell, FALSE))
+    {
+        /* Not enough log space, fail */
+        Status = STATUS_NO_LOG_SPACE;
+        goto Quickie;
+    }
+
+    /* Get the storage type */
+    Storage = HvGetCellType(Cell);
+
+    /* Check if this is small data */
+    SmallData = 0;
+    if ((DataLength <= CM_KEY_VALUE_SMALL) && (DataLength > 0))
+    {
+        /* Need SEH because user data may be invalid */
+        _SEH2_TRY
+        {
+            /* Copy it */
+            RtlCopyMemory(&SmallData, Data, DataLength);
+        }
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+        {
+            /* Return failure code */
+            Status = _SEH2_GetExceptionCode();
+            _SEH2_YIELD(goto Quickie);
+        }
+        _SEH2_END;
+    }
+
+    /* Check if we didn't find a matching key */
+    if (!Found)
+    {
+        /* Call the internal routine */
+        Status = CmpSetValueKeyNew(Hive,
+                                   Parent,
+                                   ValueName,
+                                   ChildIndex,
+                                   Type,
+                                   Data,
+                                   DataLength,
+                                   Storage,
+                                   SmallData);
+    }
+    else
+    {
+        /* Call the internal routine */
+        Status = CmpSetValueKeyExisting(Hive,
+                                        CurrentChild,
+                                        Value,
+                                        Type,
+                                        Data,
+                                        DataLength,
+                                        Storage,
+                                        SmallData);
+    }
+
+    /* Check for success */
+    if (NT_SUCCESS(Status))
+    {
+        /* Check if the maximum value name length changed */
+        ASSERT(Parent->MaxValueNameLen == Kcb->KcbMaxValueNameLen);
+        if (Parent->MaxValueNameLen < ValueName->Length)
+        {
+            /* Set the new values */
+            Parent->MaxValueNameLen = ValueName->Length;
+            Kcb->KcbMaxValueNameLen = ValueName->Length;
+        }
+    
+        /* Check if the maximum data length changed */
+        ASSERT(Parent->MaxValueDataLen == Kcb->KcbMaxValueDataLen);
+        if (Parent->MaxValueDataLen < DataLength)
+        {
+            /* Update it */
+            Parent->MaxValueDataLen = DataLength;
+            Kcb->KcbMaxValueDataLen = Parent->MaxValueDataLen;
+        }
         
         /* Save the write time */
         KeQuerySystemTime(&Parent->LastWriteTime);
-        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 @@ DoAgain:
             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 @@ CmDeleteValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
     /* 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 @@ CmDeleteValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
         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 @@ CmDeleteValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
 
         /* 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 @@ CmDeleteValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
 
         /* 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 @@ CmDeleteValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
         /* 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 @@ Quickie:
     }
 
     /* Release locks */
+    CmpUnlockHiveFlusher((PCMHIVE)Hive);
     CmpReleaseKcbLock(Kcb);
     CmpUnlockRegistry();
     return Status;
@@ -683,22 +1084,38 @@ DoAgain:
         /* 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
     {
@@ -735,344 +1152,154 @@ CmEnumerateValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
     PCM_KEY_VALUE ValueData = NULL;
     PAGED_CODE();
 
-    /* Acquire hive lock */
-    CmpLockRegistry();
-    
-    /* Lock the KCB shared */
-    CmpAcquireKcbLockShared(Kcb);
-
-    /* Don't touch deleted keys */
-DoAgain:
-    if (Kcb->Delete)
-    {
-        /* Undo everything */
-        CmpReleaseKcbLock(Kcb);
-        CmpUnlockRegistry();
-        return STATUS_KEY_DELETED;
-    }
-
-    /* Get the hive and parent */
-    Hive = Kcb->KeyHive;
-    Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
-    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)
-    {
-        /* Release the cell and fail */
-        HvReleaseCell(Hive, Kcb->KeyCell);
-        Status = STATUS_NO_MORE_ENTRIES;
-        goto Quickie;
-    }
-    
-    /* We don't deal with this yet */
-    if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)
-    {
-        /* Shouldn't happen */
-        ASSERT(FALSE);
-    }
-
-    /* Find the value list */
-    Result = CmpGetValueListFromCache(Kcb,
-                                      &CellData,
-                                      &IndexIsCached,
-                                      &CellToRelease);
-    if (Result == SearchNeedExclusiveLock)
-    {
-        /* Check if we need an exclusive lock */
-        ASSERT(CellToRelease == HCELL_NIL);
-        ASSERT(ValueData == NULL);
-        
-        /* Try with exclusive KCB lock */
-        CmpConvertKcbSharedToExclusive(Kcb);
-        goto DoAgain;
-    }
-    else if (Result != SearchSuccess)
-    {
-        /* Sanity check */
-        ASSERT(CellData == NULL);
-
-        /* Release the cell and fail */
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto Quickie;
-    }
-
-    /* Now get the key value */
-    Result = CmpGetValueKeyFromCache(Kcb,
-                                     CellData,
-                                     Index,
-                                     &CachedValue,
-                                     &ValueData,
-                                     IndexIsCached,
-                                     &ValueIsCached,
-                                     &CellToRelease2);
-    if (Result == SearchNeedExclusiveLock)
-    {
-        /* Try with exclusive KCB lock */
-        CmpConvertKcbSharedToExclusive(Kcb);
-        goto DoAgain;
-    }
-    else if (Result != SearchSuccess)
-    {
-        /* Sanity check */
-        ASSERT(ValueData == NULL);
-
-        /* Release the cells and fail */
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto Quickie;
-    }
-
-    /* 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;
-    }
-
-Quickie:
-    /* If we have a cell to release, do so */
-    if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);
-
-    /* Release the parent cell */
-    HvReleaseCell(Hive, Kcb->KeyCell);
-
-    /* If we have a cell to release, do so */
-    if (CellToRelease2 != HCELL_NIL) HvReleaseCell(Hive, CellToRelease2);
-
-    /* Release locks */
-    CmpReleaseKcbLock(Kcb);
-    CmpUnlockRegistry();
-    return Status;
-}
-
-NTSTATUS
-NTAPI
-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);
-            }
+    /* Acquire hive lock */
+    CmpLockRegistry();
+    
+    /* Lock the KCB shared */
+    CmpAcquireKcbLockShared(Kcb);
 
-            /* 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;
+    /* Don't touch deleted keys */
+DoAgain:
+    if (Kcb->Delete)
+    {
+        /* Undo everything */
+        CmpReleaseKcbLock(Kcb);
+        CmpUnlockRegistry();
+        return STATUS_KEY_DELETED;
+    }
 
-        /* Full information requsted */
-        case KeyFullInformation:
+    /* Get the hive and parent */
+    Hive = Kcb->KeyHive;
+    Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
+    ASSERT(Parent);
 
-            /* This is the size we need */
-            Size = FIELD_OFFSET(KEY_FULL_INFORMATION, Class) +
-                   Node->ClassLength;
+    /* FIXME: Lack of cache? */
+    if (Kcb->ValueCache.Count != Parent->ValueList.Count)
+    {
+        DPRINT1("HACK: Overriding value cache count\n");
+        Kcb->ValueCache.Count = Parent->ValueList.Count;
+    }
 
-            /* This is what we can work with */
-            MinimumSize = FIELD_OFFSET(KEY_FULL_INFORMATION, Class);
+    /* Make sure the index is valid */    
+    if (Index >= Kcb->ValueCache.Count)
+    {
+        /* Release the cell and fail */
+        HvReleaseCell(Hive, Kcb->KeyCell);
+        Status = STATUS_NO_MORE_ENTRIES;
+        goto Quickie;
+    }
+    
+    /* We don't deal with this yet */
+    if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)
+    {
+        /* Shouldn't happen */
+        ASSERT(FALSE);
+    }
 
-            /* Return it to caller and assume success */
-            *ResultLength = Size;
-            Status = STATUS_SUCCESS;
+    /* Find the value list */
+    Result = CmpGetValueListFromCache(Kcb,
+                                      &CellData,
+                                      &IndexIsCached,
+                                      &CellToRelease);
+    if (Result == SearchNeedExclusiveLock)
+    {
+        /* Check if we need an exclusive lock */
+        ASSERT(CellToRelease == HCELL_NIL);
+        HvReleaseCell(Hive, Kcb->KeyCell);
+        
+        /* Try with exclusive KCB lock */
+        CmpConvertKcbSharedToExclusive(Kcb);
+        goto DoAgain;
+    }
+    else if (Result != SearchSuccess)
+    {
+        /* Sanity check */
+        ASSERT(CellData == NULL);
 
-            /* Check if the caller's buffer is to small */
-            if (Length < MinimumSize)
-            {
-                /* Let them know and fail */
-                Status = STATUS_BUFFER_TOO_SMALL;
-                break;
-            }
+        /* Release the cell and fail */
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto Quickie;
+    }
 
-            /* 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;
+    /* Now get the key value */
+    Result = CmpGetValueKeyFromCache(Kcb,
+                                     CellData,
+                                     Index,
+                                     &CachedValue,
+                                     &ValueData,
+                                     IndexIsCached,
+                                     &ValueIsCached,
+                                     &CellToRelease2);
+    if (Result == SearchNeedExclusiveLock)
+    {
+        /* Cleanup state */
+        ASSERT(CellToRelease2 == HCELL_NIL);
+        if (CellToRelease)
+        {
+            HvReleaseCell(Hive, CellToRelease);
+            CellToRelease = HCELL_NIL;
+        }
+        HvReleaseCell(Hive, Kcb->KeyCell);
 
-            /* 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;
+        /* Try with exclusive KCB lock */
+        CmpConvertKcbSharedToExclusive(Kcb);
+        goto DoAgain;
+    }
+    else if (Result != SearchSuccess)
+    {
+        /* Sanity check */
+        ASSERT(ValueData == NULL);
 
-        /* Any other class that got sent here is invalid! */
-        default:
+        /* Release the cells and fail */
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto Quickie;
+    }
+    
+    /* User data, need SEH */
+    _SEH2_TRY
+    {
+        /* Query the information requested */
+        Result = CmpQueryKeyValueData(Kcb,
+                                      CachedValue,
+                                      ValueData,
+                                      ValueIsCached,
+                                      KeyValueInformationClass,
+                                      KeyValueInformation,
+                                      Length,
+                                      ResultLength,
+                                      &Status);
+        if (Result == SearchNeedExclusiveLock)
+        {
+            /* Cleanup state */
+            if (CellToRelease2) HvReleaseCell(Hive, CellToRelease2);
+            HvReleaseCell(Hive, Kcb->KeyCell);
+            if (CellToRelease) HvReleaseCell(Hive, CellToRelease);
 
-            /* Set failure code */
-            Status = STATUS_INVALID_PARAMETER;
-            break;
+            /* Try with exclusive KCB lock */
+            CmpConvertKcbSharedToExclusive(Kcb);
+            goto DoAgain;
+        }
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        /* Get exception code */
+        Status = _SEH2_GetExceptionCode();
     }
+    _SEH2_END;
 
-    /* Return status */
+Quickie:
+    /* If we have a cell to release, do so */
+    if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);
+
+    /* Release the parent cell */
+    HvReleaseCell(Hive, Kcb->KeyCell);
+
+    /* If we have a cell to release, do so */
+    if (CellToRelease2 != HCELL_NIL) HvReleaseCell(Hive, CellToRelease2);
+
+    /* Release locks */
+    CmpReleaseKcbLock(Kcb);
+    CmpUnlockRegistry();
     return Status;
 }
 
@@ -1087,6 +1314,7 @@ CmQueryKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
     NTSTATUS Status;
     PHHIVE Hive;
     PCM_KEY_NODE Parent;
+    HV_TRACK_CELL_REF CellReferences = {0};
 
     /* Acquire hive lock */
     CmpLockRegistry();
@@ -1094,16 +1322,6 @@ CmQueryKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
     /* 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 @@ CmQueryKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
         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 @@ CmQueryKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
     }
 
 Quickie:
+    /* Release references */
+    HvReleaseFreeCellRefArray(&CellReferences);
+
     /* Release locks */
     CmpReleaseKcbLock(Kcb);
     CmpUnlockRegistry();
@@ -1168,6 +1403,7 @@ CmEnumerateKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
     PHHIVE Hive;
     PCM_KEY_NODE Parent, Child;
     HCELL_INDEX ChildCell;
+    HV_TRACK_CELL_REF CellReferences = {0};
 
     /* Acquire hive lock */
     CmpLockRegistry();
@@ -1179,20 +1415,14 @@ CmEnumerateKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
     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 @@ CmEnumerateKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
 
     /* Now get the actual child node */
     Child = (PCM_KEY_NODE)HvGetCell(Hive, ChildCell);
-    if (!Child)
+    ASSERT(Child);
+    
+    /* Track references */
+    if (!HvTrackCellRef(&CellReferences, Hive, ChildCell))
     {
-        /* Fail */
+        /* 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 @@ CmDeleteKey(IN PCM_KEY_BODY KeyBody)
     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 @@ CmDeleteKey(IN PCM_KEY_BODY KeyBody)
     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 @@ CmDeleteKey(IN PCM_KEY_BODY KeyBody)
         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 @@ CmFlushKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
     }
     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 @@ CmLoadKey(IN POBJECT_ATTRIBUTES TargetKey,
     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 @@ CmLoadKey(IN POBJECT_ATTRIBUTES TargetKey,
     }
     
     /* 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,21 +1727,29 @@ CmLoadKey(IN POBJECT_ATTRIBUTES TargetKey,
             /* 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;
     CmHive->CreatorOwner = KeGetCurrentThread();
@@ -1467,23 +1765,37 @@ CmLoadKey(IN POBJECT_ATTRIBUTES TargetKey,
                                  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 */
index f4e9781..8fb2636 100644 (file)
 
 /* FUNCTIONS *****************************************************************/
 
+NTSTATUS
+NTAPI
+CmpAddToHiveFileList(IN PCMHIVE Hive)
+{
+    return STATUS_SUCCESS;
+}
+
 /* EOF */
\ No newline at end of file
index 154ba15..be7efc1 100644 (file)
@@ -119,12 +119,10 @@ CmpInitializeHive(OUT PCMHIVE *RegistryHive,
     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 @@ CmpInitializeHive(OUT PCMHIVE *RegistryHive,
     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 @@ CmpInitializeHive(OUT PCMHIVE *RegistryHive,
     {
         /* Clear allocations and fail */
         ExFreePool(Hive->ViewLock);
-#if 0
         ExFreePool(Hive->FlusherLock);
-#endif
         ExFreePool(Hive);
         return Status;
     }
@@ -211,9 +207,7 @@ CmpInitializeHive(OUT PCMHIVE *RegistryHive,
         {
             /* Free all alocations */
             ExFreePool(Hive->ViewLock);
-#if 0
             ExFreePool(Hive->FlusherLock);
-#endif
             ExFreePool(Hive);
             return STATUS_REGISTRY_CORRUPT;
         }
index 5e7060d..a26de07 100644 (file)
@@ -1135,3 +1135,62 @@ DelistKeyBodyFromKCB(IN PCM_KEY_BODY KeyBody,
     /* 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;
+    }
+}
index 42d7379..8263544 100644 (file)
@@ -414,15 +414,6 @@ CmpDoCreate(IN PHHIVE Hive,
     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 @@ CmpDoCreate(IN PHHIVE Hive,
 
 Exit:
     /* Release the flusher lock and return status */
-    ExReleasePushLock((PVOID)&((PCMHIVE)Hive)->FlusherLock);
     return Status;
 }
 
@@ -747,9 +737,6 @@ CmpCreateLinkNode(IN PHHIVE Hive,
     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 @@ CmpCreateLinkNode(IN PHHIVE Hive,
         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 @@ CmpCreateLinkNode(IN PHHIVE Hive,
     
 Exit:
     /* Release the flusher locks and return status */
-    ExReleasePushLock((PVOID)&((PCMHIVE)Context->ChildHive.KeyHive)->FlusherLock);
-    ExReleasePushLock((PVOID)&((PCMHIVE)Hive)->FlusherLock);
     return Status;
 }