2025-11-28 00:35:46 +09:00

2649 lines
78 KiB
C++

/////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2003 <company name>
//
// Module Name:
// File Share Sample.cpp
//
// Description:
// Resource DLL for File Share Sample (File Share Sample).
//
// Author:
// <name> (<e-mail name>) Mmmm DD, 2003
//
// Revision History:
//
// Notes:
//
/////////////////////////////////////////////////////////////////////////////
#include "ClRes.h"
#include <lmerr.h>
#include <lmshare.h>
#include <LMAPIbuf.h>
//
// Type and constant definitions.
//
#define CLUSCTL_RESOURCE_FILESHARESAMPLE_CALL_ISALIVE CLUSCTL_USER_CODE( 1, CLUS_OBJECT_RESOURCE )
// ADDPARAM: Add new properties here.
#define PROP_NAME__SHARENAME L"ShareName"
#define PROP_NAME__PATH L"Path"
#define PROP_NAME__REMARK L"Remark"
#define PROP_NAME__MAXUSERS L"MaxUsers"
#define PROP_MIN__MAXUSERS (0)
#define PROP_MAX__MAXUSERS (4294967295)
#define PROP_DEFAULT__MAXUSERS (4294967295)
// ADDPARAM: Add new properties here.
typedef struct _FILESHARESAMPLE_PROPS
{
PWSTR pwszShareName;
PWSTR pwszPath;
PWSTR pwszRemark;
DWORD nMaxUsers;
} FILESHARESAMPLE_PROPS, * PFILESHARESAMPLE_PROPS;
typedef struct _FILESHARESAMPLE_RESOURCE
{
RESID resid; // For validation.
FILESHARESAMPLE_PROPS propsActive; // The active props. Used for program flow and control when the resource is online.
FILESHARESAMPLE_PROPS props; // The props in cluster DB. May differ from propsActive until OnlineThread reloads them as propsActive.
HCLUSTER hCluster;
HRESOURCE hResource;
HKEY hkeyParameters;
RESOURCE_HANDLE hResourceHandle;
LPWSTR pwszResourceName;
LPWSTR pwszComputerName; // The current node name.
LPWSTR pwszExpandedPath; // The expanded path (in case there were env. variables embedded).
LPWSTR pwszUNCSharedPath; // UNC path to our share - used to test for aliveness.
CLUS_WORKER cwWorkerThread;
CLUSTER_RESOURCE_STATE state;
BOOL fIsAliveFailed;
} FILESHARESAMPLE_RESOURCE, * PFILESHARESAMPLE_RESOURCE;
///////////////////////////////////////////////////////////////////////////////
// Global data.
///////////////////////////////////////////////////////////////////////////////
//
// Forward reference to our RESAPI function table.
//
extern CLRES_FUNCTION_TABLE g_FileShareSampleFunctionTable;
//
// File Share Sample resource read-write private properties.
//
RESUTIL_PROPERTY_ITEM
FileShareSampleResourcePrivateProperties[] =
{
{ PROP_NAME__SHARENAME, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, RESUTIL_PROPITEM_REQUIRED, FIELD_OFFSET( FILESHARESAMPLE_PROPS, pwszShareName ) },
{ PROP_NAME__PATH, NULL, CLUSPROP_FORMAT_EXPAND_SZ, 0, 0, 0, RESUTIL_PROPITEM_REQUIRED, FIELD_OFFSET( FILESHARESAMPLE_PROPS, pwszPath ) },
{ PROP_NAME__REMARK, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, 0, FIELD_OFFSET( FILESHARESAMPLE_PROPS, pwszRemark ) },
{ PROP_NAME__MAXUSERS, NULL, CLUSPROP_FORMAT_DWORD, PROP_DEFAULT__MAXUSERS, PROP_MIN__MAXUSERS, PROP_MAX__MAXUSERS, 0, FIELD_OFFSET( FILESHARESAMPLE_PROPS, nMaxUsers ) },
{ 0 }
};
//
// Function prototypes.
//
RESID WINAPI
FileShareSampleOpen(
LPCWSTR pwszResourceNameIn
, HKEY hkeyResourceKeyIn
, RESOURCE_HANDLE hResourceHandleIn
);
void WINAPI
FileShareSampleClose(
RESID residIn
);
DWORD WINAPI
FileShareSampleOnline(
RESID residIn
, PHANDLE phEventHandleInout
);
DWORD WINAPI
FileShareSampleOnlineThread(
PCLUS_WORKER pWorkerIn
, PFILESHARESAMPLE_RESOURCE pResourceEntryIn
);
DWORD WINAPI
FileShareSampleOffline(
RESID residIn
);
DWORD WINAPI
FileShareSampleOfflineThread(
PCLUS_WORKER pWorkerIn
, PFILESHARESAMPLE_RESOURCE pResourceEntryIn
);
void WINAPI
FileShareSampleTerminate(
RESID residIn
);
BOOL WINAPI
FileShareSampleLooksAlive(
RESID residIn
);
BOOL WINAPI
FileShareSampleIsAlive(
RESID residIn
);
BOOL
FileShareSampleCheckIsAlive(
PFILESHARESAMPLE_RESOURCE pResourceEntryIn
, BOOL fFullCheckIn
);
DWORD WINAPI
FileShareSampleResourceControl(
RESID residIn
, DWORD nControlCodeIn
, PVOID pInBufferIn
, DWORD cbInBufferSizeIn
, PVOID pOutBufferOut
, DWORD cbOutBufferSizeIn
, LPDWORD pcbBytesReturnedOut
);
DWORD WINAPI
FileShareSampleResourceTypeControl(
LPCWSTR pwszResourceTypeNameIn
, DWORD nControlCodeIn
, PVOID pInBufferIn
, DWORD cbInBufferSizeIn
, PVOID pOutBufferOut
, DWORD cbOutBufferSizeIn
, LPDWORD pcbBytesReturnedOut
);
DWORD
FileShareSampleGetPrivateResProperties(
PFILESHARESAMPLE_RESOURCE pResourceEntryIn
, PVOID pOutBufferOut
, DWORD cbOutBufferSizeIn
, LPDWORD pcbBytesReturnedOut
);
DWORD
FileShareSampleValidatePrivateResProperties(
PFILESHARESAMPLE_RESOURCE pResourceEntryIn
, const PVOID pInBufferIn
, DWORD cbInBufferSizeIn
, PFILESHARESAMPLE_PROPS pPropsOut
);
DWORD
FileShareSampleSetPrivateResProperties(
PFILESHARESAMPLE_RESOURCE pResourceEntryIn
, const PVOID pInBufferIn
, DWORD cbInBufferSizeIn
);
DWORD
FileShareSampleSetNameHandler(
PFILESHARESAMPLE_RESOURCE pResourceEntryIn
, LPWSTR pwszNameIn
);
/////////////////////////////////////////////////////////////////////////////
//++
//
// FileShareSampleDllMain
//
// Description:
// Main DLL entry point for the File Share Sample resource type.
//
// Arguments:
// hDllHandleIn
// DLL instance handle.
//
// nReasonIn
// Reason for being called.
//
// ReservedIn
// Reserved argument.
//
// Return Value:
// TRUE
// Success.
//
// FALSE
// Failure.
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL WINAPI
FileShareSampleDllMain(
HINSTANCE hDllHandleIn
, DWORD nReasonIn
, LPVOID ReservedIn
)
{
BOOL fSuccess = TRUE;
UNREFERENCED_PARAMETER( hDllHandleIn );
UNREFERENCED_PARAMETER( ReservedIn );
switch ( nReasonIn )
{
case DLL_PROCESS_ATTACH:
break;
case DLL_PROCESS_DETACH:
break;
} // switch: nReason
return fSuccess;
} //*** FileShareSampleDllMain
/////////////////////////////////////////////////////////////////////////////
//++
//
// FileShareSampleStartup
//
// Description:
// Startup the resource DLL for the File Share Sample resource type.
// This routine verifies that at least one currently supported version
// of the resource DLL is between nMinVersionSupported and
// nMaxVersionSupported. If not, then the resource DLL should return
// ERROR_REVISION_MISMATCH.
//
// If more than one version of the resource DLL interface is supported
// by the resource DLL, then the highest version (up to
// nMaxVersionSupported) should be returned as the resource DLL's
// interface. If the returned version is not within range, then startup
// fails.
//
// The Resource Type is passed in so that if the resource DLL supports
// more than one Resource Type, it can pass back the correct function
// table associated with the Resource Type.
//
// Arguments:
// pwszResourceTypeIn
// Type of resource requesting a function table.
//
// nMinVersionSupportedIn
// Minimum resource DLL interface version supported by the cluster
// software.
//
// nMaxVersionSupportedIn
// Maximum resource DLL interface version supported by the cluster
// software.
//
// pfnSetResourceStatusIn
// Pointer to a routine that the resource DLL should call to update
// the state of a resource after the Online or Offline routine
// have returned a status of ERROR_IO_PENDING.
//
// pfnLogEventIn
// Pointer to a routine that handles the reporting of events from
// the resource DLL.
//
// pFunctionTableIn
// Returns a pointer to the function table defined for the version
// of the resource DLL interface returned by the resource DLL.
//
// Return Value:
// ERROR_SUCCESS
// The operation was successful.
//
// ERROR_CLUSTER_RESNAME_NOT_FOUND
// The resource type name is unknown by this DLL.
//
// ERROR_REVISION_MISMATCH
// The version of the cluster service doesn't match the version of
// the DLL.
//
// Win32 error code
// The operation failed.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD WINAPI
FileShareSampleStartup(
LPCWSTR pwszResourceTypeIn
, DWORD nMinVersionSupportedIn
, DWORD nMaxVersionSupportedIn
, PSET_RESOURCE_STATUS_ROUTINE pfnSetResourceStatusIn
, PLOG_EVENT_ROUTINE pfnLogEventIn
, PCLRES_FUNCTION_TABLE * pFunctionTableOut
)
{
DWORD sc = ERROR_SUCCESS;
// These are stored in the calling DllMain.
UNREFERENCED_PARAMETER( pfnSetResourceStatusIn );
UNREFERENCED_PARAMETER( pfnLogEventIn );
if ( (nMinVersionSupportedIn > CLRES_VERSION_V1_00)
|| (nMaxVersionSupportedIn < CLRES_VERSION_V1_00) )
{
sc = ERROR_REVISION_MISMATCH;
} // if: version not supported
else if ( 0 == CompareStringW(
LOCALE_SYSTEM_DEFAULT,
NORM_IGNORECASE,
pwszResourceTypeIn,
-1,
RESTYPE_NAME,
-1
)
)
{
*pFunctionTableOut = &g_FileShareSampleFunctionTable;
sc = ERROR_SUCCESS;
} // else if: we support this type of resource
else
{
//
// We don't support this resource type.
//
sc = ERROR_CLUSTER_RESNAME_NOT_FOUND;
} // else: resource type name not supported
return sc;
} //*** FileShareSampleStartup
/////////////////////////////////////////////////////////////////////////////
//++
//
// FileShareSampleOpen
//
// Description:
// Open routine for File Share Sample resources.
//
// Open the specified resource (create an instance of the resource).
// Allocate all structures necessary to bring the specified resource
// online.
//
// Arguments:
// pwszResourceNameIn
// Supplies the name of the resource to open.
//
// hkeyResourceKeyIn
// Supplies handle to the resource's cluster database key.
//
// hResourceHandleIn
// A handle that is passed back to the Resource Monitor when the
// SetResourceStatus or LogEvent method is called. See the
// description of the pfnSetResourceStatus and pfnLogEvent arguments
// to the FileShareSampleStartup routine. This handle should never be
// closed or used for any purpose other than passing it as an
// argument back to the Resource Monitor in the SetResourceStatus or
// LogEvent callbacks.
//
// Return Value:
// resid
// RESID of opened resource.
//
// NULL
// Error occurred opening the resource. Resource Monitor may call
// GetLastError() to get more details on the error.
//
//--
/////////////////////////////////////////////////////////////////////////////
RESID WINAPI
FileShareSampleOpen(
LPCWSTR pwszResourceNameIn
, HKEY hkeyResourceKeyIn
, RESOURCE_HANDLE hResourceHandleIn
)
{
DWORD sc = ERROR_SUCCESS;
size_t cchBuffer = 0;
DWORD cchComputerName = 0;
RESID resid = NULL;
HKEY hkeyParameters = NULL;
PFILESHARESAMPLE_RESOURCE pResourceEntry = NULL;
HRESULT hr = S_OK;
//
// Open the Parameters registry key for this resource.
//
sc = ClusterRegOpenKey(
hkeyResourceKeyIn
, L"Parameters"
, KEY_ALL_ACCESS
, &hkeyParameters
);
if ( sc != ERROR_SUCCESS )
{
(g_pfnLogEvent)(
hResourceHandleIn
, LOG_ERROR
, L"Open: Unable to open Parameters key. Error: %1!u!.\n"
, sc
);
goto Cleanup;
} // if: error creating the Parameters key for the resource
//
// Allocate a resource entry.
//
pResourceEntry = new FILESHARESAMPLE_RESOURCE;
if ( pResourceEntry == NULL )
{
sc = GetLastError();
(g_pfnLogEvent)(
hResourceHandleIn
, LOG_ERROR
, L"Open: Unable to allocate resource entry structure. Error: %1!u!.\n"
, sc
);
goto Cleanup;
} // if: error allocating memory for the resource
//
// Initialize the resource entry..
//
ZeroMemory( pResourceEntry, sizeof( *pResourceEntry ) );
pResourceEntry->resid = static_cast< RESID >( pResourceEntry ); // for validation
pResourceEntry->hResourceHandle = hResourceHandleIn;
pResourceEntry->hkeyParameters = hkeyParameters;
pResourceEntry->state = ClusterResourceOffline;
//
// Save the name of the resource.
//
cchBuffer = lstrlenW( pwszResourceNameIn ) + 1;
pResourceEntry->pwszResourceName = new WCHAR[ cchBuffer ];
if ( pResourceEntry->pwszResourceName == NULL )
{
sc = GetLastError();
(g_pfnLogEvent)(
hResourceHandleIn
, LOG_ERROR
, L"Open: Unable to allocate the resource name buffer. Error: %1!u!.\n"
, sc
);
goto Cleanup;
} // if: error allocating memory for the name.
hr = StringCchCopyW( pResourceEntry->pwszResourceName, cchBuffer, pwszResourceNameIn );
if ( FAILED( hr ) )
{
sc = HRESULT_CODE( hr );
(g_pfnLogEvent)(
hResourceHandleIn
, LOG_ERROR
, L"Open: Unable to allocate the resource name buffer. Error: %1!u!.\n"
, sc
);
goto Cleanup;
} // if:
//
// Open the cluster.
//
pResourceEntry->hCluster = OpenCluster( NULL );
if ( pResourceEntry->hCluster == NULL )
{
sc = GetLastError();
(g_pfnLogEvent)(
hResourceHandleIn
, LOG_ERROR
, L"Open: Unable to open the cluster. Error: %1!u!.\n"
, sc
);
goto Cleanup;
} // if: error opening the cluster
//
// Open the resource.
//
pResourceEntry->hResource = OpenClusterResource( pResourceEntry->hCluster, pwszResourceNameIn );
if ( pResourceEntry->hResource == NULL )
{
sc = GetLastError();
(g_pfnLogEvent)(
hResourceHandleIn
, LOG_ERROR
, L"Open: Unable to open the resource. Error: %1!u!.\n"
, sc
);
goto Cleanup;
} // if: error opening the resource
//
// Startup for the resource.
//
//
// Retrieve the computer name.
//
cchComputerName = 0;
GetComputerNameEx( ComputerNamePhysicalDnsHostname, NULL, &cchComputerName );
sc = GetLastError();
if ( sc != ERROR_MORE_DATA )
{
//
// Something went wrong, we should've gotten ERROR_MORE_DATA.
//
(g_pfnLogEvent)(
hResourceHandleIn
, LOG_ERROR
, L"Open: failed to retrieve the computer name with error %1!u!.\n"
, sc
);
goto Cleanup;
} // if: GetComputerNameEx failed
pResourceEntry->pwszComputerName = new WCHAR[ cchComputerName ];
if ( pResourceEntry->pwszComputerName == NULL )
{
sc = ERROR_OUTOFMEMORY;
(g_pfnLogEvent)(
hResourceHandleIn
, LOG_ERROR
, L"Open: failed to allocate computer name buffer with error %1!u!.\n"
, sc
);
goto Cleanup;
} // if: buffer allocation failed
if ( GetComputerNameEx(
ComputerNamePhysicalDnsHostname
, pResourceEntry->pwszComputerName
, &cchComputerName
) == 0 )
{
sc = GetLastError();
(g_pfnLogEvent)(
hResourceHandleIn
, LOG_ERROR
, L"Open: failed to retrieve the computer name with error %1!u!.\n"
, sc
);
goto Cleanup;
} // if: GetComputerNameEx failed
resid = static_cast< RESID >( pResourceEntry );
sc = ERROR_SUCCESS;
Cleanup:
if ( resid == NULL )
{
assert( sc != ERROR_SUCCESS );
(g_pfnLogEvent)(
hResourceHandleIn
, LOG_ERROR
, L"Open: failed with error %1!u!.\n"
, sc
);
if ( hkeyParameters != NULL )
{
ClusterRegCloseKey( hkeyParameters );
} // if:
if ( pResourceEntry != NULL )
{
if ( pResourceEntry->hResource != NULL )
{
CloseClusterResource( pResourceEntry->hResource );
} // if:
if ( pResourceEntry->hCluster != NULL )
{
CloseCluster( pResourceEntry->hCluster );
} // if:
delete [] pResourceEntry->pwszResourceName;
delete [] pResourceEntry->pwszComputerName;
delete pResourceEntry;
} // if: resource entry allocated
} // if: something failed
SetLastError( sc );
return resid;
} //*** FileShareSampleOpen
/////////////////////////////////////////////////////////////////////////////
//++
//
// FileShareSampleClose
//
// Description:
// Close routine for File Share Sample resources.
//
// Close the specified resource and deallocate all structures, etc.,
// allocated in the Open call. If the resource is not in the offline
// state, then the resource should be taken offline (by calling
// Terminate) before the close operation is performed.
//
// Arguments:
// residIn
// Supplies the resource ID of the resource to close.
//
// Return Value:
// None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void WINAPI
FileShareSampleClose(
RESID residIn
)
{
PFILESHARESAMPLE_RESOURCE pResourceEntry = NULL;
DWORD sc = ERROR_SUCCESS;
//
// Verify we have a valid resource ID.
//
pResourceEntry = static_cast< PFILESHARESAMPLE_RESOURCE >( residIn );
if ( pResourceEntry == NULL )
{
DBG_PRINT( "FileShareSample: Close request for a nonexistent resource id.\n" );
sc = ERROR_RESOURCE_NOT_FOUND;
goto Cleanup;
} // if: NULL resource ID
if ( pResourceEntry->resid != residIn )
{
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_ERROR
, L"Close resource sanity check failed! resid = 0x%1!08x!.\n"
, residIn
);
sc = ERROR_RESOURCE_NOT_FOUND;
goto Cleanup;
} // if: invalid resource ID
if ( pResourceEntry->pwszResourceName == NULL )
{
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_INFORMATION
, L"Close request for resource with resid 0x%1!08x!.\n"
, residIn
);
} // if: resource name is null...
else
{
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_INFORMATION
, L"Close request for resource '%1!s!'.\n"
, pResourceEntry->pwszResourceName
);
} // else: resource name is not null...
//
// Close the Parameters key.
//
if ( pResourceEntry->hkeyParameters != NULL )
{
ClusterRegCloseKey( pResourceEntry->hkeyParameters );
pResourceEntry->hkeyParameters = NULL;
} // if: parameters key is open
//
// Close the cluster handle.
//
if ( pResourceEntry->hCluster != NULL )
{
CloseCluster( pResourceEntry->hCluster );
pResourceEntry->hCluster = NULL;
} // if: cluster handle is open
//
// Close the resource handle.
//
if ( pResourceEntry->hResource != NULL )
{
CloseClusterResource( pResourceEntry->hResource );
pResourceEntry->hResource = NULL;
} // if: resource handle is open
// ADDPARAM: Add new properties here.
//
// The ResUtil API's use LocalAlloc, so use LocalFree on the prop list members.
//
LocalFree( pResourceEntry->propsActive.pwszShareName );
LocalFree( pResourceEntry->props.pwszShareName );
LocalFree( pResourceEntry->propsActive.pwszPath );
LocalFree( pResourceEntry->props.pwszPath );
LocalFree( pResourceEntry->propsActive.pwszRemark );
LocalFree( pResourceEntry->props.pwszRemark );
LocalFree( pResourceEntry->pwszExpandedPath );
//
// Deallocate the resource entry.
//
delete [] pResourceEntry->pwszResourceName;
delete [] pResourceEntry->pwszComputerName;
delete [] pResourceEntry->pwszUNCSharedPath;
delete pResourceEntry;
sc = ERROR_SUCCESS;
Cleanup:
SetLastError( sc );
return;
} //*** FileShareSampleClose
/////////////////////////////////////////////////////////////////////////////
//++
//
// FileShareSampleOnline
//
// Description:
// Online routine for File Share Sample resources.
//
// Bring the specified resource online (available for use). The resource
// DLL should attempt to arbitrate for the resource if it is present on
// a shared medium, like a shared SCSI bus.
//
// Arguments:
// residIn
// Supplies the resource ID of the resource to be brought online
// (available for use).
//
// phEventHandleOut
// Returns a signalable handle that is signaled when the resource DLL
// detects a failure on the resource. This argument derferences as NULL
// on input, and the resource DLL returns NULL if asynchronous
// notification of failurs is not supported. Otherwise this must be
// the address of a handle that is signaled on resource failures.
//
// Return Value:
// ERROR_SUCCESS
// The operation was successful, and the resource is now online.
//
// ERROR_RESOURCE_NOT_FOUND
// Resource ID is not valid.
//
// ERROR_RESOURCE_NOT_AVAILABLE
// If the resource was arbitrated with some other systems and one of
// the other systems won the arbitration.
//
// ERROR_IO_PENDING
// The request is pending. A thread has been activated to process
// the online request. The thread that is processing the online
// request will periodically report status by calling the
// SetResourceStatus callback method until the resource is placed
// into the ClusterResourceOnline state (or the resource monitor
// decides to timeout the online request and Terminate the resource.
// This pending timeout value is settable and has a default value of
// 3 minutes.).
//
// Win32 error code
// The operation failed.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD WINAPI
FileShareSampleOnline(
RESID residIn
, PHANDLE phEventHandleOut
)
{
PFILESHARESAMPLE_RESOURCE pResourceEntry = NULL;
DWORD sc = ERROR_SUCCESS;
UNREFERENCED_PARAMETER( phEventHandleOut );
//
// Verify we have a valid resource ID.
//
pResourceEntry = static_cast< PFILESHARESAMPLE_RESOURCE >( residIn );
if ( pResourceEntry == NULL )
{
DBG_PRINT( "File Share Sample: Online request for a nonexistent resource id.\n" );
return ERROR_RESOURCE_NOT_FOUND;
} // if: NULL resource ID
if ( pResourceEntry->resid != residIn )
{
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_ERROR
, L"Online sanity check failed! resid = 0x%1!08x!.\n"
, residIn
);
return ERROR_RESOURCE_NOT_FOUND;
} // if: invalid resource ID
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_INFORMATION
, L"Online request.\n"
);
//
// Start the Online thread to perform the online operation.
//
pResourceEntry->state = ClusterResourceOnlinePending;
ClusWorkerTerminate( &pResourceEntry->cwWorkerThread );
sc = ClusWorkerCreate(
&pResourceEntry->cwWorkerThread
, reinterpret_cast< PWORKER_START_ROUTINE >( FileShareSampleOnlineThread )
, pResourceEntry
);
if ( sc != ERROR_SUCCESS )
{
pResourceEntry->state = ClusterResourceFailed;
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_ERROR
, L"Online: Unable to start thread. Error: %1!u!.\n"
, sc
);
} // if: error creating the worker thread
else
{
sc = ERROR_IO_PENDING;
} // if: worker thread created successfully
return sc;
} //*** FileShareSampleOnline
/////////////////////////////////////////////////////////////////////////////
//++
//
// FileShareSampleOnlineThread
//
// Description:
// Worker function which brings a resource online.
// This function is executed in a separate thread.
//
// Arguments:
// pWorkerIn
// Supplies the worker thread structure.
//
// pResourceEntryIn
// A pointer to the FILESHARESAMPLE_RESOURCE block for this resource.
//
// Return Value:
// ERROR_SUCCESS
// The operation completed successfully.
//
// Win32 error code
// The operation failed.
//
// Notes:
// When using properties in this routine it is recommended that you
// use the properties in propsActive of the FILESHARESAMPLE_RESOURCE struct
// instead of the properties in props. The primary reason you should
// use propsActive is that the properties in props could be changed by
// the SetPrivateResProperties() routine. Using propsActive allows
// the online state of the resource to be steady while still allowing
// an administrator to change the stored value of the properties.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD WINAPI
FileShareSampleOnlineThread(
PCLUS_WORKER pWorkerIn
, PFILESHARESAMPLE_RESOURCE pResourceEntryIn
)
{
RESOURCE_STATUS resourceStatus;
DWORD sc = ERROR_SUCCESS;
size_t cch = 0;
LPWSTR pwszNameOfPropInError = NULL;
SHARE_INFO_2 shareInfo;
HRESULT hr = S_OK;
ResUtilInitializeResourceStatus( &resourceStatus );
resourceStatus.ResourceState = ClusterResourceFailed;
resourceStatus.WaitHint = 0;
resourceStatus.CheckPoint = 1;
//
// Parameter checking.
//
if ( pResourceEntryIn == NULL )
{
sc = ERROR_INVALID_PARAMETER;
goto Cleanup;
} // if:
//
// Read properties.
//
sc = ResUtilGetPropertiesToParameterBlock(
pResourceEntryIn->hkeyParameters
, FileShareSampleResourcePrivateProperties
, reinterpret_cast< LPBYTE >( &pResourceEntryIn->propsActive )
, TRUE // CheckForRequiredProperties
, &pwszNameOfPropInError
);
if ( sc != ERROR_SUCCESS )
{
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"OnlineThread: Unable to read the '%1!s!' property. Error: %2!u!.\n"
, (pwszNameOfPropInError == NULL ? L"" : pwszNameOfPropInError)
, sc
);
goto Cleanup;
} // if: error getting properties
//
// Start any services that we depend on.
// The call to ClusWorkerCheckTerminate checks to see if this resource
// has been told to stop the online process, in which case we should
// not start the service.
//
if ( ClusWorkerCheckTerminate( pWorkerIn ) == TRUE )
{
goto Cleanup;
} // if: terminating
sc = ResUtilStartResourceService( FILESHARESAMPLE_SVCNAME, NULL );
if ( sc == ERROR_SERVICE_ALREADY_RUNNING )
{
sc = ERROR_SUCCESS;
} // if: service was already started
else if ( sc != ERROR_SUCCESS )
{
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"OnlineThread: Failed to bring required service '%1!s!' online. Error: %2!u!.\n"
, FILESHARESAMPLE_SVCNAME
, sc
);
goto Cleanup;
} // else if: error starting the service
//
// TODO: Add code to start the resource.
//
if ( ClusWorkerCheckTerminate( pWorkerIn ) == TRUE )
{
goto Cleanup;
} // if: terminating
//
// The path may contain environment variables so we need to expand them.
//
delete [] pResourceEntryIn->pwszUNCSharedPath;
pResourceEntryIn->pwszUNCSharedPath = NULL;
LocalFree( pResourceEntryIn->pwszExpandedPath );
pResourceEntryIn->pwszExpandedPath = ResUtilExpandEnvironmentStrings( pResourceEntryIn->propsActive.pwszPath );
if ( pResourceEntryIn->pwszExpandedPath == NULL )
{
sc = GetLastError();
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"OnlineThread: Error %1!u! expanding path '%2!ws!' for share '%3!ws!'.\n"
, sc
, pResourceEntryIn->propsActive.pwszPath
, pResourceEntryIn->propsActive.pwszShareName
);
goto Cleanup;
} // if:
//
// Make sure the path doesn't end in a trailing backslash or it will fail to come online.
// Accept paths such as E:\ though.
//
cch = wcslen( pResourceEntryIn->pwszExpandedPath );
if ( ( cch > 3 ) && (pResourceEntryIn->pwszExpandedPath[ cch - 1 ] == L'\\' ) )
{
pResourceEntryIn->pwszExpandedPath[ cch - 1 ] = L'\0';
} // if:
//
// Initialize the shareInfo struct and create the share.
//
ZeroMemory( &shareInfo, sizeof( shareInfo ) );
shareInfo.shi2_netname = pResourceEntryIn->propsActive.pwszShareName;
shareInfo.shi2_type = STYPE_DISKTREE;
shareInfo.shi2_remark = pResourceEntryIn->propsActive.pwszRemark;
shareInfo.shi2_max_uses = pResourceEntryIn->propsActive.nMaxUsers;
shareInfo.shi2_path = pResourceEntryIn->pwszExpandedPath;
sc = NetShareAdd( NULL, 2, reinterpret_cast< PBYTE >( &shareInfo ), NULL );
if ( sc != NERR_Success )
{
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"OnlineThread: Error %1!u! creating share '%2!ws!' for path '%3!ws!'.\n"
, sc
, pResourceEntryIn->propsActive.pwszShareName
, pResourceEntryIn->pwszExpandedPath
);
goto Cleanup;
} // if: error creating share
//
// Calculate the UNC path in order to test for aliveness. e.g. \\node1\myshare
//
// Figure out how big of a buffer we need and allocate it.
// The magic "8" comes from this: - \\x\y\*.* - excluding the x & y and adding the null.
cch = 8 + wcslen( pResourceEntryIn->pwszComputerName ) + wcslen( pResourceEntryIn->propsActive.pwszShareName );
pResourceEntryIn->pwszUNCSharedPath = new WCHAR[ cch ];
if ( pResourceEntryIn->pwszUNCSharedPath == NULL )
{
sc = ERROR_OUTOFMEMORY;
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"OnlineThread: Error %1!u! allocating memory for local path.\n"
, sc
);
goto Cleanup;
} // if:
// Format the path into pwszUNCSharedPath.
hr = StringCchPrintf(
pResourceEntryIn->pwszUNCSharedPath
, cch
, L"\\\\%ws\\%ws\\*.*"
, pResourceEntryIn->pwszComputerName
, pResourceEntryIn->propsActive.pwszShareName
);
if ( FAILED( hr ) )
{
sc = SCODE( hr );
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"OnlineThread: Error %1!u! formatting share path from computer name '%2!s!' and share name '%3!s!'.\n"
, sc
, pResourceEntryIn->pwszComputerName
, pResourceEntryIn->propsActive.pwszShareName
);
goto Cleanup;
} // if:
sc = ERROR_SUCCESS;
resourceStatus.ResourceState = ClusterResourceOnline;
Cleanup:
if ( sc != ERROR_SUCCESS )
{
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"OnlineThread: Error %1!u! bringing resource online.\n"
, sc
);
} // if: error occurred
pResourceEntryIn->state = resourceStatus.ResourceState;
g_pfnSetResourceStatus( pResourceEntryIn->hResourceHandle, &resourceStatus );
return sc;
} //*** FileShareSampleOnlineThread
/////////////////////////////////////////////////////////////////////////////
//++
//
// FileShareSampleOffline
//
// Description:
// Offline routine for File Share Sample resources.
//
// Take the specified resource offline (unavailable for use). Wait
// for any cleanup operations to complete before returning.
//
// Arguments:
// residIn
// Supplies the resource ID of the resource to be shutdown
// gracefully.
//
// Return Value:
// ERROR_SUCCESS
// The operation was successful, and the resource is now offline.
//
// ERROR_RESOURCE_NOT_FOUND
// Resource ID is not valid.
//
// ERROR_RESOURCE_NOT_AVAILABLE
// If the resource was arbitrated with some other systems and one of
// the other systems won the arbitration.
//
// ERROR_IO_PENDING
// The request is still pending. A thread has been activated to
// process the offline request. The thread that is processing the
// offline request will periodically report status by calling the
// SetResourceStatus callback method until the resource is placed
// into the ClusterResourceOffline state (or the resource monitor
// decides to timeout the offline request and Terminate the
// resource).
//
// Win32 error code
// The operation failed. This will cause the Resource Monitor to
// log an event and call the Terminate routine.
//
// Notes:
// When using properties in this routine it is recommended that you
// use the properties in propsActive of the FILESHARESAMPLE_RESOURCE struct
// instead of the properties in props. The primary reason you should
// use propsActive is that the properties in props could be changed by
// the SetPrivateResProperties() routine. Using propsActive allows
// the online state of the resource to be steady while still allowing
// an administrator to change the stored value of the properties.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD WINAPI
FileShareSampleOffline(
RESID residIn
)
{
PFILESHARESAMPLE_RESOURCE pResourceEntry = NULL;
DWORD sc = ERROR_SUCCESS;
//
// Verify we have a valid resource ID.
//
pResourceEntry = static_cast< PFILESHARESAMPLE_RESOURCE >( residIn );
if ( pResourceEntry == NULL )
{
DBG_PRINT( "File Share Sample: Offline request for a nonexistent resource id.\n" );
sc = ERROR_RESOURCE_NOT_FOUND;
goto Cleanup;
} // if: NULL resource ID
if ( pResourceEntry->resid != residIn )
{
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_ERROR
, L"Offline resource sanity check failed! resid = 0x%1!08x!.\n"
, residIn
);
sc = ERROR_RESOURCE_NOT_FOUND;
goto Cleanup;
} // if: invalid resource ID
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_INFORMATION
, L"Offline request.\n"
);
//
// Start the Offline thread to perform the offline operation.
//
pResourceEntry->state = ClusterResourceOfflinePending;
ClusWorkerTerminate( &pResourceEntry->cwWorkerThread );
sc = ClusWorkerCreate(
&pResourceEntry->cwWorkerThread
, reinterpret_cast< PWORKER_START_ROUTINE >( FileShareSampleOfflineThread )
, pResourceEntry
);
if ( sc != ERROR_SUCCESS )
{
pResourceEntry->state = ClusterResourceFailed;
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_ERROR
, L"Offline: Unable to start thread. Error: %1!u!.\n"
, sc
);
} // if: error creating the worker thread
else
{
sc = ERROR_IO_PENDING;
} // if: worker thread created successfully
Cleanup:
return sc;
} //*** FileShareSampleOffline
/////////////////////////////////////////////////////////////////////////////
//++
//
// FileShareSampleOfflineThread
//
// Description:
// Worker function which takes a resource offline.
// This function is executed in a separate thread.
//
// Arguments:
// pWorkerIn
// Supplies the worker thread structure.
//
// pResourceEntryIn
// A pointer to the FILESHARESAMPLE_RESOURCE block for this resource.
//
// Return Value:
// ERROR_SUCCESS
// The operation completed successfully.
//
// Win32 error code
// The operation failed.
//
// Notes:
// When using properties in this routine it is recommended that you
// use the properties in propsActive of the FILESHARESAMPLE_RESOURCE struct
// instead of the properties in props. The primary reason you should
// use propsActive is that the properties in props could be changed by
// the SetPrivateResProperties() routine. Using propsActive allows
// the online state of the resource to be steady while still allowing
// an administrator to change the stored value of the properties.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD WINAPI
FileShareSampleOfflineThread(
PCLUS_WORKER pWorkerIn
, PFILESHARESAMPLE_RESOURCE pResourceEntryIn
)
{
RESOURCE_STATUS resourceStatus;
DWORD sc = ERROR_SUCCESS;
ResUtilInitializeResourceStatus( &resourceStatus );
resourceStatus.ResourceState = ClusterResourceFailed;
resourceStatus.WaitHint = 0;
resourceStatus.CheckPoint = 1;
//
// Take the resource offline.
//
//
// The call to ClusWorkerCheckTerminate checks to see if this
// resource has been terminated or not.
//
if ( ClusWorkerCheckTerminate( pWorkerIn ) == TRUE )
{
goto Cleanup;
} // if: resource terminated
sc = NetShareDel( NULL, pResourceEntryIn->propsActive.pwszShareName, 0 );
if ( (sc != NERR_Success) && (sc != NERR_NetNameNotFound) )
{
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"OfflineThread: Error %1!u! taking share '%2!s!' offline.\n"
, sc
, pResourceEntryIn->propsActive.pwszShareName
);
goto Cleanup;
} // if: error taking the resource offline
sc = ERROR_SUCCESS;
resourceStatus.ResourceState = ClusterResourceOffline;
Cleanup:
pResourceEntryIn->state = resourceStatus.ResourceState;
g_pfnSetResourceStatus( pResourceEntryIn->hResourceHandle, &resourceStatus );
return sc;
} //*** FileShareSampleOfflineThread
/////////////////////////////////////////////////////////////////////////////
//++
//
// FileShareSampleTerminate
//
// Description:
// Terminate routine for File Share Sample resources.
//
// Take the specified resource offline immediately (the resource is
// unavailable for use).
//
// Arguments:
// residIn
// Supplies the resource ID of the resource to be shutdown
// ungracefully.
//
// Return Value:
// None.
//
// Notes:
// When using properties in this routine it is recommended that you
// use the properties in propsActive of the FILESHARESAMPLE_RESOURCE struct
// instead of the properties in props. The primary reason you should
// use propsActive is that the properties in props could be changed by
// the SetPrivateResProperties() routine. Using propsActive allows
// the online state of the resource to be steady while still allowing
// an administrator to change the stored value of the properties.
//
//--
/////////////////////////////////////////////////////////////////////////////
void WINAPI
FileShareSampleTerminate(
RESID residIn
)
{
DWORD sc = ERROR_SUCCESS;
PFILESHARESAMPLE_RESOURCE pResourceEntry = NULL;
//
// Verify we have a valid resource ID.
//
pResourceEntry = static_cast< PFILESHARESAMPLE_RESOURCE >( residIn );
if ( pResourceEntry == NULL )
{
DBG_PRINT( "File Share Sample: Terminate request for a nonexistent resource id.\n" );
return;
} // if: NULL resource ID
if ( pResourceEntry->resid != residIn )
{
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_ERROR
, L"Terminate resource sanity check failed! resid = 0x%1!08x!.\n"
, residIn
);
return;
} // if: invalid resource ID
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_INFORMATION
, L"Terminate request.\n"
);
//
// Kill off any pending threads.
//
ClusWorkerTerminate( &pResourceEntry->cwWorkerThread );
//
// Terminate the resource.
//
sc = NetShareDel( NULL, pResourceEntry->propsActive.pwszShareName, 0 );
if ( sc != NERR_Success && sc != NERR_NetNameNotFound )
{
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_ERROR
, L"Terminate: Error %1!u! removing share '%2!ws!'.\n"
, sc
, pResourceEntry->propsActive.pwszShareName
);
} // if: error taking the resource offline
pResourceEntry->state = ClusterResourceFailed;
return;
} //*** FileShareSampleTerminate
/////////////////////////////////////////////////////////////////////////////
//++
//
// FileShareSampleLooksAlive
//
// Description:
// LooksAlive routine for File Share Sample resources.
//
// Perform a quick check to determine if the specified resource is
// probably online (available for use). This call should not block for
// more than 300 ms, preferably less than 50 ms.
//
// Arguments:
// residIn
// Supplies the resource ID for the resource to be polled.
//
// Return Value:
// TRUE
// The specified resource is probably online and available for use.
//
// FALSE
// The specified resource is not functioning normally. The IsAlive
// function will be called to perform a more thorough check.
//
// Notes:
// When using properties in this routine it is recommended that you
// use the properties in propsActive of the FILESHARESAMPLE_RESOURCE struct
// instead of the properties in props. The primary reason you should
// use propsActive is that the properties in props could be changed by
// the SetPrivateResProperties() routine. Using propsActive allows
// the online state of the resource to be steady while still allowing
// an administrator to change the stored value of the properties.
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL WINAPI
FileShareSampleLooksAlive(
RESID residIn
)
{
PFILESHARESAMPLE_RESOURCE pResourceEntry = NULL;
BOOL fRet = FALSE;
//
// Verify we have a valid resource ID.
//
pResourceEntry = static_cast< PFILESHARESAMPLE_RESOURCE >( residIn );
if ( pResourceEntry == NULL )
{
DBG_PRINT( "File Share Sample: LooksAlive request for a nonexistent resource id.\n" );
return FALSE;
} // if: NULL resource ID
if ( pResourceEntry->resid != residIn )
{
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_ERROR
, L"LooksAlive sanity check failed! resid = 0x%1!08x!.\n"
, residIn
);
return FALSE;
} // if: invalid resource ID
#ifdef LOG_VERBOSE
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_INFORMATION
, L"LooksAlive request.\n"
);
#endif
//
// NOTE: LooksAlive should be a quick check to see if the resource is
// available or not, whereas IsAlive should be a thorough check. If
// there are no differences between a quick check and a thorough check,
// IsAlive can be called for LooksAlive, as it is below. However, if there
// are differences, replace the call to IsAlive below with your quick
// check code.
//
//
// Check to see if the resource is alive.
//
if ( pResourceEntry->fIsAliveFailed == TRUE )
{
//
// Somebody called ClusterResourceControl with our custom control code.
// That had the same effect as calling IsAlive and the result was false,
// so we can skip doing the LooksAlive check and return false. This
// will cause IsAlive to be called and we'll do another check just in
// case we encountered a hiccup the first time.
//
fRet = FALSE;
} // if:
else
{
fRet = FileShareSampleCheckIsAlive( pResourceEntry, FALSE /* fFullCheck */ );
} // else:
return fRet;
} //*** FileShareSampleLooksAlive
/////////////////////////////////////////////////////////////////////////////
//++
//
// FileShareSampleIsAlive
//
// Description:
// IsAlive routine for File Share Sample resources.
//
// Perform a thorough check to determine if the specified resource is
// online (available for use). This call should not block for more
// more than 300 ms, preferably less than 50 ms. If it must block for
// longer than this, create a separate thread dedicated to polling for
// this information and have this routine return the status of the last
// poll performed.
//
// Arguments:
// residIn
// Supplies the resource ID for the resource to be polled.
//
// Return Value:
// TRUE
// The specified resource is online and functioning normally.
//
// FALSE
// The specified resource is not functioning normally. The resource
// will be terminated and then Online will be called.
//
// Notes:
// When using properties in this routine it is recommended that you
// use the properties in propsActive of the FILESHARESAMPLE_RESOURCE struct
// instead of the properties in props. The primary reason you should
// use propsActive is that the properties in props could be changed by
// the SetPrivateResProperties() routine. Using propsActive allows
// the online state of the resource to be steady while still allowing
// an administrator to change the stored value of the properties.
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL WINAPI
FileShareSampleIsAlive(
RESID residIn
)
{
PFILESHARESAMPLE_RESOURCE pResourceEntry = NULL;
//
// Verify we have a valid resource ID.
//
pResourceEntry = static_cast< PFILESHARESAMPLE_RESOURCE >( residIn );
if ( pResourceEntry == NULL )
{
DBG_PRINT( "File Share Sample: IsAlive request for a nonexistent resource id.\n" );
return FALSE;
} // if: NULL resource ID
if ( pResourceEntry->resid != residIn )
{
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_ERROR
, L"IsAlive sanity check failed! resid = 0x%1!08x!.\n"
, residIn
);
return FALSE;
} // if: invalid resource ID
#ifdef LOG_VERBOSE
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_INFORMATION
, L"IsAlive request.\n"
);
#endif
//
// Check to see if the resource is alive.
//
return FileShareSampleCheckIsAlive( pResourceEntry, TRUE /* fFullCheck */ );
} //** FileShareSampleIsAlive
/////////////////////////////////////////////////////////////////////////////
//++
//
// FileShareSampleCheckIsAlive
//
// Description:
// Check to see if the resource is alive for File Share Sample
// resources.
//
// Arguments:
// pResourceEntryIn
// Supplies the resource entry for the resource to polled.
//
// fFullCheckIn
// TRUE = Perform a full check.
// FALSE = Perform a cursory check.
//
// Return Value:
// TRUE
// The specified resource is online and functioning normally.
//
// FALSE
// The specified resource is not functioning normally.
//
// Notes:
// When using properties in this routine it is recommended that you
// use the properties in propsActive of the FILESHARESAMPLE_RESOURCE struct
// instead of the properties in props. The primary reason you should
// use propsActive is that the properties in props could be changed by
// the SetPrivateResProperties() routine. Using propsActive allows
// the online state of the resource to be steady while still allowing
// an administrator to change the stored value of the properties.
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL
FileShareSampleCheckIsAlive(
PFILESHARESAMPLE_RESOURCE pResourceEntryIn
, BOOL fFullCheckIn
)
{
BOOL fIsAlive = FALSE;
DWORD sc = ERROR_SUCCESS;
SHARE_INFO_2 * pshareInfo = NULL;
HANDLE hFile = NULL;
WIN32_FIND_DATA fileData;
ZeroMemory( &fileData, sizeof( fileData ) );
//
// Reset the variable.
//
pResourceEntryIn->fIsAliveFailed = FALSE;
//
// Check to see if the resource is alive.
//
sc = NetShareGetInfo(
NULL
, pResourceEntryIn->propsActive.pwszShareName
, 2 // return a SHARE_INFO_2 structure
, reinterpret_cast< LPBYTE * >( &pshareInfo )
);
if ( sc == NERR_NetNameNotFound )
{
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"CheckIsAlive: Error, share '%1!ws!' went away.\n"
, pResourceEntryIn->propsActive.pwszShareName
);
goto Cleanup;
} // if: share name not found
else if ( sc != NERR_Success )
{
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"CheckIsAlive: Error %1!u! checking for share '%2!ws!'.\n"
, sc
, pResourceEntryIn->propsActive.pwszShareName
);
goto Cleanup;
} // else if: error getting share info
if ( fFullCheckIn == TRUE )
{
//
// Do a full check by trying to open a file on the share.
//
hFile = FindFirstFileW( pResourceEntryIn->pwszUNCSharedPath, &fileData );
//
// If we fail on the first attempt, try again. There seems to be
// a bug in the RDR where the first attempt to read a share after
// it has been deleted and reinstated fails.
//
if ( hFile == INVALID_HANDLE_VALUE )
{
hFile = FindFirstFileW( pResourceEntryIn->pwszUNCSharedPath, &fileData );
} // if: error finding file
//
// If we succeeded in finding a file, or there were no files in
// the path, then return success, otherwise we had a failure.
//
sc = GetLastError();
if ( ( hFile == INVALID_HANDLE_VALUE )
&& ( sc != ERROR_FILE_NOT_FOUND )
&& ( sc != ERROR_ACCESS_DENIED )
)
{
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"CheckIsAlive: Share has gone offline!\n"
);
} // if: error finding the file
else
{
FindClose( hFile );
sc = ERROR_SUCCESS; // There are other non-failure status codes possible, reset to success.
} // else: found file
} // if: performing a full check
Cleanup:
NetApiBufferFree( reinterpret_cast< LPBYTE * >( pshareInfo ) );
pshareInfo = NULL;
if ( sc == ERROR_SUCCESS )
{
fIsAlive = TRUE;
} // if:
return fIsAlive;
} //*** FileShareSampleCheckIsAlive
/////////////////////////////////////////////////////////////////////////////
//++
//
// FileShareSampleResourceControl
//
// Description:
// ResourceControl routine for File Share Sample resources.
//
// Perform the control request specified by nControlCode on the specified
// resource.
//
// Arguments:
// residIn
// Supplies the resource ID for the specific resource.
//
// nControlCodeIn
// Supplies the control code that defines the action to be performed.
//
// pInBufferIn
// Supplies a pointer to a buffer containing input data.
//
// cbInBufferSizeIn
// Supplies the size, in bytes, of the data pointed to by pInBufferIn.
//
// pOutBufferOut
// Supplies a pointer to the output buffer to be filled in.
//
// cbOutBufferSizeIn
// Supplies the size, in bytes, of the available space pointed to by
// pOutBufferOut
//
// pcbBytesReturnedOut
// Returns the number of bytes of pOutBufferOut actually filled in by
// the resource. If pOutBufferOut is too small, pcbBytesReturnedOut
// contains the total number of bytes for the operation to succeed.
//
// Return Value:
// ERROR_SUCCESS
// The function completed successfully.
//
// ERROR_RESOURCE_NOT_FOUND
// Resource ID is not valid.
//
// ERROR_MORE_DATA
// The output buffer is too small to return the data.
// pcbBytesReturnedOut contains the required size.
//
// ERROR_INVALID_FUNCTION
// The requested control code is not supported. In some cases,
// this allows the cluster software to perform the work.
//
// Win32 error code
// The function failed.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD WINAPI
FileShareSampleResourceControl(
RESID residIn
, DWORD nControlCodeIn
, PVOID pInBufferIn
, DWORD cbInBufferSizeIn
, PVOID pOutBufferOut
, DWORD cbOutBufferSizeIn
, LPDWORD pcbBytesReturnedOut
)
{
DWORD sc = ERROR_SUCCESS;
PFILESHARESAMPLE_RESOURCE pResourceEntry = NULL;
//
// Verify we have a valid resource ID.
//
pResourceEntry = static_cast< PFILESHARESAMPLE_RESOURCE >( residIn );
if ( pResourceEntry == NULL )
{
DBG_PRINT( "File Share Sample: ResourceControl request for a nonexistent resource id.\n" );
return ERROR_RESOURCE_NOT_FOUND;
} // if: NULL resource ID
if ( pResourceEntry->resid != residIn )
{
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_ERROR
, L"ResourceControl sanity check failed! resid = 0x%1!08x!.\n"
, residIn
);
return ERROR_RESOURCE_NOT_FOUND;
} // if: invalid resource ID
switch ( nControlCodeIn )
{
case CLUSCTL_RESOURCE_UNKNOWN:
*pcbBytesReturnedOut = 0;
sc = ERROR_SUCCESS;
break;
case CLUSCTL_RESOURCE_ENUM_PRIVATE_PROPERTIES:
{
DWORD cbRequired = 0;
sc = ResUtilEnumProperties(
FileShareSampleResourcePrivateProperties
, static_cast< LPWSTR >( pOutBufferOut )
, cbOutBufferSizeIn
, pcbBytesReturnedOut
, &cbRequired
);
if ( sc == ERROR_MORE_DATA )
{
*pcbBytesReturnedOut = cbRequired;
} // if: output buffer is too small
break;
}
case CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES:
sc = FileShareSampleGetPrivateResProperties(
pResourceEntry
, pOutBufferOut
, cbOutBufferSizeIn
, pcbBytesReturnedOut
);
break;
case CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES:
sc = FileShareSampleValidatePrivateResProperties(
pResourceEntry
, pInBufferIn
, cbInBufferSizeIn
, NULL
);
break;
case CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES:
sc = FileShareSampleSetPrivateResProperties(
pResourceEntry
, pInBufferIn
, cbInBufferSizeIn
);
break;
case CLUSCTL_RESOURCE_SET_NAME:
sc = FileShareSampleSetNameHandler( pResourceEntry, static_cast< LPWSTR >( pInBufferIn ) );
break;
case CLUSCTL_RESOURCE_FILESHARESAMPLE_CALL_ISALIVE:
//
// This has the same effect as the periodic IsAlive check.
//
if ( pResourceEntry->state != ClusterResourceOnline )
{
sc = ERROR_CLUSTER_INVALID_REQUEST;
} // if:
else if ( FileShareSampleCheckIsAlive( pResourceEntry, TRUE ) == FALSE )
{
sc = ERROR_RESOURCE_FAILED;
pResourceEntry->fIsAliveFailed = TRUE;
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_ERROR
, L"ResourceControl CALL_ISALIVE failed.\n"
);
} // else if:
else
{
sc = ERROR_SUCCESS;
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_INFORMATION
, L"ResourceControl CALL_ISALIVE succeeded.\n"
);
} // else:
break;
case CLUSCTL_RESOURCE_GET_REQUIRED_DEPENDENCIES:
case CLUSCTL_RESOURCE_GET_CHARACTERISTICS:
case CLUSCTL_RESOURCE_GET_CLASS_INFO:
case CLUSCTL_RESOURCE_STORAGE_GET_DISK_INFO:
case CLUSCTL_RESOURCE_STORAGE_IS_PATH_VALID:
case CLUSCTL_RESOURCE_DELETE:
case CLUSCTL_RESOURCE_INSTALL_NODE:
case CLUSCTL_RESOURCE_EVICT_NODE:
case CLUSCTL_RESOURCE_ADD_DEPENDENCY:
case CLUSCTL_RESOURCE_REMOVE_DEPENDENCY:
case CLUSCTL_RESOURCE_ADD_OWNER:
case CLUSCTL_RESOURCE_REMOVE_OWNER:
case CLUSCTL_RESOURCE_CLUSTER_NAME_CHANGED:
case CLUSCTL_RESOURCE_CLUSTER_VERSION_CHANGED:
default:
sc = ERROR_INVALID_FUNCTION;
break;
} // switch: nControlCodeIn
return sc;
} //*** FileShareSampleResourceControl
/////////////////////////////////////////////////////////////////////////////
//++
//
// FileShareSampleResourceTypeControl
//
// Description:
// ResourceTypeControl routine for File Share Sample resources.
//
// Perform the control request specified by nControlCode.
//
// Arguments:
// pwszResourceTypeNameIn
// Supplies the name of the resource type.
//
// nControlCodeIn
// Supplies the control code that defines the action to be performed.
//
// pInBufferIn
// Supplies a pointer to a buffer containing input data.
//
// cbInBufferSizeIn
// Supplies the size, in bytes, of the data pointed to by pInBufferIn.
//
// pOutBufferOut
// Supplies a pointer to the output buffer to be filled in.
//
// cbOutBufferSizeIn
// Supplies the size, in bytes, of the available space pointed to by
// pOutBufferOut.
//
// pcbBytesReturnedOut
// Returns the number of bytes of pOutBufferOut actually filled in by
// the resource. If pOutBufferOut is too small, pcbBytesReturnedOut
// contains the total number of bytes for the operation to succeed.
//
// Return Value:
// ERROR_SUCCESS
// The function completed successfully.
//
// ERROR_MORE_DATA
// The output buffer is too small to return the data.
// pcbBytesReturned contains the required size.
//
// ERROR_INVALID_FUNCTION
// The requested control code is not supported. In some cases,
// this allows the cluster software to perform the work.
//
// Win32 error code
// The function failed.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD WINAPI
FileShareSampleResourceTypeControl(
LPCWSTR pwszResourceTypeNameIn
, DWORD nControlCodeIn
, PVOID pInBufferIn
, DWORD cbInBufferSizeIn
, PVOID pOutBufferOut
, DWORD cbOutBufferSizeIn
, LPDWORD pcbBytesReturnedOut
)
{
DWORD sc = ERROR_SUCCESS;
UNREFERENCED_PARAMETER( pwszResourceTypeNameIn );
UNREFERENCED_PARAMETER( pInBufferIn );
UNREFERENCED_PARAMETER( cbInBufferSizeIn );
switch ( nControlCodeIn )
{
case CLUSCTL_RESOURCE_TYPE_UNKNOWN:
*pcbBytesReturnedOut = 0;
sc = ERROR_SUCCESS;
break;
case CLUSCTL_RESOURCE_TYPE_ENUM_PRIVATE_PROPERTIES:
{
DWORD cbRequired = 0;
sc = ResUtilEnumProperties(
FileShareSampleResourcePrivateProperties
, static_cast< LPWSTR >( pOutBufferOut )
, cbOutBufferSizeIn
, pcbBytesReturnedOut
, &cbRequired
);
if ( sc == ERROR_MORE_DATA )
{
*pcbBytesReturnedOut = cbRequired;
} // if: output buffer is too small
break;
}
case CLUSCTL_RESOURCE_TYPE_GET_REQUIRED_DEPENDENCIES:
case CLUSCTL_RESOURCE_TYPE_GET_CHARACTERISTICS:
case CLUSCTL_RESOURCE_TYPE_GET_CLASS_INFO:
case CLUSCTL_RESOURCE_TYPE_STORAGE_GET_AVAILABLE_DISKS:
case CLUSCTL_RESOURCE_TYPE_INSTALL_NODE:
case CLUSCTL_RESOURCE_TYPE_EVICT_NODE:
default:
sc = ERROR_INVALID_FUNCTION;
break;
} // switch: nControlCode
return sc;
} //*** FileShareSampleResourceTypeControl
/////////////////////////////////////////////////////////////////////////////
//++
//
// FileShareSampleGetPrivateResProperties
//
// Description:
// Processes the CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES control
// function for resources of type File Share Sample.
//
// Arguments:
// pResourceEntryIn
// Supplies the resource entry on which to operate.
//
// pOutBufferOut
// Supplies a pointer to the output buffer to be filled in.
//
// cbOutBufferSizeIn
// Supplies the size, in bytes, of the available space pointed to by
// pOutBuffer.
//
// pcbBytesReturnedOut
// Returns the number of bytes of pOutBuffer actually filled in by
// the resource. If pOutBuffer is too small, pcbBytesReturnedOut
// contains the total number of bytes for the operation to succeed.
//
// Return Value:
// ERROR_SUCCESS
// The function completed successfully.
//
// ERROR_MORE_DATA
// The output buffer is too small to return the data.
// pcbBytesReturnedOut contains the required size.
//
// ERROR_INVALID_PARAMETER
// The data is formatted incorrectly.
//
// ERROR_NOT_ENOUGH_MEMORY
// An error occurred allocating memory.
//
// Win32 error code
// The function failed.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD
FileShareSampleGetPrivateResProperties(
PFILESHARESAMPLE_RESOURCE pResourceEntryIn
, PVOID pOutBufferOut
, DWORD cbOutBufferSizeIn
, LPDWORD pcbBytesReturnedOut
)
{
DWORD sc;
DWORD cbRequired = 0;
sc = ResUtilGetAllProperties(
pResourceEntryIn->hkeyParameters
, FileShareSampleResourcePrivateProperties
, pOutBufferOut
, cbOutBufferSizeIn
, pcbBytesReturnedOut
, &cbRequired
);
if ( sc == ERROR_MORE_DATA )
{
*pcbBytesReturnedOut = cbRequired;
} // if: output buffer is too small
return sc;
} //*** FileShareSampleGetPrivateResProperties
/////////////////////////////////////////////////////////////////////////////
//++
//
// FileShareSampleValidatePrivateResProperties
//
// Description:
// Processes the CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES control
// function for resources of type File Share Sample.
//
// Arguments:
// pResourceEntryIn
// Supplies the resource entry on which to operate.
//
// pInBufferIn
// Supplies a pointer to a buffer containing input data.
//
// cbOutBufferSizeIn
// Supplies the size, in bytes, of the data pointed to by pInBufferIn.
//
// pPropsOut
// Supplies the parameter block to fill in (optional).
//
// Return Value:
// ERROR_SUCCESS
// The function completed successfully.
//
// ERROR_INVALID_PARAMETER
// The data is formatted incorrectly.
//
// ERROR_NOT_ENOUGH_MEMORY
// An error occurred allocating memory.
//
// Win32 error code
// The function failed.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD
FileShareSampleValidatePrivateResProperties(
PFILESHARESAMPLE_RESOURCE pResourceEntryIn
, PVOID pInBufferIn
, DWORD cbInBufferSizeIn
, PFILESHARESAMPLE_PROPS pPropsOut
)
{
DWORD sc = ERROR_SUCCESS;
FILESHARESAMPLE_PROPS propsCurrent;
FILESHARESAMPLE_PROPS propsNew;
PFILESHARESAMPLE_PROPS pLocalProps = NULL;
LPWSTR pwszNameOfPropInError;
BOOL fRetrievedProps = FALSE;
//
// Check if there is input data.
//
if ( (pInBufferIn == NULL)
|| (cbInBufferSizeIn < sizeof( DWORD )) )
{
sc = ERROR_INVALID_DATA;
goto Cleanup;
} // if: no input buffer or input buffer not big enough to contain property list
//
// Retrieve the current set of private properties from the
// cluster database.
//
ZeroMemory( &propsCurrent, sizeof( propsCurrent ) );
sc = ResUtilGetPropertiesToParameterBlock(
pResourceEntryIn->hkeyParameters
, FileShareSampleResourcePrivateProperties
, reinterpret_cast< LPBYTE >( &propsCurrent )
, FALSE /*CheckForRequiredProperties*/
, &pwszNameOfPropInError
);
if ( sc != ERROR_SUCCESS )
{
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"Unable to read the '%1!s!' property. Error: %2!u!.\n"
, (pwszNameOfPropInError == NULL ? L"" : pwszNameOfPropInError)
, sc
);
goto Cleanup;
} // if: error getting properties
fRetrievedProps = TRUE;
//
// Duplicate the resource parameter block.
//
if ( pPropsOut == NULL )
{
pLocalProps = &propsNew;
} // if: no parameter block passed in
else
{
pLocalProps = pPropsOut;
} // else: parameter block passed in
ZeroMemory( pLocalProps, sizeof( *pLocalProps ) );
sc = ResUtilDupParameterBlock(
reinterpret_cast< LPBYTE >( pLocalProps )
, reinterpret_cast< LPBYTE >( &propsCurrent )
, FileShareSampleResourcePrivateProperties
);
if ( sc != ERROR_SUCCESS )
{
goto Cleanup;
} // if: error duplicating the parameter block
//
// Parse and validate the properties.
//
sc = ResUtilVerifyPropertyTable(
FileShareSampleResourcePrivateProperties
, NULL
, TRUE // AllowUnknownProperties
, pInBufferIn
, cbInBufferSizeIn
, reinterpret_cast< LPBYTE >( pLocalProps )
);
if ( sc != ERROR_SUCCESS )
{
goto Cleanup;
} // if: property list not validated successfully
//
// Validate the property values.
//
//
// TODO: Code to validate interactions between properties goes here.
//
Cleanup:
//
// Cleanup our parameter block.
//
if ( ( pLocalProps == &propsNew )
|| ( ( sc != ERROR_SUCCESS)
&& ( pLocalProps != NULL )
)
)
{
ResUtilFreeParameterBlock(
reinterpret_cast< LPBYTE >( pLocalProps )
, reinterpret_cast< LPBYTE >( &propsCurrent )
, FileShareSampleResourcePrivateProperties
);
} // if: we duplicated the parameter block
if ( fRetrievedProps )
{
ResUtilFreeParameterBlock(
reinterpret_cast< LPBYTE >( &propsCurrent )
, NULL
, FileShareSampleResourcePrivateProperties
);
} // if: properties were retrieved
return sc;
} // FileShareSampleValidatePrivateResProperties
/////////////////////////////////////////////////////////////////////////////
//++
//
// FileShareSampleSetPrivateResProperties
//
// Description:
// Processes the CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES control
// function for resources of type File Share Sample.
//
// Arguments:
// pResourceEntryIn
// Supplies the resource entry on which to operate.
//
// pInBufferIn
// Supplies a pointer to a buffer containing input data.
//
// cbOutBufferSizeIn
// Supplies the size, in bytes, of the data pointed to by pInBufferIn.
//
// Return Value:
// ERROR_SUCCESS
// The function completed successfully.
//
// ERROR_INVALID_PARAMETER
// The data is formatted incorrectly.
//
// ERROR_NOT_ENOUGH_MEMORY
// An error occurred allocating memory.
//
// Win32 error code
// The function failed.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD
FileShareSampleSetPrivateResProperties(
PFILESHARESAMPLE_RESOURCE pResourceEntryIn
, PVOID pInBufferIn
, DWORD cbInBufferSizeIn
)
{
DWORD sc = ERROR_SUCCESS;
FILESHARESAMPLE_PROPS props;
BOOL fAllowDynamicChanges = FALSE;
PSHARE_INFO_2 psiOld = NULL;
SHARE_INFO_2 siNew;
DWORD nInvalidParam;
//
// Parse the properties so they can be validated together.
// This routine does individual property validation.
//
sc = FileShareSampleValidatePrivateResProperties( pResourceEntryIn, pInBufferIn, cbInBufferSizeIn, &props );
if ( sc != ERROR_SUCCESS )
{
goto Cleanup;
} // if: error validating properties
//
// Only update the share if the name and the path didn't change.
//
if ( ( ( pResourceEntryIn->props.pwszShareName != NULL )
&& ( 0 != CompareStringW(
LOCALE_SYSTEM_DEFAULT,
NORM_IGNORECASE,
props.pwszShareName,
-1,
pResourceEntryIn->props.pwszShareName,
-1
)
)
)
|| ( ( pResourceEntryIn->props.pwszPath != NULL )
&& ( 0 != CompareStringW(
LOCALE_SYSTEM_DEFAULT,
NORM_IGNORECASE,
props.pwszPath,
-1,
pResourceEntryIn->props.pwszPath,
-1
)
)
)
)
{
fAllowDynamicChanges = FALSE;
} // if: share or path changed
else
{
fAllowDynamicChanges = TRUE;
} // else: share and path
//
// Save the property values.
//
sc = ResUtilSetPropertyParameterBlock(
pResourceEntryIn->hkeyParameters
, FileShareSampleResourcePrivateProperties
, NULL
, reinterpret_cast< LPBYTE >( &props )
, pInBufferIn
, cbInBufferSizeIn
, reinterpret_cast< LPBYTE >( &pResourceEntryIn->props )
);
ResUtilFreeParameterBlock(
reinterpret_cast< LPBYTE >( &props )
, reinterpret_cast< LPBYTE >( &pResourceEntryIn->props )
, FileShareSampleResourcePrivateProperties
);
if ( sc != ERROR_SUCCESS )
{
goto Cleanup;
} // if: properties not set successfully
//
// If the resource is online, return a non-success status.
//
if ( ( pResourceEntryIn->state == ClusterResourceOnline )
&& ( fAllowDynamicChanges == TRUE )
)
{
//
// Get the current share info.
//
sc = NetShareGetInfo(
NULL // pwszServer
, pResourceEntryIn->props.pwszShareName
, 2 // level
, reinterpret_cast< LPBYTE * >( &psiOld )
);
if ( sc != NERR_Success )
{
sc = ERROR_RESOURCE_PROPERTIES_STORED;
goto Cleanup;
} // if:
//
// Set new share info.
//
//
// Note: NetShareSetInfo ignores all but shi2_remark and shi2_max_uses.
//
CopyMemory( &siNew, psiOld, sizeof( siNew ) );
siNew.shi2_remark = pResourceEntryIn->props.pwszRemark;
siNew.shi2_max_uses = pResourceEntryIn->props.nMaxUsers;
sc = NetShareSetInfo(
NULL // pwszServer
, pResourceEntryIn->props.pwszShareName
, 2 // level
, reinterpret_cast< LPBYTE >( &siNew )
, &nInvalidParam
);
if ( sc != NERR_Success )
{
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"SetPrivateProps: Error setting info on share '%1!ws!'. Error %2!u!, property # %3!d!.\n"
, pResourceEntryIn->props.pwszShareName
, sc
, nInvalidParam
);
sc = ERROR_RESOURCE_PROPERTIES_STORED;
goto Cleanup;
} // if: error setting new share info
sc = ERROR_SUCCESS;
} // if: resource is currently online
else if ( ( pResourceEntryIn->state == ClusterResourceOnline )
|| ( pResourceEntryIn->state == ClusterResourceOnlinePending )
)
{
sc = ERROR_RESOURCE_PROPERTIES_STORED;
} // else if: resource is currently online or online pending
else
{
sc = ERROR_SUCCESS;
} // else: resource is in some other state
Cleanup:
if ( psiOld != NULL )
{
NetApiBufferFree( psiOld );
} // if:
return sc;
} //*** FileShareSampleSetPrivateResProperties
/////////////////////////////////////////////////////////////////////////////
//++
//
// FileShareSampleSetNameHandler
//
// Description:
// Handle the CLUSCTL_RESOURCE_SET_NAME control code by saving the new
// name of the resource.
//
// Arguments:
// pResourceEntryIn
// Supplies the resource entry on which to operate.
//
// pwszNameIn
// The new name of the resource.
//
// Return Value:
// ERROR_SUCCESS
// The function completed successfully.
//
// Win32 error code
// The function failed.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD
FileShareSampleSetNameHandler(
PFILESHARESAMPLE_RESOURCE pResourceEntryIn
, LPWSTR pwszNameIn
)
{
DWORD sc = ERROR_SUCCESS;
DWORD cchNameBuffer = 0;
HRESULT hr = S_OK;
WCHAR * pwszTemp = NULL;
//
// Allocate a buffer for the new name.
//
cchNameBuffer = lstrlenW( pwszNameIn ) + 1;
pwszTemp = new WCHAR[ cchNameBuffer ];
if ( pwszTemp == NULL )
{
sc = GetLastError();
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"Failed to allocate memory for the new resource name '%1!s!'. Error: %2!u!.\n"
, pwszNameIn
, sc
);
goto Cleanup;
} // if: error allocating memory for the name.
//
// Copy the new name to the new buffer.
//
hr = StringCchCopyW( pwszTemp, cchNameBuffer, pwszNameIn );
if ( FAILED( hr ) )
{
sc = HRESULT_CODE( hr );
goto Cleanup;
} // if:
//
// All went well, free the old buffer and assign the new one.
//
delete [] pResourceEntryIn->pwszResourceName;
pResourceEntryIn->pwszResourceName = pwszTemp;
pwszTemp = NULL;
Cleanup:
delete [] pwszTemp;
return sc;
} //*** FileShareSampleSetNameHandler
/////////////////////////////////////////////////////////////////////////////
//
// Define Function Table
//
/////////////////////////////////////////////////////////////////////////////
CLRES_V1_FUNCTION_TABLE(
g_FileShareSampleFunctionTable, // Name
CLRES_VERSION_V1_00, // Version
FileShareSample, // Prefix
NULL, // Arbitrate
NULL, // Release
FileShareSampleResourceControl, // ResControl
FileShareSampleResourceTypeControl // ResTypeControl
);