Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

968 lines
28 KiB

/*++ BUILD Version: 0001 // Increment this if a change has global effects
Copyright (c) 1994, 1995 Microsoft Corporation.
All rights reserved.
MODULE NAME:
provider.c
ABSTRACT:
This module contains auxiliary functions to manage internal information
on the various LSAPI license service providers.
CREATED:
1995-08-15 Jeff Parham (jeffparh)
REVISION HISTORY:
--*/
#include <stddef.h>
#include <windows.h>
#include <lsapi.h>
#include "provider.h"
#include "debug.h"
#ifdef UNICODE
# pragma error( "!! Windows 95 does not support Unicode system APIs !!" )
#endif
//////////////////////////////////////////////////////////////////////////////
// MACROS //
//////////////
// Maximum length of a "ProviderN" value name found in the
// SOFTWARE\LSAPI\PROVIDERS subkey of HKEY_CURRENT_USER and HKEY_LOCAL_MACHINE
#define LS_MAX_PROVIDER_KEY_LENGTH ( 32 )
#define LS_API_REQUIRED ( TRUE )
#define LS_API_NOT_REQUIRED ( FALSE )
#define LS_PROVIDER_MUTEX_NAME ( TEXT("LSAPI/ProviderLock") )
//////////////////////////////////////////////////////////////////////////////
// LOCAL DATA //
//////////////////
// The provider table is indexed by search preference; i.e., if no specific
// licensing subsystem is selected, try l_plspProviderTable[0] first,
// and if that fails try l_plspProviderTable[1], etc., up to the number of
// providers.
PLS_PROVIDER l_plspProviderTable = NULL;
DWORD l_dwNumProviders = 0;
HANDLE l_hProviderLock = NULL;
// The provider export table describes the functions that should be exported
// by LSAPI-compliant service providers. All of the functions must be
// exported lest the provider be declared invalid.
typedef struct _LS_PROVIDER_EXPORT_INFO
{
const char * pcszExportName;
DWORD dwProviderOffset;
BOOL bExportRequired;
} LS_PROVIDER_EXPORT_INFO;
static LS_PROVIDER_EXPORT_INFO l_alpeiProviderExportTable[] =
{
{ "LSRequest", offsetof( struct _LS_PROVIDER, pLSRequest ), LS_API_REQUIRED },
{ "LSRelease", offsetof( struct _LS_PROVIDER, pLSRelease ), LS_API_REQUIRED },
{ "LSUpdate", offsetof( struct _LS_PROVIDER, pLSUpdate ), LS_API_REQUIRED },
{ "LSGetMessage", offsetof( struct _LS_PROVIDER, pLSGetMessage ), LS_API_REQUIRED },
{ "LSEnumProviders", offsetof( struct _LS_PROVIDER, pLSEnumProviders ), LS_API_REQUIRED },
{ "LSQuery", offsetof( struct _LS_PROVIDER, pLSQuery ), LS_API_REQUIRED },
{ "LSFreeHandle", offsetof( struct _LS_PROVIDER, pLSFreeHandle ), LS_API_REQUIRED },
{ "LSInstall", offsetof( struct _LS_PROVIDER, pLSInstall ), LS_API_NOT_REQUIRED },
{ "LSLicenseUnitsGet", offsetof( struct _LS_PROVIDER, pLSLicenseUnitsGet ), LS_API_NOT_REQUIRED },
{ "LSLicenseUnitsSet", offsetof( struct _LS_PROVIDER, pLSLicenseUnitsSet ), LS_API_NOT_REQUIRED },
{ NULL, 0 , 0 }
};
//////////////////////////////////////////////////////////////////////////////
// PROTOTYPES //
//////////////////
static BOOL
LSXProviderListAddAllFromKey( HKEY hLsapiKey );
static BOOL
LSXProviderListAddFromString( LPCTSTR pcszProviderValue );
static BOOL
LSXProviderInit( PLS_PROVIDER plspProvider,
LPCTSTR pcszProviderPath );
static void
LSXProviderFree( PLS_PROVIDER plspProvider );
static LS_STATUS_CODE
LSXProviderListLock( void );
static LS_STATUS_CODE
LSXProviderListUnlock( void );
//////////////////////////////////////////////////////////////////////////////
// GLOBAL IMPLEMENTATIONS //
//////////////////////////////
BOOL
LSXProviderListInit( void )
/*++
Routine Description:
Loads all configured providers.
Calling this routine more than once without calling
LSXProviderListFree() in the interim has no effect.
Arguments:
None.
Return Value:
(BOOL)
TRUE
At least one license service provider is ready for requests.
FALSE
Otherwise.
--*/
{
LONG lError;
HKEY hLsapiKey;
LS_STATUS_CODE lsscError;
if ( 0 == l_dwNumProviders )
{
l_hProviderLock = CreateMutex( NULL, FALSE, LS_PROVIDER_MUTEX_NAME );
ASSERT( NULL != l_hProviderLock );
lsscError = LSXProviderListLock();
ASSERT( LS_SUCCESS == lsscError );
if ( LS_SUCCESS == lsscError )
{
// first get the pertinent info from the current user settings
lError = RegOpenKeyEx( HKEY_CURRENT_USER,
TEXT( "SOFTWARE\\LSAPI" ),
0,
KEY_READ,
&hLsapiKey );
if ( ERROR_SUCCESS == lError )
{
// user has private LSAPI settings
LSXProviderListAddAllFromKey( hLsapiKey );
RegCloseKey( hLsapiKey );
}
// then tack on the machine-wide settings
lError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
TEXT( "SOFTWARE\\LSAPI" ),
0,
KEY_READ,
&hLsapiKey );
if ( ERROR_SUCCESS == lError )
{
// add in the public (machine-wide) settings; shake well
LSXProviderListAddAllFromKey( hLsapiKey );
RegCloseKey( hLsapiKey );
}
LSXProviderListUnlock();
}
}
// else we have already read function providers, so just use what we have
// we succeed iff we have providers to use
return ( 0 != l_dwNumProviders );
}
//////////////////////////////////////////////////////////////////////////////
PLS_PROVIDER
LSXProviderListGetByName( const char * pcszProviderName )
/*++
Routine Description:
Looks up the provider with the given name.
Arguments:
pcszProviderName (const char *)
Provider to return.
Return Value:
(PLS_PROVIDER)
A pointer to the provider if found, NULL otherwise.
--*/
{
DWORD i;
PLS_PROVIDER plspProvider = NULL;
for ( i = 0; i < l_dwNumProviders; i++ )
{
if ( !lstrcmpi( pcszProviderName, l_plspProviderTable[ i ].szProviderName ) )
{
plspProvider = &l_plspProviderTable[ i ];
break;
}
}
return plspProvider;
}
//////////////////////////////////////////////////////////////////////////////
PLS_PROVIDER
LSXProviderListGetByOrder( DWORD dwProviderNdx )
/*++
Routine Description:
Looks up the provider with the given search ordinal. Ordinals 0 through
LSXProviderListGetSize() are valid. Lower ordinals denote more preferred
license providers.
Arguments:
dwProviderNdx (DWORD)
Return provider with the given index into the search order.
Return Value:
(PLS_PROVIDER)
A pointer to the provider if dwProviderNdx is in the proper range,
NULL otherwise.
--*/
{
return ( dwProviderNdx < l_dwNumProviders ) ? &l_plspProviderTable[ dwProviderNdx ]
: NULL;
}
//////////////////////////////////////////////////////////////////////////////
DWORD
LSXProviderListGetSize( void )
/*++
Routine Description:
Returns the number of available license providers.
Arguments:
None.
Return Value:
(DWORD)
The number of available license providers.
--*/
{
return l_dwNumProviders;
}
//////////////////////////////////////////////////////////////////////////////
void
LSXProviderListFree( void )
/*++
Routine Description:
Frees the internal representation of the provider list.
Arguments:
None.
Return Value:
None.
--*/
{
DWORD i;
if ( NULL != l_plspProviderTable )
{
for ( i=0; i < l_dwNumProviders; i++ )
{
LSXProviderFree( &l_plspProviderTable[ i ] );
}
LocalFree( l_plspProviderTable );
}
l_dwNumProviders = 0;
if ( NULL != l_hProviderLock )
{
CloseHandle( l_hProviderLock );
l_hProviderLock = NULL;
}
}
//////////////////////////////////////////////////////////////////////////////
LS_STATUS_CODE
LSXProviderInstall( LS_STR * ProviderPath )
{
LS_STATUS_CODE lsscError = LS_BAD_ARG;
BOOL ok;
LS_PROVIDER lspProvider;
PLS_PROVIDER plspProvider;
HKEY hKeyProviders;
HKEY hKeySettings;
TCHAR szProviderString[ LS_MAX_PROVIDER_VALUE_LENGTH ];
TCHAR szOrderString[ 128 ];
TCHAR szValueName[ LS_MAX_PROVIDER_KEY_LENGTH ];
DWORD dwKeyType;
DWORD cbOrderString;
DWORD dwDisposition;
DWORD i;
LS_STR * pszBaseName;
int nChars;
LONG lError;
// attempt to load provider
ok = LSXProviderInit( &lspProvider, ProviderPath );
if ( !ok )
{
// provider could not be loaded -- probably a bad path or bad DLL
lsscError = LS_BAD_ARG;
}
else
{
// given provider is valid
plspProvider = LSXProviderListGetByName( lspProvider.szProviderName );
if ( NULL != plspProvider )
{
// provider is already installed
lsscError = LS_SUCCESS;
}
else
{
// configure the new provider
lsscError = LSXProviderListLock();
ASSERT( LS_SUCCESS == lsscError );
if ( LS_SUCCESS == lsscError )
{
//////////////////////////
// Configure registry //
//////////////////////////
lError = RegCreateKeyEx( HKEY_LOCAL_MACHINE,
TEXT( "Software\\LSAPI\\Providers" ),
0,
NULL,
0,
KEY_ALL_ACCESS,
NULL,
&hKeyProviders,
&dwDisposition );
ASSERT( ERROR_SUCCESS == lError );
if ( ERROR_SUCCESS != lError )
{
lsscError = LS_RESOURCES_UNAVAILABLE;
}
else
{
///////////////////////////////////////
// Configure registry -- ProviderX //
///////////////////////////////////////
// find first empty ProviderX value
for ( i=1, lError = ERROR_SUCCESS;
ERROR_SUCCESS == lError;
i++ )
{
wsprintf( szValueName, TEXT( "Provider%lu" ), (ULONG) i );
lError = RegQueryValueEx( hKeyProviders,
szValueName,
NULL,
NULL,
NULL,
NULL );
}
--i;
ASSERT( ERROR_FILE_NOT_FOUND == lError );
// create new provider string
wsprintf( szProviderString, "%hs;1.10;%hs", ProviderPath, lspProvider.szProviderName );
lError = RegSetValueEx( hKeyProviders,
szValueName,
0,
REG_SZ,
szProviderString,
1 + lstrlen( szProviderString ) );
ASSERT( ERROR_SUCCESS == lError );
if ( ERROR_SUCCESS != lError )
{
lsscError = LS_RESOURCES_UNAVAILABLE;
}
else
{
///////////////////////////////////
// Configure registry -- Order //
///////////////////////////////////
lError = RegCreateKeyEx( HKEY_LOCAL_MACHINE,
TEXT( "Software\\LSAPI\\Settings" ),
0,
NULL,
0,
KEY_ALL_ACCESS,
NULL,
&hKeySettings,
&dwDisposition );
ASSERT( ERROR_SUCCESS == lError );
if ( ERROR_SUCCESS != lError )
{
lsscError = LS_RESOURCES_UNAVAILABLE;
}
else
{
// read current provider Order
cbOrderString = sizeof( szOrderString );
lError = RegQueryValueEx( hKeySettings,
TEXT( "Order" ),
NULL,
&dwKeyType,
(LPBYTE) &szOrderString,
&cbOrderString );
if ( ERROR_SUCCESS != lError )
{
ASSERT( ERROR_FILE_NOT_FOUND == lError );
// no Order setting exists; set it to just the new provider
wsprintf( szOrderString, "%ld", (long) i );
}
else
{
// Order setting exists; tack new provider on to the end
ASSERT( (TCHAR) '\0' == szOrderString[ cbOrderString - 1 ] );
wsprintf( szOrderString + cbOrderString - 1, " %ld", (long) i );
}
lError = RegSetValueEx( hKeySettings,
TEXT( "Order" ),
0,
REG_SZ,
szOrderString,
1 + lstrlen( szOrderString ) );
ASSERT( ERROR_SUCCESS == lError );
if ( ERROR_SUCCESS != lError )
{
lsscError = LS_RESOURCES_UNAVAILABLE;
}
else
{
////////////////////////////////////
// Add provider to current list //
////////////////////////////////////
ok = LSXProviderListAddFromString( szProviderString );
ASSERT( ok );
if ( !ok )
{
// we loaded it before, but we can't load it now (?!)
lsscError = LS_RESOURCES_UNAVAILABLE;
}
else
{
// provider successfully installed
plspProvider = LSXProviderListGetByName( lspProvider.szProviderName );
ASSERT( NULL != plspProvider );
if ( NULL == plspProvider )
{
// we added the provider, but now we can't find it (?!)
lsscError = LS_RESOURCES_UNAVAILABLE;
}
else if ( NULL != plspProvider->pLSInstall )
{
// provider has its own installation procedure as well
lsscError = ( * ( plspProvider->pLSInstall ) )( ProviderPath );
}
else
{
// all done!
lsscError = LS_SUCCESS;
}
}
}
RegCloseKey( hKeySettings );
}
}
RegCloseKey( hKeyProviders );
}
LSXProviderListUnlock();
}
}
LSXProviderFree( &lspProvider );
}
return lsscError;
}
//////////////////////////////////////////////////////////////////////////////
// LOCAL IMPLEMENTATIONS //
/////////////////////////////
static BOOL
LSXProviderListAddAllFromKey( HKEY hLsapiKey )
/*++
Routine Description:
This routine is generally executed for both
HKEY_CURRENT_USER\Software\LSAPI
and
HKEY_LOCAL_MACHINE\Software\LSAPI
Each invocation will add the providers configured under the given
LSAPI key in the configured order.
The order in which the providers are added to the table is given by
the subkey SETTINGS\Order, a REG_SZ of the form "3 1 2", which
designates that PROVIDERS\Provider3 should be added first, then
PROVIDERS\Provider1, etc.
Each ProviderN value is a REG_SZ of the form
"DllPath;LSAPI version;provider comment"
e.g.,
"C:\WINNT35\SYSTEM32\MSLSP32.DLL;1.10;Microsoft License Server Client LSAPI License Provider"
The LSAPI version should be 1.10 (until the specification is revised).
Arguments:
hLsapiKey (HKEY)
Handle to the LSAPI key from which to add providers, usually a handle
to either
HKEY_CURRENT_USER\Software\LSAPI
or
HKEY_LOCAL_MACHINE\Software\LSAPI
Return Value:
(BOOL)
TRUE
The provider was successfully loaded and the LSAPI functions
are properly exported.
FALSE
Otherwise.
--*/
{
LONG lError;
DWORD dwValueType;
DWORD dwValueSize = 0;
LPTSTR pszOrderValue = NULL;
LPTSTR pszOrderPtr = NULL;
TCHAR szKeyName[ LS_MAX_PROVIDER_KEY_LENGTH ];
LPTSTR pszKeyPtr = NULL;
TCHAR szProviderValue[ LS_MAX_PROVIDER_VALUE_LENGTH ];
BOOL bAllLoaded = FALSE;
HKEY hSettingsKey;
HKEY hProvidersKey;
lError = RegOpenKeyEx( hLsapiKey,
TEXT( "Settings" ),
0,
KEY_READ,
&hSettingsKey );
if ( ERROR_SUCCESS == lError )
{
lError = RegOpenKeyEx( hLsapiKey,
TEXT( "Providers" ),
0,
KEY_READ,
&hProvidersKey );
if ( ERROR_SUCCESS == lError )
{
// check for existence, type, and length of ORDER value
lError = RegQueryValueEx( hSettingsKey,
TEXT( "Order" ),
NULL,
&dwValueType,
NULL,
&dwValueSize );
if ( ( ERROR_SUCCESS == lError ) && ( REG_SZ == dwValueType ) )
{
// ORDER value exists; retrieve it
pszOrderValue = LocalAlloc( LPTR, dwValueSize );
if ( NULL != pszOrderValue )
{
lError = RegQueryValueEx( hSettingsKey,
TEXT( "Order" ),
NULL,
&dwValueType,
pszOrderValue,
&dwValueSize );
if ( ERROR_SUCCESS == lError )
{
bAllLoaded = TRUE;
// skip leading white space
for ( pszOrderPtr = pszOrderValue; (TCHAR) ' ' == *pszOrderPtr; pszOrderPtr++ );
while ( *pszOrderPtr )
{
// create provider key name
lstrcpy( szKeyName, TEXT( "Provider" ) );
for ( pszKeyPtr = szKeyName + lstrlen( szKeyName );
( ( pszKeyPtr - szKeyName + 1 )
< ( sizeof( szKeyName ) / sizeof( TCHAR ) ) )
&& ( (TCHAR) '\0' != *pszOrderPtr )
&& ( (TCHAR) ' ' != *pszOrderPtr );
*( pszKeyPtr++ ) = *( pszOrderPtr++ ) );
*pszKeyPtr = (TCHAR) '\0';
// get provider value
dwValueSize = LS_MAX_PROVIDER_VALUE_LENGTH;
lError = RegQueryValueEx( hProvidersKey,
szKeyName,
NULL,
&dwValueType,
szProviderValue,
&dwValueSize );
if ( ( ERROR_SUCCESS == lError ) && ( REG_SZ == dwValueType ) )
{
// add provider to our list
if ( !LSXProviderListAddFromString( szProviderValue ) )
{
bAllLoaded = FALSE;
}
}
// skip trailing white space
for ( ; (TCHAR) ' ' == *pszOrderPtr; pszOrderPtr++ );
}
}
}
}
RegCloseKey( hProvidersKey );
}
RegCloseKey( hSettingsKey );
}
return bAllLoaded;
}
//////////////////////////////////////////////////////////////////////////////
static BOOL
LSXProviderListAddFromString( LPCTSTR pcszProviderValue )
/*++
Routine Description:
Adds a provider to the end of the provider table (at the end of the search
order).
The provider string value is of the form
"DllPath;LSAPI version;provider comment"
e.g.,
"C:\WINNT35\SYSTEM32\MSLSP32.DLL;1.10;Microsoft License Server Client LSAPI License Provider"
The LSAPI version should be 1.10 (until the specification is revised).
Arguments:
pcszProviderValue (LPCTSTR)
See above. The path need not be a full path -- if the full path is
absent, the DLL will be searched for in the same places and in the
same order as is dictated by the LoadLibrary() WIN32 API.
Return Value:
(BOOL)
TRUE
The provider was successfully loaded and added to the provider table.
FALSE
Otherwise.
--*/
{
LPCTSTR pcszValuePtr;
TCHAR szProviderPath[ LS_MAX_PROVIDER_VALUE_LENGTH ];
LPTSTR pszPathPtr;
BOOL bProviderInitialized;
LS_PROVIDER lspNewProvider;
// copy path from key value
for ( pszPathPtr = szProviderPath, pcszValuePtr = pcszProviderValue;
( (TCHAR) '\0' != *pcszValuePtr )
&& ( (TCHAR) ';' != *pcszValuePtr );
*( pszPathPtr++ ) = *( pcszValuePtr++ ) );
*pszPathPtr = (TCHAR) '\0';
// initialize new table entry
bProviderInitialized = LSXProviderInit( &lspNewProvider, szProviderPath );
if ( bProviderInitialized )
{
// provider is valid; allocate room in the table for it
if ( 0 == l_dwNumProviders )
{
l_plspProviderTable = LocalAlloc( LMEM_FIXED, sizeof( *l_plspProviderTable ) );
}
else
{
// ran into problems using:
// l_plspProviderTable = LocalReAlloc( l_plspProviderTable, sizeof( *l_plspProviderTable ) * ( l_dwNumProviders + 1 ), 0 );
// Kept getting error 8 (ERROR_NOT_ENOUGH_MEMORY); go figure
LPVOID pNewBlock;
pNewBlock = LocalAlloc( LMEM_FIXED, sizeof( *l_plspProviderTable ) * ( l_dwNumProviders + 1 ) );
if ( NULL != pNewBlock )
{
CopyMemory( pNewBlock, l_plspProviderTable, sizeof( *l_plspProviderTable ) * l_dwNumProviders );
}
LocalFree( l_plspProviderTable );
l_plspProviderTable = pNewBlock;
}
if ( NULL == l_plspProviderTable )
{
// memory allocation failed
l_dwNumProviders = 0;
}
else
{
// table successfully expanded; record new provider entry
MoveMemory( &l_plspProviderTable[ l_dwNumProviders ], &lspNewProvider, sizeof( lspNewProvider ) );
l_dwNumProviders++;
}
}
// return TRUE if the designated provider was successfully added
return bProviderInitialized && ( NULL != l_plspProviderTable );
}
//////////////////////////////////////////////////////////////////////////////
static BOOL
LSXProviderInit( PLS_PROVIDER plspProvider,
LPCTSTR pcszProviderPath )
/*++
Routine Description:
Initializes the given LS_PROVIDER structure with the LSAPI-compliant
license provider found at the given filename.
The initialization is considered successful if and only if the DLL can
be loaded and all LSAPI functions are properly exported.
Arguments:
plspProvider (PLS_PROVIDER)
Pointer to the provider structure to complete describing the given
provider DLL.
pcszProviderPath (LPCTSTR)
Path to the provider DLL. This path may be a filename only, in which
case the path searched will be the same as that for the LoadLibrary()
WIN32 API.
Return Value:
(BOOL)
TRUE
The provider was successfully loaded and the LSAPI functions
are properly exported.
FALSE
Otherwise.
--*/
{
FARPROC pExportedFn = NULL;
DWORD i;
ZeroMemory( plspProvider, sizeof( *plspProvider ) );
// load the provider DLL
plspProvider->hLibrary = LoadLibrary( pcszProviderPath );
if ( NULL != plspProvider->hLibrary )
{
// found the DLL; now ascertain pointers to the DLL's exported LSAPI
for ( i = 0; NULL != l_alpeiProviderExportTable[ i ].pcszExportName; i++ )
{
pExportedFn = GetProcAddress( plspProvider->hLibrary, l_alpeiProviderExportTable[ i ].pcszExportName );
if ( ( NULL != pExportedFn ) || !l_alpeiProviderExportTable[ i ].bExportRequired )
{
// okay
* ( (FARPROC *) ( ( (LPBYTE) plspProvider ) + l_alpeiProviderExportTable[ i ].dwProviderOffset) ) = pExportedFn;
}
else
{
// required function not found
break;
}
}
if ( NULL != l_alpeiProviderExportTable[ i ].pcszExportName )
{
// load stopped prematurely due to an error (required function not found)
FreeLibrary( plspProvider->hLibrary );
plspProvider->hLibrary = NULL;
}
else
{
// DLL and LSAPI loaded successfully; save off the name of the provider
(* ( plspProvider->pLSEnumProviders ) )( (LS_ULONG) 0,
(LS_STR *) plspProvider->szProviderName );
}
}
// return TRUE if successful
return ( NULL != plspProvider->hLibrary );
}
//////////////////////////////////////////////////////////////////////////////
static void
LSXProviderFree( PLS_PROVIDER plspProvider )
{
FreeLibrary( plspProvider->hLibrary );
}
//////////////////////////////////////////////////////////////////////////////
static LS_STATUS_CODE
LSXProviderListLock( void )
{
LS_STATUS_CODE lsscError;
DWORD dwError;
ASSERT( NULL != l_hProviderLock );
if ( NULL == l_hProviderLock )
{
lsscError = LS_RESOURCES_UNAVAILABLE;
}
else
{
dwError = WaitForSingleObject( l_hProviderLock, INFINITE );
ASSERT( WAIT_OBJECT_0 == dwError );
if ( WAIT_ABANDONED == dwError )
{
// previous owner died before releasing mutex
// we now own it, but we still need to lock it (?)
// TODO: check this assertion
dwError = WaitForSingleObject( l_hProviderLock, INFINITE );
ASSERT( WAIT_OBJECT_0 == dwError );
}
lsscError = ( WAIT_OBJECT_0 == dwError ) ? LS_SUCCESS
: LS_RESOURCES_UNAVAILABLE;
}
return lsscError;
}
//////////////////////////////////////////////////////////////////////////////
static LS_STATUS_CODE
LSXProviderListUnlock( void )
{
LS_STATUS_CODE lsscError;
BOOL ok;
ASSERT( NULL != l_hProviderLock );
if ( NULL == l_hProviderLock )
{
lsscError = LS_RESOURCES_UNAVAILABLE;
}
else
{
ok = ReleaseMutex( l_hProviderLock );
ASSERT( ok );
lsscError = ok ? LS_SUCCESS
: LS_RESOURCES_UNAVAILABLE;
}
return lsscError;
}