- CmiAddValueToKey(): Double the size of the value list cell when it is completely...
[reactos.git] / reactos / ntoskrnl / cm / regfile.c
index 9642171..bebaf3b 100644 (file)
  * UPDATE HISTORY:
 */
 
-#include <ddk/ntddk.h>
-#include <roscfg.h>
-#include <internal/ob.h>
-#include <limits.h>
-#include <string.h>
-#include <internal/pool.h>
-#include <internal/registry.h>
-
+#include <ntoskrnl.h>
 #define NDEBUG
 #include <internal/debug.h>
 
 #include "cm.h"
 
 
-#define  REG_BLOCK_SIZE                        4096
-#define  REG_HEAP_BLOCK_DATA_OFFSET    32
-#define  REG_HEAP_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_BLOCK_MULTIPLE 4
-#define  REG_KEY_BLOCK_ID              0x6b6e
-#define  REG_HASH_TABLE_BLOCK_ID       0x666c
-#define  REG_VALUE_BLOCK_ID            0x6b76
-#define  REG_KEY_BLOCK_TYPE            0x20
-#define  REG_ROOT_KEY_BLOCK_TYPE       0x2c
-
-
-extern PREGISTRY_FILE  CmiVolatileFile;
-
-void CmiCreateDefaultHeaderBlock(PHEADER_BLOCK HeaderBlock)
-{      
-  RtlZeroMemory(HeaderBlock, sizeof(HEADER_BLOCK));
-  HeaderBlock->BlockId = 0x66676572;
-  HeaderBlock->DateModified.dwLowDateTime = 0;
-  HeaderBlock->DateModified.dwHighDateTime = 0;
-  HeaderBlock->Version = 1;
-  HeaderBlock->Unused3 = 3;
-  HeaderBlock->Unused5 = 1;
-  HeaderBlock->RootKeyBlock = 0;
-  HeaderBlock->BlockSize = REG_BLOCK_SIZE;
-  HeaderBlock->Unused6 = 1;
-  HeaderBlock->Checksum = 0;
-}
-
-void CmiCreateDefaultHeapBlock(PHEAP_BLOCK HeapBlock)
-{
-  RtlZeroMemory(HeapBlock, sizeof(HEAP_BLOCK));
-  HeapBlock->BlockId = REG_HEAP_ID;
-  HeapBlock->DateModified.dwLowDateTime = 0;
-  HeapBlock->DateModified.dwHighDateTime = 0;
-  HeapBlock->BlockSize = REG_BLOCK_SIZE;
-}
-
-void CmiCreateDefaultRootKeyBlock(PKEY_BLOCK RootKeyBlock)
-{
-  RtlZeroMemory(RootKeyBlock, sizeof(KEY_BLOCK));
-  RootKeyBlock->SubBlockSize=-sizeof(KEY_BLOCK);
-  RootKeyBlock->SubBlockId = REG_KEY_BLOCK_ID;
-  RootKeyBlock->Type = REG_ROOT_KEY_BLOCK_TYPE;
-  ZwQuerySystemTime((PTIME) &RootKeyBlock->LastWriteTime);
-  RootKeyBlock->ParentKeyOffset = 0;
-  RootKeyBlock->NumberOfSubKeys = 0;
-  RootKeyBlock->HashTableOffset = -1;
-  RootKeyBlock->NumberOfValues = 0;
-  RootKeyBlock->ValuesOffset = -1;
-  RootKeyBlock->SecurityKeyOffset = 0;
-  RootKeyBlock->ClassNameOffset = -1;
-  RootKeyBlock->NameSize = 0;
-  RootKeyBlock->ClassSize = 0;
-}
-
-NTSTATUS CmiCreateNewRegFile( HANDLE FileHandle )
-{
-  NTSTATUS Status;
-  PHEADER_BLOCK HeaderBlock;
-  PHEAP_BLOCK HeapBlock;
-  PKEY_BLOCK  RootKeyBlock;
-  PFREE_SUB_BLOCK FreeSubBlock;
-  
-  IO_STATUS_BLOCK IoStatusBlock;
-  char* tBuf;
-  
-  tBuf = (char*) ExAllocatePool(NonPagedPool, 2*REG_BLOCK_SIZE);
-  if( tBuf == NULL )
-    return STATUS_INSUFFICIENT_RESOURCES;
-    
-  HeaderBlock = (PHEADER_BLOCK)tBuf;
-  HeapBlock = (PHEAP_BLOCK) (tBuf+REG_BLOCK_SIZE);
-  RootKeyBlock = (PKEY_BLOCK) (tBuf+REG_BLOCK_SIZE+REG_HEAP_BLOCK_DATA_OFFSET);
-  FreeSubBlock = (PFREE_SUB_BLOCK) (tBuf+REG_BLOCK_SIZE+REG_HEAP_BLOCK_DATA_OFFSET+sizeof(KEY_BLOCK));
-  
-  CmiCreateDefaultHeaderBlock(HeaderBlock);
-  CmiCreateDefaultHeapBlock(HeapBlock);
-  CmiCreateDefaultRootKeyBlock(RootKeyBlock);
-  
-  HeapBlock->BlockOffset = 0; //First block.
-  HeaderBlock->RootKeyBlock = REG_HEAP_BLOCK_DATA_OFFSET; //Offset to root key block.
-  //The rest of the block is free
-  FreeSubBlock->SubBlockSize = REG_BLOCK_SIZE-(REG_HEAP_BLOCK_DATA_OFFSET+sizeof(KEY_BLOCK));
-  
-  Status = ZwWriteFile( FileHandle, NULL, NULL, NULL,
-                        &IoStatusBlock, tBuf, 2*REG_BLOCK_SIZE, 0, NULL );
-  
-  ExFreePool( tBuf );
-  return Status;
-}
+/* uncomment to enable hive checks (incomplete and probably buggy) */
+// #define HIVE_CHECK
 
