2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/config/cmsysini.c
5 * PURPOSE: Configuration Manager - System Initialization Code
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 * Alex Ionescu (alex.ionescu@reactos.org)
10 /* INCLUDES *******************************************************************/
16 POBJECT_TYPE CmpKeyObjectType
;
17 PCMHIVE CmiVolatileHive
;
18 LIST_ENTRY CmpHiveListHead
;
19 ERESOURCE CmpRegistryLock
;
20 KGUARDED_MUTEX CmpSelfHealQueueLock
;
21 LIST_ENTRY CmpSelfHealQueueListHead
;
22 KEVENT CmpLoadWorkerEvent
;
23 LONG CmpLoadWorkerIncrement
;
24 PEPROCESS CmpSystemProcess
;
25 BOOLEAN HvShutdownComplete
;
26 PVOID CmpRegistryLockCallerCaller
, CmpRegistryLockCaller
;
27 BOOLEAN CmpFlushOnLockRelease
;
28 BOOLEAN CmpSpecialBootCondition
;
30 BOOLEAN CmpWasSetupBoot
;
31 BOOLEAN CmpProfileLoaded
;
32 BOOLEAN CmpNoVolatileCreates
;
33 ULONG CmpTraceLevel
= 0;
35 extern LONG CmpFlushStarveWriters
;
36 extern BOOLEAN CmFirstTime
;
38 /* FUNCTIONS ******************************************************************/
43 _In_z_ PWSTR LinkKeyName
,
44 _In_z_ PWSTR TargetKeyName
)
46 OBJECT_ATTRIBUTES ObjectAttributes
;
47 UNICODE_STRING LinkKeyName_U
;
48 HANDLE TargetKeyHandle
;
53 /* Initialize the object attributes */
54 RtlInitUnicodeString(&LinkKeyName_U
, LinkKeyName
);
55 InitializeObjectAttributes(&ObjectAttributes
,
57 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
61 /* Create the link key */
62 Status
= ZwCreateKey(&TargetKeyHandle
,
67 REG_OPTION_VOLATILE
| REG_OPTION_CREATE_LINK
,
69 if (!NT_SUCCESS(Status
))
71 DPRINT1("CM: CmpLinkKeyToHive: couldn't create %S Status = 0x%lx\n",
76 /* Check if the new key was actually created */
77 if (Disposition
!= REG_CREATED_NEW_KEY
)
79 DPRINT1("CM: CmpLinkKeyToHive: %S already exists!\n", LinkKeyName
);
80 ZwClose(TargetKeyHandle
);
84 /* Set the target key name as link target */
85 Status
= ZwSetValueKey(TargetKeyHandle
,
86 &CmSymbolicLinkValueName
,
90 (ULONG
)wcslen(TargetKeyName
) * sizeof(WCHAR
));
92 /* Close the link key handle */
93 ObCloseHandle(TargetKeyHandle
, KernelMode
);
95 if (!NT_SUCCESS(Status
))
97 DPRINT1("CM: CmpLinkKeyToHive: couldn't create symbolic link for %S\n",
107 CmpDeleteKeyObject(PVOID DeletedObject
)
109 PCM_KEY_BODY KeyBody
= (PCM_KEY_BODY
)DeletedObject
;
110 PCM_KEY_CONTROL_BLOCK Kcb
;
111 REG_KEY_HANDLE_CLOSE_INFORMATION KeyHandleCloseInfo
;
112 REG_POST_OPERATION_INFORMATION PostOperationInfo
;
116 /* First off, prepare the handle close information callback */
117 PostOperationInfo
.Object
= KeyBody
;
118 KeyHandleCloseInfo
.Object
= KeyBody
;
119 Status
= CmiCallRegisteredCallbacks(RegNtPreKeyHandleClose
,
120 &KeyHandleCloseInfo
);
121 if (!NT_SUCCESS(Status
))
123 /* If we failed, notify the post routine */
124 PostOperationInfo
.Status
= Status
;
125 CmiCallRegisteredCallbacks(RegNtPostKeyHandleClose
, &PostOperationInfo
);
129 /* Acquire hive lock */
132 /* Make sure this is a valid key body */
133 if (KeyBody
->Type
== CM_KEY_BODY_TYPE
)
136 Kcb
= KeyBody
->KeyControlBlock
;
140 DelistKeyBodyFromKCB(KeyBody
, FALSE
);
142 /* Dereference the KCB */
143 CmpDelayDerefKeyControlBlock(Kcb
);
147 /* Release the registry lock */
150 /* Do the post callback */
151 PostOperationInfo
.Status
= STATUS_SUCCESS
;
152 CmiCallRegisteredCallbacks(RegNtPostKeyHandleClose
, &PostOperationInfo
);
157 CmpCloseKeyObject(IN PEPROCESS Process OPTIONAL
,
159 IN ACCESS_MASK GrantedAccess
,
160 IN ULONG ProcessHandleCount
,
161 IN ULONG SystemHandleCount
)
163 PCM_KEY_BODY KeyBody
= (PCM_KEY_BODY
)Object
;
166 /* Don't do anything if we're not the last handle */
167 if (SystemHandleCount
> 1) return;
169 /* Make sure we're a valid key body */
170 if (KeyBody
->Type
== CM_KEY_BODY_TYPE
)
172 /* Don't do anything if we don't have a notify block */
173 if (!KeyBody
->NotifyBlock
) return;
175 /* This shouldn't happen yet */
182 CmpQueryKeyName(IN PVOID ObjectBody
,
184 IN OUT POBJECT_NAME_INFORMATION ObjectNameInfo
,
186 OUT PULONG ReturnLength
,
187 IN KPROCESSOR_MODE PreviousMode
)
189 PUNICODE_STRING KeyName
;
191 NTSTATUS Status
= STATUS_SUCCESS
;
192 PCM_KEY_BODY KeyBody
= (PCM_KEY_BODY
)ObjectBody
;
193 PCM_KEY_CONTROL_BLOCK Kcb
= KeyBody
->KeyControlBlock
;
195 /* Acquire hive lock */
198 /* Lock KCB shared */
199 CmpAcquireKcbLockShared(Kcb
);
201 /* Check if it's a deleted block */
204 /* Release the locks */
205 CmpReleaseKcbLock(Kcb
);
208 /* Let the caller know it's deleted */
209 return STATUS_KEY_DELETED
;
213 KeyName
= CmpConstructName(Kcb
);
215 /* Release the locks */
216 CmpReleaseKcbLock(Kcb
);
219 /* Check if we got the name */
220 if (!KeyName
) return STATUS_INSUFFICIENT_RESOURCES
;
222 /* Set the returned length */
223 *ReturnLength
= KeyName
->Length
+ sizeof(OBJECT_NAME_INFORMATION
) + sizeof(WCHAR
);
225 /* Calculate amount of bytes to copy into the buffer */
226 BytesToCopy
= KeyName
->Length
+ sizeof(WCHAR
);
228 /* Check if the provided buffer is too small to fit even anything */
229 if ((Length
<= sizeof(OBJECT_NAME_INFORMATION
)) ||
230 ((Length
< (*ReturnLength
)) && (BytesToCopy
< sizeof(WCHAR
))))
232 /* Free the buffer allocated by CmpConstructName */
233 ExFreePoolWithTag(KeyName
, TAG_CM
);
235 /* Return buffer length failure without writing anything there because nothing fits */
236 return STATUS_INFO_LENGTH_MISMATCH
;
239 /* Check if the provided buffer can be partially written */
240 if (Length
< (*ReturnLength
))
242 /* Yes, indicate so in the return status */
243 Status
= STATUS_INFO_LENGTH_MISMATCH
;
245 /* Calculate amount of bytes which the provided buffer could handle */
246 BytesToCopy
= Length
- sizeof(OBJECT_NAME_INFORMATION
);
249 /* Remove the null termination character from the size */
250 BytesToCopy
-= sizeof(WCHAR
);
252 /* Fill in the result */
255 /* Return data to user */
256 ObjectNameInfo
->Name
.Buffer
= (PWCHAR
)(ObjectNameInfo
+ 1);
257 ObjectNameInfo
->Name
.MaximumLength
= KeyName
->Length
;
258 ObjectNameInfo
->Name
.Length
= KeyName
->Length
;
260 /* Copy string content*/
261 RtlCopyMemory(ObjectNameInfo
->Name
.Buffer
,
265 /* Null terminate it */
266 ObjectNameInfo
->Name
.Buffer
[BytesToCopy
/ sizeof(WCHAR
)] = 0;
268 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
271 Status
= _SEH2_GetExceptionCode();
275 /* Free the buffer allocated by CmpConstructName */
276 ExFreePoolWithTag(KeyName
, TAG_CM
);
284 CmpInitHiveFromFile(IN PCUNICODE_STRING HiveName
,
290 ULONG HiveDisposition
, LogDisposition
;
291 HANDLE FileHandle
= NULL
, LogHandle
= NULL
;
293 ULONG Operation
, FileType
;
300 /* Open or create the hive files */
301 Status
= CmpOpenHiveFiles(HiveName
,
311 if (!NT_SUCCESS(Status
)) return Status
;
313 /* Check if we have a log handle */
314 FileType
= (LogHandle
) ? HFILE_TYPE_LOG
: HFILE_TYPE_PRIMARY
;
316 /* Check if we created or opened the hive */
317 if (HiveDisposition
== FILE_CREATED
)
319 /* Do a create operation */
320 Operation
= HINIT_CREATE
;
325 /* Open it as a file */
326 Operation
= HINIT_FILE
;
330 /* Check if we're sharing hives */
331 if (CmpShareSystemHives
)
333 /* Then force using the primary hive */
334 FileType
= HFILE_TYPE_PRIMARY
;
337 /* Get rid of the log handle */
343 /* Check if we're too late */
344 if (HvShutdownComplete
)
348 if (LogHandle
) ZwClose(LogHandle
);
349 return STATUS_TOO_LATE
;
352 /* Initialize the hive */
353 Status
= CmpInitializeHive(&NewHive
,
363 if (!NT_SUCCESS(Status
))
367 if (LogHandle
) ZwClose(LogHandle
);
371 /* Success, return hive */
374 /* Duplicate the hive name */
375 NewHive
->FileFullPath
.Buffer
= ExAllocatePoolWithTag(PagedPool
,
378 if (NewHive
->FileFullPath
.Buffer
)
380 /* Copy the string */
381 RtlCopyMemory(NewHive
->FileFullPath
.Buffer
,
384 NewHive
->FileFullPath
.Length
= HiveName
->Length
;
385 NewHive
->FileFullPath
.MaximumLength
= HiveName
->MaximumLength
;
389 return STATUS_SUCCESS
;
395 CmpSetSystemValues(IN PLOADER_PARAMETER_BLOCK LoaderBlock
)
397 OBJECT_ATTRIBUTES ObjectAttributes
;
398 UNICODE_STRING KeyName
, ValueName
= { 0, 0, NULL
};
399 HANDLE KeyHandle
= NULL
;
401 ASSERT(LoaderBlock
!= NULL
);
403 /* Setup attributes for loader options */
404 RtlInitUnicodeString(&KeyName
,
405 L
"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\"
407 InitializeObjectAttributes(&ObjectAttributes
,
409 OBJ_CASE_INSENSITIVE
,
412 Status
= NtOpenKey(&KeyHandle
, KEY_WRITE
, &ObjectAttributes
);
413 if (!NT_SUCCESS(Status
)) goto Quickie
;
415 /* Key opened, now write to the key */
416 RtlInitUnicodeString(&KeyName
, L
"SystemStartOptions");
417 Status
= NtSetValueKey(KeyHandle
,
421 CmpLoadOptions
.Buffer
,
422 CmpLoadOptions
.Length
);
423 if (!NT_SUCCESS(Status
)) goto Quickie
;
425 /* Setup value name for system boot device in ARC format */
426 RtlInitUnicodeString(&KeyName
, L
"SystemBootDevice");
427 RtlCreateUnicodeStringFromAsciiz(&ValueName
, LoaderBlock
->ArcBootDeviceName
);
428 Status
= NtSetValueKey(KeyHandle
,
436 /* Free the buffers */
437 RtlFreeUnicodeString(&ValueName
);
439 /* Close the key and return */
440 if (KeyHandle
) NtClose(KeyHandle
);
442 /* Return the status */
443 return (ExpInTextModeSetup
? STATUS_SUCCESS
: Status
);
449 CmpCreateHardwareProfile(HANDLE ControlSetHandle
)
451 OBJECT_ATTRIBUTES ObjectAttributes
;
452 UNICODE_STRING KeyName
;
453 HANDLE ProfilesHandle
= NULL
;
454 HANDLE ProfileHandle
= NULL
;
458 DPRINT("CmpCreateHardwareProfile()\n");
460 /* Create the Hardware Profiles key */
461 RtlInitUnicodeString(&KeyName
, L
"Hardware Profiles");
462 InitializeObjectAttributes(&ObjectAttributes
,
464 OBJ_CASE_INSENSITIVE
,
467 Status
= NtCreateKey(&ProfilesHandle
,
474 if (!NT_SUCCESS(Status
))
476 DPRINT1("Creating the Hardware Profile key failed\n");
481 ASSERT(Disposition
== REG_CREATED_NEW_KEY
);
483 /* Create the 0000 key */
484 RtlInitUnicodeString(&KeyName
, L
"0000");
485 InitializeObjectAttributes(&ObjectAttributes
,
487 OBJ_CASE_INSENSITIVE
,
490 Status
= NtCreateKey(&ProfileHandle
,
497 if (!NT_SUCCESS(Status
))
499 DPRINT1("Creating the Hardware Profile\\0000 key failed\n");
504 ASSERT(Disposition
== REG_CREATED_NEW_KEY
);
508 NtClose(ProfilesHandle
);
511 NtClose(ProfileHandle
);
513 DPRINT1("CmpCreateHardwareProfile() done\n");
521 CmpCreateControlSet(IN PLOADER_PARAMETER_BLOCK LoaderBlock
)
523 UNICODE_STRING ConfigName
= RTL_CONSTANT_STRING(L
"Control\\IDConfigDB");
524 UNICODE_STRING SelectName
=
525 RTL_CONSTANT_STRING(L
"\\Registry\\Machine\\System\\Select");
526 UNICODE_STRING KeyName
;
527 OBJECT_ATTRIBUTES ObjectAttributes
;
528 CHAR ValueInfoBuffer
[128];
529 PKEY_VALUE_FULL_INFORMATION ValueInfo
;
531 WCHAR UnicodeBuffer
[128];
532 HANDLE SelectHandle
, KeyHandle
, ConfigHandle
= NULL
, ProfileHandle
= NULL
;
533 HANDLE ParentHandle
= NULL
;
534 ULONG ControlSet
, HwProfile
;
535 ANSI_STRING TempString
;
537 ULONG ResultLength
, Disposition
;
538 PLOADER_PARAMETER_EXTENSION LoaderExtension
;
541 /* Open the select key */
542 InitializeObjectAttributes(&ObjectAttributes
,
544 OBJ_CASE_INSENSITIVE
,
547 Status
= NtOpenKey(&SelectHandle
, KEY_READ
, &ObjectAttributes
);
548 if (!NT_SUCCESS(Status
))
550 /* ReactOS Hack: Hard-code current to 001 for SetupLdr */
551 if (!LoaderBlock
->RegistryBase
)
553 /* Build the ControlSet001 key */
554 RtlInitUnicodeString(&KeyName
,
555 L
"\\Registry\\Machine\\System\\ControlSet001");
556 InitializeObjectAttributes(&ObjectAttributes
,
558 OBJ_CASE_INSENSITIVE
,
561 Status
= NtCreateKey(&KeyHandle
,
568 if (!NT_SUCCESS(Status
)) return Status
;
570 /* Create the Hardware Profile keys */
571 Status
= CmpCreateHardwareProfile(KeyHandle
);
572 if (!NT_SUCCESS(Status
))
575 /* Don't need the handle */
578 /* Use hard-coded setting */
583 /* Fail for real boots */
587 /* Open the current value */
588 RtlInitUnicodeString(&KeyName
, L
"Current");
589 Status
= NtQueryValueKey(SelectHandle
,
591 KeyValueFullInformation
,
593 sizeof(ValueInfoBuffer
),
595 NtClose(SelectHandle
);
596 if (!NT_SUCCESS(Status
)) return Status
;
598 /* Get the actual value pointer, and get the control set ID */
599 ValueInfo
= (PKEY_VALUE_FULL_INFORMATION
)ValueInfoBuffer
;
600 ControlSet
= *(PULONG
)((PUCHAR
)ValueInfo
+ ValueInfo
->DataOffset
);
602 /* Create the current control set key */
604 RtlInitUnicodeString(&KeyName
,
605 L
"\\Registry\\Machine\\System\\CurrentControlSet");
606 InitializeObjectAttributes(&ObjectAttributes
,
608 OBJ_CASE_INSENSITIVE
,
611 Status
= NtCreateKey(&KeyHandle
,
616 REG_OPTION_VOLATILE
| REG_OPTION_CREATE_LINK
,
618 if (!NT_SUCCESS(Status
)) return Status
;
621 ASSERT(Disposition
== REG_CREATED_NEW_KEY
);
623 /* Initialize the symbolic link name */
625 "\\Registry\\Machine\\System\\ControlSet%03ld",
627 RtlInitAnsiString(&TempString
, Buffer
);
629 /* Create a Unicode string out of it */
630 KeyName
.MaximumLength
= sizeof(UnicodeBuffer
);
631 KeyName
.Buffer
= UnicodeBuffer
;
632 Status
= RtlAnsiStringToUnicodeString(&KeyName
, &TempString
, FALSE
);
635 Status
= NtSetValueKey(KeyHandle
,
636 &CmSymbolicLinkValueName
,
641 if (!NT_SUCCESS(Status
)) return Status
;
643 /* Get the configuration database key */
644 InitializeObjectAttributes(&ObjectAttributes
,
646 OBJ_CASE_INSENSITIVE
,
649 Status
= NtOpenKey(&ConfigHandle
, KEY_READ
, &ObjectAttributes
);
652 /* Check if we don't have one */
653 if (!NT_SUCCESS(Status
))
655 /* Cleanup and exit */
660 /* ReactOS Hack: Hard-code current to 001 for SetupLdr */
661 if (!LoaderBlock
->RegistryBase
)
667 /* Now get the current config */
668 RtlInitUnicodeString(&KeyName
, L
"CurrentConfig");
669 Status
= NtQueryValueKey(ConfigHandle
,
671 KeyValueFullInformation
,
673 sizeof(ValueInfoBuffer
),
676 /* Set pointer to buffer */
677 ValueInfo
= (PKEY_VALUE_FULL_INFORMATION
)ValueInfoBuffer
;
679 /* Check if we failed or got a non DWORD-value */
680 if (!(NT_SUCCESS(Status
)) || (ValueInfo
->Type
!= REG_DWORD
)) goto Cleanup
;
682 /* Get the hadware profile */
683 HwProfile
= *(PULONG
)((PUCHAR
)ValueInfo
+ ValueInfo
->DataOffset
);
686 /* Open the hardware profile key */
687 RtlInitUnicodeString(&KeyName
,
688 L
"\\Registry\\Machine\\System\\CurrentControlSet"
689 L
"\\Hardware Profiles");
690 InitializeObjectAttributes(&ObjectAttributes
,
692 OBJ_CASE_INSENSITIVE
,
695 Status
= NtOpenKey(&ParentHandle
, KEY_READ
, &ObjectAttributes
);
696 if (!NT_SUCCESS(Status
))
698 /* Exit and clean up */
703 /* Build the profile name */
704 sprintf(Buffer
, "%04ld", HwProfile
);
705 RtlInitAnsiString(&TempString
, Buffer
);
707 /* Convert it to Unicode */
708 KeyName
.MaximumLength
= sizeof(UnicodeBuffer
);
709 KeyName
.Buffer
= UnicodeBuffer
;
710 Status
= RtlAnsiStringToUnicodeString(&KeyName
,
713 ASSERT(Status
== STATUS_SUCCESS
);
715 /* Open the associated key */
716 InitializeObjectAttributes(&ObjectAttributes
,
718 OBJ_CASE_INSENSITIVE
,
721 Status
= NtOpenKey(&ProfileHandle
,
722 KEY_READ
| KEY_WRITE
,
724 if (!NT_SUCCESS (Status
))
726 /* Cleanup and exit */
731 /* Check if we have a loader block extension */
732 LoaderExtension
= LoaderBlock
->Extension
;
735 DPRINT("ReactOS doesn't support NTLDR Profiles yet!\n");
738 /* Create the current hardware profile key */
739 RtlInitUnicodeString(&KeyName
,
740 L
"\\Registry\\Machine\\System\\CurrentControlSet\\"
741 L
"Hardware Profiles\\Current");
742 InitializeObjectAttributes(&ObjectAttributes
,
744 OBJ_CASE_INSENSITIVE
,
747 Status
= NtCreateKey(&KeyHandle
,
752 REG_OPTION_VOLATILE
| REG_OPTION_CREATE_LINK
,
754 if (NT_SUCCESS(Status
))
757 ASSERT(Disposition
== REG_CREATED_NEW_KEY
);
759 /* Create the profile name */
761 "\\Registry\\Machine\\System\\CurrentControlSet\\"
762 "Hardware Profiles\\%04ld",
764 RtlInitAnsiString(&TempString
, Buffer
);
766 /* Convert it to Unicode */
767 KeyName
.MaximumLength
= sizeof(UnicodeBuffer
);
768 KeyName
.Buffer
= UnicodeBuffer
;
769 Status
= RtlAnsiStringToUnicodeString(&KeyName
,
772 ASSERT(STATUS_SUCCESS
== Status
);
775 Status
= NtSetValueKey(KeyHandle
,
776 &CmSymbolicLinkValueName
,
784 /* Close every opened handle */
786 if (ConfigHandle
) NtClose(ConfigHandle
);
787 if (ProfileHandle
) NtClose(ProfileHandle
);
788 if (ParentHandle
) NtClose(ParentHandle
);
790 DPRINT("CmpCreateControlSet() done\n");
793 return STATUS_SUCCESS
;
798 CmpLinkHiveToMaster(IN PUNICODE_STRING LinkName
,
799 IN HANDLE RootDirectory
,
800 IN PCMHIVE RegistryHive
,
802 IN PSECURITY_DESCRIPTOR SecurityDescriptor
)
804 OBJECT_ATTRIBUTES ObjectAttributes
;
806 CM_PARSE_CONTEXT ParseContext
= {0};
808 PCM_KEY_BODY KeyBody
;
811 /* Setup the object attributes */
812 InitializeObjectAttributes(&ObjectAttributes
,
814 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
818 /* Setup the parse context */
819 ParseContext
.CreateLink
= TRUE
;
820 ParseContext
.CreateOperation
= TRUE
;
821 ParseContext
.ChildHive
.KeyHive
= &RegistryHive
->Hive
;
823 /* Check if we have a root keycell or if we need to create it */
827 ParseContext
.ChildHive
.KeyCell
= HCELL_NIL
;
832 ParseContext
.ChildHive
.KeyCell
= RegistryHive
->Hive
.BaseBlock
->RootCell
;
835 /* Create the link node */
836 Status
= ObOpenObjectByName(&ObjectAttributes
,
840 KEY_READ
| KEY_WRITE
,
841 (PVOID
)&ParseContext
,
843 if (!NT_SUCCESS(Status
)) return Status
;
845 /* Mark the hive as clean */
846 RegistryHive
->Hive
.DirtyFlag
= FALSE
;
848 /* ReactOS Hack: Keep alive */
849 Status
= ObReferenceObjectByHandle(KeyHandle
,
855 ASSERT(NT_SUCCESS(Status
));
857 /* Close the extra handle */
859 return STATUS_SUCCESS
;
865 CmpInitializeSystemHive(IN PLOADER_PARAMETER_BLOCK LoaderBlock
)
868 ANSI_STRING LoadString
;
873 UNICODE_STRING KeyName
;
874 PCMHIVE SystemHive
= NULL
;
875 UNICODE_STRING HiveName
= RTL_CONSTANT_STRING(L
"SYSTEM");
876 PSECURITY_DESCRIPTOR SecurityDescriptor
;
879 /* Setup the ansi string */
880 RtlInitAnsiString(&LoadString
, LoaderBlock
->LoadOptions
);
882 /* Allocate the unicode buffer */
883 Length
= LoadString
.Length
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
);
884 Buffer
= ExAllocatePoolWithTag(PagedPool
, Length
, TAG_CM
);
888 KeBugCheckEx(BAD_SYSTEM_CONFIG_INFO
, 3, 1, (ULONG_PTR
)LoaderBlock
, 0);
891 /* Setup the unicode string */
892 RtlInitEmptyUnicodeString(&CmpLoadOptions
, Buffer
, (USHORT
)Length
);
894 /* Add the load options and null-terminate */
895 RtlAnsiStringToUnicodeString(&CmpLoadOptions
, &LoadString
, FALSE
);
896 CmpLoadOptions
.Buffer
[LoadString
.Length
] = UNICODE_NULL
;
897 CmpLoadOptions
.Length
+= sizeof(WCHAR
);
899 /* Get the System Hive base address */
900 HiveBase
= LoaderBlock
->RegistryBase
;
904 Status
= CmpInitializeHive(&SystemHive
,
914 if (!NT_SUCCESS(Status
)) return FALSE
;
916 /* Set the hive filename */
917 RtlCreateUnicodeString(&SystemHive
->FileFullPath
,
918 L
"\\SystemRoot\\System32\\Config\\SYSTEM");
920 /* We imported, no need to create a new hive */
923 /* Manually set the hive as volatile, if in Live CD mode */
924 if (CmpShareSystemHives
) SystemHive
->Hive
.HiveFlags
= HIVE_VOLATILE
;
929 Status
= CmpInitializeHive(&SystemHive
,
939 if (!NT_SUCCESS(Status
)) return FALSE
;
941 /* Set the hive filename */
942 RtlCreateUnicodeString(&SystemHive
->FileFullPath
,
943 L
"\\SystemRoot\\System32\\Config\\SYSTEM");
945 /* Tell CmpLinkHiveToMaster to allocate a hive */
949 /* Save the boot type */
950 CmpBootType
= SystemHive
->Hive
.BaseBlock
->BootType
;
952 /* Are we in self-healing mode? */
955 /* Disable self-healing internally and check if boot type wanted it */
959 /* We're disabled, so bugcheck */
960 KeBugCheckEx(BAD_SYSTEM_CONFIG_INFO
,
963 (ULONG_PTR
)SystemHive
,
968 /* Create the default security descriptor */
969 SecurityDescriptor
= CmpHiveRootSecurityDescriptor();
971 /* Attach it to the system key */
972 RtlInitUnicodeString(&KeyName
, L
"\\Registry\\Machine\\SYSTEM");
973 Status
= CmpLinkHiveToMaster(&KeyName
,
979 /* Free the security descriptor */
980 ExFreePoolWithTag(SecurityDescriptor
, TAG_CMSD
);
981 if (!NT_SUCCESS(Status
)) return FALSE
;
983 /* Add the hive to the hive list */
984 CmpMachineHiveList
[3].CmHive
= SystemHive
;
993 CmpCreateObjectTypes(VOID
)
995 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer
;
997 GENERIC_MAPPING CmpKeyMapping
= {KEY_READ
,
1003 /* Initialize the Key object type */
1004 RtlZeroMemory(&ObjectTypeInitializer
, sizeof(ObjectTypeInitializer
));
1005 RtlInitUnicodeString(&Name
, L
"Key");
1006 ObjectTypeInitializer
.Length
= sizeof(ObjectTypeInitializer
);
1007 ObjectTypeInitializer
.DefaultPagedPoolCharge
= sizeof(CM_KEY_BODY
);
1008 ObjectTypeInitializer
.GenericMapping
= CmpKeyMapping
;
1009 ObjectTypeInitializer
.PoolType
= PagedPool
;
1010 ObjectTypeInitializer
.ValidAccessMask
= KEY_ALL_ACCESS
;
1011 ObjectTypeInitializer
.UseDefaultObject
= TRUE
;
1012 ObjectTypeInitializer
.DeleteProcedure
= CmpDeleteKeyObject
;
1013 ObjectTypeInitializer
.ParseProcedure
= CmpParseKey
;
1014 ObjectTypeInitializer
.SecurityProcedure
= CmpSecurityMethod
;
1015 ObjectTypeInitializer
.QueryNameProcedure
= CmpQueryKeyName
;
1016 ObjectTypeInitializer
.CloseProcedure
= CmpCloseKeyObject
;
1017 ObjectTypeInitializer
.SecurityRequired
= TRUE
;
1018 ObjectTypeInitializer
.InvalidAttributes
= OBJ_EXCLUSIVE
| OBJ_PERMANENT
;
1021 return ObCreateObjectType(&Name
, &ObjectTypeInitializer
, NULL
, &CmpKeyObjectType
);
1027 CmpCreateRootNode(IN PHHIVE Hive
,
1029 OUT PHCELL_INDEX Index
)
1031 UNICODE_STRING KeyName
;
1032 PCM_KEY_NODE KeyCell
;
1035 /* Initialize the node name and allocate it */
1036 RtlInitUnicodeString(&KeyName
, Name
);
1037 *Index
= HvAllocateCell(Hive
,
1038 FIELD_OFFSET(CM_KEY_NODE
, Name
) +
1039 CmpNameSize(Hive
, &KeyName
),
1042 if (*Index
== HCELL_NIL
) return FALSE
;
1044 /* Set the cell index and get the data */
1045 Hive
->BaseBlock
->RootCell
= *Index
;
1046 KeyCell
= (PCM_KEY_NODE
)HvGetCell(Hive
, *Index
);
1047 if (!KeyCell
) return FALSE
;
1049 /* Setup the cell */
1050 KeyCell
->Signature
= CM_KEY_NODE_SIGNATURE
;
1051 KeyCell
->Flags
= KEY_HIVE_ENTRY
| KEY_NO_DELETE
;
1052 KeQuerySystemTime(&KeyCell
->LastWriteTime
);
1053 KeyCell
->Parent
= HCELL_NIL
;
1054 KeyCell
->SubKeyCounts
[Stable
] = 0;
1055 KeyCell
->SubKeyCounts
[Volatile
] = 0;
1056 KeyCell
->SubKeyLists
[Stable
] = HCELL_NIL
;
1057 KeyCell
->SubKeyLists
[Volatile
] = HCELL_NIL
;
1058 KeyCell
->ValueList
.Count
= 0;
1059 KeyCell
->ValueList
.List
= HCELL_NIL
;
1060 KeyCell
->Security
= HCELL_NIL
;
1061 KeyCell
->Class
= HCELL_NIL
;
1062 KeyCell
->ClassLength
= 0;
1063 KeyCell
->MaxNameLen
= 0;
1064 KeyCell
->MaxClassLen
= 0;
1065 KeyCell
->MaxValueNameLen
= 0;
1066 KeyCell
->MaxValueDataLen
= 0;
1068 /* Copy the name (this will also set the length) */
1069 KeyCell
->NameLength
= CmpCopyName(Hive
, KeyCell
->Name
, &KeyName
);
1071 /* Check if the name was compressed and set the flag if so */
1072 if (KeyCell
->NameLength
< KeyName
.Length
)
1073 KeyCell
->Flags
|= KEY_COMP_NAME
;
1075 /* Return success */
1076 HvReleaseCell(Hive
, *Index
);
1083 CmpCreateRegistryRoot(VOID
)
1085 UNICODE_STRING KeyName
;
1086 OBJECT_ATTRIBUTES ObjectAttributes
;
1087 PCM_KEY_BODY RootKey
;
1088 HCELL_INDEX RootIndex
;
1090 PCM_KEY_NODE KeyCell
;
1091 PSECURITY_DESCRIPTOR SecurityDescriptor
;
1092 PCM_KEY_CONTROL_BLOCK Kcb
;
1095 /* Setup the root node */
1096 if (!CmpCreateRootNode(&CmiVolatileHive
->Hive
, L
"REGISTRY", &RootIndex
))
1102 /* Create '\Registry' key. */
1103 RtlInitUnicodeString(&KeyName
, L
"\\REGISTRY");
1104 SecurityDescriptor
= CmpHiveRootSecurityDescriptor();
1105 InitializeObjectAttributes(&ObjectAttributes
,
1107 OBJ_CASE_INSENSITIVE
,
1109 SecurityDescriptor
);
1110 Status
= ObCreateObject(KernelMode
,
1115 sizeof(CM_KEY_BODY
),
1119 ExFreePoolWithTag(SecurityDescriptor
, TAG_CMSD
);
1120 if (!NT_SUCCESS(Status
)) return FALSE
;
1122 /* Sanity check, and get the key cell */
1123 ASSERT((&CmiVolatileHive
->Hive
)->ReleaseCellRoutine
== NULL
);
1124 KeyCell
= (PCM_KEY_NODE
)HvGetCell(&CmiVolatileHive
->Hive
, RootIndex
);
1125 if (!KeyCell
) return FALSE
;
1127 /* Create the KCB */
1128 RtlInitUnicodeString(&KeyName
, L
"\\REGISTRY");
1129 Kcb
= CmpCreateKeyControlBlock(&CmiVolatileHive
->Hive
,
1137 ObDereferenceObject(RootKey
);
1141 /* Initialize the object */
1142 RootKey
->KeyControlBlock
= Kcb
;
1143 RootKey
->Type
= CM_KEY_BODY_TYPE
;
1144 RootKey
->NotifyBlock
= NULL
;
1145 RootKey
->ProcessID
= PsGetCurrentProcessId();
1148 EnlistKeyBodyWithKCB(RootKey
, 0);
1150 /* Insert the key into the namespace */
1151 Status
= ObInsertObject(RootKey
,
1156 &CmpRegistryRootHandle
);
1157 if (!NT_SUCCESS(Status
))
1159 ObDereferenceObject(RootKey
);
1163 /* Reference the key again so that we never lose it */
1164 Status
= ObReferenceObjectByHandle(CmpRegistryRootHandle
,
1170 if (!NT_SUCCESS(Status
))
1172 ObDereferenceObject(RootKey
);
1176 /* Completely sucessful */
1182 CmpGetRegistryPath(IN PWCHAR ConfigPath
)
1184 OBJECT_ATTRIBUTES ObjectAttributes
;
1187 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo
;
1188 UNICODE_STRING KeyName
= RTL_CONSTANT_STRING(L
"\\Registry\\Machine\\HARDWARE");
1189 UNICODE_STRING ValueName
= RTL_CONSTANT_STRING(L
"InstallPath");
1190 ULONG BufferSize
, ResultSize
;
1192 /* Check if we are booted in setup */
1193 if (ExpInTextModeSetup
)
1195 /* Setup the object attributes */
1196 InitializeObjectAttributes(&ObjectAttributes
,
1198 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
1202 Status
= ZwOpenKey(&KeyHandle
,
1205 if (!NT_SUCCESS(Status
)) return Status
;
1207 /* Allocate the buffer */
1208 BufferSize
= sizeof(KEY_VALUE_PARTIAL_INFORMATION
) + 4096;
1209 ValueInfo
= ExAllocatePoolWithTag(PagedPool
, BufferSize
, TAG_CM
);
1214 return STATUS_INSUFFICIENT_RESOURCES
;
1217 /* Query the value */
1218 Status
= ZwQueryValueKey(KeyHandle
,
1220 KeyValuePartialInformation
,
1225 if (!NT_SUCCESS(Status
))
1228 ExFreePoolWithTag(ValueInfo
, TAG_CM
);
1232 /* Copy the config path and null-terminate it */
1233 RtlCopyMemory(ConfigPath
,
1235 ValueInfo
->DataLength
);
1236 ConfigPath
[ValueInfo
->DataLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1237 ExFreePoolWithTag(ValueInfo
, TAG_CM
);
1241 /* Just use default path */
1242 wcscpy(ConfigPath
, L
"\\SystemRoot");
1245 /* Add registry path */
1246 wcscat(ConfigPath
, L
"\\System32\\Config\\");
1249 return STATUS_SUCCESS
;
1254 CmpLoadHiveThread(IN PVOID StartContext
)
1256 WCHAR FileBuffer
[MAX_PATH
], RegBuffer
[MAX_PATH
], ConfigPath
[MAX_PATH
];
1257 UNICODE_STRING TempName
, FileName
, RegName
;
1258 ULONG i
, ErrorResponse
, WorkerCount
, Length
;
1261 ULONG PrimaryDisposition
, SecondaryDisposition
, ClusterSize
;
1263 HANDLE PrimaryHandle
= NULL
, LogHandle
= NULL
;
1264 NTSTATUS Status
= STATUS_SUCCESS
;
1265 PVOID ErrorParameters
;
1268 /* Get the hive index, make sure it makes sense */
1269 i
= PtrToUlong(StartContext
);
1270 ASSERT(CmpMachineHiveList
[i
].Name
!= NULL
);
1272 /* We were started */
1273 CmpMachineHiveList
[i
].ThreadStarted
= TRUE
;
1275 /* Build the file name and registry name strings */
1276 RtlInitEmptyUnicodeString(&FileName
, FileBuffer
, MAX_PATH
);
1277 RtlInitEmptyUnicodeString(&RegName
, RegBuffer
, MAX_PATH
);
1279 /* Now build the system root path */
1280 CmpGetRegistryPath(ConfigPath
);
1281 RtlInitUnicodeString(&TempName
, ConfigPath
);
1282 RtlAppendStringToString((PSTRING
)&FileName
, (PSTRING
)&TempName
);
1283 FileStart
= FileName
.Length
;
1285 /* And build the registry root path */
1286 RtlInitUnicodeString(&TempName
, L
"\\REGISTRY\\");
1287 RtlAppendStringToString((PSTRING
)&RegName
, (PSTRING
)&TempName
);
1288 //RegStart = RegName.Length;
1290 /* Build the base name */
1291 RtlInitUnicodeString(&TempName
, CmpMachineHiveList
[i
].BaseName
);
1292 RtlAppendStringToString((PSTRING
)&RegName
, (PSTRING
)&TempName
);
1294 /* Check if this is a child of the root */
1295 if (RegName
.Buffer
[RegName
.Length
/ sizeof(WCHAR
) - 1] == '\\')
1297 /* Then setup the whole name */
1298 RtlInitUnicodeString(&TempName
, CmpMachineHiveList
[i
].Name
);
1299 RtlAppendStringToString((PSTRING
)&RegName
, (PSTRING
)&TempName
);
1302 /* Now add the rest of the file name */
1303 RtlInitUnicodeString(&TempName
, CmpMachineHiveList
[i
].Name
);
1304 FileName
.Length
= FileStart
;
1305 RtlAppendStringToString((PSTRING
)&FileName
, (PSTRING
)&TempName
);
1306 if (!CmpMachineHiveList
[i
].CmHive
)
1308 /* We need to allocate a new hive structure */
1309 CmpMachineHiveList
[i
].Allocate
= TRUE
;
1311 /* Load the hive file */
1312 Status
= CmpInitHiveFromFile(&FileName
,
1313 CmpMachineHiveList
[i
].HHiveFlags
,
1315 &CmpMachineHiveList
[i
].Allocate
,
1317 if (!(NT_SUCCESS(Status
)) ||
1318 (!(CmHive
->FileHandles
[HFILE_TYPE_LOG
]) && !(CmpMiniNTBoot
))) // HACK
1320 /* We failed or couldn't get a log file, raise a hard error */
1321 ErrorParameters
= &FileName
;
1322 NtRaiseHardError(STATUS_CANNOT_LOAD_REGISTRY_FILE
,
1325 (PULONG_PTR
)&ErrorParameters
,
1330 /* Set the hive flags and newly allocated hive pointer */
1331 CmHive
->Flags
= CmpMachineHiveList
[i
].CmHiveFlags
;
1332 CmpMachineHiveList
[i
].CmHive2
= CmHive
;
1336 /* We already have a hive, is it volatile? */
1337 CmHive
= CmpMachineHiveList
[i
].CmHive
;
1338 if (!(CmHive
->Hive
.HiveFlags
& HIVE_VOLATILE
))
1340 /* It's now, open the hive file and log */
1341 Status
= CmpOpenHiveFiles(&FileName
,
1345 &PrimaryDisposition
,
1346 &SecondaryDisposition
,
1351 if (!(NT_SUCCESS(Status
)) || !(LogHandle
))
1353 /* Couldn't open the hive or its log file, raise a hard error */
1354 ErrorParameters
= &FileName
;
1355 NtRaiseHardError(STATUS_CANNOT_LOAD_REGISTRY_FILE
,
1358 (PULONG_PTR
)&ErrorParameters
,
1362 /* And bugcheck for posterity's sake */
1363 KeBugCheckEx(BAD_SYSTEM_CONFIG_INFO
, 9, 0, i
, Status
);
1366 /* Save the file handles. This should remove our sync hacks */
1367 CmHive
->FileHandles
[HFILE_TYPE_LOG
] = LogHandle
;
1368 CmHive
->FileHandles
[HFILE_TYPE_PRIMARY
] = PrimaryHandle
;
1370 /* Allow lazy flushing since the handles are there -- remove sync hacks */
1371 //ASSERT(CmHive->Hive.HiveFlags & HIVE_NOLAZYFLUSH);
1372 CmHive
->Hive
.HiveFlags
&= ~HIVE_NOLAZYFLUSH
;
1374 /* Get the real size of the hive */
1375 Length
= CmHive
->Hive
.Storage
[Stable
].Length
+ HBLOCK_SIZE
;
1377 /* Check if the cluster size doesn't match */
1378 if (CmHive
->Hive
.Cluster
!= ClusterSize
) ASSERT(FALSE
);
1380 /* Set the file size */
1381 DPRINT("FIXME: Should set file size: %lx\n", Length
);
1382 //if (!CmpFileSetSize((PHHIVE)CmHive, HFILE_TYPE_PRIMARY, Length, Length))
1384 /* This shouldn't fail */
1388 /* Another thing we don't support is NTLDR-recovery */
1389 if (CmHive
->Hive
.BaseBlock
->BootRecover
) ASSERT(FALSE
);
1391 /* Finally, set our allocated hive to the same hive we've had */
1392 CmpMachineHiveList
[i
].CmHive2
= CmHive
;
1393 ASSERT(CmpMachineHiveList
[i
].CmHive
== CmpMachineHiveList
[i
].CmHive2
);
1398 CmpMachineHiveList
[i
].ThreadFinished
= TRUE
;
1400 /* Check if we're the last worker */
1401 WorkerCount
= InterlockedIncrement(&CmpLoadWorkerIncrement
);
1402 if (WorkerCount
== CM_NUMBER_OF_MACHINE_HIVES
)
1404 /* Signal the event */
1405 KeSetEvent(&CmpLoadWorkerEvent
, 0, FALSE
);
1408 /* Kill the thread */
1409 PsTerminateSystemThread(Status
);
1414 CmpInitializeHiveList(IN USHORT Flag
)
1416 WCHAR FileBuffer
[MAX_PATH
], RegBuffer
[MAX_PATH
], ConfigPath
[MAX_PATH
];
1417 UNICODE_STRING TempName
, FileName
, RegName
;
1422 PSECURITY_DESCRIPTOR SecurityDescriptor
;
1425 /* Allow writing for now */
1428 /* Build the file name and registry name strings */
1429 RtlInitEmptyUnicodeString(&FileName
, FileBuffer
, MAX_PATH
);
1430 RtlInitEmptyUnicodeString(&RegName
, RegBuffer
, MAX_PATH
);
1432 /* Now build the system root path */
1433 CmpGetRegistryPath(ConfigPath
);
1434 RtlInitUnicodeString(&TempName
, ConfigPath
);
1435 RtlAppendStringToString((PSTRING
)&FileName
, (PSTRING
)&TempName
);
1437 /* And build the registry root path */
1438 RtlInitUnicodeString(&TempName
, L
"\\REGISTRY\\");
1439 RtlAppendStringToString((PSTRING
)&RegName
, (PSTRING
)&TempName
);
1440 RegStart
= RegName
.Length
;
1442 /* Setup the event to synchronize workers */
1443 KeInitializeEvent(&CmpLoadWorkerEvent
, SynchronizationEvent
, FALSE
);
1445 /* Enter special boot condition */
1446 CmpSpecialBootCondition
= TRUE
;
1448 /* Create the SD for the root hives */
1449 SecurityDescriptor
= CmpHiveRootSecurityDescriptor();
1451 /* Loop every hive we care about */
1452 for (i
= 0; i
< CM_NUMBER_OF_MACHINE_HIVES
; i
++)
1454 /* Make sure the list is setup */
1455 ASSERT(CmpMachineHiveList
[i
].Name
!= NULL
);
1457 /* Create a thread to handle this hive */
1458 Status
= PsCreateSystemThread(&Thread
,
1465 if (NT_SUCCESS(Status
))
1467 /* We don't care about the handle -- the thread self-terminates */
1472 /* Can't imagine this happening */
1473 KeBugCheckEx(BAD_SYSTEM_CONFIG_INFO
, 9, 3, i
, Status
);
1477 /* Make sure we've reached the end of the list */
1478 ASSERT(CmpMachineHiveList
[i
].Name
== NULL
);
1480 /* Wait for hive loading to finish */
1481 KeWaitForSingleObject(&CmpLoadWorkerEvent
,
1487 /* Exit the special boot condition and make sure all workers completed */
1488 CmpSpecialBootCondition
= FALSE
;
1489 ASSERT(CmpLoadWorkerIncrement
== CM_NUMBER_OF_MACHINE_HIVES
);
1491 /* Loop hives again */
1492 for (i
= 0; i
< CM_NUMBER_OF_MACHINE_HIVES
; i
++)
1494 /* Make sure the thread ran and finished */
1495 ASSERT(CmpMachineHiveList
[i
].ThreadFinished
== TRUE
);
1496 ASSERT(CmpMachineHiveList
[i
].ThreadStarted
== TRUE
);
1498 /* Check if this was a new hive */
1499 if (!CmpMachineHiveList
[i
].CmHive
)
1501 /* Make sure we allocated something */
1502 ASSERT(CmpMachineHiveList
[i
].CmHive2
!= NULL
);
1504 /* Build the base name */
1505 RegName
.Length
= RegStart
;
1506 RtlInitUnicodeString(&TempName
, CmpMachineHiveList
[i
].BaseName
);
1507 RtlAppendStringToString((PSTRING
)&RegName
, (PSTRING
)&TempName
);
1509 /* Check if this is a child of the root */
1510 if (RegName
.Buffer
[RegName
.Length
/ sizeof(WCHAR
) - 1] == '\\')
1512 /* Then setup the whole name */
1513 RtlInitUnicodeString(&TempName
, CmpMachineHiveList
[i
].Name
);
1514 RtlAppendStringToString((PSTRING
)&RegName
, (PSTRING
)&TempName
);
1517 /* Now link the hive to its master */
1518 Status
= CmpLinkHiveToMaster(&RegName
,
1520 CmpMachineHiveList
[i
].CmHive2
,
1521 CmpMachineHiveList
[i
].Allocate
,
1522 SecurityDescriptor
);
1523 if (Status
!= STATUS_SUCCESS
)
1525 /* Linking needs to work */
1526 KeBugCheckEx(CONFIG_LIST_FAILED
, 11, Status
, i
, (ULONG_PTR
)&RegName
);
1529 /* Check if we had to allocate a new hive */
1530 if (CmpMachineHiveList
[i
].Allocate
)
1532 /* Sync the new hive */
1533 //HvSyncHive((PHHIVE)(CmpMachineHiveList[i].CmHive2));
1537 /* Check if we created a new hive */
1538 if (CmpMachineHiveList
[i
].CmHive2
)
1540 /* Add to HiveList key */
1541 CmpAddToHiveFileList(CmpMachineHiveList
[i
].CmHive2
);
1545 /* Get rid of the SD */
1546 ExFreePoolWithTag(SecurityDescriptor
, TAG_CMSD
);
1548 /* Link SECURITY to SAM */
1549 CmpLinkKeyToHive(L
"\\Registry\\Machine\\Security\\SAM",
1550 L
"\\Registry\\Machine\\SAM\\SAM");
1552 /* Link S-1-5-18 to .Default */
1553 CmpNoVolatileCreates
= FALSE
;
1554 CmpLinkKeyToHive(L
"\\Registry\\User\\S-1-5-18",
1555 L
"\\Registry\\User\\.Default");
1556 CmpNoVolatileCreates
= TRUE
;
1564 OBJECT_ATTRIBUTES ObjectAttributes
;
1565 UNICODE_STRING KeyName
;
1568 PCMHIVE HardwareHive
;
1569 PSECURITY_DESCRIPTOR SecurityDescriptor
;
1572 /* Check if this is PE-boot */
1573 if (InitIsWinPEMode
)
1575 /* Set registry to PE mode */
1576 CmpMiniNTBoot
= TRUE
;
1577 CmpShareSystemHives
= TRUE
;
1580 /* Initialize the hive list and lock */
1581 InitializeListHead(&CmpHiveListHead
);
1582 ExInitializePushLock(&CmpHiveListHeadLock
);
1583 ExInitializePushLock(&CmpLoadHiveLock
);
1585 /* Initialize registry lock */
1586 ExInitializeResourceLite(&CmpRegistryLock
);
1588 /* Initialize the cache */
1589 CmpInitializeCache();
1591 /* Initialize allocation and delayed dereferencing */
1592 CmpInitCmPrivateAlloc();
1593 CmpInitCmPrivateDelayAlloc();
1594 CmpInitDelayDerefKCBEngine();
1596 /* Initialize callbacks */
1599 /* Initialize self healing */
1600 KeInitializeGuardedMutex(&CmpSelfHealQueueLock
);
1601 InitializeListHead(&CmpSelfHealQueueListHead
);
1603 /* Save the current process and lock the registry */
1604 CmpSystemProcess
= PsGetCurrentProcess();
1606 /* Create the key object types */
1607 Status
= CmpCreateObjectTypes();
1608 if (!NT_SUCCESS(Status
))
1611 KeBugCheckEx(CONFIG_INITIALIZATION_FAILED
, 1, 1, Status
, 0);
1614 /* Build the master hive */
1615 Status
= CmpInitializeHive(&CmiVolatileHive
,
1625 if (!NT_SUCCESS(Status
))
1628 KeBugCheckEx(CONFIG_INITIALIZATION_FAILED
, 1, 2, Status
, 0);
1631 /* Create the \REGISTRY key node */
1632 if (!CmpCreateRegistryRoot())
1635 KeBugCheckEx(CONFIG_INITIALIZATION_FAILED
, 1, 3, 0, 0);
1638 /* Create the default security descriptor */
1639 SecurityDescriptor
= CmpHiveRootSecurityDescriptor();
1641 /* Create '\Registry\Machine' key. */
1642 RtlInitUnicodeString(&KeyName
, L
"\\REGISTRY\\MACHINE");
1643 InitializeObjectAttributes(&ObjectAttributes
,
1645 OBJ_CASE_INSENSITIVE
,
1647 SecurityDescriptor
);
1648 Status
= NtCreateKey(&KeyHandle
,
1649 KEY_READ
| KEY_WRITE
,
1655 if (!NT_SUCCESS(Status
))
1658 KeBugCheckEx(CONFIG_INITIALIZATION_FAILED
, 1, 5, Status
, 0);
1661 /* Close the handle */
1664 /* Create '\Registry\User' key. */
1665 RtlInitUnicodeString(&KeyName
, L
"\\REGISTRY\\USER");
1666 InitializeObjectAttributes(&ObjectAttributes
,
1668 OBJ_CASE_INSENSITIVE
,
1670 SecurityDescriptor
);
1671 Status
= NtCreateKey(&KeyHandle
,
1672 KEY_READ
| KEY_WRITE
,
1678 if (!NT_SUCCESS(Status
))
1681 KeBugCheckEx(CONFIG_INITIALIZATION_FAILED
, 1, 6, Status
, 0);
1684 /* Close the handle */
1687 /* After this point, do not allow creating keys in the master hive */
1688 CmpNoVolatileCreates
= TRUE
;
1690 /* Initialize the system hive */
1691 if (!CmpInitializeSystemHive(KeLoaderBlock
))
1694 KeBugCheckEx(CONFIG_INITIALIZATION_FAILED
, 1, 7, 0, 0);
1697 /* Create the 'CurrentControlSet' link. */
1698 Status
= CmpCreateControlSet(KeLoaderBlock
);
1699 if (!NT_SUCCESS(Status
))
1702 KeBugCheckEx(CONFIG_INITIALIZATION_FAILED
, 1, 8, Status
, 0);
1705 /* Create the hardware hive */
1706 Status
= CmpInitializeHive(&HardwareHive
,
1716 if (!NT_SUCCESS(Status
))
1719 KeBugCheckEx(CONFIG_INITIALIZATION_FAILED
, 1, 11, Status
, 0);
1722 /* Add the hive to the hive list */
1723 CmpMachineHiveList
[0].CmHive
= HardwareHive
;
1725 /* Attach it to the machine key */
1726 RtlInitUnicodeString(&KeyName
, L
"\\Registry\\Machine\\HARDWARE");
1727 Status
= CmpLinkHiveToMaster(&KeyName
,
1731 SecurityDescriptor
);
1732 if (!NT_SUCCESS(Status
))
1735 KeBugCheckEx(CONFIG_INITIALIZATION_FAILED
, 1, 12, Status
, 0);
1738 /* Add to HiveList key */
1739 CmpAddToHiveFileList(HardwareHive
);
1741 /* Free the security descriptor */
1742 ExFreePoolWithTag(SecurityDescriptor
, TAG_CMSD
);
1744 /* Fill out the Hardware key with the ARC Data from the Loader */
1745 Status
= CmpInitializeHardwareConfiguration(KeLoaderBlock
);
1746 if (!NT_SUCCESS(Status
))
1749 KeBugCheckEx(CONFIG_INITIALIZATION_FAILED
, 1, 13, Status
, 0);
1752 /* Initialize machine-dependent information into the registry */
1753 Status
= CmpInitializeMachineDependentConfiguration(KeLoaderBlock
);
1754 if (!NT_SUCCESS(Status
))
1757 KeBugCheckEx(CONFIG_INITIALIZATION_FAILED
, 1, 14, Status
, 0);
1760 /* Initialize volatile registry settings */
1761 Status
= CmpSetSystemValues(KeLoaderBlock
);
1762 if (!NT_SUCCESS(Status
))
1765 KeBugCheckEx(CONFIG_INITIALIZATION_FAILED
, 1, 15, Status
, 0);
1768 /* Free the load options */
1769 ExFreePoolWithTag(CmpLoadOptions
.Buffer
, TAG_CM
);
1771 /* If we got here, all went well */
1778 CmpFreeDriverList(IN PHHIVE Hive
,
1779 IN PLIST_ENTRY DriverList
)
1781 PLIST_ENTRY NextEntry
, OldEntry
;
1782 PBOOT_DRIVER_NODE DriverNode
;
1785 /* Parse the current list */
1786 NextEntry
= DriverList
->Flink
;
1787 while (NextEntry
!= DriverList
)
1789 /* Get the driver node */
1790 DriverNode
= CONTAINING_RECORD(NextEntry
, BOOT_DRIVER_NODE
, ListEntry
.Link
);
1792 /* Get the next entry now, since we're going to free it later */
1793 OldEntry
= NextEntry
;
1794 NextEntry
= NextEntry
->Flink
;
1796 /* Was there a name? */
1797 if (DriverNode
->Name
.Buffer
)
1800 CmpFree(DriverNode
->Name
.Buffer
, DriverNode
->Name
.Length
);
1803 /* Was there a registry path? */
1804 if (DriverNode
->ListEntry
.RegistryPath
.Buffer
)
1807 CmpFree(DriverNode
->ListEntry
.RegistryPath
.Buffer
,
1808 DriverNode
->ListEntry
.RegistryPath
.MaximumLength
);
1811 /* Was there a file path? */
1812 if (DriverNode
->ListEntry
.FilePath
.Buffer
)
1815 CmpFree(DriverNode
->ListEntry
.FilePath
.Buffer
,
1816 DriverNode
->ListEntry
.FilePath
.MaximumLength
);
1819 /* Now free the node, and move on */
1820 CmpFree(OldEntry
, sizeof(BOOT_DRIVER_NODE
));
1827 CmGetSystemDriverList(VOID
)
1829 LIST_ENTRY DriverList
;
1830 OBJECT_ATTRIBUTES ObjectAttributes
;
1832 PCM_KEY_BODY KeyBody
;
1834 HCELL_INDEX RootCell
, ControlCell
;
1836 UNICODE_STRING KeyName
;
1837 PLIST_ENTRY NextEntry
;
1839 PUNICODE_STRING
* ServicePath
= NULL
;
1840 BOOLEAN Success
, AutoSelect
;
1841 PBOOT_DRIVER_LIST_ENTRY DriverEntry
;
1844 /* Initialize the driver list */
1845 InitializeListHead(&DriverList
);
1847 /* Open the system hive key */
1848 RtlInitUnicodeString(&KeyName
, L
"\\Registry\\Machine\\System");
1849 InitializeObjectAttributes(&ObjectAttributes
,
1851 OBJ_CASE_INSENSITIVE
,
1854 Status
= NtOpenKey(&KeyHandle
, KEY_READ
, &ObjectAttributes
);
1855 if (!NT_SUCCESS(Status
)) return NULL
;
1857 /* Reference the key object to get the root hive/cell to access directly */
1858 Status
= ObReferenceObjectByHandle(KeyHandle
,
1864 if (!NT_SUCCESS(Status
))
1871 /* Do all this under the registry lock */
1872 CmpLockRegistryExclusive();
1874 /* Get the hive and key cell */
1875 Hive
= KeyBody
->KeyControlBlock
->KeyHive
;
1876 RootCell
= KeyBody
->KeyControlBlock
->KeyCell
;
1878 /* Open the current control set key */
1879 RtlInitUnicodeString(&KeyName
, L
"Current");
1880 ControlCell
= CmpFindControlSet(Hive
, RootCell
, &KeyName
, &AutoSelect
);
1881 if (ControlCell
== HCELL_NIL
) goto EndPath
;
1883 /* Find all system drivers */
1884 Success
= CmpFindDrivers(Hive
, ControlCell
, SystemLoad
, NULL
, &DriverList
);
1885 if (!Success
) goto EndPath
;
1887 /* Sort by group/tag */
1888 if (!CmpSortDriverList(Hive
, ControlCell
, &DriverList
)) goto EndPath
;
1890 /* Remove circular dependencies (cycles) and sort */
1891 if (!CmpResolveDriverDependencies(&DriverList
)) goto EndPath
;
1893 /* Loop the list to count drivers */
1894 for (i
= 0, NextEntry
= DriverList
.Flink
;
1895 NextEntry
!= &DriverList
;
1896 i
++, NextEntry
= NextEntry
->Flink
);
1898 /* Allocate the array */
1899 ServicePath
= ExAllocatePool(NonPagedPool
, (i
+ 1) * sizeof(PUNICODE_STRING
));
1900 if (!ServicePath
) KeBugCheckEx(CONFIG_INITIALIZATION_FAILED
, 2, 1, 0, 0);
1902 /* Loop the driver list */
1903 for (i
= 0, NextEntry
= DriverList
.Flink
;
1904 NextEntry
!= &DriverList
;
1905 i
++, NextEntry
= NextEntry
->Flink
)
1908 DriverEntry
= CONTAINING_RECORD(NextEntry
, BOOT_DRIVER_LIST_ENTRY
, Link
);
1910 /* Allocate the path for the caller and duplicate the registry path */
1911 ServicePath
[i
] = ExAllocatePool(NonPagedPool
, sizeof(UNICODE_STRING
));
1912 RtlDuplicateUnicodeString(RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE
,
1913 &DriverEntry
->RegistryPath
,
1917 /* Terminate the list */
1918 ServicePath
[i
] = NULL
;
1921 /* Free the driver list if we had one */
1922 if (!IsListEmpty(&DriverList
)) CmpFreeDriverList(Hive
, &DriverList
);
1924 /* Unlock the registry */
1925 CmpUnlockRegistry();
1927 /* Close the key handle and dereference the object, then return the path */
1928 ObDereferenceObject(KeyBody
);
1935 CmpLockRegistryExclusive(VOID
)
1937 /* Enter a critical region and lock the registry */
1938 KeEnterCriticalRegion();
1939 ExAcquireResourceExclusiveLite(&CmpRegistryLock
, TRUE
);
1942 ASSERT(CmpFlushStarveWriters
== 0);
1943 RtlGetCallersAddress(&CmpRegistryLockCaller
, &CmpRegistryLockCallerCaller
);
1948 CmpLockRegistry(VOID
)
1950 /* Enter a critical region */
1951 KeEnterCriticalRegion();
1953 /* Check if we have to starve writers */
1954 if (CmpFlushStarveWriters
)
1956 /* Starve exlusive waiters */
1957 ExAcquireSharedStarveExclusive(&CmpRegistryLock
, TRUE
);
1961 /* Just grab the lock */
1962 ExAcquireResourceSharedLite(&CmpRegistryLock
, TRUE
);
1968 CmpTestRegistryLock(VOID
)
1971 return !ExIsResourceAcquiredSharedLite(&CmpRegistryLock
) ? FALSE
: TRUE
;
1976 CmpTestRegistryLockExclusive(VOID
)
1979 return !ExIsResourceAcquiredExclusiveLite(&CmpRegistryLock
) ? FALSE
: TRUE
;
1984 CmpLockHiveFlusherExclusive(IN PCMHIVE Hive
)
1986 /* Lock the flusher. We should already be in a critical section */
1987 CMP_ASSERT_REGISTRY_LOCK_OR_LOADING(Hive
);
1988 ASSERT((ExIsResourceAcquiredShared(Hive
->FlusherLock
) == 0) &&
1989 (ExIsResourceAcquiredExclusiveLite(Hive
->FlusherLock
) == 0));
1990 ExAcquireResourceExclusiveLite(Hive
->FlusherLock
, TRUE
);
1995 CmpLockHiveFlusherShared(IN PCMHIVE Hive
)
1997 /* Lock the flusher. We should already be in a critical section */
1998 CMP_ASSERT_REGISTRY_LOCK_OR_LOADING(Hive
);
1999 ASSERT((ExIsResourceAcquiredShared(Hive
->FlusherLock
) == 0) &&
2000 (ExIsResourceAcquiredExclusiveLite(Hive
->FlusherLock
) == 0));
2001 ExAcquireResourceSharedLite(Hive
->FlusherLock
, TRUE
);
2006 CmpUnlockHiveFlusher(IN PCMHIVE Hive
)
2009 CMP_ASSERT_REGISTRY_LOCK_OR_LOADING(Hive
);
2010 CMP_ASSERT_FLUSH_LOCK(Hive
);
2012 /* Release the lock */
2013 ExReleaseResourceLite(Hive
->FlusherLock
);
2018 CmpTestHiveFlusherLockShared(IN PCMHIVE Hive
)
2021 return !ExIsResourceAcquiredSharedLite(Hive
->FlusherLock
) ? FALSE
: TRUE
;
2026 CmpTestHiveFlusherLockExclusive(IN PCMHIVE Hive
)
2029 return !ExIsResourceAcquiredExclusiveLite(Hive
->FlusherLock
) ? FALSE
: TRUE
;
2034 CmpUnlockRegistry(VOID
)
2037 CMP_ASSERT_REGISTRY_LOCK();
2039 /* Check if we should flush the registry */
2040 if (CmpFlushOnLockRelease
)
2042 /* The registry should be exclusively locked for this */
2043 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK();
2045 /* Flush the registry */
2046 CmpDoFlushAll(TRUE
);
2047 CmpFlushOnLockRelease
= FALSE
;
2051 /* Lazy flush the registry */
2055 /* Release the lock and leave the critical region */
2056 ExReleaseResourceLite(&CmpRegistryLock
);
2057 KeLeaveCriticalRegion();
2062 CmpAcquireTwoKcbLocksExclusiveByKey(IN ULONG ConvKey1
,
2065 ULONG Index1
, Index2
;
2068 CMP_ASSERT_REGISTRY_LOCK();
2070 /* Get hash indexes */
2071 Index1
= GET_HASH_INDEX(ConvKey1
);
2072 Index2
= GET_HASH_INDEX(ConvKey2
);
2074 /* See which one is highest */
2075 if (Index1
< Index2
)
2077 /* Grab them in the proper order */
2078 CmpAcquireKcbLockExclusiveByKey(ConvKey1
);
2079 CmpAcquireKcbLockExclusiveByKey(ConvKey2
);
2083 /* Grab the second one first, then the first */
2084 CmpAcquireKcbLockExclusiveByKey(ConvKey2
);
2085 if (Index1
!= Index2
) CmpAcquireKcbLockExclusiveByKey(ConvKey1
);
2091 CmpReleaseTwoKcbLockByKey(IN ULONG ConvKey1
,
2094 ULONG Index1
, Index2
;
2097 CMP_ASSERT_REGISTRY_LOCK();
2099 /* Get hash indexes */
2100 Index1
= GET_HASH_INDEX(ConvKey1
);
2101 Index2
= GET_HASH_INDEX(ConvKey2
);
2102 ASSERT((GET_HASH_ENTRY(CmpCacheTable
, ConvKey2
).Owner
== KeGetCurrentThread()) ||
2103 (CmpTestRegistryLockExclusive()));
2105 /* See which one is highest */
2106 if (Index1
< Index2
)
2108 /* Grab them in the proper order */
2109 ASSERT((GET_HASH_ENTRY(CmpCacheTable
, ConvKey1
).Owner
== KeGetCurrentThread()) ||
2110 (CmpTestRegistryLockExclusive()));
2111 CmpReleaseKcbLockByKey(ConvKey2
);
2112 CmpReleaseKcbLockByKey(ConvKey1
);
2116 /* Release the first one first, then the second */
2117 if (Index1
!= Index2
)
2119 ASSERT((GET_HASH_ENTRY(CmpCacheTable
, ConvKey1
).Owner
== KeGetCurrentThread()) ||
2120 (CmpTestRegistryLockExclusive()));
2121 CmpReleaseKcbLockByKey(ConvKey1
);
2123 CmpReleaseKcbLockByKey(ConvKey2
);
2129 CmShutdownSystem(VOID
)
2131 PLIST_ENTRY ListEntry
;
2134 /* Kill the workers */
2135 if (!CmFirstTime
) CmpShutdownWorkers();
2137 /* Flush all hives */
2138 CmpLockRegistryExclusive();
2139 CmpDoFlushAll(TRUE
);
2141 /* Close all hive files */
2142 ListEntry
= CmpHiveListHead
.Flink
;
2143 while (ListEntry
!= &CmpHiveListHead
)
2145 Hive
= CONTAINING_RECORD(ListEntry
, CMHIVE
, HiveList
);
2147 CmpCloseHiveFiles(Hive
);
2149 ListEntry
= ListEntry
->Flink
;
2152 CmpUnlockRegistry();
2157 CmpSetVersionData(VOID
)
2160 OBJECT_ATTRIBUTES ObjectAttributes
;
2161 UNICODE_STRING KeyName
;
2162 UNICODE_STRING ValueName
;
2163 UNICODE_STRING ValueData
;
2164 ANSI_STRING TempString
;
2165 HANDLE SoftwareKeyHandle
= NULL
;
2166 HANDLE MicrosoftKeyHandle
= NULL
;
2167 HANDLE WindowsNtKeyHandle
= NULL
;
2168 HANDLE CurrentVersionKeyHandle
= NULL
;
2169 WCHAR Buffer
[128]; // Buffer large enough to contain a full ULONG in decimal representation,
2170 // and the full 'CurrentType' string.
2173 * Open the 'HKLM\Software\Microsoft\Windows NT\CurrentVersion' key
2174 * (create the intermediate subkeys if needed).
2177 RtlInitUnicodeString(&KeyName
, L
"\\REGISTRY\\MACHINE\\SOFTWARE");
2178 InitializeObjectAttributes(&ObjectAttributes
,
2180 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
2183 Status
= NtCreateKey(&SoftwareKeyHandle
,
2190 if (!NT_SUCCESS(Status
))
2192 DPRINT1("Failed to create key %wZ (Status: %08lx)\n", &KeyName
, Status
);
2196 RtlInitUnicodeString(&KeyName
, L
"Microsoft");
2197 InitializeObjectAttributes(&ObjectAttributes
,
2199 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
2202 Status
= NtCreateKey(&MicrosoftKeyHandle
,
2209 if (!NT_SUCCESS(Status
))
2211 DPRINT1("Failed to create key %wZ (Status: %08lx)\n", &KeyName
, Status
);
2215 RtlInitUnicodeString(&KeyName
, L
"Windows NT");
2216 InitializeObjectAttributes(&ObjectAttributes
,
2218 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
2221 Status
= NtCreateKey(&WindowsNtKeyHandle
,
2228 if (!NT_SUCCESS(Status
))
2230 DPRINT1("Failed to create key %wZ (Status: %08lx)\n", &KeyName
, Status
);
2234 RtlInitUnicodeString(&KeyName
, L
"CurrentVersion");
2235 InitializeObjectAttributes(&ObjectAttributes
,
2237 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
2240 Status
= NtCreateKey(&CurrentVersionKeyHandle
,
2241 KEY_CREATE_SUB_KEY
| KEY_SET_VALUE
,
2247 if (!NT_SUCCESS(Status
))
2249 DPRINT1("Failed to create key %wZ (Status: %08lx)\n", &KeyName
, Status
);
2253 /* Set the 'CurrentVersion' value */
2254 RtlInitUnicodeString(&ValueName
, L
"CurrentVersion");
2255 NtSetValueKey(CurrentVersionKeyHandle
,
2259 CmVersionString
.Buffer
,
2260 CmVersionString
.Length
+ sizeof(WCHAR
));
2262 /* Set the 'CurrentBuildNumber' value */
2263 RtlInitUnicodeString(&ValueName
, L
"CurrentBuildNumber");
2264 RtlInitEmptyUnicodeString(&ValueData
, Buffer
, sizeof(Buffer
));
2265 RtlIntegerToUnicodeString(NtBuildNumber
& 0xFFFF, 10, &ValueData
);
2266 NtSetValueKey(CurrentVersionKeyHandle
,
2271 ValueData
.Length
+ sizeof(WCHAR
));
2273 /* Set the 'BuildLab' value */
2274 RtlInitUnicodeString(&ValueName
, L
"BuildLab");
2275 RtlInitAnsiString(&TempString
, NtBuildLab
);
2276 Status
= RtlAnsiStringToUnicodeString(&ValueData
, &TempString
, FALSE
);
2277 if (NT_SUCCESS(Status
))
2279 NtSetValueKey(CurrentVersionKeyHandle
,
2284 ValueData
.Length
+ sizeof(WCHAR
));
2287 /* Set the 'CurrentType' value */
2288 RtlInitUnicodeString(&ValueName
, L
"CurrentType");
2290 swprintf(Buffer
, L
"%s %s",
2303 RtlInitUnicodeString(&ValueData
, Buffer
);
2304 NtSetValueKey(CurrentVersionKeyHandle
,
2309 ValueData
.Length
+ sizeof(WCHAR
));
2311 /* Set the 'CSDVersion' value */
2312 RtlInitUnicodeString(&ValueName
, L
"CSDVersion");
2313 if (CmCSDVersionString
.Length
!= 0)
2315 NtSetValueKey(CurrentVersionKeyHandle
,
2319 CmCSDVersionString
.Buffer
,
2320 CmCSDVersionString
.Length
+ sizeof(WCHAR
));
2324 NtDeleteValueKey(CurrentVersionKeyHandle
, &ValueName
);
2327 /* Set the 'CSDBuildNumber' value */
2328 RtlInitUnicodeString(&ValueName
, L
"CSDBuildNumber");
2329 if (CmNtSpBuildNumber
!= 0)
2331 RtlInitEmptyUnicodeString(&ValueData
, Buffer
, sizeof(Buffer
));
2332 RtlIntegerToUnicodeString(CmNtSpBuildNumber
, 10, &ValueData
);
2333 NtSetValueKey(CurrentVersionKeyHandle
,
2338 ValueData
.Length
+ sizeof(WCHAR
));
2342 NtDeleteValueKey(CurrentVersionKeyHandle
, &ValueName
);
2345 /* Set the 'SystemRoot' value */
2346 RtlInitUnicodeString(&ValueName
, L
"SystemRoot");
2347 NtSetValueKey(CurrentVersionKeyHandle
,
2351 NtSystemRoot
.Buffer
,
2352 NtSystemRoot
.Length
+ sizeof(WCHAR
));
2355 /* Close the keys */
2356 if (CurrentVersionKeyHandle
!= NULL
)
2357 NtClose(CurrentVersionKeyHandle
);
2359 if (WindowsNtKeyHandle
!= NULL
)
2360 NtClose(WindowsNtKeyHandle
);
2362 if (MicrosoftKeyHandle
!= NULL
)
2363 NtClose(MicrosoftKeyHandle
);
2365 if (SoftwareKeyHandle
!= NULL
)
2366 NtClose(SoftwareKeyHandle
);