/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    recv.c

Abstract:

    Cluster FM remote receive request routines.

Author:

    Rod Gamache (rodga) 21-Mar-1996


Revision History:


--*/

#include "fmp.h"

#define LOG_MODULE RECV

//
// Global data
//

extern BOOL FmpOkayToProceed;

//
// Local function prototypes
//
BOOL
FmpEnumMyGroups(
    IN OUT PGROUP_ENUM *Enum,
    IN LPDWORD Allocated,
    IN PFM_GROUP Group,
    IN LPCWSTR Id
    );

BOOL
FmpEnumResources(
    IN OUT PRESOURCE_ENUM *Enum,
    IN LPDWORD Allocated,
    IN PFM_RESOURCE Resource,
    IN LPCWSTR Id
    );


error_status_t
s_FmsOnlineGroupRequest(
    IN handle_t IDL_handle,
    IN LPCWSTR GroupId
    )

/*++

Routine Description:

    Receives a Group Online Request from (THE) remote system and returns
    status for that request.

Arguments:

    IDL_handle - The binding context - not used.
    GroupId - The Id of the Group to bring online.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code on error.

--*/

{
    PFM_GROUP group;
    DWORD status;

    FmpMustBeOnline( );

    ClRtlLogPrint(LOG_NOISE,
               "[FM] FmsOnlineGroupRequest: To bring group '%1!ws!' online\n",
               GroupId);

    //
    // Find the specified group.
    //

    group = OmReferenceObjectById( ObjectTypeGroup, GroupId );

    if ( group == NULL ) {
        ClRtlLogPrint(LOG_NOISE,
                   "[FM] FmsOnlineGroupRequest: Could not find Group %1!ws!\n",
                   GroupId);
        return(ERROR_GROUP_NOT_FOUND);
    }

    //
    // Only one of these requests per group at a time.
    //
    FmpAcquireLocalGroupLock( group );


    //
    // Now bring it online.
    //
    if ( group->OwnerNode == NmLocalNode ) {
        //
        // Set the Group's Current State.
        //
        FmpSetGroupPersistentState( group, ClusterGroupOnline );

        status = FmpOnlineGroup( group, TRUE );
    } else {
        status = ERROR_HOST_NODE_NOT_RESOURCE_OWNER;
    }

    FmpReleaseLocalGroupLock( group );

    OmDereferenceObject( group );

    return(status);

} // FmsOnlineGroupRequest



error_status_t
s_FmsOfflineGroupRequest(
    IN handle_t IDL_handle,
    IN LPCWSTR GroupId
    )

/*++

Routine Description:

    Receives a Group Offline Request from (THE) remote system and returns
    status for that request.

Arguments:

    IDL_handle - The binding context - not used.
    GroupId - The Id of the Group to bring offline.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code on error.

--*/

{
    PFM_GROUP group;
    DWORD status;

    FmpMustBeOnline( );

    ClRtlLogPrint(LOG_NOISE,
               "[FM] FmsOfflineGroupRequest: To take group '%1!ws!' offline\n",
               GroupId);

    //
    // Find the specified group.
    //

    group = OmReferenceObjectById( ObjectTypeGroup, GroupId );

    if ( group == NULL ) {
        ClRtlLogPrint(LOG_NOISE,
                   "[FM] FmsOfflineGroupRequest: Could not find Group %1!ws!\n",
                   GroupId);
        return(ERROR_GROUP_NOT_FOUND);
    }

    //
    // Now take it offline if we are the owner.
    //
    if ( group->OwnerNode == NmLocalNode ) {
        //
        // Set the Group's Current State.
        //
        FmpSetGroupPersistentState( group, ClusterGroupOffline );

        status = FmpOfflineGroup( group, FALSE, TRUE );
    } else {
        status = ERROR_HOST_NODE_NOT_RESOURCE_OWNER;
    }

    OmDereferenceObject( group );

    return(status);

} // FmsOfflineGroupRequest



error_status_t
s_FmsMoveGroupRequest(
    IN handle_t IDL_handle,
    IN LPCWSTR GroupId,
    IN LPCWSTR DestinationNode OPTIONAL
    )

/*++

Routine Description:

    Receives a Group Move Request from (THE) remote system and returns
    status for that request.

Arguments:

    IDL_handle - The binding context - not used.
    GroupId - The Id of the Group to move.
    DestinationNode - The Id of the node to move the Group to.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code on error.

--*/

{
    PFM_GROUP group;
    PNM_NODE node = NULL;
    DWORD status;

    FmpMustBeOnline( );

    ClRtlLogPrint(LOG_NOISE,
               "[FM] FmsMoveGroupRequest: To move group '%1!ws!'\n",
               GroupId);

    //
    //
    // Find the specified group.
    //

    group = OmReferenceObjectById( ObjectTypeGroup, GroupId );

    if ( group == NULL ) {
        ClRtlLogPrint(LOG_NOISE,
                   "[FM] FmsMoveGroupRequest: Could not find Group %1!ws!\n",
                   GroupId);
        return(ERROR_GROUP_NOT_FOUND);
    }

    //
    // Find the specified destination node.
    //

    if ( ARGUMENT_PRESENT( DestinationNode ) ) {
        node = OmReferenceObjectById( ObjectTypeNode, DestinationNode );

        if ( node == NULL ) {
            OmDereferenceObject( group );
            ClRtlLogPrint(LOG_NOISE,"[FM] FmsMoveGroupRequest: Could not find Node %1!ws!\n", DestinationNode);
            return(ERROR_HOST_NODE_NOT_AVAILABLE);
        }
    }

    //
    // Make sure we are the owner of the Group.
    //
    FmpAcquireLocalGroupLock( group );
    if ( group->OwnerNode == NmLocalNode ) {
        status = FmpDoMoveGroup( group, node, TRUE );
    } else {
        status = ERROR_HOST_NODE_NOT_RESOURCE_OWNER;
    }
    FmpReleaseLocalGroupLock( group );

    OmDereferenceObject( group );
    if ( node != NULL ) {
        OmDereferenceObject( node );
    }

    return(status);

} // FmsMoveGroupRequest



error_status_t
s_FmsTakeGroupRequest(
    IN handle_t IDL_handle,
    IN LPCWSTR GroupId,
    IN PRESOURCE_ENUM ResourceList
    )

/*++

Routine Description:

    Receives a Take Group Request from (THE) remote system and returns
    status for that request.

Arguments:

    IDL_handle - The binding context - not used.
    GroupId - The Id of the Group to take locally.
    ResourceList - The list of resources and their states.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code on error.

--*/

{
    PFM_GROUP group;
    DWORD status = ERROR_SUCCESS;

    //SS: removing this check from here 
    //FmpTakeGroupRequest does this check since if this call returns a failure,
    //the intended owner needs to be reset to invalidnode to avoid inconsistencies
    //FmpMustBeOnline( );

    ClRtlLogPrint(LOG_NOISE,
               "[FM] FmsTakeGroupRequest: To take group '%1!ws!'.\n",
               GroupId );

    //
    //
    // Find the specified group.
    //

    group = OmReferenceObjectById( ObjectTypeGroup, GroupId );

    if ( group == NULL ) {
        ClRtlLogPrint(LOG_NOISE,
                   "[FM] FmsTakeGroupRequest: Could not find Group %1!ws!\n",
                   GroupId);
        if ( !FmpFMOnline ) {
            return(ERROR_HOST_NODE_NOT_AVAILABLE);
        }
        return(ERROR_GROUP_NOT_FOUND);
    }

    status = FmpTakeGroupRequest(group, ResourceList);
    OmDereferenceObject(group);

    return(status);

} // FmsTakeGroupRequest



error_status_t
s_FmsOnlineResourceRequest(
    IN handle_t IDL_handle,
    IN LPCWSTR ResourceId
    )

/*++

Routine Description:

    Receives a Resource Online Request from (THE) remote system and returns
    status for that request.

Arguments:

    IDL_handle - The binding context - not used.
    ResourceId - The Id of the Resource to bring online.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code on error.

--*/

