update for HEAD-2003091401
[reactos.git] / ntoskrnl / cm / regfile.c
index a03290a..392b4c4 100644 (file)
@@ -7,13 +7,12 @@
 */
 
 #include <ddk/ntddk.h>
-#include <ddk/ntifs.h>
 #include <roscfg.h>
 #include <internal/ob.h>
 #include <limits.h>
 #include <string.h>
-#include <internal/pool.h>
 #include <internal/registry.h>
+#include <ntos/minmax.h>
 #include <reactos/bugcodes.h>
 
 #define NDEBUG
@@ -47,8 +46,8 @@ CmiCreateDefaultHiveHeader(PHIVE_HEADER Header)
   Header->BlockId = REG_HIVE_ID;
   Header->UpdateCounter1 = 0;
   Header->UpdateCounter2 = 0;
-  Header->DateModified.dwLowDateTime = 0;
-  Header->DateModified.dwHighDateTime = 0;
+  Header->DateModified.u.LowPart = 0;
+  Header->DateModified.u.HighPart = 0;
   Header->Unused3 = 1;
   Header->Unused4 = 3;
   Header->Unused5 = 0;
@@ -67,8 +66,8 @@ CmiCreateDefaultBinCell(PHBIN BinCell)
   assert(BinCell);
   RtlZeroMemory(BinCell, sizeof(HBIN));
   BinCell->BlockId = REG_BIN_ID;
-  BinCell->DateModified.dwLowDateTime = 0;
-  BinCell->DateModified.dwHighDateTime = 0;
+  BinCell->DateModified.u.LowPart = 0;
+  BinCell->DateModified.u.HighPart = 0;
   BinCell->BlockSize = REG_BLOCK_SIZE;
 }
 
@@ -80,8 +79,8 @@ CmiCreateDefaultRootKeyCell(PKEY_CELL RootKeyCell)
   RtlZeroMemory(RootKeyCell, sizeof(KEY_CELL));
   RootKeyCell->CellSize = -sizeof(KEY_CELL);
   RootKeyCell->Id = REG_KEY_CELL_ID;
-  RootKeyCell->Type = REG_ROOT_KEY_CELL_TYPE;
-  NtQuerySystemTime((PTIME) &RootKeyCell->LastWriteTime);
+  RootKeyCell->Flags = REG_KEY_ROOT_CELL | REG_KEY_NAME_PACKED;
+  NtQuerySystemTime(&RootKeyCell->LastWriteTime);
   RootKeyCell->ParentKeyOffset = 0;
   RootKeyCell->NumberOfSubKeys = 0;
   RootKeyCell->HashTableOffset = -1;
@@ -147,13 +146,7 @@ CmiVerifyKeyCell(PKEY_CELL KeyCell)
       assert(KeyCell->Id == REG_KEY_CELL_ID);
     }
 
-  if ((KeyCell->Type != REG_KEY_CELL_TYPE)
-    && (KeyCell->Type != REG_ROOT_KEY_CELL_TYPE))
-    {
-      DbgPrint("Type is %.08x (should be %.08x or %.08x)\n",
-        KeyCell->Type, REG_KEY_CELL_TYPE, REG_ROOT_KEY_CELL_TYPE);
-      assert(FALSE);
-    }
+  //KeyCell->Flags;
 
   //KeyCell->LastWriteTime;
 
@@ -207,11 +200,11 @@ CmiVerifyRootKeyCell(PKEY_CELL RootKeyCell)
 
   CmiVerifyKeyCell(RootKeyCell);
 
-  if (RootKeyCell->Type != REG_ROOT_KEY_CELL_TYPE)
+  if (!(RootKeyCell->Flags & REG_KEY_ROOT_CELL))
     {
-      DbgPrint("Type is %.08x (should be %.08x)\n",
-        RootKeyCell->Type, REG_ROOT_KEY_CELL_TYPE);
-      assert(RootKeyCell->Type == REG_ROOT_KEY_CELL_TYPE);
+      DbgPrint("Flags is %.08x (should be %.08x)\n",
+        RootKeyCell->Flags, REG_KEY_ROOT_CELL | REG_KEY_NAME_PACKED);
+      assert(!(RootKeyCell->Flags & (REG_KEY_ROOT_CELL | REG_KEY_NAME_PACKED)));
     }
 
     }
@@ -364,64 +357,6 @@ CmiVerifyRegistryHive(PREGISTRY_HIVE RegistryHive)
 }
 
 
-#if 0
-static NTSTATUS
-CmiPopulateHive(HANDLE FileHandle)
-{
-  IO_STATUS_BLOCK IoStatusBlock;
-  LARGE_INTEGER FileOffset;
-  PCELL_HEADER FreeCell;
-  NTSTATUS Status;
-  PHBIN BinCell;
-  PCHAR tBuf;
-  ULONG i;
-
-  tBuf = (PCHAR) ExAllocatePool(NonPagedPool, REG_BLOCK_SIZE);
-  if (tBuf == NULL)
-    return STATUS_INSUFFICIENT_RESOURCES;
-
-  BinCell = (PHBIN) tBuf;
-  FreeCell = (PCELL_HEADER) (tBuf + REG_HBIN_DATA_OFFSET);
-
-  CmiCreateDefaultBinCell(BinCell);
-
-  // The whole block is free
-  FreeCell->CellSize = REG_BLOCK_SIZE - REG_HBIN_DATA_OFFSET;
-
-  // Add free blocks so we don't need to expand
-  // the file for a while
-  for (i = 1; i < 50; i++)
-    {
-      // Block offset of this bin
-      BinCell->BlockOffset = i * REG_BLOCK_SIZE;
-
-      FileOffset.u.HighPart = 0;
-      FileOffset.u.LowPart  = (i + 1) * REG_BLOCK_SIZE;
-
-      Status = NtWriteFile(FileHandle,
-                          NULL,
-                          NULL,
-                          NULL,
-                          &IoStatusBlock,
-                          tBuf,
-                          REG_BLOCK_SIZE,
-                          &FileOffset,
-                          NULL);
-      assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
-      if (!NT_SUCCESS(Status))
-       {
-         ExFreePool(tBuf);
-         return Status;
-       }
-    }
-
-  ExFreePool(tBuf);
-
-  return Status;
-}
-#endif
-
-
 static NTSTATUS
 CmiCreateNewRegFile(HANDLE FileHandle)
 {
@@ -474,13 +409,6 @@ CmiCreateNewRegFile(HANDLE FileHandle)
       return(Status);
     }
 
-#if 0
-  if (NT_SUCCESS(Status))
-    {
-      CmiPopulateHive(FileHandle);
-    }
-#endif
-
   Status = NtFlushBuffersFile(FileHandle,
                              &IoStatusBlock);
 
@@ -599,7 +527,7 @@ CmiCheckAndFixHive(PREGISTRY_HIVE RegistryHive)
        {
          /* There is no way to fix the hive without log file - BSOD! */
          DPRINT("Hive header inconsistent and no log file available!\n");
-         KeBugCheck(CONFIG_LIST_FAILED);
+         KEBUGCHECK(CONFIG_LIST_FAILED);
        }
 
       Status = STATUS_SUCCESS;
@@ -642,7 +570,7 @@ CmiCheckAndFixHive(PREGISTRY_HIVE RegistryHive)
              HiveHeader->UpdateCounter1 != HiveHeader->UpdateCounter2)
            {
              DPRINT("Hive file and log file are inconsistent!\n");
-             KeBugCheck(CONFIG_LIST_FAILED);
+             KEBUGCHECK(CONFIG_LIST_FAILED);
            }
 
          /* Log file damaged but hive is okay */
@@ -743,27 +671,196 @@ ByeBye:
 #endif
 
 
