Set the value/data cell to dirty, if an existing value is overwritten.
[reactos.git] / reactos / ntoskrnl / cm / ntfunc.c
index 91a3f95..e506b34 100644 (file)
@@ -1,21 +1,16 @@
-/*
- * COPYRIGHT:        See COPYING in the top level directory
- * PROJECT:          ReactOS kernel
- * FILE:             ntoskrnl/cm/ntfunc.c
- * PURPOSE:          Ntxxx function for registry access
- * UPDATE HISTORY:
-*/
+/* $Id$
+ *
+ * COPYRIGHT:       See COPYING in the top level directory
+ * PROJECT:         ReactOS kernel
+ * FILE:            ntoskrnl/cm/ntfunc.c
+ * PURPOSE:         Ntxxx function for registry access
+ *
+ * PROGRAMMERS:     No programmer listed.
+ */
 
 /* INCLUDES *****************************************************************/
 
-#include <ddk/ntddk.h>
-#include <roscfg.h>
-#include <internal/ob.h>
-#include <limits.h>
-#include <string.h>
-#include <internal/pool.h>
-#include <internal/registry.h>
-
+#include <ntoskrnl.h>
 #define NDEBUG
 #include <internal/debug.h>
 
 
 extern POBJECT_TYPE  CmiKeyType;
 extern PREGISTRY_HIVE  CmiVolatileHive;
+extern LIST_ENTRY CmiKeyObjectListHead;
 
 static BOOLEAN CmiRegistryInitialized = FALSE;
 
+LIST_ENTRY CmiCallbackHead;
+FAST_MUTEX CmiCallbackLock;
 
 /* FUNCTIONS ****************************************************************/
 
+/*
+ * @implemented
+ */
+NTSTATUS STDCALL
+CmRegisterCallback(IN PEX_CALLBACK_FUNCTION Function,
+                   IN PVOID Context,
+                   IN OUT PLARGE_INTEGER Cookie)
+{
+  PREGISTRY_CALLBACK Callback;
+
+  PAGED_CODE();
+
+  ASSERT(Function && Cookie);
+
+  Callback = ExAllocatePoolWithTag(PagedPool,
+                                   sizeof(REGISTRY_CALLBACK),
+                                   TAG('C', 'M', 'c', 'b'));
+  if(Callback != NULL)
+  {
+    /* initialize the callback */
+    ExInitializeRundownProtection(&Callback->RundownRef);
+    Callback->Function = Function;
+    Callback->Context = Context;
+    Callback->PendingDelete = FALSE;
+
+    /* add it to the callback list and receive a cookie for the callback */
+    ExAcquireFastMutex(&CmiCallbackLock);
+    /* FIXME - to receive a unique cookie we'll just return the pointer to the
+       callback object */
+    Callback->Cookie.QuadPart = (ULONG_PTR)Callback;
+    InsertTailList(&CmiCallbackHead, &Callback->ListEntry);
+
+    ExReleaseFastMutex(&CmiCallbackLock);
+
+    *Cookie = Callback->Cookie;
+    return STATUS_SUCCESS;
+  }
+
+  return STATUS_INSUFFICIENT_RESOURCES;
+}
+
+
+/*
+ * @implemented
+ */
+NTSTATUS STDCALL
+CmUnRegisterCallback(IN LARGE_INTEGER Cookie)
+{
+  PLIST_ENTRY CurrentEntry;
+
+  PAGED_CODE();
+
+  ExAcquireFastMutex(&CmiCallbackLock);
+
+  for(CurrentEntry = CmiCallbackHead.Flink;
+      CurrentEntry != &CmiCallbackHead;
+      CurrentEntry = CurrentEntry->Flink)
+  {
+    PREGISTRY_CALLBACK CurrentCallback;
+
+    CurrentCallback = CONTAINING_RECORD(CurrentEntry, REGISTRY_CALLBACK, ListEntry);
+    if(CurrentCallback->Cookie.QuadPart == Cookie.QuadPart)
+    {
+      if(!CurrentCallback->PendingDelete)
+      {
+        /* found the callback, don't unlink it from the list yet so we don't screw
+           the calling loop */
+        CurrentCallback->PendingDelete = TRUE;
+        ExReleaseFastMutex(&CmiCallbackLock);
+
+        /* if the callback is currently executing, wait until it finished */
+        ExWaitForRundownProtectionRelease(&CurrentCallback->RundownRef);
+
+        /* time to unlink it. It's now safe because every attempt to acquire a
+           runtime protection on this callback will fail */
+        ExAcquireFastMutex(&CmiCallbackLock);
+        RemoveEntryList(&CurrentCallback->ListEntry);
+        ExReleaseFastMutex(&CmiCallbackLock);
+
+        /* free the callback */
+        ExFreePool(CurrentCallback);
+        return STATUS_SUCCESS;
+      }
+      else
+      {
+        /* pending delete, pretend like it already is deleted */
+        ExReleaseFastMutex(&CmiCallbackLock);
+        return STATUS_UNSUCCESSFUL;
+      }
+    }
+  }
+
+  ExReleaseFastMutex(&CmiCallbackLock);
+
+  return STATUS_UNSUCCESSFUL;
+}
+
+
+NTSTATUS
+CmiCallRegisteredCallbacks(IN REG_NOTIFY_CLASS Argument1,
+                           IN PVOID Argument2)
+{
+  PLIST_ENTRY CurrentEntry;
+  NTSTATUS Status = STATUS_SUCCESS;
+
+  PAGED_CODE();
+
+  ExAcquireFastMutex(&CmiCallbackLock);
+
+  for(CurrentEntry = CmiCallbackHead.Flink;
+      CurrentEntry != &CmiCallbackHead;
+      CurrentEntry = CurrentEntry->Flink)
+  {
+    PREGISTRY_CALLBACK CurrentCallback;
+
+    CurrentCallback = CONTAINING_RECORD(CurrentEntry, REGISTRY_CALLBACK, ListEntry);
+    if(!CurrentCallback->PendingDelete &&
+       ExAcquireRundownProtectionEx(&CurrentCallback->RundownRef, 1))
+    {
+      /* don't hold locks during the callbacks! */
+      ExReleaseFastMutex(&CmiCallbackLock);
+
+      Status = CurrentCallback->Function(CurrentCallback->Context,
+                                         Argument1,
+                                         Argument2);
+
+      ExAcquireFastMutex(&CmiCallbackLock);
+      /* don't release the rundown protection before holding the callback lock
+         so the pointer to the next callback isn't cleared in case this callback
+         get's deleted */
+      ExReleaseRundownProtectionEx(&CurrentCallback->RundownRef, 1);
+      if(!NT_SUCCESS(Status))
+      {
+        /* one callback returned failure, don't call any more callbacks */
+        break;
+      }
+    }
+  }
+
+  ExReleaseFastMutex(&CmiCallbackLock);
+
+  return Status;
+}
+
+
 NTSTATUS STDCALL
 NtCreateKey(OUT PHANDLE KeyHandle,
            IN ACCESS_MASK DesiredAccess,
@@ -41,76 +184,187 @@ NtCreateKey(OUT PHANDLE KeyHandle,
            IN ULONG CreateOptions,
            OUT PULONG Disposition)
 {
-  UNICODE_STRING RemainingPath;
+  UNICODE_STRING RemainingPath = {0};
+  BOOLEAN FreeRemainingPath = TRUE;
+  ULONG LocalDisposition;
   PKEY_OBJECT KeyObject;
-  NTSTATUS Status;
-  PVOID Object;
-  PWSTR End;
-
-  DPRINT("NtCreateKey (Name %wZ  KeyHandle %x  Root %x)\n",
-        ObjectAttributes->ObjectName,
-        KeyHandle,
-        ObjectAttributes->RootDirectory);
-
-  /* FIXME: check for standard handle prefix and adjust objectAttributes accordingly */
+  NTSTATUS Status = STATUS_SUCCESS;
+  PVOID Object = NULL;
+  PWSTR Start;
+  UNICODE_STRING ObjectName;
+  OBJECT_CREATE_INFORMATION ObjectCreateInfo;
+  unsigned i;
+  REG_PRE_CREATE_KEY_INFORMATION PreCreateKeyInfo;
+  REG_POST_CREATE_KEY_INFORMATION PostCreateKeyInfo;
+  KPROCESSOR_MODE PreviousMode;
+  UNICODE_STRING CapturedClass = {0};
+  HANDLE hKey;
+
+  PAGED_CODE();
+
+  PreviousMode = KeGetPreviousMode();
+
+  if (PreviousMode != KernelMode)
+  {
+      _SEH_TRY
+      {
+          ProbeForWriteHandle(KeyHandle);
+          if (Disposition != NULL)
+          {
+              ProbeForWriteUlong(Disposition);
+          }
+      }
+      _SEH_HANDLE
+      {
+          Status = _SEH_GetExceptionCode();
+      }
+      _SEH_END;
+      
+      if (!NT_SUCCESS(Status))
+      {
+          return Status;
+      }
+  }
+  
+  if (Class != NULL)
+  {
+      Status = ProbeAndCaptureUnicodeString(&CapturedClass,
+                                            PreviousMode,
+                                            Class);
+      if (!NT_SUCCESS(Status))
+      {
+          return Status;
+      }
+  }
+
+  /* Capture all the info */
+  DPRINT("Capturing Create Info\n");
+  Status = ObpCaptureObjectAttributes(ObjectAttributes,
+                                      PreviousMode,
+                                      CmiKeyType,
+                                      &ObjectCreateInfo,
+                                      &ObjectName);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("ObpCaptureObjectAttributes() failed (Status %lx)\n", Status);
+      goto Cleanup;
+    }
 
-  Status = ObFindObject(ObjectAttributes,
-                       &Object,
-                       &RemainingPath,
-                       CmiKeyType);
+  PostCreateKeyInfo.CompleteName = &ObjectName;
+  PreCreateKeyInfo.CompleteName = &ObjectName;
+  Status = CmiCallRegisteredCallbacks(RegNtPreCreateKey, &PreCreateKeyInfo);
   if (!NT_SUCCESS(Status))
     {
-      return(Status);
+      ObpReleaseCapturedAttributes(&ObjectCreateInfo);
+      goto Cleanup;
+    }
+    
+  Status = ObFindObject(&ObjectCreateInfo,
+                        &ObjectName,
+                        (PVOID*)&Object,
+                        &RemainingPath,
+                        CmiKeyType);
+  ObpReleaseCapturedAttributes(&ObjectCreateInfo);
+  if (!NT_SUCCESS(Status))
+    {
+      PostCreateKeyInfo.Object = NULL;
+      PostCreateKeyInfo.Status = Status;
+      CmiCallRegisteredCallbacks(RegNtPostCreateKey, &PostCreateKeyInfo);
+
+      DPRINT("CmpFindObject failed, Status: 0x%x\n", Status);
+      goto Cleanup;
     }
 
   DPRINT("RemainingPath %wZ\n", &RemainingPath);
 
-  if ((RemainingPath.Buffer == NULL) || (RemainingPath.Buffer[0] == 0))
+  if (RemainingPath.Length == 0)
     {
       /* Fail if the key has been deleted */
       if (((PKEY_OBJECT) Object)->Flags & KO_MARKED_FOR_DELETE)
        {
-         ObDereferenceObject(Object);
-         return(STATUS_UNSUCCESSFUL);
-       }
+          PostCreateKeyInfo.Object = NULL;
+          PostCreateKeyInfo.Status = STATUS_UNSUCCESSFUL;
+          CmiCallRegisteredCallbacks(RegNtPostCreateKey, &PostCreateKeyInfo);
 
-      if (Disposition)
-       *Disposition = REG_OPENED_EXISTING_KEY;
+         DPRINT("Object marked for delete!\n");
+         Status = STATUS_UNSUCCESSFUL;
+         goto Cleanup;
+       }
 
-      Status = ObCreateHandle(PsGetCurrentProcess(),
+      Status = ObpCreateHandle(PsGetCurrentProcess(),
                              Object,
                              DesiredAccess,
-                             FALSE,
-                             KeyHandle);
+                             TRUE,
+                             &hKey);
 
-      DPRINT("Status %x\n", Status);
-      ObDereferenceObject(Object);
-      return Status;
+      DPRINT("ObpCreateHandle failed Status 0x%x\n", Status);
+
+      PostCreateKeyInfo.Object = NULL;
+      PostCreateKeyInfo.Status = Status;
+      CmiCallRegisteredCallbacks(RegNtPostCreateKey, &PostCreateKeyInfo);
+
+      LocalDisposition = REG_OPENED_EXISTING_KEY;
+      goto SuccessReturn;
     }
 
   /* If RemainingPath contains \ we must return error
-     because NtCreateKey don't create trees */
-  if (RemainingPath.Buffer[0] == '\\')
-    End = wcschr(RemainingPath.Buffer + 1, '\\');
-  else
-    End = wcschr(RemainingPath.Buffer, '\\');
+     because NtCreateKey doesn't create trees */
+  Start = RemainingPath.Buffer;
+  if (*Start == L'\\')
+    Start++;
 
-  if (End != NULL)
+  for (i = 1; i < RemainingPath.Length / sizeof(WCHAR); i++)
     {
-      ObDereferenceObject(Object);
-      return STATUS_OBJECT_NAME_NOT_FOUND;
+      if (L'\\' == RemainingPath.Buffer[i])
+        {
+          DPRINT("NtCreateKey() doesn't create trees! (found \'\\\' in remaining path: \"%wZ\"!)\n", &RemainingPath);
+
+          PostCreateKeyInfo.Object = NULL;
+          PostCreateKeyInfo.Status = STATUS_OBJECT_NAME_NOT_FOUND;
+          CmiCallRegisteredCallbacks(RegNtPostCreateKey, &PostCreateKeyInfo);
+
+          Status = STATUS_OBJECT_NAME_NOT_FOUND;
+          goto Cleanup;
+        }
     }
 
-  DPRINT("RemainingPath %S  ParentObject %x\n", RemainingPath.Buffer, Object);
+  DPRINT("RemainingPath %S  ParentObject 0x%p\n", RemainingPath.Buffer, Object);
 