{
    PFM_RESOURCE resource;
    DWORD status;

    FmpMustBeOnline( );

    ClRtlLogPrint(LOG_NOISE,
               "[FM] FmsOnlineResourceRequest: To bring resource '%1!ws!' online\n",
               ResourceId);

    //
    // Find the specified resource.
    //
    resource = OmReferenceObjectById( ObjectTypeResource, ResourceId );

    if ( resource == NULL ) {
        ClRtlLogPrint(LOG_NOISE,
                  "[FM] FmsOnlineResourceRequest: Could not find Resource %1!ws!\n",
                  ResourceId);
        return(ERROR_RESOURCE_NOT_FOUND);
    }

    FmpAcquireLocalResourceLock( resource );

    if (!(resource->QuorumResource) && 
        !FmpInPreferredList( resource->Group, resource->Group->OwnerNode, TRUE, resource ) ) {
        status = ERROR_HOST_NODE_NOT_RESOURCE_OWNER;
        goto FnExit;
    }

    //
    // Now bring it online.
    //
    CL_ASSERT( resource->Group != NULL );
    if ( resource->Group->OwnerNode == NmLocalNode ) {
        //
        // This can only be invoked through the API, so force all
        // resources online.
        //
        status = FmOnlineResource( resource );
    } else {
        status = ERROR_HOST_NODE_NOT_RESOURCE_OWNER;
    }

FnExit:
    FmpReleaseLocalResourceLock( resource );

    OmDereferenceObject( resource );

    return(status);

} // FmsOnlineResourceRequest



error_status_t
s_FmsOfflineResourceRequest(
    IN handle_t IDL_handle,
    IN LPCWSTR ResourceId
    )

/*++

Routine Description:

    Receives a Resource Offline Request from (THE) remote system and returns
    status for that request.

Arguments:

    IDL_handle - The binding context - not used.
    ResourceId - The Id of the Resource to bring offline.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code on error.

--*/

{
    PFM_RESOURCE resource;
    DWORD status;

    FmpMustBeOnline( );

    ClRtlLogPrint(LOG_NOISE,
              "[FM] FmsOfflineResourceRequest: To take resource '%1!ws!' offline\n",
              ResourceId);

    //
    // Find the specified resource.
    //

    resource = OmReferenceObjectById( ObjectTypeResource, ResourceId );

    if ( resource == NULL ) {
        ClRtlLogPrint(LOG_NOISE,
                  "[FM] FmsOfflineResourceRequest: Could not find Resource %1!ws!\n",
                  ResourceId);
        return(ERROR_RESOURCE_NOT_FOUND);
    }

    FmpAcquireLocalResourceLock(resource);
    //
    // Now take it offline if we are the owner.
    //
    CL_ASSERT( resource->Group != NULL );
    if ( resource->Group->OwnerNode != NmLocalNode ) 
    {
        status = ERROR_HOST_NODE_NOT_RESOURCE_OWNER;
        goto FnExit;
    } 
    //else handle it locally
    status = FmOfflineResource( resource );

FnExit:
    FmpReleaseLocalResourceLock(resource);
    OmDereferenceObject( resource );

    return(status);

} // FmsOfflineResourceRequest



error_status_t
s_FmsChangeResourceNode(
    IN handle_t IDL_handle,
    IN LPCWSTR ResourceId,
    IN LPCWSTR NodeId,
    IN BOOL Add
    )

/*++

Routine Description:

    Receives a Resource change node request from a remote system and returns
    status for that request.

Arguments:

    IDL_handle - The binding context - not used.
    ResourceId - The Id of the Resource to change a node.
    NodeId - The node id of the node to add or remove.
    Add - Indicates whether to add or remove the node.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code on error.

--*/

{
    PFM_RESOURCE resource;
    DWORD        status;

    FmpMustBeOnline( );

    ClRtlLogPrint(LOG_NOISE,
               "[FM] FmsChangeResourceNode: To %1!ws! node %2!ws! to/from resource '%3!ws!'.\n",
               Add ? L"Add" : L"Remove",
               NodeId,
               ResourceId);

    //
    // Find the specified resource.
    //
    resource = OmReferenceObjectById( ObjectTypeResource, ResourceId );

    if ( resource == NULL ) {
        ClRtlLogPrint(LOG_NOISE,
                  "[FM] FmsChangeResourceNode: Could not find Resource %1!ws!\n",
                  ResourceId);
        return(ERROR_RESOURCE_NOT_FOUND);
    }

    FmpAcquireLocalResourceLock( resource );

    status = FmpChangeResourceNode(resource, NodeId, Add);
    FmpReleaseLocalResourceLock( resource );
    OmDereferenceObject( resource );

    return(status);

} // FmsChangeResourceNode



error_status_t
s_FmsArbitrateResource(
    IN handle_t IDL_handle,
    IN LPCWSTR ResourceId
    )

/*++

Routine Description:

    Arbitrates a Resource for a remote system.

Arguments:

    IDL_handle - The binding context - not used.
    ResourceId - The Id of the Resource to bring online.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code on error.

--*/

{
    PFM_RESOURCE resource;
    DWORD status;

    //
    // Find the specified resource.
    //
    resource = OmReferenceObjectById( ObjectTypeResource, ResourceId );

    if ( resource == NULL ) {
        ClRtlLogPrint(LOG_NOISE,
                  "[FM] FmsArbitrateResource: Could not find Resource %1!ws!\n",
                  ResourceId);
        return(ERROR_RESOURCE_NOT_FOUND);
    }

    status = FmpRmArbitrateResource( resource );

    OmDereferenceObject( resource );

    return(status);

} // FmsArbitrateResource



error_status_t
s_FmsQueryOwnedGroups(
    IN handle_t IDL_handle,
    OUT PGROUP_ENUM *OwnedGroups,
    OUT PRESOURCE_ENUM *OwnedResources
    )
/*++

Routine Description:

    Server side used to propagate FM state to a joining node.

Arguments:

    IDL_handle - Supplies RPC binding handle, not used.

    OwnedGroups - Returns the list of groups owned by this node and
        their state.

    OwnedResources - Returns the list of resources contained by groups
        owned by this node and their state.

Return Value:

    ERROR_SUCCESS if successful

    Win32 error otherwise.

--*/

{
    DWORD           status = ERROR_SUCCESS;
    DWORD           allocated;
    PGROUP_ENUM     groupEnum = NULL;
    PFM_GROUP       group;
    PRESOURCE_ENUM  resourceEnum = NULL;
    PFM_RESOURCE    resource;
    DWORD i;

    allocated = ENUM_GROW_SIZE;

    groupEnum = MIDL_user_allocate(GROUP_SIZE(allocated));
    if ( groupEnum == NULL ) {
        CL_LOGFAILURE(ERROR_NOT_ENOUGH_MEMORY);
        return(ERROR_NOT_ENOUGH_MEMORY);
    }

    groupEnum->EntryCount = 0;
    //
    // Enumerate all groups
    //
    OmEnumObjects( ObjectTypeGroup,
                   FmpEnumMyGroups,
                   &groupEnum,
                   &allocated );

    //
    // Enumerate all the resources in each group.
    //
    allocated = ENUM_GROW_SIZE;
    resourceEnum = MIDL_user_allocate(RESOURCE_SIZE(allocated));
    if (resourceEnum == NULL) {
        status = ERROR_NOT_ENOUGH_MEMORY;
        CL_LOGFAILURE(status);
        goto error_exit;
    }
    resourceEnum->EntryCount = 0;

    for (i=0; i < groupEnum->EntryCount; i++) {
        //
        // Get the group given its name.
        //
        group = OmReferenceObjectById( ObjectTypeGroup,
                                       groupEnum->Entry[i].Id );
        if (group == NULL) {
            continue;
        }

        //
        // Enumerate all the resources in this group.
        //
        status = FmpEnumerateGroupResources(group,
                                  FmpEnumResources,
                                  &resourceEnum,
                                  &allocated);
        if ( status != ERROR_SUCCESS ) {
            ClRtlLogPrint(LOG_NOISE,
                       "[FM] FmsQueryOwnedGroups: Failed group '%1!ws!', status %2!u!.\n",
                       OmObjectId(group),
                       status);
        }
        OmDereferenceObject(group);
    }

    *OwnedGroups = groupEnum;
    *OwnedResources = resourceEnum;

    return(ERROR_SUCCESS);

error_exit:
    if (groupEnum != NULL) {
        //
        // Free up group enum
        //
        for (i=0; i < groupEnum->EntryCount; i++) {
            MIDL_user_free(groupEnum->Entry[i].Id);
        }
        MIDL_user_free(groupEnum);
    }
    return(status);
}


