Use a 5-step transaction to update hive and log file.
[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 /* Read update counter */
615 RegistryHive->UpdateCounter = RegistryHive->HiveHeader->UpdateCounter1;
616
617 Status = NtQueryInformationFile(FileHandle,
618 &IoSB,
619 &fsi,
620 sizeof(fsi),
621 FileStandardInformation);
622 assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
623 if (!NT_SUCCESS(Status))
624 {
625 NtClose(FileHandle);
626 RtlFreeUnicodeString(&RegistryHive->HiveFileName);
627 RtlFreeUnicodeString(&RegistryHive->LogFileName);
628 DPRINT("CmiInitNonVolatileRegistryHive() - Failed 5.\n");
629 return Status;
630 }
631
632 RegistryHive->FileSize = fsi.EndOfFile.u.LowPart;
633 #ifdef WIN32_REGDBG
634 // assert(RegistryHive->FileSize);
635 if (RegistryHive->FileSize == 0)
636 {
637 NtClose(FileHandle);
638 RtlFreeUnicodeString(&RegistryHive->HiveFileName);
639 RtlFreeUnicodeString(&RegistryHive->LogFileName);
640 DPRINT("CmiInitPermanentRegistryHive() - Failed, zero length hive file.\n");
641 return STATUS_INSUFFICIENT_RESOURCES;
642 }
643 #endif
644 RegistryHive->BlockListSize = (RegistryHive->FileSize / 4096) - 1;
645
646 DPRINT("Space needed for block list describing hive: 0x%x\n",
647 sizeof(PHBIN *) * RegistryHive->BlockListSize);
648
649 RegistryHive->BlockList = ExAllocatePool(NonPagedPool,
650 sizeof(PHBIN *) * RegistryHive->BlockListSize);
651
652 if (RegistryHive->BlockList == NULL)
653 {
654 ExFreePool(RegistryHive->BlockList);
655 NtClose(FileHandle);
656 RtlFreeUnicodeString(&RegistryHive->HiveFileName);
657 RtlFreeUnicodeString(&RegistryHive->LogFileName);
658 DPRINT("CmiInitNonVolatileRegistryHive() - Failed 6.\n");
659 return STATUS_INSUFFICIENT_RESOURCES;
660 }
661
662 RegistryHive->BlockList[0] = ExAllocatePool(PagedPool,
663 RegistryHive->FileSize - 4096);
664 #ifdef WIN32_REGDBG
665 RtlZeroMemory(RegistryHive->BlockList[0], RegistryHive->FileSize - 4096);
666 #endif
667
668 if (RegistryHive->BlockList[0] == NULL)
669 {
670 ExFreePool(RegistryHive->BlockList);
671 NtClose(FileHandle);
672 RtlFreeUnicodeString(&RegistryHive->HiveFileName);
673 RtlFreeUnicodeString(&RegistryHive->LogFileName);
674 DPRINT("CmiInitNonVolatileRegistryHive() - Failed 8.\n");
675 return STATUS_INSUFFICIENT_RESOURCES;
676 }
677
678 FileOffset.u.HighPart = 0;
679 FileOffset.u.LowPart = 4096;
680
681 DPRINT(" Attempting to NtReadFile(%d) for %d bytes into %p\n",
682 FileHandle, RegistryHive->FileSize - 4096, (PVOID)RegistryHive->BlockList[0]);
683 Status = NtReadFile(FileHandle,
684 0,
685 0,
686 0,
687 &IoSB,
688 (PVOID) RegistryHive->BlockList[0],
689 RegistryHive->FileSize - 4096,
690 &FileOffset,
691 0);
692
693 assertmsg(NT_SUCCESS(Status), ("Status: 0x%X\n", Status));
694
695 NtClose(FileHandle);
696
697 RegistryHive->FreeListSize = 0;
698 RegistryHive->FreeListMax = 0;
699 RegistryHive->FreeList = NULL;
700
701 BlockOffset = 0;
702 for (i = 0; i < RegistryHive->BlockListSize; i++)
703 {
704 RegistryHive->BlockList[i] = (PHBIN) (((ULONG_PTR) RegistryHive->BlockList[0]) + BlockOffset);
705 tmpBin = (PHBIN) (((ULONG_PTR) RegistryHive->BlockList[i]));
706 if (tmpBin->BlockId != REG_BIN_ID)
707 {
708 DPRINT("Bad BlockId %x, offset %x\n", tmpBin->BlockId, BlockOffset);
709 //KeBugCheck(0);
710 return STATUS_INSUFFICIENT_RESOURCES;
711 }
712
713 assertmsg((tmpBin->BlockSize % 4096) == 0, ("BlockSize (0x%.08x) must be multiplum of 4K\n", tmpBin->BlockSize));
714
715 if (tmpBin->BlockSize > 4096)
716 {
717 for (j = 1; j < tmpBin->BlockSize / 4096; j++)
718 {
719 RegistryHive->BlockList[i + j] = RegistryHive->BlockList[i];
720 }
721 i = i + j - 1;
722 }
723
724 /* Search free blocks and add to list */
725 FreeOffset = REG_HBIN_DATA_OFFSET;
726 while (FreeOffset < tmpBin->BlockSize)
727 {
728 FreeBlock = (PCELL_HEADER) ((ULONG_PTR) RegistryHive->BlockList[i] + FreeOffset);
729 if (FreeBlock->CellSize > 0)
730 {
731 Status = CmiAddFree(RegistryHive,
732 FreeBlock,
733 RegistryHive->BlockList[i]->BlockOffset + FreeOffset,
734 FALSE);
735
736 if (!NT_SUCCESS(Status))
737 {
738 /* FIXME: */
739 assert(FALSE);
740 }
741
742 FreeOffset += FreeBlock->CellSize;
743 }
744 else
745 {
746 FreeOffset -= FreeBlock->CellSize;
747 }
748 }
749 BlockOffset += tmpBin->BlockSize;
750 }
751
752 /*
753 * Create block bitmap and clear all bits
754 */
755 /* Calculate bitmap size in bytes (always a multiple of 32 bits) */
756 BitmapSize = ROUND_UP(RegistryHive->BlockListSize, sizeof(ULONG) * 8) / 8;
757 DPRINT("RegistryHive->BlockListSize: %lu\n", RegistryHive->BlockListSize);
758 DPRINT("BitmapSize: %lu Bytes %lu Bits\n", BitmapSize, BitmapSize * 8);
759
760 /* Allocate bitmap */
761 BitmapBuffer = (PULONG)ExAllocatePool(PagedPool,
762 BitmapSize);
763 RtlInitializeBitMap(&RegistryHive->DirtyBitMap,
764 BitmapBuffer,
765 BitmapSize * 8);
766
767 /* Initialize bitmap */
768 RtlClearAllBits(&RegistryHive->DirtyBitMap);
769 RegistryHive->HiveDirty = FALSE;
770
771 DPRINT("CmiInitNonVolatileRegistryHive(%p, %S, %d) - Finished.\n",
772 RegistryHive, Filename, CreateNew);
773
774 return(STATUS_SUCCESS);
775 }
776
777
778 static NTSTATUS
779 CmiInitVolatileRegistryHive(PREGISTRY_HIVE RegistryHive)
780 {
781 PKEY_CELL RootKeyCell;
782
783 RegistryHive->Flags |= HIVE_VOLATILE;
784
785 CmiCreateDefaultHiveHeader(RegistryHive->HiveHeader);
786
787 RootKeyCell = (PKEY_CELL) ExAllocatePool(NonPagedPool, sizeof(KEY_CELL));
788
789 if (RootKeyCell == NULL)
790 return STATUS_INSUFFICIENT_RESOURCES;
791
792 CmiCreateDefaultRootKeyCell(RootKeyCell);
793
794 RegistryHive->HiveHeader->RootKeyCell = (BLOCK_OFFSET) RootKeyCell;
795
796 return STATUS_SUCCESS;
797 }
798
799
800 NTSTATUS
801 CmiCreateRegistryHive(PWSTR Filename,
802 PREGISTRY_HIVE *RegistryHive,
803 BOOLEAN CreateNew)
804 {
805 PREGISTRY_HIVE Hive;
806 NTSTATUS Status;
807
808 DPRINT("CmiCreateRegistryHive(Filename %S)\n", Filename);
809
810 *RegistryHive = NULL;
811
812 Hive = ExAllocatePool(NonPagedPool, sizeof(REGISTRY_HIVE));
813 if (Hive == NULL)
814 return(STATUS_INSUFFICIENT_RESOURCES);
815
816 DPRINT("Hive %x\n", Hive);
817
818 RtlZeroMemory(Hive, sizeof(REGISTRY_HIVE));
819
820 Hive->HiveHeader = (PHIVE_HEADER)
821 ExAllocatePool(NonPagedPool, sizeof(HIVE_HEADER));
822
823 if (Hive->HiveHeader == NULL)
824 {
825 ExFreePool(Hive);
826 return(STATUS_INSUFFICIENT_RESOURCES);
827 }
828
829 if (Filename != NULL)
830 {
831 Status = CmiInitNonVolatileRegistryHive(Hive, Filename, CreateNew);
832 }
833 else
834 {
835 Status = CmiInitVolatileRegistryHive(Hive);
836 }
837
838 if (!NT_SUCCESS(Status))
839 {
840 ExFreePool(Hive->HiveHeader);
841 ExFreePool(Hive);
842 return(Status);
843 }
844
845 ExInitializeResourceLite(&Hive->HiveResource);
846
847 /* Acquire hive list lock exclusively */
848 ExAcquireResourceExclusiveLite(&CmiHiveListLock, TRUE);
849
850 /* Add the new hive to the hive list */
851 InsertHeadList(&CmiHiveListHead, &Hive->HiveList);
852
853 /* Release hive list lock */
854 ExReleaseResourceLite(&CmiHiveListLock);
855
856 VERIFY_REGISTRY_HIVE(Hive);
857
858 *RegistryHive = Hive;
859
860 DPRINT("CmiCreateRegistryHive(Filename %S) - Finished.\n", Filename);
861
862 return(STATUS_SUCCESS);
863 }
864
865
866 NTSTATUS
867 CmiRemoveRegistryHive(PREGISTRY_HIVE RegistryHive)
868 {
869 /* Acquire hive list lock exclusively */
870 ExAcquireResourceExclusiveLite(&CmiHiveListLock, TRUE);
871
872 /* Remove hive from hive list */
873 RemoveEntryList(&RegistryHive->HiveList);
874
875 /* Release hive list lock */
876 ExReleaseResourceLite(&CmiHiveListLock);
877
878
879 /* FIXME: Remove attached keys and values */
880
881
882 /* Release hive header */
883 ExFreePool(RegistryHive->HiveHeader);
884
885 /* Release hive */
886 ExFreePool(RegistryHive);
887
888 return(STATUS_SUCCESS);
889 }
890
891
892 static ULONG
893 CmiCalcChecksum(PULONG Buffer)
894 {
895 ULONG Sum = 0;
896 ULONG i;
897
898 for (i = 0; i < 127; i++)
899 Sum += Buffer[i];
900
901 return(Sum);
902 }
903
904
905 static NTSTATUS
906 CmiStartLogUpdate(PREGISTRY_HIVE RegistryHive)
907 {
908 FILE_END_OF_FILE_INFORMATION EndOfFileInfo;
909 FILE_ALLOCATION_INFORMATION FileAllocationInfo;
910 OBJECT_ATTRIBUTES ObjectAttributes;
911 IO_STATUS_BLOCK IoStatusBlock;
912 HANDLE FileHandle;
913 LARGE_INTEGER FileOffset;
914 ULONG BufferSize;
915 ULONG BitmapSize;
916 PUCHAR Buffer;
917 PUCHAR Ptr;
918 ULONG BlockIndex;
919 ULONG BlockOffset;
920 PVOID BlockPtr;
921 NTSTATUS Status;
922
923 DPRINT1("CmiStartLogUpdate() called\n");
924
925 BitmapSize = ROUND_UP(RegistryHive->BlockListSize, sizeof(ULONG) * 8) / 8;
926 BufferSize = sizeof(HIVE_HEADER) +
927 sizeof(ULONG) +
928 BitmapSize;
929 BufferSize = ROUND_UP(BufferSize, 4096);
930
931 DPRINT1("Bitmap size %lu buffer size: %lu\n", BitmapSize, BufferSize);
932
933 Buffer = (PUCHAR)ExAllocatePool(NonPagedPool, BufferSize);
934 if (Buffer == NULL)
935 {
936 DPRINT1("ExAllocatePool() failed\n");
937 return(STATUS_INSUFFICIENT_RESOURCES);
938 }
939
940 /* Open log file for writing */
941 InitializeObjectAttributes(&ObjectAttributes,
942 &RegistryHive->LogFileName,
943 0,
944 NULL,
945 NULL);
946
947 Status = NtCreateFile(&FileHandle,
948 FILE_ALL_ACCESS,
949 &ObjectAttributes,
950 &IoStatusBlock,
951 NULL,
952 FILE_ATTRIBUTE_NORMAL,
953 0,
954 FILE_SUPERSEDE,
955 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
956 NULL,
957 0);
958 if (!NT_SUCCESS(Status))
959 {
960 DPRINT1("NtCreateFile() failed (Status %lx)\n", Status);
961 ExFreePool(Buffer);
962 return(Status);
963 }
964
965 /* Update firt update counter and checksum */
966 RegistryHive->HiveHeader->UpdateCounter1 = RegistryHive->UpdateCounter + 1;
967 RegistryHive->HiveHeader->Checksum = CmiCalcChecksum((PULONG)RegistryHive->HiveHeader);
968
969 /* Copy hive header */
970 RtlCopyMemory(Buffer,
971 RegistryHive->HiveHeader,
972 sizeof(HIVE_HEADER));
973 Ptr = Buffer + sizeof(HIVE_HEADER);
974
975 RtlCopyMemory(Ptr,
976 "DIRT",
977 4);
978 Ptr += 4;
979 RtlCopyMemory(Ptr,
980 RegistryHive->DirtyBitMap.Buffer,
981 BitmapSize);
982
983 /* Write hive block and block bitmap */
984 FileOffset.QuadPart = 0ULL;
985 Status = NtWriteFile(FileHandle,
986 NULL,
987 NULL,
988 NULL,
989 &IoStatusBlock,
990 Buffer,
991 BufferSize,
992 &FileOffset,
993 NULL);
994 if (!NT_SUCCESS(Status))
995 {
996 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
997 NtClose(FileHandle);
998 ExFreePool(Buffer);
999 return(Status);
1000 }
1001
1002 ExFreePool(Buffer);
1003
1004
1005 /* Write dirty blocks */
1006 FileOffset.QuadPart = (ULONGLONG)BufferSize;
1007 BlockIndex = 0;
1008 while (TRUE)
1009 {
1010 BlockIndex = RtlFindSetBits(&RegistryHive->DirtyBitMap,
1011 1,
1012 BlockIndex);
1013 if (BlockIndex == (ULONG)-1)
1014 {
1015 DPRINT("No more set bits\n");
1016 Status = STATUS_SUCCESS;
1017 break;
1018 }
1019
1020 DPRINT1("Block %lu is dirty\n", BlockIndex);
1021
1022 BlockOffset = RegistryHive->BlockList[BlockIndex]->BlockOffset;
1023 DPRINT1("Block offset %lx\n", BlockOffset);
1024
1025 BlockPtr = RegistryHive->BlockList[BlockIndex] + ((BlockIndex * 4096) - BlockOffset);
1026 DPRINT1("BlockPtr %p\n", BlockPtr);
1027
1028 DPRINT1("File offset %I64x\n", FileOffset.QuadPart);
1029
1030 /* Write hive block */
1031 Status = NtWriteFile(FileHandle,
1032 NULL,
1033 NULL,
1034 NULL,
1035 &IoStatusBlock,
1036 BlockPtr,
1037 REG_BLOCK_SIZE,
1038 &FileOffset,
1039 NULL);
1040 if (!NT_SUCCESS(Status))
1041 {
1042 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
1043 NtClose(FileHandle);
1044 return(Status);
1045 }
1046
1047 BlockIndex++;
1048 FileOffset.QuadPart += 4096ULL;
1049 }
1050
1051 /* Truncate log file */
1052 EndOfFileInfo.EndOfFile.QuadPart = FileOffset.QuadPart;
1053 Status = NtSetInformationFile(FileHandle,
1054 &IoStatusBlock,
1055 &EndOfFileInfo,
1056 sizeof(FILE_END_OF_FILE_INFORMATION),
1057 FileEndOfFileInformation);
1058 if (!NT_SUCCESS(Status))
1059 {
1060 DPRINT1("NtSetInformationFile() failed (Status %lx)\n", Status);
1061 NtClose(FileHandle);
1062 return(Status);
1063 }
1064
1065 FileAllocationInfo.AllocationSize.QuadPart = FileOffset.QuadPart;
1066 Status = NtSetInformationFile(FileHandle,
1067 &IoStatusBlock,
1068 &FileAllocationInfo,
1069 sizeof(FILE_ALLOCATION_INFORMATION),
1070 FileAllocationInformation);
1071 if (!NT_SUCCESS(Status))
1072 {
1073 DPRINT1("NtSetInformationFile() failed (Status %lx)\n", Status);
1074 NtClose(FileHandle);
1075 return(Status);
1076 }
1077
1078 /* Flush the log file */
1079 Status = NtFlushBuffersFile(FileHandle,
1080 &IoStatusBlock);
1081 if (!NT_SUCCESS(Status))
1082 {
1083 DPRINT1("NtFlushBuffersFile() failed (Status %lx)\n", Status);
1084 }
1085
1086 NtClose(FileHandle);
1087
1088 return(Status);
1089 }
1090
1091
1092 static NTSTATUS
1093 CmiFinishLogUpdate(PREGISTRY_HIVE RegistryHive)
1094 {
1095 OBJECT_ATTRIBUTES ObjectAttributes;
1096 IO_STATUS_BLOCK IoStatusBlock;
1097 HANDLE FileHandle;
1098 LARGE_INTEGER FileOffset;
1099 ULONG BufferSize;
1100 ULONG BitmapSize;
1101 PUCHAR Buffer;
1102 PUCHAR Ptr;
1103 NTSTATUS Status;
1104
1105 DPRINT1("CmiFinishLogUpdate() called\n");
1106
1107 BitmapSize = ROUND_UP(RegistryHive->BlockListSize, sizeof(ULONG) * 8) / 8;
1108 BufferSize = sizeof(HIVE_HEADER) +
1109 sizeof(ULONG) +
1110 BitmapSize;
1111 BufferSize = ROUND_UP(BufferSize, 4096);
1112
1113 DPRINT1("Bitmap size %lu buffer size: %lu\n", BitmapSize, BufferSize);
1114
1115 Buffer = (PUCHAR)ExAllocatePool(NonPagedPool, BufferSize);
1116 if (Buffer == NULL)
1117 {
1118 DPRINT1("ExAllocatePool() failed\n");
1119 return(STATUS_INSUFFICIENT_RESOURCES);
1120 }
1121
1122 /* Open log file for writing */
1123 InitializeObjectAttributes(&ObjectAttributes,
1124 &RegistryHive->LogFileName,
1125 0,
1126 NULL,
1127 NULL);
1128
1129 Status = NtCreateFile(&FileHandle,
1130 FILE_ALL_ACCESS,
1131 &ObjectAttributes,
1132 &IoStatusBlock,
1133 NULL,
1134 FILE_ATTRIBUTE_NORMAL,
1135 0,
1136 FILE_OPEN,
1137 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1138 NULL,
1139 0);
1140 if (!NT_SUCCESS(Status))
1141 {
1142 DPRINT1("NtCreateFile() failed (Status %lx)\n", Status);
1143 ExFreePool(Buffer);
1144 return(Status);
1145 }
1146
1147 /* Update first and second update counter and checksum */
1148 RegistryHive->HiveHeader->UpdateCounter1 = RegistryHive->UpdateCounter + 1;
1149 RegistryHive->HiveHeader->UpdateCounter2 = RegistryHive->UpdateCounter + 1;
1150 RegistryHive->HiveHeader->Checksum = CmiCalcChecksum((PULONG)RegistryHive->HiveHeader);
1151
1152 /* Copy hive header */
1153 RtlCopyMemory(Buffer,
1154 RegistryHive->HiveHeader,
1155 sizeof(HIVE_HEADER));
1156 Ptr = Buffer + sizeof(HIVE_HEADER);
1157
1158 /* Write empty block bitmap */
1159 RtlCopyMemory(Ptr,
1160 "DIRT",
1161 4);
1162 Ptr += 4;
1163 // RtlCopyMemory(Ptr,
1164 // RegistryHive->DirtyBitMap.Buffer,
1165 // BitmapSize);
1166 RtlZeroMemory(Ptr,
1167 BitmapSize);
1168
1169 /* Write hive block and block bitmap */
1170 FileOffset.QuadPart = 0ULL;
1171 Status = NtWriteFile(FileHandle,
1172 NULL,
1173 NULL,
1174 NULL,
1175 &IoStatusBlock,
1176 Buffer,
1177 BufferSize,
1178 &FileOffset,
1179 NULL);
1180 if (!NT_SUCCESS(Status))
1181 {
1182 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
1183 NtClose(FileHandle);
1184 ExFreePool(Buffer);
1185 return(Status);
1186 }
1187
1188 ExFreePool(Buffer);
1189
1190 /* Flush the log file */
1191 Status = NtFlushBuffersFile(FileHandle,
1192 &IoStatusBlock);
1193 if (!NT_SUCCESS(Status))
1194 {
1195 DPRINT1("NtFlushBuffersFile() failed (Status %lx)\n", Status);
1196 }
1197
1198 NtClose(FileHandle);
1199
1200 return(Status);
1201 }
1202
1203
1204 static NTSTATUS
1205 CmiCleanupLogUpdate(PREGISTRY_HIVE RegistryHive)
1206 {
1207 FILE_END_OF_FILE_INFORMATION EndOfFileInfo;
1208 FILE_ALLOCATION_INFORMATION FileAllocationInfo;
1209 OBJECT_ATTRIBUTES ObjectAttributes;
1210 IO_STATUS_BLOCK IoStatusBlock;
1211 HANDLE FileHandle;
1212 ULONG BufferSize;
1213 ULONG BitmapSize;
1214 NTSTATUS Status;
1215
1216 DPRINT1("CmiFinishLogUpdate() called\n");
1217
1218 BitmapSize = ROUND_UP(RegistryHive->BlockListSize, sizeof(ULONG) * 8) / 8;
1219 BufferSize = sizeof(HIVE_HEADER) +
1220 sizeof(ULONG) +
1221 BitmapSize;
1222 BufferSize = ROUND_UP(BufferSize, 4096);
1223
1224 DPRINT1("Bitmap size %lu buffer size: %lu\n", BitmapSize, BufferSize);
1225
1226 /* Open log file for writing */
1227 InitializeObjectAttributes(&ObjectAttributes,
1228 &RegistryHive->LogFileName,
1229 0,
1230 NULL,
1231 NULL);
1232
1233 Status = NtCreateFile(&FileHandle,
1234 FILE_ALL_ACCESS,
1235 &ObjectAttributes,
1236 &IoStatusBlock,
1237 NULL,
1238 FILE_ATTRIBUTE_NORMAL,
1239 0,
1240 FILE_OPEN,
1241 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1242 NULL,
1243 0);
1244 if (!NT_SUCCESS(Status))
1245 {
1246 DPRINT1("NtCreateFile() failed (Status %lx)\n", Status);
1247 return(Status);
1248 }
1249
1250 /* Truncate log file */
1251 EndOfFileInfo.EndOfFile.QuadPart = (ULONGLONG)BufferSize;
1252 Status = NtSetInformationFile(FileHandle,
1253 &IoStatusBlock,
1254 &EndOfFileInfo,
1255 sizeof(FILE_END_OF_FILE_INFORMATION),
1256 FileEndOfFileInformation);
1257 if (!NT_SUCCESS(Status))
1258 {
1259 DPRINT1("NtSetInformationFile() failed (Status %lx)\n", Status);
1260 NtClose(FileHandle);
1261 return(Status);
1262 }
1263
1264 FileAllocationInfo.AllocationSize.QuadPart = (ULONGLONG)BufferSize;
1265 Status = NtSetInformationFile(FileHandle,
1266 &IoStatusBlock,
1267 &FileAllocationInfo,
1268 sizeof(FILE_ALLOCATION_INFORMATION),
1269 FileAllocationInformation);
1270 if (!NT_SUCCESS(Status))
1271 {
1272 DPRINT1("NtSetInformationFile() failed (Status %lx)\n", Status);
1273 NtClose(FileHandle);
1274 return(Status);
1275 }
1276
1277 /* Flush the log file */
1278 Status = NtFlushBuffersFile(FileHandle,
1279 &IoStatusBlock);
1280 if (!NT_SUCCESS(Status))
1281 {
1282 DPRINT1("NtFlushBuffersFile() failed (Status %lx)\n", Status);
1283 }
1284
1285 NtClose(FileHandle);
1286
1287 return(Status);
1288 }
1289
1290
1291 static NTSTATUS
1292 CmiStartHiveUpdate(PREGISTRY_HIVE RegistryHive)
1293 {
1294 OBJECT_ATTRIBUTES ObjectAttributes;
1295 IO_STATUS_BLOCK IoStatusBlock;
1296 HANDLE FileHandle;
1297 LARGE_INTEGER FileOffset;
1298 ULONG BlockIndex;
1299 ULONG BlockOffset;
1300 PVOID BlockPtr;
1301 NTSTATUS Status;
1302
1303 DPRINT("CmiStartHiveUpdate() called\n");
1304
1305 /* Open hive for writing */
1306 InitializeObjectAttributes(&ObjectAttributes,
1307 &RegistryHive->HiveFileName,
1308 0,
1309 NULL,
1310 NULL);
1311
1312 Status = NtCreateFile(&FileHandle,
1313 FILE_ALL_ACCESS,
1314 &ObjectAttributes,
1315 &IoStatusBlock,
1316 NULL,
1317 FILE_ATTRIBUTE_NORMAL,
1318 0,
1319 FILE_OPEN,
1320 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1321 NULL,
1322 0);
1323 if (!NT_SUCCESS(Status))
1324 {
1325 DPRINT1("NtCreateFile() failed (Status %lx)\n", Status);
1326 return(Status);
1327 }
1328
1329 /* Update firt update counter and checksum */
1330 RegistryHive->HiveHeader->UpdateCounter1 = RegistryHive->UpdateCounter + 1;
1331 RegistryHive->HiveHeader->Checksum = CmiCalcChecksum((PULONG)RegistryHive->HiveHeader);
1332
1333 /* Write hive block */
1334 FileOffset.QuadPart = 0ULL;
1335 Status = NtWriteFile(FileHandle,
1336 NULL,
1337 NULL,
1338 NULL,
1339 &IoStatusBlock,
1340 RegistryHive->HiveHeader,
1341 sizeof(HIVE_HEADER),
1342 &FileOffset,
1343 NULL);
1344 if (!NT_SUCCESS(Status))
1345 {
1346 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
1347 NtClose(FileHandle);
1348 return(Status);
1349 }
1350
1351 BlockIndex = 0;
1352 while (TRUE)
1353 {
1354 BlockIndex = RtlFindSetBits(&RegistryHive->DirtyBitMap,
1355 1,
1356 BlockIndex);
1357 if (BlockIndex == (ULONG)-1)
1358 {
1359 DPRINT("No more set bits\n");
1360 Status = STATUS_SUCCESS;
1361 break;
1362 }
1363
1364 DPRINT1("Block %lu is dirty\n", BlockIndex);
1365
1366 BlockOffset = RegistryHive->BlockList[BlockIndex]->BlockOffset;
1367 DPRINT1("Block offset %lx\n", BlockOffset);
1368
1369 BlockPtr = RegistryHive->BlockList[BlockIndex] + ((BlockIndex * 4096) - BlockOffset);
1370 DPRINT1("BlockPtr %p\n", BlockPtr);
1371
1372 FileOffset.QuadPart = (ULONGLONG)(BlockIndex + 1) * 4096ULL;
1373 DPRINT1("File offset %I64x\n", FileOffset.QuadPart);
1374
1375
1376 /* Write hive block */
1377 Status = NtWriteFile(FileHandle,
1378 NULL,
1379 NULL,
1380 NULL,
1381 &IoStatusBlock,
1382 BlockPtr,
1383 REG_BLOCK_SIZE,
1384 &FileOffset,
1385 NULL);
1386 if (!NT_SUCCESS(Status))
1387 {
1388 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
1389 NtClose(FileHandle);
1390 return(Status);
1391 }
1392
1393 BlockIndex++;
1394 }
1395
1396 Status = NtFlushBuffersFile(FileHandle,
1397 &IoStatusBlock);
1398 if (!NT_SUCCESS(Status))
1399 {
1400 DPRINT1("NtFlushBuffersFile() failed (Status %lx)\n", Status);
1401 }
1402
1403 NtClose(FileHandle);
1404
1405 return(Status);
1406 }
1407
1408
1409 static NTSTATUS
1410 CmiFinishHiveUpdate(PREGISTRY_HIVE RegistryHive)
1411 {
1412 OBJECT_ATTRIBUTES ObjectAttributes;
1413 IO_STATUS_BLOCK IoStatusBlock;
1414 LARGE_INTEGER FileOffset;
1415 HANDLE FileHandle;
1416 NTSTATUS Status;
1417
1418 DPRINT1("CmiFinishHiveUpdate() called\n");
1419
1420 InitializeObjectAttributes(&ObjectAttributes,
1421 &RegistryHive->HiveFileName,
1422 0,
1423 NULL,
1424 NULL);
1425
1426 Status = NtCreateFile(&FileHandle,
1427 FILE_ALL_ACCESS,
1428 &ObjectAttributes,
1429 &IoStatusBlock,
1430 NULL,
1431 FILE_ATTRIBUTE_NORMAL,
1432 0,
1433 FILE_OPEN,
1434 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1435 NULL,
1436 0);
1437 if (!NT_SUCCESS(Status))
1438 {
1439 DPRINT1("NtCreateFile() failed (Status %lx)\n", Status);
1440 return(Status);
1441 }
1442
1443 /* Update second update counter and checksum */
1444 RegistryHive->HiveHeader->UpdateCounter1 = RegistryHive->UpdateCounter + 1;
1445 RegistryHive->HiveHeader->UpdateCounter2 = RegistryHive->UpdateCounter + 1;
1446 RegistryHive->HiveHeader->Checksum = CmiCalcChecksum((PULONG)RegistryHive->HiveHeader);
1447
1448 /* Write hive block */
1449 FileOffset.QuadPart = 0ULL;
1450 Status = NtWriteFile(FileHandle,
1451 NULL,
1452 NULL,
1453 NULL,
1454 &IoStatusBlock,
1455 RegistryHive->HiveHeader,
1456 sizeof(HIVE_HEADER),
1457 &FileOffset,
1458 NULL);
1459 if (!NT_SUCCESS(Status))
1460 {
1461 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
1462 NtClose(FileHandle);
1463 return(Status);
1464 }
1465
1466 Status = NtFlushBuffersFile(FileHandle,
1467 &IoStatusBlock);
1468 if (!NT_SUCCESS(Status))
1469 {
1470 DPRINT1("NtFlushBuffersFile() failed (Status %lx)\n", Status);
1471 }
1472
1473 NtClose(FileHandle);
1474
1475 return(Status);
1476 }
1477
1478
1479 NTSTATUS
1480 CmiFlushRegistryHive(PREGISTRY_HIVE RegistryHive)
1481 {
1482 NTSTATUS Status;
1483
1484 DPRINT("CmiFlushRegistryHive() called\n");
1485
1486 if (RegistryHive->HiveDirty == FALSE)
1487 {
1488 return(STATUS_SUCCESS);
1489 }
1490
1491 DPRINT1("Hive '%wZ' is dirty\n",
1492 &RegistryHive->HiveFileName);
1493
1494 DPRINT1("Log file: '%wZ'\n",
1495 &RegistryHive->LogFileName);
1496
1497
1498 /* Update hive header modification time */
1499 NtQuerySystemTime((PTIME)&RegistryHive->HiveHeader->DateModified);
1500
1501 /* Start log update */
1502 Status = CmiStartLogUpdate(RegistryHive);
1503 if (!NT_SUCCESS(Status))
1504 {
1505 DPRINT1("CmiStartLogUpdate() failed (Status %lx)\n", Status);
1506 return(Status);
1507 }
1508
1509 /* Finish log update */
1510 Status = CmiFinishLogUpdate(RegistryHive);
1511 if (!NT_SUCCESS(Status))
1512 {
1513 DPRINT1("CmiFinishLogUpdate() failed (Status %lx)\n", Status);
1514 return(Status);
1515 }
1516
1517 /* Start hive update */
1518 Status = CmiStartHiveUpdate(RegistryHive);
1519 if (!NT_SUCCESS(Status))
1520 {
1521 DPRINT1("CmiStartHiveUpdate() failed (Status %lx)\n", Status);
1522 return(Status);
1523 }
1524
1525 /* Finish the hive update */
1526 Status = CmiFinishHiveUpdate(RegistryHive);
1527 if (!NT_SUCCESS(Status))
1528 {
1529 DPRINT1("CmiFinishHiveUpdate() failed (Status %lx)\n", Status);
1530 return(Status);
1531 }
1532
1533 /* Cleanup log update */
1534 Status = CmiCleanupLogUpdate(RegistryHive);
1535 if (!NT_SUCCESS(Status))
1536 {
1537 DPRINT1("CmiFinishLogUpdate() failed (Status %lx)\n", Status);
1538 return(Status);
1539 }
1540
1541 /* Increment hive update counter */
1542 RegistryHive->UpdateCounter++;
1543
1544 /* Clear dirty bitmap and dirty flag */
1545 RtlClearAllBits(&RegistryHive->DirtyBitMap);
1546 RegistryHive->HiveDirty = FALSE;
1547
1548 DPRINT1("CmiFlushRegistryHive() done\n");
1549
1550 return(STATUS_SUCCESS);
1551 }
1552
1553
1554 ULONG
1555 CmiGetMaxNameLength(PREGISTRY_HIVE RegistryHive,
1556 PKEY_CELL KeyCell)
1557 {
1558 PHASH_TABLE_CELL HashBlock;
1559 PKEY_CELL CurSubKeyCell;
1560 ULONG MaxName;
1561 ULONG i;
1562
1563 VERIFY_KEY_CELL(KeyCell);
1564
1565 MaxName = 0;
1566 HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
1567 if (HashBlock == NULL)
1568 {
1569 return 0;
1570 }
1571
1572 for (i = 0; i < HashBlock->HashTableSize; i++)
1573 {
1574 if (HashBlock->Table[i].KeyOffset != 0)
1575 {
1576 CurSubKeyCell = CmiGetBlock(RegistryHive,
1577 HashBlock->Table[i].KeyOffset,
1578 NULL);
1579 if (MaxName < CurSubKeyCell->NameSize)
1580 {
1581 MaxName = CurSubKeyCell->NameSize;
1582 }
1583 CmiReleaseBlock(RegistryHive, CurSubKeyCell);
1584 }
1585 }
1586
1587 CmiReleaseBlock(RegistryHive, HashBlock);
1588
1589 return MaxName;
1590 }
1591
1592
1593 ULONG
1594 CmiGetMaxClassLength(PREGISTRY_HIVE RegistryHive,
1595 PKEY_CELL KeyCell)
1596 {
1597 PHASH_TABLE_CELL HashBlock;
1598 PKEY_CELL CurSubKeyCell;
1599 ULONG MaxClass;
1600 ULONG i;
1601
1602 VERIFY_KEY_CELL(KeyCell);
1603
1604 MaxClass = 0;
1605 HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
1606 if (HashBlock == NULL)
1607 {
1608 return 0;
1609 }
1610
1611 for (i = 0; i < HashBlock->HashTableSize; i++)
1612 {
1613 if (HashBlock->Table[i].KeyOffset != 0)
1614 {
1615 CurSubKeyCell = CmiGetBlock(RegistryHive,
1616 HashBlock->Table[i].KeyOffset,
1617 NULL);
1618 if (MaxClass < CurSubKeyCell->ClassSize)
1619 {
1620 MaxClass = CurSubKeyCell->ClassSize;
1621 }
1622 CmiReleaseBlock(RegistryHive, CurSubKeyCell);
1623 }
1624 }
1625
1626 CmiReleaseBlock(RegistryHive, HashBlock);
1627
1628 return MaxClass;
1629 }
1630
1631
1632 ULONG
1633 CmiGetMaxValueNameLength(PREGISTRY_HIVE RegistryHive,
1634 PKEY_CELL KeyCell)
1635 {
1636 PVALUE_LIST_CELL ValueListCell;
1637 PVALUE_CELL CurValueCell;
1638 ULONG MaxValueName;
1639 ULONG i;
1640
1641 VERIFY_KEY_CELL(KeyCell);
1642
1643 ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
1644 MaxValueName = 0;
1645 if (ValueListCell == NULL)
1646 {
1647 return 0;
1648 }
1649
1650 for (i = 0; i < KeyCell->NumberOfValues; i++)
1651 {
1652 CurValueCell = CmiGetBlock(RegistryHive,
1653 ValueListCell->Values[i],
1654 NULL);
1655 if (CurValueCell != NULL &&
1656 MaxValueName < CurValueCell->NameSize)
1657 {
1658 MaxValueName = CurValueCell->NameSize;
1659 }
1660 CmiReleaseBlock(RegistryHive, CurValueCell);
1661 }
1662
1663 CmiReleaseBlock(RegistryHive, ValueListCell);
1664
1665 return MaxValueName;
1666 }
1667
1668
1669 ULONG
1670 CmiGetMaxValueDataLength(PREGISTRY_HIVE RegistryHive,
1671 PKEY_CELL KeyCell)
1672 {
1673 PVALUE_LIST_CELL ValueListCell;
1674 PVALUE_CELL CurValueCell;
1675 LONG MaxValueData;
1676 ULONG i;
1677
1678 VERIFY_KEY_CELL(KeyCell);
1679
1680 ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
1681 MaxValueData = 0;
1682 if (ValueListCell == NULL)
1683 {
1684 return 0;
1685 }
1686
1687 for (i = 0; i < KeyCell->NumberOfValues; i++)
1688 {
1689 CurValueCell = CmiGetBlock(RegistryHive,
1690 ValueListCell->Values[i],NULL);
1691 if ((CurValueCell != NULL) &&
1692 (MaxValueData < (CurValueCell->DataSize & LONG_MAX)))
1693 {
1694 MaxValueData = CurValueCell->DataSize & LONG_MAX;
1695 }
1696 CmiReleaseBlock(RegistryHive, CurValueCell);
1697 }
1698
1699 CmiReleaseBlock(RegistryHive, ValueListCell);
1700
1701 return MaxValueData;
1702 }
1703
1704
1705 NTSTATUS
1706 CmiScanForSubKey(IN PREGISTRY_HIVE RegistryHive,
1707 IN PKEY_CELL KeyCell,
1708 OUT PKEY_CELL *SubKeyCell,
1709 OUT BLOCK_OFFSET *BlockOffset,
1710 IN PCHAR KeyName,
1711 IN ACCESS_MASK DesiredAccess,
1712 IN ULONG Attributes)
1713 {
1714 PHASH_TABLE_CELL HashBlock;
1715 PKEY_CELL CurSubKeyCell;
1716 WORD KeyLength;
1717 ULONG i;
1718
1719 VERIFY_KEY_CELL(KeyCell);
1720
1721 //DPRINT("Scanning for sub key %s\n", KeyName);
1722
1723 assert(RegistryHive);
1724
1725 KeyLength = strlen(KeyName);
1726
1727 HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
1728 *SubKeyCell = NULL;
1729 if (HashBlock == NULL)
1730 {
1731 return STATUS_SUCCESS;
1732 }
1733
1734 for (i = 0; (i < KeyCell->NumberOfSubKeys)
1735 && (i < HashBlock->HashTableSize); i++)
1736 {
1737 if (Attributes & OBJ_CASE_INSENSITIVE)
1738 {
1739 if ((HashBlock->Table[i].KeyOffset != 0) &&
1740 (HashBlock->Table[i].KeyOffset != (ULONG_PTR)-1) &&
1741 (_strnicmp(KeyName, (PCHAR) &HashBlock->Table[i].HashValue, 4) == 0))
1742 {
1743 CurSubKeyCell = CmiGetBlock(RegistryHive,
1744 HashBlock->Table[i].KeyOffset,
1745 NULL);
1746 if ((CurSubKeyCell->NameSize == KeyLength)
1747 && (_strnicmp(KeyName, CurSubKeyCell->Name, KeyLength) == 0))
1748 {
1749 *SubKeyCell = CurSubKeyCell;
1750 *BlockOffset = HashBlock->Table[i].KeyOffset;
1751 break;
1752 }
1753 else
1754 {
1755 CmiReleaseBlock(RegistryHive, CurSubKeyCell);
1756 }
1757 }
1758 }
1759 else
1760 {
1761 if (HashBlock->Table[i].KeyOffset != 0 &&
1762 HashBlock->Table[i].KeyOffset != (ULONG_PTR) -1 &&
1763 !strncmp(KeyName, (PCHAR) &HashBlock->Table[i].HashValue, 4))
1764 {
1765 CurSubKeyCell = CmiGetBlock(RegistryHive,
1766 HashBlock->Table[i].KeyOffset,NULL);
1767 if (CurSubKeyCell->NameSize == KeyLength
1768 && !_strnicmp(KeyName, CurSubKeyCell->Name, KeyLength))
1769 {
1770 *SubKeyCell = CurSubKeyCell;
1771 *BlockOffset = HashBlock->Table[i].KeyOffset;
1772 break;
1773 }
1774 else
1775 {
1776 CmiReleaseBlock(RegistryHive, CurSubKeyCell);
1777 }
1778 }
1779 }
1780 }
1781
1782 CmiReleaseBlock(RegistryHive, HashBlock);
1783
1784 return STATUS_SUCCESS;
1785 }
1786
1787
1788 NTSTATUS
1789 CmiAddSubKey(PREGISTRY_HIVE RegistryHive,
1790 PKEY_OBJECT Parent,
1791 PKEY_OBJECT SubKey,
1792 PWSTR NewSubKeyName,
1793 USHORT NewSubKeyNameSize,
1794 ULONG TitleIndex,
1795 PUNICODE_STRING Class,
1796 ULONG CreateOptions)
1797 {
1798 PHASH_TABLE_CELL NewHashBlock;
1799 PHASH_TABLE_CELL HashBlock;
1800 BLOCK_OFFSET NKBOffset;
1801 PKEY_CELL NewKeyCell;
1802 ULONG NewBlockSize;
1803 PKEY_CELL KeyCell;
1804 NTSTATUS Status;
1805 USHORT NameSize;
1806
1807 KeyCell = Parent->KeyCell;
1808
1809 VERIFY_KEY_CELL(KeyCell);
1810
1811 if (NewSubKeyName[0] == L'\\')
1812 {
1813 NewSubKeyName++;
1814 NameSize = NewSubKeyNameSize / 2 - 1;
1815 }
1816 else
1817 {
1818 NameSize = NewSubKeyNameSize / 2;
1819 }
1820 Status = STATUS_SUCCESS;
1821
1822 NewBlockSize = sizeof(KEY_CELL) + NameSize;
1823 Status = CmiAllocateBlock(RegistryHive,
1824 (PVOID) &NewKeyCell,
1825 NewBlockSize,
1826 &NKBOffset);
1827
1828 if (NewKeyCell == NULL)
1829 {
1830 Status = STATUS_INSUFFICIENT_RESOURCES;
1831 }
1832 else
1833 {
1834 NewKeyCell->Id = REG_KEY_CELL_ID;
1835 NewKeyCell->Type = REG_KEY_CELL_TYPE;
1836 ZwQuerySystemTime((PTIME) &NewKeyCell->LastWriteTime);
1837 NewKeyCell->ParentKeyOffset = -1;
1838 NewKeyCell->NumberOfSubKeys = 0;
1839 NewKeyCell->HashTableOffset = -1;
1840 NewKeyCell->NumberOfValues = 0;
1841 NewKeyCell->ValuesOffset = -1;
1842 NewKeyCell->SecurityKeyOffset = -1;
1843 NewKeyCell->NameSize = NameSize;
1844 wcstombs(NewKeyCell->Name, NewSubKeyName, NameSize);
1845 NewKeyCell->ClassNameOffset = -1;
1846
1847 VERIFY_KEY_CELL(NewKeyCell);
1848
1849 if (Class)
1850 {
1851 PDATA_CELL pClass;
1852
1853 NewKeyCell->ClassSize = Class->Length + sizeof(WCHAR);
1854 Status = CmiAllocateBlock(RegistryHive,
1855 (PVOID) &pClass,
1856 NewKeyCell->ClassSize,
1857 &NewKeyCell->ClassNameOffset);
1858 wcsncpy((PWSTR) pClass->Data, Class->Buffer, Class->Length);
1859 ((PWSTR) (pClass->Data))[Class->Length] = 0;
1860 }
1861 }
1862
1863 if (!NT_SUCCESS(Status))
1864 {
1865 return Status;
1866 }
1867
1868 SubKey->KeyCell = NewKeyCell;
1869 SubKey->BlockOffset = NKBOffset;
1870
1871 /* Don't modify hash table if key is volatile and parent is not */
1872 if (IsVolatileHive(RegistryHive) && (!IsVolatileHive(Parent->RegistryHive)))
1873 {
1874 return(Status);
1875 }
1876
1877 if (KeyCell->HashTableOffset == (ULONG_PTR) -1)
1878 {
1879 Status = CmiAllocateHashTableBlock(RegistryHive,
1880 &HashBlock,
1881 &KeyCell->HashTableOffset,
1882 REG_INIT_HASH_TABLE_SIZE);
1883 if (!NT_SUCCESS(Status))
1884 {
1885 return(Status);
1886 }
1887 }
1888 else
1889 {
1890 HashBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
1891 if (((KeyCell->NumberOfSubKeys + 1) >= HashBlock->HashTableSize))
1892 {
1893 BLOCK_OFFSET HTOffset;
1894
1895 /* Reallocate the hash table block */
1896 Status = CmiAllocateHashTableBlock(RegistryHive,
1897 &NewHashBlock,
1898 &HTOffset,
1899 HashBlock->HashTableSize +
1900 REG_EXTEND_HASH_TABLE_SIZE);
1901 if (!NT_SUCCESS(Status))
1902 {
1903 return Status;
1904 }
1905
1906 RtlZeroMemory(&NewHashBlock->Table[0],
1907 sizeof(NewHashBlock->Table[0]) * NewHashBlock->HashTableSize);
1908 RtlCopyMemory(&NewHashBlock->Table[0],
1909 &HashBlock->Table[0],
1910 sizeof(NewHashBlock->Table[0]) * HashBlock->HashTableSize);
1911 CmiDestroyBlock(RegistryHive,
1912 HashBlock,
1913 KeyCell->HashTableOffset);
1914 KeyCell->HashTableOffset = HTOffset;
1915 HashBlock = NewHashBlock;
1916 }
1917 }
1918
1919 Status = CmiAddKeyToHashTable(RegistryHive,
1920 HashBlock,
1921 NewKeyCell,
1922 NKBOffset);
1923 if (NT_SUCCESS(Status))
1924 {
1925 KeyCell->NumberOfSubKeys++;
1926 }
1927
1928 return(Status);
1929 }
1930
1931
1932 NTSTATUS
1933 CmiRemoveSubKey(PREGISTRY_HIVE RegistryHive,
1934 PKEY_OBJECT ParentKey,
1935 PKEY_OBJECT SubKey)
1936 {
1937 PHASH_TABLE_CELL HashBlock;
1938 PVALUE_LIST_CELL ValueList;
1939 PVALUE_CELL ValueCell;
1940 PDATA_CELL DataCell;
1941 ULONG i;
1942
1943 DPRINT1("CmiRemoveSubKey() called\n");
1944
1945 /* Remove all values */
1946 if (SubKey->KeyCell->NumberOfValues != 0)
1947 {
1948 /* Get pointer to the value list cell */
1949 ValueList = CmiGetBlock(RegistryHive,
1950 SubKey->KeyCell->ValuesOffset,
1951 NULL);
1952 if (ValueList != NULL)
1953 {
1954 /* Enumerate all values */
1955 for (i = 0; i < SubKey->KeyCell->NumberOfValues; i++)
1956 {
1957 /* Get pointer to value cell */
1958 ValueCell = CmiGetBlock(RegistryHive,
1959 ValueList->Values[i],
1960 NULL);
1961 if (ValueCell != NULL)
1962 {
1963 if (ValueCell->DataSize > 4)
1964 {
1965 DataCell = CmiGetBlock(RegistryHive,
1966 ValueCell->DataOffset,
1967 NULL);
1968 if (DataCell != NULL)
1969 {
1970 /* Destroy data cell */
1971 CmiDestroyBlock(RegistryHive,
1972 DataCell,
1973 ValueCell->DataOffset);
1974 }
1975 }
1976
1977 /* Destroy value cell */
1978 CmiDestroyBlock(RegistryHive,
1979 ValueCell,
1980 ValueList->Values[i]);
1981 }
1982 }
1983 }
1984
1985 /* Destroy value list cell */
1986 CmiDestroyBlock(RegistryHive,
1987 ValueList,
1988 SubKey->KeyCell->ValuesOffset);
1989
1990 SubKey->KeyCell->NumberOfValues = 0;
1991 SubKey->KeyCell->ValuesOffset = -1;
1992 }
1993
1994 /* Remove the key from the parent key's hash block */
1995 if (ParentKey->KeyCell->HashTableOffset != -1)
1996 {
1997 DPRINT1("ParentKey HashTableOffset %lx\n", ParentKey->KeyCell->HashTableOffset)
1998 HashBlock = CmiGetBlock(RegistryHive,
1999 ParentKey->KeyCell->HashTableOffset,
2000 NULL);
2001 DPRINT1("ParentKey HashBlock %p\n", HashBlock)
2002 if (HashBlock != NULL)
2003 {
2004 CmiRemoveKeyFromHashTable(RegistryHive,
2005 HashBlock,
2006 SubKey->BlockOffset);
2007 CmiMarkBlockDirty(RegistryHive,
2008 ParentKey->KeyCell->HashTableOffset);
2009 }
2010 }
2011
2012 /* Remove the key's hash block */
2013 if (SubKey->KeyCell->HashTableOffset != -1)
2014 {
2015 DPRINT1("SubKey HashTableOffset %lx\n", SubKey->KeyCell->HashTableOffset)
2016 HashBlock = CmiGetBlock(RegistryHive,
2017 SubKey->KeyCell->HashTableOffset,
2018 NULL);
2019 DPRINT1("SubKey HashBlock %p\n", HashBlock)
2020 if (HashBlock != NULL)
2021 {
2022 CmiDestroyBlock(RegistryHive,
2023 HashBlock,
2024 SubKey->KeyCell->HashTableOffset);
2025 SubKey->KeyCell->HashTableOffset = -1;
2026 }
2027 }
2028
2029 /* Decrement the number of the parent key's sub keys */
2030 if (ParentKey != NULL)
2031 {
2032 DPRINT1("ParentKey %p\n", ParentKey)
2033 ParentKey->KeyCell->NumberOfSubKeys--;
2034
2035 /* Remove the parent key's hash table */
2036 if (ParentKey->KeyCell->NumberOfSubKeys == 0)
2037 {
2038 DPRINT1("ParentKey HashTableOffset %lx\n", ParentKey->KeyCell->HashTableOffset)
2039 HashBlock = CmiGetBlock(RegistryHive,
2040 ParentKey->KeyCell->HashTableOffset,
2041 NULL);
2042 DPRINT1("ParentKey HashBlock %p\n", HashBlock)
2043 if (HashBlock != NULL)
2044 {
2045 CmiDestroyBlock(RegistryHive,
2046 HashBlock,
2047 ParentKey->KeyCell->HashTableOffset);
2048 ParentKey->KeyCell->HashTableOffset = -1;
2049 }
2050 }
2051
2052 NtQuerySystemTime((PTIME)&ParentKey->KeyCell->LastWriteTime);
2053 CmiMarkBlockDirty(RegistryHive,
2054 ParentKey->BlockOffset);
2055 }
2056
2057 /* Destroy key cell */
2058 CmiDestroyBlock(RegistryHive,
2059 SubKey->KeyCell,
2060 SubKey->BlockOffset);
2061 SubKey->BlockOffset = -1;
2062 SubKey->KeyCell = NULL;
2063
2064 return(STATUS_SUCCESS);
2065 }
2066
2067
2068 NTSTATUS
2069 CmiScanKeyForValue(IN PREGISTRY_HIVE RegistryHive,
2070 IN PKEY_CELL KeyCell,
2071 IN PUNICODE_STRING ValueName,
2072 OUT PVALUE_CELL *ValueCell,
2073 OUT BLOCK_OFFSET *VBOffset)
2074 {
2075 PVALUE_LIST_CELL ValueListCell;
2076 PVALUE_CELL CurValueCell;
2077 ULONG i;
2078
2079 ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
2080
2081 *ValueCell = NULL;
2082
2083 if (ValueListCell == NULL)
2084 {
2085 DPRINT("ValueListCell is NULL\n");
2086 return STATUS_SUCCESS;
2087 }
2088
2089 VERIFY_VALUE_LIST_CELL(ValueListCell);
2090
2091 for (i = 0; i < KeyCell->NumberOfValues; i++)
2092 {
2093 CurValueCell = CmiGetBlock(RegistryHive,
2094 ValueListCell->Values[i],
2095 NULL);
2096
2097 if ((CurValueCell != NULL) &&
2098 CmiComparePackedNames(ValueName,
2099 CurValueCell->Name,
2100 CurValueCell->NameSize,
2101 CurValueCell->Flags & REG_VALUE_NAME_PACKED))
2102 {
2103 *ValueCell = CurValueCell;
2104 if (VBOffset)
2105 *VBOffset = ValueListCell->Values[i];
2106 //DPRINT("Found value %s\n", ValueName);
2107 break;
2108 }
2109 CmiReleaseBlock(RegistryHive, CurValueCell);
2110 }
2111
2112 CmiReleaseBlock(RegistryHive, ValueListCell);
2113
2114 return STATUS_SUCCESS;
2115 }
2116
2117
2118 NTSTATUS
2119 CmiGetValueFromKeyByIndex(IN PREGISTRY_HIVE RegistryHive,
2120 IN PKEY_CELL KeyCell,
2121 IN ULONG Index,
2122 OUT PVALUE_CELL *ValueCell)
2123 {
2124 PVALUE_LIST_CELL ValueListCell;
2125 PVALUE_CELL CurValueCell;
2126
2127 ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
2128
2129 *ValueCell = NULL;
2130
2131 if (ValueListCell == NULL)
2132 {
2133 return STATUS_NO_MORE_ENTRIES;
2134 }
2135
2136 VERIFY_VALUE_LIST_CELL(ValueListCell);
2137
2138 if (Index >= KeyCell->NumberOfValues)
2139 {
2140 return STATUS_NO_MORE_ENTRIES;
2141 }
2142
2143 CurValueCell = CmiGetBlock(RegistryHive,
2144 ValueListCell->Values[Index],
2145 NULL);
2146
2147 if (CurValueCell != NULL)
2148 {
2149 *ValueCell = CurValueCell;
2150 }
2151
2152 CmiReleaseBlock(RegistryHive, CurValueCell);
2153 CmiReleaseBlock(RegistryHive, ValueListCell);
2154
2155 return STATUS_SUCCESS;
2156 }
2157
2158
2159 NTSTATUS
2160 CmiAddValueToKey(IN PREGISTRY_HIVE RegistryHive,
2161 IN PKEY_CELL KeyCell,
2162 IN PUNICODE_STRING ValueName,
2163 OUT PVALUE_CELL *pValueCell,
2164 OUT BLOCK_OFFSET *pVBOffset)
2165 {
2166 PVALUE_LIST_CELL NewValueListCell;
2167 PVALUE_LIST_CELL ValueListCell;
2168 PVALUE_CELL NewValueCell;
2169 BLOCK_OFFSET VLBOffset;
2170 BLOCK_OFFSET VBOffset;
2171 NTSTATUS Status;
2172
2173 Status = CmiAllocateValueCell(RegistryHive,
2174 &NewValueCell,
2175 &VBOffset,
2176 ValueName);
2177 *pVBOffset = VBOffset;
2178
2179 if (!NT_SUCCESS(Status))
2180 {
2181 return Status;
2182 }
2183
2184 ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
2185
2186 if (ValueListCell == NULL)
2187 {
2188 Status = CmiAllocateBlock(RegistryHive,
2189 (PVOID) &ValueListCell,
2190 sizeof(BLOCK_OFFSET) * 3,
2191 &VLBOffset);
2192
2193 if (!NT_SUCCESS(Status))
2194 {
2195 CmiDestroyValueCell(RegistryHive, NewValueCell, VBOffset);
2196 return Status;
2197 }
2198 KeyCell->ValuesOffset = VLBOffset;
2199 }
2200 else if ((KeyCell->NumberOfValues
2201 >= (ULONG) ((LONG) (ValueListCell->CellSize - 4)) / (LONG) sizeof(BLOCK_OFFSET)))
2202 {
2203 Status = CmiAllocateBlock(RegistryHive,
2204 (PVOID) &NewValueListCell,
2205 sizeof(BLOCK_OFFSET) * (KeyCell->NumberOfValues + REG_VALUE_LIST_CELL_MULTIPLE),
2206 &VLBOffset);
2207
2208 if (!NT_SUCCESS(Status))
2209 {
2210 CmiDestroyValueCell(RegistryHive, NewValueCell, VBOffset);
2211 return Status;
2212 }
2213
2214 RtlCopyMemory(&NewValueListCell->Values[0],
2215 &ValueListCell->Values[0],
2216 sizeof(BLOCK_OFFSET) * KeyCell->NumberOfValues);
2217 CmiDestroyBlock(RegistryHive, ValueListCell, KeyCell->ValuesOffset);
2218 KeyCell->ValuesOffset = VLBOffset;
2219 ValueListCell = NewValueListCell;
2220 }
2221
2222 DPRINT("KeyCell->NumberOfValues %d, ValueListCell->CellSize %d (%d %x)\n",
2223 KeyCell->NumberOfValues, ValueListCell->CellSize,
2224 -(ValueListCell->CellSize - 4) / sizeof(BLOCK_OFFSET),
2225 -(ValueListCell->CellSize - 4) / sizeof(BLOCK_OFFSET));
2226
2227 ValueListCell->Values[KeyCell->NumberOfValues] = VBOffset;
2228 KeyCell->NumberOfValues++;
2229 CmiReleaseBlock(RegistryHive, ValueListCell);
2230 CmiReleaseBlock(RegistryHive, NewValueCell);
2231 *pValueCell = NewValueCell;
2232
2233 return STATUS_SUCCESS;
2234 }
2235
2236
2237 NTSTATUS
2238 CmiDeleteValueFromKey(IN PREGISTRY_HIVE RegistryHive,
2239 IN PKEY_CELL KeyCell,
2240 IN BLOCK_OFFSET KeyCellOffset,
2241 IN PUNICODE_STRING ValueName)
2242 {
2243 PVALUE_LIST_CELL ValueListCell;
2244 PVALUE_CELL CurValueCell;
2245 ULONG i;
2246
2247 ValueListCell = CmiGetBlock(RegistryHive, KeyCell->ValuesOffset, NULL);
2248
2249 if (ValueListCell == NULL)
2250 {
2251 return STATUS_SUCCESS;
2252 }
2253
2254 VERIFY_VALUE_LIST_CELL(ValueListCell);
2255
2256 for (i = 0; i < KeyCell->NumberOfValues; i++)
2257 {
2258 CurValueCell = CmiGetBlock(RegistryHive, ValueListCell->Values[i], NULL);
2259
2260 if ((CurValueCell != NULL) &&
2261 CmiComparePackedNames(ValueName,
2262 CurValueCell->Name,
2263 CurValueCell->NameSize,
2264 CurValueCell->Flags & REG_VALUE_NAME_PACKED))
2265 {
2266 CmiDestroyValueCell(RegistryHive, CurValueCell, ValueListCell->Values[i]);
2267
2268 if ((KeyCell->NumberOfValues - 1) < i)
2269 {
2270 RtlCopyMemory(&ValueListCell->Values[i],
2271 &ValueListCell->Values[i + 1],
2272 sizeof(BLOCK_OFFSET) * (KeyCell->NumberOfValues - 1 - i));
2273 }
2274 else
2275 {
2276 RtlZeroMemory(&ValueListCell->Values[i], sizeof(BLOCK_OFFSET));
2277 }
2278
2279 KeyCell->NumberOfValues -= 1;
2280 break;
2281 }
2282 CmiReleaseBlock(RegistryHive, CurValueCell);
2283 }
2284
2285 CmiReleaseBlock(RegistryHive, ValueListCell);
2286
2287 if (KeyCell->NumberOfValues == 0)
2288 {
2289 CmiDestroyBlock(RegistryHive,
2290 ValueListCell,
2291 KeyCell->ValuesOffset);
2292 }
2293 else
2294 {
2295 CmiMarkBlockDirty(RegistryHive,
2296 KeyCell->ValuesOffset);
2297 }
2298
2299 CmiMarkBlockDirty(RegistryHive,
2300 KeyCellOffset);
2301
2302 return STATUS_SUCCESS;
2303 }
2304
2305
2306 NTSTATUS
2307 CmiAllocateHashTableBlock(IN PREGISTRY_HIVE RegistryHive,
2308 OUT PHASH_TABLE_CELL *HashBlock,
2309 OUT BLOCK_OFFSET *HBOffset,
2310 IN ULONG HashTableSize)
2311 {
2312 PHASH_TABLE_CELL NewHashBlock;
2313 ULONG NewHashSize;
2314 NTSTATUS Status;
2315
2316 Status = STATUS_SUCCESS;
2317 *HashBlock = NULL;
2318 NewHashSize = sizeof(HASH_TABLE_CELL) +
2319 (HashTableSize - 1) * sizeof(HASH_RECORD);
2320 Status = CmiAllocateBlock(RegistryHive,
2321 (PVOID*) &NewHashBlock,
2322 NewHashSize,
2323 HBOffset);
2324
2325 if ((NewHashBlock == NULL) || (!NT_SUCCESS(Status)))
2326 {
2327 Status = STATUS_INSUFFICIENT_RESOURCES;
2328 }
2329 else
2330 {
2331 NewHashBlock->Id = REG_HASH_TABLE_BLOCK_ID;
2332 NewHashBlock->HashTableSize = HashTableSize;
2333 *HashBlock = NewHashBlock;
2334 }
2335
2336 return Status;
2337 }
2338
2339
2340 PKEY_CELL
2341 CmiGetKeyFromHashByIndex(PREGISTRY_HIVE RegistryHive,
2342 PHASH_TABLE_CELL HashBlock,
2343 ULONG Index)
2344 {
2345 BLOCK_OFFSET KeyOffset;
2346 PKEY_CELL KeyCell;
2347
2348 if (HashBlock == NULL)
2349 return NULL;
2350
2351 if (IsVolatileHive(RegistryHive))
2352 {
2353 KeyCell = (PKEY_CELL) HashBlock->Table[Index].KeyOffset;
2354 }
2355 else
2356 {
2357 KeyOffset = HashBlock->Table[Index].KeyOffset;
2358 KeyCell = CmiGetBlock(RegistryHive, KeyOffset, NULL);
2359 }
2360 CmiLockBlock(RegistryHive, KeyCell);
2361
2362 return KeyCell;
2363 }
2364
2365
2366 NTSTATUS
2367 CmiAddKeyToHashTable(PREGISTRY_HIVE RegistryHive,
2368 PHASH_TABLE_CELL HashBlock,
2369 PKEY_CELL NewKeyCell,
2370 BLOCK_OFFSET NKBOffset)
2371 {
2372 ULONG i;
2373
2374 for (i = 0; i < HashBlock->HashTableSize; i++)
2375 {
2376 if (HashBlock->Table[i].KeyOffset == 0)
2377 {
2378 HashBlock->Table[i].KeyOffset = NKBOffset;
2379 RtlCopyMemory(&HashBlock->Table[i].HashValue, NewKeyCell->Name, 4);
2380 return STATUS_SUCCESS;
2381 }
2382 }
2383
2384 return STATUS_UNSUCCESSFUL;
2385 }
2386
2387
2388 NTSTATUS
2389 CmiRemoveKeyFromHashTable(PREGISTRY_HIVE RegistryHive,
2390 PHASH_TABLE_CELL HashBlock,
2391 BLOCK_OFFSET NKBOffset)
2392 {
2393 ULONG i;
2394
2395 for (i = 0; i < HashBlock->HashTableSize; i++)
2396 {
2397 if (HashBlock->Table[i].KeyOffset == NKBOffset)
2398 {
2399 HashBlock->Table[i].KeyOffset = 0;
2400 RtlZeroMemory(&HashBlock->Table[i].HashValue, 4);
2401 return STATUS_SUCCESS;
2402 }
2403 }
2404
2405 return STATUS_UNSUCCESSFUL;
2406 }
2407
2408
2409 NTSTATUS
2410 CmiAllocateValueCell(PREGISTRY_HIVE RegistryHive,
2411 PVALUE_CELL *ValueCell,
2412 BLOCK_OFFSET *VBOffset,
2413 IN PUNICODE_STRING ValueName)
2414 {
2415 PVALUE_CELL NewValueCell;
2416 NTSTATUS Status;
2417 BOOLEAN Packable;
2418 ULONG NameSize;
2419 ULONG i;
2420
2421 Status = STATUS_SUCCESS;
2422
2423 NameSize = CmiGetPackedNameLength(ValueName,
2424 &Packable);
2425
2426 DPRINT("ValueName->Length %lu NameSize %lu\n", ValueName->Length, NameSize);
2427
2428 Status = CmiAllocateBlock(RegistryHive,
2429 (PVOID*) &NewValueCell,
2430 sizeof(VALUE_CELL) + NameSize,
2431 VBOffset);
2432 if ((NewValueCell == NULL) || (!NT_SUCCESS(Status)))
2433 {
2434 Status = STATUS_INSUFFICIENT_RESOURCES;
2435 }
2436 else
2437 {
2438 NewValueCell->Id = REG_VALUE_CELL_ID;
2439 NewValueCell->NameSize = NameSize;
2440 if (Packable)
2441 {
2442 /* Pack the value name */
2443 for (i = 0; i < NameSize; i++)
2444 NewValueCell->Name[i] = (CHAR)ValueName->Buffer[i];
2445 NewValueCell->Flags |= REG_VALUE_NAME_PACKED;
2446 }
2447 else
2448 {
2449 /* Copy the value name */
2450 RtlCopyMemory(NewValueCell->Name,
2451 ValueName->Buffer,
2452 NameSize);
2453 NewValueCell->Flags = 0;
2454 }
2455 NewValueCell->DataType = 0;
2456 NewValueCell->DataSize = 0;
2457 NewValueCell->DataOffset = 0xffffffff;
2458 *ValueCell = NewValueCell;
2459 }
2460
2461 return Status;
2462 }
2463
2464
2465 NTSTATUS
2466 CmiDestroyValueCell(PREGISTRY_HIVE RegistryHive,
2467 PVALUE_CELL ValueCell,
2468 BLOCK_OFFSET VBOffset)
2469 {
2470 NTSTATUS Status;
2471 PVOID pBlock;
2472 PHBIN pBin;
2473
2474 DPRINT("CmiDestroyValueCell(Cell %p Offset %lx)\n", ValueCell, VBOffset);
2475
2476 VERIFY_VALUE_CELL(ValueCell);
2477
2478 /* Destroy the data cell */
2479 if (ValueCell->DataSize > 4)
2480 {
2481 pBlock = CmiGetBlock(RegistryHive, ValueCell->DataOffset, &pBin);
2482 Status = CmiDestroyBlock(RegistryHive, pBlock, ValueCell->DataOffset);
2483 if (!NT_SUCCESS(Status))
2484 {
2485 return Status;
2486 }
2487
2488 /* Update time of heap */
2489 if (IsPermanentHive(RegistryHive))
2490 ZwQuerySystemTime((PTIME) &pBin->DateModified);
2491 }
2492
2493 /* Destroy the value cell */
2494 Status = CmiDestroyBlock(RegistryHive, ValueCell, VBOffset);
2495
2496 /* Update time of heap */
2497 if (IsPermanentHive(RegistryHive) && CmiGetBlock(RegistryHive, VBOffset, &pBin))
2498 {
2499 ZwQuerySystemTime((PTIME) &pBin->DateModified);
2500 }
2501
2502 return Status;
2503 }
2504
2505
2506 NTSTATUS
2507 CmiAddBin(PREGISTRY_HIVE RegistryHive,
2508 PVOID *NewBlock,
2509 BLOCK_OFFSET *NewBlockOffset)
2510 {
2511 PCELL_HEADER tmpBlock;
2512 PHBIN * tmpBlockList;
2513 PHBIN tmpBin;
2514
2515 tmpBin = ExAllocatePool(PagedPool, REG_BLOCK_SIZE);
2516 if (tmpBin == NULL)
2517 {
2518 return STATUS_INSUFFICIENT_RESOURCES;
2519 }
2520
2521 tmpBin->BlockId = REG_BIN_ID;
2522 tmpBin->BlockOffset = RegistryHive->FileSize - REG_BLOCK_SIZE;
2523 RegistryHive->FileSize += REG_BLOCK_SIZE;
2524 tmpBin->BlockSize = REG_BLOCK_SIZE;
2525 tmpBin->Unused1 = 0;
2526 ZwQuerySystemTime((PTIME) &tmpBin->DateModified);
2527 tmpBin->Unused2 = 0;
2528
2529 /* Increase size of list of blocks */
2530 tmpBlockList = ExAllocatePool(NonPagedPool,
2531 sizeof(PHBIN *) * (RegistryHive->BlockListSize + 1));
2532 if (tmpBlockList == NULL)
2533 {
2534 ExFreePool(tmpBin);
2535 return STATUS_INSUFFICIENT_RESOURCES;
2536 }
2537
2538 if (RegistryHive->BlockListSize > 0)
2539 {
2540 memcpy(tmpBlockList,
2541 RegistryHive->BlockList,
2542 sizeof(PHBIN *)*(RegistryHive->BlockListSize));
2543 ExFreePool(RegistryHive->BlockList);
2544 }
2545
2546 RegistryHive->BlockList = tmpBlockList;
2547 RegistryHive->BlockList[RegistryHive->BlockListSize++] = tmpBin;
2548
2549 /* Initialize a free block in this heap : */
2550 tmpBlock = (PCELL_HEADER)((ULONG_PTR) tmpBin + REG_HBIN_DATA_OFFSET);
2551 tmpBlock->CellSize = (REG_BLOCK_SIZE - REG_HBIN_DATA_OFFSET);
2552
2553 /* Grow bitmap if necessary */
2554 if (IsVolatileHive(RegistryHive) &&
2555 (RegistryHive->BlockListSize % (sizeof(ULONG) * 8) == 0))
2556 {
2557 PULONG BitmapBuffer;
2558 ULONG BitmapSize;
2559
2560 DPRINT1("Grow hive bitmap\n");
2561
2562 /* Calculate bitmap size in bytes (always a multiple of 32 bits) */
2563 BitmapSize = ROUND_UP(RegistryHive->BlockListSize, sizeof(ULONG) * 8) / 8;
2564 DPRINT1("RegistryHive->BlockListSize: %lu\n", RegistryHive->BlockListSize);
2565 DPRINT1("BitmapSize: %lu Bytes %lu Bits\n", BitmapSize, BitmapSize * 8);
2566 BitmapBuffer = (PULONG)ExAllocatePool(PagedPool,
2567 BitmapSize);
2568 RtlZeroMemory(BitmapBuffer, BitmapSize);
2569 RtlCopyMemory(BitmapBuffer,
2570 RegistryHive->DirtyBitMap.Buffer,
2571 RegistryHive->DirtyBitMap.SizeOfBitMap);
2572 ExFreePool(RegistryHive->DirtyBitMap.Buffer);
2573 RtlInitializeBitMap(&RegistryHive->DirtyBitMap,
2574 BitmapBuffer,
2575 BitmapSize * 8);
2576 }
2577
2578 *NewBlock = (PVOID) tmpBlock;
2579
2580 if (NewBlockOffset)
2581 *NewBlockOffset = tmpBin->BlockOffset + REG_HBIN_DATA_OFFSET;
2582
2583 /* Mark new bin dirty */
2584 CmiMarkBinDirty(RegistryHive,
2585 tmpBin->BlockOffset);
2586
2587 /* FIXME: set first dword to block_offset of another free bloc */
2588
2589 return STATUS_SUCCESS;
2590 }
2591
2592
2593 NTSTATUS
2594 CmiAllocateBlock(PREGISTRY_HIVE RegistryHive,
2595 PVOID *Block,
2596 LONG BlockSize,
2597 BLOCK_OFFSET * pBlockOffset)
2598 {
2599 PCELL_HEADER NewBlock;
2600 NTSTATUS Status;
2601 PHBIN pBin;
2602
2603 Status = STATUS_SUCCESS;
2604
2605 /* Round to 16 bytes multiple */
2606 BlockSize = (BlockSize + sizeof(DWORD) + 15) & 0xfffffff0;
2607
2608 /* Handle volatile hives first */
2609 if (IsVolatileHive(RegistryHive))
2610 {
2611 NewBlock = ExAllocatePool(NonPagedPool, BlockSize);
2612
2613 if (NewBlock == NULL)
2614 {
2615 Status = STATUS_INSUFFICIENT_RESOURCES;
2616 }
2617 else
2618 {
2619 RtlZeroMemory(NewBlock, BlockSize);
2620 NewBlock->CellSize = BlockSize;
2621 CmiLockBlock(RegistryHive, NewBlock);
2622 *Block = NewBlock;
2623 if (pBlockOffset)
2624 *pBlockOffset = (BLOCK_OFFSET) NewBlock;
2625 }
2626 }
2627 else
2628 {
2629 ULONG i;
2630
2631 /* first search in free blocks */
2632 NewBlock = NULL;
2633 for (i = 0; i < RegistryHive->FreeListSize; i++)
2634 {
2635 if (RegistryHive->FreeList[i]->CellSize >= BlockSize)
2636 {
2637 PVOID Temp;
2638
2639 NewBlock = RegistryHive->FreeList[i];
2640 if (pBlockOffset)
2641 *pBlockOffset = RegistryHive->FreeListOffset[i];
2642
2643 /* Update time of heap */
2644 Temp = CmiGetBlock(RegistryHive, RegistryHive->FreeListOffset[i], &pBin);
2645
2646 if (Temp)
2647 {
2648 ZwQuerySystemTime((PTIME) &pBin->DateModified);
2649 CmiMarkBlockDirty(RegistryHive, RegistryHive->FreeListOffset[i]);
2650 }
2651
2652 if ((i + 1) < RegistryHive->FreeListSize)
2653 {
2654 RtlMoveMemory(&RegistryHive->FreeList[i],
2655 &RegistryHive->FreeList[i + 1],
2656 sizeof(RegistryHive->FreeList[0])
2657 * (RegistryHive->FreeListSize - i - 1));
2658 RtlMoveMemory(&RegistryHive->FreeListOffset[i],
2659 &RegistryHive->FreeListOffset[i + 1],
2660 sizeof(RegistryHive->FreeListOffset[0])
2661 * (RegistryHive->FreeListSize - i - 1));
2662 }
2663 RegistryHive->FreeListSize--;
2664 break;
2665 }
2666 }
2667
2668 /* Need to extend hive file : */
2669 if (NewBlock == NULL)
2670 {
2671 /* Add a new block */
2672 Status = CmiAddBin(RegistryHive, (PVOID *) &NewBlock , pBlockOffset);
2673 }
2674
2675 if (NT_SUCCESS(Status))
2676 {
2677 *Block = NewBlock;
2678
2679 /* Split the block in two parts */
2680 if (NewBlock->CellSize > BlockSize)
2681 {
2682 NewBlock = (PCELL_HEADER) ((ULONG_PTR) NewBlock+BlockSize);
2683 NewBlock->CellSize = ((PCELL_HEADER) (*Block))->CellSize - BlockSize;
2684 CmiAddFree(RegistryHive,
2685 NewBlock,
2686 *pBlockOffset + BlockSize,
2687 TRUE);
2688 CmiMarkBlockDirty(RegistryHive,
2689 *pBlockOffset + BlockSize);
2690 }
2691 else if (NewBlock->CellSize < BlockSize)
2692 {
2693 return(STATUS_UNSUCCESSFUL);
2694 }
2695
2696 RtlZeroMemory(*Block, BlockSize);
2697 ((PCELL_HEADER) (*Block))->CellSize = -BlockSize;
2698 CmiLockBlock(RegistryHive, *Block);
2699 }
2700 }
2701
2702 return(Status);
2703 }
2704
2705
2706 NTSTATUS
2707 CmiDestroyBlock(PREGISTRY_HIVE RegistryHive,
2708 PVOID Block,
2709 BLOCK_OFFSET Offset)
2710 {
2711 NTSTATUS Status;
2712 PHBIN pBin;
2713
2714 Status = STATUS_SUCCESS;
2715
2716 if (IsVolatileHive(RegistryHive))
2717 {
2718 CmiReleaseBlock(RegistryHive, Block);
2719 ExFreePool(Block);
2720 }
2721 else
2722 {
2723 PCELL_HEADER pFree = Block;
2724
2725 if (pFree->CellSize < 0)
2726 pFree->CellSize = -pFree->CellSize;
2727
2728 /* Clear block (except the block size) */
2729 RtlZeroMemory(((PVOID)pFree) + sizeof(ULONG),
2730 pFree->CellSize - sizeof(ULONG));
2731
2732 /* add block to the list of free blocks */
2733 CmiAddFree(RegistryHive, Block, Offset, TRUE);
2734 CmiReleaseBlock(RegistryHive, Block);
2735
2736 /* Update time of heap */
2737 if (IsPermanentHive(RegistryHive) && CmiGetBlock(RegistryHive, Offset,&pBin))
2738 ZwQuerySystemTime((PTIME) &pBin->DateModified);
2739
2740 CmiMarkBlockDirty(RegistryHive, Offset);
2741
2742 /* FIXME: Set first dword to block_offset of another free block ? */
2743 /* FIXME: Concatenate with previous and next block if free */
2744 }
2745
2746 return Status;
2747 }
2748
2749
2750 static BOOLEAN
2751 CmiMergeFree(PREGISTRY_HIVE RegistryHive,
2752 PCELL_HEADER FreeBlock,
2753 BLOCK_OFFSET FreeOffset)
2754 {
2755 BLOCK_OFFSET BlockOffset;
2756 BLOCK_OFFSET BinOffset;
2757 ULONG BlockSize;
2758 ULONG BinSize;
2759 PHBIN Bin;
2760 LONG i;
2761
2762 DPRINT("CmiMergeFree(Block %lx Offset %lx Size %lx) called\n",
2763 FreeBlock, FreeOffset, FreeBlock->CellSize);
2764
2765 CmiGetBlock(RegistryHive,
2766 FreeOffset,
2767 &Bin);
2768 DPRINT("Bin %p\n", Bin);
2769 if (Bin == NULL)
2770 return(FALSE);
2771
2772 BinOffset = Bin->BlockOffset;
2773 BinSize = Bin->BlockSize;
2774 DPRINT("Bin %p Offset %lx Size %lx\n", Bin, BinOffset, BinSize);
2775
2776 for (i = 0; i < RegistryHive->FreeListSize; i++)
2777 {
2778 BlockOffset = RegistryHive->FreeListOffset[i];
2779 BlockSize = RegistryHive->FreeList[i]->CellSize;
2780 if (BlockOffset > BinOffset &&
2781 BlockOffset < BinOffset + BinSize)
2782 {
2783 DPRINT("Free block: Offset %lx Size %lx\n",
2784 BlockOffset, BlockSize);
2785
2786 if ((i < (RegistryHive->FreeListSize - 1)) &&
2787 (BlockOffset + BlockSize == FreeOffset) &&
2788 (FreeOffset + FreeBlock->CellSize == RegistryHive->FreeListOffset[i + 1]))
2789 {
2790 DPRINT("Merge current block with previous and next block\n");
2791
2792 RegistryHive->FreeList[i]->CellSize +=
2793 (FreeBlock->CellSize + RegistryHive->FreeList[i + 1]->CellSize);
2794
2795 FreeBlock->CellSize = 0;
2796 RegistryHive->FreeList[i + 1]->CellSize = 0;
2797
2798
2799 if ((i + 2) < RegistryHive->FreeListSize)
2800 {
2801 RtlMoveMemory(&RegistryHive->FreeListOffset[i + 1],
2802 &RegistryHive->FreeListOffset[i + 2],
2803 sizeof(RegistryHive->FreeListOffset[0])
2804 * (RegistryHive->FreeListSize - i - 2));
2805 }
2806 RegistryHive->FreeListSize--;
2807
2808 CmiMarkBlockDirty(RegistryHive, BlockOffset);
2809
2810 return(TRUE);
2811 }
2812 else if (BlockOffset + BlockSize == FreeOffset)
2813 {
2814 DPRINT("Merge current block with previous block\n");
2815
2816 RegistryHive->FreeList[i]->CellSize += FreeBlock->CellSize;
2817 FreeBlock->CellSize = 0;
2818
2819 CmiMarkBlockDirty(RegistryHive, BlockOffset);
2820
2821 return(TRUE);
2822 }
2823 else if (FreeOffset + FreeBlock->CellSize == BlockOffset)
2824 {
2825 DPRINT("Merge current block with next block\n");
2826
2827 FreeBlock->CellSize += RegistryHive->FreeList[i]->CellSize;
2828 RegistryHive->FreeList[i]->CellSize = 0;
2829 RegistryHive->FreeList[i] = FreeBlock;
2830 RegistryHive->FreeListOffset[i] = FreeOffset;
2831
2832 CmiMarkBlockDirty(RegistryHive, FreeOffset);
2833
2834 return(TRUE);
2835 }
2836 }
2837 }
2838
2839 return(FALSE);
2840 }
2841
2842
2843 NTSTATUS
2844 CmiAddFree(PREGISTRY_HIVE RegistryHive,
2845 PCELL_HEADER FreeBlock,
2846 BLOCK_OFFSET FreeOffset,
2847 BOOLEAN MergeFreeBlocks)
2848 {
2849 PCELL_HEADER *tmpList;
2850 BLOCK_OFFSET *tmpListOffset;
2851 LONG minInd;
2852 LONG maxInd;
2853 LONG medInd;
2854
2855 assert(RegistryHive);
2856 assert(FreeBlock);
2857
2858 DPRINT("FreeBlock %.08lx FreeOffset %.08lx\n",
2859 FreeBlock, FreeOffset);
2860
2861 /* Merge free blocks */
2862 if (MergeFreeBlocks == TRUE)
2863 {
2864 if (CmiMergeFree(RegistryHive, FreeBlock, FreeOffset))
2865 return(STATUS_SUCCESS);
2866 }
2867
2868 if ((RegistryHive->FreeListSize + 1) > RegistryHive->FreeListMax)
2869 {
2870 tmpList = ExAllocatePool(PagedPool,
2871 sizeof(PCELL_HEADER) * (RegistryHive->FreeListMax + 32));
2872 if (tmpList == NULL)
2873 return STATUS_INSUFFICIENT_RESOURCES;
2874
2875 tmpListOffset = ExAllocatePool(PagedPool,
2876 sizeof(BLOCK_OFFSET *) * (RegistryHive->FreeListMax + 32));
2877
2878 if (tmpListOffset == NULL)
2879 {
2880 ExFreePool(tmpList);
2881 return STATUS_INSUFFICIENT_RESOURCES;
2882 }
2883
2884 if (RegistryHive->FreeListMax)
2885 {
2886 RtlMoveMemory(tmpList,
2887 RegistryHive->FreeList,
2888 sizeof(PCELL_HEADER) * (RegistryHive->FreeListMax));
2889 RtlMoveMemory(tmpListOffset,
2890 RegistryHive->FreeListOffset,
2891 sizeof(BLOCK_OFFSET *) * (RegistryHive->FreeListMax));
2892 ExFreePool(RegistryHive->FreeList);
2893 ExFreePool(RegistryHive->FreeListOffset);
2894 }
2895 RegistryHive->FreeList = tmpList;
2896 RegistryHive->FreeListOffset = tmpListOffset;
2897 RegistryHive->FreeListMax += 32;
2898 }
2899
2900 /* Add new offset to free list, maintaining list in ascending order */
2901 if ((RegistryHive->FreeListSize == 0)
2902 || (RegistryHive->FreeListOffset[RegistryHive->FreeListSize-1] < FreeOffset))
2903 {
2904 /* Add to end of list */
2905 RegistryHive->FreeList[RegistryHive->FreeListSize] = FreeBlock;
2906 RegistryHive->FreeListOffset[RegistryHive->FreeListSize++] = FreeOffset;
2907 }
2908 else if (RegistryHive->FreeListOffset[0] > FreeOffset)
2909 {
2910 /* Add to begin of list */
2911 RtlMoveMemory(&RegistryHive->FreeList[1],
2912 &RegistryHive->FreeList[0],
2913 sizeof(RegistryHive->FreeList[0]) * RegistryHive->FreeListSize);
2914 RtlMoveMemory(&RegistryHive->FreeListOffset[1],
2915 &RegistryHive->FreeListOffset[0],
2916 sizeof(RegistryHive->FreeListOffset[0]) * RegistryHive->FreeListSize);
2917 RegistryHive->FreeList[0] = FreeBlock;
2918 RegistryHive->FreeListOffset[0] = FreeOffset;
2919 RegistryHive->FreeListSize++;
2920 }
2921 else
2922 {
2923 /* Search where to insert */
2924 minInd = 0;
2925 maxInd = RegistryHive->FreeListSize - 1;
2926 while ((maxInd - minInd) > 1)
2927 {
2928 medInd = (minInd + maxInd) / 2;
2929 if (RegistryHive->FreeListOffset[medInd] > FreeOffset)
2930 maxInd = medInd;
2931 else
2932 minInd = medInd;
2933 }
2934
2935 /* Insert before maxInd */
2936 RtlMoveMemory(&RegistryHive->FreeList[maxInd+1],
2937 &RegistryHive->FreeList[maxInd],
2938 sizeof(RegistryHive->FreeList[0]) * (RegistryHive->FreeListSize - minInd));
2939 RtlMoveMemory(&RegistryHive->FreeListOffset[maxInd + 1],
2940 &RegistryHive->FreeListOffset[maxInd],
2941 sizeof(RegistryHive->FreeListOffset[0]) * (RegistryHive->FreeListSize-minInd));
2942 RegistryHive->FreeList[maxInd] = FreeBlock;
2943 RegistryHive->FreeListOffset[maxInd] = FreeOffset;
2944 RegistryHive->FreeListSize++;
2945 }
2946
2947 return STATUS_SUCCESS;
2948 }
2949
2950
2951 PVOID
2952 CmiGetBlock(PREGISTRY_HIVE RegistryHive,
2953 BLOCK_OFFSET BlockOffset,
2954 PHBIN * ppBin)
2955 {
2956 if (ppBin)
2957 *ppBin = NULL;
2958
2959 if ((BlockOffset == 0) || (BlockOffset == (ULONG_PTR) -1))
2960 return NULL;
2961
2962 if (IsVolatileHive(RegistryHive))
2963 {
2964 return (PVOID) BlockOffset;
2965 }
2966 else
2967 {
2968 PHBIN pBin;
2969
2970 pBin = RegistryHive->BlockList[BlockOffset / 4096];
2971 if (ppBin)
2972 *ppBin = pBin;
2973 return((PVOID)((ULONG_PTR)pBin + (BlockOffset - pBin->BlockOffset)));
2974 }
2975 }
2976
2977
2978 VOID
2979 CmiLockBlock(PREGISTRY_HIVE RegistryHive,
2980 PVOID Block)
2981 {
2982 if (IsPermanentHive(RegistryHive))
2983 {
2984 /* FIXME: Implement */
2985 }
2986 }
2987
2988
2989 VOID
2990 CmiReleaseBlock(PREGISTRY_HIVE RegistryHive,
2991 PVOID Block)
2992 {
2993 if (IsPermanentHive(RegistryHive))
2994 {
2995 /* FIXME: Implement */
2996 }
2997 }
2998
2999
3000 VOID
3001 CmiMarkBlockDirty(PREGISTRY_HIVE RegistryHive,
3002 BLOCK_OFFSET BlockOffset)
3003 {
3004 PDATA_CELL Cell;
3005 LONG CellSize;
3006 ULONG BlockNumber;
3007 ULONG BlockCount;
3008
3009 if (IsVolatileHive(RegistryHive))
3010 return;
3011
3012 DPRINT1("CmiMarkBlockDirty(Offset 0x%lx)\n", (ULONG)BlockOffset);
3013
3014 BlockNumber = (ULONG)BlockOffset / 4096;
3015
3016 Cell = CmiGetBlock(RegistryHive,
3017 BlockOffset,
3018 NULL);
3019
3020 CellSize = Cell->CellSize;
3021 if (CellSize < 0)
3022 CellSize = -CellSize;
3023
3024 BlockCount = (ROUND_UP(BlockOffset + CellSize, 4096) - ROUND_DOWN(BlockOffset, 4096)) / 4096;
3025
3026 DPRINT1(" BlockNumber %lu Size %lu (%s) BlockCount %lu\n",
3027 BlockNumber,
3028 CellSize,
3029 (Cell->CellSize < 0) ? "used" : "free",
3030 BlockCount);
3031
3032 RegistryHive->HiveDirty = TRUE;
3033 RtlSetBits(&RegistryHive->DirtyBitMap,
3034 BlockNumber,
3035 BlockCount);
3036 }
3037
3038
3039 VOID
3040 CmiMarkBinDirty(PREGISTRY_HIVE RegistryHive,
3041 BLOCK_OFFSET BinOffset)
3042 {
3043 ULONG BlockNumber;
3044 ULONG BlockCount;
3045 PHBIN Bin;
3046
3047 if (IsVolatileHive(RegistryHive))
3048 return;
3049
3050 DPRINT1("CmiMarkBinDirty(Offset 0x%lx)\n", (ULONG)BinOffset);
3051
3052 BlockNumber = (ULONG)BinOffset / 4096;
3053
3054 Bin = RegistryHive->BlockList[BlockNumber];
3055
3056 BlockCount = Bin->BlockSize / 4096;
3057
3058 DPRINT1(" BlockNumber %lu Size %lu BlockCount %lu\n",
3059 BlockNumber,
3060 Bin->BlockSize,
3061 BlockCount);
3062
3063 RegistryHive->HiveDirty = TRUE;
3064 RtlSetBits(&RegistryHive->DirtyBitMap,
3065 BlockNumber,
3066 BlockCount);
3067 }
3068
3069
3070 ULONG
3071 CmiGetPackedNameLength(IN PUNICODE_STRING Name,
3072 OUT PBOOLEAN Packable)
3073 {
3074 ULONG i;
3075
3076 if (Packable != NULL)
3077 *Packable = TRUE;
3078
3079 for (i = 0; i < Name->Length; i++)
3080 {
3081 if (Name->Buffer[i] > 0xFF)
3082 {
3083 if (Packable != NULL)
3084 *Packable = FALSE;
3085 return(Name->Length);
3086 }
3087 }
3088
3089 return(Name->Length / sizeof(WCHAR));
3090 }
3091
3092
3093 BOOLEAN
3094 CmiComparePackedNames(IN PUNICODE_STRING Name,
3095 IN PCHAR NameBuffer,
3096 IN USHORT NameBufferSize,
3097 IN BOOLEAN NamePacked)
3098 {
3099 PWCHAR UNameBuffer;
3100 ULONG i;
3101
3102 if (NamePacked == TRUE)
3103 {
3104 if (Name->Length != NameBufferSize * sizeof(WCHAR))
3105 return(FALSE);
3106
3107 for (i = 0; i < Name->Length / sizeof(WCHAR); i++)
3108 {
3109 if (RtlUpcaseUnicodeChar(Name->Buffer[i]) != RtlUpcaseUnicodeChar((WCHAR)NameBuffer[i]))
3110 return(FALSE);
3111 }
3112 }
3113 else
3114 {
3115 if (Name->Length != NameBufferSize)
3116 return(FALSE);
3117
3118 UNameBuffer = (PWCHAR)NameBuffer;
3119
3120 for (i = 0; i < Name->Length / sizeof(WCHAR); i++)
3121 {
3122 if (RtlUpcaseUnicodeChar(Name->Buffer[i]) != RtlUpcaseUnicodeChar(UNameBuffer[i]))
3123 return(FALSE);
3124 }
3125 }
3126
3127 return(TRUE);
3128 }
3129
3130
3131 VOID
3132 CmiCopyPackedName(PWCHAR NameBuffer,
3133 PCHAR PackedNameBuffer,
3134 ULONG PackedNameSize)
3135 {
3136 ULONG i;
3137
3138 for (i = 0; i < PackedNameSize; i++)
3139 NameBuffer[i] = (WCHAR)PackedNameBuffer[i];
3140 }
3141
3142 /* EOF */