[USETUP] Moving around some code.
[reactos.git] / base / setup / usetup / registry.c
index 0ae7ab7..749835c 100644 (file)
 /*
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS text-mode setup
- * FILE:            subsys/system/usetup/registry.c
+ * FILE:            base/setup/usetup/registry.c
  * PURPOSE:         Registry creation functions
- * PROGRAMMER:      Eric Kohl
+ * PROGRAMMER:
  */
 
 /* INCLUDES *****************************************************************/
 
 #include "usetup.h"
 
+// HACK!
+#include <strsafe.h>
+
 #define NDEBUG
 #include <debug.h>
 
-#ifdef __REACTOS__
 #define FLG_ADDREG_BINVALUETYPE           0x00000001
 #define FLG_ADDREG_NOCLOBBER              0x00000002
 #define FLG_ADDREG_DELVAL                 0x00000004
@@ -45,7 +47,6 @@
 #define FLG_ADDREG_TYPE_DWORD            (0x00010000 | FLG_ADDREG_BINVALUETYPE)
 #define FLG_ADDREG_TYPE_NONE             (0x00020000 | FLG_ADDREG_BINVALUETYPE)
 #define FLG_ADDREG_TYPE_MASK             (0xFFFF0000 | FLG_ADDREG_BINVALUETYPE)
-#endif
 
 #ifdef _M_IX86
 #define Architecture L"x86"
 #define Architecture L"ppc"
 #endif
 
-#include <pshpack1.h>
+/* FUNCTIONS ****************************************************************/
+
+#define REGISTRY_SETUP_MACHINE  L"\\Registry\\Machine\\SYSTEM\\USetup_Machine\\"
+#define REGISTRY_SETUP_USER     L"\\Registry\\Machine\\SYSTEM\\USetup_User\\"
+
+typedef struct _ROOT_KEY
+{
+    PCWSTR Name;
+    PCWSTR MountPoint;
+    HANDLE Handle;
+} ROOT_KEY, *PROOT_KEY;
 
-typedef struct _REG_DISK_MOUNT_INFO
+ROOT_KEY RootKeys[] =
 {
-  ULONG Signature;
-  LARGE_INTEGER StartingOffset;
-} REG_DISK_MOUNT_INFO, *PREG_DISK_MOUNT_INFO;
+    { L"HKCR", REGISTRY_SETUP_MACHINE L"SOFTWARE\\Classes\\", NULL },   /* "\\Registry\\Machine\\SOFTWARE\\Classes\\" */ // HKEY_CLASSES_ROOT
+    { L"HKCU", REGISTRY_SETUP_USER    L".DEFAULT\\"         , NULL },   /* "\\Registry\\User\\.DEFAULT\\" */             // HKEY_CURRENT_USER
+    { L"HKLM", REGISTRY_SETUP_MACHINE                       , NULL },   /* "\\Registry\\Machine\\"        */             // HKEY_LOCAL_MACHINE
+    { L"HKU" , REGISTRY_SETUP_USER                          , NULL },   /* "\\Registry\\User\\"           */             // HKEY_USERS
+#if 0
+    { L"HKR", NULL, NULL },
+#endif
+};
 
-#include <poppack.h>
+#define IsPredefKey(HKey)       \
+    (((ULONG_PTR)(HKey) & 0xF0000000) == 0x80000000)
 
-/* FUNCTIONS ****************************************************************/
+#define GetPredefKeyIndex(HKey) \
+    ((ULONG_PTR)(HKey) & 0x0FFFFFFF)
 
-static BOOLEAN
-GetRootKey (PWCHAR Name)
+HANDLE
+GetRootKeyByPredefKey(
+    IN HANDLE KeyHandle,
+    OUT PCWSTR* RootKeyMountPoint OPTIONAL)
 {
-  if (!_wcsicmp (Name, L"HKCR"))
-    {
-      wcscpy (Name, L"\\Registry\\Machine\\SOFTWARE\\Classes\\");
-      return TRUE;
-    }
+    ULONG_PTR Index = GetPredefKeyIndex(KeyHandle);
 
-  if (!_wcsicmp (Name, L"HKCU"))
-    {
-      wcscpy (Name, L"\\Registry\\User\\.DEFAULT\\");
-      return TRUE;
-    }
+    if (!IsPredefKey(KeyHandle))
+        return NULL;
+    if (GetPredefKeyIndex(KeyHandle) >= ARRAYSIZE(RootKeys))
+        return NULL;
 
-  if (!_wcsicmp (Name, L"HKLM"))
-    {
-      wcscpy (Name, L"\\Registry\\Machine\\");
-      return TRUE;
-    }
+    if (RootKeyMountPoint)
+        *RootKeyMountPoint = RootKeys[Index].MountPoint;
+    return RootKeys[Index].Handle;
+}
+
+HANDLE
+GetRootKeyByName(
+    IN PCWSTR RootKeyName,
+    OUT PCWSTR* RootKeyMountPoint OPTIONAL)
+{
+    UCHAR i;
 
-  if (!_wcsicmp (Name, L"HKU"))
+    for (i = 0; i < ARRAYSIZE(RootKeys); ++i)
     {
-      wcscpy (Name, L"\\Registry\\User\\");
-      return TRUE;
+        if (!_wcsicmp(RootKeyName, RootKeys[i].Name))
+        {
+            if (RootKeyMountPoint)
+                *RootKeyMountPoint = RootKeys[i].MountPoint;
+            return RootKeys[i].Handle;
+        }
     }
 
-#if 0
-  if (!_wcsicmp (Name, L"HKR"))
-    return FALSE;
-#endif
-
-  return FALSE;
+    return NULL;
 }
 
 
@@ -112,6 +132,7 @@ GetRootKey (PWCHAR Name)
  *
  * Append a multisz string to a multisz registry value.
  */
+// NOTE: Synced with setupapi/install.c ; see also mkhive/reginf.c
 #if 0
 static void
 append_multi_sz_value (HANDLE hkey,
@@ -125,7 +146,7 @@ append_multi_sz_value (HANDLE hkey,
     if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
     if (type != REG_MULTI_SZ) return;
 
-    if (!(buffer = HeapAlloc( GetProcessHeap(), 0, (size + str_size) * sizeof(WCHAR) ))) return;
+    if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size + str_size * sizeof(WCHAR) ))) return;
     if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
 
     /* compare each string against all the existing ones */
