Sync to trunk head (35333)
[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 BOOLEAN CmpHoldLazyFlush;
22
23 /* FUNCTIONS *****************************************************************/
24
25 VOID
26 NTAPI
27 CmpInitializeCache(VOID)
28 {
29 ULONG Length, i;
30
31 /* Calculate length for the table */
32 Length = CmpHashTableSize * sizeof(CM_KEY_HASH_TABLE_ENTRY);
33
34 /* Allocate it */
35 CmpCacheTable = ExAllocatePoolWithTag(PagedPool, Length, TAG_CM);
36 if (!CmpCacheTable)
37 {
38 /* Take the system down */
39 KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 3, 1, 0, 0);
40 }
41
42 /* Zero out the table */
43 RtlZeroMemory(CmpCacheTable, Length);
44
45 /* Initialize the locks */
46 for (i = 0;i < CmpHashTableSize; i++)
47 {
48 /* Setup the pushlock */
49 ExInitializePushLock((PULONG_PTR)&CmpCacheTable[i].Lock);
50 }
51
52 /* Calculate length for the name cache */
53 Length = CmpHashTableSize * sizeof(CM_NAME_HASH_TABLE_ENTRY);
54
55 /* Now allocate the name cache table */
56 CmpNameCacheTable = ExAllocatePoolWithTag(PagedPool, Length, TAG_CM);
57 if (!CmpNameCacheTable)
58 {
59 /* Take the system down */
60 KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 3, 3, 0, 0);
61 }
62
63 /* Zero out the table */
64 RtlZeroMemory(CmpNameCacheTable, Length);
65
66 /* Initialize the locks */
67 for (i = 0;i < CmpHashTableSize; i++)
68 {
69 /* Setup the pushlock */
70 ExInitializePushLock((PULONG_PTR)&CmpNameCacheTable[i].Lock);
71 }
72
73 /* Setup the delayed close table */
74 CmpInitializeDelayedCloseTable();
75 }
76
77 VOID
78 NTAPI
79 CmpRemoveKeyHash(IN PCM_KEY_HASH KeyHash)
80 {
81 PCM_KEY_HASH *Prev;
82 PCM_KEY_HASH Current;
83 ASSERT_VALID_HASH(KeyHash);
84
85 /* Lookup all the keys in this index entry */
86 Prev = &GET_HASH_ENTRY(CmpCacheTable, KeyHash->ConvKey).Entry;
87 while (TRUE)
88 {
89 /* Save the current one and make sure it's valid */
90 Current = *Prev;
91 ASSERT(Current != NULL);
92 ASSERT_VALID_HASH(Current);
93
94 /* Check if it matches */
95 if (Current == KeyHash)
96 {
97 /* Then write the previous one */
98 *Prev = Current->NextHash;
99 if (*Prev) ASSERT_VALID_HASH(*Prev);
100 break;
101 }
102
103 /* Otherwise, keep going */
104 Prev = &Current->NextHash;
105 }
106 }
107
108 PCM_KEY_CONTROL_BLOCK
109 NTAPI
110 CmpInsertKeyHash(IN PCM_KEY_HASH KeyHash,
111 IN BOOLEAN IsFake)
112 {
113 ULONG i;
114 PCM_KEY_HASH Entry;
115 ASSERT_VALID_HASH(KeyHash);
116
117 /* Get the hash index */
118 i = GET_HASH_INDEX(KeyHash->ConvKey);
119
120 /* If this is a fake key, increase the key cell to use the parent data */
121 if (IsFake) KeyHash->KeyCell++;
122
123 /* Loop the hash table */
124 Entry = CmpCacheTable[i].Entry;
125 while (Entry)
126 {
127 /* Check if this matches */
128 ASSERT_VALID_HASH(Entry);
129 if ((KeyHash->ConvKey == Entry->ConvKey) &&
130 (KeyHash->KeyCell == Entry->KeyCell) &&
131 (KeyHash->KeyHive == Entry->KeyHive))
132 {
133 /* Return it */
134 return CONTAINING_RECORD(Entry, CM_KEY_CONTROL_BLOCK, KeyHash);
135 }
136
137 /* Keep looping */
138 Entry = Entry->NextHash;
139 }
140
141 /* No entry found, add this one and return NULL since none existed */
142 KeyHash->NextHash = CmpCacheTable[i].Entry;
143 CmpCacheTable[i].Entry = KeyHash;
144 return NULL;
145 }
146
147 PCM_NAME_CONTROL_BLOCK
148 NTAPI
149 CmpGetNameControlBlock(IN PUNICODE_STRING NodeName)
150 {
151 PCM_NAME_CONTROL_BLOCK Ncb = NULL;
152 ULONG ConvKey = 0;
153 PWCHAR p, pp;
154 ULONG i;
155 BOOLEAN IsCompressed = TRUE, Found = FALSE;
156 PCM_NAME_HASH HashEntry;
157 ULONG Length, NcbSize;
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 valid character */
179 if (*NodeName->Buffer > (UCHAR)-1)
180 {
181 /* This is the actual size, and we know we're not compressed */
182 Length = NodeName->Length;
183 IsCompressed = FALSE;
184 }
185 }
186
187 /* Lock the NCB entry */
188 CmpAcquireNcbLockExclusiveByKey(ConvKey);
189
190 /* Get the hash entry */
191 HashEntry = GET_HASH_ENTRY(CmpNameCacheTable, ConvKey).Entry;
192 while (HashEntry)
193 {
194 /* Get the current NCB */
195 Ncb = CONTAINING_RECORD(HashEntry, CM_NAME_CONTROL_BLOCK, NameHash);
196
197 /* Check if the hash matches */
198 if ((ConvKey = HashEntry->ConvKey) && (Length = Ncb->NameLength))
199 {
200 /* Assume success */
201 Found = TRUE;
202
203 /* If the NCB is compressed, do a compressed name compare */
204 if (Ncb->Compressed)
205 {
206 /* Compare names */
207 if (CmpCompareCompressedName(NodeName, Ncb->Name, Length))
208 {
209 /* We failed */
210 Found = FALSE;
211 }
212 }
213 else
214 {
215 /* Do a manual compare */
216 p = NodeName->Buffer;
217 pp = Ncb->Name;
218 for (i = 0; i < Ncb->NameLength; i += sizeof(WCHAR))
219 {
220 /* Compare the character */
221 if (RtlUpcaseUnicodeChar(*p) != RtlUpcaseUnicodeChar(*pp))
222 {
223 /* Failed */
224 Found = FALSE;
225 break;
226 }
227
228 /* Next chars */
229 p++;
230 pp++;
231 }
232 }
233
234 /* Check if we found a name */
235 if (Found)
236 {
237 /* Reference it */
238 Ncb->RefCount++;
239 }
240 }
241
242 /* Go to the next hash */
243 HashEntry = HashEntry->NextHash;
244 }
245
246 /* Check if we didn't find it */
247 if (!Found)
248 {
249 /* Allocate one */
250 NcbSize = FIELD_OFFSET(CM_NAME_CONTROL_BLOCK, Name) + Length;
251 Ncb = ExAllocatePoolWithTag(PagedPool, NcbSize, TAG_CM);
252 if (!Ncb)
253 {
254 /* Release the lock and fail */
255 CmpReleaseNcbLockByKey(ConvKey);
256 return NULL;
257 }
258
259 /* Clear it out */
260 RtlZeroMemory(Ncb, NcbSize);
261
262 /* Check if the name was compressed */
263 if (IsCompressed)
264 {
265 /* Copy the compressed name */
266 Ncb->Compressed = TRUE;
267 for (i = 0; i < Length; i++)
268 {
269 /* Copy Unicode to ANSI */
270 ((PCHAR)Ncb->Name)[i] = (CHAR)RtlUpcaseUnicodeChar(NodeName->Buffer[i]);
271 }
272 }
273 else
274 {
275 /* Copy the name directly */
276 Ncb->Compressed = FALSE;
277 for (i = 0; i < Length; i++)
278 {
279 /* Copy each unicode character */
280 Ncb->Name[i] = RtlUpcaseUnicodeChar(NodeName->Buffer[i]);
281 }
282 }
283
284 /* Setup the rest of the NCB */
285 Ncb->ConvKey = ConvKey;
286 Ncb->RefCount++;
287 Ncb->NameLength = Length;
288
289 /* Insert the name in the hash table */
290 HashEntry = &Ncb->NameHash;
291 HashEntry->NextHash = GET_HASH_ENTRY(CmpNameCacheTable, ConvKey).Entry;
292 GET_HASH_ENTRY(CmpNameCacheTable, ConvKey).Entry = HashEntry;
293 }
294
295 /* Release NCB lock */
296 CmpReleaseNcbLockByKey(ConvKey);
297
298 /* Return the NCB found */
299 return Ncb;
300 }
301
302 VOID
303 NTAPI
304 CmpRemoveKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)
305 {
306 /* Make sure that the registry and KCB are utterly locked */
307 ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
308 (CmpTestRegistryLockExclusive() == TRUE));
309
310 /* Remove the key hash */
311 CmpRemoveKeyHash(&Kcb->KeyHash);
312 }
313
314 VOID
315 NTAPI
316 CmpDereferenceNameControlBlockWithLock(IN PCM_NAME_CONTROL_BLOCK Ncb)
317 {
318 PCM_NAME_HASH Current, *Next;
319 ULONG ConvKey = Ncb->ConvKey;
320
321 /* Lock the NCB */
322 CmpAcquireNcbLockExclusiveByKey(ConvKey);
323
324 /* Decrease the reference count */
325 if (!(--Ncb->RefCount))
326 {
327 /* Find the NCB in the table */
328 Next = &GET_HASH_ENTRY(CmpNameCacheTable, Ncb->ConvKey).Entry;
329 while (TRUE)
330 {
331 /* Check the current entry */
332 Current = *Next;
333 ASSERT(Current != NULL);
334 if (Current == &Ncb->NameHash)
335 {
336 /* Unlink it */
337 *Next = Current->NextHash;
338 break;
339 }
340
341 /* Get to the next one */
342 Next = &Current->NextHash;
343 }
344
345 /* Found it, now free it */
346 ExFreePool(Ncb);
347 }
348
349 /* Release the lock */
350 CmpReleaseNcbLockByKey(ConvKey);
351 }
352
353 BOOLEAN
354 NTAPI
355 CmpReferenceKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)
356 {
357 /* Check if this is the KCB's first reference */
358 if (Kcb->RefCount == 0)
359 {
360 /* Check if the KCB is locked in shared mode */
361 if (!CmpIsKcbLockedExclusive(Kcb))
362 {
363 /* Convert it to exclusive */
364 if (!CmpTryToConvertKcbSharedToExclusive(Kcb))
365 {
366 /* Set the delayed close index so that we can be ignored */
367 Kcb->DelayedCloseIndex = 1;
368
369 /* Increase the reference count while we release the lock */
370 InterlockedIncrement((PLONG)&Kcb->RefCount);
371
372 /* Go from shared to exclusive */
373 CmpConvertKcbSharedToExclusive(Kcb);
374
375 /* Decrement the reference count; the lock is now held again */
376 InterlockedDecrement((PLONG)&Kcb->RefCount);
377
378 /* Check if we still control the index */
379 if (Kcb->DelayedCloseIndex == 1)
380 {
381 /* Reset it */
382 Kcb->DelayedCloseIndex = 0;
383 }
384 else
385 {
386 /* Sanity check */
387 ASSERT((Kcb->DelayedCloseIndex == CmpDelayedCloseSize) ||
388 (Kcb->DelayedCloseIndex == 0));
389 }
390 }
391 }
392 }
393
394 /* Increase the reference count */
395 if ((InterlockedIncrement((PLONG)&Kcb->RefCount) & 0xFFFF) == 0)
396 {
397 /* We've overflown to 64K references, bail out */
398 InterlockedDecrement((PLONG)&Kcb->RefCount);
399 return FALSE;
400 }
401
402 /* Check if this was the last close index */
403 if (!Kcb->DelayedCloseIndex)
404 {
405 /* Check if the KCB is locked in shared mode */
406 if (!CmpIsKcbLockedExclusive(Kcb))
407 {
408 /* Convert it to exclusive */
409 if (!CmpTryToConvertKcbSharedToExclusive(Kcb))
410 {
411 /* Go from shared to exclusive */
412 CmpConvertKcbSharedToExclusive(Kcb);
413 }
414 }
415
416 /* If we're still the last entry, remove us */
417 if (!Kcb->DelayedCloseIndex) CmpRemoveFromDelayedClose(Kcb);
418 }
419
420 /* Return success */
421 return TRUE;
422 }
423
424 VOID
425 NTAPI
426 CmpCleanUpKcbValueCache(IN PCM_KEY_CONTROL_BLOCK Kcb)
427 {
428 PULONG_PTR CachedList;
429 ULONG i;
430
431 /* Sanity check */
432 ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
433 (CmpTestRegistryLockExclusive() == TRUE));
434
435 /* Check if the value list is cached */
436 if (CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList))
437 {
438 /* Get the cache list */
439 CachedList = (PULONG_PTR)CMP_GET_CACHED_DATA(Kcb->ValueCache.ValueList);
440 for (i = 0; i < Kcb->ValueCache.Count; i++)
441 {
442 /* Check if this cell is cached */
443 if (CMP_IS_CELL_CACHED(CachedList[i]))
444 {
445 /* Free it */
446 ExFreePool((PVOID)CMP_GET_CACHED_CELL(CachedList[i]));
447 }
448 }
449
450 /* Now free the list */
451 ExFreePool((PVOID)CMP_GET_CACHED_CELL(Kcb->ValueCache.ValueList));
452 Kcb->ValueCache.ValueList = HCELL_NIL;
453 }
454 else if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)
455 {
456 /* This is a sym link, check if there's only one reference left */
457 if ((Kcb->ValueCache.RealKcb->RefCount == 1) &&
458 !(Kcb->ValueCache.RealKcb->Delete))
459 {
460 /* Disable delay close for the KCB */
461 Kcb->ValueCache.RealKcb->ExtFlags |= CM_KCB_NO_DELAY_CLOSE;
462 }
463
464 /* Dereference the KCB */
465 CmpDelayDerefKeyControlBlock(Kcb->ValueCache.RealKcb);
466 Kcb->ExtFlags &= ~CM_KCB_SYM_LINK_FOUND;
467 }
468 }
469
470 VOID
471 NTAPI
472 CmpCleanUpKcbCacheWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb,
473 IN BOOLEAN LockHeldExclusively)
474 {
475 PCM_KEY_CONTROL_BLOCK Parent;
476 PAGED_CODE();
477
478 /* Sanity checks */
479 ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
480 (CmpTestRegistryLockExclusive() == TRUE));
481 ASSERT(Kcb->RefCount == 0);
482
483 /* Cleanup the value cache */
484 CmpCleanUpKcbValueCache(Kcb);
485
486 /* Reference the NCB */
487 CmpDereferenceNameControlBlockWithLock(Kcb->NameBlock);
488
489 /* Check if we have an index hint block and free it */
490 if (Kcb->ExtFlags & CM_KCB_SUBKEY_HINT) ExFreePool(Kcb->IndexHint);
491
492 /* Check if we were already deleted */
493 Parent = Kcb->ParentKcb;
494 if (!Kcb->Delete) CmpRemoveKeyControlBlock(Kcb);
495
496 /* Set invalid KCB signature */
497 Kcb->Signature = CM_KCB_INVALID_SIGNATURE;
498
499 /* Free the KCB as well */
500 CmpFreeKeyControlBlock(Kcb);
501
502 /* Check if we have a parent */
503 if (Parent)
504 {
505 /* Dereference the parent */
506 LockHeldExclusively ?
507 CmpDereferenceKeyControlBlockWithLock(Kcb,LockHeldExclusively) :
508 CmpDelayDerefKeyControlBlock(Kcb);
509 }
510 }
511
512 VOID
513 NTAPI
514 CmpCleanUpSubKeyInfo(IN PCM_KEY_CONTROL_BLOCK Kcb)
515 {
516 PCM_KEY_NODE KeyNode;
517
518 /* Sanity check */
519 ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
520 (CmpTestRegistryLockExclusive() == TRUE));
521
522 /* Check if there's any cached subkey */
523 if (Kcb->ExtFlags & (CM_KCB_NO_SUBKEY | CM_KCB_SUBKEY_ONE | CM_KCB_SUBKEY_HINT))
524 {
525 /* Check if there's a hint */
526 if (Kcb->ExtFlags & (CM_KCB_SUBKEY_HINT))
527 {
528 /* Kill it */
529 ExFreePool(Kcb->IndexHint);
530 }
531
532 /* Remove subkey flags */
533 Kcb->ExtFlags &= ~(CM_KCB_NO_SUBKEY | CM_KCB_SUBKEY_ONE | CM_KCB_SUBKEY_HINT);
534 }
535
536 /* Check if there's no linked cell */
537 if (Kcb->KeyCell == HCELL_NIL)
538 {
539 /* Make sure it's a delete */
540 ASSERT(Kcb->Delete);
541 KeyNode = NULL;
542 }
543 else
544 {
545 /* Get the key node */
546 KeyNode = (PCM_KEY_NODE)HvGetCell(Kcb->KeyHive, Kcb->KeyCell);
547 }
548
549 /* Check if we got the node */
550 if (!KeyNode)
551 {
552 /* We didn't, mark the cached data invalid */
553 Kcb->ExtFlags |= CM_KCB_INVALID_CACHED_INFO;
554 }
555 else
556 {
557 /* We have a keynode, update subkey counts */
558 Kcb->ExtFlags &= ~CM_KCB_INVALID_CACHED_INFO;
559 Kcb->SubKeyCount = KeyNode->SubKeyCounts[Stable] +
560 KeyNode->SubKeyCounts[Volatile];
561
562 /* Release the cell */
563 HvReleaseCell(Kcb->KeyHive, Kcb->KeyCell);
564 }
565 }
566
567 VOID
568 NTAPI
569 CmpDereferenceKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)
570 {
571 LONG OldRefCount, NewRefCount;
572 ULONG ConvKey;
573
574 /* Get the ref count and update it */
575 OldRefCount = *(PLONG)&Kcb->RefCount;
576 NewRefCount = OldRefCount - 1;
577
578 /* Check if we still have references */
579 if( (NewRefCount & 0xFFFF) > 0)
580 {
581 /* Do the dereference */
582 if (InterlockedCompareExchange((PLONG)&Kcb->RefCount,
583 NewRefCount,
584 OldRefCount) == OldRefCount)
585 {
586 /* We'de done */
587 return;
588 }
589 }
590
591 /* Save the key */
592 ConvKey = Kcb->ConvKey;
593
594 /* Do the dereference inside the lock */
595 CmpAcquireKcbLockExclusive(Kcb);
596 CmpDereferenceKeyControlBlockWithLock(Kcb, FALSE);
597 CmpReleaseKcbLockByKey(ConvKey);
598 }
599
600 VOID
601 NTAPI
602 CmpDereferenceKeyControlBlockWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb,
603 IN BOOLEAN LockHeldExclusively)
604 {
605 /* Sanity check */
606 ASSERT_KCB_VALID(Kcb);
607
608 /* Check if this is the last reference */
609 if ((InterlockedDecrement((PLONG)&Kcb->RefCount) & 0xFFFF) == 0)
610 {
611 /* Sanity check */
612 ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
613 (CmpTestRegistryLockExclusive() == TRUE));
614
615 /* Check if we should do a direct delete */
616 if (((CmpHoldLazyFlush) &&
617 !(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND) &&
618 !(Kcb->Flags & KEY_SYM_LINK)) ||
619 (Kcb->ExtFlags & CM_KCB_NO_DELAY_CLOSE) ||
620 (Kcb->Delete))
621 {
622 /* Clean up the KCB*/
623 CmpCleanUpKcbCacheWithLock(Kcb, LockHeldExclusively);
624 }
625 else
626 {
627 /* Otherwise, use delayed close */
628 CmpAddToDelayedClose(Kcb, LockHeldExclusively);
629 }
630 }
631 }
632
633 VOID
634 NTAPI
635 InitializeKCBKeyBodyList(IN PCM_KEY_CONTROL_BLOCK Kcb)
636 {
637 /* Initialize the list */
638 InitializeListHead(&Kcb->KeyBodyListHead);
639
640 /* Clear the bodies */
641 Kcb->KeyBodyArray[0] =
642 Kcb->KeyBodyArray[1] =
643 Kcb->KeyBodyArray[2] =
644 Kcb->KeyBodyArray[3] = NULL;
645 }
646
647 PCM_KEY_CONTROL_BLOCK
648 NTAPI
649 CmpCreateKeyControlBlock(IN PHHIVE Hive,
650 IN HCELL_INDEX Index,
651 IN PCM_KEY_NODE Node,
652 IN PCM_KEY_CONTROL_BLOCK Parent,
653 IN ULONG Flags,
654 IN PUNICODE_STRING KeyName)
655 {
656 PCM_KEY_CONTROL_BLOCK Kcb, FoundKcb = NULL;
657 UNICODE_STRING NodeName;
658 ULONG ConvKey = 0, i;
659 BOOLEAN IsFake, HashLock;
660 PWCHAR p;
661
662 /* Make sure we own this hive in case it's being unloaded */
663 if ((Hive->HiveFlags & HIVE_IS_UNLOADING) &&
664 (((PCMHIVE)Hive)->CreatorOwner != KeGetCurrentThread()))
665 {
666 /* Fail */
667 return NULL;
668 }
669
670 /* Check if this is a fake KCB */
671 IsFake = Flags & CMP_CREATE_FAKE_KCB ? TRUE : FALSE;
672
673 /* If we have a parent, use its ConvKey */
674 if (Parent) ConvKey = Parent->ConvKey;
675
676 /* Make a copy of the name */
677 NodeName = *KeyName;
678
679 /* Remove leading slash */
680 while ((NodeName.Length) && (*NodeName.Buffer == OBJ_NAME_PATH_SEPARATOR))
681 {
682 /* Move the buffer by one */
683 NodeName.Buffer++;
684 NodeName.Length -= sizeof(WCHAR);
685 }
686
687 /* Make sure we didn't get just a slash or something */
688 ASSERT(NodeName.Length > 0);
689
690 /* Now setup the hash */
691 p = NodeName.Buffer;
692 for (i = 0; i < NodeName.Length; i += sizeof(WCHAR))
693 {
694 /* Make sure it's a valid character */
695 if (*p != OBJ_NAME_PATH_SEPARATOR)
696 {
697 /* Add this key to the hash */
698 ConvKey = 37 * ConvKey + RtlUpcaseUnicodeChar(*p);
699 }
700
701 /* Move on */
702 p++;
703 }
704
705 /* Allocate the KCB */
706 Kcb = CmpAllocateKeyControlBlock();
707 if (!Kcb) return NULL;
708
709 /* Initailize the key list */
710 InitializeKCBKeyBodyList(Kcb);
711
712 /* Set it up */
713 Kcb->Signature = CM_KCB_SIGNATURE;
714 Kcb->Delete = FALSE;
715 Kcb->RefCount = 1;
716 Kcb->KeyHive = Hive;
717 Kcb->KeyCell = Index;
718 Kcb->ConvKey = ConvKey;
719 Kcb->DelayedCloseIndex = CmpDelayedCloseSize;
720 Kcb->InDelayClose = 0;
721 ASSERT_KCB_VALID(Kcb);
722
723 /* Check if we have two hash entires */
724 HashLock = Flags & CMP_LOCK_HASHES_FOR_KCB ? TRUE : FALSE;
725 if (!HashLock)
726 {
727 /* It's not locked, do we have a parent? */
728 if (Parent)
729 {
730 /* Lock the parent KCB and ourselves */
731 CmpAcquireTwoKcbLocksExclusiveByKey(ConvKey, Parent->ConvKey);
732 }
733 else
734 {
735 /* Lock only ourselves */
736 CmpAcquireKcbLockExclusive(Kcb);
737 }
738 }
739
740 /* Check if we already have a KCB */
741 FoundKcb = CmpInsertKeyHash(&Kcb->KeyHash, IsFake);
742 if (FoundKcb)
743 {
744 /* Sanity check */
745 ASSERT(!FoundKcb->Delete);
746 Kcb->Signature = CM_KCB_INVALID_SIGNATURE;
747
748 /* Free the one we allocated and reference this one */
749 CmpFreeKeyControlBlock(Kcb);
750 ASSERT_KCB_VALID(FoundKcb);
751 Kcb = FoundKcb;
752 if (!CmpReferenceKeyControlBlock(Kcb))
753 {
754 /* We got too many handles */
755 ASSERT(Kcb->RefCount + 1 != 0);
756 Kcb = NULL;
757 }
758 else
759 {
760 /* Check if we're not creating a fake one, but it used to be fake */
761 if ((Kcb->ExtFlags & CM_KCB_KEY_NON_EXIST) && !(IsFake))
762 {
763 /* Set the hive and cell */
764 Kcb->KeyHive = Hive;
765 Kcb->KeyCell = Index;
766
767 /* This means that our current information is invalid */
768 Kcb->ExtFlags = CM_KCB_INVALID_CACHED_INFO;
769 }
770
771 /* Check if we didn't have any valid data */
772 if (!(Kcb->ExtFlags & (CM_KCB_NO_SUBKEY |
773 CM_KCB_SUBKEY_ONE |
774 CM_KCB_SUBKEY_HINT)))
775 {
776 /* Calculate the index hint */
777 Kcb->SubKeyCount = Node->SubKeyCounts[Stable] +
778 Node->SubKeyCounts[Volatile];
779
780 /* Cached information is now valid */
781 Kcb->ExtFlags &= ~CM_KCB_INVALID_CACHED_INFO;
782 }
783
784 /* Setup the other data */
785 Kcb->KcbLastWriteTime = Node->LastWriteTime;
786 Kcb->KcbMaxNameLen = (USHORT)Node->MaxNameLen;
787 Kcb->KcbMaxValueNameLen = (USHORT)Node->MaxValueNameLen;
788 Kcb->KcbMaxValueDataLen = Node->MaxValueDataLen;
789 }
790 }
791 else
792 {
793 /* No KCB, do we have a parent? */
794 if (Parent)
795 {
796 /* Reference the parent */
797 if (((Parent->TotalLevels + 1) < 512) &&
798 (CmpReferenceKeyControlBlock(Parent)))
799 {
800 /* Link it */
801 Kcb->ParentKcb = Parent;
802 Kcb->TotalLevels = Parent->TotalLevels + 1;
803 }
804 else
805 {
806 /* Remove the KCB and free it */
807 CmpRemoveKeyControlBlock(Kcb);
808 Kcb->Signature = CM_KCB_INVALID_SIGNATURE;
809 CmpFreeKeyControlBlock(Kcb);
810 Kcb = NULL;
811 }
812 }
813 else
814 {
815 /* No parent, this is the root node */
816 Kcb->ParentKcb = NULL;
817 Kcb->TotalLevels = 1;
818 }
819
820 /* Check if we have a KCB */
821 if (Kcb)
822 {
823 /* Get the NCB */
824 Kcb->NameBlock = CmpGetNameControlBlock(&NodeName);
825 if (Kcb->NameBlock)
826 {
827 /* Fill it out */
828 Kcb->ValueCache.Count = Node->ValueList.Count;
829 Kcb->ValueCache.ValueList = Node->ValueList.List;
830 Kcb->Flags = Node->Flags;
831 Kcb->ExtFlags = 0;
832 Kcb->DelayedCloseIndex = CmpDelayedCloseSize;
833
834 /* Remember if this is a fake key */
835 if (IsFake) Kcb->ExtFlags |= CM_KCB_KEY_NON_EXIST;
836
837 /* Setup the other data */
838 Kcb->SubKeyCount = Node->SubKeyCounts[Stable] +
839 Node->SubKeyCounts[Volatile];
840 Kcb->KcbLastWriteTime = Node->LastWriteTime;
841 Kcb->KcbMaxNameLen = (USHORT)Node->MaxNameLen;
842 Kcb->KcbMaxValueNameLen = (USHORT)Node->MaxValueNameLen;
843 Kcb->KcbMaxValueDataLen = (USHORT)Node->MaxValueDataLen;
844 }
845 else
846 {
847 /* Dereference the KCB */
848 CmpDereferenceKeyControlBlockWithLock(Parent, FALSE);
849
850 /* Remove the KCB and free it */
851 CmpRemoveKeyControlBlock(Kcb);
852 Kcb->Signature = CM_KCB_INVALID_SIGNATURE;
853 CmpFreeKeyControlBlock(Kcb);
854 Kcb = NULL;
855 }
856 }
857 }
858
859 /* Check if this is a KCB inside a frozen hive */
860 if ((Kcb) && (((PCMHIVE)Hive)->Frozen) && (!(Kcb->Flags & KEY_SYM_LINK)))
861 {
862 /* Don't add these to the delay close */
863 Kcb->ExtFlags |= CM_KCB_NO_DELAY_CLOSE;
864 }
865
866 /* Sanity check */
867 ASSERT((!Kcb) || (Kcb->Delete == FALSE));
868
869 /* Check if we had locked the hashes */
870 if (!HashLock)
871 {
872 /* We locked them manually, do we have a parent? */
873 if (Parent)
874 {
875 /* Unlock the parent KCB and ourselves */
876 CmpReleaseTwoKcbLockByKey(ConvKey, Parent->ConvKey);
877 }
878 else
879 {
880 /* Unlock only ourselves */
881 CmpReleaseKcbLockByKey(ConvKey);
882 }
883 }
884
885 /* Return the KCB */
886 return Kcb;
887 }
888
889 VOID
890 NTAPI
891 EnlistKeyBodyWithKCB(IN PCM_KEY_BODY KeyBody,
892 IN ULONG Flags)
893 {
894 ULONG i;
895
896 /* Sanity check */
897 ASSERT(KeyBody->KeyControlBlock != NULL);
898
899 /* Initialize the list entry */
900 InitializeListHead(&KeyBody->KeyBodyList);
901
902 /* Check if we can use the parent KCB array */
903 for (i = 0; i < 4; i++)
904 {
905 /* Add it into the list */
906 if (!InterlockedCompareExchangePointer((PVOID*)&KeyBody->KeyControlBlock->
907 KeyBodyArray[i],
908 KeyBody,
909 NULL))
910 {
911 /* Added */
912 return;
913 }
914 }
915
916 /* Array full, check if we need to unlock the KCB */
917 if (Flags & CMP_ENLIST_KCB_LOCKED_SHARED)
918 {
919 /* It's shared, so release the KCB shared lock */
920 CmpReleaseKcbLock(KeyBody->KeyControlBlock);
921 ASSERT(!(Flags & CMP_ENLIST_KCB_LOCKED_EXCLUSIVE));
922 }
923
924 /* Check if we need to lock the KCB */
925 if (!(Flags & CMP_ENLIST_KCB_LOCKED_EXCLUSIVE))
926 {
927 /* Acquire the lock */
928 CmpAcquireKcbLockExclusive(KeyBody->KeyControlBlock);
929 }
930
931 /* Make sure we have the exclusive lock */
932 ASSERT((CmpIsKcbLockedExclusive(KeyBody->KeyControlBlock) == TRUE) ||
933 (CmpTestRegistryLockExclusive() == TRUE));
934
935 /* do the insert */
936 InsertTailList(&KeyBody->KeyControlBlock->KeyBodyListHead,
937 &KeyBody->KeyBodyList);
938
939 /* Check if we did a manual lock */
940 if (!(Flags & (CMP_ENLIST_KCB_LOCKED_SHARED |
941 CMP_ENLIST_KCB_LOCKED_EXCLUSIVE)))
942 {
943 /* Release the lock */
944 CmpReleaseKcbLock(KeyBody->KeyControlBlock);
945 }
946 }
947
948 VOID
949 NTAPI
950 DelistKeyBodyFromKCB(IN PCM_KEY_BODY KeyBody,
951 IN BOOLEAN LockHeld)
952 {
953 ULONG i;
954
955 /* Sanity check */
956 ASSERT(KeyBody->KeyControlBlock != NULL);
957
958 /* Check if we can use the parent KCB array */
959 for (i = 0; i < 4; i++)
960 {
961 /* Add it into the list */
962 if (InterlockedCompareExchangePointer((VOID*)&KeyBody->KeyControlBlock->
963 KeyBodyArray[i],
964 NULL,
965 KeyBody) == KeyBody)
966 {
967 /* Removed */
968 return;
969 }
970 }
971
972 /* Sanity checks */
973 ASSERT(IsListEmpty(&KeyBody->KeyControlBlock->KeyBodyListHead) == FALSE);
974 ASSERT(IsListEmpty(&KeyBody->KeyBodyList) == FALSE);
975
976 /* Lock the KCB */
977 if (!LockHeld) CmpAcquireKcbLockExclusive(KeyBody->KeyControlBlock);
978 ASSERT((CmpIsKcbLockedExclusive(KeyBody->KeyControlBlock) == TRUE) ||
979 (CmpTestRegistryLockExclusive() == TRUE));
980
981 /* Remove the entry */
982 RemoveEntryList(&KeyBody->KeyBodyList);
983
984 /* Unlock it it if we did a manual lock */
985 if (!LockHeld) CmpReleaseKcbLock(KeyBody->KeyControlBlock);
986 }