|
|
/*++
Copyright (c) 1999 Microsoft Corporation
Abstract:
@doc @module VsWrtImp.cpp | Implementation of Writer @end
Author:
Adi Oltean [aoltean] 02/02/2000
TBD: Add comments.
Remove the C++ exception-handler related code.
Revision History:
Name Date Comments aoltean 02/02/2000 Created brianb 03/25/2000 modified to include additional events brianb 03/28/2000 modified to include timeouts and sync for OnPrepareBackup brianb 03/28/2000 renamed to vswrtimp.cpp to separate internal state from external interface brianb 04/19/2000 added security checks brianb 05/03/2000 new security model brianb 05/09/2000 fix problem with autolocks
--*/
#include <stdafx.hxx>
#include <eventsys.h>
#include "vs_inc.hxx"
#include "vs_sec.hxx"
#include "vs_idl.hxx"
#include "comadmin.hxx"
#include "vsevent.h"
#include "vswriter.h"
#include "vsbackup.h"
#include "vssmsg.h"
#include "vswrtimp.h"
// xml support
#include "vs_wmxml.hxx"
#include "vs_cmxml.hxx"
#include "rpcdce.h"
////////////////////////////////////////////////////////////////////////
// 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 "WRTWRTIC"
//
////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Constants
const WCHAR g_wszPublisherID[] = L"VSS Publisher";
// event names
const WCHAR g_wszRequestInfoMethodName[] = L"RequestWriterInfo"; const WCHAR g_wszPrepareForBackupMethodName[] = L"PrepareForBackup"; const WCHAR g_wszBackupCompleteMethodName[] = L"BackupComplete"; const WCHAR g_wszPostRestoreMethodName[] = L"PostRestore"; const WCHAR g_wszPrepareForSnapshotMethodName[] = L"PrepareForSnapshot"; const WCHAR g_wszFreezeMethodName[] = L"Freeze"; const WCHAR g_wszThawMethodName[] = L"Thaw"; const WCHAR g_wszAbortMethodName[] = L"Abort";
// List of received volumes is in the following formatDate
// <Volume Name 1>;<Volume Name 2>: ... :<Volume Name N>
const WCHAR VSS_VOLUME_DELIMITERS[] = L";";
// class describing state machine for writer
class CVssWriterImplStateMachine { private: // disable default and copy constructors
CVssWriterImplStateMachine();
public: CVssWriterImplStateMachine ( VSS_WRITER_STATE previousState, VSS_WRITER_STATE successfulExitState, VSS_WRITER_STATE failureExitState, bool bBeginningState, bool bSuccessiveState, bool bResetSequenceOnLeave ) : m_previousState(previousState), m_successfulExitState(successfulExitState), m_failureExitState(failureExitState), m_bBeginningState(bBeginningState), m_bSuccessiveState(bSuccessiveState), m_bResetSequenceOnLeave(bResetSequenceOnLeave) { }
// previous state writer must be in to enter the current
// state unless this is the first state of a sequence
VSS_WRITER_STATE m_previousState;
// state we are in if the operation is successful
VSS_WRITER_STATE m_successfulExitState;
// state we are in if the operation is uncessful
VSS_WRITER_STATE m_failureExitState;
// is this state a possible state for the beginning of the sequence
bool m_bBeginningState;
// is this a possible non-beginning state in a sequence
bool m_bSuccessiveState;
// should the sequence be reset on successful exit of the state
bool m_bResetSequenceOnLeave; };
// definition of state machine
static CVssWriterImplStateMachine s_rgWriterStates[] = { // OnPrepareBackup
CVssWriterImplStateMachine ( VSS_WS_STABLE, // previous state
VSS_WS_STABLE, // next state if successful
VSS_WS_FAILED_AT_PREPARE_BACKUP, // next state if failure
true, // this can be a first state
false, // this must be a first state
false // do not reset sequence on leaving this state
),
// OnPrepareSnapshot
CVssWriterImplStateMachine ( VSS_WS_STABLE, // previous state
VSS_WS_WAITING_FOR_FREEZE, // next state if successful
VSS_WS_FAILED_AT_PREPARE_SNAPSHOT, // next state if failure
true, // this can be a first state
true, // this can be a follow on state
false // do not reset sequence on leaving this state
),
// OnFreeze
CVssWriterImplStateMachine ( VSS_WS_WAITING_FOR_FREEZE, // previous state
VSS_WS_WAITING_FOR_THAW, // next state if successful
VSS_WS_FAILED_AT_FREEZE, // next state if unsuccessful
false, // this may not be a first state
true, // this must be a follow on state
false // do not reset sequence on leaving this state
),
// OnThaw
CVssWriterImplStateMachine ( VSS_WS_WAITING_FOR_THAW, // previous state
VSS_WS_WAITING_FOR_BACKUP_COMPLETE, // next state if successful
VSS_WS_FAILED_AT_THAW, // next state if unsuccessful
false, // this may not be a first state
true, // this must be a follow on state
true // reset sequence on leaving this state
)
};
// state ids
static const unsigned s_ivwsmPrepareForBackup = 0; static const unsigned s_ivwsmPrepareForSnapshot = 1; static const unsigned s_ivwsmFreeze = 2; static const unsigned s_ivwsmThaw = 3;
/////////////////////////////////////////////////////////////////////////////
// CVssWriterImpl constructors/destructors
// constructor
CVssWriterImpl::CVssWriterImpl(): m_WriterID(GUID_NULL), m_InstanceID(GUID_NULL), m_usage(VSS_UT_UNDEFINED), m_source(VSS_ST_UNDEFINED), m_nLevel(VSS_APP_FRONT_END), m_dwTimeoutFreeze(VSS_TIMEOUT_FREEZE), m_CurrentSnapshotSetId(GUID_NULL), m_bSequenceInProgress(false), m_nVolumesCount(0), m_ppwszVolumesArray(NULL), m_pwszLocalVolumeNameList(NULL), m_dwEventMask(0), m_wszWriterName(NULL), m_state(VSS_WS_STABLE), m_hevtTimerThread(NULL), m_hmtxTimerThread(NULL), m_hThreadTimerThread(NULL), m_bLocked(false), m_bLockCreated(false), m_command(VSS_TC_UNDEFINED), m_iPreviousSnapshots(0), m_cbstrSubscriptionId(0), m_bOnAbortPermitted(false), m_bFailedAtIdentify(false), m_hrWriterFailure(S_OK) { for(UINT i = 0; i < MAX_PREVIOUS_SNAPSHOTS; i++) { m_rgidPreviousSnapshots[i] = GUID_NULL; m_rgstatePreviousSnapshots[i] = VSS_WS_UNKNOWN; m_rghrWriterFailurePreviousSnapshots[i] = E_UNEXPECTED; } }
// destructor
CVssWriterImpl::~CVssWriterImpl() { // terminate timer thread if it is still running
if (m_bLockCreated) { Lock(); TerminateTimerThread(); Unlock(); }
// delete volume array
delete[] m_ppwszVolumesArray;
// delete volume list string
::VssFreeString(m_pwszLocalVolumeNameList);
// delete writer name
free(m_wszWriterName);
if (m_hevtTimerThread) CloseHandle(m_hevtTimerThread);
if (m_hmtxTimerThread) CloseHandle(m_hmtxTimerThread); }
// create an event
void CVssWriterImpl::SetupEvent(IN HANDLE *phevt) { CVssFunctionTracer ft(VSSDBG_WRITER, L"CVssWriterImple::SetupEvent");
BS_ASSERT(phevt); // setup events as enabled and manual reset
*phevt = CreateEvent(NULL, TRUE, TRUE, NULL); if (*phevt == NULL) ft.Throw ( VSSDBG_WRITER, E_OUTOFMEMORY, L"Failure to create event object due to error %d.", GetLastError() ); }
const WCHAR SETUP_KEY[] = L"SYSTEM\\Setup";
const WCHAR SETUP_INPROGRESS_REG[] = L"SystemSetupInProgress";
const WCHAR UPGRADE_INPROGRESS_REG[] = L"UpgradeInProgress";
// initialize writer object
void CVssWriterImpl::Initialize ( IN VSS_ID WriterID, // writer class id
IN LPCWSTR wszWriterName, // friendly name of writer
IN VSS_USAGE_TYPE usage, // usage type
IN VSS_SOURCE_TYPE source, // data source type
IN VSS_APPLICATION_LEVEL nLevel, // which freeze event this writer handles
IN DWORD dwTimeoutFreeze // timeout between freeze and thaw
) { CVssFunctionTracer ft(VSSDBG_WRITER, L"CVssWriterImpl::Initialize");
{ // determine if we are in setup. If we are then reject the
// initialize call and log an error in the application log
CRegKey cRegKeySetup; DWORD dwRes; bool fInSetup = false;
dwRes = cRegKeySetup.Create(HKEY_LOCAL_MACHINE, SETUP_KEY); if (dwRes == ERROR_SUCCESS) { DWORD dwValue; dwRes = cRegKeySetup.QueryValue(dwValue, SETUP_INPROGRESS_REG); if (dwRes == ERROR_SUCCESS && dwValue > 0) fInSetup = true; dwRes = cRegKeySetup.QueryValue(dwValue, UPGRADE_INPROGRESS_REG); if (dwRes == ERROR_SUCCESS && dwValue > 0) fInSetup = true; }
if (fInSetup) ft.Throw(VSSDBG_WRITER, VSS_E_BAD_STATE, L"Calling Initialize during setup"); }
// Testing arguments validity
if (wszWriterName == NULL) ft.Throw ( VSSDBG_WRITER, E_INVALIDARG, L"NULL writer name" ); switch(nLevel) { case VSS_APP_SYSTEM: case VSS_APP_BACK_END: case VSS_APP_FRONT_END: break; default: ft.Throw ( VSSDBG_WRITER, E_INVALIDARG, L"Invalid app level %d", nLevel ); }
m_cs.Init(); // Warning - may throw NT exceptions...
m_bLockCreated = true;
// save writer class id
m_WriterID = WriterID;
// save writer name
m_wszWriterName = _wcsdup(wszWriterName); if (m_wszWriterName == NULL) ft.Throw ( VSSDBG_WRITER, E_OUTOFMEMORY, L"Cannot allocate writer name" );
// save usage type
m_usage = usage;
// save source type
m_source = source;
// create guid for this instance
ft.hr = ::CoCreateGuid(&m_InstanceID); ft.CheckForError(VSSDBG_WRITER, L"CoCreateGuid"); ft.Trace ( VSSDBG_WRITER, L" InstanceId for Writer %s is" WSTR_GUID_FMT, m_wszWriterName, GUID_PRINTF_ARG(m_InstanceID) );
// save app level
m_nLevel = nLevel;
// save timeout
m_dwTimeoutFreeze = dwTimeoutFreeze;
// setup thread mutex
m_hmtxTimerThread = CreateMutex(NULL, FALSE, NULL); if (m_hmtxTimerThread == NULL) ft.Throw ( VSSDBG_WRITER, E_OUTOFMEMORY, L"Failure to create mutex object due to error %d.", GetLastError() );
// setup event used to control the timer thread
SetupEvent(&m_hevtTimerThread); }
// start a sequence
// critical section (m_cs) must be locked upone entry to this routine
void CVssWriterImpl::BeginSequence ( IN CVssID &SnapshotSetId ) { CVssFunctionTracer ft(VSSDBG_WRITER, L"CVssWriterImpl::BeginSequence");
AssertLocked();
// terminate timer thread if it is still operating
TerminateTimerThread();
// setup current snapshot set id
m_CurrentSnapshotSetId = SnapshotSetId;
// indicate that sequence is in progress
m_bSequenceInProgress = true; BS_ASSERT(m_bOnAbortPermitted == false);
// current state is STABLE (i.e., beginning of sequence, clears
// any completion state we were in)
m_state = VSS_WS_STABLE;
// indicate that there is no failure
m_hrWriterFailure = S_OK; }
INT CVssWriterImpl::SearchForPreviousSequence( IN VSS_ID& idSnapshotSet ) { for(INT iSeqIndex = 0; iSeqIndex < MAX_PREVIOUS_SNAPSHOTS; iSeqIndex++) { if (idSnapshotSet == m_rgidPreviousSnapshots[iSeqIndex]) return iSeqIndex; } // end for
return INVALID_SEQUENCE_INDEX; }
// Reset the sequence-related data members
// critical section must be locked prior to entering this state
void CVssWriterImpl::ResetSequence(bool bCalledFromTimerThread) { CVssFunctionTracer ft(VSSDBG_WRITER, L"CVssWriterImpl::ResetSequence");
AssertLocked();
if (m_bSequenceInProgress) { // We need to test to not add the same SSID twice - bug 228622.
if (SearchForPreviousSequence(m_CurrentSnapshotSetId) == INVALID_SEQUENCE_INDEX) { BS_ASSERT(m_iPreviousSnapshots < MAX_PREVIOUS_SNAPSHOTS); m_rgidPreviousSnapshots[m_iPreviousSnapshots] = m_CurrentSnapshotSetId; m_rgstatePreviousSnapshots[m_iPreviousSnapshots] = m_state; m_rghrWriterFailurePreviousSnapshots[m_iPreviousSnapshots] = m_hrWriterFailure; m_iPreviousSnapshots = (m_iPreviousSnapshots + 1) % MAX_PREVIOUS_SNAPSHOTS; } else BS_ASSERT(false); // The same SSID was already added - programming error.
}
// Reset the sequence-related data members
m_bSequenceInProgress = false; m_bOnAbortPermitted = false;
// reset writer callback function
m_pWriterCallback = NULL;
// reset volumes array
m_nVolumesCount = 0; delete[] m_ppwszVolumesArray; m_ppwszVolumesArray = NULL;
::VssFreeString(m_pwszLocalVolumeNameList); m_CurrentSnapshotSetId = GUID_NULL;
// if bCalledFromTimerThread is true, this means that the timer
// thread is causing the sequence to be reset. We are in the timer
// thread already and it will terminate upon completion of this call
// so we shouldn't try causing it to terminate again.
if (!bCalledFromTimerThread) TerminateTimerThread();
}
// indicate why the writer failed
HRESULT CVssWriterImpl::SetWriterFailure(HRESULT hr) { if (hr != VSS_E_WRITERERROR_TIMEOUT && hr != VSS_E_WRITERERROR_RETRYABLE && hr != VSS_E_WRITERERROR_NONRETRYABLE && hr != VSS_E_WRITERERROR_OUTOFRESOURCES && hr != VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT) return E_INVALIDARG;
m_hrWriterFailure = hr; return S_OK; }
// determine if path specified is on one of the volumes that is snapshot
bool CVssWriterImpl::IsPathAffected ( IN LPCWSTR wszPath ) const { // Test the status
if (!m_bSequenceInProgress) return false;
// check for empty volume count
if (m_nVolumesCount == 0) return false;
// Get the volume mount point
WCHAR wszVolumeMP[MAX_PATH]; BOOL bRes = ::GetVolumePathNameW(wszPath, wszVolumeMP, MAX_PATH); if (!bRes) return false;
// Get the volume name
WCHAR wszVolumeName[MAX_PATH]; bRes = ::GetVolumeNameForVolumeMountPointW(wszVolumeMP, wszVolumeName, MAX_PATH); if (!bRes) return false;
// Search to see if that volume is within snapshotted volumes
for (int nIndex = 0; nIndex < m_nVolumesCount; nIndex++) { BS_ASSERT(m_ppwszVolumesArray[nIndex]); if (::wcscmp(wszVolumeName, m_ppwszVolumesArray[nIndex]) == 0) return true; }
return false; }
// obtain IVssWriterCallback from IDispatch pointer
// caller is responsible for releasing interface that is returned
void CVssWriterImpl::GetCallback ( IN IDispatch *pWriterCallback, OUT IVssWriterCallback **ppCallback ) { CVssFunctionTracer ft(VSSDBG_WRITER, L"CVssWriterImpl::GetCallback");
// check that pointer is supplied
BS_ASSERT(pWriterCallback != NULL);
ft.hr = CoSetProxyBlanket ( pWriterCallback, RPC_C_AUTHN_DEFAULT, RPC_C_AUTHZ_DEFAULT, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_NONE );
// note E_NOINTERFACE means that the pWriterCallback is a in-proc callback
// and there is no proxy
if (FAILED(ft.hr) && ft.hr != E_NOINTERFACE) { if (m_hrWriterFailure == S_OK) m_hrWriterFailure = VSS_E_WRITERERROR_NONRETRYABLE;
ft.LogError(VSS_ERROR_BLANKET_FAILED, VSSDBG_WRITER << ft.hr); ft.Throw ( VSSDBG_WRITER, E_UNEXPECTED, L"Call to CoSetProxyBlanket failed. hr = 0x%08lx", ft.hr ); }
// try QueryInterface for IVssWriterCallback interface
ft.hr = pWriterCallback->SafeQI(IVssWriterCallback, ppCallback); if (FAILED(ft.hr)) { if (m_hrWriterFailure == S_OK) m_hrWriterFailure = VSS_E_WRITERERROR_NONRETRYABLE;
ft.LogError(VSS_ERROR_QI_IVSSWRITERCALLBACK, VSSDBG_WRITER << ft.hr); ft.Throw ( VSSDBG_WRITER, E_UNEXPECTED, L"Error querying for IVssWriterCallback interface. hr = 0x%08lx", ft.hr ); } }
// create basic writer metadata for OnIdentify method
CVssCreateWriterMetadata *CVssWriterImpl::CreateBasicWriterMetadata() { CVssFunctionTracer ft(VSSDBG_WRITER, L"CVssWriterImpl::CreateBasicWriterMetadata");
// create object supporting IVssCreateMetadata interface
CVssCreateWriterMetadata *pMetadata = new CVssCreateWriterMetadata; if (pMetadata == NULL) { m_hrWriterFailure = VSS_E_WRITERERROR_OUTOFRESOURCES; ft.Throw ( VSSDBG_WRITER, E_OUTOFMEMORY, L"Cannot create CVssCreateWriterMetadata due to allocation failure." ); }
// call initialize to create IDENTIFICATION section
ft.hr = pMetadata->Initialize ( m_InstanceID, m_WriterID, m_wszWriterName, m_usage, m_source );
if (ft.HrFailed()) { m_hrWriterFailure = VSS_E_WRITERERROR_OUTOFRESOURCES; delete pMetadata; ft.Throw ( VSSDBG_WRITER, ft.hr, L"CVssCreateWriterMetadata::Initialize failed. hr = 0x%08lx", ft.hr ); }
// return object
return pMetadata; }
static LPCWSTR x_wszElementRoot = L"root"; static LPCWSTR x_wszElementWriterComponents = L"WRITER_COMPONENTS";
// get writer components for OnPrepareBackup, OnBackupComplete, and OnPostRestore
// methods
void CVssWriterImpl::InternalGetWriterComponents ( IN IVssWriterCallback *pCallback, OUT IVssWriterComponentsInt **ppWriter, bool bWriteable ) { CVssFunctionTracer ft(VSSDBG_WRITER, L"CVssWriterImpl::InternalGetWriterComponents");
BS_ASSERT(pCallback); BS_ASSERT(ppWriter);
*ppWriter = NULL;
// call GetContent callback method on the backup application
CComBSTR bstrId(m_InstanceID); if (!bstrId) { m_hrWriterFailure = VSS_E_WRITERERROR_OUTOFRESOURCES; ft.Throw ( VSSDBG_WRITER, E_OUTOFMEMORY, L"Cannot allocate instance Id string" ); }
try { BOOL bPartialFileSupport;
ft.hr = pCallback->GetBackupState ( &m_bComponentsSelected, &m_bBootableSystemStateBackup, &m_backupType, &bPartialFileSupport, NULL ); } catch(...) { ft.Trace(VSSDBG_WRITER, L"IVssWriterCallback::GetBackupState threw an exception."); throw; }
if (ft.HrFailed()) { m_hrWriterFailure = VSS_E_WRITERERROR_RETRYABLE; ft.Throw ( VSSDBG_WRITER, ft.hr, L"IVssWriterCallback::GetBackupState failed. hr = 0x%08lx", ft.hr ); }
CComBSTR bstrWriterComponentsDoc; try { ft.hr = pCallback->GetContent(bstrId, &bstrWriterComponentsDoc); } catch(...) { m_hrWriterFailure = VSS_E_WRITERERROR_RETRYABLE; ft.Trace(VSSDBG_WRITER, L"IVssWriterCallback::GetContent threw an exception."); throw; }
if (ft.HrFailed()) { m_hrWriterFailure = VSS_E_WRITERERROR_RETRYABLE; ft.Throw ( VSSDBG_WRITER, ft.hr, L"Cannot get WRITER_COMPONENTS document. hr = 0x%08lx", ft.hr ); }
if (ft.hr == S_FALSE) { // reset status code
ft.hr = S_OK;
// allocate null writer components object
*ppWriter = (IVssWriterComponentsInt *) new CVssNULLWriterComponents ( m_InstanceID, m_WriterID );
if (*ppWriter == NULL) { // indicate that the writer failed due to an out of resources condition
m_hrWriterFailure = VSS_E_WRITERERROR_OUTOFRESOURCES; ft.Throw (VSSDBG_WRITER, E_OUTOFMEMORY, L"Can't allocate CVssWriterComponents object"); }
(*ppWriter)->AddRef(); } else { CXMLDocument doc; if (!doc.LoadFromXML(bstrWriterComponentsDoc) || !doc.FindElement(x_wszElementRoot, true)) { m_hrWriterFailure = VSS_E_WRITERERROR_NONRETRYABLE; ft.LogError(VSS_ERROR_WRITER_COMPONENTS_CORRUPT, VSSDBG_WRITER); ft.Throw ( VSSDBG_WRITER, VSS_E_CORRUPT_XML_DOCUMENT, L"Internally transferred WRITER_COMPONENTS document is invalid" ); }
doc.SetToplevel();
*ppWriter = (IVssWriterComponentsInt *) new CVssWriterComponents ( doc.GetCurrentNode(), doc.GetInterface(), bWriteable );
if (*ppWriter == NULL) { m_hrWriterFailure = VSS_E_WRITERERROR_OUTOFRESOURCES; ft.Throw (VSSDBG_WRITER, E_OUTOFMEMORY, L"Can't allocate CVssWriterComponents object"); }
(*ppWriter)->AddRef(); ft.hr = (*ppWriter)->Initialize(true); if (ft.HrFailed()) { m_hrWriterFailure = VSS_E_WRITERERROR_OUTOFRESOURCES; (*ppWriter)->Release(); *ppWriter = NULL; ft.Throw ( VSSDBG_WRITER, ft.hr, L"Failed to initialize WRITER_COMPONENTS document. hr = 0x%08lx", ft.hr ); } } }
// called when entering a state to verify whether this state can be
// validly entered and generate appropriate error if not.
// this routine always obtains the critical section. If this routine
// is called then LeaveState must also be called in order to free the
// critical section.
bool CVssWriterImpl::EnterState ( IN const CVssWriterImplStateMachine &vwsm, IN BSTR bstrSnapshotSetId ) throw(HRESULT) { CVssFunctionTracer ft(VSSDBG_WRITER, L"CVssWriterImpl::EnterState");
CVssID id;
// obtain lock just in case next call throws
// no matter how this routine exits, the critical section must be locked
Lock();
// initialize id to snapshot set id
id.Initialize(ft, (LPWSTR)bstrSnapshotSetId, E_OUTOFMEMORY);
// If failed on Identify then we cannot enter in a new state until
// subsequent Identify calls will succeed
if (m_bFailedAtIdentify) return false;
if (!m_bSequenceInProgress) { if (!vwsm.m_bBeginningState) // not a beginning state. Sequence must have been
// interrupted.
return false; else { // BUG 219757 - PrepareForSnapshot, etc. cannot be
// called for the same Snapshot Set if PrepareForBackup failed
// Also we assume here that each new sequence have an UNIQUE SSID.
// This check is needed since the PrepareForBackup phase is optional
// and can be skipped sometimes. Therefore we need to distinguish between
// the case when PrepareForBackup was skipped and the case when PrepareForBackup
// was called and failed.
// Search for a previous sequence with the same Snapshot Set ID.
// If found (this means that a PrepareForBackup was called),
// then reject the call.
if (SearchForPreviousSequence(id) != INVALID_SEQUENCE_INDEX) return false; // it is a beginning state, start the sequence
BeginSequence(id); return true; } } else { if (vwsm.m_bSuccessiveState) { // it is a valid non-beginning state in the sequence
if (id != m_CurrentSnapshotSetId) { // if snapshot set id doesn't match and this is not
// a beginning state, then the event must be ignored.
// We must have aborted the sequence it references.
if (!vwsm.m_bBeginningState) return false; } else { // make sure current state matches previous state
// of state we are about to enter
return m_state == vwsm.m_previousState; } } }
// We are trying to start a new sequence.
// This means that the previous sequence was not properly
// terminated. Abort the previous sequence and then
// start a new one.
ft.Trace(VSSDBG_WRITER, L"*** Warning ***: Writer %s with ID "WSTR_GUID_FMT L"attempts to reset the previous sequence with Snapshot Set ID "WSTR_GUID_FMT L". Current state = %d", m_wszWriterName, GUID_PRINTF_ARG(m_InstanceID), GUID_PRINTF_ARG(m_CurrentSnapshotSetId), (INT)m_state); DoAbort(false); BeginSequence(id); return true; }
// do abort on failure of the sequence
// critical section must be locked prior to entering this state
void CVssWriterImpl::DoAbort ( IN bool bCalledFromTimerThread ) { CVssFunctionTracer ft(VSSDBG_WRITER, L"CVssWriterImpl::DoAbort");
AssertLocked(); // do nothing if in a sequence
if (!m_bSequenceInProgress) return;
// catch any exceptions so that we properly reset the
// sequence
BS_ASSERT(m_pWriter); try { // call writer's abort function (depending on the state)
switch(m_state) { default: BS_ASSERT(m_bOnAbortPermitted == false); break; case VSS_WS_STABLE: // This is possible since you may get an Abort
// in (or after) PrepareForBackup (BUG # 301686)
BS_ASSERT(m_bOnAbortPermitted == true); break; case VSS_WS_WAITING_FOR_FREEZE: case VSS_WS_WAITING_FOR_THAW: case VSS_WS_WAITING_FOR_BACKUP_COMPLETE: case VSS_WS_FAILED_AT_PREPARE_BACKUP: case VSS_WS_FAILED_AT_PREPARE_SNAPSHOT: case VSS_WS_FAILED_AT_FREEZE: // Fixing bug 225936
if (m_bOnAbortPermitted) m_pWriter->OnAbort(); else ft.Trace(VSSDBG_WRITER, L"Abort skipped in state %d", m_state); m_bOnAbortPermitted = false; break; } } VSS_STANDARD_CATCH(ft)
if (ft.HrFailed()) ft.Trace ( VSSDBG_WRITER, L"OnAbort failed. hr = 0x%08lx", ft.hr );
// set appropriate failure state
switch(m_state) { default: m_state = VSS_WS_UNKNOWN; BS_ASSERT(false); break;
// This state is not really kept in the m_state member
case VSS_WS_FAILED_AT_IDENTIFY: BS_ASSERT(false); break;
case VSS_WS_FAILED_AT_PREPARE_BACKUP: case VSS_WS_FAILED_AT_PREPARE_SNAPSHOT: case VSS_WS_FAILED_AT_FREEZE: case VSS_WS_FAILED_AT_THAW: // don't change state if already in a failure state
break;
case VSS_WS_STABLE: // if current state is STABLE then it means
// we were in PrepareBackup
m_state = VSS_WS_FAILED_AT_PREPARE_BACKUP; break;
case VSS_WS_WAITING_FOR_FREEZE: // if we were waiting for freeze then we failed
// between PrepareSync and Freeze
m_state = VSS_WS_FAILED_AT_PREPARE_SNAPSHOT; break;
case VSS_WS_WAITING_FOR_THAW: // if we were waiting for thaw then we failed
// between freeze and thaw
m_state = VSS_WS_FAILED_AT_FREEZE; break;
case VSS_WS_WAITING_FOR_BACKUP_COMPLETE: // if we were waiting for completion then
// we failed after thaw.
m_state = VSS_WS_FAILED_AT_THAW; break; }
if (bCalledFromTimerThread && m_hrWriterFailure == S_OK) m_hrWriterFailure = VSS_E_WRITERERROR_TIMEOUT;
// reset sequence
ResetSequence(bCalledFromTimerThread); }
// exit a state. This routine must be called with the critical
// section acquired. For a state, EnterState is called first, then work is
// done, then LeaveState is called. This routine will set the state upon
// exit and possibly reset the snapshot sequence if we are at the end of the
// sequence or the sequence is aborted.
void CVssWriterImpl::LeaveState ( IN const CVssWriterImplStateMachine &vwsm, // current state
IN bool bSucceeded // did operation succeed
) { AssertLocked(); // don't change state or call abort if we are not in a sequence
if (m_bSequenceInProgress) { m_state = bSucceeded ? vwsm.m_successfulExitState : vwsm.m_failureExitState;
// call abort on failure when we are not in the exit state
if (!bSucceeded && !vwsm.m_bResetSequenceOnLeave) DoAbort(false); else if (vwsm.m_bResetSequenceOnLeave) // if sequence ends at this state (THAW) then
// reset variables
ResetSequence(false); }
Unlock(); }
// arguments to timer function
class CVssTimerArgs { private: CVssTimerArgs();
public: CVssTimerArgs(CVssWriterImpl *pWriter, VSS_ID id) : m_snapshotSetId(id), m_pWriter(pWriter) { }
// snapshot set that we are monitoring
VSS_ID m_snapshotSetId;
// pointer to writer
CVssWriterImpl *m_pWriter; };
// timer thread startup routine
DWORD CVssWriterImpl::StartTimerThread(void *pv) { CVssFunctionTracer ft(VSSDBG_WRITER, L"CVssWriterImpl::StartTimerThread"); CVssTimerArgs *pArgs = (CVssTimerArgs *) pv; BS_ASSERT(pArgs); BS_ASSERT(pArgs->m_pWriter);
bool bCoInitializeSucceeded = false; try { // coinitialize thread
ft.hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if (ft.HrFailed()) { pArgs->m_pWriter->m_hrWriterFailure = VSS_E_WRITERERROR_OUTOFRESOURCES; ft.TranslateError ( VSSDBG_WRITER, ft.hr, L"CoInitializeEx" ); }
bCoInitializeSucceeded = true; // call timer func
pArgs->m_pWriter->TimerFunc(pArgs->m_snapshotSetId); } VSS_STANDARD_CATCH(ft)
if (bCoInitializeSucceeded) CoUninitialize();
// delete timer arguments
delete pArgs; return 0; }
// function implementing timer functionality
void CVssWriterImpl::TimerFunc(VSS_ID snapshotSetId) { CVssFunctionTracer ft(VSSDBG_WRITER, L"CVssWriterImpl::TimerFunc");
// wait on event to insure that only one timer is active at
// any point in time
if (WaitForSingleObject(m_hmtxTimerThread, INFINITE) == WAIT_FAILED) { DWORD dwErr = GetLastError(); ft.Trace(VSSDBG_WRITER, L"WaitForSingleObject failed with error %d", dwErr); BS_ASSERT(FALSE && "WaitForSingleObject failed"); }
// reset timer event
if (!ResetEvent(m_hevtTimerThread)) { DWORD dwErr = GetLastError(); ft.Trace(VSSDBG_WRITER, L"ResetEvent failed with error %d", dwErr); BS_ASSERT(FALSE && "ResetEvent failed"); }
Lock(); // make sure that we are still in a snapshot sequence
if (!m_bSequenceInProgress || snapshotSetId != GetCurrentSnapshotSetId()) { // not in sequence, exit function
Unlock(); // allow another timer thread to start
ReleaseMutex(m_hmtxTimerThread); return; }
// initial command is to abort the current sequence on timeout
m_command = VSS_TC_ABORT_CURRENT_SEQUENCE;
Unlock(); DWORD dwTimeout = m_dwTimeoutFreeze;
if (WaitForSingleObject(m_hevtTimerThread, dwTimeout) == WAIT_FAILED) { ft.Trace ( VSSDBG_WRITER, L"Wait in timer thread failed due to reason %d.", GetLastError() );
// allow another thread to start
ReleaseMutex(m_hmtxTimerThread); return; }
CVssWriterImplLock lock(this); if (m_command != VSS_TC_TERMINATE_THREAD) { BS_ASSERT(m_command == VSS_TC_ABORT_CURRENT_SEQUENCE);
// cause current sequence to abort
ft.Trace(VSSDBG_WRITER, L"Aborting due to timeout\n"); DoAbort(true); }
// allow another timer thread to start
ReleaseMutex(m_hmtxTimerThread); }
/////////////////////////////////////////////////////////////////////////////
// IVssWriter implementation
STDMETHODIMP CVssWriterImpl::RequestWriterInfo ( IN BSTR bstrSnapshotSetId, IN BOOL bWriterMetadata, IN BOOL bWriterState, IN IDispatch* pWriterCallback ) { CVssFunctionTracer ft( VSSDBG_WRITER, L"CVssWriterImpl::RequestWriterInfo" );
// MTA synchronization: The critical section will be left automatically at the end of scope.
CVssWriterImplLock lock(this);
// created metadata, deleted on exit from routine
CVssCreateWriterMetadata *pcwm = NULL; try { // validate that the flags make sense
if (bWriterMetadata && bWriterState || !bWriterMetadata && !bWriterState) ft.Throw(VSSDBG_WRITER, E_INVALIDARG, L"Incorrect flags");
// if we are requesting writer state then we must have a snapshot
// set id
if (bWriterState && bstrSnapshotSetId == NULL) ft.Throw(VSSDBG_WRITER, E_INVALIDARG, L"NULL required input parameter.");
if (!IsBackupOperator()) ft.Throw(VSSDBG_WRITER, E_ACCESSDENIED, L"Backup Operator privileges are not set");
// get IVssWriterCallback interface
CComPtr<IVssWriterCallback> pCallback; GetCallback(pWriterCallback, &pCallback);
if (bWriterMetadata) { // BUG 219757: The identify phase marked as failed
m_bFailedAtIdentify = true; // obtain writer metadata
// create basic metadata using initialization parameters
pcwm = CreateBasicWriterMetadata();
// call writer's OnIdentify method to get more metadata
BS_ASSERT(m_pWriter); bool bSucceeded; try { bSucceeded = m_pWriter->OnIdentify ( (IVssCreateWriterMetadata *) pcwm ); } catch(...) { ft.Trace(VSSDBG_WRITER, L"Writer's OnIdentify method threw and exception."); throw; }
if (!bSucceeded) { // indicate failure if writer fails OnIdentify
ft.Throw(VSSDBG_WRITER, S_FALSE, L"Writer's OnIdentify method returned false."); }
CComBSTR bstrXML; CComBSTR bstrInstanceId(m_InstanceID); CComBSTR bstrWriterId(m_WriterID); CComBSTR bstrWriterName(m_wszWriterName); if (!bstrInstanceId || !bstrWriterId || !bstrWriterName) ft.Throw(VSSDBG_WRITER, E_OUTOFMEMORY, L"Couldn't allocate memory for ids or name");
// save WRITER_METADATA document as XML string
ft.hr = pcwm->SaveAsXML(&bstrXML); if (FAILED(ft.hr)) ft.Throw ( VSSDBG_WRITER, E_OUTOFMEMORY, L"Cannot save XML document as string. hr = 0x%08lx", ft.hr );
// callback through ExposeWriterMetadata method
try { ft.hr = pCallback->ExposeWriterMetadata ( bstrInstanceId, bstrWriterId, bstrWriterName, bstrXML ); } catch(...) { ft.Trace(VSSDBG_WRITER, L"IVssWriterCallback::ExposeWriterMetadata threw an exception."); throw; } // BUG 219757: The identify phase marked as succeeded.
m_bFailedAtIdentify = false; } else { // get writer state
CComBSTR bstrInstanceId(m_InstanceID); if (!bstrInstanceId) ft.Throw(VSSDBG_WRITER, E_OUTOFMEMORY, L"Couldn't allocate memory for ids or name");
CVssID id; id.Initialize(ft, (LPCWSTR) bstrSnapshotSetId, E_INVALIDARG);
VSS_WRITER_STATE state; HRESULT hrWriterFailure;
// BUG 219757 - deal with the Identify failures correctly.
if (m_bFailedAtIdentify) { state = VSS_WS_FAILED_AT_IDENTIFY; hrWriterFailure = m_hrWriterFailure; } else { if (id == GUID_NULL || (m_bSequenceInProgress && id == m_CurrentSnapshotSetId)) { state = m_state; hrWriterFailure = m_hrWriterFailure; } else { // Search for the previous sequence with the same ID
INT nPreviousSequence = SearchForPreviousSequence(id); if (nPreviousSequence == INVALID_SEQUENCE_INDEX) { state = VSS_WS_UNKNOWN; hrWriterFailure = E_UNEXPECTED; } else { BS_ASSERT(m_rgidPreviousSnapshots[nPreviousSequence] == id); state = m_rgstatePreviousSnapshots[nPreviousSequence]; hrWriterFailure = m_rghrWriterFailurePreviousSnapshots[nPreviousSequence]; } } }
// call Backup's ExposeCurrentState callback method
try { ft.hr = pCallback->ExposeCurrentState ( bstrInstanceId, state, hrWriterFailure ); } catch(...) { ft.Trace(VSSDBG_WRITER, L"IVssWriterCallback::ExposeCurrentState threw an exception"); throw; } } } VSS_STANDARD_CATCH(ft)
delete pcwm;
// Bug 255996
return S_OK; }
// process PrepareForBackup event
STDMETHODIMP CVssWriterImpl::PrepareForBackup ( IN BSTR bstrSnapshotSetId, IN IDispatch* pWriterCallback ) { CVssFunctionTracer ft( VSSDBG_WRITER, L"CVssWriterImpl::PrepareForBackup" );
try { ft.Trace(VSSDBG_WRITER, L"\nReceived Event: PrepareForBackup\nParameters:\n"); ft.Trace(VSSDBG_WRITER, L"\tSnapshotSetID = %s\n", (LPWSTR)bstrSnapshotSetId);
// access check
if (!IsBackupOperator()) { m_hrWriterFailure = VSS_E_WRITERERROR_NONRETRYABLE; ft.Throw(VSSDBG_WRITER, E_ACCESSDENIED, L"Backup Operator privileges are not set"); }
// enter PrepareForBackup state
if (!EnterState ( s_rgWriterStates[s_ivwsmPrepareForBackup], bstrSnapshotSetId )) { m_hrWriterFailure = VSS_E_WRITERERROR_RETRYABLE; ft.Throw ( VSSDBG_WRITER, E_UNEXPECTED, L"Couldn't properly begin sequence" ); }
AssertLocked(); // get IVssWriterCallback interface
CComPtr<IVssWriterCallback> pCallback; GetCallback(pWriterCallback, &pCallback);
// get IVssWriterComponentsExt interface
CComPtr<IVssWriterComponentsInt> pComponents; InternalGetWriterComponents(pCallback, &pComponents, true);
BS_ASSERT(m_pWriter);
// call writer's OnPrepareBackup method
bool bResult; try { bResult = m_pWriter->OnPrepareBackup(pComponents); BS_ASSERT(m_bOnAbortPermitted == false); m_bOnAbortPermitted = true; } catch(...) { ft.Trace(VSSDBG_WRITER, L"Writer's OnPrepareBackup method threw an exception"); if (m_hrWriterFailure == S_OK) m_hrWriterFailure = VSS_E_WRITERERROR_NONRETRYABLE;
throw; }
if (!bResult) ft.Throw( VSSDBG_WRITER, E_UNEXPECTED, L"Writer rejected the prepare");
// save changes to components if any
if (pComponents) { bool bChanged;
// determine if components are changed
ft.hr = pComponents->IsChanged(&bChanged); BS_ASSERT(ft.hr == S_OK); if (bChanged) { // get instance id
CComBSTR bstrWriterInstanceId(m_InstanceID); if (!bstrWriterInstanceId) ft.Throw(VSSDBG_WRITER, E_OUTOFMEMORY, L"Couldn't allocate instance id string");
// get WRITER_COMPONENTS XML document
CComBSTR bstrWriterComponentsDocument; ft.hr = pComponents->SaveAsXML(&bstrWriterComponentsDocument); if (ft.HrFailed()) { m_hrWriterFailure = VSS_E_WRITERERROR_OUTOFRESOURCES; ft.Throw ( VSSDBG_WRITER, E_OUTOFMEMORY, L"Saving WRITER_COMPONENTS document as XML failed. hr = 0x%08lx", ft.hr ); }
// callback to set component in BACKUP_COMPONENTS document
try { ft.hr = pCallback->SetContent(bstrWriterInstanceId, bstrWriterComponentsDocument); } catch(...) { m_hrWriterFailure = VSS_E_WRITERERROR_RETRYABLE; ft.Trace(VSSDBG_WRITER, L"IVssWriterCallback::SetContent threw an exception."); throw; }
if (ft.HrFailed()) { m_hrWriterFailure = VSS_E_WRITERERROR_RETRYABLE; ft.Throw ( VSSDBG_WRITER, ft.hr, L"IVssWriterCallback::SetContent failed. hr = 0x%08lx", ft.hr ); } } } } VSS_STANDARD_CATCH(ft)
// leave PrepareBackup state
LeaveState(s_rgWriterStates[s_ivwsmPrepareForBackup], ft.HrSucceeded());
// Bug 255996
return S_OK; }
// process PrepareForSnapshot event
STDMETHODIMP CVssWriterImpl::PrepareForSnapshot ( IN BSTR bstrSnapshotSetId, // snapshot set id
IN BSTR bstrVolumeNamesList // list of volume names separated by ';'
) { CVssFunctionTracer ft( VSSDBG_WRITER, L"CVssWriterImpl::PrepareForSnapshot" );
try { ft.Trace(VSSDBG_WRITER, L"\nReceived Event: PrepareForSnapshot\nParameters:\n"); ft.Trace(VSSDBG_WRITER, L"\tSnapshotSetID = %s\n", (LPWSTR)bstrSnapshotSetId); ft.Trace(VSSDBG_WRITER, L"\tVolumeNamesList = %s\n", (LPWSTR)bstrVolumeNamesList);
// should only be called by coordinator
// check for admin privileges
if (!IsAdministrator()) { m_hrWriterFailure = VSS_E_WRITERERROR_NONRETRYABLE; ft.Throw(VSSDBG_WRITER, E_ACCESSDENIED, L"ADMIN privileges are not set"); }
// enter PrepareForSnapshot state
if (!EnterState ( s_rgWriterStates[s_ivwsmPrepareForSnapshot], bstrSnapshotSetId )) { m_hrWriterFailure = VSS_E_WRITERERROR_RETRYABLE; ft.Throw ( VSSDBG_WRITER, E_UNEXPECTED, L"improper state transition" ); }
AssertLocked(); // Get the array of volume names
BS_ASSERT(m_pwszLocalVolumeNameList == NULL); ::VssSafeDuplicateStr(ft, m_pwszLocalVolumeNameList, (LPWSTR)bstrVolumeNamesList);
// Get the number of volumes
BS_ASSERT(m_nVolumesCount == 0); m_nVolumesCount = 0; // For safety
LPWSTR pwszVolumesToBeParsed = m_pwszLocalVolumeNameList;
// parse volume name string
while(true) { // get pointer to next volume
WCHAR* pwszNextVolume = ::wcstok(pwszVolumesToBeParsed, VSS_VOLUME_DELIMITERS); pwszVolumesToBeParsed = NULL;
if (pwszNextVolume == NULL) // no more volumes
break;
// skip if volume name is empty
if (pwszNextVolume[0] == L'\0') continue;
// count of volumes
m_nVolumesCount++; }
// make sure there is at least one volume
if (m_nVolumesCount == 0) { m_hrWriterFailure = VSS_E_WRITERERROR_RETRYABLE; ft.LogError(VSS_ERROR_EMPTY_SNAPSHOT_SET, VSSDBG_WRITER); ft.Throw ( VSSDBG_WRITER, E_UNEXPECTED, L"No volumes in the snapshot set" ); } // Allocate the array of pointers to volume names
BS_ASSERT(m_nVolumesCount > 0); BS_ASSERT(m_ppwszVolumesArray == NULL); m_ppwszVolumesArray = new LPWSTR[m_nVolumesCount]; if (m_ppwszVolumesArray == NULL) { m_hrWriterFailure = VSS_E_WRITERERROR_OUTOFRESOURCES; ft.Throw( VSSDBG_WRITER, E_OUTOFMEMORY, L"Memory allocation error"); }
//
// Copy the volume names into the array.
//
// re-copy the whole volume list
::wcscpy(m_pwszLocalVolumeNameList, (LPWSTR)bstrVolumeNamesList);
// Fill the array by re-parsing the volume list.
INT nVolumesIndex = 0; pwszVolumesToBeParsed = m_pwszLocalVolumeNameList; while(true) { WCHAR* pwszNextVolume = ::wcstok(pwszVolumesToBeParsed, VSS_VOLUME_DELIMITERS); pwszVolumesToBeParsed = NULL;
if (pwszNextVolume == NULL) break;
if (pwszNextVolume[0] == L'\0') continue;
BS_ASSERT(nVolumesIndex < m_nVolumesCount); m_ppwszVolumesArray[nVolumesIndex] = pwszNextVolume; nVolumesIndex++; }
BS_ASSERT(nVolumesIndex == m_nVolumesCount);
// Call the writer's OnPrepareSnapshot method
BS_ASSERT(m_pWriter);
bool bResult; try { bResult = m_pWriter->OnPrepareSnapshot(); m_bOnAbortPermitted = true; } catch(...) { if (m_hrWriterFailure == S_OK) m_hrWriterFailure = VSS_E_WRITERERROR_NONRETRYABLE; ft.Trace(VSSDBG_WRITER, L"Writer's OnPrepareSnapshot method threw an execption"); throw; }
if (!bResult) ft.Throw( VSSDBG_WRITER, E_UNEXPECTED, L"Writer rejected the prepare"); } VSS_STANDARD_CATCH(ft)
// leave PrepareSnapshot state
LeaveState(s_rgWriterStates[s_ivwsmPrepareForSnapshot], ft.HrSucceeded());
// Bug 255996
return S_OK; }
// process freeze event
STDMETHODIMP CVssWriterImpl::Freeze ( IN BSTR bstrSnapshotSetId, IN INT nLevel ) { CVssFunctionTracer ft( VSSDBG_WRITER, L"CVssWriterImpl::Freeze" );
try { ft.Trace( VSSDBG_WRITER, L"\nReceived Event: Freeze\nParameters:\n"); ft.Trace( VSSDBG_WRITER, L"\tSnapshotSetID = %s\n", (LPWSTR)bstrSnapshotSetId); ft.Trace( VSSDBG_WRITER, L"\tLevel = %d\n", nLevel); // should only be called by the coordinator, access check for admin privileges
if (!IsAdministrator()) ft.Throw(VSSDBG_WRITER, E_ACCESSDENIED, L"ADMIN privileges are not set");
// Ignore other Levels
if (m_nLevel != nLevel) return S_OK;
// enter freeze state
if (!EnterState ( s_rgWriterStates[s_ivwsmFreeze], bstrSnapshotSetId )) { m_hrWriterFailure = VSS_E_WRITERERROR_RETRYABLE; ft.Throw ( VSSDBG_WRITER, E_UNEXPECTED, L"Improper entry into state" ); }
AssertLocked(); // Call writer's OnFreeze
BS_ASSERT(m_pWriter);
bool bResult; try { bResult = m_pWriter->OnFreeze(); BS_ASSERT(m_bOnAbortPermitted == true); } catch(...) { if (m_hrWriterFailure == S_OK) m_hrWriterFailure = VSS_E_WRITERERROR_NONRETRYABLE; ft.Trace(VSSDBG_WRITER, L"Writer's OnFreeze Method threw and exception"); throw; }
if (!bResult) ft.Throw( VSSDBG_WRITER, E_UNEXPECTED, L"Writer rejected the freeze");
// setup arguments to timer thread
CVssTimerArgs *pArgs = new CVssTimerArgs(this, m_CurrentSnapshotSetId); if (pArgs == NULL) { m_hrWriterFailure = VSS_E_WRITERERROR_OUTOFRESOURCES; ft.Throw ( VSSDBG_WRITER, E_OUTOFMEMORY, L"Cannot create timer args due to allocation failure" ); }
DWORD tid;
// create timer thread
m_hThreadTimerThread = CreateThread ( NULL, VSS_STACK_SIZE, &CVssWriterImpl::StartTimerThread, pArgs, 0, &tid );
if (m_hThreadTimerThread == NULL) { m_hrWriterFailure = VSS_E_WRITERERROR_OUTOFRESOURCES; delete pArgs; ft.Throw ( VSSDBG_WRITER, E_OUTOFMEMORY, L"Failure to create thread due to error %d.", GetLastError() ); } } VSS_STANDARD_CATCH(ft)
// leave OnFreeze state
LeaveState( s_rgWriterStates[s_ivwsmFreeze], ft.HrSucceeded());
// Bug 255996
return S_OK; }
// handle IVssWriter::Thaw event
STDMETHODIMP CVssWriterImpl::Thaw ( IN BSTR bstrSnapshotSetId ) { CVssFunctionTracer ft( VSSDBG_WRITER, L"CVssWriterImpl::Thaw" );
try { ft.Trace( VSSDBG_WRITER, L"\nReceived Event: Thaw\nParameters:\n"); ft.Trace( VSSDBG_WRITER, L"\tSnapshotSetID = %s\n", (LPWSTR)bstrSnapshotSetId);
// should only be called by coordinator. Access check for admin
if (!IsAdministrator()) { m_hrWriterFailure = VSS_E_WRITERERROR_NONRETRYABLE; ft.Throw(VSSDBG_WRITER, E_ACCESSDENIED, L"ADMIN privileges are not set"); }
// enter Thaw state
if (!EnterState ( s_rgWriterStates[s_ivwsmThaw], bstrSnapshotSetId )) { m_hrWriterFailure = VSS_E_WRITERERROR_RETRYABLE; ft.Throw ( VSSDBG_WRITER, E_UNEXPECTED, L"Improper entry into state" ); }
AssertLocked(); // We should "live" in a sequence since Thaw is not the first phase of the sequence.
BS_ASSERT(m_bSequenceInProgress);
// Call writer's OnThaw
BS_ASSERT(m_pWriter);
bool bResult; try { bResult = m_pWriter->OnThaw(); } catch(...) { if (m_hrWriterFailure == S_OK) m_hrWriterFailure = VSS_E_WRITERERROR_NONRETRYABLE;
ft.Trace(VSSDBG_WRITER, L"Writer's OnThaw method threw an exception"); throw; }
// throw veto if writer vetoes the event
if (!bResult) ft.Throw( VSSDBG_WRITER, E_UNEXPECTED, L"Writer rejected the thaw"); } VSS_STANDARD_CATCH(ft)
// leave OnThaw state
LeaveState(s_rgWriterStates[s_ivwsmThaw], ft.HrSucceeded()); // Bug 255996
return S_OK; }
// process backup complete event
STDMETHODIMP CVssWriterImpl::BackupComplete ( IN BSTR bstrSnapshotSetId, IN IDispatch* pWriterCallback ) { CVssFunctionTracer ft( VSSDBG_WRITER, L"CVssWriterImpl::BackupComplete" );
// MTA synchronization: The critical section will be left automatically at the end of scope.
CVssWriterImplLock lock(this);
try { ft.Trace(VSSDBG_WRITER, L"\nReceived Event: OnBackupComplete\nParameters:\n"); ft.Trace(VSSDBG_WRITER, L"\tSnapshotSetID = %s\n", (LPWSTR)bstrSnapshotSetId);
// access check
if (!IsBackupOperator()) { m_hrWriterFailure = VSS_E_WRITERERROR_NONRETRYABLE; ft.Throw(VSSDBG_WRITER, E_ACCESSDENIED, L"Backup Operator privileges are not set"); }
BS_ASSERT(m_bSequenceInProgress == false); BS_ASSERT(m_CurrentSnapshotSetId == GUID_NULL);
CVssID id; id.Initialize(ft, (LPCWSTR) bstrSnapshotSetId, E_INVALIDARG);
// We must search for a previous state - Thaw already ended the sequence.
INT iPreviousSequence = SearchForPreviousSequence(id); if (iPreviousSequence == INVALID_SEQUENCE_INDEX) { m_hrWriterFailure = VSS_E_WRITERERROR_RETRYABLE; ft.Throw ( VSSDBG_WRITER, E_UNEXPECTED, L"Couldn't find a previous sequence with the same Snapshot Set ID" ); }
// We found a previous sequence with the same SSID.
BS_ASSERT(id == m_rgidPreviousSnapshots[iPreviousSequence]);
// BUG 228622 - If we do not have a previous successful Thaw transition
// then we cannot call BackupComplete
if (m_rgstatePreviousSnapshots[iPreviousSequence] != VSS_WS_WAITING_FOR_BACKUP_COMPLETE) { m_hrWriterFailure = VSS_E_WRITERERROR_RETRYABLE; ft.Throw ( VSSDBG_WRITER, E_UNEXPECTED, L"Couldn't call BackupComplete without OnThaw as a previous state [%d]", m_rgstatePreviousSnapshots[iPreviousSequence] ); }
// BUG 219692 - indicate that sequence is complete even in the saved states
m_rgstatePreviousSnapshots[iPreviousSequence] = VSS_WS_STABLE;
// get IVssWriterCallback interface
CComPtr<IVssWriterCallback> pCallback; GetCallback(pWriterCallback, &pCallback);
// get IVssWriterComponentsInt object
CComPtr<IVssWriterComponentsInt> pComponents; InternalGetWriterComponents(pCallback, &pComponents, false);
// call writer's OnBackupComplete method
BS_ASSERT(m_pWriter); try { if (!m_pWriter->OnBackupComplete(pComponents)) ft.hr = S_FALSE; } catch(...) { if (m_hrWriterFailure == S_OK) m_hrWriterFailure = VSS_E_WRITERERROR_NONRETRYABLE;
ft.Trace(VSSDBG_WRITER, L"Writer's OnBackupComplete method threw an exception."); throw; }
} VSS_STANDARD_CATCH(ft)
// indicate that sequence is complete
if (m_state == VSS_WS_WAITING_FOR_BACKUP_COMPLETE) m_state = VSS_WS_STABLE; // Bug 255996
return S_OK; }
STDMETHODIMP CVssWriterImpl::BackupShutdown ( IN BSTR bstrSnapshotSetId ) { UNREFERENCED_PARAMETER(bstrSnapshotSetId); CVssFunctionTracer ft( VSSDBG_WRITER, L"CVssWriterImpl::BackupShutdown");
ft.hr = E_NOTIMPL; return ft.hr; }
// handle IVssWriter::Abort event
STDMETHODIMP CVssWriterImpl::Abort ( IN BSTR bstrSnapshotSetId ) { CVssFunctionTracer ft( VSSDBG_WRITER, L"CVssWriterImpl::Abort" );
ft.Trace( VSSDBG_WRITER, L"\nReceived Event: Abort\nParameters:\n"); ft.Trace( VSSDBG_WRITER, L"\tSnapshotSetID = %s\n", (LPWSTR)bstrSnapshotSetId);
if (!IsBackupOperator()) ft.Throw(VSSDBG_WRITER, E_ACCESSDENIED, L"Backup privileges are not set");
Lock(); // call do abort function
DoAbort(false); Unlock();
return S_OK; }
// process restore event
STDMETHODIMP CVssWriterImpl::PostRestore ( IN IDispatch* pWriterCallback ) { CVssFunctionTracer ft( VSSDBG_WRITER, L"CVssWriterImpl::Restore" );
ft.Trace(VSSDBG_WRITER, L"\nReceived Event: Restore\n");
// MTA synchronization: The critical section will be left automatically at the end of scope.
CVssWriterImplLock lock(this); m_hrWriterFailure = S_OK;
try { // access check
if (!IsRestoreOperator()) { m_hrWriterFailure = VSS_E_WRITERERROR_NONRETRYABLE; ft.Throw(VSSDBG_WRITER, E_ACCESSDENIED, L"Backup Operator privileges are not set"); }
// get writer callback interface
CComPtr<IVssWriterCallback> pCallback; GetCallback(pWriterCallback, &pCallback); CComPtr<IVssWriterComponentsInt> pComponents;
// get IVssWriterComponentsInt object
InternalGetWriterComponents(pCallback, &pComponents, false);
// call writer's OnPostRestore method
BS_ASSERT(m_pWriter); try { if (!m_pWriter->OnPostRestore(pComponents)) ft.hr = S_FALSE; } catch(...) { if (m_hrWriterFailure == S_OK) m_hrWriterFailure = VSS_E_WRITERERROR_NONRETRYABLE;
ft.Trace(VSSDBG_WRITER, L"Writer's OnPostRestore method threw an exception"); throw; } } VSS_STANDARD_CATCH(ft)
// Bug 255996
return S_OK; }
// process restore event
STDMETHODIMP CVssWriterImpl::PreRestore ( IN IDispatch* pWriterCallback ) { UNREFERENCED_PARAMETER(pWriterCallback);
CVssFunctionTracer ft( VSSDBG_WRITER, L"CVssWriterImpl::PreRestore" );
BS_ASSERT(FALSE);
return S_OK; }
// process restore event
STDMETHODIMP CVssWriterImpl::PostSnapshot ( IN BSTR bstrSnapshotSetId, IN IDispatch* pWriterCallback, IN BSTR SnapshotDevicesList ) { UNREFERENCED_PARAMETER(pWriterCallback); UNREFERENCED_PARAMETER(bstrSnapshotSetId); UNREFERENCED_PARAMETER(SnapshotDevicesList);
CVssFunctionTracer ft( VSSDBG_WRITER, L"CVssWriterImpl::PostSnapshot" );
BS_ASSERT(FALSE);
return S_OK; }
// table of names of events that we are subscribing to
// NOTE: this table is based on definition of VSS_EVENT_MASK. Each
// offset corresponds to a bit on that mask
const WCHAR *g_rgwszSubscriptions[] = { g_wszPrepareForBackupMethodName, // VSS_EVENT_PREPAREBackup
g_wszPrepareForSnapshotMethodName, // VSS_EVENT_PREPARESnapshot
g_wszFreezeMethodName, // VSS_EVENT_FREEZE
g_wszThawMethodName, // VSS_EVENT_THAW
g_wszAbortMethodName, // VSS_EVENT_ABORT
g_wszBackupCompleteMethodName, // VSS_EVENT_BACKUPCOMPLETE
g_wszRequestInfoMethodName, // VSS_EVENT_REQUESTINFO
g_wszPostRestoreMethodName // VSS_EVENT_POST_RESTORE
};
/////////////////////////////////////////////////////////////////////////////
// Subscription-related members
// create subscriptions
void CVssWriterImpl::Subscribe() { CVssFunctionTracer ft(VSSDBG_WRITER, L"CVssWriterImpl::Subscribe");
// validate that caller can subscribe to the event
if (!IsProcessBackupOperator() && !IsProcessLocalService() && !IsProcessNetworkService()) ft.Throw ( VSSDBG_WRITER, E_ACCESSDENIED, L"Caller is not either a backup operator, administrator, local service, or network service" );
// currently we subscribe to all events
m_dwEventMask = VSS_EVENT_ALL;
if (m_bstrSubscriptionName.Length() > 0) ft.Throw ( VSSDBG_XML, VSS_E_WRITER_ALREADY_SUBSCRIBED, L"The writer has already called the Subscribe function." );
// create event system
CComPtr<IEventSystem> pSystem; ft.hr = CoCreateInstance ( CLSID_CEventSystem, NULL, CLSCTX_SERVER, IID_IEventSystem, (void **) &pSystem );
ft.CheckForError(VSSDBG_WRITER, L"CoCreateInstance"); CComBSTR bstrClassId = CLSID_VssEvent; CComBSTR bstrIID = IID_IVssWriter; CComBSTR bstrProgId = PROGID_EventSubscription;
// see if event class already exists
CComBSTR bstrQuery = "EventClassID == "; if (!bstrQuery) ft.Throw(VSSDBG_WRITER, E_OUTOFMEMORY, L"Cannot allocate BSTR.");
bstrQuery.Append(bstrClassId); if (!bstrQuery) ft.Throw(VSSDBG_WRITER, E_OUTOFMEMORY, L"Cannot allocate BSTR.");
int location; CComPtr<IEventObjectCollection> pCollection; ft.hr = pSystem->Query ( PROGID_EventClassCollection, bstrQuery, &location, (IUnknown **) &pCollection );
ft.CheckForError(VSSDBG_WRITER, L"IEventSystem::Query"); long cEvents; ft.hr = pCollection->get_Count(&cEvents); ft.CheckForError(VSSDBG_WRITER, L"IEventObjectCollection::get_Count"); if (cEvents == 0) { // event class does not exist, create it. Note that there is a
// potential race condition here if two writers try creating the event
// class at the same time. We create the event class during installation
// so that this should rarely happen
CComPtr<IEventClass> pEvent;
CComBSTR bstrEventClassName = L"VssEvent"; WCHAR buf[MAX_PATH*2];
// event class typelib
UINT cwc = ExpandEnvironmentStrings ( L"%systemroot%\\system32\\eventcls.dll", buf, sizeof(buf)/sizeof(WCHAR) );
if (cwc == 0) { ft.hr = HRESULT_FROM_WIN32(GetLastError()); ft.CheckForError(VSSDBG_WRITER, L"ExpandEnvironmentStrings"); }
CComBSTR bstrTypelib = buf;
// create event class
ft.hr = CoCreateInstance ( CLSID_CEventClass, NULL, CLSCTX_SERVER, IID_IEventClass, (void **) &pEvent );
ft.CheckForError(VSSDBG_WRITER, L"CoCreatInstance");
// setup class id
ft.hr = pEvent->put_EventClassID(bstrClassId); ft.CheckForError(VSSDBG_WRITER, L"IEventClass::put_EventClassID");
// set up class name
ft.hr = pEvent->put_EventClassName(bstrEventClassName); ft.CheckForError(VSSDBG_WRITER, L"IEventClass::put_EventClassName");
// set up typelib
ft.hr = pEvent->put_TypeLib(bstrTypelib); ft.CheckForError(VSSDBG_WRITER, L"IEventClass::put_TypeLib");
// store event class
ft.hr = pSystem->Store(PROGID_EventClass, pEvent); ft.CheckForError(VSSDBG_WRITER, L"IEventSystem::Store"); }
// create subscription id
VSS_ID SubscriptionId; ft.hr = ::CoCreateGuid(&SubscriptionId); ft.CheckForError(VSSDBG_WRITER, L"CoCreateGuid"); m_bstrSubscriptionName = SubscriptionId;
// get IUnknown for subscribers class
IUnknown *pUnkSubscriber = GetUnknown(); UINT iwsz, mask;
try { // loop through subscriptions
for(mask = 1, iwsz = 0; mask < VSS_EVENT_ALL; mask = mask << 1, iwsz++) { if (m_dwEventMask & mask && g_rgwszSubscriptions[iwsz] != NULL) { // create IEventSubscription object
CComPtr<IEventSubscription> pSubscription; ft.hr = CoCreateInstance ( CLSID_CEventSubscription, NULL, CLSCTX_SERVER, IID_IEventSubscription, (void **) &pSubscription );
ft.CheckForError(VSSDBG_WRITER, L"CoCreateInstance");
// set subscription name
ft.hr = pSubscription->put_SubscriptionName(m_bstrSubscriptionName); ft.CheckForError(VSSDBG_WRITER, L"IEventSubscription::put_SubscriptionName");
// set event class id
ft.hr = pSubscription->put_EventClassID(bstrClassId); ft.CheckForError(VSSDBG_WRITER, L"IEventSubscription::put_EventClassID");
// set interface id
ft.hr = pSubscription->put_InterfaceID(bstrIID); ft.CheckForError(VSSDBG_WRITER, L"IEventSubscription::put_InterfaceID");
// set subcriber interface
ft.hr = pSubscription->put_SubscriberInterface(pUnkSubscriber); ft.CheckForError(VSSDBG_WRITER, L"IEventSubscription::put_SubscriberInterface");
// make subscription per user since this is not necessarily in local system
ft.hr = pSubscription->put_PerUser(TRUE); ft.CheckForError(VSSDBG_WRITER, L"IEventSubscription::put_PerUser");
// set method name for subscrpition
ft.hr = pSubscription->put_MethodName(CComBSTR(g_rgwszSubscriptions[iwsz])); ft.CheckForError(VSSDBG_WRITER, L"IEventSubscription::put_MethodName");
// store subscription
ft.hr = pSystem->Store(bstrProgId, pSubscription); ft.CheckForError(VSSDBG_WRITER, L"IEventSystem::Store");
// get constructed subscription id and save it
ft.hr = pSubscription->get_SubscriptionID(&m_rgbstrSubscriptionId[m_cbstrSubscriptionId]); ft.CheckForError(VSSDBG_WRITER, L"IEventSubscription::get_SubscriptionID");
// increment count of
m_cbstrSubscriptionId++; } } } VSS_STANDARD_CATCH(ft)
// if the operation fails with us partially subscribed, then unsubscribe
if (ft.HrFailed() && m_cbstrSubscriptionId) { Unsubscribe(); ft.Throw(VSSDBG_WRITER, ft.hr, L"Rethrowing exception"); } }
// terminate timer thread
// assumes caller has the critical section locked
void CVssWriterImpl::TerminateTimerThread() { CVssFunctionTracer ft(VSSDBG_WRITER, L"CVssWriterImpl::TerminateTimerThread");
AssertLocked(); if (m_hThreadTimerThread) { // cause timer thread to terminate
m_command = VSS_TC_TERMINATE_THREAD; if (!SetEvent(m_hevtTimerThread)) { DWORD dwErr = GetLastError(); ft.Trace(VSSDBG_WRITER, L"SetEvent failed with error %d\n", dwErr); BS_ASSERT(FALSE && "SetEvent failed"); }
// get thread handle
HANDLE hThread = m_hThreadTimerThread; m_hThreadTimerThread = NULL;
// unlock during wait
Unlock(); if (WaitForSingleObject(hThread, INFINITE) == WAIT_FAILED) { DWORD dwErr = GetLastError(); ft.Trace(VSSDBG_WRITER, L"WaitForSingleObject failed with error %d\n", dwErr); BS_ASSERT(FALSE && "WaitForSingleObject failed"); } CloseHandle(hThread); Lock(); } }
// unsubscribe this writer from IVssWriter events
void CVssWriterImpl::Unsubscribe() { CVssFunctionTracer ft(VSSDBG_WRITER, L"CVssWriterImpl::Unsubscribe");
// terminate timer thread if active
Lock(); TerminateTimerThread(); Unlock();
// make sure subscription name was assigned; if not assume subscriptions
// weren't created
if (m_bstrSubscriptionName.Length() == 0) return;
// create event system
CComPtr<IEventSystem> pSystem; ft.hr = CoCreateInstance ( CLSID_CEventSystem, NULL, CLSCTX_SERVER, IID_IEventSystem, (void **) &pSystem );
ft.CheckForError(VSSDBG_WRITER, L"CoCreateInstance");
#if 0
WCHAR buf[256]; int location; swprintf(buf, L"SubscriptionName = \"%s\"", m_bstrSubscriptionName); CComPtr<IEventObjectCollection> pCollection; ft.hr = pSystem->Query ( PROGID_EventSubscriptionCollection, buf, &location, (IUnknown **) &pCollection );
ft.CheckForError(VSSDBG_WRITER, L"IEventSystem::Query");
long cSub; ft.hr = pCollection->get_Count(&cSub); ft.CheckForError(VSSDBG_WRITER, L"IEventObjectCollection::get_Count"); pCollection = NULL; #endif
for(UINT iSubscription = 0; iSubscription < m_cbstrSubscriptionId; iSubscription++) { // setup query string
CComBSTR bstrQuery = L"SubscriptionID == "; if (!bstrQuery) ft.Throw(VSSDBG_WRITER, E_OUTOFMEMORY, L"allocation of BSTR failed");
// if subscription exists, then remove it
if (m_rgbstrSubscriptionId[iSubscription]) { bstrQuery.Append(m_rgbstrSubscriptionId[iSubscription]); if (!bstrQuery) ft.Throw(VSSDBG_WRITER, E_OUTOFMEMORY, L"allocation of BSTR failed");
int location;
// remove subscription
ft.hr = pSystem->Remove(PROGID_EventSubscription, bstrQuery, &location); ft.CheckForError(VSSDBG_WRITER, L"IEventSystem::Remove");
// indicate that subscription was removed
m_rgbstrSubscriptionId[iSubscription].Empty(); } } #if 0
ft.hr = pSystem->Query ( PROGID_EventSubscriptionCollection, buf, &location, (IUnknown **) &pCollection );
ft.CheckForError(VSSDBG_WRITER, L"IEventSystem::Query");
ft.hr = pCollection->get_Count(&cSub); ft.CheckForError(VSSDBG_WRITER, L"IEventObjectCollection::get_Count"); pCollection = NULL; #endif
// reset subscription name so unsubscribe does nothing if called again
m_bstrSubscriptionName.Empty(); m_cbstrSubscriptionId = 0; }
// create a internal writer class and link it up to the external writer class
void CVssWriterImpl::CreateWriter ( CVssWriter *pWriter, // external writer
IVssWriterImpl **ppImpl // interface to be used by external writer
) { CVssFunctionTracer ft(VSSDBG_WRITER, L"CVssWriterImpl::CreateWriter");
BS_ASSERT(ppImpl); BS_ASSERT(pWriter);
*ppImpl = NULL;
// create internal wrier class
CComObject<CVssWriterImpl> *pImpl; // create CVssWriterImpl object <core writer class>
ft.hr = CComObject<CVssWriterImpl>::CreateInstance(&pImpl); if (ft.HrFailed()) ft.Throw ( VSSDBG_WRITER, E_OUTOFMEMORY, L"Failed to create CVssWriterImpl. hr = 0x%08lx", ft.hr );
// set reference count of internal writer to 1
pImpl->GetUnknown()->AddRef();
// link external writer into internal writer
pImpl->SetWriter(pWriter);
// return internal writer interface
*ppImpl = (IVssWriterImpl *) pImpl; }
|