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