Revert r18064 and correctly set the cell size for hash cells.
[reactos.git] / reactos / ntoskrnl / cm / regfile.c
index 3a34df4..5b4abda 100644 (file)
@@ -1,59 +1,50 @@
-/*
- * COPYRIGHT:        See COPYING in the top level directory
- * PROJECT:          ReactOS kernel
- * FILE:             ntoskrnl/cm/regfile.c
- * PURPOSE:          Registry file manipulation routines
- * UPDATE HISTORY:
-*/
-
-#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>
-
+/* $Id$
+ *
+ * COPYRIGHT:       See COPYING in the top level directory
+ * PROJECT:         ReactOS kernel
+ * FILE:            ntoskrnl/cm/regfile.c
+ * PURPOSE:         Registry file manipulation routines
+ *
+ * PROGRAMMERS:     No programmer listed.
+ */
+
+#include <ntoskrnl.h>
 #define NDEBUG
 #include <internal/debug.h>
 
 #include "cm.h"
 
 
-#define REG_BLOCK_SIZE                4096
-#define REG_HBIN_DATA_OFFSET          32
-#define REG_BIN_ID                    0x6e696268
-#define REG_INIT_BLOCK_LIST_SIZE      32
-#define REG_INIT_HASH_TABLE_SIZE      3
-#define REG_EXTEND_HASH_TABLE_SIZE    4
-#define REG_VALUE_LIST_CELL_MULTIPLE  4
-#define REG_KEY_CELL_ID               0x6b6e
-#define REG_HASH_TABLE_BLOCK_ID       0x666c
-#define REG_VALUE_CELL_ID             0x6b76
-#define REG_KEY_CELL_TYPE             0x20
-#define REG_ROOT_KEY_CELL_TYPE        0x2c
-#define REG_HIVE_ID                   0x66676572
+/* uncomment to enable hive checks (incomplete and probably buggy) */
+// #define HIVE_CHECK
+
+/* LOCAL MACROS *************************************************************/
 
-extern PREGISTRY_HIVE  CmiVolatileHive;
+#define ABS_VALUE(V) (((V) < 0) ? -(V) : (V))
 
 BOOLEAN CmiDoVerify = FALSE;
 
+static ULONG
+CmiCalcChecksum(PULONG Buffer);
+
+/* FUNCTIONS ****************************************************************/
+
 VOID
 CmiCreateDefaultHiveHeader(PHIVE_HEADER Header)
 {
-  assert(Header);
+  ASSERT(Header);
   RtlZeroMemory(Header, sizeof(HIVE_HEADER));
   Header->BlockId = REG_HIVE_ID;
-  Header->DateModified.dwLowDateTime = 0;
-  Header->DateModified.dwHighDateTime = 0;
-  Header->Version = 1;
+  Header->UpdateCounter1 = 0;
+  Header->UpdateCounter2 = 0;
+  Header->DateModified.u.LowPart = 0;
+  Header->DateModified.u.HighPart = 0;
   Header->Unused3 = 1;
   Header->Unused4 = 3;
   Header->Unused5 = 0;
   Header->Unused6 = 1;
   Header->Unused7 = 1;
-  Header->RootKeyCell = 0;
+  Header->RootKeyOffset = (BLOCK_OFFSET)-1;
   Header->BlockSize = REG_BLOCK_SIZE;
   Header->Unused6 = 1;
   Header->Checksum = 0;
@@ -61,31 +52,31 @@ CmiCreateDefaultHiveHeader(PHIVE_HEADER Header)
 
 
 VOID
-CmiCreateDefaultBinCell(PHBIN BinCell)
+CmiCreateDefaultBinHeader(PHBIN BinHeader)
 {
-  assert(BinCell);
-  RtlZeroMemory(BinCell, sizeof(HBIN));
-  BinCell->BlockId = REG_BIN_ID;
-  BinCell->DateModified.dwLowDateTime = 0;
-  BinCell->DateModified.dwHighDateTime = 0;
-  BinCell->BlockSize = REG_BLOCK_SIZE;
+  ASSERT(BinHeader);
+  RtlZeroMemory(BinHeader, sizeof(HBIN));
+  BinHeader->HeaderId = REG_BIN_ID;
+  BinHeader->DateModified.u.LowPart = 0;
+  BinHeader->DateModified.u.HighPart = 0;
+  BinHeader->BinSize = REG_BLOCK_SIZE;
 }
 
 
 VOID
 CmiCreateDefaultRootKeyCell(PKEY_CELL RootKeyCell)
 {
-  assert(RootKeyCell);
+  ASSERT(RootKeyCell);
   RtlZeroMemory(RootKeyCell, sizeof(KEY_CELL));
   RootKeyCell->CellSize = -sizeof(KEY_CELL);
   RootKeyCell->Id = REG_KEY_CELL_ID;
-  RootKeyCell->Type = REG_ROOT_KEY_CELL_TYPE;
-  ZwQuerySystemTime((PTIME) &RootKeyCell->LastWriteTime);
+  RootKeyCell->Flags = REG_KEY_ROOT_CELL | REG_KEY_NAME_PACKED;
+  KeQuerySystemTime(&RootKeyCell->LastWriteTime);
   RootKeyCell->ParentKeyOffset = 0;
   RootKeyCell->NumberOfSubKeys = 0;
   RootKeyCell->HashTableOffset = -1;
   RootKeyCell->NumberOfValues = 0;
-  RootKeyCell->ValuesOffset = -1;
+  RootKeyCell->ValueListOffset = -1;
   RootKeyCell->SecurityKeyOffset = 0;
   RootKeyCell->ClassNameOffset = -1;
   RootKeyCell->NameSize = 0;
@@ -94,30 +85,30 @@ CmiCreateDefaultRootKeyCell(PKEY_CELL RootKeyCell)
 
 
 VOID
-CmiVerifyBinCell(PHBIN BinCell)
+CmiVerifyBinHeader(PHBIN BinHeader)
 {
   if (CmiDoVerify)
     {
 
-  assert(BinCell);
+  ASSERT(BinHeader);
 
-  if (BinCell->BlockId != REG_BIN_ID)
+  if (BinHeader->HeaderId != REG_BIN_ID)
     {
-      DbgPrint("BlockId is %.08x (should be %.08x)\n",
-        BinCell->BlockId, REG_BIN_ID);
-      assert(BinCell->BlockId == REG_BIN_ID);
+      DbgPrint("Bin header ID is %.08x (should be %.08x)\n",
+        BinHeader->HeaderId, REG_BIN_ID);
+      ASSERT(BinHeader->HeaderId == REG_BIN_ID);
     }
 
-  //BinCell->DateModified.dwLowDateTime
+  //BinHeader->DateModified.dwLowDateTime
 
-  //BinCell->DateModified.dwHighDateTime
+  //BinHeader->DateModified.dwHighDateTime
 
-  
-  if (BinCell->BlockSize != REG_BLOCK_SIZE)
+
+  if (BinHeader->BinSize != REG_BLOCK_SIZE)
     {
-      DbgPrint("BlockSize is %.08x (should be %.08x)\n",
-        BinCell->BlockSize, REG_BLOCK_SIZE);
-      assert(BinCell->BlockSize == REG_BLOCK_SIZE);
+      DbgPrint("BinSize is %.08x (should be a multiple of %.08x)\n",
+        BinHeader->BinSize, REG_BLOCK_SIZE);
+      ASSERT(BinHeader->BinSize % REG_BLOCK_SIZE == 0);
     }
 
     }
@@ -130,29 +121,23 @@ CmiVerifyKeyCell(PKEY_CELL KeyCell)
   if (CmiDoVerify)
     {
 
-  assert(KeyCell);
+  ASSERT(KeyCell);
 
   if (KeyCell->CellSize == 0)
     {
       DbgPrint("CellSize is %d (must not be 0)\n",
         KeyCell->CellSize);
-      assert(KeyCell->CellSize != 0);
+      ASSERT(KeyCell->CellSize != 0);
     }
 
   if (KeyCell->Id != REG_KEY_CELL_ID)
     {
       DbgPrint("Id is %.08x (should be %.08x)\n",
         KeyCell->Id, REG_KEY_CELL_ID);
-      assert(KeyCell->Id == REG_KEY_CELL_ID);
+      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;
 
@@ -160,14 +145,14 @@ CmiVerifyKeyCell(PKEY_CELL KeyCell)
     {
       DbgPrint("ParentKeyOffset is %d (must not be < 0)\n",
         KeyCell->ParentKeyOffset);
-      assert(KeyCell->ParentKeyOffset >= 0);
+      ASSERT(KeyCell->ParentKeyOffset >= 0);
     }
 
   if (KeyCell->NumberOfSubKeys < 0)
     {
       DbgPrint("NumberOfSubKeys is %d (must not be < 0)\n",
         KeyCell->NumberOfSubKeys);
-      assert(KeyCell->NumberOfSubKeys >= 0);
+      ASSERT(KeyCell->NumberOfSubKeys >= 0);
     }
 
   //KeyCell->HashTableOffset;
@@ -176,7 +161,7 @@ CmiVerifyKeyCell(PKEY_CELL KeyCell)
     {
       DbgPrint("NumberOfValues is %d (must not be < 0)\n",
         KeyCell->NumberOfValues);
-      assert(KeyCell->NumberOfValues >= 0);
+      ASSERT(KeyCell->NumberOfValues >= 0);
     }
 
   //KeyCell->ValuesOffset = -1;
@@ -185,7 +170,7 @@ CmiVerifyKeyCell(PKEY_CELL KeyCell)
     {
       DbgPrint("SecurityKeyOffset is %d (must not be < 0)\n",
         KeyCell->SecurityKeyOffset);
-      assert(KeyCell->SecurityKeyOffset >= 0);
+      ASSERT(KeyCell->SecurityKeyOffset >= 0);
     }
 
   //KeyCell->ClassNameOffset = -1;
@@ -206,11 +191,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)));
     }
 
     }
@@ -223,20 +208,20 @@ CmiVerifyValueCell(PVALUE_CELL ValueCell)
   if (CmiDoVerify)
     {
 
-  assert(ValueCell);
+  ASSERT(ValueCell);
 
   if (ValueCell->CellSize == 0)
     {
       DbgPrint("CellSize is %d (must not be 0)\n",
         ValueCell->CellSize);
-      assert(ValueCell->CellSize != 0);
+      ASSERT(ValueCell->CellSize != 0);
     }
 
   if (ValueCell->Id != REG_VALUE_CELL_ID)
     {
       DbgPrint("Id is %.08x (should be %.08x)\n",
         ValueCell->Id, REG_VALUE_CELL_ID);
-      assert(ValueCell->Id == REG_VALUE_CELL_ID);
+      ASSERT(ValueCell->Id == REG_VALUE_CELL_ID);
     }
 
   //ValueCell->NameSize;
@@ -260,7 +245,7 @@ CmiVerifyValueListCell(PVALUE_LIST_CELL ValueListCell)
     {
       DbgPrint("CellSize is %d (must not be 0)\n",
         ValueListCell->CellSize);
-      assert(ValueListCell->CellSize != 0);
+      ASSERT(ValueListCell->CellSize != 0);
     }
 
     }
@@ -277,21 +262,21 @@ CmiVerifyKeyObject(PKEY_OBJECT KeyObject)
     {
       DbgPrint("RegistryHive is NULL (must not be NULL)\n",
         KeyObject->RegistryHive);
-      assert(KeyObject->RegistryHive != NULL);
+      ASSERT(KeyObject->RegistryHive != NULL);
     }
 
   if (KeyObject->KeyCell == NULL)
     {
       DbgPrint("KeyCell is NULL (must not be NULL)\n",
         KeyObject->KeyCell);
-      assert(KeyObject->KeyCell != NULL);
+      ASSERT(KeyObject->KeyCell != NULL);
     }
 
   if (KeyObject->ParentKey == NULL)
     {
       DbgPrint("ParentKey is NULL (must not be NULL)\n",
         KeyObject->ParentKey);
-      assert(KeyObject->ParentKey != NULL);
+      ASSERT(KeyObject->ParentKey != NULL);
     }
 
     }
@@ -309,45 +294,45 @@ CmiVerifyHiveHeader(PHIVE_HEADER Header)
       DbgPrint("BlockId is %.08x (must be %.08x)\n",
         Header->BlockId,
         REG_HIVE_ID);
-      assert(Header->BlockId == REG_HIVE_ID);
+      ASSERT(Header->BlockId == REG_HIVE_ID);
     }
 
   if (Header->Unused3 != 1)
     {
       DbgPrint("Unused3 is %.08x (must be 1)\n",
         Header->Unused3);
-      assert(Header->Unused3 == 1);
+      ASSERT(Header->Unused3 == 1);
     }
 
   if (Header->Unused4 != 3)
     {
       DbgPrint("Unused4 is %.08x (must be 3)\n",
         Header->Unused4);
-      assert(Header->Unused4 == 3);
+      ASSERT(Header->Unused4 == 3);
     }
 
   if (Header->Unused5 != 0)
     {
       DbgPrint("Unused5 is %.08x (must be 0)\n",
         Header->Unused5);
-      assert(Header->Unused5 == 0);
+      ASSERT(Header->Unused5 == 0);
     }
 
   if (Header->Unused6 != 1)
     {
       DbgPrint("Unused6 is %.08x (must be 1)\n",
         Header->Unused6);
-      assert(Header->Unused6 == 1);
+      ASSERT(Header->Unused6 == 1);
     }
 
   if (Header->Unused7 != 1)
     {
       DbgPrint("Unused7 is %.08x (must be 1)\n",
         Header->Unused7);
-      assert(Header->Unused7 == 1);
+      ASSERT(Header->Unused7 == 1);
     }
 
-    }  
+    }
 }
 
 
@@ -363,63 +348,7 @@ CmiVerifyRegistryHive(PREGISTRY_HIVE RegistryHive)
 }
 
 
-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 = 0; i < 50; i++)
-    {
-      // Block offset of this bin
-      BinCell->BlockOffset = (2 + i) * REG_BLOCK_SIZE;
-
-      FileOffset.u.HighPart = 0;
-      FileOffset.u.LowPart   = (2 + i) * REG_BLOCK_SIZE;
-
-      Status = ZwWriteFile(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;
-}
-
-
-NTSTATUS
+static NTSTATUS
 CmiCreateNewRegFile(HANDLE FileHandle)
 {
   IO_STATUS_BLOCK IoStatusBlock;
@@ -427,279 +356,430 @@ CmiCreateNewRegFile(HANDLE FileHandle)
   PHIVE_HEADER HiveHeader;
   PKEY_CELL RootKeyCell;
   NTSTATUS Status;
-  PHBIN BinCell;
-  PCHAR tBuf;
-  
-  tBuf = (PCHAR) ExAllocatePool(NonPagedPool, 2 * REG_BLOCK_SIZE);
-  if (tBuf == NULL)
+  PHBIN BinHeader;
+  PCHAR Buffer;
+
+  Buffer = (PCHAR) ExAllocatePool(NonPagedPool, 2 * REG_BLOCK_SIZE);
+  if (Buffer == NULL)
     return STATUS_INSUFFICIENT_RESOURCES;
-    
-  HiveHeader = (PHIVE_HEADER) tBuf;
-  BinCell = (PHBIN) ((ULONG_PTR) tBuf + REG_BLOCK_SIZE);
-  RootKeyCell = (PKEY_CELL) ((ULONG_PTR) tBuf + REG_BLOCK_SIZE + REG_HBIN_DATA_OFFSET);
-  FreeCell = (PCELL_HEADER) ((ULONG_PTR) tBuf + REG_BLOCK_SIZE + REG_HBIN_DATA_OFFSET + sizeof(KEY_CELL));
+
+  RtlZeroMemory (Buffer,
+                2 * REG_BLOCK_SIZE);
+
+  HiveHeader = (PHIVE_HEADER)Buffer;
+  BinHeader = (PHBIN)((ULONG_PTR)Buffer + REG_BLOCK_SIZE);
+  RootKeyCell = (PKEY_CELL)((ULONG_PTR)Buffer + REG_BLOCK_SIZE + REG_HBIN_DATA_OFFSET);
+  FreeCell = (PCELL_HEADER)((ULONG_PTR)Buffer + REG_BLOCK_SIZE + REG_HBIN_DATA_OFFSET + sizeof(KEY_CELL));
 
   CmiCreateDefaultHiveHeader(HiveHeader);
-  CmiCreateDefaultBinCell(BinCell);
+  CmiCreateDefaultBinHeader(BinHeader);
   CmiCreateDefaultRootKeyCell(RootKeyCell);
 
-  // First block
-  BinCell->BlockOffset = 0;
+  /* First block */
+  BinHeader->BinOffset = 0;
 
-  // Offset to root key block
-  HiveHeader->RootKeyCell = REG_HBIN_DATA_OFFSET;
+  /* Offset to root key block */
+  HiveHeader->RootKeyOffset = REG_HBIN_DATA_OFFSET;
 
-  // The rest of the block is free
+  /* The rest of the block is free */
   FreeCell->CellSize = REG_BLOCK_SIZE - (REG_HBIN_DATA_OFFSET + sizeof(KEY_CELL));
 
   Status = ZwWriteFile(FileHandle,
-    NULL,
-    NULL,
-    NULL,
-    &IoStatusBlock,
-    tBuf,
-    2 * REG_BLOCK_SIZE,
-    0,
-    NULL);
+                      NULL,
+                      NULL,
+                      NULL,
+                      &IoStatusBlock,
+                      Buffer,
+                      2 * REG_BLOCK_SIZE,
+                      0,
+                      NULL);
 
-  ExFreePool(tBuf);
+  ExFreePool(Buffer);
 
-  assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
-
-#if 1
-  if (NT_SUCCESS(Status))
+  if (!NT_SUCCESS(Status))
     {
-  CmiPopulateHive(FileHandle);
+      return(Status);
     }
-#endif
 
-  return Status;
+  Status = ZwFlushBuffersFile(FileHandle,
+                             &IoStatusBlock);
+
+  return(Status);
 }
 
 
