[NTOS/CM]
[reactos.git] / reactos / ntoskrnl / config / cmapi.c
index e532450..dd14082 100644 (file)
@@ -5,6 +5,7 @@
  * 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 ******************************************************************/
@@ -380,9 +381,10 @@ CmpQueryKeyData(IN PHHIVE Hive,
                 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)
@@ -516,8 +518,36 @@ CmpQueryKeyData(IN PHHIVE Hive,
             /* 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
             {
@@ -563,13 +593,37 @@ CmpQueryKeyData(IN PHHIVE Hive,
             /* 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 */
+                NT_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;
 
@@ -1381,7 +1435,7 @@ CmpQueryKeyDataFromCache(
     }
 
     /* Validate buffer length (we do not copy the name!) */
-    *ResultLength = sizeof(KeyCachedInfo);
+    *ResultLength = sizeof(*KeyCachedInfo);
     if (Length < *ResultLength)
     {
         return STATUS_BUFFER_TOO_SMALL;
@@ -1460,13 +1514,104 @@ CmpQueryFlagsInformation(
     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 = NeededLength + FIELD_OFFSET(KEY_NAME_INFORMATION, Name[0]);
+        if (Length < *ResultLength)
+            return STATUS_BUFFER_TOO_SMALL;
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        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)
+    {
+        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;
@@ -1534,12 +1679,12 @@ CmQueryKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
                                               ResultLength);
             break;
 
-        /* Unsupported class for now */
         case KeyNameInformation:
-
-            /* Print message and fail */
-            DPRINT1("Unsupported class: %d!\n", KeyInformationClass);
-            Status = STATUS_NOT_IMPLEMENTED;
+            /* Call the internal API */
+            Status = CmpQueryNameInformation(Kcb,
+                                             KeyInformation,
+                                             Length,
+                                             ResultLength);
             break;
 
         /* Illegal classes */
@@ -1843,8 +1988,7 @@ CmLoadKey(IN POBJECT_ATTRIBUTES TargetKey,
     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 */
@@ -2068,3 +2212,235 @@ 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;
+    PCM_KEY_NODE DestNode = NULL;
+    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;
+    }
+
+    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;
+}