/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    fmgum.c

Abstract:

    Cluster FM Global Update processing routines.

Author:

    Rod Gamache (rodga) 24-Apr-1996


Revision History:


--*/

#include "fmp.h"

#include "ntrtl.h"

#if NO_SHARED_LOCKS
extern CRITICAL_SECTION gLockDmpRoot;
#else
extern RTL_RESOURCE gLockDmpRoot;
#endif

#define     NODE_ID_SZ  6
#define     LOG_MODULE FMGUM


DWORD
WINAPI
FmpGumReceiveUpdates(
    IN DWORD    Context,
    IN BOOL     SourceNode,
    IN DWORD    BufferLength,
    IN PVOID    Buffer
    )

/*++

Routine Description:

    Updates the specified resource (contained within Buffer) with a new
    state.

Arguments:

    Context - The message update type.
    SourceNode - TRUE if this is the source node for this update.
                 FALSE otherwise.
    BufferLength - Length of the received buffer.
    Buffer - The actual buffer

Returns:

    ERROR_SUCCESS

--*/

{
    PFM_RESOURCE resource;

    //
    //  Chittur Subbaraman (chitturs) - 4/18/99
    // 
    //  If FM groups are not fully initialized or FM is shutting down, then
    //  don't do anything.
    //
    if ( !FmpFMGroupsInited ||
         FmpShutdown ) {
        return(ERROR_SUCCESS);
    }

    switch ( Context ) {


        case FmUpdateFailureCount:
        {
            PGUM_FAILURE_COUNT failureCount;
            PFM_GROUP group;

            //
            // This update type is always sent.
            // On the originating node, all of the work must be done by
            // the sending thread.
            // On the non-originating nodes, no locks can be acquired! This
            // would cause hang situations with operations like move.
            // ... this is okay, since the locking must be done on the sending
            // node anyway, which owns the group.
            //
            if ( SourceNode == FALSE ) {
                if ( BufferLength <= sizeof(GUM_FAILURE_COUNT) ) {
                    ClRtlLogPrint(LOG_UNUSUAL, "[FM] Gum FailureCount receive buffer too small!\n");
                    return(ERROR_SUCCESS);
                }

                failureCount = (PGUM_FAILURE_COUNT)Buffer;
                group = OmReferenceObjectById( ObjectTypeGroup,
                                               (LPCWSTR)&failureCount->GroupId[0] );

                if ( group == NULL ) {
                    ClRtlLogPrint(LOG_UNUSUAL,
                               "[FM] Gum FailureCount failed to find group %1!ws!\n",
                               failureCount->GroupId);
                    return(ERROR_SUCCESS);
                }

                ClRtlLogPrint(LOG_NOISE,
                           "[FM] GUM update failure count %1!ws!, count %2!u!\n",
                           failureCount->GroupId,
                           failureCount->Count);

                //FmpAcquireLocalGroupLock( group );

                if ( group->OwnerNode == NmLocalNode ) {
                    ClRtlLogPrint(LOG_NOISE,
                               "[FM] Gum FailureCount wrong owner for %1!ws!\n",
                               failureCount->GroupId);
                } else {
                    group->NumberOfFailures = failureCount->Count;
                    if ( failureCount->NewTime ) {
                        group->FailureTime = GetTickCount();
                    }
                }

                //FmpReleaseLocalGroupLock( group );

                OmDereferenceObject( group );

            }

            break;
        }

        case FmUpdateCreateGroup:
            {
                PGUM_CREATE_GROUP GumGroup;
                DWORD Status = ERROR_SUCCESS;

                GumGroup = (PGUM_CREATE_GROUP)Buffer;

                Status = FmpUpdateCreateGroup( GumGroup, SourceNode );
                
                return(Status);
            }

        case FmUpdateCreateResource:
            {
                DWORD dwStatus = ERROR_SUCCESS;
                PGUM_CREATE_RESOURCE GumResource = 
                                (PGUM_CREATE_RESOURCE)Buffer;

                dwStatus = FmpUpdateCreateResource( GumResource );

                return( dwStatus );
            }



        case FmUpdateAddPossibleNode:
        case FmUpdateRemovePossibleNode:
            {

                PGUM_CHANGE_POSSIBLE_NODE pGumChange;
                PFM_RESOURCE              pResource;
                LPWSTR                    pszResourceId;
                LPWSTR                    pszNodeId;
                PNM_NODE                  pNode;
                DWORD                     dwStatus;
                DWORD                     dwControlCode;
                PFMP_POSSIBLE_NODE        pPossibleNode;

                pGumChange = (PGUM_CHANGE_POSSIBLE_NODE)Buffer;
                pszResourceId = pGumChange->ResourceId;
                pszNodeId = (LPWSTR)((PCHAR)pszResourceId +
                                         pGumChange->ResourceIdLen);
                                         
                pResource = OmReferenceObjectById(ObjectTypeResource,pszResourceId);
                pNode = OmReferenceObjectById(ObjectTypeNode, pszNodeId);
                CL_ASSERT(pResource != NULL);
                CL_ASSERT(pNode != NULL);
                pPossibleNode = LocalAlloc( LMEM_FIXED,
                    sizeof(FMP_POSSIBLE_NODE) );
                if ( pPossibleNode == NULL ) 
                {
                    return(ERROR_NOT_ENOUGH_MEMORY);
                }
                
                if (Context == FmUpdateAddPossibleNode) 
                {
                    dwControlCode = CLUSCTL_RESOURCE_ADD_OWNER;
                } 
                else 
                {
                    dwControlCode = CLUSCTL_RESOURCE_REMOVE_OWNER;
                }
                
                dwStatus = FmpUpdateChangeResourceNode(SourceNode, 
                    pResource, pNode, dwControlCode);
                //if status is not successful then return, else notify
                //resource dlls
                if (dwStatus != ERROR_SUCCESS)
                {
                    //dereference the objects
                    OmDereferenceObject(pResource);
                    OmDereferenceObject(pNode);
                    //free the memory
                    LocalFree(pPossibleNode);
                    return(dwStatus);
                }

                pPossibleNode->Resource = pResource;
                pPossibleNode->Node = pNode;
                pPossibleNode->ControlCode = dwControlCode;

                //
                // Tell the resource about the ADD/REMOVE in a worker thread.
                //

                FmpPostWorkItem( FM_EVENT_RESOURCE_CHANGE,
                                 pPossibleNode,
                                 0 );

                //
                //  Chittur Subbaraman (chitturs) - 6/7/99
                //  
                //  Don't reference pPossibleNode any more. It could have
                //  been freed by the worker thread by the time you get
                //  here.
                //
                ClusterEvent( CLUSTER_EVENT_RESOURCE_PROPERTY_CHANGE,
                              pResource );

                // Let the worker thread perform the derefs/Frees
                return(dwStatus);
            }                

        case FmUpdateJoin:
            if ( CsDmOrFmHasChanged )
            {
                //
                // We can only send back SEQMISMATCH if we're a pure Windows Server 2003 (or later) environment.
                // In a mixed mode cluster, a W2K node would end up infinitely retrying the FM join.
                //
                DWORD dwClusterHighestVersion;
                NmGetClusterOperationalVersion( &dwClusterHighestVersion, NULL, NULL );
                if ( CLUSTER_GET_MAJOR_VERSION( dwClusterHighestVersion ) < NT51_MAJOR_VERSION )
                {
                    ClRtlLogPrint(LOG_UNUSUAL,"[FM] DM or FM update has occured during join; rejecting FmUpdateJoin.\n" );
                    return ERROR_CLUSTER_DATABASE_SEQMISMATCH;                        
                }
            // Don't need to reset CsDmOrFmHasChanged here -- we will reset it when we see the DmUpdateJoin.
            }
            break;

            
        case FmUpdateCreateResourceType:
            {
                DWORD dwStatus;

                dwStatus = FmpUpdateCreateResourceType( Buffer );

                return( dwStatus );
            }
            break;
            
        case FmUpdateDeleteResourceType:
            {
                BOOL ResourceExists = FALSE;
                PFM_RESTYPE Type;

                Type = OmReferenceObjectById( ObjectTypeResType,
                                              (LPWSTR)Buffer);
                if (Type == NULL) {
                    return(ERROR_CLUSTER_RESOURCE_TYPE_NOT_FOUND);
                }
                //
                // Make sure no resources exist of this type.
                //
                OmEnumObjects( ObjectTypeResource,
                               FmpFindResourceType,
                               Type,
                               &ResourceExists);
                if (ResourceExists) {
                    OmDereferenceObject(Type);
                    return(ERROR_DIR_NOT_EMPTY);
                }

                //
                // We need to dereference the object twice to get
                // rid of it. But then any notification handlers will
                // not get a chance to see the object by the time
                // the handler gets called. So we use the EP_DEREF_CONTEXT
                // flag to get the event processor to do the second deref
                // once everything has been dispatched.
                //
                FmpDeleteResType(Type);
                ClusterEventEx( CLUSTER_EVENT_RESTYPE_DELETED,
                                EP_DEREF_CONTEXT,
                                Type );
            }
            break;

        case FmUpdateChangeGroup:
            {
                PGUM_CHANGE_GROUP   pGumChange;
                PFM_RESOURCE        pResource;
                LPWSTR              pszResourceId;
                LPWSTR              pszGroupId;
                PFM_GROUP           pNewGroup;
                DWORD               dwStatus;
                DWORD               dwClusterHighestVersion;
                
                pGumChange = (PGUM_CHANGE_GROUP)Buffer;

                pszResourceId = pGumChange->ResourceId;
                pszGroupId = (LPWSTR)((PCHAR)pszResourceId +
                                          pGumChange->ResourceIdLen);
                //
                // Find the specified resource and group.
                //
                pResource = OmReferenceObjectById(ObjectTypeResource,
                                                 pszResourceId);
                if (pResource == NULL) {
                    return(ERROR_RESOURCE_NOT_FOUND);
                }
                pNewGroup = OmReferenceObjectById(ObjectTypeGroup, 
                                                    pszGroupId);
                if (pNewGroup == NULL) {
                    OmDereferenceObject(pResource);
                    return(ERROR_SUCCESS);
                }

                dwStatus = FmpUpdateChangeResourceGroup(SourceNode,
                              pResource, pNewGroup);

                OmDereferenceObject(pNewGroup);
                OmDereferenceObject(pResource);

                return(dwStatus);
            }
            break;

            
        default:
            {

            }
            ClRtlLogPrint(LOG_UNUSUAL,"[FM] Gum received bad context, %1!u!\n",
                Context);

    }

    return(ERROR_SUCCESS);

} // FmpGumReceiveUpdates


DWORD
FmpUpdateChangeQuorumResource2(
    IN BOOL     SourceNode,
    IN LPCWSTR  NewQuorumResourceId,
    IN LPCWSTR  szRootClusFilePath,
    IN LPDWORD  pdwMaxQuorumLogSize,
    IN LPDWORD  pdwQuorumArbTimeout,
    IN LPDWORD  pdwNewQuorumResourceCharacterictics OPTIONAL
    )

/*++

Routine Description:

    Perform updates related to changing of the quorum resource.

Arguments:

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code otherwise.

--*/

{
    PFM_RESOURCE    pResource;
    PFM_RESOURCE    pOldQuoResource=NULL;
    DWORD           dwStatus;
    DWORD           dwChkPtSeq;
    HDMKEY          ResKey;
    HLOCALXSACTION  hXsaction = NULL;
    HLOG            hNewQuoLog=NULL;
    WCHAR           szQuorumLogPath[MAX_PATH];

    if ( !FmpFMGroupsInited ||
         FmpShutdown ) {
        return(ERROR_SUCCESS);
    }

    lstrcpyW(szQuorumLogPath, szRootClusFilePath);
    //lstrcatW(szQuorumLogPath, cszClusLogFileRootDir);

    pResource = OmReferenceObjectById( ObjectTypeResource,
                                      NewQuorumResourceId );
    if (pResource == NULL) 
    {
        ClRtlLogPrint(LOG_UNUSUAL,
                  "[FM] FmpUpdateChangeQuorumResource: Resource <%1!ws!> could not be found....\n",
                   NewQuorumResourceId);
        return(ERROR_SUCCESS);
    }

    DmPauseDiskManTimer();

    //since the resource->quorum is going to change, acquire the quocritsec
    //always acquire the gQuoCritsec before gQuoLock
    ACQUIRE_EXCLUSIVE_LOCK(gQuoChangeLock);
    
    //prevent any resources from going online at this time
    ACQUIRE_EXCLUSIVE_LOCK(gQuoLock);

    //pause any changes to the cluster database
    //always acquire this lock after gQuoLock(refer to the ordering of locks
    // in fminit.c)
    ACQUIRE_EXCLUSIVE_LOCK(gLockDmpRoot);

    //if this resource was already a quorum resource
    if (!pResource->QuorumResource)
    {

        //
        // Now find the current quorum resource.
        //
        OmEnumObjects( ObjectTypeResource,
                       FmpFindQuorumResource,
                       &pOldQuoResource,
                       NULL );
        if ( pOldQuoResource != NULL )
        {
            CL_ASSERT( pOldQuoResource->QuorumResource );
            // Stop the quorum reservation thread!
            pOldQuoResource->QuorumResource = FALSE;
        }
        //set the new resource to be the quorum resource
        pResource->QuorumResource = TRUE;

    }

    //writes to the old log file
    hXsaction = DmBeginLocalUpdate();

    if (!hXsaction)
    {
        dwStatus = GetLastError();
        goto FnExit;
    }
    dwStatus = DmLocalSetValue( hXsaction,
                                DmQuorumKey,
                                cszPath,
                                REG_SZ,
                                (LPBYTE)szQuorumLogPath,
                                (lstrlenW(szQuorumLogPath)+1) * sizeof(WCHAR));

    if (dwStatus != ERROR_SUCCESS)
        goto FnExit;


#ifdef CLUSTER_TESTPOINT
    TESTPT(TpFailLocalXsaction) {
        LPWSTR  pszStr = szQuorumLogPath;
        dwStatus = (MAX_PATH * sizeof(WCHAR));
        dwStatus = DmQuerySz( DmQuorumKey,
                        cszPath,
                        &pszStr,
                        &dwStatus,
                        &dwStatus);
        ClRtlLogPrint(LOG_UNUSUAL,
                   "[FM] Testing failing a local transaction midway- new quorum path %1!ws!\r\n",
                    szQuorumLogPath);
        dwStatus = 999999;
        goto FnExit;
    }
#endif

    dwStatus = DmLocalSetValue( hXsaction,
                                DmQuorumKey,
                                cszMaxQuorumLogSize,
                                REG_DWORD,
                                (LPBYTE)pdwMaxQuorumLogSize,
                                sizeof(DWORD));

    if (dwStatus != ERROR_SUCCESS)
        goto FnExit;


    //if the old quorum resource is different from the new quorum resource
    if ((pOldQuoResource) && (pOldQuoResource != pResource))
    {
        //get/set the new/old resource's flags
        //set the core flag on the new quorum resource
        ResKey = DmOpenKey( DmResourcesKey,
                            NewQuorumResourceId,
                            KEY_READ | KEY_SET_VALUE);
        if (!ResKey)
        {
            dwStatus = GetLastError();
            goto FnExit;
        }
        pResource->ExFlags |= CLUS_FLAG_CORE;
        dwStatus = DmLocalSetValue( hXsaction,
                                    ResKey,
                                    CLUSREG_NAME_FLAGS,
                                    REG_DWORD,
                                    (LPBYTE)&(pResource->ExFlags),
                                    sizeof(DWORD));

        DmCloseKey( ResKey );

        if (dwStatus != ERROR_SUCCESS)
            goto FnExit;

        //unset the core flag on the old quorum resource
        ResKey = DmOpenKey( DmResourcesKey,
                            OmObjectId(pOldQuoResource),
                            KEY_READ | KEY_SET_VALUE);
        if (!ResKey)
        {
            dwStatus = GetLastError();
            goto FnExit;
        }
        pOldQuoResource->ExFlags &= ~CLUS_FLAG_CORE;

        //unset the core flag on the old quorum resource
        dwStatus = DmLocalSetValue( hXsaction,
                                    ResKey,
                                    CLUSREG_NAME_FLAGS,
                                    REG_DWORD,
                                    (LPBYTE)&(pOldQuoResource->ExFlags),
                                    sizeof(DWORD));

        DmCloseKey( ResKey );

        if (dwStatus != ERROR_SUCCESS)
            goto FnExit;


    }
    //
    // Set the quorum resource value.
    //
    dwStatus = DmLocalSetValue( hXsaction,
                                DmQuorumKey,
                                CLUSREG_NAME_QUORUM_RESOURCE,
                                REG_SZ,
                                (CONST BYTE *)OmObjectId(pResource),
                                (lstrlenW(OmObjectId(pResource))+1)*sizeof(WCHAR));


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

    if (pdwQuorumArbTimeout)
    {
        //if this is a mixed mode cluster pdwQourumArbTimeout will be NULL
        // Update the cluster registry from there
        // MM reads this location on startup
        dwStatus = DmLocalSetValue( hXsaction,
                            DmClusterParametersKey,
                           CLUSREG_NAME_QUORUM_ARBITRATION_TIMEOUT,
                           REG_DWORD,
                           (CONST PUCHAR)pdwQuorumArbTimeout,
                           sizeof(DWORD) );
        if ( dwStatus != ERROR_SUCCESS ) 
        {
            ClRtlLogPrint(LOG_UNUSUAL,
               "[FM] FmSetQuorumResource: failed to set the cluster arbitration timeout, status = %1!u!\n", 
               dwStatus);
            goto FnExit;
        }
        
        ClRtlLogPrint(LOG_NOISE,
           "[FM] FmSetQuorumResource: setting QuorumArbitratrionTimeout to be = %1!u!\n", 
           *pdwQuorumArbTimeout);
        // Tell MM about the change...
        MmQuorumArbitrationTimeout = *pdwQuorumArbTimeout;
    }


FnExit:
    if (dwStatus == ERROR_SUCCESS)
    {
        LPWSTR  szClusterName=NULL;
        DWORD   dwSize=0;
        DWORD   dwCharacteristics = CLUS_CHAR_UNKNOWN;

        //commit the update on the old log file,
        //any nodes that were done, will get this change
        //I cant delete this file
        DmCommitLocalUpdate(hXsaction);

        if ( !ARGUMENT_PRESENT ( pdwNewQuorumResourceCharacterictics ) )
        {
            pdwNewQuorumResourceCharacterictics = &dwCharacteristics;
        }

        //
        //  If the caller has passed in characteristics, then don't bother to drop a control
        //  code into a resource dll to find the characteristics in the following function. 
        //  Dropping a control code from within a GUM handler is disaster waiting to happen.
        //
        
        //
        //close the old log file, open the new one and take a checkpoint
        DmSwitchToNewQuorumLog(szQuorumLogPath, *pdwNewQuorumResourceCharacterictics);

        // SS:the buffer should contain the current cluster name ?

        DmQuerySz( DmClusterParametersKey,
                        CLUSREG_NAME_CLUS_NAME,
                        &szClusterName,
                        &dwSize,
                        &dwSize);

        if (szClusterName)
            ClusterEventEx(CLUSTER_EVENT_PROPERTY_CHANGE,
                   EP_FREE_CONTEXT,
                   szClusterName);
        if ((pOldQuoResource) && (pOldQuoResource != pResource))
        {
            //generate the resource property change events
            ClusterEvent( CLUSTER_EVENT_RESOURCE_PROPERTY_CHANGE, 
                pResource );
            ClusterEvent( CLUSTER_EVENT_RESOURCE_PROPERTY_CHANGE, 
                pOldQuoResource );
            
        }            

    }
    else
    {
        if (hXsaction) DmAbortLocalUpdate(hXsaction);
        //reinstall the tombstone
        DmReinstallTombStone(szQuorumLogPath);
        //
        //  Make sure the flags are reset back
        //
        if ((pOldQuoResource) && (pOldQuoResource != pResource))
        {
            pOldQuoResource->QuorumResource = TRUE;
            pResource->QuorumResource = FALSE;
        }
    }
    if (pOldQuoResource) OmDereferenceObject(pOldQuoResource);
    OmDereferenceObject(pResource);
    //release locks
    RELEASE_LOCK(gLockDmpRoot);
    RELEASE_LOCK(gQuoLock);
    RELEASE_LOCK(gQuoChangeLock);

    DmRestartDiskManTimer();

    return(dwStatus);
}

