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 *****************************************************************/
25 CmpInitializeCache(VOID
)
29 /* Calculate length for the table */
30 Length
= CmpHashTableSize
* sizeof(CM_KEY_HASH_TABLE_ENTRY
);
33 CmpCacheTable
= CmpAllocate(Length
, TRUE
, TAG_CM
);
36 /* Take the system down */
37 KeBugCheckEx(CONFIG_INITIALIZATION_FAILED
, 3, 1, 0, 0);
40 /* Zero out the table */
41 RtlZeroMemory(CmpCacheTable
, Length
);
43 /* Initialize the locks */
44 for (i
= 0;i
< CmpHashTableSize
; i
++)
46 /* Setup the pushlock */
47 ExInitializePushLock((PULONG_PTR
)&CmpCacheTable
[i
].Lock
);
50 /* Calculate length for the name cache */
51 Length
= CmpHashTableSize
* sizeof(CM_NAME_HASH_TABLE_ENTRY
);
53 /* Now allocate the name cache table */
54 CmpNameCacheTable
= CmpAllocate(Length
, TRUE
, TAG_CM
);
55 if (!CmpNameCacheTable
)
57 /* Take the system down */
58 KeBugCheckEx(CONFIG_INITIALIZATION_FAILED
, 3, 3, 0, 0);
61 /* Zero out the table */
62 RtlZeroMemory(CmpNameCacheTable
, Length
);
64 /* Initialize the locks */
65 for (i
= 0;i
< CmpHashTableSize
; i
++)
67 /* Setup the pushlock */
68 ExInitializePushLock((PULONG_PTR
)&CmpNameCacheTable
[i
].Lock
);
71 /* Setup the delayed close table */
72 CmpInitializeDelayedCloseTable();
77 CmpRemoveKeyHash(IN PCM_KEY_HASH KeyHash
)
81 ASSERT_VALID_HASH(KeyHash
);
83 /* Lookup all the keys in this index entry */
84 Prev
= &GET_HASH_ENTRY(CmpCacheTable
, KeyHash
->ConvKey
).Entry
;
87 /* Save the current one and make sure it's valid */
89 ASSERT(Current
!= NULL
);
90 ASSERT_VALID_HASH(Current
);
92 /* Check if it matches */
93 if (Current
== KeyHash
)
95 /* Then write the previous one */
96 *Prev
= Current
->NextHash
;
97 if (*Prev
) ASSERT_VALID_HASH(*Prev
);
101 /* Otherwise, keep going */
102 Prev
= &Current
->NextHash
;
106 PCM_KEY_CONTROL_BLOCK
108 CmpInsertKeyHash(IN PCM_KEY_HASH KeyHash
,
113 ASSERT_VALID_HASH(KeyHash
);
115 /* Get the hash index */
116 i
= GET_HASH_INDEX(KeyHash
->ConvKey
);
118 /* If this is a fake key, increase the key cell to use the parent data */
119 if (IsFake
) KeyHash
->KeyCell
++;
121 /* Loop the hash table */
122 Entry
= CmpCacheTable
[i
].Entry
;
125 /* Check if this matches */
126 ASSERT_VALID_HASH(Entry
);
127 if ((KeyHash
->ConvKey
== Entry
->ConvKey
) &&
128 (KeyHash
->KeyCell
== Entry
->KeyCell
) &&
129 (KeyHash
->KeyHive
== Entry
->KeyHive
))
132 return CONTAINING_RECORD(Entry
, CM_KEY_CONTROL_BLOCK
, KeyHash
);
136 Entry
= Entry
->NextHash
;
139 /* No entry found, add this one and return NULL since none existed */
140 KeyHash
->NextHash
= CmpCacheTable
[i
].Entry
;
141 CmpCacheTable
[i
].Entry
= KeyHash
;
145 PCM_NAME_CONTROL_BLOCK
147 CmpGetNameControlBlock(IN PUNICODE_STRING NodeName
)
149 PCM_NAME_CONTROL_BLOCK Ncb
= NULL
;
153 BOOLEAN IsCompressed
= TRUE
, Found
= FALSE
;
154 PCM_NAME_HASH HashEntry
;
155 ULONG Length
, NcbSize
;
158 p
= NodeName
->Buffer
;
159 for (i
= 0; i
< NodeName
->Length
; i
+= sizeof(WCHAR
))
161 /* Make sure it's not a slash */
162 if (*p
!= OBJ_NAME_PATH_SEPARATOR
)
164 /* Add it to the hash */
165 ConvKey
= 37 * ConvKey
+ RtlUpcaseUnicodeChar(*p
);
172 /* Set assumed lengh and loop to check */
173 Length
= NodeName
->Length
/ sizeof(WCHAR
);
174 for (i
= 0; i
< (NodeName
->Length
/ sizeof(WCHAR
)); i
++)
176 /* Check if this is a 16-bit character */
177 if (NodeName
->Buffer
[i
] > (UCHAR
)-1)
179 /* This is the actual size, and we know we're not compressed */
180 Length
= NodeName
->Length
;
181 IsCompressed
= FALSE
;
186 /* Lock the NCB entry */
187 CmpAcquireNcbLockExclusiveByKey(ConvKey
);
189 /* Get the hash entry */
190 HashEntry
= GET_HASH_ENTRY(CmpNameCacheTable
, ConvKey
).Entry
;
193 /* Get the current NCB */
194 Ncb
= CONTAINING_RECORD(HashEntry
, CM_NAME_CONTROL_BLOCK
, NameHash
);
196 /* Check if the hash matches */
197 if ((ConvKey
== HashEntry
->ConvKey
) && (Length
== Ncb
->NameLength
))
202 /* If the NCB is compressed, do a compressed name compare */
206 if (CmpCompareCompressedName(NodeName
, Ncb
->Name
, Length
))
214 /* Do a manual compare */
215 p
= NodeName
->Buffer
;
217 for (i
= 0; i
< Ncb
->NameLength
; i
+= sizeof(WCHAR
))
219 /* Compare the character */
220 if (RtlUpcaseUnicodeChar(*p
) != RtlUpcaseUnicodeChar(*pp
))
233 /* Check if we found a name */
237 ASSERT(Ncb
->RefCount
!= 0xFFFF);
243 /* Go to the next hash */
244 HashEntry
= HashEntry
->NextHash
;
247 /* Check if we didn't find it */
251 NcbSize
= FIELD_OFFSET(CM_NAME_CONTROL_BLOCK
, Name
) + Length
;
252 Ncb
= CmpAllocate(NcbSize
, TRUE
, TAG_CM
);
255 /* Release the lock and fail */
256 CmpReleaseNcbLockByKey(ConvKey
);
261 RtlZeroMemory(Ncb
, NcbSize
);
263 /* Check if the name was compressed */
266 /* Copy the compressed name */
267 for (i
= 0; i
< NodeName
->Length
/ sizeof(WCHAR
); i
++)
269 /* Copy Unicode to ANSI */
270 ((PCHAR
)Ncb
->Name
)[i
] = (CHAR
)RtlUpcaseUnicodeChar(NodeName
->Buffer
[i
]);
275 /* Copy the name directly */
276 for (i
= 0; i
< NodeName
->Length
/ sizeof(WCHAR
); i
++)
278 /* Copy each unicode character */
279 Ncb
->Name
[i
] = RtlUpcaseUnicodeChar(NodeName
->Buffer
[i
]);
283 /* Setup the rest of the NCB */
284 Ncb
->Compressed
= IsCompressed
;
285 Ncb
->ConvKey
= ConvKey
;
287 Ncb
->NameLength
= Length
;
289 /* Insert the name in the hash table */
290 HashEntry
= &Ncb
->NameHash
;
291 HashEntry
->NextHash
= GET_HASH_ENTRY(CmpNameCacheTable
, ConvKey
).Entry
;
292 GET_HASH_ENTRY(CmpNameCacheTable
, ConvKey
).Entry
= HashEntry
;
295 /* Release NCB lock */
296 CmpReleaseNcbLockByKey(ConvKey
);
298 /* Return the NCB found */
304 CmpRemoveKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb
)
306 /* Make sure that the registry and KCB are utterly locked */
307 ASSERT((CmpIsKcbLockedExclusive(Kcb
) == TRUE
) ||
308 (CmpTestRegistryLockExclusive() == TRUE
));
310 /* Remove the key hash */
311 CmpRemoveKeyHash(&Kcb
->KeyHash
);
316 CmpDereferenceNameControlBlockWithLock(IN PCM_NAME_CONTROL_BLOCK Ncb
)
318 PCM_NAME_HASH Current
, *Next
;
319 ULONG ConvKey
= Ncb
->ConvKey
;
322 CmpAcquireNcbLockExclusiveByKey(ConvKey
);
324 /* Decrease the reference count */
325 ASSERT(Ncb
->RefCount
>= 1);
326 if (!(--Ncb
->RefCount
))
328 /* Find the NCB in the table */
329 Next
= &GET_HASH_ENTRY(CmpNameCacheTable
, Ncb
->ConvKey
).Entry
;
332 /* Check the current entry */
334 ASSERT(Current
!= NULL
);
335 if (Current
== &Ncb
->NameHash
)
338 *Next
= Current
->NextHash
;
342 /* Get to the next one */
343 Next
= &Current
->NextHash
;
346 /* Found it, now free it */
350 /* Release the lock */
351 CmpReleaseNcbLockByKey(ConvKey
);
356 CmpReferenceKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb
)
358 CMTRACE(CM_REFERENCE_DEBUG
,
359 "%s - Referencing KCB: %p\n", __FUNCTION__
, Kcb
);
361 /* Check if this is the KCB's first reference */
362 if (Kcb
->RefCount
== 0)
364 /* Check if the KCB is locked in shared mode */
365 if (!CmpIsKcbLockedExclusive(Kcb
))
367 /* Convert it to exclusive */
368 if (!CmpTryToConvertKcbSharedToExclusive(Kcb
))
370 /* Set the delayed close index so that we can be ignored */
371 Kcb
->DelayedCloseIndex
= 1;
373 /* Increase the reference count while we release the lock */
374 InterlockedIncrement((PLONG
)&Kcb
->RefCount
);
376 /* Go from shared to exclusive */
377 CmpConvertKcbSharedToExclusive(Kcb
);
379 /* Decrement the reference count; the lock is now held again */
380 InterlockedDecrement((PLONG
)&Kcb
->RefCount
);
382 /* Check if we still control the index */
383 if (Kcb
->DelayedCloseIndex
== 1)
386 Kcb
->DelayedCloseIndex
= 0;
391 ASSERT((Kcb
->DelayedCloseIndex
== CmpDelayedCloseSize
) ||
392 (Kcb
->DelayedCloseIndex
== 0));
398 /* Increase the reference count */
399 if ((InterlockedIncrement((PLONG
)&Kcb
->RefCount
) & 0xFFFF) == 0)
401 /* We've overflown to 64K references, bail out */
402 InterlockedDecrement((PLONG
)&Kcb
->RefCount
);
406 /* Check if this was the last close index */
407 if (!Kcb
->DelayedCloseIndex
)
409 /* Check if the KCB is locked in shared mode */
410 if (!CmpIsKcbLockedExclusive(Kcb
))
412 /* Convert it to exclusive */
413 if (!CmpTryToConvertKcbSharedToExclusive(Kcb
))
415 /* Go from shared to exclusive */
416 CmpConvertKcbSharedToExclusive(Kcb
);
420 /* If we're still the last entry, remove us */
421 if (!Kcb
->DelayedCloseIndex
) CmpRemoveFromDelayedClose(Kcb
);
430 CmpCleanUpKcbValueCache(IN PCM_KEY_CONTROL_BLOCK Kcb
)
432 PULONG_PTR CachedList
;
436 ASSERT((CmpIsKcbLockedExclusive(Kcb
) == TRUE
) ||
437 (CmpTestRegistryLockExclusive() == TRUE
));
439 /* Check if the value list is cached */
440 if (CMP_IS_CELL_CACHED(Kcb
->ValueCache
.ValueList
))
442 /* Get the cache list */
443 CachedList
= (PULONG_PTR
)CMP_GET_CACHED_DATA(Kcb
->ValueCache
.ValueList
);
444 for (i
= 0; i
< Kcb
->ValueCache
.Count
; i
++)
446 /* Check if this cell is cached */
447 if (CMP_IS_CELL_CACHED(CachedList
[i
]))
450 CmpFree((PVOID
)CMP_GET_CACHED_CELL(CachedList
[i
]), 0);
454 /* Now free the list */
455 CmpFree((PVOID
)CMP_GET_CACHED_CELL(Kcb
->ValueCache
.ValueList
), 0);
456 Kcb
->ValueCache
.ValueList
= HCELL_NIL
;
458 else if (Kcb
->ExtFlags
& CM_KCB_SYM_LINK_FOUND
)
460 /* This is a sym link, check if there's only one reference left */
461 if ((Kcb
->ValueCache
.RealKcb
->RefCount
== 1) &&
462 !(Kcb
->ValueCache
.RealKcb
->Delete
))
464 /* Disable delay close for the KCB */
465 Kcb
->ValueCache
.RealKcb
->ExtFlags
|= CM_KCB_NO_DELAY_CLOSE
;
468 /* Dereference the KCB */
469 CmpDelayDerefKeyControlBlock(Kcb
->ValueCache
.RealKcb
);
470 Kcb
->ExtFlags
&= ~CM_KCB_SYM_LINK_FOUND
;
476 CmpCleanUpKcbCacheWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb
,
477 IN BOOLEAN LockHeldExclusively
)
479 PCM_KEY_CONTROL_BLOCK Parent
;
483 ASSERT((CmpIsKcbLockedExclusive(Kcb
) == TRUE
) ||
484 (CmpTestRegistryLockExclusive() == TRUE
));
485 ASSERT(Kcb
->RefCount
== 0);
487 /* Cleanup the value cache */
488 CmpCleanUpKcbValueCache(Kcb
);
490 /* Dereference the NCB */
491 CmpDereferenceNameControlBlockWithLock(Kcb
->NameBlock
);
493 /* Check if we have an index hint block and free it */
494 if (Kcb
->ExtFlags
& CM_KCB_SUBKEY_HINT
) CmpFree(Kcb
->IndexHint
, 0);
496 /* Check if we were already deleted */
497 Parent
= Kcb
->ParentKcb
;
498 if (!Kcb
->Delete
) CmpRemoveKeyControlBlock(Kcb
);
500 /* Set invalid KCB signature */
501 Kcb
->Signature
= CM_KCB_INVALID_SIGNATURE
;
503 /* Free the KCB as well */
504 CmpFreeKeyControlBlock(Kcb
);
506 /* Check if we have a parent */
509 /* Dereference the parent */
510 LockHeldExclusively
?
511 CmpDereferenceKeyControlBlockWithLock(Parent
,LockHeldExclusively
) :
512 CmpDelayDerefKeyControlBlock(Parent
);
518 CmpCleanUpSubKeyInfo(IN PCM_KEY_CONTROL_BLOCK Kcb
)
520 PCM_KEY_NODE KeyNode
;
523 ASSERT((CmpIsKcbLockedExclusive(Kcb
) == TRUE
) ||
524 (CmpTestRegistryLockExclusive() == TRUE
));
526 /* Check if there's any cached subkey */
527 if (Kcb
->ExtFlags
& (CM_KCB_NO_SUBKEY
| CM_KCB_SUBKEY_ONE
| CM_KCB_SUBKEY_HINT
))
529 /* Check if there's a hint */
530 if (Kcb
->ExtFlags
& (CM_KCB_SUBKEY_HINT
))
533 CmpFree(Kcb
->IndexHint
, 0);
536 /* Remove subkey flags */
537 Kcb
->ExtFlags
&= ~(CM_KCB_NO_SUBKEY
| CM_KCB_SUBKEY_ONE
| CM_KCB_SUBKEY_HINT
);
540 /* Check if there's no linked cell */
541 if (Kcb
->KeyCell
== HCELL_NIL
)
543 /* Make sure it's a delete */
549 /* Get the key node */
550 KeyNode
= (PCM_KEY_NODE
)HvGetCell(Kcb
->KeyHive
, Kcb
->KeyCell
);
553 /* Check if we got the node */
556 /* We didn't, mark the cached data invalid */
557 Kcb
->ExtFlags
|= CM_KCB_INVALID_CACHED_INFO
;
561 /* We have a keynode, update subkey counts */
562 Kcb
->ExtFlags
&= ~CM_KCB_INVALID_CACHED_INFO
;
563 Kcb
->SubKeyCount
= KeyNode
->SubKeyCounts
[Stable
] +
564 KeyNode
->SubKeyCounts
[Volatile
];
566 /* Release the cell */
567 HvReleaseCell(Kcb
->KeyHive
, Kcb
->KeyCell
);
573 CmpDereferenceKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb
)
575 LONG OldRefCount
, NewRefCount
;
577 CMTRACE(CM_REFERENCE_DEBUG
,
578 "%s - Dereferencing KCB: %p\n", __FUNCTION__
, Kcb
);
580 /* Get the ref count and update it */
581 OldRefCount
= *(PLONG
)&Kcb
->RefCount
;
582 NewRefCount
= OldRefCount
- 1;
584 /* Check if we still have references */
585 if ((NewRefCount
& 0xFFFF) > 0)
587 /* Do the dereference */
588 if (InterlockedCompareExchange((PLONG
)&Kcb
->RefCount
,
590 OldRefCount
) == OldRefCount
)
598 ConvKey
= Kcb
->ConvKey
;
600 /* Do the dereference inside the lock */
601 CmpAcquireKcbLockExclusive(Kcb
);
602 CmpDereferenceKeyControlBlockWithLock(Kcb
, FALSE
);
603 CmpReleaseKcbLockByKey(ConvKey
);
608 CmpDereferenceKeyControlBlockWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb
,
609 IN BOOLEAN LockHeldExclusively
)
611 CMTRACE(CM_REFERENCE_DEBUG
,
612 "%s - Dereferencing KCB: %p\n", __FUNCTION__
, Kcb
);
615 ASSERT_KCB_VALID(Kcb
);
617 /* Check if this is the last reference */
618 if ((InterlockedDecrement((PLONG
)&Kcb
->RefCount
) & 0xFFFF) == 0)
621 ASSERT((CmpIsKcbLockedExclusive(Kcb
) == TRUE
) ||
622 (CmpTestRegistryLockExclusive() == TRUE
));
624 /* Check if we should do a direct delete */
625 if (((CmpHoldLazyFlush
) &&
626 !(Kcb
->ExtFlags
& CM_KCB_SYM_LINK_FOUND
) &&
627 !(Kcb
->Flags
& KEY_SYM_LINK
)) ||
628 (Kcb
->ExtFlags
& CM_KCB_NO_DELAY_CLOSE
) ||
631 /* Clean up the KCB*/
632 CmpCleanUpKcbCacheWithLock(Kcb
, LockHeldExclusively
);
636 /* Otherwise, use delayed close */
637 CmpAddToDelayedClose(Kcb
, LockHeldExclusively
);
644 InitializeKCBKeyBodyList(IN PCM_KEY_CONTROL_BLOCK Kcb
)
646 /* Initialize the list */
647 InitializeListHead(&Kcb
->KeyBodyListHead
);
649 /* Clear the bodies */
650 Kcb
->KeyBodyArray
[0] =
651 Kcb
->KeyBodyArray
[1] =
652 Kcb
->KeyBodyArray
[2] =
653 Kcb
->KeyBodyArray
[3] = NULL
;
656 PCM_KEY_CONTROL_BLOCK
658 CmpCreateKeyControlBlock(IN PHHIVE Hive
,
659 IN HCELL_INDEX Index
,
660 IN PCM_KEY_NODE Node
,
661 IN PCM_KEY_CONTROL_BLOCK Parent
,
663 IN PUNICODE_STRING KeyName
)
665 PCM_KEY_CONTROL_BLOCK Kcb
, FoundKcb
= NULL
;
666 UNICODE_STRING NodeName
;
667 ULONG ConvKey
= 0, i
;
668 BOOLEAN IsFake
, HashLock
;
671 /* Make sure we own this hive in case it's being unloaded */
672 if ((Hive
->HiveFlags
& HIVE_IS_UNLOADING
) &&
673 (((PCMHIVE
)Hive
)->CreatorOwner
!= KeGetCurrentThread()))
679 /* Check if this is a fake KCB */
680 IsFake
= Flags
& CMP_CREATE_FAKE_KCB
? TRUE
: FALSE
;
682 /* If we have a parent, use its ConvKey */
683 if (Parent
) ConvKey
= Parent
->ConvKey
;
685 /* Make a copy of the name */
688 /* Remove leading slash */
689 while ((NodeName
.Length
) && (*NodeName
.Buffer
== OBJ_NAME_PATH_SEPARATOR
))
691 /* Move the buffer by one */
693 NodeName
.Length
-= sizeof(WCHAR
);
696 /* Make sure we didn't get just a slash or something */
697 ASSERT(NodeName
.Length
> 0);
699 /* Now setup the hash */
701 for (i
= 0; i
< NodeName
.Length
; i
+= sizeof(WCHAR
))
703 /* Make sure it's a valid character */
704 if (*p
!= OBJ_NAME_PATH_SEPARATOR
)
706 /* Add this key to the hash */
707 ConvKey
= 37 * ConvKey
+ RtlUpcaseUnicodeChar(*p
);
714 /* Allocate the KCB */
715 Kcb
= CmpAllocateKeyControlBlock();
716 if (!Kcb
) return NULL
;
718 /* Initailize the key list */
719 InitializeKCBKeyBodyList(Kcb
);
722 Kcb
->Signature
= CM_KCB_SIGNATURE
;
726 Kcb
->KeyCell
= Index
;
727 Kcb
->ConvKey
= ConvKey
;
728 Kcb
->DelayedCloseIndex
= CmpDelayedCloseSize
;
729 Kcb
->InDelayClose
= 0;
730 ASSERT_KCB_VALID(Kcb
);
732 /* Check if we have two hash entires */
733 HashLock
= Flags
& CMP_LOCK_HASHES_FOR_KCB
? TRUE
: FALSE
;
736 /* It's not locked, do we have a parent? */
739 /* Lock the parent KCB and ourselves */
740 CmpAcquireTwoKcbLocksExclusiveByKey(ConvKey
, Parent
->ConvKey
);
744 /* Lock only ourselves */
745 CmpAcquireKcbLockExclusive(Kcb
);
749 /* Check if we already have a KCB */
750 FoundKcb
= CmpInsertKeyHash(&Kcb
->KeyHash
, IsFake
);
754 ASSERT(!FoundKcb
->Delete
);
755 Kcb
->Signature
= CM_KCB_INVALID_SIGNATURE
;
757 /* Free the one we allocated and reference this one */
758 CmpFreeKeyControlBlock(Kcb
);
759 ASSERT_KCB_VALID(FoundKcb
);
761 if (!CmpReferenceKeyControlBlock(Kcb
))
763 /* We got too many handles */
764 ASSERT(Kcb
->RefCount
+ 1 != 0);
769 /* Check if we're not creating a fake one, but it used to be fake */
770 if ((Kcb
->ExtFlags
& CM_KCB_KEY_NON_EXIST
) && !(IsFake
))
772 /* Set the hive and cell */
774 Kcb
->KeyCell
= Index
;
776 /* This means that our current information is invalid */
777 Kcb
->ExtFlags
= CM_KCB_INVALID_CACHED_INFO
;
780 /* Check if we didn't have any valid data */
781 if (!(Kcb
->ExtFlags
& (CM_KCB_NO_SUBKEY
|
783 CM_KCB_SUBKEY_HINT
)))
785 /* Calculate the index hint */
786 Kcb
->SubKeyCount
= Node
->SubKeyCounts
[Stable
] +
787 Node
->SubKeyCounts
[Volatile
];
789 /* Cached information is now valid */
790 Kcb
->ExtFlags
&= ~CM_KCB_INVALID_CACHED_INFO
;
793 /* Setup the other data */
794 Kcb
->KcbLastWriteTime
= Node
->LastWriteTime
;
795 Kcb
->KcbMaxNameLen
= (USHORT
)Node
->MaxNameLen
;
796 Kcb
->KcbMaxValueNameLen
= (USHORT
)Node
->MaxValueNameLen
;
797 Kcb
->KcbMaxValueDataLen
= Node
->MaxValueDataLen
;
802 /* No KCB, do we have a parent? */
805 /* Reference the parent */
806 if (((Parent
->TotalLevels
+ 1) < 512) &&
807 (CmpReferenceKeyControlBlock(Parent
)))
810 Kcb
->ParentKcb
= Parent
;
811 Kcb
->TotalLevels
= Parent
->TotalLevels
+ 1;
815 /* Remove the KCB and free it */
816 CmpRemoveKeyControlBlock(Kcb
);
817 Kcb
->Signature
= CM_KCB_INVALID_SIGNATURE
;
818 CmpFreeKeyControlBlock(Kcb
);
824 /* No parent, this is the root node */
825 Kcb
->ParentKcb
= NULL
;
826 Kcb
->TotalLevels
= 1;
829 /* Check if we have a KCB */
833 Kcb
->NameBlock
= CmpGetNameControlBlock(&NodeName
);
837 Kcb
->ValueCache
.Count
= Node
->ValueList
.Count
;
838 Kcb
->ValueCache
.ValueList
= Node
->ValueList
.List
;
839 Kcb
->Flags
= Node
->Flags
;
841 Kcb
->DelayedCloseIndex
= CmpDelayedCloseSize
;
843 /* Remember if this is a fake key */
844 if (IsFake
) Kcb
->ExtFlags
|= CM_KCB_KEY_NON_EXIST
;
846 /* Setup the other data */
847 Kcb
->SubKeyCount
= Node
->SubKeyCounts
[Stable
] +
848 Node
->SubKeyCounts
[Volatile
];
849 Kcb
->KcbLastWriteTime
= Node
->LastWriteTime
;
850 Kcb
->KcbMaxNameLen
= (USHORT
)Node
->MaxNameLen
;
851 Kcb
->KcbMaxValueNameLen
= (USHORT
)Node
->MaxValueNameLen
;
852 Kcb
->KcbMaxValueDataLen
= (USHORT
)Node
->MaxValueDataLen
;
856 /* Dereference the KCB */
857 CmpDereferenceKeyControlBlockWithLock(Parent
, FALSE
);
859 /* Remove the KCB and free it */
860 CmpRemoveKeyControlBlock(Kcb
);
861 Kcb
->Signature
= CM_KCB_INVALID_SIGNATURE
;
862 CmpFreeKeyControlBlock(Kcb
);
868 /* Check if this is a KCB inside a frozen hive */
869 if ((Kcb
) && (((PCMHIVE
)Hive
)->Frozen
) && (!(Kcb
->Flags
& KEY_SYM_LINK
)))
871 /* Don't add these to the delay close */
872 Kcb
->ExtFlags
|= CM_KCB_NO_DELAY_CLOSE
;
876 ASSERT((!Kcb
) || (Kcb
->Delete
== FALSE
));
878 /* Check if we had locked the hashes */
881 /* We locked them manually, do we have a parent? */
884 /* Unlock the parent KCB and ourselves */
885 CmpReleaseTwoKcbLockByKey(ConvKey
, Parent
->ConvKey
);
889 /* Unlock only ourselves */
890 CmpReleaseKcbLockByKey(ConvKey
);
900 CmpConstructName(IN PCM_KEY_CONTROL_BLOCK Kcb
)
902 PUNICODE_STRING KeyName
;
904 PCM_KEY_CONTROL_BLOCK MyKcb
;
905 PCM_KEY_NODE KeyNode
;
906 BOOLEAN DeletedKey
= FALSE
;
907 PWCHAR TargetBuffer
, CurrentNameW
;
910 /* Calculate how much size our key name is going to occupy */
916 /* Add length of the name */
917 if (!MyKcb
->NameBlock
->Compressed
)
919 NameLength
+= MyKcb
->NameBlock
->NameLength
;
923 NameLength
+= CmpCompressedNameSize(MyKcb
->NameBlock
->Name
,
924 MyKcb
->NameBlock
->NameLength
);
927 /* Sum up the separator too */
928 NameLength
+= sizeof(WCHAR
);
930 /* Go to the parent KCB */
931 MyKcb
= MyKcb
->ParentKcb
;
934 /* Allocate the unicode string now */
935 KeyName
= CmpAllocate(NameLength
+ sizeof(UNICODE_STRING
),
939 if (!KeyName
) return NULL
;
942 KeyName
->Buffer
= (PWSTR
)(KeyName
+ 1);
943 KeyName
->Length
= NameLength
;
944 KeyName
->MaximumLength
= NameLength
;
946 /* Loop the keys again, now adding names */
952 /* Sanity checks for deleted and fake keys */
953 if ((!MyKcb
->KeyCell
&& !MyKcb
->Delete
) ||
955 MyKcb
->ExtFlags
& CM_KCB_KEY_NON_EXIST
)
962 /* Try to get the name from the keynode,
963 if the key is not deleted */
964 if (!DeletedKey
&& !MyKcb
->Delete
)
966 KeyNode
= HvGetCell(MyKcb
->KeyHive
, MyKcb
->KeyCell
);
977 /* The key was deleted */
982 /* Get the pointer to the beginning of the current key name */
983 NameLength
+= (MyKcb
->NameBlock
->NameLength
+ 1) * sizeof(WCHAR
);
984 TargetBuffer
= &KeyName
->Buffer
[(KeyName
->Length
- NameLength
) / sizeof(WCHAR
)];
986 /* Add a separator */
987 TargetBuffer
[0] = OBJ_NAME_PATH_SEPARATOR
;
989 /* Add the name, but remember to go from the end to the beginning */
990 if (!MyKcb
->NameBlock
->Compressed
)
992 /* Get the pointer to the name (from the keynode, if possible) */
993 if ((MyKcb
->Flags
& (KEY_HIVE_ENTRY
| KEY_HIVE_EXIT
)) ||
996 CurrentNameW
= MyKcb
->NameBlock
->Name
;
1000 CurrentNameW
= KeyNode
->Name
;
1004 for (i
=0; i
< MyKcb
->NameBlock
->NameLength
; i
++)
1006 TargetBuffer
[i
+1] = *CurrentNameW
;
1012 /* Get the pointer to the name (from the keynode, if possible) */
1013 if ((MyKcb
->Flags
& (KEY_HIVE_ENTRY
| KEY_HIVE_EXIT
)) ||
1016 CurrentName
= (PUCHAR
)MyKcb
->NameBlock
->Name
;
1020 CurrentName
= (PUCHAR
)KeyNode
->Name
;
1024 for (i
=0; i
< MyKcb
->NameBlock
->NameLength
; i
++)
1026 TargetBuffer
[i
+1] = (WCHAR
)*CurrentName
;
1031 /* Release the cell, if needed */
1032 if (KeyNode
) HvReleaseCell(MyKcb
->KeyHive
, MyKcb
->KeyCell
);
1034 /* Go to the parent KCB */
1035 MyKcb
= MyKcb
->ParentKcb
;
1038 /* Return resulting buffer (both UNICODE_STRING and
1039 its buffer following it) */
1045 EnlistKeyBodyWithKCB(IN PCM_KEY_BODY KeyBody
,
1051 ASSERT(KeyBody
->KeyControlBlock
!= NULL
);
1053 /* Initialize the list entry */
1054 InitializeListHead(&KeyBody
->KeyBodyList
);
1056 /* Check if we can use the parent KCB array */
1057 for (i
= 0; i
< 4; i
++)
1059 /* Add it into the list */
1060 if (!InterlockedCompareExchangePointer((PVOID
*)&KeyBody
->KeyControlBlock
->
1070 /* Array full, check if we need to unlock the KCB */
1071 if (Flags
& CMP_ENLIST_KCB_LOCKED_SHARED
)
1073 /* It's shared, so release the KCB shared lock */
1074 CmpReleaseKcbLock(KeyBody
->KeyControlBlock
);
1075 ASSERT(!(Flags
& CMP_ENLIST_KCB_LOCKED_EXCLUSIVE
));
1078 /* Check if we need to lock the KCB */
1079 if (!(Flags
& CMP_ENLIST_KCB_LOCKED_EXCLUSIVE
))
1081 /* Acquire the lock */
1082 CmpAcquireKcbLockExclusive(KeyBody
->KeyControlBlock
);
1085 /* Make sure we have the exclusive lock */
1086 ASSERT((CmpIsKcbLockedExclusive(KeyBody
->KeyControlBlock
) == TRUE
) ||
1087 (CmpTestRegistryLockExclusive() == TRUE
));
1090 InsertTailList(&KeyBody
->KeyControlBlock
->KeyBodyListHead
,
1091 &KeyBody
->KeyBodyList
);
1093 /* Check if we did a manual lock */
1094 if (!(Flags
& (CMP_ENLIST_KCB_LOCKED_SHARED
|
1095 CMP_ENLIST_KCB_LOCKED_EXCLUSIVE
)))
1097 /* Release the lock */
1098 CmpReleaseKcbLock(KeyBody
->KeyControlBlock
);
1104 DelistKeyBodyFromKCB(IN PCM_KEY_BODY KeyBody
,
1105 IN BOOLEAN LockHeld
)
1110 ASSERT(KeyBody
->KeyControlBlock
!= NULL
);
1112 /* Check if we can use the parent KCB array */
1113 for (i
= 0; i
< 4; i
++)
1115 /* Add it into the list */
1116 if (InterlockedCompareExchangePointer((VOID
*)&KeyBody
->KeyControlBlock
->
1119 KeyBody
) == KeyBody
)
1127 ASSERT(IsListEmpty(&KeyBody
->KeyControlBlock
->KeyBodyListHead
) == FALSE
);
1128 ASSERT(IsListEmpty(&KeyBody
->KeyBodyList
) == FALSE
);
1131 if (!LockHeld
) CmpAcquireKcbLockExclusive(KeyBody
->KeyControlBlock
);
1132 ASSERT((CmpIsKcbLockedExclusive(KeyBody
->KeyControlBlock
) == TRUE
) ||
1133 (CmpTestRegistryLockExclusive() == TRUE
));
1135 /* Remove the entry */
1136 RemoveEntryList(&KeyBody
->KeyBodyList
);
1138 /* Unlock it it if we did a manual lock */
1139 if (!LockHeld
) CmpReleaseKcbLock(KeyBody
->KeyControlBlock
);
1144 CmpFlushNotifiesOnKeyBodyList(IN PCM_KEY_CONTROL_BLOCK Kcb
,
1145 IN BOOLEAN LockHeld
)
1147 PLIST_ENTRY NextEntry
, ListHead
;
1148 PCM_KEY_BODY KeyBody
;
1151 LockHeld
? CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK() : CmpIsKcbLockedExclusive(Kcb
);
1154 /* Is the list empty? */
1155 ListHead
= &Kcb
->KeyBodyListHead
;
1156 if (!IsListEmpty(ListHead
))
1159 NextEntry
= ListHead
->Flink
;
1160 while (NextEntry
!= ListHead
)
1162 /* Get the key body */
1163 KeyBody
= CONTAINING_RECORD(NextEntry
, CM_KEY_BODY
, KeyBodyList
);
1164 ASSERT(KeyBody
->Type
== '20yk');
1166 /* Check for notifications */
1167 if (KeyBody
->NotifyBlock
)
1169 /* Is the lock held? */
1173 CmpFlushNotify(KeyBody
, LockHeld
);
1174 ASSERT(KeyBody
->NotifyBlock
== NULL
);
1178 /* Lock isn't held, so we need to take a reference */
1179 if (ObReferenceObjectSafe(KeyBody
))
1181 /* Now we can flush */
1182 CmpFlushNotify(KeyBody
, LockHeld
);
1183 ASSERT(KeyBody
->NotifyBlock
== NULL
);
1185 /* Release the reference we took */
1186 ObDereferenceObjectDeferDelete(KeyBody
);
1191 /* Try the next entry */
1192 NextEntry
= NextEntry
->Flink
;
1196 /* List has been parsed, exit */