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