-PREGISTRY_FILE  
-CmiCreateRegistry(PWSTR  Filename)
-{
- PREGISTRY_FILE  RegistryFile;
- HANDLE FileHandle;
- PKEY_BLOCK  RootKeyBlock;
- PHEAP_BLOCK tmpHeap;
- LARGE_INTEGER fileOffset;
- PFREE_SUB_BLOCK  FreeBlock;
- DWORD  FreeOffset;
- int i, j;
- BLOCK_OFFSET BlockOffset;
-DPRINT("CmiCreateRegistry() Filename '%S'\n", Filename);
-  RegistryFile = ExAllocatePool(NonPagedPool, sizeof(REGISTRY_FILE));
+/* LOCAL MACROS *************************************************************/
 
-  if (RegistryFile == NULL)
-    return NULL;
+#define ROUND_DOWN(N, S) ((N) - ((N) % (S)))
 
-  if (Filename != NULL)
-   {
-     UNICODE_STRING TmpFileName;
-     OBJECT_ATTRIBUTES  ObjectAttributes;
-     NTSTATUS Status;
-     FILE_STANDARD_INFORMATION fsi;
-     IO_STATUS_BLOCK IoSB;
-
-      /* Duplicate Filename  */
-      RegistryFile->Filename = ExAllocatePool(NonPagedPool, MAX_PATH);
-      wcscpy(RegistryFile->Filename , Filename);
-
-      RtlInitUnicodeString (&TmpFileName, Filename);
-      InitializeObjectAttributes(&ObjectAttributes,
-                             &TmpFileName,
-                             0,
-                             NULL,
-                             NULL);
-
-      Status = NtCreateFile( &FileHandle,
-                         FILE_ALL_ACCESS,
-                         &ObjectAttributes,
-                         &IoSB,
-                         NULL,
-                         FILE_ATTRIBUTE_NORMAL,
-                         0,
-                         FILE_OPEN_IF,
-                         FILE_NON_DIRECTORY_FILE,
-                         NULL,
-                         0 );
-
-      /* FIXME: create directory if IoSB.Status == STATUS_OBJECT_PATH_NOT_FOUND */
-      if( !NT_SUCCESS(Status) )
-      {
-        ExFreePool(RegistryFile->Filename);
-       RegistryFile->Filename = NULL;
-       return NULL;
-      }
-      /*if file did not exist*/
-      if( IoSB.Information == FILE_CREATED ){
-        Status = CmiCreateNewRegFile( FileHandle );
-        if( !NT_SUCCESS(Status) )
-        {
-          ExFreePool(RegistryFile->Filename);
-         RegistryFile->Filename = NULL;
-         return NULL;
-        }
-      }
-
-      RegistryFile->HeaderBlock = (PHEADER_BLOCK) 
-        ExAllocatePool(NonPagedPool, sizeof(HEADER_BLOCK));
-
-      fileOffset.u.HighPart = 0;
-      fileOffset.u.LowPart = 0;
-      Status = ZwReadFile(FileHandle, 
-                      0, 0, 0, 0, 
-                      RegistryFile->HeaderBlock, 
-                      sizeof(HEADER_BLOCK), 
-                      &fileOffset, 0);
-      if (!NT_SUCCESS(Status))
-        {
-          ExFreePool(RegistryFile->Filename);
-         RegistryFile->Filename = NULL;
-         return NULL;
-        }
+#define ABS_VALUE(V) (((V) < 0) ? -(V) : (V))
 
-      Status = ZwQueryInformationFile(FileHandle,&IoSB,&fsi
-               ,sizeof(fsi),FileStandardInformation);
-      if (!NT_SUCCESS(Status))
-        {
-          ExFreePool(RegistryFile->Filename);
-         RegistryFile->Filename = NULL;
-         return NULL;
-        }
-      RegistryFile->FileSize = fsi.EndOfFile.u.LowPart;
-      RegistryFile->BlockListSize = RegistryFile->FileSize / 4096 -1;
-//      RegistryFile->NumberOfBlocks = RegistryFile->BlockListSize;
-      RegistryFile->BlockList = ExAllocatePool(NonPagedPool,
-          sizeof(PHEAP_BLOCK *) * (RegistryFile->BlockListSize ));
-      BlockOffset=0;
-      fileOffset.u.HighPart = 0;
-      fileOffset.u.LowPart = 4096;
-      RegistryFile->BlockList [0]
-          = ExAllocatePool(NonPagedPool,RegistryFile->FileSize-4096);
-      if (RegistryFile->BlockList[0] == NULL)
-      {
-//        Status = STATUS_INSUFFICIENT_RESOURCES;
-       DPRINT("error allocating %d bytes for registry\n"
-          ,RegistryFile->FileSize-4096);
-       ZwClose(FileHandle);
-       return NULL;
-      }
-      Status = ZwReadFile(FileHandle,
-                      0, 0, 0, 0, 
-                      RegistryFile->BlockList [0],
-                      RegistryFile->FileSize-4096,
-                      &fileOffset, 0);
-      ZwClose(FileHandle);
-      if (!NT_SUCCESS(Status))
-      {
-       DPRINT("error %x reading registry file at offset %x\n"
-                       ,Status,fileOffset.u.LowPart);
-        return NULL;
-      }
-      RegistryFile->FreeListSize = 0;
-      RegistryFile->FreeListMax = 0;
-      RegistryFile->FreeList = NULL;
-      for(i=0 ; i <RegistryFile->BlockListSize; i++)
-      {
-        tmpHeap = (PHEAP_BLOCK)(((char *)RegistryFile->BlockList [0])+BlockOffset);
-       if (tmpHeap->BlockId != REG_HEAP_ID )
-       {
-         DPRINT("bad BlockId %x,offset %x\n",tmpHeap->BlockId,fileOffset.u.LowPart);
-       }
-       RegistryFile->BlockList [i]
-          = tmpHeap;
-       if (tmpHeap->BlockSize >4096)
-       {
-         for(j=1;j<tmpHeap->BlockSize/4096;j++)
-            RegistryFile->BlockList[i+j] = RegistryFile->BlockList[i];
-         i = i+j-1;
-       }
-        /* search free blocks and add to list */
-        FreeOffset=REG_HEAP_BLOCK_DATA_OFFSET;
-        while(FreeOffset < tmpHeap->BlockSize)
-        {
-          FreeBlock = (PFREE_SUB_BLOCK)((char *)RegistryFile->BlockList[i]
-                       +FreeOffset);
-          if ( FreeBlock->SubBlockSize>0)
-         {
-           CmiAddFree(RegistryFile,FreeBlock
-               ,RegistryFile->BlockList[i]->BlockOffset+FreeOffset);
-           FreeOffset += FreeBlock->SubBlockSize;
-         }
-         else
-           FreeOffset -= FreeBlock->SubBlockSize;
-        }
-       BlockOffset += tmpHeap->BlockSize;
-      }
-      Status = ObReferenceObjectByHandle(FileHandle,
-                 FILE_ALL_ACCESS,
-                IoFileObjectType,
-                UserMode,
-                 (PVOID*)&RegistryFile->FileObject,
-                 NULL);
-   }
-  else
-    {
-      RegistryFile->Filename = NULL;
-      RegistryFile->FileObject = NULL;
+BOOLEAN CmiDoVerify = FALSE;
 
-      RegistryFile->HeaderBlock = (PHEADER_BLOCK) 
-        ExAllocatePool(NonPagedPool, sizeof(HEADER_BLOCK));
-      CmiCreateDefaultHeaderBlock(RegistryFile->HeaderBlock); 
-      
-      RootKeyBlock = (PKEY_BLOCK) ExAllocatePool(NonPagedPool, sizeof(KEY_BLOCK));
-      CmiCreateDefaultRootKeyBlock(RootKeyBlock);
+static ULONG
+CmiCalcChecksum(PULONG Buffer);
 
-      RegistryFile->HeaderBlock->RootKeyBlock = (BLOCK_OFFSET) RootKeyBlock;
-    }
-  KeInitializeSemaphore(&RegistryFile->RegSem, 1, 1);
+/* FUNCTIONS ****************************************************************/
 
-  return  RegistryFile;
+VOID
+CmiCreateDefaultHiveHeader(PHIVE_HEADER Header)
+{
+  assert(Header);
+  RtlZeroMemory(Header, sizeof(HIVE_HEADER));
+  Header->BlockId = REG_HIVE_ID;
+  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->RootKeyOffset = (BLOCK_OFFSET)-1;
+  Header->BlockSize = REG_BLOCK_SIZE;
+  Header->Unused6 = 1;
+  Header->Checksum = 0;
 }
 
-ULONG
-CmiGetMaxNameLength(PREGISTRY_FILE  RegistryFile,
-                    PKEY_BLOCK  KeyBlock)
+
+VOID
+CmiCreateDefaultBinHeader(PHBIN BinHeader)
 {
-  ULONG  Idx, MaxName;
-  PHASH_TABLE_BLOCK  HashBlock;
-  PKEY_BLOCK  CurSubKeyBlock;
+  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;
+}
 
-  MaxName = 0;
-  HashBlock = CmiGetBlock(RegistryFile, KeyBlock->HashTableOffset,NULL);
-  if (HashBlock == 0)
-    {
-      return  0;
-    }
-  for (Idx = 0; Idx < HashBlock->HashTableSize; Idx++)
-    {
-      if (HashBlock->Table[Idx].KeyOffset != 0)
-        {
-          CurSubKeyBlock = CmiGetBlock(RegistryFile,
-                                          HashBlock->Table[Idx].KeyOffset,NULL);
-          if (MaxName < CurSubKeyBlock->NameSize)
-            {
-              MaxName = CurSubKeyBlock->NameSize;
-            }
-          CmiReleaseBlock(RegistryFile, CurSubKeyBlock);
-        }
-    }
 
-  CmiReleaseBlock(RegistryFile, HashBlock);
-  
-  return  MaxName;
+VOID
+CmiCreateDefaultRootKeyCell(PKEY_CELL RootKeyCell)
+{
+  assert(RootKeyCell);
+  RtlZeroMemory(RootKeyCell, sizeof(KEY_CELL));
+  RootKeyCell->CellSize = -sizeof(KEY_CELL);
+  RootKeyCell->Id = REG_KEY_CELL_ID;
+  RootKeyCell->Flags = REG_KEY_ROOT_CELL | REG_KEY_NAME_PACKED;
+  NtQuerySystemTime(&RootKeyCell->LastWriteTime);
+  RootKeyCell->ParentKeyOffset = 0;
+  RootKeyCell->NumberOfSubKeys = 0;
+  RootKeyCell->HashTableOffset = -1;
+  RootKeyCell->NumberOfValues = 0;
+  RootKeyCell->ValueListOffset = -1;
+  RootKeyCell->SecurityKeyOffset = 0;
+  RootKeyCell->ClassNameOffset = -1;
+  RootKeyCell->NameSize = 0;
+  RootKeyCell->ClassSize = 0;
 }
 
-ULONG  
-CmiGetMaxClassLength(PREGISTRY_FILE  RegistryFile,
-                     PKEY_BLOCK  KeyBlock)
+
+VOID
+CmiVerifyBinHeader(PHBIN BinHeader)
 {
-  ULONG  Idx, MaxClass;
-  PHASH_TABLE_BLOCK  HashBlock;
-  PKEY_BLOCK  CurSubKeyBlock;
+  if (CmiDoVerify)
+    {
 
-  MaxClass = 0;
-  HashBlock = CmiGetBlock(RegistryFile, KeyBlock->HashTableOffset,NULL);
-  if (HashBlock == 0)
+  assert(BinHeader);
+
+  if (BinHeader->HeaderId != REG_BIN_ID)
     {
-      return  0;
+      DbgPrint("Bin header ID is %.08x (should be %.08x)\n",
+        BinHeader->HeaderId, REG_BIN_ID);
+      assert(BinHeader->HeaderId == REG_BIN_ID);
     }
-  for (Idx = 0; Idx < HashBlock->HashTableSize; Idx++)
+
+  //BinHeader->DateModified.dwLowDateTime
+
+  //BinHeader->DateModified.dwHighDateTime
+
+  
+  if (BinHeader->BinSize != REG_BLOCK_SIZE)
     {
-      if (HashBlock->Table[Idx].KeyOffset != 0)
-        {
-          CurSubKeyBlock = CmiGetBlock(RegistryFile,
-                                          HashBlock->Table[Idx].KeyOffset,NULL);
-          if (MaxClass < CurSubKeyBlock->ClassSize)
-            {
-              MaxClass = CurSubKeyBlock->ClassSize;
-            }
-          CmiReleaseBlock(RegistryFile, CurSubKeyBlock);
-        }
+      DbgPrint("BinSize is %.08x (should be a multiple of %.08x)\n",
+        BinHeader->BinSize, REG_BLOCK_SIZE);
+      assert(BinHeader->BinSize % REG_BLOCK_SIZE == 0);
     }
 
-  CmiReleaseBlock(RegistryFile, HashBlock);
-  
-  return  MaxClass;
+    }
 }
 
-ULONG  
-CmiGetMaxValueNameLength(PREGISTRY_FILE  RegistryFile,
-                         PKEY_BLOCK  KeyBlock)
+
+VOID
+CmiVerifyKeyCell(PKEY_CELL KeyCell)
 {
-  ULONG  Idx, MaxValueName;
-  PVALUE_LIST_BLOCK  ValueListBlock;
-  PVALUE_BLOCK  CurValueBlock;
+  if (CmiDoVerify)
+    {
 
-  ValueListBlock = CmiGetBlock(RegistryFile, 
-                               KeyBlock->ValuesOffset,NULL);
-  MaxValueName = 0;
-  if (ValueListBlock == 0)
+  assert(KeyCell);
+
+  if (KeyCell->CellSize == 0)
     {
-      return  0;
+      DbgPrint("CellSize is %d (must not be 0)\n",
+        KeyCell->CellSize);
+      assert(KeyCell->CellSize != 0);
     }
-  for (Idx = 0; Idx < KeyBlock->NumberOfValues; Idx++)
+
+  if (KeyCell->Id != REG_KEY_CELL_ID)
     {
-      CurValueBlock = CmiGetBlock(RegistryFile,
-                                  ValueListBlock->Values[Idx],NULL);
-      if (CurValueBlock != NULL &&
-          MaxValueName < CurValueBlock->NameSize)
-        {
-          MaxValueName = CurValueBlock->NameSize;
-        }
-      CmiReleaseBlock(RegistryFile, CurValueBlock);
+      DbgPrint("Id is %.08x (should be %.08x)\n",
+        KeyCell->Id, REG_KEY_CELL_ID);
+      assert(KeyCell->Id == REG_KEY_CELL_ID);
     }
 
-  CmiReleaseBlock(RegistryFile, ValueListBlock);
-  
-  return  MaxValueName;
-}
+  //KeyCell->Flags;
 
-ULONG  
-CmiGetMaxValueDataLength(PREGISTRY_FILE  RegistryFile,
-                         PKEY_BLOCK  KeyBlock)
-{
-  ULONG  Idx, MaxValueData;
-  PVALUE_LIST_BLOCK  ValueListBlock;
-  PVALUE_BLOCK  CurValueBlock;
+  //KeyCell->LastWriteTime;
 
-  ValueListBlock = CmiGetBlock(RegistryFile, 
-                               KeyBlock->ValuesOffset,NULL);
-  MaxValueData = 0;
-  if (ValueListBlock == 0)
+  if (KeyCell->ParentKeyOffset < 0)
     {
-      return  0;
+      DbgPrint("ParentKeyOffset is %d (must not be < 0)\n",
+        KeyCell->ParentKeyOffset);
+      assert(KeyCell->ParentKeyOffset >= 0);
     }
-  for (Idx = 0; Idx < KeyBlock->NumberOfValues; Idx++)
+
+  if (KeyCell->NumberOfSubKeys < 0)
     {
-      CurValueBlock = CmiGetBlock(RegistryFile,
-                                  ValueListBlock->Values[Idx],NULL);
-      if (CurValueBlock != NULL &&
-          MaxValueData < (CurValueBlock->DataSize & LONG_MAX) )
-        {
-          MaxValueData = CurValueBlock->DataSize & LONG_MAX;
-        }
-      CmiReleaseBlock(RegistryFile, CurValueBlock);
+      DbgPrint("NumberOfSubKeys is %d (must not be < 0)\n",
+        KeyCell->NumberOfSubKeys);
+      assert(KeyCell->NumberOfSubKeys >= 0);
     }
 
-  CmiReleaseBlock(RegistryFile, ValueListBlock);
-  
-  return  MaxValueData;
-}
+  //KeyCell->HashTableOffset;
 
-NTSTATUS
-CmiScanForSubKey(IN PREGISTRY_FILE  RegistryFile,
-                 IN PKEY_BLOCK  KeyBlock,
-                 OUT PKEY_BLOCK  *SubKeyBlock,
-                 OUT BLOCK_OFFSET *BlockOffset,
-                 IN PCHAR  KeyName,
-                 IN ACCESS_MASK  DesiredAccess,
-                 IN ULONG Attributes)
-{
-  ULONG  Idx;
-  PHASH_TABLE_BLOCK  HashBlock;
-  PKEY_BLOCK  CurSubKeyBlock;
-  WORD KeyLength = strlen(KeyName);
-
-  HashBlock = CmiGetBlock(RegistryFile, KeyBlock->HashTableOffset,NULL);
-  *SubKeyBlock = NULL;
-  if (HashBlock == NULL)
+  if (KeyCell->NumberOfValues < 0)
     {
-      return  STATUS_SUCCESS;
+      DbgPrint("NumberOfValues is %d (must not be < 0)\n",
+        KeyCell->NumberOfValues);
+      assert(KeyCell->NumberOfValues >= 0);
     }
-//  for (Idx = 0; Idx < HashBlock->HashTableSize; Idx++)
-  for (Idx = 0; Idx < KeyBlock->NumberOfSubKeys
-               && Idx < HashBlock->HashTableSize; Idx++)
+
+  //KeyCell->ValuesOffset = -1;
+
+  if (KeyCell->SecurityKeyOffset < 0)
     {
-      if (Attributes & OBJ_CASE_INSENSITIVE)
-        {
-          if (HashBlock->Table[Idx].KeyOffset != 0 &&
-              HashBlock->Table[Idx].KeyOffset != -1 &&
-              !_strnicmp(KeyName, (PCHAR) &HashBlock->Table[Idx].HashValue, 4))
-            {
-              CurSubKeyBlock = CmiGetBlock(RegistryFile,
-                                           HashBlock->Table[Idx].KeyOffset,NULL);
-              if ( CurSubKeyBlock->NameSize == KeyLength
-                  && !_strnicmp(KeyName, CurSubKeyBlock->Name, KeyLength))
-                {
-                  *SubKeyBlock = CurSubKeyBlock;
-                  *BlockOffset = HashBlock->Table[Idx].KeyOffset;
-                  break;
-                }
-              else
-                {
-                  CmiReleaseBlock(RegistryFile, CurSubKeyBlock);
-                }
-            }
-        }
-      else
-        {
-          if (HashBlock->Table[Idx].KeyOffset != 0 &&
-              HashBlock->Table[Idx].KeyOffset != -1 &&
-              !strncmp(KeyName, (PCHAR) &HashBlock->Table[Idx].HashValue, 4))
-            {
-              CurSubKeyBlock = CmiGetBlock(RegistryFile,
-                                           HashBlock->Table[Idx].KeyOffset,NULL);
-              if ( CurSubKeyBlock->NameSize == KeyLength
-                  && !_strnicmp(KeyName, CurSubKeyBlock->Name, KeyLength))
-                {
-                  *SubKeyBlock = CurSubKeyBlock;
-                  *BlockOffset = HashBlock->Table[Idx].KeyOffset;
-                  break;
-                }
-              else
-                {
-                  CmiReleaseBlock(RegistryFile, CurSubKeyBlock);
-                }
-            }
-        }
+      DbgPrint("SecurityKeyOffset is %d (must not be < 0)\n",
+        KeyCell->SecurityKeyOffset);
+      assert(KeyCell->SecurityKeyOffset >= 0);
     }
-  
-  CmiReleaseBlock(RegistryFile, HashBlock);
-  
-  return  STATUS_SUCCESS;
-}
 
-NTSTATUS
-CmiAddSubKey(PREGISTRY_FILE  RegistryFile, 
-             PKEY_OBJECT Parent,
-             PKEY_OBJECT  SubKey,
-             PWSTR  NewSubKeyName,
-             USHORT  NewSubKeyNameSize,
-             ULONG  TitleIndex,
-             PUNICODE_STRING  Class, 
-             ULONG  CreateOptions)
-{
-  PKEY_BLOCK  KeyBlock = Parent->KeyBlock;
-  NTSTATUS  Status;
-  PHASH_TABLE_BLOCK  HashBlock, NewHashBlock;
-  PKEY_BLOCK  NewKeyBlock; 
-  BLOCK_OFFSET NKBOffset;
-  ULONG  NewBlockSize;
-  USHORT NameSize;
+  //KeyCell->ClassNameOffset = -1;
 
-  if (NewSubKeyName[0] == L'\\')
-  {
-    NewSubKeyName++;
-    NameSize = NewSubKeyNameSize/2-1;
-  }
-  else
-    NameSize = NewSubKeyNameSize/2;
-  Status = STATUS_SUCCESS;
+  //KeyCell->NameSize
 
-  NewBlockSize = sizeof(KEY_BLOCK) + NameSize;
-  Status = CmiAllocateBlock(RegistryFile, (PVOID) &NewKeyBlock 
-               , NewBlockSize,&NKBOffset);
-  if (NewKeyBlock == NULL)
-  {
-    Status = STATUS_INSUFFICIENT_RESOURCES;
-  }
-  else
-  {
-    NewKeyBlock->SubBlockId = REG_KEY_BLOCK_ID;
-    NewKeyBlock->Type = REG_KEY_BLOCK_TYPE;
-    ZwQuerySystemTime((PTIME) &NewKeyBlock->LastWriteTime);
-    NewKeyBlock->ParentKeyOffset = -1;
-    NewKeyBlock->NumberOfSubKeys = 0;
-    NewKeyBlock->HashTableOffset = -1;
-    NewKeyBlock->NumberOfValues = 0;
-    NewKeyBlock->ValuesOffset = -1;
-    NewKeyBlock->SecurityKeyOffset = -1;
-    NewKeyBlock->NameSize = NameSize;
-    wcstombs(NewKeyBlock->Name,NewSubKeyName,NameSize);
-    NewKeyBlock->ClassNameOffset = -1;
-    if (Class)
-    {
-     PDATA_BLOCK pClass;
-      NewKeyBlock->ClassSize = Class->Length+sizeof(WCHAR);
-      Status = CmiAllocateBlock(RegistryFile
-                       ,(PVOID)&pClass
-                       ,NewKeyBlock->ClassSize
-                       ,&NewKeyBlock->ClassNameOffset );
-      wcsncpy((PWSTR)pClass->Data,Class->Buffer,Class->Length);
-      ( (PWSTR)(pClass->Data))[Class->Length]=0;
-    }
-  }
+  //KeyCell->ClassSize
 
-  if (!NT_SUCCESS(Status))
-    {
-      return  Status;
-    }
-  SubKey->KeyBlock = NewKeyBlock;
-  SubKey->BlockOffset = NKBOffset;
-  /* don't modify hash table if key is volatile and parent is not */
-  if (RegistryFile == CmiVolatileFile && Parent->RegistryFile != RegistryFile)
-  {
-    return Status;
-  }
-  if (KeyBlock->HashTableOffset == -1)
-    {
-      Status = CmiAllocateHashTableBlock(RegistryFile, 
-                                         &HashBlock,
-                                        &KeyBlock->HashTableOffset,
-                                         REG_INIT_HASH_TABLE_SIZE);
-      if (!NT_SUCCESS(Status))
-        {
-          return  Status;
-        }
     }
-  else
+}
+
+
+VOID
+CmiVerifyRootKeyCell(PKEY_CELL RootKeyCell)
+{
+  if (CmiDoVerify)
     {
-      HashBlock = CmiGetBlock(RegistryFile, KeyBlock->HashTableOffset,NULL);
-      if (KeyBlock->NumberOfSubKeys + 1 >= HashBlock->HashTableSize)
-        {
-       BLOCK_OFFSET HTOffset;
-
-          /*  Reallocate the hash table block  */
-          Status = CmiAllocateHashTableBlock(RegistryFile,
-                                             &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(RegistryFile, HashBlock
-               , KeyBlock->HashTableOffset);
-         KeyBlock->HashTableOffset = HTOffset;
-          HashBlock = NewHashBlock;
-        }
-    }
-  Status = CmiAddKeyToHashTable(RegistryFile, HashBlock, NewKeyBlock,NKBOffset);
-  if (NT_SUCCESS(Status))
+
+  CmiVerifyKeyCell(RootKeyCell);
+
+  if (!(RootKeyCell->Flags & REG_KEY_ROOT_CELL))
     {
-      KeyBlock->NumberOfSubKeys++;
-    }
-  
-  return  Status;
-}
-
-NTSTATUS  
-CmiScanKeyForValue(IN PREGISTRY_FILE  RegistryFile,
-                   IN PKEY_BLOCK  KeyBlock,
-                   IN PCHAR  ValueName,
-                   OUT PVALUE_BLOCK  *ValueBlock,
-                  OUT BLOCK_OFFSET *VBOffset)
-{
-  ULONG Length;
- ULONG  Idx;
- PVALUE_LIST_BLOCK  ValueListBlock;
- PVALUE_BLOCK  CurValueBlock;
-  ValueListBlock = CmiGetBlock(RegistryFile, 
-                               KeyBlock->ValuesOffset,NULL);
-  *ValueBlock = NULL;
-  if (ValueListBlock == NULL)
-    {
-      DPRINT("ValueListBlock is NULL\n");
-      return  STATUS_SUCCESS;
-    }
-  for (Idx = 0; Idx < KeyBlock->NumberOfValues; Idx++)
-    {
-      CurValueBlock = CmiGetBlock(RegistryFile,
-                                  ValueListBlock->Values[Idx],NULL);
-      /* FIXME : perhaps we must not ignore case if NtCreateKey has not been */
-      /*         called with OBJ_CASE_INSENSITIVE flag ? */
-      Length = strlen(ValueName);
-      if ((CurValueBlock != NULL) &&
-          (CurValueBlock->NameSize == Length) &&
-          (_strnicmp(CurValueBlock->Name, ValueName, Length) == 0))
-        {
-          *ValueBlock = CurValueBlock;
-         if(VBOffset) *VBOffset = ValueListBlock->Values[Idx];
-        DPRINT("Found value %s\n", ValueName);
-          break;
-        }
-      CmiReleaseBlock(RegistryFile, CurValueBlock);
+      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)));
     }
 
-  CmiReleaseBlock(RegistryFile, ValueListBlock);
-  
-  return  STATUS_SUCCESS;
+    }
 }
 
 