@@ -206,7 +227,7 @@ do_reg_operation(HANDLE KeyHandle,
                  PINFCONTEXT Context,
                  ULONG Flags)
 {
-  WCHAR EmptyStr = (WCHAR)0;
+  WCHAR EmptyStr = 0;
   ULONG Type;
   ULONG Size;
 
@@ -231,7 +252,7 @@ do_reg_operation(HANDLE KeyHandle,
 #if 0
   if (Flags & (FLG_ADDREG_NOCLOBBER | FLG_ADDREG_OVERWRITEONLY))
     {
-      BOOL exists = !RegQueryValueExW( hkey, value, NULL, NULL, NULL, NULL );
+      BOOL exists = !RegQueryValueExW( hkey, ValueName, NULL, NULL, NULL, NULL );
       if (exists && (flags & FLG_ADDREG_NOCLOBBER))
         return TRUE;
       if (!exists & (flags & FLG_ADDREG_OVERWRITEONLY))
@@ -282,7 +303,7 @@ do_reg_operation(HANDLE KeyHandle,
 
           if (Size)
             {
-              Str = (WCHAR*) RtlAllocateHeap (ProcessHeap, 0, Size * sizeof(WCHAR));
+              Str = (WCHAR*) RtlAllocateHeap(ProcessHeap, 0, Size * sizeof(WCHAR));
               if (Str == NULL)
                 return FALSE;
 
@@ -294,6 +315,7 @@ do_reg_operation(HANDLE KeyHandle,
               if (Str == NULL)
                 return TRUE;
 
+              DPRINT1("append_multi_sz_value '%S' commented out, WHY??\n", ValueName);
 //            append_multi_sz_value( hkey, value, str, size );
 
               RtlFreeHeap (ProcessHeap, 0, Str);
@@ -303,16 +325,16 @@ do_reg_operation(HANDLE KeyHandle,
         }
       else
         {
-          if (!SetupGetStringFieldW (Context, 5, NULL, 0, &Size))
+          if (!SetupGetStringFieldW(Context, 5, NULL, 0, &Size))
             Size = 0;
 
           if (Size)
             {
-              Str = (WCHAR*) RtlAllocateHeap (ProcessHeap, 0, Size * sizeof(WCHAR));
+              Str = (WCHAR*)RtlAllocateHeap(ProcessHeap, 0, Size * sizeof(WCHAR));
               if (Str == NULL)
                 return FALSE;
 
-              SetupGetStringFieldW (Context, 5, Str, Size, NULL);
+              SetupGetStringFieldW(Context, 5, Str, Size, NULL);
             }
         }
 
@@ -322,16 +344,12 @@ do_reg_operation(HANDLE KeyHandle,
 
           DPRINT("setting dword %wZ to %lx\n", ValueName, dw);
 
-#ifdef __REACTOS__
           NtSetValueKey (KeyHandle,
                          ValueName,
                          0,
                          Type,
                          (PVOID)&dw,
                          sizeof(ULONG));
-#else
-          RegSetValueExW(KeyHandle, ValueName, 0, Type, (const UCHAR*)&dw, sizeof(ULONG));
-#endif
         }
       else
         {
@@ -339,29 +357,21 @@ do_reg_operation(HANDLE KeyHandle,
 
           if (Str)
             {
-#ifdef __REACTOS__
               NtSetValueKey (KeyHandle,
                              ValueName,
                              0,
                              Type,
                              (PVOID)Str,
                              Size * sizeof(WCHAR));
-#else
-              RegSetValueExW(KeyHandle, ValueName, 0, Type, (const UCHAR*)Str, Size * sizeof(WCHAR));
-#endif
             }
           else
             {
-#ifdef __REACTOS__
               NtSetValueKey (KeyHandle,
                              ValueName,
                              0,
                              Type,
                              (PVOID)&EmptyStr,
                              sizeof(WCHAR));
-#else
-              RegSetValueExW(KeyHandle, ValueName, 0, Type, (const UCHAR*)&EmptyStr, sizeof(WCHAR));
-#endif
             }
         }
       RtlFreeHeap (ProcessHeap, 0, Str);
@@ -375,7 +385,7 @@ do_reg_operation(HANDLE KeyHandle,
 
       if (Size)
         {
-          Data = (unsigned char*) RtlAllocateHeap (ProcessHeap, 0, Size);
+          Data = (unsigned char*) RtlAllocateHeap(ProcessHeap, 0, Size);
           if (Data == NULL)
             return FALSE;
 
@@ -383,16 +393,12 @@ do_reg_operation(HANDLE KeyHandle,
           SetupGetBinaryField (Context, 5, Data, Size, NULL);
         }
 
-#ifdef __REACTOS__
       NtSetValueKey (KeyHandle,
                      ValueName,
                      0,
                      Type,
                      (PVOID)Data,
                      Size);
-#else
-      RegSetValueExW(KeyHandle, ValueName, 0, Type, (const UCHAR*)Data, Size);
-#endif
 
       RtlFreeHeap (ProcessHeap, 0, Data);
     }
@@ -400,99 +406,114 @@ do_reg_operation(HANDLE KeyHandle,
   return TRUE;
 }
 
-#ifdef __REACTOS__
+/*
+ * This function is similar to the one in dlls/win32/advapi32/reg/reg.c
+ * TODO: I should review both of them very carefully, because they may need
+ * some adjustments in their NtCreateKey calls, especially for CreateOptions
+ * stuff etc...
+ */
 NTSTATUS
-CreateNestedKey (PHANDLE KeyHandle,
-                 ACCESS_MASK DesiredAccess,
-                 POBJECT_ATTRIBUTES ObjectAttributes)
+CreateNestedKey(PHANDLE KeyHandle,
+                ACCESS_MASK DesiredAccess,
+                POBJECT_ATTRIBUTES ObjectAttributes,
+                ULONG CreateOptions)
 {
-  OBJECT_ATTRIBUTES LocalObjectAttributes;
-  UNICODE_STRING LocalKeyName;
-  ULONG Disposition;
-  NTSTATUS Status;
-  USHORT FullNameLength;
-  PWCHAR Ptr;
-  HANDLE LocalKeyHandle;
-
-  Status = NtCreateKey (KeyHandle,
-                        KEY_ALL_ACCESS,
-                        ObjectAttributes,
-                        0,
-                        NULL,
-                        0,
-                        &Disposition);
-  DPRINT("NtCreateKey(%wZ) called (Status %lx)\n", ObjectAttributes->ObjectName, Status);
-  if (Status != STATUS_OBJECT_NAME_NOT_FOUND)
-    return Status;
+    OBJECT_ATTRIBUTES LocalObjectAttributes;
+    UNICODE_STRING LocalKeyName;
+    ULONG Disposition;
+    NTSTATUS Status;
+    USHORT FullNameLength;
+    PWCHAR Ptr;
+    HANDLE LocalKeyHandle;
+
+    Status = NtCreateKey(KeyHandle,
+                         KEY_ALL_ACCESS,
+                         ObjectAttributes,
+                         0,
+                         NULL,
+                         CreateOptions,
+                         &Disposition);
+    DPRINT("NtCreateKey(%wZ) called (Status %lx)\n", ObjectAttributes->ObjectName, Status);
+    if (Status != STATUS_OBJECT_NAME_NOT_FOUND)
+    {
+        if (!NT_SUCCESS(Status))
+            DPRINT1("CreateNestedKey: NtCreateKey(%wZ) failed (Status %lx)\n", ObjectAttributes->ObjectName, Status);
 
-  /* Copy object attributes */
-  RtlCopyMemory (&LocalObjectAttributes,
-                 ObjectAttributes,
-                 sizeof(OBJECT_ATTRIBUTES));
-  RtlCreateUnicodeString (&LocalKeyName,
-                          ObjectAttributes->ObjectName->Buffer);
-  LocalObjectAttributes.ObjectName = &LocalKeyName;
-  FullNameLength = LocalKeyName.Length;
+        return Status;
+    }
 
-  /* Remove the last part of the key name and try to create the key again. */
-  while (Status == STATUS_OBJECT_NAME_NOT_FOUND)
+    /* Copy object attributes */
+    RtlCopyMemory(&LocalObjectAttributes,
+                  ObjectAttributes,
+                  sizeof(OBJECT_ATTRIBUTES));
+    RtlCreateUnicodeString(&LocalKeyName,
+                           ObjectAttributes->ObjectName->Buffer);
+    LocalObjectAttributes.ObjectName = &LocalKeyName;
+    FullNameLength = LocalKeyName.Length;
+
+    /* Remove the last part of the key name and try to create the key again. */
+    while (Status == STATUS_OBJECT_NAME_NOT_FOUND)
     {
-      Ptr = wcsrchr (LocalKeyName.Buffer, '\\');
-      if (Ptr == NULL || Ptr == LocalKeyName.Buffer)
+        Ptr = wcsrchr(LocalKeyName.Buffer, '\\');
+        if (Ptr == NULL || Ptr == LocalKeyName.Buffer)
         {
-          Status = STATUS_UNSUCCESSFUL;
-          break;
+            Status = STATUS_UNSUCCESSFUL;
+            break;
         }
-      *Ptr = (WCHAR)0;
-      LocalKeyName.Length = wcslen (LocalKeyName.Buffer) * sizeof(WCHAR);
+        *Ptr = (WCHAR)0;
+        LocalKeyName.Length = wcslen(LocalKeyName.Buffer) * sizeof(WCHAR);
 
-      Status = NtCreateKey (&LocalKeyHandle,
-                            KEY_ALL_ACCESS,
-                            &LocalObjectAttributes,
-                            0,
-                            NULL,
-                            0,
-                            &Disposition);
-      DPRINT("NtCreateKey(%wZ) called (Status %lx)\n", &LocalKeyName, Status);
+        Status = NtCreateKey(&LocalKeyHandle,
+                             KEY_CREATE_SUB_KEY,
+                             &LocalObjectAttributes,
+                             0,
+                             NULL,
+                             REG_OPTION_NON_VOLATILE,
+                             &Disposition);
+        DPRINT("NtCreateKey(%wZ) called (Status %lx)\n", &LocalKeyName, Status);
+        if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
+            DPRINT1("CreateNestedKey: NtCreateKey(%wZ) failed (Status %lx)\n", LocalObjectAttributes.ObjectName, Status);
     }
 
-  if (!NT_SUCCESS(Status))
+    if (!NT_SUCCESS(Status))
     {
-      RtlFreeUnicodeString (&LocalKeyName);
-      return Status;
+        RtlFreeUnicodeString(&LocalKeyName);
+        return Status;
     }
 
-  /* Add removed parts of the key name and create them too. */
-  while (TRUE)
+    /* Add removed parts of the key name and create them too. */
+    while (TRUE)
     {
-      if (LocalKeyName.Length == FullNameLength)
+        if (LocalKeyName.Length == FullNameLength)
         {
-          Status = STATUS_SUCCESS;
-          *KeyHandle = LocalKeyHandle;
-          break;
+            Status = STATUS_SUCCESS;
+            *KeyHandle = LocalKeyHandle;
+            break;
+        }
+        NtClose(LocalKeyHandle);
+
+        LocalKeyName.Buffer[LocalKeyName.Length / sizeof(WCHAR)] = L'\\';
+        LocalKeyName.Length = wcslen(LocalKeyName.Buffer) * sizeof(WCHAR);
+
+        Status = NtCreateKey(&LocalKeyHandle,
+                             KEY_ALL_ACCESS,
+                             &LocalObjectAttributes,
+                             0,
+                             NULL,
+                             CreateOptions,
+                             &Disposition);
+        DPRINT("NtCreateKey(%wZ) called (Status %lx)\n", &LocalKeyName, Status);
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT1("CreateNestedKey: NtCreateKey(%wZ) failed (Status %lx)\n", LocalObjectAttributes.ObjectName, Status);
+            break;
         }
-      NtClose (LocalKeyHandle);
-
-      LocalKeyName.Buffer[LocalKeyName.Length / sizeof(WCHAR)] = L'\\';
-      LocalKeyName.Length = wcslen (LocalKeyName.Buffer) * sizeof(WCHAR);
-
-      Status = NtCreateKey (&LocalKeyHandle,
-                            KEY_ALL_ACCESS,
-                            &LocalObjectAttributes,
-                            0,
-                            NULL,
-                            0,
-                            &Disposition);
-      DPRINT("NtCreateKey(%wZ) called (Status %lx)\n", &LocalKeyName, Status);
-      if (!NT_SUCCESS(Status))
-        break;
     }
 
-  RtlFreeUnicodeString (&LocalKeyName);
+    RtlFreeUnicodeString(&LocalKeyName);
 
-  return Status;
+    return Status;
 }
-#endif
 
 /***********************************************************************
  *            registry_callback
@@ -500,267 +521,1023 @@ CreateNestedKey (PHANDLE KeyHandle,
  * Called once for each AddReg and DelReg entry in a given section.
  */
 static BOOLEAN
-registry_callback (HINF hInf, PCWSTR Section, BOOLEAN Delete)
+registry_callback(HINF hInf, PCWSTR Section, BOOLEAN Delete)
 {
-  OBJECT_ATTRIBUTES ObjectAttributes;
-  WCHAR Buffer[MAX_INF_STRING_LENGTH];
-  UNICODE_STRING Name;
-  UNICODE_STRING Value;
-  PUNICODE_STRING ValuePtr;
-  NTSTATUS Status;
-  UINT Flags;
-  ULONG Length;
-
-  INFCONTEXT Context;
-  HANDLE KeyHandle;
-  BOOLEAN Ok;
-
-
-  Ok = SetupFindFirstLineW (hInf, Section, NULL, &Context);
-
-  if (Ok)
+    NTSTATUS Status;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    UNICODE_STRING Name, Value;
+    PUNICODE_STRING ValuePtr;
+    UINT Flags;
+    WCHAR Buffer[MAX_INF_STRING_LENGTH];
+
+    INFCONTEXT Context;
+    PCWSTR RootKeyName;
+    HANDLE RootKeyHandle, KeyHandle;
+    BOOLEAN Ok;
+
+    Ok = SetupFindFirstLineW(hInf, Section, NULL, &Context);
+    if (!Ok)
+        return TRUE; /* Don't fail if the section isn't present */
+
+    for (;Ok; Ok = SetupFindNextLine (&Context, &Context))
     {
-      for (;Ok; Ok = SetupFindNextLine (&Context, &Context))
-        {
-          /* get root */
-          if (!SetupGetStringFieldW (&Context, 1, Buffer, MAX_INF_STRING_LENGTH, NULL))
-              continue;
-          if (!GetRootKey (Buffer))
+        /* get root */
+        if (!SetupGetStringFieldW(&Context, 1, Buffer, sizeof(Buffer)/sizeof(WCHAR), NULL))
+            continue;
+        RootKeyHandle = GetRootKeyByName(Buffer, &RootKeyName);
+        if (!RootKeyHandle)
             continue;
 
-          /* get key */
-          Length = wcslen (Buffer);
-          if (!SetupGetStringFieldW (&Context, 2, Buffer + Length, MAX_INF_STRING_LENGTH - Length, NULL))
+        /* get key */
+        if (!SetupGetStringFieldW(&Context, 2, Buffer, sizeof(Buffer)/sizeof(WCHAR), NULL))
             *Buffer = 0;
 
-          DPRINT("KeyName: <%S>\n", Buffer);
+        DPRINT("KeyName: <%S\\%S>\n", RootKeyName, Buffer);
 
-          /* get flags */
-          if (!SetupGetIntField (&Context, 4, (PINT)&Flags))
+        /* get flags */
+        if (!SetupGetIntField(&Context, 4, (PINT)&Flags))
             Flags = 0;
 
-          DPRINT("Flags: %lx\n", Flags);
+        DPRINT("Flags: %lx\n", Flags);
 
-#ifdef __REACTOS__
-          RtlInitUnicodeString (&Name,
-                                Buffer);
+        RtlInitUnicodeString(&Name, Buffer);
+        InitializeObjectAttributes(&ObjectAttributes,
+                                   &Name,
+                                   OBJ_CASE_INSENSITIVE,
+                                   RootKeyHandle,
+                                   NULL);
 
-          InitializeObjectAttributes (&ObjectAttributes,
-                                      &Name,
-                                      OBJ_CASE_INSENSITIVE,
-                                      NULL,
-                                      NULL);
-
-          if (Delete || (Flags & FLG_ADDREG_OVERWRITEONLY))
+        if (Delete || (Flags & FLG_ADDREG_OVERWRITEONLY))
+        {
+            Status = NtOpenKey(&KeyHandle,
+                               KEY_ALL_ACCESS,
+                               &ObjectAttributes);
+            if (!NT_SUCCESS(Status))
             {
-              Status = NtOpenKey (&KeyHandle,
-                                  KEY_ALL_ACCESS,
-                                  &ObjectAttributes);
-              if (!NT_SUCCESS(Status))
-                {
-                  DPRINT("NtOpenKey(%wZ) failed (Status %lx)\n", &Name, Status);
-                  continue;  /* ignore if it doesn't exist */
-                }
+                DPRINT1("NtOpenKey(%wZ) failed (Status %lx)\n", &Name, Status);
+                continue;  /* ignore if it doesn't exist */
             }
-          else
+        }
+        else
+        {
+            Status = CreateNestedKey(&KeyHandle,
+                                     KEY_ALL_ACCESS,
+                                     &ObjectAttributes,
+                                     REG_OPTION_NON_VOLATILE);
+            if (!NT_SUCCESS(Status))
             {
-              Status = CreateNestedKey (&KeyHandle,
-                                        KEY_ALL_ACCESS,
-                                        &ObjectAttributes);
-              if (!NT_SUCCESS(Status))
-                {
-                  DPRINT("CreateNestedKey(%wZ) failed (Status %lx)\n", &Name, Status);
-                  continue;
-                }
+                DPRINT1("CreateNestedKey(%wZ) failed (Status %lx)\n", &Name, Status);
+                continue;
             }
-#else
-          if (Delete || (Flags & FLG_ADDREG_OVERWRITEONLY))
-             {
-                LONG rc = RegOpenKeyW(NULL, Buffer, &KeyHandle);
-                if (rc != ERROR_SUCCESS)
-                  {
-                    DPRINT("RegOpenKeyW(%S) failed (error %lu)\n", Buffer, rc);
-                    continue; /* ignore if it doesn't exist */
-                  }
-              }
-              else
-              {
-                  LONG rc = RegCreateKeyW(NULL, Buffer, &KeyHandle);
-                  if (rc != ERROR_SUCCESS)
-                  {
-                    DPRINT("RegCreateKeyW(%S) failed (error %lu)\n", Buffer, rc);
-                    continue;
-                  }
-              }
+        }
+
+        /* get value name */
+        if (SetupGetStringFieldW(&Context, 3, Buffer, sizeof(Buffer)/sizeof(WCHAR), NULL))
+        {
+            RtlInitUnicodeString(&Value, Buffer);
+            ValuePtr = &Value;
+        }
+        else
+        {
+            ValuePtr = NULL;
+        }
+
+        /* and now do it */
+        if (!do_reg_operation(KeyHandle, ValuePtr, &Context, Flags))
+        {
+            NtClose(KeyHandle);
+            return FALSE;
+        }
+
+        NtClose(KeyHandle);
+    }
+
+    return TRUE;
+}
+
+
+BOOLEAN
+ImportRegistryFile(
+    PCWSTR SourcePath,
+    PWSTR Filename,
+    PWSTR Section,
+    LCID LocaleId,
+    BOOLEAN Delete)
+{
+    WCHAR FileNameBuffer[MAX_PATH];
+    HINF hInf;
+    UINT ErrorLine;
+
+    /* Load inf file from install media. */
+    CombinePaths(FileNameBuffer, ARRAYSIZE(FileNameBuffer), 2,
+                 SourcePath, Filename);
+
+    hInf = SetupOpenInfFileExW(FileNameBuffer,
+                               NULL,
+                               INF_STYLE_WIN4,
+                               LocaleId,
+                               &ErrorLine);
+    if (hInf == INVALID_HANDLE_VALUE)
+    {
+        DPRINT1("SetupOpenInfFile() failed\n");
+        return FALSE;
+    }
+
+#if 0
+    if (!registry_callback(hInf, L"DelReg", FALSE))
+    {
+        DPRINT1("registry_callback() failed\n");
+        InfCloseFile(hInf);
+        return FALSE;
+    }
 #endif
 
-          /* get value name */
-          if (SetupGetStringFieldW (&Context, 3, Buffer, MAX_INF_STRING_LENGTH, NULL))
+    if (!registry_callback(hInf, L"AddReg", FALSE))
+    {
+        DPRINT1("registry_callback() failed\n");
+        InfCloseFile(hInf);
+        return FALSE;
+    }
+
+    if (!registry_callback(hInf, L"AddReg.NT" Architecture, FALSE))
+    {
+        DPRINT1("registry_callback() failed\n");
+        InfCloseFile(hInf);
+        return FALSE;
+    }
+
+    InfCloseFile(hInf);
+    return TRUE;
+}
+
+/*
+ * Should be called under privileges
+ */
+static NTSTATUS
+CreateRegistryFile(
+    IN PUNICODE_STRING InstallPath,
+    IN PCWSTR RegistryKey,
+    IN BOOLEAN IsHiveNew,
+    IN HANDLE ProtoKeyHandle
+/*
+    IN PUCHAR Descriptor,
+    IN ULONG DescriptorLength
+*/
+    )
+{
+    /* '.old' is for old valid hives, while '.brk' is for old broken hives */
+    static PCWSTR Extensions[] = {L"old", L"brk"};
+
+    NTSTATUS Status;
+    HANDLE FileHandle;
+    UNICODE_STRING FileName;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    IO_STATUS_BLOCK IoStatusBlock;
+    PCWSTR Extension;
+    WCHAR PathBuffer[MAX_PATH];
+    WCHAR PathBuffer2[MAX_PATH];
+
+    CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 3,
+                 InstallPath->Buffer, L"System32\\config", RegistryKey);
+
+    Extension = Extensions[IsHiveNew ? 0 : 1];
+
+    //
+    // FIXME: The best, actually, would be to rename (move) the existing
+    // System32\config\RegistryKey file to System32\config\RegistryKey.old,
+    // and if it already existed some System32\config\RegistryKey.old, we should
+    // first rename this one into System32\config\RegistryKey_N.old before
+    // performing the original rename.
+    //
+
+    /* Check whether the registry hive file already existed, and if so, rename it */
+    if (DoesFileExist(NULL, PathBuffer))
+    {
+        // UINT i;
+
+        DPRINT1("Registry hive '%S' already exists, rename it\n", PathBuffer);
+
+        // i = 1;
+        /* Try first by just appending the '.old' extension */
+        StringCchPrintfW(PathBuffer2, ARRAYSIZE(PathBuffer2), L"%s.%s", PathBuffer, Extension);
+#if 0
+        while (DoesFileExist(NULL, PathBuffer2))
+        {
+            /* An old file already exists, increments its index, but not too much */
+            if (i <= 0xFFFF)
             {
-              RtlInitUnicodeString (&Value,
-                                    Buffer);
-              ValuePtr = &Value;
+                /* Append '_N.old' extension */
+                StringCchPrintfW(PathBuffer2, ARRAYSIZE(PathBuffer2), L"%s_%lu.%s", PathBuffer, i, Extension);
+                ++i;
             }
-          else
+            else
             {
-              ValuePtr = NULL;
+                /*
+                 * Too many old files exist, we will rename the file
+                 * using the name of the oldest one.
+                 */
+                StringCchPrintfW(PathBuffer2, ARRAYSIZE(PathBuffer2), L"%s.%s", PathBuffer, Extension);
+                break;
             }
+        }
+#endif
 
-          /* and now do it */
-          if (!do_reg_operation (KeyHandle, ValuePtr, &Context, Flags))
-            {
-              NtClose (KeyHandle);
-              return FALSE;
-            }
+        /* Now rename the file (force the move) */
+        Status = SetupMoveFile(PathBuffer, PathBuffer2, MOVEFILE_REPLACE_EXISTING);
+    }
 
-#ifdef __REACTOS__
-          NtClose (KeyHandle);
-#endif
-        }
+    /* Create the file */
+    RtlInitUnicodeString(&FileName, PathBuffer);
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &FileName,
+                               OBJ_CASE_INSENSITIVE,
+                               NULL,  // Could have been InstallPath, etc...
+                               NULL); // Descriptor
+
+    Status = NtCreateFile(&FileHandle,
+                          FILE_GENERIC_WRITE,
+                          &ObjectAttributes,
+                          &IoStatusBlock,
+                          NULL,
+                          FILE_ATTRIBUTE_NORMAL,
+                          0,
+                          FILE_OVERWRITE_IF,
+                          FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE,
+                          NULL,
+                          0);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("NtCreateFile(%wZ) failed, Status 0x%08lx\n", &FileName, Status);
+        return Status;
     }
 
-  return TRUE;
+    /* Save the selected hive into the file */
+    Status = NtSaveKeyEx(ProtoKeyHandle, FileHandle, REG_LATEST_FORMAT);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("NtSaveKeyEx(%wZ) failed, Status 0x%08lx\n", &FileName, Status);
+    }
+
+    /* Close the file and return */
+    NtClose(FileHandle);
+    return Status;
 }
 
+static BOOLEAN
+CmpLinkKeyToHive(
+    IN HANDLE RootLinkKeyHandle OPTIONAL,
+    IN PCWSTR LinkKeyName,
+    IN PCWSTR TargetKeyName)
+{
+    static UNICODE_STRING CmSymbolicLinkValueName =
+        RTL_CONSTANT_STRING(L"SymbolicLinkValue");
+
+    NTSTATUS Status;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    UNICODE_STRING KeyName;
+    HANDLE TargetKeyHandle;
+    ULONG Disposition;
+
+    /* Initialize the object attributes */
+    RtlInitUnicodeString(&KeyName, LinkKeyName);
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &KeyName,
+                               OBJ_CASE_INSENSITIVE,
+                               RootLinkKeyHandle,
+                               NULL);
+
+    /* Create the link key */
+    Status = NtCreateKey(&TargetKeyHandle,
+                         KEY_SET_VALUE | KEY_CREATE_LINK,
+                         &ObjectAttributes,
+                         0,
+                         NULL,
+                         REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK,
+                         &Disposition);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("CmpLinkKeyToHive: couldn't create %S, Status = 0x%08lx\n",
+                LinkKeyName, Status);
+        return FALSE;
+    }
 
-BOOLEAN
-ImportRegistryFile(PWSTR Filename,
-                   PWSTR Section,
-                   LCID LocaleId,
-                   BOOLEAN Delete)
+    /* Check if the new key was actually created */
+    if (Disposition != REG_CREATED_NEW_KEY)
+    {
+        DPRINT1("CmpLinkKeyToHive: %S already exists!\n", LinkKeyName);
+        NtClose(TargetKeyHandle);
+        return FALSE;
+    }
+
+    /* Set the target key name as link target */
+    RtlInitUnicodeString(&KeyName, TargetKeyName);
+    Status = NtSetValueKey(TargetKeyHandle,
+                           &CmSymbolicLinkValueName,
+                           0,
+                           REG_LINK,
+                           KeyName.Buffer,
+                           KeyName.Length);
+
+    /* Close the link key handle */
+    NtClose(TargetKeyHandle);
+
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("CmpLinkKeyToHive: couldn't create symbolic link for %S, Status = 0x%08lx\n",
+                TargetKeyName, Status);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+/*
+ * Should be called under privileges
+ */
+static NTSTATUS
+ConnectRegistry(
+    IN HKEY RootKey OPTIONAL,
+    IN PCWSTR RegMountPoint,
+    // IN HANDLE RootDirectory OPTIONAL,
+    IN PUNICODE_STRING InstallPath,
+    IN PCWSTR RegistryKey
+/*
+    IN PUCHAR Descriptor,
+    IN ULONG DescriptorLength
+*/
+    )
 {
-  WCHAR FileNameBuffer[MAX_PATH];
-  HINF hInf;
-  UINT ErrorLine;
+    UNICODE_STRING KeyName, FileName;
+    OBJECT_ATTRIBUTES KeyObjectAttributes;
+    OBJECT_ATTRIBUTES FileObjectAttributes;
+    WCHAR PathBuffer[MAX_PATH];
+
+    RtlInitUnicodeString(&KeyName, RegMountPoint);
+    InitializeObjectAttributes(&KeyObjectAttributes,
+                               &KeyName,
+                               OBJ_CASE_INSENSITIVE,
+                               RootKey,
+                               NULL);   // Descriptor
+
+    CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 3,
+                 InstallPath->Buffer, L"System32\\config", RegistryKey);
+    RtlInitUnicodeString(&FileName, PathBuffer);
+    InitializeObjectAttributes(&FileObjectAttributes,
+                               &FileName,
+                               OBJ_CASE_INSENSITIVE,
+                               NULL, // RootDirectory,
+                               NULL);
+
+    /* Mount the registry hive in the registry namespace */
+    return NtLoadKey(&KeyObjectAttributes, &FileObjectAttributes);
+}
 
-  /* Load inf file from install media. */
-  wcscpy(FileNameBuffer, SourcePath.Buffer);
-  wcscat(FileNameBuffer, L"\\");
-  wcscat(FileNameBuffer, Filename);
 
-  hInf = SetupOpenInfFileW(
-                       FileNameBuffer,
-                       NULL,
-                       INF_STYLE_WIN4,
-                       LocaleId,
-                       &ErrorLine);
-  if (hInf == INVALID_HANDLE_VALUE)
+static NTSTATUS
+VerifyRegistryHive(
+    // IN HKEY RootKey OPTIONAL,
+    // // IN HANDLE RootDirectory OPTIONAL,
+    IN PUNICODE_STRING InstallPath,
+    IN PCWSTR RegistryKey /* ,
+    IN PCWSTR RegMountPoint */)
+{
+    NTSTATUS Status;
+    UNICODE_STRING KeyName;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+
+    /* Try to mount the specified registry hive */
+    Status = ConnectRegistry(NULL,
+                             L"\\Registry\\Machine\\USetup_VerifyHive",
+                             InstallPath,
+                             RegistryKey
+                             /* NULL, 0 */);
+    if (!NT_SUCCESS(Status))
     {
-      DPRINT1("SetupOpenInfFile() failed\n");
-      return FALSE;
+        DPRINT1("ConnectRegistry(%S) failed, Status 0x%08lx\n", RegistryKey, Status);
     }
 
-  if (!registry_callback (hInf, L"AddReg", FALSE))
+    DPRINT1("VerifyRegistryHive: ConnectRegistry(%S) returns Status 0x%08lx\n", RegistryKey, Status);
+
+    //
+    // TODO: Check the Status error codes: STATUS_SUCCESS, STATUS_REGISTRY_RECOVERED,
+    // STATUS_REGISTRY_HIVE_RECOVERED, STATUS_REGISTRY_CORRUPT, STATUS_REGISTRY_IO_FAILED,
+    // STATUS_NOT_REGISTRY_FILE, STATUS_CANNOT_LOAD_REGISTRY_FILE ;
+    //(STATUS_HIVE_UNLOADED) ; STATUS_SYSTEM_HIVE_TOO_LARGE
+    //
+
+    if (Status == STATUS_REGISTRY_HIVE_RECOVERED) // NT_SUCCESS is still FALSE in this case!
+        DPRINT1("VerifyRegistryHive: Registry hive %S was recovered but some data may be lost (Status 0x%08lx)\n", RegistryKey, Status);
+
+    if (!NT_SUCCESS(Status))
     {
-      DPRINT1("registry_callback() failed\n");
+        DPRINT1("VerifyRegistryHive: Registry hive %S is corrupted (Status 0x%08lx)\n", RegistryKey, Status);
+        return Status;
     }
 
-  if (!registry_callback (hInf, L"AddReg.NT" Architecture, FALSE))
+    if (Status == STATUS_REGISTRY_RECOVERED)
+        DPRINT1("VerifyRegistryHive: Registry hive %S succeeded recovered (Status 0x%08lx)\n", RegistryKey, Status);
+
+    /* Unmount the hive */
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &KeyName,
+                               OBJ_CASE_INSENSITIVE,
+                               NULL,
+                               NULL);
+
+    RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\USetup_VerifyHive");
+    Status = NtUnloadKey(&ObjectAttributes);
+    if (!NT_SUCCESS(Status))
     {
-      DPRINT1("registry_callback() failed\n");
+        DPRINT1("NtUnloadKey(%S, %wZ) failed, Status 0x%08lx\n", RegistryKey, &KeyName, Status);
     }
 
-  InfCloseFile (hInf);
+    return Status;
+}
 
