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