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