+NTSTATUS
+CmiImportHiveBins(PREGISTRY_HIVE Hive,
+                 PUCHAR ChunkPtr)
+{
+  BLOCK_OFFSET BlockOffset;
+  ULONG BlockIndex;
+  PHBIN Bin;
+  ULONG j;
+
+  BlockIndex = 0;
+  BlockOffset = 0;
+  while (BlockIndex < Hive->BlockListSize)
+    {
+      Bin = (PHBIN)((ULONG_PTR)ChunkPtr + BlockOffset);
+
+      if (Bin->BlockId != REG_BIN_ID)
+       {
+         DPRINT1 ("Bad BlockId %x, offset %x\n", Bin->BlockId, BlockOffset);
+         return STATUS_REGISTRY_CORRUPT;
+       }
+
+      assertmsg((Bin->BlockSize % 4096) == 0,
+               ("BlockSize (0x%.08x) must be multiple of 4K\n",
+               Bin->BlockSize));
+
+      /* Allocate the hive block */
+      Hive->BlockList[BlockIndex] = ExAllocatePool (PagedPool,
+                                                   Bin->BlockSize);
+      if (Hive->BlockList[BlockIndex] == NULL)
+       {
+         DPRINT1 ("ExAllocatePool() failed\n");
+         return STATUS_INSUFFICIENT_RESOURCES;
+       }
+
+      /* Import the Bin */
+      RtlCopyMemory (Hive->BlockList[BlockIndex],
+                    Bin,
+                    Bin->BlockSize);
+
+      if (Bin->BlockSize > 4096)
+       {
+         for (j = 1; j < Bin->BlockSize / 4096; j++)
+           {
+             Hive->BlockList[BlockIndex + j] = Hive->BlockList[BlockIndex];
+           }
+       }
+
+      BlockIndex += Bin->BlockSize / 4096;
+      BlockOffset += Bin->BlockSize;
+    }
+
+  return STATUS_SUCCESS;
+}
+
+
+VOID
+CmiFreeHiveBins (PREGISTRY_HIVE Hive)
+{
+  ULONG i;
+  PHBIN Bin;
+
+  Bin = NULL;
+  for (i = 0; i < Hive->BlockListSize; i++)
+    {
+      if (Hive->BlockList[i] == NULL)
+       continue;
+
+      if (Hive->BlockList[i] != Bin)
+       {
+         Bin = Hive->BlockList[i];
+         ExFreePool (Hive->BlockList[i]);
+       }
+      Hive->BlockList[i] = NULL;
+    }
+}
+
+
+NTSTATUS
+CmiCreateHiveFreeCellList(PREGISTRY_HIVE Hive)
+{
+  BLOCK_OFFSET BlockOffset;
+  PCELL_HEADER FreeBlock;
+  ULONG BlockIndex;
+  ULONG FreeOffset;
+  PHBIN Bin;
+  NTSTATUS Status;
+
+  /* Initialize the free cell list */
+  Hive->FreeListSize = 0;
+  Hive->FreeListMax = 0;
+  Hive->FreeList = NULL;
+  Hive->FreeListOffset = NULL;
+
+  BlockOffset = 0;
+  BlockIndex = 0;
+  while (BlockIndex < Hive->BlockListSize)
+    {
+      Bin = Hive->BlockList[BlockIndex];
+
+      /* Search free blocks and add to list */
+      FreeOffset = REG_HBIN_DATA_OFFSET;
+      while (FreeOffset < Bin->BlockSize)
+       {
+         FreeBlock = (PCELL_HEADER) ((ULONG_PTR) Bin + FreeOffset);
+         if (FreeBlock->CellSize > 0)
+           {
+             Status = CmiAddFree(Hive,
+                                 FreeBlock,
+                                 Bin->BlockOffset + FreeOffset,
+                                 FALSE);
+
+             if (!NT_SUCCESS(Status))
+               {
+                 return Status;
+               }
+
+             FreeOffset += FreeBlock->CellSize;
+           }
+         else
+           {
+             FreeOffset -= FreeBlock->CellSize;
+           }
+       }
+
+      BlockIndex += Bin->BlockSize / 4096;
+      BlockOffset += Bin->BlockSize;
+    }
+
+  return STATUS_SUCCESS;
+}
+
+
+VOID
+CmiFreeHiveFreeCellList(PREGISTRY_HIVE Hive)
+{
+  ExFreePool (Hive->FreeList);
+  ExFreePool (Hive->FreeListOffset);
+
+  Hive->FreeListSize = 0;
+  Hive->FreeListMax = 0;
+  Hive->FreeList = NULL;
+  Hive->FreeListOffset = NULL;
+}
+
+
+NTSTATUS
+CmiCreateHiveBitmap(PREGISTRY_HIVE Hive)
+{
+  ULONG BitmapSize;
+
+  /* Calculate bitmap size in bytes (always a multiple of 32 bits) */
+  BitmapSize = ROUND_UP(Hive->BlockListSize, sizeof(ULONG) * 8) / 8;
+  DPRINT("Hive->BlockListSize: %lu\n", Hive->BlockListSize);
+  DPRINT("BitmapSize:  %lu Bytes  %lu Bits\n", BitmapSize, BitmapSize * 8);
+
+  /* Allocate bitmap */
+  Hive->BitmapBuffer = (PULONG)ExAllocatePool(PagedPool,
+                                             BitmapSize);
+  if (Hive->BitmapBuffer == NULL)
+    {
+      return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+  RtlInitializeBitMap(&Hive->DirtyBitMap,
+                     Hive->BitmapBuffer,
+                     BitmapSize * 8);
+
+  /* Initialize bitmap */
+  RtlClearAllBits(&Hive->DirtyBitMap);
+  Hive->HiveDirty = FALSE;
+
+  return STATUS_SUCCESS;
+}
+
+
 static NTSTATUS
-CmiInitNonVolatileRegistryHive(PREGISTRY_HIVE RegistryHive,
-                              PWSTR Filename,
-                              BOOLEAN CreateNew)
+CmiInitNonVolatileRegistryHive (PREGISTRY_HIVE RegistryHive,
+                               PWSTR Filename)
 {
   OBJECT_ATTRIBUTES ObjectAttributes;
-  FILE_STANDARD_INFORMATION fsi;
-  PCELL_HEADER FreeBlock;
-  LARGE_INTEGER FileOffset;
-  BLOCK_OFFSET BlockOffset;
   ULONG CreateDisposition;
   IO_STATUS_BLOCK IoSB;
   HANDLE FileHandle;
-  ULONG FreeOffset;
+  HANDLE SectionHandle;
+  PUCHAR ViewBase;
+  ULONG ViewSize;
   NTSTATUS Status;
-  PHBIN tmpBin;
-  ULONG i, j;
-  ULONG BitmapSize;
 
-  DPRINT("CmiInitNonVolatileRegistryHive(%p, %S, %d) called\n",
-        RegistryHive, Filename, CreateNew);
+  DPRINT("CmiInitNonVolatileRegistryHive(%p, %S) called\n",
+        RegistryHive, Filename);
 
   /* Duplicate Filename */
   Status = RtlCreateUnicodeString(&RegistryHive->HiveFileName,
@@ -808,21 +905,7 @@ CmiInitNonVolatileRegistryHive(PREGISTRY_HIVE RegistryHive,
                             NULL,
                             NULL);
 
-  /*
-   * Note:
-   * This is a workaround to prevent a BSOD because of missing registry hives.
-   * The workaround is only useful for developers. An implementation for the
-   * ordinary user must bail out on missing registry hives because they are
-   * essential to booting and configuring the OS.
-   */
-#if 0
-  if (CreateNew == TRUE)
-    CreateDisposition = FILE_OPEN_IF;
-  else
-    CreateDisposition = FILE_OPEN;
-#endif
   CreateDisposition = FILE_OPEN_IF;
-
   Status = NtCreateFile(&FileHandle,
                        FILE_ALL_ACCESS,
                        &ObjectAttributes,
@@ -842,10 +925,6 @@ CmiInitNonVolatileRegistryHive(PREGISTRY_HIVE RegistryHive,
       return(Status);
     }
 
-  /* Note: Another workaround! See the note above! */
-#if 0
-  if ((CreateNew) && (IoSB.Information == FILE_CREATED))
-#endif
   if (IoSB.Information != FILE_OPENED)
     {
       Status = CmiCreateNewRegFile(FileHandle);
@@ -859,289 +938,415 @@ CmiInitNonVolatileRegistryHive(PREGISTRY_HIVE RegistryHive,
        }
     }
 
-  /* Read hive header */
-  FileOffset.u.HighPart = 0;
-  FileOffset.u.LowPart = 0;
-  DPRINT("    Attempting to ZwReadFile(%d) for %d bytes into %p\n", FileHandle, sizeof(HIVE_HEADER), RegistryHive->HiveHeader);
-  Status = NtReadFile(FileHandle,
-                     0,
-                     0,
-                     0,
-                     &IoSB,
-                     RegistryHive->HiveHeader,
-                     sizeof(HIVE_HEADER),
-                     &FileOffset,
-                     0);
-  assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
+  /* Create the hive section */
+  Status = NtCreateSection(&SectionHandle,
+                          SECTION_ALL_ACCESS,
+                          NULL,
+                          NULL,
+                          PAGE_READWRITE,
+                          SEC_COMMIT,
+                          FileHandle);
+  NtClose(FileHandle);
   if (!NT_SUCCESS(Status))
     {
-      DPRINT("NtReadFile() failed (Status %lx)\n", Status);
-      NtClose(FileHandle);
+      DPRINT1("NtCreateSection() failed (Status %lx)\n", Status);
       RtlFreeUnicodeString(&RegistryHive->HiveFileName);
       RtlFreeUnicodeString(&RegistryHive->LogFileName);
-      return Status;
+      return(Status);
     }
 
-  /* Read update counter */
-  RegistryHive->UpdateCounter = RegistryHive->HiveHeader->UpdateCounter1;
-
-  Status = NtQueryInformationFile(FileHandle,
-                                 &IoSB,
-                                 &fsi,
-                                 sizeof(fsi),
-                                 FileStandardInformation);
-  assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
-  if (!NT_SUCCESS(Status) || fsi.EndOfFile.u.LowPart == 0)
+  /* Map the hive file */
+  ViewBase = NULL;
+  ViewSize = 0;
+  Status = NtMapViewOfSection(SectionHandle,
+                             NtCurrentProcess(),
+                             (PVOID*)&ViewBase,
+                             0,
+                             ViewSize,
+                             NULL,
+                             &ViewSize,
+                             0,
+                             MEM_COMMIT,
+                             PAGE_READWRITE);
+  if (!NT_SUCCESS(Status))
     {
-      DPRINT("NtQueryInformationFile() failed (Status %lx)\n", Status);
-      NtClose(FileHandle);
+      DPRINT1("MmMapViewInSystemSpace() failed (Status %lx)\n", Status);
+      NtClose(SectionHandle);
       RtlFreeUnicodeString(&RegistryHive->HiveFileName);
       RtlFreeUnicodeString(&RegistryHive->LogFileName);
-      return Status;
+      return(Status);
     }
+  DPRINT("ViewBase %p  ViewSize %lx\n", ViewBase, ViewSize);
 
-  RegistryHive->FileSize = fsi.EndOfFile.u.LowPart;
+  /* Copy hive header and initalize hive */
+  RtlCopyMemory (RegistryHive->HiveHeader,
+                ViewBase,
+                sizeof(HIVE_HEADER));
+  RegistryHive->FileSize = ViewSize;
   RegistryHive->BlockListSize = (RegistryHive->FileSize / 4096) - 1;
+  RegistryHive->UpdateCounter = RegistryHive->HiveHeader->UpdateCounter1;
 
-  DPRINT("Space needed for block list describing hive: 0x%x\n",
-    sizeof(PHBIN *) * RegistryHive->BlockListSize);
-
+  /* Allocate hive block list */
   RegistryHive->BlockList = ExAllocatePool(NonPagedPool,
          sizeof(PHBIN *) * RegistryHive->BlockListSize);
-
   if (RegistryHive->BlockList == NULL)
     {
-      ExFreePool(RegistryHive->BlockList);
-      NtClose(FileHandle);
+      DPRINT1("Failed to allocate the hive block list\n");
+      NtUnmapViewOfSection(NtCurrentProcess(),
+                          ViewBase);
+      NtClose(SectionHandle);
       RtlFreeUnicodeString(&RegistryHive->HiveFileName);
       RtlFreeUnicodeString(&RegistryHive->LogFileName);
-      DPRINT("CmiInitNonVolatileRegistryHive() - Failed 6.\n");
       return STATUS_INSUFFICIENT_RESOURCES;
     }
 
-  RegistryHive->BlockList[0] = ExAllocatePool(PagedPool,
-         RegistryHive->FileSize - 4096);
-  if (RegistryHive->BlockList[0] == NULL)
+  /* Import the hive bins */
+  Status = CmiImportHiveBins (RegistryHive,
+                             ViewBase + 4096);
+  if (!NT_SUCCESS(Status))
     {
       ExFreePool(RegistryHive->BlockList);
-      NtClose(FileHandle);
+      NtUnmapViewOfSection(NtCurrentProcess(),
+                          ViewBase);
+      NtClose(SectionHandle);
       RtlFreeUnicodeString(&RegistryHive->HiveFileName);
       RtlFreeUnicodeString(&RegistryHive->LogFileName);
-      DPRINT("CmiInitNonVolatileRegistryHive() - Failed 8.\n");
-      return STATUS_INSUFFICIENT_RESOURCES;
+      return Status;
     }
 
-  FileOffset.u.HighPart = 0;
-  FileOffset.u.LowPart = 4096;
-
-  DPRINT("    Attempting to NtReadFile(%d) for %d bytes into %p\n",
-        FileHandle, RegistryHive->FileSize - 4096, (PVOID)RegistryHive->BlockList[0]);
-  Status = NtReadFile(FileHandle,
-                     0,
-                     0,
-                     0,
-                     &IoSB,
-                     (PVOID) RegistryHive->BlockList[0],
-                     RegistryHive->FileSize - 4096,
-                     &FileOffset,
-                     0);
-
-  assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
-
-  NtClose(FileHandle);
+  /* Unmap and dereference the hive section */
+  NtUnmapViewOfSection(NtCurrentProcess(),
+                      ViewBase);
+  NtClose(SectionHandle);
 
-  RegistryHive->FreeListSize = 0;
-  RegistryHive->FreeListMax = 0;
-  RegistryHive->FreeList = NULL;
+  /* Initialize the free cell list */
+  Status = CmiCreateHiveFreeCellList (RegistryHive);
+  if (!NT_SUCCESS(Status))
+    {
+      CmiFreeHiveBins(RegistryHive);
+      ExFreePool(RegistryHive->BlockList);
+      RtlFreeUnicodeString(&RegistryHive->HiveFileName);
+      RtlFreeUnicodeString(&RegistryHive->LogFileName);
+      return Status;
+    }
 
-  BlockOffset = 0;
-  for (i = 0; i < RegistryHive->BlockListSize; i++)
+  /* Create the block bitmap */
+  Status = CmiCreateHiveBitmap (RegistryHive);
+  if (!NT_SUCCESS(Status))
     {
-      RegistryHive->BlockList[i] = (PHBIN) (((ULONG_PTR) RegistryHive->BlockList[0]) + BlockOffset);
-      tmpBin = (PHBIN) (((ULONG_PTR) RegistryHive->BlockList[i]));
-      if (tmpBin->BlockId != REG_BIN_ID)
-       {
-         DPRINT("Bad BlockId %x, offset %x\n", tmpBin->BlockId, BlockOffset);
-         //KeBugCheck(0);
-         return STATUS_INSUFFICIENT_RESOURCES;
-       }
+      CmiFreeHiveFreeCellList(RegistryHive);
+      CmiFreeHiveBins(RegistryHive);
+      ExFreePool(RegistryHive->BlockList);
+      RtlFreeUnicodeString(&RegistryHive->HiveFileName);
+      RtlFreeUnicodeString(&RegistryHive->LogFileName);
+      return Status;
+    }
 
-      assertmsg((tmpBin->BlockSize % 4096) == 0, ("BlockSize (0x%.08x) must be multiplum of 4K\n", tmpBin->BlockSize));
+  DPRINT("CmiInitNonVolatileRegistryHive(%p, %S) - Finished.\n",
+        RegistryHive, Filename);
 
-      if (tmpBin->BlockSize > 4096)
-       {
-         for (j = 1; j < tmpBin->BlockSize / 4096; j++)
-           {
-             RegistryHive->BlockList[i + j] = RegistryHive->BlockList[i];
-           }
-         i = i + j - 1;
-       }
+  return(STATUS_SUCCESS);
+}
 
-      /* Search free blocks and add to list */
-      FreeOffset = REG_HBIN_DATA_OFFSET;
-      while (FreeOffset < tmpBin->BlockSize)
-       {
-         FreeBlock = (PCELL_HEADER) ((ULONG_PTR) RegistryHive->BlockList[i] + FreeOffset);
-         if (FreeBlock->CellSize > 0)
-           {
-             Status = CmiAddFree(RegistryHive,
-                                 FreeBlock,
-                                 RegistryHive->BlockList[i]->BlockOffset + FreeOffset,
-                                 FALSE);
 
-             if (!NT_SUCCESS(Status))
-               {
-                 /* FIXME: */
-                 assert(FALSE);
-               }
+NTSTATUS
+CmiCreateVolatileHive(PREGISTRY_HIVE *RegistryHive)
+{
+  PKEY_CELL RootKeyCell;
+  PREGISTRY_HIVE Hive;
 
-             FreeOffset += FreeBlock->CellSize;
-           }
-         else
-           {
-             FreeOffset -= FreeBlock->CellSize;
-           }
-       }
-      BlockOffset += tmpBin->BlockSize;
-    }
+  *RegistryHive = NULL;
 
-  /*
-   * Create block bitmap and clear all bits
-   */
-  /* Calculate bitmap size in bytes (always a multiple of 32 bits) */
-  BitmapSize = ROUND_UP(RegistryHive->BlockListSize, sizeof(ULONG) * 8) / 8;
-  DPRINT("RegistryHive->BlockListSize: %lu\n", RegistryHive->BlockListSize);
-  DPRINT("BitmapSize:  %lu Bytes  %lu Bits\n", BitmapSize, BitmapSize * 8);
+  Hive = ExAllocatePool (NonPagedPool,
+                        sizeof(REGISTRY_HIVE));
+  if (Hive == NULL)
+    return STATUS_INSUFFICIENT_RESOURCES;
 
-  /* Allocate bitmap */
-  RegistryHive->BitmapBuffer = (PULONG)ExAllocatePool(PagedPool,
-                                                     BitmapSize);
-  RtlInitializeBitMap(&RegistryHive->DirtyBitMap,
-                     RegistryHive->BitmapBuffer,
-                     BitmapSize * 8);
+  RtlZeroMemory (Hive,
+                sizeof(REGISTRY_HIVE));
 
-  /* Initialize bitmap */
-  RtlClearAllBits(&RegistryHive->DirtyBitMap);
-  RegistryHive->HiveDirty = FALSE;
+  DPRINT("Hive %x\n", Hive);
 
-  DPRINT("CmiInitNonVolatileRegistryHive(%p, %S, %d) - Finished.\n",
-        RegistryHive, Filename, CreateNew);
+  Hive->HiveHeader = (PHIVE_HEADER)ExAllocatePool (NonPagedPool,
+                                                  sizeof(HIVE_HEADER));
+  if (Hive->HiveHeader == NULL)
+    {
+      ExFreePool (Hive);
+      return STATUS_INSUFFICIENT_RESOURCES;
+    }
 
-  return(STATUS_SUCCESS);
-}
+  Hive->Flags = (HIVE_NO_FILE | HIVE_POINTER);
 
+  CmiCreateDefaultHiveHeader (Hive->HiveHeader);
 
-static NTSTATUS
-CmiInitVolatileRegistryHive(PREGISTRY_HIVE RegistryHive)
-{
-  PKEY_CELL RootKeyCell;
+  RootKeyCell = (PKEY_CELL)ExAllocatePool (NonPagedPool,
+                                          sizeof(KEY_CELL));
+  if (RootKeyCell == NULL)
+    {
+      ExFreePool(Hive->HiveHeader);
+      ExFreePool(Hive);
+      return STATUS_INSUFFICIENT_RESOURCES;
+    }
 
-  RegistryHive->Flags |= (HIVE_VOLATILE | HIVE_POINTER);
+  CmiCreateDefaultRootKeyCell (RootKeyCell);
+  Hive->HiveHeader->RootKeyCell = (BLOCK_OFFSET)RootKeyCell;
 
-  CmiCreateDefaultHiveHeader(RegistryHive->HiveHeader);
+  ExInitializeResourceLite (&Hive->HiveResource);
 
-  RootKeyCell = (PKEY_CELL) ExAllocatePool(NonPagedPool, sizeof(KEY_CELL));
+  /* Acquire hive list lock exclusively */
+  ExAcquireResourceExclusiveLite (&CmiHiveListLock,
+                                 TRUE);
 
-  if (RootKeyCell == NULL)
-    return STATUS_INSUFFICIENT_RESOURCES;
+  /* Add the new hive to the hive list */
+  InsertTailList (&CmiHiveListHead,
+                 &Hive->HiveList);
 
-  CmiCreateDefaultRootKeyCell(RootKeyCell);
+  /* Release hive list lock */
+  ExReleaseResourceLite (&CmiHiveListLock);
 
-  RegistryHive->HiveHeader->RootKeyCell = (BLOCK_OFFSET) RootKeyCell;
+  VERIFY_REGISTRY_HIVE (Hive);
+
+  *RegistryHive = Hive;
 
   return STATUS_SUCCESS;
 }
 
 
 NTSTATUS
-CmiCreateRegistryHive(PWSTR Filename,
-                     PREGISTRY_HIVE *RegistryHive,
-                     BOOLEAN CreateNew)
+CmiCreateTempHive(PREGISTRY_HIVE *RegistryHive)
 {
+  PHBIN BinCell;
+  PCELL_HEADER FreeCell;
   PREGISTRY_HIVE Hive;
   NTSTATUS Status;
 
-  DPRINT("CmiCreateRegistryHive(Filename %S)\n", Filename);
+  DPRINT ("CmiCreateTempHive() called\n");
 
   *RegistryHive = NULL;
 
-  Hive = ExAllocatePool(NonPagedPool, sizeof(REGISTRY_HIVE));
+  Hive = ExAllocatePool (NonPagedPool,
+                        sizeof(REGISTRY_HIVE));
   if (Hive == NULL)
-    return(STATUS_INSUFFICIENT_RESOURCES);
-
-  DPRINT("Hive %x\n", Hive);
-
-  RtlZeroMemory(Hive, sizeof(REGISTRY_HIVE));
+    {
+      DPRINT1 ("Failed to allocate registry hive block\n");
+      return STATUS_INSUFFICIENT_RESOURCES;
+    }
+  RtlZeroMemory (Hive,
+                sizeof(REGISTRY_HIVE));
 
-  Hive->HiveHeader = (PHIVE_HEADER)
-    ExAllocatePool(NonPagedPool, sizeof(HIVE_HEADER));
+  DPRINT ("Hive %x\n", Hive);
 
+  Hive->HiveHeader = (PHIVE_HEADER)ExAllocatePool (NonPagedPool,
+                                                  REG_BLOCK_SIZE);
   if (Hive->HiveHeader == NULL)
     {
-      ExFreePool(Hive);
-      return(STATUS_INSUFFICIENT_RESOURCES);
+      DPRINT1 ("Failed to allocate hive header block\n");
+      ExFreePool (Hive);
+      return STATUS_INSUFFICIENT_RESOURCES;
     }
+  RtlZeroMemory (Hive->HiveHeader,
+                REG_BLOCK_SIZE);
+
+  DPRINT ("HiveHeader %x\n", Hive->HiveHeader);
+
+  Hive->Flags = HIVE_NO_FILE;
 
-  if (Filename != NULL)
+  RtlInitUnicodeString (&Hive->HiveFileName,
+                       NULL);
+  RtlInitUnicodeString (&Hive->LogFileName,
+                       NULL);
+
+  CmiCreateDefaultHiveHeader (Hive->HiveHeader);
+
+  /* Allocate hive block list */
+  Hive->BlockList = ExAllocatePool (NonPagedPool,
+                                   sizeof(PHBIN *));
+  if (Hive->BlockList == NULL)
     {
-      Status = CmiInitNonVolatileRegistryHive(Hive, Filename, CreateNew);
+      DPRINT1 ("Failed to allocate hive block list\n");
+      ExFreePool(Hive->HiveHeader);
+      ExFreePool(Hive);
+      return STATUS_INSUFFICIENT_RESOURCES;
     }
-  else
+
+  /* Allocate first Bin */
+  Hive->BlockList[0] = ExAllocatePool (NonPagedPool,
+                                      REG_BLOCK_SIZE);
+  if (Hive->BlockList[0] == NULL)
     {
-      Status = CmiInitVolatileRegistryHive(Hive);
+      DPRINT1 ("Failed to allocate first bin\n");
+      ExFreePool(Hive->BlockList);
+      ExFreePool(Hive->HiveHeader);
+      ExFreePool(Hive);
+      return STATUS_INSUFFICIENT_RESOURCES;
     }
 
-  if (!NT_SUCCESS(Status))
+  Hive->FileSize = 2* REG_BLOCK_SIZE;
+  Hive->BlockListSize = 1;
+  Hive->UpdateCounter = Hive->HiveHeader->UpdateCounter1;
+
+
+  BinCell = (PHBIN)Hive->BlockList[0];
+  FreeCell = (PCELL_HEADER)((ULONG_PTR)BinCell + REG_HBIN_DATA_OFFSET);
+
+  CmiCreateDefaultBinCell (BinCell);
+
+  /* First block */
+  BinCell->BlockOffset = 0;
+
+  /* Offset to root key block */
+  Hive->HiveHeader->RootKeyCell = -1;
+
+  /* The rest of the block is free */
+  FreeCell->CellSize = REG_BLOCK_SIZE - REG_HBIN_DATA_OFFSET;
+
+  /* Create the free cell list */
+  Status = CmiCreateHiveFreeCellList (Hive);
+  if (Hive->BlockList[0] == NULL)
     {
+      DPRINT1 ("CmiCreateHiveFreeCellList() failed (Status %lx)\n", Status);
+      ExFreePool(Hive->BlockList[0]);
+      ExFreePool(Hive->BlockList);
       ExFreePool(Hive->HiveHeader);
       ExFreePool(Hive);
-      return(Status);
+      return Status;
     }
 
-  ExInitializeResourceLite(&Hive->HiveResource);
+
+  ExInitializeResourceLite (&Hive->HiveResource);
 
   /* Acquire hive list lock exclusively */
-  ExAcquireResourceExclusiveLite(&CmiHiveListLock, TRUE);
+  ExAcquireResourceExclusiveLite (&CmiHiveListLock,
+                                 TRUE);
 
   /* Add the new hive to the hive list */
-  InsertTailList(&CmiHiveListHead, &Hive->HiveList);
+  InsertTailList (&CmiHiveListHead,
+                 &Hive->HiveList);
 
   /* Release hive list lock */
-  ExReleaseResourceLite(&CmiHiveListLock);
+  ExReleaseResourceLite (&CmiHiveListLock);
 
-  VERIFY_REGISTRY_HIVE(Hive);
+  VERIFY_REGISTRY_HIVE (Hive);
 
   *RegistryHive = Hive;
 
-  DPRINT("CmiCreateRegistryHive(Filename %S) - Finished.\n", Filename);
+  return STATUS_SUCCESS;
+}
 
-  return(STATUS_SUCCESS);
+
+NTSTATUS
+CmiLoadHive(IN POBJECT_ATTRIBUTES KeyObjectAttributes,
+           IN PUNICODE_STRING FileName,
+           IN ULONG Flags)
+{
+  PREGISTRY_HIVE Hive;
+  NTSTATUS Status;
+
+  DPRINT ("CmiLoadHive(Filename %wZ)\n", FileName);
+
+  if (Flags & ~REG_NO_LAZY_FLUSH)
+    return STATUS_INVALID_PARAMETER;
+
+  Hive = ExAllocatePool (NonPagedPool,
+                        sizeof(REGISTRY_HIVE));
+  if (Hive == NULL)
+    {
+      DPRINT1 ("Failed to allocate hive header.\n");
+      return STATUS_INSUFFICIENT_RESOURCES;
+    }
+  RtlZeroMemory (Hive,
+                sizeof(REGISTRY_HIVE));
+
+  DPRINT ("Hive %x\n", Hive);
+  Hive->Flags = (Flags & REG_NO_LAZY_FLUSH) ? HIVE_NO_SYNCH : 0;
+
+  Hive->HiveHeader = (PHIVE_HEADER)ExAllocatePool(NonPagedPool,
+                                                 sizeof(HIVE_HEADER));
+  if (Hive->HiveHeader == NULL)
+    {
+      DPRINT1 ("Failed to allocate hive header.\n");
+      ExFreePool (Hive);
+      return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+  Status = CmiInitNonVolatileRegistryHive (Hive,
+                                          FileName->Buffer);
+  if (!NT_SUCCESS (Status))
+    {
+      DPRINT1 ("CmiInitNonVolatileRegistryHive() failed (Status %lx)\n", Status);
+      ExFreePool (Hive->HiveHeader);
+      ExFreePool (Hive);
+      return Status;
+    }
+
+  ExInitializeResourceLite (&Hive->HiveResource);
+
+  /* Add the new hive to the hive list */
+  ExAcquireResourceExclusiveLite (&CmiHiveListLock,
+                                 TRUE);
+  InsertTailList (&CmiHiveListHead,
+                 &Hive->HiveList);
+  ExReleaseResourceLite (&CmiHiveListLock);
+
+
+  VERIFY_REGISTRY_HIVE(Hive);
+
+
+  Status = CmiConnectHive (KeyObjectAttributes,
+                          Hive);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT1 ("CmiConnectHive() failed (Status %lx)\n", Status);
+//      CmiRemoveRegistryHive (Hive);
+    }
+
+  DPRINT ("CmiLoadHive() done\n");
+
+  return Status;
 }
 
 
 NTSTATUS
 CmiRemoveRegistryHive(PREGISTRY_HIVE RegistryHive)
 {
+  if (RegistryHive->Flags & HIVE_POINTER)
+    return STATUS_UNSUCCESSFUL;
+
   /* Acquire hive list lock exclusively */
-  ExAcquireResourceExclusiveLite(&CmiHiveListLock, TRUE);
+  ExAcquireResourceExclusiveLite (&CmiHiveListLock,
+                                 TRUE);
 
   /* Remove hive from hive list */
-  RemoveEntryList(&RegistryHive->HiveList);
+  RemoveEntryList (&RegistryHive->HiveList);
 
   /* Release hive list lock */
-  ExReleaseResourceLite(&CmiHiveListLock);
+  ExReleaseResourceLite (&CmiHiveListLock);
+
+  /* Release file names */
+  RtlFreeUnicodeString (&RegistryHive->HiveFileName);
+  RtlFreeUnicodeString (&RegistryHive->LogFileName);
 
+  /* Release hive bitmap */
+  ExFreePool (RegistryHive->BitmapBuffer);
 
-  /* FIXME: Remove attached keys and values */
+  /* Release free cell list */
+  ExFreePool (RegistryHive->FreeList);
+  ExFreePool (RegistryHive->FreeListOffset);
 
+  /* Release hive resource */
+  ExDeleteResource (&RegistryHive->HiveResource);
+
+  /* Release bins and bin list */
+  CmiFreeHiveBins (RegistryHive);
+  ExFreePool (RegistryHive->BlockList);
 
   /* Release hive header */
-  ExFreePool(RegistryHive->HiveHeader);
+  ExFreePool (RegistryHive->HiveHeader);
 
   /* Release hive */
-  ExFreePool(RegistryHive);
+  ExFreePool (RegistryHive);
 
-  return(STATUS_SUCCESS);
+  return STATUS_SUCCESS;
 }
 
 
@@ -1737,7 +1942,7 @@ CmiFlushRegistryHive(PREGISTRY_HIVE RegistryHive)
         &RegistryHive->LogFileName);
 
   /* Update hive header modification time */
-  NtQuerySystemTime((PTIME)&RegistryHive->HiveHeader->DateModified);
+  NtQuerySystemTime(&RegistryHive->HiveHeader->DateModified);
 
   /* Start log update */
   Status = CmiStartLogUpdate(RegistryHive);
@@ -1793,40 +1998,78 @@ CmiFlushRegistryHive(PREGISTRY_HIVE RegistryHive)
 
 
 ULONG
-CmiGetMaxNameLength(PREGISTRY_HIVE  RegistryHive,
-                   PKEY_CELL  KeyCell)
+CmiGetMaxNameLength(PKEY_OBJECT KeyObject)
 {
   PHASH_TABLE_CELL HashBlock;
+  PKEY_OBJECT CurKey;
   PKEY_CELL CurSubKeyCell;
+  PKEY_CELL KeyCell;
   ULONG MaxName;
+  ULONG NameSize;
   ULONG i;
 
+  VERIFY_KEY_OBJECT(KeyObject);
+
+  KeyCell = KeyObject->KeyCell;
   VERIFY_KEY_CELL(KeyCell);
 
   MaxName = 0;
-  HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
+  HashBlock = CmiGetBlock(KeyObject->RegistryHive, 
+                          KeyCell->HashTableOffset, 
+                         NULL);
   if (HashBlock == NULL)
     {
       DPRINT("CmiGetBlock() failed\n");
-      return 0;
-    }
-
-  for (i = 0; i < HashBlock->HashTableSize; i++)
+    }
+  else
     {
-      if (HashBlock->Table[i].KeyOffset != 0)
-       {
-         CurSubKeyCell = CmiGetBlock(RegistryHive,
-                                     HashBlock->Table[i].KeyOffset,
-                                     NULL);
-         if (CurSubKeyCell == NULL)
+      for (i = 0; i < HashBlock->HashTableSize; i++)
+        {
+          if (HashBlock->Table[i].KeyOffset != 0)
            {
-             DPRINT("CmiGetBlock() failed\n");
-             return 0;
+             CurSubKeyCell = CmiGetBlock(KeyObject->RegistryHive,
+                                         HashBlock->Table[i].KeyOffset,
+                                         NULL);
+             if (CurSubKeyCell == NULL)
+               {
+                 DPRINT("CmiGetBlock() failed\n");
+                 continue;
+               }
+              NameSize = CurSubKeyCell->NameSize;
+             if (CurSubKeyCell->Flags & REG_KEY_NAME_PACKED)
+               {
+                 NameSize *= sizeof(WCHAR);
+               }
+             if (MaxName < NameSize)
+               {
+                 MaxName = NameSize;
+               }
            }
-
-         if (MaxName < CurSubKeyCell->NameSize)
+       }
+    }
+  if (KeyObject->RegistryHive != CmiVolatileHive)
+    {
+      DPRINT("KeyObject->NumberOfSubKeys %d\n", KeyObject->NumberOfSubKeys);
+      for (i = 0; i < KeyObject->NumberOfSubKeys; i++)
+        {
+         CurKey = KeyObject->SubKeys[i];
+         if (CurKey->RegistryHive == CmiVolatileHive)
            {
-             MaxName = CurSubKeyCell->NameSize;
+             CurSubKeyCell = CurKey->KeyCell;
+             if (CurSubKeyCell == NULL)
+               {
+                  DPRINT("CmiGetBlock() failed\n");
+                 continue;
+               }
+              NameSize = CurSubKeyCell->NameSize;
+             if (CurSubKeyCell->Flags & REG_KEY_NAME_PACKED)
+               {
+                 NameSize *= sizeof(WCHAR);
+               }
+             if (MaxName < NameSize)
+               {
+                 MaxName = NameSize;
+               }
            }
        }
     }
@@ -1836,40 +2079,68 @@ CmiGetMaxNameLength(PREGISTRY_HIVE  RegistryHive,
 
 
 ULONG
-CmiGetMaxClassLength(PREGISTRY_HIVE  RegistryHive,
-                    PKEY_CELL  KeyCell)
+CmiGetMaxClassLength(PKEY_OBJECT  KeyObject)
 {
   PHASH_TABLE_CELL HashBlock;
+  PKEY_OBJECT CurKey;
   PKEY_CELL CurSubKeyCell;
+  PKEY_CELL KeyCell;
   ULONG MaxClass;
   ULONG i;
 
+  VERIFY_KEY_OBJECT(KeyObject);
+
+  KeyCell = KeyObject->KeyCell;
   VERIFY_KEY_CELL(KeyCell);
 
   MaxClass = 0;
-  HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
+  HashBlock = CmiGetBlock(KeyObject->RegistryHive, 
+                          KeyCell->HashTableOffset, 
+                         NULL);
   if (HashBlock == NULL)
     {
       DPRINT("CmiGetBlock() failed\n");
-      return 0;
     }
-
-  for (i = 0; i < HashBlock->HashTableSize; i++)
+  else
     {
-      if (HashBlock->Table[i].KeyOffset != 0)
-       {
-         CurSubKeyCell = CmiGetBlock(RegistryHive,
-                                     HashBlock->Table[i].KeyOffset,
-                                     NULL);
-         if (CurSubKeyCell == NULL)
+      for (i = 0; i < HashBlock->HashTableSize; i++)
+        {
+          if (HashBlock->Table[i].KeyOffset != 0)
            {
-             DPRINT("CmiGetBlock() failed\n");
-             return 0;
+             CurSubKeyCell = CmiGetBlock(KeyObject->RegistryHive,
+                                         HashBlock->Table[i].KeyOffset,
+                                         NULL);
+             if (CurSubKeyCell == NULL)
+               {
+                 DPRINT("CmiGetBlock() failed\n");
+                 continue;
+               }
+
+             if (MaxClass < CurSubKeyCell->ClassSize)
+               {
+                 MaxClass = CurSubKeyCell->ClassSize;
+               }
            }
-
-         if (MaxClass < CurSubKeyCell->ClassSize)
+       }
+    }
+  if (KeyObject->RegistryHive != CmiVolatileHive)
+    {
+      DPRINT("KeyObject->NumberOfSubKeys %d\n", KeyObject->NumberOfSubKeys);
+      for (i = 0; i < KeyObject->NumberOfSubKeys; i++)
+        {
+         CurKey = KeyObject->SubKeys[i];
+         if (CurKey->RegistryHive == CmiVolatileHive)
            {
-             MaxClass = CurSubKeyCell->ClassSize;
+             CurSubKeyCell = CurKey->KeyCell;
+             if (CurSubKeyCell == NULL)
+               {
+                  DPRINT("CmiGetBlock() failed\n");
+                 continue;
+               }
+             if (MaxClass < CurSubKeyCell->ClassSize)
+               {
+                 MaxClass = CurSubKeyCell->ClassSize;
+               }
            }
        }
     }
@@ -1885,6 +2156,7 @@ CmiGetMaxValueNameLength(PREGISTRY_HIVE RegistryHive,
   PVALUE_LIST_CELL ValueListCell;
   PVALUE_CELL CurValueCell;
   ULONG MaxValueName;
+  ULONG Size;
   ULONG i;
 
   VERIFY_KEY_CELL(KeyCell);
@@ -1909,10 +2181,17 @@ CmiGetMaxValueNameLength(PREGISTRY_HIVE RegistryHive,
          DPRINT("CmiGetBlock() failed\n");
        }
 
-      if (CurValueCell != NULL &&
-          MaxValueName < CurValueCell->NameSize)
+      if (CurValueCell != NULL)
         {
-          MaxValueName = CurValueCell->NameSize;
+         Size = CurValueCell->NameSize;
+         if (CurValueCell->Flags & REG_VALUE_NAME_PACKED)
+           {
+             Size *= sizeof(WCHAR);
+           }
+          if (MaxValueName < Size)
+            {
+              MaxValueName = Size;
+           }
         }
     }
 
@@ -1958,18 +2237,17 @@ CmiScanForSubKey(IN PREGISTRY_HIVE RegistryHive,
                 IN PKEY_CELL KeyCell,
                 OUT PKEY_CELL *SubKeyCell,
                 OUT BLOCK_OFFSET *BlockOffset,
-                IN PCHAR KeyName,
+                IN PUNICODE_STRING KeyName,
                 IN ACCESS_MASK DesiredAccess,
                 IN ULONG Attributes)
 {
   PHASH_TABLE_CELL HashBlock;
   PKEY_CELL CurSubKeyCell;
-  USHORT KeyLength;
   ULONG i;
 
   VERIFY_KEY_CELL(KeyCell);
 
-  //DPRINT("Scanning for sub key %s\n", KeyName);
+  DPRINT("Scanning for sub key %wZ\n", KeyName);
 
   assert(RegistryHive);
 
@@ -1989,14 +2267,14 @@ CmiScanForSubKey(IN PREGISTRY_HIVE RegistryHive,
       return STATUS_UNSUCCESSFUL;
     }
 
-  KeyLength = strlen(KeyName);
   for (i = 0; (i < KeyCell->NumberOfSubKeys) && (i < HashBlock->HashTableSize); i++)
     {
       if (Attributes & OBJ_CASE_INSENSITIVE)
        {
-         if ((HashBlock->Table[i].KeyOffset != 0) &&
-             (HashBlock->Table[i].KeyOffset != (ULONG_PTR)-1) &&
-             (_strnicmp(KeyName, (PCHAR) &HashBlock->Table[i].HashValue, 4) == 0))
+         if (HashBlock->Table[i].KeyOffset != 0 &&
+             HashBlock->Table[i].KeyOffset != (ULONG_PTR)-1 &&
+             (HashBlock->Table[i].HashValue == 0 ||
+              CmiCompareHashI(KeyName, (PCHAR)&HashBlock->Table[i].HashValue)))
            {
              CurSubKeyCell = CmiGetBlock(RegistryHive,
                                          HashBlock->Table[i].KeyOffset,
@@ -2007,20 +2285,20 @@ CmiScanForSubKey(IN PREGISTRY_HIVE RegistryHive,
                  return STATUS_UNSUCCESSFUL;
                }
 
-              if ((CurSubKeyCell->NameSize == KeyLength)
-                  && (_strnicmp(KeyName, CurSubKeyCell->Name, KeyLength) == 0))
-                {
-                  *SubKeyCell = CurSubKeyCell;
-                  *BlockOffset = HashBlock->Table[i].KeyOffset;
-                  break;
-                }
-            }
+             if (CmiCompareKeyNamesI(KeyName, CurSubKeyCell))
+               {
+                 *SubKeyCell = CurSubKeyCell;
+                 *BlockOffset = HashBlock->Table[i].KeyOffset;
+                 break;
+               }
+           }
        }
       else
        {
          if (HashBlock->Table[i].KeyOffset != 0 &&
              HashBlock->Table[i].KeyOffset != (ULONG_PTR) -1 &&
-             !strncmp(KeyName, (PCHAR) &HashBlock->Table[i].HashValue, 4))
+             (HashBlock->Table[i].HashValue == 0 ||
+              CmiCompareHash(KeyName, (PCHAR)&HashBlock->Table[i].HashValue)))
            {
              CurSubKeyCell = CmiGetBlock(RegistryHive,
                                          HashBlock->Table[i].KeyOffset,
@@ -2031,14 +2309,13 @@ CmiScanForSubKey(IN PREGISTRY_HIVE RegistryHive,
                  return STATUS_UNSUCCESSFUL;
                }
 
-              if (CurSubKeyCell->NameSize == KeyLength
-                  && !_strnicmp(KeyName, CurSubKeyCell->Name, KeyLength))
-                {
-                  *SubKeyCell = CurSubKeyCell;
-                  *BlockOffset = HashBlock->Table[i].KeyOffset;
-                  break;
-                }
-            }
+             if (CmiCompareKeyNames(KeyName, CurSubKeyCell))
+               {
+                 *SubKeyCell = CurSubKeyCell;
+                 *BlockOffset = HashBlock->Table[i].KeyOffset;
+                 break;
+               }
+           }
        }
     }
 
@@ -2050,8 +2327,7 @@ NTSTATUS
 CmiAddSubKey(PREGISTRY_HIVE RegistryHive,
             PKEY_OBJECT Parent,
             PKEY_OBJECT SubKey,
-            PWSTR NewSubKeyName,
-            USHORT NewSubKeyNameSize,
+            PUNICODE_STRING SubKeyName,
             ULONG TitleIndex,
             PUNICODE_STRING Class,
             ULONG CreateOptions)
@@ -2059,25 +2335,50 @@ CmiAddSubKey(PREGISTRY_HIVE RegistryHive,
   PHASH_TABLE_CELL NewHashBlock;
   PHASH_TABLE_CELL HashBlock;
   BLOCK_OFFSET NKBOffset;
-  PKEY_CELL NewKeyCell; 
+  PKEY_CELL NewKeyCell;
   ULONG NewBlockSize;
   PKEY_CELL KeyCell;
   NTSTATUS Status;
   USHORT NameSize;
+  PWSTR NamePtr;
+  BOOLEAN Packable;
+  ULONG i;
 
   KeyCell = Parent->KeyCell;
 
   VERIFY_KEY_CELL(KeyCell);
 
-  if (NewSubKeyName[0] == L'\\')
+  /* Skip leading backslash */
+  if (SubKeyName->Buffer[0] == L'\\')
     {
-      NewSubKeyName++;
-      NameSize = NewSubKeyNameSize / 2 - 1;
+      NamePtr = &SubKeyName->Buffer[1];
+      NameSize = SubKeyName->Length - sizeof(WCHAR);
     }
   else
     {
-      NameSize = NewSubKeyNameSize / 2;
+      NamePtr = SubKeyName->Buffer;
+      NameSize = SubKeyName->Length;
+    }
+
+  /* Check whether key name can be packed */
+  Packable = TRUE;
+  for (i = 0; i < NameSize / sizeof(WCHAR); i++)
+    {
+      if (NamePtr[i] & 0xFF00)
+       {
+         Packable = FALSE;
+         break;
+       }
+    }
+
+  /* Adjust name size */
+  if (Packable)
+    {
+      NameSize = NameSize / sizeof(WCHAR);
     }
+
+  DPRINT("Key %S  Length %lu  %s\n", NamePtr, NameSize, (Packable)?"True":"False");
+
   Status = STATUS_SUCCESS;
 
   NewBlockSize = sizeof(KEY_CELL) + NameSize;
@@ -2092,18 +2393,33 @@ CmiAddSubKey(PREGISTRY_HIVE RegistryHive,
   else
     {
       NewKeyCell->Id = REG_KEY_CELL_ID;
-      NewKeyCell->Type = REG_KEY_CELL_TYPE;
-      ZwQuerySystemTime((PTIME) &NewKeyCell->LastWriteTime);
+      NewKeyCell->Flags = 0;
+      NtQuerySystemTime(&NewKeyCell->LastWriteTime);
       NewKeyCell->ParentKeyOffset = -1;
       NewKeyCell->NumberOfSubKeys = 0;
       NewKeyCell->HashTableOffset = -1;
       NewKeyCell->NumberOfValues = 0;
       NewKeyCell->ValuesOffset = -1;
       NewKeyCell->SecurityKeyOffset = -1;
-      NewKeyCell->NameSize = NameSize;
-      wcstombs(NewKeyCell->Name, NewSubKeyName, NameSize);
       NewKeyCell->ClassNameOffset = -1;
 
+      /* Pack the key name */
+      NewKeyCell->NameSize = NameSize;
+      if (Packable)
+       {
+         NewKeyCell->Flags |= REG_KEY_NAME_PACKED;
+         for (i = 0; i < NameSize; i++)
+           {
+             NewKeyCell->Name[i] = (CHAR)(NamePtr[i] & 0x00FF);
+           }
+       }
+      else
+       {
+         RtlCopyMemory(NewKeyCell->Name,
+                       NamePtr,
+                       NameSize);
+       }
+
       VERIFY_KEY_CELL(NewKeyCell);
 
       if (Class)
@@ -2112,10 +2428,12 @@ CmiAddSubKey(PREGISTRY_HIVE RegistryHive,
 
          NewKeyCell->ClassSize = Class->Length + sizeof(WCHAR);
          Status = CmiAllocateBlock(RegistryHive,
-                                   (PVOID) &pClass,
+                                   (PVOID)&pClass,
                                    NewKeyCell->ClassSize,
                                    &NewKeyCell->ClassNameOffset);
-         wcsncpy((PWSTR) pClass->Data, Class->Buffer, Class->Length);
+         wcsncpy((PWSTR)pClass->Data,
+                 Class->Buffer,
+                 Class->Length);
          ((PWSTR) (pClass->Data))[Class->Length] = 0;
        }
     }
@@ -2147,7 +2465,9 @@ CmiAddSubKey(PREGISTRY_HIVE RegistryHive,
     }
   else
     {
-      HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
+      HashBlock = CmiGetBlock(RegistryHive,
+                             KeyCell->HashTableOffset,
+                             NULL);
       if (HashBlock == NULL)
        {
          DPRINT("CmiGetBlock() failed\n");
@@ -2273,7 +2593,7 @@ CmiRemoveSubKey(PREGISTRY_HIVE RegistryHive,
   if (ParentKey->KeyCell->HashTableOffset != (BLOCK_OFFSET) -1)
     {
       DPRINT("ParentKey HashTableOffset %lx\n", ParentKey->KeyCell->HashTableOffset)
-      HashBlock = CmiGetBlock(RegistryHive,
+      HashBlock = CmiGetBlock(ParentKey->RegistryHive,
                              ParentKey->KeyCell->HashTableOffset,
                              NULL);
       if (HashBlock == NULL)
@@ -2284,10 +2604,10 @@ CmiRemoveSubKey(PREGISTRY_HIVE RegistryHive,
       DPRINT("ParentKey HashBlock %p\n", HashBlock)
       if (HashBlock != NULL)
        {
-         CmiRemoveKeyFromHashTable(RegistryHive,
+         CmiRemoveKeyFromHashTable(ParentKey->RegistryHive,
                                    HashBlock,
                                    SubKey->BlockOffset);
-         CmiMarkBlockDirty(RegistryHive,
+         CmiMarkBlockDirty(ParentKey->RegistryHive,
                            ParentKey->KeyCell->HashTableOffset);
        }
     }
@@ -2324,7 +2644,7 @@ CmiRemoveSubKey(PREGISTRY_HIVE RegistryHive,
       if (ParentKey->KeyCell->NumberOfSubKeys == 0)
        {
          DPRINT("ParentKey HashTableOffset %lx\n", ParentKey->KeyCell->HashTableOffset)
-         HashBlock = CmiGetBlock(RegistryHive,
+         HashBlock = CmiGetBlock(ParentKey->RegistryHive,
                                  ParentKey->KeyCell->HashTableOffset,
                                  NULL);
          if (HashBlock == NULL)
@@ -2335,15 +2655,15 @@ CmiRemoveSubKey(PREGISTRY_HIVE RegistryHive,
          DPRINT("ParentKey HashBlock %p\n", HashBlock)
          if (HashBlock != NULL)
            {
-             CmiDestroyBlock(RegistryHive,
+             CmiDestroyBlock(ParentKey->RegistryHive,
                              HashBlock,
                              ParentKey->KeyCell->HashTableOffset);
              ParentKey->KeyCell->HashTableOffset = -1;
            }
        }
 
-      NtQuerySystemTime((PTIME)&ParentKey->KeyCell->LastWriteTime);
-      CmiMarkBlockDirty(RegistryHive,
+      NtQuerySystemTime(&ParentKey->KeyCell->LastWriteTime);
+      CmiMarkBlockDirty(ParentKey->RegistryHive,
                        ParentKey->BlockOffset);
     }
 
@@ -2685,7 +3005,13 @@ CmiAddKeyToHashTable(PREGISTRY_HIVE RegistryHive,
       if (HashBlock->Table[i].KeyOffset == 0)
        {
          HashBlock->Table[i].KeyOffset = NKBOffset;
-         RtlCopyMemory(&HashBlock->Table[i].HashValue, NewKeyCell->Name, 4);
+         HashBlock->Table[i].HashValue = 0;
+         if (NewKeyCell->Flags & REG_KEY_NAME_PACKED)
+           {
+             RtlCopyMemory(&HashBlock->Table[i].HashValue,
+                           NewKeyCell->Name,
+                           min(NewKeyCell->NameSize, 4));
+           }
          return STATUS_SUCCESS;
        }
     }
@@ -2706,7 +3032,7 @@ CmiRemoveKeyFromHashTable(PREGISTRY_HIVE RegistryHive,
       if (HashBlock->Table[i].KeyOffset == NKBOffset)
        {
          HashBlock->Table[i].KeyOffset = 0;
-         RtlZeroMemory(&HashBlock->Table[i].HashValue, 4);
+         HashBlock->Table[i].HashValue = 0;
          return STATUS_SUCCESS;
        }
     }
@@ -2801,17 +3127,17 @@ CmiDestroyValueCell(PREGISTRY_HIVE RegistryHive,
        }
 
       /* Update time of heap */
-      if (!IsVolatileHive(RegistryHive))
-       NtQuerySystemTime((PTIME) &pBin->DateModified);
+      if (!IsNoFileHive(RegistryHive))
+       NtQuerySystemTime(&pBin->DateModified);
     }
 
   /* Destroy the value cell */
   Status = CmiDestroyBlock(RegistryHive, ValueCell, VBOffset);
 
   /* Update time of heap */
-  if (!IsVolatileHive(RegistryHive) && CmiGetBlock(RegistryHive, VBOffset, &pBin))
+  if (!IsNoFileHive(RegistryHive) && CmiGetBlock(RegistryHive, VBOffset, &pBin))
     {
-      NtQuerySystemTime((PTIME) &pBin->DateModified);
+      NtQuerySystemTime(&pBin->DateModified);
     }
 
   return Status;
@@ -2838,7 +3164,7 @@ CmiAddBin(PREGISTRY_HIVE RegistryHive,
   RegistryHive->FileSize += REG_BLOCK_SIZE;
   tmpBin->BlockSize = REG_BLOCK_SIZE;
   tmpBin->Unused1 = 0;
-  ZwQuerySystemTime((PTIME) &tmpBin->DateModified);
+  ZwQuerySystemTime(&tmpBin->DateModified);
   tmpBin->Unused2 = 0;
 
   /* Increase size of list of blocks */
@@ -2867,7 +3193,7 @@ CmiAddBin(PREGISTRY_HIVE RegistryHive,
   tmpBlock->CellSize = (REG_BLOCK_SIZE - REG_HBIN_DATA_OFFSET);
 
   /* Grow bitmap if necessary */
-  if (IsVolatileHive(RegistryHive) &&
+  if (IsNoFileHive(RegistryHive) &&
       (RegistryHive->BlockListSize % (sizeof(ULONG) * 8) == 0))
     {
       PULONG BitmapBuffer;
@@ -2962,7 +3288,7 @@ CmiAllocateBlock(PREGISTRY_HIVE RegistryHive,
 
              if (Temp)
                {
-                 NtQuerySystemTime((PTIME) &pBin->DateModified);
+                 NtQuerySystemTime(&pBin->DateModified);
                  CmiMarkBlockDirty(RegistryHive, RegistryHive->FreeListOffset[i]);
                }
 
@@ -3048,8 +3374,8 @@ CmiDestroyBlock(PREGISTRY_HIVE RegistryHive,
       CmiAddFree(RegistryHive, Block, Offset, TRUE);
 
       /* Update time of heap */
-      if (!IsVolatileHive(RegistryHive) && CmiGetBlock(RegistryHive, Offset,&pBin))
-       NtQuerySystemTime((PTIME) &pBin->DateModified);
+      if (!IsNoFileHive(RegistryHive) && CmiGetBlock(RegistryHive, Offset,&pBin))
+       NtQuerySystemTime(&pBin->DateModified);
 
       CmiMarkBlockDirty(RegistryHive, Offset);
     }
@@ -3307,7 +3633,7 @@ CmiMarkBlockDirty(PREGISTRY_HIVE RegistryHive,
   ULONG BlockNumber;
   ULONG BlockCount;
 
-  if (IsVolatileHive(RegistryHive))
+  if (IsNoFileHive(RegistryHive))
     return;
 
   DPRINT("CmiMarkBlockDirty(Offset 0x%lx)\n", (ULONG)BlockOffset);
@@ -3345,7 +3671,7 @@ CmiMarkBinDirty(PREGISTRY_HIVE RegistryHive,
   ULONG BlockCount;
   PHBIN Bin;
 
-  if (IsVolatileHive(RegistryHive))
+  if (IsNoFileHive(RegistryHive))
     return;
 
   DPRINT("CmiMarkBinDirty(Offset 0x%lx)\n", (ULONG)BinOffset);
@@ -3377,17 +3703,17 @@ CmiGetPackedNameLength(IN PUNICODE_STRING Name,
   if (Packable != NULL)
     *Packable = TRUE;
 
-  for (i = 0; i < Name->Length; i++)
+  for (i = 0; i < Name->Length / sizeof(WCHAR); i++)
     {
-      if (Name->Buffer[i] > 0xFF)
+      if (Name->Buffer[i] & 0xFF00)
        {
          if (Packable != NULL)
            *Packable = FALSE;
-         return(Name->Length);
+         return Name->Length;
        }
     }
 
-  return(Name->Length / sizeof(WCHAR));
+  return (Name->Length / sizeof(WCHAR));
 }
 
 
@@ -3440,4 +3766,432 @@ CmiCopyPackedName(PWCHAR NameBuffer,
     NameBuffer[i] = (WCHAR)PackedNameBuffer[i];
 }
 
+
+BOOLEAN
+CmiCompareHash(PUNICODE_STRING KeyName,
+              PCHAR HashString)
+{
+  CHAR Buffer[4];
+
+  Buffer[0] = (KeyName->Length >= 2) ? (CHAR)KeyName->Buffer[0] : 0;
+  Buffer[1] = (KeyName->Length >= 4) ? (CHAR)KeyName->Buffer[1] : 0;
+  Buffer[2] = (KeyName->Length >= 6) ? (CHAR)KeyName->Buffer[2] : 0;
+  Buffer[3] = (KeyName->Length >= 8) ? (CHAR)KeyName->Buffer[3] : 0;
+
+  return (strncmp(Buffer, HashString, 4) == 0);
+}
+
+
+BOOLEAN
+CmiCompareHashI(PUNICODE_STRING KeyName,
+               PCHAR HashString)
+{
+  CHAR Buffer[4];
+
+  Buffer[0] = (KeyName->Length >= 2) ? (CHAR)KeyName->Buffer[0] : 0;
+  Buffer[1] = (KeyName->Length >= 4) ? (CHAR)KeyName->Buffer[1] : 0;
+  Buffer[2] = (KeyName->Length >= 6) ? (CHAR)KeyName->Buffer[2] : 0;
+  Buffer[3] = (KeyName->Length >= 8) ? (CHAR)KeyName->Buffer[3] : 0;
+
+  return (_strnicmp(Buffer, HashString, 4) == 0);
+}
+
+
+BOOLEAN
+CmiCompareKeyNames(PUNICODE_STRING KeyName,
+                  PKEY_CELL KeyCell)
+{
+  PWCHAR UnicodeName;
+  USHORT i;
+
+  DPRINT("Flags: %hx\n", KeyCell->Flags);
+
+  if (KeyCell->Flags & REG_KEY_NAME_PACKED)
+    {
+      if (KeyName->Length != KeyCell->NameSize * sizeof(WCHAR))
+       return FALSE;
+
+      for (i = 0; i < KeyCell->NameSize; i++)
+       {
+         if (KeyName->Buffer[i] != (WCHAR)KeyCell->Name[i])
+           return FALSE;
+       }
+    }
+  else
+    {
+      if (KeyName->Length != KeyCell->NameSize)
+       return FALSE;
+
+      UnicodeName = (PWCHAR)KeyCell->Name;
+      for (i = 0; i < KeyCell->NameSize / sizeof(WCHAR); i++)
+       {
+         if (KeyName->Buffer[i] != UnicodeName[i])
+           return FALSE;
+       }
+    }
+
+  return TRUE;
+}
+
+
+BOOLEAN
+CmiCompareKeyNamesI(PUNICODE_STRING KeyName,
+                   PKEY_CELL KeyCell)
+{
+  PWCHAR UnicodeName;
+  USHORT i;
+
+  DPRINT("Flags: %hx\n", KeyCell->Flags);
+
+  if (KeyCell->Flags & REG_KEY_NAME_PACKED)
+    {
+      if (KeyName->Length != KeyCell->NameSize * sizeof(WCHAR))
+       return FALSE;
+
+      for (i = 0; i < KeyCell->NameSize; i++)
+       {
+         if (RtlUpcaseUnicodeChar(KeyName->Buffer[i]) !=
+             RtlUpcaseUnicodeChar((WCHAR)KeyCell->Name[i]))
+           return FALSE;
+       }
+    }
+  else
+    {
+      if (KeyName->Length != KeyCell->NameSize)
+       return FALSE;
+
+      UnicodeName = (PWCHAR)KeyCell->Name;
+      for (i = 0; i < KeyCell->NameSize / sizeof(WCHAR); i++)
+       {
+         if (RtlUpcaseUnicodeChar(KeyName->Buffer[i]) !=
+             RtlUpcaseUnicodeChar(UnicodeName[i]))
+           return FALSE;
+       }
+    }
+
+  return TRUE;
+}
+
+
+NTSTATUS
+CmiCopyKey (PREGISTRY_HIVE DstHive,
+           PKEY_CELL DstKeyCell,
+           PREGISTRY_HIVE SrcHive,
+           PKEY_CELL SrcKeyCell)
+{
+  PKEY_CELL NewKeyCell;
+  ULONG NewKeyCellSize;
+  BLOCK_OFFSET NewKeyCellOffset;
+  PHASH_TABLE_CELL NewHashTableCell;
+  ULONG NewHashTableSize;
+  BLOCK_OFFSET NewHashTableOffset;
+  ULONG i;
+  NTSTATUS Status;
+
+  DPRINT ("CmiCopyKey() called\n");
+
+  if (DstKeyCell == NULL)
+    {
+      /* Allocate and copy key cell */
+      NewKeyCellSize = sizeof(KEY_CELL) + SrcKeyCell->NameSize;
+      Status = CmiAllocateBlock (DstHive,
+                                (PVOID) &NewKeyCell,
+                                NewKeyCellSize,
+                                &NewKeyCellOffset);
+      if (!NT_SUCCESS(Status))
+       {
+         DPRINT1 ("CmiAllocateBlock() failed (Status %lx)\n", Status);
+         return Status;
+       }
+      if (NewKeyCell == NULL)
+       {
+         DPRINT1 ("Failed to allocate a key cell\n");
+         return STATUS_INSUFFICIENT_RESOURCES;
+       }
+
+      RtlCopyMemory (NewKeyCell,
+                    SrcKeyCell,
+                    NewKeyCellSize);
+
+      DstHive->HiveHeader->RootKeyCell = NewKeyCellOffset;
+
+      /* Copy class name */
+      if (SrcKeyCell->ClassNameOffset != (BLOCK_OFFSET) -1)
+       {
+         PDATA_CELL SrcClassNameCell;
+         PDATA_CELL NewClassNameCell;
+         BLOCK_OFFSET NewClassNameOffset;
+
+         SrcClassNameCell = CmiGetBlock (SrcHive, SrcKeyCell->ClassNameOffset, NULL),
+
+         NewKeyCell->ClassSize = SrcKeyCell->ClassSize;
+         Status = CmiAllocateBlock (DstHive,
+                                    (PVOID)&NewClassNameCell,
+                                    NewKeyCell->ClassSize,
+                                    &NewClassNameOffset);
+         if (!NT_SUCCESS(Status))
+           {
+             DPRINT1 ("CmiAllocateBlock() failed (Status %lx)\n", Status);
+             return Status;
+           }
+
+         RtlCopyMemory (NewClassNameCell,
+                        SrcClassNameCell,
+                        NewKeyCell->ClassSize);
+         NewKeyCell->ClassNameOffset = NewClassNameOffset;
+       }
+    }
+  else
+    {
+      NewKeyCell = DstKeyCell;
+    }
+
+  /* Allocate hash table */
+  if (SrcKeyCell->NumberOfSubKeys > 0)
+    {
+      NewHashTableSize = ROUND_UP(SrcKeyCell->NumberOfSubKeys + 1, 4) - 1;
+      Status = CmiAllocateHashTableBlock (DstHive,
+                                         &NewHashTableCell,
+                                         &NewHashTableOffset,
+                                         NewHashTableSize);
+      if (!NT_SUCCESS(Status))
+       {
+         DPRINT1 ("CmiAllocateHashTableBlock() failed (Status %lx)\n", Status);
+         return Status;
+       }
+      NewKeyCell->HashTableOffset = NewHashTableOffset;
+    }
+
+  /* Allocate and copy value list and values */
+  if (SrcKeyCell->NumberOfValues != 0)
+    {
+      PVALUE_LIST_CELL NewValueListCell;
+      PVALUE_LIST_CELL SrcValueListCell;
+      PVALUE_CELL NewValueCell;
+      PVALUE_CELL SrcValueCell;
+      PDATA_CELL SrcValueDataCell;
+      PDATA_CELL NewValueDataCell;
+      BLOCK_OFFSET ValueCellOffset;
+      BLOCK_OFFSET ValueDataCellOffset;
+      ULONG NewValueListCellSize;
+      ULONG NewValueCellSize;
+
+
+      NewValueListCellSize =
+       ROUND_UP(SrcKeyCell->NumberOfValues, 4) * sizeof(BLOCK_OFFSET);
+      Status = CmiAllocateBlock (DstHive,
+                                (PVOID)&NewValueListCell,
+                                NewValueListCellSize,
+                                &NewKeyCell->ValuesOffset);
+      if (!NT_SUCCESS(Status))
+       {
+         DPRINT1 ("CmiAllocateBlock() failed (Status %lx)\n", Status);
+         return Status;
+       }
+
+      RtlZeroMemory (NewValueListCell,
+                    NewValueListCellSize);
+
+      /* Copy values */
+      SrcValueListCell = CmiGetBlock (SrcHive, SrcKeyCell->ValuesOffset, NULL);
+      for (i = 0; i < SrcKeyCell->NumberOfValues; i++)
+       {
+         /* Copy value cell */
+         SrcValueCell = CmiGetBlock (SrcHive, SrcValueListCell->Values[i], NULL);
+
+         NewValueCellSize = sizeof(VALUE_CELL) + SrcValueCell->NameSize;
+         Status = CmiAllocateBlock (DstHive,
+                                    (PVOID*) &NewValueCell,
+                                    NewValueCellSize,
+                                    &ValueCellOffset);
+         if (!NT_SUCCESS(Status))
+           {
+             DPRINT1 ("CmiAllocateBlock() failed (Status %lx)\n", Status);
+             return Status;
+           }
+
+         NewValueListCell->Values[i] = ValueCellOffset;
+         RtlCopyMemory (NewValueCell,
+                        SrcValueCell,
+                        NewValueCellSize);
+
+         /* Copy value data cell */
+         if (SrcValueCell->DataSize > (LONG) sizeof(PVOID))
+           {
+             SrcValueDataCell = CmiGetBlock (SrcHive, SrcValueCell->DataOffset, NULL);
+
+             Status = CmiAllocateBlock (DstHive,
+                                        (PVOID*) &NewValueDataCell,
+                                        SrcValueCell->DataSize,
+                                        &ValueDataCellOffset);
+             if (!NT_SUCCESS(Status))
+               {
+                 DPRINT1 ("CmiAllocateBlock() failed (Status %lx)\n", Status);
+                 return Status;
+               }
+             RtlCopyMemory (NewValueDataCell,
+                            SrcValueDataCell,
+                            SrcValueCell->DataSize);
+             NewValueCell->DataOffset = ValueDataCellOffset;
+           }
+       }
+    }
+
+  /* Copy subkeys */
+  if (SrcKeyCell->NumberOfSubKeys > 0)
+    {
+      PHASH_TABLE_CELL SrcHashTableCell;
+      PKEY_CELL SrcSubKeyCell;
+      PKEY_CELL NewSubKeyCell;
+      ULONG NewSubKeyCellSize;
+      BLOCK_OFFSET NewSubKeyCellOffset;
+      PHASH_RECORD SrcHashRecord;
+
+      SrcHashTableCell = CmiGetBlock (SrcHive,
+                                     SrcKeyCell->HashTableOffset,
+                                     NULL);
+
+      for (i = 0; i < SrcKeyCell->NumberOfSubKeys; i++)
+       {
+         SrcHashRecord = &SrcHashTableCell->Table[i];
+         SrcSubKeyCell = CmiGetBlock (SrcHive, SrcHashRecord->KeyOffset, NULL);
+
+         /* Allocate and copy key cell */
+         NewSubKeyCellSize = sizeof(KEY_CELL) + SrcSubKeyCell->NameSize;
+         Status = CmiAllocateBlock (DstHive,
+                                    (PVOID)&NewSubKeyCell,
+                                    NewSubKeyCellSize,
+                                    &NewSubKeyCellOffset);
+         if (!NT_SUCCESS(Status))
+           {
+             DPRINT1 ("CmiAllocateBlock() failed (Status %lx)\n", Status);
+             return Status;
+           }
+         if (NewKeyCell == NULL)
+           {
+             DPRINT1 ("Failed to allocate a sub key cell\n");
+             return STATUS_INSUFFICIENT_RESOURCES;
+           }
+
+         NewHashTableCell->Table[i].KeyOffset = NewSubKeyCellOffset;
+         NewHashTableCell->Table[i].HashValue = SrcHashRecord->HashValue;
+
+         RtlCopyMemory (NewSubKeyCell,
+                        SrcSubKeyCell,
+                        NewSubKeyCellSize);
+
+         /* Copy class name */
+         if (SrcSubKeyCell->ClassNameOffset != (BLOCK_OFFSET) -1)
+           {
+             PDATA_CELL SrcClassNameCell;
+             PDATA_CELL NewClassNameCell;
+             BLOCK_OFFSET NewClassNameOffset;
+
+             SrcClassNameCell = CmiGetBlock (SrcHive,
+                                             SrcSubKeyCell->ClassNameOffset,
+                                             NULL),
+
+             NewSubKeyCell->ClassSize = SrcSubKeyCell->ClassSize;
+             Status = CmiAllocateBlock (DstHive,
+                                        (PVOID)&NewClassNameCell,
+                                        NewSubKeyCell->ClassSize,
+                                        &NewClassNameOffset);
+             if (!NT_SUCCESS(Status))
+               {
+                 DPRINT1 ("CmiAllocateBlock() failed (Status %lx)\n", Status);
+                 return Status;
+               }
+
+             NewSubKeyCell->ClassNameOffset = NewClassNameOffset;
+             RtlCopyMemory (NewClassNameCell,
+                            SrcClassNameCell,
+                            NewSubKeyCell->ClassSize);
+           }
+
+         /* Copy subkey data and subkeys */
+         Status = CmiCopyKey (DstHive,
+                              NewSubKeyCell,
+                              SrcHive,
+                              SrcSubKeyCell);
+         if (!NT_SUCCESS(Status))
+           {
+             DPRINT1 ("CmiAllocateBlock() failed (Status %lx)\n", Status);
+             return Status;
+           }
+       }
+    }
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+CmiSaveTempHive (PREGISTRY_HIVE Hive,
+                HANDLE FileHandle)
+{
+  IO_STATUS_BLOCK IoStatusBlock;
+  LARGE_INTEGER FileOffset;
+  ULONG BlockIndex;
+  PVOID BlockPtr;
+  NTSTATUS Status;
+
+  DPRINT ("CmiSaveTempHive() called\n");
+
+  Hive->HiveHeader->Checksum = CmiCalcChecksum ((PULONG)Hive->HiveHeader);
+
+  /* Write hive block */
+  FileOffset.QuadPart = 0ULL;
+  Status = NtWriteFile (FileHandle,
+                       NULL,
+                       NULL,
+                       NULL,
+                       &IoStatusBlock,
+                       Hive->HiveHeader,
+                       sizeof(HIVE_HEADER),
+                       &FileOffset,
+                       NULL);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT1 ("NtWriteFile() failed (Status %lx)\n", Status);
+      return Status;
+    }
+
+  DPRINT ("Saving %lu blocks\n", Hive->BlockListSize);
+  for (BlockIndex = 0; BlockIndex < Hive->BlockListSize; BlockIndex++)
+    {
+      BlockPtr = Hive->BlockList[BlockIndex];
+      DPRINT ("BlockPtr %p\n", BlockPtr);
+
+      FileOffset.QuadPart = (ULONGLONG)(BlockIndex + 1) * 4096ULL;
+      DPRINT ("File offset %I64x\n", FileOffset.QuadPart);
+
+      /* Write hive block */
+      Status = NtWriteFile (FileHandle,
+                           NULL,
+                           NULL,
+                           NULL,
+                           &IoStatusBlock,
+                           BlockPtr,
+                           REG_BLOCK_SIZE,
+                           &FileOffset,
+                           NULL);
+      if (!NT_SUCCESS(Status))
+       {
+         DPRINT1 ("NtWriteFile() failed (Status %lx)\n", Status);
+         return Status;
+       }
+    }
+
+  Status = NtFlushBuffersFile (FileHandle,
+                              &IoStatusBlock);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT1 ("NtFlushBuffersFile() failed (Status %lx)\n", Status);
+    }
+
+  DPRINT ("CmiSaveTempHive() done\n");
+
+  return Status;
+}
+
 /* EOF */