-  Status = ObCreateObject(KeyHandle,
-                         DesiredAccess,
-                         NULL,
+  Status = ObCreateObject(PreviousMode,
                          CmiKeyType,
+                         NULL,
+                         PreviousMode,
+                         NULL,
+                         sizeof(KEY_OBJECT),
+                         0,
+                         0,
                          (PVOID*)&KeyObject);
   if (!NT_SUCCESS(Status))
     {
-      return(Status);
+      DPRINT1("ObCreateObject() failed!\n");
+      PostCreateKeyInfo.Object = NULL;
+      PostCreateKeyInfo.Status = Status;
+      CmiCallRegisteredCallbacks(RegNtPostCreateKey, &PostCreateKeyInfo);
+
+      goto Cleanup;
+    }
+
+  Status = ObInsertObject((PVOID)KeyObject,
+                         NULL,
+                         DesiredAccess,
+                         0,
+                         NULL,
+                         &hKey);
+  if (!NT_SUCCESS(Status))
+    {
+      ObDereferenceObject(KeyObject);
+      DPRINT1("ObInsertObject() failed!\n");
+
+      PostCreateKeyInfo.Object = NULL;
+      PostCreateKeyInfo.Status = Status;
+      CmiCallRegisteredCallbacks(RegNtPostCreateKey, &PostCreateKeyInfo);
+
+      goto Cleanup;
     }
 
   KeyObject->ParentKey = Object;
@@ -126,33 +380,48 @@ NtCreateKey(OUT PHANDLE KeyHandle,
   KeyObject->SubKeys = NULL;
 
   /* Acquire hive lock */
-  ExAcquireResourceExclusiveLite(&KeyObject->RegistryHive->HiveResource, TRUE);
+  KeEnterCriticalRegion();
+  ExAcquireResourceExclusiveLite(&CmiRegistryLock, TRUE);
+
+  InsertTailList(&CmiKeyObjectListHead, &KeyObject->ListEntry);
 
   /* add key to subkeys of parent if needed */
   Status = CmiAddSubKey(KeyObject->RegistryHive,
                        KeyObject->ParentKey,
                        KeyObject,
-                       RemainingPath.Buffer,
-                       RemainingPath.Length,
+                       &RemainingPath,
                        TitleIndex,
-                       Class,
+                       &CapturedClass,
                        CreateOptions);
   if (!NT_SUCCESS(Status))
     {
       DPRINT("CmiAddSubKey() failed (Status %lx)\n", Status);
       /* Release hive lock */
-      ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
+      ExReleaseResourceLite(&CmiRegistryLock);
+      KeLeaveCriticalRegion();
       ObDereferenceObject(KeyObject);
-      ObDereferenceObject(Object);
-      return STATUS_UNSUCCESSFUL;
+
+      PostCreateKeyInfo.Object = NULL;
+      PostCreateKeyInfo.Status = STATUS_UNSUCCESSFUL;
+      CmiCallRegisteredCallbacks(RegNtPostCreateKey, &PostCreateKeyInfo);
+
+      Status = STATUS_UNSUCCESSFUL;
+      goto Cleanup;
     }
 
-  KeyObject->Name = KeyObject->KeyCell->Name;
-  KeyObject->NameSize = KeyObject->KeyCell->NameSize;
+  if (Start == RemainingPath.Buffer)
+    {
+      KeyObject->Name = RemainingPath;
+      FreeRemainingPath = FALSE;
+    }
+  else
+    {
+      RtlpCreateUnicodeString(&KeyObject->Name, Start, NonPagedPool);
+    }
 
   if (KeyObject->RegistryHive == KeyObject->ParentKey->RegistryHive)
     {
-      KeyObject->KeyCell->ParentKeyOffset = KeyObject->ParentKey->BlockOffset;
+      KeyObject->KeyCell->ParentKeyOffset = KeyObject->ParentKey->KeyCellOffset;
       KeyObject->KeyCell->SecurityKeyOffset = KeyObject->ParentKey->KeyCell->SecurityKeyOffset;
     }
   else
@@ -161,10 +430,7 @@ NtCreateKey(OUT PHANDLE KeyHandle,
       KeyObject->KeyCell->SecurityKeyOffset = -1;
       /* This key must remain in memory unless it is deleted
         or file is unloaded */
-      ObReferenceObjectByPointer(KeyObject,
-                                STANDARD_RIGHTS_REQUIRED,
-                                NULL,
-                                UserMode);
+      ObReferenceObject(KeyObject);
     }
 
   CmiAddKeyToList(KeyObject->ParentKey, KeyObject);
@@ -172,15 +438,41 @@ NtCreateKey(OUT PHANDLE KeyHandle,
   VERIFY_KEY_OBJECT(KeyObject);
 
   /* Release hive lock */
-  ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
-
-  ObDereferenceObject(KeyObject);
-  ObDereferenceObject(Object);
+  ExReleaseResourceLite(&CmiRegistryLock);
+  KeLeaveCriticalRegion();
 
-  if (Disposition)
-    *Disposition = REG_CREATED_NEW_KEY;
+  PostCreateKeyInfo.Object = KeyObject;
+  PostCreateKeyInfo.Status = Status;
+  CmiCallRegisteredCallbacks(RegNtPostCreateKey, &PostCreateKeyInfo);
 
   CmiSyncHives();
+  
+  LocalDisposition = REG_CREATED_NEW_KEY;
+
+SuccessReturn:
+  _SEH_TRY
+  {
+      *KeyHandle = hKey;
+      if (Disposition != NULL)
+      {
+          *Disposition = LocalDisposition;
+      }
+  }
+  _SEH_HANDLE
+  {
+      Status = _SEH_GetExceptionCode();
+  }
+  _SEH_END;
+
+Cleanup:
+  if (Class != NULL)
+  {
+    ReleaseCapturedUnicodeString(&CapturedClass,
+                                 PreviousMode);
+  }
+  if (ObjectName.Buffer) ExFreePool(ObjectName.Buffer);
+  if (FreeRemainingPath) RtlFreeUnicodeString(&RemainingPath);
+  if (Object != NULL) ObDereferenceObject(Object);
 
   return Status;
 }
@@ -189,25 +481,45 @@ NtCreateKey(OUT PHANDLE KeyHandle,
 NTSTATUS STDCALL
 NtDeleteKey(IN HANDLE KeyHandle)
 {
+  KPROCESSOR_MODE PreviousMode;
   PKEY_OBJECT KeyObject;
   NTSTATUS Status;
+  REG_DELETE_KEY_INFORMATION DeleteKeyInfo;
+  REG_POST_OPERATION_INFORMATION PostOperationInfo;
 
-  DPRINT("KeyHandle %x\n", KeyHandle);
+  PAGED_CODE();
+
+  DPRINT("NtDeleteKey(KeyHandle 0x%p) called\n", KeyHandle);
+
+  PreviousMode = ExGetPreviousMode();
 
   /* Verify that the handle is valid and is a registry key */
   Status = ObReferenceObjectByHandle(KeyHandle,
-                                    KEY_WRITE,
+                                    DELETE,
                                     CmiKeyType,
-                                    UserMode,
+                                    PreviousMode,
                                     (PVOID *)&KeyObject,
                                     NULL);
   if (!NT_SUCCESS(Status))
     {
-      return(Status);
+      DPRINT1("ObReferenceObjectByHandle() failed (Status %lx)\n", Status);
+      return Status;
+    }
+
+  PostOperationInfo.Object = (PVOID)KeyObject;
+  DeleteKeyInfo.Object = (PVOID)KeyObject;
+  Status = CmiCallRegisteredCallbacks(RegNtPreSetValueKey, &DeleteKeyInfo);
+  if (!NT_SUCCESS(Status))
+    {
+      PostOperationInfo.Status = Status;
+      CmiCallRegisteredCallbacks(RegNtDeleteKey, &PostOperationInfo);
+      ObDereferenceObject(KeyObject);
+      return Status;
     }
 
   /* Acquire hive lock */
-  ExAcquireResourceExclusiveLite(&KeyObject->RegistryHive->HiveResource, TRUE);
+  KeEnterCriticalRegion();
+  ExAcquireResourceExclusiveLite(&CmiRegistryLock, TRUE);
 
   VERIFY_KEY_OBJECT(KeyObject);
 
@@ -224,16 +536,25 @@ NtDeleteKey(IN HANDLE KeyHandle)
     }
 
   /* Release hive lock */
-  ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
+  ExReleaseResourceLite(&CmiRegistryLock);
+  KeLeaveCriticalRegion();
 
   DPRINT("PointerCount %lu\n", ObGetObjectPointerCount((PVOID)KeyObject));
 
-  /* Dereference the object */
+  /* Remove the keep-alive reference */
   ObDereferenceObject(KeyObject);
-  if(KeyObject->RegistryHive != KeyObject->ParentKey->RegistryHive)
+
+  if (KeyObject->RegistryHive != KeyObject->ParentKey->RegistryHive)
     ObDereferenceObject(KeyObject);
 
+  PostOperationInfo.Status = Status;
+  CmiCallRegisteredCallbacks(RegNtPostDeleteKey, &PostOperationInfo);
+
+  /* Dereference the object */
+  ObDereferenceObject(KeyObject);
+
   DPRINT("PointerCount %lu\n", ObGetObjectPointerCount((PVOID)KeyObject));
+  DPRINT("HandleCount %lu\n", ObGetObjectHandleCount((PVOID)KeyObject));
 
   /*
    * Note:
@@ -242,31 +563,38 @@ NtDeleteKey(IN HANDLE KeyHandle)
    * have been released.
    */
 
-  return(Status);
+  return Status;
 }
 
 
 NTSTATUS STDCALL
-NtEnumerateKey(
-       IN      HANDLE                  KeyHandle,
-       IN      ULONG                     Index,
-       IN      KEY_INFORMATION_CLASS   KeyInformationClass,
-       OUT     PVOID                     KeyInformation,
-       IN      ULONG                     Length,
-       OUT     PULONG                  ResultLength
-       )
+NtEnumerateKey(IN HANDLE KeyHandle,
+              IN ULONG Index,
+              IN KEY_INFORMATION_CLASS KeyInformationClass,
+              OUT PVOID KeyInformation,
+              IN ULONG Length,
+              OUT PULONG ResultLength)
 {
-  NTSTATUS  Status;
-  PKEY_OBJECT  KeyObject;
+  PKEY_OBJECT KeyObject;
+  PKEY_OBJECT SubKeyObject;
   PREGISTRY_HIVE  RegistryHive;
   PKEY_CELL  KeyCell, SubKeyCell;
   PHASH_TABLE_CELL  HashTableBlock;
   PKEY_BASIC_INFORMATION  BasicInformation;
   PKEY_NODE_INFORMATION  NodeInformation;
   PKEY_FULL_INFORMATION  FullInformation;
-  PDATA_CELL pClassData;
+  PDATA_CELL ClassCell;
+  ULONG NameSize, ClassSize;
+  KPROCESSOR_MODE PreviousMode;
+  NTSTATUS Status;
+  REG_ENUMERATE_KEY_INFORMATION EnumerateKeyInfo;
+  REG_POST_OPERATION_INFORMATION PostOperationInfo;
 
-  DPRINT("KH %x  I %d  KIC %x KI %x  L %d  RL %x\n",
+  PAGED_CODE();
+
+  PreviousMode = ExGetPreviousMode();
+
+  DPRINT("KH 0x%p  I %d  KIC %x KI 0x%p  L %d  RL 0x%p\n",
         KeyHandle,
         Index,
         KeyInformationClass,
@@ -278,7 +606,7 @@ NtEnumerateKey(
   Status = ObReferenceObjectByHandle(KeyHandle,
                KEY_ENUMERATE_SUB_KEYS,
                CmiKeyType,
-               UserMode,
+               PreviousMode,
                (PVOID *) &KeyObject,
                NULL);
   if (!NT_SUCCESS(Status))
@@ -287,8 +615,23 @@ NtEnumerateKey(
       return(Status);
     }
 
+  PostOperationInfo.Object = (PVOID)KeyObject;
+  EnumerateKeyInfo.Object = (PVOID)KeyObject;
+  EnumerateKeyInfo.Index = Index;
+  EnumerateKeyInfo.KeyInformationClass = KeyInformationClass;
+  EnumerateKeyInfo.Length = Length;
+  EnumerateKeyInfo.ResultLength = ResultLength;
+
+  Status = CmiCallRegisteredCallbacks(RegNtEnumerateKey, &EnumerateKeyInfo);
+  if (!NT_SUCCESS(Status))
+    {
+      ObDereferenceObject(KeyObject);
+      return Status;
+    }
+
   /* Acquire hive lock */
-  ExAcquireResourceSharedLite(&KeyObject->RegistryHive->HiveResource, TRUE);
+  KeEnterCriticalRegion();
+  ExAcquireResourceSharedLite(&CmiRegistryLock, TRUE);
 
   VERIFY_KEY_OBJECT(KeyObject);
 
@@ -296,58 +639,78 @@ NtEnumerateKey(
   KeyCell = KeyObject->KeyCell;
   RegistryHive = KeyObject->RegistryHive;
 
+  SubKeyObject = NULL;
+
+  /* Check for hightest possible sub key index */
+  if (Index >= KeyCell->NumberOfSubKeys + KeyObject->NumberOfSubKeys)
+    {
+      ExReleaseResourceLite(&CmiRegistryLock);
+      KeLeaveCriticalRegion();
+      PostOperationInfo.Status = STATUS_NO_MORE_ENTRIES;
+      CmiCallRegisteredCallbacks(RegNtPostDeleteKey, &PostOperationInfo);
+      ObDereferenceObject(KeyObject);
+      DPRINT("No more volatile entries\n");
+      return STATUS_NO_MORE_ENTRIES;
+    }
+
   /* Get pointer to SubKey */
   if (Index >= KeyCell->NumberOfSubKeys)
     {
-      if (RegistryHive == CmiVolatileHive)
-       {
-         ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
-         ObDereferenceObject(KeyObject);
-         DPRINT("No more volatile entries\n");
-         return(STATUS_NO_MORE_ENTRIES);
-       }
-      else
-       {
-         ULONG i;
-         PKEY_OBJECT CurKey = NULL;
+      PKEY_OBJECT CurKey = NULL;
+      ULONG i;
+      ULONG j;
 
-         /* Search volatile keys */
-         for (i = 0; i < KeyObject->NumberOfSubKeys; i++)
-           {
-             CurKey = KeyObject->SubKeys[i];
-             if (CurKey->RegistryHive == CmiVolatileHive)
-               {
-                 if (Index-- == KeyObject->NumberOfSubKeys)
-                   break;
-               }
-           }
-         if (Index >= KeyCell->NumberOfSubKeys)
+      /* Search for volatile or 'foreign' keys */
+      j = KeyCell->NumberOfSubKeys;
+      for (i = 0; i < KeyObject->NumberOfSubKeys; i++)
+       {
+         CurKey = KeyObject->SubKeys[i];
+         if (CurKey->RegistryHive != RegistryHive)
            {
-             ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
-             ObDereferenceObject(KeyObject);
-             DPRINT("No more non-volatile entries\n");
-             return(STATUS_NO_MORE_ENTRIES);
+             if (j == Index)
+               break;
+             j++;
            }
-         SubKeyCell = CurKey->KeyCell;
        }
+
+      if (i >= KeyObject->NumberOfSubKeys)
+       {
+         ExReleaseResourceLite(&CmiRegistryLock);
+         KeLeaveCriticalRegion();
+          PostOperationInfo.Status = STATUS_NO_MORE_ENTRIES;
+          CmiCallRegisteredCallbacks(RegNtPostEnumerateKey, &PostOperationInfo);
+         ObDereferenceObject(KeyObject);
+         DPRINT("No more non-volatile entries\n");
+         return STATUS_NO_MORE_ENTRIES;
+       }
+
+      SubKeyObject = CurKey;
+      SubKeyCell = CurKey->KeyCell;
     }
   else
     {
       if (KeyCell->HashTableOffset == (BLOCK_OFFSET)-1)
        {
-         return(STATUS_NO_MORE_ENTRIES);
-         ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
+         ExReleaseResourceLite(&CmiRegistryLock);
+         KeLeaveCriticalRegion();
+          PostOperationInfo.Status = STATUS_NO_MORE_ENTRIES;
+          CmiCallRegisteredCallbacks(RegNtPostEnumerateKey, &PostOperationInfo);
          ObDereferenceObject(KeyObject);
+         return STATUS_NO_MORE_ENTRIES;
        }
 
-      HashTableBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
+      HashTableBlock = CmiGetCell (RegistryHive, KeyCell->HashTableOffset, NULL);
       if (HashTableBlock == NULL)
        {
          DPRINT("CmiGetBlock() failed\n");
-         ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
+         ExReleaseResourceLite(&CmiRegistryLock);
+         KeLeaveCriticalRegion();
+          PostOperationInfo.Status = STATUS_UNSUCCESSFUL;
+          CmiCallRegisteredCallbacks(RegNtPostEnumerateKey, &PostOperationInfo);
          ObDereferenceObject(KeyObject);
          return STATUS_UNSUCCESSFUL;
        }
+
       SubKeyCell = CmiGetKeyFromHashByIndex(RegistryHive,
                                            HashTableBlock,
                                            Index);
@@ -355,116 +718,229 @@ NtEnumerateKey(
 
   if (SubKeyCell == NULL)
     {
-      ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
+      ExReleaseResourceLite(&CmiRegistryLock);
+      KeLeaveCriticalRegion();
+      PostOperationInfo.Status = STATUS_NO_MORE_ENTRIES;
+      CmiCallRegisteredCallbacks(RegNtPostEnumerateKey, &PostOperationInfo);
       ObDereferenceObject(KeyObject);
       DPRINT("No more entries\n");
-      return(STATUS_NO_MORE_ENTRIES);
+      return STATUS_NO_MORE_ENTRIES;
     }
 
   Status = STATUS_SUCCESS;
   switch (KeyInformationClass)
     {
-    case KeyBasicInformation:
-      /* Check size of buffer */
-      *ResultLength = sizeof(KEY_BASIC_INFORMATION) + 
-        (SubKeyCell->NameSize ) * sizeof(WCHAR);
-      if (Length < *ResultLength)
-        {
-          Status = STATUS_BUFFER_OVERFLOW;
-        }
-      else
-        {
-          /* Fill buffer with requested info */
-          BasicInformation = (PKEY_BASIC_INFORMATION) KeyInformation;
-          BasicInformation->LastWriteTime.u.LowPart = SubKeyCell->LastWriteTime.dwLowDateTime;
-          BasicInformation->LastWriteTime.u.HighPart = SubKeyCell->LastWriteTime.dwHighDateTime;
-          BasicInformation->TitleIndex = Index;
-          BasicInformation->NameLength = SubKeyCell->NameSize * sizeof(WCHAR);
-          mbstowcs(BasicInformation->Name, 
-            SubKeyCell->Name, 
-            SubKeyCell->NameSize * 2);
-//          BasicInformation->Name[SubKeyCell->NameSize] = 0;
-        }
-      break;
-      
-    case KeyNodeInformation:
-      /* Check size of buffer */
-      *ResultLength = sizeof(KEY_NODE_INFORMATION) +
-        SubKeyCell->NameSize * sizeof(WCHAR) +
-        SubKeyCell->ClassSize;
-      if (Length < *ResultLength)
-        {
-          Status = STATUS_BUFFER_OVERFLOW;
-        }
-      else
-        {
-          /* Fill buffer with requested info */
-          NodeInformation = (PKEY_NODE_INFORMATION) KeyInformation;
-          NodeInformation->LastWriteTime.u.LowPart = SubKeyCell->LastWriteTime.dwLowDateTime;
-          NodeInformation->LastWriteTime.u.HighPart = SubKeyCell->LastWriteTime.dwHighDateTime;
-          NodeInformation->TitleIndex = Index;
-          NodeInformation->ClassOffset = sizeof(KEY_NODE_INFORMATION) + 
-            SubKeyCell->NameSize * sizeof(WCHAR);
-          NodeInformation->ClassLength = SubKeyCell->ClassSize;
-          NodeInformation->NameLength = SubKeyCell->NameSize * sizeof(WCHAR);
-          mbstowcs(NodeInformation->Name, 
-            SubKeyCell->Name,
-            SubKeyCell->NameSize * 2);
-//          NodeInformation->Name[SubKeyCell->NameSize] = 0;
-          if (SubKeyCell->ClassSize != 0)
-            {
-              pClassData=CmiGetBlock(KeyObject->RegistryHive,
-                SubKeyCell->ClassNameOffset,
-                NULL);
-              wcsncpy(NodeInformation->Name + SubKeyCell->NameSize ,
-                      (PWCHAR) pClassData->Data,
-                      SubKeyCell->ClassSize);
-            }
-        }
-      break;
-      
-    case KeyFullInformation:
-      /*  check size of buffer  */
-      *ResultLength = sizeof(KEY_FULL_INFORMATION) +
-          SubKeyCell->ClassSize;
-      if (Length < *ResultLength)
-        {
-          Status = STATUS_BUFFER_OVERFLOW;
-        }
-      else
-        {
-          /* fill buffer with requested info  */
-          FullInformation = (PKEY_FULL_INFORMATION) KeyInformation;
-          FullInformation->LastWriteTime.u.LowPart = SubKeyCell->LastWriteTime.dwLowDateTime;
-          FullInformation->LastWriteTime.u.HighPart = SubKeyCell->LastWriteTime.dwHighDateTime;
-          FullInformation->TitleIndex = Index;
-          FullInformation->ClassOffset = sizeof(KEY_FULL_INFORMATION) - 
-            sizeof(WCHAR);
-          FullInformation->ClassLength = SubKeyCell->ClassSize;
-          FullInformation->SubKeys = SubKeyCell->NumberOfSubKeys;
-          FullInformation->MaxNameLen = 
-            CmiGetMaxNameLength(RegistryHive, SubKeyCell);
-          FullInformation->MaxClassLen = 
-            CmiGetMaxClassLength(RegistryHive, SubKeyCell);
-          FullInformation->Values = SubKeyCell->NumberOfValues;
-          FullInformation->MaxValueNameLen = 
-            CmiGetMaxValueNameLength(RegistryHive, SubKeyCell);
-          FullInformation->MaxValueDataLen = 
-            CmiGetMaxValueDataLength(RegistryHive, SubKeyCell);
-          if (SubKeyCell->ClassSize != 0)
-            {
-              pClassData = CmiGetBlock(KeyObject->RegistryHive,
-                SubKeyCell->ClassNameOffset,
-                NULL);
-              wcsncpy(FullInformation->Class,
-                (PWCHAR) pClassData->Data,
-                SubKeyCell->ClassSize);
-            }
-        }
-      break;
+      case KeyBasicInformation:
+       /* Check size of buffer */
+       if (SubKeyObject != NULL)
+         {
+           NameSize = SubKeyObject->Name.Length;
+         }
+       else
+         {
+           NameSize = SubKeyCell->NameSize;
+           if (SubKeyCell->Flags & REG_KEY_NAME_PACKED)
+             {
+               NameSize *= sizeof(WCHAR);
+             }
+         }
+
+       *ResultLength = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name[0]) + NameSize;
+
+       /*
+        * NOTE: It's perfetly valid to call NtEnumerateKey to get
+         * all the information but name. Actually the NT4 sound
+         * framework does that while querying parameters from registry.
+         * -- Filip Navara, 19/07/2004
+         */
+       if (Length < FIELD_OFFSET(KEY_BASIC_INFORMATION, Name[0]))
+         {
+           Status = STATUS_BUFFER_TOO_SMALL;
+         }
+       else
+         {
+           /* Fill buffer with requested info */
+           BasicInformation = (PKEY_BASIC_INFORMATION) KeyInformation;
+           BasicInformation->LastWriteTime.u.LowPart = SubKeyCell->LastWriteTime.u.LowPart;
+           BasicInformation->LastWriteTime.u.HighPart = SubKeyCell->LastWriteTime.u.HighPart;
+           BasicInformation->TitleIndex = Index;
+           BasicInformation->NameLength = NameSize;
+
+           if (Length - FIELD_OFFSET(KEY_BASIC_INFORMATION, Name[0]) < NameSize)
+             {
+               NameSize = Length - FIELD_OFFSET(KEY_BASIC_INFORMATION, Name[0]);
+               Status = STATUS_BUFFER_OVERFLOW;
+               CHECKPOINT;
+             }
+
+           if (SubKeyObject != NULL)
+             {
+               RtlCopyMemory(BasicInformation->Name,
+                             SubKeyObject->Name.Buffer,
+                             NameSize);
+             }
+           else
+             {
+               if (SubKeyCell->Flags & REG_KEY_NAME_PACKED)
+                 {
+                   CmiCopyPackedName(BasicInformation->Name,
+                                     SubKeyCell->Name,
+                                     NameSize / sizeof(WCHAR));
+                 }
+               else
+                 {
+                   RtlCopyMemory(BasicInformation->Name,
+                                 SubKeyCell->Name,
+                                 NameSize);
+                 }
+             }
+         }
+       break;
+
+      case KeyNodeInformation:
+       /* Check size of buffer */
+       if (SubKeyObject != NULL)
+         {
+           NameSize = SubKeyObject->Name.Length;
+         }
+       else
+         {
+           NameSize = SubKeyCell->NameSize;
+           if (SubKeyCell->Flags & REG_KEY_NAME_PACKED)
+             {
+               NameSize *= sizeof(WCHAR);
+             }
+         }
+       ClassSize = SubKeyCell->ClassSize;
+
+       *ResultLength = FIELD_OFFSET(KEY_NODE_INFORMATION, Name[0]) +
+         NameSize + ClassSize;
+
+       if (Length < FIELD_OFFSET(KEY_NODE_INFORMATION, Name[0]))
+         {
+           Status = STATUS_BUFFER_TOO_SMALL;
+         }
+       else
+         {
+           /* Fill buffer with requested info */
+           NodeInformation = (PKEY_NODE_INFORMATION) KeyInformation;
+           NodeInformation->LastWriteTime.u.LowPart = SubKeyCell->LastWriteTime.u.LowPart;
+           NodeInformation->LastWriteTime.u.HighPart = SubKeyCell->LastWriteTime.u.HighPart;
+           NodeInformation->TitleIndex = Index;
+           NodeInformation->ClassOffset = sizeof(KEY_NODE_INFORMATION) + NameSize;
+           NodeInformation->ClassLength = SubKeyCell->ClassSize;
+           NodeInformation->NameLength = NameSize;
+
+           if (Length - FIELD_OFFSET(KEY_NODE_INFORMATION, Name[0]) < NameSize)
+             {
+               NameSize = Length - FIELD_OFFSET(KEY_NODE_INFORMATION, Name[0]);
+               ClassSize = 0;
+               Status = STATUS_BUFFER_OVERFLOW;
+               CHECKPOINT;
+             }
+           else if (Length - FIELD_OFFSET(KEY_NODE_INFORMATION, Name[0]) -
+                    NameSize < ClassSize)
+             {
+               ClassSize = Length - FIELD_OFFSET(KEY_NODE_INFORMATION, Name[0]) -
+                           NameSize;
+               Status = STATUS_BUFFER_OVERFLOW;
+               CHECKPOINT;
+             }
+
+           if (SubKeyObject != NULL)
+             {
+               RtlCopyMemory(NodeInformation->Name,
+                             SubKeyObject->Name.Buffer,
+                             NameSize);
+             }
+           else
+             {
+               if (SubKeyCell->Flags & REG_KEY_NAME_PACKED)
+                 {
+                   CmiCopyPackedName(NodeInformation->Name,
+                                     SubKeyCell->Name,
+                                     NameSize / sizeof(WCHAR));
+                 }
+               else
+                 {
+                   RtlCopyMemory(NodeInformation->Name,
+                                 SubKeyCell->Name,
+                                 NameSize);
+                 }
+             }
+
+           if (ClassSize != 0)
+             {
+               ClassCell = CmiGetCell (KeyObject->RegistryHive,
+                                       SubKeyCell->ClassNameOffset,
+                                       NULL);
+               RtlCopyMemory (NodeInformation->Name + SubKeyCell->NameSize,
+                              ClassCell->Data,
+                              ClassSize);
+             }
+         }
+       break;
+
+      case KeyFullInformation:
+       ClassSize = SubKeyCell->ClassSize;
+
+       *ResultLength = FIELD_OFFSET(KEY_FULL_INFORMATION, Class[0]) +
+         ClassSize;
+
+       /* Check size of buffer */
+       if (Length < FIELD_OFFSET(KEY_FULL_INFORMATION, Class[0]))
+         {
+           Status = STATUS_BUFFER_TOO_SMALL;
+         }
+       else
+         {
+           /* Fill buffer with requested info */
+           FullInformation = (PKEY_FULL_INFORMATION) KeyInformation;
+           FullInformation->LastWriteTime.u.LowPart = SubKeyCell->LastWriteTime.u.LowPart;
+           FullInformation->LastWriteTime.u.HighPart = SubKeyCell->LastWriteTime.u.HighPart;
+           FullInformation->TitleIndex = Index;
+           FullInformation->ClassOffset = sizeof(KEY_FULL_INFORMATION) -
+             sizeof(WCHAR);
+           FullInformation->ClassLength = SubKeyCell->ClassSize;
+           FullInformation->SubKeys = CmiGetNumberOfSubKeys(KeyObject); //SubKeyCell->NumberOfSubKeys;
+           FullInformation->MaxNameLen = CmiGetMaxNameLength(KeyObject);
+           FullInformation->MaxClassLen = CmiGetMaxClassLength(KeyObject);
+           FullInformation->Values = SubKeyCell->NumberOfValues;
+           FullInformation->MaxValueNameLen =
+             CmiGetMaxValueNameLength(RegistryHive, SubKeyCell);
+           FullInformation->MaxValueDataLen =
+             CmiGetMaxValueDataLength(RegistryHive, SubKeyCell);
+
+           if (Length - FIELD_OFFSET(KEY_FULL_INFORMATION, Class[0]) < ClassSize)
+             {
+               ClassSize = Length - FIELD_OFFSET(KEY_FULL_INFORMATION, Class[0]);
+               Status = STATUS_BUFFER_OVERFLOW;
+               CHECKPOINT;
+             }
+
+           if (ClassSize != 0)
+             {
+               ClassCell = CmiGetCell (KeyObject->RegistryHive,
+                                       SubKeyCell->ClassNameOffset,
+                                       NULL);
+               RtlCopyMemory (FullInformation->Class,
+                              ClassCell->Data,
+                              ClassSize);
+             }
+         }
+       break;
+
+      default:
+       DPRINT1("Not handling 0x%x\n", KeyInformationClass);
+       break;
     }
 
-  ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
+  ExReleaseResourceLite(&CmiRegistryLock);
+  KeLeaveCriticalRegion();
+
+  PostOperationInfo.Status = Status;
+  CmiCallRegisteredCallbacks(RegNtPostEnumerateKey, &PostOperationInfo);
+
   ObDereferenceObject(KeyObject);
 
   DPRINT("Returning status %x\n", Status);
@@ -487,11 +963,14 @@ NtEnumerateValueKey(IN HANDLE KeyHandle,
   PKEY_CELL  KeyCell;
   PVALUE_CELL  ValueCell;
   PDATA_CELL  DataCell;
+  ULONG NameSize, DataSize;
   PKEY_VALUE_BASIC_INFORMATION  ValueBasicInformation;
   PKEY_VALUE_PARTIAL_INFORMATION  ValuePartialInformation;
   PKEY_VALUE_FULL_INFORMATION  ValueFullInformation;
 
-  DPRINT("KH %x  I %d  KVIC %x  KVI %x  L %d  RL %x\n",
+  PAGED_CODE();
+
+  DPRINT("KH 0x%p  I %d  KVIC %x  KVI 0x%p  L %d  RL 0x%p\n",
         KeyHandle,
         Index,
         KeyValueInformationClass,
@@ -513,7 +992,8 @@ NtEnumerateValueKey(IN HANDLE KeyHandle,
     }
 
   /* Acquire hive lock */
-  ExAcquireResourceSharedLite(&KeyObject->RegistryHive->HiveResource, TRUE);
+  KeEnterCriticalRegion();
+  ExAcquireResourceSharedLite(&CmiRegistryLock, TRUE);
 
   VERIFY_KEY_OBJECT(KeyObject);
 
@@ -529,7 +1009,8 @@ NtEnumerateValueKey(IN HANDLE KeyHandle,
 
   if (!NT_SUCCESS(Status))
     {
-      ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
+      ExReleaseResourceLite(&CmiRegistryLock);
+      KeLeaveCriticalRegion();
       ObDereferenceObject(KeyObject);
       return Status;
     }
@@ -539,53 +1020,58 @@ NtEnumerateValueKey(IN HANDLE KeyHandle,
       switch (KeyValueInformationClass)
         {
         case KeyValueBasicInformation:
-          if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
-            {
-              *ResultLength = sizeof(KEY_VALUE_BASIC_INFORMATION) + 
-                (ValueCell->NameSize + 1) * sizeof(WCHAR);
-            }
-          else
-            {
-              *ResultLength = sizeof(KEY_VALUE_BASIC_INFORMATION) + 
-                ValueCell->NameSize + sizeof(WCHAR);
-            }
-          if (Length < *ResultLength)
+         NameSize = ValueCell->NameSize;
+         if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
+           {
+             NameSize *= sizeof(WCHAR);
+           }
+
+          *ResultLength = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[0]) + NameSize;
+
+          if (Length < FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[0]))
             {
-              Status = STATUS_BUFFER_OVERFLOW;
+              Status = STATUS_BUFFER_TOO_SMALL;
             }
           else
             {
-              ValueBasicInformation = (PKEY_VALUE_BASIC_INFORMATION) 
+              ValueBasicInformation = (PKEY_VALUE_BASIC_INFORMATION)
                 KeyValueInformation;
               ValueBasicInformation->TitleIndex = 0;
               ValueBasicInformation->Type = ValueCell->DataType;
+             ValueBasicInformation->NameLength = NameSize;
+
+             if (Length - FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[0]) <
+                 NameSize)
+               {
+                 NameSize = Length - FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[0]);
+                 Status = STATUS_BUFFER_OVERFLOW;
+                 CHECKPOINT;
+               }
+
               if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
                 {
-                  ValueBasicInformation->NameLength =
-                    (ValueCell->NameSize + 1) * sizeof(WCHAR);
                   CmiCopyPackedName(ValueBasicInformation->Name,
                                     ValueCell->Name,
-                                    ValueCell->NameSize);
-                  ValueBasicInformation->Name[ValueCell->NameSize] = 0;
+                                    NameSize / sizeof(WCHAR));
                 }
               else
                 {
-                  ValueBasicInformation->NameLength =
-                    ValueCell->NameSize + sizeof(WCHAR);
                   RtlCopyMemory(ValueBasicInformation->Name,
                                 ValueCell->Name,
-                                ValueCell->NameSize * sizeof(WCHAR));
-                  ValueBasicInformation->Name[ValueCell->NameSize / sizeof(WCHAR)] = 0;
+                                NameSize);
                 }
             }
           break;
 
         case KeyValuePartialInformation:
-          *ResultLength = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 
-            (ValueCell->DataSize & LONG_MAX);
-          if (Length < *ResultLength)
+          DataSize = ValueCell->DataSize & REG_DATA_SIZE_MASK;
+
+          *ResultLength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[0]) +
+            DataSize;
+
+          if (Length < FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[0]))
             {
-              Status = STATUS_BUFFER_OVERFLOW;
+              Status = STATUS_BUFFER_TOO_SMALL;
             }
           else
             {
@@ -593,89 +1079,108 @@ NtEnumerateValueKey(IN HANDLE KeyHandle,
                 KeyValueInformation;
               ValuePartialInformation->TitleIndex = 0;
               ValuePartialInformation->Type = ValueCell->DataType;
-              ValuePartialInformation->DataLength = ValueCell->DataSize & LONG_MAX;
-              if(ValueCell->DataSize >0)
+              ValuePartialInformation->DataLength = ValueCell->DataSize & REG_DATA_SIZE_MASK;
+
+              if (Length - FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[0]) <
+                  DataSize)
+                {
+                  DataSize = Length - FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[0]);
+                  Status = STATUS_BUFFER_OVERFLOW;
+                  CHECKPOINT;
+                }
+
+              if (!(ValueCell->DataSize & REG_DATA_IN_OFFSET))
               {
-                DataCell = CmiGetBlock(RegistryHive, ValueCell->DataOffset, NULL);
-                RtlCopyMemory(ValuePartialInformation->Data, 
+                DataCell = CmiGetCell (RegistryHive, ValueCell->DataOffset, NULL);
+                RtlCopyMemory(ValuePartialInformation->Data,
                   DataCell->Data,
-                  ValueCell->DataSize & LONG_MAX);
+                  DataSize);
               }
               else
               {
-                RtlCopyMemory(ValuePartialInformation->Data, 
-                  &ValueCell->DataOffset, 
-                  ValueCell->DataSize & LONG_MAX);
+                RtlCopyMemory(ValuePartialInformation->Data,
+                  &ValueCell->DataOffset,
+                  DataSize);
               }
-              DataCell = CmiGetBlock(RegistryHive, ValueCell->DataOffset, NULL);
             }
           break;
 
         case KeyValueFullInformation:
+         NameSize = ValueCell->NameSize;
           if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
             {
-              *ResultLength = sizeof(KEY_VALUE_FULL_INFORMATION) + 
-                (ValueCell->NameSize + 1) * sizeof(WCHAR) +
-                (ValueCell->DataSize & LONG_MAX);
-            }
-          else
-            {
-              *ResultLength = sizeof(KEY_VALUE_FULL_INFORMATION) + 
-                ValueCell->NameSize + sizeof(WCHAR) +
-                (ValueCell->DataSize & LONG_MAX);
-            }
-          if (Length < *ResultLength)
+             NameSize *= sizeof(WCHAR);
+           }
+         DataSize = ValueCell->DataSize & REG_DATA_SIZE_MASK;
+
+          *ResultLength = ROUND_UP(FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION,
+                          Name[0]) + NameSize, sizeof(PVOID)) + DataSize;
+
+          if (Length < FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name[0]))
             {
-              Status = STATUS_BUFFER_OVERFLOW;
+              Status = STATUS_BUFFER_TOO_SMALL;
             }
           else
             {
-              ValueFullInformation = (PKEY_VALUE_FULL_INFORMATION) 
+              ValueFullInformation = (PKEY_VALUE_FULL_INFORMATION)
                 KeyValueInformation;
               ValueFullInformation->TitleIndex = 0;
               ValueFullInformation->Type = ValueCell->DataType;
+              ValueFullInformation->NameLength = NameSize;
+              ValueFullInformation->DataOffset =
+                (ULONG_PTR)ValueFullInformation->Name -
+                (ULONG_PTR)ValueFullInformation +
+                ValueFullInformation->NameLength;
+              ValueFullInformation->DataOffset =
+                  ROUND_UP(ValueFullInformation->DataOffset, sizeof(PVOID));
+              ValueFullInformation->DataLength = ValueCell->DataSize & REG_DATA_SIZE_MASK;
+
+              if (Length < ValueFullInformation->DataOffset)
+               {
+                 NameSize = Length - FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name[0]);
+                 DataSize = 0;
+                 Status = STATUS_BUFFER_OVERFLOW;
+                 CHECKPOINT;
+               }
+              else if (Length - ValueFullInformation->DataOffset < DataSize) 
+               {
+                 DataSize = Length - ValueFullInformation->DataOffset;
+                 Status = STATUS_BUFFER_OVERFLOW;
+                 CHECKPOINT;
+               }
+
               if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
                 {
-                  ValueFullInformation->NameLength =
-                    (ValueCell->NameSize + 1) * sizeof(WCHAR);
-
                   CmiCopyPackedName(ValueFullInformation->Name,
                                    ValueCell->Name,
-                                   ValueCell->NameSize);
-                  ValueFullInformation->Name[ValueCell->NameSize] = 0;
+                                   NameSize / sizeof(WCHAR));
                 }
               else
                 {
-                  ValueFullInformation->NameLength =
-                    ValueCell->NameSize + sizeof(WCHAR);
                   RtlCopyMemory(ValueFullInformation->Name,
                                ValueCell->Name,
-                               ValueCell->NameSize);
-                  ValueFullInformation->Name[ValueCell->NameSize / sizeof(WCHAR)] = 0;
+                               NameSize);
                 }
-              ValueFullInformation->DataOffset = 
-                (ULONG)ValueFullInformation->Name - (ULONG)ValueFullInformation +
-                ValueFullInformation->NameLength;
-              ValueFullInformation->DataOffset =
-                (ValueFullInformation->DataOffset + 3) & 0xfffffffc;
-              ValueFullInformation->DataLength = ValueCell->DataSize & LONG_MAX;
-              if (ValueCell->DataSize > 0)
+
+              if (!(ValueCell->DataSize & REG_DATA_IN_OFFSET))
                 {
-                  DataCell = CmiGetBlock(RegistryHive, ValueCell->DataOffset, NULL);
+                  DataCell = CmiGetCell (RegistryHive, ValueCell->DataOffset, NULL);
                   RtlCopyMemory((PCHAR) ValueFullInformation
                     + ValueFullInformation->DataOffset,
-                    DataCell->Data,
-                    ValueCell->DataSize & LONG_MAX);
+                    DataCell->Data, DataSize);
                 }
               else
                 {
                   RtlCopyMemory((PCHAR) ValueFullInformation
                     + ValueFullInformation->DataOffset,
-                    &ValueCell->DataOffset,
-                    ValueCell->DataSize & LONG_MAX);
+                    &ValueCell->DataOffset, DataSize);
                 }
             }
           break;