-  return TRUE;
+
+typedef enum _HIVE_UPDATE_STATE
+{
+    Create, // Create a new hive file and save possibly existing old one with a .old extension.
+    Repair, // Re-create a new hive file and save possibly existing old one with a .brk extension.
+    Update  // Hive update, do not need to be recreated.
+} HIVE_UPDATE_STATE;
+
+typedef struct _HIVE_LIST_ENTRY
+{
+    PCWSTR HiveName;            // HiveFileName;
+    PCWSTR HiveRegistryPath;    // HiveRegMountPoint;
+    HANDLE PredefKeyHandle;
+    PCWSTR RegSymLink;
+    HIVE_UPDATE_STATE State;
+    // PUCHAR SecurityDescriptor;
+    // ULONG  SecurityDescriptorLength;
+} HIVE_LIST_ENTRY, *PHIVE_LIST_ENTRY;
+
+#define NUMBER_OF_STANDARD_REGISTRY_HIVES   3
+
+HIVE_LIST_ENTRY RegistryHives[/*NUMBER_OF_STANDARD_REGISTRY_HIVES*/] =
+{
+    { L"SYSTEM"  , L"\\Registry\\Machine\\USetup_SYSTEM"  , HKEY_LOCAL_MACHINE, L"SYSTEM"  , Create /* , SystemSecurity  , sizeof(SystemSecurity)   */ },
+    { L"SOFTWARE", L"\\Registry\\Machine\\USetup_SOFTWARE", HKEY_LOCAL_MACHINE, L"SOFTWARE", Create /* , SoftwareSecurity, sizeof(SoftwareSecurity) */ },
+    { L"DEFAULT" , L"\\Registry\\User\\USetup_DEFAULT"    , HKEY_USERS        , L".DEFAULT", Create /* , SystemSecurity  , sizeof(SystemSecurity)   */ },
+
+//  { L"BCD"     , L"\\Registry\\Machine\\USetup_BCD", HKEY_LOCAL_MACHINE, L"BCD00000000", Create /* , BcdSecurity     , sizeof(BcdSecurity)      */ },
+};
+C_ASSERT(_countof(RegistryHives) == NUMBER_OF_STANDARD_REGISTRY_HIVES);
+
+#define NUMBER_OF_SECURITY_REGISTRY_HIVES   2
+
+/** These hives are created by LSASS during 2nd stage setup */
+HIVE_LIST_ENTRY SecurityRegistryHives[/*NUMBER_OF_SECURITY_REGISTRY_HIVES*/] =
+{
+    { L"SAM"     , L"\\Registry\\Machine\\USetup_SAM"     , HKEY_LOCAL_MACHINE, L"SAM"     , Create /* , SystemSecurity  , sizeof(SystemSecurity)   */ },
+    { L"SECURITY", L"\\Registry\\Machine\\USetup_SECURITY", HKEY_LOCAL_MACHINE, L"SECURITY", Create /* , NULL            , 0                        */ },
+};
+C_ASSERT(_countof(SecurityRegistryHives) == NUMBER_OF_SECURITY_REGISTRY_HIVES);
+
+
+NTSTATUS
+VerifyRegistryHives(
+    IN PUNICODE_STRING InstallPath,
+    OUT PBOOLEAN ShouldRepairRegistry)
+{
+    NTSTATUS Status;
+    BOOLEAN PrivilegeSet[2] = {FALSE, FALSE};
+    UINT i;
+
+    /* Suppose first the registry hives do not have to be fully recreated */
+    *ShouldRepairRegistry = FALSE;
+
+    /* Acquire restore privilege */
+    Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &PrivilegeSet[0]);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE) failed (Status 0x%08lx)\n", Status);
+        /* Exit prematurely here.... */
+        return Status;
+    }
+
+    /* Acquire backup privilege */
+    Status = RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, TRUE, FALSE, &PrivilegeSet[1]);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE) failed (Status 0x%08lx)\n", Status);
+        RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, PrivilegeSet[0], FALSE, &PrivilegeSet[0]);
+        /* Exit prematurely here.... */
+        return Status;
+    }
+
+    for (i = 0; i < ARRAYSIZE(RegistryHives); ++i)
+    {
+        Status = VerifyRegistryHive(InstallPath, RegistryHives[i].HiveName);
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT1("Registry hive '%S' needs repair!\n", RegistryHives[i].HiveName);
+            RegistryHives[i].State = Repair;
+            *ShouldRepairRegistry = TRUE;
+        }
+        else
+        {
+            RegistryHives[i].State = Update;
+        }
+    }
+
+    /** These hives are created by LSASS during 2nd stage setup */
+    for (i = 0; i < ARRAYSIZE(SecurityRegistryHives); ++i)
+    {
+        Status = VerifyRegistryHive(InstallPath, SecurityRegistryHives[i].HiveName);
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT1("Registry hive '%S' needs repair!\n", SecurityRegistryHives[i].HiveName);
+            SecurityRegistryHives[i].State = Repair;
+            /*
+             * Note that it's not the role of the 1st-stage installer to fix
+             * the security hives. This should be done at 2nd-stage installation
+             * by LSASS.
+             */
+        }
+        else
+        {
+            SecurityRegistryHives[i].State = Update;
+        }
+    }
+
+    /* Reset the status (we succeeded in checking all the hives) */
+    Status = STATUS_SUCCESS;
+
+    /* Remove restore and backup privileges */
+    RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, PrivilegeSet[1], FALSE, &PrivilegeSet[1]);
+    RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, PrivilegeSet[0], FALSE, &PrivilegeSet[0]);
+
+    return Status;
 }
 
 
