update for HEAD-2003091401
[reactos.git] / ntoskrnl / cm / regfile.c
1 /*
2  * COPYRIGHT:        See COPYING in the top level directory
3  * PROJECT:          ReactOS kernel
4  * FILE:             ntoskrnl/cm/regfile.c
5  * PURPOSE:          Registry file manipulation routines
6  * UPDATE HISTORY:
7 */
8
9 #include <ddk/ntddk.h>
10 #include <roscfg.h>
11 #include <internal/ob.h>
12 #include <limits.h>
13 #include <string.h>
14 #include <internal/registry.h>
15 #include <ntos/minmax.h>
16 #include <reactos/bugcodes.h>
17
18 #define NDEBUG
19 #include <internal/debug.h>
20
21 #include "cm.h"
22
23
24 /* uncomment to enable hive checks (incomplete and probably buggy) */
25 // #define HIVE_CHECK
26
27 /* LOCAL MACROS *************************************************************/
28
29 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
30 #define ROUND_DOWN(N, S) ((N) - ((N) % (S)))
31
32 #define ABS_VALUE(V) (((V) < 0) ? -(V) : (V))
33
34 BOOLEAN CmiDoVerify = FALSE;
35
36 static ULONG
37 CmiCalcChecksum(PULONG Buffer);
38
39 /* FUNCTIONS ****************************************************************/
40
41 VOID
42 CmiCreateDefaultHiveHeader(PHIVE_HEADER Header)
43 {
44   assert(Header);
45   RtlZeroMemory(Header, sizeof(HIVE_HEADER));
46   Header->BlockId = REG_HIVE_ID;
47   Header->UpdateCounter1 = 0;
48   Header->UpdateCounter2 = 0;
49   Header->DateModified.u.LowPart = 0;
50   Header->DateModified.u.HighPart = 0;
51   Header->Unused3 = 1;
52   Header->Unused4 = 3;
53   Header->Unused5 = 0;
54   Header->Unused6 = 1;
55   Header->Unused7 = 1;
56   Header->RootKeyCell = 0;
57   Header->BlockSize = REG_BLOCK_SIZE;
58   Header->Unused6 = 1;
59   Header->Checksum = 0;
60 }
61
62
63 VOID
64 CmiCreateDefaultBinCell(PHBIN BinCell)
65 {
66   assert(BinCell);
67   RtlZeroMemory(BinCell, sizeof(HBIN));
68   BinCell->BlockId = REG_BIN_ID;
69   BinCell->DateModified.u.LowPart = 0;
70   BinCell->DateModified.u.HighPart = 0;
71   BinCell->BlockSize = REG_BLOCK_SIZE;
72 }
73
74
75 VOID
76 CmiCreateDefaultRootKeyCell(PKEY_CELL RootKeyCell)
77 {
78   assert(RootKeyCell);
79   RtlZeroMemory(RootKeyCell, sizeof(KEY_CELL));
80   RootKeyCell->CellSize = -sizeof(KEY_CELL);
81   RootKeyCell->Id = REG_KEY_CELL_ID;
82   RootKeyCell->Flags = REG_KEY_ROOT_CELL | REG_KEY_NAME_PACKED;
83   NtQuerySystemTime(&RootKeyCell->LastWriteTime);
84   RootKeyCell->ParentKeyOffset = 0;
85   RootKeyCell->NumberOfSubKeys = 0;
86   RootKeyCell->HashTableOffset = -1;
87   RootKeyCell->NumberOfValues = 0;
88   RootKeyCell->ValuesOffset = -1;
89   RootKeyCell->SecurityKeyOffset = 0;
90   RootKeyCell->ClassNameOffset = -1;
91   RootKeyCell->NameSize = 0;
92   RootKeyCell->ClassSize = 0;
93 }
94
95
96 VOID
97 CmiVerifyBinCell(PHBIN BinCell)
98 {
99   if (CmiDoVerify)
100     {
101
102   assert(BinCell);
103
104   if (BinCell->BlockId != REG_BIN_ID)
105     {
106       DbgPrint("BlockId is %.08x (should be %.08x)\n",
107         BinCell->BlockId, REG_BIN_ID);
108       assert(BinCell->BlockId == REG_BIN_ID);
109     }
110
111   //BinCell->DateModified.dwLowDateTime
112
113   //BinCell->DateModified.dwHighDateTime
114
115   
116   if (BinCell->BlockSize != REG_BLOCK_SIZE)
117     {
118       DbgPrint("BlockSize is %.08x (should be %.08x)\n",
119         BinCell->BlockSize, REG_BLOCK_SIZE);
120       assert(BinCell->BlockSize == REG_BLOCK_SIZE);
121     }
122
123     }
124 }
125
126
127 VOID
128 CmiVerifyKeyCell(PKEY_CELL KeyCell)
129 {
130   if (CmiDoVerify)
131     {
132
133   assert(KeyCell);
134
135   if (KeyCell->CellSize == 0)
136     {
137       DbgPrint("CellSize is %d (must not be 0)\n",
138         KeyCell->CellSize);
139       assert(KeyCell->CellSize != 0);
140     }
141
142   if (KeyCell->Id != REG_KEY_CELL_ID)
143     {
144       DbgPrint("Id is %.08x (should be %.08x)\n",
145         KeyCell->Id, REG_KEY_CELL_ID);
146       assert(KeyCell->Id == REG_KEY_CELL_ID);
147     }
148
149   //KeyCell->Flags;
150
151   //KeyCell->LastWriteTime;
152
153   if (KeyCell->ParentKeyOffset < 0)
154     {
155       DbgPrint("ParentKeyOffset is %d (must not be < 0)\n",
156         KeyCell->ParentKeyOffset);
157       assert(KeyCell->ParentKeyOffset >= 0);
158     }
159
160   if (KeyCell->NumberOfSubKeys < 0)
161     {
162       DbgPrint("NumberOfSubKeys is %d (must not be < 0)\n",
163         KeyCell->NumberOfSubKeys);
164       assert(KeyCell->NumberOfSubKeys >= 0);
165     }
166
167   //KeyCell->HashTableOffset;
168
169   if (KeyCell->NumberOfValues < 0)
170     {
171       DbgPrint("NumberOfValues is %d (must not be < 0)\n",
172         KeyCell->NumberOfValues);
173       assert(KeyCell->NumberOfValues >= 0);
174     }
175
176   //KeyCell->ValuesOffset = -1;
177
178   if (KeyCell->SecurityKeyOffset < 0)
179     {
180       DbgPrint("SecurityKeyOffset is %d (must not be < 0)\n",
181         KeyCell->SecurityKeyOffset);
182       assert(KeyCell->SecurityKeyOffset >= 0);
183     }
184
185   //KeyCell->ClassNameOffset = -1;
186
187   //KeyCell->NameSize
188
189   //KeyCell->ClassSize
190
191     }
192 }
193
194
195 VOID
196 CmiVerifyRootKeyCell(PKEY_CELL RootKeyCell)
197 {
198   if (CmiDoVerify)
199     {
200
201   CmiVerifyKeyCell(RootKeyCell);
202
203   if (!(RootKeyCell->Flags & REG_KEY_ROOT_CELL))
204     {
205       DbgPrint("Flags is %.08x (should be %.08x)\n",
206         RootKeyCell->Flags, REG_KEY_ROOT_CELL | REG_KEY_NAME_PACKED);
207       assert(!(RootKeyCell->Flags & (REG_KEY_ROOT_CELL | REG_KEY_NAME_PACKED)));
208     }
209
210     }
211 }
212
213
214 VOID
215 CmiVerifyValueCell(PVALUE_CELL ValueCell)
216 {
217   if (CmiDoVerify)
218     {
219
220   assert(ValueCell);
221
222   if (ValueCell->CellSize == 0)
223     {
224       DbgPrint("CellSize is %d (must not be 0)\n",
225         ValueCell->CellSize);
226       assert(ValueCell->CellSize != 0);
227     }
228
229   if (ValueCell->Id != REG_VALUE_CELL_ID)
230     {
231       DbgPrint("Id is %.08x (should be %.08x)\n",
232         ValueCell->Id, REG_VALUE_CELL_ID);
233       assert(ValueCell->Id == REG_VALUE_CELL_ID);
234     }
235
236   //ValueCell->NameSize;
237   //ValueCell->LONG  DataSize;
238   //ValueCell->DataOffset;
239   //ValueCell->ULONG  DataType;
240   //ValueCell->USHORT Flags;
241   //ValueCell->USHORT Unused1;
242   //ValueCell->UCHAR  Name[0];
243     }
244 }
245
246
247 VOID
248 CmiVerifyValueListCell(PVALUE_LIST_CELL ValueListCell)
249 {
250   if (CmiDoVerify)
251     {
252
253   if (ValueListCell->CellSize == 0)
254     {
255       DbgPrint("CellSize is %d (must not be 0)\n",
256         ValueListCell->CellSize);
257       assert(ValueListCell->CellSize != 0);
258     }
259
260     }
261 }
262
263
264 VOID
265 CmiVerifyKeyObject(PKEY_OBJECT KeyObject)
266 {
267   if (CmiDoVerify)
268     {
269
270   if (KeyObject->RegistryHive == NULL)
271     {
272       DbgPrint("RegistryHive is NULL (must not be NULL)\n",
273         KeyObject->RegistryHive);
274       assert(KeyObject->RegistryHive != NULL);
275     }
276
277   if (KeyObject->KeyCell == NULL)
278     {
279       DbgPrint("KeyCell is NULL (must not be NULL)\n",
280         KeyObject->KeyCell);
281       assert(KeyObject->KeyCell != NULL);
282     }
283
284   if (KeyObject->ParentKey == NULL)
285     {
286       DbgPrint("ParentKey is NULL (must not be NULL)\n",
287         KeyObject->ParentKey);
288       assert(KeyObject->ParentKey != NULL);
289     }
290
291     }
292 }
293
294
295 VOID
296 CmiVerifyHiveHeader(PHIVE_HEADER Header)
297 {
298   if (CmiDoVerify)
299     {
300
301   if (Header->BlockId != REG_HIVE_ID)
302     {
303       DbgPrint("BlockId is %.08x (must be %.08x)\n",
304         Header->BlockId,
305         REG_HIVE_ID);
306       assert(Header->BlockId == REG_HIVE_ID);
307     }
308
309   if (Header->Unused3 != 1)
310     {
311       DbgPrint("Unused3 is %.08x (must be 1)\n",
312         Header->Unused3);
313       assert(Header->Unused3 == 1);
314     }
315
316   if (Header->Unused4 != 3)
317     {
318       DbgPrint("Unused4 is %.08x (must be 3)\n",
319         Header->Unused4);
320       assert(Header->Unused4 == 3);
321     }
322
323   if (Header->Unused5 != 0)
324     {
325       DbgPrint("Unused5 is %.08x (must be 0)\n",
326         Header->Unused5);
327       assert(Header->Unused5 == 0);
328     }
329
330   if (Header->Unused6 != 1)
331     {
332       DbgPrint("Unused6 is %.08x (must be 1)\n",
333         Header->Unused6);
334       assert(Header->Unused6 == 1);
335     }
336
337   if (Header->Unused7 != 1)
338     {
339       DbgPrint("Unused7 is %.08x (must be 1)\n",
340         Header->Unused7);
341       assert(Header->Unused7 == 1);
342     }
343
344     }
345 }
346
347
348 VOID
349 CmiVerifyRegistryHive(PREGISTRY_HIVE RegistryHive)
350 {
351   if (CmiDoVerify)
352     {
353
354       CmiVerifyHiveHeader(RegistryHive->HiveHeader);
355
356     }
357 }
358
359
360 static NTSTATUS
361 CmiCreateNewRegFile(HANDLE FileHandle)
362 {
363   IO_STATUS_BLOCK IoStatusBlock;
364   PCELL_HEADER FreeCell;
365   PHIVE_HEADER HiveHeader;
366   PKEY_CELL RootKeyCell;
367   NTSTATUS Status;
368   PHBIN BinCell;
369   PCHAR Buffer;
370
371   Buffer = (PCHAR) ExAllocatePool(NonPagedPool, 2 * REG_BLOCK_SIZE);
372   if (Buffer == NULL)
373     return STATUS_INSUFFICIENT_RESOURCES;
374
375   HiveHeader = (PHIVE_HEADER)Buffer;
376   BinCell = (PHBIN)((ULONG_PTR)Buffer + REG_BLOCK_SIZE);
377   RootKeyCell = (PKEY_CELL)((ULONG_PTR)Buffer + REG_BLOCK_SIZE + REG_HBIN_DATA_OFFSET);
378   FreeCell = (PCELL_HEADER)((ULONG_PTR)Buffer + REG_BLOCK_SIZE + REG_HBIN_DATA_OFFSET + sizeof(KEY_CELL));
379
380   CmiCreateDefaultHiveHeader(HiveHeader);
381   CmiCreateDefaultBinCell(BinCell);
382   CmiCreateDefaultRootKeyCell(RootKeyCell);
383
384   /* First block */
385   BinCell->BlockOffset = 0;
386
387   /* Offset to root key block */
388   HiveHeader->RootKeyCell = REG_HBIN_DATA_OFFSET;
389
390   /* The rest of the block is free */
391   FreeCell->CellSize = REG_BLOCK_SIZE - (REG_HBIN_DATA_OFFSET + sizeof(KEY_CELL));
392
393   Status = NtWriteFile(FileHandle,
394                        NULL,
395                        NULL,
396                        NULL,
397                        &IoStatusBlock,
398                        Buffer,
399                        2 * REG_BLOCK_SIZE,
400                        0,
401                        NULL);
402
403   ExFreePool(Buffer);
404
405   assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
406
407   if (!NT_SUCCESS(Status))
408     {
409       return(Status);
410     }
411
412   Status = NtFlushBuffersFile(FileHandle,
413                               &IoStatusBlock);
414
415   return(Status);
416 }
417
418
419 #ifdef HIVE_CHECK
420 static NTSTATUS
421 CmiCheckAndFixHive(PREGISTRY_HIVE RegistryHive)
422 {
423   OBJECT_ATTRIBUTES ObjectAttributes;
424   FILE_STANDARD_INFORMATION fsi;
425   IO_STATUS_BLOCK IoStatusBlock;
426   HANDLE HiveHandle = INVALID_HANDLE_VALUE;
427   HANDLE LogHandle = INVALID_HANDLE_VALUE;
428   PHIVE_HEADER HiveHeader = NULL;
429   PHIVE_HEADER LogHeader = NULL;
430   LARGE_INTEGER FileOffset;
431   ULONG FileSize;
432   ULONG BufferSize;
433   ULONG BitmapSize;
434   RTL_BITMAP BlockBitMap;
435   NTSTATUS Status;
436
437   DPRINT("CmiCheckAndFixHive() called\n");
438
439   /* Try to open the hive file */
440   InitializeObjectAttributes(&ObjectAttributes,
441                              &RegistryHive->HiveFileName,
442                              0,
443                              NULL,
444                              NULL);
445
446   Status = NtCreateFile(&HiveHandle,
447                         FILE_READ_DATA | FILE_READ_ATTRIBUTES,
448                         &ObjectAttributes,
449                         &IoStatusBlock,
450                         NULL,
451                         FILE_ATTRIBUTE_NORMAL,
452                         0,
453                         FILE_OPEN,
454                         FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
455                         NULL,
456                         0);
457   if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
458     {
459       return(STATUS_SUCCESS);
460     }
461   if (!NT_SUCCESS(Status))
462     {
463       DPRINT("NtCreateFile() failed (Status %lx)\n", Status);
464       return(Status);
465     }
466
467   /* Try to open the log file */
468   InitializeObjectAttributes(&ObjectAttributes,
469                              &RegistryHive->LogFileName,
470                              0,
471                              NULL,
472                              NULL);
473
474   Status = NtCreateFile(&LogHandle,
475                         FILE_READ_DATA | FILE_READ_ATTRIBUTES,
476                         &ObjectAttributes,
477                         &IoStatusBlock,
478                         NULL,
479                         FILE_ATTRIBUTE_NORMAL,
480                         0,
481                         FILE_OPEN,
482                         FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
483                         NULL,
484                         0);
485   if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
486     {
487       LogHandle = INVALID_HANDLE_VALUE;
488     }
489   else if (!NT_SUCCESS(Status))
490     {
491       DPRINT("NtCreateFile() failed (Status %lx)\n", Status);
492       NtClose(HiveHandle);
493       return(Status);
494     }
495
496   /* Allocate hive header */
497   HiveHeader = ExAllocatePool(PagedPool,
498                               sizeof(HIVE_HEADER));
499   if (HiveHeader == NULL)
500     {
501       DPRINT("ExAllocatePool() failed\n");
502       Status = STATUS_INSUFFICIENT_RESOURCES;
503       goto ByeBye;
504     }
505
506   /* Read hive base block */
507   FileOffset.QuadPart = 0ULL;
508   Status = NtReadFile(HiveHandle,
509                       0,
510                       0,
511                       0,
512                       &IoStatusBlock,
513                       HiveHeader,
514                       sizeof(HIVE_HEADER),
515                       &FileOffset,
516                       0);
517   if (!NT_SUCCESS(Status))
518     {
519       DPRINT("NtReadFile() failed (Status %lx)\n", Status);
520       goto ByeBye;
521     }
522
523   if (LogHandle == INVALID_HANDLE_VALUE)
524     {
525       if (HiveHeader->Checksum != CmiCalcChecksum((PULONG)HiveHeader) ||
526           HiveHeader->UpdateCounter1 != HiveHeader->UpdateCounter2)
527         {
528           /* There is no way to fix the hive without log file - BSOD! */
529           DPRINT("Hive header inconsistent and no log file available!\n");
530           KEBUGCHECK(CONFIG_LIST_FAILED);
531         }
532
533       Status = STATUS_SUCCESS;
534       goto ByeBye;
535     }
536   else
537     {
538       /* Allocate hive header */
539       LogHeader = ExAllocatePool(PagedPool,
540                                  sizeof(HIVE_HEADER));
541       if (LogHeader == NULL)
542         {
543           DPRINT("ExAllocatePool() failed\n");
544           Status = STATUS_INSUFFICIENT_RESOURCES;
545           goto ByeBye;
546         }
547
548       /* Read log file header */
549       FileOffset.QuadPart = 0ULL;
550       Status = NtReadFile(LogHandle,
551                           0,
552                           0,
553                           0,
554                           &IoStatusBlock,
555                           LogHeader,
556                           sizeof(HIVE_HEADER),
557                           &FileOffset,
558                           0);
559       if (!NT_SUCCESS(Status))
560         {
561           DPRINT("NtReadFile() failed (Status %lx)\n", Status);
562           goto ByeBye;
563         }
564
565       /* Check log file header integrity */
566       if (LogHeader->Checksum != CmiCalcChecksum((PULONG)LogHeader) ||
567           LogHeader->UpdateCounter1 != LogHeader->UpdateCounter2)
568         {
569           if (HiveHeader->Checksum != CmiCalcChecksum((PULONG)HiveHeader) ||
570               HiveHeader->UpdateCounter1 != HiveHeader->UpdateCounter2)
571             {
572               DPRINT("Hive file and log file are inconsistent!\n");
573               KEBUGCHECK(CONFIG_LIST_FAILED);
574             }
575
576           /* Log file damaged but hive is okay */
577           Status = STATUS_SUCCESS;
578           goto ByeBye;
579         }
580
581       if (HiveHeader->UpdateCounter1 == HiveHeader->UpdateCounter2 &&
582           HiveHeader->UpdateCounter1 == LogHeader->UpdateCounter1)
583         {
584           /* Hive and log file are up-to-date */
585           Status = STATUS_SUCCESS;
586           goto ByeBye;
587         }
588
589       /*
590        * Hive needs an update!
591        */
592
593       /* Get file size */
594       Status = NtQueryInformationFile(LogHandle,
595                                       &IoStatusBlock,
596                                       &fsi,
597                                       sizeof(fsi),
598                                       FileStandardInformation);
599       if (!NT_SUCCESS(Status))
600         {
601           DPRINT("NtQueryInformationFile() failed (Status %lx)\n", Status);
602           goto ByeBye;
603         }
604       FileSize = fsi.EndOfFile.u.LowPart;
605
606       /* Calculate bitmap and block size */
607       BitmapSize = ROUND_UP((FileSize / 4096) - 1, sizeof(ULONG) * 8) / 8;
608       BufferSize = sizeof(HIVE_HEADER) +
609                           sizeof(ULONG) +
610                           BitmapSize;
611       BufferSize = ROUND_UP(BufferSize, 4096);
612
613       /* Reallocate log header block */
614       ExFreePool(LogHeader);
615       LogHeader = ExAllocatePool(PagedPool,
616                                  BufferSize);
617       if (LogHeader == NULL)
618         {
619           DPRINT("ExAllocatePool() failed\n");
620           Status = STATUS_INSUFFICIENT_RESOURCES;
621           goto ByeBye;
622         }
623
624       /* Read log file header */
625       FileOffset.QuadPart = 0ULL;
626       Status = NtReadFile(LogHandle,
627                           0,
628                           0,
629                           0,
630                           &IoStatusBlock,
631                           LogHeader,
632                           BufferSize,
633                           &FileOffset,
634                           0);
635       if (!NT_SUCCESS(Status))
636         {
637           DPRINT("NtReadFile() failed (Status %lx)\n", Status);
638           goto ByeBye;
639         }
640
641       /* Initialize bitmap */
642       RtlInitializeBitMap(&BlockBitMap,
643                           (PVOID)((ULONG)LogHeader + 4096 + sizeof(ULONG)),
644                           BitmapSize * 8);
645
646       /* FIXME: Update dirty blocks */
647
648
649       /* FIXME: Update hive header */
650
651
652       Status = STATUS_SUCCESS;
653     }
654
655
656   /* Clean up the mess */
657 ByeBye:
658   if (HiveHeader != NULL)
659     ExFreePool(HiveHeader);
660
661   if (LogHeader != NULL)
662     ExFreePool(LogHeader);
663
664   if (LogHandle != INVALID_HANDLE_VALUE)
665     NtClose(LogHandle);
666
667   NtClose(HiveHandle);
668
669   return(Status);
670 }
671 #endif
672
673
674 NTSTATUS
675 CmiImportHiveBins(PREGISTRY_HIVE Hive,
676                   PUCHAR ChunkPtr)
677 {
678   BLOCK_OFFSET BlockOffset;
679   ULONG BlockIndex;
680   PHBIN Bin;
681   ULONG j;
682
683   BlockIndex = 0;
684   BlockOffset = 0;
685   while (BlockIndex < Hive->BlockListSize)
686     {
687       Bin = (PHBIN)((ULONG_PTR)ChunkPtr + BlockOffset);
688
689       if (Bin->BlockId != REG_BIN_ID)
690         {
691           DPRINT1 ("Bad BlockId %x, offset %x\n", Bin->BlockId, BlockOffset);
692           return STATUS_REGISTRY_CORRUPT;
693         }
694
695       assertmsg((Bin->BlockSize % 4096) == 0,
696                 ("BlockSize (0x%.08x) must be multiple of 4K\n",
697                 Bin->BlockSize));
698
699       /* Allocate the hive block */
700       Hive->BlockList[BlockIndex] = ExAllocatePool (PagedPool,
701                                                     Bin->BlockSize);
702       if (Hive->BlockList[BlockIndex] == NULL)
703         {
704           DPRINT1 ("ExAllocatePool() failed\n");
705           return STATUS_INSUFFICIENT_RESOURCES;
706         }
707
708       /* Import the Bin */
709       RtlCopyMemory (Hive->BlockList[BlockIndex],
710                      Bin,
711                      Bin->BlockSize);
712
713       if (Bin->BlockSize > 4096)
714         {
715           for (j = 1; j < Bin->BlockSize / 4096; j++)
716             {
717               Hive->BlockList[BlockIndex + j] = Hive->BlockList[BlockIndex];
718             }
719         }
720
721       BlockIndex += Bin->BlockSize / 4096;
722       BlockOffset += Bin->BlockSize;
723     }
724
725   return STATUS_SUCCESS;
726 }
727
728
729 VOID
730 CmiFreeHiveBins (PREGISTRY_HIVE Hive)
731 {
732   ULONG i;
733   PHBIN Bin;
734
735   Bin = NULL;
736   for (i = 0; i < Hive->BlockListSize; i++)
737     {
738       if (Hive->BlockList[i] == NULL)
739         continue;
740
741       if (Hive->BlockList[i] != Bin)
742         {
743           Bin = Hive->BlockList[i];
744           ExFreePool (Hive->BlockList[i]);
745         }
746       Hive->BlockList[i] = NULL;
747     }
748 }
749
750
751 NTSTATUS
752 CmiCreateHiveFreeCellList(PREGISTRY_HIVE Hive)
753 {
754   BLOCK_OFFSET BlockOffset;
755   PCELL_HEADER FreeBlock;
756   ULONG BlockIndex;
757   ULONG FreeOffset;
758   PHBIN Bin;
759   NTSTATUS Status;
760
761   /* Initialize the free cell list */
762   Hive->FreeListSize = 0;
763   Hive->FreeListMax = 0;
764   Hive->FreeList = NULL;
765   Hive->FreeListOffset = NULL;
766
767   BlockOffset = 0;
768   BlockIndex = 0;
769   while (BlockIndex < Hive->BlockListSize)
770     {
771       Bin = Hive->BlockList[BlockIndex];
772
773       /* Search free blocks and add to list */
774       FreeOffset = REG_HBIN_DATA_OFFSET;
775       while (FreeOffset < Bin->BlockSize)
776         {
777           FreeBlock = (PCELL_HEADER) ((ULONG_PTR) Bin + FreeOffset);
778           if (FreeBlock->CellSize > 0)
779             {
780               Status = CmiAddFree(Hive,
781                                   FreeBlock,
782                                   Bin->BlockOffset + FreeOffset,
783                                   FALSE);
784
785               if (!NT_SUCCESS(Status))
786                 {
787                   return Status;
788                 }
789
790               FreeOffset += FreeBlock->CellSize;
791             }
792           else
793             {
794               FreeOffset -= FreeBlock->CellSize;
795             }
796         }
797
798       BlockIndex += Bin->BlockSize / 4096;
799       BlockOffset += Bin->BlockSize;
800     }
801
802   return STATUS_SUCCESS;
803 }
804
805
806 VOID
807 CmiFreeHiveFreeCellList(PREGISTRY_HIVE Hive)
808 {
809   ExFreePool (Hive->FreeList);
810   ExFreePool (Hive->FreeListOffset);
811
812   Hive->FreeListSize = 0;
813   Hive->FreeListMax = 0;
814   Hive->FreeList = NULL;
815   Hive->FreeListOffset = NULL;
816 }
817
818
819 NTSTATUS
820 CmiCreateHiveBitmap(PREGISTRY_HIVE Hive)
821 {
822   ULONG BitmapSize;
823
824   /* Calculate bitmap size in bytes (always a multiple of 32 bits) */
825   BitmapSize = ROUND_UP(Hive->BlockListSize, sizeof(ULONG) * 8) / 8;
826   DPRINT("Hive->BlockListSize: %lu\n", Hive->BlockListSize);
827   DPRINT("BitmapSize:  %lu Bytes  %lu Bits\n", BitmapSize, BitmapSize * 8);
828
829   /* Allocate bitmap */
830   Hive->BitmapBuffer = (PULONG)ExAllocatePool(PagedPool,
831                                               BitmapSize);
832   if (Hive->BitmapBuffer == NULL)
833     {
834       return STATUS_INSUFFICIENT_RESOURCES;
835     }
836
837   RtlInitializeBitMap(&Hive->DirtyBitMap,
838                       Hive->BitmapBuffer,
839                       BitmapSize * 8);
840
841   /* Initialize bitmap */
842   RtlClearAllBits(&Hive->DirtyBitMap);
843   Hive->HiveDirty = FALSE;
844
845   return STATUS_SUCCESS;
846 }
847
848
849 static NTSTATUS
850 CmiInitNonVolatileRegistryHive (PREGISTRY_HIVE RegistryHive,
851                                 PWSTR Filename)
852 {
853   OBJECT_ATTRIBUTES ObjectAttributes;
854   ULONG CreateDisposition;
855   IO_STATUS_BLOCK IoSB;
856   HANDLE FileHandle;
857   HANDLE SectionHandle;
858   PUCHAR ViewBase;
859   ULONG ViewSize;
860   NTSTATUS Status;
861
862   DPRINT("CmiInitNonVolatileRegistryHive(%p, %S) called\n",
863          RegistryHive, Filename);
864
865   /* Duplicate Filename */
866   Status = RtlCreateUnicodeString(&RegistryHive->HiveFileName,
867                                   Filename);
868   if (!NT_SUCCESS(Status))
869     {
870       DPRINT("RtlCreateUnicodeString() failed (Status %lx)\n", Status);
871       return(Status);
872     }
873
874   /* Create log file name */
875   RegistryHive->LogFileName.Length = (wcslen(Filename) + 4) * sizeof(WCHAR);
876   RegistryHive->LogFileName.MaximumLength = RegistryHive->LogFileName.Length + sizeof(WCHAR);
877   RegistryHive->LogFileName.Buffer = ExAllocatePool(NonPagedPool,
878                                                     RegistryHive->LogFileName.MaximumLength);
879   if (RegistryHive->LogFileName.Buffer == NULL)
880     {
881       RtlFreeUnicodeString(&RegistryHive->HiveFileName);
882       DPRINT("ExAllocatePool() failed\n");
883       return(STATUS_INSUFFICIENT_RESOURCES);
884     }
885   wcscpy(RegistryHive->LogFileName.Buffer,
886          Filename);
887   wcscat(RegistryHive->LogFileName.Buffer,
888          L".log");
889
890 #ifdef HIVE_CHECK
891   /* Check and eventually fix a hive */
892   Status = CmiCheckAndFixHive(RegistryHive);
893   if (!NT_SUCCESS(Status))
894     {
895       RtlFreeUnicodeString(&RegistryHive->HiveFileName);
896       RtlFreeUnicodeString(&RegistryHive->LogFileName);
897       DPRINT1("CmiCheckAndFixHive() failed (Status %lx)\n", Status);
898       return(Status);
899     }
900 #endif
901
902   InitializeObjectAttributes(&ObjectAttributes,
903                              &RegistryHive->HiveFileName,
904                              0,
905                              NULL,
906                              NULL);
907
908   CreateDisposition = FILE_OPEN_IF;
909   Status = NtCreateFile(&FileHandle,
910                         FILE_ALL_ACCESS,
911                         &ObjectAttributes,
912                         &IoSB,
913                         NULL,
914                         FILE_ATTRIBUTE_NORMAL,
915                         0,
916                         CreateDisposition,
917                         FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
918                         NULL,
919                         0);
920   if (!NT_SUCCESS(Status))
921     {
922       RtlFreeUnicodeString(&RegistryHive->HiveFileName);
923       RtlFreeUnicodeString(&RegistryHive->LogFileName);
924       DPRINT("NtCreateFile() failed (Status %lx)\n", Status);
925       return(Status);
926     }
927
928   if (IoSB.Information != FILE_OPENED)
929     {
930       Status = CmiCreateNewRegFile(FileHandle);
931       if (!NT_SUCCESS(Status))
932         {
933           DPRINT("CmiCreateNewRegFile() failed (Status %lx)\n", Status);
934           NtClose(FileHandle);
935           RtlFreeUnicodeString(&RegistryHive->HiveFileName);
936           RtlFreeUnicodeString(&RegistryHive->LogFileName);
937           return(Status);
938         }
939     }
940
941   /* Create the hive section */
942   Status = NtCreateSection(&SectionHandle,
943                            SECTION_ALL_ACCESS,
944                            NULL,
945                            NULL,
946                            PAGE_READWRITE,
947                            SEC_COMMIT,
948                            FileHandle);
949   NtClose(FileHandle);
950   if (!NT_SUCCESS(Status))
951     {
952       DPRINT1("NtCreateSection() failed (Status %lx)\n", Status);
953       RtlFreeUnicodeString(&RegistryHive->HiveFileName);
954       RtlFreeUnicodeString(&RegistryHive->LogFileName);
955       return(Status);
956     }
957
958   /* Map the hive file */
959   ViewBase = NULL;
960   ViewSize = 0;
961   Status = NtMapViewOfSection(SectionHandle,
962                               NtCurrentProcess(),
963                               (PVOID*)&ViewBase,
964                               0,
965                               ViewSize,
966                               NULL,
967                               &ViewSize,
968                               0,
969                               MEM_COMMIT,
970                               PAGE_READWRITE);
971   if (!NT_SUCCESS(Status))
972     {
973       DPRINT1("MmMapViewInSystemSpace() failed (Status %lx)\n", Status);
974       NtClose(SectionHandle);
975       RtlFreeUnicodeString(&RegistryHive->HiveFileName);
976       RtlFreeUnicodeString(&RegistryHive->LogFileName);
977       return(Status);
978     }
979   DPRINT("ViewBase %p  ViewSize %lx\n", ViewBase, ViewSize);
980
981   /* Copy hive header and initalize hive */
982   RtlCopyMemory (RegistryHive->HiveHeader,
983                  ViewBase,
984                  sizeof(HIVE_HEADER));
985   RegistryHive->FileSize = ViewSize;
986   RegistryHive->BlockListSize = (RegistryHive->FileSize / 4096) - 1;
987   RegistryHive->UpdateCounter = RegistryHive->HiveHeader->UpdateCounter1;
988
989   /* Allocate hive block list */
990   RegistryHive->BlockList = ExAllocatePool(NonPagedPool,
991           sizeof(PHBIN *) * RegistryHive->BlockListSize);
992   if (RegistryHive->BlockList == NULL)
993     {
994       DPRINT1("Failed to allocate the hive block list\n");
995       NtUnmapViewOfSection(NtCurrentProcess(),
996                            ViewBase);
997       NtClose(SectionHandle);
998       RtlFreeUnicodeString(&RegistryHive->HiveFileName);
999       RtlFreeUnicodeString(&RegistryHive->LogFileName);
1000       return STATUS_INSUFFICIENT_RESOURCES;
1001     }
1002
1003   /* Import the hive bins */
1004   Status = CmiImportHiveBins (RegistryHive,
1005                               ViewBase + 4096);
1006   if (!NT_SUCCESS(Status))
1007     {
1008       ExFreePool(RegistryHive->BlockList);
1009       NtUnmapViewOfSection(NtCurrentProcess(),
1010                            ViewBase);
1011       NtClose(SectionHandle);
1012       RtlFreeUnicodeString(&RegistryHive->HiveFileName);
1013       RtlFreeUnicodeString(&RegistryHive->LogFileName);
1014       return Status;
1015     }
1016
1017   /* Unmap and dereference the hive section */
1018   NtUnmapViewOfSection(NtCurrentProcess(),
1019                        ViewBase);
1020   NtClose(SectionHandle);
1021
1022   /* Initialize the free cell list */
1023   Status = CmiCreateHiveFreeCellList (RegistryHive);
1024   if (!NT_SUCCESS(Status))
1025     {
1026       CmiFreeHiveBins(RegistryHive);
1027       ExFreePool(RegistryHive->BlockList);
1028       RtlFreeUnicodeString(&RegistryHive->HiveFileName);
1029       RtlFreeUnicodeString(&RegistryHive->LogFileName);
1030       return Status;
1031     }
1032
1033   /* Create the block bitmap */
1034   Status = CmiCreateHiveBitmap (RegistryHive);
1035   if (!NT_SUCCESS(Status))
1036     {
1037       CmiFreeHiveFreeCellList(RegistryHive);
1038       CmiFreeHiveBins(RegistryHive);
1039       ExFreePool(RegistryHive->BlockList);
1040       RtlFreeUnicodeString(&RegistryHive->HiveFileName);
1041       RtlFreeUnicodeString(&RegistryHive->LogFileName);
1042       return Status;
1043     }
1044
1045   DPRINT("CmiInitNonVolatileRegistryHive(%p, %S) - Finished.\n",
1046          RegistryHive, Filename);
1047
1048   return(STATUS_SUCCESS);
1049 }
1050
1051
1052 NTSTATUS
1053 CmiCreateVolatileHive(PREGISTRY_HIVE *RegistryHive)
1054 {
1055   PKEY_CELL RootKeyCell;
1056   PREGISTRY_HIVE Hive;
1057
1058   *RegistryHive = NULL;
1059
1060   Hive = ExAllocatePool (NonPagedPool,
1061                          sizeof(REGISTRY_HIVE));
1062   if (Hive == NULL)
1063     return STATUS_INSUFFICIENT_RESOURCES;
1064
1065   RtlZeroMemory (Hive,
1066                  sizeof(REGISTRY_HIVE));
1067
1068   DPRINT("Hive %x\n", Hive);
1069
1070   Hive->HiveHeader = (PHIVE_HEADER)ExAllocatePool (NonPagedPool,
1071                                                    sizeof(HIVE_HEADER));
1072   if (Hive->HiveHeader == NULL)
1073     {
1074       ExFreePool (Hive);
1075       return STATUS_INSUFFICIENT_RESOURCES;
1076     }
1077
1078   Hive->Flags = (HIVE_NO_FILE | HIVE_POINTER);
1079
1080   CmiCreateDefaultHiveHeader (Hive->HiveHeader);
1081
1082   RootKeyCell = (PKEY_CELL)ExAllocatePool (NonPagedPool,
1083                                            sizeof(KEY_CELL));
1084   if (RootKeyCell == NULL)
1085     {
1086       ExFreePool(Hive->HiveHeader);
1087       ExFreePool(Hive);
1088       return STATUS_INSUFFICIENT_RESOURCES;
1089     }
1090
1091   CmiCreateDefaultRootKeyCell (RootKeyCell);
1092   Hive->HiveHeader->RootKeyCell = (BLOCK_OFFSET)RootKeyCell;
1093
1094   ExInitializeResourceLite (&Hive->HiveResource);
1095
1096   /* Acquire hive list lock exclusively */
1097   ExAcquireResourceExclusiveLite (&CmiHiveListLock,
1098                                   TRUE);
1099
1100   /* Add the new hive to the hive list */
1101   InsertTailList (&CmiHiveListHead,
1102                   &Hive->HiveList);
1103
1104   /* Release hive list lock */
1105   ExReleaseResourceLite (&CmiHiveListLock);
1106
1107   VERIFY_REGISTRY_HIVE (Hive);
1108
1109   *RegistryHive = Hive;
1110
1111   return STATUS_SUCCESS;
1112 }
1113
1114
1115 NTSTATUS
1116 CmiCreateTempHive(PREGISTRY_HIVE *RegistryHive)
1117 {
1118   PHBIN BinCell;
1119   PCELL_HEADER FreeCell;
1120   PREGISTRY_HIVE Hive;
1121   NTSTATUS Status;
1122
1123   DPRINT ("CmiCreateTempHive() called\n");
1124
1125   *RegistryHive = NULL;
1126
1127   Hive = ExAllocatePool (NonPagedPool,
1128                          sizeof(REGISTRY_HIVE));
1129   if (Hive == NULL)
1130     {
1131       DPRINT1 ("Failed to allocate registry hive block\n");
1132       return STATUS_INSUFFICIENT_RESOURCES;
1133     }
1134   RtlZeroMemory (Hive,
1135                  sizeof(REGISTRY_HIVE));
1136
1137   DPRINT ("Hive %x\n", Hive);
1138
1139   Hive->HiveHeader = (PHIVE_HEADER)ExAllocatePool (NonPagedPool,
1140                                                    REG_BLOCK_SIZE);
1141   if (Hive->HiveHeader == NULL)
1142     {
1143       DPRINT1 ("Failed to allocate hive header block\n");
1144       ExFreePool (Hive);
1145       return STATUS_INSUFFICIENT_RESOURCES;
1146     }
1147   RtlZeroMemory (Hive->HiveHeader,
1148                  REG_BLOCK_SIZE);
1149
1150   DPRINT ("HiveHeader %x\n", Hive->HiveHeader);
1151
1152   Hive->Flags = HIVE_NO_FILE;
1153
1154   RtlInitUnicodeString (&Hive->HiveFileName,
1155                         NULL);
1156   RtlInitUnicodeString (&Hive->LogFileName,
1157                         NULL);
1158
1159   CmiCreateDefaultHiveHeader (Hive->HiveHeader);
1160
1161   /* Allocate hive block list */
1162   Hive->BlockList = ExAllocatePool (NonPagedPool,
1163                                     sizeof(PHBIN *));
1164   if (Hive->BlockList == NULL)
1165     {
1166       DPRINT1 ("Failed to allocate hive block list\n");
1167       ExFreePool(Hive->HiveHeader);
1168       ExFreePool(Hive);
1169       return STATUS_INSUFFICIENT_RESOURCES;
1170     }
1171
1172   /* Allocate first Bin */
1173   Hive->BlockList[0] = ExAllocatePool (NonPagedPool,
1174                                        REG_BLOCK_SIZE);
1175   if (Hive->BlockList[0] == NULL)
1176     {
1177       DPRINT1 ("Failed to allocate first bin\n");
1178       ExFreePool(Hive->BlockList);
1179       ExFreePool(Hive->HiveHeader);
1180       ExFreePool(Hive);
1181       return STATUS_INSUFFICIENT_RESOURCES;
1182     }
1183
1184   Hive->FileSize = 2* REG_BLOCK_SIZE;
1185   Hive->BlockListSize = 1;
1186   Hive->UpdateCounter = Hive->HiveHeader->UpdateCounter1;
1187
1188
1189   BinCell = (PHBIN)Hive->BlockList[0];
1190   FreeCell = (PCELL_HEADER)((ULONG_PTR)BinCell + REG_HBIN_DATA_OFFSET);
1191
1192   CmiCreateDefaultBinCell (BinCell);
1193
1194   /* First block */
1195   BinCell->BlockOffset = 0;
1196
1197   /* Offset to root key block */
1198   Hive->HiveHeader->RootKeyCell = -1;
1199
1200   /* The rest of the block is free */
1201   FreeCell->CellSize = REG_BLOCK_SIZE - REG_HBIN_DATA_OFFSET;
1202
1203   /* Create the free cell list */
1204   Status = CmiCreateHiveFreeCellList (Hive);
1205   if (Hive->BlockList[0] == NULL)
1206     {
1207       DPRINT1 ("CmiCreateHiveFreeCellList() failed (Status %lx)\n", Status);
1208       ExFreePool(Hive->BlockList[0]);
1209       ExFreePool(Hive->BlockList);
1210       ExFreePool(Hive->HiveHeader);
1211       ExFreePool(Hive);
1212       return Status;
1213     }
1214
1215
1216   ExInitializeResourceLite (&Hive->HiveResource);
1217
1218   /* Acquire hive list lock exclusively */
1219   ExAcquireResourceExclusiveLite (&CmiHiveListLock,
1220                                   TRUE);
1221
1222   /* Add the new hive to the hive list */
1223   InsertTailList (&CmiHiveListHead,
1224                   &Hive->HiveList);
1225
1226   /* Release hive list lock */
1227   ExReleaseResourceLite (&CmiHiveListLock);
1228
1229   VERIFY_REGISTRY_HIVE (Hive);
1230
1231   *RegistryHive = Hive;
1232
1233   return STATUS_SUCCESS;
1234 }
1235
1236
1237 NTSTATUS
1238 CmiLoadHive(IN POBJECT_ATTRIBUTES KeyObjectAttributes,
1239             IN PUNICODE_STRING FileName,
1240             IN ULONG Flags)
1241 {
1242   PREGISTRY_HIVE Hive;
1243   NTSTATUS Status;
1244
1245   DPRINT ("CmiLoadHive(Filename %wZ)\n", FileName);
1246
1247   if (Flags & ~REG_NO_LAZY_FLUSH)
1248     return STATUS_INVALID_PARAMETER;
1249
1250   Hive = ExAllocatePool (NonPagedPool,
1251                          sizeof(REGISTRY_HIVE));
1252   if (Hive == NULL)
1253     {
1254       DPRINT1 ("Failed to allocate hive header.\n");
1255       return STATUS_INSUFFICIENT_RESOURCES;
1256     }
1257   RtlZeroMemory (Hive,
1258                  sizeof(REGISTRY_HIVE));
1259
1260   DPRINT ("Hive %x\n", Hive);
1261   Hive->Flags = (Flags & REG_NO_LAZY_FLUSH) ? HIVE_NO_SYNCH : 0;
1262
1263   Hive->HiveHeader = (PHIVE_HEADER)ExAllocatePool(NonPagedPool,
1264                                                   sizeof(HIVE_HEADER));
1265   if (Hive->HiveHeader == NULL)
1266     {
1267       DPRINT1 ("Failed to allocate hive header.\n");
1268       ExFreePool (Hive);
1269       return STATUS_INSUFFICIENT_RESOURCES;
1270     }
1271
1272   Status = CmiInitNonVolatileRegistryHive (Hive,
1273                                            FileName->Buffer);
1274   if (!NT_SUCCESS (Status))
1275     {
1276       DPRINT1 ("CmiInitNonVolatileRegistryHive() failed (Status %lx)\n", Status);
1277       ExFreePool (Hive->HiveHeader);
1278       ExFreePool (Hive);
1279       return Status;
1280     }
1281
1282   ExInitializeResourceLite (&Hive->HiveResource);
1283
1284   /* Add the new hive to the hive list */
1285   ExAcquireResourceExclusiveLite (&CmiHiveListLock,
1286                                   TRUE);
1287   InsertTailList (&CmiHiveListHead,
1288                   &Hive->HiveList);
1289   ExReleaseResourceLite (&CmiHiveListLock);
1290
1291
1292   VERIFY_REGISTRY_HIVE(Hive);
1293
1294
1295   Status = CmiConnectHive (KeyObjectAttributes,
1296                            Hive);
1297   if (!NT_SUCCESS(Status))
1298     {
1299       DPRINT1 ("CmiConnectHive() failed (Status %lx)\n", Status);
1300 //      CmiRemoveRegistryHive (Hive);
1301     }
1302
1303   DPRINT ("CmiLoadHive() done\n");
1304
1305   return Status;
1306 }
1307
1308
1309 NTSTATUS
1310 CmiRemoveRegistryHive(PREGISTRY_HIVE RegistryHive)
1311 {
1312   if (RegistryHive->Flags & HIVE_POINTER)
1313     return STATUS_UNSUCCESSFUL;
1314
1315   /* Acquire hive list lock exclusively */
1316   ExAcquireResourceExclusiveLite (&CmiHiveListLock,
1317                                   TRUE);
1318
1319   /* Remove hive from hive list */
1320   RemoveEntryList (&RegistryHive->HiveList);
1321
1322   /* Release hive list lock */
1323   ExReleaseResourceLite (&CmiHiveListLock);
1324
1325   /* Release file names */
1326   RtlFreeUnicodeString (&RegistryHive->HiveFileName);
1327   RtlFreeUnicodeString (&RegistryHive->LogFileName);
1328
1329   /* Release hive bitmap */
1330   ExFreePool (RegistryHive->BitmapBuffer);
1331
1332   /* Release free cell list */
1333   ExFreePool (RegistryHive->FreeList);
1334   ExFreePool (RegistryHive->FreeListOffset);
1335
1336   /* Release hive resource */
1337   ExDeleteResource (&RegistryHive->HiveResource);
1338
1339   /* Release bins and bin list */
1340   CmiFreeHiveBins (RegistryHive);
1341   ExFreePool (RegistryHive->BlockList);
1342
1343   /* Release hive header */
1344   ExFreePool (RegistryHive->HiveHeader);
1345
1346   /* Release hive */
1347   ExFreePool (RegistryHive);
1348
1349   return STATUS_SUCCESS;
1350 }
1351
1352
1353 static ULONG
1354 CmiCalcChecksum(PULONG Buffer)
1355 {
1356   ULONG Sum = 0;
1357   ULONG i;
1358
1359   for (i = 0; i < 127; i++)
1360     Sum += Buffer[i];
1361
1362   return(Sum);
1363 }
1364
1365
1366 static NTSTATUS
1367 CmiStartLogUpdate(PREGISTRY_HIVE RegistryHive)
1368 {
1369   FILE_END_OF_FILE_INFORMATION EndOfFileInfo;
1370   FILE_ALLOCATION_INFORMATION FileAllocationInfo;
1371   OBJECT_ATTRIBUTES ObjectAttributes;
1372   IO_STATUS_BLOCK IoStatusBlock;
1373   HANDLE FileHandle;
1374   LARGE_INTEGER FileOffset;
1375   ULONG BufferSize;
1376   ULONG BitmapSize;
1377   PUCHAR Buffer;
1378   PUCHAR Ptr;
1379   ULONG BlockIndex;
1380   PVOID BlockPtr;
1381   NTSTATUS Status;
1382
1383   DPRINT("CmiStartLogUpdate() called\n");
1384
1385   BitmapSize = ROUND_UP(RegistryHive->BlockListSize, sizeof(ULONG) * 8) / 8;
1386   BufferSize = sizeof(HIVE_HEADER) +
1387                sizeof(ULONG) +
1388                BitmapSize;
1389   BufferSize = ROUND_UP(BufferSize, 4096);
1390
1391   DPRINT("Bitmap size %lu  buffer size: %lu\n", BitmapSize, BufferSize);
1392
1393   Buffer = (PUCHAR)ExAllocatePool(NonPagedPool, BufferSize);
1394   if (Buffer == NULL)
1395     {
1396       DPRINT("ExAllocatePool() failed\n");
1397       return(STATUS_INSUFFICIENT_RESOURCES);
1398     }
1399
1400   /* Open log file for writing */
1401   InitializeObjectAttributes(&ObjectAttributes,
1402                              &RegistryHive->LogFileName,
1403                              0,
1404                              NULL,
1405                              NULL);
1406
1407   Status = NtCreateFile(&FileHandle,
1408                         FILE_ALL_ACCESS,
1409                         &ObjectAttributes,
1410                         &IoStatusBlock,
1411                         NULL,
1412                         FILE_ATTRIBUTE_NORMAL,
1413                         0,
1414                         FILE_SUPERSEDE,
1415                         FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1416                         NULL,
1417                         0);
1418   if (!NT_SUCCESS(Status))
1419     {
1420       DPRINT("NtCreateFile() failed (Status %lx)\n", Status);
1421       ExFreePool(Buffer);
1422       return(Status);
1423     }
1424
1425   /* Update firt update counter and checksum */
1426   RegistryHive->HiveHeader->UpdateCounter1 = RegistryHive->UpdateCounter + 1;
1427   RegistryHive->HiveHeader->Checksum = CmiCalcChecksum((PULONG)RegistryHive->HiveHeader);
1428
1429   /* Copy hive header */
1430   RtlCopyMemory(Buffer,
1431                 RegistryHive->HiveHeader,
1432                 sizeof(HIVE_HEADER));
1433   Ptr = Buffer + sizeof(HIVE_HEADER);
1434
1435   RtlCopyMemory(Ptr,
1436                 "DIRT",
1437                 4);
1438   Ptr += 4;
1439   RtlCopyMemory(Ptr,
1440                 RegistryHive->DirtyBitMap.Buffer,
1441                 BitmapSize);
1442
1443   /* Write hive block and block bitmap */
1444   FileOffset.QuadPart = 0ULL;
1445   Status = NtWriteFile(FileHandle,
1446                        NULL,
1447                        NULL,
1448                        NULL,
1449                        &IoStatusBlock,
1450                        Buffer,
1451                        BufferSize,
1452                        &FileOffset,
1453                        NULL);
1454   if (!NT_SUCCESS(Status))
1455     {
1456       DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
1457       NtClose(FileHandle);
1458       ExFreePool(Buffer);
1459       return(Status);
1460     }
1461   ExFreePool(Buffer);
1462
1463   /* Write dirty blocks */
1464   FileOffset.QuadPart = (ULONGLONG)BufferSize;
1465   BlockIndex = 0;
1466   while (TRUE)
1467     {
1468       BlockIndex = RtlFindSetBits(&RegistryHive->DirtyBitMap,
1469                                   1,
1470                                   BlockIndex);
1471       if ((BlockIndex == (ULONG)-1) ||
1472           (BlockIndex >= RegistryHive->BlockListSize))
1473         {
1474           DPRINT("No more set bits\n");
1475           Status = STATUS_SUCCESS;
1476           break;
1477         }
1478
1479       DPRINT("Block %lu is dirty\n", BlockIndex);
1480
1481       BlockPtr = RegistryHive->BlockList[BlockIndex];
1482       DPRINT("BlockPtr %p\n", BlockPtr);
1483       DPRINT("File offset %I64x\n", FileOffset.QuadPart);
1484
1485       /* Write hive block */
1486       Status = NtWriteFile(FileHandle,
1487                            NULL,
1488                            NULL,
1489                            NULL,
1490                            &IoStatusBlock,
1491                            BlockPtr,
1492                            REG_BLOCK_SIZE,
1493                            &FileOffset,
1494                            NULL);
1495       if (!NT_SUCCESS(Status))
1496         {
1497           DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
1498           NtClose(FileHandle);
1499           return(Status);
1500         }
1501
1502       BlockIndex++;
1503       FileOffset.QuadPart += 4096ULL;
1504     }
1505
1506   /* Truncate log file */
1507   EndOfFileInfo.EndOfFile.QuadPart = FileOffset.QuadPart;
1508   Status = NtSetInformationFile(FileHandle,
1509                                 &IoStatusBlock,
1510                                 &EndOfFileInfo,
1511                                 sizeof(FILE_END_OF_FILE_INFORMATION),
1512                                 FileEndOfFileInformation);
1513   if (!NT_SUCCESS(Status))
1514     {
1515       DPRINT("NtSetInformationFile() failed (Status %lx)\n", Status);
1516       NtClose(FileHandle);
1517       return(Status);
1518     }
1519
1520   FileAllocationInfo.AllocationSize.QuadPart = FileOffset.QuadPart;
1521   Status = NtSetInformationFile(FileHandle,
1522                                 &IoStatusBlock,
1523                                 &FileAllocationInfo,
1524                                 sizeof(FILE_ALLOCATION_INFORMATION),
1525                                 FileAllocationInformation);
1526   if (!NT_SUCCESS(Status))
1527     {
1528       DPRINT("NtSetInformationFile() failed (Status %lx)\n", Status);
1529       NtClose(FileHandle);
1530       return(Status);
1531     }
1532
1533   /* Flush the log file */
1534   Status = NtFlushBuffersFile(FileHandle,
1535                               &IoStatusBlock);
1536   if (!NT_SUCCESS(Status))
1537     {
1538       DPRINT("NtFlushBuffersFile() failed (Status %lx)\n", Status);
1539     }
1540
1541   NtClose(FileHandle);
1542
1543   return(Status);
1544 }
1545
1546
1547 static NTSTATUS
1548 CmiFinishLogUpdate(PREGISTRY_HIVE RegistryHive)
1549 {
1550   OBJECT_ATTRIBUTES ObjectAttributes;
1551   IO_STATUS_BLOCK IoStatusBlock;
1552   HANDLE FileHandle;
1553   LARGE_INTEGER FileOffset;
1554   ULONG BufferSize;
1555   ULONG BitmapSize;
1556   PUCHAR Buffer;
1557   PUCHAR Ptr;
1558   NTSTATUS Status;
1559
1560   DPRINT("CmiFinishLogUpdate() called\n");
1561
1562   BitmapSize = ROUND_UP(RegistryHive->BlockListSize, sizeof(ULONG) * 8) / 8;
1563   BufferSize = sizeof(HIVE_HEADER) +
1564                sizeof(ULONG) +
1565                BitmapSize;
1566   BufferSize = ROUND_UP(BufferSize, 4096);
1567
1568   DPRINT("Bitmap size %lu  buffer size: %lu\n", BitmapSize, BufferSize);
1569
1570   Buffer = (PUCHAR)ExAllocatePool(NonPagedPool, BufferSize);
1571   if (Buffer == NULL)
1572     {
1573       DPRINT("ExAllocatePool() failed\n");
1574       return(STATUS_INSUFFICIENT_RESOURCES);
1575     }
1576
1577   /* Open log file for writing */
1578   InitializeObjectAttributes(&ObjectAttributes,
1579                              &RegistryHive->LogFileName,
1580                              0,
1581                              NULL,
1582                              NULL);
1583
1584   Status = NtCreateFile(&FileHandle,
1585                         FILE_ALL_ACCESS,
1586                         &ObjectAttributes,
1587                         &IoStatusBlock,
1588                         NULL,
1589                         FILE_ATTRIBUTE_NORMAL,
1590                         0,
1591                         FILE_OPEN,
1592                         FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1593                         NULL,
1594                         0);
1595   if (!NT_SUCCESS(Status))
1596     {
1597       DPRINT("NtCreateFile() failed (Status %lx)\n", Status);
1598       ExFreePool(Buffer);
1599       return(Status);
1600     }
1601
1602   /* Update first and second update counter and checksum */
1603   RegistryHive->HiveHeader->UpdateCounter1 = RegistryHive->UpdateCounter + 1;
1604   RegistryHive->HiveHeader->UpdateCounter2 = RegistryHive->UpdateCounter + 1;
1605   RegistryHive->HiveHeader->Checksum = CmiCalcChecksum((PULONG)RegistryHive->HiveHeader);
1606
1607   /* Copy hive header */
1608   RtlCopyMemory(Buffer,
1609                 RegistryHive->HiveHeader,
1610                 sizeof(HIVE_HEADER));
1611   Ptr = Buffer + sizeof(HIVE_HEADER);
1612
1613   /* Write empty block bitmap */
1614   RtlCopyMemory(Ptr,
1615                 "DIRT",
1616                 4);
1617   Ptr += 4;
1618   RtlZeroMemory(Ptr,
1619                 BitmapSize);
1620
1621   /* Write hive block and block bitmap */
1622   FileOffset.QuadPart = 0ULL;
1623   Status = NtWriteFile(FileHandle,
1624                        NULL,
1625                        NULL,
1626                        NULL,
1627                        &IoStatusBlock,
1628                        Buffer,
1629                        BufferSize,
1630                        &FileOffset,
1631                        NULL);
1632   if (!NT_SUCCESS(Status))
1633     {
1634       DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
1635       NtClose(FileHandle);
1636       ExFreePool(Buffer);
1637       return(Status);
1638     }
1639
1640   ExFreePool(Buffer);
1641
1642   /* Flush the log file */
1643   Status = NtFlushBuffersFile(FileHandle,
1644                               &IoStatusBlock);
1645   if (!NT_SUCCESS(Status))
1646     {
1647       DPRINT("NtFlushBuffersFile() failed (Status %lx)\n", Status);
1648     }
1649
1650   NtClose(FileHandle);
1651
1652   return(Status);
1653 }
1654
1655
1656 static NTSTATUS
1657 CmiCleanupLogUpdate(PREGISTRY_HIVE RegistryHive)
1658 {
1659   FILE_END_OF_FILE_INFORMATION EndOfFileInfo;
1660   FILE_ALLOCATION_INFORMATION FileAllocationInfo;
1661   OBJECT_ATTRIBUTES ObjectAttributes;
1662   IO_STATUS_BLOCK IoStatusBlock;
1663   HANDLE FileHandle;
1664   ULONG BufferSize;
1665   ULONG BitmapSize;
1666   NTSTATUS Status;
1667
1668   DPRINT("CmiCleanupLogUpdate() called\n");
1669
1670   BitmapSize = ROUND_UP(RegistryHive->BlockListSize, sizeof(ULONG) * 8) / 8;
1671   BufferSize = sizeof(HIVE_HEADER) +
1672                sizeof(ULONG) +
1673                BitmapSize;
1674   BufferSize = ROUND_UP(BufferSize, 4096);
1675
1676   DPRINT("Bitmap size %lu  buffer size: %lu\n", BitmapSize, BufferSize);
1677
1678   /* Open log file for writing */
1679   InitializeObjectAttributes(&ObjectAttributes,
1680                              &RegistryHive->LogFileName,
1681                              0,
1682                              NULL,
1683                              NULL);
1684
1685   Status = NtCreateFile(&FileHandle,
1686                         FILE_ALL_ACCESS,
1687                         &ObjectAttributes,
1688                         &IoStatusBlock,
1689                         NULL,
1690                         FILE_ATTRIBUTE_NORMAL,
1691                         0,
1692                         FILE_OPEN,
1693                         FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1694                         NULL,
1695                         0);
1696   if (!NT_SUCCESS(Status))
1697     {
1698       DPRINT("NtCreateFile() failed (Status %lx)\n", Status);
1699       return(Status);
1700     }
1701
1702   /* Truncate log file */
1703   EndOfFileInfo.EndOfFile.QuadPart = (ULONGLONG)BufferSize;
1704   Status = NtSetInformationFile(FileHandle,
1705                                 &IoStatusBlock,
1706                                 &EndOfFileInfo,
1707                                 sizeof(FILE_END_OF_FILE_INFORMATION),
1708                                 FileEndOfFileInformation);
1709   if (!NT_SUCCESS(Status))
1710     {
1711       DPRINT("NtSetInformationFile() failed (Status %lx)\n", Status);
1712       NtClose(FileHandle);
1713       return(Status);
1714     }
1715
1716   FileAllocationInfo.AllocationSize.QuadPart = (ULONGLONG)BufferSize;
1717   Status = NtSetInformationFile(FileHandle,
1718                                 &IoStatusBlock,
1719                                 &FileAllocationInfo,
1720                                 sizeof(FILE_ALLOCATION_INFORMATION),
1721                                 FileAllocationInformation);
1722   if (!NT_SUCCESS(Status))
1723     {
1724       DPRINT("NtSetInformationFile() failed (Status %lx)\n", Status);
1725       NtClose(FileHandle);
1726       return(Status);
1727     }
1728
1729   /* Flush the log file */
1730   Status = NtFlushBuffersFile(FileHandle,
1731                               &IoStatusBlock);
1732   if (!NT_SUCCESS(Status))
1733     {
1734       DPRINT("NtFlushBuffersFile() failed (Status %lx)\n", Status);
1735     }
1736
1737   NtClose(FileHandle);
1738
1739   return(Status);
1740 }
1741
1742
1743 static NTSTATUS
1744 CmiStartHiveUpdate(PREGISTRY_HIVE RegistryHive)
1745 {
1746   OBJECT_ATTRIBUTES ObjectAttributes;
1747   IO_STATUS_BLOCK IoStatusBlock;
1748   HANDLE FileHandle;
1749   LARGE_INTEGER FileOffset;
1750   ULONG BlockIndex;
1751   PVOID BlockPtr;
1752   NTSTATUS Status;
1753
1754   DPRINT("CmiStartHiveUpdate() called\n");
1755
1756   /* Open hive for writing */
1757   InitializeObjectAttributes(&ObjectAttributes,
1758                              &RegistryHive->HiveFileName,
1759                              0,
1760                              NULL,
1761                              NULL);
1762
1763   Status = NtCreateFile(&FileHandle,
1764                         FILE_ALL_ACCESS,
1765                         &ObjectAttributes,
1766                         &IoStatusBlock,
1767                         NULL,
1768                         FILE_ATTRIBUTE_NORMAL,
1769                         0,
1770                         FILE_OPEN,
1771                         FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1772                         NULL,
1773                         0);
1774   if (!NT_SUCCESS(Status))
1775     {
1776       DPRINT("NtCreateFile() failed (Status %lx)\n", Status);
1777       return(Status);
1778     }
1779
1780   /* Update firt update counter and checksum */
1781   RegistryHive->HiveHeader->UpdateCounter1 = RegistryHive->UpdateCounter + 1;
1782   RegistryHive->HiveHeader->Checksum = CmiCalcChecksum((PULONG)RegistryHive->HiveHeader);
1783
1784   /* Write hive block */
1785   FileOffset.QuadPart = 0ULL;
1786   Status = NtWriteFile(FileHandle,
1787                        NULL,
1788                        NULL,
1789                        NULL,
1790                        &IoStatusBlock,
1791                        RegistryHive->HiveHeader,
1792                        sizeof(HIVE_HEADER),
1793                        &FileOffset,
1794                        NULL);
1795   if (!NT_SUCCESS(Status))
1796     {
1797       DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
1798       NtClose(FileHandle);
1799       return(Status);
1800     }
1801
1802   BlockIndex = 0;
1803   while (TRUE)
1804     {
1805       BlockIndex = RtlFindSetBits(&RegistryHive->DirtyBitMap,
1806                                   1,
1807                                   BlockIndex);
1808       if ((BlockIndex == (ULONG)-1) ||
1809           (BlockIndex >= RegistryHive->BlockListSize))
1810         {
1811           DPRINT("No more set bits\n");
1812           Status = STATUS_SUCCESS;
1813           break;
1814         }
1815
1816       DPRINT("Block %lu is dirty\n", BlockIndex);
1817
1818       BlockPtr = RegistryHive->BlockList[BlockIndex];
1819       DPRINT("BlockPtr %p\n", BlockPtr);
1820
1821       FileOffset.QuadPart = (ULONGLONG)(BlockIndex + 1) * 4096ULL;
1822       DPRINT("File offset %I64x\n", FileOffset.QuadPart);
1823
1824       /* Write hive block */
1825       Status = NtWriteFile(FileHandle,
1826                            NULL,
1827                            NULL,
1828                            NULL,
1829                            &IoStatusBlock,
1830                            BlockPtr,
1831                            REG_BLOCK_SIZE,
1832                            &FileOffset,
1833                            NULL);
1834       if (!NT_SUCCESS(Status))
1835         {
1836           DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
1837           NtClose(FileHandle);
1838           return(Status);
1839         }
1840
1841       BlockIndex++;
1842     }
1843
1844   Status = NtFlushBuffersFile(FileHandle,
1845                               &IoStatusBlock);
1846   if (!NT_SUCCESS(Status))
1847     {
1848       DPRINT("NtFlushBuffersFile() failed (Status %lx)\n", Status);
1849     }
1850
1851   NtClose(FileHandle);
1852
1853   return(Status);
1854 }
1855
1856
1857 static NTSTATUS
1858 CmiFinishHiveUpdate(PREGISTRY_HIVE RegistryHive)
1859 {
1860   OBJECT_ATTRIBUTES ObjectAttributes;
1861   IO_STATUS_BLOCK IoStatusBlock;
1862   LARGE_INTEGER FileOffset;
1863   HANDLE FileHandle;
1864   NTSTATUS Status;
1865
1866   DPRINT("CmiFinishHiveUpdate() called\n");
1867
1868   InitializeObjectAttributes(&ObjectAttributes,
1869                              &RegistryHive->HiveFileName,
1870                              0,
1871                              NULL,
1872                              NULL);
1873
1874   Status = NtCreateFile(&FileHandle,
1875                         FILE_ALL_ACCESS,
1876                         &ObjectAttributes,
1877                         &IoStatusBlock,
1878                         NULL,
1879                         FILE_ATTRIBUTE_NORMAL,
1880                         0,
1881                         FILE_OPEN,
1882                         FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1883                         NULL,
1884                         0);
1885   if (!NT_SUCCESS(Status))
1886     {
1887       DPRINT("NtCreateFile() failed (Status %lx)\n", Status);
1888       return(Status);
1889     }
1890
1891   /* Update second update counter and checksum */
1892   RegistryHive->HiveHeader->UpdateCounter1 = RegistryHive->UpdateCounter + 1;
1893   RegistryHive->HiveHeader->UpdateCounter2 = RegistryHive->UpdateCounter + 1;
1894   RegistryHive->HiveHeader->Checksum = CmiCalcChecksum((PULONG)RegistryHive->HiveHeader);
1895
1896   /* Write hive block */
1897   FileOffset.QuadPart = 0ULL;
1898   Status = NtWriteFile(FileHandle,
1899                        NULL,
1900                        NULL,
1901                        NULL,
1902                        &IoStatusBlock,
1903                        RegistryHive->HiveHeader,
1904                        sizeof(HIVE_HEADER),
1905                        &FileOffset,
1906                        NULL);
1907   if (!NT_SUCCESS(Status))
1908     {
1909       DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
1910       NtClose(FileHandle);
1911       return(Status);
1912     }
1913
1914   Status = NtFlushBuffersFile(FileHandle,
1915                               &IoStatusBlock);
1916   if (!NT_SUCCESS(Status))
1917     {
1918       DPRINT("NtFlushBuffersFile() failed (Status %lx)\n", Status);
1919     }
1920
1921   NtClose(FileHandle);
1922
1923   return(Status);
1924 }
1925
1926
1927 NTSTATUS
1928 CmiFlushRegistryHive(PREGISTRY_HIVE RegistryHive)
1929 {
1930   NTSTATUS Status;
1931
1932   DPRINT("CmiFlushRegistryHive() called\n");
1933
1934   if (RegistryHive->HiveDirty == FALSE)
1935     {
1936       return(STATUS_SUCCESS);
1937     }
1938
1939   DPRINT("Hive '%wZ' is dirty\n",
1940          &RegistryHive->HiveFileName);
1941   DPRINT("Log file: '%wZ'\n",
1942          &RegistryHive->LogFileName);
1943
1944   /* Update hive header modification time */
1945   NtQuerySystemTime(&RegistryHive->HiveHeader->DateModified);
1946
1947   /* Start log update */
1948   Status = CmiStartLogUpdate(RegistryHive);
1949   if (!NT_SUCCESS(Status))
1950     {
1951       DPRINT("CmiStartLogUpdate() failed (Status %lx)\n", Status);
1952       return(Status);
1953     }
1954
1955   /* Finish log update */
1956   Status = CmiFinishLogUpdate(RegistryHive);
1957   if (!NT_SUCCESS(Status))
1958     {
1959       DPRINT("CmiFinishLogUpdate() failed (Status %lx)\n", Status);
1960       return(Status);
1961     }
1962
1963   /* Start hive update */
1964   Status = CmiStartHiveUpdate(RegistryHive);
1965   if (!NT_SUCCESS(Status))
1966     {
1967       DPRINT("CmiStartHiveUpdate() failed (Status %lx)\n", Status);
1968       return(Status);
1969     }
1970
1971   /* Finish the hive update */
1972   Status = CmiFinishHiveUpdate(RegistryHive);
1973   if (!NT_SUCCESS(Status))
1974     {
1975       DPRINT("CmiFinishHiveUpdate() failed (Status %lx)\n", Status);
1976       return(Status);
1977     }
1978
1979   /* Cleanup log update */
1980   Status = CmiCleanupLogUpdate(RegistryHive);
1981   if (!NT_SUCCESS(Status))
1982     {
1983       DPRINT("CmiFinishLogUpdate() failed (Status %lx)\n", Status);
1984       return(Status);
1985     }
1986
1987   /* Increment hive update counter */
1988   RegistryHive->UpdateCounter++;
1989
1990   /* Clear dirty bitmap and dirty flag */
1991   RtlClearAllBits(&RegistryHive->DirtyBitMap);
1992   RegistryHive->HiveDirty = FALSE;
1993
1994   DPRINT("CmiFlushRegistryHive() done\n");
1995
1996   return(STATUS_SUCCESS);
1997 }
1998
1999
2000 ULONG
2001 CmiGetMaxNameLength(PKEY_OBJECT KeyObject)
2002 {
2003   PHASH_TABLE_CELL HashBlock;
2004   PKEY_OBJECT CurKey;
2005   PKEY_CELL CurSubKeyCell;
2006   PKEY_CELL KeyCell;
2007   ULONG MaxName;
2008   ULONG NameSize;
2009   ULONG i;
2010
2011   VERIFY_KEY_OBJECT(KeyObject);
2012
2013   KeyCell = KeyObject->KeyCell;
2014   VERIFY_KEY_CELL(KeyCell);
2015
2016   MaxName = 0;
2017   HashBlock = CmiGetBlock(KeyObject->RegistryHive, 
2018                           KeyCell->HashTableOffset, 
2019                           NULL);
2020   if (HashBlock == NULL)
2021     {
2022       DPRINT("CmiGetBlock() failed\n");
2023     }
2024   else
2025     {
2026       for (i = 0; i < HashBlock->HashTableSize; i++)
2027         {
2028           if (HashBlock->Table[i].KeyOffset != 0)
2029             {
2030               CurSubKeyCell = CmiGetBlock(KeyObject->RegistryHive,
2031                                           HashBlock->Table[i].KeyOffset,
2032                                           NULL);
2033               if (CurSubKeyCell == NULL)
2034                 {
2035                   DPRINT("CmiGetBlock() failed\n");
2036                   continue;
2037                 }
2038               NameSize = CurSubKeyCell->NameSize;
2039               if (CurSubKeyCell->Flags & REG_KEY_NAME_PACKED)
2040                 {
2041                   NameSize *= sizeof(WCHAR);
2042                 }
2043               if (MaxName < NameSize)
2044                 {
2045                   MaxName = NameSize;
2046                 }
2047             }
2048         }
2049     }
2050   if (KeyObject->RegistryHive != CmiVolatileHive)
2051     {
2052       DPRINT("KeyObject->NumberOfSubKeys %d\n", KeyObject->NumberOfSubKeys);
2053       for (i = 0; i < KeyObject->NumberOfSubKeys; i++)
2054         {
2055           CurKey = KeyObject->SubKeys[i];
2056           if (CurKey->RegistryHive == CmiVolatileHive)
2057             {
2058               CurSubKeyCell = CurKey->KeyCell;
2059               if (CurSubKeyCell == NULL)
2060                 {
2061                   DPRINT("CmiGetBlock() failed\n");
2062                   continue;
2063                 }
2064               NameSize = CurSubKeyCell->NameSize;
2065               if (CurSubKeyCell->Flags & REG_KEY_NAME_PACKED)
2066                 {
2067                   NameSize *= sizeof(WCHAR);
2068                 }
2069               if (MaxName < NameSize)
2070                 {
2071                   MaxName = NameSize;
2072                 }
2073             }
2074         }
2075     }
2076
2077   return MaxName;
2078 }
2079
2080
2081 ULONG
2082 CmiGetMaxClassLength(PKEY_OBJECT  KeyObject)
2083 {
2084   PHASH_TABLE_CELL HashBlock;
2085   PKEY_OBJECT CurKey;
2086   PKEY_CELL CurSubKeyCell;
2087   PKEY_CELL KeyCell;
2088   ULONG MaxClass;
2089   ULONG i;
2090
2091   VERIFY_KEY_OBJECT(KeyObject);
2092
2093   KeyCell = KeyObject->KeyCell;
2094   VERIFY_KEY_CELL(KeyCell);
2095
2096   MaxClass = 0;
2097   HashBlock = CmiGetBlock(KeyObject->RegistryHive, 
2098                           KeyCell->HashTableOffset, 
2099                           NULL);
2100   if (HashBlock == NULL)
2101     {
2102       DPRINT("CmiGetBlock() failed\n");
2103     }
2104   else
2105     {
2106       for (i = 0; i < HashBlock->HashTableSize; i++)
2107         {
2108           if (HashBlock->Table[i].KeyOffset != 0)
2109             {
2110               CurSubKeyCell = CmiGetBlock(KeyObject->RegistryHive,
2111                                           HashBlock->Table[i].KeyOffset,
2112                                           NULL);
2113               if (CurSubKeyCell == NULL)
2114                 {
2115                   DPRINT("CmiGetBlock() failed\n");
2116                   continue;
2117                 }
2118
2119               if (MaxClass < CurSubKeyCell->ClassSize)
2120                 {
2121                   MaxClass = CurSubKeyCell->ClassSize;
2122                 }
2123             }
2124         }
2125     }
2126   if (KeyObject->RegistryHive != CmiVolatileHive)
2127     {
2128       DPRINT("KeyObject->NumberOfSubKeys %d\n", KeyObject->NumberOfSubKeys);
2129       for (i = 0; i < KeyObject->NumberOfSubKeys; i++)
2130         {
2131           CurKey = KeyObject->SubKeys[i];
2132           if (CurKey->RegistryHive == CmiVolatileHive)
2133             {
2134               CurSubKeyCell = CurKey->KeyCell;
2135               if (CurSubKeyCell == NULL)
2136                 {
2137                   DPRINT("CmiGetBlock() failed\n");
2138                   continue;
2139                 }
2140               if (MaxClass < CurSubKeyCell->ClassSize)
2141                 {
2142                   MaxClass = CurSubKeyCell->ClassSize;
2143                 }
2144             }
2145         }
2146     }
2147
2148   return MaxClass;
2149 }
2150
2151
2152 ULONG
2153 CmiGetMaxValueNameLength(PREGISTRY_HIVE RegistryHive,
2154                          PKEY_CELL KeyCell)
2155 {
2156   PVALUE_LIST_CELL ValueListCell;
2157   PVALUE_CELL CurValueCell;
2158   ULONG MaxValueName;
2159   ULONG Size;
2160   ULONG i;
2161
2162   VERIFY_KEY_CELL(KeyCell);
2163
2164   MaxValueName = 0;
2165   ValueListCell = CmiGetBlock(RegistryHive,
2166                               KeyCell->ValuesOffset,
2167                               NULL);
2168   if (ValueListCell == NULL)
2169     {
2170       DPRINT("CmiGetBlock() failed\n");
2171       return 0;
2172     }
2173
2174   for (i = 0; i < KeyCell->NumberOfValues; i++)
2175     {
2176       CurValueCell = CmiGetBlock (RegistryHive,
2177                                   ValueListCell->Values[i],
2178                                   NULL);
2179       if (CurValueCell == NULL)
2180         {
2181           DPRINT("CmiGetBlock() failed\n");
2182         }
2183
2184       if (CurValueCell != NULL)
2185         {
2186           Size = CurValueCell->NameSize;
2187           if (CurValueCell->Flags & REG_VALUE_NAME_PACKED)
2188             {
2189               Size *= sizeof(WCHAR);
2190             }
2191           if (MaxValueName < Size)
2192             {
2193               MaxValueName = Size;
2194             }
2195         }
2196     }
2197
2198   return MaxValueName;
2199 }
2200
2201
2202 ULONG
2203 CmiGetMaxValueDataLength(PREGISTRY_HIVE RegistryHive,
2204                          PKEY_CELL KeyCell)
2205 {
2206   PVALUE_LIST_CELL ValueListCell;
2207   PVALUE_CELL CurValueCell;
2208   LONG MaxValueData;
2209   ULONG i;
2210
2211   VERIFY_KEY_CELL(KeyCell);
2212
2213   MaxValueData = 0;
2214   ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
2215   if (ValueListCell == NULL)
2216     {
2217       return 0;
2218     }
2219
2220   for (i = 0; i < KeyCell->NumberOfValues; i++)
2221     {
2222       CurValueCell = CmiGetBlock(RegistryHive,
2223                                   ValueListCell->Values[i],NULL);
2224       if ((CurValueCell != NULL) &&
2225           (MaxValueData < (CurValueCell->DataSize & LONG_MAX)))
2226         {
2227           MaxValueData = CurValueCell->DataSize & LONG_MAX;
2228         }
2229     }
2230
2231   return MaxValueData;
2232 }
2233
2234
2235 NTSTATUS
2236 CmiScanForSubKey(IN PREGISTRY_HIVE RegistryHive,
2237                  IN PKEY_CELL KeyCell,
2238                  OUT PKEY_CELL *SubKeyCell,
2239                  OUT BLOCK_OFFSET *BlockOffset,
2240                  IN PUNICODE_STRING KeyName,
2241                  IN ACCESS_MASK DesiredAccess,
2242                  IN ULONG Attributes)
2243 {
2244   PHASH_TABLE_CELL HashBlock;
2245   PKEY_CELL CurSubKeyCell;
2246   ULONG i;
2247
2248   VERIFY_KEY_CELL(KeyCell);
2249
2250   DPRINT("Scanning for sub key %wZ\n", KeyName);
2251
2252   assert(RegistryHive);
2253
2254   *SubKeyCell = NULL;
2255
2256   /* The key does not have any subkeys */
2257   if (KeyCell->HashTableOffset == (BLOCK_OFFSET)-1)
2258     {
2259       return STATUS_SUCCESS;
2260     }
2261
2262   /* Get hash table */
2263   HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
2264   if (HashBlock == NULL)
2265     {
2266       DPRINT("CmiGetBlock() failed\n");
2267       return STATUS_UNSUCCESSFUL;
2268     }
2269
2270   for (i = 0; (i < KeyCell->NumberOfSubKeys) && (i < HashBlock->HashTableSize); i++)
2271     {
2272       if (Attributes & OBJ_CASE_INSENSITIVE)
2273         {
2274           if (HashBlock->Table[i].KeyOffset != 0 &&
2275               HashBlock->Table[i].KeyOffset != (ULONG_PTR)-1 &&
2276               (HashBlock->Table[i].HashValue == 0 ||
2277                CmiCompareHashI(KeyName, (PCHAR)&HashBlock->Table[i].HashValue)))
2278             {
2279               CurSubKeyCell = CmiGetBlock(RegistryHive,
2280                                           HashBlock->Table[i].KeyOffset,
2281                                           NULL);
2282               if (CurSubKeyCell == NULL)
2283                 {
2284                   DPRINT("CmiGetBlock() failed\n");
2285                   return STATUS_UNSUCCESSFUL;
2286                 }
2287
2288               if (CmiCompareKeyNamesI(KeyName, CurSubKeyCell))
2289                 {
2290                   *SubKeyCell = CurSubKeyCell;
2291                   *BlockOffset = HashBlock->Table[i].KeyOffset;
2292                   break;
2293                 }
2294             }
2295         }
2296       else
2297         {
2298           if (HashBlock->Table[i].KeyOffset != 0 &&
2299               HashBlock->Table[i].KeyOffset != (ULONG_PTR) -1 &&
2300               (HashBlock->Table[i].HashValue == 0 ||
2301                CmiCompareHash(KeyName, (PCHAR)&HashBlock->Table[i].HashValue)))
2302             {
2303               CurSubKeyCell = CmiGetBlock(RegistryHive,
2304                                           HashBlock->Table[i].KeyOffset,
2305                                           NULL);
2306               if (CurSubKeyCell == NULL)
2307                 {
2308                   DPRINT("CmiGetBlock() failed\n");
2309                   return STATUS_UNSUCCESSFUL;
2310                 }
2311
2312               if (CmiCompareKeyNames(KeyName, CurSubKeyCell))
2313                 {
2314                   *SubKeyCell = CurSubKeyCell;
2315                   *BlockOffset = HashBlock->Table[i].KeyOffset;
2316                   break;
2317                 }
2318             }
2319         }
2320     }
2321
2322   return STATUS_SUCCESS;
2323 }
2324
2325
2326 NTSTATUS
2327 CmiAddSubKey(PREGISTRY_HIVE RegistryHive,
2328              PKEY_OBJECT Parent,
2329              PKEY_OBJECT SubKey,
2330              PUNICODE_STRING SubKeyName,
2331              ULONG TitleIndex,
2332              PUNICODE_STRING Class,
2333              ULONG CreateOptions)
2334 {
2335   PHASH_TABLE_CELL NewHashBlock;
2336   PHASH_TABLE_CELL HashBlock;
2337   BLOCK_OFFSET NKBOffset;
2338   PKEY_CELL NewKeyCell;
2339   ULONG NewBlockSize;
2340   PKEY_CELL KeyCell;
2341   NTSTATUS Status;
2342   USHORT NameSize;
2343   PWSTR NamePtr;
2344   BOOLEAN Packable;
2345   ULONG i;
2346
2347   KeyCell = Parent->KeyCell;
2348
2349   VERIFY_KEY_CELL(KeyCell);
2350
2351   /* Skip leading backslash */
2352   if (SubKeyName->Buffer[0] == L'\\')
2353     {
2354       NamePtr = &SubKeyName->Buffer[1];
2355       NameSize = SubKeyName->Length - sizeof(WCHAR);
2356     }
2357   else
2358     {
2359       NamePtr = SubKeyName->Buffer;
2360       NameSize = SubKeyName->Length;
2361     }
2362
2363   /* Check whether key name can be packed */
2364   Packable = TRUE;
2365   for (i = 0; i < NameSize / sizeof(WCHAR); i++)
2366     {
2367       if (NamePtr[i] & 0xFF00)
2368         {
2369           Packable = FALSE;
2370           break;
2371         }
2372     }
2373
2374   /* Adjust name size */
2375   if (Packable)
2376     {
2377       NameSize = NameSize / sizeof(WCHAR);
2378     }
2379
2380   DPRINT("Key %S  Length %lu  %s\n", NamePtr, NameSize, (Packable)?"True":"False");
2381
2382   Status = STATUS_SUCCESS;
2383
2384   NewBlockSize = sizeof(KEY_CELL) + NameSize;
2385   Status = CmiAllocateBlock(RegistryHive,
2386                             (PVOID) &NewKeyCell,
2387                             NewBlockSize,
2388                             &NKBOffset);
2389   if (NewKeyCell == NULL)
2390     {
2391       Status = STATUS_INSUFFICIENT_RESOURCES;
2392     }
2393   else
2394     {
2395       NewKeyCell->Id = REG_KEY_CELL_ID;
2396       NewKeyCell->Flags = 0;
2397       NtQuerySystemTime(&NewKeyCell->LastWriteTime);
2398       NewKeyCell->ParentKeyOffset = -1;
2399       NewKeyCell->NumberOfSubKeys = 0;
2400       NewKeyCell->HashTableOffset = -1;
2401       NewKeyCell->NumberOfValues = 0;
2402       NewKeyCell->ValuesOffset = -1;
2403       NewKeyCell->SecurityKeyOffset = -1;
2404       NewKeyCell->ClassNameOffset = -1;
2405
2406       /* Pack the key name */
2407       NewKeyCell->NameSize = NameSize;
2408       if (Packable)
2409         {
2410           NewKeyCell->Flags |= REG_KEY_NAME_PACKED;
2411           for (i = 0; i < NameSize; i++)
2412             {
2413               NewKeyCell->Name[i] = (CHAR)(NamePtr[i] & 0x00FF);
2414             }
2415         }
2416       else
2417         {
2418           RtlCopyMemory(NewKeyCell->Name,
2419                         NamePtr,
2420                         NameSize);
2421         }
2422
2423       VERIFY_KEY_CELL(NewKeyCell);
2424
2425       if (Class)
2426         {
2427           PDATA_CELL pClass;
2428
2429           NewKeyCell->ClassSize = Class->Length + sizeof(WCHAR);
2430           Status = CmiAllocateBlock(RegistryHive,
2431                                     (PVOID)&pClass,
2432                                     NewKeyCell->ClassSize,
2433                                     &NewKeyCell->ClassNameOffset);
2434           wcsncpy((PWSTR)pClass->Data,
2435                   Class->Buffer,
2436                   Class->Length);
2437           ((PWSTR) (pClass->Data))[Class->Length] = 0;
2438         }
2439     }
2440
2441   if (!NT_SUCCESS(Status))
2442     {
2443       return Status;
2444     }
2445
2446   SubKey->KeyCell = NewKeyCell;
2447   SubKey->BlockOffset = NKBOffset;
2448
2449   /* Don't modify hash table if key is located in a pointer-based hive and parent key is not */
2450   if (IsPointerHive(RegistryHive) && (!IsPointerHive(Parent->RegistryHive)))
2451     {
2452       return(Status);
2453     }
2454
2455   if (KeyCell->HashTableOffset == (ULONG_PTR) -1)
2456     {
2457       Status = CmiAllocateHashTableBlock(RegistryHive,
2458                                          &HashBlock,
2459                                          &KeyCell->HashTableOffset,
2460                                          REG_INIT_HASH_TABLE_SIZE);
2461       if (!NT_SUCCESS(Status))
2462         {
2463           return(Status);
2464         }
2465     }
2466   else
2467     {
2468       HashBlock = CmiGetBlock(RegistryHive,
2469                               KeyCell->HashTableOffset,
2470                               NULL);
2471       if (HashBlock == NULL)
2472         {
2473           DPRINT("CmiGetBlock() failed\n");
2474           return STATUS_UNSUCCESSFUL;
2475         }
2476
2477       if (((KeyCell->NumberOfSubKeys + 1) >= HashBlock->HashTableSize))
2478         {
2479           BLOCK_OFFSET HTOffset;
2480
2481           /* Reallocate the hash table block */
2482           Status = CmiAllocateHashTableBlock(RegistryHive,
2483                                              &NewHashBlock,
2484                                              &HTOffset,
2485                                              HashBlock->HashTableSize +
2486                                                REG_EXTEND_HASH_TABLE_SIZE);
2487           if (!NT_SUCCESS(Status))
2488             {
2489               return Status;
2490             }
2491
2492           RtlZeroMemory(&NewHashBlock->Table[0],
2493                         sizeof(NewHashBlock->Table[0]) * NewHashBlock->HashTableSize);
2494           RtlCopyMemory(&NewHashBlock->Table[0],
2495                         &HashBlock->Table[0],
2496                         sizeof(NewHashBlock->Table[0]) * HashBlock->HashTableSize);
2497           CmiDestroyBlock(RegistryHive,
2498                           HashBlock,
2499                           KeyCell->HashTableOffset);
2500           KeyCell->HashTableOffset = HTOffset;
2501           HashBlock = NewHashBlock;
2502         }
2503     }
2504
2505   Status = CmiAddKeyToHashTable(RegistryHive,
2506                                 HashBlock,
2507                                 NewKeyCell,
2508                                 NKBOffset);
2509   if (NT_SUCCESS(Status))
2510     {
2511       KeyCell->NumberOfSubKeys++;
2512     }
2513
2514   return(Status);
2515 }
2516
2517
2518 NTSTATUS
2519 CmiRemoveSubKey(PREGISTRY_HIVE RegistryHive,
2520                 PKEY_OBJECT ParentKey,
2521                 PKEY_OBJECT SubKey)
2522 {
2523   PHASH_TABLE_CELL HashBlock;
2524   PVALUE_LIST_CELL ValueList;
2525   PVALUE_CELL ValueCell;
2526   PDATA_CELL DataCell;
2527   ULONG i;
2528
2529   DPRINT("CmiRemoveSubKey() called\n");
2530
2531   /* Remove all values */
2532   if (SubKey->KeyCell->NumberOfValues != 0)
2533     {
2534       /* Get pointer to the value list cell */
2535       ValueList = CmiGetBlock(RegistryHive,
2536                               SubKey->KeyCell->ValuesOffset,
2537                               NULL);
2538       if (ValueList == NULL)
2539         {
2540           DPRINT("CmiGetBlock() failed\n");
2541           return STATUS_UNSUCCESSFUL;
2542         }
2543
2544       if (ValueList != NULL)
2545         {
2546           /* Enumerate all values */
2547           for (i = 0; i < SubKey->KeyCell->NumberOfValues; i++)
2548             {
2549               /* Get pointer to value cell */
2550               ValueCell = CmiGetBlock(RegistryHive,
2551                                       ValueList->Values[i],
2552                                       NULL);
2553               if (ValueCell != NULL)
2554                 {
2555                   if (ValueCell->DataSize > 4)
2556                     {
2557                       DataCell = CmiGetBlock(RegistryHive,
2558                                              ValueCell->DataOffset,
2559                                              NULL);
2560                       if (DataCell == NULL)
2561                         {
2562                           DPRINT("CmiGetBlock() failed\n");
2563                           return STATUS_UNSUCCESSFUL;
2564                         }
2565
2566                       if (DataCell != NULL)
2567                         {
2568                           /* Destroy data cell */
2569                           CmiDestroyBlock(RegistryHive,
2570                                           DataCell,
2571                                           ValueCell->DataOffset);
2572                         }
2573                     }
2574
2575                   /* Destroy value cell */
2576                   CmiDestroyBlock(RegistryHive,
2577                                   ValueCell,
2578                                   ValueList->Values[i]);
2579                 }
2580             }
2581         }
2582
2583       /* Destroy value list cell */
2584       CmiDestroyBlock(RegistryHive,
2585                       ValueList,
2586                       SubKey->KeyCell->ValuesOffset);
2587
2588       SubKey->KeyCell->NumberOfValues = 0;
2589       SubKey->KeyCell->ValuesOffset = -1;
2590     }
2591
2592   /* Remove the key from the parent key's hash block */
2593   if (ParentKey->KeyCell->HashTableOffset != (BLOCK_OFFSET) -1)
2594     {
2595       DPRINT("ParentKey HashTableOffset %lx\n", ParentKey->KeyCell->HashTableOffset)
2596       HashBlock = CmiGetBlock(ParentKey->RegistryHive,
2597                               ParentKey->KeyCell->HashTableOffset,
2598                               NULL);
2599       if (HashBlock == NULL)
2600         {
2601           DPRINT("CmiGetBlock() failed\n");
2602           return STATUS_UNSUCCESSFUL;
2603         }
2604       DPRINT("ParentKey HashBlock %p\n", HashBlock)
2605       if (HashBlock != NULL)
2606         {
2607           CmiRemoveKeyFromHashTable(ParentKey->RegistryHive,
2608                                     HashBlock,
2609                                     SubKey->BlockOffset);
2610           CmiMarkBlockDirty(ParentKey->RegistryHive,
2611                             ParentKey->KeyCell->HashTableOffset);
2612         }
2613     }
2614
2615   /* Remove the key's hash block */
2616   if (SubKey->KeyCell->HashTableOffset != (BLOCK_OFFSET) -1)
2617     {
2618       DPRINT("SubKey HashTableOffset %lx\n", SubKey->KeyCell->HashTableOffset)
2619       HashBlock = CmiGetBlock(RegistryHive,
2620                               SubKey->KeyCell->HashTableOffset,
2621                               NULL);
2622       if (HashBlock == NULL)
2623         {
2624           DPRINT("CmiGetBlock() failed\n");
2625           return STATUS_UNSUCCESSFUL;
2626         }
2627       DPRINT("SubKey HashBlock %p\n", HashBlock)
2628       if (HashBlock != NULL)
2629         {
2630           CmiDestroyBlock(RegistryHive,
2631                           HashBlock,
2632                           SubKey->KeyCell->HashTableOffset);
2633           SubKey->KeyCell->HashTableOffset = -1;
2634         }
2635     }
2636
2637   /* Decrement the number of the parent key's sub keys */
2638   if (ParentKey != NULL)
2639     {
2640       DPRINT("ParentKey %p\n", ParentKey)
2641       ParentKey->KeyCell->NumberOfSubKeys--;
2642
2643       /* Remove the parent key's hash table */
2644       if (ParentKey->KeyCell->NumberOfSubKeys == 0)
2645         {
2646           DPRINT("ParentKey HashTableOffset %lx\n", ParentKey->KeyCell->HashTableOffset)
2647           HashBlock = CmiGetBlock(ParentKey->RegistryHive,
2648                                   ParentKey->KeyCell->HashTableOffset,
2649                                   NULL);
2650           if (HashBlock == NULL)
2651             {
2652               DPRINT("CmiGetBlock() failed\n");
2653               return STATUS_UNSUCCESSFUL;
2654             }
2655           DPRINT("ParentKey HashBlock %p\n", HashBlock)
2656           if (HashBlock != NULL)
2657             {
2658               CmiDestroyBlock(ParentKey->RegistryHive,
2659                               HashBlock,
2660                               ParentKey->KeyCell->HashTableOffset);
2661               ParentKey->KeyCell->HashTableOffset = -1;
2662             }
2663         }
2664
2665       NtQuerySystemTime(&ParentKey->KeyCell->LastWriteTime);
2666       CmiMarkBlockDirty(ParentKey->RegistryHive,
2667                         ParentKey->BlockOffset);
2668     }
2669
2670   /* Destroy key cell */
2671   CmiDestroyBlock(RegistryHive,
2672                   SubKey->KeyCell,
2673                   SubKey->BlockOffset);
2674   SubKey->BlockOffset = -1;
2675   SubKey->KeyCell = NULL;
2676
2677   return(STATUS_SUCCESS);
2678 }
2679
2680
2681 NTSTATUS
2682 CmiScanKeyForValue(IN PREGISTRY_HIVE RegistryHive,
2683                    IN PKEY_CELL KeyCell,
2684                    IN PUNICODE_STRING ValueName,
2685                    OUT PVALUE_CELL *ValueCell,
2686                    OUT BLOCK_OFFSET *VBOffset)
2687 {
2688   PVALUE_LIST_CELL ValueListCell;
2689   PVALUE_CELL CurValueCell;
2690   ULONG i;
2691
2692   *ValueCell = NULL;
2693
2694   /* The key does not have any values */
2695   if (KeyCell->ValuesOffset == (BLOCK_OFFSET)-1)
2696     {
2697       return STATUS_SUCCESS;
2698     }
2699
2700   ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
2701   if (ValueListCell == NULL)
2702     {
2703       DPRINT("ValueListCell is NULL\n");
2704       return STATUS_UNSUCCESSFUL;
2705     }
2706
2707   VERIFY_VALUE_LIST_CELL(ValueListCell);
2708
2709   for (i = 0; i < KeyCell->NumberOfValues; i++)
2710     {
2711       CurValueCell = CmiGetBlock(RegistryHive,
2712                                  ValueListCell->Values[i],
2713                                  NULL);
2714       if (CurValueCell == NULL)
2715         {
2716           DPRINT("CmiGetBlock() failed\n");
2717           return STATUS_UNSUCCESSFUL;
2718         }
2719
2720       if ((CurValueCell != NULL) &&
2721           CmiComparePackedNames(ValueName,
2722                                 CurValueCell->Name,
2723                                 CurValueCell->NameSize,
2724                                 CurValueCell->Flags & REG_VALUE_NAME_PACKED))
2725         {
2726           *ValueCell = CurValueCell;
2727           if (VBOffset)
2728             *VBOffset = ValueListCell->Values[i];
2729           //DPRINT("Found value %s\n", ValueName);
2730           break;
2731         }
2732     }
2733
2734   return STATUS_SUCCESS;
2735 }
2736
2737
2738 NTSTATUS
2739 CmiGetValueFromKeyByIndex(IN PREGISTRY_HIVE RegistryHive,
2740                           IN PKEY_CELL KeyCell,
2741                           IN ULONG Index,
2742                           OUT PVALUE_CELL *ValueCell)
2743 {
2744   PVALUE_LIST_CELL ValueListCell;
2745   PVALUE_CELL CurValueCell;
2746
2747   *ValueCell = NULL;
2748
2749   if (KeyCell->ValuesOffset == (BLOCK_OFFSET)-1)
2750     {
2751       return STATUS_NO_MORE_ENTRIES;
2752     }
2753
2754   if (Index >= KeyCell->NumberOfValues)
2755     {
2756       return STATUS_NO_MORE_ENTRIES;
2757     }
2758
2759
2760   ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
2761   if (ValueListCell == NULL)
2762     {
2763       DPRINT("CmiGetBlock() failed\n");
2764       return STATUS_UNSUCCESSFUL;
2765     }
2766
2767   VERIFY_VALUE_LIST_CELL(ValueListCell);
2768
2769
2770   CurValueCell = CmiGetBlock(RegistryHive,
2771                              ValueListCell->Values[Index],
2772                              NULL);
2773   if (CurValueCell == NULL)
2774     {
2775       DPRINT("CmiGetBlock() failed\n");
2776       return STATUS_UNSUCCESSFUL;
2777     }
2778
2779   *ValueCell = CurValueCell;
2780
2781   return STATUS_SUCCESS;
2782 }
2783
2784
2785 NTSTATUS
2786 CmiAddValueToKey(IN PREGISTRY_HIVE RegistryHive,
2787                  IN PKEY_CELL KeyCell,
2788                  IN PUNICODE_STRING ValueName,
2789                  OUT PVALUE_CELL *pValueCell,
2790                  OUT BLOCK_OFFSET *pVBOffset)
2791 {
2792   PVALUE_LIST_CELL NewValueListCell;
2793   PVALUE_LIST_CELL ValueListCell;
2794   PVALUE_CELL NewValueCell;
2795   BLOCK_OFFSET VLBOffset;
2796   BLOCK_OFFSET VBOffset;
2797   NTSTATUS Status;
2798
2799   Status = CmiAllocateValueCell(RegistryHive,
2800                                 &NewValueCell,
2801                                 &VBOffset,
2802                                 ValueName);
2803   if (!NT_SUCCESS(Status))
2804     {
2805       return Status;
2806     }
2807
2808   DPRINT("KeyCell->ValuesOffset %lu\n", (ULONG)KeyCell->ValuesOffset);
2809
2810   ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
2811
2812   if (ValueListCell == NULL)
2813     {
2814       Status = CmiAllocateBlock(RegistryHive,
2815               (PVOID) &ValueListCell,
2816               sizeof(BLOCK_OFFSET) * 3,
2817               &VLBOffset);
2818
2819       if (!NT_SUCCESS(Status))
2820         {
2821           CmiDestroyValueCell(RegistryHive, NewValueCell, VBOffset);
2822           return Status;
2823         }
2824       KeyCell->ValuesOffset = VLBOffset;
2825     }
2826   else if (KeyCell->NumberOfValues >= 
2827            (((ULONG)ABS_VALUE(ValueListCell->CellSize) - 4) / sizeof(BLOCK_OFFSET)))
2828     {
2829       Status = CmiAllocateBlock(RegistryHive,
2830                                 (PVOID) &NewValueListCell,
2831                                 (KeyCell->NumberOfValues + REG_VALUE_LIST_CELL_MULTIPLE) *
2832                                   sizeof(BLOCK_OFFSET),
2833                                 &VLBOffset);
2834       if (!NT_SUCCESS(Status))
2835         {
2836           CmiDestroyValueCell(RegistryHive, NewValueCell, VBOffset);
2837           return Status;
2838         }
2839
2840       RtlCopyMemory(&NewValueListCell->Values[0],
2841                     &ValueListCell->Values[0],
2842                     sizeof(BLOCK_OFFSET) * KeyCell->NumberOfValues);
2843       CmiDestroyBlock(RegistryHive, ValueListCell, KeyCell->ValuesOffset);
2844       KeyCell->ValuesOffset = VLBOffset;
2845       ValueListCell = NewValueListCell;
2846     }
2847
2848   DPRINT("KeyCell->NumberOfValues %lu, ValueListCell->CellSize %lu (%lu %lx)\n",
2849          KeyCell->NumberOfValues,
2850          (ULONG)ABS_VALUE(ValueListCell->CellSize),
2851          ((ULONG)ABS_VALUE(ValueListCell->CellSize) - 4) / sizeof(BLOCK_OFFSET),
2852          ((ULONG)ABS_VALUE(ValueListCell->CellSize) - 4) / sizeof(BLOCK_OFFSET));
2853
2854   ValueListCell->Values[KeyCell->NumberOfValues] = VBOffset;
2855   KeyCell->NumberOfValues++;
2856
2857   *pValueCell = NewValueCell;
2858   *pVBOffset = VBOffset;
2859
2860   return STATUS_SUCCESS;
2861 }
2862
2863
2864 NTSTATUS
2865 CmiDeleteValueFromKey(IN PREGISTRY_HIVE RegistryHive,
2866                       IN PKEY_CELL KeyCell,
2867                       IN BLOCK_OFFSET KeyCellOffset,
2868                       IN PUNICODE_STRING ValueName)
2869 {
2870   PVALUE_LIST_CELL ValueListCell;
2871   PVALUE_CELL CurValueCell;
2872   ULONG  i;
2873
2874   ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
2875
2876   if (ValueListCell == NULL)
2877     {
2878       DPRINT("CmiGetBlock() failed\n");
2879       return STATUS_SUCCESS;
2880     }
2881
2882   VERIFY_VALUE_LIST_CELL(ValueListCell);
2883
2884   for (i = 0; i < KeyCell->NumberOfValues; i++)
2885     {
2886       CurValueCell = CmiGetBlock(RegistryHive, ValueListCell->Values[i], NULL);
2887       if (CurValueCell == NULL)
2888         {
2889           DPRINT("CmiGetBlock() failed\n");
2890           return STATUS_UNSUCCESSFUL;
2891         }
2892
2893       if ((CurValueCell != NULL) &&
2894           CmiComparePackedNames(ValueName,
2895                                 CurValueCell->Name,
2896                                 CurValueCell->NameSize,
2897                                 CurValueCell->Flags & REG_VALUE_NAME_PACKED))
2898         {
2899           CmiDestroyValueCell(RegistryHive, CurValueCell, ValueListCell->Values[i]);
2900
2901           if ((KeyCell->NumberOfValues - 1) < i)
2902             {
2903               RtlCopyMemory(&ValueListCell->Values[i],
2904                             &ValueListCell->Values[i + 1],
2905                             sizeof(BLOCK_OFFSET) * (KeyCell->NumberOfValues - 1 - i));
2906             }
2907           else
2908             {
2909               RtlZeroMemory(&ValueListCell->Values[i], sizeof(BLOCK_OFFSET));
2910             }
2911
2912           KeyCell->NumberOfValues -= 1;
2913           break;
2914         }
2915     }
2916
2917   if (KeyCell->NumberOfValues == 0)
2918     {
2919       CmiDestroyBlock(RegistryHive,
2920                       ValueListCell,
2921                       KeyCell->ValuesOffset);
2922     }
2923   else
2924     {
2925       CmiMarkBlockDirty(RegistryHive,
2926                         KeyCell->ValuesOffset);
2927     }
2928
2929   CmiMarkBlockDirty(RegistryHive,
2930                     KeyCellOffset);
2931
2932   return STATUS_SUCCESS;
2933 }
2934
2935
2936 NTSTATUS
2937 CmiAllocateHashTableBlock(IN PREGISTRY_HIVE RegistryHive,
2938         OUT PHASH_TABLE_CELL *HashBlock,
2939         OUT BLOCK_OFFSET *HBOffset,
2940         IN ULONG HashTableSize)
2941 {
2942   PHASH_TABLE_CELL NewHashBlock;
2943   ULONG NewHashSize;
2944   NTSTATUS Status;
2945
2946   Status = STATUS_SUCCESS;
2947   *HashBlock = NULL;
2948   NewHashSize = sizeof(HASH_TABLE_CELL) + 
2949     (HashTableSize - 1) * sizeof(HASH_RECORD);
2950   Status = CmiAllocateBlock(RegistryHive,
2951                             (PVOID*) &NewHashBlock,
2952                             NewHashSize,
2953                             HBOffset);
2954
2955   if ((NewHashBlock == NULL) || (!NT_SUCCESS(Status)))
2956     {
2957       Status = STATUS_INSUFFICIENT_RESOURCES;
2958     }
2959   else
2960     {
2961       NewHashBlock->Id = REG_HASH_TABLE_BLOCK_ID;
2962       NewHashBlock->HashTableSize = HashTableSize;
2963       *HashBlock = NewHashBlock;
2964     }
2965
2966   return Status;
2967 }
2968
2969
2970 PKEY_CELL
2971 CmiGetKeyFromHashByIndex(PREGISTRY_HIVE RegistryHive,
2972                          PHASH_TABLE_CELL HashBlock,
2973                          ULONG Index)
2974 {
2975   BLOCK_OFFSET KeyOffset;
2976   PKEY_CELL KeyCell;
2977
2978   if (HashBlock == NULL)
2979     return NULL;
2980
2981   if (IsPointerHive(RegistryHive))
2982     {
2983       KeyCell = (PKEY_CELL) HashBlock->Table[Index].KeyOffset;
2984     }
2985   else
2986     {
2987       KeyOffset =  HashBlock->Table[Index].KeyOffset;
2988       KeyCell = CmiGetBlock(RegistryHive, KeyOffset, NULL);
2989     }
2990
2991   return KeyCell;
2992 }
2993
2994
2995 NTSTATUS
2996 CmiAddKeyToHashTable(PREGISTRY_HIVE RegistryHive,
2997                      PHASH_TABLE_CELL HashBlock,
2998                      PKEY_CELL NewKeyCell,
2999                      BLOCK_OFFSET NKBOffset)
3000 {
3001   ULONG i;
3002
3003   for (i = 0; i < HashBlock->HashTableSize; i++)
3004     {
3005       if (HashBlock->Table[i].KeyOffset == 0)
3006         {
3007           HashBlock->Table[i].KeyOffset = NKBOffset;
3008           HashBlock->Table[i].HashValue = 0;
3009           if (NewKeyCell->Flags & REG_KEY_NAME_PACKED)
3010             {
3011               RtlCopyMemory(&HashBlock->Table[i].HashValue,
3012                             NewKeyCell->Name,
3013                             min(NewKeyCell->NameSize, 4));
3014             }
3015           return STATUS_SUCCESS;
3016         }
3017     }
3018
3019   return STATUS_UNSUCCESSFUL;
3020 }
3021
3022
3023 NTSTATUS
3024 CmiRemoveKeyFromHashTable(PREGISTRY_HIVE RegistryHive,
3025                           PHASH_TABLE_CELL HashBlock,
3026                           BLOCK_OFFSET NKBOffset)
3027 {
3028   ULONG i;
3029
3030   for (i = 0; i < HashBlock->HashTableSize; i++)
3031     {
3032       if (HashBlock->Table[i].KeyOffset == NKBOffset)
3033         {
3034           HashBlock->Table[i].KeyOffset = 0;
3035           HashBlock->Table[i].HashValue = 0;
3036           return STATUS_SUCCESS;
3037         }
3038     }
3039
3040   return STATUS_UNSUCCESSFUL;
3041 }
3042
3043
3044 NTSTATUS
3045 CmiAllocateValueCell(PREGISTRY_HIVE RegistryHive,
3046                      PVALUE_CELL *ValueCell,
3047                      BLOCK_OFFSET *VBOffset,
3048                      IN PUNICODE_STRING ValueName)
3049 {
3050   PVALUE_CELL NewValueCell;
3051   NTSTATUS Status;
3052   BOOLEAN Packable;
3053   ULONG NameSize;
3054   ULONG i;
3055
3056   Status = STATUS_SUCCESS;
3057
3058   NameSize = CmiGetPackedNameLength(ValueName,
3059                                     &Packable);
3060
3061   DPRINT("ValueName->Length %lu  NameSize %lu\n", ValueName->Length, NameSize);
3062
3063   Status = CmiAllocateBlock(RegistryHive,
3064                             (PVOID*) &NewValueCell,
3065                             sizeof(VALUE_CELL) + NameSize,
3066                             VBOffset);
3067   if ((NewValueCell == NULL) || (!NT_SUCCESS(Status)))
3068     {
3069       Status = STATUS_INSUFFICIENT_RESOURCES;
3070     }
3071   else
3072     {
3073       NewValueCell->Id = REG_VALUE_CELL_ID;
3074       NewValueCell->NameSize = NameSize;
3075       if (Packable)
3076         {
3077           /* Pack the value name */
3078           for (i = 0; i < NameSize; i++)
3079             NewValueCell->Name[i] = (CHAR)ValueName->Buffer[i];
3080           NewValueCell->Flags |= REG_VALUE_NAME_PACKED;
3081         }
3082       else
3083         {
3084           /* Copy the value name */
3085           RtlCopyMemory(NewValueCell->Name,
3086                         ValueName->Buffer,
3087                         NameSize);
3088           NewValueCell->Flags = 0;
3089         }
3090       NewValueCell->DataType = 0;
3091       NewValueCell->DataSize = 0;
3092       NewValueCell->DataOffset = 0xffffffff;
3093       *ValueCell = NewValueCell;
3094     }
3095
3096   return Status;
3097 }
3098
3099
3100 NTSTATUS
3101 CmiDestroyValueCell(PREGISTRY_HIVE RegistryHive,
3102                     PVALUE_CELL ValueCell,
3103                     BLOCK_OFFSET VBOffset)
3104 {
3105   NTSTATUS Status;
3106   PVOID pBlock;
3107   PHBIN pBin;
3108
3109   DPRINT("CmiDestroyValueCell(Cell %p  Offset %lx)\n", ValueCell, VBOffset);
3110
3111   VERIFY_VALUE_CELL(ValueCell);
3112
3113   /* Destroy the data cell */
3114   if (ValueCell->DataSize > 4)
3115     {
3116       pBlock = CmiGetBlock(RegistryHive, ValueCell->DataOffset, &pBin);
3117       if (pBlock == NULL)
3118         {
3119           DPRINT("CmiGetBlock() failed\n");
3120           return STATUS_UNSUCCESSFUL;
3121         }
3122
3123       Status = CmiDestroyBlock(RegistryHive, pBlock, ValueCell->DataOffset);
3124       if (!NT_SUCCESS(Status))
3125         {
3126           return  Status;
3127         }
3128
3129       /* Update time of heap */
3130       if (!IsNoFileHive(RegistryHive))
3131         NtQuerySystemTime(&pBin->DateModified);
3132     }
3133
3134   /* Destroy the value cell */
3135   Status = CmiDestroyBlock(RegistryHive, ValueCell, VBOffset);
3136
3137   /* Update time of heap */
3138   if (!IsNoFileHive(RegistryHive) && CmiGetBlock(RegistryHive, VBOffset, &pBin))
3139     {
3140       NtQuerySystemTime(&pBin->DateModified);
3141     }
3142
3143   return Status;
3144 }
3145
3146
3147 NTSTATUS
3148 CmiAddBin(PREGISTRY_HIVE RegistryHive,
3149           PVOID *NewBlock,
3150           BLOCK_OFFSET *NewBlockOffset)
3151 {
3152   PCELL_HEADER tmpBlock;
3153   PHBIN * tmpBlockList;
3154   PHBIN tmpBin;
3155
3156   tmpBin = ExAllocatePool(PagedPool, REG_BLOCK_SIZE);
3157   if (tmpBin == NULL)
3158     {
3159       return STATUS_INSUFFICIENT_RESOURCES;
3160     }
3161
3162   tmpBin->BlockId = REG_BIN_ID;
3163   tmpBin->BlockOffset = RegistryHive->FileSize - REG_BLOCK_SIZE;
3164   RegistryHive->FileSize += REG_BLOCK_SIZE;
3165   tmpBin->BlockSize = REG_BLOCK_SIZE;
3166   tmpBin->Unused1 = 0;
3167   ZwQuerySystemTime(&tmpBin->DateModified);
3168   tmpBin->Unused2 = 0;
3169
3170   /* Increase size of list of blocks */
3171   tmpBlockList = ExAllocatePool(NonPagedPool,
3172           sizeof(PHBIN *) * (RegistryHive->BlockListSize + 1));
3173   if (tmpBlockList == NULL)
3174     {
3175       ExFreePool(tmpBin);
3176       return STATUS_INSUFFICIENT_RESOURCES;
3177     }
3178
3179   if (RegistryHive->BlockListSize > 0)
3180     {
3181       RtlCopyMemory (tmpBlockList,
3182                      RegistryHive->BlockList,
3183                      sizeof(PHBIN *)*(RegistryHive->BlockListSize));
3184       ExFreePool(RegistryHive->BlockList);
3185     }
3186
3187   RegistryHive->BlockList = tmpBlockList;
3188   RegistryHive->BlockList[RegistryHive->BlockListSize] = tmpBin;
3189   RegistryHive->BlockListSize++;
3190
3191   /* Initialize a free block in this heap : */
3192   tmpBlock = (PCELL_HEADER)((ULONG_PTR) tmpBin + REG_HBIN_DATA_OFFSET);
3193   tmpBlock->CellSize = (REG_BLOCK_SIZE - REG_HBIN_DATA_OFFSET);
3194
3195   /* Grow bitmap if necessary */
3196   if (IsNoFileHive(RegistryHive) &&
3197       (RegistryHive->BlockListSize % (sizeof(ULONG) * 8) == 0))
3198     {
3199       PULONG BitmapBuffer;
3200       ULONG BitmapSize;
3201
3202       DPRINT("Grow hive bitmap\n");
3203
3204       /* Calculate bitmap size in bytes (always a multiple of 32 bits) */
3205       BitmapSize = ROUND_UP(RegistryHive->BlockListSize, sizeof(ULONG) * 8) / 8;
3206       DPRINT("RegistryHive->BlockListSize: %lu\n", RegistryHive->BlockListSize);
3207       DPRINT("BitmapSize:  %lu Bytes  %lu Bits\n", BitmapSize, BitmapSize * 8);
3208       BitmapBuffer = (PULONG)ExAllocatePool(PagedPool,
3209                                             BitmapSize);
3210       RtlZeroMemory(BitmapBuffer, BitmapSize);
3211       RtlCopyMemory(BitmapBuffer,
3212                     RegistryHive->DirtyBitMap.Buffer,
3213                     RegistryHive->DirtyBitMap.SizeOfBitMap);
3214       ExFreePool(RegistryHive->BitmapBuffer);
3215       RegistryHive->BitmapBuffer = BitmapBuffer;
3216       RtlInitializeBitMap(&RegistryHive->DirtyBitMap,
3217                           RegistryHive->BitmapBuffer,
3218                           BitmapSize * 8);
3219     }
3220
3221   *NewBlock = (PVOID) tmpBlock;
3222
3223   if (NewBlockOffset)
3224     *NewBlockOffset = tmpBin->BlockOffset + REG_HBIN_DATA_OFFSET;
3225
3226   /* Mark new bin dirty */
3227   CmiMarkBinDirty(RegistryHive,
3228                   tmpBin->BlockOffset);
3229
3230   return STATUS_SUCCESS;
3231 }
3232
3233
3234 NTSTATUS
3235 CmiAllocateBlock(PREGISTRY_HIVE RegistryHive,
3236                  PVOID *Block,
3237                  LONG BlockSize,
3238                  BLOCK_OFFSET * pBlockOffset)
3239 {
3240   PCELL_HEADER NewBlock;
3241   NTSTATUS Status;
3242   PHBIN pBin;
3243   ULONG i;
3244   PVOID Temp;
3245
3246   Status = STATUS_SUCCESS;
3247
3248   /* Round to 16 bytes multiple */
3249   BlockSize = (BlockSize + sizeof(DWORD) + 15) & 0xfffffff0;
3250
3251   /* Handle volatile hives first */
3252   if (IsPointerHive(RegistryHive))
3253     {
3254       NewBlock = ExAllocatePool(NonPagedPool, BlockSize);
3255
3256       if (NewBlock == NULL)
3257         {
3258           Status = STATUS_INSUFFICIENT_RESOURCES;
3259         }
3260       else
3261         {
3262           RtlZeroMemory(NewBlock, BlockSize);
3263           NewBlock->CellSize = BlockSize;
3264           *Block = NewBlock;
3265           if (pBlockOffset)
3266             *pBlockOffset = (BLOCK_OFFSET) NewBlock;
3267         }
3268     }
3269   else
3270     {
3271       /* first search in free blocks */
3272       NewBlock = NULL;
3273       for (i = 0; i < RegistryHive->FreeListSize; i++)
3274         {
3275           if (RegistryHive->FreeList[i]->CellSize >= BlockSize)
3276             {
3277               NewBlock = RegistryHive->FreeList[i];
3278               if (pBlockOffset)
3279                 *pBlockOffset = RegistryHive->FreeListOffset[i];
3280
3281               /* Update time of heap */
3282               Temp = CmiGetBlock(RegistryHive, RegistryHive->FreeListOffset[i], &pBin);
3283               if (Temp == NULL)
3284                 {
3285                   DPRINT("CmiGetBlock() failed\n");
3286                   return STATUS_UNSUCCESSFUL;
3287                 }
3288
3289               if (Temp)
3290                 {
3291                   NtQuerySystemTime(&pBin->DateModified);
3292                   CmiMarkBlockDirty(RegistryHive, RegistryHive->FreeListOffset[i]);
3293                 }
3294
3295               if ((i + 1) < RegistryHive->FreeListSize)
3296                 {
3297                   RtlMoveMemory(&RegistryHive->FreeList[i],
3298                                 &RegistryHive->FreeList[i + 1],
3299                                 sizeof(RegistryHive->FreeList[0])
3300                                   * (RegistryHive->FreeListSize - i - 1));
3301                   RtlMoveMemory(&RegistryHive->FreeListOffset[i],
3302                                 &RegistryHive->FreeListOffset[i + 1],
3303                                 sizeof(RegistryHive->FreeListOffset[0])
3304                                   * (RegistryHive->FreeListSize - i - 1));
3305                 }
3306               RegistryHive->FreeListSize--;
3307               break;
3308             }
3309         }
3310
3311       /* Need to extend hive file : */
3312       if (NewBlock == NULL)
3313         {
3314           /* Add a new block */
3315           Status = CmiAddBin(RegistryHive, (PVOID *) &NewBlock , pBlockOffset);
3316         }
3317
3318       if (NT_SUCCESS(Status))
3319         {
3320           *Block = NewBlock;
3321
3322           /* Split the block in two parts */
3323           if (NewBlock->CellSize > BlockSize)
3324             {
3325               NewBlock = (PCELL_HEADER) ((ULONG_PTR) NewBlock+BlockSize);
3326               NewBlock->CellSize = ((PCELL_HEADER) (*Block))->CellSize - BlockSize;
3327               CmiAddFree(RegistryHive,
3328                          NewBlock,
3329                          *pBlockOffset + BlockSize,
3330                          TRUE);
3331               CmiMarkBlockDirty(RegistryHive,
3332                                 *pBlockOffset + BlockSize);
3333             }
3334           else if (NewBlock->CellSize < BlockSize)
3335             {
3336               return(STATUS_UNSUCCESSFUL);
3337             }
3338
3339           RtlZeroMemory(*Block, BlockSize);
3340           ((PCELL_HEADER) (*Block))->CellSize = -BlockSize;
3341         }
3342     }
3343
3344   return(Status);
3345 }
3346
3347
3348 NTSTATUS
3349 CmiDestroyBlock(PREGISTRY_HIVE RegistryHive,
3350                 PVOID Block,
3351                 BLOCK_OFFSET Offset)
3352 {
3353   NTSTATUS Status;
3354   PHBIN pBin;
3355
3356   Status = STATUS_SUCCESS;
3357
3358   if (IsPointerHive(RegistryHive))
3359     {
3360       ExFreePool(Block);
3361     }
3362   else
3363     {
3364       PCELL_HEADER pFree = Block;
3365
3366       if (pFree->CellSize < 0)
3367         pFree->CellSize = -pFree->CellSize;
3368
3369       /* Clear block (except the block size) */
3370       RtlZeroMemory(((PVOID)pFree) + sizeof(ULONG),
3371                     pFree->CellSize - sizeof(ULONG));
3372
3373       /* Add block to the list of free blocks */
3374       CmiAddFree(RegistryHive, Block, Offset, TRUE);
3375
3376       /* Update time of heap */
3377       if (!IsNoFileHive(RegistryHive) && CmiGetBlock(RegistryHive, Offset,&pBin))
3378         NtQuerySystemTime(&pBin->DateModified);
3379
3380       CmiMarkBlockDirty(RegistryHive, Offset);
3381     }
3382
3383   return Status;
3384 }
3385
3386
3387 static BOOLEAN
3388 CmiMergeFree(PREGISTRY_HIVE RegistryHive,
3389              PCELL_HEADER FreeBlock,
3390              BLOCK_OFFSET FreeOffset)
3391 {
3392   BLOCK_OFFSET BlockOffset;
3393   BLOCK_OFFSET BinOffset;
3394   ULONG BlockSize;
3395   ULONG BinSize;
3396   PHBIN Bin;
3397   ULONG i;
3398
3399   DPRINT("CmiMergeFree(Block %lx  Offset %lx  Size %lx) called\n",
3400          FreeBlock, FreeOffset, FreeBlock->CellSize);
3401
3402   CmiGetBlock(RegistryHive,
3403               FreeOffset,
3404               &Bin);
3405   DPRINT("Bin %p\n", Bin);
3406   if (Bin == NULL)
3407     return(FALSE);
3408
3409   BinOffset = Bin->BlockOffset;
3410   BinSize = Bin->BlockSize;
3411   DPRINT("Bin %p  Offset %lx  Size %lx\n", Bin, BinOffset, BinSize);
3412
3413   for (i = 0; i < RegistryHive->FreeListSize; i++)
3414     {
3415       BlockOffset = RegistryHive->FreeListOffset[i];
3416       BlockSize = RegistryHive->FreeList[i]->CellSize;
3417       if (BlockOffset > BinOffset &&
3418           BlockOffset < BinOffset + BinSize)
3419         {
3420           DPRINT("Free block: Offset %lx  Size %lx\n",
3421                   BlockOffset, BlockSize);
3422
3423           if ((i < (RegistryHive->FreeListSize - 1)) &&
3424               (BlockOffset + BlockSize == FreeOffset) &&
3425               (FreeOffset + FreeBlock->CellSize == RegistryHive->FreeListOffset[i + 1]))
3426             {
3427               DPRINT("Merge current block with previous and next block\n");
3428
3429               RegistryHive->FreeList[i]->CellSize +=
3430                 (FreeBlock->CellSize + RegistryHive->FreeList[i + 1]->CellSize);
3431
3432               FreeBlock->CellSize = 0;
3433               RegistryHive->FreeList[i + 1]->CellSize = 0;
3434
3435
3436               if ((i + 2) < RegistryHive->FreeListSize)
3437                 {
3438                   RtlMoveMemory(&RegistryHive->FreeList[i + 1],
3439                                 &RegistryHive->FreeList[i + 2],
3440                                 sizeof(RegistryHive->FreeList[0])
3441                                   * (RegistryHive->FreeListSize - i - 2));
3442                   RtlMoveMemory(&RegistryHive->FreeListOffset[i + 1],
3443                                 &RegistryHive->FreeListOffset[i + 2],
3444                                 sizeof(RegistryHive->FreeListOffset[0])
3445                                   * (RegistryHive->FreeListSize - i - 2));
3446                 }
3447               RegistryHive->FreeListSize--;
3448
3449               CmiMarkBlockDirty(RegistryHive, BlockOffset);
3450
3451               return(TRUE);
3452             }
3453           else if (BlockOffset + BlockSize == FreeOffset)
3454             {
3455               DPRINT("Merge current block with previous block\n");
3456
3457               RegistryHive->FreeList[i]->CellSize += FreeBlock->CellSize;
3458               FreeBlock->CellSize = 0;
3459
3460               CmiMarkBlockDirty(RegistryHive, BlockOffset);
3461
3462               return(TRUE);
3463             }
3464           else if (FreeOffset + FreeBlock->CellSize == BlockOffset)
3465             {
3466               DPRINT("Merge current block with next block\n");
3467
3468               FreeBlock->CellSize += RegistryHive->FreeList[i]->CellSize;
3469               RegistryHive->FreeList[i]->CellSize = 0;
3470               RegistryHive->FreeList[i] = FreeBlock;
3471               RegistryHive->FreeListOffset[i] = FreeOffset;
3472
3473               CmiMarkBlockDirty(RegistryHive, FreeOffset);
3474
3475               return(TRUE);
3476             }
3477         }
3478     }
3479
3480   return(FALSE);
3481 }
3482
3483
3484 NTSTATUS
3485 CmiAddFree(PREGISTRY_HIVE RegistryHive,
3486            PCELL_HEADER FreeBlock,
3487            BLOCK_OFFSET FreeOffset,
3488            BOOLEAN MergeFreeBlocks)
3489 {
3490   PCELL_HEADER *tmpList;
3491   BLOCK_OFFSET *tmpListOffset;
3492   LONG minInd;
3493   LONG maxInd;
3494   LONG medInd;
3495
3496   assert(RegistryHive);
3497   assert(FreeBlock);
3498
3499   DPRINT("FreeBlock %.08lx  FreeOffset %.08lx\n",
3500          FreeBlock, FreeOffset);
3501
3502   /* Merge free blocks */
3503   if (MergeFreeBlocks == TRUE)
3504     {
3505       if (CmiMergeFree(RegistryHive, FreeBlock, FreeOffset))
3506         return(STATUS_SUCCESS);
3507     }
3508
3509   if ((RegistryHive->FreeListSize + 1) > RegistryHive->FreeListMax)
3510     {
3511       tmpList = ExAllocatePool(PagedPool,
3512                           sizeof(PCELL_HEADER) * (RegistryHive->FreeListMax + 32));
3513       if (tmpList == NULL)
3514         return STATUS_INSUFFICIENT_RESOURCES;
3515
3516       tmpListOffset = ExAllocatePool(PagedPool,
3517                           sizeof(BLOCK_OFFSET) * (RegistryHive->FreeListMax + 32));
3518
3519       if (tmpListOffset == NULL)
3520         {
3521           ExFreePool(tmpList);
3522           return STATUS_INSUFFICIENT_RESOURCES;
3523         }
3524
3525       if (RegistryHive->FreeListMax)
3526         {
3527           RtlMoveMemory(tmpList,
3528                         RegistryHive->FreeList,
3529                         sizeof(PCELL_HEADER) * (RegistryHive->FreeListMax));
3530           RtlMoveMemory(tmpListOffset,
3531                         RegistryHive->FreeListOffset,
3532                         sizeof(BLOCK_OFFSET) * (RegistryHive->FreeListMax));
3533           ExFreePool(RegistryHive->FreeList);
3534           ExFreePool(RegistryHive->FreeListOffset);
3535         }
3536       RegistryHive->FreeList = tmpList;
3537       RegistryHive->FreeListOffset = tmpListOffset;
3538       RegistryHive->FreeListMax += 32;
3539     }
3540
3541   /* Add new offset to free list, maintaining list in ascending order */
3542   if ((RegistryHive->FreeListSize == 0)
3543      || (RegistryHive->FreeListOffset[RegistryHive->FreeListSize-1] < FreeOffset))
3544     {
3545       /* Add to end of list */
3546       RegistryHive->FreeList[RegistryHive->FreeListSize] = FreeBlock;
3547       RegistryHive->FreeListOffset[RegistryHive->FreeListSize++] = FreeOffset;
3548     }
3549   else if (RegistryHive->FreeListOffset[0] > FreeOffset)
3550     {
3551       /* Add to begin of list */
3552       RtlMoveMemory(&RegistryHive->FreeList[1],
3553                     &RegistryHive->FreeList[0],
3554                     sizeof(RegistryHive->FreeList[0]) * RegistryHive->FreeListSize);
3555       RtlMoveMemory(&RegistryHive->FreeListOffset[1],
3556                     &RegistryHive->FreeListOffset[0],
3557                     sizeof(RegistryHive->FreeListOffset[0]) * RegistryHive->FreeListSize);
3558       RegistryHive->FreeList[0] = FreeBlock;
3559       RegistryHive->FreeListOffset[0] = FreeOffset;
3560       RegistryHive->FreeListSize++;
3561     }
3562   else
3563     {
3564       /* Search where to insert */
3565       minInd = 0;
3566       maxInd = RegistryHive->FreeListSize - 1;
3567       while ((maxInd - minInd) > 1)
3568         {
3569           medInd = (minInd + maxInd) / 2;
3570           if (RegistryHive->FreeListOffset[medInd] > FreeOffset)
3571             maxInd = medInd;
3572           else
3573             minInd = medInd;
3574         }
3575
3576       /* Insert before maxInd */
3577       RtlMoveMemory(&RegistryHive->FreeList[maxInd+1],
3578                     &RegistryHive->FreeList[maxInd],
3579                     sizeof(RegistryHive->FreeList[0]) * (RegistryHive->FreeListSize - minInd));
3580       RtlMoveMemory(&RegistryHive->FreeListOffset[maxInd + 1],
3581                     &RegistryHive->FreeListOffset[maxInd],
3582                     sizeof(RegistryHive->FreeListOffset[0]) * (RegistryHive->FreeListSize-minInd));
3583       RegistryHive->FreeList[maxInd] = FreeBlock;
3584       RegistryHive->FreeListOffset[maxInd] = FreeOffset;
3585       RegistryHive->FreeListSize++;
3586     }
3587
3588   return STATUS_SUCCESS;
3589 }
3590
3591
3592 PVOID
3593 CmiGetBlock(PREGISTRY_HIVE RegistryHive,
3594             BLOCK_OFFSET BlockOffset,
3595             PHBIN * ppBin)
3596 {
3597   PHBIN pBin;
3598
3599   if (ppBin)
3600     *ppBin = NULL;
3601
3602   if (BlockOffset == (BLOCK_OFFSET)-1)
3603     {
3604       return NULL;
3605     }
3606
3607   if (IsPointerHive (RegistryHive))
3608     {
3609       return (PVOID)BlockOffset;
3610     }
3611   else
3612     {
3613       if (BlockOffset > RegistryHive->BlockListSize * 4096)
3614         {
3615           DPRINT1("BlockOffset exceeds valid range (%lu > %lu)\n",
3616                   BlockOffset, RegistryHive->BlockListSize * 4096);
3617           return NULL;
3618         }
3619       pBin = RegistryHive->BlockList[BlockOffset / 4096];
3620       if (ppBin)
3621         *ppBin = pBin;
3622       return((PVOID)((ULONG_PTR)pBin + (BlockOffset - pBin->BlockOffset)));
3623     }
3624 }
3625
3626
3627 VOID
3628 CmiMarkBlockDirty(PREGISTRY_HIVE RegistryHive,
3629                   BLOCK_OFFSET BlockOffset)
3630 {
3631   PDATA_CELL Cell;
3632   LONG CellSize;
3633   ULONG BlockNumber;
3634   ULONG BlockCount;
3635
3636   if (IsNoFileHive(RegistryHive))
3637     return;
3638
3639   DPRINT("CmiMarkBlockDirty(Offset 0x%lx)\n", (ULONG)BlockOffset);
3640
3641   BlockNumber = (ULONG)BlockOffset / 4096;
3642
3643   Cell = CmiGetBlock(RegistryHive,
3644                      BlockOffset,
3645                      NULL);
3646
3647   CellSize = Cell->CellSize;
3648   if (CellSize < 0)
3649     CellSize = -CellSize;
3650
3651   BlockCount = (ROUND_UP(BlockOffset + CellSize, 4096) - ROUND_DOWN(BlockOffset, 4096)) / 4096;
3652
3653   DPRINT("  BlockNumber %lu  Size %lu (%s)  BlockCount %lu\n",
3654          BlockNumber,
3655          CellSize,
3656          (Cell->CellSize < 0) ? "used" : "free",
3657          BlockCount);
3658
3659   RegistryHive->HiveDirty = TRUE;
3660   RtlSetBits(&RegistryHive->DirtyBitMap,
3661              BlockNumber,
3662              BlockCount);
3663 }
3664
3665
3666 VOID
3667 CmiMarkBinDirty(PREGISTRY_HIVE RegistryHive,
3668                 BLOCK_OFFSET BinOffset)
3669 {
3670   ULONG BlockNumber;
3671   ULONG BlockCount;
3672   PHBIN Bin;
3673
3674   if (IsNoFileHive(RegistryHive))
3675     return;
3676
3677   DPRINT("CmiMarkBinDirty(Offset 0x%lx)\n", (ULONG)BinOffset);
3678
3679   BlockNumber = (ULONG)BinOffset / 4096;
3680
3681   Bin = RegistryHive->BlockList[BlockNumber];
3682
3683   BlockCount = Bin->BlockSize / 4096;
3684
3685   DPRINT("  BlockNumber %lu  Size %lu  BlockCount %lu\n",
3686          BlockNumber,
3687          Bin->BlockSize,
3688          BlockCount);
3689
3690   RegistryHive->HiveDirty = TRUE;
3691   RtlSetBits(&RegistryHive->DirtyBitMap,
3692              BlockNumber,
3693              BlockCount);
3694 }
3695
3696
3697 ULONG
3698 CmiGetPackedNameLength(IN PUNICODE_STRING Name,
3699                        OUT PBOOLEAN Packable)
3700 {
3701   ULONG i;
3702
3703   if (Packable != NULL)
3704     *Packable = TRUE;
3705
3706   for (i = 0; i < Name->Length / sizeof(WCHAR); i++)
3707     {
3708       if (Name->Buffer[i] & 0xFF00)
3709         {
3710           if (Packable != NULL)
3711             *Packable = FALSE;
3712           return Name->Length;
3713         }
3714     }
3715
3716   return (Name->Length / sizeof(WCHAR));
3717 }
3718
3719
3720 BOOLEAN
3721 CmiComparePackedNames(IN PUNICODE_STRING Name,
3722                       IN PCHAR NameBuffer,
3723                       IN USHORT NameBufferSize,
3724                       IN BOOLEAN NamePacked)
3725 {
3726   PWCHAR UNameBuffer;
3727   ULONG i;
3728
3729   if (NamePacked == TRUE)
3730     {
3731       if (Name->Length != NameBufferSize * sizeof(WCHAR))
3732         return(FALSE);
3733
3734       for (i = 0; i < Name->Length / sizeof(WCHAR); i++)
3735         {
3736           if (RtlUpcaseUnicodeChar(Name->Buffer[i]) != RtlUpcaseUnicodeChar((WCHAR)NameBuffer[i]))
3737             return(FALSE);
3738         }
3739     }
3740   else
3741     {
3742       if (Name->Length != NameBufferSize)
3743         return(FALSE);
3744
3745       UNameBuffer = (PWCHAR)NameBuffer;
3746
3747       for (i = 0; i < Name->Length / sizeof(WCHAR); i++)
3748         {
3749           if (RtlUpcaseUnicodeChar(Name->Buffer[i]) != RtlUpcaseUnicodeChar(UNameBuffer[i]))
3750             return(FALSE);
3751         }
3752     }
3753
3754   return(TRUE);
3755 }
3756
3757
3758 VOID
3759 CmiCopyPackedName(PWCHAR NameBuffer,
3760                   PCHAR PackedNameBuffer,
3761                   ULONG PackedNameSize)
3762 {
3763   ULONG i;
3764
3765   for (i = 0; i < PackedNameSize; i++)
3766     NameBuffer[i] = (WCHAR)PackedNameBuffer[i];
3767 }
3768
3769
3770 BOOLEAN
3771 CmiCompareHash(PUNICODE_STRING KeyName,
3772                PCHAR HashString)
3773 {
3774   CHAR Buffer[4];
3775
3776   Buffer[0] = (KeyName->Length >= 2) ? (CHAR)KeyName->Buffer[0] : 0;
3777   Buffer[1] = (KeyName->Length >= 4) ? (CHAR)KeyName->Buffer[1] : 0;
3778   Buffer[2] = (KeyName->Length >= 6) ? (CHAR)KeyName->Buffer[2] : 0;
3779   Buffer[3] = (KeyName->Length >= 8) ? (CHAR)KeyName->Buffer[3] : 0;
3780
3781   return (strncmp(Buffer, HashString, 4) == 0);
3782 }
3783
3784
3785 BOOLEAN
3786 CmiCompareHashI(PUNICODE_STRING KeyName,
3787                 PCHAR HashString)
3788 {
3789   CHAR Buffer[4];
3790
3791   Buffer[0] = (KeyName->Length >= 2) ? (CHAR)KeyName->Buffer[0] : 0;
3792   Buffer[1] = (KeyName->Length >= 4) ? (CHAR)KeyName->Buffer[1] : 0;
3793   Buffer[2] = (KeyName->Length >= 6) ? (CHAR)KeyName->Buffer[2] : 0;
3794   Buffer[3] = (KeyName->Length >= 8) ? (CHAR)KeyName->Buffer[3] : 0;
3795
3796   return (_strnicmp(Buffer, HashString, 4) == 0);
3797 }
3798
3799
3800 BOOLEAN
3801 CmiCompareKeyNames(PUNICODE_STRING KeyName,
3802                    PKEY_CELL KeyCell)
3803 {
3804   PWCHAR UnicodeName;
3805   USHORT i;
3806
3807   DPRINT("Flags: %hx\n", KeyCell->Flags);
3808
3809   if (KeyCell->Flags & REG_KEY_NAME_PACKED)
3810     {
3811       if (KeyName->Length != KeyCell->NameSize * sizeof(WCHAR))
3812         return FALSE;
3813
3814       for (i = 0; i < KeyCell->NameSize; i++)
3815         {
3816           if (KeyName->Buffer[i] != (WCHAR)KeyCell->Name[i])
3817             return FALSE;
3818         }
3819     }
3820   else
3821     {
3822       if (KeyName->Length != KeyCell->NameSize)
3823         return FALSE;
3824
3825       UnicodeName = (PWCHAR)KeyCell->Name;
3826       for (i = 0; i < KeyCell->NameSize / sizeof(WCHAR); i++)
3827         {
3828           if (KeyName->Buffer[i] != UnicodeName[i])
3829             return FALSE;
3830         }
3831     }
3832
3833   return TRUE;
3834 }
3835
3836
3837 BOOLEAN
3838 CmiCompareKeyNamesI(PUNICODE_STRING KeyName,
3839                     PKEY_CELL KeyCell)
3840 {
3841   PWCHAR UnicodeName;
3842   USHORT i;
3843
3844   DPRINT("Flags: %hx\n", KeyCell->Flags);
3845
3846   if (KeyCell->Flags & REG_KEY_NAME_PACKED)
3847     {
3848       if (KeyName->Length != KeyCell->NameSize * sizeof(WCHAR))
3849         return FALSE;
3850
3851       for (i = 0; i < KeyCell->NameSize; i++)
3852         {
3853           if (RtlUpcaseUnicodeChar(KeyName->Buffer[i]) !=
3854               RtlUpcaseUnicodeChar((WCHAR)KeyCell->Name[i]))
3855             return FALSE;
3856         }
3857     }
3858   else
3859     {
3860       if (KeyName->Length != KeyCell->NameSize)
3861         return FALSE;
3862
3863       UnicodeName = (PWCHAR)KeyCell->Name;
3864       for (i = 0; i < KeyCell->NameSize / sizeof(WCHAR); i++)
3865         {
3866           if (RtlUpcaseUnicodeChar(KeyName->Buffer[i]) !=
3867               RtlUpcaseUnicodeChar(UnicodeName[i]))
3868             return FALSE;
3869         }
3870     }
3871
3872   return TRUE;
3873 }
3874
3875
3876 NTSTATUS
3877 CmiCopyKey (PREGISTRY_HIVE DstHive,
3878             PKEY_CELL DstKeyCell,
3879             PREGISTRY_HIVE SrcHive,
3880             PKEY_CELL SrcKeyCell)
3881 {
3882   PKEY_CELL NewKeyCell;
3883   ULONG NewKeyCellSize;
3884   BLOCK_OFFSET NewKeyCellOffset;
3885   PHASH_TABLE_CELL NewHashTableCell;
3886   ULONG NewHashTableSize;
3887   BLOCK_OFFSET NewHashTableOffset;
3888   ULONG i;
3889   NTSTATUS Status;
3890
3891   DPRINT ("CmiCopyKey() called\n");
3892
3893   if (DstKeyCell == NULL)
3894     {
3895       /* Allocate and copy key cell */
3896       NewKeyCellSize = sizeof(KEY_CELL) + SrcKeyCell->NameSize;
3897       Status = CmiAllocateBlock (DstHive,
3898                                  (PVOID) &NewKeyCell,
3899                                  NewKeyCellSize,
3900                                  &NewKeyCellOffset);
3901       if (!NT_SUCCESS(Status))
3902         {
3903           DPRINT1 ("CmiAllocateBlock() failed (Status %lx)\n", Status);
3904           return Status;
3905         }
3906       if (NewKeyCell == NULL)
3907         {
3908           DPRINT1 ("Failed to allocate a key cell\n");
3909           return STATUS_INSUFFICIENT_RESOURCES;
3910         }
3911
3912       RtlCopyMemory (NewKeyCell,
3913                      SrcKeyCell,
3914                      NewKeyCellSize);
3915
3916       DstHive->HiveHeader->RootKeyCell = NewKeyCellOffset;
3917
3918       /* Copy class name */
3919       if (SrcKeyCell->ClassNameOffset != (BLOCK_OFFSET) -1)
3920         {
3921           PDATA_CELL SrcClassNameCell;
3922           PDATA_CELL NewClassNameCell;
3923           BLOCK_OFFSET NewClassNameOffset;
3924
3925           SrcClassNameCell = CmiGetBlock (SrcHive, SrcKeyCell->ClassNameOffset, NULL),
3926
3927           NewKeyCell->ClassSize = SrcKeyCell->ClassSize;
3928           Status = CmiAllocateBlock (DstHive,
3929                                      (PVOID)&NewClassNameCell,
3930                                      NewKeyCell->ClassSize,
3931                                      &NewClassNameOffset);
3932           if (!NT_SUCCESS(Status))
3933             {
3934               DPRINT1 ("CmiAllocateBlock() failed (Status %lx)\n", Status);
3935               return Status;
3936             }
3937
3938           RtlCopyMemory (NewClassNameCell,
3939                          SrcClassNameCell,
3940                          NewKeyCell->ClassSize);
3941           NewKeyCell->ClassNameOffset = NewClassNameOffset;
3942         }
3943     }
3944   else
3945     {
3946       NewKeyCell = DstKeyCell;
3947     }
3948
3949   /* Allocate hash table */
3950   if (SrcKeyCell->NumberOfSubKeys > 0)
3951     {
3952       NewHashTableSize = ROUND_UP(SrcKeyCell->NumberOfSubKeys + 1, 4) - 1;
3953       Status = CmiAllocateHashTableBlock (DstHive,
3954                                           &NewHashTableCell,
3955                                           &NewHashTableOffset,
3956                                           NewHashTableSize);
3957       if (!NT_SUCCESS(Status))
3958         {
3959           DPRINT1 ("CmiAllocateHashTableBlock() failed (Status %lx)\n", Status);
3960           return Status;
3961         }
3962       NewKeyCell->HashTableOffset = NewHashTableOffset;
3963     }
3964
3965   /* Allocate and copy value list and values */
3966   if (SrcKeyCell->NumberOfValues != 0)
3967     {
3968       PVALUE_LIST_CELL NewValueListCell;
3969       PVALUE_LIST_CELL SrcValueListCell;
3970       PVALUE_CELL NewValueCell;
3971       PVALUE_CELL SrcValueCell;
3972       PDATA_CELL SrcValueDataCell;
3973       PDATA_CELL NewValueDataCell;
3974       BLOCK_OFFSET ValueCellOffset;
3975       BLOCK_OFFSET ValueDataCellOffset;
3976       ULONG NewValueListCellSize;
3977       ULONG NewValueCellSize;
3978
3979
3980       NewValueListCellSize =
3981         ROUND_UP(SrcKeyCell->NumberOfValues, 4) * sizeof(BLOCK_OFFSET);
3982       Status = CmiAllocateBlock (DstHive,
3983                                  (PVOID)&NewValueListCell,
3984                                  NewValueListCellSize,
3985                                  &NewKeyCell->ValuesOffset);
3986       if (!NT_SUCCESS(Status))
3987         {
3988           DPRINT1 ("CmiAllocateBlock() failed (Status %lx)\n", Status);
3989           return Status;
3990         }
3991
3992       RtlZeroMemory (NewValueListCell,
3993                      NewValueListCellSize);
3994
3995       /* Copy values */
3996       SrcValueListCell = CmiGetBlock (SrcHive, SrcKeyCell->ValuesOffset, NULL);
3997       for (i = 0; i < SrcKeyCell->NumberOfValues; i++)
3998         {
3999           /* Copy value cell */
4000           SrcValueCell = CmiGetBlock (SrcHive, SrcValueListCell->Values[i], NULL);
4001
4002           NewValueCellSize = sizeof(VALUE_CELL) + SrcValueCell->NameSize;
4003           Status = CmiAllocateBlock (DstHive,
4004                                      (PVOID*) &NewValueCell,
4005                                      NewValueCellSize,
4006                                      &ValueCellOffset);
4007           if (!NT_SUCCESS(Status))
4008             {
4009               DPRINT1 ("CmiAllocateBlock() failed (Status %lx)\n", Status);
4010               return Status;
4011             }
4012
4013           NewValueListCell->Values[i] = ValueCellOffset;
4014           RtlCopyMemory (NewValueCell,
4015                          SrcValueCell,
4016                          NewValueCellSize);
4017
4018           /* Copy value data cell */
4019           if (SrcValueCell->DataSize > (LONG) sizeof(PVOID))
4020             {
4021               SrcValueDataCell = CmiGetBlock (SrcHive, SrcValueCell->DataOffset, NULL);
4022
4023               Status = CmiAllocateBlock (DstHive,
4024                                          (PVOID*) &NewValueDataCell,
4025                                          SrcValueCell->DataSize,
4026                                          &ValueDataCellOffset);
4027               if (!NT_SUCCESS(Status))
4028                 {
4029                   DPRINT1 ("CmiAllocateBlock() failed (Status %lx)\n", Status);
4030                   return Status;
4031                 }
4032               RtlCopyMemory (NewValueDataCell,
4033                              SrcValueDataCell,
4034                              SrcValueCell->DataSize);
4035               NewValueCell->DataOffset = ValueDataCellOffset;
4036             }
4037         }
4038     }
4039
4040   /* Copy subkeys */
4041   if (SrcKeyCell->NumberOfSubKeys > 0)
4042     {
4043       PHASH_TABLE_CELL SrcHashTableCell;
4044       PKEY_CELL SrcSubKeyCell;
4045       PKEY_CELL NewSubKeyCell;
4046       ULONG NewSubKeyCellSize;
4047       BLOCK_OFFSET NewSubKeyCellOffset;
4048       PHASH_RECORD SrcHashRecord;
4049
4050       SrcHashTableCell = CmiGetBlock (SrcHive,
4051                                       SrcKeyCell->HashTableOffset,
4052                                       NULL);
4053
4054       for (i = 0; i < SrcKeyCell->NumberOfSubKeys; i++)
4055         {
4056           SrcHashRecord = &SrcHashTableCell->Table[i];
4057           SrcSubKeyCell = CmiGetBlock (SrcHive, SrcHashRecord->KeyOffset, NULL);
4058
4059           /* Allocate and copy key cell */
4060           NewSubKeyCellSize = sizeof(KEY_CELL) + SrcSubKeyCell->NameSize;
4061           Status = CmiAllocateBlock (DstHive,
4062                                      (PVOID)&NewSubKeyCell,
4063                                      NewSubKeyCellSize,
4064                                      &NewSubKeyCellOffset);
4065           if (!NT_SUCCESS(Status))
4066             {
4067               DPRINT1 ("CmiAllocateBlock() failed (Status %lx)\n", Status);
4068               return Status;
4069             }
4070           if (NewKeyCell == NULL)
4071             {
4072               DPRINT1 ("Failed to allocate a sub key cell\n");
4073               return STATUS_INSUFFICIENT_RESOURCES;
4074             }
4075
4076           NewHashTableCell->Table[i].KeyOffset = NewSubKeyCellOffset;
4077           NewHashTableCell->Table[i].HashValue = SrcHashRecord->HashValue;
4078
4079           RtlCopyMemory (NewSubKeyCell,
4080                          SrcSubKeyCell,
4081                          NewSubKeyCellSize);
4082
4083           /* Copy class name */
4084           if (SrcSubKeyCell->ClassNameOffset != (BLOCK_OFFSET) -1)
4085             {
4086               PDATA_CELL SrcClassNameCell;
4087               PDATA_CELL NewClassNameCell;
4088               BLOCK_OFFSET NewClassNameOffset;
4089
4090               SrcClassNameCell = CmiGetBlock (SrcHive,
4091                                               SrcSubKeyCell->ClassNameOffset,
4092                                               NULL),
4093
4094               NewSubKeyCell->ClassSize = SrcSubKeyCell->ClassSize;
4095               Status = CmiAllocateBlock (DstHive,
4096                                          (PVOID)&NewClassNameCell,
4097                                          NewSubKeyCell->ClassSize,
4098                                          &NewClassNameOffset);
4099               if (!NT_SUCCESS(Status))
4100                 {
4101                   DPRINT1 ("CmiAllocateBlock() failed (Status %lx)\n", Status);
4102                   return Status;
4103                 }
4104
4105               NewSubKeyCell->ClassNameOffset = NewClassNameOffset;
4106               RtlCopyMemory (NewClassNameCell,
4107                              SrcClassNameCell,
4108                              NewSubKeyCell->ClassSize);
4109             }
4110
4111           /* Copy subkey data and subkeys */
4112           Status = CmiCopyKey (DstHive,
4113                                NewSubKeyCell,
4114                                SrcHive,
4115                                SrcSubKeyCell);
4116           if (!NT_SUCCESS(Status))
4117             {
4118               DPRINT1 ("CmiAllocateBlock() failed (Status %lx)\n", Status);
4119               return Status;
4120             }
4121         }
4122     }
4123
4124   return STATUS_SUCCESS;
4125 }
4126
4127
4128 NTSTATUS
4129 CmiSaveTempHive (PREGISTRY_HIVE Hive,
4130                  HANDLE FileHandle)
4131 {
4132   IO_STATUS_BLOCK IoStatusBlock;
4133   LARGE_INTEGER FileOffset;
4134   ULONG BlockIndex;
4135   PVOID BlockPtr;
4136   NTSTATUS Status;
4137
4138   DPRINT ("CmiSaveTempHive() called\n");
4139
4140   Hive->HiveHeader->Checksum = CmiCalcChecksum ((PULONG)Hive->HiveHeader);
4141
4142   /* Write hive block */
4143   FileOffset.QuadPart = 0ULL;
4144   Status = NtWriteFile (FileHandle,
4145                         NULL,
4146                         NULL,
4147                         NULL,
4148                         &IoStatusBlock,
4149                         Hive->HiveHeader,
4150                         sizeof(HIVE_HEADER),
4151                         &FileOffset,
4152                         NULL);
4153   if (!NT_SUCCESS(Status))
4154     {
4155       DPRINT1 ("NtWriteFile() failed (Status %lx)\n", Status);
4156       return Status;
4157     }
4158
4159   DPRINT ("Saving %lu blocks\n", Hive->BlockListSize);
4160   for (BlockIndex = 0; BlockIndex < Hive->BlockListSize; BlockIndex++)
4161     {
4162       BlockPtr = Hive->BlockList[BlockIndex];
4163       DPRINT ("BlockPtr %p\n", BlockPtr);
4164
4165       FileOffset.QuadPart = (ULONGLONG)(BlockIndex + 1) * 4096ULL;
4166       DPRINT ("File offset %I64x\n", FileOffset.QuadPart);
4167
4168       /* Write hive block */
4169       Status = NtWriteFile (FileHandle,
4170                             NULL,
4171                             NULL,
4172                             NULL,
4173                             &IoStatusBlock,
4174                             BlockPtr,
4175                             REG_BLOCK_SIZE,
4176                             &FileOffset,
4177                             NULL);
4178       if (!NT_SUCCESS(Status))
4179         {
4180           DPRINT1 ("NtWriteFile() failed (Status %lx)\n", Status);
4181           return Status;
4182         }
4183     }
4184
4185   Status = NtFlushBuffersFile (FileHandle,
4186                                &IoStatusBlock);
4187   if (!NT_SUCCESS(Status))
4188     {
4189       DPRINT1 ("NtFlushBuffersFile() failed (Status %lx)\n", Status);
4190     }
4191
4192   DPRINT ("CmiSaveTempHive() done\n");
4193
4194   return Status;
4195 }
4196
4197 /* EOF */