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