DWORD
FmpUpdateChangeQuorumResource(
    IN BOOL     SourceNode,
    IN LPCWSTR  NewQuorumResourceId,
    IN LPCWSTR  szRootClusFilePath,
    IN LPDWORD  pdwMaxQuorumLogSize
    )

/*++

Routine Description:

    Perform updates related to changing of the quorum resource.

Arguments:

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code otherwise.

--*/

{
    return(FmpUpdateChangeQuorumResource2(SourceNode, NewQuorumResourceId, szRootClusFilePath,
        pdwMaxQuorumLogSize, NULL, NULL));

}



DWORD
FmpUpdateResourceState(
    IN BOOL SourceNode,
    IN LPCWSTR ResourceId,
    IN PGUM_RESOURCE_STATE ResourceState
    )
/*++

Routine Description:

    GUM update handler for resource state changes.

Arguments:

    SourceNode - Supplies whether or not this node was the source of the update

    ResourceId - Supplies the id of the resource whose state is changing

    ResourceState - Supplies the new state of the resource.

Return Value:

    ERROR_SUCCESS if successful

    Win32 error code otherwise

--*/

{
    PFM_RESOURCE resource;

    if ( !FmpFMGroupsInited ) {
        return(ERROR_SUCCESS);
    }

    //
    // This update type is always sent.
    // On the originating node, all of the work must be done by
    // the sending thread.
    // On the non-originating nodes, no locks can be acquired! This
    // would cause some hang situations with operations like move.
    // ... this is okay, since the locking must be done on the sending
    // node anyway, which owns the group.
    //
    if ( SourceNode == FALSE ) {
        resource = OmReferenceObjectById( ObjectTypeResource, ResourceId );

        if ( resource == NULL ) {
            ClRtlLogPrint(LOG_UNUSUAL,
                       "[FM] Gum ResourceState failed to find resource %1!ws!\n",
                       ResourceId);
            CL_LOGFAILURE( ERROR_RESOURCE_NOT_FOUND );
            return(ERROR_SUCCESS);
        }

        ClRtlLogPrint(LOG_NOISE,
                   "[FM] Gum update resource %1!ws!, state %2!u!, current state %3!u!.\n",
                   ResourceId,
                   ResourceState->State,
                   ResourceState->PersistentState);

        //FmpAcquireLocalResourceLock( resource );

        if ( resource->Group->OwnerNode == NmLocalNode ) {
            ClRtlLogPrint(LOG_NOISE,
                       "[FM] Gum ResourceState wrong owner for %1!ws!\n",
                       ResourceId);
        } else {
            resource->State = ResourceState->State;
            resource->PersistentState = ResourceState->PersistentState;
            resource->StateSequence = ResourceState->StateSequence;

            switch ( ResourceState->State ) {
                case ClusterResourceOnline:
                    ClusterEvent( CLUSTER_EVENT_RESOURCE_ONLINE, resource );
                    break;
                case ClusterResourceOffline:
                    ClusterEvent( CLUSTER_EVENT_RESOURCE_OFFLINE, resource );
                    break;
                case ClusterResourceFailed:
                    ClusterEvent( CLUSTER_EVENT_RESOURCE_FAILED, resource );
                    break;
                case ClusterResourceOnlinePending:
                case ClusterResourceOfflinePending:
                    ClusterEvent( CLUSTER_EVENT_RESOURCE_CHANGE, resource );
                    break;
                default:
                    ClRtlLogPrint(LOG_UNUSUAL,
                               "[FM] Gum update resource state, bad state %1!u!\n",
                               ResourceState->State);
                    break;
            }
        }

        OmDereferenceObject( resource );
    }
    return(ERROR_SUCCESS);
}



DWORD
FmpUpdateGroupState(
    IN BOOL SourceNode,
    IN LPCWSTR GroupId,
    IN LPCWSTR NodeId,
    IN PGUM_GROUP_STATE GroupState
    )
/*++

Routine Description:

    GUM update handler for group state changes.

Arguments:

    SourceNode - Supplies whether or not this node was the source of the update

    GroupId - Supplies the id of the resource whose state is changing

    NodeId - Supplies the node id of the group owner.

    GroupState - Supplies the new state of the group.

Return Value:

    ERROR_SUCCESS if successful

    Win32 error code otherwise

--*/

{
    PFM_GROUP group;
    PWSTR     nodeId;
    PNM_NODE  node;

    if ( !FmpFMGroupsInited ) {
        return(ERROR_SUCCESS);
    }

    //
    // This update type is always sent.
    // On the originating node, all of the work must be done by
    // the sending thread.
    // On the non-originating nodes, no locks can be acquired! This
    // would cause some hang situations with operations like move.
    // ... this is okay, since the locking must be done on the sending
    // node anyway, which owns the group.
    //
    if ( SourceNode == FALSE ) {
        group = OmReferenceObjectById( ObjectTypeGroup,
                                       GroupId );

        if ( group == NULL ) {
            ClRtlLogPrint(LOG_UNUSUAL,
                       "[FM] Gum GroupState failed to find group %1!ws!\n",
                       GroupId);
            return(ERROR_SUCCESS);
        }

        ClRtlLogPrint(LOG_NOISE,
                   "[FM] GUM update group %1!ws!, state %2!u!\n",
                   GroupId,
                   GroupState->State);

        if ( group->OwnerNode == NmLocalNode ) {
            ClRtlLogPrint(LOG_NOISE,
                       "[FM] Gum GroupState wrong owner for %1!ws!\n",
                       GroupId);
        } else {
            group->State = GroupState->State;
            group->PersistentState = GroupState->PersistentState;
            group->StateSequence = GroupState->StateSequence;
            node = OmReferenceObjectById( ObjectTypeNode,
                                          NodeId );
            if ( node == NULL ) {
                ClRtlLogPrint(LOG_UNUSUAL,
                           "[FM] Owner of Group %1!ws! cannot be found %2!ws!\n",
                           GroupId,
                           NodeId);
            } else {
                ClRtlLogPrint(LOG_NOISE,
                       "[FM] New owner of Group %1!ws! is %2!ws!, state %3!u!, curstate %4!u!.\n",
                       OmObjectId( group ),
                       OmObjectId( node ),
                       group->State,
                       group->PersistentState);
                if ( !FmpInPreferredList( group, node, FALSE,  NULL ) ) {
                    ClRtlLogPrint( LOG_UNUSUAL,
                                "[FM] New owner %1!ws! is not in preferred list for group %2!ws!.\n",
                                OmObjectId( node ),
                                OmObjectId( group ));
                }
            }
            group->OwnerNode = node;

            switch ( GroupState->State ) {
            case ClusterGroupOnline:
            case ClusterGroupPartialOnline:
                ClusterEvent( CLUSTER_EVENT_GROUP_ONLINE, group );
                break;
            case ClusterGroupOffline:
                ClusterEvent( CLUSTER_EVENT_GROUP_OFFLINE, group );
                break;
            default:
                ClRtlLogPrint(LOG_UNUSUAL,"[FM] Gum update group state, bad state %1!u!\n", GroupState->State);
                break;
            }
        }

        OmDereferenceObject( group );

    }

    return(ERROR_SUCCESS);
}

DWORD
FmpUpdateGroupNode(
    IN BOOL SourceNode,
    IN LPCWSTR GroupId,
    IN LPCWSTR NodeId
    )
/*++

Routine Description:

    GUM update handler for group node changes. This is required for 
notification
    when a group moves between nodes but does not change state (i.e. it was
    already offline)

Arguments:

    SourceNode - Supplies whether or not this node was the source of the update

    GroupId - Supplies the id of the resource whose state is changing

    NodeId - Supplies the node id of the group owner.

Return Value:

    ERROR_SUCCESS if successful

    Win32 error code otherwise

--*/

{
    PFM_GROUP pGroup;
    DWORD     dwStatus = ERROR_SUCCESS;
    PNM_NODE  pNode = NULL;
    PNM_NODE  pPrevNode = NULL;
    
    if ( !FmpFMGroupsInited ) 
    {
        return(ERROR_SUCCESS);
    }

    pGroup = OmReferenceObjectById( ObjectTypeGroup,
                                    GroupId );

    if (pGroup == NULL)
    {
        ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateGroupNode: GroupID = %1!ws! could not be found...\n",
                   GroupId);
        //
        //  Chittur Subbaraman (chitturs) - 6/12/99
        //
        //  Return ERROR_SUCCESS here since this is what NT4 side does.
        //  Compatibility pain !
        //
        goto FnExit;
    }


    pNode = OmReferenceObjectById(ObjectTypeNode,
                                    NodeId);

    if (pNode == NULL)
    {
        dwStatus = ERROR_CLUSTER_NODE_NOT_FOUND;
        goto FnExit;
    }
    //
    // HACKHACK: Chittur Subbaraman (chitturs) - 5/20/99
    // Comment out as a temporary solution to avoid deadlocks.
    //
    // FmpAcquireLocalGroupLock(pGroup);
    
    pPrevNode = pGroup->OwnerNode;

    //set the new owner node, incr ref count
    OmReferenceObject(pNode);
    pGroup->OwnerNode = pNode;

    //decr ref count on previous owner
    OmDereferenceObject(pPrevNode);
    //
    // HACKHACK: Chittur Subbaraman (chitturs) - 5/20/99
    // Comment out as a temporary solution to avoid deadlocks.
    //
    // FmpReleaseLocalGroupLock(pGroup);

    //generate an event to signify group owner node change
    ClusterEvent(CLUSTER_EVENT_GROUP_CHANGE, pGroup);
    
FnExit:
    if (pGroup) OmDereferenceObject(pGroup);
    if (pNode) OmDereferenceObject(pNode);
    return(dwStatus);
}


DWORD
FmpUpdateChangeClusterName(
    IN BOOL     SourceNode,
    IN LPCWSTR  szNewName
    )
/*++

Routine Description:

    GUM update routine for changing the name of the cluster.

    This changes the name property of the core network name resource
    as well.  The resource is notified about it by a worker thread that
    the name has been changed.

Arguments:

    SourceNode - Supplies whether or not this node originated the update.

    NewName - Supplies the new name of the cluster.

Return Value:

    ERROR_SUCCESS if successful

    Win32 error code otherwise

--*/

{
    LPWSTR          Buffer;
    DWORD           Length;
    DWORD           Status = ERROR_SUCCESS;
    LPWSTR          ClusterNameId=NULL;
    DWORD           idMaxSize = 0;
    DWORD           idSize = 0;
    PFM_RESOURCE    Resource=NULL;
    HDMKEY          ResKey = NULL;
    HDMKEY          ParamKey = NULL;
    HLOCALXSACTION  hXsaction=NULL;
    DWORD           cbNewClusterName;
    LPWSTR          lpszNewClusterName;

    if ( !FmpFMGroupsInited ||
         FmpShutdown ) 
    {
        return(ERROR_SUCCESS);
    }

    cbNewClusterName = ( lstrlen ( szNewName ) + 1 ) * sizeof ( WCHAR );

    hXsaction = DmBeginLocalUpdate();

    if (!hXsaction)
    {
        Status = ERROR_SUCCESS;
        goto FnExit;

    }
    //find the core network name resource, set its private properties
    Status = DmQuerySz( DmClusterParametersKey,
                        CLUSREG_NAME_CLUS_CLUSTER_NAME_RES,
                        (LPWSTR*)&ClusterNameId,
                        &idMaxSize,
                        &idSize);
    if (Status != ERROR_SUCCESS) {
        ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateChangeClusterName: failed to get cluster name resource, status=%1!u!.\n",
                   Status);
        goto FnExit;
    }

    //
    // Reference the specified resource ID.
    //
    Resource = OmReferenceObjectById( ObjectTypeResource, ClusterNameId );
    if (Resource == NULL) {
        Status = ERROR_RESOURCE_NOT_FOUND;
        ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateChangeClusterName: failed to find the cluster name resource, status=%1!u!\n",
                   Status);
        goto FnExit;
    }


    ResKey = DmOpenKey(DmResourcesKey, ClusterNameId, KEY_READ | KEY_SET_VALUE);
    if (!ResKey)
    {
        Status = GetLastError();
        goto FnExit;
    }
    ParamKey = DmOpenKey(ResKey, cszParameters, KEY_READ | KEY_SET_VALUE);

    if (!ParamKey)
    {
        Status = GetLastError();
        goto FnExit;
    }

    Status = DmLocalSetValue(hXsaction,
                ParamKey,
                CLUSREG_NAME_NET_NAME,
                REG_SZ,
                (CONST BYTE *)szNewName,
                cbNewClusterName);

    if ( Status != ERROR_SUCCESS )
    {
        ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateChangeClusterName: failed to set the Name property, Status=%1!u!\n",
                   Status);
        goto FnExit;
    }
    
    //update the default cluster name
    Status = DmLocalSetValue(hXsaction,
                    DmClusterParametersKey,
                    CLUSREG_NAME_CLUS_NAME,
                    REG_SZ,
                    (CONST BYTE *)szNewName,
                    cbNewClusterName);

    if (Status != ERROR_SUCCESS)
    {
        ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateChangeClusterName: failed to set the cluster name property,Status=%1!u!\n",
                   Status);
        goto FnExit;
    }

    //
    //  Update the CsClusterName variable. 
    // TODO: Need synchronization on CsClusterName
    //
    lpszNewClusterName = LocalAlloc ( LPTR, cbNewClusterName );

    if ( lpszNewClusterName == NULL )
    {
        Status = GetLastError();
        ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpUpdateChangeClusterName: Alloc for name failed, Status=%1!u!\n",
                      Status);
        goto FnExit;
    }
    
    LocalFree ( CsClusterName );

    CsClusterName = lpszNewClusterName;
    
    lstrcpy ( CsClusterName, szNewName );
    
    //the cluster_event_property_change is generated by the api itself using 
    //the cluster wide event after netname has finished the applying the changes