+
+          default:
+            DPRINT1("Not handling 0x%x\n", KeyValueInformationClass);
+               break;
         }
     }
   else
@@ -683,7 +1188,8 @@ NtEnumerateValueKey(IN HANDLE KeyHandle,
       Status = STATUS_UNSUCCESSFUL;
     }
 
-  ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
+  ExReleaseResourceLite(&CmiRegistryLock);
+  KeLeaveCriticalRegion();
   ObDereferenceObject(KeyObject);
 
   return Status;
@@ -696,14 +1202,19 @@ NtFlushKey(IN HANDLE KeyHandle)
   NTSTATUS Status;
   PKEY_OBJECT  KeyObject;
   PREGISTRY_HIVE  RegistryHive;
+  KPROCESSOR_MODE  PreviousMode;
+
+  PAGED_CODE();
 
   DPRINT("NtFlushKey (KeyHandle %lx) called\n", KeyHandle);
 
+  PreviousMode = ExGetPreviousMode();
+
   /* Verify that the handle is valid and is a registry key */
   Status = ObReferenceObjectByHandle(KeyHandle,
-                                    KEY_QUERY_VALUE,
+                                    0,
                                     CmiKeyType,
-                                    UserMode,
+                                    PreviousMode,
                                     (PVOID *)&KeyObject,
                                     NULL);
   if (!NT_SUCCESS(Status))
