:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[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 <ddk/ntifs.h>
11 #include <roscfg.h>
12 #include <internal/ob.h>
13 #include <limits.h>
14 #include <string.h>
15 #include <internal/pool.h>
16 #include <internal/registry.h>
17
18 #define NDEBUG
19 #include <internal/debug.h>
20
21 #include "cm.h"
22
23
24
25 BOOLEAN CmiDoVerify = FALSE;
26
27 VOID
28 CmiCreateDefaultHiveHeader(PHIVE_HEADER Header)
29 {
30   assert(Header);
31   RtlZeroMemory(Header, sizeof(HIVE_HEADER));
32   Header->BlockId = REG_HIVE_ID;
33   Header->DateModified.dwLowDateTime = 0;
34   Header->DateModified.dwHighDateTime = 0;
35   Header->Version = 1;
36   Header->Unused3 = 1;
37   Header->Unused4 = 3;
38   Header->Unused5 = 0;
39   Header->Unused6 = 1;
40   Header->Unused7 = 1;
41   Header->RootKeyCell = 0;
42   Header->BlockSize = REG_BLOCK_SIZE;
43   Header->Unused6 = 1;
44   Header->Checksum = 0;
45 }
46
47
48 VOID
49 CmiCreateDefaultBinCell(PHBIN BinCell)
50 {
51   assert(BinCell);
52   RtlZeroMemory(BinCell, sizeof(HBIN));
53   BinCell->BlockId = REG_BIN_ID;
54   BinCell->DateModified.dwLowDateTime = 0;
55   BinCell->DateModified.dwHighDateTime = 0;
56   BinCell->BlockSize = REG_BLOCK_SIZE;
57 }
58
59
60 VOID
61 CmiCreateDefaultRootKeyCell(PKEY_CELL RootKeyCell)
62 {
63   assert(RootKeyCell);
64   RtlZeroMemory(RootKeyCell, sizeof(KEY_CELL));
65   RootKeyCell->CellSize = -sizeof(KEY_CELL);
66   RootKeyCell->Id = REG_KEY_CELL_ID;
67   RootKeyCell->Type = REG_ROOT_KEY_CELL_TYPE;
68   ZwQuerySystemTime((PTIME) &RootKeyCell->LastWriteTime);
69   RootKeyCell->ParentKeyOffset = 0;
70   RootKeyCell->NumberOfSubKeys = 0;
71   RootKeyCell->HashTableOffset = -1;
72   RootKeyCell->NumberOfValues = 0;
73   RootKeyCell->ValuesOffset = -1;
74   RootKeyCell->SecurityKeyOffset = 0;
75   RootKeyCell->ClassNameOffset = -1;
76   RootKeyCell->NameSize = 0;
77   RootKeyCell->ClassSize = 0;
78 }
79
80
81 VOID
82 CmiVerifyBinCell(PHBIN BinCell)
83 {
84   if (CmiDoVerify)
85     {
86
87   assert(BinCell);
88
89   if (BinCell->BlockId != REG_BIN_ID)
90     {
91       DbgPrint("BlockId is %.08x (should be %.08x)\n",
92         BinCell->BlockId, REG_BIN_ID);
93       assert(BinCell->BlockId == REG_BIN_ID);
94     }
95
96   //BinCell->DateModified.dwLowDateTime
97
98   //BinCell->DateModified.dwHighDateTime
99
100   
101   if (BinCell->BlockSize != REG_BLOCK_SIZE)
102     {
103       DbgPrint("BlockSize is %.08x (should be %.08x)\n",
104         BinCell->BlockSize, REG_BLOCK_SIZE);
105       assert(BinCell->BlockSize == REG_BLOCK_SIZE);
106     }
107
108     }
109 }
110
111
112 VOID
113 CmiVerifyKeyCell(PKEY_CELL KeyCell)
114 {
115   if (CmiDoVerify)
116     {
117
118   assert(KeyCell);
119
120   if (KeyCell->CellSize == 0)
121     {
122       DbgPrint("CellSize is %d (must not be 0)\n",
123         KeyCell->CellSize);
124       assert(KeyCell->CellSize != 0);
125     }
126
127   if (KeyCell->Id != REG_KEY_CELL_ID)
128     {
129       DbgPrint("Id is %.08x (should be %.08x)\n",
130         KeyCell->Id, REG_KEY_CELL_ID);
131       assert(KeyCell->Id == REG_KEY_CELL_ID);
132     }
133
134   if ((KeyCell->Type != REG_KEY_CELL_TYPE)
135     && (KeyCell->Type != REG_ROOT_KEY_CELL_TYPE))
136     {
137       DbgPrint("Type is %.08x (should be %.08x or %.08x)\n",
138         KeyCell->Type, REG_KEY_CELL_TYPE, REG_ROOT_KEY_CELL_TYPE);
139       assert(FALSE);
140     }
141
142   //KeyCell->LastWriteTime;
143
144   if (KeyCell->ParentKeyOffset < 0)
145     {
146       DbgPrint("ParentKeyOffset is %d (must not be < 0)\n",
147         KeyCell->ParentKeyOffset);
148       assert(KeyCell->ParentKeyOffset >= 0);
149     }
150
151   if (KeyCell->NumberOfSubKeys < 0)
152     {
153       DbgPrint("NumberOfSubKeys is %d (must not be < 0)\n",
154         KeyCell->NumberOfSubKeys);
155       assert(KeyCell->NumberOfSubKeys >= 0);
156     }
157
158   //KeyCell->HashTableOffset;
159
160   if (KeyCell->NumberOfValues < 0)
161     {
162       DbgPrint("NumberOfValues is %d (must not be < 0)\n",
163         KeyCell->NumberOfValues);
164       assert(KeyCell->NumberOfValues >= 0);
165     }
166
167   //KeyCell->ValuesOffset = -1;
168
169   if (KeyCell->SecurityKeyOffset < 0)
170     {
171       DbgPrint("SecurityKeyOffset is %d (must not be < 0)\n",
172         KeyCell->SecurityKeyOffset);
173       assert(KeyCell->SecurityKeyOffset >= 0);
174     }
175
176   //KeyCell->ClassNameOffset = -1;
177
178   //KeyCell->NameSize
179
180   //KeyCell->ClassSize
181
182     }
183 }
184
185
186 VOID
187 CmiVerifyRootKeyCell(PKEY_CELL RootKeyCell)
188 {
189   if (CmiDoVerify)
190     {
191
192   CmiVerifyKeyCell(RootKeyCell);
193
194   if (RootKeyCell->Type != REG_ROOT_KEY_CELL_TYPE)
195     {
196       DbgPrint("Type is %.08x (should be %.08x)\n",
197         RootKeyCell->Type, REG_ROOT_KEY_CELL_TYPE);
198       assert(RootKeyCell->Type == REG_ROOT_KEY_CELL_TYPE);
199     }
200
201     }
202 }
203
204
205 VOID
206 CmiVerifyValueCell(PVALUE_CELL ValueCell)
207 {
208   if (CmiDoVerify)
209     {
210
211   assert(ValueCell);
212
213   if (ValueCell->CellSize == 0)
214     {
215       DbgPrint("CellSize is %d (must not be 0)\n",
216         ValueCell->CellSize);
217       assert(ValueCell->CellSize != 0);
218     }
219
220   if (ValueCell->Id != REG_VALUE_CELL_ID)
221     {
222       DbgPrint("Id is %.08x (should be %.08x)\n",
223         ValueCell->Id, REG_VALUE_CELL_ID);
224       assert(ValueCell->Id == REG_VALUE_CELL_ID);
225     }
226
227   //ValueCell->NameSize;
228   //ValueCell->LONG  DataSize;
229   //ValueCell->DataOffset;
230   //ValueCell->ULONG  DataType;
231   //ValueCell->USHORT Flags;
232   //ValueCell->USHORT Unused1;
233   //ValueCell->UCHAR  Name[0];
234     }
235 }
236
237
238 VOID
239 CmiVerifyValueListCell(PVALUE_LIST_CELL ValueListCell)
240 {
241   if (CmiDoVerify)
242     {
243
244   if (ValueListCell->CellSize == 0)
245     {
246       DbgPrint("CellSize is %d (must not be 0)\n",
247         ValueListCell->CellSize);
248       assert(ValueListCell->CellSize != 0);
249     }
250
251     }
252 }
253
254
255 VOID
256 CmiVerifyKeyObject(PKEY_OBJECT KeyObject)
257 {
258   if (CmiDoVerify)
259     {
260
261   if (KeyObject->RegistryHive == NULL)
262     {
263       DbgPrint("RegistryHive is NULL (must not be NULL)\n",
264         KeyObject->RegistryHive);
265       assert(KeyObject->RegistryHive != NULL);
266     }
267
268   if (KeyObject->KeyCell == NULL)
269     {
270       DbgPrint("KeyCell is NULL (must not be NULL)\n",
271         KeyObject->KeyCell);
272       assert(KeyObject->KeyCell != NULL);
273     }
274
275   if (KeyObject->ParentKey == NULL)
276     {
277       DbgPrint("ParentKey is NULL (must not be NULL)\n",
278         KeyObject->ParentKey);
279       assert(KeyObject->ParentKey != NULL);
280     }
281
282     }
283 }
284
285
286 VOID
287 CmiVerifyHiveHeader(PHIVE_HEADER Header)
288 {
289   if (CmiDoVerify)
290     {
291
292   if (Header->BlockId != REG_HIVE_ID)
293     {
294       DbgPrint("BlockId is %.08x (must be %.08x)\n",
295         Header->BlockId,
296         REG_HIVE_ID);
297       assert(Header->BlockId == REG_HIVE_ID);
298     }
299
300   if (Header->Unused3 != 1)
301     {
302       DbgPrint("Unused3 is %.08x (must be 1)\n",
303         Header->Unused3);
304       assert(Header->Unused3 == 1);
305     }
306
307   if (Header->Unused4 != 3)
308     {
309       DbgPrint("Unused4 is %.08x (must be 3)\n",
310         Header->Unused4);
311       assert(Header->Unused4 == 3);
312     }
313
314   if (Header->Unused5 != 0)
315     {
316       DbgPrint("Unused5 is %.08x (must be 0)\n",
317         Header->Unused5);
318       assert(Header->Unused5 == 0);
319     }
320
321   if (Header->Unused6 != 1)
322     {
323       DbgPrint("Unused6 is %.08x (must be 1)\n",
324         Header->Unused6);
325       assert(Header->Unused6 == 1);
326     }
327
328   if (Header->Unused7 != 1)
329     {
330       DbgPrint("Unused7 is %.08x (must be 1)\n",
331         Header->Unused7);
332       assert(Header->Unused7 == 1);
333     }
334
335     }  
336 }
337
338
339 VOID
340 CmiVerifyRegistryHive(PREGISTRY_HIVE RegistryHive)
341 {
342   if (CmiDoVerify)
343     {
344
345       CmiVerifyHiveHeader(RegistryHive->HiveHeader);
346
347     }
348 }
349
350
351 NTSTATUS
352 CmiPopulateHive(HANDLE FileHandle)
353 {
354   IO_STATUS_BLOCK IoStatusBlock;
355   LARGE_INTEGER FileOffset;
356   PCELL_HEADER FreeCell;
357   NTSTATUS Status;
358   PHBIN BinCell;
359   PCHAR tBuf;
360   ULONG i;
361   
362   tBuf = (PCHAR) ExAllocatePool(NonPagedPool, REG_BLOCK_SIZE);
363   if (tBuf == NULL)
364     return STATUS_INSUFFICIENT_RESOURCES;
365
366   BinCell = (PHBIN) tBuf;
367   FreeCell = (PCELL_HEADER) (tBuf + REG_HBIN_DATA_OFFSET);
368
369   CmiCreateDefaultBinCell(BinCell);
370
371   // The whole block is free
372   FreeCell->CellSize = REG_BLOCK_SIZE - REG_HBIN_DATA_OFFSET;
373
374   // Add free blocks so we don't need to expand
375   // the file for a while
376   for (i = 0; i < 50; i++)
377     {
378       // Block offset of this bin
379       BinCell->BlockOffset = (2 + i) * REG_BLOCK_SIZE;
380
381       FileOffset.u.HighPart = 0;
382       FileOffset.u.LowPart   = (2 + i) * REG_BLOCK_SIZE;
383
384       Status = ZwWriteFile(FileHandle,
385                     NULL,
386                     NULL,
387                     NULL,
388                     &IoStatusBlock,
389                     tBuf,
390                     REG_BLOCK_SIZE,
391                     &FileOffset,
392         NULL);
393       assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
394       if (!NT_SUCCESS(Status))
395         {
396           ExFreePool(tBuf);
397           return Status;
398         }
399     }
400
401   ExFreePool(tBuf);
402
403   return Status;
404 }
405
406
407 NTSTATUS
408 CmiCreateNewRegFile(HANDLE FileHandle)
409 {
410   IO_STATUS_BLOCK IoStatusBlock;
411   PCELL_HEADER FreeCell;
412   PHIVE_HEADER HiveHeader;
413   PKEY_CELL RootKeyCell;
414   NTSTATUS Status;
415   PHBIN BinCell;
416   PCHAR tBuf;
417   
418   tBuf = (PCHAR) ExAllocatePool(NonPagedPool, 2 * REG_BLOCK_SIZE);
419   if (tBuf == NULL)
420     return STATUS_INSUFFICIENT_RESOURCES;
421     
422   HiveHeader = (PHIVE_HEADER) tBuf;
423   BinCell = (PHBIN) ((ULONG_PTR) tBuf + REG_BLOCK_SIZE);
424   RootKeyCell = (PKEY_CELL) ((ULONG_PTR) tBuf + REG_BLOCK_SIZE + REG_HBIN_DATA_OFFSET);
425   FreeCell = (PCELL_HEADER) ((ULONG_PTR) tBuf + REG_BLOCK_SIZE + REG_HBIN_DATA_OFFSET + sizeof(KEY_CELL));
426
427   CmiCreateDefaultHiveHeader(HiveHeader);
428   CmiCreateDefaultBinCell(BinCell);
429   CmiCreateDefaultRootKeyCell(RootKeyCell);
430
431   // First block
432   BinCell->BlockOffset = 0;
433
434   // Offset to root key block
435   HiveHeader->RootKeyCell = REG_HBIN_DATA_OFFSET;
436
437   // The rest of the block is free
438   FreeCell->CellSize = REG_BLOCK_SIZE - (REG_HBIN_DATA_OFFSET + sizeof(KEY_CELL));
439
440   Status = ZwWriteFile(FileHandle,
441     NULL,
442     NULL,
443     NULL,
444     &IoStatusBlock,
445     tBuf,
446     2 * REG_BLOCK_SIZE,
447     0,
448     NULL);
449
450   ExFreePool(tBuf);
451
452   assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
453
454 #if 1
455   if (NT_SUCCESS(Status))
456     {
457   CmiPopulateHive(FileHandle);
458     }
459 #endif
460
461   return Status;
462 }
463
464
465 NTSTATUS
466 CmiInitPermanentRegistryHive(PREGISTRY_HIVE RegistryHive,
467   PWSTR Filename,
468   BOOLEAN CreateNew)
469 {
470   OBJECT_ATTRIBUTES ObjectAttributes;
471   FILE_STANDARD_INFORMATION fsi;
472   PCELL_HEADER FreeBlock;
473   LARGE_INTEGER FileOffset;
474   BLOCK_OFFSET BlockOffset;
475   ULONG CreateDisposition;
476   IO_STATUS_BLOCK IoSB;
477   HANDLE FileHandle;
478   DWORD FreeOffset;
479   NTSTATUS Status;
480   //BOOLEAN Success;
481   PHBIN tmpBin;
482   ULONG i, j;
483
484   /* Duplicate Filename */
485   Status = RtlCreateUnicodeString(&RegistryHive->Filename, Filename);
486   if (!NT_SUCCESS(Status))
487     return Status;
488
489   InitializeObjectAttributes(&ObjectAttributes,
490                              &RegistryHive->Filename,
491                              0,
492                              NULL,
493                              NULL);
494
495   if (CreateNew)
496     CreateDisposition = FILE_OPEN_IF;
497   else
498     CreateDisposition = FILE_OPEN;
499
500   Status = NtCreateFile(&FileHandle,
501                 FILE_ALL_ACCESS,
502                 &ObjectAttributes,
503                 &IoSB,
504                 NULL,
505                 FILE_ATTRIBUTE_NORMAL,
506                 0,
507                 CreateDisposition,
508                 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
509                 NULL,
510                 0);
511
512   if ((CreateNew) && (IoSB.Information == FILE_CREATED))
513     {
514       Status = CmiCreateNewRegFile(FileHandle);
515     }
516
517   if (!NT_SUCCESS(Status))
518     {
519       RtlFreeUnicodeString(&RegistryHive->Filename);
520       return Status;
521     }
522
523   Status = ObReferenceObjectByHandle(FileHandle,
524                 FILE_ALL_ACCESS,
525                 IoFileObjectType,
526                 UserMode,
527                 (PVOID*) &RegistryHive->FileObject,
528                 NULL);
529
530   assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
531
532   if (!NT_SUCCESS(Status))
533     {
534       ZwClose(FileHandle);
535       RtlFreeUnicodeString(&RegistryHive->Filename);
536       return Status;
537     }
538
539   FileOffset.u.HighPart = 0;
540   FileOffset.u.LowPart = 0;
541   Status = ZwReadFile(FileHandle,
542                       0,
543                       0,
544                       0,
545                       0,
546                       RegistryHive->HiveHeader,
547                       sizeof(HIVE_HEADER),
548                       &FileOffset,
549                       0);
550
551   assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
552
553   if (!NT_SUCCESS(Status))
554     {
555       ObDereferenceObject(RegistryHive->FileObject);
556       RtlFreeUnicodeString(&RegistryHive->Filename);
557       return Status;
558     }
559
560   Status = ZwQueryInformationFile(FileHandle,
561     &IoSB,
562     &fsi,
563     sizeof(fsi),
564     FileStandardInformation);
565
566   assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
567
568   if (!NT_SUCCESS(Status))
569     {
570       ObDereferenceObject(RegistryHive->FileObject);
571       RtlFreeUnicodeString(&RegistryHive->Filename);
572       return Status;
573     }
574 #if 0
575   /* We have a reference to the file object so we don't need the handle anymore */
576   ZwClose(FileHandle);
577 #endif
578
579   RegistryHive->FileSize = fsi.EndOfFile.u.LowPart;
580   RegistryHive->BlockListSize = (RegistryHive->FileSize / 4096) - 1;
581
582   DPRINT("Space needed for block list describing hive: 0x%x\n",
583     sizeof(PHBIN *) * RegistryHive->BlockListSize);
584
585   RegistryHive->BlockList = ExAllocatePool(NonPagedPool,
586           sizeof(PHBIN *) * RegistryHive->BlockListSize);
587
588   if (RegistryHive->BlockList == NULL)
589     {
590       ExFreePool(RegistryHive->BlockList);
591       ObDereferenceObject(RegistryHive->FileObject);
592       RtlFreeUnicodeString(&RegistryHive->Filename);
593       return STATUS_INSUFFICIENT_RESOURCES;
594     }
595
596 #if 0
597   /* Map hive into cache memory (readonly) (skip the base block) */
598         FileOffset.u.HighPart = 0;
599         FileOffset.u.LowPart = 4096;
600   Success = CcMapData(RegistryHive->FileObject,   /* File object */
601     &FileOffset,                                  /* File offset */
602     RegistryHive->FileSize - 4096,                /* Region length */
603     TRUE,                                         /* Wait if needed */
604     &RegistryHive->Bcb,                           /* OUT: Buffer Control Block */
605     (PVOID*) &RegistryHive->BlockList[0]);        /* OUT: Mapped data pointer */
606
607   assertmsg(Success, ("Success: %d\n", Success));
608
609   if (!Success)
610     {
611       ExFreePool(RegistryHive->BlockList);
612       ObDereferenceObject(RegistryHive->FileObject);
613       RtlFreeUnicodeString(&RegistryHive->Filename);
614       return Status;
615     }
616
617 #else
618
619   RegistryHive->BlockList[0] = ExAllocatePool(PagedPool,
620           RegistryHive->FileSize - 4096);
621
622   if (RegistryHive->BlockList[0] == NULL)
623     {
624       ExFreePool(RegistryHive->BlockList);
625       ObDereferenceObject(RegistryHive->FileObject);
626       RtlFreeUnicodeString(&RegistryHive->Filename);
627       return STATUS_INSUFFICIENT_RESOURCES;
628     }
629
630   FileOffset.u.HighPart = 0;
631   FileOffset.u.LowPart = 4096;
632
633   Status = ZwReadFile(FileHandle,
634                 0,
635     0,
636     0,
637     0,
638                 (PVOID) RegistryHive->BlockList[0],
639                 RegistryHive->FileSize - 4096,
640                 &FileOffset,
641     0);
642
643   assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
644
645 #endif
646
647   RegistryHive->FreeListSize = 0;
648   RegistryHive->FreeListMax = 0;
649   RegistryHive->FreeList = NULL;
650
651   BlockOffset = 0;
652   for (i = 0; i < RegistryHive->BlockListSize; i++)
653     {
654       RegistryHive->BlockList[i] = (PHBIN) (((ULONG_PTR) RegistryHive->BlockList[0]) + BlockOffset);
655       tmpBin = (PHBIN) (((ULONG_PTR) RegistryHive->BlockList[i]));
656       if (tmpBin->BlockId != REG_BIN_ID)
657         {
658           DPRINT("Bad BlockId %x, offset %x\n", tmpBin->BlockId, BlockOffset);
659           KeBugCheck(0);
660         }
661
662       assertmsg((tmpBin->BlockSize % 4096) == 0, ("BlockSize (0x%.08x) must be multiplum of 4K\n", tmpBin->BlockSize));
663
664       if (tmpBin->BlockSize > 4096)
665         {
666           for (j = 1; j < tmpBin->BlockSize / 4096; j++)
667             {
668               RegistryHive->BlockList[i + j] = RegistryHive->BlockList[i];
669             }
670           i = i + j - 1;
671         }
672
673       /* Search free blocks and add to list */
674       FreeOffset = REG_HBIN_DATA_OFFSET;
675       while (FreeOffset < tmpBin->BlockSize)
676         {
677           FreeBlock = (PCELL_HEADER) ((ULONG_PTR) RegistryHive->BlockList[i] + FreeOffset);
678           if (FreeBlock->CellSize > 0)
679             {
680               Status = CmiAddFree(RegistryHive,
681                                   FreeBlock,
682                                   RegistryHive->BlockList[i]->BlockOffset + FreeOffset);
683
684               if (!NT_SUCCESS(Status))
685                 {
686                   /* FIXME: */
687                   assert(FALSE);
688                 }
689
690               FreeOffset += FreeBlock->CellSize;
691             }
692           else
693             {
694               FreeOffset -= FreeBlock->CellSize;
695             }
696         }
697       BlockOffset += tmpBin->BlockSize;
698     }
699
700   return STATUS_SUCCESS;
701 }
702
703
704 NTSTATUS
705 CmiInitVolatileRegistryHive(PREGISTRY_HIVE RegistryHive)
706 {
707   PKEY_CELL RootKeyCell;
708
709   RegistryHive->Flags |= HIVE_VOLATILE;
710
711   CmiCreateDefaultHiveHeader(RegistryHive->HiveHeader);
712   
713   RootKeyCell = (PKEY_CELL) ExAllocatePool(NonPagedPool, sizeof(KEY_CELL));
714
715   if (RootKeyCell == NULL)
716     return STATUS_INSUFFICIENT_RESOURCES;
717
718   CmiCreateDefaultRootKeyCell(RootKeyCell);
719
720   RegistryHive->HiveHeader->RootKeyCell = (BLOCK_OFFSET) RootKeyCell;
721
722   return STATUS_SUCCESS;
723 }
724
725
726 NTSTATUS
727 CmiCreateRegistryHive(PWSTR Filename,
728   PREGISTRY_HIVE *RegistryHive,
729   BOOLEAN CreateNew)
730 {
731   PREGISTRY_HIVE Hive;
732   NTSTATUS Status;
733
734   DPRINT("CmiCreateRegistryHive(Filename %S)\n", Filename);
735
736   *RegistryHive = NULL;
737
738   Hive = ExAllocatePool(NonPagedPool, sizeof(REGISTRY_HIVE));
739   if (Hive == NULL)
740     return STATUS_INSUFFICIENT_RESOURCES;
741
742   DPRINT("Hive %x\n", Hive);
743
744   RtlZeroMemory(Hive, sizeof(REGISTRY_HIVE));
745
746   Hive->HiveHeader = (PHIVE_HEADER)
747     ExAllocatePool(NonPagedPool, sizeof(HIVE_HEADER));
748
749   if (Hive->HiveHeader == NULL)
750     {
751       ExFreePool(Hive);
752       return STATUS_INSUFFICIENT_RESOURCES;
753     }
754
755   if (Filename != NULL)
756     {
757       Status = CmiInitPermanentRegistryHive(Hive, Filename, CreateNew);
758     }
759   else
760     {
761       Status = CmiInitVolatileRegistryHive(Hive);
762     }
763
764   if (!NT_SUCCESS(Status))
765     {
766       ExFreePool(Hive->HiveHeader);
767       ExFreePool(Hive);
768       return(Status);
769     }
770
771   KeInitializeSemaphore(&Hive->RegSem, 1, 1);
772   VERIFY_REGISTRY_HIVE(Hive);
773
774   *RegistryHive = Hive;
775
776   return(STATUS_SUCCESS);
777 }
778
779
780 ULONG
781 CmiGetMaxNameLength(PREGISTRY_HIVE  RegistryHive,
782   PKEY_CELL  KeyCell)
783 {
784   PHASH_TABLE_CELL HashBlock;
785   PKEY_CELL CurSubKeyCell;
786   ULONG MaxName;
787   ULONG i;
788
789   VERIFY_KEY_CELL(KeyCell);
790
791   MaxName = 0;
792   HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
793   if (HashBlock == NULL)
794     {
795       return 0;
796     }
797
798   for (i = 0; i < HashBlock->HashTableSize; i++)
799     {
800       if (HashBlock->Table[i].KeyOffset != 0)
801         {
802           CurSubKeyCell = CmiGetBlock(RegistryHive,
803             HashBlock->Table[i].KeyOffset,
804             NULL);
805           if (MaxName < CurSubKeyCell->NameSize)
806             {
807               MaxName = CurSubKeyCell->NameSize;
808             }
809           CmiReleaseBlock(RegistryHive, CurSubKeyCell);
810         }
811     }
812
813   CmiReleaseBlock(RegistryHive, HashBlock);
814   
815   return MaxName;
816 }
817
818
819 ULONG
820 CmiGetMaxClassLength(PREGISTRY_HIVE  RegistryHive,
821   PKEY_CELL  KeyCell)
822 {
823   PHASH_TABLE_CELL HashBlock;
824   PKEY_CELL CurSubKeyCell;
825   ULONG MaxClass;
826   ULONG i;
827
828   VERIFY_KEY_CELL(KeyCell);
829
830   MaxClass = 0;
831   HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
832   if (HashBlock == NULL)
833     {
834       return 0;
835     }
836
837   for (i = 0; i < HashBlock->HashTableSize; i++)
838     {
839       if (HashBlock->Table[i].KeyOffset != 0)
840         {
841           CurSubKeyCell = CmiGetBlock(RegistryHive,
842             HashBlock->Table[i].KeyOffset,
843             NULL);
844           if (MaxClass < CurSubKeyCell->ClassSize)
845             {
846               MaxClass = CurSubKeyCell->ClassSize;
847             }
848           CmiReleaseBlock(RegistryHive, CurSubKeyCell);
849         }
850     }
851
852   CmiReleaseBlock(RegistryHive, HashBlock);
853
854   return MaxClass;
855 }
856
857
858 ULONG  
859 CmiGetMaxValueNameLength(PREGISTRY_HIVE RegistryHive,
860   PKEY_CELL KeyCell)
861 {
862   PVALUE_LIST_CELL ValueListCell;
863   PVALUE_CELL CurValueCell;
864   ULONG MaxValueName;
865   ULONG i;
866
867   VERIFY_KEY_CELL(KeyCell);
868
869   ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
870   MaxValueName = 0;
871   if (ValueListCell == NULL)
872     {
873       return 0;
874     }
875
876   for (i = 0; i < KeyCell->NumberOfValues; i++)
877     {
878       CurValueCell = CmiGetBlock(RegistryHive,
879         ValueListCell->Values[i],
880         NULL);
881       if (CurValueCell != NULL &&
882           MaxValueName < CurValueCell->NameSize)
883         {
884           MaxValueName = CurValueCell->NameSize;
885         }
886       CmiReleaseBlock(RegistryHive, CurValueCell);
887     }
888
889   CmiReleaseBlock(RegistryHive, ValueListCell);
890   
891   return MaxValueName;
892 }
893
894
895 ULONG  
896 CmiGetMaxValueDataLength(PREGISTRY_HIVE RegistryHive,
897   PKEY_CELL KeyCell)
898 {
899   PVALUE_LIST_CELL ValueListCell;
900   PVALUE_CELL CurValueCell;
901   ULONG MaxValueData;
902   ULONG i;
903
904   VERIFY_KEY_CELL(KeyCell);
905
906   ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
907   MaxValueData = 0;
908   if (ValueListCell == NULL)
909     {
910       return 0;
911     }
912
913   for (i = 0; i < KeyCell->NumberOfValues; i++)
914     {
915       CurValueCell = CmiGetBlock(RegistryHive,
916                                   ValueListCell->Values[i],NULL);
917       if ((CurValueCell != NULL) &&
918           (MaxValueData < (CurValueCell->DataSize & LONG_MAX)))
919         {
920           MaxValueData = CurValueCell->DataSize & LONG_MAX;
921         }
922       CmiReleaseBlock(RegistryHive, CurValueCell);
923     }
924
925   CmiReleaseBlock(RegistryHive, ValueListCell);
926   
927   return MaxValueData;
928 }
929
930
931 NTSTATUS
932 CmiScanForSubKey(IN PREGISTRY_HIVE RegistryHive,
933         IN PKEY_CELL KeyCell,
934         OUT PKEY_CELL *SubKeyCell,
935         OUT BLOCK_OFFSET *BlockOffset,
936         IN PCHAR KeyName,
937         IN ACCESS_MASK DesiredAccess,
938         IN ULONG Attributes)
939 {
940   PHASH_TABLE_CELL HashBlock;
941   PKEY_CELL CurSubKeyCell;
942   WORD KeyLength;
943   ULONG i;
944
945   VERIFY_KEY_CELL(KeyCell);
946
947   DPRINT("Scanning for sub key %s\n", KeyName);
948
949   assert(RegistryHive);
950
951   KeyLength = strlen(KeyName);
952
953   HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
954   *SubKeyCell = NULL;
955   if (HashBlock == NULL)
956     {
957       return STATUS_SUCCESS;
958     }
959
960   for (i = 0; (i < KeyCell->NumberOfSubKeys)
961                 && (i < HashBlock->HashTableSize); i++)
962     {
963       if (Attributes & OBJ_CASE_INSENSITIVE)
964         {
965           if ((HashBlock->Table[i].KeyOffset != 0) &&
966               (HashBlock->Table[i].KeyOffset != -1) &&
967               (_strnicmp(KeyName, (PCHAR) &HashBlock->Table[i].HashValue, 4) == 0))
968             {
969               CurSubKeyCell = CmiGetBlock(RegistryHive, 
970                 HashBlock->Table[i].KeyOffset,
971                 NULL);
972               if ((CurSubKeyCell->NameSize == KeyLength)
973                   && (_strnicmp(KeyName, CurSubKeyCell->Name, KeyLength) == 0))
974                 {
975                   *SubKeyCell = CurSubKeyCell;
976                   *BlockOffset = HashBlock->Table[i].KeyOffset;
977                   break;
978                 }
979               else
980                 {
981                   CmiReleaseBlock(RegistryHive, CurSubKeyCell);
982                 }
983             }
984         }
985       else
986         {
987           if (HashBlock->Table[i].KeyOffset != 0 &&
988               HashBlock->Table[i].KeyOffset != -1 &&
989               !strncmp(KeyName, (PCHAR) &HashBlock->Table[i].HashValue, 4))
990             {
991               CurSubKeyCell = CmiGetBlock(RegistryHive,
992                 HashBlock->Table[i].KeyOffset,NULL);
993               if (CurSubKeyCell->NameSize == KeyLength
994                   && !_strnicmp(KeyName, CurSubKeyCell->Name, KeyLength))
995                 {
996                   *SubKeyCell = CurSubKeyCell;
997                   *BlockOffset = HashBlock->Table[i].KeyOffset;
998                   break;
999                 }
1000               else
1001                 {
1002                   CmiReleaseBlock(RegistryHive, CurSubKeyCell);
1003                 }
1004             }
1005         }
1006     }
1007   
1008   CmiReleaseBlock(RegistryHive, HashBlock);
1009   
1010   return STATUS_SUCCESS;
1011 }
1012
1013
1014 NTSTATUS
1015 CmiAddSubKey(PREGISTRY_HIVE RegistryHive,
1016         PKEY_OBJECT Parent,
1017         PKEY_OBJECT SubKey,
1018         PWSTR NewSubKeyName,
1019         USHORT NewSubKeyNameSize,
1020         ULONG TitleIndex,
1021         PUNICODE_STRING Class,
1022         ULONG CreateOptions)
1023 {
1024   PHASH_TABLE_CELL NewHashBlock;
1025   PHASH_TABLE_CELL HashBlock;
1026   BLOCK_OFFSET NKBOffset;
1027   PKEY_CELL NewKeyCell; 
1028   ULONG NewBlockSize;
1029   PKEY_CELL KeyCell;
1030   NTSTATUS Status;
1031   USHORT NameSize;
1032
1033   KeyCell = Parent->KeyCell;
1034
1035   VERIFY_KEY_CELL(KeyCell);
1036
1037   if (NewSubKeyName[0] == L'\\')
1038     {
1039       NewSubKeyName++;
1040       NameSize = NewSubKeyNameSize / 2 - 1;
1041     }
1042   else
1043     {
1044       NameSize = NewSubKeyNameSize / 2;
1045     }
1046   Status = STATUS_SUCCESS;
1047
1048   NewBlockSize = sizeof(KEY_CELL) + NameSize;
1049   Status = CmiAllocateBlock(RegistryHive,
1050     (PVOID) &NewKeyCell,
1051     NewBlockSize,
1052     &NKBOffset);
1053
1054   if (NewKeyCell == NULL)
1055     {
1056       Status = STATUS_INSUFFICIENT_RESOURCES;
1057     }
1058   else
1059     {
1060             NewKeyCell->Id = REG_KEY_CELL_ID;
1061             NewKeyCell->Type = REG_KEY_CELL_TYPE;
1062             ZwQuerySystemTime((PTIME) &NewKeyCell->LastWriteTime);
1063             NewKeyCell->ParentKeyOffset = -1;
1064             NewKeyCell->NumberOfSubKeys = 0;
1065             NewKeyCell->HashTableOffset = -1;
1066             NewKeyCell->NumberOfValues = 0;
1067             NewKeyCell->ValuesOffset = -1;
1068             NewKeyCell->SecurityKeyOffset = -1;
1069             NewKeyCell->NameSize = NameSize;
1070             wcstombs(NewKeyCell->Name, NewSubKeyName, NameSize);
1071             NewKeyCell->ClassNameOffset = -1;
1072
1073       VERIFY_KEY_CELL(NewKeyCell);
1074
1075             if (Class)
1076                     {
1077                       PDATA_CELL pClass;
1078
1079                       NewKeyCell->ClassSize = Class->Length + sizeof(WCHAR);
1080                       Status = CmiAllocateBlock(RegistryHive,
1081                                           (PVOID) &pClass,
1082                                           NewKeyCell->ClassSize,
1083                                           &NewKeyCell->ClassNameOffset);
1084                       wcsncpy((PWSTR) pClass->Data, Class->Buffer, Class->Length);
1085                       ((PWSTR) (pClass->Data))[Class->Length] = 0;
1086                     }
1087     }
1088
1089   if (!NT_SUCCESS(Status))
1090     {
1091       return Status;
1092     }
1093
1094   SubKey->KeyCell = NewKeyCell;
1095   SubKey->BlockOffset = NKBOffset;
1096
1097   /* Don't modify hash table if key is volatile and parent is not */
1098   if (IsVolatileHive(RegistryHive) && (!IsVolatileHive(Parent->RegistryHive)))
1099           {
1100             return Status;
1101           }
1102
1103   if (KeyCell->HashTableOffset == -1)
1104     {
1105       Status = CmiAllocateHashTableBlock(RegistryHive,
1106         &HashBlock,
1107                                 &KeyCell->HashTableOffset,
1108         REG_INIT_HASH_TABLE_SIZE);
1109
1110       if (!NT_SUCCESS(Status))
1111         {
1112           return Status;
1113         }
1114     }
1115   else
1116     {
1117       HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
1118       if (((KeyCell->NumberOfSubKeys + 1) >= HashBlock->HashTableSize))
1119         {
1120           BLOCK_OFFSET HTOffset;
1121
1122           /* Reallocate the hash table block */
1123           Status = CmiAllocateHashTableBlock(RegistryHive,
1124             &NewHashBlock,
1125                                           &HTOffset,
1126             HashBlock->HashTableSize +
1127             REG_EXTEND_HASH_TABLE_SIZE);
1128
1129           if (!NT_SUCCESS(Status))
1130             {
1131               return Status;
1132             }
1133
1134           RtlZeroMemory(&NewHashBlock->Table[0],
1135             sizeof(NewHashBlock->Table[0]) * NewHashBlock->HashTableSize);
1136           RtlCopyMemory(&NewHashBlock->Table[0],
1137             &HashBlock->Table[0],
1138             sizeof(NewHashBlock->Table[0]) * HashBlock->HashTableSize);
1139           CmiDestroyBlock(RegistryHive, HashBlock, KeyCell->HashTableOffset);
1140                 KeyCell->HashTableOffset = HTOffset;
1141           HashBlock = NewHashBlock;
1142         }
1143     }
1144
1145   Status = CmiAddKeyToHashTable(RegistryHive, HashBlock, NewKeyCell, NKBOffset);
1146   if (NT_SUCCESS(Status))
1147     {
1148       KeyCell->NumberOfSubKeys++;
1149     }
1150   
1151   return Status;
1152 }
1153
1154
1155 NTSTATUS
1156 CmiScanKeyForValue(IN PREGISTRY_HIVE RegistryHive,
1157         IN PKEY_CELL KeyCell,
1158         IN PCHAR ValueName,
1159         OUT PVALUE_CELL *ValueCell,
1160         OUT BLOCK_OFFSET *VBOffset)
1161 {
1162   PVALUE_LIST_CELL ValueListCell;
1163   PVALUE_CELL CurValueCell;
1164   ULONG Length;
1165   ULONG i;
1166
1167   ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
1168
1169   *ValueCell = NULL;
1170
1171   if (ValueListCell == NULL)
1172     {
1173       DPRINT("ValueListCell is NULL\n");
1174       return STATUS_SUCCESS;
1175     }
1176
1177   VERIFY_VALUE_LIST_CELL(ValueListCell);
1178
1179   for (i = 0; i < KeyCell->NumberOfValues; i++)
1180     {
1181       CurValueCell = CmiGetBlock(RegistryHive,
1182         ValueListCell->Values[i],
1183         NULL);
1184       /* FIXME: perhaps we must not ignore case if NtCreateKey has not been */
1185       /*        called with OBJ_CASE_INSENSITIVE flag ? */
1186       Length = strlen(ValueName);
1187       if ((CurValueCell != NULL) &&
1188           (CurValueCell->NameSize == Length) &&
1189           (_strnicmp(CurValueCell->Name, ValueName, Length) == 0))
1190         {
1191           *ValueCell = CurValueCell;
1192                 if (VBOffset)
1193             *VBOffset = ValueListCell->Values[i];
1194           DPRINT("Found value %s\n", ValueName);
1195           break;
1196         }
1197       CmiReleaseBlock(RegistryHive, CurValueCell);
1198     }
1199
1200   CmiReleaseBlock(RegistryHive, ValueListCell);
1201   
1202   return STATUS_SUCCESS;
1203 }
1204
1205
1206 NTSTATUS
1207 CmiGetValueFromKeyByIndex(IN PREGISTRY_HIVE RegistryHive,
1208         IN PKEY_CELL KeyCell,
1209         IN ULONG Index,
1210         OUT PVALUE_CELL *ValueCell)
1211 {
1212   PVALUE_LIST_CELL ValueListCell;
1213   PVALUE_CELL CurValueCell;
1214  
1215   ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
1216
1217   *ValueCell = NULL;
1218
1219   if (ValueListCell == NULL)
1220     {
1221       return STATUS_NO_MORE_ENTRIES;
1222     }
1223
1224   VERIFY_VALUE_LIST_CELL(ValueListCell);
1225
1226   if (Index >= KeyCell->NumberOfValues)
1227     {
1228       return STATUS_NO_MORE_ENTRIES;
1229     }
1230
1231   CurValueCell = CmiGetBlock(RegistryHive,
1232     ValueListCell->Values[Index],
1233     NULL);
1234
1235   if (CurValueCell != NULL)
1236     {
1237       *ValueCell = CurValueCell;
1238     }
1239
1240   CmiReleaseBlock(RegistryHive, CurValueCell);
1241   CmiReleaseBlock(RegistryHive, ValueListCell);
1242   
1243   return STATUS_SUCCESS;
1244 }
1245
1246
1247 NTSTATUS
1248 CmiAddValueToKey(IN PREGISTRY_HIVE RegistryHive,
1249         IN PKEY_CELL KeyCell,
1250         IN PCHAR ValueNameBuf,
1251         OUT PVALUE_CELL *pValueCell,
1252         OUT BLOCK_OFFSET *pVBOffset)
1253 {
1254   PVALUE_LIST_CELL NewValueListCell;
1255   PVALUE_LIST_CELL ValueListCell;
1256   PVALUE_CELL NewValueCell;
1257   BLOCK_OFFSET VLBOffset;
1258   BLOCK_OFFSET VBOffset;
1259   NTSTATUS Status;
1260
1261   Status = CmiAllocateValueCell(RegistryHive,
1262                 &NewValueCell,
1263                 &VBOffset,
1264                 ValueNameBuf);
1265   *pVBOffset = VBOffset;
1266
1267   if (!NT_SUCCESS(Status))
1268     {
1269       return Status;
1270     }
1271
1272   ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
1273
1274   if (ValueListCell == NULL)
1275     {
1276       Status = CmiAllocateBlock(RegistryHive,
1277               (PVOID) &ValueListCell,
1278               sizeof(BLOCK_OFFSET) * 3,
1279               &VLBOffset);
1280
1281       if (!NT_SUCCESS(Status))
1282         {
1283           CmiDestroyValueCell(RegistryHive, NewValueCell, VBOffset);
1284           return Status;
1285         }
1286       KeyCell->ValuesOffset = VLBOffset;
1287     }
1288   else if ((KeyCell->NumberOfValues
1289                 >= ((LONG) (ValueListCell->CellSize - 4)) / (LONG) sizeof(BLOCK_OFFSET)))
1290     {
1291       Status = CmiAllocateBlock(RegistryHive,
1292               (PVOID) &NewValueListCell,
1293               sizeof(BLOCK_OFFSET) * (KeyCell->NumberOfValues + REG_VALUE_LIST_CELL_MULTIPLE),
1294               &VLBOffset);
1295
1296       if (!NT_SUCCESS(Status))
1297         {
1298           CmiDestroyValueCell(RegistryHive, NewValueCell, VBOffset);
1299           return Status;
1300         }
1301
1302       RtlCopyMemory(&NewValueListCell->Values[0],
1303         &ValueListCell->Values[0],
1304         sizeof(BLOCK_OFFSET) * KeyCell->NumberOfValues);
1305       CmiDestroyBlock(RegistryHive, ValueListCell, KeyCell->ValuesOffset);
1306       KeyCell->ValuesOffset = VLBOffset;
1307       ValueListCell = NewValueListCell;
1308     }
1309
1310   DPRINT("KeyCell->NumberOfValues %d, ValueListCell->CellSize %d (%d %x)\n",
1311          KeyCell->NumberOfValues, ValueListCell->CellSize,
1312          -(ValueListCell->CellSize - 4) / sizeof(BLOCK_OFFSET),
1313          -(ValueListCell->CellSize - 4) / sizeof(BLOCK_OFFSET));
1314
1315   ValueListCell->Values[KeyCell->NumberOfValues] = VBOffset;
1316   KeyCell->NumberOfValues++;
1317   CmiReleaseBlock(RegistryHive, ValueListCell);
1318   CmiReleaseBlock(RegistryHive, NewValueCell);
1319   *pValueCell = NewValueCell;
1320
1321   return STATUS_SUCCESS;
1322 }
1323
1324
1325 NTSTATUS
1326 CmiDeleteValueFromKey(IN PREGISTRY_HIVE RegistryHive,
1327                       IN PKEY_CELL KeyCell,
1328                       IN PCHAR ValueName)
1329 {
1330   PVALUE_LIST_CELL ValueListCell;
1331   PVALUE_CELL CurValueCell;
1332   ULONG  i;
1333
1334   ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
1335
1336   if (ValueListCell == NULL)
1337     {
1338       return STATUS_SUCCESS;
1339     }
1340
1341   VERIFY_VALUE_LIST_CELL(ValueListCell);
1342
1343   for (i = 0; i < KeyCell->NumberOfValues; i++)
1344     {
1345       CurValueCell = CmiGetBlock(RegistryHive, ValueListCell->Values[i], NULL);
1346       if ((CurValueCell != NULL) &&
1347           (CurValueCell->NameSize == strlen(ValueName)) &&
1348           (memcmp(CurValueCell->Name, ValueName, strlen(ValueName)) == 0))
1349         {
1350           if ((KeyCell->NumberOfValues - 1) < i)
1351             {
1352               RtlCopyMemory(&ValueListCell->Values[i],
1353                 &ValueListCell->Values[i + 1],
1354                 sizeof(BLOCK_OFFSET) * (KeyCell->NumberOfValues - 1 - i));
1355             }
1356           else
1357             {
1358               RtlZeroMemory(&ValueListCell->Values[i], sizeof(BLOCK_OFFSET));
1359             }
1360
1361           KeyCell->NumberOfValues -= 1;
1362           CmiDestroyValueCell(RegistryHive, CurValueCell, ValueListCell->Values[i]);
1363           break;
1364         }
1365       CmiReleaseBlock(RegistryHive, CurValueCell);
1366     }
1367
1368   CmiReleaseBlock(RegistryHive, ValueListCell);
1369
1370   return STATUS_SUCCESS;
1371 }
1372
1373
1374 NTSTATUS
1375 CmiAllocateHashTableBlock(IN PREGISTRY_HIVE RegistryHive,
1376         OUT PHASH_TABLE_CELL *HashBlock,
1377         OUT BLOCK_OFFSET *HBOffset,
1378         IN ULONG HashTableSize)
1379 {
1380   PHASH_TABLE_CELL NewHashBlock;
1381   ULONG NewHashSize;
1382   NTSTATUS Status;
1383
1384   Status = STATUS_SUCCESS;
1385   *HashBlock = NULL;
1386   NewHashSize = sizeof(HASH_TABLE_CELL) + 
1387     (HashTableSize - 1) * sizeof(HASH_RECORD);
1388   Status = CmiAllocateBlock(RegistryHive,
1389                 (PVOID*) &NewHashBlock,
1390                 NewHashSize,
1391     HBOffset);
1392
1393   if ((NewHashBlock == NULL) || (!NT_SUCCESS(Status)))
1394     {
1395       Status = STATUS_INSUFFICIENT_RESOURCES;
1396     }
1397   else
1398     {
1399       NewHashBlock->Id = REG_HASH_TABLE_BLOCK_ID;
1400       NewHashBlock->HashTableSize = HashTableSize;
1401       *HashBlock = NewHashBlock;
1402     }
1403
1404   return Status;
1405 }
1406
1407
1408 PKEY_CELL
1409 CmiGetKeyFromHashByIndex(PREGISTRY_HIVE RegistryHive,
1410         PHASH_TABLE_CELL HashBlock,
1411         ULONG Index)
1412 {
1413   BLOCK_OFFSET KeyOffset;
1414   PKEY_CELL KeyCell;
1415
1416   if (HashBlock == NULL)
1417     return NULL;
1418
1419   if (IsVolatileHive(RegistryHive))
1420     {
1421       KeyCell = (PKEY_CELL) HashBlock->Table[Index].KeyOffset;
1422     }
1423   else
1424     {
1425       KeyOffset =  HashBlock->Table[Index].KeyOffset;
1426       KeyCell = CmiGetBlock(RegistryHive, KeyOffset, NULL);
1427     }
1428   CmiLockBlock(RegistryHive, KeyCell);
1429
1430   return KeyCell;
1431 }
1432
1433
1434 NTSTATUS
1435 CmiAddKeyToHashTable(PREGISTRY_HIVE RegistryHive,
1436         PHASH_TABLE_CELL HashBlock,
1437         PKEY_CELL NewKeyCell,
1438         BLOCK_OFFSET NKBOffset)
1439 {
1440   ULONG i;
1441
1442   for (i = 0; i < HashBlock->HashTableSize; i++)
1443     {
1444       if (HashBlock->Table[i].KeyOffset == 0)
1445         {
1446           HashBlock->Table[i].KeyOffset = NKBOffset;
1447           RtlCopyMemory(&HashBlock->Table[i].HashValue, NewKeyCell->Name, 4);
1448           return STATUS_SUCCESS;
1449         }
1450     }
1451
1452   return STATUS_UNSUCCESSFUL;
1453 }
1454
1455
1456 NTSTATUS
1457 CmiAllocateValueCell(PREGISTRY_HIVE RegistryHive,
1458   PVALUE_CELL *ValueCell,
1459   BLOCK_OFFSET *VBOffset,
1460   IN PCHAR ValueNameBuf)
1461 {
1462   PVALUE_CELL NewValueCell;
1463   ULONG NewValueSize;
1464   NTSTATUS Status;
1465
1466   Status = STATUS_SUCCESS;
1467
1468   NewValueSize = sizeof(VALUE_CELL) + strlen(ValueNameBuf);
1469   Status = CmiAllocateBlock(RegistryHive,
1470     (PVOID*) &NewValueCell,
1471     NewValueSize,
1472     VBOffset);
1473
1474   if ((NewValueCell == NULL) || (!NT_SUCCESS(Status)))
1475     {
1476       Status = STATUS_INSUFFICIENT_RESOURCES;
1477     }
1478   else
1479     {
1480       NewValueCell->Id = REG_VALUE_CELL_ID;
1481       NewValueCell->NameSize = strlen(ValueNameBuf);
1482       memcpy(NewValueCell->Name, ValueNameBuf, strlen(ValueNameBuf));
1483       NewValueCell->DataType = 0;
1484       NewValueCell->DataSize = 0;
1485       NewValueCell->DataOffset = 0xffffffff;
1486       *ValueCell = NewValueCell;
1487     }
1488
1489   return Status;
1490 }
1491
1492
1493 NTSTATUS
1494 CmiDestroyValueCell(PREGISTRY_HIVE RegistryHive,
1495   PVALUE_CELL ValueCell,
1496   BLOCK_OFFSET VBOffset)
1497 {
1498   NTSTATUS Status;
1499   PVOID pBlock;
1500   PHBIN pBin;
1501
1502   VERIFY_VALUE_CELL(ValueCell);
1503
1504   /* First, release datas: */
1505   if (ValueCell->DataSize > 0)
1506     {
1507       pBlock = CmiGetBlock(RegistryHive, ValueCell->DataOffset, &pBin);
1508       Status = CmiDestroyBlock(RegistryHive, pBlock, ValueCell->DataOffset);
1509       if (!NT_SUCCESS(Status))
1510         {
1511           return  Status;
1512         }
1513
1514       /* Update time of heap */
1515       if (IsPermanentHive(RegistryHive))
1516         ZwQuerySystemTime((PTIME) &pBin->DateModified);
1517     }
1518
1519   Status = CmiDestroyBlock(RegistryHive, ValueCell, VBOffset);
1520
1521   /* Update time of heap */
1522   if (IsPermanentHive(RegistryHive) && CmiGetBlock(RegistryHive, VBOffset, &pBin))
1523     {
1524       ZwQuerySystemTime((PTIME) &pBin->DateModified);
1525     }
1526
1527   return Status;
1528 }
1529
1530
1531 NTSTATUS
1532 CmiAddBin(PREGISTRY_HIVE RegistryHive,
1533   PVOID *NewBlock,
1534   BLOCK_OFFSET *NewBlockOffset)
1535 {
1536   PCELL_HEADER tmpBlock;
1537   PHBIN * tmpBlockList;
1538   PHBIN tmpBin;
1539
1540   tmpBin = ExAllocatePool(PagedPool, REG_BLOCK_SIZE);
1541   if (tmpBin == NULL)
1542     {
1543       return STATUS_INSUFFICIENT_RESOURCES;
1544     }
1545
1546   tmpBin->BlockId = REG_BIN_ID;
1547   tmpBin->BlockOffset = RegistryHive->FileSize - REG_BLOCK_SIZE;
1548   RegistryHive->FileSize += REG_BLOCK_SIZE;
1549   tmpBin->BlockSize = REG_BLOCK_SIZE;
1550   tmpBin->Unused1 = 0;
1551   ZwQuerySystemTime((PTIME) &tmpBin->DateModified);
1552   tmpBin->Unused2 = 0;
1553
1554   /* Increase size of list of blocks */
1555   tmpBlockList = ExAllocatePool(NonPagedPool,
1556           sizeof(PHBIN *) * (RegistryHive->BlockListSize + 1));
1557   if (tmpBlockList == NULL)
1558     {
1559       ExFreePool(tmpBin);
1560       return STATUS_INSUFFICIENT_RESOURCES;
1561     }
1562
1563   if (RegistryHive->BlockListSize > 0)
1564     {
1565       memcpy(tmpBlockList,
1566              RegistryHive->BlockList,
1567              sizeof(PHBIN *)*(RegistryHive->BlockListSize));
1568       ExFreePool(RegistryHive->BlockList);
1569     }
1570
1571   RegistryHive->BlockList = tmpBlockList;
1572   RegistryHive->BlockList[RegistryHive->BlockListSize++] = tmpBin;
1573
1574   /* Initialize a free block in this heap : */
1575   tmpBlock = (PCELL_HEADER)((ULONG_PTR) tmpBin + REG_HBIN_DATA_OFFSET);
1576   tmpBlock->CellSize = (REG_BLOCK_SIZE - REG_HBIN_DATA_OFFSET);
1577   *NewBlock = (PVOID) tmpBlock;
1578
1579   if (NewBlockOffset)
1580     *NewBlockOffset = tmpBin->BlockOffset + REG_HBIN_DATA_OFFSET;
1581
1582   /* FIXME: set first dword to block_offset of another free bloc */
1583
1584   return STATUS_SUCCESS;
1585 }
1586
1587
1588 NTSTATUS
1589 CmiAllocateBlock(PREGISTRY_HIVE RegistryHive,
1590         PVOID *Block,
1591         LONG BlockSize,
1592   BLOCK_OFFSET * pBlockOffset)
1593 {
1594   PCELL_HEADER NewBlock;
1595   NTSTATUS Status;
1596   PHBIN pBin;
1597
1598   Status = STATUS_SUCCESS;
1599
1600   /* Round to 16 bytes multiple */
1601   BlockSize = (BlockSize + sizeof(DWORD) + 15) & 0xfffffff0;
1602
1603   /* Handle volatile hives first */
1604   if (IsVolatileHive(RegistryHive))
1605     {
1606       NewBlock = ExAllocatePool(NonPagedPool, BlockSize);
1607
1608             if (NewBlock == NULL)
1609                     {
1610                       Status = STATUS_INSUFFICIENT_RESOURCES;
1611                     }
1612             else
1613                     {
1614                       RtlZeroMemory(NewBlock, BlockSize);
1615                       NewBlock->CellSize = BlockSize;
1616                       CmiLockBlock(RegistryHive, NewBlock);
1617                       *Block = NewBlock;
1618                       if (pBlockOffset)
1619             *pBlockOffset = (BLOCK_OFFSET) NewBlock;
1620                     }
1621     }
1622   else
1623     {
1624             ULONG i;
1625
1626             /* first search in free blocks */
1627             NewBlock = NULL;
1628             for (i = 0; i < RegistryHive->FreeListSize; i++)
1629                     {
1630                       if (RegistryHive->FreeList[i]->CellSize >= BlockSize)
1631                               {
1632               PVOID Temp;
1633                                                         NewBlock = RegistryHive->FreeList[i];
1634
1635                                                         if (pBlockOffset)
1636                                         *pBlockOffset = RegistryHive->FreeListOffset[i];
1637
1638                                  /* Update time of heap */
1639                Temp = CmiGetBlock(RegistryHive, RegistryHive->FreeListOffset[i], &pBin);
1640
1641                                  if (Temp)
1642                                    ZwQuerySystemTime((PTIME) &pBin->DateModified);
1643
1644                                  if ((i + 1) < RegistryHive->FreeListSize)
1645                  {
1646                                      RtlMoveMemory(&RegistryHive->FreeList[i],
1647                      &RegistryHive->FreeList[i + 1],
1648                                              sizeof(RegistryHive->FreeList[0])
1649                        * (RegistryHive->FreeListSize - i - 1));
1650                                      RtlMoveMemory(&RegistryHive->FreeListOffset[i],
1651                                              &RegistryHive->FreeListOffset[i + 1],
1652                                              sizeof(RegistryHive->FreeListOffset[0])
1653                        * (RegistryHive->FreeListSize - i - 1));
1654                  }
1655                                  RegistryHive->FreeListSize--;
1656                                  break;
1657                               }
1658                     }
1659
1660             /* Need to extend hive file : */
1661             if (NewBlock == NULL)
1662                     {
1663                       /* Add a new block */
1664                       Status = CmiAddBin(RegistryHive, (PVOID *) &NewBlock , pBlockOffset);
1665                     }
1666
1667             if (NT_SUCCESS(Status))
1668                     {
1669                       *Block = NewBlock;
1670
1671                       /* Split the block in two parts */
1672                       if (NewBlock->CellSize > BlockSize)
1673                               {
1674                                                         NewBlock = (PCELL_HEADER) ((ULONG_PTR) NewBlock+BlockSize);
1675                                                         NewBlock->CellSize = ((PCELL_HEADER) (*Block))->CellSize - BlockSize;
1676                                                         CmiAddFree(RegistryHive, NewBlock, *pBlockOffset + BlockSize);
1677                               }
1678                       else if (NewBlock->CellSize < BlockSize)
1679             {
1680                                 return STATUS_UNSUCCESSFUL;
1681             }
1682                       RtlZeroMemory(*Block, BlockSize);
1683                       ((PCELL_HEADER) (*Block))->CellSize = -BlockSize;
1684                       CmiLockBlock(RegistryHive, *Block);
1685                     }
1686           }
1687   return  Status;
1688 }
1689
1690
1691 NTSTATUS
1692 CmiDestroyBlock(PREGISTRY_HIVE RegistryHive,
1693   PVOID Block,
1694   BLOCK_OFFSET Offset)
1695 {
1696   NTSTATUS Status;
1697   PHBIN pBin;
1698
1699   Status = STATUS_SUCCESS;
1700
1701   if (IsVolatileHive(RegistryHive))
1702           {
1703             CmiReleaseBlock(RegistryHive, Block);
1704             ExFreePool(Block);
1705           }
1706   else
1707           {
1708             PCELL_HEADER pFree = Block;
1709
1710             if (pFree->CellSize < 0)
1711               pFree->CellSize = -pFree->CellSize;
1712
1713             CmiAddFree(RegistryHive, Block, Offset);
1714             CmiReleaseBlock(RegistryHive, Block);
1715
1716             /* Update time of heap */
1717             if (IsPermanentHive(RegistryHive) && CmiGetBlock(RegistryHive, Offset,&pBin))
1718               ZwQuerySystemTime((PTIME) &pBin->DateModified);
1719
1720               /* FIXME: Set first dword to block_offset of another free block ? */
1721               /* FIXME: Concatenate with previous and next block if free */
1722           }
1723
1724   return Status;
1725 }
1726
1727
1728 NTSTATUS
1729 CmiAddFree(PREGISTRY_HIVE RegistryHive,
1730   PCELL_HEADER FreeBlock,
1731   BLOCK_OFFSET FreeOffset)
1732 {
1733         PCELL_HEADER *tmpList;
1734         BLOCK_OFFSET *tmpListOffset;
1735         LONG minInd;
1736   LONG maxInd;
1737   LONG medInd;
1738
1739   assert(RegistryHive);
1740   assert(FreeBlock);
1741
1742   DPRINT("FreeBlock %.08x  FreeOffset %.08x\n",
1743     FreeBlock, FreeOffset);
1744 DPRINT("\n");
1745   if ((RegistryHive->FreeListSize + 1) > RegistryHive->FreeListMax)
1746           {
1747 DPRINT("\n");
1748             tmpList = ExAllocatePool(PagedPool,
1749                           sizeof(PCELL_HEADER) * (RegistryHive->FreeListMax + 32));
1750 DPRINT("\n");
1751
1752             if (tmpList == NULL)
1753         return STATUS_INSUFFICIENT_RESOURCES;
1754 DPRINT("\n");
1755
1756             tmpListOffset = ExAllocatePool(PagedPool,
1757                           sizeof(BLOCK_OFFSET *) * (RegistryHive->FreeListMax + 32));
1758 DPRINT("\n");
1759
1760             if (tmpListOffset == NULL)
1761         {
1762           ExFreePool(tmpList);
1763           return STATUS_INSUFFICIENT_RESOURCES;
1764         }
1765 DPRINT("\n");
1766
1767             if (RegistryHive->FreeListMax)
1768             {
1769 DPRINT("\n");
1770         RtlMoveMemory(tmpList, RegistryHive->FreeList,
1771           sizeof(PCELL_HEADER) * (RegistryHive->FreeListMax));
1772 DPRINT("\n");
1773         RtlMoveMemory(tmpListOffset, RegistryHive->FreeListOffset,
1774           sizeof(BLOCK_OFFSET *) * (RegistryHive->FreeListMax));
1775 DPRINT("\n");
1776                     ExFreePool(RegistryHive->FreeList);
1777 DPRINT("\n");
1778                     ExFreePool(RegistryHive->FreeListOffset);
1779 DPRINT("\n");
1780       }
1781 DPRINT("\n");
1782             RegistryHive->FreeList = tmpList;
1783             RegistryHive->FreeListOffset = tmpListOffset;
1784             RegistryHive->FreeListMax += 32;
1785 DPRINT("\n");
1786           }
1787 DPRINT("\n");
1788
1789           /* Add new offset to free list, maintaining list in ascending order */
1790           if ((RegistryHive->FreeListSize == 0)
1791              || (RegistryHive->FreeListOffset[RegistryHive->FreeListSize-1] < FreeOffset))
1792                   {
1793 DPRINT("\n");
1794                     /* Add to end of list */
1795                     RegistryHive->FreeList[RegistryHive->FreeListSize] = FreeBlock;
1796                     RegistryHive->FreeListOffset[RegistryHive->FreeListSize++] = FreeOffset;
1797                   }
1798           else if (RegistryHive->FreeListOffset[0] > FreeOffset)
1799                   {
1800 DPRINT("\n");
1801                     /* Add to begin of list */
1802                     RtlMoveMemory(&RegistryHive->FreeList[1],
1803           &RegistryHive->FreeList[0],
1804                             sizeof(RegistryHive->FreeList[0]) * RegistryHive->FreeListSize);
1805                     RtlMoveMemory(&RegistryHive->FreeListOffset[1],
1806           &RegistryHive->FreeListOffset[0],
1807                             sizeof(RegistryHive->FreeListOffset[0]) * RegistryHive->FreeListSize);
1808                     RegistryHive->FreeList[0] = FreeBlock;
1809                     RegistryHive->FreeListOffset[0] = FreeOffset;
1810                     RegistryHive->FreeListSize++;
1811                   }
1812           else
1813           {
1814 DPRINT("\n");
1815             /* Search where to insert */
1816             minInd = 0;
1817             maxInd = RegistryHive->FreeListSize - 1;
1818             while ((maxInd - minInd) > 1)
1819                     {
1820                       medInd = (minInd + maxInd) / 2;
1821                       if (RegistryHive->FreeListOffset[medInd] > FreeOffset)
1822                         maxInd = medInd;
1823                       else
1824                         minInd = medInd;
1825                     }
1826
1827             /* Insert before maxInd */
1828             RtlMoveMemory(&RegistryHive->FreeList[maxInd+1],
1829         &RegistryHive->FreeList[maxInd],
1830                     sizeof(RegistryHive->FreeList[0]) * (RegistryHive->FreeListSize - minInd));
1831             RtlMoveMemory(&RegistryHive->FreeListOffset[maxInd + 1],
1832                     &RegistryHive->FreeListOffset[maxInd],
1833                     sizeof(RegistryHive->FreeListOffset[0]) * (RegistryHive->FreeListSize-minInd));
1834             RegistryHive->FreeList[maxInd] = FreeBlock;
1835             RegistryHive->FreeListOffset[maxInd] = FreeOffset;
1836             RegistryHive->FreeListSize++;
1837           }
1838 DPRINT("\n");
1839
1840   return STATUS_SUCCESS;
1841 }
1842
1843
1844 PVOID
1845 CmiGetBlock(PREGISTRY_HIVE RegistryHive,
1846         BLOCK_OFFSET BlockOffset,
1847         PHBIN * ppBin)
1848 {
1849   if (ppBin)
1850     *ppBin = NULL;
1851
1852   if ((BlockOffset == 0) || (BlockOffset == -1))
1853     return NULL;
1854
1855   if (IsVolatileHive(RegistryHive))
1856     {
1857       return (PVOID) BlockOffset;
1858     }
1859   else
1860     {
1861       PHBIN pBin;
1862
1863       pBin = RegistryHive->BlockList[BlockOffset / 4096];
1864       if (ppBin)
1865         *ppBin = pBin;
1866       return ((PVOID) ((ULONG_PTR) pBin + (BlockOffset - pBin->BlockOffset)));
1867     }
1868 }
1869
1870
1871 VOID
1872 CmiPrepareForWrite(PREGISTRY_HIVE RegistryHive,
1873   PHBIN pBin)
1874 {
1875   if (IsVolatileHive(RegistryHive))
1876     {
1877       /* No need to do anything special for volatile hives */
1878       return;
1879     }
1880   else
1881     {
1882       
1883     }
1884 }
1885
1886
1887 VOID
1888 CmiLockBlock(PREGISTRY_HIVE RegistryHive,
1889   PVOID Block)
1890 {
1891   if (IsPermanentHive(RegistryHive))
1892     {
1893       /* FIXME: Implement */
1894     }
1895 }
1896
1897
1898 VOID
1899 CmiReleaseBlock(PREGISTRY_HIVE RegistryHive,
1900   PVOID Block)
1901 {
1902   if (IsPermanentHive(RegistryHive))
1903     {
1904       /* FIXME: Implement */
1905     }
1906 }