FnExit:
    if (ClusterNameId) LocalFree(ClusterNameId);
    if (ParamKey) DmCloseKey(ParamKey);
    if (ResKey) DmCloseKey(ResKey);
    if (Resource) OmDereferenceObject(Resource);
    if (hXsaction) 
    {
        if (Status == ERROR_SUCCESS) 
            DmCommitLocalUpdate(hXsaction);
        else 
            DmAbortLocalUpdate(hXsaction);
    }
    return(Status);
}


DWORD
FmpUpdateChangeResourceName(
    IN BOOL bSourceNode,
    IN LPCWSTR lpszResourceId,
    IN LPCWSTR lpszNewName
    )
/*++

Routine Description:

    GUM dispatch routine for changing the friendly name of a resource.

Arguments:

    bSourceNode - Supplies whether or not this node initiated the GUM update.
        Not used.

    lpszResourceId - Supplies the resource ID.

    lpszNewName - Supplies the new friendly name.

Return Value:

    ERROR_SUCCESS if successful.

    Win32 error code otherwise.

--*/

{
    PFM_RESOURCE pResource = NULL;
    DWORD dwStatus;
    HDMKEY      hKey = NULL;
    DWORD       dwDisposition;
    HLOCALXSACTION      
                hXsaction = NULL;
    PFM_RES_CHANGE_NAME  pResChangeName = NULL; 

    if ( !FmpFMGroupsInited ||
         FmpShutdown ) {
        return( ERROR_SUCCESS );
    }

    //
    //  Chittur Subbaraman (chitturs) - 6/28/99
    //
    //  Restructure this GUM update as a local transaction.
    //
    //
    pResource = OmReferenceObjectById( ObjectTypeResource, lpszResourceId );

    if ( pResource == NULL ) 
    {
        ClRtlLogPrint(LOG_UNUSUAL,
                  "[FM] FmpUpdateChangeResourceName: Resource <%1!ws!> could not be found....\n",
                   lpszResourceId);
        return( ERROR_RESOURCE_NOT_FOUND );
    }

    ClRtlLogPrint(LOG_NOISE,
               "[FM] FmpUpdateChangeResourceName: Entry for resource <%1!ws!>, New name = <%2!ws!>...\n",
                lpszResourceId,
                lpszNewName);

    //
    // Start a transaction
    //
    hXsaction = DmBeginLocalUpdate();

    if ( !hXsaction )
    {
        dwStatus = GetLastError();
        ClRtlLogPrint(LOG_UNUSUAL,
                  "[FM] FmpUpdateChangeResourceName: Failed in starting a transaction for resource %1!ws!, Status =%2!d!....\n",
                   lpszResourceId,
                   dwStatus);
        goto FnExit;
    }

    //
    // Open the resources key.
    //
    hKey = DmLocalCreateKey( hXsaction,
                             DmResourcesKey,
                             lpszResourceId,
                             0,
                             KEY_READ | KEY_WRITE,
                             NULL,
                             &dwDisposition );
                            
    if ( hKey == NULL ) 
    {
        dwStatus = GetLastError();
        ClRtlLogPrint(LOG_UNUSUAL,
                  "[FM] FmpUpdateChangeResourceName: Failed in opening the resources key for resource %1!ws!, Status =%2!d!....\n",
                   lpszResourceId,
                   dwStatus);
        goto FnExit;
    }
    
    CL_ASSERT( dwDisposition != REG_CREATED_NEW_KEY ); 

    //
    // Set the resource name in the registry
    //
    dwStatus = DmLocalSetValue( hXsaction,
                                hKey,
                                CLUSREG_NAME_RES_NAME,
                                REG_SZ,
                                ( CONST BYTE * ) lpszNewName,
                                ( lstrlenW( lpszNewName ) + 1 ) * 
                                    sizeof( WCHAR ) );

    if( dwStatus != ERROR_SUCCESS )
    {
        ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateChangeResourceName: DmLocalSetValue for resource %1!ws! fails, Status = %2!d!...\n",
                   lpszResourceId,
                   dwStatus);
        goto FnExit;     
    }

    pResChangeName = LocalAlloc( LMEM_FIXED,
                                 lstrlenW( lpszNewName ) * sizeof ( WCHAR ) + 
                                   sizeof( FM_RES_CHANGE_NAME ) );

    if ( pResChangeName == NULL ) 
    {
        dwStatus = GetLastError();
        ClRtlLogPrint(LOG_UNUSUAL,
                  "[FM] FmpUpdateChangeResourceName: Unable to allocate memory for ResChangeName structure for resource <%1!ws!>, Status =%2!d!....\n",
                   lpszResourceId,
                   dwStatus);
        goto FnExit;    
    }

    dwStatus = OmSetObjectName( pResource, lpszNewName );

    if ( dwStatus != ERROR_SUCCESS )
    {
        ClRtlLogPrint(LOG_CRITICAL,
                  "[FM] FmpUpdateChangeResourceName: Unable to set name <%3!ws!> for resource <%1!ws!>, Status =%2!d!....\n",
                   lpszResourceId,
                   dwStatus,
                   lpszNewName );
        LocalFree( pResChangeName );
        goto FnExit;
    }

    pResChangeName->pResource = pResource;

    lstrcpyW( pResChangeName->szNewResourceName, lpszNewName );

    //
    //  The FM worker thread will free the memory for the pResChangeName
    //  structure as well as dereference the pResource object.
    //
    FmpPostWorkItem( FM_EVENT_RESOURCE_NAME_CHANGE, pResChangeName, 0 );   

    pResource = NULL;

    ClRtlLogPrint(LOG_NOISE,
               "[FM] FmpUpdateChangeResourceName: Successfully changed name of resource <%1!ws!> to <%2!ws!>...\n",
                lpszResourceId,
                lpszNewName);

FnExit:
    if ( pResource != NULL )
    {
        OmDereferenceObject( pResource );
    }

    if ( hKey != NULL ) 
    {
        DmCloseKey( hKey );
    }

    if ( ( dwStatus == ERROR_SUCCESS ) && ( hXsaction ) ) 
    {
        DmCommitLocalUpdate( hXsaction );
    }
    else
    {
        if ( hXsaction ) DmAbortLocalUpdate( hXsaction );
    }

    ClRtlLogPrint(LOG_NOISE,
               "[FM] FmpUpdateChangeResourceName: Exit for resource %1!ws!, Status=%2!u!...\n",
                lpszResourceId,
                dwStatus);

    return( dwStatus );
}


/****
@func       DWORD | FmpUpdatePossibleNodesForResType| This update is called to 
            update the possible nodes for a resource type.  

@parm       IN BOOL | SourceNode | set to TRUE, if the update originated at this
            node.
            
@parm       IN LPCWSTR | lpszResTypeName | The name of the resource type.

@parm       IN DWORD | dwBufLength | The size of the multi-sz string pointed
            to by pBuf

@parm       IN PVOID | pBuf | A pointer to the buffer containing the names of 
            the nodes that support this resource type.

@comm       The possible list of nodes that supports the given resource type is 
            updated with the list provided.

@rdesc      Returns a result code. ERROR_SUCCESS on success.

@xref       <f FmpDecisionPossibleDmSwitchToNewQuorumLog>
****/
DWORD
FmpUpdatePossibleNodeForResType(
    IN BOOL         SourceNode,
    IN LPCWSTR      lpszResTypeName,
    IN LPDWORD      pdwBufLength,
    IN PVOID        pBuf
    )
{
    PFM_RESTYPE         pResType;
    DWORD               dwStatus;
    HDMKEY              hResTypeKey = NULL;
    HLOCALXSACTION      hXsaction = NULL;
    LIST_ENTRY          NewPosNodeList;
    PLIST_ENTRY         pListEntry;
    PRESTYPE_POSSIBLE_ENTRY pResTypePosEntry = NULL;

    //
    //  Chittur Subbaraman (chitturs) - 5/13/99
    // 
    //  Don't check for FmpFMGroupsInited condition since this GUM
    //  handler is called by the forming node before that variable
    //  is set to TRUE. This update always comes after the 
    //  corresponding restypes have been created and is made
    //  internally by the clussvc following this order. Note that
    //  a joining node cannot receive this update until groups are
    //  inited since GUM receive updates are turned on only after 
    //  the FmpFMGroupsInited variable is set to TRUE. Also, the
    //  intracluster RPC is fired up in a forming node only after
    //  the groups are inited. Hence, there is no major danger 
    //  of this GUM handler being called if the corresponding 
    //  restype is not created.
    //
    if ( FmpShutdown ) {
        return(ERROR_SUCCESS);
    }

    InitializeListHead(&NewPosNodeList);

    pResType = OmReferenceObjectById( ObjectTypeResType,
                                      lpszResTypeName);



    if (!pResType)
    {
        dwStatus = ERROR_CLUSTER_RESOURCE_TYPE_NOT_FOUND;
        goto FnExit;
    }

    dwStatus = FmpAddPossibleNodeToList(pBuf, *pdwBufLength, &NewPosNodeList);

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


    //writes to the old log file
    hXsaction = DmBeginLocalUpdate();

    if (!hXsaction)
    {
        dwStatus = GetLastError();
        goto FnExit;
    }

    hResTypeKey = DmOpenKey(DmResourceTypesKey,
                   lpszResTypeName,
                   KEY_READ | KEY_WRITE);
    if (hResTypeKey == NULL) 
    {
        dwStatus = GetLastError();
        goto FnExit;
    }

    //if there are no possible owners, delete the value
    if (pBuf && *pdwBufLength)
    {
        dwStatus = DmLocalSetValue( hXsaction,
                                hResTypeKey,
                                CLUSREG_NAME_RESTYPE_POSSIBLE_NODES,
                                REG_MULTI_SZ,
                                (LPBYTE)pBuf,
                                *pdwBufLength);
    }
    else
    {
        dwStatus = DmLocalDeleteValue( hXsaction,
                                hResTypeKey,
                                CLUSREG_NAME_RESTYPE_POSSIBLE_NODES);
                                
        if (dwStatus == ERROR_FILE_NOT_FOUND)
        {
            dwStatus = ERROR_SUCCESS;
        }
    }


FnExit:
    if (dwStatus == ERROR_SUCCESS)
    {
        //commit the update on the old log file,
        //any nodes that were done, will get this change
        //I cant delete this file
        DmCommitLocalUpdate(hXsaction);

        ACQUIRE_EXCLUSIVE_LOCK(gResTypeLock);
        
        //free the old list
        while (!IsListEmpty(&pResType->PossibleNodeList))
        {
            pListEntry = RemoveHeadList(&pResType->PossibleNodeList);
            pResTypePosEntry = CONTAINING_RECORD(pListEntry, RESTYPE_POSSIBLE_ENTRY, 
                PossibleLinkage);
            OmDereferenceObject(pResTypePosEntry->PossibleNode);
            LocalFree(pResTypePosEntry);
        }
        //now switch the possible owners list for the
        //resource type
        while (!IsListEmpty(&(NewPosNodeList)))
        {
            //remove from the new prepared list and hang
            //it of the restype structure
            pListEntry = RemoveHeadList(&NewPosNodeList);
            InsertTailList(&pResType->PossibleNodeList, pListEntry);
            pResTypePosEntry = CONTAINING_RECORD(pListEntry, RESTYPE_POSSIBLE_ENTRY, 
                PossibleLinkage);
            
            ClRtlLogPrint(LOG_NOISE,
                       "[FM] FmpUpdatePossibleNodesForRestype:Adding node  %1!ws! to %2!ws! resource type's possible node list...\n",
                        OmObjectId(pResTypePosEntry->PossibleNode),
                        lpszResTypeName);
            

        }

        RELEASE_LOCK(gResTypeLock);
        
        ClusterEvent( CLUSTER_EVENT_RESTYPE_PROPERTY_CHANGE,
                                pResType );

    
    }
    else
    {
        //free up the NewPostNodeList
        if (hXsaction) DmAbortLocalUpdate(hXsaction);
        //if a new list was prepared, free it
        while (!IsListEmpty(&(NewPosNodeList)))
        {
            pListEntry = RemoveHeadList(&NewPosNodeList);
            pResTypePosEntry = CONTAINING_RECORD(pListEntry, RESTYPE_POSSIBLE_ENTRY, 
                PossibleLinkage);
            OmDereferenceObject(pResTypePosEntry->PossibleNode);
            LocalFree(pResTypePosEntry);
        }

        
    }
    if (hResTypeKey) DmCloseKey(hResTypeKey);
    if (pResType) OmDereferenceObject(pResType);

    return(dwStatus);
}


