diff --git a/reactos/lib/cmlib/cmlib.h b/reactos/lib/cmlib/cmlib.h index fae049507d2..c6823b06a47 100644 --- a/reactos/lib/cmlib/cmlib.h +++ b/reactos/lib/cmlib/cmlib.h @@ -198,6 +198,22 @@ typedef struct _CMHIVE #endif +typedef struct _HV_HIVE_CELL_PAIR +{ + PHHIVE Hive; + HCELL_INDEX Cell; +} HV_HIVE_CELL_PAIR, *PHV_HIVE_CELL_PAIR; + +#define STATIC_CELL_PAIR_COUNT 4 +typedef struct _HV_TRACK_CELL_REF +{ + USHORT Count; + USHORT Max; + PHV_HIVE_CELL_PAIR CellArray; + HV_HIVE_CELL_PAIR StaticArray[STATIC_CELL_PAIR_COUNT]; + USHORT StaticCount; +} HV_TRACK_CELL_REF, *PHV_TRACK_CELL_REF; + extern ULONG CmlibTraceLevel; /* @@ -272,6 +288,12 @@ HvIsCellDirty( IN HCELL_INDEX Cell ); +BOOLEAN +CMAPI +HvHiveWillShrink( + IN PHHIVE RegistryHive +); + BOOLEAN CMAPI HvSyncHive( PHHIVE RegistryHive); @@ -288,6 +310,21 @@ CmCreateRootNode( VOID CMAPI CmPrepareHive( PHHIVE RegistryHive); + + +BOOLEAN +CMAPI +HvTrackCellRef( + PHV_TRACK_CELL_REF CellRef, + PHHIVE Hive, + HCELL_INDEX Cell +); + +VOID +CMAPI +HvReleaseFreeCellRefArray( + PHV_TRACK_CELL_REF CellRef +); /* * Private functions. diff --git a/reactos/lib/cmlib/hivecell.c b/reactos/lib/cmlib/hivecell.c index 994ca98170d..815165182a5 100644 --- a/reactos/lib/cmlib/hivecell.c +++ b/reactos/lib/cmlib/hivecell.c @@ -113,7 +113,7 @@ HvMarkCellDirty( __FUNCTION__, RegistryHive, CellIndex, HoldingLock); if ((CellIndex & HCELL_TYPE_MASK) >> HCELL_TYPE_SHIFT != Stable) - return FALSE; + return TRUE; CellBlock = (CellIndex & HCELL_BLOCK_MASK) >> HCELL_BLOCK_SHIFT; CellLastBlock = ((CellIndex + HV_BLOCK_SIZE - 1) & HCELL_BLOCK_MASK) >> HCELL_BLOCK_SHIFT; @@ -525,3 +525,56 @@ HvFreeCell( if (CellType == Stable) HvMarkCellDirty(RegistryHive, CellIndex, FALSE); } + +BOOLEAN +CMAPI +HvTrackCellRef(PHV_TRACK_CELL_REF CellRef, + PHHIVE Hive, + HCELL_INDEX Cell) +{ + /* Sanity checks */ + ASSERT(CellRef); + ASSERT(Hive ); + ASSERT(Cell != HCELL_NIL); + + /* Less than 4? */ + if (CellRef->StaticCount < STATIC_CELL_PAIR_COUNT) + { + /* Add reference */ + CellRef->StaticArray[CellRef->StaticCount].Hive = Hive; + CellRef->StaticArray[CellRef->StaticCount].Cell = Cell; + CellRef->StaticCount++; + return TRUE; + } + + /* FIXME: TODO */ + DPRINT1("ERROR: Too many references\n"); + while (TRUE); + return FALSE; +} + +VOID +CMAPI +HvReleaseFreeCellRefArray(PHV_TRACK_CELL_REF CellRef) +{ + ULONG i; + ASSERT(CellRef); + + /* Any references? */ + if (CellRef->StaticCount > 0) + { + /* Sanity check */ + ASSERT(CellRef->StaticCount <= STATIC_CELL_PAIR_COUNT); + + /* Loop them */ + for (i = 0; i < CellRef->StaticCount;i++) + { + /* Release them */ + HvReleaseCell(CellRef->StaticArray[i].Hive, + CellRef->StaticArray[i].Cell); + } + + /* Free again */ + CellRef->StaticCount = 0; + } +} \ No newline at end of file diff --git a/reactos/lib/cmlib/hivewrt.c b/reactos/lib/cmlib/hivewrt.c index 5cc20cfe503..7ecadff2062 100644 --- a/reactos/lib/cmlib/hivewrt.c +++ b/reactos/lib/cmlib/hivewrt.c @@ -265,6 +265,14 @@ HvSyncHive( return TRUE; } +BOOLEAN +CMAPI +HvHiveWillShrink(IN PHHIVE RegistryHive) +{ + /* No shrinking yet */ + return FALSE; +} + BOOLEAN CMAPI HvWriteHive( PHHIVE RegistryHive) diff --git a/reactos/ntoskrnl/config/cmapi.c b/reactos/ntoskrnl/config/cmapi.c index 85f2ed9fe14..538c3e5b560 100644 --- a/reactos/ntoskrnl/config/cmapi.c +++ b/reactos/ntoskrnl/config/cmapi.c @@ -14,6 +14,67 @@ /* FUNCTIONS *****************************************************************/ +BOOLEAN +NTAPI +CmpIsHiveAlreadyLoaded(IN HANDLE KeyHandle, + IN POBJECT_ATTRIBUTES SourceFile, + OUT PCMHIVE *CmHive) +{ + NTSTATUS Status; + PCM_KEY_BODY KeyBody; + PCMHIVE Hive; + BOOLEAN Loaded = FALSE; + PAGED_CODE(); + + /* Sanity check */ + CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK(); + + /* Reference the handle */ + Status = ObReferenceObjectByHandle(KeyHandle, + 0, + CmpKeyObjectType, + KernelMode, + (PVOID)&KeyBody, + NULL); + if (!NT_SUCCESS(Status)) return Loaded; + + /* Don't touch deleted KCBs */ + if (KeyBody->KeyControlBlock->Delete) return Loaded; + + Hive = CONTAINING_RECORD(KeyBody->KeyControlBlock->KeyHive, CMHIVE, Hive); + + /* Must be the root key */ + if (!(KeyBody->KeyControlBlock->Flags & KEY_HIVE_ENTRY) || + !(Hive->FileUserName.Buffer)) + { + /* It isn't */ + ObDereferenceObject(KeyBody); + return Loaded; + } + + /* Now compare the name of the file */ + if (!RtlCompareUnicodeString(&Hive->FileUserName, + SourceFile->ObjectName, + TRUE)) + { + /* Same file found */ + Loaded = TRUE; + *CmHive = Hive; + + /* If the hive is frozen, not sure what to do */ + if (Hive->Frozen) + { + /* FIXME: TODO */ + DPRINT1("ERROR: Hive is frozen\n"); + while (TRUE); + } + } + + /* Dereference and return result */ + ObDereferenceObject(KeyBody); + return Loaded; + } + BOOLEAN NTAPI CmpDoFlushAll(IN BOOLEAN ForceFlush) @@ -39,16 +100,35 @@ CmpDoFlushAll(IN BOOLEAN ForceFlush) if (!(Hive->Hive.HiveFlags & HIVE_NOLAZYFLUSH)) { /* Acquire the flusher lock */ - ExAcquirePushLockExclusive((PVOID)&Hive->FlusherLock); + CmpLockHiveFlusherExclusive(Hive); + + /* Check for illegal state */ + if ((ForceFlush) && (Hive->UseCount)) + { + /* Registry needs to be locked down */ + CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK(); + 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))) + { + /* Do the sync */ + Status = HvSyncHive(&Hive->Hive); - /* Do the sync */ - Status = HvSyncHive(&Hive->Hive); - - /* If something failed - set the flag and continue looping*/ - if (!NT_SUCCESS(Status)) Result = FALSE; + /* If something failed - set the flag and continue looping */ + if (!NT_SUCCESS(Status)) Result = FALSE; + } + else + { + /* We won't flush if the hive might shrink */ + Result = FALSE; + CmpForceForceFlush = TRUE; + } /* Release the flusher lock */ - ExReleasePushLock((PVOID)&Hive->FlusherLock); + CmpUnlockHiveFlusher(Hive); } /* Try the next entry */ @@ -81,10 +161,14 @@ CmpSetValueKeyNew(IN PHHIVE Hive, { /* Then make sure it's valid and dirty it */ ASSERT(Parent->ValueList.List != HCELL_NIL); - HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE); + if (!HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE)) + { + /* Fail if we're out of space for log changes */ + return STATUS_NO_LOG_SPACE; + } } - /* Allocate avalue cell */ + /* Allocate a value cell */ ValueCell = HvAllocateCell(Hive, FIELD_OFFSET(CM_KEY_VALUE, Name) + CmpNameSize(Hive, ValueName), @@ -102,16 +186,33 @@ CmpSetValueKeyNew(IN PHHIVE Hive, /* 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); + _SEH2_TRY + { + /* This can crash since the name is coming from user-mode */ + CellData->u.KeyValue.NameLength = CmpCopyName(Hive, + CellData->u.KeyValue.Name, + ValueName); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + /* Fail */ + DPRINT1("Invalid user data!\n"); + HvFreeCell(Hive, ValueCell); + _SEH2_YIELD(return _SEH2_GetExceptionCode()); + } + _SEH2_END; + + /* Check for compressed name */ if (CellData->u.KeyValue.NameLength < ValueName->Length) { /* This is a compressed name */ CellData->u.KeyValue.Flags = VALUE_COMP_NAME; } + else + { + /* No flags to set */ + CellData->u.KeyValue.Flags = 0; + } /* Check if this is a normal key */ if (DataSize > CM_KEY_VALUE_SMALL) @@ -140,6 +241,9 @@ CmpSetValueKeyNew(IN PHHIVE 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; /* Add this value cell to the child list */ Status = CmpAddValueToList(Hive, @@ -149,7 +253,12 @@ CmpSetValueKeyNew(IN PHHIVE Hive, &Parent->ValueList); /* If we failed, free the entire cell, including the data */ - if (!NT_SUCCESS(Status)) CmpFreeValue(Hive, ValueCell); + if (!NT_SUCCESS(Status)) + { + /* Overwrite the status with a known one */ + CmpFreeValue(Hive, ValueCell); + Status = STATUS_INSUFFICIENT_RESOURCES; + } /* Return Status */ return Status; @@ -170,9 +279,12 @@ CmpSetValueKeyExisting(IN PHHIVE Hive, PCELL_DATA CellData; ULONG Length; BOOLEAN WasSmall, IsSmall; + + /* Registry writes must be blocked */ + CMP_ASSERT_FLUSH_LOCK(Hive); /* Mark the old child cell dirty */ - HvMarkCellDirty(Hive, OldChild, FALSE); + if (!HvMarkCellDirty(Hive, OldChild, FALSE)) return STATUS_NO_LOG_SPACE; /* See if this is a small or normal key */ WasSmall = CmpIsKeyValueSmall(&Length, Value->DataLength); @@ -185,7 +297,7 @@ CmpSetValueKeyExisting(IN PHHIVE Hive, ASSERT_VALUE_BIG(Hive, DataSize); /* Mark the old value dirty */ - CmpMarkValueDataDirty(Hive, Value); + if (!CmpMarkValueDataDirty(Hive, Value)) return STATUS_NO_LOG_SPACE; /* Check if we have a small key */ if (IsSmall) @@ -203,662 +315,58 @@ CmpSetValueKeyExisting(IN PHHIVE Hive, 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)) { - /* 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))) { - /* 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; - } + /* Re-use it then */ + NewCell = DataCell; } else { - /* This was a small key, or a key with no data, allocate a cell */ - NewCell = HvAllocateCell(Hive, DataSize, StorageType, HCELL_NIL); + /* Otherwise, re-allocate the current data cell */ + NewCell = HvReallocateCell(Hive, DataCell, DataSize); 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); + /* 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; } - /* 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); - } + /* Now get the actual data for our data cell */ + CellData = HvGetCell(Hive, NewCell); + if (!CellData) ASSERT(FALSE); - /* 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); + /* Release it immediately */ + HvReleaseCell(Hive, NewCell); - /* 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; -} + /* 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; -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; + /* Return success */ + ASSERT(HvIsCellDirty(Hive, NewCell)); + return STATUS_SUCCESS; } NTSTATUS @@ -1076,6 +584,725 @@ CmpQueryKeyData(IN PHHIVE Hive, return Status; } +NTSTATUS +NTAPI +CmSetValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb, + IN PUNICODE_STRING ValueName, + IN ULONG Type, + IN PVOID Data, + IN ULONG DataLength) +{ + PHHIVE Hive = NULL; + 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; + BOOLEAN FirstTry = TRUE, FlusherLocked = FALSE; + HCELL_INDEX ParentCell = HCELL_NIL, ChildCell = HCELL_NIL; + + /* 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) + { + /* 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; + } + + /* Check if this is the first attempt */ + if (FirstTry) + { + /* Search for the value in the cache */ + 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); + } + + /* 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); + FlusherLocked = TRUE; + goto DoAgain; + } + else + { + /* Get pointer to key cell */ + Hive = Kcb->KeyHive; + Cell = Kcb->KeyCell; + + /* Get the parent */ + Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell); + ASSERT(Parent); + ParentCell = Cell; + + /* Prepare to scan the key node */ + 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) + { + /* Release existing child */ + if (ChildCell != HCELL_NIL) + { + HvReleaseCell(Hive, ChildCell); + ChildCell = 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 */ + ChildCell = CurrentChild; + Found = TRUE; + } + } + else + { + /* No child list, we'll need to add it */ + 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)) + { + /* Not enough log space, fail */ + Status = STATUS_NO_LOG_SPACE; + goto Quickie; + } + + /* Get the storage type */ + Storage = HvGetCellType(Cell); + + /* Check if this is small data */ + SmallData = 0; + if ((DataLength <= CM_KEY_VALUE_SMALL) && (DataLength > 0)) + { + /* Need SEH because user data may be invalid */ + _SEH2_TRY + { + /* Copy it */ + RtlCopyMemory(&SmallData, Data, DataLength); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + /* Return failure code */ + Status = _SEH2_GetExceptionCode(); + _SEH2_YIELD(goto Quickie); + } + _SEH2_END; + } + + /* 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); + Kcb->KcbLastWriteTime = Parent->LastWriteTime; + + /* 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; + } + + /* 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); + if ((ChildCell != HCELL_NIL) && (Hive)) HvReleaseCell(Hive, ChildCell); + + /* Release the locks */ + if (FlusherLocked) CmpUnlockHiveFlusher((PCMHIVE)Hive); + 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; + + /* Lock flushes */ + CmpLockHiveFlusherShared((PCMHIVE)Hive); + + /* Get the parent key node */ + Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell); + ASSERT(Parent); + + /* 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 */ + if (!((HvMarkCellDirty(Hive, Cell, FALSE)) && + (HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE)) && + (HvMarkCellDirty(Hive, ChildCell, FALSE)))) + { + /* Not enough log space, fail */ + Status = STATUS_NO_LOG_SPACE; + goto Quickie; + } + + /* Get the key value */ + Value = (PCM_KEY_VALUE)HvGetCell(Hive,ChildCell); + ASSERT(Value); + + /* Mark it and all related data as dirty */ + if (!CmpMarkValueDataDirty(Hive, Value)) + { + /* Not enough log space, fail */ + Status = STATUS_NO_LOG_SPACE; + goto Quickie; + } + + /* 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)) + { + /* Set known error */ + Status = STATUS_INSUFFICIENT_RESOURCES; + 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); + Kcb->KcbLastWriteTime = Parent->LastWriteTime; + + /* 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; + + /* Notify registered callbacks */ + CmpReportNotify(Kcb, Hive, Cell, REG_NOTIFY_CHANGE_LAST_SET); + + /* 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 */ + CmpUnlockHiveFlusher((PCMHIVE)Hive); + 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); + + /* User data, protect against exceptions */ + _SEH2_TRY + { + /* Query the information requested */ + Result = CmpQueryKeyValueData(Kcb, + CachedValue, + ValueData, + ValueCached, + KeyValueInformationClass, + KeyValueInformation, + Length, + ResultLength, + &Status); + if (Result == SearchNeedExclusiveLock) + { + /* Release the value cell */ + if (CellToRelease != HCELL_NIL) + { + HvReleaseCell(Hive, CellToRelease); + CellToRelease = HCELL_NIL; + } + + /* Try with exclusive KCB lock */ + CmpConvertKcbSharedToExclusive(Kcb); + goto DoAgain; + } + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Status = _SEH2_GetExceptionCode(); + } + _SEH2_END; + } + 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); + ASSERT(Parent); + + /* FIXME: Lack of cache? */ + if (Kcb->ValueCache.Count != Parent->ValueList.Count) + { + DPRINT1("HACK: Overriding value cache count\n"); + Kcb->ValueCache.Count = Parent->ValueList.Count; + } + + /* Make sure the index is valid */ + if (Index >= Kcb->ValueCache.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); + HvReleaseCell(Hive, Kcb->KeyCell); + + /* 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) + { + /* Cleanup state */ + ASSERT(CellToRelease2 == HCELL_NIL); + if (CellToRelease) + { + HvReleaseCell(Hive, CellToRelease); + CellToRelease = HCELL_NIL; + } + HvReleaseCell(Hive, Kcb->KeyCell); + + /* 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; + } + + /* User data, need SEH */ + _SEH2_TRY + { + /* Query the information requested */ + Result = CmpQueryKeyValueData(Kcb, + CachedValue, + ValueData, + ValueIsCached, + KeyValueInformationClass, + KeyValueInformation, + Length, + ResultLength, + &Status); + if (Result == SearchNeedExclusiveLock) + { + /* Cleanup state */ + if (CellToRelease2) HvReleaseCell(Hive, CellToRelease2); + HvReleaseCell(Hive, Kcb->KeyCell); + if (CellToRelease) HvReleaseCell(Hive, CellToRelease); + + /* Try with exclusive KCB lock */ + CmpConvertKcbSharedToExclusive(Kcb); + goto DoAgain; + } + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + /* Get exception code */ + Status = _SEH2_GetExceptionCode(); + } + _SEH2_END; + +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 CmQueryKey(IN PCM_KEY_CONTROL_BLOCK Kcb, @@ -1087,6 +1314,7 @@ CmQueryKey(IN PCM_KEY_CONTROL_BLOCK Kcb, NTSTATUS Status; PHHIVE Hive; PCM_KEY_NODE Parent; + HV_TRACK_CELL_REF CellReferences = {0}; /* Acquire hive lock */ CmpLockRegistry(); @@ -1094,16 +1322,6 @@ CmQueryKey(IN PCM_KEY_CONTROL_BLOCK Kcb, /* 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) { @@ -1120,13 +1338,27 @@ CmQueryKey(IN PCM_KEY_CONTROL_BLOCK Kcb, case KeyBasicInformation: case KeyNodeInformation: - /* Call the internal API */ - Status = CmpQueryKeyData(Hive, - Parent, - KeyInformationClass, - KeyInformation, - Length, - ResultLength); + /* 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; /* Unsupported classes for now */ @@ -1149,6 +1381,9 @@ CmQueryKey(IN PCM_KEY_CONTROL_BLOCK Kcb, } Quickie: + /* Release references */ + HvReleaseFreeCellRefArray(&CellReferences); + /* Release locks */ CmpReleaseKcbLock(Kcb); CmpUnlockRegistry(); @@ -1168,6 +1403,7 @@ CmEnumerateKey(IN PCM_KEY_CONTROL_BLOCK Kcb, PHHIVE Hive; PCM_KEY_NODE Parent, Child; HCELL_INDEX ChildCell; + HV_TRACK_CELL_REF CellReferences = {0}; /* Acquire hive lock */ CmpLockRegistry(); @@ -1179,20 +1415,14 @@ CmEnumerateKey(IN PCM_KEY_CONTROL_BLOCK Kcb, if (Kcb->Delete) { /* Undo everything */ - CmpReleaseKcbLock(Kcb); - CmpUnlockRegistry(); - return STATUS_KEY_DELETED; + Status = STATUS_KEY_DELETED; + goto Quickie; } /* 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; - } + ASSERT(Parent); /* Get the child cell */ ChildCell = CmpFindSubKeyByNumber(Hive, Parent, Index); @@ -1210,22 +1440,39 @@ CmEnumerateKey(IN PCM_KEY_CONTROL_BLOCK Kcb, /* Now get the actual child node */ Child = (PCM_KEY_NODE)HvGetCell(Hive, ChildCell); - if (!Child) + ASSERT(Child); + + /* Track references */ + if (!HvTrackCellRef(&CellReferences, Hive, ChildCell)) { - /* Fail */ + /* Can't allocate memory for tracking */ Status = STATUS_INSUFFICIENT_RESOURCES; goto Quickie; } - /* Query the data requested */ - Status = CmpQueryKeyData(Hive, - Child, - KeyInformationClass, - KeyInformation, - Length, - ResultLength); + /* Data can be user-mode, use SEH */ + _SEH2_TRY + { + /* Query the data requested */ + Status = CmpQueryKeyData(Hive, + Child, + KeyInformationClass, + KeyInformation, + Length, + ResultLength); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + /* Fail with exception code */ + Status = _SEH2_GetExceptionCode(); + _SEH2_YIELD(goto Quickie); + } + _SEH2_END; Quickie: + /* Release references */ + HvReleaseFreeCellRefArray(&CellReferences); + /* Release locks */ CmpReleaseKcbLock(Kcb); CmpUnlockRegistry(); @@ -1271,14 +1518,12 @@ CmDeleteKey(IN PCM_KEY_BODY KeyBody) Hive = Kcb->KeyHive; Cell = Kcb->KeyCell; + /* Lock flushes */ + CmpLockHiveFlusherShared((PCMHIVE)Hive); + /* Get the key node */ Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell); - if (!Node) - { - /* Fail */ - Status = STATUS_INSUFFICIENT_RESOURCES; - goto Quickie; - } + ASSERT(Node); /* Sanity check */ ASSERT(Node->Flags == Kcb->Flags); @@ -1287,11 +1532,17 @@ CmDeleteKey(IN PCM_KEY_BODY KeyBody) if (!(Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile]) && !(Node->Flags & KEY_NO_DELETE)) { + /* 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); if (NT_SUCCESS(Status)) { + /* Flush any notifications */ + CmpFlushNotifiesOnKeyBodyList(Kcb, FALSE); + /* Clean up information we have on the subkey */ CmpCleanUpSubKeyInfo(Kcb->ParentKcb); @@ -1327,10 +1578,12 @@ CmDeleteKey(IN PCM_KEY_BODY KeyBody) Status = STATUS_CANNOT_DELETE; } -Quickie: /* Release the cell */ HvReleaseCell(Hive, Cell); - + + /* Release flush lock */ + CmpUnlockHiveFlusher((PCMHIVE)Hive); + /* Release the KCB locks */ Quickie2: CmpReleaseTwoKcbLockByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey); @@ -1364,12 +1617,36 @@ CmFlushKey(IN PCM_KEY_CONTROL_BLOCK Kcb, } else { + /* Don't touch the hive */ + CmpLockHiveFlusherExclusive(CmHive); + ASSERT(CmHive->ViewLock); + KeAcquireGuardedMutex(CmHive->ViewLock); + CmHive->ViewLockOwner = KeGetCurrentThread(); + + /* Will the hive shrink? */ + if (HvHiveWillShrink(Hive)) + { + /* I don't believe the current Hv does shrinking */ + ASSERT(FALSE); + } + else + { + /* Now we can release views */ + ASSERT(CmHive->ViewLock); + CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK_OR_LOADING(CmHive); + 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); } /* Return the status */ @@ -1387,8 +1664,9 @@ CmLoadKey(IN POBJECT_ATTRIBUTES TargetKey, SECURITY_CLIENT_CONTEXT ClientSecurityContext; HANDLE KeyHandle; BOOLEAN Allocate = TRUE; - PCMHIVE CmHive; + PCMHIVE CmHive, LoadedHive; NTSTATUS Status; + CM_PARSE_CONTEXT ParseContext; /* Check if we have a trust key */ if (KeyBody) @@ -1415,9 +1693,21 @@ CmLoadKey(IN POBJECT_ATTRIBUTES TargetKey, } /* Open the target key */ +#if 0 Status = ZwOpenKey(&KeyHandle, KEY_READ, TargetKey); +#else + RtlZeroMemory(&ParseContext, sizeof(ParseContext)); + ParseContext.CreateOperation = FALSE; + Status = ObOpenObjectByName(TargetKey, + CmpKeyObjectType, + KernelMode, + NULL, + KEY_READ, + &ParseContext, + &KeyHandle); +#endif if (!NT_SUCCESS(Status)) KeyHandle = NULL; - + /* Open the hive */ Status = CmpCmdHiveOpen(SourceFile, &ClientSecurityContext, @@ -1437,21 +1727,29 @@ CmLoadKey(IN POBJECT_ATTRIBUTES TargetKey, /* Lock the registry */ CmpLockRegistryExclusive(); - /* FIXME: Check if we are already loaded */ - + /* Check if we are already loaded */ + if (CmpIsHiveAlreadyLoaded(KeyHandle, SourceFile, &LoadedHive)) + { + /* That's okay then */ + ASSERT(LoadedHive); + Status = STATUS_SUCCESS; + } + /* 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 loading */ + ExAcquirePushLockExclusive(&CmpLoadHiveLock); + /* Lock the hive to this thread */ CmHive->Hive.HiveFlags |= HIVE_IS_UNLOADING; CmHive->CreatorOwner = KeGetCurrentThread(); @@ -1467,23 +1765,37 @@ CmLoadKey(IN POBJECT_ATTRIBUTES TargetKey, TargetKey->SecurityDescriptor); if (NT_SUCCESS(Status)) { - /* FIXME: Add to HiveList key */ + /* Add to HiveList key */ + CmpAddToHiveFileList(CmHive); /* Sync the hive if necessary */ if (Allocate) { - /* Sync it */ + /* Sync it under the flusher lock */ + CmpLockHiveFlusherExclusive(CmHive); HvSyncHive(&CmHive->Hive); + CmpUnlockHiveFlusher(CmHive); } /* Release the hive */ CmHive->Hive.HiveFlags &= ~HIVE_IS_UNLOADING; CmHive->CreatorOwner = NULL; + + /* Allow loads */ + ExReleasePushLock(&CmpLoadHiveLock); } else { /* FIXME: TODO */ - + ASSERT(FALSE); + } + + /* Is this first profile load? */ + if (!(CmpProfileLoaded) && !(CmpWasSetupBoot)) + { + /* User is now logged on, set quotas */ + CmpProfileLoaded = TRUE; + CmpSetGlobalQuotaAllowed(); } /* Unlock the registry */ diff --git a/reactos/ntoskrnl/config/cmhvlist.c b/reactos/ntoskrnl/config/cmhvlist.c index f4e97814bbd..8fb2636f3dc 100644 --- a/reactos/ntoskrnl/config/cmhvlist.c +++ b/reactos/ntoskrnl/config/cmhvlist.c @@ -14,4 +14,11 @@ /* FUNCTIONS *****************************************************************/ +NTSTATUS +NTAPI +CmpAddToHiveFileList(IN PCMHIVE Hive) +{ + return STATUS_SUCCESS; +} + /* EOF */ \ No newline at end of file diff --git a/reactos/ntoskrnl/config/cminit.c b/reactos/ntoskrnl/config/cminit.c index 154ba1530b6..be7efc1660f 100644 --- a/reactos/ntoskrnl/config/cminit.c +++ b/reactos/ntoskrnl/config/cminit.c @@ -119,12 +119,10 @@ CmpInitializeHive(OUT PCMHIVE *RegistryHive, if (!Hive->ViewLock) return STATUS_INSUFFICIENT_RESOURCES; /* Allocate the flush lock */ -#if 0 Hive->FlusherLock = ExAllocatePoolWithTag(NonPagedPool, sizeof(ERESOURCE), TAG_CM); if (!Hive->FlusherLock) return STATUS_INSUFFICIENT_RESOURCES; -#endif /* Setup the handles */ Hive->FileHandles[HFILE_TYPE_PRIMARY] = Primary; @@ -136,7 +134,7 @@ CmpInitializeHive(OUT PCMHIVE *RegistryHive, Hive->ViewLockOwner = NULL; /* Initialize the flush lock */ - ExInitializePushLock((PULONG_PTR)&Hive->FlusherLock); + ExInitializeResourceLite(Hive->FlusherLock); /* Setup hive locks */ ExInitializePushLock((PULONG_PTR)&Hive->HiveLock); @@ -193,9 +191,7 @@ CmpInitializeHive(OUT PCMHIVE *RegistryHive, { /* Clear allocations and fail */ ExFreePool(Hive->ViewLock); -#if 0 ExFreePool(Hive->FlusherLock); -#endif ExFreePool(Hive); return Status; } @@ -211,9 +207,7 @@ CmpInitializeHive(OUT PCMHIVE *RegistryHive, { /* Free all alocations */ ExFreePool(Hive->ViewLock); -#if 0 ExFreePool(Hive->FlusherLock); -#endif ExFreePool(Hive); return STATUS_REGISTRY_CORRUPT; } diff --git a/reactos/ntoskrnl/config/cmkcbncb.c b/reactos/ntoskrnl/config/cmkcbncb.c index 5e7060d61c4..a26de07cf1b 100644 --- a/reactos/ntoskrnl/config/cmkcbncb.c +++ b/reactos/ntoskrnl/config/cmkcbncb.c @@ -1135,3 +1135,62 @@ DelistKeyBodyFromKCB(IN PCM_KEY_BODY KeyBody, /* Unlock it it if we did a manual lock */ if (!LockHeld) CmpReleaseKcbLock(KeyBody->KeyControlBlock); } + +VOID +NTAPI +CmpFlushNotifiesOnKeyBodyList(IN PCM_KEY_CONTROL_BLOCK Kcb, + IN BOOLEAN LockHeld) +{ + PLIST_ENTRY NextEntry, ListHead; + PCM_KEY_BODY KeyBody; + + /* Sanity check */ + LockHeld ? CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK() : CmpIsKcbLockedExclusive(Kcb); + while (TRUE) + { + /* Is the list empty? */ + ListHead = &Kcb->KeyBodyListHead; + if (!IsListEmpty(ListHead)) + { + /* Loop the list */ + NextEntry = ListHead->Flink; + while (NextEntry != ListHead) + { + /* Get the key body */ + KeyBody = CONTAINING_RECORD(NextEntry, CM_KEY_BODY, KeyBodyList); + ASSERT(KeyBody->Type == '20yk'); + + /* Check for notifications */ + if (KeyBody->NotifyBlock) + { + /* Is the lock held? */ + if (LockHeld) + { + /* Flush it */ + CmpFlushNotify(KeyBody, LockHeld); + ASSERT(KeyBody->NotifyBlock == NULL); + continue; + } + + /* Lock isn't held, so we need to take a reference */ + if (ObReferenceObjectSafe(KeyBody)) + { + /* Now we can flush */ + CmpFlushNotify(KeyBody, LockHeld); + ASSERT(KeyBody->NotifyBlock == NULL); + + /* Release the reference we took */ + ObDereferenceObjectDeferDelete(KeyBody); + continue; + } + } + + /* Try the next entry */ + NextEntry = NextEntry->Flink; + } + } + + /* List has been parsed, exit */ + break; + } +} diff --git a/reactos/ntoskrnl/config/cmparse.c b/reactos/ntoskrnl/config/cmparse.c index 42d737908a0..82635445dda 100644 --- a/reactos/ntoskrnl/config/cmparse.c +++ b/reactos/ntoskrnl/config/cmparse.c @@ -414,15 +414,6 @@ CmpDoCreate(IN PHHIVE Hive, LARGE_INTEGER TimeStamp; PCM_KEY_NODE KeyNode; - /* Sanity check */ -#if 0 - ASSERT((CmpIsKcbLockedExclusive(ParentKcb) == TRUE) || - (CmpTestRegistryLockExclusive() == TRUE)); -#endif - - /* Acquire the flusher lock */ - ExAcquirePushLockShared((PVOID)&((PCMHIVE)Hive)->FlusherLock); - /* Check if the parent is being deleted */ if (ParentKcb->Delete) { @@ -555,7 +546,6 @@ CmpDoCreate(IN PHHIVE Hive, Exit: /* Release the flusher lock and return status */ - ExReleasePushLock((PVOID)&((PCMHIVE)Hive)->FlusherLock); return Status; } @@ -747,9 +737,6 @@ CmpCreateLinkNode(IN PHHIVE Hive, LARGE_INTEGER TimeStamp; PCM_KEY_NODE KeyNode; PCM_KEY_CONTROL_BLOCK Kcb = ParentKcb; -#if 0 - CMP_ASSERT_REGISTRY_LOCK(); -#endif /* Link nodes only allowed on the master */ if (Hive != &CmiVolatileHive->Hive) @@ -759,10 +746,6 @@ CmpCreateLinkNode(IN PHHIVE Hive, return STATUS_ACCESS_DENIED; } - /* Acquire the flusher locks */ - ExAcquirePushLockShared((PVOID)&((PCMHIVE)Hive)->FlusherLock); - ExAcquirePushLockShared((PVOID)&((PCMHIVE)Context->ChildHive.KeyHive)->FlusherLock); - /* Check if the parent is being deleted */ if (ParentKcb->Delete) { @@ -964,8 +947,6 @@ CmpCreateLinkNode(IN PHHIVE Hive, Exit: /* Release the flusher locks and return status */ - ExReleasePushLock((PVOID)&((PCMHIVE)Context->ChildHive.KeyHive)->FlusherLock); - ExReleasePushLock((PVOID)&((PCMHIVE)Hive)->FlusherLock); return Status; }