@@ -716,10 +1227,10 @@ NtFlushKey(IN HANDLE KeyHandle)
   RegistryHive = KeyObject->RegistryHive;
 
   /* Acquire hive lock */
-  ExAcquireResourceExclusiveLite(&RegistryHive->HiveResource,
-                                TRUE);
+  KeEnterCriticalRegion();
+  ExAcquireResourceExclusiveLite(&CmiRegistryLock, TRUE);
 
-  if (IsVolatileHive(RegistryHive))
+  if (IsNoFileHive(RegistryHive))
     {
       Status = STATUS_SUCCESS;
     }
@@ -729,7 +1240,8 @@ NtFlushKey(IN HANDLE KeyHandle)
       Status = CmiFlushRegistryHive(RegistryHive);
     }
 
-  ExReleaseResourceLite(&RegistryHive->HiveResource);
+  ExReleaseResourceLite(&CmiRegistryLock);
+  KeLeaveCriticalRegion();
 
   ObDereferenceObject(KeyObject);
 
@@ -743,23 +1255,89 @@ NtOpenKey(OUT PHANDLE KeyHandle,
          IN POBJECT_ATTRIBUTES ObjectAttributes)
 {
   UNICODE_STRING RemainingPath;
-  NTSTATUS Status;
-  PVOID Object;
-
-  DPRINT("NtOpenFile(KH %x  DA %x  OA %x  OA->ON '%wZ'\n",
+  KPROCESSOR_MODE PreviousMode;
+  PVOID Object = NULL;
+  HANDLE hKey;
+  NTSTATUS Status = STATUS_SUCCESS;
+  UNICODE_STRING ObjectName;
+  OBJECT_CREATE_INFORMATION ObjectCreateInfo;
+  REG_PRE_OPEN_KEY_INFORMATION PreOpenKeyInfo;
+  REG_POST_OPEN_KEY_INFORMATION PostOpenKeyInfo;
+
+  PAGED_CODE();
+
+  DPRINT("NtOpenKey(KH 0x%p  DA %x  OA 0x%p  OA->ON '%wZ'\n",
         KeyHandle,
         DesiredAccess,
         ObjectAttributes,
         ObjectAttributes ? ObjectAttributes->ObjectName : NULL);
 
+  /* Check place for result handle, if it's null - return immediately */
+  if (KeyHandle == NULL)
+         return(STATUS_INVALID_PARAMETER);
+
+  PreviousMode = ExGetPreviousMode();
+
+  if(PreviousMode != KernelMode)
+  {
+    _SEH_TRY
+    {
+      ProbeForWriteHandle(KeyHandle);
+    }
+    _SEH_HANDLE
+    {
+      Status = _SEH_GetExceptionCode();
+    }
+    _SEH_END;
+
+    if(!NT_SUCCESS(Status))
+    {
+      return Status;
+    }
+  }
+
+  /* WINE checks for the length also */
+  /*if (ObjectAttributes->ObjectName->Length > MAX_NAME_LENGTH)
+         return(STATUS_BUFFER_OVERFLOW);*/
+
+      /* Capture all the info */
+   DPRINT("Capturing Create Info\n");
+   Status = ObpCaptureObjectAttributes(ObjectAttributes,
+                                       PreviousMode,
+                                       CmiKeyType,
+                                       &ObjectCreateInfo,
+                                       &ObjectName);
+   if (!NT_SUCCESS(Status))
+     {
+       DPRINT("ObpCaptureObjectAttributes() failed (Status %lx)\n", Status);
+       return Status;
+     }
+
+  PostOpenKeyInfo.CompleteName = &ObjectName;
+  PreOpenKeyInfo.CompleteName = &ObjectName;
+  Status = CmiCallRegisteredCallbacks(RegNtPreOpenKey, &PreOpenKeyInfo);
+  if (!NT_SUCCESS(Status))
+    {
+      ObpReleaseCapturedAttributes(&ObjectCreateInfo);
+      if (ObjectName.Buffer) ExFreePool(ObjectName.Buffer);
+      return Status;
+    }
+
+
   RemainingPath.Buffer = NULL;
-  Status = ObFindObject(ObjectAttributes,
-                       &Object,
-                       &RemainingPath,
-                       CmiKeyType);
+
+  Status = ObFindObject(&ObjectCreateInfo,
+                        &ObjectName,
+                       (PVOID*)&Object,
+                        &RemainingPath,
+                        CmiKeyType);
+  ObpReleaseCapturedAttributes(&ObjectCreateInfo);
   if (!NT_SUCCESS(Status))
     {
-      return(Status);
+      DPRINT("CmpFindObject() returned 0x%08lx\n", Status);
+      Status = STATUS_INVALID_HANDLE; /* Because CmpFindObject returns STATUS_UNSUCCESSFUL */
+      hKey = *KeyHandle; /* Preserve hkResult value */
+      goto openkey_cleanup;
     }
 
   VERIFY_KEY_OBJECT((PKEY_OBJECT) Object);
@@ -768,30 +1346,54 @@ NtOpenKey(OUT PHANDLE KeyHandle,
 
   if ((RemainingPath.Buffer != NULL) && (RemainingPath.Buffer[0] != 0))
     {
-      ObDereferenceObject(Object);
-      return(STATUS_UNSUCCESSFUL);
+      RtlFreeUnicodeString(&RemainingPath);
+      Status = STATUS_OBJECT_NAME_NOT_FOUND;
+      hKey = NULL;
+      goto openkey_cleanup;
     }
 
+  RtlFreeUnicodeString(&RemainingPath);
+
   /* Fail if the key has been deleted */
   if (((PKEY_OBJECT)Object)->Flags & KO_MARKED_FOR_DELETE)
     {
-      ObDereferenceObject(Object);
-      return(STATUS_UNSUCCESSFUL);
+      Status = STATUS_UNSUCCESSFUL;
+      hKey = NULL;
+      goto openkey_cleanup;
     }
 
-  Status = ObCreateHandle(PsGetCurrentProcess(),
+  Status = ObpCreateHandle(PsGetCurrentProcess(),
                          Object,
                          DesiredAccess,
-                         FALSE,
-                         KeyHandle);
-  ObDereferenceObject(Object);
+                         TRUE,
+                         &hKey);
 
   if (!NT_SUCCESS(Status))
+     hKey = NULL;
+
+openkey_cleanup:
+
+  PostOpenKeyInfo.Object = NT_SUCCESS(Status) ? (PVOID)Object : NULL;
+  PostOpenKeyInfo.Status = Status;
+  CmiCallRegisteredCallbacks (RegNtPostOpenKey, &PostOpenKeyInfo);
+  if (ObjectName.Buffer) ExFreePool(ObjectName.Buffer);
+
+  if (Object)
     {
-      return(Status);
+      ObDereferenceObject(Object);
     }
 
-  return(STATUS_SUCCESS);
+  _SEH_TRY
+  {
+    *KeyHandle = hKey;
+  }
+  _SEH_HANDLE
+  {
+    Status = _SEH_GetExceptionCode();
+  }
+  _SEH_END;
+
+  return Status;
 }
 
 
@@ -806,12 +1408,17 @@ NtQueryKey(IN HANDLE KeyHandle,
   PKEY_NODE_INFORMATION NodeInformation;
   PKEY_FULL_INFORMATION FullInformation;
   PREGISTRY_HIVE RegistryHive;
-  PDATA_CELL pClassData;
+  PDATA_CELL ClassCell;
   PKEY_OBJECT KeyObject;
   PKEY_CELL KeyCell;
+  ULONG NameSize, ClassSize;
   NTSTATUS Status;
+  REG_QUERY_KEY_INFORMATION QueryKeyInfo;
+  REG_POST_OPERATION_INFORMATION PostOperationInfo;
 
-  DPRINT("KH %x  KIC %x  KI %x  L %d  RL %x\n",
+  PAGED_CODE();
+
+  DPRINT("NtQueryKey(KH 0x%p  KIC %x  KI 0x%p  L %d  RL 0x%p)\n",
         KeyHandle,
         KeyInformationClass,
         KeyInformation,
@@ -820,7 +1427,7 @@ NtQueryKey(IN HANDLE KeyHandle,
 
   /* Verify that the handle is valid and is a registry key */
   Status = ObReferenceObjectByHandle(KeyHandle,
-               KEY_READ,
+               (KeyInformationClass != KeyNameInformation ? KEY_QUERY_VALUE : 0),
                CmiKeyType,
                UserMode,
                (PVOID *) &KeyObject,
@@ -830,8 +1437,23 @@ NtQueryKey(IN HANDLE KeyHandle,
       return Status;
     }
 
+  PostOperationInfo.Object = (PVOID)KeyObject;
+  QueryKeyInfo.Object = (PVOID)KeyObject;
+  QueryKeyInfo.KeyInformationClass = KeyInformationClass;
+  QueryKeyInfo.KeyInformation = KeyInformation;
+  QueryKeyInfo.Length = Length;
+  QueryKeyInfo.ResultLength = ResultLength;
+  
+  Status = CmiCallRegisteredCallbacks(RegNtQueryKey, &QueryKeyInfo);
+  if (!NT_SUCCESS(Status))
+    {
+      ObDereferenceObject(KeyObject);
+      return Status;
+    }
+
   /* Acquire hive lock */
-  ExAcquireResourceSharedLite(&KeyObject->RegistryHive->HiveResource, TRUE);
+  KeEnterCriticalRegion();
+  ExAcquireResourceSharedLite(&CmiRegistryLock, TRUE);
 
   VERIFY_KEY_OBJECT(KeyObject);
 
@@ -842,107 +1464,161 @@ NtQueryKey(IN HANDLE KeyHandle,
   Status = STATUS_SUCCESS;
   switch (KeyInformationClass)
     {
-    case KeyBasicInformation:
-      /* Check size of buffer */
-      if (Length < sizeof(KEY_BASIC_INFORMATION) + 
-          KeyObject->NameSize * sizeof(WCHAR))
-        {
-          Status = STATUS_BUFFER_OVERFLOW;
-        }
-      else
-        {
-          /* Fill buffer with requested info */
-          BasicInformation = (PKEY_BASIC_INFORMATION) KeyInformation;
-          BasicInformation->LastWriteTime.u.LowPart = KeyCell->LastWriteTime.dwLowDateTime;
-          BasicInformation->LastWriteTime.u.HighPart = KeyCell->LastWriteTime.dwHighDateTime;
-          BasicInformation->TitleIndex = 0;
-          BasicInformation->NameLength = (KeyObject->NameSize) * sizeof(WCHAR);
-          mbstowcs(BasicInformation->Name,
-            KeyObject->Name, 
-            KeyObject->NameSize*sizeof(WCHAR));
-          *ResultLength = sizeof(KEY_BASIC_INFORMATION) + 
-            KeyObject->NameSize * sizeof(WCHAR);
-        }
-      break;
+      case KeyBasicInformation:
+        NameSize = KeyObject->Name.Length;
+
+       *ResultLength = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name[0]);
+
+       /* Check size of buffer */
+       if (Length < FIELD_OFFSET(KEY_BASIC_INFORMATION, Name[0]))
+         {
+           Status = STATUS_BUFFER_TOO_SMALL;
+         }
+       else
+         {
+           /* Fill buffer with requested info */
+           BasicInformation = (PKEY_BASIC_INFORMATION) KeyInformation;
+           BasicInformation->LastWriteTime.u.LowPart = KeyCell->LastWriteTime.u.LowPart;
+           BasicInformation->LastWriteTime.u.HighPart = KeyCell->LastWriteTime.u.HighPart;
+           BasicInformation->TitleIndex = 0;
+           BasicInformation->NameLength = KeyObject->Name.Length;
+
+           if (Length - FIELD_OFFSET(KEY_BASIC_INFORMATION, Name[0]) <
+               NameSize)
+             {
+               NameSize = Length - FIELD_OFFSET(KEY_BASIC_INFORMATION, Name[0]);
+               Status = STATUS_BUFFER_OVERFLOW;
+               CHECKPOINT;
+             }
+
+           RtlCopyMemory(BasicInformation->Name,
+                         KeyObject->Name.Buffer,
+                         NameSize);
+         }
+       break;
+
+      case KeyNodeInformation:
+        NameSize = KeyObject->Name.Length;
+        ClassSize = KeyCell->ClassSize;
+
+       *ResultLength = FIELD_OFFSET(KEY_NODE_INFORMATION, Name[0]) +
+         NameSize + ClassSize;
+
+       /* Check size of buffer */
+       if (Length < *ResultLength)
+         {
+           Status = STATUS_BUFFER_TOO_SMALL;
+         }
+       else
+         {
+           /* Fill buffer with requested info */
+           NodeInformation = (PKEY_NODE_INFORMATION) KeyInformation;
+           NodeInformation->LastWriteTime.u.LowPart = KeyCell->LastWriteTime.u.LowPart;
+           NodeInformation->LastWriteTime.u.HighPart = KeyCell->LastWriteTime.u.HighPart;
+           NodeInformation->TitleIndex = 0;
+           NodeInformation->ClassOffset = sizeof(KEY_NODE_INFORMATION) +
+             KeyObject->Name.Length;
+           NodeInformation->ClassLength = KeyCell->ClassSize;
+           NodeInformation->NameLength = KeyObject->Name.Length;
+
+           if (Length - FIELD_OFFSET(KEY_NODE_INFORMATION, Name[0]) < NameSize)
+             {
+               NameSize = Length - FIELD_OFFSET(KEY_NODE_INFORMATION, Name[0]);
+               ClassSize = 0;
+               Status = STATUS_BUFFER_OVERFLOW;
+               CHECKPOINT;
+             }
+           else if (Length - FIELD_OFFSET(KEY_NODE_INFORMATION, Name[0]) -
+                    NameSize < ClassSize)
+             {
+               ClassSize = Length - FIELD_OFFSET(KEY_NODE_INFORMATION, Name[0]) -
+                           NameSize;
+               Status = STATUS_BUFFER_OVERFLOW;
+               CHECKPOINT;
+             }
+
+           RtlCopyMemory(NodeInformation->Name,
+                         KeyObject->Name.Buffer,
+                         NameSize);
+
+           if (ClassSize != 0)
+             {
+               ClassCell = CmiGetCell (KeyObject->RegistryHive,
+                                       KeyCell->ClassNameOffset,
+                                       NULL);
+               RtlCopyMemory (NodeInformation->Name + KeyObject->Name.Length,
+                              ClassCell->Data,
+                              ClassSize);
+             }
+         }
+       break;
+
+      case KeyFullInformation:
+        ClassSize = KeyCell->ClassSize;
+
+       *ResultLength = FIELD_OFFSET(KEY_FULL_INFORMATION, Class) +
+         ClassSize;
+
+       /* Check size of buffer */
+       if (Length < FIELD_OFFSET(KEY_FULL_INFORMATION, Class))
+         {
+           Status = STATUS_BUFFER_TOO_SMALL;
+         }
+       else
+         {
+           /* Fill buffer with requested info */
+           FullInformation = (PKEY_FULL_INFORMATION) KeyInformation;
+           FullInformation->LastWriteTime.u.LowPart = KeyCell->LastWriteTime.u.LowPart;
+           FullInformation->LastWriteTime.u.HighPart = KeyCell->LastWriteTime.u.HighPart;
+           FullInformation->TitleIndex = 0;
+           FullInformation->ClassOffset = sizeof(KEY_FULL_INFORMATION) - sizeof(WCHAR);
+           FullInformation->ClassLength = KeyCell->ClassSize;
+           FullInformation->SubKeys = CmiGetNumberOfSubKeys(KeyObject); //KeyCell->NumberOfSubKeys;
+           FullInformation->MaxNameLen = CmiGetMaxNameLength(KeyObject);
+           FullInformation->MaxClassLen = CmiGetMaxClassLength(KeyObject);
+           FullInformation->Values = KeyCell->NumberOfValues;
+           FullInformation->MaxValueNameLen =
+             CmiGetMaxValueNameLength(RegistryHive, KeyCell);
+           FullInformation->MaxValueDataLen =
+             CmiGetMaxValueDataLength(RegistryHive, KeyCell);
+
+           if (Length - FIELD_OFFSET(KEY_FULL_INFORMATION, Class[0]) < ClassSize)
+             {
+               ClassSize = Length - FIELD_OFFSET(KEY_FULL_INFORMATION, Class[0]);
+               Status = STATUS_BUFFER_OVERFLOW;
+               CHECKPOINT;
+             }
+
+           if (ClassSize)
+             {
+               ClassCell = CmiGetCell (KeyObject->RegistryHive,
+                                       KeyCell->ClassNameOffset,
+                                       NULL);
+               RtlCopyMemory (FullInformation->Class,
+                              ClassCell->Data, ClassSize);
+             }
+         }
+       break;
+
+      case KeyNameInformation:
+      case KeyCachedInformation:
+      case KeyFlagsInformation:
+        DPRINT1("Key information class 0x%x not yet implemented!\n", KeyInformationClass);
+        Status = STATUS_NOT_IMPLEMENTED;
+        break;
+
+      default:
+       DPRINT1("Not handling 0x%x\n", KeyInformationClass);
+       Status = STATUS_INVALID_INFO_CLASS;
+       break;
+    }
 
-    case KeyNodeInformation:
-      /* Check size of buffer */
-      if (Length < sizeof(KEY_NODE_INFORMATION)
-         + KeyObject->NameSize * sizeof(WCHAR)
-         + KeyCell->ClassSize)
-        {
-          Status = STATUS_BUFFER_OVERFLOW;
-        }
-      else
-        {
-          /* Fill buffer with requested info */
-          NodeInformation = (PKEY_NODE_INFORMATION) KeyInformation;
-          NodeInformation->LastWriteTime.u.LowPart = KeyCell->LastWriteTime.dwLowDateTime;
-          NodeInformation->LastWriteTime.u.HighPart = KeyCell->LastWriteTime.dwHighDateTime;
-          NodeInformation->TitleIndex = 0;
-          NodeInformation->ClassOffset = sizeof(KEY_NODE_INFORMATION) + 
-            KeyObject->NameSize * sizeof(WCHAR);
-          NodeInformation->ClassLength = KeyCell->ClassSize;
-          NodeInformation->NameLength = KeyObject->NameSize * sizeof(WCHAR);
-          mbstowcs(NodeInformation->Name,
-            KeyObject->Name,
-            KeyObject->NameSize * sizeof(WCHAR));
-
-          if (KeyCell->ClassSize != 0)
-            {
-              pClassData = CmiGetBlock(KeyObject->RegistryHive,
-                KeyCell->ClassNameOffset,
-                NULL);
-              wcsncpy(NodeInformation->Name + KeyObject->NameSize * sizeof(WCHAR),
-                (PWCHAR)pClassData->Data,
-                KeyCell->ClassSize);
-            }
-          *ResultLength = sizeof(KEY_NODE_INFORMATION)
-            + KeyObject->NameSize * sizeof(WCHAR)
-            + KeyCell->ClassSize;
-        }
-      break;
+  ExReleaseResourceLite(&CmiRegistryLock);
+  KeLeaveCriticalRegion();
 
-    case KeyFullInformation:
-      /* Check size of buffer */
-      if (Length < sizeof(KEY_FULL_INFORMATION) + KeyCell->ClassSize)
-        {
-          Status = STATUS_BUFFER_OVERFLOW;
-        }
-      else
-        {
-          /* Fill buffer with requested info */
-          FullInformation = (PKEY_FULL_INFORMATION) KeyInformation;
-          FullInformation->LastWriteTime.u.LowPart = KeyCell->LastWriteTime.dwLowDateTime;
-          FullInformation->LastWriteTime.u.HighPart = KeyCell->LastWriteTime.dwHighDateTime;
-          FullInformation->TitleIndex = 0;
-          FullInformation->ClassOffset = sizeof(KEY_FULL_INFORMATION) - sizeof(WCHAR);
-          FullInformation->ClassLength = KeyCell->ClassSize;
-          FullInformation->SubKeys = KeyCell->NumberOfSubKeys;
-          FullInformation->MaxNameLen =
-            CmiGetMaxNameLength(RegistryHive, KeyCell);
-          FullInformation->MaxClassLen =
-            CmiGetMaxClassLength(RegistryHive, KeyCell);
-          FullInformation->Values = KeyCell->NumberOfValues;
-          FullInformation->MaxValueNameLen =
-            CmiGetMaxValueNameLength(RegistryHive, KeyCell);
-          FullInformation->MaxValueDataLen = 
-            CmiGetMaxValueDataLength(RegistryHive, KeyCell);
-          if (KeyCell->ClassSize != 0)
-            {
-              pClassData=CmiGetBlock(KeyObject->RegistryHive,
-                KeyCell->ClassNameOffset,
-                NULL);
-              wcsncpy(FullInformation->Class,
-                (PWCHAR)pClassData->Data,
-                KeyCell->ClassSize);
-            }
-          *ResultLength = sizeof(KEY_FULL_INFORMATION) + KeyCell->ClassSize;
-        }
-      break;
-    }
+  PostOperationInfo.Status = Status;
+  CmiCallRegisteredCallbacks(RegNtPostQueryKey, &PostOperationInfo);
 
-  ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
   ObDereferenceObject(KeyObject);
 
   return(Status);
@@ -958,6 +1634,7 @@ NtQueryValueKey(IN HANDLE KeyHandle,
        OUT PULONG ResultLength)
 {
   NTSTATUS  Status;
+  ULONG NameSize, DataSize;
   PKEY_OBJECT  KeyObject;
   PREGISTRY_HIVE  RegistryHive;
   PKEY_CELL  KeyCell;
@@ -966,8 +1643,12 @@ NtQueryValueKey(IN HANDLE KeyHandle,
   PKEY_VALUE_BASIC_INFORMATION  ValueBasicInformation;
   PKEY_VALUE_PARTIAL_INFORMATION  ValuePartialInformation;
   PKEY_VALUE_FULL_INFORMATION  ValueFullInformation;
+  REG_QUERY_VALUE_KEY_INFORMATION QueryValueKeyInfo;
+  REG_POST_OPERATION_INFORMATION PostOperationInfo;
+
+  PAGED_CODE();
 
-  DPRINT("NtQueryValueKey(KeyHandle %x  ValueName %S  Length %x)\n",
+  DPRINT("NtQueryValueKey(KeyHandle 0x%p  ValueName %S  Length %x)\n",
     KeyHandle, ValueName->Buffer, Length);
 
   /* Verify that the handle is valid and is a registry key */
@@ -980,12 +1661,27 @@ NtQueryValueKey(IN HANDLE KeyHandle,
 
   if (!NT_SUCCESS(Status))
     {
-      DPRINT("ObReferenceObjectByHandle() failed with status %x\n", Status);
+      DPRINT1("ObReferenceObjectByHandle() failed with status %x %p\n", Status, KeyHandle);
+      return Status;
+    }
+  
+  PostOperationInfo.Object = (PVOID)KeyObject;
+  QueryValueKeyInfo.Object = (PVOID)KeyObject;
+  QueryValueKeyInfo.ValueName = ValueName;
+  QueryValueKeyInfo.KeyValueInformationClass = KeyValueInformationClass;
+  QueryValueKeyInfo.Length = Length;
+  QueryValueKeyInfo.ResultLength = ResultLength;
+
+  Status = CmiCallRegisteredCallbacks(RegNtPreQueryValueKey, &QueryValueKeyInfo);
+  if (!NT_SUCCESS(Status))
+    {
+      ObDereferenceObject(KeyObject);
       return Status;
     }
 
   /* Acquire hive lock */
-  ExAcquireResourceSharedLite(&KeyObject->RegistryHive->HiveResource, TRUE);
+  KeEnterCriticalRegion();
+  ExAcquireResourceSharedLite(&CmiRegistryLock, TRUE);
 
   VERIFY_KEY_OBJECT(KeyObject);
 
@@ -993,7 +1689,7 @@ NtQueryValueKey(IN HANDLE KeyHandle,
   KeyCell = KeyObject->KeyCell;
   RegistryHive = KeyObject->RegistryHive;
 
-  /* Get Value block of interest */
+  /* Get value cell by name */
   Status = CmiScanKeyForValue(RegistryHive,
                              KeyCell,
                              ValueName,
@@ -1002,158 +1698,188 @@ NtQueryValueKey(IN HANDLE KeyHandle,
   if (!NT_SUCCESS(Status))
     {
       DPRINT("CmiScanKeyForValue() failed with status %x\n", Status);
-      ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
-      ObDereferenceObject(KeyObject);
-      return(Status);
+      goto ByeBye;
     }
-  else if (ValueCell != NULL)
-    {
-      switch (KeyValueInformationClass)
-        {
-        case KeyValueBasicInformation:
-          if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
-            {
-              *ResultLength = sizeof(KEY_VALUE_BASIC_INFORMATION) + 
-                (ValueCell->NameSize + 1) * sizeof(WCHAR);
-            }
-          else
-            {
-              *ResultLength = sizeof(KEY_VALUE_BASIC_INFORMATION) + 
-                ValueCell->NameSize + sizeof(WCHAR);
-            }
-          if (Length < *ResultLength)
-            {
-              Status = STATUS_BUFFER_TOO_SMALL;
-            }
-          else
-            {
-              ValueBasicInformation = (PKEY_VALUE_BASIC_INFORMATION) 
-                KeyValueInformation;
-              ValueBasicInformation->TitleIndex = 0;
-              ValueBasicInformation->Type = ValueCell->DataType;
-              if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
-                {
-                  ValueBasicInformation->NameLength =
-                    (ValueCell->NameSize + 1) * sizeof(WCHAR);
-                  CmiCopyPackedName(ValueBasicInformation->Name,
-                                    ValueCell->Name,
-                                    ValueCell->NameSize);
-                  ValueBasicInformation->Name[ValueCell->NameSize] = 0;
-                }
-              else
-                {
-                  ValueBasicInformation->NameLength =
-                    ValueCell->NameSize + sizeof(WCHAR);
-                  RtlCopyMemory(ValueBasicInformation->Name,
-                                ValueCell->Name,
-                                ValueCell->NameSize * sizeof(WCHAR));
-                  ValueBasicInformation->Name[ValueCell->NameSize / sizeof(WCHAR)] = 0;
-                }
-            }
-          break;
 
-        case KeyValuePartialInformation:
-          *ResultLength = sizeof(KEY_VALUE_PARTIAL_INFORMATION)
-            + (ValueCell->DataSize & LONG_MAX);
-          if (Length < *ResultLength)
-            {
-              Status = STATUS_BUFFER_TOO_SMALL;
-            }
-          else
-            {
-              ValuePartialInformation = (PKEY_VALUE_PARTIAL_INFORMATION)
-                KeyValueInformation;
-              ValuePartialInformation->TitleIndex = 0;
-              ValuePartialInformation->Type = ValueCell->DataType;
-              ValuePartialInformation->DataLength = ValueCell->DataSize & LONG_MAX;
-              if (ValueCell->DataSize > 0)
-                {
-                  DataCell = CmiGetBlock(RegistryHive, ValueCell->DataOffset, NULL);
-                  RtlCopyMemory(ValuePartialInformation->Data,
-                    DataCell->Data,
-                    ValueCell->DataSize & LONG_MAX);
-                }
-              else
-                {
-                  RtlCopyMemory(ValuePartialInformation->Data,
-                    &ValueCell->DataOffset,
-                    ValueCell->DataSize & LONG_MAX);
-                }
-            }
-          break;
-
-        case KeyValueFullInformation:
-          if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
-            {
-              *ResultLength = sizeof(KEY_VALUE_FULL_INFORMATION) + 
-                (ValueCell->NameSize + 1) * sizeof(WCHAR) +
-                (ValueCell->DataSize & LONG_MAX);
-            }
-          else
-            {
-              *ResultLength = sizeof(KEY_VALUE_FULL_INFORMATION) + 
-                ValueCell->NameSize + sizeof(WCHAR) +
-                (ValueCell->DataSize & LONG_MAX);
-            }
-          if (Length < *ResultLength)
-            {
-              Status = STATUS_BUFFER_TOO_SMALL;
-            }
-          else
-            {
-              ValueFullInformation = (PKEY_VALUE_FULL_INFORMATION)
-                KeyValueInformation;
-              ValueFullInformation->TitleIndex = 0;
-              ValueFullInformation->Type = ValueCell->DataType;
-              if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
-                {
-                  ValueFullInformation->NameLength =
-                    (ValueCell->NameSize + 1) * sizeof(WCHAR);
-                  CmiCopyPackedName(ValueFullInformation->Name,
-                                    ValueCell->Name,
-                                    ValueCell->NameSize);
-                  ValueFullInformation->Name[ValueCell->NameSize] = 0;
-                }
-              else
-                {
-                  ValueFullInformation->NameLength =
-                    ValueCell->NameSize + sizeof(WCHAR);
-                  RtlCopyMemory(ValueFullInformation->Name,
-                                ValueCell->Name,
-                                ValueCell->NameSize);
-                  ValueFullInformation->Name[ValueCell->NameSize / sizeof(WCHAR)] = 0;
-                }
-              ValueFullInformation->DataOffset = 
-                (ULONG)ValueFullInformation->Name - (ULONG)ValueFullInformation +
-                ValueFullInformation->NameLength;
-              ValueFullInformation->DataOffset =
-                (ValueFullInformation->DataOffset + 3) & 0xfffffffc;
-              ValueFullInformation->DataLength = ValueCell->DataSize & LONG_MAX;
-              if (ValueCell->DataSize > 0)
-                {
-                  DataCell = CmiGetBlock(RegistryHive, ValueCell->DataOffset, NULL);
-                  RtlCopyMemory((PCHAR) ValueFullInformation
-                                + ValueFullInformation->DataOffset,
-                                DataCell->Data,
-                                ValueCell->DataSize & LONG_MAX);
-                }
-              else
-                {
-                  RtlCopyMemory((PCHAR) ValueFullInformation
-                                + ValueFullInformation->DataOffset,
-                                &ValueCell->DataOffset,
-                  ValueCell->DataSize & LONG_MAX);
-                }
-            }
-          break;
-        }
-    }
-  else
+  Status = STATUS_SUCCESS;
+  switch (KeyValueInformationClass)
     {
-      Status = STATUS_OBJECT_NAME_NOT_FOUND;
+      case KeyValueBasicInformation:
+       NameSize = ValueCell->NameSize;
+       if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
+         {
+           NameSize *= sizeof(WCHAR);
+         }
+
+       *ResultLength = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[0]) +
+                       NameSize;
+
+       if (Length < FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[0]))
+         {
+           Status = STATUS_BUFFER_TOO_SMALL;
+         }
+       else
+         {
+           ValueBasicInformation = (PKEY_VALUE_BASIC_INFORMATION)
+             KeyValueInformation;
+           ValueBasicInformation->TitleIndex = 0;
+           ValueBasicInformation->Type = ValueCell->DataType;
+           ValueBasicInformation->NameLength = NameSize;
+
+           if (Length - FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[0]) <
+               NameSize)
+             {
+               NameSize = Length - FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[0]);
+               Status = STATUS_BUFFER_OVERFLOW;
+               CHECKPOINT;
+             }
+
+           if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
+             {
+               CmiCopyPackedName(ValueBasicInformation->Name,
+                                 ValueCell->Name,
+                                 NameSize / sizeof(WCHAR));
+             }
+           else
+             {
+               RtlCopyMemory(ValueBasicInformation->Name,
+                             ValueCell->Name,
+                             NameSize);
+             }
+         }
+       break;
+
+      case KeyValuePartialInformation:
+       DataSize = ValueCell->DataSize & REG_DATA_SIZE_MASK;
+
+       *ResultLength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[0]) +
+                       DataSize;
+
+       if (Length < FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[0]))
+         {
+           Status = STATUS_BUFFER_TOO_SMALL;
+         }
+       else
+         {
+           ValuePartialInformation = (PKEY_VALUE_PARTIAL_INFORMATION)
+             KeyValueInformation;
+           ValuePartialInformation->TitleIndex = 0;
+           ValuePartialInformation->Type = ValueCell->DataType;
+           ValuePartialInformation->DataLength = DataSize;
+
+           if (Length - FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[0]) <
+               DataSize)
+             {
+               DataSize = Length - FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[0]);
+               Status = STATUS_BUFFER_OVERFLOW;
+               CHECKPOINT;
+             }
+
+           if (!(ValueCell->DataSize & REG_DATA_IN_OFFSET))
+             {
+               DataCell = CmiGetCell (RegistryHive, ValueCell->DataOffset, NULL);
+               RtlCopyMemory(ValuePartialInformation->Data,
+                             DataCell->Data,
+                             DataSize);
+             }
+           else
+             {
+               RtlCopyMemory(ValuePartialInformation->Data,
+                             &ValueCell->DataOffset,
+                             DataSize);
+             }
+         }
+       break;
+
+      case KeyValueFullInformation:
+       NameSize = ValueCell->NameSize;
+       if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
+         {
+           NameSize *= sizeof(WCHAR);
+         }
+       DataSize = ValueCell->DataSize & REG_DATA_SIZE_MASK;
+
+       *ResultLength = ROUND_UP(FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION,
+                       Name[0]) + NameSize, sizeof(PVOID)) + DataSize;
+
+       if (Length < FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name[0]))
+         {
+           Status = STATUS_BUFFER_TOO_SMALL;
+         }
+       else
+         {
+           ValueFullInformation = (PKEY_VALUE_FULL_INFORMATION)
+             KeyValueInformation;
+           ValueFullInformation->TitleIndex = 0;
+           ValueFullInformation->Type = ValueCell->DataType;
+           ValueFullInformation->NameLength = NameSize;
+           ValueFullInformation->DataOffset =
+             (ULONG_PTR)ValueFullInformation->Name -
+             (ULONG_PTR)ValueFullInformation +
+             ValueFullInformation->NameLength;
+           ValueFullInformation->DataOffset =
+             ROUND_UP(ValueFullInformation->DataOffset, sizeof(PVOID));
+           ValueFullInformation->DataLength = ValueCell->DataSize & REG_DATA_SIZE_MASK;
+
+           if (Length - FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name[0]) <
+               NameSize)
+             {
+               NameSize = Length - FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name[0]);
+               DataSize = 0;
+               Status = STATUS_BUFFER_OVERFLOW;
+               CHECKPOINT;
+             }
+            else if (ROUND_UP(Length - FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION,
+                     Name[0]) - NameSize, sizeof(PVOID)) < DataSize)
+             {
+               DataSize = ROUND_UP(Length - FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION,
+                                   Name[0]) - NameSize, sizeof(PVOID));
+               Status = STATUS_BUFFER_OVERFLOW;
+               CHECKPOINT;
+             }
+
+           if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
+             {
+               CmiCopyPackedName(ValueFullInformation->Name,
+                                 ValueCell->Name,
+                                 NameSize / sizeof(WCHAR));
+             }
+           else
+             {
+               RtlCopyMemory(ValueFullInformation->Name,
+                             ValueCell->Name,
+                             NameSize);
+             }
+           if (!(ValueCell->DataSize & REG_DATA_IN_OFFSET))
+             {
+               DataCell = CmiGetCell (RegistryHive, ValueCell->DataOffset, NULL);
+               RtlCopyMemory((PCHAR) ValueFullInformation
+                             + ValueFullInformation->DataOffset,
+                             DataCell->Data,
+                             DataSize);
+             }
+           else
+             {
+               RtlCopyMemory((PCHAR) ValueFullInformation
+                             + ValueFullInformation->DataOffset,
+                             &ValueCell->DataOffset,
+                             DataSize);
+             }
+         }
+       break;
+
+      default:
+       DPRINT1("Not handling 0x%x\n", KeyValueInformationClass);
+       Status = STATUS_INVALID_INFO_CLASS;
+       break;
     }
 
