/*++ Copyright (c) 1996-1999 Microsoft Corporation Module Name: nmver.c Abstract: Version management functions used by rolling upgrade. Author: Sunita Shrivastava (sunitas) Revision History: 1/29/98 Created. --*/ #include "nmp.h" error_status_t s_CsRpcGetJoinVersionData( handle_t handle, DWORD JoiningNodeId, DWORD JoinerHighestVersion, DWORD JoinerLowestVersion, LPDWORD SponsorNodeId, LPDWORD ClusterHighestVersion, LPDWORD ClusterLowestVersion, LPDWORD JoinStatus ) /*++ Routine Description: Get from and supply to the joiner, version information about the sponsor. Mostly a no-op for first version. Arguments: A pile... Return Value: None --*/ { *SponsorNodeId = NmLocalNodeId; NmpAcquireLock(); if (JoiningNodeId == 0) { //called by setup join *ClusterHighestVersion = CsClusterHighestVersion; *ClusterLowestVersion = CsClusterLowestVersion; //dont exclude any node for version calculation and checking *JoinStatus = NmpIsNodeVersionAllowed(ClusterInvalidNodeId, JoinerHighestVersion, JoinerLowestVersion, TRUE); } else { //called by regular join //SS: we should verify this against the cluster version NmpCalcClusterVersion( JoiningNodeId, ClusterHighestVersion, ClusterLowestVersion ); *JoinStatus = NmpIsNodeVersionAllowed(JoiningNodeId, JoinerHighestVersion, JoinerLowestVersion, TRUE); } NmpReleaseLock(); return ERROR_SUCCESS; } /**** @func HLOG | NmGetClusterOperationalVersion| This returns the operational version for the cluster. @parm LPDWORD | pdwClusterHighestVersion | A pointer to a DWORD where the Cluster Highest Version is returned. @parm LPDWORD | pdwClusterHighestVersion | A pointer to a DWORD where the Cluster Lowest Version is returned. @parm LPDWORD | pdwFlags | A pointer to a DWORD where the flags describing the cluster mode(pure vs fixed version etc) are returned. @rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure. @comm @xref <> ****/ DWORD NmGetClusterOperationalVersion( OUT LPDWORD pdwClusterHighestVersion, OPTIONAL OUT LPDWORD pdwClusterLowestVersion, OPTIONAL OUT LPDWORD pdwFlags OPTIONAL ) { DWORD dwStatus = ERROR_SUCCESS; DWORD flags = 0; //acquire the lock, we are going to be messing with the operational //versions for the cluster NmpAcquireLock(); if (pdwClusterHighestVersion != NULL) { *pdwClusterHighestVersion = CsClusterHighestVersion; } if (pdwClusterLowestVersion != NULL) { *pdwClusterLowestVersion = CsClusterLowestVersion; } if (CsClusterHighestVersion == CsClusterLowestVersion) { //this is a mixed mode cluster, with the possible exception of //nt 4 release(which didnt quite understand anything about rolling //upgrades flags = CLUSTER_VERSION_FLAG_MIXED_MODE; } NmpReleaseLock(); if (pdwFlags != NULL) { *pdwFlags = flags; } return (ERROR_SUCCESS); } /**** @func HLOG | NmpResetClusterVersion| An operational version of the cluster is maintained in the service. This function recalculates the operation version. The operational version describes the mode in which the cluster is running and prevents nodes which are two versions away from running in the same cluster. @rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure. @comm This function is called when a node forms a cluster(to initialize the operational version) OR when a node joins a cluster (to initialize its version) OR when a node is ejected from a cluster(to recalculate the clusterversion). @xref <> ****/ VOID NmpResetClusterVersion( BOOL ProcessChanges ) { PNM_NODE pNmNode; //acquire the lock, we are going to be messing with the operational //versions for the cluster NmpAcquireLock(); //initialize the clusterhighestverion and clusterlowest version NmpCalcClusterVersion( ClusterInvalidNodeId, &CsClusterHighestVersion, &CsClusterLowestVersion ); ClRtlLogPrint(LOG_NOISE, "[NM] [NmpResetClusterVersion] ClusterHighestVer=0x%1!08lx! ClusterLowestVer=0x%2!08lx!\r\n", CsClusterHighestVersion, CsClusterLowestVersion ); if (ProcessChanges) { // // If the cluster operational version changed, adjust // algorithms and data as needed. // NmpProcessClusterVersionChange(); } NmpReleaseLock(); return; } /**** @func HLOG | NmpValidateNodeVersion| The sponsor validates that the version of the joiner is still the same as before. @parm IN LPWSTR| NodeJoinerId | The Id of the node that is trying to join. @parm IN DWORD | NodeHighestVersion | The highest version with which this node can communicate. @parm IN DWORD | NodeLowestVersion | The lowest version with which this node can communicate. @rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure. @comm This function is called at join time to make sure that the joiner's version is still the same as when he last joined. Due to uninstalls/upgrade, the cluster service version may change on a node. Usually on a complete uninstall, one is expected to evict the node out before it may join again. @xref <> ****/ DWORD NmpValidateNodeVersion( IN LPCWSTR NodeId, IN DWORD dwHighestVersion, IN DWORD dwLowestVersion ) { DWORD dwStatus = ERROR_SUCCESS; PNM_NODE pNmNode = NULL; ClRtlLogPrint(LOG_NOISE, "[NM] NmpValidateNodeVersion: Node=%1!ws!, HighestVersion=0x%2!08lx!, LowestVersion=0x%3!08lx!\r\n", NodeId, dwHighestVersion, dwLowestVersion); //acquire the NmpLocks, we will be examining the node structure for // the joiner node NmpAcquireLock(); pNmNode = OmReferenceObjectById(ObjectTypeNode, NodeId); if (!pNmNode) { dwStatus = ERROR_CLUSTER_NODE_NOT_MEMBER; goto FnExit; } if ((pNmNode->HighestVersion != dwHighestVersion) || (pNmNode->LowestVersion != dwLowestVersion)) { dwStatus = ERROR_REVISION_MISMATCH; goto FnExit; } FnExit: if (pNmNode) OmDereferenceObject(pNmNode); ClRtlLogPrint(LOG_NOISE, "[NM] NmpValidateNodeVersion: returns %1!u!\r\n", dwStatus); NmpReleaseLock(); return(dwStatus); } /**** @func DWORD | NmpFormFixupNodeVersion| This may be called by a node when it is forming a cluster to fix the registry reflect its correct version. @parm IN LPCWSTR| NodeId | The Id of the node that is trying to join. @parm IN DWORD | dwHighestVersion | The highest version of the cluster s/w running on this code. @parm IN DWORD | dwLowestVersion | The lowest version of the cluster s/w running on this node. @rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure. @comm If on a form, there is a mismatch between the versions of the cluster s/w and what is recorded as the version in the cluster database, the forming node checks to see if the version of its current s/w is compatible with the operational version of the cluster. If so, it resets the registry to reflect the correct version. Else, the form is aborted. @xref ****/ DWORD NmpFormFixupNodeVersion( IN LPCWSTR NodeId, IN DWORD dwHighestVersion, IN DWORD dwLowestVersion ) { DWORD dwStatus = ERROR_SUCCESS; PNM_NODE pNmNode = NULL; HDMKEY hNodeKey = NULL; //acquire the NmpLocks, we will be fixing up the node structure for // the joiner node NmpAcquireLock(); ClRtlLogPrint(LOG_NOISE, "[NM] NmpFormFixupNodeVersion: Node=%1!ws! to HighestVer=0x%2!08lx!, LowestVer=0x%3!08lx!\r\n", NodeId, dwHighestVersion, dwLowestVersion); pNmNode = OmReferenceObjectById(ObjectTypeNode, NodeId); if (!pNmNode) { dwStatus = ERROR_CLUSTER_NODE_NOT_MEMBER; goto FnExit; } hNodeKey = DmOpenKey(DmNodesKey, NodeId, KEY_WRITE); if (hNodeKey == NULL) { dwStatus = GetLastError(); ClRtlLogPrint(LOG_CRITICAL, "[NM] NmpFormFixupNodeVersion: Failed to open node key, status %1!u!\n", dwStatus); CL_LOGFAILURE(dwStatus); goto FnExit; } //set the node's highest version dwStatus = DmSetValue(hNodeKey, CLUSREG_NAME_NODE_HIGHEST_VERSION, REG_DWORD, (LPBYTE)&dwHighestVersion, sizeof(DWORD)); if (dwStatus != ERROR_SUCCESS) { ClRtlLogPrint(LOG_CRITICAL, "[NM] NmpFormFixupNodeVersion: Failed to set the highest version\r\n"); CL_LOGFAILURE(dwStatus); goto FnExit; } //set the node's lowest version dwStatus = DmSetValue(hNodeKey, CLUSREG_NAME_NODE_LOWEST_VERSION, REG_DWORD, (LPBYTE)&dwLowestVersion, sizeof(DWORD)); if (dwStatus != ERROR_SUCCESS) { ClRtlLogPrint(LOG_CRITICAL, "[NM] NmpFormFixupNodeVersion: Failed to set the lowest version\r\n"); CL_LOGFAILURE(dwStatus); goto FnExit; } pNmNode->HighestVersion = dwHighestVersion; pNmNode->LowestVersion = dwLowestVersion; FnExit: NmpReleaseLock(); if (pNmNode) OmDereferenceObject(pNmNode); if (hNodeKey != NULL) DmCloseKey(hNodeKey); return(dwStatus); } /**** @func DWORD | NmpJoinFixupNodeVersion| This may be called by a node when it is forming a cluster to fix the registry reflect its correct version. @parm IN LPCWSTR| NodeId | The Id of the node that is trying to join. @parm IN DWORD | dwHighestVersion | The highest version of this cluster s/w running on this code. @parm IN DWORD | dwLowestVersion | The lowest version of the cluster s/w running on this node. @rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure. @comm If on a form, their is a mismatch between the versions of the cluster s/w and what is recorded as the version in the cluster database, the forming node checks to see if the version of its current s/w compatible with the operational version of the cluster. If so, it resets the registry to reflect the correct version. Else, the form is aborted. @xref ****/ DWORD NmpJoinFixupNodeVersion( IN HLOCALXSACTION hXsaction, IN LPCWSTR szNodeId, IN DWORD dwHighestVersion, IN DWORD dwLowestVersion ) { DWORD dwStatus = ERROR_SUCCESS; PNM_NODE pNmNode = NULL; HDMKEY hNodeKey = NULL; //acquire the NmpLocks, we will be fixing up the node structure for // the joiner node NmpAcquireLock(); ClRtlLogPrint(LOG_NOISE, "[NM] NmpJoinFixupNodeVersion: Node=%1!ws! to HighestVer=0x%2!08lx!, LowestVer=0x%3!08lx!\r\n", szNodeId, dwHighestVersion, dwLowestVersion); pNmNode = OmReferenceObjectById(ObjectTypeNode, szNodeId); if (!pNmNode) { dwStatus = ERROR_CLUSTER_NODE_NOT_MEMBER; goto FnExit; } hNodeKey = DmOpenKey(DmNodesKey, szNodeId, KEY_WRITE); if (hNodeKey == NULL) { dwStatus = GetLastError(); ClRtlLogPrint(LOG_CRITICAL, "[NM] NmpJoinFixupNodeVersion: Failed to open node key, status %1!u!\n", dwStatus); CL_LOGFAILURE(dwStatus); goto FnExit; } //set the node's highest version dwStatus = DmLocalSetValue( hXsaction, hNodeKey, CLUSREG_NAME_NODE_HIGHEST_VERSION, REG_DWORD, (LPBYTE)&dwHighestVersion, sizeof(DWORD) ); if (dwStatus != ERROR_SUCCESS) { ClRtlLogPrint(LOG_CRITICAL, "[NM] NmpJoinFixupNodeVersion: Failed to set the highest version\r\n" ); CL_LOGFAILURE(dwStatus); goto FnExit; } //set the node's lowest version dwStatus = DmLocalSetValue( hXsaction, hNodeKey, CLUSREG_NAME_NODE_LOWEST_VERSION, REG_DWORD, (LPBYTE)&dwLowestVersion, sizeof(DWORD) ); if (dwStatus != ERROR_SUCCESS) { ClRtlLogPrint(LOG_CRITICAL, "[NM] NmpJoinFixupNodeVersion: Failed to set the lowest version\r\n" ); CL_LOGFAILURE(dwStatus); goto FnExit; } //if written to the registry successfully, update the in-memory structures pNmNode->HighestVersion = dwHighestVersion; pNmNode->LowestVersion = dwLowestVersion; if (dwStatus == ERROR_SUCCESS) { ClusterEvent(CLUSTER_EVENT_NODE_PROPERTY_CHANGE, pNmNode); } FnExit: NmpReleaseLock(); if (pNmNode) OmDereferenceObject(pNmNode); if (hNodeKey != NULL) DmCloseKey(hNodeKey); return(dwStatus); } /**** @func HLOG | NmpIsNodeVersionAllowed| This is called at join time (not setup join) e sponsor validates if a joiner should be allowed to join a cluster at this time. In a mixed mode cluster, a node may not be able to join a cluster if another node that is two versions away is already a part of the cluster. @parm IN DWORD | dwExcludeNodeId | The node Id to exclude while evaluating the cluster operational version. @parm IN DWORD | NodeHighestVersion | The highest version with which this node can communicate. @parm IN DWORD | NodeLowestVersion | The lowest version with which this node can communicate. @parm IN BOOL |bJoin| If this is being invoked at join or form time. @rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure. @comm This function is called when a node requests a sponsor to allow it to join a cluster. @xref <> ****/ DWORD NmpIsNodeVersionAllowed( IN DWORD dwExcludeNodeId, IN DWORD dwNodeHighestVersion, IN DWORD dwNodeLowestVersion, IN BOOL bJoin ) { DWORD dwStatus = ERROR_SUCCESS; DWORD ClusterHighestVersion; DWORD ClusterLowestVersion; PLIST_ENTRY pListEntry; DWORD dwCnt; PNM_NODE pNmNode; ClRtlLogPrint(LOG_UNUSUAL, "[NM] NmpIsNodeVersionAllowed: Entry\r\n"); //acquire the NmpLocks, we will be examining the node structures NmpAcquireLock(); //if NoVersionCheckOption is true if (CsNoVersionCheck) goto FnExit; //if this is a single node cluster, and this is being called at form //the count of nodes is zero. //this will happen when the registry versions dont match with //cluster service exe version numbers and we need to allow the single //node to form for (dwCnt=0, pListEntry = NmpNodeList.Flink; pListEntry != &NmpNodeList; pListEntry = pListEntry->Flink ) { pNmNode = CONTAINING_RECORD(pListEntry, NM_NODE, Linkage); if (NmGetNodeId(pNmNode) == dwExcludeNodeId) continue; dwCnt++; } if (!dwCnt) { //allow the node to form goto FnExit; } dwStatus = NmpCalcClusterVersion( dwExcludeNodeId, &ClusterHighestVersion, &ClusterLowestVersion ); if (dwStatus != ERROR_SUCCESS) { goto FnExit; } //if the node is forming and this node has just upgraded //allow the node to form as long as its minor(or build number) //is greater than or equal to all other nodes with the same //major number in the cluster if (!bJoin && CsUpgrade) { DWORD dwMinorVersion = 0x00000000; for (pListEntry = NmpNodeList.Flink; pListEntry != &NmpNodeList; pListEntry = pListEntry->Flink ) { pNmNode = CONTAINING_RECORD(pListEntry, NM_NODE, Linkage); if (NmGetNodeId(pNmNode) == dwExcludeNodeId) continue; if (CLUSTER_GET_MAJOR_VERSION(pNmNode->HighestVersion) == CLUSTER_GET_MAJOR_VERSION(dwNodeHighestVersion)) { //the minor version to check is the maximum of the //build numbers amongst the nodes with the same major //version dwMinorVersion = max(dwMinorVersion, CLUSTER_GET_MINOR_VERSION(pNmNode->HighestVersion)); } } //dont allow a lower build on a node to regress the cluster version // if other nodes have already upgraded to higher builds if ((dwMinorVersion != 0) && (CLUSTER_GET_MINOR_VERSION(dwNodeHighestVersion) < dwMinorVersion)) { dwStatus = ERROR_CLUSTER_INCOMPATIBLE_VERSIONS; goto FnExit; } else { //ISSUE ::for now we allow double jumps(from n-1 to n+1) // on upgrades //skip checking versioning goto FnExit; } } //if the joiners lowest version is equal the clusters highest //For instance 3/2, 2/1 and 4/3 can all join 3/2 if ((dwNodeHighestVersion == ClusterHighestVersion) || (dwNodeHighestVersion == ClusterLowestVersion) || (dwNodeLowestVersion == ClusterHighestVersion)) { PNM_NODE pNmNode= NULL; DWORD dwMinorVersion; //since the version numbers include build number as the minor part // and we disallow a node from operating with a cluster if its // major number is equal but its minor number is different from // any of the nodes in the cluster. // The CsClusterHighestVersion doesnt encapsulate this since it just // remembers the highest version that the cluster as a whole can talk // to. // E.g 1. // 3.2003 should be able to join a cluster with nodes // 3.2002(not running and not upgraded as yet but a part of the cluster)and // 3.2003(running). // E.g 2 // 3.2002 will not be able to join a cluster with nodes 3.2003(running)and // 3.2002 (not running but a part of the cluster) // E.g 3. // 3.2003 will not able to join a cluster with nodes 3.2002(running) and // 3.2002(running) dwMinorVersion = 0x00000000; for (pListEntry = NmpNodeList.Flink; pListEntry != &NmpNodeList; pListEntry = pListEntry->Flink ) { pNmNode = CONTAINING_RECORD(pListEntry, NM_NODE, Linkage); if (NmGetNodeId(pNmNode) == dwExcludeNodeId) continue; if (CLUSTER_GET_MAJOR_VERSION(pNmNode->HighestVersion) == CLUSTER_GET_MAJOR_VERSION(dwNodeHighestVersion)) { //the minor version to check is the maximum of the //build numbers amongst the nodes with the same major //version dwMinorVersion = max(dwMinorVersion, CLUSTER_GET_MINOR_VERSION(pNmNode->HighestVersion)); } } // if the joining node's build number is the same as max of build //number of all nodes within the cluster with the same major version //allow it to participate in this cluster, else dont allow it to participate in this cluster //take care of a single node case by checking the minor number against //0 if ((dwMinorVersion != 0) && (CLUSTER_GET_MINOR_VERSION(dwNodeHighestVersion) != dwMinorVersion)) { dwStatus = ERROR_CLUSTER_INCOMPATIBLE_VERSIONS; } } else { dwStatus = ERROR_CLUSTER_INCOMPATIBLE_VERSIONS; } FnExit: NmpReleaseLock(); ClRtlLogPrint(LOG_UNUSUAL, "[NM] NmpIsNodeVersionAllowed: Exit, Status=%1!u!\r\n", dwStatus); return(dwStatus); } /**** @func HLOG | NmpCalcClusterVersion| This is called to calculate the operational cluster version. @parm IN DWORD | dwExcludeNodeId | The node Id to exclude while evaluating the cluster operational version. @parm OUT LPDWORD | pdwClusterHighestVersion | The highest version with which this node can communicate. @parm IN LPDWORD | pdwClusterLowestVersion | The lowest version with which this node can communicate. @rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure. @comm This function must be called with the NmpLock held. @xref ****/ DWORD NmpCalcClusterVersion( IN DWORD dwExcludeNodeId, OUT LPDWORD pdwClusterHighestVersion, OUT LPDWORD pdwClusterLowestVersion ) { WCHAR Buffer[4]; PNM_NODE pExcludeNode=NULL; PNM_NODE pNmNode; DWORD dwStatus = ERROR_SUCCESS; PLIST_ENTRY pListEntry; DWORD dwCnt = 0; //initialize the values such that min/max do the right thing *pdwClusterHighestVersion = 0xFFFFFFFF; *pdwClusterLowestVersion = 0x00000000; if (dwExcludeNodeId != ClusterInvalidNodeId) { wsprintfW(Buffer, L"%d", dwExcludeNodeId); pExcludeNode = OmReferenceObjectById(ObjectTypeNode, Buffer); if (!pExcludeNode) { dwStatus = ERROR_INVALID_PARAMETER; ClRtlLogPrint(LOG_UNUSUAL, "[NM] NmpCalcClusterVersion :Node=%1!ws! to be excluded not found\r\n", Buffer); goto FnExit; } } for ( pListEntry = NmpNodeList.Flink; pListEntry != &NmpNodeList; pListEntry = pListEntry->Flink ) { pNmNode = CONTAINING_RECORD(pListEntry, NM_NODE, Linkage); if ((pExcludeNode) && (pExcludeNode->NodeId == pNmNode->NodeId)) continue; //Actually to fix upgrade scenarios, we must fix the cluster //version such that the node with the highest minor version //is able to form/join but others arent // This is needed for multinode clusters if (CLUSTER_GET_MAJOR_VERSION(pNmNode->HighestVersion) == CLUSTER_GET_MAJOR_VERSION(*pdwClusterHighestVersion)) { if (CLUSTER_GET_MINOR_VERSION(pNmNode->HighestVersion) > CLUSTER_GET_MINOR_VERSION(*pdwClusterHighestVersion)) { *pdwClusterHighestVersion = pNmNode->HighestVersion; } } else { *pdwClusterHighestVersion = min( *pdwClusterHighestVersion, pNmNode->HighestVersion ); } *pdwClusterLowestVersion = max( *pdwClusterLowestVersion, pNmNode->LowestVersion ); dwCnt++; } if (dwCnt == 0) { ClRtlLogPrint(LOG_NOISE, "[NM] NmpCalcClusterVersion: Single node version. Setting cluster version to node version\r\n" ); //single node cluster, even though the we were requested to //exclude this node, the cluster version must be calculated //using that node's version *pdwClusterHighestVersion = pExcludeNode->HighestVersion; *pdwClusterLowestVersion = pExcludeNode->LowestVersion; } CL_ASSERT(*pdwClusterHighestVersion != 0xFFFFFFFF); CL_ASSERT(*pdwClusterLowestVersion != 0x00000000); FnExit: ClRtlLogPrint(LOG_NOISE, "[NM] NmpCalcClusterVersion: status = %1!u! ClusHighestVer=0x%2!08lx!, ClusLowestVer=0x%3!08lx!\r\n", dwStatus, *pdwClusterHighestVersion, *pdwClusterLowestVersion); if (pExcludeNode) OmDereferenceObject(pExcludeNode); return(dwStatus); } VOID NmpProcessClusterVersionChange( VOID ) /*++ Notes: Called with the NmpLock held. --*/ { DWORD status; LPWSTR szClusterName=NULL; DWORD dwSize=0; NmpMulticastProcessClusterVersionChange(); //rjain: issue CLUSTER_EVENT_PROPERTY_CHANGE to propagate new //cluster version info DmQuerySz( DmClusterParametersKey, CLUSREG_NAME_CLUS_NAME, &szClusterName, &dwSize, &dwSize); if(szClusterName) ClusterEventEx( CLUSTER_EVENT_PROPERTY_CHANGE, EP_FREE_CONTEXT, szClusterName ); return; } // NmpProcessClusterVersionChange /**** @func DWORD | NmpBuildVersionInfo| Its a callback function used by NmPerformFixups to build a property list of the Major Version, Minor version, Build Number and CSDVersionInfo, This propertylist is used by NmUpdatePerformFixups Update type to store this info in registry. @parm IN DWORD | dwFixupType| JoinFixup or FormFixup @parm OUT PVOID* | ppPropertyList| Pointer to the pointer to the property list @parm OUT LPDWORD | pdwProperyListSize | Pointer to the property list size @param OUT LPWSTR* | pszKeyName. The name of registry key for which this property list is being constructed. @rdesc Returns a result code. ERROR_SUCCESS on success. @xref ****/ DWORD NmpBuildVersionInfo( IN DWORD dwFixUpType, OUT PVOID * ppPropertyList, OUT LPDWORD pdwPropertyListSize, OUT LPWSTR * pszKeyName ) { DWORD dwStatus=ERROR_SUCCESS; LPBYTE pInParams=NULL; DWORD Required,Returned; HDMKEY hdmKey; DWORD dwTemp; CLUSTERVERSIONINFO ClusterVersionInfo; LPWSTR szTemp=NULL; *ppPropertyList = NULL; *pdwPropertyListSize = 0; //check we if need to send this information dwTemp=(lstrlenW(CLUSREG_KEYNAME_NODES) + lstrlenW(L"\\")+lstrlenW(NmLocalNodeIdString)+1)*sizeof(WCHAR); *pszKeyName=(LPWSTR)LocalAlloc(LMEM_FIXED,dwTemp); if(*pszKeyName==NULL) { dwStatus =GetLastError(); goto FnExit; } lstrcpyW(*pszKeyName,CLUSREG_KEYNAME_NODES); lstrcatW(*pszKeyName,L"\\"); lstrcatW(*pszKeyName,NmLocalNodeIdString); // Build the parameter list pInParams=(LPBYTE)LocalAlloc(LMEM_FIXED,4*sizeof(DWORD)+sizeof(LPWSTR)); if(pInParams==NULL) { dwStatus =GetLastError(); goto FnExit; } CsGetClusterVersionInfo(&ClusterVersionInfo); dwTemp=(DWORD)ClusterVersionInfo.MajorVersion; CopyMemory(pInParams,&dwTemp,sizeof(DWORD)); dwTemp=(DWORD)ClusterVersionInfo.MinorVersion; CopyMemory(pInParams+sizeof(DWORD),&dwTemp,sizeof(DWORD)); dwTemp=(DWORD)ClusterVersionInfo.BuildNumber; CopyMemory(pInParams+2*sizeof(DWORD),&dwTemp,sizeof(DWORD)); if(ClusterVersionInfo.szCSDVersion==NULL) szTemp=NULL; else { szTemp=(LPWSTR)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,(lstrlenW(ClusterVersionInfo.szCSDVersion) +1)*sizeof(WCHAR)); if (szTemp==NULL) { dwStatus=GetLastError(); goto FnExit; } lstrcpyW(szTemp,ClusterVersionInfo.szCSDVersion); szTemp[lstrlenW(ClusterVersionInfo.szCSDVersion)]=L'\0'; } CopyMemory(pInParams+3*sizeof(DWORD),&szTemp,sizeof(LPWSTR)); //copy the suite information CopyMemory(pInParams+3*sizeof(DWORD)+sizeof(LPWSTR*), &CsMyProductSuite, sizeof(DWORD)); Required=sizeof(DWORD); AllocMem: *ppPropertyList=(LPBYTE)LocalAlloc(LMEM_FIXED, Required); if(*ppPropertyList==NULL) { dwStatus=GetLastError(); goto FnExit; } *pdwPropertyListSize=Required; dwStatus = ClRtlPropertyListFromParameterBlock( NmFixupVersionInfo, *ppPropertyList, pdwPropertyListSize, (LPBYTE)pInParams, &Returned, &Required ); *pdwPropertyListSize=Returned; if (dwStatus==ERROR_MORE_DATA) { LocalFree(*ppPropertyList); *ppPropertyList=NULL; goto AllocMem; } else if (dwStatus != ERROR_SUCCESS) { ClRtlLogPrint(LOG_CRITICAL,"[NM] NmBuildVersionInfo - error = %1!u!\r\n",dwStatus); goto FnExit; } FnExit: // Cleanup if (szTemp) LocalFree(szTemp); if(pInParams) LocalFree(pInParams); return dwStatus; }//NmpBuildVersionInfo /**** @func HLOG | NmpCalcClusterNodeLimit|This is called to calculate the operational cluster node limit. @rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure. @comm This acquires/releases NmpLock. @xref ****/ DWORD NmpCalcClusterNodeLimit( ) { PNM_NODE pNmNode; DWORD dwStatus = ERROR_SUCCESS; PLIST_ENTRY pListEntry; //acquire the lock, we are going to be messing with the operational //versions for the cluster NmpAcquireLock(); CsClusterNodeLimit = NmMaxNodeId; for ( pListEntry = NmpNodeList.Flink; pListEntry != &NmpNodeList; pListEntry = pListEntry->Flink ) { pNmNode = CONTAINING_RECORD(pListEntry, NM_NODE, Linkage); CsClusterNodeLimit = min( CsClusterNodeLimit, ClRtlGetDefaultNodeLimit( pNmNode->ProductSuite ) ); } ClRtlLogPrint(LOG_NOISE, "[NM] Calculated cluster node limit = %1!u!\r\n", CsClusterNodeLimit); NmpReleaseLock(); return (dwStatus); } /**** @func VOID| NmpResetClusterNodeLimit| An operational node limit on the number of nodes that can join this cluster is maintained. @rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure. @comm This function is called when a node forms a cluster(to initialize the operational version) OR when a node joins a cluster (to initialize its version) OR when a node is ejected from a cluster(to recalculate the clusterversion). @xref <> ****/ VOID NmpResetClusterNodeLimit( ) { NmpCalcClusterNodeLimit(); }