-NTSTATUS
-CmiGetValueFromKeyByIndex(IN PREGISTRY_FILE  RegistryFile,
-                          IN PKEY_BLOCK  KeyBlock,
-                          IN ULONG  Index,
-                          OUT PVALUE_BLOCK  *ValueBlock)
-{
- PVALUE_LIST_BLOCK  ValueListBlock;
- PVALUE_BLOCK  CurValueBlock;
-  ValueListBlock = CmiGetBlock(RegistryFile,
-                               KeyBlock->ValuesOffset,NULL);
-  *ValueBlock = NULL;
-  if (ValueListBlock == NULL)
+VOID
+CmiVerifyValueCell(PVALUE_CELL ValueCell)
+{
+  if (CmiDoVerify)
     {
-      return STATUS_NO_MORE_ENTRIES;
-    }
-  if (Index >= KeyBlock->NumberOfValues)
+
+  assert(ValueCell);
+
+  if (ValueCell->CellSize == 0)
     {
-      return STATUS_NO_MORE_ENTRIES;
+      DbgPrint("CellSize is %d (must not be 0)\n",
+        ValueCell->CellSize);
+      assert(ValueCell->CellSize != 0);
     }
-  CurValueBlock = CmiGetBlock(RegistryFile,
-                              ValueListBlock->Values[Index],NULL);
-  if (CurValueBlock != NULL)
+
+  if (ValueCell->Id != REG_VALUE_CELL_ID)
     {
-      *ValueBlock = CurValueBlock;
+      DbgPrint("Id is %.08x (should be %.08x)\n",
+        ValueCell->Id, REG_VALUE_CELL_ID);
+      assert(ValueCell->Id == REG_VALUE_CELL_ID);
     }
-  CmiReleaseBlock(RegistryFile, CurValueBlock);
-  CmiReleaseBlock(RegistryFile, ValueListBlock);
-  
-  return  STATUS_SUCCESS;
-}
-
-NTSTATUS  
-CmiAddValueToKey(IN PREGISTRY_FILE  RegistryFile,
-                 IN PKEY_BLOCK  KeyBlock,
-                 IN PCHAR  ValueNameBuf,
-                OUT PVALUE_BLOCK *pValueBlock,
-                OUT BLOCK_OFFSET *pVBOffset)
-{
- NTSTATUS  Status;
- PVALUE_LIST_BLOCK  ValueListBlock, NewValueListBlock;
- BLOCK_OFFSET  VBOffset;
- BLOCK_OFFSET  VLBOffset;
- PVALUE_BLOCK NewValueBlock;
-
-  Status = CmiAllocateValueBlock(RegistryFile,
-                                 &NewValueBlock,
-                                 &VBOffset,
-                                 ValueNameBuf);
-  *pVBOffset=VBOffset;
-  if (!NT_SUCCESS(Status))
-    {
-      return  Status;
+
+  //ValueCell->NameSize;
+  //ValueCell->LONG  DataSize;
+  //ValueCell->DataOffset;
+  //ValueCell->ULONG  DataType;
+  //ValueCell->USHORT Flags;
+  //ValueCell->USHORT Unused1;
+  //ValueCell->UCHAR  Name[0];
     }
-  ValueListBlock = CmiGetBlock(RegistryFile, 
-                               KeyBlock->ValuesOffset,NULL);
-  if (ValueListBlock == NULL)
+}
+
+
+VOID
+CmiVerifyValueListCell(PVALUE_LIST_CELL ValueListCell)
+{
+  if (CmiDoVerify)
     {
-      Status = CmiAllocateBlock(RegistryFile,
-                                (PVOID) &ValueListBlock,
-                                sizeof(BLOCK_OFFSET) * 3,
-                                &VLBOffset);
-      if (!NT_SUCCESS(Status))
-        {
-          CmiDestroyValueBlock(RegistryFile,
-                               NewValueBlock,VBOffset);
-          return  Status;
-        }
-      KeyBlock->ValuesOffset = VLBOffset;
-    }
-  else if ( KeyBlock->NumberOfValues 
-               >= -(ValueListBlock->SubBlockSize-4)/sizeof(BLOCK_OFFSET))
+
+  if (ValueListCell->CellSize == 0)
     {
-      Status = CmiAllocateBlock(RegistryFile,
-                                (PVOID) &NewValueListBlock,
-                                sizeof(BLOCK_OFFSET) *
-                                  (KeyBlock->NumberOfValues + 
-                                    REG_VALUE_LIST_BLOCK_MULTIPLE),&VLBOffset);
-      if (!NT_SUCCESS(Status))
-        {
-          CmiDestroyValueBlock(RegistryFile,
-                               NewValueBlock,VBOffset);
-          return  Status;
-        }
-      RtlCopyMemory(&NewValueListBlock->Values[0],
-                    &ValueListBlock->Values[0],
-                    sizeof(BLOCK_OFFSET) * KeyBlock->NumberOfValues);
-      CmiDestroyBlock(RegistryFile, ValueListBlock,KeyBlock->ValuesOffset);
-      KeyBlock->ValuesOffset = VLBOffset;
-      ValueListBlock = NewValueListBlock;
+      DbgPrint("CellSize is %d (must not be 0)\n",
+        ValueListCell->CellSize);
+      assert(ValueListCell->CellSize != 0);
     }
-  ValueListBlock->Values[KeyBlock->NumberOfValues] = VBOffset;
-  KeyBlock->NumberOfValues++;
-  CmiReleaseBlock(RegistryFile, ValueListBlock);
-  CmiReleaseBlock(RegistryFile, NewValueBlock);
-  *pValueBlock = NewValueBlock;
 
-  return  STATUS_SUCCESS;
+    }
 }
 
