Leaked source code of windows server 2003
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.

1197 lines
44 KiB

  1. //////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 2000-2002 Microsoft Corporation
  4. //
  5. // Module Name:
  6. // ClusComp.cpp
  7. //
  8. // Header File:
  9. // There is no header file for this source file.
  10. //
  11. // Description:
  12. // This file implements that function that is called by WinNT32.exe before
  13. // an upgrade to ensure that no incompatibilities occur as a result of the
  14. // upgrade. For example, in a cluster of two NT4 nodes, one node cannot
  15. // be upgraded to Whistler while the other is still at NT4. The user is
  16. // warned about such problems by this function.
  17. //
  18. // NOTE: This function is called by WinNT32.exe on the OS *before* an
  19. // upgrade. If OS version X is being upgraded to OS version X+1, then
  20. // the X+1 verion of this DLL is loaded on OS version X. To make sure
  21. // that this DLL can function properly in an downlevel OS, it is linked
  22. // against only the indispensible libraries.
  23. //
  24. // Maintained By:
  25. // David Potter (DavidP) 24-MAY-2001
  26. // Vij Vasu (Vvasu) 25-JUL-2000
  27. //
  28. //////////////////////////////////////////////////////////////////////////////
  29. //////////////////////////////////////////////////////////////////////////////
  30. // Include Files
  31. //////////////////////////////////////////////////////////////////////////////
  32. // Precompiled header for this DLL
  33. #include "Pch.h"
  34. #include "Common.h"
  35. // For LsaClose, LSA_HANDLE, etc.
  36. #include <ntsecapi.h>
  37. // For the compatibility check function and types
  38. #include <comp.h>
  39. // For the cluster API
  40. #include <clusapi.h>
  41. // For the names of several cluster service related registry keys and values
  42. #include <clusudef.h>
  43. //////////////////////////////////////////////////////////////////////////////
  44. // Forward declarations
  45. //////////////////////////////////////////////////////////////////////////////
  46. DWORD ScIsClusterServiceRegistered( bool & rfIsRegisteredOut );
  47. DWORD ScLoadString( UINT nStringIdIn, WCHAR *& rpszDestOut );
  48. DWORD ScWriteOSVersionInfo( const OSVERSIONINFO & rcosviOSVersionInfoIn );
  49. //////////////////////////////////////////////////////////////////////////////
  50. //++
  51. //
  52. // InitLsaString
  53. //
  54. // Description:
  55. // Initialize a LSA_UNICODE_STRING structure
  56. //
  57. // Arguments:
  58. // pszSourceIn
  59. // The string used to initialize the unicode string structure.
  60. //
  61. // plusUnicodeStringOut,
  62. // The output unicode string structure.
  63. //
  64. // Return Value:
  65. // None.
  66. //
  67. // Exceptions Thrown:
  68. // None.
  69. //
  70. //--
  71. //////////////////////////////////////////////////////////////////////////////
  72. void
  73. InitLsaString(
  74. LPWSTR pszSourceIn
  75. , PLSA_UNICODE_STRING plusUnicodeStringOut
  76. )
  77. {
  78. TraceFunc( "" );
  79. if ( pszSourceIn == NULL )
  80. {
  81. plusUnicodeStringOut->Buffer = NULL;
  82. plusUnicodeStringOut->Length = 0;
  83. plusUnicodeStringOut->MaximumLength = 0;
  84. } // if: input string is NULL
  85. else
  86. {
  87. plusUnicodeStringOut->Buffer = pszSourceIn;
  88. plusUnicodeStringOut->Length = static_cast< USHORT >( wcslen( pszSourceIn ) * sizeof( *pszSourceIn ) );
  89. plusUnicodeStringOut->MaximumLength = static_cast< USHORT >( plusUnicodeStringOut->Length + sizeof( *pszSourceIn ) );
  90. } // else: input string is not NULL
  91. TraceFuncExit();
  92. } //*** InitLsaString
  93. //////////////////////////////////////////////////////////////////////////////
  94. //++
  95. //
  96. // ClusterCheckForTCBPrivilege
  97. //
  98. // Description:
  99. // Determine if the cluster service account has TCB
  100. // privilege granted. We assume all other privileges
  101. // are intact. "Borrowed" from base cluster code in cluscfg
  102. //
  103. // Arguments:
  104. // bool & rfTCBIsNotGranted
  105. // True if TCB is not granted to the CSA. Only valid if
  106. // return value is ERROR_SUCCESS.
  107. //
  108. // Return Value:
  109. // ERROR_SUCCESS if all went well.
  110. // Other Win32 error codes on failure.
  111. //
  112. //
  113. //--
  114. //////////////////////////////////////////////////////////////////////////////
  115. DWORD
  116. ClusterCheckForTCBPrivilege( bool & rfTCBIsNotGranted )
  117. {
  118. TraceFunc( "" );
  119. // typedefs for Smart resource handles/pointers
  120. //
  121. typedef CSmartResource<
  122. CHandleTrait<
  123. PLSA_UNICODE_STRING
  124. , NTSTATUS
  125. , reinterpret_cast< NTSTATUS (*)( PLSA_UNICODE_STRING ) >( LsaFreeMemory )
  126. , reinterpret_cast< PLSA_UNICODE_STRING >( NULL )
  127. >
  128. >
  129. SmartLsaUnicodeStringPtr;
  130. typedef CSmartResource<
  131. CHandleTrait<
  132. LSA_HANDLE
  133. , NTSTATUS
  134. , LsaClose
  135. >
  136. >
  137. SmartLSAHandle;
  138. typedef CSmartGenericPtr< CPtrTrait< SID > > SmartSIDPtr;
  139. typedef CSmartResource< CHandleTrait< SC_HANDLE, BOOL, CloseServiceHandle > > SmartServiceHandle;
  140. typedef CSmartGenericPtr< CArrayPtrTrait< QUERY_SERVICE_CONFIG > > SmartServiceConfig;
  141. // automatic vars
  142. //
  143. NTSTATUS ntStatus;
  144. PLSA_UNICODE_STRING plusAccountRights = NULL;
  145. ULONG clusOriginalRightsCount = 0;
  146. ULONG ulIndex;
  147. DWORD dwReturnValue = ERROR_SUCCESS;
  148. // Initial size of the QUERY_SERVICE_CONFIG buffer. If not large enough, we'll loop through after
  149. // capturing the correct size.
  150. DWORD cbServiceConfigBufSize = 512;
  151. DWORD cbRequiredSize = 0;
  152. DWORD cbSidSize = 0;
  153. DWORD cchDomainSize = 0;
  154. SID_NAME_USE snuSidNameUse;
  155. // smart resources: we need to declare them at the beginning since we use
  156. // a goto as the clean up mechanism
  157. //
  158. SmartLSAHandle slsahPolicyHandle;
  159. SmartServiceHandle shServiceMgr;
  160. SmartServiceHandle shService;
  161. SmartServiceConfig spscServiceConfig;
  162. SmartSIDPtr sspClusterAccountSid;
  163. SmartSz sszDomainName;
  164. SmartLsaUnicodeStringPtr splusOriginalRights;
  165. // initialize return value to reflect the default and have the code prove
  166. // that it is granted.
  167. rfTCBIsNotGranted = true;
  168. // Open a handle to the LSA policy so we can eventually enumerate the
  169. // account rights for the cluster service account
  170. //
  171. {
  172. LSA_OBJECT_ATTRIBUTES loaObjectAttributes;
  173. LSA_HANDLE hPolicyHandle;
  174. LogMsg( "Getting a handle to the Local LSA Policy." );
  175. ZeroMemory( &loaObjectAttributes, sizeof( loaObjectAttributes ) );
  176. ntStatus = THR( LsaOpenPolicy(
  177. NULL // System name
  178. , &loaObjectAttributes // Object attributes.
  179. , POLICY_ALL_ACCESS // Desired Access
  180. , &hPolicyHandle // Policy handle
  181. )
  182. );
  183. if ( ntStatus != STATUS_SUCCESS )
  184. {
  185. LogMsg( "Error %#08x occurred trying to open the LSA Policy.", ntStatus );
  186. dwReturnValue = ntStatus;
  187. goto Cleanup;
  188. } // if LsaOpenPolicy failed.
  189. // Store the opened handle in smart variable.
  190. slsahPolicyHandle.Assign( hPolicyHandle );
  191. }
  192. LogMsg( "Getting the Cluster Service Account from SCM." );
  193. // Connect to the Service Control Manager
  194. shServiceMgr.Assign( OpenSCManager( NULL, NULL, GENERIC_READ ) );
  195. // Was the service control manager database opened successfully?
  196. if ( shServiceMgr.HHandle() == NULL )
  197. {
  198. dwReturnValue = TW32( GetLastError() );
  199. LogMsg( "Error %#08x occurred trying to open a connection to the local service control manager.", dwReturnValue );
  200. goto Cleanup;
  201. } // if: opening the SCM was unsuccessful
  202. // Open a handle to the Cluster Service.
  203. shService.Assign( OpenService( shServiceMgr, L"ClusSvc", GENERIC_READ ) );
  204. // Was the handle to the service opened?
  205. if ( shService.HHandle() == NULL )
  206. {
  207. dwReturnValue = TW32( GetLastError() );
  208. if ( dwReturnValue == ERROR_SERVICE_DOES_NOT_EXIST )
  209. {
  210. // cluster service not found
  211. LogMsg( "Cluster Service not registered on this node." );
  212. rfTCBIsNotGranted = false;
  213. dwReturnValue = ERROR_SUCCESS;
  214. } // if: the cluster service wasn't found
  215. else
  216. {
  217. LogMsg( "Error %#08x occurred trying to open a handle to the cluster service.", dwReturnValue );
  218. } // else: couldn't determine if the cluster service was installed
  219. goto Cleanup;
  220. } // if: the handle could not be opened
  221. // Allocate memory for the service configuration info buffer. The memory is automatically freed when the
  222. // object is destroyed.
  223. for ( ; ; ) { // ever
  224. spscServiceConfig.Assign( reinterpret_cast< QUERY_SERVICE_CONFIG * >( new BYTE[ cbServiceConfigBufSize ] ) );
  225. // Did the memory allocation succeed
  226. if ( spscServiceConfig.FIsEmpty() )
  227. {
  228. dwReturnValue = TW32( ERROR_OUTOFMEMORY );
  229. LogMsg( "Error: There was not enough memory to get the cluster service configuration information." );
  230. break;
  231. } // if: memory allocation failed
  232. // Get the configuration information.
  233. if ( QueryServiceConfig(
  234. shService.HHandle()
  235. , spscServiceConfig.PMem()
  236. , cbServiceConfigBufSize
  237. , &cbRequiredSize
  238. )
  239. == FALSE
  240. )
  241. {
  242. dwReturnValue = GetLastError();
  243. if ( dwReturnValue != ERROR_INSUFFICIENT_BUFFER )
  244. {
  245. TW32( dwReturnValue );
  246. LogMsg( "Error %#08x occurred trying to get the cluster service configuration information.", dwReturnValue );
  247. break;
  248. } // if: something has really gone wrong
  249. else
  250. {
  251. // We need to allocate more memory - try again
  252. dwReturnValue = ERROR_SUCCESS;
  253. cbServiceConfigBufSize = cbRequiredSize;
  254. }
  255. } // if: QueryServiceConfig() failed
  256. else
  257. {
  258. break;
  259. }
  260. } // forever
  261. if ( dwReturnValue != ERROR_SUCCESS )
  262. {
  263. goto Cleanup;
  264. }
  265. // Lookup the cluster service account SID.
  266. LogMsg( "Getting the SID of the Cluster Service Account." );
  267. // Find out how much space is required by the SID.
  268. if ( LookupAccountName(
  269. NULL
  270. , spscServiceConfig->lpServiceStartName
  271. , NULL
  272. , &cbSidSize
  273. , NULL
  274. , &cchDomainSize
  275. , &snuSidNameUse
  276. )
  277. == FALSE
  278. )
  279. {
  280. dwReturnValue = GetLastError();
  281. if ( dwReturnValue != ERROR_INSUFFICIENT_BUFFER )
  282. {
  283. TW32( dwReturnValue );
  284. LogMsg( "LookupAccountName() failed with error %#08x while querying for required buffer size.", dwReturnValue );
  285. goto Cleanup;
  286. } // if: something else has gone wrong.
  287. else
  288. {
  289. // This is expected.
  290. dwReturnValue = ERROR_SUCCESS;
  291. } // if: ERROR_INSUFFICIENT_BUFFER was returned.
  292. } // if: LookupAccountName failed
  293. // Allocate memory for the new SID and the domain name.
  294. sspClusterAccountSid.Assign( reinterpret_cast< SID * >( new BYTE[ cbSidSize ] ) );
  295. sszDomainName.Assign( new WCHAR[ cchDomainSize ] );
  296. if ( sspClusterAccountSid.FIsEmpty() || sszDomainName.FIsEmpty() )
  297. {
  298. dwReturnValue = TW32( ERROR_OUTOFMEMORY );
  299. goto Cleanup;
  300. } // if: there wasn't enough memory for this SID.
  301. // Fill in the SID
  302. if ( LookupAccountName(
  303. NULL
  304. , spscServiceConfig->lpServiceStartName
  305. , sspClusterAccountSid.PMem()
  306. , &cbSidSize
  307. , sszDomainName.PMem()
  308. , &cchDomainSize
  309. , &snuSidNameUse
  310. )
  311. == FALSE
  312. )
  313. {
  314. dwReturnValue = TW32( GetLastError() );
  315. LogMsg( "LookupAccountName() failed with error %#08x while attempting to get the cluster account SID.", dwReturnValue );
  316. goto Cleanup;
  317. } // if: LookupAccountName failed
  318. LogMsg( "Determining the rights that need to be granted to the cluster service account." );
  319. // Get the list of rights already granted to the cluster service account.
  320. ntStatus = THR( LsaEnumerateAccountRights(
  321. slsahPolicyHandle
  322. , sspClusterAccountSid.PMem()
  323. , &plusAccountRights
  324. , &clusOriginalRightsCount
  325. ));
  326. if ( ntStatus != STATUS_SUCCESS )
  327. {
  328. //
  329. // LSA returns this error code if the account has no rights granted or denied to it
  330. // locally. Post the warning since this is dreadfully wrong.
  331. //
  332. if ( ntStatus == STATUS_OBJECT_NAME_NOT_FOUND )
  333. {
  334. LogMsg( "The account has no locally assigned rights." );
  335. dwReturnValue = ERROR_SUCCESS;
  336. } // if: the account does not have any rights assigned locally to it.
  337. else
  338. {
  339. dwReturnValue = THR( ntStatus );
  340. LogMsg( "Error %#08x occurred trying to enumerate the cluster service account rights.", ntStatus );
  341. } // else: something went wrong.
  342. goto Cleanup;
  343. } // if: LsaEnumerateAccountRights() failed
  344. // Store the account rights just enumerated in a smart pointer for automatic release.
  345. splusOriginalRights.Assign( plusAccountRights );
  346. // Determine if TCB is present
  347. for ( ulIndex = 0; ulIndex < clusOriginalRightsCount; ++ulIndex )
  348. {
  349. const WCHAR * pchGrantedRight = plusAccountRights[ ulIndex ].Buffer;
  350. USHORT usCharCount = plusAccountRights[ ulIndex ].Length / sizeof( WCHAR );
  351. size_t cchTCBNameLength = wcslen( SE_TCB_NAME );
  352. if ( ClRtlStrNICmp( SE_TCB_NAME, pchGrantedRight, min( cchTCBNameLength, usCharCount )) == 0 )
  353. {
  354. rfTCBIsNotGranted = false;
  355. break;
  356. }
  357. } // for: loop through the list of rights that we want to grant the account
  358. Cleanup:
  359. LogMsg( "Return Value is %#08x. rfTCBIsNotGranted is %d", dwReturnValue, rfTCBIsNotGranted );
  360. RETURN( dwReturnValue );
  361. } //*** ClusterCheckForTCBPrivilege
  362. //////////////////////////////////////////////////////////////////////////////
  363. //++
  364. //
  365. // ClusterUpgradeCompatibilityCheck
  366. //
  367. // Description:
  368. // This function is called by WinNT32.exe before an upgrade to ensure that
  369. // no incompatibilities occur as a result of the upgrade. For example,
  370. // in a cluster of two NT4 nodes, one node cannot be upgraded to Whistler
  371. // while the other is still at NT4.
  372. //
  373. // Arguments:
  374. // PCOMPAIBILITYCALLBACK pfnCompatibilityCallbackIn
  375. // Points to the callback function used to supply compatibility
  376. // information to WinNT32.exe
  377. //
  378. // LPVOID pvContextIn
  379. // Pointer to the context buffer supplied by WinNT32.exe
  380. //
  381. // Return Values:
  382. // TRUE if there were no errors or no compatibility problems.
  383. // FALSE otherwise.
  384. //
  385. //--
  386. //////////////////////////////////////////////////////////////////////////////
  387. extern "C"
  388. BOOL
  389. ClusterUpgradeCompatibilityCheck(
  390. PCOMPAIBILITYCALLBACK pfnCompatibilityCallbackIn
  391. , LPVOID pvContextIn
  392. )
  393. {
  394. TraceFunc( "" );
  395. LogMsg( "Entering function " __FUNCTION__ "()" );
  396. BOOL fCompatCallbackReturnValue = TRUE;
  397. BOOL fTCBCheckFailed = FALSE;
  398. bool fNT4WarningRequired = true;
  399. bool fTCBWarningRequired = false;
  400. DWORD dwError = ERROR_SUCCESS;
  401. do
  402. {
  403. typedef CSmartResource< CHandleTrait< HCLUSTER, BOOL, CloseCluster > > SmartClusterHandle;
  404. OSVERSIONINFO osviOSVersionInfo;
  405. SmartClusterHandle schClusterHandle;
  406. DWORD cchBufferSize = 256;
  407. osviOSVersionInfo.dwOSVersionInfoSize = sizeof( osviOSVersionInfo );
  408. //
  409. // First of all, get and store the OS version info into the registry.
  410. //
  411. // Cannot call VerifyVerionInfo as this requires Win2k.
  412. if ( GetVersionEx( &osviOSVersionInfo ) == FALSE )
  413. {
  414. // We could not get OS version info.
  415. // Show the warning, just in case.
  416. dwError = TW32( GetLastError() );
  417. LogMsg( "Error %#x occurred trying to get the OS version info.", dwError );
  418. break;
  419. } // if: GetVersionEx() failed
  420. // Write the OS version info to the registry. This data will be used later by ClusOCM
  421. // to figure out which OS version we are upgrading from.
  422. dwError = TW32( ScWriteOSVersionInfo( osviOSVersionInfo ) );
  423. if ( dwError != ERROR_SUCCESS )
  424. {
  425. LogMsg( "Error %#x occurred trying to store the OS version info. This is not a fatal error.", dwError );
  426. // This is not a fatal error. So reset the error code.
  427. dwError = ERROR_SUCCESS;
  428. } // if: there was an error writing the OS version info
  429. else
  430. {
  431. TraceFlow( "The OS version info was successfully written to the registry." );
  432. } // else: the OS version info was successfully written to the registry
  433. // Check if the cluster service is registered.
  434. dwError = TW32( ScIsClusterServiceRegistered( fNT4WarningRequired ) );
  435. if ( dwError != ERROR_SUCCESS )
  436. {
  437. // We could not get the state of the cluster service
  438. // Show the warning, just in case.
  439. LogMsg( "Error %#x occurred trying to check if the cluster service is registered.", dwError );
  440. break;
  441. } // if: ScIsClusterServiceRegistered() returned an error
  442. if ( !fNT4WarningRequired )
  443. {
  444. // If the cluster service was not registered, no warning is needed.
  445. LogMsg( "The cluster service is not registered." );
  446. break;
  447. } // if: no warning is required
  448. LogMsg( "The cluster service is registered. Checking the node versions." );
  449. // Check if this is an NT4 node
  450. if ( osviOSVersionInfo.dwMajorVersion < 5 )
  451. {
  452. LogMsg( "This is a Windows NT 4.0 node." );
  453. fNT4WarningRequired = true;
  454. break;
  455. } // if: this is an NT4 node
  456. TraceFlow( "This is not a Windows NT 4.0 node." );
  457. // Check if the OS version is Whistler or if it is a non-NT OS
  458. if ( ( osviOSVersionInfo.dwPlatformId != VER_PLATFORM_WIN32_NT )
  459. || ( ( osviOSVersionInfo.dwMajorVersion >= 5 )
  460. && ( osviOSVersionInfo.dwMinorVersion >= 1 )
  461. )
  462. )
  463. {
  464. // If the OS not of the NT family or if the OS version of this
  465. // node is Whistler or greater, no warning is required.
  466. LogMsg(
  467. "The version of the OS on this node is %d.%d, which is Windows Server 2003 or later (or is not running NT)."
  468. , osviOSVersionInfo.dwMajorVersion
  469. , osviOSVersionInfo.dwMinorVersion
  470. );
  471. LogMsg( "No Windows NT 4.0 nodes can exist in this cluster." );
  472. fNT4WarningRequired = false;
  473. break;
  474. } // if: the OS is not NT or if it is Win2k or greater
  475. TraceFlow( "This is not a Windows Server 2003 node - this must to be a Windows 2000 node." );
  476. TraceFlow( "Trying to check if there are any Windows NT 4.0 nodes in the cluster." );
  477. //
  478. // Get the cluster version information
  479. //
  480. // Open a handle to the local cluster
  481. schClusterHandle.Assign( OpenCluster( NULL ) );
  482. if ( schClusterHandle.HHandle() == NULL )
  483. {
  484. // Show the warning, just to be safe.
  485. dwError = TW32( GetLastError() );
  486. LogMsg( "Error %#x occurred trying to get information about the cluster.", dwError );
  487. break;
  488. } // if: we could not get the cluster handle
  489. TraceFlow( "OpenCluster() was successful." );
  490. // Get the cluster version info
  491. for ( ;; ) // forever
  492. {
  493. // Allocate the buffer - this memory is automatically freed when this object
  494. // goes out of scope ( or during the next iteration ).
  495. SmartSz sszClusterName( new WCHAR[ cchBufferSize ] );
  496. CLUSTERVERSIONINFO cviClusterVersionInfo;
  497. if ( sszClusterName.FIsEmpty() )
  498. {
  499. dwError = TW32( ERROR_NOT_ENOUGH_MEMORY );
  500. LogMsg( "Error %#x occurred while allocating a buffer for the cluster name.", dwError );
  501. break;
  502. } // if: memory allocation failed
  503. TraceFlow( "Memory for the cluster name has been allocated." );
  504. cviClusterVersionInfo.dwVersionInfoSize = sizeof( cviClusterVersionInfo );
  505. dwError = GetClusterInformation(
  506. schClusterHandle.HHandle()
  507. , sszClusterName.PMem()
  508. , &cchBufferSize
  509. , &cviClusterVersionInfo
  510. );
  511. if ( dwError == ERROR_SUCCESS )
  512. {
  513. // A warning is required if this node version is less than Win2k or
  514. // if there is a node in the cluster whose version is less than Win2k
  515. // NOTE: cviClusterVersionInfo.MajorVersion is the OS version
  516. // while cviClusterVersionInfo.dwClusterHighestVersion is the cluster version.
  517. fNT4WarningRequired =
  518. ( ( cviClusterVersionInfo.MajorVersion < 5 )
  519. || ( CLUSTER_GET_MAJOR_VERSION( cviClusterVersionInfo.dwClusterHighestVersion ) < NT5_MAJOR_VERSION )
  520. );
  521. if ( fNT4WarningRequired )
  522. {
  523. LogMsg( "There is at least one node in the cluster whose OS version is earlier than Windows 2000." );
  524. } // if: a warning will be shown
  525. else
  526. {
  527. LogMsg( "The OS versions of all the nodes in the cluster are Windows 2000 or later." );
  528. } // else: a warning will not be shown
  529. break;
  530. } // if: we got the cluster version info
  531. else
  532. {
  533. if ( dwError == ERROR_MORE_DATA )
  534. {
  535. // Insufficient buffer - try again
  536. ++cchBufferSize;
  537. dwError = ERROR_SUCCESS;
  538. TraceFlow1( "The buffer size is insufficient. Need %d bytes. Reallocating.", cchBufferSize );
  539. continue;
  540. } // if: the size of the buffer was insufficient
  541. // If we are here, something has gone wrong - show the warning
  542. TW32( dwError );
  543. LogMsg( "Error %#x occurred trying to get cluster information.", dwError );
  544. break;
  545. } // else: we could not get the cluster version info
  546. } // forever get cluster information (loop for allocation)
  547. // We are done.
  548. //break;
  549. }
  550. while( false ); // Dummy do-while loop to avoid gotos
  551. // make sure the cluster service account has the necessary privileges on the upgraded system
  552. dwError = ClusterCheckForTCBPrivilege( fTCBWarningRequired );
  553. if ( dwError != ERROR_SUCCESS )
  554. {
  555. fTCBCheckFailed = TRUE;
  556. } // if: there was an error checking for TCB privilege
  557. if ( fNT4WarningRequired )
  558. {
  559. SmartSz sszWarningTitle;
  560. COMPATIBILITY_ENTRY ceCompatibilityEntry;
  561. LogMsg( "The NT4 compatibility warning is required." );
  562. {
  563. WCHAR * pszWarningTitle = NULL;
  564. dwError = TW32( ScLoadString( IDS_ERROR_UPGRADE_OTHER_NODES, pszWarningTitle ) );
  565. if ( dwError != ERROR_SUCCESS )
  566. {
  567. // We cannot show the warning
  568. LogMsg( "Error %#x occurred trying to show the warning.", dwError );
  569. } // if: the load string failed
  570. else
  571. {
  572. sszWarningTitle.Assign( pszWarningTitle );
  573. } // else: assign the pointer to a smart pointer
  574. }
  575. if ( !sszWarningTitle.FIsEmpty() ) {
  576. //
  577. // Call the callback function
  578. //
  579. ceCompatibilityEntry.Description = sszWarningTitle.PMem();
  580. ceCompatibilityEntry.HtmlName = L"CompData\\ClusComp.htm";
  581. ceCompatibilityEntry.TextName = L"CompData\\ClusComp.txt";
  582. ceCompatibilityEntry.RegKeyName = NULL;
  583. ceCompatibilityEntry.RegValName = NULL ;
  584. ceCompatibilityEntry.RegValDataSize = 0;
  585. ceCompatibilityEntry.RegValData = NULL;
  586. ceCompatibilityEntry.SaveValue = NULL;
  587. ceCompatibilityEntry.Flags = 0;
  588. ceCompatibilityEntry.InfName = NULL;
  589. ceCompatibilityEntry.InfSection = NULL;
  590. TraceFlow( "About to call the compatibility callback function." );
  591. // This function returns TRUE if the compatibility warning data was successfully set.
  592. fCompatCallbackReturnValue = pfnCompatibilityCallbackIn( &ceCompatibilityEntry, pvContextIn );
  593. TraceFlow1( "The compatibility callback function returned %d.", fCompatCallbackReturnValue );
  594. }
  595. } // while: we need to show the warning
  596. if ( !fNT4WarningRequired )
  597. {
  598. LogMsg( "The NT4 compatibility warning need not be shown." );
  599. } // if: we did not need to show the warning
  600. // If the check for TCB failed, it has precedence over the TCB privilege check
  601. if ( fTCBCheckFailed )
  602. {
  603. SmartSz sszWarningTitle;
  604. COMPATIBILITY_ENTRY ceCompatibilityEntry;
  605. LogMsg( "The TCB check failed warning is required." );
  606. {
  607. WCHAR * pszWarningTitle = NULL;
  608. dwError = TW32( ScLoadString( IDS_ERROR_TCB_CHECK_FAILED, pszWarningTitle ) );
  609. if ( dwError != ERROR_SUCCESS )
  610. {
  611. // We cannot show the warning
  612. LogMsg( "Error %#x occurred trying to show the warning.", dwError );
  613. } // if: the load string failed
  614. else
  615. {
  616. sszWarningTitle.Assign( pszWarningTitle );
  617. } // else: assign the pointer to a smart pointer
  618. }
  619. if ( !sszWarningTitle.FIsEmpty() )
  620. {
  621. //
  622. // Call the callback function
  623. //
  624. ceCompatibilityEntry.Description = sszWarningTitle.PMem();
  625. ceCompatibilityEntry.HtmlName = L"CompData\\ClusTCBF.htm";
  626. ceCompatibilityEntry.TextName = L"CompData\\ClusTCBF.txt";
  627. ceCompatibilityEntry.RegKeyName = NULL;
  628. ceCompatibilityEntry.RegValName = NULL ;
  629. ceCompatibilityEntry.RegValDataSize = 0;
  630. ceCompatibilityEntry.RegValData = NULL;
  631. ceCompatibilityEntry.SaveValue = NULL;
  632. ceCompatibilityEntry.Flags = 0;
  633. ceCompatibilityEntry.InfName = NULL;
  634. ceCompatibilityEntry.InfSection = NULL;
  635. TraceFlow( "About to call the compatibility callback function." );
  636. // This function returns TRUE if the compatibility warning data was successfully set.
  637. fCompatCallbackReturnValue = pfnCompatibilityCallbackIn( &ceCompatibilityEntry, pvContextIn );
  638. TraceFlow1( "The compatibility callback function returned %d.", fCompatCallbackReturnValue );
  639. } // if: the warning title string wasn't empty
  640. } // if: the TCB check failed
  641. else if ( fTCBWarningRequired )
  642. {
  643. SmartSz sszWarningTitle;
  644. COMPATIBILITY_ENTRY ceCompatibilityEntry;
  645. LogMsg( "The TCB privilege error is required." );
  646. {
  647. WCHAR * pszWarningTitle = NULL;
  648. dwError = TW32( ScLoadString( IDS_ERROR_TCB_PRIVILEGE_NEEDED, pszWarningTitle ) );
  649. if ( dwError != ERROR_SUCCESS )
  650. {
  651. // We cannot show the warning
  652. LogMsg( "Error %#x occurred trying to show the warning.", dwError );
  653. } // if: the load string failed
  654. else
  655. {
  656. sszWarningTitle.Assign( pszWarningTitle );
  657. }
  658. }
  659. if ( !sszWarningTitle.FIsEmpty() ) {
  660. //
  661. // Call the callback function
  662. //
  663. ceCompatibilityEntry.Description = sszWarningTitle.PMem();
  664. ceCompatibilityEntry.HtmlName = L"CompData\\ClusTCB.htm";
  665. ceCompatibilityEntry.TextName = L"CompData\\ClusTCB.txt";
  666. ceCompatibilityEntry.RegKeyName = NULL;
  667. ceCompatibilityEntry.RegValName = NULL ;
  668. ceCompatibilityEntry.RegValDataSize = 0;
  669. ceCompatibilityEntry.RegValData = NULL;
  670. ceCompatibilityEntry.SaveValue = NULL;
  671. ceCompatibilityEntry.Flags = 0;
  672. ceCompatibilityEntry.InfName = NULL;
  673. ceCompatibilityEntry.InfSection = NULL;
  674. TraceFlow( "About to call the compatibility callback function." );
  675. // This function returns TRUE if the compatibility warning data was successfully set.
  676. fCompatCallbackReturnValue = pfnCompatibilityCallbackIn( &ceCompatibilityEntry, pvContextIn );
  677. TraceFlow1( "The compatibility callback function returned %d.", fCompatCallbackReturnValue );
  678. }
  679. } // else: the TCB error is required
  680. else
  681. {
  682. LogMsg( "Neither TCB message was shown." );
  683. } // else: we did not need to show either message
  684. LogMsg( "Exiting function ClusterUpgradeCompatibilityCheck(). Return value is %d.", fCompatCallbackReturnValue );
  685. RETURN( fCompatCallbackReturnValue );
  686. } //*** ClusterUpgradeCompatibilityCheck
  687. /////////////////////////////////////////////////////////////////////////////
  688. //++
  689. //
  690. // ScIsClusterServiceRegistered
  691. //
  692. // Description:
  693. // This function determines whether the Cluster Service has been registered
  694. // with the Service Control Manager or not. It is not possible to use the
  695. // GetNodeClusterState() API to see if this node is a member of a cluster
  696. // or not, since this API was not available on NT4 SP3.
  697. //
  698. // Arguments:
  699. // bool & rfIsRegisteredOut
  700. // If true, Cluster Service (ClusSvc) is registered with the Service
  701. // Control Manager (SCM). Else, Cluster Service (ClusSvc) is not
  702. // registered with SCM
  703. //
  704. // Return Value:
  705. // ERROR_SUCCESS if all went well.
  706. // Other Win32 error codes on failure.
  707. //
  708. //--
  709. /////////////////////////////////////////////////////////////////////////////
  710. DWORD
  711. ScIsClusterServiceRegistered( bool & rfIsRegisteredOut )
  712. {
  713. TraceFunc( "" );
  714. DWORD dwError = ERROR_SUCCESS;
  715. // Initialize the output
  716. rfIsRegisteredOut = false;
  717. // dummy do-while loop to avoid gotos
  718. do
  719. {
  720. // Instantiate the SmartServiceHandle smart handle class.
  721. typedef CSmartResource< CHandleTrait< SC_HANDLE, BOOL, CloseServiceHandle, NULL > > SmartServiceHandle;
  722. // Connect to the Service Control Manager
  723. SmartServiceHandle shServiceMgr( OpenSCManager( NULL, NULL, GENERIC_READ ) );
  724. // Was the service control manager database opened successfully?
  725. if ( shServiceMgr.HHandle() == NULL )
  726. {
  727. dwError = TW32( GetLastError() );
  728. LogMsg( "Error %#x occurred trying open a handle to the service control manager.", dwError );
  729. break;
  730. } // if: opening the SCM was unsuccessful
  731. // Open a handle to the Cluster Service.
  732. SmartServiceHandle shService( OpenService( shServiceMgr, L"ClusSvc", GENERIC_READ ) );
  733. // Was the handle to the service opened?
  734. if ( shService.HHandle() != NULL )
  735. {
  736. TraceFlow( "Successfully opened a handle to the cluster service. Therefore, it is registered." );
  737. rfIsRegisteredOut = true;
  738. break;
  739. } // if: handle to clussvc could be opened
  740. dwError = GetLastError();
  741. if ( dwError == ERROR_SERVICE_DOES_NOT_EXIST )
  742. {
  743. TraceFlow( "The cluster service is not registered." );
  744. dwError = ERROR_SUCCESS;
  745. break;
  746. } // if: the handle could not be opened because the service did not exist.
  747. // If we are here, then some error occurred.
  748. TW32( dwError );
  749. LogMsg( "Error %#x occurred trying open a handle to the cluster service.", dwError );
  750. // Handles are closed by the CSmartHandle destructor.
  751. }
  752. while ( false ); // dummy do-while loop to avoid gotos
  753. RETURN( dwError );
  754. } //*** ScIsClusterServiceRegistered
  755. //////////////////////////////////////////////////////////////////////////////
  756. //++
  757. //
  758. // ScLoadString
  759. //
  760. // Description:
  761. // Allocate memory for and load a string from the string table.
  762. //
  763. // Arguments:
  764. // uiStringIdIn
  765. // Id of the string to look up
  766. //
  767. // rpszDestOut
  768. // Reference to the pointer that will hold the address of the
  769. // loaded string. The memory will have to be freed by the caller
  770. // by using the delete operator.
  771. //
  772. // Return Value:
  773. // S_OK
  774. // If the call succeeded
  775. //
  776. // Other Win32 error codes
  777. // If the call failed.
  778. //
  779. // Remarks:
  780. // This function cannot load a zero length string.
  781. //--
  782. //////////////////////////////////////////////////////////////////////////////
  783. DWORD
  784. ScLoadString(
  785. UINT nStringIdIn
  786. , WCHAR *& rpszDestOut
  787. )
  788. {
  789. TraceFunc( "" );
  790. DWORD dwError = ERROR_SUCCESS;
  791. UINT uiCurrentSize = 0;
  792. SmartSz sszCurrentString;
  793. UINT uiReturnedStringLen = 0;
  794. // Initialize the output.
  795. rpszDestOut = NULL;
  796. do
  797. {
  798. // Grow the current string by an arbitrary amount.
  799. uiCurrentSize += 256;
  800. sszCurrentString.Assign( new WCHAR[ uiCurrentSize ] );
  801. if ( sszCurrentString.FIsEmpty() )
  802. {
  803. dwError = TW32( ERROR_NOT_ENOUGH_MEMORY );
  804. LogMsg( "Error %#x occurred trying allocate memory for string (string id is %d).", dwError, nStringIdIn );
  805. break;
  806. } // if: the memory allocation has failed
  807. uiReturnedStringLen = ::LoadStringW(
  808. g_hInstance
  809. , nStringIdIn
  810. , sszCurrentString.PMem()
  811. , uiCurrentSize
  812. );
  813. if ( uiReturnedStringLen == 0 )
  814. {
  815. dwError = TW32( GetLastError() );
  816. LogMsg( "Error %#x occurred trying load string (string id is %d).", dwError, nStringIdIn );
  817. break;
  818. } // if: LoadString() had an error
  819. ++uiReturnedStringLen;
  820. }
  821. while( uiCurrentSize <= uiReturnedStringLen );
  822. if ( dwError == ERROR_SUCCESS )
  823. {
  824. // Detach the smart pointer from the string, so that it is not freed by this function.
  825. // Store the string pointer in the output.
  826. rpszDestOut = sszCurrentString.PRelease();
  827. } // if: there were no errors in this function
  828. else
  829. {
  830. rpszDestOut = NULL;
  831. } // else: something went wrong
  832. RETURN( dwError );
  833. } //*** ScLoadString
  834. /////////////////////////////////////////////////////////////////////////////
  835. //++
  836. //
  837. // ScWriteOSVersionInfo
  838. //
  839. // Description:
  840. // This function writes the OS major and minor version information into the
  841. // registry. This information will be used later by ClusOCM to determine the
  842. // OS version before the upgrade.
  843. //
  844. // Arguments:
  845. // const OSVERSIONINFO & rcosviOSVersionInfoIn
  846. // Reference to the OSVERSIONINFO structure that has information about the
  847. // OS version of this node.
  848. //
  849. // Return Value:
  850. // ERROR_SUCCESS if all went well.
  851. // Other Win32 error codes on failure.
  852. //
  853. //--
  854. /////////////////////////////////////////////////////////////////////////////
  855. DWORD
  856. ScWriteOSVersionInfo( const OSVERSIONINFO & rcosviOSVersionInfoIn )
  857. {
  858. TraceFunc( "" );
  859. DWORD dwError = ERROR_SUCCESS;
  860. HKEY hkey;
  861. DWORD cbData;
  862. DWORD dwType;
  863. NODE_CLUSTER_STATE ncsNodeClusterState;
  864. // Instantiate the SmartRegistryKey smart handle class.
  865. typedef CSmartResource< CHandleTrait< HKEY, LONG, RegCloseKey, NULL > > SmartRegistryKey;
  866. SmartRegistryKey srkClusSvcSW;
  867. SmartRegistryKey srkOSInfoKey;
  868. //
  869. // If the InstallationState value does not exist, set it to the
  870. // NotConfigured value. This is required due to a bug in the Win2K
  871. // version of GetNodeClusterState which doesn't handle the case where
  872. // the key exists but the value doesn't.
  873. //
  874. // Open the Cluster Service SOFTWARE key.
  875. // Don't use TW32 here since we are checking for a specific return value.
  876. dwError = RegOpenKeyExW(
  877. HKEY_LOCAL_MACHINE
  878. , CLUSREG_KEYNAME_NODE_DATA
  879. , 0 // ulOptions
  880. , KEY_ALL_ACCESS // samDesired
  881. , &hkey
  882. );
  883. if ( dwError == ERROR_FILE_NOT_FOUND )
  884. {
  885. // Create the Cluster Service key.
  886. dwError = TW32( RegCreateKeyExW(
  887. HKEY_LOCAL_MACHINE
  888. , CLUSREG_KEYNAME_NODE_DATA
  889. , 0 // Reserved
  890. , L"" // lpClass
  891. , REG_OPTION_NON_VOLATILE
  892. , KEY_ALL_ACCESS
  893. , NULL // lpSecurityAttributes
  894. , &hkey
  895. , NULL // lpdwDisposition
  896. ) );
  897. if ( dwError != ERROR_SUCCESS )
  898. {
  899. LogMsg( "Error %#x occurred attempting to create the '%ws' registry key.", dwError, CLUSREG_KEYNAME_NODE_DATA );
  900. goto Cleanup;
  901. } // if: error creating the Cluster Service key
  902. } // if: Cluster Service key doesn't exist
  903. else if ( dwError != ERROR_SUCCESS )
  904. {
  905. TW32( dwError );
  906. LogMsg( "Error %#x occurred attempting to open the '%ws' registry key.", dwError, CLUSREG_KEYNAME_NODE_DATA );
  907. goto Cleanup;
  908. } // else if: error opening the Cluster Service key
  909. // Assign the key to the smart handle.
  910. srkClusSvcSW.Assign( hkey );
  911. //
  912. // Get the InstallationState value. If it is not set, set it to
  913. // NotConfigured.
  914. //
  915. // Get the current value.
  916. // Don't use TW32 here since we are checking for a specific return value.
  917. cbData = sizeof( ncsNodeClusterState );
  918. dwError = RegQueryValueExW(
  919. srkClusSvcSW.HHandle()
  920. , CLUSREG_NAME_INSTALLATION_STATE
  921. , 0 // lpReserved
  922. , &dwType // lpType
  923. , reinterpret_cast< BYTE * >( &ncsNodeClusterState )
  924. , &cbData
  925. );
  926. if ( dwError == ERROR_FILE_NOT_FOUND )
  927. {
  928. ncsNodeClusterState = ClusterStateNotInstalled;
  929. // Write the InstallationState value.
  930. dwError = TW32( RegSetValueExW(
  931. srkClusSvcSW.HHandle()
  932. , CLUSREG_NAME_INSTALLATION_STATE
  933. , 0 // lpReserved
  934. , REG_DWORD
  935. , reinterpret_cast< const BYTE * >( &ncsNodeClusterState )
  936. , sizeof( DWORD )
  937. ) );
  938. if ( dwError != ERROR_SUCCESS )
  939. {
  940. #define INSTALLSTATEVALUE CLUSREG_KEYNAME_NODE_DATA L"\\" CLUSREG_NAME_INSTALLATION_STATE
  941. LogMsg( "Error %#x occurred attempting to set the '%ws' value on the '%ws' registry value to '%d'.", dwError, CLUSREG_NAME_INSTALLATION_STATE, INSTALLSTATEVALUE, ClusterStateNotInstalled );
  942. goto InstallStateError;
  943. } // if: error creating the Cluster Service key
  944. } // if: InstallationState value didn't exist before
  945. else if ( dwError != ERROR_SUCCESS )
  946. {
  947. TW32( dwError );
  948. LogMsg( "Error %#x occurred attempting to query for the '%ws' value on the registry key.", dwError, CLUSREG_NAME_INSTALLATION_STATE, INSTALLSTATEVALUE );
  949. goto InstallStateError;
  950. } // else if: error querying for the InstallationState value
  951. else
  952. {
  953. Assert( dwType == REG_DWORD );
  954. }
  955. //
  956. // Open the node version info registry key.
  957. // If it doesn't exist, create it.
  958. //
  959. dwError = TW32( RegCreateKeyExW(
  960. srkClusSvcSW.HHandle()
  961. , CLUSREG_KEYNAME_PREV_OS_INFO
  962. , 0
  963. , L""
  964. , REG_OPTION_NON_VOLATILE
  965. , KEY_ALL_ACCESS
  966. , NULL
  967. , &hkey
  968. , NULL
  969. ) );
  970. if ( dwError != ERROR_SUCCESS )
  971. {
  972. #define PREVOSINFOKEY CLUSREG_KEYNAME_NODE_DATA L"\\" CLUSREG_KEYNAME_PREV_OS_INFO
  973. LogMsg( "Error %#x occurred attempting to create the registry key where the node OS info is stored (%ws).", dwError, PREVOSINFOKEY );
  974. goto Cleanup;
  975. } // if: RegCreateKeyEx() failed
  976. srkOSInfoKey.Assign( hkey );
  977. // Write the OS major version
  978. dwError = TW32( RegSetValueExW(
  979. srkOSInfoKey.HHandle()
  980. , CLUSREG_NAME_NODE_MAJOR_VERSION
  981. , 0
  982. , REG_DWORD
  983. , reinterpret_cast< const BYTE * >( &rcosviOSVersionInfoIn.dwMajorVersion )
  984. , sizeof( rcosviOSVersionInfoIn.dwMajorVersion )
  985. ) );
  986. if ( dwError != ERROR_SUCCESS )
  987. {
  988. LogMsg( "Error %#x occurred trying to store the OS major version info.", dwError );
  989. goto Cleanup;
  990. } // if: RegSetValueEx() failed while writing rcosviOSVersionInfoIn.dwMajorVersion
  991. // Write the OS minor version
  992. dwError = TW32( RegSetValueExW(
  993. srkOSInfoKey.HHandle()
  994. , CLUSREG_NAME_NODE_MINOR_VERSION
  995. , 0
  996. , REG_DWORD
  997. , reinterpret_cast< const BYTE * >( &rcosviOSVersionInfoIn.dwMinorVersion )
  998. , sizeof( rcosviOSVersionInfoIn.dwMinorVersion )
  999. ) );
  1000. if ( dwError != ERROR_SUCCESS )
  1001. {
  1002. LogMsg( "Error %#x occurred trying to store the OS minor version info.", dwError );
  1003. goto Cleanup;
  1004. } // if: RegSetValueEx() failed while writing rcosviOSVersionInfoIn.dwMinorVersion
  1005. LogMsg( "OS version information successfully stored in the registry." );
  1006. goto Cleanup;
  1007. InstallStateError:
  1008. //
  1009. // Attempt to delete the key, since having the key around without the
  1010. // InstallationState value causes GetNodeClusterState to break on a
  1011. // Win2K machine.
  1012. //
  1013. TW32( RegDeleteKey( HKEY_LOCAL_MACHINE, CLUSREG_KEYNAME_NODE_DATA ) );
  1014. goto Cleanup;
  1015. Cleanup:
  1016. RETURN( dwError );
  1017. } //*** ScWriteOSVersionInfo