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.
3773 lines
106 KiB
3773 lines
106 KiB
/*++
|
|
|
|
Copyright (c) 1996-1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
fmapi.c
|
|
|
|
Abstract:
|
|
|
|
Cluster manager api service routines.
|
|
|
|
Author:
|
|
|
|
Rod Gamache (rodga) 8-Mar-1996
|
|
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "fmp.h"
|
|
|
|
#define LOG_MODULE FMAPI
|
|
|
|
//
|
|
// Local Functions
|
|
//
|
|
DWORD
|
|
FmpCanonicalizePath(
|
|
IN OUT LPWSTR lpszPath,
|
|
OUT PBOOL pfIsPathUNC
|
|
);
|
|
|
|
|
|
//
|
|
// Functions Exported to the rest of the Cluster Manager
|
|
//
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////
|
|
//
|
|
// Group management functions.
|
|
//
|
|
////////////////////////////////////////////////////////
|
|
|
|
DWORD
|
|
WINAPI
|
|
FmOnlineGroup(
|
|
IN PFM_GROUP Group
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Bring the specified group online. This means bringing all of the
|
|
individual resources contained within the group online. This is an
|
|
atomic operation - so either all resources contained within the group
|
|
are brought online, or none of them are.
|
|
|
|
Arguments:
|
|
|
|
Group - Supplies a pointer to the group structure to bring online.
|
|
|
|
Retruns:
|
|
|
|
ERROR_SUCCESS if the request was successful.
|
|
|
|
A Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
PLIST_ENTRY listEntry;
|
|
|
|
FmpMustBeOnline( );
|
|
|
|
FmpAcquireLocalGroupLock( Group );
|
|
|
|
//if the group has been marked for delete, then fail this call
|
|
if (!IS_VALID_FM_GROUP(Group))
|
|
{
|
|
FmpReleaseLocalGroupLock( Group);
|
|
return (ERROR_GROUP_NOT_AVAILABLE);
|
|
}
|
|
|
|
//
|
|
// Make sure the owning node is not paused.
|
|
//
|
|
if (NmGetNodeState(Group->OwnerNode) == ClusterNodePaused) {
|
|
FmpReleaseLocalGroupLock( Group );
|
|
return(ERROR_SHARING_PAUSED);
|
|
}
|
|
|
|
//
|
|
// Check if we are the owner... if not, ship the request off someplace
|
|
// else.
|
|
//
|
|
if ( Group->OwnerNode != NmLocalNode ) {
|
|
FmpReleaseLocalGroupLock( Group );
|
|
return(FmcOnlineGroupRequest(Group));
|
|
}
|
|
|
|
//
|
|
// Set the PersistentState for this Group - the PersistentState is persistent.
|
|
//
|
|
FmpSetGroupPersistentState( Group, ClusterGroupOnline );
|
|
|
|
status = FmpOnlineGroup( Group, TRUE );
|
|
|
|
FmpReleaseLocalGroupLock( Group );
|
|
|
|
return(status);
|
|
|
|
} // FmOnlineGroup
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
FmOfflineGroup(
|
|
IN PFM_GROUP Group
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Bring the specified group offline. This means bringing all of the
|
|
individual resources contained within the group offline.
|
|
|
|
Arguments:
|
|
|
|
Group - Supplies a pointer to the group structure to bring offline.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS if the request was successful.
|
|
|
|
A Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
PLIST_ENTRY listEntry;
|
|
PFM_RESOURCE Resource;
|
|
|
|
FmpMustBeOnline( );
|
|
|
|
//
|
|
// Check if we are the owner... if not, ship the request off to some
|
|
// other place.
|
|
//
|
|
|
|
if ( Group->OwnerNode != NmLocalNode ) {
|
|
return(FmcOfflineGroupRequest(Group));
|
|
}
|
|
|
|
//
|
|
// Set the PersistentState for this Group - the PersistentState is persistent.
|
|
//
|
|
FmpSetGroupPersistentState( Group, ClusterGroupOffline );
|
|
|
|
status = FmpOfflineGroup( Group, FALSE, TRUE);
|
|
|
|
|
|
return(status);
|
|
|
|
} // FmOfflineGroup
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
FmMoveGroup(
|
|
IN PFM_GROUP Group,
|
|
IN PNM_NODE DestinationNode OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Failover the specified Group. This means taking all of the individual
|
|
resources contained within the group offline and requesting the
|
|
DestinationNode to bring the Group Online.
|
|
|
|
Arguments:
|
|
|
|
Group - Supplies a pointer to the group structure to move.
|
|
|
|
DestinationNode - Supplies the node object to move the group to. If not
|
|
present, then move it to THE OTHER node.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS if the request was successful.
|
|
|
|
A Win32 error code on failure.
|
|
|
|
Notes:
|
|
|
|
The Group may or may not be online on the DestinationNode, depending on
|
|
whether the online request succeeded. This means that the status return
|
|
is merely the status return for the Online request for the DestinationNode.
|
|
|
|
--*/
|
|
|
|
{
|
|
FmpMustBeOnline( );
|
|
|
|
return(FmpDoMoveGroup( Group, DestinationNode, TRUE ));
|
|
|
|
} // FmMoveGroup
|
|
|
|
|
|
|
|
PFM_GROUP
|
|
WINAPI
|
|
FmCreateGroup(
|
|
IN LPWSTR GroupId,
|
|
IN LPCWSTR GroupName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create the specified GroupId. This requires verifying that the
|
|
specified GroupId does not already exist and then creating an
|
|
empty Group container into which resources can be added.
|
|
|
|
Note that the returned PFM_GROUP will have already been referenced.
|
|
This prevents somebody from deleting the group before the caller
|
|
gets a chance to reference it.
|
|
|
|
Arguments:
|
|
|
|
GroupId - Supplies the Id of the Group to create.
|
|
|
|
GroupName - Supplies the 'user-friendly' name of the Group.
|
|
|
|
Returns:
|
|
|
|
Pointer to the newly created group if successful.
|
|
|
|
NULL if unsuccessful. GetLastError() will return the specific error.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Status;
|
|
PFM_GROUP Group;
|
|
PGUM_CREATE_GROUP GumGroup;
|
|
DWORD BufSize;
|
|
DWORD GroupIdLen;
|
|
DWORD GroupNameLen;
|
|
|
|
FmpMustBeOnlineEx( NULL );
|
|
|
|
//
|
|
// Allocate a message buffer.
|
|
//
|
|
GroupIdLen = (lstrlenW(GroupId)+1)*sizeof(WCHAR);
|
|
GroupNameLen = (lstrlenW(GroupName)+1)*sizeof(WCHAR);
|
|
BufSize = sizeof(GUM_CREATE_GROUP) - sizeof(WCHAR) + GroupIdLen +
|
|
GroupNameLen + (lstrlenW( OmObjectId(NmLocalNode) ) + 1) * sizeof(WCHAR);
|
|
GumGroup = LocalAlloc(LMEM_FIXED, BufSize);
|
|
if (GumGroup == NULL) {
|
|
ClRtlLogPrint(LOG_CRITICAL, "[FM] FmCreateGroup: Unable to alloc mem for group <%1!ws!>....\n",
|
|
GroupName);
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Fill in message buffer.
|
|
//
|
|
GumGroup->Group = NULL;
|
|
GumGroup->GroupIdLen = GroupIdLen;
|
|
GumGroup->GroupNameLen = GroupNameLen;
|
|
wcscpy(GumGroup->GroupId, GroupId);
|
|
CopyMemory((PCHAR)GumGroup->GroupId + GroupIdLen,
|
|
GroupName,
|
|
GroupNameLen);
|
|
CopyMemory((PCHAR)GumGroup->GroupId + GroupIdLen + GroupNameLen,
|
|
OmObjectId(NmLocalNode),
|
|
(lstrlenW( OmObjectId(NmLocalNode) ) + 1) * sizeof(WCHAR));
|
|
|
|
//
|
|
// Send message.
|
|
//
|
|
Status = GumSendUpdate(GumUpdateFailoverManager,
|
|
FmUpdateCreateGroup,
|
|
BufSize,
|
|
GumGroup);
|
|
|
|
if ((GumGroup->Group == NULL) && (FmpShutdown)) {
|
|
Status = ERROR_CLUSTER_NODE_SHUTTING_DOWN;
|
|
}
|
|
|
|
if (Status != ERROR_SUCCESS) {
|
|
LocalFree(GumGroup);
|
|
SetLastError(Status);
|
|
return(NULL);
|
|
}
|
|
|
|
Group = GumGroup->Group;
|
|
CL_ASSERT(Group != NULL);
|
|
LocalFree(GumGroup);
|
|
return(Group);
|
|
} // FmCreateGroup
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
FmDeleteGroup(
|
|
IN PFM_GROUP pGroup
|
|
)
|
|
|
|
/*--
|
|
|
|
Routine Description:
|
|
|
|
Delete the specified Group. This means verifying that the specified
|
|
Group does not contain any resources (resources must be removed
|
|
by a separate call to remove the resources), and then deleting the
|
|
Group.
|
|
|
|
Arguments:
|
|
|
|
Group - Supplies the Group to delete.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS if the request was successful.
|
|
|
|
A Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwStatus;
|
|
|
|
FmpMustBeOnline( );
|
|
|
|
FmpAcquireLocalGroupLock( pGroup );
|
|
|
|
if (pGroup->OwnerNode == NmLocalNode)
|
|
{
|
|
dwStatus = FmpDeleteGroup(pGroup);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// FmcDeleteGroup releases the group lock
|
|
//
|
|
dwStatus = FmcDeleteGroupRequest(pGroup);
|
|
goto FnExit;
|
|
}
|
|
|
|
FmpReleaseLocalGroupLock(pGroup);
|
|
|
|
FnExit:
|
|
return(dwStatus);
|
|
|
|
} // FmDeleteGroup
|
|
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
FmSetGroupName(
|
|
IN PFM_GROUP Group,
|
|
IN LPCWSTR FriendlyName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the user-friendly name for the specified Group.
|
|
|
|
Note that the Group must have already been created. It is also
|
|
assumed that the caller of this routine (the cluster API) has already
|
|
verified that the name is NOT a duplicate.
|
|
|
|
Arguments:
|
|
|
|
Group - Supplies the Group to enter a new name.
|
|
|
|
FriendlyName - Supplies the user-friendly name for the resource.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
|
|
A Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
LPCWSTR GroupId;
|
|
DWORD Status;
|
|
|
|
GroupId = OmObjectId(Group);
|
|
|
|
Status = GumSendUpdateEx(GumUpdateFailoverManager,
|
|
FmUpdateChangeGroupName,
|
|
2,
|
|
(lstrlenW(GroupId)+1)*sizeof(WCHAR),
|
|
GroupId,
|
|
(lstrlenW(FriendlyName)+1)*sizeof(WCHAR),
|
|
FriendlyName);
|
|
return(Status);
|
|
|
|
} // FmSetGroupName
|
|
|
|
|
|
|
|
CLUSTER_GROUP_STATE
|
|
WINAPI
|
|
FmGetGroupState(
|
|
IN PFM_GROUP Group,
|
|
OUT LPWSTR NodeName,
|
|
IN OUT PDWORD NameLength OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the current state for the specified Group. The Group state
|
|
consists of state of the group, along with the current node that is
|
|
hosting the Group (if the state of the Group is anything but
|
|
ClusterGroupOffline.
|
|
|
|
Arguments:
|
|
|
|
Group - Supplies the group object to get the state.
|
|
|
|
NodeName - Supplies a pointer to a buffer into which the name of
|
|
the node in the cluster the specified Group is currently hosted.
|
|
This field can be NULL, if NameLength is zero.
|
|
|
|
NameLength - Supplies a pointer to a DWORD containing the number of
|
|
characters available to the NodeName buffer (including the terminating
|
|
NULL character. On return, it is the number of characters written
|
|
into the NodeName buffer not including the NULL character.
|
|
|
|
Returns:
|
|
|
|
Returns the current state of the group:
|
|
|
|
ClusterGroupOnline
|
|
ClusterGroupOffline
|
|
ClusterGroupPending
|
|
ClusterGroupPartialOnline
|
|
ClusterGroupFailed
|
|
|
|
If the function fails, then the return value is ClusterGroupStateUnknown.
|
|
|
|
--*/
|
|
|
|
{
|
|
CLUSTER_GROUP_STATE state;
|
|
DWORD nameLength=0;
|
|
DWORD length;
|
|
|
|
if ( ARGUMENT_PRESENT( NameLength ) ) {
|
|
nameLength = *NameLength;
|
|
*NodeName = (WCHAR)0;
|
|
*NameLength = 0;
|
|
}
|
|
|
|
FmpMustBeOnlineEx( ClusterGroupStateUnknown );
|
|
|
|
FmpAcquireLocalGroupLock( Group );
|
|
|
|
//if the group has been marked for delete, then fail this call
|
|
if (!IS_VALID_FM_GROUP(Group))
|
|
{
|
|
FmpReleaseLocalGroupLock( Group);
|
|
return (ERROR_GROUP_NOT_AVAILABLE);
|
|
}
|
|
|
|
//
|
|
// Check if the OwnerNodes exists
|
|
//
|
|
// SS: dont filter out the node if it not in the preferred list
|
|
// how is the poor user going to know who the current owner is??
|
|
if (Group->OwnerNode != NULL) {
|
|
//
|
|
// The Group is 'owned' by some system
|
|
//
|
|
if ( ARGUMENT_PRESENT( NameLength ) ) {
|
|
length = lstrlenW( OmObjectName(Group->OwnerNode) ) + 1;
|
|
if ( nameLength < length ) {
|
|
length = nameLength;
|
|
}
|
|
lstrcpynW( NodeName, OmObjectName(Group->OwnerNode), length );
|
|
*NameLength = length;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the group state which is not normalized
|
|
//
|
|
state = FmpGetGroupState( Group, FALSE );
|
|
|
|
FmpReleaseLocalGroupLock( Group );
|
|
|
|
if ( state == ClusterGroupStateUnknown ) {
|
|
SetLastError(ERROR_INVALID_STATE);
|
|
}
|
|
|
|
return(state);
|
|
|
|
} // FmGetGroupState
|
|
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
FmEnumerateGroupResources(
|
|
IN PFM_GROUP Group,
|
|
IN FM_ENUM_GROUP_RESOURCE_ROUTINE EnumerationRoutine,
|
|
IN PVOID Context1,
|
|
IN PVOID Context2
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enumerate all the resources in a group.
|
|
|
|
Arguments:
|
|
|
|
Group - Supplies the group which must be enumerated.
|
|
|
|
EnumerationRoutine - The enumeration function.
|
|
|
|
Context1 - The enumeration list (allocated by the caller).
|
|
|
|
Context2 - Size of the enumerated list.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS on success.
|
|
|
|
A Win32 error code otherwise.
|
|
|
|
Comments:
|
|
|
|
This function executes only when the FM is fully online.
|
|
|
|
--*/
|
|
{
|
|
FmpMustBeOnline();
|
|
|
|
FmpEnumerateGroupResources( Group,
|
|
EnumerationRoutine,
|
|
Context1,
|
|
Context2 );
|
|
|
|
return(ERROR_SUCCESS);
|
|
} // FmEnumerateGroupResources
|
|
|
|
DWORD
|
|
FmpEnumerateGroupResources(
|
|
IN PFM_GROUP pGroup,
|
|
IN FM_ENUM_GROUP_RESOURCE_ROUTINE pfnEnumerationRoutine,
|
|
IN PVOID pContext1,
|
|
IN PVOID pContext2
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enumerate all the resources in a group.
|
|
|
|
Arguments:
|
|
|
|
pGroup - Supplies the group which must be enumerated.
|
|
|
|
pfnEnumerationRoutine - The enumeration function.
|
|
|
|
pContext1 - The enumeration list (allocated by the caller).
|
|
|
|
pContext2 - Size of the enumerated list.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS.
|
|
|
|
Comments:
|
|
|
|
This function executes even when the FM is not fully online. This is
|
|
necessary for a joining node to query the resource states while the
|
|
owner node of the group is shutting down.
|
|
|
|
--*/
|
|
{
|
|
PFM_RESOURCE pResource;
|
|
PLIST_ENTRY pListEntry;
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpEnumerateGroupResources: Entry for group <%1!ws!>....\n",
|
|
OmObjectId(pGroup));
|
|
|
|
FmpAcquireLocalGroupLock( pGroup );
|
|
|
|
//
|
|
// If the group has been marked for delete, then fail this call
|
|
//
|
|
if ( !IS_VALID_FM_GROUP( pGroup ) )
|
|
{
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[FM] FmpEnumerateGroupResources: Group <%1!ws!> marked for deletion....\n",
|
|
OmObjectId(pGroup));
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Run through contains list, then find all resources under that tree.
|
|
//
|
|
for ( pListEntry = pGroup->Contains.Flink;
|
|
pListEntry != &(pGroup->Contains);
|
|
pListEntry = pListEntry->Flink )
|
|
{
|
|
pResource = CONTAINING_RECORD( pListEntry,
|
|
FM_RESOURCE,
|
|
ContainsLinkage );
|
|
|
|
if ( !pfnEnumerationRoutine( pContext1,
|
|
pContext2,
|
|
pResource,
|
|
OmObjectId( pResource ) ) )
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[FM] FmpEnumerateGroupResources: Enumeration routine for group <%1!ws!> fails....\n",
|
|
OmObjectId(pGroup));
|
|
break;
|
|
}
|
|
}
|
|
|
|
FnExit:
|
|
FmpReleaseLocalGroupLock( pGroup );
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpEnumerateGroupResources: Exit for group <%1!ws!>....\n",
|
|
OmObjectId(pGroup));
|
|
return( ERROR_SUCCESS );
|
|
} // FmpEnumerateGroupResources
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////
|
|
//
|
|
// Resource management functions.
|
|
//
|
|
////////////////////////////////////////////////////////
|
|
|
|
PFM_RESOURCE
|
|
WINAPI
|
|
FmCreateResource(
|
|
IN PFM_GROUP Group,
|
|
IN LPWSTR ResourceId,
|
|
IN LPCWSTR ResourceName,
|
|
IN LPCWSTR ResourceType,
|
|
IN DWORD dwFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create the specified resource.
|
|
|
|
Note that the returned PFM_RESOURCE will have already been referenced.
|
|
This prevents somebody from deleting the resource before the caller
|
|
gets a chance to reference it.
|
|
|
|
Arguments:
|
|
|
|
Group - Supplies the group in which this resource belongs.
|
|
|
|
ResourceId - Supplies the Id of the resource to create.
|
|
|
|
ResourceName - Supplies the 'user-friendly' name of the resource.
|
|
|
|
ResourceType - Supplies the 'user-friendly' name of the resource type.
|
|
|
|
dwFlags - The flags for the resource.
|
|
|
|
Returns:
|
|
|
|
Pointer to the newly created resource if successful.
|
|
|
|
NULL if unsuccessful. GetLastError() will return the specific error.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Status;
|
|
PFM_RESOURCE Resource;
|
|
LPCWSTR GroupId;
|
|
PGUM_CREATE_RESOURCE GumResource;
|
|
DWORD GroupIdLen;
|
|
DWORD ResourceIdLen;
|
|
DWORD ResourceNameLen;
|
|
DWORD ResourceTypeLen;
|
|
DWORD BufSize;
|
|
HDMKEY ResourceKey;
|
|
HDMKEY ParamsKey;
|
|
DWORD Disposition;
|
|
|
|
FmpMustBeOnlineEx( NULL );
|
|
|
|
FmpAcquireLocalGroupLock( Group );
|
|
|
|
//
|
|
// If we own the group then we can issue the Gum request to create
|
|
// the resource. Otherwise, request the owner to initiate the request.
|
|
//
|
|
if ( Group->OwnerNode == NmLocalNode ) {
|
|
//
|
|
// Allocate a message buffer.
|
|
//
|
|
GroupId = OmObjectId(Group);
|
|
GroupIdLen = (lstrlenW(GroupId)+1) * sizeof(WCHAR);
|
|
ResourceIdLen = (lstrlenW(ResourceId)+1) * sizeof(WCHAR);
|
|
ResourceNameLen = (lstrlenW(ResourceName)+1) * sizeof(WCHAR);
|
|
ResourceTypeLen = (lstrlenW(ResourceType)+1) * sizeof(WCHAR);
|
|
BufSize = sizeof(GUM_CREATE_RESOURCE) - sizeof(WCHAR) +
|
|
GroupIdLen + ResourceIdLen + ResourceNameLen + ResourceTypeLen + 2 * sizeof( DWORD );
|
|
GumResource = LocalAlloc(LMEM_FIXED, BufSize);
|
|
if (GumResource == NULL) {
|
|
ClRtlLogPrint(LOG_CRITICAL, "[FM] FmCreateResource: Unable to allocate memory for resource <%1!ws!>....\n",
|
|
ResourceName);
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Fill in message buffer.
|
|
//
|
|
GumResource->Resource = NULL;
|
|
GumResource->GroupIdLen = GroupIdLen;
|
|
GumResource->ResourceIdLen = ResourceIdLen;
|
|
CopyMemory(GumResource->GroupId, GroupId, GroupIdLen);
|
|
CopyMemory((PCHAR)GumResource->GroupId + GroupIdLen,
|
|
ResourceId,
|
|
ResourceIdLen);
|
|
CopyMemory((PCHAR)GumResource->GroupId + GroupIdLen + ResourceIdLen,
|
|
ResourceName,
|
|
ResourceNameLen);
|
|
|
|
CopyMemory((PCHAR)GumResource->GroupId + GroupIdLen + ResourceIdLen + ResourceNameLen,
|
|
&ResourceTypeLen,
|
|
sizeof( DWORD ) );
|
|
|
|
CopyMemory((PCHAR)GumResource->GroupId + GroupIdLen + ResourceIdLen + ResourceNameLen + sizeof( DWORD ),
|
|
ResourceType,
|
|
ResourceTypeLen );
|
|
|
|
CopyMemory((PCHAR)GumResource->GroupId + GroupIdLen + ResourceIdLen + ResourceNameLen + sizeof( DWORD ) + ResourceTypeLen,
|
|
&dwFlags,
|
|
sizeof( DWORD ) );
|
|
|
|
//
|
|
// Send message.
|
|
//
|
|
Status = GumSendUpdate(GumUpdateFailoverManager,
|
|
FmUpdateCreateResource,
|
|
BufSize,
|
|
GumResource);
|
|
|
|
//
|
|
// If the GUM call was successful, ensure that the resource DLL initialization stuff is
|
|
// also done so that APIs following the CreateClusterResource API can make assumptions
|
|
// that the resource is fully created. Note that the GUM call above will post a work item
|
|
// for the FM worker thread to initialize a resource but there is no guarantee when the
|
|
// FM worker thread will act on the work item. The following call will make sure we
|
|
// won't return from this API until the initialization is fully done thus not giving any
|
|
// chance for APIs such as ChangeClusterResourceGroup that follow this API to screw things
|
|
// up. For backward compatibility reasons (consider a create call originating from a
|
|
// W2K node), we still keep the work item posting in GUM and it won't do any harm since
|
|
// the FmpInitializeResource call is idempotent.
|
|
//
|
|
if ( ( Status == ERROR_SUCCESS ) &&
|
|
( GumResource->Resource != NULL ) )
|
|
{
|
|
FmpClusterWideInitializeResource ( GumResource->Resource );
|
|
}
|
|
|
|
FmpReleaseLocalGroupLock( Group );
|
|
if (Status != ERROR_SUCCESS) {
|
|
LocalFree(GumResource);
|
|
SetLastError(Status);
|
|
return(NULL);
|
|
}
|
|
//The create resource by default adds all nodes
|
|
//as possible nodes for a resource without filtering
|
|
//out the nodes that dont support the resource type
|
|
if( GumResource->Resource != NULL ) {
|
|
FmpCleanupPossibleNodeList(GumResource->Resource);
|
|
}
|
|
Resource = GumResource->Resource;
|
|
if( ( Resource == NULL ) && FmpShutdown ) {
|
|
SetLastError( ERROR_CLUSTER_NODE_SHUTTING_DOWN );
|
|
}
|
|
LocalFree(GumResource);
|
|
} else {
|
|
//
|
|
// The Group lock is released by FmcCreateResource
|
|
//
|
|
Resource = FmcCreateResource( Group,
|
|
ResourceId,
|
|
ResourceName,
|
|
ResourceType,
|
|
dwFlags );
|
|
}
|
|
|
|
|
|
//giving a reference to the client, increment ref count
|
|
if ( Resource ) {
|
|
OmReferenceObject(Resource);
|
|
}
|
|
|
|
return(Resource);
|
|
|
|
} // FmCreateResource
|
|
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
FmDeleteResource(
|
|
IN PFM_RESOURCE Resource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete the specified resource.
|
|
|
|
Arguments:
|
|
|
|
Resource - Supplies the resource to delete.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS if the request was successful.
|
|
|
|
A Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Status;
|
|
LPCWSTR ResourceId;
|
|
DWORD ResourceLen;
|
|
|
|
FmpMustBeOnline( );
|
|
|
|
FmpAcquireLocalResourceLock( Resource );
|
|
|
|
//
|
|
// Check if this is the quorum resource.
|
|
//
|
|
if ( Resource->QuorumResource ) {
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
return(ERROR_QUORUM_RESOURCE);
|
|
}
|
|
|
|
//other core resources cannot be deleted either
|
|
if (Resource->ExFlags & CLUS_FLAG_CORE)
|
|
{
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
return (ERROR_CORE_RESOURCE);
|
|
}
|
|
|
|
//
|
|
// If we own the resource then we can issue the Gum request to delete
|
|
// the resource. Otherwise, request the owner to initiate the request.
|
|
//
|
|
if ( Resource->Group->OwnerNode == NmLocalNode ) {
|
|
|
|
//
|
|
// Check the state of the resource, before attempting to delete it.
|
|
// It must be offline or failed in order to perform the delete.
|
|
//
|
|
if ((Resource->State != ClusterResourceOffline) &&
|
|
(Resource->State != ClusterResourceFailed)) {
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
return(ERROR_RESOURCE_ONLINE);
|
|
}
|
|
|
|
//
|
|
// Check whether this resource provides for any other resources.
|
|
// If so, it cannot be deleted.
|
|
//
|
|
if (!IsListEmpty(&Resource->ProvidesFor)) {
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
return(ERROR_DEPENDENT_RESOURCE_EXISTS);
|
|
}
|
|
|
|
if (Resource->Group->MovingList)
|
|
{
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
return(ERROR_INVALID_STATE);
|
|
}
|
|
|
|
Status = FmpBroadcastDeleteControl(Resource);
|
|
if ( Status != ERROR_SUCCESS ) {
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
return(Status);
|
|
}
|
|
ResourceId = OmObjectId( Resource );
|
|
ResourceLen = (lstrlenW(ResourceId)+1) * sizeof(WCHAR);
|
|
|
|
//
|
|
// Send message.
|
|
//
|
|
Status = GumSendUpdateEx(GumUpdateFailoverManager,
|
|
FmUpdateDeleteResource,
|
|
1,
|
|
ResourceLen,
|
|
ResourceId);
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
} else {
|
|
Status = FmcDeleteResource( Resource );
|
|
}
|
|
|
|
return(Status);
|
|
|
|
} // FmDeleteResource
|
|
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
FmSetResourceName(
|
|
IN PFM_RESOURCE Resource,
|
|
IN LPCWSTR FriendlyName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the user-friendly name for the specified resource.
|
|
|
|
Note that the resource must have already been created. It is also
|
|
assumed that the caller of this routine (the cluster API) has already
|
|
verified that the name is NOT a duplicate.
|
|
|
|
Arguments:
|
|
|
|
Resource - Supplies the resource to enter a new name.
|
|
|
|
FriendlyName - Supplies the user-friendly name for the resource.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
|
|
A Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwStatus = ERROR_SUCCESS;
|
|
|
|
dwStatus = FmpSetResourceName( Resource, FriendlyName );
|
|
|
|
if( dwStatus != ERROR_SUCCESS )
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[FM] FmSetResourceName: FmpSetResourceName for resource %1!ws! fails, Status = %2!d!...\n",
|
|
OmObjectId(Resource),
|
|
dwStatus);
|
|
}
|
|
|
|
return( dwStatus );
|
|
} // FmSetResourceName
|
|
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
FmOnlineResource(
|
|
IN PFM_RESOURCE Resource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine brings a resource online. It also updates the registry to
|
|
indicate the new persistent, desired state of the resource.
|
|
|
|
|
|
Arguments:
|
|
|
|
Resource - A pointer to the resource to bring online.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS if the request is successful.
|
|
ERROR_IO_PENDING if the request is pending.
|
|
A Win32 error code if the request fails.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
|
|
FmpMustBeOnline( );
|
|
|
|
FmpAcquireLocalResourceLock( Resource );
|
|
|
|
//if the resource has been marked for delete, then dont let
|
|
//it be brought online
|
|
if (!IS_VALID_FM_RESOURCE(Resource))
|
|
{
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
return (ERROR_RESOURCE_NOT_AVAILABLE);
|
|
}
|
|
|
|
|
|
//
|
|
// Check if we are the owner... if not, ship the request off someplace
|
|
// else.
|
|
//
|
|
CL_ASSERT( Resource->Group != NULL );
|
|
if ( Resource->Group->OwnerNode != NmLocalNode ) {
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
status = FmcOnlineResourceRequest( Resource );
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// Check if the resource has been initialized. If not, attempt
|
|
// to initialize the resource now.
|
|
//
|
|
if ( Resource->Monitor == NULL ) {
|
|
status = FmpInitializeResource( Resource, TRUE );
|
|
if ( status != ERROR_SUCCESS ) {
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
return(status);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Chittur Subbaraman (chitturs) - 08/04/2000
|
|
//
|
|
// If the group is moving, fail this operation.
|
|
//
|
|
if ( Resource->Group->MovingList != NULL )
|
|
{
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
return (ERROR_GROUP_NOT_AVAILABLE);
|
|
}
|
|
|
|
//
|
|
// Try to bring the resource online.
|
|
//
|
|
status = FmpDoOnlineResource( Resource, TRUE );
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
return(status);
|
|
|
|
} // FmOnlineResource
|
|
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
FmOfflineResource(
|
|
IN PFM_RESOURCE Resource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes a resource offline. It also updates the registry
|
|
to indicate the new persistent, desired state of the resource.
|
|
|
|
|
|
Arguments:
|
|
|
|
Resource - A pointer to the resource to take offline.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS if the request is successful.
|
|
ERROR_IO_PENDING if the request is pending.
|
|
A Win32 error code if the request fails.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
|
|
FmpMustBeOnline( );
|
|
|
|
|
|
FmpAcquireLocalResourceLock( Resource );
|
|
|
|
//if the resource has been marked for delete, then fail this call
|
|
if (!IS_VALID_FM_RESOURCE(Resource))
|
|
{
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
return (ERROR_RESOURCE_NOT_AVAILABLE);
|
|
}
|
|
|
|
//
|
|
// Check if this is the quorum resource.
|
|
//
|
|
if ( Resource->QuorumResource ) {
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
return(ERROR_QUORUM_RESOURCE);
|
|
}
|
|
|
|
|
|
//
|
|
// Chittur Subbaraman (chitturs) - 4/8/99
|
|
//
|
|
// Don't attempt to do anything if the resource has failed. You could
|
|
// get into some funny cases in which the resource switches between
|
|
// offline pending and failed states for ever.
|
|
//
|
|
if ( Resource->State == ClusterResourceFailed ) {
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
return(ERROR_INVALID_STATE);
|
|
}
|
|
|
|
//
|
|
// Check if we are the owner... if not, ship the request off someplace
|
|
// else.
|
|
//
|
|
CL_ASSERT( Resource->Group != NULL );
|
|
if ( Resource->Group->OwnerNode != NmLocalNode ) {
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
return(FmcOfflineResourceRequest(Resource));
|
|
}
|
|
|
|
//
|
|
// Check if the resource has been initialized. If not, return
|
|
// success because the resource is not online.
|
|
//
|
|
if ( Resource->Monitor == NULL ) {
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
|
|
//
|
|
// Chittur Subbaraman (chitturs) - 08/04/2000
|
|
//
|
|
// If the group is moving, fail this operation.
|
|
//
|
|
if ( Resource->Group->MovingList != NULL )
|
|
{
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
return (ERROR_GROUP_NOT_AVAILABLE);
|
|
}
|
|
|
|
//
|
|
// Take the resource offline.
|
|
//
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
return(FmpDoOfflineResource( Resource, TRUE));
|
|
|
|
} // FmOfflineResource
|
|
|
|
|
|
|
|
CLUSTER_RESOURCE_STATE
|
|
WINAPI
|
|
FmGetResourceState(
|
|
IN PFM_RESOURCE Resource,
|
|
OUT LPWSTR NodeName,
|
|
IN OUT PDWORD NameLength OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the current state for the specified resource. The resource state
|
|
consists of state of the resource, along with the current node that is
|
|
hosting the resource.
|
|
|
|
Arguments:
|
|
|
|
Resource - Supplies the resource object to get the state.
|
|
|
|
NodeName - Supplies a pointer to a buffer into which the name of
|
|
the node in the cluster the specified resource is currently hosted.
|
|
This field can be NULL, if NameLength is zero.
|
|
|
|
NameLength - Supplies a pointer to a DWORD containing the number of
|
|
characters available to the NodeName buffer (including the terminating
|
|
NULL character. On return, it is the number of characters written
|
|
into the NodeName buffer not including the NULL character.
|
|
|
|
Returns:
|
|
|
|
Returns the current state of the resource:
|
|
|
|
ClusterResourceOnline
|
|
ClusterResourceOffline
|
|
ClusterResourceFailed
|
|
etc.
|
|
|
|
If the function fails, then the return value is ClusterResourceStateUnknown.
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR computerName[MAX_COMPUTERNAME_LENGTH+1];
|
|
DWORD nameLength;
|
|
DWORD length;
|
|
PNM_NODE OwnerNode;
|
|
CLUSTER_RESOURCE_STATE state;
|
|
BOOL acquired;
|
|
|
|
CL_ASSERT( OmObjectSignature(Resource) == FMP_RESOURCE_SIGNATURE );
|
|
if ( ARGUMENT_PRESENT( NameLength ) ) {
|
|
nameLength = *NameLength;
|
|
*NodeName = (WCHAR)0;
|
|
*NameLength = 0;
|
|
}
|
|
|
|
FmpMustBeOnlineEx( ClusterResourceStateUnknown );
|
|
|
|
//
|
|
// Try to acquire the lock to perform this work, so that resources
|
|
// can query their current status and where the resource should be run.
|
|
//
|
|
// This does leave a potential window though if we can't get the lock,
|
|
// some other thread could be changing the data!
|
|
//
|
|
|
|
FmpTryAcquireLocalResourceLock( Resource, acquired );
|
|
|
|
OwnerNode = Resource->Group->OwnerNode;
|
|
if ( OwnerNode != NULL ) {
|
|
//
|
|
// The Group is 'owned' by some system
|
|
//
|
|
if ( ARGUMENT_PRESENT( NameLength ) ) {
|
|
length = lstrlenW( OmObjectName(OwnerNode) ) + 1;
|
|
if ( nameLength < length ) {
|
|
length = nameLength;
|
|
}
|
|
lstrcpynW( NodeName,
|
|
OmObjectName(OwnerNode),
|
|
length );
|
|
*NameLength = length;
|
|
}
|
|
}
|
|
|
|
state = Resource->State;
|
|
|
|
if ( acquired ) {
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
}
|
|
|
|
if ( state == ClusterGroupStateUnknown ) {
|
|
SetLastError(ERROR_INVALID_STATE);
|
|
}
|
|
|
|
return(state);
|
|
|
|
} // FmGetResourceState
|
|
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
FmAddResourceDependency(
|
|
IN PFM_RESOURCE pResource,
|
|
IN PFM_RESOURCE pDependentResource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Add a dependency from one resource to another.
|
|
|
|
Arguments:
|
|
|
|
Resource - The resource to add the dependent resource.
|
|
|
|
DependentResource - The dependent resource.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
|
|
A Win32 error code on failure.
|
|
|
|
--*/
|
|
{
|
|
LPCWSTR pszResourceId;
|
|
DWORD dwResourceLen;
|
|
LPCWSTR pszDependsOnId;
|
|
DWORD dwDependsOnLen;
|
|
DWORD dwStatus = ERROR_SUCCESS;
|
|
|
|
//
|
|
// Chittur Subbaraman (chitturs) - 5/16/99
|
|
//
|
|
// Modify this API to route requests to owner node. Handle the
|
|
// mixed mode case as well.
|
|
//
|
|
FmpMustBeOnline( );
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmAddResourceDependency : Resource <%1!ws!>, DependentResource <%2!ws!>...\n",
|
|
OmObjectId( pResource ),
|
|
OmObjectId( pDependentResource ));
|
|
|
|
FmpAcquireLocalResourceLock( pResource );
|
|
|
|
//
|
|
// Check if we are the owner... if not, ship the request off some place
|
|
// else.
|
|
//
|
|
if ( pResource->Group->OwnerNode != NmLocalNode )
|
|
{
|
|
//
|
|
// FmcAddResourceDependency releases the local resource lock
|
|
//
|
|
dwStatus = FmcAddResourceDependency( pResource, pDependentResource );
|
|
goto FnExit;
|
|
}
|
|
|
|
dwStatus = FmpValAddResourceDependency( pResource, pDependentResource );
|
|
|
|
if ( dwStatus != ERROR_SUCCESS )
|
|
{
|
|
goto FnUnlock;
|
|
}
|
|
|
|
|
|
pszResourceId = OmObjectId( pResource );
|
|
dwResourceLen = ( lstrlenW( pszResourceId ) +1 ) * sizeof( WCHAR) ;
|
|
|
|
pszDependsOnId = OmObjectId( pDependentResource );
|
|
dwDependsOnLen = ( lstrlenW( pszDependsOnId ) + 1 ) * sizeof( WCHAR );
|
|
|
|
dwStatus = GumSendUpdateEx( GumUpdateFailoverManager,
|
|
FmUpdateAddDependency,
|
|
2,
|
|
dwResourceLen,
|
|
pszResourceId,
|
|
dwDependsOnLen,
|
|
pszDependsOnId );
|
|
|
|
if ( dwStatus == ERROR_SUCCESS )
|
|
{
|
|
FmpBroadcastDependencyChange( pResource,
|
|
pszDependsOnId,
|
|
FALSE );
|
|
}
|
|
|
|
FnUnlock:
|
|
FmpReleaseLocalResourceLock( pResource );
|
|
|
|
FnExit:
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmAddResourceDependency Exit: Status = <%1!u!>...\n",
|
|
dwStatus);
|
|
|
|
return( dwStatus );
|
|
|
|
}
|
|
// FmAddResourceDependency
|
|
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
FmRemoveResourceDependency(
|
|
IN PFM_RESOURCE pResource,
|
|
IN PFM_RESOURCE pDependentResource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Remove a dependency from a resource.
|
|
|
|
Arguments:
|
|
|
|
Resource - The resource to remove the dependent resource.
|
|
DependentResource - The dependent resource.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
|
|
A Win32 error code on failure.
|
|
|
|
--*/
|
|
{
|
|
|
|
LPCWSTR pszResourceId;
|
|
DWORD dwResourceLen;
|
|
LPCWSTR pszDependsOnId;
|
|
DWORD dwDependsOnLen;
|
|
DWORD dwStatus;
|
|
|
|
//
|
|
// Chittur Subbaraman (chitturs) - 5/16/99
|
|
//
|
|
// Modify this API to route requests to owner node. Handle the
|
|
// mixed mode case as well.
|
|
//
|
|
FmpMustBeOnline( );
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmRemoveResourceDependency : Resource <%1!ws!>, DependentResource <%2!ws!>...\n",
|
|
OmObjectId( pResource ),
|
|
OmObjectId( pDependentResource ));
|
|
|
|
FmpAcquireLocalResourceLock( pResource );
|
|
|
|
//
|
|
// Check if we are the owner... if not, ship the request off some place
|
|
// else.
|
|
//
|
|
if ( pResource->Group->OwnerNode != NmLocalNode )
|
|
{
|
|
//
|
|
// FmcRemoveResourceDependency releases the local resource lock
|
|
//
|
|
dwStatus = FmcRemoveResourceDependency( pResource, pDependentResource );
|
|
goto FnExit;
|
|
}
|
|
|
|
dwStatus = FmpValRemoveResourceDependency( pResource, pDependentResource );
|
|
|
|
if ( dwStatus != ERROR_SUCCESS )
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmRemoveResourceDependency: FmpValRemoveResourceDependency returns status = <%1!u!>...\n",
|
|
dwStatus);
|
|
goto FnUnlock;
|
|
}
|
|
|
|
pszResourceId = OmObjectId( pResource );
|
|
dwResourceLen = ( lstrlenW( pszResourceId ) + 1 ) * sizeof( WCHAR );
|
|
|
|
pszDependsOnId = OmObjectId( pDependentResource );
|
|
dwDependsOnLen = ( lstrlenW( pszDependsOnId ) + 1 ) * sizeof( WCHAR );
|
|
|
|
|
|
dwStatus = GumSendUpdateEx( GumUpdateFailoverManager,
|
|
FmUpdateRemoveDependency,
|
|
2,
|
|
dwResourceLen,
|
|
pszResourceId,
|
|
dwDependsOnLen,
|
|
pszDependsOnId );
|
|
|
|
if ( dwStatus == ERROR_SUCCESS )
|
|
{
|
|
FmpBroadcastDependencyChange( pResource,
|
|
pszDependsOnId,
|
|
TRUE );
|
|
}
|
|
|
|
FnUnlock:
|
|
FmpReleaseLocalResourceLock( pResource );
|
|
FnExit:
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmRemoveResourceDependency Exit: Status = <%1!u!>...\n",
|
|
dwStatus);
|
|
|
|
return( dwStatus );
|
|
|
|
}
|
|
|
|
// FmRemoveResourceDependency
|
|
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
FmEnumResourceDependent(
|
|
IN PFM_RESOURCE Resource,
|
|
IN DWORD Index,
|
|
OUT PFM_RESOURCE *DependentResource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enumerate the dependencies of a resources.
|
|
|
|
Arguments:
|
|
|
|
Resource - The resource to enumerate.
|
|
|
|
Index - The index for this enumeration.
|
|
|
|
DependentResource - The dependent resource. The returned resource
|
|
pointer will be referenced by this routine and should
|
|
be dereferenced when the caller is done with it.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
|
|
A Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
DWORD i = 0;
|
|
PFM_RESOURCE Current;
|
|
PDEPENDENCY Dependency;
|
|
DWORD Status = ERROR_NO_MORE_ITEMS;
|
|
|
|
FmpMustBeOnline( );
|
|
|
|
FmpAcquireResourceLock();
|
|
if (!IS_VALID_FM_RESOURCE(Resource))
|
|
{
|
|
Status = ERROR_RESOURCE_NOT_AVAILABLE;
|
|
goto FnExit;
|
|
}
|
|
|
|
ListEntry = Resource->DependsOn.Flink;
|
|
while (ListEntry != &Resource->DependsOn) {
|
|
Dependency = CONTAINING_RECORD(ListEntry,
|
|
DEPENDENCY,
|
|
DependentLinkage);
|
|
CL_ASSERT(Dependency->DependentResource == Resource);
|
|
CL_ASSERT(Dependency->ProviderResource != Resource);
|
|
if (i==Index) {
|
|
//
|
|
// Got the right index
|
|
//
|
|
OmReferenceObject(Dependency->ProviderResource);
|
|
*DependentResource = Dependency->ProviderResource;
|
|
Status = ERROR_SUCCESS;
|
|
break;
|
|
}
|
|
ListEntry = ListEntry->Flink;
|
|
++i;
|
|
}
|
|
|
|
FnExit:
|
|
FmpReleaseResourceLock();
|
|
|
|
return(Status);
|
|
} // FmEnumResourceDependent
|
|
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
FmEnumResourceProvider(
|
|
IN PFM_RESOURCE Resource,
|
|
IN DWORD Index,
|
|
OUT PFM_RESOURCE *DependentResource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enumerate the providers for a resources.
|
|
|
|
Arguments:
|
|
|
|
Resource - The resource to enumerate.
|
|
|
|
Index - The index for this enumeration.
|
|
|
|
DependentResource - The provider resource. The returned resource
|
|
pointer will be referenced by this routine and should
|
|
be dereferenced when the caller is done with it.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
|
|
A Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
DWORD i = 0;
|
|
PFM_RESOURCE Current;
|
|
PDEPENDENCY Dependency;
|
|
DWORD Status = ERROR_NO_MORE_ITEMS;
|
|
|
|
FmpMustBeOnline( );
|
|
|
|
FmpAcquireResourceLock();
|
|
|
|
if (!IS_VALID_FM_RESOURCE(Resource))
|
|
{
|
|
Status = ERROR_RESOURCE_NOT_AVAILABLE;
|
|
goto FnExit;
|
|
}
|
|
|
|
ListEntry = Resource->ProvidesFor.Flink;
|
|
while (ListEntry != &Resource->ProvidesFor) {
|
|
Dependency = CONTAINING_RECORD(ListEntry,
|
|
DEPENDENCY,
|
|
ProviderLinkage);
|
|
CL_ASSERT(Dependency->DependentResource != Resource);
|
|
CL_ASSERT(Dependency->ProviderResource == Resource);
|
|
if (i==Index) {
|
|
//
|
|
// Got the right index
|
|
//
|
|
OmReferenceObject(Dependency->DependentResource);
|
|
*DependentResource = Dependency->DependentResource;
|
|
Status = ERROR_SUCCESS;
|
|
break;
|
|
}
|
|
ListEntry = ListEntry->Flink;
|
|
++i;
|
|
}
|
|
|
|
FnExit:
|
|
FmpReleaseResourceLock();
|
|
|
|
return(Status);
|
|
|
|
} // FmEnumResourceProvider
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
FmEnumResourceNode(
|
|
IN PFM_RESOURCE Resource,
|
|
IN DWORD Index,
|
|
OUT PNM_NODE *PossibleNode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enumerate the possible nodes for a resources.
|
|
|
|
Arguments:
|
|
|
|
Resource - The resource to enumerate.
|
|
|
|
Index - The index for this enumeration.
|
|
|
|
PossibleNode - The possible node. The returned node
|
|
pointer will be referenced by this routine and should
|
|
be dereferenced when the caller is done with it.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
|
|
A Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
DWORD i = 0;
|
|
PFM_RESOURCE Current;
|
|
PPOSSIBLE_ENTRY PossibleEntry;
|
|
DWORD Status = ERROR_NO_MORE_ITEMS;
|
|
|
|
FmpMustBeOnline( );
|
|
|
|
FmpAcquireResourceLock();
|
|
if (!IS_VALID_FM_RESOURCE(Resource))
|
|
{
|
|
Status = ERROR_RESOURCE_NOT_AVAILABLE;
|
|
goto FnExit;
|
|
}
|
|
|
|
|
|
ListEntry = Resource->PossibleOwners.Flink;
|
|
while (ListEntry != &Resource->PossibleOwners) {
|
|
PossibleEntry = CONTAINING_RECORD(ListEntry,
|
|
POSSIBLE_ENTRY,
|
|
PossibleLinkage);
|
|
if (i==Index) {
|
|
//
|
|
// Got the right index
|
|
//
|
|
OmReferenceObject(PossibleEntry->PossibleNode);
|
|
*PossibleNode = PossibleEntry->PossibleNode;
|
|
Status = ERROR_SUCCESS;
|
|
break;
|
|
}
|
|
ListEntry = ListEntry->Flink;
|
|
++i;
|
|
}
|
|
|
|
FnExit:
|
|
FmpReleaseResourceLock();
|
|
|
|
return(Status);
|
|
|
|
} // FmEnumResourceNode
|
|
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
FmFailResource(
|
|
IN PFM_RESOURCE Resource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cause the specified resource to fail.
|
|
|
|
Arguments:
|
|
|
|
Resource - The resource to make fail.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS - if successful.
|
|
|
|
A Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
FmpMustBeOnline( );
|
|
|
|
if ( Resource->Group->OwnerNode != NmLocalNode ) {
|
|
return(FmcFailResource( Resource ));
|
|
}
|
|
|
|
return(FmpRmFailResource( Resource ));
|
|
|
|
} // FmFailResource
|
|
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
FmChangeResourceNode(
|
|
IN PFM_RESOURCE Resource,
|
|
IN PNM_NODE Node,
|
|
IN BOOL Add
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Changes the list of nodes where the specified resource
|
|
can be brought online.
|
|
|
|
Arguments:
|
|
|
|
Resource - Supplies the resource whose list of possible nodes is
|
|
to be modified.
|
|
|
|
Node - Supplies the node to be added to the resource's list.
|
|
|
|
Add - Supplies whether the specified node is to be added (TRUE) or
|
|
deleted (FALSE) from the resource's node list.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
{
|
|
DWORD Status;
|
|
|
|
FmpAcquireLocalResourceLock( Resource );
|
|
|
|
if ( Resource->Group->OwnerNode != NmLocalNode ) {
|
|
// Note: FmcChangeResourceNode must release the resource lock.
|
|
Status = FmcChangeResourceNode( Resource, Node, Add );
|
|
}
|
|
else
|
|
{
|
|
|
|
Status = FmpChangeResourceNode(Resource, OmObjectId(Node), Add);
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
}
|
|
return(Status);
|
|
} // FmChangeResourceNode
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
FmSetQuorumResource(
|
|
IN PFM_RESOURCE Resource,
|
|
IN LPCWSTR pszClusFileRootPath,
|
|
IN DWORD dwMaxQuorumLogSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the specified resource as the quorum resource. This requires making
|
|
sure that the specified resource can perform an arbitrate. We do this
|
|
by asking the owner node to perform an arbitrate of the resource.
|
|
|
|
Arguments:
|
|
|
|
Resource - Supplies the resource that must be arbitrated.
|
|
|
|
pszLogPathName - The root path where the log files will be moved. "Microsoft
|
|
Cluster Manager Directory" is created under the root path provided. If NULL,
|
|
a partition on the shared quorum device is picked up randomly. And
|
|
the log files are placed in the directory specified by the
|
|
CLUSTER_QUORUM_DEFAULT_MAX_LOG_SIZE constant at the root of that partition.
|
|
|
|
dwMaxQuorumLogSize - The maximum size of the quorum logs. If 0, the default
|
|
used. If smaller that 32K, 32K is used.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
|
|
A Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
DWORD resourceIdLen;
|
|
PFM_RESOURCE quorumResource = NULL;
|
|
PFM_RESOURCE pOldQuoResource = NULL;
|
|
PVOID gumResource = NULL;
|
|
DWORD dwBytesReturned;
|
|
DWORD dwRequired;
|
|
DWORD dwBufSize;
|
|
WCHAR szQuoLogPath[MAX_PATH] = L"\0";
|
|
WCHAR szLogRootPath[MAX_PATH];
|
|
CLUS_RESOURCE_CLASS_INFO resClassInfo;
|
|
PUCHAR pBuf = NULL;
|
|
LPWSTR pszOldQuoLogPath = NULL;
|
|
LPWSTR pszNext = NULL;
|
|
LPWSTR pszExpClusFileRootPath = NULL;
|
|
DWORD dwCharacteristics;
|
|
BOOL fIsPathUNC;
|
|
DWORD dwCurrentNodeCnt;
|
|
DWORD dwClusterHighestVersion;
|
|
|
|
FmpMustBeOnline( );
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmSetQuorumResource: Entry, pszClusFileRootPath=%1!ws!\r\n",
|
|
((pszClusFileRootPath)? pszClusFileRootPath:szQuoLogPath));
|
|
|
|
dwCurrentNodeCnt = NmGetCurrentNumberOfNodes();
|
|
// find the old quorum resource
|
|
status = FmFindQuorumResource(&pOldQuoResource);
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Synchronize access to Quorum Resource changes.
|
|
//
|
|
//
|
|
// Synchronize both the old and the new resource.
|
|
// Lock the lowest by lowest Group Id first - to prevent deadlocks!
|
|
// Note - the order of release is unimportant.
|
|
//
|
|
// if the old and new resource belong to the same group
|
|
// the comparison will be be equal!
|
|
//
|
|
ACQUIRE_EXCLUSIVE_LOCK(gQuoChangeLock);
|
|
|
|
if ( lstrcmpiW( OmObjectId( pOldQuoResource->Group ),
|
|
OmObjectId( Resource->Group ) ) <= 0 ) {
|
|
FmpAcquireLocalGroupLock( pOldQuoResource->Group );
|
|
FmpAcquireLocalGroupLock( Resource->Group );
|
|
} else {
|
|
FmpAcquireLocalGroupLock( Resource->Group );
|
|
FmpAcquireLocalGroupLock( pOldQuoResource->Group );
|
|
}
|
|
|
|
status = FmpGetResourceCharacteristics(Resource, &dwCharacteristics);
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL, "[FM] FmSetQuorumResource: getresourcecharacteristics failed, status=%1!u!\n",
|
|
status);
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// If the resource doesn't advertise itself as quorum-capable, no point in proceeding.
|
|
//
|
|
if ( !( dwCharacteristics & CLUS_CHAR_QUORUM ) )
|
|
{
|
|
status = ERROR_NOT_QUORUM_CAPABLE;
|
|
ClRtlLogPrint(LOG_CRITICAL, "[FM] FmSetQuorumResource: Resource %1!ws! can't be quorum, status %2!u!\n",
|
|
OmObjectName(Resource),
|
|
status);
|
|
goto FnExit;
|
|
}
|
|
|
|
if (dwCurrentNodeCnt > 1)
|
|
{
|
|
//if the cluster is a multinode cluster, dont allow a switch to
|
|
//local quorum type resources unless the debug characteristic is set
|
|
if ((dwCharacteristics & CLUS_CHAR_LOCAL_QUORUM) &&
|
|
!(dwCharacteristics & CLUS_CHAR_LOCAL_QUORUM_DEBUG))
|
|
{
|
|
status = ERROR_INVALID_PARAMETER;
|
|
ClRtlLogPrint(LOG_CRITICAL, "[FM] FmSetQuorumResource: No debug property for local quorum resource %1!ws!, status %2!u!\n",
|
|
OmObjectName(Resource),
|
|
status);
|
|
goto FnExit;
|
|
}
|
|
|
|
}
|
|
if (Resource->State != ClusterResourceOnline)
|
|
{
|
|
status = ERROR_RESOURCE_NOT_ONLINE;
|
|
goto FnExit;
|
|
|
|
}
|
|
|
|
if (!IsListEmpty(&Resource->DependsOn))
|
|
{
|
|
status = ERROR_DEPENDENCY_NOT_ALLOWED;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Get the old log path.
|
|
//
|
|
dwBytesReturned = 0;
|
|
dwRequired = 0;
|
|
|
|
status = DmQuerySz( DmQuorumKey,
|
|
cszPath,
|
|
(LPWSTR*)&pszOldQuoLogPath,
|
|
&dwRequired,
|
|
&dwBytesReturned);
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[FM] FmSetQuorumResource Failed to get the old quo log path, error %1!u!.\n",
|
|
status);
|
|
goto FnExit;
|
|
}
|
|
//SS: if you want to have a sub dir for logging files
|
|
|
|
//check the resource class
|
|
status = FmResourceControl(Resource, NULL, CLUSCTL_RESOURCE_GET_CLASS_INFO, NULL, 0,
|
|
(PUCHAR)&resClassInfo, sizeof(resClassInfo), &dwBytesReturned, &dwRequired);
|
|
if ( status != ERROR_SUCCESS )
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL, "[FM] FmSetQuorumResource: Unable to get class info for resource %1!ws!, status %2!u!\n",
|
|
OmObjectName(Resource),
|
|
status);
|
|
goto FnExit;
|
|
}
|
|
|
|
if ( ( resClassInfo.SubClass & CLUS_RESSUBCLASS_SHARED ) == 0 )
|
|
{
|
|
status = ERROR_NOT_QUORUM_CLASS;
|
|
goto FnExit;
|
|
}
|
|
|
|
//allocate info for the disk info
|
|
//get disk info
|
|
dwBufSize = 2048;
|
|
Retry:
|
|
pBuf = LocalAlloc(LMEM_FIXED, dwBufSize);
|
|
if (pBuf == NULL ) {
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto FnExit;
|
|
}
|
|
status = FmResourceControl(Resource, NULL, CLUSCTL_RESOURCE_STORAGE_GET_DISK_INFO,
|
|
NULL, 0, pBuf, dwBufSize, &dwBytesReturned, &dwRequired);
|
|
if ((status == ERROR_MORE_DATA) && (dwBufSize < dwRequired))
|
|
{
|
|
dwBufSize = dwRequired;
|
|
LocalFree(pBuf);
|
|
goto Retry;
|
|
}
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL, "[FM] FmSetQuorumResource: Unable to get disk info for resource %1!ws!, status %2!u!\n",
|
|
OmObjectName(Resource),
|
|
status);
|
|
goto FnExit;
|
|
}
|
|
|
|
if (pszClusFileRootPath)
|
|
pszExpClusFileRootPath = ClRtlExpandEnvironmentStrings(pszClusFileRootPath);
|
|
|
|
//use the expanded path name for validation
|
|
if (pszExpClusFileRootPath)
|
|
{
|
|
WCHAR cColon=L':';
|
|
|
|
//
|
|
// The quorum management code assumes that the quorum path + file names such as quolog.log can all fit in MAX_PATH
|
|
// sized buffers. It is too much code churn to fix all places in which this assumption is made. Let us reject paths that
|
|
// are too long right here.
|
|
//
|
|
if ( lstrlen ( pszExpClusFileRootPath ) + 20 > MAX_PATH )
|
|
{
|
|
status = ERROR_BAD_PATHNAME;
|
|
ClRtlLogPrint(LOG_UNUSUAL, "[FM] FmSetQuorumResource: Path specified is too long, status %1!u!\n",
|
|
status);
|
|
goto FnExit;
|
|
}
|
|
|
|
pszNext = wcschr(pszExpClusFileRootPath, cColon);
|
|
//pick up just the drive letter
|
|
if (pszNext)
|
|
{
|
|
lstrcpynW(szLogRootPath, pszExpClusFileRootPath,
|
|
(UINT)(pszNext-pszExpClusFileRootPath+2));
|
|
}
|
|
else
|
|
{
|
|
//if there is no drive letter, pick up a drive letter at random
|
|
szLogRootPath[0] = L'\0';
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
szLogRootPath[0] = L'\0';
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmSetQuorumResource: szLogRootPath=%1!ws!\r\n",
|
|
szLogRootPath);
|
|
|
|
//save the drive letter for the new quorum path
|
|
status = FmpGetDiskInfoParseProperties(pBuf, dwBytesReturned, szLogRootPath);
|
|
|
|
//if the status was invalid parameter for a local quorum, ignore the local
|
|
//quorum path setting..what is specified through this api overrides
|
|
if ((status == ERROR_INVALID_PARAMETER) &&
|
|
(dwCharacteristics & CLUS_CHAR_LOCAL_QUORUM))
|
|
{
|
|
status = ERROR_SUCCESS;
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmSetQuorumResource: LocalQuorum force success, szLogRootPath=%1!ws!\r\n",
|
|
szLogRootPath);
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmSetQuorumResource: szLogRootPath=%1!ws!\r\n",
|
|
szLogRootPath);
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL, "[FM] FmSetQuorumResource: Unable to parse disk info for resource %1!ws!, status %2!u!\n",
|
|
OmObjectName(Resource),
|
|
status);
|
|
goto FnExit;
|
|
}
|
|
|
|
if (szLogRootPath[0] == L'\0')
|
|
{
|
|
//no valid drive letter is found
|
|
status = ERROR_INVALID_PARAMETER;
|
|
ClRtlLogPrint(LOG_CRITICAL, "[FM] FmSetQuorumResource: No valid drive letter for resource %1!ws!, status %2!u!\n",
|
|
OmObjectName(Resource),
|
|
status);
|
|
goto FnExit;
|
|
}
|
|
//got the drive letter
|
|
lstrcpyW(szQuoLogPath, szLogRootPath);
|
|
if (pszNext)
|
|
{
|
|
// if the driver letter was supplied, append the rest of the path
|
|
lstrcatW(szQuoLogPath, pszNext+1);
|
|
}
|
|
else
|
|
{
|
|
//if no drive letter was supplied
|
|
// if a path was supplied, append the path
|
|
if ( pszExpClusFileRootPath )
|
|
{
|
|
//
|
|
// If the user specifies an SMB path, we override whatever the resource told us.
|
|
//
|
|
if ( ( lstrlenW( pszExpClusFileRootPath ) >=2 ) &&
|
|
( pszExpClusFileRootPath[0] == L'\\' ) &&
|
|
( pszExpClusFileRootPath[1] == L'\\' ) &&
|
|
( dwCharacteristics & CLUS_CHAR_QUORUM ) )
|
|
{
|
|
lstrcpyW( szQuoLogPath, pszExpClusFileRootPath );
|
|
}
|
|
else if ( pszExpClusFileRootPath[0] == L'\\' )
|
|
{
|
|
lstrcatW( szQuoLogPath, pszExpClusFileRootPath );
|
|
}
|
|
else
|
|
{
|
|
lstrcatW( szQuoLogPath, L"\\" );
|
|
lstrcatW( szQuoLogPath, pszExpClusFileRootPath );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// else append the default path
|
|
lstrcatW( szQuoLogPath, L"\\" );
|
|
lstrcatW(szQuoLogPath, CLUS_NAME_DEFAULT_FILESPATH);
|
|
}
|
|
}
|
|
|
|
//if the path name is provided, check if it is terminated with '\'
|
|
//if not, terminate it
|
|
if (szQuoLogPath[lstrlenW(szQuoLogPath) - 1] != L'\\')
|
|
{
|
|
lstrcatW( szQuoLogPath, L"\\" );
|
|
}
|
|
|
|
//
|
|
// If the path is of UNC form, then prepend a \\?\UNC\ to it.
|
|
//
|
|
status = FmpCanonicalizePath( szQuoLogPath, &fIsPathUNC );
|
|
|
|
if ( status != ERROR_SUCCESS ) goto FnExit;
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmSetQuorumResource: szQuoLogPath=%1!ws!\r\n",
|
|
szQuoLogPath);
|
|
|
|
//
|
|
// Allocate a message buffer.
|
|
//
|
|
resourceIdLen = (lstrlenW(OmObjectId(Resource))+1) * sizeof(WCHAR);
|
|
gumResource = LocalAlloc(LMEM_FIXED, resourceIdLen);
|
|
if (gumResource == NULL)
|
|
{
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto FnExit;
|
|
}
|
|
//
|
|
// Fill in message buffer.
|
|
//
|
|
CopyMemory(gumResource, OmObjectId(Resource), resourceIdLen);
|
|
|
|
//
|
|
// Make sure that we can arbitrate the new quorum resource.
|
|
//
|
|
if ( Resource->Group->OwnerNode != NmLocalNode ) {
|
|
status = FmcArbitrateResource( Resource );
|
|
} else {
|
|
status = FmpRmArbitrateResource( Resource );
|
|
}
|
|
|
|
if ( status != ERROR_SUCCESS ) {
|
|
ClRtlLogPrint(LOG_CRITICAL, "[FM] FmSetQuorumResource: Arbitrate for resource %1!ws! failed, status %2!u!\n",
|
|
OmObjectName(Resource),
|
|
status);
|
|
goto FnExit;
|
|
}
|
|
|
|
//check the log size, if it not zero but less than the min
|
|
//limit set it to 32K.
|
|
if ((dwMaxQuorumLogSize) && (dwMaxQuorumLogSize < CLUSTER_QUORUM_MIN_LOG_SIZE))
|
|
{
|
|
dwMaxQuorumLogSize = CLUSTER_QUORUM_MIN_LOG_SIZE;
|
|
}
|
|
//Prepare to move to a new quorum resource
|
|
//create a new quorum log file and
|
|
//move the registry files there.
|
|
if ( Resource->Group->OwnerNode != NmLocalNode ) {
|
|
status = FmcPrepareQuorumResChange( Resource, szQuoLogPath, dwMaxQuorumLogSize );
|
|
} else {
|
|
status = FmpPrepareQuorumResChange( Resource, szQuoLogPath, dwMaxQuorumLogSize );
|
|
}
|
|
|
|
if ( status != ERROR_SUCCESS ) {
|
|
if ((dwCharacteristics & CLUS_CHAR_LOCAL_QUORUM) && (fIsPathUNC == FALSE))
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmSetQuorumResource: Local quorum, map FmpPrepareQuorumResChange to success\n");
|
|
status = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// If we are dealing with the mixed mode cluster then dont set the arbitration
|
|
// timeouts
|
|
//
|
|
NmGetClusterOperationalVersion( &dwClusterHighestVersion,
|
|
NULL,
|
|
NULL );
|
|
|
|
if ((CLUSTER_GET_MAJOR_VERSION(dwClusterHighestVersion) < NT51_MAJOR_VERSION ))
|
|
{
|
|
//
|
|
// This is a mixed mode cluster, send the old gum message.
|
|
//
|
|
status = GumSendUpdateEx(GumUpdateFailoverManager,
|
|
FmUpdateChangeQuorumResource,
|
|
3,
|
|
resourceIdLen,
|
|
gumResource,
|
|
(lstrlenW(szQuoLogPath) + 1 ) * sizeof(WCHAR),
|
|
szQuoLogPath,
|
|
sizeof(DWORD),
|
|
&dwMaxQuorumLogSize
|
|
);
|
|
}
|
|
else
|
|
{
|
|
DWORD dwArbTimeout;
|
|
CLUSPROP_DWORD ClusPropArbTimeout;
|
|
|
|
// Read and set the new arbitration timeout
|
|
status = FmResourceTypeControl(OmObjectId(Resource->Type), NULL, CLUSCTL_RESOURCE_TYPE_GET_ARB_TIMEOUT,
|
|
NULL, 0, (CHAR *)&ClusPropArbTimeout, sizeof(ClusPropArbTimeout), &dwBytesReturned, &dwRequired);
|
|
if ( status != ERROR_SUCCESS )
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmSetQuorumResource: couldnt get RESTYPE arbitration timeout, defaulting to 60 seconds. Status = %1!u!\n",
|
|
status);
|
|
dwArbTimeout = CLUSTER_QUORUM_DEFAULT_ARBITRATION_TIMEOUT;
|
|
//map to success, since we dont want to fail the request in this case
|
|
status = ERROR_SUCCESS;
|
|
} else {
|
|
dwArbTimeout = ClusPropArbTimeout.dw;
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmSetQuorumResource: setting arbitration timeout to %1!u! seconds.\n", dwArbTimeout );
|
|
|
|
//
|
|
// Send the message.
|
|
//
|
|
status = GumSendUpdateEx(GumUpdateFailoverManager,
|
|
FmUpdateChangeQuorumResource2,
|
|
5,
|
|
resourceIdLen,
|
|
gumResource,
|
|
(lstrlenW(szQuoLogPath) + 1 ) * sizeof(WCHAR),
|
|
szQuoLogPath,
|
|
sizeof(DWORD),
|
|
&dwMaxQuorumLogSize,
|
|
sizeof(DWORD),
|
|
&dwArbTimeout,
|
|
sizeof(DWORD),
|
|
&dwCharacteristics
|
|
);
|
|
}
|
|
|
|
//if the old path is not the same as the new path
|
|
//create a tombstone for the quorum log files on the old path
|
|
//this is to prevent nodes that are not present in this update
|
|
//from doing a form.
|
|
if ( (status == ERROR_SUCCESS) &&
|
|
(lstrcmpiW(szQuoLogPath, pszOldQuoLogPath)) ) {
|
|
//
|
|
// delete the old quorum log files on the old resource and create a tombstone file
|
|
// in there.
|
|
//
|
|
if ( pOldQuoResource->Group->OwnerNode != NmLocalNode ) {
|
|
status = FmcCompleteQuorumResChange( pOldQuoResource, pszOldQuoLogPath );
|
|
} else {
|
|
status = FmpCompleteQuorumResChange( OmObjectId(pOldQuoResource), pszOldQuoLogPath );
|
|
}
|
|
|
|
}
|
|
|
|
FnExit:
|
|
//not the order of release is not important
|
|
FmpReleaseLocalGroupLock(pOldQuoResource->Group);
|
|
FmpReleaseLocalGroupLock(Resource->Group);
|
|
RELEASE_LOCK(gQuoChangeLock);
|
|
|
|
if (pBuf) LocalFree(pBuf);
|
|
if (gumResource) LocalFree(gumResource);
|
|
if (pOldQuoResource) OmDereferenceObject(pOldQuoResource);
|
|
if (pszOldQuoLogPath) LocalFree(pszOldQuoLogPath);
|
|
if (pszExpClusFileRootPath) LocalFree(pszExpClusFileRootPath);
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmSetQuorumResource: Exit, status=%1!u!\r\n",
|
|
status);
|
|
|
|
return(status);
|
|
|
|
} // FmSetQuorumResource
|
|
|
|
|
|
DWORD
|
|
FmCreateResourceType(
|
|
IN LPCWSTR lpszTypeName,
|
|
IN LPCWSTR lpszDisplayName,
|
|
IN LPCWSTR lpszDllName,
|
|
IN DWORD dwLooksAlive,
|
|
IN DWORD dwIsAlive
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Issues a GUM update to instantiate a resource type on every
|
|
node. The registry update as well as the FM in-memory state
|
|
update is done as a transaction within the GUM handler (NT5
|
|
clusters only).
|
|
|
|
Arguments:
|
|
|
|
lpszTypeName - Supplies the name of the new cluster resource type.
|
|
|
|
lpszDisplayName - Supplies the display name for the new resource
|
|
type. While lpszResourceTypeName should uniquely identify the
|
|
resource type on all clusters, the lpszDisplayName should be
|
|
a localized friendly name for the resource, suitable for displaying
|
|
to administrators.
|
|
|
|
lpszDllName - Supplies the name of the new resource type’s DLL.
|
|
|
|
dwLooksAlive - Supplies the default LooksAlive poll interval
|
|
for the new resource type in milliseconds.
|
|
|
|
dwIsAlive - Supplies the default IsAlive poll interval for
|
|
the new resource type in milliseconds.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
|
|
Win32 error otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwStatus = ERROR_SUCCESS;
|
|
PFM_RESTYPE pResType = NULL;
|
|
DWORD dwTypeNameLen;
|
|
DWORD dwDisplayNameLen;
|
|
DWORD dwDllNameLen;
|
|
DWORD dwBufferLen;
|
|
LPVOID Buffer = NULL;
|
|
|
|
//
|
|
// Chittur Subbaraman (chitturs) - 2/8/2000
|
|
//
|
|
// Rewrite this API to use a GUM handler which performs a local
|
|
// transaction for NT5.1
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmCreateResourceType: Entry for %1!ws!...\r\n",
|
|
lpszTypeName);
|
|
|
|
//
|
|
// If the resource type already exists, do not issue a GUM and have it fail.
|
|
//
|
|
pResType = OmReferenceObjectById( ObjectTypeResType,
|
|
lpszTypeName );
|
|
if ( pResType )
|
|
{
|
|
dwStatus = ERROR_ALREADY_EXISTS;
|
|
OmDereferenceObject( pResType );
|
|
goto FnExit;
|
|
}
|
|
|
|
dwTypeNameLen = ( lstrlenW( lpszTypeName ) + 1 ) * sizeof( WCHAR );
|
|
dwDisplayNameLen = ( lstrlenW( lpszDisplayName ) + 1 ) * sizeof( WCHAR );
|
|
dwDllNameLen = ( lstrlenW( lpszDllName ) + 1 ) * sizeof( WCHAR );
|
|
dwBufferLen = dwTypeNameLen + dwDisplayNameLen + dwDllNameLen +
|
|
2 * sizeof( DWORD );
|
|
|
|
Buffer = LocalAlloc( LMEM_FIXED, dwBufferLen );
|
|
|
|
if ( Buffer == NULL )
|
|
{
|
|
dwStatus = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[FM] FmCreateResourceType: FmUpdateCreateResourceType for %1!ws! returned %2!u! on mem alloc...\n",
|
|
lpszTypeName,
|
|
dwStatus);
|
|
goto FnExit;
|
|
}
|
|
|
|
CopyMemory( Buffer, lpszTypeName, dwTypeNameLen );
|
|
CopyMemory( ( PCHAR ) Buffer + dwTypeNameLen, lpszDisplayName, dwDisplayNameLen );
|
|
CopyMemory( ( PCHAR ) Buffer + dwTypeNameLen + dwDisplayNameLen, lpszDllName, dwDllNameLen );
|
|
CopyMemory( ( PCHAR ) Buffer +
|
|
dwTypeNameLen +
|
|
dwDisplayNameLen +
|
|
dwDllNameLen, &dwLooksAlive, sizeof( DWORD ) );
|
|
CopyMemory( ( PCHAR ) Buffer +
|
|
dwTypeNameLen +
|
|
dwDisplayNameLen +
|
|
dwDllNameLen + sizeof( DWORD ), &dwIsAlive, sizeof( DWORD ) );
|
|
|
|
dwStatus = GumSendUpdate( GumUpdateFailoverManager,
|
|
FmUpdateCreateResourceType,
|
|
dwBufferLen,
|
|
Buffer );
|
|
|
|
if ( dwStatus != ERROR_SUCCESS )
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[FM] FmCreateResourceType: FmUpdateCreateResourceType for %1!ws! returned %2!u!...\r\n",
|
|
lpszTypeName,
|
|
dwStatus);
|
|
goto FnExit;
|
|
}
|
|
|
|
dwStatus = FmpSetPossibleNodeForResType( lpszTypeName , FALSE );
|
|
|
|
if ( dwStatus != ERROR_SUCCESS )
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[FM] FmCreateResourceType: FmpSetPossibleNodeForResType for %2!ws! returned <%1!u!>...\r\n",
|
|
lpszTypeName,
|
|
dwStatus);
|
|
goto FnExit;
|
|
}
|
|
|
|
pResType = OmReferenceObjectById( ObjectTypeResType, lpszTypeName );
|
|
|
|
if ( pResType )
|
|
{
|
|
ClusterWideEvent( CLUSTER_EVENT_RESTYPE_ADDED, pResType );
|
|
OmDereferenceObject( pResType );
|
|
} else
|
|
{
|
|
if ( !FmpFMGroupsInited )
|
|
{
|
|
dwStatus = ERROR_CLUSTER_NODE_NOT_READY;
|
|
} else if ( FmpShutdown )
|
|
{
|
|
dwStatus = ERROR_CLUSTER_NODE_SHUTTING_DOWN;
|
|
} else
|
|
{
|
|
dwStatus = ERROR_CLUSTER_RESOURCE_TYPE_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
FnExit:
|
|
LocalFree( Buffer );
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmCreateResourceType: Exit for %1!ws!, Status=%2!u!...\r\n",
|
|
lpszTypeName,
|
|
dwStatus);
|
|
|
|
return( dwStatus );
|
|
} // FmCreateResourceType
|
|
|
|
|
|
|
|
DWORD
|
|
FmDeleteResourceType(
|
|
IN LPCWSTR TypeName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Issues a GUM update to delete a resource type on every
|
|
node.
|
|
|
|
Arguments:
|
|
|
|
TypeName - Supplies the name of the cluster resource type
|
|
to delete
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
|
|
Win32 error otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFM_RESTYPE pResType;
|
|
BOOL fResourceExists = FALSE;
|
|
|
|
//
|
|
// Chittur Subbaraman (chitturs) - 5/9/2001
|
|
//
|
|
// Make sure the resource type exists so that you can avoid a GUM if
|
|
// that is not necessary. This also takes care of the case in which one node was
|
|
// shutting down and so the GUM returns success and another node fails in the GUM
|
|
// and gets evicted since the resource type does not exist.
|
|
//
|
|
pResType = OmReferenceObjectById( ObjectTypeResType,
|
|
TypeName );
|
|
|
|
if ( pResType == NULL )
|
|
{
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[FM] FmDeleteResourceType: Resource type %1!ws! does not exist...\n",
|
|
TypeName);
|
|
return( ERROR_CLUSTER_RESOURCE_TYPE_NOT_FOUND );
|
|
}
|
|
|
|
//
|
|
// Check if any resources of this type exist, we do this to avoid a GUM since at shutdown
|
|
// or startup we return ERROR_SUCCESS from the common GUM handler and that triggers
|
|
// the deletion of the type from the cluster database. Ideally, we should have made the
|
|
// API transactional and do this check inside GUM but for lack of time, this is the best
|
|
// we can do now.
|
|
//
|
|
OmEnumObjects( ObjectTypeResource,
|
|
FmpFindResourceType,
|
|
pResType,
|
|
&fResourceExists );
|
|
|
|
if ( fResourceExists )
|
|
{
|
|
OmDereferenceObject ( pResType );
|
|
return ( ERROR_DIR_NOT_EMPTY );
|
|
}
|
|
|
|
OmDereferenceObject ( pResType );
|
|
|
|
return(GumSendUpdate( GumUpdateFailoverManager,
|
|
FmUpdateDeleteResourceType,
|
|
(lstrlenW(TypeName)+1)*sizeof(WCHAR),
|
|
(PVOID)TypeName ));
|
|
} // FmDeleteResourceType
|
|
|
|
/****
|
|
@func DWORD | FmEnumResTypeNode | Enumerate the possible nodes for
|
|
a resource type
|
|
|
|
@parm IN PFM_RESTYPE | pResType | Pointer to the resource type
|
|
@parm IN DWORD | dwIndex | The index for this enumeration.
|
|
@parm OUT PNM_NODE | pPossibleNode | The possible node. The returned node
|
|
pointer will be referenced by this routine and should
|
|
be dereferenced when the caller is done with it.
|
|
|
|
@comm This routine helps enumerating all the nodes that a particular
|
|
resource type can be supported on.
|
|
|
|
@rdesc Returns a result code. ERROR_SUCCESS on success.
|
|
|
|
@xref
|
|
****/
|
|
DWORD
|
|
FmEnumResourceTypeNode(
|
|
IN PFM_RESTYPE pResType,
|
|
IN DWORD dwIndex,
|
|
OUT PNM_NODE *pPossibleNode
|
|
)
|
|
{
|
|
PLIST_ENTRY pListEntry;
|
|
DWORD i = 0;
|
|
PRESTYPE_POSSIBLE_ENTRY pResTypePosEntry;
|
|
DWORD Status = ERROR_NO_MORE_ITEMS;
|
|
|
|
FmpMustBeOnline();
|
|
|
|
//
|
|
// Chittur Subbaraman (chitturs) - 09/06/98
|
|
//
|
|
// The creation and deletion of resource types are done
|
|
// via atomic GUM operations. Hence these two operations
|
|
// (i.e. API's) are guaranteed to be mutually exclusive.
|
|
// In contrast, the resource type enumeration operation
|
|
// is not mutually exclusive with either the create
|
|
// or the delete operation. Thus, when a resource type is
|
|
// being created/deleted, there is nothing that prevents a
|
|
// client from trying to enumerate the same resource type
|
|
// in a concurrent fashion, thus producing a potential race
|
|
// condition. Thus, it is advisable to consider some form
|
|
// of locking to avoid this situation !
|
|
//
|
|
|
|
// update the list to include all nodes that now support
|
|
// the resource type
|
|
if (dwIndex == 0)
|
|
FmpSetPossibleNodeForResType(OmObjectId(pResType), TRUE);
|
|
|
|
ACQUIRE_SHARED_LOCK(gResTypeLock);
|
|
|
|
pListEntry = pResType->PossibleNodeList.Flink;
|
|
while (pListEntry != &pResType->PossibleNodeList) {
|
|
pResTypePosEntry = CONTAINING_RECORD(pListEntry,
|
|
RESTYPE_POSSIBLE_ENTRY,
|
|
PossibleLinkage);
|
|
if (i==dwIndex) {
|
|
//
|
|
// Got the right index
|
|
//
|
|
OmReferenceObject(pResTypePosEntry->PossibleNode);
|
|
*pPossibleNode = pResTypePosEntry->PossibleNode;
|
|
Status = ERROR_SUCCESS;
|
|
break;
|
|
}
|
|
pListEntry = pListEntry->Flink;
|
|
++i;
|
|
}
|
|
|
|
RELEASE_LOCK(gResTypeLock);
|
|
|
|
return(Status);
|
|
|
|
} // FmEnumResTypeNode
|
|
|
|
|
|
DWORD
|
|
FmChangeResourceGroup(
|
|
IN PFM_RESOURCE pResource,
|
|
IN PFM_GROUP pNewGroup
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Moves a resource from one group to another.
|
|
|
|
Arguments:
|
|
|
|
Resource - Supplies the resource to move.
|
|
|
|
Group - Supplies the new group that the resource should be in.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwStatus;
|
|
PFM_GROUP pOldGroup;
|
|
|
|
FmpMustBeOnline( );
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmChangeResourceGroup : Resource <%1!ws!> NewGroup %2!lx!\n",
|
|
OmObjectId( pResource ),
|
|
OmObjectId( pNewGroup));
|
|
|
|
//
|
|
// Synchronize both the old and the new groups.
|
|
// Lock the lowest by lowest Group Id first - to prevent deadlocks!
|
|
// Note - the order of release is unimportant.
|
|
//
|
|
// strictly, the comparison below cannot be equal!
|
|
//
|
|
if ( lstrcmpiW( OmObjectId( pResource->Group ), OmObjectId( pNewGroup ) ) <= 0 ) {
|
|
FmpAcquireLocalGroupLock( pResource->Group );
|
|
FmpAcquireLocalGroupLock( pNewGroup );
|
|
} else {
|
|
FmpAcquireLocalGroupLock( pNewGroup );
|
|
FmpAcquireLocalGroupLock( pResource->Group );
|
|
}
|
|
|
|
|
|
//remember the old group for freeing locks
|
|
pOldGroup = pResource->Group;
|
|
|
|
//if the resource has been marked for delete, then fail this call
|
|
if (!IS_VALID_FM_RESOURCE(pResource))
|
|
{
|
|
dwStatus = ERROR_RESOURCE_NOT_AVAILABLE;
|
|
goto FnUnlock;
|
|
}
|
|
|
|
|
|
//
|
|
// Check if we're moving to same group.
|
|
//
|
|
if (pResource->Group == pNewGroup) {
|
|
dwStatus = ERROR_ALREADY_EXISTS;
|
|
goto FnUnlock;
|
|
}
|
|
|
|
|
|
if ( pResource->Group->OwnerNode != NmLocalNode ) {
|
|
// Note: FmcChangeResourceNode must release the both resource lock.
|
|
dwStatus = FmcChangeResourceGroup( pResource, pNewGroup);
|
|
goto FnExit;
|
|
}
|
|
else
|
|
{
|
|
dwStatus = FmpChangeResourceGroup(pResource, pNewGroup );
|
|
}
|
|
|
|
|
|
FnUnlock:
|
|
FmpReleaseLocalGroupLock(pNewGroup);
|
|
FmpReleaseLocalGroupLock(pOldGroup);
|
|
FnExit:
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmChangeResourceGroup : returned <%1!u!>\r\n",
|
|
dwStatus);
|
|
return(dwStatus);
|
|
|
|
} // FmChangeResourceGroup
|
|
|
|
|
|
DWORD
|
|
FmChangeClusterName(
|
|
IN LPCWSTR pszNewName,
|
|
IN LPCWSTR pszOldName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Changes the name of the cluster
|
|
|
|
Arguments:
|
|
|
|
pszNewName - Supplies the new cluster name.
|
|
lpszOldName - Supplies the current name
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful. ERROR_RESOURCE_PROPERTIES STORED if the name
|
|
has been changed but wont be effective until the core network name resource
|
|
is brought online again.
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwStatus=ERROR_INVALID_PARAMETER;
|
|
BOOL bNameUpdated = FALSE;
|
|
PFM_RESOURCE pCoreNetNameResource = NULL;
|
|
PVOID pPropList = NULL;
|
|
DWORD dwPropListSize = 0;
|
|
LPWSTR pszBuffer;
|
|
DWORD dwLength;
|
|
DWORD dwClusterHighestVersion;
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmChangeClusterName : Entry NewName=%1!ws! OldName=%2!ws!\r\n",
|
|
pszNewName, pszOldName);
|
|
|
|
//get all the info to be able to change the name
|
|
dwStatus = FmpGetClusterNameChangeParams(pszNewName, &pCoreNetNameResource,
|
|
&pPropList, &dwPropListSize);
|
|
if (dwStatus != ERROR_SUCCESS)
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[FM] FmChangeClusterName : Failed to prepare params to set new name, Status=%1!u!\r\n",
|
|
dwStatus);
|
|
goto FnExit;
|
|
}
|
|
|
|
|
|
//first validate the name
|
|
dwStatus = FmpValidateCoreNetNameChange( pCoreNetNameResource, pPropList,
|
|
dwPropListSize);
|
|
if (dwStatus != ERROR_SUCCESS)
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[FM] FmChangeClusterName : Failed validate the name, Status=%1!u!\r\n",
|
|
dwStatus);
|
|
goto FnExit;
|
|
}
|
|
|
|
|
|
dwStatus = GumSendUpdateEx(GumUpdateFailoverManager,
|
|
FmUpdateChangeClusterName,
|
|
1,
|
|
(lstrlenW(pszNewName)+1)*sizeof(WCHAR),
|
|
pszNewName);
|
|
|
|
if (dwStatus != ERROR_SUCCESS)
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[FM] FmChangeClusterName : GumUpdate to set new name failed with staus=%1!u!\r\n",
|
|
dwStatus);
|
|
goto FnExit;
|
|
|
|
}
|
|
|
|
bNameUpdated = TRUE;
|
|
//Now notify the resource dll of the change via the set private properties call
|
|
//the resource dll will try to commit the change, if it fails it will undo the change
|
|
//and return failure
|
|
//SS : what if it not online
|
|
dwStatus = FmpCoreNetNameChange(pCoreNetNameResource, pPropList, dwPropListSize);
|
|
|
|
//the core network name property/cluster name has been set
|
|
//but the name change isnt effective till the resource is brought
|
|
//offline and then online again, the netname dll is expected to return the correct error
|
|
//ie we dont do any mapping
|
|
if ((dwStatus != ERROR_SUCCESS) && (dwStatus != ERROR_RESOURCE_PROPERTIES_STORED))
|
|
{
|
|
//if the resource properties have been stored or have been handled
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[FM] FmChangeClusterName : FmpCoreNetNameChange failed status=%1!u!\r\n",
|
|
dwStatus);
|
|
goto FnExit;
|
|
}
|
|
|
|
//the name change was successful generate the event notifications
|
|
//ignore the errors caused due to notifications, it is hard enough to
|
|
//recover from changes spanning the service and netname that at this point
|
|
//we wont bother with event notification problems
|
|
//dont issue the event notification in a mixed mode cluster..a bug in win2K
|
|
//cluster wide notification might result in an av
|
|
//
|
|
// If we are dealing with the mixed mode cluster or if the group does not have the antiaffinity
|
|
// property set, then don't do anything.
|
|
//
|
|
NmGetClusterOperationalVersion( &dwClusterHighestVersion,
|
|
NULL,
|
|
NULL );
|
|
|
|
if ( ( CLUSTER_GET_MAJOR_VERSION( dwClusterHighestVersion ) < NT51_MAJOR_VERSION ))
|
|
{
|
|
//there is a lower version(lower than whistler) node in the cluster
|
|
//we could issue a local event here but that doesnt help if the
|
|
//cluster has more than two nodes..who would issue the event on other
|
|
//whistler nodes?
|
|
goto FnExit;
|
|
}
|
|
else
|
|
{
|
|
ClusterWideEventEx(CLUSTER_EVENT_PROPERTY_CHANGE, EP_CONTEXT_VALID|EP_FREE_CONTEXT, (PVOID)pszNewName,
|
|
((lstrlenW(pszNewName) + 1) * sizeof(WCHAR)));
|
|
}
|
|
|
|
FnExit:
|
|
if (((dwStatus != ERROR_SUCCESS) && (dwStatus != ERROR_RESOURCE_PROPERTIES_STORED))
|
|
&& (bNameUpdated))
|
|
{
|
|
DWORD dwError;
|
|
|
|
//we need to revert the change, netname doesnt like it
|
|
//and doesnt return success or properties stored.
|
|
//netname should not set the registry to revert to the old name
|
|
//it should leave that to FM
|
|
//If the locker fails to revert it back(hopefully that doesnt happen
|
|
//often, then the admin needs to fix the problem since now we will
|
|
//have a new cluster name that perhaps wont ever come online
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[FM] FmChangeClusterName : Making the GumUpdate to revert to old name.\r\n");
|
|
|
|
dwError = GumSendUpdateEx(GumUpdateFailoverManager,
|
|
FmUpdateChangeClusterName,
|
|
1,
|
|
(lstrlenW(pszOldName)+1)*sizeof(WCHAR),
|
|
pszOldName);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[FM] FmChangeClusterName : GumUpdate to revert to old name failed, status=%1!u!\r\n",
|
|
dwError);
|
|
//log an ominous message in the event log to say the network name
|
|
//change didnt happen successfully but the name has been changed and this might require administrative
|
|
//action to fix the problems
|
|
CL_LOGCLUSWARNING(SERVICE_NETNAME_CHANGE_WARNING);
|
|
}
|
|
}
|
|
|
|
if (pCoreNetNameResource)
|
|
OmDereferenceObject(pCoreNetNameResource);
|
|
if (pPropList)
|
|
LocalFree(pPropList);
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmChangeClusterName : Exit dwStatus=%1!u!\r\n",
|
|
dwStatus);
|
|
|
|
return(dwStatus);
|
|
|
|
} // FmChangeClusterName
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
FmpSetResourceName(
|
|
IN PFM_RESOURCE pResource,
|
|
IN LPCWSTR lpszFriendlyName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Updates the resource name consistently in the fm databases across
|
|
the cluster.
|
|
|
|
Arguments:
|
|
|
|
pResource - The resource whose name is changed.
|
|
|
|
lpszFriendlyName - The new name of the resource.
|
|
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
|
|
A Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
LPCWSTR ResourceId;
|
|
DWORD Status;
|
|
|
|
ResourceId = OmObjectId(pResource);
|
|
|
|
return(GumSendUpdateEx( GumUpdateFailoverManager,
|
|
FmUpdateChangeResourceName,
|
|
2,
|
|
(lstrlenW(ResourceId)+1)*sizeof(WCHAR),
|
|
ResourceId,
|
|
(lstrlenW(lpszFriendlyName)+1)*sizeof(WCHAR),
|
|
lpszFriendlyName ));
|
|
|
|
} // FmpSetResourceName
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
FmpRegUpdateClusterName(
|
|
IN LPCWSTR szNewClusterName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine updates the cluster name in the cluster database.
|
|
|
|
Arguments:
|
|
|
|
szNewClusterName - A pointer to the new cluster name string.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
|
|
A Win32 error on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
return(DmSetValue( DmClusterParametersKey,
|
|
CLUSREG_NAME_CLUS_NAME,
|
|
REG_SZ,
|
|
(CONST BYTE *)szNewClusterName,
|
|
(lstrlenW(szNewClusterName)+1)*sizeof(WCHAR) ));
|
|
|
|
} // FmpRegUpdateClusterName
|
|
|
|
|
|
|
|
DWORD
|
|
FmEvictNode(
|
|
IN PNM_NODE Node
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Removes any references to the specified node that the FM might
|
|
have put on.
|
|
|
|
Arguments:
|
|
|
|
Node - Supplies the node that is being evicted.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
//add a reference to the node object, the worker thread will remove this
|
|
OmReferenceObject(Node);
|
|
FmpPostWorkItem(FM_EVENT_NODE_EVICTED,
|
|
Node,
|
|
0);
|
|
|
|
|
|
return(ERROR_SUCCESS);
|
|
|
|
} // FmEvictNode
|
|
|
|
|
|
|
|
BOOL
|
|
FmCheckNetworkDependency(
|
|
IN LPCWSTR DependentNetwork
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if any IP Address resource has a dependency on a given network.
|
|
|
|
Arguments:
|
|
|
|
DependentNetwork - the GUID for the network to check.
|
|
|
|
Return Value:
|
|
|
|
TRUE - if an IP Address resource depends on the given network.
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
return( FmpCheckNetworkDependency( DependentNetwork ) );
|
|
|
|
} // FmCheckNetworkDependency
|
|
|
|
DWORD
|
|
WINAPI
|
|
FmBackupClusterDatabase(
|
|
IN LPCWSTR lpszPathName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Attempts a backup of the quorum log files.
|
|
|
|
Arguments:
|
|
|
|
lpszPathName - The directory path name where the files have to be
|
|
backed up. This path must be visible to the node
|
|
on which the quorum resource is online.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
|
|
A Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
PFM_RESOURCE pQuoResource = NULL;
|
|
|
|
FmpMustBeOnline( );
|
|
|
|
//
|
|
// Chittur Subbaraman (chitturs) - 10/12/98
|
|
//
|
|
// Find the quorum resource
|
|
//
|
|
status = FmFindQuorumResource( &pQuoResource );
|
|
if ( status != ERROR_SUCCESS )
|
|
{
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[FM] FmBackupQuorumLog: Could not find quorum resource...\r\n");
|
|
goto FnExit;
|
|
}
|
|
//
|
|
// Acquire the local resource lock
|
|
//
|
|
FmpAcquireLocalResourceLock( pQuoResource );
|
|
|
|
//
|
|
// Handle the request here if this node is the owner of the
|
|
// quorum resource, else redirect it to the appropriate node.
|
|
//
|
|
if ( pQuoResource->Group->OwnerNode != NmLocalNode )
|
|
{
|
|
//
|
|
// This function will release the resource lock
|
|
//
|
|
status = FmcBackupClusterDatabase( pQuoResource, lpszPathName );
|
|
} else
|
|
{
|
|
status = FmpBackupClusterDatabase( pQuoResource, lpszPathName );
|
|
FmpReleaseLocalResourceLock( pQuoResource );
|
|
}
|
|
|
|
OmDereferenceObject ( pQuoResource );
|
|
|
|
FnExit:
|
|
return( status );
|
|
} // FmBackupClusterDatabase
|
|
|
|
DWORD
|
|
FmpBackupClusterDatabase(
|
|
IN PFM_RESOURCE pQuoResource,
|
|
IN LPCWSTR lpszPathName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine first waits until the quorum resource becomes
|
|
online. Then, it attempts to backup the quorum log file and the
|
|
checkpoint file to the specified directory path. This function
|
|
is called with the local resource lock held.
|
|
|
|
Arguments:
|
|
|
|
pQuoResource - Pointer to the quorum resource.
|
|
|
|
lpszPathName - The directory path name where the files have to be
|
|
backed up. This path must be visible to the node
|
|
on which the quorum resource is online.
|
|
|
|
Comments:
|
|
|
|
The order in which the locks are acquired is very crucial here.
|
|
Carelessness in following this strict order of acquisition can lead
|
|
to potential deadlocks. The order that is followed is
|
|
(1) Local resource lock - pQuoResource->Group->Lock acquired
|
|
outside this function.
|
|
(2) Global quorum resource lock - gQuoLock acquired here
|
|
(3) Global Dm root lock - gLockDmpRoot acquired in
|
|
DmBackupClusterDatabase( ).
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD retry = 200;
|
|
DWORD Status = ERROR_SUCCESS;
|
|
|
|
CL_ASSERT( pQuoResource->Group->OwnerNode == NmLocalNode );
|
|
|
|
//
|
|
// Chittur Subbaraman (chitturs) - 10/12/1998
|
|
//
|
|
// If quorum logging is not turned on, then log an error
|
|
// and exit immediately.
|
|
//
|
|
if ( CsNoQuorumLogging )
|
|
{
|
|
Status = ERROR_QUORUMLOG_OPEN_FAILED;
|
|
CL_LOGFAILURE( ERROR_QUORUMLOG_OPEN_FAILED );
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpBackupClusterDatabase: Quorum logging is not turned on, can't backup...\r\n");
|
|
goto FnExit;
|
|
}
|
|
|
|
CheckQuorumState:
|
|
ACQUIRE_EXCLUSIVE_LOCK( gQuoLock );
|
|
//
|
|
// Check the state of the quorum resource. If it has failed or is
|
|
// offline, release the lock and exit immediately !
|
|
//
|
|
if ( pQuoResource->State == ClusterResourceFailed )
|
|
{
|
|
Status = ERROR_QUORUM_RESOURCE_ONLINE_FAILED;
|
|
CL_LOGFAILURE( ERROR_QUORUM_RESOURCE_ONLINE_FAILED );
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpBackupClusterDatabase: Quorum resource is in failed state, exiting...\r\n");
|
|
RELEASE_LOCK( gQuoLock );
|
|
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 ( ( ( pQuoResource->State != ClusterResourceOnline ) &&
|
|
( ( pQuoResource->State != ClusterResourceOfflinePending ) ||
|
|
( !( pQuoResource->Flags & RESOURCE_WAITING ) ) ) )
|
|
)
|
|
{
|
|
//
|
|
// 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] FmpBackupClusterDatabase: Release ghQuoLock and wait on ghQuoOnlineEvent...\r\n");
|
|
Status = WaitForSingleObject( ghQuoOnlineEvent, 500 );
|
|
if ( Status == WAIT_OBJECT_0 )
|
|
{
|
|
//
|
|
// If we are going to retry, wait a little bit and retry.
|
|
//
|
|
Sleep( 500 );
|
|
}
|
|
if ( retry-- )
|
|
{
|
|
goto CheckQuorumState;
|
|
}
|
|
|
|
CL_LOGFAILURE( ERROR_QUORUM_RESOURCE_ONLINE_FAILED ) ;
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpBackupClusterDatabase: All retries to check for quorum resource online failed, exiting...\r\n");
|
|
return( ERROR_QUORUM_RESOURCE_ONLINE_FAILED );
|
|
}
|
|
|
|
Status = DmBackupClusterDatabase( lpszPathName );
|
|
|
|
RELEASE_LOCK( gQuoLock );
|
|
FnExit:
|
|
return ( Status );
|
|
} // FmpBackupClusterDatabase
|
|
|
|
|
|
|
|
/****
|
|
@func WORD| FmCheckQuorumState| If the quorum resource is online
|
|
on this node right now, it calls the callback and the boolean
|
|
value passed in is set to FALSE. If not, the boolean is
|
|
set to TRUE.
|
|
|
|
@parm LPWSTR | szQuorumLogPath | A pointer to a wide string of size MAX_PATH.
|
|
@parm DWORD | dwSize | The size of szQuorumLogPath in bytes.
|
|
|
|
@rdesc Returns ERROR_SUCCESS for success, else returns the error code.
|
|
|
|
@comm If the quorum resource is not cabaple of logging this should not be set.
|
|
@xref
|
|
****/
|
|
void FmCheckQuorumState(
|
|
FM_ONLINE_ONTHISNODE_CB OnlineOnThisNodeCb,
|
|
PBOOL pbOfflineOnThisNode)
|
|
{
|
|
BOOL bLocked = FALSE;
|
|
DWORD dwRetryCount = 1200; // Wait 10 min max
|
|
|
|
//
|
|
// SS: The mutual exclusion between this event handler and
|
|
// the synchronous resource online/offline callback is
|
|
// achieved by using the quorum change lock(gQuoChangeLock)
|
|
//
|
|
|
|
//
|
|
// Chittur Subbaraman (chitturs) - 7/5/99
|
|
//
|
|
// Modify group lock acquisition to release gQuoChangeLock and
|
|
// retry lock acquisition. This is necessary to take care of the
|
|
// case in which the quorum online notification is stuck in
|
|
// FmpHandleResourceTransition waiting for the gQuoChangeLock and
|
|
// some other resource in the quorum group is stuck in FmpRmOnlineResource
|
|
// holding the quorum group lock and waiting for the quorum resource
|
|
// to go online.
|
|
//
|
|
try_acquire_lock:
|
|
|
|
ACQUIRE_EXCLUSIVE_LOCK( gQuoChangeLock );
|
|
|
|
FmpTryAcquireLocalGroupLock( gpQuoResource->Group, bLocked );
|
|
|
|
if ( bLocked == FALSE )
|
|
{
|
|
RELEASE_LOCK( gQuoChangeLock );
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmCheckQuorumState - Release gQuoChangeLock, sleep and retry group lock acquisition...\r\n");
|
|
if ( dwRetryCount == 0 )
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[FM] FmCheckQuorumState - Unable to get quorum group lock for 10 min, halting...\r\n");
|
|
CsInconsistencyHalt( ERROR_LOCK_FAILED );
|
|
}
|
|
dwRetryCount --;
|
|
Sleep( 500 );
|
|
goto try_acquire_lock;
|
|
}
|
|
|
|
CL_ASSERT( bLocked == TRUE );
|
|
|
|
*pbOfflineOnThisNode = FALSE;
|
|
if (gpQuoResource->Group->OwnerNode == NmLocalNode)
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmCheckQuorumState - I am owner, check the state of the resource .\r\n");
|
|
|
|
//if the quorum resource is not online right now
|
|
//it might be in the middle of a move and this node
|
|
//might be the target of the move
|
|
//set a flag to indicate that a checkpoint is necessary
|
|
//when it does come online
|
|
if(gpQuoResource->State != ClusterResourceOnline)
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmCheckQuorumState - Quorum is owned but not online on this node.\r\n");
|
|
*pbOfflineOnThisNode = TRUE;
|
|
}
|
|
else
|
|
{
|
|
(*OnlineOnThisNodeCb)();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmCheckQuorumState - Quorum is owned by another node.\r\n");
|
|
*pbOfflineOnThisNode = TRUE;
|
|
}
|
|
|
|
FmpReleaseLocalGroupLock(gpQuoResource->Group);
|
|
|
|
RELEASE_LOCK(gQuoChangeLock);
|
|
}
|
|
|
|
/****
|
|
@func WORD| FmDoesQuorumAllowJoin| If the quorum resource doesnt support
|
|
multiple nodes, return error. Added to officially support local quorum resources.
|
|
|
|
@rdesc Returns ERROR_SUCCESS for success, else returns the error code.
|
|
|
|
@comm If the quorum resource is not cabaple of logging this should not be set.
|
|
@xref
|
|
****/
|
|
DWORD FmDoesQuorumAllowJoin(
|
|
IN PCWSTR pszJoinerNodeId )
|
|
{
|
|
|
|
|
|
DWORD dwStatus = ERROR_SUCCESS;
|
|
|
|
ACQUIRE_SHARED_LOCK(gQuoChangeLock);
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmDoesQuorumAllowJoin - Entry\r\n");
|
|
|
|
//get the characteristics for the new quorum resource
|
|
dwStatus = FmpGetResourceCharacteristics(gpQuoResource,
|
|
&(gpQuoResource->Characteristic));
|
|
if (dwStatus != ERROR_SUCCESS)
|
|
{
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[FM] FmDoesQuorumAllowJoin - couldnt get quorum characteristics %1!u!\r\n",
|
|
dwStatus);
|
|
goto FnExit;
|
|
}
|
|
|
|
if ((gpQuoResource->Characteristic & CLUS_CHAR_LOCAL_QUORUM) &&
|
|
!(gpQuoResource->Characteristic & CLUS_CHAR_LOCAL_QUORUM_DEBUG))
|
|
{
|
|
//Note :: we need an error code?
|
|
dwStatus = ERROR_OPERATION_ABORTED;
|
|
goto FnExit;
|
|
}
|
|
|
|
// JAF: RAID 513705: if a site is being started with ForceQuorum, and a 2nd site
|
|
// comes online automatically after, e.g., a power failure, they will attempt to join
|
|
// and will have a different idea of what constitutes a majority.
|
|
// Do not allow them to join if ForceQuorum is true.
|
|
if ( CsForceQuorum && ( CsForceQuorumNodes != NULL ))
|
|
{
|
|
if ( !FmpIsNodeInForceQuorumNodes( pszJoinerNodeId ))
|
|
{
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[FM] Disallowing join of %1!ws! since it is not in the list of forceQuorum nodes.\n",
|
|
pszJoinerNodeId );
|
|
dwStatus = ERROR_OPERATION_ABORTED;
|
|
goto FnExit;
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] Node %1!ws! is in the list of forceQuorum nodes; allowing join to proceed.\n",
|
|
pszJoinerNodeId );
|
|
}
|
|
}
|
|
|
|
|
|
FnExit:
|
|
RELEASE_LOCK(gQuoChangeLock);
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmDoesQuorumAllowJoin - Exit, Status=%1!u!\r\n",
|
|
dwStatus);
|
|
|
|
return(dwStatus);
|
|
}
|
|
|
|
|
|
/****
|
|
@func WORD| FmDoesQuorumAllowLogging| If the quorum resource doesnt support
|
|
multiple nodes, return error. Added to officially support local quorum resources.
|
|
|
|
@rdesc Returns ERROR_SUCCESS for success, else returns the error code.
|
|
|
|
@comm If the quorum resource is not cabaple of logging this should not be set.
|
|
@xref
|
|
****/
|
|
DWORD FmDoesQuorumAllowLogging(
|
|
IN DWORD dwQuorumResourceCharacteristics OPTIONAL
|
|
)
|
|
{
|
|
|
|
DWORD dwStatus = ERROR_SUCCESS;
|
|
|
|
ACQUIRE_SHARED_LOCK(gQuoChangeLock);
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmDoesQuorumAllowLogging - Entry\r\n");
|
|
|
|
if ( dwQuorumResourceCharacteristics == CLUS_CHAR_UNKNOWN )
|
|
{
|
|
//get the characteristics for the new quorum resource
|
|
dwStatus = FmpGetResourceCharacteristics(gpQuoResource,
|
|
&(gpQuoResource->Characteristic));
|
|
if (dwStatus != ERROR_SUCCESS)
|
|
{
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[FM] FmDoesQuorumAllowLogging - couldnt get quorum characteristics %1!u!\r\n",
|
|
dwStatus);
|
|
goto FnExit;
|
|
}
|
|
} else
|
|
{
|
|
gpQuoResource->Characteristic = dwQuorumResourceCharacteristics;
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmDoesQuorumAllowLogging - Using passed in characteristics 0x%1!08lx!\n",
|
|
dwQuorumResourceCharacteristics);
|
|
}
|
|
|
|
if (gpQuoResource->Characteristic & CLUS_CHAR_LOCAL_QUORUM)
|
|
{
|
|
WCHAR szQuorumFileName[MAX_PATH];
|
|
|
|
//Note :: we need an error code?
|
|
//if the path is an smb path name, we should allow logging
|
|
//else we should disable it
|
|
dwStatus = DmGetQuorumLogPath(szQuorumFileName, sizeof(szQuorumFileName));
|
|
if ((szQuorumFileName[0] == L'\\') && (szQuorumFileName[1] == L'\\'))
|
|
{
|
|
//assume this is an smb path
|
|
//allow logging
|
|
dwStatus = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
dwStatus = ERROR_OPERATION_ABORTED;
|
|
}
|
|
}
|
|
|
|
|
|
FnExit:
|
|
RELEASE_LOCK(gQuoChangeLock);
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmDoesQuorumAllowLogging - Exit, status=%1!u!\r\n",
|
|
dwStatus);
|
|
|
|
return(dwStatus);
|
|
}
|
|
|
|
DWORD
|
|
FmpCanonicalizePath(
|
|
IN OUT LPWSTR lpszPath,
|
|
OUT PBOOL pfIsPathUNC
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine converts a path of the form \\xyz\share to \\?\UNC\xyz\share.
|
|
|
|
Arguments:
|
|
|
|
lpszPathName - The path that must be converted.
|
|
|
|
pfIsPathUNC - Is the path of UNC type.
|
|
|
|
Return value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
DWORD dwStatus = ERROR_SUCCESS;
|
|
WCHAR szUNC[] = L"\\\\?\\UNC\\";
|
|
WCHAR szSlash[] = L"\\\\";
|
|
WCHAR szTempPath[MAX_PATH];
|
|
LPCWSTR lpszUNCStart, lpszSlashStart;
|
|
|
|
//
|
|
// Optimistically assume it is a UNC path. If it is not a UNC path, we set it back to
|
|
// FALSE. In a failure case, we don't care about the value of the boolean variable.
|
|
//
|
|
*pfIsPathUNC = TRUE;
|
|
|
|
//
|
|
// First check if the path starts with a "\\". If not, you are done.
|
|
//
|
|
lpszSlashStart = wcsstr( lpszPath, szSlash );
|
|
|
|
if ( lpszSlashStart == NULL )
|
|
{
|
|
*pfIsPathUNC = FALSE;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Next, make sure there are not more than 2 slashes any where in the path except at
|
|
// the start.
|
|
//
|
|
lpszSlashStart = wcsstr( lpszPath+1, szSlash );
|
|
|
|
if ( lpszSlashStart != NULL )
|
|
{
|
|
ClRtlLogPrint(LOG_ERROR, "[FM] FmpCanonicalizePath: Supplied path %1!ws! is invalid...\n",
|
|
lpszPath);
|
|
dwStatus = ERROR_INVALID_PARAMETER;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Next, check if the path already has UNC at the beginning. If so, you are done.
|
|
//
|
|
lpszUNCStart = wcsstr( lpszPath, szUNC );
|
|
|
|
if ( lpszUNCStart != NULL )
|
|
{
|
|
if ( lpszUNCStart != lpszPath )
|
|
{
|
|
ClRtlLogPrint(LOG_ERROR, "[FM] FmpCanonicalizePath: Supplied path %1!ws! is invalid...\n",
|
|
lpszPath);
|
|
dwStatus = ERROR_INVALID_PARAMETER;
|
|
}
|
|
goto FnExit;
|
|
}
|
|
|
|
lstrcpy( szTempPath, szUNC );
|
|
lstrcat( szTempPath, lpszPath+2 );
|
|
lstrcpy( lpszPath, szTempPath );
|
|
|
|
FnExit:
|
|
ClRtlLogPrint(LOG_NOISE, "[FM] FmpCanonicalizePath: Exit Status %1!u!, Return path = %2!ws!\n",
|
|
dwStatus,
|
|
lpszPath);
|
|
return ( dwStatus );
|
|
} // FmpCanonicalizePath
|
|
|
|
|
|
DWORD FmpGetClusterNameChangeParams(
|
|
IN LPCWSTR lpszNewName,
|
|
OUT PFM_RESOURCE *ppCoreNetNameResource,
|
|
OUT PVOID *ppPropList,
|
|
OUT LPDWORD pdwPropListSize
|
|
)
|
|
{
|
|
|
|
PFM_RESOURCE pResource = NULL;
|
|
DWORD dwSize = 0;
|
|
DWORD dwStatus;
|
|
LPWSTR lpszClusterNameResource = NULL;
|
|
CLUSPROP_BUFFER_HELPER ListEntry;
|
|
PVOID pPropList = NULL;
|
|
DWORD cbListSize = 0;
|
|
DWORD dwBufferSize;
|
|
|
|
//initialize the returns
|
|
*ppCoreNetNameResource = NULL;
|
|
*pdwPropListSize = 0;
|
|
*ppPropList = NULL;
|
|
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpGetClusterNameChangeParams: Entry !\n");
|
|
|
|
dwStatus = DmQuerySz( DmClusterParametersKey,
|
|
CLUSREG_NAME_CLUS_CLUSTER_NAME_RES,
|
|
&lpszClusterNameResource,
|
|
&dwSize,
|
|
&dwSize );
|
|
|
|
if ( dwStatus != ERROR_SUCCESS )
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[FM] FmpGetClusterNameChangeParams: Failed to get cluster name resource from registry, error %1!u!...\n",
|
|
dwStatus);
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Reference the specified resource ID.
|
|
//
|
|
pResource = OmReferenceObjectById( ObjectTypeResource,
|
|
lpszClusterNameResource );
|
|
|
|
if ( pResource == NULL )
|
|
{
|
|
dwStatus = ERROR_RESOURCE_NOT_FOUND;
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[FM] FmGetClusterNameChangeParams: Failed to find cluster name resource, %1!u!...\n",
|
|
dwStatus);
|
|
goto FnExit;
|
|
}
|
|
|
|
dwBufferSize = sizeof( ListEntry.pList->nPropertyCount ) +
|
|
sizeof( *ListEntry.pName ) +
|
|
ALIGN_CLUSPROP( ( lstrlenW( CLUSREG_NAME_NET_NAME ) + 1 ) * sizeof( WCHAR ) ) +
|
|
sizeof( *ListEntry.pStringValue ) +
|
|
ALIGN_CLUSPROP( ( lstrlenW( lpszNewName ) + 1 ) * sizeof( WCHAR ) ) +
|
|
sizeof( *ListEntry.pSyntax );
|
|
|
|
ListEntry.pb = (PBYTE) LocalAlloc( LPTR, dwBufferSize );
|
|
|
|
if ( ListEntry.pb == NULL )
|
|
{
|
|
dwStatus = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[FM] FmGetClusterNameChangeParams: Error %1!u! in allocating memory...\n",
|
|
dwStatus);
|
|
goto FnExit;
|
|
}
|
|
|
|
pPropList = ListEntry.pb;
|
|
|
|
ListEntry.pList->nPropertyCount = 1;
|
|
cbListSize += sizeof( ListEntry.pList->nPropertyCount );
|
|
ListEntry.pb += sizeof( ListEntry.pList->nPropertyCount );
|
|
|
|
ListEntry.pName->Syntax.dw = CLUSPROP_SYNTAX_NAME;
|
|
ListEntry.pName->cbLength = ( lstrlenW( CLUSREG_NAME_NET_NAME ) + 1 ) * sizeof( WCHAR );
|
|
lstrcpyW( ListEntry.pName->sz, CLUSREG_NAME_NET_NAME );
|
|
cbListSize += sizeof( *ListEntry.pName ) + ALIGN_CLUSPROP( ListEntry.pName->cbLength );
|
|
ListEntry.pb += sizeof( *ListEntry.pName ) + ALIGN_CLUSPROP( ListEntry.pName->cbLength );
|
|
|
|
ListEntry.pStringValue->Syntax.dw = CLUSPROP_SYNTAX_LIST_VALUE_SZ;
|
|
ListEntry.pStringValue->cbLength = ( lstrlenW( lpszNewName ) + 1 ) * sizeof( WCHAR );
|
|
lstrcpyW( ListEntry.pStringValue->sz, lpszNewName );
|
|
cbListSize += sizeof( *ListEntry.pStringValue ) + ALIGN_CLUSPROP( ListEntry.pName->cbLength );
|
|
ListEntry.pb += sizeof( *ListEntry.pStringValue ) + ALIGN_CLUSPROP( ListEntry.pName->cbLength );
|
|
|
|
ListEntry.pSyntax->dw = CLUSPROP_SYNTAX_ENDMARK;
|
|
cbListSize += sizeof( *ListEntry.pSyntax );
|
|
ListEntry.pb += sizeof( *ListEntry.pSyntax );
|
|
|
|
|
|
|
|
*ppCoreNetNameResource = pResource;
|
|
*ppPropList = pPropList;
|
|
*pdwPropListSize = dwBufferSize;
|
|
|
|
FnExit:
|
|
if (lpszClusterNameResource)
|
|
LocalFree(lpszClusterNameResource);
|
|
if (dwStatus != ERROR_SUCCESS)
|
|
{
|
|
if (pResource) OmDereferenceObject(pResource);
|
|
if (pPropList) LocalFree(pPropList);
|
|
}
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpGetClusterNameChangeParams: Exit !\n");
|
|
|
|
return(dwStatus);
|
|
}
|
|
|
|
|
|
|
|
|
|
/****
|
|
@func DWORD| FmpValideCoreNetNameChange| Send a control code to the core
|
|
netname resource to validate the change.
|
|
|
|
@param IN PFM_RESOURCE | pResource | A pointer to the core netname resource.
|
|
|
|
@param IN PVOID | pPropList | Pointer to a property list.
|
|
|
|
@param IN DWORD | cbListSize | Size of the property list.
|
|
|
|
@rdesc Returns ERROR_SUCCESS for success, else returns the error code.
|
|
|
|
@comm
|
|
@xref
|
|
****/
|
|
DWORD FmpValidateCoreNetNameChange(
|
|
IN PFM_RESOURCE pResource,
|
|
IN PVOID pPropList,
|
|
IN DWORD cbListSize
|
|
)
|
|
{
|
|
DWORD dwBytesReturned;
|
|
DWORD dwRequired;
|
|
DWORD dwStatus;
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpValidateCoreNetNameChange : Calling Core netname to validate\r\n");
|
|
|
|
dwStatus = FmResourceControl( pResource,
|
|
NULL,
|
|
CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES,
|
|
(PUCHAR)pPropList,
|
|
cbListSize,
|
|
NULL,
|
|
0,
|
|
&dwBytesReturned,
|
|
&dwRequired );
|
|
return(dwStatus);
|
|
|
|
}
|
|
|
|
/****
|
|
@func WORD| FmCoreNetNameChange| Asks the core network name resource
|
|
to perform all the network name change related operations.
|
|
|
|
@param IN PFM_RESOURCE | pResource | A pointer to the core netname resource.
|
|
|
|
@param IN PVOID | pPropList | Pointer to a property list.
|
|
|
|
@param IN DWORD | cbListSize | Size of the property list.
|
|
|
|
@rdesc Returns ERROR_SUCCESS for success, else returns the error code.
|
|
|
|
@comm
|
|
@xref
|
|
****/
|
|
DWORD FmpCoreNetNameChange(
|
|
IN PFM_RESOURCE pResource,
|
|
IN PVOID pPropList,
|
|
IN DWORD cbListSize
|
|
)
|
|
{
|
|
DWORD dwBytesReturned;
|
|
DWORD dwRequired;
|
|
DWORD dwStatus;
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpCoreNetNameChange : Invoking the core netname resource dll\r\n");
|
|
|
|
dwStatus = FmResourceControl( pResource,
|
|
NULL,
|
|
CLUSCTL_RESOURCE_CLUSTER_NAME_CHANGED,
|
|
(PUCHAR)pPropList,
|
|
cbListSize,
|
|
NULL,
|
|
0,
|
|
&dwBytesReturned,
|
|
&dwRequired );
|
|
return(dwStatus);
|
|
|
|
}
|
|
|
|
|