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