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.

1446 lines
51 KiB

  1. /*++
  2. Copyright (c) 2002 Microsoft Corporation
  3. Abstract:
  4. @doc
  5. @module cluster.cxx | Implementation of CVssClusterAPI
  6. @end
  7. Author:
  8. Adi Oltean [aoltean] 04/25/2002
  9. Revision History:
  10. Name Date Comments
  11. aoltean 03/14/2001 Created
  12. --*/
  13. /////////////////////////////////////////////////////////////////////////////
  14. // Includes
  15. #include "stdafx.hxx"
  16. #include <queue>
  17. #pragma warning(disable: 4554)
  18. #include <msclus.h>
  19. #include <mstask.h>
  20. #include "vs_inc.hxx"
  21. #include "vs_reg.hxx"
  22. #include "vs_clus.hxx"
  23. #include "vs_quorum.hxx"
  24. ////////////////////////////////////////////////////////////////////////
  25. // Standard foo for file name aliasing. This code block must be after
  26. // all includes of VSS header files.
  27. //
  28. #ifdef VSS_FILE_ALIAS
  29. #undef VSS_FILE_ALIAS
  30. #endif
  31. #define VSS_FILE_ALIAS "CLUCLUSC"
  32. //
  33. ////////////////////////////////////////////////////////////////////////
  34. #define CHECK_COM( Call ) \
  35. { \
  36. ft.hr = Call; \
  37. if (ft.HrFailed()) \
  38. ft.TranslateComError(VSSDBG_GEN, VSS_WSTRINGIZE(Call)); \
  39. }
  40. /////////////////////////////////////////////////////////////////////////////
  41. // Constants
  42. const WCHAR x_ClusReg_TypeName_PhysicalDisk[] = L"Physical Disk";
  43. const WCHAR x_ClusReg_TypeName_TaskScheduler[] = L"Volume Shadow Copy Service Task";
  44. const WCHAR x_ClusReg_Name_PhysicalDisk_MPVolGuids[] = L"MPVolGuids";
  45. const WCHAR x_ClusReg_Name_TaskScheduler_ApplicationName[] = L"ApplicationName";
  46. const WCHAR x_ClusReg_Name_TaskScheduler_ApplicationParams[] = L"ApplicationParams";
  47. const WCHAR x_ClusReg_Name_TaskScheduler_TriggerArray[] = L"TriggerArray";
  48. //
  49. // BUG# 698766 - Deployment blocker:
  50. // Failure on managing diff areas on large volumes since Cluster Timeout is too low
  51. //
  52. // 600 seconds (10 min) timeout in bringing the resource online/ofline
  53. //
  54. const x_nDefaultBringOnlineTimeout = 600;
  55. const x_nDefaultBringOfflineTimeout = 600;
  56. /////////////////////////////////////////////////////////////////////////////
  57. // CVssClusterResourceList definition and implementation
  58. class CVssClusterResourceList
  59. {
  60. public:
  61. // Pushes a resource in the list
  62. // Increases the ref count by 1
  63. void Push(ISClusResource* pResource) throw(HRESULT)
  64. {
  65. CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterResourceList::Push" );
  66. try
  67. {
  68. // AddRef
  69. CComPtr<ISClusResource> ptrResource = pResource;
  70. // AddRef
  71. resourceList.push(ptrResource);
  72. // Release (CComPtr destructor)
  73. }
  74. catch(const std::bad_alloc&)
  75. {
  76. ft.ThrowOutOfMemory(VSSDBG_GEN);
  77. }
  78. catch(const std::exception& ex)
  79. {
  80. ft.Throw( VSSDBG_GEN, E_UNEXPECTED, L"STL exception catched: %hs", ex.what());
  81. }
  82. }
  83. // Pops a resource from the list
  84. // Does not decrease the ref count by 1 (that is caller responsibility - actually the smart pointer will do it)
  85. void Pop(CComPtr<ISClusResource> & pResource)
  86. {
  87. pResource = NULL;
  88. // If the list is empty, stop here
  89. if (resourceList.empty())
  90. return;
  91. // AddRef
  92. pResource = resourceList.front();
  93. // Release
  94. resourceList.pop();
  95. }
  96. private:
  97. std::queue< CComPtr<ISClusResource> > resourceList;
  98. };
  99. /////////////////////////////////////////////////////////////////////////////
  100. // CVssClusterAPI implementation
  101. // Default constructor
  102. CVssClusterAPI::CVssClusterAPI():
  103. m_dwOfflineTimeout(x_nDefaultBringOfflineTimeout),
  104. m_dwOnlineTimeout(x_nDefaultBringOnlineTimeout)
  105. {
  106. }
  107. // Returns FALSE if the cluster API is not present
  108. bool CVssClusterAPI::Initialize(
  109. IN LPCWSTR pwszCluster /* = NULL */
  110. ) throw(HRESULT)
  111. {
  112. CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::Initialize" );
  113. // Get the offline and online values from registry (if any)
  114. try
  115. {
  116. CVssRegistryKey keyVssSettings(KEY_READ);
  117. if (keyVssSettings.Open(HKEY_LOCAL_MACHINE, x_wszVssCASettingsPath))
  118. {
  119. // Reading the online timeout
  120. DWORD dwRegistryOnlineTimeout = 0;
  121. if (keyVssSettings.GetValue(x_wszVssOnlineTimeoutValueName, dwRegistryOnlineTimeout, false))
  122. m_dwOnlineTimeout = dwRegistryOnlineTimeout;
  123. // Reading the offline timeout
  124. DWORD dwRegistryOfflineTimeout = 0;
  125. if (keyVssSettings.GetValue(x_wszVssOfflineTimeoutValueName, dwRegistryOfflineTimeout, false))
  126. m_dwOfflineTimeout = dwRegistryOfflineTimeout;
  127. }
  128. }
  129. VSS_STANDARD_CATCH(ft) // Ignore any exceptions...
  130. // Get the cluster object
  131. ft.hr = m_pCluster.CoCreateInstance( _uuidof(Cluster));
  132. if (ft.HrFailed())
  133. {
  134. ft.Trace(VSSDBG_GEN, L"m_pCluster.CoCreateInstance( _uuidof(Cluster)) [0x%08lx]", ft.hr);
  135. return ft.Exit(false);
  136. }
  137. // Open the local cluster
  138. ft.hr = m_pCluster->Open(CVssComBSTR(pwszCluster? pwszCluster: L""));
  139. if (ft.HrFailed())
  140. {
  141. ft.Trace(VSSDBG_GEN, L"m_pCluster->Open(%s) [0x%08lx]", pwszCluster, ft.hr);
  142. return ft.Exit(false);
  143. }
  144. return ft.Exit(true);
  145. }
  146. // Make pwszFromVolumeName to be dependent on pwszToVolumeName
  147. // - Returns TRUE if the dependency was added
  148. // - Returns FALSE if a dependency does not need to be added
  149. // - Throws E_INVALIDARG on bad parameters
  150. // - Throws VSS_E_VOLUME_NOT_SUPPORTED when attempting to add a bogus dependency
  151. bool CVssClusterAPI::AddDependency(
  152. IN LPCWSTR pwszFromVolumeName,
  153. IN LPCWSTR pwszToVolumeName
  154. ) throw(HRESULT)
  155. {
  156. CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::AddDependency" );
  157. ft.Trace( VSSDBG_GEN, L"Parameters: From = %s, To = %s", pwszFromVolumeName, pwszToVolumeName);
  158. if ((pwszFromVolumeName == NULL) || (pwszFromVolumeName == NULL)) {
  159. BS_ASSERT(false);
  160. ft.Throw(VSSDBG_GEN, E_INVALIDARG, L"NULL parameters");
  161. }
  162. CComPtr<ISClusResource> pFromResource = GetPhysicalDiskResourceForVolumeName(pwszFromVolumeName);
  163. CComPtr<ISClusResource> pToResource = GetPhysicalDiskResourceForVolumeName(pwszToVolumeName);
  164. // Ignore the case if no volumes is shared
  165. if ((pFromResource == NULL) && (pToResource == NULL)) {
  166. ft.Trace(VSSDBG_GEN, L"Original and diff area volumes are no shared. No dependency cannot be added");
  167. return ft.Exit(false);
  168. }
  169. // Either both volumes must belong to resources or none of them
  170. if ((pFromResource == NULL) && (pToResource != NULL))
  171. ft.Throw(VSSDBG_GEN, VSS_E_VOLUME_NOT_SUPPORTED,
  172. L"Cannot create a dependency from a non-shared volume to a shared diff area");
  173. if ((pFromResource != NULL) && (pToResource == NULL))
  174. ft.Throw(VSSDBG_GEN, VSS_E_VOLUME_NOT_SUPPORTED,
  175. L"Cannot create a dependency from a shared volume to a non-shared diff area");
  176. BS_ASSERT((pFromResource != NULL) && (pToResource != NULL));
  177. // Ignore if both belong to the same physical disk
  178. if (AreResourcesEqual(pFromResource, pToResource)) {
  179. if (wcscmp(pwszFromVolumeName, pwszToVolumeName) == 0)
  180. {
  181. ft.Trace(VSSDBG_GEN, L"Volumes are identical. No dependency is added");
  182. return ft.Exit(false);
  183. }
  184. else
  185. ft.Throw(VSSDBG_GEN, VSS_E_VOLUME_NOT_SUPPORTED,
  186. L"Cannot create a dependency between different volumes on the same disk resource");
  187. }
  188. // Now check if the dependency already exists
  189. if (IsDependencyAlreadyEstablished(pFromResource, pToResource))
  190. {
  191. ft.Trace(VSSDBG_GEN, L"Dependency already exists.");
  192. return ft.Exit(false);
  193. }
  194. // Now check if we can establish a dependency (for ex. the resources are part of the same group)
  195. if (!CanEstablishDependency(pFromResource, pToResource))
  196. ft.Throw(VSSDBG_GEN, VSS_E_VOLUME_NOT_SUPPORTED, L"Cannot create a dependency between volumes");
  197. // Get the dependencies collection for the "FROM" resource
  198. CComPtr<ISClusResDependencies> pDependencies;
  199. CHECK_COM( pFromResource->get_Dependencies(&pDependencies) );
  200. // Get the list of resources that would need to be brought online after this resource is brought online
  201. CVssClusterResourceList finalList;
  202. GetFinalOnlineResourceList(pFromResource, finalList);
  203. // Take the resource offline
  204. TakeResourceOffline(pFromResource);
  205. // Add the dependency
  206. ft.hr = pDependencies->AddItem(pToResource);
  207. // Bring all the affected resources back online
  208. BringResourceListOnline(finalList);
  209. // Rethrow if needed
  210. if (ft.HrFailed())
  211. ft.ReThrow();
  212. return ft.Exit(true);
  213. }
  214. // Remove the dependency
  215. // - Returns TRUE if the dependency was removed
  216. // - Returns FALSE if the dependency could not be found
  217. // - Throws E_INVALIDARG on bad parameters
  218. bool CVssClusterAPI::RemoveDependency(
  219. IN LPCWSTR pwszFromVolumeName,
  220. IN LPCWSTR pwszToVolumeName
  221. ) throw(HRESULT)
  222. {
  223. CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::RemoveDependency" );
  224. ft.Trace( VSSDBG_GEN, L"Parameters: From = %s, To = %s", pwszFromVolumeName, pwszToVolumeName);
  225. if ((pwszFromVolumeName == NULL) || (pwszFromVolumeName == NULL)) {
  226. BS_ASSERT(false);
  227. ft.Throw(VSSDBG_GEN, E_INVALIDARG, L"NULL parameters");
  228. }
  229. // Get the corresponding resources
  230. CComPtr<ISClusResource> pFromResource = GetPhysicalDiskResourceForVolumeName(pwszFromVolumeName);
  231. CComPtr<ISClusResource> pToResource = GetPhysicalDiskResourceForVolumeName(pwszToVolumeName);
  232. // If both volumes are not belonging to any resource, then ignore them
  233. if ((pFromResource == NULL) || (pToResource == NULL)) {
  234. ft.Trace(VSSDBG_GEN, L"One of the volumes does not belong to a physical disk resource.");
  235. return ft.Exit(false);
  236. }
  237. if (AreResourcesEqual(pFromResource, pToResource)) {
  238. ft.Trace(VSSDBG_GEN, L"Volumes belong to the same resource. No dependency is removed");
  239. return ft.Exit(false);
  240. }
  241. // Get the dependents collection for the "FROM" resource
  242. CComPtr<ISClusResDependencies> pDependencies;
  243. CHECK_COM( pFromResource->get_Dependencies(&pDependencies) );
  244. // Get the dependencies count
  245. LONG lDependenciesCount = 0;
  246. CHECK_COM( pDependencies->get_Count(&lDependenciesCount) );
  247. // Iterate through all dependencies
  248. for (INT nDependencyIndex = 1; nDependencyIndex <= lDependenciesCount; nDependencyIndex++)
  249. {
  250. // Get the dependency with that index
  251. CComPtr<ISClusResource> pDependencyResource;
  252. CComVariant varDependencyIndex = nDependencyIndex;
  253. CHECK_COM( pDependencies->get_Item(varDependencyIndex, &pDependencyResource) );
  254. // Check to see if this is our resource. If not, continue
  255. if (!AreResourcesEqual(pToResource, pDependencyResource))
  256. continue;
  257. // Get the list of resources that would need to be brought online after this resource is brought online
  258. CVssClusterResourceList finalList;
  259. GetFinalOnlineResourceList(pFromResource, finalList);
  260. // Take the resource offline
  261. TakeResourceOffline(pFromResource);
  262. // Remove the association
  263. ft.hr = pDependencies->RemoveItem(varDependencyIndex);
  264. // Bring all the affected resources back online
  265. BringResourceListOnline(finalList);
  266. // Rethrow if needed
  267. if (ft.HrFailed())
  268. ft.ReThrow();
  269. return ft.Exit(true);
  270. }
  271. return ft.Exit(false);
  272. }
  273. // Adds the registry key to the cluster resource that corresponds to our disk.
  274. // - Returns TRUE if the reg key was added
  275. // - Returns FALSE if there is no need to add a reg key
  276. // - Throws E_INVALIDARG on bad parameters
  277. // NOTE: this shouldn't fail if the checkpoint is already added!
  278. bool CVssClusterAPI::AddRegistryKey(
  279. IN LPCWSTR pwszVolumeName,
  280. IN LPCWSTR pwszPathFormat,
  281. IN ...
  282. ) throw(HRESULT)
  283. {
  284. CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::AddRegistryKey" );
  285. ft.Trace( VSSDBG_GEN, L"Parameters: From = %s, Reg = %s", pwszVolumeName, pwszPathFormat);
  286. if ((pwszVolumeName == NULL) || (pwszPathFormat == NULL)) {
  287. BS_ASSERT(false);
  288. ft.Throw(VSSDBG_GEN, E_INVALIDARG, L"NULL parameters");
  289. }
  290. // Build the path to the key
  291. WCHAR wszKeyPath[x_nVssMaxRegBuffer];
  292. va_list marker;
  293. va_start( marker, pwszPathFormat );
  294. ft.hr = StringCchVPrintfW( STRING_CCH_PARAM(wszKeyPath), pwszPathFormat, marker );
  295. va_end( marker );
  296. if (ft.HrFailed())
  297. ft.TranslateGenericError(VSSDBG_GEN, ft.hr, L"StringCchVPrintfW()");
  298. ft.Trace( VSSDBG_GEN, L"Formatted path: '%s'", wszKeyPath);
  299. // Build the BSTR
  300. CVssComBSTR bstrPath = wszKeyPath;
  301. // Get the corresponding resource
  302. CComPtr<ISClusResource> pResource = GetPhysicalDiskResourceForVolumeName(pwszVolumeName);
  303. if (pResource == NULL) {
  304. ft.Trace(VSSDBG_GEN, L"The volume is not shared. No registry key can be added");
  305. return ft.Exit(false);
  306. }
  307. // Get the Registry checkpoints collection
  308. CComPtr<ISClusRegistryKeys> pRegKeys;
  309. CHECK_COM( pResource->get_RegistryKeys(&pRegKeys) );
  310. // Enumerate the existing checkpoints (and make sure we don't add the same checkpoint twice)
  311. //
  312. // Get the checkpoints count
  313. LONG lRegKeysCount = 0;
  314. CHECK_COM( pRegKeys->get_Count(&lRegKeysCount) );
  315. // Iterate through all checkpoints on that resource
  316. for (INT nRegKeyIndex = 1; nRegKeyIndex <= lRegKeysCount; nRegKeyIndex++)
  317. {
  318. // Get the registry key
  319. CComVariant varRegKeyIndex = nRegKeyIndex;
  320. CVssComBSTR bstrRegKey;
  321. CHECK_COM( pRegKeys->get_Item(varRegKeyIndex, &bstrRegKey) );
  322. // Check to see if this is our registry key
  323. if (!bstrRegKey || _wcsicmp(bstrRegKey, wszKeyPath))
  324. continue;
  325. // We found an existing checkpoint with the same registry path
  326. return ft.Exit(false);
  327. }
  328. // Add the registry key
  329. CHECK_COM( pRegKeys->AddItem( bstrPath ) );
  330. return ft.Exit(true);
  331. }
  332. // Removes the registry key from the cluster resource that corresponds to our disk.
  333. bool CVssClusterAPI::RemoveRegistryKey(
  334. IN LPCWSTR pwszVolumeName,
  335. IN LPCWSTR pwszPathFormat,
  336. IN ...
  337. ) throw(HRESULT)
  338. {
  339. CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::RemoveRegistryKey" );
  340. ft.Trace( VSSDBG_GEN, L"Parameters: From = %s, Reg = %s", pwszVolumeName, pwszPathFormat);
  341. if ((pwszVolumeName == NULL) || (pwszPathFormat == NULL)) {
  342. BS_ASSERT(false);
  343. ft.Throw(VSSDBG_GEN, E_INVALIDARG, L"NULL parameters");
  344. }
  345. // Build the path to the key
  346. WCHAR wszKeyPath[x_nVssMaxRegBuffer];
  347. va_list marker;
  348. va_start( marker, pwszPathFormat );
  349. ft.hr = StringCchVPrintfW( STRING_CCH_PARAM(wszKeyPath), pwszPathFormat, marker );
  350. va_end( marker );
  351. if (ft.HrFailed())
  352. ft.TranslateGenericError(VSSDBG_GEN, ft.hr, L"StringCchVPrintfW()");
  353. ft.Trace( VSSDBG_GEN, L"Formatted path: '%s'", wszKeyPath);
  354. // Get the corresponding resource
  355. CComPtr<ISClusResource> pResource = GetPhysicalDiskResourceForVolumeName(pwszVolumeName);
  356. if (pResource == NULL) {
  357. ft.Trace(VSSDBG_GEN, L"The volume is not shared. No registry key can be removed");
  358. return ft.Exit(false);
  359. }
  360. // Get the Registry checkpoints collection
  361. CComPtr<ISClusRegistryKeys> pRegKeys;
  362. CHECK_COM( pResource->get_RegistryKeys(&pRegKeys) );
  363. // Get the checkpoints count
  364. LONG lRegKeysCount = 0;
  365. CHECK_COM( pRegKeys->get_Count(&lRegKeysCount) );
  366. // Iterate through all checkpoints on that resource
  367. for (INT nRegKeyIndex = 1; nRegKeyIndex <= lRegKeysCount; nRegKeyIndex++)
  368. {
  369. // Get the registry key
  370. CComVariant varRegKeyIndex = nRegKeyIndex;
  371. CVssComBSTR bstrRegKey;
  372. CHECK_COM( pRegKeys->get_Item(varRegKeyIndex, &bstrRegKey) );
  373. // Check to see if this is our registry key
  374. if (!bstrRegKey || _wcsicmp(bstrRegKey, wszKeyPath))
  375. continue;
  376. // Remove the association
  377. CHECK_COM( pRegKeys->RemoveItem(varRegKeyIndex) );
  378. return ft.Exit(true);
  379. }
  380. return ft.Exit(false);
  381. }
  382. // Create a task scheduler resource
  383. // Create the dependency from it to the Physical Disk Resource identified by the volume name
  384. // Then bring the resource online
  385. // - Returns TRUE if the TS resource was created
  386. // - Returns FALSE if no task needs to be created
  387. bool CVssClusterAPI::CreateTaskSchedulerResource(
  388. IN LPCWSTR pwszTaskSchedulerResourceName, // This will be the task name also
  389. IN LPCWSTR pwszApplicationName,
  390. IN LPCWSTR pwszApplicationParams,
  391. IN INT nTaskTriggersCount,
  392. IN PTASK_TRIGGER ptsTaskTriggersArray,
  393. IN LPCWSTR pwszMakeDependentOnVolumeName
  394. ) throw(HRESULT)
  395. {
  396. CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::CreateTaskSchedulerResource" );
  397. // Verify parameters
  398. ft.Trace( VSSDBG_GEN, L"Parameters: Task = %s, AppName = '%s', Params = '%s', Volume = '%s'",
  399. pwszTaskSchedulerResourceName, pwszApplicationName, pwszApplicationParams, pwszMakeDependentOnVolumeName);
  400. if ((pwszTaskSchedulerResourceName == NULL) || (pwszTaskSchedulerResourceName[0] == L'\0') ||
  401. (pwszApplicationName == NULL) || (pwszApplicationName[0] == L'\0') ||
  402. (pwszApplicationParams == NULL) || (pwszApplicationParams[0] == L'\0') ||
  403. (nTaskTriggersCount <= 0) || (ptsTaskTriggersArray == NULL) ||
  404. (pwszMakeDependentOnVolumeName == NULL) || (pwszMakeDependentOnVolumeName[0] == L'\0'))
  405. {
  406. BS_ASSERT(false);
  407. ft.Throw(VSSDBG_GEN, E_INVALIDARG, L"NULL parameters");
  408. }
  409. // Get the resource for the volume
  410. CComPtr<ISClusResource> pPhysicalDiskResource = GetPhysicalDiskResourceForVolumeName(pwszMakeDependentOnVolumeName);
  411. if (pPhysicalDiskResource == NULL) {
  412. ft.Trace(VSSDBG_GEN, L"The volume is not shared. No task scheduler can be added");
  413. return ft.Exit(false);
  414. }
  415. // Get the resource group
  416. CComPtr<ISClusResGroup> pGroup;
  417. CHECK_COM( pPhysicalDiskResource->get_Group(&pGroup) );
  418. // Get the resources list
  419. CComPtr<ISClusResGroupResources> pResources;
  420. CHECK_COM( pGroup->get_Resources(&pResources) );
  421. // Build the resource name
  422. CVssComBSTR bstrResourceName = pwszTaskSchedulerResourceName;
  423. // Build the resource type
  424. CVssComBSTR bstrResourceType = x_ClusReg_TypeName_TaskScheduler;
  425. // Create our Task Scheduler resource
  426. CComPtr<ISClusResource> pResource;
  427. CHECK_COM( pResources->CreateItem(
  428. bstrResourceName,
  429. bstrResourceType,
  430. CLUSTER_RESOURCE_DEFAULT_MONITOR,
  431. &pResource) );
  432. // Get the private properties collection
  433. CComPtr<ISClusProperties> pProperties;
  434. CHECK_COM( pResource->get_PrivateProperties(&pProperties) );
  435. // Add the Application Name private property
  436. CComPtr<ISClusProperty> pProperty;
  437. CHECK_COM( pProperties->CreateItem(
  438. CVssComBSTR(x_ClusReg_Name_TaskScheduler_ApplicationName),
  439. CComVariant((BSTR)CVssComBSTR(pwszApplicationName)),
  440. &pProperty) );
  441. // Add the Application Params private property
  442. pProperty = NULL;
  443. CHECK_COM( pProperties->CreateItem(
  444. CVssComBSTR(x_ClusReg_Name_TaskScheduler_ApplicationParams),
  445. CComVariant((BSTR)CVssComBSTR(pwszApplicationParams)),
  446. &pProperty) );
  447. // Add the task trigger as a binary value
  448. CComVariant varTaskTrigger;
  449. CopyBinaryIntoVariant( (PBYTE)ptsTaskTriggersArray, sizeof(TASK_TRIGGER) * nTaskTriggersCount, varTaskTrigger);
  450. BS_ASSERT(varTaskTrigger.vt == (VT_ARRAY | VT_UI1));
  451. BS_ASSERT(varTaskTrigger.parray->cDims == 1);
  452. BS_ASSERT(varTaskTrigger.parray->rgsabound[0].cElements == sizeof(TASK_TRIGGER) * nTaskTriggersCount);
  453. ft.Trace(VSSDBG_GEN, L"Writing a binary property with %d bytes", varTaskTrigger.parray->rgsabound[0].cElements);
  454. // Add the Trigger Array private property
  455. pProperty = NULL;
  456. CHECK_COM( pProperties->CreateItem( CVssComBSTR(x_ClusReg_Name_TaskScheduler_TriggerArray),
  457. varTaskTrigger, &pProperty) );
  458. // Save changes
  459. CComVariant varStatusCode;
  460. CHECK_COM( pProperties->SaveChanges(&varStatusCode) );
  461. BS_ASSERT(varStatusCode.vt == VT_ERROR);
  462. ft.hr = HRESULT_FROM_WIN32( varStatusCode.scode );
  463. if (ft.HrFailed())
  464. ft.TranslateComError( VSSDBG_GEN, L"pProperties->SaveChanges(&varStatusCode)");
  465. // Add the dependency between this resource and the volume resource
  466. CComPtr<ISClusResDependencies> pDependencies;
  467. CHECK_COM( pResource->get_Dependencies(&pDependencies) );
  468. CHECK_COM( pDependencies->AddItem(pPhysicalDiskResource) );
  469. // Bring the resource online
  470. // This will insert the task into the TS DB
  471. BringResourceOnline(pResource);
  472. return ft.Exit(true);
  473. }
  474. // Update task scheduler information
  475. bool CVssClusterAPI::UpdateTaskSchedulerResource(
  476. IN LPCWSTR pwszTaskSchedulerResourceName,
  477. IN INT nTaskTriggersCount,
  478. IN PTASK_TRIGGER ptsTaskTriggersArray
  479. ) throw(HRESULT)
  480. {
  481. CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::UpdateTaskSchedulerResource" );
  482. // Verify parameters
  483. ft.Trace( VSSDBG_GEN, L"Parameters: Resource = %s, pTaskTrigger = %p",
  484. pwszTaskSchedulerResourceName, ptsTaskTriggersArray);
  485. if ((pwszTaskSchedulerResourceName == NULL) || (pwszTaskSchedulerResourceName[0] == L'\0') ||
  486. (nTaskTriggersCount <= 0) || (ptsTaskTriggersArray == NULL))
  487. {
  488. BS_ASSERT(false);
  489. ft.Throw(VSSDBG_GEN, E_INVALIDARG, L"NULL parameters");
  490. }
  491. // Get the resources collection
  492. CComPtr<ISClusResources> pResources;
  493. BS_ASSERT(m_pCluster);
  494. CHECK_COM( m_pCluster->get_Resources(&pResources) );
  495. // Get the resources count
  496. LONG lResourcesCount = 0;
  497. CHECK_COM( pResources->get_Count(&lResourcesCount) );
  498. // Iterate through all resources
  499. bool bFound = false;
  500. CComPtr<ISClusResource> pResource;
  501. for (INT nResourceIndex = 1; nResourceIndex <= lResourcesCount; nResourceIndex++)
  502. {
  503. // Get the resource interface
  504. CComVariant varResourceIndex = nResourceIndex;
  505. pResource = NULL;
  506. CHECK_COM( pResources->get_Item(varResourceIndex, &pResource) );
  507. // Ignore resources with type != "Task Scheduler"
  508. CVssComBSTR bstrTypeName;
  509. CHECK_COM( pResource->get_TypeName(&bstrTypeName) );
  510. if (!bstrTypeName || wcscmp(x_ClusReg_TypeName_TaskScheduler, bstrTypeName)) {
  511. // ft.Trace(VSSDBG_GEN, L"Different type names: %s - %s", x_ClusReg_TypeName_TaskScheduler, bstrTypeName);
  512. continue;
  513. }
  514. // Ignore resources with a different name
  515. CVssComBSTR bstrResourceName;
  516. CHECK_COM( pResource->get_Name(&bstrResourceName) );
  517. if (!bstrResourceName || wcscmp(pwszTaskSchedulerResourceName, bstrResourceName)) {
  518. // ft.Trace(VSSDBG_GEN, L"Different names: %s - %s", pwszTaskSchedulerResourceName, bstrResourceName);
  519. continue;
  520. }
  521. bFound = true;
  522. break;
  523. }
  524. if (!bFound) {
  525. ft.Trace(VSSDBG_GEN, L"Resource not found");
  526. return ft.Exit(false);
  527. }
  528. // We found the resource (assuming the names uniquely identify a resource)
  529. // Get the private properties collection
  530. CComPtr<ISClusProperties> pProperties;
  531. CHECK_COM( pResource->get_PrivateProperties(&pProperties) );
  532. // Get the properties count
  533. LONG lPropertiesCount = 0;
  534. CHECK_COM( pProperties->get_Count(&lPropertiesCount) );
  535. // Iterate through all properties
  536. bFound = false;
  537. CComPtr<ISClusProperty> pProperty;
  538. for (INT nPropertyIndex = 1; nPropertyIndex <= lPropertiesCount; nPropertyIndex++)
  539. {
  540. // Get the resource type interface
  541. CComVariant varPropertyIndex = nPropertyIndex;
  542. pProperty = NULL;
  543. CHECK_COM( pProperties->get_Item(varPropertyIndex, &pProperty) );
  544. // Get the property name
  545. CVssComBSTR bstrPropertyName;
  546. CHECK_COM( pProperty->get_Name(&bstrPropertyName) );
  547. // Check to see if this is our "Trigger Array" property.
  548. // If not, continue
  549. if (!bstrPropertyName || wcscmp(bstrPropertyName, x_ClusReg_Name_TaskScheduler_TriggerArray))
  550. continue;
  551. // We found the property
  552. bFound = true;
  553. break;
  554. }
  555. // If we didn't found that property, then return
  556. if (!bFound) {
  557. // The TriggerArray property is mandatory in our case
  558. BS_ASSERT(false);
  559. ft.LogGenericWarning(VSSDBG_COORD, L"pProperties->get_Item(varPropertyIndex, &pProperty) [%s, %s]",
  560. x_ClusReg_Name_TaskScheduler_TriggerArray, pwszTaskSchedulerResourceName);
  561. return ft.Exit(false);
  562. }
  563. // Add the task trigger as a binary value
  564. CComVariant varTaskTrigger;
  565. CopyBinaryIntoVariant( (PBYTE)ptsTaskTriggersArray, sizeof(TASK_TRIGGER) * nTaskTriggersCount, varTaskTrigger);
  566. // Update the Task Trigger private property
  567. CHECK_COM( pProperty->put_Value(varTaskTrigger) );
  568. // Save changes
  569. CComVariant varStatusCode;
  570. CHECK_COM( pProperties->SaveChanges(&varStatusCode) );
  571. // This error code means: The properties were stored but not all changes will
  572. // take effect until the next time the resource is brought online.
  573. BS_ASSERT(varStatusCode.vt == VT_ERROR);
  574. ft.hr = HRESULT_FROM_WIN32( varStatusCode.scode );
  575. if (ft.hr != HRESULT_FROM_WIN32( ERROR_RESOURCE_PROPERTIES_STORED ) )
  576. ft.TranslateComError( VSSDBG_GEN, L"pProperties->SaveChanges() => [0x%08lx]", ft.hr);
  577. // Take the resource offline
  578. // This will delete the task into the TS DB
  579. TakeResourceOffline(pResource);
  580. // Take the resource offline
  581. // This will insert the task into the TS DB
  582. BringResourceOnline(pResource);
  583. // Report success
  584. return ft.Exit(true);
  585. }
  586. // Delete a task scheduler resource.
  587. // Before that, take the resource offline and remove the dependency
  588. bool CVssClusterAPI::DeleteTaskSchedulerResource(
  589. IN LPCWSTR pwszTaskSchedulerResourceName
  590. ) throw(HRESULT)
  591. {
  592. CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::DeleteTaskSchedulerResource" );
  593. // Verify parameters
  594. ft.Trace( VSSDBG_GEN, L"Parameters: Resource = %s",
  595. pwszTaskSchedulerResourceName);
  596. if ((pwszTaskSchedulerResourceName == NULL) || (pwszTaskSchedulerResourceName[0] == L'\0'))
  597. {
  598. BS_ASSERT(false);
  599. ft.Throw(VSSDBG_GEN, E_INVALIDARG, L"NULL parameters");
  600. }
  601. // Get the resources collection
  602. CComPtr<ISClusResources> pResources;
  603. BS_ASSERT(m_pCluster);
  604. CHECK_COM( m_pCluster->get_Resources(&pResources) );
  605. // Get the resources count
  606. LONG lResourcesCount = 0;
  607. CHECK_COM( pResources->get_Count(&lResourcesCount) );
  608. // Iterate through all resources
  609. CComPtr<ISClusResource> pResource;
  610. for (INT nResourceIndex = 1; nResourceIndex <= lResourcesCount; nResourceIndex++)
  611. {
  612. // Get the resource interface
  613. CComVariant varResourceIndex = nResourceIndex;
  614. pResource = NULL;
  615. CHECK_COM( pResources->get_Item(varResourceIndex, &pResource) );
  616. // Ignore resources with type != "Task Scheduler"
  617. CVssComBSTR bstrTypeName;
  618. CHECK_COM( pResource->get_TypeName(&bstrTypeName) );
  619. if (!bstrTypeName || wcscmp(x_ClusReg_TypeName_TaskScheduler, bstrTypeName))
  620. continue;
  621. // Ignore resources with a different name
  622. CVssComBSTR bstrResourceName;
  623. CHECK_COM( pResource->get_Name(&bstrResourceName) );
  624. if (!bstrResourceName || wcscmp(pwszTaskSchedulerResourceName, bstrResourceName))
  625. continue;
  626. // We found the resource (assuming the names uniquely identify a resource)
  627. // Take the resource offline
  628. // This will delete the task into the TS DB
  629. TakeResourceOffline(pResource);
  630. // Now delete the resource from the cluster (this will remove any dependencies)
  631. CHECK_COM( pResource->Delete() );
  632. // Report success
  633. return ft.Exit(true);
  634. }
  635. return ft.Exit(false);
  636. }
  637. // Returns the Physical Disk resource that contains the given volume
  638. // If the volume doesn't belong to a physical disk resource, then return NULL
  639. // The returned volume must be freed with CoTaskMemFree
  640. ISClusResource* CVssClusterAPI::GetPhysicalDiskResourceForVolumeName(
  641. IN LPCWSTR pwszVolumeName
  642. ) throw(HRESULT)
  643. {
  644. CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::GetPhysicalDiskResourceForVolumeName" );
  645. // Verify parameters
  646. ft.Trace( VSSDBG_GEN, L"Parameters: Volume = %s", pwszVolumeName);
  647. if ((pwszVolumeName == NULL) || (pwszVolumeName[0] == L'\0'))
  648. {
  649. BS_ASSERT(false);
  650. ft.Throw(VSSDBG_GEN, E_INVALIDARG, L"NULL parameters");
  651. }
  652. // Get the resources collection
  653. CComPtr<ISClusResources> pResources;
  654. BS_ASSERT(m_pCluster);
  655. CHECK_COM( m_pCluster->get_Resources(&pResources) );
  656. // Get the resources count
  657. LONG lResourcesCount = 0;
  658. CHECK_COM( pResources->get_Count(&lResourcesCount) );
  659. // Iterate through all resources
  660. for (INT nResourceIndex = 1; nResourceIndex <= lResourcesCount; nResourceIndex++)
  661. {
  662. // Get the resource interface
  663. CComPtr<ISClusResource> pResource;
  664. CComVariant varResourceIndex = nResourceIndex;
  665. CHECK_COM( pResources->get_Item(varResourceIndex, &pResource) );
  666. // Checks if relates with the partition
  667. if (IsResourceRefferingVolume(pResource, pwszVolumeName))
  668. return ft.Exit(pResource.Detach());
  669. }
  670. // No resource found
  671. return ft.Exit( (ISClusResource*)NULL );
  672. }
  673. // Returns TRUE if a dependency can be established
  674. bool CVssClusterAPI::CanEstablishDependency(
  675. IN ISClusResource* pFromResource, // To be the dependent
  676. IN ISClusResource* pToResource // To be the dependency
  677. ) throw(HRESULT)
  678. {
  679. CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::CanEstablishDependency" );
  680. // Verify parameters
  681. ft.Trace( VSSDBG_GEN, L"Parameters: pFromResource = %p, pToResource = %p", pFromResource, pToResource);
  682. if ((pFromResource == NULL) || (pToResource == NULL))
  683. {
  684. BS_ASSERT(false);
  685. ft.Throw(VSSDBG_GEN, E_INVALIDARG, L"NULL parameters");
  686. }
  687. // Get the name of the first resource
  688. CComVariant varBoolResult;
  689. CHECK_COM( pFromResource->CanResourceBeDependent(pToResource, &varBoolResult) );
  690. BS_ASSERT( varBoolResult.vt == VT_BOOL );
  691. bool bResult = (varBoolResult.boolVal == VARIANT_TRUE);
  692. return ft.Exit(bResult);
  693. }
  694. // Returns TRUE if a dependency is already established
  695. // - Returns TRUE if the dependency already exists
  696. // - Returns FALSE if the dependency could not be found
  697. // - Throws E_INVALIDARG on bad parameters
  698. bool CVssClusterAPI::IsDependencyAlreadyEstablished(
  699. IN ISClusResource* pFromResource, // Is dependent
  700. IN ISClusResource* pToResource // Is dependency
  701. )
  702. {
  703. CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::IsDependencyAlreadyEstablished" );
  704. // Verify parameters
  705. ft.Trace( VSSDBG_GEN, L"Parameters: pFromResource = %p, pToResource = %p", pFromResource, pToResource);
  706. if ((pFromResource == NULL) || (pToResource == NULL))
  707. {
  708. BS_ASSERT(false);
  709. ft.Throw(VSSDBG_GEN, E_INVALIDARG, L"NULL parameters");
  710. }
  711. // Get the dependents collection for the "FROM" resource
  712. CComPtr<ISClusResDependencies> pDependencies;
  713. CHECK_COM( pFromResource->get_Dependencies(&pDependencies) );
  714. // Get the dependencies count
  715. LONG lDependenciesCount = 0;
  716. CHECK_COM( pDependencies->get_Count(&lDependenciesCount) );
  717. // Iterate through all dependencies
  718. for (INT nDependencyIndex = 1; nDependencyIndex <= lDependenciesCount; nDependencyIndex++)
  719. {
  720. // Get the dependency with that index
  721. CComPtr<ISClusResource> pDependencyResource;
  722. CComVariant varDependencyIndex = nDependencyIndex;
  723. CHECK_COM( pDependencies->get_Item(varDependencyIndex, &pDependencyResource) );
  724. // Check to see if this is our resource. If not, continue
  725. if (!AreResourcesEqual(pToResource, pDependencyResource))
  726. continue;
  727. // We find the dependency
  728. return ft.Exit(true);
  729. }
  730. return ft.Exit(false);
  731. }
  732. // Returns true if hte volume belongs to a Physical Disk resource
  733. bool CVssClusterAPI::IsVolumeBelongingToPhysicalDiskResource(
  734. IN LPCWSTR pwszVolumeName
  735. ) throw(HRESULT)
  736. {
  737. CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::IsVolumeBelongingToPhysicalDiskResource" );
  738. if (!m_pCluster)
  739. return ft.Exit(false);
  740. CComPtr<ISClusResource> ptrResource = GetPhysicalDiskResourceForVolumeName(pwszVolumeName);
  741. return ft.Exit(ptrResource != NULL);
  742. }
  743. // Returns TRUE if the resource is a Physical Disk resource that contains the given volume
  744. bool CVssClusterAPI::IsResourceRefferingVolume(
  745. IN ISClusResource* pResource,
  746. IN LPCWSTR pwszVolumeName
  747. ) throw(HRESULT)
  748. {
  749. CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::IsResourceRefferingVolume" );
  750. BS_ASSERT(pResource);
  751. BS_ASSERT(pwszVolumeName);
  752. CVssComBSTR bstrName;
  753. CHECK_COM( pResource->get_Name(&bstrName) );
  754. ft.Trace( VSSDBG_GEN, L"Parameters: resource name = %s, pwszVolumeName = %s", (LPWSTR) bstrName, pwszVolumeName);
  755. // Ignore resources that are not in the storage class
  756. CLUSTER_RESOURCE_CLASS rcClassInfo;
  757. CHECK_COM( pResource->get_ClassInfo(&rcClassInfo) );
  758. if (rcClassInfo != CLUS_RESCLASS_STORAGE)
  759. return ft.Exit(false);
  760. // Ignore resources with type != "Physical Disk"
  761. CVssComBSTR bstrTypeName;
  762. CHECK_COM( pResource->get_TypeName(&bstrTypeName) );
  763. if (!bstrTypeName || wcscmp(x_ClusReg_TypeName_PhysicalDisk, bstrTypeName))
  764. return ft.Exit(false);
  765. //
  766. // Get the list of volume MpM guids
  767. //
  768. // Look into the MULTI_SZ string for our volume
  769. // We assume the format of one string is
  770. // "[byte offset] [volume in kernel-mode format]"
  771. //
  772. // This private property is described in the following documentation:
  773. // \\index1\sdnt\commontest\wtt\wttwebddb\webdocs\management\mount_point_support.doc
  774. //
  775. // Get the private properties collection
  776. CComPtr<ISClusProperties> pProperties;
  777. CHECK_COM( pResource->get_PrivateProperties(&pProperties) );
  778. // Get the properties count
  779. LONG lPropertiesCount = 0;
  780. CHECK_COM( pProperties->get_Count(&lPropertiesCount) );
  781. // Iterate through all properties
  782. bool bFound = false;
  783. CComPtr<ISClusProperty> pProperty;
  784. for (INT nPropertyIndex = 1; nPropertyIndex <= lPropertiesCount; nPropertyIndex++)
  785. {
  786. // Get the resource type interface
  787. CComVariant varPropertyIndex = nPropertyIndex;
  788. pProperty = NULL;
  789. CHECK_COM( pProperties->get_Item(varPropertyIndex, &pProperty) );
  790. // Get the property name
  791. CVssComBSTR bstrPropertyName;
  792. CHECK_COM( pProperty->get_Name(&bstrPropertyName) );
  793. // Check to see if this is our "Mount Volume Name" property.
  794. // If not, continue
  795. if (!bstrPropertyName || wcscmp(bstrPropertyName, x_ClusReg_Name_PhysicalDisk_MPVolGuids))
  796. continue;
  797. // We found the property
  798. bFound = true;
  799. break;
  800. }
  801. // If we didn't found that (optional) property, then return
  802. if (!bFound)
  803. return ft.Exit(false);
  804. // Copy the volume locally in order to convert it
  805. CVssAutoLocalString strVolumeName;
  806. strVolumeName.CopyFrom(pwszVolumeName);
  807. // Convert the volume name into the Kernel-mode representation
  808. if (!ConvertVolMgmtVolumeNameIntoKernelObject( strVolumeName )) {
  809. // Programming error - the original volume is not in the \\?\Volume{GUID}\ format
  810. BS_ASSERT(false);
  811. ft.TranslateGenericError(VSSDBG_GEN, E_UNEXPECTED,
  812. L"ConvertVolMgmtVolumeNameIntoKernelObject(%s)", strVolumeName);
  813. }
  814. // Get the value list
  815. CComPtr<ISClusPropertyValues> pValues;
  816. CHECK_COM( pProperty->get_Values(&pValues) );
  817. // Get the values count
  818. LONG lValueCount = 0;
  819. CHECK_COM( pValues->get_Count(&lValueCount) );
  820. ft.Trace(VSSDBG_GEN, L"Number of volumes: %ld", lValueCount);
  821. // Iterate through all the values
  822. CComPtr<ISClusPropertyValue> pValue;
  823. for (INT nValueIndex = 1; nValueIndex <= lValueCount; nValueIndex++)
  824. {
  825. // Get the resource type interface
  826. CComVariant varValueIndex = nValueIndex;
  827. pValue = NULL;
  828. CHECK_COM( pValues->get_Item(varValueIndex, &pValue) );
  829. // Get the count of Data values. It must be a positive number
  830. LONG lDataCount;
  831. CHECK_COM( pValue->get_DataCount(&lDataCount) );
  832. // Ignore properties with no data
  833. if (lDataCount <=0) {
  834. BS_ASSERT(false);
  835. continue;
  836. }
  837. // Get the Data collection
  838. CComPtr<ISClusPropertyValueData> pData;
  839. CHECK_COM( pValue->get_Data(&pData) );
  840. // Get the count of Data values.
  841. // Just verify that is the same number
  842. LONG lInternalDataCount;
  843. CHECK_COM( pData->get_Count(&lInternalDataCount) );
  844. BS_ASSERT(lInternalDataCount == lDataCount);
  845. // Get the property value.
  846. CComVariant varVolumeMountPoint;
  847. for (INT nDataIndex = 1; nDataIndex <= lDataCount; nDataIndex++)
  848. {
  849. CComVariant varDataIndex = nDataIndex;
  850. varVolumeMountPoint.Clear();
  851. CHECK_COM( pData->get_Item(varDataIndex, &varVolumeMountPoint) );
  852. // This has to be a BSTR
  853. if (varVolumeMountPoint.vt != VT_BSTR) {
  854. BS_ASSERT(false);
  855. ft.TranslateGenericError(VSSDBG_GEN, E_UNEXPECTED, L"pProperty->get_Value() != VT_BSTR");
  856. }
  857. LPWSTR pwszMountPoint = varVolumeMountPoint.bstrVal;
  858. ft.Trace(VSSDBG_GEN, L"Processing volume: %s", pwszMountPoint);
  859. // Skip the hex number composing the internal offset
  860. for(;isalnum(pwszMountPoint[0]);pwszMountPoint++);
  861. // Skip the spaces after the number
  862. for(;iswspace(pwszMountPoint[0]);pwszMountPoint++);
  863. // Now we get to a volume name (according to the current doc). Compare it to the original volume.
  864. if (_wcsicmp(pwszMountPoint, strVolumeName) == 0)
  865. return ft.Exit(true);
  866. }
  867. }
  868. return ft.Exit(false);
  869. }
  870. // returns TRUE if the two COM objects are identifying the same resource
  871. bool CVssClusterAPI::AreResourcesEqual(
  872. IN ISClusResource* pResource1,
  873. IN ISClusResource* pResource2
  874. ) throw(HRESULT)
  875. {
  876. CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::AreResourcesEqual" );
  877. BS_ASSERT(pResource1);
  878. BS_ASSERT(pResource2);
  879. // Get the name of the first resource
  880. CVssComBSTR bstrName1;
  881. CHECK_COM( pResource1->get_Name(&bstrName1) );
  882. // Get the name of the second resource
  883. CVssComBSTR bstrName2;
  884. CHECK_COM( pResource2->get_Name(&bstrName2) );
  885. ft.Trace( VSSDBG_GEN, L"Comparing: resource = %s, resource = %s", (LPWSTR) bstrName1, (LPWSTR) bstrName2);
  886. if (bstrName1 != bstrName2)
  887. return ft.Exit(false);
  888. return ft.Exit(true);
  889. }
  890. // Copy the given binary data into the variant
  891. void CVssClusterAPI::CopyBinaryIntoVariant(
  892. IN PBYTE pbData,
  893. IN DWORD cbSize,
  894. IN OUT CComVariant & variant
  895. )
  896. {
  897. CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::CopyBinaryIntoVariant" );
  898. // Describe the new array
  899. SAFEARRAYBOUND sab[ 1 ];
  900. sab[ 0 ].lLbound = 0;
  901. sab[ 0 ].cElements = cbSize;
  902. // allocate a one dimensional SafeArray of BYTES
  903. CVssAutoSafearrayPtr pSafeArray = ::SafeArrayCreate( VT_UI1, 1, sab );
  904. if (!pSafeArray.IsValid())
  905. ft.ThrowOutOfMemory(VSSDBG_GEN);
  906. // get a pointer to the SafeArray
  907. PBYTE pbArrayData = NULL;
  908. ft.hr = ::SafeArrayAccessData( pSafeArray, (PVOID *) &pbArrayData );
  909. if ( ft.HrFailed() )
  910. ft.TranslateGenericError(VSSDBG_GEN, ft.hr, L"::SafeArrayAccessData( pSafeArray, (PVOID *) &pb )");
  911. // transfer data into the array
  912. ::CopyMemory( pbArrayData, pbData, cbSize );
  913. // release the pointer into the SafeArray
  914. ft.hr = ::SafeArrayUnaccessData( pSafeArray );
  915. if ( ft.HrFailed() )
  916. ft.TranslateGenericError(VSSDBG_GEN, ft.hr, L"::SafeArrayUnaccessData( pSafeArray )");
  917. // Clear the variant
  918. variant.Clear();
  919. // tell the variant what it is holding onto
  920. variant.parray = pSafeArray.Detach();
  921. variant.vt = VT_ARRAY | VT_UI1;
  922. }
  923. // Take the resource offline
  924. void CVssClusterAPI::TakeResourceOffline(
  925. IN ISClusResource* pResource
  926. ) throw(HRESULT)
  927. {
  928. CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::TakeResourceOffline" );
  929. BS_ASSERT(pResource);
  930. // Get resource name
  931. CVssComBSTR bstrName;
  932. CHECK_COM( pResource->get_Name(&bstrName) );
  933. // Take the resource offline
  934. CComVariant varStillPending;
  935. CHECK_COM( pResource->Offline(m_dwOfflineTimeout, &varStillPending) );
  936. BS_ASSERT( varStillPending.vt == VT_BOOL );
  937. if (varStillPending.boolVal == VARIANT_TRUE)
  938. {
  939. ft.LogGenericWarning(VSSDBG_GEN, L"pResource->Offline(%ld, [VT_TRUE]) [%s]",
  940. (LONG)m_dwOfflineTimeout, (LPWSTR)bstrName);
  941. ft.Throw( VSSDBG_GEN, E_UNEXPECTED,
  942. L"The resource [%s] couldn't be brought offline in the required timeframe.",
  943. (LPWSTR)bstrName);
  944. }
  945. }
  946. // Bring the resource online
  947. void CVssClusterAPI::BringResourceOnline(
  948. IN ISClusResource* pResource
  949. ) throw(HRESULT)
  950. {
  951. CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::BringResourceOnline" );
  952. BS_ASSERT(pResource);
  953. // Get resource name
  954. CVssComBSTR bstrName;
  955. CHECK_COM( pResource->get_Name(&bstrName) );
  956. // Bring the resource online
  957. CComVariant varStillPending;
  958. CHECK_COM( pResource->Online(m_dwOnlineTimeout, &varStillPending) );
  959. BS_ASSERT( varStillPending.vt == VT_BOOL );
  960. if (varStillPending.boolVal == VARIANT_TRUE)
  961. {
  962. ft.LogGenericWarning(VSSDBG_GEN, L"pResource->Online(%ld, [VT_TRUE]) [%s]",
  963. (LONG)m_dwOnlineTimeout, (LPWSTR)bstrName);
  964. ft.Throw( VSSDBG_GEN, E_UNEXPECTED,
  965. L"The resource [%s] couldn't be brought online in the required timeframe.",
  966. (LPWSTR)bstrName);
  967. }
  968. }
  969. // Calculate the list of dependent resources that needs to be brought online after adding a
  970. // dependency fron this resource
  971. void CVssClusterAPI::GetFinalOnlineResourceList(
  972. IN ISClusResource* pResource,
  973. IN OUT CVssClusterResourceList & list
  974. ) throw(HRESULT)
  975. {
  976. CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::GetFinalOnlineResourceList" );
  977. // Determine if the resource is online.
  978. // If not, we stop here.
  979. CLUSTER_RESOURCE_STATE csState;
  980. CHECK_COM( pResource->get_State(&csState) );
  981. if (csState != ClusterResourceOnline)
  982. return;
  983. // Start with one resource only
  984. CVssClusterResourceList frontier;
  985. frontier.Push(pResource);
  986. while (true)
  987. {
  988. CComPtr<ISClusResource> pCurrentResource;
  989. frontier.Pop(pCurrentResource);
  990. // If the frontier is empty, the we are finished (the dependency graph cannot contain cycles)
  991. if (pCurrentResource == NULL)
  992. break;
  993. // Get the dependents collection for the current resource
  994. CComPtr<ISClusResDependents> pDependents;
  995. CHECK_COM( pCurrentResource->get_Dependents(&pDependents) );
  996. // Get the dependencies count
  997. LONG lDependentsCount = 0;
  998. CHECK_COM( pDependents->get_Count(&lDependentsCount) );
  999. // Iterate through all dependencies
  1000. DWORD dwOnlineDependentResources = 0;
  1001. for (INT nDependentIndex = 1; nDependentIndex <= lDependentsCount; nDependentIndex++)
  1002. {
  1003. // Get the dependency with that index
  1004. CComPtr<ISClusResource> pDependentResource;
  1005. CComVariant varDependentIndex = nDependentIndex;
  1006. CHECK_COM( pDependents->get_Item(varDependentIndex, &pDependentResource) );
  1007. // Determine if the resource is online.
  1008. // If not, we ignore it.
  1009. CHECK_COM( pDependentResource->get_State(&csState) );
  1010. if (csState != ClusterResourceOnline)
  1011. continue;
  1012. // We add this resource to the frontier
  1013. frontier.Push(pDependentResource);
  1014. dwOnlineDependentResources++;
  1015. }
  1016. // If this was a final resource (no dependents added to the list) then add it to the final list
  1017. if (dwOnlineDependentResources == 0)
  1018. list.Push(pCurrentResource);
  1019. }
  1020. }
  1021. // Bring the resources in the list online
  1022. void CVssClusterAPI::BringResourceListOnline(
  1023. IN CVssClusterResourceList & list
  1024. ) throw(HRESULT)
  1025. {
  1026. CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::BringResourceListOnline" );
  1027. while (true)
  1028. {
  1029. CComPtr<ISClusResource> pCurrentResource;
  1030. list.Pop(pCurrentResource);
  1031. // If the frontier is empty, then we are finished (the dependency graph cannot contain cycles)
  1032. if (pCurrentResource == NULL)
  1033. break;
  1034. // Determine if the resource is online.
  1035. // If not, we ignore it.
  1036. CLUSTER_RESOURCE_STATE csState;
  1037. CHECK_COM( pCurrentResource->get_State(&csState) );
  1038. if (csState != ClusterResourceOffline)
  1039. continue;
  1040. // Bring this resource online
  1041. BringResourceOnline(pCurrentResource);
  1042. }
  1043. }
  1044. // Bring the resources in the list online
  1045. void CVssClusterAPI::GetQuorumPath(
  1046. CComBSTR & bstrQuorumPath
  1047. ) throw(HRESULT)
  1048. {
  1049. CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::GetQuorumPath" );
  1050. bstrQuorumPath.Empty();
  1051. // Get the path to the Quorum log file
  1052. BS_ASSERT(m_pCluster);
  1053. CHECK_COM( m_pCluster->get_QuorumPath(&bstrQuorumPath) );
  1054. ft.Trace( VSSDBG_GEN, L"Quorum file path: %s", bstrQuorumPath);
  1055. if ((NULL == (LPWSTR)bstrQuorumPath) || (L'\0' == bstrQuorumPath[0])) {
  1056. BS_ASSERT(false);
  1057. ft.TranslateGenericError(VSSDBG_GEN, E_UNEXPECTED, L"get_QuorumPath([NULL])");
  1058. }
  1059. }
  1060. /////////////////////////////////////////////////////////////////////////////
  1061. // CVssClusterQuorumVolume definition and implementation
  1062. //
  1063. // Note: this class is separate from CVssClusterAPI so its instances can be static.
  1064. // Checks if the given volume is the quorum
  1065. // This will return FALSE in non-cluster case.
  1066. bool CVssClusterQuorumVolume::IsQuorumVolume(
  1067. IN LPCWSTR pwszVolumeName
  1068. ) throw(HRESULT)
  1069. {
  1070. CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterQuorumVolume::IsQuorumVolume" );
  1071. // If the volume is not initialized, then try to initialize it
  1072. if (!m_bQuorumVolumeInitialized)
  1073. InitializeQuorumVolume();
  1074. // Return TRUE only if this is the quorum volume
  1075. return ft.Exit(m_awszQuorumVolumeName.GetRef()
  1076. && (wcscmp(m_awszQuorumVolumeName.GetRef(), pwszVolumeName) == 0));
  1077. }
  1078. // Checks if the given volume is the quorum
  1079. // This will work in non-cluster case also.
  1080. void CVssClusterQuorumVolume::InitializeQuorumVolume() throw(HRESULT)
  1081. {
  1082. CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterQuorumVolume::InitializeQuorumVolume" );
  1083. BS_ASSERT(!m_bQuorumVolumeInitialized)
  1084. // Mark this as being already initialized
  1085. // (to not recompute the quorum each time we call IsQuorumVolume)
  1086. m_bQuorumVolumeInitialized = true;
  1087. // Try to initialize the cluster, if possibles
  1088. CVssClusterAPI clus;
  1089. bool bClusterInitialized = clus.Initialize(L"");
  1090. // If we don't have a cluster, then we don't have quorum volume.
  1091. if (!bClusterInitialized)
  1092. return;
  1093. // Get the quorum path
  1094. CComBSTR bstrQuorumPath;
  1095. clus.GetQuorumPath(bstrQuorumPath);
  1096. //
  1097. // Test if the path starts with a drive letter
  1098. // NOTE: We assume that the path is a drive letter (we ignore shares)
  1099. //
  1100. // 1) We should have room at least for "<drive letter>:\"
  1101. if (wcslen(bstrQuorumPath) < 3)
  1102. return;
  1103. // 2) This is not at share
  1104. if (bstrQuorumPath[0] == L'\\')
  1105. return;
  1106. // 3) Test if the rest of the path matches ":\"
  1107. if (bstrQuorumPath[1] != L':')
  1108. return;
  1109. if (bstrQuorumPath[2] != L'\\')
  1110. return;
  1111. // Get the volume path
  1112. WCHAR wszQuorumVolume[MAX_PATH];
  1113. if (!GetVolumePathNameW(bstrQuorumPath, STRING_CCH_PARAM(wszQuorumVolume)))
  1114. {
  1115. switch(GetLastError())
  1116. {
  1117. // The volume might be offline
  1118. case ERROR_FILE_NOT_FOUND:
  1119. case ERROR_DEVICE_NOT_CONNECTED:
  1120. case ERROR_NOT_READY:
  1121. return;
  1122. // Otherwise
  1123. default:
  1124. ft.TranslateWin32Error(VSSDBG_GEN,
  1125. L"GetVolumePathNameW('%s', STRING_CCH_PARAM(wszQuorumVolumePath))", bstrQuorumPath);
  1126. }
  1127. }
  1128. // Get the volume name
  1129. WCHAR wszVolumeName[x_nLengthOfVolMgmtVolumeName + 1];
  1130. if (!GetVolumeNameForVolumeMountPointW( wszQuorumVolume, STRING_CCH_PARAM(wszVolumeName) ))
  1131. {
  1132. switch(GetLastError())
  1133. {
  1134. // The volume might be offline
  1135. case ERROR_FILE_NOT_FOUND:
  1136. case ERROR_DEVICE_NOT_CONNECTED:
  1137. case ERROR_NOT_READY:
  1138. return;
  1139. // Otherwise
  1140. default:
  1141. ft.TranslateWin32Error(VSSDBG_GEN,
  1142. L"GetVolumeNameForVolumeMountPointW( '%s', STRING_CCH_PARAM(wszVolumeName))", wszQuorumVolume);
  1143. }
  1144. }
  1145. ft.Trace( VSSDBG_GEN, L"Quorum volume name: %s", wszVolumeName);
  1146. // Cache the quorum volume
  1147. m_awszQuorumVolumeName.CopyFrom(wszVolumeName);
  1148. }