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