f/*++ Copyright (c) 2002 Microsoft Corporation Abstract: @doc @module comregdbwriter.cxx | Implementation of the COM+ RegDB Writer @end Author: Ran Kalach [rankala] 05/17/2002 Revision History: Name Date Comments rankala 05/17/2002 created --*/ #include "stdafx.h" #include "vs_idl.hxx" #include "vs_hash.hxx" #include #include #include "vs_seh.hxx" #include "vs_trace.hxx" #include "vs_debug.hxx" #include "vs_reg.hxx" #include "comregdbwriter.hxx" #include "allerror.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 "WRTCDBRC" // //////////////////////////////////////////////////////////////////////// static GUID s_writerId = { 0x542da469, 0xd3e1, 0x473c, 0x9f, 0x4f, 0x78, 0x47, 0xf0, 0x1f, 0xc6, 0x4f }; // The display name of the Event Log writer. static LPCWSTR s_wszWriterName = L"COM+ REGDB Writer"; // The file group component name for the Event Log static LPCWSTR s_wszComponentName = L"COM+ REGDB"; // The original location of the RegDB files static CBsString s_cwsSystemComRegDBDir = L"%SystemRoot%\\Registration"; // The backup/restore location of the RegDB files static CBsString s_cwsComRegDBTargetDir = ROOT_BACKUP_DIR BOOTABLE_STATE_SUBDIR L"\\ComRegistrationDatabase"; // The backup/restore file name of the RegDB database static CBsString s_cwsBackupFilename = L"\\ComRegDb.bak"; // The COM+ System Applicationservice name static LPCWSTR s_wszCOMSysAppServiceName = L"COMSysApp"; // Class wide member variables CComRegDBWriter *CComRegDBWriter::sm_pWriter = NULL; // RegDB function declarations typedef HRESULT (*PF_REG_DB_API)(BSTR); // // Methods // HRESULT STDMETHODCALLTYPE CComRegDBWriter::Initialize() { CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::Initialize"); try { ft.hr = CVssWriter::Initialize ( s_writerId, s_wszWriterName, VSS_UT_BOOTABLESYSTEMSTATE, VSS_ST_OTHER, VSS_APP_SYSTEM, 60000 ); if (ft.HrFailed()) ft.TranslateGenericError ( VSSDBG_WRITER, ft.hr, L"CVssWriter::Initialize(" WSTR_GUID_FMT L", %s)", GUID_PRINTF_ARG(s_writerId), s_wszWriterName ); ft.hr = Subscribe(); if (ft.HrFailed()) ft.TranslateGenericError ( VSSDBG_WRITER, ft.hr, L"CVssWriter::Subscribe for" WSTR_GUID_FMT L" %s)", GUID_PRINTF_ARG(s_writerId), s_wszWriterName ); } VSS_STANDARD_CATCH(ft) return ft.hr; } HRESULT STDMETHODCALLTYPE CComRegDBWriter::Uninitialize() { CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::Uninitialize"); return Unsubscribe(); } // handle request for WRITER_METADATA // implements CVssWriter::OnIdentify bool STDMETHODCALLTYPE CComRegDBWriter::OnIdentify(IVssCreateWriterMetadata *pMetadata) { CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::OnIdentify"); try { // Converted to CUSTOM restore method (see bug# 688278) #if 0 // // Set the restore method for the writer // ft.hr = pMetadata->SetRestoreMethod ( VSS_RME_RESTORE_IF_CAN_REPLACE, NULL, NULL, VSS_WRE_ALWAYS, // writer always involved false ); ft.CheckForErrorInternal(VSSDBG_WRITER, L"IVssCreateWriterMetadata::SetRestoreMethod"); #endif // // Set the restore method for the writer // ft.hr = pMetadata->SetRestoreMethod ( VSS_RME_CUSTOM, NULL, NULL, VSS_WRE_NEVER, // writer never involved in restore false ); ft.CheckForErrorInternal(VSSDBG_WRITER, L"IVssCreateWriterMetadata::SetRestoreMethod"); // // Exclude original location of COM+ RegDB files // ft.hr = pMetadata->AddExcludeFiles ( s_cwsSystemComRegDBDir, L"*", true ); ft.CheckForErrorInternal(VSSDBG_WRITER, L"IVssCreateWriterMetadata::AddExcludeFiles"); // // Add the one file-group component // ft.hr = pMetadata->AddComponent ( VSS_CT_FILEGROUP, NULL, s_wszComponentName, s_wszComponentName, NULL, // icon 0, false, false, true ); ft.CheckForErrorInternal(VSSDBG_WRITER, L"IVssCreateWriterMetadata::AddComponent"); // // Add files to the file group // ft.hr = pMetadata->AddFilesToFileGroup ( NULL, s_wszComponentName, s_cwsComRegDBTargetDir, L"*", true, NULL ); ft.CheckForErrorInternal(VSSDBG_WRITER, L"IVssCreateWriterMetadata::AddFilesToFileGroup"); } VSS_STANDARD_CATCH(ft) TranslateWriterError(ft.hr); return ft.HrSucceeded(); } // Check whether the writer component is selected // implements CVssWriter::OnPrepareSnapshot bool STDMETHODCALLTYPE CComRegDBWriter::OnPrepareBackup(IN IVssWriterComponents *pWriterComponents) { CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::OnPrepareBackup"); try { m_bPerformSnapshot = FALSE; if ( AreComponentsSelected() ) { // We are in component selection mode... UINT cComponents; ft.hr = pWriterComponents->GetComponentCount( &cComponents ); ft.CheckForErrorInternal( VSSDBG_WRITER, L"IVssWriterComponents::GetComponentCount" ); if (cComponents > 0) // This writer has only one component so this means the component is selected m_bPerformSnapshot = TRUE; } } VSS_STANDARD_CATCH( ft ); TranslateWriterError(ft.hr); ft.Trace(VSSDBG_WRITER, L"CComRegDBWriter returning with hresult 0x%08lx", ft.hr); return ft.HrSucceeded(); } // Backup the database // implements CVssWriter::OnPrepareSnapshot bool STDMETHODCALLTYPE CComRegDBWriter::OnPrepareSnapshot() { CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::OnPrepareSnapshot"); HMODULE hRegDbDll = NULL; try { CBsString cwsExpanded; if (!cwsExpanded.ExpandEnvironmentStrings(s_cwsComRegDBTargetDir)) { ft.TranslateError(VSSDBG_WRITER, HRESULT_FROM_WIN32(::GetLastError()), L"CBsString::ExpandEnvironmentStrings"); } // Make sure that we don't backup up old stuff in the COM+ RegDB backup dir // (Do this even if this writer is not supposed to spit such that we don't backup old data) ft.hr = ::RemoveDirectoryTree(cwsExpanded); ft.CheckForErrorInternal(VSSDBG_WRITER, L"::RemoveDirectoryTree"); ft.hr = ::CreateTargetPath(cwsExpanded); ft.CheckForErrorInternal(VSSDBG_WRITER, L"::CreateTargetPath"); // Spit only if component is selected or this is a BootableSystemState backup if ( !m_bPerformSnapshot && IsBootableSystemStateBackedUp() ) m_bPerformSnapshot = TRUE; if (m_bPerformSnapshot) { // // Backup the Com+ RegDB // // Load DLL and Get backup-func address hRegDbDll = LoadLibrary(L"catsrvut.dll"); if (hRegDbDll == NULL) ft.TranslateError(VSSDBG_WRITER, HRESULT_FROM_WIN32(::GetLastError()), L"LoadLibrary catsrvut.dll"); PF_REG_DB_API pfRegDBBackup; pfRegDBBackup = (PF_REG_DB_API)GetProcAddress(hRegDbDll, "RegDBBackup"); if (pfRegDBBackup == NULL) ft.TranslateError(VSSDBG_WRITER, HRESULT_FROM_WIN32(::GetLastError()), L"GetProcAddress RegDBBackup catsrvut.dll"); // Add file-name to backup path cwsExpanded += s_cwsBackupFilename; // Call backup function CComBSTR bstrExpanded = cwsExpanded; if (bstrExpanded.Length() == 0) ft.Throw(VSSDBG_WRITER, E_OUTOFMEMORY, L"Memory allocation error"); ft.hr = pfRegDBBackup(bstrExpanded); if (ft.HrFailed()) ft.TranslateGenericError ( VSSDBG_WRITER, ft.hr, L"::RegDBBackup(%s)", bstrExpanded ); } } VSS_STANDARD_CATCH( ft ); TranslateWriterError(ft.hr); if (hRegDbDll != NULL) { FreeLibrary(hRegDbDll); hRegDbDll = NULL; } ft.Trace(VSSDBG_WRITER, L"CComRegDBWriter returning with hresult 0x%08lx", ft.hr); return ft.HrSucceeded(); } bool STDMETHODCALLTYPE CComRegDBWriter::OnFreeze() { CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::OnFreeze"); return ft.HrSucceeded(); } bool STDMETHODCALLTYPE CComRegDBWriter::OnThaw() { CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::OnThaw"); return ft.HrSucceeded(); } bool STDMETHODCALLTYPE CComRegDBWriter::OnAbort() { CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::OnAbort"); return ft.HrSucceeded(); } // Converted to CUSTOM restore method (see bug# 688278) #if 0 // Clean-up restore directory // implements CVssWriter::OnPreRestore bool STDMETHODCALLTYPE CComRegDBWriter::OnPreRestore(IVssWriterComponents *pComponent) { UNREFERENCED_PARAMETER(pComponent); CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::OnPreRestore"); try { // // Note: There is no need to check for selected components here since the writer is being // called during restore only if its component (there's only one) is being selected // CBsString cwsExpanded; if (!cwsExpanded.ExpandEnvironmentStrings(s_cwsComRegDBTargetDir)) { ft.TranslateError(VSSDBG_WRITER, HRESULT_FROM_WIN32(::GetLastError()), L"CBsString::ExpandEnvironmentStrings"); } // make sure that we don't mix up old stuff with the new restore in the COM+ RegDB backup dir ft.hr = ::RemoveDirectoryTree(cwsExpanded); ft.CheckForErrorInternal(VSSDBG_WRITER, L"::RemoveDirectoryTree"); ft.hr = ::CreateTargetPath(cwsExpanded); ft.CheckForErrorInternal(VSSDBG_WRITER, L"::CreateTargetPath"); } VSS_STANDARD_CATCH( ft ); TranslateWriterError(ft.hr); ft.Trace(VSSDBG_WRITER, L"CComRegDBWriter returning with hresult 0x%08lx", ft.hr); return ft.HrSucceeded(); } // Restore files using a legacy COM+ API // implements CVssWriter::OnPostRestore bool STDMETHODCALLTYPE CComRegDBWriter::OnPostRestore(IVssWriterComponents *pComponent) { UNREFERENCED_PARAMETER(pComponent); CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::OnPostRestore"); HMODULE hRegDbDll = NULL; try { // // Note: There is no need to check for selected components here since the writer is being // called during restore only if its component (there's only one) is being selected // CBsString cwsExpanded; if (!cwsExpanded.ExpandEnvironmentStrings(s_cwsComRegDBTargetDir)) { ft.TranslateError(VSSDBG_WRITER, HRESULT_FROM_WIN32(::GetLastError()), L"CBsString::ExpandEnvironmentStrings"); } // Add file-name to backup path cwsExpanded += s_cwsBackupFilename; CComBSTR bstrExpanded = cwsExpanded; if (bstrExpanded.Length() == 0) ft.Throw(VSSDBG_WRITER, E_OUTOFMEMORY, L"Memory allocation error"); // // Restore the Com+ RegDB // Use similar approach to ICOMAdminCatalog::RestoreREGDB (implemented by CCatalog2::RestoreREGDB) // // Load DLL and Get restore-func address hRegDbDll = LoadLibrary(L"catsrvut.dll"); if (hRegDbDll == NULL) ft.TranslateError(VSSDBG_WRITER, HRESULT_FROM_WIN32(::GetLastError()), L"LoadLibrary catsrvut.dll"); PF_REG_DB_API pfRegDBRestore; pfRegDBRestore = (PF_REG_DB_API)GetProcAddress(hRegDbDll, "RegDBRestore"); if (pfRegDBRestore == NULL) ft.TranslateError(VSSDBG_WRITER, HRESULT_FROM_WIN32(::GetLastError()), L"GetProcAddress RegDBRestore catsrvut.dll"); // Stop the COMSysApp service (best effort - continue if we fail) StopCOMSysAppService(); // should we log a warning here? // Call restore function for (int iRetry = 0; iRetry < 5; iRetry++) { ft.hr = pfRegDBRestore(bstrExpanded); if (ft.HrSucceeded()) break; else if (ft.hr != HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)) ft.TranslateGenericError ( VSSDBG_WRITER, ft.hr, L"::RegDBRestore(%s)", bstrExpanded ); Sleep(1000); } if (! ft.HrSucceeded()) ft.TranslateGenericError ( VSSDBG_WRITER, ft.hr, L"::RegDBRestore(%s)", bstrExpanded ); } VSS_STANDARD_CATCH( ft ); TranslateWriterError(ft.hr); ft.Trace(VSSDBG_WRITER, L"CComRegDBWriter returning with hresult 0x%08lx", ft.hr); return ft.HrSucceeded(); } #endif // translate a sql writer error code into a writer error void CComRegDBWriter::TranslateWriterError( HRESULT hr ) { // // To DO: Check whether the spit backup/restore methods return any "RETRYABLE" errors // switch(hr) { default: SetWriterFailure(VSS_E_WRITERERROR_NONRETRYABLE); break; case S_OK: break; case E_OUTOFMEMORY: case HRESULT_FROM_WIN32(ERROR_DISK_FULL): case HRESULT_FROM_WIN32(ERROR_TOO_MANY_OPEN_FILES): case HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY): case HRESULT_FROM_WIN32(ERROR_NO_MORE_USER_HANDLES): SetWriterFailure(VSS_E_WRITERERROR_OUTOFRESOURCES); break; } } // Creates one instance of the Event Log writer class. If one already exists, it simple returns. // Note: This method is not MT-safe - it is not expected that it will be called twice at the same time HRESULT CComRegDBWriter::CreateWriter() { CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::CreateWriter"); if ( sm_pWriter != NULL ) return S_OK; // // Create and initialize a writer // try { sm_pWriter = new CComRegDBWriter; if (sm_pWriter == NULL) ft.Throw(VSSDBG_WRITER, E_OUTOFMEMORY, L"Allocation of CComRegDBWriter object failed."); ft.hr = sm_pWriter->Initialize(); if (! ft.HrSucceeded()) ft.Throw(VSSDBG_WRITER, ft.hr, L"Initialization of CComRegDBWriter object failed."); } VSS_STANDARD_CATCH(ft); return ft.hr; } // static void CComRegDBWriter::DestroyWriter() { CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::DestroyWriter"); if (sm_pWriter) { sm_pWriter->Uninitialize(); delete sm_pWriter; sm_pWriter = NULL; } } // // Stop the COMSysApp service if exists // HRESULT STDMETHODCALLTYPE CComRegDBWriter::StopCOMSysAppService() { CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::StopCOMSysAppService"); SC_HANDLE hSCM = NULL; SC_HANDLE hService = NULL; try { SERVICE_STATUS svcStatus; BOOL bWait = TRUE; BOOL bStop = TRUE; hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (hSCM == NULL) ft.TranslateError(VSSDBG_WRITER, HRESULT_FROM_WIN32(GetLastError()), L"::OpenSCManager"); hService = OpenService(hSCM, s_wszCOMSysAppServiceName, (SERVICE_STOP | SERVICE_QUERY_STATUS)); if (hService == NULL) { DWORD dwErr = GetLastError(); switch (dwErr) { case ERROR_SERVICE_DOES_NOT_EXIST: // No such service (maybe ASR mode, service is not registered yet), no need to stop or wait bStop = FALSE; bWait = FALSE; break; default: // Any other error is unexpected ft.TranslateError(VSSDBG_WRITER, HRESULT_FROM_WIN32(dwErr), L"::OpenService"); break; } } // Stop the service if (bStop) { // Retry couple of times if the service caanot accept controls for (int iRetry = 0; iRetry < 5; iRetry++) { memset(&svcStatus, 0, sizeof(svcStatus)); if (! ControlService(hService, SERVICE_CONTROL_STOP, &svcStatus)) { DWORD dwErr = GetLastError(); switch (dwErr) { case ERROR_SERVICE_NOT_ACTIVE: // Service is not running, no need to wait bWait = FALSE; break; case ERROR_SERVICE_CANNOT_ACCEPT_CTRL: // Service may be at STOP_PENDING or START_PENDING, retry break; default: // Any other error is unexpected ft.TranslateError(VSSDBG_WRITER, HRESULT_FROM_WIN32(dwErr), L"::ControlService"); break; } if (! bWait) // Service not active break; // Sleep before retry Sleep(2000); } else { // Success, no need to retry break; } } } if (bWait) { // Wait till service is stopped. 30 seconds max DWORD dwWaitTime = 0; while (TRUE) { memset(&svcStatus, 0, sizeof(svcStatus)); if (! QueryServiceStatus(hService, &svcStatus)) ft.TranslateError(VSSDBG_WRITER, HRESULT_FROM_WIN32(GetLastError()), L"::QueryServiceStatus"); if (svcStatus.dwCurrentState == SERVICE_STOPPED) break; Sleep(1000); dwWaitTime += 1000; if (dwWaitTime >= (30 * 1000)) ft.TranslateGenericError( VSSDBG_WRITER, HRESULT_FROM_WIN32(ERROR_SERVICE_CANNOT_ACCEPT_CTRL), L"ControlService (%s, SERVICE_CONTROL_STOP)", s_wszCOMSysAppServiceName ); } } } VSS_STANDARD_CATCH( ft ); if (hService != NULL) { CloseServiceHandle(hService); hService = NULL; } if (hSCM != NULL) { CloseServiceHandle(hSCM); hSCM = NULL; } return (ft.hr); }