-BOOLEAN
-SetInstallPathValue(PUNICODE_STRING InstallPath)
+NTSTATUS
+RegInitializeRegistry(
+    IN PUNICODE_STRING InstallPath)
 {
-  OBJECT_ATTRIBUTES ObjectAttributes;
-  UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\HARDWARE");
-  UNICODE_STRING ValueName = RTL_CONSTANT_STRING(L"InstallPath");
-  HANDLE KeyHandle;
-  NTSTATUS Status;
-
-  /* Create the 'secret' InstallPath key */
-  InitializeObjectAttributes (&ObjectAttributes,
-                              &KeyName,
-                              OBJ_CASE_INSENSITIVE,
-                              NULL,
-                              NULL);
-  Status =  NtOpenKey (&KeyHandle,
+    NTSTATUS Status;
+    HANDLE KeyHandle;
+    UNICODE_STRING KeyName;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    BOOLEAN PrivilegeSet[2] = {FALSE, FALSE};
+    ULONG Disposition;
+    UINT i;
+
+    /* Acquire restore privilege */
+    Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &PrivilegeSet[0]);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE) failed (Status 0x%08lx)\n", Status);
+        /* Exit prematurely here.... */
+        return Status;
+    }
+
+    /* Acquire backup privilege */
+    Status = RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, TRUE, FALSE, &PrivilegeSet[1]);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE) failed (Status 0x%08lx)\n", Status);
+        RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, PrivilegeSet[0], FALSE, &PrivilegeSet[0]);
+        /* Exit prematurely here.... */
+        return Status;
+    }
+
+    /*
+     * Create the template proto-hive.
+     *
+     * Use a dummy root key name:
+     * - On 2k/XP/2k3, this is "$$$PROTO.HIV"
+     * - On Vista+, this is "CMI-CreateHive{guid}"
+     * See https://github.com/libyal/winreg-kb/blob/master/documentation/Registry%20files.asciidoc
+     * for more information.
+     */
+    RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\SYSTEM\\$$$PROTO.HIV");
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &KeyName,
+                               OBJ_CASE_INSENSITIVE,
+                               NULL,
+                               NULL);
+    Status = NtCreateKey(&KeyHandle,
+                         KEY_ALL_ACCESS,
+                         &ObjectAttributes,
+                         0,
+                         NULL,
+                         REG_OPTION_NON_VOLATILE,
+                         NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("NtCreateKey() failed to create the proto-hive (Status %lx)\n", Status);
+        goto Quit;
+    }
+    NtFlushKey(KeyHandle);
+
+    for (i = 0; i < ARRAYSIZE(RegistryHives); ++i)
+    {
+        if (RegistryHives[i].State != Create && RegistryHives[i].State != Repair)
+            continue;
+
+        Status = CreateRegistryFile(InstallPath,
+                                    RegistryHives[i].HiveName,
+                                    RegistryHives[i].State != Repair, // RegistryHives[i].State == Create,
+                                    KeyHandle);
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT1("CreateRegistryFile(%S) failed, Status 0x%08lx\n", RegistryHives[i].HiveName, Status);
+            /* Exit prematurely here.... */
+            /* That is now done, remove the proto-hive */
+            NtDeleteKey(KeyHandle);
+            NtClose(KeyHandle);
+            goto Quit;
+        }
+    }
+
+    /* That is now done, remove the proto-hive */
+    NtDeleteKey(KeyHandle);
+    NtClose(KeyHandle);
+
+
+    /*
+     * Prepare the registry root keys. Since we cannot create real registry keys
+     * inside the master keys (\Registry, \Registry\Machine or \Registry\User),
+     * we need to perform some SymLink tricks instead.
+     */
+
+    /* Our offline HKLM '\Registry\Machine' is inside '\Registry\Machine\SYSTEM\USetup_Machine' */
+    RtlInitUnicodeString(&KeyName, RootKeys[GetPredefKeyIndex(HKEY_LOCAL_MACHINE)].MountPoint);
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &KeyName,
+                               OBJ_CASE_INSENSITIVE,
+                               NULL,
+                               NULL);
+    KeyHandle = NULL;
+    Status = NtCreateKey(&KeyHandle,
+                         KEY_ALL_ACCESS,
+                         &ObjectAttributes,
+                         0,
+                         NULL,
+                         REG_OPTION_NON_VOLATILE, // REG_OPTION_VOLATILE, // FIXME!
+                         &Disposition);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("NtCreateKey(%wZ) failed (Status 0x%08lx)\n", &KeyName, Status);
+        // return Status;
+    }
+    RootKeys[GetPredefKeyIndex(HKEY_LOCAL_MACHINE)].Handle = KeyHandle;
+
+    /* Our offline HKU '\Registry\User' is inside '\Registry\Machine\SYSTEM\USetup_User' */
+    RtlInitUnicodeString(&KeyName, RootKeys[GetPredefKeyIndex(HKEY_USERS)].MountPoint);
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &KeyName,
+                               OBJ_CASE_INSENSITIVE,
+                               NULL,
+                               NULL);
+    KeyHandle = NULL;
+    Status = NtCreateKey(&KeyHandle,
+                         KEY_ALL_ACCESS,
+                         &ObjectAttributes,
+                         0,
+                         NULL,
+                         REG_OPTION_NON_VOLATILE, // REG_OPTION_VOLATILE, // FIXME!
+                         &Disposition);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("NtCreateKey(%wZ) failed (Status 0x%08lx)\n", &KeyName, Status);
+        // return Status;
+    }
+    RootKeys[GetPredefKeyIndex(HKEY_USERS)].Handle = KeyHandle;
+
+
+    /*
+     * Now properly mount the offline hive files
+     */
+    for (i = 0; i < ARRAYSIZE(RegistryHives); ++i)
+    {
+        // if (RegistryHives[i].State != Create && RegistryHives[i].State != Repair)
+            // continue;
+
+        if (RegistryHives[i].State == Create || RegistryHives[i].State == Repair)
+        {
+            Status = ConnectRegistry(NULL,
+                                     RegistryHives[i].HiveRegistryPath,
+                                     InstallPath,
+                                     RegistryHives[i].HiveName
+                                     /* SystemSecurity, sizeof(SystemSecurity) */);
+            if (!NT_SUCCESS(Status))
+            {
+                DPRINT1("ConnectRegistry(%S) failed, Status 0x%08lx\n", RegistryHives[i].HiveName, Status);
+            }
+
+            /* Create the registry symlink to this key */
+            if (!CmpLinkKeyToHive(RootKeys[GetPredefKeyIndex(RegistryHives[i].PredefKeyHandle)].Handle,
+                                  RegistryHives[i].RegSymLink,
+                                  RegistryHives[i].HiveRegistryPath))
+            {
+                DPRINT1("CmpLinkKeyToHive(%S) failed!\n", RegistryHives[i].HiveName);
+            }
+        }
+        else
+        {
+            /* Create *DUMMY* volatile hives just to make the update procedure working */
+
+            RtlInitUnicodeString(&KeyName, RegistryHives[i].RegSymLink);
+            InitializeObjectAttributes(&ObjectAttributes,
+                                       &KeyName,
+                                       OBJ_CASE_INSENSITIVE,
+                                       RootKeys[GetPredefKeyIndex(RegistryHives[i].PredefKeyHandle)].Handle,
+                                       NULL);
+            KeyHandle = NULL;
+            Status = NtCreateKey(&KeyHandle,
+                                 KEY_ALL_ACCESS,
+                                 &ObjectAttributes,
+                                 0,
+                                 NULL,
+                                 REG_OPTION_NON_VOLATILE, // REG_OPTION_VOLATILE, // FIXME!
+                                 &Disposition);
+            if (!NT_SUCCESS(Status))
+            {
+                DPRINT1("NtCreateKey(%wZ) failed (Status 0x%08lx)\n", &KeyName, Status);
+                // return Status;
+            }
+            NtClose(KeyHandle);
+        }
+    }
+
+
+    /* HKCU is a handle to 'HKU\.DEFAULT' */
+#if 0
+    RtlInitUnicodeString(&KeyName, L".DEFAULT");
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &KeyName,
+                               OBJ_CASE_INSENSITIVE,
+                               RootKeys[GetPredefKeyIndex(HKEY_USERS)].Handle,
+                               NULL);
+#else
+    RtlInitUnicodeString(&KeyName, RootKeys[GetPredefKeyIndex(HKEY_CURRENT_USER)].MountPoint);
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &KeyName,
+                               OBJ_CASE_INSENSITIVE,
+                               NULL,
+                               NULL);
+#endif
+    KeyHandle = NULL;
+    Status = NtOpenKey(&KeyHandle,
                        KEY_ALL_ACCESS,
                        &ObjectAttributes);
