1c093403c480b9ea46fd96b6d52ffb65582179a3
[reactos.git] / 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((PULONG_PTR)&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((PULONG_PTR)&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 Length, NcbSize;
157
158 /* Loop the name */
159 p = NodeName->Buffer;
160 for (i = 0; i < NodeName->Length; i += sizeof(WCHAR))
161 {
162 /* Make sure it's not a slash */
163 if (*p != OBJ_NAME_PATH_SEPARATOR)
164 {
165 /* Add it to the hash */
166 ConvKey = 37 * ConvKey + RtlUpcaseUnicodeChar(*p);
167 }
168
169 /* Next character */
170 p++;
171 }
172
173 /* Set assumed lengh and loop to check */
174 Length = NodeName->Length / sizeof(WCHAR);
175 for (i = 0; i < (NodeName->Length / sizeof(WCHAR)); i++)
176 {
177 /* Check if this is a 16-bit character */
178 if (NodeName->Buffer[i] > (UCHAR)-1)
179 {
180 /* This is the actual size, and we know we're not compressed */
181 Length = NodeName->Length;
182 IsCompressed = FALSE;
183 break;
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 ASSERT(Ncb->RefCount != 0xFFFF);
239 Ncb->RefCount++;
240 break;
241 }
242 }
243
244 /* Go to the next hash */
245 HashEntry = HashEntry->NextHash;
246 }
247
248 /* Check if we didn't find it */
249 if (!Found)
250 {
251 /* Allocate one */
252 NcbSize = FIELD_OFFSET(CM_NAME_CONTROL_BLOCK, Name) + Length;
253 Ncb = CmpAllocate(NcbSize, TRUE, TAG_CM);
254 if (!Ncb)
255 {
256 /* Release the lock and fail */
257 CmpReleaseNcbLockByKey(ConvKey);
258 return NULL;
259 }
260
261 /* Clear it out */
262 RtlZeroMemory(Ncb, NcbSize);
263
264 /* Check if the name was compressed */
265 if (IsCompressed)
266 {
267 /* Copy the compressed name */
268 for (i = 0; i < NodeName->Length / sizeof(WCHAR); i++)
269 {
270 /* Copy Unicode to ANSI */
271 ((PCHAR)Ncb->Name)[i] = (CHAR)RtlUpcaseUnicodeChar(NodeName->Buffer[i]);
272 }
273 }
274 else
275 {
276 /* Copy the name directly */
277 for (i = 0; i < NodeName->Length / sizeof(WCHAR); 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->Compressed = IsCompressed;
286 Ncb->ConvKey = ConvKey;
287 Ncb->RefCount++;
288 Ncb->NameLength = Length;
289
290 /* Insert the name in the hash table */
291 HashEntry = &Ncb->NameHash;
292 HashEntry->NextHash = GET_HASH_ENTRY(CmpNameCacheTable, ConvKey).Entry;
293 GET_HASH_ENTRY(CmpNameCacheTable, ConvKey).Entry = HashEntry;
294 }
295
296 /* Release NCB lock */
297 CmpReleaseNcbLockByKey(ConvKey);
298
299 /* Return the NCB found */
300 return Ncb;
301 }
302
303 VOID
304 NTAPI
305 CmpRemoveKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)
306 {
307 /* Make sure that the registry and KCB are utterly locked */
308 ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
309 (CmpTestRegistryLockExclusive() == TRUE));
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 /* Sanity check */
437 ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
438 (CmpTestRegistryLockExclusive() == TRUE));
439
440 /* Check if the value list is cached */
441 if (CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList))
442 {
443 /* Get the cache list */
444 CachedList = (PULONG_PTR)CMP_GET_CACHED_DATA(Kcb->ValueCache.ValueList);
445 for (i = 0; i < Kcb->ValueCache.Count; i++)
446 {
447 /* Check if this cell is cached */
448 if (CMP_IS_CELL_CACHED(CachedList[i]))
449 {
450 /* Free it */
451 CmpFree((PVOID)CMP_GET_CACHED_CELL(CachedList[i]), 0);
452 }
453 }
454
455 /* Now free the list */
456 CmpFree((PVOID)CMP_GET_CACHED_CELL(Kcb->ValueCache.ValueList), 0);
457 Kcb->ValueCache.ValueList = HCELL_NIL;
458 }
459 else if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)
460 {
461 /* This is a sym link, check if there's only one reference left */
462 if ((Kcb->ValueCache.RealKcb->RefCount == 1) &&
463 !(Kcb->ValueCache.RealKcb->Delete))
464 {
465 /* Disable delay close for the KCB */
466 Kcb->ValueCache.RealKcb->ExtFlags |= CM_KCB_NO_DELAY_CLOSE;
467 }
468
469 /* Dereference the KCB */
470 CmpDelayDerefKeyControlBlock(Kcb->ValueCache.RealKcb);
471 Kcb->ExtFlags &= ~CM_KCB_SYM_LINK_FOUND;
472 }
473 }
474
475 VOID
476 NTAPI
477 CmpCleanUpKcbCacheWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb,
478 IN BOOLEAN LockHeldExclusively)
479 {
480 PCM_KEY_CONTROL_BLOCK Parent;
481 PAGED_CODE();
482
483 /* Sanity checks */
484 ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
485 (CmpTestRegistryLockExclusive() == TRUE));
486 ASSERT(Kcb->RefCount == 0);
487
488 /* Cleanup the value cache */
489 CmpCleanUpKcbValueCache(Kcb);
490
491 /* Dereference the NCB */
492 CmpDereferenceNameControlBlockWithLock(Kcb->NameBlock);
493
494 /* Check if we have an index hint block and free it */
495 if (Kcb->ExtFlags & CM_KCB_SUBKEY_HINT) CmpFree(Kcb->IndexHint, 0);
496
497 /* Check if we were already deleted */
498 Parent = Kcb->ParentKcb;
499 if (!Kcb->Delete) CmpRemoveKeyControlBlock(Kcb);
500
501 /* Set invalid KCB signature */
502 Kcb->Signature = CM_KCB_INVALID_SIGNATURE;
503
504 /* Free the KCB as well */
505 CmpFreeKeyControlBlock(Kcb);
506
507 /* Check if we have a parent */
508 if (Parent)
509 {
510 /* Dereference the parent */
511 LockHeldExclusively ?
512 CmpDereferenceKeyControlBlockWithLock(Parent,LockHeldExclusively) :
513 CmpDelayDerefKeyControlBlock(Parent);
514 }
515 }
516
517 VOID
518 NTAPI
519 CmpCleanUpSubKeyInfo(IN PCM_KEY_CONTROL_BLOCK Kcb)
520 {
521 PCM_KEY_NODE KeyNode;
522
523 /* Sanity check */
524 ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
525 (CmpTestRegistryLockExclusive() == TRUE));
526
527 /* Check if there's any cached subkey */
528 if (Kcb->ExtFlags & (CM_KCB_NO_SUBKEY | CM_KCB_SUBKEY_ONE | CM_KCB_SUBKEY_HINT))
529 {
530 /* Check if there's a hint */
531 if (Kcb->ExtFlags & (CM_KCB_SUBKEY_HINT))
532 {
533 /* Kill it */
534 CmpFree(Kcb->IndexHint, 0);
535 }
536
537 /* Remove subkey flags */
538 Kcb->ExtFlags &= ~(CM_KCB_NO_SUBKEY | CM_KCB_SUBKEY_ONE | CM_KCB_SUBKEY_HINT);
539 }
540
541 /* Check if there's no linked cell */
542 if (Kcb->KeyCell == HCELL_NIL)
543 {
544 /* Make sure it's a delete */
545 ASSERT(Kcb->Delete);
546 KeyNode = NULL;
547 }
548 else
549 {
550 /* Get the key node */
551 KeyNode = (PCM_KEY_NODE)HvGetCell(Kcb->KeyHive, Kcb->KeyCell);
552 }
553
554 /* Check if we got the node */
555 if (!KeyNode)
556 {
557 /* We didn't, mark the cached data invalid */
558 Kcb->ExtFlags |= CM_KCB_INVALID_CACHED_INFO;
559 }
560 else
561 {
562 /* We have a keynode, update subkey counts */
563 Kcb->ExtFlags &= ~CM_KCB_INVALID_CACHED_INFO;
564 Kcb->SubKeyCount = KeyNode->SubKeyCounts[Stable] +
565 KeyNode->SubKeyCounts[Volatile];
566
567 /* Release the cell */
568 HvReleaseCell(Kcb->KeyHive, Kcb->KeyCell);
569 }
570 }
571
572 VOID
573 NTAPI
574 CmpDereferenceKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)
575 {
576 LONG OldRefCount, NewRefCount;
577 ULONG ConvKey;
578 CMTRACE(CM_REFERENCE_DEBUG,
579 "%s - Dereferencing KCB: %p\n", __FUNCTION__, Kcb);
580
581 /* Get the ref count and update it */
582 OldRefCount = *(PLONG)&Kcb->RefCount;
583 NewRefCount = OldRefCount - 1;
584
585 /* Check if we still have references */
586 if ((NewRefCount & 0xFFFF) > 0)
587 {
588 /* Do the dereference */
589 if (InterlockedCompareExchange((PLONG)&Kcb->RefCount,
590 NewRefCount,
591 OldRefCount) == OldRefCount)
592 {
593 /* We'de done */
594 return;
595 }
596 }
597
598 /* Save the key */
599 ConvKey = Kcb->ConvKey;
600
601 /* Do the dereference inside the lock */
602 CmpAcquireKcbLockExclusive(Kcb);
603 CmpDereferenceKeyControlBlockWithLock(Kcb, FALSE);
604 CmpReleaseKcbLockByKey(ConvKey);
605 }
606
607 VOID
608 NTAPI
609 CmpDereferenceKeyControlBlockWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb,
610 IN BOOLEAN LockHeldExclusively)
611 {
612 CMTRACE(CM_REFERENCE_DEBUG,
613 "%s - Dereferencing KCB: %p\n", __FUNCTION__, Kcb);
614
615 /* Sanity check */
616 ASSERT_KCB_VALID(Kcb);
617
618 /* Check if this is the last reference */
619 if ((InterlockedDecrement((PLONG)&Kcb->RefCount) & 0xFFFF) == 0)
620 {
621 /* Sanity check */
622 ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
623 (CmpTestRegistryLockExclusive() == TRUE));
624
625 /* Check if we should do a direct delete */
626 if (((CmpHoldLazyFlush) &&
627 !(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND) &&
628 !(Kcb->Flags & KEY_SYM_LINK)) ||
629 (Kcb->ExtFlags & CM_KCB_NO_DELAY_CLOSE) ||
630 (Kcb->Delete))
631 {
632 /* Clean up the KCB*/
633 CmpCleanUpKcbCacheWithLock(Kcb, LockHeldExclusively);
634 }
635 else
636 {
637 /* Otherwise, use delayed close */
638 CmpAddToDelayedClose(Kcb, LockHeldExclusively);
639 }
640 }
641 }
642
643 VOID
644 NTAPI
645 InitializeKCBKeyBodyList(IN PCM_KEY_CONTROL_BLOCK Kcb)
646 {
647 /* Initialize the list */
648 InitializeListHead(&Kcb->KeyBodyListHead);
649
650 /* Clear the bodies */
651 Kcb->KeyBodyArray[0] =
652 Kcb->KeyBodyArray[1] =
653 Kcb->KeyBodyArray[2] =
654 Kcb->KeyBodyArray[3] = NULL;
655 }
656
657 PCM_KEY_CONTROL_BLOCK
658 NTAPI
659 CmpCreateKeyControlBlock(IN PHHIVE Hive,
660 IN HCELL_INDEX Index,
661 IN PCM_KEY_NODE Node,
662 IN PCM_KEY_CONTROL_BLOCK Parent,
663 IN ULONG Flags,
664 IN PUNICODE_STRING KeyName)
665 {
666 PCM_KEY_CONTROL_BLOCK Kcb, FoundKcb = NULL;
667 UNICODE_STRING NodeName;
668 ULONG ConvKey = 0, i;
669 BOOLEAN IsFake, HashLock;
670 PWCHAR p;
671
672 /* Make sure we own this hive in case it's being unloaded */
673 if ((Hive->HiveFlags & HIVE_IS_UNLOADING) &&
674 (((PCMHIVE)Hive)->CreatorOwner != KeGetCurrentThread()))
675 {
676 /* Fail */
677 return NULL;
678 }
679
680 /* Check if this is a fake KCB */
681 IsFake = Flags & CMP_CREATE_FAKE_KCB ? TRUE : FALSE;
682
683 /* If we have a parent, use its ConvKey */
684 if (Parent) ConvKey = Parent->ConvKey;
685
686 /* Make a copy of the name */
687 NodeName = *KeyName;
688
689 /* Remove leading slash */
690 while ((NodeName.Length) && (*NodeName.Buffer == OBJ_NAME_PATH_SEPARATOR))
691 {
692 /* Move the buffer by one */
693 NodeName.Buffer++;
694 NodeName.Length -= sizeof(WCHAR);
695 }
696
697 /* Make sure we didn't get just a slash or something */
698 ASSERT(NodeName.Length > 0);
699
700 /* Now setup the hash */
701 p = NodeName.Buffer;
702 for (i = 0; i < NodeName.Length; i += sizeof(WCHAR))
703 {
704 /* Make sure it's a valid character */
705 if (*p != OBJ_NAME_PATH_SEPARATOR)
706 {
707 /* Add this key to the hash */
708 ConvKey = 37 * ConvKey + RtlUpcaseUnicodeChar(*p);
709 }
710
711 /* Move on */
712 p++;
713 }
714
715 /* Allocate the KCB */
716 Kcb = CmpAllocateKeyControlBlock();
717 if (!Kcb) return NULL;
718
719 /* Initailize the key list */
720 InitializeKCBKeyBodyList(Kcb);
721
722 /* Set it up */
723 Kcb->Signature = CM_KCB_SIGNATURE;
724 Kcb->Delete = FALSE;
725 Kcb->RefCount = 1;
726 Kcb->KeyHive = Hive;
727 Kcb->KeyCell = Index;
728 Kcb->ConvKey = ConvKey;
729 Kcb->DelayedCloseIndex = CmpDelayedCloseSize;
730 Kcb->InDelayClose = 0;
731 ASSERT_KCB_VALID(Kcb);
732
733 /* Check if we have two hash entires */
734 HashLock = Flags & CMP_LOCK_HASHES_FOR_KCB ? TRUE : FALSE;
735 if (!HashLock)
736 {
737 /* It's not locked, do we have a parent? */
738 if (Parent)
739 {
740 /* Lock the parent KCB and ourselves */
741 CmpAcquireTwoKcbLocksExclusiveByKey(ConvKey, Parent->ConvKey);
742 }
743 else
744 {
745 /* Lock only ourselves */
746 CmpAcquireKcbLockExclusive(Kcb);
747 }
748 }
749
750 /* Check if we already have a KCB */
751 FoundKcb = CmpInsertKeyHash(&Kcb->KeyHash, IsFake);
752 if (FoundKcb)
753 {
754 /* Sanity check */
755 ASSERT(!FoundKcb->Delete);
756 Kcb->Signature = CM_KCB_INVALID_SIGNATURE;
757
758 /* Free the one we allocated and reference this one */
759 CmpFreeKeyControlBlock(Kcb);
760 ASSERT_KCB_VALID(FoundKcb);
761 Kcb = FoundKcb;
762 if (!CmpReferenceKeyControlBlock(Kcb))
763 {
764 /* We got too many handles */
765 ASSERT(Kcb->RefCount + 1 != 0);
766 Kcb = NULL;
767 }
768 else
769 {
770 /* Check if we're not creating a fake one, but it used to be fake */
771 if ((Kcb->ExtFlags & CM_KCB_KEY_NON_EXIST) && !(IsFake))
772 {
773 /* Set the hive and cell */
774 Kcb->KeyHive = Hive;
775 Kcb->KeyCell = Index;
776
777 /* This means that our current information is invalid */
778 Kcb->ExtFlags = CM_KCB_INVALID_CACHED_INFO;
779 }
780
781 /* Check if we didn't have any valid data */
782 if (!(Kcb->ExtFlags & (CM_KCB_NO_SUBKEY |
783 CM_KCB_SUBKEY_ONE |
784 CM_KCB_SUBKEY_HINT)))
785 {
786 /* Calculate the index hint */
787 Kcb->SubKeyCount = Node->SubKeyCounts[Stable] +
788 Node->SubKeyCounts[Volatile];
789
790 /* Cached information is now valid */
791 Kcb->ExtFlags &= ~CM_KCB_INVALID_CACHED_INFO;
792 }
793
794 /* Setup the other data */
795 Kcb->KcbLastWriteTime = Node->LastWriteTime;
796 Kcb->KcbMaxNameLen = (USHORT)Node->MaxNameLen;
797 Kcb->KcbMaxValueNameLen = (USHORT)Node->MaxValueNameLen;
798 Kcb->KcbMaxValueDataLen = Node->MaxValueDataLen;
799 }
800 }
801 else
802 {
803 /* No KCB, do we have a parent? */
804 if (Parent)
805 {
806 /* Reference the parent */
807 if (((Parent->TotalLevels + 1) < 512) &&
808 (CmpReferenceKeyControlBlock(Parent)))
809 {
810 /* Link it */
811 Kcb->ParentKcb = Parent;
812 Kcb->TotalLevels = Parent->TotalLevels + 1;
813 }
814 else
815 {
816 /* Remove the KCB and free it */
817 CmpRemoveKeyControlBlock(Kcb);
818 Kcb->Signature = CM_KCB_INVALID_SIGNATURE;
819 CmpFreeKeyControlBlock(Kcb);
820 Kcb = NULL;
821 }
822 }
823 else
824 {
825 /* No parent, this is the root node */
826 Kcb->ParentKcb = NULL;
827 Kcb->TotalLevels = 1;
828 }
829
830 /* Check if we have a KCB */
831 if (Kcb)
832 {
833 /* Get the NCB */
834 Kcb->NameBlock = CmpGetNameControlBlock(&NodeName);
835 if (Kcb->NameBlock)
836 {
837 /* Fill it out */
838 Kcb->ValueCache.Count = Node->ValueList.Count;
839 Kcb->ValueCache.ValueList = Node->ValueList.List;
840 Kcb->Flags = Node->Flags;
841 Kcb->ExtFlags = 0;
842 Kcb->DelayedCloseIndex = CmpDelayedCloseSize;
843
844 /* Remember if this is a fake key */
845 if (IsFake) Kcb->ExtFlags |= CM_KCB_KEY_NON_EXIST;
846
847 /* Setup the other data */
848 Kcb->SubKeyCount = Node->SubKeyCounts[Stable] +
849 Node->SubKeyCounts[Volatile];
850 Kcb->KcbLastWriteTime = Node->LastWriteTime;
851 Kcb->KcbMaxNameLen = (USHORT)Node->MaxNameLen;
852 Kcb->KcbMaxValueNameLen = (USHORT)Node->MaxValueNameLen;
853 Kcb->KcbMaxValueDataLen = (USHORT)Node->MaxValueDataLen;
854 }
855 else
856 {
857 /* Dereference the KCB */
858 CmpDereferenceKeyControlBlockWithLock(Parent, FALSE);
859
860 /* Remove the KCB and free it */
861 CmpRemoveKeyControlBlock(Kcb);
862 Kcb->Signature = CM_KCB_INVALID_SIGNATURE;
863 CmpFreeKeyControlBlock(Kcb);
864 Kcb = NULL;
865 }
866 }
867 }
868
869 /* Check if this is a KCB inside a frozen hive */
870 if ((Kcb) && (((PCMHIVE)Hive)->Frozen) && (!(Kcb->Flags & KEY_SYM_LINK)))
871 {
872 /* Don't add these to the delay close */
873 Kcb->ExtFlags |= CM_KCB_NO_DELAY_CLOSE;
874 }
875
876 /* Sanity check */
877 ASSERT((!Kcb) || (Kcb->Delete == FALSE));
878
879 /* Check if we had locked the hashes */
880 if (!HashLock)
881 {
882 /* We locked them manually, do we have a parent? */
883 if (Parent)
884 {
885 /* Unlock the parent KCB and ourselves */
886 CmpReleaseTwoKcbLockByKey(ConvKey, Parent->ConvKey);
887 }
888 else
889 {
890 /* Unlock only ourselves */
891 CmpReleaseKcbLockByKey(ConvKey);
892 }
893 }
894
895 /* Return the KCB */
896 return Kcb;
897 }
898
899 PUNICODE_STRING
900 NTAPI
901 CmpConstructName(IN PCM_KEY_CONTROL_BLOCK Kcb)
902 {
903 PUNICODE_STRING KeyName;
904 ULONG NameLength, i;
905 PCM_KEY_CONTROL_BLOCK MyKcb;
906 PCM_KEY_NODE KeyNode;
907 BOOLEAN DeletedKey = FALSE;
908 PWCHAR TargetBuffer, CurrentNameW;
909 PUCHAR CurrentName;
910
911 /* Calculate how much size our key name is going to occupy */
912 NameLength = 0;
913 MyKcb = Kcb;
914
915 while (MyKcb)
916 {
917 /* Add length of the name */
918 if (!MyKcb->NameBlock->Compressed)
919 {
920 NameLength += MyKcb->NameBlock->NameLength;
921 }
922 else
923 {
924 NameLength += CmpCompressedNameSize(MyKcb->NameBlock->Name,
925 MyKcb->NameBlock->NameLength);
926 }
927
928 /* Sum up the separator too */
929 NameLength += sizeof(WCHAR);
930
931 /* Go to the parent KCB */
932 MyKcb = MyKcb->ParentKcb;
933 }
934
935 /* Allocate the unicode string now */
936 KeyName = CmpAllocate(NameLength + sizeof(UNICODE_STRING),
937 TRUE,
938 TAG_CM);
939
940 if (!KeyName) return NULL;
941
942 /* Set it up */
943 KeyName->Buffer = (PWSTR)(KeyName + 1);
944 KeyName->Length = NameLength;
945 KeyName->MaximumLength = NameLength;
946
947 /* Loop the keys again, now adding names */
948 NameLength = 0;
949 MyKcb = Kcb;
950
951 while (MyKcb)
952 {
953 /* Sanity checks for deleted and fake keys */
954 if ((!MyKcb->KeyCell && !MyKcb->Delete) ||
955 !MyKcb->KeyHive ||
956 MyKcb->ExtFlags & CM_KCB_KEY_NON_EXIST)
957 {
958 /* Failure */
959 CmpFree(KeyName, 0);
960 return NULL;
961 }
962
963 /* Try to get the name from the keynode,
964 if the key is not deleted */
965 if (!DeletedKey && !MyKcb->Delete)
966 {
967 KeyNode = HvGetCell(MyKcb->KeyHive, MyKcb->KeyCell);
968
969 if (!KeyNode)
970 {
971 /* Failure */
972 CmpFree(KeyName, 0);
973 return NULL;
974 }
975 }
976 else
977 {
978 /* The key was deleted */
979 KeyNode = NULL;
980 DeletedKey = TRUE;
981 }
982
983 /* Get the pointer to the beginning of the current key name */
984 NameLength += (MyKcb->NameBlock->NameLength + 1) * sizeof(WCHAR);
985 TargetBuffer = &KeyName->Buffer[(KeyName->Length - NameLength) / sizeof(WCHAR)];
986
987 /* Add a separator */
988 TargetBuffer[0] = OBJ_NAME_PATH_SEPARATOR;
989
990 /* Add the name, but remember to go from the end to the beginning */
991 if (!MyKcb->NameBlock->Compressed)
992 {
993 /* Get the pointer to the name (from the keynode, if possible) */
994 if ((MyKcb->Flags & (KEY_HIVE_ENTRY | KEY_HIVE_EXIT)) ||
995 !KeyNode)
996 {
997 CurrentNameW = MyKcb->NameBlock->Name;
998 }
999 else
1000 {
1001 CurrentNameW = KeyNode->Name;
1002 }
1003
1004 /* Copy the name */
1005 for (i=0; i < MyKcb->NameBlock->NameLength; i++)
1006 {
1007 TargetBuffer[i+1] = *CurrentNameW;
1008 CurrentNameW++;
1009 }
1010 }
1011 else
1012 {
1013 /* Get the pointer to the name (from the keynode, if possible) */
1014 if ((MyKcb->Flags & (KEY_HIVE_ENTRY | KEY_HIVE_EXIT)) ||
1015 !KeyNode)
1016 {
1017 CurrentName = (PUCHAR)MyKcb->NameBlock->Name;
1018 }
1019 else
1020 {
1021 CurrentName = (PUCHAR)KeyNode->Name;
1022 }
1023
1024 /* Copy the name */
1025 for (i=0; i < MyKcb->NameBlock->NameLength; i++)
1026 {
1027 TargetBuffer[i+1] = (WCHAR)*CurrentName;
1028 CurrentName++;
1029 }
1030 }
1031
1032 /* Release the cell, if needed */
1033 if (KeyNode) HvReleaseCell(MyKcb->KeyHive, MyKcb->KeyCell);
1034
1035 /* Go to the parent KCB */
1036 MyKcb = MyKcb->ParentKcb;
1037 }
1038
1039 /* Return resulting buffer (both UNICODE_STRING and
1040 its buffer following it) */
1041 return KeyName;
1042 }
1043
1044 VOID
1045 NTAPI
1046 EnlistKeyBodyWithKCB(IN PCM_KEY_BODY KeyBody,
1047 IN ULONG Flags)
1048 {
1049 ULONG i;
1050
1051 /* Sanity check */
1052 ASSERT(KeyBody->KeyControlBlock != NULL);
1053
1054 /* Initialize the list entry */
1055 InitializeListHead(&KeyBody->KeyBodyList);
1056
1057 /* Check if we can use the parent KCB array */
1058 for (i = 0; i < 4; i++)
1059 {
1060 /* Add it into the list */
1061 if (!InterlockedCompareExchangePointer((PVOID*)&KeyBody->KeyControlBlock->
1062 KeyBodyArray[i],
1063 KeyBody,
1064 NULL))
1065 {
1066 /* Added */
1067 return;
1068 }
1069 }
1070
1071 /* Array full, check if we need to unlock the KCB */
1072 if (Flags & CMP_ENLIST_KCB_LOCKED_SHARED)
1073 {
1074 /* It's shared, so release the KCB shared lock */
1075 CmpReleaseKcbLock(KeyBody->KeyControlBlock);
1076 ASSERT(!(Flags & CMP_ENLIST_KCB_LOCKED_EXCLUSIVE));
1077 }
1078
1079 /* Check if we need to lock the KCB */
1080 if (!(Flags & CMP_ENLIST_KCB_LOCKED_EXCLUSIVE))
1081 {
1082 /* Acquire the lock */
1083 CmpAcquireKcbLockExclusive(KeyBody->KeyControlBlock);
1084 }
1085
1086 /* Make sure we have the exclusive lock */
1087 ASSERT((CmpIsKcbLockedExclusive(KeyBody->KeyControlBlock) == TRUE) ||
1088 (CmpTestRegistryLockExclusive() == TRUE));
1089
1090 /* do the insert */
1091 InsertTailList(&KeyBody->KeyControlBlock->KeyBodyListHead,
1092 &KeyBody->KeyBodyList);
1093
1094 /* Check if we did a manual lock */
1095 if (!(Flags & (CMP_ENLIST_KCB_LOCKED_SHARED |
1096 CMP_ENLIST_KCB_LOCKED_EXCLUSIVE)))
1097 {
1098 /* Release the lock */
1099 CmpReleaseKcbLock(KeyBody->KeyControlBlock);
1100 }
1101 }
1102
1103 VOID
1104 NTAPI
1105 DelistKeyBodyFromKCB(IN PCM_KEY_BODY KeyBody,
1106 IN BOOLEAN LockHeld)
1107 {
1108 ULONG i;
1109
1110 /* Sanity check */
1111 ASSERT(KeyBody->KeyControlBlock != NULL);
1112
1113 /* Check if we can use the parent KCB array */
1114 for (i = 0; i < 4; i++)
1115 {
1116 /* Add it into the list */
1117 if (InterlockedCompareExchangePointer((VOID*)&KeyBody->KeyControlBlock->
1118 KeyBodyArray[i],
1119 NULL,
1120 KeyBody) == KeyBody)
1121 {
1122 /* Removed */
1123 return;
1124 }
1125 }
1126
1127 /* Sanity checks */
1128 ASSERT(IsListEmpty(&KeyBody->KeyControlBlock->KeyBodyListHead) == FALSE);
1129 ASSERT(IsListEmpty(&KeyBody->KeyBodyList) == FALSE);
1130
1131 /* Lock the KCB */
1132 if (!LockHeld) CmpAcquireKcbLockExclusive(KeyBody->KeyControlBlock);
1133 ASSERT((CmpIsKcbLockedExclusive(KeyBody->KeyControlBlock) == TRUE) ||
1134 (CmpTestRegistryLockExclusive() == TRUE));
1135
1136 /* Remove the entry */
1137 RemoveEntryList(&KeyBody->KeyBodyList);
1138
1139 /* Unlock it it if we did a manual lock */
1140 if (!LockHeld) CmpReleaseKcbLock(KeyBody->KeyControlBlock);
1141 }
1142
1143 VOID
1144 NTAPI
1145 CmpFlushNotifiesOnKeyBodyList(IN PCM_KEY_CONTROL_BLOCK Kcb,
1146 IN BOOLEAN LockHeld)
1147 {
1148 PLIST_ENTRY NextEntry, ListHead;
1149 PCM_KEY_BODY KeyBody;
1150
1151 /* Sanity check */
1152 LockHeld ? CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK() : CmpIsKcbLockedExclusive(Kcb);
1153 while (TRUE)
1154 {
1155 /* Is the list empty? */
1156 ListHead = &Kcb->KeyBodyListHead;
1157 if (!IsListEmpty(ListHead))
1158 {
1159 /* Loop the list */
1160 NextEntry = ListHead->Flink;
1161 while (NextEntry != ListHead)
1162 {
1163 /* Get the key body */
1164 KeyBody = CONTAINING_RECORD(NextEntry, CM_KEY_BODY, KeyBodyList);
1165 ASSERT(KeyBody->Type == '20yk');
1166
1167 /* Check for notifications */
1168 if (KeyBody->NotifyBlock)
1169 {
1170 /* Is the lock held? */
1171 if (LockHeld)
1172 {
1173 /* Flush it */
1174 CmpFlushNotify(KeyBody, LockHeld);
1175 ASSERT(KeyBody->NotifyBlock == NULL);
1176 continue;
1177 }
1178
1179 /* Lock isn't held, so we need to take a reference */
1180 if (ObReferenceObjectSafe(KeyBody))
1181 {
1182 /* Now we can flush */
1183 CmpFlushNotify(KeyBody, LockHeld);
1184 ASSERT(KeyBody->NotifyBlock == NULL);
1185
1186 /* Release the reference we took */
1187 ObDereferenceObjectDeferDelete(KeyBody);
1188 continue;
1189 }
1190 }
1191
1192 /* Try the next entry */
1193 NextEntry = NextEntry->Flink;
1194 }
1195 }
1196
1197 /* List has been parsed, exit */
1198 break;
1199 }
1200 }