You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4254 lines
135 KiB
4254 lines
135 KiB
/*++
|
|
|
|
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
|