xref: /reactos/ntoskrnl/config/cmkcbncb.c (revision 8a978a17)
1 /*
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)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 /* GLOBALS *******************************************************************/
16 
17 ULONG CmpHashTableSize = 2048;
18 PCM_KEY_HASH_TABLE_ENTRY CmpCacheTable;
19 PCM_NAME_HASH_TABLE_ENTRY CmpNameCacheTable;
20 
21 /* FUNCTIONS *****************************************************************/
22 
23 CODE_SEG("INIT")
24 VOID
25 NTAPI
26 CmpInitializeCache(VOID)
27 {
28     ULONG Length, i;
29 
30     /* Calculate length for the table */
31     Length = CmpHashTableSize * sizeof(CM_KEY_HASH_TABLE_ENTRY);
32 
33     /* Allocate it */
34     CmpCacheTable = CmpAllocate(Length, TRUE, TAG_CM);
35     if (!CmpCacheTable)
36     {
37         /* Take the system down */
38         KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 3, 1, 0, 0);
39     }
40 
41     /* Zero out the table */
42     RtlZeroMemory(CmpCacheTable, Length);
43 
44     /* Initialize the locks */
45     for (i = 0;i < CmpHashTableSize; i++)
46     {
47         /* Setup the pushlock */
48         ExInitializePushLock(&CmpCacheTable[i].Lock);
49     }
50 
51     /* Calculate length for the name cache */
52     Length = CmpHashTableSize * sizeof(CM_NAME_HASH_TABLE_ENTRY);
53 
54     /* Now allocate the name cache table */
55     CmpNameCacheTable = CmpAllocate(Length, TRUE, TAG_CM);
56     if (!CmpNameCacheTable)
57     {
58         /* Take the system down */
59         KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 3, 3, 0, 0);
60     }
61 
62     /* Zero out the table */
63     RtlZeroMemory(CmpNameCacheTable, Length);
64 
65     /* Initialize the locks */
66     for (i = 0;i < CmpHashTableSize; i++)
67     {
68         /* Setup the pushlock */
69         ExInitializePushLock(&CmpNameCacheTable[i].Lock);
70     }
71 
72     /* Setup the delayed close table */
73     CmpInitializeDelayedCloseTable();
74 }
75 
76 VOID
77 NTAPI
78 CmpRemoveKeyHash(IN PCM_KEY_HASH KeyHash)
79 {
80     PCM_KEY_HASH *Prev;
81     PCM_KEY_HASH Current;
82     ASSERT_VALID_HASH(KeyHash);
83 
84     /* Lookup all the keys in this index entry */
85     Prev = &GET_HASH_ENTRY(CmpCacheTable, KeyHash->ConvKey)->Entry;
86     while (TRUE)
87     {
88         /* Save the current one and make sure it's valid */
89         Current = *Prev;
90         ASSERT(Current != NULL);
91         ASSERT_VALID_HASH(Current);
92 
93         /* Check if it matches */
94         if (Current == KeyHash)
95         {
96             /* Then write the previous one */
97             *Prev = Current->NextHash;
98             if (*Prev) ASSERT_VALID_HASH(*Prev);
99             break;
100         }
101 
102         /* Otherwise, keep going */
103         Prev = &Current->NextHash;
104     }
105 }
106 
107 PCM_KEY_CONTROL_BLOCK
108 NTAPI
109 CmpInsertKeyHash(IN PCM_KEY_HASH KeyHash,
110                  IN BOOLEAN IsFake)
111 {
112     ULONG i;
113     PCM_KEY_HASH Entry;
114     ASSERT_VALID_HASH(KeyHash);
115 
116     /* Get the hash index */
117     i = GET_HASH_INDEX(KeyHash->ConvKey);
118 
119     /* If this is a fake key, increase the key cell to use the parent data */
120     if (IsFake) KeyHash->KeyCell++;
121 
122     /* Loop the hash table */
123     Entry = CmpCacheTable[i].Entry;
124     while (Entry)
125     {
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))
131         {
132             /* Return it */
133             return CONTAINING_RECORD(Entry, CM_KEY_CONTROL_BLOCK, KeyHash);
134         }
135 
136         /* Keep looping */
137         Entry = Entry->NextHash;
138     }
139 
140     /* No entry found, add this one and return NULL since none existed */
141     KeyHash->NextHash = CmpCacheTable[i].Entry;
142     CmpCacheTable[i].Entry = KeyHash;
143     return NULL;
144 }
145 
146 PCM_NAME_CONTROL_BLOCK
147 NTAPI
148 CmpGetNameControlBlock(IN PUNICODE_STRING NodeName)
149 {
150     PCM_NAME_CONTROL_BLOCK Ncb = NULL;
151     ULONG ConvKey = 0;
152     PWCHAR p, pp;
153     ULONG i;
154     BOOLEAN IsCompressed = TRUE, Found = FALSE;
155     PCM_NAME_HASH HashEntry;
156     ULONG NcbSize;
157     USHORT Length;
158 
159     /* Loop the name */
160     p = NodeName->Buffer;
161     for (i = 0; i < NodeName->Length; i += sizeof(WCHAR))
162     {
163         /* Make sure it's not a slash */
164         if (*p != OBJ_NAME_PATH_SEPARATOR)
165         {
166             /* Add it to the hash */
167             ConvKey = 37 * ConvKey + RtlUpcaseUnicodeChar(*p);
168         }
169 
170         /* Next character */
171         p++;
172     }
173 
174     /* Set assumed lengh and loop to check */
175     Length = NodeName->Length / sizeof(WCHAR);
176     for (i = 0; i < (NodeName->Length / sizeof(WCHAR)); i++)
177     {
178         /* Check if this is a 16-bit character */
179         if (NodeName->Buffer[i] > (UCHAR)-1)
180         {
181             /* This is the actual size, and we know we're not compressed */
182             Length = NodeName->Length;
183             IsCompressed = FALSE;
184             break;
185         }
186     }
187 
188     /* Lock the NCB entry */
189     CmpAcquireNcbLockExclusiveByKey(ConvKey);
190 
191     /* Get the hash entry */
192     HashEntry = GET_HASH_ENTRY(CmpNameCacheTable, ConvKey)->Entry;
193     while (HashEntry)
194     {
195         /* Get the current NCB */
196         Ncb = CONTAINING_RECORD(HashEntry, CM_NAME_CONTROL_BLOCK, NameHash);
197 
198         /* Check if the hash matches */
199         if ((ConvKey == HashEntry->ConvKey) && (Length == Ncb->NameLength))
200         {
201             /* Assume success */
202             Found = TRUE;
203 
204             /* If the NCB is compressed, do a compressed name compare */
205             if (Ncb->Compressed)
206             {
207                 /* Compare names */
208                 if (CmpCompareCompressedName(NodeName, Ncb->Name, Length))
209                 {
210                     /* We failed */
211                     Found = FALSE;
212                 }
213             }
214             else
215             {
216                 /* Do a manual compare */
217                 p = NodeName->Buffer;
218                 pp = Ncb->Name;
219                 for (i = 0; i < Ncb->NameLength; i += sizeof(WCHAR))
220                 {
221                     /* Compare the character */
222                     if (RtlUpcaseUnicodeChar(*p) != RtlUpcaseUnicodeChar(*pp))
223                     {
224                         /* Failed */
225                         Found = FALSE;
226                         break;
227                     }
228 
229                     /* Next chars */
230                     p++;
231                     pp++;
232                 }
233             }
234 
235             /* Check if we found a name */
236             if (Found)
237             {
238                 /* Reference it */
239                 ASSERT(Ncb->RefCount != 0xFFFF);
240                 Ncb->RefCount++;
241                 break;
242             }
243         }
244 
245         /* Go to the next hash */
246         HashEntry = HashEntry->NextHash;
247     }
248 
249     /* Check if we didn't find it */
250     if (!Found)
251     {
252         /* Allocate one */
253         NcbSize = FIELD_OFFSET(CM_NAME_CONTROL_BLOCK, Name) + Length;
254         Ncb = CmpAllocate(NcbSize, TRUE, TAG_CM);
255         if (!Ncb)
256         {
257             /* Release the lock and fail */
258             CmpReleaseNcbLockByKey(ConvKey);
259             return NULL;
260         }
261 
262         /* Clear it out */
263         RtlZeroMemory(Ncb, NcbSize);
264 
265         /* Check if the name was compressed */
266         if (IsCompressed)
267         {
268             /* Copy the compressed name */
269             for (i = 0; i < NodeName->Length / sizeof(WCHAR); i++)
270             {
271                 /* Copy Unicode to ANSI */
272                 ((PCHAR)Ncb->Name)[i] = (CHAR)RtlUpcaseUnicodeChar(NodeName->Buffer[i]);
273             }
274         }
275         else
276         {
277             /* Copy the name directly */
278             for (i = 0; i < NodeName->Length / sizeof(WCHAR); i++)
279             {
280                 /* Copy each unicode character */
281                 Ncb->Name[i] = RtlUpcaseUnicodeChar(NodeName->Buffer[i]);
282             }
283         }
284 
285         /* Setup the rest of the NCB */
286         Ncb->Compressed = IsCompressed;
287         Ncb->ConvKey = ConvKey;
288         Ncb->RefCount++;
289         Ncb->NameLength = Length;
290 
291         /* Insert the name in the hash table */
292         HashEntry = &Ncb->NameHash;
293         HashEntry->NextHash = GET_HASH_ENTRY(CmpNameCacheTable, ConvKey)->Entry;
294         GET_HASH_ENTRY(CmpNameCacheTable, ConvKey)->Entry = HashEntry;
295     }
296 
297     /* Release NCB lock */
298     CmpReleaseNcbLockByKey(ConvKey);
299 
300     /* Return the NCB found */
301     return Ncb;
302 }
303 
304 VOID
305 NTAPI
306 CmpRemoveKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)
307 {
308     /* Make sure we have the exclusive lock */
309     CMP_ASSERT_KCB_LOCK(Kcb);
310 
311     /* Remove the key hash */
312     CmpRemoveKeyHash(&Kcb->KeyHash);
313 }
314 
315 VOID
316 NTAPI
317 CmpDereferenceNameControlBlockWithLock(IN PCM_NAME_CONTROL_BLOCK Ncb)
318 {
319     PCM_NAME_HASH Current, *Next;
320     ULONG ConvKey = Ncb->ConvKey;
321 
322     /* Lock the NCB */
323     CmpAcquireNcbLockExclusiveByKey(ConvKey);
324 
325     /* Decrease the reference count */
326     ASSERT(Ncb->RefCount >= 1);
327     if (!(--Ncb->RefCount))
328     {
329         /* Find the NCB in the table */
330         Next = &GET_HASH_ENTRY(CmpNameCacheTable, Ncb->ConvKey)->Entry;
331         while (TRUE)
332         {
333             /* Check the current entry */
334             Current = *Next;
335             ASSERT(Current != NULL);
336             if (Current == &Ncb->NameHash)
337             {
338                 /* Unlink it */
339                 *Next = Current->NextHash;
340                 break;
341             }
342 
343             /* Get to the next one */
344             Next = &Current->NextHash;
345         }
346 
347         /* Found it, now free it */
348         CmpFree(Ncb, 0);
349     }
350 
351     /* Release the lock */
352     CmpReleaseNcbLockByKey(ConvKey);
353 }
354 
355 BOOLEAN
356 NTAPI
357 CmpReferenceKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)
358 {
359     CMTRACE(CM_REFERENCE_DEBUG,
360             "%s - Referencing KCB: %p\n", __FUNCTION__, Kcb);
361 
362     /* Check if this is the KCB's first reference */
363     if (Kcb->RefCount == 0)
364     {
365         /* Check if the KCB is locked in shared mode */
366         if (!CmpIsKcbLockedExclusive(Kcb))
367         {
368             /* Convert it to exclusive */
369             if (!CmpTryToConvertKcbSharedToExclusive(Kcb))
370             {
371                 /* Set the delayed close index so that we can be ignored */
372                 Kcb->DelayedCloseIndex = 1;
373 
374                 /* Increase the reference count while we release the lock */
375                 InterlockedIncrement((PLONG)&Kcb->RefCount);
376 
377                 /* Go from shared to exclusive */
378                 CmpConvertKcbSharedToExclusive(Kcb);
379 
380                 /* Decrement the reference count; the lock is now held again */
381                 InterlockedDecrement((PLONG)&Kcb->RefCount);
382 
383                 /* Check if we still control the index */
384                 if (Kcb->DelayedCloseIndex == 1)
385                 {
386                     /* Reset it */
387                     Kcb->DelayedCloseIndex = 0;
388                 }
389                 else
390                 {
391                     /* Sanity check */
392                     ASSERT((Kcb->DelayedCloseIndex == CmpDelayedCloseSize) ||
393                            (Kcb->DelayedCloseIndex == 0));
394                 }
395             }
396         }
397     }
398 
399     /* Increase the reference count */
400     if ((InterlockedIncrement((PLONG)&Kcb->RefCount) & 0xFFFF) == 0)
401     {
402         /* We've overflown to 64K references, bail out */
403         InterlockedDecrement((PLONG)&Kcb->RefCount);
404         return FALSE;
405     }
406 
407     /* Check if this was the last close index */
408     if (!Kcb->DelayedCloseIndex)
409     {
410         /* Check if the KCB is locked in shared mode */
411         if (!CmpIsKcbLockedExclusive(Kcb))
412         {
413             /* Convert it to exclusive */
414             if (!CmpTryToConvertKcbSharedToExclusive(Kcb))
415             {
416                 /* Go from shared to exclusive */
417                 CmpConvertKcbSharedToExclusive(Kcb);
418             }
419         }
420 
421         /* If we're still the last entry, remove us */
422         if (!Kcb->DelayedCloseIndex) CmpRemoveFromDelayedClose(Kcb);
423     }
424 
425     /* Return success */
426     return TRUE;
427 }
428 
429 VOID
430 NTAPI
431 CmpCleanUpKcbValueCache(IN PCM_KEY_CONTROL_BLOCK Kcb)
432 {
433     PULONG_PTR CachedList;
434     ULONG i;
435 
436     /* Make sure we have the exclusive lock */
437     CMP_ASSERT_KCB_LOCK(Kcb);
438 
439     /* Check if the value list is cached */
440     if (CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList))
441     {
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++)
445         {
446             /* Check if this cell is cached */
447             if (CMP_IS_CELL_CACHED(CachedList[i]))
448             {
449                 /* Free it */
450                 CmpFree((PVOID)CMP_GET_CACHED_CELL(CachedList[i]), 0);
451             }
452         }
453 
454         /* Now free the list */
455         CmpFree((PVOID)CMP_GET_CACHED_CELL(Kcb->ValueCache.ValueList), 0);
456         Kcb->ValueCache.ValueList = HCELL_NIL;
457     }
458     else if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)
459     {
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))
463         {
464             /* Disable delay close for the KCB */
465             Kcb->ValueCache.RealKcb->ExtFlags |= CM_KCB_NO_DELAY_CLOSE;
466         }
467 
468         /* Dereference the KCB */
469         CmpDelayDerefKeyControlBlock(Kcb->ValueCache.RealKcb);
470         Kcb->ExtFlags &= ~CM_KCB_SYM_LINK_FOUND;
471     }
472 }
473 
474 VOID
475 NTAPI
476 CmpCleanUpKcbCacheWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb,
477                            IN BOOLEAN LockHeldExclusively)
478 {
479     PCM_KEY_CONTROL_BLOCK Parent;
480     PAGED_CODE();
481 
482     /* Sanity checks */
483     CMP_ASSERT_KCB_LOCK(Kcb);
484     ASSERT(Kcb->RefCount == 0);
485 
486     /* Cleanup the value cache */
487     CmpCleanUpKcbValueCache(Kcb);
488 
489     /* Dereference the NCB */
490     CmpDereferenceNameControlBlockWithLock(Kcb->NameBlock);
491 
492     /* Check if we have an index hint block and free it */
493     if (Kcb->ExtFlags & CM_KCB_SUBKEY_HINT) CmpFree(Kcb->IndexHint, 0);
494 
495     /* Check if we were already deleted */
496     Parent = Kcb->ParentKcb;
497     if (!Kcb->Delete) CmpRemoveKeyControlBlock(Kcb);
498 
499     /* Set invalid KCB signature */
500     Kcb->Signature = CM_KCB_INVALID_SIGNATURE;
501 
502     /* Free the KCB as well */
503     CmpFreeKeyControlBlock(Kcb);
504 
505     /* Check if we have a parent */
506     if (Parent)
507     {
508         /* Dereference the parent */
509         LockHeldExclusively ?
510             CmpDereferenceKeyControlBlockWithLock(Parent,LockHeldExclusively) :
511             CmpDelayDerefKeyControlBlock(Parent);
512     }
513 }
514 
515 VOID
516 NTAPI
517 CmpCleanUpSubKeyInfo(IN PCM_KEY_CONTROL_BLOCK Kcb)
518 {
519     PCM_KEY_NODE KeyNode;
520 
521     /* Make sure we have the exclusive lock */
522     CMP_ASSERT_KCB_LOCK(Kcb);
523 
524     /* Check if there's any cached subkey */
525     if (Kcb->ExtFlags & (CM_KCB_NO_SUBKEY | CM_KCB_SUBKEY_ONE | CM_KCB_SUBKEY_HINT))
526     {
527         /* Check if there's a hint */
528         if (Kcb->ExtFlags & (CM_KCB_SUBKEY_HINT))
529         {
530             /* Kill it */
531             CmpFree(Kcb->IndexHint, 0);
532         }
533 
534         /* Remove subkey flags */
535         Kcb->ExtFlags &= ~(CM_KCB_NO_SUBKEY | CM_KCB_SUBKEY_ONE | CM_KCB_SUBKEY_HINT);
536     }
537 
538     /* Check if there's no linked cell */
539     if (Kcb->KeyCell == HCELL_NIL)
540     {
541         /* Make sure it's a delete */
542         ASSERT(Kcb->Delete);
543         KeyNode = NULL;
544     }
545     else
546     {
547         /* Get the key node */
548         KeyNode = (PCM_KEY_NODE)HvGetCell(Kcb->KeyHive, Kcb->KeyCell);
549     }
550 
551     /* Check if we got the node */
552     if (!KeyNode)
553     {
554         /* We didn't, mark the cached data invalid */
555         Kcb->ExtFlags |= CM_KCB_INVALID_CACHED_INFO;
556     }
557     else
558     {
559         /* We have a keynode, update subkey counts */
560         Kcb->ExtFlags &= ~CM_KCB_INVALID_CACHED_INFO;
561         Kcb->SubKeyCount = KeyNode->SubKeyCounts[Stable] +
562                            KeyNode->SubKeyCounts[Volatile];
563 
564         /* Release the cell */
565         HvReleaseCell(Kcb->KeyHive, Kcb->KeyCell);
566     }
567 }
568 
569 VOID
570 NTAPI
571 CmpDereferenceKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)
572 {
573     LONG OldRefCount, NewRefCount;
574     ULONG ConvKey;
575     CMTRACE(CM_REFERENCE_DEBUG,
576             "%s - Dereferencing KCB: %p\n", __FUNCTION__, Kcb);
577 
578     /* Get the ref count and update it */
579     OldRefCount = *(PLONG)&Kcb->RefCount;
580     NewRefCount = OldRefCount - 1;
581 
582     /* Check if we still have references */
583     if ((NewRefCount & 0xFFFF) > 0)
584     {
585         /* Do the dereference */
586         if (InterlockedCompareExchange((PLONG)&Kcb->RefCount,
587                                        NewRefCount,
588                                        OldRefCount) == OldRefCount)
589         {
590             /* We'de done */
591             return;
592         }
593     }
594 
595     /* Save the key */
596     ConvKey = Kcb->ConvKey;
597 
598     /* Do the dereference inside the lock */
599     CmpAcquireKcbLockExclusive(Kcb);
600     CmpDereferenceKeyControlBlockWithLock(Kcb, FALSE);
601     CmpReleaseKcbLockByKey(ConvKey);
602 }
603 
604 VOID
605 NTAPI
606 CmpDereferenceKeyControlBlockWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb,
607                                       IN BOOLEAN LockHeldExclusively)
608 {
609     CMTRACE(CM_REFERENCE_DEBUG,
610             "%s - Dereferencing KCB: %p\n", __FUNCTION__, Kcb);
611 
612     /* Sanity check */
613     ASSERT_KCB_VALID(Kcb);
614 
615     /* Check if this is the last reference */
616     if ((InterlockedDecrement((PLONG)&Kcb->RefCount) & 0xFFFF) == 0)
617     {
618         /* Make sure we have the exclusive lock */
619         CMP_ASSERT_KCB_LOCK(Kcb);
620 
621         /* Check if we should do a direct delete */
622         if (((CmpHoldLazyFlush) &&
623              !(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND) &&
624              !(Kcb->Flags & KEY_SYM_LINK)) ||
625             (Kcb->ExtFlags & CM_KCB_NO_DELAY_CLOSE) ||
626             (Kcb->Delete))
627         {
628             /* Clean up the KCB*/
629             CmpCleanUpKcbCacheWithLock(Kcb, LockHeldExclusively);
630         }
631         else
632         {
633             /* Otherwise, use delayed close */
634             CmpAddToDelayedClose(Kcb, LockHeldExclusively);
635         }
636     }
637 }
638 
639 VOID
640 NTAPI
641 InitializeKCBKeyBodyList(IN PCM_KEY_CONTROL_BLOCK Kcb)
642 {
643     /* Initialize the list */
644     InitializeListHead(&Kcb->KeyBodyListHead);
645 
646     /* Clear the bodies */
647     Kcb->KeyBodyArray[0] =
648     Kcb->KeyBodyArray[1] =
649     Kcb->KeyBodyArray[2] =
650     Kcb->KeyBodyArray[3] = NULL;
651 }
652 
653 PCM_KEY_CONTROL_BLOCK
654 NTAPI
655 CmpCreateKeyControlBlock(IN PHHIVE Hive,
656                          IN HCELL_INDEX Index,
657                          IN PCM_KEY_NODE Node,
658                          IN PCM_KEY_CONTROL_BLOCK Parent,
659                          IN ULONG Flags,
660                          IN PUNICODE_STRING KeyName)
661 {
662     PCM_KEY_CONTROL_BLOCK Kcb, FoundKcb = NULL;
663     UNICODE_STRING NodeName;
664     ULONG ConvKey = 0, i;
665     BOOLEAN IsFake, HashLock;
666     PWCHAR p;
667 
668     /* Make sure we own this hive in case it's being unloaded */
669     if ((Hive->HiveFlags & HIVE_IS_UNLOADING) &&
670         (((PCMHIVE)Hive)->CreatorOwner != KeGetCurrentThread()))
671     {
672         /* Fail */
673         return NULL;
674     }
675 
676     /* Check if this is a fake KCB */
677     IsFake = Flags & CMP_CREATE_FAKE_KCB ? TRUE : FALSE;
678 
679     /* If we have a parent, use its ConvKey */
680     if (Parent) ConvKey = Parent->ConvKey;
681 
682     /* Make a copy of the name */
683     NodeName = *KeyName;
684 
685     /* Remove leading slash */
686     while ((NodeName.Length) && (*NodeName.Buffer == OBJ_NAME_PATH_SEPARATOR))
687     {
688         /* Move the buffer by one */
689         NodeName.Buffer++;
690         NodeName.Length -= sizeof(WCHAR);
691     }
692 
693     /* Make sure we didn't get just a slash or something */
694     ASSERT(NodeName.Length > 0);
695 
696     /* Now setup the hash */
697     p = NodeName.Buffer;
698     for (i = 0; i < NodeName.Length; i += sizeof(WCHAR))
699     {
700         /* Make sure it's a valid character */
701         if (*p != OBJ_NAME_PATH_SEPARATOR)
702         {
703             /* Add this key to the hash */
704             ConvKey = 37 * ConvKey + RtlUpcaseUnicodeChar(*p);
705         }
706 
707         /* Move on */
708         p++;
709     }
710 
711     /* Allocate the KCB */
712     Kcb = CmpAllocateKeyControlBlock();
713     if (!Kcb) return NULL;
714 
715     /* Initailize the key list */
716     InitializeKCBKeyBodyList(Kcb);
717 
718     /* Set it up */
719     Kcb->Signature = CM_KCB_SIGNATURE;
720     Kcb->Delete = FALSE;
721     Kcb->RefCount = 1;
722     Kcb->KeyHive = Hive;
723     Kcb->KeyCell = Index;
724     Kcb->ConvKey = ConvKey;
725     Kcb->DelayedCloseIndex = CmpDelayedCloseSize;
726     Kcb->InDelayClose = 0;
727     ASSERT_KCB_VALID(Kcb);
728 
729     /* Check if we have two hash entires */
730     HashLock = Flags & CMP_LOCK_HASHES_FOR_KCB ? TRUE : FALSE;
731     if (!HashLock)
732     {
733         /* It's not locked, do we have a parent? */
734         if (Parent)
735         {
736             /* Lock the parent KCB and ourselves */
737             CmpAcquireTwoKcbLocksExclusiveByKey(ConvKey, Parent->ConvKey);
738         }
739         else
740         {
741             /* Lock only ourselves */
742             CmpAcquireKcbLockExclusive(Kcb);
743         }
744     }
745 
746     /* Check if we already have a KCB */
747     FoundKcb = CmpInsertKeyHash(&Kcb->KeyHash, IsFake);
748     if (FoundKcb)
749     {
750         /* Sanity check */
751         ASSERT(!FoundKcb->Delete);
752         Kcb->Signature = CM_KCB_INVALID_SIGNATURE;
753 
754         /* Free the one we allocated and reference this one */
755         CmpFreeKeyControlBlock(Kcb);
756         ASSERT_KCB_VALID(FoundKcb);
757         Kcb = FoundKcb;
758         if (!CmpReferenceKeyControlBlock(Kcb))
759         {
760             /* We got too many handles */
761             ASSERT(Kcb->RefCount + 1 != 0);
762             Kcb = NULL;
763         }
764         else
765         {
766             /* Check if we're not creating a fake one, but it used to be fake */
767             if ((Kcb->ExtFlags & CM_KCB_KEY_NON_EXIST) && !(IsFake))
768             {
769                 /* Set the hive and cell */
770                 Kcb->KeyHive = Hive;
771                 Kcb->KeyCell = Index;
772 
773                 /* This means that our current information is invalid */
774                 Kcb->ExtFlags = CM_KCB_INVALID_CACHED_INFO;
775             }
776 
777             /* Check if we didn't have any valid data */
778             if (!(Kcb->ExtFlags & (CM_KCB_NO_SUBKEY |
779                                    CM_KCB_SUBKEY_ONE |
780                                    CM_KCB_SUBKEY_HINT)))
781             {
782                 /* Calculate the index hint */
783                 Kcb->SubKeyCount = Node->SubKeyCounts[Stable] +
784                                    Node->SubKeyCounts[Volatile];
785 
786                 /* Cached information is now valid */
787                 Kcb->ExtFlags &= ~CM_KCB_INVALID_CACHED_INFO;
788             }
789 
790             /* Setup the other data */
791             Kcb->KcbLastWriteTime = Node->LastWriteTime;
792             Kcb->KcbMaxNameLen = (USHORT)Node->MaxNameLen;
793             Kcb->KcbMaxValueNameLen = (USHORT)Node->MaxValueNameLen;
794             Kcb->KcbMaxValueDataLen = Node->MaxValueDataLen;
795         }
796     }
797     else
798     {
799         /* No KCB, do we have a parent? */
800         if (Parent)
801         {
802             /* Reference the parent */
803             if (((Parent->TotalLevels + 1) < 512) &&
804                 (CmpReferenceKeyControlBlock(Parent)))
805             {
806                 /* Link it */
807                 Kcb->ParentKcb = Parent;
808                 Kcb->TotalLevels = Parent->TotalLevels + 1;
809             }
810             else
811             {
812                 /* Remove the KCB and free it */
813                 CmpRemoveKeyControlBlock(Kcb);
814                 Kcb->Signature = CM_KCB_INVALID_SIGNATURE;
815                 CmpFreeKeyControlBlock(Kcb);
816                 Kcb = NULL;
817             }
818         }
819         else
820         {
821             /* No parent, this is the root node */
822             Kcb->ParentKcb = NULL;
823             Kcb->TotalLevels = 1;
824         }
825 
826         /* Check if we have a KCB */
827         if (Kcb)
828         {
829             /* Get the NCB */
830             Kcb->NameBlock = CmpGetNameControlBlock(&NodeName);
831             if (Kcb->NameBlock)
832             {
833                 /* Fill it out */
834                 Kcb->ValueCache.Count = Node->ValueList.Count;
835                 Kcb->ValueCache.ValueList = Node->ValueList.List;
836                 Kcb->Flags = Node->Flags;
837                 Kcb->ExtFlags = 0;
838                 Kcb->DelayedCloseIndex = CmpDelayedCloseSize;
839 
840                 /* Remember if this is a fake key */
841                 if (IsFake) Kcb->ExtFlags |= CM_KCB_KEY_NON_EXIST;
842 
843                 /* Setup the other data */
844                 Kcb->SubKeyCount = Node->SubKeyCounts[Stable] +
845                                    Node->SubKeyCounts[Volatile];
846                 Kcb->KcbLastWriteTime = Node->LastWriteTime;
847                 Kcb->KcbMaxNameLen = (USHORT)Node->MaxNameLen;
848                 Kcb->KcbMaxValueNameLen = (USHORT)Node->MaxValueNameLen;
849                 Kcb->KcbMaxValueDataLen = (USHORT)Node->MaxValueDataLen;
850             }
851             else
852             {
853                 /* Dereference the KCB */
854                 CmpDereferenceKeyControlBlockWithLock(Parent, FALSE);
855 
856                 /* Remove the KCB and free it */
857                 CmpRemoveKeyControlBlock(Kcb);
858                 Kcb->Signature = CM_KCB_INVALID_SIGNATURE;
859                 CmpFreeKeyControlBlock(Kcb);
860                 Kcb = NULL;
861             }
862         }
863     }
864 
865     /* Check if this is a KCB inside a frozen hive */
866     if ((Kcb) && (((PCMHIVE)Hive)->Frozen) && (!(Kcb->Flags & KEY_SYM_LINK)))
867     {
868         /* Don't add these to the delay close */
869         Kcb->ExtFlags |= CM_KCB_NO_DELAY_CLOSE;
870     }
871 
872     /* Sanity check */
873     ASSERT((!Kcb) || (Kcb->Delete == FALSE));
874 
875     /* Check if we had locked the hashes */
876     if (!HashLock)
877     {
878         /* We locked them manually, do we have a parent? */
879         if (Parent)
880         {
881             /* Unlock the parent KCB and ourselves */
882             CmpReleaseTwoKcbLockByKey(ConvKey, Parent->ConvKey);
883         }
884         else
885         {
886             /* Unlock only ourselves */
887             CmpReleaseKcbLockByKey(ConvKey);
888         }
889     }
890 
891     /* Return the KCB */
892     return Kcb;
893 }
894 
895 PUNICODE_STRING
896 NTAPI
897 CmpConstructName(IN PCM_KEY_CONTROL_BLOCK Kcb)
898 {
899     PUNICODE_STRING KeyName;
900     ULONG i;
901     USHORT NameLength;
902     PCM_KEY_CONTROL_BLOCK MyKcb;
903     PCM_KEY_NODE KeyNode;
904     BOOLEAN DeletedKey = FALSE;
905     PWCHAR TargetBuffer, CurrentNameW;
906     PUCHAR CurrentName;
907 
908     /* Calculate how much size our key name is going to occupy */
909     NameLength = 0;
910     MyKcb = Kcb;
911 
912     while (MyKcb)
913     {
914         /* Add length of the name */
915         if (!MyKcb->NameBlock->Compressed)
916         {
917             NameLength += MyKcb->NameBlock->NameLength;
918         }
919         else
920         {
921             NameLength += CmpCompressedNameSize(MyKcb->NameBlock->Name,
922                                                 MyKcb->NameBlock->NameLength);
923         }
924 
925         /* Sum up the separator too */
926         NameLength += sizeof(WCHAR);
927 
928         /* Go to the parent KCB */
929         MyKcb = MyKcb->ParentKcb;
930     }
931 
932     /* Allocate the unicode string now */
933     KeyName = CmpAllocate(NameLength + sizeof(UNICODE_STRING),
934                           TRUE,
935                           TAG_CM);
936 
937     if (!KeyName) return NULL;
938 
939     /* Set it up */
940     KeyName->Buffer = (PWSTR)(KeyName + 1);
941     KeyName->Length = NameLength;
942     KeyName->MaximumLength = NameLength;
943 
944     /* Loop the keys again, now adding names */
945     NameLength = 0;
946     MyKcb = Kcb;
947 
948     while (MyKcb)
949     {
950         /* Sanity checks for deleted and fake keys */
951         if ((!MyKcb->KeyCell && !MyKcb->Delete) ||
952             !MyKcb->KeyHive ||
953             MyKcb->ExtFlags & CM_KCB_KEY_NON_EXIST)
954         {
955             /* Failure */
956             CmpFree(KeyName, 0);
957             return NULL;
958         }
959 
960         /* Try to get the name from the keynode,
961            if the key is not deleted */
962         if (!DeletedKey && !MyKcb->Delete)
963         {
964             KeyNode = HvGetCell(MyKcb->KeyHive, MyKcb->KeyCell);
965 
966             if (!KeyNode)
967             {
968                 /* Failure */
969                 CmpFree(KeyName, 0);
970                 return NULL;
971             }
972         }
973         else
974         {
975             /* The key was deleted */
976             KeyNode = NULL;
977             DeletedKey = TRUE;
978         }
979 
980         /* Get the pointer to the beginning of the current key name */
981         NameLength += (MyKcb->NameBlock->NameLength + 1) * sizeof(WCHAR);
982         TargetBuffer = &KeyName->Buffer[(KeyName->Length - NameLength) / sizeof(WCHAR)];
983 
984         /* Add a separator */
985         TargetBuffer[0] = OBJ_NAME_PATH_SEPARATOR;
986 
987         /* Add the name, but remember to go from the end to the beginning */
988         if (!MyKcb->NameBlock->Compressed)
989         {
990             /* Get the pointer to the name (from the keynode, if possible) */
991             if ((MyKcb->Flags & (KEY_HIVE_ENTRY | KEY_HIVE_EXIT)) ||
992                 !KeyNode)
993             {
994                 CurrentNameW = MyKcb->NameBlock->Name;
995             }
996             else
997             {
998                 CurrentNameW = KeyNode->Name;
999             }
1000 
1001             /* Copy the name */
1002             for (i=0; i < MyKcb->NameBlock->NameLength; i++)
1003             {
1004                 TargetBuffer[i+1] = *CurrentNameW;
1005                 CurrentNameW++;
1006             }
1007         }
1008         else
1009         {
1010             /* Get the pointer to the name (from the keynode, if possible) */
1011             if ((MyKcb->Flags & (KEY_HIVE_ENTRY | KEY_HIVE_EXIT)) ||
1012                 !KeyNode)
1013             {
1014                 CurrentName = (PUCHAR)MyKcb->NameBlock->Name;
1015             }
1016             else
1017             {
1018                 CurrentName = (PUCHAR)KeyNode->Name;
1019             }
1020 
1021             /* Copy the name */
1022             for (i=0; i < MyKcb->NameBlock->NameLength; i++)
1023             {
1024                 TargetBuffer[i+1] = (WCHAR)*CurrentName;
1025                 CurrentName++;
1026             }
1027         }
1028 
1029         /* Release the cell, if needed */
1030         if (KeyNode) HvReleaseCell(MyKcb->KeyHive, MyKcb->KeyCell);
1031 
1032         /* Go to the parent KCB */
1033         MyKcb = MyKcb->ParentKcb;
1034     }
1035 
1036     /* Return resulting buffer (both UNICODE_STRING and
1037        its buffer following it) */
1038     return KeyName;
1039 }
1040 
1041 VOID
1042 NTAPI
1043 EnlistKeyBodyWithKCB(IN PCM_KEY_BODY KeyBody,
1044                      IN ULONG Flags)
1045 {
1046     ULONG i;
1047 
1048     /* Sanity check */
1049     ASSERT(KeyBody->KeyControlBlock != NULL);
1050 
1051     /* Initialize the list entry */
1052     InitializeListHead(&KeyBody->KeyBodyList);
1053 
1054     /* Check if we can use the parent KCB array */
1055     for (i = 0; i < 4; i++)
1056     {
1057         /* Add it into the list */
1058         if (!InterlockedCompareExchangePointer((PVOID*)&KeyBody->KeyControlBlock->
1059                                                KeyBodyArray[i],
1060                                                KeyBody,
1061                                                NULL))
1062         {
1063             /* Added */
1064             return;
1065         }
1066     }
1067 
1068     /* Array full, check if we need to unlock the KCB */
1069     if (Flags & CMP_ENLIST_KCB_LOCKED_SHARED)
1070     {
1071         /* It's shared, so release the KCB shared lock */
1072         CmpReleaseKcbLock(KeyBody->KeyControlBlock);
1073         ASSERT(!(Flags & CMP_ENLIST_KCB_LOCKED_EXCLUSIVE));
1074     }
1075 
1076     /* Check if we need to lock the KCB */
1077     if (!(Flags & CMP_ENLIST_KCB_LOCKED_EXCLUSIVE))
1078     {
1079         /* Acquire the lock */
1080         CmpAcquireKcbLockExclusive(KeyBody->KeyControlBlock);
1081     }
1082 
1083     /* Make sure we have the exclusive lock */
1084     CMP_ASSERT_KCB_LOCK(KeyBody->KeyControlBlock);
1085 
1086     /* Do the insert */
1087     InsertTailList(&KeyBody->KeyControlBlock->KeyBodyListHead,
1088                    &KeyBody->KeyBodyList);
1089 
1090     /* Check if we did a manual lock */
1091     if (!(Flags & (CMP_ENLIST_KCB_LOCKED_SHARED |
1092                    CMP_ENLIST_KCB_LOCKED_EXCLUSIVE)))
1093     {
1094         /* Release the lock */
1095         CmpReleaseKcbLock(KeyBody->KeyControlBlock);
1096     }
1097 }
1098 
1099 VOID
1100 NTAPI
1101 DelistKeyBodyFromKCB(IN PCM_KEY_BODY KeyBody,
1102                      IN BOOLEAN LockHeld)
1103 {
1104     ULONG i;
1105 
1106     /* Sanity check */
1107     ASSERT(KeyBody->KeyControlBlock != NULL);
1108 
1109     /* Check if we can use the parent KCB array */
1110     for (i = 0; i < 4; i++)
1111     {
1112         /* Add it into the list */
1113         if (InterlockedCompareExchangePointer((VOID*)&KeyBody->KeyControlBlock->
1114                                               KeyBodyArray[i],
1115                                               NULL,
1116                                               KeyBody) == KeyBody)
1117         {
1118             /* Removed */
1119             return;
1120         }
1121     }
1122 
1123     /* Sanity checks */
1124     ASSERT(IsListEmpty(&KeyBody->KeyControlBlock->KeyBodyListHead) == FALSE);
1125     ASSERT(IsListEmpty(&KeyBody->KeyBodyList) == FALSE);
1126 
1127     /* Lock the KCB */
1128     if (!LockHeld) CmpAcquireKcbLockExclusive(KeyBody->KeyControlBlock);
1129     CMP_ASSERT_KCB_LOCK(KeyBody->KeyControlBlock);
1130 
1131     /* Remove the entry */
1132     RemoveEntryList(&KeyBody->KeyBodyList);
1133 
1134     /* Unlock it it if we did a manual lock */
1135     if (!LockHeld) CmpReleaseKcbLock(KeyBody->KeyControlBlock);
1136 }
1137 
1138 VOID
1139 NTAPI
1140 CmpFlushNotifiesOnKeyBodyList(IN PCM_KEY_CONTROL_BLOCK Kcb,
1141                               IN BOOLEAN LockHeld)
1142 {
1143     PLIST_ENTRY NextEntry, ListHead;
1144     PCM_KEY_BODY KeyBody;
1145 
1146     /* Sanity check */
1147     LockHeld ? CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK() : CmpIsKcbLockedExclusive(Kcb);
1148     while (TRUE)
1149     {
1150         /* Is the list empty? */
1151         ListHead = &Kcb->KeyBodyListHead;
1152         if (!IsListEmpty(ListHead))
1153         {
1154             /* Loop the list */
1155             NextEntry = ListHead->Flink;
1156             while (NextEntry != ListHead)
1157             {
1158                 /* Get the key body */
1159                 KeyBody = CONTAINING_RECORD(NextEntry, CM_KEY_BODY, KeyBodyList);
1160                 ASSERT(KeyBody->Type == CM_KEY_BODY_TYPE);
1161 
1162                 /* Check for notifications */
1163                 if (KeyBody->NotifyBlock)
1164                 {
1165                     /* Is the lock held? */
1166                     if (LockHeld)
1167                     {
1168                         /* Flush it */
1169                         CmpFlushNotify(KeyBody, LockHeld);
1170                         ASSERT(KeyBody->NotifyBlock == NULL);
1171                         continue;
1172                     }
1173 
1174                     /* Lock isn't held, so we need to take a reference */
1175                     if (ObReferenceObjectSafe(KeyBody))
1176                     {
1177                         /* Now we can flush */
1178                         CmpFlushNotify(KeyBody, LockHeld);
1179                         ASSERT(KeyBody->NotifyBlock == NULL);
1180 
1181                         /* Release the reference we took */
1182                         ObDereferenceObjectDeferDelete(KeyBody);
1183                         continue;
1184                     }
1185                 }
1186 
1187                 /* Try the next entry */
1188                 NextEntry = NextEntry->Flink;
1189             }
1190         }
1191 
1192         /* List has been parsed, exit */
1193         break;
1194     }
1195 }
1196