/****
@func       DWORD | FmpDecidePossibleNodeForResType| When the quorum resource is changed,
            the FM invokes this api on the owner node of the new quorum resource
            to create a new quorum log file.

@parm       IN PVOID | pResource | The new quorum resource.
@parm       IN LPCWSTR | lpszPath | The path for temporary cluster files.
@parm       IN DWORD | dwMaxQuoLogSize | The maximum size limit for the quorum log file.

@comm       When a quorum resource is changed, the fm calls this funtion before it
            updates the quorum resource.  If a new log file needs to be created,
            a checkpoint is taken.

@rdesc      Returns a result code. ERROR_SUCCESS on success.

@xref       <f DmSwitchToNewQuorumLog>
****/
DWORD FmpDecidePossibleNodeForResType
(
    IN PGUM_VOTE_DECISION_CONTEXT pDecisionContext,
    IN DWORD dwVoteBufLength,
    IN PVOID pVoteBuf,
    IN DWORD dwNumVotes,
    IN BOOL  bDidAllActiveNodesVote,
    OUT LPDWORD pdwOutputBufSize,
    OUT PVOID   *ppOutputBuf
)
{
    DWORD                               dwStatus = ERROR_SUCCESS;
    DWORD                               i;
    PFMP_VOTE_POSSIBLE_NODE_FOR_RESTYPE pFmpVote;
    LPWSTR                              lpmszPossibleNodes = NULL;
    DWORD                               dwlpmszLen = 0;
    PVOID                               pGumBuffer = NULL;
    DWORD                               dwNodeId;
    WCHAR                               szNodeId[NODE_ID_SZ];
    LPWSTR                              lpmszCurrentPossibleNodes=NULL;
    BOOL                                bChange = FALSE;
    HDMKEY                              hResTypeKey = NULL;
    DWORD                               dwSize;
    DWORD                               dwStringBufSize = 0;
    BOOL                                bAssumeSupported;
    LPWSTR                              TypeName = NULL;

    //First get the type name from pDecisionContext
    
    TypeName=(LPWSTR)LocalAlloc(LMEM_FIXED,pDecisionContext->dwInputBufLength);

    if(TypeName==NULL)
    {
        ClRtlLogPrint(LOG_CRITICAL,"[FM] FmpDecidePossibleNodeForResType: Not Enough Memory, error= %1!d!\r\n",
                     GetLastError());
        goto FnExit;                             
    }

    CopyMemory(TypeName,pDecisionContext->pInputBuf,pDecisionContext->dwInputBufLength);

    //initialize the out params
    *ppOutputBuf = NULL;
    *pdwOutputBufSize = 0;

    bAssumeSupported= *((BOOL*)pDecisionContext->pContext);

    if (bAssumeSupported)
    {
        hResTypeKey = DmOpenKey(DmResourceTypesKey,
                   TypeName,
                   KEY_READ | KEY_WRITE);
        if (hResTypeKey == NULL) 
        {
            dwStatus = GetLastError();
            CL_LOGFAILURE(dwStatus);
            goto FnExit;
        }

        //pass the current possible node list to the decider
        dwStatus = DmQueryString(hResTypeKey,
                                CLUSREG_NAME_RESTYPE_POSSIBLE_NODES,
                                REG_MULTI_SZ,
                                &lpmszCurrentPossibleNodes,
                                &dwStringBufSize,
                                &dwSize);
        if (dwStatus != ERROR_SUCCESS)
        {
            //if the possible node list is not found this is ok
            //ie. only if there is some other error we give up
            if ( dwStatus != ERROR_FILE_NOT_FOUND ) 
            {
                CL_LOGFAILURE(dwStatus);
                goto FnExit;
            }
            
        }
        DmCloseKey(hResTypeKey);
        hResTypeKey = NULL;
    }
    
    //if the current list is passed in, dont remove any possible
    //nodes from the list if they dont vote, simply add the new ones
    if (lpmszCurrentPossibleNodes)
    {
        DWORD   dwStrLen;
        
        //make a copy of the multi-sz
        dwlpmszLen = ClRtlMultiSzLength(lpmszCurrentPossibleNodes);

        dwStrLen = dwlpmszLen * sizeof(WCHAR);
        lpmszPossibleNodes = LocalAlloc(LMEM_FIXED, dwStrLen);
        if (!lpmszPossibleNodes)
        {
            dwStatus = ERROR_NOT_ENOUGH_MEMORY;
            CL_LOGFAILURE(dwStatus);
            goto FnExit;
        }
        CopyMemory(lpmszPossibleNodes, lpmszCurrentPossibleNodes, dwStrLen);
    }        
    for (i = 0; i< dwNumVotes; i++)
    {
        pFmpVote = (PFMP_VOTE_POSSIBLE_NODE_FOR_RESTYPE) 
            GETVOTEFROMBUF(pVoteBuf, pDecisionContext->dwVoteLength, i+1 , &dwNodeId);         
        //if not a valid vote, skip
        if (!pFmpVote)
            continue;
        CL_ASSERT((PBYTE)pFmpVote <= ((PBYTE)pVoteBuf + dwVoteBufLength - 
            sizeof(FMP_VOTE_POSSIBLE_NODE_FOR_RESTYPE)));
        szNodeId[ NODE_ID_SZ-1 ] = UNICODE_NULL;
        _snwprintf( szNodeId, NODE_ID_SZ-1, L"%d" , dwNodeId );
        if (pFmpVote->bPossibleNode)
        {
            if (lpmszCurrentPossibleNodes)
            {
                //if the string is already there, dont append it again
                if (ClRtlMultiSzScan(lpmszCurrentPossibleNodes, szNodeId))
                    continue;

            }
            dwStatus = ClRtlMultiSzAppend(&lpmszPossibleNodes,
                    &dwlpmszLen, szNodeId);
            bChange = TRUE;                    
            if (dwStatus != ERROR_SUCCESS)
                goto FnExit;
                    
        }
        else
        {
            //if a current list was specified
            //this node is not a possible node anymore, remove it from the list
            if (lpmszCurrentPossibleNodes)
            {
                ClRtlLogPrint(LOG_NOISE,
                            "[FM] FmpDecidePossibleNodesForRestype: Removing node %1!ws! from  %2!ws! restype possibleowner list \r\n",
                            szNodeId,TypeName);
                dwStatus = ClRtlMultiSzRemove(lpmszPossibleNodes, &dwlpmszLen, szNodeId);
                if (dwStatus == ERROR_SUCCESS)
                {
                    //if the node is successfully removed
                    bChange = TRUE;
                }
                else if (dwStatus != ERROR_FILE_NOT_FOUND)
                {
                    //if the node exists but cannot be removed return with error
                    //if the node didnt exist, we dont do anything bChange remains
                    //set at FALSE
                    goto FnExit;
                }
                else
                {
                    dwStatus = ERROR_SUCCESS;
                }
                
            }                
        }
    }

    //if nothing has changed dont issue a gum update
    if (!bChange)
    {
        dwStatus = ERROR_ALREADY_EXISTS;
        goto FnExit;
    }

    //dwlpmszLen contains the size of the multi-sz string in the
    //number of characters, make it the number of bytes
    dwlpmszLen *= sizeof(WCHAR);
    
    pGumBuffer = GumMarshallArgs(pdwOutputBufSize, 3, 
        pDecisionContext->dwInputBufLength, pDecisionContext->pInputBuf, 
        sizeof(DWORD), &dwlpmszLen, dwlpmszLen, lpmszPossibleNodes);

    *ppOutputBuf = pGumBuffer;
        
FnExit:
    if (lpmszPossibleNodes) LocalFree(lpmszPossibleNodes);
    if (hResTypeKey)
        DmCloseKey(hResTypeKey);
    if (lpmszCurrentPossibleNodes)
        LocalFree(lpmszCurrentPossibleNodes);
    if(TypeName)
        LocalFree(TypeName);

    return(dwStatus);
}



/****
@func       DWORD | FmpUpdateChangeResourceNode| This update is called to 
            update the possible nodes for a resource.  

@parm       IN BOOL | SourceNode | set to TRUE, if the update originated at this
            node.
            
@parm       IN PFM_RESOURCE | pResource | A pointer to the resource whose
            possible node list is being updated.

@parm       IN PNM_NODE | pNode | A pointer to the node to be added/removed
            from the possible node lis.

@parm       IN  DWORD | dwControlCode | If CLUSCTL_RESOURCE_ADD_OWNER then
            the node is added to the possible node list, else it is removed.

@comm       The possible list of nodes for a resource is updated.

@rdesc      Returns a result code. ERROR_SUCCESS on success.

****/
DWORD
FmpUpdateChangeResourceNode(
    IN BOOL         SourceNode,
    IN PFM_RESOURCE pResource,
    IN PNM_NODE     pNode,
    IN DWORD        dwControlCode
    )
{
    DWORD               dwStatus;
    HDMKEY              hResKey = NULL;
    HLOCALXSACTION      hXsaction = NULL;

    //Dont acquire the local resource lock since acquiring that
    //within gum updates causes deadlock
    //use the global resource lock to synchronize this call
    //with the enumeration of possible nodes
    FmpAcquireResourceLock();

    //start a transaction
    hXsaction = DmBeginLocalUpdate();

    if (!hXsaction)
    {
        dwStatus = GetLastError();
        goto FnExit;
    }

    //
    //  BUGBUG:  What if in-memory stuff succeeds and registry stuff fails ? We get into
    //  inconsistent state. Better to move this code to the end after succeeding in
    //  registry changes.
    //
    if (dwControlCode == CLUSCTL_RESOURCE_ADD_OWNER) 
    {
        dwStatus = FmpAddPossibleNode(pResource,
                                    pNode);
    } else 
    {
        dwStatus = FmpRemovePossibleNode(pResource,
                                       pNode,
                                       FALSE);
    }
    if (dwStatus != ERROR_SUCCESS) 
    {
        ClRtlLogPrint( LOG_NOISE,
                    "[FM] FmpUpdateChangeResourceNode, failed possible node updatefor resource <%1!ws!>, error %2!u!\n",
                    OmObjectName(pResource),
                    dwStatus );
        goto FnExit;                    
    }
                
    //fix the registry
    //SS - do we need to fix the preferred node list
    hResKey = DmOpenKey(DmResourcesKey,
                       OmObjectId(pResource),
                       KEY_READ | KEY_WRITE);
    if (hResKey == NULL) 
    {
        dwStatus = GetLastError();
        goto FnExit;
    }

    if (dwControlCode == CLUSCTL_RESOURCE_ADD_OWNER) 
    {
        dwStatus = DmLocalAppendToMultiSz(
                            hXsaction,
                            hResKey,
                            CLUSREG_NAME_RES_POSSIBLE_OWNERS,
                            OmObjectId(pNode));
    }
    else
    {
        dwStatus = DmLocalRemoveFromMultiSz(
                            hXsaction,
                            hResKey,
                            CLUSREG_NAME_RES_POSSIBLE_OWNERS,
                            OmObjectId(pNode));
        if (dwStatus == ERROR_FILE_NOT_FOUND) 
        {
            DWORD       i;
            DWORD       Result;
            PNM_NODE    pEnumNode;                

            //
            // Possible nodes did not exist, so create a new entry
            // with every possible node in it. FM will already have
            // removed the passed in node from the possible node list.
            //
            i=0;
            do {
                Result = FmEnumResourceNode(pResource,
                                            i,
                                            &pEnumNode);
                if (Result == ERROR_SUCCESS) 
                {
                    dwStatus = DmLocalAppendToMultiSz(
                                    hXsaction,
                                    hResKey,
                                    CLUSREG_NAME_RES_POSSIBLE_OWNERS,
                                    OmObjectId(pEnumNode));
                    OmDereferenceObject(pEnumNode);

                } 
                else if ((Result == ERROR_NO_MORE_ITEMS) &&
                           (i == 0)) 
                {
                    //
                    // This is a funny corner case where there is a one
                    // node cluster and a resource with no possibleowners
                    // entry, and somebody removes the only node in the cluster
                    // from the possible owners list. Set PossibleOwners to
                    // the empty set.
                    //
                    dwStatus = DmLocalSetValue(
                                    hXsaction,
                                    hResKey,
                                    CLUSREG_NAME_RES_POSSIBLE_OWNERS,
                                    REG_MULTI_SZ,
                                    (CONST BYTE *)L"\0",
                                    2);

                }
                ++i;
            } while ( Result == ERROR_SUCCESS );
            //map the error to success
            dwStatus = ERROR_SUCCESS;
        }
    }
    
    DmCloseKey(hResKey);
            

FnExit:        
    //release the lock
    FmpReleaseResourceLock();
    if (dwStatus == ERROR_SUCCESS)
    {
        //commit the update on the old log file,
        //any nodes that were done, will get this change
        //I cant delete this file
        DmCommitLocalUpdate(hXsaction);

    }
    else
    {
        //SS: BUGBUG :: validation for possible node should
        //be done before the registry is switched
        //the inmemory structure should be changed only on success
        //if there is a failure in the registry apis..the
        //in memory structure will be out of sync with registry
        if (hXsaction) DmAbortLocalUpdate(hXsaction);
    }

    return(dwStatus);
}


/****
@func       DWORD | FmpUpdateChangeResourceGroup| This update is called to 
            update the group to which the resource belongs.

@parm       IN BOOL | bSourceNode | set to TRUE, if the update originated at this
            node.
            
@parm       IN PFM_RESOURCE | pResource | A pointer to the resource whose
            possible node list is being updated.

@parm       IN PFM_GROUP | pNewGroup | A pointer to the node to be added/removed
            from the possible node lis.

@comm       The possible list of nodes for a resource is updated.

@rdesc      Returns a result code. ERROR_SUCCESS on success.

****/
DWORD FmpUpdateChangeResourceGroup(
    IN BOOL         bSourceNode,
    IN PFM_RESOURCE pResource,
    IN PFM_GROUP    pNewGroup)
{
    DWORD               dwStatus = ERROR_SUCCESS;
    PFM_GROUP           pOldGroup;
    PFM_DEPENDENCY_TREE pTree = NULL;
    HLOCALXSACTION      hXsaction = NULL;
    HDMKEY              hOldGroupKey = NULL;
    HDMKEY              hNewGroupKey = NULL;
    PLIST_ENTRY         pListEntry;
    PFM_DEPENDTREE_ENTRY pEntry;

    pOldGroup = pResource->Group;

    //
    // Check to make sure the resource is not already in the group.
    //
    if (pOldGroup == pNewGroup) 
    {
        dwStatus = ERROR_ALREADY_EXISTS;
        goto FnExit;
    }

    //
    // 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( pOldGroup ), OmObjectId( pNewGroup ) ) <= 0 ) 
    {
        FmpAcquireLocalGroupLock( pOldGroup );
        FmpAcquireLocalGroupLock( pNewGroup );
    } 
    else 
    {
        FmpAcquireLocalGroupLock( pNewGroup );
        FmpAcquireLocalGroupLock( pOldGroup );
    }

    //start a transaction
    hXsaction = DmBeginLocalUpdate();

    if (!hXsaction)
    {
        dwStatus = GetLastError();
        goto FnUnlock;
    }

    //
    // For now... both Groups must be owned by the same node.
    //
    if ( pResource->Group->OwnerNode != pNewGroup->OwnerNode ) 
    {
        dwStatus = ERROR_HOST_NODE_NOT_GROUP_OWNER;
        goto FnUnlock;
    }


    //
    // Create a full dependency tree, 
    //
    pTree = FmCreateFullDependencyTree(pResource);
    if ( pTree == NULL )
    {
        dwStatus = GetLastError();        
        goto FnUnlock;
    }
   

    //
    // Add each resource in the dependency tree to its new group's list.
    //
    hNewGroupKey = DmOpenKey(DmGroupsKey,
                            OmObjectId(pNewGroup),
                            KEY_READ | KEY_WRITE);
    if (hNewGroupKey == NULL) {
        dwStatus = GetLastError();
        goto FnUnlock;
    }
    hOldGroupKey = DmOpenKey(DmGroupsKey,
                            OmObjectId(pOldGroup),
                            KEY_READ | KEY_WRITE);
    if (hOldGroupKey == NULL) {
        dwStatus = GetLastError();
        goto FnUnlock;
    }

    //
    // For each resource in the dependency tree, remove it from the
    // old group list and add it to the new group list
    //
    pListEntry = pTree->ListHead.Flink;
    while (pListEntry != &pTree->ListHead) {
        pEntry = CONTAINING_RECORD(pListEntry,
                                  FM_DEPENDTREE_ENTRY,
                                  ListEntry);
        pListEntry = pListEntry->Flink;

        dwStatus = DmLocalRemoveFromMultiSz(hXsaction,
                        hOldGroupKey,
                        CLUSREG_NAME_GRP_CONTAINS,
                        OmObjectId(pEntry->Resource));

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

        dwStatus = DmLocalAppendToMultiSz(hXsaction,
                        hNewGroupKey,
                        CLUSREG_NAME_GRP_CONTAINS,
                        OmObjectId(pEntry->Resource));

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

    }
    
    //
    // Passed all the checks, do the in-memorymove.
    //
    pListEntry = pTree->ListHead.Flink;
    while (pListEntry != &pTree->ListHead) 
    {
        pEntry = CONTAINING_RECORD(pListEntry,
                                  FM_DEPENDTREE_ENTRY,
                                  ListEntry);
        pListEntry = pListEntry->Flink;

        //
        // Move this resource
        //
        RemoveEntryList(&pEntry->Resource->ContainsLinkage);

        InsertHeadList(&pNewGroup->Contains,
                       &pEntry->Resource->ContainsLinkage);
        OmReferenceObject(pNewGroup);
        pEntry->Resource->Group = pNewGroup;
        ++pEntry->Resource->StateSequence;

        ClusterEvent(CLUSTER_EVENT_RESOURCE_CHANGE,pEntry->Resource);
        OmDereferenceObject(pOldGroup);
    }

FnUnlock:
    //
    // Now release all locks.
    //
    FmpReleaseLocalGroupLock( pNewGroup );
    FmpReleaseLocalGroupLock( pOldGroup );

FnExit:
    if (pTree) FmDestroyFullDependencyTree(pTree);
    if (hOldGroupKey) DmCloseKey(hOldGroupKey);
    if (hNewGroupKey) DmCloseKey(hNewGroupKey);
    if (dwStatus == ERROR_SUCCESS)
    {
        ClusterEvent(CLUSTER_EVENT_GROUP_PROPERTY_CHANGE,pNewGroup);
        ClusterEvent(CLUSTER_EVENT_GROUP_PROPERTY_CHANGE,pOldGroup);
        DmCommitLocalUpdate(hXsaction);
    }
    else
    {
        if (hXsaction) DmAbortLocalUpdate(hXsaction);
    }

    
    return(dwStatus);

}

DWORD
FmpUpdateAddDependency(
    IN BOOL SourceNode,
    IN LPCWSTR ResourceId,
    IN LPCWSTR DependsOnId
    )
/*++

Routine Description:

    GUM dispatch routine for adding a dependency

Arguments:

    SourceNode - Supplies whether or not this node initiated the GUM update.
        Not used.

    ResourceId - Supplies the resource ID of the resource that should have a
        dependency added.

    DependsOnId - Supplies the resource ID of the resource that should provide
        for ResourceId.

Return Value:

    ERROR_SUCCESS if successful.

    Win32 error code otherwise.

--*/