-NTSTATUS
-CmiInitPermanentRegistryHive(PREGISTRY_HIVE RegistryHive,
-  PWSTR Filename,
-  BOOLEAN CreateNew)
+#ifdef HIVE_CHECK
+static NTSTATUS
+CmiCheckAndFixHive(PREGISTRY_HIVE RegistryHive)
 {
   OBJECT_ATTRIBUTES ObjectAttributes;
   FILE_STANDARD_INFORMATION fsi;
-  PCELL_HEADER FreeBlock;
+  IO_STATUS_BLOCK IoStatusBlock;
+  HANDLE HiveHandle = INVALID_HANDLE_VALUE;
+  HANDLE LogHandle = INVALID_HANDLE_VALUE;
+  PHIVE_HEADER HiveHeader = NULL;
+  PHIVE_HEADER LogHeader = NULL;
   LARGE_INTEGER FileOffset;
-  BLOCK_OFFSET BlockOffset;
-  ULONG CreateDisposition;
-  IO_STATUS_BLOCK IoSB;
-  HANDLE FileHandle;
-  DWORD FreeOffset;
+  ULONG FileSize;
+  ULONG BufferSize;
+  ULONG BitmapSize;
+  RTL_BITMAP BlockBitMap;
   NTSTATUS Status;
-  //BOOLEAN Success;
-  PHBIN tmpBin;
-  ULONG i, j;
 
-  /* Duplicate Filename */
-  Status = RtlCreateUnicodeString(&RegistryHive->Filename, Filename);
-  if (!NT_SUCCESS(Status))
-    return Status;
+  DPRINT("CmiCheckAndFixHive() called\n");
 
+  /* Try to open the hive file */
   InitializeObjectAttributes(&ObjectAttributes,
-                            &RegistryHive->Filename,
-                            0,
+                            &RegistryHive->HiveFileName,
+                            OBJ_CASE_INSENSITIVE,
                             NULL,
                             NULL);
 
-  if (CreateNew)
-    CreateDisposition = FILE_OPEN_IF;
-  else
-    CreateDisposition = FILE_OPEN;
-
-  Status = NtCreateFile(&FileHandle,
-               FILE_ALL_ACCESS,
-               &ObjectAttributes,
-               &IoSB,
-               NULL,
-               FILE_ATTRIBUTE_NORMAL,
-               0,
-               CreateDisposition,
-               FILE_NON_DIRECTORY_FILE,
-               NULL,
-               0);
-
-  if ((CreateNew) && (IoSB.Information == FILE_CREATED))
+  Status = ZwCreateFile(&HiveHandle,
+                       FILE_READ_DATA | FILE_READ_ATTRIBUTES,
+                       &ObjectAttributes,
+                       &IoStatusBlock,
+                       NULL,
+                       FILE_ATTRIBUTE_NORMAL,
+                       0,
+                       FILE_OPEN,
+                       FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+                       NULL,
+                       0);
+  if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
     {
-      Status = CmiCreateNewRegFile(FileHandle);
+      return(STATUS_SUCCESS);
     }
-
   if (!NT_SUCCESS(Status))
     {
-      RtlFreeUnicodeString(&RegistryHive->Filename);
-      return Status;
+      DPRINT("ZwCreateFile() failed (Status %lx)\n", Status);
+      return(Status);
     }
 
-  Status = ObReferenceObjectByHandle(FileHandle,
-               FILE_ALL_ACCESS,
-               IoFileObjectType,
-               UserMode,
-               (PVOID*) &RegistryHive->FileObject,
-               NULL);
+  /* Try to open the log file */
+  InitializeObjectAttributes(&ObjectAttributes,
+                            &RegistryHive->LogFileName,
+                            OBJ_CASE_INSENSITIVE,
+                            NULL,
+                            NULL);
 
-  assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
+  Status = ZwCreateFile(&LogHandle,
+                       FILE_READ_DATA | FILE_READ_ATTRIBUTES,
+                       &ObjectAttributes,
+                       &IoStatusBlock,
+                       NULL,
+                       FILE_ATTRIBUTE_NORMAL,
+                       0,
+                       FILE_OPEN,
+                       FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+                       NULL,
+                       0);
+  if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
+    {
+      LogHandle = INVALID_HANDLE_VALUE;
+    }
+  else if (!NT_SUCCESS(Status))
+    {
+      DPRINT("ZwCreateFile() failed (Status %lx)\n", Status);
+      ZwClose(HiveHandle);
+      return(Status);
+    }
 
-  if (!NT_SUCCESS(Status))
+  /* Allocate hive header */
+  HiveHeader = ExAllocatePool(PagedPool,
+                             sizeof(HIVE_HEADER));
+  if (HiveHeader == NULL)
     {
-      ZwClose(FileHandle);
-      RtlFreeUnicodeString(&RegistryHive->Filename);
-      return Status;
+      DPRINT("ExAllocatePool() failed\n");
+      Status = STATUS_INSUFFICIENT_RESOURCES;
+      goto ByeBye;
     }
 
-  FileOffset.u.HighPart = 0;
-  FileOffset.u.LowPart = 0;
-  Status = ZwReadFile(FileHandle,
+  /* Read hive base block */
+  FileOffset.QuadPart = 0ULL;
+  Status = ZwReadFile(HiveHandle,
                      0,
                      0,
                      0,
-                     0,
-                     RegistryHive->HiveHeader,
+                     &IoStatusBlock,
+                     HiveHeader,
                      sizeof(HIVE_HEADER),
                      &FileOffset,
                      0);
-
-  assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
-
   if (!NT_SUCCESS(Status))
     {
-      ObDereferenceObject(RegistryHive->FileObject);
-      RtlFreeUnicodeString(&RegistryHive->Filename);
-      return Status;
+      DPRINT("ZwReadFile() failed (Status %lx)\n", Status);
+      goto ByeBye;
     }
 
-  Status = ZwQueryInformationFile(FileHandle,
-    &IoSB,
-    &fsi,
-    sizeof(fsi),
-    FileStandardInformation);
-
-  assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
-
-  if (!NT_SUCCESS(Status))
+  if (LogHandle == INVALID_HANDLE_VALUE)
     {
-      ObDereferenceObject(RegistryHive->FileObject);
-      RtlFreeUnicodeString(&RegistryHive->Filename);
-      return Status;
+      if (HiveHeader->Checksum != CmiCalcChecksum((PULONG)HiveHeader) ||
+         HiveHeader->UpdateCounter1 != HiveHeader->UpdateCounter2)
+       {
+         /* 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);
+       }
+
+      Status = STATUS_SUCCESS;
+      goto ByeBye;
     }
-#if 0
-  /* We have a reference to the file object so we don't need the handle anymore */
-  ZwClose(FileHandle);
-#endif
+  else
+    {
+      /* Allocate hive header */
+      LogHeader = ExAllocatePool(PagedPool,
+                                sizeof(HIVE_HEADER));
+      if (LogHeader == NULL)
+       {
+         DPRINT("ExAllocatePool() failed\n");
+         Status = STATUS_INSUFFICIENT_RESOURCES;
+         goto ByeBye;
+       }
+
+      /* Read log file header */
+      FileOffset.QuadPart = 0ULL;
+      Status = ZwReadFile(LogHandle,
+                         0,
+                         0,
+                         0,
+                         &IoStatusBlock,
+                         LogHeader,
+                         sizeof(HIVE_HEADER),
+                         &FileOffset,
+                         0);
+      if (!NT_SUCCESS(Status))
+       {
+         DPRINT("ZwReadFile() failed (Status %lx)\n", Status);
+         goto ByeBye;
+       }
 
-  RegistryHive->FileSize = fsi.EndOfFile.u.LowPart;
-  RegistryHive->BlockListSize = (RegistryHive->FileSize / 4096) - 1;
+      /* Check log file header integrity */
+      if (LogHeader->Checksum != CmiCalcChecksum((PULONG)LogHeader) ||
+          LogHeader->UpdateCounter1 != LogHeader->UpdateCounter2)
+       {
+         if (HiveHeader->Checksum != CmiCalcChecksum((PULONG)HiveHeader) ||
+             HiveHeader->UpdateCounter1 != HiveHeader->UpdateCounter2)
+           {
+             DPRINT("Hive file and log file are inconsistent!\n");
+             KEBUGCHECK(CONFIG_LIST_FAILED);
+           }
 
-  DPRINT("Space needed for block list describing hive: 0x%x\n",
-    sizeof(PHBIN *) * RegistryHive->BlockListSize);
+         /* Log file damaged but hive is okay */
+         Status = STATUS_SUCCESS;
+         goto ByeBye;
+       }
 
-  RegistryHive->BlockList = ExAllocatePool(NonPagedPool,
-         sizeof(PHBIN *) * RegistryHive->BlockListSize);
+      if (HiveHeader->UpdateCounter1 == HiveHeader->UpdateCounter2 &&
+         HiveHeader->UpdateCounter1 == LogHeader->UpdateCounter1)
+       {
+         /* Hive and log file are up-to-date */
+         Status = STATUS_SUCCESS;
+         goto ByeBye;
+       }
 
-  if (RegistryHive->BlockList == NULL)
-    {
-      ExFreePool(RegistryHive->BlockList);
-      ObDereferenceObject(RegistryHive->FileObject);
-      RtlFreeUnicodeString(&RegistryHive->Filename);
-      return STATUS_INSUFFICIENT_RESOURCES;
-    }
+      /*
+       * Hive needs an update!
+       */
 
-#if 0
-  /* Map hive into cache memory (readonly) (skip the base block) */
-       FileOffset.u.HighPart = 0;
-       FileOffset.u.LowPart = 4096;
-  Success = CcMapData(RegistryHive->FileObject,   /* File object */
-    &FileOffset,                                  /* File offset */
-    RegistryHive->FileSize - 4096,                /* Region length */
-    TRUE,                                         /* Wait if needed */
-    &RegistryHive->Bcb,                           /* OUT: Buffer Control Block */
-    (PVOID*) &RegistryHive->BlockList[0]);        /* OUT: Mapped data pointer */
+      /* Get file size */
+      Status = ZwQueryInformationFile(LogHandle,
+                                     &IoStatusBlock,
+                                     &fsi,
+                                     sizeof(fsi),
+                                     FileStandardInformation);
+      if (!NT_SUCCESS(Status))
+       {
+         DPRINT("ZwQueryInformationFile() failed (Status %lx)\n", Status);
+         goto ByeBye;
+       }
+      FileSize = fsi.EndOfFile.u.LowPart;
+
+      /* Calculate bitmap and block size */
+      BitmapSize = ROUND_UP((FileSize / REG_BLOCK_SIZE) - 1, sizeof(ULONG) * 8) / 8;
+      BufferSize = sizeof(HIVE_HEADER) +
+                         sizeof(ULONG) +
+                         BitmapSize;
+      BufferSize = ROUND_UP(BufferSize, REG_BLOCK_SIZE);
+
+      /* Reallocate log header block */
+      ExFreePool(LogHeader);
+      LogHeader = ExAllocatePool(PagedPool,
+                                BufferSize);
+      if (LogHeader == NULL)
+       {
+         DPRINT("ExAllocatePool() failed\n");
+         Status = STATUS_INSUFFICIENT_RESOURCES;
+         goto ByeBye;
+       }
 
-  assertmsg(Success, ("Success: %d\n", Success));
+      /* Read log file header */
+      FileOffset.QuadPart = 0ULL;
+      Status = ZwReadFile(LogHandle,
+                         0,
+                         0,
+                         0,
+                         &IoStatusBlock,
+                         LogHeader,
+                         BufferSize,
+                         &FileOffset,
+                         0);
+      if (!NT_SUCCESS(Status))
+       {
+         DPRINT("ZwReadFile() failed (Status %lx)\n", Status);
+         goto ByeBye;
+       }
 
-  if (!Success)
-    {
-      ExFreePool(RegistryHive->BlockList);
-      ObDereferenceObject(RegistryHive->FileObject);
-      RtlFreeUnicodeString(&RegistryHive->Filename);
-      return Status;
-    }
+      /* Initialize bitmap */
+      RtlInitializeBitMap(&BlockBitMap,
+                         (PVOID)((ULONG_PTR)LogHeader + REG_BLOCK_SIZE + sizeof(ULONG)),
+                         BitmapSize * 8);
 
-#else
+      /* FIXME: Update dirty blocks */
 
-  RegistryHive->BlockList[0] = ExAllocatePool(PagedPool,
-         RegistryHive->FileSize - 4096);
 
-  if (RegistryHive->BlockList == NULL)
-    {
-      ExFreePool(RegistryHive->BlockList);
-      ObDereferenceObject(RegistryHive->FileObject);
-      RtlFreeUnicodeString(&RegistryHive->Filename);
-      return STATUS_INSUFFICIENT_RESOURCES;
+      /* FIXME: Update hive header */
+
+
+      Status = STATUS_SUCCESS;
     }
 
-  FileOffset.u.HighPart = 0;
-  FileOffset.u.LowPart = 4096;
 
-  Status = ZwReadFile(FileHandle,
-               0,
-    0,
-    0,
-    0,
-               (PVOID) RegistryHive->BlockList[0],
-               RegistryHive->FileSize - 4096,
-               &FileOffset,
-    0);
+  /* Clean up the mess */
+ByeBye:
+  if (HiveHeader != NULL)
+    ExFreePool(HiveHeader);
+
+  if (LogHeader != NULL)
+    ExFreePool(LogHeader);
 
-  assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
+  if (LogHandle != INVALID_HANDLE_VALUE)
+    ZwClose(LogHandle);
 
+  ZwClose(HiveHandle);
+
+  return(Status);
+}
 #endif
 
-  RegistryHive->FreeListSize = 0;
-  RegistryHive->FreeListMax = 0;
-  RegistryHive->FreeList = NULL;
 
+NTSTATUS
+CmiImportHiveBins(PREGISTRY_HIVE Hive,
+                 PUCHAR ChunkPtr)
+{
+  BLOCK_OFFSET BlockOffset;
+  ULONG BlockIndex;
+  PHBIN Bin;
+  ULONG j;
+
+  BlockIndex = 0;
   BlockOffset = 0;
-  for (i = 0; i < RegistryHive->BlockListSize; i++)
+  while (BlockIndex < Hive->BlockListSize)
     {
-      RegistryHive->BlockList[i] = (PHBIN) (((ULONG_PTR) RegistryHive->BlockList[0]) + BlockOffset);
-      tmpBin = (PHBIN) (((ULONG_PTR) RegistryHive->BlockList[i]));
-      if (tmpBin->BlockId != REG_BIN_ID)
+      Bin = (PHBIN)((ULONG_PTR)ChunkPtr + BlockOffset);
+
+      if (Bin->HeaderId != REG_BIN_ID)
+       {
+         DPRINT1 ("Bad bin header id %x, offset %x\n", Bin->HeaderId, BlockOffset);
+         return STATUS_REGISTRY_CORRUPT;
+       }
+
+      ASSERTMSG("Bin size must be multiple of 4K\n",
+                (Bin->BinSize % REG_BLOCK_SIZE) == 0);
+
+      /* Allocate the hive block */
+      Hive->BlockList[BlockIndex].Bin = ExAllocatePool (PagedPool,
+                                                       Bin->BinSize);
+      if (Hive->BlockList[BlockIndex].Bin == NULL)
        {
-         DPRINT("Bad BlockId %x, offset %x\n", tmpBin->BlockId, BlockOffset);
-         KeBugCheck(0);
+         DPRINT1 ("ExAllocatePool() failed\n");
+         return STATUS_INSUFFICIENT_RESOURCES;
        }
+      Hive->BlockList[BlockIndex].Block = (PVOID)Hive->BlockList[BlockIndex].Bin;
 
-      assertmsg((tmpBin->BlockSize % 4096) == 0, ("BlockSize (0x%.08x) must be multiplum of 4K\n", tmpBin->BlockSize));
+      /* Import the Bin */
+      RtlCopyMemory (Hive->BlockList[BlockIndex].Bin,
+                    Bin,
+                    Bin->BinSize);
 
-      if (tmpBin->BlockSize > 4096)
+      if (Bin->BinSize > REG_BLOCK_SIZE)
        {
-         for (j = 1; j < tmpBin->BlockSize / 4096; j++)
+         for (j = 1; j < Bin->BinSize / REG_BLOCK_SIZE; j++)
            {
-             RegistryHive->BlockList[i + j] = RegistryHive->BlockList[i];
+             Hive->BlockList[BlockIndex + j].Bin = Hive->BlockList[BlockIndex].Bin;
+             Hive->BlockList[BlockIndex + j].Block =
+               (PVOID)((ULONG_PTR)Hive->BlockList[BlockIndex].Bin + (j * REG_BLOCK_SIZE));
            }
-         i = i + j - 1;
        }
 
+      BlockIndex += Bin->BinSize / REG_BLOCK_SIZE;
+      BlockOffset += Bin->BinSize;
+    }
+
+  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].Bin == NULL)
+       continue;
+
+      if (Hive->BlockList[i].Bin != Bin)
+       {
+         Bin = Hive->BlockList[i].Bin;
+         ExFreePool (Hive->BlockList[i].Bin);
+       }
+      Hive->BlockList[i].Bin = NULL;
+      Hive->BlockList[i].Block = 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].Bin;
+
       /* Search free blocks and add to list */
       FreeOffset = REG_HBIN_DATA_OFFSET;
-      while (FreeOffset < tmpBin->BlockSize)
+      while (FreeOffset < Bin->BinSize)
        {
-         FreeBlock = (PCELL_HEADER) ((ULONG_PTR) RegistryHive->BlockList[i] + FreeOffset);
+         FreeBlock = (PCELL_HEADER) ((ULONG_PTR) Bin + FreeOffset);
          if (FreeBlock->CellSize > 0)
            {
-             Status = CmiAddFree(RegistryHive,
+             Status = CmiAddFree(Hive,
                                  FreeBlock,
-                                 RegistryHive->BlockList[i]->BlockOffset + FreeOffset);
+                                 Bin->BinOffset + FreeOffset,
+                                 FALSE);
 
              if (!NT_SUCCESS(Status))
                {
-                 /* FIXME: */
-                 assert(FALSE);
+                 return Status;
                }
 
              FreeOffset += FreeBlock->CellSize;
@@ -709,1213 +789,3518 @@ CmiInitPermanentRegistryHive(PREGISTRY_HIVE RegistryHive,
              FreeOffset -= FreeBlock->CellSize;
            }
        }
-      BlockOffset += tmpBin->BlockSize;
+
+      BlockIndex += Bin->BinSize / REG_BLOCK_SIZE;
+      BlockOffset += Bin->BinSize;
     }
 
   return STATUS_SUCCESS;
 }
 
 
-NTSTATUS
-CmiInitVolatileRegistryHive(PREGISTRY_HIVE RegistryHive)
+VOID
+CmiFreeHiveFreeCellList(PREGISTRY_HIVE Hive)
 {
-  PKEY_CELL RootKeyCell;
+  ExFreePool (Hive->FreeList);
+  ExFreePool (Hive->FreeListOffset);
 
-  RegistryHive->Flags |= HIVE_VOLATILE;
+  Hive->FreeListSize = 0;
+  Hive->FreeListMax = 0;
+  Hive->FreeList = NULL;
+  Hive->FreeListOffset = NULL;
+}
 
-  CmiCreateDefaultHiveHeader(RegistryHive->HiveHeader);
-  
-  RootKeyCell = (PKEY_CELL) ExAllocatePool(NonPagedPool, sizeof(KEY_CELL));
 
-  if (RootKeyCell == NULL)
-    return STATUS_INSUFFICIENT_RESOURCES;
+NTSTATUS
+CmiCreateHiveBitmap(PREGISTRY_HIVE Hive)
+{
+  ULONG BitmapSize;
 
-  CmiCreateDefaultRootKeyCell(RootKeyCell);
+  /* 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);
 
-  RegistryHive->HiveHeader->RootKeyCell = (BLOCK_OFFSET) RootKeyCell;
+  /* Initialize bitmap */
+  RtlClearAllBits(&Hive->DirtyBitMap);
+  Hive->HiveDirty = FALSE;
 
   return STATUS_SUCCESS;
 }
 
 
-NTSTATUS
-CmiCreateRegistryHive(PWSTR Filename,
-  PREGISTRY_HIVE *RegistryHive,
-  BOOLEAN CreateNew)
+static NTSTATUS
+CmiInitNonVolatileRegistryHive (PREGISTRY_HIVE RegistryHive,
+                               PWSTR Filename)
 {
-  PREGISTRY_HIVE Hive;
+  OBJECT_ATTRIBUTES ObjectAttributes;
+  ULONG CreateDisposition;
+  IO_STATUS_BLOCK IoSB;
+  HANDLE FileHandle;
+  PSECTION_OBJECT SectionObject;
+  PUCHAR ViewBase;
+  ULONG ViewSize;
   NTSTATUS Status;
 
-  DPRINT("CmiCreateRegistryHive(Filename %S)\n", Filename);
-
-  *RegistryHive = NULL;
-
-  Hive = ExAllocatePool(NonPagedPool, sizeof(REGISTRY_HIVE));
-  if (Hive == NULL)
-    return STATUS_INSUFFICIENT_RESOURCES;
-
-  DPRINT("Hive %x\n", Hive);
-
-  RtlZeroMemory(Hive, sizeof(REGISTRY_HIVE));
-
-  Hive->HiveHeader = (PHIVE_HEADER)
-    ExAllocatePool(NonPagedPool, sizeof(HIVE_HEADER));
+  DPRINT("CmiInitNonVolatileRegistryHive(%p, %S) called\n",
+        RegistryHive, Filename);
 
-  if (Hive->HiveHeader == NULL)
+  /* Duplicate Filename */
+  Status = RtlCreateUnicodeString(&RegistryHive->HiveFileName,
+                                  Filename);
+  if (!NT_SUCCESS(Status))
     {
-      ExFreePool(Hive);
-      return STATUS_INSUFFICIENT_RESOURCES;
+      DPRINT("RtlpCreateUnicodeString() failed (Status %lx)\n", Status);
+      return(Status);
     }
 
-  if (Filename != NULL)
+  /* Create log file name */
+  RegistryHive->LogFileName.Length = (wcslen(Filename) + 4) * sizeof(WCHAR);
+  RegistryHive->LogFileName.MaximumLength = RegistryHive->LogFileName.Length + sizeof(WCHAR);
+  RegistryHive->LogFileName.Buffer = ExAllocatePoolWithTag(PagedPool,
+                                                          RegistryHive->LogFileName.MaximumLength,
+                                                           TAG('U', 'S', 'T', 'R'));
+  if (RegistryHive->LogFileName.Buffer == NULL)
     {
-      Status = CmiInitPermanentRegistryHive(Hive, Filename, CreateNew);
+      RtlFreeUnicodeString(&RegistryHive->HiveFileName);
+      DPRINT("ExAllocatePool() failed\n");
+      return(STATUS_INSUFFICIENT_RESOURCES);
     }
-  else
+  wcscpy(RegistryHive->LogFileName.Buffer,
+        Filename);
+  wcscat(RegistryHive->LogFileName.Buffer,
+        L".log");
+
+#ifdef HIVE_CHECK
+  /* Check and eventually fix a hive */
+  Status = CmiCheckAndFixHive(RegistryHive);
+  if (!NT_SUCCESS(Status))
     {
-      Status = CmiInitVolatileRegistryHive(Hive);
+      RtlFreeUnicodeString(&RegistryHive->HiveFileName);
+      RtlFreeUnicodeString(&RegistryHive->LogFileName);
+      DPRINT1("CmiCheckAndFixHive() failed (Status %lx)\n", Status);
+      return(Status);
     }
+#endif
+
+  InitializeObjectAttributes(&ObjectAttributes,
+                            &RegistryHive->HiveFileName,
+                            OBJ_CASE_INSENSITIVE,
+                            NULL,
+                            NULL);
 
+  CreateDisposition = FILE_OPEN_IF;
+  Status = ZwCreateFile(&FileHandle,
+                       FILE_ALL_ACCESS,
+                       &ObjectAttributes,
+                       &IoSB,
+                       NULL,
+                       FILE_ATTRIBUTE_NORMAL,
+                       0,
+                       CreateDisposition,
+                       FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+                       NULL,
+                       0);
   if (!NT_SUCCESS(Status))
     {
-      ExFreePool(Hive->HiveHeader);
-      ExFreePool(Hive);
+      RtlFreeUnicodeString(&RegistryHive->HiveFileName);
+      RtlFreeUnicodeString(&RegistryHive->LogFileName);
+      DPRINT("ZwCreateFile() failed (Status %lx)\n", Status);
       return(Status);
     }
 
-  KeInitializeSemaphore(&Hive->RegSem, 1, 1);
-  VERIFY_REGISTRY_HIVE(Hive);
-
-  *RegistryHive = Hive;
-
-  return(STATUS_SUCCESS);
-}
+  if (IoSB.Information != FILE_OPENED)
+    {
+      Status = CmiCreateNewRegFile(FileHandle);
+      if (!NT_SUCCESS(Status))
+       {
+         DPRINT("CmiCreateNewRegFile() failed (Status %lx)\n", Status);
+         ZwClose(FileHandle);
+         RtlFreeUnicodeString(&RegistryHive->HiveFileName);
+         RtlFreeUnicodeString(&RegistryHive->LogFileName);
+         return(Status);
+       }
+    }
 
+  /* Create the hive section */
+  Status = MmCreateSection(&SectionObject,
+                          SECTION_ALL_ACCESS,
+                          NULL,
+                          NULL,
+                          PAGE_READWRITE,
+                          SEC_COMMIT,
+                          FileHandle,
+                          NULL);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT1("MmCreateSection() failed (Status %lx)\n", Status);
+      RtlFreeUnicodeString(&RegistryHive->HiveFileName);
+      RtlFreeUnicodeString(&RegistryHive->LogFileName);
+      return(Status);
+    }
 
-ULONG
-CmiGetMaxNameLength(PREGISTRY_HIVE  RegistryHive,
-  PKEY_CELL  KeyCell)
-{
-  PHASH_TABLE_CELL HashBlock;
-  PKEY_CELL CurSubKeyCell;
-  ULONG MaxName;
-  ULONG i;
+  /* Map the hive file */
+  ViewBase = NULL;
+  ViewSize = 0;
+  Status = MmMapViewOfSection(SectionObject,
+                             PsGetCurrentProcess(),
+                             (PVOID*)&ViewBase,
+                             0,
+                             ViewSize,
+                             NULL,
+                             &ViewSize,
+                             0,
+                             MEM_COMMIT,
+                             PAGE_READWRITE);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT1("MmMapViewOfSection() failed (Status %lx)\n", Status);
+      ObDereferenceObject(SectionObject);
+      RtlFreeUnicodeString(&RegistryHive->HiveFileName);
+      RtlFreeUnicodeString(&RegistryHive->LogFileName);
+      ZwClose(FileHandle);
+      return(Status);
+    }
+  DPRINT("ViewBase %p  ViewSize %lx\n", ViewBase, ViewSize);
 
-  VERIFY_KEY_CELL(KeyCell);
+  /* Copy hive header and initalize hive */
+  RtlCopyMemory (RegistryHive->HiveHeader,
+                ViewBase,
+                sizeof(HIVE_HEADER));
+  RegistryHive->FileSize = ViewSize;
+  RegistryHive->BlockListSize = (RegistryHive->FileSize / REG_BLOCK_SIZE) - 1;
+  RegistryHive->UpdateCounter = RegistryHive->HiveHeader->UpdateCounter1;
 
-  MaxName = 0;
-  HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
-  if (HashBlock == NULL)
+  /* Allocate hive block list */
+  RegistryHive->BlockList = ExAllocatePool(NonPagedPool,
+                                          RegistryHive->BlockListSize * sizeof(BLOCK_LIST_ENTRY));
+  if (RegistryHive->BlockList == NULL)
     {
-      return 0;
+      DPRINT1("Failed to allocate the hive block list\n");
+      MmUnmapViewOfSection(PsGetCurrentProcess(),
+                          ViewBase);
+      ObDereferenceObject(SectionObject);
+      RtlFreeUnicodeString(&RegistryHive->HiveFileName);
+      RtlFreeUnicodeString(&RegistryHive->LogFileName);
+      ZwClose(FileHandle);
+      return STATUS_INSUFFICIENT_RESOURCES;
     }
+  RtlZeroMemory (RegistryHive->BlockList,
+                RegistryHive->BlockListSize * sizeof(BLOCK_LIST_ENTRY));
 
-  for (i = 0; i < HashBlock->HashTableSize; i++)
+  /* Import the hive bins */
+  Status = CmiImportHiveBins (RegistryHive,
+                             ViewBase + REG_BLOCK_SIZE);
+  if (!NT_SUCCESS(Status))
     {
-      if (HashBlock->Table[i].KeyOffset != 0)
-        {
-          CurSubKeyCell = CmiGetBlock(RegistryHive,
-            HashBlock->Table[i].KeyOffset,
-            NULL);
-          if (MaxName < CurSubKeyCell->NameSize)
-            {
-              MaxName = CurSubKeyCell->NameSize;
-            }
-          CmiReleaseBlock(RegistryHive, CurSubKeyCell);
-        }
+      ExFreePool(RegistryHive->BlockList);
+      MmUnmapViewOfSection(PsGetCurrentProcess(),
+                          ViewBase);
+      ObDereferenceObject(SectionObject);
+      RtlFreeUnicodeString(&RegistryHive->HiveFileName);
+      RtlFreeUnicodeString(&RegistryHive->LogFileName);
+      ZwClose(FileHandle);
+      return Status;
     }
 