error_status_t
s_FmsFailResource(
    IN handle_t IDL_handle,
    IN LPCWSTR ResourceId
    )

/*++

Routine Description:

    Receives a Resource Fail Request from a remote system and returns
    status for that request.

Arguments:

    IDL_handle - The binding context - not used.
    ResourceId - The Id of the Resource to fail.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code on error.

--*/

{
    PFM_RESOURCE resource;
    DWORD status;

    FmpMustBeOnline( );

    ClRtlLogPrint(LOG_NOISE,
               "[FM] FmsFailResource: To fail resource '%1!ws!'.\n",
               ResourceId);

    //
    // Find the specified resource.
    //
    resource = OmReferenceObjectById( ObjectTypeResource, ResourceId );

    if ( resource == NULL ) {
        ClRtlLogPrint(LOG_NOISE,
                  "[FM] FmsFailResource: Could not find Resource %1!ws!\n",
                  ResourceId);
        return(ERROR_RESOURCE_NOT_FOUND);
    }

    FmpAcquireLocalResourceLock( resource );

    //
    // Now fail it.
    //
    if ( resource->Group->OwnerNode == NmLocalNode ) {
        status = FmpRmFailResource( resource );
    } else {
        status = ERROR_HOST_NODE_NOT_RESOURCE_OWNER;
    }

    FmpReleaseLocalResourceLock( resource );

    return(status);

} // FmsFailResource


error_status_t
s_FmsCreateResource(
    IN handle_t IDL_handle,
    IN LPCWSTR GroupId,
    IN LPWSTR ResourceId,
    IN LPCWSTR ResourceName
    )

/*++

Routine Description:

    Receives a Create Resource Request from a remote system and returns
    status for that request.

Arguments:

    IDL_handle - The binding context - not used.
    GroupId - The Id of the Group to create the resource inside.
    ResourceId - The Id of the Resource to create.
    ResourceName - The name of the Resource to create.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code on error.

Notes:

    The Resource lock is acquired to synchronize access to the resource. This
    satisfies locking the resource on all nodes in the cluster... so long
    as the local node is the owner of the resource.

--*/

{
    PFM_GROUP group;
    DWORD status;
    PGUM_CREATE_RESOURCE gumResource;
    DWORD groupIdLen;
    DWORD resourceIdLen;
    DWORD resourceNameLen;
    DWORD bufSize;
    HDMKEY resourceKey;
    HDMKEY paramsKey;
    DWORD  disposition;

    ClRtlLogPrint(LOG_NOISE,
               "[FM] FmsCreateResource: To create resource '%1!ws!'\n",
               ResourceId);

    //
    // Find the specified group.
    //
    group = OmReferenceObjectById( ObjectTypeGroup,
                                   GroupId );

    if ( group == NULL ) {
        ClRtlLogPrint(LOG_NOISE,
                  "[FM] FmsCreateResource: Could not find Group %1!ws!\n",
                  GroupId);
        return(ERROR_GROUP_NOT_FOUND);
    }

    FmpAcquireLocalGroupLock( group );

    //
    // Now delete it on all nodes in the cluster if we are the owner.
    //
    if ( group->OwnerNode == NmLocalNode ) {
        //
        // Allocate a message buffer.
        //
        groupIdLen = (lstrlenW(GroupId)+1) * sizeof(WCHAR);
        resourceIdLen = (lstrlenW(ResourceId)+1) * sizeof(WCHAR);
        resourceNameLen = (lstrlenW(ResourceName)+1) * sizeof(WCHAR);
        bufSize = sizeof(GUM_CREATE_RESOURCE) - sizeof(WCHAR) +
                  groupIdLen + resourceIdLen + resourceNameLen;
        gumResource = LocalAlloc(LMEM_FIXED, bufSize);
        if (gumResource == NULL) {
            CsInconsistencyHalt( ERROR_NOT_ENOUGH_MEMORY );
            return(ERROR_NOT_ENOUGH_MEMORY);
        }

        //
        // Fill in message buffer.
        //
        gumResource->Resource = NULL;
        gumResource->GroupIdLen = groupIdLen;
        gumResource->ResourceIdLen = resourceIdLen;
        CopyMemory(gumResource->GroupId, GroupId, groupIdLen);
        CopyMemory((PCHAR)gumResource->GroupId + groupIdLen,
                   ResourceId,
                   resourceIdLen);
        CopyMemory((PCHAR)gumResource->GroupId + groupIdLen + resourceIdLen,
                   ResourceName,
                   resourceNameLen);

        //
        // Send message.
        //
        status = GumSendUpdate(GumUpdateFailoverManager,
                               FmUpdateCreateResource,
                               bufSize,
                               gumResource);
        if ( ( status == ERROR_SUCCESS ) && 
             ( gumResource->Resource != NULL ) )
            FmpCleanupPossibleNodeList(gumResource->Resource);                               
        LocalFree(gumResource);
    } else {
        status = ERROR_HOST_NODE_NOT_RESOURCE_OWNER;
    }

    FmpReleaseLocalGroupLock( group );

    return(status);

} // FmsCreateResource


error_status_t
s_FmsDeleteResource(
    IN handle_t IDL_handle,
    IN LPCWSTR ResourceId
    )

/*++

Routine Description:

    Receives a Delete Resource Request from a remote system and returns
    status for that request.

Arguments:

    IDL_handle - The binding context - not used.
    ResourceId - The Id of the Resource to delete.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code on error.

Notes:

    The Resource lock is acquired to synchronize access to the resource. This
    satisfies locking the resource on all nodes in the cluster... so long
    as the local node is the owner of the resource.

--*/

{
    PFM_RESOURCE resource;
    DWORD status;
    DWORD resourceLen;

    FmpMustBeOnline();

    ClRtlLogPrint(LOG_NOISE,
               "[FM] FmsDeleteResource: To delete resource '%1!ws!'\n",
               ResourceId);

    //
    // Find the specified resource.
    //
    resource = OmReferenceObjectById( ObjectTypeResource,
                                      ResourceId );

    if ( resource == NULL ) {
        ClRtlLogPrint(LOG_NOISE,
                  "[FM] FmsDeleteResource: Could not find Resource %1!ws!\n",
                  ResourceId);
        return(ERROR_RESOURCE_NOT_FOUND);
    }

    FmpAcquireLocalResourceLock( resource );

    //
    // Now delete it on all nodes in the cluster if we are the owner.
    //
    if ( resource->Group->OwnerNode == NmLocalNode ) {
        //
        // Check if this is the quorum resource.
        //
        if ( resource->QuorumResource ) {
            status =  ERROR_QUORUM_RESOURCE;
            goto FnExit;
        }

        //other core resources cannot be deleted either
        if (resource->ExFlags & CLUS_FLAG_CORE)
        {
            status = ERROR_CORE_RESOURCE;
            goto FnExit;
        }


        //
        // Check the state of the resource, before attempting to delete it.
        // It must be offline or failed in order to perform the delete.
        //
        if ((resource->State != ClusterResourceOffline) &&
            (resource->State != ClusterResourceFailed)) {
            status = ERROR_RESOURCE_ONLINE;
            goto FnExit;
        }

        //
        // Check whether this resource provides for any other resources.
        // If so, it cannot be deleted.
        //
        if (!IsListEmpty(&resource->ProvidesFor)) {
            status = ERROR_DEPENDENT_RESOURCE_EXISTS;
            goto FnExit;
        }

        if (resource->Group->MovingList)
        {
            status = ERROR_INVALID_STATE;
            goto FnExit;
        }
        
        resourceLen = (lstrlenW(ResourceId)+1) * sizeof(WCHAR);

        FmpBroadcastDeleteControl(resource);
        //
        // Send message.
        //
        status = GumSendUpdateEx(GumUpdateFailoverManager,
                                 FmUpdateDeleteResource,
                                 1,
                                 resourceLen,
                                 ResourceId);
    } else {
    
        status = ERROR_HOST_NODE_NOT_RESOURCE_OWNER;
        
    }


FnExit:
    FmpReleaseLocalResourceLock( resource );
    return(status);

} // FmsDeleteResource


