update for HEAD-2003021201
[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 #ifdef WIN32_REGDBG
10 #include "cm_win32.h"
11 #else
12 #include <ddk/ntddk.h>
13 #include <ddk/ntifs.h>
14 #include <roscfg.h>
15 #include <internal/ob.h>
16 #include <limits.h>
17 #include <string.h>
18 #include <internal/pool.h>
19 #include <internal/registry.h>
20
21 #define NDEBUG
22 #include <internal/debug.h>
23
24 #include "cm.h"
25 #endif
26
27 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
28
29 BOOLEAN CmiDoVerify = FALSE;
30
31 /* FUNCTIONS ****************************************************************/
32
33 VOID
34 CmiCreateDefaultHiveHeader(PHIVE_HEADER Header)
35 {
36   assert(Header);
37   RtlZeroMemory(Header, sizeof(HIVE_HEADER));
38   Header->BlockId = REG_HIVE_ID;
39   Header->DateModified.dwLowDateTime = 0;
40   Header->DateModified.dwHighDateTime = 0;
41   Header->Version = 1;
42   Header->Unused3 = 1;
43   Header->Unused4 = 3;
44   Header->Unused5 = 0;
45   Header->Unused6 = 1;
46   Header->Unused7 = 1;
47   Header->RootKeyCell = 0;
48   Header->BlockSize = REG_BLOCK_SIZE;
49   Header->Unused6 = 1;
50   Header->Checksum = 0;
51 }
52
53
54 VOID
55 CmiCreateDefaultBinCell(PHBIN BinCell)
56 {
57   assert(BinCell);
58   RtlZeroMemory(BinCell, sizeof(HBIN));
59   BinCell->BlockId = REG_BIN_ID;
60   BinCell->DateModified.dwLowDateTime = 0;
61   BinCell->DateModified.dwHighDateTime = 0;
62   BinCell->BlockSize = REG_BLOCK_SIZE;
63 }
64
65
66 VOID
67 CmiCreateDefaultRootKeyCell(PKEY_CELL RootKeyCell)
68 {
69   assert(RootKeyCell);
70   RtlZeroMemory(RootKeyCell, sizeof(KEY_CELL));
71 #ifdef WIN32_REGDBG
72   RootKeyCell->CellSize = -(LONG)sizeof(KEY_CELL);
73 #else
74   RootKeyCell->CellSize = -sizeof(KEY_CELL);
75 #endif
76   RootKeyCell->Id = REG_KEY_CELL_ID;
77   RootKeyCell->Type = REG_ROOT_KEY_CELL_TYPE;
78   ZwQuerySystemTime((PTIME) &RootKeyCell->LastWriteTime);
79   RootKeyCell->ParentKeyOffset = 0;
80   RootKeyCell->NumberOfSubKeys = 0;
81   RootKeyCell->HashTableOffset = -1;
82   RootKeyCell->NumberOfValues = 0;
83   RootKeyCell->ValuesOffset = -1;
84   RootKeyCell->SecurityKeyOffset = 0;
85   RootKeyCell->ClassNameOffset = -1;
86   RootKeyCell->NameSize = 0;
87   RootKeyCell->ClassSize = 0;
88 }
89
90
91 VOID
92 CmiVerifyBinCell(PHBIN BinCell)
93 {
94   if (CmiDoVerify)
95     {
96
97   assert(BinCell);
98
99   if (BinCell->BlockId != REG_BIN_ID)
100     {
101       DbgPrint("BlockId is %.08x (should be %.08x)\n",
102         BinCell->BlockId, REG_BIN_ID);
103       assert(BinCell->BlockId == REG_BIN_ID);
104     }
105
106   //BinCell->DateModified.dwLowDateTime
107
108   //BinCell->DateModified.dwHighDateTime
109
110   
111   if (BinCell->BlockSize != REG_BLOCK_SIZE)
112     {
113       DbgPrint("BlockSize is %.08x (should be %.08x)\n",
114         BinCell->BlockSize, REG_BLOCK_SIZE);
115       assert(BinCell->BlockSize == REG_BLOCK_SIZE);
116     }
117
118     }
119 }
120
121
122 VOID
123 CmiVerifyKeyCell(PKEY_CELL KeyCell)
124 {
125   if (CmiDoVerify)
126     {
127
128   assert(KeyCell);
129
130   if (KeyCell->CellSize == 0)
131     {
132       DbgPrint("CellSize is %d (must not be 0)\n",
133         KeyCell->CellSize);
134       assert(KeyCell->CellSize != 0);
135     }
136
137   if (KeyCell->Id != REG_KEY_CELL_ID)
138     {
139       DbgPrint("Id is %.08x (should be %.08x)\n",
140         KeyCell->Id, REG_KEY_CELL_ID);
141       assert(KeyCell->Id == REG_KEY_CELL_ID);
142     }
143
144   if ((KeyCell->Type != REG_KEY_CELL_TYPE)
145     && (KeyCell->Type != REG_ROOT_KEY_CELL_TYPE))
146     {
147       DbgPrint("Type is %.08x (should be %.08x or %.08x)\n",
148         KeyCell->Type, REG_KEY_CELL_TYPE, REG_ROOT_KEY_CELL_TYPE);
149       assert(FALSE);
150     }
151
152   //KeyCell->LastWriteTime;
153
154   if (KeyCell->ParentKeyOffset < 0)
155     {
156       DbgPrint("ParentKeyOffset is %d (must not be < 0)\n",
157         KeyCell->ParentKeyOffset);
158       assert(KeyCell->ParentKeyOffset >= 0);
159     }
160
161   if (KeyCell->NumberOfSubKeys < 0)
162     {
163       DbgPrint("NumberOfSubKeys is %d (must not be < 0)\n",
164         KeyCell->NumberOfSubKeys);
165       assert(KeyCell->NumberOfSubKeys >= 0);
166     }
167
168   //KeyCell->HashTableOffset;
169
170   if (KeyCell->NumberOfValues < 0)
171     {
172       DbgPrint("NumberOfValues is %d (must not be < 0)\n",
173         KeyCell->NumberOfValues);
174       assert(KeyCell->NumberOfValues >= 0);
175     }
176
177   //KeyCell->ValuesOffset = -1;
178
179   if (KeyCell->SecurityKeyOffset < 0)
180     {
181       DbgPrint("SecurityKeyOffset is %d (must not be < 0)\n",
182         KeyCell->SecurityKeyOffset);
183       assert(KeyCell->SecurityKeyOffset >= 0);
184     }
185
186   //KeyCell->ClassNameOffset = -1;
187
188   //KeyCell->NameSize
189
190   //KeyCell->ClassSize
191
192     }
193 }
194
195
196 VOID
197 CmiVerifyRootKeyCell(PKEY_CELL RootKeyCell)
198 {
199   if (CmiDoVerify)
200     {
201
202   CmiVerifyKeyCell(RootKeyCell);
203
204   if (RootKeyCell->Type != REG_ROOT_KEY_CELL_TYPE)
205     {
206       DbgPrint("Type is %.08x (should be %.08x)\n",
207         RootKeyCell->Type, REG_ROOT_KEY_CELL_TYPE);
208       assert(RootKeyCell->Type == REG_ROOT_KEY_CELL_TYPE);
209     }
210
211     }
212 }
213
214
215 VOID
216 CmiVerifyValueCell(PVALUE_CELL ValueCell)
217 {
218   if (CmiDoVerify)
219     {
220
221   assert(ValueCell);
222
223   if (ValueCell->CellSize == 0)
224     {
225       DbgPrint("CellSize is %d (must not be 0)\n",
226         ValueCell->CellSize);
227       assert(ValueCell->CellSize != 0);
228     }
229
230   if (ValueCell->Id != REG_VALUE_CELL_ID)
231     {
232       DbgPrint("Id is %.08x (should be %.08x)\n",
233         ValueCell->Id, REG_VALUE_CELL_ID);
234       assert(ValueCell->Id == REG_VALUE_CELL_ID);
235     }
236
237   //ValueCell->NameSize;
238   //ValueCell->LONG  DataSize;
239   //ValueCell->DataOffset;
240   //ValueCell->ULONG  DataType;
241   //ValueCell->USHORT Flags;
242   //ValueCell->USHORT Unused1;
243   //ValueCell->UCHAR  Name[0];
244     }
245 }
246
247
248 VOID
249 CmiVerifyValueListCell(PVALUE_LIST_CELL ValueListCell)
250 {
251   if (CmiDoVerify)
252     {
253
254   if (ValueListCell->CellSize == 0)
255     {
256       DbgPrint("CellSize is %d (must not be 0)\n",
257         ValueListCell->CellSize);
258       assert(ValueListCell->CellSize != 0);
259     }
260
261     }
262 }
263
264
265 VOID
266 CmiVerifyKeyObject(PKEY_OBJECT KeyObject)
267 {
268   if (CmiDoVerify)
269     {
270
271   if (KeyObject->RegistryHive == NULL)
272     {
273       DbgPrint("RegistryHive is NULL (must not be NULL)\n",
274         KeyObject->RegistryHive);
275       assert(KeyObject->RegistryHive != NULL);
276     }
277
278   if (KeyObject->KeyCell == NULL)
279     {
280       DbgPrint("KeyCell is NULL (must not be NULL)\n",
281         KeyObject->KeyCell);
282       assert(KeyObject->KeyCell != NULL);
283     }
284
285   if (KeyObject->ParentKey == NULL)
286     {
287       DbgPrint("ParentKey is NULL (must not be NULL)\n",
288         KeyObject->ParentKey);
289       assert(KeyObject->ParentKey != NULL);
290     }
291
292     }
293 }
294
295
296 VOID
297 CmiVerifyHiveHeader(PHIVE_HEADER Header)
298 {
299   if (CmiDoVerify)
300     {
301
302   if (Header->BlockId != REG_HIVE_ID)
303     {
304       DbgPrint("BlockId is %.08x (must be %.08x)\n",
305         Header->BlockId,
306         REG_HIVE_ID);
307       assert(Header->BlockId == REG_HIVE_ID);
308     }
309
310   if (Header->Unused3 != 1)
311     {
312       DbgPrint("Unused3 is %.08x (must be 1)\n",
313         Header->Unused3);
314       assert(Header->Unused3 == 1);
315     }
316
317   if (Header->Unused4 != 3)
318     {
319       DbgPrint("Unused4 is %.08x (must be 3)\n",
320         Header->Unused4);
321       assert(Header->Unused4 == 3);
322     }
323
324   if (Header->Unused5 != 0)
325     {
326       DbgPrint("Unused5 is %.08x (must be 0)\n",
327         Header->Unused5);
328       assert(Header->Unused5 == 0);
329     }
330
331   if (Header->Unused6 != 1)
332     {
333       DbgPrint("Unused6 is %.08x (must be 1)\n",
334         Header->Unused6);
335       assert(Header->Unused6 == 1);
336     }
337
338   if (Header->Unused7 != 1)
339     {
340       DbgPrint("Unused7 is %.08x (must be 1)\n",
341         Header->Unused7);
342       assert(Header->Unused7 == 1);
343     }
344
345     }  
346 }
347
348
349 VOID
350 CmiVerifyRegistryHive(PREGISTRY_HIVE RegistryHive)
351 {
352   if (CmiDoVerify)
353     {
354
355       CmiVerifyHiveHeader(RegistryHive->HiveHeader);
356
357     }
358 }
359
360
361 NTSTATUS
362 CmiPopulateHive(HANDLE FileHandle)
363 {
364   IO_STATUS_BLOCK IoStatusBlock;
365   LARGE_INTEGER FileOffset;
366   PCELL_HEADER FreeCell;
367   NTSTATUS Status;
368   PHBIN BinCell;
369   PCHAR tBuf;
370   ULONG i;
371
372   tBuf = (PCHAR) ExAllocatePool(NonPagedPool, REG_BLOCK_SIZE);
373   if (tBuf == NULL)
374     return STATUS_INSUFFICIENT_RESOURCES;
375
376   BinCell = (PHBIN) tBuf;
377   FreeCell = (PCELL_HEADER) (tBuf + REG_HBIN_DATA_OFFSET);
378
379   CmiCreateDefaultBinCell(BinCell);
380
381   // The whole block is free
382   FreeCell->CellSize = REG_BLOCK_SIZE - REG_HBIN_DATA_OFFSET;
383
384   // Add free blocks so we don't need to expand
385   // the file for a while
386   for (i = 0; i < 50; i++)
387     {
388       // Block offset of this bin
389       BinCell->BlockOffset = (2 + i) * REG_BLOCK_SIZE;
390
391       FileOffset.u.HighPart = 0;
392       FileOffset.u.LowPart   = (2 + i) * REG_BLOCK_SIZE;
393
394       Status = ZwWriteFile(FileHandle,
395                            NULL,
396                            NULL,
397                            NULL,
398                            &IoStatusBlock,
399                            tBuf,
400                            REG_BLOCK_SIZE,
401                            &FileOffset,
402                            NULL);
403       assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
404       if (!NT_SUCCESS(Status))
405         {
406           ExFreePool(tBuf);
407           return Status;
408         }
409     }
410
411   ExFreePool(tBuf);
412
413   return Status;
414 }
415
416
417 NTSTATUS
418 CmiCreateNewRegFile(HANDLE FileHandle)
419 {
420   IO_STATUS_BLOCK IoStatusBlock;
421   PCELL_HEADER FreeCell;
422   PHIVE_HEADER HiveHeader;
423   PKEY_CELL RootKeyCell;
424   NTSTATUS Status;
425   PHBIN BinCell;
426   PCHAR Buffer;
427
428   Buffer = (PCHAR) ExAllocatePool(NonPagedPool, 2 * REG_BLOCK_SIZE);
429   if (Buffer == NULL)
430     return STATUS_INSUFFICIENT_RESOURCES;
431
432   HiveHeader = (PHIVE_HEADER)Buffer;
433   BinCell = (PHBIN)((ULONG_PTR)Buffer + REG_BLOCK_SIZE);
434   RootKeyCell = (PKEY_CELL)((ULONG_PTR)Buffer + REG_BLOCK_SIZE + REG_HBIN_DATA_OFFSET);
435   FreeCell = (PCELL_HEADER)((ULONG_PTR)Buffer + REG_BLOCK_SIZE + REG_HBIN_DATA_OFFSET + sizeof(KEY_CELL));
436
437   CmiCreateDefaultHiveHeader(HiveHeader);
438   CmiCreateDefaultBinCell(BinCell);
439   CmiCreateDefaultRootKeyCell(RootKeyCell);
440
441   /* First block */
442   BinCell->BlockOffset = 0;
443
444   /* Offset to root key block */
445   HiveHeader->RootKeyCell = REG_HBIN_DATA_OFFSET;
446
447   /* The rest of the block is free */
448   FreeCell->CellSize = REG_BLOCK_SIZE - (REG_HBIN_DATA_OFFSET + sizeof(KEY_CELL));
449
450   Status = ZwWriteFile(FileHandle,
451                        NULL,
452                        NULL,
453                        NULL,
454                        &IoStatusBlock,
455                        Buffer,
456                        2 * REG_BLOCK_SIZE,
457                        0,
458                        NULL);
459
460   ExFreePool(Buffer);
461
462   assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
463
464 #if 1
465   if (NT_SUCCESS(Status))
466     {
467       CmiPopulateHive(FileHandle);
468     }
469 #endif
470
471   return Status;
472 }
473
474
475 NTSTATUS
476 CmiInitPermanentRegistryHive(PREGISTRY_HIVE RegistryHive,
477                              PWSTR Filename,
478                              BOOLEAN CreateNew)
479 {
480   OBJECT_ATTRIBUTES ObjectAttributes;
481   FILE_STANDARD_INFORMATION fsi;
482   PCELL_HEADER FreeBlock;
483   LARGE_INTEGER FileOffset;
484   BLOCK_OFFSET BlockOffset;
485   ULONG CreateDisposition;
486   IO_STATUS_BLOCK IoSB;
487   HANDLE FileHandle;
488   ULONG FreeOffset;
489   NTSTATUS Status;
490   PHBIN tmpBin;
491   ULONG i, j;
492   ULONG BitmapSize;
493   PULONG BitmapBuffer;
494
495   DPRINT1("CmiInitPermanentRegistryHive(%p, %S, %d) - Entered.\n", RegistryHive, Filename, CreateNew);
496
497   /* Duplicate Filename */
498   Status = RtlCreateUnicodeString(&RegistryHive->Filename, Filename);
499   if (!NT_SUCCESS(Status))
500     {
501       DPRINT1("CmiInitPermanentRegistryHive() - Failed 1.\n");
502       return Status;
503     }
504
505   InitializeObjectAttributes(&ObjectAttributes,
506                              &RegistryHive->Filename,
507                              0,
508                              NULL,
509                              NULL);
510
511   /*
512    * Note:
513    * This is a workaround to prevent a BSOD because of missing registry hives.
514    * The workaround is only useful for developers. An implementation for the
515    * ordinary user must bail out on missing registry hives because they are
516    * essential to booting and configuring the OS.
517    */
518 #if 0
519   if (CreateNew == TRUE)
520     CreateDisposition = FILE_OPEN_IF;
521   else
522     CreateDisposition = FILE_OPEN;
523 #endif
524   CreateDisposition = FILE_OPEN_IF;
525
526   Status = NtCreateFile(&FileHandle,
527                         FILE_ALL_ACCESS,
528                         &ObjectAttributes,
529                         &IoSB,
530                         NULL,
531                         FILE_ATTRIBUTE_NORMAL,
532                         0,
533                         CreateDisposition,
534                         FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
535                         NULL,
536                         0);
537   if (!NT_SUCCESS(Status))
538     {
539       RtlFreeUnicodeString(&RegistryHive->Filename);
540       DPRINT1("NtCreateFile() failed (Status %lx)\n", Status);
541       return(Status);
542     }
543
544   /* Note: Another workaround! See the note above! */
545 #if 0
546   if ((CreateNew) && (IoSB.Information == FILE_CREATED))
547 #endif
548   if (IoSB.Information != FILE_OPENED)
549     {
550       Status = CmiCreateNewRegFile(FileHandle);
551       if (!NT_SUCCESS(Status))
552         {
553           DPRINT1("CmiCreateNewRegFile() failed (Status %lx)\n", Status);
554           RtlFreeUnicodeString(&RegistryHive->Filename);
555           return(Status);
556         }
557     }
558
559   Status = ObReferenceObjectByHandle(FileHandle,
560                                      FILE_ALL_ACCESS,
561                                      IoFileObjectType,
562                                      UserMode,
563                                      (PVOID*)&RegistryHive->FileObject,
564                                      NULL);
565
566   assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
567
568   if (!NT_SUCCESS(Status))
569     {
570       NtClose(FileHandle);
571       RtlFreeUnicodeString(&RegistryHive->Filename);
572       DPRINT1("CmiInitPermanentRegistryHive() - ObReferenceObjectByHandle Failed with status %x.\n", Status);
573       return Status;
574     }
575
576   /* Read hive header */
577   FileOffset.u.HighPart = 0;
578   FileOffset.u.LowPart = 0;
579   DPRINT("    Attempting to ZwReadFile(%d) for %d bytes into %p\n", FileHandle, sizeof(HIVE_HEADER), RegistryHive->HiveHeader);
580   Status = NtReadFile(FileHandle,
581                       0,
582                       0,
583                       0,
584                       &IoSB,
585                       RegistryHive->HiveHeader,
586                       sizeof(HIVE_HEADER),
587                       &FileOffset,
588                       0);
589   assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
590   if (!NT_SUCCESS(Status))
591     {
592       ObDereferenceObject(RegistryHive->FileObject);
593       RtlFreeUnicodeString(&RegistryHive->Filename);
594       DPRINT("CmiInitPermanentRegistryHive() - Failed 4.\n");
595       return Status;
596     }
597
598   Status = NtQueryInformationFile(FileHandle,
599                                   &IoSB,
600                                   &fsi,
601                                   sizeof(fsi),
602                                   FileStandardInformation);
603
604   assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
605
606   if (!NT_SUCCESS(Status))
607     {
608       ObDereferenceObject(RegistryHive->FileObject);
609       RtlFreeUnicodeString(&RegistryHive->Filename);
610       DPRINT("CmiInitPermanentRegistryHive() - Failed 5.\n");
611       return Status;
612     }
613
614 #if 0
615   /* We have a reference to the file object so we don't need the handle anymore */
616   ZwClose(FileHandle);
617 #endif
618
619   RegistryHive->FileSize = fsi.EndOfFile.u.LowPart;
620 #ifdef WIN32_REGDBG
621 //  assert(RegistryHive->FileSize);
622   if (RegistryHive->FileSize)
623   {
624     RegistryHive->BlockListSize = (RegistryHive->FileSize / 4096) - 1;
625   }
626   else
627   {
628     ObDereferenceObject(RegistryHive->FileObject);
629     RtlFreeUnicodeString(&RegistryHive->Filename);
630     DPRINT("CmiInitPermanentRegistryHive() - Failed, zero length hive file.\n");
631     return STATUS_INSUFFICIENT_RESOURCES;
632   }
633 #else
634   RegistryHive->BlockListSize = (RegistryHive->FileSize / 4096) - 1;
635 #endif
636
637   DPRINT("Space needed for block list describing hive: 0x%x\n",
638     sizeof(PHBIN *) * RegistryHive->BlockListSize);
639
640   RegistryHive->BlockList = ExAllocatePool(NonPagedPool,
641           sizeof(PHBIN *) * RegistryHive->BlockListSize);
642
643   if (RegistryHive->BlockList == NULL)
644     {
645       ExFreePool(RegistryHive->BlockList);
646       ObDereferenceObject(RegistryHive->FileObject);
647       RtlFreeUnicodeString(&RegistryHive->Filename);
648       DPRINT("CmiInitPermanentRegistryHive() - Failed 6.\n");
649       return STATUS_INSUFFICIENT_RESOURCES;
650     }
651
652 #if 0
653   /* Map hive into cache memory (readonly) (skip the base block) */
654         FileOffset.u.HighPart = 0;
655         FileOffset.u.LowPart = 4096;
656   Success = CcMapData(RegistryHive->FileObject,   /* File object */
657     &FileOffset,                                  /* File offset */
658     RegistryHive->FileSize - 4096,                /* Region length */
659     TRUE,                                         /* Wait if needed */
660     &RegistryHive->Bcb,                           /* OUT: Buffer Control Block */
661     (PVOID*) &RegistryHive->BlockList[0]);        /* OUT: Mapped data pointer */
662
663   assertmsg(Success, ("Success: %d\n", Success));
664
665   if (!Success)
666     {
667       ExFreePool(RegistryHive->BlockList);
668       ObDereferenceObject(RegistryHive->FileObject);
669       RtlFreeUnicodeString(&RegistryHive->Filename);
670       DPRINT("CmiInitPermanentRegistryHive() - Failed 7.\n");
671       return Status;
672     }
673
674 #else
675
676   RegistryHive->BlockList[0] = ExAllocatePool(PagedPool,
677           RegistryHive->FileSize - 4096);
678 #ifdef WIN32_REGDBG
679   RtlZeroMemory(RegistryHive->BlockList[0], RegistryHive->FileSize - 4096);
680 #endif
681
682   if (RegistryHive->BlockList[0] == NULL)
683     {
684       ExFreePool(RegistryHive->BlockList);
685       ObDereferenceObject(RegistryHive->FileObject);
686       RtlFreeUnicodeString(&RegistryHive->Filename);
687       DPRINT("CmiInitPermanentRegistryHive() - Failed 8.\n");
688       return STATUS_INSUFFICIENT_RESOURCES;
689     }
690
691   FileOffset.u.HighPart = 0;
692   FileOffset.u.LowPart = 4096;
693
694   DPRINT("    Attempting to ZwReadFile(%d) for %d bytes into %p\n", FileHandle, RegistryHive->FileSize - 4096, (PVOID)RegistryHive->BlockList[0]);
695   Status = NtReadFile(FileHandle,
696                       0,
697                       0,
698                       0,
699                       &IoSB,
700                       (PVOID) RegistryHive->BlockList[0],
701                       RegistryHive->FileSize - 4096,
702                       &FileOffset,
703                       0);
704
705   assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
706
707   NtClose(FileHandle);
708 #endif
709
710   RegistryHive->FreeListSize = 0;
711   RegistryHive->FreeListMax = 0;
712   RegistryHive->FreeList = NULL;
713
714   BlockOffset = 0;
715   for (i = 0; i < RegistryHive->BlockListSize; i++)
716     {
717       RegistryHive->BlockList[i] = (PHBIN) (((ULONG_PTR) RegistryHive->BlockList[0]) + BlockOffset);
718       tmpBin = (PHBIN) (((ULONG_PTR) RegistryHive->BlockList[i]));
719       if (tmpBin->BlockId != REG_BIN_ID)
720         {
721           DPRINT("Bad BlockId %x, offset %x\n", tmpBin->BlockId, BlockOffset);
722           //KeBugCheck(0);
723           return STATUS_INSUFFICIENT_RESOURCES;
724         }
725
726       assertmsg((tmpBin->BlockSize % 4096) == 0, ("BlockSize (0x%.08x) must be multiplum of 4K\n", tmpBin->BlockSize));
727
728       if (tmpBin->BlockSize > 4096)
729         {
730           for (j = 1; j < tmpBin->BlockSize / 4096; j++)
731             {
732               RegistryHive->BlockList[i + j] = RegistryHive->BlockList[i];
733             }
734           i = i + j - 1;
735         }
736
737       /* Search free blocks and add to list */
738       FreeOffset = REG_HBIN_DATA_OFFSET;
739       while (FreeOffset < tmpBin->BlockSize)
740         {
741           FreeBlock = (PCELL_HEADER) ((ULONG_PTR) RegistryHive->BlockList[i] + FreeOffset);
742           if (FreeBlock->CellSize > 0)
743             {
744               Status = CmiAddFree(RegistryHive,
745                                   FreeBlock,
746                                   RegistryHive->BlockList[i]->BlockOffset + FreeOffset);
747
748               if (!NT_SUCCESS(Status))
749                 {
750                   /* FIXME: */
751                   assert(FALSE);
752                 }
753
754               FreeOffset += FreeBlock->CellSize;
755             }
756           else
757             {
758               FreeOffset -= FreeBlock->CellSize;
759             }
760         }
761       BlockOffset += tmpBin->BlockSize;
762     }
763
764   /* Create block bitmap and clear all bits */
765
766   /* Calculate bitmap size in bytes (always a multiple of 32 bits) */
767   BitmapSize = ROUND_UP(RegistryHive->BlockListSize, sizeof(ULONG) * 8) / 8;
768   DPRINT1("RegistryHive->BlockListSize: %lu\n", RegistryHive->BlockListSize);
769   DPRINT1("BitmapSize:  %lu Bytes  %lu Bits\n", BitmapSize, BitmapSize * 8);
770   BitmapBuffer = (PULONG)ExAllocatePool(PagedPool,
771                                         BitmapSize);
772   RtlInitializeBitMap(&RegistryHive->DirtyBitMap,
773                       BitmapBuffer,
774                       BitmapSize * 8);
775   RtlClearAllBits(&RegistryHive->DirtyBitMap);
776   RegistryHive->HiveDirty = FALSE;
777
778   DPRINT1("CmiInitPermanentRegistryHive(%p, %S, %d) - Finished.\n", RegistryHive, Filename, CreateNew);
779
780   return(STATUS_SUCCESS);
781 }
782
783
784 NTSTATUS
785 CmiInitVolatileRegistryHive(PREGISTRY_HIVE RegistryHive)
786 {
787   PKEY_CELL RootKeyCell;
788
789   RegistryHive->Flags |= HIVE_VOLATILE;
790
791   CmiCreateDefaultHiveHeader(RegistryHive->HiveHeader);
792   
793   RootKeyCell = (PKEY_CELL) ExAllocatePool(NonPagedPool, sizeof(KEY_CELL));
794
795   if (RootKeyCell == NULL)
796     return STATUS_INSUFFICIENT_RESOURCES;
797
798   CmiCreateDefaultRootKeyCell(RootKeyCell);
799
800   RegistryHive->HiveHeader->RootKeyCell = (BLOCK_OFFSET) RootKeyCell;
801
802   return STATUS_SUCCESS;
803 }
804
805
806 NTSTATUS
807 CmiCreateRegistryHive(PWSTR Filename,
808   PREGISTRY_HIVE *RegistryHive,
809   BOOLEAN CreateNew)
810 {
811   PREGISTRY_HIVE Hive;
812   NTSTATUS Status;
813
814   DPRINT("CmiCreateRegistryHive(Filename %S)\n", Filename);
815
816   *RegistryHive = NULL;
817
818   Hive = ExAllocatePool(NonPagedPool, sizeof(REGISTRY_HIVE));
819   if (Hive == NULL)
820     return(STATUS_INSUFFICIENT_RESOURCES);
821
822   DPRINT("Hive %x\n", Hive);
823
824   RtlZeroMemory(Hive, sizeof(REGISTRY_HIVE));
825
826   Hive->HiveHeader = (PHIVE_HEADER)
827     ExAllocatePool(NonPagedPool, sizeof(HIVE_HEADER));
828
829   if (Hive->HiveHeader == NULL)
830     {
831       ExFreePool(Hive);
832       return(STATUS_INSUFFICIENT_RESOURCES);
833     }
834
835   if (Filename != NULL)
836     {
837       Status = CmiInitPermanentRegistryHive(Hive, Filename, CreateNew);
838     }
839   else
840     {
841       Status = CmiInitVolatileRegistryHive(Hive);
842     }
843
844   if (!NT_SUCCESS(Status))
845     {
846       ExFreePool(Hive->HiveHeader);
847       ExFreePool(Hive);
848       return(Status);
849     }
850
851   ExInitializeResourceLite(&Hive->HiveResource);
852
853   /* Acquire hive list lock exclusively */
854   ExAcquireResourceExclusiveLite(&CmiHiveListLock, TRUE);
855
856   /* Add the new hive to the hive list */
857   InsertHeadList(&CmiHiveListHead, &Hive->HiveList);
858
859   /* Release hive list lock */
860   ExReleaseResourceLite(&CmiHiveListLock);
861
862   VERIFY_REGISTRY_HIVE(Hive);
863
864   *RegistryHive = Hive;
865
866   DPRINT("CmiCreateRegistryHive(Filename %S) - Finished.\n", Filename);
867
868   return(STATUS_SUCCESS);
869 }
870
871
872 NTSTATUS
873 CmiRemoveRegistryHive(PREGISTRY_HIVE RegistryHive)
874 {
875   /* Acquire hive list lock exclusively */
876   ExAcquireResourceExclusiveLite(&CmiHiveListLock, TRUE);
877
878   /* Remove hive from hive list */
879   RemoveEntryList(&RegistryHive->HiveList);
880
881   /* Release hive list lock */
882   ExReleaseResourceLite(&CmiHiveListLock);
883
884
885   /* FIXME: Remove attached keys and values */
886
887
888   /* Release hive header */
889   ExFreePool(RegistryHive->HiveHeader);
890
891   /* Release hive */
892   ExFreePool(RegistryHive);
893
894   return(STATUS_SUCCESS);
895 }
896
897
898 NTSTATUS
899 CmiFlushRegistryHive(PREGISTRY_HIVE RegistryHive)
900 {
901   ULONG BlockIndex;
902   ULONG BlockOffset;
903   PVOID BlockPtr;
904   LARGE_INTEGER FileOffset;
905   OBJECT_ATTRIBUTES ObjectAttributes;
906   IO_STATUS_BLOCK IoStatusBlock;
907   HANDLE FileHandle;
908   NTSTATUS Status;
909
910
911
912
913   DPRINT("CmiFlushRegistryHive() called\n");
914
915   if (RegistryHive->HiveDirty == FALSE)
916     {
917       return(STATUS_SUCCESS);
918     }
919
920   DPRINT1("Hive '%wZ' is dirty\n", &RegistryHive->Filename);
921
922
923   /* Open hive for writing */
924   InitializeObjectAttributes(&ObjectAttributes,
925                              &RegistryHive->Filename,
926                              0,
927                              NULL,
928                              NULL);
929
930   Status = NtCreateFile(&FileHandle,
931                         FILE_ALL_ACCESS,
932                         &ObjectAttributes,
933                         &IoStatusBlock,
934                         NULL,
935                         FILE_ATTRIBUTE_NORMAL,
936                         0,
937                         FILE_OPEN,
938                         FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
939                         NULL,
940                         0);
941   if (!NT_SUCCESS(Status))
942     {
943       DPRINT1("NtCreateFile() failed (Status %lx)\n", Status);
944       return(Status);
945     }
946
947
948
949   BlockIndex = 0;
950   while (TRUE)
951     {
952       BlockIndex = RtlFindSetBitsAndClear(&RegistryHive->DirtyBitMap,
953                                           1,
954                                           BlockIndex);
955       if (BlockIndex == (ULONG)-1)
956         {
957           DPRINT("No more set bits\n");
958           break;
959         }
960
961       DPRINT1("Block %lu is dirty\n", BlockIndex);
962
963       BlockOffset = RegistryHive->BlockList[BlockIndex]->BlockOffset;
964       DPRINT1("Block offset %lx\n", BlockOffset);
965
966       BlockPtr = RegistryHive->BlockList[BlockIndex] + ((BlockIndex * 4096) - BlockOffset);
967       DPRINT1("BlockPtr %p\n", BlockPtr);
968
969       FileOffset.QuadPart = (ULONGLONG)(BlockIndex + 1) * 4096ULL;
970       DPRINT1("File offset %I64x\n", FileOffset.QuadPart);
971
972
973       /* Write hive block */
974       Status = NtWriteFile(FileHandle,
975                            NULL,
976                            NULL,
977                            NULL,
978                            &IoStatusBlock,
979                            BlockPtr,
980                            REG_BLOCK_SIZE,
981                            &FileOffset,
982                            NULL);
983       if (!NT_SUCCESS(Status))
984         {
985           DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
986           NtClose(FileHandle);
987           return(Status);
988         }
989
990
991     }
992
993   NtClose(FileHandle);
994
995
996   /* Clear dirty flag */
997   RegistryHive->HiveDirty = FALSE;
998
999   DPRINT("CmiFlushRegistryHive() done\n");
1000
1001   return(STATUS_SUCCESS);
1002 }
1003
1004
1005 ULONG
1006 CmiGetMaxNameLength(PREGISTRY_HIVE  RegistryHive,
1007   PKEY_CELL  KeyCell)
1008 {
1009   PHASH_TABLE_CELL HashBlock;
1010   PKEY_CELL CurSubKeyCell;
1011   ULONG MaxName;
1012   ULONG i;
1013
1014   VERIFY_KEY_CELL(KeyCell);
1015
1016   MaxName = 0;
1017   HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
1018   if (HashBlock == NULL)
1019     {
1020       return 0;
1021     }
1022
1023   for (i = 0; i < HashBlock->HashTableSize; i++)
1024     {
1025       if (HashBlock->Table[i].KeyOffset != 0)
1026         {
1027           CurSubKeyCell = CmiGetBlock(RegistryHive,
1028             HashBlock->Table[i].KeyOffset,
1029             NULL);
1030           if (MaxName < CurSubKeyCell->NameSize)
1031             {
1032               MaxName = CurSubKeyCell->NameSize;
1033             }
1034           CmiReleaseBlock(RegistryHive, CurSubKeyCell);
1035         }
1036     }
1037
1038   CmiReleaseBlock(RegistryHive, HashBlock);
1039   
1040   return MaxName;
1041 }
1042
1043
1044 ULONG
1045 CmiGetMaxClassLength(PREGISTRY_HIVE  RegistryHive,
1046   PKEY_CELL  KeyCell)
1047 {
1048   PHASH_TABLE_CELL HashBlock;
1049   PKEY_CELL CurSubKeyCell;
1050   ULONG MaxClass;
1051   ULONG i;
1052
1053   VERIFY_KEY_CELL(KeyCell);
1054
1055   MaxClass = 0;
1056   HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
1057   if (HashBlock == NULL)
1058     {
1059       return 0;
1060     }
1061
1062   for (i = 0; i < HashBlock->HashTableSize; i++)
1063     {
1064       if (HashBlock->Table[i].KeyOffset != 0)
1065         {
1066           CurSubKeyCell = CmiGetBlock(RegistryHive,
1067             HashBlock->Table[i].KeyOffset,
1068             NULL);
1069           if (MaxClass < CurSubKeyCell->ClassSize)
1070             {
1071               MaxClass = CurSubKeyCell->ClassSize;
1072             }
1073           CmiReleaseBlock(RegistryHive, CurSubKeyCell);
1074         }
1075     }
1076
1077   CmiReleaseBlock(RegistryHive, HashBlock);
1078
1079   return MaxClass;
1080 }
1081
1082
1083 ULONG  
1084 CmiGetMaxValueNameLength(PREGISTRY_HIVE RegistryHive,
1085   PKEY_CELL KeyCell)
1086 {
1087   PVALUE_LIST_CELL ValueListCell;
1088   PVALUE_CELL CurValueCell;
1089   ULONG MaxValueName;
1090   ULONG i;
1091
1092   VERIFY_KEY_CELL(KeyCell);
1093
1094   ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
1095   MaxValueName = 0;
1096   if (ValueListCell == NULL)
1097     {
1098       return 0;
1099     }
1100
1101   for (i = 0; i < KeyCell->NumberOfValues; i++)
1102     {
1103       CurValueCell = CmiGetBlock(RegistryHive,
1104         ValueListCell->Values[i],
1105         NULL);
1106       if (CurValueCell != NULL &&
1107           MaxValueName < CurValueCell->NameSize)
1108         {
1109           MaxValueName = CurValueCell->NameSize;
1110         }
1111       CmiReleaseBlock(RegistryHive, CurValueCell);
1112     }
1113
1114   CmiReleaseBlock(RegistryHive, ValueListCell);
1115   
1116   return MaxValueName;
1117 }
1118
1119
1120 ULONG  
1121 CmiGetMaxValueDataLength(PREGISTRY_HIVE RegistryHive,
1122   PKEY_CELL KeyCell)
1123 {
1124   PVALUE_LIST_CELL ValueListCell;
1125   PVALUE_CELL CurValueCell;
1126   LONG MaxValueData;
1127   ULONG i;
1128
1129   VERIFY_KEY_CELL(KeyCell);
1130
1131   ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
1132   MaxValueData = 0;
1133   if (ValueListCell == NULL)
1134     {
1135       return 0;
1136     }
1137
1138   for (i = 0; i < KeyCell->NumberOfValues; i++)
1139     {
1140       CurValueCell = CmiGetBlock(RegistryHive,
1141                                   ValueListCell->Values[i],NULL);
1142       if ((CurValueCell != NULL) &&
1143           (MaxValueData < (CurValueCell->DataSize & LONG_MAX)))
1144         {
1145           MaxValueData = CurValueCell->DataSize & LONG_MAX;
1146         }
1147       CmiReleaseBlock(RegistryHive, CurValueCell);
1148     }
1149
1150   CmiReleaseBlock(RegistryHive, ValueListCell);
1151   
1152   return MaxValueData;
1153 }
1154
1155
1156 NTSTATUS
1157 CmiScanForSubKey(IN PREGISTRY_HIVE RegistryHive,
1158         IN PKEY_CELL KeyCell,
1159         OUT PKEY_CELL *SubKeyCell,
1160         OUT BLOCK_OFFSET *BlockOffset,
1161         IN PCHAR KeyName,
1162         IN ACCESS_MASK DesiredAccess,
1163         IN ULONG Attributes)
1164 {
1165   PHASH_TABLE_CELL HashBlock;
1166   PKEY_CELL CurSubKeyCell;
1167   WORD KeyLength;
1168   ULONG i;
1169
1170   VERIFY_KEY_CELL(KeyCell);
1171
1172   //DPRINT("Scanning for sub key %s\n", KeyName);
1173
1174   assert(RegistryHive);
1175
1176   KeyLength = strlen(KeyName);
1177
1178   HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
1179   *SubKeyCell = NULL;
1180   if (HashBlock == NULL)
1181     {
1182       return STATUS_SUCCESS;
1183     }
1184
1185   for (i = 0; (i < KeyCell->NumberOfSubKeys)
1186                 && (i < HashBlock->HashTableSize); i++)
1187     {
1188       if (Attributes & OBJ_CASE_INSENSITIVE)
1189         {
1190           if ((HashBlock->Table[i].KeyOffset != 0) &&
1191               (HashBlock->Table[i].KeyOffset != (ULONG_PTR)-1) &&
1192               (_strnicmp(KeyName, (PCHAR) &HashBlock->Table[i].HashValue, 4) == 0))
1193             {
1194               CurSubKeyCell = CmiGetBlock(RegistryHive, 
1195                 HashBlock->Table[i].KeyOffset,
1196                 NULL);
1197               if ((CurSubKeyCell->NameSize == KeyLength)
1198                   && (_strnicmp(KeyName, CurSubKeyCell->Name, KeyLength) == 0))
1199                 {
1200                   *SubKeyCell = CurSubKeyCell;
1201                   *BlockOffset = HashBlock->Table[i].KeyOffset;
1202                   break;
1203                 }
1204               else
1205                 {
1206                   CmiReleaseBlock(RegistryHive, CurSubKeyCell);
1207                 }
1208             }
1209         }
1210       else
1211         {
1212           if (HashBlock->Table[i].KeyOffset != 0 &&
1213               HashBlock->Table[i].KeyOffset != (ULONG_PTR) -1 &&
1214               !strncmp(KeyName, (PCHAR) &HashBlock->Table[i].HashValue, 4))
1215             {
1216               CurSubKeyCell = CmiGetBlock(RegistryHive,
1217                 HashBlock->Table[i].KeyOffset,NULL);
1218               if (CurSubKeyCell->NameSize == KeyLength
1219                   && !_strnicmp(KeyName, CurSubKeyCell->Name, KeyLength))
1220                 {
1221                   *SubKeyCell = CurSubKeyCell;
1222                   *BlockOffset = HashBlock->Table[i].KeyOffset;
1223                   break;
1224                 }
1225               else
1226                 {
1227                   CmiReleaseBlock(RegistryHive, CurSubKeyCell);
1228                 }
1229             }
1230         }
1231     }
1232   
1233   CmiReleaseBlock(RegistryHive, HashBlock);
1234   
1235   return STATUS_SUCCESS;
1236 }
1237
1238
1239 NTSTATUS
1240 CmiAddSubKey(PREGISTRY_HIVE RegistryHive,
1241              PKEY_OBJECT Parent,
1242              PKEY_OBJECT SubKey,
1243              PWSTR NewSubKeyName,
1244              USHORT NewSubKeyNameSize,
1245              ULONG TitleIndex,
1246              PUNICODE_STRING Class,
1247              ULONG CreateOptions)
1248 {
1249   PHASH_TABLE_CELL NewHashBlock;
1250   PHASH_TABLE_CELL HashBlock;
1251   BLOCK_OFFSET NKBOffset;
1252   PKEY_CELL NewKeyCell; 
1253   ULONG NewBlockSize;
1254   PKEY_CELL KeyCell;
1255   NTSTATUS Status;
1256   USHORT NameSize;
1257
1258   KeyCell = Parent->KeyCell;
1259
1260   VERIFY_KEY_CELL(KeyCell);
1261
1262   if (NewSubKeyName[0] == L'\\')
1263     {
1264       NewSubKeyName++;
1265       NameSize = NewSubKeyNameSize / 2 - 1;
1266     }
1267   else
1268     {
1269       NameSize = NewSubKeyNameSize / 2;
1270     }
1271   Status = STATUS_SUCCESS;
1272
1273   NewBlockSize = sizeof(KEY_CELL) + NameSize;
1274   Status = CmiAllocateBlock(RegistryHive,
1275     (PVOID) &NewKeyCell,
1276     NewBlockSize,
1277     &NKBOffset);
1278
1279   if (NewKeyCell == NULL)
1280     {
1281       Status = STATUS_INSUFFICIENT_RESOURCES;
1282     }
1283   else
1284     {
1285       NewKeyCell->Id = REG_KEY_CELL_ID;
1286       NewKeyCell->Type = REG_KEY_CELL_TYPE;
1287       ZwQuerySystemTime((PTIME) &NewKeyCell->LastWriteTime);
1288       NewKeyCell->ParentKeyOffset = -1;
1289       NewKeyCell->NumberOfSubKeys = 0;
1290       NewKeyCell->HashTableOffset = -1;
1291       NewKeyCell->NumberOfValues = 0;
1292       NewKeyCell->ValuesOffset = -1;
1293       NewKeyCell->SecurityKeyOffset = -1;
1294       NewKeyCell->NameSize = NameSize;
1295       wcstombs(NewKeyCell->Name, NewSubKeyName, NameSize);
1296       NewKeyCell->ClassNameOffset = -1;
1297
1298       VERIFY_KEY_CELL(NewKeyCell);
1299
1300       if (Class)
1301         {
1302           PDATA_CELL pClass;
1303
1304           NewKeyCell->ClassSize = Class->Length + sizeof(WCHAR);
1305           Status = CmiAllocateBlock(RegistryHive,
1306                                     (PVOID) &pClass,
1307                                     NewKeyCell->ClassSize,
1308                                     &NewKeyCell->ClassNameOffset);
1309           wcsncpy((PWSTR) pClass->Data, Class->Buffer, Class->Length);
1310           ((PWSTR) (pClass->Data))[Class->Length] = 0;
1311         }
1312     }
1313
1314   if (!NT_SUCCESS(Status))
1315     {
1316       return Status;
1317     }
1318
1319   SubKey->KeyCell = NewKeyCell;
1320   SubKey->BlockOffset = NKBOffset;
1321
1322   /* Don't modify hash table if key is volatile and parent is not */
1323   if (IsVolatileHive(RegistryHive) && (!IsVolatileHive(Parent->RegistryHive)))
1324     {
1325       return(Status);
1326     }
1327
1328   if (KeyCell->HashTableOffset == (ULONG_PTR) -1)
1329     {
1330       Status = CmiAllocateHashTableBlock(RegistryHive,
1331                                          &HashBlock,
1332                                          &KeyCell->HashTableOffset,
1333                                          REG_INIT_HASH_TABLE_SIZE);
1334       if (!NT_SUCCESS(Status))
1335         {
1336           return(Status);
1337         }
1338     }
1339   else
1340     {
1341       HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
1342       if (((KeyCell->NumberOfSubKeys + 1) >= HashBlock->HashTableSize))
1343         {
1344           BLOCK_OFFSET HTOffset;
1345
1346           /* Reallocate the hash table block */
1347           Status = CmiAllocateHashTableBlock(RegistryHive,
1348                                              &NewHashBlock,
1349                                              &HTOffset,
1350                                              HashBlock->HashTableSize +
1351                                                REG_EXTEND_HASH_TABLE_SIZE);
1352           if (!NT_SUCCESS(Status))
1353             {
1354               return Status;
1355             }
1356
1357           RtlZeroMemory(&NewHashBlock->Table[0],
1358                         sizeof(NewHashBlock->Table[0]) * NewHashBlock->HashTableSize);
1359           RtlCopyMemory(&NewHashBlock->Table[0],
1360                         &HashBlock->Table[0],
1361                         sizeof(NewHashBlock->Table[0]) * HashBlock->HashTableSize);
1362           CmiDestroyBlock(RegistryHive,
1363                           HashBlock,
1364                           KeyCell->HashTableOffset);
1365           KeyCell->HashTableOffset = HTOffset;
1366           HashBlock = NewHashBlock;
1367         }
1368     }
1369
1370   Status = CmiAddKeyToHashTable(RegistryHive,
1371                                 HashBlock,
1372                                 NewKeyCell,
1373                                 NKBOffset);
1374   if (NT_SUCCESS(Status))
1375     {
1376       KeyCell->NumberOfSubKeys++;
1377     }
1378
1379   return(Status);
1380 }
1381
1382
1383 NTSTATUS
1384 CmiRemoveSubKey(PREGISTRY_HIVE RegistryHive,
1385                 PKEY_OBJECT ParentKey,
1386                 PKEY_OBJECT SubKey)
1387 {
1388   PHASH_TABLE_CELL HashBlock;
1389
1390   DPRINT1("CmiRemoveSubKey() called\n");
1391
1392   /* Remove the key from the parent key's hash block */
1393   if (ParentKey->KeyCell->HashTableOffset != -1)
1394     {
1395       DPRINT1("ParentKey HashTableOffset %lx\n", ParentKey->KeyCell->HashTableOffset)
1396       HashBlock = CmiGetBlock(RegistryHive,
1397                               ParentKey->KeyCell->HashTableOffset,
1398                               NULL);
1399       DPRINT1("ParentKey HashBlock %p\n", HashBlock)
1400       if (HashBlock != NULL)
1401         {
1402           CmiRemoveKeyFromHashTable(RegistryHive,
1403                                     HashBlock,
1404                                     SubKey->BlockOffset);
1405           CmiMarkBlockDirty(RegistryHive,
1406                             ParentKey->KeyCell->HashTableOffset);
1407         }
1408     }
1409
1410   /* Remove the key's hash block */
1411   if (SubKey->KeyCell->HashTableOffset != -1)
1412     {
1413       DPRINT1("SubKey HashTableOffset %lx\n", SubKey->KeyCell->HashTableOffset)
1414       HashBlock = CmiGetBlock(RegistryHive,
1415                               SubKey->KeyCell->HashTableOffset,
1416                               NULL);
1417       DPRINT1("SubKey HashBlock %p\n", HashBlock)
1418       if (HashBlock != NULL)
1419         {
1420           CmiDestroyBlock(RegistryHive,
1421                           HashBlock,
1422                           SubKey->KeyCell->HashTableOffset);
1423           SubKey->KeyCell->HashTableOffset = -1;
1424         }
1425     }
1426
1427   /* Decrement the number of the parent key's sub keys */
1428   if (ParentKey != NULL)
1429     {
1430       DPRINT1("ParentKey %p\n", ParentKey)
1431       ParentKey->KeyCell->NumberOfSubKeys--;
1432       NtQuerySystemTime((PTIME)&ParentKey->KeyCell->LastWriteTime);
1433       CmiMarkBlockDirty(RegistryHive,
1434                         ParentKey->BlockOffset);
1435
1436       /* Remove the parent key's hash table */
1437       if (ParentKey->KeyCell->NumberOfSubKeys == 0)
1438         {
1439           DPRINT1("FIXME: Remove parent key hash table\n")
1440
1441         }
1442     }
1443
1444   /* Destroy key cell */
1445   CmiDestroyBlock(RegistryHive,
1446                   SubKey->KeyCell,
1447                   SubKey->BlockOffset);
1448   SubKey->BlockOffset = -1;
1449   SubKey->KeyCell = NULL;
1450
1451   /* FIXME: Merge free blocks within the Bin */
1452
1453   return(STATUS_SUCCESS);
1454 }
1455
1456
1457 NTSTATUS
1458 CmiScanKeyForValue(IN PREGISTRY_HIVE RegistryHive,
1459         IN PKEY_CELL KeyCell,
1460         IN PUNICODE_STRING ValueName,
1461         OUT PVALUE_CELL *ValueCell,
1462         OUT BLOCK_OFFSET *VBOffset)
1463 {
1464   PVALUE_LIST_CELL ValueListCell;
1465   PVALUE_CELL CurValueCell;
1466   ULONG i;
1467
1468   ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
1469
1470   *ValueCell = NULL;
1471
1472   if (ValueListCell == NULL)
1473     {
1474       DPRINT("ValueListCell is NULL\n");
1475       return STATUS_SUCCESS;
1476     }
1477
1478   VERIFY_VALUE_LIST_CELL(ValueListCell);
1479
1480   for (i = 0; i < KeyCell->NumberOfValues; i++)
1481     {
1482       CurValueCell = CmiGetBlock(RegistryHive,
1483                                  ValueListCell->Values[i],
1484                                  NULL);
1485
1486       if ((CurValueCell != NULL) &&
1487           CmiComparePackedNames(ValueName,
1488                                 CurValueCell->Name,
1489                                 CurValueCell->NameSize,
1490                                 CurValueCell->Flags & REG_VALUE_NAME_PACKED))
1491         {
1492           *ValueCell = CurValueCell;
1493           if (VBOffset)
1494             *VBOffset = ValueListCell->Values[i];
1495           //DPRINT("Found value %s\n", ValueName);
1496           break;
1497         }
1498       CmiReleaseBlock(RegistryHive, CurValueCell);
1499     }
1500
1501   CmiReleaseBlock(RegistryHive, ValueListCell);
1502
1503   return STATUS_SUCCESS;
1504 }
1505
1506
1507 NTSTATUS
1508 CmiGetValueFromKeyByIndex(IN PREGISTRY_HIVE RegistryHive,
1509         IN PKEY_CELL KeyCell,
1510         IN ULONG Index,
1511         OUT PVALUE_CELL *ValueCell)
1512 {
1513   PVALUE_LIST_CELL ValueListCell;
1514   PVALUE_CELL CurValueCell;
1515
1516   ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
1517
1518   *ValueCell = NULL;
1519
1520   if (ValueListCell == NULL)
1521     {
1522       return STATUS_NO_MORE_ENTRIES;
1523     }
1524
1525   VERIFY_VALUE_LIST_CELL(ValueListCell);
1526
1527   if (Index >= KeyCell->NumberOfValues)
1528     {
1529       return STATUS_NO_MORE_ENTRIES;
1530     }
1531
1532   CurValueCell = CmiGetBlock(RegistryHive,
1533     ValueListCell->Values[Index],
1534     NULL);
1535
1536   if (CurValueCell != NULL)
1537     {
1538       *ValueCell = CurValueCell;
1539     }
1540
1541   CmiReleaseBlock(RegistryHive, CurValueCell);
1542   CmiReleaseBlock(RegistryHive, ValueListCell);
1543
1544   return STATUS_SUCCESS;
1545 }
1546
1547
1548 NTSTATUS
1549 CmiAddValueToKey(IN PREGISTRY_HIVE RegistryHive,
1550         IN PKEY_CELL KeyCell,
1551         IN PUNICODE_STRING ValueName,
1552         OUT PVALUE_CELL *pValueCell,
1553         OUT BLOCK_OFFSET *pVBOffset)
1554 {
1555   PVALUE_LIST_CELL NewValueListCell;
1556   PVALUE_LIST_CELL ValueListCell;
1557   PVALUE_CELL NewValueCell;
1558   BLOCK_OFFSET VLBOffset;
1559   BLOCK_OFFSET VBOffset;
1560   NTSTATUS Status;
1561
1562   Status = CmiAllocateValueCell(RegistryHive,
1563                 &NewValueCell,
1564                 &VBOffset,
1565                 ValueName);
1566   *pVBOffset = VBOffset;
1567
1568   if (!NT_SUCCESS(Status))
1569     {
1570       return Status;
1571     }
1572
1573   ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
1574
1575   if (ValueListCell == NULL)
1576     {
1577       Status = CmiAllocateBlock(RegistryHive,
1578               (PVOID) &ValueListCell,
1579               sizeof(BLOCK_OFFSET) * 3,
1580               &VLBOffset);
1581
1582       if (!NT_SUCCESS(Status))
1583         {
1584           CmiDestroyValueCell(RegistryHive, NewValueCell, VBOffset);
1585           return Status;
1586         }
1587       KeyCell->ValuesOffset = VLBOffset;
1588     }
1589   else if ((KeyCell->NumberOfValues
1590                 >= (ULONG) ((LONG) (ValueListCell->CellSize - 4)) / (LONG) sizeof(BLOCK_OFFSET)))
1591     {
1592       Status = CmiAllocateBlock(RegistryHive,
1593               (PVOID) &NewValueListCell,
1594               sizeof(BLOCK_OFFSET) * (KeyCell->NumberOfValues + REG_VALUE_LIST_CELL_MULTIPLE),
1595               &VLBOffset);
1596
1597       if (!NT_SUCCESS(Status))
1598         {
1599           CmiDestroyValueCell(RegistryHive, NewValueCell, VBOffset);
1600           return Status;
1601         }
1602
1603       RtlCopyMemory(&NewValueListCell->Values[0],
1604         &ValueListCell->Values[0],
1605         sizeof(BLOCK_OFFSET) * KeyCell->NumberOfValues);
1606       CmiDestroyBlock(RegistryHive, ValueListCell, KeyCell->ValuesOffset);
1607       KeyCell->ValuesOffset = VLBOffset;
1608       ValueListCell = NewValueListCell;
1609     }
1610
1611   DPRINT("KeyCell->NumberOfValues %d, ValueListCell->CellSize %d (%d %x)\n",
1612          KeyCell->NumberOfValues, ValueListCell->CellSize,
1613          -(ValueListCell->CellSize - 4) / sizeof(BLOCK_OFFSET),
1614          -(ValueListCell->CellSize - 4) / sizeof(BLOCK_OFFSET));
1615
1616   ValueListCell->Values[KeyCell->NumberOfValues] = VBOffset;
1617   KeyCell->NumberOfValues++;
1618   CmiReleaseBlock(RegistryHive, ValueListCell);
1619   CmiReleaseBlock(RegistryHive, NewValueCell);
1620   *pValueCell = NewValueCell;
1621
1622   return STATUS_SUCCESS;
1623 }
1624
1625
1626 NTSTATUS
1627 CmiDeleteValueFromKey(IN PREGISTRY_HIVE RegistryHive,
1628                       IN PKEY_CELL KeyCell,
1629                       IN BLOCK_OFFSET KeyCellOffset,
1630                       IN PUNICODE_STRING ValueName)
1631 {
1632   PVALUE_LIST_CELL ValueListCell;
1633   PVALUE_CELL CurValueCell;
1634   ULONG  i;
1635
1636   ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
1637
1638   if (ValueListCell == NULL)
1639     {
1640       return STATUS_SUCCESS;
1641     }
1642
1643   VERIFY_VALUE_LIST_CELL(ValueListCell);
1644
1645   for (i = 0; i < KeyCell->NumberOfValues; i++)
1646     {
1647       CurValueCell = CmiGetBlock(RegistryHive, ValueListCell->Values[i], NULL);
1648
1649       if ((CurValueCell != NULL) &&
1650           CmiComparePackedNames(ValueName,
1651                                 CurValueCell->Name,
1652                                 CurValueCell->NameSize,
1653                                 CurValueCell->Flags & REG_VALUE_NAME_PACKED))
1654         {
1655           if ((KeyCell->NumberOfValues - 1) < i)
1656             {
1657               RtlCopyMemory(&ValueListCell->Values[i],
1658                 &ValueListCell->Values[i + 1],
1659                 sizeof(BLOCK_OFFSET) * (KeyCell->NumberOfValues - 1 - i));
1660             }
1661           else
1662             {
1663               RtlZeroMemory(&ValueListCell->Values[i], sizeof(BLOCK_OFFSET));
1664             }
1665
1666           KeyCell->NumberOfValues -= 1;
1667           CmiDestroyValueCell(RegistryHive, CurValueCell, ValueListCell->Values[i]);
1668           break;
1669         }
1670       CmiReleaseBlock(RegistryHive, CurValueCell);
1671     }
1672
1673   CmiReleaseBlock(RegistryHive, ValueListCell);
1674
1675   if (KeyCell->NumberOfValues == 0)
1676     {
1677       CmiDestroyBlock(RegistryHive,
1678                       ValueListCell,
1679                       KeyCell->ValuesOffset);
1680     }
1681   else
1682     {
1683       CmiMarkBlockDirty(RegistryHive,
1684                         KeyCell->ValuesOffset);
1685     }
1686
1687   CmiMarkBlockDirty(RegistryHive,
1688                     KeyCellOffset);
1689
1690   return STATUS_SUCCESS;
1691 }
1692
1693
1694 NTSTATUS
1695 CmiAllocateHashTableBlock(IN PREGISTRY_HIVE RegistryHive,
1696         OUT PHASH_TABLE_CELL *HashBlock,
1697         OUT BLOCK_OFFSET *HBOffset,
1698         IN ULONG HashTableSize)
1699 {
1700   PHASH_TABLE_CELL NewHashBlock;
1701   ULONG NewHashSize;
1702   NTSTATUS Status;
1703
1704   Status = STATUS_SUCCESS;
1705   *HashBlock = NULL;
1706   NewHashSize = sizeof(HASH_TABLE_CELL) + 
1707     (HashTableSize - 1) * sizeof(HASH_RECORD);
1708   Status = CmiAllocateBlock(RegistryHive,
1709                             (PVOID*) &NewHashBlock,
1710                             NewHashSize,
1711                             HBOffset);
1712
1713   if ((NewHashBlock == NULL) || (!NT_SUCCESS(Status)))
1714     {
1715       Status = STATUS_INSUFFICIENT_RESOURCES;
1716     }
1717   else
1718     {
1719       NewHashBlock->Id = REG_HASH_TABLE_BLOCK_ID;
1720       NewHashBlock->HashTableSize = HashTableSize;
1721       *HashBlock = NewHashBlock;
1722     }
1723
1724   return Status;
1725 }
1726
1727
1728 PKEY_CELL
1729 CmiGetKeyFromHashByIndex(PREGISTRY_HIVE RegistryHive,
1730         PHASH_TABLE_CELL HashBlock,
1731         ULONG Index)
1732 {
1733   BLOCK_OFFSET KeyOffset;
1734   PKEY_CELL KeyCell;
1735
1736   if (HashBlock == NULL)
1737     return NULL;
1738
1739   if (IsVolatileHive(RegistryHive))
1740     {
1741       KeyCell = (PKEY_CELL) HashBlock->Table[Index].KeyOffset;
1742     }
1743   else
1744     {
1745       KeyOffset =  HashBlock->Table[Index].KeyOffset;
1746       KeyCell = CmiGetBlock(RegistryHive, KeyOffset, NULL);
1747     }
1748   CmiLockBlock(RegistryHive, KeyCell);
1749
1750   return KeyCell;
1751 }
1752
1753
1754 NTSTATUS
1755 CmiAddKeyToHashTable(PREGISTRY_HIVE RegistryHive,
1756         PHASH_TABLE_CELL HashBlock,
1757         PKEY_CELL NewKeyCell,
1758         BLOCK_OFFSET NKBOffset)
1759 {
1760   ULONG i;
1761
1762   for (i = 0; i < HashBlock->HashTableSize; i++)
1763     {
1764       if (HashBlock->Table[i].KeyOffset == 0)
1765         {
1766           HashBlock->Table[i].KeyOffset = NKBOffset;
1767           RtlCopyMemory(&HashBlock->Table[i].HashValue, NewKeyCell->Name, 4);
1768           return STATUS_SUCCESS;
1769         }
1770     }
1771
1772   return STATUS_UNSUCCESSFUL;
1773 }
1774
1775
1776 NTSTATUS
1777 CmiRemoveKeyFromHashTable(PREGISTRY_HIVE RegistryHive,
1778                           PHASH_TABLE_CELL HashBlock,
1779                           BLOCK_OFFSET NKBOffset)
1780 {
1781   ULONG i;
1782
1783   for (i = 0; i < HashBlock->HashTableSize; i++)
1784     {
1785       if (HashBlock->Table[i].KeyOffset == NKBOffset)
1786         {
1787           HashBlock->Table[i].KeyOffset = 0;
1788           RtlZeroMemory(&HashBlock->Table[i].HashValue, 4);
1789           return STATUS_SUCCESS;
1790         }
1791     }
1792
1793   return STATUS_UNSUCCESSFUL;
1794 }
1795
1796
1797 NTSTATUS
1798 CmiAllocateValueCell(PREGISTRY_HIVE RegistryHive,
1799   PVALUE_CELL *ValueCell,
1800   BLOCK_OFFSET *VBOffset,
1801   IN PUNICODE_STRING ValueName)
1802 {
1803   PVALUE_CELL NewValueCell;
1804   NTSTATUS Status;
1805   BOOLEAN Packable;
1806   ULONG NameSize;
1807   ULONG i;
1808
1809   Status = STATUS_SUCCESS;
1810
1811   NameSize = CmiGetPackedNameLength(ValueName,
1812                                     &Packable);
1813
1814   DPRINT("ValueName->Length %lu  NameSize %lu\n", ValueName->Length, NameSize);
1815
1816   Status = CmiAllocateBlock(RegistryHive,
1817                             (PVOID*) &NewValueCell,
1818                             sizeof(VALUE_CELL) + NameSize,
1819                             VBOffset);
1820   if ((NewValueCell == NULL) || (!NT_SUCCESS(Status)))
1821     {
1822       Status = STATUS_INSUFFICIENT_RESOURCES;
1823     }
1824   else
1825     {
1826       NewValueCell->Id = REG_VALUE_CELL_ID;
1827       NewValueCell->NameSize = NameSize;
1828       if (Packable)
1829         {
1830           /* Pack the value name */
1831           for (i = 0; i < NameSize; i++)
1832             NewValueCell->Name[i] = (CHAR)ValueName->Buffer[i];
1833           NewValueCell->Flags |= REG_VALUE_NAME_PACKED;
1834         }
1835       else
1836         {
1837           /* Copy the value name */
1838           RtlCopyMemory(NewValueCell->Name,
1839                         ValueName->Buffer,
1840                         NameSize);
1841           NewValueCell->Flags = 0;
1842         }
1843       NewValueCell->DataType = 0;
1844       NewValueCell->DataSize = 0;
1845       NewValueCell->DataOffset = 0xffffffff;
1846       *ValueCell = NewValueCell;
1847     }
1848
1849   return Status;
1850 }
1851
1852
1853 NTSTATUS
1854 CmiDestroyValueCell(PREGISTRY_HIVE RegistryHive,
1855   PVALUE_CELL ValueCell,
1856   BLOCK_OFFSET VBOffset)
1857 {
1858   NTSTATUS Status;
1859   PVOID pBlock;
1860   PHBIN pBin;
1861
1862   VERIFY_VALUE_CELL(ValueCell);
1863
1864   /* First, release data: */
1865   if (ValueCell->DataSize > 4)
1866     {
1867       pBlock = CmiGetBlock(RegistryHive, ValueCell->DataOffset, &pBin);
1868       Status = CmiDestroyBlock(RegistryHive, pBlock, ValueCell->DataOffset);
1869       if (!NT_SUCCESS(Status))
1870         {
1871           return  Status;
1872         }
1873
1874       /* Update time of heap */
1875       if (IsPermanentHive(RegistryHive))
1876         ZwQuerySystemTime((PTIME) &pBin->DateModified);
1877     }
1878
1879   Status = CmiDestroyBlock(RegistryHive, ValueCell, VBOffset);
1880
1881   /* Update time of heap */
1882   if (IsPermanentHive(RegistryHive) && CmiGetBlock(RegistryHive, VBOffset, &pBin))
1883     {
1884       ZwQuerySystemTime((PTIME) &pBin->DateModified);
1885     }
1886
1887   return Status;
1888 }
1889
1890
1891 NTSTATUS
1892 CmiAddBin(PREGISTRY_HIVE RegistryHive,
1893   PVOID *NewBlock,
1894   BLOCK_OFFSET *NewBlockOffset)
1895 {
1896   PCELL_HEADER tmpBlock;
1897   PHBIN * tmpBlockList;
1898   PHBIN tmpBin;
1899
1900   tmpBin = ExAllocatePool(PagedPool, REG_BLOCK_SIZE);
1901   if (tmpBin == NULL)
1902     {
1903       return STATUS_INSUFFICIENT_RESOURCES;
1904     }
1905
1906   tmpBin->BlockId = REG_BIN_ID;
1907   tmpBin->BlockOffset = RegistryHive->FileSize - REG_BLOCK_SIZE;
1908   RegistryHive->FileSize += REG_BLOCK_SIZE;
1909   tmpBin->BlockSize = REG_BLOCK_SIZE;
1910   tmpBin->Unused1 = 0;
1911   ZwQuerySystemTime((PTIME) &tmpBin->DateModified);
1912   tmpBin->Unused2 = 0;
1913
1914   /* Increase size of list of blocks */
1915   tmpBlockList = ExAllocatePool(NonPagedPool,
1916           sizeof(PHBIN *) * (RegistryHive->BlockListSize + 1));
1917   if (tmpBlockList == NULL)
1918     {
1919       ExFreePool(tmpBin);
1920       return STATUS_INSUFFICIENT_RESOURCES;
1921     }
1922
1923   if (RegistryHive->BlockListSize > 0)
1924     {
1925       memcpy(tmpBlockList,
1926              RegistryHive->BlockList,
1927              sizeof(PHBIN *)*(RegistryHive->BlockListSize));
1928       ExFreePool(RegistryHive->BlockList);
1929     }
1930
1931   RegistryHive->BlockList = tmpBlockList;
1932   RegistryHive->BlockList[RegistryHive->BlockListSize++] = tmpBin;
1933
1934   /* Initialize a free block in this heap : */
1935   tmpBlock = (PCELL_HEADER)((ULONG_PTR) tmpBin + REG_HBIN_DATA_OFFSET);
1936   tmpBlock->CellSize = (REG_BLOCK_SIZE - REG_HBIN_DATA_OFFSET);
1937
1938   /* Grow bitmap if necessary */
1939   if (IsVolatileHive(RegistryHive) &&
1940       (RegistryHive->BlockListSize % (sizeof(ULONG) * 8) == 0))
1941     {
1942       DPRINT1("Grow hive bitmap - BlockListSize %lu\n", RegistryHive->BlockListSize);
1943
1944       /* FIXME */
1945
1946     }
1947
1948   *NewBlock = (PVOID) tmpBlock;
1949
1950   if (NewBlockOffset)
1951     *NewBlockOffset = tmpBin->BlockOffset + REG_HBIN_DATA_OFFSET;
1952
1953   /* FIXME: set first dword to block_offset of another free bloc */
1954
1955   return STATUS_SUCCESS;
1956 }
1957
1958
1959 NTSTATUS
1960 CmiAllocateBlock(PREGISTRY_HIVE RegistryHive,
1961                  PVOID *Block,
1962                  LONG BlockSize,
1963                  BLOCK_OFFSET * pBlockOffset)
1964 {
1965   PCELL_HEADER NewBlock;
1966   NTSTATUS Status;
1967   PHBIN pBin;
1968
1969   Status = STATUS_SUCCESS;
1970
1971   /* Round to 16 bytes multiple */
1972   BlockSize = (BlockSize + sizeof(DWORD) + 15) & 0xfffffff0;
1973
1974   /* Handle volatile hives first */
1975   if (IsVolatileHive(RegistryHive))
1976     {
1977       NewBlock = ExAllocatePool(NonPagedPool, BlockSize);
1978
1979       if (NewBlock == NULL)
1980         {
1981           Status = STATUS_INSUFFICIENT_RESOURCES;
1982         }
1983       else
1984         {
1985           RtlZeroMemory(NewBlock, BlockSize);
1986           NewBlock->CellSize = BlockSize;
1987           CmiLockBlock(RegistryHive, NewBlock);
1988           *Block = NewBlock;
1989           if (pBlockOffset)
1990             *pBlockOffset = (BLOCK_OFFSET) NewBlock;
1991         }
1992     }
1993   else
1994     {
1995       ULONG i;
1996
1997       /* first search in free blocks */
1998       NewBlock = NULL;
1999       for (i = 0; i < RegistryHive->FreeListSize; i++)
2000         {
2001           if (RegistryHive->FreeList[i]->CellSize >= BlockSize)
2002             {
2003               PVOID Temp;
2004
2005               NewBlock = RegistryHive->FreeList[i];
2006               if (pBlockOffset)
2007                 *pBlockOffset = RegistryHive->FreeListOffset[i];
2008
2009               /* Update time of heap */
2010               Temp = CmiGetBlock(RegistryHive, RegistryHive->FreeListOffset[i], &pBin);
2011
2012               if (Temp)
2013                 {
2014                   ZwQuerySystemTime((PTIME) &pBin->DateModified);
2015                   CmiMarkBlockDirty(RegistryHive, RegistryHive->FreeListOffset[i]);
2016                 }
2017
2018               if ((i + 1) < RegistryHive->FreeListSize)
2019                 {
2020                   RtlMoveMemory(&RegistryHive->FreeList[i],
2021                                 &RegistryHive->FreeList[i + 1],
2022                                 sizeof(RegistryHive->FreeList[0])
2023                                   * (RegistryHive->FreeListSize - i - 1));
2024                   RtlMoveMemory(&RegistryHive->FreeListOffset[i],
2025                                 &RegistryHive->FreeListOffset[i + 1],
2026                                 sizeof(RegistryHive->FreeListOffset[0])
2027                                   * (RegistryHive->FreeListSize - i - 1));
2028                 }
2029               RegistryHive->FreeListSize--;
2030               break;
2031             }
2032         }
2033
2034       /* Need to extend hive file : */
2035       if (NewBlock == NULL)
2036         {
2037           /* Add a new block */
2038           Status = CmiAddBin(RegistryHive, (PVOID *) &NewBlock , pBlockOffset);
2039         }
2040
2041       if (NT_SUCCESS(Status))
2042         {
2043           *Block = NewBlock;
2044
2045           /* Split the block in two parts */
2046           if (NewBlock->CellSize > BlockSize)
2047             {
2048               NewBlock = (PCELL_HEADER) ((ULONG_PTR) NewBlock+BlockSize);
2049               NewBlock->CellSize = ((PCELL_HEADER) (*Block))->CellSize - BlockSize;
2050               CmiAddFree(RegistryHive, NewBlock, *pBlockOffset + BlockSize);
2051               CmiMarkBlockDirty(RegistryHive, *pBlockOffset + BlockSize);
2052             }
2053           else if (NewBlock->CellSize < BlockSize)
2054             {
2055               return(STATUS_UNSUCCESSFUL);
2056             }
2057
2058           RtlZeroMemory(*Block, BlockSize);
2059           ((PCELL_HEADER) (*Block))->CellSize = -BlockSize;
2060           CmiLockBlock(RegistryHive, *Block);
2061         }
2062     }
2063
2064   return(Status);
2065 }
2066
2067
2068 NTSTATUS
2069 CmiDestroyBlock(PREGISTRY_HIVE RegistryHive,
2070   PVOID Block,
2071   BLOCK_OFFSET Offset)
2072 {
2073   NTSTATUS Status;
2074   PHBIN pBin;
2075
2076   Status = STATUS_SUCCESS;
2077
2078   if (IsVolatileHive(RegistryHive))
2079     {
2080       CmiReleaseBlock(RegistryHive, Block);
2081       ExFreePool(Block);
2082     }
2083   else
2084     {
2085       PCELL_HEADER pFree = Block;
2086
2087       if (pFree->CellSize < 0)
2088         pFree->CellSize = -pFree->CellSize;
2089
2090       /* Clear block (except the block size) */
2091       RtlZeroMemory(((PVOID)pFree) + sizeof(ULONG),
2092                     pFree->CellSize - sizeof(ULONG));
2093
2094       CmiAddFree(RegistryHive, Block, Offset);
2095       CmiReleaseBlock(RegistryHive, Block);
2096
2097       /* Update time of heap */
2098       if (IsPermanentHive(RegistryHive) && CmiGetBlock(RegistryHive, Offset,&pBin))
2099         ZwQuerySystemTime((PTIME) &pBin->DateModified);
2100
2101       CmiMarkBlockDirty(RegistryHive, Offset);
2102
2103       /* FIXME: Set first dword to block_offset of another free block ? */
2104       /* FIXME: Concatenate with previous and next block if free */
2105     }
2106
2107   return Status;
2108 }
2109
2110
2111 NTSTATUS
2112 CmiAddFree(PREGISTRY_HIVE RegistryHive,
2113   PCELL_HEADER FreeBlock,
2114   BLOCK_OFFSET FreeOffset)
2115 {
2116         PCELL_HEADER *tmpList;
2117         BLOCK_OFFSET *tmpListOffset;
2118         LONG minInd;
2119   LONG maxInd;
2120   LONG medInd;
2121
2122   assert(RegistryHive);
2123   assert(FreeBlock);
2124
2125   DPRINT("FreeBlock %.08x  FreeOffset %.08x\n",
2126     FreeBlock, FreeOffset);
2127 DPRINT("\n");
2128   if ((RegistryHive->FreeListSize + 1) > RegistryHive->FreeListMax)
2129     {
2130 DPRINT("\n");
2131       tmpList = ExAllocatePool(PagedPool,
2132                           sizeof(PCELL_HEADER) * (RegistryHive->FreeListMax + 32));
2133 DPRINT("\n");
2134
2135       if (tmpList == NULL)
2136         return STATUS_INSUFFICIENT_RESOURCES;
2137 DPRINT("\n");
2138
2139       tmpListOffset = ExAllocatePool(PagedPool,
2140                           sizeof(BLOCK_OFFSET *) * (RegistryHive->FreeListMax + 32));
2141 DPRINT("\n");
2142
2143       if (tmpListOffset == NULL)
2144         {
2145           ExFreePool(tmpList);
2146           return STATUS_INSUFFICIENT_RESOURCES;
2147         }
2148 DPRINT("\n");
2149
2150       if (RegistryHive->FreeListMax)
2151         {
2152 DPRINT("\n");
2153           RtlMoveMemory(tmpList,
2154                         RegistryHive->FreeList,
2155                         sizeof(PCELL_HEADER) * (RegistryHive->FreeListMax));
2156 DPRINT("\n");
2157           RtlMoveMemory(tmpListOffset,
2158                         RegistryHive->FreeListOffset,
2159                         sizeof(BLOCK_OFFSET *) * (RegistryHive->FreeListMax));
2160 DPRINT("\n");
2161           ExFreePool(RegistryHive->FreeList);
2162 DPRINT("\n");
2163           ExFreePool(RegistryHive->FreeListOffset);
2164 DPRINT("\n");
2165         }
2166 DPRINT("\n");
2167       RegistryHive->FreeList = tmpList;
2168       RegistryHive->FreeListOffset = tmpListOffset;
2169       RegistryHive->FreeListMax += 32;
2170 DPRINT("\n");
2171     }
2172 DPRINT("\n");
2173
2174   /* Add new offset to free list, maintaining list in ascending order */
2175   if ((RegistryHive->FreeListSize == 0)
2176      || (RegistryHive->FreeListOffset[RegistryHive->FreeListSize-1] < FreeOffset))
2177     {
2178 DPRINT("\n");
2179       /* Add to end of list */
2180       RegistryHive->FreeList[RegistryHive->FreeListSize] = FreeBlock;
2181       RegistryHive->FreeListOffset[RegistryHive->FreeListSize++] = FreeOffset;
2182     }
2183   else if (RegistryHive->FreeListOffset[0] > FreeOffset)
2184     {
2185 DPRINT("\n");
2186       /* Add to begin of list */
2187       RtlMoveMemory(&RegistryHive->FreeList[1],
2188                     &RegistryHive->FreeList[0],
2189                     sizeof(RegistryHive->FreeList[0]) * RegistryHive->FreeListSize);
2190       RtlMoveMemory(&RegistryHive->FreeListOffset[1],
2191                     &RegistryHive->FreeListOffset[0],
2192                     sizeof(RegistryHive->FreeListOffset[0]) * RegistryHive->FreeListSize);
2193       RegistryHive->FreeList[0] = FreeBlock;
2194       RegistryHive->FreeListOffset[0] = FreeOffset;
2195       RegistryHive->FreeListSize++;
2196     }
2197   else
2198     {
2199 DPRINT("\n");
2200       /* Search where to insert */
2201       minInd = 0;
2202       maxInd = RegistryHive->FreeListSize - 1;
2203       while ((maxInd - minInd) > 1)
2204         {
2205           medInd = (minInd + maxInd) / 2;
2206           if (RegistryHive->FreeListOffset[medInd] > FreeOffset)
2207             maxInd = medInd;
2208           else
2209             minInd = medInd;
2210         }
2211
2212       /* Insert before maxInd */
2213       RtlMoveMemory(&RegistryHive->FreeList[maxInd+1],
2214                     &RegistryHive->FreeList[maxInd],
2215                     sizeof(RegistryHive->FreeList[0]) * (RegistryHive->FreeListSize - minInd));
2216       RtlMoveMemory(&RegistryHive->FreeListOffset[maxInd + 1],
2217                     &RegistryHive->FreeListOffset[maxInd],
2218                     sizeof(RegistryHive->FreeListOffset[0]) * (RegistryHive->FreeListSize-minInd));
2219       RegistryHive->FreeList[maxInd] = FreeBlock;
2220       RegistryHive->FreeListOffset[maxInd] = FreeOffset;
2221       RegistryHive->FreeListSize++;
2222     }
2223 DPRINT("\n");
2224
2225   return STATUS_SUCCESS;
2226 }
2227
2228
2229 PVOID
2230 CmiGetBlock(PREGISTRY_HIVE RegistryHive,
2231             BLOCK_OFFSET BlockOffset,
2232             PHBIN * ppBin)
2233 {
2234   if (ppBin)
2235     *ppBin = NULL;
2236
2237   if ((BlockOffset == 0) || (BlockOffset == (ULONG_PTR) -1))
2238     return NULL;
2239
2240   if (IsVolatileHive(RegistryHive))
2241     {
2242       return (PVOID) BlockOffset;
2243     }
2244   else
2245     {
2246       PHBIN pBin;
2247
2248       pBin = RegistryHive->BlockList[BlockOffset / 4096];
2249       if (ppBin)
2250         *ppBin = pBin;
2251       return ((PVOID) ((ULONG_PTR) pBin + (BlockOffset - pBin->BlockOffset)));
2252     }
2253 }
2254
2255
2256 VOID
2257 CmiLockBlock(PREGISTRY_HIVE RegistryHive,
2258              PVOID Block)
2259 {
2260   if (IsPermanentHive(RegistryHive))
2261     {
2262       /* FIXME: Implement */
2263     }
2264 }
2265
2266
2267 VOID
2268 CmiReleaseBlock(PREGISTRY_HIVE RegistryHive,
2269                 PVOID Block)
2270 {
2271   if (IsPermanentHive(RegistryHive))
2272     {
2273       /* FIXME: Implement */
2274     }
2275 }
2276
2277
2278 VOID
2279 CmiMarkBlockDirty(PREGISTRY_HIVE RegistryHive,
2280                   BLOCK_OFFSET BlockOffset)
2281 {
2282   ULONG Index;
2283
2284   if (IsVolatileHive(RegistryHive))
2285       return;
2286
2287   Index = (ULONG)BlockOffset / 4096;
2288
2289   DPRINT1("CmiMarkBlockDirty(Offset 0x%lx)  Index %lu\n",
2290           (ULONG)BlockOffset, Index);
2291
2292   RegistryHive->HiveDirty = TRUE;
2293   RtlSetBits(&RegistryHive->DirtyBitMap,
2294              Index,
2295              1);
2296 }
2297
2298
2299 ULONG
2300 CmiGetPackedNameLength(IN PUNICODE_STRING Name,
2301                        OUT PBOOLEAN Packable)
2302 {
2303   ULONG i;
2304
2305   if (Packable != NULL)
2306     *Packable = TRUE;
2307
2308   for (i = 0; i < Name->Length; i++)
2309     {
2310       if (Name->Buffer[i] > 0xFF)
2311         {
2312           if (Packable != NULL)
2313             *Packable = FALSE;
2314           return(Name->Length);
2315         }
2316     }
2317
2318   return(Name->Length / sizeof(WCHAR));
2319 }
2320
2321
2322 BOOLEAN
2323 CmiComparePackedNames(IN PUNICODE_STRING Name,
2324                       IN PCHAR NameBuffer,
2325                       IN USHORT NameBufferSize,
2326                       IN BOOLEAN NamePacked)
2327 {
2328   PWCHAR UNameBuffer;
2329   ULONG i;
2330
2331   if (NamePacked == TRUE)
2332     {
2333       if (Name->Length != NameBufferSize * sizeof(WCHAR))
2334         return(FALSE);
2335
2336       for (i = 0; i < Name->Length / sizeof(WCHAR); i++)
2337         {
2338           if (RtlUpcaseUnicodeChar(Name->Buffer[i]) != RtlUpcaseUnicodeChar((WCHAR)NameBuffer[i]))
2339             return(FALSE);
2340         }
2341     }
2342   else
2343     {
2344       if (Name->Length != NameBufferSize)
2345         return(FALSE);
2346
2347       UNameBuffer = (PWCHAR)NameBuffer;
2348
2349       for (i = 0; i < Name->Length / sizeof(WCHAR); i++)
2350         {
2351           if (RtlUpcaseUnicodeChar(Name->Buffer[i]) != RtlUpcaseUnicodeChar(UNameBuffer[i]))
2352             return(FALSE);
2353         }
2354     }
2355
2356   return(TRUE);
2357 }
2358
2359
2360 VOID
2361 CmiCopyPackedName(PWCHAR NameBuffer,
2362                   PCHAR PackedNameBuffer,
2363                   ULONG PackedNameSize)
2364 {
2365   ULONG i;
2366
2367   for (i = 0; i < PackedNameSize; i++)
2368     NameBuffer[i] = (WCHAR)PackedNameBuffer[i];
2369 }
2370
2371 /* EOF */