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