-NTSTATUS  
-CmiDeleteValueFromKey(IN PREGISTRY_FILE  RegistryFile,
-                      IN PKEY_BLOCK  KeyBlock,
-                      IN PCHAR  ValueName)
+
+VOID
+CmiVerifyKeyObject(PKEY_OBJECT KeyObject)
 {
-  ULONG  Idx;
-  PVALUE_LIST_BLOCK  ValueListBlock;
-  PVALUE_BLOCK  CurValueBlock;
-  PHEAP_BLOCK pHeap;
+  if (CmiDoVerify)
+    {
 
-  ValueListBlock = CmiGetBlock(RegistryFile, 
-                               KeyBlock->ValuesOffset,NULL);
-  if (ValueListBlock == 0)
+  if (KeyObject->RegistryHive == NULL)
     {
-      return  STATUS_SUCCESS;
+      DbgPrint("RegistryHive is NULL (must not be NULL)\n",
+        KeyObject->RegistryHive);
+      assert(KeyObject->RegistryHive != NULL);
     }
-  for (Idx = 0; Idx < KeyBlock->NumberOfValues; Idx++)
+
+  if (KeyObject->KeyCell == NULL)
     {
-      CurValueBlock = CmiGetBlock(RegistryFile,
-                                  ValueListBlock->Values[Idx],&pHeap);
-      if (CurValueBlock != NULL &&
-          CurValueBlock->NameSize == strlen(ValueName) &&
-          !memcmp(CurValueBlock->Name, ValueName,strlen(ValueName)))
-        {
-          if (KeyBlock->NumberOfValues - 1 < Idx)
-            {
-              RtlCopyMemory(&ValueListBlock->Values[Idx],
-                            &ValueListBlock->Values[Idx + 1],
-                            sizeof(BLOCK_OFFSET) * 
-                              (KeyBlock->NumberOfValues - 1 - Idx));
-            }
-          else
-            {
-              RtlZeroMemory(&ValueListBlock->Values[Idx],
-                            sizeof(BLOCK_OFFSET));
-            }
-          KeyBlock->NumberOfValues -= 1;
-          CmiDestroyValueBlock(RegistryFile, CurValueBlock, ValueListBlock->Values[Idx]);
-          /* update time of heap */
-          ZwQuerySystemTime((PTIME) &pHeap->DateModified);
-
-          break;
-        }
-      CmiReleaseBlock(RegistryFile, CurValueBlock);
+      DbgPrint("KeyCell is NULL (must not be NULL)\n",
+        KeyObject->KeyCell);
+      assert(KeyObject->KeyCell != NULL);
     }
 
-  CmiReleaseBlock(RegistryFile, ValueListBlock);
-  
-  return  STATUS_SUCCESS;
+  if (KeyObject->ParentKey == NULL)
+    {
+      DbgPrint("ParentKey is NULL (must not be NULL)\n",
+        KeyObject->ParentKey);
+      assert(KeyObject->ParentKey != NULL);
+    }
+
+    }
 }
 
-NTSTATUS
-CmiAllocateHashTableBlock(IN PREGISTRY_FILE  RegistryFile,
-                          OUT PHASH_TABLE_BLOCK  *HashBlock,
-                          OUT BLOCK_OFFSET  *HBOffset,
-                          IN ULONG  HashTableSize)
+
+VOID
+CmiVerifyHiveHeader(PHIVE_HEADER Header)
 {
- NTSTATUS  Status;
- ULONG  NewHashSize;
- PHASH_TABLE_BLOCK  NewHashBlock;
+  if (CmiDoVerify)
+    {
 
-  Status = STATUS_SUCCESS;
-  *HashBlock = NULL;
-  NewHashSize = sizeof(HASH_TABLE_BLOCK) + 
-        (HashTableSize - 1) * sizeof(HASH_RECORD);
-  Status = CmiAllocateBlock(RegistryFile,
-                             (PVOID*)&NewHashBlock,
-                             NewHashSize,HBOffset);
-  if (NewHashBlock == NULL || !NT_SUCCESS(Status) )
-  {
-    Status = STATUS_INSUFFICIENT_RESOURCES;
-  }
-  else
-  {
-    NewHashBlock->SubBlockId = REG_HASH_TABLE_BLOCK_ID;
-    NewHashBlock->HashTableSize = HashTableSize;
-    *HashBlock = NewHashBlock;
-  }
+  if (Header->BlockId != REG_HIVE_ID)
+    {
+      DbgPrint("BlockId is %.08x (must be %.08x)\n",
+        Header->BlockId,
+        REG_HIVE_ID);
+      assert(Header->BlockId == REG_HIVE_ID);
+    }
 
-  return  Status;
-}
+  if (Header->Unused3 != 1)
+    {
+      DbgPrint("Unused3 is %.08x (must be 1)\n",
+        Header->Unused3);
+      assert(Header->Unused3 == 1);
+    }
 
-PKEY_BLOCK  
-CmiGetKeyFromHashByIndex(PREGISTRY_FILE RegistryFile,
-                         PHASH_TABLE_BLOCK  HashBlock,
-                         ULONG  Index)
-{
-  PKEY_BLOCK  KeyBlock;
-  BLOCK_OFFSET KeyOffset;
+  if (Header->Unused4 != 3)
+    {
+      DbgPrint("Unused4 is %.08x (must be 3)\n",
+        Header->Unused4);
+      assert(Header->Unused4 == 3);
+    }
 
-  if( HashBlock == NULL)
-    return NULL;
-  if (RegistryFile->Filename == NULL)
+  if (Header->Unused5 != 0)
     {
-      KeyBlock = (PKEY_BLOCK) HashBlock->Table[Index].KeyOffset;
+      DbgPrint("Unused5 is %.08x (must be 0)\n",
+        Header->Unused5);
+      assert(Header->Unused5 == 0);
     }
-  else
+
+  if (Header->Unused6 != 1)
     {
-      KeyOffset =  HashBlock->Table[Index].KeyOffset;
-      KeyBlock = CmiGetBlock(RegistryFile,KeyOffset,NULL);
+      DbgPrint("Unused6 is %.08x (must be 1)\n",
+        Header->Unused6);
+      assert(Header->Unused6 == 1);
+    }
+
+  if (Header->Unused7 != 1)
+    {
+      DbgPrint("Unused7 is %.08x (must be 1)\n",
+        Header->Unused7);
+      assert(Header->Unused7 == 1);
     }
-  CmiLockBlock(RegistryFile, KeyBlock);
 
-  return  KeyBlock;
+    }
 }
 
-NTSTATUS  
-CmiAddKeyToHashTable(PREGISTRY_FILE  RegistryFile,
-                     PHASH_TABLE_BLOCK  HashBlock,
-                     PKEY_BLOCK  NewKeyBlock,
-                     BLOCK_OFFSET  NKBOffset)
-{
-  ULONG i;
 
-  for (i = 0; i < HashBlock->HashTableSize; i++)
+VOID
+CmiVerifyRegistryHive(PREGISTRY_HIVE RegistryHive)
+{
+  if (CmiDoVerify)
     {
-       if (HashBlock->Table[i].KeyOffset == 0)
-         {
-            HashBlock->Table[i].KeyOffset = NKBOffset;
-            RtlCopyMemory(&HashBlock->Table[i].HashValue,
-                          NewKeyBlock->Name,
-                          4);
-            return STATUS_SUCCESS;
-         }
+
+      CmiVerifyHiveHeader(RegistryHive->HiveHeader);
+
     }
-  return STATUS_UNSUCCESSFUL;
 }
 
-NTSTATUS
-CmiAllocateValueBlock(PREGISTRY_FILE  RegistryFile,
-                      PVALUE_BLOCK  *ValueBlock,
-                      BLOCK_OFFSET  *VBOffset,
-                      IN PCHAR  ValueNameBuf)
+
+static NTSTATUS
+CmiCreateNewRegFile(HANDLE FileHandle)
 {
-  NTSTATUS  Status;
-  ULONG  NewValueSize;
-  PVALUE_BLOCK  NewValueBlock;
+  IO_STATUS_BLOCK IoStatusBlock;
+  PCELL_HEADER FreeCell;
+  PHIVE_HEADER HiveHeader;
+  PKEY_CELL RootKeyCell;
+  NTSTATUS Status;
+  PHBIN BinHeader;
+  PCHAR Buffer;
 
-  Status = STATUS_SUCCESS;
+  Buffer = (PCHAR) ExAllocatePool(NonPagedPool, 2 * REG_BLOCK_SIZE);
+  if (Buffer == NULL)
+    return STATUS_INSUFFICIENT_RESOURCES;
 
-      NewValueSize = sizeof(VALUE_BLOCK) + strlen(ValueNameBuf);
-      Status = CmiAllocateBlock(RegistryFile,
-                                    (PVOID*)&NewValueBlock,
-                                    NewValueSize,VBOffset);
-      if (NewValueBlock == NULL || !NT_SUCCESS(Status))
-        {
-          Status = STATUS_INSUFFICIENT_RESOURCES;
-        }
-      else
-        {
-          NewValueBlock->SubBlockId = REG_VALUE_BLOCK_ID;
-          NewValueBlock->NameSize = strlen(ValueNameBuf);
-          memcpy(NewValueBlock->Name, ValueNameBuf,strlen(ValueNameBuf));
-          NewValueBlock->DataType = 0;
-          NewValueBlock->DataSize = 0;
-          NewValueBlock->DataOffset = 0xffffffff;
-         *ValueBlock = NewValueBlock;
-        }
+  RtlZeroMemory (Buffer,
+                2 * REG_BLOCK_SIZE);
 
-  return  Status;
-}
+  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));
 
