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