VOID NTAPI CmpDereferenceKeyControlBlockWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb, IN BOOLEAN LockHeldExclusively) { CMTRACE(CM_REFERENCE_DEBUG, "%s - Dereferencing KCB: %p\n", __FUNCTION__, Kcb); /* Sanity check */ ASSERT_KCB_VALID(Kcb); /* Check if this is the last reference */ if ((InterlockedDecrement((PLONG)&Kcb->RefCount) & 0xFFFF) == 0) { /* Sanity check */ ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) || (CmpTestRegistryLockExclusive() == TRUE)); /* Check if we should do a direct delete */ if (((CmpHoldLazyFlush) && !(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND) && !(Kcb->Flags & KEY_SYM_LINK)) || (Kcb->ExtFlags & CM_KCB_NO_DELAY_CLOSE) || (Kcb->Delete)) { /* Clean up the KCB*/ CmpCleanUpKcbCacheWithLock(Kcb, LockHeldExclusively); } else { /* Otherwise, use delayed close */ CmpAddToDelayedClose(Kcb, LockHeldExclusively); } } }
VOID NTAPI EnlistKeyBodyWithKCB(IN PCM_KEY_BODY KeyBody, IN ULONG Flags) { ULONG i; /* Sanity check */ ASSERT(KeyBody->KeyControlBlock != NULL); /* Initialize the list entry */ InitializeListHead(&KeyBody->KeyBodyList); /* Check if we can use the parent KCB array */ for (i = 0; i < 4; i++) { /* Add it into the list */ if (!InterlockedCompareExchangePointer((PVOID*)&KeyBody->KeyControlBlock-> KeyBodyArray[i], KeyBody, NULL)) { /* Added */ return; } } /* Array full, check if we need to unlock the KCB */ if (Flags & CMP_ENLIST_KCB_LOCKED_SHARED) { /* It's shared, so release the KCB shared lock */ CmpReleaseKcbLock(KeyBody->KeyControlBlock); ASSERT(!(Flags & CMP_ENLIST_KCB_LOCKED_EXCLUSIVE)); } /* Check if we need to lock the KCB */ if (!(Flags & CMP_ENLIST_KCB_LOCKED_EXCLUSIVE)) { /* Acquire the lock */ CmpAcquireKcbLockExclusive(KeyBody->KeyControlBlock); } /* Make sure we have the exclusive lock */ ASSERT((CmpIsKcbLockedExclusive(KeyBody->KeyControlBlock) == TRUE) || (CmpTestRegistryLockExclusive() == TRUE)); /* do the insert */ InsertTailList(&KeyBody->KeyControlBlock->KeyBodyListHead, &KeyBody->KeyBodyList); /* Check if we did a manual lock */ if (!(Flags & (CMP_ENLIST_KCB_LOCKED_SHARED | CMP_ENLIST_KCB_LOCKED_EXCLUSIVE))) { /* Release the lock */ CmpReleaseKcbLock(KeyBody->KeyControlBlock); } }
VOID NTAPI CmpCleanUpSubKeyInfo(IN PCM_KEY_CONTROL_BLOCK Kcb) { PCM_KEY_NODE KeyNode; /* Sanity check */ ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) || (CmpTestRegistryLockExclusive() == TRUE)); /* Check if there's any cached subkey */ if (Kcb->ExtFlags & (CM_KCB_NO_SUBKEY | CM_KCB_SUBKEY_ONE | CM_KCB_SUBKEY_HINT)) { /* Check if there's a hint */ if (Kcb->ExtFlags & (CM_KCB_SUBKEY_HINT)) { /* Kill it */ CmpFree(Kcb->IndexHint, 0); } /* Remove subkey flags */ Kcb->ExtFlags &= ~(CM_KCB_NO_SUBKEY | CM_KCB_SUBKEY_ONE | CM_KCB_SUBKEY_HINT); } /* Check if there's no linked cell */ if (Kcb->KeyCell == HCELL_NIL) { /* Make sure it's a delete */ ASSERT(Kcb->Delete); KeyNode = NULL; } else { /* Get the key node */ KeyNode = (PCM_KEY_NODE)HvGetCell(Kcb->KeyHive, Kcb->KeyCell); } /* Check if we got the node */ if (!KeyNode) { /* We didn't, mark the cached data invalid */ Kcb->ExtFlags |= CM_KCB_INVALID_CACHED_INFO; } else { /* We have a keynode, update subkey counts */ Kcb->ExtFlags &= ~CM_KCB_INVALID_CACHED_INFO; Kcb->SubKeyCount = KeyNode->SubKeyCounts[Stable] + KeyNode->SubKeyCounts[Volatile]; /* Release the cell */ HvReleaseCell(Kcb->KeyHive, Kcb->KeyCell); } }
VOID NTAPI CmpRemoveKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb) { /* Make sure that the registry and KCB are utterly locked */ ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) || (CmpTestRegistryLockExclusive() == TRUE)); /* Remove the key hash */ CmpRemoveKeyHash(&Kcb->KeyHash); }
VOID NTAPI CmpCleanUpKcbValueCache(IN PCM_KEY_CONTROL_BLOCK Kcb) { PULONG_PTR CachedList; ULONG i; /* Sanity check */ ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) || (CmpTestRegistryLockExclusive() == TRUE)); /* Check if the value list is cached */ if (CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)) { /* Get the cache list */ CachedList = (PULONG_PTR)CMP_GET_CACHED_DATA(Kcb->ValueCache.ValueList); for (i = 0; i < Kcb->ValueCache.Count; i++) { /* Check if this cell is cached */ if (CMP_IS_CELL_CACHED(CachedList[i])) { /* Free it */ CmpFree((PVOID)CMP_GET_CACHED_CELL(CachedList[i]), 0); } } /* Now free the list */ CmpFree((PVOID)CMP_GET_CACHED_CELL(Kcb->ValueCache.ValueList), 0); Kcb->ValueCache.ValueList = HCELL_NIL; } else if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND) { /* This is a sym link, check if there's only one reference left */ if ((Kcb->ValueCache.RealKcb->RefCount == 1) && !(Kcb->ValueCache.RealKcb->Delete)) { /* Disable delay close for the KCB */ Kcb->ValueCache.RealKcb->ExtFlags |= CM_KCB_NO_DELAY_CLOSE; } /* Dereference the KCB */ CmpDelayDerefKeyControlBlock(Kcb->ValueCache.RealKcb); Kcb->ExtFlags &= ~CM_KCB_SYM_LINK_FOUND; } }
VOID NTAPI CmpCleanUpKcbCacheWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb, IN BOOLEAN LockHeldExclusively) { PCM_KEY_CONTROL_BLOCK Parent; PAGED_CODE(); /* Sanity checks */ ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) || (CmpTestRegistryLockExclusive() == TRUE)); ASSERT(Kcb->RefCount == 0); /* Cleanup the value cache */ CmpCleanUpKcbValueCache(Kcb); /* Dereference the NCB */ CmpDereferenceNameControlBlockWithLock(Kcb->NameBlock); /* Check if we have an index hint block and free it */ if (Kcb->ExtFlags & CM_KCB_SUBKEY_HINT) CmpFree(Kcb->IndexHint, 0); /* Check if we were already deleted */ Parent = Kcb->ParentKcb; if (!Kcb->Delete) CmpRemoveKeyControlBlock(Kcb); /* Set invalid KCB signature */ Kcb->Signature = CM_KCB_INVALID_SIGNATURE; /* Free the KCB as well */ CmpFreeKeyControlBlock(Kcb); /* Check if we have a parent */ if (Parent) { /* Dereference the parent */ LockHeldExclusively ? CmpDereferenceKeyControlBlockWithLock(Parent,LockHeldExclusively) : CmpDelayDerefKeyControlBlock(Parent); } }
VOID NTAPI DelistKeyBodyFromKCB(IN PCM_KEY_BODY KeyBody, IN BOOLEAN LockHeld) { ULONG i; /* Sanity check */ ASSERT(KeyBody->KeyControlBlock != NULL); /* Check if we can use the parent KCB array */ for (i = 0; i < 4; i++) { /* Add it into the list */ if (InterlockedCompareExchangePointer((VOID*)&KeyBody->KeyControlBlock-> KeyBodyArray[i], NULL, KeyBody) == KeyBody) { /* Removed */ return; } } /* Sanity checks */ ASSERT(IsListEmpty(&KeyBody->KeyControlBlock->KeyBodyListHead) == FALSE); ASSERT(IsListEmpty(&KeyBody->KeyBodyList) == FALSE); /* Lock the KCB */ if (!LockHeld) CmpAcquireKcbLockExclusive(KeyBody->KeyControlBlock); ASSERT((CmpIsKcbLockedExclusive(KeyBody->KeyControlBlock) == TRUE) || (CmpTestRegistryLockExclusive() == TRUE)); /* Remove the entry */ RemoveEntryList(&KeyBody->KeyBodyList); /* Unlock it it if we did a manual lock */ if (!LockHeld) CmpReleaseKcbLock(KeyBody->KeyControlBlock); }
VOID CmpRunDownDelayDerefKCBEngine( PCM_KEY_CONTROL_BLOCK KeyControlBlock, BOOLEAN RegLockHeldEx) { PCM_DELAY_DEREF_KCB_ITEM DelayItem; ULONG NewIndex; ULONG Index1 = CmpHashTableSize; ULONG Index2 = CmpHashTableSize; PCMHIVE CmHive = NULL; BOOLEAN DoUnloadCheck = FALSE; CM_PAGED_CODE(); ASSERT_CM_LOCK_OWNED(); ASSERT( (KeyControlBlock && CmpIsKCBLockedExclusive(KeyControlBlock)) || (CmpTestRegistryLockExclusive() == TRUE) ); if( KeyControlBlock ) { Index1 = GET_HASH_INDEX(KeyControlBlock->ConvKey); if( KeyControlBlock->ParentKcb ) { Index2 = GET_HASH_INDEX(KeyControlBlock->ParentKcb->ConvKey); } } LOCK_KCB_DELAY_DEREF_LIST(); while( !CmpIsListEmpty(&CmpDelayDerefKCBListHead) ) { // // pull it out of the list // DelayItem = (PCM_DELAY_DEREF_KCB_ITEM)RemoveHeadList(&CmpDelayDerefKCBListHead); UNLOCK_KCB_DELAY_DEREF_LIST(); DelayItem = CONTAINING_RECORD( DelayItem, CM_DELAY_DEREF_KCB_ITEM, ListEntry); CmpClearListEntry(&(DelayItem->ListEntry)); // // take additional precaution in the case the hive has been unloaded and this is the root // DoUnloadCheck = FALSE; if( !DelayItem->Kcb->Delete ) { CmHive = (PCMHIVE)CONTAINING_RECORD(DelayItem->Kcb->KeyHive, CMHIVE, Hive); if( IsHiveFrozen(CmHive) ) { // // unload is pending for this hive; // DoUnloadCheck = TRUE; } } NewIndex = GET_HASH_INDEX(DelayItem->Kcb->ConvKey); // // now deref and free // if( (NewIndex == Index1) || (NewIndex == Index2) ) { // // we already hold the lock // ASSERT_KCB_LOCKED_EXCLUSIVE(DelayItem->Kcb); CmpDereferenceKeyControlBlockWithLock(DelayItem->Kcb,RegLockHeldEx); } else { // // we can't afford to force acquire; try to acquire and if it fails bail out // if( CmpKCBLockForceAcquireAllowed(Index1,Index2,NewIndex) ) { CmpLockHashEntryByIndexExclusive(NewIndex); } else if( CmpTryToLockHashEntryByIndexExclusive(NewIndex) == FALSE ) { // // add the item back to the list and bail out // ASSERT( CmpTestRegistryLockExclusive() == FALSE ); LOCK_KCB_DELAY_DEREF_LIST(); InsertHeadList(&CmpDelayDerefKCBListHead,&(DelayItem->ListEntry)); UNLOCK_KCB_DELAY_DEREF_LIST(); return; } ASSERT_KCB_LOCKED_EXCLUSIVE(DelayItem->Kcb); CmpDereferenceKeyControlBlockWithLock(DelayItem->Kcb,RegLockHeldEx); CmpUnlockHashEntryByIndex(NewIndex); } CmpDelayDerefKCBFreeEntry(DelayItem); // // if we dropped a reference inside a frozen hive, we may need to unload the hive // if( DoUnloadCheck == TRUE ) { CmpDoQueueLateUnloadWorker(CmHive); } LOCK_KCB_DELAY_DEREF_LIST(); } UNLOCK_KCB_DELAY_DEREF_LIST(); }
VOID CmpAddToDelayedClose( IN PCM_KEY_CONTROL_BLOCK kcb, IN BOOLEAN RegLockHeldEx ) /*++ Routine Description: Adds a kcb to the delayed close table Arguments: kcb - the kcb in question Note: kcb lock/resource should be acquired exclusively when this function is called Return Value: NONE. --*/ { PCM_DELAYED_CLOSE_ENTRY DelayedEntry = NULL; CM_PAGED_CODE(); ASSERT( (CmpIsKCBLockedExclusive(kcb) == TRUE) || // this kcb is owned exclusive (CmpTestRegistryLockExclusive() == TRUE) ); // or the entire registry is locked exclusive // Already on delayed close, don't try to put on again if (kcb->DelayedCloseIndex != CmpDelayedCloseSize) { // see if we really need this ASSERT( FALSE ); return; } ASSERT(kcb->RefCount == 0); // // now materialize a new entry for this kcb // ASSERT_KEYBODY_LIST_EMPTY(kcb); DelayedEntry = CmpDelayCloseAllocNewEntry(); if( !DelayedEntry ) { // // this is bad luck; we need to free the kcb in place // CmpCleanUpKcbCacheWithLock(kcb,RegLockHeldEx); return; } #if DBG if( kcb->InDelayClose != 0 ) { ASSERT( FALSE ); } { LONG OldRefCount; LONG NewRefCount; OldRefCount = *(PLONG)&kcb->InDelayClose; //get entire dword ASSERT( OldRefCount == 0 ); NewRefCount = 1; if( InterlockedCompareExchange((PLONG)&kcb->InDelayClose,NewRefCount,OldRefCount) != OldRefCount ) { ASSERT( FALSE ); } } #endif //DBG // // populate the entry and insert it into the LRU list (at the top). // kcb->DelayedCloseIndex = 0; // see if we really need this kcb->DelayCloseEntry = (PVOID)DelayedEntry; // need this for removing it back from here DelayedEntry->KeyControlBlock = kcb; InterlockedIncrement((PLONG)&CmpDelayedCloseElements); LOCK_DELAY_CLOSE(); InsertHeadList( &CmpDelayedLRUListHead, &(DelayedEntry->DelayedLRUList) ); // // check if limit hit and arm timer if not already armed // if( (CmpDelayedCloseElements > CmpDelayedCloseSize) && (!CmpDelayCloseWorkItemActive) ) { CmpArmDelayedCloseTimer(); } UNLOCK_DELAY_CLOSE(); // // we're done here // }
VOID CmpRemoveFromDelayedClose( IN PCM_KEY_CONTROL_BLOCK kcb ) /*++ Routine Description: Removes a KCB from the delayed close table; Arguments: kcb - the kcb in question Note: kcb lock/resource should be acquired exclusively when this function is called Return Value: NONE. --*/ { PCM_DELAYED_CLOSE_ENTRY DelayedEntry; CM_PAGED_CODE(); ASSERT( (CmpIsKCBLockedExclusive(kcb) == TRUE) || // this kcb is owned exclusive (CmpTestRegistryLockExclusive() == TRUE) ); // or the entire registry is locked exclusive // not on delay close; don't try to remove it. if (kcb->DelayedCloseIndex == CmpDelayedCloseSize) { // see if we really need this ASSERT( FALSE ); return; } DelayedEntry = (PCM_DELAYED_CLOSE_ENTRY)kcb->DelayCloseEntry; if( DelayedEntry != NULL ) { LOCK_DELAY_CLOSE(); CmpRemoveEntryList(&(DelayedEntry->DelayedLRUList)); UNLOCK_DELAY_CLOSE(); // // give back the entry // CmpDelayCloseFreeEntry(DelayedEntry); InterlockedDecrement((PLONG)&CmpDelayedCloseElements); #if DBG if( kcb->InDelayClose == 0 ) { ASSERT( FALSE ); } { LONG OldRefCount; LONG NewRefCount; OldRefCount = *(PLONG)&kcb->InDelayClose; //get entire dword ASSERT( OldRefCount == 1 ); NewRefCount = 0; if( InterlockedCompareExchange((PLONG)&kcb->InDelayClose,NewRefCount,OldRefCount) != OldRefCount ) { ASSERT( FALSE ); } } #endif //DBG } // // easy enough huh ? // kcb->DelayedCloseIndex = CmpDelayedCloseSize; // see if we really need this kcb->DelayCloseEntry = NULL; }