update for HEAD-2003021201
[reactos.git] / ntoskrnl / cm / registry.c
index d0d9c1d..8b7769e 100644 (file)
  *                  Created 22/05/98
  */
 
+#ifdef WIN32_REGDBG
+#include "cm_win32.h"
+#else
 #include <ddk/ntddk.h>
 #include <roscfg.h>
 #include <limits.h>
 #include <string.h>
 #include <internal/pool.h>
 #include <internal/registry.h>
+#include <reactos/bugcodes.h>
 
 #define NDEBUG
 #include <internal/debug.h>
 
 #include "cm.h"
+#endif
 
 /*  -------------------------------------------------  File Statics  */
 
@@ -29,6 +34,14 @@ POBJECT_TYPE  CmiKeyType = NULL;
 PREGISTRY_HIVE  CmiVolatileHive = NULL;
 KSPIN_LOCK  CmiKeyListLock;
 
+LIST_ENTRY CmiHiveListHead;
+ERESOURCE CmiHiveListLock;
+
+volatile BOOLEAN CmiHiveSyncEnabled = FALSE;
+volatile BOOLEAN CmiHiveSyncPending = FALSE;
+KDPC CmiHiveSyncDpc;
+KTIMER CmiHiveSyncTimer;
+
 static PKEY_OBJECT  CmiRootKey = NULL;
 static PKEY_OBJECT  CmiMachineKey = NULL;
 static PKEY_OBJECT  CmiUserKey = NULL;
@@ -38,6 +51,7 @@ static GENERIC_MAPPING CmiKeyMapping =
        {KEY_READ, KEY_WRITE, KEY_EXECUTE, KEY_ALL_ACCESS};
 
 
+
 VOID
 CmiCheckKey(BOOLEAN Verbose,
   HANDLE Key);