BOOL
FmpEnumMyGroups(
    IN OUT PGROUP_ENUM *Enum,
    IN LPDWORD Allocated,
    IN PFM_GROUP Group,
    IN LPCWSTR Id
    )

/*++

Routine Description:

    Worker callback routine for the enumeration of Groups.
    This routine adds the specified Group to the list that is being
    generated if it is owned by the local system.

Arguments:

    Enum - The Group Enumeration list. Can be an output if a new list is
            allocated.
    Allocated - The current number of allocated entries in Enum.
    Group - The Group object being enumerated.
    Id - The Id of the Group object being enumerated.

Returns:

    TRUE - to indicate that the enumeration should continue.

--*/

{
    PGROUP_ENUM groupEnum;
    PGROUP_ENUM newEnum;
    DWORD newAllocated;
    DWORD index;
    LPWSTR newId;
    CLUSTER_GROUP_STATE state;

    //
    // If we don't own the Group, return now.
    //
    if (Group->OwnerNode != NmLocalNode) {
        return(TRUE);
    }

    groupEnum = *Enum;

    if ( groupEnum->EntryCount >= *Allocated ) {
        //
        // Time to grow the GROUP_ENUM
        //

        newAllocated = *Allocated + ENUM_GROW_SIZE;
        newEnum = MIDL_user_allocate(GROUP_SIZE(newAllocated));
        if ( newEnum == NULL ) {
            return(FALSE);
        }

        CopyMemory(newEnum, groupEnum, GROUP_SIZE(*Allocated));
        *Allocated = newAllocated;
        *Enum = newEnum;
        MIDL_user_free(groupEnum);
        groupEnum = newEnum;
    }

    //
    // Initialize new entry
    //
    newId = MIDL_user_allocate((lstrlenW(Id)+1) * sizeof(WCHAR));
    if ( newId == NULL ) {
        return(FALSE);
    }

    lstrcpyW(newId, Id);
    groupEnum->Entry[groupEnum->EntryCount].Id = newId;
    groupEnum->Entry[groupEnum->EntryCount].State = Group->State;
    groupEnum->Entry[groupEnum->EntryCount].StateSequence = Group->StateSequence;
    ++groupEnum->EntryCount;

    return(TRUE);

} // FmpEnumMyGroups


BOOL
FmpEnumResources(
    IN OUT PRESOURCE_ENUM *Enum,
    IN LPDWORD Allocated,
    IN PFM_RESOURCE Resource,
    IN LPCWSTR Id
    )

/*++

Routine Description:

    Worker callback routine for the enumeration of Group resources.
    This routine adds the specified resource to the list that is being
    generated.

Arguments:

    Enum - The resource enumeration list. Can be an output if a new list is
            allocated.
    Allocated - The current number of allocated entries in Enum.
    Resource - The Resource object being enumerated.
    Id - The Id of the resource object being enumerated.

Returns:

    TRUE - to indicate that the enumeration should continue.
    FALSE - to indicate that the enumeration should not continue.

--*/

{
    PRESOURCE_ENUM resourceEnum;
    PRESOURCE_ENUM newEnum;
    DWORD newAllocated;
    DWORD index;
    LPWSTR newId;

    resourceEnum = *Enum;

    if ( resourceEnum->EntryCount >= *Allocated ) {
        //
        // Time to grow the RESOURCE_ENUM
        //
        newAllocated = *Allocated + ENUM_GROW_SIZE;
        newEnum = MIDL_user_allocate(RESOURCE_SIZE(newAllocated));
        if ( newEnum == NULL ) {
            return(FALSE);
        }

        CopyMemory(newEnum, resourceEnum, RESOURCE_SIZE(*Allocated));
        *Allocated = newAllocated;
        *Enum = newEnum;
        MIDL_user_free(resourceEnum);
        resourceEnum = newEnum;
    }

    //
    // Initialize new entry
    //
    newId = MIDL_user_allocate((lstrlenW(Id)+1) * sizeof(WCHAR));
    if ( newId == NULL ) {
        return(FALSE);
    }

    lstrcpyW(newId, Id);
    resourceEnum->Entry[resourceEnum->EntryCount].Id = newId;
    resourceEnum->Entry[resourceEnum->EntryCount].State = Resource->State;
    resourceEnum->Entry[resourceEnum->EntryCount].StateSequence = Resource->StateSequence;
    ++resourceEnum->EntryCount;

    return(TRUE);

} // FmpEnumResources



error_status_t
s_FmsResourceControl(
    IN handle_t IDL_handle,
    IN LPCWSTR ResourceId,
    IN DWORD ControlCode,
    IN PUCHAR InBuffer,
    IN DWORD InBufferSize,
    OUT PUCHAR OutBuffer,
    IN DWORD OutBufferSize,
    OUT LPDWORD BytesReturned,
    OUT LPDWORD Required
    )

/*++

Routine Description:

    This routine passes a resource control request from a remote system.

Arguments:

    IDL_handle - the binding context - not used

    ResourceId - the Id of the Resource to control

    ControlCode - the control code for this request

    InBuffer - the input buffer

    InBufferSize - the size of the input buffer

    OutBuffer - the output buffer

    OutBufferSize - the size of the output buffer

    ByteReturned - the number of bytes returned in the output buffer

    Required - the number of bytes required if OutBuffer is not big enough.

Returns:

    ERROR_SUCCESS if successful.

    Win32 error code on failure.

--*/

{
    PFM_RESOURCE resource;
    DWORD status;
    DWORD bufSize;
    DWORD dataLength;
    CLUSPROP_BUFFER_HELPER props;

    //
    // Find the specified resource.
    //
    resource = OmReferenceObjectById( ObjectTypeResource, ResourceId );

    if ( resource == NULL ) {
        ClRtlLogPrint(LOG_NOISE,
                  "[FM] FmsResourceControl: Could not find Resource %1!ws!\n",
                  ResourceId);
        return(ERROR_RESOURCE_NOT_FOUND);
    }

    CL_ASSERT( resource->Group != NULL );

    status = FmpRmResourceControl( resource,
                                   ControlCode,
                                   InBuffer,
                                   InBufferSize,
                                   OutBuffer,
                                   OutBufferSize,
                                   BytesReturned,
                                   Required
                                   );
    OmDereferenceObject(resource);

    return(status);
} // FmsResourceControl



error_status_t
s_FmsResourceTypeControl(
    IN handle_t IDL_handle,
    IN LPCWSTR ResourceTypeName,
    IN DWORD ControlCode,
    IN PUCHAR InBuffer,
    IN DWORD InBufferSize,
    OUT PUCHAR OutBuffer,
    IN DWORD OutBufferSize,
    OUT LPDWORD BytesReturned,
    OUT LPDWORD Required
    )

/*++

Routine Description:

    This routine passes a resource control request from a remote system.

Arguments:

    IDL_handle - the binding context - not used

    ResourceTypeName - the name of the Resource Type to control

    ControlCode - the control code for this request

    InBuffer - the input buffer

    InBufferSize - the size of the input buffer

    OutBuffer - the output buffer

    OutBufferSize - the size of the output buffer

    ByteReturned - the number of bytes returned in the output buffer

    Required - the number of bytes required if OutBuffer is not big enough.

Returns:

    ERROR_SUCCESS if successful.

    Win32 error code on failure.

--*/

