Create hardware keys only if hardware hive was not imported.
[reactos.git] / reactos / 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 ULONG BlockOffset;
1176 PVOID BlockPtr;
1177 NTSTATUS Status;
1178
1179 DPRINT("CmiStartLogUpdate() called\n");
1180
1181 BitmapSize = ROUND_UP(RegistryHive->BlockListSize, sizeof(ULONG) * 8) / 8;
1182 BufferSize = sizeof(HIVE_HEADER) +
1183 sizeof(ULONG) +
1184 BitmapSize;
1185 BufferSize = ROUND_UP(BufferSize, 4096);
1186
1187 DPRINT("Bitmap size %lu buffer size: %lu\n", BitmapSize, BufferSize);
1188
1189 Buffer = (PUCHAR)ExAllocatePool(NonPagedPool, BufferSize);
1190 if (Buffer == NULL)
1191 {
1192 DPRINT("ExAllocatePool() failed\n");
1193 return(STATUS_INSUFFICIENT_RESOURCES);
1194 }
1195
1196 /* Open log file for writing */
1197 InitializeObjectAttributes(&ObjectAttributes,
1198 &RegistryHive->LogFileName,
1199 0,
1200 NULL,
1201 NULL);
1202
1203 Status = NtCreateFile(&FileHandle,
1204 FILE_ALL_ACCESS,
1205 &ObjectAttributes,
1206 &IoStatusBlock,
1207 NULL,
1208 FILE_ATTRIBUTE_NORMAL,
1209 0,
1210 FILE_SUPERSEDE,
1211 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1212 NULL,
1213 0);
1214 if (!NT_SUCCESS(Status))
1215 {
1216 DPRINT("NtCreateFile() failed (Status %lx)\n", Status);
1217 ExFreePool(Buffer);
1218 return(Status);
1219 }
1220
1221 /* Update firt update counter and checksum */
1222 RegistryHive->HiveHeader->UpdateCounter1 = RegistryHive->UpdateCounter + 1;
1223 RegistryHive->HiveHeader->Checksum = CmiCalcChecksum((PULONG)RegistryHive->HiveHeader);
1224
1225 /* Copy hive header */
1226 RtlCopyMemory(Buffer,
1227 RegistryHive->HiveHeader,
1228 sizeof(HIVE_HEADER));
1229 Ptr = Buffer + sizeof(HIVE_HEADER);
1230
1231 RtlCopyMemory(Ptr,
1232 "DIRT",
1233 4);
1234 Ptr += 4;
1235 RtlCopyMemory(Ptr,
1236 RegistryHive->DirtyBitMap.Buffer,
1237 BitmapSize);
1238
1239 /* Write hive block and block bitmap */
1240 FileOffset.QuadPart = 0ULL;
1241 Status = NtWriteFile(FileHandle,
1242 NULL,
1243 NULL,
1244 NULL,
1245 &IoStatusBlock,
1246 Buffer,
1247 BufferSize,
1248 &FileOffset,
1249 NULL);
1250 if (!NT_SUCCESS(Status))
1251 {
1252 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
1253 NtClose(FileHandle);
1254 ExFreePool(Buffer);
1255 return(Status);
1256 }
1257 ExFreePool(Buffer);
1258
1259 /* Write dirty blocks */
1260 FileOffset.QuadPart = (ULONGLONG)BufferSize;
1261 BlockIndex = 0;
1262 while (TRUE)
1263 {
1264 BlockIndex = RtlFindSetBits(&RegistryHive->DirtyBitMap,
1265 1,
1266 BlockIndex);
1267 if (BlockIndex == (ULONG)-1)
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 BlockOffset = RegistryHive->BlockList[BlockIndex]->BlockOffset;
1277 DPRINT("Block offset %lx\n", BlockOffset);
1278
1279 BlockPtr = RegistryHive->BlockList[BlockIndex] + ((BlockIndex * 4096) - BlockOffset);
1280 DPRINT("BlockPtr %p\n", BlockPtr);
1281
1282 DPRINT("File offset %I64x\n", FileOffset.QuadPart);
1283
1284 /* Write hive block */
1285 Status = NtWriteFile(FileHandle,
1286 NULL,
1287 NULL,
1288 NULL,
1289 &IoStatusBlock,
1290 BlockPtr,
1291 REG_BLOCK_SIZE,
1292 &FileOffset,
1293 NULL);
1294 if (!NT_SUCCESS(Status))
1295 {
1296 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
1297 NtClose(FileHandle);
1298 return(Status);
1299 }
1300
1301 BlockIndex++;
1302 FileOffset.QuadPart += 4096ULL;
1303 }
1304
1305 /* Truncate log file */
1306 EndOfFileInfo.EndOfFile.QuadPart = FileOffset.QuadPart;
1307 Status = NtSetInformationFile(FileHandle,
1308 &IoStatusBlock,
1309 &EndOfFileInfo,
1310 sizeof(FILE_END_OF_FILE_INFORMATION),
1311 FileEndOfFileInformation);
1312 if (!NT_SUCCESS(Status))
1313 {
1314 DPRINT("NtSetInformationFile() failed (Status %lx)\n", Status);
1315 NtClose(FileHandle);
1316 return(Status);
1317 }
1318
1319 FileAllocationInfo.AllocationSize.QuadPart = FileOffset.QuadPart;
1320 Status = NtSetInformationFile(FileHandle,
1321 &IoStatusBlock,
1322 &FileAllocationInfo,
1323 sizeof(FILE_ALLOCATION_INFORMATION),
1324 FileAllocationInformation);
1325 if (!NT_SUCCESS(Status))
1326 {
1327 DPRINT("NtSetInformationFile() failed (Status %lx)\n", Status);
1328 NtClose(FileHandle);
1329 return(Status);
1330 }
1331
1332 /* Flush the log file */
1333 Status = NtFlushBuffersFile(FileHandle,
1334 &IoStatusBlock);
1335 if (!NT_SUCCESS(Status))
1336 {
1337 DPRINT("NtFlushBuffersFile() failed (Status %lx)\n", Status);
1338 }
1339
1340 NtClose(FileHandle);
1341
1342 return(Status);
1343 }
1344
1345
1346 static NTSTATUS
1347 CmiFinishLogUpdate(PREGISTRY_HIVE RegistryHive)
1348 {
1349 OBJECT_ATTRIBUTES ObjectAttributes;
1350 IO_STATUS_BLOCK IoStatusBlock;
1351 HANDLE FileHandle;
1352 LARGE_INTEGER FileOffset;
1353 ULONG BufferSize;
1354 ULONG BitmapSize;
1355 PUCHAR Buffer;
1356 PUCHAR Ptr;
1357 NTSTATUS Status;
1358
1359 DPRINT("CmiFinishLogUpdate() called\n");
1360
1361 BitmapSize = ROUND_UP(RegistryHive->BlockListSize, sizeof(ULONG) * 8) / 8;
1362 BufferSize = sizeof(HIVE_HEADER) +
1363 sizeof(ULONG) +
1364 BitmapSize;
1365 BufferSize = ROUND_UP(BufferSize, 4096);
1366
1367 DPRINT("Bitmap size %lu buffer size: %lu\n", BitmapSize, BufferSize);
1368
1369 Buffer = (PUCHAR)ExAllocatePool(NonPagedPool, BufferSize);
1370 if (Buffer == NULL)
1371 {
1372 DPRINT("ExAllocatePool() failed\n");
1373 return(STATUS_INSUFFICIENT_RESOURCES);
1374 }
1375
1376 /* Open log file for writing */
1377 InitializeObjectAttributes(&ObjectAttributes,
1378 &RegistryHive->LogFileName,
1379 0,
1380 NULL,
1381 NULL);
1382
1383 Status = NtCreateFile(&FileHandle,
1384 FILE_ALL_ACCESS,
1385 &ObjectAttributes,
1386 &IoStatusBlock,
1387 NULL,
1388 FILE_ATTRIBUTE_NORMAL,
1389 0,
1390 FILE_OPEN,
1391 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1392 NULL,
1393 0);
1394 if (!NT_SUCCESS(Status))
1395 {
1396 DPRINT("NtCreateFile() failed (Status %lx)\n", Status);
1397 ExFreePool(Buffer);
1398 return(Status);
1399 }
1400
1401 /* Update first and second update counter and checksum */
1402 RegistryHive->HiveHeader->UpdateCounter1 = RegistryHive->UpdateCounter + 1;
1403 RegistryHive->HiveHeader->UpdateCounter2 = RegistryHive->UpdateCounter + 1;
1404 RegistryHive->HiveHeader->Checksum = CmiCalcChecksum((PULONG)RegistryHive->HiveHeader);
1405
1406 /* Copy hive header */
1407 RtlCopyMemory(Buffer,
1408 RegistryHive->HiveHeader,
1409 sizeof(HIVE_HEADER));
1410 Ptr = Buffer + sizeof(HIVE_HEADER);
1411
1412 /* Write empty block bitmap */
1413 RtlCopyMemory(Ptr,
1414 "DIRT",
1415 4);
1416 Ptr += 4;
1417 RtlZeroMemory(Ptr,
1418 BitmapSize);
1419
1420 /* Write hive block and block bitmap */
1421 FileOffset.QuadPart = 0ULL;
1422 Status = NtWriteFile(FileHandle,
1423 NULL,
1424 NULL,
1425 NULL,
1426 &IoStatusBlock,
1427 Buffer,
1428 BufferSize,
1429 &FileOffset,
1430 NULL);
1431 if (!NT_SUCCESS(Status))
1432 {
1433 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
1434 NtClose(FileHandle);
1435 ExFreePool(Buffer);
1436 return(Status);
1437 }
1438
1439 ExFreePool(Buffer);
1440
1441 /* Flush the log file */
1442 Status = NtFlushBuffersFile(FileHandle,
1443 &IoStatusBlock);
1444 if (!NT_SUCCESS(Status))
1445 {
1446 DPRINT("NtFlushBuffersFile() failed (Status %lx)\n", Status);
1447 }
1448
1449 NtClose(FileHandle);
1450
1451 return(Status);
1452 }
1453
1454
1455 static NTSTATUS
1456 CmiCleanupLogUpdate(PREGISTRY_HIVE RegistryHive)
1457 {
1458 FILE_END_OF_FILE_INFORMATION EndOfFileInfo;
1459 FILE_ALLOCATION_INFORMATION FileAllocationInfo;
1460 OBJECT_ATTRIBUTES ObjectAttributes;
1461 IO_STATUS_BLOCK IoStatusBlock;
1462 HANDLE FileHandle;
1463 ULONG BufferSize;
1464 ULONG BitmapSize;
1465 NTSTATUS Status;
1466
1467 DPRINT("CmiFinishLogUpdate() called\n");
1468
1469 BitmapSize = ROUND_UP(RegistryHive->BlockListSize, sizeof(ULONG) * 8) / 8;
1470 BufferSize = sizeof(HIVE_HEADER) +
1471 sizeof(ULONG) +
1472 BitmapSize;
1473 BufferSize = ROUND_UP(BufferSize, 4096);
1474
1475 DPRINT("Bitmap size %lu buffer size: %lu\n", BitmapSize, BufferSize);
1476
1477 /* Open log file for writing */
1478 InitializeObjectAttributes(&ObjectAttributes,
1479 &RegistryHive->LogFileName,
1480 0,
1481 NULL,
1482 NULL);
1483
1484 Status = NtCreateFile(&FileHandle,
1485 FILE_ALL_ACCESS,
1486 &ObjectAttributes,
1487 &IoStatusBlock,
1488 NULL,
1489 FILE_ATTRIBUTE_NORMAL,
1490 0,
1491 FILE_OPEN,
1492 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1493 NULL,
1494 0);
1495 if (!NT_SUCCESS(Status))
1496 {
1497 DPRINT("NtCreateFile() failed (Status %lx)\n", Status);
1498 return(Status);
1499 }
1500
1501 /* Truncate log file */
1502 EndOfFileInfo.EndOfFile.QuadPart = (ULONGLONG)BufferSize;
1503 Status = NtSetInformationFile(FileHandle,
1504 &IoStatusBlock,
1505 &EndOfFileInfo,
1506 sizeof(FILE_END_OF_FILE_INFORMATION),
1507 FileEndOfFileInformation);
1508 if (!NT_SUCCESS(Status))
1509 {
1510 DPRINT("NtSetInformationFile() failed (Status %lx)\n", Status);
1511 NtClose(FileHandle);
1512 return(Status);
1513 }
1514
1515 FileAllocationInfo.AllocationSize.QuadPart = (ULONGLONG)BufferSize;
1516 Status = NtSetInformationFile(FileHandle,
1517 &IoStatusBlock,
1518 &FileAllocationInfo,
1519 sizeof(FILE_ALLOCATION_INFORMATION),
1520 FileAllocationInformation);
1521 if (!NT_SUCCESS(Status))
1522 {
1523 DPRINT("NtSetInformationFile() failed (Status %lx)\n", Status);
1524 NtClose(FileHandle);
1525 return(Status);
1526 }
1527
1528 /* Flush the log file */
1529 Status = NtFlushBuffersFile(FileHandle,
1530 &IoStatusBlock);
1531 if (!NT_SUCCESS(Status))
1532 {
1533 DPRINT("NtFlushBuffersFile() failed (Status %lx)\n", Status);
1534 }
1535
1536 NtClose(FileHandle);
1537
1538 return(Status);
1539 }
1540
1541
1542 static NTSTATUS
1543 CmiStartHiveUpdate(PREGISTRY_HIVE RegistryHive)
1544 {
1545 OBJECT_ATTRIBUTES ObjectAttributes;
1546 IO_STATUS_BLOCK IoStatusBlock;
1547 HANDLE FileHandle;
1548 LARGE_INTEGER FileOffset;
1549 ULONG BlockIndex;
1550 ULONG BlockOffset;
1551 PVOID BlockPtr;
1552 NTSTATUS Status;
1553
1554 DPRINT("CmiStartHiveUpdate() called\n");
1555
1556 /* Open hive for writing */
1557 InitializeObjectAttributes(&ObjectAttributes,
1558 &RegistryHive->HiveFileName,
1559 0,
1560 NULL,
1561 NULL);
1562
1563 Status = NtCreateFile(&FileHandle,
1564 FILE_ALL_ACCESS,
1565 &ObjectAttributes,
1566 &IoStatusBlock,
1567 NULL,
1568 FILE_ATTRIBUTE_NORMAL,
1569 0,
1570 FILE_OPEN,
1571 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1572 NULL,
1573 0);
1574 if (!NT_SUCCESS(Status))
1575 {
1576 DPRINT("NtCreateFile() failed (Status %lx)\n", Status);
1577 return(Status);
1578 }
1579
1580 /* Update firt update counter and checksum */
1581 RegistryHive->HiveHeader->UpdateCounter1 = RegistryHive->UpdateCounter + 1;
1582 RegistryHive->HiveHeader->Checksum = CmiCalcChecksum((PULONG)RegistryHive->HiveHeader);
1583
1584 /* Write hive block */
1585 FileOffset.QuadPart = 0ULL;
1586 Status = NtWriteFile(FileHandle,
1587 NULL,
1588 NULL,
1589 NULL,
1590 &IoStatusBlock,
1591 RegistryHive->HiveHeader,
1592 sizeof(HIVE_HEADER),
1593 &FileOffset,
1594 NULL);
1595 if (!NT_SUCCESS(Status))
1596 {
1597 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
1598 NtClose(FileHandle);
1599 return(Status);
1600 }
1601
1602 BlockIndex = 0;
1603 while (TRUE)
1604 {
1605 BlockIndex = RtlFindSetBits(&RegistryHive->DirtyBitMap,
1606 1,
1607 BlockIndex);
1608 if (BlockIndex == (ULONG)-1)
1609 {
1610 DPRINT("No more set bits\n");
1611 Status = STATUS_SUCCESS;
1612 break;
1613 }
1614
1615 DPRINT("Block %lu is dirty\n", BlockIndex);
1616
1617 BlockOffset = RegistryHive->BlockList[BlockIndex]->BlockOffset;
1618 DPRINT("Block offset %lx\n", BlockOffset);
1619
1620 BlockPtr = RegistryHive->BlockList[BlockIndex] + ((BlockIndex * 4096) - BlockOffset);
1621 DPRINT("BlockPtr %p\n", BlockPtr);
1622
1623 FileOffset.QuadPart = (ULONGLONG)(BlockIndex + 1) * 4096ULL;
1624 DPRINT("File offset %I64x\n", FileOffset.QuadPart);
1625
1626
1627 /* Write hive block */
1628 Status = NtWriteFile(FileHandle,
1629 NULL,
1630 NULL,
1631 NULL,
1632 &IoStatusBlock,
1633 BlockPtr,
1634 REG_BLOCK_SIZE,
1635 &FileOffset,
1636 NULL);
1637 if (!NT_SUCCESS(Status))
1638 {
1639 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
1640 NtClose(FileHandle);
1641 return(Status);
1642 }
1643
1644 BlockIndex++;
1645 }
1646
1647 Status = NtFlushBuffersFile(FileHandle,
1648 &IoStatusBlock);
1649 if (!NT_SUCCESS(Status))
1650 {
1651 DPRINT("NtFlushBuffersFile() failed (Status %lx)\n", Status);
1652 }
1653
1654 NtClose(FileHandle);
1655
1656 return(Status);
1657 }
1658
1659
1660 static NTSTATUS
1661 CmiFinishHiveUpdate(PREGISTRY_HIVE RegistryHive)
1662 {
1663 OBJECT_ATTRIBUTES ObjectAttributes;
1664 IO_STATUS_BLOCK IoStatusBlock;
1665 LARGE_INTEGER FileOffset;
1666 HANDLE FileHandle;
1667 NTSTATUS Status;
1668
1669 DPRINT("CmiFinishHiveUpdate() called\n");
1670
1671 InitializeObjectAttributes(&ObjectAttributes,
1672 &RegistryHive->HiveFileName,
1673 0,
1674 NULL,
1675 NULL);
1676
1677 Status = NtCreateFile(&FileHandle,
1678 FILE_ALL_ACCESS,
1679 &ObjectAttributes,
1680 &IoStatusBlock,
1681 NULL,
1682 FILE_ATTRIBUTE_NORMAL,
1683 0,
1684 FILE_OPEN,
1685 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1686 NULL,
1687 0);
1688 if (!NT_SUCCESS(Status))
1689 {
1690 DPRINT("NtCreateFile() failed (Status %lx)\n", Status);
1691 return(Status);
1692 }
1693
1694 /* Update second update counter and checksum */
1695 RegistryHive->HiveHeader->UpdateCounter1 = RegistryHive->UpdateCounter + 1;
1696 RegistryHive->HiveHeader->UpdateCounter2 = RegistryHive->UpdateCounter + 1;
1697 RegistryHive->HiveHeader->Checksum = CmiCalcChecksum((PULONG)RegistryHive->HiveHeader);
1698
1699 /* Write hive block */
1700 FileOffset.QuadPart = 0ULL;
1701 Status = NtWriteFile(FileHandle,
1702 NULL,
1703 NULL,
1704 NULL,
1705 &IoStatusBlock,
1706 RegistryHive->HiveHeader,
1707 sizeof(HIVE_HEADER),
1708 &FileOffset,
1709 NULL);
1710 if (!NT_SUCCESS(Status))
1711 {
1712 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
1713 NtClose(FileHandle);
1714 return(Status);
1715 }
1716
1717 Status = NtFlushBuffersFile(FileHandle,
1718 &IoStatusBlock);
1719 if (!NT_SUCCESS(Status))
1720 {
1721 DPRINT("NtFlushBuffersFile() failed (Status %lx)\n", Status);
1722 }
1723
1724 NtClose(FileHandle);
1725
1726 return(Status);
1727 }
1728
1729
1730 NTSTATUS
1731 CmiFlushRegistryHive(PREGISTRY_HIVE RegistryHive)
1732 {
1733 NTSTATUS Status;
1734
1735 DPRINT("CmiFlushRegistryHive() called\n");
1736
1737 if (RegistryHive->HiveDirty == FALSE)
1738 {
1739 return(STATUS_SUCCESS);
1740 }
1741
1742 DPRINT("Hive '%wZ' is dirty\n",
1743 &RegistryHive->HiveFileName);
1744 DPRINT("Log file: '%wZ'\n",
1745 &RegistryHive->LogFileName);
1746
1747 /* Update hive header modification time */
1748 NtQuerySystemTime((PTIME)&RegistryHive->HiveHeader->DateModified);
1749
1750 /* Start log update */
1751 Status = CmiStartLogUpdate(RegistryHive);
1752 if (!NT_SUCCESS(Status))
1753 {
1754 DPRINT("CmiStartLogUpdate() failed (Status %lx)\n", Status);
1755 return(Status);
1756 }
1757
1758 /* Finish log update */
1759 Status = CmiFinishLogUpdate(RegistryHive);
1760 if (!NT_SUCCESS(Status))
1761 {
1762 DPRINT("CmiFinishLogUpdate() failed (Status %lx)\n", Status);
1763 return(Status);
1764 }
1765
1766 /* Start hive update */
1767 Status = CmiStartHiveUpdate(RegistryHive);
1768 if (!NT_SUCCESS(Status))
1769 {
1770 DPRINT("CmiStartHiveUpdate() failed (Status %lx)\n", Status);
1771 return(Status);
1772 }
1773
1774 /* Finish the hive update */
1775 Status = CmiFinishHiveUpdate(RegistryHive);
1776 if (!NT_SUCCESS(Status))
1777 {
1778 DPRINT("CmiFinishHiveUpdate() failed (Status %lx)\n", Status);
1779 return(Status);
1780 }
1781
1782 /* Cleanup log update */
1783 Status = CmiCleanupLogUpdate(RegistryHive);
1784 if (!NT_SUCCESS(Status))
1785 {
1786 DPRINT("CmiFinishLogUpdate() failed (Status %lx)\n", Status);
1787 return(Status);
1788 }
1789
1790 /* Increment hive update counter */
1791 RegistryHive->UpdateCounter++;
1792
1793 /* Clear dirty bitmap and dirty flag */
1794 RtlClearAllBits(&RegistryHive->DirtyBitMap);
1795 RegistryHive->HiveDirty = FALSE;
1796
1797 DPRINT("CmiFlushRegistryHive() done\n");
1798
1799 return(STATUS_SUCCESS);
1800 }
1801
1802
1803 ULONG
1804 CmiGetMaxNameLength(PREGISTRY_HIVE RegistryHive,
1805 PKEY_CELL KeyCell)
1806 {
1807 PHASH_TABLE_CELL HashBlock;
1808 PKEY_CELL CurSubKeyCell;
1809 ULONG MaxName;
1810 ULONG i;
1811
1812 VERIFY_KEY_CELL(KeyCell);
1813
1814 MaxName = 0;
1815 HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
1816 if (HashBlock == NULL)
1817 {
1818 return 0;
1819 }
1820
1821 for (i = 0; i < HashBlock->HashTableSize; i++)
1822 {
1823 if (HashBlock->Table[i].KeyOffset != 0)
1824 {
1825 CurSubKeyCell = CmiGetBlock(RegistryHive,
1826 HashBlock->Table[i].KeyOffset,
1827 NULL);
1828 if (MaxName < CurSubKeyCell->NameSize)
1829 {
1830 MaxName = CurSubKeyCell->NameSize;
1831 }
1832 }
1833 }
1834
1835 return MaxName;
1836 }
1837
1838
1839 ULONG
1840 CmiGetMaxClassLength(PREGISTRY_HIVE RegistryHive,
1841 PKEY_CELL KeyCell)
1842 {
1843 PHASH_TABLE_CELL HashBlock;
1844 PKEY_CELL CurSubKeyCell;
1845 ULONG MaxClass;
1846 ULONG i;
1847
1848 VERIFY_KEY_CELL(KeyCell);
1849
1850 MaxClass = 0;
1851 HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
1852 if (HashBlock == NULL)
1853 {
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 (MaxClass < CurSubKeyCell->ClassSize)
1865 {
1866 MaxClass = CurSubKeyCell->ClassSize;
1867 }
1868 }
1869 }
1870
1871 return MaxClass;
1872 }
1873
1874
1875 ULONG
1876 CmiGetMaxValueNameLength(PREGISTRY_HIVE RegistryHive,
1877 PKEY_CELL KeyCell)
1878 {
1879 PVALUE_LIST_CELL ValueListCell;
1880 PVALUE_CELL CurValueCell;
1881 ULONG MaxValueName;
1882 ULONG i;
1883
1884 VERIFY_KEY_CELL(KeyCell);
1885
1886 ValueListCell = CmiGetBlock(RegistryHive,
1887 KeyCell->ValuesOffset,
1888 NULL);
1889 MaxValueName = 0;
1890 if (ValueListCell == NULL)
1891 {
1892 return 0;
1893 }
1894
1895 for (i = 0; i < KeyCell->NumberOfValues; i++)
1896 {
1897 CurValueCell = CmiGetBlock(RegistryHive,
1898 ValueListCell->Values[i],
1899 NULL);
1900 if (CurValueCell != NULL &&
1901 MaxValueName < CurValueCell->NameSize)
1902 {
1903 MaxValueName = CurValueCell->NameSize;
1904 }
1905 }
1906
1907 return MaxValueName;
1908 }
1909
1910
1911 ULONG
1912 CmiGetMaxValueDataLength(PREGISTRY_HIVE RegistryHive,
1913 PKEY_CELL KeyCell)
1914 {
1915 PVALUE_LIST_CELL ValueListCell;
1916 PVALUE_CELL CurValueCell;
1917 LONG MaxValueData;
1918 ULONG i;
1919
1920 VERIFY_KEY_CELL(KeyCell);
1921
1922 ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
1923 MaxValueData = 0;
1924 if (ValueListCell == NULL)
1925 {
1926 return 0;
1927 }
1928
1929 for (i = 0; i < KeyCell->NumberOfValues; i++)
1930 {
1931 CurValueCell = CmiGetBlock(RegistryHive,
1932 ValueListCell->Values[i],NULL);
1933 if ((CurValueCell != NULL) &&
1934 (MaxValueData < (CurValueCell->DataSize & LONG_MAX)))
1935 {
1936 MaxValueData = CurValueCell->DataSize & LONG_MAX;
1937 }
1938 }
1939
1940 return MaxValueData;
1941 }
1942
1943
1944 NTSTATUS
1945 CmiScanForSubKey(IN PREGISTRY_HIVE RegistryHive,
1946 IN PKEY_CELL KeyCell,
1947 OUT PKEY_CELL *SubKeyCell,
1948 OUT BLOCK_OFFSET *BlockOffset,
1949 IN PCHAR KeyName,
1950 IN ACCESS_MASK DesiredAccess,
1951 IN ULONG Attributes)
1952 {
1953 PHASH_TABLE_CELL HashBlock;
1954 PKEY_CELL CurSubKeyCell;
1955 WORD KeyLength;
1956 ULONG i;
1957
1958 VERIFY_KEY_CELL(KeyCell);
1959
1960 //DPRINT("Scanning for sub key %s\n", KeyName);
1961
1962 assert(RegistryHive);
1963
1964 *SubKeyCell = NULL;
1965 KeyLength = strlen(KeyName);
1966
1967 HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
1968 if (HashBlock == NULL)
1969 {
1970 return STATUS_SUCCESS;
1971 }
1972
1973 for (i = 0; (i < KeyCell->NumberOfSubKeys)
1974 && (i < HashBlock->HashTableSize); i++)
1975 {
1976 if (Attributes & OBJ_CASE_INSENSITIVE)
1977 {
1978 if ((HashBlock->Table[i].KeyOffset != 0) &&
1979 (HashBlock->Table[i].KeyOffset != (ULONG_PTR)-1) &&
1980 (_strnicmp(KeyName, (PCHAR) &HashBlock->Table[i].HashValue, 4) == 0))
1981 {
1982 CurSubKeyCell = CmiGetBlock(RegistryHive,
1983 HashBlock->Table[i].KeyOffset,
1984 NULL);
1985 if ((CurSubKeyCell->NameSize == KeyLength)
1986 && (_strnicmp(KeyName, CurSubKeyCell->Name, KeyLength) == 0))
1987 {
1988 *SubKeyCell = CurSubKeyCell;
1989 *BlockOffset = HashBlock->Table[i].KeyOffset;
1990 break;
1991 }
1992 }
1993 }
1994 else
1995 {
1996 if (HashBlock->Table[i].KeyOffset != 0 &&
1997 HashBlock->Table[i].KeyOffset != (ULONG_PTR) -1 &&
1998 !strncmp(KeyName, (PCHAR) &HashBlock->Table[i].HashValue, 4))
1999 {
2000 CurSubKeyCell = CmiGetBlock(RegistryHive,
2001 HashBlock->Table[i].KeyOffset,NULL);
2002 if (CurSubKeyCell->NameSize == KeyLength
2003 && !_strnicmp(KeyName, CurSubKeyCell->Name, KeyLength))
2004 {
2005 *SubKeyCell = CurSubKeyCell;
2006 *BlockOffset = HashBlock->Table[i].KeyOffset;
2007 break;
2008 }
2009 }
2010 }
2011 }
2012
2013 return STATUS_SUCCESS;
2014 }
2015
2016
2017 NTSTATUS
2018 CmiAddSubKey(PREGISTRY_HIVE RegistryHive,
2019 PKEY_OBJECT Parent,
2020 PKEY_OBJECT SubKey,
2021 PWSTR NewSubKeyName,
2022 USHORT NewSubKeyNameSize,
2023 ULONG TitleIndex,
2024 PUNICODE_STRING Class,
2025 ULONG CreateOptions)
2026 {
2027 PHASH_TABLE_CELL NewHashBlock;
2028 PHASH_TABLE_CELL HashBlock;
2029 BLOCK_OFFSET NKBOffset;
2030 PKEY_CELL NewKeyCell;
2031 ULONG NewBlockSize;
2032 PKEY_CELL KeyCell;
2033 NTSTATUS Status;
2034 USHORT NameSize;
2035
2036 KeyCell = Parent->KeyCell;
2037
2038 VERIFY_KEY_CELL(KeyCell);
2039
2040 if (NewSubKeyName[0] == L'\\')
2041 {
2042 NewSubKeyName++;
2043 NameSize = NewSubKeyNameSize / 2 - 1;
2044 }
2045 else
2046 {
2047 NameSize = NewSubKeyNameSize / 2;
2048 }
2049 Status = STATUS_SUCCESS;
2050
2051 NewBlockSize = sizeof(KEY_CELL) + NameSize;
2052 Status = CmiAllocateBlock(RegistryHive,
2053 (PVOID) &NewKeyCell,
2054 NewBlockSize,
2055 &NKBOffset);
2056
2057 if (NewKeyCell == NULL)
2058 {
2059 Status = STATUS_INSUFFICIENT_RESOURCES;
2060 }
2061 else
2062 {
2063 NewKeyCell->Id = REG_KEY_CELL_ID;
2064 NewKeyCell->Type = REG_KEY_CELL_TYPE;
2065 ZwQuerySystemTime((PTIME) &NewKeyCell->LastWriteTime);
2066 NewKeyCell->ParentKeyOffset = -1;
2067 NewKeyCell->NumberOfSubKeys = 0;
2068 NewKeyCell->HashTableOffset = -1;
2069 NewKeyCell->NumberOfValues = 0;
2070 NewKeyCell->ValuesOffset = -1;
2071 NewKeyCell->SecurityKeyOffset = -1;
2072 NewKeyCell->NameSize = NameSize;
2073 wcstombs(NewKeyCell->Name, NewSubKeyName, NameSize);
2074 NewKeyCell->ClassNameOffset = -1;
2075
2076 VERIFY_KEY_CELL(NewKeyCell);
2077
2078 if (Class)
2079 {
2080 PDATA_CELL pClass;
2081
2082 NewKeyCell->ClassSize = Class->Length + sizeof(WCHAR);
2083 Status = CmiAllocateBlock(RegistryHive,
2084 (PVOID) &pClass,
2085 NewKeyCell->ClassSize,
2086 &NewKeyCell->ClassNameOffset);
2087 wcsncpy((PWSTR) pClass->Data, Class->Buffer, Class->Length);
2088 ((PWSTR) (pClass->Data))[Class->Length] = 0;
2089 }
2090 }
2091
2092 if (!NT_SUCCESS(Status))
2093 {
2094 return Status;
2095 }
2096
2097 SubKey->KeyCell = NewKeyCell;
2098 SubKey->BlockOffset = NKBOffset;
2099
2100 /* Don't modify hash table if key is located in a pointer-based hive and parent key is not */
2101 if (IsPointerHive(RegistryHive) && (!IsPointerHive(Parent->RegistryHive)))
2102 {
2103 return(Status);
2104 }
2105
2106 if (KeyCell->HashTableOffset == (ULONG_PTR) -1)
2107 {
2108 Status = CmiAllocateHashTableBlock(RegistryHive,
2109 &HashBlock,
2110 &KeyCell->HashTableOffset,
2111 REG_INIT_HASH_TABLE_SIZE);
2112 if (!NT_SUCCESS(Status))
2113 {
2114 return(Status);
2115 }
2116 }
2117 else
2118 {
2119 HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
2120 if (((KeyCell->NumberOfSubKeys + 1) >= HashBlock->HashTableSize))
2121 {
2122 BLOCK_OFFSET HTOffset;
2123
2124 /* Reallocate the hash table block */
2125 Status = CmiAllocateHashTableBlock(RegistryHive,
2126 &NewHashBlock,
2127 &HTOffset,
2128 HashBlock->HashTableSize +
2129 REG_EXTEND_HASH_TABLE_SIZE);
2130 if (!NT_SUCCESS(Status))
2131 {
2132 return Status;
2133 }
2134
2135 RtlZeroMemory(&NewHashBlock->Table[0],
2136 sizeof(NewHashBlock->Table[0]) * NewHashBlock->HashTableSize);
2137 RtlCopyMemory(&NewHashBlock->Table[0],
2138 &HashBlock->Table[0],
2139 sizeof(NewHashBlock->Table[0]) * HashBlock->HashTableSize);
2140 CmiDestroyBlock(RegistryHive,
2141 HashBlock,
2142 KeyCell->HashTableOffset);
2143 KeyCell->HashTableOffset = HTOffset;
2144 HashBlock = NewHashBlock;
2145 }
2146 }
2147
2148 Status = CmiAddKeyToHashTable(RegistryHive,
2149 HashBlock,
2150 NewKeyCell,
2151 NKBOffset);
2152 if (NT_SUCCESS(Status))
2153 {
2154 KeyCell->NumberOfSubKeys++;
2155 }
2156
2157 return(Status);
2158 }
2159
2160
2161 NTSTATUS
2162 CmiRemoveSubKey(PREGISTRY_HIVE RegistryHive,
2163 PKEY_OBJECT ParentKey,
2164 PKEY_OBJECT SubKey)
2165 {
2166 PHASH_TABLE_CELL HashBlock;
2167 PVALUE_LIST_CELL ValueList;
2168 PVALUE_CELL ValueCell;
2169 PDATA_CELL DataCell;
2170 ULONG i;
2171
2172 DPRINT("CmiRemoveSubKey() called\n");
2173
2174 /* Remove all values */
2175 if (SubKey->KeyCell->NumberOfValues != 0)
2176 {
2177 /* Get pointer to the value list cell */
2178 ValueList = CmiGetBlock(RegistryHive,
2179 SubKey->KeyCell->ValuesOffset,
2180 NULL);
2181 if (ValueList != NULL)
2182 {
2183 /* Enumerate all values */
2184 for (i = 0; i < SubKey->KeyCell->NumberOfValues; i++)
2185 {
2186 /* Get pointer to value cell */
2187 ValueCell = CmiGetBlock(RegistryHive,
2188 ValueList->Values[i],
2189 NULL);
2190 if (ValueCell != NULL)
2191 {
2192 if (ValueCell->DataSize > 4)
2193 {
2194 DataCell = CmiGetBlock(RegistryHive,
2195 ValueCell->DataOffset,
2196 NULL);
2197 if (DataCell != NULL)
2198 {
2199 /* Destroy data cell */
2200 CmiDestroyBlock(RegistryHive,
2201 DataCell,
2202 ValueCell->DataOffset);
2203 }
2204 }
2205
2206 /* Destroy value cell */
2207 CmiDestroyBlock(RegistryHive,
2208 ValueCell,
2209 ValueList->Values[i]);
2210 }
2211 }
2212 }
2213
2214 /* Destroy value list cell */
2215 CmiDestroyBlock(RegistryHive,
2216 ValueList,
2217 SubKey->KeyCell->ValuesOffset);
2218
2219 SubKey->KeyCell->NumberOfValues = 0;
2220 SubKey->KeyCell->ValuesOffset = -1;
2221 }
2222
2223 /* Remove the key from the parent key's hash block */
2224 if (ParentKey->KeyCell->HashTableOffset != (BLOCK_OFFSET) -1)
2225 {
2226 DPRINT("ParentKey HashTableOffset %lx\n", ParentKey->KeyCell->HashTableOffset)
2227 HashBlock = CmiGetBlock(RegistryHive,
2228 ParentKey->KeyCell->HashTableOffset,
2229 NULL);
2230 DPRINT("ParentKey HashBlock %p\n", HashBlock)
2231 if (HashBlock != NULL)
2232 {
2233 CmiRemoveKeyFromHashTable(RegistryHive,
2234 HashBlock,
2235 SubKey->BlockOffset);
2236 CmiMarkBlockDirty(RegistryHive,
2237 ParentKey->KeyCell->HashTableOffset);
2238 }
2239 }
2240
2241 /* Remove the key's hash block */
2242 if (SubKey->KeyCell->HashTableOffset != (BLOCK_OFFSET) -1)
2243 {
2244 DPRINT("SubKey HashTableOffset %lx\n", SubKey->KeyCell->HashTableOffset)
2245 HashBlock = CmiGetBlock(RegistryHive,
2246 SubKey->KeyCell->HashTableOffset,
2247 NULL);
2248 DPRINT("SubKey HashBlock %p\n", HashBlock)
2249 if (HashBlock != NULL)
2250 {
2251 CmiDestroyBlock(RegistryHive,
2252 HashBlock,
2253 SubKey->KeyCell->HashTableOffset);
2254 SubKey->KeyCell->HashTableOffset = -1;
2255 }
2256 }
2257
2258 /* Decrement the number of the parent key's sub keys */
2259 if (ParentKey != NULL)
2260 {
2261 DPRINT("ParentKey %p\n", ParentKey)
2262 ParentKey->KeyCell->NumberOfSubKeys--;
2263
2264 /* Remove the parent key's hash table */
2265 if (ParentKey->KeyCell->NumberOfSubKeys == 0)
2266 {
2267 DPRINT("ParentKey HashTableOffset %lx\n", ParentKey->KeyCell->HashTableOffset)
2268 HashBlock = CmiGetBlock(RegistryHive,
2269 ParentKey->KeyCell->HashTableOffset,
2270 NULL);
2271 DPRINT("ParentKey HashBlock %p\n", HashBlock)
2272 if (HashBlock != NULL)
2273 {
2274 CmiDestroyBlock(RegistryHive,
2275 HashBlock,
2276 ParentKey->KeyCell->HashTableOffset);
2277 ParentKey->KeyCell->HashTableOffset = -1;
2278 }
2279 }
2280
2281 NtQuerySystemTime((PTIME)&ParentKey->KeyCell->LastWriteTime);
2282 CmiMarkBlockDirty(RegistryHive,
2283 ParentKey->BlockOffset);
2284 }
2285
2286 /* Destroy key cell */
2287 CmiDestroyBlock(RegistryHive,
2288 SubKey->KeyCell,
2289 SubKey->BlockOffset);
2290 SubKey->BlockOffset = -1;
2291 SubKey->KeyCell = NULL;
2292
2293 return(STATUS_SUCCESS);
2294 }
2295
2296
2297 NTSTATUS
2298 CmiScanKeyForValue(IN PREGISTRY_HIVE RegistryHive,
2299 IN PKEY_CELL KeyCell,
2300 IN PUNICODE_STRING ValueName,
2301 OUT PVALUE_CELL *ValueCell,
2302 OUT BLOCK_OFFSET *VBOffset)
2303 {
2304 PVALUE_LIST_CELL ValueListCell;
2305 PVALUE_CELL CurValueCell;
2306 ULONG i;
2307
2308 *ValueCell = NULL;
2309
2310 ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
2311 if (ValueListCell == NULL)
2312 {
2313 DPRINT("ValueListCell is NULL\n");
2314 return STATUS_SUCCESS;
2315 }
2316
2317 VERIFY_VALUE_LIST_CELL(ValueListCell);
2318
2319 for (i = 0; i < KeyCell->NumberOfValues; i++)
2320 {
2321 CurValueCell = CmiGetBlock(RegistryHive,
2322 ValueListCell->Values[i],
2323 NULL);
2324
2325 if ((CurValueCell != NULL) &&
2326 CmiComparePackedNames(ValueName,
2327 CurValueCell->Name,
2328 CurValueCell->NameSize,
2329 CurValueCell->Flags & REG_VALUE_NAME_PACKED))
2330 {
2331 *ValueCell = CurValueCell;
2332 if (VBOffset)
2333 *VBOffset = ValueListCell->Values[i];
2334 //DPRINT("Found value %s\n", ValueName);
2335 break;
2336 }
2337 }
2338
2339 return STATUS_SUCCESS;
2340 }
2341
2342
2343 NTSTATUS
2344 CmiGetValueFromKeyByIndex(IN PREGISTRY_HIVE RegistryHive,
2345 IN PKEY_CELL KeyCell,
2346 IN ULONG Index,
2347 OUT PVALUE_CELL *ValueCell)
2348 {
2349 PVALUE_LIST_CELL ValueListCell;
2350 PVALUE_CELL CurValueCell;
2351
2352 *ValueCell = NULL;
2353
2354 ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
2355 if (ValueListCell == NULL)
2356 {
2357 return STATUS_NO_MORE_ENTRIES;
2358 }
2359
2360 VERIFY_VALUE_LIST_CELL(ValueListCell);
2361
2362 if (Index >= KeyCell->NumberOfValues)
2363 {
2364 return STATUS_NO_MORE_ENTRIES;
2365 }
2366
2367 CurValueCell = CmiGetBlock(RegistryHive,
2368 ValueListCell->Values[Index],
2369 NULL);
2370 if (CurValueCell != NULL)
2371 {
2372 *ValueCell = CurValueCell;
2373 }
2374
2375 return STATUS_SUCCESS;
2376 }
2377
2378
2379 NTSTATUS
2380 CmiAddValueToKey(IN PREGISTRY_HIVE RegistryHive,
2381 IN PKEY_CELL KeyCell,
2382 IN PUNICODE_STRING ValueName,
2383 OUT PVALUE_CELL *pValueCell,
2384 OUT BLOCK_OFFSET *pVBOffset)
2385 {
2386 PVALUE_LIST_CELL NewValueListCell;
2387 PVALUE_LIST_CELL ValueListCell;
2388 PVALUE_CELL NewValueCell;
2389 BLOCK_OFFSET VLBOffset;
2390 BLOCK_OFFSET VBOffset;
2391 NTSTATUS Status;
2392
2393 *pVBOffset = VBOffset;
2394
2395 Status = CmiAllocateValueCell(RegistryHive,
2396 &NewValueCell,
2397 &VBOffset,
2398 ValueName);
2399 if (!NT_SUCCESS(Status))
2400 {
2401 return Status;
2402 }
2403
2404 ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
2405
2406 if (ValueListCell == NULL)
2407 {
2408 Status = CmiAllocateBlock(RegistryHive,
2409 (PVOID) &ValueListCell,
2410 sizeof(BLOCK_OFFSET) * 3,
2411 &VLBOffset);
2412
2413 if (!NT_SUCCESS(Status))
2414 {
2415 CmiDestroyValueCell(RegistryHive, NewValueCell, VBOffset);
2416 return Status;
2417 }
2418 KeyCell->ValuesOffset = VLBOffset;
2419 }
2420 else if (KeyCell->NumberOfValues >=
2421 (((ULONG)ABS_VALUE(ValueListCell->CellSize) - 4) / sizeof(BLOCK_OFFSET)))
2422 {
2423 Status = CmiAllocateBlock(RegistryHive,
2424 (PVOID) &NewValueListCell,
2425 (KeyCell->NumberOfValues + REG_VALUE_LIST_CELL_MULTIPLE) *
2426 sizeof(BLOCK_OFFSET),
2427 &VLBOffset);
2428 if (!NT_SUCCESS(Status))
2429 {
2430 CmiDestroyValueCell(RegistryHive, NewValueCell, VBOffset);
2431 return Status;
2432 }
2433
2434 RtlCopyMemory(&NewValueListCell->Values[0],
2435 &ValueListCell->Values[0],
2436 sizeof(BLOCK_OFFSET) * KeyCell->NumberOfValues);
2437 CmiDestroyBlock(RegistryHive, ValueListCell, KeyCell->ValuesOffset);
2438 KeyCell->ValuesOffset = VLBOffset;
2439 ValueListCell = NewValueListCell;
2440 }
2441
2442 DPRINT("KeyCell->NumberOfValues %lu, ValueListCell->CellSize %lu (%lu %lx)\n",
2443 KeyCell->NumberOfValues,
2444 (ULONG)ABS_VALUE(ValueListCell->CellSize),
2445 ((ULONG)ABS_VALUE(ValueListCell->CellSize) - 4) / sizeof(BLOCK_OFFSET),
2446 ((ULONG)ABS_VALUE(ValueListCell->CellSize) - 4) / sizeof(BLOCK_OFFSET));
2447
2448 ValueListCell->Values[KeyCell->NumberOfValues] = VBOffset;
2449 KeyCell->NumberOfValues++;
2450
2451 *pValueCell = NewValueCell;
2452
2453 return STATUS_SUCCESS;
2454 }
2455
2456
2457 NTSTATUS
2458 CmiDeleteValueFromKey(IN PREGISTRY_HIVE RegistryHive,
2459 IN PKEY_CELL KeyCell,
2460 IN BLOCK_OFFSET KeyCellOffset,
2461 IN PUNICODE_STRING ValueName)
2462 {
2463 PVALUE_LIST_CELL ValueListCell;
2464 PVALUE_CELL CurValueCell;
2465 ULONG i;
2466
2467 ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
2468
2469 if (ValueListCell == NULL)
2470 {
2471 return STATUS_SUCCESS;
2472 }
2473
2474 VERIFY_VALUE_LIST_CELL(ValueListCell);
2475
2476 for (i = 0; i < KeyCell->NumberOfValues; i++)
2477 {
2478 CurValueCell = CmiGetBlock(RegistryHive, ValueListCell->Values[i], NULL);
2479
2480 if ((CurValueCell != NULL) &&
2481 CmiComparePackedNames(ValueName,
2482 CurValueCell->Name,
2483 CurValueCell->NameSize,
2484 CurValueCell->Flags & REG_VALUE_NAME_PACKED))
2485 {
2486 CmiDestroyValueCell(RegistryHive, CurValueCell, ValueListCell->Values[i]);
2487
2488 if ((KeyCell->NumberOfValues - 1) < i)
2489 {
2490 RtlCopyMemory(&ValueListCell->Values[i],
2491 &ValueListCell->Values[i + 1],
2492 sizeof(BLOCK_OFFSET) * (KeyCell->NumberOfValues - 1 - i));
2493 }
2494 else
2495 {
2496 RtlZeroMemory(&ValueListCell->Values[i], sizeof(BLOCK_OFFSET));
2497 }
2498
2499 KeyCell->NumberOfValues -= 1;
2500 break;
2501 }
2502 }
2503
2504 if (KeyCell->NumberOfValues == 0)
2505 {
2506 CmiDestroyBlock(RegistryHive,
2507 ValueListCell,
2508 KeyCell->ValuesOffset);
2509 }
2510 else
2511 {
2512 CmiMarkBlockDirty(RegistryHive,
2513 KeyCell->ValuesOffset);
2514 }
2515
2516 CmiMarkBlockDirty(RegistryHive,
2517 KeyCellOffset);
2518
2519 return STATUS_SUCCESS;
2520 }
2521
2522
2523 NTSTATUS
2524 CmiAllocateHashTableBlock(IN PREGISTRY_HIVE RegistryHive,
2525 OUT PHASH_TABLE_CELL *HashBlock,
2526 OUT BLOCK_OFFSET *HBOffset,
2527 IN ULONG HashTableSize)
2528 {
2529 PHASH_TABLE_CELL NewHashBlock;
2530 ULONG NewHashSize;
2531 NTSTATUS Status;
2532
2533 Status = STATUS_SUCCESS;
2534 *HashBlock = NULL;
2535 NewHashSize = sizeof(HASH_TABLE_CELL) +
2536 (HashTableSize - 1) * sizeof(HASH_RECORD);
2537 Status = CmiAllocateBlock(RegistryHive,
2538 (PVOID*) &NewHashBlock,
2539 NewHashSize,
2540 HBOffset);
2541
2542 if ((NewHashBlock == NULL) || (!NT_SUCCESS(Status)))
2543 {
2544 Status = STATUS_INSUFFICIENT_RESOURCES;
2545 }
2546 else
2547 {
2548 NewHashBlock->Id = REG_HASH_TABLE_BLOCK_ID;
2549 NewHashBlock->HashTableSize = HashTableSize;
2550 *HashBlock = NewHashBlock;
2551 }
2552
2553 return Status;
2554 }
2555
2556
2557 PKEY_CELL
2558 CmiGetKeyFromHashByIndex(PREGISTRY_HIVE RegistryHive,
2559 PHASH_TABLE_CELL HashBlock,
2560 ULONG Index)
2561 {
2562 BLOCK_OFFSET KeyOffset;
2563 PKEY_CELL KeyCell;
2564
2565 if (HashBlock == NULL)
2566 return NULL;
2567
2568 if (IsPointerHive(RegistryHive))
2569 {
2570 KeyCell = (PKEY_CELL) HashBlock->Table[Index].KeyOffset;
2571 }
2572 else
2573 {
2574 KeyOffset = HashBlock->Table[Index].KeyOffset;
2575 KeyCell = CmiGetBlock(RegistryHive, KeyOffset, NULL);
2576 }
2577
2578 return KeyCell;
2579 }
2580
2581
2582 NTSTATUS
2583 CmiAddKeyToHashTable(PREGISTRY_HIVE RegistryHive,
2584 PHASH_TABLE_CELL HashBlock,
2585 PKEY_CELL NewKeyCell,
2586 BLOCK_OFFSET NKBOffset)
2587 {
2588 ULONG i;
2589
2590 for (i = 0; i < HashBlock->HashTableSize; i++)
2591 {
2592 if (HashBlock->Table[i].KeyOffset == 0)
2593 {
2594 HashBlock->Table[i].KeyOffset = NKBOffset;
2595 RtlCopyMemory(&HashBlock->Table[i].HashValue, NewKeyCell->Name, 4);
2596 return STATUS_SUCCESS;
2597 }
2598 }
2599
2600 return STATUS_UNSUCCESSFUL;
2601 }
2602
2603
2604 NTSTATUS
2605 CmiRemoveKeyFromHashTable(PREGISTRY_HIVE RegistryHive,
2606 PHASH_TABLE_CELL HashBlock,
2607 BLOCK_OFFSET NKBOffset)
2608 {
2609 ULONG i;
2610
2611 for (i = 0; i < HashBlock->HashTableSize; i++)
2612 {
2613 if (HashBlock->Table[i].KeyOffset == NKBOffset)
2614 {
2615 HashBlock->Table[i].KeyOffset = 0;
2616 RtlZeroMemory(&HashBlock->Table[i].HashValue, 4);
2617 return STATUS_SUCCESS;
2618 }
2619 }
2620
2621 return STATUS_UNSUCCESSFUL;
2622 }
2623
2624
2625 NTSTATUS
2626 CmiAllocateValueCell(PREGISTRY_HIVE RegistryHive,
2627 PVALUE_CELL *ValueCell,
2628 BLOCK_OFFSET *VBOffset,
2629 IN PUNICODE_STRING ValueName)
2630 {
2631 PVALUE_CELL NewValueCell;
2632 NTSTATUS Status;
2633 BOOLEAN Packable;
2634 ULONG NameSize;
2635 ULONG i;
2636
2637 Status = STATUS_SUCCESS;
2638
2639 NameSize = CmiGetPackedNameLength(ValueName,
2640 &Packable);
2641
2642 DPRINT("ValueName->Length %lu NameSize %lu\n", ValueName->Length, NameSize);
2643
2644 Status = CmiAllocateBlock(RegistryHive,
2645 (PVOID*) &NewValueCell,
2646 sizeof(VALUE_CELL) + NameSize,
2647 VBOffset);
2648 if ((NewValueCell == NULL) || (!NT_SUCCESS(Status)))
2649 {
2650 Status = STATUS_INSUFFICIENT_RESOURCES;
2651 }
2652 else
2653 {
2654 NewValueCell->Id = REG_VALUE_CELL_ID;
2655 NewValueCell->NameSize = NameSize;
2656 if (Packable)
2657 {
2658 /* Pack the value name */
2659 for (i = 0; i < NameSize; i++)
2660 NewValueCell->Name[i] = (CHAR)ValueName->Buffer[i];
2661 NewValueCell->Flags |= REG_VALUE_NAME_PACKED;
2662 }
2663 else
2664 {
2665 /* Copy the value name */
2666 RtlCopyMemory(NewValueCell->Name,
2667 ValueName->Buffer,
2668 NameSize);
2669 NewValueCell->Flags = 0;
2670 }
2671 NewValueCell->DataType = 0;
2672 NewValueCell->DataSize = 0;
2673 NewValueCell->DataOffset = 0xffffffff;
2674 *ValueCell = NewValueCell;
2675 }
2676
2677 return Status;
2678 }
2679
2680
2681 NTSTATUS
2682 CmiDestroyValueCell(PREGISTRY_HIVE RegistryHive,
2683 PVALUE_CELL ValueCell,
2684 BLOCK_OFFSET VBOffset)
2685 {
2686 NTSTATUS Status;
2687 PVOID pBlock;
2688 PHBIN pBin;
2689
2690 DPRINT("CmiDestroyValueCell(Cell %p Offset %lx)\n", ValueCell, VBOffset);
2691
2692 VERIFY_VALUE_CELL(ValueCell);
2693
2694 /* Destroy the data cell */
2695 if (ValueCell->DataSize > 4)
2696 {
2697 pBlock = CmiGetBlock(RegistryHive, ValueCell->DataOffset, &pBin);
2698 Status = CmiDestroyBlock(RegistryHive, pBlock, ValueCell->DataOffset);
2699 if (!NT_SUCCESS(Status))
2700 {
2701 return Status;
2702 }
2703
2704 /* Update time of heap */
2705 if (!IsVolatileHive(RegistryHive))
2706 NtQuerySystemTime((PTIME) &pBin->DateModified);
2707 }
2708
2709 /* Destroy the value cell */
2710 Status = CmiDestroyBlock(RegistryHive, ValueCell, VBOffset);
2711
2712 /* Update time of heap */
2713 if (!IsVolatileHive(RegistryHive) && CmiGetBlock(RegistryHive, VBOffset, &pBin))
2714 {
2715 NtQuerySystemTime((PTIME) &pBin->DateModified);
2716 }
2717
2718 return Status;
2719 }
2720
2721
2722 NTSTATUS
2723 CmiAddBin(PREGISTRY_HIVE RegistryHive,
2724 PVOID *NewBlock,
2725 BLOCK_OFFSET *NewBlockOffset)
2726 {
2727 PCELL_HEADER tmpBlock;
2728 PHBIN * tmpBlockList;
2729 PHBIN tmpBin;
2730
2731 tmpBin = ExAllocatePool(PagedPool, REG_BLOCK_SIZE);
2732 if (tmpBin == NULL)
2733 {
2734 return STATUS_INSUFFICIENT_RESOURCES;
2735 }
2736
2737 tmpBin->BlockId = REG_BIN_ID;
2738 tmpBin->BlockOffset = RegistryHive->FileSize - REG_BLOCK_SIZE;
2739 RegistryHive->FileSize += REG_BLOCK_SIZE;
2740 tmpBin->BlockSize = REG_BLOCK_SIZE;
2741 tmpBin->Unused1 = 0;
2742 ZwQuerySystemTime((PTIME) &tmpBin->DateModified);
2743 tmpBin->Unused2 = 0;
2744
2745 /* Increase size of list of blocks */
2746 tmpBlockList = ExAllocatePool(NonPagedPool,
2747 sizeof(PHBIN *) * (RegistryHive->BlockListSize + 1));
2748 if (tmpBlockList == NULL)
2749 {
2750 ExFreePool(tmpBin);
2751 return STATUS_INSUFFICIENT_RESOURCES;
2752 }
2753
2754 if (RegistryHive->BlockListSize > 0)
2755 {
2756 memcpy(tmpBlockList,
2757 RegistryHive->BlockList,
2758 sizeof(PHBIN *)*(RegistryHive->BlockListSize));
2759 ExFreePool(RegistryHive->BlockList);
2760 }
2761
2762 RegistryHive->BlockList = tmpBlockList;
2763 RegistryHive->BlockList[RegistryHive->BlockListSize++] = tmpBin;
2764
2765 /* Initialize a free block in this heap : */
2766 tmpBlock = (PCELL_HEADER)((ULONG_PTR) tmpBin + REG_HBIN_DATA_OFFSET);
2767 tmpBlock->CellSize = (REG_BLOCK_SIZE - REG_HBIN_DATA_OFFSET);
2768
2769 /* Grow bitmap if necessary */
2770 if (IsVolatileHive(RegistryHive) &&
2771 (RegistryHive->BlockListSize % (sizeof(ULONG) * 8) == 0))
2772 {
2773 PULONG BitmapBuffer;
2774 ULONG BitmapSize;
2775
2776 DPRINT1("Grow hive bitmap\n");
2777
2778 /* Calculate bitmap size in bytes (always a multiple of 32 bits) */
2779 BitmapSize = ROUND_UP(RegistryHive->BlockListSize, sizeof(ULONG) * 8) / 8;
2780 DPRINT1("RegistryHive->BlockListSize: %lu\n", RegistryHive->BlockListSize);
2781 DPRINT1("BitmapSize: %lu Bytes %lu Bits\n", BitmapSize, BitmapSize * 8);
2782 BitmapBuffer = (PULONG)ExAllocatePool(PagedPool,
2783 BitmapSize);
2784 RtlZeroMemory(BitmapBuffer, BitmapSize);
2785 RtlCopyMemory(BitmapBuffer,
2786 RegistryHive->DirtyBitMap.Buffer,
2787 RegistryHive->DirtyBitMap.SizeOfBitMap);
2788 ExFreePool(RegistryHive->BitmapBuffer);
2789 RegistryHive->BitmapBuffer = BitmapBuffer;
2790 RtlInitializeBitMap(&RegistryHive->DirtyBitMap,
2791 RegistryHive->BitmapBuffer,
2792 BitmapSize * 8);
2793 }
2794
2795 *NewBlock = (PVOID) tmpBlock;
2796
2797 if (NewBlockOffset)
2798 *NewBlockOffset = tmpBin->BlockOffset + REG_HBIN_DATA_OFFSET;
2799
2800 /* Mark new bin dirty */
2801 CmiMarkBinDirty(RegistryHive,
2802 tmpBin->BlockOffset);
2803
2804 return STATUS_SUCCESS;
2805 }
2806
2807
2808 NTSTATUS
2809 CmiAllocateBlock(PREGISTRY_HIVE RegistryHive,
2810 PVOID *Block,
2811 LONG BlockSize,
2812 BLOCK_OFFSET * pBlockOffset)
2813 {
2814 PCELL_HEADER NewBlock;
2815 NTSTATUS Status;
2816 PHBIN pBin;
2817
2818 Status = STATUS_SUCCESS;
2819
2820 /* Round to 16 bytes multiple */
2821 BlockSize = (BlockSize + sizeof(DWORD) + 15) & 0xfffffff0;
2822
2823 /* Handle volatile hives first */
2824 if (IsPointerHive(RegistryHive))
2825 {
2826 NewBlock = ExAllocatePool(NonPagedPool, BlockSize);
2827
2828 if (NewBlock == NULL)
2829 {
2830 Status = STATUS_INSUFFICIENT_RESOURCES;
2831 }
2832 else
2833 {
2834 RtlZeroMemory(NewBlock, BlockSize);
2835 NewBlock->CellSize = BlockSize;
2836 *Block = NewBlock;
2837 if (pBlockOffset)
2838 *pBlockOffset = (BLOCK_OFFSET) NewBlock;
2839 }
2840 }
2841 else
2842 {
2843 ULONG i;
2844
2845 /* first search in free blocks */
2846 NewBlock = NULL;
2847 for (i = 0; i < RegistryHive->FreeListSize; i++)
2848 {
2849 if (RegistryHive->FreeList[i]->CellSize >= BlockSize)
2850 {
2851 PVOID Temp;
2852
2853 NewBlock = RegistryHive->FreeList[i];
2854 if (pBlockOffset)
2855 *pBlockOffset = RegistryHive->FreeListOffset[i];
2856
2857 /* Update time of heap */
2858 Temp = CmiGetBlock(RegistryHive, RegistryHive->FreeListOffset[i], &pBin);
2859
2860 if (Temp)
2861 {
2862 NtQuerySystemTime((PTIME) &pBin->DateModified);
2863 CmiMarkBlockDirty(RegistryHive, RegistryHive->FreeListOffset[i]);
2864 }
2865
2866 if ((i + 1) < RegistryHive->FreeListSize)
2867 {
2868 RtlMoveMemory(&RegistryHive->FreeList[i],
2869 &RegistryHive->FreeList[i + 1],
2870 sizeof(RegistryHive->FreeList[0])
2871 * (RegistryHive->FreeListSize - i - 1));
2872 RtlMoveMemory(&RegistryHive->FreeListOffset[i],
2873 &RegistryHive->FreeListOffset[i + 1],
2874 sizeof(RegistryHive->FreeListOffset[0])
2875 * (RegistryHive->FreeListSize - i - 1));
2876 }
2877 RegistryHive->FreeListSize--;
2878 break;
2879 }
2880 }
2881
2882 /* Need to extend hive file : */
2883 if (NewBlock == NULL)
2884 {
2885 /* Add a new block */
2886 Status = CmiAddBin(RegistryHive, (PVOID *) &NewBlock , pBlockOffset);
2887 }
2888
2889 if (NT_SUCCESS(Status))
2890 {
2891 *Block = NewBlock;
2892
2893 /* Split the block in two parts */
2894 if (NewBlock->CellSize > BlockSize)
2895 {
2896 NewBlock = (PCELL_HEADER) ((ULONG_PTR) NewBlock+BlockSize);
2897 NewBlock->CellSize = ((PCELL_HEADER) (*Block))->CellSize - BlockSize;
2898 CmiAddFree(RegistryHive,
2899 NewBlock,
2900 *pBlockOffset + BlockSize,
2901 TRUE);
2902 CmiMarkBlockDirty(RegistryHive,
2903 *pBlockOffset + BlockSize);
2904 }
2905 else if (NewBlock->CellSize < BlockSize)
2906 {
2907 return(STATUS_UNSUCCESSFUL);
2908 }
2909
2910 RtlZeroMemory(*Block, BlockSize);
2911 ((PCELL_HEADER) (*Block))->CellSize = -BlockSize;
2912 }
2913 }
2914
2915 return(Status);
2916 }
2917
2918
2919 NTSTATUS
2920 CmiDestroyBlock(PREGISTRY_HIVE RegistryHive,
2921 PVOID Block,
2922 BLOCK_OFFSET Offset)
2923 {
2924 NTSTATUS Status;
2925 PHBIN pBin;
2926
2927 Status = STATUS_SUCCESS;
2928
2929 if (IsPointerHive(RegistryHive))
2930 {
2931 ExFreePool(Block);
2932 }
2933 else
2934 {
2935 PCELL_HEADER pFree = Block;
2936
2937 if (pFree->CellSize < 0)
2938 pFree->CellSize = -pFree->CellSize;
2939
2940 /* Clear block (except the block size) */
2941 RtlZeroMemory(((PVOID)pFree) + sizeof(ULONG),
2942 pFree->CellSize - sizeof(ULONG));
2943
2944 /* Add block to the list of free blocks */
2945 CmiAddFree(RegistryHive, Block, Offset, TRUE);
2946
2947 /* Update time of heap */
2948 if (!IsVolatileHive(RegistryHive) && CmiGetBlock(RegistryHive, Offset,&pBin))
2949 NtQuerySystemTime((PTIME) &pBin->DateModified);
2950
2951 CmiMarkBlockDirty(RegistryHive, Offset);
2952 }
2953
2954 return Status;
2955 }
2956
2957
2958 static BOOLEAN
2959 CmiMergeFree(PREGISTRY_HIVE RegistryHive,
2960 PCELL_HEADER FreeBlock,
2961 BLOCK_OFFSET FreeOffset)
2962 {
2963 BLOCK_OFFSET BlockOffset;
2964 BLOCK_OFFSET BinOffset;
2965 ULONG BlockSize;
2966 ULONG BinSize;
2967 PHBIN Bin;
2968 ULONG i;
2969
2970 DPRINT("CmiMergeFree(Block %lx Offset %lx Size %lx) called\n",
2971 FreeBlock, FreeOffset, FreeBlock->CellSize);
2972
2973 CmiGetBlock(RegistryHive,
2974 FreeOffset,
2975 &Bin);
2976 DPRINT("Bin %p\n", Bin);
2977 if (Bin == NULL)
2978 return(FALSE);
2979
2980 BinOffset = Bin->BlockOffset;
2981 BinSize = Bin->BlockSize;
2982 DPRINT("Bin %p Offset %lx Size %lx\n", Bin, BinOffset, BinSize);
2983
2984 for (i = 0; i < RegistryHive->FreeListSize; i++)
2985 {
2986 BlockOffset = RegistryHive->FreeListOffset[i];
2987 BlockSize = RegistryHive->FreeList[i]->CellSize;
2988 if (BlockOffset > BinOffset &&
2989 BlockOffset < BinOffset + BinSize)
2990 {
2991 DPRINT("Free block: Offset %lx Size %lx\n",
2992 BlockOffset, BlockSize);
2993
2994 if ((i < (RegistryHive->FreeListSize - 1)) &&
2995 (BlockOffset + BlockSize == FreeOffset) &&
2996 (FreeOffset + FreeBlock->CellSize == RegistryHive->FreeListOffset[i + 1]))
2997 {
2998 DPRINT("Merge current block with previous and next block\n");
2999
3000 RegistryHive->FreeList[i]->CellSize +=
3001 (FreeBlock->CellSize + RegistryHive->FreeList[i + 1]->CellSize);
3002
3003 FreeBlock->CellSize = 0;
3004 RegistryHive->FreeList[i + 1]->CellSize = 0;
3005
3006
3007 if ((i + 2) < RegistryHive->FreeListSize)
3008 {
3009 RtlMoveMemory(&RegistryHive->FreeListOffset[i + 1],
3010 &RegistryHive->FreeListOffset[i + 2],
3011 sizeof(RegistryHive->FreeListOffset[0])
3012 * (RegistryHive->FreeListSize - i - 2));
3013 }
3014 RegistryHive->FreeListSize--;
3015
3016 CmiMarkBlockDirty(RegistryHive, BlockOffset);
3017
3018 return(TRUE);
3019 }
3020 else if (BlockOffset + BlockSize == FreeOffset)
3021 {
3022 DPRINT("Merge current block with previous block\n");
3023
3024 RegistryHive->FreeList[i]->CellSize += FreeBlock->CellSize;
3025 FreeBlock->CellSize = 0;
3026
3027 CmiMarkBlockDirty(RegistryHive, BlockOffset);
3028
3029 return(TRUE);
3030 }
3031 else if (FreeOffset + FreeBlock->CellSize == BlockOffset)
3032 {
3033 DPRINT("Merge current block with next block\n");
3034
3035 FreeBlock->CellSize += RegistryHive->FreeList[i]->CellSize;
3036 RegistryHive->FreeList[i]->CellSize = 0;
3037 RegistryHive->FreeList[i] = FreeBlock;
3038 RegistryHive->FreeListOffset[i] = FreeOffset;
3039
3040 CmiMarkBlockDirty(RegistryHive, FreeOffset);
3041
3042 return(TRUE);
3043 }
3044 }
3045 }
3046
3047 return(FALSE);
3048 }
3049
3050
3051 NTSTATUS
3052 CmiAddFree(PREGISTRY_HIVE RegistryHive,
3053 PCELL_HEADER FreeBlock,
3054 BLOCK_OFFSET FreeOffset,
3055 BOOLEAN MergeFreeBlocks)
3056 {
3057 PCELL_HEADER *tmpList;
3058 BLOCK_OFFSET *tmpListOffset;
3059 LONG minInd;
3060 LONG maxInd;
3061 LONG medInd;
3062
3063 assert(RegistryHive);
3064 assert(FreeBlock);
3065
3066 DPRINT("FreeBlock %.08lx FreeOffset %.08lx\n",
3067 FreeBlock, FreeOffset);
3068
3069 /* Merge free blocks */
3070 if (MergeFreeBlocks == TRUE)
3071 {
3072 if (CmiMergeFree(RegistryHive, FreeBlock, FreeOffset))
3073 return(STATUS_SUCCESS);
3074 }
3075
3076 if ((RegistryHive->FreeListSize + 1) > RegistryHive->FreeListMax)
3077 {
3078 tmpList = ExAllocatePool(PagedPool,
3079 sizeof(PCELL_HEADER) * (RegistryHive->FreeListMax + 32));
3080 if (tmpList == NULL)
3081 return STATUS_INSUFFICIENT_RESOURCES;
3082
3083 tmpListOffset = ExAllocatePool(PagedPool,
3084 sizeof(BLOCK_OFFSET) * (RegistryHive->FreeListMax + 32));
3085
3086 if (tmpListOffset == NULL)
3087 {
3088 ExFreePool(tmpList);
3089 return STATUS_INSUFFICIENT_RESOURCES;
3090 }
3091
3092 if (RegistryHive->FreeListMax)
3093 {
3094 RtlMoveMemory(tmpList,
3095 RegistryHive->FreeList,
3096 sizeof(PCELL_HEADER) * (RegistryHive->FreeListMax));
3097 RtlMoveMemory(tmpListOffset,
3098 RegistryHive->FreeListOffset,
3099 sizeof(BLOCK_OFFSET) * (RegistryHive->FreeListMax));
3100 ExFreePool(RegistryHive->FreeList);
3101 ExFreePool(RegistryHive->FreeListOffset);
3102 }
3103 RegistryHive->FreeList = tmpList;
3104 RegistryHive->FreeListOffset = tmpListOffset;
3105 RegistryHive->FreeListMax += 32;
3106 }
3107
3108 /* Add new offset to free list, maintaining list in ascending order */
3109 if ((RegistryHive->FreeListSize == 0)
3110 || (RegistryHive->FreeListOffset[RegistryHive->FreeListSize-1] < FreeOffset))
3111 {
3112 /* Add to end of list */
3113 RegistryHive->FreeList[RegistryHive->FreeListSize] = FreeBlock;
3114 RegistryHive->FreeListOffset[RegistryHive->FreeListSize++] = FreeOffset;
3115 }
3116 else if (RegistryHive->FreeListOffset[0] > FreeOffset)
3117 {
3118 /* Add to begin of list */
3119 RtlMoveMemory(&RegistryHive->FreeList[1],
3120 &RegistryHive->FreeList[0],
3121 sizeof(RegistryHive->FreeList[0]) * RegistryHive->FreeListSize);
3122 RtlMoveMemory(&RegistryHive->FreeListOffset[1],
3123 &RegistryHive->FreeListOffset[0],
3124 sizeof(RegistryHive->FreeListOffset[0]) * RegistryHive->FreeListSize);
3125 RegistryHive->FreeList[0] = FreeBlock;
3126 RegistryHive->FreeListOffset[0] = FreeOffset;
3127 RegistryHive->FreeListSize++;
3128 }
3129 else
3130 {
3131 /* Search where to insert */
3132 minInd = 0;
3133 maxInd = RegistryHive->FreeListSize - 1;
3134 while ((maxInd - minInd) > 1)
3135 {
3136 medInd = (minInd + maxInd) / 2;
3137 if (RegistryHive->FreeListOffset[medInd] > FreeOffset)
3138 maxInd = medInd;
3139 else
3140 minInd = medInd;
3141 }
3142
3143 /* Insert before maxInd */
3144 RtlMoveMemory(&RegistryHive->FreeList[maxInd+1],
3145 &RegistryHive->FreeList[maxInd],
3146 sizeof(RegistryHive->FreeList[0]) * (RegistryHive->FreeListSize - minInd));
3147 RtlMoveMemory(&RegistryHive->FreeListOffset[maxInd + 1],
3148 &RegistryHive->FreeListOffset[maxInd],
3149 sizeof(RegistryHive->FreeListOffset[0]) * (RegistryHive->FreeListSize-minInd));
3150 RegistryHive->FreeList[maxInd] = FreeBlock;
3151 RegistryHive->FreeListOffset[maxInd] = FreeOffset;
3152 RegistryHive->FreeListSize++;
3153 }
3154
3155 return STATUS_SUCCESS;
3156 }
3157
3158
3159 PVOID
3160 CmiGetBlock(PREGISTRY_HIVE RegistryHive,
3161 BLOCK_OFFSET BlockOffset,
3162 PHBIN * ppBin)
3163 {
3164 if (ppBin)
3165 *ppBin = NULL;
3166
3167 if ((BlockOffset == 0) || (BlockOffset == (ULONG_PTR) -1))
3168 return NULL;
3169
3170 if (IsPointerHive(RegistryHive))
3171 {
3172 return (PVOID) BlockOffset;
3173 }
3174 else
3175 {
3176 PHBIN pBin;
3177
3178 pBin = RegistryHive->BlockList[BlockOffset / 4096];
3179 if (ppBin)
3180 *ppBin = pBin;
3181 return((PVOID)((ULONG_PTR)pBin + (BlockOffset - pBin->BlockOffset)));
3182 }
3183 }
3184
3185
3186 VOID
3187 CmiMarkBlockDirty(PREGISTRY_HIVE RegistryHive,
3188 BLOCK_OFFSET BlockOffset)
3189 {
3190 PDATA_CELL Cell;
3191 LONG CellSize;
3192 ULONG BlockNumber;
3193 ULONG BlockCount;
3194
3195 if (IsVolatileHive(RegistryHive))
3196 return;
3197
3198 DPRINT("CmiMarkBlockDirty(Offset 0x%lx)\n", (ULONG)BlockOffset);
3199
3200 BlockNumber = (ULONG)BlockOffset / 4096;
3201
3202 Cell = CmiGetBlock(RegistryHive,
3203 BlockOffset,
3204 NULL);
3205
3206 CellSize = Cell->CellSize;
3207 if (CellSize < 0)
3208 CellSize = -CellSize;
3209
3210 BlockCount = (ROUND_UP(BlockOffset + CellSize, 4096) - ROUND_DOWN(BlockOffset, 4096)) / 4096;
3211
3212 DPRINT(" BlockNumber %lu Size %lu (%s) BlockCount %lu\n",
3213 BlockNumber,
3214 CellSize,
3215 (Cell->CellSize < 0) ? "used" : "free",
3216 BlockCount);
3217
3218 RegistryHive->HiveDirty = TRUE;
3219 RtlSetBits(&RegistryHive->DirtyBitMap,
3220 BlockNumber,
3221 BlockCount);
3222 }
3223
3224
3225 VOID
3226 CmiMarkBinDirty(PREGISTRY_HIVE RegistryHive,
3227 BLOCK_OFFSET BinOffset)
3228 {
3229 ULONG BlockNumber;
3230 ULONG BlockCount;
3231 PHBIN Bin;
3232
3233 if (IsVolatileHive(RegistryHive))
3234 return;
3235
3236 DPRINT("CmiMarkBinDirty(Offset 0x%lx)\n", (ULONG)BinOffset);
3237
3238 BlockNumber = (ULONG)BinOffset / 4096;
3239
3240 Bin = RegistryHive->BlockList[BlockNumber];
3241
3242 BlockCount = Bin->BlockSize / 4096;
3243
3244 DPRINT(" BlockNumber %lu Size %lu BlockCount %lu\n",
3245 BlockNumber,
3246 Bin->BlockSize,
3247 BlockCount);
3248
3249 RegistryHive->HiveDirty = TRUE;
3250 RtlSetBits(&RegistryHive->DirtyBitMap,
3251 BlockNumber,
3252 BlockCount);
3253 }
3254
3255
3256 ULONG
3257 CmiGetPackedNameLength(IN PUNICODE_STRING Name,
3258 OUT PBOOLEAN Packable)
3259 {
3260 ULONG i;
3261
3262 if (Packable != NULL)
3263 *Packable = TRUE;
3264
3265 for (i = 0; i < Name->Length; i++)
3266 {
3267 if (Name->Buffer[i] > 0xFF)
3268 {
3269 if (Packable != NULL)
3270 *Packable = FALSE;
3271 return(Name->Length);
3272 }
3273 }
3274
3275 return(Name->Length / sizeof(WCHAR));
3276 }
3277
3278
3279 BOOLEAN
3280 CmiComparePackedNames(IN PUNICODE_STRING Name,
3281 IN PCHAR NameBuffer,
3282 IN USHORT NameBufferSize,
3283 IN BOOLEAN NamePacked)
3284 {
3285 PWCHAR UNameBuffer;
3286 ULONG i;
3287
3288 if (NamePacked == TRUE)
3289 {
3290 if (Name->Length != NameBufferSize * sizeof(WCHAR))
3291 return(FALSE);
3292
3293 for (i = 0; i < Name->Length / sizeof(WCHAR); i++)
3294 {
3295 if (RtlUpcaseUnicodeChar(Name->Buffer[i]) != RtlUpcaseUnicodeChar((WCHAR)NameBuffer[i]))
3296 return(FALSE);
3297 }
3298 }
3299 else
3300 {
3301 if (Name->Length != NameBufferSize)
3302 return(FALSE);
3303
3304 UNameBuffer = (PWCHAR)NameBuffer;
3305
3306 for (i = 0; i < Name->Length / sizeof(WCHAR); i++)
3307 {
3308 if (RtlUpcaseUnicodeChar(Name->Buffer[i]) != RtlUpcaseUnicodeChar(UNameBuffer[i]))
3309 return(FALSE);
3310 }
3311 }
3312
3313 return(TRUE);
3314 }
3315
3316
3317 VOID
3318 CmiCopyPackedName(PWCHAR NameBuffer,
3319 PCHAR PackedNameBuffer,
3320 ULONG PackedNameSize)
3321 {
3322 ULONG i;
3323
3324 for (i = 0; i < PackedNameSize; i++)
3325 NameBuffer[i] = (WCHAR)PackedNameBuffer[i];
3326 }
3327
3328 /* EOF */