update for HEAD-2003050101
[reactos.git] / ntoskrnl / cm / regfile.c
index ea67394..a03290a 100644 (file)
@@ -6,9 +6,6 @@
  * UPDATE HISTORY:
 */
 
-#ifdef WIN32_REGDBG
-#include "cm_win32.h"
-#else
 #include <ddk/ntddk.h>
 #include <ddk/ntifs.h>
 #include <roscfg.h>
 #include <string.h>
 #include <internal/pool.h>
 #include <internal/registry.h>
+#include <reactos/bugcodes.h>
 
 #define NDEBUG
 #include <internal/debug.h>
 
 #include "cm.h"
-#endif
+
+
+/* uncomment to enable hive checks (incomplete and probably buggy) */
+// #define HIVE_CHECK
+
+/* LOCAL MACROS *************************************************************/
 
 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
+#define ROUND_DOWN(N, S) ((N) - ((N) % (S)))
+
+#define ABS_VALUE(V) (((V) < 0) ? -(V) : (V))
 
 BOOLEAN CmiDoVerify = FALSE;
 
+static ULONG
+CmiCalcChecksum(PULONG Buffer);
+
 /* FUNCTIONS ****************************************************************/
 
 VOID
@@ -36,9 +45,10 @@ CmiCreateDefaultHiveHeader(PHIVE_HEADER Header)
   assert(Header);
   RtlZeroMemory(Header, sizeof(HIVE_HEADER));
   Header->BlockId = REG_HIVE_ID;
+  Header->UpdateCounter1 = 0;
+  Header->UpdateCounter2 = 0;
   Header->DateModified.dwLowDateTime = 0;
   Header->DateModified.dwHighDateTime = 0;
-  Header->Version = 1;
   Header->Unused3 = 1;
   Header->Unused4 = 3;
   Header->Unused5 = 0;
@@ -68,14 +78,10 @@ CmiCreateDefaultRootKeyCell(PKEY_CELL RootKeyCell)
 {
   assert(RootKeyCell);
   RtlZeroMemory(RootKeyCell, sizeof(KEY_CELL));
-#ifdef WIN32_REGDBG
-  RootKeyCell->CellSize = -(LONG)sizeof(KEY_CELL);
-#else
   RootKeyCell->CellSize = -sizeof(KEY_CELL);
-#endif
   RootKeyCell->Id = REG_KEY_CELL_ID;
   RootKeyCell->Type = REG_ROOT_KEY_CELL_TYPE;
-  ZwQuerySystemTime((PTIME) &RootKeyCell->LastWriteTime);
+  NtQuerySystemTime((PTIME) &RootKeyCell->LastWriteTime);
   RootKeyCell->ParentKeyOffset = 0;
   RootKeyCell->NumberOfSubKeys = 0;
   RootKeyCell->HashTableOffset = -1;
@@ -342,7 +348,7 @@ CmiVerifyHiveHeader(PHIVE_HEADER Header)
       assert(Header->Unused7 == 1);
     }
 
-    }  
+    }
 }
 
 
@@ -358,7 +364,8 @@ CmiVerifyRegistryHive(PREGISTRY_HIVE RegistryHive)
 }
 
 