-  if (!NT_SUCCESS(Status))
+    if (!NT_SUCCESS(Status))
     {
-      DPRINT1("NtOpenKey() failed (Status %lx)\n", Status);
-      return FALSE;
+        DPRINT1("NtOpenKey(%wZ) failed (Status %lx)\n", &KeyName, Status);
     }
+    RootKeys[GetPredefKeyIndex(HKEY_CURRENT_USER)].Handle = KeyHandle;
 
-  Status = NtSetValueKey (KeyHandle,
-                          &ValueName,
-                          0,
-                          REG_SZ,
-                          (PVOID)InstallPath->Buffer,
-                          InstallPath->Length + sizeof(WCHAR));
-  NtClose(KeyHandle);
-  if (!NT_SUCCESS(Status))
+
+    /* HKCR is a handle to 'HKLM\Software\Classes' */
+#if 0
+    RtlInitUnicodeString(&KeyName, L"Software\\Classes");
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &KeyName,
+                               OBJ_CASE_INSENSITIVE,
+                               RootKeys[GetPredefKeyIndex(HKEY_LOCAL_MACHINE)].Handle,
+                               NULL);
+#else
+    RtlInitUnicodeString(&KeyName, RootKeys[GetPredefKeyIndex(HKEY_CLASSES_ROOT)].MountPoint);
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &KeyName,
+                               OBJ_CASE_INSENSITIVE,
+                               NULL,
+                               NULL);
+#endif
+    KeyHandle = NULL;
+    /* We use NtCreateKey instead of NtOpenKey because Software\Classes doesn't exist originally */
+    Status = NtCreateKey(&KeyHandle,
+                         KEY_ALL_ACCESS,
+                         &ObjectAttributes,
+                         0,
+                         NULL,
+                         REG_OPTION_NON_VOLATILE,
+                         &Disposition);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("NtCreateKey(%wZ) failed (Status %lx)\n", &KeyName, Status);
+    }
+    else
     {
-      DPRINT1("NtSetValueKey() failed (Status %lx)\n", Status);
-      return FALSE;
+        DPRINT1("NtCreateKey() succeeded to %s the %wZ key (Status %lx)\n",
+                Disposition == REG_CREATED_NEW_KEY ? "create" : /* REG_OPENED_EXISTING_KEY */ "open",
+                &KeyName, Status);
     }
