mirror of https://github.com/tongzx/nt5src
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.
1776 lines
52 KiB
1776 lines
52 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
resmon.c
|
|
|
|
Abstract:
|
|
|
|
Cluster resource manager interface routines for the resource monitor.
|
|
|
|
Author:
|
|
|
|
Rod Gamache (rodga) 17-Apr-1996
|
|
|
|
|
|
Notes:
|
|
|
|
WARNING: All of the routines in this file assume that the resource
|
|
lock is held when they are called.
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include "fmp.h"
|
|
|
|
#define LOG_MODULE RESMONF
|
|
|
|
//
|
|
// Global Data
|
|
//
|
|
|
|
//
|
|
// Local function prototypes
|
|
//
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Resource Control Routines (via Resource Monitor)
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
DWORD
|
|
FmpRmExceptionFilter(
|
|
DWORD ExceptionCode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Exception filter for calls to the Resource Monitor. These calls will
|
|
often raise an exception if the RPC path to the Resource Monitor fails.
|
|
|
|
Arguments:
|
|
|
|
ExceptionCode - the exception to process.
|
|
|
|
Returns:
|
|
|
|
EXCEPTION_EXECUTE_HANDLE if the exception handler should handle this failure
|
|
EXCEPTION_CONTINUE_SEARCH if the exception is a fatal exception and the handler
|
|
should not handle it.
|
|
|
|
--*/
|
|
|
|
{
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[FM] FmpRmExceptionFilter: Unusual exception %1!u! occurred.\n",
|
|
ExceptionCode);
|
|
return(I_RpcExceptionFilter(ExceptionCode));
|
|
} // FmpRmExceptionFilter
|
|
|
|
|
|
|
|
DWORD
|
|
FmpRmCreateResource(
|
|
PFM_RESOURCE Resource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Add a resource to the list of resources managed by the resource monitor.
|
|
|
|
Arguments:
|
|
|
|
Resource - The resource to add.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
PRESMON monitor;
|
|
LPWSTR debugPrefix;
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpRmCreateResource: creating resource %1!ws! in %2!ws! resource monitor\n",
|
|
OmObjectId(Resource),
|
|
Resource->Flags & RESOURCE_SEPARATE_MONITOR ?
|
|
L"separate" : L"shared");
|
|
|
|
if (Resource->Flags & RESOURCE_SEPARATE_MONITOR) {
|
|
if ( Resource->DebugPrefix != NULL ) {
|
|
debugPrefix = Resource->DebugPrefix;
|
|
} else {
|
|
debugPrefix = Resource->Type->DebugPrefix;
|
|
}
|
|
Resource->Monitor = FmpCreateMonitor(debugPrefix, TRUE);
|
|
if (Resource->Monitor == NULL) {
|
|
return(GetLastError());
|
|
}
|
|
} else {
|
|
CL_ASSERT(FmpDefaultMonitor != NULL);
|
|
Resource->Monitor = FmpDefaultMonitor;
|
|
}
|
|
|
|
try {
|
|
Resource->Id = RmCreateResource(Resource->Monitor->Binding,
|
|
Resource->Type->DllName,
|
|
OmObjectId(Resource->Type),
|
|
OmObjectId(Resource),
|
|
Resource->LooksAlivePollInterval,
|
|
Resource->IsAlivePollInterval,
|
|
(RM_NOTIFY_KEY)Resource,
|
|
Resource->PendingTimeout,
|
|
&status);
|
|
}
|
|
except( FmpRmExceptionFilter(GetExceptionCode()) ) {
|
|
|
|
DWORD code = GetExceptionCode();
|
|
|
|
ClRtlLogPrint(LOG_NOISE,"[FM] RmCreateResource issued exception %1!u!\n", code);
|
|
|
|
//
|
|
// Stop this resource monitor if it is a separate resource monitor.
|
|
//
|
|
if (Resource->Flags & RESOURCE_SEPARATE_MONITOR) {
|
|
monitor = Resource->Monitor;
|
|
#if 0
|
|
CL_ASSERT( monitor->NotifyThread != NULL );
|
|
CL_ASSERT( monitor->Process != NULL );
|
|
|
|
// Terminate Thread call removed: ( monitor->NotifyThread, 1 );
|
|
CloseHandle( monitor->NotifyThread );
|
|
|
|
TerminateProcess( monitor->Process, 1 );
|
|
LocalFree( monitor );
|
|
#endif
|
|
FmpShutdownMonitor( monitor );
|
|
}
|
|
|
|
Resource->Monitor = NULL;
|
|
return(code);
|
|
}
|
|
|
|
if (Resource->Id != 0) {
|
|
Resource->Flags |= RESOURCE_CREATED;
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpRmCreateResource: created resource %1!ws!, resid %2!u!\n",
|
|
OmObjectId(Resource),
|
|
Resource->Id);
|
|
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpRmCreateResource: unable to create resource %1!ws!\n",
|
|
OmObjectId(Resource));
|
|
//
|
|
// Stop this resource monitor if it is a separate resource monitor.
|
|
//
|
|
if (Resource->Flags & RESOURCE_SEPARATE_MONITOR) {
|
|
monitor = Resource->Monitor;
|
|
#if 0
|
|
CL_ASSERT( monitor->NotifyThread != NULL );
|
|
CL_ASSERT( monitor->Process != NULL );
|
|
|
|
// Terminate Thread call removed: ( monitor->NotifyThread, 1 );
|
|
CloseHandle( monitor->NotifyThread );
|
|
|
|
TerminateProcess( monitor->Process, 1 );
|
|
LocalFree( monitor );
|
|
#endif
|
|
FmpShutdownMonitor( monitor );
|
|
}
|
|
|
|
Resource->Monitor = NULL;
|
|
return(status);
|
|
|
|
} // FmpRmCreateResource
|
|
|
|
|
|
|
|
DWORD
|
|
FmpRmOnlineResource(
|
|
PFM_RESOURCE Resource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine requests the Resource Monitor to bring a resource online.
|
|
|
|
Arguments:
|
|
|
|
Resource - A pointer to the resource to bring online.
|
|
|
|
Comments :
|
|
If this is the quorum resource, the exclusive quorum lock should be
|
|
held when this routine is called. Else the quorum lock should be held
|
|
in shared mode. This routine release the lock.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS - if the request was successful.
|
|
ERROR_IO_PENDING - if the request is pending.
|
|
A Win32 error if the request failed.
|
|
|
|
--*/
|
|
|
|
{
|
|
CLUSTER_RESOURCE_STATE state;
|
|
DWORD Status=ERROR_SUCCESS;
|
|
DWORD retry = 200;
|
|
|
|
#if 0
|
|
PVOID callersAddress;
|
|
PVOID callersCaller;
|
|
|
|
RtlGetCallersAddress(
|
|
&callersAddress,
|
|
&callersCaller );
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] RmOnlineResource for <%1!ws!> called from %2!lx! and %3!lx!\n",
|
|
OmObjectId( Resource ),
|
|
callersAddress, callersCaller );
|
|
#endif
|
|
if ( Resource->State > ClusterResourcePending ) {
|
|
Status = ERROR_IO_PENDING;
|
|
return(Status);
|
|
}
|
|
|
|
if ( Resource->State == ClusterResourceOnline ) {
|
|
Status = ERROR_SUCCESS;
|
|
return(Status);
|
|
}
|
|
|
|
|
|
CL_ASSERT((Resource->State == ClusterResourceOffline) ||
|
|
(Resource->State == ClusterResourceFailed));
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpRmOnlineResource: bringing resource %1!ws! (resid %2!u!) online.\n",
|
|
OmObjectId(Resource),
|
|
Resource->Id);
|
|
|
|
//if this is the quorum resource acquire the quolock
|
|
// For registry replication to work, the resource should
|
|
// not be brought online while the quorum resource is offline
|
|
// what do we do for fixquorum mode
|
|
|
|
OmNotifyCb(Resource, NOTIFY_RESOURCE_PREONLINE);
|
|
|
|
//SS:initialize state so that in case of a failure, a failed state is
|
|
// propagated.
|
|
state = ClusterResourceFailed;
|
|
|
|
CheckQuorumState:
|
|
|
|
//CL_ASSERT( (LONG)gdwQuoBlockingResources >= 0 );
|
|
|
|
|
|
if (Resource->QuorumResource) {
|
|
ACQUIRE_EXCLUSIVE_LOCK(gQuoLock);
|
|
|
|
} else {
|
|
DWORD dwOldBlockingFlag;
|
|
|
|
ACQUIRE_SHARED_LOCK(gQuoLock);
|
|
|
|
// if it is not the quorum resource,
|
|
// check the state of the quorum resource
|
|
|
|
// check if the quorum resource is failed
|
|
// we must exit from here and let the recovery for the
|
|
// quorum resource to kick in
|
|
if (gpQuoResource->State == ClusterResourceFailed)
|
|
{
|
|
Status = ERROR_QUORUM_RESOURCE_ONLINE_FAILED;
|
|
CL_LOGFAILURE(ERROR_QUORUM_RESOURCE_ONLINE_FAILED);
|
|
//we dont halt, we will try online again at a later time
|
|
FmpCallResourceNotifyCb(Resource, state);
|
|
FmpPropagateResourceState( Resource, state );
|
|
goto FnExit;
|
|
|
|
}
|
|
|
|
// check if the quorum resource is online,
|
|
// if the quorum resource is marked as waiting and offlinepending,
|
|
// it is actually online
|
|
// if the quorum resource still needs to come online
|
|
// release the lock and wait
|
|
if (((gpQuoResource->State != ClusterResourceOnline) &&
|
|
((gpQuoResource->State != ClusterResourceOfflinePending) ||
|
|
(!(gpQuoResource->Flags & RESOURCE_WAITING))))
|
|
&& !CsNoQuorum)
|
|
{
|
|
// we release the lock here since the quorum resource
|
|
// state transition from pending needs to acquire the lock
|
|
// In general it is a bad idea to do a wait holding locks
|
|
RELEASE_LOCK(gQuoLock);
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpRmOnlineResource: release quolock/group lock and wait on ghQuoOnlineEvent\r\n");
|
|
Status = WaitForSingleObject(ghQuoOnlineEvent, 500);
|
|
if ( Status == WAIT_OBJECT_0 ) {
|
|
// If we're going to retry - make sure we wait a little.
|
|
Sleep( 500 );
|
|
}
|
|
if ( retry-- ) {
|
|
goto CheckQuorumState;
|
|
}
|
|
#if DBG
|
|
if ( IsDebuggerPresent() ) {
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
CL_LOGFAILURE(ERROR_QUORUM_RESOURCE_ONLINE_FAILED);
|
|
//we dont halt, we will try online again at a later time
|
|
FmpCallResourceNotifyCb(Resource, state);
|
|
FmpPropagateResourceState( Resource, state );
|
|
return(ERROR_QUORUM_RESOURCE_ONLINE_FAILED);
|
|
//CsInconsistencyHalt(ERROR_INVALID_STATE);
|
|
}
|
|
|
|
//
|
|
// assume that we'll be pending... mark the resource as having
|
|
// bumped the QuoBlockResource count.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpRmOnlineResource: called InterlockedIncrement on gdwQuoBlockingResources for resource %1!ws!\n",
|
|
OmObjectId(Resource));
|
|
|
|
dwOldBlockingFlag = InterlockedExchange( &Resource->BlockingQuorum, 1 );
|
|
CL_ASSERT( dwOldBlockingFlag == 0 );
|
|
|
|
InterlockedIncrement(&gdwQuoBlockingResources);
|
|
|
|
//
|
|
// everything is now fine on the local node... if any other
|
|
// component (CP) needs to synchronize with the quorum resource, then
|
|
// should acquire the shared lock on the quorum node as part of
|
|
// their operation. If that fails, then they should assume the quorum
|
|
// resource moved, and they should retry.
|
|
//
|
|
}
|
|
|
|
// By now we have either the shared or the exclusive lock on the
|
|
// quorum resource.
|
|
// If we have the shared lock then the quorum resource is online(somewhere).
|
|
// Unlesss there is a failure, it should not go offline.
|
|
|
|
|
|
|
|
Status = ERROR_SUCCESS;
|
|
if (Resource->QuorumResource) {
|
|
Status = FmpRmArbitrateResource( Resource );
|
|
}
|
|
if (Status == ERROR_SUCCESS) {
|
|
Status = RmOnlineResource( Resource->Id,
|
|
(LPDWORD)&state // cast to quiet win64 warning
|
|
);
|
|
if (Resource->QuorumResource && Status != ERROR_SUCCESS) {
|
|
MMSetQuorumOwner( MM_INVALID_NODE , /* Block = */ FALSE, NULL );
|
|
}
|
|
}
|
|
|
|
FmpCallResourceNotifyCb(Resource, state);
|
|
|
|
//SS: the synchronous state propagation must happen when it goes offline
|
|
FmpPropagateResourceState( Resource, state );
|
|
|
|
//
|
|
// Cleanup for the non-quorum resource case.
|
|
//
|
|
if ( !Resource->QuorumResource &&
|
|
Resource->State < ClusterResourcePending ) {
|
|
DWORD dwOldBlockingFlag;
|
|
|
|
dwOldBlockingFlag = InterlockedExchange( &Resource->BlockingQuorum, 0 );
|
|
if ( dwOldBlockingFlag ) {
|
|
//
|
|
// If the Transition Thread processed the request, then we can't
|
|
// perform the decrement.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpRmOnlineResource: InterlockedDecrement on gdwQuoBlockingResources for resource %1!ws!\n",
|
|
OmObjectId(Resource));
|
|
|
|
InterlockedDecrement(&gdwQuoBlockingResources);
|
|
}
|
|
}
|
|
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpRmOnlineResource: RmOnlineResource Failed. Resource %1!ws!, status %2!u!\n",
|
|
OmObjectId(Resource),
|
|
Status);
|
|
}
|
|
|
|
//if RmOnlineResource is successful, do the post processing
|
|
if ( Resource->State == ClusterResourceOnline ) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpRmOnlineResource: %1!ws! is now online\n",
|
|
OmObjectId(Resource));
|
|
//if this is the quorum resource and it goes into online state
|
|
//immediately, wake other threads
|
|
if (Resource->QuorumResource)
|
|
SetEvent(ghQuoOnlineEvent);
|
|
|
|
} else if ( Resource->State > ClusterResourcePending ) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpRmOnlineResource: Resource %1!ws! pending\n",
|
|
OmObjectId(Resource));
|
|
//SS: what should we tell the callbacks
|
|
//FmpNotifyResourceCb(Resource,??);
|
|
//will they eventually get called if so how ?
|
|
if (Resource->QuorumResource)
|
|
{
|
|
//the quorum resource is coming online, unsignal the event so that
|
|
//all threads that need quorum resource to be online will block
|
|
ResetEvent(ghQuoOnlineEvent);
|
|
}
|
|
Status = ERROR_IO_PENDING;
|
|
} else {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpRmOnlineResource: Failed. Resource %1!ws!, state %2!u!\n",
|
|
OmObjectId(Resource),
|
|
Resource->State);
|
|
//
|
|
// rjain: for a synchronous resource we must post RESOURCE_FAILED event
|
|
// so thatfailback policies are correctly followed
|
|
// Also pretend that the old state to be online to actually force the
|
|
// restart behaviour. See: FmpProcessResourceEvents.
|
|
//
|
|
OmReferenceObject(Resource);
|
|
FmpPostWorkItem(FM_EVENT_RES_RESOURCE_FAILED,
|
|
Resource,
|
|
ClusterResourceOnline);
|
|
Status = ERROR_RESMON_ONLINE_FAILED;
|
|
}
|
|
|
|
FnExit:
|
|
RELEASE_LOCK(gQuoLock);
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpRmOnlineResource: Returning. Resource %1!ws!, state %2!u!, status %3!u!.\n",
|
|
OmObjectId(Resource),
|
|
Resource->State,
|
|
Status);
|
|
return (Status);
|
|
|
|
} // FmpRmOnlineResource
|
|
|
|
|
|
|
|
VOID
|
|
FmpRmTerminateResource(
|
|
PFM_RESOURCE Resource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Terminates (immediately) a resource.
|
|
|
|
Arguments:
|
|
|
|
Resource - A pointer to the resource to terminate.
|
|
|
|
Returns:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwOldBlockingFlag;
|
|
|
|
|
|
//notify callbacks that need preprocessing before a resource is
|
|
//brought offline-call this here since all resources may not go
|
|
//thru the offline pending transition
|
|
//SS - what if the resource never even goes to offline
|
|
//pending state - then should we renotify the callbacks that it
|
|
//is still online?
|
|
OmNotifyCb(Resource, NOTIFY_RESOURCE_PREOFFLINE);
|
|
|
|
//
|
|
// Try to terminate the resource.
|
|
//
|
|
try {
|
|
if (Resource->QuorumResource) {
|
|
MMSetQuorumOwner( MM_INVALID_NODE, /* Block = */ FALSE, NULL );
|
|
}
|
|
RmTerminateResource(Resource->Id);
|
|
|
|
// if FmpRmterminate was called for a failed resource, mark
|
|
// the resource as Failed and not Offline.
|
|
if (Resource->State == ClusterResourceFailed)
|
|
{
|
|
FmpCallResourceNotifyCb(Resource, ClusterResourceFailed);
|
|
FmpPropagateResourceState( Resource, ClusterResourceFailed );
|
|
}
|
|
|
|
else
|
|
{
|
|
FmpCallResourceNotifyCb(Resource, ClusterResourceOffline);
|
|
FmpPropagateResourceState( Resource, ClusterResourceOffline );
|
|
}
|
|
}
|
|
except( FmpRmExceptionFilter(GetExceptionCode()) ) {
|
|
|
|
DWORD code = GetExceptionCode();
|
|
|
|
ClRtlLogPrint(LOG_NOISE,"[FM] RmTerminateResource issued exception %1!u!\n", code);
|
|
|
|
return;
|
|
}
|
|
|
|
//if terminate was called during a pending state, this resource may be
|
|
//blocking the quorum resource, decrement the blocking count
|
|
dwOldBlockingFlag = InterlockedExchange( &Resource->BlockingQuorum, 0 );
|
|
|
|
if ( dwOldBlockingFlag ) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpRmTerminateResource: InterlockedDecrement on gdwQuoBlockingResources, Resource %1!ws!\n",
|
|
OmObjectId(Resource));
|
|
InterlockedDecrement(&gdwQuoBlockingResources);
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] RmTerminateResource: %1!ws! is now offline\n",
|
|
OmObjectId(Resource));
|
|
|
|
return;
|
|
|
|
} // FmpRmTerminateResource
|
|
|
|
|
|
|
|
DWORD
|
|
FmpRmOfflineResource(
|
|
PFM_RESOURCE Resource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Calls the Resource Monitor to take a resource offline.
|
|
|
|
Arguments:
|
|
|
|
Resource - A pointer to the resource to terminate.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS if the request is successful.
|
|
ERROR_IO_PENDING if the request is pending.
|
|
Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
CLUSTER_RESOURCE_STATE state;
|
|
DWORD status;
|
|
DWORD retry = 200;
|
|
|
|
#if DBG
|
|
PLIST_ENTRY listEntry;
|
|
#endif
|
|
|
|
if ( Resource->State > ClusterResourcePending ) {
|
|
ClRtlLogPrint(LOG_NOISE,"[FM] FmpRmOfflineResource: pending condition\n");
|
|
return(ERROR_IO_PENDING);
|
|
}
|
|
|
|
CL_ASSERT(Resource->State != ClusterResourceOffline);
|
|
|
|
#if DBG
|
|
// everything else in the same group must be offline if this is the
|
|
// quorum resource
|
|
if ( Resource->QuorumResource ) {
|
|
PFM_GROUP group = Resource->Group;
|
|
PFM_RESOURCE resource;
|
|
|
|
for ( listEntry = group->Contains.Flink;
|
|
listEntry != &(group->Contains);
|
|
listEntry = listEntry->Flink ) {
|
|
|
|
resource = CONTAINING_RECORD(listEntry,
|
|
FM_RESOURCE,
|
|
ContainsLinkage );
|
|
if ( (Resource != resource) &&
|
|
(resource->State != ClusterResourceOffline) &&
|
|
(resource->State != ClusterResourceFailed) &&
|
|
(resource->State != ClusterResourceOfflinePending) ) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] RmOfflineResource: resource <%1!ws!> not offline when the quorum resource was shutting down.\n",
|
|
OmObjectId(resource));
|
|
CsInconsistencyHalt(ERROR_INVALID_STATE);
|
|
}
|
|
}
|
|
} else {
|
|
// this is not the quorum resource... but if the quorum resource is in
|
|
// this group, it must not be offline!
|
|
PFM_GROUP group = Resource->Group;
|
|
PFM_RESOURCE resource;
|
|
|
|
for ( listEntry = group->Contains.Flink;
|
|
listEntry != &(group->Contains);
|
|
listEntry = listEntry->Flink ) {
|
|
|
|
resource = CONTAINING_RECORD(listEntry,
|
|
FM_RESOURCE,
|
|
ContainsLinkage );
|
|
if ( (resource->QuorumResource) &&
|
|
((resource->State == ClusterResourceOffline) ||
|
|
(resource->State == ClusterResourceFailed) ||
|
|
((resource->State == ClusterResourceOfflinePending) &&
|
|
(!resource->Flags & RESOURCE_WAITING))) ) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] RmOfflineResource: quorum resource <%1!ws!> offline when resource <%2!ws!> was shutting down.\n",
|
|
OmObjectId(resource),
|
|
OmObjectId(Resource));
|
|
CsInconsistencyHalt(ERROR_INVALID_STATE);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
state = ClusterResourceFailed;
|
|
|
|
CheckQuorumState:
|
|
|
|
//if this is the quorum resource acquire the quolock
|
|
// For registry replication to work, the resource should
|
|
// not be brought online while the quorum resource is offline
|
|
// what do we do for fixquorum mode
|
|
if (Resource->QuorumResource) {
|
|
ACQUIRE_EXCLUSIVE_LOCK(gQuoLock);
|
|
} else {
|
|
ACQUIRE_SHARED_LOCK(gQuoLock);
|
|
}
|
|
|
|
//if it is not the quorum resource, check the state of the quorum resource
|
|
if (!Resource->QuorumResource)
|
|
{
|
|
DWORD dwOldBlockingFlag;
|
|
|
|
// check if the quorum resource is failed
|
|
// we must exit from here and allow the recovery for the
|
|
// quorum resource to kick in, which can only happen when
|
|
// the group lock is released
|
|
if (gpQuoResource->State == ClusterResourceFailed)
|
|
{
|
|
status = ERROR_QUORUM_RESOURCE_ONLINE_FAILED;
|
|
CL_LOGFAILURE(ERROR_QUORUM_RESOURCE_ONLINE_FAILED);
|
|
RELEASE_LOCK(gQuoLock);
|
|
FmpCallResourceNotifyCb(Resource, state);
|
|
FmpPropagateResourceState( Resource, state );
|
|
return(status);
|
|
|
|
}
|
|
|
|
// check if the quorum resource is online,
|
|
// if the quorum resource is marked as waiting and offlinepending,
|
|
// it is actually online.
|
|
// if the quorum resource still needs to come online,
|
|
// release the lock and wait
|
|
if (((gpQuoResource->State != ClusterResourceOnline) &&
|
|
((gpQuoResource->State != ClusterResourceOfflinePending) ||
|
|
(!(gpQuoResource->Flags & RESOURCE_WAITING))))
|
|
&& !CsNoQuorum)
|
|
{
|
|
RELEASE_LOCK(gQuoLock);
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpRmOfflineResource: release quolock/group lock and wait on ghQuoOnlineEvent\r\n");
|
|
WaitForSingleObject(ghQuoOnlineEvent, 500);
|
|
if ( retry-- ) {
|
|
Sleep(500);
|
|
goto CheckQuorumState;
|
|
}
|
|
#if DBG
|
|
if ( IsDebuggerPresent() ) {
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
CL_LOGFAILURE(ERROR_QUORUM_RESOURCE_ONLINE_FAILED);
|
|
// Should we halt? What about the pre-online notification above?
|
|
FmpCallResourceNotifyCb(Resource, state);
|
|
FmpPropagateResourceState( Resource, state );
|
|
return(ERROR_QUORUM_RESOURCE_ONLINE_FAILED);
|
|
//CsInconsistencyHalt(ERROR_INVALID_STATE);
|
|
|
|
}
|
|
|
|
//
|
|
// assume that we'll be pending... mark the resource as having
|
|
// bumped the QuoBlockResource count.
|
|
//
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpRmOfflineResource: InterlockedIncrement on gdwQuoBlockingResources for resource %1!ws!\n",
|
|
OmObjectId(Resource));
|
|
|
|
dwOldBlockingFlag = InterlockedExchange( &Resource->BlockingQuorum, 1 );
|
|
CL_ASSERT( dwOldBlockingFlag == 0 );
|
|
|
|
InterlockedIncrement(&gdwQuoBlockingResources);
|
|
|
|
}
|
|
else
|
|
{
|
|
DWORD dwNumBlockingResources;
|
|
|
|
//allow resources about 30 seconds to finish a pending
|
|
//operation
|
|
retry = 60;
|
|
|
|
// This is for a quorum resource.
|
|
|
|
CheckPendingResources:
|
|
|
|
// this is the quorum resource, wait for other resources
|
|
// to get out of their pending states
|
|
// new resources are not allowed to queue since the quorum
|
|
// lock is held exclusively
|
|
|
|
dwNumBlockingResources =
|
|
InterlockedCompareExchange( &gdwQuoBlockingResources, 0, 0 );
|
|
|
|
if (dwNumBlockingResources)
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpRmOfflineResource: Quorum resource waiting to be brought offline-sleep.BlckingRes=%1!u!\r\n",
|
|
dwNumBlockingResources);
|
|
//sleep for 500 msec
|
|
Sleep(500);
|
|
if ( retry-- ) {
|
|
goto CheckPendingResources;
|
|
}
|
|
//if some resources are still pending, go ahead and offline
|
|
//the quorum, the checkpointing code will simply retry when
|
|
//it finds that the quorum resource is not available
|
|
#if 0
|
|
if ( IsDebuggerPresent() ) {
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpRmOfflineResource: Quorum resource is being brought offline despite %1!u! pending resources...\r\n",
|
|
dwNumBlockingResources);
|
|
|
|
}
|
|
}
|
|
|
|
status = ERROR_SUCCESS;
|
|
if (Resource->QuorumResource) {
|
|
state = Resource->State;
|
|
status = MMSetQuorumOwner( MM_INVALID_NODE, /* Block = */ TRUE, NULL );
|
|
}
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
//notify callbacks that need preprocessing before a resource is
|
|
//brought offline-call this here since all resources may not go
|
|
//thru the offline pending transition
|
|
//SS - what if the resource never even goes to offline
|
|
//pending state - then should we renotify the callbacks that it
|
|
//is still online?
|
|
state = ClusterResourceOffline;
|
|
|
|
OmNotifyCb(Resource, NOTIFY_RESOURCE_PREOFFLINE);
|
|
status = RmOfflineResource( Resource->Id,
|
|
(LPDWORD)&state // cast to quiet win64 warning
|
|
);
|
|
}
|
|
|
|
//
|
|
// Cleanup for the non-quorum resource case
|
|
// if the resource has gone offline, decrement the count
|
|
//
|
|
if ( !Resource->QuorumResource &&
|
|
state < ClusterResourcePending ) {
|
|
DWORD dwOldBlockingFlag;
|
|
|
|
dwOldBlockingFlag = InterlockedExchange( &Resource->BlockingQuorum, 0 );
|
|
if ( dwOldBlockingFlag ) {
|
|
//
|
|
// If the Transition Thread processed the request, then we can't
|
|
// perform the decrement.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpRmOfflineResource: InterlockedDecrement on gdwQuoBlockingResources for resource %1!ws!\n",
|
|
OmObjectId(Resource));
|
|
|
|
InterlockedDecrement(&gdwQuoBlockingResources);
|
|
}
|
|
}
|
|
|
|
if (status == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// If the new state is pending, then we must wait.
|
|
//
|
|
if ( state == ClusterResourceOffline ) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpRmOfflineResource: %1!ws! is now offline\n",
|
|
OmObjectId(Resource));
|
|
} else if ( state == ClusterResourceOfflinePending ) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpRmOfflineResource: %1!ws! offline pending\n",
|
|
OmObjectId(Resource));
|
|
status = ERROR_IO_PENDING;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpRmOfflineResource: RmOffline() for %1!ws! returned error %2!u!\r\n",
|
|
OmObjectId(Resource), status);
|
|
}
|
|
|
|
FmpCallResourceNotifyCb(Resource, state);
|
|
FmpPropagateResourceState( Resource, state );
|
|
RELEASE_LOCK(gQuoLock);
|
|
|
|
return(status);
|
|
|
|
} // FmpRmOfflineResource
|
|
|
|
|
|
|
|
DWORD
|
|
FmpRmCloseResource(
|
|
PFM_RESOURCE Resource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Removes a resource from those being managed by the resource monitor.
|
|
|
|
Arguments:
|
|
|
|
Resource - The resource to remove.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
PRESMON monitor;
|
|
|
|
if (Resource->Id == 0) {
|
|
//
|
|
// This resource was never fully created.
|
|
//
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
|
|
monitor = Resource->Monitor;
|
|
Resource->Monitor = NULL;
|
|
|
|
if ( Resource->QuorumResource ) {
|
|
RmReleaseResource( Resource->Id );
|
|
Resource->QuorumResource = FALSE;
|
|
}
|
|
|
|
try {
|
|
RmCloseResource(&Resource->Id);
|
|
}
|
|
except( FmpRmExceptionFilter(GetExceptionCode()) ) {
|
|
|
|
DWORD status = GetExceptionCode();
|
|
|
|
ClRtlLogPrint(LOG_NOISE,"[FM] RmDestroyResource issued exception %1!u!\n", status);
|
|
|
|
}
|
|
|
|
if ( monitor &&
|
|
Resource->Flags & RESOURCE_SEPARATE_MONITOR) {
|
|
//
|
|
// Shutdown the resource monitor as well.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] Shutting down separate resource monitor!\n");
|
|
FmpShutdownMonitor(monitor);
|
|
}
|
|
|
|
Resource->Id = 0;
|
|
|
|
return(ERROR_SUCCESS);
|
|
|
|
} // FmpRmCloseResource
|
|
|
|
|
|
|
|
DWORD
|
|
FmpRmArbitrateResource(
|
|
IN PFM_RESOURCE Resource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arbitrate for the given resource.
|
|
|
|
Arguments:
|
|
|
|
Resource - The resource to arbitrate.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
|
|
if (Resource->Id == 0) {
|
|
//
|
|
// This resource was never fully created.
|
|
//
|
|
return(ERROR_RESOURCE_NOT_AVAILABLE);
|
|
}
|
|
try {
|
|
if (Resource->QuorumResource) {
|
|
status = MMSetQuorumOwner( NmGetNodeId(NmLocalNode), /* Block = */ TRUE, NULL );
|
|
}
|
|
if (status == ERROR_SUCCESS) {
|
|
status = RmArbitrateResource(Resource->Id);
|
|
if (status != ERROR_SUCCESS) {
|
|
if (Resource->QuorumResource) {
|
|
MMSetQuorumOwner( MM_INVALID_NODE , /* Block = */ FALSE, NULL );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
except( FmpRmExceptionFilter(GetExceptionCode()) ) {
|
|
|
|
DWORD status = GetExceptionCode();
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] RmArbitrateResource issued exception %1!u!\n",
|
|
status);
|
|
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // FmpRmArbitrateResource
|
|
|
|
|
|
|
|
DWORD
|
|
FmpRmReleaseResource(
|
|
IN PFM_RESOURCE Resource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Release arbitration on a given resource.
|
|
|
|
Arguments:
|
|
|
|
Resource - The resource to release.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
|
|
if (Resource->Id == 0) {
|
|
//
|
|
// This resource was never fully created.
|
|
//
|
|
return(ERROR_RESOURCE_NOT_AVAILABLE);
|
|
}
|
|
try {
|
|
status = RmReleaseResource(Resource->Id);
|
|
}
|
|
except( FmpRmExceptionFilter(GetExceptionCode()) ) {
|
|
|
|
DWORD status = GetExceptionCode();
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] RmReleaseResource issued exception %1!u!\n",
|
|
status);
|
|
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // FmpRmReleaseResource
|
|
|
|
|
|
|
|
DWORD
|
|
FmpRmFailResource(
|
|
IN PFM_RESOURCE Resource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Fail a given resource.
|
|
|
|
Arguments:
|
|
|
|
Resource - The resource to fail.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
if (Resource->QuorumResource) {
|
|
MMSetQuorumOwner( MM_INVALID_NODE, /* Block = */ FALSE, NULL );
|
|
}
|
|
return(RmFailResource(Resource->Id));
|
|
|
|
} // FmpRmFailResource
|
|
|
|
DWORD FmpRmLoadResTypeDll(
|
|
IN PFM_RESTYPE pResType
|
|
)
|
|
{
|
|
PRESMON monitor;
|
|
DWORD dwStatus;
|
|
LPWSTR pszDebugPrefix;
|
|
|
|
|
|
// Read the DebugControlFunction registry value.
|
|
//
|
|
|
|
if ( pResType->Flags & RESTYPE_DEBUG_CONTROL_FUNC ) {
|
|
if ( pResType->DebugPrefix != NULL ) {
|
|
pszDebugPrefix = pResType->DebugPrefix;
|
|
} else {
|
|
pszDebugPrefix = pResType->DebugPrefix;
|
|
}
|
|
monitor = FmpCreateMonitor(pszDebugPrefix, TRUE);
|
|
if (monitor == NULL) {
|
|
dwStatus = GetLastError();
|
|
goto FnExit;
|
|
}
|
|
} else {
|
|
CL_ASSERT(FmpDefaultMonitor != NULL);
|
|
monitor = FmpDefaultMonitor;
|
|
}
|
|
|
|
|
|
dwStatus = RmLoadResourceTypeDll(monitor->Binding, OmObjectId(pResType),
|
|
pResType->DllName);
|
|
|
|
|
|
if (dwStatus != ERROR_SUCCESS)
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] RmLoadResourceTypeDll call failed %1!u!\n",
|
|
dwStatus);
|
|
}
|
|
|
|
if ( pResType->Flags & RESTYPE_DEBUG_CONTROL_FUNC )
|
|
{
|
|
//
|
|
// Stop this resource monitor if it is a separate resource monitor.
|
|
//
|
|
CL_ASSERT( monitor->NotifyThread != NULL );
|
|
CL_ASSERT( monitor->Process != NULL );
|
|
|
|
FmpShutdownMonitor( monitor );
|
|
|
|
}
|
|
|
|
|
|
FnExit:
|
|
return(dwStatus);
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
FmpRmChangeResourceParams(
|
|
IN PFM_RESOURCE Resource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Tell the resource monitor to change parameters for the given resource.
|
|
|
|
Arguments:
|
|
|
|
Resource - The resource to change parameters.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
|
|
A Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpRmChangeResourceParams for resource <%1!ws!> called...\n",
|
|
OmObjectId(Resource));
|
|
|
|
return(RmChangeResourceParams(
|
|
Resource->Id,
|
|
Resource->LooksAlivePollInterval,
|
|
Resource->IsAlivePollInterval,
|
|
Resource->PendingTimeout ) );
|
|
|
|
} // FmpRmChangeResourceParams
|
|
|
|
|
|
|
|
DWORD
|
|
FmpRmResourceControl(
|
|
IN PFM_RESOURCE Resource,
|
|
IN DWORD ControlCode,
|
|
IN PUCHAR InBuffer,
|
|
IN DWORD InBufferSize,
|
|
OUT PUCHAR OutBuffer,
|
|
IN DWORD OutBufferSize,
|
|
OUT LPDWORD BytesReturned,
|
|
OUT LPDWORD Required
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Provides for arbitrary communication and control between an application
|
|
and a specific instance of a resource.
|
|
|
|
Arguments:
|
|
|
|
Resource - Supplies the resource to be controlled.
|
|
|
|
ControlCode- Supplies the control code that defines the
|
|
structure and action of the resource control.
|
|
Values of ControlCode between 0 and 0x10000000 are reserved
|
|
for future definition and use by Microsoft. All other values
|
|
are available for use by ISVs
|
|
|
|
InBuffer- Supplies a pointer to the input buffer to be passed
|
|
to the resource.
|
|
|
|
InBufferSize- Supplies the size, in bytes, of the data pointed
|
|
to by lpInBuffer..
|
|
|
|
OutBuffer- Supplies a pointer to the output buffer to be
|
|
filled in by the resource..
|
|
|
|
OutBufferSize- Supplies the size, in bytes, of the available
|
|
space pointed to by lpOutBuffer.
|
|
|
|
BytesReturned - Returns the number of bytes of lpOutBuffer
|
|
actually filled in by the resource..
|
|
|
|
Required - The number of bytes required if OutBuffer is not big enough.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
DWORD Dummy;
|
|
DWORD dwTmpBytesReturned;
|
|
DWORD dwTmpBytesRequired;
|
|
CLUSPROP_BUFFER_HELPER props;
|
|
DWORD bufSize;
|
|
|
|
CL_ASSERT( Resource->Group != NULL );
|
|
//
|
|
// Handle any requests that must be done without locks helds.
|
|
//
|
|
switch ( ControlCode ) {
|
|
|
|
case CLUSCTL_RESOURCE_GET_NAME:
|
|
if ( (Resource->Monitor == NULL) ||
|
|
(OmObjectName( Resource ) == NULL) ) {
|
|
return(ERROR_NOT_READY);
|
|
}
|
|
props.pb = OutBuffer;
|
|
bufSize = (lstrlenW( OmObjectName( Resource ) ) + 1) * sizeof(WCHAR);
|
|
if ( bufSize > OutBufferSize ) {
|
|
*Required = bufSize;
|
|
*BytesReturned = 0;
|
|
status = ERROR_MORE_DATA;
|
|
} else {
|
|
lstrcpyW( props.psz, OmObjectName( Resource ) );
|
|
*BytesReturned = bufSize;
|
|
*Required = 0;
|
|
status = ERROR_SUCCESS;
|
|
}
|
|
return(status);
|
|
|
|
case CLUSCTL_RESOURCE_GET_ID:
|
|
if ( (Resource->Monitor == NULL) ||
|
|
(OmObjectId( Resource ) == NULL) ) {
|
|
return(ERROR_NOT_READY);
|
|
}
|
|
props.pb = OutBuffer;
|
|
bufSize = (lstrlenW( OmObjectId( Resource ) ) + 1) * sizeof(WCHAR);
|
|
if ( bufSize > OutBufferSize ) {
|
|
*Required = bufSize;
|
|
*BytesReturned = 0;
|
|
status = ERROR_MORE_DATA;
|
|
} else {
|
|
lstrcpyW( props.psz, OmObjectId( Resource ) );
|
|
*BytesReturned = bufSize;
|
|
*Required = 0;
|
|
status = ERROR_SUCCESS;
|
|
}
|
|
return(status);
|
|
|
|
case CLUSCTL_RESOURCE_GET_RESOURCE_TYPE:
|
|
if ( (Resource->Monitor == NULL) ||
|
|
(OmObjectId( Resource->Type ) == NULL) ) {
|
|
return(ERROR_NOT_READY);
|
|
}
|
|
props.pb = OutBuffer;
|
|
bufSize = (lstrlenW( OmObjectId( Resource->Type ) ) + 1) * sizeof(WCHAR);
|
|
if ( bufSize > OutBufferSize ) {
|
|
*Required = bufSize;
|
|
*BytesReturned = 0;
|
|
status = ERROR_MORE_DATA;
|
|
} else {
|
|
lstrcpyW( props.psz, OmObjectId( Resource->Type ) );
|
|
*BytesReturned = bufSize;
|
|
*Required = 0;
|
|
status = ERROR_SUCCESS;
|
|
}
|
|
return(status);
|
|
|
|
case CLUSCTL_RESOURCE_ADD_REGISTRY_CHECKPOINT:
|
|
case CLUSCTL_RESOURCE_DELETE_REGISTRY_CHECKPOINT:
|
|
{
|
|
LPWSTR RegistryKey;
|
|
DWORD LastChar;
|
|
|
|
//
|
|
// Validate the input buffer
|
|
//
|
|
RegistryKey = (LPWSTR)InBuffer;
|
|
LastChar = (InBufferSize/sizeof(WCHAR)) - 1;
|
|
//
|
|
// If the length of the input buffer is zero, or not a integral
|
|
// number of WCHARs, or the last character is not NULL, the
|
|
// request is invalid.
|
|
//
|
|
if ((InBufferSize < sizeof(WCHAR)) ||
|
|
((InBufferSize % sizeof(WCHAR)) != 0) ||
|
|
(RegistryKey == NULL) ||
|
|
(RegistryKey[LastChar] != L'\0')) {
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
//
|
|
// If we are not the owner of this resource, don't let the set
|
|
// happen.
|
|
//
|
|
if (Resource->Group->OwnerNode != NmLocalNode) {
|
|
return(ERROR_HOST_NODE_NOT_RESOURCE_OWNER);
|
|
}
|
|
|
|
//
|
|
// Call the checkpoint manager to perform the change.
|
|
//
|
|
if (ControlCode == CLUSCTL_RESOURCE_ADD_REGISTRY_CHECKPOINT) {
|
|
status = CpAddRegistryCheckpoint(Resource, RegistryKey);
|
|
} else {
|
|
status = CpDeleteRegistryCheckpoint(Resource, RegistryKey);
|
|
}
|
|
}
|
|
*BytesReturned = 0;
|
|
return(status);
|
|
|
|
case CLUSCTL_RESOURCE_ADD_CRYPTO_CHECKPOINT:
|
|
case CLUSCTL_RESOURCE_DELETE_CRYPTO_CHECKPOINT:
|
|
{
|
|
//
|
|
// If we are not the owner of this resource, don't let the set
|
|
// happen.
|
|
//
|
|
if (Resource->Group->OwnerNode != NmLocalNode) {
|
|
return(ERROR_HOST_NODE_NOT_RESOURCE_OWNER);
|
|
}
|
|
|
|
//
|
|
// Call the checkpoint manager to perform the change.
|
|
//
|
|
if (ControlCode == CLUSCTL_RESOURCE_ADD_CRYPTO_CHECKPOINT) {
|
|
status = CpckAddCryptoCheckpoint(Resource, InBuffer, InBufferSize);
|
|
} else {
|
|
status = CpckDeleteCryptoCheckpoint(Resource, InBuffer, InBufferSize);
|
|
}
|
|
}
|
|
*BytesReturned = 0;
|
|
return(status);
|
|
|
|
case CLUSCTL_RESOURCE_GET_REGISTRY_CHECKPOINTS:
|
|
//
|
|
// Call the checkpoint manager to retrieve the list of checkpoints
|
|
//
|
|
status = CpGetRegistryCheckpoints(Resource,
|
|
OutBuffer,
|
|
OutBufferSize,
|
|
BytesReturned,
|
|
Required);
|
|
return(status);
|
|
|
|
case CLUSCTL_RESOURCE_GET_CRYPTO_CHECKPOINTS:
|
|
//
|
|
// Call the checkpoint manager to retrieve the list of checkpoints
|
|
//
|
|
status = CpckGetCryptoCheckpoints(Resource,
|
|
OutBuffer,
|
|
OutBufferSize,
|
|
BytesReturned,
|
|
Required);
|
|
return(status);
|
|
|
|
case CLUSCTL_RESOURCE_UPGRADE_DLL:
|
|
status = FmpUpgradeResourceDLL(Resource,
|
|
( LPWSTR ) InBuffer);
|
|
return(status);
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
OmReferenceObject( Resource );
|
|
|
|
FmpAcquireLocalResourceLock( Resource );
|
|
|
|
//if the resource has been marked for delete, then fail this call
|
|
if (!IS_VALID_FM_RESOURCE(Resource))
|
|
{
|
|
status = ERROR_RESOURCE_NOT_AVAILABLE;
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
goto FnExit;
|
|
}
|
|
|
|
if ( Resource->Monitor == NULL ) {
|
|
status = FmpInitializeResource( Resource, TRUE );
|
|
if ( status != ERROR_SUCCESS ) {
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
goto FnExit;
|
|
}
|
|
}
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
|
|
//to take care of the output reference pointer which cannot be NULL.
|
|
if (!OutBuffer)
|
|
{
|
|
OutBuffer = (PUCHAR)&Dummy;
|
|
OutBufferSize = 0;
|
|
}
|
|
if (!BytesReturned)
|
|
BytesReturned = &dwTmpBytesReturned;
|
|
if (!Required)
|
|
Required = &dwTmpBytesRequired;
|
|
|
|
try {
|
|
status = RmResourceControl(Resource->Id,
|
|
ControlCode,
|
|
InBuffer,
|
|
InBufferSize,
|
|
OutBuffer,
|
|
OutBufferSize,
|
|
BytesReturned,
|
|
Required
|
|
);
|
|
}
|
|
except( FmpRmExceptionFilter(GetExceptionCode()) ) {
|
|
status = GetExceptionCode();
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] RmResourceControl issued exception %1!u!\n",
|
|
status);
|
|
}
|
|
|
|
if ( ( status != ERROR_SUCCESS ) &&
|
|
( status != ERROR_MORE_DATA ) &&
|
|
( status != ERROR_INVALID_FUNCTION ) )
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpRmResourceControl: RmResourceControl returned %1!u! for resource %2!ws!, resid=%3!u!...\n",
|
|
status,
|
|
OmObjectId(Resource),
|
|
Resource->Id);
|
|
}
|
|
|
|
//for core resource we may need special handling
|
|
if ((status == ERROR_SUCCESS) || (status == ERROR_RESOURCE_PROPERTIES_STORED))
|
|
{
|
|
DWORD dwPostProcessStatus;
|
|
|
|
dwPostProcessStatus = FmpPostProcessResourceControl( Resource,
|
|
ControlCode,
|
|
InBuffer,
|
|
InBufferSize,
|
|
OutBuffer,
|
|
OutBufferSize,
|
|
BytesReturned,
|
|
Required );
|
|
if ( dwPostProcessStatus != ERROR_SUCCESS ) status = dwPostProcessStatus;
|
|
}
|
|
|
|
if ( ((status == ERROR_SUCCESS) ||
|
|
(status == ERROR_RESOURCE_PROPERTIES_STORED)) &&
|
|
(ControlCode & CLCTL_MODIFY_MASK) ) {
|
|
|
|
//
|
|
// We cannot just broadcast a cluster wide event... which is what
|
|
// we want to do. Unfortunately, this code path can be activated
|
|
// from within a GUM call, and we cannot call GUM back until we
|
|
// have dispatched the current event.
|
|
//
|
|
|
|
//
|
|
// Reference the resource object to keep it around while we
|
|
// perform the post notification. The dereference must occur
|
|
// in the post routine after the event posting.
|
|
//
|
|
OmReferenceObject( Resource );
|
|
|
|
FmpPostWorkItem( FM_EVENT_RESOURCE_PROPERTY_CHANGE,
|
|
Resource,
|
|
0 );
|
|
}
|
|
|
|
FnExit:
|
|
OmDereferenceObject( Resource );
|
|
//FmpReleaseLocalResourceLock( Resource );
|
|
return(status);
|
|
|
|
} // FmpRmResourceControl
|
|
|
|
|
|
DWORD
|
|
FmpRmResourceTypeControl(
|
|
IN LPCWSTR ResourceTypeName,
|
|
IN DWORD ControlCode,
|
|
IN PUCHAR InBuffer,
|
|
IN DWORD InBufferSize,
|
|
OUT PUCHAR OutBuffer,
|
|
IN DWORD OutBufferSize,
|
|
OUT LPDWORD BytesReturned,
|
|
OUT LPDWORD Required
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Provides for arbitrary communication and control between an application
|
|
and a specific instance of a resource type.
|
|
|
|
Arguments:
|
|
|
|
ResourceTypeName - Supplies the name of the resource type to be
|
|
controlled.
|
|
|
|
ControlCode- Supplies the control code that defines the
|
|
structure and action of the resource control.
|
|
Values of dwControlCode between 0 and 0x10000000 are reserved
|
|
for future definition and use by Microsoft. All other values
|
|
are available for use by ISVs
|
|
|
|
InBuffer- Supplies a pointer to the input buffer to be passed
|
|
to the resource.
|
|
|
|
InBufferSize- Supplies the size, in bytes, of the data pointed
|
|
to by lpInBuffer..
|
|
|
|
OutBuffer- Supplies a pointer to the output buffer to be
|
|
filled in by the resource..
|
|
|
|
OutBufferSize- Supplies the size, in bytes, of the available
|
|
space pointed to by lpOutBuffer.
|
|
|
|
BytesReturned - Returns the number of bytes of lpOutBuffer
|
|
actually filled in by the resource..
|
|
|
|
Required - The number of bytes required if OutBuffer is not big enough.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
PRESMON monitor;
|
|
PFM_RESTYPE type = NULL;
|
|
LPWSTR debugPrefix;
|
|
DWORD Dummy;
|
|
DWORD dwTmpBytesReturned;
|
|
DWORD dwTmpBytesRequired;
|
|
|
|
|
|
//
|
|
// Find the resource type structure associated with this resource type name
|
|
//
|
|
OmEnumObjects( ObjectTypeResType,
|
|
FmpReturnResourceType,
|
|
&type,
|
|
(PVOID)ResourceTypeName );
|
|
if ( type == NULL ) {
|
|
return(ERROR_CLUSTER_RESOURCE_TYPE_NOT_FOUND);
|
|
}
|
|
|
|
//
|
|
// Read the DebugControlFunction registry value.
|
|
//
|
|
|
|
if ( type->Flags & RESTYPE_DEBUG_CONTROL_FUNC ) {
|
|
if ( type->DebugPrefix != NULL ) {
|
|
debugPrefix = type->DebugPrefix;
|
|
} else {
|
|
debugPrefix = type->DebugPrefix;
|
|
}
|
|
monitor = FmpCreateMonitor(debugPrefix, TRUE);
|
|
if (monitor == NULL) {
|
|
return(GetLastError());
|
|
}
|
|
} else {
|
|
CL_ASSERT(FmpDefaultMonitor != NULL);
|
|
monitor = FmpDefaultMonitor;
|
|
}
|
|
|
|
//to take care of the output reference pointer which cannot be NULL.
|
|
if (!OutBuffer)
|
|
{
|
|
OutBuffer = (PUCHAR)&Dummy;
|
|
OutBufferSize = 0;
|
|
}
|
|
if (!BytesReturned)
|
|
BytesReturned = &dwTmpBytesReturned;
|
|
if (!Required)
|
|
Required = &dwTmpBytesRequired;
|
|
|
|
|
|
|
|
try {
|
|
status = RmResourceTypeControl(monitor->Binding,
|
|
ResourceTypeName,
|
|
type->DllName,
|
|
ControlCode,
|
|
InBuffer,
|
|
InBufferSize,
|
|
OutBuffer,
|
|
OutBufferSize,
|
|
BytesReturned,
|
|
Required
|
|
);
|
|
}
|
|
except( FmpRmExceptionFilter(GetExceptionCode()) ) {
|
|
status = GetExceptionCode();
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] RmResourceTypeControl issued exception %1!u!\n",
|
|
status);
|
|
}
|
|
|
|
if ( type->Flags & RESTYPE_DEBUG_CONTROL_FUNC ) {
|
|
//
|
|
// Stop this resource monitor if it is a separate resource monitor.
|
|
//
|
|
CL_ASSERT( monitor->NotifyThread != NULL );
|
|
CL_ASSERT( monitor->Process != NULL );
|
|
|
|
FmpShutdownMonitor( monitor );
|
|
|
|
}
|
|
|
|
//
|
|
// If we successfully processed this request then re-fetch any changed
|
|
// data items.
|
|
//
|
|
if ( (status == ERROR_SUCCESS ||
|
|
(status == ERROR_RESOURCE_PROPERTIES_STORED)) &&
|
|
(ControlCode & CLCTL_MODIFY_MASK) ) {
|
|
FmpHandleResourceTypeControl( type,
|
|
ControlCode,
|
|
InBuffer,
|
|
InBufferSize,
|
|
OutBuffer,
|
|
OutBufferSize,
|
|
BytesReturned,
|
|
Required );
|
|
// ignore status
|
|
}
|
|
OmDereferenceObject(type);
|
|
|
|
return(status);
|
|
|
|
} // FmpRmResourceTypeControl
|
|
|
|
|
|
|
|
|
|
/****
|
|
@func BOOL | FmpPostProcessResourceControl| For core resource, if the control
|
|
code is handled successfully by the resource dll, the fm handles
|
|
any special handling in this function.
|
|
|
|
@parm PFM_RESOURCE | Resource | Supplies the resource to be controlled.
|
|
|
|
@parm DWORD| ControlCode | Supplies the control code that defines the
|
|
structure and action of the resource control.
|
|
Values of ControlCode between 0 and 0x10000000 are reserved
|
|
for future definition and use by Microsoft. All other values
|
|
are available for use by ISVs
|
|
|
|
@parm PUCHAR | InBuffer | Supplies a pointer to the input buffer to be passed
|
|
to the resource.
|
|
|
|
@parm DWORD | InBufferSize | Supplies the size, in bytes, of the data pointed
|
|
to by lpInBuffer..
|
|
|
|
@parm PUCHAR | OutBuffer | Supplies a pointer to the output buffer to be
|
|
filled in by the resource..
|
|
|
|
@parm DWORD | OutBufferSize | Supplies the size, in bytes, of the available
|
|
space pointed to by lpOutBuffer.
|
|
|
|
@parm LPDWORD | BytesReturned | Returns the number of bytes of lpOutBuffer
|
|
actually filled in by the resource..
|
|
|
|
@parm LPDWORD | Required | The number of bytes required if OutBuffer is not big enough.
|
|
|
|
|
|
@comm Called only for core resources.
|
|
|
|
@xref
|
|
****/
|
|
|
|
DWORD
|
|
FmpPostProcessResourceControl(
|
|
IN PFM_RESOURCE Resource,
|
|
IN DWORD ControlCode,
|
|
IN PUCHAR InBuffer,
|
|
IN DWORD InBufferSize,
|
|
OUT PUCHAR OutBuffer,
|
|
IN DWORD OutBufferSize,
|
|
OUT LPDWORD BytesReturned,
|
|
OUT LPDWORD Required
|
|
)
|
|
{
|
|
DWORD dwStatus=ERROR_SUCCESS;
|
|
|
|
//handle cluster name change
|
|
switch(ControlCode)
|
|
{
|
|
case CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES:
|
|
{
|
|
LPWSTR pszClusterName=NULL;
|
|
PFM_RESTYPE pResType;
|
|
|
|
//need to check this only for core resources
|
|
if (Resource->ExFlags & CLUS_FLAG_CORE)
|
|
{
|
|
pResType = Resource->Type;
|
|
//SS: chk follow the name
|
|
if (!lstrcmpiW(OmObjectId(pResType), CLUS_RESTYPE_NAME_NETNAME))
|
|
{
|
|
dwStatus = FmNetNameParseProperties(InBuffer, InBufferSize,
|
|
&pszClusterName);
|
|
if (dwStatus == ERROR_SUCCESS && pszClusterName)
|
|
{
|
|
dwStatus = FmpRegUpdateClusterName(pszClusterName);
|
|
LocalFree(pszClusterName);
|
|
} else if ( dwStatus == ERROR_FILE_NOT_FOUND ) {
|
|
dwStatus = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CLUSCTL_RESOURCE_GET_CHARACTERISTICS:
|
|
{
|
|
LPDWORD pdwCharacteristics = ( LPDWORD ) OutBuffer;
|
|
//
|
|
// If the resource has dependencies, remove the quorum capable flag
|
|
//
|
|
if ( ( pdwCharacteristics != NULL ) &&
|
|
( ( *BytesReturned ) == sizeof ( DWORD ) ) &&
|
|
( ( *pdwCharacteristics ) & ( CLUS_CHAR_QUORUM ) ) )
|
|
{
|
|
FmpAcquireLocalResourceLock( Resource );
|
|
//
|
|
// The resource says it is quorum capable, however it has a dependency so it
|
|
// cant be a quorum.
|
|
//
|
|
if ( !IsListEmpty( &Resource->DependsOn ) )
|
|
{
|
|
//
|
|
// We will mask the quorum capable bit
|
|
//
|
|
*pdwCharacteristics = ( *pdwCharacteristics ) & ( ~CLUS_CHAR_QUORUM );
|
|
}
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return(dwStatus);
|
|
}
|