xref: /reactos/ntoskrnl/config/cmsysini.c (revision f7d612f3)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4  * PURPOSE:         Configuration Manager - System Initialization Code
5  * PROGRAMMERS:     ReactOS Portable Systems Group
6  *                  Alex Ionescu (alex.ionescu@reactos.org)
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include "ntoskrnl.h"
12 #define NDEBUG
13 #include "debug.h"
14 
15 POBJECT_TYPE CmpKeyObjectType;
16 PCMHIVE CmiVolatileHive;
17 LIST_ENTRY CmpHiveListHead;
18 ERESOURCE CmpRegistryLock;
19 KGUARDED_MUTEX CmpSelfHealQueueLock;
20 LIST_ENTRY CmpSelfHealQueueListHead;
21 KEVENT CmpLoadWorkerEvent;
22 LONG CmpLoadWorkerIncrement;
23 PEPROCESS CmpSystemProcess;
24 PVOID CmpRegistryLockCallerCaller, CmpRegistryLockCaller;
25 BOOLEAN CmpFlushOnLockRelease;
26 BOOLEAN CmpSpecialBootCondition;
27 BOOLEAN CmpNoWrite;
28 BOOLEAN CmpWasSetupBoot;
29 BOOLEAN CmpProfileLoaded;
30 BOOLEAN CmpNoVolatileCreates;
31 ULONG CmpTraceLevel = 0;
32 BOOLEAN HvShutdownComplete = FALSE;
33 
34 extern LONG CmpFlushStarveWriters;
35 extern BOOLEAN CmFirstTime;
36 
37 /* FUNCTIONS ******************************************************************/
38 
39 BOOLEAN
40 NTAPI
41 CmpLinkKeyToHive(
42     _In_z_ PCWSTR LinkKeyName,
43     _In_z_ PCWSTR TargetKeyName)
44 {
45     NTSTATUS Status;
46     OBJECT_ATTRIBUTES ObjectAttributes;
47     UNICODE_STRING KeyName;
48     HANDLE LinkKeyHandle;
49     ULONG Disposition;
50 
51     PAGED_CODE();
52 
53     /* Initialize the object attributes */
54     RtlInitUnicodeString(&KeyName, LinkKeyName);
55     InitializeObjectAttributes(&ObjectAttributes,
56                                &KeyName,
57                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
58                                NULL,
59                                NULL);
60 
61     /* Create the link key */
62     Status = ZwCreateKey(&LinkKeyHandle,
63                          KEY_CREATE_LINK,
64                          &ObjectAttributes,
65                          0,
66                          NULL,
67                          REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK,
68                          &Disposition);
69     if (!NT_SUCCESS(Status))
70     {
71         DPRINT1("CM: CmpLinkKeyToHive: couldn't create %S, Status = 0x%lx\n",
72                 LinkKeyName, Status);
73         return FALSE;
74     }
75 
76     /* Check if the new key was actually created */
77     if (Disposition != REG_CREATED_NEW_KEY)
78     {
79         DPRINT1("CM: CmpLinkKeyToHive: %S already exists!\n", LinkKeyName);
80         ZwClose(LinkKeyHandle);
81         return FALSE;
82     }
83 
84     /* Set the target key name as link target */
85     RtlInitUnicodeString(&KeyName, TargetKeyName);
86     Status = ZwSetValueKey(LinkKeyHandle,
87                            &CmSymbolicLinkValueName,
88                            0,
89                            REG_LINK,
90                            KeyName.Buffer,
91                            KeyName.Length);
92 
93     /* Close the link key handle */
94     ObCloseHandle(LinkKeyHandle, KernelMode);
95 
96     if (!NT_SUCCESS(Status))
97     {
98         DPRINT1("CM: CmpLinkKeyToHive: couldn't create symbolic link for %S, Status = 0x%lx\n",
99                 TargetKeyName, Status);
100         return FALSE;
101     }
102 
103     return TRUE;
104 }
105 
106 VOID
107 NTAPI
108 CmpDeleteKeyObject(PVOID DeletedObject)
109 {
110     PCM_KEY_BODY KeyBody = (PCM_KEY_BODY)DeletedObject;
111     PCM_KEY_CONTROL_BLOCK Kcb;
112     REG_KEY_HANDLE_CLOSE_INFORMATION KeyHandleCloseInfo;
113     REG_POST_OPERATION_INFORMATION PostOperationInfo;
114     NTSTATUS Status;
115     PAGED_CODE();
116 
117     /* First off, prepare the handle close information callback */
118     PostOperationInfo.Object = KeyBody;
119     KeyHandleCloseInfo.Object = KeyBody;
120     Status = CmiCallRegisteredCallbacks(RegNtPreKeyHandleClose,
121                                         &KeyHandleCloseInfo);
122     if (!NT_SUCCESS(Status))
123     {
124         /* If we failed, notify the post routine */
125         PostOperationInfo.Status = Status;
126         CmiCallRegisteredCallbacks(RegNtPostKeyHandleClose, &PostOperationInfo);
127         return;
128     }
129 
130     /* Acquire hive lock */
131     CmpLockRegistry();
132 
133     /* Make sure this is a valid key body */
134     if (KeyBody->Type == CM_KEY_BODY_TYPE)
135     {
136         /* Get the KCB */
137         Kcb = KeyBody->KeyControlBlock;
138         if (Kcb)
139         {
140             /* Delist the key */
141             DelistKeyBodyFromKCB(KeyBody, KeyBody->KcbLocked);
142 
143             /* Dereference the KCB */
144             CmpDelayDerefKeyControlBlock(Kcb);
145         }
146     }
147 
148     /* Release the registry lock */
149     CmpUnlockRegistry();
150 
151     /* Do the post callback */
152     PostOperationInfo.Status = STATUS_SUCCESS;
153     CmiCallRegisteredCallbacks(RegNtPostKeyHandleClose, &PostOperationInfo);
154 }
155 
156 VOID
157 NTAPI
158 CmpCloseKeyObject(IN PEPROCESS Process OPTIONAL,
159                   IN PVOID Object,
160                   IN ACCESS_MASK GrantedAccess,
161                   IN ULONG ProcessHandleCount,
162                   IN ULONG SystemHandleCount)
163 {
164     PCM_KEY_BODY KeyBody = (PCM_KEY_BODY)Object;
165     PAGED_CODE();
166 
167     /* Don't do anything if we're not the last handle */
168     if (SystemHandleCount > 1) return;
169 
170     /* Make sure we're a valid key body */
171     if (KeyBody->Type == CM_KEY_BODY_TYPE)
172     {
173         /* Don't do anything if we don't have a notify block */
174         if (!KeyBody->NotifyBlock) return;
175 
176         /* This shouldn't happen yet */
177         ASSERT(FALSE);
178     }
179 }
180 
181 NTSTATUS
182 NTAPI
183 CmpQueryKeyName(IN PVOID ObjectBody,
184                 IN BOOLEAN HasName,
185                 IN OUT POBJECT_NAME_INFORMATION ObjectNameInfo,
186                 IN ULONG Length,
187                 OUT PULONG ReturnLength,
188                 IN KPROCESSOR_MODE PreviousMode)
189 {
190     PUNICODE_STRING KeyName;
191     ULONG BytesToCopy;
192     NTSTATUS Status = STATUS_SUCCESS;
193     PCM_KEY_BODY KeyBody = (PCM_KEY_BODY)ObjectBody;
194     PCM_KEY_CONTROL_BLOCK Kcb = KeyBody->KeyControlBlock;
195 
196     /* Acquire hive lock */
197     CmpLockRegistry();
198 
199     /* Lock KCB shared */
200     CmpAcquireKcbLockShared(Kcb);
201 
202     /* Check if it's a deleted block */
203     if (Kcb->Delete)
204     {
205         /* Release the locks */
206         CmpReleaseKcbLock(Kcb);
207         CmpUnlockRegistry();
208 
209         /* Let the caller know it's deleted */
210         return STATUS_KEY_DELETED;
211     }
212 
213     /* Get the name */
214     KeyName = CmpConstructName(Kcb);
215 
216     /* Release the locks */
217     CmpReleaseKcbLock(Kcb);
218     CmpUnlockRegistry();
219 
220     /* Check if we got the name */
221     if (!KeyName) return STATUS_INSUFFICIENT_RESOURCES;
222 
223     /* Set the returned length */
224     *ReturnLength = KeyName->Length + sizeof(OBJECT_NAME_INFORMATION) + sizeof(WCHAR);
225 
226     /* Calculate amount of bytes to copy into the buffer */
227     BytesToCopy = KeyName->Length + sizeof(WCHAR);
228 
229     /* Check if the provided buffer is too small to fit even anything */
230     if ((Length <= sizeof(OBJECT_NAME_INFORMATION)) ||
231         ((Length < (*ReturnLength)) && (BytesToCopy < sizeof(WCHAR))))
232     {
233         /* Free the buffer allocated by CmpConstructName */
234         ExFreePoolWithTag(KeyName, TAG_CM);
235 
236         /* Return buffer length failure without writing anything there because nothing fits */
237         return STATUS_INFO_LENGTH_MISMATCH;
238     }
239 
240     /* Check if the provided buffer can be partially written */
241     if (Length < (*ReturnLength))
242     {
243         /* Yes, indicate so in the return status */
244         Status = STATUS_INFO_LENGTH_MISMATCH;
245 
246         /* Calculate amount of bytes which the provided buffer could handle */
247         BytesToCopy = Length - sizeof(OBJECT_NAME_INFORMATION);
248     }
249 
250     /* Remove the null termination character from the size */
251     BytesToCopy -= sizeof(WCHAR);
252 
253     /* Fill in the result */
254     _SEH2_TRY
255     {
256         /* Return data to user */
257         ObjectNameInfo->Name.Buffer = (PWCHAR)(ObjectNameInfo + 1);
258         ObjectNameInfo->Name.MaximumLength = KeyName->Length;
259         ObjectNameInfo->Name.Length = KeyName->Length;
260 
261         /* Copy string content*/
262         RtlCopyMemory(ObjectNameInfo->Name.Buffer,
263                       KeyName->Buffer,
264                       BytesToCopy);
265 
266         /* Null terminate it */
267         ObjectNameInfo->Name.Buffer[BytesToCopy / sizeof(WCHAR)] = UNICODE_NULL;
268     }
269     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
270     {
271         /* Get the status */
272         Status = _SEH2_GetExceptionCode();
273     }
274     _SEH2_END;
275 
276     /* Free the buffer allocated by CmpConstructName */
277     ExFreePoolWithTag(KeyName, TAG_CM);
278 
279     /* Return status */
280     return Status;
281 }
282 
283 NTSTATUS
284 NTAPI
285 CmpInitHiveFromFile(IN PCUNICODE_STRING HiveName,
286                     IN ULONG HiveFlags,
287                     OUT PCMHIVE *Hive,
288                     IN OUT PBOOLEAN New,
289                     IN ULONG CheckFlags)
290 {
291     ULONG HiveDisposition, LogDisposition;
292     HANDLE FileHandle = NULL, LogHandle = NULL;
293     NTSTATUS Status;
294     ULONG Operation, FileType;
295     PCMHIVE NewHive;
296     PAGED_CODE();
297 
298     /* Assume failure */
299     *Hive = NULL;
300 
301     /* Open or create the hive files */
302     Status = CmpOpenHiveFiles(HiveName,
303                               L".LOG",
304                               &FileHandle,
305                               &LogHandle,
306                               &HiveDisposition,
307                               &LogDisposition,
308                               *New,
309                               FALSE,
310                               TRUE,
311                               NULL);
312     if (!NT_SUCCESS(Status)) return Status;
313 
314     /* Check if we have a log handle */
315     FileType = (LogHandle) ? HFILE_TYPE_LOG : HFILE_TYPE_PRIMARY;
316 
317     /* Check if we created or opened the hive */
318     if (HiveDisposition == FILE_CREATED)
319     {
320         /* Do a create operation */
321         Operation = HINIT_CREATE;
322         *New = TRUE;
323     }
324     else
325     {
326         /* Open it as a file */
327         Operation = HINIT_FILE;
328         *New = FALSE;
329     }
330 
331     /* Check if we're sharing hives */
332     if (CmpShareSystemHives)
333     {
334         /* Then force using the primary hive */
335         FileType = HFILE_TYPE_PRIMARY;
336         if (LogHandle)
337         {
338             /* Get rid of the log handle */
339             ZwClose(LogHandle);
340             LogHandle = NULL;
341         }
342     }
343 
344     /* Check if we're too late */
345     if (HvShutdownComplete)
346     {
347         /* Fail */
348         ZwClose(FileHandle);
349         if (LogHandle) ZwClose(LogHandle);
350         return STATUS_TOO_LATE;
351     }
352 
353     /* Initialize the hive */
354     Status = CmpInitializeHive(&NewHive,
355                                Operation,
356                                HiveFlags,
357                                FileType,
358                                NULL,
359                                FileHandle,
360                                LogHandle,
361                                NULL,
362                                NULL,
363                                HiveName,
364                                CheckFlags);
365     if (!NT_SUCCESS(Status))
366     {
367         /* Fail */
368         ZwClose(FileHandle);
369         if (LogHandle) ZwClose(LogHandle);
370         return Status;
371     }
372 
373     /* Success, return hive */
374     *Hive = NewHive;
375 
376     /* Duplicate the hive name */
377     NewHive->FileFullPath.Buffer = ExAllocatePoolWithTag(PagedPool,
378                                                          HiveName->Length,
379                                                          TAG_CM);
380     if (NewHive->FileFullPath.Buffer)
381     {
382         /* Copy the string */
383         RtlCopyMemory(NewHive->FileFullPath.Buffer,
384                       HiveName->Buffer,
385                       HiveName->Length);
386         NewHive->FileFullPath.Length = HiveName->Length;
387         NewHive->FileFullPath.MaximumLength = HiveName->Length;
388     }
389 
390     /* Return success */
391     return STATUS_SUCCESS;
392 }
393 
394 CODE_SEG("INIT")
395 NTSTATUS
396 NTAPI
397 CmpSetSystemValues(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
398 {
399     NTSTATUS Status;
400     OBJECT_ATTRIBUTES ObjectAttributes;
401     HANDLE KeyHandle;
402     UNICODE_STRING KeyName, ValueName = { 0, 0, NULL };
403 
404     ASSERT(LoaderBlock != NULL);
405 
406     /* Setup attributes for loader options */
407     RtlInitUnicodeString(&KeyName,
408                          L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\"
409                          L"Control");
410     InitializeObjectAttributes(&ObjectAttributes,
411                                &KeyName,
412                                OBJ_CASE_INSENSITIVE,
413                                NULL,
414                                NULL);
415     Status = NtOpenKey(&KeyHandle, KEY_WRITE, &ObjectAttributes);
416     if (!NT_SUCCESS(Status))
417         return Status;
418 
419     /* Setup the value for the system start options */
420     RtlInitUnicodeString(&KeyName, L"SystemStartOptions");
421     Status = NtSetValueKey(KeyHandle,
422                            &KeyName,
423                            0,
424                            REG_SZ,
425                            CmpLoadOptions.Buffer,
426                            CmpLoadOptions.Length);
427     if (!NT_SUCCESS(Status))
428         goto Quit;
429 
430     /* Setup the value for the system boot device in ARC format */
431     RtlInitUnicodeString(&KeyName, L"SystemBootDevice");
432     RtlCreateUnicodeStringFromAsciiz(&ValueName, LoaderBlock->ArcBootDeviceName);
433     Status = NtSetValueKey(KeyHandle,
434                            &KeyName,
435                            0,
436                            REG_SZ,
437                            ValueName.Buffer,
438                            ValueName.Length);
439 
440     /* Free the temporary string */
441     RtlFreeUnicodeString(&ValueName);
442 
443 Quit:
444     /* Close the key and return */
445     NtClose(KeyHandle);
446     return Status;
447 }
448 
449 static
450 CODE_SEG("INIT")
451 NTSTATUS
452 CmpCreateHardwareProfile(HANDLE ControlSetHandle)
453 {
454     OBJECT_ATTRIBUTES ObjectAttributes;
455     UNICODE_STRING KeyName;
456     HANDLE ProfilesHandle = NULL;
457     HANDLE ProfileHandle = NULL;
458     ULONG Disposition;
459     NTSTATUS Status;
460 
461     DPRINT("CmpCreateHardwareProfile()\n");
462 
463     /* Create the Hardware Profiles key */
464     RtlInitUnicodeString(&KeyName, L"Hardware Profiles");
465     InitializeObjectAttributes(&ObjectAttributes,
466                                &KeyName,
467                                OBJ_CASE_INSENSITIVE,
468                                ControlSetHandle,
469                                NULL);
470     Status = NtCreateKey(&ProfilesHandle,
471                          KEY_ALL_ACCESS,
472                          &ObjectAttributes,
473                          0,
474                          NULL,
475                          0,
476                          &Disposition);
477     if (!NT_SUCCESS(Status))
478     {
479         DPRINT1("Creating the Hardware Profile key failed\n");
480         goto done;
481     }
482 
483     /* Sanity check */
484     ASSERT(Disposition == REG_CREATED_NEW_KEY);
485 
486     /* Create the 0000 key */
487     RtlInitUnicodeString(&KeyName, L"0000");
488     InitializeObjectAttributes(&ObjectAttributes,
489                                &KeyName,
490                                OBJ_CASE_INSENSITIVE,
491                                ProfilesHandle,
492                                NULL);
493     Status = NtCreateKey(&ProfileHandle,
494                          KEY_ALL_ACCESS,
495                          &ObjectAttributes,
496                          0,
497                          NULL,
498                          0,
499                          &Disposition);
500     if (!NT_SUCCESS(Status))
501     {
502         DPRINT1("Creating the Hardware Profile\\0000 key failed\n");
503         goto done;
504     }
505 
506     /* Sanity check */
507     ASSERT(Disposition == REG_CREATED_NEW_KEY);
508 
509 done:
510     if (ProfilesHandle)
511         NtClose(ProfilesHandle);
512 
513     if (ProfileHandle)
514         NtClose(ProfileHandle);
515 
516     DPRINT("CmpCreateHardwareProfile() done\n");
517 
518     return Status;
519 }
520 
521 CODE_SEG("INIT")
522 NTSTATUS
523 NTAPI
524 CmpCreateControlSet(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
525 {
526     UNICODE_STRING ConfigName = RTL_CONSTANT_STRING(L"Control\\IDConfigDB");
527     UNICODE_STRING SelectName =
528         RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\Select");
529     UNICODE_STRING KeyName;
530     OBJECT_ATTRIBUTES ObjectAttributes;
531     CHAR ValueInfoBuffer[128];
532     PKEY_VALUE_FULL_INFORMATION ValueInfo;
533     WCHAR UnicodeBuffer[128];
534     HANDLE SelectHandle = NULL;
535     HANDLE KeyHandle = NULL;
536     HANDLE ConfigHandle = NULL;
537     HANDLE ProfileHandle = NULL;
538     HANDLE ParentHandle = NULL;
539     ULONG ControlSet, HwProfile;
540     NTSTATUS Status;
541     ULONG ResultLength, Disposition;
542     PLOADER_PARAMETER_EXTENSION LoaderExtension;
543     PAGED_CODE();
544 
545     /* ReactOS Hack: Hard-code current to 001 for SetupLdr */
546     if (LoaderBlock->RegistryBase == NULL)
547     {
548         /* Build the ControlSet001 key */
549         RtlInitUnicodeString(&KeyName,
550                              L"\\Registry\\Machine\\System\\ControlSet001");
551         InitializeObjectAttributes(&ObjectAttributes,
552                                    &KeyName,
553                                    OBJ_CASE_INSENSITIVE,
554                                    NULL,
555                                    NULL);
556         Status = NtCreateKey(&KeyHandle,
557                              KEY_ALL_ACCESS,
558                              &ObjectAttributes,
559                              0,
560                              NULL,
561                              0,
562                              &Disposition);
563         if (!NT_SUCCESS(Status))
564         {
565             DPRINT1("Failed to create ControlSet001 key: 0x%lx\n", Status);
566             goto Cleanup;
567         }
568 
569         /* Create the Hardware Profile keys */
570         Status = CmpCreateHardwareProfile(KeyHandle);
571         if (!NT_SUCCESS(Status))
572         {
573             DPRINT1("Failed to create Hardware profile keys: 0x%lx\n", Status);
574             goto Cleanup;
575         }
576 
577         /* Use hard-coded setting */
578         ControlSet = 1;
579     }
580     else
581     {
582         /* Open the select key */
583         InitializeObjectAttributes(&ObjectAttributes,
584                                    &SelectName,
585                                    OBJ_CASE_INSENSITIVE,
586                                    NULL,
587                                    NULL);
588         Status = NtOpenKey(&SelectHandle, KEY_READ, &ObjectAttributes);
589         if (!NT_SUCCESS(Status))
590         {
591             DPRINT1("Failed to open select key: 0x%lx\n", Status);
592             goto Cleanup;
593         }
594 
595         /* Open the current value */
596         RtlInitUnicodeString(&KeyName, L"Current");
597         Status = NtQueryValueKey(SelectHandle,
598                                  &KeyName,
599                                  KeyValueFullInformation,
600                                  ValueInfoBuffer,
601                                  sizeof(ValueInfoBuffer),
602                                  &ResultLength);
603         if (!NT_SUCCESS(Status))
604         {
605             DPRINT1("Failed to open the Current value: 0x%lx\n", Status);
606             goto Cleanup;
607         }
608 
609         /* Get the actual value pointer, and get the control set ID */
610         ValueInfo = (PKEY_VALUE_FULL_INFORMATION)ValueInfoBuffer;
611         ControlSet = *(PULONG)((PUCHAR)ValueInfo + ValueInfo->DataOffset);
612     }
613 
614     /* Create the current control set key */
615     RtlInitUnicodeString(&KeyName,
616                          L"\\Registry\\Machine\\System\\CurrentControlSet");
617     InitializeObjectAttributes(&ObjectAttributes,
618                                &KeyName,
619                                OBJ_CASE_INSENSITIVE,
620                                NULL,
621                                NULL);
622     Status = NtCreateKey(&KeyHandle,
623                          KEY_CREATE_LINK,
624                          &ObjectAttributes,
625                          0,
626                          NULL,
627                          REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK,
628                          &Disposition);
629     if (!NT_SUCCESS(Status))
630         goto Cleanup;
631 
632     /* Sanity check */
633     ASSERT(Disposition == REG_CREATED_NEW_KEY);
634 
635     /* Initialize the target link name */
636     Status = RtlStringCbPrintfW(UnicodeBuffer, sizeof(UnicodeBuffer),
637                                 L"\\Registry\\Machine\\System\\ControlSet%03ld",
638                                 ControlSet);
639     if (!NT_SUCCESS(Status))
640         goto Cleanup;
641 
642     RtlInitUnicodeString(&KeyName, UnicodeBuffer);
643 
644     /* Set the value */
645     Status = NtSetValueKey(KeyHandle,
646                            &CmSymbolicLinkValueName,
647                            0,
648                            REG_LINK,
649                            KeyName.Buffer,
650                            KeyName.Length);
651     if (!NT_SUCCESS(Status))
652         goto Cleanup;
653 
654     /* Get the configuration database key */
655     InitializeObjectAttributes(&ObjectAttributes,
656                                &ConfigName,
657                                OBJ_CASE_INSENSITIVE,
658                                KeyHandle,
659                                NULL);
660     Status = NtOpenKey(&ConfigHandle, KEY_READ, &ObjectAttributes);
661 
662     /* Check if we don't have one */
663     if (!NT_SUCCESS(Status))
664     {
665         /* Cleanup and exit */
666         Status = STATUS_SUCCESS;
667         goto Cleanup;
668     }
669 
670     /* ReactOS Hack: Hard-code current to 001 for SetupLdr */
671     if (LoaderBlock->RegistryBase == NULL)
672     {
673         HwProfile = 0;
674     }
675     else
676     {
677         /* Now get the current config */
678         RtlInitUnicodeString(&KeyName, L"CurrentConfig");
679         Status = NtQueryValueKey(ConfigHandle,
680                                  &KeyName,
681                                  KeyValueFullInformation,
682                                  ValueInfoBuffer,
683                                  sizeof(ValueInfoBuffer),
684                                  &ResultLength);
685 
686         /* Set pointer to buffer */
687         ValueInfo = (PKEY_VALUE_FULL_INFORMATION)ValueInfoBuffer;
688 
689         /* Check if we failed or got a non DWORD-value */
690         if (!(NT_SUCCESS(Status)) || (ValueInfo->Type != REG_DWORD))
691         {
692             Status = STATUS_SUCCESS;
693             goto Cleanup;
694         }
695 
696         /* Get the hadware profile */
697         HwProfile = *(PULONG)((PUCHAR)ValueInfo + ValueInfo->DataOffset);
698     }
699 
700     /* Open the hardware profile key */
701     RtlInitUnicodeString(&KeyName,
702                          L"\\Registry\\Machine\\System\\CurrentControlSet"
703                          L"\\Hardware Profiles");
704     InitializeObjectAttributes(&ObjectAttributes,
705                                &KeyName,
706                                OBJ_CASE_INSENSITIVE,
707                                NULL,
708                                NULL);
709     Status = NtOpenKey(&ParentHandle, KEY_READ, &ObjectAttributes);
710     if (!NT_SUCCESS(Status))
711     {
712         /* Exit and clean up */
713         Status = STATUS_SUCCESS;
714         goto Cleanup;
715     }
716 
717     /* Build the profile name */
718     RtlStringCbPrintfW(UnicodeBuffer, sizeof(UnicodeBuffer),
719                        L"%04ld", HwProfile);
720     RtlInitUnicodeString(&KeyName, UnicodeBuffer);
721 
722     /* Open the associated key */
723     InitializeObjectAttributes(&ObjectAttributes,
724                                &KeyName,
725                                OBJ_CASE_INSENSITIVE,
726                                ParentHandle,
727                                NULL);
728     Status = NtOpenKey(&ProfileHandle,
729                        KEY_READ | KEY_WRITE,
730                        &ObjectAttributes);
731     if (!NT_SUCCESS (Status))
732     {
733         /* Cleanup and exit */
734         Status = STATUS_SUCCESS;
735         goto Cleanup;
736     }
737 
738     /* Check if we have a loader block extension */
739     LoaderExtension = LoaderBlock->Extension;
740     if (LoaderExtension)
741     {
742         DPRINT("ReactOS doesn't support NTLDR Profiles yet!\n");
743     }
744 
745     /* Create the current hardware profile key */
746     RtlInitUnicodeString(&KeyName,
747                          L"\\Registry\\Machine\\System\\CurrentControlSet\\"
748                          L"Hardware Profiles\\Current");
749     InitializeObjectAttributes(&ObjectAttributes,
750                                &KeyName,
751                                OBJ_CASE_INSENSITIVE,
752                                NULL,
753                                NULL);
754     Status = NtCreateKey(&KeyHandle,
755                          KEY_CREATE_LINK,
756                          &ObjectAttributes,
757                          0,
758                          NULL,
759                          REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK,
760                          &Disposition);
761     if (NT_SUCCESS(Status))
762     {
763         /* Sanity check */
764         ASSERT(Disposition == REG_CREATED_NEW_KEY);
765 
766         /* Create the profile name */
767         RtlStringCbPrintfW(UnicodeBuffer, sizeof(UnicodeBuffer),
768                            L"\\Registry\\Machine\\System\\CurrentControlSet\\"
769                            L"Hardware Profiles\\%04ld",
770                            HwProfile);
771         RtlInitUnicodeString(&KeyName, UnicodeBuffer);
772 
773         /* Set it */
774         Status = NtSetValueKey(KeyHandle,
775                                &CmSymbolicLinkValueName,
776                                0,
777                                REG_LINK,
778                                KeyName.Buffer,
779                                KeyName.Length);
780     }
781 
782     Status = STATUS_SUCCESS;
783 
784 Cleanup:
785     /* Close every opened handle */
786     if (SelectHandle) NtClose(SelectHandle);
787     if (KeyHandle) NtClose(KeyHandle);
788     if (ConfigHandle) NtClose(ConfigHandle);
789     if (ProfileHandle) NtClose(ProfileHandle);
790     if (ParentHandle) NtClose(ParentHandle);
791 
792     DPRINT("CmpCreateControlSet() done\n");
793     return Status;
794 }
795 
796 NTSTATUS
797 NTAPI
798 CmpLinkHiveToMaster(IN PUNICODE_STRING LinkName,
799                     IN HANDLE RootDirectory,
800                     IN PCMHIVE RegistryHive,
801                     IN BOOLEAN Allocate,
802                     IN PSECURITY_DESCRIPTOR SecurityDescriptor)
803 {
804     OBJECT_ATTRIBUTES ObjectAttributes;
805     NTSTATUS Status;
806     CM_PARSE_CONTEXT ParseContext = {0};
807     HANDLE KeyHandle;
808     PCM_KEY_BODY KeyBody;
809     PAGED_CODE();
810 
811     /* Setup the object attributes */
812     InitializeObjectAttributes(&ObjectAttributes,
813                                LinkName,
814                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
815                                RootDirectory,
816                                SecurityDescriptor);
817 
818     /* Setup the parse context */
819     ParseContext.CreateLink = TRUE;
820     ParseContext.CreateOperation = TRUE;
821     ParseContext.ChildHive.KeyHive = &RegistryHive->Hive;
822 
823     /* Check if we have a root keycell or if we need to create it */
824     if (Allocate)
825     {
826         /* Create it */
827         ParseContext.ChildHive.KeyCell = HCELL_NIL;
828     }
829     else
830     {
831         /* We have one */
832         ParseContext.ChildHive.KeyCell = RegistryHive->Hive.BaseBlock->RootCell;
833     }
834 
835     /* Create the link node */
836     Status = ObOpenObjectByName(&ObjectAttributes,
837                                 CmpKeyObjectType,
838                                 KernelMode,
839                                 NULL,
840                                 KEY_READ | KEY_WRITE,
841                                 (PVOID)&ParseContext,
842                                 &KeyHandle);
843     if (!NT_SUCCESS(Status)) return Status;
844 
845     /* Mark the hive as clean */
846     RegistryHive->Hive.DirtyFlag = FALSE;
847 
848     /* ReactOS Hack: Keep alive */
849     Status = ObReferenceObjectByHandle(KeyHandle,
850                                        0,
851                                        CmpKeyObjectType,
852                                        KernelMode,
853                                        (PVOID*)&KeyBody,
854                                        NULL);
855     ASSERT(NT_SUCCESS(Status));
856 
857     /* Close the extra handle */
858     ZwClose(KeyHandle);
859     return STATUS_SUCCESS;
860 }
861 
862 CODE_SEG("INIT")
863 BOOLEAN
864 NTAPI
865 CmpInitializeSystemHive(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
866 {
867     static const UNICODE_STRING HiveName = RTL_CONSTANT_STRING(L"SYSTEM");
868     PVOID HiveBase;
869     ANSI_STRING LoadString;
870     PVOID Buffer;
871     ULONG Length;
872     NTSTATUS Status;
873     UNICODE_STRING KeyName;
874     PCMHIVE SystemHive = NULL;
875     PSECURITY_DESCRIPTOR SecurityDescriptor;
876 
877     PAGED_CODE();
878 
879     /* Setup the ansi string */
880     RtlInitAnsiString(&LoadString, LoaderBlock->LoadOptions);
881 
882     /* Allocate the unicode buffer */
883     Length = LoadString.Length * sizeof(WCHAR) + sizeof(UNICODE_NULL);
884     Buffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_CM);
885     if (!Buffer)
886     {
887         /* Fail */
888         KeBugCheckEx(BAD_SYSTEM_CONFIG_INFO, 3, 1, (ULONG_PTR)LoaderBlock, 0);
889     }
890 
891     /* Setup the unicode string */
892     RtlInitEmptyUnicodeString(&CmpLoadOptions, Buffer, (USHORT)Length);
893 
894     /* Add the load options and null-terminate */
895     Status = RtlAnsiStringToUnicodeString(&CmpLoadOptions, &LoadString, FALSE);
896     if (!NT_SUCCESS(Status))
897     {
898         return FALSE;
899     }
900 
901     CmpLoadOptions.Buffer[LoadString.Length] = UNICODE_NULL;
902     CmpLoadOptions.Length += sizeof(WCHAR);
903 
904     /* Get the System Hive base address */
905     HiveBase = LoaderBlock->RegistryBase;
906 
907     Status = CmpInitializeHive(&SystemHive,
908                                HiveBase ? HINIT_MEMORY : HINIT_CREATE,
909                                HIVE_NOLAZYFLUSH,
910                                HFILE_TYPE_ALTERNATE,
911                                HiveBase,
912                                NULL,
913                                NULL,
914                                NULL,
915                                NULL,
916                                &HiveName,
917                                HiveBase ? CM_CHECK_REGISTRY_PURGE_VOLATILES : CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES);
918     if (!NT_SUCCESS(Status))
919     {
920         return FALSE;
921     }
922 
923     /* Set the hive filename */
924     if (!RtlCreateUnicodeString(&SystemHive->FileFullPath, L"\\SystemRoot\\System32\\Config\\SYSTEM"))
925         return FALSE;
926 
927     /* Manually set the hive as volatile, if in Live CD mode */
928     if (HiveBase && CmpShareSystemHives)
929     {
930         SystemHive->Hive.HiveFlags = HIVE_VOLATILE;
931     }
932 
933     /* Save the boot type */
934     CmpBootType = SystemHive->Hive.BaseBlock->BootType;
935 
936     /* Are we in self-healing mode? */
937     if (!CmSelfHeal)
938     {
939         /* Disable self-healing internally and check if boot type wanted it */
940         CmpSelfHeal = FALSE;
941         if (CmpBootType & HBOOT_TYPE_SELF_HEAL)
942         {
943             /* We're disabled, so bugcheck */
944             KeBugCheckEx(BAD_SYSTEM_CONFIG_INFO,
945                          3,
946                          3,
947                          (ULONG_PTR)SystemHive,
948                          0);
949         }
950     }
951 
952     /* Create the default security descriptor */
953     SecurityDescriptor = CmpHiveRootSecurityDescriptor();
954 
955     /* Attach it to the system key */
956     /* Let CmpLinkHiveToMaster allocate a new hive if we got none from the LoaderBlock. */
957     RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\SYSTEM");
958     Status = CmpLinkHiveToMaster(&KeyName,
959                                  NULL,
960                                  SystemHive,
961                                  !HiveBase,
962                                  SecurityDescriptor);
963 
964     /* Free the security descriptor */
965     ExFreePoolWithTag(SecurityDescriptor, TAG_CMSD);
966     if (!NT_SUCCESS(Status)) return FALSE;
967 
968     /* Add the hive to the hive list */
969     CmpMachineHiveList[3].CmHive = SystemHive;
970 
971     /* Success! */
972     return TRUE;
973 }
974 
975 CODE_SEG("INIT")
976 NTSTATUS
977 NTAPI
978 CmpCreateObjectTypes(VOID)
979 {
980     OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
981     UNICODE_STRING Name;
982     GENERIC_MAPPING CmpKeyMapping = {KEY_READ,
983                                      KEY_WRITE,
984                                      KEY_EXECUTE,
985                                      KEY_ALL_ACCESS};
986     PAGED_CODE();
987 
988     /* Initialize the Key object type */
989     RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
990     RtlInitUnicodeString(&Name, L"Key");
991     ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
992     ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(CM_KEY_BODY);
993     ObjectTypeInitializer.GenericMapping = CmpKeyMapping;
994     ObjectTypeInitializer.PoolType = PagedPool;
995     ObjectTypeInitializer.ValidAccessMask = KEY_ALL_ACCESS;
996     ObjectTypeInitializer.UseDefaultObject = TRUE;
997     ObjectTypeInitializer.DeleteProcedure = CmpDeleteKeyObject;
998     ObjectTypeInitializer.ParseProcedure = CmpParseKey;
999     ObjectTypeInitializer.SecurityProcedure = CmpSecurityMethod;
1000     ObjectTypeInitializer.QueryNameProcedure = CmpQueryKeyName;
1001     ObjectTypeInitializer.CloseProcedure = CmpCloseKeyObject;
1002     ObjectTypeInitializer.SecurityRequired = TRUE;
1003     ObjectTypeInitializer.InvalidAttributes = OBJ_EXCLUSIVE | OBJ_PERMANENT;
1004 
1005     /* Create it */
1006     return ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &CmpKeyObjectType);
1007 }
1008 
1009 CODE_SEG("INIT")
1010 BOOLEAN
1011 NTAPI
1012 CmpCreateRootNode(IN PHHIVE Hive,
1013                   IN PCWSTR Name,
1014                   OUT PHCELL_INDEX Index)
1015 {
1016     UNICODE_STRING KeyName;
1017     PCM_KEY_NODE KeyCell;
1018     PAGED_CODE();
1019 
1020     /* Initialize the node name and allocate it */
1021     RtlInitUnicodeString(&KeyName, Name);
1022     *Index = HvAllocateCell(Hive,
1023                             FIELD_OFFSET(CM_KEY_NODE, Name) +
1024                             CmpNameSize(Hive, &KeyName),
1025                             Stable,
1026                             HCELL_NIL);
1027     if (*Index == HCELL_NIL) return FALSE;
1028 
1029     /* Set the cell index and get the data */
1030     Hive->BaseBlock->RootCell = *Index;
1031     KeyCell = (PCM_KEY_NODE)HvGetCell(Hive, *Index);
1032     if (!KeyCell) return FALSE;
1033 
1034     /* Setup the cell */
1035     KeyCell->Signature = CM_KEY_NODE_SIGNATURE;
1036     KeyCell->Flags = KEY_HIVE_ENTRY | KEY_NO_DELETE;
1037     KeQuerySystemTime(&KeyCell->LastWriteTime);
1038     KeyCell->Parent = HCELL_NIL;
1039     KeyCell->SubKeyCounts[Stable] = 0;
1040     KeyCell->SubKeyCounts[Volatile] = 0;
1041     KeyCell->SubKeyLists[Stable] = HCELL_NIL;
1042     KeyCell->SubKeyLists[Volatile] = HCELL_NIL;
1043     KeyCell->ValueList.Count = 0;
1044     KeyCell->ValueList.List = HCELL_NIL;
1045     KeyCell->Security = HCELL_NIL;
1046     KeyCell->Class = HCELL_NIL;
1047     KeyCell->ClassLength = 0;
1048     KeyCell->MaxNameLen = 0;
1049     KeyCell->MaxClassLen = 0;
1050     KeyCell->MaxValueNameLen = 0;
1051     KeyCell->MaxValueDataLen = 0;
1052 
1053     /* Copy the name (this will also set the length) */
1054     KeyCell->NameLength = CmpCopyName(Hive, KeyCell->Name, &KeyName);
1055 
1056     /* Check if the name was compressed and set the flag if so */
1057     if (KeyCell->NameLength < KeyName.Length)
1058         KeyCell->Flags |= KEY_COMP_NAME;
1059 
1060     /* Return success */
1061     HvReleaseCell(Hive, *Index);
1062     return TRUE;
1063 }
1064 
1065 CODE_SEG("INIT")
1066 BOOLEAN
1067 NTAPI
1068 CmpCreateRegistryRoot(VOID)
1069 {
1070     UNICODE_STRING KeyName;
1071     OBJECT_ATTRIBUTES ObjectAttributes;
1072     PCM_KEY_BODY RootKey;
1073     HCELL_INDEX RootIndex;
1074     NTSTATUS Status;
1075     PCM_KEY_NODE KeyCell;
1076     PSECURITY_DESCRIPTOR SecurityDescriptor;
1077     PCM_KEY_CONTROL_BLOCK Kcb;
1078     PAGED_CODE();
1079 
1080     /* Setup the root node */
1081     if (!CmpCreateRootNode(&CmiVolatileHive->Hive, L"REGISTRY", &RootIndex))
1082     {
1083         /* We failed */
1084         return FALSE;
1085     }
1086 
1087     /* Create '\Registry' key. */
1088     RtlInitUnicodeString(&KeyName, L"\\REGISTRY");
1089     SecurityDescriptor = CmpHiveRootSecurityDescriptor();
1090     InitializeObjectAttributes(&ObjectAttributes,
1091                                &KeyName,
1092                                OBJ_CASE_INSENSITIVE,
1093                                NULL,
1094                                SecurityDescriptor);
1095     Status = ObCreateObject(KernelMode,
1096                             CmpKeyObjectType,
1097                             &ObjectAttributes,
1098                             KernelMode,
1099                             NULL,
1100                             sizeof(CM_KEY_BODY),
1101                             0,
1102                             0,
1103                             (PVOID*)&RootKey);
1104     ExFreePoolWithTag(SecurityDescriptor, TAG_CMSD);
1105     if (!NT_SUCCESS(Status)) return FALSE;
1106 
1107     /* Sanity check, and get the key cell */
1108     ASSERT((&CmiVolatileHive->Hive)->ReleaseCellRoutine == NULL);
1109     KeyCell = (PCM_KEY_NODE)HvGetCell(&CmiVolatileHive->Hive, RootIndex);
1110     if (!KeyCell) return FALSE;
1111 
1112     /* Create the KCB */
1113     RtlInitUnicodeString(&KeyName, L"\\REGISTRY");
1114     Kcb = CmpCreateKeyControlBlock(&CmiVolatileHive->Hive,
1115                                    RootIndex,
1116                                    KeyCell,
1117                                    NULL,
1118                                    0,
1119                                    &KeyName);
1120     if (!Kcb)
1121     {
1122         ObDereferenceObject(RootKey);
1123         return FALSE;
1124     }
1125 
1126     /* Initialize the object */
1127     RootKey->KeyControlBlock = Kcb;
1128     RootKey->Type = CM_KEY_BODY_TYPE;
1129     RootKey->NotifyBlock = NULL;
1130     RootKey->ProcessID = PsGetCurrentProcessId();
1131     RootKey->KcbLocked = FALSE;
1132 
1133     /* Link with KCB */
1134     EnlistKeyBodyWithKCB(RootKey, 0);
1135 
1136     /* Insert the key into the namespace */
1137     Status = ObInsertObject(RootKey,
1138                             NULL,
1139                             KEY_ALL_ACCESS,
1140                             0,
1141                             NULL,
1142                             &CmpRegistryRootHandle);
1143     if (!NT_SUCCESS(Status))
1144     {
1145         ObDereferenceObject(RootKey);
1146         return FALSE;
1147     }
1148 
1149     /* Reference the key again so that we never lose it */
1150     Status = ObReferenceObjectByHandle(CmpRegistryRootHandle,
1151                                        KEY_READ,
1152                                        NULL,
1153                                        KernelMode,
1154                                        (PVOID*)&RootKey,
1155                                        NULL);
1156     if (!NT_SUCCESS(Status))
1157     {
1158         ObDereferenceObject(RootKey);
1159         return FALSE;
1160     }
1161 
1162     /* Completely sucessful */
1163     return TRUE;
1164 }
1165 
1166 static PCWSTR
1167 CmpGetRegistryPath(VOID)
1168 {
1169     PCWSTR ConfigPath;
1170 
1171     /* Check if we are booted in setup */
1172     if (!ExpInTextModeSetup)
1173     {
1174         ConfigPath = L"\\SystemRoot\\System32\\Config\\";
1175     }
1176     else
1177     {
1178         ConfigPath = L"\\SystemRoot\\";
1179     }
1180 
1181     DPRINT1("CmpGetRegistryPath: ConfigPath = '%S'\n", ConfigPath);
1182 
1183     return ConfigPath;
1184 }
1185 
1186 /**
1187  * @brief
1188  * Checks if the primary and alternate backing hive are
1189  * the same, by determining the time stamp of both hives.
1190  *
1191  * @param[in] FileName
1192  * A pointer to a string containing the file name of the
1193  * primary hive.
1194  *
1195  * @param[in] CmMainmHive
1196  * A pointer to a CM hive descriptor associated with the
1197  * primary hive.
1198  *
1199  * @param[in] AlternateHandle
1200  * A handle to a file that represents the alternate hive.
1201  *
1202  * @param[in] Diverged
1203  * A pointer to a boolean value, if both hives are the same
1204  * it returns TRUE. Otherwise it returns FALSE.
1205  */
1206 static
1207 VOID
1208 CmpHasAlternateHiveDiverged(
1209     _In_ PCUNICODE_STRING FileName,
1210     _In_ PCMHIVE CmMainmHive,
1211     _In_ HANDLE AlternateHandle,
1212     _Out_ PBOOLEAN Diverged)
1213 {
1214     PHHIVE Hive, AlternateHive;
1215     NTSTATUS Status;
1216     PCMHIVE CmiAlternateHive;
1217 
1218     /* Assume it has not diverged */
1219     *Diverged = FALSE;
1220 
1221     /* Initialize the SYSTEM alternate hive */
1222     Status = CmpInitializeHive(&CmiAlternateHive,
1223                                HINIT_FILE,
1224                                0,
1225                                HFILE_TYPE_PRIMARY,
1226                                NULL,
1227                                AlternateHandle,
1228                                NULL,
1229                                NULL,
1230                                NULL,
1231                                FileName,
1232                                CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES);
1233     if (!NT_SUCCESS(Status))
1234     {
1235         /* Assume it has diverged... */
1236         DPRINT1("Failed to initialize the alternate hive to check for diversion (Status 0x%lx)\n", Status);
1237         *Diverged = TRUE;
1238         return;
1239     }
1240 
1241     /*
1242      * Check the timestamp of both hives. If they do not match they
1243      * have diverged, the kernel has to synchronize the both hives.
1244      */
1245     Hive = &CmMainmHive->Hive;
1246     AlternateHive = &CmiAlternateHive->Hive;
1247     if (AlternateHive->BaseBlock->TimeStamp.QuadPart !=
1248         Hive->BaseBlock->TimeStamp.QuadPart)
1249     {
1250         *Diverged = TRUE;
1251     }
1252 
1253     CmpDestroyHive(CmiAlternateHive);
1254 }
1255 
1256 _Function_class_(KSTART_ROUTINE)
1257 VOID
1258 NTAPI
1259 CmpLoadHiveThread(IN PVOID StartContext)
1260 {
1261     WCHAR FileBuffer[64], RegBuffer[64];
1262     PCWSTR ConfigPath;
1263     UNICODE_STRING TempName, FileName, RegName;
1264     ULONG i, ErrorResponse, WorkerCount, Length;
1265     USHORT FileStart;
1266     ULONG PrimaryDisposition, SecondaryDisposition, ClusterSize;
1267     PCMHIVE CmHive;
1268     HANDLE PrimaryHandle = NULL, AlternateHandle = NULL;
1269     NTSTATUS Status = STATUS_SUCCESS;
1270     PVOID ErrorParameters;
1271     BOOLEAN HasDiverged;
1272     PAGED_CODE();
1273 
1274     /* Get the hive index, make sure it makes sense */
1275     i = PtrToUlong(StartContext);
1276     ASSERT(CmpMachineHiveList[i].Name != NULL);
1277 
1278     /* We were started */
1279     CmpMachineHiveList[i].ThreadStarted = TRUE;
1280 
1281     /* Build the file name and registry name strings */
1282     RtlInitEmptyUnicodeString(&FileName, FileBuffer, sizeof(FileBuffer));
1283     RtlInitEmptyUnicodeString(&RegName, RegBuffer, sizeof(RegBuffer));
1284 
1285     /* Now build the system root path */
1286     ConfigPath = CmpGetRegistryPath();
1287     RtlInitUnicodeString(&TempName, ConfigPath);
1288     RtlAppendUnicodeStringToString(&FileName, &TempName);
1289     FileStart = FileName.Length;
1290 
1291     /* And build the registry root path */
1292     RtlInitUnicodeString(&TempName, L"\\REGISTRY\\");
1293     RtlAppendUnicodeStringToString(&RegName, &TempName);
1294 
1295     /* Build the base name */
1296     RtlInitUnicodeString(&TempName, CmpMachineHiveList[i].BaseName);
1297     RtlAppendUnicodeStringToString(&RegName, &TempName);
1298 
1299     /* Check if this is a child of the root */
1300     if (RegName.Buffer[RegName.Length / sizeof(WCHAR) - 1] == OBJ_NAME_PATH_SEPARATOR)
1301     {
1302         /* Then setup the whole name */
1303         RtlInitUnicodeString(&TempName, CmpMachineHiveList[i].Name);
1304         RtlAppendUnicodeStringToString(&RegName, &TempName);
1305     }
1306 
1307     /* Now add the rest of the file name */
1308     RtlInitUnicodeString(&TempName, CmpMachineHiveList[i].Name);
1309     FileName.Length = FileStart;
1310     RtlAppendUnicodeStringToString(&FileName, &TempName);
1311     if (!CmpMachineHiveList[i].CmHive)
1312     {
1313         /* We need to allocate a new hive structure */
1314         CmpMachineHiveList[i].Allocate = TRUE;
1315 
1316         /* Load the hive file */
1317         Status = CmpInitHiveFromFile(&FileName,
1318                                      CmpMachineHiveList[i].HHiveFlags,
1319                                      &CmHive,
1320                                      &CmpMachineHiveList[i].Allocate,
1321                                      CM_CHECK_REGISTRY_PURGE_VOLATILES);
1322         if (!(NT_SUCCESS(Status)) ||
1323             (!(CmpShareSystemHives) && !(CmHive->FileHandles[HFILE_TYPE_LOG])))
1324         {
1325             /*
1326              * We failed, or could not get a log file (unless
1327              * the hive is shared), raise a hard error.
1328              */
1329             ErrorParameters = &FileName;
1330             NtRaiseHardError(STATUS_CANNOT_LOAD_REGISTRY_FILE,
1331                              1,
1332                              1,
1333                              (PULONG_PTR)&ErrorParameters,
1334                              OptionOk,
1335                              &ErrorResponse);
1336         }
1337 
1338         /* Set the hive flags and newly allocated hive pointer */
1339         CmHive->Flags = CmpMachineHiveList[i].CmHiveFlags;
1340         CmpMachineHiveList[i].CmHive2 = CmHive;
1341     }
1342     else
1343     {
1344         /* We already have a hive, is it volatile? */
1345         CmHive = CmpMachineHiveList[i].CmHive;
1346         if (!(CmHive->Hive.HiveFlags & HIVE_VOLATILE))
1347         {
1348             /* It's now, open the hive file and log */
1349             Status = CmpOpenHiveFiles(&FileName,
1350                                       L".ALT",
1351                                       &PrimaryHandle,
1352                                       &AlternateHandle,
1353                                       &PrimaryDisposition,
1354                                       &SecondaryDisposition,
1355                                       TRUE,
1356                                       TRUE,
1357                                       FALSE,
1358                                       &ClusterSize);
1359             if (!(NT_SUCCESS(Status)) || !(AlternateHandle))
1360             {
1361                 /* Couldn't open the hive or its alternate file, raise a hard error */
1362                 ErrorParameters = &FileName;
1363                 NtRaiseHardError(STATUS_CANNOT_LOAD_REGISTRY_FILE,
1364                                  1,
1365                                  1,
1366                                  (PULONG_PTR)&ErrorParameters,
1367                                  OptionOk,
1368                                  &ErrorResponse);
1369 
1370                 /* And bugcheck for posterity's sake */
1371                 KeBugCheckEx(BAD_SYSTEM_CONFIG_INFO, 9, 0, i, Status);
1372             }
1373 
1374             /* Save the file handles. This should remove our sync hacks */
1375             /*
1376              * FIXME: Any hive that relies on the alternate hive for recovery purposes
1377              * will only get an alternate hive. As a result, the LOG file would never
1378              * get synced each time a write is done to the hive. In the future it would
1379              * be best to adapt the code so that a primary hive can use a LOG and ALT
1380              * hives at the same time.
1381              */
1382             CmHive->FileHandles[HFILE_TYPE_ALTERNATE] = AlternateHandle;
1383             CmHive->FileHandles[HFILE_TYPE_PRIMARY] = PrimaryHandle;
1384 
1385             /* Allow lazy flushing since the handles are there -- remove sync hacks */
1386             //ASSERT(CmHive->Hive.HiveFlags & HIVE_NOLAZYFLUSH);
1387             CmHive->Hive.HiveFlags &= ~HIVE_NOLAZYFLUSH;
1388 
1389             /* Get the real size of the hive */
1390             Length = CmHive->Hive.Storage[Stable].Length + HBLOCK_SIZE;
1391 
1392             /* Check if the cluster size doesn't match */
1393             if (CmHive->Hive.Cluster != ClusterSize)
1394             {
1395                 DPRINT1("FIXME: Support for CmHive->Hive.Cluster (%lu) != ClusterSize (%lu) is unimplemented!\n",
1396                         CmHive->Hive.Cluster, ClusterSize);
1397             }
1398 
1399             /* Set the file size */
1400             DPRINT("FIXME: Should set file size: %lu\n", Length);
1401             //if (!CmpFileSetSize((PHHIVE)CmHive, HFILE_TYPE_PRIMARY, Length, Length))
1402             //{
1403                 /* This shouldn't fail */
1404                 //ASSERT(FALSE);
1405             //}
1406 
1407             /* FreeLdr has recovered the hive with a log, we must do a flush */
1408             if (CmHive->Hive.BaseBlock->BootRecover == HBOOT_BOOT_RECOVERED_BY_HIVE_LOG)
1409             {
1410                 DPRINT1("FreeLdr recovered the hive (hive 0x%p)\n", CmHive);
1411                 RtlSetAllBits(&CmHive->Hive.DirtyVector);
1412                 CmHive->Hive.DirtyCount = CmHive->Hive.DirtyVector.SizeOfBitMap;
1413                 HvSyncHive((PHHIVE)CmHive);
1414             }
1415             else
1416             {
1417                 /*
1418                  * Check whether the both primary and alternate hives are the same,
1419                  * or that the primary or alternate were created for the first time.
1420                  * Do a write against the alternate hive in these cases.
1421                  */
1422                 CmpHasAlternateHiveDiverged(&FileName,
1423                                             CmHive,
1424                                             AlternateHandle,
1425                                             &HasDiverged);
1426                 if (HasDiverged ||
1427                     PrimaryDisposition == FILE_CREATED ||
1428                     SecondaryDisposition == FILE_CREATED)
1429                 {
1430                     if (!HvWriteAlternateHive((PHHIVE)CmHive))
1431                     {
1432                         DPRINT1("Failed to write to alternate hive\n");
1433                         goto Exit;
1434                     }
1435                 }
1436             }
1437 
1438             /* Finally, set our allocated hive to the same hive we've had */
1439             CmpMachineHiveList[i].CmHive2 = CmHive;
1440             ASSERT(CmpMachineHiveList[i].CmHive == CmpMachineHiveList[i].CmHive2);
1441         }
1442     }
1443 
1444 Exit:
1445     /* We're done */
1446     CmpMachineHiveList[i].ThreadFinished = TRUE;
1447 
1448     /* Check if we're the last worker */
1449     WorkerCount = InterlockedIncrement(&CmpLoadWorkerIncrement);
1450     if (WorkerCount == CM_NUMBER_OF_MACHINE_HIVES)
1451     {
1452         /* Signal the event */
1453         KeSetEvent(&CmpLoadWorkerEvent, 0, FALSE);
1454     }
1455 
1456     /* Kill the thread */
1457     PsTerminateSystemThread(Status);
1458 }
1459 
1460 VOID
1461 NTAPI
1462 CmpInitializeHiveList(VOID)
1463 {
1464     WCHAR FileBuffer[64], RegBuffer[64];
1465     PCWSTR ConfigPath;
1466     UNICODE_STRING TempName, FileName, RegName;
1467     HANDLE Thread;
1468     NTSTATUS Status;
1469     ULONG i;
1470     USHORT RegStart;
1471     PSECURITY_DESCRIPTOR SecurityDescriptor;
1472     PAGED_CODE();
1473 
1474     /* Allow writing for now */
1475     CmpNoWrite = FALSE;
1476 
1477     /* Build the file name and registry name strings */
1478     RtlInitEmptyUnicodeString(&FileName, FileBuffer, sizeof(FileBuffer));
1479     RtlInitEmptyUnicodeString(&RegName, RegBuffer, sizeof(RegBuffer));
1480 
1481     /* Now build the system root path */
1482     ConfigPath = CmpGetRegistryPath();
1483     RtlInitUnicodeString(&TempName, ConfigPath);
1484     RtlAppendUnicodeStringToString(&FileName, &TempName);
1485 
1486     /* And build the registry root path */
1487     RtlInitUnicodeString(&TempName, L"\\REGISTRY\\");
1488     RtlAppendUnicodeStringToString(&RegName, &TempName);
1489     RegStart = RegName.Length;
1490 
1491     /* Setup the event to synchronize workers */
1492     KeInitializeEvent(&CmpLoadWorkerEvent, SynchronizationEvent, FALSE);
1493 
1494     /* Enter special boot condition */
1495     CmpSpecialBootCondition = TRUE;
1496 
1497     /* Create the SD for the root hives */
1498     SecurityDescriptor = CmpHiveRootSecurityDescriptor();
1499 
1500     /* Loop every hive we care about */
1501     for (i = 0; i < CM_NUMBER_OF_MACHINE_HIVES; i++)
1502     {
1503         /* Make sure the list is set up */
1504         ASSERT(CmpMachineHiveList[i].Name != NULL);
1505 
1506         /* Load the hive as volatile, if in LiveCD mode */
1507         if (CmpShareSystemHives)
1508             CmpMachineHiveList[i].HHiveFlags |= HIVE_VOLATILE;
1509 
1510         /* Create a thread to handle this hive */
1511         Status = PsCreateSystemThread(&Thread,
1512                                       THREAD_ALL_ACCESS,
1513                                       NULL,
1514                                       0,
1515                                       NULL,
1516                                       CmpLoadHiveThread,
1517                                       UlongToPtr(i));
1518         if (NT_SUCCESS(Status))
1519         {
1520             /* We don't care about the handle -- the thread self-terminates */
1521             ZwClose(Thread);
1522         }
1523         else
1524         {
1525             /* Can't imagine this happening */
1526             KeBugCheckEx(BAD_SYSTEM_CONFIG_INFO, 9, 3, i, Status);
1527         }
1528     }
1529 
1530     /* Make sure we've reached the end of the list */
1531     ASSERT(CmpMachineHiveList[i].Name == NULL);
1532 
1533     /* Wait for hive loading to finish */
1534     KeWaitForSingleObject(&CmpLoadWorkerEvent,
1535                           Executive,
1536                           KernelMode,
1537                           FALSE,
1538                           NULL);
1539 
1540     /* Exit the special boot condition and make sure all workers completed */
1541     CmpSpecialBootCondition = FALSE;
1542     ASSERT(CmpLoadWorkerIncrement == CM_NUMBER_OF_MACHINE_HIVES);
1543 
1544     /* Loop hives again */
1545     for (i = 0; i < CM_NUMBER_OF_MACHINE_HIVES; i++)
1546     {
1547         /* Make sure the thread ran and finished */
1548         ASSERT(CmpMachineHiveList[i].ThreadFinished == TRUE);
1549         ASSERT(CmpMachineHiveList[i].ThreadStarted == TRUE);
1550 
1551         /* Check if this was a new hive */
1552         if (!CmpMachineHiveList[i].CmHive)
1553         {
1554             /* Make sure we allocated something */
1555             ASSERT(CmpMachineHiveList[i].CmHive2 != NULL);
1556 
1557             /* Build the base name */
1558             RegName.Length = RegStart;
1559             RtlInitUnicodeString(&TempName, CmpMachineHiveList[i].BaseName);
1560             RtlAppendUnicodeStringToString(&RegName, &TempName);
1561 
1562             /* Check if this is a child of the root */
1563             if (RegName.Buffer[RegName.Length / sizeof(WCHAR) - 1] == OBJ_NAME_PATH_SEPARATOR)
1564             {
1565                 /* Then setup the whole name */
1566                 RtlInitUnicodeString(&TempName, CmpMachineHiveList[i].Name);
1567                 RtlAppendUnicodeStringToString(&RegName, &TempName);
1568             }
1569 
1570             /* Now link the hive to its master */
1571             Status = CmpLinkHiveToMaster(&RegName,
1572                                          NULL,
1573                                          CmpMachineHiveList[i].CmHive2,
1574                                          CmpMachineHiveList[i].Allocate,
1575                                          SecurityDescriptor);
1576             if (Status != STATUS_SUCCESS)
1577             {
1578                 /* Linking needs to work */
1579                 KeBugCheckEx(CONFIG_LIST_FAILED, 11, Status, i, (ULONG_PTR)&RegName);
1580             }
1581 
1582             /* Check if we had to allocate a new hive */
1583             if (CmpMachineHiveList[i].Allocate)
1584             {
1585                 /* Sync the new hive */
1586                 //HvSyncHive((PHHIVE)(CmpMachineHiveList[i].CmHive2));
1587             }
1588         }
1589 
1590         /* Check if we created a new hive */
1591         if (CmpMachineHiveList[i].CmHive2)
1592         {
1593             /* Add to HiveList key */
1594             CmpAddToHiveFileList(CmpMachineHiveList[i].CmHive2);
1595         }
1596     }
1597 
1598     /* Get rid of the SD */
1599     ExFreePoolWithTag(SecurityDescriptor, TAG_CMSD);
1600 
1601     /* Link SECURITY to SAM */
1602     CmpLinkKeyToHive(L"\\Registry\\Machine\\Security\\SAM",
1603                      L"\\Registry\\Machine\\SAM\\SAM");
1604 
1605     /* Link S-1-5-18 to .Default */
1606     CmpNoVolatileCreates = FALSE;
1607     CmpLinkKeyToHive(L"\\Registry\\User\\S-1-5-18",
1608                      L"\\Registry\\User\\.Default");
1609     CmpNoVolatileCreates = TRUE;
1610 }
1611 
1612 CODE_SEG("INIT")
1613 BOOLEAN
1614 NTAPI
1615 CmInitSystem1(VOID)
1616 {
1617     OBJECT_ATTRIBUTES ObjectAttributes;
1618     UNICODE_STRING KeyName;
1619     HANDLE KeyHandle;
1620     NTSTATUS Status;
1621     PCMHIVE HardwareHive;
1622     PSECURITY_DESCRIPTOR SecurityDescriptor;
1623     PAGED_CODE();
1624 
1625     /* Check if this is PE-boot */
1626     if (InitIsWinPEMode)
1627     {
1628         /* Set registry to PE mode */
1629         CmpMiniNTBoot = TRUE;
1630         CmpShareSystemHives = TRUE;
1631     }
1632 
1633     /* Initialize the hive list and lock */
1634     InitializeListHead(&CmpHiveListHead);
1635     ExInitializePushLock(&CmpHiveListHeadLock);
1636     ExInitializePushLock(&CmpLoadHiveLock);
1637 
1638     /* Initialize registry lock */
1639     ExInitializeResourceLite(&CmpRegistryLock);
1640 
1641     /* Initialize the cache */
1642     CmpInitializeCache();
1643 
1644     /* Initialize allocation and delayed dereferencing */
1645     CmpInitCmPrivateAlloc();
1646     CmpInitCmPrivateDelayAlloc();
1647     CmpInitDelayDerefKCBEngine();
1648 
1649     /* Initialize callbacks */
1650     CmpInitCallback();
1651 
1652     /* Initialize self healing */
1653     KeInitializeGuardedMutex(&CmpSelfHealQueueLock);
1654     InitializeListHead(&CmpSelfHealQueueListHead);
1655 
1656     /* Save the current process and lock the registry */
1657     CmpSystemProcess = PsGetCurrentProcess();
1658 
1659     /* Create the key object types */
1660     Status = CmpCreateObjectTypes();
1661     if (!NT_SUCCESS(Status))
1662     {
1663         /* Bugcheck */
1664         KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 1, 1, Status, 0);
1665     }
1666 
1667     /* Build the master hive */
1668     Status = CmpInitializeHive(&CmiVolatileHive,
1669                                HINIT_CREATE,
1670                                HIVE_VOLATILE,
1671                                HFILE_TYPE_PRIMARY,
1672                                NULL,
1673                                NULL,
1674                                NULL,
1675                                NULL,
1676                                NULL,
1677                                NULL,
1678                                CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES);
1679     if (!NT_SUCCESS(Status))
1680     {
1681         /* Bugcheck */
1682         KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 1, 2, Status, 0);
1683     }
1684 
1685     /* Create the \REGISTRY key node */
1686     if (!CmpCreateRegistryRoot())
1687     {
1688         /* Bugcheck */
1689         KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 1, 3, 0, 0);
1690     }
1691 
1692     /* Create the default security descriptor */
1693     SecurityDescriptor = CmpHiveRootSecurityDescriptor();
1694 
1695     /* Create '\Registry\Machine' key */
1696     RtlInitUnicodeString(&KeyName, L"\\REGISTRY\\MACHINE");
1697     InitializeObjectAttributes(&ObjectAttributes,
1698                                &KeyName,
1699                                OBJ_CASE_INSENSITIVE,
1700                                NULL,
1701                                SecurityDescriptor);
1702     Status = NtCreateKey(&KeyHandle,
1703                          KEY_READ | KEY_WRITE,
1704                          &ObjectAttributes,
1705                          0,
1706                          NULL,
1707                          0,
1708                          NULL);
1709     if (!NT_SUCCESS(Status))
1710     {
1711         /* Bugcheck */
1712         KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 1, 5, Status, 0);
1713     }
1714 
1715     /* Close the handle */
1716     NtClose(KeyHandle);
1717 
1718     /* Create '\Registry\User' key */
1719     RtlInitUnicodeString(&KeyName, L"\\REGISTRY\\USER");
1720     InitializeObjectAttributes(&ObjectAttributes,
1721                                &KeyName,
1722                                OBJ_CASE_INSENSITIVE,
1723                                NULL,
1724                                SecurityDescriptor);
1725     Status = NtCreateKey(&KeyHandle,
1726                          KEY_READ | KEY_WRITE,
1727                          &ObjectAttributes,
1728                          0,
1729                          NULL,
1730                          0,
1731                          NULL);
1732     if (!NT_SUCCESS(Status))
1733     {
1734         /* Bugcheck */
1735         KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 1, 6, Status, 0);
1736     }
1737 
1738     /* Close the handle */
1739     NtClose(KeyHandle);
1740 
1741     /* After this point, do not allow creating keys in the master hive */
1742     CmpNoVolatileCreates = TRUE;
1743 
1744     /* Initialize the system hive */
1745     if (!CmpInitializeSystemHive(KeLoaderBlock))
1746     {
1747         /* Bugcheck */
1748         KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 1, 7, 0, 0);
1749     }
1750 
1751     /* Create the 'CurrentControlSet' link */
1752     Status = CmpCreateControlSet(KeLoaderBlock);
1753     if (!NT_SUCCESS(Status))
1754     {
1755         /* Bugcheck */
1756         KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 1, 8, Status, 0);
1757     }
1758 
1759     /* Create the hardware hive */
1760     Status = CmpInitializeHive(&HardwareHive,
1761                                HINIT_CREATE,
1762                                HIVE_VOLATILE,
1763                                HFILE_TYPE_PRIMARY,
1764                                NULL,
1765                                NULL,
1766                                NULL,
1767                                NULL,
1768                                NULL,
1769                                NULL,
1770                                CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES);
1771     if (!NT_SUCCESS(Status))
1772     {
1773         /* Bugcheck */
1774         KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 1, 11, Status, 0);
1775     }
1776 
1777     /* Add the hive to the hive list */
1778     CmpMachineHiveList[0].CmHive = HardwareHive;
1779 
1780     /* Attach it to the machine key */
1781     RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\HARDWARE");
1782     Status = CmpLinkHiveToMaster(&KeyName,
1783                                  NULL,
1784                                  HardwareHive,
1785                                  TRUE,
1786                                  SecurityDescriptor);
1787     if (!NT_SUCCESS(Status))
1788     {
1789         /* Bugcheck */
1790         KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 1, 12, Status, 0);
1791     }
1792 
1793     /* Add to HiveList key */
1794     CmpAddToHiveFileList(HardwareHive);
1795 
1796     /* Free the security descriptor */
1797     ExFreePoolWithTag(SecurityDescriptor, TAG_CMSD);
1798 
1799     /* Fill out the Hardware key with the ARC Data from the Loader */
1800     Status = CmpInitializeHardwareConfiguration(KeLoaderBlock);
1801     if (!NT_SUCCESS(Status))
1802     {
1803         /* Bugcheck */
1804         KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 1, 13, Status, 0);
1805     }
1806 
1807     /* Initialize machine-dependent information into the registry */
1808     Status = CmpInitializeMachineDependentConfiguration(KeLoaderBlock);
1809     if (!NT_SUCCESS(Status))
1810     {
1811         /* Bugcheck */
1812         KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 1, 14, Status, 0);
1813     }
1814 
1815     /* Initialize volatile registry settings */
1816     Status = CmpSetSystemValues(KeLoaderBlock);
1817     if (!NT_SUCCESS(Status))
1818     {
1819         /* Bugcheck */
1820         KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 1, 15, Status, 0);
1821     }
1822 
1823     /* Free the load options */
1824     ExFreePoolWithTag(CmpLoadOptions.Buffer, TAG_CM);
1825 
1826     /* If we got here, all went well */
1827     return TRUE;
1828 }
1829 
1830 CODE_SEG("INIT")
1831 PUNICODE_STRING*
1832 NTAPI
1833 CmGetSystemDriverList(VOID)
1834 {
1835     LIST_ENTRY DriverList;
1836     OBJECT_ATTRIBUTES ObjectAttributes;
1837     NTSTATUS Status;
1838     PCM_KEY_BODY KeyBody;
1839     PHHIVE Hive;
1840     HCELL_INDEX RootCell, ControlCell;
1841     HANDLE KeyHandle;
1842     UNICODE_STRING KeyName;
1843     PLIST_ENTRY NextEntry;
1844     ULONG i;
1845     PUNICODE_STRING* ServicePath = NULL;
1846     BOOLEAN Success, AutoSelect;
1847     PBOOT_DRIVER_LIST_ENTRY DriverEntry;
1848     PAGED_CODE();
1849 
1850     /* Initialize the driver list */
1851     InitializeListHead(&DriverList);
1852 
1853     /* Open the system hive key */
1854     RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\System");
1855     InitializeObjectAttributes(&ObjectAttributes,
1856                                &KeyName,
1857                                OBJ_CASE_INSENSITIVE,
1858                                NULL,
1859                                NULL);
1860     Status = NtOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
1861     if (!NT_SUCCESS(Status)) return NULL;
1862 
1863     /* Reference the key object to get the root hive/cell to access directly */
1864     Status = ObReferenceObjectByHandle(KeyHandle,
1865                                        KEY_QUERY_VALUE,
1866                                        CmpKeyObjectType,
1867                                        KernelMode,
1868                                        (PVOID*)&KeyBody,
1869                                        NULL);
1870     if (!NT_SUCCESS(Status))
1871     {
1872         /* Fail */
1873         NtClose(KeyHandle);
1874         return NULL;
1875     }
1876 
1877     /* Do all this under the registry lock */
1878     CmpLockRegistryExclusive();
1879 
1880     /* Get the hive and key cell */
1881     Hive = KeyBody->KeyControlBlock->KeyHive;
1882     RootCell = KeyBody->KeyControlBlock->KeyCell;
1883 
1884     /* Open the current control set key */
1885     RtlInitUnicodeString(&KeyName, L"Current");
1886     ControlCell = CmpFindControlSet(Hive, RootCell, &KeyName, &AutoSelect);
1887     if (ControlCell == HCELL_NIL) goto EndPath;
1888 
1889     /* Find all system drivers */
1890     Success = CmpFindDrivers(Hive, ControlCell, SystemLoad, NULL, &DriverList);
1891     if (!Success) goto EndPath;
1892 
1893     /* Sort by group/tag */
1894     if (!CmpSortDriverList(Hive, ControlCell, &DriverList)) goto EndPath;
1895 
1896     /* Remove circular dependencies (cycles) and sort */
1897     if (!CmpResolveDriverDependencies(&DriverList)) goto EndPath;
1898 
1899     /* Loop the list to count drivers */
1900     for (i = 0, NextEntry = DriverList.Flink;
1901          NextEntry != &DriverList;
1902          i++, NextEntry = NextEntry->Flink);
1903 
1904     /* Allocate the array */
1905     ServicePath = ExAllocatePool(NonPagedPool, (i + 1) * sizeof(PUNICODE_STRING));
1906     if (!ServicePath) KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 2, 1, 0, 0);
1907 
1908     /* Loop the driver list */
1909     for (i = 0, NextEntry = DriverList.Flink;
1910          NextEntry != &DriverList;
1911          i++, NextEntry = NextEntry->Flink)
1912     {
1913         /* Get the entry */
1914         DriverEntry = CONTAINING_RECORD(NextEntry, BOOT_DRIVER_LIST_ENTRY, Link);
1915 
1916         /* Allocate the path for the caller */
1917         ServicePath[i] = ExAllocatePool(NonPagedPool, sizeof(UNICODE_STRING));
1918         if (!ServicePath[i])
1919         {
1920             KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 2, 1, 0, 0);
1921         }
1922 
1923         /* Duplicate the registry path */
1924         Status = RtlDuplicateUnicodeString(RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
1925                                            &DriverEntry->RegistryPath,
1926                                            ServicePath[i]);
1927         if (!NT_SUCCESS(Status))
1928         {
1929             KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 2, 1, 0, 0);
1930         }
1931     }
1932 
1933     /* Terminate the list */
1934     ServicePath[i] = NULL;
1935 
1936 EndPath:
1937     /* Free the driver list if we had one */
1938     if (!IsListEmpty(&DriverList)) CmpFreeDriverList(Hive, &DriverList);
1939 
1940     /* Unlock the registry */
1941     CmpUnlockRegistry();
1942 
1943     /* Close the key handle and dereference the object, then return the path */
1944     ObDereferenceObject(KeyBody);
1945     NtClose(KeyHandle);
1946     return ServicePath;
1947 }
1948 
1949 VOID
1950 NTAPI
1951 CmpLockRegistryExclusive(VOID)
1952 {
1953     /* Enter a critical region and lock the registry */
1954     KeEnterCriticalRegion();
1955     ExAcquireResourceExclusiveLite(&CmpRegistryLock, TRUE);
1956 
1957     /* Sanity check */
1958     ASSERT(CmpFlushStarveWriters == 0);
1959     RtlGetCallersAddress(&CmpRegistryLockCaller, &CmpRegistryLockCallerCaller);
1960 }
1961 
1962 VOID
1963 NTAPI
1964 CmpLockRegistry(VOID)
1965 {
1966     /* Enter a critical region */
1967     KeEnterCriticalRegion();
1968 
1969     /* Check if we have to starve writers */
1970     if (CmpFlushStarveWriters)
1971     {
1972         /* Starve exlusive waiters */
1973         ExAcquireSharedStarveExclusive(&CmpRegistryLock, TRUE);
1974     }
1975     else
1976     {
1977         /* Just grab the lock */
1978         ExAcquireResourceSharedLite(&CmpRegistryLock, TRUE);
1979     }
1980 }
1981 
1982 BOOLEAN
1983 NTAPI
1984 CmpTestRegistryLock(VOID)
1985 {
1986     /* Test the lock */
1987     return !ExIsResourceAcquiredSharedLite(&CmpRegistryLock) ? FALSE : TRUE;
1988 }
1989 
1990 BOOLEAN
1991 NTAPI
1992 CmpTestRegistryLockExclusive(VOID)
1993 {
1994     /* Test the lock */
1995     return !ExIsResourceAcquiredExclusiveLite(&CmpRegistryLock) ? FALSE : TRUE;
1996 }
1997 
1998 VOID
1999 NTAPI
2000 CmpLockHiveFlusherExclusive(IN PCMHIVE Hive)
2001 {
2002     /* Lock the flusher. We should already be in a critical section */
2003     CMP_ASSERT_REGISTRY_LOCK_OR_LOADING(Hive);
2004     ASSERT((ExIsResourceAcquiredShared(Hive->FlusherLock) == 0) &&
2005            (ExIsResourceAcquiredExclusiveLite(Hive->FlusherLock) == 0));
2006     ExAcquireResourceExclusiveLite(Hive->FlusherLock, TRUE);
2007 }
2008 
2009 VOID
2010 NTAPI
2011 CmpLockHiveFlusherShared(IN PCMHIVE Hive)
2012 {
2013     /* Lock the flusher. We should already be in a critical section */
2014     CMP_ASSERT_REGISTRY_LOCK_OR_LOADING(Hive);
2015     ASSERT((ExIsResourceAcquiredShared(Hive->FlusherLock) == 0) &&
2016            (ExIsResourceAcquiredExclusiveLite(Hive->FlusherLock) == 0));
2017     ExAcquireResourceSharedLite(Hive->FlusherLock, TRUE);
2018 }
2019 
2020 VOID
2021 NTAPI
2022 CmpUnlockHiveFlusher(IN PCMHIVE Hive)
2023 {
2024     /* Sanity check */
2025     CMP_ASSERT_REGISTRY_LOCK_OR_LOADING(Hive);
2026     CMP_ASSERT_FLUSH_LOCK(Hive);
2027 
2028     /* Release the lock */
2029     ExReleaseResourceLite(Hive->FlusherLock);
2030 }
2031 
2032 BOOLEAN
2033 NTAPI
2034 CmpTestHiveFlusherLockShared(IN PCMHIVE Hive)
2035 {
2036     /* Test the lock */
2037     return !ExIsResourceAcquiredSharedLite(Hive->FlusherLock) ? FALSE : TRUE;
2038 }
2039 
2040 BOOLEAN
2041 NTAPI
2042 CmpTestHiveFlusherLockExclusive(IN PCMHIVE Hive)
2043 {
2044     /* Test the lock */
2045     return !ExIsResourceAcquiredExclusiveLite(Hive->FlusherLock) ? FALSE : TRUE;
2046 }
2047 
2048 VOID
2049 NTAPI
2050 CmpUnlockRegistry(VOID)
2051 {
2052     /* Sanity check */
2053     CMP_ASSERT_REGISTRY_LOCK();
2054 
2055     /* Check if we should flush the registry */
2056     if (CmpFlushOnLockRelease)
2057     {
2058         /* The registry should be exclusively locked for this */
2059         CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK();
2060 
2061         /* Flush the registry */
2062         CmpDoFlushAll(TRUE);
2063         CmpFlushOnLockRelease = FALSE;
2064     }
2065 
2066     /* Release the lock and leave the critical region */
2067     ExReleaseResourceLite(&CmpRegistryLock);
2068     KeLeaveCriticalRegion();
2069 }
2070 
2071 VOID
2072 NTAPI
2073 CmpAcquireTwoKcbLocksExclusiveByKey(IN ULONG ConvKey1,
2074                                     IN ULONG ConvKey2)
2075 {
2076     ULONG Index1, Index2;
2077 
2078     /* Sanity check */
2079     CMP_ASSERT_REGISTRY_LOCK();
2080 
2081     /* Get hash indexes */
2082     Index1 = GET_HASH_INDEX(ConvKey1);
2083     Index2 = GET_HASH_INDEX(ConvKey2);
2084 
2085     /* See which one is highest */
2086     if (Index1 < Index2)
2087     {
2088         /* Grab them in the proper order */
2089         CmpAcquireKcbLockExclusiveByKey(ConvKey1);
2090         CmpAcquireKcbLockExclusiveByKey(ConvKey2);
2091     }
2092     else
2093     {
2094         /* Grab the second one first, then the first */
2095         CmpAcquireKcbLockExclusiveByKey(ConvKey2);
2096         if (Index1 != Index2) CmpAcquireKcbLockExclusiveByKey(ConvKey1);
2097     }
2098 }
2099 
2100 VOID
2101 NTAPI
2102 CmpReleaseTwoKcbLockByKey(IN ULONG ConvKey1,
2103                           IN ULONG ConvKey2)
2104 {
2105     ULONG Index1, Index2;
2106 
2107     /* Sanity check */
2108     CMP_ASSERT_REGISTRY_LOCK();
2109 
2110     /* Get hash indexes */
2111     Index1 = GET_HASH_INDEX(ConvKey1);
2112     Index2 = GET_HASH_INDEX(ConvKey2);
2113     ASSERT((GET_HASH_ENTRY(CmpCacheTable, ConvKey2)->Owner == KeGetCurrentThread()) ||
2114            (CmpTestRegistryLockExclusive()));
2115 
2116     /* See which one is highest */
2117     if (Index1 < Index2)
2118     {
2119         /* Grab them in the proper order */
2120         ASSERT((GET_HASH_ENTRY(CmpCacheTable, ConvKey1)->Owner == KeGetCurrentThread()) ||
2121                (CmpTestRegistryLockExclusive()));
2122         CmpReleaseKcbLockByKey(ConvKey2);
2123         CmpReleaseKcbLockByKey(ConvKey1);
2124     }
2125     else
2126     {
2127         /* Release the first one first, then the second */
2128         if (Index1 != Index2)
2129         {
2130             ASSERT((GET_HASH_ENTRY(CmpCacheTable, ConvKey1)->Owner == KeGetCurrentThread()) ||
2131                    (CmpTestRegistryLockExclusive()));
2132             CmpReleaseKcbLockByKey(ConvKey1);
2133         }
2134         CmpReleaseKcbLockByKey(ConvKey2);
2135     }
2136 }
2137 
2138 VOID
2139 NTAPI
2140 CmShutdownSystem(VOID)
2141 {
2142     PLIST_ENTRY ListEntry;
2143     PCMHIVE Hive;
2144 
2145     /* Kill the workers */
2146     if (!CmFirstTime) CmpShutdownWorkers();
2147 
2148     /* Flush all hives */
2149     CmpLockRegistryExclusive();
2150     CmpDoFlushAll(TRUE);
2151 
2152     /* Close all hive files */
2153     ListEntry = CmpHiveListHead.Flink;
2154     while (ListEntry != &CmpHiveListHead)
2155     {
2156         Hive = CONTAINING_RECORD(ListEntry, CMHIVE, HiveList);
2157 
2158         CmpCloseHiveFiles(Hive);
2159 
2160         ListEntry = ListEntry->Flink;
2161     }
2162 
2163     /*
2164      * As we flushed all the hives on the disk,
2165      * tell the system we do not want any further
2166      * registry flushing or syncing at this point
2167      * since we are shutting down the registry anyway.
2168      */
2169     HvShutdownComplete = TRUE;
2170 
2171     CmpUnlockRegistry();
2172 }
2173 
2174 VOID
2175 NTAPI
2176 CmpSetVersionData(VOID)
2177 {
2178     NTSTATUS Status;
2179     OBJECT_ATTRIBUTES ObjectAttributes;
2180     UNICODE_STRING KeyName;
2181     UNICODE_STRING ValueName;
2182     UNICODE_STRING ValueData;
2183     ANSI_STRING TempString;
2184     HANDLE SoftwareKeyHandle = NULL;
2185     HANDLE MicrosoftKeyHandle = NULL;
2186     HANDLE WindowsNtKeyHandle = NULL;
2187     HANDLE CurrentVersionKeyHandle = NULL;
2188     WCHAR Buffer[128]; // Buffer large enough to contain a full ULONG in decimal
2189                        // representation, and the full 'CurrentType' string.
2190 
2191     /*
2192      * Open the 'HKLM\Software\Microsoft\Windows NT\CurrentVersion' key
2193      * (create the intermediate subkeys if needed).
2194      */
2195 
2196     RtlInitUnicodeString(&KeyName, L"\\REGISTRY\\MACHINE\\SOFTWARE");
2197     InitializeObjectAttributes(&ObjectAttributes,
2198                                &KeyName,
2199                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
2200                                NULL,
2201                                NULL);
2202     Status = NtCreateKey(&SoftwareKeyHandle,
2203                          KEY_CREATE_SUB_KEY,
2204                          &ObjectAttributes,
2205                          0,
2206                          NULL,
2207                          0,
2208                          NULL);
2209     if (!NT_SUCCESS(Status))
2210     {
2211         DPRINT1("Failed to create key %wZ (Status: %08lx)\n", &KeyName, Status);
2212         return;
2213     }
2214 
2215     RtlInitUnicodeString(&KeyName, L"Microsoft");
2216     InitializeObjectAttributes(&ObjectAttributes,
2217                                &KeyName,
2218                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
2219                                SoftwareKeyHandle,
2220                                NULL);
2221     Status = NtCreateKey(&MicrosoftKeyHandle,
2222                          KEY_CREATE_SUB_KEY,
2223                          &ObjectAttributes,
2224                          0,
2225                          NULL,
2226                          0,
2227                          NULL);
2228     if (!NT_SUCCESS(Status))
2229     {
2230         DPRINT1("Failed to create key %wZ (Status: %08lx)\n", &KeyName, Status);
2231         goto Quit;
2232     }
2233 
2234     RtlInitUnicodeString(&KeyName, L"Windows NT");
2235     InitializeObjectAttributes(&ObjectAttributes,
2236                                &KeyName,
2237                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
2238                                MicrosoftKeyHandle,
2239                                NULL);
2240     Status = NtCreateKey(&WindowsNtKeyHandle,
2241                          KEY_CREATE_SUB_KEY,
2242                          &ObjectAttributes,
2243                          0,
2244                          NULL,
2245                          0,
2246                          NULL);
2247     if (!NT_SUCCESS(Status))
2248     {
2249         DPRINT1("Failed to create key %wZ (Status: %08lx)\n", &KeyName, Status);
2250         goto Quit;
2251     }
2252 
2253     RtlInitUnicodeString(&KeyName, L"CurrentVersion");
2254     InitializeObjectAttributes(&ObjectAttributes,
2255                                &KeyName,
2256                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
2257                                WindowsNtKeyHandle,
2258                                NULL);
2259     Status = NtCreateKey(&CurrentVersionKeyHandle,
2260                          KEY_CREATE_SUB_KEY | KEY_SET_VALUE,
2261                          &ObjectAttributes,
2262                          0,
2263                          NULL,
2264                          0,
2265                          NULL);
2266     if (!NT_SUCCESS(Status))
2267     {
2268         DPRINT1("Failed to create key %wZ (Status: %08lx)\n", &KeyName, Status);
2269         goto Quit;
2270     }
2271 
2272     /* Set the 'CurrentVersion' value */
2273     RtlInitUnicodeString(&ValueName, L"CurrentVersion");
2274     NtSetValueKey(CurrentVersionKeyHandle,
2275                   &ValueName,
2276                   0,
2277                   REG_SZ,
2278                   CmVersionString.Buffer,
2279                   CmVersionString.Length + sizeof(WCHAR));
2280 
2281     /* Set the 'CurrentBuildNumber' value */
2282     RtlInitUnicodeString(&ValueName, L"CurrentBuildNumber");
2283     RtlInitEmptyUnicodeString(&ValueData, Buffer, sizeof(Buffer));
2284     RtlIntegerToUnicodeString(NtBuildNumber & 0xFFFF, 10, &ValueData);
2285     NtSetValueKey(CurrentVersionKeyHandle,
2286                   &ValueName,
2287                   0,
2288                   REG_SZ,
2289                   ValueData.Buffer,
2290                   ValueData.Length + sizeof(WCHAR));
2291 
2292     /* Set the 'BuildLab' value */
2293     RtlInitUnicodeString(&ValueName, L"BuildLab");
2294     RtlInitAnsiString(&TempString, NtBuildLab);
2295     Status = RtlAnsiStringToUnicodeString(&ValueData, &TempString, FALSE);
2296     if (NT_SUCCESS(Status))
2297     {
2298         NtSetValueKey(CurrentVersionKeyHandle,
2299                       &ValueName,
2300                       0,
2301                       REG_SZ,
2302                       ValueData.Buffer,
2303                       ValueData.Length + sizeof(WCHAR));
2304     }
2305 
2306     /* Set the 'CurrentType' value */
2307     RtlInitUnicodeString(&ValueName, L"CurrentType");
2308     RtlStringCbPrintfW(Buffer, sizeof(Buffer),
2309                        L"%s %s",
2310 #ifdef CONFIG_SMP
2311                        L"Multiprocessor"
2312 #else
2313                        L"Uniprocessor"
2314 #endif
2315                        ,
2316 #if (DBG == 1)
2317                        L"Checked"
2318 #else
2319                        L"Free"
2320 #endif
2321                        );
2322     RtlInitUnicodeString(&ValueData, Buffer);
2323     NtSetValueKey(CurrentVersionKeyHandle,
2324                   &ValueName,
2325                   0,
2326                   REG_SZ,
2327                   ValueData.Buffer,
2328                   ValueData.Length + sizeof(WCHAR));
2329 
2330     /* Set the 'CSDVersion' value */
2331     RtlInitUnicodeString(&ValueName, L"CSDVersion");
2332     if (CmCSDVersionString.Length != 0)
2333     {
2334         NtSetValueKey(CurrentVersionKeyHandle,
2335                       &ValueName,
2336                       0,
2337                       REG_SZ,
2338                       CmCSDVersionString.Buffer,
2339                       CmCSDVersionString.Length + sizeof(WCHAR));
2340     }
2341     else
2342     {
2343         NtDeleteValueKey(CurrentVersionKeyHandle, &ValueName);
2344     }
2345 
2346     /* Set the 'CSDBuildNumber' value */
2347     RtlInitUnicodeString(&ValueName, L"CSDBuildNumber");
2348     if (CmNtSpBuildNumber != 0)
2349     {
2350         RtlInitEmptyUnicodeString(&ValueData, Buffer, sizeof(Buffer));
2351         RtlIntegerToUnicodeString(CmNtSpBuildNumber, 10, &ValueData);
2352         NtSetValueKey(CurrentVersionKeyHandle,
2353                       &ValueName,
2354                       0,
2355                       REG_SZ,
2356                       ValueData.Buffer,
2357                       ValueData.Length + sizeof(WCHAR));
2358     }
2359     else
2360     {
2361         NtDeleteValueKey(CurrentVersionKeyHandle, &ValueName);
2362     }
2363 
2364     /* Set the 'SystemRoot' value */
2365     RtlInitUnicodeString(&ValueName, L"SystemRoot");
2366     NtSetValueKey(CurrentVersionKeyHandle,
2367                   &ValueName,
2368                   0,
2369                   REG_SZ,
2370                   NtSystemRoot.Buffer,
2371                   NtSystemRoot.Length + sizeof(WCHAR));
2372 
2373 Quit:
2374     /* Close the keys */
2375     if (CurrentVersionKeyHandle != NULL)
2376         NtClose(CurrentVersionKeyHandle);
2377 
2378     if (WindowsNtKeyHandle != NULL)
2379         NtClose(WindowsNtKeyHandle);
2380 
2381     if (MicrosoftKeyHandle != NULL)
2382         NtClose(MicrosoftKeyHandle);
2383 
2384     if (SoftwareKeyHandle != NULL)
2385         NtClose(SoftwareKeyHandle);
2386 }
2387 
2388 /* EOF */
2389