/*++ Copyright (c) 1996 Microsoft Corporation Module Name: group.c Abstract: Server side support for Cluster APIs dealing with groups Author: John Vert (jvert) 7-Mar-1996 Revision History: --*/ #include "apip.h" HGROUP_RPC s_ApiOpenGroup( IN handle_t IDL_handle, IN LPCWSTR lpszGroupName, OUT error_status_t *Status ) /*++ Routine Description: Opens a handle to an existing group object. Arguments: IDL_handle - RPC binding handle, not used. lpszGroupName - Supplies the name of the group to open. Status - Returns any error that may occur. Return Value: A context handle to a group object if successful NULL otherwise. --*/ { PAPI_HANDLE Handle; HGROUP_RPC Group; if (ApiState != ApiStateOnline) { *Status = ERROR_SHARING_PAUSED; return(NULL); } Handle = LocalAlloc(LMEM_FIXED, sizeof(API_HANDLE)); if (Handle == NULL) { *Status = ERROR_NOT_ENOUGH_MEMORY; return(NULL); } Group = OmReferenceObjectByName(ObjectTypeGroup, lpszGroupName); if (Group == NULL) { LocalFree(Handle); *Status = ERROR_GROUP_NOT_FOUND; return(NULL); } Handle->Type = API_GROUP_HANDLE; Handle->Flags = 0; Handle->Group = Group; InitializeListHead(&Handle->NotifyList); *Status = ERROR_SUCCESS; return(Handle); } HGROUP_RPC s_ApiCreateGroup( IN handle_t IDL_handle, IN LPCWSTR lpszGroupName, OUT error_status_t *pStatus ) /*++ Routine Description: Creates a new group object. Arguments: IDL_handle - RPC binding handle, not used. lpszGroupName - Supplies the name of the group to create. Status - Returns any error that may occur. Return Value: A context handle to a group object if successful NULL otherwise. --*/ { HGROUP_RPC Group=NULL; UUID Guid; DWORD Status = ERROR_SUCCESS; WCHAR *KeyName=NULL; PAPI_HANDLE Handle; DWORD dwDisposition; HDMKEY hKey = NULL; if (ApiState != ApiStateOnline) { *pStatus = ERROR_SHARING_PAUSED; return(NULL); } Handle = LocalAlloc(LMEM_FIXED, sizeof(API_HANDLE)); if (Handle == NULL) { *pStatus = ERROR_NOT_ENOUGH_MEMORY; return(NULL); } retry: // // // Create a GUID for this group. // Status = UuidCreate(&Guid); if (Status != RPC_S_OK) { goto error_exit; } Status = UuidToString(&Guid, &KeyName); if (Status != RPC_S_OK) { goto error_exit; } // // Create this group in the FM. This will also trigger the notification. // Group = FmCreateGroup(KeyName, lpszGroupName); if (Group == NULL) { Status = GetLastError(); if (Status == ERROR_ALREADY_EXISTS) { RpcStringFree(&KeyName); goto retry; } } error_exit: if (KeyName != NULL) { RpcStringFree(&KeyName); } *pStatus = Status; if (Status == ERROR_SUCCESS) { CL_ASSERT(Group != NULL); Handle->Type = API_GROUP_HANDLE; Handle->Group = Group; Handle->Flags = 0; InitializeListHead(&Handle->NotifyList); return(Handle); } else { LocalFree(Handle); return(NULL); } } error_status_t s_ApiDeleteGroup( IN HGROUP_RPC hGroup ) /*++ Routine Description: Deletes a cluster group. The group must contain no resources. Arguments: hGroup - Supplies the group to delete. Return Value: ERROR_SUCCESS if successful. Win32 error code otherwise. --*/ { DWORD Status; PFM_GROUP Group; HDMKEY Key; API_ASSERT_INIT(); VALIDATE_GROUP_EXISTS(Group, hGroup); Status = FmDeleteGroup(Group); if (Status == ERROR_SUCCESS) { DmDeleteTree(DmGroupsKey,OmObjectId(Group)); } return(Status); } error_status_t s_ApiCloseGroup( IN OUT HGROUP_RPC *phGroup ) /*++ Routine Description: Closes an open group context handle. Arguments: Group - Supplies a pointer to the HGROUP_RPC to be closed. Returns NULL Return Value: None. --*/ { PFM_GROUP Group; PAPI_HANDLE Handle; API_ASSERT_INIT(); VALIDATE_GROUP(Group, *phGroup); Handle = (PAPI_HANDLE)*phGroup; ApipRundownNotify(Handle); OmDereferenceObject(Group); LocalFree(Handle); *phGroup = NULL; return(ERROR_SUCCESS); } VOID HGROUP_RPC_rundown( IN HGROUP_RPC Group ) /*++ Routine Description: RPC rundown procedure for a HGROUP_RPC. Just closes the handle. Arguments: Group - Supplies the HGROUP_RPC that is to be rundown. Return Value: None. --*/ { API_ASSERT_INIT(); s_ApiCloseGroup(&Group); } error_status_t s_ApiGetGroupState( IN HGROUP_RPC hGroup, OUT DWORD *lpState, OUT LPWSTR *lpNodeName ) /*++ Routine Description: Returns the current state of the specified group. Arguments: hGroup - Supplies the group whose state is to be returned. lpState - Returns the current state of the group lpNodeName - Returns the name of the node where the group is currently online Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { PFM_GROUP Group; LPWSTR NodeName; DWORD NameLength; API_ASSERT_INIT(); VALIDATE_GROUP_EXISTS(Group, hGroup); NameLength = MAX_COMPUTERNAME_LENGTH+1; NodeName = MIDL_user_allocate(NameLength*sizeof(WCHAR)); if (NodeName == NULL) { return(ERROR_NOT_ENOUGH_MEMORY); } *lpState = FmGetGroupState( Group, NodeName, &NameLength); if ( *lpState == ClusterGroupStateUnknown ) { MIDL_user_free(NodeName); return(GetLastError()); } *lpNodeName = NodeName; return( ERROR_SUCCESS ); } error_status_t s_ApiSetGroupName( IN HGROUP_RPC hGroup, IN LPCWSTR lpszGroupName ) /*++ Routine Description: Sets the new friendly name of a group. Arguments: hGroup - Supplies the group whose name is to be set. lpszGroupName - Supplies the new name of hGroup Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { PFM_GROUP Group; HDMKEY GroupKey; DWORD Status; API_ASSERT_INIT(); VALIDATE_GROUP_EXISTS(Group, hGroup); // // Tell the FM about the new name. If it is OK with the // FM, go ahead and update the registry. // Status = FmSetGroupName(Group, lpszGroupName); if (Status == ERROR_SUCCESS) { GroupKey = DmOpenKey(DmGroupsKey, OmObjectId(Group), KEY_SET_VALUE); if (GroupKey == NULL) { return(GetLastError()); } Status = DmSetValue(GroupKey, CLUSREG_NAME_GRP_NAME, REG_SZ, (CONST BYTE *)lpszGroupName, (lstrlenW(lpszGroupName)+1)*sizeof(WCHAR)); DmCloseKey(GroupKey); } return(Status); } error_status_t s_ApiGetGroupId( IN HGROUP_RPC hGroup, OUT LPWSTR *pGuid ) /*++ Routine Description: Returns the unique identifier (GUID) for a group. Arguments: hGroup - Supplies the group whose identifer is to be returned pGuid - Returns the unique identifier. This memory must be freed on the client side. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise. --*/ { PFM_GROUP Group; DWORD IdLen; LPCWSTR Id; API_ASSERT_INIT(); VALIDATE_GROUP_EXISTS(Group, hGroup); Id = OmObjectId(Group); IdLen = (lstrlenW(Id)+1)*sizeof(WCHAR); *pGuid = MIDL_user_allocate(IdLen); if (*pGuid == NULL) { return(ERROR_NOT_ENOUGH_MEMORY); } CopyMemory(*pGuid, Id, IdLen); return(ERROR_SUCCESS); } DWORD s_ApiOnlineGroup( IN HGROUP_RPC hGroup ) /*++ Routine Description: Brings a group and all its dependencies online Arguments: hGroup - Supplies the group to be brought online Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { PFM_GROUP Group; API_ASSERT_INIT(); VALIDATE_GROUP_EXISTS(Group, hGroup); return(FmOnlineGroup(Group)); } DWORD s_ApiOfflineGroup( IN HGROUP_RPC hGroup ) /*++ Routine Description: Brings a group and all its dependents offline Arguments: hGroup - Supplies the group to be brought offline Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { PFM_GROUP Group; API_ASSERT_INIT(); VALIDATE_GROUP_EXISTS(Group, hGroup); return(FmOfflineGroup(Group)); } DWORD s_ApiMoveGroup( IN HGROUP_RPC hGroup ) /*++ Routine Description: Moves a group and all its dependents to another system. Arguments: hGroup - Supplies the group to be moved Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { PFM_GROUP Group; API_ASSERT_INIT(); VALIDATE_GROUP_EXISTS(Group, hGroup); return(FmMoveGroup(Group, NULL)); } DWORD s_ApiMoveGroupToNode( IN HGROUP_RPC hGroup, IN HNODE_RPC hNode ) /*++ Routine Description: Moves a group and all its dependents to another system. Arguments: hGroup - Supplies the group to be moved hNode - Supplies the node to move the group to Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { PFM_GROUP Group; PNM_NODE Node; API_ASSERT_INIT(); VALIDATE_GROUP_EXISTS(Group, hGroup); VALIDATE_NODE(Node, hNode); return(FmMoveGroup(Group, Node)); } error_status_t s_ApiSetGroupNodeList( IN HGROUP_RPC hGroup, IN UCHAR *lpNodeList, IN DWORD cbListSize ) /*++ Routine Description: Sets the list of preferred nodes for a group. Arguments: hGroup - Supplies the group to set the preferred nodes. lpNodeList - Supplies the list of preferred owners, as a REG_MULTI_SZ. cbListSize - Supplies the size in bytes of the preferred owners list. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { PFM_GROUP Group; HDMKEY GroupKey; DWORD Status; LPWSTR lpszTemp = ( LPWSTR ) lpNodeList; DWORD cchListSize = cbListSize / sizeof ( WCHAR ); API_ASSERT_INIT(); // // Make sure the node list is formatted as a MULTI_SZ (and if it is empty it should // have at least a single NULL). Don't rely on clusapi.dll doing this since a hacker may be able to // bypass clusapi.dll (even though we make that hard with security callback). Also, since // lpNodeList is declared as a byte buffer in the IDL file, RPC won't help you with // anything (except make sure cbListSize is allocated.) // if ( ( lpNodeList == NULL ) || ( cbListSize % sizeof ( WCHAR ) != 0 ) || // User passed in a size not a multiple of WCHAR ( cchListSize < 1 ) || // List is of zero length ( lpszTemp[ cchListSize - 1 ] != UNICODE_NULL ) || // Last char is not NULL ( ( cchListSize > 1 ) && // List has at least 2 elements and last but one is not NULL ( lpszTemp[ cchListSize - 2 ] != UNICODE_NULL ) ) ) { return ERROR_INVALID_PARAMETER; } VALIDATE_GROUP_EXISTS(Group, hGroup); // // Set the registry with the REG_MULTI_SZ. Let the FM pick it up from // there. // GroupKey = DmOpenKey(DmGroupsKey, OmObjectId(Group), KEY_SET_VALUE); if (GroupKey == NULL) { return(GetLastError()); } Status = DmSetValue(GroupKey, CLUSREG_NAME_GRP_PREFERRED_OWNERS, REG_MULTI_SZ, (CONST BYTE *)lpNodeList, cbListSize); DmCloseKey(GroupKey); return(Status); }