2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/config/cmparse.c
5 * PURPOSE: Configuration Manager - Object Manager Parse Interface
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES ******************************************************************/
15 /* GLOBALS *******************************************************************/
17 /* FUNCTIONS *****************************************************************/
21 CmpGetNextName(IN OUT PUNICODE_STRING RemainingName
,
22 OUT PUNICODE_STRING NextName
,
23 OUT PBOOLEAN LastName
)
25 BOOLEAN NameValid
= TRUE
;
27 ASSERT(RemainingName
->Length
% sizeof(WCHAR
) == 0);
29 /* Check if there's nothing left in the name */
30 if (!(RemainingName
->Buffer
) ||
31 (!RemainingName
->Length
) ||
32 !(*RemainingName
->Buffer
))
34 /* Clear the next name and set this as last */
36 NextName
->Buffer
= NULL
;
41 /* Check if we have a path separator */
42 while (RemainingName
->Length
&&
43 (*RemainingName
->Buffer
== OBJ_NAME_PATH_SEPARATOR
))
46 RemainingName
->Buffer
++;
47 RemainingName
->Length
-= sizeof(WCHAR
);
48 RemainingName
->MaximumLength
-= sizeof(WCHAR
);
51 /* Start loop at where the current buffer is */
52 NextName
->Buffer
= RemainingName
->Buffer
;
53 while (RemainingName
->Length
&&
54 (*RemainingName
->Buffer
!= OBJ_NAME_PATH_SEPARATOR
))
56 /* Move to the next character */
57 RemainingName
->Buffer
++;
58 RemainingName
->Length
-= sizeof(WCHAR
);
59 RemainingName
->MaximumLength
-= sizeof(WCHAR
);
62 /* See how many chars we parsed and validate the length */
63 NextName
->Length
= (USHORT
)((ULONG_PTR
)RemainingName
->Buffer
-
64 (ULONG_PTR
)NextName
->Buffer
);
65 if (NextName
->Length
> 512) NameValid
= FALSE
;
66 NextName
->MaximumLength
= NextName
->Length
;
68 /* If there's nothing left, we're last */
69 *LastName
= !RemainingName
->Length
;
75 CmpGetSymbolicLink(IN PHHIVE Hive
,
76 IN OUT PUNICODE_STRING ObjectName
,
77 IN OUT PCM_KEY_CONTROL_BLOCK SymbolicKcb
,
78 IN PUNICODE_STRING RemainingName OPTIONAL
)
80 HCELL_INDEX LinkCell
= HCELL_NIL
;
81 PCM_KEY_VALUE LinkValue
= NULL
;
82 PWSTR LinkName
= NULL
;
83 BOOLEAN LinkNameAllocated
= FALSE
;
86 ULONG ValueLength
= 0;
87 BOOLEAN Result
= FALSE
;
88 HCELL_INDEX CellToRelease
= HCELL_NIL
;
90 UNICODE_STRING NewObjectName
;
92 /* Make sure we're not being deleted */
93 if (SymbolicKcb
->Delete
) return FALSE
;
95 /* Get the key node */
96 Node
= (PCM_KEY_NODE
)HvGetCell(SymbolicKcb
->KeyHive
, SymbolicKcb
->KeyCell
);
99 /* Find the symbolic link key */
100 LinkCell
= CmpFindValueByName(Hive
, Node
, &CmSymbolicLinkValueName
);
101 HvReleaseCell(SymbolicKcb
->KeyHive
, SymbolicKcb
->KeyCell
);
102 if (LinkCell
== HCELL_NIL
) goto Exit
;
104 /* Get the value cell */
105 LinkValue
= (PCM_KEY_VALUE
)HvGetCell(Hive
, LinkCell
);
106 if (!LinkValue
) goto Exit
;
108 /* Make sure it's a registry link */
109 if (LinkValue
->Type
!= REG_LINK
) goto Exit
;
111 /* Now read the value data */
112 if (!CmpGetValueData(Hive
,
124 Length
= ValueLength
+ sizeof(WCHAR
);
126 /* Make sure we start with a slash */
127 if (*LinkName
!= OBJ_NAME_PATH_SEPARATOR
) goto Exit
;
129 /* Add the remaining name if needed */
130 if (RemainingName
) Length
+= RemainingName
->Length
+ sizeof(WCHAR
);
132 /* Check for overflow */
133 if (Length
> 0xFFFF) goto Exit
;
135 /* Check if we need a new buffer */
136 if (Length
> ObjectName
->MaximumLength
)
138 /* We do -- allocate one */
139 NewBuffer
= ExAllocatePoolWithTag(PagedPool
, Length
, TAG_CM
);
140 if (!NewBuffer
) goto Exit
;
142 /* Setup the new string and copy the symbolic target */
143 NewObjectName
.Buffer
= NewBuffer
;
144 NewObjectName
.MaximumLength
= (USHORT
)Length
;
145 NewObjectName
.Length
= (USHORT
)ValueLength
;
146 RtlCopyMemory(NewBuffer
, LinkName
, ValueLength
);
148 /* Check if we need to add anything else */
151 /* Add the remaining name */
152 NewBuffer
[ValueLength
/ sizeof(WCHAR
)] = OBJ_NAME_PATH_SEPARATOR
;
153 NewObjectName
.Length
+= sizeof(WCHAR
);
154 RtlAppendUnicodeStringToString(&NewObjectName
, RemainingName
);
157 /* Free the old buffer */
158 ExFreePool(ObjectName
->Buffer
);
159 *ObjectName
= NewObjectName
;
163 /* The old name is large enough -- update the length */
164 ObjectName
->Length
= (USHORT
)ValueLength
;
167 /* Copy the remaining name inside */
168 RtlMoveMemory(&ObjectName
->Buffer
[(ValueLength
/ sizeof(WCHAR
)) + 1],
169 RemainingName
->Buffer
,
170 RemainingName
->Length
);
172 /* Add the slash and update the length */
173 ObjectName
->Buffer
[ValueLength
/ sizeof(WCHAR
)] = OBJ_NAME_PATH_SEPARATOR
;
174 ObjectName
->Length
+= RemainingName
->Length
+ sizeof(WCHAR
);
177 /* Copy the symbolic link target name */
178 RtlCopyMemory(ObjectName
->Buffer
, LinkName
, ValueLength
);
181 /* Null-terminate the whole thing */
182 ObjectName
->Buffer
[ObjectName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
186 /* Free the link name */
187 if (LinkNameAllocated
) ExFreePool(LinkName
);
189 /* Check if we had a value cell */
193 ASSERT(LinkCell
!= HCELL_NIL
);
194 HvReleaseCell(Hive
, LinkCell
);
197 /* Check if we had an active cell and release it, then return the result */
198 if (CellToRelease
!= HCELL_NIL
) HvReleaseCell(Hive
, CellToRelease
);
204 CmpDoCreateChild(IN PHHIVE Hive
,
205 IN HCELL_INDEX ParentCell
,
206 IN PSECURITY_DESCRIPTOR ParentDescriptor OPTIONAL
,
207 IN PACCESS_STATE AccessState
,
208 IN PUNICODE_STRING Name
,
209 IN KPROCESSOR_MODE AccessMode
,
210 IN PCM_PARSE_CONTEXT ParseContext
,
211 IN PCM_KEY_CONTROL_BLOCK ParentKcb
,
213 OUT PHCELL_INDEX KeyCell
,
216 NTSTATUS Status
= STATUS_SUCCESS
;
217 PCM_KEY_BODY KeyBody
;
218 HCELL_INDEX ClassCell
= HCELL_NIL
;
219 PCM_KEY_NODE KeyNode
;
222 PCM_KEY_CONTROL_BLOCK Kcb
;
223 PSECURITY_DESCRIPTOR NewDescriptor
;
225 /* Get the storage type */
226 StorageType
= Stable
;
227 if (ParseContext
->CreateOptions
& REG_OPTION_VOLATILE
) StorageType
= Volatile
;
229 /* Allocate the child */
230 *KeyCell
= HvAllocateCell(Hive
,
231 FIELD_OFFSET(CM_KEY_NODE
, Name
) +
232 CmpNameSize(Hive
, Name
),
235 if (*KeyCell
== HCELL_NIL
)
238 Status
= STATUS_INSUFFICIENT_RESOURCES
;
242 /* Get the key node */
243 KeyNode
= (PCM_KEY_NODE
)HvGetCell(Hive
, *KeyCell
);
246 /* Fail, this should never happen */
248 Status
= STATUS_INSUFFICIENT_RESOURCES
;
252 /* Release the cell */
253 HvReleaseCell(Hive
, *KeyCell
);
255 /* Check if we have a class name */
256 if (ParseContext
->Class
.Length
> 0)
258 /* Allocate a class cell */
259 ClassCell
= HvAllocateCell(Hive
,
260 ParseContext
->Class
.Length
,
263 if (ClassCell
== HCELL_NIL
)
266 Status
= STATUS_INSUFFICIENT_RESOURCES
;
271 /* Allocate the Cm Object */
272 Status
= ObCreateObject(AccessMode
,
281 if (!NT_SUCCESS(Status
)) goto Quickie
;
283 /* Setup the key body */
284 KeyBody
= (PCM_KEY_BODY
)(*Object
);
285 KeyBody
->Type
= CM_KEY_BODY_TYPE
;
286 KeyBody
->KeyControlBlock
= NULL
;
287 KeyBody
->KcbLocked
= FALSE
;
289 /* Check if we had a class */
290 if (ParseContext
->Class
.Length
> 0)
292 /* Get the class cell */
293 CellData
= HvGetCell(Hive
, ClassCell
);
296 /* Fail, this should never happen */
298 Status
= STATUS_INSUFFICIENT_RESOURCES
;
299 ObDereferenceObject(*Object
);
303 /* Release the cell */
304 HvReleaseCell(Hive
, ClassCell
);
306 /* Copy the class data */
307 RtlCopyMemory(&CellData
->u
.KeyString
[0],
308 ParseContext
->Class
.Buffer
,
309 ParseContext
->Class
.Length
);
312 /* Fill out the key node */
313 KeyNode
->Signature
= CM_KEY_NODE_SIGNATURE
;
314 KeyNode
->Flags
= Flags
;
315 KeQuerySystemTime(&KeyNode
->LastWriteTime
);
317 KeyNode
->Parent
= ParentCell
;
318 KeyNode
->SubKeyCounts
[Stable
] = 0;
319 KeyNode
->SubKeyCounts
[Volatile
] = 0;
320 KeyNode
->SubKeyLists
[Stable
] = HCELL_NIL
;
321 KeyNode
->SubKeyLists
[Volatile
] = HCELL_NIL
;
322 KeyNode
->ValueList
.Count
= 0;
323 KeyNode
->ValueList
.List
= HCELL_NIL
;
324 KeyNode
->Security
= HCELL_NIL
;
325 KeyNode
->Class
= ClassCell
;
326 KeyNode
->ClassLength
= ParseContext
->Class
.Length
;
327 KeyNode
->MaxValueDataLen
= 0;
328 KeyNode
->MaxNameLen
= 0;
329 KeyNode
->MaxValueNameLen
= 0;
330 KeyNode
->MaxClassLen
= 0;
331 KeyNode
->NameLength
= CmpCopyName(Hive
, KeyNode
->Name
, Name
);
332 if (KeyNode
->NameLength
< Name
->Length
) KeyNode
->Flags
|= KEY_COMP_NAME
;
335 Kcb
= CmpCreateKeyControlBlock(Hive
,
339 CMP_LOCK_HASHES_FOR_KCB
,
344 ObDereferenceObjectDeferDelete(*Object
);
345 Status
= STATUS_INSUFFICIENT_RESOURCES
;
350 ASSERT(Kcb
->RefCount
== 1);
352 /* Now fill out the Cm object */
353 KeyBody
->NotifyBlock
= NULL
;
354 KeyBody
->ProcessID
= PsGetCurrentProcessId();
355 KeyBody
->KeyControlBlock
= Kcb
;
357 /* Link it with the KCB */
358 EnlistKeyBodyWithKCB(KeyBody
, CMP_ENLIST_KCB_LOCKED_EXCLUSIVE
);
360 /* Assign security */
361 Status
= SeAssignSecurity(ParentDescriptor
,
362 AccessState
->SecurityDescriptor
,
365 &AccessState
->SubjectSecurityContext
,
366 &CmpKeyObjectType
->TypeInfo
.GenericMapping
,
367 CmpKeyObjectType
->TypeInfo
.PoolType
);
368 if (NT_SUCCESS(Status
))
371 * FIXME: We must acquire a security lock when assigning
372 * a security descriptor to this hive but since the
373 * CmpAssignSecurityDescriptor function does nothing
374 * (we lack the necessary security management implementations
375 * anyway), do not do anything for now.
377 Status
= CmpAssignSecurityDescriptor(Kcb
, NewDescriptor
);
380 /* Now that the security descriptor is copied in the hive, we can free the original */
381 SeDeassignSecurity(&NewDescriptor
);
383 if (NT_SUCCESS(Status
))
385 /* Send notification to registered callbacks */
386 CmpReportNotify(Kcb
, Hive
, Kcb
->KeyCell
, REG_NOTIFY_CHANGE_NAME
);
390 /* Check if we got here because of failure */
391 if (!NT_SUCCESS(Status
))
393 /* Free any cells we might've allocated */
394 if (ParseContext
->Class
.Length
> 0) HvFreeCell(Hive
, ClassCell
);
395 HvFreeCell(Hive
, *KeyCell
);
404 CmpDoCreate(IN PHHIVE Hive
,
406 IN PACCESS_STATE AccessState
,
407 IN PUNICODE_STRING Name
,
408 IN KPROCESSOR_MODE AccessMode
,
409 IN PCM_PARSE_CONTEXT ParseContext
,
410 IN PCM_KEY_CONTROL_BLOCK ParentKcb
,
417 PCM_KEY_BODY KeyBody
;
418 PSECURITY_DESCRIPTOR SecurityDescriptor
= NULL
;
419 LARGE_INTEGER TimeStamp
;
420 PCM_KEY_NODE KeyNode
;
422 /* Make sure the KCB is locked and lock the flusher */
423 CMP_ASSERT_KCB_LOCK(ParentKcb
);
424 CmpLockHiveFlusherShared((PCMHIVE
)Hive
);
426 /* Bail out on read-only KCBs */
427 if (ParentKcb
->ExtFlags
& CM_KCB_READ_ONLY_KEY
)
429 Status
= STATUS_ACCESS_DENIED
;
433 /* Check if the parent is being deleted */
434 if (ParentKcb
->Delete
)
438 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
442 /* Get the parent node */
443 KeyNode
= (PCM_KEY_NODE
)HvGetCell(Hive
, Cell
);
448 Status
= STATUS_INSUFFICIENT_RESOURCES
;
452 /* Make sure nobody added us yet */
453 if (CmpFindSubKeyByName(Hive
, KeyNode
, Name
) != HCELL_NIL
)
457 Status
= STATUS_REPARSE
;
462 ASSERT(Cell
== ParentKcb
->KeyCell
);
464 /* Get the parent type */
465 ParentType
= HvGetCellType(Cell
);
466 if ((ParentType
== Volatile
) &&
467 !(ParseContext
->CreateOptions
& REG_OPTION_VOLATILE
))
469 /* Children of volatile parents must also be volatile */
471 Status
= STATUS_CHILD_MUST_BE_VOLATILE
;
475 /* Don't allow children under symlinks */
476 if (ParentKcb
->Flags
& KEY_SYM_LINK
)
480 Status
= STATUS_ACCESS_DENIED
;
484 /* Make the cell dirty for now */
485 HvMarkCellDirty(Hive
, Cell
, FALSE
);
487 /* Do the actual create operation */
488 Status
= CmpDoCreateChild(Hive
,
499 if (NT_SUCCESS(Status
))
501 /* Get the key body */
502 KeyBody
= (PCM_KEY_BODY
)(*Object
);
504 /* Now add the subkey */
505 if (!CmpAddSubKey(Hive
, Cell
, KeyCell
))
507 /* Free the created child */
508 CmpFreeKeyByCell(Hive
, KeyCell
, FALSE
);
510 /* Purge out this KCB */
511 KeyBody
->KeyControlBlock
->Delete
= TRUE
;
512 CmpRemoveKeyControlBlock(KeyBody
->KeyControlBlock
);
514 /* And cleanup the key body object */
515 ObDereferenceObjectDeferDelete(*Object
);
516 Status
= STATUS_INSUFFICIENT_RESOURCES
;
520 /* Get the key node */
521 KeyNode
= (PCM_KEY_NODE
)HvGetCell(Hive
, Cell
);
524 /* Fail, this shouldn't happen */
525 CmpFreeKeyByCell(Hive
, KeyCell
, TRUE
); // Subkey linked above
527 /* Purge out this KCB */
528 KeyBody
->KeyControlBlock
->Delete
= TRUE
;
529 CmpRemoveKeyControlBlock(KeyBody
->KeyControlBlock
);
531 /* And cleanup the key body object */
532 ObDereferenceObjectDeferDelete(*Object
);
533 Status
= STATUS_INSUFFICIENT_RESOURCES
;
537 /* Clean up information on this subkey */
538 CmpCleanUpSubKeyInfo(KeyBody
->KeyControlBlock
->ParentKcb
);
541 ASSERT(KeyBody
->KeyControlBlock
->ParentKcb
->KeyCell
== Cell
);
542 ASSERT(KeyBody
->KeyControlBlock
->ParentKcb
->KeyHive
== Hive
);
543 ASSERT(KeyBody
->KeyControlBlock
->ParentKcb
== ParentKcb
);
544 ASSERT(KeyBody
->KeyControlBlock
->ParentKcb
->KcbMaxNameLen
== KeyNode
->MaxNameLen
);
546 /* Update the timestamp */
547 KeQuerySystemTime(&TimeStamp
);
548 KeyNode
->LastWriteTime
= TimeStamp
;
549 KeyBody
->KeyControlBlock
->ParentKcb
->KcbLastWriteTime
= TimeStamp
;
551 /* Check if we need to update name maximum */
552 if (KeyNode
->MaxNameLen
< Name
->Length
)
555 KeyNode
->MaxNameLen
= Name
->Length
;
556 KeyBody
->KeyControlBlock
->ParentKcb
->KcbMaxNameLen
= Name
->Length
;
559 /* Check if we need to update class length maximum */
560 if (KeyNode
->MaxClassLen
< ParseContext
->Class
.Length
)
563 KeyNode
->MaxClassLen
= ParseContext
->Class
.Length
;
566 /* Check if we're creating a symbolic link */
567 if (ParseContext
->CreateOptions
& REG_OPTION_CREATE_LINK
)
569 /* Get the cell data */
570 CellData
= HvGetCell(Hive
, KeyCell
);
573 /* This shouldn't happen */
574 CmpFreeKeyByCell(Hive
, KeyCell
, TRUE
); // Subkey linked above
576 /* Purge out this KCB */
577 KeyBody
->KeyControlBlock
->Delete
= TRUE
;
578 CmpRemoveKeyControlBlock(KeyBody
->KeyControlBlock
);
580 /* And cleanup the key body object */
581 ObDereferenceObjectDeferDelete(*Object
);
582 Status
= STATUS_INSUFFICIENT_RESOURCES
;
586 /* Update the flags */
587 CellData
->u
.KeyNode
.Flags
|= KEY_SYM_LINK
;
588 KeyBody
->KeyControlBlock
->Flags
= CellData
->u
.KeyNode
.Flags
;
589 HvReleaseCell(Hive
, KeyCell
);
594 /* Release the flusher lock and return status */
595 CmpUnlockHiveFlusher((PCMHIVE
)Hive
);
601 CmpDoOpen(IN PHHIVE Hive
,
603 IN PCM_KEY_NODE Node
,
604 IN PACCESS_STATE AccessState
,
605 IN KPROCESSOR_MODE AccessMode
,
607 IN PCM_PARSE_CONTEXT Context OPTIONAL
,
608 IN ULONG ControlFlags
,
609 IN OUT PCM_KEY_CONTROL_BLOCK
*CachedKcb
,
610 IN PULONG KcbsLocked
,
611 IN PUNICODE_STRING KeyName
,
615 BOOLEAN LockKcb
= FALSE
;
616 BOOLEAN IsLockShared
= FALSE
;
617 PCM_KEY_BODY KeyBody
= NULL
;
618 PCM_KEY_CONTROL_BLOCK Kcb
= NULL
;
620 /* Make sure the hive isn't locked */
621 if ((Hive
->HiveFlags
& HIVE_IS_UNLOADING
) &&
622 (((PCMHIVE
)Hive
)->CreatorOwner
!= KeGetCurrentThread()))
624 /* It is, don't touch it */
625 return STATUS_OBJECT_NAME_NOT_FOUND
;
628 /* Check if we have a context */
631 /* Check if this is a link create (which shouldn't be an open) */
632 if (Context
->CreateLink
)
634 return STATUS_ACCESS_DENIED
;
637 /* Check if this is symlink create attempt */
638 if (Context
->CreateOptions
& REG_OPTION_CREATE_LINK
)
640 /* Key already exists */
641 return STATUS_OBJECT_NAME_COLLISION
;
644 /* Set the disposition */
645 Context
->Disposition
= REG_OPENED_EXISTING_KEY
;
648 /* Lock the KCB on creation if asked */
649 if (ControlFlags
& CMP_CREATE_KCB_KCB_LOCKED
)
654 /* Check if caller doesn't want to create a KCB */
655 if (ControlFlags
& CMP_OPEN_KCB_NO_CREATE
)
658 * The caller doesn't want to create a KCB. This means the KCB
659 * is already in cache and other threads may take use of it
660 * so it has to be locked in share mode.
664 /* Check if this is a symlink */
665 if (((*CachedKcb
)->Flags
& KEY_SYM_LINK
) && !(Attributes
& OBJ_OPENLINK
))
667 /* Is this symlink found? */
668 if ((*CachedKcb
)->ExtFlags
& CM_KCB_SYM_LINK_FOUND
)
670 /* Get the real KCB, is this deleted? */
671 Kcb
= (*CachedKcb
)->ValueCache
.RealKcb
;
675 * The real KCB is gone, do a reparse. We used to lock the KCB in
676 * shared mode as others may have taken use of it but since we
677 * must do a reparse of the key the only thing that matter is us.
678 * Lock the KCB exclusively so nobody is going to mess with the KCB.
680 DPRINT1("The real KCB is deleted, attempt a reparse\n");
681 CmpUnLockKcbArray(KcbsLocked
);
682 CmpAcquireKcbLockExclusiveByIndex(GET_HASH_INDEX((*CachedKcb
)->ConvKey
));
683 CmpCleanUpKcbValueCache(*CachedKcb
);
685 KcbsLocked
[1] = GET_HASH_INDEX((*CachedKcb
)->ConvKey
);
686 return STATUS_REPARSE
;
690 * The symlink has been found. As in the similar case above,
691 * the KCB of the symlink exclusively, we don't want anybody
694 CmpUnLockKcbArray(KcbsLocked
);
695 CmpAcquireKcbLockExclusiveByIndex(GET_HASH_INDEX((*CachedKcb
)->ConvKey
));
697 KcbsLocked
[1] = GET_HASH_INDEX((*CachedKcb
)->ConvKey
);
701 /* We must do a reparse */
702 DPRINT("The symlink is not found, attempt a reparse\n");
703 return STATUS_REPARSE
;
708 /* This is not a symlink, just give the cached KCB already */
712 /* The caller wants to open a cached KCB */
713 if (!CmpReferenceKeyControlBlock(Kcb
))
715 /* Return failure code */
716 return STATUS_INSUFFICIENT_RESOURCES
;
722 * The caller wants to create a new KCB. Unlike the code path above, here
723 * we must check if the lock is exclusively held because in the scenario
724 * where the caller doesn't want to create a KCB is because it is already
725 * in the cache and it must have a shared lock instead.
727 ASSERT(CmpIsKcbLockedExclusive(*CachedKcb
));
729 /* Check if this is a symlink */
730 if ((Node
->Flags
& KEY_SYM_LINK
) && !(Attributes
& OBJ_OPENLINK
))
732 /* Create the KCB for the symlink */
733 Kcb
= CmpCreateKeyControlBlock(Hive
,
737 LockKcb
? CMP_LOCK_HASHES_FOR_KCB
: 0,
742 return STATUS_INSUFFICIENT_RESOURCES
;
745 /* Make sure it's also locked, and set the pointer */
746 ASSERT(CmpIsKcbLockedExclusive(Kcb
));
749 /* Return reparse required */
750 return STATUS_REPARSE
;
754 Kcb
= CmpCreateKeyControlBlock(Hive
,
758 LockKcb
? CMP_LOCK_HASHES_FOR_KCB
: 0,
763 return STATUS_INSUFFICIENT_RESOURCES
;
766 /* Make sure it's also locked, and set the pointer */
767 ASSERT(CmpIsKcbLockedExclusive(Kcb
));
771 /* Allocate the key object */
772 Status
= ObCreateObject(AccessMode
,
781 if (NT_SUCCESS(Status
))
783 /* Get the key body and fill it out */
784 KeyBody
= (PCM_KEY_BODY
)(*Object
);
785 KeyBody
->KeyControlBlock
= Kcb
;
786 KeyBody
->Type
= CM_KEY_BODY_TYPE
;
787 KeyBody
->ProcessID
= PsGetCurrentProcessId();
788 KeyBody
->NotifyBlock
= NULL
;
790 /* Link to the KCB */
791 EnlistKeyBodyWithKCB(KeyBody
, IsLockShared
? CMP_ENLIST_KCB_LOCKED_SHARED
: CMP_ENLIST_KCB_LOCKED_EXCLUSIVE
);
794 * We are already holding a lock against the KCB that is assigned
795 * to this key body. This is to prevent a potential deadlock on
796 * CmpSecurityMethod as ObCheckObjectAccess will invoke the Object
797 * Manager to call that method, of which CmpSecurityMethod would
798 * attempt to acquire a lock again.
800 KeyBody
->KcbLocked
= TRUE
;
802 if (!ObCheckObjectAccess(*Object
,
808 /* Access check failed */
809 ObDereferenceObject(*Object
);
813 * We are done, the lock we are holding will be released
814 * once the registry parsing is done.
816 KeyBody
->KcbLocked
= FALSE
;
820 /* Failed, dereference the KCB */
821 CmpDereferenceKeyControlBlockWithLock(Kcb
, FALSE
);
830 CmpCreateLinkNode(IN PHHIVE Hive
,
832 IN PACCESS_STATE AccessState
,
833 IN UNICODE_STRING Name
,
834 IN KPROCESSOR_MODE AccessMode
,
835 IN ULONG CreateOptions
,
836 IN PCM_PARSE_CONTEXT Context
,
837 IN PCM_KEY_CONTROL_BLOCK ParentKcb
,
838 IN PULONG KcbsLocked
,
842 HCELL_INDEX KeyCell
, LinkCell
, ChildCell
;
843 PCM_KEY_BODY KeyBody
;
844 LARGE_INTEGER TimeStamp
;
845 PCM_KEY_NODE KeyNode
;
846 PCM_KEY_CONTROL_BLOCK Kcb
= ParentKcb
;
848 /* Link nodes only allowed on the master */
849 if (Hive
!= &CmiVolatileHive
->Hive
)
852 DPRINT1("Invalid link node attempt\n");
853 return STATUS_ACCESS_DENIED
;
856 /* Make sure the KCB is locked and lock the flusher */
857 CMP_ASSERT_KCB_LOCK(ParentKcb
);
858 CmpLockHiveFlusherShared((PCMHIVE
)Hive
);
859 CmpLockHiveFlusherShared((PCMHIVE
)Context
->ChildHive
.KeyHive
);
861 /* Bail out on read-only KCBs */
862 if (ParentKcb
->ExtFlags
& CM_KCB_READ_ONLY_KEY
)
864 Status
= STATUS_ACCESS_DENIED
;
868 /* Check if the parent is being deleted */
869 if (ParentKcb
->Delete
)
873 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
877 /* Allocate a link node */
878 LinkCell
= HvAllocateCell(Hive
,
879 FIELD_OFFSET(CM_KEY_NODE
, Name
) +
880 CmpNameSize(Hive
, &Name
),
883 if (LinkCell
== HCELL_NIL
)
886 Status
= STATUS_INSUFFICIENT_RESOURCES
;
890 /* Get the key cell */
891 KeyCell
= Context
->ChildHive
.KeyCell
;
892 if (KeyCell
!= HCELL_NIL
)
897 /* Get the node data */
898 KeyNode
= (PCM_KEY_NODE
)HvGetCell(Context
->ChildHive
.KeyHive
, ChildCell
);
903 Status
= STATUS_INSUFFICIENT_RESOURCES
;
907 /* Fill out the data */
908 KeyNode
->Parent
= LinkCell
;
909 KeyNode
->Flags
|= KEY_HIVE_ENTRY
| KEY_NO_DELETE
;
910 HvReleaseCell(Context
->ChildHive
.KeyHive
, ChildCell
);
912 /* Now open the key cell */
913 KeyNode
= (PCM_KEY_NODE
)HvGetCell(Context
->ChildHive
.KeyHive
, KeyCell
);
918 Status
= STATUS_INSUFFICIENT_RESOURCES
;
922 /* Open the parent */
923 Status
= CmpDoOpen(Context
->ChildHive
.KeyHive
,
930 CMP_CREATE_KCB_KCB_LOCKED
,
935 HvReleaseCell(Context
->ChildHive
.KeyHive
, KeyCell
);
939 /* Do the actual create operation */
940 Status
= CmpDoCreateChild(Context
->ChildHive
.KeyHive
,
948 KEY_HIVE_ENTRY
| KEY_NO_DELETE
,
951 if (NT_SUCCESS(Status
))
953 /* Setup root pointer */
954 Context
->ChildHive
.KeyHive
->BaseBlock
->RootCell
= ChildCell
;
958 /* Check if open or create suceeded */
959 if (NT_SUCCESS(Status
))
961 /* Mark the cell dirty */
962 HvMarkCellDirty(Context
->ChildHive
.KeyHive
, ChildCell
, FALSE
);
964 /* Get the key node */
965 KeyNode
= (PCM_KEY_NODE
)HvGetCell(Context
->ChildHive
.KeyHive
, ChildCell
);
970 Status
= STATUS_INSUFFICIENT_RESOURCES
;
975 HvReleaseCell(Context
->ChildHive
.KeyHive
, ChildCell
);
977 /* Set the parent and flags */
978 KeyNode
->Parent
= LinkCell
;
979 KeyNode
->Flags
|= KEY_HIVE_ENTRY
| KEY_NO_DELETE
;
981 /* Get the link node */
982 KeyNode
= (PCM_KEY_NODE
)HvGetCell(Hive
, LinkCell
);
987 Status
= STATUS_INSUFFICIENT_RESOURCES
;
992 KeyNode
->Signature
= CM_LINK_NODE_SIGNATURE
;
993 KeyNode
->Flags
= KEY_HIVE_EXIT
| KEY_NO_DELETE
;
994 KeyNode
->Parent
= Cell
;
995 KeyNode
->NameLength
= CmpCopyName(Hive
, KeyNode
->Name
, &Name
);
996 if (KeyNode
->NameLength
< Name
.Length
) KeyNode
->Flags
|= KEY_COMP_NAME
;
997 KeQuerySystemTime(&TimeStamp
);
998 KeyNode
->LastWriteTime
= TimeStamp
;
1000 /* Clear out the rest */
1001 KeyNode
->SubKeyCounts
[Stable
] = 0;
1002 KeyNode
->SubKeyCounts
[Volatile
] = 0;
1003 KeyNode
->SubKeyLists
[Stable
] = HCELL_NIL
;
1004 KeyNode
->SubKeyLists
[Volatile
] = HCELL_NIL
;
1005 KeyNode
->ValueList
.Count
= 0;
1006 KeyNode
->ValueList
.List
= HCELL_NIL
;
1007 KeyNode
->ClassLength
= 0;
1009 /* Reference the root node */
1010 KeyNode
->ChildHiveReference
.KeyHive
= Context
->ChildHive
.KeyHive
;
1011 KeyNode
->ChildHiveReference
.KeyCell
= ChildCell
;
1012 HvReleaseCell(Hive
, LinkCell
);
1014 /* Get the parent node */
1015 KeyNode
= (PCM_KEY_NODE
)HvGetCell(Hive
, Cell
);
1020 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1024 /* Now add the subkey */
1025 if (!CmpAddSubKey(Hive
, Cell
, LinkCell
))
1027 /* Failure! We don't handle this yet! */
1031 /* Get the key body */
1032 KeyBody
= (PCM_KEY_BODY
)*Object
;
1034 /* Clean up information on this subkey */
1035 CmpCleanUpSubKeyInfo(KeyBody
->KeyControlBlock
->ParentKcb
);
1038 ASSERT(KeyBody
->KeyControlBlock
->ParentKcb
->KeyCell
== Cell
);
1039 ASSERT(KeyBody
->KeyControlBlock
->ParentKcb
->KeyHive
== Hive
);
1040 ASSERT(KeyBody
->KeyControlBlock
->ParentKcb
->KcbMaxNameLen
== KeyNode
->MaxNameLen
);
1042 /* Update the timestamp */
1043 KeQuerySystemTime(&TimeStamp
);
1044 KeyNode
->LastWriteTime
= TimeStamp
;
1045 KeyBody
->KeyControlBlock
->ParentKcb
->KcbLastWriteTime
= TimeStamp
;
1047 /* Check if we need to update name maximum */
1048 if (KeyNode
->MaxNameLen
< Name
.Length
)
1051 KeyNode
->MaxNameLen
= Name
.Length
;
1052 KeyBody
->KeyControlBlock
->ParentKcb
->KcbMaxNameLen
= Name
.Length
;
1055 /* Check if we need to update class length maximum */
1056 if (KeyNode
->MaxClassLen
< Context
->Class
.Length
)
1059 KeyNode
->MaxClassLen
= Context
->Class
.Length
;
1062 /* Release the cell */
1063 HvReleaseCell(Hive
, Cell
);
1067 /* Release the link cell */
1068 HvReleaseCell(Hive
, LinkCell
);
1072 /* Release the flusher locks and return status */
1073 CmpUnlockHiveFlusher((PCMHIVE
)Context
->ChildHive
.KeyHive
);
1074 CmpUnlockHiveFlusher((PCMHIVE
)Hive
);
1080 CmpHandleExitNode(IN OUT PHHIVE
*Hive
,
1081 IN OUT HCELL_INDEX
*Cell
,
1082 IN OUT PCM_KEY_NODE
*KeyNode
,
1083 IN OUT PHHIVE
*ReleaseHive
,
1084 IN OUT HCELL_INDEX
*ReleaseCell
)
1086 /* Check if we have anything to release */
1087 if (*ReleaseCell
!= HCELL_NIL
)
1090 ASSERT(*ReleaseHive
!= NULL
);
1091 HvReleaseCell(*ReleaseHive
, *ReleaseCell
);
1094 /* Get the link references */
1095 *Hive
= (*KeyNode
)->ChildHiveReference
.KeyHive
;
1096 *Cell
= (*KeyNode
)->ChildHiveReference
.KeyCell
;
1098 /* Get the new node */
1099 *KeyNode
= (PCM_KEY_NODE
)HvGetCell(*Hive
, *Cell
);
1102 /* Set the new release values */
1103 *ReleaseCell
= *Cell
;
1104 *ReleaseHive
= *Hive
;
1108 /* Nothing to release */
1109 *ReleaseCell
= HCELL_NIL
;
1110 *ReleaseHive
= NULL
;
1116 * Computes the hashes of each subkey in key path name
1117 * and stores them in a hash stack for cache lookup.
1119 * @param[in] RemainingName
1120 * A Unicode string structure consisting of the remaining
1121 * registry key path name.
1123 * @param[in] ConvKey
1124 * The hash convkey of the current KCB to be supplied.
1126 * @param[in,out] HashCacheStack
1127 * An array stack. This function uses this array to store
1128 * all the computed hashes of a key pathname.
1130 * @param[out] TotalSubKeys
1131 * The number of total subkeys that have been found, returned
1132 * by this function to the caller. If no subkey levels are found
1133 * the function returns 0.
1136 * Returns the number of remaining subkey levels to caller.
1137 * If no subkey levels are found then this function returns 0.
1141 CmpComputeHashValue(
1142 _In_ PUNICODE_STRING RemainingName
,
1144 _Inout_ PCM_HASH_CACHE_STACK HashCacheStack
,
1145 _Out_ PULONG TotalSubKeys
)
1148 ULONG SubkeysInTotal
;
1149 ULONG RemainingSubkeysInTotal
;
1150 PWCHAR RemainingNameBuffer
;
1151 USHORT RemainingNameLength
;
1152 USHORT KeyNameLength
;
1154 /* Don't compute the hashes on a NULL remaining name */
1155 RemainingNameBuffer
= RemainingName
->Buffer
;
1156 RemainingNameLength
= RemainingName
->Length
;
1157 if (RemainingNameLength
== 0)
1163 /* Skip any leading separator */
1164 while (RemainingNameLength
>= sizeof(WCHAR
) &&
1165 *RemainingNameBuffer
== OBJ_NAME_PATH_SEPARATOR
)
1167 RemainingNameBuffer
++;
1168 RemainingNameLength
-= sizeof(WCHAR
);
1171 /* Now set up the hash stack entries and compute the hashes */
1173 RemainingSubkeysInTotal
= 0;
1175 CopyConvKey
= ConvKey
;
1176 HashCacheStack
[RemainingSubkeysInTotal
].NameOfKey
.Buffer
= RemainingNameBuffer
;
1177 while (RemainingNameLength
> 0)
1179 /* Is this character a separator? */
1180 if (*RemainingNameBuffer
!= OBJ_NAME_PATH_SEPARATOR
)
1182 /* It's not, add it to the hash */
1183 CopyConvKey
= COMPUTE_HASH_CHAR(CopyConvKey
, *RemainingNameBuffer
);
1185 /* Go to the next character (add up the length of the character as well) */
1186 RemainingNameBuffer
++;
1187 KeyNameLength
+= sizeof(WCHAR
);
1188 RemainingNameLength
-= sizeof(WCHAR
);
1191 * We are at the end of the key name path. Take into account
1192 * the last character and if we still have space in the hash
1193 * stack, add it up in the remaining list.
1195 if (RemainingNameLength
== 0)
1197 if (RemainingSubkeysInTotal
< CMP_SUBKEY_LEVELS_DEPTH_LIMIT
)
1199 HashCacheStack
[RemainingSubkeysInTotal
].NameOfKey
.Length
= KeyNameLength
;
1200 HashCacheStack
[RemainingSubkeysInTotal
].NameOfKey
.MaximumLength
= KeyNameLength
;
1201 HashCacheStack
[RemainingSubkeysInTotal
].ConvKey
= CopyConvKey
;
1202 RemainingSubkeysInTotal
++;
1210 /* Skip any leading separator */
1211 while (RemainingNameLength
>= sizeof(WCHAR
) &&
1212 *RemainingNameBuffer
== OBJ_NAME_PATH_SEPARATOR
)
1214 RemainingNameBuffer
++;
1215 RemainingNameLength
-= sizeof(WCHAR
);
1219 * It would be possible that a malformed key pathname may be passed
1220 * to the registry parser such as a path with only separators like
1221 * "\\\\" for example. This would trick the function into believing
1222 * the key path has subkeys albeit that is not the case.
1224 ASSERT(RemainingNameLength
!= 0);
1226 /* Take into account this subkey */
1229 /* And add it up to the hash stack */
1230 if (RemainingSubkeysInTotal
< CMP_SUBKEY_LEVELS_DEPTH_LIMIT
)
1232 HashCacheStack
[RemainingSubkeysInTotal
].NameOfKey
.Length
= KeyNameLength
;
1233 HashCacheStack
[RemainingSubkeysInTotal
].NameOfKey
.MaximumLength
= KeyNameLength
;
1234 HashCacheStack
[RemainingSubkeysInTotal
].ConvKey
= CopyConvKey
;
1236 RemainingSubkeysInTotal
++;
1240 * Precaution check -- we have added up a remaining
1241 * subkey above but we must ensure we still have space
1242 * to hold up the new subkey for which we will compute
1243 * the hashes, so that we don't blow up the hash stack.
1245 if (RemainingSubkeysInTotal
< CMP_SUBKEY_LEVELS_DEPTH_LIMIT
)
1247 HashCacheStack
[RemainingSubkeysInTotal
].NameOfKey
.Buffer
= RemainingNameBuffer
;
1253 *TotalSubKeys
= SubkeysInTotal
;
1254 return RemainingSubkeysInTotal
;
1259 * Compares each subkey's hash and name with those
1260 * captured in the hash cache stack.
1262 * @param[in] HashCacheStack
1263 * A pointer to a hash cache stack array filled with
1264 * subkey hashes and names for comparison.
1266 * @param[in] CurrentKcb
1267 * A pointer to the currently given KCB.
1269 * @param[in] RemainingSubkeys
1270 * The remaining subkey levels to be supplied.
1272 * @param[out] ParentKcb
1273 * A pointer to the parent KCB returned to the caller.
1274 * This parameter points to the parent of the current
1275 * KCB if all the subkeys match, otherwise it points
1276 * to the actual current KCB.
1279 * Returns TRUE if all the subkey levels match, otherwise
1280 * FALSE is returned.
1285 _In_ PCM_HASH_CACHE_STACK HashCacheStack
,
1286 _In_ PCM_KEY_CONTROL_BLOCK CurrentKcb
,
1287 _In_ ULONG RemainingSubkeys
,
1288 _Out_ PCM_KEY_CONTROL_BLOCK
*ParentKcb
)
1290 LONG HashStackIndex
;
1292 PCM_NAME_CONTROL_BLOCK NameBlock
;
1293 UNICODE_STRING CurrentNameBlock
;
1295 ASSERT(CurrentKcb
!= NULL
);
1297 /* Loop each hash and check that they match */
1298 HashStackIndex
= RemainingSubkeys
;
1299 while (HashStackIndex
>= 0)
1301 /* Does the subkey hash match? */
1302 if (CurrentKcb
->ConvKey
!= HashCacheStack
[HashStackIndex
].ConvKey
)
1304 *ParentKcb
= CurrentKcb
;
1308 /* Compare the subkey string, is the name compressed? */
1309 NameBlock
= CurrentKcb
->NameBlock
;
1310 if (NameBlock
->Compressed
)
1312 Result
= CmpCompareCompressedName(&HashCacheStack
[HashStackIndex
].NameOfKey
,
1314 NameBlock
->NameLength
);
1318 CurrentNameBlock
.Buffer
= NameBlock
->Name
;
1319 CurrentNameBlock
.Length
= NameBlock
->NameLength
;
1320 CurrentNameBlock
.MaximumLength
= NameBlock
->NameLength
;
1322 Result
= RtlCompareUnicodeString(&HashCacheStack
[HashStackIndex
].NameOfKey
,
1327 /* Do the subkey names match? */
1330 *ParentKcb
= CurrentKcb
;
1334 /* Go to the next subkey hash */
1338 /* All the subkeys match */
1339 *ParentKcb
= CurrentKcb
->ParentKcb
;
1345 * Removes the subkeys on a remaining key pathname.
1347 * @param[in] HashCacheStack
1348 * A pointer to a hash cache stack array filled with
1349 * subkey hashes and names.
1351 * @param[in] RemainingSubkeys
1352 * The remaining subkey levels to be supplied.
1354 * @param[in,out] RemainingName
1355 * A Unicode string structure consisting of the remaining
1356 * registry key path name, where the subkeys of such path
1357 * are to be removed.
1361 CmpRemoveSubkeysInRemainingName(
1362 _In_ PCM_HASH_CACHE_STACK HashCacheStack
,
1363 _In_ ULONG RemainingSubkeys
,
1364 _Inout_ PUNICODE_STRING RemainingName
)
1366 ULONG HashStackIndex
= 0;
1368 /* Skip any leading separator on matching name */
1369 while (RemainingName
->Length
>= sizeof(WCHAR
) &&
1370 RemainingName
->Buffer
[0] == OBJ_NAME_PATH_SEPARATOR
)
1372 RemainingName
->Buffer
++;
1373 RemainingName
->Length
-= sizeof(WCHAR
);
1376 /* Skip the subkeys as well */
1377 while (HashStackIndex
<= RemainingSubkeys
)
1379 RemainingName
->Buffer
+= HashCacheStack
[HashStackIndex
].NameOfKey
.Length
/ sizeof(WCHAR
);
1380 RemainingName
->Length
-= HashCacheStack
[HashStackIndex
].NameOfKey
.Length
;
1382 /* Skip any leading separator */
1383 while (RemainingName
->Length
>= sizeof(WCHAR
) &&
1384 RemainingName
->Buffer
[0] == OBJ_NAME_PATH_SEPARATOR
)
1386 RemainingName
->Buffer
++;
1387 RemainingName
->Length
-= sizeof(WCHAR
);
1390 /* Go to the next hash */
1397 * Looks up in the pool cache for key pathname that matches
1398 * with one in the said cache and returns a KCB pointing
1399 * to that name. This function performs locking of KCBs
1400 * during cache lookup.
1402 * @param[in] HashCacheStack
1403 * A pointer to a hash cache stack array filled with
1404 * subkey hashes and names.
1406 * @param[in] LockKcbsExclusive
1407 * If set to TRUE, the KCBs are locked exclusively by the
1408 * calling thread, otherwise they are locked in shared mode.
1409 * See Remarks for further information.
1411 * @param[in] TotalRemainingSubkeys
1412 * The total remaining subkey levels to be supplied.
1414 * @param[in,out] RemainingName
1415 * A Unicode string structure consisting of the remaining
1416 * registry key path name. The remaining name is updated
1417 * by the function if a key pathname is found in cache.
1419 * @param[in,out] OuterStackArray
1420 * A pointer to an array that lives on the caller's stack.
1421 * The expected size of the array is up to 32 elements,
1422 * which is the imposed limit by CMP_HASH_STACK_LIMIT.
1423 * This limit also corresponds to the maximum depth of
1426 * @param[in,out] Kcb
1427 * A pointer to a KCB, this KCB is changed if the key pathname
1428 * is found in cache.
1431 * A pointer to a hive, this hive is changed if the key pathname
1432 * is found in cache.
1435 * A pointer to a cell, this cell is changed if the key pathname
1436 * is found in cache.
1438 * @param[out] MatchRemainSubkeyLevel
1439 * A pointer to match subkey levels returned by the function.
1440 * If no match levels are found, this is 0.
1443 * Returns STATUS_SUCCESS if cache lookup has completed successfully.
1444 * STATUS_OBJECT_NAME_NOT_FOUND is returned if the current KCB of
1445 * the key pathname has been deleted. STATUS_RETRY is returned if
1446 * at least the current KCB or its parent have been deleted
1447 * and a cache lookup must be retried again. STATUS_UNSUCCESSFUL is
1448 * returned if a KCB is referenced too many times.
1451 * The function attempts to do a cache lookup with a shared lock
1452 * on KCBs so that other threads can simultaneously get access
1453 * to these KCBs. When the captured KCB is being deleted on us
1454 * we have to retry a lookup with exclusive look so that no other
1455 * threads will mess with the KCBs and perform appropriate actions
1456 * if a KCB is deleted.
1461 _In_ PCM_HASH_CACHE_STACK HashCacheStack
,
1462 _In_ BOOLEAN LockKcbsExclusive
,
1463 _In_ ULONG TotalRemainingSubkeys
,
1464 _Inout_ PUNICODE_STRING RemainingName
,
1465 _Inout_ PULONG OuterStackArray
,
1466 _Inout_ PCM_KEY_CONTROL_BLOCK
*Kcb
,
1468 _Out_ PHCELL_INDEX Cell
,
1469 _Out_ PULONG MatchRemainSubkeyLevel
)
1471 LONG RemainingSubkeys
;
1473 BOOLEAN SubkeysMatch
;
1474 PCM_KEY_CONTROL_BLOCK CurrentKcb
, ParentKcb
;
1475 PCM_KEY_HASH HashEntry
= NULL
;
1476 BOOLEAN KeyFoundInCache
= FALSE
;
1477 PULONG LockedKcbs
= NULL
;
1479 /* Reference the KCB */
1480 if (!CmpReferenceKeyControlBlock(*Kcb
))
1482 /* This key is opened too many times, bail out */
1483 DPRINT1("Could not reference the KCB, too many references (KCB 0x%p)\n", Kcb
);
1484 return STATUS_UNSUCCESSFUL
;
1487 /* Prepare to lock the KCBs */
1488 LockedKcbs
= CmpBuildAndLockKcbArray(HashCacheStack
,
1489 LockKcbsExclusive
? CMP_LOCK_KCB_ARRAY_EXCLUSIVE
: CMP_LOCK_KCB_ARRAY_SHARED
,
1492 TotalRemainingSubkeys
,
1494 NT_ASSERT(LockedKcbs
);
1496 /* Lookup in the cache */
1497 RemainingSubkeys
= TotalRemainingSubkeys
- 1;
1498 TotalLevels
= TotalRemainingSubkeys
+ (*Kcb
)->TotalLevels
+ 1;
1499 while (RemainingSubkeys
>= 0)
1501 /* Get the hash entry from the cache */
1502 HashEntry
= GET_HASH_ENTRY(CmpCacheTable
, HashCacheStack
[RemainingSubkeys
].ConvKey
)->Entry
;
1504 /* Take one level down as we are processing this hash entry */
1507 while (HashEntry
!= NULL
)
1509 /* Validate this hash and obtain the current KCB */
1510 ASSERT_VALID_HASH(HashEntry
);
1511 CurrentKcb
= CONTAINING_RECORD(HashEntry
, CM_KEY_CONTROL_BLOCK
, KeyHash
);
1513 /* Does this KCB have matching levels? */
1514 if (TotalLevels
== CurrentKcb
->TotalLevels
)
1517 * We have matching subkey levels but don't directly assume we have
1518 * a matching key path in cache. Start comparing each subkey.
1520 SubkeysMatch
= CmpCompareSubkeys(HashCacheStack
,
1526 /* All subkeys match, now check if the base KCB matches with parent */
1527 if (*Kcb
== ParentKcb
)
1529 /* Is the KCB marked as deleted? */
1530 if (CurrentKcb
->Delete
||
1531 CurrentKcb
->ParentKcb
->Delete
)
1534 * Either the current or its parent KCB is marked
1535 * but we had a shared lock so probably a naughty
1536 * thread was deleting it. Retry doing a cache
1537 * lookup again with exclusive lock.
1539 if (!LockKcbsExclusive
)
1541 CmpUnLockKcbArray(LockedKcbs
);
1542 CmpDereferenceKeyControlBlock(*Kcb
);
1543 DPRINT1("The current KCB or its parent is deleted, retrying looking in the cache\n");
1544 return STATUS_RETRY
;
1547 /* We're under an exclusive lock, is the KCB deleted yet? */
1548 if (CurrentKcb
->Delete
)
1550 /* The KCB is gone, the key should no longer belong in the cache */
1551 CmpRemoveKeyControlBlock(CurrentKcb
);
1552 CmpUnLockKcbArray(LockedKcbs
);
1553 CmpDereferenceKeyControlBlock(*Kcb
);
1554 DPRINT1("The current KCB is deleted (KCB 0x%p)\n", CurrentKcb
);
1555 return STATUS_OBJECT_NAME_NOT_FOUND
;
1559 * The parent is deleted so it must be that somebody created
1560 * a fake key. Assert ourselves that is the case.
1562 ASSERT(CurrentKcb
->ExtFlags
& CM_KCB_KEY_NON_EXIST
);
1564 /* Remove this KCB out of cache if someone still uses it */
1565 if (CurrentKcb
->RefCount
!= 0)
1567 CurrentKcb
->Delete
= TRUE
;
1568 CmpRemoveKeyControlBlock(CurrentKcb
);
1572 /* Otherwise expunge it */
1573 CmpRemoveFromDelayedClose(CurrentKcb
);
1574 CmpCleanUpKcbCacheWithLock(CurrentKcb
, FALSE
);
1577 /* Stop looking for next hashes as the KCB is kaput */
1581 /* We finally found the key in cache, acknowledge it */
1582 KeyFoundInCache
= TRUE
;
1584 /* Remove the subkeys in the remaining name and stop looking in the cache */
1585 CmpRemoveSubkeysInRemainingName(HashCacheStack
, RemainingSubkeys
, RemainingName
);
1591 /* Go to the next hash */
1592 HashEntry
= HashEntry
->NextHash
;
1595 /* Stop looking in cache if we found the matching key */
1596 if (KeyFoundInCache
)
1598 DPRINT("Key found in cache, stop looking\n");
1602 /* Keep looking in the cache until we run out of remaining subkeys */
1606 /* Return the matching subkey levels */
1607 *MatchRemainSubkeyLevel
= RemainingSubkeys
+ 1;
1609 /* We have to update the KCB if the key was found in cache */
1610 if (KeyFoundInCache
)
1613 * Before we change the KCB we must dereference the prior
1614 * KCB that we no longer need it.
1616 CmpDereferenceKeyControlBlock(*Kcb
);
1619 /* Reference the new KCB now */
1620 if (!CmpReferenceKeyControlBlock(*Kcb
))
1622 /* This key is opened too many times, bail out */
1623 DPRINT1("Could not reference the KCB, too many references (KCB 0x%p)\n", Kcb
);
1624 return STATUS_UNSUCCESSFUL
;
1627 /* Update hive and cell data from current KCB */
1628 *Hive
= CurrentKcb
->KeyHive
;
1629 *Cell
= CurrentKcb
->KeyCell
;
1632 /* Unlock the KCBs */
1633 CmpUnLockKcbArray(LockedKcbs
);
1634 return STATUS_SUCCESS
;
1639 * Builds a hash stack cache and looks up in the
1640 * pool cache for a matching key pathname.
1642 * @param[in] ParseObject
1643 * A pointer to a parse object, acting as a key
1644 * body. This parameter is unused.
1646 * @param[in,out] Kcb
1647 * A pointer to a KCB. This KCB is used by the
1648 * registry parser after hash stack and cache
1649 * lookup are done. This KCB might change if the
1650 * key is found to be cached in the cache pool.
1652 * @param[in] Current
1653 * The current remaining key pathname.
1656 * A pointer to a registry hive, returned by the caller.
1659 * A pointer to a hive cell, returned by the caller.
1661 * @param[out] TotalRemainingSubkeys
1662 * A pointer to a number of total remaining subkey levels,
1663 * returned by the caller. This can be 0 if no subkey levels
1666 * @param[out] MatchRemainSubkeyLevel
1667 * A pointer to a number of remaining subkey levels that match,
1668 * returned by the caller. This can be 0 if no matching levels
1671 * @param[out] TotalSubkeys
1672 * A pointer to a number of total subkeys. This can be 0 if no
1673 * subkey levels are found. By definition, both MatchRemainSubkeyLevel
1674 * and TotalRemainingSubkeys are 0 as well.
1676 * @param[in,out] OuterStackArray
1677 * A pointer to an array that lives on the caller's stack.
1678 * The expected size of the array is up to 32 elements,
1679 * which is the imposed limit by CMP_HASH_STACK_LIMIT.
1680 * This limit also corresponds to the maximum depth of
1683 * @param[out] LockedKcbs
1684 * A pointer to an array of locked KCBs, returned by the caller.
1687 * Returns STATUS_SUCCESS if all the operations have succeeded without
1688 * problems. STATUS_NAME_TOO_LONG is returned if the key pathname has
1689 * too many subkey levels (more than 32 levels deep). A failure NTSTATUS
1690 * code is returned otherwise. Refer to CmpLookInCache documentation
1691 * for more information about other returned status codes.
1692 * STATUS_UNSUCCESSFUL is returned if a KCB is referenced too many times.
1696 CmpBuildHashStackAndLookupCache(
1697 _In_ PCM_KEY_BODY ParseObject
,
1698 _Inout_ PCM_KEY_CONTROL_BLOCK
*Kcb
,
1699 _In_ PUNICODE_STRING Current
,
1701 _Out_ PHCELL_INDEX Cell
,
1702 _Out_ PULONG TotalRemainingSubkeys
,
1703 _Out_ PULONG MatchRemainSubkeyLevel
,
1704 _Out_ PULONG TotalSubkeys
,
1705 _Inout_ PULONG OuterStackArray
,
1706 _Out_ PULONG
*LockedKcbs
)
1710 ULONG SubkeysInTotal
, RemainingSubkeysInTotal
, MatchRemainingSubkeys
;
1711 CM_HASH_CACHE_STACK HashCacheStack
[CMP_SUBKEY_LEVELS_DEPTH_LIMIT
];
1713 /* Make sure it's not a dead KCB */
1714 ASSERT((*Kcb
)->RefCount
> 0);
1716 /* Lock the registry */
1719 /* Calculate hash values for every subkey this key path has */
1720 ConvKey
= (*Kcb
)->ConvKey
;
1721 RemainingSubkeysInTotal
= CmpComputeHashValue(Current
,
1726 /* This key path has too many subkeys */
1727 if (SubkeysInTotal
> CMP_SUBKEY_LEVELS_DEPTH_LIMIT
)
1729 DPRINT1("The key path has too many subkeys - %lu\n", SubkeysInTotal
);
1731 return STATUS_NAME_TOO_LONG
;
1734 /* Return hive and cell data */
1735 *Hive
= (*Kcb
)->KeyHive
;
1736 *Cell
= (*Kcb
)->KeyCell
;
1738 /* Do we have any subkeys? */
1739 if (!RemainingSubkeysInTotal
&& !SubkeysInTotal
)
1742 * We don't have any subkeys nor remaining levels, the
1743 * KCB points to the actual key. Lock it.
1745 if (!CmpReferenceKeyControlBlock(*Kcb
))
1747 /* This key is opened too many times, bail out */
1748 DPRINT1("Could not reference the KCB, too many references (KCB 0x%p)\n", Kcb
);
1749 return STATUS_UNSUCCESSFUL
;
1752 CmpAcquireKcbLockSharedByIndex(GET_HASH_INDEX(ConvKey
));
1754 /* Add this KCB in the array of locked KCBs */
1755 OuterStackArray
[0] = 1;
1756 OuterStackArray
[1] = GET_HASH_INDEX(ConvKey
);
1757 *LockedKcbs
= OuterStackArray
;
1759 /* And return all the subkey level counters */
1760 *TotalRemainingSubkeys
= RemainingSubkeysInTotal
;
1761 *MatchRemainSubkeyLevel
= 0;
1762 *TotalSubkeys
= SubkeysInTotal
;
1763 return STATUS_SUCCESS
;
1766 /* Lookup in the cache */
1767 Status
= CmpLookInCache(HashCacheStack
,
1769 RemainingSubkeysInTotal
,
1775 &MatchRemainingSubkeys
);
1776 if (!NT_SUCCESS(Status
))
1778 /* Bail out if cache lookup failed for other reasons */
1779 if (Status
!= STATUS_RETRY
)
1781 DPRINT1("CmpLookInCache() failed (Status 0x%lx)\n", Status
);
1786 /* Retry looking in the cache but with KCBs locked exclusively */
1787 Status
= CmpLookInCache(HashCacheStack
,
1789 RemainingSubkeysInTotal
,
1795 &MatchRemainingSubkeys
);
1796 if (!NT_SUCCESS(Status
))
1798 DPRINT1("CmpLookInCache() failed after retry (Status 0x%lx)\n", Status
);
1805 * Check if we have a full match of remaining levels.
1807 * FIXME: It is possible we can catch a fake key from the cache
1808 * when we did the lookup, in such case we should not do any
1809 * locking as such KCB does not point to any real information.
1810 * Currently ReactOS doesn't create fake KCBs so we are good
1813 if (RemainingSubkeysInTotal
== MatchRemainingSubkeys
)
1816 * Just simply lock this KCB as it points to the full
1817 * subkey levels in cache.
1819 CmpAcquireKcbLockSharedByIndex(GET_HASH_INDEX((*Kcb
)->ConvKey
));
1820 OuterStackArray
[0] = 1;
1821 OuterStackArray
[1] = GET_HASH_INDEX((*Kcb
)->ConvKey
);
1822 *LockedKcbs
= OuterStackArray
;
1827 * We only have a partial match so other subkey levels
1828 * have each KCB. Simply just lock them.
1830 *LockedKcbs
= CmpBuildAndLockKcbArray(HashCacheStack
,
1831 CMP_LOCK_KCB_ARRAY_EXCLUSIVE
,
1834 RemainingSubkeysInTotal
,
1835 MatchRemainingSubkeys
);
1836 NT_ASSERT(*LockedKcbs
);
1839 /* Return all the subkey level counters */
1840 *TotalRemainingSubkeys
= RemainingSubkeysInTotal
;
1841 *MatchRemainSubkeyLevel
= MatchRemainingSubkeys
;
1842 *TotalSubkeys
= SubkeysInTotal
;
1848 CmpParseKey(IN PVOID ParseObject
,
1849 IN PVOID ObjectType
,
1850 IN OUT PACCESS_STATE AccessState
,
1851 IN KPROCESSOR_MODE AccessMode
,
1852 IN ULONG Attributes
,
1853 IN OUT PUNICODE_STRING CompleteName
,
1854 IN OUT PUNICODE_STRING RemainingName
,
1855 IN OUT PVOID Context OPTIONAL
,
1856 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL
,
1860 PCM_KEY_CONTROL_BLOCK Kcb
, ParentKcb
;
1862 PCM_KEY_NODE Node
= NULL
;
1863 HCELL_INDEX Cell
= HCELL_NIL
, NextCell
;
1864 PHHIVE HiveToRelease
= NULL
;
1865 HCELL_INDEX CellToRelease
= HCELL_NIL
;
1866 UNICODE_STRING Current
, NextName
;
1867 PCM_PARSE_CONTEXT ParseContext
= Context
;
1868 ULONG TotalRemainingSubkeys
= 0, MatchRemainSubkeyLevel
= 0, TotalSubkeys
= 0;
1869 ULONG LockedKcbArray
[CMP_KCBS_IN_ARRAY_LIMIT
];
1871 BOOLEAN IsKeyCached
= FALSE
;
1872 BOOLEAN Result
, Last
;
1875 /* Loop path separators at the end */
1876 while (RemainingName
->Length
&&
1877 (RemainingName
->Buffer
[(RemainingName
->Length
/ sizeof(WCHAR
)) - 1] ==
1878 OBJ_NAME_PATH_SEPARATOR
))
1880 /* Remove path separator */
1881 RemainingName
->Length
-= sizeof(WCHAR
);
1884 /* Fail if this isn't a key object */
1885 if (ObjectType
!= CmpKeyObjectType
) return STATUS_OBJECT_TYPE_MISMATCH
;
1887 /* Copy the remaining name */
1888 Current
= *RemainingName
;
1890 /* Check if this is a create */
1891 if (!ParseContext
|| !ParseContext
->CreateOperation
)
1893 /* It isn't, so no context */
1894 ParseContext
= NULL
;
1898 Kcb
= ((PCM_KEY_BODY
)ParseObject
)->KeyControlBlock
;
1901 ASSERT(Kcb
!= NULL
);
1903 /* Fail if the key was marked as deleted */
1905 return STATUS_KEY_DELETED
;
1907 /* Lookup in the cache */
1908 Status
= CmpBuildHashStackAndLookupCache(ParseObject
,
1913 &TotalRemainingSubkeys
,
1914 &MatchRemainSubkeyLevel
,
1918 CMP_ASSERT_REGISTRY_LOCK();
1919 if (!NT_SUCCESS(Status
))
1921 DPRINT1("Failed to look in cache, stop parsing (Status 0x%lx)\n", Status
);
1926 /* This is now the parent */
1930 ASSERT(ParentKcb
!= NULL
);
1932 /* Don't do anything if we're being deleted */
1935 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
1939 /* Check if everything was found cached */
1940 if (!TotalRemainingSubkeys
)
1943 * We don't have any remaining subkey levels so we're good
1944 * that we have an already perfect candidate for a KCB, just
1945 * do the open directly.
1947 DPRINT("No remaining subkeys, the KCB points to the actual key\n");
1949 goto KeyCachedOpenNow
;
1952 /* Check if we have a matching level */
1953 if (MatchRemainSubkeyLevel
)
1956 * We have a matching level, check if that matches
1957 * with the total levels of subkeys. Do the open directly
1958 * if that is the case, because the whole subkeys levels
1961 if (MatchRemainSubkeyLevel
== TotalSubkeys
)
1963 DPRINT("We have a full matching level, open the key now\n");
1965 goto KeyCachedOpenNow
;
1969 * We only have a partial match, make sure we did not
1970 * get mismatched hive data.
1972 ASSERT(Hive
== Kcb
->KeyHive
);
1973 ASSERT(Cell
== Kcb
->KeyCell
);
1977 * FIXME: Currently the registry parser doesn't check for fake
1978 * KCBs. CmpCreateKeyControlBlock does have the necessary implementation
1979 * to create such fake keys but we don't create these fake keys anywhere.
1980 * When we will do, we must improve the registry parser routine to handle
1981 * fake keys a bit differently here.
1984 /* Check if this is a symlink */
1985 if (Kcb
->Flags
& KEY_SYM_LINK
)
1987 /* Get the next name */
1988 Result
= CmpGetNextName(&Current
, &NextName
, &Last
);
1989 Current
.Buffer
= NextName
.Buffer
;
1991 /* Validate the current name string length */
1992 if (Current
.Length
+ NextName
.Length
> MAXUSHORT
)
1995 Status
= STATUS_NAME_TOO_LONG
;
1998 Current
.Length
+= NextName
.Length
;
2000 /* Validate the current name string maximum length */
2001 if (Current
.MaximumLength
+ NextName
.MaximumLength
> MAXUSHORT
)
2004 Status
= STATUS_NAME_TOO_LONG
;
2007 Current
.MaximumLength
+= NextName
.MaximumLength
;
2009 /* CmpGetSymbolicLink doesn't want a lock */
2010 CmpUnLockKcbArray(LockedKcbs
);
2013 /* Parse the symlink */
2014 if (CmpGetSymbolicLink(Hive
,
2019 /* Symlink parse succeeded */
2020 Status
= STATUS_REPARSE
;
2024 /* Couldn't find symlink */
2025 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
2032 /* Get the key node */
2033 Node
= (PCM_KEY_NODE
)HvGetCell(Hive
, Cell
);
2036 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2041 Status
= STATUS_NOT_IMPLEMENTED
;
2044 /* Get the next component */
2045 Result
= CmpGetNextName(&Current
, &NextName
, &Last
);
2046 if (Result
&& NextName
.Length
)
2048 /* See if this is a sym link */
2049 if (!(Kcb
->Flags
& KEY_SYM_LINK
))
2051 /* Find the subkey */
2052 NextCell
= CmpFindSubKeyByName(Hive
, Node
, &NextName
);
2053 if (NextCell
!= HCELL_NIL
)
2055 /* Get the new node */
2057 Node
= (PCM_KEY_NODE
)HvGetCell(Hive
, Cell
);
2060 /* Check if this was the last key */
2063 /* Is this an exit node */
2064 if (Node
->Flags
& KEY_HIVE_EXIT
)
2067 CmpHandleExitNode(&Hive
,
2075 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2082 Status
= CmpDoOpen(Hive
,
2089 IsKeyCached
? CMP_OPEN_KCB_NO_CREATE
: CMP_CREATE_KCB_KCB_LOCKED
,
2094 if (Status
== STATUS_REPARSE
)
2096 /* CmpGetSymbolicLink doesn't want a lock */
2097 CmpUnLockKcbArray(LockedKcbs
);
2100 /* Parse the symlink */
2101 if (!CmpGetSymbolicLink(Hive
,
2106 /* Symlink parse failed */
2107 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
2115 /* Is this an exit node */
2116 if (Node
->Flags
& KEY_HIVE_EXIT
)
2119 CmpHandleExitNode(&Hive
,
2127 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2132 /* Create a KCB for this key */
2133 Kcb
= CmpCreateKeyControlBlock(Hive
,
2137 CMP_LOCK_HASHES_FOR_KCB
,
2142 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2146 /* Dereference the parent and set the new one */
2147 CmpDereferenceKeyControlBlockWithLock(ParentKcb
, FALSE
);
2152 /* Check if this was the last key for a create */
2153 if (Last
&& ParseContext
)
2155 /* Check if we're doing a link node */
2156 if (ParseContext
->CreateLink
)
2158 /* The only thing we should see */
2159 Status
= CmpCreateLinkNode(Hive
,
2170 else if (Hive
== &CmiVolatileHive
->Hive
&& CmpNoVolatileCreates
)
2172 /* Creating keys in the master hive is not allowed */
2173 Status
= STATUS_INVALID_PARAMETER
;
2178 Status
= CmpDoCreate(Hive
,
2188 /* Check for reparse (in this case, someone beat us) */
2189 if (Status
== STATUS_REPARSE
) break;
2191 /* Update disposition */
2192 ParseContext
->Disposition
= REG_CREATED_NEW_KEY
;
2198 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
2205 /* Save the next name */
2206 Current
.Buffer
= NextName
.Buffer
;
2208 /* Validate the current name string length */
2209 if (Current
.Length
+ NextName
.Length
> MAXUSHORT
)
2212 Status
= STATUS_NAME_TOO_LONG
;
2215 Current
.Length
+= NextName
.Length
;
2217 /* Validate the current name string maximum length */
2218 if (Current
.MaximumLength
+ NextName
.MaximumLength
> MAXUSHORT
)
2221 Status
= STATUS_NAME_TOO_LONG
;
2224 Current
.MaximumLength
+= NextName
.MaximumLength
;
2226 /* CmpGetSymbolicLink doesn't want a lock */
2227 CmpUnLockKcbArray(LockedKcbs
);
2230 /* Parse the symlink */
2231 if (CmpGetSymbolicLink(Hive
,
2236 /* Symlink parse succeeded */
2237 Status
= STATUS_REPARSE
;
2241 /* Couldn't find symlink */
2242 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
2249 else if (Result
&& Last
)
2251 /* Opening the root. Is this an exit node? */
2252 if (Node
->Flags
& KEY_HIVE_EXIT
)
2255 CmpHandleExitNode(&Hive
,
2263 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2269 Status
= CmpDoOpen(Hive
,
2276 CMP_OPEN_KCB_NO_CREATE
,
2281 if (Status
== STATUS_REPARSE
)
2292 Status
= STATUS_INVALID_PARAMETER
;
2298 /* Unlock all the KCBs */
2299 if (LockedKcbs
!= NULL
)
2301 CmpUnLockKcbArray(LockedKcbs
);
2304 /* Dereference the parent if it exists */
2306 CmpDereferenceKeyControlBlock(ParentKcb
);
2308 /* Unlock the registry */
2309 CmpUnlockRegistry();