From 0d6c552a9dc4a1b546f564cb994a164ff2a49ee9 Mon Sep 17 00:00:00 2001 From: Aleksandar Andrejevic Date: Thu, 29 May 2014 19:44:36 +0000 Subject: [PATCH] [NTOSKRNL] Implement NtSaveKey/NtSaveKeyEx and its backend CmSaveKey. CORE-8259 #resolve #comment Committed in revision r63495. svn path=/trunk/; revision=63495 --- reactos/ntoskrnl/config/cmapi.c | 225 +++++++++++++++++++++++++ reactos/ntoskrnl/config/cminit.c | 22 +++ reactos/ntoskrnl/config/cmvalue.c | 59 +++++++ reactos/ntoskrnl/config/ntapi.c | 43 ++++- reactos/ntoskrnl/include/internal/cm.h | 43 +++++ 5 files changed, 388 insertions(+), 4 deletions(-) diff --git a/reactos/ntoskrnl/config/cmapi.c b/reactos/ntoskrnl/config/cmapi.c index ed2eda03410..e52f572b0ed 100644 --- a/reactos/ntoskrnl/config/cmapi.c +++ b/reactos/ntoskrnl/config/cmapi.c @@ -5,6 +5,7 @@ * PURPOSE: Configuration Manager - Internal Registry APIs * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) * Eric Kohl + * Aleksandar Andrejevic */ /* INCLUDES ******************************************************************/ @@ -2120,3 +2121,227 @@ CmCountOpenSubKeys(IN PCM_KEY_CONTROL_BLOCK RootKcb, return SubKeys; } + +HCELL_INDEX +NTAPI +CmpCopyCell(IN PHHIVE SourceHive, + IN HCELL_INDEX SourceCell, + IN PHHIVE DestinationHive, + IN HSTORAGE_TYPE StorageType) +{ + PCELL_DATA SourceData; + PCELL_DATA DestinationData = NULL; + HCELL_INDEX DestinationCell = HCELL_NIL; + LONG DataSize; + PAGED_CODE(); + + /* Get the data and the size of the source cell */ + SourceData = HvGetCell(SourceHive, SourceCell); + DataSize = HvGetCellSize(SourceHive, SourceData); + + /* Allocate a new cell in the destination hive */ + DestinationCell = HvAllocateCell(DestinationHive, + DataSize, + StorageType, + HCELL_NIL); + if (DestinationCell == HCELL_NIL) goto Cleanup; + + /* Get the data of the destination cell */ + DestinationData = HvGetCell(DestinationHive, DestinationCell); + + /* Copy the data from the source cell to the destination cell */ + RtlMoveMemory(DestinationData, SourceData, DataSize); + +Cleanup: + + /* Release the cells */ + if (SourceData) HvReleaseCell(SourceHive, SourceCell); + if (DestinationData) HvReleaseCell(DestinationHive, DestinationCell); + + /* Return the destination cell index */ + return DestinationCell; +} + +static +NTSTATUS +NTAPI +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, DestNode; + HCELL_INDEX NewKeyCell, 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); + + /* 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); + + /* Set the parent */ + DestNode->Parent = Parent; + + // TODO: These should also be copied! + DestNode->Security = DestNode->Class = HCELL_NIL; + + /* 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 */ + 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)) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Cleanup; + } + } + + /* Set the cell index if requested and return success */ + if (DestKeyCell) *DestKeyCell = NewKeyCell; + Status = STATUS_SUCCESS; + +Cleanup: + + /* Release the cells */ + if (SrcNode) HvReleaseCell(SourceHive, SrcKeyCell); + if (DestNode) HvReleaseCell(DestinationHive, 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; + } + + /* 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; +} diff --git a/reactos/ntoskrnl/config/cminit.c b/reactos/ntoskrnl/config/cminit.c index 897e58460ce..8689e8eab5d 100644 --- a/reactos/ntoskrnl/config/cminit.c +++ b/reactos/ntoskrnl/config/cminit.c @@ -238,6 +238,28 @@ CmpInitializeHive(OUT PCMHIVE *RegistryHive, return STATUS_SUCCESS; } +NTSTATUS +NTAPI +CmpDestroyHive(IN PCMHIVE CmHive) +{ + /* Remove the hive from the list */ + ExAcquirePushLockExclusive(&CmpHiveListHeadLock); + RemoveEntryList(&CmHive->HiveList); + ExReleasePushLock(&CmpHiveListHeadLock); + + /* Delete the flusher lock */ + ExDeleteResourceLite(CmHive->FlusherLock); + ExFreePoolWithTag(CmHive->FlusherLock, TAG_CM); + + /* Delete the view lock */ + ExFreePoolWithTag(CmHive->ViewLock, TAG_CM); + + /* Free the hive */ + HvFree(&CmHive->Hive); + + return STATUS_SUCCESS; +} + NTSTATUS NTAPI CmpOpenHiveFiles(IN PCUNICODE_STRING BaseName, diff --git a/reactos/ntoskrnl/config/cmvalue.c b/reactos/ntoskrnl/config/cmvalue.c index 0c401e67863..30fd6229127 100644 --- a/reactos/ntoskrnl/config/cmvalue.c +++ b/reactos/ntoskrnl/config/cmvalue.c @@ -361,3 +361,62 @@ CmpRemoveValueFromList(IN PHHIVE Hive, ChildList->Count = Count; return STATUS_SUCCESS; } + +NTSTATUS +NTAPI +CmpCopyKeyValueList(IN PHHIVE SourceHive, + IN PCHILD_LIST SrcValueList, + IN PHHIVE DestinationHive, + IN OUT PCHILD_LIST DestValueList, + IN HSTORAGE_TYPE StorageType) + +{ + NTSTATUS Status = STATUS_SUCCESS; + HCELL_INDEX CellIndex = HCELL_NIL; + ULONG Index; + PCELL_DATA SrcListData = NULL; + PCELL_DATA DestListData = NULL; + + PAGED_CODE(); + + /* Set the count */ + DestValueList->Count = SrcValueList->Count; + + /* Check if the list is empty */ + if (!DestValueList->Count) + { + DestValueList->List = HCELL_NIL; + return STATUS_SUCCESS; + } + + /* Create a simple copy of the list */ + CellIndex = CmpCopyCell(SourceHive, + SrcValueList->List, + DestinationHive, + StorageType); + if (CellIndex == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES; + + /* Get the source and the destination value list */ + SrcListData = HvGetCell(SourceHive, SrcValueList->List); + DestListData = HvGetCell(DestinationHive, CellIndex); + + /* Copy the actual values */ + for (Index = 0; Index < SrcValueList->Count; Index++) + { + DestListData->u.KeyList[Index] = CmpCopyCell(SourceHive, + SrcListData->u.KeyList[Index], + DestinationHive, + StorageType); + if (DestListData->u.KeyList[Index] == HCELL_NIL) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + } + + /* Release the cells */ + if (SrcListData) HvReleaseCell(SourceHive, SrcValueList->List); + if (DestListData) HvReleaseCell(DestinationHive, CellIndex); + + return Status; +} diff --git a/reactos/ntoskrnl/config/ntapi.c b/reactos/ntoskrnl/config/ntapi.c index fe72872388b..b3bf0ece98d 100644 --- a/reactos/ntoskrnl/config/ntapi.c +++ b/reactos/ntoskrnl/config/ntapi.c @@ -1148,8 +1148,8 @@ NTAPI NtSaveKey(IN HANDLE KeyHandle, IN HANDLE FileHandle) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + /* Call the extended API */ + return NtSaveKeyEx(KeyHandle, FileHandle, REG_STANDARD_FORMAT); } NTSTATUS @@ -1158,8 +1158,43 @@ NtSaveKeyEx(IN HANDLE KeyHandle, IN HANDLE FileHandle, IN ULONG Flags) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + NTSTATUS Status; + PCM_KEY_BODY KeyObject; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + + PAGED_CODE(); + + DPRINT("NtSaveKeyEx(0x%08X, 0x%08X, %lu)\n", KeyHandle, FileHandle, Flags); + + /* Verify the flags */ + if ((Flags != REG_STANDARD_FORMAT) + && (Flags != REG_LATEST_FORMAT) + && (Flags != REG_NO_COMPRESSION)) + { + /* Only one of these values can be specified */ + return STATUS_INVALID_PARAMETER; + } + + /* Check for the SeBackupPrivilege */ + if (!SeSinglePrivilegeCheck(SeBackupPrivilege, PreviousMode)) + { + return STATUS_PRIVILEGE_NOT_HELD; + } + + /* Verify that the handle is valid and is a registry key */ + Status = ObReferenceObjectByHandle(KeyHandle, + KEY_READ, + CmpKeyObjectType, + PreviousMode, + (PVOID*)&KeyObject, + NULL); + if (!NT_SUCCESS(Status)) return Status; + + /* Call the internal API */ + Status = CmSaveKey(KeyObject->KeyControlBlock, FileHandle, Flags); + + ObDereferenceObject(KeyObject); + return Status; } NTSTATUS diff --git a/reactos/ntoskrnl/include/internal/cm.h b/reactos/ntoskrnl/include/internal/cm.h index d34f5b32bb2..d4d7c9e5156 100644 --- a/reactos/ntoskrnl/include/internal/cm.h +++ b/reactos/ntoskrnl/include/internal/cm.h @@ -786,6 +786,12 @@ CmpInitializeHive( IN ULONG CheckFlags ); +NTSTATUS +NTAPI +CmpDestroyHive( + IN PCMHIVE CmHive +); + PSECURITY_DESCRIPTOR NTAPI CmpHiveRootSecurityDescriptor( @@ -1322,6 +1328,16 @@ CmpGetValueData( OUT PHCELL_INDEX CellToRelease ); +NTSTATUS +NTAPI +CmpCopyKeyValueList( + IN PHHIVE SourceHive, + IN PCHILD_LIST SrcValueList, + IN PHHIVE DestinationHive, + IN OUT PCHILD_LIST DestValueList, + IN HSTORAGE_TYPE StorageType +); + // // Boot Routines // @@ -1527,6 +1543,33 @@ CmCountOpenSubKeys( IN BOOLEAN RemoveEmptyCacheEntries ); +HCELL_INDEX +NTAPI +CmpCopyCell( + IN PHHIVE SourceHive, + IN HCELL_INDEX SourceCell, + IN PHHIVE DestinationHive, + IN HSTORAGE_TYPE StorageType +); + +NTSTATUS +NTAPI +CmpDeepCopyKey( + IN PHHIVE SourceHive, + IN HCELL_INDEX SrcKeyCell, + IN PHHIVE DestinationHive, + IN HSTORAGE_TYPE StorageType, + OUT PHCELL_INDEX DestKeyCell OPTIONAL +); + +NTSTATUS +NTAPI +CmSaveKey( + IN PCM_KEY_CONTROL_BLOCK Kcb, + IN HANDLE FileHandle, + IN ULONG Flags +); + // // Startup and Shutdown // -- 2.17.1