/*++ Copyright (c) 2000 Microsoft Corporation Module Name: mdwriter.cxx Abstract: This file contains implementation for snapshot writer class Author: Ming Lu (MingLu) 30-Apr-2000 --*/ #include #include "mdwriter.hxx" #define TIMEOUT_INTERVAL ( 2 * 60 ) #define DEFAULT_SAVE_TIMEOUT 30000 #define MDWRITER_EVENT_TIMEOUT 30000 #define IISCOMPONENT L"IISMETABASE" #define METABASEPATH L"%windir%\\system32\\inetsrv" #define METABASENAME1 L"metabase.bin" #define METABASENAME2 L"MetaBase.XML" #define METABASENAME3 L"MBSchema.XML" #define NT_SETUP_KEY "SYSTEM\\Setup" static VSS_ID s_WRITERID = { 0x59b1f0cf, 0x90ef, 0x465f, 0x96, 0x09, 0x6c, 0xa8, 0xb2, 0x93, 0x83, 0x66 }; static LPCWSTR s_WRITERNAME = L"IIS Metabase Writer"; BOOL g_fWriterSubscribed = FALSE; CIISVssWriter * g_pIISVssWriter = NULL; VOID CALLBACK UnlockMBProc( LPVOID pIISVssWriter, DWORD dwTimerLowValue, DWORD dwTimerHighValue ); BOOL CIISVssWriter::Initialize( VOID ) { HRESULT hr; hr = CVssWriter::Initialize( s_WRITERID, s_WRITERNAME, VSS_UT_SYSTEMSERVICE, VSS_ST_OTHER ); if( FAILED( hr ) ) { DBGPRINTF(( DBG_CONTEXT, "Error in base object Initialize(). hr = %x\n", hr )); return FALSE; } m_hTimer = CreateWaitableTimer( NULL, FALSE, NULL ); if( !m_hTimer ) { DBGPRINTF(( DBG_CONTEXT, "Error creating timer object. hr = %x\n", HRESULT_FROM_WIN32( GetLastError() ) )); return FALSE; } hr = CoCreateInstance( CLSID_MDCOM, NULL, CLSCTX_SERVER, IID_IMDCOM, (void**) &m_pMdObject); if( SUCCEEDED( hr ) ) { hr = m_pMdObject->ComMDInitialize(); if( FAILED( hr ) ) { DBGPRINTF(( DBG_CONTEXT, "Error initialize MDCOM object. hr = %x\n", hr )); m_pMdObject->Release(); m_pMdObject = NULL; return FALSE; } } else { DBGPRINTF(( DBG_CONTEXT, "Error creating MDCOM object. hr = %x\n", hr )); return FALSE; } return TRUE; } bool STDMETHODCALLTYPE CIISVssWriter::OnIdentify( IN IVssCreateWriterMetadata *pMetadata ) { HRESULT hr; hr = pMetadata->AddComponent( VSS_CT_FILEGROUP, NULL, IISCOMPONENT, NULL, 0, 0, FALSE, FALSE, FALSE ); if( FAILED( hr ) ) { DBGPRINTF(( DBG_CONTEXT, "Error AddComponent(). hr = %x\n", hr )); return FALSE; } hr = pMetadata->AddFilesToFileGroup( NULL, IISCOMPONENT, METABASEPATH, METABASENAME1, FALSE, NULL ); if( FAILED( hr ) ) { DBGPRINTF(( DBG_CONTEXT, "Error AddFilesToFileGroup(). hr = %x\n", hr )); return FALSE; } hr = pMetadata->AddFilesToFileGroup( NULL, IISCOMPONENT, METABASEPATH, METABASENAME2, FALSE, NULL ); if( FAILED( hr ) ) { DBGPRINTF(( DBG_CONTEXT, "Error AddFilesToFileGroup(). hr = %x\n", hr )); return FALSE; } hr = pMetadata->AddFilesToFileGroup( NULL, IISCOMPONENT, METABASEPATH, METABASENAME3, FALSE, NULL ); if( FAILED( hr ) ) { DBGPRINTF(( DBG_CONTEXT, "Error AddFilesToFileGroup(). hr = %x\n", hr )); return FALSE; } hr = pMetadata->SetRestoreMethod( VSS_RME_RESTORE_AT_REBOOT, // restore method NULL, // service name NULL, // user procedure VSS_WRE_NEVER, // when to call writer restore method TRUE // reboot is required ); if( FAILED( hr ) ) { DBGPRINTF(( DBG_CONTEXT, "Error setting restore method. hr = %x\n", hr )); return FALSE; } return TRUE; } bool STDMETHODCALLTYPE CIISVssWriter::OnPrepareBackup( IN IVssWriterComponents *pWriterComponents ) { return TRUE; } bool STDMETHODCALLTYPE CIISVssWriter::OnPrepareSnapshot( VOID ) { HRESULT hr; if( IsPathAffected( METABASEPATH ) ) { // // First try to lock the tree // hr = m_pMdObject->ComMDOpenMetaObjectW( METADATA_MASTER_ROOT_HANDLE, NULL, METADATA_PERMISSION_READ, DEFAULT_SAVE_TIMEOUT, &m_mdhRoot); if ( SUCCEEDED( hr ) ) { // // call metadata com api // hr = m_pMdObject->ComMDSaveData( m_mdhRoot ); if( SUCCEEDED( hr ) ) { hr = m_pMdObject->ComMDCloseMetaObject( m_mdhRoot ); if( SUCCEEDED ( hr ) ) { return TRUE; } DBGPRINTF(( DBG_CONTEXT, "Error on unlocking the metabase. hr = %x\n", hr )); return FALSE; } DBGPRINTF(( DBG_CONTEXT, "Error on saving the metabase. hr = %x\n", hr )); m_pMdObject->ComMDCloseMetaObject( m_mdhRoot ); return FALSE; } DBGPRINTF(( DBG_CONTEXT, "Error on locking down the metabase. hr = %x\n", hr )); return FALSE; } return TRUE; } bool STDMETHODCALLTYPE CIISVssWriter::OnFreeze( VOID ) { HRESULT hr; if( IsPathAffected( METABASEPATH ) ) { // // Lock down the metabase // hr = m_pMdObject->ComMDOpenMetaObjectW(METADATA_MASTER_ROOT_HANDLE, NULL, METADATA_PERMISSION_READ, DEFAULT_SAVE_TIMEOUT, &m_mdhRoot); if( FAILED( hr ) ) { DBGPRINTF(( DBG_CONTEXT, "Error on locking down the metabase. hr = %x\n", hr )); return FALSE; } else { if( !ResetTimer( m_hTimer, TIMEOUT_INTERVAL ) ) { DBGPRINTF(( DBG_CONTEXT, "Could not reset the internal timer. hr = %x\n", hr )); return FALSE; } EnterCriticalSection( &m_csMBLock ); m_fMBLocked = TRUE; LeaveCriticalSection( &m_csMBLock ); } } return TRUE; } bool STDMETHODCALLTYPE CIISVssWriter::OnThaw( VOID ) { HRESULT hr; if( IsPathAffected( METABASEPATH ) ) { EnterCriticalSection( &m_csMBLock ); if( m_fMBLocked ) { hr = m_pMdObject->ComMDCloseMetaObject( m_mdhRoot ); if( FAILED( hr ) ) { DBGPRINTF(( DBG_CONTEXT, "Error on unlocking the metabase. hr = %x\n", hr )); LeaveCriticalSection( &m_csMBLock ); return FALSE; } m_fMBLocked = FALSE; LeaveCriticalSection( &m_csMBLock ); CancelWaitableTimer( m_hTimer ); } else { LeaveCriticalSection( &m_csMBLock ); } } return TRUE; } bool STDMETHODCALLTYPE CIISVssWriter::OnBackupComplete( IN IVssWriterComponents *pWriterComponents ) { return TRUE; } bool STDMETHODCALLTYPE CIISVssWriter::OnAbort( VOID ) { HRESULT hr; if( IsPathAffected( METABASEPATH ) ) { EnterCriticalSection( &m_csMBLock ); if( m_fMBLocked ) { hr = m_pMdObject->ComMDCloseMetaObject( m_mdhRoot ); if( FAILED( hr ) ) { DBGPRINTF(( DBG_CONTEXT, "Error on unlocking the metabase. hr = %x\n", hr )); LeaveCriticalSection( &m_csMBLock ); return FALSE; } m_fMBLocked = FALSE; LeaveCriticalSection( &m_csMBLock ); CancelWaitableTimer( m_hTimer ); } else { LeaveCriticalSection( &m_csMBLock ); } } return true; } VOID CIISVssWriter::UnlockMetaBase( VOID ) { HRESULT hr; EnterCriticalSection( &m_csMBLock ); if( m_fMBLocked ) { hr = m_pMdObject->ComMDCloseMetaObject( m_mdhRoot ); if( FAILED( hr ) ) { DBGPRINTF(( DBG_CONTEXT, "Error on unlocking the metabase. hr = %x\n", hr )); LeaveCriticalSection( &m_csMBLock ); return; } m_fMBLocked = FALSE; } LeaveCriticalSection( &m_csMBLock ); } BOOL CIISVssWriter::ResetTimer( HANDLE hTimer, DWORD dwDuration ) { LARGE_INTEGER li; const int nNanosecondsPersecond = 10000000; __int64 qwTimeFromNowInNanoseconds = (__int64)dwDuration * nNanosecondsPersecond; qwTimeFromNowInNanoseconds = -qwTimeFromNowInNanoseconds; li.LowPart = (DWORD) (qwTimeFromNowInNanoseconds & 0xFFFFFFFF); li.HighPart = (LONG) (qwTimeFromNowInNanoseconds >> 32); if( !SetWaitableTimer( hTimer, &li, 0, UnlockMBProc, this, FALSE ) ) { return FALSE; } return TRUE; } VOID CALLBACK UnlockMBProc( LPVOID pIISVssWriter, DWORD dwTimerLowValue, DWORD dwTimerHighValue ) { ( ( CIISVssWriter * )pIISVssWriter )->UnlockMetaBase(); } VOID InitMDWriterThread( HANDLE hMDWriterEvent ) { HKEY hKey; DWORD dwType; DWORD cbData; DWORD dwSetupInProgress = 0; DWORD dwUpgradeInProcess = 0; DBG_ASSERT( hMDWriterEvent != NULL ); // // Read the setup registry key to see if we are in // setup mode. If we are, don't init IIS writer. // if ( !RegOpenKeyEx( HKEY_LOCAL_MACHINE, NT_SETUP_KEY, 0, KEY_QUERY_VALUE, &hKey ) ) { cbData = sizeof( DWORD ); if( !RegQueryValueEx( hKey, "SystemSetupInProgress", NULL, &dwType, ( LPBYTE )&dwSetupInProgress, &cbData ) ) { if( dwType == REG_DWORD && dwSetupInProgress != 0 ) { // // We are in setup mode // RegCloseKey( hKey ); goto exit; } } if( !RegQueryValueEx( hKey, "UpgradeInProgress", NULL, &dwType, ( LPBYTE )&dwUpgradeInProcess, &cbData ) ) { if( dwType == REG_DWORD && dwUpgradeInProcess != 0 ) { // // We are in upgrade mode // RegCloseKey( hKey ); goto exit; } } RegCloseKey( hKey ); } // // OK, we are not in setup mode, initialize our IIS writer // g_pIISVssWriter = new CIISVssWriter; if ( g_pIISVssWriter == NULL ) { // // oh well. guess we won’t support snapshots // DBGPRINTF(( DBG_CONTEXT, "Error on creating the writer object, out of memory\n" )); goto exit; } else { // // cool, we’ve got the object now. // if( !g_pIISVssWriter->Initialize() ) { DBGPRINTF(( DBG_CONTEXT, "Error on initializing the writer object\n" )); delete g_pIISVssWriter; g_pIISVssWriter = NULL; goto exit; } if( SUCCEEDED( g_pIISVssWriter->Subscribe() ) ) { g_fWriterSubscribed = TRUE; } else { DBGPRINTF(( DBG_CONTEXT, "Error on subscribing the writer object\n" )); delete g_pIISVssWriter; g_pIISVssWriter = NULL; } } exit: // // Let the TerminateMDWriter know we are ready to terminate during // IISADMIN service shutdown // SetEvent( hMDWriterEvent ); } HRESULT InitializeMDWriter( HANDLE hMDWriterEvent ) { HRESULT hr = S_OK; HANDLE hThread = NULL; DWORD dwThreadID; DBG_ASSERT( hMDWriterEvent != NULL ); hThread = CreateThread( NULL, 0, ( LPTHREAD_START_ROUTINE )InitMDWriterThread, ( PVOID )hMDWriterEvent, 0, &dwThreadID); if( hThread == NULL ) { hr = HRESULT_FROM_WIN32( GetLastError() ); } return hr; } VOID TerminateMDWriter( HANDLE hMDWriterEvent ) { DBG_ASSERT( hMDWriterEvent != NULL ); // // Only do cleanup if the hMDWriterEvent is signaled // if( WAIT_OBJECT_0 == WaitForSingleObject( hMDWriterEvent, MDWRITER_EVENT_TIMEOUT ) ) { if( g_fWriterSubscribed ) { DBG_ASSERT( g_pIISVssWriter ); g_pIISVssWriter->Unsubscribe(); } if( g_pIISVssWriter ) { delete g_pIISVssWriter; g_pIISVssWriter = NULL; } } }