{
    PFM_RESOURCE resource;
    DWORD status;

    status = FmpRmResourceTypeControl( ResourceTypeName,
                                       ControlCode,
                                       InBuffer,
                                       InBufferSize,
                                       OutBuffer,
                                       OutBufferSize,
                                       BytesReturned,
                                       Required
                                       );

    if ((status == ERROR_MOD_NOT_FOUND) || (status == ERROR_PROC_NOT_FOUND))
    {
        FmpRemovePossibleNodeForResType(ResourceTypeName, NmLocalNode);
    }
#if 0
    if ( ((status == ERROR_SUCCESS) ||
          (status == ERROR_RESOURCE_PROPERTIES_STORED)) &&
         (ControlCode & CLCTL_MODIFY_MASK) ) {
        ClusterEvent( CLUSTER_EVENT_RESTYPE_PROPERTY_CHANGE, XXX );
    }
#endif

    return(status);

} // FmsResourceTypeControl


error_status_t
s_FmsGroupControl(
    IN handle_t IDL_handle,
    IN LPCWSTR GroupId,
    IN DWORD ControlCode,
    IN PUCHAR InBuffer,
    IN DWORD InBufferSize,
    OUT PUCHAR OutBuffer,
    IN DWORD OutBufferSize,
    OUT LPDWORD BytesReturned,
    OUT LPDWORD Required
    )

/*++

Routine Description:

    This routine processes a group control request from a remote system.

Arguments:

    IDL_handle - the binding context - not used

    GroupId - the Id of the Group to control

    ControlCode - the control code for this request

    InBuffer - the input buffer

    InBufferSize - the size of the input buffer

    OutBuffer - the output buffer

    OutBufferSize - the size of the output buffer

    ByteReturned - the number of bytes returned in the output buffer

    Required - the number of bytes required if OutBuffer is not big enough.

Returns:

    ERROR_SUCCESS if successful.

    Win32 error code on failure.

--*/

{
    PFM_GROUP group;
    DWORD status;

    //
    // Find the specified group
    //
    group = OmReferenceObjectById( ObjectTypeGroup, GroupId );

    if ( group == NULL ) {
        ClRtlLogPrint(LOG_NOISE,
                  "[FM] FmsGroupControl: Could not find Group %1!ws!\n",
                  GroupId);
        return(ERROR_GROUP_NOT_FOUND);
    }


    status = FmpGroupControl(group, ControlCode, InBuffer, InBufferSize,
        OutBuffer, OutBufferSize, BytesReturned, Required);
        
    OmDereferenceObject(group);
    return(status);

} // FmsGroupControl


error_status_t
s_FmsPrepareQuorumResChange(
    IN handle_t IDL_handle,
    IN LPCWSTR  ResourceId,
    IN LPCWSTR  lpszQuoLogPath,
    IN DWORD    dwMaxQuoLogSize
    )

/*++

Routine Description:

    Receives a request to prepare a resource to become the quorum resource.
    A resource must be able to support a quorum log file and registry replication
    files.

Arguments:

    IDL_handle - The binding context - not used.
    ResourceId - The Id of the resource to be made the quorum resource.
    lpszQuoLogPath - The path where the logs must be created.
    dwMaxQuoLogSize - The maximum size of the quorum log file.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code on error.

--*/

{
    PFM_RESOURCE    pResource=NULL;
    DWORD           Status;

    ClRtlLogPrint(LOG_NOISE,
               "[FM] FmsPrepareQuorumResChange: Entry\r\n");

    // Find the specified resource.
    //
    pResource = OmReferenceObjectById( ObjectTypeResource, ResourceId );

    if ( pResource == NULL )
    {
        ClRtlLogPrint(LOG_NOISE,
                  "[FM] FmsPrepareQuorumResource: Could not find Resource %1!ws!\n",
                  ResourceId);
        Status = ERROR_RESOURCE_NOT_FOUND;
        goto FnExit;
    }

    CL_ASSERT( pResource->Group != NULL );
    if ( pResource->Group->OwnerNode == NmLocalNode )
        Status = FmpPrepareQuorumResChange(pResource, lpszQuoLogPath, dwMaxQuoLogSize);
    else
        Status = ERROR_HOST_NODE_NOT_RESOURCE_OWNER;

FnExit:
    if (pResource) OmDereferenceObject( pResource );
    return(Status);

} // FmsPrepareQuorumResChange



error_status_t
s_FmsCompleteQuorumResChange(
    IN handle_t IDL_handle,
    IN LPCWSTR  lpszOldQuoResId,
    IN LPCWSTR  lpszOldQuoLogPath
    )

/*++

Routine Description:

    Receives a request to clean up quorum logging and cluster registry files
    on the old quorum resource.

Arguments:

    IDL_handle - The binding context - not used.
    lpszOldQuoResId - The Id of the resource to be made the quorum resource.
    lpszQuoLogPath - The path where the logs must be created.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code on error.

--*/

{
    PFM_RESOURCE    pResource=NULL;
    DWORD           Status;

    ClRtlLogPrint(LOG_NOISE,
               "[FM] FmsCompleteQuorumResChange: Entry\r\n");

    // Find the specified resource.
    //
    pResource = OmReferenceObjectById( ObjectTypeResource, lpszOldQuoResId );

    if ( pResource == NULL )
    {
        ClRtlLogPrint(LOG_NOISE,
                  "[FM] FmsCompleteQuorumResource: Could not find Resource %1!ws!\n",
                  lpszOldQuoResId);
        Status = ERROR_RESOURCE_NOT_FOUND;
        goto FnExit;
    }

    CL_ASSERT( pResource->Group != NULL );
    if ( pResource->Group->OwnerNode == NmLocalNode )
        Status = FmpCompleteQuorumResChange(lpszOldQuoResId, lpszOldQuoLogPath);
    else
        Status = ERROR_HOST_NODE_NOT_RESOURCE_OWNER;

FnExit:
    if (pResource) OmDereferenceObject( pResource );
    return(Status);

} // FmsCompleteQuorumResChange



error_status_t
s_FmsQuoNodeOnlineResource(
    IN handle_t IDL_handle,
    IN LPCWSTR ResourceId,
    IN LPCWSTR NodeId,
    OUT LPDWORD State
    )

/*++

Routine Description:

    Receives a RmResourceOnline request from (THE) remote system and returns
    status for that request.

    This system must own the quorum resource.

    This is the first half of the sling shot. We acquire locks
    and then RPC back to the originating node with a request to perform
    the online.

    This request is only valid for non-quorum resources.

Arguments:

    IDL_handle - The binding context - not used.

    ResourceId - The Id of the Resource to bring online.

    NodeId - The Id of the Node that originated the request.

    State - Returns the new state of the resource.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code on error.

--*/
{
    //not used
    return(ERROR_INVALID_FUNCTION);        
}
 // FmsQuoNodeOnlineResource



error_status_t
s_FmsQuoNodeOfflineResource(
    IN handle_t IDL_handle,
    IN LPCWSTR ResourceId,
    IN LPCWSTR NodeId,
    OUT LPDWORD State
    )

/*++

Routine Description:

    Receives a Resource Offline Request from (THE) remote system and returns
    status for that request.

Arguments:

    IDL_handle - The binding context - not used.

    ResourceId - The Id of the Resource to bring offline.

    NodeId - The Id of the Node that originated the request.

    State - Returns the new state of the resource.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code on error.

--*/
{
    //not used
    return(ERROR_INVALID_FUNCTION);
}// FmsQuoNodeOfflineResource



error_status_t
s_FmsRmOnlineResource(
    IN handle_t IDL_handle,
    IN LPCWSTR ResourceId,
    OUT LPDWORD pdwState
    )

/*++

Routine Description:

    Receives a RmResourceOnline request from quorum node and returns
    status for that request.

    This system was the originator of the original online request.

    This is the second half of the sling shot. It just does the online
    request.

    This request is only valid for non-quorum resources.

Arguments:

    IDL_handle - The binding context - not used.

    ResourceId - The Id of the Resource to bring online.

    State - Returns the new state of the resource.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code on error.

Notes:

    We can't acquire any locks... but the originating thread has the
    lock held for us.

--*/

