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