[SHELL/EXPERIMENTS]
[reactos.git] / ntoskrnl / config / cmsysini.c
index 9d1540c..45242d2 100644 (file)
@@ -1,12 +1,13 @@
 /*
  * PROJECT:         ReactOS Kernel
- * LICENSE:         GPL - See COPYING in the top level directory
+ * LICENSE:         BSD - See COPYING.ARM in the top level directory
  * FILE:            ntoskrnl/config/cmsysini.c
  * PURPOSE:         Configuration Manager - System Initialization Code
- * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
+ * PROGRAMMERS:     ReactOS Portable Systems Group
+ *                  Alex Ionescu (alex.ionescu@reactos.org)
  */
 
-/* INCLUDES ******************************************************************/
+/* INCLUDES *******************************************************************/
 
 #include "ntoskrnl.h"
 #define NDEBUG
@@ -23,16 +24,82 @@ LONG CmpLoadWorkerIncrement;
 PEPROCESS CmpSystemProcess;
 BOOLEAN HvShutdownComplete;
 PVOID CmpRegistryLockCallerCaller, CmpRegistryLockCaller;
-BOOLEAN CmpFlushStarveWriters;
 BOOLEAN CmpFlushOnLockRelease;
 BOOLEAN CmpSpecialBootCondition;
 BOOLEAN CmpNoWrite;
-BOOLEAN CmpForceForceFlush;
 BOOLEAN CmpWasSetupBoot;
+BOOLEAN CmpProfileLoaded;
+ULONG CmpTraceLevel = 0;
 
+extern LONG CmpFlushStarveWriters;
 extern BOOLEAN CmFirstTime;
 
-/* FUNCTIONS *****************************************************************/
+/* FUNCTIONS ******************************************************************/
+
+BOOLEAN
+NTAPI
+CmpLinkKeyToHive(
+    _In_z_ PWSTR LinkKeyName,
+    _In_z_ PWSTR TargetKeyName)
+{
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    UNICODE_STRING LinkKeyName_U;
+    HANDLE TargetKeyHandle;
+    ULONG Disposition;
+    NTSTATUS Status;
+    PAGED_CODE();
+
+    /* Initialize the object attributes */
+    RtlInitUnicodeString(&LinkKeyName_U, LinkKeyName);
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &LinkKeyName_U,
+                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+                               NULL,
+                               NULL);
+
+    /* Create the link key */
+    Status = ZwCreateKey(&TargetKeyHandle,
+                         KEY_CREATE_LINK,
+                         &ObjectAttributes,
+                         0,
+                         NULL,
+                         REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK,
+                         &Disposition);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("CM: CmpLinkKeyToHive: couldn't create %S Status = 0x%lx\n",
+                LinkKeyName, Status);
+        return FALSE;
+    }
+
+    /* Check if the new key was actually created */
+    if (Disposition != REG_CREATED_NEW_KEY)
+    {
+        DPRINT1("CM: CmpLinkKeyToHive: %S already exists!\n", LinkKeyName);
+        ZwClose(TargetKeyHandle);
+        return FALSE;
+    }
+
+    /* Set the target key name as link target */
+    Status = ZwSetValueKey(TargetKeyHandle,
+                           &CmSymbolicLinkValueName,
+                           0,
+                           REG_LINK,
+                           TargetKeyName,
+                           wcslen(TargetKeyName) * sizeof(WCHAR));
+
+    /* Close the link key handle */
+    ObCloseHandle(TargetKeyHandle, KernelMode);
+
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("CM: CmpLinkKeyToHive: couldn't create symbolic link for %S\n",
+                TargetKeyName);
+        return FALSE;
+    }
+
+    return TRUE;
+}
 
 VOID
 NTAPI
@@ -44,7 +111,7 @@ CmpDeleteKeyObject(PVOID DeletedObject)
     REG_POST_OPERATION_INFORMATION PostOperationInfo;
     NTSTATUS Status;
     PAGED_CODE();
-    
+
     /* First off, prepare the handle close information callback */
     PostOperationInfo.Object = KeyBody;
     KeyHandleCloseInfo.Object = KeyBody;
@@ -57,12 +124,12 @@ CmpDeleteKeyObject(PVOID DeletedObject)
         CmiCallRegisteredCallbacks(RegNtPostKeyHandleClose, &PostOperationInfo);
         return;
     }
-    
+
     /* Acquire hive lock */
     CmpLockRegistry();
-    
+
     /* Make sure this is a valid key body */
-    if (KeyBody->Type == TAG('k', 'y', '0', '2'))
+    if (KeyBody->Type == '20yk')
     {
         /* Get the KCB */
         Kcb = KeyBody->KeyControlBlock;
@@ -70,16 +137,15 @@ CmpDeleteKeyObject(PVOID DeletedObject)
         {
             /* Delist the key */
             DelistKeyBodyFromKCB(KeyBody, FALSE);
+
+            /* Dereference the KCB */
+            CmpDelayDerefKeyControlBlock(Kcb);
         }
-        
-        /* Dereference the KCB */
-        CmpDelayDerefKeyControlBlock(Kcb);
-        
     }
-    
+
     /* Release the registry lock */
     CmpUnlockRegistry();
-    
+
     /* Do the post callback */
     PostOperationInfo.Status = STATUS_SUCCESS;
     CmiCallRegisteredCallbacks(RegNtPostKeyHandleClose, &PostOperationInfo);
