Merge trunk head (r43756)
[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 CmpInitializeCache(VOID)
26 {
27 ULONG Length, i;
28
29 /* Calculate length for the table */
30 Length = CmpHashTableSize * sizeof(CM_KEY_HASH_TABLE_ENTRY);
31
32 /* Allocate it */
33 CmpCacheTable = CmpAllocate(Length, TRUE, TAG_CM);
34 if (!CmpCacheTable)
35 {
36 /* Take the system down */
37 KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 3, 1, 0, 0);
38 }
39
40 /* Zero out the table */
41 RtlZeroMemory(CmpCacheTable, Length);
42
43 /* Initialize the locks */
44 for (i = 0;i < CmpHashTableSize; i++)
45 {
46 /* Setup the pushlock */
47 ExInitializePushLock((PULONG_PTR)&CmpCacheTable[i].Lock);
48 }
49
50 /* Calculate length for the name cache */
51 Length = CmpHashTableSize * sizeof(CM_NAME_HASH_TABLE_ENTRY);
52
53 /* Now allocate the name cache table */
54 CmpNameCacheTable = CmpAllocate(Length, TRUE, TAG_CM);
55 if (!CmpNameCacheTable)
56 {
57 /* Take the system down */
58 KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 3, 3, 0, 0);
59 }
60
61 /* Zero out the table */
62 RtlZeroMemory(CmpNameCacheTable, Length);
63
64 /* Initialize the locks */
65 for (i = 0;i < CmpHashTableSize; i++)
66 {
67 /* Setup the pushlock */
68 ExInitializePushLock((PULONG_PTR)&CmpNameCacheTable[i].Lock);
69 }
70
71 /* Setup the delayed close table */
72 CmpInitializeDelayedCloseTable();
73 }
74
75 VOID
76 NTAPI
77 CmpRemoveKeyHash(IN PCM_KEY_HASH KeyHash)
78 {
79 PCM_KEY_HASH *Prev;
80 PCM_KEY_HASH Current;
81 ASSERT_VALID_HASH(KeyHash);
82
83 /* Lookup all the keys in this index entry */
84 Prev = &GET_HASH_ENTRY(CmpCacheTable, KeyHash->ConvKey).Entry;
85 while (TRUE)
86 {
87 /* Save the current one and make sure it's valid */
88 Current = *Prev;
89 ASSERT(Current != NULL);
90 ASSERT_VALID_HASH(Current);
91
92 /* Check if it matches */
93 if (Current == KeyHash)
94 {
95 /* Then write the previous one */
96 *Prev = Current->NextHash;
97 if (*Prev) ASSERT_VALID_HASH(*Prev);
98 break;
99 }
100
101 /* Otherwise, keep going */
102 Prev = &Current->NextHash;
103 }
104 }
105
106 PCM_KEY_CONTROL_BLOCK
107 NTAPI
108 CmpInsertKeyHash(IN PCM_KEY_HASH KeyHash,
109 IN BOOLEAN IsFake)
110 {
111 ULONG i;
112 PCM_KEY_HASH Entry;
113 ASSERT_VALID_HASH(KeyHash);
114
115 /* Get the hash index */
116 i = GET_HASH_INDEX(KeyHash->ConvKey);
117
118 /* If this is a fake key, increase the key cell to use the parent data */
119 if (IsFake) KeyHash->KeyCell++;
120
121 /* Loop the hash table */
122 Entry = CmpCacheTable[i].Entry;
123 while (Entry)
124 {
125 /* Check if this matches */
126 ASSERT_VALID_HASH(Entry);
127 if ((KeyHash->ConvKey == Entry->ConvKey) &&
128 (KeyHash->KeyCell == Entry->KeyCell) &&
129 (KeyHash->KeyHive == Entry->KeyHive))
130 {
131 /* Return it */
132 return CONTAINING_RECORD(Entry, CM_KEY_CONTROL_BLOCK, KeyHash);
133 }
134
135 /* Keep looping */
136 Entry = Entry->NextHash;
137 }
138
139 /* No entry found, add this one and return NULL since none existed */
140 KeyHash->NextHash = CmpCacheTable[i].Entry;
141 CmpCacheTable[i].Entry = KeyHash;
142 return NULL;
143 }
144
145 PCM_NAME_CONTROL_BLOCK
146 NTAPI
147 CmpGetNameControlBlock(IN PUNICODE_STRING NodeName)
148 {
149 PCM_NAME_CONTROL_BLOCK Ncb = NULL;
150 ULONG ConvKey = 0;
151 PWCHAR p, pp;
152 ULONG i;
153 BOOLEAN IsCompressed = TRUE, Found = FALSE;
154 PCM_NAME_HASH HashEntry;
155 ULONG Length, NcbSize;
156
157 /* Loop the name */
158 p = NodeName->Buffer;
159 for (i = 0; i < NodeName->Length; i += sizeof(WCHAR))
160 {
161 /* Make sure it's not a slash */
162 if (*p != OBJ_NAME_PATH_SEPARATOR)
163 {
164 /* Add it to the hash */
165 ConvKey = 37 * ConvKey + RtlUpcaseUnicodeChar(*p);
166 }
167
168 /* Next character */
169 p++;
170 }
171
172 /* Set assumed lengh and loop to check */
173 Length = NodeName->Length / sizeof(WCHAR);
174 for (i = 0; i < (NodeName->Length / sizeof(WCHAR)); i++)
175 {
176 /* Check if this is a 16-bit character */
177 if (NodeName->Buffer[i] > (UCHAR)-1)
178 {
179 /* This is the actual size, and we know we're not compressed */
180 Length = NodeName->Length;
181 IsCompressed = FALSE;
182 break;
183 }
184 }
185
186 /* Lock the NCB entry */
187 CmpAcquireNcbLockExclusiveByKey(ConvKey);
188
189 /* Get the hash entry */
190 HashEntry = GET_HASH_ENTRY(CmpNameCacheTable, ConvKey).Entry;
191 while (HashEntry)
192 {
193 /* Get the current NCB */
194 Ncb = CONTAINING_RECORD(HashEntry, CM_NAME_CONTROL_BLOCK, NameHash);
195
196 /* Check if the hash matches */
197 if ((ConvKey == HashEntry->ConvKey) && (Length == Ncb->NameLength))
198 {
199 /* Assume success */
200 Found = TRUE;
201
202 /* If the NCB is compressed, do a compressed name compare */
203 if (Ncb->Compressed)
204 {
205 /* Compare names */
206 if (CmpCompareCompressedName(NodeName, Ncb->Name, Length))
207 {
208 /* We failed */
209 Found = FALSE;
210 }
211 }
212 else
213 {
214 /* Do a manual compare */
215 p = NodeName->Buffer;
216 pp = Ncb->Name;
217 for (i = 0; i < Ncb->NameLength; i += sizeof(WCHAR))
218 {
219 /* Compare the character */
220 if (RtlUpcaseUnicodeChar(*p) != RtlUpcaseUnicodeChar(*pp))
221 {
222 /* Failed */
223 Found = FALSE;
224 break;
225 }
226
227 /* Next chars */
228 p++;
229 pp++;
230 }
231 }
232
233 /* Check if we found a name */
234 if (Found)
235 {
236 /* Reference it */
237 Ncb->RefCount++;
238 }
239 }
240
241 /* Go to the next hash */
242 HashEntry = HashEntry->NextHash;
243 }
244
245 /* Check if we didn't find it */
246 if (!Found)
247 {
248 /* Allocate one */
249 NcbSize = FIELD_OFFSET(CM_NAME_CONTROL_BLOCK, Name) + Length;
250 Ncb = CmpAllocate(NcbSize, TRUE, TAG_CM);
251 if (!Ncb)
252 {
253 /* Release the lock and fail */
254 CmpReleaseNcbLockByKey(ConvKey);
255 return NULL;
256 }
257
258 /* Clear it out */
259 RtlZeroMemory(Ncb, NcbSize);
260
261 /* Check if the name was compressed */
262 if (IsCompressed)
263 {
264 /* Copy the compressed name */
265 for (i = 0; i < NodeName->Length / sizeof(WCHAR); i++)
266 {
267 /* Copy Unicode to ANSI */
268 ((PCHAR)Ncb->Name)[i] = (CHAR)RtlUpcaseUnicodeChar(NodeName->Buffer[i]);
269 }
270 }
271 else
272 {
273 /* Copy the name directly */
274 for (i = 0; i < NodeName->Length / sizeof(WCHAR); i++)
275 {
276 /* Copy each unicode character */
277 Ncb->Name[i] = RtlUpcaseUnicodeChar(NodeName->Buffer[i]);
278 }
279 }
280
281 /* Setup the rest of the NCB */
282 Ncb->Compressed = IsCompressed;
283 Ncb->ConvKey = ConvKey;
284 Ncb->RefCount++;
285 Ncb->NameLength = Length;
286
287 /* Insert the name in the hash table */
288 HashEntry = &Ncb->NameHash;
289 HashEntry->NextHash = GET_HASH_ENTRY(CmpNameCacheTable, ConvKey).Entry;
290 GET_HASH_ENTRY(CmpNameCacheTable, ConvKey).Entry = HashEntry;
291 }
292
293 /* Release NCB lock */
294 CmpReleaseNcbLockByKey(ConvKey);
295
296 /* Return the NCB found */
297 return Ncb;
298 }
299
300 VOID
301 NTAPI
302 CmpRemoveKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)
303 {
304 /* Make sure that the registry and KCB are utterly locked */
305 ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
306 (CmpTestRegistryLockExclusive() == TRUE));
307
308 /* Remove the key hash */
309 CmpRemoveKeyHash(&Kcb->KeyHash);
310 }
311
312 VOID
313 NTAPI
314 CmpDereferenceNameControlBlockWithLock(IN PCM_NAME_CONTROL_BLOCK Ncb)
315 {
316 PCM_NAME_HASH Current, *Next;
317 ULONG ConvKey = Ncb->ConvKey;
318
319 /* Lock the NCB */
320 CmpAcquireNcbLockExclusiveByKey(ConvKey);
321
322 /* Decrease the reference count */
323 if (!(--Ncb->RefCount))
324 {
325 /* Find the NCB in the table */
326 Next = &GET_HASH_ENTRY(CmpNameCacheTable, Ncb->ConvKey).Entry;
327 while (TRUE)
328 {
329 /* Check the current entry */
330 Current = *Next;
331 ASSERT(Current != NULL);
332 if (Current == &Ncb->NameHash)
333 {
334 /* Unlink it */
335 *Next = Current->NextHash;
336 break;
337 }
338
339 /* Get to the next one */
340 Next = &Current->NextHash;
341 }
342
343 /* Found it, now free it */
344 CmpFree(Ncb, 0);
345 }
346
347 /* Release the lock */
348 CmpReleaseNcbLockByKey(ConvKey);
349 }
350
351 BOOLEAN
352 NTAPI
353 CmpReferenceKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)
354 {
355 CMTRACE(CM_REFERENCE_DEBUG,
356 "%s - Referencing KCB: %p\n", __FUNCTION__, Kcb);
357
358 /* Check if this is the KCB's first reference */
359 if (Kcb->RefCount == 0)
360 {
361 /* Check if the KCB is locked in shared mode */
362 if (!CmpIsKcbLockedExclusive(Kcb))
363 {
364 /* Convert it to exclusive */
365 if (!CmpTryToConvertKcbSharedToExclusive(Kcb))
366 {
367 /* Set the delayed close index so that we can be ignored */
368 Kcb->DelayedCloseIndex = 1;
369
370 /* Increase the reference count while we release the lock */
371 InterlockedIncrement((PLONG)&Kcb->RefCount);
372
373 /* Go from shared to exclusive */
374 CmpConvertKcbSharedToExclusive(Kcb);
375
376 /* Decrement the reference count; the lock is now held again */
377 InterlockedDecrement((PLONG)&Kcb->RefCount);
378
379 /* Check if we still control the index */
380 if (Kcb->DelayedCloseIndex == 1)
381 {
382 /* Reset it */
383 Kcb->DelayedCloseIndex = 0;
384 }
385 else
386 {
387 /* Sanity check */
388 ASSERT((Kcb->DelayedCloseIndex == CmpDelayedCloseSize) ||
389 (Kcb->DelayedCloseIndex == 0));
390 }
391 }
392 }
393 }
394
395 /* Increase the reference count */
396 if ((InterlockedIncrement((PLONG)&Kcb->RefCount) & 0xFFFF) == 0)
397 {
398 /* We've overflown to 64K references, bail out */
399 InterlockedDecrement((PLONG)&Kcb->RefCount);
400 return FALSE;
401 }
402
403 /* Check if this was the last close index */
404 if (!Kcb->DelayedCloseIndex)
405 {
406 /* Check if the KCB is locked in shared mode */
407 if (!CmpIsKcbLockedExclusive(Kcb))
408 {
409 /* Convert it to exclusive */
410 if (!CmpTryToConvertKcbSharedToExclusive(Kcb))
411 {
412 /* Go from shared to exclusive */
413 CmpConvertKcbSharedToExclusive(Kcb);
414 }
415 }
416
417 /* If we're still the last entry, remove us */
418 if (!Kcb->DelayedCloseIndex) CmpRemoveFromDelayedClose(Kcb);
419 }
420
421 /* Return success */
422 return TRUE;
423 }
424
425 VOID
426 NTAPI
427 CmpCleanUpKcbValueCache(IN PCM_KEY_CONTROL_BLOCK Kcb)
428 {
429 PULONG_PTR CachedList;
430 ULONG i;
431
432 /* Sanity check */
433 ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
434 (CmpTestRegistryLockExclusive() == TRUE));
435
436 /* Check if the value list is cached */
437 if (CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList))
438 {
439 /* Get the cache list */
440 CachedList = (PULONG_PTR)CMP_GET_CACHED_DATA(Kcb->ValueCache.ValueList);
441 for (i = 0; i < Kcb->ValueCache.Count; i++)
442 {
443 /* Check if this cell is cached */
444 if (CMP_IS_CELL_CACHED(CachedList[i]))
445 {
446 /* Free it */
447 CmpFree((PVOID)CMP_GET_CACHED_CELL(CachedList[i]), 0);
448 }
449 }
450
451 /* Now free the list */
452 CmpFree((PVOID)CMP_GET_CACHED_CELL(Kcb->ValueCache.ValueList), 0);
453 Kcb->ValueCache.ValueList = HCELL_NIL;
454 }
455 else if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)
456 {
457 /* This is a sym link, check if there's only one reference left */
458 if ((Kcb->ValueCache.RealKcb->RefCount == 1) &&
459 !(Kcb->ValueCache.RealKcb->Delete))
460 {
461 /* Disable delay close for the KCB */
462 Kcb->ValueCache.RealKcb->ExtFlags |= CM_KCB_NO_DELAY_CLOSE;
463 }
464
465 /* Dereference the KCB */
466 CmpDelayDerefKeyControlBlock(Kcb->ValueCache.RealKcb);
467 Kcb->ExtFlags &= ~CM_KCB_SYM_LINK_FOUND;
468 }
469 }
470
471 VOID
472 NTAPI
473 CmpCleanUpKcbCacheWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb,
474 IN BOOLEAN LockHeldExclusively)
475 {
476 PCM_KEY_CONTROL_BLOCK Parent;
477 PAGED_CODE();
478
479 /* Sanity checks */
480 ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
481 (CmpTestRegistryLockExclusive() == TRUE));
482 ASSERT(Kcb->RefCount == 0);
483
484 /* Cleanup the value cache */
485 CmpCleanUpKcbValueCache(Kcb);
486
487 /* Dereference the NCB */
488 CmpDereferenceNameControlBlockWithLock(Kcb->NameBlock);
489
490 /* Check if we have an index hint block and free it */
491 if (Kcb->ExtFlags & CM_KCB_SUBKEY_HINT) CmpFree(Kcb->IndexHint, 0);
492
493 /* Check if we were already deleted */
494 Parent = Kcb->ParentKcb;
495 if (!Kcb->Delete) CmpRemoveKeyControlBlock(Kcb);
496
497 /* Set invalid KCB signature */
498 Kcb->Signature = CM_KCB_INVALID_SIGNATURE;
499
500 /* Free the KCB as well */
501 CmpFreeKeyControlBlock(Kcb);
502
503 /* Check if we have a parent */
504 if (Parent)
505 {
506 /* Dereference the parent */
507 LockHeldExclusively ?
508 CmpDereferenceKeyControlBlockWithLock(Parent,LockHeldExclusively) :
509 CmpDelayDerefKeyControlBlock(Parent);
510 }
511 }
512
513 VOID
514 NTAPI
515 CmpCleanUpSubKeyInfo(IN PCM_KEY_CONTROL_BLOCK Kcb)
516 {
517 PCM_KEY_NODE KeyNode;
518
519 /* Sanity check */
520 ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
521 (CmpTestRegistryLockExclusive() == TRUE));
522
523 /* Check if there's any cached subkey */
524 if (Kcb->ExtFlags & (CM_KCB_NO_SUBKEY | CM_KCB_SUBKEY_ONE | CM_KCB_SUBKEY_HINT))
525 {
526 /* Check if there's a hint */
527 if (Kcb->ExtFlags & (CM_KCB_SUBKEY_HINT))
528 {
529 /* Kill it */
530 CmpFree(Kcb->IndexHint, 0);
531 }
532
533 /* Remove subkey flags */
534 Kcb->ExtFlags &= ~(CM_KCB_NO_SUBKEY | CM_KCB_SUBKEY_ONE | CM_KCB_SUBKEY_HINT);
535 }
536
537 /* Check if there's no linked cell */
538 if (Kcb->KeyCell == HCELL_NIL)
539 {
540 /* Make sure it's a delete */
541 ASSERT(Kcb->Delete);
542 KeyNode = NULL;
543 }
544 else
545 {
546 /* Get the key node */
547 KeyNode = (PCM_KEY_NODE)HvGetCell(Kcb->KeyHive, Kcb->KeyCell);
548 }
549
550 /* Check if we got the node */
551 if (!KeyNode)
552 {
553 /* We didn't, mark the cached data invalid */
554 Kcb->ExtFlags |= CM_KCB_INVALID_CACHED_INFO;
555 }
556 else
557 {
558 /* We have a keynode, update subkey counts */
559 Kcb->ExtFlags &= ~CM_KCB_INVALID_CACHED_INFO;
560 Kcb->SubKeyCount = KeyNode->SubKeyCounts[Stable] +
561 KeyNode->SubKeyCounts[Volatile];
562
563 /* Release the cell */
564 HvReleaseCell(Kcb->KeyHive, Kcb->KeyCell);
565 }
566 }
567
568 VOID
569 NTAPI
570 CmpDereferenceKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)
571 {
572 LONG OldRefCount, NewRefCount;
573 ULONG ConvKey;
574 CMTRACE(CM_REFERENCE_DEBUG,
575 "%s - Dereferencing KCB: %p\n", __FUNCTION__, Kcb);
576
577 /* Get the ref count and update it */
578 OldRefCount = *(PLONG)&Kcb->RefCount;
579 NewRefCount = OldRefCount - 1;
580
581 /* Check if we still have references */
582 if( (NewRefCount & 0xFFFF) > 0)
583 {
584 /* Do the dereference */
585 if (InterlockedCompareExchange((PLONG)&Kcb->RefCount,
586 NewRefCount,
587 OldRefCount) == OldRefCount)
588 {
589 /* We'de done */
590 return;
591 }
592 }
593
594 /* Save the key */
595 ConvKey = Kcb->ConvKey;
596
597 /* Do the dereference inside the lock */
598 CmpAcquireKcbLockExclusive(Kcb);
599 CmpDereferenceKeyControlBlockWithLock(Kcb, FALSE);
600 CmpReleaseKcbLockByKey(ConvKey);
601 }
602
603 VOID
604 NTAPI
605 CmpDereferenceKeyControlBlockWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb,
606 IN BOOLEAN LockHeldExclusively)
607 {
608 CMTRACE(CM_REFERENCE_DEBUG,
609 "%s - Dereferencing KCB: %p\n", __FUNCTION__, Kcb);
610
611 /* Sanity check */
612 ASSERT_KCB_VALID(Kcb);
613
614 /* Check if this is the last reference */
615 if ((InterlockedDecrement((PLONG)&Kcb->RefCount) & 0xFFFF) == 0)
616 {
617 /* Sanity check */
618 ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
619 (CmpTestRegistryLockExclusive() == TRUE));
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 NameLength, i;
901 PCM_KEY_CONTROL_BLOCK MyKcb;
902 PCM_KEY_NODE KeyNode;
903 BOOLEAN DeletedKey = FALSE;
904 PWCHAR TargetBuffer, CurrentNameW;
905 PUCHAR CurrentName;
906
907 /* Calculate how much size our key name is going to occupy */
908 NameLength = 0;
909 MyKcb = Kcb;
910
911 while (MyKcb)
912 {
913 /* Add length of the name */
914 if (!MyKcb->NameBlock->Compressed)
915 {
916 NameLength += MyKcb->NameBlock->NameLength;
917 }
918 else
919 {
920 NameLength += CmpCompressedNameSize(MyKcb->NameBlock->Name,
921 MyKcb->NameBlock->NameLength);
922 }
923
924 /* Sum up the separator too */
925 NameLength += sizeof(WCHAR);
926
927 /* Go to the parent KCB */
928 MyKcb = MyKcb->ParentKcb;
929 }
930
931 /* Allocate the unicode string now */
932 KeyName = CmpAllocate(NameLength + sizeof(UNICODE_STRING),
933 TRUE,
934 TAG_CM);
935
936 if (!KeyName) return NULL;
937
938 /* Set it up */
939 KeyName->Buffer = (PWSTR)(KeyName + 1);
940 KeyName->Length = NameLength;
941 KeyName->MaximumLength = NameLength;
942
943 /* Loop the keys again, now adding names */
944 NameLength = 0;
945 MyKcb = Kcb;
946
947 while (MyKcb)
948 {
949 /* Sanity checks for deleted and fake keys */
950 if ((!MyKcb->KeyCell && !MyKcb->Delete) ||
951 !MyKcb->KeyHive ||
952 MyKcb->ExtFlags & CM_KCB_KEY_NON_EXIST)
953 {
954 /* Failure */
955 CmpFree(KeyName, 0);
956 return NULL;
957 }
958
959 /* Try to get the name from the keynode,
960 if the key is not deleted */
961 if (!DeletedKey && !MyKcb->Delete)
962 {
963 KeyNode = HvGetCell(MyKcb->KeyHive, MyKcb->KeyCell);
964
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
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 ASSERT((CmpIsKcbLockedExclusive(KeyBody->KeyControlBlock) == TRUE) ||
1084 (CmpTestRegistryLockExclusive() == TRUE));
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 ASSERT((CmpIsKcbLockedExclusive(KeyBody->KeyControlBlock) == TRUE) ||
1130 (CmpTestRegistryLockExclusive() == TRUE));
1131
1132 /* Remove the entry */
1133 RemoveEntryList(&KeyBody->KeyBodyList);
1134
1135 /* Unlock it it if we did a manual lock */
1136 if (!LockHeld) CmpReleaseKcbLock(KeyBody->KeyControlBlock);
1137 }