+    RootKeys[GetPredefKeyIndex(HKEY_CLASSES_ROOT)].Handle = KeyHandle;
 
-  return TRUE;
+
+    Status = STATUS_SUCCESS;
+
+
+    /* Create the 'HKLM\SYSTEM\ControlSet001' key */
+    // REGISTRY_SETUP_MACHINE L"SYSTEM\\ControlSet001"
+    RtlInitUnicodeString(&KeyName, L"SYSTEM\\ControlSet001");
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &KeyName,
+                               OBJ_CASE_INSENSITIVE,
+                               RootKeys[GetPredefKeyIndex(HKEY_LOCAL_MACHINE)].Handle,
+                               NULL);
+    Status = NtCreateKey(&KeyHandle,
+                         KEY_ALL_ACCESS,
+                         &ObjectAttributes,
+                         0,
+                         NULL,
+                         REG_OPTION_NON_VOLATILE,
+                         &Disposition);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("NtCreateKey() failed to create the ControlSet001 key (Status %lx)\n", Status);
+        // return Status;
+    }
+    else
+    {
+        DPRINT1("NtCreateKey() succeeded to %s the ControlSet001 key (Status %lx)\n",
+                Disposition == REG_CREATED_NEW_KEY ? "create" : /* REG_OPENED_EXISTING_KEY */ "open",
+                Status);
+    }
+    NtClose(KeyHandle);
+
+    /* Create the 'HKLM\SYSTEM\CurrentControlSet' symlink */
+    if (!CmpLinkKeyToHive(RootKeys[GetPredefKeyIndex(HKEY_LOCAL_MACHINE)].Handle,
+                          L"SYSTEM\\CurrentControlSet",
+                          REGISTRY_SETUP_MACHINE L"SYSTEM\\ControlSet001"))
+    {
+        DPRINT1("CmpLinkKeyToHive(CurrentControlSet) failed!\n");
+    }
+
+
+    Status = STATUS_SUCCESS;
+
+
+Quit:
+    /* Remove restore and backup privileges */
+    RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, PrivilegeSet[1], FALSE, &PrivilegeSet[1]);
+    RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, PrivilegeSet[0], FALSE, &PrivilegeSet[0]);
+
+    return Status;
 }
 