{
    PFM_RESOURCE resource;
    DWORD status;

    FmpMustBeOnline( );

    ClRtlLogPrint(LOG_NOISE,
               "[FM] s_FmsRmOnlineResource: To bring resource '%1!ws!' online\n",
               ResourceId);

    //
    // Find the specified resource.
    //
    resource = OmReferenceObjectById( ObjectTypeResource, ResourceId );

    if ( resource == NULL ) {
        ClRtlLogPrint(LOG_NOISE,
                  "[FM] s_FmsRmOnlineResource: Could not find Resource %1!ws!\n",
                  ResourceId);
        return(ERROR_RESOURCE_NOT_FOUND);
    }

    //
    // This must not be done on the quorum resource, and the local system
    // must not own the quorum resource. We'll assume that the quorum
    // resource migrated while we were performng this request.
    //
    CL_ASSERT( gpQuoResource != NULL );
    CL_ASSERT( gpQuoResource->Group != NULL );
    CL_ASSERT( gpQuoResource->Group->OwnerNode != NULL );

    if ( resource->QuorumResource ||
         (gpQuoResource->Group->OwnerNode == NmLocalNode) ) {
        OmDereferenceObject( resource );
        return(ERROR_HOST_NODE_NOT_RESOURCE_OWNER);
    }

    //
    // The local node must own the resource.
    //
    CL_ASSERT( resource->Group != NULL );
    CL_ASSERT( resource->Group->OwnerNode != NULL );
    if ( resource->Group->OwnerNode != NmLocalNode ) {
        OmDereferenceObject( resource );
        return(ERROR_RESOURCE_NOT_AVAILABLE);
    }

    //
    // Just call the function that does the work.
    //
    OmNotifyCb( resource, NOTIFY_RESOURCE_PREONLINE );

    status = RmOnlineResource( resource->Id, pdwState );
    //call the synchronous notifications on the resource
    FmpCallResourceNotifyCb(resource, *pdwState);

    OmDereferenceObject( resource );

    return(status);

} // FmsRmOnlineResource



error_status_t
s_FmsRmOfflineResource(
    IN handle_t IDL_handle,
    IN LPCWSTR ResourceId,
    OUT LPDWORD pdwState
    )

/*++

Routine Description:

    Receives a Resource Offline Request from (THE) remote system and returns
    status for that request.

Arguments:

    IDL_handle - The binding context - not used.

    ResourceId - The Id of the Resource to bring offline.

    State - Returns the new state of the resource.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code on error.

Notes:

    We can't acquire any locks... but the originating thread has the
    lock held for us.

--*/

{
    PFM_RESOURCE resource;
    DWORD status;

    FmpMustBeOnline( );

    ClRtlLogPrint(LOG_NOISE,
              "[FM] s_FmsRmOfflineResource: To take resource '%1!ws!' offline\n",
              ResourceId);

    //
    // Find the specified resource.
    //
    resource = OmReferenceObjectById( ObjectTypeResource, ResourceId );

    if ( resource == NULL ) {
        ClRtlLogPrint(LOG_NOISE,
                  "[FM] s_FmsRmOfflineResource: Could not find Resource %1!ws!\n",
                  ResourceId);
        return(ERROR_RESOURCE_NOT_FOUND);
    }

    //
    // This must not be done on the quorum resource, and the local system
    // must not own the quorum resource. We'll assume that the quorum
    // resource migrated while we were performng this request.
    //
    CL_ASSERT( gpQuoResource != NULL );
    CL_ASSERT( gpQuoResource->Group != NULL );
    CL_ASSERT( gpQuoResource->Group->OwnerNode != NULL );

    if ( resource->QuorumResource ||
         (gpQuoResource->Group->OwnerNode == NmLocalNode) ) {
        OmDereferenceObject( resource );
        return(ERROR_HOST_NODE_NOT_RESOURCE_OWNER);
    }

    //
    // The local node must own the resource.
    //
    CL_ASSERT( resource->Group != NULL );
    CL_ASSERT( resource->Group->OwnerNode != NULL );
    if ( resource->Group->OwnerNode != NmLocalNode ) {
        OmDereferenceObject( resource );
        return(ERROR_RESOURCE_NOT_AVAILABLE);
    }

    //
    // Just call the function that does the work.
    //
    OmNotifyCb( resource, NOTIFY_RESOURCE_PREOFFLINE );

    status = RmOfflineResource( resource->Id, pdwState );

    //call the post offline obj synchronous notifications
    FmpCallResourceNotifyCb(resource, *pdwState);

    OmDereferenceObject( resource );

    return(status);

} // FmsRmOfflineResource

error_status_t
s_FmsBackupClusterDatabase(
    IN handle_t IDL_handle,
    IN LPCWSTR  ResourceId,
    IN LPCWSTR  lpszPathName
    )

/*++

Routine Description:

    Receives a request to backup the quorum log file and the checkpoint
    file

Arguments:

    IDL_handle - The binding context - not used.
    ResourceId - The Id of the quorum resource
    lpszPathName - The directory path name where the files have to be 
                   backed up. This path must be visible to the node
                   on which the quorum resource is online.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code on error.

--*/

{
    PFM_RESOURCE    pResource = NULL;
    DWORD           Status;

    FmpMustBeOnline( );

    ClRtlLogPrint(LOG_NOISE,
               "[FM] s_FmsBackupClusterDatabase: Entry...\r\n");

    //
    //  Chittur Subbaraman (chitturs) - 10/12/1998
    //
    //  Find the specified quorum resource
    //
    pResource = OmReferenceObjectById( ObjectTypeResource, ResourceId );

    if ( pResource == NULL )
    {
        ClRtlLogPrint(LOG_NOISE,
                  "[FM] s_FmsBackupClusterDatabase: Could not find Resource %1!ws!\n",
                  ResourceId);
        Status = ERROR_RESOURCE_NOT_FOUND;
        goto FnExit;
    }

    FmpAcquireLocalResourceLock( pResource );

    CL_ASSERT( pResource->Group != NULL );

    //
    //  Make sure the local node owns the quorum resource
    //
    if ( pResource->Group->OwnerNode == NmLocalNode )
    {
        Status = FmpBackupClusterDatabase( pResource, lpszPathName );
    }
    else
    {
        Status = ERROR_HOST_NODE_NOT_RESOURCE_OWNER;
        ClRtlLogPrint(LOG_NOISE,
                  "[FM] s_FmsBackupClusterDatabase: This node is not the quorum resource owner...\n"
                  );
    }
    
    FmpReleaseLocalResourceLock( pResource );

FnExit:
    if ( pResource ) OmDereferenceObject( pResource );
    return( Status );
} // FmsBackupClusterDatabase

error_status_t
s_FmsChangeResourceGroup(
    IN handle_t IDL_handle,
    IN LPCWSTR pszResourceId,
    IN LPCWSTR pszGroupId
    )

/*++

Routine Description:

    Receives a Resource change group request from a remote system and returns
    status for that request.

Arguments:

    IDL_handle - The binding context - not used.
    pszResourceId - The Id of the Resource to change a resource
    pszGroupId - The Id of the Group to change to.
    
Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code on error.

--*/

