Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1231 lines
31 KiB

/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
certnotf.cxx
Abstract:
This module contains the code for the class to deal with CAPI store change notifications
Author:
Alex Mallet [amallet] 17-Dec-1997
Revision History:
--*/
#include "tcpdllp.hxx"
#pragma hdrstop
#include <winbase.h>
#include <dbgutil.h>
#include <ole2.h>
#include <imd.h>
#include <nturtl.h>
#include <certnotf.hxx>
#if DBG
#define VALIDATE_HEAP() DBG_ASSERT( RtlValidateProcessHeaps() )
#else
#define VALIDATE_HEAP()
#endif
DWORD STORE_CHANGE_ENTRY::m_dwNumEntries = 0;
CRITICAL_SECTION *STORE_CHANGE_NOTIFIER::m_pStoreListCS = NULL;
STORE_CHANGE_NOTIFIER::STORE_CHANGE_NOTIFIER() :
m_dwSignature(NOTIFIER_GOOD_SIG)
/*++
Routine Description:
Constructor
Arguments:
None
Returns:
Nothing
--*/
{
//
// Critical sections
//
if ( !STORE_CHANGE_NOTIFIER::m_pStoreListCS )
{
STORE_CHANGE_NOTIFIER::m_pStoreListCS = new CRITICAL_SECTION;
if ( STORE_CHANGE_NOTIFIER::m_pStoreListCS )
{
INITIALIZE_CRITICAL_SECTION(STORE_CHANGE_NOTIFIER::m_pStoreListCS);
}
else
{
DBGPRINTF((DBG_CONTEXT,
"Failed to allocate memory for store list critical section !\n"));
}
}
InitializeListHead( &m_StoreList );
}
STORE_CHANGE_NOTIFIER::~STORE_CHANGE_NOTIFIER()
/*++
Routine Description:
Destructor
Arguments:
None
Returns:
Nothing
--*/
{
DBG_ASSERT( CheckSignature() );
STORE_CHANGE_NOTIFIER::Lock();
//
// Clean up all the stores, so we don't get bitten in the @$$ when trying to
// clean up the STORE_CHANGE_ENTRY objects : CertCloseStore() calls RtlDeregisterWait()
// which can't be called from inside a callback function, so we need to clean up
// the cert stores before triggering any of the callbacks used to clean up the
// STORE_CHANGE_ENTRY objects.
//
LIST_ENTRY *pListEntry;
STORE_CHANGE_ENTRY *pStoreEntry;
for ( pListEntry = m_StoreList.Flink;
pListEntry != &m_StoreList;
pListEntry = pListEntry->Flink )
{
pStoreEntry = CONTAINING_RECORD( pListEntry, STORE_CHANGE_ENTRY, m_StoreListEntry );
CertCloseStore( pStoreEntry->m_hCertStore,
0 );
pStoreEntry->m_hCertStore = NULL;
}
//
// Go through both the active and inactive store entries and start the process to
// clean them up [they'll actually be cleaned up on a different thread, in a callback from
// the thread pool].
//
while ( !IsListEmpty( &m_StoreList ) )
{
pStoreEntry = CONTAINING_RECORD( m_StoreList.Flink,
STORE_CHANGE_ENTRY,
m_StoreListEntry );
RemoveEntryList( &(pStoreEntry->m_StoreListEntry) );
InitializeListHead( &(pStoreEntry->m_StoreListEntry) );
StartCleanup( pStoreEntry );
}
STORE_CHANGE_NOTIFIER::Unlock();
//
// The STORE_CHANGE_ENTRY objects are cleaned up on a different so we loop and wait
// until they've all been cleaned up, to avoid problems with a thread/DLL going away
// before proper cleanup has occurred.
//
DWORD dwNumWaits = 0;
DWORD dwNumEntries = 0;
while ( ( dwNumEntries = STORE_CHANGE_ENTRY::QueryStoreEntryCount() ) &&
dwNumWaits < 30 * 5 ) //sleep for 2 secs => 30x/min, wait for 5 mins
{
Sleep( 2000 );
DBGPRINTF((DBG_CONTEXT,
"Waiting %d seconds for %d store entries to be cleaned up\n",
dwNumWaits * 2,
dwNumEntries));
dwNumWaits++;
}
if ( dwNumEntries != 0 )
{
DBGPRINTF((DBG_CONTEXT,
"WARNING : Failed to clean up all STORE_CHANGE_ENTRY objects, %d left\n",
dwNumEntries));
}
else
{
DBGPRINTF((DBG_CONTEXT,
"Cleaned up all store entries \n"));
}
m_dwSignature = NOTIFIER_BAD_SIG;
}
VOID STORE_CHANGE_NOTIFIER::ReleaseRegisteredStores()
/*++
Routine Description:
Releases all the events that have been registered for store change notifications
and closes the stores that were being watched.
Arguments:
None
Returns:
Nothing
--*/
{
LIST_ENTRY * pEntry;
DBG_ASSERT( CheckSignature() );
STORE_CHANGE_NOTIFIER::Lock();
for ( pEntry = m_StoreList.Flink;
pEntry != &m_StoreList;
pEntry = pEntry->Flink )
{
STORE_CHANGE_ENTRY *pStoreEntry = CONTAINING_RECORD( pEntry,
STORE_CHANGE_ENTRY,
m_StoreListEntry );
StartCleanup( pStoreEntry );
}
STORE_CHANGE_NOTIFIER::Unlock();
}
VOID STORE_CHANGE_NOTIFIER::StartCleanup( IN STORE_CHANGE_ENTRY *pEntry )
/*++
Routine Description:
Start the cleanup for a STORE_CHANGE_ENTRY object.
This function MUST be called between calls to STORE_CHANGE_NOTIFIER::Lock()/Unlock() !
Arguments:
pEntry - STORE_CHANGE_ENTRY to clean up.
Returns:
Nothing
--*/
{
//
// Grab the lock, mark the entry as being ready for deletion,signal the event associated
// with it and release a reference to the entry . We hold the lock, so we have exclusive
// access to the entries. Possible execution paths :
//
// #1. No callback comes in while we're executing this code. We set the deletion bit and
// signal the event. If the callback now fires, it smacks into
// the lock and waits its turn. If it doesn't fire, that's OK too. We unlock and go on our
// merry way. At some point in time, the callback -will- fire, because we didn't call
// UnregisterWait(). Then, in the callback, we see the "delete me" flag and delete the
// entry. Also, the wait is cancelled because we acquired the wait handle with the
// WT_EXECUTEDELETEWAIT flag, which removes the wait immediately after calling the callback.
//
// #2. A callback comes in while we're executing this code. It waits to acquire the lock,
// and by the time it acquires it we've set the deletion bit and signalled the event.
// The callback sees the deletion bit and deletes the entry. The wait is cancelled, and
// so it doesn't matter that we signalled the event.
//
pEntry->MarkForDelete();
SetEvent( pEntry->QueryStoreEvent() );
}
BOOL STORE_CHANGE_NOTIFIER::IsStoreRegisteredForChange( IN LPTSTR pszStoreName,
IN NOTIFFNCPTR pFncPtr,
IN LPVOID pvParam )
/*++
Routine Description:
Check whether the store described by the parameters already has an event registered
for change notifications.
Arguments:
pszStoreName - name of cert store
pFncPtr - pointer to notification function. If pFncPtr == INVALID_FNC_PTR, checks for any
notification functions
pvParam - arg to notification function; if pFncPtr == INVALID_FNC_PTR, is ignored
Returns:
TRUE if store is registered, FALSE if not.
--*/
{
DBG_ASSERT( CheckSignature() );
DBG_ASSERT( pszStoreName );
DBG_ASSERT( pFncPtr );
PSTORE_CHANGE_ENTRY pEntry = InternalIsStoreRegisteredForChange( pszStoreName,
pFncPtr,
pvParam ) ;
return ( pEntry ? TRUE : FALSE );
}
PSTORE_CHANGE_ENTRY
STORE_CHANGE_NOTIFIER::InternalIsStoreRegisteredForChange( IN LPTSTR pszStoreName,
IN NOTIFFNCPTR pFncPtr,
IN LPVOID pvParam )
/*++
Routine Description:
Check whether the store described by the parameters already has an event registered
for change notifications.
Arguments:
pszStoreName - name of cert store
pFncPtr - pointer to notification function. If pFncPtr == INVALID_FNC_PTR, checks for any
notification functions
pvParam - arg for notification function. Ignored if pFncPtr == INVALID_FNC_PTR
Returns:
pointer to STORE_ENTRY structure that contains the registration info, NULL if non-existent.
--*/
{
DBG_ASSERT( CheckSignature() );
DBG_ASSERT( pszStoreName );
DBG_ASSERT( pFncPtr );
STORE_CHANGE_ENTRY *pStoreEntry = NULL, *pMatchingEntry = NULL;
LIST_ENTRY *pEntry;
BOOL fFound = FALSE;
STORE_CHANGE_NOTIFIER::Lock();
for ( pEntry = m_StoreList.Flink;
pEntry != &m_StoreList;
pEntry = pEntry->Flink )
{
pStoreEntry = CONTAINING_RECORD( pEntry, STORE_CHANGE_ENTRY, m_StoreListEntry );
if ( pStoreEntry->Matches( pszStoreName,
pFncPtr,
pvParam ) )
{
pMatchingEntry = pStoreEntry;
break;
}
}
STORE_CHANGE_NOTIFIER::Unlock();
return pMatchingEntry;
}
BOOL STORE_CHANGE_NOTIFIER::RegisterStoreForChange( IN LPTSTR pszStoreName,
IN HCERTSTORE hStore,
IN NOTIFFNCPTR pFncPtr,
IN LPVOID pvParam )
/*++
Routine Description:
Register the store for change notifications
Critical Sections acquired : m_pStoreListCS, m_pStoreArrayCS
Arguments:
pszStoreName - name of cert store
hCertStore - handle to cert store
pFncPtr - pointer to notification function
pvParam - arg to notification function
Returns:
TRUE if store was registered, FALSE if not
--*/
{
DBG_ASSERT( CheckSignature() );
DBG_ASSERT( pszStoreName );
DBG_ASSERT( pFncPtr );
BOOL fAlreadyRegistered = FALSE;
BOOL fSuccess = FALSE;
PSTORE_CHANGE_ENTRY pStoreEntry = NULL;
STORE_CHANGE_NOTIFIER::Lock();
//
// Check whether there already some notifications on this store
//
pStoreEntry = InternalIsStoreRegisteredForChange( pszStoreName,
(NOTIFFNCPTR) INVALID_FNC_PTR,
NULL );
fAlreadyRegistered = (pStoreEntry == NULL ? FALSE : TRUE );
//
// If this is a totally new store, need to allocate and fill in new store watch entry
//
if ( !pStoreEntry )
{
HCERTSTORE hCertStore = NULL;
HANDLE hStoreEvent = NULL;
HANDLE hWaitHandle = NULL;
pStoreEntry = new STORE_CHANGE_ENTRY( this,
pszStoreName,
hStore,
pFncPtr,
pvParam) ;
if ( !pStoreEntry || pStoreEntry->GetLastError() )
{
if ( pStoreEntry )
{
SetLastError( pStoreEntry->GetLastError() );
}
goto EndRegisterStore;
}
//
// Add the entire entry to the list of stores to be watched
//
InsertTailList( &m_StoreList, &pStoreEntry->m_StoreListEntry );
}
//
// Else, possibly update the notification functions to be called - only update
// if there isn't already a copy of this function with the same parameters
//
else
{
DBG_ASSERT( pStoreEntry->QueryStoreHandle() &&
pStoreEntry->QueryStoreEvent() &&
pStoreEntry->QueryNotifier() &&
pStoreEntry->QueryWaitHandle() );
if ( !pStoreEntry->ContainsNotifFnc( pFncPtr,
pvParam ) )
{
if ( !pStoreEntry->AddNotifFnc( pFncPtr,
pvParam ) )
{
SetLastError( pStoreEntry->GetLastError() );
goto EndRegisterStore;
}
}
}
fSuccess = TRUE;
EndRegisterStore:
if ( !fSuccess )
{
//
// If we failed to register the store and we allocated a new STORE_CHANGE_ENTRY
// object, clean it up. Note that ref count is only set if everything succeeds
//
if ( !fAlreadyRegistered && pStoreEntry )
{
delete pStoreEntry;
}
DBGPRINTF((DBG_CONTEXT,"Failed to register store %s : 0x%x\n",
pszStoreName, GetLastError()));
}
STORE_CHANGE_NOTIFIER::Unlock();
return fSuccess;
}
VOID STORE_CHANGE_NOTIFIER::UnregisterStore( IN LPTSTR pszStoreName,
IN NOTIFFNCPTR pNotifFnc,
IN LPVOID pvParam )
/*++
Routine Description:
Unregister a notification function for a store
Arguments:
pszStoreName - name of store
pNotifFnc - notification function to deregister. If pNotifFnc == INVALID_FNC_PTR,
all notifications for that store are removed
pvParam - arg to notification function. Ignored if pNotifFnc == INVALID_FNC_PTR
--*/
{
DBG_ASSERT( CheckSignature() );
DBG_ASSERT( pszStoreName );
DBG_ASSERT( pNotifFnc );
STORE_CHANGE_ENTRY *pStoreEntry;
LIST_ENTRY *pEntry;
BOOL fRemoveAll = (pNotifFnc == (NOTIFFNCPTR) INVALID_FNC_PTR ? TRUE : FALSE );
STORE_CHANGE_NOTIFIER::Lock();
//
// Iterate through the active list to find it
//
for ( pEntry = m_StoreList.Flink;
pEntry != &m_StoreList;
pEntry = pEntry->Flink )
{
pStoreEntry = CONTAINING_RECORD( pEntry, STORE_CHANGE_ENTRY, m_StoreListEntry );
if ( pStoreEntry->Matches( pszStoreName,
pNotifFnc,
pvParam ) )
{
//
// If we're removing all notifications for this store, or there will be no
// notification functions left for this store, clean everything up
//
if ( fRemoveAll || ( pStoreEntry->RemoveNotifFnc( pNotifFnc,
pvParam ) &&
!pStoreEntry->HasNotifFncs() ) )
{
StartCleanup( pStoreEntry );
}
break;
}
}
STORE_CHANGE_NOTIFIER::Unlock();
}
#if DBG
VOID STORE_CHANGE_NOTIFIER::DumpRegisteredStores()
/*++
Routine Description:
Dumps all the stores currently being watched
Arguments:
None
Returns:
Nothing
--*/
{
STORE_CHANGE_ENTRY *pStoreEntry = NULL;
LIST_ENTRY *pEntry1 = NULL, *pEntry2 = NULL;
DBG_ASSERT( CheckSignature() );
STORE_CHANGE_NOTIFIER::Lock();
DBGPRINTF((DBG_CONTEXT,
"------------------------------------------------------------------------\nRegistered Stores : \n"));
for ( pEntry1 = m_StoreList.Flink;
pEntry1 != &m_StoreList;
pEntry1 = pEntry1->Flink )
{
pStoreEntry = CONTAINING_RECORD( pEntry1,
STORE_CHANGE_ENTRY,
m_StoreListEntry );
DBGPRINTF((DBG_CONTEXT,
"Store %s, store handle 0x%x, event handle 0x%x, wait handle 0x%x has the ff functions registered : \n",
pStoreEntry->QueryStoreName(),
pStoreEntry->QueryStoreHandle(),
pStoreEntry->QueryStoreEvent(),
pStoreEntry->QueryWaitHandle() ));
LIST_ENTRY *pFncs = pStoreEntry->QueryNotifFncChain();
for ( pEntry2 = pFncs->Flink;
pEntry2 != pFncs;
pEntry2 = pEntry2->Flink )
{
PNOTIF_FNC_CHAIN_ENTRY pNFE = CONTAINING_RECORD( pEntry2,
NOTIF_FNC_CHAIN_ENTRY,
ListEntry );
DBGPRINTF((DBG_CONTEXT,"Function 0x%x, parameter 0x%x\n",
pNFE->pNotifFnc,
pNFE->pvParam ));
}
}
DBGPRINTF((DBG_CONTEXT,
"------------------------------------------------------------------------\n"));
STORE_CHANGE_NOTIFIER::Unlock();
}
#endif //DBG
VOID NTAPI STORE_CHANGE_NOTIFIER::NotifFncCaller( IN PVOID pvCallbackArg,
IN BOOLEAN fUnused )
/*++
Routine Description:
This is the function called when one of the events we registered for is signalled.
The context passed in can be in one of three states :
1. Valid : has an associated chain of notification functions that are to be called.
2. Invalid : don't do any processing, because we're not interested in that store anymore
ie UnregisterStore() has been called on it
3. To Be Deleted : the context is to be deleted, because we're shutting down
Arguments:
pvCallbackArg - context pointer
fUnused - boolean, not used. [part of WAITFORTIMERCALLBACKFUNC prototype, which this function
has to conform to]
--*/
{
LIST_ENTRY * pNotifFncChain = NULL;
PSTORE_CHANGE_ENTRY pEntry = NULL;
if ( !pvCallbackArg )
{
DBG_ASSERT( FALSE ); //make sure we barf in debug build
return;
}
pEntry = (PSTORE_CHANGE_ENTRY) pvCallbackArg;
//
// Make sure we have exclusive access
//
STORE_CHANGE_NOTIFIER::Lock();
DBG_ASSERT( pEntry->CheckSignature() );
//
// If we signalled the event ourselves because we need to clean up this context,
// just delete it - we know we won't get any more callbacks, since we acquired the
// wait handle with WTEXECUTEDELETEWAIT. We don't need to remove it from a list
// because it has already been removed prior to this callback being generated.
// [in the destructor for STORE_CHANGE_NOTIFIER]
//
if ( !pEntry->IsMarkedForDelete() && !pEntry->IsInvalid() )
{
//
// Make a copy of the list of notification functions to call, so that even if
// one of the functions called changes the list, we still have a kosher copy to
// work with.
//
// Note that another assumption is that if functions A and B are both in the list
// being walked, and A is called before B, it doesn't destroy anything that B uses.
//
pNotifFncChain = CopyNotifFncChain( pEntry->QueryNotifFncChain() );
//
// Remove this entry from the active list and delete it: it'll never get notified again
// because we acquired it with the WTEXECUTEDELETEWAIT flag
//
}
RemoveEntryList( &(pEntry->m_StoreListEntry) );
delete pEntry;
STORE_CHANGE_NOTIFIER::Unlock();
// Call the notification functions now, after releasing the lock. This
// prevents deadlock with SSPIFILT. In particular the scenario where:
//
// SSPIFILT__AddFullyQualifiedItem acquires SSPI then StoreChange
// NotifyFncCaller acquires StoreChange then SSPI
//
// should be avoided
if ( pNotifFncChain )
{
NOTIF_FNC_CHAIN_ENTRY * pChainEntry = NULL;
LIST_ENTRY * pListEntry = NULL;
//
// Walk the list, call the functions and be generally studly ...
//
for ( pListEntry = pNotifFncChain->Flink;
pListEntry != pNotifFncChain;
pListEntry = pListEntry->Flink )
{
pChainEntry = CONTAINING_RECORD( pListEntry, NOTIF_FNC_CHAIN_ENTRY,
ListEntry );
if ( pChainEntry->pNotifFnc )
{
#ifdef NOTIFICATION_DBG
DBGPRINTF((DBG_CONTEXT,
"Calling notification fnc %p, arg %p\n",
pChainEntry->pNotifFnc, pChainEntry->pvParam));
#endif
pChainEntry->pNotifFnc( pChainEntry->pvParam );
}
}
DeallocateNotifFncChain( pNotifFncChain );
}
return;
}
STORE_CHANGE_ENTRY::STORE_CHANGE_ENTRY( STORE_CHANGE_NOTIFIER *pNotifier,
LPSTR pszStoreName,
HCERTSTORE hStore,
NOTIFFNCPTR pFncPtr,
PVOID pvParam ) :
m_dwSignature( STORE_ENTRY_GOOD_SIG ),
m_pNotifier( pNotifier ),
m_dwRefCount( -1 ),
m_dwError( 0 ),
m_hCertStore( NULL ),
m_hStoreEvent( NULL ),
m_hWaitHandle( NULL ),
m_fDeleteMe( FALSE ),
m_fInvalid( FALSE ),
m_strStoreName( pszStoreName )
/*++
Routine Description:
Constructor
Arguments:
pNotifier - parent notifier object
pszStoreName - name of store to be watched
hStore - handle to store to be watched
pFncPtr - notification function to call when store changes
pvParam - arg to notification function
Returns:
Nothing
--*/
{
PNOTIF_FNC_CHAIN_ENTRY pNewNotifFnEntry = NULL;
INITIALIZE_CRITICAL_SECTION( &m_CS );
InitializeListHead( &m_NotifFncChain );
//
// Duplicate store handle to watch
//
m_hCertStore = CertDuplicateStore( hStore );
//
// Create the event to be signalled when store changes
//
if ( !(m_hStoreEvent = CreateEvent( NULL, //default attributes,
TRUE,
FALSE, //initally non-signalled
NULL ) ) ) //no name
{
m_dwError = GetLastError();
return;
}
//
// Register with wait thread pool
//
#if 1
if ( !NT_SUCCESS( RtlRegisterWait( &m_hWaitHandle,
m_hStoreEvent,
STORE_CHANGE_NOTIFIER::NotifFncCaller,
(PVOID) this,
INFINITE,
WT_EXECUTEONLYONCE ) ) )
#else
if ( !(m_hWaitHandle = RegisterWaitForSingleObjectEx( m_hStoreEvent,
STORE_CHANGE_NOTIFIER::NotifFncCaller,
(PVOID) this,
INFINITE,
WT_EXECUTEONLYONCE ) ) )
#endif
{
m_dwError = GetLastError();
goto cleanup;
}
//
// Register for change events on the store
//
if ( !CertControlStore( m_hCertStore,
0,
CERT_STORE_CTRL_NOTIFY_CHANGE,
(LPVOID) &m_hStoreEvent) )
{
m_dwError = GetLastError();
goto cleanup;
}
//
// Create a new chain of notification functions
//
pNewNotifFnEntry = new NOTIF_FNC_CHAIN_ENTRY;
if ( !pNewNotifFnEntry )
{
DBGPRINTF((DBG_CONTEXT,
"Couldn't allocate new notification function chain : 0x%x\n",
GetLastError()));
m_dwError = ERROR_OUTOFMEMORY;
goto cleanup;
}
pNewNotifFnEntry->pNotifFnc = pFncPtr;
pNewNotifFnEntry->pvParam = pvParam;
//
// Add the function to the chain
//
InsertTailList( &m_NotifFncChain, &pNewNotifFnEntry->ListEntry );
//
// Increment number of entry objects, to help in cleanup later
//
STORE_CHANGE_ENTRY::IncrementStoreEntryCount();
cleanup:
//
// Cleanup that's only done on error
//
if ( m_dwError != 0 )
{
if ( m_hWaitHandle )
{
RtlDeregisterWait( m_hWaitHandle );
m_hWaitHandle = NULL;
}
if ( m_hStoreEvent )
{
CloseHandle( m_hStoreEvent );
m_hStoreEvent = NULL;
}
if ( m_hCertStore )
{
CertCloseStore( m_hCertStore,
0 );
m_hCertStore = NULL;
}
//
// Go through the chain of notification functions and clean it up
//
while ( !IsListEmpty(&(m_NotifFncChain)) )
{
NOTIF_FNC_CHAIN_ENTRY *pChainEntry = CONTAINING_RECORD( m_NotifFncChain.Flink,
NOTIF_FNC_CHAIN_ENTRY,
ListEntry );
RemoveEntryList( &(pChainEntry->ListEntry) );
delete pChainEntry;
}
}
}
STORE_CHANGE_ENTRY::~STORE_CHANGE_ENTRY()
/*++
Routine Description:
Destructor
Arguments:
None
Return value:
None
--*/
{
DBG_ASSERT( CheckSignature() );
//
// No need to call RtlDeregisterWait() for the handle, since it's already been
// deregistered [after having been used in the callback]
//
//
// Clean up store change event
//
if ( m_hStoreEvent )
{
CloseHandle( m_hStoreEvent );
m_hStoreEvent = NULL;
}
//
// Close cert store
//
if ( m_hCertStore )
{
CertCloseStore( m_hCertStore,
0 );
m_hCertStore = NULL;
}
//
// Go through the chain of notification functions and clean it up
//
while ( !IsListEmpty(&(m_NotifFncChain)) )
{
NOTIF_FNC_CHAIN_ENTRY *pChainEntry = CONTAINING_RECORD( m_NotifFncChain.Flink,
NOTIF_FNC_CHAIN_ENTRY,
ListEntry );
RemoveEntryList( &(pChainEntry->ListEntry) );
delete pChainEntry;
}
DeleteCriticalSection( &m_CS );
//
// Another one bites the dust ...
//
STORE_CHANGE_ENTRY::DecrementStoreEntryCount();
m_dwSignature = STORE_ENTRY_BAD_SIG;
}
BOOL STORE_CHANGE_ENTRY::ContainsNotifFnc( IN NOTIFFNCPTR pFncPtr,
IN LPVOID pvParam )
/*++
Routine Description:
Checks whether the given store watch entry contains the specified notification function with
the specified args
Arguments:
pFncPtr - pointer to notification function
pvParam - arg to notification function
Returns:
True if function is found, false otherwise
--*/
{
NOTIF_FNC_CHAIN_ENTRY *pChainEntry;
LIST_ENTRY *pEntry = NULL;
BOOL fFound = FALSE;
Lock();
for ( pEntry = m_NotifFncChain.Flink;
pEntry != &m_NotifFncChain;
pEntry = pEntry->Flink )
{
pChainEntry = CONTAINING_RECORD( pEntry, NOTIF_FNC_CHAIN_ENTRY, ListEntry );
if ( pChainEntry->pNotifFnc == pFncPtr && pChainEntry->pvParam == pvParam )
{
fFound = TRUE;
break;
}
}
Unlock();
return fFound;
}
BOOL STORE_CHANGE_ENTRY::AddNotifFnc( IN NOTIFFNCPTR pFncPtr,
IN LPVOID pvParam )
/*++
Routine Description:
Adds a notification function to a store entry
Arguments:
pFncPtr - pointer to notification function
pvParam - arg to notification function
Returns:
TRUE if function is added, FALSE otherwise
--*/
{
PNOTIF_FNC_CHAIN_ENTRY pNewFnc = new NOTIF_FNC_CHAIN_ENTRY;
if ( !pNewFnc )
{
DBGPRINTF((DBG_CONTEXT,
"Failed to get new notif fnc entry : 0x%x\n",
GetLastError()));
m_dwError = ERROR_OUTOFMEMORY;
return FALSE;
}
pNewFnc->pNotifFnc = pFncPtr;
pNewFnc->pvParam = pvParam;
Lock();
InsertTailList( &m_NotifFncChain, &pNewFnc->ListEntry );
Unlock();
return TRUE;
}
BOOL STORE_CHANGE_ENTRY::RemoveNotifFnc( IN NOTIFFNCPTR pFncPtr,
IN LPVOID pvParam )
/*++
Routine Description:
Removes a notification function from a store entry
Arguments:
pFncPtr - pointer to notification function
pvParam - arg to notification function
Returns:
Noting
--*/
{
NOTIF_FNC_CHAIN_ENTRY *pChainEntry;
LIST_ENTRY *pEntry = NULL;
Lock();
for ( pEntry = m_NotifFncChain.Flink;
pEntry != &m_NotifFncChain;
pEntry = pEntry->Flink )
{
pChainEntry = CONTAINING_RECORD( pEntry, NOTIF_FNC_CHAIN_ENTRY, ListEntry );
if ( pChainEntry->pNotifFnc == pFncPtr && pChainEntry->pvParam == pvParam )
{
RemoveEntryList( pEntry );
break;
}
}
Unlock();
return TRUE;
}
BOOL STORE_CHANGE_ENTRY::Matches( IN LPSTR pszStoreName,
IN NOTIFFNCPTR pFncPtr,
IN PVOID pvParam )
/*++
Routine Description:
Checks whether a given store change object matches the given store/function combination
Arguments:
pszStoreName - name of cert store
pFncPtr - pointer to notification function; may be INVALID_FNC_PTR if any function will match
pvParam - parameter for function pointed to by pFncPtr
Return Value:
TRUE if it matches, FALSE if not
--*/
{
BOOL fFound = FALSE;
Lock();
if ( ( ( m_strStoreName.IsEmpty() && pszStoreName == NULL) ||
!strcmp(m_strStoreName.QueryStr(), pszStoreName) ) &&
( pFncPtr == (NOTIFFNCPTR) INVALID_FNC_PTR ||
ContainsNotifFnc( pFncPtr,
pvParam ) ) )
{
fFound = TRUE;
}
Unlock();
return fFound;
}
LIST_ENTRY* CopyNotifFncChain( LIST_ENTRY *pNotifFncChain )
/*++
Routine Description:
Function that copies a chain of notification functions
Arguments:
pNotifFncChain - pointer to chain to be copied
Return Value:
Pointer to copied chain, NULL on failure
--*/
{
LIST_ENTRY *pNewChain = new LIST_ENTRY;
if ( !pNewChain )
{
return NULL;
}
InitializeListHead( pNewChain );
NOTIF_FNC_CHAIN_ENTRY *pChainEntry, *pNewChainEntry;
LIST_ENTRY *pListEntry;
for ( pListEntry = pNotifFncChain->Flink;
pListEntry != pNotifFncChain;
pListEntry = pListEntry->Flink )
{
pChainEntry = CONTAINING_RECORD( pListEntry, NOTIF_FNC_CHAIN_ENTRY,
ListEntry );
pNewChainEntry = new NOTIF_FNC_CHAIN_ENTRY;
if ( !pNewChainEntry )
{
DeallocateNotifFncChain( pNewChain );
return NULL;
}
pNewChainEntry->pNotifFnc = pChainEntry->pNotifFnc;
pNewChainEntry->pvParam = pChainEntry->pvParam;
InsertTailList( pNewChain, &(pNewChainEntry->ListEntry) );
}
return ( pNewChain );
}
VOID DeallocateNotifFncChain( LIST_ENTRY *pChain )
/*++
Routine Description:
Function that cleans up resources associated with a notification function chain
Arguments:
pChain - chain to be cleaned up
Return Value:
None
--*/
{
if ( !pChain )
{
return;
}
NOTIF_FNC_CHAIN_ENTRY *pChainEntry;
LIST_ENTRY *pListEntry;
while ( !IsListEmpty( pChain ) )
{
pChainEntry = CONTAINING_RECORD( pChain->Flink,
NOTIF_FNC_CHAIN_ENTRY,
ListEntry );
RemoveEntryList( &(pChainEntry->ListEntry) );
delete pChainEntry;
}
delete pChain;
}