/*++ Copyright (c) 2000 Microsoft Corporation Module Name: MajorityNodeSet.c Abstract: Resource DLL for Majority Node Set (MajorityNodeSet). Author: Ahmed Mohamed (ahmedm) 12, 01, 2000 Revision History: George Potts (gpotts) 05, 17, 2001 Renamed from Node Quorum to Majority Node Set --*/ #pragma comment(lib, "clusapi.lib") #pragma comment(lib, "resutils.lib") #define UNICODE 1 #pragma warning( disable : 4115 ) // named type definition in parentheses #pragma warning( disable : 4201 ) // nonstandard extension used : nameless struct/union #pragma warning( disable : 4214 ) // nonstandard extension used : bit field types other than int #include #include #include #include #include #pragma warning( default : 4214 ) // nonstandard extension used : bit field types other than int #pragma warning( default : 4201 ) // nonstandard extension used : nameless struct/union #pragma warning( default : 4115 ) // named type definition in parentheses #include #include #include #include #include "clusres.h" #include "fsapi.h" #include "pipe.h" #include "crs.h" // for crssetforcedquorumsize() #include "clusrtl.h" // Enable MNS to auconfig, allowing MNS config parameters can be a security risk. // #define ENABLE_MNS_AUTOCONFIG_ONLY 1 // // Define a separate MNS resource Class. So that DTC doesn't try to use it a disk #573603. // Use a unique ID obtained from resclass@microsoft.com. // #define CLUS_RESCLASS_MAJORITY_NODE_SET 32775 // // Type and constant definitions. // #ifdef STANDALONE_DLL #define MajorityNodeSetDllEntryPoint DllEntryPoint #define MNS_RESNAME L"Majority Node Set" // 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 // Event Logging routine. #define g_LogEvent ClusResLogEvent // Resource Status routine for pending Online and Offline calls. #define g_SetResourceStatus ClusResSetResourceStatus #endif // end of standalone_DLL // ADDPARAM: Add new parameters here. #define PARAM_NAME__PATH L"Path" #define PARAM_NAME__ALLOWREMOTEACCESS L"AllowRemoteAccess" #define PARAM_NAME__DISKLIST L"DiskList" #define PARAM_MIN__ALLOWREMOTEACCESS (0) #define PARAM_MAX__ALLOWREMOTEACCESS (4294967295) #define PARAM_DEFAULT__ALLOWREMOTEACCESS (0) #define MUTEX_FILE_NAME L"MajorityNodeSet_FileMutex" // ADDPARAM: Add new parameters here. typedef struct _MNS_PARAMS { PWSTR Path; DWORD AllowRemoteAccess; PWSTR DiskList; DWORD DiskListSize; } MNS_PARAMS, *PMNS_PARAMS; // Once we have UNC support in service, we need to disable this flag // #define USE_DRIVE_LETTER 1 typedef struct _MNS_SETUP { LPWSTR Path; #ifdef USE_DRIVE_LETTER WCHAR DriveLetter[10]; #else #define DriveLetter Path #endif LPWSTR DiskList[FsMaxNodes]; DWORD DiskListSz; DWORD Nic; LPWSTR Transport; DWORD ArbTime; } MNS_SETUP, *PMNS_SETUP; typedef struct _MNS_RESOURCE { RESID ResId; // for validation MNS_PARAMS Params; HKEY ParametersKey; RESOURCE_HANDLE ResourceHandle; LPWSTR ResourceName; CLUS_WORKER OnlineThread; CLUS_WORKER ReserveThread; CLUSTER_RESOURCE_STATE State; PQUORUM_RESOURCE_LOST LostQuorumResource; CRITICAL_SECTION Lock; HANDLE ArbThread; HANDLE hMutexFile; PVOID PipeHdl; PVOID FsHdl; PVOID VolHdl; MNS_SETUP Setup; } MNS_RESOURCE, *PMNS_RESOURCE; #define MNS_ONLINE_PERIOD (4 * 1000) #define MNS_RESERVE_PERIOD (4 * 1000) // // Global data. // RESOURCE_HANDLE g_resHdl = 0; // Forward reference to our RESAPI function table. extern CLRES_FUNCTION_TABLE MajorityNodeSetFunctionTable; #ifdef ENABLE_MNS_AUTOCONFIG_ONLY RESUTIL_PROPERTY_ITEM MajorityNodeSetResourcePrivateProperties[] = {{ 0 }}; #else // // MajorityNodeSet resource read-write private properties. // RESUTIL_PROPERTY_ITEM MajorityNodeSetResourcePrivateProperties[] = { { PARAM_NAME__PATH, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, RESUTIL_PROPITEM_REQUIRED, FIELD_OFFSET(MNS_PARAMS,Path) }, { PARAM_NAME__ALLOWREMOTEACCESS, NULL, CLUSPROP_FORMAT_DWORD, PARAM_DEFAULT__ALLOWREMOTEACCESS, PARAM_MIN__ALLOWREMOTEACCESS, PARAM_MAX__ALLOWREMOTEACCESS, 0, FIELD_OFFSET(MNS_PARAMS,AllowRemoteAccess) }, { PARAM_NAME__DISKLIST, NULL, CLUSPROP_FORMAT_MULTI_SZ, 0, 0, 0, RESUTIL_PROPITEM_REQUIRED, FIELD_OFFSET(MNS_PARAMS,DiskList) }, { 0 } }; #endif #define MajorityNodeSetIoctlPhase1 CLUSCTL_USER_CODE(0, CLUS_OBJECT_RESOURCE) // // Function prototypes. // extern DWORD SetupIoctlQuorumResource(LPWSTR ResType, DWORD ControlCode); extern DWORD SetupDelete(IN LPWSTR Path); extern DWORD SetupStart(LPWSTR ResourceName, LPWSTR *SrvPath, LPWSTR *DiskList, DWORD *DiskListSize, DWORD *NicId, LPWSTR *Transport, DWORD *ArbTime); DWORD GetIDFromRegistry(IN HKEY hClusKey, IN LPWSTR resname, OUT LPWSTR *id); DWORD SetupShare(LPWSTR name, LPWSTR *lpath); extern DWORD SetupTree( IN LPTSTR TreeName, IN LPTSTR DlBuf, IN OUT DWORD *DlBufSz, IN LPTSTR TransportName OPTIONAL, IN LPVOID SecurityDescriptor OPTIONAL ); RESID WINAPI MajorityNodeSetOpen( IN LPCWSTR ResourceName, IN HKEY ResourceKey, IN RESOURCE_HANDLE ResourceHandle ); VOID WINAPI MajorityNodeSetClose( IN RESID ResourceId ); DWORD WINAPI MajorityNodeSetOnline( IN RESID ResourceId, IN OUT PHANDLE EventHandle ); DWORD WINAPI MajorityNodeSetOnlineThread( PCLUS_WORKER WorkerPtr, IN PMNS_RESOURCE ResourceEntry ); DWORD WINAPI MajorityNodeSetOffline( IN RESID ResourceId ); VOID WINAPI MajorityNodeSetTerminate( IN RESID ResourceId ); DWORD MajorityNodeSetDoTerminate( IN PMNS_RESOURCE ResourceEntry ); BOOL WINAPI MajorityNodeSetLooksAlive( IN RESID ResourceId ); BOOL WINAPI MajorityNodeSetIsAlive( IN RESID ResourceId ); BOOL MajorityNodeSetCheckIsAlive( IN PMNS_RESOURCE ResourceEntry ); DWORD WINAPI MajorityNodeSetResourceControl( IN RESID ResourceId, IN DWORD ControlCode, IN PVOID InBuffer, IN DWORD InBufferSize, OUT PVOID OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ); DWORD MajorityNodeSetGetPrivateResProperties( IN OUT PMNS_RESOURCE ResourceEntry, OUT PVOID OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ); DWORD MajorityNodeSetValidatePrivateResProperties( IN OUT PMNS_RESOURCE ResourceEntry, IN const PVOID InBuffer, IN DWORD InBufferSize, OUT PMNS_PARAMS Params ); DWORD MajorityNodeSetSetPrivateResProperties( IN OUT PMNS_RESOURCE ResourceEntry, IN const PVOID InBuffer, IN DWORD InBufferSize ); DWORD MajorityNodeSetGetDiskInfo( IN LPWSTR lpszPath, OUT PVOID *OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ) ; DWORD WINAPI MajorityNodeSetReserveThread( PCLUS_WORKER WorkerPtr, IN PMNS_RESOURCE ResourceEntry ); DWORD WINAPI MajorityNodeSetRelease( IN RESID ResourceId ); DWORD MajorityNodeSetReadDefaultValues( PMNS_RESOURCE ResourceEntry ); BOOLEAN WINAPI MajorityNodeSetDllEntryPoint( IN HINSTANCE DllHandle, IN DWORD Reason, IN LPVOID Reserved ) /*++ Routine Description: Main DLL entry point. Arguments: DllHandle - DLL instance handle. Reason - Reason for being called. Reserved - Reserved argument. Return Value: TRUE - Success. FALSE - Failure. --*/ { switch( Reason ) { case DLL_PROCESS_ATTACH: break; case DLL_PROCESS_DETACH: break; } return(TRUE); } // DllMain #ifdef STANDALONE_DLL 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 ( lstrcmpiW( ResourceType, MNS_RESNAME ) != 0 ) { (LogEvent)( NULL, LOG_ERROR, L"MajorityNodeSet: %1 %2.\n", ResourceType, MNS_RESNAME); return(ERROR_MOD_NOT_FOUND); } if ( !g_LogEvent ) { g_LogEvent = LogEvent; g_SetResourceStatus = SetResourceStatus; } *FunctionTable = &MajorityNodeSetFunctionTable; return(ERROR_SUCCESS); } // Startup #endif DWORD OpenMutexFileExclusive( IN RESOURCE_HANDLE ResourceHandle, IN LPWSTR Name, OUT HANDLE* pHandle ) { WCHAR fname[MAX_PATH]; DWORD Status; int ccLen = wcslen(Name); *pHandle = INVALID_HANDLE_VALUE; if ( ( Status = ClRtlGetClusterDirectory( fname, MAX_PATH - ccLen - 1) ) != ERROR_SUCCESS ) { (g_LogEvent)( ResourceHandle, LOG_ERROR, L"OpenMutexFileExclusive: Error %1!d! in getting cluster dir !!!\n", Status); goto exit_gracefully; } wcscat(fname, L"\\"); wcscat(fname, Name); *pHandle = CreateFile( fname, // file name GENERIC_READ | GENERIC_WRITE, // access mode 0, // No sharing whatsoever NULL, // SD CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | FILE_ATTRIBUTE_HIDDEN | FILE_FLAG_DELETE_ON_CLOSE, // file attributes NULL // handle to template file ); if (*pHandle == INVALID_HANDLE_VALUE) { Status = GetLastError(); (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Only one resource of MNS type is supported. Status: %1!u!.\n", Status ); } exit_gracefully: return Status; } RESID WINAPI MajorityNodeSetOpen( IN LPCWSTR ResourceName, IN HKEY ResourceKey, IN RESOURCE_HANDLE ResourceHandle ) /*++ Routine Description: Open routine for MajorityNodeSet 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 MajorityNodeSetStatup 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; DWORD disposition; RESID resid = 0; HKEY parametersKey = NULL; PMNS_RESOURCE resourceEntry = NULL; // // 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 = (PMNS_RESOURCE) LocalAlloc( LMEM_FIXED, sizeof(MNS_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(MNS_RESOURCE) ); resourceEntry->ResId = (RESID)resourceEntry; // for validation resourceEntry->ResourceHandle = ResourceHandle; resourceEntry->ParametersKey = parametersKey; resourceEntry->State = ClusterResourceOffline; resourceEntry->hMutexFile = INVALID_HANDLE_VALUE; // todo: get ride off this hack. See bug # 389483 if (g_resHdl == 0) g_resHdl = resourceEntry->ResourceHandle; // initialize lock InitializeCriticalSection(&resourceEntry->Lock); // // Save the name of the resource. // resourceEntry->ResourceName = LocalAlloc( LMEM_FIXED, (lstrlenW( ResourceName ) + 1) * sizeof(WCHAR) ); if ( resourceEntry->ResourceName == NULL ) { status = GetLastError(); goto exit; } lstrcpyW( resourceEntry->ResourceName, ResourceName ); status = OpenMutexFileExclusive(ResourceHandle, MUTEX_FILE_NAME, &resourceEntry->hMutexFile); if (status != ERROR_SUCCESS) { goto exit; } // // Setup stuff // memset(&resourceEntry->Setup, 0, sizeof(resourceEntry->Setup)); // // If we are the quorum, we need to make sure the share has been created. So, // we call setup now. // #if 0 { HKEY hClusKey=NULL; LPWSTR guid=NULL, lpath=NULL; status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, CLUSREG_KEYNAME_CLUSTER, 0, KEY_READ, &hClusKey); if (status != ERROR_SUCCESS) { goto setup_done; } status = GetIDFromRegistry(hClusKey, resourceEntry->ResourceName, &guid); if (status != ERROR_SUCCESS) { goto setup_done; } wcscat(guid, L"$"); status = SetupShare(guid, &lpath); setup_done: if (guid) { LocalFree(guid); } if (lpath) { LocalFree(lpath); } if (hClusKey) { RegCloseKey(hClusKey); } } (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Open share setup status %1!u!.\n", status); #else // read from private properties status = MajorityNodeSetReadDefaultValues(resourceEntry); if (status != ERROR_SUCCESS || resourceEntry->Setup.DiskListSz == 0) { // read from our own setup stuff status = SetupStart(resourceEntry->ResourceName, &resourceEntry->Setup.Path, resourceEntry->Setup.DiskList, &resourceEntry->Setup.DiskListSz, &resourceEntry->Setup.Nic, &resourceEntry->Setup.Transport, &resourceEntry->Setup.ArbTime); (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Open %1 setup status %2!u!.\n", ResourceName, status); } #endif // init fs if (status == ERROR_SUCCESS) { status = FsInit((PVOID)resourceEntry, &resourceEntry->FsHdl); (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Open %1 fs status %2!u!.\n", ResourceName, status); } // init pipe srv if (status == ERROR_SUCCESS) { status = PipeInit((PVOID)resourceEntry, resourceEntry->FsHdl, &resourceEntry->PipeHdl); (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Open %1 pipe status %2!u!.\n", ResourceName, status); } #ifdef ENABLE_SMB // init srv if (status == ERROR_SUCCESS) { status = SrvInit((PVOID)resourceEntry, resourceEntry->FsHdl, &resourceEntry->SrvHdl); (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Open %1 srv status %2!u!.\n", ResourceName, status); } #endif if (status == ERROR_SUCCESS) { resid = (RESID)resourceEntry; // // Startup for the resource. // } exit: (g_LogEvent)( ResourceHandle, LOG_INFORMATION, L"Open %1 status %2!u!.\n", ResourceName, status); if ( resid == 0 ) { if (resourceEntry) { if (g_resHdl == resourceEntry->ResourceHandle) { g_resHdl = NULL; } MajorityNodeSetClose((RESID)resourceEntry); } else if ( parametersKey != NULL ) { ClusterRegCloseKey( parametersKey ); } } if ( status != ERROR_SUCCESS ) { SetLastError( status ); } return(resid); } // MajorityNodeSetOpen VOID WINAPI MajorityNodeSetClose( IN RESID ResourceId ) /*++ Routine Description: Close routine for MajorityNodeSet 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. --*/ { PMNS_RESOURCE resourceEntry; resourceEntry = (PMNS_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { return; } if ( resourceEntry->ResId != ResourceId ) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"Close resource sanity check failed! ResourceId = %1!u!.\n", ResourceId ); return; } (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Close request.\n" ); // // Hack: Check if we are online, just return. This must be the RPC run-down stuff // if (resourceEntry->VolHdl && (FsIsOnlineReadonly(resourceEntry->VolHdl) == ERROR_SUCCESS)) return; // // Close the Parameters key. // if ( resourceEntry->ParametersKey ) { ClusterRegCloseKey( resourceEntry->ParametersKey ); } // // Sync any arb threads // if (resourceEntry->ArbThread) { WaitForSingleObject(resourceEntry->ArbThread, INFINITE); CloseHandle(resourceEntry->ArbThread); resourceEntry->ArbThread = NULL; } if (resourceEntry->PipeHdl) { PipeExit(resourceEntry->PipeHdl); } if (resourceEntry->FsHdl) { FsExit(resourceEntry->FsHdl); } // // Deallocate setup stuff // if (resourceEntry->Setup.Path) { LocalFree( resourceEntry->Setup.Path); } if (resourceEntry->Setup.DiskList) { DWORD i; for (i = 0; i < FsMaxNodes; i++) { if (resourceEntry->Setup.DiskList[i] != NULL) LocalFree(resourceEntry->Setup.DiskList[i]); } } if (resourceEntry->Setup.Transport) { LocalFree( resourceEntry->Setup.Transport); } // ADDPARAM: Add new parameters here. if ( resourceEntry->Params.Path ) LocalFree( resourceEntry->Params.Path ); if ( resourceEntry->Params.DiskList ) LocalFree( resourceEntry->Params.DiskList ); if ( resourceEntry->ResourceName ) LocalFree( resourceEntry->ResourceName ); if (resourceEntry->hMutexFile != INVALID_HANDLE_VALUE) { CloseHandle(resourceEntry->hMutexFile); } DeleteCriticalSection(&resourceEntry->Lock); LocalFree( resourceEntry ); g_resHdl = 0; // [HACKHACK] Assumes that there could be only one MNS resource } // MajorityNodeSetClose DWORD WINAPI MajorityNodeSetOnline( IN RESID ResourceId, IN OUT PHANDLE EventHandle ) /*++ Routine Description: Online routine for MajorityNodeSet 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: 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. 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. --*/ { PMNS_RESOURCE resourceEntry = NULL; DWORD status; resourceEntry = (PMNS_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { 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); } (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Online request.\n" ); resourceEntry->State = ClusterResourceOffline; ClusWorkerTerminate( &resourceEntry->OnlineThread ); status = ClusWorkerCreate( &resourceEntry->OnlineThread, (PWORKER_START_ROUTINE)MajorityNodeSetOnlineThread, resourceEntry ); if ( status != ERROR_SUCCESS ) { resourceEntry->State = ClusterResourceFailed; (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"Online: Unable to start thread, status %1!u!.\n", status ); } else { status = ERROR_IO_PENDING; } return(status); } // MajorityNodeSetOnline DWORD MajorityNodeSetReadDefaultValues(PMNS_RESOURCE ResourceEntry) { return ERROR_NOT_SUPPORTED; } // MajorityNodeSetReadDefaultValues DWORD MajorityNodeSetDoRegister(IN PMNS_RESOURCE ResourceEntry) { DWORD status = ERROR_SUCCESS; if (ResourceEntry->VolHdl == NULL) { // if we have no volume handle, read config now // read from private properties status = MajorityNodeSetReadDefaultValues(ResourceEntry); if ((status != ERROR_SUCCESS) || (ResourceEntry->Setup.DiskListSz == 0)) { // read from our own setup stuff status = SetupStart(ResourceEntry->ResourceName, &ResourceEntry->Setup.Path, ResourceEntry->Setup.DiskList, &ResourceEntry->Setup.DiskListSz, &ResourceEntry->Setup.Nic, &ResourceEntry->Setup.Transport, &ResourceEntry->Setup.ArbTime); } if (status == ERROR_SUCCESS) { LPWSTR ShareName, IpcName; // register volume ShareName = ResourceEntry->Setup.Path + 2; ShareName = wcschr(ShareName, L'\\'); ASSERT(ShareName); ShareName++; ASSERT(*ShareName != L'\0'); IpcName = ResourceEntry->Setup.DiskList[0]; if (IpcName == NULL) { // We use first replica. This must be the case when our private property is set IpcName = ResourceEntry->Setup.DiskList[1]; } ASSERT(IpcName); status = FsRegister(ResourceEntry->FsHdl, ShareName, // share name IpcName, // ipc local name ResourceEntry->Setup.DiskList, // replica set ResourceEntry->Setup.DiskListSz, // num of replicas ResourceEntry->Setup.ArbTime, &ResourceEntry->VolHdl); } } return status; } DWORD WINAPI MajorityNodeSetOnlineThread( PCLUS_WORKER WorkerPtr, IN PMNS_RESOURCE ResourceEntry ) /*++ Routine Description: Worker function which brings a resource from the resource table online. This function is executed in a separate thread. Arguments: WorkerPtr - Supplies the worker structure ResourceEntry - A pointer to the MNS_RESOURCE block for this resource. Returns: ERROR_SUCCESS - The operation completed successfully. Win32 error code - The operation failed. --*/ { RESOURCE_STATUS resourceStatus; DWORD i, status = ERROR_SUCCESS, status1; ASSERT(ResourceEntry != NULL); ResUtilInitializeResourceStatus( &resourceStatus ); resourceStatus.ResourceState = ClusterResourceFailed; resourceStatus.WaitHint = 0; resourceStatus.CheckPoint = 1; (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"onlinethread request.\n" ); // Get lock EnterCriticalSection(&ResourceEntry->Lock); // There are two cases here. If the MNS resource is the quorum, arbitration has // already been called, then try to avoid arbitrating again. To do this call into // FsIsOnlineReadWrite(). If MNS is not the quorum we would have to do arbitration. // // NOTE: Here we are trying to get MNS online. This is a bit different from just arbitrate // path. Arbitrate path is optimized to return ASAP, even before FspJoin completes, but here // we need to wait till FspJoin completes and then verify that the volume is in // VolumeStateOnlineReadWrite state. // status = MajorityNodeSetDoRegister(ResourceEntry); if (status == ERROR_SUCCESS) { HANDLE th; PVOID arb; HANDLE Cleanup; ASSERT(ResourceEntry->VolHdl); status = FsIsOnlineReadWrite(ResourceEntry->VolHdl); if (status != ERROR_SUCCESS) { // We need to start a new arbitration or else wait for the current arbitrate // thread to complete. // status = ERROR_SUCCESS; if (ResourceEntry->ArbThread != NULL) { // check if this is an old completed handle status1 = WaitForSingleObject(ResourceEntry->ArbThread, 0); if (status1 != WAIT_TIMEOUT) { CloseHandle(ResourceEntry->ArbThread); ResourceEntry->ArbThread = NULL; arb = FsArbitrate(ResourceEntry->VolHdl, &Cleanup, &ResourceEntry->ArbThread); if (arb == NULL) { status = GetLastError(); } else { // Set the cleanup event now, else the arbitrate thread would get // stuck forever. SetEvent(Cleanup); } } } else { arb = FsArbitrate(ResourceEntry->VolHdl, &Cleanup, &ResourceEntry->ArbThread); if (arb == NULL) { status = GetLastError(); } else { SetEvent(Cleanup); } } if (status == ERROR_SUCCESS) { // Now wait for the arbitrate thread to exit. // while (ResourceEntry->ArbThread != NULL) { // The ArbThread handle might be closed from other places, so duplicate it // instead of copying. // th = INVALID_HANDLE_VALUE; DuplicateHandle( GetCurrentProcess(), ResourceEntry->ArbThread, GetCurrentProcess(), &th, 0, FALSE, DUPLICATE_SAME_ACCESS); ASSERT(th != INVALID_HANDLE_VALUE); LeaveCriticalSection(&ResourceEntry->Lock); do { // inform rcmon that we are working resourceStatus.ResourceState = ClusterResourceOnlinePending; resourceStatus.CheckPoint++; g_SetResourceStatus( ResourceEntry->ResourceHandle, &resourceStatus ); (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"waiting for fs to online %1!u!.\n", status ); status = WaitForSingleObject(th, MNS_ONLINE_PERIOD); } while (status == WAIT_TIMEOUT); CloseHandle(th); EnterCriticalSection(&ResourceEntry->Lock); if (ResourceEntry->ArbThread != NULL) { status1 = WaitForSingleObject(ResourceEntry->ArbThread, 0); if (status1 != WAIT_TIMEOUT) { CloseHandle(ResourceEntry->ArbThread); ResourceEntry->ArbThread = NULL; } } } LeaveCriticalSection(&ResourceEntry->Lock); } else { LeaveCriticalSection(&ResourceEntry->Lock); } // arbitrate thread must have finished, check if we are online or not status = FsIsOnlineReadWrite(ResourceEntry->VolHdl); } else { LeaveCriticalSection(&ResourceEntry->Lock); } } else { // drop lock LeaveCriticalSection(&ResourceEntry->Lock); } if (status == ERROR_SUCCESS) { status = PipeOnline(ResourceEntry->PipeHdl, ResourceEntry->Setup.Path); } #ifdef ENABLE_SMB if (status == ERROR_SUCCESS) { LPWSTR SrvName; // Online server SrvName = ResourceEntry->Setup.Path + 2; status = SrvOnline(ResourceEntry->SrvHdl, SrvName, ResourceEntry->Setup.Nic); } // // Bring drive letter online // if (status == ERROR_SUCCESS) { PDWORD psz = NULL; #ifdef USE_DRIVE_LETTER DWORD sz; sz = sizeof(ResourceEntry->Setup.DriveLetter); psz = &sz; #endif // todo: create security descriptor and pass it onto tree status = SetupTree(ResourceEntry->Setup.Path, ResourceEntry->Setup.DriveLetter, psz, ResourceEntry->Setup.Transport, NULL); status = ERROR_SUCCESS; #ifdef USE_DRIVE_LETTERxx if (status == ERROR_DUP_NAME) status = ERROR_SUCCESS; #endif if (status == ERROR_SUCCESS) { #ifdef USE_DRIVE_LETTER ResourceEntry->Setup.DriveLetter[sz] = L'\0'; #endif (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Resource %1 mounted on drive %2.\n", ResourceEntry->ResourceName, ResourceEntry->Setup.DriveLetter); } else { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"failed to setup tree path %1 drive %2 %3!u!.\n", ResourceEntry->Setup.Path, ResourceEntry->Setup.DriveLetter, status); } } #endif if ( status != ERROR_SUCCESS ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Error %1!u! bringing resource online.\n", status ); resourceStatus.ResourceState = ClusterResourceFailed; } else { resourceStatus.ResourceState = ClusterResourceOnline; } // _ASSERTE(g_SetResourceStatus != NULL); g_SetResourceStatus( ResourceEntry->ResourceHandle, &resourceStatus ); ResourceEntry->State = resourceStatus.ResourceState; return(status); } // MajorityNodeSetOnlineThread DWORD WINAPI MajorityNodeSetOffline( IN RESID ResourceId ) /*++ Routine Description: Offline routine for MajorityNodeSet 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. --*/ { PMNS_RESOURCE resourceEntry; resourceEntry = (PMNS_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { 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); } (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Offline request.\n" ); // 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. // return MajorityNodeSetDoTerminate( resourceEntry ); } // MajorityNodeSetOffline VOID WINAPI MajorityNodeSetTerminate( IN RESID ResourceId ) /*++ Routine Description: Terminate routine for MajorityNodeSet 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. --*/ { PMNS_RESOURCE resourceEntry; resourceEntry = (PMNS_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { return; } if ( resourceEntry->ResId != ResourceId ) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"Terminate resource sanity check failed! ResourceId = %1!u!.\n", ResourceId ); return; } (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Terminate request.\n" ); // // Terminate the resource. // MajorityNodeSetDoTerminate( resourceEntry ); resourceEntry->State = ClusterResourceOffline; } // MajorityNodeSetTerminate DWORD MajorityNodeSetDoTerminate( IN PMNS_RESOURCE ResourceEntry ) /*++ Routine Description: Do the actual Terminate work for MajorityNodeSet 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; // Set the volume going away flag. FsSignalShutdown(ResourceEntry->VolHdl); // Get lock EnterCriticalSection(&ResourceEntry->Lock); // // wait for arb thread if any // (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"DoTerminate: Wait for any arbitration threads to finish.\n" ); while (ResourceEntry->ArbThread) { HANDLE th = ResourceEntry->ArbThread; // drop lock LeaveCriticalSection(&ResourceEntry->Lock); WaitForSingleObject(ResourceEntry->ArbThread, INFINITE); // Get lock EnterCriticalSection(&ResourceEntry->Lock); if (th == ResourceEntry->ArbThread) { CloseHandle(ResourceEntry->ArbThread); ResourceEntry->ArbThread = NULL; } } // Drop lock LeaveCriticalSection(&ResourceEntry->Lock); (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"DoTerminate: Now kill off any pending Online or Reserve threads.\n" ); // // Kill off any pending threads. // ClusWorkerTerminate( &ResourceEntry->OnlineThread ); ClusWorkerTerminate( &ResourceEntry->ReserveThread ); // // Terminate the resource. // (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Offlining server.\n" ); #ifdef ENABLE_SMB // Remove our network name now ASSERT(ResourceEntry->SrvHdl); SrvOffline(ResourceEntry->SrvHdl); #endif PipeOffline(ResourceEntry->PipeHdl); #ifdef ENABLE_SMB // disconnect network connection status = WNetCancelConnection2(ResourceEntry->Setup.DriveLetter, FALSE, TRUE); if (status != NO_ERROR) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Failed to disconnect '%1' err %2!d!.\n", ResourceEntry->Setup.DriveLetter, status); status = ERROR_SUCCESS; } else { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"DoTerminate: Server is now offline and connections cancelled.\n" ); } #endif // xxx: call our release since resmon won't do it MajorityNodeSetRelease((RESID)ResourceEntry); if ( status == ERROR_SUCCESS ) { ResourceEntry->State = ClusterResourceOffline; } return(status); } // MajorityNodeSetDoTerminate BOOL WINAPI MajorityNodeSetLooksAlive( IN RESID ResourceId ) /*++ Routine Description: LooksAlive routine for MajorityNodeSet 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. --*/ { PMNS_RESOURCE resourceEntry; resourceEntry = (PMNS_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { 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(MajorityNodeSetCheckIsAlive( resourceEntry )); } // MajorityNodeSetLooksAlive BOOL WINAPI MajorityNodeSetIsAlive( IN RESID ResourceId ) /*++ Routine Description: IsAlive routine for MajorityNodeSet 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. --*/ { PMNS_RESOURCE resourceEntry; resourceEntry = (PMNS_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { 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(MajorityNodeSetCheckIsAlive( resourceEntry )); } // MajorityNodeSetIsAlive BOOL MajorityNodeSetCheckIsAlive( IN PMNS_RESOURCE ResourceEntry ) /*++ Routine Description: Check to see if the resource is alive for MajorityNodeSet 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. --*/ { DWORD err; HANDLE vol; // // Check to see if the resource is alive. // if (ResourceEntry->State == ClusterResourceFailed) { return FALSE; } else { return TRUE; } // Not reliable in the new design. // Get lock EnterCriticalSection(&ResourceEntry->Lock); vol = ResourceEntry->VolHdl; // Drop lock LeaveCriticalSection(&ResourceEntry->Lock); if (vol) { err = FsIsOnlineReadonly(vol); } else { err = ERROR_INVALID_PARAMETER; } if (err != ERROR_SUCCESS && err != ERROR_IO_PENDING) { return FALSE; } return(TRUE); } // MajorityNodeSetCheckIsAlive ///////////////////////////////////////////////////////////////////////////// //++ // // MajorityNodeSetNameHandler // // Description: // Handle the CLUSCTL_RESOURCE_SET_NAME control code by saving the new // name of the resource. // // Arguments: // pResourceEntry [IN OUT] // Supplies the resource entry on which to operate. // // pszName [IN] // The new name of the resource. // // Return Value: // ERROR_SUCCESS // The function completed successfully. // // Win32 error code // The function failed. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD MajorityNodeSetNameHandler( IN OUT PMNS_RESOURCE pResourceEntry, IN LPWSTR pszName ) { DWORD nStatus = ERROR_SUCCESS; DWORD cbNameBuffer; HRESULT hr = S_OK; // // Save the name of the resource. // if (pResourceEntry->ResourceName != NULL) { LocalFree(pResourceEntry->ResourceName); } cbNameBuffer = (lstrlenW(pszName) + 1) * sizeof(WCHAR); pResourceEntry->ResourceName = LocalAlloc(LMEM_FIXED, cbNameBuffer); if (pResourceEntry->ResourceName == NULL) { nStatus = GetLastError(); (g_LogEvent)( pResourceEntry->ResourceHandle, LOG_ERROR, L"Failed to allocate memory for the new resource name '%1'. Error %2!u!.\n", pszName, nStatus ); goto Cleanup; } // if: error allocating memory for the name. lstrcpyW( pResourceEntry->ResourceName, pszName ); Cleanup: return nStatus; } //*** MajorityNodeSetNameHandler() ///////////////////////////////////////////////////////////////////////////// // // System Check Functions. // ///////////////////////////////////////////////////////////////////////////// DWORD WINAPI MajorityNodeSetResourceControl( 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 MajorityNodeSet 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; PMNS_RESOURCE resourceEntry; DWORD required; resourceEntry = (PMNS_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { 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_SET_NAME: if (InBuffer != NULL) { status = MajorityNodeSetNameHandler( resourceEntry, InBuffer ); } else { status = ERROR_INVALID_PARAMETER; } break; case CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTY_FMTS: status = ResUtilGetPropertyFormats( MajorityNodeSetResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } break; case CLUSCTL_RESOURCE_FORCE_QUORUM: *BytesReturned = 0; status = ERROR_SUCCESS; if (InBufferSize >= sizeof(CLUS_FORCE_QUORUM_INFO)) { PCLUS_FORCE_QUORUM_INFO p = (PCLUS_FORCE_QUORUM_INFO)InBuffer; DWORD mask = p->dwNodeBitMask; // count number of set bits. for (required = 0; mask != 0; mask = mask >> 1) { if (mask & 0x1) required++; } (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Setting quorum size = %1!u!.\n", required/2+1); // BugFix: 653110 CrsSetForcedQuorumSize((required/2)+1); } else { status = ERROR_INVALID_PARAMETER; } 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_DELETE_REQUIRES_ALL_NODES | CLUS_CHAR_REQUIRES_STATE_CHANGE_REASON; status = ERROR_SUCCESS; } break; case CLUSCTL_RESOURCE_STATE_CHANGE_REASON: status = ERROR_SUCCESS; if (InBufferSize == sizeof(CLUSCTL_RESOURCE_STATE_CHANGE_REASON_STRUCT)) { PCLUSCTL_RESOURCE_STATE_CHANGE_REASON_STRUCT pStateChangeReason=(PCLUSCTL_RESOURCE_STATE_CHANGE_REASON_STRUCT)InBuffer; // Do this only for rundown not shutdown. if (pStateChangeReason->eReason == eResourceStateChangeReasonRundown) { FsSignalShutdown(resourceEntry->VolHdl); } } else { status = ERROR_INVALID_PARAMETER; } 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_MAJORITY_NODE_SET; ptrResClassInfo->SubClass = (DWORD) CLUS_RESSUBCLASS_SHARED; status = ERROR_SUCCESS; } break; case CLUSCTL_RESOURCE_STORAGE_GET_DISK_INFO: // // If the local quorum drive letter cannot be found in the // path parameter, it defaults to "SystemDrive" environment // variable. // status = MajorityNodeSetGetDiskInfo(resourceEntry->Setup.DriveLetter, &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( MajorityNodeSetResourcePrivateProperties, (LPWSTR)OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } break; case CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES: status = MajorityNodeSetGetPrivateResProperties( resourceEntry, OutBuffer, OutBufferSize, BytesReturned ); break; case CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES: status = MajorityNodeSetValidatePrivateResProperties( resourceEntry, InBuffer, InBufferSize, NULL ); break; case CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES: status = MajorityNodeSetSetPrivateResProperties( resourceEntry, InBuffer, InBufferSize ); break; case CLUSCTL_RESOURCE_DELETE: // todo: we need to only do this if are using local defaults. // we need to remove our share and directory now if (resourceEntry->Setup.DiskList[0]) { // need to end IPC session in order to be able to delete // directory FsEnd(resourceEntry->FsHdl); status = SetupDelete(resourceEntry->Setup.DiskList[0]); } else { status = ERROR_INVALID_PARAMETER; } (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Delete ResourceId = %1 err %2!u!.\n", resourceEntry->ResourceName, status); break; // We need to find which node got added and adjust our // disklist. If we are online or we own quorum, then we need // to add this new replica to current filesystem set. case CLUSCTL_RESOURCE_INSTALL_NODE: case CLUSCTL_RESOURCE_EVICT_NODE: (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Recompute %1 quorum set changed, Install or Evict node = '%2'.\n", resourceEntry->ResourceName, (InBuffer ? InBuffer : L"")); // fall through case MajorityNodeSetIoctlPhase1: // we need to enumerate the current cluster and check it against // our disklist. we need to do this only if we actually have some if (1) { MNS_SETUP Setup; memset(&Setup, 0, sizeof(Setup)); status = SetupStart(resourceEntry->ResourceName, &Setup.Path, Setup.DiskList, &Setup.DiskListSz, &Setup.Nic, &Setup.Transport, &Setup.ArbTime); if (status != ERROR_SUCCESS) return status; EnterCriticalSection(&resourceEntry->Lock); if (resourceEntry->Setup.DiskListSz != Setup.DiskListSz) status = ERROR_INVALID_PARAMETER; else { DWORD i; for (i = 0; i < FsMaxNodes; i++) { if (Setup.DiskList[i] == NULL && resourceEntry->Setup.DiskList[i] == NULL) continue; if (Setup.DiskList[i] == NULL || resourceEntry->Setup.DiskList[i] == NULL) { status = ERROR_INVALID_PARAMETER; break; } if (wcscmp(Setup.DiskList[i], resourceEntry->Setup.DiskList[i])) { status = ERROR_INVALID_PARAMETER; break; } } } if (status != ERROR_SUCCESS && resourceEntry->VolHdl) { // Update ourself now status = FsUpdateReplicaSet(resourceEntry->VolHdl, Setup.DiskList, Setup.DiskListSz); if (status == ERROR_SUCCESS) { DWORD i; // we need to free the current disklist, careful with slot 0 if (Setup.DiskList[0]) LocalFree(Setup.DiskList[0]); for (i = 1; i < FsMaxNodes; i++) { if (resourceEntry->Setup.DiskList[i]) { LocalFree(resourceEntry->Setup.DiskList[i]); } resourceEntry->Setup.DiskList[i] = Setup.DiskList[i]; } resourceEntry->Setup.DiskListSz = Setup.DiskListSz; } // set new arb timeout value resourceEntry->Setup.ArbTime = Setup.ArbTime; (g_LogEvent)( resourceEntry->ResourceHandle, LOG_WARNING, L"Configuration change, new set size %1!u! status %2!u!.\n", Setup.DiskListSz, status ); } LeaveCriticalSection(&resourceEntry->Lock); // free stuff if (Setup.Path) LocalFree(Setup.Path); if (Setup.Transport) LocalFree(Setup.Transport); } break; default: status = ERROR_INVALID_FUNCTION; break; } return(status); } // MajorityNodeSetResourceControl DWORD WINAPI MajorityNodeSetResourceTypeControl( 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 MajorityNodeSet resources. Perform the control request specified by ControlCode. Arguments: ResourceTypeName - Supplies the name of the resource type. 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_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 = ERROR_INVALID_FUNCTION; DWORD required; switch ( ControlCode ) { case CLUSCTL_RESOURCE_TYPE_UNKNOWN: *BytesReturned = 0; status = ERROR_SUCCESS; break; case CLUSCTL_RESOURCE_TYPE_GET_ARB_TIMEOUT: { PCLUSPROP_DWORD ptrDword; DWORD bytesReturned; // Return the Arbitration Timeout value needed - 180. bytesReturned = sizeof(CLUSPROP_DWORD); *BytesReturned = bytesReturned; if ( bytesReturned <= OutBufferSize ) { ptrDword = (PCLUSPROP_DWORD)OutBuffer; ptrDword->Syntax.dw = CLUSPROP_SYNTAX_LIST_VALUE_DWORD; ptrDword->cbLength = sizeof(DWORD); ptrDword->dw = 180; status = ERROR_SUCCESS; } else { status = ERROR_MORE_DATA; } break; } case CLUSCTL_RESOURCE_TYPE_GET_PRIVATE_RESOURCE_PROPERTY_FMTS: status = ResUtilGetPropertyFormats( MajorityNodeSetResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } break; case CLUSCTL_RESOURCE_TYPE_ENUM_PRIVATE_PROPERTIES: status = ResUtilEnumProperties( MajorityNodeSetResourcePrivateProperties, (LPWSTR)OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } break; case CLUSCTL_RESOURCE_TYPE_STARTING_PHASE1: status = SetupIoctlQuorumResource(CLUS_RESTYPE_NAME_MAJORITYNODESET, MajorityNodeSetIoctlPhase1); break; case CLUSCTL_RESOURCE_TYPE_STARTING_PHASE2: status = ERROR_SUCCESS; break; default: status = ERROR_INVALID_FUNCTION; break; } return(status); } // MajorityNodeSetResourceTypeControl DWORD MajorityNodeSetGetPrivateResProperties( IN OUT PMNS_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 MajorityNodeSet. 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; status = ResUtilGetAllProperties( ResourceEntry->ParametersKey, MajorityNodeSetResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } return(status); } // MajorityNodeSetGetPrivateResProperties DWORD MajorityNodeSetValidatePrivateResProperties( IN OUT PMNS_RESOURCE ResourceEntry, IN PVOID InBuffer, IN DWORD InBufferSize, OUT PMNS_PARAMS Params ) /*++ Routine Description: Processes the CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES control function for resources of type MajorityNodeSet. 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 = ERROR_SUCCESS; MNS_PARAMS params; PMNS_PARAMS pParams; // // 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(MNS_PARAMS) ); status = ResUtilDupParameterBlock( (LPBYTE) pParams, (LPBYTE) &ResourceEntry->Params, MajorityNodeSetResourcePrivateProperties ); if ( status != ERROR_SUCCESS ) { return(status); } // // Parse and validate the properties. // status = ResUtilVerifyPropertyTable( MajorityNodeSetResourcePrivateProperties, NULL, TRUE, // AllowUnknownProperties InBuffer, InBufferSize, (LPBYTE) pParams ); if ( status == ERROR_SUCCESS ) { // // Validate the parameter values. // // TODO: Code to validate interactions between parameters goes here. // we need to validate that the user specified '\\srvname\share' if (pParams->Path != NULL) { if (pParams->Path[0] != L'\\' || pParams->Path[1] != L'\\' || lstrlenW(pParams->Path) < 3) { status = ERROR_INVALID_PARAMETER; } else { LPWSTR sharename; sharename = wcschr(&pParams->Path[2], L'\\'); if (sharename == NULL || sharename[1] == L'\0') { status = ERROR_INVALID_PARAMETER; } } } // we need to validate user specified disklist 'drive:\path or \\srv\path' if (pParams->DiskList != NULL) { DWORD cnt = 0, i, len; LPWSTR p; p = pParams->DiskList; for (i = 0; p != NULL && *p != L'\0' && i < pParams->DiskListSize; ) { // validate format as '\\srvname\share' if (p[0] == L'\\' && p[1] == L'\\' && p[2] != L'\0') { if (wcschr(&p[2],L'\\') == NULL) { status = ERROR_INVALID_PARAMETER; } } else if (p[0] == L'\0' || !iswalpha(p[0]) || p[1] != L':' || p[2] != L'\\') { status = ERROR_INVALID_PARAMETER; } if (status != ERROR_INVALID_PARAMETER) break; cnt++; len = wcslen(p) + 1; i += (len * sizeof(WCHAR)); p += len; } if (cnt == 0) status = ERROR_INVALID_PARAMETER; } } // // Cleanup our parameter block. // if ( pParams == ¶ms ) { ResUtilFreeParameterBlock( (LPBYTE) ¶ms, (LPBYTE) &ResourceEntry->Params, MajorityNodeSetResourcePrivateProperties ); } return status; } // MajorityNodeSetValidatePrivateResProperties DWORD MajorityNodeSetSetPrivateResProperties( IN OUT PMNS_RESOURCE ResourceEntry, IN PVOID InBuffer, IN DWORD InBufferSize ) /*++ Routine Description: Processes the CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES control function for resources of type MajorityNodeSet. 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; MNS_PARAMS params; // // Parse the properties so they can be validated together. // This routine does individual property validation. // ZeroMemory( ¶ms, sizeof(params)); status = MajorityNodeSetValidatePrivateResProperties( ResourceEntry, InBuffer, InBufferSize, ¶ms ); if ( status != ERROR_SUCCESS ) { ResUtilFreeParameterBlock( (LPBYTE) ¶ms, (LPBYTE) &ResourceEntry->Params, MajorityNodeSetResourcePrivateProperties ); return(status); } // // Save the parameter values. // status = ResUtilSetPropertyParameterBlock( ResourceEntry->ParametersKey, MajorityNodeSetResourcePrivateProperties, NULL, (LPBYTE) ¶ms, InBuffer, InBufferSize, (LPBYTE) &ResourceEntry->Params ); ResUtilFreeParameterBlock( (LPBYTE) ¶ms, (LPBYTE) &ResourceEntry->Params, MajorityNodeSetResourcePrivateProperties ); // // If the resource is online, return a non-success status. // // TODO: Modify the code below if your resource can handle // changes to properties while it is still online. 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; } // MajorityNodeSetSetPrivateResProperties DWORD WINAPI MajorityNodeSetReserveThread( PCLUS_WORKER WorkerPtr, IN PMNS_RESOURCE ResourceEntry ) /*++ Routine Description: Worker function which brings a resource from the resource table online. This function is executed in a separate thread. Arguments: WorkerPtr - Supplies the worker structure ResourceEntry - A pointer to the MNS_RESOURCE block for this resource. Returns: ERROR_SUCCESS - The operation completed successfully. Win32 error code - The operation failed. --*/ { DWORD status = ERROR_SUCCESS; PVOID vol; (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"MNS FsReserve thread start.\n" ); // // todo: this should wait on a notify port and listen for // 1- New nodes added to the cluster. // 2- Nodes removed from the cluster. // 3- Network priority changes. // 4- Network binding (added, removed, state) changes. // // check if we are being killed do { if (ResourceEntry->State != ClusterResourceFailed) { #if 0 vol=NULL; // don't hold any locks as not to hold back an arb EnterCriticalSection(&ResourceEntry->Lock); // Check if arbitration is going on, then don't do this. if (ResourceEntry->ArbThread != NULL) { DWORD err; err = WaitForSingleObject(ResourceEntry->ArbThread, 0); if (err != WAIT_TIMEOUT) { vol = ResourceEntry->VolHdl; } } else { vol = ResourceEntry->VolHdl; } LeaveCriticalSection(&ResourceEntry->Lock); #else vol = ResourceEntry->VolHdl; WaitForArbCompletion(vol); #endif if (vol) status = FsReserve(vol); } else { status = ERROR_INVALID_PARAMETER; } if (status == ERROR_SHUTDOWN_IN_PROGRESS) { goto error_exit; } else if (status != ERROR_SUCCESS) { PQUORUM_RESOURCE_LOST func; (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Reserve thread failed status %1!u!, resource '%2'.\n", status, ResourceEntry->ResourceName); #if 0 EnterCriticalSection(&ResourceEntry->Lock); func = ResourceEntry->LostQuorumResource; LeaveCriticalSection(&ResourceEntry->Lock); if (func) { func(ResourceEntry->ResourceHandle); } break; #endif } // Check every x seconds. // todo: need to make this a private property Sleep(MNS_RESERVE_PERIOD); } while(!ClusWorkerCheckTerminate(&ResourceEntry->ReserveThread)); error_exit: (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Reserve thread exiting, resource '%1' %2!u!.\n",ResourceEntry->ResourceName, status); return(status); } // MajorityNodeSetReserveThread DWORD WINAPI MajorityNodeSetArbitrate( 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; PMNS_RESOURCE ResourceEntry; PVOID arb; HANDLE Cleanup; ResourceEntry = (PMNS_RESOURCE)ResourceId; if ( ResourceEntry == NULL ) { return(FALSE); } if ( ResourceEntry->ResId != ResourceId ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"IsAlive sanity check failed! ResourceId = %1!u!.\n", ResourceId ); return(FALSE); } (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Arbitrate request.\n" ); // If an arbitration is in progress wait for it to complete and directly return // it's result. No need to rearbitrate. // if ((status = FsIsOnlineReadWrite(ResourceEntry->VolHdl)) == ERROR_SUCCESS) { goto Finally; } else if (IsArbInProgress(ResourceEntry->VolHdl)) { PVOID vol=ResourceEntry->VolHdl; WaitForArbCompletion(vol); status = FsIsOnlineReadWrite(vol); if (status == ERROR_SUCCESS) { goto Finally; } } // Get lock EnterCriticalSection(&ResourceEntry->Lock); status = MajorityNodeSetDoRegister(ResourceEntry); // If an arbitrate thread is active we need to wait for it to complete. while (ResourceEntry->ArbThread != NULL) { HANDLE th = ResourceEntry->ArbThread; LeaveCriticalSection(&ResourceEntry->Lock); WaitForSingleObject(th, INFINITE); EnterCriticalSection(&ResourceEntry->Lock); if (th == ResourceEntry->ArbThread) { CloseHandle(ResourceEntry->ArbThread); ResourceEntry->ArbThread = NULL; } } if (status == ERROR_SUCCESS) { // we wait for arbitration timeout in worse case DWORD delta = ResourceEntry->Setup.ArbTime; // NOTE: There might be another srbitrate thread active, but we cannot wait for that. // FsArbitrate() routine is reentrant (hopefully). ASSERT(ResourceEntry->VolHdl); if((arb = FsArbitrate(ResourceEntry->VolHdl, &Cleanup, &ResourceEntry->ArbThread)) == NULL) { status = GetLastError(); } else { status = FsCompleteArbitration(arb, delta); } } if (status == ERROR_SUCCESS) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Arb: status %1!u!.\n", status ); // we remember the callback and create a thread to monitor the quorum if we // don't have one already if (ResourceEntry->LostQuorumResource == NULL) { status = ClusWorkerCreate( &ResourceEntry->ReserveThread, (PWORKER_START_ROUTINE)MajorityNodeSetReserveThread, ResourceEntry ); } ResourceEntry->LostQuorumResource = LostQuorumResource; if ( status != ERROR_SUCCESS ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Arb: Unable to start thread, status %1!u!.\n", status ); } } // Drop lock LeaveCriticalSection(&ResourceEntry->Lock); Finally: (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Arbitrate request status %1!u!.\n",status); return status; } DWORD WINAPI MajorityNodeSetRelease( IN RESID ResourceId ) /*++ 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 ; PMNS_RESOURCE resourceEntry; resourceEntry = (PMNS_RESOURCE)ResourceId; again: (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Release request resource %1.\n",resourceEntry->ResourceName); // Get lock EnterCriticalSection(&resourceEntry->Lock); // clear callback and stop thread resourceEntry->LostQuorumResource = NULL; // Drop lock LeaveCriticalSection(&resourceEntry->Lock); // kill reserve thread ClusWorkerTerminate( &resourceEntry->ReserveThread ); // Get lock EnterCriticalSection(&resourceEntry->Lock); // clear callback and stop thread if (resourceEntry->LostQuorumResource != NULL) { // dam arb got called again goto again; } // issue FsRelease if (resourceEntry->VolHdl) { status = FsRelease(resourceEntry->VolHdl); if (status == ERROR_SUCCESS) resourceEntry->VolHdl = NULL; } // Drop lock LeaveCriticalSection(&resourceEntry->Lock); (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Release request status %1!u!.\n",status); return status ; } DWORD MajorityNodeSetGetDiskInfo( 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; // 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; if (g_resHdl) (g_LogEvent)( g_resHdl, LOG_INFORMATION, L"Expanded path '%1'\n",lpszPath); 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); // set flags // ptrPartitionInfo->dwFlags = CLUSPROP_PIFLAG_STICKY; ptrPartitionInfo->dwFlags |= CLUSPROP_PIFLAG_USABLE; // copy device name if (lpszPath[0] == L'\\') { wcscpy(ptrPartitionInfo->szDeviceName, lpszPath); wcscat(ptrPartitionInfo->szDeviceName, L"\\"); } else { ptrPartitionInfo->szDeviceName[0] = lpszPath[0]; ptrPartitionInfo->szDeviceName[1] = L':'; ptrPartitionInfo->szDeviceName[2] = L'\\'; ptrPartitionInfo->szDeviceName[3] = L'\0'; } #ifdef ENABLE_SMB if ( !GetVolumeInformationW( ptrPartitionInfo->szDeviceName, ptrPartitionInfo->szVolumeLabel, sizeof(ptrPartitionInfo->szVolumeLabel)/sizeof(WCHAR), &ptrPartitionInfo->dwSerialNumber, &ptrPartitionInfo->rgdwMaximumComponentLength, &ptrPartitionInfo->dwFileSystemFlags, ptrPartitionInfo->szFileSystem, sizeof(ptrPartitionInfo->szFileSystem)/sizeof(WCHAR) ) ) #endif { ptrPartitionInfo->szVolumeLabel[0] = L'\0'; } //set the partition name to the path, nothing to do if (ptrPartitionInfo->szDeviceName[0] == L'\\') wcscpy(ptrPartitionInfo->szDeviceName, lpszPath); else ptrPartitionInfo->szDeviceName[2] = L'\0'; ptrPartitionInfo++; ptrBuffer = ptrPartitionInfo; } // // Check if we got what we were looking for. // *OutBuffer = ptrBuffer; *BytesReturned = bytesReturned; return(status); } // MajorityNodeSetGetDiskInfo VOID MajorityNodeSetCallLostquorumCallback(PVOID arg) { PMNS_RESOURCE ResourceEntry=(PMNS_RESOURCE)arg; PQUORUM_RESOURCE_LOST func; if (!ResourceEntry) { return; } (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Lost quorum resource '%1'.\n", ResourceEntry->ResourceName); EnterCriticalSection(&ResourceEntry->Lock); func = ResourceEntry->LostQuorumResource; LeaveCriticalSection(&ResourceEntry->Lock); if (func) { FsSignalShutdown(ResourceEntry->VolHdl); func(ResourceEntry->ResourceHandle); } } //*********************************************************** // // Define Function Table // //*********************************************************** CLRES_V1_FUNCTION_TABLE( MajorityNodeSetFunctionTable, // Name CLRES_VERSION_V1_00, // Version MajorityNodeSet, // Prefix MajorityNodeSetArbitrate, // Arbitrate MajorityNodeSetRelease, // Release MajorityNodeSetResourceControl, // ResControl MajorityNodeSetResourceTypeControl); // ResTypeControl void msg_log(int level, char *buf, int cnt) { WCHAR wbuf[1024]; cnt = mbstowcs(wbuf, buf, cnt-1); wbuf[cnt] = L'\0'; if (g_resHdl) g_LogEvent(g_resHdl, level, L"%1\n", wbuf); } void WINAPI debug_log(char *format, ...) { va_list marker; char buf[1024]; int cnt; va_start(marker, format); cnt = vsprintf(buf, format, marker); msg_log(LOG_INFORMATION, buf, cnt); va_end(marker); } void WINAPI error_log(char *format, ...) { va_list marker; char buf[1024]; int cnt; va_start(marker, format); cnt = vsprintf(buf, format, marker); msg_log(LOG_ERROR, buf, cnt); va_end(marker); }