-  ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
+ByeBye:;
+  ExReleaseResourceLite(&CmiRegistryLock);
+  KeLeaveCriticalRegion();
+
+  PostOperationInfo.Status = Status;
+  CmiCallRegisteredCallbacks(RegNtPostQueryValueKey, &PostOperationInfo);
   ObDereferenceObject(KeyObject);
 
   return Status;
@@ -1173,31 +1899,48 @@ NtSetValueKey(IN HANDLE KeyHandle,
   PREGISTRY_HIVE  RegistryHive;
   PKEY_CELL  KeyCell;
   PVALUE_CELL  ValueCell;
-  BLOCK_OFFSET VBOffset;
+  BLOCK_OFFSET ValueCellOffset;
   PDATA_CELL DataCell;
   PDATA_CELL NewDataCell;
   PHBIN pBin;
   ULONG DesiredAccess;
+  REG_SET_VALUE_KEY_INFORMATION SetValueKeyInfo;
+  REG_POST_OPERATION_INFORMATION PostOperationInfo;
+
+  PAGED_CODE();
 
-  DPRINT("NtSetValueKey(KeyHandle %x  ValueName '%wZ'  Type %d)\n",
+  DPRINT("NtSetValueKey(KeyHandle 0x%p  ValueName '%wZ'  Type %d)\n",
         KeyHandle, ValueName, Type);
 
   DesiredAccess = KEY_SET_VALUE;
-  if (Type == REG_LINK)
-    DesiredAccess |= KEY_CREATE_LINK;
 
   /* Verify that the handle is valid and is a registry key */
   Status = ObReferenceObjectByHandle(KeyHandle,
                                     DesiredAccess,
                                     CmiKeyType,
-                                    UserMode,
+                                    ExGetPreviousMode(),
                                     (PVOID *)&KeyObject,
                                     NULL);
   if (!NT_SUCCESS(Status))
     return(Status);
 
+  PostOperationInfo.Object = (PVOID)KeyObject;
+  SetValueKeyInfo.Object = (PVOID)KeyObject;
+  SetValueKeyInfo.ValueName = ValueName;
+  SetValueKeyInfo.TitleIndex = TitleIndex;
+  SetValueKeyInfo.Type = Type;
+  SetValueKeyInfo.Data = Data;
+  SetValueKeyInfo.DataSize = DataSize;
+  Status = CmiCallRegisteredCallbacks(RegNtPreSetValueKey, &SetValueKeyInfo);
+  if (!NT_SUCCESS(Status))
+    {
+      ObDereferenceObject(KeyObject);
+      return Status;
+    }
+
   /* Acquire hive lock exclucively */
-  ExAcquireResourceExclusiveLite(&KeyObject->RegistryHive->HiveResource, TRUE);
+  KeEnterCriticalRegion();
+  ExAcquireResourceExclusiveLite(&CmiRegistryLock, TRUE);
 
   VERIFY_KEY_OBJECT(KeyObject);
 
@@ -1208,74 +1951,61 @@ NtSetValueKey(IN HANDLE KeyHandle,
                              KeyCell,
                              ValueName,
                              &ValueCell,
-                             &VBOffset);
-  if (!NT_SUCCESS(Status))
-    {
-      DPRINT("Value not found. Status 0x%X\n", Status);
-
-      ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
-      ObDereferenceObject(KeyObject);
-      return(Status);
-    }
-
-  if (ValueCell == NULL)
+                             &ValueCellOffset);
+  if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
     {
       DPRINT("Allocate new value cell\n");
       Status = CmiAddValueToKey(RegistryHive,
                                KeyCell,
+                               KeyObject->KeyCellOffset,
                                ValueName,
                                &ValueCell,
-                               &VBOffset);
-      if (NT_SUCCESS(Status))
-       {
-         CmiMarkBlockDirty(RegistryHive, VBOffset);
-       }
+                               &ValueCellOffset);
     }
 
   if (!NT_SUCCESS(Status))
     {
       DPRINT("Cannot add value. Status 0x%X\n", Status);
 
-      ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
+      ExReleaseResourceLite(&CmiRegistryLock);
+      KeLeaveCriticalRegion();
+      PostOperationInfo.Status = Status;
+      CmiCallRegisteredCallbacks(RegNtPostSetValueKey, &PostOperationInfo);
       ObDereferenceObject(KeyObject);
-      return(Status);
+      return Status;
     }
 
   DPRINT("DataSize %lu\n", DataSize);
   DPRINT("ValueCell %p\n", ValueCell);
   DPRINT("ValueCell->DataSize %lu\n", ValueCell->DataSize);
 