{
    PFM_RESOURCE Resource;
    PFM_RESOURCE DependsOn;
    PDEPENDENCY dependency;

    //
    //  Chittur Subbaraman (chitturs) - 4/18/99
    // 
    //  If FM groups are not fully initialized or FM is shutting down, then
    //  don't do anything.
    //
    if ( !FmpFMGroupsInited ||
         FmpShutdown ) {
        return(ERROR_SUCCESS);
    }

    dependency = LocalAlloc(LMEM_FIXED, sizeof(DEPENDENCY));
    if (dependency == NULL) {
        CsInconsistencyHalt( ERROR_NOT_ENOUGH_MEMORY );
        return(ERROR_NOT_ENOUGH_MEMORY);
    }

    Resource = OmReferenceObjectById(ObjectTypeResource,
                                     ResourceId);
    if (Resource == NULL) {
        CL_LOGFAILURE( ERROR_RESOURCE_NOT_FOUND );
        LocalFree(dependency);
        return(ERROR_RESOURCE_NOT_FOUND);
    }

    DependsOn = OmReferenceObjectById(ObjectTypeResource,
                                      DependsOnId);
    if (DependsOn == NULL) {
        OmDereferenceObject(Resource);
        LocalFree(dependency);
        CL_LOGFAILURE( ERROR_DEPENDENCY_NOT_FOUND );
        return(ERROR_DEPENDENCY_NOT_FOUND);
    }

    dependency->DependentResource = Resource;
    dependency->ProviderResource = DependsOn;
    FmpAcquireResourceLock();
    InsertTailList( &DependsOn->ProvidesFor,
                    &dependency->ProviderLinkage );
    InsertTailList( &Resource->DependsOn,
                    &dependency->DependentLinkage );
    FmpReleaseResourceLock();

    ClusterEvent( CLUSTER_EVENT_RESOURCE_PROPERTY_CHANGE,
                  Resource );

    //SS: we leave the reference counts on both the objects
    //as a dependency referrring to them has been created.
    return(ERROR_SUCCESS);

} // FmpUpdateAddDependency



DWORD
FmpUpdateRemoveDependency(
    IN BOOL SourceNode,
    IN LPCWSTR ResourceId,
    IN LPCWSTR DependsOnId
    )
/*++

Routine Description:

    GUM dispatch routine for adding a dependency

Arguments:

    SourceNode - Supplies whether or not this node initiated the GUM update.
        Not used.

    ResourceId - Supplies the resource ID of the resource that should have a
        dependency removed.

    DependsOnId - Supplies the resource ID of the resource that provides
        for ResourceId.

Return Value:

    ERROR_SUCCESS if successful.

    Win32 error code otherwise.

--*/

{
    PFM_RESOURCE Resource;
    PFM_RESOURCE DependsOn;
    PDEPENDENCY dependency;
    PLIST_ENTRY ListEntry;
    DWORD       Status=ERROR_SUCCESS;

    //
    //  Chittur Subbaraman (chitturs) - 4/18/99
    // 
    //  If FM groups are not fully initialized or FM is shutting down, then
    //  don't do anything.
    //
    if ( !FmpFMGroupsInited ||
         FmpShutdown ) {
        return(ERROR_SUCCESS);
    }

    Resource = OmReferenceObjectById(ObjectTypeResource,
                                     ResourceId);
    if (Resource == NULL) {
        CL_LOGFAILURE( ERROR_RESOURCE_NOT_FOUND );
        return(ERROR_RESOURCE_NOT_FOUND);
    }

    DependsOn = OmReferenceObjectById(ObjectTypeResource,
                                      DependsOnId);
    if (DependsOn == NULL) {
        OmDereferenceObject(Resource);
        CL_LOGFAILURE( ERROR_RESOURCE_NOT_FOUND );
        return(ERROR_RESOURCE_NOT_FOUND);
    }

    //
    // Walk through the dependency list of the resource searching
    // for a match.
    //
    FmpAcquireResourceLock();
    ListEntry = Resource->DependsOn.Flink;
    while (ListEntry != &Resource->DependsOn) {
        dependency = CONTAINING_RECORD(ListEntry,
                                       DEPENDENCY,
                                       DependentLinkage);
        CL_ASSERT(dependency->DependentResource == Resource);
        if (dependency->ProviderResource == DependsOn) {
            //
            // Found a match. Remove it from its list and
            // free it up.
            //
            RemoveEntryList(&dependency->ProviderLinkage);
            RemoveEntryList(&dependency->DependentLinkage);
            // dereference the providor and dependent resource
            OmDereferenceObject(dependency->DependentResource);
            OmDereferenceObject(dependency->ProviderResource);
            LocalFree(dependency);
            break;
        }
        ListEntry = ListEntry->Flink;
    }
    FmpReleaseResourceLock();

    if (ListEntry != &Resource->DependsOn) {
        //
        // A match was found. Dereference the provider resource
        // to account for the dependency removal and return success.
        //
        ClusterEvent( CLUSTER_EVENT_RESOURCE_PROPERTY_CHANGE,
                      Resource );
        Status = ERROR_SUCCESS;
    } else {
        Status = ERROR_DEPENDENCY_NOT_FOUND;
    }

    //SS: dereference the objects earlier referenced
    OmDereferenceObject(Resource);
    OmDereferenceObject(DependsOn);
    return(Status);

} // FmpUpdateRemoveDependency

DWORD
FmpUpdateDeleteGroup(
    IN BOOL SourceNode,
    IN LPCWSTR GroupId
    )
/*++

Routine Description:

    GUM dispatch routine for deleting a group.

Arguments:

    SourceNode - Supplies whether or not this node initiated the GUM update.
        Not used.

    GroupId - Supplies the group ID.

Return Value:

    ERROR_SUCCESS if successful.

    Win32 error code otherwise.

--*/

{
    DWORD           dwStatus = ERROR_SUCCESS;
    PFM_GROUP       pGroup = NULL;
    PLIST_ENTRY     listEntry;
    PPREFERRED_ENTRY preferredEntry;
    BOOL            bLocked = FALSE;

    //
    //  Chittur Subbaraman (chitturs) - 4/18/99
    // 
    //  If FM groups are not fully initialized or FM is shutting down, then
    //  don't do anything.
    //
    if ( !FmpFMGroupsInited ||
         FmpShutdown ) {
        return(ERROR_SUCCESS);
    }

    //
    // Find the specified Group.
    //
    pGroup = OmReferenceObjectById( ObjectTypeGroup,
                                    GroupId );
    if ( pGroup == NULL ) {
        dwStatus = ERROR_GROUP_NOT_FOUND;
        return(dwStatus);
    }

    ClRtlLogPrint(LOG_NOISE,
               "[FM] DeleteGroup %1!ws!, address = %2!lx!.\n",
               OmObjectId(pGroup),
               pGroup );
    //
    // Chittur Subbaraman (chitturs) - 1/12/99
    //
    // Try to acquire lock, and make sure the Contains list is empty.
    //
    // Most of the calls to manipulate groups make calls to the owner
    // node of the group and this operation is serialized by GUM. So,
    // there is no major danger if we do the operations in this function
    // without holding the group lock. However, we can't rule out
    // corruption 100% as of now.
    //
    // If you block within the GUM handler here, then no events in
    // the cluster proceed forward and things come to a grinding halt.
    //
    // A case in point:
    // (1) Thread 1 (the thread that calls this function) grabs the 
    // GUM lock and waits for the group lock.
    // (2) Thread 2 (FmWorkerThread) grabs the group lock and calls
    // resmon attempting to close a resource. It gets blocked on
    // the resmon eventlist lock.
    // (3) Thread 3 calls RmResourceControl to set the resource name
    // which grabs the resmon eventlist lock and then in turn calls 
    // ClusterRegSetValue and then gets blocked on the GUM lock.
    //
    FmpTryAcquireLocalGroupLock( pGroup, bLocked );

    if ( !IsListEmpty( &pGroup->Contains ) ) 
    {
        dwStatus = ERROR_DIR_NOT_EMPTY;
        goto FnExit;
    }

    //
    // Close the Group's registry key.
    //
    DmRundownList( &pGroup->DmRundownList );
    if ( pGroup->RegistryKey != NULL ) {
        DmCloseKey( pGroup->RegistryKey );
        pGroup->RegistryKey = NULL;
    }

    //
    // Remove from the node list
    //
    dwStatus = OmRemoveObject( pGroup );
    
    ClusterEvent( CLUSTER_EVENT_GROUP_DELETED, pGroup );
    //
    // This dereference would normally cause the group to eventually disappear,
    // however the event notification above will keep a ref on the object
    // until all notifications have been delivered.
    //
    OmDereferenceObject( pGroup );

    //
    // Make sure the preferred owners list is drained.
    //
    while ( !IsListEmpty( &pGroup->PreferredOwners ) ) {
        listEntry = RemoveHeadList(&pGroup->PreferredOwners);
        preferredEntry = CONTAINING_RECORD( listEntry,
                                            PREFERRED_ENTRY,
                                            PreferredLinkage );
        OmDereferenceObject( preferredEntry->PreferredNode );
        LocalFree( preferredEntry );
    }

    //
    //  Free the string associated with the AntiAffinityClassName field.
    //
    LocalFree ( pGroup->lpszAntiAffinityClassName );

    pGroup->dwStructState |= FM_GROUP_STRUCT_MARKED_FOR_DELETE;
    
FnExit:
    if( bLocked ) 
    {
        FmpReleaseLocalGroupLock( pGroup );
    }

    //
    // Dereference for reference above.
    //
    if (pGroup) OmDereferenceObject( pGroup );

    return(dwStatus);

} // FmpUpdateDeleteGroup

/****
@func       DWORD | FmpUpdateGroupIntendedOwner| This update is called on
            a move just before the source node requests the target node
            to take over the group.  

@parm       IN BOOL | bSourceNode | set to TRUE, if the update originated at 
this
            node.
            
@parm       IN PFM_GROUP | pszGroupId | The ID of the group that is about 
            to move.

@parm       IN PDWORD | pdwNodeId| A pointer to a DWORD that contains the
            ID of the node that is the destination of this move.  It is 
            set to ClusterInvalidNodeId by the destination node when it has 
            accepted the group.

@comm       The purpose of this update is to let all nodes know that a move
            is impending.  If the source node dies while a move is in progress
            then preference is given to the target of the move rather than the
            node that is chosen by the FmpUpdateAssignOwnerToGroups

@rdesc      Returns a result code. ERROR_SUCCESS on success.
****/
DWORD
FmpUpdateGroupIntendedOwner(
    IN BOOL     SourceNode,
    IN LPCWSTR  pszGroupId,
    IN PDWORD   pdwNodeId
    )
{
    PFM_GROUP   pGroup = NULL;
    DWORD       dwStatus = ERROR_SUCCESS;
    PNM_NODE    pNode = NULL;
    PNM_NODE    pPrevNode;
    WCHAR       pszNodeId[ NODE_ID_SZ ];
    
    if ( !FmpFMGroupsInited ) 
    {
        return(ERROR_SUCCESS);
    }

    pGroup = OmReferenceObjectById( ObjectTypeGroup,
                                   pszGroupId );

    if (pGroup == NULL)
    {
        dwStatus =  ERROR_GROUP_NOT_FOUND;
        goto FnExit;
    }

    if (*pdwNodeId != ClusterInvalidNodeId)
    {
        pszNodeId [ NODE_ID_SZ - 1 ] = UNICODE_NULL;
        _snwprintf(pszNodeId, NODE_ID_SZ-1, L"%u", *pdwNodeId);

        pNode = OmReferenceObjectById(ObjectTypeNode,
                                        pszNodeId);

        if (pNode == NULL)
        {
            dwStatus = ERROR_CLUSTER_NODE_NOT_FOUND;
            goto FnExit;
        }
    } else if (pGroup->pIntendedOwner == NULL)
    {
        dwStatus = ERROR_CLUSTER_INVALID_NODE;
        ClRtlLogPrint(LOG_NOISE,
              "[FM] FmpUpdateGroupIntendedOwner: Group <%1!ws!> intended owner is already invalid, not setting....\n",
              pszGroupId);
        goto FnExit;
    }
    
    //
    // HACKHACK: Chittur Subbaraman (chitturs) - 5/20/99
    // Comment out as a temporary solution to avoid deadlocks.
    //
    // FmpAcquireLocalGroupLock(pGroup);
    
    pPrevNode = pGroup->pIntendedOwner;

    //set the new owner node, incr ref count
    if (pNode) OmReferenceObject(pNode);
    pGroup->pIntendedOwner = pNode;

    //decr ref count on previous owner
    if (pPrevNode) OmDereferenceObject(pPrevNode);
    //
    // HACKHACK: Chittur Subbaraman (chitturs) - 5/20/99
    // Comment out as a temporary solution to avoid deadlocks.
    //
    // FmpReleaseLocalGroupLock(pGroup);
    
FnExit:
    if (pGroup) OmDereferenceObject(pGroup);
    if (pNode) OmDereferenceObject(pNode);
    return(dwStatus);
}


/****
@func       DWORD | FmpUpdateAssignOwnerToGroups| This update is made when
            a node goes down to take ownership of all the orphaned groups.

@parm       IN BOOL | bSourceNode | set to TRUE, if the update originated at 
this
            node.
            
@parm       IN LPCWSTR | pszGroupId | The ID of the group that is about 
            to move.

@parm       IN PDWORD | pdwNodeId| A pointer to a DWORD that contains the
            ID of the node that is the destination of this move.  It is 
            set to ClusterInvalidNodeId by the destination node when it has 
            accepted the group.

@comm       The purpose of this update is to let all nodes know that a move
            is impending.  If the source node dies while a move is in progress
, 
            then preference is given to the target of the move rather than the
            node that is chosen by the FmpClaimNodeGroups algorithm.

@rdesc      returns ERROR_SUCCESS.
****/
DWORD
FmpUpdateAssignOwnerToGroups(
    IN BOOL     SourceNode,
    IN LPCWSTR  pszNodeId
    )
{
    PNM_NODE    pNode = NULL;
    DWORD       dwStatus = ERROR_SUCCESS;
    DWORD       dwNodeId;

    //
    //  Chittur Subbaraman (chitturs) - 4/18/99
    // 
    //  If FM groups are not fully initialized or FM is shutting down, then
    //  don't do anything.
    //
    if ( !FmpFMGroupsInited || FmpShutdown ) 
    {
        return(ERROR_SUCCESS);
    }

    //
    //  In a node evict, the NM GUM handler gets rids of the dead node from the OM list.
    //  This node down FM GUM handler could follow the NM evict GUM handler since NM
    //  lets an evict through once *it* declares a node as down. At that time, there is no
    //  guarantee that the FM node down GUM has executed since that GUM is issued in the
    //  async phase of node down processing and could very well follow the NM evict GUM
    //  handler.  Thus, this GUM handler and associated functions CANNOT call OM to get
    //  a node object from the node ID string.  Thus, this GUM handler and associated
    //  functions are carefully written to work with a node ID as opposed to a node object.
    //
    dwNodeId = wcstoul( pszNodeId, NULL, 10 );

    //if this update has already been seen after the node down
    //ignore this one
    if (gFmpNodeArray[dwNodeId].dwNodeDownProcessingInProgress == 0)
    {
        ClRtlLogPrint(LOG_NOISE,
            "[FM] FmpUpdateAssignOwnersToGroups, %1!ws! node down has been processed already\n",
                   pszNodeId);
        goto FnExit;                   
    }
    //
    // Assign ownership to all groups owned by the dead node
    //
    dwStatus = FmpAssignOwnersToGroups(pszNodeId, NULL, NULL);

    if (dwStatus != ERROR_SUCCESS) 
    {
        ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateAssignOwnersToGroups failed %1!d!\n",
                   dwStatus);
    }                   
    
    //mark that the node down processing has been done
    gFmpNodeArray[dwNodeId].dwNodeDownProcessingInProgress = 0;
    
FnExit:        
    return(dwStatus);
}

/****
@func       DWORD | FmpUpdateApproveJoin| The joining node
            makes this update call.

@parm       IN BOOL | bSourceNode | set to TRUE, if the update originated at 
this
            node.
            
@parm       IN LPCWSTR | pszGroupId | The ID of the group that is about 
            to move.

@parm       IN PDWORD | pdwNodeId| A pointer to a DWORD that contains the
            ID of the node that is the destination of this move.  It is 
            set to ClusterInvalidNodeId by the destination node when it has 
            accepted the group.

@comm       The purpose of this update is to let all nodes know that a move
            is impending.  If the source node dies while a move is in progress
, 
            then preference is given to the target of the move rather than the
            node that is chosen by the FmpClaimNodeGroups algorithm.

@rdesc      returns ERROR_SUCCESS.
****/
DWORD
FmpUpdateApproveJoin(
    IN BOOL     SourceNode,
    IN LPCWSTR  pszNodeId
    )
{

    PNM_NODE    pNode = NULL;
    DWORD       dwStatus = ERROR_SUCCESS;

    //
    //  Chittur Subbaraman (chitturs) - 4/18/99
    // 
    //  If FM groups are not fully initialized or FM is shutting down, then
    //  don't do anything.
    //
    if ( !FmpFMGroupsInited || FmpShutdown ) 
    {
        return(ERROR_SUCCESS);
    }

    pNode = OmReferenceObjectById( ObjectTypeNode,
                                   pszNodeId );

    if (!pNode)
    {
        ClRtlLogPrint(LOG_CRITICAL,
            "[FM] FmpUpdateAssignOwnersToGroups, %1!ws! node not found\n",
                   pszNodeId);
        //should we return failure here
        //is evict of a node synchronized with everything
        goto FnExit;                   
    }

    if (pNode == NmLocalNode)
    {
        // SS: can I become the locker now
        // If so, there what do I do
        //i approve of my own join
        goto FnExit;
    }
    //if a node is trying to join before the processing
    //for its last death has been completed, ask it to retry
    if (gFmpNodeArray[NmGetNodeId(pNode)].dwNodeDownProcessingInProgress == 1)
    {
        ClRtlLogPrint(LOG_CRITICAL,
            "[FM] FmpUpdateApproveJoin, %1!ws! node down hasnt been processed as yet\n",
                   pszNodeId);
        dwStatus = ERROR_RETRY;                   
        goto FnExit;                   
    }
FnExit:        
    if (pNode) OmDereferenceObject(pNode);
    return(dwStatus);
}

