/*++ Copyright (c) 1992-1997 Microsoft Corporation Module Name: LkQuorum.c Abstract: Resource DLL for Local Quorum Author: Sivaprasad Padisetty (sivapad) April 21, 1997 Revision History: --*/ #define UNICODE 1 #include "clusres.h" #include "clusrtl.h" // // Type and constant definitions. // #define MAX_RETRIES 20 #define DBG_PRINT printf // ADDPARAM: Add new parameters here. #define PARAM_NAME__PATH L"Path" #define PARAM_NAME__DEBUG L"Debug" // ADDPARAM: Add new parameters here. typedef struct _LKQUORUM_PARAMS { PWSTR Path; DWORD Debug; } LKQUORUM_PARAMS, *PLKQUORUM_PARAMS; typedef struct _LKQUORUM_RESOURCE { RESID ResId; // for validation LKQUORUM_PARAMS Params; HKEY ParametersKey; RESOURCE_HANDLE ResourceHandle; CLUSTER_RESOURCE_STATE State; } LKQUORUM_RESOURCE, *PLKQUORUM_RESOURCE; // // Global data. // WCHAR LkQuorumDefaultPath[MAX_PATH]=L"%SystemRoot%\\cluster"; // // Local Quorum resource read-write private properties. // RESUTIL_PROPERTY_ITEM LkQuorumResourcePrivateProperties[] = { { PARAM_NAME__PATH, NULL, CLUSPROP_FORMAT_SZ, (DWORD_PTR) LkQuorumDefaultPath, 0, 0, 0, FIELD_OFFSET(LKQUORUM_PARAMS,Path) }, { PARAM_NAME__DEBUG, NULL, CLUSPROP_FORMAT_DWORD, 0, 0, 1, 0, FIELD_OFFSET(LKQUORUM_PARAMS,Debug) }, { 0 } }; #ifdef OLD // Event Logging routine. PLOG_EVENT_ROUTINE g_LogEvent = NULL; // Resource Status routine for pending Online and Offline calls. PSET_RESOURCE_STATUS_ROUTINE g_SetResourceStatus = NULL; #else #define g_LogEvent ClusResLogEvent #endif // Forward reference to our RESAPI function table. extern CLRES_FUNCTION_TABLE g_LkQuorumFunctionTable; // // Function prototypes. // DWORD WINAPI Startup( IN LPCWSTR ResourceType, IN DWORD MinVersionSupported, IN DWORD MaxVersionSupported, IN PSET_RESOURCE_STATUS_ROUTINE SetResourceStatus, IN PLOG_EVENT_ROUTINE LogEvent, OUT PCLRES_FUNCTION_TABLE *FunctionTable ); RESID WINAPI LkQuorumOpen( IN LPCWSTR ResourceName, IN HKEY ResourceKey, IN RESOURCE_HANDLE ResourceHandle ); VOID WINAPI LkQuorumClose( IN RESID ResourceId ); DWORD WINAPI LkQuorumOnline( IN RESID ResourceId, IN OUT PHANDLE EventHandle ); DWORD WINAPI LkQuorumOffline( IN RESID ResourceId ); VOID WINAPI LkQuorumTerminate( IN RESID ResourceId ); DWORD LkQuorumDoTerminate( IN PLKQUORUM_RESOURCE ResourceEntry ); BOOL WINAPI LkQuorumLooksAlive( IN RESID ResourceId ); BOOL WINAPI LkQuorumIsAlive( IN RESID ResourceId ); BOOL LkQuorumCheckIsAlive( IN PLKQUORUM_RESOURCE ResourceEntry ); DWORD WINAPI LkQuorumResourceControl( IN RESID ResourceId, IN DWORD ControlCode, IN PVOID InBuffer, IN DWORD InBufferSize, OUT PVOID OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ); DWORD LkQuorumGetPrivateResProperties( IN OUT PLKQUORUM_RESOURCE ResourceEntry, OUT PVOID OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ); DWORD LkQuorumValidatePrivateResProperties( IN OUT PLKQUORUM_RESOURCE ResourceEntry, IN PVOID InBuffer, IN DWORD InBufferSize, OUT PLKQUORUM_PARAMS Params ); DWORD LkQuorumSetPrivateResProperties( IN OUT PLKQUORUM_RESOURCE ResourceEntry, IN PVOID InBuffer, IN DWORD InBufferSize ); DWORD LkQuorumGetDiskInfo( IN LPWSTR lpszPath, OUT PVOID *OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ) ; BOOLEAN LkQuorumInit( VOID ) { return(TRUE); } BOOLEAN WINAPI LkQuorumDllEntryPoint( IN HINSTANCE DllHandle, IN DWORD Reason, IN LPVOID Reserved ) { switch( Reason ) { case DLL_PROCESS_ATTACH: if ( !LkQuorumInit() ) { return(FALSE); } break; case DLL_PROCESS_DETACH: break; default: break; } return(TRUE); } // LkQuorumDllEntryPoint DWORD BreakIntoDebugger (LPCWSTR) ; #ifdef OLD DWORD WINAPI Startup( IN LPCWSTR ResourceType, IN DWORD MinVersionSupported, IN DWORD MaxVersionSupported, IN PSET_RESOURCE_STATUS_ROUTINE SetResourceStatus, IN PLOG_EVENT_ROUTINE LogEvent, OUT PCLRES_FUNCTION_TABLE *FunctionTable ) /*++ Routine Description: Startup the resource DLL. This routine verifies that at least one currently supported version of the resource DLL is between MinVersionSupported and MaxVersionSupported. 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 MaxVersionSupported) should be returned as the resource DLL's interface. If the returned version is not within range, then startup fails. The ResourceType is passed in so that if the resource DLL supports more than one ResourceType, it can pass back the correct function table associated with the ResourceType. Arguments: ResourceType - The type of resource requesting a function table. MinVersionSupported - The minimum resource DLL interface version supported by the cluster software. MaxVersionSupported - The maximum resource DLL interface version supported by the cluster software. SetResourceStatus - Pointer to a routine that the resource DLL should call to update the state of a resource after the Online or Offline routine returns a status of ERROR_IO_PENDING. LogEvent - Pointer to a routine that handles the reporting of events from the resource DLL. FunctionTable - 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_MOD_NOT_FOUND - The resource type is unknown by this DLL. ERROR_REVISION_MISMATCH - The version of the cluster service doesn't match the versrion of the DLL. Win32 error code - The operation failed. --*/ { if ( (MinVersionSupported > CLRES_VERSION_V1_00) || (MaxVersionSupported < CLRES_VERSION_V1_00) ) { return(ERROR_REVISION_MISMATCH); } if ( !g_LogEvent ) { g_LogEvent = LogEvent; g_SetResourceStatus = SetResourceStatus; } *FunctionTable = &g_LkQuorumFunctionTable; return(ERROR_SUCCESS); } // Startup #endif RESID WINAPI LkQuorumOpen( IN LPCWSTR ResourceName, IN HKEY ResourceKey, IN RESOURCE_HANDLE ResourceHandle ) /*++ Routine Description: Open routine for Local Quourm resources. Open the specified resource (create an instance of the resource). Allocate all structures necessary to bring the specified resource online. Arguments: ResourceName - Supplies the name of the resource to open. ResourceKey - Supplies handle to the resource's cluster configuration database key. ResourceHandle - A handle that is passed back to the resource monitor when the SetResourceStatus or LogEvent method is called. See the description of the SetResourceStatus and LogEvent methods on the LkQuorumStatup 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 callback. Return Value: RESID of created resource. NULL on failure. --*/ { DWORD status = ERROR_SUCCESS; DWORD disposition; RESID resid = 0; HKEY parametersKey = NULL; PLKQUORUM_RESOURCE resourceEntry = NULL; DWORD dwStrLen = 0; DWORD dwSubStrLen = 0; LPWSTR lpszNameofPropInError; // // Open the Parameters registry key for this resource. // status = ClusterRegOpenKey( ResourceKey, L"Parameters", KEY_READ, ¶metersKey); if ( status != ERROR_SUCCESS ) { (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Unable to open Parameters key. Error: %1!u!.\n", status ); goto exit; } // // Allocate a resource entry. // resourceEntry = (PLKQUORUM_RESOURCE) LocalAlloc( LMEM_FIXED, sizeof(LKQUORUM_RESOURCE) ); if ( resourceEntry == NULL ) { status = GetLastError(); (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Unable to allocate resource entry structure. Error: %1!u!.\n", status ); goto exit; } // // Initialize the resource entry.. // ZeroMemory( resourceEntry, sizeof(LKQUORUM_RESOURCE) ); resourceEntry->ResId = (RESID)resourceEntry; // for validation resourceEntry->ResourceHandle = ResourceHandle; resourceEntry->ParametersKey = parametersKey; resourceEntry->State = ClusterResourceOffline; // // Read the resource's properties. // status = ResUtilGetPropertiesToParameterBlock( resourceEntry->ParametersKey, LkQuorumResourcePrivateProperties, (LPBYTE) &resourceEntry->Params, TRUE, // CheckForRequiredProperties &lpszNameofPropInError ); if (status != ERROR_SUCCESS) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"Unable to read the parameter lock\n"); goto exit; } // // Startup for the resource. // resid = (RESID)resourceEntry; exit: if ( resid == 0 ) { if ( parametersKey != NULL ) { ClusterRegCloseKey( parametersKey ); } if ( resourceEntry != NULL ) { LocalFree( resourceEntry ); } } SetLastError( status ); return(resid); } // LkQuorumOpen VOID WINAPI LkQuorumClose( IN RESID ResourceId ) /*++ Routine Description: Close routine for Local Quourm 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: ResourceId - Supplies the RESID of the resource to close. Return Value: None. --*/ { PLKQUORUM_RESOURCE resourceEntry; resourceEntry = (PLKQUORUM_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { DBG_PRINT( "LkQuorum: Close request for a nonexistent resource id 0x%p\n", ResourceId ); return; } if ( resourceEntry->ResId != ResourceId ) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"Close resource sanity check failed! ResourceId = %1!u!.\n", ResourceId ); return; } #ifdef LOG_VERBOSE (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Close request.\n" ); #endif // // Close the Parameters key. // if ( resourceEntry->ParametersKey ) { ClusterRegCloseKey( resourceEntry->ParametersKey ); } // // Deallocate the resource entry. // // ADDPARAM: Add new parameters here. LocalFree( resourceEntry->Params.Path ); LocalFree( resourceEntry ); } // LkQuorumClose DWORD WINAPI LkQuorumOnline( IN RESID ResourceId, IN OUT PHANDLE EventHandle ) /*++ Routine Description: Online routine for Local Quourm resources. Bring the specified resource online (available for use). Arguments: ResourceId - Supplies the resource id for the resource to be brought online (available for use). EventHandle - Returns a signalable handle that is signaled when the resource DLL detects a failure on the resource. This argument is NULL on input, and the resource DLL returns NULL if asynchronous notification of failures 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 - RESID is not valid. Win32 error code - The operation failed. --*/ { PLKQUORUM_RESOURCE resourceEntry = NULL; DWORD status = ERROR_SUCCESS; LPWSTR lpszNameofPropInError; resourceEntry = (PLKQUORUM_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { DBG_PRINT( "LkQuorum: Online request for a nonexistent resource id 0x%p.\n", ResourceId ); return(ERROR_RESOURCE_NOT_FOUND); } if ( resourceEntry->ResId != ResourceId ) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"Online service sanity check failed! ResourceId = %1!u!.\n", ResourceId ); return(ERROR_RESOURCE_NOT_FOUND); } // // Read the resource's properties. // status = ResUtilGetPropertiesToParameterBlock( resourceEntry->ParametersKey, LkQuorumResourcePrivateProperties, (LPBYTE) &resourceEntry->Params, TRUE, // CheckForRequiredProperties &lpszNameofPropInError ); if (status != ERROR_SUCCESS) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"Unable to read the parameter lock\n"); return( status ); } #ifdef LOG_VERBOSE (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Online request.\n" ); #endif resourceEntry->State = ClusterResourceOnline; return(status); } // LkQuorumOnline DWORD WINAPI LkQuorumOffline( IN RESID ResourceId ) /*++ Routine Description: Offline routine for Local Quourm resources. Take the specified resource offline gracefully (unavailable for use). Wait for any cleanup operations to complete before returning. Arguments: ResourceId - Supplies the resource id for the resource to be shutdown gracefully. Return Value: ERROR_SUCCESS - The request completed successfully and the resource is offline. ERROR_RESOURCE_NOT_FOUND - RESID is not valid. 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 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 - Will cause the resource monitor to log an event and call the Terminate routine. --*/ { PLKQUORUM_RESOURCE resourceEntry; resourceEntry = (PLKQUORUM_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { DBG_PRINT( "LkQuorum: Offline request for a nonexistent resource id 0x%p\n", ResourceId ); return(ERROR_RESOURCE_NOT_FOUND); } if ( resourceEntry->ResId != ResourceId ) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"Offline resource sanity check failed! ResourceId = %1!u!.\n", ResourceId ); return(ERROR_RESOURCE_NOT_FOUND); } #ifdef LOG_VERBOSE (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Offline request.\n" ); #endif // TODO: Offline code // NOTE: Offline should try to shut the resource down gracefully, whereas // Terminate must shut the resource down immediately. If there are no // differences between a graceful shut down and an immediate shut down, // Terminate can be called for Offline, as it is below. However, if there // are differences, replace the call to Terminate below with your graceful // shutdown code. // // Terminate the resource. // LkQuorumDoTerminate( resourceEntry ); return ERROR_SUCCESS ; } // LkQuorumOffline VOID WINAPI LkQuorumTerminate( IN RESID ResourceId ) /*++ Routine Description: Terminate routine for Local Quourm resources. Take the specified resource offline immediately (the resource is unavailable for use). Arguments: ResourceId - Supplies the resource id for the resource to be brought offline. Return Value: None. --*/ { PLKQUORUM_RESOURCE resourceEntry; resourceEntry = (PLKQUORUM_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { DBG_PRINT( "LkQuorum: Terminate request for a nonexistent resource id 0x%p\n", ResourceId ); return; } if ( resourceEntry->ResId != ResourceId ) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"Terminate resource sanity check failed! ResourceId = %1!u!.\n", ResourceId ); return; } #ifdef LOG_VERBOSE (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Terminate request.\n" ); #endif // // Terminate the resource. // LkQuorumDoTerminate( resourceEntry ); } // LkQuorumTerminate DWORD LkQuorumDoTerminate( IN PLKQUORUM_RESOURCE ResourceEntry ) /*++ Routine Description: Do the actual Terminate work for Local Quourm resources. Arguments: ResourceEntry - Supplies resource entry for resource to be terminated Return Value: ERROR_SUCCESS - The request completed successfully and the resource is offline. Win32 error code - Will cause the resource monitor to log an event and call the Terminate routine. --*/ { DWORD status = ERROR_SUCCESS; ResourceEntry->State = ClusterResourceOffline; return(status); } // LkQuorumDoTerminate BOOL WINAPI LkQuorumLooksAlive( IN RESID ResourceId ) /*++ Routine Description: LooksAlive routine for Local Quourm 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: ResourceId - Supplies the resource id for the resource to polled. Return Value: TRUE - The specified resource is probably online and available for use. FALSE - The specified resource is not functioning normally. --*/ { PLKQUORUM_RESOURCE resourceEntry; resourceEntry = (PLKQUORUM_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { DBG_PRINT("LkQuorum: LooksAlive request for a nonexistent resource id 0x%p\n", ResourceId ); return(FALSE); } if ( resourceEntry->ResId != ResourceId ) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"LooksAlive sanity check failed! ResourceId = %1!u!.\n", ResourceId ); return(FALSE); } #ifdef LOG_VERBOSE (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"LooksAlive request.\n" ); #endif // TODO: LooksAlive code // 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. // return(LkQuorumCheckIsAlive( resourceEntry )); } // LkQuorumLooksAlive BOOL WINAPI LkQuorumIsAlive( IN RESID ResourceId ) /*++ Routine Description: IsAlive routine for Local Quourm resources. Perform a thorough check to determine if the specified resource is online (available for use). This call should not block for more than 400 ms, preferably less than 100 ms. Arguments: ResourceId - Supplies the resource id for the resource to polled. Return Value: TRUE - The specified resource is online and functioning normally. FALSE - The specified resource is not functioning normally. --*/ { PLKQUORUM_RESOURCE resourceEntry; resourceEntry = (PLKQUORUM_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { DBG_PRINT("LkQuorum: IsAlive request for a nonexistent resource id 0x%p\n", ResourceId ); return(FALSE); } if ( resourceEntry->ResId != ResourceId ) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"IsAlive sanity check failed! ResourceId = %1!u!.\n", ResourceId ); return(FALSE); } #ifdef LOG_VERBOSE (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"IsAlive request.\n" ); #endif // // Check to see if the resource is alive. // return(LkQuorumCheckIsAlive( resourceEntry )); } // LkQuorumIsAlive BOOL LkQuorumCheckIsAlive( IN PLKQUORUM_RESOURCE ResourceEntry ) /*++ Routine Description: Check to see if the resource is alive for Local Quourm resources. Arguments: ResourceEntry - Supplies the resource entry for the resource to polled. Return Value: TRUE - The specified resource is online and functioning normally. FALSE - The specified resource is not functioning normally. --*/ { return(TRUE); } // LkQuorumCheckIsAlive DWORD WINAPI LkQuorumResourceControl( IN RESID ResourceId, IN DWORD ControlCode, IN PVOID InBuffer, IN DWORD InBufferSize, OUT PVOID OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ) /*++ Routine Description: ResourceControl routine for Local Quourm resources. Perform the control request specified by ControlCode on the specified resource. Arguments: ResourceId - Supplies the resource id for the specific resource. ControlCode - Supplies the control code that defines the action to be performed. InBuffer - Supplies a pointer to a buffer containing input data. InBufferSize - Supplies the size, in bytes, of the data pointed to by InBuffer. OutBuffer - Supplies a pointer to the output buffer to be filled in. OutBufferSize - Supplies the size, in bytes, of the available space pointed to by OutBuffer. BytesReturned - Returns the number of bytes of OutBuffer actually filled in by the resource. If OutBuffer is too small, BytesReturned contains the total number of bytes for the operation to succeed. Return Value: ERROR_SUCCESS - The function completed successfully. ERROR_RESOURCE_NOT_FOUND - RESID is not valid. 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 status; PLKQUORUM_RESOURCE resourceEntry; DWORD required; resourceEntry = (PLKQUORUM_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { DBG_PRINT("LkQuorum: ResourceControl request for a nonexistent resource id 0x%p\n", ResourceId ); return(ERROR_RESOURCE_NOT_FOUND); } if ( resourceEntry->ResId != ResourceId ) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"ResourceControl sanity check failed! ResourceId = %1!u!.\n", ResourceId ); return(ERROR_RESOURCE_NOT_FOUND); } switch ( ControlCode ) { case CLUSCTL_RESOURCE_UNKNOWN: *BytesReturned = 0; status = ERROR_SUCCESS; break; case CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTY_FMTS: status = ResUtilGetPropertyFormats( LkQuorumResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } break; case CLUSCTL_RESOURCE_GET_CHARACTERISTICS: *BytesReturned = sizeof(DWORD); if ( OutBufferSize < sizeof(DWORD) ) { status = ERROR_MORE_DATA; } else { LPDWORD ptrDword = OutBuffer; *ptrDword = CLUS_CHAR_QUORUM | CLUS_CHAR_LOCAL_QUORUM | ((resourceEntry->Params.Debug == TRUE) ? CLUS_CHAR_LOCAL_QUORUM_DEBUG : 0); status = ERROR_SUCCESS; } break; case CLUSCTL_RESOURCE_GET_CLASS_INFO: *BytesReturned = sizeof(CLUS_RESOURCE_CLASS_INFO); if ( OutBufferSize < sizeof(CLUS_RESOURCE_CLASS_INFO) ) { status = ERROR_MORE_DATA; } else { PCLUS_RESOURCE_CLASS_INFO ptrResClassInfo = (PCLUS_RESOURCE_CLASS_INFO) OutBuffer; ptrResClassInfo->rc = CLUS_RESCLASS_STORAGE; ptrResClassInfo->SubClass = (DWORD) CLUS_RESSUBCLASS_SHARED; status = ERROR_SUCCESS; } break; case CLUSCTL_RESOURCE_STORAGE_GET_DISK_INFO: // // Chittur Subbaraman (chitturs) - 12/23/98 // // If the local quorum drive letter cannot be found in the // path parameter, it defaults to "SystemDrive" environment // variable. // status = LkQuorumGetDiskInfo(resourceEntry->Params.Path, &OutBuffer, OutBufferSize, BytesReturned); // Add the endmark. if ( OutBufferSize > *BytesReturned ) { OutBufferSize -= *BytesReturned; } else { OutBufferSize = 0; } *BytesReturned += sizeof(CLUSPROP_SYNTAX); if ( OutBufferSize >= sizeof(CLUSPROP_SYNTAX) ) { PCLUSPROP_SYNTAX ptrSyntax = (PCLUSPROP_SYNTAX) OutBuffer; ptrSyntax->dw = CLUSPROP_SYNTAX_ENDMARK; } break; case CLUSCTL_RESOURCE_ENUM_PRIVATE_PROPERTIES: status = ResUtilEnumProperties( LkQuorumResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } break; case CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES: status = LkQuorumGetPrivateResProperties( resourceEntry, OutBuffer, OutBufferSize, BytesReturned ); break; case CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES: status = LkQuorumValidatePrivateResProperties( resourceEntry, InBuffer, InBufferSize, NULL ); break; case CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES: status = LkQuorumSetPrivateResProperties( resourceEntry, InBuffer, InBufferSize ); break; default: status = ERROR_INVALID_FUNCTION; break; } return(status); } // LkQuorumResourceControl DWORD WINAPI LkQuorumResourceTypeControl( IN LPCWSTR ResourceTypeName, IN DWORD ControlCode, IN PVOID InBuffer, IN DWORD InBufferSize, OUT PVOID OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ) /*++ Routine Description: ResourceTypeControl routine for Local Quourm resources. Perform the control request specified by ControlCode on this resource type. Arguments: ResourceTypeName - Supplies the resource type name. ControlCode - Supplies the control code that defines the action to be performed. InBuffer - Supplies a pointer to a buffer containing input data. InBufferSize - Supplies the size, in bytes, of the data pointed to by InBuffer. OutBuffer - Supplies a pointer to the output buffer to be filled in. OutBufferSize - Supplies the size, in bytes, of the available space pointed to by OutBuffer. BytesReturned - Returns the number of bytes of OutBuffer actually filled in by the resource. If OutBuffer is too small, BytesReturned contains the total number of bytes for the operation to succeed. Return Value: ERROR_SUCCESS - The function completed successfully. ERROR_RESOURCE_NOT_FOUND - RESID is not valid. 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 status; DWORD required; switch ( ControlCode ) { case CLUSCTL_RESOURCE_TYPE_UNKNOWN: *BytesReturned = 0; status = ERROR_SUCCESS; break; case CLUSCTL_RESOURCE_TYPE_GET_PRIVATE_RESOURCE_PROPERTY_FMTS: status = ResUtilGetPropertyFormats( LkQuorumResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } break; case CLUSCTL_RESOURCE_TYPE_ENUM_PRIVATE_PROPERTIES: status = ResUtilEnumProperties( LkQuorumResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } break; case CLUSCTL_RESOURCE_TYPE_GET_CHARACTERISTICS: *BytesReturned = sizeof(DWORD); if ( OutBufferSize < sizeof(DWORD) ) { status = ERROR_MORE_DATA; } else { LPDWORD ptrDword = OutBuffer; *ptrDword = CLUS_CHAR_QUORUM | CLUS_CHAR_LOCAL_QUORUM ; status = ERROR_SUCCESS; } break; case CLUSCTL_RESOURCE_TYPE_GET_CLASS_INFO: *BytesReturned = sizeof(CLUS_RESOURCE_CLASS_INFO); if ( OutBufferSize < sizeof(CLUS_RESOURCE_CLASS_INFO) ) { status = ERROR_MORE_DATA; } else { PCLUS_RESOURCE_CLASS_INFO ptrResClassInfo = (PCLUS_RESOURCE_CLASS_INFO) OutBuffer; ptrResClassInfo->rc = CLUS_RESCLASS_STORAGE; ptrResClassInfo->SubClass = (DWORD) CLUS_RESSUBCLASS_SHARED; status = ERROR_SUCCESS; } break; default: status = ERROR_INVALID_FUNCTION; break; } return(status); } // LkQuorumResourceTypeControl DWORD LkQuorumGetPrivateResProperties( IN OUT PLKQUORUM_RESOURCE ResourceEntry, OUT PVOID OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ) /*++ Routine Description: Processes the CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES control function for resources of type Local Quorum. Arguments: ResourceEntry - Supplies the resource entry on which to operate. OutBuffer - Returns the output data. OutBufferSize - Supplies the size, in bytes, of the data pointed to by OutBuffer. BytesReturned - The number of bytes returned in OutBuffer. 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 status; DWORD required; // // Get all our properties. // status = ResUtilGetAllProperties( ResourceEntry->ParametersKey, LkQuorumResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } return(status); } // LkQuorumGetPrivateResProperties DWORD LkQuorumValidatePrivateResProperties( IN OUT PLKQUORUM_RESOURCE ResourceEntry, IN PVOID InBuffer, IN DWORD InBufferSize, OUT PLKQUORUM_PARAMS Params ) /*++ Routine Description: Processes the CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES control function for resources of type Local Quourm. Arguments: ResourceEntry - Supplies the resource entry on which to operate. InBuffer - Supplies a pointer to a buffer containing input data. InBufferSize - Supplies the size, in bytes, of the data pointed to by InBuffer. Params - Supplies the parameter block to fill in. 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 status; LKQUORUM_PARAMS params; PLKQUORUM_PARAMS pParams; HCLUSTER hCluster = NULL; HCLUSENUM hEnum = NULL; // // Check if there is input data. // if ( (InBuffer == NULL) || (InBufferSize < sizeof(DWORD)) ) { return(ERROR_INVALID_DATA); } // // Duplicate the resource parameter block. // if ( Params == NULL ) { pParams = ¶ms; } else { pParams = Params; } ZeroMemory( pParams, sizeof(LKQUORUM_PARAMS) ); status = ResUtilDupParameterBlock( (LPBYTE) pParams, (LPBYTE) &ResourceEntry->Params, LkQuorumResourcePrivateProperties ); if ( status != ERROR_SUCCESS ) { return(status); } // // Parse and validate the properties. // status = ResUtilVerifyPropertyTable( LkQuorumResourcePrivateProperties, NULL, TRUE, // Accept unknowns InBuffer, InBufferSize, (LPBYTE) pParams ); if ( status == ERROR_SUCCESS ) { //initialize to the value already set DWORD dwTmp = ResourceEntry->Params.Debug; // // Validate the parameter values. // // TODO: Code to validate interactions between parameters goes here. // see if the Debug value is being set, if so we need to check if // the number of nodes in the cluster allows it if (ClRtlFindDwordProperty(InBuffer, InBufferSize,PARAM_NAME__DEBUG, &dwTmp) == ERROR_SUCCESS) { if (dwTmp == FALSE) { //dont allow the DEBUG parameter to be set to FALSE, if this //cluster is multi-node DWORD dwNumNodes; hCluster = OpenCluster(NULL); if (hCluster == NULL) { status = GetLastError(); (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"ValidatePrivateProperties: OpenCluster() failed! status = %1!u!.\n", status ); goto FnExit; } hEnum = ClusterOpenEnum(hCluster, CLUSTER_ENUM_NODE); if (hEnum == NULL) { status = GetLastError(); (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"ValidatePrivateProperties: ClusterOpenEnum() failed! status = %1!u!.\n", status ); goto FnExit; } dwNumNodes = ClusterGetEnumCount(hEnum); if (dwNumNodes > 1) { //if there are more than one node in the cluster, //dont allow the user to set the debug property to FALSE status = ERROR_INVALID_PARAMETER; goto FnExit; } status = ClusterCloseEnum(hEnum); if (status != ERROR_SUCCESS) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"ValidatePrivateProperties: ClusterCloseEnum() failed! status = %1!u!.\n", status ); goto FnExit; } hEnum = NULL; } } } FnExit: if (hEnum) ClusterCloseEnum(hEnum); if (hCluster) CloseCluster(hCluster); // // Cleanup our parameter block. // if ( pParams == ¶ms ) { ResUtilFreeParameterBlock( (LPBYTE) ¶ms, (LPBYTE) &ResourceEntry->Params, LkQuorumResourcePrivateProperties ); } return(status); } // LkQuorumValidatePrivateResProperties DWORD LkQuorumSetPrivateResProperties( IN OUT PLKQUORUM_RESOURCE ResourceEntry, IN PVOID InBuffer, IN DWORD InBufferSize ) /*++ Routine Description: Processes the CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES control function for resources of type Local Quourm. Arguments: ResourceEntry - Supplies the resource entry on which to operate. InBuffer - Supplies a pointer to a buffer containing input data. InBufferSize - Supplies the size, in bytes, of the data pointed to by InBuffer. 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 status = ERROR_SUCCESS; LKQUORUM_PARAMS params; ZeroMemory( ¶ms, sizeof(LKQUORUM_PARAMS) ); // // Parse the properties so they can be validated together. // This routine does individual property validation. // status = LkQuorumValidatePrivateResProperties( ResourceEntry, InBuffer, InBufferSize, ¶ms ); if ( status != ERROR_SUCCESS ) { ResUtilFreeParameterBlock( (LPBYTE) ¶ms, (LPBYTE) &ResourceEntry->Params, LkQuorumResourcePrivateProperties ); return(status); } // // Save the parameter values. // status = ResUtilSetPropertyParameterBlock( ResourceEntry->ParametersKey, LkQuorumResourcePrivateProperties, NULL, (LPBYTE) ¶ms, InBuffer, InBufferSize, (LPBYTE) &ResourceEntry->Params ); ResUtilFreeParameterBlock( (LPBYTE) ¶ms, (LPBYTE) &ResourceEntry->Params, LkQuorumResourcePrivateProperties ); if ( status == ERROR_SUCCESS ) { if ( ResourceEntry->State == ClusterResourceOnline ) { status = ERROR_RESOURCE_PROPERTIES_STORED; } else if ( ResourceEntry->State == ClusterResourceOnlinePending ) { status = ERROR_RESOURCE_PROPERTIES_STORED; } else { status = ERROR_SUCCESS; } } return(status); } // LkQuorumSetPrivateResProperties DWORD WINAPI LkQuorumArbitrate( RESID ResourceId, PQUORUM_RESOURCE_LOST LostQuorumResource ) /*++ Routine Description: Perform full arbitration for a disk. Once arbitration has succeeded, a thread is started that will keep reservations on the disk, one per second. Arguments: DiskResource - the disk info structure for the disk. Return Value: ERROR_SUCCESS if successful. A Win32 error code on failure. --*/ { DWORD status = ERROR_SUCCESS ; return status ; } DWORD WINAPI LkQuorumRelease( IN RESID Resource ) /*++ Routine Description: Release arbitration for a device by stopping the reservation thread. Arguments: Resource - supplies resource id to be brought online Return Value: ERROR_SUCCESS if successful. ERROR_HOST_NODE_NOT_OWNER if the resource is not owned. A Win32 error code if other failure. --*/ { DWORD status = ERROR_SUCCESS ; return status ; } DWORD LkQuorumGetDiskInfo( IN LPWSTR lpszPath, OUT PVOID * OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ) /*++ Routine Description: Gets all of the disk information for a given signature. Arguments: Signature - the signature of the disk to return info. OutBuffer - pointer to the output buffer to return the data. OutBufferSize - size of the output buffer. BytesReturned - the actual number of bytes that were returned (or the number of bytes that should have been returned if OutBufferSize is too small). Return Value: ERROR_SUCCESS if successful. A Win32 error on failure. --*/ { DWORD status; DWORD bytesReturned = *BytesReturned; PVOID ptrBuffer = *OutBuffer; PCLUSPROP_DWORD ptrDword; PCLUSPROP_PARTITION_INFO ptrPartitionInfo; DWORD letterIndex; DWORD letterCount = 1; WCHAR driveLetters[1]; LPWSTR pszExpandedPath = NULL; WCHAR chDrive; DWORD dwLength; // Return the signature - a DWORD bytesReturned += sizeof(CLUSPROP_DWORD); if ( bytesReturned <= OutBufferSize ) { ptrDword = (PCLUSPROP_DWORD)ptrBuffer; ptrDword->Syntax.dw = CLUSPROP_SYNTAX_DISK_SIGNATURE; ptrDword->cbLength = sizeof(DWORD); ptrDword->dw = 777;//return a bogus signature for now ptrDword++; ptrBuffer = ptrDword; } status = ERROR_SUCCESS; pszExpandedPath = ClRtlExpandEnvironmentStrings(lpszPath); if (!pszExpandedPath) { status = GetLastError(); goto FnExit; } //get a drive letter to fake the partition info //if the first char is drive letter, use that if ((lstrlenW(pszExpandedPath) > 2) && (pszExpandedPath[1] == L':')) { driveLetters[0] = pszExpandedPath[0]; } else { WCHAR lpszTmpPath[MAX_PATH]; DWORD dwStrLen; //the path name could not have a drive letter //it can point to a share \\xyz\abc dwStrLen = GetWindowsDirectoryW( lpszTmpPath, MAX_PATH ); if (!dwStrLen) { status = GetLastError(); goto FnExit; } driveLetters[0] = lpszTmpPath[0]; } for ( letterIndex = 0 ; letterIndex < letterCount ; letterIndex++ ) { bytesReturned += sizeof(CLUSPROP_PARTITION_INFO); if ( bytesReturned <= OutBufferSize ) { ptrPartitionInfo = (PCLUSPROP_PARTITION_INFO) ptrBuffer; ZeroMemory( ptrPartitionInfo, sizeof(CLUSPROP_PARTITION_INFO) ); ptrPartitionInfo->Syntax.dw = CLUSPROP_SYNTAX_PARTITION_INFO; ptrPartitionInfo->cbLength = sizeof(CLUSPROP_PARTITION_INFO) - sizeof(CLUSPROP_VALUE); if ( iswlower( driveLetters[letterIndex] ) ) { driveLetters[letterIndex] = towupper( driveLetters[letterIndex] ); } else { ptrPartitionInfo->dwFlags = CLUSPROP_PIFLAG_STICKY; } ptrPartitionInfo->dwFlags |= CLUSPROP_PIFLAG_USABLE; ptrPartitionInfo->dwFlags |= CLUSPROP_PIFLAG_DEFAULT_QUORUM; wsprintfW( ptrPartitionInfo->szDeviceName, L"%hc:\\", driveLetters[letterIndex] ); if ( !GetVolumeInformationW( ptrPartitionInfo->szDeviceName, ptrPartitionInfo->szVolumeLabel, sizeof(ptrPartitionInfo->szVolumeLabel)/sizeof(WCHAR), &ptrPartitionInfo->dwSerialNumber, &ptrPartitionInfo->rgdwMaximumComponentLength, &ptrPartitionInfo->dwFileSystemFlags, ptrPartitionInfo->szFileSystem, sizeof(ptrPartitionInfo->szFileSystem)/sizeof(WCHAR) ) ) { ptrPartitionInfo->szVolumeLabel[0] = L'\0'; } //set the partition name to the path //in future, siva wants to be able to provide an smb name here lstrcpy(ptrPartitionInfo->szDeviceName, pszExpandedPath); dwLength = lstrlenW(ptrPartitionInfo->szDeviceName); //this should not be terminated in a '\' if (ptrPartitionInfo->szDeviceName[dwLength-1] == L'\\') { ptrPartitionInfo->szDeviceName[dwLength-1] = L'\0'; } ptrPartitionInfo++; ptrBuffer = ptrPartitionInfo; } } // // Check if we got what we were looking for. // *OutBuffer = ptrBuffer; *BytesReturned = bytesReturned; FnExit: if (pszExpandedPath) LocalFree(pszExpandedPath); return(status); } // LkQuorumGetDiskInfo //*********************************************************** // // Define Function Table // //*********************************************************** CLRES_V1_FUNCTION_TABLE( LkQuorumFunctionTable, // Name CLRES_VERSION_V1_00, // Version LkQuorum, // Prefix LkQuorumArbitrate, // Arbitrate LkQuorumRelease, // Release LkQuorumResourceControl, // ResControl LkQuorumResourceTypeControl); // ResTypeControl