-  if (DataSize <= 4)
+  if (DataSize <= sizeof(BLOCK_OFFSET))
     {
-      /* If datasize <= 4 then write in valueblock directly */
+      /* If data size <= sizeof(BLOCK_OFFSET) then store data in the data offset */
       DPRINT("ValueCell->DataSize %lu\n", ValueCell->DataSize);
-      if ((ValueCell->DataSize >= 0) &&
-         (DataCell = CmiGetBlock(RegistryHive, ValueCell->DataOffset, NULL)))
+      if (!(ValueCell->DataSize & REG_DATA_IN_OFFSET) &&
+         (ValueCell->DataSize & REG_DATA_SIZE_MASK) != 0)
        {
-         CmiDestroyBlock(RegistryHive, DataCell, ValueCell->DataOffset);
+         DataCell = CmiGetCell (RegistryHive, ValueCell->DataOffset, NULL);
+         CmiDestroyCell(RegistryHive, DataCell, ValueCell->DataOffset);
        }
 
       RtlCopyMemory(&ValueCell->DataOffset, Data, DataSize);
-      ValueCell->DataSize = DataSize | 0x80000000;
+      ValueCell->DataSize = DataSize | REG_DATA_IN_OFFSET;
       ValueCell->DataType = Type;
-      RtlMoveMemory(&ValueCell->DataOffset, Data, DataSize);
-      CmiMarkBlockDirty(RegistryHive, VBOffset);
+      CmiMarkBlockDirty(RegistryHive, ValueCellOffset);
     }