-  CmiReleaseBlock(RegistryHive, HashBlock);
-  
-  return MaxName;
-}
+  /* Unmap and dereference the hive section */
+  MmUnmapViewOfSection(PsGetCurrentProcess(),
+                       ViewBase);
+  ObDereferenceObject(SectionObject);
 
+  /* Close the hive file */
+  ZwClose(FileHandle);
+
+  /* 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;
+    }
+
+  /* Create the block bitmap */
+  Status = CmiCreateHiveBitmap (RegistryHive);
+  if (!NT_SUCCESS(Status))
+    {
+      CmiFreeHiveFreeCellList(RegistryHive);
+      CmiFreeHiveBins(RegistryHive);
+      ExFreePool(RegistryHive->BlockList);
+      RtlFreeUnicodeString(&RegistryHive->HiveFileName);
+      RtlFreeUnicodeString(&RegistryHive->LogFileName);
+      return Status;
+    }
+
+  DPRINT("CmiInitNonVolatileRegistryHive(%p, %S) - Finished.\n",
+        RegistryHive, Filename);
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+CmiCreateVolatileHive(PREGISTRY_HIVE *RegistryHive)
+{
+  PKEY_CELL RootKeyCell;
+  PREGISTRY_HIVE Hive;
+
+  *RegistryHive = NULL;
+
+  Hive = ExAllocatePool (NonPagedPool,
+                        sizeof(REGISTRY_HIVE));
+  if (Hive == NULL)
+    return STATUS_INSUFFICIENT_RESOURCES;
+
+  RtlZeroMemory (Hive,
+                sizeof(REGISTRY_HIVE));
+
+  DPRINT("Hive 0x%p\n", Hive);
+
+  Hive->HiveHeader = (PHIVE_HEADER)ExAllocatePool (NonPagedPool,
+                                                  sizeof(HIVE_HEADER));
+  if (Hive->HiveHeader == NULL)
+    {
+      ExFreePool (Hive);
+      return STATUS_INSUFFICIENT_RESOURCES;
+    }
+  RtlZeroMemory (Hive->HiveHeader,
+                sizeof(HIVE_HEADER));
+
+  Hive->Flags = (HIVE_NO_FILE | HIVE_POINTER);
+
+  CmiCreateDefaultHiveHeader (Hive->HiveHeader);
+
+  RootKeyCell = (PKEY_CELL)ExAllocatePool (NonPagedPool,
+                                          sizeof(KEY_CELL));
+  if (RootKeyCell == NULL)
+    {
+      ExFreePool(Hive->HiveHeader);
+      ExFreePool(Hive);
+      return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+  CmiCreateDefaultRootKeyCell (RootKeyCell);
+  Hive->HiveHeader->RootKeyOffset = (BLOCK_OFFSET)RootKeyCell;
+
+  /* Acquire hive list lock exclusively */
+  KeEnterCriticalRegion();
+  ExAcquireResourceExclusiveLite (&CmiRegistryLock, TRUE);
+
+  /* Add the new hive to the hive list */
+  InsertTailList (&CmiHiveListHead,
+                 &Hive->HiveList);
+
+  /* Release hive list lock */
+  ExReleaseResourceLite (&CmiRegistryLock);
+  KeLeaveCriticalRegion();
+
+  VERIFY_REGISTRY_HIVE (Hive);
+
+  *RegistryHive = Hive;
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+CmiCreateTempHive(PREGISTRY_HIVE *RegistryHive)
+{
+  PHBIN BinHeader;
+  PCELL_HEADER FreeCell;
+  PREGISTRY_HIVE Hive;
+  NTSTATUS Status;
+
+  DPRINT ("CmiCreateTempHive() called\n");
+
+  *RegistryHive = NULL;
+
+  Hive = ExAllocatePool (NonPagedPool,
+                        sizeof(REGISTRY_HIVE));
+  if (Hive == NULL)
+    {
+      DPRINT1 ("Failed to allocate registry hive block\n");
+      return STATUS_INSUFFICIENT_RESOURCES;
+    }
+  RtlZeroMemory (Hive,
+                sizeof(REGISTRY_HIVE));
+
+  DPRINT ("Hive 0x%p\n", Hive);
+
+  Hive->HiveHeader = (PHIVE_HEADER)ExAllocatePool (NonPagedPool,
+                                                  REG_BLOCK_SIZE);
+  if (Hive->HiveHeader == NULL)
+    {
+      DPRINT1 ("Failed to allocate hive header block\n");
+      ExFreePool (Hive);
+      return STATUS_INSUFFICIENT_RESOURCES;
+    }
+  RtlZeroMemory (Hive->HiveHeader,
+                REG_BLOCK_SIZE);
+
+  DPRINT ("HiveHeader 0x%p\n", Hive->HiveHeader);
+
+  Hive->Flags = HIVE_NO_FILE;
+
+  RtlInitUnicodeString (&Hive->HiveFileName,
+                       NULL);
+  RtlInitUnicodeString (&Hive->LogFileName,
+                       NULL);
+
+  CmiCreateDefaultHiveHeader (Hive->HiveHeader);
+
+  /* Allocate hive block list */
+  Hive->BlockList = ExAllocatePool (NonPagedPool,
+                                   sizeof(PBLOCK_LIST_ENTRY));
+  if (Hive->BlockList == NULL)
+    {
+      DPRINT1 ("Failed to allocate hive block list\n");
+      ExFreePool(Hive->HiveHeader);
+      ExFreePool(Hive);
+      return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+  /* Allocate first Bin */
+  Hive->BlockList[0].Bin = ExAllocatePool (NonPagedPool,
+                                          REG_BLOCK_SIZE);
+  if (Hive->BlockList[0].Bin == NULL)
+    {
+      DPRINT1 ("Failed to allocate first bin\n");
+      ExFreePool(Hive->BlockList);
+      ExFreePool(Hive->HiveHeader);
+      ExFreePool(Hive);
+      return STATUS_INSUFFICIENT_RESOURCES;
+    }
+  Hive->BlockList[0].Block = (PVOID)Hive->BlockList[0].Bin;
+
+  Hive->FileSize = 2* REG_BLOCK_SIZE;
+  Hive->BlockListSize = 1;
+  Hive->UpdateCounter = Hive->HiveHeader->UpdateCounter1;
+
+
+  BinHeader = Hive->BlockList[0].Bin;
+  FreeCell = (PCELL_HEADER)((ULONG_PTR)BinHeader + REG_HBIN_DATA_OFFSET);
+
+  CmiCreateDefaultBinHeader (BinHeader);
+
+  /* First block */
+  BinHeader->BinOffset = 0;
+
+  /* Offset to root key block */
+  Hive->HiveHeader->RootKeyOffset = (BLOCK_OFFSET)-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].Bin == NULL)
+    {
+      DPRINT1 ("CmiCreateHiveFreeCellList() failed (Status %lx)\n", Status);
+      ExFreePool(Hive->BlockList[0].Bin);
+      ExFreePool(Hive->BlockList);
+      ExFreePool(Hive->HiveHeader);
+      ExFreePool(Hive);
+      return Status;
+    }
+
+  /* Acquire hive list lock exclusively */
+  KeEnterCriticalRegion();
+  ExAcquireResourceExclusiveLite(&CmiRegistryLock, TRUE);
+
+  /* Add the new hive to the hive list */
+  InsertTailList (&CmiHiveListHead,
+                 &Hive->HiveList);
+
+  /* Release hive list lock */
+  ExReleaseResourceLite(&CmiRegistryLock);
+  KeLeaveCriticalRegion();
+
+  VERIFY_REGISTRY_HIVE (Hive);
+
+  *RegistryHive = Hive;
+
+  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 0x%p\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;
+    }
+
+  RtlZeroMemory (Hive->HiveHeader,
+                sizeof(HIVE_HEADER));
+
+  Status = CmiInitNonVolatileRegistryHive (Hive,
+                                          FileName->Buffer);
+  if (!NT_SUCCESS (Status))
+    {
+      DPRINT1 ("CmiInitNonVolatileRegistryHive() failed (Status %lx)\n", Status);
+      ExFreePool (Hive->HiveHeader);
+      ExFreePool (Hive);
+      return Status;
+    }
+
+  /* Add the new hive to the hive list */
+  InsertTailList (&CmiHiveListHead,
+                 &Hive->HiveList);
+
+  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;
+
+  /* Remove hive from hive list */
+  RemoveEntryList (&RegistryHive->HiveList);
+
+  /* Release file names */
+  RtlFreeUnicodeString (&RegistryHive->HiveFileName);
+  RtlFreeUnicodeString (&RegistryHive->LogFileName);
+
+  /* Release hive bitmap */
+  ExFreePool (RegistryHive->BitmapBuffer);
+
+  /* Release free cell list */
+  ExFreePool (RegistryHive->FreeList);
+  ExFreePool (RegistryHive->FreeListOffset);
+
+  /* Release bins and bin list */
+  CmiFreeHiveBins (RegistryHive);
+  ExFreePool (RegistryHive->BlockList);
+
+  /* Release hive header */
+  ExFreePool (RegistryHive->HiveHeader);
+
+  /* Release hive */
+  ExFreePool (RegistryHive);
+
+  return STATUS_SUCCESS;
+}
+
+
+static ULONG
+CmiCalcChecksum(PULONG Buffer)
+{
+  ULONG Sum = 0;
+  ULONG i;
+
+  for (i = 0; i < 127; i++)
+    Sum += Buffer[i];
+
+  return(Sum);
+}
+
+
+static NTSTATUS
+CmiStartLogUpdate(PREGISTRY_HIVE RegistryHive)
+{
+  FILE_END_OF_FILE_INFORMATION EndOfFileInfo;
+  FILE_ALLOCATION_INFORMATION FileAllocationInfo;
+  OBJECT_ATTRIBUTES ObjectAttributes;
+  IO_STATUS_BLOCK IoStatusBlock;
+  HANDLE FileHandle;
+  LARGE_INTEGER FileOffset;
+  ULONG BufferSize;
+  ULONG BitmapSize;
+  PUCHAR Buffer;
+  PUCHAR Ptr;
+  ULONG BlockIndex;
+  ULONG LastIndex;
+  PVOID BlockPtr;
+  NTSTATUS Status;
+
+  DPRINT("CmiStartLogUpdate() called\n");
+
+  BitmapSize = ROUND_UP(RegistryHive->BlockListSize, sizeof(ULONG) * 8) / 8;
+  BufferSize = sizeof(HIVE_HEADER) +
+              sizeof(ULONG) +
+              BitmapSize;
+  BufferSize = ROUND_UP(BufferSize, REG_BLOCK_SIZE);
+
+  DPRINT("Bitmap size %lu  buffer size: %lu\n", BitmapSize, BufferSize);
+
+  Buffer = (PUCHAR)ExAllocatePool(NonPagedPool,
+                                 BufferSize);
+  if (Buffer == NULL)
+    {
+      DPRINT("ExAllocatePool() failed\n");
+      return(STATUS_INSUFFICIENT_RESOURCES);
+    }
+  RtlZeroMemory (Buffer,
+                BufferSize);
+
+  /* Open log file for writing */
+  InitializeObjectAttributes(&ObjectAttributes,
+                            &RegistryHive->LogFileName,
+                            OBJ_CASE_INSENSITIVE,
+                            NULL,
+                            NULL);
+
+  Status = ZwCreateFile(&FileHandle,
+                       FILE_ALL_ACCESS,
+                       &ObjectAttributes,
+                       &IoStatusBlock,
+                       NULL,
+                       FILE_ATTRIBUTE_NORMAL,
+                       0,
+                       FILE_SUPERSEDE,
+                       FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+                       NULL,
+                       0);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("ZwCreateFile() failed (Status %lx)\n", Status);
+      ExFreePool(Buffer);
+      return(Status);
+    }
+
+  /* Update firt update counter and checksum */
+  RegistryHive->HiveHeader->UpdateCounter1 = RegistryHive->UpdateCounter + 1;
+  RegistryHive->HiveHeader->Checksum = CmiCalcChecksum((PULONG)RegistryHive->HiveHeader);
+
+  /* Copy hive header */
+  RtlCopyMemory(Buffer,
+               RegistryHive->HiveHeader,
+               sizeof(HIVE_HEADER));
+  Ptr = Buffer + sizeof(HIVE_HEADER);
+
+  RtlCopyMemory(Ptr,
+               "DIRT",
+               4);
+  Ptr += 4;
+  RtlCopyMemory(Ptr,
+               RegistryHive->DirtyBitMap.Buffer,
+               BitmapSize);
+
+  /* Write hive block and block bitmap */
+  FileOffset.QuadPart = (ULONGLONG)0;
+  Status = ZwWriteFile(FileHandle,
+                      NULL,
+                      NULL,
+                      NULL,
+                      &IoStatusBlock,
+                      Buffer,
+                      BufferSize,
+                      &FileOffset,
+                      NULL);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("ZwWriteFile() failed (Status %lx)\n", Status);
+      ZwClose(FileHandle);
+      ExFreePool(Buffer);
+      return(Status);
+    }
+  ExFreePool(Buffer);
+
+  /* Write dirty blocks */
+  FileOffset.QuadPart = (ULONGLONG)BufferSize;
+  BlockIndex = 0;
+  while (BlockIndex < RegistryHive->BlockListSize)
+    {
+      LastIndex = BlockIndex;
+      BlockIndex = RtlFindSetBits(&RegistryHive->DirtyBitMap,
+                                 1,
+                                 BlockIndex);
+      if (BlockIndex == (ULONG)-1 || BlockIndex < LastIndex)
+       {
+         DPRINT("No more set bits\n");
+         Status = STATUS_SUCCESS;
+         break;
+       }
+
+      DPRINT("Block %lu is dirty\n", BlockIndex);
+
+      BlockPtr = RegistryHive->BlockList[BlockIndex].Block;
+      DPRINT("BlockPtr %p\n", BlockPtr);
+      DPRINT("File offset %I64x\n", FileOffset.QuadPart);
+
+      /* Write hive block */
+      Status = ZwWriteFile(FileHandle,
+                          NULL,
+                          NULL,
+                          NULL,
+                          &IoStatusBlock,
+                          BlockPtr,
+                          REG_BLOCK_SIZE,
+                          &FileOffset,
+                          NULL);
+      if (!NT_SUCCESS(Status))
+       {
+         DPRINT1("ZwWriteFile() failed (Status %lx)\n", Status);
+         ZwClose(FileHandle);
+         return(Status);
+       }
+
+      BlockIndex++;
+      FileOffset.QuadPart += (ULONGLONG)REG_BLOCK_SIZE;
+    }
+
+  /* Truncate log file */
+  EndOfFileInfo.EndOfFile.QuadPart = FileOffset.QuadPart;
+  Status = ZwSetInformationFile(FileHandle,
+                               &IoStatusBlock,
+                               &EndOfFileInfo,
+                               sizeof(FILE_END_OF_FILE_INFORMATION),
+                               FileEndOfFileInformation);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("ZwSetInformationFile() failed (Status %lx)\n", Status);
+      ZwClose(FileHandle);
+      return(Status);
+    }
+
+  FileAllocationInfo.AllocationSize.QuadPart = FileOffset.QuadPart;
+  Status = ZwSetInformationFile(FileHandle,
+                               &IoStatusBlock,
+                               &FileAllocationInfo,
+                               sizeof(FILE_ALLOCATION_INFORMATION),
+                               FileAllocationInformation);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("ZwSetInformationFile() failed (Status %lx)\n", Status);
+      ZwClose(FileHandle);
+      return(Status);
+    }
+
+  /* Flush the log file */
+  Status = ZwFlushBuffersFile(FileHandle,
+                             &IoStatusBlock);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("ZwFlushBuffersFile() failed (Status %lx)\n", Status);
+    }
+
+  ZwClose(FileHandle);
+
+  return(Status);
+}
+
+
+static NTSTATUS
+CmiFinishLogUpdate(PREGISTRY_HIVE RegistryHive)
+{
+  OBJECT_ATTRIBUTES ObjectAttributes;
+  IO_STATUS_BLOCK IoStatusBlock;
+  HANDLE FileHandle;
+  LARGE_INTEGER FileOffset;
+  ULONG BufferSize;
+  ULONG BitmapSize;
+  PUCHAR Buffer;
+  PUCHAR Ptr;
+  NTSTATUS Status;
+
+  DPRINT("CmiFinishLogUpdate() called\n");
+
+  BitmapSize = ROUND_UP(RegistryHive->BlockListSize, sizeof(ULONG) * 8) / 8;
+  BufferSize = sizeof(HIVE_HEADER) +
+              sizeof(ULONG) +
+              BitmapSize;
+  BufferSize = ROUND_UP(BufferSize, REG_BLOCK_SIZE);
+
+  DPRINT("Bitmap size %lu  buffer size: %lu\n", BitmapSize, BufferSize);
+
+  Buffer = (PUCHAR)ExAllocatePool(NonPagedPool, BufferSize);
+  if (Buffer == NULL)
+    {
+      DPRINT("ExAllocatePool() failed\n");
+      return(STATUS_INSUFFICIENT_RESOURCES);
+    }
+
+  /* Open log file for writing */
+  InitializeObjectAttributes(&ObjectAttributes,
+                            &RegistryHive->LogFileName,
+                            OBJ_CASE_INSENSITIVE,
+                            NULL,
+                            NULL);
+
+  Status = ZwCreateFile(&FileHandle,
+                       FILE_ALL_ACCESS,
+                       &ObjectAttributes,
+                       &IoStatusBlock,
+                       NULL,
+                       FILE_ATTRIBUTE_NORMAL,
+                       0,
+                       FILE_OPEN,
+                       FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+                       NULL,
+                       0);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("ZwCreateFile() failed (Status %lx)\n", Status);
+      ExFreePool(Buffer);
+      return(Status);
+    }
+
+  /* Update first and second update counter and checksum */
+  RegistryHive->HiveHeader->UpdateCounter1 = RegistryHive->UpdateCounter + 1;
+  RegistryHive->HiveHeader->UpdateCounter2 = RegistryHive->UpdateCounter + 1;
+  RegistryHive->HiveHeader->Checksum = CmiCalcChecksum((PULONG)RegistryHive->HiveHeader);
+
+  /* Copy hive header */
+  RtlCopyMemory(Buffer,
+               RegistryHive->HiveHeader,
+               sizeof(HIVE_HEADER));
+  Ptr = Buffer + sizeof(HIVE_HEADER);
+
+  /* Write empty block bitmap */
+  RtlCopyMemory(Ptr,
+               "DIRT",
+               4);
+  Ptr += 4;
+  RtlZeroMemory(Ptr,
+               BitmapSize);
+
+  /* Write hive block and block bitmap */
+  FileOffset.QuadPart = (ULONGLONG)0;
+  Status = ZwWriteFile(FileHandle,
+                      NULL,
+                      NULL,
+                      NULL,
+                      &IoStatusBlock,
+                      Buffer,
+                      BufferSize,
+                      &FileOffset,
+                      NULL);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("ZwWriteFile() failed (Status %lx)\n", Status);
+      ZwClose(FileHandle);
+      ExFreePool(Buffer);
+      return(Status);
+    }
+
+  ExFreePool(Buffer);
+
+  /* Flush the log file */
+  Status = ZwFlushBuffersFile(FileHandle,
+                             &IoStatusBlock);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("ZwFlushBuffersFile() failed (Status %lx)\n", Status);
+    }
+
+  ZwClose(FileHandle);
+
+  return(Status);
+}
+
+
+static NTSTATUS
+CmiCleanupLogUpdate(PREGISTRY_HIVE RegistryHive)
+{
+  FILE_END_OF_FILE_INFORMATION EndOfFileInfo;
+  FILE_ALLOCATION_INFORMATION FileAllocationInfo;
+  OBJECT_ATTRIBUTES ObjectAttributes;
+  IO_STATUS_BLOCK IoStatusBlock;
+  HANDLE FileHandle;
+  ULONG BufferSize;
+  ULONG BitmapSize;
+  NTSTATUS Status;
+
+  DPRINT("CmiCleanupLogUpdate() called\n");
+
+  BitmapSize = ROUND_UP(RegistryHive->BlockListSize, sizeof(ULONG) * 8) / 8;
+  BufferSize = sizeof(HIVE_HEADER) +
+              sizeof(ULONG) +
+              BitmapSize;
+  BufferSize = ROUND_UP(BufferSize, REG_BLOCK_SIZE);
+
+  DPRINT("Bitmap size %lu  buffer size: %lu\n", BitmapSize, BufferSize);
+
+  /* Open log file for writing */
+  InitializeObjectAttributes(&ObjectAttributes,
+                            &RegistryHive->LogFileName,
+                            OBJ_CASE_INSENSITIVE,
+                            NULL,
+                            NULL);
+
+  Status = ZwCreateFile(&FileHandle,
+                       FILE_ALL_ACCESS,
+                       &ObjectAttributes,
+                       &IoStatusBlock,
+                       NULL,
+                       FILE_ATTRIBUTE_NORMAL,
+                       0,
+                       FILE_OPEN,
+                       FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+                       NULL,
+                       0);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("ZwCreateFile() failed (Status %lx)\n", Status);
+      return(Status);
+    }
+
+  /* Truncate log file */
+  EndOfFileInfo.EndOfFile.QuadPart = (ULONGLONG)BufferSize;
+  Status = ZwSetInformationFile(FileHandle,
+                               &IoStatusBlock,
+                               &EndOfFileInfo,
+                               sizeof(FILE_END_OF_FILE_INFORMATION),
+                               FileEndOfFileInformation);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("ZwSetInformationFile() failed (Status %lx)\n", Status);
+      ZwClose(FileHandle);
+      return(Status);
+    }
+
+  FileAllocationInfo.AllocationSize.QuadPart = (ULONGLONG)BufferSize;
+  Status = ZwSetInformationFile(FileHandle,
+                               &IoStatusBlock,
+                               &FileAllocationInfo,
+                               sizeof(FILE_ALLOCATION_INFORMATION),
+                               FileAllocationInformation);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("NtSetInformationFile() failed (Status %lx)\n", Status);
+      ZwClose(FileHandle);
+      return(Status);
+    }
+
+  /* Flush the log file */
+  Status = ZwFlushBuffersFile(FileHandle,
+                             &IoStatusBlock);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("ZwFlushBuffersFile() failed (Status %lx)\n", Status);
+    }
+
+  ZwClose(FileHandle);
+
+  return(Status);
+}
+
+
+static NTSTATUS
+CmiStartHiveUpdate(PREGISTRY_HIVE RegistryHive)
+{
+  OBJECT_ATTRIBUTES ObjectAttributes;
+  IO_STATUS_BLOCK IoStatusBlock;
+  HANDLE FileHandle;
+  LARGE_INTEGER FileOffset;
+  ULONG BlockIndex;
+  ULONG LastIndex;
+  PVOID BlockPtr;
+  NTSTATUS Status;
+
+  DPRINT("CmiStartHiveUpdate() called\n");
+
+  /* Open hive for writing */
+  InitializeObjectAttributes(&ObjectAttributes,
+                            &RegistryHive->HiveFileName,
+                            OBJ_CASE_INSENSITIVE,
+                            NULL,
+                            NULL);
+
+  Status = ZwCreateFile(&FileHandle,
+                       FILE_ALL_ACCESS,
+                       &ObjectAttributes,
+                       &IoStatusBlock,
+                       NULL,
+                       FILE_ATTRIBUTE_NORMAL,
+                       0,
+                       FILE_OPEN,
+                       FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+                       NULL,
+                       0);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("ZwCreateFile() failed (Status %lx)\n", Status);
+      return(Status);
+    }
+
+  /* Update firt update counter and checksum */
+  RegistryHive->HiveHeader->UpdateCounter1 = RegistryHive->UpdateCounter + 1;
+  RegistryHive->HiveHeader->Checksum = CmiCalcChecksum((PULONG)RegistryHive->HiveHeader);
+
+  /* Write hive block */
+  FileOffset.QuadPart = (ULONGLONG)0;
+  Status = ZwWriteFile(FileHandle,
+                      NULL,
+                      NULL,
+                      NULL,
+                      &IoStatusBlock,
+                      RegistryHive->HiveHeader,
+                      sizeof(HIVE_HEADER),
+                      &FileOffset,
+                      NULL);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("ZwWriteFile() failed (Status %lx)\n", Status);
+      ZwClose(FileHandle);
+      return(Status);
+    }
+
+  BlockIndex = 0;
+  while (BlockIndex < RegistryHive->BlockListSize)
+    {
+      LastIndex = BlockIndex;
+      BlockIndex = RtlFindSetBits(&RegistryHive->DirtyBitMap,
+                                 1,
+                                 BlockIndex);
+      if (BlockIndex == (ULONG)-1 || BlockIndex < LastIndex)
+       {
+         DPRINT("No more set bits\n");
+         Status = STATUS_SUCCESS;
+         break;
+       }
+
+      DPRINT("Block %lu is dirty\n", BlockIndex);
+
+      BlockPtr = RegistryHive->BlockList[BlockIndex].Block;
+      DPRINT("  BlockPtr %p\n", BlockPtr);
+
+      FileOffset.QuadPart = (ULONGLONG)(BlockIndex + 1) * (ULONGLONG)REG_BLOCK_SIZE;
+      DPRINT("  File offset %I64x\n", FileOffset.QuadPart);
+
+      /* Write hive block */
+      Status = ZwWriteFile(FileHandle,
+                          NULL,
+                          NULL,
+                          NULL,
+                          &IoStatusBlock,
+                          BlockPtr,
+                          REG_BLOCK_SIZE,
+                          &FileOffset,
+                          NULL);
+      if (!NT_SUCCESS(Status))
+       {
+         DPRINT("ZwWriteFile() failed (Status %lx)\n", Status);
+         ZwClose(FileHandle);
+         return(Status);
+       }
+
+      BlockIndex++;
+    }
+
+  Status = ZwFlushBuffersFile(FileHandle,
+                             &IoStatusBlock);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("ZwFlushBuffersFile() failed (Status %lx)\n", Status);
+    }
+
+  ZwClose(FileHandle);
+
+  return(Status);
+}
+
+
+static NTSTATUS
+CmiFinishHiveUpdate(PREGISTRY_HIVE RegistryHive)
+{
+  OBJECT_ATTRIBUTES ObjectAttributes;
+  IO_STATUS_BLOCK IoStatusBlock;
+  LARGE_INTEGER FileOffset;
+  HANDLE FileHandle;
+  NTSTATUS Status;
+
+  DPRINT("CmiFinishHiveUpdate() called\n");
+
+  InitializeObjectAttributes(&ObjectAttributes,
+                            &RegistryHive->HiveFileName,
+                            OBJ_CASE_INSENSITIVE,
+                            NULL,
+                            NULL);
+
+  Status = ZwCreateFile(&FileHandle,
+                       FILE_ALL_ACCESS,
+                       &ObjectAttributes,
+                       &IoStatusBlock,
+                       NULL,
+                       FILE_ATTRIBUTE_NORMAL,
+                       0,
+                       FILE_OPEN,
+                       FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+                       NULL,
+                       0);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("ZwCreateFile() failed (Status %lx)\n", Status);
+      return(Status);
+    }
+
+  /* Update second update counter and checksum */
+  RegistryHive->HiveHeader->UpdateCounter1 = RegistryHive->UpdateCounter + 1;
+  RegistryHive->HiveHeader->UpdateCounter2 = RegistryHive->UpdateCounter + 1;
+  RegistryHive->HiveHeader->Checksum = CmiCalcChecksum((PULONG)RegistryHive->HiveHeader);
+
+  /* Write hive block */
+  FileOffset.QuadPart = (ULONGLONG)0;
+  Status = ZwWriteFile(FileHandle,
+                      NULL,
+                      NULL,
+                      NULL,
+                      &IoStatusBlock,
+                      RegistryHive->HiveHeader,
+                      sizeof(HIVE_HEADER),
+                      &FileOffset,
+                      NULL);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("ZwWriteFile() failed (Status %lx)\n", Status);
+      ZwClose(FileHandle);
+      return(Status);
+    }
+
+  Status = ZwFlushBuffersFile(FileHandle,
+                             &IoStatusBlock);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("ZwFlushBuffersFile() failed (Status %lx)\n", Status);
+    }
+
+  ZwClose(FileHandle);
+
+  return(Status);
+}
+
+
+NTSTATUS
+CmiFlushRegistryHive(PREGISTRY_HIVE RegistryHive)
+{
+  NTSTATUS Status;
+
+  DPRINT("CmiFlushRegistryHive() called\n");
+
+  if (RegistryHive->HiveDirty == FALSE)
+    {
+      return(STATUS_SUCCESS);
+    }
+
+  DPRINT("Hive '%wZ' is dirty\n",
+        &RegistryHive->HiveFileName);
+  DPRINT("Log file: '%wZ'\n",
+        &RegistryHive->LogFileName);
+
+  /* Update hive header modification time */
+  KeQuerySystemTime(&RegistryHive->HiveHeader->DateModified);
+
+  /* Start log update */
+  Status = CmiStartLogUpdate(RegistryHive);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("CmiStartLogUpdate() failed (Status %lx)\n", Status);
+      return(Status);
+    }
+
+  /* Finish log update */
+  Status = CmiFinishLogUpdate(RegistryHive);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("CmiFinishLogUpdate() failed (Status %lx)\n", Status);
+      return(Status);
+    }
+
+  /* Start hive update */
+  Status = CmiStartHiveUpdate(RegistryHive);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("CmiStartHiveUpdate() failed (Status %lx)\n", Status);
+      return(Status);
+    }
+
+  /* Finish the hive update */
+  Status = CmiFinishHiveUpdate(RegistryHive);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("CmiFinishHiveUpdate() failed (Status %lx)\n", Status);
+      return(Status);
+    }
+
+  /* Cleanup log update */
+  Status = CmiCleanupLogUpdate(RegistryHive);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("CmiFinishLogUpdate() failed (Status %lx)\n", Status);
+      return(Status);
+    }
+
+  /* Increment hive update counter */
+  RegistryHive->UpdateCounter++;
+
+  /* Clear dirty bitmap and dirty flag */
+  RtlClearAllBits(&RegistryHive->DirtyBitMap);
+  RegistryHive->HiveDirty = FALSE;
+
+  DPRINT("CmiFlushRegistryHive() done\n");
+
+  return(STATUS_SUCCESS);
+}
+
+
+ULONG
+CmiGetNumberOfSubKeys(PKEY_OBJECT KeyObject)
+{
+  PKEY_OBJECT CurKey;
+  PKEY_CELL KeyCell;
+  ULONG SubKeyCount;
+  ULONG i;
+
+  VERIFY_KEY_OBJECT(KeyObject);
+
+  KeyCell = KeyObject->KeyCell;
+  VERIFY_KEY_CELL(KeyCell);
+
+  SubKeyCount = (KeyCell == NULL) ? 0 : KeyCell->NumberOfSubKeys;
+
+  /* Search for volatile or 'foreign' keys */
+  for (i = 0; i < KeyObject->NumberOfSubKeys; i++)
+    {
+      CurKey = KeyObject->SubKeys[i];
+      if (CurKey->RegistryHive == CmiVolatileHive ||
+         CurKey->RegistryHive != KeyObject->RegistryHive)
+       {
+         SubKeyCount++;
+       }
+    }
+
+  return SubKeyCount;
+}
+
+
+ULONG
+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 = CmiGetCell (KeyObject->RegistryHive,
+                         KeyCell->HashTableOffset,
+                         NULL);
+  if (HashBlock == NULL)
+    {
+      DPRINT("CmiGetBlock() failed\n");
+    }
+  else
+    {
+      for (i = 0; i < HashBlock->HashTableSize; i++)
+       {
+         if (HashBlock->Table[i].KeyOffset != 0)
+           {
+             CurSubKeyCell = CmiGetCell (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 (NameSize > MaxName)
+               {
+                 MaxName = NameSize;
+               }
+           }
+       }
+    }
+
+  DPRINT ("KeyObject->NumberOfSubKeys %d\n", KeyObject->NumberOfSubKeys);
+  for (i = 0; i < KeyObject->NumberOfSubKeys; i++)
+    {
+      CurKey = KeyObject->SubKeys[i];
+      if (CurKey->RegistryHive == CmiVolatileHive ||
+         CurKey->RegistryHive != KeyObject->RegistryHive)
+       {
+         CurSubKeyCell = CurKey->KeyCell;
+         if (CurSubKeyCell == NULL)
+           {
+             DPRINT("CmiGetBlock() failed\n");
+             continue;
+           }
+
+         if ((CurSubKeyCell->Flags & REG_KEY_ROOT_CELL) == REG_KEY_ROOT_CELL)
+           {
+             /* Use name of the key object */
+             NameSize = CurKey->Name.Length;
+           }
+         else
+           {
+             /* Use name of the key cell */
+             NameSize = CurSubKeyCell->NameSize;
+             if (CurSubKeyCell->Flags & REG_KEY_NAME_PACKED)
+               {
+                 NameSize *= sizeof(WCHAR);
+               }
+           }
+         DPRINT ("NameSize %lu\n", NameSize);
+
+         if (NameSize > MaxName)
+           {
+             MaxName = NameSize;
+           }
+       }
+    }
+
+  DPRINT ("MaxName %lu\n", MaxName);
+
+  return MaxName;
+}
+
+
+ULONG
+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 = CmiGetCell (KeyObject->RegistryHive,
+                         KeyCell->HashTableOffset,
+                         NULL);
+  if (HashBlock == NULL)
+    {
+      DPRINT("CmiGetBlock() failed\n");
+    }
+  else
+    {
+      for (i = 0; i < HashBlock->HashTableSize; i++)
+       {
+         if (HashBlock->Table[i].KeyOffset != 0)
+           {
+             CurSubKeyCell = CmiGetCell (KeyObject->RegistryHive,
+                                         HashBlock->Table[i].KeyOffset,
+                                         NULL);
+             if (CurSubKeyCell == NULL)
+               {
+                 DPRINT("CmiGetBlock() failed\n");
+                 continue;
+               }
+
+             if (MaxClass < CurSubKeyCell->ClassSize)
+               {
+                 MaxClass = CurSubKeyCell->ClassSize;
+               }
+           }
+       }
+    }
+
+  DPRINT("KeyObject->NumberOfSubKeys %d\n", KeyObject->NumberOfSubKeys);
+  for (i = 0; i < KeyObject->NumberOfSubKeys; i++)
+    {
+      CurKey = KeyObject->SubKeys[i];
+      if (CurKey->RegistryHive == CmiVolatileHive ||
+         CurKey->RegistryHive != KeyObject->RegistryHive)
+       {
+         CurSubKeyCell = CurKey->KeyCell;
+         if (CurSubKeyCell == NULL)
+           {
+             DPRINT("CmiGetBlock() failed\n");
+             continue;
+           }
+
+         if (MaxClass < CurSubKeyCell->ClassSize)
+           {
+             MaxClass = CurSubKeyCell->ClassSize;
+           }
+       }
+    }
+
+  return MaxClass;
+}
+
+
+ULONG
+CmiGetMaxValueNameLength(PREGISTRY_HIVE RegistryHive,
+                        PKEY_CELL KeyCell)
+{
+  PVALUE_LIST_CELL ValueListCell;
+  PVALUE_CELL CurValueCell;
+  ULONG MaxValueName;
+  ULONG Size;
+  ULONG i;
+
+  VERIFY_KEY_CELL(KeyCell);
+
+  MaxValueName = 0;
+  ValueListCell = CmiGetCell (RegistryHive,
+                             KeyCell->ValueListOffset,
+                             NULL);
+  if (ValueListCell == NULL)
+    {
+      DPRINT("CmiGetBlock() failed\n");
+      return 0;
+    }
+
+  for (i = 0; i < KeyCell->NumberOfValues; i++)
+    {
+      CurValueCell = CmiGetCell (RegistryHive,
+                                ValueListCell->ValueOffset[i],
+                                NULL);
+      if (CurValueCell == NULL)
+       {
+         DPRINT("CmiGetBlock() failed\n");
+       }
+
+      if (CurValueCell != NULL)
+       {
+         Size = CurValueCell->NameSize;
+         if (CurValueCell->Flags & REG_VALUE_NAME_PACKED)
+           {
+             Size *= sizeof(WCHAR);
+           }
+         if (MaxValueName < Size)
+           {
+             MaxValueName = Size;
+           }
+        }
+    }
+
+  return MaxValueName;
+}
+
+
+ULONG
+CmiGetMaxValueDataLength(PREGISTRY_HIVE RegistryHive,
+                        PKEY_CELL KeyCell)
+{
+  PVALUE_LIST_CELL ValueListCell;
+  PVALUE_CELL CurValueCell;
+  LONG MaxValueData;
+  ULONG i;
+
+  VERIFY_KEY_CELL(KeyCell);
+
+  MaxValueData = 0;
+  ValueListCell = CmiGetCell (RegistryHive, KeyCell->ValueListOffset, NULL);
+  if (ValueListCell == NULL)
+    {
+      return 0;
+    }
+
+  for (i = 0; i < KeyCell->NumberOfValues; i++)
+    {
+      CurValueCell = CmiGetCell (RegistryHive,
+                                 ValueListCell->ValueOffset[i],NULL);
+      if ((CurValueCell != NULL) &&
+          (MaxValueData < (LONG)(CurValueCell->DataSize & REG_DATA_SIZE_MASK)))
+        {
+          MaxValueData = CurValueCell->DataSize & REG_DATA_SIZE_MASK;
+        }
+    }
+
+  return MaxValueData;
+}
+
+
+NTSTATUS
+CmiScanForSubKey(IN PREGISTRY_HIVE RegistryHive,
+                IN PKEY_CELL KeyCell,
+                OUT PKEY_CELL *SubKeyCell,
+                OUT BLOCK_OFFSET *BlockOffset,
+                IN PUNICODE_STRING KeyName,
+                IN ACCESS_MASK DesiredAccess,
+                IN ULONG Attributes)
+{
+  PHASH_TABLE_CELL HashBlock;
+  PKEY_CELL CurSubKeyCell;
+  ULONG i;
+
+  VERIFY_KEY_CELL(KeyCell);
+
+  DPRINT("Scanning for sub key %wZ\n", KeyName);
+
+  ASSERT(RegistryHive);
+
+  *SubKeyCell = NULL;
+
+  /* The key does not have any subkeys */
+  if (KeyCell->HashTableOffset == (BLOCK_OFFSET)-1)
+    {
+      return STATUS_SUCCESS;
+    }
+
+  /* Get hash table */
+  HashBlock = CmiGetCell (RegistryHive, KeyCell->HashTableOffset, NULL);
+  if (HashBlock == NULL)
+    {
+      DPRINT("CmiGetBlock() failed\n");
+      return STATUS_UNSUCCESSFUL;
+    }
+
+  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 &&
+             (HashBlock->Table[i].HashValue == 0 ||
+              CmiCompareHashI(KeyName, (PCHAR)&HashBlock->Table[i].HashValue)))
+           {
+             CurSubKeyCell = CmiGetCell (RegistryHive,
+                                         HashBlock->Table[i].KeyOffset,
+                                         NULL);
+             if (CurSubKeyCell == NULL)
+               {
+                 DPRINT("CmiGetBlock() failed\n");
+                 return STATUS_UNSUCCESSFUL;
+               }
+
+             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 &&
+             (HashBlock->Table[i].HashValue == 0 ||
+              CmiCompareHash(KeyName, (PCHAR)&HashBlock->Table[i].HashValue)))
+           {
+             CurSubKeyCell = CmiGetCell (RegistryHive,
+                                         HashBlock->Table[i].KeyOffset,
+                                         NULL);
+             if (CurSubKeyCell == NULL)
+               {
+                 DPRINT("CmiGetBlock() failed\n");
+                 return STATUS_UNSUCCESSFUL;
+               }
+
+             if (CmiCompareKeyNames(KeyName, CurSubKeyCell))
+               {
+                 *SubKeyCell = CurSubKeyCell;
+                 *BlockOffset = HashBlock->Table[i].KeyOffset;
+                 break;
+               }
+           }
+       }
+    }
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+CmiAddSubKey(PREGISTRY_HIVE RegistryHive,
+            PKEY_OBJECT ParentKey,
+            PKEY_OBJECT SubKey,
+            PUNICODE_STRING SubKeyName,
+            ULONG TitleIndex,
+            PUNICODE_STRING Class,
+            ULONG CreateOptions)
+{
+  PHASH_TABLE_CELL HashBlock;
+  BLOCK_OFFSET NKBOffset;
+  PKEY_CELL NewKeyCell;
+  ULONG NewBlockSize;
+  PKEY_CELL ParentKeyCell;
+  PDATA_CELL ClassCell;
+  NTSTATUS Status;
+  USHORT NameSize;
+  PWSTR NamePtr;
+  BOOLEAN Packable;
+  ULONG i;
+
+  ParentKeyCell = ParentKey->KeyCell;
+
+  VERIFY_KEY_CELL(ParentKeyCell);
+
+  /* Skip leading backslash */
+  if (SubKeyName->Buffer[0] == L'\\')
+    {
+      NamePtr = &SubKeyName->Buffer[1];
+      NameSize = SubKeyName->Length - sizeof(WCHAR);
+    }
+  else
+    {
+      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;
+  Status = CmiAllocateCell (RegistryHive,
+                           NewBlockSize,
+                           (PVOID) &NewKeyCell,
+                           &NKBOffset);
+  if (NewKeyCell == NULL)
+    {
+      Status = STATUS_INSUFFICIENT_RESOURCES;
+    }
+  else
+    {
+      NewKeyCell->Id = REG_KEY_CELL_ID;
+      NewKeyCell->Flags = 0;
+      KeQuerySystemTime(&NewKeyCell->LastWriteTime);
+      NewKeyCell->ParentKeyOffset = -1;
+      NewKeyCell->NumberOfSubKeys = 0;
+      NewKeyCell->HashTableOffset = -1;
+      NewKeyCell->NumberOfValues = 0;
+      NewKeyCell->ValueListOffset = -1;
+      NewKeyCell->SecurityKeyOffset = -1;
+      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 != NULL)
+       {
+         NewKeyCell->ClassSize = Class->Length;
+         Status = CmiAllocateCell (RegistryHive,
+                                   sizeof(CELL_HEADER) + NewKeyCell->ClassSize,
+                                   (PVOID)&ClassCell,
+                                   &NewKeyCell->ClassNameOffset);
+         RtlCopyMemory (ClassCell->Data,
+                        Class->Buffer,
+                        Class->Length);
+       }
+    }
+
+  if (!NT_SUCCESS(Status))
+    {
+      return Status;
+    }
+
+  SubKey->KeyCell = NewKeyCell;
+  SubKey->KeyCellOffset = NKBOffset;
+
+  /* Don't modify hash table if key is located in a pointer-based hive and parent key is not */
+  if (IsPointerHive(RegistryHive) && (!IsPointerHive(ParentKey->RegistryHive)))
+    {
+      return(Status);
+    }
+
+  if (ParentKeyCell->HashTableOffset == (ULONG_PTR) -1)
+    {
+      Status = CmiAllocateHashTableCell (RegistryHive,
+                                        &HashBlock,
+                                        &ParentKeyCell->HashTableOffset,
+                                        REG_INIT_HASH_TABLE_SIZE);
+      if (!NT_SUCCESS(Status))
+       {
+         return(Status);
+       }
+    }
+  else
+    {
+      HashBlock = CmiGetCell (RegistryHive,
+                             ParentKeyCell->HashTableOffset,
+                             NULL);
+      if (HashBlock == NULL)
+       {
+         DPRINT("CmiGetCell() failed\n");
+         return STATUS_UNSUCCESSFUL;
+       }
+
+      if (((ParentKeyCell->NumberOfSubKeys + 1) >= HashBlock->HashTableSize))
+       {
+         PHASH_TABLE_CELL NewHashBlock;
+         BLOCK_OFFSET HTOffset;
+
+         /* Reallocate the hash table cell */
+         Status = CmiAllocateHashTableCell (RegistryHive,
+                                            &NewHashBlock,
+                                            &HTOffset,
+                                            HashBlock->HashTableSize +
+                                              REG_EXTEND_HASH_TABLE_SIZE);
+         if (!NT_SUCCESS(Status))
+           {
+             return Status;
+           }
+
+         RtlZeroMemory(&NewHashBlock->Table[0],
+                       sizeof(NewHashBlock->Table[0]) * NewHashBlock->HashTableSize);
+         RtlCopyMemory(&NewHashBlock->Table[0],
+                       &HashBlock->Table[0],
+                       sizeof(NewHashBlock->Table[0]) * HashBlock->HashTableSize);
+         CmiDestroyCell (RegistryHive,
+                         HashBlock,
+                         ParentKeyCell->HashTableOffset);
+         ParentKeyCell->HashTableOffset = HTOffset;
+         HashBlock = NewHashBlock;
+       }
+    }
+
+  Status = CmiAddKeyToHashTable(RegistryHive,
+                               HashBlock,
+                               ParentKeyCell->HashTableOffset,
+                               NewKeyCell,
+                               NKBOffset);
+  if (NT_SUCCESS(Status))
+    {
+      ParentKeyCell->NumberOfSubKeys++;
+    }
+
+  KeQuerySystemTime (&ParentKeyCell->LastWriteTime);
+  CmiMarkBlockDirty (RegistryHive, ParentKey->KeyCellOffset);
+
+  return(Status);
+}
+
+
+NTSTATUS
+CmiRemoveSubKey(PREGISTRY_HIVE RegistryHive,
+               PKEY_OBJECT ParentKey,
+               PKEY_OBJECT SubKey)
+{
+  PHASH_TABLE_CELL HashBlock;
+  PVALUE_LIST_CELL ValueList;
+  PVALUE_CELL ValueCell;
+  PDATA_CELL DataCell;
+  ULONG i;
+
+  DPRINT("CmiRemoveSubKey() called\n");
+
+  /* Remove all values */
+  if (SubKey->KeyCell->NumberOfValues != 0)
+    {
+      /* Get pointer to the value list cell */
+      ValueList = CmiGetCell (RegistryHive,
+                             SubKey->KeyCell->ValueListOffset,
+                             NULL);
+      if (ValueList == NULL)
+       {
+         DPRINT("CmiGetCell() failed\n");
+         return STATUS_UNSUCCESSFUL;
+       }
+
+      /* Enumerate all values */
+      for (i = 0; i < SubKey->KeyCell->NumberOfValues; i++)
+       {
+         /* Get pointer to value cell */
+         ValueCell = CmiGetCell(RegistryHive,
+                                ValueList->ValueOffset[i],
+                                NULL);
+         if (ValueCell == NULL)
+           {
+             DPRINT("CmiGetCell() failed\n");
+             return STATUS_UNSUCCESSFUL;
+           }
+
+         if (!(ValueCell->DataSize & REG_DATA_IN_OFFSET)
+              && ValueCell->DataSize > sizeof(BLOCK_OFFSET))
+           {
+             DataCell = CmiGetCell (RegistryHive,
+                                    ValueCell->DataOffset,
+                                    NULL);
+             if (DataCell == NULL)
+               {
+                 DPRINT("CmiGetCell() failed\n");
+                 return STATUS_UNSUCCESSFUL;
+               }
+
+             if (DataCell != NULL)
+               {
+                 /* Destroy data cell */
+                 CmiDestroyCell (RegistryHive,
+                                 DataCell,
+                                 ValueCell->DataOffset);
+               }
+           }
+
+         /* Destroy value cell */
+         CmiDestroyCell (RegistryHive,
+                         ValueCell,
+                         ValueList->ValueOffset[i]);
+       }
+
+      /* Destroy value list cell */
+      CmiDestroyCell (RegistryHive,
+                     ValueList,
+                     SubKey->KeyCell->ValueListOffset);
+
+      SubKey->KeyCell->NumberOfValues = 0;
+      SubKey->KeyCell->ValueListOffset = (BLOCK_OFFSET)-1;
+
+      CmiMarkBlockDirty(RegistryHive,
+                       SubKey->KeyCellOffset);
+    }
+
+  /* Remove the key from the parent key's hash block */
+  if (ParentKey->KeyCell->HashTableOffset != (BLOCK_OFFSET) -1)
+    {
+      DPRINT("ParentKey HashTableOffset %lx\n", ParentKey->KeyCell->HashTableOffset);
+      HashBlock = CmiGetCell (ParentKey->RegistryHive,
+                             ParentKey->KeyCell->HashTableOffset,
+                             NULL);
+      if (HashBlock == NULL)
+       {
+         DPRINT("CmiGetCell() failed\n");
+         return STATUS_UNSUCCESSFUL;
+       }
+      DPRINT("ParentKey HashBlock %p\n", HashBlock);
+      if (HashBlock != NULL)
+       {
+         CmiRemoveKeyFromHashTable(ParentKey->RegistryHive,
+                                   HashBlock,
+                                   SubKey->KeyCellOffset);
+         CmiMarkBlockDirty(ParentKey->RegistryHive,
+                           ParentKey->KeyCell->HashTableOffset);
+       }
+    }
+
+  /* Remove the key's hash block */
+  if (SubKey->KeyCell->HashTableOffset != (BLOCK_OFFSET) -1)
+    {
+      DPRINT("SubKey HashTableOffset %lx\n", SubKey->KeyCell->HashTableOffset);
+      HashBlock = CmiGetCell (RegistryHive,
+                             SubKey->KeyCell->HashTableOffset,
+                             NULL);
+      if (HashBlock == NULL)
+       {
+         DPRINT("CmiGetCell() failed\n");
+         return STATUS_UNSUCCESSFUL;
+       }
+      DPRINT("SubKey HashBlock %p\n", HashBlock);
+      if (HashBlock != NULL)
+       {
+         CmiDestroyCell (RegistryHive,
+                         HashBlock,
+                         SubKey->KeyCell->HashTableOffset);
+         SubKey->KeyCell->HashTableOffset = -1;
+       }
+    }
+
+  /* Decrement the number of the parent key's sub keys */
+  if (ParentKey != NULL)
+    {
+      DPRINT("ParentKey %p\n", ParentKey);
+      ParentKey->KeyCell->NumberOfSubKeys--;
+
+      /* Remove the parent key's hash table */
+      if (ParentKey->KeyCell->NumberOfSubKeys == 0)
+       {
+         DPRINT("ParentKey HashTableOffset %lx\n", ParentKey->KeyCell->HashTableOffset);
+         HashBlock = CmiGetCell (ParentKey->RegistryHive,
+                                 ParentKey->KeyCell->HashTableOffset,
+                                 NULL);
+         if (HashBlock == NULL)
+           {
+             DPRINT("CmiGetCell() failed\n");
+             return STATUS_UNSUCCESSFUL;
+           }
+         DPRINT("ParentKey HashBlock %p\n", HashBlock);
+         if (HashBlock != NULL)
+           {
+             CmiDestroyCell (ParentKey->RegistryHive,
+                             HashBlock,
+                             ParentKey->KeyCell->HashTableOffset);
+             ParentKey->KeyCell->HashTableOffset = (BLOCK_OFFSET)-1;
+           }
+       }
+
+      KeQuerySystemTime(&ParentKey->KeyCell->LastWriteTime);
+      CmiMarkBlockDirty(ParentKey->RegistryHive,
+                       ParentKey->KeyCellOffset);
+    }
+
+  /* Destroy key cell */
+  CmiDestroyCell (RegistryHive,
+                 SubKey->KeyCell,
+                 SubKey->KeyCellOffset);
+  SubKey->KeyCell = NULL;
+  SubKey->KeyCellOffset = (BLOCK_OFFSET)-1;
+
+  DPRINT("CmiRemoveSubKey() done\n");
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+CmiScanKeyForValue(IN PREGISTRY_HIVE RegistryHive,
+                  IN PKEY_CELL KeyCell,
+                  IN PUNICODE_STRING ValueName,
+                  OUT PVALUE_CELL *ValueCell,
+                  OUT BLOCK_OFFSET *ValueCellOffset)
+{
+  PVALUE_LIST_CELL ValueListCell;
+  PVALUE_CELL CurValueCell;
+  ULONG i;
+
+  *ValueCell = NULL;
+  if (ValueCellOffset != NULL)
+    *ValueCellOffset = (BLOCK_OFFSET)-1;
+
+  /* The key does not have any values */
+  if (KeyCell->ValueListOffset == (BLOCK_OFFSET)-1)
+    {
+      return STATUS_OBJECT_NAME_NOT_FOUND;
+    }
+
+  ValueListCell = CmiGetCell (RegistryHive, KeyCell->ValueListOffset, NULL);
+  if (ValueListCell == NULL)
+    {
+      DPRINT("ValueListCell is NULL\n");
+      return STATUS_UNSUCCESSFUL;
+    }
+
+  VERIFY_VALUE_LIST_CELL(ValueListCell);
+
+  for (i = 0; i < KeyCell->NumberOfValues; i++)
+    {
+      CurValueCell = CmiGetCell (RegistryHive,
+                                ValueListCell->ValueOffset[i],
+                                NULL);
+      if (CurValueCell == NULL)
+       {
+         DPRINT("CmiGetBlock() failed\n");
+         return STATUS_UNSUCCESSFUL;
+       }
 
-ULONG
-CmiGetMaxClassLength(PREGISTRY_HIVE  RegistryHive,
-  PKEY_CELL  KeyCell)
+      if ((CurValueCell != NULL) &&
+         CmiComparePackedNames(ValueName,
+                               CurValueCell->Name,
+                               CurValueCell->NameSize,
+                               (BOOLEAN)((CurValueCell->Flags & REG_VALUE_NAME_PACKED) ? TRUE : FALSE)))
+       {
+         *ValueCell = CurValueCell;
+         if (ValueCellOffset != NULL)
+           *ValueCellOffset = ValueListCell->ValueOffset[i];
+         //DPRINT("Found value %s\n", ValueName);
+         return STATUS_SUCCESS;
+       }
+    }
+
+  return STATUS_OBJECT_NAME_NOT_FOUND;
+}
+
+
+NTSTATUS
+CmiGetValueFromKeyByIndex(IN PREGISTRY_HIVE RegistryHive,
+                         IN PKEY_CELL KeyCell,
+                         IN ULONG Index,
+                         OUT PVALUE_CELL *ValueCell)
 {
-  PHASH_TABLE_CELL HashBlock;
-  PKEY_CELL CurSubKeyCell;
-  ULONG MaxClass;
+  PVALUE_LIST_CELL ValueListCell;
+  PVALUE_CELL CurValueCell;
+
+  *ValueCell = NULL;
+
+  if (KeyCell->ValueListOffset == (BLOCK_OFFSET)-1)
+    {
+      return STATUS_NO_MORE_ENTRIES;
+    }
+
+  if (Index >= KeyCell->NumberOfValues)
+    {
+      return STATUS_NO_MORE_ENTRIES;
+    }
+
+
+  ValueListCell = CmiGetCell (RegistryHive, KeyCell->ValueListOffset, NULL);
+  if (ValueListCell == NULL)
+    {
+      DPRINT("CmiGetBlock() failed\n");
+      return STATUS_UNSUCCESSFUL;
+    }
+
+  VERIFY_VALUE_LIST_CELL(ValueListCell);
+
+
+  CurValueCell = CmiGetCell (RegistryHive,
+                            ValueListCell->ValueOffset[Index],
+                            NULL);
+  if (CurValueCell == NULL)
+    {
+      DPRINT("CmiGetBlock() failed\n");
+      return STATUS_UNSUCCESSFUL;
+    }
+
+  *ValueCell = CurValueCell;
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+CmiAddValueToKey(IN PREGISTRY_HIVE RegistryHive,
+                IN PKEY_CELL KeyCell,
+                IN BLOCK_OFFSET KeyCellOffset,
+                IN PUNICODE_STRING ValueName,
+                OUT PVALUE_CELL *pValueCell,
+                OUT BLOCK_OFFSET *pValueCellOffset)
+{
+  PVALUE_LIST_CELL NewValueListCell;
+  PVALUE_LIST_CELL ValueListCell;
+  PVALUE_CELL NewValueCell;
+  BLOCK_OFFSET NewValueListCellOffset;
+  BLOCK_OFFSET ValueListCellOffset;
+  BLOCK_OFFSET NewValueCellOffset;
+  ULONG CellSize;
+  NTSTATUS Status;
+
+  DPRINT("KeyCell->ValuesOffset %lu\n", (ULONG)KeyCell->ValueListOffset);
+
+  ValueListCell = CmiGetCell (RegistryHive, KeyCell->ValueListOffset, NULL);
+  if (ValueListCell == NULL)
+    {
+      CellSize = sizeof(VALUE_LIST_CELL) +
+                (3 * sizeof(BLOCK_OFFSET));
+      Status = CmiAllocateCell (RegistryHive,
+                               CellSize,
+                               (PVOID) &ValueListCell,
+                               &ValueListCellOffset);
+      if (!NT_SUCCESS(Status))
+       {
+         return Status;
+       }
+
+      KeyCell->ValueListOffset = ValueListCellOffset;
+      CmiMarkBlockDirty(RegistryHive, KeyCellOffset);
+      CmiMarkBlockDirty(RegistryHive, ValueListCellOffset);
+    }
+  else if (KeyCell->NumberOfValues >=
+          (((ULONG)ABS_VALUE(ValueListCell->CellSize) - sizeof(VALUE_LIST_CELL)) / sizeof(BLOCK_OFFSET)))
+    {
+#if 0
+      CellSize = sizeof(VALUE_LIST_CELL) +
+                ((KeyCell->NumberOfValues + REG_VALUE_LIST_CELL_MULTIPLE) * sizeof(BLOCK_OFFSET));
+#endif
+      CellSize = 2 * (ULONG)ABS_VALUE(ValueListCell->CellSize);
+      Status = CmiAllocateCell (RegistryHive,
+                               CellSize,
+                               (PVOID) &NewValueListCell,
+                               &NewValueListCellOffset);
+      if (!NT_SUCCESS(Status))
+       {
+         return Status;
+       }
+
+      RtlCopyMemory(&NewValueListCell->ValueOffset[0],
+                   &ValueListCell->ValueOffset[0],
+                   sizeof(BLOCK_OFFSET) * KeyCell->NumberOfValues);
+      CmiDestroyCell (RegistryHive, ValueListCell, KeyCell->ValueListOffset);
+      CmiMarkBlockDirty (RegistryHive, KeyCell->ValueListOffset);
+
+      KeyCell->ValueListOffset = NewValueListCellOffset;
+      ValueListCell = NewValueListCell;
+      CmiMarkBlockDirty (RegistryHive, KeyCellOffset);
+      CmiMarkBlockDirty (RegistryHive, NewValueListCellOffset);
+    }
+
+  DPRINT("KeyCell->NumberOfValues %lu, ValueListCell->CellSize %lu (%lu %lx)\n",
+        KeyCell->NumberOfValues,
+        (ULONG)ABS_VALUE(ValueListCell->CellSize),
+        ((ULONG)ABS_VALUE(ValueListCell->CellSize) - sizeof(VALUE_LIST_CELL)) / sizeof(BLOCK_OFFSET),
+        ((ULONG)ABS_VALUE(ValueListCell->CellSize) - sizeof(VALUE_LIST_CELL)) / sizeof(BLOCK_OFFSET));
+
+  Status = CmiAllocateValueCell(RegistryHive,
+                               &NewValueCell,
+                               &NewValueCellOffset,
+                               ValueName);
+  if (!NT_SUCCESS(Status))
+    {
+      return Status;
+    }
+
+  ValueListCell->ValueOffset[KeyCell->NumberOfValues] = NewValueCellOffset;
+  KeyCell->NumberOfValues++;
+
+  CmiMarkBlockDirty(RegistryHive, KeyCellOffset);
+  CmiMarkBlockDirty(RegistryHive, KeyCell->ValueListOffset);
+  CmiMarkBlockDirty(RegistryHive, NewValueCellOffset);
+
+  *pValueCell = NewValueCell;
+  *pValueCellOffset = NewValueCellOffset;
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+CmiDeleteValueFromKey(IN PREGISTRY_HIVE RegistryHive,
+                     IN PKEY_CELL KeyCell,
+                     IN BLOCK_OFFSET KeyCellOffset,
+                     IN PUNICODE_STRING ValueName)
+{
+  PVALUE_LIST_CELL ValueListCell;
+  PVALUE_CELL CurValueCell;
+  ULONG i;
+  NTSTATUS Status;
+
+  ValueListCell = CmiGetCell (RegistryHive, KeyCell->ValueListOffset, NULL);
+  if (ValueListCell == NULL)
+    {
+      DPRINT1("CmiGetBlock() failed\n");
+      return STATUS_SUCCESS;
+    }
+
+  VERIFY_VALUE_LIST_CELL(ValueListCell);
+
+  for (i = 0; i < KeyCell->NumberOfValues; i++)
+    {
+      CurValueCell = CmiGetCell (RegistryHive, ValueListCell->ValueOffset[i], NULL);
+      if (CurValueCell == NULL)
+       {
+         DPRINT1("CmiGetBlock() failed\n");
+         return STATUS_UNSUCCESSFUL;
+       }
+
+      if (CmiComparePackedNames(ValueName,
+                               CurValueCell->Name,
+                               CurValueCell->NameSize,
+                               (BOOLEAN)((CurValueCell->Flags & REG_VALUE_NAME_PACKED) ? TRUE : FALSE)))
+       {
+         Status = CmiDestroyValueCell(RegistryHive,
+                                      CurValueCell,
+                                      ValueListCell->ValueOffset[i]);
+         if (CurValueCell == NULL)
+           {
+             DPRINT1("CmiDestroyValueCell() failed\n");
+             return Status;
+           }
+
+         if (i < (KeyCell->NumberOfValues - 1))
+           {
+             RtlMoveMemory(&ValueListCell->ValueOffset[i],
+                           &ValueListCell->ValueOffset[i + 1],
+                           sizeof(BLOCK_OFFSET) * (KeyCell->NumberOfValues - 1 - i));
+           }
+         ValueListCell->ValueOffset[KeyCell->NumberOfValues - 1] = 0;
+
+
+         KeyCell->NumberOfValues--;
+
+         if (KeyCell->NumberOfValues == 0)
+           {
+             CmiDestroyCell(RegistryHive,
+                            ValueListCell,
+                            KeyCell->ValueListOffset);
+             KeyCell->ValueListOffset = -1;
+           }
+         else
+           {
+             CmiMarkBlockDirty(RegistryHive,
+                               KeyCell->ValueListOffset);
+           }
+
+         CmiMarkBlockDirty(RegistryHive,
+                           KeyCellOffset);
+
+         return STATUS_SUCCESS;
+       }
+    }
+
+  DPRINT("Couldn't find the desired value\n");
+
+  return STATUS_OBJECT_NAME_NOT_FOUND;
+}
+
+
+NTSTATUS
+CmiAllocateHashTableCell (IN PREGISTRY_HIVE RegistryHive,
+       OUT PHASH_TABLE_CELL *HashBlock,
+       OUT BLOCK_OFFSET *HBOffset,
+       IN ULONG SubKeyCount)
+{
+  PHASH_TABLE_CELL NewHashBlock;
+  ULONG NewHashSize;
+  NTSTATUS Status;
+
+  Status = STATUS_SUCCESS;
+  *HashBlock = NULL;
+  NewHashSize = sizeof(HASH_TABLE_CELL) +
+               (SubKeyCount * sizeof(HASH_RECORD));
+  Status = CmiAllocateCell (RegistryHive,
+                           NewHashSize,
+                           (PVOID*) &NewHashBlock,
+                           HBOffset);
+
+  if ((NewHashBlock == NULL) || (!NT_SUCCESS(Status)))
+    {
+      Status = STATUS_INSUFFICIENT_RESOURCES;
+    }
+  else
+    {
+      ASSERT(SubKeyCount <= 0xffff); /* should really be USHORT_MAX or similar */
+      NewHashBlock->CellSize = -NewHashSize;
+      NewHashBlock->Id = REG_HASH_TABLE_CELL_ID;
+      NewHashBlock->HashTableSize = (USHORT)SubKeyCount;
+      *HashBlock = NewHashBlock;
+    }
+
+  return Status;
+}
+
+
+PKEY_CELL
+CmiGetKeyFromHashByIndex(PREGISTRY_HIVE RegistryHive,
+                        PHASH_TABLE_CELL HashBlock,
+                        ULONG Index)
+{
+  BLOCK_OFFSET KeyOffset;
+  PKEY_CELL KeyCell;
+
+  if (HashBlock == NULL)
+    return NULL;
+
+  if (IsPointerHive(RegistryHive))
+    {
+      KeyCell = (PKEY_CELL) HashBlock->Table[Index].KeyOffset;
+    }
+  else
+    {
+      KeyOffset =  HashBlock->Table[Index].KeyOffset;
+      KeyCell = CmiGetCell (RegistryHive, KeyOffset, NULL);
+    }
+
+  return KeyCell;
+}
+
+
+NTSTATUS
+CmiAddKeyToHashTable(PREGISTRY_HIVE RegistryHive,
+                    PHASH_TABLE_CELL HashCell,
+                    BLOCK_OFFSET HashCellOffset,
+                    PKEY_CELL NewKeyCell,
+                    BLOCK_OFFSET NKBOffset)
+{
+  ULONG i;
+
+  for (i = 0; i < HashCell->HashTableSize; i++)
+    {
+      if (HashCell->Table[i].KeyOffset == 0)
+       {
+         HashCell->Table[i].KeyOffset = NKBOffset;
+         HashCell->Table[i].HashValue = 0;
+         if (NewKeyCell->Flags & REG_KEY_NAME_PACKED)
+           {
+             RtlCopyMemory(&HashCell->Table[i].HashValue,
+                           NewKeyCell->Name,
+                           min(NewKeyCell->NameSize, sizeof(ULONG)));
+           }
+         CmiMarkBlockDirty(RegistryHive, HashCellOffset);
+         return STATUS_SUCCESS;
+       }
+    }
+
+  return STATUS_UNSUCCESSFUL;
+}
+
+
+NTSTATUS
+CmiRemoveKeyFromHashTable(PREGISTRY_HIVE RegistryHive,
+                         PHASH_TABLE_CELL HashBlock,
+                         BLOCK_OFFSET NKBOffset)
+{
+  ULONG i;
+
+  for (i = 0; i < HashBlock->HashTableSize; i++)
+    {
+      if (HashBlock->Table[i].KeyOffset == NKBOffset)
+       {
+         HashBlock->Table[i].KeyOffset = 0;
+         HashBlock->Table[i].HashValue = 0;
+         return STATUS_SUCCESS;
+       }
+    }
+
+  return STATUS_UNSUCCESSFUL;
+}
+
+
+NTSTATUS
+CmiAllocateValueCell(PREGISTRY_HIVE RegistryHive,
+                    PVALUE_CELL *ValueCell,
+                    BLOCK_OFFSET *VBOffset,
+                    IN PUNICODE_STRING ValueName)
+{
+  PVALUE_CELL NewValueCell;
+  NTSTATUS Status;
+  BOOLEAN Packable;
+  ULONG NameSize;
   ULONG i;
 
-  VERIFY_KEY_CELL(KeyCell);
+  Status = STATUS_SUCCESS;
+
+  NameSize = CmiGetPackedNameLength(ValueName,
+                                   &Packable);
+
+  DPRINT("ValueName->Length %lu  NameSize %lu\n", ValueName->Length, NameSize);
+
+  Status = CmiAllocateCell (RegistryHive,
+                           sizeof(VALUE_CELL) + NameSize,
+                           (PVOID*) &NewValueCell,
+                           VBOffset);
+  if ((NewValueCell == NULL) || (!NT_SUCCESS(Status)))
+    {
+      Status = STATUS_INSUFFICIENT_RESOURCES;
+    }
+  else
+    {
+      ASSERT(NameSize <= 0xffff); /* should really be USHORT_MAX or similar */
+      NewValueCell->Id = REG_VALUE_CELL_ID;
+      NewValueCell->NameSize = (USHORT)NameSize;
+      if (Packable)
+       {
+         /* Pack the value name */
+         for (i = 0; i < NameSize; i++)
+           NewValueCell->Name[i] = (CHAR)ValueName->Buffer[i];
+         NewValueCell->Flags |= REG_VALUE_NAME_PACKED;
+       }
+      else
+       {
+         /* Copy the value name */
+         RtlCopyMemory(NewValueCell->Name,
+                       ValueName->Buffer,
+                       NameSize);
+         NewValueCell->Flags = 0;
+       }
+      NewValueCell->DataType = 0;
+      NewValueCell->DataSize = 0;
+      NewValueCell->DataOffset = (BLOCK_OFFSET)-1;
+      *ValueCell = NewValueCell;
+    }
+
+  return Status;
+}
+
+
+NTSTATUS
+CmiDestroyValueCell(PREGISTRY_HIVE RegistryHive,
+                   PVALUE_CELL ValueCell,
+                   BLOCK_OFFSET ValueCellOffset)
+{
+  NTSTATUS Status;
+  PVOID DataCell;
+  PHBIN Bin;
+
+  DPRINT("CmiDestroyValueCell(Cell %p  Offset %lx)\n",
+        ValueCell, ValueCellOffset);
+
+  VERIFY_VALUE_CELL(ValueCell);
+
+  /* Destroy the data cell */
+  if (!(ValueCell->DataSize & REG_DATA_IN_OFFSET)
+      && ValueCell->DataSize > sizeof(BLOCK_OFFSET))
+    {
+      DataCell = CmiGetCell (RegistryHive, ValueCell->DataOffset, &Bin);
+      if (DataCell == NULL)
+       {
+         DPRINT("CmiGetCell() failed\n");
+         return STATUS_UNSUCCESSFUL;
+       }
+
+      Status = CmiDestroyCell (RegistryHive, DataCell, ValueCell->DataOffset);
+      if (!NT_SUCCESS(Status))
+       {
+         return Status;
+       }
 
-  MaxClass = 0;
-  HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
-  if (HashBlock == NULL)
-    {
-      return 0;
+      /* Update time of heap */
+      if (!IsNoFileHive(RegistryHive))
+       KeQuerySystemTime(&Bin->DateModified);
     }
 
-  for (i = 0; i < HashBlock->HashTableSize; i++)
+  /* Destroy the value cell */
+  Status = CmiDestroyCell (RegistryHive, ValueCell, ValueCellOffset);
+
+  /* Update time of heap */
+  if (!IsNoFileHive(RegistryHive) && CmiGetCell (RegistryHive, ValueCellOffset, &Bin))
     {
-      if (HashBlock->Table[i].KeyOffset != 0)
-        {
-          CurSubKeyCell = CmiGetBlock(RegistryHive,
-            HashBlock->Table[i].KeyOffset,
-            NULL);
-          if (MaxClass < CurSubKeyCell->ClassSize)
-            {
-              MaxClass = CurSubKeyCell->ClassSize;
-            }
-          CmiReleaseBlock(RegistryHive, CurSubKeyCell);
-        }
+      KeQuerySystemTime(&Bin->DateModified);
     }
 
-  CmiReleaseBlock(RegistryHive, HashBlock);
-
-  return MaxClass;
+  return Status;
 }
 
 
-ULONG  
-CmiGetMaxValueNameLength(PREGISTRY_HIVE RegistryHive,
-  PKEY_CELL KeyCell)
+NTSTATUS
+CmiAddBin(PREGISTRY_HIVE RegistryHive,
+         ULONG BlockCount,
+         PVOID *NewBlock,
+         BLOCK_OFFSET *NewBlockOffset)
 {
-  PVALUE_LIST_CELL ValueListCell;
-  PVALUE_CELL CurValueCell;
-  ULONG MaxValueName;
+  PBLOCK_LIST_ENTRY BlockList;
+  PCELL_HEADER tmpBlock;
+  PHBIN tmpBin;
+  ULONG BinSize;
   ULONG i;
 
-  VERIFY_KEY_CELL(KeyCell);
-
-  ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
-  MaxValueName = 0;
-  if (ValueListCell == NULL)
-    {
-      return 0;
-    }
+  DPRINT ("CmiAddBin (BlockCount %lu)\n", BlockCount);
 
-  for (i = 0; i < KeyCell->NumberOfValues; i++)
+  BinSize = BlockCount * REG_BLOCK_SIZE;
+  tmpBin = ExAllocatePool(PagedPool, BinSize);
+  if (tmpBin == NULL)
     {
-      CurValueCell = CmiGetBlock(RegistryHive,
-        ValueListCell->Values[i],
-        NULL);
-      if (CurValueCell != NULL &&
-          MaxValueName < CurValueCell->NameSize)
-        {
-          MaxValueName = CurValueCell->NameSize;
-        }
-      CmiReleaseBlock(RegistryHive, CurValueCell);
+      return STATUS_INSUFFICIENT_RESOURCES;
     }
+  RtlZeroMemory (tmpBin,
+                BinSize);
 
-  CmiReleaseBlock(RegistryHive, ValueListCell);
-  
-  return MaxValueName;
-}
+  tmpBin->HeaderId = REG_BIN_ID;
+  tmpBin->BinOffset = RegistryHive->FileSize - REG_BLOCK_SIZE;
+  RegistryHive->FileSize += BinSize;
+  tmpBin->BinSize = BinSize;
+  tmpBin->Unused1 = 0;
+  KeQuerySystemTime(&tmpBin->DateModified);
+  tmpBin->Unused2 = 0;
 
+  DPRINT ("  BinOffset %lx  BinSize %lx\n", tmpBin->BinOffset,tmpBin->BinSize);
 
-ULONG  
-CmiGetMaxValueDataLength(PREGISTRY_HIVE RegistryHive,
-  PKEY_CELL KeyCell)
-{
-  PVALUE_LIST_CELL ValueListCell;
-  PVALUE_CELL CurValueCell;
-  ULONG MaxValueData;
-  ULONG i;
+  /* Allocate new block list */
+  BlockList = ExAllocatePool(NonPagedPool,
+                            sizeof(BLOCK_LIST_ENTRY) * (RegistryHive->BlockListSize + BlockCount));
+  if (BlockList == NULL)
+    {
+      ExFreePool(tmpBin);
+      return STATUS_INSUFFICIENT_RESOURCES;
+    }
 
-  VERIFY_KEY_CELL(KeyCell);
+  if (RegistryHive->BlockListSize > 0)
+    {
+      RtlCopyMemory (BlockList,
+                    RegistryHive->BlockList,
+                    sizeof(BLOCK_LIST_ENTRY) * RegistryHive->BlockListSize);
+      ExFreePool(RegistryHive->BlockList);
+    }
 
-  ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
-  MaxValueData = 0;
-  if (ValueListCell == NULL)
+  RegistryHive->BlockList = BlockList;
+  for (i = 0; i < BlockCount; i++)
     {
-      return 0;
+      RegistryHive->BlockList[RegistryHive->BlockListSize + i].Block =
+       (PVOID)((ULONG_PTR)tmpBin + (i * REG_BLOCK_SIZE));
+      RegistryHive->BlockList[RegistryHive->BlockListSize + i].Bin = tmpBin;
     }
+  RegistryHive->BlockListSize += BlockCount;
 
-  for (i = 0; i < KeyCell->NumberOfValues; i++)
+  /* Initialize a free block in this heap : */
+  tmpBlock = (PCELL_HEADER)((ULONG_PTR) tmpBin + REG_HBIN_DATA_OFFSET);
+  tmpBlock->CellSize = (BinSize - REG_HBIN_DATA_OFFSET);
+
+  /* Grow bitmap if necessary */
+  if (IsNoFileHive(RegistryHive) &&
+      (RegistryHive->BlockListSize % (sizeof(ULONG) * 8) == 0))
     {
-      CurValueCell = CmiGetBlock(RegistryHive,
-                                  ValueListCell->Values[i],NULL);
-      if ((CurValueCell != NULL) &&
-          (MaxValueData < (CurValueCell->DataSize & LONG_MAX)))
-        {
-          MaxValueData = CurValueCell->DataSize & LONG_MAX;
-        }
-      CmiReleaseBlock(RegistryHive, CurValueCell);
+      PULONG BitmapBuffer;
+      ULONG BitmapSize;
+
+      DPRINT("Grow hive bitmap\n");
+
+      /* 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);
+      BitmapBuffer = (PULONG)ExAllocatePool(PagedPool,
+                                           BitmapSize);
+      RtlZeroMemory(BitmapBuffer, BitmapSize);
+      RtlCopyMemory(BitmapBuffer,
+                   RegistryHive->DirtyBitMap.Buffer,
+                   RegistryHive->DirtyBitMap.SizeOfBitMap);
+      ExFreePool(RegistryHive->BitmapBuffer);
+      RegistryHive->BitmapBuffer = BitmapBuffer;
+      RtlInitializeBitMap(&RegistryHive->DirtyBitMap,
+                         RegistryHive->BitmapBuffer,
+                         BitmapSize * 8);
     }
 
-  CmiReleaseBlock(RegistryHive, ValueListCell);
-  
-  return MaxValueData;
+  *NewBlock = (PVOID) tmpBlock;
+
+  if (NewBlockOffset)
+    *NewBlockOffset = tmpBin->BinOffset + REG_HBIN_DATA_OFFSET;
+
+  /* Mark new bin dirty */
+  CmiMarkBinDirty(RegistryHive,
+                 tmpBin->BinOffset);
+
+  return STATUS_SUCCESS;
 }
 
 
 NTSTATUS
-CmiScanForSubKey(IN PREGISTRY_HIVE RegistryHive,
-       IN PKEY_CELL KeyCell,
-       OUT PKEY_CELL *SubKeyCell,
-       OUT BLOCK_OFFSET *BlockOffset,
-       IN PCHAR KeyName,
-       IN ACCESS_MASK DesiredAccess,
-       IN ULONG Attributes)
+CmiAllocateCell (PREGISTRY_HIVE RegistryHive,
+                LONG CellSize,
+                PVOID *Cell,
+                BLOCK_OFFSET *CellOffset)
 {
-  PHASH_TABLE_CELL HashBlock;
-  PKEY_CELL CurSubKeyCell;
-  WORD KeyLength;
+  PCELL_HEADER NewCell;
+  PHBIN Bin;
   ULONG i;
+  PVOID Temp;
+  NTSTATUS Status;
 
-  VERIFY_KEY_CELL(KeyCell);
-
-  DPRINT("Scanning for sub key %s\n", KeyName);
+  /* Round to 16 bytes multiple */
+  CellSize = ROUND_UP(CellSize, 16);
 
-  assert(RegistryHive);
+  /* Handle volatile hives first */
+  if (IsPointerHive(RegistryHive))
+    {
+      NewCell = ExAllocatePool(NonPagedPool, CellSize);
+      if (NewCell == NULL)
+       {
+         return STATUS_INSUFFICIENT_RESOURCES;
+       }
 
-  KeyLength = strlen(KeyName);
+      RtlZeroMemory (NewCell,
+                    CellSize);
+      NewCell->CellSize = -CellSize;
 
-  HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
-  *SubKeyCell = NULL;
-  if (HashBlock == NULL)
-    {
-      return STATUS_SUCCESS;
+      *Cell = NewCell;
+      if (CellOffset != NULL)
+       *CellOffset = (BLOCK_OFFSET) NewCell;
     }
-
-  for (i = 0; (i < KeyCell->NumberOfSubKeys)
-               && (i < HashBlock->HashTableSize); i++)
+  else
     {
-      if (Attributes & OBJ_CASE_INSENSITIVE)
-        {
-          if ((HashBlock->Table[i].KeyOffset != 0) &&
-              (HashBlock->Table[i].KeyOffset != -1) &&
-              (_strnicmp(KeyName, (PCHAR) &HashBlock->Table[i].HashValue, 4) == 0))
-            {
-              CurSubKeyCell = CmiGetBlock(RegistryHive, 
-                HashBlock->Table[i].KeyOffset,
-                NULL);
-              if ((CurSubKeyCell->NameSize == KeyLength)
-                  && (_strnicmp(KeyName, CurSubKeyCell->Name, KeyLength) == 0))
-                {
-                  *SubKeyCell = CurSubKeyCell;
-                  *BlockOffset = HashBlock->Table[i].KeyOffset;
-                  break;
-                }
-              else
-                {
-                  CmiReleaseBlock(RegistryHive, CurSubKeyCell);
-                }
-            }
-        }
-      else
-        {
-          if (HashBlock->Table[i].KeyOffset != 0 &&
-              HashBlock->Table[i].KeyOffset != -1 &&
-              !strncmp(KeyName, (PCHAR) &HashBlock->Table[i].HashValue, 4))
-            {
-              CurSubKeyCell = CmiGetBlock(RegistryHive,
-                HashBlock->Table[i].KeyOffset,NULL);
-              if (CurSubKeyCell->NameSize == KeyLength
-                  && !_strnicmp(KeyName, CurSubKeyCell->Name, KeyLength))
-                {
-                  *SubKeyCell = CurSubKeyCell;
-                  *BlockOffset = HashBlock->Table[i].KeyOffset;
-                  break;
-                }
-              else
-                {
-                  CmiReleaseBlock(RegistryHive, CurSubKeyCell);
-                }
-            }
-        }
+      /* first search in free blocks */
+      NewCell = NULL;
+      for (i = 0; i < RegistryHive->FreeListSize; i++)
+       {
+         if (RegistryHive->FreeList[i]->CellSize >= CellSize)
+           {
+             NewCell = RegistryHive->FreeList[i];
+             if (CellOffset != NULL)
+               *CellOffset = RegistryHive->FreeListOffset[i];
+
+             /* Update time of heap */
+             Temp = CmiGetCell (RegistryHive,
+                                RegistryHive->FreeListOffset[i],
+                                &Bin);
+             if (Temp == NULL)
+               {
+                 DPRINT("CmiGetBlock() failed\n");
+                 return STATUS_UNSUCCESSFUL;
+               }
+
+             KeQuerySystemTime(&Bin->DateModified);
+             CmiMarkBlockDirty(RegistryHive, RegistryHive->FreeListOffset[i]);
+
+             if ((i + 1) < RegistryHive->FreeListSize)
+               {
+                 RtlMoveMemory(&RegistryHive->FreeList[i],
+                               &RegistryHive->FreeList[i + 1],
+                               sizeof(RegistryHive->FreeList[0])
+                                 * (RegistryHive->FreeListSize - i - 1));
+                 RtlMoveMemory(&RegistryHive->FreeListOffset[i],
+                               &RegistryHive->FreeListOffset[i + 1],
+                               sizeof(RegistryHive->FreeListOffset[0])
+                                 * (RegistryHive->FreeListSize - i - 1));
+               }
+             RegistryHive->FreeListSize--;
+             break;
+           }
+       }
+
+      /* Need to extend hive file : */
+      if (NewCell == NULL)
+       {
+         /* Add a new bin */
+         Status = CmiAddBin(RegistryHive,
+                            ((CellSize + sizeof(HBIN) - 1) / REG_BLOCK_SIZE) + 1,
+                            (PVOID *)&NewCell,
+                            CellOffset);
+         if (!NT_SUCCESS(Status))
+           return Status;
+       }
+
+      *Cell = NewCell;
+
+      /* Split the block in two parts */
+      if (NewCell->CellSize > CellSize)
+       {
+         NewCell = (PCELL_HEADER) ((ULONG_PTR) NewCell + CellSize);
+         NewCell->CellSize = ((PCELL_HEADER) (*Cell))->CellSize - CellSize;
+         CmiAddFree(RegistryHive,
+                    NewCell,
+                    *CellOffset + CellSize,
+                    TRUE);
+         CmiMarkBlockDirty(RegistryHive,
+                           *CellOffset + CellSize);
+       }
+      else if (NewCell->CellSize < CellSize)
+       {
+         return STATUS_UNSUCCESSFUL;
+       }
+
+      RtlZeroMemory(*Cell,
+                   CellSize);
+      ((PCELL_HEADER) (*Cell))->CellSize = -CellSize;
     }
-  
-  CmiReleaseBlock(RegistryHive, HashBlock);
-  
+
   return STATUS_SUCCESS;
 }
 
 
 NTSTATUS
-CmiAddSubKey(PREGISTRY_HIVE RegistryHive,
-       PKEY_OBJECT Parent,
-       PKEY_OBJECT SubKey,
-       PWSTR NewSubKeyName,
-       USHORT NewSubKeyNameSize,
-       ULONG TitleIndex,
-       PUNICODE_STRING Class,
-       ULONG CreateOptions)
+CmiDestroyCell (PREGISTRY_HIVE RegistryHive,
+               PVOID Cell,
+               BLOCK_OFFSET CellOffset)
 {
-  PHASH_TABLE_CELL NewHashBlock;
-  PHASH_TABLE_CELL HashBlock;
-  BLOCK_OFFSET NKBOffset;
-  PKEY_CELL NewKeyCell; 
-  ULONG NewBlockSize;
-  PKEY_CELL KeyCell;
   NTSTATUS Status;
-  USHORT NameSize;
-
-  KeyCell = Parent->KeyCell;
+  PHBIN pBin;
 
-  VERIFY_KEY_CELL(KeyCell);
+  Status = STATUS_SUCCESS;
 
-  if (NewSubKeyName[0] == L'\\')
+  if (IsPointerHive(RegistryHive))
     {
-      NewSubKeyName++;
-      NameSize = NewSubKeyNameSize / 2 - 1;
+      ExFreePool(Cell);
     }
   else
     {
-      NameSize = NewSubKeyNameSize / 2;
-    }
-  Status = STATUS_SUCCESS;
+      PCELL_HEADER pFree = Cell;
 
-  NewBlockSize = sizeof(KEY_CELL) + NameSize;
-  Status = CmiAllocateBlock(RegistryHive,
-    (PVOID) &NewKeyCell,
-    NewBlockSize,
-    &NKBOffset);
+      if (pFree->CellSize < 0)
+        pFree->CellSize = -pFree->CellSize;
 
-  if (NewKeyCell == NULL)
-         {
-           Status = STATUS_INSUFFICIENT_RESOURCES;
-         }
-  else
-         {
-           NewKeyCell->Id = REG_KEY_CELL_ID;
-           NewKeyCell->Type = REG_KEY_CELL_TYPE;
-           ZwQuerySystemTime((PTIME) &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;
+      /* Clear block (except the block size) */
+      RtlZeroMemory(((char*)pFree) + sizeof(ULONG),
+                   pFree->CellSize - sizeof(ULONG));
 
-      VERIFY_KEY_CELL(NewKeyCell);
+      /* Add block to the list of free blocks */
+      CmiAddFree(RegistryHive, Cell, CellOffset, TRUE);
 
-           if (Class)
-                   {
-                     PDATA_CELL pClass;
+      /* Update time of heap */
+      if (!IsNoFileHive(RegistryHive) && CmiGetCell (RegistryHive, CellOffset,&pBin))
+       KeQuerySystemTime(&pBin->DateModified);
 
-                     NewKeyCell->ClassSize = Class->Length + sizeof(WCHAR);
-                     Status = CmiAllocateBlock(RegistryHive,
-                                         (PVOID) &pClass,
-                                         NewKeyCell->ClassSize,
-                                         &NewKeyCell->ClassNameOffset);
-                     wcsncpy((PWSTR) pClass->Data, Class->Buffer, Class->Length);
-                     ((PWSTR) (pClass->Data))[Class->Length] = 0;
-                   }
+      CmiMarkBlockDirty(RegistryHive, CellOffset);
     }
 
-  if (!NT_SUCCESS(Status))
+  return Status;
+}
+
+
+PVOID
+CmiGetCell (PREGISTRY_HIVE RegistryHive,
+           BLOCK_OFFSET CellOffset,
+           PHBIN *Bin)
+{
+  PHBIN pBin;
+
+  if (Bin != NULL)
     {
-      return Status;
+      *Bin = NULL;
     }
 
-  SubKey->KeyCell = NewKeyCell;
-  SubKey->BlockOffset = NKBOffset;
-
-  /* Don't modify hash table if key is volatile and parent is not */
-  if (IsVolatileHive(RegistryHive) && (!IsVolatileHive(Parent->RegistryHive)))
-         {
-           return Status;
-         }
+  if (CellOffset == (BLOCK_OFFSET)-1)
+    {
+      return NULL;
+    }
 
-  if (KeyCell->HashTableOffset == -1)
+  if (IsPointerHive (RegistryHive))
     {
-      Status = CmiAllocateHashTableBlock(RegistryHive,
-        &HashBlock,
-                               &KeyCell->HashTableOffset,
-        REG_INIT_HASH_TABLE_SIZE);
+      return (PVOID)CellOffset;
+    }
 
-      if (!NT_SUCCESS(Status))
-        {
-          return Status;
-        }
+  if (CellOffset > RegistryHive->BlockListSize * REG_BLOCK_SIZE)
+    {
+      DPRINT1("CellOffset exceeds valid range (%lu > %lu)\n",
+             CellOffset, RegistryHive->BlockListSize * REG_BLOCK_SIZE);
+      return NULL;
     }
-  else
+
+  pBin = RegistryHive->BlockList[CellOffset / REG_BLOCK_SIZE].Bin;
+  if (pBin == NULL)
     {
-      HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
-      if (((KeyCell->NumberOfSubKeys + 1) >= HashBlock->HashTableSize))
-        {
-          BLOCK_OFFSET HTOffset;
-
-          /* Reallocate the hash table block */
-          Status = CmiAllocateHashTableBlock(RegistryHive,
-            &NewHashBlock,
-                                         &HTOffset,
-            HashBlock->HashTableSize +
-            REG_EXTEND_HASH_TABLE_SIZE);
-
-          if (!NT_SUCCESS(Status))
-            {
-              return Status;
-            }
-
-          RtlZeroMemory(&NewHashBlock->Table[0],
-            sizeof(NewHashBlock->Table[0]) * NewHashBlock->HashTableSize);
-          RtlCopyMemory(&NewHashBlock->Table[0],
-            &HashBlock->Table[0],
-            sizeof(NewHashBlock->Table[0]) * HashBlock->HashTableSize);
-          CmiDestroyBlock(RegistryHive, HashBlock, KeyCell->HashTableOffset);
-               KeyCell->HashTableOffset = HTOffset;
-          HashBlock = NewHashBlock;
-        }
+      return NULL;
     }
 
-  Status = CmiAddKeyToHashTable(RegistryHive, HashBlock, NewKeyCell, NKBOffset);
-  if (NT_SUCCESS(Status))
+  if (Bin != NULL)
     {
-      KeyCell->NumberOfSubKeys++;
+      *Bin = pBin;
     }
-  
-  return Status;
+
+  return((PVOID)((ULONG_PTR)pBin + (CellOffset - pBin->BinOffset)));
 }
 
 
-NTSTATUS
-CmiScanKeyForValue(IN PREGISTRY_HIVE RegistryHive,
-       IN PKEY_CELL KeyCell,
-       IN PCHAR ValueName,
-       OUT PVALUE_CELL *ValueCell,
-       OUT BLOCK_OFFSET *VBOffset)
+static BOOLEAN
+CmiMergeFree(PREGISTRY_HIVE RegistryHive,
+            PCELL_HEADER FreeBlock,
+            BLOCK_OFFSET FreeOffset)
 {
-  PVALUE_LIST_CELL ValueListCell;
-  PVALUE_CELL CurValueCell;
-  ULONG Length;
+  BLOCK_OFFSET BlockOffset;
+  BLOCK_OFFSET BinOffset;
+  ULONG BlockSize;
+  ULONG BinSize;
+  PHBIN Bin;
   ULONG i;
 
-  ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
+  DPRINT("CmiMergeFree(Block %lx  Offset %lx  Size %lx) called\n",
+        FreeBlock, FreeOffset, FreeBlock->CellSize);
 
-  *ValueCell = NULL;
+  CmiGetCell (RegistryHive,
+             FreeOffset,
+             &Bin);
+  DPRINT("Bin %p\n", Bin);
+  if (Bin == NULL)
+    return(FALSE);
 
-  if (ValueListCell == NULL)
+  BinOffset = Bin->BinOffset;
+  BinSize = Bin->BinSize;
+  DPRINT("Bin %p  Offset %lx  Size %lx\n", Bin, BinOffset, BinSize);
+
+  for (i = 0; i < RegistryHive->FreeListSize; i++)
     {
-      DPRINT("ValueListCell is NULL\n");
-      return STATUS_SUCCESS;
-    }
+      BlockOffset = RegistryHive->FreeListOffset[i];
+      BlockSize = RegistryHive->FreeList[i]->CellSize;
+      if (BlockOffset > BinOffset &&
+         BlockOffset < BinOffset + BinSize)
+       {
+         DPRINT("Free block: Offset %lx  Size %lx\n",
+                 BlockOffset, BlockSize);
 
-  VERIFY_VALUE_LIST_CELL(ValueListCell);
+         if ((i < (RegistryHive->FreeListSize - 1)) &&
+             (BlockOffset + BlockSize == FreeOffset) &&
+             (FreeOffset + FreeBlock->CellSize == RegistryHive->FreeListOffset[i + 1]))
+           {
+             DPRINT("Merge current block with previous and next block\n");
 
-  for (i = 0; i < KeyCell->NumberOfValues; i++)
-    {
-      CurValueCell = CmiGetBlock(RegistryHive,
-        ValueListCell->Values[i],
-        NULL);
-      /* FIXME: perhaps we must not ignore case if NtCreateKey has not been */
-      /*        called with OBJ_CASE_INSENSITIVE flag ? */
-      Length = strlen(ValueName);
-      if ((CurValueCell != NULL) &&
-          (CurValueCell->NameSize == Length) &&
-          (_strnicmp(CurValueCell->Name, ValueName, Length) == 0))
-        {
-          *ValueCell = CurValueCell;
-               if (VBOffset)
-            *VBOffset = ValueListCell->Values[i];
-          DPRINT("Found value %s\n", ValueName);
-          break;
-        }
-      CmiReleaseBlock(RegistryHive, CurValueCell);
-    }
+             RegistryHive->FreeList[i]->CellSize +=
+               (FreeBlock->CellSize + RegistryHive->FreeList[i + 1]->CellSize);
 
-  CmiReleaseBlock(RegistryHive, ValueListCell);
-  
-  return STATUS_SUCCESS;
-}
+             FreeBlock->CellSize = 0;
+             RegistryHive->FreeList[i + 1]->CellSize = 0;
 
 
-NTSTATUS
-CmiGetValueFromKeyByIndex(IN PREGISTRY_HIVE RegistryHive,
-       IN PKEY_CELL KeyCell,
-       IN ULONG Index,
-       OUT PVALUE_CELL *ValueCell)
-{
-  PVALUE_LIST_CELL ValueListCell;
-  PVALUE_CELL CurValueCell;
-  ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
+             if ((i + 2) < RegistryHive->FreeListSize)
+               {
+                 RtlMoveMemory(&RegistryHive->FreeList[i + 1],
+                               &RegistryHive->FreeList[i + 2],
+                               sizeof(RegistryHive->FreeList[0])
+                                 * (RegistryHive->FreeListSize - i - 2));
+                 RtlMoveMemory(&RegistryHive->FreeListOffset[i + 1],
+                               &RegistryHive->FreeListOffset[i + 2],
+                               sizeof(RegistryHive->FreeListOffset[0])
+                                 * (RegistryHive->FreeListSize - i - 2));
+               }
+             RegistryHive->FreeListSize--;
 
-  *ValueCell = NULL;
+             CmiMarkBlockDirty(RegistryHive, BlockOffset);
 
-  if (ValueListCell == NULL)
-    {
-      return STATUS_NO_MORE_ENTRIES;
-    }
+             return(TRUE);
+           }
+         else if (BlockOffset + BlockSize == FreeOffset)
+           {
+             DPRINT("Merge current block with previous block\n");
 
-  VERIFY_VALUE_LIST_CELL(ValueListCell);
+             RegistryHive->FreeList[i]->CellSize += FreeBlock->CellSize;
+             FreeBlock->CellSize = 0;
 
-  if (Index >= KeyCell->NumberOfValues)
-    {
-      return STATUS_NO_MORE_ENTRIES;
-    }
+             CmiMarkBlockDirty(RegistryHive, BlockOffset);
 
-  CurValueCell = CmiGetBlock(RegistryHive,
-    ValueListCell->Values[Index],
-    NULL);
+             return(TRUE);
+           }
+         else if (FreeOffset + FreeBlock->CellSize == BlockOffset)
+           {
+             DPRINT("Merge current block with next block\n");
 
-  if (CurValueCell != NULL)
-    {
-      *ValueCell = CurValueCell;
+             FreeBlock->CellSize += RegistryHive->FreeList[i]->CellSize;
+             RegistryHive->FreeList[i]->CellSize = 0;
+             RegistryHive->FreeList[i] = FreeBlock;
+             RegistryHive->FreeListOffset[i] = FreeOffset;
+
+             CmiMarkBlockDirty(RegistryHive, FreeOffset);
+
+             return(TRUE);
+           }
+       }
     }
 
-  CmiReleaseBlock(RegistryHive, CurValueCell);
-  CmiReleaseBlock(RegistryHive, ValueListCell);
-  
-  return STATUS_SUCCESS;
+  return(FALSE);
 }
 
 
 NTSTATUS
-CmiAddValueToKey(IN PREGISTRY_HIVE RegistryHive,
-       IN PKEY_CELL KeyCell,
-       IN PCHAR ValueNameBuf,
-       OUT PVALUE_CELL *pValueCell,
-       OUT BLOCK_OFFSET *pVBOffset)
+CmiAddFree(PREGISTRY_HIVE RegistryHive,
+          PCELL_HEADER FreeBlock,
+          BLOCK_OFFSET FreeOffset,
+          BOOLEAN MergeFreeBlocks)
 {
-  PVALUE_LIST_CELL NewValueListCell;
-  PVALUE_LIST_CELL ValueListCell;
-  PVALUE_CELL NewValueCell;
-  BLOCK_OFFSET VLBOffset;
-  BLOCK_OFFSET VBOffset;
-  NTSTATUS Status;
+  PCELL_HEADER *tmpList;
+  BLOCK_OFFSET *tmpListOffset;
+  LONG minInd;
+  LONG maxInd;
+  LONG medInd;
 
-  Status = CmiAllocateValueCell(RegistryHive,
-               &NewValueCell,
-               &VBOffset,
-               ValueNameBuf);
-  *pVBOffset = VBOffset;
+  ASSERT(RegistryHive);
+  ASSERT(FreeBlock);
 
-  if (!NT_SUCCESS(Status))
+  DPRINT("FreeBlock %.08lx  FreeOffset %.08lx\n",
+        FreeBlock, FreeOffset);
+
+  /* Merge free blocks */
+  if (MergeFreeBlocks == TRUE)
     {
-      return Status;
+      if (CmiMergeFree(RegistryHive, FreeBlock, FreeOffset))
+       return(STATUS_SUCCESS);
     }
 
-  ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
-
-  if (ValueListCell == NULL)
+  if ((RegistryHive->FreeListSize + 1) > RegistryHive->FreeListMax)
     {
-      Status = CmiAllocateBlock(RegistryHive,
-             (PVOID) &ValueListCell,
-             sizeof(BLOCK_OFFSET) * 3,
-             &VLBOffset);
+      tmpList = ExAllocatePool(PagedPool,
+                         sizeof(PCELL_HEADER) * (RegistryHive->FreeListMax + 32));
+      if (tmpList == NULL)
+       return STATUS_INSUFFICIENT_RESOURCES;
 
-      if (!NT_SUCCESS(Status))
-        {
-          CmiDestroyValueCell(RegistryHive, NewValueCell, VBOffset);
-          return Status;
-        }
-      KeyCell->ValuesOffset = VLBOffset;
-    }
-  else if ((KeyCell->NumberOfValues
-               >= ((LONG) (ValueListCell->CellSize - 4)) / (LONG) sizeof(BLOCK_OFFSET)))
-    {
-      Status = CmiAllocateBlock(RegistryHive,
-             (PVOID) &NewValueListCell,
-             sizeof(BLOCK_OFFSET) * (KeyCell->NumberOfValues + REG_VALUE_LIST_CELL_MULTIPLE),
-             &VLBOffset);
+      tmpListOffset = ExAllocatePool(PagedPool,
+                         sizeof(BLOCK_OFFSET) * (RegistryHive->FreeListMax + 32));
 
-      if (!NT_SUCCESS(Status))
-        {
-          CmiDestroyValueCell(RegistryHive, NewValueCell, VBOffset);
-          return Status;
-        }
+      if (tmpListOffset == NULL)
+       {
+         ExFreePool(tmpList);
+         return STATUS_INSUFFICIENT_RESOURCES;
+       }
 
-      RtlCopyMemory(&NewValueListCell->Values[0],
-        &ValueListCell->Values[0],
-        sizeof(BLOCK_OFFSET) * KeyCell->NumberOfValues);
-      CmiDestroyBlock(RegistryHive, ValueListCell, KeyCell->ValuesOffset);
-      KeyCell->ValuesOffset = VLBOffset;
-      ValueListCell = NewValueListCell;
+      if (RegistryHive->FreeListMax)
+       {
+         RtlMoveMemory(tmpList,
+                       RegistryHive->FreeList,
+                       sizeof(PCELL_HEADER) * (RegistryHive->FreeListMax));
+         RtlMoveMemory(tmpListOffset,
+                       RegistryHive->FreeListOffset,
+                       sizeof(BLOCK_OFFSET) * (RegistryHive->FreeListMax));
+         ExFreePool(RegistryHive->FreeList);
+         ExFreePool(RegistryHive->FreeListOffset);
+       }
+      RegistryHive->FreeList = tmpList;
+      RegistryHive->FreeListOffset = tmpListOffset;
+      RegistryHive->FreeListMax += 32;
     }
 
-  DPRINT("KeyCell->NumberOfValues %d, ValueListCell->CellSize %d (%d %x)\n",
-        KeyCell->NumberOfValues, ValueListCell->CellSize,
-        -(ValueListCell->CellSize - 4) / sizeof(BLOCK_OFFSET),
-        -(ValueListCell->CellSize - 4) / sizeof(BLOCK_OFFSET));
+  /* Add new offset to free list, maintaining list in ascending order */
+  if ((RegistryHive->FreeListSize == 0)
+     || (RegistryHive->FreeListOffset[RegistryHive->FreeListSize-1] < FreeOffset))
+    {
+      /* Add to end of list */
+      RegistryHive->FreeList[RegistryHive->FreeListSize] = FreeBlock;
+      RegistryHive->FreeListOffset[RegistryHive->FreeListSize++] = FreeOffset;
+    }
+  else if (RegistryHive->FreeListOffset[0] > FreeOffset)
+    {
+      /* Add to begin of list */
+      RtlMoveMemory(&RegistryHive->FreeList[1],
+                   &RegistryHive->FreeList[0],
+                   sizeof(RegistryHive->FreeList[0]) * RegistryHive->FreeListSize);
+      RtlMoveMemory(&RegistryHive->FreeListOffset[1],
+                   &RegistryHive->FreeListOffset[0],
+                   sizeof(RegistryHive->FreeListOffset[0]) * RegistryHive->FreeListSize);
+      RegistryHive->FreeList[0] = FreeBlock;
+      RegistryHive->FreeListOffset[0] = FreeOffset;
+      RegistryHive->FreeListSize++;
+    }
+  else
+    {
+      /* Search where to insert */
+      minInd = 0;
+      maxInd = RegistryHive->FreeListSize - 1;
+      while ((maxInd - minInd) > 1)
+       {
+         medInd = (minInd + maxInd) / 2;
+         if (RegistryHive->FreeListOffset[medInd] > FreeOffset)
+           maxInd = medInd;
+         else
+           minInd = medInd;
+       }
 
-  ValueListCell->Values[KeyCell->NumberOfValues] = VBOffset;
-  KeyCell->NumberOfValues++;
-  CmiReleaseBlock(RegistryHive, ValueListCell);
-  CmiReleaseBlock(RegistryHive, NewValueCell);
-  *pValueCell = NewValueCell;
+      /* Insert before maxInd */
+      RtlMoveMemory(&RegistryHive->FreeList[maxInd+1],
+                   &RegistryHive->FreeList[maxInd],
+                   sizeof(RegistryHive->FreeList[0]) * (RegistryHive->FreeListSize - minInd));
+      RtlMoveMemory(&RegistryHive->FreeListOffset[maxInd + 1],
+                   &RegistryHive->FreeListOffset[maxInd],
+                   sizeof(RegistryHive->FreeListOffset[0]) * (RegistryHive->FreeListSize-minInd));
+      RegistryHive->FreeList[maxInd] = FreeBlock;
+      RegistryHive->FreeListOffset[maxInd] = FreeOffset;
+      RegistryHive->FreeListSize++;
+    }
 
   return STATUS_SUCCESS;
 }
 
 
-NTSTATUS
-CmiDeleteValueFromKey(IN PREGISTRY_HIVE RegistryHive,
-       IN PKEY_CELL KeyCell,
-       IN PCHAR ValueName)
+VOID
+CmiMarkBlockDirty(PREGISTRY_HIVE RegistryHive,
+                 BLOCK_OFFSET BlockOffset)
 {
-  PVALUE_LIST_CELL ValueListCell;
-  PVALUE_CELL CurValueCell;
-  PHBIN pBin;
-  ULONG  i;
+  PDATA_CELL Cell;
+  LONG CellSize;
+  ULONG BlockNumber;
+  ULONG BlockCount;
 
-  ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
+  if (IsNoFileHive(RegistryHive))
+    return;
 
-  if (ValueListCell == NULL)
-    {
-      return STATUS_SUCCESS;
-    }
+  DPRINT("CmiMarkBlockDirty(Offset 0x%lx)\n", (ULONG)BlockOffset);
 
-  VERIFY_VALUE_LIST_CELL(ValueListCell);
+  BlockNumber = (ULONG)BlockOffset / REG_BLOCK_SIZE;
 
-  for (i = 0; i < KeyCell->NumberOfValues; i++)
-    {
-      CurValueCell = CmiGetBlock(RegistryHive, ValueListCell->Values[i], &pBin);
-      if ((CurValueCell != NULL) &&
-          (CurValueCell->NameSize == strlen(ValueName)) &&
-          (memcmp(CurValueCell->Name, ValueName, strlen(ValueName)) == 0))
-        {
-          if ((KeyCell->NumberOfValues - 1) < i)
-            {
-              RtlCopyMemory(&ValueListCell->Values[i],
-                &ValueListCell->Values[i + 1],
-                sizeof(BLOCK_OFFSET) * (KeyCell->NumberOfValues - 1 - i));
-            }
-          else
-            {
-              RtlZeroMemory(&ValueListCell->Values[i], sizeof(BLOCK_OFFSET));
-            }
-
-          KeyCell->NumberOfValues -= 1;
-          CmiDestroyValueCell(RegistryHive, CurValueCell, ValueListCell->Values[i]);
-          /* update time of heap */
-          ZwQuerySystemTime((PTIME) &pBin->DateModified);
-          break;
-        }
-      CmiReleaseBlock(RegistryHive, CurValueCell);
-    }
+  Cell = CmiGetCell (RegistryHive,
+                    BlockOffset,
+                    NULL);
 
-  CmiReleaseBlock(RegistryHive, ValueListCell);
+  CellSize = Cell->CellSize;
+  if (CellSize < 0)
+    CellSize = -CellSize;
 
-  return STATUS_SUCCESS;
+  BlockCount = (ROUND_UP(BlockOffset + CellSize, REG_BLOCK_SIZE) -
+               ROUND_DOWN(BlockOffset, REG_BLOCK_SIZE)) / REG_BLOCK_SIZE;
+
+  DPRINT("  BlockNumber %lu  Size %lu (%s)  BlockCount %lu\n",
+        BlockNumber,
+        CellSize,
+        (Cell->CellSize < 0) ? "used" : "free",
+        BlockCount);
+
+  RegistryHive->HiveDirty = TRUE;
+  RtlSetBits(&RegistryHive->DirtyBitMap,
+            BlockNumber,
+            BlockCount);
 }
 
 
-NTSTATUS
-CmiAllocateHashTableBlock(IN PREGISTRY_HIVE RegistryHive,
-       OUT PHASH_TABLE_CELL *HashBlock,
-       OUT BLOCK_OFFSET *HBOffset,
-       IN ULONG HashTableSize)
+VOID
+CmiMarkBinDirty(PREGISTRY_HIVE RegistryHive,
+               BLOCK_OFFSET BinOffset)
 {
-  PHASH_TABLE_CELL NewHashBlock;
-  ULONG NewHashSize;
-  NTSTATUS Status;
+  ULONG BlockNumber;
+  ULONG BlockCount;
+  PHBIN Bin;
 
-  Status = STATUS_SUCCESS;
-  *HashBlock = NULL;
-  NewHashSize = sizeof(HASH_TABLE_CELL) + 
-    (HashTableSize - 1) * sizeof(HASH_RECORD);
-  Status = CmiAllocateBlock(RegistryHive,
-               (PVOID*) &NewHashBlock,
-               NewHashSize,
-    HBOffset);
-
-  if ((NewHashBlock == NULL) || (!NT_SUCCESS(Status)))
-         {
-           Status = STATUS_INSUFFICIENT_RESOURCES;
-         }
-  else
-         {
-           NewHashBlock->Id = REG_HASH_TABLE_BLOCK_ID;
-           NewHashBlock->HashTableSize = HashTableSize;
-           *HashBlock = NewHashBlock;
-         }
+  if (IsNoFileHive(RegistryHive))
+    return;
 
-  return Status;
-}
+  DPRINT("CmiMarkBinDirty(Offset 0x%lx)\n", (ULONG)BinOffset);
 
+  BlockNumber = (ULONG)BinOffset / REG_BLOCK_SIZE;
 
-PKEY_CELL
-CmiGetKeyFromHashByIndex(PREGISTRY_HIVE RegistryHive,
-       PHASH_TABLE_CELL HashBlock,
-       ULONG Index)
-{
-  BLOCK_OFFSET KeyOffset;
-  PKEY_CELL KeyCell;
+  Bin = RegistryHive->BlockList[BlockNumber].Bin;
 
-  if (HashBlock == NULL)
-    return NULL;
+  BlockCount = Bin->BinSize / REG_BLOCK_SIZE;
 
-  if (IsVolatileHive(RegistryHive))
-    {
-      KeyCell = (PKEY_CELL) HashBlock->Table[Index].KeyOffset;
-    }
-  else
-    {
-      KeyOffset =  HashBlock->Table[Index].KeyOffset;
-      KeyCell = CmiGetBlock(RegistryHive, KeyOffset, NULL);
-    }
-  CmiLockBlock(RegistryHive, KeyCell);
+  DPRINT("  BlockNumber %lu  BinSize %lu  BlockCount %lu\n",
+        BlockNumber,
+        Bin->BinSize,
+        BlockCount);
 
-  return KeyCell;
+  RegistryHive->HiveDirty = TRUE;
+  RtlSetBits(&RegistryHive->DirtyBitMap,
+            BlockNumber,
+            BlockCount);
 }
 
 
-NTSTATUS
-CmiAddKeyToHashTable(PREGISTRY_HIVE RegistryHive,
-       PHASH_TABLE_CELL HashBlock,
-       PKEY_CELL NewKeyCell,
-       BLOCK_OFFSET NKBOffset)
+ULONG
+CmiGetPackedNameLength(IN PUNICODE_STRING Name,
+                      OUT PBOOLEAN Packable)
 {
   ULONG i;
 
-  for (i = 0; i < HashBlock->HashTableSize; i++)
+  if (Packable != NULL)
+    *Packable = TRUE;
+
+  for (i = 0; i < Name->Length / sizeof(WCHAR); i++)
     {
-      if (HashBlock->Table[i].KeyOffset == 0)
+      if (Name->Buffer[i] & 0xFF00)
        {
-         HashBlock->Table[i].KeyOffset = NKBOffset;
-         RtlCopyMemory(&HashBlock->Table[i].HashValue, NewKeyCell->Name, 4);
-         return STATUS_SUCCESS;
+         if (Packable != NULL)
+           *Packable = FALSE;
+         return Name->Length;
        }
     }
 
-  return STATUS_UNSUCCESSFUL;
+  return (Name->Length / sizeof(WCHAR));
 }
 
 
-NTSTATUS
-CmiAllocateValueCell(PREGISTRY_HIVE RegistryHive,
-  PVALUE_CELL *ValueCell,
-  BLOCK_OFFSET *VBOffset,
-  IN PCHAR ValueNameBuf)
+BOOLEAN
+CmiComparePackedNames(IN PUNICODE_STRING Name,
+                     IN PUCHAR NameBuffer,
+                     IN USHORT NameBufferSize,
+                     IN BOOLEAN NamePacked)
 {
-  PVALUE_CELL NewValueCell;
-  ULONG NewValueSize;
-  NTSTATUS Status;
-
-  Status = STATUS_SUCCESS;
-
-  NewValueSize = sizeof(VALUE_CELL) + strlen(ValueNameBuf);
-  Status = CmiAllocateBlock(RegistryHive,
-    (PVOID*) &NewValueCell,
-    NewValueSize,
-    VBOffset);
+  PWCHAR UNameBuffer;
+  ULONG i;
 
-  if ((NewValueCell == NULL) || (!NT_SUCCESS(Status)))
+  if (NamePacked == TRUE)
     {
-      Status = STATUS_INSUFFICIENT_RESOURCES;
+      if (Name->Length != NameBufferSize * sizeof(WCHAR))
+       return(FALSE);
+
+      for (i = 0; i < Name->Length / sizeof(WCHAR); i++)
+       {
+         if (RtlUpcaseUnicodeChar(Name->Buffer[i]) != RtlUpcaseUnicodeChar((WCHAR)NameBuffer[i]))
+           return(FALSE);
+       }
     }
   else
     {
-      NewValueCell->Id = REG_VALUE_CELL_ID;
-      NewValueCell->NameSize = strlen(ValueNameBuf);
-      memcpy(NewValueCell->Name, ValueNameBuf, strlen(ValueNameBuf));
-      NewValueCell->DataType = 0;
-      NewValueCell->DataSize = 0;
-      NewValueCell->DataOffset = 0xffffffff;
-      *ValueCell = NewValueCell;
+      if (Name->Length != NameBufferSize)
+       return(FALSE);
+
+      UNameBuffer = (PWCHAR)NameBuffer;
+
+      for (i = 0; i < Name->Length / sizeof(WCHAR); i++)
+       {
+         if (RtlUpcaseUnicodeChar(Name->Buffer[i]) != RtlUpcaseUnicodeChar(UNameBuffer[i]))
+           return(FALSE);
+       }
     }
 
-  return Status;
+  return(TRUE);
 }
 
 
-NTSTATUS
-CmiDestroyValueCell(PREGISTRY_HIVE RegistryHive,
-  PVALUE_CELL ValueCell,
-  BLOCK_OFFSET VBOffset)
+VOID
+CmiCopyPackedName(PWCHAR NameBuffer,
+                 PUCHAR PackedNameBuffer,
+                 ULONG PackedNameSize)
 {
-  NTSTATUS Status;
-  PVOID pBlock;
-  PHBIN pBin;
-
-  VERIFY_VALUE_CELL(ValueCell);
+  ULONG i;
 
-  /* First, release datas: */
-  if (ValueCell->DataSize > 0)
-    {
-      pBlock = CmiGetBlock(RegistryHive, ValueCell->DataOffset, &pBin);
-      Status = CmiDestroyBlock(RegistryHive, pBlock, ValueCell->DataOffset);
-      if (!NT_SUCCESS(Status))
-       {
-         return  Status;
-       }
+  for (i = 0; i < PackedNameSize; i++)
+    NameBuffer[i] = (WCHAR)PackedNameBuffer[i];
+}
 
-      /* Update time of heap */
-      if (IsPermanentHive(RegistryHive))
-       ZwQuerySystemTime((PTIME) &pBin->DateModified);
-    }
 
-  Status = CmiDestroyBlock(RegistryHive, ValueCell, VBOffset);
+BOOLEAN
+CmiCompareHash(PUNICODE_STRING KeyName,
+              PCHAR HashString)
+{
+  CHAR Buffer[4];
 
-  /* Update time of heap */
-  if (IsPermanentHive(RegistryHive) && CmiGetBlock(RegistryHive, VBOffset, &pBin))
-    {
-      ZwQuerySystemTime((PTIME) &pBin->DateModified);
-    }
+  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 Status;
+  return (strncmp(Buffer, HashString, 4) == 0);
 }
 
 
-NTSTATUS
-CmiAddBin(PREGISTRY_HIVE RegistryHive,
-  PVOID *NewBlock,
-  BLOCK_OFFSET *NewBlockOffset)
+BOOLEAN
+CmiCompareHashI(PUNICODE_STRING KeyName,
+               PCHAR HashString)
 {
-  PCELL_HEADER tmpBlock;
-  PHBIN * tmpBlockList;
-  PHBIN tmpBin;
+  CHAR Buffer[4];
 
-  tmpBin = ExAllocatePool(PagedPool, REG_BLOCK_SIZE);
-  if (tmpBin == NULL)
-    {
-      return STATUS_INSUFFICIENT_RESOURCES;
-    }
+  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;
 
-  tmpBin->BlockId = REG_BIN_ID;
-  tmpBin->BlockOffset = RegistryHive->FileSize - REG_BLOCK_SIZE;
-  RegistryHive->FileSize += REG_BLOCK_SIZE;
-  tmpBin->BlockSize = REG_BLOCK_SIZE;
-  tmpBin->Unused1 = 0;
-  ZwQuerySystemTime((PTIME) &tmpBin->DateModified);
-  tmpBin->Unused2 = 0;
+  return (_strnicmp(Buffer, HashString, 4) == 0);
+}
 
-  /* Increase size of list of blocks */
-  tmpBlockList = ExAllocatePool(NonPagedPool,
-         sizeof(PHBIN *) * (RegistryHive->BlockListSize + 1));
-  if (tmpBlockList == NULL)
-    {
-      ExFreePool(tmpBin);
-      return STATUS_INSUFFICIENT_RESOURCES;
-    }
 
-  if (RegistryHive->BlockListSize > 0)
-    {
-      memcpy(tmpBlockList,
-            RegistryHive->BlockList,
-            sizeof(PHBIN *)*(RegistryHive->BlockListSize));
-      ExFreePool(RegistryHive->BlockList);
-    }
+BOOLEAN
+CmiCompareKeyNames(PUNICODE_STRING KeyName,
+                  PKEY_CELL KeyCell)
+{
+  PWCHAR UnicodeName;
+  USHORT i;
 
-  RegistryHive->BlockList = tmpBlockList;
-  RegistryHive->BlockList[RegistryHive->BlockListSize++] = tmpBin;
+  DPRINT("Flags: %hx\n", KeyCell->Flags);
 
-  /* Initialize a free block in this heap : */
-  tmpBlock = (PCELL_HEADER)((ULONG_PTR) tmpBin + REG_HBIN_DATA_OFFSET);
-  tmpBlock->CellSize = (REG_BLOCK_SIZE - REG_HBIN_DATA_OFFSET);
-  *NewBlock = (PVOID) tmpBlock;
+  if (KeyCell->Flags & REG_KEY_NAME_PACKED)
+    {
+      if (KeyName->Length != KeyCell->NameSize * sizeof(WCHAR))
+       return FALSE;
 
-  if (NewBlockOffset)
-    *NewBlockOffset = tmpBin->BlockOffset + REG_HBIN_DATA_OFFSET;
+      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;
 
-  /* FIXME: set first dword to block_offset of another free bloc */
+      UnicodeName = (PWCHAR)KeyCell->Name;
+      for (i = 0; i < KeyCell->NameSize / sizeof(WCHAR); i++)
+       {
+         if (KeyName->Buffer[i] != UnicodeName[i])
+           return FALSE;
+       }
+    }
 
-  return STATUS_SUCCESS;
+  return TRUE;
 }
 
 
-NTSTATUS
-CmiAllocateBlock(PREGISTRY_HIVE RegistryHive,
-       PVOID *Block,
-       LONG BlockSize,
-  BLOCK_OFFSET * pBlockOffset)
+BOOLEAN
+CmiCompareKeyNamesI(PUNICODE_STRING KeyName,
+                   PKEY_CELL KeyCell)
 {
-  PCELL_HEADER NewBlock;
-  NTSTATUS Status;
-  PHBIN pBin;
+  PWCHAR UnicodeName;
+  USHORT i;
 
-  Status = STATUS_SUCCESS;
+  DPRINT("Flags: %hx\n", KeyCell->Flags);
 
-  /* Round to 16 bytes multiple */
-  BlockSize = (BlockSize + sizeof(DWORD) + 15) & 0xfffffff0;
+  if (KeyCell->Flags & REG_KEY_NAME_PACKED)
+    {
+      if (KeyName->Length != KeyCell->NameSize * sizeof(WCHAR))
+       return FALSE;
 
-  /* Handle volatile hives first */
-  if (IsVolatileHive(RegistryHive))
-    {
-      NewBlock = ExAllocatePool(NonPagedPool, BlockSize);
-
-           if (NewBlock == NULL)
-                   {
-                     Status = STATUS_INSUFFICIENT_RESOURCES;
-                   }
-           else
-                   {
-                     RtlZeroMemory(NewBlock, BlockSize);
-                     NewBlock->CellSize = BlockSize;
-                     CmiLockBlock(RegistryHive, NewBlock);
-                     *Block = NewBlock;
-                     if (pBlockOffset)
-            *pBlockOffset = (BLOCK_OFFSET) NewBlock;
-                   }
+      for (i = 0; i < KeyCell->NameSize; i++)
+       {
+         if (RtlUpcaseUnicodeChar(KeyName->Buffer[i]) !=
+             RtlUpcaseUnicodeChar((WCHAR)KeyCell->Name[i]))
+           return FALSE;
+       }
     }
   else
     {
-           ULONG i;
-
-           /* first search in free blocks */
-           NewBlock = NULL;
-           for (i = 0; i < RegistryHive->FreeListSize; i++)
-                   {
-                     if (RegistryHive->FreeList[i]->CellSize >= BlockSize)
-                             {
-              PVOID Temp;
-                                                       NewBlock = RegistryHive->FreeList[i];
-
-                                                       if (pBlockOffset)
-                                       *pBlockOffset = RegistryHive->FreeListOffset[i];
-
-                                /* Update time of heap */
-               Temp = CmiGetBlock(RegistryHive, RegistryHive->FreeListOffset[i], &pBin);
-
-                                if (Temp)
-                                  ZwQuerySystemTime((PTIME) &pBin->DateModified);
-
-                                if ((i + 1) < RegistryHive->FreeListSize)
-                 {
-                                    RtlMoveMemory(&RegistryHive->FreeList[i],
-                     &RegistryHive->FreeList[i + 1],
-                                            sizeof(RegistryHive->FreeList[0])
-                       * (RegistryHive->FreeListSize - i - 1));
-                                    RtlMoveMemory(&RegistryHive->FreeListOffset[i],
-                                            &RegistryHive->FreeListOffset[i + 1],
-                                            sizeof(RegistryHive->FreeListOffset[0])
-                       * (RegistryHive->FreeListSize - i - 1));
-                 }
-                                RegistryHive->FreeListSize--;
-                                break;
-                             }
-                   }
-
-           /* Need to extend hive file : */
-           if (NewBlock == NULL)
-                   {
-                     /* Add a new block */
-                     Status = CmiAddBin(RegistryHive, (PVOID *) &NewBlock , pBlockOffset);
-                   }
-
-           if (NT_SUCCESS(Status))
-                   {
-                     *Block = NewBlock;
-
-                     /* Split the block in two parts */
-                     if (NewBlock->CellSize > BlockSize)
-                             {
-                                                       NewBlock = (PCELL_HEADER) ((ULONG_PTR) NewBlock+BlockSize);
-                                                       NewBlock->CellSize = ((PCELL_HEADER) (*Block))->CellSize - BlockSize;
-                                                       CmiAddFree(RegistryHive, NewBlock, *pBlockOffset + BlockSize);
-                             }
-                     else if (NewBlock->CellSize < BlockSize)
-            {
-                               return STATUS_UNSUCCESSFUL;
-            }
-                     RtlZeroMemory(*Block, BlockSize);
-                     ((PCELL_HEADER) (*Block))->CellSize = -BlockSize;
-                     CmiLockBlock(RegistryHive, *Block);
-                   }
-         }
-  return  Status;
+      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
-CmiDestroyBlock(PREGISTRY_HIVE RegistryHive,
-  PVOID Block,
-  BLOCK_OFFSET Offset)
+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;
-  PHBIN pBin;
-
-  Status = STATUS_SUCCESS;
 
-  if (IsVolatileHive(RegistryHive))
-         {
-           CmiReleaseBlock(RegistryHive, Block);
-           ExFreePool(Block);
-         }
-  else
-         {
-           PCELL_HEADER pFree = Block;
+  DPRINT ("CmiCopyKey() called\n");
 
-           if (pFree->CellSize < 0)
-             pFree->CellSize = -pFree->CellSize;
+  if (DstKeyCell == NULL)
+    {
+      /* Allocate and copy key cell */
+      NewKeyCellSize = sizeof(KEY_CELL) + SrcKeyCell->NameSize;
+      Status = CmiAllocateCell (DstHive,
+                               NewKeyCellSize,
+                               (PVOID) &NewKeyCell,
+                               &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;
+       }
 
-           CmiAddFree(RegistryHive, Block, Offset);
-           CmiReleaseBlock(RegistryHive, Block);
+      RtlCopyMemory (NewKeyCell,
+                    SrcKeyCell,
+                    NewKeyCellSize);
 
-           /* Update time of heap */
-           if (IsPermanentHive(RegistryHive) && CmiGetBlock(RegistryHive, Offset,&pBin))
-             ZwQuerySystemTime((PTIME) &pBin->DateModified);
+      DstHive->HiveHeader->RootKeyOffset = NewKeyCellOffset;
 
-             /* FIXME: Set first dword to block_offset of another free block ? */
-             /* FIXME: Concatenate with previous and next block if free */
-         }
+      /* Copy class name */
+      if (SrcKeyCell->ClassNameOffset != (BLOCK_OFFSET) -1)
+       {
+         PDATA_CELL SrcClassNameCell;
+         PDATA_CELL NewClassNameCell;
+         BLOCK_OFFSET NewClassNameOffset;
+
+         SrcClassNameCell = CmiGetCell (SrcHive, SrcKeyCell->ClassNameOffset, NULL),
+
+         NewKeyCell->ClassSize = SrcKeyCell->ClassSize;
+         Status = CmiAllocateCell (DstHive,
+                                   sizeof(CELL_HEADER) + NewKeyCell->ClassSize,
+                                   (PVOID)&NewClassNameCell,
+                                   &NewClassNameOffset);
+         if (!NT_SUCCESS(Status))
+           {
+             DPRINT1 ("CmiAllocateBlock() failed (Status %lx)\n", Status);
+             return 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 = CmiAllocateHashTableCell (DstHive,
+                                        &NewHashTableCell,
+                                        &NewHashTableOffset,
+                                        NewHashTableSize);
+      if (!NT_SUCCESS(Status))
+       {
+         DPRINT1 ("CmiAllocateHashTableBlock() failed (Status %lx)\n", Status);
+         return Status;
+       }
+      NewKeyCell->HashTableOffset = NewHashTableOffset;
+    }
+  else
+    {
+      NewHashTableCell = NULL;
+    }
 
-NTSTATUS
-CmiAddFree(PREGISTRY_HIVE RegistryHive,
-  PCELL_HEADER FreeBlock,
-  BLOCK_OFFSET FreeOffset)
-{
-       PCELL_HEADER *tmpList;
-       BLOCK_OFFSET *tmpListOffset;
-       LONG minInd;
-  LONG maxInd;
-  LONG medInd;
+  /* 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 = CmiAllocateCell (DstHive,
+                               NewValueListCellSize,
+                               (PVOID)&NewValueListCell,
+                               &NewKeyCell->ValueListOffset);
+      if (!NT_SUCCESS(Status))
+       {
+         DPRINT1 ("CmiAllocateBlock() failed (Status %lx)\n", Status);
+         return Status;
+       }
 
-  assert(RegistryHive);
-  assert(FreeBlock);
+      RtlZeroMemory (NewValueListCell,
+                    NewValueListCellSize);
 
-  DPRINT("FreeBlock %.08x  FreeOffset %.08x\n",
-    FreeBlock, FreeOffset);
-DPRINT("\n");
-  if ((RegistryHive->FreeListSize + 1) > RegistryHive->FreeListMax)
-         {
-DPRINT("\n");
-           tmpList = ExAllocatePool(PagedPool,
-                         sizeof(PCELL_HEADER) * (RegistryHive->FreeListMax + 32));
-DPRINT("\n");
+      /* Copy values */
+      SrcValueListCell = CmiGetCell (SrcHive, SrcKeyCell->ValueListOffset, NULL);
+      for (i = 0; i < SrcKeyCell->NumberOfValues; i++)
+       {
+         /* Copy value cell */
+         SrcValueCell = CmiGetCell (SrcHive, SrcValueListCell->ValueOffset[i], NULL);
+
+         NewValueCellSize = sizeof(VALUE_CELL) + SrcValueCell->NameSize;
+         Status = CmiAllocateCell (DstHive,
+                                   NewValueCellSize,
+                                   (PVOID*) &NewValueCell,
+                                   &ValueCellOffset);
+         if (!NT_SUCCESS(Status))
+           {
+             DPRINT1 ("CmiAllocateBlock() failed (Status %lx)\n", Status);
+             return Status;
+           }
 
-           if (tmpList == NULL)
-        return STATUS_INSUFFICIENT_RESOURCES;
-DPRINT("\n");
+         NewValueListCell->ValueOffset[i] = ValueCellOffset;
+         RtlCopyMemory (NewValueCell,
+                        SrcValueCell,
+                        NewValueCellSize);
 
-           tmpListOffset = ExAllocatePool(PagedPool,
-                         sizeof(BLOCK_OFFSET *) * (RegistryHive->FreeListMax + 32));
-DPRINT("\n");
+         /* Copy value data cell */
+         if (SrcValueCell->DataSize > (LONG) sizeof(PVOID))
+           {
+             SrcValueDataCell = CmiGetCell (SrcHive, SrcValueCell->DataOffset, NULL);
 
-           if (tmpListOffset == NULL)
-        {
-          ExFreePool(tmpList);
-          return STATUS_INSUFFICIENT_RESOURCES;
-        }
-DPRINT("\n");
+             Status = CmiAllocateCell (DstHive,
+                                       sizeof(CELL_HEADER) + SrcValueCell->DataSize,
+                                       (PVOID*) &NewValueDataCell,
+                                       &ValueDataCellOffset);
+             if (!NT_SUCCESS(Status))
+               {
+                 DPRINT1 ("CmiAllocateBlock() failed (Status %lx)\n", Status);
+                 return Status;
+               }
+             RtlCopyMemory (NewValueDataCell,
+                            SrcValueDataCell,
+                            SrcValueCell->DataSize);
+             NewValueCell->DataOffset = ValueDataCellOffset;
+           }
+       }
+    }
 
-           if (RegistryHive->FreeListMax)
+  /* 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 = CmiGetCell (SrcHive,
+                                    SrcKeyCell->HashTableOffset,
+                                    NULL);
+
+      for (i = 0; i < SrcKeyCell->NumberOfSubKeys; i++)
+       {
+         SrcHashRecord = &SrcHashTableCell->Table[i];
+         SrcSubKeyCell = CmiGetCell (SrcHive, SrcHashRecord->KeyOffset, NULL);
+
+         /* Allocate and copy key cell */
+         NewSubKeyCellSize = sizeof(KEY_CELL) + SrcSubKeyCell->NameSize;
+         Status = CmiAllocateCell (DstHive,
+                                   NewSubKeyCellSize,
+                                   (PVOID)&NewSubKeyCell,
+                                   &NewSubKeyCellOffset);
+         if (!NT_SUCCESS(Status))
            {
-DPRINT("\n");
-        RtlMoveMemory(tmpList, RegistryHive->FreeList,
-          sizeof(PCELL_HEADER) * (RegistryHive->FreeListMax));
-DPRINT("\n");
-        RtlMoveMemory(tmpListOffset, RegistryHive->FreeListOffset,
-          sizeof(BLOCK_OFFSET *) * (RegistryHive->FreeListMax));
-DPRINT("\n");
-                   ExFreePool(RegistryHive->FreeList);
-DPRINT("\n");
-                   ExFreePool(RegistryHive->FreeListOffset);
-DPRINT("\n");
-      }
-DPRINT("\n");
-           RegistryHive->FreeList = tmpList;
-           RegistryHive->FreeListOffset = tmpListOffset;
-           RegistryHive->FreeListMax += 32;
-DPRINT("\n");
-         }
-DPRINT("\n");
-
-         /* Add new offset to free list, maintaining list in ascending order */
-         if ((RegistryHive->FreeListSize == 0)
-            || (RegistryHive->FreeListOffset[RegistryHive->FreeListSize-1] < FreeOffset))
-                 {
-DPRINT("\n");
-                   /* Add to end of list */
-                   RegistryHive->FreeList[RegistryHive->FreeListSize] = FreeBlock;
-                   RegistryHive->FreeListOffset[RegistryHive->FreeListSize++] = FreeOffset;
-                 }
-         else if (RegistryHive->FreeListOffset[0] > FreeOffset)
-                 {
-DPRINT("\n");
-                   /* Add to begin of list */
-                   RtlMoveMemory(&RegistryHive->FreeList[1],
-          &RegistryHive->FreeList[0],
-                           sizeof(RegistryHive->FreeList[0]) * RegistryHive->FreeListSize);
-                   RtlMoveMemory(&RegistryHive->FreeListOffset[1],
-          &RegistryHive->FreeListOffset[0],
-                           sizeof(RegistryHive->FreeListOffset[0]) * RegistryHive->FreeListSize);
-                   RegistryHive->FreeList[0] = FreeBlock;
-                   RegistryHive->FreeListOffset[0] = FreeOffset;
-                   RegistryHive->FreeListSize++;
-                 }
-         else
-         {
-DPRINT("\n");
-           /* Search where to insert */
-           minInd = 0;
-           maxInd = RegistryHive->FreeListSize - 1;
-           while ((maxInd - minInd) > 1)
-                   {
-                     medInd = (minInd + maxInd) / 2;
-                     if (RegistryHive->FreeListOffset[medInd] > FreeOffset)
-                       maxInd = medInd;
-                     else
-                       minInd = medInd;
-                   }
-
-           /* Insert before maxInd */
-           RtlMoveMemory(&RegistryHive->FreeList[maxInd+1],
-        &RegistryHive->FreeList[maxInd],
-                   sizeof(RegistryHive->FreeList[0]) * (RegistryHive->FreeListSize - minInd));
-           RtlMoveMemory(&RegistryHive->FreeListOffset[maxInd + 1],
-                   &RegistryHive->FreeListOffset[maxInd],
-                   sizeof(RegistryHive->FreeListOffset[0]) * (RegistryHive->FreeListSize-minInd));
-           RegistryHive->FreeList[maxInd] = FreeBlock;
-           RegistryHive->FreeListOffset[maxInd] = FreeOffset;
-           RegistryHive->FreeListSize++;
-         }
-DPRINT("\n");
+             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;
+           }
 
-  return STATUS_SUCCESS;
-}
+         NewHashTableCell->Table[i].KeyOffset = NewSubKeyCellOffset;
+         NewHashTableCell->Table[i].HashValue = SrcHashRecord->HashValue;
 
+         RtlCopyMemory (NewSubKeyCell,
+                        SrcSubKeyCell,
+                        NewSubKeyCellSize);
 
-PVOID
-CmiGetBlock(PREGISTRY_HIVE RegistryHive,
-       BLOCK_OFFSET BlockOffset,
-       PHBIN * ppBin)
-{
-  if ((BlockOffset == 0) || (BlockOffset == -1))
-    return NULL;
+         /* Copy class name */
+         if (SrcSubKeyCell->ClassNameOffset != (BLOCK_OFFSET) -1)
+           {
+             PDATA_CELL SrcClassNameCell;
+             PDATA_CELL NewClassNameCell;
+             BLOCK_OFFSET NewClassNameOffset;
+
+             SrcClassNameCell = CmiGetCell (SrcHive,
+                                            SrcSubKeyCell->ClassNameOffset,
+                                            NULL),
+
+             NewSubKeyCell->ClassSize = SrcSubKeyCell->ClassSize;
+             Status = CmiAllocateCell (DstHive,
+                                       sizeof(CELL_HEADER) + NewSubKeyCell->ClassSize,
+                                       (PVOID)&NewClassNameCell,
+                                       &NewClassNameOffset);
+             if (!NT_SUCCESS(Status))
+               {
+                 DPRINT1 ("CmiAllocateBlock() failed (Status %lx)\n", Status);
+                 return Status;
+               }
 
-  if (IsVolatileHive(RegistryHive))
-    {
-      return (PVOID) BlockOffset;
-    }
-  else
-    {
-      PHBIN pBin;
+             NewSubKeyCell->ClassNameOffset = NewClassNameOffset;
+             RtlCopyMemory (NewClassNameCell,
+                            SrcClassNameCell,
+                            NewSubKeyCell->ClassSize);
+           }
 
-      pBin = RegistryHive->BlockList[BlockOffset / 4096];
-      if (ppBin)
-       *ppBin = pBin;
-      return ((PVOID) ((ULONG_PTR) pBin + (BlockOffset - pBin->BlockOffset)));
+         /* 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;
 }
 
 
-VOID
-CmiPrepareForWrite(PREGISTRY_HIVE RegistryHive,
-  PHBIN pBin)
+NTSTATUS
+CmiSaveTempHive (PREGISTRY_HIVE Hive,
+                HANDLE FileHandle)
 {
-  if (IsVolatileHive(RegistryHive))
+  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 = (ULONGLONG)0;
+  Status = ZwWriteFile (FileHandle,
+                       NULL,
+                       NULL,
+                       NULL,
+                       &IoStatusBlock,
+                       Hive->HiveHeader,
+                       sizeof(HIVE_HEADER),
+                       &FileOffset,
+                       NULL);
+  if (!NT_SUCCESS(Status))
     {
-      /* No need to do anything special for volatile hives */
-      return;
+      DPRINT1 ("ZwWriteFile() failed (Status %lx)\n", Status);
+      return Status;
     }
-  else
+
+  DPRINT ("Saving %lu blocks\n", Hive->BlockListSize);
+  for (BlockIndex = 0; BlockIndex < Hive->BlockListSize; BlockIndex++)
     {
-      
+      BlockPtr = Hive->BlockList[BlockIndex].Block;
+      DPRINT ("BlockPtr %p\n", BlockPtr);
+
+      FileOffset.QuadPart = (ULONGLONG)(BlockIndex + 1) * (ULONGLONG)REG_BLOCK_SIZE;
+      DPRINT ("File offset %I64x\n", FileOffset.QuadPart);
+
+      /* Write hive block */
+      Status = ZwWriteFile (FileHandle,
+                           NULL,
+                           NULL,
+                           NULL,
+                           &IoStatusBlock,
+                           BlockPtr,
+                           REG_BLOCK_SIZE,
+                           &FileOffset,
+                           NULL);
+      if (!NT_SUCCESS(Status))
+       {
+         DPRINT1 ("ZwWriteFile() failed (Status %lx)\n", Status);
+         return Status;
+       }
     }
-}
-
 
-VOID
-CmiLockBlock(PREGISTRY_HIVE RegistryHive,
-  PVOID Block)
-{
-  if (IsPermanentHive(RegistryHive))
+  Status = ZwFlushBuffersFile (FileHandle,
+                              &IoStatusBlock);
+  if (!NT_SUCCESS(Status))
     {
-      /* FIXME: Implement */
+      DPRINT1 ("ZwFlushBuffersFile() failed (Status %lx)\n", Status);
     }
-}
 
+  DPRINT ("CmiSaveTempHive() done\n");
 
-VOID
-CmiReleaseBlock(PREGISTRY_HIVE RegistryHive,
-  PVOID Block)
-{
-  if (IsPermanentHive(RegistryHive))
-    {
-      /* FIXME: Implement */
-    }
+  return Status;
 }
+
+/* EOF */