xref: /reactos/ntoskrnl/config/cmkcbncb.c (revision 76f1da56)
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
CmpInitializeCache(VOID)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
CmpRemoveKeyHash(IN PCM_KEY_HASH KeyHash)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
CmpInsertKeyHash(IN PCM_KEY_HASH KeyHash,IN BOOLEAN IsFake)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
CmpGetNameControlBlock(IN PUNICODE_STRING NodeName)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 = COMPUTE_HASH_CHAR(ConvKey, *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
CmpRemoveKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)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
CmpDereferenceNameControlBlockWithLock(IN PCM_NAME_CONTROL_BLOCK Ncb)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
CmpReferenceKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)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
CmpCleanUpKcbValueCache(IN PCM_KEY_CONTROL_BLOCK Kcb)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
CmpCleanUpKcbCacheWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb,IN BOOLEAN LockHeldExclusively)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
CmpCleanUpSubKeyInfo(IN PCM_KEY_CONTROL_BLOCK Kcb)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
CmpDereferenceKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)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
CmpDereferenceKeyControlBlockWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb,IN BOOLEAN LockHeldExclusively)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
InitializeKCBKeyBodyList(IN PCM_KEY_CONTROL_BLOCK Kcb)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
CmpCreateKeyControlBlock(IN PHHIVE Hive,IN HCELL_INDEX Index,IN PCM_KEY_NODE Node,IN PCM_KEY_CONTROL_BLOCK Parent,IN ULONG Flags,IN PUNICODE_STRING KeyName)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 = COMPUTE_HASH_CHAR(ConvKey, *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);
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
CmpConstructName(IN PCM_KEY_CONTROL_BLOCK Kcb)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 = (PCM_KEY_NODE)HvGetCell(MyKcb->KeyHive, MyKcb->KeyCell);
965             if (!KeyNode)
966             {
967                 /* Failure */
968                 CmpFree(KeyName, 0);
969                 return NULL;
970             }
971         }
972         else
973         {
974             /* The key was deleted */
975             KeyNode = NULL;
976             DeletedKey = TRUE;
977         }
978 
979         /* Get the pointer to the beginning of the current key name */
980         NameLength += (MyKcb->NameBlock->NameLength + 1) * sizeof(WCHAR);
981         TargetBuffer = &KeyName->Buffer[(KeyName->Length - NameLength) / sizeof(WCHAR)];
982 
983         /* Add a separator */
984         TargetBuffer[0] = OBJ_NAME_PATH_SEPARATOR;
985 
986         /* Add the name, but remember to go from the end to the beginning */
987         if (!MyKcb->NameBlock->Compressed)
988         {
989             /* Get the pointer to the name (from the keynode, if possible) */
990             if ((MyKcb->Flags & (KEY_HIVE_ENTRY | KEY_HIVE_EXIT)) ||
991                 !KeyNode)
992             {
993                 CurrentNameW = MyKcb->NameBlock->Name;
994             }
995             else
996             {
997                 CurrentNameW = KeyNode->Name;
998             }
999 
1000             /* Copy the name */
1001             for (i=0; i < MyKcb->NameBlock->NameLength; i++)
1002             {
1003                 TargetBuffer[i+1] = *CurrentNameW;
1004                 CurrentNameW++;
1005             }
1006         }
1007         else
1008         {
1009             /* Get the pointer to the name (from the keynode, if possible) */
1010             if ((MyKcb->Flags & (KEY_HIVE_ENTRY | KEY_HIVE_EXIT)) ||
1011                 !KeyNode)
1012             {
1013                 CurrentName = (PUCHAR)MyKcb->NameBlock->Name;
1014             }
1015             else
1016             {
1017                 CurrentName = (PUCHAR)KeyNode->Name;
1018             }
1019 
1020             /* Copy the name */
1021             for (i=0; i < MyKcb->NameBlock->NameLength; i++)
1022             {
1023                 TargetBuffer[i+1] = (WCHAR)*CurrentName;
1024                 CurrentName++;
1025             }
1026         }
1027 
1028         /* Release the cell, if needed */
1029         if (KeyNode) HvReleaseCell(MyKcb->KeyHive, MyKcb->KeyCell);
1030 
1031         /* Go to the parent KCB */
1032         MyKcb = MyKcb->ParentKcb;
1033     }
1034 
1035     /* Return resulting buffer (both UNICODE_STRING and
1036        its buffer following it) */
1037     return KeyName;
1038 }
1039 
1040 VOID
1041 NTAPI
EnlistKeyBodyWithKCB(IN PCM_KEY_BODY KeyBody,IN ULONG Flags)1042 EnlistKeyBodyWithKCB(IN PCM_KEY_BODY KeyBody,
1043                      IN ULONG Flags)
1044 {
1045     ULONG i;
1046 
1047     /* Sanity check */
1048     ASSERT(KeyBody->KeyControlBlock != NULL);
1049 
1050     /* Initialize the list entry */
1051     InitializeListHead(&KeyBody->KeyBodyList);
1052 
1053     /* Check if we can use the parent KCB array */
1054     for (i = 0; i < 4; i++)
1055     {
1056         /* Add it into the list */
1057         if (!InterlockedCompareExchangePointer((PVOID*)&KeyBody->KeyControlBlock->
1058                                                KeyBodyArray[i],
1059                                                KeyBody,
1060                                                NULL))
1061         {
1062             /* Added */
1063             return;
1064         }
1065     }
1066 
1067     /* Array full, check if we need to unlock the KCB */
1068     if (Flags & CMP_ENLIST_KCB_LOCKED_SHARED)
1069     {
1070         /* It's shared, so release the KCB shared lock */
1071         CmpReleaseKcbLock(KeyBody->KeyControlBlock);
1072         ASSERT(!(Flags & CMP_ENLIST_KCB_LOCKED_EXCLUSIVE));
1073     }
1074 
1075     /* Check if we need to lock the KCB */
1076     if (!(Flags & CMP_ENLIST_KCB_LOCKED_EXCLUSIVE))
1077     {
1078         /* Acquire the lock */
1079         CmpAcquireKcbLockExclusive(KeyBody->KeyControlBlock);
1080     }
1081 
1082     /* Make sure we have the exclusive lock */
1083     CMP_ASSERT_KCB_LOCK(KeyBody->KeyControlBlock);
1084 
1085     /* Do the insert */
1086     InsertTailList(&KeyBody->KeyControlBlock->KeyBodyListHead,
1087                    &KeyBody->KeyBodyList);
1088 
1089     /* Check if we did a manual lock */
1090     if (!(Flags & (CMP_ENLIST_KCB_LOCKED_SHARED |
1091                    CMP_ENLIST_KCB_LOCKED_EXCLUSIVE)))
1092     {
1093         /* Release the lock */
1094         CmpReleaseKcbLock(KeyBody->KeyControlBlock);
1095     }
1096 }
1097 
1098 VOID
1099 NTAPI
DelistKeyBodyFromKCB(IN PCM_KEY_BODY KeyBody,IN BOOLEAN LockHeld)1100 DelistKeyBodyFromKCB(IN PCM_KEY_BODY KeyBody,
1101                      IN BOOLEAN LockHeld)
1102 {
1103     ULONG i;
1104 
1105     /* Sanity check */
1106     ASSERT(KeyBody->KeyControlBlock != NULL);
1107 
1108     /* Check if we can use the parent KCB array */
1109     for (i = 0; i < 4; i++)
1110     {
1111         /* Add it into the list */
1112         if (InterlockedCompareExchangePointer((PVOID*)&KeyBody->KeyControlBlock->
1113                                               KeyBodyArray[i],
1114                                               NULL,
1115                                               KeyBody) == KeyBody)
1116         {
1117             /* Removed */
1118             return;
1119         }
1120     }
1121 
1122     /* Sanity checks */
1123     ASSERT(IsListEmpty(&KeyBody->KeyControlBlock->KeyBodyListHead) == FALSE);
1124     ASSERT(IsListEmpty(&KeyBody->KeyBodyList) == FALSE);
1125 
1126     /* Lock the KCB */
1127     if (!LockHeld) CmpAcquireKcbLockExclusive(KeyBody->KeyControlBlock);
1128     CMP_ASSERT_KCB_LOCK(KeyBody->KeyControlBlock);
1129 
1130     /* Remove the entry */
1131     RemoveEntryList(&KeyBody->KeyBodyList);
1132 
1133     /* Unlock it it if we did a manual lock */
1134     if (!LockHeld) CmpReleaseKcbLock(KeyBody->KeyControlBlock);
1135 }
1136 
1137 /**
1138  * @brief
1139  * Unlocks a number of KCBs provided by a KCB array.
1140  *
1141  * @param[in] KcbArray
1142  * A pointer to an array of KCBs to be unlocked.
1143  */
1144 VOID
CmpUnLockKcbArray(_In_ PULONG KcbArray)1145 CmpUnLockKcbArray(
1146     _In_ PULONG KcbArray)
1147 {
1148     ULONG i;
1149 
1150     /* Release the locked KCBs in reverse order */
1151     for (i = KcbArray[0]; i > 0; i--)
1152     {
1153         CmpReleaseKcbLockByIndex(KcbArray[i]);
1154     }
1155 }
1156 
1157 /**
1158  * @brief
1159  * Locks a given number of KCBs.
1160  *
1161  * @param[in] KcbArray
1162  * A pointer to an array of KCBs to be locked.
1163  * The count of KCBs to be locked is defined by the
1164  * first element in the array.
1165  *
1166  * @param[in] KcbLockFlags
1167  * Define a lock flag to lock the KCBs.
1168  *
1169  * CMP_LOCK_KCB_ARRAY_EXCLUSIVE -- indicates the KCBs are locked
1170  * exclusively and owned by the calling thread.
1171  *
1172  * CMP_LOCK_KCB_ARRAY_SHARED --  indicates the KCBs are locked
1173  * in shared mode by the owning threads.
1174  */
1175 static
1176 VOID
CmpLockKcbArray(_In_ PULONG KcbArray,_In_ ULONG KcbLockFlags)1177 CmpLockKcbArray(
1178     _In_ PULONG KcbArray,
1179     _In_ ULONG KcbLockFlags)
1180 {
1181     ULONG i;
1182 
1183     /* Lock the KCBs */
1184     for (i = 1; i <= KcbArray[0]; i++)
1185     {
1186         if (KcbLockFlags & CMP_LOCK_KCB_ARRAY_EXCLUSIVE)
1187         {
1188             CmpAcquireKcbLockExclusiveByIndex(KcbArray[i]);
1189         }
1190         else // CMP_LOCK_KCB_ARRAY_SHARED
1191         {
1192             CmpAcquireKcbLockSharedByIndex(KcbArray[i]);
1193         }
1194     }
1195 }
1196 
1197 /**
1198  * @brief
1199  * Sorts an array of KCB hashes in ascending order
1200  * and removes any key indices that are duplicates.
1201  * The purpose of sorting the KCB elements is to
1202  * ensure consistent and proper locking order, so
1203  * that we can prevent a deadlock.
1204  *
1205  * @param[in,out] KcbArray
1206  * A pointer to an array of KCBs of which the key
1207  * indices are to be sorted.
1208  */
1209 static
1210 VOID
CmpSortKcbArray(_Inout_ PULONG KcbArray)1211 CmpSortKcbArray(
1212     _Inout_ PULONG KcbArray)
1213 {
1214     ULONG i, j, k, KcbCount;
1215 
1216     /* Ensure we don't go above the limit of KCBs we can hold */
1217     KcbCount = KcbArray[0];
1218     ASSERT(KcbCount < CMP_KCBS_IN_ARRAY_LIMIT);
1219 
1220     /* Exchange-Sort the array in ascending order. Complexity: O[n^2] */
1221     for (i = 1; i <= KcbCount; i++)
1222     {
1223         for (j = i + 1; j <= KcbCount; j++)
1224         {
1225             if (KcbArray[i] > KcbArray[j])
1226             {
1227                 ULONG Temp = KcbArray[i];
1228                 KcbArray[i] = KcbArray[j];
1229                 KcbArray[j] = Temp;
1230             }
1231         }
1232     }
1233 
1234     /* Now remove any duplicated indices on the sorted array if any */
1235     for (i = 1; i <= KcbCount; i++)
1236     {
1237         for (j = i + 1; j <= KcbCount; j++)
1238         {
1239             if (KcbArray[i] == KcbArray[j])
1240             {
1241                 for (k = j; k <= KcbCount; k++)
1242                 {
1243                     KcbArray[k - 1] = KcbArray[k];
1244                 }
1245 
1246                 j--;
1247                 KcbCount--;
1248             }
1249         }
1250     }
1251 
1252     /* Update the KCB count */
1253     KcbArray[0] = KcbCount;
1254 }
1255 
1256 /**
1257  * @brief
1258  * Builds an array of KCBs and locks them. Whether these
1259  * KCBs are locked exclusively or in shared mode by the calling
1260  * thread, is specified by the KcbLockFlags parameter. The array
1261  * is sorted.
1262  *
1263  * @param[in] HashCacheStack
1264  * A pointer to a hash cache stack. This stack parameter
1265  * stores the convkey hashes of interested KCBs of a
1266  * key path name that need to be locked.
1267  *
1268  * @param[in] KcbLockFlags
1269  * Define a lock flag to lock the KCBs. Consult the
1270  * CmpLockKcbArray documentation for more information.
1271  *
1272  * @param[in] Kcb
1273  * A pointer to a key control block to be given. This
1274  * KCB is included in the array for locking, that is,
1275  * given by the CmpParseKey from the parser object.
1276  *
1277  * @param[in,out] OuterStackArray
1278  * A pointer to an array that lives on the caller's
1279  * stack. It acts like an auxiliary array used by
1280  * the function to store the KCB elements for locking.
1281  * The expected size of the array is up to 32 elements,
1282  * which is the imposed limit by CMP_HASH_STACK_LIMIT.
1283  * This limit also corresponds to the maximum depth of
1284  * subkey levels.
1285  *
1286  * @param[in] TotalRemainingSubkeys
1287  * The number of total remaining subkey levels.
1288  *
1289  * @param[in] MatchRemainSubkeyLevel
1290  * The number of remaining subkey levels that match.
1291  *
1292  * @return
1293  * Returns a pointer to an array of KCBs that have been
1294  * locked.
1295  *
1296  * @remarks
1297  * The caller HAS THE RESPONSIBILITY to unlock the KCBs
1298  * after the necessary operations are done!
1299  */
1300 PULONG
1301 NTAPI
CmpBuildAndLockKcbArray(_In_ PCM_HASH_CACHE_STACK HashCacheStack,_In_ ULONG KcbLockFlags,_In_ PCM_KEY_CONTROL_BLOCK Kcb,_Inout_ PULONG OuterStackArray,_In_ ULONG TotalRemainingSubkeys,_In_ ULONG MatchRemainSubkeyLevel)1302 CmpBuildAndLockKcbArray(
1303     _In_ PCM_HASH_CACHE_STACK HashCacheStack,
1304     _In_ ULONG KcbLockFlags,
1305     _In_ PCM_KEY_CONTROL_BLOCK Kcb,
1306     _Inout_ PULONG OuterStackArray,
1307     _In_ ULONG TotalRemainingSubkeys,
1308     _In_ ULONG MatchRemainSubkeyLevel)
1309 {
1310     ULONG KcbIndex = 1, HashStackIndex, TotalRemaining;
1311     PULONG LockedKcbs = NULL;
1312     PCM_KEY_CONTROL_BLOCK ParentKcb = Kcb->ParentKcb;;
1313 
1314     /* These parameters are expected */
1315     ASSERT(HashCacheStack != NULL);
1316     ASSERT(Kcb != NULL);
1317     ASSERT(OuterStackArray != NULL);
1318 
1319     /*
1320      * Ensure when we build an array of KCBs to lock, that
1321      * we don't go beyond the boundary the limit allows us
1322      * to. 1 is the current KCB we would want to lock
1323      * alongside with the remaining key levels in the formula.
1324      */
1325     TotalRemaining = (1 + TotalRemainingSubkeys) - MatchRemainSubkeyLevel;
1326     ASSERT(TotalRemaining <= CMP_KCBS_IN_ARRAY_LIMIT);
1327 
1328     /* Count the parent if we have one */
1329     if (ParentKcb)
1330     {
1331         /* Ensure we are still below the limit and add the parent to KCBs to lock */
1332         if (TotalRemainingSubkeys == MatchRemainSubkeyLevel)
1333         {
1334             TotalRemaining++;
1335             ASSERT(TotalRemaining <= CMP_KCBS_IN_ARRAY_LIMIT);
1336             OuterStackArray[KcbIndex++] = GET_HASH_INDEX(ParentKcb->ConvKey);
1337         }
1338     }
1339 
1340     /* Add the current KCB */
1341     OuterStackArray[KcbIndex++] = GET_HASH_INDEX(Kcb->ConvKey);
1342 
1343     /* Loop over the hash stack and grab the hashes for locking (they will be converted to indices) */
1344     for (HashStackIndex = 0;
1345          HashStackIndex < TotalRemainingSubkeys;
1346          HashStackIndex++)
1347     {
1348         OuterStackArray[KcbIndex++] = GET_HASH_INDEX(HashCacheStack[HashStackIndex].ConvKey);
1349     }
1350 
1351     /*
1352      * Store how many KCBs we need to lock and sort the array.
1353      * Remove any duplicated indices from the array if any.
1354      */
1355     OuterStackArray[0] = KcbIndex - 1;
1356     CmpSortKcbArray(OuterStackArray);
1357 
1358     /* Lock them */
1359     CmpLockKcbArray(OuterStackArray, KcbLockFlags);
1360 
1361     /* Give the locked KCBs to caller now */
1362     LockedKcbs = OuterStackArray;
1363     return LockedKcbs;
1364 }
1365 
1366 VOID
1367 NTAPI
CmpFlushNotifiesOnKeyBodyList(IN PCM_KEY_CONTROL_BLOCK Kcb,IN BOOLEAN LockHeld)1368 CmpFlushNotifiesOnKeyBodyList(IN PCM_KEY_CONTROL_BLOCK Kcb,
1369                               IN BOOLEAN LockHeld)
1370 {
1371     PLIST_ENTRY NextEntry, ListHead;
1372     PCM_KEY_BODY KeyBody;
1373 
1374     /* Sanity check */
1375     LockHeld ? CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK() : CmpIsKcbLockedExclusive(Kcb);
1376     while (TRUE)
1377     {
1378         /* Is the list empty? */
1379         ListHead = &Kcb->KeyBodyListHead;
1380         if (!IsListEmpty(ListHead))
1381         {
1382             /* Loop the list */
1383             NextEntry = ListHead->Flink;
1384             while (NextEntry != ListHead)
1385             {
1386                 /* Get the key body */
1387                 KeyBody = CONTAINING_RECORD(NextEntry, CM_KEY_BODY, KeyBodyList);
1388                 ASSERT(KeyBody->Type == CM_KEY_BODY_TYPE);
1389 
1390                 /* Check for notifications */
1391                 if (KeyBody->NotifyBlock)
1392                 {
1393                     /* Is the lock held? */
1394                     if (LockHeld)
1395                     {
1396                         /* Flush it */
1397                         CmpFlushNotify(KeyBody, LockHeld);
1398                         ASSERT(KeyBody->NotifyBlock == NULL);
1399                         continue;
1400                     }
1401 
1402                     /* Lock isn't held, so we need to take a reference */
1403                     if (ObReferenceObjectSafe(KeyBody))
1404                     {
1405                         /* Now we can flush */
1406                         CmpFlushNotify(KeyBody, LockHeld);
1407                         ASSERT(KeyBody->NotifyBlock == NULL);
1408 
1409                         /* Release the reference we took */
1410                         ObDereferenceObjectDeferDelete(KeyBody);
1411                         continue;
1412                     }
1413                 }
1414 
1415                 /* Try the next entry */
1416                 NextEntry = NextEntry->Flink;
1417             }
1418         }
1419 
1420         /* List has been parsed, exit */
1421         break;
1422     }
1423 }
1424