Author: fireball Date: Sat Apr 3 22:22:32 2010 New Revision: 46702
URL: http://svn.reactos.org/svn/reactos?rev=46702&view=rev Log: [NTOSKRNL/CONFIG] - Flusher lock fixes: wrong kind of lock,total mess (and the wrong kind of lock). Properly fixed throughout cmapi.c, but still missing in many other places. - Add support for detecting loading of an already loaded hive. - Start adding calls to CmpReportNotify to support registry callbacks. - Do work needed to flush notifications for a deleted node (but CmpFlushNotify not yet implemented). - Add support for adding each newly loaded hive to the HiveList key in the registry (but CmpAddHiveToFileList not yet implemented). - Add some ViewLock acquire/releases where needed. - Load the key in a faster way (Ob vs Zw) - Add checks everywhere for HvMarkCellDirty success. In future (when log/backup file is enabled), it can return FALSE (e.g. when we are out of space). - Change logic in CmpDoFlushAll to only flush a hive if it won't shrink (in the future, flushing may lead to hive shrinkage for efficiency). - Add SEH2 protection to all CmApis that may deal with user-mode data. - Add HvTrackCellRef/HvReleaseCellRef for tracking cell references in scenarios where we might need many GetCell/ReleaseCell calls. For now stubbed to only work with up to 4 static references. - Properly unlock/free in some failure paths in some of the CM APIs. - Add some missing HvReleaseCell in paths where it was missing. - Try to fix hack in enumerate key. - Fix wrong usage of KeQuerySystemTime. It was called twice to save it in 2 different places. Instead, there should be only one call, and then duplicate the value across. - Fix logic in CmpSetValueExistingData/Key.
Tested with winetests and .NET framework 1.1 installation which fully completes.
Modified: trunk/reactos/lib/cmlib/cmlib.h trunk/reactos/lib/cmlib/hivecell.c trunk/reactos/lib/cmlib/hivewrt.c trunk/reactos/ntoskrnl/config/cmapi.c trunk/reactos/ntoskrnl/config/cmhvlist.c trunk/reactos/ntoskrnl/config/cminit.c trunk/reactos/ntoskrnl/config/cmkcbncb.c trunk/reactos/ntoskrnl/config/cmparse.c
Modified: trunk/reactos/lib/cmlib/cmlib.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/lib/cmlib/cmlib.h?rev=46702... ============================================================================== --- trunk/reactos/lib/cmlib/cmlib.h [iso-8859-1] (original) +++ trunk/reactos/lib/cmlib/cmlib.h [iso-8859-1] Sat Apr 3 22:22:32 2010 @@ -198,6 +198,22 @@
#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 @@ IN HCELL_INDEX Cell );
+BOOLEAN +CMAPI +HvHiveWillShrink( + IN PHHIVE RegistryHive +); + BOOLEAN CMAPI HvSyncHive( PHHIVE RegistryHive); @@ -288,6 +310,21 @@ 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.
Modified: trunk/reactos/lib/cmlib/hivecell.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/lib/cmlib/hivecell.c?rev=46... ============================================================================== --- trunk/reactos/lib/cmlib/hivecell.c [iso-8859-1] (original) +++ trunk/reactos/lib/cmlib/hivecell.c [iso-8859-1] Sat Apr 3 22:22:32 2010 @@ -113,7 +113,7 @@ __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 @@ 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; + } +}
Modified: trunk/reactos/lib/cmlib/hivewrt.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/lib/cmlib/hivewrt.c?rev=467... ============================================================================== --- trunk/reactos/lib/cmlib/hivewrt.c [iso-8859-1] (original) +++ trunk/reactos/lib/cmlib/hivewrt.c [iso-8859-1] Sat Apr 3 22:22:32 2010 @@ -265,6 +265,14 @@ return TRUE; }
+BOOLEAN +CMAPI +HvHiveWillShrink(IN PHHIVE RegistryHive) +{ + /* No shrinking yet */ + return FALSE; +} + BOOLEAN CMAPI HvWriteHive( PHHIVE RegistryHive)
Modified: trunk/reactos/ntoskrnl/config/cmapi.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/config/cmapi.c?rev... ============================================================================== --- trunk/reactos/ntoskrnl/config/cmapi.c [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/config/cmapi.c [iso-8859-1] Sat Apr 3 22:22:32 2010 @@ -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 @@ 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; + 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); + + /* 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 @@ { /* Then make sure it's valid and dirty it */ ASSERT(Parent->ValueList.List != HCELL_NIL); - HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE); - } - - /* Allocate avalue cell */ + if (!HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE)) + { + /* Fail if we're out of space for log changes */ + return STATUS_NO_LOG_SPACE; + } + } + + /* Allocate a value cell */ ValueCell = HvAllocateCell(Hive, FIELD_OFFSET(CM_KEY_VALUE, Name) + CmpNameSize(Hive, ValueName), @@ -102,15 +186,32 @@
/* 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 */ @@ -140,6 +241,9 @@ 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 @@ &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 @@ 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 @@ 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,60 +315,273 @@ Value->Type = Type; return STATUS_SUCCESS; } + + /* 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 { - /* 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; + /* 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 +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, 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; - } + /* 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 @@ -267,7 +592,7 @@ IN PVOID Data, IN ULONG DataLength) { - PHHIVE Hive; + PHHIVE Hive = NULL; PCM_KEY_NODE Parent; PCM_KEY_VALUE Value = NULL; HCELL_INDEX CurrentChild, Cell; @@ -275,8 +600,10 @@ BOOLEAN Found, Result; ULONG Count, ChildIndex, SmallData, Storage; VALUE_SEARCH_RETURN_TYPE SearchResult; - - /* Acquire hive lock */ + BOOLEAN FirstTry = TRUE, FlusherLocked = FALSE; + HCELL_INDEX ParentCell = HCELL_NIL, ChildCell = HCELL_NIL; + + /* Acquire hive and KCB lock */ CmpLockRegistry(); CmpAcquireKcbLockShared(Kcb);
@@ -302,84 +629,119 @@ 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); + + /* 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 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) + 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; }
- /* Remember that we found it */ - Found = TRUE; - } - } - else - { - /* No child list, we'll need to add it */ - ChildIndex = 0; - } + /* 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 */ - ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) || - (CmpTestRegistryLockExclusive() == TRUE)); + CMP_ASSERT_KCB_LOCK(Kcb);
/* Mark the cell dirty */ - HvMarkCellDirty(Hive, Cell, FALSE); + if (!HvMarkCellDirty(Hive, Cell, FALSE)) + { + /* Not enough log space, fail */ + Status = STATUS_NO_LOG_SPACE; + goto Quickie; + }
/* Get the storage type */ Storage = HvGetCellType(Cell); @@ -388,8 +750,19 @@ SmallData = 0; if ((DataLength <= CM_KEY_VALUE_SMALL) && (DataLength > 0)) { - /* Copy it */ - RtlCopyMemory(&SmallData, Data, DataLength); + /* 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 */ @@ -442,7 +815,7 @@
/* Save the write time */ KeQuerySystemTime(&Parent->LastWriteTime); - KeQuerySystemTime(&Kcb->KcbLastWriteTime); + Kcb->KcbLastWriteTime = Parent->LastWriteTime;
/* Check if the cell is cached */ if ((Found) && (CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList))) @@ -463,10 +836,21 @@ 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; @@ -504,15 +888,13 @@ /* 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); - if (!Parent) - { - /* Fail */ - Status = STATUS_INSUFFICIENT_RESOURCES; - goto Quickie; - } + ASSERT(Parent);
/* Get the value list and check if it has any entries */ ChildList = &Parent->ValueList; @@ -535,16 +917,26 @@ 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); + 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); - if (!Value) ASSERT(FALSE); + ASSERT(Value);
/* Mark it and all related data as dirty */ - CmpMarkValueDataDirty(Hive, Value); + if (!CmpMarkValueDataDirty(Hive, Value)) + { + /* Not enough log space, fail */ + Status = STATUS_NO_LOG_SPACE; + goto Quickie; + }
/* Ssanity checks */ ASSERT(HvIsCellDirty(Hive, Parent->ValueList.List)); @@ -552,7 +944,12 @@
/* Remove the value from the child list */ Status = CmpRemoveValueFromList(Hive, ChildIndex, ChildList); - if(!NT_SUCCESS(Status)) goto Quickie; + if (!NT_SUCCESS(Status)) + { + /* Set known error */ + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Quickie; + }
/* Remove the value and its data itself */ if (!CmpFreeValue(Hive, ChildCell)) @@ -564,7 +961,7 @@
/* Set the last write time */ KeQuerySystemTime(&Parent->LastWriteTime); - KeQuerySystemTime(&Kcb->KcbLastWriteTime); + Kcb->KcbLastWriteTime = Parent->LastWriteTime;
/* Sanity check */ ASSERT(Parent->MaxValueNameLen == Kcb->KcbMaxValueNameLen); @@ -591,6 +988,9 @@ /* 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; @@ -609,6 +1009,7 @@ }
/* Release locks */ + CmpUnlockHiveFlusher((PCMHIVE)Hive); CmpReleaseKcbLock(Kcb); CmpUnlockRegistry(); return Status; @@ -683,22 +1084,38 @@ /* 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; - } + /* 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 { @@ -754,16 +1171,17 @@ /* 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) + 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); @@ -787,7 +1205,7 @@ { /* Check if we need an exclusive lock */ ASSERT(CellToRelease == HCELL_NIL); - ASSERT(ValueData == NULL); + HvReleaseCell(Hive, Kcb->KeyCell);
/* Try with exclusive KCB lock */ CmpConvertKcbSharedToExclusive(Kcb); @@ -814,6 +1232,15 @@ &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; @@ -827,23 +1254,38 @@ 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; - } + + /* 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 */ @@ -858,221 +1300,6 @@ /* 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; }
@@ -1087,6 +1314,7 @@ NTSTATUS Status; PHHIVE Hive; PCM_KEY_NODE Parent; + HV_TRACK_CELL_REF CellReferences = {0};
/* Acquire hive lock */ CmpLockRegistry(); @@ -1094,16 +1322,6 @@ /* 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 @@ 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 @@ }
Quickie: + /* Release references */ + HvReleaseFreeCellRefArray(&CellReferences); + /* Release locks */ CmpReleaseKcbLock(Kcb); CmpUnlockRegistry(); @@ -1168,6 +1403,7 @@ PHHIVE Hive; PCM_KEY_NODE Parent, Child; HCELL_INDEX ChildCell; + HV_TRACK_CELL_REF CellReferences = {0};
/* Acquire hive lock */ CmpLockRegistry(); @@ -1179,20 +1415,14 @@ 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 @@
/* Now get the actual child node */ Child = (PCM_KEY_NODE)HvGetCell(Hive, ChildCell); - if (!Child) - { - /* Fail */ + ASSERT(Child); + + /* Track references */ + if (!HvTrackCellRef(&CellReferences, Hive, ChildCell)) + { + /* 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 @@ 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 @@ 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 @@ 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 @@ } 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 @@ 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 @@ }
/* 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,20 +1727,28 @@ /* 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; @@ -1467,23 +1765,37 @@ 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 */
Modified: trunk/reactos/ntoskrnl/config/cmhvlist.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/config/cmhvlist.c?... ============================================================================== --- trunk/reactos/ntoskrnl/config/cmhvlist.c [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/config/cmhvlist.c [iso-8859-1] Sat Apr 3 22:22:32 2010 @@ -14,4 +14,11 @@
/* FUNCTIONS *****************************************************************/
+NTSTATUS +NTAPI +CmpAddToHiveFileList(IN PCMHIVE Hive) +{ + return STATUS_SUCCESS; +} + /* EOF */
Modified: trunk/reactos/ntoskrnl/config/cminit.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/config/cminit.c?re... ============================================================================== --- trunk/reactos/ntoskrnl/config/cminit.c [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/config/cminit.c [iso-8859-1] Sat Apr 3 22:22:32 2010 @@ -119,12 +119,10 @@ 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 @@ 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 @@ { /* Clear allocations and fail */ ExFreePool(Hive->ViewLock); -#if 0 ExFreePool(Hive->FlusherLock); -#endif ExFreePool(Hive); return Status; } @@ -211,9 +207,7 @@ { /* Free all alocations */ ExFreePool(Hive->ViewLock); -#if 0 ExFreePool(Hive->FlusherLock); -#endif ExFreePool(Hive); return STATUS_REGISTRY_CORRUPT; }
Modified: trunk/reactos/ntoskrnl/config/cmkcbncb.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/config/cmkcbncb.c?... ============================================================================== --- trunk/reactos/ntoskrnl/config/cmkcbncb.c [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/config/cmkcbncb.c [iso-8859-1] Sat Apr 3 22:22:32 2010 @@ -1135,3 +1135,62 @@ /* 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; + } +}
Modified: trunk/reactos/ntoskrnl/config/cmparse.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/config/cmparse.c?r... ============================================================================== --- trunk/reactos/ntoskrnl/config/cmparse.c [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/config/cmparse.c [iso-8859-1] Sat Apr 3 22:22:32 2010 @@ -414,15 +414,6 @@ 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 @@
Exit: /* Release the flusher lock and return status */ - ExReleasePushLock((PVOID)&((PCMHIVE)Hive)->FlusherLock); return Status; }
@@ -747,9 +737,6 @@ 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 @@ 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 @@
Exit: /* Release the flusher locks and return status */ - ExReleasePushLock((PVOID)&((PCMHIVE)Context->ChildHive.KeyHive)->FlusherLock); - ExReleasePushLock((PVOID)&((PCMHIVE)Hive)->FlusherLock); return Status; }