/****
@func       DWORD | FmpUpdateCreateGroup | GUM update handler for creating
            a group.
           
@parm       IN OUT PGUM_CREATE_GROUP | pGumGroup | Buffer containing group info

@parm       IN BOOL | bSourceNode | Indicates whether this call originated
            from this node.

@comm       This GUM update creates a group and is structured as a local 
            transaction so that both registry entries and in-memory
            structures are updated consistently.

@rdesc      Returns ERROR_SUCCESS on success. A Win32 error code otherwise.
****/
DWORD
FmpUpdateCreateGroup(
    IN OUT PGUM_CREATE_GROUP pGumGroup,
    IN BOOL    bSourceNode
    )
{
    DWORD       dwStatus = ERROR_SUCCESS;
    HDMKEY      hKey = NULL;
    DWORD       dwDisposition;
    HLOCALXSACTION      
                hXsaction = NULL;
    LPCWSTR     lpszNodeId = NULL;
    PNM_NODE    pNode = NULL;
    DWORD       dwGroupIdLen = 0;
    DWORD       dwGroupNameLen = 0;
    LPWSTR      lpszGroupId = NULL;
    LPCWSTR     lpszGroupName = NULL;
    BOOL        bLocked = FALSE;

    //
    //  Chittur Subbaraman (chitturs) - 5/27/99
    //
    //  Restructure this GUM update as a local transaction.
    //
    dwGroupIdLen = pGumGroup->GroupIdLen;  
    dwGroupNameLen = pGumGroup->GroupNameLen;  
    lpszGroupId = pGumGroup->GroupId; 
    lpszGroupName = (PWSTR)((PCHAR)lpszGroupId +
                                   dwGroupIdLen );
    ClRtlLogPrint(LOG_NOISE,
               "[FM] FmpUpdateCreateGroup: Entry for group %1!ws!...\n",
                lpszGroupId);
    //
    // Start a transaction
    //
    hXsaction = DmBeginLocalUpdate();

    if ( !hXsaction )
    {
        dwStatus = GetLastError();
        ClRtlLogPrint(LOG_UNUSUAL,
                  "[FM] FmpUpdateCreateGroup, Failed in starting a transaction for group %1!ws!, Status =%2!d!....\n",
                   lpszGroupId,
                   dwStatus);
        return( dwStatus );
    }

    //
    // Create the new group key.
    //
    hKey = DmLocalCreateKey( hXsaction,
                             DmGroupsKey,
                             lpszGroupId,
                             0,
                             KEY_READ | KEY_WRITE,
                             NULL,
                             &dwDisposition );
                            
    if ( hKey == NULL ) 
    {
        dwStatus = GetLastError();
        ClRtlLogPrint(LOG_UNUSUAL,
                  "[FM] FmpUpdateCreateGroup, Failed in creating the group key for group %1!ws!, Status =%2!d!....\n",
                   lpszGroupId,
                   dwStatus);
        goto FnExit;
    }
    
    if ( dwDisposition != REG_CREATED_NEW_KEY ) 
    {
        ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateCreateGroup used GUID %1!ws! that already existed! This is impossible.\n",
                   lpszGroupId);
        dwStatus = ERROR_ALREADY_EXISTS;
        goto FnExit;
    }

    CL_ASSERT( dwDisposition == REG_CREATED_NEW_KEY );

    //
    // Set the group name in the registry
    //
    dwStatus = DmLocalSetValue( hXsaction,
                                hKey,
                                CLUSREG_NAME_GRP_NAME,
                                REG_SZ,
                                ( CONST BYTE * ) lpszGroupName,
                                ( lstrlenW( lpszGroupName ) + 1 ) * 
                                    sizeof( WCHAR ) );

    if( dwStatus != ERROR_SUCCESS )
    {
        ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateCreateGroup: DmLocalSetValue for group %1!ws! fails, Status = %2!d!...\n",
                   lpszGroupId,
                   dwStatus);
        goto FnExit;     
    }
    
    //
    // We really shouldn't be acquiring locks here... but
    // we'll try anyway. If we fail, we must return an error
    // because we have nothing to return.
    //
    FmpTryAcquireGroupLock( bLocked, 500 );
    if ( !bLocked ) 
    {
        pGumGroup->Group = NULL;
        dwStatus = ERROR_SHARING_VIOLATION;
        goto FnExit;
    }

    pGumGroup->Group = FmpCreateGroup( lpszGroupId, TRUE );
    
    if ( pGumGroup->Group == NULL ) 
    {
        dwStatus = GetLastError();
        ClRtlLogPrint(LOG_UNUSUAL,
                  "[FM] FmpUpdateCreateGroup, FmpCreateFroup failed for group %1!ws!, Status =%2!d!....\n",
                   lpszGroupId,
                   dwStatus);
        goto FnExit;
    } else 
    {
        if ( bSourceNode ) 
        {
            OmReferenceObject( pGumGroup->Group );
            OmReferenceObject( NmLocalNode );
            pNode = NmLocalNode;
        } else {
            lpszNodeId = (PWSTR)((PCHAR)lpszGroupId +
                                   dwGroupIdLen +
                                   dwGroupNameLen );
            pNode = OmReferenceObjectById( ObjectTypeNode, lpszNodeId );
            if ( pNode == NULL ) 
            {
                CL_LOGFAILURE( ERROR_CLUSTER_NODE_NOT_FOUND );
                dwStatus = ERROR_CLUSTER_NODE_NOT_FOUND;
                ClRtlLogPrint(LOG_UNUSUAL,
                            "[FM] FmpUpdateCreateGroup, Could not find node for group %1!ws!, Status =%2!d!....\n",
                            lpszGroupId,
                            dwStatus);
                CsInconsistencyHalt( ERROR_CLUSTER_NODE_NOT_FOUND );
            }
        }

        CL_ASSERT( pGumGroup->Group->OwnerNode == NULL );

        if ( !FmpInPreferredList( pGumGroup->Group, pNode , FALSE, NULL) ) 
        {
            ClRtlLogPrint(LOG_UNUSUAL,
                        "[FM] FmpUpdateCreateGroup, node %1!ws! is not in preferred list for group %2!ws!.\n",
                         OmObjectId( pNode ),
                         OmObjectId( pGumGroup->Group ));
        }

        pGumGroup->Group->OwnerNode = pNode;
              
        if ( OmSetObjectName( pGumGroup->Group, lpszGroupName ) != ERROR_SUCCESS )
        {
            ClRtlLogPrint(LOG_UNUSUAL,
                        "[FM] FmpUpdateCreateGroup, Cannot set name for group %1!ws!...\n",
                        OmObjectId( pGumGroup->Group ));
        }
        
        ClusterEvent( CLUSTER_EVENT_GROUP_ADDED, pGumGroup->Group );
    }
                           
FnExit:
    if ( bLocked ) 
    {
        FmpReleaseGroupLock( );
    }
    
    if ( hKey != NULL ) 
    {
        DmCloseKey( hKey );
    }

    if ( ( dwStatus == ERROR_SUCCESS ) && 
         ( hXsaction != NULL ) )
    {
        DmCommitLocalUpdate( hXsaction );
    }
    else
    {
        if ( hXsaction ) DmAbortLocalUpdate( hXsaction );
    }

    ClRtlLogPrint(LOG_NOISE,
               "[FM] FmpUpdateCreateGroup: Exit for group %1!ws!, Status=%2!u!...\n",
                lpszGroupId,
                dwStatus);

    return( dwStatus );
}

/****
@func       DWORD | FmpUpdateCompleteGroupMove | This update is made when
            FmpTakeGroupRequest fails with an RPC error.

@parm       IN BOOL | bSourceNode | Set to TRUE, if the update originated at 
            this node. Not used.
            
@parm       IN LPCWSTR | pszNodeId | The ID of the dead node.

@parm       IN LPCWSTR | pszGroupId | The ID of the group which was in the
            middle of the move.

@comm       The purpose of this update is to let the ownership of the
            group which was in the middle of the move determined consistently.

@rdesc      Returns ERROR_SUCCESS.
****/
DWORD
FmpUpdateCompleteGroupMove(
    IN BOOL     bSourceNode,
    IN LPCWSTR  pszNodeId,
    IN LPCWSTR  pszGroupId
    )
{
    PFM_GROUP   pGroup = NULL;
    DWORD       dwStatus = ERROR_SUCCESS;

    //
    //  Chittur Subbaraman (chitturs) - 4/2/2000
    // 
    //  If FM groups are not fully initialized, then  don't do anything.
    //  Don't check for shutdown since we need to handle take group
    //  exceptions for the quorum group even during a shutdown.
    //
    if ( !FmpFMGroupsInited ) 
    {
        return( ERROR_SUCCESS );
    }

    pGroup = OmReferenceObjectById( ObjectTypeGroup,
                                    pszGroupId );

    if ( !pGroup )
    {
        ClRtlLogPrint(LOG_CRITICAL,
            "[FM] FmpUpdateCompleteGroupMove, %1!ws! group not found\n",
                   pszGroupId);
        goto FnExit;                   
    }

    //
    // Assign ownership to this group which was in the middle of a move
    //
    dwStatus = FmpAssignOwnersToGroups( pszNodeId, pGroup, NULL );

    if ( dwStatus != ERROR_SUCCESS ) 
    {
        ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateCompleteGroupMove failed with error %1!d!\n",
                   dwStatus);
    }                   
    
FnExit:           
    if ( pGroup ) OmDereferenceObject( pGroup );

    return( dwStatus );
}

DWORD
FmpUpdateCheckAndSetGroupOwner(
    IN BOOL bSourceNode,
    IN LPCWSTR lpszGroupId,
    IN LPCWSTR lpszNodeId
    )
/*++

Routine Description:

    GUM update handler called from FmpTakeGroupRequest for NT5 cluster
    to set the group owner ONLY IF its intended owner is the future
    owner node.

Arguments:

    bSourceNode - Supplies whether or not this node was the source of the update

    lpszGroupId - Supplies the id of the resource whose state is changing

    lpszNodeId - Supplies the node id of the group owner.

Return Value:

    ERROR_SUCCESS if successful

    Win32 error code otherwise

--*/

{
    PFM_GROUP pGroup = NULL;
    DWORD     dwStatus = ERROR_SUCCESS;
    PNM_NODE  pNode = NULL;
    PNM_NODE  pPrevNode = NULL;

    //dont check for shutdown - we cant afford to lose ownership notifications
    //while we are shutting down
    //since we dont destroy any fm structures - there shouldnt be a problem in
    //handling these
    if ( !FmpFMGroupsInited ) 
    {
        return( ERROR_SUCCESS );
    }

    ClRtlLogPrint(LOG_NOISE,
              "[FM] FmpUpdateCheckAndSetGroupOwner: Entry for Group = <%1!ws!>....\n",
              lpszGroupId);
    //
    //  Chittur Subbaraman (chitturs) - 7/27/99
    //
    //  This GUM handler sets the group ownership only if the future owner
    //  node is the group's intended owner. If the intended owner is NULL, 
    //  it means the node down processing GUM handler has taken charge 
    //  of this group. If the intended owner is not NULL and not the 
    //  future owner node, then it means that the node down processing 
    //  GUM handler has assigned ownership to the group and the group 
    //  started moving to a different target before the FmpTakeGroupRequest 
    //  that issued this GUM due as a part of the first move operation 
    //  got a chance to execute. In both cases, lay your hands off the 
    //  group.
    //
    pGroup = OmReferenceObjectById( ObjectTypeGroup,
                                    lpszGroupId );

    if ( pGroup == NULL )
    {
        ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateCheckAndSetGroupOwner: GroupID = %1!ws! could not be found...\n",
                   lpszGroupId);
        dwStatus = ERROR_GROUP_NOT_FOUND;
        goto FnExit;
    }

    pNode = OmReferenceObjectById( ObjectTypeNode,
                                   lpszNodeId );

    if ( pNode == NULL )
    {
        ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateCheckAndSetGroupOwner: NodeID = %1!ws! could not be found, Group = %2!ws!...\n",
                   lpszNodeId,
                   lpszGroupId);
        dwStatus = ERROR_CLUSTER_NODE_NOT_FOUND;
        goto FnExit;
    }

    if ( pGroup->pIntendedOwner != pNode )
    {
        ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateCheckAndSetGroupOwner: Group = <%1!ws!> intended owner is invalid, not setting group ownership...\n",
                   lpszGroupId);
        dwStatus = ERROR_GROUP_NOT_AVAILABLE;
        goto FnExit;
    }
    
    pPrevNode = pGroup->OwnerNode;

    //
    // Set the new owner node, incr ref count
    //
    OmReferenceObject( pNode );
    
    pGroup->OwnerNode = pNode;

    //
    // Decrement the ref count on previous owner
    //
    OmDereferenceObject( pPrevNode );

    //
    // Generate an event to signify group owner node change
    //
    ClusterEvent( CLUSTER_EVENT_GROUP_CHANGE, pGroup );
    
FnExit:
    if ( pGroup ) OmDereferenceObject( pGroup );
    
    if ( pNode ) OmDereferenceObject( pNode );

    ClRtlLogPrint(LOG_NOISE,
              "[FM] FmpUpdateCheckAndSetGroupOwner: Exit for Group = <%1!ws!>, Status=%2!u!....\n",
              lpszGroupId,
              dwStatus);
    
    return( dwStatus );
}

DWORD
FmpUpdateCreateResourceType(
    IN PVOID Buffer    
    )
/*++

Routine Description:

    GUM update handler called for creating a resource type. For
    NT5.1 clusters, this GUM handler does both the registry and
    in-memory updates as a local transaction.

Arguments:

    Buffer - Buffer containing resource type information.
    
Return Value:

    ERROR_SUCCESS if successful

    Win32 error code otherwise

--*/

