/*++

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);
}