Of course, I forgot to commit the new file in revision 22049...
[reactos.git] / reactos / ntoskrnl / cm / registry.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/cm/registry.c
6 * PURPOSE: Registry functions
7 *
8 * PROGRAMMERS: Rex Jolliff
9 * Matt Pyne
10 * Jean Michault
11 */
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 #include "cm.h"
18
19 #if defined (ALLOC_PRAGMA)
20 #pragma alloc_text(INIT, CmInitHives)
21 #pragma alloc_text(INIT, CmInitializeRegistry)
22 #pragma alloc_text(INIT, CmInit2)
23 #endif
24
25
26 /* GLOBALS ******************************************************************/
27
28 POBJECT_TYPE CmiKeyType = NULL;
29 PREGISTRY_HIVE CmiVolatileHive = NULL;
30
31 LIST_ENTRY CmiHiveListHead;
32
33 ERESOURCE CmiRegistryLock;
34
35 KTIMER CmiWorkerTimer;
36 LIST_ENTRY CmiKeyObjectListHead;
37 ULONG CmiTimer = 0;
38
39 volatile BOOLEAN CmiHiveSyncEnabled = FALSE;
40 volatile BOOLEAN CmiHiveSyncPending = FALSE;
41 KDPC CmiHiveSyncDpc;
42 KTIMER CmiHiveSyncTimer;
43
44 static GENERIC_MAPPING CmiKeyMapping =
45 {KEY_READ, KEY_WRITE, KEY_EXECUTE, KEY_ALL_ACCESS};
46
47
48
49 VOID
50 CmiCheckKey(BOOLEAN Verbose,
51 HANDLE Key);
52
53 static NTSTATUS
54 CmiCreateCurrentControlSetLink(VOID);
55
56 static VOID STDCALL
57 CmiHiveSyncDpcRoutine(PKDPC Dpc,
58 PVOID DeferredContext,
59 PVOID SystemArgument1,
60 PVOID SystemArgument2);
61
62 extern LIST_ENTRY CmiCallbackHead;
63 extern FAST_MUTEX CmiCallbackLock;
64
65 /* FUNCTIONS ****************************************************************/
66
67 VOID
68 CmiCheckSubKeys(BOOLEAN Verbose,
69 HANDLE Key)
70 {
71 OBJECT_ATTRIBUTES ObjectAttributes;
72 PKEY_NODE_INFORMATION KeyInfo;
73 WCHAR KeyBuffer[MAX_PATH];
74 UNICODE_STRING KeyPath;
75 WCHAR Name[MAX_PATH];
76 ULONG BufferSize;
77 ULONG ResultSize;
78 NTSTATUS Status;
79 HANDLE SubKey;
80 ULONG Index;
81
82 Index = 0;
83 while (TRUE)
84 {
85 BufferSize = sizeof(KEY_NODE_INFORMATION) + 4096;
86 KeyInfo = ExAllocatePool(PagedPool, BufferSize);
87
88 Status = ZwEnumerateKey(Key,
89 Index,
90 KeyNodeInformation,
91 KeyInfo,
92 BufferSize,
93 &ResultSize);
94 if (!NT_SUCCESS(Status))
95 {
96 ExFreePool(KeyInfo);
97 if (Status == STATUS_NO_MORE_ENTRIES)
98 Status = STATUS_SUCCESS;
99 break;
100 }
101
102 wcsncpy(Name,
103 KeyInfo->Name,
104 KeyInfo->NameLength / sizeof(WCHAR));
105
106 if (Verbose)
107 {
108 DbgPrint("Key: %S\n", Name);
109 }
110
111 /* FIXME: Check info. */
112
113 ExFreePool(KeyInfo);
114
115 wcscpy(KeyBuffer, L"\\Registry\\");
116 wcscat(KeyBuffer, Name);
117
118 RtlInitUnicodeString(&KeyPath, KeyBuffer);
119
120 InitializeObjectAttributes(&ObjectAttributes,
121 &KeyPath,
122 OBJ_CASE_INSENSITIVE,
123 NULL,
124 NULL);
125
126 Status = ZwOpenKey(&SubKey,
127 KEY_ALL_ACCESS,
128 &ObjectAttributes);
129
130 ASSERT(NT_SUCCESS(Status));
131
132 CmiCheckKey(Verbose, SubKey);
133
134 ZwClose(SubKey);
135
136 Index++;
137 }
138
139 ASSERT(NT_SUCCESS(Status));
140 }
141
142
143 VOID
144 CmiCheckValues(BOOLEAN Verbose,
145 HANDLE Key)
146 {
147 PKEY_NODE_INFORMATION ValueInfo;
148 WCHAR Name[MAX_PATH];
149 ULONG BufferSize;
150 ULONG ResultSize;
151 NTSTATUS Status;
152 ULONG Index;
153
154 Index = 0;
155 while (TRUE)
156 {
157 BufferSize = sizeof(KEY_NODE_INFORMATION) + 4096;
158 ValueInfo = ExAllocatePool(PagedPool, BufferSize);
159
160 Status = ZwEnumerateValueKey(Key,
161 Index,
162 KeyNodeInformation,
163 ValueInfo,
164 BufferSize,
165 &ResultSize);
166 if (!NT_SUCCESS(Status))
167 {
168 ExFreePool(ValueInfo);
169 if (Status == STATUS_NO_MORE_ENTRIES)
170 Status = STATUS_SUCCESS;
171 break;
172 }
173
174 wcsncpy(Name,
175 ValueInfo->Name,
176 ValueInfo->NameLength / sizeof(WCHAR));
177
178 if (Verbose)
179 {
180 DbgPrint("Value: %S\n", Name);
181 }
182
183 /* FIXME: Check info. */
184
185 ExFreePool(ValueInfo);
186
187 Index++;
188 }
189
190 ASSERT(NT_SUCCESS(Status));
191 }
192
193
194 VOID
195 CmiCheckKey(BOOLEAN Verbose,
196 HANDLE Key)
197 {
198 CmiCheckValues(Verbose, Key);
199 CmiCheckSubKeys(Verbose, Key);
200 }
201
202
203 VOID
204 CmiCheckByName(BOOLEAN Verbose,
205 PWSTR KeyName)
206 {
207 OBJECT_ATTRIBUTES ObjectAttributes;
208 WCHAR KeyPathBuffer[MAX_PATH];
209 UNICODE_STRING KeyPath;
210 NTSTATUS Status;
211 HANDLE Key;
212
213 wcscpy(KeyPathBuffer, L"\\Registry\\");
214 wcscat(KeyPathBuffer, KeyName);
215
216 RtlInitUnicodeString(&KeyPath, KeyPathBuffer);
217
218 InitializeObjectAttributes(&ObjectAttributes,
219 &KeyPath,
220 OBJ_CASE_INSENSITIVE,
221 NULL,
222 NULL);
223
224 Status = ZwOpenKey(&Key,
225 KEY_ALL_ACCESS,
226 &ObjectAttributes);
227
228 if (CHECKED)
229 {
230 if (!NT_SUCCESS(Status))
231 {
232 DbgPrint("KeyPath %wZ Status: %.08x", KeyPath, Status);
233 DbgPrint("KeyPath %S Status: %.08x", KeyPath.Buffer, Status);
234 ASSERT(NT_SUCCESS(Status));
235 }
236 }
237
238 CmiCheckKey(Verbose, Key);
239
240 ZwClose(Key);
241 }
242
243
244 VOID
245 CmiCheckRegistry(BOOLEAN Verbose)
246 {
247 if (Verbose)
248 DbgPrint("Checking registry internals\n");
249
250 CmiCheckByName(Verbose, L"Machine");
251 CmiCheckByName(Verbose, L"User");
252 }
253
254 VOID STDCALL
255 CmiWorkerThread(PVOID Param)
256 {
257 NTSTATUS Status;
258 PLIST_ENTRY CurrentEntry;
259 PKEY_OBJECT CurrentKey;
260 ULONG Count;
261
262
263 while (1)
264 {
265 Status = KeWaitForSingleObject(&CmiWorkerTimer,
266 Executive,
267 KernelMode,
268 FALSE,
269 NULL);
270 if (Status == STATUS_SUCCESS)
271 {
272 DPRINT("CmiWorkerThread\n");
273
274 /* Acquire hive lock */
275 KeEnterCriticalRegion();
276 ExAcquireResourceExclusiveLite(&CmiRegistryLock, TRUE);
277
278 CmiTimer++;
279
280 Count = 0;
281 CurrentEntry = CmiKeyObjectListHead.Blink;
282 while (CurrentEntry != &CmiKeyObjectListHead)
283 {
284 CurrentKey = CONTAINING_RECORD(CurrentEntry, KEY_OBJECT, ListEntry);
285 if (CurrentKey->TimeStamp + 120 > CmiTimer)
286 {
287 /* The object was accessed in the last 10min */
288 break;
289 }
290 if (1 == ObGetObjectPointerCount(CurrentKey) &&
291 !(CurrentKey->Flags & KO_MARKED_FOR_DELETE))
292 {
293 ObDereferenceObject(CurrentKey);
294 CurrentEntry = CmiKeyObjectListHead.Blink;
295 Count++;
296 }
297 else
298 {
299 CurrentEntry = CurrentEntry->Blink;
300 }
301 }
302 ExReleaseResourceLite(&CmiRegistryLock);
303 KeLeaveCriticalRegion();
304
305 DPRINT("Removed %d key objects\n", Count);
306
307 }
308 else
309 {
310 KEBUGCHECK(0);
311 }
312 }
313 }
314
315 VOID
316 INIT_FUNCTION
317 STDCALL
318 CmInitHives(BOOLEAN SetupBoot)
319 {
320 PCHAR BaseAddress;
321
322 /* Load Registry Hives. This one can be missing. */
323 if (CachedModules[SystemRegistry]) {
324 BaseAddress = (PCHAR)CachedModules[SystemRegistry]->ModStart;
325 CmImportSystemHive(BaseAddress,
326 CachedModules[SystemRegistry]->ModEnd - (ULONG_PTR)BaseAddress);
327 }
328
329 BaseAddress = (PCHAR)CachedModules[HardwareRegistry]->ModStart;
330 CmImportHardwareHive(BaseAddress,
331 CachedModules[HardwareRegistry]->ModEnd - (ULONG_PTR)BaseAddress);
332
333
334 /* Create dummy keys if no hardware hive was found */
335 CmImportHardwareHive (NULL, 0);
336
337 /* Initialize volatile registry settings */
338 if (SetupBoot == FALSE) CmInit2((PCHAR)KeLoaderBlock.CommandLine);
339 }
340
341 VOID
342 INIT_FUNCTION
343 CmInitializeRegistry(VOID)
344 {
345 OBJECT_ATTRIBUTES ObjectAttributes;
346 UNICODE_STRING KeyName;
347 PKEY_OBJECT RootKey;
348 #if 0
349 PSECURITY_CELL RootSecurityCell;
350 #endif
351 HANDLE RootKeyHandle;
352 HANDLE KeyHandle;
353 NTSTATUS Status;
354 LARGE_INTEGER DueTime;
355 HANDLE ThreadHandle;
356 CLIENT_ID ThreadId;
357 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
358 UNICODE_STRING Name;
359
360 DPRINT("Creating Registry Object Type\n");
361
362 /* Initialize the Key object type */
363 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
364 RtlInitUnicodeString(&Name, L"Key");
365 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
366 ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(KEY_OBJECT);
367 ObjectTypeInitializer.GenericMapping = CmiKeyMapping;
368 ObjectTypeInitializer.PoolType = PagedPool;
369 ObjectTypeInitializer.ValidAccessMask = KEY_ALL_ACCESS;
370 ObjectTypeInitializer.UseDefaultObject = TRUE;
371 ObjectTypeInitializer.DeleteProcedure = CmiObjectDelete;
372 ObjectTypeInitializer.ParseProcedure = CmiObjectParse;
373 ObjectTypeInitializer.SecurityProcedure = CmiObjectSecurity;
374 ObjectTypeInitializer.QueryNameProcedure = CmiObjectQueryName;
375
376 ObpCreateTypeObject(&ObjectTypeInitializer, &Name, &CmiKeyType);
377
378 /* Initialize the hive list */
379 InitializeListHead(&CmiHiveListHead);
380
381 /* Initialize registry lock */
382 ExInitializeResourceLite(&CmiRegistryLock);
383
384 /* Initialize the key object list */
385 InitializeListHead(&CmiKeyObjectListHead);
386
387 /* Initialize the worker timer */
388 KeInitializeTimerEx(&CmiWorkerTimer, SynchronizationTimer);
389
390 /* Initialize the worker thread */
391 Status = PsCreateSystemThread(&ThreadHandle,
392 THREAD_ALL_ACCESS,
393 NULL,
394 NULL,
395 &ThreadId,
396 CmiWorkerThread,
397 NULL);
398 if (!NT_SUCCESS(Status))
399 {
400 KEBUGCHECK(0);
401 }
402
403 /* Start the timer */
404 DueTime.QuadPart = -1;
405 KeSetTimerEx(&CmiWorkerTimer, DueTime, 5000, NULL); /* 5sec */
406
407 /* Build volatile registry store */
408 Status = CmiCreateVolatileHive (&CmiVolatileHive);
409 ASSERT(NT_SUCCESS(Status));
410
411 InitializeListHead(&CmiCallbackHead);
412 ExInitializeFastMutex(&CmiCallbackLock);
413
414 /* Create '\Registry' key. */
415 RtlInitUnicodeString(&KeyName, REG_ROOT_KEY_NAME);
416 InitializeObjectAttributes(&ObjectAttributes, &KeyName, 0, NULL, NULL);
417 Status = ObCreateObject(KernelMode,
418 CmiKeyType,
419 &ObjectAttributes,
420 KernelMode,
421 NULL,
422 sizeof(KEY_OBJECT),
423 0,
424 0,
425 (PVOID *) &RootKey);
426 ASSERT(NT_SUCCESS(Status));
427 Status = ObInsertObject(RootKey,
428 NULL,
429 KEY_ALL_ACCESS,
430 0,
431 NULL,
432 &RootKeyHandle);
433 ASSERT(NT_SUCCESS(Status));
434 RootKey->RegistryHive = CmiVolatileHive;
435 RootKey->KeyCellOffset = CmiVolatileHive->HiveHeader->RootKeyOffset;
436 RootKey->KeyCell = CmiGetCell (CmiVolatileHive, RootKey->KeyCellOffset, NULL);
437 RootKey->ParentKey = RootKey;
438 RootKey->Flags = 0;
439 RootKey->NumberOfSubKeys = 0;
440 RootKey->SubKeys = NULL;
441 RootKey->SizeOfSubKeys = 0;
442 InsertTailList(&CmiKeyObjectListHead, &RootKey->ListEntry);
443 Status = RtlpCreateUnicodeString(&RootKey->Name, L"Registry", NonPagedPool);
444 ASSERT(NT_SUCCESS(Status));
445
446 #if 0
447 Status = CmiAllocateCell(CmiVolatileHive,
448 0x10, //LONG CellSize,
449 (PVOID *)&RootSecurityCell,
450 &RootKey->KeyCell->SecurityKeyOffset);
451 ASSERT(NT_SUCCESS(Status));
452
453 /* Copy the security descriptor */
454
455 CmiVolatileHive->RootSecurityCell = RootSecurityCell;
456 #endif
457
458
459 /* Create '\Registry\Machine' key. */
460 RtlInitUnicodeString(&KeyName,
461 L"Machine");
462 InitializeObjectAttributes(&ObjectAttributes,
463 &KeyName,
464 0,
465 RootKeyHandle,
466 NULL);
467 Status = ZwCreateKey(&KeyHandle,
468 KEY_ALL_ACCESS,
469 &ObjectAttributes,
470 0,
471 NULL,
472 REG_OPTION_VOLATILE,
473 NULL);
474 ASSERT(NT_SUCCESS(Status));
475
476 /* Create '\Registry\User' key. */
477 RtlInitUnicodeString(&KeyName,
478 L"User");
479 InitializeObjectAttributes(&ObjectAttributes,
480 &KeyName,
481 0,
482 RootKeyHandle,
483 NULL);
484 Status = ZwCreateKey(&KeyHandle,
485 KEY_ALL_ACCESS,
486 &ObjectAttributes,
487 0,
488 NULL,
489 REG_OPTION_VOLATILE,
490 NULL);
491 ASSERT(NT_SUCCESS(Status));
492 }
493
494
495 VOID INIT_FUNCTION
496 CmInit2(PCHAR CommandLine)
497 {
498 ULONG PiceStart = 4;
499 BOOLEAN MiniNT = FALSE;
500 PWCHAR SystemBootDevice;
501 PWCHAR SystemStartOptions;
502 ULONG Position;
503 NTSTATUS Status;
504
505 /* Create the 'CurrentControlSet' link. */
506 Status = CmiCreateCurrentControlSetLink();
507 if (!NT_SUCCESS(Status))
508 KEBUGCHECK(CONFIG_INITIALIZATION_FAILED);
509
510 /*
511 * Parse the system boot device.
512 */
513 Position = 0;
514 SystemBootDevice = ExAllocatePool(PagedPool,
515 (strlen(CommandLine) + 1) * sizeof(WCHAR));
516 if (SystemBootDevice == NULL)
517 {
518 KEBUGCHECK(CONFIG_INITIALIZATION_FAILED);
519 }
520
521 while (*CommandLine != 0 && *CommandLine != ' ')
522 SystemBootDevice[Position++] = *(CommandLine++);
523 SystemBootDevice[Position++] = 0;
524
525 /*
526 * Write the system boot device to registry.
527 */
528 Status = RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
529 L"\\Registry\\Machine\\System\\CurrentControlSet\\Control",
530 L"SystemBootDevice",
531 REG_SZ,
532 SystemBootDevice,
533 Position * sizeof(WCHAR));
534 if (!NT_SUCCESS(Status))
535 {
536 KEBUGCHECK(CONFIG_INITIALIZATION_FAILED);
537 }
538
539 /*
540 * Parse the system start options.
541 */
542 Position = 0;
543 SystemStartOptions = SystemBootDevice;
544 while ((CommandLine = strchr(CommandLine, '/')) != NULL)
545 {
546 /* Skip over the slash */
547 CommandLine++;
548
549 /* Special options */
550 if (!_strnicmp(CommandLine, "MININT", 6))
551 MiniNT = TRUE;
552 else if (!_strnicmp(CommandLine, "DEBUGPORT=PICE", 14))
553 PiceStart = 1;
554
555 /* Add a space between the options */
556 if (Position != 0)
557 SystemStartOptions[Position++] = L' ';
558
559 /* Copy the command */
560 while (*CommandLine != 0 && *CommandLine != ' ')
561 SystemStartOptions[Position++] = *(CommandLine++);
562 }
563 SystemStartOptions[Position++] = 0;
564
565 /*
566 * Write the system start options to registry.
567 */
568 Status = RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
569 L"\\Registry\\Machine\\System\\CurrentControlSet\\Control",
570 L"SystemStartOptions",
571 REG_SZ,
572 SystemStartOptions,
573 Position * sizeof(WCHAR));
574 if (!NT_SUCCESS(Status))
575 {
576 KEBUGCHECK(CONFIG_INITIALIZATION_FAILED);
577 }
578
579 /*
580 * Create a CurrentControlSet\Control\MiniNT key that is used
581 * to detect WinPE/MiniNT systems.
582 */
583 if (MiniNT)
584 {
585 Status = RtlCreateRegistryKey(RTL_REGISTRY_CONTROL, L"MiniNT");
586 if (!NT_SUCCESS(Status))
587 KEBUGCHECK(CONFIG_INITIALIZATION_FAILED);
588 }
589
590 /* Set PICE 'Start' value to 1, if PICE debugging is enabled */
591 Status = RtlWriteRegistryValue(
592 RTL_REGISTRY_SERVICES,
593 L"\\Pice",
594 L"Start",
595 REG_DWORD,
596 &PiceStart,
597 sizeof(ULONG));
598 if (!NT_SUCCESS(Status))
599 KEBUGCHECK(CONFIG_INITIALIZATION_FAILED);
600
601 ExFreePool(SystemBootDevice);
602 }
603
604
605 static NTSTATUS
606 CmiCreateCurrentControlSetLink(VOID)
607 {
608 RTL_QUERY_REGISTRY_TABLE QueryTable[5];
609 WCHAR TargetNameBuffer[80];
610 ULONG TargetNameLength;
611 UNICODE_STRING LinkName = RTL_CONSTANT_STRING(
612 L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet");
613 UNICODE_STRING LinkValue = RTL_CONSTANT_STRING(L"SymbolicLinkValue");
614 ULONG CurrentSet;
615 ULONG DefaultSet;
616 ULONG Failed;
617 ULONG LastKnownGood;
618 NTSTATUS Status;
619 OBJECT_ATTRIBUTES ObjectAttributes;
620 HANDLE KeyHandle;
621
622 DPRINT("CmiCreateCurrentControlSetLink() called\n");
623
624 RtlZeroMemory(&QueryTable, sizeof(QueryTable));
625
626 QueryTable[0].Name = L"Current";
627 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
628 QueryTable[0].EntryContext = &CurrentSet;
629
630 QueryTable[1].Name = L"Default";
631 QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
632 QueryTable[1].EntryContext = &DefaultSet;
633
634 QueryTable[2].Name = L"Failed";
635 QueryTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT;
636 QueryTable[2].EntryContext = &Failed;
637
638 QueryTable[3].Name = L"LastKnownGood";
639 QueryTable[3].Flags = RTL_QUERY_REGISTRY_DIRECT;
640 QueryTable[3].EntryContext = &LastKnownGood;
641
642 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
643 L"\\Registry\\Machine\\SYSTEM\\Select",
644 QueryTable,
645 NULL,
646 NULL);
647 if (!NT_SUCCESS(Status))
648 {
649 return(Status);
650 }
651
652 DPRINT("Current %ld Default %ld\n", CurrentSet, DefaultSet);
653
654 swprintf(TargetNameBuffer,
655 L"\\Registry\\Machine\\SYSTEM\\ControlSet%03lu",
656 CurrentSet);
657 TargetNameLength = wcslen(TargetNameBuffer) * sizeof(WCHAR);
658
659 DPRINT("Link target '%S'\n", TargetNameBuffer);
660
661 InitializeObjectAttributes(&ObjectAttributes,
662 &LinkName,
663 OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_OPENLINK,
664 NULL,
665 NULL);
666 Status = ZwCreateKey(&KeyHandle,
667 KEY_ALL_ACCESS | KEY_CREATE_LINK,
668 &ObjectAttributes,
669 0,
670 NULL,
671 REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK,
672 NULL);
673 if (!NT_SUCCESS(Status))
674 {
675 DPRINT1("ZwCreateKey() failed (Status %lx)\n", Status);
676 return(Status);
677 }
678
679 Status = ZwSetValueKey(KeyHandle,
680 &LinkValue,
681 0,
682 REG_LINK,
683 (PVOID)TargetNameBuffer,
684 TargetNameLength);
685 if (!NT_SUCCESS(Status))
686 {
687 DPRINT1("ZwSetValueKey() failed (Status %lx)\n", Status);
688 }
689
690 ZwClose(KeyHandle);
691
692 return Status;
693 }
694
695
696 NTSTATUS
697 CmiConnectHive(IN POBJECT_ATTRIBUTES KeyObjectAttributes,
698 IN PREGISTRY_HIVE RegistryHive)
699 {
700 UNICODE_STRING RemainingPath;
701 PKEY_OBJECT ParentKey;
702 PKEY_OBJECT NewKey;
703 NTSTATUS Status;
704 PWSTR SubName;
705 UNICODE_STRING ObjectName;
706 OBJECT_CREATE_INFORMATION ObjectCreateInfo;
707
708 DPRINT("CmiConnectHive(%p, %p) called.\n",
709 KeyObjectAttributes, RegistryHive);
710
711 /* Capture all the info */
712 DPRINT("Capturing Create Info\n");
713 Status = ObpCaptureObjectAttributes(KeyObjectAttributes,
714 KernelMode,
715 CmiKeyType,
716 &ObjectCreateInfo,
717 &ObjectName);
718 if (!NT_SUCCESS(Status))
719 {
720 DPRINT("ObpCaptureObjectAttributes() failed (Status %lx)\n", Status);
721 return Status;
722 }
723
724 Status = CmFindObject(&ObjectCreateInfo,
725 &ObjectName,
726 (PVOID*)&ParentKey,
727 &RemainingPath,
728 CmiKeyType,
729 NULL,
730 NULL);
731 ObpReleaseCapturedAttributes(&ObjectCreateInfo);
732 if (ObjectName.Buffer) ExFreePool(ObjectName.Buffer);
733 if (!NT_SUCCESS(Status))
734 {
735 return Status;
736 }
737
738 DPRINT ("RemainingPath %wZ\n", &RemainingPath);
739
740 if ((RemainingPath.Buffer == NULL) || (RemainingPath.Buffer[0] == 0))
741 {
742 ObDereferenceObject (ParentKey);
743 RtlFreeUnicodeString(&RemainingPath);
744 return STATUS_OBJECT_NAME_COLLISION;
745 }
746
747 /* Ignore leading backslash */
748 SubName = RemainingPath.Buffer;
749 if (*SubName == L'\\')
750 SubName++;
751
752 /* If RemainingPath contains \ we must return error
753 because CmiConnectHive() can not create trees */
754 if (wcschr (SubName, L'\\') != NULL)
755 {
756 ObDereferenceObject (ParentKey);
757 RtlFreeUnicodeString(&RemainingPath);
758 return STATUS_OBJECT_NAME_NOT_FOUND;
759 }
760
761 DPRINT("RemainingPath %wZ ParentKey %p\n",
762 &RemainingPath, ParentKey);
763
764 Status = ObCreateObject(KernelMode,
765 CmiKeyType,
766 NULL,
767 KernelMode,
768 NULL,
769 sizeof(KEY_OBJECT),
770 0,
771 0,
772 (PVOID*)&NewKey);
773
774 if (!NT_SUCCESS(Status))
775 {
776 DPRINT1 ("ObCreateObject() failed (Status %lx)\n", Status);
777 ObDereferenceObject (ParentKey);
778 RtlFreeUnicodeString(&RemainingPath);
779 return Status;
780 }
781 DPRINT("Inserting Key into Object Tree\n");
782 Status = ObInsertObject((PVOID)NewKey,
783 NULL,
784 KEY_ALL_ACCESS,
785 0,
786 NULL,
787 NULL);
788 DPRINT("Status %x\n", Status);
789 NewKey->RegistryHive = RegistryHive;
790 NewKey->KeyCellOffset = RegistryHive->HiveHeader->RootKeyOffset;
791 NewKey->KeyCell = CmiGetCell (RegistryHive, NewKey->KeyCellOffset, NULL);
792 NewKey->Flags = 0;
793 NewKey->NumberOfSubKeys = 0;
794 InsertTailList(&CmiKeyObjectListHead, &NewKey->ListEntry);
795 if (NewKey->KeyCell->NumberOfSubKeys != 0)
796 {
797 NewKey->SubKeys = ExAllocatePool(NonPagedPool,
798 NewKey->KeyCell->NumberOfSubKeys * sizeof(ULONG));
799 if (NewKey->SubKeys == NULL)
800 {
801 DPRINT("ExAllocatePool() failed\n");
802 ObDereferenceObject (NewKey);
803 ObDereferenceObject (ParentKey);
804 RtlFreeUnicodeString(&RemainingPath);
805 return STATUS_INSUFFICIENT_RESOURCES;
806 }
807 }
808 else
809 {
810 NewKey->SubKeys = NULL;
811 }
812
813 DPRINT ("SubName %S\n", SubName);
814
815 Status = RtlpCreateUnicodeString(&NewKey->Name,
816 SubName, NonPagedPool);
817 RtlFreeUnicodeString(&RemainingPath);
818 if (!NT_SUCCESS(Status))
819 {
820 DPRINT1("RtlpCreateUnicodeString() failed (Status %lx)\n", Status);
821 if (NewKey->SubKeys != NULL)
822 {
823 ExFreePool (NewKey->SubKeys);
824 }
825 ObDereferenceObject (NewKey);
826 ObDereferenceObject (ParentKey);
827 return STATUS_INSUFFICIENT_RESOURCES;
828 }
829
830 CmiAddKeyToList (ParentKey, NewKey);
831 ObDereferenceObject (ParentKey);
832
833 VERIFY_KEY_OBJECT(NewKey);
834
835 /* Note: Do not dereference NewKey here! */
836
837 return STATUS_SUCCESS;
838 }
839
840
841 NTSTATUS
842 CmiDisconnectHive (IN POBJECT_ATTRIBUTES KeyObjectAttributes,
843 OUT PREGISTRY_HIVE *RegistryHive)
844 {
845 PKEY_OBJECT KeyObject;
846 PREGISTRY_HIVE Hive;
847 HANDLE KeyHandle;
848 NTSTATUS Status;
849 PLIST_ENTRY CurrentEntry;
850 PKEY_OBJECT CurrentKey;
851
852 DPRINT("CmiDisconnectHive() called\n");
853
854 *RegistryHive = NULL;
855
856 Status = ObOpenObjectByName (KeyObjectAttributes,
857 CmiKeyType,
858 KernelMode,
859 NULL,
860 STANDARD_RIGHTS_REQUIRED,
861 NULL,
862 &KeyHandle);
863 if (!NT_SUCCESS(Status))
864 {
865 DPRINT1 ("ObOpenObjectByName() failed (Status %lx)\n", Status);
866 return Status;
867 }
868
869 Status = ObReferenceObjectByHandle (KeyHandle,
870 STANDARD_RIGHTS_REQUIRED,
871 CmiKeyType,
872 KernelMode,
873 (PVOID*)&KeyObject,
874 NULL);
875 ZwClose (KeyHandle);
876 if (!NT_SUCCESS(Status))
877 {
878 DPRINT1 ("ObReferenceObjectByName() failed (Status %lx)\n", Status);
879 return Status;
880 }
881 DPRINT ("KeyObject %p Hive %p\n", KeyObject, KeyObject->RegistryHive);
882
883 if (!(KeyObject->KeyCell->Flags & REG_KEY_ROOT_CELL))
884 {
885 DPRINT1 ("Key is not the Hive-Root-Key\n");
886 ObDereferenceObject (KeyObject);
887 return STATUS_INVALID_PARAMETER;
888 }
889
890 /* Acquire registry lock exclusively */
891 KeEnterCriticalRegion();
892 ExAcquireResourceExclusiveLite(&CmiRegistryLock, TRUE);
893
894 CurrentEntry = CmiKeyObjectListHead.Flink;
895 while (CurrentEntry != &CmiKeyObjectListHead)
896 {
897 CurrentKey = CONTAINING_RECORD(CurrentEntry, KEY_OBJECT, ListEntry);
898 if (1 == ObGetObjectPointerCount(CurrentKey) &&
899 !(CurrentKey->Flags & KO_MARKED_FOR_DELETE))
900 {
901 ObDereferenceObject(CurrentKey);
902 CurrentEntry = CmiKeyObjectListHead.Flink;
903 }
904 else
905 {
906 CurrentEntry = CurrentEntry->Flink;
907 }
908 }
909
910 if (ObGetObjectHandleCount (KeyObject) != 0 ||
911 ObGetObjectPointerCount (KeyObject) != 2)
912 {
913 DPRINT1 ("Hive is still in use (hc %d, rc %d)\n", ObGetObjectHandleCount (KeyObject), ObGetObjectPointerCount (KeyObject));
914 ObDereferenceObject (KeyObject);
915
916 /* Release registry lock */
917 ExReleaseResourceLite (&CmiRegistryLock);
918 KeLeaveCriticalRegion();
919
920 return STATUS_UNSUCCESSFUL;
921 }
922
923 Hive = KeyObject->RegistryHive;
924
925 /* Dereference KeyObject twice to delete it */
926 ObDereferenceObject (KeyObject);
927 ObDereferenceObject (KeyObject);
928
929 *RegistryHive = Hive;
930
931 /* Release registry lock */
932 ExReleaseResourceLite (&CmiRegistryLock);
933 KeLeaveCriticalRegion();
934
935 DPRINT ("CmiDisconnectHive() done\n");
936
937 return STATUS_SUCCESS;
938 }
939
940
941 static NTSTATUS
942 CmiInitControlSetLink (VOID)
943 {
944 OBJECT_ATTRIBUTES ObjectAttributes;
945 UNICODE_STRING ControlSetKeyName = RTL_CONSTANT_STRING(
946 L"\\Registry\\Machine\\SYSTEM\\ControlSet001");
947 UNICODE_STRING ControlSetLinkName = RTL_CONSTANT_STRING(
948 L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet");
949 UNICODE_STRING ControlSetValueName = RTL_CONSTANT_STRING(L"SymbolicLinkValue");
950 HANDLE KeyHandle;
951 NTSTATUS Status;
952
953 /* Create 'ControlSet001' key */
954 InitializeObjectAttributes (&ObjectAttributes,
955 &ControlSetKeyName,
956 OBJ_CASE_INSENSITIVE,
957 NULL,
958 NULL);
959 Status = ZwCreateKey (&KeyHandle,
960 KEY_ALL_ACCESS,
961 &ObjectAttributes,
962 0,
963 NULL,
964 REG_OPTION_NON_VOLATILE,
965 NULL);
966 if (!NT_SUCCESS(Status))
967 {
968 DPRINT1 ("ZwCreateKey() failed (Status %lx)\n", Status);
969 return Status;
970 }
971 ZwClose (KeyHandle);
972
973 /* Link 'CurrentControlSet' to 'ControlSet001' key */
974 InitializeObjectAttributes (&ObjectAttributes,
975 &ControlSetLinkName,
976 OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_OPENLINK,
977 NULL,
978 NULL);
979 Status = ZwCreateKey (&KeyHandle,
980 KEY_ALL_ACCESS | KEY_CREATE_LINK,
981 &ObjectAttributes,
982 0,
983 NULL,
984 REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK,
985 NULL);
986 if (!NT_SUCCESS(Status))
987 {
988 DPRINT1 ("ZwCreateKey() failed (Status %lx)\n", Status);
989 return Status;
990 }
991
992 Status = ZwSetValueKey (KeyHandle,
993 &ControlSetValueName,
994 0,
995 REG_LINK,
996 (PVOID)ControlSetKeyName.Buffer,
997 ControlSetKeyName.Length);
998 if (!NT_SUCCESS(Status))
999 {
1000 DPRINT1 ("ZwSetValueKey() failed (Status %lx)\n", Status);
1001 }
1002 ZwClose (KeyHandle);
1003
1004 return STATUS_SUCCESS;
1005 }
1006
1007
1008 NTSTATUS
1009 CmiInitHives(BOOLEAN SetupBoot)
1010 {
1011 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
1012 OBJECT_ATTRIBUTES ObjectAttributes;
1013 UNICODE_STRING FileName;
1014 UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\HARDWARE");
1015 UNICODE_STRING ValueName = RTL_CONSTANT_STRING(L"InstallPath");
1016 HANDLE KeyHandle;
1017
1018 NTSTATUS Status;
1019
1020 WCHAR ConfigPath[MAX_PATH];
1021
1022 ULONG BufferSize;
1023 ULONG ResultSize;
1024 PWSTR EndPtr;
1025
1026
1027 DPRINT("CmiInitHives() called\n");
1028
1029 if (SetupBoot == TRUE)
1030 {
1031 InitializeObjectAttributes(&ObjectAttributes,
1032 &KeyName,
1033 OBJ_CASE_INSENSITIVE,
1034 NULL,
1035 NULL);
1036 Status = ZwOpenKey(&KeyHandle,
1037 KEY_ALL_ACCESS,
1038 &ObjectAttributes);
1039 if (!NT_SUCCESS(Status))
1040 {
1041 DPRINT1("ZwOpenKey() failed (Status %lx)\n", Status);
1042 return(Status);
1043 }
1044
1045 BufferSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 4096;
1046 ValueInfo = ExAllocatePool(PagedPool,
1047 BufferSize);
1048 if (ValueInfo == NULL)
1049 {
1050 ZwClose(KeyHandle);
1051 return(STATUS_INSUFFICIENT_RESOURCES);
1052 }
1053
1054 Status = ZwQueryValueKey(KeyHandle,
1055 &ValueName,
1056 KeyValuePartialInformation,
1057 ValueInfo,
1058 BufferSize,
1059 &ResultSize);
1060 ZwClose(KeyHandle);
1061 if (!NT_SUCCESS(Status))
1062 {
1063 ExFreePool(ValueInfo);
1064 return(Status);
1065 }
1066
1067 RtlCopyMemory(ConfigPath,
1068 ValueInfo->Data,
1069 ValueInfo->DataLength);
1070 ConfigPath[ValueInfo->DataLength / sizeof(WCHAR)] = (WCHAR)0;
1071 ExFreePool(ValueInfo);
1072 }
1073 else
1074 {
1075 wcscpy(ConfigPath, L"\\SystemRoot");
1076 }
1077 wcscat(ConfigPath, L"\\system32\\config");
1078
1079 DPRINT("ConfigPath: %S\n", ConfigPath);
1080
1081 EndPtr = ConfigPath + wcslen(ConfigPath);
1082
1083 CmiDoVerify = TRUE;
1084
1085 /* FIXME: Save boot log */
1086
1087 /* Connect the SYSTEM hive only if it has been created */
1088 if (SetupBoot == TRUE)
1089 {
1090 wcscpy(EndPtr, REG_SYSTEM_FILE_NAME);
1091 DPRINT ("ConfigPath: %S\n", ConfigPath);
1092
1093 RtlInitUnicodeString (&KeyName,
1094 REG_SYSTEM_KEY_NAME);
1095 InitializeObjectAttributes(&ObjectAttributes,
1096 &KeyName,
1097 OBJ_CASE_INSENSITIVE,
1098 NULL,
1099 NULL);
1100
1101 RtlInitUnicodeString (&FileName,
1102 ConfigPath);
1103 Status = CmiLoadHive (&ObjectAttributes,
1104 &FileName,
1105 0);
1106 if (!NT_SUCCESS(Status))
1107 {
1108 DPRINT1 ("CmiLoadHive() failed (Status %lx)\n", Status);
1109 return Status;
1110 }
1111
1112 Status = CmiInitControlSetLink ();
1113 if (!NT_SUCCESS(Status))
1114 {
1115 DPRINT1("CmiInitControlSetLink() failed (Status %lx)\n", Status);
1116 return(Status);
1117 }
1118 }
1119
1120 /* Connect the SOFTWARE hive */
1121 wcscpy(EndPtr, REG_SOFTWARE_FILE_NAME);
1122 RtlInitUnicodeString (&FileName,
1123 ConfigPath);
1124 DPRINT ("ConfigPath: %S\n", ConfigPath);
1125
1126 RtlInitUnicodeString (&KeyName,
1127 REG_SOFTWARE_KEY_NAME);
1128 InitializeObjectAttributes(&ObjectAttributes,
1129 &KeyName,
1130 OBJ_CASE_INSENSITIVE,
1131 NULL,
1132 NULL);
1133
1134 Status = CmiLoadHive (&ObjectAttributes,
1135 &FileName,
1136 0);
1137 if (!NT_SUCCESS(Status))
1138 {
1139 DPRINT1("CmiInitializeHive() failed (Status %lx)\n", Status);
1140 return(Status);
1141 }
1142
1143 /* Connect the SAM hive */
1144 wcscpy(EndPtr, REG_SAM_FILE_NAME);
1145 RtlInitUnicodeString (&FileName,
1146 ConfigPath);
1147 DPRINT ("ConfigPath: %S\n", ConfigPath);
1148
1149 RtlInitUnicodeString (&KeyName,
1150 REG_SAM_KEY_NAME);
1151 InitializeObjectAttributes(&ObjectAttributes,
1152 &KeyName,
1153 OBJ_CASE_INSENSITIVE,
1154 NULL,
1155 NULL);
1156 Status = CmiLoadHive (&ObjectAttributes,
1157 &FileName,
1158 0);
1159 if (!NT_SUCCESS(Status))
1160 {
1161 DPRINT1("CmiInitializeHive() failed (Status %lx)\n", Status);
1162 return(Status);
1163 }
1164
1165 /* Connect the SECURITY hive */
1166 wcscpy(EndPtr, REG_SEC_FILE_NAME);
1167 RtlInitUnicodeString (&FileName,
1168 ConfigPath);
1169 DPRINT ("ConfigPath: %S\n", ConfigPath);
1170
1171 RtlInitUnicodeString (&KeyName,
1172 REG_SEC_KEY_NAME);
1173 InitializeObjectAttributes(&ObjectAttributes,
1174 &KeyName,
1175 OBJ_CASE_INSENSITIVE,
1176 NULL,
1177 NULL);
1178 Status = CmiLoadHive (&ObjectAttributes,
1179 &FileName,
1180 0);
1181 if (!NT_SUCCESS(Status))
1182 {
1183 DPRINT1("CmiInitializeHive() failed (Status %lx)\n", Status);
1184 return(Status);
1185 }
1186
1187 /* Connect the DEFAULT hive */
1188 wcscpy(EndPtr, REG_DEFAULT_USER_FILE_NAME);
1189 RtlInitUnicodeString (&FileName,
1190 ConfigPath);
1191 DPRINT ("ConfigPath: %S\n", ConfigPath);
1192
1193 RtlInitUnicodeString (&KeyName,
1194 REG_DEFAULT_USER_KEY_NAME);
1195 InitializeObjectAttributes(&ObjectAttributes,
1196 &KeyName,
1197 OBJ_CASE_INSENSITIVE,
1198 NULL,
1199 NULL);
1200 Status = CmiLoadHive (&ObjectAttributes,
1201 &FileName,
1202 0);
1203 if (!NT_SUCCESS(Status))
1204 {
1205 DPRINT1("CmiInitializeHive() failed (Status %lx)\n", Status);
1206 return(Status);
1207 }
1208
1209 // CmiCheckRegistry(TRUE);
1210
1211 /* Start automatic hive synchronization */
1212 KeInitializeDpc(&CmiHiveSyncDpc,
1213 CmiHiveSyncDpcRoutine,
1214 NULL);
1215 KeInitializeTimer(&CmiHiveSyncTimer);
1216 CmiHiveSyncEnabled = TRUE;
1217
1218 DPRINT("CmiInitHives() done\n");
1219
1220 return(STATUS_SUCCESS);
1221 }
1222
1223
1224 VOID
1225 CmShutdownRegistry(VOID)
1226 {
1227 PREGISTRY_HIVE Hive;
1228 PLIST_ENTRY Entry;
1229
1230 DPRINT("CmShutdownRegistry() called\n");
1231
1232 /* Stop automatic hive synchronization */
1233 CmiHiveSyncEnabled = FALSE;
1234
1235 /* Cancel pending hive synchronization */
1236 if (CmiHiveSyncPending == TRUE)
1237 {
1238 KeCancelTimer(&CmiHiveSyncTimer);
1239 CmiHiveSyncPending = FALSE;
1240 }
1241
1242 /* Acquire hive list lock exclusively */
1243 KeEnterCriticalRegion();
1244 ExAcquireResourceExclusiveLite(&CmiRegistryLock, TRUE);
1245
1246 Entry = CmiHiveListHead.Flink;
1247 while (Entry != &CmiHiveListHead)
1248 {
1249 Hive = CONTAINING_RECORD(Entry, REGISTRY_HIVE, HiveList);
1250
1251 if (!(IsNoFileHive(Hive) || IsNoSynchHive(Hive)))
1252 {
1253 /* Flush non-volatile hive */
1254 CmiFlushRegistryHive(Hive);
1255 }
1256
1257 Entry = Entry->Flink;
1258 }
1259
1260 /* Release hive list lock */
1261 ExReleaseResourceLite(&CmiRegistryLock);
1262 KeLeaveCriticalRegion();
1263
1264 DPRINT("CmShutdownRegistry() done\n");
1265 }
1266
1267
1268 VOID STDCALL
1269 CmiHiveSyncRoutine(PVOID DeferredContext)
1270 {
1271 PREGISTRY_HIVE Hive;
1272 PLIST_ENTRY Entry;
1273
1274 DPRINT("CmiHiveSyncRoutine() called\n");
1275
1276 CmiHiveSyncPending = FALSE;
1277
1278 /* Acquire hive list lock exclusively */
1279 KeEnterCriticalRegion();
1280 ExAcquireResourceExclusiveLite(&CmiRegistryLock, TRUE);
1281
1282 Entry = CmiHiveListHead.Flink;
1283 while (Entry != &CmiHiveListHead)
1284 {
1285 Hive = CONTAINING_RECORD(Entry, REGISTRY_HIVE, HiveList);
1286
1287 if (!(IsNoFileHive(Hive) || IsNoSynchHive(Hive)))
1288 {
1289 /* Flush non-volatile hive */
1290 CmiFlushRegistryHive(Hive);
1291 }
1292
1293 Entry = Entry->Flink;
1294 }
1295
1296 /* Release hive list lock */
1297 ExReleaseResourceLite(&CmiRegistryLock);
1298 KeLeaveCriticalRegion();
1299
1300 DPRINT("DeferredContext 0x%p\n", DeferredContext);
1301 ExFreePool(DeferredContext);
1302
1303 DPRINT("CmiHiveSyncRoutine() done\n");
1304 }
1305
1306
1307 static VOID STDCALL
1308 CmiHiveSyncDpcRoutine(PKDPC Dpc,
1309 PVOID DeferredContext,
1310 PVOID SystemArgument1,
1311 PVOID SystemArgument2)
1312 {
1313 PWORK_QUEUE_ITEM WorkQueueItem;
1314
1315 WorkQueueItem = ExAllocatePool(NonPagedPool,
1316 sizeof(WORK_QUEUE_ITEM));
1317 if (WorkQueueItem == NULL)
1318 {
1319 DbgPrint("Failed to allocate work item\n");
1320 return;
1321 }
1322
1323 ExInitializeWorkItem(WorkQueueItem,
1324 CmiHiveSyncRoutine,
1325 WorkQueueItem);
1326
1327 DPRINT("DeferredContext 0x%p\n", WorkQueueItem);
1328 ExQueueWorkItem(WorkQueueItem,
1329 CriticalWorkQueue);
1330 }
1331
1332
1333 VOID
1334 CmiSyncHives(VOID)
1335 {
1336 LARGE_INTEGER Timeout;
1337
1338 DPRINT("CmiSyncHives() called\n");
1339
1340 if (CmiHiveSyncEnabled == FALSE ||
1341 CmiHiveSyncPending == TRUE)
1342 return;
1343
1344 CmiHiveSyncPending = TRUE;
1345
1346 Timeout.QuadPart = (LONGLONG)-50000000;
1347 KeSetTimer(&CmiHiveSyncTimer,
1348 Timeout,
1349 &CmiHiveSyncDpc);
1350 }
1351
1352 /* EOF */