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