xref: /reactos/ntoskrnl/config/cmapi.c (revision c2c66aff)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/config/cmapi.c
5  * PURPOSE:         Configuration Manager - Internal Registry APIs
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  *                  Eric Kohl
8  *                  Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9  */
10 
11 /* INCLUDES ******************************************************************/
12 
13 #include "ntoskrnl.h"
14 #define NDEBUG
15 #include "debug.h"
16 
17 /* FUNCTIONS *****************************************************************/
18 
19 BOOLEAN
20 NTAPI
21 CmpIsHiveAlreadyLoaded(IN HANDLE KeyHandle,
22                        IN POBJECT_ATTRIBUTES SourceFile,
23                        OUT PCMHIVE *CmHive)
24 {
25     NTSTATUS Status;
26     PCM_KEY_BODY KeyBody;
27     PCMHIVE Hive;
28     BOOLEAN Loaded = FALSE;
29     PAGED_CODE();
30 
31     /* Sanity check */
32     CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK();
33 
34     /* Reference the handle */
35     Status = ObReferenceObjectByHandle(KeyHandle,
36                                        0,
37                                        CmpKeyObjectType,
38                                        KernelMode,
39                                        (PVOID)&KeyBody,
40                                        NULL);
41     if (!NT_SUCCESS(Status)) return Loaded;
42 
43     /* Don't touch deleted KCBs */
44     if (KeyBody->KeyControlBlock->Delete) return Loaded;
45 
46     Hive = CONTAINING_RECORD(KeyBody->KeyControlBlock->KeyHive, CMHIVE, Hive);
47 
48     /* Must be the root key */
49     if (!(KeyBody->KeyControlBlock->Flags & KEY_HIVE_ENTRY) ||
50         !(Hive->FileUserName.Buffer))
51     {
52         /* It isn't */
53         ObDereferenceObject(KeyBody);
54         return Loaded;
55     }
56 
57     /* Now compare the name of the file */
58     if (!RtlCompareUnicodeString(&Hive->FileUserName,
59                                  SourceFile->ObjectName,
60                                  TRUE))
61     {
62         /* Same file found */
63         Loaded = TRUE;
64         *CmHive = Hive;
65 
66         /* If the hive is frozen, not sure what to do */
67         if (Hive->Frozen)
68         {
69             /* FIXME: TODO */
70             DPRINT1("ERROR: Hive is frozen\n");
71             while (TRUE);
72         }
73      }
74 
75      /* Dereference and return result */
76      ObDereferenceObject(KeyBody);
77      return Loaded;
78  }
79 
80 BOOLEAN
81 NTAPI
82 CmpDoFlushAll(IN BOOLEAN ForceFlush)
83 {
84     PLIST_ENTRY NextEntry;
85     PCMHIVE Hive;
86     NTSTATUS Status;
87     BOOLEAN Result = TRUE;
88 
89     /* Make sure that the registry isn't read-only now */
90     if (CmpNoWrite) return TRUE;
91 
92     /* Otherwise, acquire the hive list lock and disable force flush */
93     CmpForceForceFlush = FALSE;
94     ExAcquirePushLockShared(&CmpHiveListHeadLock);
95 
96     /* Loop the hive list */
97     NextEntry = CmpHiveListHead.Flink;
98     while (NextEntry != &CmpHiveListHead)
99     {
100         /* Get the hive */
101         Hive = CONTAINING_RECORD(NextEntry, CMHIVE, HiveList);
102         if (!(Hive->Hive.HiveFlags & HIVE_NOLAZYFLUSH))
103         {
104             /* Acquire the flusher lock */
105             CmpLockHiveFlusherExclusive(Hive);
106 
107             /* Check for illegal state */
108             if ((ForceFlush) && (Hive->UseCount))
109             {
110                 /* Registry needs to be locked down */
111                 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK();
112                 DPRINT1("FIXME: Hive is damaged and needs fixup\n");
113                 while (TRUE);
114             }
115 
116             /* Only sync if we are forced to or if it won't cause a hive shrink */
117             if ((ForceFlush) || (!HvHiveWillShrink(&Hive->Hive)))
118             {
119                 /* Do the sync */
120                 Status = HvSyncHive(&Hive->Hive);
121 
122                 /* If something failed - set the flag and continue looping */
123                 if (!NT_SUCCESS(Status)) Result = FALSE;
124             }
125             else
126             {
127                 /* We won't flush if the hive might shrink */
128                 Result = FALSE;
129                 CmpForceForceFlush = TRUE;
130             }
131 
132             /* Release the flusher lock */
133             CmpUnlockHiveFlusher(Hive);
134         }
135 
136         /* Try the next entry */
137         NextEntry = NextEntry->Flink;
138     }
139 
140     /* Release lock and return */
141     ExReleasePushLock(&CmpHiveListHeadLock);
142     return Result;
143 }
144 
145 NTSTATUS
146 NTAPI
147 CmpSetValueKeyNew(IN PHHIVE Hive,
148                   IN PCM_KEY_NODE Parent,
149                   IN PUNICODE_STRING ValueName,
150                   IN ULONG Index,
151                   IN ULONG Type,
152                   IN PVOID Data,
153                   IN ULONG DataSize,
154                   IN ULONG StorageType,
155                   IN ULONG SmallData)
156 {
157     PCELL_DATA CellData;
158     HCELL_INDEX ValueCell;
159     NTSTATUS Status;
160 
161     /* Check if we already have a value list */
162     if (Parent->ValueList.Count)
163     {
164         /* Then make sure it's valid and dirty it */
165         ASSERT(Parent->ValueList.List != HCELL_NIL);
166         if (!HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE))
167         {
168             /* Fail if we're out of space for log changes */
169             return STATUS_NO_LOG_SPACE;
170         }
171     }
172 
173     /* Allocate a value cell */
174     ValueCell = HvAllocateCell(Hive,
175                                FIELD_OFFSET(CM_KEY_VALUE, Name) +
176                                CmpNameSize(Hive, ValueName),
177                                StorageType,
178                                HCELL_NIL);
179     if (ValueCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;
180 
181     /* Get the actual data for it */
182     CellData = HvGetCell(Hive, ValueCell);
183     if (!CellData) ASSERT(FALSE);
184 
185     /* Now we can release it, make sure it's also dirty */
186     HvReleaseCell(Hive, ValueCell);
187     ASSERT(HvIsCellDirty(Hive, ValueCell));
188 
189     /* Set it up and copy the name */
190     CellData->u.KeyValue.Signature = CM_KEY_VALUE_SIGNATURE;
191     _SEH2_TRY
192     {
193         /* This can crash since the name is coming from user-mode */
194         CellData->u.KeyValue.NameLength = CmpCopyName(Hive,
195                                                       CellData->u.KeyValue.Name,
196                                                       ValueName);
197     }
198     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
199     {
200         /* Fail */
201         DPRINT1("Invalid user data!\n");
202         HvFreeCell(Hive, ValueCell);
203         _SEH2_YIELD(return _SEH2_GetExceptionCode());
204     }
205     _SEH2_END;
206 
207     /* Check for compressed name */
208     if (CellData->u.KeyValue.NameLength < ValueName->Length)
209     {
210         /* This is a compressed name */
211         CellData->u.KeyValue.Flags = VALUE_COMP_NAME;
212     }
213     else
214     {
215         /* No flags to set */
216         CellData->u.KeyValue.Flags = 0;
217     }
218 
219     /* Check if this is a normal key */
220     if (DataSize > CM_KEY_VALUE_SMALL)
221     {
222         /* Build a data cell for it */
223         Status = CmpSetValueDataNew(Hive,
224                                     Data,
225                                     DataSize,
226                                     StorageType,
227                                     ValueCell,
228                                     &CellData->u.KeyValue.Data);
229         if (!NT_SUCCESS(Status))
230         {
231             /* We failed, free the cell */
232             HvFreeCell(Hive, ValueCell);
233             return Status;
234         }
235 
236         /* Otherwise, set the data length, and make sure the data is dirty */
237         CellData->u.KeyValue.DataLength = DataSize;
238         ASSERT(HvIsCellDirty(Hive, CellData->u.KeyValue.Data));
239     }
240     else
241     {
242         /* This is a small key, set the data directly inside */
243         CellData->u.KeyValue.DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE;
244         CellData->u.KeyValue.Data = SmallData;
245     }
246 
247     /* Set the type now */
248     CellData->u.KeyValue.Type = Type;
249 
250     /* Add this value cell to the child list */
251     Status = CmpAddValueToList(Hive,
252                                ValueCell,
253                                Index,
254                                StorageType,
255                                &Parent->ValueList);
256 
257     /* If we failed, free the entire cell, including the data */
258     if (!NT_SUCCESS(Status))
259     {
260         /* Overwrite the status with a known one */
261         CmpFreeValue(Hive, ValueCell);
262         Status = STATUS_INSUFFICIENT_RESOURCES;
263     }
264 
265     /* Return Status */
266     return Status;
267 }
268 
269 NTSTATUS
270 NTAPI
271 CmpSetValueKeyExisting(IN PHHIVE Hive,
272                        IN HCELL_INDEX OldChild,
273                        IN PCM_KEY_VALUE Value,
274                        IN ULONG Type,
275                        IN PVOID Data,
276                        IN ULONG DataSize,
277                        IN ULONG StorageType,
278                        IN ULONG TempData)
279 {
280     HCELL_INDEX DataCell, NewCell;
281     PCELL_DATA CellData;
282     ULONG Length;
283     BOOLEAN WasSmall, IsSmall;
284 
285     /* Registry writes must be blocked */
286     CMP_ASSERT_FLUSH_LOCK(Hive);
287 
288     /* Mark the old child cell dirty */
289     if (!HvMarkCellDirty(Hive, OldChild, FALSE)) return STATUS_NO_LOG_SPACE;
290 
291     /* See if this is a small or normal key */
292     WasSmall = CmpIsKeyValueSmall(&Length, Value->DataLength);
293 
294     /* See if our new data can fit in a small key */
295     IsSmall = (DataSize <= CM_KEY_VALUE_SMALL) ? TRUE: FALSE;
296 
297     /* Big keys are unsupported */
298     ASSERT_VALUE_BIG(Hive, Length);
299     ASSERT_VALUE_BIG(Hive, DataSize);
300 
301     /* Mark the old value dirty */
302     if (!CmpMarkValueDataDirty(Hive, Value)) return STATUS_NO_LOG_SPACE;
303 
304     /* Check if we have a small key */
305     if (IsSmall)
306     {
307         /* Check if we had a normal key with some data in it */
308         if (!(WasSmall) && (Length > 0))
309         {
310             /* Free the previous data */
311             CmpFreeValueData(Hive, Value->Data, Length);
312         }
313 
314         /* Write our data directly */
315         Value->DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE;
316         Value->Data = TempData;
317         Value->Type = Type;
318         return STATUS_SUCCESS;
319     }
320 
321     /* We have a normal key. Was the old cell also normal and had data? */
322     if (!(WasSmall) && (Length > 0))
323     {
324         /* Get the current data cell and actual data inside it */
325         DataCell = Value->Data;
326         ASSERT(DataCell != HCELL_NIL);
327         CellData = HvGetCell(Hive, DataCell);
328         if (!CellData) return STATUS_INSUFFICIENT_RESOURCES;
329 
330         /* Immediately release the cell */
331         HvReleaseCell(Hive, DataCell);
332 
333         /* Make sure that the data cell actually has a size */
334         ASSERT(HvGetCellSize(Hive, CellData) > 0);
335 
336         /* Check if the previous data cell could fit our new data */
337         if (DataSize <= (ULONG)(HvGetCellSize(Hive, CellData)))
338         {
339             /* Re-use it then */
340             NewCell = DataCell;
341         }
342         else
343         {
344             /* Otherwise, re-allocate the current data cell */
345             NewCell = HvReallocateCell(Hive, DataCell, DataSize);
346             if (NewCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;
347         }
348     }
349     else
350     {
351         /* This was a small key, or a key with no data, allocate a cell */
352         NewCell = HvAllocateCell(Hive, DataSize, StorageType, HCELL_NIL);
353         if (NewCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;
354     }
355 
356     /* Now get the actual data for our data cell */
357     CellData = HvGetCell(Hive, NewCell);
358     if (!CellData) ASSERT(FALSE);
359 
360     /* Release it immediately */
361     HvReleaseCell(Hive, NewCell);
362 
363     /* Copy our data into the data cell's buffer, and set up the value */
364     RtlCopyMemory(CellData, Data, DataSize);
365     Value->Data = NewCell;
366     Value->DataLength = DataSize;
367     Value->Type = Type;
368 
369     /* Return success */
370     ASSERT(HvIsCellDirty(Hive, NewCell));
371     return STATUS_SUCCESS;
372 }
373 
374 NTSTATUS
375 NTAPI
376 CmpQueryKeyData(IN PHHIVE Hive,
377                 IN PCM_KEY_NODE Node,
378                 IN KEY_INFORMATION_CLASS KeyInformationClass,
379                 IN OUT PVOID KeyInformation,
380                 IN ULONG Length,
381                 IN OUT PULONG ResultLength)
382 {
383     NTSTATUS Status;
384     ULONG Size, SizeLeft, MinimumSize, Offset;
385     PKEY_INFORMATION Info = (PKEY_INFORMATION)KeyInformation;
386     USHORT NameLength;
387     PVOID ClassData;
388 
389     /* Check if the value is compressed */
390     if (Node->Flags & KEY_COMP_NAME)
391     {
392         /* Get the compressed name size */
393         NameLength = CmpCompressedNameSize(Node->Name, Node->NameLength);
394     }
395     else
396     {
397         /* Get the real size */
398         NameLength = Node->NameLength;
399     }
400 
401     /* Check what kind of information is being requested */
402     switch (KeyInformationClass)
403     {
404         /* Basic information */
405         case KeyBasicInformation:
406 
407             /* This is the size we need */
408             Size = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name) + NameLength;
409 
410             /* And this is the minimum we can work with */
411             MinimumSize = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name);
412 
413             /* Let the caller know and assume success */
414             *ResultLength = Size;
415             Status = STATUS_SUCCESS;
416 
417             /* Check if the bufer we got is too small */
418             if (Length < MinimumSize)
419             {
420                 /* Let the caller know and fail */
421                 Status = STATUS_BUFFER_TOO_SMALL;
422                 break;
423             }
424 
425             /* Copy the basic information */
426             Info->KeyBasicInformation.LastWriteTime = Node->LastWriteTime;
427             Info->KeyBasicInformation.TitleIndex = 0;
428             Info->KeyBasicInformation.NameLength = NameLength;
429 
430             /* Only the name is left */
431             SizeLeft = Length - MinimumSize;
432             Size = NameLength;
433 
434             /* Check if we don't have enough space for the name */
435             if (SizeLeft < Size)
436             {
437                 /* Truncate the name we'll return, and tell the caller */
438                 Size = SizeLeft;
439                 Status = STATUS_BUFFER_OVERFLOW;
440             }
441 
442             /* Check if this is a compressed key */
443             if (Node->Flags & KEY_COMP_NAME)
444             {
445                 /* Copy the compressed name */
446                 CmpCopyCompressedName(Info->KeyBasicInformation.Name,
447                                       SizeLeft,
448                                       Node->Name,
449                                       Node->NameLength);
450             }
451             else
452             {
453                 /* Otherwise, copy the raw name */
454                 RtlCopyMemory(Info->KeyBasicInformation.Name,
455                               Node->Name,
456                               Size);
457             }
458             break;
459 
460         /* Node information */
461         case KeyNodeInformation:
462 
463             /* Calculate the size we need */
464             Size = FIELD_OFFSET(KEY_NODE_INFORMATION, Name) +
465                    NameLength +
466                    Node->ClassLength;
467 
468             /* And the minimum size we can support */
469             MinimumSize = FIELD_OFFSET(KEY_NODE_INFORMATION, Name);
470 
471             /* Return the size to the caller and assume succes */
472             *ResultLength = Size;
473             Status = STATUS_SUCCESS;
474 
475             /* Check if the caller's buffer is too small */
476             if (Length < MinimumSize)
477             {
478                 /* Let them know, and fail */
479                 Status = STATUS_BUFFER_TOO_SMALL;
480                 break;
481             }
482 
483             /* Copy the basic information */
484             Info->KeyNodeInformation.LastWriteTime = Node->LastWriteTime;
485             Info->KeyNodeInformation.TitleIndex = 0;
486             Info->KeyNodeInformation.ClassLength = Node->ClassLength;
487             Info->KeyNodeInformation.NameLength = NameLength;
488 
489             /* Now the name is left */
490             SizeLeft = Length - MinimumSize;
491             Size = NameLength;
492 
493             /* Check if the name can fit entirely */
494             if (SizeLeft < Size)
495             {
496                 /* It can't, we'll have to truncate. Tell the caller */
497                 Size = SizeLeft;
498                 Status = STATUS_BUFFER_OVERFLOW;
499             }
500 
501             /* Check if the key node name is compressed */
502             if (Node->Flags & KEY_COMP_NAME)
503             {
504                 /* Copy the compressed name */
505                 CmpCopyCompressedName(Info->KeyNodeInformation.Name,
506                                       SizeLeft,
507                                       Node->Name,
508                                       Node->NameLength);
509             }
510             else
511             {
512                 /* It isn't, so copy the raw name */
513                 RtlCopyMemory(Info->KeyNodeInformation.Name,
514                               Node->Name,
515                               Size);
516             }
517 
518             /* Check if the node has a class */
519             if (Node->ClassLength > 0)
520             {
521                 /* Set the class offset */
522                 Offset = FIELD_OFFSET(KEY_NODE_INFORMATION, Name) + NameLength;
523                 Offset = ALIGN_UP_BY(Offset, sizeof(ULONG));
524                 Info->KeyNodeInformation.ClassOffset = Offset;
525 
526                 /* Get the class data */
527                 ClassData = HvGetCell(Hive, Node->Class);
528                 if (ClassData == NULL)
529                 {
530                     Status = STATUS_INSUFFICIENT_RESOURCES;
531                     break;
532                 }
533 
534                 /* Check if we can copy anything */
535                 if (Length > Offset)
536                 {
537                     /* Copy the class data */
538                     RtlCopyMemory((PUCHAR)Info + Offset,
539                                   ClassData,
540                                   min(Node->ClassLength, Length - Offset));
541                 }
542 
543                 /* Check if the buffer was large enough */
544                 if (Length < Offset + Node->ClassLength)
545                 {
546                     Status = STATUS_BUFFER_OVERFLOW;
547                 }
548 
549                 /* Release the class cell */
550                 HvReleaseCell(Hive, Node->Class);
551             }
552             else
553             {
554                 /* It doesn't, so set offset to -1, not 0! */
555                 Info->KeyNodeInformation.ClassOffset = 0xFFFFFFFF;
556             }
557             break;
558 
559         /* Full information requsted */
560         case KeyFullInformation:
561 
562             /* This is the size we need */
563             Size = FIELD_OFFSET(KEY_FULL_INFORMATION, Class) +
564                    Node->ClassLength;
565 
566             /* This is what we can work with */
567             MinimumSize = FIELD_OFFSET(KEY_FULL_INFORMATION, Class);
568 
569             /* Return it to caller and assume success */
570             *ResultLength = Size;
571             Status = STATUS_SUCCESS;
572 
573             /* Check if the caller's buffer is to small */
574             if (Length < MinimumSize)
575             {
576                 /* Let them know and fail */
577                 Status = STATUS_BUFFER_TOO_SMALL;
578                 break;
579             }
580 
581             /* Now copy all the basic information */
582             Info->KeyFullInformation.LastWriteTime = Node->LastWriteTime;
583             Info->KeyFullInformation.TitleIndex = 0;
584             Info->KeyFullInformation.ClassLength = Node->ClassLength;
585             Info->KeyFullInformation.SubKeys = Node->SubKeyCounts[Stable] +
586                                                Node->SubKeyCounts[Volatile];
587             Info->KeyFullInformation.Values = Node->ValueList.Count;
588             Info->KeyFullInformation.MaxNameLen = Node->MaxNameLen;
589             Info->KeyFullInformation.MaxClassLen = Node->MaxClassLen;
590             Info->KeyFullInformation.MaxValueNameLen = Node->MaxValueNameLen;
591             Info->KeyFullInformation.MaxValueDataLen = Node->MaxValueDataLen;
592 
593             /* Check if we have a class */
594             if (Node->ClassLength > 0)
595             {
596                 /* Set the class offset */
597                 Offset = FIELD_OFFSET(KEY_FULL_INFORMATION, Class);
598                 Info->KeyFullInformation.ClassOffset = Offset;
599 
600                 /* Get the class data */
601                 ClassData = HvGetCell(Hive, Node->Class);
602                 if (ClassData == NULL)
603                 {
604                     Status = STATUS_INSUFFICIENT_RESOURCES;
605                     break;
606                 }
607 
608                 /* Copy the class data */
609                 ASSERT(Length >= Offset);
610                 RtlCopyMemory(Info->KeyFullInformation.Class,
611                               ClassData,
612                               min(Node->ClassLength, Length - Offset));
613 
614                 /* Check if the buffer was large enough */
615                 if (Length < Offset + Node->ClassLength)
616                 {
617                     Status = STATUS_BUFFER_OVERFLOW;
618                 }
619 
620                 /* Release the class cell */
621                 HvReleaseCell(Hive, Node->Class);
622             }
623             else
624             {
625                 /* We don't have a class, so set offset to -1, not 0! */
626                 Info->KeyFullInformation.ClassOffset = 0xFFFFFFFF;
627             }
628             break;
629 
630         /* Any other class that got sent here is invalid! */
631         default:
632 
633             /* Set failure code */
634             Status = STATUS_INVALID_PARAMETER;
635             break;
636     }
637 
638     /* Return status */
639     return Status;
640 }
641 
642 NTSTATUS
643 NTAPI
644 CmSetValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
645               IN PUNICODE_STRING ValueName,
646               IN ULONG Type,
647               IN PVOID Data,
648               IN ULONG DataLength)
649 {
650     PHHIVE Hive = NULL;
651     PCM_KEY_NODE Parent;
652     PCM_KEY_VALUE Value = NULL;
653     HCELL_INDEX CurrentChild, Cell;
654     NTSTATUS Status;
655     BOOLEAN Found, Result;
656     ULONG Count, ChildIndex, SmallData, Storage;
657     VALUE_SEARCH_RETURN_TYPE SearchResult;
658     BOOLEAN FirstTry = TRUE, FlusherLocked = FALSE;
659     HCELL_INDEX ParentCell = HCELL_NIL, ChildCell = HCELL_NIL;
660 
661     /* Acquire hive and KCB lock */
662     CmpLockRegistry();
663     CmpAcquireKcbLockShared(Kcb);
664 
665     /* Sanity check */
666     ASSERT(sizeof(ULONG) == CM_KEY_VALUE_SMALL);
667 
668     /* Don't touch deleted KCBs */
669 DoAgain:
670     if (Kcb->Delete)
671     {
672         /* Fail */
673         Status = STATUS_KEY_DELETED;
674         goto Quickie;
675     }
676 
677     /* Don't let anyone mess with symlinks */
678     if ((Kcb->Flags & KEY_SYM_LINK) &&
679         ((Type != REG_LINK) ||
680          !(ValueName) ||
681          !(RtlEqualUnicodeString(&CmSymbolicLinkValueName, ValueName, TRUE))))
682     {
683         /* Invalid modification of a symlink key */
684         Status = STATUS_ACCESS_DENIED;
685         goto Quickie;
686     }
687 
688     /* Check if this is the first attempt */
689     if (FirstTry)
690     {
691         /* Search for the value in the cache */
692         SearchResult = CmpCompareNewValueDataAgainstKCBCache(Kcb,
693                                                              ValueName,
694                                                              Type,
695                                                              Data,
696                                                              DataLength);
697         if (SearchResult == SearchNeedExclusiveLock)
698         {
699             /* Try again with the exclusive lock */
700             CmpConvertKcbSharedToExclusive(Kcb);
701             goto DoAgain;
702         }
703         else if (SearchResult == SearchSuccess)
704         {
705             /* We don't actually need to do anything! */
706             Status = STATUS_SUCCESS;
707             goto Quickie;
708         }
709 
710         /* We need the exclusive KCB lock now */
711         if (!(CmpIsKcbLockedExclusive(Kcb)) &&
712             !(CmpTryToConvertKcbSharedToExclusive(Kcb)))
713         {
714             /* Acquire exclusive lock */
715             CmpConvertKcbSharedToExclusive(Kcb);
716         }
717 
718         /* Cache lookup failed, so don't try it next time */
719         FirstTry = FALSE;
720 
721         /* Now grab the flush lock since the key will be modified */
722         ASSERT(FlusherLocked == FALSE);
723         CmpLockHiveFlusherShared((PCMHIVE)Kcb->KeyHive);
724         FlusherLocked = TRUE;
725         goto DoAgain;
726     }
727     else
728     {
729         /* Get pointer to key cell */
730         Hive = Kcb->KeyHive;
731         Cell = Kcb->KeyCell;
732 
733         /* Get the parent */
734         Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
735         ASSERT(Parent);
736         ParentCell = Cell;
737 
738         /* Prepare to scan the key node */
739         Count = Parent->ValueList.Count;
740         Found = FALSE;
741         if (Count > 0)
742         {
743             /* Try to find the existing name */
744             Result = CmpFindNameInList(Hive,
745                                        &Parent->ValueList,
746                                        ValueName,
747                                        &ChildIndex,
748                                        &CurrentChild);
749             if (!Result)
750             {
751                 /* Fail */
752                 Status = STATUS_INSUFFICIENT_RESOURCES;
753                 goto Quickie;
754             }
755 
756             /* Check if we found something */
757             if (CurrentChild != HCELL_NIL)
758             {
759                 /* Release existing child */
760                 if (ChildCell != HCELL_NIL)
761                 {
762                     HvReleaseCell(Hive, ChildCell);
763                     ChildCell = HCELL_NIL;
764                 }
765 
766                 /* Get its value */
767                 Value = (PCM_KEY_VALUE)HvGetCell(Hive, CurrentChild);
768                 if (!Value)
769                 {
770                     /* Fail */
771                     Status = STATUS_INSUFFICIENT_RESOURCES;
772                     goto Quickie;
773                 }
774 
775                 /* Remember that we found it */
776                 ChildCell = CurrentChild;
777                 Found = TRUE;
778             }
779         }
780         else
781         {
782             /* No child list, we'll need to add it */
783             ChildIndex = 0;
784         }
785     }
786 
787     /* Should only get here on the second pass */
788     ASSERT(FirstTry == FALSE);
789 
790     /* The KCB must be locked exclusive at this point */
791     CMP_ASSERT_KCB_LOCK(Kcb);
792 
793     /* Mark the cell dirty */
794     if (!HvMarkCellDirty(Hive, Cell, FALSE))
795     {
796         /* Not enough log space, fail */
797         Status = STATUS_NO_LOG_SPACE;
798         goto Quickie;
799     }
800 
801     /* Get the storage type */
802     Storage = HvGetCellType(Cell);
803 
804     /* Check if this is small data */
805     SmallData = 0;
806     if ((DataLength <= CM_KEY_VALUE_SMALL) && (DataLength > 0))
807     {
808         /* Need SEH because user data may be invalid */
809         _SEH2_TRY
810         {
811             /* Copy it */
812             RtlCopyMemory(&SmallData, Data, DataLength);
813         }
814         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
815         {
816             /* Return failure code */
817             Status = _SEH2_GetExceptionCode();
818             _SEH2_YIELD(goto Quickie);
819         }
820         _SEH2_END;
821     }
822 
823     /* Check if we didn't find a matching key */
824     if (!Found)
825     {
826         /* Call the internal routine */
827         Status = CmpSetValueKeyNew(Hive,
828                                    Parent,
829                                    ValueName,
830                                    ChildIndex,
831                                    Type,
832                                    Data,
833                                    DataLength,
834                                    Storage,
835                                    SmallData);
836     }
837     else
838     {
839         /* Call the internal routine */
840         Status = CmpSetValueKeyExisting(Hive,
841                                         CurrentChild,
842                                         Value,
843                                         Type,
844                                         Data,
845                                         DataLength,
846                                         Storage,
847                                         SmallData);
848     }
849 
850     /* Check for success */
851     if (NT_SUCCESS(Status))
852     {
853         /* Check if the maximum value name length changed */
854         ASSERT(Parent->MaxValueNameLen == Kcb->KcbMaxValueNameLen);
855         if (Parent->MaxValueNameLen < ValueName->Length)
856         {
857             /* Set the new values */
858             Parent->MaxValueNameLen = ValueName->Length;
859             Kcb->KcbMaxValueNameLen = ValueName->Length;
860         }
861 
862         /* Check if the maximum data length changed */
863         ASSERT(Parent->MaxValueDataLen == Kcb->KcbMaxValueDataLen);
864         if (Parent->MaxValueDataLen < DataLength)
865         {
866             /* Update it */
867             Parent->MaxValueDataLen = DataLength;
868             Kcb->KcbMaxValueDataLen = Parent->MaxValueDataLen;
869         }
870 
871         /* Save the write time */
872         KeQuerySystemTime(&Parent->LastWriteTime);
873         Kcb->KcbLastWriteTime = Parent->LastWriteTime;
874 
875         /* Check if the cell is cached */
876         if ((Found) && (CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)))
877         {
878             /* Shouldn't happen */
879             ASSERT(FALSE);
880         }
881         else
882         {
883             /* Cleanup the value cache */
884             CmpCleanUpKcbValueCache(Kcb);
885 
886             /* Sanity checks */
887             ASSERT(!(CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)));
888             ASSERT(!(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND));
889 
890             /* Set the value cache */
891             Kcb->ValueCache.Count = Parent->ValueList.Count;
892             Kcb->ValueCache.ValueList = Parent->ValueList.List;
893         }
894 
895         /* Notify registered callbacks */
896         CmpReportNotify(Kcb,
897                         Hive,
898                         Kcb->KeyCell,
899                         REG_NOTIFY_CHANGE_LAST_SET);
900     }
901 
902     /* Release the cells */
903 Quickie:
904     if ((ParentCell != HCELL_NIL) && (Hive)) HvReleaseCell(Hive, ParentCell);
905     if ((ChildCell != HCELL_NIL) && (Hive)) HvReleaseCell(Hive, ChildCell);
906 
907     /* Release the locks */
908     if (FlusherLocked) CmpUnlockHiveFlusher((PCMHIVE)Hive);
909     CmpReleaseKcbLock(Kcb);
910     CmpUnlockRegistry();
911     return Status;
912 }
913 
914 NTSTATUS
915 NTAPI
916 CmDeleteValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
917                  IN UNICODE_STRING ValueName)
918 {
919     NTSTATUS Status = STATUS_OBJECT_NAME_NOT_FOUND;
920     PHHIVE Hive;
921     PCM_KEY_NODE Parent;
922     HCELL_INDEX ChildCell, Cell;
923     PCHILD_LIST ChildList;
924     PCM_KEY_VALUE Value = NULL;
925     ULONG ChildIndex;
926     BOOLEAN Result;
927 
928     /* Acquire hive lock */
929     CmpLockRegistry();
930 
931     /* Lock KCB exclusively */
932     CmpAcquireKcbLockExclusive(Kcb);
933 
934     /* Don't touch deleted keys */
935     if (Kcb->Delete)
936     {
937         /* Undo everything */
938         CmpReleaseKcbLock(Kcb);
939         CmpUnlockRegistry();
940         return STATUS_KEY_DELETED;
941     }
942 
943     /* Get the hive and the cell index */
944     Hive = Kcb->KeyHive;
945     Cell = Kcb->KeyCell;
946 
947     /* Lock flushes */
948     CmpLockHiveFlusherShared((PCMHIVE)Hive);
949 
950     /* Get the parent key node */
951     Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
952     ASSERT(Parent);
953 
954     /* Get the value list and check if it has any entries */
955     ChildList = &Parent->ValueList;
956     if (ChildList->Count)
957     {
958         /* Try to find this value */
959         Result = CmpFindNameInList(Hive,
960                                    ChildList,
961                                    &ValueName,
962                                    &ChildIndex,
963                                    &ChildCell);
964         if (!Result)
965         {
966             /* Fail */
967             Status = STATUS_INSUFFICIENT_RESOURCES;
968             goto Quickie;
969         }
970 
971         /* Value not found, return error */
972         if (ChildCell == HCELL_NIL) goto Quickie;
973 
974         /* We found the value, mark all relevant cells dirty */
975         if (!((HvMarkCellDirty(Hive, Cell, FALSE)) &&
976               (HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE)) &&
977               (HvMarkCellDirty(Hive, ChildCell, FALSE))))
978         {
979             /* Not enough log space, fail */
980             Status = STATUS_NO_LOG_SPACE;
981             goto Quickie;
982         }
983 
984         /* Get the key value */
985         Value = (PCM_KEY_VALUE)HvGetCell(Hive,ChildCell);
986         ASSERT(Value);
987 
988         /* Mark it and all related data as dirty */
989         if (!CmpMarkValueDataDirty(Hive, Value))
990         {
991             /* Not enough log space, fail */
992             Status = STATUS_NO_LOG_SPACE;
993             goto Quickie;
994         }
995 
996         /* Sanity checks */
997         ASSERT(HvIsCellDirty(Hive, Parent->ValueList.List));
998         ASSERT(HvIsCellDirty(Hive, ChildCell));
999 
1000         /* Remove the value from the child list */
1001         Status = CmpRemoveValueFromList(Hive, ChildIndex, ChildList);
1002         if (!NT_SUCCESS(Status))
1003         {
1004             /* Set known error */
1005             Status = STATUS_INSUFFICIENT_RESOURCES;
1006             goto Quickie;
1007         }
1008 
1009         /* Remove the value and its data itself */
1010         if (!CmpFreeValue(Hive, ChildCell))
1011         {
1012             /* Failed to free the value, fail */
1013             Status = STATUS_INSUFFICIENT_RESOURCES;
1014             goto Quickie;
1015         }
1016 
1017         /* Set the last write time */
1018         KeQuerySystemTime(&Parent->LastWriteTime);
1019         Kcb->KcbLastWriteTime = Parent->LastWriteTime;
1020 
1021         /* Sanity check */
1022         ASSERT(Parent->MaxValueNameLen == Kcb->KcbMaxValueNameLen);
1023         ASSERT(Parent->MaxValueDataLen == Kcb->KcbMaxValueDataLen);
1024         ASSERT(HvIsCellDirty(Hive, Cell));
1025 
1026         /* Check if the value list is empty now */
1027         if (!Parent->ValueList.Count)
1028         {
1029             /* Then clear key node data */
1030             Parent->MaxValueNameLen = 0;
1031             Parent->MaxValueDataLen = 0;
1032             Kcb->KcbMaxValueNameLen = 0;
1033             Kcb->KcbMaxValueDataLen = 0;
1034         }
1035 
1036         /* Cleanup the value cache */
1037         CmpCleanUpKcbValueCache(Kcb);
1038 
1039         /* Sanity checks */
1040         ASSERT(!(CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)));
1041         ASSERT(!(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND));
1042 
1043         /* Set the value cache */
1044         Kcb->ValueCache.Count = ChildList->Count;
1045         Kcb->ValueCache.ValueList = ChildList->List;
1046 
1047         /* Notify registered callbacks */
1048         CmpReportNotify(Kcb, Hive, Cell, REG_NOTIFY_CHANGE_LAST_SET);
1049 
1050         /* Change default Status to success */
1051         Status = STATUS_SUCCESS;
1052     }
1053 
1054 Quickie:
1055     /* Release the parent cell, if any */
1056     if (Parent) HvReleaseCell(Hive, Cell);
1057 
1058     /* Check if we had a value */
1059     if (Value)
1060     {
1061         /* Release the child cell */
1062         ASSERT(ChildCell != HCELL_NIL);
1063         HvReleaseCell(Hive, ChildCell);
1064     }
1065 
1066     /* Release locks */
1067     CmpUnlockHiveFlusher((PCMHIVE)Hive);
1068     CmpReleaseKcbLock(Kcb);
1069     CmpUnlockRegistry();
1070     return Status;
1071 }
1072 
1073 NTSTATUS
1074 NTAPI
1075 CmQueryValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
1076                 IN UNICODE_STRING ValueName,
1077                 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
1078                 IN PVOID KeyValueInformation,
1079                 IN ULONG Length,
1080                 IN PULONG ResultLength)
1081 {
1082     NTSTATUS Status;
1083     PCM_KEY_VALUE ValueData;
1084     ULONG Index;
1085     BOOLEAN ValueCached = FALSE;
1086     PCM_CACHED_VALUE *CachedValue;
1087     HCELL_INDEX CellToRelease;
1088     VALUE_SEARCH_RETURN_TYPE Result;
1089     PHHIVE Hive;
1090     PAGED_CODE();
1091 
1092     /* Acquire hive lock */
1093     CmpLockRegistry();
1094 
1095     /* Lock the KCB shared */
1096     CmpAcquireKcbLockShared(Kcb);
1097 
1098     /* Don't touch deleted keys */
1099 DoAgain:
1100     if (Kcb->Delete)
1101     {
1102         /* Undo everything */
1103         CmpReleaseKcbLock(Kcb);
1104         CmpUnlockRegistry();
1105         return STATUS_KEY_DELETED;
1106     }
1107 
1108     /* We don't deal with this yet */
1109     if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)
1110     {
1111         /* Shouldn't happen */
1112         ASSERT(FALSE);
1113     }
1114 
1115     /* Get the hive */
1116     Hive = Kcb->KeyHive;
1117 
1118     /* Find the key value */
1119     Result = CmpFindValueByNameFromCache(Kcb,
1120                                          &ValueName,
1121                                          &CachedValue,
1122                                          &Index,
1123                                          &ValueData,
1124                                          &ValueCached,
1125                                          &CellToRelease);
1126     if (Result == SearchNeedExclusiveLock)
1127     {
1128         /* Check if we need an exclusive lock */
1129         ASSERT(CellToRelease == HCELL_NIL);
1130         ASSERT(ValueData == NULL);
1131 
1132         /* Try with exclusive KCB lock */
1133         CmpConvertKcbSharedToExclusive(Kcb);
1134         goto DoAgain;
1135     }
1136 
1137     if (Result == SearchSuccess)
1138     {
1139         /* Sanity check */
1140         ASSERT(ValueData != NULL);
1141 
1142         /* User data, protect against exceptions */
1143         _SEH2_TRY
1144         {
1145             /* Query the information requested */
1146             Result = CmpQueryKeyValueData(Kcb,
1147                                           CachedValue,
1148                                           ValueData,
1149                                           ValueCached,
1150                                           KeyValueInformationClass,
1151                                           KeyValueInformation,
1152                                           Length,
1153                                           ResultLength,
1154                                           &Status);
1155             if (Result == SearchNeedExclusiveLock)
1156             {
1157                 /* Release the value cell */
1158                 if (CellToRelease != HCELL_NIL)
1159                 {
1160                     HvReleaseCell(Hive, CellToRelease);
1161                     CellToRelease = HCELL_NIL;
1162                 }
1163 
1164                 /* Try with exclusive KCB lock */
1165                 CmpConvertKcbSharedToExclusive(Kcb);
1166                 _SEH2_YIELD(goto DoAgain);
1167             }
1168         }
1169         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1170         {
1171             Status = _SEH2_GetExceptionCode();
1172         }
1173         _SEH2_END;
1174     }
1175     else
1176     {
1177         /* Failed to find the value */
1178         Status = STATUS_OBJECT_NAME_NOT_FOUND;
1179     }
1180 
1181     /* If we have a cell to release, do so */
1182     if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);
1183 
1184     /* Release locks */
1185     CmpReleaseKcbLock(Kcb);
1186     CmpUnlockRegistry();
1187     return Status;
1188 }
1189 
1190 NTSTATUS
1191 NTAPI
1192 CmEnumerateValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
1193                     IN ULONG Index,
1194                     IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
1195                     IN PVOID KeyValueInformation,
1196                     IN ULONG Length,
1197                     IN PULONG ResultLength)
1198 {
1199     NTSTATUS Status;
1200     PHHIVE Hive;
1201     PCM_KEY_NODE Parent;
1202     HCELL_INDEX CellToRelease = HCELL_NIL, CellToRelease2 = HCELL_NIL;
1203     VALUE_SEARCH_RETURN_TYPE Result;
1204     BOOLEAN IndexIsCached, ValueIsCached = FALSE;
1205     PCELL_DATA CellData;
1206     PCM_CACHED_VALUE *CachedValue;
1207     PCM_KEY_VALUE ValueData = NULL;
1208     PAGED_CODE();
1209 
1210     /* Acquire hive lock */
1211     CmpLockRegistry();
1212 
1213     /* Lock the KCB shared */
1214     CmpAcquireKcbLockShared(Kcb);
1215 
1216     /* Don't touch deleted keys */
1217 DoAgain:
1218     if (Kcb->Delete)
1219     {
1220         /* Undo everything */
1221         CmpReleaseKcbLock(Kcb);
1222         CmpUnlockRegistry();
1223         return STATUS_KEY_DELETED;
1224     }
1225 
1226     /* Get the hive and parent */
1227     Hive = Kcb->KeyHive;
1228     Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
1229     ASSERT(Parent);
1230 
1231     /* FIXME: Lack of cache? */
1232     if (Kcb->ValueCache.Count != Parent->ValueList.Count)
1233     {
1234         DPRINT1("HACK: Overriding value cache count\n");
1235         Kcb->ValueCache.Count = Parent->ValueList.Count;
1236     }
1237 
1238     /* Make sure the index is valid */
1239     if (Index >= Kcb->ValueCache.Count)
1240     {
1241         /* Release the cell and fail */
1242         HvReleaseCell(Hive, Kcb->KeyCell);
1243         Status = STATUS_NO_MORE_ENTRIES;
1244         goto Quickie;
1245     }
1246 
1247     /* We don't deal with this yet */
1248     if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)
1249     {
1250         /* Shouldn't happen */
1251         ASSERT(FALSE);
1252     }
1253 
1254     /* Find the value list */
1255     Result = CmpGetValueListFromCache(Kcb,
1256                                       &CellData,
1257                                       &IndexIsCached,
1258                                       &CellToRelease);
1259     if (Result == SearchNeedExclusiveLock)
1260     {
1261         /* Check if we need an exclusive lock */
1262         ASSERT(CellToRelease == HCELL_NIL);
1263         HvReleaseCell(Hive, Kcb->KeyCell);
1264 
1265         /* Try with exclusive KCB lock */
1266         CmpConvertKcbSharedToExclusive(Kcb);
1267         goto DoAgain;
1268     }
1269     else if (Result != SearchSuccess)
1270     {
1271         /* Sanity check */
1272         ASSERT(CellData == NULL);
1273 
1274         /* Release the cell and fail */
1275         Status = STATUS_INSUFFICIENT_RESOURCES;
1276         goto Quickie;
1277     }
1278 
1279     /* Now get the key value */
1280     Result = CmpGetValueKeyFromCache(Kcb,
1281                                      CellData,
1282                                      Index,
1283                                      &CachedValue,
1284                                      &ValueData,
1285                                      IndexIsCached,
1286                                      &ValueIsCached,
1287                                      &CellToRelease2);
1288     if (Result == SearchNeedExclusiveLock)
1289     {
1290         /* Cleanup state */
1291         ASSERT(CellToRelease2 == HCELL_NIL);
1292         if (CellToRelease)
1293         {
1294             HvReleaseCell(Hive, CellToRelease);
1295             CellToRelease = HCELL_NIL;
1296         }
1297         HvReleaseCell(Hive, Kcb->KeyCell);
1298 
1299         /* Try with exclusive KCB lock */
1300         CmpConvertKcbSharedToExclusive(Kcb);
1301         goto DoAgain;
1302     }
1303     else if (Result != SearchSuccess)
1304     {
1305         /* Sanity check */
1306         ASSERT(ValueData == NULL);
1307 
1308         /* Release the cells and fail */
1309         Status = STATUS_INSUFFICIENT_RESOURCES;
1310         goto Quickie;
1311     }
1312 
1313     /* User data, need SEH */
1314     _SEH2_TRY
1315     {
1316         /* Query the information requested */
1317         Result = CmpQueryKeyValueData(Kcb,
1318                                       CachedValue,
1319                                       ValueData,
1320                                       ValueIsCached,
1321                                       KeyValueInformationClass,
1322                                       KeyValueInformation,
1323                                       Length,
1324                                       ResultLength,
1325                                       &Status);
1326         if (Result == SearchNeedExclusiveLock)
1327         {
1328             /* Cleanup state */
1329             if (CellToRelease2) HvReleaseCell(Hive, CellToRelease2);
1330             HvReleaseCell(Hive, Kcb->KeyCell);
1331             if (CellToRelease) HvReleaseCell(Hive, CellToRelease);
1332 
1333             /* Try with exclusive KCB lock */
1334             CmpConvertKcbSharedToExclusive(Kcb);
1335             _SEH2_YIELD(goto DoAgain);
1336         }
1337     }
1338     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1339     {
1340         /* Get exception code */
1341         Status = _SEH2_GetExceptionCode();
1342     }
1343     _SEH2_END;
1344 
1345 Quickie:
1346     /* If we have a cell to release, do so */
1347     if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);
1348 
1349     /* Release the parent cell */
1350     HvReleaseCell(Hive, Kcb->KeyCell);
1351 
1352     /* If we have a cell to release, do so */
1353     if (CellToRelease2 != HCELL_NIL) HvReleaseCell(Hive, CellToRelease2);
1354 
1355     /* Release locks */
1356     CmpReleaseKcbLock(Kcb);
1357     CmpUnlockRegistry();
1358     return Status;
1359 }
1360 
1361 static
1362 NTSTATUS
1363 CmpQueryKeyDataFromCache(
1364     _In_ PCM_KEY_CONTROL_BLOCK Kcb,
1365     _Out_ PKEY_CACHED_INFORMATION KeyCachedInfo,
1366     _In_ ULONG Length,
1367     _Out_ PULONG ResultLength)
1368 {
1369     PCM_KEY_NODE Node;
1370     PHHIVE KeyHive;
1371     HCELL_INDEX KeyCell;
1372     USHORT NameLength;
1373     PAGED_CODE();
1374 
1375     /* Get the hive and cell index */
1376     KeyHive = Kcb->KeyHash.KeyHive;
1377     KeyCell = Kcb->KeyHash.KeyCell;
1378 
1379 #if DBG
1380     /* Get the cell node */
1381     Node = HvGetCell(KeyHive, KeyCell);
1382     if (Node != NULL)
1383     {
1384         ULONG SubKeyCount;
1385         ASSERT(Node->ValueList.Count == Kcb->ValueCache.Count);
1386 
1387         if (!(Kcb->ExtFlags & CM_KCB_INVALID_CACHED_INFO))
1388         {
1389             SubKeyCount = Node->SubKeyCounts[0] + Node->SubKeyCounts[1];
1390             if (Kcb->ExtFlags & CM_KCB_NO_SUBKEY)
1391             {
1392                 ASSERT(SubKeyCount == 0);
1393             }
1394             else if (Kcb->ExtFlags & CM_KCB_SUBKEY_ONE)
1395             {
1396                 ASSERT(SubKeyCount == 1);
1397             }
1398             else if (Kcb->ExtFlags & CM_KCB_SUBKEY_HINT)
1399             {
1400                 ASSERT(SubKeyCount == Kcb->IndexHint->Count);
1401             }
1402             else
1403             {
1404                 ASSERT(SubKeyCount == Kcb->SubKeyCount);
1405             }
1406         }
1407 
1408         ASSERT(Node->LastWriteTime.QuadPart == Kcb->KcbLastWriteTime.QuadPart);
1409         ASSERT(Node->MaxNameLen == Kcb->KcbMaxNameLen);
1410         ASSERT(Node->MaxValueNameLen == Kcb->KcbMaxValueNameLen);
1411         ASSERT(Node->MaxValueDataLen == Kcb->KcbMaxValueDataLen);
1412 
1413         /* Release the cell */
1414         HvReleaseCell(KeyHive, KeyCell);
1415     }
1416 #endif // DBG
1417 
1418     /* Make sure we have a name block */
1419     if (Kcb->NameBlock == NULL)
1420     {
1421         return STATUS_INSUFFICIENT_RESOURCES;
1422     }
1423 
1424     /* Check for compressed name */
1425     if (Kcb->NameBlock->Compressed)
1426     {
1427         /* Calculate the name size */
1428         NameLength = CmpCompressedNameSize(Kcb->NameBlock->NameHash.Name,
1429                                            Kcb->NameBlock->NameHash.NameLength);
1430     }
1431     else
1432     {
1433         /* Use the stored name size */
1434         NameLength = Kcb->NameBlock->NameHash.NameLength;
1435     }
1436 
1437     /* Validate buffer length (we do not copy the name!) */
1438     *ResultLength = sizeof(*KeyCachedInfo);
1439     if (Length < *ResultLength)
1440     {
1441         return STATUS_BUFFER_TOO_SMALL;
1442     }
1443 
1444     /* Fill the structure */
1445     KeyCachedInfo->LastWriteTime = Kcb->KcbLastWriteTime;
1446     KeyCachedInfo->TitleIndex = 0;
1447     KeyCachedInfo->NameLength = NameLength;
1448     KeyCachedInfo->Values = Kcb->ValueCache.Count;
1449     KeyCachedInfo->MaxNameLen = Kcb->KcbMaxNameLen;
1450     KeyCachedInfo->MaxValueNameLen = Kcb->KcbMaxValueNameLen;
1451     KeyCachedInfo->MaxValueDataLen = Kcb->KcbMaxValueDataLen;
1452 
1453     /* Check the ExtFlags for what we have */
1454     if (Kcb->ExtFlags & CM_KCB_INVALID_CACHED_INFO)
1455     {
1456         /* Cache is not valid, do a full lookup */
1457         DPRINT1("Kcb cache incoherency detected, kcb = %p\n", Kcb);
1458 
1459         /* Get the cell node */
1460         Node = HvGetCell(KeyHive, KeyCell);
1461         if (Node == NULL)
1462         {
1463             return STATUS_INSUFFICIENT_RESOURCES;
1464         }
1465 
1466         /* Calculate number of subkeys */
1467         KeyCachedInfo->SubKeys = Node->SubKeyCounts[0] + Node->SubKeyCounts[1];
1468 
1469         /* Release the cell */
1470         HvReleaseCell(KeyHive, KeyCell);
1471     }
1472     else if (Kcb->ExtFlags & CM_KCB_NO_SUBKEY)
1473     {
1474         /* There are no subkeys */
1475         KeyCachedInfo->SubKeys = 0;
1476     }
1477     else if (Kcb->ExtFlags & CM_KCB_SUBKEY_ONE)
1478     {
1479         /* There is exactly one subley */
1480         KeyCachedInfo->SubKeys = 1;
1481     }
1482     else if (Kcb->ExtFlags & CM_KCB_SUBKEY_HINT)
1483     {
1484         /* Get the number of subkeys from the subkey hint */
1485         KeyCachedInfo->SubKeys = Kcb->IndexHint->Count;
1486     }
1487     else
1488     {
1489         /* No subkey hint, use the key count field */
1490         KeyCachedInfo->SubKeys = Kcb->SubKeyCount;
1491     }
1492 
1493     return STATUS_SUCCESS;
1494 }
1495 
1496 static
1497 NTSTATUS
1498 CmpQueryFlagsInformation(
1499     _In_ PCM_KEY_CONTROL_BLOCK Kcb,
1500     _Out_ PKEY_USER_FLAGS_INFORMATION KeyFlagsInfo,
1501     _In_ ULONG Length,
1502     _In_ PULONG ResultLength)
1503 {
1504     /* Validate the buffer size */
1505     *ResultLength = sizeof(*KeyFlagsInfo);
1506     if (Length < *ResultLength)
1507     {
1508         return STATUS_BUFFER_TOO_SMALL;
1509     }
1510 
1511     /* Copy the user flags */
1512     KeyFlagsInfo->UserFlags = Kcb->KcbUserFlags;
1513 
1514     return STATUS_SUCCESS;
1515 }
1516 
1517 static
1518 NTSTATUS
1519 CmpQueryNameInformation(
1520     _In_ PCM_KEY_CONTROL_BLOCK Kcb,
1521     _Out_opt_ PKEY_NAME_INFORMATION KeyNameInfo,
1522     _In_ ULONG Length,
1523     _Out_ PULONG ResultLength)
1524 {
1525     ULONG NeededLength;
1526     PCM_KEY_CONTROL_BLOCK CurrentKcb;
1527 
1528     NeededLength = 0;
1529     CurrentKcb = Kcb;
1530 
1531     /* Count the needed buffer size */
1532     while (CurrentKcb)
1533     {
1534         if (CurrentKcb->NameBlock->Compressed)
1535             NeededLength += CmpCompressedNameSize(CurrentKcb->NameBlock->Name, CurrentKcb->NameBlock->NameLength);
1536         else
1537             NeededLength += CurrentKcb->NameBlock->NameLength;
1538 
1539         NeededLength += sizeof(OBJ_NAME_PATH_SEPARATOR);
1540 
1541         CurrentKcb = CurrentKcb->ParentKcb;
1542     }
1543 
1544     _SEH2_TRY
1545     {
1546         *ResultLength = FIELD_OFFSET(KEY_NAME_INFORMATION, Name) + NeededLength;
1547         if (Length < RTL_SIZEOF_THROUGH_FIELD(KEY_NAME_INFORMATION, NameLength))
1548             _SEH2_YIELD(return STATUS_BUFFER_TOO_SMALL);
1549         if (Length < *ResultLength)
1550         {
1551             KeyNameInfo->NameLength = NeededLength;
1552             _SEH2_YIELD(return STATUS_BUFFER_OVERFLOW);
1553         }
1554     }
1555     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1556     {
1557         _SEH2_YIELD(return _SEH2_GetExceptionCode());
1558     }
1559     _SEH2_END;
1560 
1561     /* Do the real copy */
1562     KeyNameInfo->NameLength = 0;
1563     CurrentKcb = Kcb;
1564 
1565     _SEH2_TRY
1566     {
1567         while (CurrentKcb)
1568         {
1569             ULONG NameLength;
1570 
1571             if (CurrentKcb->NameBlock->Compressed)
1572             {
1573                 NameLength = CmpCompressedNameSize(CurrentKcb->NameBlock->Name, CurrentKcb->NameBlock->NameLength);
1574                 /* Copy the compressed name */
1575                 CmpCopyCompressedName(&KeyNameInfo->Name[(NeededLength - NameLength)/sizeof(WCHAR)],
1576                                       NameLength,
1577                                       CurrentKcb->NameBlock->Name,
1578                                       CurrentKcb->NameBlock->NameLength);
1579             }
1580             else
1581             {
1582                 NameLength = CurrentKcb->NameBlock->NameLength;
1583                 /* Otherwise, copy the raw name */
1584                 RtlCopyMemory(&KeyNameInfo->Name[(NeededLength - NameLength)/sizeof(WCHAR)],
1585                               CurrentKcb->NameBlock->Name,
1586                               NameLength);
1587             }
1588 
1589             NeededLength -= NameLength;
1590             NeededLength -= sizeof(OBJ_NAME_PATH_SEPARATOR);
1591             /* Add path separator */
1592             KeyNameInfo->Name[NeededLength/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
1593             KeyNameInfo->NameLength += NameLength + sizeof(OBJ_NAME_PATH_SEPARATOR);
1594 
1595             CurrentKcb = CurrentKcb->ParentKcb;
1596         }
1597     }
1598     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1599     {
1600         _SEH2_YIELD(return _SEH2_GetExceptionCode());
1601     }
1602     _SEH2_END;
1603 
1604     /* Make sure we copied everything */
1605     ASSERT(NeededLength == 0);
1606     ASSERT(KeyNameInfo->Name[0] == OBJ_NAME_PATH_SEPARATOR);
1607 
1608     /* We're done */
1609     return STATUS_SUCCESS;
1610 }
1611 
1612 
1613 NTSTATUS
1614 NTAPI
1615 CmQueryKey(_In_ PCM_KEY_CONTROL_BLOCK Kcb,
1616            _In_ KEY_INFORMATION_CLASS KeyInformationClass,
1617            _Out_opt_ PVOID KeyInformation,
1618            _In_ ULONG Length,
1619            _Out_ PULONG ResultLength)
1620 {
1621     NTSTATUS Status;
1622     PHHIVE Hive;
1623     PCM_KEY_NODE Parent;
1624     HV_TRACK_CELL_REF CellReferences = {0};
1625 
1626     /* Acquire hive lock */
1627     CmpLockRegistry();
1628 
1629     /* Lock KCB shared */
1630     CmpAcquireKcbLockShared(Kcb);
1631 
1632     /* Don't touch deleted keys */
1633     if (Kcb->Delete)
1634     {
1635         /* Fail */
1636         Status = STATUS_KEY_DELETED;
1637         goto Quickie;
1638     }
1639 
1640     /* Data can be user-mode, use SEH */
1641     _SEH2_TRY
1642     {
1643         /* Check what class we got */
1644         switch (KeyInformationClass)
1645         {
1646             /* Typical information */
1647             case KeyFullInformation:
1648             case KeyBasicInformation:
1649             case KeyNodeInformation:
1650             {
1651                 /* Get the hive and parent */
1652                 Hive = Kcb->KeyHive;
1653                 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
1654                 ASSERT(Parent);
1655 
1656                 /* Track cell references */
1657                 if (!HvTrackCellRef(&CellReferences, Hive, Kcb->KeyCell))
1658                 {
1659                     /* Not enough memory to track references */
1660                     Status = STATUS_INSUFFICIENT_RESOURCES;
1661                 }
1662                 else
1663                 {
1664                     /* Call the internal API */
1665                     Status = CmpQueryKeyData(Hive,
1666                                              Parent,
1667                                              KeyInformationClass,
1668                                              KeyInformation,
1669                                              Length,
1670                                              ResultLength);
1671                 }
1672                 break;
1673             }
1674 
1675             case KeyCachedInformation:
1676             {
1677                 /* Call the internal API */
1678                 Status = CmpQueryKeyDataFromCache(Kcb,
1679                                                   KeyInformation,
1680                                                   Length,
1681                                                   ResultLength);
1682                 break;
1683             }
1684 
1685             case KeyFlagsInformation:
1686             {
1687                 /* Call the internal API */
1688                 Status = CmpQueryFlagsInformation(Kcb,
1689                                                   KeyInformation,
1690                                                   Length,
1691                                                   ResultLength);
1692                 break;
1693             }
1694 
1695             case KeyNameInformation:
1696             {
1697                 /* Call the internal API */
1698                 Status = CmpQueryNameInformation(Kcb,
1699                                                  KeyInformation,
1700                                                  Length,
1701                                                  ResultLength);
1702                 break;
1703             }
1704 
1705             /* Illegal classes */
1706             default:
1707             {
1708                 /* Print message and fail */
1709                 DPRINT1("Unsupported class: %d!\n", KeyInformationClass);
1710                 Status = STATUS_INVALID_INFO_CLASS;
1711                 break;
1712             }
1713         }
1714     }
1715     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1716     {
1717         /* Fail with exception code */
1718         Status = _SEH2_GetExceptionCode();
1719         _SEH2_YIELD(goto Quickie);
1720     }
1721     _SEH2_END;
1722 
1723 Quickie:
1724     /* Release references */
1725     HvReleaseFreeCellRefArray(&CellReferences);
1726 
1727     /* Release locks */
1728     CmpReleaseKcbLock(Kcb);
1729     CmpUnlockRegistry();
1730     return Status;
1731 }
1732 
1733 NTSTATUS
1734 NTAPI
1735 CmEnumerateKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
1736                IN ULONG Index,
1737                IN KEY_INFORMATION_CLASS KeyInformationClass,
1738                IN PVOID KeyInformation,
1739                IN ULONG Length,
1740                IN PULONG ResultLength)
1741 {
1742     NTSTATUS Status;
1743     PHHIVE Hive;
1744     PCM_KEY_NODE Parent, Child;
1745     HCELL_INDEX ChildCell;
1746     HV_TRACK_CELL_REF CellReferences = {0};
1747 
1748     /* Acquire hive lock */
1749     CmpLockRegistry();
1750 
1751     /* Lock the KCB shared */
1752     CmpAcquireKcbLockShared(Kcb);
1753 
1754     /* Don't touch deleted keys */
1755     if (Kcb->Delete)
1756     {
1757         /* Undo everything */
1758         Status = STATUS_KEY_DELETED;
1759         goto Quickie;
1760     }
1761 
1762     /* Get the hive and parent */
1763     Hive = Kcb->KeyHive;
1764     Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
1765     ASSERT(Parent);
1766 
1767     /* Get the child cell */
1768     ChildCell = CmpFindSubKeyByNumber(Hive, Parent, Index);
1769 
1770     /* Release the parent cell */
1771     HvReleaseCell(Hive, Kcb->KeyCell);
1772 
1773     /* Check if we found the child */
1774     if (ChildCell == HCELL_NIL)
1775     {
1776         /* We didn't, fail */
1777         Status = STATUS_NO_MORE_ENTRIES;
1778         goto Quickie;
1779     }
1780 
1781     /* Now get the actual child node */
1782     Child = (PCM_KEY_NODE)HvGetCell(Hive, ChildCell);
1783     ASSERT(Child);
1784 
1785     /* Track references */
1786     if (!HvTrackCellRef(&CellReferences, Hive, ChildCell))
1787     {
1788         /* Can't allocate memory for tracking */
1789         Status = STATUS_INSUFFICIENT_RESOURCES;
1790         goto Quickie;
1791     }
1792 
1793     /* Data can be user-mode, use SEH */
1794     _SEH2_TRY
1795     {
1796         /* Query the data requested */
1797         Status = CmpQueryKeyData(Hive,
1798                                  Child,
1799                                  KeyInformationClass,
1800                                  KeyInformation,
1801                                  Length,
1802                                  ResultLength);
1803     }
1804     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1805     {
1806         /* Fail with exception code */
1807         Status = _SEH2_GetExceptionCode();
1808         _SEH2_YIELD(goto Quickie);
1809     }
1810     _SEH2_END;
1811 
1812 Quickie:
1813     /* Release references */
1814     HvReleaseFreeCellRefArray(&CellReferences);
1815 
1816     /* Release locks */
1817     CmpReleaseKcbLock(Kcb);
1818     CmpUnlockRegistry();
1819     return Status;
1820 }
1821 
1822 NTSTATUS
1823 NTAPI
1824 CmDeleteKey(IN PCM_KEY_BODY KeyBody)
1825 {
1826     NTSTATUS Status;
1827     PHHIVE Hive;
1828     PCM_KEY_NODE Node, Parent;
1829     HCELL_INDEX Cell, ParentCell;
1830     PCM_KEY_CONTROL_BLOCK Kcb;
1831 
1832     /* Acquire hive lock */
1833     CmpLockRegistry();
1834 
1835     /* Get the kcb */
1836     Kcb = KeyBody->KeyControlBlock;
1837 
1838     /* Don't allow deleting the root */
1839     if (!Kcb->ParentKcb)
1840     {
1841         /* Fail */
1842         CmpUnlockRegistry();
1843         return STATUS_CANNOT_DELETE;
1844     }
1845 
1846     /* Lock parent and child */
1847     CmpAcquireTwoKcbLocksExclusiveByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey);
1848 
1849     /* Check if we're already being deleted */
1850     if (Kcb->Delete)
1851     {
1852         /* Don't do it twice */
1853         Status = STATUS_SUCCESS;
1854         goto Quickie2;
1855     }
1856 
1857     /* Get the hive and node */
1858     Hive = Kcb->KeyHive;
1859     Cell = Kcb->KeyCell;
1860 
1861     /* Lock flushes */
1862     CmpLockHiveFlusherShared((PCMHIVE)Hive);
1863 
1864     /* Get the key node */
1865     Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
1866     ASSERT(Node);
1867 
1868     /* Sanity check */
1869     ASSERT(Node->Flags == Kcb->Flags);
1870 
1871     /* Check if we don't have any children */
1872     if (!(Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile]) &&
1873         !(Node->Flags & KEY_NO_DELETE))
1874     {
1875         /* Send notification to registered callbacks */
1876         CmpReportNotify(Kcb, Hive, Cell, REG_NOTIFY_CHANGE_NAME);
1877 
1878         /* Get the parent and free the cell */
1879         ParentCell = Node->Parent;
1880         Status = CmpFreeKeyByCell(Hive, Cell, TRUE);
1881         if (NT_SUCCESS(Status))
1882         {
1883             /* Flush any notifications */
1884             CmpFlushNotifiesOnKeyBodyList(Kcb, FALSE);
1885 
1886             /* Clean up information we have on the subkey */
1887             CmpCleanUpSubKeyInfo(Kcb->ParentKcb);
1888 
1889             /* Get the parent node */
1890             Parent = (PCM_KEY_NODE)HvGetCell(Hive, ParentCell);
1891             if (Parent)
1892             {
1893                 /* Update the maximum name length */
1894                 Kcb->ParentKcb->KcbMaxNameLen = (USHORT)Parent->MaxNameLen;
1895 
1896                 /* Make sure we're dirty */
1897                 ASSERT(HvIsCellDirty(Hive, ParentCell));
1898 
1899                 /* Update the write time */
1900                 KeQuerySystemTime(&Parent->LastWriteTime);
1901                 Kcb->ParentKcb->KcbLastWriteTime = Parent->LastWriteTime;
1902 
1903                 /* Release the cell */
1904                 HvReleaseCell(Hive, ParentCell);
1905             }
1906 
1907             /* Set the KCB in delete mode and remove it */
1908             Kcb->Delete = TRUE;
1909             CmpRemoveKeyControlBlock(Kcb);
1910 
1911             /* Clear the cell */
1912             Kcb->KeyCell = HCELL_NIL;
1913         }
1914     }
1915     else
1916     {
1917         /* Fail */
1918         Status = STATUS_CANNOT_DELETE;
1919     }
1920 
1921     /* Release the cell */
1922     HvReleaseCell(Hive, Cell);
1923 
1924     /* Release flush lock */
1925     CmpUnlockHiveFlusher((PCMHIVE)Hive);
1926 
1927     /* Release the KCB locks */
1928 Quickie2:
1929     CmpReleaseTwoKcbLockByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey);
1930 
1931     /* Release hive lock */
1932     CmpUnlockRegistry();
1933     return Status;
1934 }
1935 
1936 NTSTATUS
1937 NTAPI
1938 CmFlushKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
1939            IN BOOLEAN ExclusiveLock)
1940 {
1941     PCMHIVE CmHive;
1942     NTSTATUS Status = STATUS_SUCCESS;
1943     PHHIVE Hive;
1944 
1945     /* Ignore flushes until we're ready */
1946     if (CmpNoWrite) return STATUS_SUCCESS;
1947 
1948     /* Get the hives */
1949     Hive = Kcb->KeyHive;
1950     CmHive = (PCMHIVE)Hive;
1951 
1952     /* Check if this is the master hive */
1953     if (CmHive == CmiVolatileHive)
1954     {
1955         /* Flush all the hives instead */
1956         CmpDoFlushAll(FALSE);
1957     }
1958     else
1959     {
1960         /* Don't touch the hive */
1961         CmpLockHiveFlusherExclusive(CmHive);
1962         ASSERT(CmHive->ViewLock);
1963         KeAcquireGuardedMutex(CmHive->ViewLock);
1964         CmHive->ViewLockOwner = KeGetCurrentThread();
1965 
1966         /* Will the hive shrink? */
1967         if (HvHiveWillShrink(Hive))
1968         {
1969             /* I don't believe the current Hv does shrinking */
1970             ASSERT(FALSE);
1971         }
1972         else
1973         {
1974             /* Now we can release views */
1975             ASSERT(CmHive->ViewLock);
1976             CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK_OR_LOADING(CmHive);
1977             ASSERT(KeGetCurrentThread() == CmHive->ViewLockOwner);
1978             KeReleaseGuardedMutex(CmHive->ViewLock);
1979         }
1980 
1981         /* Flush only this hive */
1982         if (!HvSyncHive(Hive))
1983         {
1984             /* Fail */
1985             Status = STATUS_REGISTRY_IO_FAILED;
1986         }
1987 
1988         /* Release the flush lock */
1989         CmpUnlockHiveFlusher(CmHive);
1990     }
1991 
1992     /* Return the status */
1993     return Status;
1994 }
1995 
1996 NTSTATUS
1997 NTAPI
1998 CmLoadKey(IN POBJECT_ATTRIBUTES TargetKey,
1999           IN POBJECT_ATTRIBUTES SourceFile,
2000           IN ULONG Flags,
2001           IN PCM_KEY_BODY KeyBody)
2002 {
2003     SECURITY_QUALITY_OF_SERVICE ServiceQos;
2004     SECURITY_CLIENT_CONTEXT ClientSecurityContext;
2005     HANDLE KeyHandle;
2006     BOOLEAN Allocate = TRUE;
2007     PCMHIVE CmHive, LoadedHive;
2008     NTSTATUS Status;
2009     CM_PARSE_CONTEXT ParseContext;
2010 
2011     /* Check if we have a trust key */
2012     if (KeyBody)
2013     {
2014         /* Fail */
2015         DPRINT("Trusted classes not yet supported\n");
2016     }
2017 
2018     /* Build a service QoS for a security context */
2019     ServiceQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
2020     ServiceQos.ImpersonationLevel = SecurityImpersonation;
2021     ServiceQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
2022     ServiceQos.EffectiveOnly = TRUE;
2023     Status = SeCreateClientSecurity(PsGetCurrentThread(),
2024                                     &ServiceQos,
2025                                     FALSE,
2026                                     &ClientSecurityContext);
2027     if (!NT_SUCCESS(Status))
2028     {
2029         /* Fail */
2030         DPRINT1("Security context failed\n");
2031         return Status;
2032     }
2033 
2034     /* Open the target key */
2035     RtlZeroMemory(&ParseContext, sizeof(ParseContext));
2036     ParseContext.CreateOperation = FALSE;
2037     Status = ObOpenObjectByName(TargetKey,
2038                                 CmpKeyObjectType,
2039                                 KernelMode,
2040                                 NULL,
2041                                 KEY_READ,
2042                                 &ParseContext,
2043                                 &KeyHandle);
2044     if (!NT_SUCCESS(Status)) KeyHandle = NULL;
2045 
2046     /* Open the hive */
2047     Status = CmpCmdHiveOpen(SourceFile,
2048                             &ClientSecurityContext,
2049                             &Allocate,
2050                             &CmHive,
2051                             0);
2052 
2053     /* Get rid of the security context */
2054     SeDeleteClientSecurity(&ClientSecurityContext);
2055 
2056     /* See if we failed */
2057     if (!NT_SUCCESS(Status))
2058     {
2059         /* See if the target already existed */
2060         if (KeyHandle)
2061         {
2062             /* Lock the registry */
2063             CmpLockRegistryExclusive();
2064 
2065             /* Check if we are already loaded */
2066             if (CmpIsHiveAlreadyLoaded(KeyHandle, SourceFile, &LoadedHive))
2067             {
2068                 /* That's okay then */
2069                 ASSERT(LoadedHive);
2070                 Status = STATUS_SUCCESS;
2071             }
2072 
2073             /* Release the registry */
2074             CmpUnlockRegistry();
2075         }
2076 
2077         /* Close the key handle if we had one */
2078         if (KeyHandle) ZwClose(KeyHandle);
2079         return Status;
2080     }
2081 
2082     /* Lock the registry shared */
2083     CmpLockRegistry();
2084 
2085     /* Lock loading */
2086     ExAcquirePushLockExclusive(&CmpLoadHiveLock);
2087 
2088     /* Lock the hive to this thread */
2089     CmHive->Hive.HiveFlags |= HIVE_IS_UNLOADING;
2090     CmHive->CreatorOwner = KeGetCurrentThread();
2091 
2092     /* Set flag */
2093     if (Flags & REG_NO_LAZY_FLUSH) CmHive->Hive.HiveFlags |= HIVE_NOLAZYFLUSH;
2094 
2095     /* Link the hive */
2096     Status = CmpLinkHiveToMaster(TargetKey->ObjectName,
2097                                  TargetKey->RootDirectory,
2098                                  CmHive,
2099                                  Allocate,
2100                                  TargetKey->SecurityDescriptor);
2101     if (NT_SUCCESS(Status))
2102     {
2103         /* Add to HiveList key */
2104         CmpAddToHiveFileList(CmHive);
2105 
2106         /* Sync the hive if necessary */
2107         if (Allocate)
2108         {
2109             /* Sync it under the flusher lock */
2110             CmpLockHiveFlusherExclusive(CmHive);
2111             HvSyncHive(&CmHive->Hive);
2112             CmpUnlockHiveFlusher(CmHive);
2113         }
2114 
2115         /* Release the hive */
2116         CmHive->Hive.HiveFlags &= ~HIVE_IS_UNLOADING;
2117         CmHive->CreatorOwner = NULL;
2118 
2119         /* Allow loads */
2120         ExReleasePushLock(&CmpLoadHiveLock);
2121     }
2122     else
2123     {
2124         DPRINT1("CmpLinkHiveToMaster failed, Status %lx\n", Status);
2125         /* FIXME: TODO */
2126         ASSERT(FALSE);
2127     }
2128 
2129     /* Is this first profile load? */
2130     if (!CmpProfileLoaded && !CmpWasSetupBoot)
2131     {
2132         /* User is now logged on, set quotas */
2133         CmpProfileLoaded = TRUE;
2134         CmpSetGlobalQuotaAllowed();
2135     }
2136 
2137     /* Unlock the registry */
2138     CmpUnlockRegistry();
2139 
2140     /* Close handle and return */
2141     if (KeyHandle) ZwClose(KeyHandle);
2142     return Status;
2143 }
2144 
2145 static
2146 BOOLEAN
2147 NTAPI
2148 CmpUnlinkHiveFromMaster(IN PCMHIVE CmHive,
2149                         IN HCELL_INDEX Cell)
2150 {
2151     PCELL_DATA CellData;
2152     HCELL_INDEX LinkCell;
2153     NTSTATUS Status;
2154 
2155     DPRINT("CmpUnlinkHiveFromMaster()\n");
2156 
2157     /* Get the cell data */
2158     CellData = HvGetCell(&CmHive->Hive, Cell);
2159     if (CellData == NULL)
2160         return FALSE;
2161 
2162     /* Get the link cell and release the current cell */
2163     LinkCell = CellData->u.KeyNode.Parent;
2164     HvReleaseCell(&CmHive->Hive, Cell);
2165 
2166     /* Remove the link cell from the master hive */
2167     CmpLockHiveFlusherExclusive(CmiVolatileHive);
2168     Status = CmpFreeKeyByCell((PHHIVE)CmiVolatileHive,
2169                               LinkCell,
2170                               TRUE);
2171     CmpUnlockHiveFlusher(CmiVolatileHive);
2172     if (!NT_SUCCESS(Status))
2173     {
2174         DPRINT1("CmpFreeKeyByCell() failed (Status 0x%08lx)\n", Status);
2175         return FALSE;
2176     }
2177 
2178     /* Remove the hive from the list */
2179     ExAcquirePushLockExclusive(&CmpHiveListHeadLock);
2180     RemoveEntryList(&CmHive->HiveList);
2181     ExReleasePushLock(&CmpHiveListHeadLock);
2182 
2183     return TRUE;
2184 }
2185 
2186 NTSTATUS
2187 NTAPI
2188 CmUnloadKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
2189             IN ULONG Flags)
2190 {
2191     PHHIVE Hive;
2192     PCMHIVE CmHive;
2193     HCELL_INDEX Cell;
2194 
2195     DPRINT("CmUnloadKey(%p, %lx)\n", Kcb, Flags);
2196 
2197     /* Get the hive */
2198     Hive = Kcb->KeyHive;
2199     Cell = Kcb->KeyCell;
2200     CmHive = (PCMHIVE)Hive;
2201 
2202     /* Fail if the key is not a hive root key */
2203     if (Cell != Hive->BaseBlock->RootCell)
2204     {
2205         DPRINT1("Key is not a hive root key!\n");
2206         return STATUS_INVALID_PARAMETER;
2207     }
2208 
2209     /* Fail if we try to unload the master hive */
2210     if (CmHive == CmiVolatileHive)
2211     {
2212         DPRINT1("Do not try to unload the master hive!\n");
2213         return STATUS_INVALID_PARAMETER;
2214     }
2215 
2216     /* Mark this hive as being unloaded */
2217     Hive->HiveFlags |= HIVE_IS_UNLOADING;
2218 
2219     /* Search for any opened keys in this hive, and take an appropriate action */
2220     if (Kcb->RefCount > 1)
2221     {
2222         if (Flags != REG_FORCE_UNLOAD)
2223         {
2224             if (CmCountOpenSubKeys(Kcb, FALSE) != 0)
2225             {
2226                 /* There are open subkeys but we don't force hive unloading, fail */
2227                 Hive->HiveFlags &= ~HIVE_IS_UNLOADING;
2228                 return STATUS_CANNOT_DELETE;
2229             }
2230         }
2231         else
2232         {
2233             DPRINT1("CmUnloadKey: Force unloading is UNIMPLEMENTED, expect dangling KCBs problems!\n");
2234         }
2235     }
2236 
2237     /* Flush the hive */
2238     CmFlushKey(Kcb, TRUE);
2239 
2240     /* Unlink the hive from the master hive */
2241     if (!CmpUnlinkHiveFromMaster(CmHive, Cell))
2242     {
2243         DPRINT("CmpUnlinkHiveFromMaster() failed!\n");
2244 
2245         /* Remove the unloading flag and return failure */
2246         Hive->HiveFlags &= ~HIVE_IS_UNLOADING;
2247         return STATUS_INSUFFICIENT_RESOURCES;
2248     }
2249 
2250     /* Clean up information we have on the subkey */
2251     CmpCleanUpSubKeyInfo(Kcb->ParentKcb);
2252 
2253     /* Set the KCB in delete mode and remove it */
2254     Kcb->Delete = TRUE;
2255     CmpRemoveKeyControlBlock(Kcb);
2256 
2257     if (Flags != REG_FORCE_UNLOAD)
2258     {
2259         /* Release the KCB locks */
2260         CmpReleaseTwoKcbLockByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey);
2261 
2262         /* Release the hive loading lock */
2263         ExReleasePushLockExclusive(&CmpLoadHiveLock);
2264     }
2265 
2266     /* Release hive lock */
2267     CmpUnlockRegistry();
2268 
2269     /* Close file handles */
2270     CmpCloseHiveFiles(CmHive);
2271 
2272     /* Remove the hive from the hive file list */
2273     CmpRemoveFromHiveFileList(CmHive);
2274 
2275     /* Destroy the security descriptor cache */
2276     CmpDestroySecurityCache(CmHive);
2277 
2278     /* Destroy the view list */
2279     CmpDestroyHiveViewList(CmHive);
2280 
2281     /* Delete the flusher lock */
2282     ExDeleteResourceLite(CmHive->FlusherLock);
2283     ExFreePoolWithTag(CmHive->FlusherLock, TAG_CMHIVE);
2284 
2285     /* Delete the view lock */
2286     ExFreePoolWithTag(CmHive->ViewLock, TAG_CMHIVE);
2287 
2288     /* Free the hive storage */
2289     HvFree(Hive);
2290 
2291     /* Free the hive */
2292     CmpFree(CmHive, TAG_CM);
2293 
2294     return STATUS_SUCCESS;
2295 }
2296 
2297 ULONG
2298 NTAPI
2299 CmCountOpenSubKeys(IN PCM_KEY_CONTROL_BLOCK RootKcb,
2300                    IN BOOLEAN RemoveEmptyCacheEntries)
2301 {
2302     PCM_KEY_HASH Entry;
2303     PCM_KEY_CONTROL_BLOCK CachedKcb;
2304     PCM_KEY_CONTROL_BLOCK ParentKcb;
2305     ULONG ParentKeyCount;
2306     ULONG i, j;
2307     ULONG SubKeys = 0;
2308 
2309     DPRINT("CmCountOpenSubKeys() called\n");
2310 
2311     /* The root key is the only referenced key. There are no refereced sub keys. */
2312     if (RootKcb->RefCount == 1)
2313     {
2314         DPRINT("Open sub keys: 0\n");
2315         return 0;
2316     }
2317 
2318     /* Enumerate all hash lists */
2319     for (i = 0; i < CmpHashTableSize; i++)
2320     {
2321         /* Get the first cache entry */
2322         Entry = CmpCacheTable[i].Entry;
2323 
2324         /* Enumerate all cache entries */
2325         while (Entry)
2326         {
2327             /* Get the KCB of the current cache entry */
2328             CachedKcb = CONTAINING_RECORD(Entry, CM_KEY_CONTROL_BLOCK, KeyHash);
2329 
2330             /* Check keys only that are subkeys to our root key */
2331             if (CachedKcb->TotalLevels > RootKcb->TotalLevels)
2332             {
2333                 /* Calculate the number of parent keys to the root key */
2334                 ParentKeyCount = CachedKcb->TotalLevels - RootKcb->TotalLevels;
2335 
2336                 /* Find a parent key that could be the root key */
2337                 ParentKcb = CachedKcb;
2338                 for (j = 0; j < ParentKeyCount; j++)
2339                 {
2340                     ParentKcb = ParentKcb->ParentKcb;
2341                 }
2342 
2343                 /* Check whether the parent is the root key */
2344                 if (ParentKcb == RootKcb)
2345                 {
2346                     DPRINT("Found a sub key, RefCount = %u\n", CachedKcb->RefCount);
2347 
2348                     if (CachedKcb->RefCount > 0)
2349                     {
2350                         /* Count the current hash entry if it is in use */
2351                         SubKeys++;
2352                     }
2353                     else if ((CachedKcb->RefCount == 0) && (RemoveEmptyCacheEntries != FALSE))
2354                     {
2355                         /* Remove the current key from the delayed close list */
2356                         CmpRemoveFromDelayedClose(CachedKcb);
2357 
2358                         /* Remove the current cache entry */
2359                         CmpCleanUpKcbCacheWithLock(CachedKcb, TRUE);
2360 
2361                         /* Restart, because the hash list has changed */
2362                         Entry = CmpCacheTable[i].Entry;
2363                         continue;
2364                     }
2365                 }
2366             }
2367 
2368             /* Get the next cache entry */
2369             Entry = Entry->NextHash;
2370         }
2371     }
2372 
2373     DPRINT("Open sub keys: %u\n", SubKeys);
2374     return SubKeys;
2375 }
2376 
2377 static
2378 NTSTATUS
2379 CmpDeepCopyKeyInternal(IN PHHIVE SourceHive,
2380                        IN HCELL_INDEX SrcKeyCell,
2381                        IN PHHIVE DestinationHive,
2382                        IN HCELL_INDEX Parent,
2383                        IN HSTORAGE_TYPE StorageType,
2384                        OUT PHCELL_INDEX DestKeyCell OPTIONAL)
2385 {
2386     NTSTATUS Status;
2387     PCM_KEY_NODE SrcNode;
2388     PCM_KEY_NODE DestNode = NULL;
2389     HCELL_INDEX NewKeyCell = HCELL_NIL;
2390     HCELL_INDEX NewClassCell = HCELL_NIL, NewSecCell = HCELL_NIL;
2391     HCELL_INDEX SubKey, NewSubKey;
2392     ULONG Index, SubKeyCount;
2393 
2394     PAGED_CODE();
2395 
2396     DPRINT("CmpDeepCopyKeyInternal(0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X)\n",
2397            SourceHive,
2398            SrcKeyCell,
2399            DestinationHive,
2400            Parent,
2401            StorageType,
2402            DestKeyCell);
2403 
2404     /* Get the source cell node */
2405     SrcNode = HvGetCell(SourceHive, SrcKeyCell);
2406     ASSERT(SrcNode);
2407 
2408     /* Sanity check */
2409     ASSERT(SrcNode->Signature == CM_KEY_NODE_SIGNATURE);
2410 
2411     /* Create a simple copy of the source key */
2412     NewKeyCell = CmpCopyCell(SourceHive,
2413                              SrcKeyCell,
2414                              DestinationHive,
2415                              StorageType);
2416     if (NewKeyCell == HCELL_NIL)
2417     {
2418         /* Not enough storage space */
2419         Status = STATUS_INSUFFICIENT_RESOURCES;
2420         goto Cleanup;
2421     }
2422 
2423     /* Get the destination cell node */
2424     DestNode = HvGetCell(DestinationHive, NewKeyCell);
2425     ASSERT(DestNode);
2426 
2427     /* Set the parent and copy the flags */
2428     DestNode->Parent = Parent;
2429     DestNode->Flags  = (SrcNode->Flags & KEY_COMP_NAME); // Keep only the single permanent flag
2430     if (Parent == HCELL_NIL)
2431     {
2432         /* This is the new root node */
2433         DestNode->Flags |= KEY_HIVE_ENTRY | KEY_NO_DELETE;
2434     }
2435 
2436     /* Copy the class cell */
2437     if (SrcNode->ClassLength > 0)
2438     {
2439         NewClassCell = CmpCopyCell(SourceHive,
2440                                    SrcNode->Class,
2441                                    DestinationHive,
2442                                    StorageType);
2443         if (NewClassCell == HCELL_NIL)
2444         {
2445             /* Not enough storage space */
2446             Status = STATUS_INSUFFICIENT_RESOURCES;
2447             goto Cleanup;
2448         }
2449 
2450         DestNode->Class = NewClassCell;
2451         DestNode->ClassLength = SrcNode->ClassLength;
2452     }
2453     else
2454     {
2455         DestNode->Class = HCELL_NIL;
2456         DestNode->ClassLength = 0;
2457     }
2458 
2459     /* Copy the security cell (FIXME: HACKish poor-man version) */
2460     if (SrcNode->Security != HCELL_NIL)
2461     {
2462         NewSecCell = CmpCopyCell(SourceHive,
2463                                  SrcNode->Security,
2464                                  DestinationHive,
2465                                  StorageType);
2466         if (NewSecCell == HCELL_NIL)
2467         {
2468             /* Not enough storage space */
2469             Status = STATUS_INSUFFICIENT_RESOURCES;
2470             goto Cleanup;
2471         }
2472     }
2473     DestNode->Security = NewSecCell;
2474 
2475     /* Copy the value list */
2476     Status = CmpCopyKeyValueList(SourceHive,
2477                                  &SrcNode->ValueList,
2478                                  DestinationHive,
2479                                  &DestNode->ValueList,
2480                                  StorageType);
2481     if (!NT_SUCCESS(Status))
2482         goto Cleanup;
2483 
2484     /* Clear the invalid subkey index */
2485     DestNode->SubKeyCounts[Stable] = DestNode->SubKeyCounts[Volatile] = 0;
2486     DestNode->SubKeyLists[Stable] = DestNode->SubKeyLists[Volatile] = HCELL_NIL;
2487 
2488     /* Calculate the total number of subkeys */
2489     SubKeyCount = SrcNode->SubKeyCounts[Stable] + SrcNode->SubKeyCounts[Volatile];
2490 
2491     /* Loop through all the subkeys */
2492     for (Index = 0; Index < SubKeyCount; Index++)
2493     {
2494         /* Get the subkey */
2495         SubKey = CmpFindSubKeyByNumber(SourceHive, SrcNode, Index);
2496         ASSERT(SubKey != HCELL_NIL);
2497 
2498         /* Call the function recursively for the subkey */
2499         //
2500         // FIXME: Danger!! Kernel stack exhaustion!!
2501         //
2502         Status = CmpDeepCopyKeyInternal(SourceHive,
2503                                         SubKey,
2504                                         DestinationHive,
2505                                         NewKeyCell,
2506                                         StorageType,
2507                                         &NewSubKey);
2508         if (!NT_SUCCESS(Status))
2509             goto Cleanup;
2510 
2511         /* Add the copy of the subkey to the new key */
2512         if (!CmpAddSubKey(DestinationHive,
2513                           NewKeyCell,
2514                           NewSubKey))
2515         {
2516             /* Cleanup allocated cell */
2517             HvFreeCell(DestinationHive, NewSubKey);
2518 
2519             Status = STATUS_INSUFFICIENT_RESOURCES;
2520             goto Cleanup;
2521         }
2522     }
2523 
2524     /* Set success */
2525     Status = STATUS_SUCCESS;
2526 
2527 Cleanup:
2528 
2529     /* Release the cells */
2530     if (DestNode) HvReleaseCell(DestinationHive, NewKeyCell);
2531     if (SrcNode) HvReleaseCell(SourceHive, SrcKeyCell);
2532 
2533     /* Cleanup allocated cells in case of failure */
2534     if (!NT_SUCCESS(Status))
2535     {
2536         if (NewSecCell != HCELL_NIL)
2537             HvFreeCell(DestinationHive, NewSecCell);
2538 
2539         if (NewClassCell != HCELL_NIL)
2540             HvFreeCell(DestinationHive, NewClassCell);
2541 
2542         if (NewKeyCell != HCELL_NIL)
2543             HvFreeCell(DestinationHive, NewKeyCell);
2544 
2545         NewKeyCell = HCELL_NIL;
2546     }
2547 
2548     /* Set the cell index if requested and return status */
2549     if (DestKeyCell) *DestKeyCell = NewKeyCell;
2550     return Status;
2551 }
2552 
2553 NTSTATUS
2554 NTAPI
2555 CmpDeepCopyKey(IN PHHIVE SourceHive,
2556                IN HCELL_INDEX SrcKeyCell,
2557                IN PHHIVE DestinationHive,
2558                IN HSTORAGE_TYPE StorageType,
2559                OUT PHCELL_INDEX DestKeyCell OPTIONAL)
2560 {
2561     /* Call the internal function */
2562     return CmpDeepCopyKeyInternal(SourceHive,
2563                                   SrcKeyCell,
2564                                   DestinationHive,
2565                                   HCELL_NIL,
2566                                   StorageType,
2567                                   DestKeyCell);
2568 }
2569 
2570 NTSTATUS
2571 NTAPI
2572 CmSaveKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
2573           IN HANDLE FileHandle,
2574           IN ULONG Flags)
2575 {
2576     NTSTATUS Status = STATUS_SUCCESS;
2577     PCMHIVE KeyHive = NULL;
2578     PAGED_CODE();
2579 
2580     DPRINT("CmSaveKey(0x%08X, 0x%08X, %lu)\n", Kcb, FileHandle, Flags);
2581 
2582     /* Lock the registry and KCB */
2583     CmpLockRegistry();
2584     CmpAcquireKcbLockShared(Kcb);
2585 
2586     if (Kcb->Delete)
2587     {
2588         /* The source key has been deleted, do nothing */
2589         Status = STATUS_KEY_DELETED;
2590         goto Cleanup;
2591     }
2592 
2593     if (Kcb->KeyHive == &CmiVolatileHive->Hive)
2594     {
2595         /* Keys that are directly in the master hive can't be saved */
2596         Status = STATUS_ACCESS_DENIED;
2597         goto Cleanup;
2598     }
2599 
2600     /* Create a new hive that will hold the key */
2601     Status = CmpInitializeHive(&KeyHive,
2602                                HINIT_CREATE,
2603                                HIVE_VOLATILE,
2604                                HFILE_TYPE_PRIMARY,
2605                                NULL,
2606                                NULL,
2607                                NULL,
2608                                NULL,
2609                                NULL,
2610                                0);
2611     if (!NT_SUCCESS(Status)) goto Cleanup;
2612 
2613     /* Copy the key recursively into the new hive */
2614     Status = CmpDeepCopyKey(Kcb->KeyHive,
2615                             Kcb->KeyCell,
2616                             &KeyHive->Hive,
2617                             Stable,
2618                             &KeyHive->Hive.BaseBlock->RootCell);
2619     if (!NT_SUCCESS(Status)) goto Cleanup;
2620 
2621     /* Set the primary handle of the hive */
2622     KeyHive->FileHandles[HFILE_TYPE_PRIMARY] = FileHandle;
2623 
2624     /* Dump the hive into the file */
2625     HvWriteHive(&KeyHive->Hive);
2626 
2627 Cleanup:
2628 
2629     /* Free the hive */
2630     if (KeyHive) CmpDestroyHive(KeyHive);
2631 
2632     /* Release the locks */
2633     CmpReleaseKcbLock(Kcb);
2634     CmpUnlockRegistry();
2635 
2636     return Status;
2637 }
2638 
2639 NTSTATUS
2640 NTAPI
2641 CmSaveMergedKeys(IN PCM_KEY_CONTROL_BLOCK HighKcb,
2642                  IN PCM_KEY_CONTROL_BLOCK LowKcb,
2643                  IN HANDLE FileHandle)
2644 {
2645     PCMHIVE KeyHive = NULL;
2646     NTSTATUS Status = STATUS_SUCCESS;
2647 
2648     PAGED_CODE();
2649 
2650     DPRINT("CmSaveKey(%p, %p, %p)\n", HighKcb, LowKcb, FileHandle);
2651 
2652     /* Lock the registry and the KCBs */
2653     CmpLockRegistry();
2654     CmpAcquireKcbLockShared(HighKcb);
2655     CmpAcquireKcbLockShared(LowKcb);
2656 
2657     if (LowKcb->Delete || HighKcb->Delete)
2658     {
2659         /* The source key has been deleted, do nothing */
2660         Status = STATUS_KEY_DELETED;
2661         goto done;
2662     }
2663 
2664     /* Create a new hive that will hold the key */
2665     Status = CmpInitializeHive(&KeyHive,
2666                                HINIT_CREATE,
2667                                HIVE_VOLATILE,
2668                                HFILE_TYPE_PRIMARY,
2669                                NULL,
2670                                NULL,
2671                                NULL,
2672                                NULL,
2673                                NULL,
2674                                0);
2675     if (!NT_SUCCESS(Status))
2676         goto done;
2677 
2678     /* Copy the low precedence key recursively into the new hive */
2679     Status = CmpDeepCopyKey(LowKcb->KeyHive,
2680                             LowKcb->KeyCell,
2681                             &KeyHive->Hive,
2682                             Stable,
2683                             &KeyHive->Hive.BaseBlock->RootCell);
2684     if (!NT_SUCCESS(Status))
2685         goto done;
2686 
2687     /* Copy the high precedence key recursively into the new hive */
2688     Status = CmpDeepCopyKey(HighKcb->KeyHive,
2689                             HighKcb->KeyCell,
2690                             &KeyHive->Hive,
2691                             Stable,
2692                             &KeyHive->Hive.BaseBlock->RootCell);
2693     if (!NT_SUCCESS(Status))
2694         goto done;
2695 
2696     /* Set the primary handle of the hive */
2697     KeyHive->FileHandles[HFILE_TYPE_PRIMARY] = FileHandle;
2698 
2699     /* Dump the hive into the file */
2700     HvWriteHive(&KeyHive->Hive);
2701 
2702 done:
2703     /* Free the hive */
2704     if (KeyHive)
2705         CmpDestroyHive(KeyHive);
2706 
2707     /* Release the locks */
2708     CmpReleaseKcbLock(LowKcb);
2709     CmpReleaseKcbLock(HighKcb);
2710     CmpUnlockRegistry();
2711 
2712     return Status;
2713 }
2714