/*++ Copyright (c) 1996-1997 Microsoft Corporation Module Name: ioctl.c Abstract: Resource and Resource Type control functions. Author: John Vert (jvert) 10/16/1996 Revision History: --*/ #include "fmp.h" #define LOG_MODULE IOCTL DWORD WINAPI FmResourceControl( IN PFM_RESOURCE Resource, IN PNM_NODE Node OPTIONAL, IN DWORD ControlCode, IN PUCHAR InBuffer, IN DWORD InBufferSize, OUT PUCHAR OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned, OUT LPDWORD Required ) /*++ Routine Description: Provides for arbitrary communication and control between an application and a specific instance of a resource. Arguments: Resource - Supplies the resource to be controlled. Node - Supplies the node on which the resource control should be delivered. If this is NULL, then if the owner is up, it is used. Else one of the other possible nodes is used. Else, one of the nodes that can support a resource of this type is used. ControlCode- Supplies the control code that defines the structure and action of the resource control. Values of ControlCode between 0 and 0x10000000 are reserved for future definition and use by Microsoft. All other values are available for use by ISVs InBuffer- Supplies a pointer to the input buffer to be passed to the resource. InBufferSize- Supplies the size, in bytes, of the data pointed to by lpInBuffer.. OutBuffer- Supplies a pointer to the output buffer to be filled in by the resource.. OutBufferSize- Supplies the size, in bytes, of the available space pointed to by lpOutBuffer. BytesReturned - Returns the number of bytes of lpOutBuffer actually filled in by the resource.. Required - Returns the number of bytes if the OutBuffer is not big enough. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { DWORD status; PNM_NODE node; PLIST_ENTRY pListEntry; //SS: dont require FM to be online, since these calls //can be made by the Open() call in resource dlls which //is called before the resource is online. //FmpMustBeOnline( ); // // TODO - we should verify the access mode - in the future! // if ( CLUSCTL_GET_CONTROL_OBJECT( ControlCode ) != CLUS_OBJECT_RESOURCE ) { return(ERROR_INVALID_FUNCTION); } // // Check if this is an internal, private control code. // if ( ControlCode & CLCTL_INTERNAL_MASK ) { return(ERROR_PRIVILEGE_NOT_HELD); } // // If a Node was specified, then ship the request off to that node. // if ( Node != NULL ) { if ( Node == NmLocalNode ) { status = FmpRmResourceControl( Resource, ControlCode, InBuffer, InBufferSize, OutBuffer, OutBufferSize, BytesReturned, Required ); } else { status = FmcResourceControl( Node, Resource, ControlCode, InBuffer, InBufferSize, OutBuffer, OutBufferSize, BytesReturned, Required ); } } else { PLIST_ENTRY pListEntry; PPOSSIBLE_ENTRY pPossibleEntry; pListEntry = &Resource->PossibleOwners; node = Node; // // If there is no supplied node, then use a possible node that is up. // for (pListEntry = pListEntry->Flink; pListEntry != &Resource->PossibleOwners; pListEntry = pListEntry->Flink) { pPossibleEntry = CONTAINING_RECORD(pListEntry, POSSIBLE_ENTRY, PossibleLinkage); // if Node is not given, then attempt to use a node that is // UP - giving preference to the group owner node node. node = pPossibleEntry->PossibleNode; if ( node == Resource->Group->OwnerNode ) { break; } if ( NmGetNodeState(node) != ClusterNodeUp ) { node = NULL; // try again } } //if no such node was found, find a node that can host this resource type if (!node) { PFM_RESTYPE pResType; PRESTYPE_POSSIBLE_ENTRY pResTypePosEntry; PNM_NODE prev_node = NULL; pResType = Resource->Type; // protect with the ResType lock ACQUIRE_SHARED_LOCK(gResTypeLock); pListEntry = &pResType->PossibleNodeList; // // If there is no supplied node, then use a possible node that is up. // for (pListEntry = pListEntry->Flink; pListEntry != &pResType->PossibleNodeList; pListEntry = pListEntry->Flink) { pResTypePosEntry = CONTAINING_RECORD(pListEntry, RESTYPE_POSSIBLE_ENTRY, PossibleLinkage); // if Node is not given, then attempt to use a node that is // UP - giving preference to the local node. node = pResTypePosEntry->PossibleNode; if ( node == NmLocalNode ) { break; } if ( NmGetNodeState(node) != ClusterNodeUp ) { node = NULL; // try again } else if (prev_node == NULL) prev_node = node; } RELEASE_LOCK(gResTypeLock); if(!node && prev_node) node=prev_node; } //if we still dont have a node, we have to throw up a failure if ( !node ) { // either the restype is not supported - or the supporting node is // not up! status = ERROR_CLUSTER_RESTYPE_NOT_SUPPORTED; return(status); } // // If we are the owner, then do the work, otherwise... // Ship the request off to the owner node. // if ( node == NmLocalNode ) { status = FmpRmResourceControl( Resource, ControlCode, InBuffer, InBufferSize, OutBuffer, OutBufferSize, BytesReturned, Required ); } else { status = FmcResourceControl( node, Resource, ControlCode, InBuffer, InBufferSize, OutBuffer, OutBufferSize, BytesReturned, Required ); } } return(status); } // FmResourceControl DWORD WINAPI FmResourceTypeControl( IN LPCWSTR ResourceTypeName, IN PNM_NODE Node OPTIONAL, IN DWORD ControlCode, IN PUCHAR InBuffer, IN DWORD InBufferSize, OUT PUCHAR OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned, OUT LPDWORD Required ) /*++ Routine Description: Provides for arbitrary communication and control between an application and a specific instance of a resource type. Arguments: ResourceTypeName - Supplies the name of the resource type to be controlled. Node - Supplies the node on which the resource control should be delivered. If this is NULL, the local node is used. ControlCode- Supplies the control code that defines the structure and action of the resource type control. Values of dwControlCode between 0 and 0x10000000 are reserved for future definition and use by Microsoft. All other values are available for use by ISVs InBuffer- Supplies a pointer to the input buffer to be passed to the resource. InBufferSize- Supplies the size, in bytes, of the data pointed to by lpInBuffer.. OutBuffer- Supplies a pointer to the output buffer to be filled in by the resource.. OutBufferSize- Supplies the size, in bytes, of the available space pointed to by lpOutBuffer. BytesReturned - Returns the number of bytes of lpOutBuffer actually filled in by the resource.. Required - Returns the number of bytes if the OutBuffer is not big enough. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { DWORD status; DWORD retry = TRUE; PNM_NODE node = NULL; PNM_NODE prev_node=NULL; PFM_RESTYPE pResType = NULL; FmpMustBeOnline( ); // // TODO - we should verify the access mode - in the future! // if ( CLUSCTL_GET_CONTROL_OBJECT( ControlCode ) != CLUS_OBJECT_RESOURCE_TYPE ) { status = ERROR_INVALID_FUNCTION; goto FnExit; } // // Check if this is an internal, private control code. // if ( ControlCode & CLCTL_INTERNAL_MASK ) { status = ERROR_PRIVILEGE_NOT_HELD; goto FnExit; } //find a node that can handle this resource type control pResType = OmReferenceObjectById(ObjectTypeResType, ResourceTypeName); if (!pResType) { status = ERROR_CLUSTER_RESOURCE_TYPE_NOT_FOUND; goto FnExit; } retry_search: prev_node = NULL; //if node wasnt specified choose a node if ( !Node ) { PLIST_ENTRY pListEntry; PRESTYPE_POSSIBLE_ENTRY pResTypePosEntry; // protect with the ResType lock ACQUIRE_SHARED_LOCK(gResTypeLock); pListEntry = &pResType->PossibleNodeList; // // If there is no supplied node, then use a possible node that is up. // for (pListEntry = pListEntry->Flink; pListEntry != &pResType->PossibleNodeList; pListEntry = pListEntry->Flink) { pResTypePosEntry = CONTAINING_RECORD(pListEntry, RESTYPE_POSSIBLE_ENTRY, PossibleLinkage); // if Node is not given, then attempt to use a node that is // UP - giving preference to the local node. node = pResTypePosEntry->PossibleNode; if ( node == NmLocalNode ) { break; } if ( NmGetNodeState(node) != ClusterNodeUp ) { node = NULL; // try again } else if (prev_node == NULL) prev_node = node; } RELEASE_LOCK(gResTypeLock); if(!node && prev_node) node=prev_node; // node should now contain a valid node to use or NULL! // if NULL, then let's see if the required ResDLL has been updated // on some other nodes. if ( !node && retry ) { retry = FALSE; ClRtlLogPrint(LOG_NOISE, "[FM] FmResourceTypeControl: No possible nodes for restype %1!ws!, " "calling FmpSetPossibleNodeForRestype\r\n", ResourceTypeName); FmpSetPossibleNodeForResType( ResourceTypeName, TRUE ); // ignore status goto retry_search; } // node should now contain a valid node to use or NULL! // if NULL, then it is hopeless! if ( !node ) { // either the restype is not supported - or the supporting node is // not up! status = ERROR_CLUSTER_RESTYPE_NOT_SUPPORTED; goto FnExit; } } else { // If the supplied node is on the list of possible nodes, then use it. // else return error if (!FmpInPossibleListForResType(pResType, Node)) { // either the restype is not supported - or the supporting node is // not up! status = ERROR_CLUSTER_RESTYPE_NOT_SUPPORTED; goto FnExit; } node = Node; } CL_ASSERT(node != NULL); if ( (node != NmLocalNode) && (NmGetNodeState(node) != ClusterNodeUp) ) { status = ERROR_HOST_NODE_NOT_AVAILABLE; goto FnExit; } // // If the node is remote, then ship the request off to that node, else // do the work locally. // if ( node == NmLocalNode ) { status = FmpRmResourceTypeControl( ResourceTypeName, ControlCode, InBuffer, InBufferSize, OutBuffer, OutBufferSize, BytesReturned, Required ); //if no node was specified and the local node doesnt support the resource //dll, remove it from the list and then retry if ((Node == NULL) && ((status == ERROR_MOD_NOT_FOUND) || (status == ERROR_PROC_NOT_FOUND))) { ClRtlLogPrint(LOG_NOISE, "[FM] FmResourceTypeControl: Removing Local Node from Possible Owners List for %1!ws! restype because of error %2!u! \r\n", ResourceTypeName,status); FmpRemovePossibleNodeForResType(ResourceTypeName, NmLocalNode); node = NULL; retry = FALSE; goto retry_search; } } else { status = FmcResourceTypeControl( node, ResourceTypeName, ControlCode, InBuffer, InBufferSize, OutBuffer, OutBufferSize, BytesReturned, Required ); if ((Node == NULL) && ((status == ERROR_MOD_NOT_FOUND) || (status == ERROR_PROC_NOT_FOUND))) { node = NULL; retry = FALSE; goto retry_search; } } FnExit: if (pResType) OmDereferenceObject(pResType); return(status); } // FmResourceTypeControl DWORD WINAPI FmGroupControl( IN PFM_GROUP Group, IN PNM_NODE Node OPTIONAL, IN DWORD ControlCode, IN PUCHAR InBuffer, IN DWORD InBufferSize, OUT PUCHAR OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned, OUT LPDWORD Required ) /*++ Routine Description: Provides for arbitrary communication and control between an application and a specific instance of a group. Arguments: Group - Supplies the group to be controlled. Node - Supplies the node on which the resource control should be delivered. If this is NULL, the node where the group is owned is used. ControlCode- Supplies the control code that defines the structure and action of the group control. Values of ControlCode between 0 and 0x10000000 are reserved for future definition and use by Microsoft. All other values are available for use by ISVs InBuffer- Supplies a pointer to the input buffer to be passed to the group. InBufferSize- Supplies the size, in bytes, of the data pointed to by lpInBuffer. OutBuffer- Supplies a pointer to the output buffer to be filled in by the group. OutBufferSize- Supplies the size, in bytes, of the available space pointed to by lpOutBuffer. BytesReturned - Returns the number of bytes of lpOutBuffer actually filled in by the group. Required - Returns the number of bytes if the OutBuffer is not big enough. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { DWORD status; FmpMustBeOnline( ); // // TODO - we should verify the access mode - in the future! // if ( CLUSCTL_GET_CONTROL_OBJECT( ControlCode ) != CLUS_OBJECT_GROUP ) { return(ERROR_INVALID_FUNCTION); } // // Check if this is an internal, private control code. // if ( ControlCode & CLCTL_INTERNAL_MASK ) { return(ERROR_PRIVILEGE_NOT_HELD); } // // If a Node was specified, then ship the request off to that node, else // // If we are the owner, then do the work, otherwise... // Ship the request off to the owner node. // if ( (Node != NULL) && (Node != NmLocalNode) ) { status = FmcGroupControl( Node, Group, ControlCode, InBuffer, InBufferSize, OutBuffer, OutBufferSize, BytesReturned, Required ); } else { CL_ASSERT( Group != NULL ); if ( (Node == NULL) && (Group->OwnerNode != NmLocalNode) ) { status = FmcGroupControl( Group->OwnerNode, Group, ControlCode, InBuffer, InBufferSize, OutBuffer, OutBufferSize, BytesReturned, Required ); } else { status = FmpGroupControl( Group, ControlCode, InBuffer, InBufferSize, OutBuffer, OutBufferSize, BytesReturned, Required); } } return(status); } // FmGroupControl DWORD FmpGroupControl( IN PFM_GROUP Group, IN DWORD ControlCode, IN PUCHAR InBuffer, IN DWORD InBufferSize, OUT PUCHAR OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned, OUT LPDWORD Required ) { CLUSPROP_BUFFER_HELPER props; DWORD bufSize; DWORD status; // // Handle any requests that must be done without locks helds. // switch ( ControlCode ) { case CLUSCTL_GROUP_GET_COMMON_PROPERTY_FMTS: status = ClRtlGetPropertyFormats( FmpGroupCommonProperties, OutBuffer, OutBufferSize, BytesReturned, Required ); break; case CLUSCTL_GROUP_GET_NAME: if ( OmObjectName( Group ) == NULL ) { return(ERROR_NOT_READY); } props.pb = OutBuffer; bufSize = (lstrlenW( OmObjectName( Group ) ) + 1) * sizeof(WCHAR); if ( bufSize > OutBufferSize ) { *Required = bufSize; *BytesReturned = 0; status = ERROR_MORE_DATA; } else { lstrcpyW( props.psz, OmObjectName( Group ) ); *BytesReturned = bufSize; *Required = 0; status = ERROR_SUCCESS; } return(status); case CLUSCTL_GROUP_GET_ID: if ( OmObjectId( Group ) == NULL ) { return(ERROR_NOT_READY); } props.pb = OutBuffer; bufSize = (lstrlenW( OmObjectId( Group ) ) + 1) * sizeof(WCHAR); if ( bufSize > OutBufferSize ) { *Required = bufSize; *BytesReturned = 0; status = ERROR_MORE_DATA; } else { lstrcpyW( props.psz, OmObjectId( Group ) ); *BytesReturned = bufSize; *Required = 0; status = ERROR_SUCCESS; } return(status); default: break; } FmpAcquireLocalGroupLock( Group ); status = FmpHandleGroupControl( Group, ControlCode, InBuffer, InBufferSize, OutBuffer, OutBufferSize, BytesReturned, Required ); FmpReleaseLocalGroupLock( Group ); if ( ((status == ERROR_SUCCESS) || (status == ERROR_RESOURCE_PROPERTIES_STORED)) && (ControlCode & CLCTL_MODIFY_MASK) ) { ClusterWideEvent( CLUSTER_EVENT_GROUP_PROPERTY_CHANGE, Group ); } return(status); } DWORD WINAPI FmpHandleGroupControl( IN PFM_GROUP Group, IN DWORD ControlCode, IN PUCHAR InBuffer, IN DWORD InBufferSize, OUT PUCHAR OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned, OUT LPDWORD Required ) /*++ Routine Description: Provides for arbitrary communication and control between an application and a specific instance of a group. Arguments: Group - Supplies the group to be controlled. ControlCode- Supplies the control code that defines the structure and action of the group control. Values of ControlCode between 0 and 0x10000000 are reserved for future definition and use by Microsoft. All other values are available for use by ISVs InBuffer- Supplies a pointer to the input buffer to be passed to the group. InBufferSize- Supplies the size, in bytes, of the data pointed to by lpInBuffer. OutBuffer- Supplies a pointer to the output buffer to be filled in by the group. OutBufferSize- Supplies the size, in bytes, of the available space pointed to by lpOutBuffer. BytesReturned - Returns the number of bytes of lpOutBuffer actually filled in by the group. Required - Returns the number of bytes if the OutBuffer is not big enough. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { DWORD status; switch ( ControlCode ) { case CLUSCTL_GROUP_UNKNOWN: *BytesReturned = 0; status = ERROR_SUCCESS; break; case CLUSCTL_GROUP_GET_FLAGS: status = FmpGroupGetFlags( Group, OutBuffer, OutBufferSize, BytesReturned, Required ); break; case CLUSCTL_GROUP_ENUM_COMMON_PROPERTIES: status = FmpGroupEnumCommonProperties( OutBuffer, OutBufferSize, BytesReturned, Required ); break; case CLUSCTL_GROUP_GET_RO_COMMON_PROPERTIES: status = FmpGroupGetCommonProperties( Group, TRUE, OutBuffer, OutBufferSize, BytesReturned, Required ); break; case CLUSCTL_GROUP_GET_COMMON_PROPERTIES: status = FmpGroupGetCommonProperties( Group, FALSE, OutBuffer, OutBufferSize, BytesReturned, Required ); break; case CLUSCTL_GROUP_VALIDATE_COMMON_PROPERTIES: status = FmpGroupValidateCommonProperties( Group, InBuffer, InBufferSize ); break; case CLUSCTL_GROUP_SET_COMMON_PROPERTIES: status = FmpGroupSetCommonProperties( Group, InBuffer, InBufferSize ); break; case CLUSCTL_GROUP_GET_RO_PRIVATE_PROPERTIES: if ( OutBufferSize < sizeof(DWORD) ) { *BytesReturned = 0; *Required = sizeof(DWORD); if ( OutBuffer == NULL ) { status = ERROR_SUCCESS; } else { status = ERROR_MORE_DATA; } } else { LPDWORD ptrDword = (LPDWORD) OutBuffer; *ptrDword = 0; *BytesReturned = sizeof(DWORD); status = ERROR_SUCCESS; } break; case CLUSCTL_GROUP_ENUM_PRIVATE_PROPERTIES: status = FmpGroupEnumPrivateProperties( Group, OutBuffer, OutBufferSize, BytesReturned, Required ); break; case CLUSCTL_GROUP_GET_PRIVATE_PROPERTIES: status = FmpGroupGetPrivateProperties( Group, OutBuffer, OutBufferSize, BytesReturned, Required ); break; case CLUSCTL_GROUP_VALIDATE_PRIVATE_PROPERTIES: status = FmpGroupValidatePrivateProperties( Group, InBuffer, InBufferSize ); break; case CLUSCTL_GROUP_SET_PRIVATE_PROPERTIES: status = FmpGroupSetPrivateProperties( Group, InBuffer, InBufferSize ); break; case CLUSCTL_GROUP_GET_CHARACTERISTICS: if ( OutBufferSize < sizeof(DWORD) ) { *BytesReturned = 0; *Required = sizeof(DWORD); if ( OutBuffer == NULL ) { status = ERROR_SUCCESS; } else { status = ERROR_MORE_DATA; } } else { *BytesReturned = sizeof(DWORD); *(LPDWORD)OutBuffer = 0; status = ERROR_SUCCESS; } break; default: status = ERROR_INVALID_FUNCTION; break; } return(status); } // FmpHandleGroupControl /**** @func DWORD | FmNetNameParseProperties| Updates the cluster name in the cluster database. @parm PUCHAR | InBuffer | A pointer to special property list. @parm DWORD | InBufferSize | The size of the InBuffer in bytes. @parm LPCWSTR | * ppszClusterName | A cluster name string is returned via this. @comm The string must be freed by the caller using LocalFree(). @rdesc returns ERROR_SUCCESS if successful in getting the cluster name from the private properties. @xref ****/ DWORD FmNetNameParseProperties( IN PUCHAR InBuffer, IN DWORD InBufferSize, OUT LPWSTR *ppszClusterName) { // // Find the Cluster Name property // *ppszClusterName = NULL; return (ClRtlpFindSzProperty( InBuffer, InBufferSize, CLUSREG_NAME_NET_NAME, ppszClusterName, TRUE )); } // FmNetNameParseProperties /**** @func DWORD | FmGetDiskInfoParseProperties| Updates the cluster name in the cluster database. @parm PUCHAR | InBuffer | A pointer to special property list. @parm DWORD | InBufferSize | The size of the InBuffer in bytes. @parm LPWSTR | pszPath | If this a null string, the first drive letter on the disk resource is returned, else you can validate a path of form "g:" on this storage class resource. @comm The string must be freed by the caller using LocalFree(). @rdesc returns ERROR_SUCCESS if successful in getting the cluster name from the private properties. @xref ****/ DWORD FmpGetDiskInfoParseProperties( IN PUCHAR InBuffer, IN DWORD InBufferSize, IN OUT LPWSTR pszPath) { DWORD status = ERROR_INVALID_PARAMETER; DWORD dwValueSize; CLUSPROP_BUFFER_HELPER props; PCLUSPROP_PARTITION_INFO pPartitionInfo; WCHAR szRootPath[MAX_PATH]; props.pb = InBuffer; szRootPath[0] = L'\0'; // // Set defaults in the parameter block. // // Loop through each property. while ( (InBufferSize > sizeof(CLUSPROP_SYNTAX)) && (props.pSyntax->dw != CLUSPROP_SYNTAX_ENDMARK) ) { // Get the size of this value and verify there is enough buffer left. dwValueSize = sizeof(*props.pValue) + ALIGN_CLUSPROP( props.pValue->cbLength ); if ( dwValueSize > InBufferSize ) { break; } if ( props.pSyntax->dw == CLUSPROP_SYNTAX_PARTITION_INFO ) { // Validate the data. There must be a device name. pPartitionInfo = props.pPartitionInfoValue; if ( (dwValueSize != sizeof(*pPartitionInfo)) || (pPartitionInfo->szDeviceName[0] == L'\0')) { break; } if (!(pPartitionInfo->dwFlags & CLUSPROP_PIFLAG_USABLE)) { //check that it is formatted with NTFS. //if it is not usable,skip to the next one goto SkipToNext; } if (pszPath[0] == L'\0') { // // Chittur Subbaraman (chitturs) - 12/12/2000 // // Save the first available NTFS partition if the user does not explicitly // indicate any partition in the SetClusterQuorumResource API. This path will be // returned in two cases. // // (1) This cluster is a Whistler-Win2K cluster and the quorum disk // is currently owned by the Win2K node. The Win2K disk resource does not // set the CLUSPROP_PIFLAG_DEFAULT_QUORUM flags and so we have to revert the // behavior of the SetClusterQuorumResource API to the old behavior. // // (2) A pre-Whistler third party implemented quorum resource is used in a // Whistler cluster. In this case, this resource may not support the // CLUSPROP_PIFLAG_DEFAULT_QUORUM flags and so we have to revert the // behavior of the SetClusterQuorumResource API to the old behavior. // if ( szRootPath[0] == L'\0' ) { lstrcpyW( szRootPath, pPartitionInfo->szDeviceName ); } // // See whether you can find a default quorum partition (one that is // larger than 50 MB and still the minimum among the usable partitions.) // if ( !( pPartitionInfo->dwFlags & CLUSPROP_PIFLAG_DEFAULT_QUORUM ) ) { goto SkipToNext; } // Construct a path from the device name. lstrcpyW( pszPath, pPartitionInfo->szDeviceName ); status = ERROR_SUCCESS; break; } else { // Construct a path from the device name. if (!lstrcmpiW( pszPath, pPartitionInfo->szDeviceName )) { status = ERROR_SUCCESS; break; } } } SkipToNext: InBufferSize -= dwValueSize; props.pb += dwValueSize; } // // No path was found. However, a usable path got saved. So, use this saved path. // if ( ( status != ERROR_SUCCESS ) && ( szRootPath[0] != L'\0' ) ) { lstrcpyW( pszPath, szRootPath ); ClRtlLogPrint(LOG_NOISE, "[FM] FmpGetDiskInfoParseProperties: Using saved path %1!ws!...\n", pszPath); status = ERROR_SUCCESS; } return(status); } // FmpGetDiskInfoParseProperties DWORD FmpBroadcastDeleteControl( IN PFM_RESOURCE Resource ) /*++ Routine Description: Broadcasts a resource control to each node that notifies it that the resource is being deleted. Arguments: Resource - Supplies the resource that is being deleted. Return Value: ERROR_SUCCESS if successful. A Win32 error code on failure. --*/ { DWORD status; DWORD characteristics; DWORD i; PNM_NODE Node; // // Only perform the broadcast if delete notification is required. // Otherwise, just perform the notification on the local node. // status = FmpRmResourceControl( Resource, CLUSCTL_RESOURCE_GET_CHARACTERISTICS, NULL, 0, (PUCHAR)&characteristics, sizeof(DWORD), NULL, NULL ); if ( (status != ERROR_SUCCESS) || !(characteristics & CLUS_CHAR_DELETE_REQUIRES_ALL_NODES) ) { // // Note: the following 'local node only' notification is fairly useless. // FmpRmResourceControl( Resource, CLUSCTL_RESOURCE_DELETE, NULL, 0, NULL, 0, NULL, NULL ); return(ERROR_SUCCESS); } // // All nodes must be up in the cluster in order to perform this operation. // for ( i = ClusterMinNodeId; i <= NmMaxNodeId; i++ ) { Node = NmReferenceNodeById(i); if ( Node != NULL ) { if ( NmGetNodeState(Node) != ClusterNodeUp ) { return(ERROR_ALL_NODES_NOT_AVAILABLE); } } } // // Passed all checks, now broadcast to all nodes in the cluster. // for ( i = ClusterMinNodeId; i <= NmMaxNodeId; i++ ) { // // If this is the local node, do the ioctl directly // if (i == NmLocalNodeId) { FmpRmResourceControl( Resource, CLUSCTL_RESOURCE_DELETE, NULL, 0, NULL, 0, NULL, NULL ); } else { Node = NmReferenceNodeById(i); if ((Node != NULL) && (NmGetNodeState(Node) == ClusterNodeUp)) { CL_ASSERT(Session[i] != NULL); FmcResourceControl( Node, Resource, CLUSCTL_RESOURCE_DELETE, NULL, 0, NULL, 0, NULL, NULL ); OmDereferenceObject(Node); } } } return(ERROR_SUCCESS); } // FmpBroadcastDeleteControl DWORD FmpBroadcastDependencyChange( IN PFM_RESOURCE Resource, IN LPCWSTR DependsOnId, IN BOOL Remove ) /*++ Routine Description: Broadcasts a resource control to each node that notifies it that the resource has had a dependency added or removed. Arguments: Resource - Supplies the resource that has had the dependency added or removed DependsOnId - Supplies the id of the provider resource Remove - TRUE indicates that the dependency is being removed FALSE indicates that the dependency is being added. Return Value: ERROR_SUCCESS if successful. A Win32 error code on failure. --*/ { DWORD i; PNM_NODE Node; DWORD Control; DWORD Length; PFM_RESOURCE providerResource; if (Remove) { Control = CLUSCTL_RESOURCE_REMOVE_DEPENDENCY; } else { Control = CLUSCTL_RESOURCE_ADD_DEPENDENCY; } // // Get the provider resource. // providerResource = OmReferenceObjectById( ObjectTypeResource, DependsOnId ); if ( providerResource == NULL ) { return(ERROR_RESOURCE_NOT_FOUND); } Length = (lstrlenW(OmObjectName(providerResource)) + 1) * sizeof(WCHAR); // // Broadcast to all nodes in the cluster. // for ( i = ClusterMinNodeId; i <= NmMaxNodeId; i++ ) { // // If this is the local node, do the ioctl directly // if (i == NmLocalNodeId) { FmpRmResourceControl( Resource, Control, (PUCHAR)OmObjectName(providerResource), Length, NULL, 0, NULL, NULL ); } else { Node = NmReferenceNodeById(i); if ((Node != NULL) && (NmGetNodeState(Node) == ClusterNodeUp)) { CL_ASSERT(Session[i] != NULL); FmcResourceControl( Node, Resource, Control, (PUCHAR)OmObjectName(providerResource), Length, NULL, 0, NULL, NULL ); OmDereferenceObject(Node); } } } OmDereferenceObject( providerResource ); return(ERROR_SUCCESS); } // FmpBroadcastDeleteControl /**** @func DWORD | FmpGetResourceCharacteristics| Gets the characteristics for a given resource. @parm IN PFM_RESOURCE | pResource | Points to a FM_RESOURCE. @parm OUT LPDWORD | pdwCharacteristics | The ID of the dead node. @comm This is used to get the quorum characteristics during join since local quorums cant support multi-node clusters. @rdesc Returns ERROR_SUCCESS. ****/ DWORD FmpGetResourceCharacteristics( IN PFM_RESOURCE pResource, OUT LPDWORD pdwCharacteristics) { DWORD dwStatus; dwStatus = FmpRmResourceControl( pResource, CLUSCTL_RESOURCE_GET_CHARACTERISTICS, NULL, 0, (PUCHAR)pdwCharacteristics, sizeof(DWORD), NULL, NULL ); SetLastError(dwStatus); return (dwStatus); }