2025-11-28 00:35:46 +09:00

1539 lines
43 KiB
C++

/*--
Copyright (C) Microsoft Corporation
Module Name:
SampleProvider.cpp
Abstract:
Implementation of the CSampleProvider class, a sample VSS HW provider
that makes use of a virtual disk driver to create snapshots.
Notes:
Revision History:
--*/
#include "stdafx.h"
#include "SampleProvider.h"
#include "async.h"
extern WCHAR *g_wszProviderName;
// CSampleProvider ctor and dtor
CSampleProvider::CSampleProvider(
)
: m_state( VSS_SS_UNKNOWN )
{
TRACE_FUNCTION();
memset(&m_setId, 0, sizeof m_setId);
InitializeCriticalSection( &m_cs );
}
CSampleProvider::~CSampleProvider(
)
{
TRACE_FUNCTION();
//
// Cleanup any in-progress LUNs by unloading. The OnUnload() call
// will mostly be redundant since VSS should have called it
// before releasing the interface, but this is harmless and
// provides extra safety.
//
OnUnload(TRUE);
DeleteCriticalSection( &m_cs );
}
// Helpers
void
CSampleProvider::FreeLunInfo(
VDS_LUN_INFORMATION& lun
)
{
SAFE_COFREE( lun.m_szVendorId );
SAFE_COFREE( lun.m_szProductId );
SAFE_COFREE( lun.m_szProductRevision );
SAFE_COFREE( lun.m_szSerialNumber );
VDS_STORAGE_DEVICE_ID_DESCRIPTOR& desc = lun.m_deviceIdDescriptor;
for (ULONG i = 0; i < desc.m_cIdentifiers; ++i) {
SAFE_COFREE( desc.m_rgIdentifiers[i].m_rgbIdentifier );
}
SAFE_COFREE( desc.m_rgIdentifiers );
for (ULONG i = 0; i < lun.m_cInterconnects; ++i) {
VDS_INTERCONNECT& inter = lun.m_rgInterconnects[i];
SAFE_COFREE( inter.m_pbPort );
SAFE_COFREE( inter.m_pbAddress );
}
SAFE_COFREE( lun.m_rgInterconnects );
}
void
CSampleProvider::CopyBasicLunInfo(
VDS_LUN_INFORMATION& lunDst,
VDS_LUN_INFORMATION& lunSrc
)
{
ZeroMemory(&lunDst,sizeof(VDS_LUN_INFORMATION));
lunDst.m_version = lunSrc.m_version;
lunDst.m_DeviceType = lunSrc.m_DeviceType;
lunDst.m_DeviceTypeModifier = lunSrc.m_DeviceTypeModifier;
lunDst.m_bCommandQueueing = lunSrc.m_bCommandQueueing;
lunDst.m_BusType = lunSrc.m_BusType;
//
// These NewString() calls may throw HRESULT exceptions, the
// caller should be prepared to deal with them
//
lunDst.m_szVendorId = NewString( lunSrc.m_szVendorId );
lunDst.m_szProductId = NewString( lunSrc.m_szProductId );
lunDst.m_szProductRevision = NewString( lunSrc.m_szProductRevision );
lunDst.m_szSerialNumber = NewString( lunSrc.m_szSerialNumber );
lunDst.m_diskSignature = lunSrc.m_diskSignature;
}
void
CSampleProvider::DisplayLunInfo(
VDS_LUN_INFORMATION& lun
)
{
TRACE_FUNCTION();
TraceMsg(L"Initial: m_deviceIdDescriptor.m_cIdentifiers=%d, m_deviceIdDescriptor.m_rgIdentifiers=0x%08x\n",
lun.m_deviceIdDescriptor.m_cIdentifiers,
lun.m_deviceIdDescriptor.m_rgIdentifiers);
TraceMsg(L"Initial: m_cInterconnects=%d, m_rgInterconnects=0x%08x\n",
lun.m_cInterconnects,
lun.m_rgInterconnects);
}
//
// This function makes a best effort to delete any outstanding snapshots, but
// will not indicate errors and will never throw an exception.
//
void
CSampleProvider::DeleteAbortedSnapshots(
)
{
TRACE_FUNCTION();
HRESULT hr = S_OK;
SnapshotInfoVector::iterator i;
AutoLock lock(m_cs);
for (i = m_vSnapshotInfo.begin(); i != m_vSnapshotInfo.end(); ++i) {
try {
//
// We ignore errors here, since the drive may not yet have even
// been created.
//
hr = m_vbus.RemoveDrive(i->snapLunId, false);
if(FAILED(hr))
{
TraceMsg(L"Error was returned calling removeDrive, hr: %x \n", hr);
}
std::wstring fileName = SnapshotImageFile(i->snapLunId);
int retry = 0;
while (DeleteFile(fileName.c_str()) == FALSE &&
GetLastError() == ERROR_SHARING_VIOLATION && ++ retry < 5)
{
//
// Sleep 2 seconds to wait for the virtual storage driver to
// release the snapshot image file.
// Retry 5 times in total. Ignore if the file cannot be
// deleted after 5 times, or if the error is not due
// to ERROR_SHARING_VIOLATION
//
Sleep(2000);
}
} catch (std::bad_alloc) {
// Ignore out of memory errors, just do best effort to delete
// snapshots.
}
}
m_vSnapshotInfo.clear();
}
BOOL
CSampleProvider::FindSnapId(
GUID origLunId,
GUID& snapLunId
)
{
SnapshotInfoVector::iterator i;
for (i = m_vSnapshotInfo.begin(); i != m_vSnapshotInfo.end(); ++i) {
if (IsEqualGUID(i->origLunId, origLunId) == TRUE) {
snapLunId = i->snapLunId;
return TRUE;
}
}
return FALSE;
}
BOOL
CSampleProvider::FindOrigId(
GUID snapLunId,
GUID& origLunId
)
{
SnapshotInfoVector::iterator i;
for (i = m_vSnapshotInfo.begin(); i != m_vSnapshotInfo.end(); ++i) {
if (IsEqualGUID(i->snapLunId, snapLunId) == TRUE) {
origLunId = i->origLunId;
return TRUE;
}
}
return FALSE;
}
std::wstring
CSampleProvider::SnapshotImageFile(
GUID snapLunId
)
{
HRESULT hr = S_OK;
std::wstring sysDrive;
std::wstring envName(L"SystemDrive");
hr = GetEnvVar(envName, sysDrive);
if (SUCCEEDED( hr )) {
return sysDrive + std::wstring(L"\\") + GuidToWString(snapLunId) + L".image";
} else {
return std::wstring(L"C:\\") + GuidToWString(snapLunId) + L".image";
}
}
//
// Create virtual drive using virtualstorage driver
//
HRESULT CSampleProvider::CreateVirtualDrive (
GUID snapId,
std::wstring fileName,
LARGE_INTEGER fileSize,
VDS_STORAGE_DEVICE_ID_DESCRIPTOR& vdsDesc,
VDS_STORAGE_IDENTIFIER& vdsStorId
)
{
TRACE_FUNCTION();
//
// Calcualte the size of storageId descriptor,
// only count the first storage identifier (see IsLunSupported)
//
size_t sizeStorageIdDesc =
sizeof(STORAGE_DEVICE_ID_DESCRIPTOR) +
sizeof(STORAGE_IDENTIFIER) +
vdsStorId.m_cbIdentifier;
//
// Create new virtual drive using image file
//
size_t sizeStruct = sizeof(NEW_VIRTUAL_DRIVE_DESCRIPTION);
std::wstring strImagePath(L"\\??\\" + fileName);
size_t sizeImagePath = strImagePath.size() * sizeof(WCHAR);
size_t sizeImagePathAligned = (sizeImagePath % sizeof(ULONG) == 0) ?
sizeImagePath : (sizeImagePath + sizeImagePath % sizeof(ULONG));
sizeStruct += sizeImagePathAligned;
sizeStruct += sizeStorageIdDesc;
std::vector<BYTE> vecData(sizeStruct,0);
NEW_VIRTUAL_DRIVE_DESCRIPTION* pInfoDrive = reinterpret_cast<NEW_VIRTUAL_DRIVE_DESCRIPTION*>(&vecData[0]);
pInfoDrive->Length = static_cast<USHORT>(sizeStruct);
pInfoDrive->BlockSize = 512;
pInfoDrive->NumberOfBlocks = static_cast<ULONG>( fileSize.QuadPart / pInfoDrive->BlockSize );
pInfoDrive->Flags = 0;
pInfoDrive->DeviceType = VIRTUAL_FIXED_DISK;
pInfoDrive->DriveID = snapId;
pInfoDrive->FileNameOffset = 0;
pInfoDrive->FileNameLength = static_cast<USHORT>(sizeImagePath);
memcpy(pInfoDrive->Buffer, strImagePath.c_str(), pInfoDrive->FileNameLength);
pInfoDrive->StorageDeviceIdDescOffset = static_cast<USHORT>(sizeImagePathAligned);
pInfoDrive->StorageDeviceIdDescLength = static_cast<USHORT>(sizeStorageIdDesc);
//
// Copy storageId descriptor and storage identifiers
//
STORAGE_DEVICE_ID_DESCRIPTOR* pDesc =
reinterpret_cast<STORAGE_DEVICE_ID_DESCRIPTOR*>(pInfoDrive->Buffer + pInfoDrive->StorageDeviceIdDescOffset);
pDesc->Version = vdsDesc.m_version;
pDesc->Size = (ULONG)sizeStorageIdDesc;
pDesc->NumberOfIdentifiers = 1; // vdsDesc.m_cIdentifiers;
STORAGE_IDENTIFIER* pId = reinterpret_cast<STORAGE_IDENTIFIER*>(pDesc->Identifiers);
pId->CodeSet = (STORAGE_IDENTIFIER_CODE_SET)vdsStorId.m_CodeSet;
pId->Type = (STORAGE_IDENTIFIER_TYPE)vdsStorId.m_Type;
pId->IdentifierSize = (USHORT)vdsStorId.m_cbIdentifier;
pId->NextOffset = 0;
pId->Association = StorageIdAssocDevice;
memcpy(pId->Identifier, vdsStorId.m_rgbIdentifier, pId->IdentifierSize);
//
// Create virtual drive
//
VIRTUAL_DRIVE_INFORMATION driveInfo;
HRESULT hr = m_vbus.CreateDriveEx( pInfoDrive, driveInfo );
return hr;
}
//
// The VDS_LUN_INFO of the supportd LUNs must have
// VendorId = "Microsoft Corporation"
// ProductId = "VIRTUALSTORAGEVSS"
// Storage Identifiers > 1
// The 1st storageId = L"VSS Sample HW Provider" + 16 bytes' GUID
//
BOOL CSampleProvider::IsLunSupported (
VDS_LUN_INFORMATION& LunInfo
)
{
TRACE_FUNCTION();
if( strcmp(LunInfo.m_szVendorId, "Microsoft Corporation") != 0 ||
strcmp(LunInfo.m_szProductId, "VIRTUALSTORAGE") !=0
)
return FALSE;
VDS_STORAGE_DEVICE_ID_DESCRIPTOR& desc = LunInfo.m_deviceIdDescriptor;
if(desc.m_cIdentifiers < 1)
return FALSE;
VDS_STORAGE_IDENTIFIER& storId = desc.m_rgIdentifiers[0];
size_t sizeName = wcslen(g_wszProviderName) * sizeof(WCHAR);
size_t sizeGuid = sizeof(GUID);
if( storId.m_cbIdentifier != (sizeName+sizeGuid) )
return FALSE;
if( memcmp(storId.m_rgbIdentifier, g_wszProviderName, sizeName)!=0 )
return FALSE;
return TRUE;
}
//
// IVssHardwareSnapshotProvider methods
//
STDMETHODIMP
CSampleProvider::AreLunsSupported(
IN LONG lLunCount,
IN LONG /*lContext*/,
__RPC__in_ecount_full_opt(lLunCount) VSS_PWSZ* /*rgwszDevices*/,
__RPC__inout_ecount_full(lLunCount) VDS_LUN_INFORMATION* rgLunInformation,
__RPC__out BOOL* pbIsSupported
)
{
TRACE_FUNCTION();
HRESULT hr = S_OK;
AutoLock lock( m_cs );
try {
*pbIsSupported = FALSE;
for (int i = 0; i < lLunCount; i++) {
GUID gId;
if ( !IsLunSupported(rgLunInformation[i]) ) {
goto done;
}
hr = AnsiToGuid(rgLunInformation[i].m_szSerialNumber, gId);
switch (hr) {
case S_OK:
break;
case E_OUTOFMEMORY:
//
// Allow out of memory errors to float up
//
throw hr;
default:
//
// An invalid GUID isn't a failure, it just means that
// the LUN can't possibly be one of ours. In this
// case we should still return S_OK with
// *pbIsSupported = FALSE.
//
hr = S_OK;
goto done;
}
//
// Query the virtual storage driver for info on this drive,
// if the driver returns an error then the drive isn't one
// of ours. In this case we should still return S_OK with
// *pbIsSupported = FALSE;
//
VstorInterface::VirtualBus::STORAGE_INFORMATION storageInfo;
HRESULT hrv = m_vbus.QueryStorageInformation(gId, storageInfo);
if (hrv != S_OK) {
//Talked to faisala and dineshh, AreLunsSupported actually means "Are the LUNs taken care of by this provider"
//So we will also need to return true if it is one of the snapshot LUNs created by us.
std::wstring wstrSnapfileName = SnapshotImageFile(gId);
HANDLE hFile = CreateFile( wstrSnapfileName.c_str(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
if (hFile == INVALID_HANDLE_VALUE) {
hr = E_FAIL;
CloseHandle(hFile);
goto done;
}
CloseHandle(hFile);
}
//
// Fudge the bus type to VDSBusTypeScsi
//
rgLunInformation[i].m_BusType = VDSBusTypeScsi;
}
*pbIsSupported = TRUE;
} catch(HRESULT hre) {
hr = hre;
} catch(std::bad_alloc) {
hr = E_OUTOFMEMORY;
}
done:
TraceMsg(L"returning 0x%08x\n", hr);
return hr;
}
STDMETHODIMP
CSampleProvider::GetTargetLuns(
IN LONG lLunCount,
__RPC__in_ecount_full_opt(lLunCount) VSS_PWSZ* /*rgwszDevices*/,
__RPC__in_ecount_full_opt(lLunCount) VDS_LUN_INFORMATION* rgSourceLuns,
__RPC__inout_ecount_full(lLunCount) VDS_LUN_INFORMATION* rgDestinationLuns
)
{
TRACE_FUNCTION();
HRESULT hr = S_OK;
if(rgSourceLuns == NULL)
{
//Invalid pointer
hr = E_POINTER;
TraceMsg(L"rgSourceLuns is NULL, returning 0x%08x\n", hr);
return hr;
}
AutoLock lock( m_cs );
try {
for (LONG i = 0; i < lLunCount; i++) {
VDS_LUN_INFORMATION& lunSource = rgSourceLuns[i];
VDS_LUN_INFORMATION& lunTarget = rgDestinationLuns[i];
FreeLunInfo(lunTarget);
CopyBasicLunInfo(lunTarget, lunSource);
memset(&lunTarget.m_diskSignature, 0, sizeof lunTarget.m_diskSignature);
//
// Set storage device id descriptor
//
VDS_STORAGE_DEVICE_ID_DESCRIPTOR& lunDesc = lunTarget.m_deviceIdDescriptor;
lunDesc.m_version = 1;
lunDesc.m_cIdentifiers = 1;
lunDesc.m_rgIdentifiers = reinterpret_cast<VDS_STORAGE_IDENTIFIER*>
(CoTaskMemAlloc(sizeof(VDS_STORAGE_IDENTIFIER)));
if( lunDesc.m_rgIdentifiers == NULL )
throw E_OUTOFMEMORY;
//
// Set storage identifier
//
VDS_STORAGE_IDENTIFIER& storageId = lunDesc.m_rgIdentifiers[0];
storageId.m_CodeSet = VDSStorageIdCodeSetBinary;
storageId.m_Type = VDSStorageIdTypeVendorId;
ULONG cbIdentifier = (ULONG)(wcslen(g_wszProviderName)*sizeof(WCHAR) + sizeof(GUID));
storageId.m_cbIdentifier = cbIdentifier;
storageId.m_rgbIdentifier = reinterpret_cast<BYTE*>(CoTaskMemAlloc(cbIdentifier));
if( storageId.m_rgbIdentifier == NULL )
throw E_OUTOFMEMORY;
memcpy(storageId.m_rgbIdentifier, g_wszProviderName,
wcslen(g_wszProviderName) * sizeof(WCHAR));
GUID storageGuid;
CoCreateGuid(&storageGuid);
memcpy(storageId.m_rgbIdentifier + wcslen(g_wszProviderName)*sizeof(WCHAR),
&storageGuid, sizeof(GUID));
GUID origId, snapId;
hr = AnsiToGuid(lunSource.m_szSerialNumber, origId);
switch (hr) {
case S_OK:
break;
case E_OUTOFMEMORY:
//
// Allow out of memory errors to float up
//
throw hr;
default:
//
// Any other error indicates we were passed a bad source LUN,
// so log it and return VSS_E_PROVIDER_VETO
//
LogEvent(L"GetTargetLuns called with invalid source LUN ('%S')",
lunSource.m_szSerialNumber);
throw (HRESULT) VSS_E_PROVIDER_VETO;
}
//
// Find the snapshot GUID associated with this LUN
//
BOOL b = FindSnapId(origId, snapId);
if (b == FALSE) {
LogEvent(L"GetTargetLuns called with unknown LUN ('%S')",
lunSource.m_szSerialNumber);
throw (HRESULT) VSS_E_PROVIDER_VETO;
}
SAFE_COFREE(lunTarget.m_szSerialNumber);
lunTarget.m_szSerialNumber = GuidToAnsi(snapId);
//
// Fudge the bus type to VDSBusTypeScsi
//
lunTarget.m_BusType = VDSBusTypeScsi;
}
} catch (HRESULT hre) {
hr = hre;
} catch (std::bad_alloc) {
hr = E_OUTOFMEMORY;
}
TraceMsg(L"returning 0x%08x\n", hr);
return hr;
}
STDMETHODIMP
CSampleProvider::LocateLuns(
IN LONG lLunCount,
__RPC__in_ecount_full_opt(lLunCount) VDS_LUN_INFORMATION* rgSourceLuns
)
{
TRACE_FUNCTION();
HRESULT hr = S_OK;
if(rgSourceLuns == NULL)
{
// Invalid pointer:
hr = E_POINTER;
TraceMsg(L"rgSourceLuns is NULL, returning 0x%08x\n", hr);
return hr;
}
AutoLock lock( m_cs );
try {
// Map for keeping track of loaded virtual storage drives.
// This is so that we don't load the same drive twice.
// Using map for faster lookups.
std::map<std::wstring, bool> loadedDriveMap;
for (LONG i = 0; i < lLunCount; i++) {
VDS_LUN_INFORMATION& lunSource = rgSourceLuns[i];
GUID snapId;
hr = AnsiToGuid(lunSource.m_szSerialNumber, snapId);
switch (hr) {
case S_OK:
break;
case E_OUTOFMEMORY:
//
// Allow out of memory errors to float up
//
throw hr;
default:
//
// Any other error indicates we were passed a bad source LUN,
// so log it and return VSS_E_PROVIDER_VETO
//
LogEvent(L"LocateLuns called with invalid source LUN m_szSerialNumber ('%S')",
lunSource.m_szSerialNumber);
throw (HRESULT) VSS_E_PROVIDER_VETO;
}
//
// Calculate size of image file
// If the snapshot ID is invalid, CreateFile will fail and
// so log it and return VSS_E_PROVIDER_VETO
//
std::wstring fileName = SnapshotImageFile(snapId);
// If we already loaded this virtual storage image, continue
if (loadedDriveMap.find(fileName) != loadedDriveMap.end())
continue;
HANDLE hFile = CreateFile( fileName.c_str(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
if (hFile == INVALID_HANDLE_VALUE) {
DWORD err = GetLastError();
LogEvent( L"Error opening image file '%s' (%d)",
fileName.c_str(),
err );
throw (HRESULT) VSS_E_PROVIDER_VETO;
}
LARGE_INTEGER fileSize;
BOOL br = GetFileSizeEx( hFile, &fileSize );
if (br == FALSE) {
DWORD err = GetLastError();
LogEvent( L"Error getting size of image file '%s' (%d)",
fileName.c_str(),
err );
CloseHandle(hFile);
throw (HRESULT) VSS_E_PROVIDER_VETO;
}
CloseHandle(hFile);
//
// Validate the input vds_lun_info
//
VDS_STORAGE_DEVICE_ID_DESCRIPTOR& vdsDesc = lunSource.m_deviceIdDescriptor;
if(vdsDesc.m_cIdentifiers==0 || vdsDesc.m_rgIdentifiers==NULL) {
throw (HRESULT) VSS_E_PROVIDER_VETO;
}
VDS_STORAGE_IDENTIFIER& vdsStorId = vdsDesc.m_rgIdentifiers[0];
if( vdsStorId.m_cbIdentifier==0 || vdsStorId.m_rgbIdentifier==NULL ) {
throw (HRESULT) VSS_E_PROVIDER_VETO;
}
//
// Create virtual drive
//
hr = CreateVirtualDrive(snapId, fileName, fileSize, vdsDesc, vdsStorId);
switch (hr) {
case S_OK:
// Record the fact that we loaded this vstor file.
loadedDriveMap[fileName] = true;
break;
default:
LogEvent(L"CreateDriveEx for '%S' failed with 0x%08x",
lunSource.m_szSerialNumber,
hr);
throw (HRESULT) VSS_E_PROVIDER_VETO;
}
}
} catch (HRESULT hre) {
hr = hre;
} catch (std::bad_alloc) {
hr = E_OUTOFMEMORY;
}
TraceMsg(L"returning 0x%08x\n", hr);
return hr;
}
STDMETHODIMP
CSampleProvider::FillInLunInfo(
IN VSS_PWSZ /*wszDeviceName*/,
__RPC__inout VDS_LUN_INFORMATION* pLunInformation,
__RPC__out BOOL* pbIsSupported
)
{
TRACE_FUNCTION();
HRESULT hr = S_OK;
AutoLock lock( m_cs );
try {
//
// We see if the new LUN is one of ours, and modify the
// m_szSerialNumber field to match the GUID format that we set
// in GetTargetLuns(), since the case and format are not
// necessarily the same. Luns that are not ours are ignored
// and pbIsSupported is set to FALSE.
//
*pbIsSupported = FALSE;
//
// We only fill LUN's info if it is one of ours.
//
if ( !IsLunSupported(*pLunInformation) ) {
goto done;
}
GUID snapId;
hr = AnsiToGuid(pLunInformation->m_szSerialNumber, snapId);
switch (hr) {
case S_OK:
break;
case E_OUTOFMEMORY:
throw hr;
default:
//
// An invalid GUID indicates that the LUN can't be ours,
// just skip it.
//
hr = S_OK;
goto done;
}
//
// Query the virtual storage driver for info on this drive,
// if the driver returns an error then the drive isn't one
// of ours. In this case we just ignore it.
//
VstorInterface::VirtualBus::STORAGE_INFORMATION storageInfo;
HRESULT hrv = m_vbus.QueryStorageInformation(snapId, storageInfo);
if (hrv != S_OK) {
goto done;
}
SAFE_COFREE(pLunInformation->m_szSerialNumber);
pLunInformation->m_szSerialNumber = GuidToAnsi(snapId);
*pbIsSupported = TRUE;
//
// Fudge the bus type to VDSBusTypeScsi
//
pLunInformation->m_BusType = VDSBusTypeScsi;
} catch (HRESULT hre) {
hr = hre;
} catch (std::bad_alloc) {
hr = E_OUTOFMEMORY;
}
done:
TraceMsg(L"returning 0x%08x\n", hr);
return hr;
}
STDMETHODIMP
CSampleProvider::OnLunEmpty(
__RPC__in_opt VSS_PWSZ /*wszDevice*/,
__RPC__in_opt VDS_LUN_INFORMATION* pInfo
)
{
TRACE_FUNCTION();
HRESULT hr = S_OK;
if(pInfo == NULL)
{
// Invalid pointer:
hr = E_POINTER;
TraceMsg(L"pInfo is NULL, returning 0x%08x\n", hr);
return hr;
}
AutoLock lock( m_cs );
try {
GUID snapId;
hr = AnsiToGuid(pInfo->m_szSerialNumber, snapId);
switch (hr) {
case S_OK:
break;
case E_OUTOFMEMORY:
throw hr;
default:
//
// Any other error indicates we were passed a bad source LUN,
// so log it and return VSS_E_PROVIDER_VETO
//
LogEvent(L"OnLunEmpty called with invalid LUN ('%S')",
pInfo->m_szSerialNumber);
throw (HRESULT) VSS_E_PROVIDER_VETO;
}
hr = m_vbus.RemoveDrive(snapId, false);
if (FAILED(hr)) {
//
// If we can't delete the LUN, log and return VSS_E_PROVIDER_VETO
//
LogEvent(L"RemoveDrive for '%S' failed with 0x%08x",
pInfo->m_szSerialNumber,
hr);
throw (HRESULT) VSS_E_PROVIDER_VETO;
}
std::wstring fileName = SnapshotImageFile(snapId);
int i = 0;
while (DeleteFile(fileName.c_str()) == FALSE) {
DWORD err = GetLastError();
if(err == ERROR_SHARING_VIOLATION && ++ i < 5)
{
//
// Sleep 2 seconds to wait for the virtual storage driver to
// release the snapshot image file.
// Retry 5 times in total.
//
Sleep(2000);
continue;
}
//
// If we can't delete the image file, log and return error
//
LogEvent(L"DeleteFile for '%S' failed (%d)",
pInfo->m_szSerialNumber,
err);
throw (HRESULT) VSS_E_PROVIDER_VETO;
}
} catch (HRESULT hre) {
hr = hre;
} catch (std::bad_alloc) {
hr = E_OUTOFMEMORY;
}
TraceMsg(L"returning 0x%08x\n", hr);
return hr;
}
STDMETHODIMP
CSampleProvider::BeginPrepareSnapshot(
IN VSS_ID SnapshotSetId,
IN VSS_ID /*SnapshotId*/,
IN LONG /*lContext*/,
IN LONG lLunCount,
__RPC__in_ecount_full_opt(lLunCount) VSS_PWSZ* /*rgwszDevices*/,
__RPC__inout_ecount_full(lLunCount) VDS_LUN_INFORMATION* rgLunInformation
)
{
TRACE_FUNCTION();
HRESULT hr = S_OK;
AutoLock lock( m_cs );
try {
switch (m_state) {
case VSS_SS_PREPARING:
//
// If we get a new snapshot set id, then we are starting a
// new snapshot and we should delete any uncompleted
// snapshots. Otherwise continue to add LUNs to the set.
//
if (!IsEqualGUID(SnapshotSetId, m_setId)) {
DeleteAbortedSnapshots();
}
break;
case VSS_SS_UNKNOWN:
case VSS_SS_CREATED:
case VSS_SS_ABORTED:
//
// If we are in the initial state, or completed/aborted
// the previous snapshot, initialize the list of LUNs
// participating in this snapshot
//
m_vSnapshotInfo.clear();
break;
default:
//
// If we were in any other state we should abort the
// current snapshot and delete any in-progess snapshots
//
DeleteAbortedSnapshots();
break;
}
for (LONG i = 0; i < lLunCount; i++) {
GUID origId;
GUID snapId;
hr = AnsiToGuid(rgLunInformation[i].m_szSerialNumber, origId);
switch (hr) {
case S_OK:
break;
case E_OUTOFMEMORY:
throw hr;
default:
//
// Any other error indicates we were passed a bad source LUN,
// so log it and return VSS_E_PROVIDER_VETO
//
LogEvent(L"BeginPrepareSnapshot called with invalid LUN ('%S')",
rgLunInformation[i].m_szSerialNumber);
throw (HRESULT) VSS_E_PROVIDER_VETO;
}
//
// If we already have this LUN included in this snapshot set, skip it
//
if (FindSnapId(origId, snapId)) {
continue;
}
//
// Create a unique GUID to represent the snapshot drive.
// A real provider might ask the array to prepare to
// create the LUN but not expose or commit the snapshot.
//
CoCreateGuid( &snapId );
//
// Associate the original LUN with the snapshot LUN.
//
SnapshotInfo infoSnap;
infoSnap.origLunId = origId;
infoSnap.snapLunId = snapId;
m_vSnapshotInfo.push_back(infoSnap);
m_state = VSS_SS_PREPARING;
m_setId = SnapshotSetId;
}
} catch (HRESULT hre) {
hr = hre;
} catch (std::bad_alloc) {
hr = E_OUTOFMEMORY;
}
if (hr != S_OK) {
DeleteAbortedSnapshots();
m_state = VSS_SS_ABORTED;
}
TraceMsg(L"returning 0x%08x\n", hr);
return hr;
}
//
// IVssProviderCreateSnapshotSet methods
//
STDMETHODIMP
CSampleProvider::EndPrepareSnapshots(
IN VSS_ID SnapshotSetId
)
{
TRACE_FUNCTION();
HRESULT hr = S_OK;
AutoLock lock( m_cs );
try {
switch (m_state) {
case VSS_SS_PREPARING:
if (!IsEqualGUID(SnapshotSetId, m_setId)) {
LogEvent(L"Unexpected snapshot set ID during EndPrepareSnapshots");
throw (HRESULT) VSS_E_PROVIDER_VETO;
} else {
m_state = VSS_SS_PREPARED;
}
break;
default:
LogEvent(L"EndPrepareSnapshots called out of order");
throw (HRESULT) VSS_E_PROVIDER_VETO;
}
} catch (HRESULT hre) {
hr = hre;
} catch (std::bad_alloc()) {
hr = E_OUTOFMEMORY;
}
if (hr != S_OK) {
DeleteAbortedSnapshots();
m_state = VSS_SS_ABORTED;
}
TraceMsg(L"returning 0x%08x\n", hr);
return hr;
}
STDMETHODIMP
CSampleProvider::PreCommitSnapshots(
IN VSS_ID SnapshotSetId
)
{
TRACE_FUNCTION();
HRESULT hr = S_OK;
AutoLock lock( m_cs );
try {
switch (m_state) {
case VSS_SS_PREPARED:
if (!IsEqualGUID(SnapshotSetId, m_setId)) {
LogEvent(L"Unexpected snapshot set ID during PreCommitSnapshots");
throw (HRESULT) VSS_E_PROVIDER_VETO;
} else {
m_state = VSS_SS_PRECOMMITTED;
}
break;
default:
LogEvent(L"PreCommitSnapshots called out of order");
throw (HRESULT) VSS_E_PROVIDER_VETO;
}
} catch (HRESULT hre) {
hr = hre;
} catch (std::bad_alloc) {
hr = E_OUTOFMEMORY;
}
if (hr != S_OK) {
DeleteAbortedSnapshots();
m_state = VSS_SS_ABORTED;
}
TraceMsg(L"returning 0x%08x\n", hr);
return hr;
}
STDMETHODIMP
CSampleProvider::CommitSnapshots(
IN VSS_ID SnapshotSetId
)
{
TRACE_FUNCTION();
HRESULT hr = S_OK;
AutoLock lock( m_cs );
try {
switch (m_state) {
case VSS_SS_PRECOMMITTED:
if (!IsEqualGUID(SnapshotSetId, m_setId)) {
LogEvent(L"Unexpected snapshot set ID during CommitSnapshots");
throw (HRESULT) VSS_E_PROVIDER_VETO;
} else {
//
// Actually perform the snapshot for each LUN in the set
//
SnapshotInfoVector::iterator i;
for (i = m_vSnapshotInfo.begin(); i != m_vSnapshotInfo.end(); ++i) {
//
// Find the image file associated with the
// original id and "commit" the snapshot by
// copying to the snapshot image. This is time
// critical and should take 10s or less. For a
// real implementation it is preferable to start
// the commit for all LUNs in the set at the same
// time and then wait for them all to finish
// instead of processing each one serially.
//
// This implementation is also flawed in that the
// time to commit could easily exceed the 10s
// window if the LUNs are greater than a few MB.
//
// std::wstring origImage;
WCHAR origImage[256];
hr = m_vbus.QueryMountedImage(i->origLunId, origImage, 256);
if (FAILED(hr)) {
LogEvent(L"Unable to find image for LUN during CommitSnapshots");
throw (HRESULT) VSS_E_PROVIDER_VETO;
}
std::wstring snapImage = SnapshotImageFile(i->snapLunId);
BOOL br = CopyFile(origImage, snapImage.c_str(), FALSE);
if (br == FALSE) {
DWORD err = GetLastError();
LogEvent( L"Error copying image file from '%s' to '%s' (%d)",
origImage,
snapImage.c_str(),
err );
throw (HRESULT) VSS_E_PROVIDER_VETO;
}
}
m_state = VSS_SS_COMMITTED;
}
break;
default:
LogEvent(L"CommitSnapshots called out of order");
throw (HRESULT) VSS_E_PROVIDER_VETO;
}
} catch (HRESULT hre) {
hr = hre;
} catch (std::bad_alloc) {
hr = E_OUTOFMEMORY;
}
if (hr != S_OK) {
DeleteAbortedSnapshots();
m_state = VSS_SS_ABORTED;
}
TraceMsg(L"returning 0x%08x\n", hr);
return hr;
}
STDMETHODIMP
CSampleProvider::PostCommitSnapshots(
IN VSS_ID SnapshotSetId,
IN LONG /*lSnapshotsCount*/
)
{
TRACE_FUNCTION();
HRESULT lResult = S_OK;
HKEY hKey;
//If the registry key is added, we will simulate a failure.
lResult = RegOpenKeyEx (HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\VSS\\FailHWSampleProvider", 0, KEY_READ, &hKey);
if(lResult == ERROR_SUCCESS)
{
RegCloseKey (hKey);
return E_FAIL;
}
HRESULT hr = S_OK;
AutoLock lock( m_cs );
try {
switch (m_state) {
case VSS_SS_COMMITTED:
if (!IsEqualGUID(SnapshotSetId, m_setId)) {
LogEvent(L"Unexpected snapshot set ID during PostCommitSnapshots");
throw (HRESULT) VSS_E_PROVIDER_VETO;
} else {
m_state = VSS_SS_CREATED;
}
break;
default:
LogEvent(L"PostCommitSnapshots called out of order");
throw (HRESULT) VSS_E_PROVIDER_VETO;
}
} catch (HRESULT hre) {
hr = hre;
} catch (std::bad_alloc) {
hr = E_OUTOFMEMORY;
}
if (hr != S_OK) {
DeleteAbortedSnapshots();
m_state = VSS_SS_ABORTED;
}
TraceMsg(L"returning 0x%08x\n", hr);
return hr;
}
//
// These two methods are stubs for Windows Server 2003, and should merely return S_OK
//
STDMETHODIMP
CSampleProvider::PreFinalCommitSnapshots(
IN VSS_ID /*SnapshotSetId*/
)
{
TRACE_FUNCTION();
HRESULT hr = S_OK;
TraceMsg(L"returning 0x%08x\n", hr);
return hr;
}
STDMETHODIMP
CSampleProvider::PostFinalCommitSnapshots(
IN VSS_ID /*SnapshotSetId*/
)
{
TRACE_FUNCTION();
HRESULT hr = S_OK;
TraceMsg(L"returning 0x%08x\n", hr);
return hr;
}
STDMETHODIMP
CSampleProvider::AbortSnapshots(
IN VSS_ID /*SnapshotSetId*/
)
{
TRACE_FUNCTION();
HRESULT hr = S_OK;
AutoLock lock( m_cs );
try {
switch (m_state) {
case VSS_SS_CREATED:
//
// Aborts are ignored after create
//
m_state = VSS_SS_CREATED;
break;
default:
DeleteAbortedSnapshots();
m_state = VSS_SS_ABORTED;
break;
}
} catch (HRESULT hre) {
hr = hre;
} catch (std::bad_alloc) {
hr = E_OUTOFMEMORY;
}
if (hr != S_OK) {
m_state = VSS_SS_ABORTED;
}
TraceMsg(L"returning 0x%08x\n", hr);
return hr;
}
//
// IVssProviderNotifications methods
//
STDMETHODIMP
CSampleProvider::OnLoad(
__RPC__in_opt IUnknown *
)
{
TRACE_FUNCTION();
return S_OK;
}
STDMETHODIMP
CSampleProvider::OnUnload(
IN BOOL /* bForceUnload */
)
{
TRACE_FUNCTION();
HRESULT hr = S_OK;
AutoLock lock( m_cs );
try {
switch (m_state) {
case VSS_SS_UNKNOWN:
case VSS_SS_ABORTED:
case VSS_SS_CREATED:
break;
default:
//
// Treat unloading during snapshot creation as an abort
//
DeleteAbortedSnapshots();
break;
}
} catch (HRESULT hre) {
hr = hre;
} catch (std::bad_alloc) {
hr = E_OUTOFMEMORY;
}
m_state = VSS_SS_UNKNOWN;
TraceMsg(L"returning 0x%08x\n", hr);
return hr;
}
////////////////////////////////////////////////////////////
// Begin IVssHardwareSnapshotProviderEx Methods
// Informs VSS of provider supported features
STDMETHODIMP
CSampleProvider::GetProviderCapabilities(
__RPC__out ULONGLONG *pllOriginalCapabilityMask
)
{
TRACE_FUNCTION();
HRESULT hr = E_NOTIMPL;
UNREFERENCED_PARAMETER(pllOriginalCapabilityMask);
return hr;
}
void LogOnLunStateChangeMessage(__in VDS_LUN_INFORMATION *pSnapshotLun, __in DWORD dwFlags)
{
if (dwFlags & VSS_ONLUNSTATECHANGE_NOTIFY_READ_WRITE)
LogEvent(L"\nNotify Read/Write : '%S'",pSnapshotLun->m_szSerialNumber);
if (dwFlags & VSS_ONLUNSTATECHANGE_NOTIFY_LUN_PRE_RECOVERY)
LogEvent(L"\nNotify pre-recovery : '%S'",pSnapshotLun->m_szSerialNumber);
if (dwFlags & VSS_ONLUNSTATECHANGE_NOTIFY_LUN_POST_RECOVERY)
LogEvent(L"\nNotify post-recovery : '%S'",pSnapshotLun->m_szSerialNumber);
}
// Notifies provider about LUN state change during break/fast recovery
STDMETHODIMP
CSampleProvider::OnLunStateChange (
__RPC__in_ecount_full_opt(dwCount) VDS_LUN_INFORMATION *pSnapshotLuns,
__RPC__in_ecount_full_opt(dwCount) VDS_LUN_INFORMATION *pOriginalLuns,
DWORD dwCount,
DWORD dwFlags
)
{
TRACE_FUNCTION();
HRESULT hr = S_OK;
UNREFERENCED_PARAMETER(pOriginalLuns);
if( pSnapshotLuns == NULL )
{
//if pSnapshotLuns is not valid, throw exception
hr = E_POINTER;
TraceMsg(L"pSnapshotLuns is NULL, returning 0x%08x\n", hr);
return hr;
}
try
{
if (VSS_ONLUNSTATECHANGE_DO_MASK_LUNS & dwFlags)
{
for (DWORD i = 0; i < dwCount; i++)
{
VDS_LUN_INFORMATION *pInfo = &pSnapshotLuns[i];
//
// Form GUID
//
GUID snapId;
hr = AnsiToGuid(pInfo->m_szSerialNumber, snapId);
switch (hr)
{
case S_OK:
break;
case E_OUTOFMEMORY:
throw hr;
default:
//
// Any other error indicates we were passed a bad source LUN,
// so log it and return VSS_E_PROVIDER_VETO
//
LogEvent(
L"OnLunStateChange called with invalid LUN SerialNumber = '%S'",
pInfo->m_szSerialNumber
);
throw (HRESULT) VSS_E_PROVIDER_VETO;
}
//
// Remove drive given by GUID
//
hr = m_vbus.RemoveDrive(snapId, false);
if (FAILED(hr))
{
LogEvent(
L"OnLunStateChange: RemoveDrive for '%S' failed with 0x%08x",
pInfo->m_szSerialNumber,
hr
);
throw (HRESULT) VSS_E_PROVIDER_VETO;
}
}
}
else
{
for (DWORD i = 0; i < dwCount; i++)
LogOnLunStateChangeMessage(&pSnapshotLuns[i], dwFlags);
}
}
catch (HRESULT hre)
{
hr = hre;
}
catch (std::bad_alloc)
{
hr = E_OUTOFMEMORY;
}
return hr;
}
// Resync LUNs (used during Fast Recovery LUN resync)
STDMETHODIMP
#pragma warning(suppress: 6101)
CSampleProvider::ResyncLuns (
__RPC__in_ecount_full_opt(dwCount) VDS_LUN_INFORMATION *pSourceLuns,
__RPC__in_ecount_full_opt(dwCount) VDS_LUN_INFORMATION *pTargetLuns,
DWORD dwCount,
__RPC__deref_out_opt IVssAsync ** ppAsync
)
{
TRACE_FUNCTION();
HRESULT hr = S_OK;
unsigned int i;
for(i=0; i < dwCount; i++)
{
GUID snapId = GUID_NULL;
GUID targetDiskId = GUID_NULL;
try {
if( pSourceLuns == NULL || pTargetLuns == NULL)
{
//if pSourceLuns or pTargetLuns is not valid, throw exception
throw E_POINTER;
}
hr = AnsiToGuid(pSourceLuns[i].m_szSerialNumber, snapId);
if(SUCCEEDED(hr))
{
hr = AnsiToGuid(pTargetLuns[i].m_szSerialNumber, targetDiskId);
}
switch (hr) {
case S_OK:
break;
case E_OUTOFMEMORY:
//
// Allow out of memory errors to float up
//
throw hr;
default:
//
// Any other error indicates we were passed a bad source LUN,
// so log it and return VSS_E_PROVIDER_VETO
//
LogEvent(L"ResyncLuns called with invalid source, target LUN m_szSerialNumber ('%S'), ('%S')",
pSourceLuns[i].m_szSerialNumber, pTargetLuns[i].m_szSerialNumber);
throw (HRESULT) VSS_E_PROVIDER_VETO;
}
std::wstring fileName = SnapshotImageFile(snapId);
} catch (HRESULT hre) {
hr = hre;
if(FAILED(hr))
{
TraceMsg(L"Exception is caught with hr: %x \n", hr);
}
break; //TODO: best effort or fail fast ? fail fast for now (2/13/2008)
} catch (std::bad_alloc) {
hr = E_OUTOFMEMORY;
if(FAILED(hr))
{
TraceMsg(L"Exception is caught with hr: %x ", hr);
}
break;
}
//Translate the snapshot ID GUID to the path fo the image file.
std::wstring sourceLunPath = SnapshotImageFile(snapId);
//Resync the target virtual disk using the image file we just got
hr = m_vbus.ReSync(targetDiskId, sourceLunPath.c_str());
//Now, fake an async operation. From the client(VSS)'s side of view, this task just started, the client will wait on the **ppAsync object and get the
//status of hr from there.
*ppAsync = CVssAsync::CreateInstanceAndStartJob(hr);
}
//Well, we always return S_OK here, because to the client, this operation is barely beginning, the client will wait on the **ppAsync object and get the
//real hr value there.
return S_OK;
}
// Reuse recyclable LUNs (during Delete)
STDMETHODIMP
CSampleProvider::OnReuseLuns(
__RPC__in_ecount_full_opt(dwCount) VDS_LUN_INFORMATION *pSnapshotLuns,
__RPC__in_ecount_full_opt(dwCount) VDS_LUN_INFORMATION *pOriginalLuns,
DWORD dwCount
)
{
TRACE_FUNCTION();
HRESULT hr = E_NOTIMPL;
UNREFERENCED_PARAMETER(pSnapshotLuns);
UNREFERENCED_PARAMETER(pOriginalLuns);
UNREFERENCED_PARAMETER(dwCount);
return hr;
}
// End IVssHardwareSnapshotProviderEx Methods
////////////////////////////////////////////////////////////