2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/config/cmkcbncb.c
5 * PURPOSE: Routines for handling KCBs, NCBs, as well as key hashes.
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES ******************************************************************/
15 /* GLOBALS *******************************************************************/
17 ULONG CmpHashTableSize
= 2048;
18 PCM_KEY_HASH_TABLE_ENTRY CmpCacheTable
;
19 PCM_NAME_HASH_TABLE_ENTRY CmpNameCacheTable
;
21 /* FUNCTIONS *****************************************************************/
26 CmpInitializeCache(VOID
)
30 /* Calculate length for the table */
31 Length
= CmpHashTableSize
* sizeof(CM_KEY_HASH_TABLE_ENTRY
);
34 CmpCacheTable
= CmpAllocate(Length
, TRUE
, TAG_CM
);
37 /* Take the system down */
38 KeBugCheckEx(CONFIG_INITIALIZATION_FAILED
, 3, 1, 0, 0);
41 /* Zero out the table */
42 RtlZeroMemory(CmpCacheTable
, Length
);
44 /* Initialize the locks */
45 for (i
= 0;i
< CmpHashTableSize
; i
++)
47 /* Setup the pushlock */
48 ExInitializePushLock((PULONG_PTR
)&CmpCacheTable
[i
].Lock
);
51 /* Calculate length for the name cache */
52 Length
= CmpHashTableSize
* sizeof(CM_NAME_HASH_TABLE_ENTRY
);
54 /* Now allocate the name cache table */
55 CmpNameCacheTable
= CmpAllocate(Length
, TRUE
, TAG_CM
);
56 if (!CmpNameCacheTable
)
58 /* Take the system down */
59 KeBugCheckEx(CONFIG_INITIALIZATION_FAILED
, 3, 3, 0, 0);
62 /* Zero out the table */
63 RtlZeroMemory(CmpNameCacheTable
, Length
);
65 /* Initialize the locks */
66 for (i
= 0;i
< CmpHashTableSize
; i
++)
68 /* Setup the pushlock */
69 ExInitializePushLock((PULONG_PTR
)&CmpNameCacheTable
[i
].Lock
);
72 /* Setup the delayed close table */
73 CmpInitializeDelayedCloseTable();
78 CmpRemoveKeyHash(IN PCM_KEY_HASH KeyHash
)
82 ASSERT_VALID_HASH(KeyHash
);
84 /* Lookup all the keys in this index entry */
85 Prev
= &GET_HASH_ENTRY(CmpCacheTable
, KeyHash
->ConvKey
).Entry
;
88 /* Save the current one and make sure it's valid */
90 ASSERT(Current
!= NULL
);
91 ASSERT_VALID_HASH(Current
);
93 /* Check if it matches */
94 if (Current
== KeyHash
)
96 /* Then write the previous one */
97 *Prev
= Current
->NextHash
;
98 if (*Prev
) ASSERT_VALID_HASH(*Prev
);
102 /* Otherwise, keep going */
103 Prev
= &Current
->NextHash
;
107 PCM_KEY_CONTROL_BLOCK
109 CmpInsertKeyHash(IN PCM_KEY_HASH KeyHash
,
114 ASSERT_VALID_HASH(KeyHash
);
116 /* Get the hash index */
117 i
= GET_HASH_INDEX(KeyHash
->ConvKey
);
119 /* If this is a fake key, increase the key cell to use the parent data */
120 if (IsFake
) KeyHash
->KeyCell
++;
122 /* Loop the hash table */
123 Entry
= CmpCacheTable
[i
].Entry
;
126 /* Check if this matches */
127 ASSERT_VALID_HASH(Entry
);
128 if ((KeyHash
->ConvKey
== Entry
->ConvKey
) &&
129 (KeyHash
->KeyCell
== Entry
->KeyCell
) &&
130 (KeyHash
->KeyHive
== Entry
->KeyHive
))
133 return CONTAINING_RECORD(Entry
, CM_KEY_CONTROL_BLOCK
, KeyHash
);
137 Entry
= Entry
->NextHash
;
140 /* No entry found, add this one and return NULL since none existed */
141 KeyHash
->NextHash
= CmpCacheTable
[i
].Entry
;
142 CmpCacheTable
[i
].Entry
= KeyHash
;
146 PCM_NAME_CONTROL_BLOCK
148 CmpGetNameControlBlock(IN PUNICODE_STRING NodeName
)
150 PCM_NAME_CONTROL_BLOCK Ncb
= NULL
;
154 BOOLEAN IsCompressed
= TRUE
, Found
= FALSE
;
155 PCM_NAME_HASH HashEntry
;
156 ULONG Length
, NcbSize
;
159 p
= NodeName
->Buffer
;
160 for (i
= 0; i
< NodeName
->Length
; i
+= sizeof(WCHAR
))
162 /* Make sure it's not a slash */
163 if (*p
!= OBJ_NAME_PATH_SEPARATOR
)
165 /* Add it to the hash */
166 ConvKey
= 37 * ConvKey
+ RtlUpcaseUnicodeChar(*p
);
173 /* Set assumed lengh and loop to check */
174 Length
= NodeName
->Length
/ sizeof(WCHAR
);
175 for (i
= 0; i
< (NodeName
->Length
/ sizeof(WCHAR
)); i
++)
177 /* Check if this is a 16-bit character */
178 if (NodeName
->Buffer
[i
] > (UCHAR
)-1)
180 /* This is the actual size, and we know we're not compressed */
181 Length
= NodeName
->Length
;
182 IsCompressed
= FALSE
;
187 /* Lock the NCB entry */
188 CmpAcquireNcbLockExclusiveByKey(ConvKey
);
190 /* Get the hash entry */
191 HashEntry
= GET_HASH_ENTRY(CmpNameCacheTable
, ConvKey
).Entry
;
194 /* Get the current NCB */
195 Ncb
= CONTAINING_RECORD(HashEntry
, CM_NAME_CONTROL_BLOCK
, NameHash
);
197 /* Check if the hash matches */
198 if ((ConvKey
== HashEntry
->ConvKey
) && (Length
== Ncb
->NameLength
))
203 /* If the NCB is compressed, do a compressed name compare */
207 if (CmpCompareCompressedName(NodeName
, Ncb
->Name
, Length
))
215 /* Do a manual compare */
216 p
= NodeName
->Buffer
;
218 for (i
= 0; i
< Ncb
->NameLength
; i
+= sizeof(WCHAR
))
220 /* Compare the character */
221 if (RtlUpcaseUnicodeChar(*p
) != RtlUpcaseUnicodeChar(*pp
))
234 /* Check if we found a name */
238 ASSERT(Ncb
->RefCount
!= 0xFFFF);
244 /* Go to the next hash */
245 HashEntry
= HashEntry
->NextHash
;
248 /* Check if we didn't find it */
252 NcbSize
= FIELD_OFFSET(CM_NAME_CONTROL_BLOCK
, Name
) + Length
;
253 Ncb
= CmpAllocate(NcbSize
, TRUE
, TAG_CM
);
256 /* Release the lock and fail */
257 CmpReleaseNcbLockByKey(ConvKey
);
262 RtlZeroMemory(Ncb
, NcbSize
);
264 /* Check if the name was compressed */
267 /* Copy the compressed name */
268 for (i
= 0; i
< NodeName
->Length
/ sizeof(WCHAR
); i
++)
270 /* Copy Unicode to ANSI */
271 ((PCHAR
)Ncb
->Name
)[i
] = (CHAR
)RtlUpcaseUnicodeChar(NodeName
->Buffer
[i
]);
276 /* Copy the name directly */
277 for (i
= 0; i
< NodeName
->Length
/ sizeof(WCHAR
); i
++)
279 /* Copy each unicode character */
280 Ncb
->Name
[i
] = RtlUpcaseUnicodeChar(NodeName
->Buffer
[i
]);
284 /* Setup the rest of the NCB */
285 Ncb
->Compressed
= IsCompressed
;
286 Ncb
->ConvKey
= ConvKey
;
288 Ncb
->NameLength
= Length
;
290 /* Insert the name in the hash table */
291 HashEntry
= &Ncb
->NameHash
;
292 HashEntry
->NextHash
= GET_HASH_ENTRY(CmpNameCacheTable
, ConvKey
).Entry
;
293 GET_HASH_ENTRY(CmpNameCacheTable
, ConvKey
).Entry
= HashEntry
;
296 /* Release NCB lock */
297 CmpReleaseNcbLockByKey(ConvKey
);
299 /* Return the NCB found */
305 CmpRemoveKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb
)
307 /* Make sure that the registry and KCB are utterly locked */
308 ASSERT((CmpIsKcbLockedExclusive(Kcb
) == TRUE
) ||
309 (CmpTestRegistryLockExclusive() == TRUE
));
311 /* Remove the key hash */
312 CmpRemoveKeyHash(&Kcb
->KeyHash
);
317 CmpDereferenceNameControlBlockWithLock(IN PCM_NAME_CONTROL_BLOCK Ncb
)
319 PCM_NAME_HASH Current
, *Next
;
320 ULONG ConvKey
= Ncb
->ConvKey
;
323 CmpAcquireNcbLockExclusiveByKey(ConvKey
);
325 /* Decrease the reference count */
326 ASSERT(Ncb
->RefCount
>= 1);
327 if (!(--Ncb
->RefCount
))
329 /* Find the NCB in the table */
330 Next
= &GET_HASH_ENTRY(CmpNameCacheTable
, Ncb
->ConvKey
).Entry
;
333 /* Check the current entry */
335 ASSERT(Current
!= NULL
);
336 if (Current
== &Ncb
->NameHash
)
339 *Next
= Current
->NextHash
;
343 /* Get to the next one */
344 Next
= &Current
->NextHash
;
347 /* Found it, now free it */
351 /* Release the lock */
352 CmpReleaseNcbLockByKey(ConvKey
);
357 CmpReferenceKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb
)
359 CMTRACE(CM_REFERENCE_DEBUG
,
360 "%s - Referencing KCB: %p\n", __FUNCTION__
, Kcb
);
362 /* Check if this is the KCB's first reference */
363 if (Kcb
->RefCount
== 0)
365 /* Check if the KCB is locked in shared mode */
366 if (!CmpIsKcbLockedExclusive(Kcb
))
368 /* Convert it to exclusive */
369 if (!CmpTryToConvertKcbSharedToExclusive(Kcb
))
371 /* Set the delayed close index so that we can be ignored */
372 Kcb
->DelayedCloseIndex
= 1;
374 /* Increase the reference count while we release the lock */
375 InterlockedIncrement((PLONG
)&Kcb
->RefCount
);
377 /* Go from shared to exclusive */
378 CmpConvertKcbSharedToExclusive(Kcb
);
380 /* Decrement the reference count; the lock is now held again */
381 InterlockedDecrement((PLONG
)&Kcb
->RefCount
);
383 /* Check if we still control the index */
384 if (Kcb
->DelayedCloseIndex
== 1)
387 Kcb
->DelayedCloseIndex
= 0;
392 ASSERT((Kcb
->DelayedCloseIndex
== CmpDelayedCloseSize
) ||
393 (Kcb
->DelayedCloseIndex
== 0));
399 /* Increase the reference count */
400 if ((InterlockedIncrement((PLONG
)&Kcb
->RefCount
) & 0xFFFF) == 0)
402 /* We've overflown to 64K references, bail out */
403 InterlockedDecrement((PLONG
)&Kcb
->RefCount
);
407 /* Check if this was the last close index */
408 if (!Kcb
->DelayedCloseIndex
)
410 /* Check if the KCB is locked in shared mode */
411 if (!CmpIsKcbLockedExclusive(Kcb
))
413 /* Convert it to exclusive */
414 if (!CmpTryToConvertKcbSharedToExclusive(Kcb
))
416 /* Go from shared to exclusive */
417 CmpConvertKcbSharedToExclusive(Kcb
);
421 /* If we're still the last entry, remove us */
422 if (!Kcb
->DelayedCloseIndex
) CmpRemoveFromDelayedClose(Kcb
);
431 CmpCleanUpKcbValueCache(IN PCM_KEY_CONTROL_BLOCK Kcb
)
433 PULONG_PTR CachedList
;
437 ASSERT((CmpIsKcbLockedExclusive(Kcb
) == TRUE
) ||
438 (CmpTestRegistryLockExclusive() == TRUE
));
440 /* Check if the value list is cached */
441 if (CMP_IS_CELL_CACHED(Kcb
->ValueCache
.ValueList
))
443 /* Get the cache list */
444 CachedList
= (PULONG_PTR
)CMP_GET_CACHED_DATA(Kcb
->ValueCache
.ValueList
);
445 for (i
= 0; i
< Kcb
->ValueCache
.Count
; i
++)
447 /* Check if this cell is cached */
448 if (CMP_IS_CELL_CACHED(CachedList
[i
]))
451 CmpFree((PVOID
)CMP_GET_CACHED_CELL(CachedList
[i
]), 0);
455 /* Now free the list */
456 CmpFree((PVOID
)CMP_GET_CACHED_CELL(Kcb
->ValueCache
.ValueList
), 0);
457 Kcb
->ValueCache
.ValueList
= HCELL_NIL
;
459 else if (Kcb
->ExtFlags
& CM_KCB_SYM_LINK_FOUND
)
461 /* This is a sym link, check if there's only one reference left */
462 if ((Kcb
->ValueCache
.RealKcb
->RefCount
== 1) &&
463 !(Kcb
->ValueCache
.RealKcb
->Delete
))
465 /* Disable delay close for the KCB */
466 Kcb
->ValueCache
.RealKcb
->ExtFlags
|= CM_KCB_NO_DELAY_CLOSE
;
469 /* Dereference the KCB */
470 CmpDelayDerefKeyControlBlock(Kcb
->ValueCache
.RealKcb
);
471 Kcb
->ExtFlags
&= ~CM_KCB_SYM_LINK_FOUND
;
477 CmpCleanUpKcbCacheWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb
,
478 IN BOOLEAN LockHeldExclusively
)
480 PCM_KEY_CONTROL_BLOCK Parent
;
484 ASSERT((CmpIsKcbLockedExclusive(Kcb
) == TRUE
) ||
485 (CmpTestRegistryLockExclusive() == TRUE
));
486 ASSERT(Kcb
->RefCount
== 0);
488 /* Cleanup the value cache */
489 CmpCleanUpKcbValueCache(Kcb
);
491 /* Dereference the NCB */
492 CmpDereferenceNameControlBlockWithLock(Kcb
->NameBlock
);
494 /* Check if we have an index hint block and free it */
495 if (Kcb
->ExtFlags
& CM_KCB_SUBKEY_HINT
) CmpFree(Kcb
->IndexHint
, 0);
497 /* Check if we were already deleted */
498 Parent
= Kcb
->ParentKcb
;
499 if (!Kcb
->Delete
) CmpRemoveKeyControlBlock(Kcb
);
501 /* Set invalid KCB signature */
502 Kcb
->Signature
= CM_KCB_INVALID_SIGNATURE
;
504 /* Free the KCB as well */
505 CmpFreeKeyControlBlock(Kcb
);
507 /* Check if we have a parent */
510 /* Dereference the parent */
511 LockHeldExclusively
?
512 CmpDereferenceKeyControlBlockWithLock(Parent
,LockHeldExclusively
) :
513 CmpDelayDerefKeyControlBlock(Parent
);
519 CmpCleanUpSubKeyInfo(IN PCM_KEY_CONTROL_BLOCK Kcb
)
521 PCM_KEY_NODE KeyNode
;
524 ASSERT((CmpIsKcbLockedExclusive(Kcb
) == TRUE
) ||
525 (CmpTestRegistryLockExclusive() == TRUE
));
527 /* Check if there's any cached subkey */
528 if (Kcb
->ExtFlags
& (CM_KCB_NO_SUBKEY
| CM_KCB_SUBKEY_ONE
| CM_KCB_SUBKEY_HINT
))
530 /* Check if there's a hint */
531 if (Kcb
->ExtFlags
& (CM_KCB_SUBKEY_HINT
))
534 CmpFree(Kcb
->IndexHint
, 0);
537 /* Remove subkey flags */
538 Kcb
->ExtFlags
&= ~(CM_KCB_NO_SUBKEY
| CM_KCB_SUBKEY_ONE
| CM_KCB_SUBKEY_HINT
);
541 /* Check if there's no linked cell */
542 if (Kcb
->KeyCell
== HCELL_NIL
)
544 /* Make sure it's a delete */
550 /* Get the key node */
551 KeyNode
= (PCM_KEY_NODE
)HvGetCell(Kcb
->KeyHive
, Kcb
->KeyCell
);
554 /* Check if we got the node */
557 /* We didn't, mark the cached data invalid */
558 Kcb
->ExtFlags
|= CM_KCB_INVALID_CACHED_INFO
;
562 /* We have a keynode, update subkey counts */
563 Kcb
->ExtFlags
&= ~CM_KCB_INVALID_CACHED_INFO
;
564 Kcb
->SubKeyCount
= KeyNode
->SubKeyCounts
[Stable
] +
565 KeyNode
->SubKeyCounts
[Volatile
];
567 /* Release the cell */
568 HvReleaseCell(Kcb
->KeyHive
, Kcb
->KeyCell
);
574 CmpDereferenceKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb
)
576 LONG OldRefCount
, NewRefCount
;
578 CMTRACE(CM_REFERENCE_DEBUG
,
579 "%s - Dereferencing KCB: %p\n", __FUNCTION__
, Kcb
);
581 /* Get the ref count and update it */
582 OldRefCount
= *(PLONG
)&Kcb
->RefCount
;
583 NewRefCount
= OldRefCount
- 1;
585 /* Check if we still have references */
586 if ((NewRefCount
& 0xFFFF) > 0)
588 /* Do the dereference */
589 if (InterlockedCompareExchange((PLONG
)&Kcb
->RefCount
,
591 OldRefCount
) == OldRefCount
)
599 ConvKey
= Kcb
->ConvKey
;
601 /* Do the dereference inside the lock */
602 CmpAcquireKcbLockExclusive(Kcb
);
603 CmpDereferenceKeyControlBlockWithLock(Kcb
, FALSE
);
604 CmpReleaseKcbLockByKey(ConvKey
);
609 CmpDereferenceKeyControlBlockWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb
,
610 IN BOOLEAN LockHeldExclusively
)
612 CMTRACE(CM_REFERENCE_DEBUG
,
613 "%s - Dereferencing KCB: %p\n", __FUNCTION__
, Kcb
);
616 ASSERT_KCB_VALID(Kcb
);
618 /* Check if this is the last reference */
619 if ((InterlockedDecrement((PLONG
)&Kcb
->RefCount
) & 0xFFFF) == 0)
622 ASSERT((CmpIsKcbLockedExclusive(Kcb
) == TRUE
) ||
623 (CmpTestRegistryLockExclusive() == TRUE
));
625 /* Check if we should do a direct delete */
626 if (((CmpHoldLazyFlush
) &&
627 !(Kcb
->ExtFlags
& CM_KCB_SYM_LINK_FOUND
) &&
628 !(Kcb
->Flags
& KEY_SYM_LINK
)) ||
629 (Kcb
->ExtFlags
& CM_KCB_NO_DELAY_CLOSE
) ||
632 /* Clean up the KCB*/
633 CmpCleanUpKcbCacheWithLock(Kcb
, LockHeldExclusively
);
637 /* Otherwise, use delayed close */
638 CmpAddToDelayedClose(Kcb
, LockHeldExclusively
);
645 InitializeKCBKeyBodyList(IN PCM_KEY_CONTROL_BLOCK Kcb
)
647 /* Initialize the list */
648 InitializeListHead(&Kcb
->KeyBodyListHead
);
650 /* Clear the bodies */
651 Kcb
->KeyBodyArray
[0] =
652 Kcb
->KeyBodyArray
[1] =
653 Kcb
->KeyBodyArray
[2] =
654 Kcb
->KeyBodyArray
[3] = NULL
;
657 PCM_KEY_CONTROL_BLOCK
659 CmpCreateKeyControlBlock(IN PHHIVE Hive
,
660 IN HCELL_INDEX Index
,
661 IN PCM_KEY_NODE Node
,
662 IN PCM_KEY_CONTROL_BLOCK Parent
,
664 IN PUNICODE_STRING KeyName
)
666 PCM_KEY_CONTROL_BLOCK Kcb
, FoundKcb
= NULL
;
667 UNICODE_STRING NodeName
;
668 ULONG ConvKey
= 0, i
;
669 BOOLEAN IsFake
, HashLock
;
672 /* Make sure we own this hive in case it's being unloaded */
673 if ((Hive
->HiveFlags
& HIVE_IS_UNLOADING
) &&
674 (((PCMHIVE
)Hive
)->CreatorOwner
!= KeGetCurrentThread()))
680 /* Check if this is a fake KCB */
681 IsFake
= Flags
& CMP_CREATE_FAKE_KCB
? TRUE
: FALSE
;
683 /* If we have a parent, use its ConvKey */
684 if (Parent
) ConvKey
= Parent
->ConvKey
;
686 /* Make a copy of the name */
689 /* Remove leading slash */
690 while ((NodeName
.Length
) && (*NodeName
.Buffer
== OBJ_NAME_PATH_SEPARATOR
))
692 /* Move the buffer by one */
694 NodeName
.Length
-= sizeof(WCHAR
);
697 /* Make sure we didn't get just a slash or something */
698 ASSERT(NodeName
.Length
> 0);
700 /* Now setup the hash */
702 for (i
= 0; i
< NodeName
.Length
; i
+= sizeof(WCHAR
))
704 /* Make sure it's a valid character */
705 if (*p
!= OBJ_NAME_PATH_SEPARATOR
)
707 /* Add this key to the hash */
708 ConvKey
= 37 * ConvKey
+ RtlUpcaseUnicodeChar(*p
);
715 /* Allocate the KCB */
716 Kcb
= CmpAllocateKeyControlBlock();
717 if (!Kcb
) return NULL
;
719 /* Initailize the key list */
720 InitializeKCBKeyBodyList(Kcb
);
723 Kcb
->Signature
= CM_KCB_SIGNATURE
;
727 Kcb
->KeyCell
= Index
;
728 Kcb
->ConvKey
= ConvKey
;
729 Kcb
->DelayedCloseIndex
= CmpDelayedCloseSize
;
730 Kcb
->InDelayClose
= 0;
731 ASSERT_KCB_VALID(Kcb
);
733 /* Check if we have two hash entires */
734 HashLock
= Flags
& CMP_LOCK_HASHES_FOR_KCB
? TRUE
: FALSE
;
737 /* It's not locked, do we have a parent? */
740 /* Lock the parent KCB and ourselves */
741 CmpAcquireTwoKcbLocksExclusiveByKey(ConvKey
, Parent
->ConvKey
);
745 /* Lock only ourselves */
746 CmpAcquireKcbLockExclusive(Kcb
);
750 /* Check if we already have a KCB */
751 FoundKcb
= CmpInsertKeyHash(&Kcb
->KeyHash
, IsFake
);
755 ASSERT(!FoundKcb
->Delete
);
756 Kcb
->Signature
= CM_KCB_INVALID_SIGNATURE
;
758 /* Free the one we allocated and reference this one */
759 CmpFreeKeyControlBlock(Kcb
);
760 ASSERT_KCB_VALID(FoundKcb
);
762 if (!CmpReferenceKeyControlBlock(Kcb
))
764 /* We got too many handles */
765 ASSERT(Kcb
->RefCount
+ 1 != 0);
770 /* Check if we're not creating a fake one, but it used to be fake */
771 if ((Kcb
->ExtFlags
& CM_KCB_KEY_NON_EXIST
) && !(IsFake
))
773 /* Set the hive and cell */
775 Kcb
->KeyCell
= Index
;
777 /* This means that our current information is invalid */
778 Kcb
->ExtFlags
= CM_KCB_INVALID_CACHED_INFO
;
781 /* Check if we didn't have any valid data */
782 if (!(Kcb
->ExtFlags
& (CM_KCB_NO_SUBKEY
|
784 CM_KCB_SUBKEY_HINT
)))
786 /* Calculate the index hint */
787 Kcb
->SubKeyCount
= Node
->SubKeyCounts
[Stable
] +
788 Node
->SubKeyCounts
[Volatile
];
790 /* Cached information is now valid */
791 Kcb
->ExtFlags
&= ~CM_KCB_INVALID_CACHED_INFO
;
794 /* Setup the other data */
795 Kcb
->KcbLastWriteTime
= Node
->LastWriteTime
;
796 Kcb
->KcbMaxNameLen
= (USHORT
)Node
->MaxNameLen
;
797 Kcb
->KcbMaxValueNameLen
= (USHORT
)Node
->MaxValueNameLen
;
798 Kcb
->KcbMaxValueDataLen
= Node
->MaxValueDataLen
;
803 /* No KCB, do we have a parent? */
806 /* Reference the parent */
807 if (((Parent
->TotalLevels
+ 1) < 512) &&
808 (CmpReferenceKeyControlBlock(Parent
)))
811 Kcb
->ParentKcb
= Parent
;
812 Kcb
->TotalLevels
= Parent
->TotalLevels
+ 1;
816 /* Remove the KCB and free it */
817 CmpRemoveKeyControlBlock(Kcb
);
818 Kcb
->Signature
= CM_KCB_INVALID_SIGNATURE
;
819 CmpFreeKeyControlBlock(Kcb
);
825 /* No parent, this is the root node */
826 Kcb
->ParentKcb
= NULL
;
827 Kcb
->TotalLevels
= 1;
830 /* Check if we have a KCB */
834 Kcb
->NameBlock
= CmpGetNameControlBlock(&NodeName
);
838 Kcb
->ValueCache
.Count
= Node
->ValueList
.Count
;
839 Kcb
->ValueCache
.ValueList
= Node
->ValueList
.List
;
840 Kcb
->Flags
= Node
->Flags
;
842 Kcb
->DelayedCloseIndex
= CmpDelayedCloseSize
;
844 /* Remember if this is a fake key */
845 if (IsFake
) Kcb
->ExtFlags
|= CM_KCB_KEY_NON_EXIST
;
847 /* Setup the other data */
848 Kcb
->SubKeyCount
= Node
->SubKeyCounts
[Stable
] +
849 Node
->SubKeyCounts
[Volatile
];
850 Kcb
->KcbLastWriteTime
= Node
->LastWriteTime
;
851 Kcb
->KcbMaxNameLen
= (USHORT
)Node
->MaxNameLen
;
852 Kcb
->KcbMaxValueNameLen
= (USHORT
)Node
->MaxValueNameLen
;
853 Kcb
->KcbMaxValueDataLen
= (USHORT
)Node
->MaxValueDataLen
;
857 /* Dereference the KCB */
858 CmpDereferenceKeyControlBlockWithLock(Parent
, FALSE
);
860 /* Remove the KCB and free it */
861 CmpRemoveKeyControlBlock(Kcb
);
862 Kcb
->Signature
= CM_KCB_INVALID_SIGNATURE
;
863 CmpFreeKeyControlBlock(Kcb
);
869 /* Check if this is a KCB inside a frozen hive */
870 if ((Kcb
) && (((PCMHIVE
)Hive
)->Frozen
) && (!(Kcb
->Flags
& KEY_SYM_LINK
)))
872 /* Don't add these to the delay close */
873 Kcb
->ExtFlags
|= CM_KCB_NO_DELAY_CLOSE
;
877 ASSERT((!Kcb
) || (Kcb
->Delete
== FALSE
));
879 /* Check if we had locked the hashes */
882 /* We locked them manually, do we have a parent? */
885 /* Unlock the parent KCB and ourselves */
886 CmpReleaseTwoKcbLockByKey(ConvKey
, Parent
->ConvKey
);
890 /* Unlock only ourselves */
891 CmpReleaseKcbLockByKey(ConvKey
);
901 CmpConstructName(IN PCM_KEY_CONTROL_BLOCK Kcb
)
903 PUNICODE_STRING KeyName
;
905 PCM_KEY_CONTROL_BLOCK MyKcb
;
906 PCM_KEY_NODE KeyNode
;
907 BOOLEAN DeletedKey
= FALSE
;
908 PWCHAR TargetBuffer
, CurrentNameW
;
911 /* Calculate how much size our key name is going to occupy */
917 /* Add length of the name */
918 if (!MyKcb
->NameBlock
->Compressed
)
920 NameLength
+= MyKcb
->NameBlock
->NameLength
;
924 NameLength
+= CmpCompressedNameSize(MyKcb
->NameBlock
->Name
,
925 MyKcb
->NameBlock
->NameLength
);
928 /* Sum up the separator too */
929 NameLength
+= sizeof(WCHAR
);
931 /* Go to the parent KCB */
932 MyKcb
= MyKcb
->ParentKcb
;
935 /* Allocate the unicode string now */
936 KeyName
= CmpAllocate(NameLength
+ sizeof(UNICODE_STRING
),
940 if (!KeyName
) return NULL
;
943 KeyName
->Buffer
= (PWSTR
)(KeyName
+ 1);
944 KeyName
->Length
= NameLength
;
945 KeyName
->MaximumLength
= NameLength
;
947 /* Loop the keys again, now adding names */
953 /* Sanity checks for deleted and fake keys */
954 if ((!MyKcb
->KeyCell
&& !MyKcb
->Delete
) ||
956 MyKcb
->ExtFlags
& CM_KCB_KEY_NON_EXIST
)
963 /* Try to get the name from the keynode,
964 if the key is not deleted */
965 if (!DeletedKey
&& !MyKcb
->Delete
)
967 KeyNode
= HvGetCell(MyKcb
->KeyHive
, MyKcb
->KeyCell
);
978 /* The key was deleted */
983 /* Get the pointer to the beginning of the current key name */
984 NameLength
+= (MyKcb
->NameBlock
->NameLength
+ 1) * sizeof(WCHAR
);
985 TargetBuffer
= &KeyName
->Buffer
[(KeyName
->Length
- NameLength
) / sizeof(WCHAR
)];
987 /* Add a separator */
988 TargetBuffer
[0] = OBJ_NAME_PATH_SEPARATOR
;
990 /* Add the name, but remember to go from the end to the beginning */
991 if (!MyKcb
->NameBlock
->Compressed
)
993 /* Get the pointer to the name (from the keynode, if possible) */
994 if ((MyKcb
->Flags
& (KEY_HIVE_ENTRY
| KEY_HIVE_EXIT
)) ||
997 CurrentNameW
= MyKcb
->NameBlock
->Name
;
1001 CurrentNameW
= KeyNode
->Name
;
1005 for (i
=0; i
< MyKcb
->NameBlock
->NameLength
; i
++)
1007 TargetBuffer
[i
+1] = *CurrentNameW
;
1013 /* Get the pointer to the name (from the keynode, if possible) */
1014 if ((MyKcb
->Flags
& (KEY_HIVE_ENTRY
| KEY_HIVE_EXIT
)) ||
1017 CurrentName
= (PUCHAR
)MyKcb
->NameBlock
->Name
;
1021 CurrentName
= (PUCHAR
)KeyNode
->Name
;
1025 for (i
=0; i
< MyKcb
->NameBlock
->NameLength
; i
++)
1027 TargetBuffer
[i
+1] = (WCHAR
)*CurrentName
;
1032 /* Release the cell, if needed */
1033 if (KeyNode
) HvReleaseCell(MyKcb
->KeyHive
, MyKcb
->KeyCell
);
1035 /* Go to the parent KCB */
1036 MyKcb
= MyKcb
->ParentKcb
;
1039 /* Return resulting buffer (both UNICODE_STRING and
1040 its buffer following it) */
1046 EnlistKeyBodyWithKCB(IN PCM_KEY_BODY KeyBody
,
1052 ASSERT(KeyBody
->KeyControlBlock
!= NULL
);
1054 /* Initialize the list entry */
1055 InitializeListHead(&KeyBody
->KeyBodyList
);
1057 /* Check if we can use the parent KCB array */
1058 for (i
= 0; i
< 4; i
++)
1060 /* Add it into the list */
1061 if (!InterlockedCompareExchangePointer((PVOID
*)&KeyBody
->KeyControlBlock
->
1071 /* Array full, check if we need to unlock the KCB */
1072 if (Flags
& CMP_ENLIST_KCB_LOCKED_SHARED
)
1074 /* It's shared, so release the KCB shared lock */
1075 CmpReleaseKcbLock(KeyBody
->KeyControlBlock
);
1076 ASSERT(!(Flags
& CMP_ENLIST_KCB_LOCKED_EXCLUSIVE
));
1079 /* Check if we need to lock the KCB */
1080 if (!(Flags
& CMP_ENLIST_KCB_LOCKED_EXCLUSIVE
))
1082 /* Acquire the lock */
1083 CmpAcquireKcbLockExclusive(KeyBody
->KeyControlBlock
);
1086 /* Make sure we have the exclusive lock */
1087 ASSERT((CmpIsKcbLockedExclusive(KeyBody
->KeyControlBlock
) == TRUE
) ||
1088 (CmpTestRegistryLockExclusive() == TRUE
));
1091 InsertTailList(&KeyBody
->KeyControlBlock
->KeyBodyListHead
,
1092 &KeyBody
->KeyBodyList
);
1094 /* Check if we did a manual lock */
1095 if (!(Flags
& (CMP_ENLIST_KCB_LOCKED_SHARED
|
1096 CMP_ENLIST_KCB_LOCKED_EXCLUSIVE
)))
1098 /* Release the lock */
1099 CmpReleaseKcbLock(KeyBody
->KeyControlBlock
);
1105 DelistKeyBodyFromKCB(IN PCM_KEY_BODY KeyBody
,
1106 IN BOOLEAN LockHeld
)
1111 ASSERT(KeyBody
->KeyControlBlock
!= NULL
);
1113 /* Check if we can use the parent KCB array */
1114 for (i
= 0; i
< 4; i
++)
1116 /* Add it into the list */
1117 if (InterlockedCompareExchangePointer((VOID
*)&KeyBody
->KeyControlBlock
->
1120 KeyBody
) == KeyBody
)
1128 ASSERT(IsListEmpty(&KeyBody
->KeyControlBlock
->KeyBodyListHead
) == FALSE
);
1129 ASSERT(IsListEmpty(&KeyBody
->KeyBodyList
) == FALSE
);
1132 if (!LockHeld
) CmpAcquireKcbLockExclusive(KeyBody
->KeyControlBlock
);
1133 ASSERT((CmpIsKcbLockedExclusive(KeyBody
->KeyControlBlock
) == TRUE
) ||
1134 (CmpTestRegistryLockExclusive() == TRUE
));
1136 /* Remove the entry */
1137 RemoveEntryList(&KeyBody
->KeyBodyList
);
1139 /* Unlock it it if we did a manual lock */
1140 if (!LockHeld
) CmpReleaseKcbLock(KeyBody
->KeyControlBlock
);
1145 CmpFlushNotifiesOnKeyBodyList(IN PCM_KEY_CONTROL_BLOCK Kcb
,
1146 IN BOOLEAN LockHeld
)
1148 PLIST_ENTRY NextEntry
, ListHead
;
1149 PCM_KEY_BODY KeyBody
;
1152 LockHeld
? CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK() : CmpIsKcbLockedExclusive(Kcb
);
1155 /* Is the list empty? */
1156 ListHead
= &Kcb
->KeyBodyListHead
;
1157 if (!IsListEmpty(ListHead
))
1160 NextEntry
= ListHead
->Flink
;
1161 while (NextEntry
!= ListHead
)
1163 /* Get the key body */
1164 KeyBody
= CONTAINING_RECORD(NextEntry
, CM_KEY_BODY
, KeyBodyList
);
1165 ASSERT(KeyBody
->Type
== '20yk');
1167 /* Check for notifications */
1168 if (KeyBody
->NotifyBlock
)
1170 /* Is the lock held? */
1174 CmpFlushNotify(KeyBody
, LockHeld
);
1175 ASSERT(KeyBody
->NotifyBlock
== NULL
);
1179 /* Lock isn't held, so we need to take a reference */
1180 if (ObReferenceObjectSafe(KeyBody
))
1182 /* Now we can flush */
1183 CmpFlushNotify(KeyBody
, LockHeld
);
1184 ASSERT(KeyBody
->NotifyBlock
== NULL
);
1186 /* Release the reference we took */
1187 ObDereferenceObjectDeferDelete(KeyBody
);
1192 /* Try the next entry */
1193 NextEntry
= NextEntry
->Flink
;
1197 /* List has been parsed, exit */