-NTSTATUS
-CmiDestroyValueBlock(PREGISTRY_FILE  RegistryFile,
-                     PVALUE_BLOCK  ValueBlock, BLOCK_OFFSET VBOffset)
-{
- NTSTATUS  Status;
- PHEAP_BLOCK pHeap;
- PVOID pBlock;
+  CmiCreateDefaultHiveHeader(HiveHeader);
+  CmiCreateDefaultBinHeader(BinHeader);
+  CmiCreateDefaultRootKeyCell(RootKeyCell);
+
+  /* First block */
+  BinHeader->BinOffset = 0;
 
-  /* first, release datas : */
-  if (ValueBlock->DataSize >0)
-  {
-    pBlock = CmiGetBlock(RegistryFile, ValueBlock->DataOffset,&pHeap);
-    Status = CmiDestroyBlock(RegistryFile, pBlock, ValueBlock->DataOffset);
-    if (!NT_SUCCESS(Status))
+  /* Offset to root key block */
+  HiveHeader->RootKeyOffset = REG_HBIN_DATA_OFFSET;
+
+  /* The rest of the block is free */
+  FreeCell->CellSize = REG_BLOCK_SIZE - (REG_HBIN_DATA_OFFSET + sizeof(KEY_CELL));
+
+  Status = NtWriteFile(FileHandle,
+                      NULL,
+                      NULL,
+                      NULL,
+                      &IoStatusBlock,
+                      Buffer,
+                      2 * REG_BLOCK_SIZE,
+                      0,
+                      NULL);
+
+  ExFreePool(Buffer);
+
+  assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
+
+  if (!NT_SUCCESS(Status))
     {
-      return  Status;
+      return(Status);
     }
-    /* update time of heap */
-    if(RegistryFile->Filename)
-      ZwQuerySystemTime((PTIME) &pHeap->DateModified);
-  }
 
-  Status = CmiDestroyBlock(RegistryFile, ValueBlock, VBOffset);
-  /* update time of heap */
-  if(RegistryFile->Filename && CmiGetBlock(RegistryFile, VBOffset,&pHeap))
-    ZwQuerySystemTime((PTIME) &pHeap->DateModified);
-  return  Status;
-}
+  Status = NtFlushBuffersFile(FileHandle,
+                             &IoStatusBlock);
 
-NTSTATUS
-CmiAddHeap(PREGISTRY_FILE  RegistryFile,PVOID *NewBlock,BLOCK_OFFSET *NewBlockOffset)
-{
- PHEAP_BLOCK tmpHeap;
- PHEAP_BLOCK * tmpBlockList;
- PFREE_SUB_BLOCK tmpBlock;
-  tmpHeap=ExAllocatePool(PagedPool, REG_BLOCK_SIZE);
-  tmpHeap->BlockId = REG_HEAP_ID;
-  tmpHeap->BlockOffset = RegistryFile->FileSize - REG_BLOCK_SIZE;
-  RegistryFile->FileSize += REG_BLOCK_SIZE;
-  tmpHeap->BlockSize = REG_BLOCK_SIZE;
-  tmpHeap->Unused1 = 0;
-  ZwQuerySystemTime((PTIME) &tmpHeap->DateModified);
-  tmpHeap->Unused2 = 0;
-  /* increase size of list of blocks */
-  tmpBlockList=ExAllocatePool(NonPagedPool,
-          sizeof(PHEAP_BLOCK *) * (RegistryFile->BlockListSize +1));
-  if (tmpBlockList == NULL)
-  {
-    KeBugCheck(0);
-    return(STATUS_INSUFFICIENT_RESOURCES);
-  }
-  if(RegistryFile->BlockListSize > 0)
-  {
-    memcpy(tmpBlockList,RegistryFile->BlockList,
-       sizeof(PHEAP_BLOCK *)*(RegistryFile->BlockListSize ));
-    ExFreePool(RegistryFile->BlockList);
-  }
-  RegistryFile->BlockList = tmpBlockList;
-  RegistryFile->BlockList [RegistryFile->BlockListSize++] = tmpHeap;
-  /* initialize a free block in this heap : */
-  tmpBlock = (PFREE_SUB_BLOCK)((char *) tmpHeap + REG_HEAP_BLOCK_DATA_OFFSET);
-  tmpBlock-> SubBlockSize =  (REG_BLOCK_SIZE - REG_HEAP_BLOCK_DATA_OFFSET) ;
-  *NewBlock = (PVOID)tmpBlock;
-  if (NewBlockOffset)
-    *NewBlockOffset = tmpHeap->BlockOffset + REG_HEAP_BLOCK_DATA_OFFSET;
-  /* FIXME : set first dword to block_offset of another free bloc */
-  return STATUS_SUCCESS;
+  return(Status);
 }
 
-NTSTATUS
-CmiAllocateBlock(PREGISTRY_FILE  RegistryFile,
-                 PVOID  *Block,
-                 LONG  BlockSize,
-                BLOCK_OFFSET * pBlockOffset)
+
+#ifdef HIVE_CHECK
+static NTSTATUS
+CmiCheckAndFixHive(PREGISTRY_HIVE RegistryHive)
 {
- NTSTATUS  Status;
- PFREE_SUB_BLOCK NewBlock;
- PHEAP_BLOCK pHeap;
+  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;
 
-  Status = STATUS_SUCCESS;
-  /* round to 16 bytes  multiple */
-  BlockSize = (BlockSize + sizeof(DWORD) + 15) & 0xfffffff0;
+  DPRINT("CmiCheckAndFixHive() called\n");
+
+  /* Try to open the hive file */
+  InitializeObjectAttributes(&ObjectAttributes,
+                            &RegistryHive->HiveFileName,
+                            OBJ_CASE_INSENSITIVE,
+                            NULL,
+                            NULL);
 
-  /*  Handle volatile files first  */
-  if (RegistryFile->Filename == NULL)
-  {
-    NewBlock = ExAllocatePool(NonPagedPool, BlockSize);
-    if (NewBlock == 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)
     {
-      Status = STATUS_INSUFFICIENT_RESOURCES;
+      return(STATUS_SUCCESS);
     }
-    else
+  if (!NT_SUCCESS(Status))
     {
-      RtlZeroMemory(NewBlock, BlockSize);
-      NewBlock->SubBlockSize = BlockSize;
-      CmiLockBlock(RegistryFile, NewBlock);
-      *Block = NewBlock;
-      if (pBlockOffset) *pBlockOffset = (BLOCK_OFFSET)NewBlock;
+      DPRINT("NtCreateFile() failed (Status %lx)\n", Status);
+      return(Status);
     }
-  }
-  else
-  {
-   int i;
-    /* first search in free blocks */
-    NewBlock = NULL;
-    for (i=0 ; i<RegistryFile->FreeListSize ; i++)
-    {
-      if(RegistryFile->FreeList[i]->SubBlockSize >=BlockSize)
-      {
-         NewBlock = RegistryFile->FreeList[i];
-         if(pBlockOffset)
-          *pBlockOffset = RegistryFile->FreeListOffset[i];
-         /* update time of heap */
-         if(RegistryFile->Filename
-           && CmiGetBlock(RegistryFile, RegistryFile->FreeListOffset[i],&pHeap))
-           ZwQuerySystemTime((PTIME) &pHeap->DateModified);
-        if( (i+1) <RegistryFile->FreeListSize)
-        {
-           memmove( &RegistryFile->FreeList[i]
-              ,&RegistryFile->FreeList[i+1]
-              ,sizeof(RegistryFile->FreeList[0])
-               *(RegistryFile->FreeListSize-i-1));
-           memmove( &RegistryFile->FreeListOffset[i]
-              ,&RegistryFile->FreeListOffset[i+1]
-              ,sizeof(RegistryFile->FreeListOffset[0])
-               *(RegistryFile->FreeListSize-i-1));
-        }
-         RegistryFile->FreeListSize--;
-         break;
-      }
-    }
-    /* need to extend hive file : */
-    if (NewBlock == NULL)
-    {
-      /*  add a new block : */
-      Status = CmiAddHeap(RegistryFile, (PVOID *)&NewBlock , pBlockOffset);
-    }
-    if (NT_SUCCESS(Status))
-    {
-      *Block = NewBlock;
-      /* split the block in two parts */
-      if(NewBlock->SubBlockSize > BlockSize)
-      {
-       NewBlock = (PFREE_SUB_BLOCK)((char *)NewBlock+BlockSize);
-       NewBlock->SubBlockSize=((PFREE_SUB_BLOCK) (*Block))->SubBlockSize-BlockSize;
-       CmiAddFree(RegistryFile,NewBlock,*pBlockOffset+BlockSize);
-      }
-      else if(NewBlock->SubBlockSize < BlockSize)
-       return STATUS_UNSUCCESSFUL;
-      RtlZeroMemory(*Block, BlockSize);
-      ((PFREE_SUB_BLOCK)(*Block)) ->SubBlockSize = - BlockSize;
-      CmiLockBlock(RegistryFile, *Block);
-    }
-  }
-  return  Status;
-}
-
-NTSTATUS
-CmiDestroyBlock(PREGISTRY_FILE  RegistryFile,
-                PVOID  Block,BLOCK_OFFSET Offset)
-{
- NTSTATUS  Status;
- PHEAP_BLOCK pHeap;
-
-  Status = STATUS_SUCCESS;
-
-  if (RegistryFile->Filename == NULL)
-  {
-    CmiReleaseBlock(RegistryFile, Block);
-    ExFreePool(Block);
-  }
-  else
-  {  
-   PFREE_SUB_BLOCK pFree = Block;
-    if (pFree->SubBlockSize <0)
-      pFree->SubBlockSize = -pFree->SubBlockSize;
-    CmiAddFree(RegistryFile,Block,Offset);
-    CmiReleaseBlock(RegistryFile, Block);
-    /* update time of heap */
-    if(RegistryFile->Filename && CmiGetBlock(RegistryFile, Offset,&pHeap))
-      ZwQuerySystemTime((PTIME) &pHeap->DateModified);
-      /* FIXME : set first dword to block_offset of another free bloc ? */
-      /* FIXME : concatenate with previous and next block if free */
-  }
-
-  return  Status;
-}
 
-NTSTATUS
-CmiAddFree(PREGISTRY_FILE  RegistryFile,
-               PFREE_SUB_BLOCK FreeBlock,BLOCK_OFFSET FreeOffset)
-{
- PFREE_SUB_BLOCK *tmpList;
- BLOCK_OFFSET *tmpListOffset;
- int minInd,maxInd,medInd;
-  if( (RegistryFile->FreeListSize+1) > RegistryFile->FreeListMax)
-  {
-    tmpList=ExAllocatePool(PagedPool
-               ,sizeof(PFREE_SUB_BLOCK)*(RegistryFile->FreeListMax+32));
-    if (tmpList == NULL)
-       return STATUS_INSUFFICIENT_RESOURCES;
-    tmpListOffset=ExAllocatePool(PagedPool
-               ,sizeof(BLOCK_OFFSET *)*(RegistryFile->FreeListMax+32));
-    if (tmpListOffset == NULL)
-       return STATUS_INSUFFICIENT_RESOURCES;
-    if (RegistryFile->FreeListMax)
-    {
-       memcpy(tmpList,RegistryFile->FreeList
-               ,sizeof(PFREE_SUB_BLOCK)*(RegistryFile->FreeListMax));
-       memcpy(tmpListOffset,RegistryFile->FreeListOffset
-               ,sizeof(BLOCK_OFFSET *)*(RegistryFile->FreeListMax));
-       ExFreePool(RegistryFile->FreeList);
-       ExFreePool(RegistryFile->FreeListOffset);
-    }
-    RegistryFile->FreeList = tmpList;
-    RegistryFile->FreeListOffset = tmpListOffset;
-    RegistryFile->FreeListMax +=32;
-  }
-  /* add new offset to free list, maintening list in ascending order */
-  if (  RegistryFile->FreeListSize==0
-     || RegistryFile->FreeListOffset[RegistryFile->FreeListSize-1] < FreeOffset)
-  {
-    /* add to end of list : */
-    RegistryFile->FreeList[RegistryFile->FreeListSize] = FreeBlock;
-    RegistryFile->FreeListOffset[RegistryFile->FreeListSize ++] = FreeOffset;
-  }
-  else if (RegistryFile->FreeListOffset[0] > FreeOffset)
-  {
-    /* add to begin of list : */
-    memmove( &RegistryFile->FreeList[1],&RegistryFile->FreeList[0]
-          ,sizeof(RegistryFile->FreeList[0])*RegistryFile->FreeListSize);
-    memmove( &RegistryFile->FreeListOffset[1],&RegistryFile->FreeListOffset[0]
-          ,sizeof(RegistryFile->FreeListOffset[0])*RegistryFile->FreeListSize);
-    RegistryFile->FreeList[0] = FreeBlock;
-    RegistryFile->FreeListOffset[0] = FreeOffset;
-    RegistryFile->FreeListSize ++;
-  }
-  else
-  {
-    /* search where to insert : */
-    minInd=0;
-    maxInd=RegistryFile->FreeListSize-1;
-    while( (maxInd-minInd) >1)
-    {
-      medInd=(minInd+maxInd)/2;
-      if (RegistryFile->FreeListOffset[medInd] > FreeOffset)
-        maxInd=medInd;
-      else
-        minInd=medInd;
-    }
-    /* insert before maxInd : */
-    memmove( &RegistryFile->FreeList[maxInd+1],&RegistryFile->FreeList[maxInd]
-          ,sizeof(RegistryFile->FreeList[0])
-               *(RegistryFile->FreeListSize-minInd));
-    memmove(  &RegistryFile->FreeListOffset[maxInd+1]
-           , &RegistryFile->FreeListOffset[maxInd]
-           , sizeof(RegistryFile->FreeListOffset[0])
-               *(RegistryFile->FreeListSize-minInd));
-    RegistryFile->FreeList[maxInd] = FreeBlock;
-    RegistryFile->FreeListOffset[maxInd] = FreeOffset;
-    RegistryFile->FreeListSize ++;
-  }
-  return STATUS_SUCCESS;
-}
+  /* Try to open the log file */
+  InitializeObjectAttributes(&ObjectAttributes,
+                            &RegistryHive->LogFileName,
+                            OBJ_CASE_INSENSITIVE,
+                            NULL,
+                            NULL);
 
