ca60d4e01df7b0b6e591611b6c91397529e65b5a
[reactos.git] / reactos / ntoskrnl / config / cmkcbncb.c
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 VOID
24 NTAPI
25 INIT_FUNCTION
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 }