/* * PROJECT: ReactOS Kernel * LICENSE: GPL - See COPYING in the top level directory * FILE: ntoskrnl/config/cmapi.c * PURPOSE: Configuration Manager - Internal Registry APIs * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) */ /* INCLUDES ******************************************************************/ #include "ntoskrnl.h" #define NDEBUG #include "debug.h" /* FUNCTIONS *****************************************************************/ BOOLEAN NTAPI CmpDoFlushAll(IN BOOLEAN ForceFlush) { PLIST_ENTRY NextEntry; PCMHIVE Hive; NTSTATUS Status; BOOLEAN Result = TRUE; /* Make sure that the registry isn't read-only now */ if (CmpNoWrite) return TRUE; /* Otherwise, acquire the hive list lock and disable force flush */ CmpForceForceFlush = FALSE; ExAcquirePushLockShared(&CmpHiveListHeadLock); /* Loop the hive list */ NextEntry = CmpHiveListHead.Flink; while (NextEntry != &CmpHiveListHead) { /* Get the hive */ Hive = CONTAINING_RECORD(NextEntry, CMHIVE, HiveList); if (!(Hive->Hive.HiveFlags & HIVE_NOLAZYFLUSH)) { /* Acquire the flusher lock */ ExAcquirePushLockExclusive((PVOID)&Hive->FlusherLock); /* Do the sync */ Status = HvSyncHive(&Hive->Hive); /* If something failed - set the flag and continue looping*/ if (!NT_SUCCESS(Status)) Result = FALSE; /* Release the flusher lock */ ExReleasePushLock((PVOID)&Hive->FlusherLock); } /* Try the next entry */ NextEntry = NextEntry->Flink; } /* Release lock and return */ ExReleasePushLock(&CmpHiveListHeadLock); return Result; } NTSTATUS NTAPI CmpSetValueKeyNew(IN PHHIVE Hive, IN PCM_KEY_NODE Parent, IN PUNICODE_STRING ValueName, IN ULONG Index, IN ULONG Type, IN PVOID Data, IN ULONG DataSize, IN ULONG StorageType, IN ULONG SmallData) { PCELL_DATA CellData; HCELL_INDEX ValueCell; NTSTATUS Status; /* Check if we already have a value list */ if (Parent->ValueList.Count) { /* Then make sure it's valid and dirty it */ ASSERT(Parent->ValueList.List != HCELL_NIL); HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE); } /* Allocate avalue cell */ ValueCell = HvAllocateCell(Hive, FIELD_OFFSET(CM_KEY_VALUE, Name) + CmpNameSize(Hive, ValueName), StorageType, HCELL_NIL); if (ValueCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES; /* Get the actual data for it */ CellData = HvGetCell(Hive, ValueCell); if (!CellData) ASSERT(FALSE); /* Now we can release it, make sure it's also dirty */ HvReleaseCell(Hive, ValueCell); ASSERT(HvIsCellDirty(Hive, ValueCell)); /* Set it up and copy the name */ CellData->u.KeyValue.Signature = CM_KEY_VALUE_SIGNATURE; CellData->u.KeyValue.Flags = 0; CellData->u.KeyValue.Type = Type; CellData->u.KeyValue.NameLength = CmpCopyName(Hive, CellData->u.KeyValue.Name, ValueName); if (CellData->u.KeyValue.NameLength < ValueName->Length) { /* This is a compressed name */ CellData->u.KeyValue.Flags = VALUE_COMP_NAME; } /* Check if this is a normal key */ if (DataSize > CM_KEY_VALUE_SMALL) { /* Build a data cell for it */ Status = CmpSetValueDataNew(Hive, Data, DataSize, StorageType, ValueCell, &CellData->u.KeyValue.Data); if (!NT_SUCCESS(Status)) { /* We failed, free the cell */ HvFreeCell(Hive, ValueCell); return Status; } /* Otherwise, set the data length, and make sure the data is dirty */ CellData->u.KeyValue.DataLength = DataSize; ASSERT(HvIsCellDirty(Hive, CellData->u.KeyValue.Data)); } else { /* This is a small key, set the data directly inside */ CellData->u.KeyValue.DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE; CellData->u.KeyValue.Data = SmallData; } /* Add this value cell to the child list */ Status = CmpAddValueToList(Hive, ValueCell, Index, StorageType, &Parent->ValueList); /* If we failed, free the entire cell, including the data */ if (!NT_SUCCESS(Status)) CmpFreeValue(Hive, ValueCell); /* Return Status */ return Status; } NTSTATUS NTAPI CmpSetValueKeyExisting(IN PHHIVE Hive, IN HCELL_INDEX OldChild, IN PCM_KEY_VALUE Value, IN ULONG Type, IN PVOID Data, IN ULONG DataSize, IN ULONG StorageType, IN ULONG TempData) { HCELL_INDEX DataCell, NewCell; PCELL_DATA CellData; ULONG Length; BOOLEAN WasSmall, IsSmall; /* Mark the old child cell dirty */ HvMarkCellDirty(Hive, OldChild, FALSE); /* See if this is a small or normal key */ WasSmall = CmpIsKeyValueSmall(&Length, Value->DataLength); /* See if our new data can fit in a small key */ IsSmall = (DataSize <= CM_KEY_VALUE_SMALL) ? TRUE: FALSE; /* Big keys are unsupported */ ASSERT_VALUE_BIG(Hive, Length); ASSERT_VALUE_BIG(Hive, DataSize); /* Mark the old value dirty */ CmpMarkValueDataDirty(Hive, Value); /* Check if we have a small key */ if (IsSmall) { /* Check if we had a normal key with some data in it */ if (!(WasSmall) && (Length > 0)) { /* Free the previous data */ CmpFreeValueData(Hive, Value->Data, Length); } /* Write our data directly */ Value->DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE; Value->Data = TempData; Value->Type = Type; return STATUS_SUCCESS; } else { /* We have a normal key. Was the old cell also normal and had data? */ if (!(WasSmall) && (Length > 0)) { /* Get the current data cell and actual data inside it */ DataCell = Value->Data; ASSERT(DataCell != HCELL_NIL); CellData = HvGetCell(Hive, DataCell); if (!CellData) return STATUS_INSUFFICIENT_RESOURCES; /* Immediately release the cell */ HvReleaseCell(Hive, DataCell); /* Make sure that the data cell actually has a size */ ASSERT(HvGetCellSize(Hive, CellData) > 0); /* Check if the previous data cell could fit our new data */ if (DataSize <= (ULONG)(HvGetCellSize(Hive, CellData))) { /* Re-use it then */ NewCell = DataCell; } else { /* Otherwise, re-allocate the current data cell */ NewCell = HvReallocateCell(Hive, DataCell, DataSize); if (NewCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES; } } else { /* This was a small key, or a key with no data, allocate a cell */ NewCell = HvAllocateCell(Hive, DataSize, StorageType, HCELL_NIL); if (NewCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES; } /* Now get the actual data for our data cell */ CellData = HvGetCell(Hive, NewCell); if (!CellData) ASSERT(FALSE); /* Release it immediately */ HvReleaseCell(Hive, NewCell); /* Copy our data into the data cell's buffer, and set up the value */ RtlCopyMemory(CellData, Data, DataSize); Value->Data = NewCell; Value->DataLength = DataSize; Value->Type = Type; /* Return success */ ASSERT(HvIsCellDirty(Hive, NewCell)); return STATUS_SUCCESS; } } NTSTATUS NTAPI CmSetValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb, IN PUNICODE_STRING ValueName, IN ULONG Type, IN PVOID Data, IN ULONG DataLength) { PHHIVE Hive; PCM_KEY_NODE Parent; PCM_KEY_VALUE Value = NULL; HCELL_INDEX CurrentChild, Cell; NTSTATUS Status; BOOLEAN Found, Result; ULONG Count, ChildIndex, SmallData, Storage; VALUE_SEARCH_RETURN_TYPE SearchResult; /* Acquire hive lock */ CmpLockRegistry(); CmpAcquireKcbLockShared(Kcb); /* Sanity check */ ASSERT(sizeof(ULONG) == CM_KEY_VALUE_SMALL); /* Don't touch deleted KCBs */ DoAgain: if (Kcb->Delete) { /* Fail */ Status = STATUS_KEY_DELETED; goto Quickie; } /* Don't let anyone mess with symlinks */ if ((Kcb->Flags & KEY_SYM_LINK) && ((Type != REG_LINK) || !(ValueName) || !(RtlEqualUnicodeString(&CmSymbolicLinkValueName, ValueName, TRUE)))) { /* Invalid modification of a symlink key */ Status = STATUS_ACCESS_DENIED; goto Quickie; } /* Search for the value */ SearchResult = CmpCompareNewValueDataAgainstKCBCache(Kcb, ValueName, Type, Data, DataLength); if (SearchResult == SearchNeedExclusiveLock) { /* Try again with the exclusive lock */ CmpConvertKcbSharedToExclusive(Kcb); goto DoAgain; } else if (SearchResult == SearchSuccess) { /* We don't actually need to do anything! */ Status = STATUS_SUCCESS; goto Quickie; } /* We need the exclusive KCB lock now */ if (!(CmpIsKcbLockedExclusive(Kcb)) && !(CmpTryToConvertKcbSharedToExclusive(Kcb))) { /* Acquire exclusive lock */ CmpConvertKcbSharedToExclusive(Kcb); } /* Get pointer to key cell */ Hive = Kcb->KeyHive; Cell = Kcb->KeyCell; /* Prepare to scan the key node */ Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell); Count = Parent->ValueList.Count; Found = FALSE; if (Count > 0) { /* Try to find the existing name */ Result = CmpFindNameInList(Hive, &Parent->ValueList, ValueName, &ChildIndex, &CurrentChild); if (!Result) { /* Fail */ Status = STATUS_INSUFFICIENT_RESOURCES; goto Quickie; } /* Check if we found something */ if (CurrentChild != HCELL_NIL) { /* Get its value */ Value = (PCM_KEY_VALUE)HvGetCell(Hive, CurrentChild); if (!Value) { /* Fail */ Status = STATUS_INSUFFICIENT_RESOURCES; goto Quickie; } /* Remember that we found it */ Found = TRUE; } } else { /* No child list, we'll need to add it */ ChildIndex = 0; } /* The KCB must be locked exclusive at this point */ ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) || (CmpTestRegistryLockExclusive() == TRUE)); /* Mark the cell dirty */ HvMarkCellDirty(Hive, Cell, FALSE); /* Get the storage type */ Storage = HvGetCellType(Cell); /* Check if this is small data */ SmallData = 0; if ((DataLength <= CM_KEY_VALUE_SMALL) && (DataLength > 0)) { /* Copy it */ RtlCopyMemory(&SmallData, Data, DataLength); } /* Check if we didn't find a matching key */ if (!Found) { /* Call the internal routine */ Status = CmpSetValueKeyNew(Hive, Parent, ValueName, ChildIndex, Type, Data, DataLength, Storage, SmallData); } else { /* Call the internal routine */ Status = CmpSetValueKeyExisting(Hive, CurrentChild, Value, Type, Data, DataLength, Storage, SmallData); } /* Check for success */ if (NT_SUCCESS(Status)) { /* Check if the maximum value name length changed */ ASSERT(Parent->MaxValueNameLen == Kcb->KcbMaxValueNameLen); if (Parent->MaxValueNameLen < ValueName->Length) { /* Set the new values */ Parent->MaxValueNameLen = ValueName->Length; Kcb->KcbMaxValueNameLen = ValueName->Length; } /* Check if the maximum data length changed */ ASSERT(Parent->MaxValueDataLen == Kcb->KcbMaxValueDataLen); if (Parent->MaxValueDataLen < DataLength) { /* Update it */ Parent->MaxValueDataLen = DataLength; Kcb->KcbMaxValueDataLen = Parent->MaxValueDataLen; } /* Save the write time */ KeQuerySystemTime(&Parent->LastWriteTime); KeQuerySystemTime(&Kcb->KcbLastWriteTime); /* Check if the cell is cached */ if ((Found) && (CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList))) { /* Shouldn't happen */ ASSERT(FALSE); } else { /* 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 = Parent->ValueList.Count; Kcb->ValueCache.ValueList = Parent->ValueList.List; } } Quickie: /* Release the locks */ CmpReleaseKcbLock(Kcb); CmpUnlockRegistry(); return Status; } NTSTATUS NTAPI CmDeleteValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb, IN UNICODE_STRING ValueName) { NTSTATUS Status = STATUS_OBJECT_NAME_NOT_FOUND; PHHIVE Hive; PCM_KEY_NODE Parent; HCELL_INDEX ChildCell, Cell; PCHILD_LIST ChildList; PCM_KEY_VALUE Value = NULL; ULONG ChildIndex; BOOLEAN Result; /* Acquire hive lock */ CmpLockRegistry(); /* Lock KCB exclusively */ CmpAcquireKcbLockExclusive(Kcb); /* Don't touch deleted keys */ if (Kcb->Delete) { /* Undo everything */ CmpReleaseKcbLock(Kcb); CmpUnlockRegistry(); return STATUS_KEY_DELETED; } /* Get the hive and the cell index */ Hive = Kcb->KeyHive; Cell = Kcb->KeyCell; /* Get the parent key node */ Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell); if (!Parent) { /* Fail */ Status = STATUS_INSUFFICIENT_RESOURCES; goto Quickie; } /* Get the value list and check if it has any entries */ ChildList = &Parent->ValueList; if (ChildList->Count) { /* Try to find this value */ Result = CmpFindNameInList(Hive, ChildList, &ValueName, &ChildIndex, &ChildCell); if (!Result) { /* Fail */ Status = STATUS_INSUFFICIENT_RESOURCES; goto Quickie; } /* Value not found, return error */ if (ChildCell == HCELL_NIL) goto Quickie; /* We found the value, mark all relevant cells dirty */ HvMarkCellDirty(Hive, Cell, FALSE); HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE); HvMarkCellDirty(Hive, ChildCell, FALSE); /* Get the key value */ Value = (PCM_KEY_VALUE)HvGetCell(Hive,ChildCell); if (!Value) ASSERT(FALSE); /* Mark it and all related data as dirty */ CmpMarkValueDataDirty(Hive, Value); /* Ssanity checks */ ASSERT(HvIsCellDirty(Hive, Parent->ValueList.List)); ASSERT(HvIsCellDirty(Hive, ChildCell)); /* Remove the value from the child list */ Status = CmpRemoveValueFromList(Hive, ChildIndex, ChildList); if(!NT_SUCCESS(Status)) goto Quickie; /* Remove the value and its data itself */ if (!CmpFreeValue(Hive, ChildCell)) { /* Failed to free the value, fail */ Status = STATUS_INSUFFICIENT_RESOURCES; goto Quickie; } /* Set the last write time */ KeQuerySystemTime(&Parent->LastWriteTime); KeQuerySystemTime(&Kcb->KcbLastWriteTime); /* Sanity check */ ASSERT(Parent->MaxValueNameLen == Kcb->KcbMaxValueNameLen); ASSERT(Parent->MaxValueDataLen == Kcb->KcbMaxValueDataLen); ASSERT(HvIsCellDirty(Hive, Cell)); /* Check if the value list is empty now */ if (!Parent->ValueList.Count) { /* Then clear key node data */ Parent->MaxValueNameLen = 0; Parent->MaxValueDataLen = 0; 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; /* Change default Status to success */ Status = STATUS_SUCCESS; } Quickie: /* Release the parent cell, if any */ if (Parent) HvReleaseCell(Hive, Cell); /* Check if we had a value */ if (Value) { /* Release the child cell */ ASSERT(ChildCell != HCELL_NIL); HvReleaseCell(Hive, ChildCell); } /* Release locks */ CmpReleaseKcbLock(Kcb); CmpUnlockRegistry(); return Status; } NTSTATUS NTAPI CmQueryValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb, IN UNICODE_STRING ValueName, IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, IN PVOID KeyValueInformation, IN ULONG Length, IN PULONG ResultLength) { NTSTATUS Status; PCM_KEY_VALUE ValueData; ULONG Index; BOOLEAN ValueCached = FALSE; PCM_CACHED_VALUE *CachedValue; HCELL_INDEX CellToRelease; VALUE_SEARCH_RETURN_TYPE Result; PHHIVE Hive; PAGED_CODE(); /* Acquire hive lock */ CmpLockRegistry(); /* Lock the KCB shared */ CmpAcquireKcbLockShared(Kcb); /* Don't touch deleted keys */ DoAgain: if (Kcb->Delete) { /* Undo everything */ CmpReleaseKcbLock(Kcb); CmpUnlockRegistry(); return STATUS_KEY_DELETED; } /* We don't deal with this yet */ if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND) { /* Shouldn't happen */ ASSERT(FALSE); } /* Get the hive */ Hive = Kcb->KeyHive; /* Find the key value */ Result = CmpFindValueByNameFromCache(Kcb, &ValueName, &CachedValue, &Index, &ValueData, &ValueCached, &CellToRelease); if (Result == SearchNeedExclusiveLock) { /* 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 */ ASSERT(ValueData != NULL); /* Query the information requested */ Result = CmpQueryKeyValueData(Kcb, CachedValue, ValueData, ValueCached, KeyValueInformationClass, KeyValueInformation, Length, ResultLength, &Status); if (Result == SearchNeedExclusiveLock) { /* Try with exclusive KCB lock */ CmpConvertKcbSharedToExclusive(Kcb); goto DoAgain; } } else { /* Failed to find the value */ Status = STATUS_OBJECT_NAME_NOT_FOUND; } /* If we have a cell to release, do so */ if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease); /* Release locks */ CmpReleaseKcbLock(Kcb); CmpUnlockRegistry(); return Status; } NTSTATUS NTAPI CmEnumerateValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb, IN ULONG Index, IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, IN PVOID KeyValueInformation, IN ULONG Length, IN PULONG ResultLength) { NTSTATUS Status; PHHIVE Hive; PCM_KEY_NODE Parent; HCELL_INDEX CellToRelease = HCELL_NIL, CellToRelease2 = HCELL_NIL; VALUE_SEARCH_RETURN_TYPE Result; BOOLEAN IndexIsCached, ValueIsCached = FALSE; PCELL_DATA CellData; PCM_CACHED_VALUE *CachedValue; PCM_KEY_VALUE ValueData = NULL; PAGED_CODE(); /* Acquire hive lock */ CmpLockRegistry(); /* Lock the KCB shared */ CmpAcquireKcbLockShared(Kcb); /* Don't touch deleted keys */ DoAgain: if (Kcb->Delete) { /* Undo everything */ CmpReleaseKcbLock(Kcb); CmpUnlockRegistry(); return STATUS_KEY_DELETED; } /* Get the hive and parent */ Hive = Kcb->KeyHive; Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell); if (!Parent) { /* Fail */ Status = STATUS_INSUFFICIENT_RESOURCES; goto Quickie; } /* Make sure the index is valid */ //if (Index >= Kcb->ValueCache.Count) if (Index >= Parent->ValueList.Count) { /* Release the cell and fail */ HvReleaseCell(Hive, Kcb->KeyCell); Status = STATUS_NO_MORE_ENTRIES; goto Quickie; } /* We don't deal with this yet */ if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND) { /* Shouldn't happen */ ASSERT(FALSE); } /* Find the value list */ Result = CmpGetValueListFromCache(Kcb, &CellData, &IndexIsCached, &CellToRelease); if (Result == SearchNeedExclusiveLock) { /* Check if we need an exclusive lock */ ASSERT(CellToRelease == HCELL_NIL); ASSERT(ValueData == NULL); /* Try with exclusive KCB lock */ CmpConvertKcbSharedToExclusive(Kcb); goto DoAgain; } else if (Result != SearchSuccess) { /* Sanity check */ ASSERT(CellData == NULL); /* Release the cell and fail */ Status = STATUS_INSUFFICIENT_RESOURCES; goto Quickie; } /* Now get the key value */ Result = CmpGetValueKeyFromCache(Kcb, CellData, Index, &CachedValue, &ValueData, IndexIsCached, &ValueIsCached, &CellToRelease2); if (Result == SearchNeedExclusiveLock) { /* Try with exclusive KCB lock */ CmpConvertKcbSharedToExclusive(Kcb); goto DoAgain; } else if (Result != SearchSuccess) { /* Sanity check */ ASSERT(ValueData == NULL); /* Release the cells and fail */ Status = STATUS_INSUFFICIENT_RESOURCES; goto Quickie; } /* Query the information requested */ Result = CmpQueryKeyValueData(Kcb, CachedValue, ValueData, ValueIsCached, KeyValueInformationClass, KeyValueInformation, Length, ResultLength, &Status); if (Result == SearchNeedExclusiveLock) { /* Try with exclusive KCB lock */ CmpConvertKcbSharedToExclusive(Kcb); goto DoAgain; } Quickie: /* If we have a cell to release, do so */ if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease); /* Release the parent cell */ HvReleaseCell(Hive, Kcb->KeyCell); /* If we have a cell to release, do so */ if (CellToRelease2 != HCELL_NIL) HvReleaseCell(Hive, CellToRelease2); /* Release locks */ CmpReleaseKcbLock(Kcb); CmpUnlockRegistry(); return Status; } NTSTATUS NTAPI CmpQueryKeyData(IN PHHIVE Hive, IN PCM_KEY_NODE Node, IN KEY_INFORMATION_CLASS KeyInformationClass, IN OUT PVOID KeyInformation, IN ULONG Length, IN OUT PULONG ResultLength) { NTSTATUS Status; ULONG Size, SizeLeft, MinimumSize; PKEY_INFORMATION Info = (PKEY_INFORMATION)KeyInformation; USHORT NameLength; /* Check if the value is compressed */ if (Node->Flags & KEY_COMP_NAME) { /* Get the compressed name size */ NameLength = CmpCompressedNameSize(Node->Name, Node->NameLength); } else { /* Get the real size */ NameLength = Node->NameLength; } /* Check what kind of information is being requested */ switch (KeyInformationClass) { /* Basic information */ case KeyBasicInformation: /* This is the size we need */ Size = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name) + NameLength; /* And this is the minimum we can work with */ MinimumSize = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name); /* Let the caller know and assume success */ *ResultLength = Size; Status = STATUS_SUCCESS; /* Check if the bufer we got is too small */ if (Length < MinimumSize) { /* Let the caller know and fail */ Status = STATUS_BUFFER_TOO_SMALL; break; } /* Copy the basic information */ Info->KeyBasicInformation.LastWriteTime = Node->LastWriteTime; Info->KeyBasicInformation.TitleIndex = 0; Info->KeyBasicInformation.NameLength = NameLength; /* Only the name is left */ SizeLeft = Length - MinimumSize; Size = NameLength; /* Check if we don't have enough space for the name */ if (SizeLeft < Size) { /* Truncate the name we'll return, and tell the caller */ Size = SizeLeft; Status = STATUS_BUFFER_OVERFLOW; } /* Check if this is a compressed key */ if (Node->Flags & KEY_COMP_NAME) { /* Copy the compressed name */ CmpCopyCompressedName(Info->KeyBasicInformation.Name, SizeLeft, Node->Name, Node->NameLength); } else { /* Otherwise, copy the raw name */ RtlCopyMemory(Info->KeyBasicInformation.Name, Node->Name, Size); } break; /* Node information */ case KeyNodeInformation: /* Calculate the size we need */ Size = FIELD_OFFSET(KEY_NODE_INFORMATION, Name) + NameLength + Node->ClassLength; /* And the minimum size we can support */ MinimumSize = FIELD_OFFSET(KEY_NODE_INFORMATION, Name); /* Return the size to the caller and assume succes */ *ResultLength = Size; Status = STATUS_SUCCESS; /* Check if the caller's buffer is too small */ if (Length < MinimumSize) { /* Let them know, and fail */ Status = STATUS_BUFFER_TOO_SMALL; break; } /* Copy the basic information */ Info->KeyNodeInformation.LastWriteTime = Node->LastWriteTime; Info->KeyNodeInformation.TitleIndex = 0; Info->KeyNodeInformation.ClassLength = Node->ClassLength; Info->KeyNodeInformation.NameLength = NameLength; /* Now the name is left */ SizeLeft = Length - MinimumSize; Size = NameLength; /* Check if the name can fit entirely */ if (SizeLeft < Size) { /* It can't, we'll have to truncate. Tell the caller */ Size = SizeLeft; Status = STATUS_BUFFER_OVERFLOW; } /* Check if the key node name is compressed */ if (Node->Flags & KEY_COMP_NAME) { /* Copy the compressed name */ CmpCopyCompressedName(Info->KeyNodeInformation.Name, SizeLeft, Node->Name, Node->NameLength); } else { /* It isn't, so copy the raw name */ RtlCopyMemory(Info->KeyNodeInformation.Name, Node->Name, Size); } /* 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); } else { /* It doesn't, so set offset to -1, not 0! */ Info->KeyNodeInformation.ClassOffset = 0xFFFFFFFF; } break; /* Full information requsted */ case KeyFullInformation: /* This is the size we need */ Size = FIELD_OFFSET(KEY_FULL_INFORMATION, Class) + Node->ClassLength; /* This is what we can work with */ MinimumSize = FIELD_OFFSET(KEY_FULL_INFORMATION, Class); /* Return it to caller and assume success */ *ResultLength = Size; Status = STATUS_SUCCESS; /* Check if the caller's buffer is to small */ if (Length < MinimumSize) { /* Let them know and fail */ Status = STATUS_BUFFER_TOO_SMALL; break; } /* Now copy all the basic information */ Info->KeyFullInformation.LastWriteTime = Node->LastWriteTime; Info->KeyFullInformation.TitleIndex = 0; Info->KeyFullInformation.ClassLength = Node->ClassLength; Info->KeyFullInformation.SubKeys = Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile]; Info->KeyFullInformation.Values = Node->ValueList.Count; Info->KeyFullInformation.MaxNameLen = Node->MaxNameLen; Info->KeyFullInformation.MaxClassLen = Node->MaxClassLen; Info->KeyFullInformation.MaxValueNameLen = Node->MaxValueNameLen; Info->KeyFullInformation.MaxValueDataLen = Node->MaxValueDataLen; /* 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); } else { /* We don't have a class, so set offset to -1, not 0! */ Info->KeyNodeInformation.ClassOffset = 0xFFFFFFFF; } break; /* Any other class that got sent here is invalid! */ default: /* Set failure code */ Status = STATUS_INVALID_PARAMETER; break; } /* Return status */ return Status; } NTSTATUS NTAPI CmQueryKey(IN PCM_KEY_CONTROL_BLOCK Kcb, IN KEY_INFORMATION_CLASS KeyInformationClass, IN PVOID KeyInformation, IN ULONG Length, IN PULONG ResultLength) { NTSTATUS Status; PHHIVE Hive; PCM_KEY_NODE Parent; /* Acquire hive lock */ CmpLockRegistry(); /* Lock KCB shared */ CmpAcquireKcbLockShared(Kcb); /* Get the hive and parent */ Hive = Kcb->KeyHive; Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell); if (!Parent) { /* Fail */ Status = STATUS_INSUFFICIENT_RESOURCES; goto Quickie; } /* Don't touch deleted keys */ if (Kcb->Delete) { /* Fail */ Status = STATUS_KEY_DELETED; goto Quickie; } /* Check what class we got */ switch (KeyInformationClass) { /* Typical information */ case KeyFullInformation: case KeyBasicInformation: case KeyNodeInformation: /* Call the internal API */ Status = CmpQueryKeyData(Hive, Parent, KeyInformationClass, KeyInformation, Length, ResultLength); 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; /* Illegal classes */ default: /* Print message and fail */ DPRINT1("Unsupported class: %d!\n", KeyInformationClass); Status = STATUS_INVALID_INFO_CLASS; break; } Quickie: /* Release locks */ CmpReleaseKcbLock(Kcb); CmpUnlockRegistry(); return Status; } NTSTATUS NTAPI CmEnumerateKey(IN PCM_KEY_CONTROL_BLOCK Kcb, IN ULONG Index, IN KEY_INFORMATION_CLASS KeyInformationClass, IN PVOID KeyInformation, IN ULONG Length, IN PULONG ResultLength) { NTSTATUS Status; PHHIVE Hive; PCM_KEY_NODE Parent, Child; HCELL_INDEX ChildCell; /* Acquire hive lock */ CmpLockRegistry(); /* Lock the KCB shared */ CmpAcquireKcbLockShared(Kcb); /* Don't touch deleted keys */ if (Kcb->Delete) { /* Undo everything */ CmpReleaseKcbLock(Kcb); CmpUnlockRegistry(); return STATUS_KEY_DELETED; } /* Get the hive and parent */ Hive = Kcb->KeyHive; Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell); if (!Parent) { /* Fail */ Status = STATUS_INSUFFICIENT_RESOURCES; goto Quickie; } /* Get the child cell */ ChildCell = CmpFindSubKeyByNumber(Hive, Parent, Index); /* Release the parent cell */ HvReleaseCell(Hive, Kcb->KeyCell); /* Check if we found the child */ if (ChildCell == HCELL_NIL) { /* We didn't, fail */ Status = STATUS_NO_MORE_ENTRIES; goto Quickie; } /* Now get the actual child node */ Child = (PCM_KEY_NODE)HvGetCell(Hive, ChildCell); if (!Child) { /* Fail */ Status = STATUS_INSUFFICIENT_RESOURCES; goto Quickie; } /* Query the data requested */ Status = CmpQueryKeyData(Hive, Child, KeyInformationClass, KeyInformation, Length, ResultLength); Quickie: /* Release locks */ CmpReleaseKcbLock(Kcb); CmpUnlockRegistry(); return Status; } NTSTATUS NTAPI CmDeleteKey(IN PCM_KEY_BODY KeyBody) { NTSTATUS Status; PHHIVE Hive; PCM_KEY_NODE Node, Parent; HCELL_INDEX Cell, ParentCell; PCM_KEY_CONTROL_BLOCK Kcb; /* Acquire hive lock */ CmpLockRegistry(); /* Get the kcb */ Kcb = KeyBody->KeyControlBlock; /* Don't allow deleting the root */ if (!Kcb->ParentKcb) { /* Fail */ 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; } /* Get the hive and node */ Hive = Kcb->KeyHive; Cell = Kcb->KeyCell; /* Get the key node */ Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell); if (!Node) { /* Fail */ Status = STATUS_INSUFFICIENT_RESOURCES; goto Quickie; } /* Sanity check */ ASSERT(Node->Flags == Kcb->Flags); /* Check if we don't have any children */ if (!(Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile]) && !(Node->Flags & KEY_NO_DELETE)) { /* Get the parent and free the cell */ ParentCell = Node->Parent; Status = CmpFreeKeyByCell(Hive, Cell, TRUE); if (NT_SUCCESS(Status)) { /* Clean up information we have on the subkey */ CmpCleanUpSubKeyInfo(Kcb->ParentKcb); /* Get the parent node */ Parent = (PCM_KEY_NODE)HvGetCell(Hive, ParentCell); if (Parent) { /* Update the maximum name length */ Kcb->ParentKcb->KcbMaxNameLen = Parent->MaxNameLen; /* Make sure we're dirty */ ASSERT(HvIsCellDirty(Hive, ParentCell)); /* Update the write time */ KeQuerySystemTime(&Parent->LastWriteTime); Kcb->ParentKcb->KcbLastWriteTime = Parent->LastWriteTime; /* Release the cell */ HvReleaseCell(Hive, ParentCell); } /* Set the KCB in delete mode and remove it */ Kcb->Delete = TRUE; CmpRemoveKeyControlBlock(Kcb); /* Clear the cell */ Kcb->KeyCell = HCELL_NIL; } } else { /* Fail */ Status = STATUS_CANNOT_DELETE; } Quickie: /* Release the cell */ HvReleaseCell(Hive, Cell); /* Release the KCB locks */ Quickie2: CmpReleaseTwoKcbLockByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey); /* Release hive lock */ CmpUnlockRegistry(); return Status; } NTSTATUS NTAPI CmFlushKey(IN PCM_KEY_CONTROL_BLOCK Kcb, IN BOOLEAN ExclusiveLock) { PCMHIVE CmHive; NTSTATUS Status = STATUS_SUCCESS; PHHIVE Hive; /* 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) { /* Flush all the hives instead */ CmpDoFlushAll(FALSE); } else { /* Flush only this hive */ if (!HvSyncHive(Hive)) { /* Fail */ Status = STATUS_REGISTRY_IO_FAILED; } } /* Return the status */ return Status; } NTSTATUS NTAPI CmLoadKey(IN POBJECT_ATTRIBUTES TargetKey, IN POBJECT_ATTRIBUTES SourceFile, IN ULONG Flags, IN PCM_KEY_BODY KeyBody) { SECURITY_QUALITY_OF_SERVICE ServiceQos; SECURITY_CLIENT_CONTEXT ClientSecurityContext; HANDLE KeyHandle; BOOLEAN Allocate = TRUE; PCMHIVE CmHive; NTSTATUS Status; /* Check if we have a trust key */ if (KeyBody) { /* Fail */ DPRINT1("Trusted classes not yet supported\n"); return STATUS_NOT_IMPLEMENTED; } /* Build a service QoS for a security context */ ServiceQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); ServiceQos.ImpersonationLevel = SecurityImpersonation; ServiceQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; ServiceQos.EffectiveOnly = TRUE; Status = SeCreateClientSecurity(PsGetCurrentThread(), &ServiceQos, FALSE, &ClientSecurityContext); if (!NT_SUCCESS(Status)) { /* Fail */ DPRINT1("Security context failed\n"); return Status; } /* Open the target key */ Status = ZwOpenKey(&KeyHandle, KEY_READ, TargetKey); if (!NT_SUCCESS(Status)) KeyHandle = NULL; /* Open the hive */ Status = CmpCmdHiveOpen(SourceFile, &ClientSecurityContext, &Allocate, &CmHive, 0); /* Get rid of the security context */ SeDeleteClientSecurity(&ClientSecurityContext); /* See if we failed */ if (!NT_SUCCESS(Status)) { /* See if the target already existed */ if (KeyHandle) { /* Lock the registry */ CmpLockRegistryExclusive(); /* FIXME: Check if we are already loaded */ /* Release the registry */ CmpUnlockRegistry(); } /* Close the key handle if we had one */ if (KeyHandle) ZwClose(KeyHandle); DPRINT1("Failed: %lx\n", Status); return Status; } /* Lock the registry shared */ CmpLockRegistry(); /* 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, CmHive, Allocate, TargetKey->SecurityDescriptor); if (NT_SUCCESS(Status)) { /* FIXME: Add to HiveList key */ /* Sync the hive if necessary */ if (Allocate) { /* Sync it */ HvSyncHive(&CmHive->Hive); } /* Release the hive */ CmHive->Hive.HiveFlags &= ~HIVE_IS_UNLOADING; CmHive->CreatorOwner = NULL; } else { /* FIXME: TODO */ } /* Unlock the registry */ CmpUnlockRegistry(); /* Close handle and return */ if (KeyHandle) ZwClose(KeyHandle); return Status; } NTSTATUS NTAPI CmUnloadKey(IN PCM_KEY_CONTROL_BLOCK Kcb, IN ULONG Flags) { UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED; }