{
    PFM_RESOURCE    pResource = NULL;
    PFM_GROUP       pNewGroup = NULL;
    DWORD           dwStatus;
    PFM_GROUP       pOldGroup;

    FmpMustBeOnline( );

    //
    // Find the specified resource.
    //
    pResource = OmReferenceObjectById( ObjectTypeResource, pszResourceId );

    if ( pResource == NULL ) {
        ClRtlLogPrint(LOG_NOISE,
                  "[FM] FmsChangeResourceNode: Could not find Resource %1!ws!\n",
                  pszResourceId);
        dwStatus = ERROR_RESOURCE_NOT_FOUND;
        goto FnExit;
    }

    //
    // Find the specified group.
    //
    pNewGroup = OmReferenceObjectById( ObjectTypeGroup, pszGroupId );

    if ( pNewGroup == NULL ) {
        ClRtlLogPrint(LOG_NOISE,
                  "[FM] s_FmsChangeResourceGroupe: Could not find NewGroup %1!ws!\n",
                  pszGroupId);
        dwStatus = ERROR_GROUP_NOT_FOUND;
        goto FnExit;
    }

    ClRtlLogPrint(LOG_NOISE,
       "[FM] s_FmChangeResourceGroup : Resource <%1!ws!> NewGroup %2!ws!\n",
       OmObjectId( pResource ),
       OmObjectId( pNewGroup ));

    //
    // Synchronize both the old and the new groups.
    // Lock the lowest by lowest Group Id first - to prevent deadlocks!
    // Note - the order of release is unimportant.
    //
    // strictly, the comparison below cannot be equal!
    //
    if ( lstrcmpiW( OmObjectId( pResource->Group ), OmObjectId( pNewGroup ) ) <= 0 ) {
        FmpAcquireLocalGroupLock( pResource->Group );
        FmpAcquireLocalGroupLock( pNewGroup );
    } else {
        FmpAcquireLocalGroupLock( pNewGroup );
        FmpAcquireLocalGroupLock( pResource->Group );
    }

    //remember the old group for freeing locks
    pOldGroup = pResource->Group;

    //boy if we are not the owner any more
    //shunt the request to the new owner
    if ( pResource->Group->OwnerNode != NmLocalNode ) 
    {
        // Note: FmcChangeResourceNode must release the resource lock.
        dwStatus = FmcChangeResourceGroup( pResource, pNewGroup );
        goto FnExit;
    } 
    else 
    {
        dwStatus = FmpChangeResourceGroup( pResource, pNewGroup );
    }

    FmpReleaseLocalGroupLock( pNewGroup );
    FmpReleaseLocalGroupLock( pOldGroup );
    
FnExit:
    if ( pResource ) OmDereferenceObject( pResource );
    if ( pNewGroup ) OmDereferenceObject( pNewGroup );
    ClRtlLogPrint(LOG_NOISE,
       "[FM] s_FmsChangeResourceGroup : returned <%1!u!>\r\n",
       dwStatus);
    return( dwStatus );
} //s_FmsChangeResourceGroup



error_status_t
s_FmsDeleteGroupRequest(
    IN handle_t IDL_handle,
    IN LPCWSTR pszGroupId
    )

/*++

Routine Description:

    Receives a delete group request from a remote system and returns
    the status for that request.

Arguments:

    IDL_handle - The binding context - not used.
    pszGroupId - The Id of the group to be deleted.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code on error.

--*/

{
    PFM_GROUP    pGroup = NULL;
    DWORD        dwStatus = ERROR_SUCCESS;

    FmpMustBeOnline( );

    //
    // Find the specified group.
    //
    pGroup = OmReferenceObjectById( ObjectTypeGroup, pszGroupId );

    if ( pGroup == NULL ) 
    {
        ClRtlLogPrint(LOG_NOISE,
                  "[FM] FmsDeleteGroupRequest: Could not find group %1!ws!\n",
                  pszGroupId);
        dwStatus = ERROR_GROUP_NOT_FOUND;
        goto FnExit;
    }

    FmpAcquireLocalGroupLock( pGroup );

    if ( pGroup->OwnerNode == NmLocalNode )
    {
        dwStatus = FmpDeleteGroup( pGroup );
    }
    else
    {
        //
        // FmcDeleteGroup releases the group lock
        //
        dwStatus = FmcDeleteGroupRequest( pGroup );
        goto FnExit;
    }

    FmpReleaseLocalGroupLock( pGroup );
    
FnExit:
    if ( pGroup ) OmDereferenceObject( pGroup );
    return( dwStatus );
} //s_FmsChangeResourceGroup


error_status_t
s_FmsAddResourceDependency(
    IN handle_t IDL_handle,
    IN LPCWSTR pszResourceId,
    IN LPCWSTR pszDependsOnId
    )

/*++

Routine Description:

    Receives an add resource dependency request from a remote system 
    and returns the status for that request.

Arguments:

    IDL_handle - The binding context - not used.
    
    pszResourceId - The Id of the resource to add a dependent resource.

    pszDependentResourceId - The Id of the dependent resource.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code on error.

--*/

{
    PFM_RESOURCE    pResource = NULL;
    PFM_RESOURCE    pDependentResource = NULL;
    DWORD           dwStatus = ERROR_SUCCESS;
    DWORD           dwResourceLen;
    DWORD           dwDependsOnLen;

    //
    //  Chittur Subbaraman (chitturs) - 5/16/99
    //
    //  Handle add resource dependency RPC calls from non-owner nodes
    //
    FmpMustBeOnline( );

    ClRtlLogPrint(LOG_NOISE,
                  "[FM] s_FmsAddResourceDependency: Resource <%1!ws!>, Dependent Resource <%2!ws!>\n",
                  pszResourceId,
                  pszDependsOnId);

    //
    // Find the specified resources.
    //
    pResource = OmReferenceObjectById( ObjectTypeResource, pszResourceId );

    if ( pResource == NULL ) 
    {
        ClRtlLogPrint(LOG_NOISE,
                  "[FM] s_FmsAddResourceDependency: Could not find resource <%1!ws!>\n",
                  pszResourceId);
        dwStatus = ERROR_RESOURCE_NOT_FOUND;
        goto FnExit;
    }

    pDependentResource = OmReferenceObjectById( ObjectTypeResource, 
                                                pszDependsOnId );

    if ( pDependentResource == NULL ) 
    {
        ClRtlLogPrint(LOG_NOISE,
                  "[FM] s_FmsAddResourceDependency: Could not find dependent resource <%1!ws!>\n",
                  pszDependsOnId);
        dwStatus = ERROR_RESOURCE_NOT_FOUND;
        goto FnExit;
    }
    //    
    // Acquire the resource lock 
    //
    FmpAcquireLocalResourceLock( pResource );

    if ( pResource->Group->OwnerNode != NmLocalNode )
    {
        dwStatus = ERROR_HOST_NODE_NOT_RESOURCE_OWNER;
        goto FnUnlock;
    }

    dwStatus = FmpValAddResourceDependency( pResource, pDependentResource );

    if ( dwStatus != ERROR_SUCCESS )
    {
        goto FnUnlock;
    }

    dwResourceLen = ( lstrlenW( pszResourceId ) + 1 ) * sizeof( WCHAR );

    dwDependsOnLen = ( lstrlenW( pszDependsOnId ) + 1 ) * sizeof( WCHAR );

    
    dwStatus = GumSendUpdateEx( GumUpdateFailoverManager,
                                FmUpdateAddDependency,
                                2,
                                dwResourceLen,
                                pszResourceId,
                                dwDependsOnLen,
                                pszDependsOnId );

    if ( dwStatus == ERROR_SUCCESS ) 
    {
        FmpBroadcastDependencyChange( pResource,
                                      pszDependsOnId,
                                      FALSE );
    }

FnUnlock:
    FmpReleaseLocalResourceLock( pResource );


FnExit:
    if ( pResource ) OmDereferenceObject( pResource );

    if ( pDependentResource ) OmDereferenceObject( pDependentResource );

    ClRtlLogPrint(LOG_NOISE,
                  "[FM] FmsAddResourceDependency Exit: Status = %1!u!\n",
                  dwStatus);

    return( dwStatus );
} // s_FmsAddResourceDependency

error_status_t
s_FmsRemoveResourceDependency(
    IN handle_t IDL_handle,
    IN LPCWSTR pszResourceId,
    IN LPCWSTR pszDependsOnId
    )

/*++

Routine Description:

    Receives a remove resource dependency request from a remote system 
    and returns the status for that request.

Arguments:

    IDL_handle - The binding context - not used.
    
    pszResourceId - The Id of the resource to remove a dependent resource from.

    pszDependentResourceId - The Id of the dependent resource.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code on error.

--*/