-  else if (DataSize <= (ULONG) (ValueCell->DataSize & 0x7fffffff))
+  else if (!(ValueCell->DataSize & REG_DATA_IN_OFFSET) &&
+          (DataSize <= (ValueCell->DataSize & REG_DATA_SIZE_MASK)))
     {
       /* If new data size is <= current then overwrite current data */
-      DataCell = CmiGetBlock(RegistryHive, ValueCell->DataOffset,&pBin);
+      DataCell = CmiGetCell (RegistryHive, ValueCell->DataOffset,&pBin);
       RtlZeroMemory(DataCell->Data, ValueCell->DataSize);
       RtlCopyMemory(DataCell->Data, Data, DataSize);
       ValueCell->DataSize = DataSize;
       ValueCell->DataType = Type;
-
-      /* Update time of heap */
-      if (!IsVolatileHive(RegistryHive))
-       {
-         NtQuerySystemTime((PTIME) &pBin->DateModified);
-       }
       CmiMarkBlockDirty(RegistryHive, ValueCell->DataOffset);
+      CmiMarkBlockDirty(RegistryHive, ValueCellOffset);
     }
   else
     {
@@ -1287,73 +2017,85 @@ NtSetValueKey(IN HANDLE KeyHandle,
 
       DPRINT("ValueCell->DataSize %lu\n", ValueCell->DataSize);
 
-      if ((ValueCell->DataSize >= 0) &&
-         (DataCell = CmiGetBlock(RegistryHive, ValueCell->DataOffset, NULL)))
+      if (!(ValueCell->DataSize & REG_DATA_IN_OFFSET) &&
+         (ValueCell->DataSize & REG_DATA_SIZE_MASK) != 0)
        {
-         CmiDestroyBlock(RegistryHive, DataCell, ValueCell->DataOffset);
+         DataCell = CmiGetCell (RegistryHive, ValueCell->DataOffset, NULL);
+         CmiDestroyCell(RegistryHive, DataCell, ValueCell->DataOffset);
          ValueCell->DataSize = 0;
          ValueCell->DataType = 0;
-         ValueCell->DataOffset = 0xffffffff;
+         ValueCell->DataOffset = (BLOCK_OFFSET)-1;
        }
 
-      Status = CmiAllocateBlock(RegistryHive,
+      Status = CmiAllocateCell (RegistryHive,
+                               sizeof(CELL_HEADER) + DataSize,
                                (PVOID *)&NewDataCell,
-                               DataSize,
                                &NewOffset);
       if (!NT_SUCCESS(Status))
        {
          DPRINT("CmiAllocateBlock() failed (Status %lx)\n", Status);
 
-         ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
+         ExReleaseResourceLite(&CmiRegistryLock);
+         KeLeaveCriticalRegion();
+          PostOperationInfo.Status = Status;
+          CmiCallRegisteredCallbacks(RegNtPostSetValueKey, &PostOperationInfo);
          ObDereferenceObject(KeyObject);
 
-         return(Status);
+         return Status;
        }
 
       RtlCopyMemory(&NewDataCell->Data[0], Data, DataSize);
-      ValueCell->DataSize = DataSize;
+      ValueCell->DataSize = DataSize & REG_DATA_SIZE_MASK;
       ValueCell->DataType = Type;
       ValueCell->DataOffset = NewOffset;
       CmiMarkBlockDirty(RegistryHive, ValueCell->DataOffset);
+      CmiMarkBlockDirty(RegistryHive, ValueCellOffset);
     }
 
   /* Mark link key */
-  if ((_wcsicmp(ValueName->Buffer, L"SymbolicLinkValue") == 0) &&
-      (Type == REG_LINK))
+  if ((Type == REG_LINK) &&
+      (_wcsicmp(ValueName->Buffer, L"SymbolicLinkValue") == 0))
     {
-      KeyCell->Type = REG_LINK_KEY_CELL_TYPE;
-      CmiMarkBlockDirty(RegistryHive, KeyObject->BlockOffset);
+      KeyCell->Flags |= REG_KEY_LINK_CELL;
     }
 
-  /* Update time of heap */
-  if (!IsVolatileHive(RegistryHive) && CmiGetBlock(RegistryHive, VBOffset, &pBin))
-    {
-      NtQuerySystemTime((PTIME) &pBin->DateModified);
-    }
+  KeQuerySystemTime (&KeyCell->LastWriteTime);
+  CmiMarkBlockDirty (RegistryHive, KeyObject->KeyCellOffset);
 
-  ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
+  ExReleaseResourceLite(&CmiRegistryLock);
+  KeLeaveCriticalRegion();
+  PostOperationInfo.Status = Status;
+  CmiCallRegisteredCallbacks(RegNtPostSetValueKey, &PostOperationInfo);
   ObDereferenceObject(KeyObject);
 
   CmiSyncHives();
 
   DPRINT("Return Status 0x%X\n", Status);
 
-  return(Status);
+  return Status;
 }
 
 
 NTSTATUS STDCALL
-NtDeleteValueKey(IN HANDLE KeyHandle,
-                IN PUNICODE_STRING ValueName)
+NtDeleteValueKey (IN HANDLE KeyHandle,
+                 IN PUNICODE_STRING ValueName)
 {
   PKEY_OBJECT KeyObject;
   NTSTATUS Status;
+  REG_DELETE_VALUE_KEY_INFORMATION DeleteValueKeyInfo;
+  REG_POST_OPERATION_INFORMATION PostOperationInfo;
+  KPROCESSOR_MODE PreviousMode;
+  UNICODE_STRING CapturedValueName;
+
+  PAGED_CODE();
+  
+  PreviousMode = KeGetPreviousMode();
 
   /* Verify that the handle is valid and is a registry key */
   Status = ObReferenceObjectByHandle(KeyHandle,
-               KEY_QUERY_VALUE,
+               KEY_SET_VALUE,
                CmiKeyType,
-               UserMode,
+               PreviousMode,
                (PVOID *)&KeyObject,
                NULL);
   if (!NT_SUCCESS(Status))
@@ -1361,36 +2103,73 @@ NtDeleteValueKey(IN HANDLE KeyHandle,
       return Status;
     }
 
+  Status = ProbeAndCaptureUnicodeString(&CapturedValueName,
+                                        PreviousMode,
+                                        ValueName);
+  if (!NT_SUCCESS(Status))
+    {
+      goto Fail;
+    }
+  DeleteValueKeyInfo.Object = (PVOID)KeyObject;
+  DeleteValueKeyInfo.ValueName = &CapturedValueName;
+
+  /* FIXME - check if value exists before calling the callbacks? */
+  Status = CmiCallRegisteredCallbacks(RegNtPreDeleteValueKey, &DeleteValueKeyInfo);
+  if (!NT_SUCCESS(Status))
+    {
+      ReleaseCapturedUnicodeString(&CapturedValueName,
+                                   PreviousMode);
+Fail:
+      ObDereferenceObject(KeyObject);
+      return Status;
+    }
+
   /* Acquire hive lock */
-  ExAcquireResourceExclusiveLite(&KeyObject->RegistryHive->HiveResource, TRUE);
+  KeEnterCriticalRegion();
+  ExAcquireResourceExclusiveLite(&CmiRegistryLock, TRUE);
 
   VERIFY_KEY_OBJECT(KeyObject);
 
   Status = CmiDeleteValueFromKey(KeyObject->RegistryHive,
                                 KeyObject->KeyCell,
-                                KeyObject->BlockOffset,
+                                KeyObject->KeyCellOffset,
                                 ValueName);
 
+  KeQuerySystemTime (&KeyObject->KeyCell->LastWriteTime);
+  CmiMarkBlockDirty (KeyObject->RegistryHive, KeyObject->KeyCellOffset);
+
   /* Release hive lock */
-  ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
+  ExReleaseResourceLite(&CmiRegistryLock);
+  KeLeaveCriticalRegion();
 
-  ObDereferenceObject(KeyObject);
+  ReleaseCapturedUnicodeString(&CapturedValueName,
+                               PreviousMode);
 
-  CmiSyncHives();
+  PostOperationInfo.Object = (PVOID)KeyObject;
+  PostOperationInfo.Status = Status;
+
+  CmiCallRegisteredCallbacks(RegNtPostDeleteValueKey, &PostOperationInfo);
+
+  ObDereferenceObject (KeyObject);
+
+  CmiSyncHives ();
 
   return Status;
 }
 
+
 /*
  * NOTE:
  * KeyObjectAttributes->RootDirectory specifies the handle to the parent key and
  * KeyObjectAttributes->Name specifies the name of the key to load.
  */
 NTSTATUS STDCALL
-NtLoadKey(IN POBJECT_ATTRIBUTES KeyObjectAttributes,
-         IN POBJECT_ATTRIBUTES FileObjectAttributes)
+NtLoadKey (IN POBJECT_ATTRIBUTES KeyObjectAttributes,
+          IN POBJECT_ATTRIBUTES FileObjectAttributes)
 {
-  return NtLoadKey2(KeyObjectAttributes, FileObjectAttributes, 0);
+  return NtLoadKey2 (KeyObjectAttributes,
+                    FileObjectAttributes,
+                    0);
 }
 
 
@@ -1398,41 +2177,177 @@ NtLoadKey(IN POBJECT_ATTRIBUTES KeyObjectAttributes,
  * NOTE:
  * KeyObjectAttributes->RootDirectory specifies the handle to the parent key and
  * KeyObjectAttributes->Name specifies the name of the key to load.
+ * Flags can be 0 or REG_NO_LAZY_FLUSH.
  */
 NTSTATUS STDCALL
-NtLoadKey2(IN POBJECT_ATTRIBUTES KeyObjectAttributes,
-          IN POBJECT_ATTRIBUTES FileObjectAttributes,
-          IN ULONG Flags)
+NtLoadKey2 (IN POBJECT_ATTRIBUTES KeyObjectAttributes,
+           IN POBJECT_ATTRIBUTES FileObjectAttributes,
+           IN ULONG Flags)
 {
-  UNIMPLEMENTED;
-  return STATUS_NOT_IMPLEMENTED;
+  POBJECT_NAME_INFORMATION NameInfo;
+  PUNICODE_STRING NamePointer;
+  PUCHAR Buffer;
+  ULONG BufferSize;
+  ULONG Length;
+  NTSTATUS Status;
+
+  PAGED_CODE();
+
+  DPRINT ("NtLoadKey2() called\n");
+
+#if 0
+  if (!SeSinglePrivilegeCheck (SeRestorePrivilege, KeGetPreviousMode ()))
+    return STATUS_PRIVILEGE_NOT_HELD;
+#endif
+
+  if (FileObjectAttributes->RootDirectory != NULL)
+    {
+      BufferSize =
+       sizeof(OBJECT_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR);
+      Buffer = ExAllocatePool (NonPagedPool,
+                              BufferSize);
+      if (Buffer == NULL)
+       return STATUS_INSUFFICIENT_RESOURCES;
+
+      Status = ZwQueryObject (FileObjectAttributes->RootDirectory,
+                             ObjectNameInformation,
+                             Buffer,
+                             BufferSize,
+                             &Length);
+      if (!NT_SUCCESS(Status))
+       {
+         DPRINT1 ("NtQueryObject() failed (Status %lx)\n", Status);
+         ExFreePool (Buffer);
+         return Status;
+       }
+
+      NameInfo = (POBJECT_NAME_INFORMATION)Buffer;
+      DPRINT ("ObjectPath: '%wZ'  Length %hu\n",
+             &NameInfo->Name, NameInfo->Name.Length);
+
+      NameInfo->Name.MaximumLength = MAX_PATH * sizeof(WCHAR);
+      if (FileObjectAttributes->ObjectName->Buffer[0] != L'\\')
+       {
+         RtlAppendUnicodeToString (&NameInfo->Name,
+                                   L"\\");
+         DPRINT ("ObjectPath: '%wZ'  Length %hu\n",
+                 &NameInfo->Name, NameInfo->Name.Length);
+       }
+      RtlAppendUnicodeStringToString (&NameInfo->Name,
+                                     FileObjectAttributes->ObjectName);
+
+      DPRINT ("ObjectPath: '%wZ'  Length %hu\n",
+             &NameInfo->Name, NameInfo->Name.Length);
+      NamePointer = &NameInfo->Name;
+    }
+  else
+    {
+      if (FileObjectAttributes->ObjectName->Buffer[0] == L'\\')
+       {
+         Buffer = NULL;
+         NamePointer = FileObjectAttributes->ObjectName;
+       }
+      else
+       {
+         BufferSize =
+           sizeof(OBJECT_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR);
+         Buffer = ExAllocatePool (NonPagedPool,
+                                  BufferSize);
+         if (Buffer == NULL)
+           return STATUS_INSUFFICIENT_RESOURCES;
+
+         NameInfo = (POBJECT_NAME_INFORMATION)Buffer;
+         NameInfo->Name.MaximumLength = MAX_PATH * sizeof(WCHAR);
+         NameInfo->Name.Length = 0;
+         NameInfo->Name.Buffer = (PWSTR)((ULONG_PTR)Buffer + sizeof(OBJECT_NAME_INFORMATION));
+         NameInfo->Name.Buffer[0] = 0;
+
+         RtlAppendUnicodeToString (&NameInfo->Name,
+                                   L"\\");
+         RtlAppendUnicodeStringToString (&NameInfo->Name,
+                                         FileObjectAttributes->ObjectName);
+
+         NamePointer = &NameInfo->Name;
+       }
+    }
+
+  DPRINT ("Full name: '%wZ'\n", NamePointer);
+
+  /* Acquire hive lock */
+  KeEnterCriticalRegion();
+  ExAcquireResourceExclusiveLite(&CmiRegistryLock, TRUE);
+
+  Status = CmiLoadHive (KeyObjectAttributes,
+                       NamePointer,
+                       Flags);
+  if (!NT_SUCCESS (Status))
+    {
+      DPRINT1 ("CmiLoadHive() failed (Status %lx)\n", Status);
+    }
+
+  /* Release hive lock */
+  ExReleaseResourceLite(&CmiRegistryLock);
+  KeLeaveCriticalRegion();
+
+  if (Buffer != NULL)
+    ExFreePool (Buffer);
+
+  return Status;
 }
 
 
 NTSTATUS STDCALL
-NtNotifyChangeKey(
-       IN      HANDLE  KeyHandle,
-       IN      HANDLE  Event,
-       IN      PIO_APC_ROUTINE  ApcRoutine  OPTIONAL,
-       IN      PVOID  ApcContext  OPTIONAL,
-       OUT     PIO_STATUS_BLOCK  IoStatusBlock,
-       IN      ULONG  CompletionFilter,
-       IN      BOOLEAN  Asynchroneous,
-       OUT     PVOID  ChangeBuffer,
-       IN      ULONG  Length,
-       IN      BOOLEAN  WatchSubtree)
+NtNotifyChangeKey (IN HANDLE KeyHandle,
+                  IN HANDLE Event,
+                  IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
+                  IN PVOID ApcContext OPTIONAL,
+                  OUT PIO_STATUS_BLOCK IoStatusBlock,
+                  IN ULONG CompletionFilter,
+                  IN BOOLEAN WatchSubtree,
+                  OUT PVOID Buffer,
+                  IN ULONG Length,
+                  IN BOOLEAN Asynchronous)
 {
        UNIMPLEMENTED;
+       return(STATUS_NOT_IMPLEMENTED);
 }
 
+#if 0
+NTSTATUS STDCALL
+NtNotifyChangeKey (IN HANDLE KeyHandle,
+                  IN HANDLE Event,
+                  IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
+                  IN PVOID ApcContext OPTIONAL,
+                  OUT PIO_STATUS_BLOCK IoStatusBlock,
+                  IN ULONG CompletionFilter,
+                  IN BOOLEAN WatchSubtree,
+                  OUT PVOID Buffer,
+                  IN ULONG Length,
+                  IN BOOLEAN Asynchronous)
+{
+     return NtNotifyChangeMultipleKeys(KeyHandle,
+                                       0,
+                                       NULL,
+                                       Event,
+                                       ApcRoutine,
+                                       ApcContext,
+                                       IoStatusBlock,
+                                       CompletionFilter,
+                                       WatchTree,
+                                       Buffer,
+                                       Length,
+                                       Asynchronous);
+}
+
+#endif
 
 NTSTATUS STDCALL
-NtQueryMultipleValueKey(IN HANDLE KeyHandle,
-                       IN OUT PKEY_VALUE_ENTRY ValueList,
-                       IN ULONG NumberOfValues,
-                       OUT PVOID Buffer,
-                       IN OUT PULONG Length,
-                       OUT PULONG ReturnLength)
+NtQueryMultipleValueKey (IN HANDLE KeyHandle,
+                        IN OUT PKEY_VALUE_ENTRY ValueList,
+                        IN ULONG NumberOfValues,
+                        OUT PVOID Buffer,
+                        IN OUT PULONG Length,
+                        OUT PULONG ReturnLength)
 {
   PREGISTRY_HIVE RegistryHive;
   PVALUE_CELL ValueCell;
@@ -1443,6 +2358,10 @@ NtQueryMultipleValueKey(IN HANDLE KeyHandle,
   NTSTATUS Status;
   PUCHAR DataPtr;
   ULONG i;
+  REG_QUERY_MULTIPLE_VALUE_KEY_INFORMATION QueryMultipleValueKeyInfo;
+  REG_POST_OPERATION_INFORMATION PostOperationInfo;
+
+  PAGED_CODE();
 
   /* Verify that the handle is valid and is a registry key */
   Status = ObReferenceObjectByHandle(KeyHandle,
@@ -1457,8 +2376,24 @@ NtQueryMultipleValueKey(IN HANDLE KeyHandle,
       return(Status);
     }
 
+  PostOperationInfo.Object = (PVOID)KeyObject;
+  QueryMultipleValueKeyInfo.Object = (PVOID)KeyObject;
+  QueryMultipleValueKeyInfo.ValueEntries = ValueList;
+  QueryMultipleValueKeyInfo.EntryCount = NumberOfValues;
+  QueryMultipleValueKeyInfo.ValueBuffer = Buffer;
+  QueryMultipleValueKeyInfo.BufferLength = Length;
+  QueryMultipleValueKeyInfo.RequiredBufferLength = ReturnLength;
+
+  Status = CmiCallRegisteredCallbacks(RegNtPreQueryMultipleValueKey, &QueryMultipleValueKeyInfo);
+  if (!NT_SUCCESS(Status))
+    {
+      ObDereferenceObject(KeyObject);
+      return Status;
+    }
+
   /* Acquire hive lock */
-  ExAcquireResourceSharedLite(&KeyObject->RegistryHive->HiveResource, TRUE);
+  KeEnterCriticalRegion();
+  ExAcquireResourceSharedLite(&CmiRegistryLock, TRUE);
 
   VERIFY_KEY_OBJECT(KeyObject);
 
@@ -1490,40 +2425,40 @@ NtQueryMultipleValueKey(IN HANDLE KeyHandle,
          break;
        }
 
-      BufferLength = (BufferLength + 3) & 0xfffffffc;
+      BufferLength = ROUND_UP(BufferLength, sizeof(PVOID));
 
-      if (BufferLength + (ValueCell->DataSize & LONG_MAX) <= *Length)
+      if (BufferLength + (ValueCell->DataSize & REG_DATA_SIZE_MASK) <= *Length)
        {
-         DataPtr = (PUCHAR)(((ULONG)DataPtr + 3) & 0xfffffffc);
+         DataPtr = (PUCHAR)ROUND_UP((ULONG_PTR)DataPtr, sizeof(PVOID));
 
          ValueList[i].Type = ValueCell->DataType;
-         ValueList[i].DataLength = ValueCell->DataSize & LONG_MAX;
-         ValueList[i].DataOffset = (ULONG) DataPtr - (ULONG) Buffer;
+         ValueList[i].DataLength = ValueCell->DataSize & REG_DATA_SIZE_MASK;
+         ValueList[i].DataOffset = (ULONG_PTR)DataPtr - (ULONG_PTR)Buffer;
 
-         if (ValueCell->DataSize > 0)
+         if (!(ValueCell->DataSize & REG_DATA_IN_OFFSET))
            {
-             DataCell = CmiGetBlock(RegistryHive,
+             DataCell = CmiGetCell (RegistryHive,
                                     ValueCell->DataOffset,
                                     NULL);
              RtlCopyMemory(DataPtr,
                            DataCell->Data,
-                           ValueCell->DataSize & LONG_MAX);
+                           ValueCell->DataSize & REG_DATA_SIZE_MASK);
            }
          else
            {
              RtlCopyMemory(DataPtr,
                            &ValueCell->DataOffset,
-                           ValueCell->DataSize & LONG_MAX);
+                           ValueCell->DataSize & REG_DATA_SIZE_MASK);
            }
 
-         DataPtr += ValueCell->DataSize & LONG_MAX;
+         DataPtr += ValueCell->DataSize & REG_DATA_SIZE_MASK;
        }
       else
        {
          Status = STATUS_BUFFER_TOO_SMALL;
        }
 
-      BufferLength +=  ValueCell->DataSize & LONG_MAX;
+      BufferLength +=  ValueCell->DataSize & REG_DATA_SIZE_MASK;
     }
 
   if (NT_SUCCESS(Status))
@@ -1532,55 +2467,223 @@ NtQueryMultipleValueKey(IN HANDLE KeyHandle,
   *ReturnLength = BufferLength;
 
   /* Release hive lock */
-  ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
+  ExReleaseResourceLite(&CmiRegistryLock);
+  KeLeaveCriticalRegion();
+
+  PostOperationInfo.Status = Status;
+  CmiCallRegisteredCallbacks(RegNtPostQueryMultipleValueKey, &PostOperationInfo);
 
   ObDereferenceObject(KeyObject);
 
   DPRINT("Return Status 0x%X\n", Status);
 
-  return(Status);
+  return Status;
 }
 
 
 NTSTATUS STDCALL
-NtReplaceKey(
-       IN      POBJECT_ATTRIBUTES      ObjectAttributes,
-       IN      HANDLE  Key,
-       IN      POBJECT_ATTRIBUTES      ReplacedObjectAttributes
-       )
+NtReplaceKey (IN POBJECT_ATTRIBUTES ObjectAttributes,
+             IN HANDLE Key,
+             IN POBJECT_ATTRIBUTES ReplacedObjectAttributes)
 {
        UNIMPLEMENTED;
+       return(STATUS_NOT_IMPLEMENTED);
 }
 
 
 NTSTATUS STDCALL
-NtRestoreKey(
-       IN      HANDLE  KeyHandle,
-       IN      HANDLE  FileHandle,
-       IN      ULONG   RestoreFlags
-       )
+NtRestoreKey (IN HANDLE KeyHandle,
+             IN HANDLE FileHandle,
+             IN ULONG RestoreFlags)
 {
        UNIMPLEMENTED;
+       return(STATUS_NOT_IMPLEMENTED);
 }
 
 
 NTSTATUS STDCALL
-NtSaveKey(
-       IN      HANDLE  KeyHandle,
-       IN      HANDLE  FileHandle)
+NtSaveKey (IN HANDLE KeyHandle,
+          IN HANDLE FileHandle)
+{
+  PREGISTRY_HIVE TempHive;
+  PKEY_OBJECT KeyObject;
+  NTSTATUS Status;
+
+  PAGED_CODE();
+
+  DPRINT ("NtSaveKey() called\n");
+
+#if 0
+  if (!SeSinglePrivilegeCheck (SeBackupPrivilege, KeGetPreviousMode ()))
+    return STATUS_PRIVILEGE_NOT_HELD;
+#endif
+
+  Status = ObReferenceObjectByHandle (KeyHandle,
+                                     0,
+                                     CmiKeyType,
+                                     KeGetPreviousMode(),
+                                     (PVOID *)&KeyObject,
+                                     NULL);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT1 ("ObReferenceObjectByHandle() failed (Status %lx)\n", Status);
+      return Status;
+    }
+
+  /* Acquire hive lock exclucively */
+  KeEnterCriticalRegion();
+  ExAcquireResourceExclusiveLite(&CmiRegistryLock, TRUE);
+
+  /* Refuse to save a volatile key */
+  if (KeyObject->RegistryHive == CmiVolatileHive)
+    {
+      DPRINT1 ("Cannot save a volatile key\n");
+      ExReleaseResourceLite(&CmiRegistryLock);
+      KeLeaveCriticalRegion();
+      ObDereferenceObject (KeyObject);
+      return STATUS_ACCESS_DENIED;
+    }
+
+  Status = CmiCreateTempHive(&TempHive);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT1 ("CmiCreateTempHive() failed (Status %lx)\n", Status);
+      ExReleaseResourceLite(&CmiRegistryLock);
+      KeLeaveCriticalRegion();
+      ObDereferenceObject (KeyObject);
+      return(Status);
+    }
+
+  Status = CmiCopyKey (TempHive,
+                      NULL,
+                      KeyObject->RegistryHive,
+                      KeyObject->KeyCell);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT1 ("CmiCopyKey() failed (Status %lx)\n", Status);
+      CmiRemoveRegistryHive (TempHive);
+      ExReleaseResourceLite(&CmiRegistryLock);
+      KeLeaveCriticalRegion();
+      ObDereferenceObject (KeyObject);
+      return(Status);
+    }
+
+  Status = CmiSaveTempHive (TempHive,
+                           FileHandle);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT1 ("CmiSaveTempHive() failed (Status %lx)\n", Status);
+    }
+
+  CmiRemoveRegistryHive (TempHive);
+
+  /* Release hive lock */
+  ExReleaseResourceLite(&CmiRegistryLock);
+  KeLeaveCriticalRegion();
+
+  ObDereferenceObject (KeyObject);
+
+  DPRINT ("NtSaveKey() done\n");
+
+  return STATUS_SUCCESS;
+}
+
+/*
+ * @unimplemented
+ */
+NTSTATUS
+STDCALL
+NtSaveKeyEx(
+       IN HANDLE KeyHandle,
+       IN HANDLE FileHandle,
+       IN ULONG Flags // REG_STANDARD_FORMAT, etc..
+       )
 {
        UNIMPLEMENTED;
+       return STATUS_NOT_IMPLEMENTED;
 }
 
 
 NTSTATUS STDCALL