-PVOID
-CmiGetBlock(PREGISTRY_FILE  RegistryFile,
-            BLOCK_OFFSET  BlockOffset,
-           OUT PHEAP_BLOCK * ppHeap)
-{
-  if( BlockOffset == 0 || BlockOffset == -1) return 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);
+    }
 
-  if (RegistryFile->Filename == NULL)
-  {
-      return (PVOID)BlockOffset;
-  }
-  else
-  {
-   PHEAP_BLOCK pHeap;
-    pHeap = RegistryFile->BlockList[BlockOffset/4096];
-    if(ppHeap) *ppHeap = pHeap;
-    return ((char *)pHeap
-       +(BlockOffset - pHeap->BlockOffset));
-  }
-}
+  /* Allocate hive header */
+  HiveHeader = ExAllocatePool(PagedPool,
+                             sizeof(HIVE_HEADER));
+  if (HiveHeader == NULL)
+    {
+      DPRINT("ExAllocatePool() failed\n");
+      Status = STATUS_INSUFFICIENT_RESOURCES;
+      goto ByeBye;
+    }
 
-void 
-CmiLockBlock(PREGISTRY_FILE  RegistryFile,
-             PVOID  Block)
-{
-  if (RegistryFile->Filename != NULL)
+  /* Read hive base block */
+  FileOffset.QuadPart = 0ULL;
+  Status = NtReadFile(HiveHandle,
+                     0,
+                     0,
+                     0,
+                     &IoStatusBlock,
+                     HiveHeader,
+                     sizeof(HIVE_HEADER),
+                     &FileOffset,
+                     0);
+  if (!NT_SUCCESS(Status))
     {
-      /* FIXME : implement */
+      DPRINT("NtReadFile() failed (Status %lx)\n", Status);
+      goto ByeBye;
     }
-}
 