{
    PFM_RESOURCE    pResource = NULL;
    PFM_RESOURCE    pDependentResource = NULL;
    DWORD           dwStatus = ERROR_SUCCESS;
    DWORD           dwResourceLen;
    DWORD           dwDependsOnLen;

    //
    //  Chittur Subbaraman (chitturs) - 5/16/99
    //
    //  Handle remove resource dependency RPC calls from non-owner nodes
    //
    FmpMustBeOnline( );

    ClRtlLogPrint(LOG_NOISE,
                  "[FM] s_FmsRemoveResourceDependency: Resource <%1!ws!>, Dependent Resource <%2!ws!>\n",
                  pszResourceId,
                  pszDependsOnId);

    //
    // Find the specified resources.
    //
    pResource = OmReferenceObjectById( ObjectTypeResource, pszResourceId );

    if ( pResource == NULL ) 
    {
        ClRtlLogPrint(LOG_NOISE,
                  "[FM] s_FmsRemoveResourceDependency: Could not find resource <%1!ws!>\n",
                  pszResourceId);
        dwStatus = ERROR_RESOURCE_NOT_FOUND;
        goto FnExit;
    }

    pDependentResource = OmReferenceObjectById( ObjectTypeResource, 
                                                pszDependsOnId );

    if ( pDependentResource == NULL ) 
    {
        ClRtlLogPrint(LOG_NOISE,
                  "[FM] s_FmsRemoveResourceDependency: Could not find dependent resource <%1!ws!>\n",
                  pszDependsOnId);
        dwStatus = ERROR_RESOURCE_NOT_FOUND;
        goto FnExit;
    }
    //    
    // Acquire the resource lock 
    //
    FmpAcquireLocalResourceLock( pResource );

    dwStatus = FmpValRemoveResourceDependency( pResource, pDependentResource );

    if ( dwStatus != ERROR_SUCCESS )
    {
        ClRtlLogPrint(LOG_NOISE,
                  "[FM] s_FmsRemoveResourceDependency: FmpValRemoveResourceDependency returns %1!u!\n",
                  dwStatus);
        goto FnUnlock;
    }

    if ( pResource->Group->OwnerNode != NmLocalNode )
    {
        dwStatus = ERROR_HOST_NODE_NOT_RESOURCE_OWNER;
        goto FnUnlock;
    }

    pszResourceId = OmObjectId( pResource );
    dwResourceLen = ( lstrlenW( pszResourceId ) + 1 ) * sizeof( WCHAR );

    pszDependsOnId = OmObjectId( pDependentResource );
    dwDependsOnLen = ( lstrlenW( pszDependsOnId ) + 1 ) * sizeof( WCHAR );


    dwStatus = GumSendUpdateEx( GumUpdateFailoverManager,
                                FmUpdateRemoveDependency,
                                2,
                                dwResourceLen,
                                pszResourceId,
                                dwDependsOnLen,
                                pszDependsOnId );

    if ( dwStatus == ERROR_SUCCESS ) 
    {
        FmpBroadcastDependencyChange( pResource,
                                      pszDependsOnId,
                                      TRUE );
    }
    
FnUnlock:
    FmpReleaseLocalResourceLock( pResource );

FnExit:
    if ( pResource ) OmDereferenceObject( pResource );

    if ( pDependentResource ) OmDereferenceObject( pDependentResource );

    ClRtlLogPrint(LOG_NOISE,
                  "[FM] FmsRemoveResourceDependency Exit: Status = %1!u!\n",
                  dwStatus);

    return( dwStatus );
} // s_FmsRemoveResourceDependency

error_status_t
s_FmsCreateResource2(
    IN handle_t IDL_handle,
    IN LPCWSTR GroupId,
    IN LPWSTR ResourceId,
    IN LPCWSTR ResourceName,
    IN LPCWSTR ResourceType,
    IN DWORD   dwFlags
    )

/*++

Routine Description:

    Receives a Create Resource Request from a remote system and returns
    status for that request.

Arguments:

    IDL_handle - The binding context - not used.
    GroupId - The Id of the Group to create the resource inside.
    ResourceId - The Id of the Resource to create.
    ResourceName - The name of the Resource to create.
    ResourceType - The name of the Resource Type.
    dwFlags - Flags for the resource.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code on error.

Notes:

    The Resource lock is acquired to synchronize access to the resource. This
    satisfies locking the resource on all nodes in the cluster... so long
    as the local node is the owner of the resource.

--*/

{
    PFM_GROUP group;
    DWORD status;
    PGUM_CREATE_RESOURCE gumResource;
    DWORD groupIdLen;
    DWORD resourceIdLen;
    DWORD resourceNameLen;
    DWORD resourceTypeLen;
    DWORD bufSize;
    HDMKEY resourceKey;
    HDMKEY paramsKey;
    DWORD  disposition;

    FmpMustBeOnline();

    ClRtlLogPrint(LOG_NOISE,
               "[FM] FmsCreateResource2: To create resource '%1!ws!'\n",
               ResourceId);

    //
    // Find the specified group.
    //
    group = OmReferenceObjectById( ObjectTypeGroup,
                                   GroupId );

    if ( group == NULL ) {
        ClRtlLogPrint(LOG_NOISE,
                  "[FM] FmsCreateResource2: Could not find Group %1!ws!\n",
                  GroupId);
        return(ERROR_GROUP_NOT_FOUND);
    }

    FmpAcquireLocalGroupLock( group );

    //
    // Now delete it on all nodes in the cluster if we are the owner.
    //
    if ( group->OwnerNode == NmLocalNode ) {
        //
        // Allocate a message buffer.
        //
        groupIdLen = (lstrlenW(GroupId)+1) * sizeof(WCHAR);
        resourceIdLen = (lstrlenW(ResourceId)+1) * sizeof(WCHAR);
        resourceNameLen = (lstrlenW(ResourceName)+1) * sizeof(WCHAR);
        resourceTypeLen = (lstrlenW(ResourceType)+1) * sizeof(WCHAR);
        bufSize = sizeof(GUM_CREATE_RESOURCE) - sizeof(WCHAR) +
                  groupIdLen + resourceIdLen + resourceNameLen + resourceTypeLen + 2 * sizeof( DWORD );
        gumResource = LocalAlloc(LMEM_FIXED, bufSize);
        if (gumResource == NULL) {
            CsInconsistencyHalt( ERROR_NOT_ENOUGH_MEMORY );
            return(ERROR_NOT_ENOUGH_MEMORY);
        }

        //
        // Fill in message buffer.
        //
        gumResource->Resource = NULL;
        gumResource->GroupIdLen = groupIdLen;
        gumResource->ResourceIdLen = resourceIdLen;
        CopyMemory(gumResource->GroupId, GroupId, groupIdLen);
        CopyMemory((PCHAR)gumResource->GroupId + groupIdLen,
                   ResourceId,
                   resourceIdLen);
        CopyMemory((PCHAR)gumResource->GroupId + groupIdLen + resourceIdLen,
                   ResourceName,
                   resourceNameLen);

        CopyMemory((PCHAR)gumResource->GroupId + groupIdLen + resourceIdLen + resourceNameLen,
                   &resourceTypeLen,
                   sizeof( DWORD ) );

        CopyMemory((PCHAR)gumResource->GroupId + groupIdLen + resourceIdLen + resourceNameLen + sizeof( DWORD ),
                   ResourceType,
                   resourceTypeLen );

        CopyMemory((PCHAR)gumResource->GroupId + groupIdLen + resourceIdLen + resourceNameLen + sizeof( DWORD ) + resourceTypeLen,
                   &dwFlags,
                   sizeof( DWORD ) );
        //
        // Send message.
        //
        status = GumSendUpdate(GumUpdateFailoverManager,
                               FmUpdateCreateResource,
                               bufSize,
                               gumResource);
        if ( ( status == ERROR_SUCCESS ) && 
             ( gumResource->Resource != NULL ) )
            FmpCleanupPossibleNodeList(gumResource->Resource); 
        if( ( gumResource->Resource == NULL ) && FmpShutdown ) {
            status = ERROR_CLUSTER_NODE_SHUTTING_DOWN;
        }
        LocalFree(gumResource);
    } else {
        status = ERROR_HOST_NODE_NOT_RESOURCE_OWNER;
    }

    FmpReleaseLocalGroupLock( group );

    return(status);

} // FmsCreateResource2