-NtSetInformationKey(
-       IN      HANDLE  KeyHandle,
-       IN      CINT    KeyInformationClass,
-       IN      PVOID    KeyInformation,
-       IN      ULONG    KeyInformationLength)
+NtSetInformationKey (IN HANDLE KeyHandle,
+                    IN KEY_SET_INFORMATION_CLASS KeyInformationClass,
+                    IN PVOID KeyInformation,
+                    IN ULONG KeyInformationLength)
 {
-  UNIMPLEMENTED;
+  PKEY_OBJECT KeyObject;
+  NTSTATUS Status;
+  REG_SET_INFORMATION_KEY_INFORMATION SetInformationKeyInfo;
+  REG_POST_OPERATION_INFORMATION PostOperationInfo;
+
+  PAGED_CODE();
+
+  /* Verify that the handle is valid and is a registry key */
+  Status = ObReferenceObjectByHandle (KeyHandle,
+                                     KEY_SET_VALUE,
+                                     CmiKeyType,
+                                     UserMode,
+                                     (PVOID *)&KeyObject,
+                                     NULL);
+  if (!NT_SUCCESS (Status))
+    {
+      DPRINT ("ObReferenceObjectByHandle() failed with status %x\n", Status);
+      return Status;
+    }
+
+  PostOperationInfo.Object = (PVOID)KeyObject;
+  SetInformationKeyInfo.Object = (PVOID)KeyObject;
+  SetInformationKeyInfo.KeySetInformationClass = KeyInformationClass;
+  SetInformationKeyInfo.KeySetInformation = KeyInformation;
+  SetInformationKeyInfo.KeySetInformationLength = KeyInformationLength;
+
+  Status = CmiCallRegisteredCallbacks(RegNtSetInformationKey, &SetInformationKeyInfo);
+  if (!NT_SUCCESS(Status))
+    {
+      ObDereferenceObject (KeyObject);
+      return Status;
+    }
+
+  if (KeyInformationClass != KeyWriteTimeInformation)
+    {
+      Status = STATUS_INVALID_INFO_CLASS;
+    }
+
+  else if (KeyInformationLength != sizeof (KEY_WRITE_TIME_INFORMATION))
+    {
+      Status = STATUS_INFO_LENGTH_MISMATCH;
+    }
+  else
+    {
+      /* Acquire hive lock */
+      KeEnterCriticalRegion();
+      ExAcquireResourceExclusiveLite(&CmiRegistryLock, TRUE);
+
+      VERIFY_KEY_OBJECT(KeyObject);
+
+      KeyObject->KeyCell->LastWriteTime.QuadPart =
+        ((PKEY_WRITE_TIME_INFORMATION)KeyInformation)->LastWriteTime.QuadPart;
+
+      CmiMarkBlockDirty (KeyObject->RegistryHive,
+                        KeyObject->KeyCellOffset);
+
+      /* Release hive lock */
+      ExReleaseResourceLite(&CmiRegistryLock);
+      KeLeaveCriticalRegion();
+    }
+
+  PostOperationInfo.Status = Status;
+  CmiCallRegisteredCallbacks(RegNtPostSetInformationKey, &PostOperationInfo);
+
+  ObDereferenceObject (KeyObject);
+
+  if (NT_SUCCESS(Status))
+    {
+      CmiSyncHives ();
+    }
+
+  DPRINT ("NtSaveKey() done\n");
+
+  return STATUS_SUCCESS;
 }
 
 
@@ -1590,27 +2693,72 @@ NtSetInformationKey(
  * KeyObjectAttributes->Name specifies the name of the key to unload.
  */
 NTSTATUS STDCALL
-NtUnloadKey(IN POBJECT_ATTRIBUTES KeyObjectAttributes)
+NtUnloadKey (IN POBJECT_ATTRIBUTES KeyObjectAttributes)
 {
-  UNIMPLEMENTED;
+  PREGISTRY_HIVE RegistryHive;
+  NTSTATUS Status;
+
+  PAGED_CODE();
+
+  DPRINT ("NtUnloadKey() called\n");
+
+#if 0
+  if (!SeSinglePrivilegeCheck (SeRestorePrivilege, KeGetPreviousMode ()))
+    return STATUS_PRIVILEGE_NOT_HELD;
+#endif
+
+  /* Acquire registry lock exclusively */
+  KeEnterCriticalRegion();
+  ExAcquireResourceExclusiveLite(&CmiRegistryLock, TRUE);
+
+  Status = CmiDisconnectHive (KeyObjectAttributes,
+                             &RegistryHive);
+  if (!NT_SUCCESS (Status))
+    {
+      DPRINT1 ("CmiDisconnectHive() failed (Status %lx)\n", Status);
+      ExReleaseResourceLite (&CmiRegistryLock);
+      KeLeaveCriticalRegion();
+      return Status;
+    }
+
+  DPRINT ("RegistryHive %p\n", RegistryHive);
+
+#if 0
+  /* Flush hive */
+  if (!IsNoFileHive (RegistryHive))
+    CmiFlushRegistryHive (RegistryHive);
+#endif
+
+  CmiRemoveRegistryHive (RegistryHive);
+
+  /* Release registry lock */
+  ExReleaseResourceLite (&CmiRegistryLock);
+  KeLeaveCriticalRegion();
+
+  DPRINT ("NtUnloadKey() done\n");
+
+  return STATUS_SUCCESS;
 }
 
 
 NTSTATUS STDCALL
-NtInitializeRegistry(IN BOOLEAN SetUpBoot)
+NtInitializeRegistry (IN BOOLEAN SetUpBoot)
 {
-  NTSTATUS Status = STATUS_ACCESS_DENIED;
+  NTSTATUS Status;
 
-  if (CmiRegistryInitialized == FALSE)
-    {
-      /* FIXME: save boot log file */
+  PAGED_CODE();
 
-      Status = CmiInitHives(SetUpBoot);
+  if (CmiRegistryInitialized == TRUE)
+    return STATUS_ACCESS_DENIED;
 
-      CmiRegistryInitialized = TRUE;
-    }
+  /* Save boot log file */
+  IopSaveBootLogToFile();
 
-  return(Status);
+  Status = CmiInitHives (SetUpBoot);
+
+  CmiRegistryInitialized = TRUE;
+
+  return Status;
 }
 
 /* EOF */