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
1446 lines
51 KiB
/*++
|
|
|
|
Copyright (c) 2002 Microsoft Corporation
|
|
|
|
Abstract:
|
|
|
|
@doc
|
|
@module cluster.cxx | Implementation of CVssClusterAPI
|
|
@end
|
|
|
|
Author:
|
|
|
|
Adi Oltean [aoltean] 04/25/2002
|
|
|
|
Revision History:
|
|
|
|
Name Date Comments
|
|
aoltean 03/14/2001 Created
|
|
|
|
--*/
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Includes
|
|
#include "stdafx.hxx"
|
|
|
|
#include <queue>
|
|
|
|
#pragma warning(disable: 4554)
|
|
#include <msclus.h>
|
|
#include <mstask.h>
|
|
|
|
#include "vs_inc.hxx"
|
|
#include "vs_reg.hxx"
|
|
#include "vs_clus.hxx"
|
|
#include "vs_quorum.hxx"
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Standard foo for file name aliasing. This code block must be after
|
|
// all includes of VSS header files.
|
|
//
|
|
#ifdef VSS_FILE_ALIAS
|
|
#undef VSS_FILE_ALIAS
|
|
#endif
|
|
#define VSS_FILE_ALIAS "CLUCLUSC"
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
#define CHECK_COM( Call ) \
|
|
{ \
|
|
ft.hr = Call; \
|
|
if (ft.HrFailed()) \
|
|
ft.TranslateComError(VSSDBG_GEN, VSS_WSTRINGIZE(Call)); \
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Constants
|
|
|
|
|
|
const WCHAR x_ClusReg_TypeName_PhysicalDisk[] = L"Physical Disk";
|
|
const WCHAR x_ClusReg_TypeName_TaskScheduler[] = L"Volume Shadow Copy Service Task";
|
|
|
|
const WCHAR x_ClusReg_Name_PhysicalDisk_MPVolGuids[] = L"MPVolGuids";
|
|
const WCHAR x_ClusReg_Name_TaskScheduler_ApplicationName[] = L"ApplicationName";
|
|
const WCHAR x_ClusReg_Name_TaskScheduler_ApplicationParams[] = L"ApplicationParams";
|
|
const WCHAR x_ClusReg_Name_TaskScheduler_TriggerArray[] = L"TriggerArray";
|
|
|
|
//
|
|
// BUG# 698766 - Deployment blocker:
|
|
// Failure on managing diff areas on large volumes since Cluster Timeout is too low
|
|
//
|
|
// 600 seconds (10 min) timeout in bringing the resource online/ofline
|
|
//
|
|
const x_nDefaultBringOnlineTimeout = 600;
|
|
const x_nDefaultBringOfflineTimeout = 600;
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CVssClusterResourceList definition and implementation
|
|
|
|
|
|
class CVssClusterResourceList
|
|
{
|
|
public:
|
|
|
|
// Pushes a resource in the list
|
|
// Increases the ref count by 1
|
|
void Push(ISClusResource* pResource) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterResourceList::Push" );
|
|
|
|
try
|
|
{
|
|
// AddRef
|
|
CComPtr<ISClusResource> ptrResource = pResource;
|
|
|
|
// AddRef
|
|
resourceList.push(ptrResource);
|
|
|
|
// Release (CComPtr destructor)
|
|
}
|
|
catch(const std::bad_alloc&)
|
|
{
|
|
ft.ThrowOutOfMemory(VSSDBG_GEN);
|
|
}
|
|
catch(const std::exception& ex)
|
|
{
|
|
ft.Throw( VSSDBG_GEN, E_UNEXPECTED, L"STL exception catched: %hs", ex.what());
|
|
}
|
|
}
|
|
|
|
// Pops a resource from the list
|
|
// Does not decrease the ref count by 1 (that is caller responsibility - actually the smart pointer will do it)
|
|
void Pop(CComPtr<ISClusResource> & pResource)
|
|
{
|
|
pResource = NULL;
|
|
|
|
// If the list is empty, stop here
|
|
if (resourceList.empty())
|
|
return;
|
|
|
|
// AddRef
|
|
pResource = resourceList.front();
|
|
|
|
// Release
|
|
resourceList.pop();
|
|
}
|
|
|
|
private:
|
|
std::queue< CComPtr<ISClusResource> > resourceList;
|
|
};
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CVssClusterAPI implementation
|
|
|
|
|
|
|
|
// Default constructor
|
|
CVssClusterAPI::CVssClusterAPI():
|
|
m_dwOfflineTimeout(x_nDefaultBringOfflineTimeout),
|
|
m_dwOnlineTimeout(x_nDefaultBringOnlineTimeout)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
// Returns FALSE if the cluster API is not present
|
|
bool CVssClusterAPI::Initialize(
|
|
IN LPCWSTR pwszCluster /* = NULL */
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::Initialize" );
|
|
|
|
// Get the offline and online values from registry (if any)
|
|
try
|
|
{
|
|
CVssRegistryKey keyVssSettings(KEY_READ);
|
|
if (keyVssSettings.Open(HKEY_LOCAL_MACHINE, x_wszVssCASettingsPath))
|
|
{
|
|
// Reading the online timeout
|
|
DWORD dwRegistryOnlineTimeout = 0;
|
|
if (keyVssSettings.GetValue(x_wszVssOnlineTimeoutValueName, dwRegistryOnlineTimeout, false))
|
|
m_dwOnlineTimeout = dwRegistryOnlineTimeout;
|
|
|
|
// Reading the offline timeout
|
|
DWORD dwRegistryOfflineTimeout = 0;
|
|
if (keyVssSettings.GetValue(x_wszVssOfflineTimeoutValueName, dwRegistryOfflineTimeout, false))
|
|
m_dwOfflineTimeout = dwRegistryOfflineTimeout;
|
|
}
|
|
}
|
|
VSS_STANDARD_CATCH(ft) // Ignore any exceptions...
|
|
|
|
// Get the cluster object
|
|
ft.hr = m_pCluster.CoCreateInstance( _uuidof(Cluster));
|
|
if (ft.HrFailed())
|
|
{
|
|
ft.Trace(VSSDBG_GEN, L"m_pCluster.CoCreateInstance( _uuidof(Cluster)) [0x%08lx]", ft.hr);
|
|
return ft.Exit(false);
|
|
}
|
|
|
|
// Open the local cluster
|
|
ft.hr = m_pCluster->Open(CVssComBSTR(pwszCluster? pwszCluster: L""));
|
|
if (ft.HrFailed())
|
|
{
|
|
ft.Trace(VSSDBG_GEN, L"m_pCluster->Open(%s) [0x%08lx]", pwszCluster, ft.hr);
|
|
return ft.Exit(false);
|
|
}
|
|
|
|
return ft.Exit(true);
|
|
}
|
|
|
|
|
|
// Make pwszFromVolumeName to be dependent on pwszToVolumeName
|
|
// - Returns TRUE if the dependency was added
|
|
// - Returns FALSE if a dependency does not need to be added
|
|
// - Throws E_INVALIDARG on bad parameters
|
|
// - Throws VSS_E_VOLUME_NOT_SUPPORTED when attempting to add a bogus dependency
|
|
bool CVssClusterAPI::AddDependency(
|
|
IN LPCWSTR pwszFromVolumeName,
|
|
IN LPCWSTR pwszToVolumeName
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::AddDependency" );
|
|
|
|
ft.Trace( VSSDBG_GEN, L"Parameters: From = %s, To = %s", pwszFromVolumeName, pwszToVolumeName);
|
|
if ((pwszFromVolumeName == NULL) || (pwszFromVolumeName == NULL)) {
|
|
BS_ASSERT(false);
|
|
ft.Throw(VSSDBG_GEN, E_INVALIDARG, L"NULL parameters");
|
|
}
|
|
|
|
CComPtr<ISClusResource> pFromResource = GetPhysicalDiskResourceForVolumeName(pwszFromVolumeName);
|
|
CComPtr<ISClusResource> pToResource = GetPhysicalDiskResourceForVolumeName(pwszToVolumeName);
|
|
|
|
// Ignore the case if no volumes is shared
|
|
if ((pFromResource == NULL) && (pToResource == NULL)) {
|
|
ft.Trace(VSSDBG_GEN, L"Original and diff area volumes are no shared. No dependency cannot be added");
|
|
return ft.Exit(false);
|
|
}
|
|
|
|
// Either both volumes must belong to resources or none of them
|
|
if ((pFromResource == NULL) && (pToResource != NULL))
|
|
ft.Throw(VSSDBG_GEN, VSS_E_VOLUME_NOT_SUPPORTED,
|
|
L"Cannot create a dependency from a non-shared volume to a shared diff area");
|
|
if ((pFromResource != NULL) && (pToResource == NULL))
|
|
ft.Throw(VSSDBG_GEN, VSS_E_VOLUME_NOT_SUPPORTED,
|
|
L"Cannot create a dependency from a shared volume to a non-shared diff area");
|
|
|
|
BS_ASSERT((pFromResource != NULL) && (pToResource != NULL));
|
|
|
|
// Ignore if both belong to the same physical disk
|
|
if (AreResourcesEqual(pFromResource, pToResource)) {
|
|
if (wcscmp(pwszFromVolumeName, pwszToVolumeName) == 0)
|
|
{
|
|
ft.Trace(VSSDBG_GEN, L"Volumes are identical. No dependency is added");
|
|
return ft.Exit(false);
|
|
}
|
|
else
|
|
ft.Throw(VSSDBG_GEN, VSS_E_VOLUME_NOT_SUPPORTED,
|
|
L"Cannot create a dependency between different volumes on the same disk resource");
|
|
}
|
|
|
|
// Now check if the dependency already exists
|
|
if (IsDependencyAlreadyEstablished(pFromResource, pToResource))
|
|
{
|
|
ft.Trace(VSSDBG_GEN, L"Dependency already exists.");
|
|
return ft.Exit(false);
|
|
}
|
|
|
|
// Now check if we can establish a dependency (for ex. the resources are part of the same group)
|
|
if (!CanEstablishDependency(pFromResource, pToResource))
|
|
ft.Throw(VSSDBG_GEN, VSS_E_VOLUME_NOT_SUPPORTED, L"Cannot create a dependency between volumes");
|
|
|
|
// Get the dependencies collection for the "FROM" resource
|
|
CComPtr<ISClusResDependencies> pDependencies;
|
|
CHECK_COM( pFromResource->get_Dependencies(&pDependencies) );
|
|
|
|
// Get the list of resources that would need to be brought online after this resource is brought online
|
|
CVssClusterResourceList finalList;
|
|
GetFinalOnlineResourceList(pFromResource, finalList);
|
|
|
|
// Take the resource offline
|
|
TakeResourceOffline(pFromResource);
|
|
|
|
// Add the dependency
|
|
ft.hr = pDependencies->AddItem(pToResource);
|
|
|
|
// Bring all the affected resources back online
|
|
BringResourceListOnline(finalList);
|
|
|
|
// Rethrow if needed
|
|
if (ft.HrFailed())
|
|
ft.ReThrow();
|
|
|
|
return ft.Exit(true);
|
|
}
|
|
|
|
|
|
// Remove the dependency
|
|
// - Returns TRUE if the dependency was removed
|
|
// - Returns FALSE if the dependency could not be found
|
|
// - Throws E_INVALIDARG on bad parameters
|
|
bool CVssClusterAPI::RemoveDependency(
|
|
IN LPCWSTR pwszFromVolumeName,
|
|
IN LPCWSTR pwszToVolumeName
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::RemoveDependency" );
|
|
|
|
ft.Trace( VSSDBG_GEN, L"Parameters: From = %s, To = %s", pwszFromVolumeName, pwszToVolumeName);
|
|
if ((pwszFromVolumeName == NULL) || (pwszFromVolumeName == NULL)) {
|
|
BS_ASSERT(false);
|
|
ft.Throw(VSSDBG_GEN, E_INVALIDARG, L"NULL parameters");
|
|
}
|
|
|
|
// Get the corresponding resources
|
|
CComPtr<ISClusResource> pFromResource = GetPhysicalDiskResourceForVolumeName(pwszFromVolumeName);
|
|
CComPtr<ISClusResource> pToResource = GetPhysicalDiskResourceForVolumeName(pwszToVolumeName);
|
|
|
|
// If both volumes are not belonging to any resource, then ignore them
|
|
if ((pFromResource == NULL) || (pToResource == NULL)) {
|
|
ft.Trace(VSSDBG_GEN, L"One of the volumes does not belong to a physical disk resource.");
|
|
return ft.Exit(false);
|
|
}
|
|
|
|
if (AreResourcesEqual(pFromResource, pToResource)) {
|
|
ft.Trace(VSSDBG_GEN, L"Volumes belong to the same resource. No dependency is removed");
|
|
return ft.Exit(false);
|
|
}
|
|
|
|
// Get the dependents collection for the "FROM" resource
|
|
CComPtr<ISClusResDependencies> pDependencies;
|
|
CHECK_COM( pFromResource->get_Dependencies(&pDependencies) );
|
|
|
|
// Get the dependencies count
|
|
LONG lDependenciesCount = 0;
|
|
CHECK_COM( pDependencies->get_Count(&lDependenciesCount) );
|
|
|
|
// Iterate through all dependencies
|
|
for (INT nDependencyIndex = 1; nDependencyIndex <= lDependenciesCount; nDependencyIndex++)
|
|
{
|
|
// Get the dependency with that index
|
|
CComPtr<ISClusResource> pDependencyResource;
|
|
CComVariant varDependencyIndex = nDependencyIndex;
|
|
CHECK_COM( pDependencies->get_Item(varDependencyIndex, &pDependencyResource) );
|
|
|
|
// Check to see if this is our resource. If not, continue
|
|
if (!AreResourcesEqual(pToResource, pDependencyResource))
|
|
continue;
|
|
|
|
// Get the list of resources that would need to be brought online after this resource is brought online
|
|
CVssClusterResourceList finalList;
|
|
GetFinalOnlineResourceList(pFromResource, finalList);
|
|
|
|
// Take the resource offline
|
|
TakeResourceOffline(pFromResource);
|
|
|
|
// Remove the association
|
|
ft.hr = pDependencies->RemoveItem(varDependencyIndex);
|
|
|
|
// Bring all the affected resources back online
|
|
BringResourceListOnline(finalList);
|
|
|
|
// Rethrow if needed
|
|
if (ft.HrFailed())
|
|
ft.ReThrow();
|
|
|
|
return ft.Exit(true);
|
|
}
|
|
|
|
return ft.Exit(false);
|
|
}
|
|
|
|
|
|
// Adds the registry key to the cluster resource that corresponds to our disk.
|
|
// - Returns TRUE if the reg key was added
|
|
// - Returns FALSE if there is no need to add a reg key
|
|
// - Throws E_INVALIDARG on bad parameters
|
|
// NOTE: this shouldn't fail if the checkpoint is already added!
|
|
bool CVssClusterAPI::AddRegistryKey(
|
|
IN LPCWSTR pwszVolumeName,
|
|
IN LPCWSTR pwszPathFormat,
|
|
IN ...
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::AddRegistryKey" );
|
|
|
|
ft.Trace( VSSDBG_GEN, L"Parameters: From = %s, Reg = %s", pwszVolumeName, pwszPathFormat);
|
|
if ((pwszVolumeName == NULL) || (pwszPathFormat == NULL)) {
|
|
BS_ASSERT(false);
|
|
ft.Throw(VSSDBG_GEN, E_INVALIDARG, L"NULL parameters");
|
|
}
|
|
|
|
// Build the path to the key
|
|
WCHAR wszKeyPath[x_nVssMaxRegBuffer];
|
|
va_list marker;
|
|
va_start( marker, pwszPathFormat );
|
|
ft.hr = StringCchVPrintfW( STRING_CCH_PARAM(wszKeyPath), pwszPathFormat, marker );
|
|
va_end( marker );
|
|
if (ft.HrFailed())
|
|
ft.TranslateGenericError(VSSDBG_GEN, ft.hr, L"StringCchVPrintfW()");
|
|
ft.Trace( VSSDBG_GEN, L"Formatted path: '%s'", wszKeyPath);
|
|
|
|
// Build the BSTR
|
|
CVssComBSTR bstrPath = wszKeyPath;
|
|
|
|
// Get the corresponding resource
|
|
CComPtr<ISClusResource> pResource = GetPhysicalDiskResourceForVolumeName(pwszVolumeName);
|
|
if (pResource == NULL) {
|
|
ft.Trace(VSSDBG_GEN, L"The volume is not shared. No registry key can be added");
|
|
return ft.Exit(false);
|
|
}
|
|
|
|
// Get the Registry checkpoints collection
|
|
CComPtr<ISClusRegistryKeys> pRegKeys;
|
|
CHECK_COM( pResource->get_RegistryKeys(&pRegKeys) );
|
|
|
|
// Enumerate the existing checkpoints (and make sure we don't add the same checkpoint twice)
|
|
//
|
|
|
|
// Get the checkpoints count
|
|
LONG lRegKeysCount = 0;
|
|
CHECK_COM( pRegKeys->get_Count(&lRegKeysCount) );
|
|
|
|
// Iterate through all checkpoints on that resource
|
|
for (INT nRegKeyIndex = 1; nRegKeyIndex <= lRegKeysCount; nRegKeyIndex++)
|
|
{
|
|
// Get the registry key
|
|
CComVariant varRegKeyIndex = nRegKeyIndex;
|
|
CVssComBSTR bstrRegKey;
|
|
CHECK_COM( pRegKeys->get_Item(varRegKeyIndex, &bstrRegKey) );
|
|
|
|
// Check to see if this is our registry key
|
|
if (!bstrRegKey || _wcsicmp(bstrRegKey, wszKeyPath))
|
|
continue;
|
|
|
|
// We found an existing checkpoint with the same registry path
|
|
return ft.Exit(false);
|
|
}
|
|
|
|
// Add the registry key
|
|
CHECK_COM( pRegKeys->AddItem( bstrPath ) );
|
|
|
|
return ft.Exit(true);
|
|
}
|
|
|
|
|
|
// Removes the registry key from the cluster resource that corresponds to our disk.
|
|
bool CVssClusterAPI::RemoveRegistryKey(
|
|
IN LPCWSTR pwszVolumeName,
|
|
IN LPCWSTR pwszPathFormat,
|
|
IN ...
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::RemoveRegistryKey" );
|
|
|
|
ft.Trace( VSSDBG_GEN, L"Parameters: From = %s, Reg = %s", pwszVolumeName, pwszPathFormat);
|
|
if ((pwszVolumeName == NULL) || (pwszPathFormat == NULL)) {
|
|
BS_ASSERT(false);
|
|
ft.Throw(VSSDBG_GEN, E_INVALIDARG, L"NULL parameters");
|
|
}
|
|
|
|
// Build the path to the key
|
|
WCHAR wszKeyPath[x_nVssMaxRegBuffer];
|
|
va_list marker;
|
|
va_start( marker, pwszPathFormat );
|
|
ft.hr = StringCchVPrintfW( STRING_CCH_PARAM(wszKeyPath), pwszPathFormat, marker );
|
|
va_end( marker );
|
|
if (ft.HrFailed())
|
|
ft.TranslateGenericError(VSSDBG_GEN, ft.hr, L"StringCchVPrintfW()");
|
|
ft.Trace( VSSDBG_GEN, L"Formatted path: '%s'", wszKeyPath);
|
|
|
|
// Get the corresponding resource
|
|
CComPtr<ISClusResource> pResource = GetPhysicalDiskResourceForVolumeName(pwszVolumeName);
|
|
if (pResource == NULL) {
|
|
ft.Trace(VSSDBG_GEN, L"The volume is not shared. No registry key can be removed");
|
|
return ft.Exit(false);
|
|
}
|
|
|
|
// Get the Registry checkpoints collection
|
|
CComPtr<ISClusRegistryKeys> pRegKeys;
|
|
CHECK_COM( pResource->get_RegistryKeys(&pRegKeys) );
|
|
|
|
// Get the checkpoints count
|
|
LONG lRegKeysCount = 0;
|
|
CHECK_COM( pRegKeys->get_Count(&lRegKeysCount) );
|
|
|
|
// Iterate through all checkpoints on that resource
|
|
for (INT nRegKeyIndex = 1; nRegKeyIndex <= lRegKeysCount; nRegKeyIndex++)
|
|
{
|
|
// Get the registry key
|
|
CComVariant varRegKeyIndex = nRegKeyIndex;
|
|
CVssComBSTR bstrRegKey;
|
|
CHECK_COM( pRegKeys->get_Item(varRegKeyIndex, &bstrRegKey) );
|
|
|
|
// Check to see if this is our registry key
|
|
if (!bstrRegKey || _wcsicmp(bstrRegKey, wszKeyPath))
|
|
continue;
|
|
|
|
// Remove the association
|
|
CHECK_COM( pRegKeys->RemoveItem(varRegKeyIndex) );
|
|
|
|
return ft.Exit(true);
|
|
}
|
|
|
|
return ft.Exit(false);
|
|
}
|
|
|
|
|
|
// Create a task scheduler resource
|
|
// Create the dependency from it to the Physical Disk Resource identified by the volume name
|
|
// Then bring the resource online
|
|
// - Returns TRUE if the TS resource was created
|
|
// - Returns FALSE if no task needs to be created
|
|
bool CVssClusterAPI::CreateTaskSchedulerResource(
|
|
IN LPCWSTR pwszTaskSchedulerResourceName, // This will be the task name also
|
|
IN LPCWSTR pwszApplicationName,
|
|
IN LPCWSTR pwszApplicationParams,
|
|
IN INT nTaskTriggersCount,
|
|
IN PTASK_TRIGGER ptsTaskTriggersArray,
|
|
IN LPCWSTR pwszMakeDependentOnVolumeName
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::CreateTaskSchedulerResource" );
|
|
|
|
// Verify parameters
|
|
ft.Trace( VSSDBG_GEN, L"Parameters: Task = %s, AppName = '%s', Params = '%s', Volume = '%s'",
|
|
pwszTaskSchedulerResourceName, pwszApplicationName, pwszApplicationParams, pwszMakeDependentOnVolumeName);
|
|
if ((pwszTaskSchedulerResourceName == NULL) || (pwszTaskSchedulerResourceName[0] == L'\0') ||
|
|
(pwszApplicationName == NULL) || (pwszApplicationName[0] == L'\0') ||
|
|
(pwszApplicationParams == NULL) || (pwszApplicationParams[0] == L'\0') ||
|
|
(nTaskTriggersCount <= 0) || (ptsTaskTriggersArray == NULL) ||
|
|
(pwszMakeDependentOnVolumeName == NULL) || (pwszMakeDependentOnVolumeName[0] == L'\0'))
|
|
{
|
|
BS_ASSERT(false);
|
|
ft.Throw(VSSDBG_GEN, E_INVALIDARG, L"NULL parameters");
|
|
}
|
|
|
|
// Get the resource for the volume
|
|
CComPtr<ISClusResource> pPhysicalDiskResource = GetPhysicalDiskResourceForVolumeName(pwszMakeDependentOnVolumeName);
|
|
if (pPhysicalDiskResource == NULL) {
|
|
ft.Trace(VSSDBG_GEN, L"The volume is not shared. No task scheduler can be added");
|
|
return ft.Exit(false);
|
|
}
|
|
|
|
// Get the resource group
|
|
CComPtr<ISClusResGroup> pGroup;
|
|
CHECK_COM( pPhysicalDiskResource->get_Group(&pGroup) );
|
|
|
|
// Get the resources list
|
|
CComPtr<ISClusResGroupResources> pResources;
|
|
CHECK_COM( pGroup->get_Resources(&pResources) );
|
|
|
|
// Build the resource name
|
|
CVssComBSTR bstrResourceName = pwszTaskSchedulerResourceName;
|
|
|
|
// Build the resource type
|
|
CVssComBSTR bstrResourceType = x_ClusReg_TypeName_TaskScheduler;
|
|
|
|
// Create our Task Scheduler resource
|
|
CComPtr<ISClusResource> pResource;
|
|
CHECK_COM( pResources->CreateItem(
|
|
bstrResourceName,
|
|
bstrResourceType,
|
|
CLUSTER_RESOURCE_DEFAULT_MONITOR,
|
|
&pResource) );
|
|
|
|
// Get the private properties collection
|
|
CComPtr<ISClusProperties> pProperties;
|
|
CHECK_COM( pResource->get_PrivateProperties(&pProperties) );
|
|
|
|
// Add the Application Name private property
|
|
CComPtr<ISClusProperty> pProperty;
|
|
CHECK_COM( pProperties->CreateItem(
|
|
CVssComBSTR(x_ClusReg_Name_TaskScheduler_ApplicationName),
|
|
CComVariant((BSTR)CVssComBSTR(pwszApplicationName)),
|
|
&pProperty) );
|
|
|
|
// Add the Application Params private property
|
|
pProperty = NULL;
|
|
CHECK_COM( pProperties->CreateItem(
|
|
CVssComBSTR(x_ClusReg_Name_TaskScheduler_ApplicationParams),
|
|
CComVariant((BSTR)CVssComBSTR(pwszApplicationParams)),
|
|
&pProperty) );
|
|
|
|
// Add the task trigger as a binary value
|
|
CComVariant varTaskTrigger;
|
|
CopyBinaryIntoVariant( (PBYTE)ptsTaskTriggersArray, sizeof(TASK_TRIGGER) * nTaskTriggersCount, varTaskTrigger);
|
|
BS_ASSERT(varTaskTrigger.vt == (VT_ARRAY | VT_UI1));
|
|
BS_ASSERT(varTaskTrigger.parray->cDims == 1);
|
|
BS_ASSERT(varTaskTrigger.parray->rgsabound[0].cElements == sizeof(TASK_TRIGGER) * nTaskTriggersCount);
|
|
|
|
ft.Trace(VSSDBG_GEN, L"Writing a binary property with %d bytes", varTaskTrigger.parray->rgsabound[0].cElements);
|
|
|
|
// Add the Trigger Array private property
|
|
pProperty = NULL;
|
|
CHECK_COM( pProperties->CreateItem( CVssComBSTR(x_ClusReg_Name_TaskScheduler_TriggerArray),
|
|
varTaskTrigger, &pProperty) );
|
|
|
|
// Save changes
|
|
CComVariant varStatusCode;
|
|
CHECK_COM( pProperties->SaveChanges(&varStatusCode) );
|
|
BS_ASSERT(varStatusCode.vt == VT_ERROR);
|
|
ft.hr = HRESULT_FROM_WIN32( varStatusCode.scode );
|
|
if (ft.HrFailed())
|
|
ft.TranslateComError( VSSDBG_GEN, L"pProperties->SaveChanges(&varStatusCode)");
|
|
|
|
// Add the dependency between this resource and the volume resource
|
|
CComPtr<ISClusResDependencies> pDependencies;
|
|
CHECK_COM( pResource->get_Dependencies(&pDependencies) );
|
|
CHECK_COM( pDependencies->AddItem(pPhysicalDiskResource) );
|
|
|
|
// Bring the resource online
|
|
// This will insert the task into the TS DB
|
|
BringResourceOnline(pResource);
|
|
|
|
return ft.Exit(true);
|
|
}
|
|
|
|
|
|
// Update task scheduler information
|
|
bool CVssClusterAPI::UpdateTaskSchedulerResource(
|
|
IN LPCWSTR pwszTaskSchedulerResourceName,
|
|
IN INT nTaskTriggersCount,
|
|
IN PTASK_TRIGGER ptsTaskTriggersArray
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::UpdateTaskSchedulerResource" );
|
|
|
|
// Verify parameters
|
|
ft.Trace( VSSDBG_GEN, L"Parameters: Resource = %s, pTaskTrigger = %p",
|
|
pwszTaskSchedulerResourceName, ptsTaskTriggersArray);
|
|
if ((pwszTaskSchedulerResourceName == NULL) || (pwszTaskSchedulerResourceName[0] == L'\0') ||
|
|
(nTaskTriggersCount <= 0) || (ptsTaskTriggersArray == NULL))
|
|
{
|
|
BS_ASSERT(false);
|
|
ft.Throw(VSSDBG_GEN, E_INVALIDARG, L"NULL parameters");
|
|
}
|
|
|
|
// Get the resources collection
|
|
CComPtr<ISClusResources> pResources;
|
|
BS_ASSERT(m_pCluster);
|
|
CHECK_COM( m_pCluster->get_Resources(&pResources) );
|
|
|
|
// Get the resources count
|
|
LONG lResourcesCount = 0;
|
|
CHECK_COM( pResources->get_Count(&lResourcesCount) );
|
|
|
|
// Iterate through all resources
|
|
bool bFound = false;
|
|
CComPtr<ISClusResource> pResource;
|
|
for (INT nResourceIndex = 1; nResourceIndex <= lResourcesCount; nResourceIndex++)
|
|
{
|
|
// Get the resource interface
|
|
CComVariant varResourceIndex = nResourceIndex;
|
|
pResource = NULL;
|
|
CHECK_COM( pResources->get_Item(varResourceIndex, &pResource) );
|
|
|
|
// Ignore resources with type != "Task Scheduler"
|
|
CVssComBSTR bstrTypeName;
|
|
CHECK_COM( pResource->get_TypeName(&bstrTypeName) );
|
|
if (!bstrTypeName || wcscmp(x_ClusReg_TypeName_TaskScheduler, bstrTypeName)) {
|
|
// ft.Trace(VSSDBG_GEN, L"Different type names: %s - %s", x_ClusReg_TypeName_TaskScheduler, bstrTypeName);
|
|
continue;
|
|
}
|
|
|
|
// Ignore resources with a different name
|
|
CVssComBSTR bstrResourceName;
|
|
CHECK_COM( pResource->get_Name(&bstrResourceName) );
|
|
if (!bstrResourceName || wcscmp(pwszTaskSchedulerResourceName, bstrResourceName)) {
|
|
// ft.Trace(VSSDBG_GEN, L"Different names: %s - %s", pwszTaskSchedulerResourceName, bstrResourceName);
|
|
continue;
|
|
}
|
|
|
|
bFound = true;
|
|
break;
|
|
}
|
|
|
|
if (!bFound) {
|
|
ft.Trace(VSSDBG_GEN, L"Resource not found");
|
|
return ft.Exit(false);
|
|
}
|
|
|
|
// We found the resource (assuming the names uniquely identify a resource)
|
|
|
|
// Get the private properties collection
|
|
CComPtr<ISClusProperties> pProperties;
|
|
CHECK_COM( pResource->get_PrivateProperties(&pProperties) );
|
|
|
|
// Get the properties count
|
|
LONG lPropertiesCount = 0;
|
|
CHECK_COM( pProperties->get_Count(&lPropertiesCount) );
|
|
|
|
// Iterate through all properties
|
|
bFound = false;
|
|
CComPtr<ISClusProperty> pProperty;
|
|
for (INT nPropertyIndex = 1; nPropertyIndex <= lPropertiesCount; nPropertyIndex++)
|
|
{
|
|
// Get the resource type interface
|
|
CComVariant varPropertyIndex = nPropertyIndex;
|
|
pProperty = NULL;
|
|
CHECK_COM( pProperties->get_Item(varPropertyIndex, &pProperty) );
|
|
|
|
// Get the property name
|
|
CVssComBSTR bstrPropertyName;
|
|
CHECK_COM( pProperty->get_Name(&bstrPropertyName) );
|
|
|
|
// Check to see if this is our "Trigger Array" property.
|
|
// If not, continue
|
|
if (!bstrPropertyName || wcscmp(bstrPropertyName, x_ClusReg_Name_TaskScheduler_TriggerArray))
|
|
continue;
|
|
|
|
// We found the property
|
|
bFound = true;
|
|
break;
|
|
}
|
|
|
|
// If we didn't found that property, then return
|
|
if (!bFound) {
|
|
// The TriggerArray property is mandatory in our case
|
|
BS_ASSERT(false);
|
|
ft.LogGenericWarning(VSSDBG_COORD, L"pProperties->get_Item(varPropertyIndex, &pProperty) [%s, %s]",
|
|
x_ClusReg_Name_TaskScheduler_TriggerArray, pwszTaskSchedulerResourceName);
|
|
return ft.Exit(false);
|
|
}
|
|
|
|
// Add the task trigger as a binary value
|
|
CComVariant varTaskTrigger;
|
|
CopyBinaryIntoVariant( (PBYTE)ptsTaskTriggersArray, sizeof(TASK_TRIGGER) * nTaskTriggersCount, varTaskTrigger);
|
|
|
|
// Update the Task Trigger private property
|
|
CHECK_COM( pProperty->put_Value(varTaskTrigger) );
|
|
|
|
// Save changes
|
|
CComVariant varStatusCode;
|
|
CHECK_COM( pProperties->SaveChanges(&varStatusCode) );
|
|
|
|
// This error code means: The properties were stored but not all changes will
|
|
// take effect until the next time the resource is brought online.
|
|
BS_ASSERT(varStatusCode.vt == VT_ERROR);
|
|
ft.hr = HRESULT_FROM_WIN32( varStatusCode.scode );
|
|
if (ft.hr != HRESULT_FROM_WIN32( ERROR_RESOURCE_PROPERTIES_STORED ) )
|
|
ft.TranslateComError( VSSDBG_GEN, L"pProperties->SaveChanges() => [0x%08lx]", ft.hr);
|
|
|
|
// Take the resource offline
|
|
// This will delete the task into the TS DB
|
|
TakeResourceOffline(pResource);
|
|
|
|
// Take the resource offline
|
|
// This will insert the task into the TS DB
|
|
BringResourceOnline(pResource);
|
|
|
|
// Report success
|
|
return ft.Exit(true);
|
|
}
|
|
|
|
|
|
// Delete a task scheduler resource.
|
|
// Before that, take the resource offline and remove the dependency
|
|
bool CVssClusterAPI::DeleteTaskSchedulerResource(
|
|
IN LPCWSTR pwszTaskSchedulerResourceName
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::DeleteTaskSchedulerResource" );
|
|
|
|
// Verify parameters
|
|
ft.Trace( VSSDBG_GEN, L"Parameters: Resource = %s",
|
|
pwszTaskSchedulerResourceName);
|
|
if ((pwszTaskSchedulerResourceName == NULL) || (pwszTaskSchedulerResourceName[0] == L'\0'))
|
|
{
|
|
BS_ASSERT(false);
|
|
ft.Throw(VSSDBG_GEN, E_INVALIDARG, L"NULL parameters");
|
|
}
|
|
|
|
// Get the resources collection
|
|
CComPtr<ISClusResources> pResources;
|
|
BS_ASSERT(m_pCluster);
|
|
CHECK_COM( m_pCluster->get_Resources(&pResources) );
|
|
|
|
// Get the resources count
|
|
LONG lResourcesCount = 0;
|
|
CHECK_COM( pResources->get_Count(&lResourcesCount) );
|
|
|
|
// Iterate through all resources
|
|
CComPtr<ISClusResource> pResource;
|
|
for (INT nResourceIndex = 1; nResourceIndex <= lResourcesCount; nResourceIndex++)
|
|
{
|
|
// Get the resource interface
|
|
CComVariant varResourceIndex = nResourceIndex;
|
|
pResource = NULL;
|
|
CHECK_COM( pResources->get_Item(varResourceIndex, &pResource) );
|
|
|
|
// Ignore resources with type != "Task Scheduler"
|
|
CVssComBSTR bstrTypeName;
|
|
CHECK_COM( pResource->get_TypeName(&bstrTypeName) );
|
|
if (!bstrTypeName || wcscmp(x_ClusReg_TypeName_TaskScheduler, bstrTypeName))
|
|
continue;
|
|
|
|
// Ignore resources with a different name
|
|
CVssComBSTR bstrResourceName;
|
|
CHECK_COM( pResource->get_Name(&bstrResourceName) );
|
|
if (!bstrResourceName || wcscmp(pwszTaskSchedulerResourceName, bstrResourceName))
|
|
continue;
|
|
|
|
// We found the resource (assuming the names uniquely identify a resource)
|
|
|
|
// Take the resource offline
|
|
// This will delete the task into the TS DB
|
|
TakeResourceOffline(pResource);
|
|
|
|
// Now delete the resource from the cluster (this will remove any dependencies)
|
|
CHECK_COM( pResource->Delete() );
|
|
|
|
// Report success
|
|
return ft.Exit(true);
|
|
}
|
|
|
|
return ft.Exit(false);
|
|
}
|
|
|
|
|
|
// Returns the Physical Disk resource that contains the given volume
|
|
// If the volume doesn't belong to a physical disk resource, then return NULL
|
|
// The returned volume must be freed with CoTaskMemFree
|
|
ISClusResource* CVssClusterAPI::GetPhysicalDiskResourceForVolumeName(
|
|
IN LPCWSTR pwszVolumeName
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::GetPhysicalDiskResourceForVolumeName" );
|
|
|
|
// Verify parameters
|
|
ft.Trace( VSSDBG_GEN, L"Parameters: Volume = %s", pwszVolumeName);
|
|
if ((pwszVolumeName == NULL) || (pwszVolumeName[0] == L'\0'))
|
|
{
|
|
BS_ASSERT(false);
|
|
ft.Throw(VSSDBG_GEN, E_INVALIDARG, L"NULL parameters");
|
|
}
|
|
|
|
// Get the resources collection
|
|
CComPtr<ISClusResources> pResources;
|
|
BS_ASSERT(m_pCluster);
|
|
CHECK_COM( m_pCluster->get_Resources(&pResources) );
|
|
|
|
// Get the resources count
|
|
LONG lResourcesCount = 0;
|
|
CHECK_COM( pResources->get_Count(&lResourcesCount) );
|
|
|
|
// Iterate through all resources
|
|
for (INT nResourceIndex = 1; nResourceIndex <= lResourcesCount; nResourceIndex++)
|
|
{
|
|
// Get the resource interface
|
|
CComPtr<ISClusResource> pResource;
|
|
CComVariant varResourceIndex = nResourceIndex;
|
|
CHECK_COM( pResources->get_Item(varResourceIndex, &pResource) );
|
|
|
|
// Checks if relates with the partition
|
|
if (IsResourceRefferingVolume(pResource, pwszVolumeName))
|
|
return ft.Exit(pResource.Detach());
|
|
}
|
|
|
|
// No resource found
|
|
return ft.Exit( (ISClusResource*)NULL );
|
|
}
|
|
|
|
|
|
// Returns TRUE if a dependency can be established
|
|
bool CVssClusterAPI::CanEstablishDependency(
|
|
IN ISClusResource* pFromResource, // To be the dependent
|
|
IN ISClusResource* pToResource // To be the dependency
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::CanEstablishDependency" );
|
|
|
|
// Verify parameters
|
|
ft.Trace( VSSDBG_GEN, L"Parameters: pFromResource = %p, pToResource = %p", pFromResource, pToResource);
|
|
if ((pFromResource == NULL) || (pToResource == NULL))
|
|
{
|
|
BS_ASSERT(false);
|
|
ft.Throw(VSSDBG_GEN, E_INVALIDARG, L"NULL parameters");
|
|
}
|
|
|
|
// Get the name of the first resource
|
|
CComVariant varBoolResult;
|
|
CHECK_COM( pFromResource->CanResourceBeDependent(pToResource, &varBoolResult) );
|
|
BS_ASSERT( varBoolResult.vt == VT_BOOL );
|
|
bool bResult = (varBoolResult.boolVal == VARIANT_TRUE);
|
|
|
|
return ft.Exit(bResult);
|
|
}
|
|
|
|
|
|
// Returns TRUE if a dependency is already established
|
|
// - Returns TRUE if the dependency already exists
|
|
// - Returns FALSE if the dependency could not be found
|
|
// - Throws E_INVALIDARG on bad parameters
|
|
bool CVssClusterAPI::IsDependencyAlreadyEstablished(
|
|
IN ISClusResource* pFromResource, // Is dependent
|
|
IN ISClusResource* pToResource // Is dependency
|
|
)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::IsDependencyAlreadyEstablished" );
|
|
|
|
// Verify parameters
|
|
ft.Trace( VSSDBG_GEN, L"Parameters: pFromResource = %p, pToResource = %p", pFromResource, pToResource);
|
|
if ((pFromResource == NULL) || (pToResource == NULL))
|
|
{
|
|
BS_ASSERT(false);
|
|
ft.Throw(VSSDBG_GEN, E_INVALIDARG, L"NULL parameters");
|
|
}
|
|
|
|
// Get the dependents collection for the "FROM" resource
|
|
CComPtr<ISClusResDependencies> pDependencies;
|
|
CHECK_COM( pFromResource->get_Dependencies(&pDependencies) );
|
|
|
|
// Get the dependencies count
|
|
LONG lDependenciesCount = 0;
|
|
CHECK_COM( pDependencies->get_Count(&lDependenciesCount) );
|
|
|
|
// Iterate through all dependencies
|
|
for (INT nDependencyIndex = 1; nDependencyIndex <= lDependenciesCount; nDependencyIndex++)
|
|
{
|
|
// Get the dependency with that index
|
|
CComPtr<ISClusResource> pDependencyResource;
|
|
CComVariant varDependencyIndex = nDependencyIndex;
|
|
CHECK_COM( pDependencies->get_Item(varDependencyIndex, &pDependencyResource) );
|
|
|
|
// Check to see if this is our resource. If not, continue
|
|
if (!AreResourcesEqual(pToResource, pDependencyResource))
|
|
continue;
|
|
|
|
// We find the dependency
|
|
return ft.Exit(true);
|
|
}
|
|
|
|
return ft.Exit(false);
|
|
}
|
|
|
|
|
|
// Returns true if hte volume belongs to a Physical Disk resource
|
|
bool CVssClusterAPI::IsVolumeBelongingToPhysicalDiskResource(
|
|
IN LPCWSTR pwszVolumeName
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::IsVolumeBelongingToPhysicalDiskResource" );
|
|
|
|
if (!m_pCluster)
|
|
return ft.Exit(false);
|
|
|
|
CComPtr<ISClusResource> ptrResource = GetPhysicalDiskResourceForVolumeName(pwszVolumeName);
|
|
return ft.Exit(ptrResource != NULL);
|
|
}
|
|
|
|
|
|
// Returns TRUE if the resource is a Physical Disk resource that contains the given volume
|
|
bool CVssClusterAPI::IsResourceRefferingVolume(
|
|
IN ISClusResource* pResource,
|
|
IN LPCWSTR pwszVolumeName
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::IsResourceRefferingVolume" );
|
|
|
|
BS_ASSERT(pResource);
|
|
BS_ASSERT(pwszVolumeName);
|
|
CVssComBSTR bstrName;
|
|
CHECK_COM( pResource->get_Name(&bstrName) );
|
|
ft.Trace( VSSDBG_GEN, L"Parameters: resource name = %s, pwszVolumeName = %s", (LPWSTR) bstrName, pwszVolumeName);
|
|
|
|
// Ignore resources that are not in the storage class
|
|
CLUSTER_RESOURCE_CLASS rcClassInfo;
|
|
CHECK_COM( pResource->get_ClassInfo(&rcClassInfo) );
|
|
if (rcClassInfo != CLUS_RESCLASS_STORAGE)
|
|
return ft.Exit(false);
|
|
|
|
// Ignore resources with type != "Physical Disk"
|
|
CVssComBSTR bstrTypeName;
|
|
CHECK_COM( pResource->get_TypeName(&bstrTypeName) );
|
|
if (!bstrTypeName || wcscmp(x_ClusReg_TypeName_PhysicalDisk, bstrTypeName))
|
|
return ft.Exit(false);
|
|
|
|
//
|
|
// Get the list of volume MpM guids
|
|
//
|
|
// Look into the MULTI_SZ string for our volume
|
|
// We assume the format of one string is
|
|
// "[byte offset] [volume in kernel-mode format]"
|
|
//
|
|
// This private property is described in the following documentation:
|
|
// \\index1\sdnt\commontest\wtt\wttwebddb\webdocs\management\mount_point_support.doc
|
|
//
|
|
|
|
// Get the private properties collection
|
|
CComPtr<ISClusProperties> pProperties;
|
|
CHECK_COM( pResource->get_PrivateProperties(&pProperties) );
|
|
|
|
// Get the properties count
|
|
LONG lPropertiesCount = 0;
|
|
CHECK_COM( pProperties->get_Count(&lPropertiesCount) );
|
|
|
|
// Iterate through all properties
|
|
bool bFound = false;
|
|
CComPtr<ISClusProperty> pProperty;
|
|
for (INT nPropertyIndex = 1; nPropertyIndex <= lPropertiesCount; nPropertyIndex++)
|
|
{
|
|
// Get the resource type interface
|
|
CComVariant varPropertyIndex = nPropertyIndex;
|
|
pProperty = NULL;
|
|
CHECK_COM( pProperties->get_Item(varPropertyIndex, &pProperty) );
|
|
|
|
// Get the property name
|
|
CVssComBSTR bstrPropertyName;
|
|
CHECK_COM( pProperty->get_Name(&bstrPropertyName) );
|
|
|
|
// Check to see if this is our "Mount Volume Name" property.
|
|
// If not, continue
|
|
if (!bstrPropertyName || wcscmp(bstrPropertyName, x_ClusReg_Name_PhysicalDisk_MPVolGuids))
|
|
continue;
|
|
|
|
// We found the property
|
|
bFound = true;
|
|
break;
|
|
}
|
|
|
|
// If we didn't found that (optional) property, then return
|
|
if (!bFound)
|
|
return ft.Exit(false);
|
|
|
|
// Copy the volume locally in order to convert it
|
|
CVssAutoLocalString strVolumeName;
|
|
strVolumeName.CopyFrom(pwszVolumeName);
|
|
|
|
// Convert the volume name into the Kernel-mode representation
|
|
if (!ConvertVolMgmtVolumeNameIntoKernelObject( strVolumeName )) {
|
|
// Programming error - the original volume is not in the \\?\Volume{GUID}\ format
|
|
BS_ASSERT(false);
|
|
ft.TranslateGenericError(VSSDBG_GEN, E_UNEXPECTED,
|
|
L"ConvertVolMgmtVolumeNameIntoKernelObject(%s)", strVolumeName);
|
|
}
|
|
|
|
// Get the value list
|
|
CComPtr<ISClusPropertyValues> pValues;
|
|
CHECK_COM( pProperty->get_Values(&pValues) );
|
|
|
|
// Get the values count
|
|
LONG lValueCount = 0;
|
|
CHECK_COM( pValues->get_Count(&lValueCount) );
|
|
ft.Trace(VSSDBG_GEN, L"Number of volumes: %ld", lValueCount);
|
|
|
|
// Iterate through all the values
|
|
CComPtr<ISClusPropertyValue> pValue;
|
|
for (INT nValueIndex = 1; nValueIndex <= lValueCount; nValueIndex++)
|
|
{
|
|
// Get the resource type interface
|
|
CComVariant varValueIndex = nValueIndex;
|
|
pValue = NULL;
|
|
CHECK_COM( pValues->get_Item(varValueIndex, &pValue) );
|
|
|
|
// Get the count of Data values. It must be a positive number
|
|
LONG lDataCount;
|
|
CHECK_COM( pValue->get_DataCount(&lDataCount) );
|
|
|
|
// Ignore properties with no data
|
|
if (lDataCount <=0) {
|
|
BS_ASSERT(false);
|
|
continue;
|
|
}
|
|
|
|
// Get the Data collection
|
|
CComPtr<ISClusPropertyValueData> pData;
|
|
CHECK_COM( pValue->get_Data(&pData) );
|
|
|
|
// Get the count of Data values.
|
|
// Just verify that is the same number
|
|
LONG lInternalDataCount;
|
|
CHECK_COM( pData->get_Count(&lInternalDataCount) );
|
|
BS_ASSERT(lInternalDataCount == lDataCount);
|
|
|
|
// Get the property value.
|
|
CComVariant varVolumeMountPoint;
|
|
for (INT nDataIndex = 1; nDataIndex <= lDataCount; nDataIndex++)
|
|
{
|
|
CComVariant varDataIndex = nDataIndex;
|
|
varVolumeMountPoint.Clear();
|
|
CHECK_COM( pData->get_Item(varDataIndex, &varVolumeMountPoint) );
|
|
|
|
// This has to be a BSTR
|
|
if (varVolumeMountPoint.vt != VT_BSTR) {
|
|
BS_ASSERT(false);
|
|
ft.TranslateGenericError(VSSDBG_GEN, E_UNEXPECTED, L"pProperty->get_Value() != VT_BSTR");
|
|
}
|
|
|
|
LPWSTR pwszMountPoint = varVolumeMountPoint.bstrVal;
|
|
ft.Trace(VSSDBG_GEN, L"Processing volume: %s", pwszMountPoint);
|
|
|
|
// Skip the hex number composing the internal offset
|
|
for(;isalnum(pwszMountPoint[0]);pwszMountPoint++);
|
|
|
|
// Skip the spaces after the number
|
|
for(;iswspace(pwszMountPoint[0]);pwszMountPoint++);
|
|
|
|
// Now we get to a volume name (according to the current doc). Compare it to the original volume.
|
|
if (_wcsicmp(pwszMountPoint, strVolumeName) == 0)
|
|
return ft.Exit(true);
|
|
}
|
|
}
|
|
|
|
return ft.Exit(false);
|
|
}
|
|
|
|
|
|
// returns TRUE if the two COM objects are identifying the same resource
|
|
bool CVssClusterAPI::AreResourcesEqual(
|
|
IN ISClusResource* pResource1,
|
|
IN ISClusResource* pResource2
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::AreResourcesEqual" );
|
|
|
|
BS_ASSERT(pResource1);
|
|
BS_ASSERT(pResource2);
|
|
|
|
// Get the name of the first resource
|
|
CVssComBSTR bstrName1;
|
|
CHECK_COM( pResource1->get_Name(&bstrName1) );
|
|
|
|
// Get the name of the second resource
|
|
CVssComBSTR bstrName2;
|
|
CHECK_COM( pResource2->get_Name(&bstrName2) );
|
|
|
|
ft.Trace( VSSDBG_GEN, L"Comparing: resource = %s, resource = %s", (LPWSTR) bstrName1, (LPWSTR) bstrName2);
|
|
|
|
if (bstrName1 != bstrName2)
|
|
return ft.Exit(false);
|
|
|
|
return ft.Exit(true);
|
|
}
|
|
|
|
|
|
// Copy the given binary data into the variant
|
|
void CVssClusterAPI::CopyBinaryIntoVariant(
|
|
IN PBYTE pbData,
|
|
IN DWORD cbSize,
|
|
IN OUT CComVariant & variant
|
|
)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::CopyBinaryIntoVariant" );
|
|
|
|
// Describe the new array
|
|
SAFEARRAYBOUND sab[ 1 ];
|
|
sab[ 0 ].lLbound = 0;
|
|
sab[ 0 ].cElements = cbSize;
|
|
|
|
// allocate a one dimensional SafeArray of BYTES
|
|
CVssAutoSafearrayPtr pSafeArray = ::SafeArrayCreate( VT_UI1, 1, sab );
|
|
if (!pSafeArray.IsValid())
|
|
ft.ThrowOutOfMemory(VSSDBG_GEN);
|
|
|
|
// get a pointer to the SafeArray
|
|
PBYTE pbArrayData = NULL;
|
|
ft.hr = ::SafeArrayAccessData( pSafeArray, (PVOID *) &pbArrayData );
|
|
if ( ft.HrFailed() )
|
|
ft.TranslateGenericError(VSSDBG_GEN, ft.hr, L"::SafeArrayAccessData( pSafeArray, (PVOID *) &pb )");
|
|
|
|
// transfer data into the array
|
|
::CopyMemory( pbArrayData, pbData, cbSize );
|
|
|
|
// release the pointer into the SafeArray
|
|
ft.hr = ::SafeArrayUnaccessData( pSafeArray );
|
|
if ( ft.HrFailed() )
|
|
ft.TranslateGenericError(VSSDBG_GEN, ft.hr, L"::SafeArrayUnaccessData( pSafeArray )");
|
|
|
|
// Clear the variant
|
|
variant.Clear();
|
|
|
|
// tell the variant what it is holding onto
|
|
variant.parray = pSafeArray.Detach();
|
|
variant.vt = VT_ARRAY | VT_UI1;
|
|
}
|
|
|
|
|
|
// Take the resource offline
|
|
void CVssClusterAPI::TakeResourceOffline(
|
|
IN ISClusResource* pResource
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::TakeResourceOffline" );
|
|
|
|
BS_ASSERT(pResource);
|
|
|
|
// Get resource name
|
|
CVssComBSTR bstrName;
|
|
CHECK_COM( pResource->get_Name(&bstrName) );
|
|
|
|
// Take the resource offline
|
|
CComVariant varStillPending;
|
|
CHECK_COM( pResource->Offline(m_dwOfflineTimeout, &varStillPending) );
|
|
|
|
BS_ASSERT( varStillPending.vt == VT_BOOL );
|
|
if (varStillPending.boolVal == VARIANT_TRUE)
|
|
{
|
|
ft.LogGenericWarning(VSSDBG_GEN, L"pResource->Offline(%ld, [VT_TRUE]) [%s]",
|
|
(LONG)m_dwOfflineTimeout, (LPWSTR)bstrName);
|
|
ft.Throw( VSSDBG_GEN, E_UNEXPECTED,
|
|
L"The resource [%s] couldn't be brought offline in the required timeframe.",
|
|
(LPWSTR)bstrName);
|
|
}
|
|
}
|
|
|
|
|
|
// Bring the resource online
|
|
void CVssClusterAPI::BringResourceOnline(
|
|
IN ISClusResource* pResource
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::BringResourceOnline" );
|
|
|
|
BS_ASSERT(pResource);
|
|
|
|
// Get resource name
|
|
CVssComBSTR bstrName;
|
|
CHECK_COM( pResource->get_Name(&bstrName) );
|
|
|
|
// Bring the resource online
|
|
CComVariant varStillPending;
|
|
CHECK_COM( pResource->Online(m_dwOnlineTimeout, &varStillPending) );
|
|
|
|
BS_ASSERT( varStillPending.vt == VT_BOOL );
|
|
if (varStillPending.boolVal == VARIANT_TRUE)
|
|
{
|
|
ft.LogGenericWarning(VSSDBG_GEN, L"pResource->Online(%ld, [VT_TRUE]) [%s]",
|
|
(LONG)m_dwOnlineTimeout, (LPWSTR)bstrName);
|
|
ft.Throw( VSSDBG_GEN, E_UNEXPECTED,
|
|
L"The resource [%s] couldn't be brought online in the required timeframe.",
|
|
(LPWSTR)bstrName);
|
|
}
|
|
}
|
|
|
|
|
|
// Calculate the list of dependent resources that needs to be brought online after adding a
|
|
// dependency fron this resource
|
|
void CVssClusterAPI::GetFinalOnlineResourceList(
|
|
IN ISClusResource* pResource,
|
|
IN OUT CVssClusterResourceList & list
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::GetFinalOnlineResourceList" );
|
|
|
|
// Determine if the resource is online.
|
|
// If not, we stop here.
|
|
CLUSTER_RESOURCE_STATE csState;
|
|
CHECK_COM( pResource->get_State(&csState) );
|
|
if (csState != ClusterResourceOnline)
|
|
return;
|
|
|
|
// Start with one resource only
|
|
CVssClusterResourceList frontier;
|
|
frontier.Push(pResource);
|
|
|
|
while (true)
|
|
{
|
|
CComPtr<ISClusResource> pCurrentResource;
|
|
frontier.Pop(pCurrentResource);
|
|
|
|
// If the frontier is empty, the we are finished (the dependency graph cannot contain cycles)
|
|
if (pCurrentResource == NULL)
|
|
break;
|
|
|
|
// Get the dependents collection for the current resource
|
|
CComPtr<ISClusResDependents> pDependents;
|
|
CHECK_COM( pCurrentResource->get_Dependents(&pDependents) );
|
|
|
|
// Get the dependencies count
|
|
LONG lDependentsCount = 0;
|
|
CHECK_COM( pDependents->get_Count(&lDependentsCount) );
|
|
|
|
// Iterate through all dependencies
|
|
DWORD dwOnlineDependentResources = 0;
|
|
for (INT nDependentIndex = 1; nDependentIndex <= lDependentsCount; nDependentIndex++)
|
|
{
|
|
// Get the dependency with that index
|
|
CComPtr<ISClusResource> pDependentResource;
|
|
CComVariant varDependentIndex = nDependentIndex;
|
|
CHECK_COM( pDependents->get_Item(varDependentIndex, &pDependentResource) );
|
|
|
|
// Determine if the resource is online.
|
|
// If not, we ignore it.
|
|
CHECK_COM( pDependentResource->get_State(&csState) );
|
|
if (csState != ClusterResourceOnline)
|
|
continue;
|
|
|
|
// We add this resource to the frontier
|
|
frontier.Push(pDependentResource);
|
|
dwOnlineDependentResources++;
|
|
}
|
|
|
|
// If this was a final resource (no dependents added to the list) then add it to the final list
|
|
if (dwOnlineDependentResources == 0)
|
|
list.Push(pCurrentResource);
|
|
}
|
|
}
|
|
|
|
|
|
// Bring the resources in the list online
|
|
void CVssClusterAPI::BringResourceListOnline(
|
|
IN CVssClusterResourceList & list
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::BringResourceListOnline" );
|
|
|
|
while (true)
|
|
{
|
|
CComPtr<ISClusResource> pCurrentResource;
|
|
list.Pop(pCurrentResource);
|
|
|
|
// If the frontier is empty, then we are finished (the dependency graph cannot contain cycles)
|
|
if (pCurrentResource == NULL)
|
|
break;
|
|
|
|
// Determine if the resource is online.
|
|
// If not, we ignore it.
|
|
CLUSTER_RESOURCE_STATE csState;
|
|
CHECK_COM( pCurrentResource->get_State(&csState) );
|
|
if (csState != ClusterResourceOffline)
|
|
continue;
|
|
|
|
// Bring this resource online
|
|
BringResourceOnline(pCurrentResource);
|
|
}
|
|
}
|
|
|
|
|
|
// Bring the resources in the list online
|
|
void CVssClusterAPI::GetQuorumPath(
|
|
CComBSTR & bstrQuorumPath
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterAPI::GetQuorumPath" );
|
|
|
|
bstrQuorumPath.Empty();
|
|
|
|
// Get the path to the Quorum log file
|
|
BS_ASSERT(m_pCluster);
|
|
CHECK_COM( m_pCluster->get_QuorumPath(&bstrQuorumPath) );
|
|
ft.Trace( VSSDBG_GEN, L"Quorum file path: %s", bstrQuorumPath);
|
|
|
|
if ((NULL == (LPWSTR)bstrQuorumPath) || (L'\0' == bstrQuorumPath[0])) {
|
|
BS_ASSERT(false);
|
|
ft.TranslateGenericError(VSSDBG_GEN, E_UNEXPECTED, L"get_QuorumPath([NULL])");
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CVssClusterQuorumVolume definition and implementation
|
|
//
|
|
// Note: this class is separate from CVssClusterAPI so its instances can be static.
|
|
|
|
|
|
// Checks if the given volume is the quorum
|
|
// This will return FALSE in non-cluster case.
|
|
bool CVssClusterQuorumVolume::IsQuorumVolume(
|
|
IN LPCWSTR pwszVolumeName
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterQuorumVolume::IsQuorumVolume" );
|
|
|
|
// If the volume is not initialized, then try to initialize it
|
|
if (!m_bQuorumVolumeInitialized)
|
|
InitializeQuorumVolume();
|
|
|
|
// Return TRUE only if this is the quorum volume
|
|
return ft.Exit(m_awszQuorumVolumeName.GetRef()
|
|
&& (wcscmp(m_awszQuorumVolumeName.GetRef(), pwszVolumeName) == 0));
|
|
}
|
|
|
|
|
|
// Checks if the given volume is the quorum
|
|
// This will work in non-cluster case also.
|
|
void CVssClusterQuorumVolume::InitializeQuorumVolume() throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssClusterQuorumVolume::InitializeQuorumVolume" );
|
|
|
|
BS_ASSERT(!m_bQuorumVolumeInitialized)
|
|
|
|
// Mark this as being already initialized
|
|
// (to not recompute the quorum each time we call IsQuorumVolume)
|
|
m_bQuorumVolumeInitialized = true;
|
|
|
|
// Try to initialize the cluster, if possibles
|
|
CVssClusterAPI clus;
|
|
bool bClusterInitialized = clus.Initialize(L"");
|
|
|
|
// If we don't have a cluster, then we don't have quorum volume.
|
|
if (!bClusterInitialized)
|
|
return;
|
|
|
|
// Get the quorum path
|
|
CComBSTR bstrQuorumPath;
|
|
clus.GetQuorumPath(bstrQuorumPath);
|
|
|
|
//
|
|
// Test if the path starts with a drive letter
|
|
// NOTE: We assume that the path is a drive letter (we ignore shares)
|
|
//
|
|
|
|
// 1) We should have room at least for "<drive letter>:\"
|
|
if (wcslen(bstrQuorumPath) < 3)
|
|
return;
|
|
|
|
// 2) This is not at share
|
|
if (bstrQuorumPath[0] == L'\\')
|
|
return;
|
|
|
|
// 3) Test if the rest of the path matches ":\"
|
|
if (bstrQuorumPath[1] != L':')
|
|
return;
|
|
if (bstrQuorumPath[2] != L'\\')
|
|
return;
|
|
|
|
// Get the volume path
|
|
WCHAR wszQuorumVolume[MAX_PATH];
|
|
if (!GetVolumePathNameW(bstrQuorumPath, STRING_CCH_PARAM(wszQuorumVolume)))
|
|
{
|
|
switch(GetLastError())
|
|
{
|
|
// The volume might be offline
|
|
case ERROR_FILE_NOT_FOUND:
|
|
case ERROR_DEVICE_NOT_CONNECTED:
|
|
case ERROR_NOT_READY:
|
|
return;
|
|
|
|
// Otherwise
|
|
default:
|
|
ft.TranslateWin32Error(VSSDBG_GEN,
|
|
L"GetVolumePathNameW('%s', STRING_CCH_PARAM(wszQuorumVolumePath))", bstrQuorumPath);
|
|
}
|
|
}
|
|
|
|
// Get the volume name
|
|
WCHAR wszVolumeName[x_nLengthOfVolMgmtVolumeName + 1];
|
|
if (!GetVolumeNameForVolumeMountPointW( wszQuorumVolume, STRING_CCH_PARAM(wszVolumeName) ))
|
|
{
|
|
switch(GetLastError())
|
|
{
|
|
// The volume might be offline
|
|
case ERROR_FILE_NOT_FOUND:
|
|
case ERROR_DEVICE_NOT_CONNECTED:
|
|
case ERROR_NOT_READY:
|
|
return;
|
|
|
|
// Otherwise
|
|
default:
|
|
ft.TranslateWin32Error(VSSDBG_GEN,
|
|
L"GetVolumeNameForVolumeMountPointW( '%s', STRING_CCH_PARAM(wszVolumeName))", wszQuorumVolume);
|
|
}
|
|
}
|
|
ft.Trace( VSSDBG_GEN, L"Quorum volume name: %s", wszVolumeName);
|
|
|
|
// Cache the quorum volume
|
|
m_awszQuorumVolumeName.CopyFrom(wszVolumeName);
|
|
}
|