-BOOLEAN
-SetMountedDeviceValue(CHAR Letter, ULONG Signature, LARGE_INTEGER StartingOffset)
+VOID
+RegCleanupRegistry(
+    IN PUNICODE_STRING InstallPath)
 {
-  OBJECT_ATTRIBUTES ObjectAttributes;
-  WCHAR ValueNameBuffer[16];
-  UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\MountedDevices");
-  UNICODE_STRING ValueName;
-  REG_DISK_MOUNT_INFO MountInfo;
-  NTSTATUS Status;
-  HANDLE KeyHandle;
-
-  swprintf(ValueNameBuffer, L"\\DosDevices\\%C:", Letter);
-  RtlInitUnicodeString(&ValueName, ValueNameBuffer);
-
-  InitializeObjectAttributes (&ObjectAttributes,
-                              &KeyName,
-                              OBJ_CASE_INSENSITIVE,
-                              NULL,
-                              NULL);
-  Status =  NtOpenKey (&KeyHandle,
-                       KEY_ALL_ACCESS,
-                       &ObjectAttributes);
-  if (!NT_SUCCESS(Status))
+    NTSTATUS Status;
+    HANDLE KeyHandle;
+    UNICODE_STRING KeyName;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    BOOLEAN PrivilegeSet[2] = {FALSE, FALSE};
+    UINT i;
+    WCHAR SrcPath[MAX_PATH];
+    WCHAR DstPath[MAX_PATH];
+
+    /* Acquire restore privilege */
+    Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &PrivilegeSet[0]);
+    if (!NT_SUCCESS(Status))
     {
-      Status = NtCreateKey(&KeyHandle,
-                           KEY_ALL_ACCESS,
-                           &ObjectAttributes,
-                           0,
-                           NULL,
-                           REG_OPTION_NON_VOLATILE,
-                           NULL);
+        DPRINT1("RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE) failed (Status 0x%08lx)\n", Status);
+        /* Exit prematurely here.... */
+        return;
     }
 