-NTSTATUS
+#if 0
+static NTSTATUS
 CmiPopulateHive(HANDLE FileHandle)
 {
   IO_STATUS_BLOCK IoStatusBlock;
@@ -383,15 +390,15 @@ CmiPopulateHive(HANDLE FileHandle)
 
   // Add free blocks so we don't need to expand
   // the file for a while
-  for (i = 0; i < 50; i++)
+  for (i = 1; i < 50; i++)
     {
       // Block offset of this bin
-      BinCell->BlockOffset = (2 + i) * REG_BLOCK_SIZE;
+      BinCell->BlockOffset = i * REG_BLOCK_SIZE;
 
       FileOffset.u.HighPart = 0;
-      FileOffset.u.LowPart   = (2 + i) * REG_BLOCK_SIZE;
+      FileOffset.u.LowPart  = (i + 1) * REG_BLOCK_SIZE;
 
-      Status = ZwWriteFile(FileHandle,
+      Status = NtWriteFile(FileHandle,
                           NULL,
                           NULL,
                           NULL,
@@ -412,9 +419,10 @@ CmiPopulateHive(HANDLE FileHandle)
 
   return Status;
 }
+#endif
 
 
-NTSTATUS
+static NTSTATUS
 CmiCreateNewRegFile(HANDLE FileHandle)
 {
   IO_STATUS_BLOCK IoStatusBlock;
@@ -447,7 +455,7 @@ CmiCreateNewRegFile(HANDLE FileHandle)
   /* The rest of the block is free */
   FreeCell->CellSize = REG_BLOCK_SIZE - (REG_HBIN_DATA_OFFSET + sizeof(KEY_CELL));
 
-  Status = ZwWriteFile(FileHandle,
+  Status = NtWriteFile(FileHandle,
                       NULL,
                       NULL,
                       NULL,
@@ -461,21 +469,284 @@ CmiCreateNewRegFile(HANDLE FileHandle)
 
   assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
 
-#if 1
+  if (!NT_SUCCESS(Status))
+    {
+      return(Status);
+    }
+
+#if 0
   if (NT_SUCCESS(Status))
     {
       CmiPopulateHive(FileHandle);
     }
 #endif
 
-  return Status;
+  Status = NtFlushBuffersFile(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;
+  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;
+  ULONG FileSize;
+  ULONG BufferSize;
+  ULONG BitmapSize;
+  RTL_BITMAP BlockBitMap;
+  NTSTATUS Status;
+
+  DPRINT("CmiCheckAndFixHive() called\n");
+
+  /* Try to open the hive file */
+  InitializeObjectAttributes(&ObjectAttributes,
+                            &RegistryHive->HiveFileName,
+                            0,
+                            NULL,
+                            NULL);
+
+  Status = NtCreateFile(&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)
+    {
+      return(STATUS_SUCCESS);
+    }
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("NtCreateFile() failed (Status %lx)\n", Status);
+      return(Status);
+    }
+
+  /* Try to open the log file */
+  InitializeObjectAttributes(&ObjectAttributes,
+                            &RegistryHive->LogFileName,
+                            0,
+                            NULL,
+                            NULL);
+
+  Status = NtCreateFile(&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("NtCreateFile() failed (Status %lx)\n", Status);
+      NtClose(HiveHandle);
+      return(Status);
+    }
+
+  /* Allocate hive header */
+  HiveHeader = ExAllocatePool(PagedPool,
+                             sizeof(HIVE_HEADER));
+  if (HiveHeader == NULL)
+    {
+      DPRINT("ExAllocatePool() failed\n");
+      Status = STATUS_INSUFFICIENT_RESOURCES;
+      goto ByeBye;
+    }
+
+  /* Read hive base block */
+  FileOffset.QuadPart = 0ULL;
+  Status = NtReadFile(HiveHandle,
+                     0,
+                     0,
+                     0,
+                     &IoStatusBlock,
+                     HiveHeader,
+                     sizeof(HIVE_HEADER),
+                     &FileOffset,
+                     0);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("NtReadFile() failed (Status %lx)\n", Status);
+      goto ByeBye;
+    }
+
+  if (LogHandle == INVALID_HANDLE_VALUE)
+    {
+      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;
+    }
+  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 = NtReadFile(LogHandle,
+                         0,
+                         0,
+                         0,
+                         &IoStatusBlock,
+                         LogHeader,
+                         sizeof(HIVE_HEADER),
+                         &FileOffset,
+                         0);
+      if (!NT_SUCCESS(Status))
+       {
+         DPRINT("NtReadFile() failed (Status %lx)\n", Status);
+         goto ByeBye;
+       }
+
+      /* 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);
+           }
+
+         /* Log file damaged but hive is okay */
+         Status = STATUS_SUCCESS;
+         goto ByeBye;
+       }
+
+      if (HiveHeader->UpdateCounter1 == HiveHeader->UpdateCounter2 &&
+         HiveHeader->UpdateCounter1 == LogHeader->UpdateCounter1)
+       {
+         /* Hive and log file are up-to-date */
+         Status = STATUS_SUCCESS;
+         goto ByeBye;
+       }
+
+      /*
+       * Hive needs an update!
+       */
+
+      /* Get file size */
+      Status = NtQueryInformationFile(LogHandle,
+                                     &IoStatusBlock,
+                                     &fsi,
+                                     sizeof(fsi),
+                                     FileStandardInformation);
+      if (!NT_SUCCESS(Status))
+       {
+         DPRINT("NtQueryInformationFile() failed (Status %lx)\n", Status);
+         goto ByeBye;
+       }
+      FileSize = fsi.EndOfFile.u.LowPart;
+
+      /* Calculate bitmap and block size */
+      BitmapSize = ROUND_UP((FileSize / 4096) - 1, sizeof(ULONG) * 8) / 8;
+      BufferSize = sizeof(HIVE_HEADER) +
+                         sizeof(ULONG) +
+                         BitmapSize;
+      BufferSize = ROUND_UP(BufferSize, 4096);
+
+      /* Reallocate log header block */
+      ExFreePool(LogHeader);
+      LogHeader = ExAllocatePool(PagedPool,
+                                BufferSize);
+      if (LogHeader == NULL)
+       {
+         DPRINT("ExAllocatePool() failed\n");
+         Status = STATUS_INSUFFICIENT_RESOURCES;
+         goto ByeBye;
+       }
+
+      /* Read log file header */
+      FileOffset.QuadPart = 0ULL;
+      Status = NtReadFile(LogHandle,
+                         0,
+                         0,
+                         0,
+                         &IoStatusBlock,
+                         LogHeader,
+                         BufferSize,
+                         &FileOffset,
+                         0);
+      if (!NT_SUCCESS(Status))
+       {
+         DPRINT("NtReadFile() failed (Status %lx)\n", Status);
+         goto ByeBye;
+       }
+
+      /* Initialize bitmap */
+      RtlInitializeBitMap(&BlockBitMap,
+                         (PVOID)((ULONG)LogHeader + 4096 + sizeof(ULONG)),
+                         BitmapSize * 8);
+
+      /* FIXME: Update dirty blocks */
+
+
+      /* FIXME: Update hive header */
+
+
+      Status = STATUS_SUCCESS;
+    }
+
+
+  /* Clean up the mess */
+ByeBye:
+  if (HiveHeader != NULL)
+    ExFreePool(HiveHeader);
+
+  if (LogHeader != NULL)
+    ExFreePool(LogHeader);
+
+  if (LogHandle != INVALID_HANDLE_VALUE)
+    NtClose(LogHandle);
+
+  NtClose(HiveHandle);
+
+  return(Status);
+}
+#endif
+
+
+static NTSTATUS
+CmiInitNonVolatileRegistryHive(PREGISTRY_HIVE RegistryHive,
+                              PWSTR Filename,
+                              BOOLEAN CreateNew)
 {
   OBJECT_ATTRIBUTES ObjectAttributes;
   FILE_STANDARD_INFORMATION fsi;
@@ -490,20 +761,49 @@ CmiInitPermanentRegistryHive(PREGISTRY_HIVE RegistryHive,
   PHBIN tmpBin;
   ULONG i, j;
   ULONG BitmapSize;
-  PULONG BitmapBuffer;
 
-  DPRINT1("CmiInitPermanentRegistryHive(%p, %S, %d) - Entered.\n", RegistryHive, Filename, CreateNew);
+  DPRINT("CmiInitNonVolatileRegistryHive(%p, %S, %d) called\n",
+        RegistryHive, Filename, CreateNew);
 
   /* Duplicate Filename */
-  Status = RtlCreateUnicodeString(&RegistryHive->Filename, Filename);
+  Status = RtlCreateUnicodeString(&RegistryHive->HiveFileName,
+                                 Filename);
   if (!NT_SUCCESS(Status))
     {
-      DPRINT1("CmiInitPermanentRegistryHive() - Failed 1.\n");
-      return Status;
+      DPRINT("RtlCreateUnicodeString() failed (Status %lx)\n", Status);
+      return(Status);
+    }
+
+  /* Create log file name */
+  RegistryHive->LogFileName.Length = (wcslen(Filename) + 4) * sizeof(WCHAR);
+  RegistryHive->LogFileName.MaximumLength = RegistryHive->LogFileName.Length + sizeof(WCHAR);
+  RegistryHive->LogFileName.Buffer = ExAllocatePool(NonPagedPool,
+                                                   RegistryHive->LogFileName.MaximumLength);
+  if (RegistryHive->LogFileName.Buffer == NULL)
+    {
+      RtlFreeUnicodeString(&RegistryHive->HiveFileName);
+      DPRINT("ExAllocatePool() failed\n");
+      return(STATUS_INSUFFICIENT_RESOURCES);
+    }
+  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))
+    {
+      RtlFreeUnicodeString(&RegistryHive->HiveFileName);
+      RtlFreeUnicodeString(&RegistryHive->LogFileName);
+      DPRINT1("CmiCheckAndFixHive() failed (Status %lx)\n", Status);
+      return(Status);
     }
+#endif
 
   InitializeObjectAttributes(&ObjectAttributes,
-                            &RegistryHive->Filename,
+                            &RegistryHive->HiveFileName,
                             0,
                             NULL,
                             NULL);
@@ -536,8 +836,9 @@ CmiInitPermanentRegistryHive(PREGISTRY_HIVE RegistryHive,
                        0);
   if (!NT_SUCCESS(Status))
     {
-      RtlFreeUnicodeString(&RegistryHive->Filename);
-      DPRINT1("NtCreateFile() failed (Status %lx)\n", Status);
+      RtlFreeUnicodeString(&RegistryHive->HiveFileName);
+      RtlFreeUnicodeString(&RegistryHive->LogFileName);
+      DPRINT("NtCreateFile() failed (Status %lx)\n", Status);
       return(Status);
     }
 
@@ -550,29 +851,14 @@ CmiInitPermanentRegistryHive(PREGISTRY_HIVE RegistryHive,
       Status = CmiCreateNewRegFile(FileHandle);
       if (!NT_SUCCESS(Status))
        {
-         DPRINT1("CmiCreateNewRegFile() failed (Status %lx)\n", Status);
-         RtlFreeUnicodeString(&RegistryHive->Filename);
+         DPRINT("CmiCreateNewRegFile() failed (Status %lx)\n", Status);
+         NtClose(FileHandle);
+         RtlFreeUnicodeString(&RegistryHive->HiveFileName);
+         RtlFreeUnicodeString(&RegistryHive->LogFileName);
          return(Status);
        }
     }
 
-  Status = ObReferenceObjectByHandle(FileHandle,
-                                    FILE_ALL_ACCESS,
-                                    IoFileObjectType,
-                                    UserMode,
-                                    (PVOID*)&RegistryHive->FileObject,
-                                    NULL);
-
-  assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
-
-  if (!NT_SUCCESS(Status))
-    {
-      NtClose(FileHandle);
-      RtlFreeUnicodeString(&RegistryHive->Filename);
-      DPRINT1("CmiInitPermanentRegistryHive() - ObReferenceObjectByHandle Failed with status %x.\n", Status);
-      return Status;
-    }
-
   /* Read hive header */
   FileOffset.u.HighPart = 0;
   FileOffset.u.LowPart = 0;
@@ -589,50 +875,33 @@ CmiInitPermanentRegistryHive(PREGISTRY_HIVE RegistryHive,
   assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
   if (!NT_SUCCESS(Status))
     {
-      ObDereferenceObject(RegistryHive->FileObject);
-      RtlFreeUnicodeString(&RegistryHive->Filename);
-      DPRINT("CmiInitPermanentRegistryHive() - Failed 4.\n");
+      DPRINT("NtReadFile() failed (Status %lx)\n", Status);
+      NtClose(FileHandle);
+      RtlFreeUnicodeString(&RegistryHive->HiveFileName);
+      RtlFreeUnicodeString(&RegistryHive->LogFileName);
       return Status;
     }
 
+  /* Read update counter */
+  RegistryHive->UpdateCounter = RegistryHive->HiveHeader->UpdateCounter1;
+
   Status = NtQueryInformationFile(FileHandle,
                                  &IoSB,
                                  &fsi,
                                  sizeof(fsi),
                                  FileStandardInformation);
-
   assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
-
-  if (!NT_SUCCESS(Status))
+  if (!NT_SUCCESS(Status) || fsi.EndOfFile.u.LowPart == 0)
     {
-      ObDereferenceObject(RegistryHive->FileObject);
-      RtlFreeUnicodeString(&RegistryHive->Filename);
-      DPRINT("CmiInitPermanentRegistryHive() - Failed 5.\n");
+      DPRINT("NtQueryInformationFile() failed (Status %lx)\n", Status);
+      NtClose(FileHandle);
+      RtlFreeUnicodeString(&RegistryHive->HiveFileName);
+      RtlFreeUnicodeString(&RegistryHive->LogFileName);
       return Status;
     }
 
-#if 0
-  /* We have a reference to the file object so we don't need the handle anymore */
-  ZwClose(FileHandle);
-#endif
-
   RegistryHive->FileSize = fsi.EndOfFile.u.LowPart;
-#ifdef WIN32_REGDBG
-//  assert(RegistryHive->FileSize);
-  if (RegistryHive->FileSize)
-  {
-    RegistryHive->BlockListSize = (RegistryHive->FileSize / 4096) - 1;
-  }
-  else
-  {
-    ObDereferenceObject(RegistryHive->FileObject);
-    RtlFreeUnicodeString(&RegistryHive->Filename);
-    DPRINT("CmiInitPermanentRegistryHive() - Failed, zero length hive file.\n");
-    return STATUS_INSUFFICIENT_RESOURCES;
-  }
-#else
   RegistryHive->BlockListSize = (RegistryHive->FileSize / 4096) - 1;
-#endif
 
   DPRINT("Space needed for block list describing hive: 0x%x\n",
     sizeof(PHBIN *) * RegistryHive->BlockListSize);
@@ -643,55 +912,30 @@ CmiInitPermanentRegistryHive(PREGISTRY_HIVE RegistryHive,
   if (RegistryHive->BlockList == NULL)
     {
       ExFreePool(RegistryHive->BlockList);
-      ObDereferenceObject(RegistryHive->FileObject);
-      RtlFreeUnicodeString(&RegistryHive->Filename);
-      DPRINT("CmiInitPermanentRegistryHive() - Failed 6.\n");
+      NtClose(FileHandle);
+      RtlFreeUnicodeString(&RegistryHive->HiveFileName);
+      RtlFreeUnicodeString(&RegistryHive->LogFileName);
+      DPRINT("CmiInitNonVolatileRegistryHive() - Failed 6.\n");
       return STATUS_INSUFFICIENT_RESOURCES;
     }
 
-#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 */
-
-  assertmsg(Success, ("Success: %d\n", Success));
-
-  if (!Success)
-    {
-      ExFreePool(RegistryHive->BlockList);
-      ObDereferenceObject(RegistryHive->FileObject);
-      RtlFreeUnicodeString(&RegistryHive->Filename);
-      DPRINT("CmiInitPermanentRegistryHive() - Failed 7.\n");
-      return Status;
-    }
-
-#else
-
   RegistryHive->BlockList[0] = ExAllocatePool(PagedPool,
          RegistryHive->FileSize - 4096);
-#ifdef WIN32_REGDBG
-  RtlZeroMemory(RegistryHive->BlockList[0], RegistryHive->FileSize - 4096);
-#endif
-
   if (RegistryHive->BlockList[0] == NULL)
     {
       ExFreePool(RegistryHive->BlockList);
-      ObDereferenceObject(RegistryHive->FileObject);
-      RtlFreeUnicodeString(&RegistryHive->Filename);
-      DPRINT("CmiInitPermanentRegistryHive() - Failed 8.\n");
+      NtClose(FileHandle);
+      RtlFreeUnicodeString(&RegistryHive->HiveFileName);
+      RtlFreeUnicodeString(&RegistryHive->LogFileName);
+      DPRINT("CmiInitNonVolatileRegistryHive() - Failed 8.\n");
       return STATUS_INSUFFICIENT_RESOURCES;
     }
 
   FileOffset.u.HighPart = 0;
   FileOffset.u.LowPart = 4096;
 
-  DPRINT("    Attempting to ZwReadFile(%d) for %d bytes into %p\n", FileHandle, RegistryHive->FileSize - 4096, (PVOID)RegistryHive->BlockList[0]);
+  DPRINT("    Attempting to NtReadFile(%d) for %d bytes into %p\n",
+        FileHandle, RegistryHive->FileSize - 4096, (PVOID)RegistryHive->BlockList[0]);
   Status = NtReadFile(FileHandle,
                      0,
                      0,
@@ -705,7 +949,6 @@ CmiInitPermanentRegistryHive(PREGISTRY_HIVE RegistryHive,
   assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
 
   NtClose(FileHandle);
-#endif
 
   RegistryHive->FreeListSize = 0;
   RegistryHive->FreeListMax = 0;
@@ -743,7 +986,8 @@ CmiInitPermanentRegistryHive(PREGISTRY_HIVE RegistryHive,
            {
              Status = CmiAddFree(RegistryHive,
                                  FreeBlock,
-                                 RegistryHive->BlockList[i]->BlockOffset + FreeOffset);
+                                 RegistryHive->BlockList[i]->BlockOffset + FreeOffset,
+                                 FALSE);
 
              if (!NT_SUCCESS(Status))
                {
@@ -761,35 +1005,41 @@ CmiInitPermanentRegistryHive(PREGISTRY_HIVE RegistryHive,
       BlockOffset += tmpBin->BlockSize;
     }
 
-  /* Create block bitmap and clear all bits */
-
+  /*
+   * Create block bitmap and clear all bits
+   */
   /* Calculate bitmap size in bytes (always a multiple of 32 bits) */
   BitmapSize = ROUND_UP(RegistryHive->BlockListSize, sizeof(ULONG) * 8) / 8;
-  DPRINT1("RegistryHive->BlockListSize: %lu\n", RegistryHive->BlockListSize);
-  DPRINT1("BitmapSize:  %lu Bytes  %lu Bits\n", BitmapSize, BitmapSize * 8);
-  BitmapBuffer = (PULONG)ExAllocatePool(PagedPool,
-                                       BitmapSize);
+  DPRINT("RegistryHive->BlockListSize: %lu\n", RegistryHive->BlockListSize);
+  DPRINT("BitmapSize:  %lu Bytes  %lu Bits\n", BitmapSize, BitmapSize * 8);
+
+  /* Allocate bitmap */
+  RegistryHive->BitmapBuffer = (PULONG)ExAllocatePool(PagedPool,
+                                                     BitmapSize);
   RtlInitializeBitMap(&RegistryHive->DirtyBitMap,
-                     BitmapBuffer,
+                     RegistryHive->BitmapBuffer,
                      BitmapSize * 8);
+
+  /* Initialize bitmap */
   RtlClearAllBits(&RegistryHive->DirtyBitMap);
   RegistryHive->HiveDirty = FALSE;
 
-  DPRINT1("CmiInitPermanentRegistryHive(%p, %S, %d) - Finished.\n", RegistryHive, Filename, CreateNew);
+  DPRINT("CmiInitNonVolatileRegistryHive(%p, %S, %d) - Finished.\n",
+        RegistryHive, Filename, CreateNew);
 
   return(STATUS_SUCCESS);
 }
 
 
-NTSTATUS
+static NTSTATUS
 CmiInitVolatileRegistryHive(PREGISTRY_HIVE RegistryHive)
 {
   PKEY_CELL RootKeyCell;
 
-  RegistryHive->Flags |= HIVE_VOLATILE;
+  RegistryHive->Flags |= (HIVE_VOLATILE | HIVE_POINTER);
 
   CmiCreateDefaultHiveHeader(RegistryHive->HiveHeader);
-  
+
   RootKeyCell = (PKEY_CELL) ExAllocatePool(NonPagedPool, sizeof(KEY_CELL));
 
   if (RootKeyCell == NULL)
@@ -805,8 +1055,8 @@ CmiInitVolatileRegistryHive(PREGISTRY_HIVE RegistryHive)
 
 NTSTATUS
 CmiCreateRegistryHive(PWSTR Filename,
-  PREGISTRY_HIVE *RegistryHive,
-  BOOLEAN CreateNew)
+                     PREGISTRY_HIVE *RegistryHive,
+                     BOOLEAN CreateNew)
 {
   PREGISTRY_HIVE Hive;
   NTSTATUS Status;
@@ -834,7 +1084,7 @@ CmiCreateRegistryHive(PWSTR Filename,
 
   if (Filename != NULL)
     {
-      Status = CmiInitPermanentRegistryHive(Hive, Filename, CreateNew);
+      Status = CmiInitNonVolatileRegistryHive(Hive, Filename, CreateNew);
     }
   else
     {
@@ -854,7 +1104,7 @@ CmiCreateRegistryHive(PWSTR Filename,
   ExAcquireResourceExclusiveLite(&CmiHiveListLock, TRUE);
 
   /* Add the new hive to the hive list */
-  InsertHeadList(&CmiHiveListHead, &Hive->HiveList);
+  InsertTailList(&CmiHiveListHead, &Hive->HiveList);
 
   /* Release hive list lock */
   ExReleaseResourceLite(&CmiHiveListLock);
@@ -895,34 +1145,56 @@ CmiRemoveRegistryHive(PREGISTRY_HIVE RegistryHive)
 }
 
 
-NTSTATUS
-CmiFlushRegistryHive(PREGISTRY_HIVE RegistryHive)
+static ULONG
+CmiCalcChecksum(PULONG Buffer)
 {
-  ULONG BlockIndex;
-  ULONG BlockOffset;
-  PVOID BlockPtr;
-  LARGE_INTEGER FileOffset;
+  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;
+  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, 4096);
 
+  DPRINT("Bitmap size %lu  buffer size: %lu\n", BitmapSize, BufferSize);
 
-  DPRINT("CmiFlushRegistryHive() called\n");
-
-  if (RegistryHive->HiveDirty == FALSE)
+  Buffer = (PUCHAR)ExAllocatePool(NonPagedPool, BufferSize);
+  if (Buffer == NULL)
     {
-      return(STATUS_SUCCESS);
+      DPRINT("ExAllocatePool() failed\n");
+      return(STATUS_INSUFFICIENT_RESOURCES);
     }
 
-  DPRINT1("Hive '%wZ' is dirty\n", &RegistryHive->Filename);
-
-
-  /* Open hive for writing */
+  /* Open log file for writing */
   InitializeObjectAttributes(&ObjectAttributes,
-                            &RegistryHive->Filename,
+                            &RegistryHive->LogFileName,
                             0,
                             NULL,
                             NULL);
@@ -934,41 +1206,76 @@ CmiFlushRegistryHive(PREGISTRY_HIVE RegistryHive)
                        NULL,
                        FILE_ATTRIBUTE_NORMAL,
                        0,
-                       FILE_OPEN,
+                       FILE_SUPERSEDE,
                        FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
                        NULL,
                        0);
   if (!NT_SUCCESS(Status))
     {
-      DPRINT1("NtCreateFile() failed (Status %lx)\n", Status);
+      DPRINT("NtCreateFile() 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 = 0ULL;
+  Status = NtWriteFile(FileHandle,
+                      NULL,
+                      NULL,
+                      NULL,
+                      &IoStatusBlock,
+                      Buffer,
+                      BufferSize,
+                      &FileOffset,
+                      NULL);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
+      NtClose(FileHandle);
+      ExFreePool(Buffer);
+      return(Status);
+    }
+  ExFreePool(Buffer);
+
+  /* Write dirty blocks */
+  FileOffset.QuadPart = (ULONGLONG)BufferSize;
   BlockIndex = 0;
   while (TRUE)
     {
-      BlockIndex = RtlFindSetBitsAndClear(&RegistryHive->DirtyBitMap,
-                                         1,
-                                         BlockIndex);
-      if (BlockIndex == (ULONG)-1)
+      BlockIndex = RtlFindSetBits(&RegistryHive->DirtyBitMap,
+                                 1,
+                                 BlockIndex);
+      if ((BlockIndex == (ULONG)-1) ||
+         (BlockIndex >= RegistryHive->BlockListSize))
        {
          DPRINT("No more set bits\n");
+         Status = STATUS_SUCCESS;
          break;
        }
 
-      DPRINT1("Block %lu is dirty\n", BlockIndex);
-
-      BlockOffset = RegistryHive->BlockList[BlockIndex]->BlockOffset;
-      DPRINT1("Block offset %lx\n", BlockOffset);
-
-      BlockPtr = RegistryHive->BlockList[BlockIndex] + ((BlockIndex * 4096) - BlockOffset);
-      DPRINT1("BlockPtr %p\n", BlockPtr);
-
-      FileOffset.QuadPart = (ULONGLONG)(BlockIndex + 1) * 4096ULL;
-      DPRINT1("File offset %I64x\n", FileOffset.QuadPart);
+      DPRINT("Block %lu is dirty\n", BlockIndex);
 
+      BlockPtr = RegistryHive->BlockList[BlockIndex];
+      DPRINT("BlockPtr %p\n", BlockPtr);
+      DPRINT("File offset %I64x\n", FileOffset.QuadPart);
 
       /* Write hive block */
       Status = NtWriteFile(FileHandle,
@@ -987,13 +1294,496 @@ CmiFlushRegistryHive(PREGISTRY_HIVE RegistryHive)
          return(Status);
        }
 
+      BlockIndex++;
+      FileOffset.QuadPart += 4096ULL;
+    }
 
+  /* Truncate log file */
+  EndOfFileInfo.EndOfFile.QuadPart = FileOffset.QuadPart;
+  Status = NtSetInformationFile(FileHandle,
+                               &IoStatusBlock,
+                               &EndOfFileInfo,
+                               sizeof(FILE_END_OF_FILE_INFORMATION),
+                               FileEndOfFileInformation);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("NtSetInformationFile() failed (Status %lx)\n", Status);
+      NtClose(FileHandle);
+      return(Status);
     }
 
-  NtClose(FileHandle);
+  FileAllocationInfo.AllocationSize.QuadPart = FileOffset.QuadPart;
+  Status = NtSetInformationFile(FileHandle,
+                               &IoStatusBlock,
+                               &FileAllocationInfo,
+                               sizeof(FILE_ALLOCATION_INFORMATION),
+                               FileAllocationInformation);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("NtSetInformationFile() failed (Status %lx)\n", Status);
+      NtClose(FileHandle);
+      return(Status);
+    }
 
+  /* Flush the log file */
+  Status = NtFlushBuffersFile(FileHandle,
+                             &IoStatusBlock);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("NtFlushBuffersFile() failed (Status %lx)\n", Status);
+    }
 
-  /* Clear dirty flag */
+  NtClose(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, 4096);
+
+  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,
+                            0,
+                            NULL,
+                            NULL);
+
+  Status = NtCreateFile(&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("NtCreateFile() 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 = 0ULL;
+  Status = NtWriteFile(FileHandle,
+                      NULL,
+                      NULL,
+                      NULL,
+                      &IoStatusBlock,
+                      Buffer,
+                      BufferSize,
+                      &FileOffset,
+                      NULL);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
+      NtClose(FileHandle);
+      ExFreePool(Buffer);
+      return(Status);
+    }
+
+  ExFreePool(Buffer);
+
+  /* Flush the log file */
+  Status = NtFlushBuffersFile(FileHandle,
+                             &IoStatusBlock);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("NtFlushBuffersFile() failed (Status %lx)\n", Status);
+    }
+
+  NtClose(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, 4096);
+
+  DPRINT("Bitmap size %lu  buffer size: %lu\n", BitmapSize, BufferSize);
+
+  /* Open log file for writing */
+  InitializeObjectAttributes(&ObjectAttributes,
+                            &RegistryHive->LogFileName,
+                            0,
+                            NULL,
+                            NULL);
+
+  Status = NtCreateFile(&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("NtCreateFile() failed (Status %lx)\n", Status);
+      return(Status);
+    }
+
+  /* Truncate log file */
+  EndOfFileInfo.EndOfFile.QuadPart = (ULONGLONG)BufferSize;
+  Status = NtSetInformationFile(FileHandle,
+                               &IoStatusBlock,
+                               &EndOfFileInfo,
+                               sizeof(FILE_END_OF_FILE_INFORMATION),
+                               FileEndOfFileInformation);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("NtSetInformationFile() failed (Status %lx)\n", Status);
+      NtClose(FileHandle);
+      return(Status);
+    }
+
+  FileAllocationInfo.AllocationSize.QuadPart = (ULONGLONG)BufferSize;
+  Status = NtSetInformationFile(FileHandle,
+                               &IoStatusBlock,
+                               &FileAllocationInfo,
+                               sizeof(FILE_ALLOCATION_INFORMATION),
+                               FileAllocationInformation);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("NtSetInformationFile() failed (Status %lx)\n", Status);
+      NtClose(FileHandle);
+      return(Status);
+    }
+
+  /* Flush the log file */
+  Status = NtFlushBuffersFile(FileHandle,
+                             &IoStatusBlock);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("NtFlushBuffersFile() failed (Status %lx)\n", Status);
+    }
+
+  NtClose(FileHandle);
+
+  return(Status);
+}
+
+
+static NTSTATUS
+CmiStartHiveUpdate(PREGISTRY_HIVE RegistryHive)
+{
+  OBJECT_ATTRIBUTES ObjectAttributes;
+  IO_STATUS_BLOCK IoStatusBlock;
+  HANDLE FileHandle;
+  LARGE_INTEGER FileOffset;
+  ULONG BlockIndex;
+  PVOID BlockPtr;
+  NTSTATUS Status;
+
+  DPRINT("CmiStartHiveUpdate() called\n");
+
+  /* Open hive for writing */
+  InitializeObjectAttributes(&ObjectAttributes,
+                            &RegistryHive->HiveFileName,
+                            0,
+                            NULL,
+                            NULL);
+
+  Status = NtCreateFile(&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("NtCreateFile() 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 = 0ULL;
+  Status = NtWriteFile(FileHandle,
+                      NULL,
+                      NULL,
+                      NULL,
+                      &IoStatusBlock,
+                      RegistryHive->HiveHeader,
+                      sizeof(HIVE_HEADER),
+                      &FileOffset,
+                      NULL);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
+      NtClose(FileHandle);
+      return(Status);
+    }
+
+  BlockIndex = 0;
+  while (TRUE)
+    {
+      BlockIndex = RtlFindSetBits(&RegistryHive->DirtyBitMap,
+                                 1,
+                                 BlockIndex);
+      if ((BlockIndex == (ULONG)-1) ||
+         (BlockIndex >= RegistryHive->BlockListSize))
+       {
+         DPRINT("No more set bits\n");
+         Status = STATUS_SUCCESS;
+         break;
+       }
+
+      DPRINT("Block %lu is dirty\n", BlockIndex);
+
+      BlockPtr = RegistryHive->BlockList[BlockIndex];
+      DPRINT("BlockPtr %p\n", BlockPtr);
+
+      FileOffset.QuadPart = (ULONGLONG)(BlockIndex + 1) * 4096ULL;
+      DPRINT("File offset %I64x\n", FileOffset.QuadPart);
+
+      /* Write hive block */
+      Status = NtWriteFile(FileHandle,
+                          NULL,
+                          NULL,
+                          NULL,
+                          &IoStatusBlock,
+                          BlockPtr,
+                          REG_BLOCK_SIZE,
+                          &FileOffset,
+                          NULL);
+      if (!NT_SUCCESS(Status))
+       {
+         DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
+         NtClose(FileHandle);
+         return(Status);
+       }
+
+      BlockIndex++;
+    }
+
+  Status = NtFlushBuffersFile(FileHandle,
+                             &IoStatusBlock);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("NtFlushBuffersFile() failed (Status %lx)\n", Status);
+    }
+
+  NtClose(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,
+                            0,
+                            NULL,
+                            NULL);
+
+  Status = NtCreateFile(&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("NtCreateFile() 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 = 0ULL;
+  Status = NtWriteFile(FileHandle,
+                      NULL,
+                      NULL,
+                      NULL,
+                      &IoStatusBlock,
+                      RegistryHive->HiveHeader,
+                      sizeof(HIVE_HEADER),
+                      &FileOffset,
+                      NULL);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
+      NtClose(FileHandle);
+      return(Status);
+    }
+
+  Status = NtFlushBuffersFile(FileHandle,
+                             &IoStatusBlock);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("NtFlushBuffersFile() failed (Status %lx)\n", Status);
+    }
+
+  NtClose(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 */
+  NtQuerySystemTime((PTIME)&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");
@@ -1004,7 +1794,7 @@ CmiFlushRegistryHive(PREGISTRY_HIVE RegistryHive)
 
 ULONG
 CmiGetMaxNameLength(PREGISTRY_HIVE  RegistryHive,
-  PKEY_CELL  KeyCell)
+                   PKEY_CELL  KeyCell)
 {
   PHASH_TABLE_CELL HashBlock;
   PKEY_CELL CurSubKeyCell;
@@ -1017,33 +1807,37 @@ CmiGetMaxNameLength(PREGISTRY_HIVE  RegistryHive,
   HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
   if (HashBlock == NULL)
     {
+      DPRINT("CmiGetBlock() failed\n");
       return 0;
     }
 
   for (i = 0; i < HashBlock->HashTableSize; i++)
     {
-      if (HashBlock->Table[i].KeyOffset != 0)
-        {
-          CurSubKeyCell = CmiGetBlock(RegistryHive,
-            HashBlock->Table[i].KeyOffset,
-            NULL);
-          if (MaxName < CurSubKeyCell->NameSize)
-            {
-              MaxName = CurSubKeyCell->NameSize;
-            }
-          CmiReleaseBlock(RegistryHive, CurSubKeyCell);
-        }
+      if (HashBlock->Table[i].KeyOffset != 0)
+       {
+         CurSubKeyCell = CmiGetBlock(RegistryHive,
+                                     HashBlock->Table[i].KeyOffset,
+                                     NULL);
+         if (CurSubKeyCell == NULL)
+           {
+             DPRINT("CmiGetBlock() failed\n");
+             return 0;
+           }
+
+         if (MaxName < CurSubKeyCell->NameSize)
+           {
+             MaxName = CurSubKeyCell->NameSize;
+           }
+       }
     }
 
-  CmiReleaseBlock(RegistryHive, HashBlock);
-  
   return MaxName;
 }
 
 
 ULONG
 CmiGetMaxClassLength(PREGISTRY_HIVE  RegistryHive,
-  PKEY_CELL  KeyCell)
+                    PKEY_CELL  KeyCell)
 {
   PHASH_TABLE_CELL HashBlock;
   PKEY_CELL CurSubKeyCell;
@@ -1056,33 +1850,37 @@ CmiGetMaxClassLength(PREGISTRY_HIVE  RegistryHive,
   HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
   if (HashBlock == NULL)
     {
+      DPRINT("CmiGetBlock() failed\n");
       return 0;
     }
 
   for (i = 0; i < HashBlock->HashTableSize; i++)
     {
       if (HashBlock->Table[i].KeyOffset != 0)
-        {
-          CurSubKeyCell = CmiGetBlock(RegistryHive,
-            HashBlock->Table[i].KeyOffset,
-            NULL);
-          if (MaxClass < CurSubKeyCell->ClassSize)
-            {
-              MaxClass = CurSubKeyCell->ClassSize;
-            }
-          CmiReleaseBlock(RegistryHive, CurSubKeyCell);
-        }
-    }
+       {
+         CurSubKeyCell = CmiGetBlock(RegistryHive,
+                                     HashBlock->Table[i].KeyOffset,
+                                     NULL);
+         if (CurSubKeyCell == NULL)
+           {
+             DPRINT("CmiGetBlock() failed\n");
+             return 0;
+           }
 
-  CmiReleaseBlock(RegistryHive, HashBlock);
+         if (MaxClass < CurSubKeyCell->ClassSize)
+           {
+             MaxClass = CurSubKeyCell->ClassSize;
+           }
+       }
+    }
 
   return MaxClass;
 }
 
 
-ULONG  
+ULONG
 CmiGetMaxValueNameLength(PREGISTRY_HIVE RegistryHive,
-  PKEY_CELL KeyCell)
+                        PKEY_CELL KeyCell)
 {
   PVALUE_LIST_CELL ValueListCell;
   PVALUE_CELL CurValueCell;
@@ -1091,35 +1889,40 @@ CmiGetMaxValueNameLength(PREGISTRY_HIVE RegistryHive,
 
   VERIFY_KEY_CELL(KeyCell);
 
-  ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
   MaxValueName = 0;
+  ValueListCell = CmiGetBlock(RegistryHive,
+                             KeyCell->ValuesOffset,
+                             NULL);
   if (ValueListCell == NULL)
     {
+      DPRINT("CmiGetBlock() failed\n");
       return 0;
     }
 
   for (i = 0; i < KeyCell->NumberOfValues; i++)
     {
-      CurValueCell = CmiGetBlock(RegistryHive,
-        ValueListCell->Values[i],
-        NULL);
+      CurValueCell = CmiGetBlock (RegistryHive,
+                                 ValueListCell->Values[i],
+                                 NULL);
+      if (CurValueCell == NULL)
+       {
+         DPRINT("CmiGetBlock() failed\n");
+       }
+
       if (CurValueCell != NULL &&
           MaxValueName < CurValueCell->NameSize)
         {
           MaxValueName = CurValueCell->NameSize;
         }
-      CmiReleaseBlock(RegistryHive, CurValueCell);
     }
 
-  CmiReleaseBlock(RegistryHive, ValueListCell);
-  
   return MaxValueName;
 }
 
 
-ULONG  
+ULONG
 CmiGetMaxValueDataLength(PREGISTRY_HIVE RegistryHive,
-  PKEY_CELL KeyCell)
+                        PKEY_CELL KeyCell)
 {
   PVALUE_LIST_CELL ValueListCell;
   PVALUE_CELL CurValueCell;
@@ -1128,8 +1931,8 @@ CmiGetMaxValueDataLength(PREGISTRY_HIVE RegistryHive,
 
   VERIFY_KEY_CELL(KeyCell);
 
-  ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
   MaxValueData = 0;
+  ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
   if (ValueListCell == NULL)
     {
       return 0;
@@ -1144,27 +1947,24 @@ CmiGetMaxValueDataLength(PREGISTRY_HIVE RegistryHive,
         {
           MaxValueData = CurValueCell->DataSize & LONG_MAX;
         }
-      CmiReleaseBlock(RegistryHive, CurValueCell);
     }
 
-  CmiReleaseBlock(RegistryHive, ValueListCell);
-  
   return MaxValueData;
 }
 
 
 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)
+                IN PKEY_CELL KeyCell,
+                OUT PKEY_CELL *SubKeyCell,
+                OUT BLOCK_OFFSET *BlockOffset,
+                IN PCHAR KeyName,
+                IN ACCESS_MASK DesiredAccess,
+                IN ULONG Attributes)
 {
   PHASH_TABLE_CELL HashBlock;
   PKEY_CELL CurSubKeyCell;
-  WORD KeyLength;
+  USHORT KeyLength;
   ULONG i;
 
   VERIFY_KEY_CELL(KeyCell);
@@ -1173,27 +1973,40 @@ CmiScanForSubKey(IN PREGISTRY_HIVE RegistryHive,
 
   assert(RegistryHive);
 
-  KeyLength = strlen(KeyName);
+  *SubKeyCell = NULL;
+
+  /* The key does not have any subkeys */
+  if (KeyCell->HashTableOffset == (BLOCK_OFFSET)-1)
+    {
+      return STATUS_SUCCESS;
+    }
 
+  /* Get hash table */
   HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
-  *SubKeyCell = NULL;
   if (HashBlock == NULL)
     {
-      return STATUS_SUCCESS;
+      DPRINT("CmiGetBlock() failed\n");
+      return STATUS_UNSUCCESSFUL;
     }
 
-  for (i = 0; (i < KeyCell->NumberOfSubKeys)
-               && (i < HashBlock->HashTableSize); i++)
+  KeyLength = strlen(KeyName);
+  for (i = 0; (i < KeyCell->NumberOfSubKeys) && (i < HashBlock->HashTableSize); i++)
     {
       if (Attributes & OBJ_CASE_INSENSITIVE)
-        {
-          if ((HashBlock->Table[i].KeyOffset != 0) &&
-              (HashBlock->Table[i].KeyOffset != (ULONG_PTR)-1) &&
-              (_strnicmp(KeyName, (PCHAR) &HashBlock->Table[i].HashValue, 4) == 0))
-            {
-              CurSubKeyCell = CmiGetBlock(RegistryHive, 
-                HashBlock->Table[i].KeyOffset,
-                NULL);
+       {
+         if ((HashBlock->Table[i].KeyOffset != 0) &&
+             (HashBlock->Table[i].KeyOffset != (ULONG_PTR)-1) &&
+             (_strnicmp(KeyName, (PCHAR) &HashBlock->Table[i].HashValue, 4) == 0))
+           {
+             CurSubKeyCell = CmiGetBlock(RegistryHive,
+                                         HashBlock->Table[i].KeyOffset,
+                                         NULL);
+             if (CurSubKeyCell == NULL)
+               {
+                 DPRINT("CmiGetBlock() failed\n");
+                 return STATUS_UNSUCCESSFUL;
+               }
+
               if ((CurSubKeyCell->NameSize == KeyLength)
                   && (_strnicmp(KeyName, CurSubKeyCell->Name, KeyLength) == 0))
                 {
@@ -1201,20 +2014,23 @@ CmiScanForSubKey(IN PREGISTRY_HIVE RegistryHive,
                   *BlockOffset = HashBlock->Table[i].KeyOffset;
                   break;
                 }
-              else
-                {
-                  CmiReleaseBlock(RegistryHive, CurSubKeyCell);
-                }
             }
-        }
+       }
       else
-        {
-          if (HashBlock->Table[i].KeyOffset != 0 &&
-              HashBlock->Table[i].KeyOffset != (ULONG_PTR) -1 &&
-              !strncmp(KeyName, (PCHAR) &HashBlock->Table[i].HashValue, 4))
-            {
-              CurSubKeyCell = CmiGetBlock(RegistryHive,
-                HashBlock->Table[i].KeyOffset,NULL);
+       {
+         if (HashBlock->Table[i].KeyOffset != 0 &&
+             HashBlock->Table[i].KeyOffset != (ULONG_PTR) -1 &&
+             !strncmp(KeyName, (PCHAR) &HashBlock->Table[i].HashValue, 4))
+           {
+             CurSubKeyCell = CmiGetBlock(RegistryHive,
+                                         HashBlock->Table[i].KeyOffset,
+                                         NULL);
+             if (CurSubKeyCell == NULL)
+               {
+                 DPRINT("CmiGetBlock() failed\n");
+                 return STATUS_UNSUCCESSFUL;
+               }
+
               if (CurSubKeyCell->NameSize == KeyLength
                   && !_strnicmp(KeyName, CurSubKeyCell->Name, KeyLength))
                 {
@@ -1222,16 +2038,10 @@ CmiScanForSubKey(IN PREGISTRY_HIVE RegistryHive,
                   *BlockOffset = HashBlock->Table[i].KeyOffset;
                   break;
                 }
-              else
-                {
-                  CmiReleaseBlock(RegistryHive, CurSubKeyCell);
-                }
             }
-        }
+       }
     }
-  
-  CmiReleaseBlock(RegistryHive, HashBlock);
-  
+
   return STATUS_SUCCESS;
 }
 
@@ -1272,10 +2082,9 @@ CmiAddSubKey(PREGISTRY_HIVE RegistryHive,
 
   NewBlockSize = sizeof(KEY_CELL) + NameSize;
   Status = CmiAllocateBlock(RegistryHive,
-    (PVOID) &NewKeyCell,
-    NewBlockSize,
-    &NKBOffset);
-
+                           (PVOID) &NewKeyCell,
+                           NewBlockSize,
+                           &NKBOffset);
   if (NewKeyCell == NULL)
     {
       Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -1319,8 +2128,8 @@ CmiAddSubKey(PREGISTRY_HIVE RegistryHive,
   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)))
+  /* Don't modify hash table if key is located in a pointer-based hive and parent key is not */
+  if (IsPointerHive(RegistryHive) && (!IsPointerHive(Parent->RegistryHive)))
     {
       return(Status);
     }
@@ -1339,6 +2148,12 @@ CmiAddSubKey(PREGISTRY_HIVE RegistryHive,
   else
     {
       HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
+      if (HashBlock == NULL)
+       {
+         DPRINT("CmiGetBlock() failed\n");
+         return STATUS_UNSUCCESSFUL;
+       }
+
       if (((KeyCell->NumberOfSubKeys + 1) >= HashBlock->HashTableSize))
        {
          BLOCK_OFFSET HTOffset;
@@ -1386,17 +2201,87 @@ CmiRemoveSubKey(PREGISTRY_HIVE RegistryHive,
                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 = CmiGetBlock(RegistryHive,
+                             SubKey->KeyCell->ValuesOffset,
+                             NULL);
+      if (ValueList == NULL)
+       {
+         DPRINT("CmiGetBlock() failed\n");
+         return STATUS_UNSUCCESSFUL;
+       }
+
+      if (ValueList != NULL)
+       {
+         /* Enumerate all values */
+         for (i = 0; i < SubKey->KeyCell->NumberOfValues; i++)
+           {
+             /* Get pointer to value cell */
+             ValueCell = CmiGetBlock(RegistryHive,
+                                     ValueList->Values[i],
+                                     NULL);
+             if (ValueCell != NULL)
+               {
+                 if (ValueCell->DataSize > 4)
+                   {
+                     DataCell = CmiGetBlock(RegistryHive,
+                                            ValueCell->DataOffset,
+                                            NULL);
+                     if (DataCell == NULL)
+                       {
+                         DPRINT("CmiGetBlock() failed\n");
+                         return STATUS_UNSUCCESSFUL;
+                       }
+
+                     if (DataCell != NULL)
+                       {
+                         /* Destroy data cell */
+                         CmiDestroyBlock(RegistryHive,
+                                         DataCell,
+                                         ValueCell->DataOffset);
+                       }
+                   }
+
+                 /* Destroy value cell */
+                 CmiDestroyBlock(RegistryHive,
+                                 ValueCell,
+                                 ValueList->Values[i]);
+               }
+           }
+       }
+
+      /* Destroy value list cell */
+      CmiDestroyBlock(RegistryHive,
+                     ValueList,
+                     SubKey->KeyCell->ValuesOffset);
 
-  DPRINT1("CmiRemoveSubKey() called\n");
+      SubKey->KeyCell->NumberOfValues = 0;
+      SubKey->KeyCell->ValuesOffset = -1;
+    }
 
   /* Remove the key from the parent key's hash block */
-  if (ParentKey->KeyCell->HashTableOffset != -1)
+  if (ParentKey->KeyCell->HashTableOffset != (BLOCK_OFFSET) -1)
     {
-      DPRINT1("ParentKey HashTableOffset %lx\n", ParentKey->KeyCell->HashTableOffset)
+      DPRINT("ParentKey HashTableOffset %lx\n", ParentKey->KeyCell->HashTableOffset)
       HashBlock = CmiGetBlock(RegistryHive,
                              ParentKey->KeyCell->HashTableOffset,
                              NULL);
-      DPRINT1("ParentKey HashBlock %p\n", HashBlock)
+      if (HashBlock == NULL)
+       {
+         DPRINT("CmiGetBlock() failed\n");
+         return STATUS_UNSUCCESSFUL;
+       }
+      DPRINT("ParentKey HashBlock %p\n", HashBlock)
       if (HashBlock != NULL)
        {
          CmiRemoveKeyFromHashTable(RegistryHive,
@@ -1408,13 +2293,18 @@ CmiRemoveSubKey(PREGISTRY_HIVE RegistryHive,
     }
 
   /* Remove the key's hash block */
-  if (SubKey->KeyCell->HashTableOffset != -1)
+  if (SubKey->KeyCell->HashTableOffset != (BLOCK_OFFSET) -1)
     {
-      DPRINT1("SubKey HashTableOffset %lx\n", SubKey->KeyCell->HashTableOffset)
+      DPRINT("SubKey HashTableOffset %lx\n", SubKey->KeyCell->HashTableOffset)
       HashBlock = CmiGetBlock(RegistryHive,
                              SubKey->KeyCell->HashTableOffset,
                              NULL);
-      DPRINT1("SubKey HashBlock %p\n", HashBlock)
+      if (HashBlock == NULL)
+       {
+         DPRINT("CmiGetBlock() failed\n");
+         return STATUS_UNSUCCESSFUL;
+       }
+      DPRINT("SubKey HashBlock %p\n", HashBlock)
       if (HashBlock != NULL)
        {
          CmiDestroyBlock(RegistryHive,
@@ -1427,18 +2317,34 @@ CmiRemoveSubKey(PREGISTRY_HIVE RegistryHive,
   /* Decrement the number of the parent key's sub keys */
   if (ParentKey != NULL)
     {
-      DPRINT1("ParentKey %p\n", ParentKey)
+      DPRINT("ParentKey %p\n", ParentKey)
       ParentKey->KeyCell->NumberOfSubKeys--;
-      NtQuerySystemTime((PTIME)&ParentKey->KeyCell->LastWriteTime);
-      CmiMarkBlockDirty(RegistryHive,
-                       ParentKey->BlockOffset);
 
       /* Remove the parent key's hash table */
       if (ParentKey->KeyCell->NumberOfSubKeys == 0)
        {
-         DPRINT1("FIXME: Remove parent key hash table\n")
-
+         DPRINT("ParentKey HashTableOffset %lx\n", ParentKey->KeyCell->HashTableOffset)
+         HashBlock = CmiGetBlock(RegistryHive,
+                                 ParentKey->KeyCell->HashTableOffset,
+                                 NULL);
+         if (HashBlock == NULL)
+           {
+             DPRINT("CmiGetBlock() failed\n");
+             return STATUS_UNSUCCESSFUL;
+           }
+         DPRINT("ParentKey HashBlock %p\n", HashBlock)
+         if (HashBlock != NULL)
+           {
+             CmiDestroyBlock(RegistryHive,
+                             HashBlock,
+                             ParentKey->KeyCell->HashTableOffset);
+             ParentKey->KeyCell->HashTableOffset = -1;
+           }
        }
+
+      NtQuerySystemTime((PTIME)&ParentKey->KeyCell->LastWriteTime);
+      CmiMarkBlockDirty(RegistryHive,
+                       ParentKey->BlockOffset);
     }
 
   /* Destroy key cell */
@@ -1448,31 +2354,34 @@ CmiRemoveSubKey(PREGISTRY_HIVE RegistryHive,
   SubKey->BlockOffset = -1;
   SubKey->KeyCell = NULL;
 
-  /* FIXME: Merge free blocks within the Bin */
-
   return(STATUS_SUCCESS);
 }
 
 
 NTSTATUS
 CmiScanKeyForValue(IN PREGISTRY_HIVE RegistryHive,
-       IN PKEY_CELL KeyCell,
-       IN PUNICODE_STRING ValueName,
-       OUT PVALUE_CELL *ValueCell,
-       OUT BLOCK_OFFSET *VBOffset)
+                  IN PKEY_CELL KeyCell,
+                  IN PUNICODE_STRING ValueName,
+                  OUT PVALUE_CELL *ValueCell,
+                  OUT BLOCK_OFFSET *VBOffset)
 {
   PVALUE_LIST_CELL ValueListCell;
   PVALUE_CELL CurValueCell;
   ULONG i;
 
-  ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
-
   *ValueCell = NULL;
 
+  /* The key does not have any values */
+  if (KeyCell->ValuesOffset == (BLOCK_OFFSET)-1)
+    {
+      return STATUS_SUCCESS;
+    }
+
+  ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
   if (ValueListCell == NULL)
     {
       DPRINT("ValueListCell is NULL\n");
-      return STATUS_SUCCESS;
+      return STATUS_UNSUCCESSFUL;
     }
 
   VERIFY_VALUE_LIST_CELL(ValueListCell);
@@ -1482,64 +2391,72 @@ CmiScanKeyForValue(IN PREGISTRY_HIVE RegistryHive,
       CurValueCell = CmiGetBlock(RegistryHive,
                                 ValueListCell->Values[i],
                                 NULL);
+      if (CurValueCell == NULL)
+       {
+         DPRINT("CmiGetBlock() failed\n");
+         return STATUS_UNSUCCESSFUL;
+       }
 
       if ((CurValueCell != NULL) &&
          CmiComparePackedNames(ValueName,
                                CurValueCell->Name,
                                CurValueCell->NameSize,
                                CurValueCell->Flags & REG_VALUE_NAME_PACKED))
-        {
-          *ValueCell = CurValueCell;
-          if (VBOffset)
-            *VBOffset = ValueListCell->Values[i];
-          //DPRINT("Found value %s\n", ValueName);
-          break;
-        }
-      CmiReleaseBlock(RegistryHive, CurValueCell);
+       {
+         *ValueCell = CurValueCell;
+         if (VBOffset)
+           *VBOffset = ValueListCell->Values[i];
+         //DPRINT("Found value %s\n", ValueName);
+         break;
+       }
     }
 
-  CmiReleaseBlock(RegistryHive, ValueListCell);
-
   return STATUS_SUCCESS;
 }
 
 
 NTSTATUS
 CmiGetValueFromKeyByIndex(IN PREGISTRY_HIVE RegistryHive,
-       IN PKEY_CELL KeyCell,
-       IN ULONG Index,
-       OUT PVALUE_CELL *ValueCell)
+                         IN PKEY_CELL KeyCell,
+                         IN ULONG Index,
+                         OUT PVALUE_CELL *ValueCell)
 {
   PVALUE_LIST_CELL ValueListCell;
   PVALUE_CELL CurValueCell;
 
-  ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
-
   *ValueCell = NULL;
 
-  if (ValueListCell == NULL)
+  if (KeyCell->ValuesOffset == (BLOCK_OFFSET)-1)
     {
       return STATUS_NO_MORE_ENTRIES;
     }
 
-  VERIFY_VALUE_LIST_CELL(ValueListCell);
-
   if (Index >= KeyCell->NumberOfValues)
     {
       return STATUS_NO_MORE_ENTRIES;
     }
 
-  CurValueCell = CmiGetBlock(RegistryHive,
-    ValueListCell->Values[Index],
-    NULL);
 
-  if (CurValueCell != NULL)
+  ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
+  if (ValueListCell == NULL)
+    {
+      DPRINT("CmiGetBlock() failed\n");
+      return STATUS_UNSUCCESSFUL;
+    }
+
+  VERIFY_VALUE_LIST_CELL(ValueListCell);
+
+
+  CurValueCell = CmiGetBlock(RegistryHive,
+                            ValueListCell->Values[Index],
+                            NULL);
+  if (CurValueCell == NULL)
     {
-      *ValueCell = CurValueCell;
+      DPRINT("CmiGetBlock() failed\n");
+      return STATUS_UNSUCCESSFUL;
     }
 
-  CmiReleaseBlock(RegistryHive, CurValueCell);
-  CmiReleaseBlock(RegistryHive, ValueListCell);
+  *ValueCell = CurValueCell;
 
   return STATUS_SUCCESS;
 }
@@ -1547,10 +2464,10 @@ CmiGetValueFromKeyByIndex(IN PREGISTRY_HIVE RegistryHive,
 
 NTSTATUS
 CmiAddValueToKey(IN PREGISTRY_HIVE RegistryHive,
-       IN PKEY_CELL KeyCell,
-       IN PUNICODE_STRING ValueName,
-       OUT PVALUE_CELL *pValueCell,
-       OUT BLOCK_OFFSET *pVBOffset)
+                IN PKEY_CELL KeyCell,
+                IN PUNICODE_STRING ValueName,
+                OUT PVALUE_CELL *pValueCell,
+                OUT BLOCK_OFFSET *pVBOffset)
 {
   PVALUE_LIST_CELL NewValueListCell;
   PVALUE_LIST_CELL ValueListCell;
@@ -1560,16 +2477,16 @@ CmiAddValueToKey(IN PREGISTRY_HIVE RegistryHive,
   NTSTATUS Status;
 
   Status = CmiAllocateValueCell(RegistryHive,
-               &NewValueCell,
-               &VBOffset,
-               ValueName);
-  *pVBOffset = VBOffset;
-
+                               &NewValueCell,
+                               &VBOffset,
+                               ValueName);
   if (!NT_SUCCESS(Status))
     {
       return Status;
     }
 
+  DPRINT("KeyCell->ValuesOffset %lu\n", (ULONG)KeyCell->ValuesOffset);
+
   ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
 
   if (ValueListCell == NULL)
@@ -1580,44 +2497,45 @@ CmiAddValueToKey(IN PREGISTRY_HIVE RegistryHive,
              &VLBOffset);
 
       if (!NT_SUCCESS(Status))
-        {
-          CmiDestroyValueCell(RegistryHive, NewValueCell, VBOffset);
-          return Status;
-        }
+       {
+         CmiDestroyValueCell(RegistryHive, NewValueCell, VBOffset);
+         return Status;
+       }
       KeyCell->ValuesOffset = VLBOffset;
     }
-  else if ((KeyCell->NumberOfValues
-               >= (ULONG) ((LONG) (ValueListCell->CellSize - 4)) / (LONG) sizeof(BLOCK_OFFSET)))
+  else if (KeyCell->NumberOfValues >= 
+          (((ULONG)ABS_VALUE(ValueListCell->CellSize) - 4) / sizeof(BLOCK_OFFSET)))
     {
       Status = CmiAllocateBlock(RegistryHive,
-             (PVOID) &NewValueListCell,
-             sizeof(BLOCK_OFFSET) * (KeyCell->NumberOfValues + REG_VALUE_LIST_CELL_MULTIPLE),
-             &VLBOffset);
-
+                               (PVOID) &NewValueListCell,
+                               (KeyCell->NumberOfValues + REG_VALUE_LIST_CELL_MULTIPLE) *
+                                 sizeof(BLOCK_OFFSET),
+                               &VLBOffset);
       if (!NT_SUCCESS(Status))
-        {
-          CmiDestroyValueCell(RegistryHive, NewValueCell, VBOffset);
-          return Status;
-        }
+       {
+         CmiDestroyValueCell(RegistryHive, NewValueCell, VBOffset);
+         return Status;
+       }
 
       RtlCopyMemory(&NewValueListCell->Values[0],
-        &ValueListCell->Values[0],
-        sizeof(BLOCK_OFFSET) * KeyCell->NumberOfValues);
+                   &ValueListCell->Values[0],
+                   sizeof(BLOCK_OFFSET) * KeyCell->NumberOfValues);
       CmiDestroyBlock(RegistryHive, ValueListCell, KeyCell->ValuesOffset);
       KeyCell->ValuesOffset = VLBOffset;
       ValueListCell = NewValueListCell;
     }
 
-  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));
+  DPRINT("KeyCell->NumberOfValues %lu, ValueListCell->CellSize %lu (%lu %lx)\n",
+        KeyCell->NumberOfValues,
+        (ULONG)ABS_VALUE(ValueListCell->CellSize),
+        ((ULONG)ABS_VALUE(ValueListCell->CellSize) - 4) / sizeof(BLOCK_OFFSET),
+        ((ULONG)ABS_VALUE(ValueListCell->CellSize) - 4) / sizeof(BLOCK_OFFSET));
 
   ValueListCell->Values[KeyCell->NumberOfValues] = VBOffset;
   KeyCell->NumberOfValues++;
-  CmiReleaseBlock(RegistryHive, ValueListCell);
-  CmiReleaseBlock(RegistryHive, NewValueCell);
+
   *pValueCell = NewValueCell;
+  *pVBOffset = VBOffset;
 
   return STATUS_SUCCESS;
 }
@@ -1637,6 +2555,7 @@ CmiDeleteValueFromKey(IN PREGISTRY_HIVE RegistryHive,
 
   if (ValueListCell == NULL)
     {
+      DPRINT("CmiGetBlock() failed\n");
       return STATUS_SUCCESS;
     }
 
@@ -1645,32 +2564,35 @@ CmiDeleteValueFromKey(IN PREGISTRY_HIVE RegistryHive,
   for (i = 0; i < KeyCell->NumberOfValues; i++)
     {
       CurValueCell = CmiGetBlock(RegistryHive, ValueListCell->Values[i], NULL);
+      if (CurValueCell == NULL)
+       {
+         DPRINT("CmiGetBlock() failed\n");
+         return STATUS_UNSUCCESSFUL;
+       }
 
       if ((CurValueCell != NULL) &&
          CmiComparePackedNames(ValueName,
                                CurValueCell->Name,
                                CurValueCell->NameSize,
                                CurValueCell->Flags & REG_VALUE_NAME_PACKED))
-        {
-          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));
-            }
+       {
+         CmiDestroyValueCell(RegistryHive, CurValueCell, ValueListCell->Values[i]);
 
-          KeyCell->NumberOfValues -= 1;
-          CmiDestroyValueCell(RegistryHive, CurValueCell, ValueListCell->Values[i]);
-          break;
-        }
-      CmiReleaseBlock(RegistryHive, CurValueCell);
-    }
+         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));
+           }
 
-  CmiReleaseBlock(RegistryHive, ValueListCell);
+         KeyCell->NumberOfValues -= 1;
+         break;
+       }
+    }
 
   if (KeyCell->NumberOfValues == 0)
     {
@@ -1727,8 +2649,8 @@ CmiAllocateHashTableBlock(IN PREGISTRY_HIVE RegistryHive,
 
 PKEY_CELL
 CmiGetKeyFromHashByIndex(PREGISTRY_HIVE RegistryHive,
-       PHASH_TABLE_CELL HashBlock,
-       ULONG Index)
+                        PHASH_TABLE_CELL HashBlock,
+                        ULONG Index)
 {
   BLOCK_OFFSET KeyOffset;
   PKEY_CELL KeyCell;
@@ -1736,7 +2658,7 @@ CmiGetKeyFromHashByIndex(PREGISTRY_HIVE RegistryHive,
   if (HashBlock == NULL)
     return NULL;
 
-  if (IsVolatileHive(RegistryHive))
+  if (IsPointerHive(RegistryHive))
     {
       KeyCell = (PKEY_CELL) HashBlock->Table[Index].KeyOffset;
     }
@@ -1745,7 +2667,6 @@ CmiGetKeyFromHashByIndex(PREGISTRY_HIVE RegistryHive,
       KeyOffset =  HashBlock->Table[Index].KeyOffset;
       KeyCell = CmiGetBlock(RegistryHive, KeyOffset, NULL);
     }
-  CmiLockBlock(RegistryHive, KeyCell);
 
   return KeyCell;
 }
@@ -1753,9 +2674,9 @@ CmiGetKeyFromHashByIndex(PREGISTRY_HIVE RegistryHive,
 
 NTSTATUS
 CmiAddKeyToHashTable(PREGISTRY_HIVE RegistryHive,
-       PHASH_TABLE_CELL HashBlock,
-       PKEY_CELL NewKeyCell,
-       BLOCK_OFFSET NKBOffset)
+                    PHASH_TABLE_CELL HashBlock,
+                    PKEY_CELL NewKeyCell,
+                    BLOCK_OFFSET NKBOffset)
 {
   ULONG i;
 
@@ -1796,9 +2717,9 @@ CmiRemoveKeyFromHashTable(PREGISTRY_HIVE RegistryHive,
 
 NTSTATUS
 CmiAllocateValueCell(PREGISTRY_HIVE RegistryHive,
-  PVALUE_CELL *ValueCell,
-  BLOCK_OFFSET *VBOffset,
-  IN PUNICODE_STRING ValueName)
+                    PVALUE_CELL *ValueCell,
+                    BLOCK_OFFSET *VBOffset,
+                    IN PUNICODE_STRING ValueName)
 {
   PVALUE_CELL NewValueCell;
   NTSTATUS Status;
@@ -1852,19 +2773,27 @@ CmiAllocateValueCell(PREGISTRY_HIVE RegistryHive,
 
 NTSTATUS
 CmiDestroyValueCell(PREGISTRY_HIVE RegistryHive,
-  PVALUE_CELL ValueCell,
-  BLOCK_OFFSET VBOffset)
+                   PVALUE_CELL ValueCell,
+                   BLOCK_OFFSET VBOffset)
 {
   NTSTATUS Status;
   PVOID pBlock;
   PHBIN pBin;
 
+  DPRINT("CmiDestroyValueCell(Cell %p  Offset %lx)\n", ValueCell, VBOffset);
+
   VERIFY_VALUE_CELL(ValueCell);
 
-  /* First, release data: */
+  /* Destroy the data cell */
   if (ValueCell->DataSize > 4)
     {
       pBlock = CmiGetBlock(RegistryHive, ValueCell->DataOffset, &pBin);
+      if (pBlock == NULL)
+       {
+         DPRINT("CmiGetBlock() failed\n");
+         return STATUS_UNSUCCESSFUL;
+       }
+
       Status = CmiDestroyBlock(RegistryHive, pBlock, ValueCell->DataOffset);
       if (!NT_SUCCESS(Status))
        {
@@ -1872,16 +2801,17 @@ CmiDestroyValueCell(PREGISTRY_HIVE RegistryHive,
        }
 
       /* Update time of heap */
-      if (IsPermanentHive(RegistryHive))
-       ZwQuerySystemTime((PTIME) &pBin->DateModified);
+      if (!IsVolatileHive(RegistryHive))
+       NtQuerySystemTime((PTIME) &pBin->DateModified);
     }
 
+  /* Destroy the value cell */
   Status = CmiDestroyBlock(RegistryHive, ValueCell, VBOffset);
 
   /* Update time of heap */
-  if (IsPermanentHive(RegistryHive) && CmiGetBlock(RegistryHive, VBOffset, &pBin))
+  if (!IsVolatileHive(RegistryHive) && CmiGetBlock(RegistryHive, VBOffset, &pBin))
     {
-      ZwQuerySystemTime((PTIME) &pBin->DateModified);
+      NtQuerySystemTime((PTIME) &pBin->DateModified);
     }
 
   return Status;
@@ -1890,8 +2820,8 @@ CmiDestroyValueCell(PREGISTRY_HIVE RegistryHive,
 
 NTSTATUS
 CmiAddBin(PREGISTRY_HIVE RegistryHive,
-  PVOID *NewBlock,
-  BLOCK_OFFSET *NewBlockOffset)
+         PVOID *NewBlock,
+         BLOCK_OFFSET *NewBlockOffset)
 {
   PCELL_HEADER tmpBlock;
   PHBIN * tmpBlockList;
@@ -1922,14 +2852,15 @@ CmiAddBin(PREGISTRY_HIVE RegistryHive,
 
   if (RegistryHive->BlockListSize > 0)
     {
-      memcpy(tmpBlockList,
-            RegistryHive->BlockList,
-            sizeof(PHBIN *)*(RegistryHive->BlockListSize));
+      RtlCopyMemory (tmpBlockList,
+                    RegistryHive->BlockList,
+                    sizeof(PHBIN *)*(RegistryHive->BlockListSize));
       ExFreePool(RegistryHive->BlockList);
     }
 
   RegistryHive->BlockList = tmpBlockList;
-  RegistryHive->BlockList[RegistryHive->BlockListSize++] = tmpBin;
+  RegistryHive->BlockList[RegistryHive->BlockListSize] = tmpBin;
+  RegistryHive->BlockListSize++;
 
   /* Initialize a free block in this heap : */
   tmpBlock = (PCELL_HEADER)((ULONG_PTR) tmpBin + REG_HBIN_DATA_OFFSET);
@@ -1939,10 +2870,26 @@ CmiAddBin(PREGISTRY_HIVE RegistryHive,
   if (IsVolatileHive(RegistryHive) &&
       (RegistryHive->BlockListSize % (sizeof(ULONG) * 8) == 0))
     {
-      DPRINT1("Grow hive bitmap - BlockListSize %lu\n", RegistryHive->BlockListSize);
+      PULONG BitmapBuffer;
+      ULONG BitmapSize;
 
-      /* FIXME */
+      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);
     }
 
   *NewBlock = (PVOID) tmpBlock;
@@ -1950,7 +2897,9 @@ CmiAddBin(PREGISTRY_HIVE RegistryHive,
   if (NewBlockOffset)
     *NewBlockOffset = tmpBin->BlockOffset + REG_HBIN_DATA_OFFSET;
 
-  /* FIXME: set first dword to block_offset of another free bloc */
+  /* Mark new bin dirty */
+  CmiMarkBinDirty(RegistryHive,
+                 tmpBin->BlockOffset);
 
   return STATUS_SUCCESS;
 }
@@ -1965,6 +2914,8 @@ CmiAllocateBlock(PREGISTRY_HIVE RegistryHive,
   PCELL_HEADER NewBlock;
   NTSTATUS Status;
   PHBIN pBin;
+  ULONG i;
+  PVOID Temp;
 
   Status = STATUS_SUCCESS;
 
@@ -1972,7 +2923,7 @@ CmiAllocateBlock(PREGISTRY_HIVE RegistryHive,
   BlockSize = (BlockSize + sizeof(DWORD) + 15) & 0xfffffff0;
 
   /* Handle volatile hives first */
-  if (IsVolatileHive(RegistryHive))
+  if (IsPointerHive(RegistryHive))
     {
       NewBlock = ExAllocatePool(NonPagedPool, BlockSize);
 
@@ -1984,7 +2935,6 @@ CmiAllocateBlock(PREGISTRY_HIVE RegistryHive,
        {
          RtlZeroMemory(NewBlock, BlockSize);
          NewBlock->CellSize = BlockSize;
-         CmiLockBlock(RegistryHive, NewBlock);
          *Block = NewBlock;
          if (pBlockOffset)
            *pBlockOffset = (BLOCK_OFFSET) NewBlock;
@@ -1992,26 +2942,27 @@ CmiAllocateBlock(PREGISTRY_HIVE RegistryHive,
     }
   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 == NULL)
+               {
+                 DPRINT("CmiGetBlock() failed\n");
+                 return STATUS_UNSUCCESSFUL;
+               }
 
              if (Temp)
                {
-                 ZwQuerySystemTime((PTIME) &pBin->DateModified);
+                 NtQuerySystemTime((PTIME) &pBin->DateModified);
                  CmiMarkBlockDirty(RegistryHive, RegistryHive->FreeListOffset[i]);
                }
 
@@ -2047,8 +2998,12 @@ CmiAllocateBlock(PREGISTRY_HIVE RegistryHive,
            {
              NewBlock = (PCELL_HEADER) ((ULONG_PTR) NewBlock+BlockSize);
              NewBlock->CellSize = ((PCELL_HEADER) (*Block))->CellSize - BlockSize;
-             CmiAddFree(RegistryHive, NewBlock, *pBlockOffset + BlockSize);
-             CmiMarkBlockDirty(RegistryHive, *pBlockOffset + BlockSize);
+             CmiAddFree(RegistryHive,
+                        NewBlock,
+                        *pBlockOffset + BlockSize,
+                        TRUE);
+             CmiMarkBlockDirty(RegistryHive,
+                               *pBlockOffset + BlockSize);
            }
          else if (NewBlock->CellSize < BlockSize)
            {
@@ -2057,7 +3012,6 @@ CmiAllocateBlock(PREGISTRY_HIVE RegistryHive,
 
          RtlZeroMemory(*Block, BlockSize);
          ((PCELL_HEADER) (*Block))->CellSize = -BlockSize;
-         CmiLockBlock(RegistryHive, *Block);
        }
     }
 
@@ -2067,17 +3021,16 @@ CmiAllocateBlock(PREGISTRY_HIVE RegistryHive,
 
 NTSTATUS
 CmiDestroyBlock(PREGISTRY_HIVE RegistryHive,
-  PVOID Block,
-  BLOCK_OFFSET Offset)
+               PVOID Block,
+               BLOCK_OFFSET Offset)
 {
   NTSTATUS Status;
   PHBIN pBin;
 
   Status = STATUS_SUCCESS;
 
-  if (IsVolatileHive(RegistryHive))
+  if (IsPointerHive(RegistryHive))
     {
-      CmiReleaseBlock(RegistryHive, Block);
       ExFreePool(Block);
     }
   else
@@ -2091,98 +3044,184 @@ CmiDestroyBlock(PREGISTRY_HIVE RegistryHive,
       RtlZeroMemory(((PVOID)pFree) + sizeof(ULONG),
                    pFree->CellSize - sizeof(ULONG));
 
-      CmiAddFree(RegistryHive, Block, Offset);
-      CmiReleaseBlock(RegistryHive, Block);
+      /* Add block to the list of free blocks */
+      CmiAddFree(RegistryHive, Block, Offset, TRUE);
 
       /* Update time of heap */
-      if (IsPermanentHive(RegistryHive) && CmiGetBlock(RegistryHive, Offset,&pBin))
-       ZwQuerySystemTime((PTIME) &pBin->DateModified);
+      if (!IsVolatileHive(RegistryHive) && CmiGetBlock(RegistryHive, Offset,&pBin))
+       NtQuerySystemTime((PTIME) &pBin->DateModified);
 
       CmiMarkBlockDirty(RegistryHive, Offset);
-
-      /* FIXME: Set first dword to block_offset of another free block ? */
-      /* FIXME: Concatenate with previous and next block if free */
     }
 
   return Status;
 }
 
 
+static BOOLEAN
+CmiMergeFree(PREGISTRY_HIVE RegistryHive,
+            PCELL_HEADER FreeBlock,
+            BLOCK_OFFSET FreeOffset)
+{
+  BLOCK_OFFSET BlockOffset;
+  BLOCK_OFFSET BinOffset;
+  ULONG BlockSize;
+  ULONG BinSize;
+  PHBIN Bin;
+  ULONG i;
+
+  DPRINT("CmiMergeFree(Block %lx  Offset %lx  Size %lx) called\n",
+        FreeBlock, FreeOffset, FreeBlock->CellSize);
+
+  CmiGetBlock(RegistryHive,
+             FreeOffset,
+             &Bin);
+  DPRINT("Bin %p\n", Bin);
+  if (Bin == NULL)
+    return(FALSE);
+
+  BinOffset = Bin->BlockOffset;
+  BinSize = Bin->BlockSize;
+  DPRINT("Bin %p  Offset %lx  Size %lx\n", Bin, BinOffset, BinSize);
+
+  for (i = 0; i < RegistryHive->FreeListSize; i++)
+    {
+      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);
+
+         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");
+
+             RegistryHive->FreeList[i]->CellSize +=
+               (FreeBlock->CellSize + RegistryHive->FreeList[i + 1]->CellSize);
+
+             FreeBlock->CellSize = 0;
+             RegistryHive->FreeList[i + 1]->CellSize = 0;
+
+
+             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--;
+
+             CmiMarkBlockDirty(RegistryHive, BlockOffset);
+
+             return(TRUE);
+           }
+         else if (BlockOffset + BlockSize == FreeOffset)
+           {
+             DPRINT("Merge current block with previous block\n");
+
+             RegistryHive->FreeList[i]->CellSize += FreeBlock->CellSize;
+             FreeBlock->CellSize = 0;
+
+             CmiMarkBlockDirty(RegistryHive, BlockOffset);
+
+             return(TRUE);
+           }
+         else if (FreeOffset + FreeBlock->CellSize == BlockOffset)
+           {
+             DPRINT("Merge current block with next block\n");
+
+             FreeBlock->CellSize += RegistryHive->FreeList[i]->CellSize;
+             RegistryHive->FreeList[i]->CellSize = 0;
+             RegistryHive->FreeList[i] = FreeBlock;
+             RegistryHive->FreeListOffset[i] = FreeOffset;
+
+             CmiMarkBlockDirty(RegistryHive, FreeOffset);
+
+             return(TRUE);
+           }
+       }
+    }
+
+  return(FALSE);
+}
+
+
 NTSTATUS
 CmiAddFree(PREGISTRY_HIVE RegistryHive,
-  PCELL_HEADER FreeBlock,
-  BLOCK_OFFSET FreeOffset)
+          PCELL_HEADER FreeBlock,
+          BLOCK_OFFSET FreeOffset,
+          BOOLEAN MergeFreeBlocks)
 {
-       PCELL_HEADER *tmpList;
-       BLOCK_OFFSET *tmpListOffset;
-       LONG minInd;
+  PCELL_HEADER *tmpList;
+  BLOCK_OFFSET *tmpListOffset;
+  LONG minInd;
   LONG maxInd;
   LONG medInd;
 
   assert(RegistryHive);
   assert(FreeBlock);
 
-  DPRINT("FreeBlock %.08x  FreeOffset %.08x\n",
-    FreeBlock, FreeOffset);
-DPRINT("\n");
+  DPRINT("FreeBlock %.08lx  FreeOffset %.08lx\n",
+        FreeBlock, FreeOffset);
+
+  /* Merge free blocks */
+  if (MergeFreeBlocks == TRUE)
+    {
+      if (CmiMergeFree(RegistryHive, FreeBlock, FreeOffset))
+       return(STATUS_SUCCESS);
+    }
+
   if ((RegistryHive->FreeListSize + 1) > RegistryHive->FreeListMax)
     {
-DPRINT("\n");
       tmpList = ExAllocatePool(PagedPool,
                          sizeof(PCELL_HEADER) * (RegistryHive->FreeListMax + 32));
-DPRINT("\n");
-
       if (tmpList == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;
-DPRINT("\n");
 
       tmpListOffset = ExAllocatePool(PagedPool,
-                         sizeof(BLOCK_OFFSET *) * (RegistryHive->FreeListMax + 32));
-DPRINT("\n");
+                         sizeof(BLOCK_OFFSET) * (RegistryHive->FreeListMax + 32));
 
       if (tmpListOffset == NULL)
        {
          ExFreePool(tmpList);
          return STATUS_INSUFFICIENT_RESOURCES;
        }
-DPRINT("\n");
 
       if (RegistryHive->FreeListMax)
        {
-DPRINT("\n");
          RtlMoveMemory(tmpList,
                        RegistryHive->FreeList,
                        sizeof(PCELL_HEADER) * (RegistryHive->FreeListMax));
-DPRINT("\n");
          RtlMoveMemory(tmpListOffset,
                        RegistryHive->FreeListOffset,
-                       sizeof(BLOCK_OFFSET *) * (RegistryHive->FreeListMax));
-DPRINT("\n");
+                       sizeof(BLOCK_OFFSET) * (RegistryHive->FreeListMax));
          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],
@@ -2196,7 +3235,6 @@ DPRINT("\n");
     }
   else
     {
-DPRINT("\n");
       /* Search where to insert */
       minInd = 0;
       maxInd = RegistryHive->FreeListSize - 1;
@@ -2220,7 +3258,6 @@ DPRINT("\n");
       RegistryHive->FreeListOffset[maxInd] = FreeOffset;
       RegistryHive->FreeListSize++;
     }
-DPRINT("\n");
 
   return STATUS_SUCCESS;
 }
@@ -2231,68 +3268,103 @@ CmiGetBlock(PREGISTRY_HIVE RegistryHive,
            BLOCK_OFFSET BlockOffset,
            PHBIN * ppBin)
 {
+  PHBIN pBin;
+
   if (ppBin)
     *ppBin = NULL;
 
-  if ((BlockOffset == 0) || (BlockOffset == (ULONG_PTR) -1))
-    return NULL;
+  if (BlockOffset == (BLOCK_OFFSET)-1)
+    {
+      return NULL;
+    }
 
-  if (IsVolatileHive(RegistryHive))
+  if (IsPointerHive (RegistryHive))
     {
-      return (PVOID) BlockOffset;
+      return (PVOID)BlockOffset;
     }
   else
     {
-      PHBIN pBin;
-
+      if (BlockOffset > RegistryHive->BlockListSize * 4096)
+       {
+         DPRINT1("BlockOffset exceeds valid range (%lu > %lu)\n",
+                 BlockOffset, RegistryHive->BlockListSize * 4096);
+         return NULL;
+       }
       pBin = RegistryHive->BlockList[BlockOffset / 4096];
       if (ppBin)
        *ppBin = pBin;
-      return ((PVOID) ((ULONG_PTR) pBin + (BlockOffset - pBin->BlockOffset)));
+      return((PVOID)((ULONG_PTR)pBin + (BlockOffset - pBin->BlockOffset)));
     }
 }
 
 
 VOID
-CmiLockBlock(PREGISTRY_HIVE RegistryHive,
-            PVOID Block)
+CmiMarkBlockDirty(PREGISTRY_HIVE RegistryHive,
+                 BLOCK_OFFSET BlockOffset)
 {
-  if (IsPermanentHive(RegistryHive))
-    {
-      /* FIXME: Implement */
-    }
-}
+  PDATA_CELL Cell;
+  LONG CellSize;
+  ULONG BlockNumber;
+  ULONG BlockCount;
 
+  if (IsVolatileHive(RegistryHive))
+    return;
 
-VOID
-CmiReleaseBlock(PREGISTRY_HIVE RegistryHive,
-               PVOID Block)
-{
-  if (IsPermanentHive(RegistryHive))
-    {
-      /* FIXME: Implement */
-    }
+  DPRINT("CmiMarkBlockDirty(Offset 0x%lx)\n", (ULONG)BlockOffset);
+
+  BlockNumber = (ULONG)BlockOffset / 4096;
+
+  Cell = CmiGetBlock(RegistryHive,
+                    BlockOffset,
+                    NULL);
+
+  CellSize = Cell->CellSize;
+  if (CellSize < 0)
+    CellSize = -CellSize;
+
+  BlockCount = (ROUND_UP(BlockOffset + CellSize, 4096) - ROUND_DOWN(BlockOffset, 4096)) / 4096;
+
+  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);
 }
 
 
 VOID
-CmiMarkBlockDirty(PREGISTRY_HIVE RegistryHive,
-                 BLOCK_OFFSET BlockOffset)
+CmiMarkBinDirty(PREGISTRY_HIVE RegistryHive,
+               BLOCK_OFFSET BinOffset)
 {
-  ULONG Index;
+  ULONG BlockNumber;
+  ULONG BlockCount;
+  PHBIN Bin;
 
   if (IsVolatileHive(RegistryHive))
-      return;
+    return;
+
+  DPRINT("CmiMarkBinDirty(Offset 0x%lx)\n", (ULONG)BinOffset);
+
+  BlockNumber = (ULONG)BinOffset / 4096;
+
+  Bin = RegistryHive->BlockList[BlockNumber];
 
-  Index = (ULONG)BlockOffset / 4096;
+  BlockCount = Bin->BlockSize / 4096;
 
-  DPRINT1("CmiMarkBlockDirty(Offset 0x%lx)  Index %lu\n",
-         (ULONG)BlockOffset, Index);
+  DPRINT("  BlockNumber %lu  Size %lu  BlockCount %lu\n",
+        BlockNumber,
+        Bin->BlockSize,
+        BlockCount);
 
   RegistryHive->HiveDirty = TRUE;
   RtlSetBits(&RegistryHive->DirtyBitMap,
-            Index,
-            1);
+            BlockNumber,
+            BlockCount);
 }