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