xref: /reactos/ntoskrnl/config/cmapi.c (revision ebaf247c)
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 Quickie;
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 Quickie:
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 
1963         ASSERT(CmHive->ViewLock);
1964         KeAcquireGuardedMutex(CmHive->ViewLock);
1965         CmHive->ViewLockOwner = KeGetCurrentThread();
1966 
1967         /* Will the hive shrink? */
1968         if (HvHiveWillShrink(Hive))
1969         {
1970             /* I don't believe the current Hv does shrinking */
1971             ASSERT(FALSE);
1972             // CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK_OR_LOADING(CmHive);
1973         }
1974         else
1975         {
1976             /* Now we can release views */
1977             ASSERT(CmHive->ViewLock);
1978             // CMP_ASSERT_VIEW_LOCK_OWNED(CmHive);
1979             ASSERT((CmpSpecialBootCondition == TRUE) ||
1980                    (CmHive->HiveIsLoading == TRUE) ||
1981                    (CmHive->ViewLockOwner == KeGetCurrentThread()) ||
1982                    (CmpTestRegistryLockExclusive() == TRUE));
1983             CmHive->ViewLockOwner = NULL;
1984             KeReleaseGuardedMutex(CmHive->ViewLock);
1985         }
1986 
1987         /* Flush only this hive */
1988         if (!HvSyncHive(Hive))
1989         {
1990             /* Fail */
1991             Status = STATUS_REGISTRY_IO_FAILED;
1992         }
1993 
1994         /* Release the flush lock */
1995         CmpUnlockHiveFlusher(CmHive);
1996     }
1997 
1998     /* Return the status */
1999     return Status;
2000 }
2001 
2002 NTSTATUS
2003 NTAPI
2004 CmLoadKey(IN POBJECT_ATTRIBUTES TargetKey,
2005           IN POBJECT_ATTRIBUTES SourceFile,
2006           IN ULONG Flags,
2007           IN PCM_KEY_BODY KeyBody)
2008 {
2009     SECURITY_QUALITY_OF_SERVICE ServiceQos;
2010     SECURITY_CLIENT_CONTEXT ClientSecurityContext;
2011     HANDLE KeyHandle;
2012     BOOLEAN Allocate = TRUE;
2013     PCMHIVE CmHive, LoadedHive;
2014     NTSTATUS Status;
2015     CM_PARSE_CONTEXT ParseContext;
2016 
2017     /* Check if we have a trust key */
2018     if (KeyBody)
2019     {
2020         /* Fail */
2021         DPRINT("Trusted classes not yet supported\n");
2022     }
2023 
2024     /* Build a service QoS for a security context */
2025     ServiceQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
2026     ServiceQos.ImpersonationLevel = SecurityImpersonation;
2027     ServiceQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
2028     ServiceQos.EffectiveOnly = TRUE;
2029     Status = SeCreateClientSecurity(PsGetCurrentThread(),
2030                                     &ServiceQos,
2031                                     FALSE,
2032                                     &ClientSecurityContext);
2033     if (!NT_SUCCESS(Status))
2034     {
2035         /* Fail */
2036         DPRINT1("Security context failed\n");
2037         return Status;
2038     }
2039 
2040     /* Open the target key */
2041     RtlZeroMemory(&ParseContext, sizeof(ParseContext));
2042     ParseContext.CreateOperation = FALSE;
2043     Status = ObOpenObjectByName(TargetKey,
2044                                 CmpKeyObjectType,
2045                                 KernelMode,
2046                                 NULL,
2047                                 KEY_READ,
2048                                 &ParseContext,
2049                                 &KeyHandle);
2050     if (!NT_SUCCESS(Status)) KeyHandle = NULL;
2051 
2052     /* Open the hive */
2053     Status = CmpCmdHiveOpen(SourceFile,
2054                             &ClientSecurityContext,
2055                             &Allocate,
2056                             &CmHive,
2057                             0);
2058 
2059     /* Get rid of the security context */
2060     SeDeleteClientSecurity(&ClientSecurityContext);
2061 
2062     /* See if we failed */
2063     if (!NT_SUCCESS(Status))
2064     {
2065         /* See if the target already existed */
2066         if (KeyHandle)
2067         {
2068             /* Lock the registry */
2069             CmpLockRegistryExclusive();
2070 
2071             /* Check if we are already loaded */
2072             if (CmpIsHiveAlreadyLoaded(KeyHandle, SourceFile, &LoadedHive))
2073             {
2074                 /* That's okay then */
2075                 ASSERT(LoadedHive);
2076                 Status = STATUS_SUCCESS;
2077             }
2078 
2079             /* Release the registry */
2080             CmpUnlockRegistry();
2081         }
2082 
2083         /* Close the key handle if we had one */
2084         if (KeyHandle) ZwClose(KeyHandle);
2085         return Status;
2086     }
2087 
2088     /* Lock the registry shared */
2089     CmpLockRegistry();
2090 
2091     /* Lock loading */
2092     ExAcquirePushLockExclusive(&CmpLoadHiveLock);
2093 
2094     /* Lock the hive to this thread */
2095     CmHive->Hive.HiveFlags |= HIVE_IS_UNLOADING;
2096     CmHive->CreatorOwner = KeGetCurrentThread();
2097 
2098     /* Set flag */
2099     if (Flags & REG_NO_LAZY_FLUSH) CmHive->Hive.HiveFlags |= HIVE_NOLAZYFLUSH;
2100 
2101     /* Link the hive */
2102     Status = CmpLinkHiveToMaster(TargetKey->ObjectName,
2103                                  TargetKey->RootDirectory,
2104                                  CmHive,
2105                                  Allocate,
2106                                  TargetKey->SecurityDescriptor);
2107     if (NT_SUCCESS(Status))
2108     {
2109         /* Add to HiveList key */
2110         CmpAddToHiveFileList(CmHive);
2111 
2112         /* Sync the hive if necessary */
2113         if (Allocate)
2114         {
2115             /* Sync it under the flusher lock */
2116             CmpLockHiveFlusherExclusive(CmHive);
2117             HvSyncHive(&CmHive->Hive);
2118             CmpUnlockHiveFlusher(CmHive);
2119         }
2120 
2121         /* Release the hive */
2122         CmHive->Hive.HiveFlags &= ~HIVE_IS_UNLOADING;
2123         CmHive->CreatorOwner = NULL;
2124 
2125         /* Allow loads */
2126         ExReleasePushLock(&CmpLoadHiveLock);
2127     }
2128     else
2129     {
2130         DPRINT1("CmpLinkHiveToMaster failed, Status %lx\n", Status);
2131         /* FIXME: TODO */
2132         ASSERT(FALSE);
2133     }
2134 
2135     /* Is this first profile load? */
2136     if (!CmpProfileLoaded && !CmpWasSetupBoot)
2137     {
2138         /* User is now logged on, set quotas */
2139         CmpProfileLoaded = TRUE;
2140         CmpSetGlobalQuotaAllowed();
2141     }
2142 
2143     /* Unlock the registry */
2144     CmpUnlockRegistry();
2145 
2146     /* Close handle and return */
2147     if (KeyHandle) ZwClose(KeyHandle);
2148     return Status;
2149 }
2150 
2151 static
2152 BOOLEAN
2153 NTAPI
2154 CmpUnlinkHiveFromMaster(IN PCMHIVE CmHive,
2155                         IN HCELL_INDEX Cell)
2156 {
2157     PCELL_DATA CellData;
2158     HCELL_INDEX LinkCell;
2159     NTSTATUS Status;
2160 
2161     DPRINT("CmpUnlinkHiveFromMaster()\n");
2162 
2163     /* Get the cell data */
2164     CellData = HvGetCell(&CmHive->Hive, Cell);
2165     if (CellData == NULL)
2166         return FALSE;
2167 
2168     /* Get the link cell and release the current cell */
2169     LinkCell = CellData->u.KeyNode.Parent;
2170     HvReleaseCell(&CmHive->Hive, Cell);
2171 
2172     /* Remove the link cell from the master hive */
2173     CmpLockHiveFlusherExclusive(CmiVolatileHive);
2174     Status = CmpFreeKeyByCell((PHHIVE)CmiVolatileHive,
2175                               LinkCell,
2176                               TRUE);
2177     CmpUnlockHiveFlusher(CmiVolatileHive);
2178     if (!NT_SUCCESS(Status))
2179     {
2180         DPRINT1("CmpFreeKeyByCell() failed (Status 0x%08lx)\n", Status);
2181         return FALSE;
2182     }
2183 
2184     /* Remove the hive from the list */
2185     ExAcquirePushLockExclusive(&CmpHiveListHeadLock);
2186     RemoveEntryList(&CmHive->HiveList);
2187     ExReleasePushLock(&CmpHiveListHeadLock);
2188 
2189     return TRUE;
2190 }
2191 
2192 NTSTATUS
2193 NTAPI
2194 CmUnloadKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
2195             IN ULONG Flags)
2196 {
2197     PHHIVE Hive;
2198     PCMHIVE CmHive;
2199     HCELL_INDEX Cell;
2200 
2201     DPRINT("CmUnloadKey(%p, %lx)\n", Kcb, Flags);
2202 
2203     /* Get the hive */
2204     Hive = Kcb->KeyHive;
2205     Cell = Kcb->KeyCell;
2206     CmHive = (PCMHIVE)Hive;
2207 
2208     /* Fail if the key is not a hive root key */
2209     if (Cell != Hive->BaseBlock->RootCell)
2210     {
2211         DPRINT1("Key is not a hive root key!\n");
2212         return STATUS_INVALID_PARAMETER;
2213     }
2214 
2215     /* Fail if we try to unload the master hive */
2216     if (CmHive == CmiVolatileHive)
2217     {
2218         DPRINT1("Do not try to unload the master hive!\n");
2219         return STATUS_INVALID_PARAMETER;
2220     }
2221 
2222     /* Mark this hive as being unloaded */
2223     Hive->HiveFlags |= HIVE_IS_UNLOADING;
2224 
2225     /* Search for any opened keys in this hive, and take an appropriate action */
2226     if (Kcb->RefCount > 1)
2227     {
2228         if (Flags != REG_FORCE_UNLOAD)
2229         {
2230             if (CmpEnumerateOpenSubKeys(Kcb, FALSE, FALSE) != 0)
2231             {
2232                 /* There are open subkeys but we don't force hive unloading, fail */
2233                 Hive->HiveFlags &= ~HIVE_IS_UNLOADING;
2234                 return STATUS_CANNOT_DELETE;
2235             }
2236         }
2237         else
2238         {
2239             DPRINT1("CmUnloadKey: Force unloading is HALF-IMPLEMENTED, expect dangling KCBs problems!\n");
2240             if (CmpEnumerateOpenSubKeys(Kcb, TRUE, TRUE) != 0)
2241             {
2242                 /* There are open subkeys that we cannot force to unload, fail */
2243                 Hive->HiveFlags &= ~HIVE_IS_UNLOADING;
2244                 return STATUS_CANNOT_DELETE;
2245             }
2246         }
2247     }
2248 
2249     /* Set the loading flag */
2250     CmHive->HiveIsLoading = TRUE;
2251 
2252     /* Flush the hive */
2253     CmFlushKey(Kcb, TRUE);
2254 
2255     /* Unlink the hive from the master hive */
2256     if (!CmpUnlinkHiveFromMaster(CmHive, Cell))
2257     {
2258         DPRINT("CmpUnlinkHiveFromMaster() failed!\n");
2259 
2260         /* Remove the unloading flag */
2261         Hive->HiveFlags &= ~HIVE_IS_UNLOADING;
2262 
2263         /* Reset the loading flag */
2264         CmHive->HiveIsLoading = FALSE;
2265 
2266         /* Return failure */
2267         return STATUS_INSUFFICIENT_RESOURCES;
2268     }
2269 
2270     /* Flush any notifications if we force hive unloading */
2271     if (Flags == REG_FORCE_UNLOAD)
2272         CmpFlushNotifiesOnKeyBodyList(Kcb, TRUE); // Lock is already held
2273 
2274     /* Clean up information we have on the subkey */
2275     CmpCleanUpSubKeyInfo(Kcb->ParentKcb);
2276 
2277     /* Set the KCB in delete mode and remove it */
2278     Kcb->Delete = TRUE;
2279     CmpRemoveKeyControlBlock(Kcb);
2280 
2281     if (Flags != REG_FORCE_UNLOAD)
2282     {
2283         /* Release the KCB locks */
2284         CmpReleaseTwoKcbLockByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey);
2285 
2286         /* Release the hive loading lock */
2287         ExReleasePushLockExclusive(&CmpLoadHiveLock);
2288     }
2289 
2290     /* Release hive lock */
2291     CmpUnlockRegistry();
2292 
2293     /* Close file handles */
2294     CmpCloseHiveFiles(CmHive);
2295 
2296     /* Remove the hive from the hive file list */
2297     CmpRemoveFromHiveFileList(CmHive);
2298 
2299 /**
2300  ** NOTE:
2301  ** The following code is mostly equivalent to what we "call" CmpDestroyHive()
2302  **/
2303     /* Destroy the security descriptor cache */
2304     CmpDestroySecurityCache(CmHive);
2305 
2306     /* Destroy the view list */
2307     CmpDestroyHiveViewList(CmHive);
2308 
2309     /* Delete the flusher lock */
2310     ExDeleteResourceLite(CmHive->FlusherLock);
2311     ExFreePoolWithTag(CmHive->FlusherLock, TAG_CMHIVE);
2312 
2313     /* Delete the view lock */
2314     ExFreePoolWithTag(CmHive->ViewLock, TAG_CMHIVE);
2315 
2316     /* Free the hive storage */
2317     HvFree(Hive);
2318 
2319     /* Free the hive */
2320     CmpFree(CmHive, TAG_CM);
2321 
2322     return STATUS_SUCCESS;
2323 }
2324 
2325 ULONG
2326 NTAPI
2327 CmpEnumerateOpenSubKeys(
2328     IN PCM_KEY_CONTROL_BLOCK RootKcb,
2329     IN BOOLEAN RemoveEmptyCacheEntries,
2330     IN BOOLEAN DereferenceOpenedEntries)
2331 {
2332     PCM_KEY_HASH Entry;
2333     PCM_KEY_CONTROL_BLOCK CachedKcb;
2334     PCM_KEY_CONTROL_BLOCK ParentKcb;
2335     ULONG ParentKeyCount;
2336     ULONG i, j;
2337     ULONG SubKeys = 0;
2338 
2339     DPRINT("CmpEnumerateOpenSubKeys() called\n");
2340 
2341     /* The root key is the only referenced key. There are no referenced sub keys. */
2342     if (RootKcb->RefCount == 1)
2343     {
2344         DPRINT("Open sub keys: 0\n");
2345         return 0;
2346     }
2347 
2348     /* Enumerate all hash lists */
2349     for (i = 0; i < CmpHashTableSize; i++)
2350     {
2351         /* Get the first cache entry */
2352         Entry = CmpCacheTable[i].Entry;
2353 
2354         /* Enumerate all cache entries */
2355         while (Entry)
2356         {
2357             /* Get the KCB of the current cache entry */
2358             CachedKcb = CONTAINING_RECORD(Entry, CM_KEY_CONTROL_BLOCK, KeyHash);
2359 
2360             /* Check keys only that are subkeys to our root key */
2361             if (CachedKcb->TotalLevels > RootKcb->TotalLevels)
2362             {
2363                 /* Calculate the number of parent keys to the root key */
2364                 ParentKeyCount = CachedKcb->TotalLevels - RootKcb->TotalLevels;
2365 
2366                 /* Find a parent key that could be the root key */
2367                 ParentKcb = CachedKcb;
2368                 for (j = 0; j < ParentKeyCount; j++)
2369                 {
2370                     ParentKcb = ParentKcb->ParentKcb;
2371                 }
2372 
2373                 /* Check whether the parent is the root key */
2374                 if (ParentKcb == RootKcb)
2375                 {
2376                     DPRINT("Found a sub key, RefCount = %u\n", CachedKcb->RefCount);
2377 
2378                     if (CachedKcb->RefCount > 0)
2379                     {
2380                         DPRINT("Found a sub key pointing to '%.*s', RefCount = %u\n",
2381                                CachedKcb->NameBlock->NameLength, CachedKcb->NameBlock->Name,
2382                                CachedKcb->RefCount);
2383 
2384                         /* If we dereference opened KCBs, don't touch read-only keys */
2385                         if (DereferenceOpenedEntries &&
2386                             !(CachedKcb->ExtFlags & CM_KCB_READ_ONLY_KEY))
2387                         {
2388                             /* Registry needs to be locked down */
2389                             CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK();
2390 
2391                             /* Flush any notifications */
2392                             CmpFlushNotifiesOnKeyBodyList(CachedKcb, TRUE); // Lock is already held
2393 
2394                             /* Clean up information we have on the subkey */
2395                             CmpCleanUpSubKeyInfo(CachedKcb->ParentKcb);
2396 
2397                             /* Get and cache the next cache entry */
2398                             // Entry = Entry->NextHash;
2399                             Entry = CachedKcb->NextHash;
2400 
2401                             /* Set the KCB in delete mode and remove it */
2402                             CachedKcb->Delete = TRUE;
2403                             CmpRemoveKeyControlBlock(CachedKcb);
2404 
2405                             /* Clear the cell */
2406                             CachedKcb->KeyCell = HCELL_NIL;
2407 
2408                             /* Restart with the next cache entry */
2409                             continue;
2410                         }
2411                         /* Else, the key cannot be dereferenced, and we count it as in use */
2412 
2413                         /* Count the current hash entry if it is in use */
2414                         SubKeys++;
2415                     }
2416                     else if ((CachedKcb->RefCount == 0) && RemoveEmptyCacheEntries)
2417                     {
2418                         /* Remove the current key from the delayed close list */
2419                         CmpRemoveFromDelayedClose(CachedKcb);
2420 
2421                         /* Remove the current cache entry */
2422                         CmpCleanUpKcbCacheWithLock(CachedKcb, TRUE);
2423 
2424                         /* Restart, because the hash list has changed */
2425                         Entry = CmpCacheTable[i].Entry;
2426                         continue;
2427                     }
2428                 }
2429             }
2430 
2431             /* Get the next cache entry */
2432             Entry = Entry->NextHash;
2433         }
2434     }
2435 
2436     if (SubKeys > 0)
2437         DPRINT1("Open sub keys: %u\n", SubKeys);
2438 
2439     return SubKeys;
2440 }
2441 
2442 static
2443 NTSTATUS
2444 CmpDeepCopyKeyInternal(IN PHHIVE SourceHive,
2445                        IN HCELL_INDEX SrcKeyCell,
2446                        IN PHHIVE DestinationHive,
2447                        IN HCELL_INDEX Parent,
2448                        IN HSTORAGE_TYPE StorageType,
2449                        OUT PHCELL_INDEX DestKeyCell OPTIONAL)
2450 {
2451     NTSTATUS Status;
2452     PCM_KEY_NODE SrcNode;
2453     PCM_KEY_NODE DestNode = NULL;
2454     HCELL_INDEX NewKeyCell = HCELL_NIL;
2455     HCELL_INDEX NewClassCell = HCELL_NIL, NewSecCell = HCELL_NIL;
2456     HCELL_INDEX SubKey, NewSubKey;
2457     ULONG Index, SubKeyCount;
2458 
2459     PAGED_CODE();
2460 
2461     DPRINT("CmpDeepCopyKeyInternal(0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X)\n",
2462            SourceHive,
2463            SrcKeyCell,
2464            DestinationHive,
2465            Parent,
2466            StorageType,
2467            DestKeyCell);
2468 
2469     /* Get the source cell node */
2470     SrcNode = HvGetCell(SourceHive, SrcKeyCell);
2471     ASSERT(SrcNode);
2472 
2473     /* Sanity check */
2474     ASSERT(SrcNode->Signature == CM_KEY_NODE_SIGNATURE);
2475 
2476     /* Create a simple copy of the source key */
2477     NewKeyCell = CmpCopyCell(SourceHive,
2478                              SrcKeyCell,
2479                              DestinationHive,
2480                              StorageType);
2481     if (NewKeyCell == HCELL_NIL)
2482     {
2483         /* Not enough storage space */
2484         Status = STATUS_INSUFFICIENT_RESOURCES;
2485         goto Cleanup;
2486     }
2487 
2488     /* Get the destination cell node */
2489     DestNode = HvGetCell(DestinationHive, NewKeyCell);
2490     ASSERT(DestNode);
2491 
2492     /* Set the parent and copy the flags */
2493     DestNode->Parent = Parent;
2494     DestNode->Flags  = (SrcNode->Flags & KEY_COMP_NAME); // Keep only the single permanent flag
2495     if (Parent == HCELL_NIL)
2496     {
2497         /* This is the new root node */
2498         DestNode->Flags |= KEY_HIVE_ENTRY | KEY_NO_DELETE;
2499     }
2500 
2501     /* Copy the class cell */
2502     if (SrcNode->ClassLength > 0)
2503     {
2504         NewClassCell = CmpCopyCell(SourceHive,
2505                                    SrcNode->Class,
2506                                    DestinationHive,
2507                                    StorageType);
2508         if (NewClassCell == HCELL_NIL)
2509         {
2510             /* Not enough storage space */
2511             Status = STATUS_INSUFFICIENT_RESOURCES;
2512             goto Cleanup;
2513         }
2514 
2515         DestNode->Class = NewClassCell;
2516         DestNode->ClassLength = SrcNode->ClassLength;
2517     }
2518     else
2519     {
2520         DestNode->Class = HCELL_NIL;
2521         DestNode->ClassLength = 0;
2522     }
2523 
2524     /* Copy the security cell (FIXME: HACKish poor-man version) */
2525     if (SrcNode->Security != HCELL_NIL)
2526     {
2527         NewSecCell = CmpCopyCell(SourceHive,
2528                                  SrcNode->Security,
2529                                  DestinationHive,
2530                                  StorageType);
2531         if (NewSecCell == HCELL_NIL)
2532         {
2533             /* Not enough storage space */
2534             Status = STATUS_INSUFFICIENT_RESOURCES;
2535             goto Cleanup;
2536         }
2537     }
2538     DestNode->Security = NewSecCell;
2539 
2540     /* Copy the value list */
2541     Status = CmpCopyKeyValueList(SourceHive,
2542                                  &SrcNode->ValueList,
2543                                  DestinationHive,
2544                                  &DestNode->ValueList,
2545                                  StorageType);
2546     if (!NT_SUCCESS(Status))
2547         goto Cleanup;
2548 
2549     /* Clear the invalid subkey index */
2550     DestNode->SubKeyCounts[Stable] = DestNode->SubKeyCounts[Volatile] = 0;
2551     DestNode->SubKeyLists[Stable] = DestNode->SubKeyLists[Volatile] = HCELL_NIL;
2552 
2553     /* Calculate the total number of subkeys */
2554     SubKeyCount = SrcNode->SubKeyCounts[Stable] + SrcNode->SubKeyCounts[Volatile];
2555 
2556     /* Loop through all the subkeys */
2557     for (Index = 0; Index < SubKeyCount; Index++)
2558     {
2559         /* Get the subkey */
2560         SubKey = CmpFindSubKeyByNumber(SourceHive, SrcNode, Index);
2561         ASSERT(SubKey != HCELL_NIL);
2562 
2563         /* Call the function recursively for the subkey */
2564         //
2565         // FIXME: Danger!! Kernel stack exhaustion!!
2566         //
2567         Status = CmpDeepCopyKeyInternal(SourceHive,
2568                                         SubKey,
2569                                         DestinationHive,
2570                                         NewKeyCell,
2571                                         StorageType,
2572                                         &NewSubKey);
2573         if (!NT_SUCCESS(Status))
2574             goto Cleanup;
2575 
2576         /* Add the copy of the subkey to the new key */
2577         if (!CmpAddSubKey(DestinationHive,
2578                           NewKeyCell,
2579                           NewSubKey))
2580         {
2581             /* Cleanup allocated cell */
2582             HvFreeCell(DestinationHive, NewSubKey);
2583 
2584             Status = STATUS_INSUFFICIENT_RESOURCES;
2585             goto Cleanup;
2586         }
2587     }
2588 
2589     /* Set success */
2590     Status = STATUS_SUCCESS;
2591 
2592 Cleanup:
2593 
2594     /* Release the cells */
2595     if (DestNode) HvReleaseCell(DestinationHive, NewKeyCell);
2596     if (SrcNode) HvReleaseCell(SourceHive, SrcKeyCell);
2597 
2598     /* Cleanup allocated cells in case of failure */
2599     if (!NT_SUCCESS(Status))
2600     {
2601         if (NewSecCell != HCELL_NIL)
2602             HvFreeCell(DestinationHive, NewSecCell);
2603 
2604         if (NewClassCell != HCELL_NIL)
2605             HvFreeCell(DestinationHive, NewClassCell);
2606 
2607         if (NewKeyCell != HCELL_NIL)
2608             HvFreeCell(DestinationHive, NewKeyCell);
2609 
2610         NewKeyCell = HCELL_NIL;
2611     }
2612 
2613     /* Set the cell index if requested and return status */
2614     if (DestKeyCell) *DestKeyCell = NewKeyCell;
2615     return Status;
2616 }
2617 
2618 NTSTATUS
2619 NTAPI
2620 CmpDeepCopyKey(IN PHHIVE SourceHive,
2621                IN HCELL_INDEX SrcKeyCell,
2622                IN PHHIVE DestinationHive,
2623                IN HSTORAGE_TYPE StorageType,
2624                OUT PHCELL_INDEX DestKeyCell OPTIONAL)
2625 {
2626     /* Call the internal function */
2627     return CmpDeepCopyKeyInternal(SourceHive,
2628                                   SrcKeyCell,
2629                                   DestinationHive,
2630                                   HCELL_NIL,
2631                                   StorageType,
2632                                   DestKeyCell);
2633 }
2634 
2635 NTSTATUS
2636 NTAPI
2637 CmSaveKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
2638           IN HANDLE FileHandle,
2639           IN ULONG Flags)
2640 {
2641     NTSTATUS Status = STATUS_SUCCESS;
2642     PCMHIVE KeyHive = NULL;
2643     PAGED_CODE();
2644 
2645     DPRINT("CmSaveKey(0x%08X, 0x%08X, %lu)\n", Kcb, FileHandle, Flags);
2646 
2647     /* Lock the registry and KCB */
2648     CmpLockRegistry();
2649     CmpAcquireKcbLockShared(Kcb);
2650 
2651     if (Kcb->Delete)
2652     {
2653         /* The source key has been deleted, do nothing */
2654         Status = STATUS_KEY_DELETED;
2655         goto Cleanup;
2656     }
2657 
2658     if (Kcb->KeyHive == &CmiVolatileHive->Hive)
2659     {
2660         /* Keys that are directly in the master hive can't be saved */
2661         Status = STATUS_ACCESS_DENIED;
2662         goto Cleanup;
2663     }
2664 
2665     /* Create a new hive that will hold the key */
2666     Status = CmpInitializeHive(&KeyHive,
2667                                HINIT_CREATE,
2668                                HIVE_VOLATILE,
2669                                HFILE_TYPE_PRIMARY,
2670                                NULL,
2671                                NULL,
2672                                NULL,
2673                                NULL,
2674                                NULL,
2675                                0);
2676     if (!NT_SUCCESS(Status)) goto Cleanup;
2677 
2678     /* Copy the key recursively into the new hive */
2679     Status = CmpDeepCopyKey(Kcb->KeyHive,
2680                             Kcb->KeyCell,
2681                             &KeyHive->Hive,
2682                             Stable,
2683                             &KeyHive->Hive.BaseBlock->RootCell);
2684     if (!NT_SUCCESS(Status)) goto Cleanup;
2685 
2686     /* Set the primary handle of the hive */
2687     KeyHive->FileHandles[HFILE_TYPE_PRIMARY] = FileHandle;
2688 
2689     /* Dump the hive into the file */
2690     HvWriteHive(&KeyHive->Hive);
2691 
2692 Cleanup:
2693 
2694     /* Free the hive */
2695     if (KeyHive) CmpDestroyHive(KeyHive);
2696 
2697     /* Release the locks */
2698     CmpReleaseKcbLock(Kcb);
2699     CmpUnlockRegistry();
2700 
2701     return Status;
2702 }
2703 
2704 NTSTATUS
2705 NTAPI
2706 CmSaveMergedKeys(IN PCM_KEY_CONTROL_BLOCK HighKcb,
2707                  IN PCM_KEY_CONTROL_BLOCK LowKcb,
2708                  IN HANDLE FileHandle)
2709 {
2710     PCMHIVE KeyHive = NULL;
2711     NTSTATUS Status = STATUS_SUCCESS;
2712 
2713     PAGED_CODE();
2714 
2715     DPRINT("CmSaveKey(%p, %p, %p)\n", HighKcb, LowKcb, FileHandle);
2716 
2717     /* Lock the registry and the KCBs */
2718     CmpLockRegistry();
2719     CmpAcquireKcbLockShared(HighKcb);
2720     CmpAcquireKcbLockShared(LowKcb);
2721 
2722     if (LowKcb->Delete || HighKcb->Delete)
2723     {
2724         /* The source key has been deleted, do nothing */
2725         Status = STATUS_KEY_DELETED;
2726         goto done;
2727     }
2728 
2729     /* Create a new hive that will hold the key */
2730     Status = CmpInitializeHive(&KeyHive,
2731                                HINIT_CREATE,
2732                                HIVE_VOLATILE,
2733                                HFILE_TYPE_PRIMARY,
2734                                NULL,
2735                                NULL,
2736                                NULL,
2737                                NULL,
2738                                NULL,
2739                                0);
2740     if (!NT_SUCCESS(Status))
2741         goto done;
2742 
2743     /* Copy the low precedence key recursively into the new hive */
2744     Status = CmpDeepCopyKey(LowKcb->KeyHive,
2745                             LowKcb->KeyCell,
2746                             &KeyHive->Hive,
2747                             Stable,
2748                             &KeyHive->Hive.BaseBlock->RootCell);
2749     if (!NT_SUCCESS(Status))
2750         goto done;
2751 
2752     /* Copy the high precedence key recursively into the new hive */
2753     Status = CmpDeepCopyKey(HighKcb->KeyHive,
2754                             HighKcb->KeyCell,
2755                             &KeyHive->Hive,
2756                             Stable,
2757                             &KeyHive->Hive.BaseBlock->RootCell);
2758     if (!NT_SUCCESS(Status))
2759         goto done;
2760 
2761     /* Set the primary handle of the hive */
2762     KeyHive->FileHandles[HFILE_TYPE_PRIMARY] = FileHandle;
2763 
2764     /* Dump the hive into the file */
2765     HvWriteHive(&KeyHive->Hive);
2766 
2767 done:
2768     /* Free the hive */
2769     if (KeyHive)
2770         CmpDestroyHive(KeyHive);
2771 
2772     /* Release the locks */
2773     CmpReleaseKcbLock(LowKcb);
2774     CmpReleaseKcbLock(HighKcb);
2775     CmpUnlockRegistry();
2776 
2777     return Status;
2778 }
2779