@@ -95,16 +161,16 @@ CmpCloseKeyObject(IN PEPROCESS Process OPTIONAL,
 {
     PCM_KEY_BODY KeyBody = (PCM_KEY_BODY)Object;
     PAGED_CODE();
-    
+
     /* Don't do anything if we're not the last handle */
     if (SystemHandleCount > 1) return;
-    
+
     /* Make sure we're a valid key body */
-    if (KeyBody->Type == TAG('k', 'y', '0', '2'))
+    if (KeyBody->Type == '20yk')
     {
         /* Don't do anything if we don't have a notify block */
         if (!KeyBody->NotifyBlock) return;
-        
+
         /* This shouldn't happen yet */
         ASSERT(FALSE);
     }
@@ -119,9 +185,97 @@ CmpQueryKeyName(IN PVOID ObjectBody,
                 OUT PULONG ReturnLength,
                 IN KPROCESSOR_MODE PreviousMode)
 {
-    DPRINT1("CmpQueryKeyName() called\n");
-    while (TRUE);
-    return STATUS_SUCCESS;
+    PUNICODE_STRING KeyName;
+    ULONG BytesToCopy;
+    NTSTATUS Status = STATUS_SUCCESS;
+    PCM_KEY_BODY KeyBody = (PCM_KEY_BODY)ObjectBody;
+    PCM_KEY_CONTROL_BLOCK Kcb = KeyBody->KeyControlBlock;
+
+    /* Acquire hive lock */
+    CmpLockRegistry();
+
+    /* Lock KCB shared */
+    CmpAcquireKcbLockShared(Kcb);
+
+    /* Check if it's a deleted block */
+    if (Kcb->Delete)
+    {
+        /* Release the locks */
+        CmpReleaseKcbLock(Kcb);
+        CmpUnlockRegistry();
+
+        /* Let the caller know it's deleted */
+        return STATUS_KEY_DELETED;
+    }
+
+    /* Get the name */
+    KeyName = CmpConstructName(Kcb);
+
+    /* Release the locks */
+    CmpReleaseKcbLock(Kcb);
+    CmpUnlockRegistry();
+
+    /* Check if we got the name */
+    if (!KeyName) return STATUS_INSUFFICIENT_RESOURCES;
+
+    /* Set the returned length */
+    *ReturnLength = KeyName->Length + sizeof(OBJECT_NAME_INFORMATION) + sizeof(WCHAR);
+
+    /* Calculate amount of bytes to copy into the buffer */
+    BytesToCopy = KeyName->Length + sizeof(WCHAR);
+
+    /* Check if the provided buffer is too small to fit even anything */
+    if ((Length <= sizeof(OBJECT_NAME_INFORMATION)) ||
+        ((Length < (*ReturnLength)) && (BytesToCopy < sizeof(WCHAR))))
+    {
+        /* Free the buffer allocated by CmpConstructName */
+        ExFreePoolWithTag(KeyName, TAG_CM);
+
+        /* Return buffer length failure without writing anything there because nothing fits */
+        return STATUS_INFO_LENGTH_MISMATCH;
+    }
+
+    /* Check if the provided buffer can be partially written */
+    if (Length < (*ReturnLength))
+    {
+        /* Yes, indicate so in the return status */
+        Status = STATUS_INFO_LENGTH_MISMATCH;
+
+        /* Calculate amount of bytes which the provided buffer could handle */
+        BytesToCopy = Length - sizeof(OBJECT_NAME_INFORMATION);
+    }
+
+    /* Remove the null termination character from the size */
+    BytesToCopy -= sizeof(WCHAR);
+
+    /* Fill in the result */
+    _SEH2_TRY
+    {
+        /* Return data to user */
+        ObjectNameInfo->Name.Buffer = (PWCHAR)(ObjectNameInfo + 1);
+        ObjectNameInfo->Name.MaximumLength = KeyName->Length;
+        ObjectNameInfo->Name.Length = KeyName->Length;
+
+        /* Copy string content*/
+        RtlCopyMemory(ObjectNameInfo->Name.Buffer,
+                      KeyName->Buffer,
+                      BytesToCopy);
+
+        /* Null terminate it */
+        ObjectNameInfo->Name.Buffer[BytesToCopy / sizeof(WCHAR)] = 0;
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        /* Get the status */
+        Status = _SEH2_GetExceptionCode();
+    }
+    _SEH2_END;
+
+    /* Free the buffer allocated by CmpConstructName */
+    ExFreePoolWithTag(KeyName, TAG_CM);
+
+    /* Return status */
+    return Status;
 }
 
 NTSTATUS
@@ -239,11 +393,12 @@ CmpInitHiveFromFile(IN PCUNICODE_STRING HiveName,
 
 NTSTATUS
 NTAPI
+INIT_FUNCTION
 CmpSetSystemValues(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
 {
     OBJECT_ATTRIBUTES ObjectAttributes;
-    UNICODE_STRING KeyName, ValueName = {0};
-    HANDLE KeyHandle;
+    UNICODE_STRING KeyName, ValueName = { 0, 0, NULL };
+    HANDLE KeyHandle = NULL;
     NTSTATUS Status;
     ASSERT(LoaderBlock != NULL);
 
@@ -269,9 +424,9 @@ CmpSetSystemValues(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
                            CmpLoadOptions.Length);
     if (!NT_SUCCESS(Status)) goto Quickie;
 
-    /* Setup value name for system boot device */
+    /* Setup value name for system boot device in ARC format */
     RtlInitUnicodeString(&KeyName, L"SystemBootDevice");
-    RtlCreateUnicodeStringFromAsciiz(&ValueName, LoaderBlock->NtBootPathName);
+    RtlCreateUnicodeStringFromAsciiz(&ValueName, LoaderBlock->ArcBootDeviceName);
     Status = NtSetValueKey(KeyHandle,
                            &KeyName,
                            0,
@@ -284,7 +439,7 @@ Quickie:
     RtlFreeUnicodeString(&ValueName);
 
     /* Close the key and return */
-    NtClose(KeyHandle);
+    if (KeyHandle) NtClose(KeyHandle);
 
     /* Return the status */
     return (ExpInTextModeSetup ? STATUS_SUCCESS : Status);
@@ -292,6 +447,7 @@ Quickie:
 
 NTSTATUS
 NTAPI
+INIT_FUNCTION
 CmpCreateControlSet(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
 {
     UNICODE_STRING ConfigName = RTL_CONSTANT_STRING(L"Control\\IDConfigDB");
@@ -493,7 +649,7 @@ UseSet:
     LoaderExtension = LoaderBlock->Extension;
     if (LoaderExtension)
     {
-        ASSERTMSG("ReactOS doesn't support NTLDR Profiles yet!\n", FALSE);
+        DPRINT("ReactOS doesn't support NTLDR Profiles yet!\n");
     }
 
     /* Create the current hardware profile key */
@@ -566,19 +722,19 @@ CmpLinkHiveToMaster(IN PUNICODE_STRING LinkName,
     HANDLE KeyHandle;
     PCM_KEY_BODY KeyBody;
     PAGED_CODE();
-    
+
     /* Setup the object attributes */
     InitializeObjectAttributes(&ObjectAttributes,
                                LinkName,
                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                                RootDirectory,
                                SecurityDescriptor);
-    
+
     /* Setup the parse context */
     ParseContext.CreateLink = TRUE;
     ParseContext.CreateOperation = TRUE;
     ParseContext.ChildHive.KeyHive = &RegistryHive->Hive;
-    
+
     /* Check if we have a root keycell or if we need to create it */
     if (Allocate)
     {
@@ -588,7 +744,7 @@ CmpLinkHiveToMaster(IN PUNICODE_STRING LinkName,
     else
     {
         /* We have one */
-        ParseContext.ChildHive.KeyCell = RegistryHive->Hive.BaseBlock->RootCell;   
+        ParseContext.ChildHive.KeyCell = RegistryHive->Hive.BaseBlock->RootCell;
     }
 
     /* Create the link node */
@@ -600,10 +756,10 @@ CmpLinkHiveToMaster(IN PUNICODE_STRING LinkName,
                                 (PVOID)&ParseContext,
                                 &KeyHandle);
     if (!NT_SUCCESS(Status)) return Status;
-    
+
     /* Mark the hive as clean */
     RegistryHive->Hive.DirtyFlag = FALSE;
-    
+
     /* ReactOS Hack: Keep alive */
     Status = ObReferenceObjectByHandle(KeyHandle,
                                        0,
@@ -615,11 +771,12 @@ CmpLinkHiveToMaster(IN PUNICODE_STRING LinkName,
 
     /* Close the extra handle */
     ZwClose(KeyHandle);
-    return STATUS_SUCCESS;    
+    return STATUS_SUCCESS;
 }
 
 BOOLEAN
 NTAPI
+INIT_FUNCTION
 CmpInitializeSystemHive(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
 {
     PVOID HiveBase;
@@ -643,7 +800,7 @@ CmpInitializeSystemHive(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
     if (!Buffer)
     {
         /* Fail */
-        KEBUGCHECKEX(BAD_SYSTEM_CONFIG_INFO, 3, 1, (ULONG_PTR)LoaderBlock, 0);
+        KeBugCheckEx(BAD_SYSTEM_CONFIG_INFO, 3, 1, (ULONG_PTR)LoaderBlock, 0);
     }
 
     /* Setup the unicode string */
@@ -659,7 +816,6 @@ CmpInitializeSystemHive(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
     if (HiveBase)
     {
         /* Import it */
-        ((PHBASE_BLOCK)HiveBase)->Length = LoaderBlock->RegistryLength;
         Status = CmpInitializeHive((PCMHIVE*)&SystemHive,
                                    HINIT_MEMORY,
                                    HIVE_NOLAZYFLUSH,
@@ -696,7 +852,7 @@ CmpInitializeSystemHive(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
                                    &HiveName,
                                    0);
         if (!NT_SUCCESS(Status)) return FALSE;
-        
+
         /* Set the hive filename */
         RtlCreateUnicodeString(&SystemHive->FileFullPath,
                                L"\\SystemRoot\\System32\\Config\\SYSTEM");
@@ -706,7 +862,7 @@ CmpInitializeSystemHive(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
     }
 
     /* Save the boot type */
-    if (SystemHive) CmpBootType = SystemHive->Hive.BaseBlock->BootType;
+    CmpBootType = SystemHive->Hive.BaseBlock->BootType;
 
     /* Are we in self-healing mode? */
     if (!CmSelfHeal)
@@ -716,7 +872,7 @@ CmpInitializeSystemHive(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
         if (CmpBootType & 4)
         {
             /* We're disabled, so bugcheck */
-            KEBUGCHECKEX(BAD_SYSTEM_CONFIG_INFO,
+            KeBugCheckEx(BAD_SYSTEM_CONFIG_INFO,
                          3,
                          3,
                          (ULONG_PTR)SystemHive,
@@ -736,7 +892,7 @@ CmpInitializeSystemHive(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
                                  SecurityDescriptor);
 
     /* Free the security descriptor */
-    ExFreePool(SecurityDescriptor);
+    ExFreePoolWithTag(SecurityDescriptor, TAG_CM);
     if (!NT_SUCCESS(Status)) return FALSE;
 
     /* Add the hive to the hive list */
@@ -748,6 +904,7 @@ CmpInitializeSystemHive(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
 
 NTSTATUS
 NTAPI
+INIT_FUNCTION
 CmpCreateObjectTypes(VOID)
 {
     OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
@@ -773,6 +930,7 @@ CmpCreateObjectTypes(VOID)
     ObjectTypeInitializer.QueryNameProcedure = CmpQueryKeyName;
     ObjectTypeInitializer.CloseProcedure = CmpCloseKeyObject;
     ObjectTypeInitializer.SecurityRequired = TRUE;
+    ObjectTypeInitializer.InvalidAttributes = OBJ_EXCLUSIVE | OBJ_PERMANENT;
 
     /* Create it */
     return ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &CmpKeyObjectType);
@@ -780,6 +938,7 @@ CmpCreateObjectTypes(VOID)
 
 BOOLEAN
 NTAPI
+INIT_FUNCTION
 CmpCreateRootNode(IN PHHIVE Hive,
                   IN PCWSTR Name,
                   OUT PHCELL_INDEX Index)
@@ -840,6 +999,7 @@ CmpCreateRootNode(IN PHHIVE Hive,
 
 BOOLEAN
 NTAPI
+INIT_FUNCTION
 CmpCreateRegistryRoot(VOID)
 {
     UNICODE_STRING KeyName;
@@ -876,7 +1036,7 @@ CmpCreateRegistryRoot(VOID)
                             0,
                             0,
                             (PVOID*)&RootKey);
-    ExFreePool(SecurityDescriptor);
+    ExFreePoolWithTag(SecurityDescriptor, TAG_CM);
     if (!NT_SUCCESS(Status)) return FALSE;
 
     /* Sanity check, and get the key cell */
@@ -896,7 +1056,7 @@ CmpCreateRegistryRoot(VOID)
 
     /* Initialize the object */
     RootKey->KeyControlBlock = Kcb;
-    RootKey->Type = TAG('k', 'y', '0', '2');
+    RootKey->Type = '20yk';
     RootKey->NotifyBlock = NULL;
     RootKey->ProcessID = PsGetCurrentProcessId();
 
@@ -951,7 +1111,7 @@ CmpGetRegistryPath(IN PWCHAR ConfigPath)
                             KEY_ALL_ACCESS,
                             &ObjectAttributes);
         if (!NT_SUCCESS(Status)) return Status;
-        
+
         /* Allocate the buffer */
         BufferSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 4096;
         ValueInfo = ExAllocatePoolWithTag(PagedPool, BufferSize, TAG_CM);
@@ -973,7 +1133,7 @@ CmpGetRegistryPath(IN PWCHAR ConfigPath)
         if (!NT_SUCCESS(Status))
         {
             /* Fail */
-            ExFreePool(ValueInfo);
+            ExFreePoolWithTag(ValueInfo, TAG_CM);
             return Status;
         }
 
@@ -982,7 +1142,7 @@ CmpGetRegistryPath(IN PWCHAR ConfigPath)
                       ValueInfo->Data,
                       ValueInfo->DataLength);
         ConfigPath[ValueInfo->DataLength / sizeof(WCHAR)] = UNICODE_NULL;
-        ExFreePool(ValueInfo);
+        ExFreePoolWithTag(ValueInfo, TAG_CM);
     }
     else
     {
@@ -1003,41 +1163,42 @@ CmpLoadHiveThread(IN PVOID StartContext)
 {
     WCHAR FileBuffer[MAX_PATH], RegBuffer[MAX_PATH], ConfigPath[MAX_PATH];
     UNICODE_STRING TempName, FileName, RegName;
-    ULONG FileStart, RegStart, i, ErrorResponse, WorkerCount, Length;
+    ULONG i, ErrorResponse, WorkerCount, Length;
+    USHORT FileStart;
+    //ULONG RegStart;
     ULONG PrimaryDisposition, SecondaryDisposition, ClusterSize;
     PCMHIVE CmHive;
-    HANDLE PrimaryHandle, LogHandle;
+    HANDLE PrimaryHandle = NULL, LogHandle = NULL;
     NTSTATUS Status = STATUS_SUCCESS;
     PVOID ErrorParameters;
     PAGED_CODE();
-    
+
     /* Get the hive index, make sure it makes sense */
-    i = (ULONG)StartContext;
+    i = PtrToUlong(StartContext);
     ASSERT(CmpMachineHiveList[i].Name != NULL);
-   
+
     /* We were started */
     CmpMachineHiveList[i].ThreadStarted = TRUE;
-    
+
     /* Build the file name and registry name strings */
     RtlInitEmptyUnicodeString(&FileName, FileBuffer, MAX_PATH);
     RtlInitEmptyUnicodeString(&RegName, RegBuffer, MAX_PATH);
-    
+
     /* Now build the system root path */
     CmpGetRegistryPath(ConfigPath);
     RtlInitUnicodeString(&TempName, ConfigPath);
     RtlAppendStringToString((PSTRING)&FileName, (PSTRING)&TempName);
     FileStart = FileName.Length;
-    
+
     /* And build the registry root path */
     RtlInitUnicodeString(&TempName, L"\\REGISTRY\\");
     RtlAppendStringToString((PSTRING)&RegName, (PSTRING)&TempName);
-    RegStart = RegName.Length;
-    
+    //RegStart = RegName.Length;
+
     /* Build the base name */
-    RegName.Length = RegStart;
     RtlInitUnicodeString(&TempName, CmpMachineHiveList[i].BaseName);
     RtlAppendStringToString((PSTRING)&RegName, (PSTRING)&TempName);
-    
+
     /* Check if this is a child of the root */
     if (RegName.Buffer[RegName.Length / sizeof(WCHAR) - 1] == '\\')
     {
@@ -1045,7 +1206,7 @@ CmpLoadHiveThread(IN PVOID StartContext)
         RtlInitUnicodeString(&TempName, CmpMachineHiveList[i].Name);
         RtlAppendStringToString((PSTRING)&RegName, (PSTRING)&TempName);
     }
-    
+
     /* Now add the rest of the file name */
     RtlInitUnicodeString(&TempName, CmpMachineHiveList[i].Name);
     FileName.Length = FileStart;
@@ -1054,7 +1215,7 @@ CmpLoadHiveThread(IN PVOID StartContext)
     {
         /* We need to allocate a new hive structure */
         CmpMachineHiveList[i].Allocate = TRUE;
-        
+
         /* Load the hive file */
         Status = CmpInitHiveFromFile(&FileName,
                                      CmpMachineHiveList[i].HHiveFlags,
@@ -1073,7 +1234,7 @@ CmpLoadHiveThread(IN PVOID StartContext)
                              OptionOk,
                              &ErrorResponse);
         }
-        
+
         /* Set the hive flags and newly allocated hive pointer */
         CmHive->Flags = CmpMachineHiveList[i].CmHiveFlags;
         CmpMachineHiveList[i].CmHive2 = CmHive;
@@ -1105,11 +1266,11 @@ CmpLoadHiveThread(IN PVOID StartContext)
                                  (PULONG_PTR)&ErrorParameters,
                                  OptionOk,
                                  &ErrorResponse);
-                
+
                 /* And bugcheck for posterity's sake */
                 KeBugCheckEx(BAD_SYSTEM_CONFIG_INFO, 9, 0, i, Status);
             }
-            
+
             /* Save the file handles. This should remove our sync hacks */
             CmHive->FileHandles[HFILE_TYPE_LOG] = LogHandle;
             CmHive->FileHandles[HFILE_TYPE_PRIMARY] = PrimaryHandle;
@@ -1120,29 +1281,30 @@ CmpLoadHiveThread(IN PVOID StartContext)
 
             /* Get the real size of the hive */
             Length = CmHive->Hive.Storage[Stable].Length + HBLOCK_SIZE;
-          
+
             /* Check if the cluster size doesn't match */
             if (CmHive->Hive.Cluster != ClusterSize) ASSERT(FALSE);
-            
+
             /* Set the file size */
+            DPRINT("FIXME: Should set file size: %lx\n", Length);
             //if (!CmpFileSetSize((PHHIVE)CmHive, HFILE_TYPE_PRIMARY, Length, Length))
             {
                 /* This shouldn't fail */
                 //ASSERT(FALSE);
             }
-     
+
             /* Another thing we don't support is NTLDR-recovery */
             if (CmHive->Hive.BaseBlock->BootRecover) ASSERT(FALSE);
-            
+
             /* Finally, set our allocated hive to the same hive we've had */
             CmpMachineHiveList[i].CmHive2 = CmHive;
             ASSERT(CmpMachineHiveList[i].CmHive == CmpMachineHiveList[i].CmHive2);
         }
     }
-    
+
     /* We're done */
     CmpMachineHiveList[i].ThreadFinished = TRUE;
-    
+
     /* Check if we're the last worker */
     WorkerCount = InterlockedIncrement(&CmpLoadWorkerIncrement);
     if (WorkerCount == CM_NUMBER_OF_MACHINE_HIVES)
@@ -1163,43 +1325,43 @@ CmpInitializeHiveList(IN USHORT Flag)
     UNICODE_STRING TempName, FileName, RegName;
     HANDLE Thread;
     NTSTATUS Status;
-    ULONG FileStart, RegStart, i;
+    ULONG i;
+    USHORT RegStart;
     PSECURITY_DESCRIPTOR SecurityDescriptor;
     PAGED_CODE();
-    
+
     /* Allow writing for now */
     CmpNoWrite = FALSE;
-    
+
     /* Build the file name and registry name strings */
     RtlInitEmptyUnicodeString(&FileName, FileBuffer, MAX_PATH);
     RtlInitEmptyUnicodeString(&RegName, RegBuffer, MAX_PATH);
-    
+
     /* Now build the system root path */
     CmpGetRegistryPath(ConfigPath);
     RtlInitUnicodeString(&TempName, ConfigPath);
     RtlAppendStringToString((PSTRING)&FileName, (PSTRING)&TempName);
-    FileStart = FileName.Length;
-    
+
     /* And build the registry root path */
     RtlInitUnicodeString(&TempName, L"\\REGISTRY\\");
     RtlAppendStringToString((PSTRING)&RegName, (PSTRING)&TempName);
     RegStart = RegName.Length;
-    
+
     /* Setup the event to synchronize workers */
     KeInitializeEvent(&CmpLoadWorkerEvent, SynchronizationEvent, FALSE);
-    
+
     /* Enter special boot condition */
     CmpSpecialBootCondition = TRUE;
-    
+
     /* Create the SD for the root hives */
-    SecurityDescriptor = CmpHiveRootSecurityDescriptor();      
-    
+    SecurityDescriptor = CmpHiveRootSecurityDescriptor();
+
     /* Loop every hive we care about */
     for (i = 0; i < CM_NUMBER_OF_MACHINE_HIVES; i++)
     {
         /* Make sure the list is setup */
         ASSERT(CmpMachineHiveList[i].Name != NULL);
-        
+
         /* Create a thread to handle this hive */
         Status = PsCreateSystemThread(&Thread,
                                       THREAD_ALL_ACCESS,
@@ -1207,7 +1369,7 @@ CmpInitializeHiveList(IN USHORT Flag)
                                       0,
                                       NULL,
                                       CmpLoadHiveThread,
-                                      (PVOID)i);
+                                      UlongToPtr(i));
         if (NT_SUCCESS(Status))
         {
             /* We don't care about the handle -- the thread self-terminates */
@@ -1219,39 +1381,39 @@ CmpInitializeHiveList(IN USHORT Flag)
             KeBugCheckEx(BAD_SYSTEM_CONFIG_INFO, 9, 3, i, Status);
         }
     }
-    
+
     /* Make sure we've reached the end of the list */
     ASSERT(CmpMachineHiveList[i].Name == NULL);
-    
+
     /* Wait for hive loading to finish */
     KeWaitForSingleObject(&CmpLoadWorkerEvent,
                           Executive,
                           KernelMode,
                           FALSE,
                           NULL);
-    
+
     /* Exit the special boot condition and make sure all workers completed */
     CmpSpecialBootCondition = FALSE;
     ASSERT(CmpLoadWorkerIncrement == CM_NUMBER_OF_MACHINE_HIVES);
-    
+
     /* Loop hives again */
     for (i = 0; i < CM_NUMBER_OF_MACHINE_HIVES; i++)
     {
         /* Make sure the thread ran and finished */
         ASSERT(CmpMachineHiveList[i].ThreadFinished == TRUE);
         ASSERT(CmpMachineHiveList[i].ThreadStarted == TRUE);
-        
+
         /* Check if this was a new hive */
         if (!CmpMachineHiveList[i].CmHive)
         {
             /* Make sure we allocated something */
             ASSERT(CmpMachineHiveList[i].CmHive2 != NULL);
-            
+
             /* Build the base name */
             RegName.Length = RegStart;
             RtlInitUnicodeString(&TempName, CmpMachineHiveList[i].BaseName);
             RtlAppendStringToString((PSTRING)&RegName, (PSTRING)&TempName);
-            
+
             /* Check if this is a child of the root */
             if (RegName.Buffer[RegName.Length / sizeof(WCHAR) - 1] == '\\')
             {
@@ -1259,7 +1421,7 @@ CmpInitializeHiveList(IN USHORT Flag)
                 RtlInitUnicodeString(&TempName, CmpMachineHiveList[i].Name);
                 RtlAppendStringToString((PSTRING)&RegName, (PSTRING)&TempName);
             }
-            
+
             /* Now link the hive to its master */
             Status = CmpLinkHiveToMaster(&RegName,
                                          NULL,
@@ -1271,32 +1433,38 @@ CmpInitializeHiveList(IN USHORT Flag)
                 /* Linking needs to work */
                 KeBugCheckEx(CONFIG_LIST_FAILED, 11, Status, i, (ULONG_PTR)&RegName);
             }
-            
+
             /* Check if we had to allocate a new hive */
-                       if (CmpMachineHiveList[i].Allocate)
+            if (CmpMachineHiveList[i].Allocate)
             {
                 /* Sync the new hive */
-                               //HvSyncHive((PHHIVE)(CmpMachineHiveList[i].CmHive2));
-                       }   
+                //HvSyncHive((PHHIVE)(CmpMachineHiveList[i].CmHive2));
+            }
         }
-        
+
         /* Check if we created a new hive */
         if (CmpMachineHiveList[i].CmHive2)
         {
-            /* TODO: Add to HiveList key */
+            /* Add to HiveList key */
+            CmpAddToHiveFileList(CmpMachineHiveList[i].CmHive2);
         }
     }
-    
+
     /* Get rid of the SD */
-    ExFreePool(SecurityDescriptor);
+    ExFreePoolWithTag(SecurityDescriptor, TAG_CM);
+
+    /* Link SECURITY to SAM */
+    CmpLinkKeyToHive(L"\\Registry\\Machine\\Security\\SAM",
+                     L"\\Registry\\Machine\\SAM\\SAM");
 
-    /* FIXME: Link SECURITY to SAM */
-    
-    /* FIXME: Link S-1-5-18 to .Default */
+    /* Link S-1-5-18 to .Default */
+    CmpLinkKeyToHive(L"\\Registry\\User\\S-1-5-18",
+                     L"\\Registry\\User\\.Default");
 }
 
 BOOLEAN
 NTAPI
+INIT_FUNCTION
 CmInitSystem1(VOID)
 {
     OBJECT_ATTRIBUTES ObjectAttributes;
@@ -1317,8 +1485,8 @@ CmInitSystem1(VOID)
 
     /* Initialize the hive list and lock */
     InitializeListHead(&CmpHiveListHead);
-    ExInitializePushLock((PVOID)&CmpHiveListHeadLock);
-    ExInitializePushLock((PVOID)&CmpLoadHiveLock);
+    ExInitializePushLock(&CmpHiveListHeadLock);
+    ExInitializePushLock(&CmpLoadHiveLock);
 
     /* Initialize registry lock */
     ExInitializeResourceLite(&CmpRegistryLock);
@@ -1346,7 +1514,7 @@ CmInitSystem1(VOID)
     if (!NT_SUCCESS(Status))
     {
         /* Bugcheck */
-        KEBUGCHECKEX(CONFIG_INITIALIZATION_FAILED, 1, 1, Status, 0);
+        KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 1, 1, Status, 0);
     }
 
     /* Build the master hive */
@@ -1363,14 +1531,14 @@ CmInitSystem1(VOID)
     if (!NT_SUCCESS(Status))
     {
         /* Bugcheck */
-        KEBUGCHECKEX(CONFIG_INITIALIZATION_FAILED, 1, 2, Status, 0);
+        KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 1, 2, Status, 0);
     }
 
     /* Create the \REGISTRY key node */
     if (!CmpCreateRegistryRoot())
     {
         /* Bugcheck */
-        KEBUGCHECKEX(CONFIG_INITIALIZATION_FAILED, 1, 3, 0, 0);
+        KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 1, 3, 0, 0);
     }
 
     /* Create the default security descriptor */
@@ -1393,7 +1561,7 @@ CmInitSystem1(VOID)
     if (!NT_SUCCESS(Status))
     {
         /* Bugcheck */
-        KEBUGCHECKEX(CONFIG_INITIALIZATION_FAILED, 1, 5, Status, 0);
+        KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 1, 5, Status, 0);
     }
 
     /* Close the handle */
@@ -1416,7 +1584,7 @@ CmInitSystem1(VOID)
     if (!NT_SUCCESS(Status))
     {
         /* Bugcheck */
-        KEBUGCHECKEX(CONFIG_INITIALIZATION_FAILED, 1, 6, Status, 0);
+        KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 1, 6, Status, 0);
     }
 
     /* Close the handle */
@@ -1426,7 +1594,7 @@ CmInitSystem1(VOID)
     if (!CmpInitializeSystemHive(KeLoaderBlock))
     {
         /* Bugcheck */
-        KEBUGCHECKEX(CONFIG_INITIALIZATION_FAILED, 1, 7, 0, 0);
+        KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 1, 7, 0, 0);
     }
 
     /* Create the 'CurrentControlSet' link. */
@@ -1434,7 +1602,7 @@ CmInitSystem1(VOID)
     if (!NT_SUCCESS(Status))
     {
         /* Bugcheck */
-        KEBUGCHECKEX(CONFIG_INITIALIZATION_FAILED, 1, 8, Status, 0);
+        KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 1, 8, Status, 0);
     }
 
     /* Create the hardware hive */
@@ -1451,9 +1619,9 @@ CmInitSystem1(VOID)
     if (!NT_SUCCESS(Status))
     {
         /* Bugcheck */
-        KEBUGCHECKEX(CONFIG_INITIALIZATION_FAILED, 1, 11, Status, 0);
+        KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 1, 11, Status, 0);
     }
-    
+
     /* Add the hive to the hive list */
     CmpMachineHiveList[0].CmHive = (PCMHIVE)HardwareHive;
 
@@ -1467,20 +1635,21 @@ CmInitSystem1(VOID)
     if (!NT_SUCCESS(Status))
     {
         /* Bugcheck */
-        KEBUGCHECKEX(CONFIG_INITIALIZATION_FAILED, 1, 12, Status, 0);
+        KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 1, 12, Status, 0);
     }
-    
-    /* FIXME: Add to HiveList key */
-    
+
+    /* Add to HiveList key */
+    CmpAddToHiveFileList(HardwareHive);
+
     /* Free the security descriptor */
-    ExFreePool(SecurityDescriptor);
+    ExFreePoolWithTag(SecurityDescriptor, TAG_CM);
 
     /* Fill out the Hardware key with the ARC Data from the Loader */
     Status = CmpInitializeHardwareConfiguration(KeLoaderBlock);
     if (!NT_SUCCESS(Status))
     {
         /* Bugcheck */
-        KEBUGCHECKEX(CONFIG_INITIALIZATION_FAILED, 1, 13, Status, 0);
+        KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 1, 13, Status, 0);
     }
 
     /* Initialize machine-dependent information into the registry */
@@ -1488,7 +1657,7 @@ CmInitSystem1(VOID)
     if (!NT_SUCCESS(Status))
     {
         /* Bugcheck */
-        KEBUGCHECKEX(CONFIG_INITIALIZATION_FAILED, 1, 14, Status, 0);
+        KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 1, 14, Status, 0);
     }
 
     /* Initialize volatile registry settings */
@@ -1496,16 +1665,174 @@ CmInitSystem1(VOID)
     if (!NT_SUCCESS(Status))
     {
         /* Bugcheck */
-        KEBUGCHECKEX(CONFIG_INITIALIZATION_FAILED, 1, 15, Status, 0);
+        KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 1, 15, Status, 0);
     }
 
     /* Free the load options */
-    ExFreePool(CmpLoadOptions.Buffer);
+    ExFreePoolWithTag(CmpLoadOptions.Buffer, TAG_CM);
 
     /* If we got here, all went well */
     return TRUE;
 }
 
+VOID
+NTAPI
+INIT_FUNCTION
+CmpFreeDriverList(IN PHHIVE Hive,
+                  IN PLIST_ENTRY DriverList)
+{
+    PLIST_ENTRY NextEntry, OldEntry;
+    PBOOT_DRIVER_NODE DriverNode;
+    PAGED_CODE();
+
+    /* Parse the current list */
+    NextEntry = DriverList->Flink;
+    while (NextEntry != DriverList)
+    {
+        /* Get the driver node */
+        DriverNode = CONTAINING_RECORD(NextEntry, BOOT_DRIVER_NODE, ListEntry.Link);
+
+        /* Get the next entry now, since we're going to free it later */
+        OldEntry = NextEntry;
+        NextEntry = NextEntry->Flink;
+
+        /* Was there a name? */
+        if (DriverNode->Name.Buffer)
+        {
+            /* Free it */
+            CmpFree(DriverNode->Name.Buffer, DriverNode->Name.Length);
+        }
+
+        /* Was there a registry path? */
+        if (DriverNode->ListEntry.RegistryPath.Buffer)
+        {
+            /* Free it */
+            CmpFree(DriverNode->ListEntry.RegistryPath.Buffer,
+                    DriverNode->ListEntry.RegistryPath.MaximumLength);
+        }
+
+        /* Was there a file path? */
+        if (DriverNode->ListEntry.FilePath.Buffer)
+        {
+            /* Free it */
+            CmpFree(DriverNode->ListEntry.FilePath.Buffer,
+                    DriverNode->ListEntry.FilePath.MaximumLength);
+        }
+
+        /* Now free the node, and move on */
+        CmpFree(OldEntry, sizeof(BOOT_DRIVER_NODE));
+    }
+}
+
+PUNICODE_STRING*
+NTAPI
+INIT_FUNCTION
+CmGetSystemDriverList(VOID)
+{
+    LIST_ENTRY DriverList;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    NTSTATUS Status;
+    PCM_KEY_BODY KeyBody;
+    PHHIVE Hive;
+    HCELL_INDEX RootCell, ControlCell;
+    HANDLE KeyHandle;
+    UNICODE_STRING KeyName;
+    PLIST_ENTRY NextEntry;
+    ULONG i;
+    PUNICODE_STRING* ServicePath = NULL;
+    BOOLEAN Success, AutoSelect;
+    PBOOT_DRIVER_LIST_ENTRY DriverEntry;
+    PAGED_CODE();
+
+    /* Initialize the driver list */
+    InitializeListHead(&DriverList);
+
+    /* Open the system hive key */
+    RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\System");
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &KeyName,
+                               OBJ_CASE_INSENSITIVE,
+                               NULL,
+                               NULL);
+    Status = NtOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
+    if (!NT_SUCCESS(Status)) return NULL;
+
+    /* Reference the key object to get the root hive/cell to access directly */
+    Status = ObReferenceObjectByHandle(KeyHandle,
+                                       KEY_QUERY_VALUE,
+                                       CmpKeyObjectType,
+                                       KernelMode,
+                                       (PVOID*)&KeyBody,
+                                       NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        /* Fail */
+        NtClose(KeyHandle);
+        return NULL;
+    }
+
+    /* Do all this under the registry lock */
+    CmpLockRegistryExclusive();
+
+    /* Get the hive and key cell */
+    Hive = KeyBody->KeyControlBlock->KeyHive;
+    RootCell = KeyBody->KeyControlBlock->KeyCell;
+
+    /* Open the current control set key */
+    RtlInitUnicodeString(&KeyName, L"Current");
+    ControlCell = CmpFindControlSet(Hive, RootCell, &KeyName, &AutoSelect);
+    if (ControlCell == HCELL_NIL) goto EndPath;
+
+    /* Find all system drivers */
+    Success = CmpFindDrivers(Hive, ControlCell, SystemLoad, NULL, &DriverList);
+    if (!Success) goto EndPath;
+
+    /* Sort by group/tag */
+    if (!CmpSortDriverList(Hive, ControlCell, &DriverList)) goto EndPath;
+
+    /* Remove circular dependencies (cycles) and sort */
+    if (!CmpResolveDriverDependencies(&DriverList)) goto EndPath;
+
+    /* Loop the list to count drivers */
+    for (i = 0, NextEntry = DriverList.Flink;
+         NextEntry != &DriverList;
+         i++, NextEntry = NextEntry->Flink);
+
+    /* Allocate the array */
+    ServicePath = ExAllocatePool(NonPagedPool, (i + 1) * sizeof(PUNICODE_STRING));
+    if (!ServicePath) KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 2, 1, 0, 0);
+
+    /* Loop the driver list */
+    for (i = 0, NextEntry = DriverList.Flink;
+         NextEntry != &DriverList;
+         i++, NextEntry = NextEntry->Flink)
+    {
+        /* Get the entry */
+        DriverEntry = CONTAINING_RECORD(NextEntry, BOOT_DRIVER_LIST_ENTRY, Link);
+
+        /* Allocate the path for the caller and duplicate the registry path */
+        ServicePath[i] = ExAllocatePool(NonPagedPool, sizeof(UNICODE_STRING));
+        RtlDuplicateUnicodeString(RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
+                                  &DriverEntry->RegistryPath,
+                                  ServicePath[i]);
+    }
+
+    /* Terminate the list */
+    ServicePath[i] = NULL;
+
+EndPath:
+    /* Free the driver list if we had one */
+    if (!IsListEmpty(&DriverList)) CmpFreeDriverList(Hive, &DriverList);
+
+    /* Unlock the registry */
+    CmpUnlockRegistry();
+
+    /* Close the key handle and dereference the object, then return the path */
+    ObDereferenceObject(KeyBody);
+    NtClose(KeyHandle);
+    return ServicePath;
+}
+
 VOID
 NTAPI
 CmpLockRegistryExclusive(VOID)
@@ -1513,7 +1840,7 @@ CmpLockRegistryExclusive(VOID)
     /* Enter a critical region and lock the registry */
     KeEnterCriticalRegion();
     ExAcquireResourceExclusiveLite(&CmpRegistryLock, TRUE);
-    
+
     /* Sanity check */
     ASSERT(CmpFlushStarveWriters == 0);
     RtlGetCallersAddress(&CmpRegistryLockCaller, &CmpRegistryLockCallerCaller);
@@ -1525,7 +1852,7 @@ CmpLockRegistry(VOID)
 {
     /* Enter a critical region */
     KeEnterCriticalRegion();
-    
+
     /* Check if we have to starve writers */
     if (CmpFlushStarveWriters)
     {
@@ -1555,24 +1882,79 @@ CmpTestRegistryLockExclusive(VOID)
     return !ExIsResourceAcquiredExclusiveLite(&CmpRegistryLock) ? FALSE : TRUE;
 }
 
+VOID
+NTAPI
+CmpLockHiveFlusherExclusive(IN PCMHIVE Hive)
+{
+    /* Lock the flusher. We should already be in a critical section */
+    CMP_ASSERT_REGISTRY_LOCK_OR_LOADING(Hive);
+    ASSERT((ExIsResourceAcquiredShared(Hive->FlusherLock) == 0) &&
+           (ExIsResourceAcquiredExclusiveLite(Hive->FlusherLock) == 0));
+    ExAcquireResourceExclusiveLite(Hive->FlusherLock, TRUE);
+}
+
+VOID
+NTAPI
+CmpLockHiveFlusherShared(IN PCMHIVE Hive)
+{
+    /* Lock the flusher. We should already be in a critical section */
+    CMP_ASSERT_REGISTRY_LOCK_OR_LOADING(Hive);
+    ASSERT((ExIsResourceAcquiredShared(Hive->FlusherLock) == 0) &&
+           (ExIsResourceAcquiredExclusiveLite(Hive->FlusherLock) == 0));
+    ExAcquireResourceSharedLite(Hive->FlusherLock, TRUE);
+}
+
+VOID
+NTAPI
+CmpUnlockHiveFlusher(IN PCMHIVE Hive)
+{
+    /* Sanity check */
+    CMP_ASSERT_REGISTRY_LOCK_OR_LOADING(Hive);
+    CMP_ASSERT_FLUSH_LOCK(Hive);
+
+    /* Release the lock */
+    ExReleaseResourceLite(Hive->FlusherLock);
+}
+
+BOOLEAN
+NTAPI
+CmpTestHiveFlusherLockShared(IN PCMHIVE Hive)
+{
+    /* Test the lock */
+    return !ExIsResourceAcquiredSharedLite(Hive->FlusherLock) ? FALSE : TRUE;
+}
+
+BOOLEAN
+NTAPI
+CmpTestHiveFlusherLockExclusive(IN PCMHIVE Hive)
+{
+    /* Test the lock */
+    return !ExIsResourceAcquiredExclusiveLite(Hive->FlusherLock) ? FALSE : TRUE;
+}
+
 VOID
 NTAPI
 CmpUnlockRegistry(VOID)
 {
     /* Sanity check */
     CMP_ASSERT_REGISTRY_LOCK();
-    
+
     /* Check if we should flush the registry */
     if (CmpFlushOnLockRelease)
     {
         /* The registry should be exclusively locked for this */
         CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK();
-        
+
         /* Flush the registry */
         CmpDoFlushAll(TRUE);
         CmpFlushOnLockRelease = FALSE;
     }
-    
+    else
+    {
+        /* Lazy flush the registry */
+        CmpLazyFlush();
+    }
+
     /* Release the lock and leave the critical region */
     ExReleaseResourceLite(&CmpRegistryLock);
     KeLeaveCriticalRegion();
@@ -1584,7 +1966,7 @@ CmpAcquireTwoKcbLocksExclusiveByKey(IN ULONG ConvKey1,
                                     IN ULONG ConvKey2)
 {
     ULONG Index1, Index2;
-    
+
     /* Sanity check */
     CMP_ASSERT_REGISTRY_LOCK();
 
@@ -1603,7 +1985,7 @@ CmpAcquireTwoKcbLocksExclusiveByKey(IN ULONG ConvKey1,
     {
         /* Grab the second one first, then the first */
         CmpAcquireKcbLockExclusiveByKey(ConvKey2);
-        if (Index1 != Index2) CmpAcquireKcbLockExclusiveByKey(ConvKey1);        
+        if (Index1 != Index2) CmpAcquireKcbLockExclusiveByKey(ConvKey1);
     }
 }
 
@@ -1613,16 +1995,16 @@ CmpReleaseTwoKcbLockByKey(IN ULONG ConvKey1,
                           IN ULONG ConvKey2)
 {
     ULONG Index1, Index2;
-    
+
     /* Sanity check */
     CMP_ASSERT_REGISTRY_LOCK();
-    
+
     /* Get hash indexes */
     Index1 = GET_HASH_INDEX(ConvKey1);
     Index2 = GET_HASH_INDEX(ConvKey2);
     ASSERT((GET_HASH_ENTRY(CmpCacheTable, ConvKey2).Owner == KeGetCurrentThread()) ||
            (CmpTestRegistryLockExclusive()));
-    
+
     /* See which one is highest */
     if (Index1 < Index2)
     {
@@ -1649,7 +2031,163 @@ VOID
 NTAPI
 CmShutdownSystem(VOID)
 {
-    /* Kill the workers and flush all hives */
+    /* Kill the workers */
     if (!CmFirstTime) CmpShutdownWorkers();
+
+    /* Flush all hives */
+    CmpLockRegistryExclusive();
     CmpDoFlushAll(TRUE);
+    CmpUnlockRegistry();
 }
+
+VOID
+NTAPI
+CmpSetVersionData(VOID)
+{
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    UNICODE_STRING KeyName;
+    UNICODE_STRING ValueName;
+    UNICODE_STRING ValueData;
+    HANDLE SoftwareKeyHandle = NULL;
+    HANDLE MicrosoftKeyHandle = NULL;
+    HANDLE WindowsNtKeyHandle = NULL;
+    HANDLE CurrentVersionKeyHandle = NULL;
+    WCHAR Buffer[128];
+    NTSTATUS Status;
+
+    /* Open the 'CurrentVersion' key */
+    RtlInitUnicodeString(&KeyName,
+                         L"\\REGISTRY\\MACHINE\\SOFTWARE");
+
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &KeyName,
+                               OBJ_CASE_INSENSITIVE,
+                               NULL,
+                               NULL);
+
+    Status = NtCreateKey(&SoftwareKeyHandle,
+                         KEY_CREATE_SUB_KEY,
+                         &ObjectAttributes,
+                         0,
+                         NULL,
+                         0,
+                         NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to create key %wZ (Status: %08lx)\n", &KeyName, Status);
+        return;
+    }
+
+    /* Open the 'CurrentVersion' key */
+    RtlInitUnicodeString(&KeyName,
+                         L"Microsoft");
+
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &KeyName,
+                               OBJ_CASE_INSENSITIVE,
+                               SoftwareKeyHandle,
+                               NULL);
+
+    Status = NtCreateKey(&MicrosoftKeyHandle,
+                         KEY_CREATE_SUB_KEY,
+                         &ObjectAttributes,
+                         0,
+                         NULL,
+                         0,
+                         NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to create key %wZ (Status: %08lx)\n", &KeyName, Status);
+        goto done;
+    }
+
+    /* Open the 'CurrentVersion' key */
+    RtlInitUnicodeString(&KeyName,
+                         L"Windows NT");
+
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &KeyName,
+                               OBJ_CASE_INSENSITIVE,
+                               MicrosoftKeyHandle,
+                               NULL);
+
+    Status = NtCreateKey(&WindowsNtKeyHandle,
+                         KEY_CREATE_SUB_KEY,
+                         &ObjectAttributes,
+                         0,
+                         NULL,
+                         0,
+                         NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to create key %wZ (Status: %08lx)\n", &KeyName, Status);
+        goto done;
+    }
+
+    /* Open the 'CurrentVersion' key */
+    RtlInitUnicodeString(&KeyName,
+                         L"CurrentVersion");
+
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &KeyName,
+                               OBJ_CASE_INSENSITIVE,
+                               WindowsNtKeyHandle,
+                               NULL);
+
+    Status = NtCreateKey(&CurrentVersionKeyHandle,
+                         KEY_CREATE_SUB_KEY | KEY_SET_VALUE,
+                         &ObjectAttributes,
+                         0,
+                         NULL,
+                         0,
+                         NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to create key %wZ (Status: %08lx)\n", &KeyName, Status);
+        goto done;
+    }
+
+    /* Set the 'CurrentType' value */
+    RtlInitUnicodeString(&ValueName,
+                         L"CurrentType");
+
+#ifdef CONFIG_SMP
+    wcscpy(Buffer, L"Multiprocessor");
+#else
+    wcscpy(Buffer, L"Uniprocessor");
+#endif
+
+    wcscat(Buffer, L" ");
+
+#if (DBG == 1)
+    wcscat(Buffer, L"Checked");
+#else
+    wcscat(Buffer, L"Free");
+#endif
+
+    RtlInitUnicodeString(&ValueData,
+                         Buffer);
+
+    NtSetValueKey(CurrentVersionKeyHandle,
+                  &ValueName,
+                  0,
+                  REG_SZ,
+                  ValueData.Buffer,
+                  ValueData.Length + sizeof(WCHAR));
+
+done:;
+    /* Close the keys */
+    if (CurrentVersionKeyHandle != NULL)
+        NtClose(CurrentVersionKeyHandle);
+
+    if (WindowsNtKeyHandle != NULL)
+        NtClose(WindowsNtKeyHandle);
+
+    if (MicrosoftKeyHandle != NULL)
+        NtClose(MicrosoftKeyHandle);
+
+    if (SoftwareKeyHandle != NULL)
+        NtClose(SoftwareKeyHandle);
+}
+
+/* EOF */