804 lines
18 KiB
C
804 lines
18 KiB
C
/*++
|
||
|
||
Copyright (c) 1996-1999 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
fmval.c
|
||
|
||
Abstract:
|
||
|
||
Cluster manager api validation/support routines.
|
||
|
||
Author:
|
||
|
||
Sunita Shrivastava (sunitas) 29-April-1999.
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "fmp.h"
|
||
|
||
#define LOG_MODULE FMVAL
|
||
|
||
////////////////////////////////////////////////////////
|
||
//
|
||
// Validation routines for Group operations.
|
||
//
|
||
////////////////////////////////////////////////////////
|
||
|
||
DWORD
|
||
FmpValOnlineGroup(
|
||
IN PFM_GROUP Group
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Validation routine before group is brought online.
|
||
|
||
Arguments:
|
||
|
||
Group - Supplies a pointer to the group structure to bring online.
|
||
|
||
Comments:
|
||
|
||
Is called with the localgroup lock held
|
||
|
||
Returns:
|
||
|
||
ERROR_SUCCESS if the validation is successful.
|
||
|
||
A Win32 error code if the validation fails.
|
||
|
||
--*/
|
||
{
|
||
DWORD dwStatus = ERROR_SUCCESS;
|
||
PLIST_ENTRY listEntry;
|
||
|
||
|
||
//if the group has been marked for delete, then fail this call
|
||
if (!IS_VALID_FM_GROUP(Group))
|
||
{
|
||
dwStatus = ERROR_GROUP_NOT_AVAILABLE;
|
||
goto FnExit;
|
||
}
|
||
|
||
//
|
||
// Make sure the owning node can run the group.
|
||
//
|
||
if ( !FmpInPreferredList( Group, Group->OwnerNode ) )
|
||
{
|
||
dwStatus = ERROR_CLUSTER_OWNER_NOT_IN_PREFLIST;
|
||
goto FnExit;
|
||
}
|
||
|
||
//
|
||
// Make sure the owning node is not paused.
|
||
//
|
||
if (NmGetNodeState(Group->OwnerNode) == ClusterNodePaused)
|
||
{
|
||
dwStatus = ERROR_SHARING_PAUSED;
|
||
goto FnExit;
|
||
}
|
||
|
||
FnExit:
|
||
return(dwStatus);
|
||
|
||
} // FmpValOnlineGroup
|
||
|
||
|
||
DWORD
|
||
FmpValMoveGroup(
|
||
IN PFM_GROUP Group,
|
||
IN PNM_NODE DestinationNode OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Validation routine for group move.
|
||
|
||
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 validation is successful.
|
||
|
||
A Win32 error code if the validation fails.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD dwStatus = ERROR_SUCCESS;
|
||
|
||
//if the group has been marked for delete, then fail this call
|
||
if (!IS_VALID_FM_GROUP(Group))
|
||
{
|
||
dwStatus = ERROR_GROUP_NOT_AVAILABLE;
|
||
goto FnExit;
|
||
}
|
||
|
||
if ( FmpIsGroupPending(Group) )
|
||
{
|
||
dwStatus = ERROR_GROUP_NOT_AVAILABLE;
|
||
goto FnExit;
|
||
}
|
||
|
||
if ( Group->OwnerNode == NULL )
|
||
{
|
||
dwStatus = ERROR_HOST_NODE_NOT_AVAILABLE;
|
||
goto FnExit;
|
||
}
|
||
|
||
FnExit:
|
||
return(dwStatus);
|
||
|
||
} // FmpValMoveGroup
|
||
|
||
////////////////////////////////////////////////////////
|
||
//
|
||
// Validation routines for resource operations
|
||
//
|
||
////////////////////////////////////////////////////////
|
||
|
||
DWORD
|
||
FmpValCreateResource(
|
||
IN PFM_GROUP Group,
|
||
IN LPWSTR ResourceId,
|
||
IN LPCWSTR ResourceName,
|
||
OUT PGUM_CREATE_RESOURCE *ppGumResource,
|
||
OUT PDWORD pdwBufSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Validation routine for resource creation.
|
||
|
||
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.
|
||
|
||
ppGumResource - Message buffer to hold resource info.
|
||
|
||
pdwBufSize - Message buffer size.
|
||
|
||
Returns:
|
||
|
||
ERROR_SUCCESS if the validation is successful.
|
||
|
||
A Win32 error code if the validation fails.
|
||
|
||
|
||
--*/
|
||
{
|
||
DWORD dwStatus = ERROR_SUCCESS;
|
||
PFM_RESOURCE Resource;
|
||
LPCWSTR GroupId;
|
||
PGUM_CREATE_RESOURCE GumResource;
|
||
DWORD GroupIdLen;
|
||
DWORD ResourceIdLen;
|
||
DWORD ResourceNameLen;
|
||
DWORD BufSize;
|
||
HDMKEY ResourceKey;
|
||
HDMKEY ParamsKey;
|
||
DWORD Disposition;
|
||
|
||
*ppGumResource = NULL;
|
||
*pdwBufSize = 0;
|
||
|
||
//
|
||
// First create the parameters field.
|
||
//
|
||
ResourceKey = DmOpenKey( DmResourcesKey,
|
||
ResourceId,
|
||
MAXIMUM_ALLOWED );
|
||
if ( ResourceKey == NULL )
|
||
{
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] CreateResource: Failed to open registry key for %1!ws!, status = %2!u!.\n",
|
||
ResourceId,
|
||
GetLastError() );
|
||
dwStatus = GetLastError();
|
||
goto FnExit;
|
||
}
|
||
|
||
ParamsKey = DmCreateKey( ResourceKey,
|
||
CLUSREG_KEYNAME_PARAMETERS,
|
||
0,
|
||
KEY_READ | KEY_WRITE,
|
||
NULL,
|
||
&Disposition );
|
||
if ( ParamsKey != NULL )
|
||
{
|
||
DmCloseKey( ParamsKey );
|
||
}
|
||
DmCloseKey( ResourceKey );
|
||
|
||
//
|
||
// 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);
|
||
BufSize = sizeof(GUM_CREATE_RESOURCE) - sizeof(WCHAR) +
|
||
GroupIdLen + ResourceIdLen + ResourceNameLen;
|
||
GumResource = LocalAlloc(LMEM_FIXED, BufSize);
|
||
if (GumResource == NULL) {
|
||
CsInconsistencyHalt( ERROR_NOT_ENOUGH_MEMORY );
|
||
dwStatus = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto FnExit;
|
||
}
|
||
|
||
//
|
||
// 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);
|
||
|
||
|
||
|
||
*ppGumResource = GumResource;
|
||
*pdwBufSize = BufSize;
|
||
|
||
FnExit:
|
||
return(dwStatus);
|
||
|
||
} // FmpValCreateResource
|
||
|
||
|
||
|
||
DWORD
|
||
FmpValDeleteResource(
|
||
IN PFM_RESOURCE pResource
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Validation routine for delete resource.
|
||
|
||
Arguments:
|
||
|
||
Resource - Supplies the resource to delete.
|
||
|
||
Returns:
|
||
|
||
ERROR_SUCCESS if the validation is successful.
|
||
|
||
A Win32 error code if the validation fails.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD dwStatus = ERROR_SUCCESS;
|
||
|
||
|
||
//
|
||
// Check if this is the quorum resource.
|
||
//
|
||
if ( pResource->QuorumResource )
|
||
{
|
||
dwStatus = ERROR_QUORUM_RESOURCE;
|
||
goto FnExit;
|
||
}
|
||
|
||
//other core resources cannot be deleted either
|
||
if (pResource->ExFlags & CLUS_FLAG_CORE)
|
||
{
|
||
dwStatus = ERROR_CORE_RESOURCE;
|
||
goto FnExit;
|
||
}
|
||
|
||
//
|
||
// Check the state of the resource, before attempting to delete it.
|
||
// It must be offline or failed in order to perform the delete.
|
||
//
|
||
if ((pResource->State != ClusterResourceOffline) &&
|
||
(pResource->State != ClusterResourceFailed))
|
||
{
|
||
dwStatus = ERROR_RESOURCE_ONLINE;
|
||
goto FnExit;
|
||
}
|
||
|
||
//
|
||
// Check whether this resource provides for any other resources.
|
||
// If so, it cannot be deleted.
|
||
//
|
||
if (!IsListEmpty(&pResource->ProvidesFor))
|
||
{
|
||
dwStatus = ERROR_DEPENDENT_RESOURCE_EXISTS;
|
||
goto FnExit;
|
||
}
|
||
|
||
if (pResource->Group->MovingList)
|
||
{
|
||
dwStatus = ERROR_INVALID_STATE;
|
||
goto FnExit;
|
||
}
|
||
|
||
FnExit:
|
||
return(dwStatus);
|
||
|
||
} // FmpValDeleteResource
|
||
|
||
|
||
DWORD
|
||
FmpValOnlineResource(
|
||
IN PFM_RESOURCE pResource
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine validates if a resource can be brought online.
|
||
|
||
Arguments:
|
||
|
||
Resource - A pointer to the resource to bring online.
|
||
|
||
Returns:
|
||
|
||
ERROR_SUCCESS if the validation is successful.
|
||
|
||
A Win32 error code if the validation fails.
|
||
|
||
--*/
|
||
{
|
||
|
||
DWORD dwStatus = ERROR_SUCCESS;
|
||
|
||
//if the resource has been marked for delete, then dont let
|
||
//it be brought online
|
||
if (!IS_VALID_FM_RESOURCE(pResource))
|
||
{
|
||
dwStatus = ERROR_RESOURCE_NOT_AVAILABLE;
|
||
goto FnExit;
|
||
}
|
||
|
||
//
|
||
// Check if the resource has been initialized. If not, attempt
|
||
// to initialize the resource now.
|
||
//
|
||
if ( pResource->Monitor == NULL )
|
||
{
|
||
dwStatus = FmpInitializeResource( pResource, TRUE );
|
||
}
|
||
|
||
FnExit:
|
||
return(dwStatus);
|
||
} // FmpValOnlineResource
|
||
|
||
|
||
DWORD
|
||
FmpValOfflineResource(
|
||
IN PFM_RESOURCE pResource
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine validates if a given resource can be taken offline.
|
||
|
||
Arguments:
|
||
|
||
Resource - A pointer to the resource to take offline.
|
||
|
||
Returns:
|
||
|
||
ERROR_SUCCESS if the validation is successful.
|
||
|
||
A Win32 error code if the validation fails.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD dwStatus = ERROR_SUCCESS;
|
||
|
||
|
||
//if the resource has been marked for delete, then fail this call
|
||
if (!IS_VALID_FM_RESOURCE(pResource))
|
||
{
|
||
dwStatus = ERROR_RESOURCE_NOT_AVAILABLE;
|
||
goto FnExit;
|
||
}
|
||
|
||
//
|
||
// Check if this is the quorum resource.
|
||
//
|
||
if ( pResource->QuorumResource )
|
||
{
|
||
dwStatus = ERROR_QUORUM_RESOURCE;
|
||
goto FnExit;
|
||
}
|
||
|
||
//
|
||
// Check if the resource has been initialized. If not, return
|
||
// success because the resource is not online.
|
||
//
|
||
if ( pResource->Monitor == NULL )
|
||
{
|
||
dwStatus = ERROR_SUCCESS;
|
||
goto FnExit;
|
||
}
|
||
|
||
//
|
||
// 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 ( pResource->State == ClusterResourceFailed )
|
||
{
|
||
dwStatus = ERROR_INVALID_STATE;
|
||
goto FnExit;
|
||
}
|
||
|
||
FnExit:
|
||
return(dwStatus);
|
||
|
||
} // FmpValOfflineResource
|
||
|
||
|
||
|
||
DWORD
|
||
FmpValAddResourceDependency(
|
||
IN PFM_RESOURCE pResource,
|
||
IN PFM_RESOURCE pDependentResource
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Validation routine for dependency addition.
|
||
|
||
Arguments:
|
||
|
||
Resource - The resource to add the dependent resource.
|
||
|
||
DependentResource - The dependent resource.
|
||
|
||
Returns:
|
||
|
||
ERROR_SUCCESS if the validation is successful.
|
||
|
||
A Win32 error code if the validation fails.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD dwStatus = ERROR_SUCCESS;
|
||
|
||
//if the resource has been marked for delete, then dont let
|
||
//it be brought online
|
||
if (!IS_VALID_FM_RESOURCE(pResource))
|
||
{
|
||
dwStatus = ERROR_RESOURCE_NOT_AVAILABLE;
|
||
goto FnExit;
|
||
}
|
||
|
||
if (pResource->QuorumResource)
|
||
{
|
||
dwStatus = ERROR_DEPENDENCY_NOT_ALLOWED;
|
||
goto FnExit;
|
||
}
|
||
//
|
||
// If the resources are not in the same group, fail the
|
||
// call. Also fail if some one tries to make a resource
|
||
// dependent upon itself.
|
||
//
|
||
if ((pResource->Group != pDependentResource->Group) ||
|
||
(pResource == pDependentResource))
|
||
{
|
||
dwStatus = ERROR_INVALID_PARAMETER;
|
||
goto FnExit;
|
||
}
|
||
|
||
// The resource to which the dependency is being added must be offline
|
||
// Otherwise, it looks like the dependency is in effect when the depending
|
||
// resource was not really brought online at the time the dependency existed
|
||
// must also be offline or failed.
|
||
// SS: For instance if a network name is dependent on two ip addresesses and
|
||
// is online and a third ip address resource dependency is added, the
|
||
// network name must be brought offline and online for the dependency
|
||
// to be truly in effect
|
||
//
|
||
if ((pResource->State != ClusterResourceOffline) &&
|
||
(pResource->State != ClusterResourceFailed))
|
||
{
|
||
dwStatus = ERROR_RESOURCE_ONLINE;
|
||
goto FnExit;
|
||
}
|
||
|
||
//
|
||
// Make sure that we don't have any circular dependencies!
|
||
//
|
||
if ( FmDependentResource( pDependentResource, pResource, FALSE ) )
|
||
{
|
||
dwStatus = ERROR_CIRCULAR_DEPENDENCY;
|
||
goto FnExit;
|
||
}
|
||
|
||
//
|
||
// Make sure that this dependency does not already exist!
|
||
//
|
||
if ( FmDependentResource(pResource, pDependentResource, TRUE))
|
||
{
|
||
dwStatus = ERROR_DEPENDENCY_ALREADY_EXISTS;
|
||
goto FnExit;
|
||
}
|
||
|
||
FnExit:
|
||
return(dwStatus);
|
||
|
||
} // FmpValAddResourceDependency
|
||
|
||
|
||
DWORD
|
||
FmpValChangeResourceNode(
|
||
IN PFM_RESOURCE pResource,
|
||
IN LPCWSTR pszNodeId,
|
||
IN BOOL bAdd,
|
||
OUT PGUM_CHANGE_POSSIBLE_NODE *ppGumChange,
|
||
OUT PDWORD pdwBufSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Validation routine for changing the possible owner node of a resource.
|
||
|
||
Arguments:
|
||
|
||
pResource - A pointer to the resource structure.
|
||
|
||
pszNodeId - A pointer to the node id
|
||
|
||
bAdd - Indicates add or remove
|
||
|
||
ppGumChange - Message buffer to hold the resource info
|
||
|
||
pdwBufSize - Size of the message buffer
|
||
|
||
Comments:
|
||
|
||
Lock must be held when this routine is called
|
||
|
||
Returns:
|
||
|
||
ERROR_SUCCESS if the validation is successful.
|
||
|
||
A Win32 error code if the validation fails.
|
||
|
||
--*/
|
||
{
|
||
DWORD dwStatus = ERROR_SUCCESS;
|
||
PLIST_ENTRY pListEntry;
|
||
PRESTYPE_POSSIBLE_ENTRY pResTypePosEntry = NULL;
|
||
BOOL bNodeSupportsResType = FALSE;
|
||
LPCWSTR pszResourceId;
|
||
DWORD dwResourceLen;
|
||
DWORD dwNodeLen;
|
||
DWORD dwBufSize;
|
||
PGUM_CHANGE_POSSIBLE_NODE pGumChange;
|
||
|
||
*ppGumChange = NULL;
|
||
*pdwBufSize = 0;
|
||
|
||
|
||
//if the resource has been marked for delete, then perform
|
||
//any operations on it
|
||
if (!IS_VALID_FM_RESOURCE(pResource))
|
||
{
|
||
dwStatus = ERROR_RESOURCE_NOT_AVAILABLE;
|
||
goto FnExit;
|
||
}
|
||
|
||
if ( pResource->QuorumResource )
|
||
{
|
||
dwStatus = ERROR_INVALID_OPERATION_ON_QUORUM;
|
||
goto FnExit;
|
||
}
|
||
|
||
//
|
||
// We can't allow the owner node to be removed if the state
|
||
// of the resource or the group is not offline or failed.
|
||
//
|
||
if ( !bAdd &&
|
||
(pszNodeId == OmObjectId(NmLocalNode)) &&
|
||
(((pResource->State != ClusterResourceOffline) &&
|
||
(pResource->State != ClusterResourceFailed)) ||
|
||
(FmpGetGroupState( pResource->Group, TRUE ) != ClusterGroupOffline)) )
|
||
{
|
||
dwStatus = ERROR_INVALID_STATE;
|
||
goto FnExit;
|
||
}
|
||
|
||
//make sure the node is on the list of possible nodes for this
|
||
// resource type
|
||
if (bAdd)
|
||
{
|
||
pListEntry = &(pResource->Type->PossibleNodeList);
|
||
for (pListEntry = pListEntry->Flink;
|
||
pListEntry != &(pResource->Type->PossibleNodeList);
|
||
pListEntry = pListEntry->Flink)
|
||
{
|
||
|
||
pResTypePosEntry = CONTAINING_RECORD(pListEntry, RESTYPE_POSSIBLE_ENTRY,
|
||
PossibleLinkage);
|
||
|
||
if (!lstrcmpW(OmObjectId(pResTypePosEntry->PossibleNode), pszNodeId))
|
||
{
|
||
bNodeSupportsResType = TRUE;
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
if (!bNodeSupportsResType)
|
||
{
|
||
dwStatus = ERROR_CLUSTER_RESTYPE_NOT_SUPPORTED;
|
||
goto FnExit;
|
||
}
|
||
}
|
||
|
||
pszResourceId = OmObjectId(pResource);
|
||
dwResourceLen = (lstrlenW(pszResourceId)+1)*sizeof(WCHAR);
|
||
|
||
dwNodeLen = (lstrlenW(pszNodeId)+1)*sizeof(WCHAR);
|
||
|
||
dwBufSize = sizeof(GUM_CHANGE_POSSIBLE_NODE) - sizeof(WCHAR) +
|
||
dwResourceLen + dwNodeLen;
|
||
pGumChange = LocalAlloc(LMEM_FIXED, dwBufSize);
|
||
if (pGumChange == NULL) {
|
||
CsInconsistencyHalt( ERROR_NOT_ENOUGH_MEMORY );
|
||
return(ERROR_NOT_ENOUGH_MEMORY);
|
||
}
|
||
|
||
pGumChange->ResourceIdLen = dwResourceLen;
|
||
CopyMemory(pGumChange->ResourceId, pszResourceId, dwResourceLen);
|
||
CopyMemory((PCHAR)pGumChange->ResourceId + dwResourceLen,
|
||
pszNodeId,
|
||
dwNodeLen);
|
||
|
||
|
||
*ppGumChange = pGumChange;
|
||
*pdwBufSize = dwBufSize;
|
||
|
||
FnExit:
|
||
return(dwStatus);
|
||
} // FmpValChangeResourceNode
|
||
|
||
|
||
DWORD
|
||
FmpValChangeResourceGroup(
|
||
IN PFM_RESOURCE pResource,
|
||
IN PFM_GROUP pNewGroup,
|
||
OUT PGUM_CHANGE_GROUP *ppGumChange,
|
||
OUT LPDWORD pdwBufSize)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Validation routine for changing a resource's group.
|
||
|
||
Arguments:
|
||
|
||
pResource - Pointer to the resource structure
|
||
|
||
pNewGroup - Pointer to the group to which the resource is moved to
|
||
|
||
ppGumChange - Message buffer to hold the resource info
|
||
|
||
pdwBufSize - Size of the message buffer
|
||
|
||
Comments:
|
||
|
||
Lock must be held when this routine is called
|
||
|
||
Returns:
|
||
|
||
ERROR_SUCCESS if validation is successful.
|
||
|
||
A Win32 error code otherwise.
|
||
|
||
--*/
|
||
{
|
||
DWORD dwBufSize;
|
||
LPCWSTR pszResourceId;
|
||
DWORD dwResourceLen;
|
||
LPCWSTR pszGroupId;
|
||
DWORD dwGroupLen;
|
||
DWORD dwStatus = ERROR_SUCCESS;
|
||
PGUM_CHANGE_GROUP pGumChange;
|
||
|
||
*pdwBufSize = 0;
|
||
*ppGumChange = NULL;
|
||
|
||
// we need to validate here as well
|
||
// this is called by the server side
|
||
// this will help avoid a gum call if things have changed
|
||
// since the request started from the originator
|
||
// and got to the server
|
||
//if the resource has been marked for delete, then fail this call
|
||
if (!IS_VALID_FM_RESOURCE(pResource))
|
||
{
|
||
dwStatus = ERROR_RESOURCE_NOT_AVAILABLE;
|
||
goto FnExit;
|
||
}
|
||
|
||
//
|
||
// Check if we're moving to same group.
|
||
//
|
||
if (pResource->Group == pNewGroup)
|
||
{
|
||
dwStatus = ERROR_ALREADY_EXISTS;
|
||
goto FnExit;
|
||
}
|
||
|
||
//
|
||
// For now... both Groups must be owned by the same node.
|
||
//
|
||
if ( pResource->Group->OwnerNode != pNewGroup->OwnerNode )
|
||
{
|
||
dwStatus = ERROR_HOST_NODE_NOT_GROUP_OWNER;
|
||
goto FnExit;
|
||
}
|
||
|
||
|
||
pszResourceId = OmObjectId(pResource);
|
||
dwResourceLen = (lstrlenW(pszResourceId)+1)*sizeof(WCHAR);
|
||
|
||
pszGroupId = OmObjectId(pNewGroup);
|
||
dwGroupLen = (lstrlenW(pszGroupId)+1)*sizeof(WCHAR);
|
||
|
||
dwBufSize = sizeof(GUM_CHANGE_GROUP) - sizeof(WCHAR) + dwResourceLen + dwGroupLen;
|
||
pGumChange = LocalAlloc(LMEM_FIXED, dwBufSize);
|
||
if (pGumChange == NULL) {
|
||
dwStatus = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto FnExit;
|
||
}
|
||
|
||
pGumChange->ResourceIdLen = dwResourceLen;
|
||
CopyMemory(pGumChange->ResourceId, pszResourceId, dwResourceLen);
|
||
CopyMemory((PCHAR)pGumChange->ResourceId + dwResourceLen,
|
||
pszGroupId,
|
||
dwGroupLen);
|
||
|
||
*ppGumChange = pGumChange;
|
||
*pdwBufSize = dwBufSize;
|
||
|
||
FnExit:
|
||
return(dwStatus);
|
||
} // FmpValChangeResourceGroup
|
||
|