@@ -45,6 +59,12 @@ CmiCheckKey(BOOLEAN Verbose,
 static NTSTATUS
 CmiCreateCurrentControlSetLink(VOID);
 
+static VOID STDCALL
+CmiHiveSyncDpcRoutine(PKDPC Dpc,
+                     PVOID DeferredContext,
+                     PVOID SystemArgument1,
+                     PVOID SystemArgument2);
+
 /* FUNCTIONS ****************************************************************/
 
 VOID
@@ -65,58 +85,58 @@ CmiCheckSubKeys(BOOLEAN Verbose,
   Index = 0;
   while (TRUE)
     {
-         BufferSize = sizeof(KEY_NODE_INFORMATION) + 4096;
-           KeyInfo = ExAllocatePool(PagedPool, BufferSize);
-
-           Status = NtEnumerateKey(Key,
-                         Index,
-                               KeyNodeInformation,
-                               KeyInfo,
-                               BufferSize,
-                               &ResultSize);
+      BufferSize = sizeof(KEY_NODE_INFORMATION) + 4096;
+      KeyInfo = ExAllocatePool(PagedPool, BufferSize);
+
+      Status = NtEnumerateKey(Key,
+                             Index,
+                             KeyNodeInformation,
+                             KeyInfo,
+                             BufferSize,
+                             &ResultSize);
       if (!NT_SUCCESS(Status))
-                   {
-          ExFreePool(KeyInfo);
-                     if (Status == STATUS_NO_MORE_ENTRIES)
-                             Status = STATUS_SUCCESS;
-                     break;
-                   }
+       {
+         ExFreePool(KeyInfo);
+         if (Status == STATUS_NO_MORE_ENTRIES)
+           Status = STATUS_SUCCESS;
+         break;
+       }
 
       wcsncpy(Name,
-        KeyInfo->Name,
-        KeyInfo->NameLength / sizeof(WCHAR));
+             KeyInfo->Name,
+             KeyInfo->NameLength / sizeof(WCHAR));
 
       if (Verbose)
-                               {
-          DbgPrint("Key: %S\n", Name);
-                               }
+       {
+         DbgPrint("Key: %S\n", Name);
+       }
 
       /* FIXME: Check info. */
 
       ExFreePool(KeyInfo);
 
-       wcscpy(KeyBuffer, L"\\Registry\\");
-       wcscat(KeyBuffer, Name);
+      wcscpy(KeyBuffer, L"\\Registry\\");
+      wcscat(KeyBuffer, Name);
 
-       RtlInitUnicodeString(&KeyPath, KeyBuffer);
+      RtlInitUnicodeString(&KeyPath, KeyBuffer);
 
-                 InitializeObjectAttributes(&ObjectAttributes,
-                               &KeyPath,
-                               OBJ_CASE_INSENSITIVE,
-                               NULL,
-                               NULL);
+      InitializeObjectAttributes(&ObjectAttributes,
+                                &KeyPath,
+                                OBJ_CASE_INSENSITIVE,
+                                NULL,
+                                NULL);
 
-                 Status = NtOpenKey(&SubKey,
-                               KEY_ALL_ACCESS,
-                               &ObjectAttributes);
+      Status = NtOpenKey(&SubKey,
+                        KEY_ALL_ACCESS,
+                        &ObjectAttributes);
 
-                 assert(NT_SUCCESS(Status));
-               
-                 CmiCheckKey(Verbose, SubKey);
-               
-                 NtClose(SubKey);
+      assert(NT_SUCCESS(Status));
 
-                 Index++;
+      CmiCheckKey(Verbose, SubKey);
+
+      NtClose(SubKey);
+
+      Index++;
     }
 
   assert(NT_SUCCESS(Status));
@@ -137,37 +157,37 @@ CmiCheckValues(BOOLEAN Verbose,
   Index = 0;
   while (TRUE)
     {
-         BufferSize = sizeof(KEY_NODE_INFORMATION) + 4096;
-           ValueInfo = ExAllocatePool(PagedPool, BufferSize);
-
-           Status = NtEnumerateValueKey(Key,
-                         Index,
-                               KeyNodeInformation,
-                               ValueInfo,
-                               BufferSize,
-                               &ResultSize);
+      BufferSize = sizeof(KEY_NODE_INFORMATION) + 4096;
+      ValueInfo = ExAllocatePool(PagedPool, BufferSize);
+
+      Status = NtEnumerateValueKey(Key,
+                                  Index,
+                                  KeyNodeInformation,
+                                  ValueInfo,
+                                  BufferSize,
+                                  &ResultSize);
       if (!NT_SUCCESS(Status))
-                   {
-          ExFreePool(ValueInfo);
-                     if (Status == STATUS_NO_MORE_ENTRIES)
-                             Status = STATUS_SUCCESS;
-                     break;
-                   }
+       {
+         ExFreePool(ValueInfo);
+         if (Status == STATUS_NO_MORE_ENTRIES)
+           Status = STATUS_SUCCESS;
+         break;
+       }
 
       wcsncpy(Name,
-        ValueInfo->Name,
-        ValueInfo->NameLength / sizeof(WCHAR));
+             ValueInfo->Name,
+             ValueInfo->NameLength / sizeof(WCHAR));
 
       if (Verbose)
-                               {
-          DbgPrint("Value: %S\n", Name);
-                               }
+       {
+         DbgPrint("Value: %S\n", Name);
+       }
 
       /* FIXME: Check info. */
 
       ExFreePool(ValueInfo);
 
-                 Index++;
+      Index++;
     }
 
   assert(NT_SUCCESS(Status));
@@ -196,7 +216,7 @@ CmiCheckByName(BOOLEAN Verbose,
   wcscpy(KeyPathBuffer, L"\\Registry\\");
   wcscat(KeyPathBuffer, KeyName);
 
-       RtlInitUnicodeString(&KeyPath, KeyPathBuffer);
+  RtlInitUnicodeString(&KeyPath, KeyPathBuffer);
 
   InitializeObjectAttributes(&ObjectAttributes,
                &KeyPath,
@@ -211,11 +231,11 @@ CmiCheckByName(BOOLEAN Verbose,
   if (CHECKED)
     {
       if (!NT_SUCCESS(Status))
-                               {
+       {
           DbgPrint("KeyPath %wZ  Status: %.08x", KeyPath, Status);
           DbgPrint("KeyPath %S  Status: %.08x", KeyPath.Buffer, Status);
           assert(NT_SUCCESS(Status));
-                               }
+       }
     }
 
   CmiCheckKey(Verbose, Key);
@@ -244,7 +264,7 @@ CmInitializeRegistry(VOID)
   PKEY_OBJECT NewKey;
   HANDLE KeyHandle;
   NTSTATUS Status;
-  
+
   /*  Initialize the Key object type  */
   CmiKeyType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE));
   assert(CmiKeyType);
@@ -267,11 +287,15 @@ CmInitializeRegistry(VOID)
   CmiKeyType->DuplicationNotify = NULL;
   RtlInitUnicodeString(&CmiKeyType->TypeName, L"Key");
 
+  /* Initialize the hive list */
+  InitializeListHead(&CmiHiveListHead);
+  ExInitializeResourceLite(&CmiHiveListLock);
+
   /*  Build volatile registry store  */
   Status = CmiCreateRegistryHive(NULL, &CmiVolatileHive, FALSE);
   assert(NT_SUCCESS(Status));
 
-  /* Build the Root Key Object */
+  /* Create '\Registry' key. */
   RtlInitUnicodeString(&RootKeyName, REG_ROOT_KEY_NAME);
   InitializeObjectAttributes(&ObjectAttributes, &RootKeyName, 0, NULL, NULL);
   Status = ObCreateObject(&RootKeyHandle,
@@ -303,7 +327,7 @@ CmInitializeRegistry(VOID)
 
   /* Create initial predefined symbolic links */
 
-  /* HKEY_LOCAL_MACHINE */
+  /* Create '\Registry\Machine' key. */
   Status = ObCreateObject(&KeyHandle,
     STANDARD_RIGHTS_REQUIRED,
     NULL,
@@ -330,7 +354,7 @@ CmInitializeRegistry(VOID)
   CmiAddKeyToList(CmiRootKey, NewKey);
   CmiMachineKey = NewKey;
 
-  /* HKEY_USERS */
+  /* Create '\Registry\User' key. */
   Status = ObCreateObject(&KeyHandle,
                STANDARD_RIGHTS_REQUIRED,
                NULL,
@@ -357,7 +381,7 @@ CmInitializeRegistry(VOID)
        CmiAddKeyToList(CmiRootKey, NewKey);
        CmiUserKey = NewKey;
 
-  /* Create '\\Registry\\Machine\\HARDWARE' key. */
+  /* Create '\Registry\Machine\HARDWARE' key. */
   Status = ObCreateObject(&KeyHandle,
                STANDARD_RIGHTS_REQUIRED,
                NULL,
@@ -384,7 +408,7 @@ CmInitializeRegistry(VOID)
   CmiAddKeyToList(CmiMachineKey, NewKey);
   CmiHardwareKey = NewKey;
 
-  /* Create '\\Registry\\Machine\\HARDWARE\\DESCRIPTION' key. */
+  /* Create '\Registry\Machine\HARDWARE\DESCRIPTION' key. */
   Status = ObCreateObject(&KeyHandle,
                STANDARD_RIGHTS_REQUIRED,
                NULL,
@@ -410,7 +434,7 @@ CmInitializeRegistry(VOID)
   memcpy(NewKey->Name, "DESCRIPTION", strlen("DESCRIPTION"));
   CmiAddKeyToList(CmiHardwareKey, NewKey);
 
-  /* Create '\\Registry\\Machine\\HARDWARE\\DEVICEMAP' key. */
+  /* Create '\Registry\Machine\HARDWARE\DEVICEMAP' key. */
   Status = ObCreateObject(&KeyHandle,
                STANDARD_RIGHTS_REQUIRED,
                NULL,
@@ -436,7 +460,7 @@ CmInitializeRegistry(VOID)
   memcpy(NewKey->Name, "DEVICEMAP", strlen("DEVICEMAP"));
   CmiAddKeyToList(CmiHardwareKey,NewKey);
 
-  /* Create '\\Registry\\Machine\\HARDWARE\\RESOURCEMAP' key. */
+  /* Create '\Registry\Machine\HARDWARE\RESOURCEMAP' key. */
   Status = ObCreateObject(&KeyHandle,
                STANDARD_RIGHTS_REQUIRED,
                NULL,
@@ -472,13 +496,21 @@ CmInit2(PCHAR CommandLine)
 {
   PCHAR p1, p2;
   ULONG PiceStart;
+  NTSTATUS Status;
 
-  /* FIXME: Store current command line */
+  /* FIXME: Store system start options */
 
-  /* Create the 'CurrentControlSet' link. */
-  CmiCreateCurrentControlSetLink();
 
 
+  /* Create the 'CurrentControlSet' link. */
+  Status = CmiCreateCurrentControlSetLink();
+#ifndef WIN32_REGDBG
+  if (!NT_SUCCESS(Status))
+    {
+      KeBugCheck(CONFIG_INITIALIZATION_FAILED);
+    }
+#endif
+
   /* Set PICE 'Start' value to 1, if PICE debugging is enabled */
   PiceStart = 4;
   p1 = (PCHAR)CommandLine;
@@ -500,14 +532,18 @@ CmInit2(PCHAR CommandLine)
        }
       p1 = p2;
     }
-
-  RtlWriteRegistryValue(RTL_REGISTRY_SERVICES,
-                       L"\\Pice",
-                       L"Start",
-                       REG_DWORD,
-                       &PiceStart,
-                       sizeof(ULONG));
-
+#ifndef WIN32_REGDBG
+  Status = RtlWriteRegistryValue(RTL_REGISTRY_SERVICES,
+                                L"\\Pice",
+                                L"Start",
+                                REG_DWORD,
+                                &PiceStart,
+                                sizeof(ULONG));
+  if (!NT_SUCCESS(Status))
+    {
+      KeBugCheck(CONFIG_INITIALIZATION_FAILED);
+    }
+#endif
 }
 
 
@@ -588,12 +624,12 @@ CmiCreateCurrentControlSetLink(VOID)
 
   RtlInitUnicodeStringFromLiteral(&LinkValue,
                       L"SymbolicLinkValue");
-  Status=NtSetValueKey(KeyHandle,
-                      &LinkValue,
-                      0,
-                      REG_LINK,
-                      (PVOID)TargetNameBuffer,
-                      TargetNameLength);
+  Status = NtSetValueKey(KeyHandle,
+                        &LinkValue,
+                        0,
+                        REG_LINK,
+                        (PVOID)TargetNameBuffer,
+                        TargetNameLength);
   if (!NT_SUCCESS(Status))
     {
       DPRINT1("NtSetValueKey() failed (Status %lx)\n", Status);
@@ -619,11 +655,15 @@ CmiConnectHive(PWSTR FileName,
   HANDLE KeyHandle;
   NTSTATUS Status;
 
-  DPRINT("Called. FileName %S\n", FullName);
+  DPRINT("CmiConnectHive(%S, %S, %s, %p, %d) - Called.\n", FileName, FullName, KeyName, Parent, CreateNew);
 
   Status = CmiCreateRegistryHive(FileName, &RegistryHive, CreateNew);
   if (!NT_SUCCESS(Status))
-    return(Status);
+    {
+      DPRINT1("CmiCreateRegistryHive() failed (Status %lx)\n", Status);
+      KeBugCheck(0);
+      return(Status);
+    }
 
   RtlInitUnicodeString(&uKeyName, FullName);
 
@@ -639,7 +679,12 @@ CmiConnectHive(PWSTR FileName,
                          CmiKeyType,
                          (PVOID*)&NewKey);
   if (!NT_SUCCESS(Status))
-    return(Status);
+    {
+      DPRINT1("ObCreateObject() failed (Status %lx)\n", Status);
+      KeBugCheck(0);
+      CmiRemoveRegistryHive(RegistryHive);
+      return(Status);
+    }
 
   NewKey->RegistryHive = RegistryHive;
   NewKey->BlockOffset = RegistryHive->HiveHeader->RootKeyCell;
@@ -651,9 +696,9 @@ CmiConnectHive(PWSTR FileName,
 
   if ((NewKey->SubKeys == NULL) && (NewKey->KeyCell->NumberOfSubKeys != 0))
     {
-      /* FIXME: Cleanup from CmiCreateRegistryHive() */
       DPRINT("NumberOfSubKeys %d\n", NewKey->KeyCell->NumberOfSubKeys);
       ZwClose(NewKey);
+      CmiRemoveRegistryHive(RegistryHive);
       return(STATUS_INSUFFICIENT_RESOURCES);
     }
 
@@ -662,11 +707,11 @@ CmiConnectHive(PWSTR FileName,
 
   if ((NewKey->Name == NULL) && (strlen(KeyName) != 0))
     {
-      /* FIXME: Cleanup from CmiCreateRegistryHive() */
       DPRINT("strlen(KeyName) %d\n", strlen(KeyName));
       if (NewKey->SubKeys != NULL)
        ExFreePool(NewKey->SubKeys);
       ZwClose(NewKey);
+      CmiRemoveRegistryHive(RegistryHive);
       return(STATUS_INSUFFICIENT_RESOURCES);
     }
 
@@ -697,23 +742,25 @@ CmiInitializeHive(PWSTR FileName,
 
   if (!NT_SUCCESS(Status))
     {
-      DPRINT("Status %.08x\n", Status);
 #if 0
+#ifdef WIN32_REGDBG
       WCHAR AltFileName[MAX_PATH];
 
       CPRINT("WARNING! Registry file %S not found\n", FileName);
+      //DPRINT("Status %.08x\n", Status);
 
       wcscpy(AltFileName, FileName);
       wcscat(AltFileName, L".alt");
 
+      DPRINT("Attempting to connect the alternative hive %S\n", AltFileName);
       /* Try to connect the alternative hive */
       Status = CmiConnectHive(AltFileName, FullName, KeyName, Parent, TRUE);
 
-      if (!NT_SUCCESS(Status))
-       {
-         CPRINT("WARNING! Alternative registry file %S not found\n", AltFileName);
-         DPRINT("Status %.08x\n", Status);
-       }
+      if (!NT_SUCCESS(Status)) {
+             CPRINT("WARNING! Alternative registry file %S not found\n", AltFileName);
+           //DPRINT("Status %.08x\n", Status);
+         }
+#endif
 #endif
     }
 
@@ -726,39 +773,166 @@ CmiInitializeHive(PWSTR FileName,
 NTSTATUS
 CmiInitHives(BOOLEAN SetUpBoot)
 {
+  PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
+  OBJECT_ATTRIBUTES ObjectAttributes;
+  UNICODE_STRING KeyName;
+  UNICODE_STRING ValueName;
+  HANDLE KeyHandle;
+
   NTSTATUS Status;
 
+  WCHAR ConfigPath[MAX_PATH];
+
+  ULONG BufferSize;
+  ULONG ResultSize;
+  PWSTR EndPtr;
+
+
   DPRINT("CmiInitHives() called\n");
 
+  if (SetUpBoot == TRUE)
+  {
+    RtlInitUnicodeStringFromLiteral(&KeyName,
+                                   L"\\Registry\\Machine\\HARDWARE");
+    InitializeObjectAttributes(&ObjectAttributes,
+                              &KeyName,
+                              OBJ_CASE_INSENSITIVE,
+                              NULL,
+                              NULL);
+    Status =  NtOpenKey(&KeyHandle,
+                       KEY_ALL_ACCESS,
+                       &ObjectAttributes);
+    if (!NT_SUCCESS(Status))
+    {
+      DPRINT1("NtOpenKey() failed (Status %lx)\n", Status);
+      return(Status);
+    }
+
+    RtlInitUnicodeStringFromLiteral(&ValueName,
+                                   L"InstallPath");
+
+    BufferSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 4096;
+    ValueInfo = ExAllocatePool(PagedPool,
+                              BufferSize);
+    if (ValueInfo == NULL)
+    {
+      NtClose(KeyHandle);
+      return(STATUS_INSUFFICIENT_RESOURCES);
+    }
+
+    Status = NtQueryValueKey(KeyHandle,
+                            &ValueName,
+                            KeyValuePartialInformation,
+                            ValueInfo,
+                            BufferSize,
+                            &ResultSize);
+    NtClose(KeyHandle);
+    if (ValueInfo == NULL)
+    {
+      ExFreePool(ValueInfo);
+      return(Status);
+    }
+
+    RtlCopyMemory(ConfigPath,
+                 ValueInfo->Data,
+                 ValueInfo->DataLength);
+    ConfigPath[ValueInfo->DataLength / sizeof(WCHAR)] = (WCHAR)0;
+    ExFreePool(ValueInfo);
+  }
+  else
+  {
+    wcscpy(ConfigPath, L"\\SystemRoot");
+  }
+  wcscat(ConfigPath, L"\\system32\\config");
+
+  DPRINT("ConfigPath: %S\n", ConfigPath);
+
+  EndPtr = ConfigPath + wcslen(ConfigPath);
+
   CmiDoVerify = TRUE;
 
-  /* FIXME: Delete temporary \Registry\Machine\System */
+  /* FIXME: Save boot log */
+
+  /* FIXME: Rename \Registry\Machine\System */
 
   /* Connect the SYSTEM hive */
-  /* FIXME: Don't overwrite the existing 'System' hive yet */
-//  Status = CmiInitializeHive(SYSTEM_REG_FILE, REG_SYSTEM_KEY_NAME, "System", CmiMachineKey);
+//  Status = CmiInitializeHive(SYSTEM_REG_FILE, REG_SYSTEM_KEY_NAME, "System", CmiMachineKey, SetUpBoot);
 //  assert(NT_SUCCESS(Status));
 
+  /* FIXME: Synchronize old and new system hive (??) */
+
+  /* FIXME: Delete old system hive */
+
   /* Connect the SOFTWARE hive */
-  Status = CmiInitializeHive(SOFTWARE_REG_FILE, REG_SOFTWARE_KEY_NAME, "Software", CmiMachineKey, SetUpBoot);
-  //assert(NT_SUCCESS(Status));
+  wcscpy(EndPtr, REG_SOFTWARE_FILE_NAME);
+  DPRINT1("ConfigPath: %S\n", ConfigPath);
+
+  Status = CmiInitializeHive(ConfigPath,
+                            REG_SOFTWARE_KEY_NAME,
+                            "Software",
+                            CmiMachineKey,
+                            SetUpBoot);
+  if (!NT_SUCCESS(Status))
+  {
+    DPRINT1("CmiInitializeHive() failed (Status %lx)\n", Status);
+    return(Status);
+  }
 
   /* Connect the SAM hive */
-  Status = CmiInitializeHive(SAM_REG_FILE,REG_SAM_KEY_NAME, "Sam", CmiMachineKey, SetUpBoot);
-  //assert(NT_SUCCESS(Status));
+  wcscpy(EndPtr, REG_SAM_FILE_NAME);
+  DPRINT1("ConfigPath: %S\n", ConfigPath);
+
+  Status = CmiInitializeHive(ConfigPath,
+                            REG_SAM_KEY_NAME,
+                            "Sam",
+                            CmiMachineKey,
+                            SetUpBoot);
+  if (!NT_SUCCESS(Status))
+  {
+    DPRINT1("CmiInitializeHive() failed (Status %lx)\n", Status);
+    return(Status);
+  }
 
   /* Connect the SECURITY hive */
-  Status = CmiInitializeHive(SEC_REG_FILE, REG_SEC_KEY_NAME, "Security", CmiMachineKey, SetUpBoot);
-  //assert(NT_SUCCESS(Status));
+  wcscpy(EndPtr, REG_SEC_FILE_NAME);
+  DPRINT1("ConfigPath: %S\n", ConfigPath);
+  Status = CmiInitializeHive(ConfigPath,
+                            REG_SEC_KEY_NAME,
+                            "Security",
+                            CmiMachineKey,
+                            SetUpBoot);
+  if (!NT_SUCCESS(Status))
+  {
+    DPRINT1("CmiInitializeHive() failed (Status %lx)\n", Status);
+    return(Status);
+  }
 
   /* Connect the DEFAULT hive */
-  Status = CmiInitializeHive(USER_REG_FILE, REG_USER_KEY_NAME, ".Default", CmiUserKey, SetUpBoot);
-  //assert(NT_SUCCESS(Status));
+  wcscpy(EndPtr, REG_USER_FILE_NAME);
+  DPRINT1("ConfigPath: %S\n", ConfigPath);
+
+  Status = CmiInitializeHive(ConfigPath,
+                            REG_USER_KEY_NAME,
+                            ".Default",
+                            CmiUserKey,
+                            SetUpBoot);
+  if (!NT_SUCCESS(Status))
+  {
+    DPRINT1("CmiInitializeHive() failed (Status %lx)\n", Status);
+    return(Status);
+  }
 
   /* FIXME : initialize standards symbolic links */
 
 //  CmiCheckRegistry(TRUE);
 
+  /* Start automatic hive synchronization */
+  KeInitializeDpc(&CmiHiveSyncDpc,
+                 CmiHiveSyncDpcRoutine,
+                 NULL);
+  KeInitializeTimer(&CmiHiveSyncTimer);
+  CmiHiveSyncEnabled = TRUE;
+
   DPRINT("CmiInitHives() done\n");
 
   return(STATUS_SUCCESS);
@@ -768,12 +942,135 @@ CmiInitHives(BOOLEAN SetUpBoot)
 VOID
 CmShutdownRegistry(VOID)
 {
-  DPRINT("CmShutdownRegistry() called\n");
+  PREGISTRY_HIVE Hive;
+  PLIST_ENTRY Entry;
+
+  DPRINT1("CmShutdownRegistry() called\n");
+
+  /* Stop automatic hive synchronization */
+  CmiHiveSyncEnabled = FALSE;
+
+  /* Acquire hive list lock exclusively */
+  ExAcquireResourceExclusiveLite(&CmiHiveListLock, TRUE);
+
+  Entry = CmiHiveListHead.Flink;
+  while (Entry != &CmiHiveListHead)
+    {
+      Hive = CONTAINING_RECORD(Entry, REGISTRY_HIVE, HiveList);
+
+      if (IsPermanentHive(Hive))
+       {
+         /* Acquire hive resource exclusively */
+         ExAcquireResourceExclusiveLite(&Hive->HiveResource,
+                                        TRUE);
+
+         /* Flush non-volatile hive */
+         CmiFlushRegistryHive(Hive);
+
+         /* Dereference file */
+         ObDereferenceObject(Hive->FileObject);
+         Hive->FileObject = NULL;
+
+         /* Release hive resource */
+         ExReleaseResourceLite(&Hive->HiveResource);
+       }
+
+      Entry = Entry->Flink;
+    }
+
+  /* Release hive list lock */
+  ExReleaseResourceLite(&CmiHiveListLock);
+
+  DPRINT1("CmShutdownRegistry() done\n");
+}
+
+
+VOID STDCALL
+CmiHiveSyncRoutine(PVOID DeferredContext)
+{
+  PREGISTRY_HIVE Hive;
+  PLIST_ENTRY Entry;
+
+  DPRINT1("CmiHiveSyncRoutine() called\n");
+
+  CmiHiveSyncPending = FALSE;
+
+  /* Acquire hive list lock exclusively */
+  ExAcquireResourceExclusiveLite(&CmiHiveListLock, TRUE);
+
+  Entry = CmiHiveListHead.Flink;
+  while (Entry != &CmiHiveListHead)
+    {
+      Hive = CONTAINING_RECORD(Entry, REGISTRY_HIVE, HiveList);
+
+      if (IsPermanentHive(Hive))
+       {
+         /* Acquire hive resource exclusively */
+         ExAcquireResourceExclusiveLite(&Hive->HiveResource,
+                                        TRUE);
+
+         /* Flush non-volatile hive */
+         CmiFlushRegistryHive(Hive);
+
+         /* Release hive resource */
+         ExReleaseResourceLite(&Hive->HiveResource);
+       }
+
+      Entry = Entry->Flink;
+    }
+
+  /* Release hive list lock */
+  ExReleaseResourceLite(&CmiHiveListLock);
+
+  DPRINT("DeferredContext %x\n", DeferredContext);
+  ExFreePool(DeferredContext);
+}
+
+
+static VOID STDCALL
+CmiHiveSyncDpcRoutine(PKDPC Dpc,
+                     PVOID DeferredContext,
+                     PVOID SystemArgument1,
+                     PVOID SystemArgument2)
+{
+  PWORK_QUEUE_ITEM WorkQueueItem;
+
+  WorkQueueItem = ExAllocatePool(NonPagedPool,
+                                sizeof(WORK_QUEUE_ITEM));
+  if (WorkQueueItem == NULL)
+    {
+      DbgPrint("Failed to allocate work item\n");
+      return;
+    }
+
+  ExInitializeWorkItem(WorkQueueItem,
+                      CmiHiveSyncRoutine,
+                      WorkQueueItem);
+
+  DPRINT("DeferredContext %x\n", WorkQueueItem);
+  ExQueueWorkItem(WorkQueueItem,
+                 CriticalWorkQueue);
+}
+
+
+VOID
+CmiSyncHives(VOID)
+{
+  LARGE_INTEGER Timeout;
+
+  DPRINT("CmiSyncHives() called\n");
+
+  if (CmiHiveSyncEnabled == FALSE ||
+      CmiHiveSyncPending == TRUE)
+    return;
+
+  CmiHiveSyncPending = TRUE;
+
 
-  /* Note:
-   *   Don't call UNIMPLEMENTED() here since this function is
-   *   called by NtShutdownSystem().
-   */
+  Timeout.QuadPart = -50000000LL;
+  KeSetTimer(&CmiHiveSyncTimer,
+            Timeout,
+            &CmiHiveSyncDpc);
 }
 
 /* EOF */