-  if (!NT_SUCCESS(Status))
+    /* Acquire backup privilege */
+    Status = RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, TRUE, FALSE, &PrivilegeSet[1]);
+    if (!NT_SUCCESS(Status))
     {
-      DPRINT1("NtCreateKey() failed (Status %lx)\n", Status);
-      return FALSE;
+        DPRINT1("RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE) failed (Status 0x%08lx)\n", Status);
+        RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, PrivilegeSet[0], FALSE, &PrivilegeSet[0]);
+        /* Exit prematurely here.... */
+        return;
     }
 
-  MountInfo.Signature = Signature;
-  MountInfo.StartingOffset = StartingOffset;
-  Status = NtSetValueKey (KeyHandle,
-                          &ValueName,
-                          0,
-                          REG_BINARY,
-                          (PVOID)&MountInfo,
-                          sizeof(MountInfo));
-  NtClose(KeyHandle);
-  if (!NT_SUCCESS(Status))
+    /*
+     * Note that we don't need to explicitly remove the symlinks we have created
+     * since they are created volatile, inside registry keys that will be however
+     * removed explictly in the following.
+     */
+
+    for (i = 0; i < ARRAYSIZE(RegistryHives); ++i)
     {
-      DPRINT1("NtSetValueKey() failed (Status %lx)\n", Status);
-      return FALSE;
+        if (RegistryHives[i].State != Create && RegistryHives[i].State != Repair)
+        {
+            RtlInitUnicodeString(&KeyName, RegistryHives[i].RegSymLink);
+            InitializeObjectAttributes(&ObjectAttributes,
+                                       &KeyName,
+                                       OBJ_CASE_INSENSITIVE,
+                                       RootKeys[GetPredefKeyIndex(RegistryHives[i].PredefKeyHandle)].Handle,
+                                       NULL);
+            KeyHandle = NULL;
+            Status = NtOpenKey(&KeyHandle,
+                               DELETE,
+                               &ObjectAttributes);
+            if (!NT_SUCCESS(Status))
+            {
+                DPRINT1("NtOpenKey(%wZ) failed, Status 0x%08lx\n", &KeyName, Status);
+                // return;
+            }
+
+            NtDeleteKey(KeyHandle);
+            NtClose(KeyHandle);
+        }
+        else
+        {
+            RtlInitUnicodeString(&KeyName, RegistryHives[i].HiveRegistryPath);
+            InitializeObjectAttributes(&ObjectAttributes,
+                                       &KeyName,
+                                       OBJ_CASE_INSENSITIVE,
+                                       NULL,
+                                       NULL);
+            // Status = NtUnloadKey(&ObjectAttributes);
+            Status = NtUnloadKey2(&ObjectAttributes, 1 /* REG_FORCE_UNLOAD */);
+            DPRINT1("Unmounting '%S' %s\n", RegistryHives[i].HiveRegistryPath, NT_SUCCESS(Status) ? "succeeded" : "failed");
+
+            /* Switch the hive state to 'Update' */
+            RegistryHives[i].State = Update;
+        }
     }
 
-  return TRUE;
+    /*
+     * FIXME: Once force-unloading keys is correctly fixed, I'll fix
+     * this code that closes some of the registry keys that were opened
+     * inside the hives we've just unmounted above...
+     */
+
+    /* Remove the registry root keys */
+    for (i = 0; i < ARRAYSIZE(RootKeys); ++i)
+    {
+        if (RootKeys[i].Handle)
+        {
+            /**/NtFlushKey(RootKeys[i].Handle);/**/ // FIXME: Why does it hang? Answer: because we have some problems in CMAPI!
+            NtDeleteKey(RootKeys[i].Handle);
+            NtClose(RootKeys[i].Handle);
+            RootKeys[i].Handle = NULL;
+        }
+    }
+
+    //
+    // RegBackupRegistry()
+    //
+    /* Now backup the hives into .sav files */
+    for (i = 0; i < ARRAYSIZE(RegistryHives); ++i)
+    {
+        if (RegistryHives[i].State != Create && RegistryHives[i].State != Repair)
+            continue;
+
+        CombinePaths(SrcPath, ARRAYSIZE(SrcPath), 3,
+                     InstallPath->Buffer, L"System32\\config", RegistryHives[i].HiveName);
+        StringCchCopyW(DstPath, ARRAYSIZE(DstPath), SrcPath);
+        StringCchCatW(DstPath, ARRAYSIZE(DstPath), L".sav");
+
+        DPRINT1("Copy hive: %S ==> %S\n", SrcPath, DstPath);
+        Status = SetupCopyFile(SrcPath, DstPath, FALSE);
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT1("SetupCopyFile() failed (Status %lx)\n", Status);
+            // return Status;
+        }
+    }
+
+    /* Remove restore and backup privileges */
+    RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, PrivilegeSet[1], FALSE, &PrivilegeSet[1]);
+    RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, PrivilegeSet[0], FALSE, &PrivilegeSet[0]);
+}
+
+
+VOID
+SetDefaultPagefile(
+    WCHAR Drive)
+{
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management");
+    UNICODE_STRING ValueName = RTL_CONSTANT_STRING(L"PagingFiles");
+    WCHAR ValueBuffer[] = L"?:\\pagefile.sys 0 0\0";
+    HANDLE KeyHandle;
+    NTSTATUS Status;
+
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &KeyName,
+                               OBJ_CASE_INSENSITIVE,
+                               RootKeys[GetPredefKeyIndex(HKEY_LOCAL_MACHINE)].Handle,
+                               NULL);
+    Status = NtOpenKey(&KeyHandle,
+                       KEY_ALL_ACCESS,
+                       &ObjectAttributes);
+    if (!NT_SUCCESS(Status))
+        return;
+
+    ValueBuffer[0] = Drive;
+
+    NtSetValueKey(KeyHandle,
+                  &ValueName,
+                  0,
+                  REG_MULTI_SZ,
+                  (PVOID)&ValueBuffer,
+                  sizeof(ValueBuffer));
+
+    NtClose(KeyHandle);
 }
 
 /* EOF */