{
    PFM_RESTYPE         pResType = NULL;
    LPWSTR              lpszTypeName;
    LPWSTR              lpszDisplayName;
    LPWSTR              lpszDllName;
    DWORD               dwStatus = ERROR_SUCCESS;
    DWORD               dwLooksAlive;
    DWORD               dwIsAlive;
    DWORD               dwDllNameLen;
    DWORD               dwDisplayNameLen;
    DWORD               dwTypeNameLen;
    DWORD               dwClusterHighestVersion;
    DWORD               dwDisposition;
    HLOCALXSACTION      hXsaction = NULL;
    HDMKEY              hTypeKey = NULL;

    //
    //  Chittur Subbaraman (chitturs) - 2/8/2000
    //
    //  Rewrite this GUM handler as a local transaction (for NT5.1 only)
    //
    lpszTypeName = ( LPWSTR ) Buffer;

    ClRtlLogPrint(LOG_NOISE,
              "[FM] FmpUpdateCreateResourceType, Entry for resource type %1!ws!...\n",
               lpszTypeName);       

    pResType = OmReferenceObjectById( ObjectTypeResType,
                                      lpszTypeName );
    if ( pResType )
    {
        dwStatus = ERROR_ALREADY_EXISTS;
        ClRtlLogPrint(LOG_CRITICAL,
                  "[FM] FmpUpdateCreateResourceType, Resource type %1!ws! already exists, Status = %2!d!...\n",
                  lpszTypeName,
                  dwStatus);       
        OmDereferenceObject( pResType );
        return( dwStatus );
    }

    dwTypeNameLen = ( lstrlenW( lpszTypeName ) + 1 ) * sizeof( WCHAR );

    lpszDisplayName = ( LPWSTR ) ( ( PCHAR ) Buffer + dwTypeNameLen );

    dwDisplayNameLen = ( lstrlenW( lpszDisplayName ) + 1 ) * sizeof( WCHAR );

    lpszDllName = ( LPWSTR ) ( ( PCHAR ) Buffer +
                               dwTypeNameLen +
                               dwDisplayNameLen );

    dwDllNameLen = ( lstrlenW( lpszDllName ) + 1 ) * sizeof( WCHAR );

    NmGetClusterOperationalVersion( &dwClusterHighestVersion, 
                                    NULL, 
                                    NULL );

    if ( CLUSTER_GET_MAJOR_VERSION( dwClusterHighestVersion ) < 
                NT51_MAJOR_VERSION ) 
    {   
        goto skip_registry_updates;
    }

    dwLooksAlive = *( DWORD UNALIGNED * ) ( ( ( PCHAR ) Buffer +
                               dwTypeNameLen +
                               dwDisplayNameLen + 
                               dwDllNameLen ) );
                              
    dwIsAlive = *( DWORD UNALIGNED * ) ( ( ( PCHAR ) Buffer +
                            dwTypeNameLen +
                            dwDisplayNameLen + 
                            dwDllNameLen + 
                            sizeof( DWORD ) ) );

    //
    // Start a transaction
    //
    hXsaction = DmBeginLocalUpdate();

    if ( !hXsaction )
    {
        dwStatus = GetLastError();
        ClRtlLogPrint(LOG_CRITICAL,
                  "[FM] FmpUpdateCreateResourceType, Failed in starting a transaction for resource type %1!ws!, Status =%2!d!....\n",
                   lpszTypeName,
                   dwStatus);
        return( dwStatus );
    }

    hTypeKey = DmLocalCreateKey( hXsaction,
                                 DmResourceTypesKey,
                                 lpszTypeName,
                                 0,
                                 KEY_READ | KEY_WRITE,
                                 NULL,
                                 &dwDisposition );
    if ( hTypeKey == NULL ) 
    {
        dwStatus = GetLastError();
        ClRtlLogPrint(LOG_CRITICAL,
                  "[FM] FmpUpdateCreateResourceType, Failed in creating the resource types key for resource type %1!ws!, Status =%2!d!....\n",
                   lpszTypeName,
                   dwStatus);       
        goto FnExit;
    }

    if ( dwDisposition != REG_CREATED_NEW_KEY ) 
    {
        dwStatus = ERROR_ALREADY_EXISTS;
        ClRtlLogPrint(LOG_CRITICAL,
                  "[FM] FmpUpdateCreateResourceType, Duplicate resource types key exists for resource type %1!ws!, Status =%2!d!....\n",
                   lpszTypeName,
                   dwStatus);              
        goto FnExit;
    }

    dwStatus = DmLocalSetValue( hXsaction,
                                hTypeKey,
                                CLUSREG_NAME_RESTYPE_DLL_NAME,
                                REG_SZ,
                                ( CONST BYTE * )lpszDllName,
                                dwDllNameLen );

    if ( dwStatus != ERROR_SUCCESS ) 
    {
        ClRtlLogPrint(LOG_CRITICAL,
                  "[FM] FmpUpdateCreateResourceType, Failed in setting the DLL name for resource type %1!ws!, Status =%2!d!....\n",
                   lpszTypeName,
                   dwStatus);              
        goto FnExit;
    }

    dwStatus = DmLocalSetValue( hXsaction,
                                hTypeKey,
                                CLUSREG_NAME_RESTYPE_IS_ALIVE,
                                REG_DWORD,
                                ( CONST BYTE * )&dwIsAlive,
                                sizeof( DWORD ) );


    if ( dwStatus != ERROR_SUCCESS ) 
    {
        ClRtlLogPrint(LOG_CRITICAL,
                  "[FM] FmpUpdateCreateResourceType, Failed in setting the Is Alive interval for resource type %1!ws!, Status =%2!d!....\n",
                   lpszTypeName,
                   dwStatus);              
        goto FnExit;
    }

    dwStatus = DmLocalSetValue( hXsaction,
                                hTypeKey,
                                CLUSREG_NAME_RESTYPE_LOOKS_ALIVE,
                                REG_DWORD,
                                ( CONST BYTE * )&dwLooksAlive,
                                sizeof( DWORD ) );

    if ( dwStatus != ERROR_SUCCESS ) 
    {
        ClRtlLogPrint(LOG_CRITICAL,
                  "[FM] FmpUpdateCreateResourceType, Failed in setting the Looks Alive interval for resource type %1!ws!, Status =%2!d!....\n",
                   lpszTypeName,
                   dwStatus);              
        goto FnExit;
    }

    dwStatus = DmLocalSetValue( hXsaction,
                                hTypeKey,
                                CLUSREG_NAME_RESTYPE_NAME,
                                REG_SZ,
                                ( CONST BYTE * )lpszDisplayName,
                                dwDisplayNameLen );

    if ( dwStatus != ERROR_SUCCESS ) 
    {
        ClRtlLogPrint(LOG_CRITICAL,
                  "[FM] FmpUpdateCreateResourceType, Failed in setting the display name for resource type %1!ws!, Status =%2!d!....\n",
                   lpszTypeName,
                   dwStatus);              
        goto FnExit;
    }
    
skip_registry_updates:
    pResType = FmpCreateResType( lpszTypeName );

    if ( pResType != NULL ) 
    {
        dwStatus = FmpRmLoadResTypeDll( pResType );
        if ( dwStatus == ERROR_SUCCESS )
        {
            pResType->State = RESTYPE_STATE_LOADS;
        } else
        {
            ClRtlLogPrint(LOG_NOISE,
                       "[FM] FmpUpdateCreateResourceType: Unable to load dll for resource type %1!ws!, Status=%2!u!...\n",
                       lpszTypeName,
                       dwStatus);
            //
            //  Some nodes may not support this resource type. So, consider
            //  the loading failure as success. However, log the error.
            //
            dwStatus = ERROR_SUCCESS;
        }
    } else
    {
        dwStatus = GetLastError();
        ClRtlLogPrint(LOG_NOISE,
                   "[FM] FmpUpdateCreateResourceType: Unable to create resource type %1!ws!, Status=%2!u!...\n",
                   lpszTypeName,
                   dwStatus);
    }

FnExit:
    if ( hTypeKey != NULL ) 
    {
        DmCloseKey( hTypeKey );
    }

    if ( ( dwStatus == ERROR_SUCCESS ) && 
         ( hXsaction != NULL ) )
    {
        DmCommitLocalUpdate( hXsaction );
    }
    else
    {
        if ( hXsaction ) DmAbortLocalUpdate( hXsaction );
    }

    ClRtlLogPrint(LOG_NOISE,
               "[FM] FmpUpdateCreateResourceType: Exit for resource type %1!ws!, Status=%2!u!...\n",
                lpszTypeName,
                dwStatus);

    return( dwStatus ); 
}

DWORD
FmpUpdateCreateResource(
    IN OUT PGUM_CREATE_RESOURCE pGumResource
    )
{
/*++

Routine Description:

    GUM update handler called for creating a resource. For
    NT5.1 clusters, this GUM handler does both the registry and
    in-memory updates as a local transaction.

Arguments:

    pGumResource - Structure containing resource information.
    
Return Value:

    ERROR_SUCCESS if successful

    Win32 error code otherwise

--*/
    DWORD       dwStatus = ERROR_SUCCESS;
    HDMKEY      hResourceKey = NULL;
    HDMKEY      hGroupKey = NULL;
    DWORD       dwDisposition;
    HLOCALXSACTION      
                hXsaction = NULL;
    DWORD       dwClusterHighestVersion;
    PGUM_CREATE_RESOURCE GumResource;
    LPWSTR      lpszResourceId = NULL;
    LPWSTR      lpszResourceName = NULL;
    LPWSTR      lpszResourceType = NULL;
    PFM_GROUP   pGroup = NULL;
    PFM_RESTYPE pResType = NULL;
    DWORD       dwpollIntervals = CLUSTER_RESOURCE_USE_DEFAULT_POLL_INTERVAL;
    DWORD       dwPersistentState = 0;
    DWORD       dwResourceTypeLen = 0;
    DWORD       dwFlags = 0;
    HDMKEY      hParamKey = NULL;

    //
    //  Chittur Subbaraman (chitturs) - 1/30/2000
    //
    //  Restructure this GUM update as a local transaction.
    //

    lpszResourceId = (LPWSTR)( (PCHAR) pGumResource->GroupId +
                                       pGumResource->GroupIdLen );

    lpszResourceName = (LPWSTR)( (PCHAR) pGumResource->GroupId +
                                         pGumResource->GroupIdLen +
                                         pGumResource->ResourceIdLen );

    pGroup = OmReferenceObjectById( ObjectTypeGroup,
                                    pGumResource->GroupId );

    if ( pGroup == NULL ) 
    {
        CL_LOGFAILURE( ERROR_GROUP_NOT_FOUND );
        ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateCreateResource: Group for resource %1!ws! not found.\n",
                   lpszResourceId);
        return( ERROR_GROUP_NOT_FOUND );
    }
 
    ClRtlLogPrint(LOG_NOISE,
               "[FM] FmpUpdateCreateResource: Entry for resource %1!ws!...\n",
                lpszResourceId);
    //
    //  If we are dealing with the mixed mode cluster, don't bother to
    //  do these registry updates since the API layer would do it.
    //
    NmGetClusterOperationalVersion( &dwClusterHighestVersion, 
                                    NULL, 
                                    NULL );

    if ( CLUSTER_GET_MAJOR_VERSION( dwClusterHighestVersion ) < 
                NT51_MAJOR_VERSION )
    {
        ClRtlLogPrint(LOG_NOISE,
               "[FM] FmpUpdateCreateResource: Skipping registry updates for resource %1!ws!...\n",
                lpszResourceId);
        goto skip_registry_updates;
    }

    dwResourceTypeLen = *( DWORD UNALIGNED * )( (PCHAR) pGumResource->GroupId +
                                         pGumResource->GroupIdLen +
                                         pGumResource->ResourceIdLen +
                                         (lstrlenW(lpszResourceName)+1) * sizeof(WCHAR) );

    lpszResourceType = (LPWSTR)( (PCHAR) pGumResource->GroupId +
                                         pGumResource->GroupIdLen +
                                         pGumResource->ResourceIdLen +
                                         (lstrlenW(lpszResourceName)+1) * sizeof(WCHAR) + 
                                         sizeof( DWORD ) );
    
    dwFlags = *( DWORD UNALIGNED * )( (PCHAR) pGumResource->GroupId +
                               pGumResource->GroupIdLen +
                               pGumResource->ResourceIdLen +
                               (lstrlenW(lpszResourceName)+1) * sizeof(WCHAR) +
                               sizeof( DWORD ) + 
                               dwResourceTypeLen );

    //
    // Start a transaction
    //
    hXsaction = DmBeginLocalUpdate();

    if ( !hXsaction )
    {
        dwStatus = GetLastError();
        ClRtlLogPrint(LOG_UNUSUAL,
                  "[FM] FmpUpdateCreateResource, Failed in starting a transaction for resource %1!ws!, Status =%2!d!....\n",
                   lpszResourceId,
                   dwStatus);
        OmDereferenceObject( pGroup );
        return( dwStatus );
    }

    //
    // Create the new resources key.
    //
    hResourceKey = DmLocalCreateKey( hXsaction,
                                     DmResourcesKey,
                                     lpszResourceId,
                                     0,
                                     KEY_READ | KEY_WRITE,
                                     NULL,
                                     &dwDisposition );
                            
    if ( hResourceKey == NULL ) 
    {
        dwStatus = GetLastError();
        ClRtlLogPrint(LOG_UNUSUAL,
                  "[FM] FmpUpdateCreateResource, Failed in creating the resource key for resource %1!ws!, Status =%2!d!....\n",
                   lpszResourceId,
                   dwStatus);
        goto FnExit;
    }
    
    if ( dwDisposition != REG_CREATED_NEW_KEY ) 
    {
        ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateCreateResource used GUID %1!ws! that already existed! This is impossible.\n",
                   lpszResourceId);
        dwStatus = ERROR_ALREADY_EXISTS;
        goto FnExit;
    }

    CL_ASSERT( dwDisposition == REG_CREATED_NEW_KEY );

    //
    // Set the resource name in the registry
    //
    dwStatus = DmLocalSetValue( hXsaction,
                                hResourceKey,
                                CLUSREG_NAME_RES_NAME,
                                REG_SZ,
                                (CONST BYTE *)lpszResourceName,
                                (lstrlenW(lpszResourceName)+1)*sizeof(WCHAR) );

    if( dwStatus != ERROR_SUCCESS )
    {
        ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateCreateResource: DmLocalSetValue (resource name) for resource %1!ws! fails, Status = %2!d!...\n",
                   lpszResourceId,
                   dwStatus);
        goto FnExit;     
    }

    //
    // Set the resource's type in the registry
    // Note we reference the resource type and use its ID
    // so that the case is correct.
    //
    pResType = OmReferenceObjectById( ObjectTypeResType, lpszResourceType );
    CL_ASSERT( pResType != NULL );
    dwStatus = DmLocalSetValue( hXsaction,
                                hResourceKey,
                                CLUSREG_NAME_RES_TYPE,
                                REG_SZ,
                                (CONST BYTE *) OmObjectId( pResType ),
                                (lstrlenW( lpszResourceType ) + 1 )*sizeof(WCHAR) );
    OmDereferenceObject( pResType );

    if ( dwStatus != ERROR_SUCCESS ) 
    {
        ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateCreateResource: DmLocalSetValue (resource type) for resource %1!ws! fails, Status = %2!d!...\n",
                   lpszResourceId,
                   dwStatus);
        goto FnExit;
    }

    //
    // Set the resource's poll intervals in the registry.
    //
    dwStatus = DmLocalSetValue( hXsaction,
                                hResourceKey,
                                CLUSREG_NAME_RES_LOOKS_ALIVE,
                                REG_DWORD,
                                (CONST BYTE *)&dwpollIntervals,
                                4 );
                              
    if ( dwStatus != ERROR_SUCCESS ) 
    {
        ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateCreateResource: DmLocalSetValue (looks alive) for resource %1!ws! fails, Status = %2!d!...\n",
                   lpszResourceId,
                   dwStatus);
        goto FnExit;
    }

    dwStatus = DmLocalSetValue( hXsaction,
                                hResourceKey,
                                CLUSREG_NAME_RES_IS_ALIVE,
                                REG_DWORD,
                                (CONST BYTE *)&dwpollIntervals,
                                4);

    if ( dwStatus != ERROR_SUCCESS ) 
    {
        ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateCreateResource: DmLocalSetValue (is alive) for resource %1!ws! fails, Status = %2!d!...\n",
                   lpszResourceId,
                   dwStatus);
        goto FnExit;
    }

    //
    // If this resource should be started in a separate monitor, set that
    // parameter now.
    //
    if ( dwFlags & CLUSTER_RESOURCE_SEPARATE_MONITOR ) 
    {
        DWORD dwSeparateMonitor = 1;

        dwStatus = DmLocalSetValue( hXsaction,
                                    hResourceKey,
                                    CLUSREG_NAME_RES_SEPARATE_MONITOR,
                                    REG_DWORD,
                                    (CONST BYTE *)&dwSeparateMonitor,
                                    sizeof( dwSeparateMonitor ) );
                                  
        if ( dwStatus != ERROR_SUCCESS) 
        {
            ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateCreateResource: DmLocalSetValue (separate monitor) for resource %1!ws! fails, Status = %2!d!...\n",
                   lpszResourceId,
                   dwStatus);
            goto FnExit;
        }
    }

    //
    // Create a Parameters key for the resource.
    //
    hParamKey = DmLocalCreateKey( hXsaction,
                                  hResourceKey,
                                  CLUSREG_KEYNAME_PARAMETERS,                   
                                  0,
                                  KEY_READ,
                                  NULL,
                                  &dwDisposition );
    if ( hParamKey == NULL ) 
    {
        dwStatus = GetLastError();
        ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateCreateResource: DmLocalCreateKey (parameters) for resource %1!ws! fails, Status = %2!d!...\n",
                   lpszResourceId,
                   dwStatus);
        CL_LOGFAILURE( dwStatus );
        goto FnExit;
    } else 
    {
        DmCloseKey( hParamKey );
    }

    hGroupKey = DmOpenKey( DmGroupsKey, 
                           OmObjectId(pGroup), 
                           KEY_READ | KEY_WRITE);

    if ( hGroupKey == NULL ) 
    {
        dwStatus = GetLastError();
        ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateCreateResource: DmOpenKey (group key) for resource %1!ws! fails, Status = %2!d!...\n",
                   lpszResourceId,
                   dwStatus);
        goto FnExit;
    }

    //
    //  Chittur Subbaraman (chitturs) - 5/25/99
    //
    //  Make sure you set the persistent state of the resource to 
    //  ClusterResourceOffline before you create the resource. If
    //  this is not done, if you create a resource in a group which
    //  is online, the group's persistent state value (i.e., 1 in
    //  this case) is inherited by the resource in FmpQueryResourceInfo
    //  (only the memory state is set and not the registry state and
    //  this was a problem as well) and if you move such a group to 
    //  another node, it will bring the newly created resource online.
    //
    dwStatus = DmLocalSetValue(  hXsaction,
                                 hResourceKey,
                                 CLUSREG_NAME_RES_PERSISTENT_STATE,
                                 REG_DWORD,
                                 ( CONST BYTE * )&dwPersistentState,
                                 sizeof( DWORD ) );
                         
    if ( dwStatus != ERROR_SUCCESS ) 
    {
        ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateCreateResource: DmLocalSetValue (persistent state) for resource %1!ws! fails, Status = %2!d!...\n",
                   lpszResourceId,
                   dwStatus);
         goto FnExit;
    }

    //
    // Add the resource to the Contains value of the specified group.
    //      
    dwStatus = DmLocalAppendToMultiSz( hXsaction,
                                       hGroupKey,
                                       CLUSREG_NAME_GRP_CONTAINS,
                                       lpszResourceId );

    if ( dwStatus != ERROR_SUCCESS ) 
    {
        ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateCreateResource: DmLocalAppendToMultiSz (contains key) for resource %1!ws! fails, Status = %2!d!...\n",
                   lpszResourceId,
                   dwStatus);
        goto FnExit;
    }
    