-void 
-CmiReleaseBlock(PREGISTRY_FILE  RegistryFile,
-               PVOID  Block)
-{
-  if (RegistryFile->Filename != NULL)
+  if (LogHandle == INVALID_HANDLE_VALUE)
     {
-      /* FIXME : implement */
+      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 / 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;
+       }
+
+      /* 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_PTR)LogHeader + REG_BLOCK_SIZE + 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
+
+
+NTSTATUS
+CmiImportHiveBins(PREGISTRY_HIVE Hive,
+                 PUCHAR ChunkPtr)
+{
+  BLOCK_OFFSET BlockOffset;
+  ULONG BlockIndex;
+  PHBIN Bin;
+  ULONG j;
+
+  BlockIndex = 0;
+  BlockOffset = 0;
+  while (BlockIndex < Hive->BlockListSize)
+    {
+      Bin = (PHBIN)((ULONG_PTR)ChunkPtr + BlockOffset);
+
+      if (Bin->HeaderId != REG_BIN_ID)
+       {
+         DPRINT1 ("Bad bin header id %x, offset %x\n", Bin->HeaderId, BlockOffset);
+         return STATUS_REGISTRY_CORRUPT;
+       }
+
+      assertmsg((Bin->BinSize % REG_BLOCK_SIZE) == 0,
+               ("Bin size (0x%.08x) must be multiple of 4K\n",
+               Bin->BinSize));
+
+      /* Allocate the hive block */
+      Hive->BlockList[BlockIndex].Bin = ExAllocatePool (PagedPool,
+                                                       Bin->BinSize);
+      if (Hive->BlockList[BlockIndex].Bin == NULL)
+       {
+         DPRINT1 ("ExAllocatePool() failed\n");
+         return STATUS_INSUFFICIENT_RESOURCES;
+       }
+      Hive->BlockList[BlockIndex].Block = (PVOID)Hive->BlockList[BlockIndex].Bin;
+
+      /* Import the Bin */
+      RtlCopyMemory (Hive->BlockList[BlockIndex].Bin,
+                    Bin,
+                    Bin->BinSize);
+
+      if (Bin->BinSize > REG_BLOCK_SIZE)
+       {
+         for (j = 1; j < Bin->BinSize / REG_BLOCK_SIZE; j++)
+           {
+             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));
+           }
+       }
+
+      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 < Bin->BinSize)
+       {
+         FreeBlock = (PCELL_HEADER) ((ULONG_PTR) Bin + FreeOffset);
+         if (FreeBlock->CellSize > 0)
+           {
+             Status = CmiAddFree(Hive,
+                                 FreeBlock,
+                                 Bin->BinOffset + FreeOffset,
+                                 FALSE);
+
+             if (!NT_SUCCESS(Status))
+               {
+                 return Status;
+               }
+
+             FreeOffset += FreeBlock->CellSize;
+           }
+         else
+           {
+             FreeOffset -= FreeBlock->CellSize;
+           }
+       }
+
+      BlockIndex += Bin->BinSize / REG_BLOCK_SIZE;
+      BlockOffset += Bin->BinSize;
+    }
+
+  return STATUS_SUCCESS;
+}
+
+
+VOID
+CmiFreeHiveFreeCellList(PREGISTRY_HIVE Hive)
+{
+  ExFreePool (Hive->FreeList);
+  ExFreePool (Hive->FreeListOffset);
+
+  Hive->FreeListSize = 0;
+  Hive->FreeListMax = 0;
+  Hive->FreeList = NULL;
+  Hive->FreeListOffset = NULL;
+}
+
+
+NTSTATUS
+CmiCreateHiveBitmap(PREGISTRY_HIVE Hive)
+{
+  ULONG BitmapSize;
+
+  /* Calculate bitmap size in bytes (always a multiple of 32 bits) */
+  BitmapSize = ROUND_UP(Hive->BlockListSize, sizeof(ULONG) * 8) / 8;
+  DPRINT("Hive->BlockListSize: %lu\n", Hive->BlockListSize);
+  DPRINT("BitmapSize:  %lu Bytes  %lu Bits\n", BitmapSize, BitmapSize * 8);
+
+  /* Allocate bitmap */
+  Hive->BitmapBuffer = (PULONG)ExAllocatePool(PagedPool,
+                                             BitmapSize);
+  if (Hive->BitmapBuffer == NULL)
+    {
+      return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+  RtlInitializeBitMap(&Hive->DirtyBitMap,
+                     Hive->BitmapBuffer,
+                     BitmapSize * 8);
+
+  /* Initialize bitmap */
+  RtlClearAllBits(&Hive->DirtyBitMap);
+  Hive->HiveDirty = FALSE;
+
+  return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS
+CmiInitNonVolatileRegistryHive (PREGISTRY_HIVE RegistryHive,
+                               PWSTR Filename)
+{
+  OBJECT_ATTRIBUTES ObjectAttributes;
+  ULONG CreateDisposition;
+  IO_STATUS_BLOCK IoSB;
+  HANDLE FileHandle;
+  PSECTION_OBJECT SectionObject;
+  PUCHAR ViewBase;
+  ULONG ViewSize;
+  NTSTATUS Status;
+
+  DPRINT("CmiInitNonVolatileRegistryHive(%p, %S) called\n",
+        RegistryHive, Filename);
+
+  /* Duplicate Filename */
+  Status = RtlCreateUnicodeString(&RegistryHive->HiveFileName,
+                                 Filename);
+  if (!NT_SUCCESS(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->HiveFileName,
+                            OBJ_CASE_INSENSITIVE,
+                            NULL,
+                            NULL);
+
+  CreateDisposition = FILE_OPEN_IF;
+  Status = NtCreateFile(&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))
+    {
+      RtlFreeUnicodeString(&RegistryHive->HiveFileName);
+      RtlFreeUnicodeString(&RegistryHive->LogFileName);
+      DPRINT("NtCreateFile() failed (Status %lx)\n", Status);
+      return(Status);
+    }
+
+  if (IoSB.Information != FILE_OPENED)
+    {
+      Status = CmiCreateNewRegFile(FileHandle);
+      if (!NT_SUCCESS(Status))
+       {
+         DPRINT("CmiCreateNewRegFile() failed (Status %lx)\n", Status);
+         NtClose(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);
+    }
+
+  /* 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);
+      NtClose(FileHandle);
+      return(Status);
+    }
+  DPRINT("ViewBase %p  ViewSize %lx\n", ViewBase, ViewSize);
+
+  /* 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;
+
+  /* Allocate hive block list */
+  RegistryHive->BlockList = ExAllocatePool(NonPagedPool,
+                                          RegistryHive->BlockListSize * sizeof(BLOCK_LIST_ENTRY));
+  if (RegistryHive->BlockList == NULL)
+    {
+      DPRINT1("Failed to allocate the hive block list\n");
+      MmUnmapViewOfSection(PsGetCurrentProcess(),
+                          ViewBase);
+      ObDereferenceObject(SectionObject);
+      RtlFreeUnicodeString(&RegistryHive->HiveFileName);
+      RtlFreeUnicodeString(&RegistryHive->LogFileName);
+      NtClose(FileHandle);
+      return STATUS_INSUFFICIENT_RESOURCES;
+    }
+  RtlZeroMemory (RegistryHive->BlockList,
+                RegistryHive->BlockListSize * sizeof(BLOCK_LIST_ENTRY));
+
+  /* Import the hive bins */
+  Status = CmiImportHiveBins (RegistryHive,
+                             ViewBase + REG_BLOCK_SIZE);
+  if (!NT_SUCCESS(Status))
+    {
+      ExFreePool(RegistryHive->BlockList);
+      MmUnmapViewOfSection(PsGetCurrentProcess(),
+                          ViewBase);
+      ObDereferenceObject(SectionObject);
+      RtlFreeUnicodeString(&RegistryHive->HiveFileName);
+      RtlFreeUnicodeString(&RegistryHive->LogFileName);
+      NtClose(FileHandle);
+      return Status;
+    }
+
+  /* Unmap and dereference the hive section */
+  MmUnmapViewOfSection(PsGetCurrentProcess(),
+                       ViewBase);
+  ObDereferenceObject(SectionObject);
+
+  /* Close the hive file */
+  NtClose(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 %x\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;
+
+  ExInitializeResourceLite (&Hive->HiveResource);
+
+  /* Acquire hive list lock exclusively */
+  KeEnterCriticalRegion();
+  ExAcquireResourceExclusiveLite (&CmiHiveListLock,
+                                 TRUE);
+
+  /* Add the new hive to the hive list */
+  InsertTailList (&CmiHiveListHead,
+                 &Hive->HiveList);
+
+  /* Release hive list lock */
+  ExReleaseResourceLite (&CmiHiveListLock);
+  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 %x\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 %x\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;
+    }
+
+
+  ExInitializeResourceLite (&Hive->HiveResource);
+
+  /* Acquire hive list lock exclusively */
+  KeEnterCriticalRegion();
+  ExAcquireResourceExclusiveLite (&CmiHiveListLock,
+                                 TRUE);
+
+  /* Add the new hive to the hive list */
+  InsertTailList (&CmiHiveListHead,
+                 &Hive->HiveList);
+
+  /* Release hive list lock */
+  ExReleaseResourceLite (&CmiHiveListLock);
+  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 %x\n", Hive);
+  Hive->Flags = (Flags & REG_NO_LAZY_FLUSH) ? HIVE_NO_SYNCH : 0;
+
+  Hive->HiveHeader = (PHIVE_HEADER)ExAllocatePool(NonPagedPool,
+                                                 sizeof(HIVE_HEADER));
+  if (Hive->HiveHeader == NULL)
+    {
+      DPRINT1 ("Failed to allocate hive header.\n");
+      ExFreePool (Hive);
+      return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+  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;
+    }
+
+  ExInitializeResourceLite (&Hive->HiveResource);
+
+  /* Add the new hive to the hive list */
+  KeEnterCriticalRegion();
+  ExAcquireResourceExclusiveLite (&CmiHiveListLock,
+                                 TRUE);
+  InsertTailList (&CmiHiveListHead,
+                 &Hive->HiveList);
+  ExReleaseResourceLite (&CmiHiveListLock);
+  KeLeaveCriticalRegion();
+
+
+  VERIFY_REGISTRY_HIVE(Hive);
+
+
+  Status = CmiConnectHive (KeyObjectAttributes,
+                          Hive);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT1 ("CmiConnectHive() failed (Status %lx)\n", Status);
+//      CmiRemoveRegistryHive (Hive);
+    }
+
+  DPRINT ("CmiLoadHive() done\n");
+
+  return Status;
+}
+
+
+NTSTATUS
+CmiRemoveRegistryHive(PREGISTRY_HIVE RegistryHive)
+{
+  if (RegistryHive->Flags & HIVE_POINTER)
+    return STATUS_UNSUCCESSFUL;
+
+  /* Acquire hive list lock exclusively */
+  KeEnterCriticalRegion();
+  ExAcquireResourceExclusiveLite (&CmiHiveListLock,
+                                 TRUE);
+
+  /* Remove hive from hive list */
+  RemoveEntryList (&RegistryHive->HiveList);
+
+  /* Release hive list lock */
+  ExReleaseResourceLite (&CmiHiveListLock);
+  KeLeaveCriticalRegion();
+
+  /* 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 hive resource */
+  ExDeleteResource (&RegistryHive->HiveResource);
+
+  /* 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;
+  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 = NtCreateFile(&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("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 = (ULONGLONG)0;
+  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 = 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].Block;
+      DPRINT("BlockPtr %p\n", BlockPtr);
+      DPRINT("File offset %I64x\n", FileOffset.QuadPart);
+
+      /* Write hive block */
+      Status = NtWriteFile(FileHandle,
+                          NULL,
+                          NULL,
+                          NULL,
+                          &IoStatusBlock,
+                          BlockPtr,
+                          REG_BLOCK_SIZE,
+                          &FileOffset,
+                          NULL);
+      if (!NT_SUCCESS(Status))
+       {
+         DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
+         NtClose(FileHandle);
+         return(Status);
+       }
+
+      BlockIndex++;
+      FileOffset.QuadPart += (ULONGLONG)REG_BLOCK_SIZE;
+    }
+
+  /* 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);
+    }
+
+  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);
+    }
+
+  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, 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 = 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 = (ULONGLONG)0;
+  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, 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 = 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,
+                            OBJ_CASE_INSENSITIVE,
+                            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 = (ULONGLONG)0;
+  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].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 = 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,
+                            OBJ_CASE_INSENSITIVE,
+                            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 = (ULONGLONG)0;
+  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(&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;
+      NtQuerySystemTime(&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++;
+    }
+
+  NtQuerySystemTime (&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 > 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;
+           }
+       }
+
+      NtQuerySystemTime(&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;
+       }
+
+      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)
+{
+  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->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;
+
+  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 > 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;
+       }
+
+      /* Update time of heap */
+      if (!IsNoFileHive(RegistryHive))
+       NtQuerySystemTime(&Bin->DateModified);
+    }
+
+  /* Destroy the value cell */
+  Status = CmiDestroyCell (RegistryHive, ValueCell, ValueCellOffset);
+
+  /* Update time of heap */
+  if (!IsNoFileHive(RegistryHive) && CmiGetCell (RegistryHive, ValueCellOffset, &Bin))
+    {
+      NtQuerySystemTime(&Bin->DateModified);
+    }
+
+  return Status;
+}
+
+
+NTSTATUS
+CmiAddBin(PREGISTRY_HIVE RegistryHive,
+         ULONG BlockCount,
+         PVOID *NewBlock,
+         BLOCK_OFFSET *NewBlockOffset)
+{
+  PBLOCK_LIST_ENTRY BlockList;
+  PCELL_HEADER tmpBlock;
+  PHBIN tmpBin;
+  ULONG BinSize;
+  ULONG i;
+
+  DPRINT ("CmiAddBin (BlockCount %lu)\n", BlockCount);
+
+  BinSize = BlockCount * REG_BLOCK_SIZE;
+  tmpBin = ExAllocatePool(PagedPool, BinSize);
+  if (tmpBin == NULL)
+    {
+      return STATUS_INSUFFICIENT_RESOURCES;
+    }
+  RtlZeroMemory (tmpBin,
+                BinSize);
+
+  tmpBin->HeaderId = REG_BIN_ID;
+  tmpBin->BinOffset = RegistryHive->FileSize - REG_BLOCK_SIZE;
+  RegistryHive->FileSize += BinSize;
+  tmpBin->BinSize = BinSize;
+  tmpBin->Unused1 = 0;
+  NtQuerySystemTime(&tmpBin->DateModified);
+  tmpBin->Unused2 = 0;
+
+  DPRINT ("  BinOffset %lx  BinSize %lx\n", tmpBin->BinOffset,tmpBin->BinSize);
+
+  /* Allocate new block list */
+  BlockList = ExAllocatePool(NonPagedPool,
+                            sizeof(BLOCK_LIST_ENTRY) * (RegistryHive->BlockListSize + BlockCount));
+  if (BlockList == NULL)
+    {
+      ExFreePool(tmpBin);
+      return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+  if (RegistryHive->BlockListSize > 0)
+    {
+      RtlCopyMemory (BlockList,
+                    RegistryHive->BlockList,
+                    sizeof(BLOCK_LIST_ENTRY) * RegistryHive->BlockListSize);
+      ExFreePool(RegistryHive->BlockList);
+    }
+
+  RegistryHive->BlockList = BlockList;
+  for (i = 0; i < BlockCount; i++)
+    {
+      RegistryHive->BlockList[RegistryHive->BlockListSize + i].Block =
+       (PVOID)((ULONG_PTR)tmpBin + (i * REG_BLOCK_SIZE));
+      RegistryHive->BlockList[RegistryHive->BlockListSize + i].Bin = tmpBin;
+    }
+  RegistryHive->BlockListSize += BlockCount;
+
+  /* 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))
+    {
+      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);
+    }
+
+  *NewBlock = (PVOID) tmpBlock;
+
+  if (NewBlockOffset)
+    *NewBlockOffset = tmpBin->BinOffset + REG_HBIN_DATA_OFFSET;
+
+  /* Mark new bin dirty */
+  CmiMarkBinDirty(RegistryHive,
+                 tmpBin->BinOffset);
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+CmiAllocateCell (PREGISTRY_HIVE RegistryHive,
+                LONG CellSize,
+                PVOID *Cell,
+                BLOCK_OFFSET *CellOffset)
+{
+  PCELL_HEADER NewCell;
+  PHBIN Bin;
+  ULONG i;
+  PVOID Temp;
+  NTSTATUS Status;
+
+  /* Round to 16 bytes multiple */
+  CellSize = ROUND_UP(CellSize, 16);
+
+  /* Handle volatile hives first */
+  if (IsPointerHive(RegistryHive))
+    {
+      NewCell = ExAllocatePool(NonPagedPool, CellSize);
+      if (NewCell == NULL)
+       {
+         return STATUS_INSUFFICIENT_RESOURCES;
+       }
+
+      RtlZeroMemory (NewCell,
+                    CellSize);
+      NewCell->CellSize = -CellSize;
+
+      *Cell = NewCell;
+      if (CellOffset != NULL)
+       *CellOffset = (BLOCK_OFFSET) NewCell;
+    }
+  else
+    {
+      /* 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;
+               }
+
+             NtQuerySystemTime(&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;
+    }
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+CmiDestroyCell (PREGISTRY_HIVE RegistryHive,
+               PVOID Cell,
+               BLOCK_OFFSET CellOffset)
+{
+  NTSTATUS Status;
+  PHBIN pBin;
+
+  Status = STATUS_SUCCESS;
+
+  if (IsPointerHive(RegistryHive))
+    {
+      ExFreePool(Cell);
+    }
+  else
+    {
+      PCELL_HEADER pFree = Cell;
+
+      if (pFree->CellSize < 0)
+        pFree->CellSize = -pFree->CellSize;
+
+      /* Clear block (except the block size) */
+      RtlZeroMemory(((char*)pFree) + sizeof(ULONG),
+                   pFree->CellSize - sizeof(ULONG));
+
+      /* Add block to the list of free blocks */
+      CmiAddFree(RegistryHive, Cell, CellOffset, TRUE);
+
+      /* Update time of heap */
+      if (!IsNoFileHive(RegistryHive) && CmiGetCell (RegistryHive, CellOffset,&pBin))
+       NtQuerySystemTime(&pBin->DateModified);
+
+      CmiMarkBlockDirty(RegistryHive, CellOffset);
+    }
+
+  return Status;
+}
+
+
+PVOID
+CmiGetCell (PREGISTRY_HIVE RegistryHive,
+           BLOCK_OFFSET CellOffset,
+           PHBIN *Bin)
+{
+  PHBIN pBin;
+
+  if (Bin != NULL)
+    {
+      *Bin = NULL;
+    }
+
+  if (CellOffset == (BLOCK_OFFSET)-1)
+    {
+      return NULL;
+    }
+
+  if (IsPointerHive (RegistryHive))
+    {
+      return (PVOID)CellOffset;
+    }
+
+  if (CellOffset > RegistryHive->BlockListSize * REG_BLOCK_SIZE)
+    {
+      DPRINT1("CellOffset exceeds valid range (%lu > %lu)\n",
+             CellOffset, RegistryHive->BlockListSize * REG_BLOCK_SIZE);
+      return NULL;
+    }
+
+  pBin = RegistryHive->BlockList[CellOffset / REG_BLOCK_SIZE].Bin;
+  if (pBin == NULL)
+    {
+      return NULL;
+    }
+
+  if (Bin != NULL)
+    {
+      *Bin = pBin;
+    }
+
+  return((PVOID)((ULONG_PTR)pBin + (CellOffset - pBin->BinOffset)));
+}
+
+
+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);
+
+  CmiGetCell (RegistryHive,
+             FreeOffset,
+             &Bin);
+  DPRINT("Bin %p\n", Bin);
+  if (Bin == NULL)
+    return(FALSE);
+
+  BinOffset = Bin->BinOffset;
+  BinSize = Bin->BinSize;
+  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,
+          BOOLEAN MergeFreeBlocks)
+{
+  PCELL_HEADER *tmpList;
+  BLOCK_OFFSET *tmpListOffset;
+  LONG minInd;
+  LONG maxInd;
+  LONG medInd;
+
+  assert(RegistryHive);
+  assert(FreeBlock);
+
+  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)
+    {
+      tmpList = ExAllocatePool(PagedPool,
+                         sizeof(PCELL_HEADER) * (RegistryHive->FreeListMax + 32));
+      if (tmpList == NULL)
+       return STATUS_INSUFFICIENT_RESOURCES;
+
+      tmpListOffset = ExAllocatePool(PagedPool,
+                         sizeof(BLOCK_OFFSET) * (RegistryHive->FreeListMax + 32));
+
+      if (tmpListOffset == NULL)
+       {
+         ExFreePool(tmpList);
+         return STATUS_INSUFFICIENT_RESOURCES;
+       }
+
+      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;
+    }
+
+  /* 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;
+       }
+
+      /* 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;
+}
+
+
+VOID
+CmiMarkBlockDirty(PREGISTRY_HIVE RegistryHive,
+                 BLOCK_OFFSET BlockOffset)
+{
+  PDATA_CELL Cell;
+  LONG CellSize;
+  ULONG BlockNumber;
+  ULONG BlockCount;
+
+  if (IsNoFileHive(RegistryHive))
+    return;
+
+  DPRINT("CmiMarkBlockDirty(Offset 0x%lx)\n", (ULONG)BlockOffset);
+
+  BlockNumber = (ULONG)BlockOffset / REG_BLOCK_SIZE;
+
+  Cell = CmiGetCell (RegistryHive,
+                    BlockOffset,
+                    NULL);
+
+  CellSize = Cell->CellSize;
+  if (CellSize < 0)
+    CellSize = -CellSize;
+
+  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);
+}
+
+
+VOID
+CmiMarkBinDirty(PREGISTRY_HIVE RegistryHive,
+               BLOCK_OFFSET BinOffset)
+{
+  ULONG BlockNumber;
+  ULONG BlockCount;
+  PHBIN Bin;
+
+  if (IsNoFileHive(RegistryHive))
+    return;
+
+  DPRINT("CmiMarkBinDirty(Offset 0x%lx)\n", (ULONG)BinOffset);
+
+  BlockNumber = (ULONG)BinOffset / REG_BLOCK_SIZE;
+
+  Bin = RegistryHive->BlockList[BlockNumber].Bin;
+
+  BlockCount = Bin->BinSize / REG_BLOCK_SIZE;
+
+  DPRINT("  BlockNumber %lu  BinSize %lu  BlockCount %lu\n",
+        BlockNumber,
+        Bin->BinSize,
+        BlockCount);
+
+  RegistryHive->HiveDirty = TRUE;
+  RtlSetBits(&RegistryHive->DirtyBitMap,
+            BlockNumber,
+            BlockCount);
+}
+
+
+ULONG
+CmiGetPackedNameLength(IN PUNICODE_STRING Name,
+                      OUT PBOOLEAN Packable)
+{
+  ULONG i;
+
+  if (Packable != NULL)
+    *Packable = TRUE;
+
+  for (i = 0; i < Name->Length / sizeof(WCHAR); i++)
+    {
+      if (Name->Buffer[i] & 0xFF00)
+       {
+         if (Packable != NULL)
+           *Packable = FALSE;
+         return Name->Length;
+       }
+    }
+
+  return (Name->Length / sizeof(WCHAR));
+}
+
+
+BOOLEAN
+CmiComparePackedNames(IN PUNICODE_STRING Name,
+                     IN PCHAR NameBuffer,
+                     IN USHORT NameBufferSize,
+                     IN BOOLEAN NamePacked)
+{
+  PWCHAR UNameBuffer;
+  ULONG i;
+
+  if (NamePacked == TRUE)
+    {
+      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
+    {
+      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(TRUE);
+}
+
+
+VOID
+CmiCopyPackedName(PWCHAR NameBuffer,
+                 PCHAR PackedNameBuffer,
+                 ULONG PackedNameSize)
+{
+  ULONG i;
+
+  for (i = 0; i < PackedNameSize; i++)
+    NameBuffer[i] = (WCHAR)PackedNameBuffer[i];
+}
+
+
+BOOLEAN
+CmiCompareHash(PUNICODE_STRING KeyName,
+              PCHAR HashString)
+{
+  CHAR Buffer[4];
+
+  Buffer[0] = (KeyName->Length >= 2) ? (CHAR)KeyName->Buffer[0] : 0;
+  Buffer[1] = (KeyName->Length >= 4) ? (CHAR)KeyName->Buffer[1] : 0;
+  Buffer[2] = (KeyName->Length >= 6) ? (CHAR)KeyName->Buffer[2] : 0;
+  Buffer[3] = (KeyName->Length >= 8) ? (CHAR)KeyName->Buffer[3] : 0;
+
+  return (strncmp(Buffer, HashString, 4) == 0);
+}
+
+
+BOOLEAN
+CmiCompareHashI(PUNICODE_STRING KeyName,
+               PCHAR HashString)
+{
+  CHAR Buffer[4];
+
+  Buffer[0] = (KeyName->Length >= 2) ? (CHAR)KeyName->Buffer[0] : 0;
+  Buffer[1] = (KeyName->Length >= 4) ? (CHAR)KeyName->Buffer[1] : 0;
+  Buffer[2] = (KeyName->Length >= 6) ? (CHAR)KeyName->Buffer[2] : 0;
+  Buffer[3] = (KeyName->Length >= 8) ? (CHAR)KeyName->Buffer[3] : 0;
+
+  return (_strnicmp(Buffer, HashString, 4) == 0);
+}
+
+
+BOOLEAN
+CmiCompareKeyNames(PUNICODE_STRING KeyName,
+                  PKEY_CELL KeyCell)
+{
+  PWCHAR UnicodeName;
+  USHORT i;
+
+  DPRINT("Flags: %hx\n", KeyCell->Flags);
+
+  if (KeyCell->Flags & REG_KEY_NAME_PACKED)
+    {
+      if (KeyName->Length != KeyCell->NameSize * sizeof(WCHAR))
+       return FALSE;
+
+      for (i = 0; i < KeyCell->NameSize; i++)
+       {
+         if (KeyName->Buffer[i] != (WCHAR)KeyCell->Name[i])
+           return FALSE;
+       }
+    }
+  else
+    {
+      if (KeyName->Length != KeyCell->NameSize)
+       return FALSE;
+
+      UnicodeName = (PWCHAR)KeyCell->Name;
+      for (i = 0; i < KeyCell->NameSize / sizeof(WCHAR); i++)
+       {
+         if (KeyName->Buffer[i] != UnicodeName[i])
+           return FALSE;
+       }
+    }
+
+  return TRUE;
+}
+
+
+BOOLEAN
+CmiCompareKeyNamesI(PUNICODE_STRING KeyName,
+                   PKEY_CELL KeyCell)
+{
+  PWCHAR UnicodeName;
+  USHORT i;
+
+  DPRINT("Flags: %hx\n", KeyCell->Flags);
+
+  if (KeyCell->Flags & REG_KEY_NAME_PACKED)
+    {
+      if (KeyName->Length != KeyCell->NameSize * sizeof(WCHAR))
+       return FALSE;
+
+      for (i = 0; i < KeyCell->NameSize; i++)
+       {
+         if (RtlUpcaseUnicodeChar(KeyName->Buffer[i]) !=
+             RtlUpcaseUnicodeChar((WCHAR)KeyCell->Name[i]))
+           return FALSE;
+       }
+    }
+  else
+    {
+      if (KeyName->Length != KeyCell->NameSize)
+       return FALSE;
+
+      UnicodeName = (PWCHAR)KeyCell->Name;
+      for (i = 0; i < KeyCell->NameSize / sizeof(WCHAR); i++)
+       {
+         if (RtlUpcaseUnicodeChar(KeyName->Buffer[i]) !=
+             RtlUpcaseUnicodeChar(UnicodeName[i]))
+           return FALSE;
+       }
+    }
+
+  return TRUE;
+}
+
+
+NTSTATUS
+CmiCopyKey (PREGISTRY_HIVE DstHive,
+           PKEY_CELL DstKeyCell,
+           PREGISTRY_HIVE SrcHive,
+           PKEY_CELL SrcKeyCell)
+{
+  PKEY_CELL NewKeyCell;
+  ULONG NewKeyCellSize;
+  BLOCK_OFFSET NewKeyCellOffset;
+  PHASH_TABLE_CELL NewHashTableCell;
+  ULONG NewHashTableSize;
+  BLOCK_OFFSET NewHashTableOffset;
+  ULONG i;
+  NTSTATUS Status;
+
+  DPRINT ("CmiCopyKey() called\n");
+
+  if (DstKeyCell == NULL)
+    {
+      /* Allocate and copy key cell */
+      NewKeyCellSize = sizeof(KEY_CELL) + SrcKeyCell->NameSize;
+      Status = 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;
+       }
+
+      RtlCopyMemory (NewKeyCell,
+                    SrcKeyCell,
+                    NewKeyCellSize);
+
+      DstHive->HiveHeader->RootKeyOffset = NewKeyCellOffset;
+
+      /* 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;
+           }
+
+         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;
+    }
+
+  /* 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;
+       }
+
+      RtlZeroMemory (NewValueListCell,
+                    NewValueListCellSize);
+
+      /* 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;
+           }
+
+         NewValueListCell->ValueOffset[i] = ValueCellOffset;
+         RtlCopyMemory (NewValueCell,
+                        SrcValueCell,
+                        NewValueCellSize);
+
+         /* Copy value data cell */
+         if (SrcValueCell->DataSize > (LONG) sizeof(PVOID))
+           {
+             SrcValueDataCell = CmiGetCell (SrcHive, SrcValueCell->DataOffset, NULL);
+
+             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;
+           }
+       }
+    }
+
+  /* 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))
+           {
+             DPRINT1 ("CmiAllocateBlock() failed (Status %lx)\n", Status);
+             return Status;
+           }
+         if (NewKeyCell == NULL)
+           {
+             DPRINT1 ("Failed to allocate a sub key cell\n");
+             return STATUS_INSUFFICIENT_RESOURCES;
+           }
+
+         NewHashTableCell->Table[i].KeyOffset = NewSubKeyCellOffset;
+         NewHashTableCell->Table[i].HashValue = SrcHashRecord->HashValue;
+
+         RtlCopyMemory (NewSubKeyCell,
+                        SrcSubKeyCell,
+                        NewSubKeyCellSize);
+
+         /* Copy class name */
+         if (SrcSubKeyCell->ClassNameOffset != (BLOCK_OFFSET) -1)
+           {
+             PDATA_CELL SrcClassNameCell;
+             PDATA_CELL NewClassNameCell;
+             BLOCK_OFFSET NewClassNameOffset;
+
+             SrcClassNameCell = 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;
+               }
+
+             NewSubKeyCell->ClassNameOffset = NewClassNameOffset;
+             RtlCopyMemory (NewClassNameCell,
+                            SrcClassNameCell,
+                            NewSubKeyCell->ClassSize);
+           }
+
+         /* Copy subkey data and subkeys */
+         Status = CmiCopyKey (DstHive,
+                              NewSubKeyCell,
+                              SrcHive,
+                              SrcSubKeyCell);
+         if (!NT_SUCCESS(Status))
+           {
+             DPRINT1 ("CmiAllocateBlock() failed (Status %lx)\n", Status);
+             return Status;
+           }
+       }
+    }
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+CmiSaveTempHive (PREGISTRY_HIVE Hive,
+                HANDLE FileHandle)
+{
+  IO_STATUS_BLOCK IoStatusBlock;
+  LARGE_INTEGER FileOffset;
+  ULONG BlockIndex;
+  PVOID BlockPtr;
+  NTSTATUS Status;
+
+  DPRINT ("CmiSaveTempHive() called\n");
+
+  Hive->HiveHeader->Checksum = CmiCalcChecksum ((PULONG)Hive->HiveHeader);
+
+  /* Write hive block */
+  FileOffset.QuadPart = (ULONGLONG)0;
+  Status = NtWriteFile (FileHandle,
+                       NULL,
+                       NULL,
+                       NULL,
+                       &IoStatusBlock,
+                       Hive->HiveHeader,
+                       sizeof(HIVE_HEADER),
+                       &FileOffset,
+                       NULL);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT1 ("NtWriteFile() failed (Status %lx)\n", Status);
+      return Status;
+    }
+
+  DPRINT ("Saving %lu blocks\n", Hive->BlockListSize);
+  for (BlockIndex = 0; BlockIndex < Hive->BlockListSize; BlockIndex++)
+    {
+      BlockPtr = Hive->BlockList[BlockIndex].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 = NtWriteFile (FileHandle,
+                           NULL,
+                           NULL,
+                           NULL,
+                           &IoStatusBlock,
+                           BlockPtr,
+                           REG_BLOCK_SIZE,
+                           &FileOffset,
+                           NULL);
+      if (!NT_SUCCESS(Status))
+       {
+         DPRINT1 ("NtWriteFile() failed (Status %lx)\n", Status);
+         return Status;
+       }
+    }
+
+  Status = NtFlushBuffersFile (FileHandle,
+                              &IoStatusBlock);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT1 ("NtFlushBuffersFile() failed (Status %lx)\n", Status);
+    }
+
+  DPRINT ("CmiSaveTempHive() done\n");
+
+  return Status;
+}
+
+/* EOF */