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