skip_registry_updates:
    FmpAcquireResourceLock();

    pGumResource->Resource = FmpCreateResource( pGroup,
                                                lpszResourceId,
                                                lpszResourceName,
                                                FALSE );
                                               
    if ( pGumResource->Resource == NULL ) 
    {
       dwStatus = GetLastError();
       ClRtlLogPrint(LOG_CRITICAL,
                   "[FM] FmpUpdateCreateResource: FmpCreateResource for resource %1!ws! fails, Status = %2!d!...\n",
                   lpszResourceId,
                   dwStatus);      
    } else 
    {
        ClusterEvent( CLUSTER_EVENT_GROUP_PROPERTY_CHANGE,
                                  pGroup );
        ClusterEvent( CLUSTER_EVENT_RESOURCE_ADDED,
                                  pGumResource->Resource );
        if ( pGumResource->Resource ) 
        {
            OmReferenceObject( pGumResource->Resource );
            FmpPostWorkItem( FM_EVENT_RESOURCE_ADDED,
                             pGumResource->Resource,
                             0 );
        }
    }

    FmpReleaseResourceLock();

FnExit:
    if ( pGroup != NULL )
    {
        OmDereferenceObject( pGroup );
    }
    
    if ( hResourceKey != NULL ) 
    {
        DmCloseKey( hResourceKey );
    }

    if ( hGroupKey != NULL ) 
    {
        DmCloseKey( hGroupKey );
    }

    if ( ( dwStatus == ERROR_SUCCESS ) && 
         ( hXsaction != NULL ) )
    {
        DmCommitLocalUpdate( hXsaction );
    }
    else
    {
        if ( hXsaction ) DmAbortLocalUpdate( hXsaction );
    }

    ClRtlLogPrint(LOG_NOISE,
               "[FM] FmpUpdateCreateResource: Exit for resource %1!ws!, Status=%2!u!...\n",
                lpszResourceId,
                dwStatus);

    return( dwStatus );
}

DWORD
FmpUpdateDeleteResource(
    IN BOOL bSourceNode,
    IN LPCWSTR lpszResourceId
    )
/*++

Routine Description:

    GUM dispatch routine for deleting a resource.  For NT5.1 clusters, this is structured as
    as local transaction.

Arguments:

    bSourceNode - Supplies whether or not this node initiated the GUM update.
        Not used.

    lpszResourceId - Supplies the resource ID.

Return Value:

    ERROR_SUCCESS if successful.

    Win32 error code otherwise.

--*/
{
    PFM_RESOURCE    pResource = NULL;
    PFM_GROUP       pGroup = NULL;
    PLIST_ENTRY     pListEntry = NULL;
    PDEPENDENCY     pDependency = NULL;
    PPOSSIBLE_ENTRY pPossibleEntry = NULL;
    DWORD           dwStatus;
    HLOCALXSACTION      
                    hXsaction = NULL;
    DWORD           dwClusterHighestVersion;
    HDMKEY          pGroupKey;

    //
    //  Chittur Subbaraman (chitturs) - 9/7/2000
    //  
    //  Structure this GUM update as a local transaction.
    //
    
    // 
    //  If FM groups are not fully initialized or FM is shutting down, then
    //  don't do anything.
    //
    if ( !FmpFMGroupsInited || FmpShutdown ) 
    {
        return( ERROR_SUCCESS );
    }

    pResource = OmReferenceObjectById( ObjectTypeResource, lpszResourceId );
    
    if ( pResource == NULL ) 
    {
        ClRtlLogPrint(LOG_UNUSUAL,
                     "[FM] FmpUpdateDeleteResource: Resource %1!ws! cannot be found....\n",
                     lpszResourceId );
        return( ERROR_RESOURCE_NOT_FOUND );
    }

    ClRtlLogPrint(LOG_NOISE,
                 "[FM] FmpUpdateDeleteResource: Delete resource %1!ws!, address %2!lx!....\n",
                 lpszResourceId,
                 pResource );

    //
    //  NOTE: It is difficult to include the checkpoint removal in a local transaction, so keep it 
    //  out for now.  Also, note that these functions MUST be called BEFORE the Resources key is
    //  deleted since they enumerate the values under "Resources\RegSync" and "Resources\CryptoSync".
    //
    if ( pResource->Group->OwnerNode == NmLocalNode ) 
    {
        CpckRemoveResourceCheckpoints( pResource );
        CpRemoveResourceCheckpoints( pResource );
    }

    //
    // Start a transaction
    //
    hXsaction = DmBeginLocalUpdate();

    if ( !hXsaction )
    {
        dwStatus = GetLastError();
        ClRtlLogPrint(LOG_CRITICAL,
                  "[FM] FmpUpdateDeleteResource: Failed in starting a transaction for resource %1!ws!, Status =%2!d!....\n",
                   lpszResourceId,
                   dwStatus);
        goto FnExit;
    }

    //
    // Cannot acquire group lock here to avoid deadlocks with this current design.
    //
    
    //
    // Remove all registry entries corresponding to the DependsOn list.
    //
    pListEntry = pResource->DependsOn.Flink;
    while ( pListEntry != &pResource->DependsOn ) 
    {
        pDependency = CONTAINING_RECORD( pListEntry,
                                         DEPENDENCY,
                                         DependentLinkage );
        CL_ASSERT( pDependency->DependentResource == pResource );
        pListEntry = pListEntry->Flink;
        //
        //  Note that the removal of registry entries is done as a local transaction.
        //
        dwStatus = FmpRemoveResourceDependency( hXsaction, 
                                                pResource,
                                                pDependency->ProviderResource );
        if ( dwStatus != ERROR_SUCCESS )
        {
            ClRtlLogPrint(LOG_CRITICAL,
                  "[FM] FmpUpdateDeleteResource: Unable to remove 'DependsOn' registry entries for resource %1!ws!, Status =%2!d!....\n",
                   lpszResourceId,
                   dwStatus);   
            goto FnExit;
        }
    }

    //
    // Remove all registry entries corresponding to the ProvidesFor list.
    //
    pListEntry = pResource->ProvidesFor.Flink;
    while ( pListEntry != &pResource->ProvidesFor ) 
    {
        pDependency = CONTAINING_RECORD( pListEntry,
                                         DEPENDENCY,
                                         ProviderLinkage );
        CL_ASSERT( pDependency->ProviderResource == pResource );
        pListEntry = pListEntry->Flink;
        //
        //  Note that the removal of registry entries is done as a local transaction.
        //
        dwStatus = FmpRemoveResourceDependency( hXsaction, 
                                                pDependency->DependentResource,
                                                pResource );
        if ( dwStatus != ERROR_SUCCESS )
        {
            ClRtlLogPrint(LOG_CRITICAL,
                  "[FM] FmpUpdateDeleteResource: Unable to remove 'ProvidesFor' registry entries for resource %1!ws!, Status=%2!d!....\n",
                   lpszResourceId,
                   dwStatus);   
            goto FnExit;
        }
    }

    //
    //  If we are dealing with a Whistler-Win2K cluster, don't bother to
    //  do these registry updates since the API layer would do it.
    //
    NmGetClusterOperationalVersion( &dwClusterHighestVersion, 
                                    NULL, 
                                    NULL );

    if ( CLUSTER_GET_MAJOR_VERSION( dwClusterHighestVersion ) < 
                NT51_MAJOR_VERSION )
    {
        ClRtlLogPrint(LOG_NOISE,
               "[FM] FmpUpdateDeleteResource: Skipping registry updates for resource %1!ws!...\n",
                lpszResourceId);
        goto skip_registry_updates;
    }

    dwStatus = DmLocalDeleteTree( hXsaction, 
                                  DmResourcesKey, 
                                  OmObjectId( pResource ) );

    if ( ( dwStatus != ERROR_SUCCESS ) &&
         ( dwStatus != ERROR_FILE_NOT_FOUND ) ) 
    {
        ClRtlLogPrint(LOG_CRITICAL,
                      "[FM] FmpUpdateDeleteResource: Unable to remove 'Resources' tree for resource %1!ws!, Status=%2!d!....\n",
                      lpszResourceId,
                      dwStatus);   
        goto FnExit;
    }

    pGroupKey = DmOpenKey( DmGroupsKey,
                           OmObjectId( pResource->Group ),
                           KEY_READ | KEY_SET_VALUE );

    if ( pGroupKey == NULL ) 
    {
        dwStatus = GetLastError();
        ClRtlLogPrint(LOG_CRITICAL,
                      "[FM] FmpUpdateDeleteResource: Unable to find 'Groups' key for resource %1!ws!, Status=%2!d!....\n",
                      lpszResourceId,
                      dwStatus);   
        goto FnExit;
    }

    dwStatus = DmLocalRemoveFromMultiSz( hXsaction,
                                         pGroupKey,
                                         CLUSREG_NAME_GRP_CONTAINS,
                                         OmObjectId( pResource ) );

    DmCloseKey( pGroupKey );

    if ( dwStatus != ERROR_SUCCESS ) 
    {
        ClRtlLogPrint(LOG_CRITICAL,
                     "[FM] FmpUpdateDeleteResource: Unable to remove contains list for resource %1!ws! in group %2!ws!, Status=%3!d!....\n",
                      lpszResourceId,
                      OmObjectId( pResource->Group ),
                      dwStatus);   
        goto FnExit;
    }
    
skip_registry_updates:
    //
    // Remove all list entries corresponding to the DependsOn list.
    //
    pListEntry = pResource->DependsOn.Flink;
    while ( pListEntry != &pResource->DependsOn ) {
        pDependency = CONTAINING_RECORD( pListEntry,
                                         DEPENDENCY,
                                         DependentLinkage );
        pListEntry = pListEntry->Flink;
        RemoveEntryList( &pDependency->ProviderLinkage );
        RemoveEntryList( &pDependency->DependentLinkage );
        OmDereferenceObject( pDependency->DependentResource );
        OmDereferenceObject( pDependency->ProviderResource );
        LocalFree( pDependency );
    }

    //
    // Remove all list entries corresponding to the ProvidesFor list.
    //
    pListEntry = pResource->ProvidesFor.Flink;
    while ( pListEntry != &pResource->ProvidesFor ) {
        pDependency = CONTAINING_RECORD( pListEntry,
                                         DEPENDENCY,
                                         ProviderLinkage );
        pListEntry = pListEntry->Flink;
        RemoveEntryList( &pDependency->ProviderLinkage );
        RemoveEntryList( &pDependency->DependentLinkage );
        OmDereferenceObject( pDependency->DependentResource );
        OmDereferenceObject( pDependency->ProviderResource );
        LocalFree( pDependency );
    }
    
    //
    // Remove all entries from the possible owners list.
    //
    while ( !IsListEmpty( &pResource->PossibleOwners ) ) 
    {
        pListEntry = RemoveHeadList( &pResource->PossibleOwners );
        pPossibleEntry = CONTAINING_RECORD( pListEntry,
                                            POSSIBLE_ENTRY,
                                            PossibleLinkage );
        OmDereferenceObject( pPossibleEntry->PossibleNode );
        LocalFree( pPossibleEntry );
    }

    //
    // Remove this resource from the Contains list.
    //
    RemoveEntryList( &pResource->ContainsLinkage );

    OmDereferenceObject( pResource );

    //
    // Close the resource's registry key.
    //
    DmRundownList( &pResource->DmRundownList );
    if ( pResource->RegistryKey != NULL ) 
    {
        DmCloseKey( pResource->RegistryKey );
        pResource->RegistryKey = NULL;
    }

    //
    // SS: we do not delete the reference to the resource here
    // since we will shortly have to add one before posting a notification
    // to the fm worker thread.
    //
    // Post a work item to close the resource in the resource handler.
    // Note that this must be done asynchronously as we cannot call
    // the resource monitor from a GUM handler. If we do, resources
    // do funny things and make deadlocks.
    //
    FmpPostWorkItem( FM_EVENT_RESOURCE_DELETED, pResource, 0 );

    //
    // Decrement resource type reference.
    //
    if ( pResource->Type != NULL ) {
        OmDereferenceObject( pResource->Type );
        pResource->Type = NULL;
    }

    //
    // Remove the resource from the resource list.
    //
    dwStatus = OmRemoveObject( pResource );

    ClusterEvent( CLUSTER_EVENT_RESOURCE_DELETED, pResource );
    ClusterEvent( CLUSTER_EVENT_GROUP_PROPERTY_CHANGE,
                  pResource->Group );

    //
    // Mark the resource as deleted
    //
    pResource->dwStructState = FM_RESOURCE_STRUCT_MARKED_FOR_DELETE;

FnExit:
    OmDereferenceObject( pResource );

    if ( ( dwStatus == ERROR_SUCCESS ) && 
         ( hXsaction != NULL ) )
    {
        DmCommitLocalUpdate( hXsaction );
    }
    else
    {
        if ( hXsaction ) DmAbortLocalUpdate( hXsaction );
    }

    ClRtlLogPrint(LOG_NOISE,
               "[FM] FmpUpdateDeleteResource: Exit for resource %1!ws!, Status=%2!u!...\n",
                lpszResourceId,
                dwStatus);

    return( dwStatus );
} // FmpUpdateDeleteResource

DWORD
FmpUpdateUseRandomizedNodeListForGroups(
    IN BOOL     SourceNode,
    IN LPCWSTR  pszNodeId,
    IN PFM_GROUP_NODE_LIST  pGroupNodeList
    )
/*++

Routine Description:

    GUM dispatch routine for using a randomized preferred list for group ownership on
    node down.

Arguments:

    bSourceNode - Supplies whether or not this node initiated the GUM update.
        Not used.

    pszNodeId - Supplies the ID of the node that is down.

    pGroupNodeList - Randomized preferred node list for groups.

Return Value:

    ERROR_SUCCESS if successful.

    Win32 error code otherwise.

--*/
{
    DWORD       dwStatus = ERROR_SUCCESS;
    DWORD       dwNodeId;

    //
    //  Chittur Subbaraman (chitturs) - 4/19/2001
    // 
    //  If FM groups are not fully initialized or FM is shutting down, then
    //  don't do anything.
    //
    if ( !FmpFMGroupsInited || FmpShutdown ) 
    {
        return( ERROR_SUCCESS );
    }

    //
    //  In a node evict, the NM GUM handler gets rids of the dead node from the OM list.
    //  This node down FM GUM handler could follow the NM evict GUM handler since NM
    //  lets an evict through once *it* declares a node as down. At that time, there is no
    //  guarantee that the FM node down GUM has executed since that GUM is issued in the
    //  async phase of node down processing and could very well follow the NM evict GUM
    //  handler.  Thus, this GUM handler and associated functions CANNOT call OM to get
    //  a node object from the node ID string.  Thus, this GUM handler and associated
    //  functions are carefully written to work with a node ID as opposed to a node object.
    //
    dwNodeId = wcstoul( pszNodeId, NULL, 10 );
    
    //
    // If this update has already been seen after the node down, ignore this one
    //
    if ( gFmpNodeArray[dwNodeId].dwNodeDownProcessingInProgress == 0 )
    {
        ClRtlLogPrint(LOG_NOISE,
                      "[FM] FmpUpdateUseRandomizedNodeListForGroups: %1!ws! node down has been processed already...\n",
                      pszNodeId);
        goto FnExit;                   
    }

    //
    // Assign ownership to all groups owned by the dead node
    //
    dwStatus = FmpAssignOwnersToGroups( pszNodeId, 
                                        NULL,
                                        pGroupNodeList );

    if ( dwStatus != ERROR_SUCCESS ) 
    {
        ClRtlLogPrint(LOG_CRITICAL,
                      "[FM] FmpUpdateUseRandomizedNodeListForGroups: FmpAssignOwnersToGroups failed %1!d!\n",
                      dwStatus);
    }                   

    //
    // Mark that the node down processing has been done
    //
    gFmpNodeArray[dwNodeId].dwNodeDownProcessingInProgress = 0;
    
FnExit:        
    return( dwStatus );
}// FmpUpdateUseRandomizedNodeListForGroups