- Implement NtCreateKey using the new parse routine.
[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
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
92 /* Check if it matches */
93 if (Current == KeyHash)
94 {
95 /* Then write the previous one */
96 *Prev = Current->NextHash;
97 break;
98 }
99
100 /* Otherwise, keep going */
101 Prev = &Current->NextHash;
102 }
103 }
104
105 PCM_KEY_CONTROL_BLOCK
106 NTAPI
107 CmpInsertKeyHash(IN PCM_KEY_HASH KeyHash,
108 IN BOOLEAN IsFake)
109 {
110 ULONG i;
111 PCM_KEY_HASH Entry;
112
113 /* Get the hash index */
114 i = GET_HASH_INDEX(KeyHash->ConvKey);
115
116 /* If this is a fake key, increase the key cell to use the parent data */
117 if (IsFake) KeyHash->KeyCell++;
118
119 /* Loop the hash table */
120 Entry = CmpCacheTable[i].Entry;
121 while (Entry)
122 {
123 /* Check if this matches */
124 if ((KeyHash->ConvKey == Entry->ConvKey) &&
125 (KeyHash->KeyCell == Entry->KeyCell) &&
126 (KeyHash->KeyHive == Entry->KeyHive))
127 {
128 /* Return it */
129 return CONTAINING_RECORD(Entry, CM_KEY_CONTROL_BLOCK, KeyHash);
130 }
131
132 /* Keep looping */
133 Entry = Entry->NextHash;
134 }
135
136 /* No entry found, add this one and return NULL since none existed */
137 KeyHash->NextHash = CmpCacheTable[i].Entry;
138 CmpCacheTable[i].Entry = KeyHash;
139 return NULL;
140 }
141
142 PCM_NAME_CONTROL_BLOCK
143 NTAPI
144 CmpGetNameControlBlock(IN PUNICODE_STRING NodeName)
145 {
146 PCM_NAME_CONTROL_BLOCK Ncb = NULL;
147 ULONG ConvKey = 0;
148 PWCHAR p, pp;
149 ULONG i;
150 BOOLEAN IsCompressed = TRUE, Found = FALSE;
151 PCM_NAME_HASH HashEntry;
152 ULONG Length, NcbSize;
153
154 /* Loop the name */
155 p = NodeName->Buffer;
156 for (i = 0; i < NodeName->Length; i += sizeof(WCHAR))
157 {
158 /* Make sure it's not a slash */
159 if (*p != OBJ_NAME_PATH_SEPARATOR)
160 {
161 /* Add it to the hash */
162 ConvKey = 37 * ConvKey + RtlUpcaseUnicodeChar(*p);
163 }
164
165 /* Next character */
166 p++;
167 }
168
169 /* Set assumed lengh and loop to check */
170 Length = NodeName->Length / sizeof(WCHAR);
171 for (i = 0; i < (NodeName->Length / sizeof(WCHAR)); i++)
172 {
173 /* Check if this is a valid character */
174 if (*NodeName->Buffer > (UCHAR)-1)
175 {
176 /* This is the actual size, and we know we're not compressed */
177 Length = NodeName->Length;
178 IsCompressed = FALSE;
179 }
180 }
181
182 /* Lock the NCB entry */
183 CmpAcquireNcbLockExclusiveByKey(ConvKey);
184
185 /* Get the hash entry */
186 HashEntry = GET_HASH_ENTRY(CmpNameCacheTable, ConvKey).Entry;
187 while (HashEntry)
188 {
189 /* Get the current NCB */
190 Ncb = CONTAINING_RECORD(HashEntry, CM_NAME_CONTROL_BLOCK, NameHash);
191
192 /* Check if the hash matches */
193 if ((ConvKey = HashEntry->ConvKey) && (Length = Ncb->NameLength))
194 {
195 /* Assume success */
196 Found = TRUE;
197
198 /* If the NCB is compressed, do a compressed name compare */
199 if (Ncb->Compressed)
200 {
201 /* Compare names */
202 if (CmpCompareCompressedName(NodeName, Ncb->Name, Length))
203 {
204 /* We failed */
205 Found = FALSE;
206 }
207 }
208 else
209 {
210 /* Do a manual compare */
211 p = NodeName->Buffer;
212 pp = Ncb->Name;
213 for (i = 0; i < Ncb->NameLength; i += sizeof(WCHAR))
214 {
215 /* Compare the character */
216 if (RtlUpcaseUnicodeChar(*p) != RtlUpcaseUnicodeChar(*pp))
217 {
218 /* Failed */
219 Found = FALSE;
220 break;
221 }
222
223 /* Next chars */
224 p++;
225 pp++;
226 }
227 }
228
229 /* Check if we found a name */
230 if (Found)
231 {
232 /* Reference it */
233 Ncb->RefCount++;
234 }
235 }
236
237 /* Go to the next hash */
238 HashEntry = HashEntry->NextHash;
239 }
240
241 /* Check if we didn't find it */
242 if (!Found)
243 {
244 /* Allocate one */
245 NcbSize = FIELD_OFFSET(CM_NAME_CONTROL_BLOCK, Name) + Length;
246 Ncb = ExAllocatePoolWithTag(PagedPool, NcbSize, TAG_CM);
247 if (!Ncb)
248 {
249 /* Release the lock and fail */
250 CmpReleaseNcbLockByKey(ConvKey);
251 return NULL;
252 }
253
254 /* Clear it out */
255 RtlZeroMemory(Ncb, NcbSize);
256
257 /* Check if the name was compressed */
258 if (IsCompressed)
259 {
260 /* Copy the compressed name */
261 Ncb->Compressed = TRUE;
262 for (i = 0; i < Length; i++)
263 {
264 /* Copy Unicode to ANSI */
265 ((PCHAR)Ncb->Name)[i] = (CHAR)RtlUpcaseUnicodeChar(NodeName->Buffer[i]);
266 }
267 }
268 else
269 {
270 /* Copy the name directly */
271 Ncb->Compressed = FALSE;
272 for (i = 0; i < Length; i++)
273 {
274 /* Copy each unicode character */
275 Ncb->Name[i] = RtlUpcaseUnicodeChar(NodeName->Buffer[i]);
276 }
277 }
278
279 /* Setup the rest of the NCB */
280 Ncb->ConvKey = ConvKey;
281 Ncb->RefCount++;
282 Ncb->NameLength = Length;
283
284 /* Insert the name in the hash table */
285 HashEntry = &Ncb->NameHash;
286 HashEntry->NextHash = GET_HASH_ENTRY(CmpNameCacheTable, ConvKey).Entry;
287 GET_HASH_ENTRY(CmpNameCacheTable, ConvKey).Entry = HashEntry;
288 }
289
290 /* Release NCB lock */
291 CmpReleaseNcbLockByKey(ConvKey);
292
293 /* Return the NCB found */
294 return Ncb;
295 }
296
297 VOID
298 NTAPI
299 CmpRemoveKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)
300 {
301 /* Make sure that the registry and KCB are utterly locked */
302 ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
303 (CmpTestRegistryLockExclusive() == TRUE));
304
305 /* Remove the key hash */
306 CmpRemoveKeyHash(&Kcb->KeyHash);
307 }
308
309 VOID
310 NTAPI
311 CmpDereferenceNameControlBlockWithLock(IN PCM_NAME_CONTROL_BLOCK Ncb)
312 {
313 PCM_NAME_HASH Current, *Next;
314 ULONG ConvKey = Ncb->ConvKey;
315
316 /* Lock the NCB */
317 CmpAcquireNcbLockExclusiveByKey(ConvKey);
318
319 /* Decrease the reference count */
320 if (!(--Ncb->RefCount))
321 {
322 /* Find the NCB in the table */
323 Next = &GET_HASH_ENTRY(CmpNameCacheTable, Ncb->ConvKey).Entry;
324 while (TRUE)
325 {
326 /* Check the current entry */
327 Current = *Next;
328 ASSERT(Current != NULL);
329 if (Current == &Ncb->NameHash)
330 {
331 /* Unlink it */
332 *Next = Current->NextHash;
333 break;
334 }
335
336 /* Get to the next one */
337 Next = &Current->NextHash;
338 }
339
340 /* Found it, now free it */
341 ExFreePool(Ncb);
342 }
343
344 /* Release the lock */
345 CmpReleaseNcbLockByKey(ConvKey);
346 }
347
348 BOOLEAN
349 NTAPI
350 CmpReferenceKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)
351 {
352 /* Check if this is the KCB's first reference */
353 if (Kcb->RefCount == 0)
354 {
355 /* Check if the KCB is locked in shared mode */
356 if (!CmpIsKcbLockedExclusive(Kcb))
357 {
358 /* Convert it to exclusive */
359 if (!CmpTryToConvertKcbSharedToExclusive(Kcb))
360 {
361 /* Set the delayed close index so that we can be ignored */
362 Kcb->DelayedCloseIndex = 1;
363
364 /* Increase the reference count while we release the lock */
365 InterlockedIncrement((PLONG)&Kcb->RefCount);
366
367 /* Go from shared to exclusive */
368 CmpConvertKcbSharedToExclusive(Kcb);
369
370 /* Decrement the reference count; the lock is now held again */
371 InterlockedDecrement((PLONG)&Kcb->RefCount);
372
373 /* Check if we still control the index */
374 if (Kcb->DelayedCloseIndex == 1)
375 {
376 /* Reset it */
377 Kcb->DelayedCloseIndex = 0;
378 }
379 else
380 {
381 /* Sanity check */
382 ASSERT((Kcb->DelayedCloseIndex == CmpDelayedCloseSize) ||
383 (Kcb->DelayedCloseIndex == 0));
384 }
385 }
386 }
387 }
388
389 /* Increase the reference count */
390 if ((InterlockedIncrement((PLONG)&Kcb->RefCount) & 0xFFFF) == 0)
391 {
392 /* We've overflown to 64K references, bail out */
393 InterlockedDecrement((PLONG)&Kcb->RefCount);
394 return FALSE;
395 }
396
397 /* Check if this was the last close index */
398 if (!Kcb->DelayedCloseIndex)
399 {
400 /* Check if the KCB is locked in shared mode */
401 if (!CmpIsKcbLockedExclusive(Kcb))
402 {
403 /* Convert it to exclusive */
404 if (!CmpTryToConvertKcbSharedToExclusive(Kcb))
405 {
406 /* Go from shared to exclusive */
407 CmpConvertKcbSharedToExclusive(Kcb);
408 }
409 }
410
411 /* If we're still the last entry, remove us */
412 if (!Kcb->DelayedCloseIndex) CmpRemoveFromDelayedClose(Kcb);
413 }
414
415 /* Return success */
416 return TRUE;
417 }
418
419 VOID
420 NTAPI
421 CmpCleanUpKcbValueCache(IN PCM_KEY_CONTROL_BLOCK Kcb)
422 {
423 PULONG_PTR CachedList;
424 ULONG i;
425
426 /* Sanity check */
427 ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
428 (CmpTestRegistryLockExclusive() == TRUE));
429
430 /* Check if the value list is cached */
431 if (CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList))
432 {
433 /* Get the cache list */
434 CachedList = (PULONG_PTR)CMP_GET_CACHED_DATA(Kcb->ValueCache.ValueList);
435 for (i = 0; i < Kcb->ValueCache.Count; i++)
436 {
437 /* Check if this cell is cached */
438 if (CMP_IS_CELL_CACHED(CachedList[i]))
439 {
440 /* Free it */
441 ExFreePool((PVOID)CMP_GET_CACHED_CELL(CachedList[i]));
442 }
443 }
444
445 /* Now free the list */
446 ExFreePool((PVOID)CMP_GET_CACHED_CELL(Kcb->ValueCache.ValueList));
447 Kcb->ValueCache.ValueList = HCELL_NIL;
448 }
449 else if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)
450 {
451 /* This is a sym link, check if there's only one reference left */
452 if ((((PCM_KEY_CONTROL_BLOCK)Kcb->ValueCache.RealKcb)->RefCount == 1) &&
453 !(((PCM_KEY_CONTROL_BLOCK)Kcb->ValueCache.RealKcb)->Delete))
454 {
455 /* Disable delay close for the KCB */
456 ((PCM_KEY_CONTROL_BLOCK)Kcb->ValueCache.RealKcb)->ExtFlags |= CM_KCB_NO_DELAY_CLOSE;
457 }
458
459 /* Dereference the KCB */
460 CmpDelayDerefKeyControlBlock((PCM_KEY_CONTROL_BLOCK)Kcb->ValueCache.RealKcb);
461 Kcb->ExtFlags &= ~CM_KCB_SYM_LINK_FOUND;
462 }
463 }
464
465 VOID
466 NTAPI
467 CmpCleanUpKcbCacheWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb,
468 IN BOOLEAN LockHeldExclusively)
469 {
470 PCM_KEY_CONTROL_BLOCK Parent;
471 PAGED_CODE();
472
473 /* Sanity checks */
474 ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
475 (CmpTestRegistryLockExclusive() == TRUE));
476 ASSERT(Kcb->RefCount == 0);
477
478 /* Cleanup the value cache */
479 CmpCleanUpKcbValueCache(Kcb);
480
481 /* Reference the NCB */
482 CmpDereferenceNameControlBlockWithLock(Kcb->NameBlock);
483
484 /* Check if we have an index hint block and free it */
485 if (Kcb->ExtFlags & CM_KCB_SUBKEY_HINT) ExFreePool(Kcb->IndexHint);
486
487 /* Check if we were already deleted */
488 Parent = Kcb->ParentKcb;
489 if (!Kcb->Delete) CmpRemoveKeyControlBlock(Kcb);
490
491 /* Free the KCB as well */
492 CmpFreeKeyControlBlock(Kcb);
493
494 /* Check if we have a parent */
495 if (Parent)
496 {
497 /* Dereference the parent */
498 LockHeldExclusively ?
499 CmpDereferenceKeyControlBlockWithLock(Kcb,LockHeldExclusively) :
500 CmpDelayDerefKeyControlBlock(Kcb);
501 }
502 }
503
504 VOID
505 NTAPI
506 CmpDereferenceKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)
507 {
508 LONG OldRefCount, NewRefCount;
509 ULONG ConvKey;
510
511 /* Get the ref count and update it */
512 OldRefCount = *(PLONG)&Kcb->RefCount;
513 NewRefCount = OldRefCount - 1;
514
515 /* Check if we still have references */
516 if( (NewRefCount & 0xFFFF) > 0)
517 {
518 /* Do the dereference */
519 if (InterlockedCompareExchange((PLONG)&Kcb->RefCount,
520 NewRefCount,
521 OldRefCount) == OldRefCount)
522 {
523 /* We'de done */
524 return;
525 }
526 }
527
528 /* Save the key */
529 ConvKey = Kcb->ConvKey;
530
531 /* Do the dereference inside the lock */
532 CmpAcquireKcbLockExclusive(Kcb);
533 CmpDereferenceKeyControlBlockWithLock(Kcb, FALSE);
534 CmpReleaseKcbLockByKey(ConvKey);
535 }
536
537 VOID
538 NTAPI
539 CmpDereferenceKeyControlBlockWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb,
540 IN BOOLEAN LockHeldExclusively)
541 {
542 /* Sanity check */
543 ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
544 (CmpTestRegistryLockExclusive() == TRUE));
545
546 /* Check if this is the last reference */
547 if ((InterlockedDecrement((PLONG)&Kcb->RefCount) & 0xFFFF) == 0)
548 {
549 /* Check if we should do a direct delete */
550 if (((CmpHoldLazyFlush) &&
551 !(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND) &&
552 !(Kcb->Flags & KEY_SYM_LINK)) ||
553 (Kcb->ExtFlags & CM_KCB_NO_DELAY_CLOSE) ||
554 (Kcb->Delete))
555 {
556 /* Clean up the KCB*/
557 CmpCleanUpKcbCacheWithLock(Kcb, LockHeldExclusively);
558 }
559 else
560 {
561 /* Otherwise, use delayed close */
562 CmpAddToDelayedClose(Kcb, LockHeldExclusively);
563 }
564 }
565 }
566
567 VOID
568 NTAPI
569 InitializeKCBKeyBodyList(IN PCM_KEY_CONTROL_BLOCK Kcb)
570 {
571 /* Initialize the list */
572 InitializeListHead(&Kcb->KeyBodyListHead);
573
574 /* Clear the bodies */
575 Kcb->KeyBodyArray[0] =
576 Kcb->KeyBodyArray[1] =
577 Kcb->KeyBodyArray[2] =
578 Kcb->KeyBodyArray[3] = NULL;
579 }
580
581 PCM_KEY_CONTROL_BLOCK
582 NTAPI
583 CmpCreateKeyControlBlock(IN PHHIVE Hive,
584 IN HCELL_INDEX Index,
585 IN PCM_KEY_NODE Node,
586 IN PCM_KEY_CONTROL_BLOCK Parent,
587 IN ULONG Flags,
588 IN PUNICODE_STRING KeyName)
589 {
590 PCM_KEY_CONTROL_BLOCK Kcb, FoundKcb = NULL;
591 UNICODE_STRING NodeName;
592 ULONG ConvKey = 0, i;
593 BOOLEAN IsFake, HashLock;
594 PWCHAR p;
595
596 /* Make sure we own this hive in case it's being unloaded */
597 if ((Hive->HiveFlags & HIVE_IS_UNLOADING) &&
598 (((PCMHIVE)Hive)->CreatorOwner != KeGetCurrentThread()))
599 {
600 /* Fail */
601 return NULL;
602 }
603
604 /* Check if this is a fake KCB */
605 IsFake = Flags & CMP_CREATE_FAKE_KCB ? TRUE : FALSE;
606
607 /* If we have a parent, use its ConvKey */
608 if (Parent) ConvKey = Parent->ConvKey;
609
610 /* Make a copy of the name */
611 NodeName = *KeyName;
612
613 /* Remove leading slash */
614 while ((NodeName.Length) && (*NodeName.Buffer == OBJ_NAME_PATH_SEPARATOR))
615 {
616 /* Move the buffer by one */
617 NodeName.Buffer++;
618 NodeName.Length -= sizeof(WCHAR);
619 }
620
621 /* Make sure we didn't get just a slash or something */
622 ASSERT(NodeName.Length > 0);
623
624 /* Now setup the hash */
625 p = NodeName.Buffer;
626 for (i = 0; i < NodeName.Length; i += sizeof(WCHAR))
627 {
628 /* Make sure it's a valid character */
629 if (*p != OBJ_NAME_PATH_SEPARATOR)
630 {
631 /* Add this key to the hash */
632 ConvKey = 37 * ConvKey + RtlUpcaseUnicodeChar(*p);
633 }
634
635 /* Move on */
636 p++;
637 }
638
639 /* Allocate the KCB */
640 Kcb = CmpAllocateKeyControlBlock();
641 if (!Kcb) return NULL;
642
643 /* Initailize the key list */
644 InitializeKCBKeyBodyList(Kcb);
645
646 /* Set it up */
647 Kcb->Delete = FALSE;
648 Kcb->RefCount = 1;
649 Kcb->KeyHive = Hive;
650 Kcb->KeyCell = Index;
651 Kcb->ConvKey = ConvKey;
652 Kcb->DelayedCloseIndex = CmpDelayedCloseSize;
653 Kcb->InDelayClose = 0;
654
655 /* Check if we have two hash entires */
656 HashLock = Flags & CMP_LOCK_HASHES_FOR_KCB ? TRUE : FALSE;
657 if (!HashLock)
658 {
659 /* It's not locked, do we have a parent? */
660 if (Parent)
661 {
662 /* Lock the parent KCB and ourselves */
663 CmpAcquireTwoKcbLocksExclusiveByKey(ConvKey, Parent->ConvKey);
664 }
665 else
666 {
667 /* Lock only ourselves */
668 CmpAcquireKcbLockExclusive(Kcb);
669 }
670 }
671
672 /* Check if we already have a KCB */
673 FoundKcb = CmpInsertKeyHash(&Kcb->KeyHash, IsFake);
674 if (FoundKcb)
675 {
676 /* Sanity check */
677 ASSERT(!FoundKcb->Delete);
678
679 /* Free the one we allocated and reference this one */
680 CmpFreeKeyControlBlock(Kcb);
681 Kcb = FoundKcb;
682 if (!CmpReferenceKeyControlBlock(Kcb))
683 {
684 /* We got too many handles */
685 ASSERT(Kcb->RefCount + 1 != 0);
686 Kcb = NULL;
687 }
688 else
689 {
690 /* Check if we're not creating a fake one, but it used to be fake */
691 if ((Kcb->ExtFlags & CM_KCB_KEY_NON_EXIST) && !(IsFake))
692 {
693 /* Set the hive and cell */
694 Kcb->KeyHive = Hive;
695 Kcb->KeyCell = Index;
696
697 /* This means that our current information is invalid */
698 Kcb->ExtFlags = CM_KCB_INVALID_CACHED_INFO;
699 }
700
701 /* Check if we didn't have any valid data */
702 if (!(Kcb->ExtFlags & (CM_KCB_NO_SUBKEY |
703 CM_KCB_SUBKEY_ONE |
704 CM_KCB_SUBKEY_HINT)))
705 {
706 /* Calculate the index hint */
707 Kcb->SubKeyCount = Node->SubKeyCounts[Stable] +
708 Node->SubKeyCounts[Volatile];
709
710 /* Cached information is now valid */
711 Kcb->ExtFlags &= ~CM_KCB_INVALID_CACHED_INFO;
712 }
713
714 /* Setup the other data */
715 Kcb->KcbLastWriteTime = Node->LastWriteTime;
716 Kcb->KcbMaxNameLen = (USHORT)Node->MaxNameLen;
717 Kcb->KcbMaxValueNameLen = (USHORT)Node->MaxValueNameLen;
718 Kcb->KcbMaxValueDataLen = Node->MaxValueDataLen;
719 }
720 }
721 else
722 {
723 /* No KCB, do we have a parent? */
724 if (Parent)
725 {
726 /* Reference the parent */
727 if (((Parent->TotalLevels + 1) < 512) &&
728 (CmpReferenceKeyControlBlock(Parent)))
729 {
730 /* Link it */
731 Kcb->ParentKcb = Parent;
732 Kcb->TotalLevels = Parent->TotalLevels + 1;
733 }
734 else
735 {
736 /* Remove the KCB and free it */
737 CmpRemoveKeyControlBlock(Kcb);
738 CmpFreeKeyControlBlock(Kcb);
739 Kcb = NULL;
740 }
741 }
742 else
743 {
744 /* No parent, this is the root node */
745 Kcb->ParentKcb = NULL;
746 Kcb->TotalLevels = 1;
747 }
748
749 /* Check if we have a KCB */
750 if (Kcb)
751 {
752 /* Get the NCB */
753 Kcb->NameBlock = CmpGetNameControlBlock(&NodeName);
754 if (Kcb->NameBlock)
755 {
756 /* Fill it out */
757 Kcb->ValueCache.Count = Node->ValueList.Count;
758 Kcb->ValueCache.ValueList = Node->ValueList.List;
759 Kcb->Flags = Node->Flags;
760 Kcb->ExtFlags = 0;
761 Kcb->DelayedCloseIndex = CmpDelayedCloseSize;
762
763 /* Remember if this is a fake key */
764 if (IsFake) Kcb->ExtFlags |= CM_KCB_KEY_NON_EXIST;
765
766 /* Setup the other data */
767 Kcb->SubKeyCount = Node->SubKeyCounts[Stable] +
768 Node->SubKeyCounts[Volatile];
769 Kcb->KcbLastWriteTime = Node->LastWriteTime;
770 Kcb->KcbMaxNameLen = (USHORT)Node->MaxNameLen;
771 Kcb->KcbMaxValueNameLen = (USHORT)Node->MaxValueNameLen;
772 Kcb->KcbMaxValueDataLen = (USHORT)Node->MaxValueDataLen;
773 }
774 else
775 {
776 /* Dereference the KCB */
777 CmpDereferenceKeyControlBlockWithLock(Parent, FALSE);
778
779 /* Remove the KCB and free it */
780 CmpRemoveKeyControlBlock(Kcb);
781 CmpFreeKeyControlBlock(Kcb);
782 Kcb = NULL;
783 }
784 }
785 }
786
787 /* Sanity check */
788 ASSERT((!Kcb) || (Kcb->Delete == FALSE));
789
790 /* Check if we had locked the hashes */
791 if (!HashLock)
792 {
793 /* We locked them manually, do we have a parent? */
794 if (Parent)
795 {
796 /* Unlock the parent KCB and ourselves */
797 CmpReleaseTwoKcbLockByKey(ConvKey, Parent->ConvKey);
798 }
799 else
800 {
801 /* Unlock only ourselves */
802 CmpReleaseKcbLockByKey(ConvKey);
803 }
804 }
805
806 /* Return the KCB */
807 return Kcb;
808 }
809
810 VOID
811 NTAPI
812 EnlistKeyBodyWithKCB(IN PCM_KEY_BODY KeyBody,
813 IN ULONG Flags)
814 {
815 /* Sanity check */
816 ASSERT(KeyBody->KeyControlBlock != NULL);
817
818 /* Initialize the list entry */
819 InitializeListHead(&KeyBody->KeyBodyList);
820
821 /* FIXME: Implement once we don't link parents to children anymore */
822 }
823
824