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