* PURPOSE: Configuration Manager - Internal Registry APIs
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
* Eric Kohl
+ * Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
*/
/* INCLUDES ******************************************************************/
/* Same file found */
Loaded = TRUE;
*CmHive = Hive;
-
+
/* If the hive is frozen, not sure what to do */
if (Hive->Frozen)
{
{
/* Acquire the flusher lock */
CmpLockHiveFlusherExclusive(Hive);
-
+
/* Check for illegal state */
if ((ForceFlush) && (Hive->UseCount))
{
DPRINT1("FIXME: Hive is damaged and needs fixup\n");
while (TRUE);
}
-
+
/* Only sync if we are forced to or if it won't cause a hive shrink */
if ((ForceFlush) || (!HvHiveWillShrink(&Hive->Hive)))
{
CellData->u.KeyValue.DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE;
CellData->u.KeyValue.Data = SmallData;
}
-
+
/* Set the type now */
CellData->u.KeyValue.Type = Type;
PCELL_DATA CellData;
ULONG Length;
BOOLEAN WasSmall, IsSmall;
-
+
/* Registry writes must be blocked */
CMP_ASSERT_FLUSH_LOCK(Hive);
Value->Type = Type;
return STATUS_SUCCESS;
}
-
+
/* We have a normal key. Was the old cell also normal and had data? */
if (!(WasSmall) && (Length > 0))
{
IN OUT PULONG ResultLength)
{
NTSTATUS Status;
- ULONG Size, SizeLeft, MinimumSize;
+ ULONG Size, SizeLeft, MinimumSize, Offset;
PKEY_INFORMATION Info = (PKEY_INFORMATION)KeyInformation;
USHORT NameLength;
+ PVOID ClassData;
/* Check if the value is compressed */
if (Node->Flags & KEY_COMP_NAME)
/* Check if the node has a class */
if (Node->ClassLength > 0)
{
- /* It does. We don't support these yet */
- ASSERTMSG("Classes not supported\n", FALSE);
+ /* Set the class offset */
+ Offset = FIELD_OFFSET(KEY_NODE_INFORMATION, Name) + NameLength;
+ Offset = ALIGN_UP_BY(Offset, sizeof(ULONG));
+ Info->KeyNodeInformation.ClassOffset = Offset;
+
+ /* Get the class data */
+ ClassData = HvGetCell(Hive, Node->Class);
+ if (ClassData == NULL)
+ {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ /* Check if we can copy anything */
+ if (Length > Offset)
+ {
+ /* Copy the class data */
+ RtlCopyMemory((PUCHAR)Info + Offset,
+ ClassData,
+ min(Node->ClassLength, Length - Offset));
+ }
+
+ /* Check if the buffer was large enough */
+ if (Length < Offset + Node->ClassLength)
+ {
+ Status = STATUS_BUFFER_OVERFLOW;
+ }
+
+ /* Release the class cell */
+ HvReleaseCell(Hive, Node->Class);
}
else
{
/* Check if we have a class */
if (Node->ClassLength > 0)
{
- /* We do, but we currently don't support this */
- ASSERTMSG("Classes not supported\n", FALSE);
+ /* Set the class offset */
+ Offset = FIELD_OFFSET(KEY_FULL_INFORMATION, Class);
+ Info->KeyFullInformation.ClassOffset = Offset;
+
+ /* Get the class data */
+ ClassData = HvGetCell(Hive, Node->Class);
+ if (ClassData == NULL)
+ {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ /* Copy the class data */
+ ASSERT(Length >= Offset);
+ RtlCopyMemory(Info->KeyFullInformation.Class,
+ ClassData,
+ min(Node->ClassLength, Length - Offset));
+
+ /* Check if the buffer was large enough */
+ if (Length < Offset + Node->ClassLength)
+ {
+ Status = STATUS_BUFFER_OVERFLOW;
+ }
+
+ /* Release the class cell */
+ HvReleaseCell(Hive, Node->Class);
}
else
{
/* We don't have a class, so set offset to -1, not 0! */
- Info->KeyNodeInformation.ClassOffset = 0xFFFFFFFF;
+ Info->KeyFullInformation.ClassOffset = 0xFFFFFFFF;
}
break;
/* Acquire hive and KCB lock */
CmpLockRegistry();
CmpAcquireKcbLockShared(Kcb);
-
+
/* Sanity check */
ASSERT(sizeof(ULONG) == CM_KEY_VALUE_SMALL);
-
+
/* Don't touch deleted KCBs */
DoAgain:
if (Kcb->Delete)
Status = STATUS_KEY_DELETED;
goto Quickie;
}
-
+
/* Don't let anyone mess with symlinks */
if ((Kcb->Flags & KEY_SYM_LINK) &&
((Type != REG_LINK) ||
/* Acquire exclusive lock */
CmpConvertKcbSharedToExclusive(Kcb);
}
-
+
/* Cache lookup failed, so don't try it next time */
FirstTry = FALSE;
-
+
/* Now grab the flush lock since the key will be modified */
ASSERT(FlusherLocked == FALSE);
CmpLockHiveFlusherShared((PCMHIVE)Kcb->KeyHive);
Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
ASSERT(Parent);
ParentCell = Cell;
-
+
/* Prepare to scan the key node */
Count = Parent->ValueList.Count;
Found = FALSE;
HvReleaseCell(Hive, ChildCell);
ChildCell = HCELL_NIL;
}
-
+
/* Get its value */
Value = (PCM_KEY_VALUE)HvGetCell(Hive, CurrentChild);
if (!Value)
ChildIndex = 0;
}
}
-
+
/* Should only get here on the second pass */
ASSERT(FirstTry == FALSE);
-
+
/* The KCB must be locked exclusive at this point */
CMP_ASSERT_KCB_LOCK(Kcb);
-
+
/* Mark the cell dirty */
if (!HvMarkCellDirty(Hive, Cell, FALSE))
{
Parent->MaxValueNameLen = ValueName->Length;
Kcb->KcbMaxValueNameLen = ValueName->Length;
}
-
+
/* Check if the maximum data length changed */
ASSERT(Parent->MaxValueDataLen == Kcb->KcbMaxValueDataLen);
if (Parent->MaxValueDataLen < DataLength)
Parent->MaxValueDataLen = DataLength;
Kcb->KcbMaxValueDataLen = Parent->MaxValueDataLen;
}
-
+
/* Save the write time */
KeQuerySystemTime(&Parent->LastWriteTime);
Kcb->KcbLastWriteTime = Parent->LastWriteTime;
-
+
/* Check if the cell is cached */
if ((Found) && (CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)))
{
/* Sanity checks */
ASSERT(!(CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)));
ASSERT(!(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND));
-
+
/* Set the value cache */
Kcb->ValueCache.Count = Parent->ValueList.Count;
Kcb->ValueCache.ValueList = Parent->ValueList.List;
}
-
+
/* Notify registered callbacks */
CmpReportNotify(Kcb,
Hive,
Kcb->KeyCell,
REG_NOTIFY_CHANGE_LAST_SET);
}
-
+
/* Release the cells */
Quickie:
if ((ParentCell != HCELL_NIL) && (Hive)) HvReleaseCell(Hive, ParentCell);
/* Acquire hive lock */
CmpLockRegistry();
-
+
/* Lock KCB exclusively */
CmpAcquireKcbLockExclusive(Kcb);
-
+
/* Don't touch deleted keys */
if (Kcb->Delete)
{
/* Get the hive and the cell index */
Hive = Kcb->KeyHive;
Cell = Kcb->KeyCell;
-
+
/* Lock flushes */
CmpLockHiveFlusherShared((PCMHIVE)Hive);
}
/* Get the key value */
- Value = (PCM_KEY_VALUE)HvGetCell(Hive,ChildCell);
+ Value = (PCM_KEY_VALUE)HvGetCell(Hive, ChildCell);
ASSERT(Value);
/* Mark it and all related data as dirty */
goto Quickie;
}
- /* Ssanity checks */
+ /* Sanity checks */
ASSERT(HvIsCellDirty(Hive, Parent->ValueList.List));
ASSERT(HvIsCellDirty(Hive, ChildCell));
Kcb->KcbMaxValueNameLen = 0;
Kcb->KcbMaxValueDataLen = 0;
}
-
+
/* Cleanup the value cache */
CmpCleanUpKcbValueCache(Kcb);
-
+
/* Sanity checks */
ASSERT(!(CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)));
ASSERT(!(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND));
-
+
/* Set the value cache */
Kcb->ValueCache.Count = ChildList->Count;
Kcb->ValueCache.ValueList = ChildList->List;
-
+
/* Notify registered callbacks */
CmpReportNotify(Kcb, Hive, Cell, REG_NOTIFY_CHANGE_LAST_SET);
/* Acquire hive lock */
CmpLockRegistry();
-
+
/* Lock the KCB shared */
CmpAcquireKcbLockShared(Kcb);
-
+
/* Don't touch deleted keys */
DoAgain:
if (Kcb->Delete)
CmpUnlockRegistry();
return STATUS_KEY_DELETED;
}
-
+
/* We don't deal with this yet */
if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)
{
/* Check if we need an exclusive lock */
ASSERT(CellToRelease == HCELL_NIL);
ASSERT(ValueData == NULL);
-
+
/* Try with exclusive KCB lock */
CmpConvertKcbSharedToExclusive(Kcb);
goto DoAgain;
}
-
+
if (Result == SearchSuccess)
{
/* Sanity check */
HvReleaseCell(Hive, CellToRelease);
CellToRelease = HCELL_NIL;
}
-
+
/* Try with exclusive KCB lock */
CmpConvertKcbSharedToExclusive(Kcb);
_SEH2_YIELD(goto DoAgain);
/* Acquire hive lock */
CmpLockRegistry();
-
+
/* Lock the KCB shared */
CmpAcquireKcbLockShared(Kcb);
Status = STATUS_NO_MORE_ENTRIES;
goto Quickie;
}
-
+
/* We don't deal with this yet */
if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)
{
/* Check if we need an exclusive lock */
ASSERT(CellToRelease == HCELL_NIL);
HvReleaseCell(Hive, Kcb->KeyCell);
-
+
/* Try with exclusive KCB lock */
CmpConvertKcbSharedToExclusive(Kcb);
goto DoAgain;
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Quickie;
}
-
+
/* User data, need SEH */
_SEH2_TRY
{
return Status;
}
+static
+NTSTATUS
+CmpQueryKeyDataFromCache(
+ _In_ PCM_KEY_CONTROL_BLOCK Kcb,
+ _Out_ PKEY_CACHED_INFORMATION KeyCachedInfo,
+ _In_ ULONG Length,
+ _Out_ PULONG ResultLength)
+{
+ PCM_KEY_NODE Node;
+ PHHIVE KeyHive;
+ HCELL_INDEX KeyCell;
+ USHORT NameLength;
+ PAGED_CODE();
+
+ /* Get the hive and cell index */
+ KeyHive = Kcb->KeyHash.KeyHive;
+ KeyCell = Kcb->KeyHash.KeyCell;
+
+#if DBG
+ /* Get the cell node */
+ Node = HvGetCell(KeyHive, KeyCell);
+ if (Node != NULL)
+ {
+ ULONG SubKeyCount;
+ ASSERT(Node->ValueList.Count == Kcb->ValueCache.Count);
+
+ if (!(Kcb->ExtFlags & CM_KCB_INVALID_CACHED_INFO))
+ {
+ SubKeyCount = Node->SubKeyCounts[0] + Node->SubKeyCounts[1];
+ if (Kcb->ExtFlags & CM_KCB_NO_SUBKEY)
+ {
+ ASSERT(SubKeyCount == 0);
+ }
+ else if (Kcb->ExtFlags & CM_KCB_SUBKEY_ONE)
+ {
+ ASSERT(SubKeyCount == 1);
+ }
+ else if (Kcb->ExtFlags & CM_KCB_SUBKEY_HINT)
+ {
+ ASSERT(SubKeyCount == Kcb->IndexHint->Count);
+ }
+ else
+ {
+ ASSERT(SubKeyCount == Kcb->SubKeyCount);
+ }
+ }
+
+ ASSERT(Node->LastWriteTime.QuadPart == Kcb->KcbLastWriteTime.QuadPart);
+ ASSERT(Node->MaxNameLen == Kcb->KcbMaxNameLen);
+ ASSERT(Node->MaxValueNameLen == Kcb->KcbMaxValueNameLen);
+ ASSERT(Node->MaxValueDataLen == Kcb->KcbMaxValueDataLen);
+
+ /* Release the cell */
+ HvReleaseCell(KeyHive, KeyCell);
+ }
+#endif // DBG
+
+ /* Make sure we have a name block */
+ if (Kcb->NameBlock == NULL)
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ /* Check for compressed name */
+ if (Kcb->NameBlock->Compressed)
+ {
+ /* Calculate the name size */
+ NameLength = CmpCompressedNameSize(Kcb->NameBlock->NameHash.Name,
+ Kcb->NameBlock->NameHash.NameLength);
+ }
+ else
+ {
+ /* Use the stored name size */
+ NameLength = Kcb->NameBlock->NameHash.NameLength;
+ }
+
+ /* Validate buffer length (we do not copy the name!) */
+ *ResultLength = sizeof(*KeyCachedInfo);
+ if (Length < *ResultLength)
+ {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ /* Fill the structure */
+ KeyCachedInfo->LastWriteTime = Kcb->KcbLastWriteTime;
+ KeyCachedInfo->TitleIndex = 0;
+ KeyCachedInfo->NameLength = NameLength;
+ KeyCachedInfo->Values = Kcb->ValueCache.Count;
+ KeyCachedInfo->MaxNameLen = Kcb->KcbMaxNameLen;
+ KeyCachedInfo->MaxValueNameLen = Kcb->KcbMaxValueNameLen;
+ KeyCachedInfo->MaxValueDataLen = Kcb->KcbMaxValueDataLen;
+
+ /* Check the ExtFlags for what we have */
+ if (Kcb->ExtFlags & CM_KCB_INVALID_CACHED_INFO)
+ {
+ /* Cache is not valid, do a full lookup */
+ DPRINT1("Kcb cache incoherency detected, kcb = %p\n", Kcb);
+
+ /* Get the cell node */
+ Node = HvGetCell(KeyHive, KeyCell);
+ if (Node == NULL)
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ /* Calculate number of subkeys */
+ KeyCachedInfo->SubKeys = Node->SubKeyCounts[0] + Node->SubKeyCounts[1];
+
+ /* Release the cell */
+ HvReleaseCell(KeyHive, KeyCell);
+ }
+ else if (Kcb->ExtFlags & CM_KCB_NO_SUBKEY)
+ {
+ /* There are no subkeys */
+ KeyCachedInfo->SubKeys = 0;
+ }
+ else if (Kcb->ExtFlags & CM_KCB_SUBKEY_ONE)
+ {
+ /* There is exactly one subley */
+ KeyCachedInfo->SubKeys = 1;
+ }
+ else if (Kcb->ExtFlags & CM_KCB_SUBKEY_HINT)
+ {
+ /* Get the number of subkeys from the subkey hint */
+ KeyCachedInfo->SubKeys = Kcb->IndexHint->Count;
+ }
+ else
+ {
+ /* No subkey hint, use the key count field */
+ KeyCachedInfo->SubKeys = Kcb->SubKeyCount;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static
+NTSTATUS
+CmpQueryFlagsInformation(
+ _In_ PCM_KEY_CONTROL_BLOCK Kcb,
+ _Out_ PKEY_USER_FLAGS_INFORMATION KeyFlagsInfo,
+ _In_ ULONG Length,
+ _In_ PULONG ResultLength)
+{
+ /* Validate the buffer size */
+ *ResultLength = sizeof(*KeyFlagsInfo);
+ if (Length < *ResultLength)
+ {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ /* Copy the user flags */
+ KeyFlagsInfo->UserFlags = Kcb->KcbUserFlags;
+
+ return STATUS_SUCCESS;
+}
+
+static
+NTSTATUS
+CmpQueryNameInformation(
+ _In_ PCM_KEY_CONTROL_BLOCK Kcb,
+ _Out_opt_ PKEY_NAME_INFORMATION KeyNameInfo,
+ _In_ ULONG Length,
+ _Out_ PULONG ResultLength)
+{
+ ULONG NeededLength;
+ PCM_KEY_CONTROL_BLOCK CurrentKcb;
+
+ NeededLength = 0;
+ CurrentKcb = Kcb;
+
+ /* Count the needed buffer size */
+ while (CurrentKcb)
+ {
+ if (CurrentKcb->NameBlock->Compressed)
+ NeededLength += CmpCompressedNameSize(CurrentKcb->NameBlock->Name, CurrentKcb->NameBlock->NameLength);
+ else
+ NeededLength += CurrentKcb->NameBlock->NameLength;
+
+ NeededLength += sizeof(OBJ_NAME_PATH_SEPARATOR);
+
+ CurrentKcb = CurrentKcb->ParentKcb;
+ }
+
+ _SEH2_TRY
+ {
+ *ResultLength = FIELD_OFFSET(KEY_NAME_INFORMATION, Name) + NeededLength;
+ if (Length < RTL_SIZEOF_THROUGH_FIELD(KEY_NAME_INFORMATION, NameLength))
+ _SEH2_YIELD(return STATUS_BUFFER_TOO_SMALL);
+ if (Length < *ResultLength)
+ {
+ KeyNameInfo->NameLength = NeededLength;
+ _SEH2_YIELD(return STATUS_BUFFER_OVERFLOW);
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ _SEH2_YIELD(return _SEH2_GetExceptionCode());
+ }
+ _SEH2_END;
+
+ /* Do the real copy */
+ KeyNameInfo->NameLength = 0;
+ CurrentKcb = Kcb;
+
+ _SEH2_TRY
+ {
+ while (CurrentKcb)
+ {
+ ULONG NameLength;
+
+ if (CurrentKcb->NameBlock->Compressed)
+ {
+ NameLength = CmpCompressedNameSize(CurrentKcb->NameBlock->Name, CurrentKcb->NameBlock->NameLength);
+ /* Copy the compressed name */
+ CmpCopyCompressedName(&KeyNameInfo->Name[(NeededLength - NameLength)/sizeof(WCHAR)],
+ NameLength,
+ CurrentKcb->NameBlock->Name,
+ CurrentKcb->NameBlock->NameLength);
+ }
+ else
+ {
+ NameLength = CurrentKcb->NameBlock->NameLength;
+ /* Otherwise, copy the raw name */
+ RtlCopyMemory(&KeyNameInfo->Name[(NeededLength - NameLength)/sizeof(WCHAR)],
+ CurrentKcb->NameBlock->Name,
+ NameLength);
+ }
+
+ NeededLength -= NameLength;
+ NeededLength -= sizeof(OBJ_NAME_PATH_SEPARATOR);
+ /* Add path separator */
+ KeyNameInfo->Name[NeededLength/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
+ KeyNameInfo->NameLength += NameLength + sizeof(OBJ_NAME_PATH_SEPARATOR);
+
+ CurrentKcb = CurrentKcb->ParentKcb;
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ _SEH2_YIELD(return _SEH2_GetExceptionCode());
+ }
+ _SEH2_END;
+
+ /* Make sure we copied everything */
+ ASSERT(NeededLength == 0);
+ ASSERT(KeyNameInfo->Name[0] == OBJ_NAME_PATH_SEPARATOR);
+
+ /* We're done */
+ return STATUS_SUCCESS;
+}
+
+
NTSTATUS
NTAPI
-CmQueryKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
- IN KEY_INFORMATION_CLASS KeyInformationClass,
- IN PVOID KeyInformation,
- IN ULONG Length,
- IN PULONG ResultLength)
+CmQueryKey(_In_ PCM_KEY_CONTROL_BLOCK Kcb,
+ _In_ KEY_INFORMATION_CLASS KeyInformationClass,
+ _Out_opt_ PVOID KeyInformation,
+ _In_ ULONG Length,
+ _Out_ PULONG ResultLength)
{
NTSTATUS Status;
PHHIVE Hive;
/* Acquire hive lock */
CmpLockRegistry();
-
+
/* Lock KCB shared */
CmpAcquireKcbLockShared(Kcb);
goto Quickie;
}
- /* Check what class we got */
- switch (KeyInformationClass)
+ /* Data can be user-mode, use SEH */
+ _SEH2_TRY
{
- /* Typical information */
- case KeyFullInformation:
- case KeyBasicInformation:
- case KeyNodeInformation:
-
- /* Get the hive and parent */
- Hive = Kcb->KeyHive;
- Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
- ASSERT(Parent);
-
- /* Track cell references */
- if (!HvTrackCellRef(&CellReferences, Hive, Kcb->KeyCell))
+ /* Check what class we got */
+ switch (KeyInformationClass)
+ {
+ /* Typical information */
+ case KeyFullInformation:
+ case KeyBasicInformation:
+ case KeyNodeInformation:
{
- /* Not enough memory to track references */
- Status = STATUS_INSUFFICIENT_RESOURCES;
+ /* Get the hive and parent */
+ Hive = Kcb->KeyHive;
+ Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
+ ASSERT(Parent);
+
+ /* Track cell references */
+ if (!HvTrackCellRef(&CellReferences, Hive, Kcb->KeyCell))
+ {
+ /* Not enough memory to track references */
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ else
+ {
+ /* Call the internal API */
+ Status = CmpQueryKeyData(Hive,
+ Parent,
+ KeyInformationClass,
+ KeyInformation,
+ Length,
+ ResultLength);
+ }
+ break;
}
- else
+
+ case KeyCachedInformation:
{
/* Call the internal API */
- Status = CmpQueryKeyData(Hive,
- Parent,
- KeyInformationClass,
- KeyInformation,
- Length,
- ResultLength);
+ Status = CmpQueryKeyDataFromCache(Kcb,
+ KeyInformation,
+ Length,
+ ResultLength);
+ break;
}
- break;
-
- /* Unsupported classes for now */
- case KeyNameInformation:
- case KeyCachedInformation:
- case KeyFlagsInformation:
- /* Print message and fail */
- DPRINT1("Unsupported class: %d!\n", KeyInformationClass);
- Status = STATUS_NOT_IMPLEMENTED;
- break;
+ case KeyFlagsInformation:
+ {
+ /* Call the internal API */
+ Status = CmpQueryFlagsInformation(Kcb,
+ KeyInformation,
+ Length,
+ ResultLength);
+ break;
+ }
- /* Illegal classes */
- default:
+ case KeyNameInformation:
+ {
+ /* Call the internal API */
+ Status = CmpQueryNameInformation(Kcb,
+ KeyInformation,
+ Length,
+ ResultLength);
+ break;
+ }
- /* Print message and fail */
- DPRINT1("Unsupported class: %d!\n", KeyInformationClass);
- Status = STATUS_INVALID_INFO_CLASS;
- break;
+ /* Illegal classes */
+ default:
+ {
+ /* Print message and fail */
+ DPRINT1("Unsupported class: %d!\n", KeyInformationClass);
+ Status = STATUS_INVALID_INFO_CLASS;
+ break;
+ }
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* Fail with exception code */
+ Status = _SEH2_GetExceptionCode();
+ _SEH2_YIELD(goto Quickie);
}
+ _SEH2_END;
Quickie:
/* Release references */
/* Acquire hive lock */
CmpLockRegistry();
-
+
/* Lock the KCB shared */
CmpAcquireKcbLockShared(Kcb);
-
+
/* Don't touch deleted keys */
if (Kcb->Delete)
{
/* Now get the actual child node */
Child = (PCM_KEY_NODE)HvGetCell(Hive, ChildCell);
ASSERT(Child);
-
+
/* Track references */
if (!HvTrackCellRef(&CellReferences, Hive, ChildCell))
{
/* Acquire hive lock */
CmpLockRegistry();
-
+
/* Get the kcb */
Kcb = KeyBody->KeyControlBlock;
-
+
/* Don't allow deleting the root */
if (!Kcb->ParentKcb)
{
CmpUnlockRegistry();
return STATUS_CANNOT_DELETE;
}
-
+
/* Lock parent and child */
CmpAcquireTwoKcbLocksExclusiveByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey);
-
+
/* Check if we're already being deleted */
if (Kcb->Delete)
{
/* Don't do it twice */
Status = STATUS_SUCCESS;
- goto Quickie2;
+ goto Quickie;
}
/* Get the hive and node */
Hive = Kcb->KeyHive;
Cell = Kcb->KeyCell;
-
+
/* Lock flushes */
CmpLockHiveFlusherShared((PCMHIVE)Hive);
-
+
/* Get the key node */
Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
ASSERT(Node);
-
+
/* Sanity check */
ASSERT(Node->Flags == Kcb->Flags);
{
/* Send notification to registered callbacks */
CmpReportNotify(Kcb, Hive, Cell, REG_NOTIFY_CHANGE_NAME);
-
+
/* Get the parent and free the cell */
ParentCell = Node->Parent;
Status = CmpFreeKeyByCell(Hive, Cell, TRUE);
{
/* Flush any notifications */
CmpFlushNotifiesOnKeyBodyList(Kcb, FALSE);
-
+
/* Clean up information we have on the subkey */
CmpCleanUpSubKeyInfo(Kcb->ParentKcb);
{
/* Update the maximum name length */
Kcb->ParentKcb->KcbMaxNameLen = (USHORT)Parent->MaxNameLen;
-
+
/* Make sure we're dirty */
ASSERT(HvIsCellDirty(Hive, ParentCell));
/* Release the cell */
HvReleaseCell(Hive, ParentCell);
}
-
+
/* Set the KCB in delete mode and remove it */
Kcb->Delete = TRUE;
CmpRemoveKeyControlBlock(Kcb);
/* Fail */
Status = STATUS_CANNOT_DELETE;
}
-
+
/* Release the cell */
HvReleaseCell(Hive, Cell);
CmpUnlockHiveFlusher((PCMHIVE)Hive);
/* Release the KCB locks */
-Quickie2:
+Quickie:
CmpReleaseTwoKcbLockByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey);
/* Release hive lock */
/* Ignore flushes until we're ready */
if (CmpNoWrite) return STATUS_SUCCESS;
-
+
/* Get the hives */
Hive = Kcb->KeyHive;
CmHive = (PCMHIVE)Hive;
-
+
/* Check if this is the master hive */
if (CmHive == CmiVolatileHive)
{
ASSERT(CmHive->ViewLock);
KeAcquireGuardedMutex(CmHive->ViewLock);
CmHive->ViewLockOwner = KeGetCurrentThread();
-
+
/* Will the hive shrink? */
if (HvHiveWillShrink(Hive))
{
ASSERT(KeGetCurrentThread() == CmHive->ViewLockOwner);
KeReleaseGuardedMutex(CmHive->ViewLock);
}
-
+
/* Flush only this hive */
if (!HvSyncHive(Hive))
{
/* Fail */
Status = STATUS_REGISTRY_IO_FAILED;
}
-
+
/* Release the flush lock */
- CmpUnlockHiveFlusher((PCMHIVE)Hive);
+ CmpUnlockHiveFlusher(CmHive);
}
/* Return the status */
PCMHIVE CmHive, LoadedHive;
NTSTATUS Status;
CM_PARSE_CONTEXT ParseContext;
-
+
/* Check if we have a trust key */
if (KeyBody)
{
/* Fail */
- DPRINT1("Trusted classes not yet supported\n");
- return STATUS_NOT_IMPLEMENTED;
+ DPRINT("Trusted classes not yet supported\n");
}
-
+
/* Build a service QoS for a security context */
ServiceQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
ServiceQos.ImpersonationLevel = SecurityImpersonation;
DPRINT1("Security context failed\n");
return Status;
}
-
+
/* Open the target key */
-#if 0
- Status = ZwOpenKey(&KeyHandle, KEY_READ, TargetKey);
-#else
RtlZeroMemory(&ParseContext, sizeof(ParseContext));
ParseContext.CreateOperation = FALSE;
Status = ObOpenObjectByName(TargetKey,
KEY_READ,
&ParseContext,
&KeyHandle);
-#endif
if (!NT_SUCCESS(Status)) KeyHandle = NULL;
/* Open the hive */
{
/* Lock the registry */
CmpLockRegistryExclusive();
-
+
/* Check if we are already loaded */
if (CmpIsHiveAlreadyLoaded(KeyHandle, SourceFile, &LoadedHive))
{
/* Release the registry */
CmpUnlockRegistry();
}
-
+
/* Close the key handle if we had one */
if (KeyHandle) ZwClose(KeyHandle);
return Status;
}
-
+
/* Lock the registry shared */
CmpLockRegistry();
-
+
/* Lock loading */
ExAcquirePushLockExclusive(&CmpLoadHiveLock);
-
+
/* Lock the hive to this thread */
CmHive->Hive.HiveFlags |= HIVE_IS_UNLOADING;
CmHive->CreatorOwner = KeGetCurrentThread();
-
+
/* Set flag */
if (Flags & REG_NO_LAZY_FLUSH) CmHive->Hive.HiveFlags |= HIVE_NOLAZYFLUSH;
-
+
/* Link the hive */
Status = CmpLinkHiveToMaster(TargetKey->ObjectName,
TargetKey->RootDirectory,
{
/* Add to HiveList key */
CmpAddToHiveFileList(CmHive);
-
+
/* Sync the hive if necessary */
if (Allocate)
{
HvSyncHive(&CmHive->Hive);
CmpUnlockHiveFlusher(CmHive);
}
-
+
/* Release the hive */
CmHive->Hive.HiveFlags &= ~HIVE_IS_UNLOADING;
CmHive->CreatorOwner = NULL;
-
+
/* Allow loads */
ExReleasePushLock(&CmpLoadHiveLock);
}
/* FIXME: TODO */
ASSERT(FALSE);
}
-
+
/* Is this first profile load? */
- if (!(CmpProfileLoaded) && !(CmpWasSetupBoot))
+ if (!CmpProfileLoaded && !CmpWasSetupBoot)
{
/* User is now logged on, set quotas */
CmpProfileLoaded = TRUE;
CmpSetGlobalQuotaAllowed();
}
-
+
/* Unlock the registry */
CmpUnlockRegistry();
-
+
/* Close handle and return */
if (KeyHandle) ZwClose(KeyHandle);
return Status;
}
+static
+BOOLEAN
+NTAPI
+CmpUnlinkHiveFromMaster(IN PCMHIVE CmHive,
+ IN HCELL_INDEX Cell)
+{
+ PCELL_DATA CellData;
+ HCELL_INDEX LinkCell;
+ NTSTATUS Status;
+
+ DPRINT("CmpUnlinkHiveFromMaster()\n");
+
+ /* Get the cell data */
+ CellData = HvGetCell(&CmHive->Hive, Cell);
+ if (CellData == NULL)
+ return FALSE;
+
+ /* Get the link cell and release the current cell */
+ LinkCell = CellData->u.KeyNode.Parent;
+ HvReleaseCell(&CmHive->Hive, Cell);
+
+ /* Remove the link cell from the master hive */
+ CmpLockHiveFlusherExclusive(CmiVolatileHive);
+ Status = CmpFreeKeyByCell((PHHIVE)CmiVolatileHive,
+ LinkCell,
+ TRUE);
+ CmpUnlockHiveFlusher(CmiVolatileHive);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("CmpFreeKeyByCell() failed (Status 0x%08lx)\n", Status);
+ return FALSE;
+ }
+
+ /* Remove the hive from the list */
+ ExAcquirePushLockExclusive(&CmpHiveListHeadLock);
+ RemoveEntryList(&CmHive->HiveList);
+ ExReleasePushLock(&CmpHiveListHeadLock);
+
+ return TRUE;
+}
+
NTSTATUS
NTAPI
CmUnloadKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
IN ULONG Flags)
{
- UNIMPLEMENTED;
- return STATUS_NOT_IMPLEMENTED;
+ PHHIVE Hive;
+ PCMHIVE CmHive;
+ HCELL_INDEX Cell;
+
+ DPRINT("CmUnloadKey(%p, %lx)\n", Kcb, Flags);
+
+ /* Get the hive */
+ Hive = Kcb->KeyHive;
+ Cell = Kcb->KeyCell;
+ CmHive = (PCMHIVE)Hive;
+
+ /* Fail if the key is not a hive root key */
+ if (Cell != Hive->BaseBlock->RootCell)
+ {
+ DPRINT1("Key is not a hive root key!\n");
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ /* Fail if we try to unload the master hive */
+ if (CmHive == CmiVolatileHive)
+ {
+ DPRINT1("Do not try to unload the master hive!\n");
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ /* Mark this hive as being unloaded */
+ Hive->HiveFlags |= HIVE_IS_UNLOADING;
+
+ /* Search for any opened keys in this hive, and take an appropriate action */
+ if (Kcb->RefCount > 1)
+ {
+ if (Flags != REG_FORCE_UNLOAD)
+ {
+ if (CmpEnumerateOpenSubKeys(Kcb, FALSE, FALSE) != 0)
+ {
+ /* There are open subkeys but we don't force hive unloading, fail */
+ Hive->HiveFlags &= ~HIVE_IS_UNLOADING;
+ return STATUS_CANNOT_DELETE;
+ }
+ }
+ else
+ {
+ DPRINT1("CmUnloadKey: Force unloading is HALF-IMPLEMENTED, expect dangling KCBs problems!\n");
+ if (CmpEnumerateOpenSubKeys(Kcb, TRUE, TRUE) != 0)
+ {
+ /* There are open subkeys that we cannot force to unload, fail */
+ Hive->HiveFlags &= ~HIVE_IS_UNLOADING;
+ return STATUS_CANNOT_DELETE;
+ }
+ }
+ }
+
+ /* Flush the hive */
+ CmFlushKey(Kcb, TRUE);
+
+ /* Unlink the hive from the master hive */
+ if (!CmpUnlinkHiveFromMaster(CmHive, Cell))
+ {
+ DPRINT("CmpUnlinkHiveFromMaster() failed!\n");
+
+ /* Remove the unloading flag and return failure */
+ Hive->HiveFlags &= ~HIVE_IS_UNLOADING;
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ /* Flush any notifications if we force hive unloading */
+ if (Flags == REG_FORCE_UNLOAD)
+ CmpFlushNotifiesOnKeyBodyList(Kcb, TRUE); // Lock is already held
+
+ /* Clean up information we have on the subkey */
+ CmpCleanUpSubKeyInfo(Kcb->ParentKcb);
+
+ /* Set the KCB in delete mode and remove it */
+ Kcb->Delete = TRUE;
+ CmpRemoveKeyControlBlock(Kcb);
+
+ if (Flags != REG_FORCE_UNLOAD)
+ {
+ /* Release the KCB locks */
+ CmpReleaseTwoKcbLockByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey);
+
+ /* Release the hive loading lock */
+ ExReleasePushLockExclusive(&CmpLoadHiveLock);
+ }
+
+ /* Release hive lock */
+ CmpUnlockRegistry();
+
+ /* Close file handles */
+ CmpCloseHiveFiles(CmHive);
+
+ /* Remove the hive from the hive file list */
+ CmpRemoveFromHiveFileList(CmHive);
+
+/**
+ ** NOTE:
+ ** The following code is mostly equivalent to what we "call" CmpDestroyHive()
+ **/
+ /* Destroy the security descriptor cache */
+ CmpDestroySecurityCache(CmHive);
+
+ /* Destroy the view list */
+ CmpDestroyHiveViewList(CmHive);
+
+ /* Delete the flusher lock */
+ ExDeleteResourceLite(CmHive->FlusherLock);
+ ExFreePoolWithTag(CmHive->FlusherLock, TAG_CMHIVE);
+
+ /* Delete the view lock */
+ ExFreePoolWithTag(CmHive->ViewLock, TAG_CMHIVE);
+
+ /* Free the hive storage */
+ HvFree(Hive);
+
+ /* Free the hive */
+ CmpFree(CmHive, TAG_CM);
+
+ return STATUS_SUCCESS;
}
ULONG
NTAPI
-CmCountOpenSubKeys(IN PCM_KEY_CONTROL_BLOCK RootKcb,
- IN BOOLEAN RemoveEmptyCacheEntries)
+CmpEnumerateOpenSubKeys(
+ IN PCM_KEY_CONTROL_BLOCK RootKcb,
+ IN BOOLEAN RemoveEmptyCacheEntries,
+ IN BOOLEAN DereferenceOpenedEntries)
{
PCM_KEY_HASH Entry;
PCM_KEY_CONTROL_BLOCK CachedKcb;
ULONG i, j;
ULONG SubKeys = 0;
- DPRINT("CmCountOpenSubKeys() called\n");
+ DPRINT("CmpEnumerateOpenSubKeys() called\n");
- /* The root key is the only referenced key. There are no refereced sub keys. */
+ /* The root key is the only referenced key. There are no referenced sub keys. */
if (RootKcb->RefCount == 1)
{
- DPRINT("open sub keys: 0\n");
+ DPRINT("Open sub keys: 0\n");
return 0;
}
/* Check whether the parent is the root key */
if (ParentKcb == RootKcb)
{
- DPRINT("Found a sub key \n");
- DPRINT("RefCount = %u\n", CachedKcb->RefCount);
+ DPRINT("Found a sub key, RefCount = %u\n", CachedKcb->RefCount);
if (CachedKcb->RefCount > 0)
{
+ DPRINT1("Found a sub key pointing to '%.*s', RefCount = %u\n",
+ CachedKcb->NameBlock->NameLength, CachedKcb->NameBlock->Name,
+ CachedKcb->RefCount);
+
+ /* If we dereference opened KCBs, don't touch read-only keys */
+ if (DereferenceOpenedEntries &&
+ !(CachedKcb->ExtFlags & CM_KCB_READ_ONLY_KEY))
+ {
+ /* Registry needs to be locked down */
+ CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK();
+
+ /* Flush any notifications */
+ CmpFlushNotifiesOnKeyBodyList(CachedKcb, TRUE); // Lock is already held
+
+ /* Clean up information we have on the subkey */
+ CmpCleanUpSubKeyInfo(CachedKcb->ParentKcb);
+
+ /* Get and cache the next cache entry */
+ // Entry = Entry->NextHash;
+ Entry = CachedKcb->NextHash;
+
+ /* Set the KCB in delete mode and remove it */
+ CachedKcb->Delete = TRUE;
+ CmpRemoveKeyControlBlock(CachedKcb);
+
+ /* Clear the cell */
+ CachedKcb->KeyCell = HCELL_NIL;
+
+ /* Restart with the next cache entry */
+ continue;
+ }
+ /* Else, the key cannot be dereferenced, and we count it as in use */
+
/* Count the current hash entry if it is in use */
SubKeys++;
}
- else if ((CachedKcb->RefCount == 0) && (RemoveEmptyCacheEntries == TRUE))
+ else if ((CachedKcb->RefCount == 0) && RemoveEmptyCacheEntries)
{
/* Remove the current key from the delayed close list */
CmpRemoveFromDelayedClose(CachedKcb);
}
}
- DPRINT("open sub keys: %u\n", SubKeys);
+ if (SubKeys > 0)
+ DPRINT1("Open sub keys: %u\n", SubKeys);
return SubKeys;
}
+
+static
+NTSTATUS
+CmpDeepCopyKeyInternal(IN PHHIVE SourceHive,
+ IN HCELL_INDEX SrcKeyCell,
+ IN PHHIVE DestinationHive,
+ IN HCELL_INDEX Parent,
+ IN HSTORAGE_TYPE StorageType,
+ OUT PHCELL_INDEX DestKeyCell OPTIONAL)
+{
+ NTSTATUS Status;
+ PCM_KEY_NODE SrcNode;
+ PCM_KEY_NODE DestNode = NULL;
+ HCELL_INDEX NewKeyCell = HCELL_NIL;
+ HCELL_INDEX NewClassCell = HCELL_NIL, NewSecCell = HCELL_NIL;
+ HCELL_INDEX SubKey, NewSubKey;
+ ULONG Index, SubKeyCount;
+
+ PAGED_CODE();
+
+ DPRINT("CmpDeepCopyKeyInternal(0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X)\n",
+ SourceHive,
+ SrcKeyCell,
+ DestinationHive,
+ Parent,
+ StorageType,
+ DestKeyCell);
+
+ /* Get the source cell node */
+ SrcNode = HvGetCell(SourceHive, SrcKeyCell);
+ ASSERT(SrcNode);
+
+ /* Sanity check */
+ ASSERT(SrcNode->Signature == CM_KEY_NODE_SIGNATURE);
+
+ /* Create a simple copy of the source key */
+ NewKeyCell = CmpCopyCell(SourceHive,
+ SrcKeyCell,
+ DestinationHive,
+ StorageType);
+ if (NewKeyCell == HCELL_NIL)
+ {
+ /* Not enough storage space */
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ /* Get the destination cell node */
+ DestNode = HvGetCell(DestinationHive, NewKeyCell);
+ ASSERT(DestNode);
+
+ /* Set the parent and copy the flags */
+ DestNode->Parent = Parent;
+ DestNode->Flags = (SrcNode->Flags & KEY_COMP_NAME); // Keep only the single permanent flag
+ if (Parent == HCELL_NIL)
+ {
+ /* This is the new root node */
+ DestNode->Flags |= KEY_HIVE_ENTRY | KEY_NO_DELETE;
+ }
+
+ /* Copy the class cell */
+ if (SrcNode->ClassLength > 0)
+ {
+ NewClassCell = CmpCopyCell(SourceHive,
+ SrcNode->Class,
+ DestinationHive,
+ StorageType);
+ if (NewClassCell == HCELL_NIL)
+ {
+ /* Not enough storage space */
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ DestNode->Class = NewClassCell;
+ DestNode->ClassLength = SrcNode->ClassLength;
+ }
+ else
+ {
+ DestNode->Class = HCELL_NIL;
+ DestNode->ClassLength = 0;
+ }
+
+ /* Copy the security cell (FIXME: HACKish poor-man version) */
+ if (SrcNode->Security != HCELL_NIL)
+ {
+ NewSecCell = CmpCopyCell(SourceHive,
+ SrcNode->Security,
+ DestinationHive,
+ StorageType);
+ if (NewSecCell == HCELL_NIL)
+ {
+ /* Not enough storage space */
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+ }
+ DestNode->Security = NewSecCell;
+
+ /* Copy the value list */
+ Status = CmpCopyKeyValueList(SourceHive,
+ &SrcNode->ValueList,
+ DestinationHive,
+ &DestNode->ValueList,
+ StorageType);
+ if (!NT_SUCCESS(Status))
+ goto Cleanup;
+
+ /* Clear the invalid subkey index */
+ DestNode->SubKeyCounts[Stable] = DestNode->SubKeyCounts[Volatile] = 0;
+ DestNode->SubKeyLists[Stable] = DestNode->SubKeyLists[Volatile] = HCELL_NIL;
+
+ /* Calculate the total number of subkeys */
+ SubKeyCount = SrcNode->SubKeyCounts[Stable] + SrcNode->SubKeyCounts[Volatile];
+
+ /* Loop through all the subkeys */
+ for (Index = 0; Index < SubKeyCount; Index++)
+ {
+ /* Get the subkey */
+ SubKey = CmpFindSubKeyByNumber(SourceHive, SrcNode, Index);
+ ASSERT(SubKey != HCELL_NIL);
+
+ /* Call the function recursively for the subkey */
+ //
+ // FIXME: Danger!! Kernel stack exhaustion!!
+ //
+ Status = CmpDeepCopyKeyInternal(SourceHive,
+ SubKey,
+ DestinationHive,
+ NewKeyCell,
+ StorageType,
+ &NewSubKey);
+ if (!NT_SUCCESS(Status))
+ goto Cleanup;
+
+ /* Add the copy of the subkey to the new key */
+ if (!CmpAddSubKey(DestinationHive,
+ NewKeyCell,
+ NewSubKey))
+ {
+ /* Cleanup allocated cell */
+ HvFreeCell(DestinationHive, NewSubKey);
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+ }
+
+ /* Set success */
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+
+ /* Release the cells */
+ if (DestNode) HvReleaseCell(DestinationHive, NewKeyCell);
+ if (SrcNode) HvReleaseCell(SourceHive, SrcKeyCell);
+
+ /* Cleanup allocated cells in case of failure */
+ if (!NT_SUCCESS(Status))
+ {
+ if (NewSecCell != HCELL_NIL)
+ HvFreeCell(DestinationHive, NewSecCell);
+
+ if (NewClassCell != HCELL_NIL)
+ HvFreeCell(DestinationHive, NewClassCell);
+
+ if (NewKeyCell != HCELL_NIL)
+ HvFreeCell(DestinationHive, NewKeyCell);
+
+ NewKeyCell = HCELL_NIL;
+ }
+
+ /* Set the cell index if requested and return status */
+ if (DestKeyCell) *DestKeyCell = NewKeyCell;
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+CmpDeepCopyKey(IN PHHIVE SourceHive,
+ IN HCELL_INDEX SrcKeyCell,
+ IN PHHIVE DestinationHive,
+ IN HSTORAGE_TYPE StorageType,
+ OUT PHCELL_INDEX DestKeyCell OPTIONAL)
+{
+ /* Call the internal function */
+ return CmpDeepCopyKeyInternal(SourceHive,
+ SrcKeyCell,
+ DestinationHive,
+ HCELL_NIL,
+ StorageType,
+ DestKeyCell);
+}
+
+NTSTATUS
+NTAPI
+CmSaveKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
+ IN HANDLE FileHandle,
+ IN ULONG Flags)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PCMHIVE KeyHive = NULL;
+ PAGED_CODE();
+
+ DPRINT("CmSaveKey(0x%08X, 0x%08X, %lu)\n", Kcb, FileHandle, Flags);
+
+ /* Lock the registry and KCB */
+ CmpLockRegistry();
+ CmpAcquireKcbLockShared(Kcb);
+
+ if (Kcb->Delete)
+ {
+ /* The source key has been deleted, do nothing */
+ Status = STATUS_KEY_DELETED;
+ goto Cleanup;
+ }
+
+ if (Kcb->KeyHive == &CmiVolatileHive->Hive)
+ {
+ /* Keys that are directly in the master hive can't be saved */
+ Status = STATUS_ACCESS_DENIED;
+ goto Cleanup;
+ }
+
+ /* Create a new hive that will hold the key */
+ Status = CmpInitializeHive(&KeyHive,
+ HINIT_CREATE,
+ HIVE_VOLATILE,
+ HFILE_TYPE_PRIMARY,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ 0);
+ if (!NT_SUCCESS(Status)) goto Cleanup;
+
+ /* Copy the key recursively into the new hive */
+ Status = CmpDeepCopyKey(Kcb->KeyHive,
+ Kcb->KeyCell,
+ &KeyHive->Hive,
+ Stable,
+ &KeyHive->Hive.BaseBlock->RootCell);
+ if (!NT_SUCCESS(Status)) goto Cleanup;
+
+ /* Set the primary handle of the hive */
+ KeyHive->FileHandles[HFILE_TYPE_PRIMARY] = FileHandle;
+
+ /* Dump the hive into the file */
+ HvWriteHive(&KeyHive->Hive);
+
+Cleanup:
+
+ /* Free the hive */
+ if (KeyHive) CmpDestroyHive(KeyHive);
+
+ /* Release the locks */
+ CmpReleaseKcbLock(Kcb);
+ CmpUnlockRegistry();
+
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+CmSaveMergedKeys(IN PCM_KEY_CONTROL_BLOCK HighKcb,
+ IN PCM_KEY_CONTROL_BLOCK LowKcb,
+ IN HANDLE FileHandle)
+{
+ PCMHIVE KeyHive = NULL;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ DPRINT("CmSaveKey(%p, %p, %p)\n", HighKcb, LowKcb, FileHandle);
+
+ /* Lock the registry and the KCBs */
+ CmpLockRegistry();
+ CmpAcquireKcbLockShared(HighKcb);
+ CmpAcquireKcbLockShared(LowKcb);
+
+ if (LowKcb->Delete || HighKcb->Delete)
+ {
+ /* The source key has been deleted, do nothing */
+ Status = STATUS_KEY_DELETED;
+ goto done;
+ }
+
+ /* Create a new hive that will hold the key */
+ Status = CmpInitializeHive(&KeyHive,
+ HINIT_CREATE,
+ HIVE_VOLATILE,
+ HFILE_TYPE_PRIMARY,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ 0);
+ if (!NT_SUCCESS(Status))
+ goto done;
+
+ /* Copy the low precedence key recursively into the new hive */
+ Status = CmpDeepCopyKey(LowKcb->KeyHive,
+ LowKcb->KeyCell,
+ &KeyHive->Hive,
+ Stable,
+ &KeyHive->Hive.BaseBlock->RootCell);
+ if (!NT_SUCCESS(Status))
+ goto done;
+
+ /* Copy the high precedence key recursively into the new hive */
+ Status = CmpDeepCopyKey(HighKcb->KeyHive,
+ HighKcb->KeyCell,
+ &KeyHive->Hive,
+ Stable,
+ &KeyHive->Hive.BaseBlock->RootCell);
+ if (!NT_SUCCESS(Status))
+ goto done;
+
+ /* Set the primary handle of the hive */
+ KeyHive->FileHandles[HFILE_TYPE_PRIMARY] = FileHandle;
+
+ /* Dump the hive into the file */
+ HvWriteHive(&KeyHive->Hive);
+
+done:
+ /* Free the hive */
+ if (KeyHive)
+ CmpDestroyHive(KeyHive);
+
+ /* Release the locks */
+ CmpReleaseKcbLock(LowKcb);
+ CmpReleaseKcbLock(HighKcb);
+ CmpUnlockRegistry();
+
+ return Status;
+}