Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

667 lines
20 KiB

  1. f/*++
  2. Copyright (c) 2002 Microsoft Corporation
  3. Abstract:
  4. @doc
  5. @module comregdbwriter.cxx | Implementation of the COM+ RegDB Writer
  6. @end
  7. Author:
  8. Ran Kalach [rankala] 05/17/2002
  9. Revision History:
  10. Name Date Comments
  11. rankala 05/17/2002 created
  12. --*/
  13. #include "stdafx.h"
  14. #include "vs_idl.hxx"
  15. #include "vs_hash.hxx"
  16. #include <vswriter.h>
  17. #include <Sddl.h>
  18. #include "vs_seh.hxx"
  19. #include "vs_trace.hxx"
  20. #include "vs_debug.hxx"
  21. #include "vs_reg.hxx"
  22. #include "comregdbwriter.hxx"
  23. #include "allerror.h"
  24. ////////////////////////////////////////////////////////////////////////
  25. // Standard foo for file name aliasing. This code block must be after
  26. // all includes of VSS header files.
  27. //
  28. #ifdef VSS_FILE_ALIAS
  29. #undef VSS_FILE_ALIAS
  30. #endif
  31. #define VSS_FILE_ALIAS "WRTCDBRC"
  32. //
  33. ////////////////////////////////////////////////////////////////////////
  34. static GUID s_writerId =
  35. {
  36. 0x542da469, 0xd3e1, 0x473c, 0x9f, 0x4f, 0x78, 0x47, 0xf0, 0x1f, 0xc6, 0x4f
  37. };
  38. // The display name of the Event Log writer.
  39. static LPCWSTR s_wszWriterName = L"COM+ REGDB Writer";
  40. // The file group component name for the Event Log
  41. static LPCWSTR s_wszComponentName = L"COM+ REGDB";
  42. // The original location of the RegDB files
  43. static CBsString s_cwsSystemComRegDBDir = L"%SystemRoot%\\Registration";
  44. // The backup/restore location of the RegDB files
  45. static CBsString s_cwsComRegDBTargetDir = ROOT_BACKUP_DIR BOOTABLE_STATE_SUBDIR L"\\ComRegistrationDatabase";
  46. // The backup/restore file name of the RegDB database
  47. static CBsString s_cwsBackupFilename = L"\\ComRegDb.bak";
  48. // The COM+ System Applicationservice name
  49. static LPCWSTR s_wszCOMSysAppServiceName = L"COMSysApp";
  50. // Class wide member variables
  51. CComRegDBWriter *CComRegDBWriter::sm_pWriter = NULL;
  52. // RegDB function declarations
  53. typedef HRESULT (*PF_REG_DB_API)(BSTR);
  54. //
  55. // Methods
  56. //
  57. HRESULT STDMETHODCALLTYPE CComRegDBWriter::Initialize()
  58. {
  59. CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::Initialize");
  60. try
  61. {
  62. ft.hr = CVssWriter::Initialize
  63. (
  64. s_writerId,
  65. s_wszWriterName,
  66. VSS_UT_BOOTABLESYSTEMSTATE,
  67. VSS_ST_OTHER,
  68. VSS_APP_SYSTEM,
  69. 60000
  70. );
  71. if (ft.HrFailed())
  72. ft.TranslateGenericError
  73. (
  74. VSSDBG_WRITER,
  75. ft.hr,
  76. L"CVssWriter::Initialize(" WSTR_GUID_FMT L", %s)",
  77. GUID_PRINTF_ARG(s_writerId),
  78. s_wszWriterName
  79. );
  80. ft.hr = Subscribe();
  81. if (ft.HrFailed())
  82. ft.TranslateGenericError
  83. (
  84. VSSDBG_WRITER,
  85. ft.hr,
  86. L"CVssWriter::Subscribe for" WSTR_GUID_FMT L" %s)",
  87. GUID_PRINTF_ARG(s_writerId),
  88. s_wszWriterName
  89. );
  90. }
  91. VSS_STANDARD_CATCH(ft)
  92. return ft.hr;
  93. }
  94. HRESULT STDMETHODCALLTYPE CComRegDBWriter::Uninitialize()
  95. {
  96. CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::Uninitialize");
  97. return Unsubscribe();
  98. }
  99. // handle request for WRITER_METADATA
  100. // implements CVssWriter::OnIdentify
  101. bool STDMETHODCALLTYPE CComRegDBWriter::OnIdentify(IVssCreateWriterMetadata *pMetadata)
  102. {
  103. CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::OnIdentify");
  104. try
  105. {
  106. // Converted to CUSTOM restore method (see bug# 688278)
  107. #if 0
  108. //
  109. // Set the restore method for the writer
  110. //
  111. ft.hr = pMetadata->SetRestoreMethod
  112. (
  113. VSS_RME_RESTORE_IF_CAN_REPLACE,
  114. NULL,
  115. NULL,
  116. VSS_WRE_ALWAYS, // writer always involved
  117. false
  118. );
  119. ft.CheckForErrorInternal(VSSDBG_WRITER, L"IVssCreateWriterMetadata::SetRestoreMethod");
  120. #endif
  121. //
  122. // Set the restore method for the writer
  123. //
  124. ft.hr = pMetadata->SetRestoreMethod
  125. (
  126. VSS_RME_CUSTOM,
  127. NULL,
  128. NULL,
  129. VSS_WRE_NEVER, // writer never involved in restore
  130. false
  131. );
  132. ft.CheckForErrorInternal(VSSDBG_WRITER, L"IVssCreateWriterMetadata::SetRestoreMethod");
  133. //
  134. // Exclude original location of COM+ RegDB files
  135. //
  136. ft.hr = pMetadata->AddExcludeFiles
  137. (
  138. s_cwsSystemComRegDBDir,
  139. L"*",
  140. true
  141. );
  142. ft.CheckForErrorInternal(VSSDBG_WRITER, L"IVssCreateWriterMetadata::AddExcludeFiles");
  143. //
  144. // Add the one file-group component
  145. //
  146. ft.hr = pMetadata->AddComponent
  147. (
  148. VSS_CT_FILEGROUP,
  149. NULL,
  150. s_wszComponentName,
  151. s_wszComponentName,
  152. NULL, // icon
  153. 0,
  154. false,
  155. false,
  156. true
  157. );
  158. ft.CheckForErrorInternal(VSSDBG_WRITER, L"IVssCreateWriterMetadata::AddComponent");
  159. //
  160. // Add files to the file group
  161. //
  162. ft.hr = pMetadata->AddFilesToFileGroup
  163. (
  164. NULL,
  165. s_wszComponentName,
  166. s_cwsComRegDBTargetDir,
  167. L"*",
  168. true,
  169. NULL
  170. );
  171. ft.CheckForErrorInternal(VSSDBG_WRITER, L"IVssCreateWriterMetadata::AddFilesToFileGroup");
  172. }
  173. VSS_STANDARD_CATCH(ft)
  174. TranslateWriterError(ft.hr);
  175. return ft.HrSucceeded();
  176. }
  177. // Check whether the writer component is selected
  178. // implements CVssWriter::OnPrepareSnapshot
  179. bool STDMETHODCALLTYPE CComRegDBWriter::OnPrepareBackup(IN IVssWriterComponents *pWriterComponents)
  180. {
  181. CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::OnPrepareBackup");
  182. try
  183. {
  184. m_bPerformSnapshot = FALSE;
  185. if ( AreComponentsSelected() )
  186. {
  187. // We are in component selection mode...
  188. UINT cComponents;
  189. ft.hr = pWriterComponents->GetComponentCount( &cComponents );
  190. ft.CheckForErrorInternal( VSSDBG_WRITER, L"IVssWriterComponents::GetComponentCount" );
  191. if (cComponents > 0)
  192. // This writer has only one component so this means the component is selected
  193. m_bPerformSnapshot = TRUE;
  194. }
  195. }
  196. VSS_STANDARD_CATCH( ft );
  197. TranslateWriterError(ft.hr);
  198. ft.Trace(VSSDBG_WRITER, L"CComRegDBWriter returning with hresult 0x%08lx", ft.hr);
  199. return ft.HrSucceeded();
  200. }
  201. // Backup the database
  202. // implements CVssWriter::OnPrepareSnapshot
  203. bool STDMETHODCALLTYPE CComRegDBWriter::OnPrepareSnapshot()
  204. {
  205. CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::OnPrepareSnapshot");
  206. HMODULE hRegDbDll = NULL;
  207. try
  208. {
  209. CBsString cwsExpanded;
  210. if (!cwsExpanded.ExpandEnvironmentStrings(s_cwsComRegDBTargetDir))
  211. {
  212. ft.TranslateError(VSSDBG_WRITER, HRESULT_FROM_WIN32(::GetLastError()),
  213. L"CBsString::ExpandEnvironmentStrings");
  214. }
  215. // Make sure that we don't backup up old stuff in the COM+ RegDB backup dir
  216. // (Do this even if this writer is not supposed to spit such that we don't backup old data)
  217. ft.hr = ::RemoveDirectoryTree(cwsExpanded);
  218. ft.CheckForErrorInternal(VSSDBG_WRITER, L"::RemoveDirectoryTree");
  219. ft.hr = ::CreateTargetPath(cwsExpanded);
  220. ft.CheckForErrorInternal(VSSDBG_WRITER, L"::CreateTargetPath");
  221. // Spit only if component is selected or this is a BootableSystemState backup
  222. if ( !m_bPerformSnapshot && IsBootableSystemStateBackedUp() )
  223. m_bPerformSnapshot = TRUE;
  224. if (m_bPerformSnapshot)
  225. {
  226. //
  227. // Backup the Com+ RegDB
  228. //
  229. // Load DLL and Get backup-func address
  230. hRegDbDll = LoadLibrary(L"catsrvut.dll");
  231. if (hRegDbDll == NULL)
  232. ft.TranslateError(VSSDBG_WRITER, HRESULT_FROM_WIN32(::GetLastError()),
  233. L"LoadLibrary catsrvut.dll");
  234. PF_REG_DB_API pfRegDBBackup;
  235. pfRegDBBackup = (PF_REG_DB_API)GetProcAddress(hRegDbDll, "RegDBBackup");
  236. if (pfRegDBBackup == NULL)
  237. ft.TranslateError(VSSDBG_WRITER, HRESULT_FROM_WIN32(::GetLastError()),
  238. L"GetProcAddress RegDBBackup catsrvut.dll");
  239. // Add file-name to backup path
  240. cwsExpanded += s_cwsBackupFilename;
  241. // Call backup function
  242. CComBSTR bstrExpanded = cwsExpanded;
  243. if (bstrExpanded.Length() == 0)
  244. ft.Throw(VSSDBG_WRITER, E_OUTOFMEMORY, L"Memory allocation error");
  245. ft.hr = pfRegDBBackup(bstrExpanded);
  246. if (ft.HrFailed())
  247. ft.TranslateGenericError
  248. (
  249. VSSDBG_WRITER,
  250. ft.hr,
  251. L"::RegDBBackup(%s)",
  252. bstrExpanded
  253. );
  254. }
  255. }
  256. VSS_STANDARD_CATCH( ft );
  257. TranslateWriterError(ft.hr);
  258. if (hRegDbDll != NULL) {
  259. FreeLibrary(hRegDbDll);
  260. hRegDbDll = NULL;
  261. }
  262. ft.Trace(VSSDBG_WRITER, L"CComRegDBWriter returning with hresult 0x%08lx", ft.hr);
  263. return ft.HrSucceeded();
  264. }
  265. bool STDMETHODCALLTYPE CComRegDBWriter::OnFreeze()
  266. {
  267. CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::OnFreeze");
  268. return ft.HrSucceeded();
  269. }
  270. bool STDMETHODCALLTYPE CComRegDBWriter::OnThaw()
  271. {
  272. CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::OnThaw");
  273. return ft.HrSucceeded();
  274. }
  275. bool STDMETHODCALLTYPE CComRegDBWriter::OnAbort()
  276. {
  277. CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::OnAbort");
  278. return ft.HrSucceeded();
  279. }
  280. // Converted to CUSTOM restore method (see bug# 688278)
  281. #if 0
  282. // Clean-up restore directory
  283. // implements CVssWriter::OnPreRestore
  284. bool STDMETHODCALLTYPE CComRegDBWriter::OnPreRestore(IVssWriterComponents *pComponent)
  285. {
  286. UNREFERENCED_PARAMETER(pComponent);
  287. CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::OnPreRestore");
  288. try
  289. {
  290. //
  291. // Note: There is no need to check for selected components here since the writer is being
  292. // called during restore only if its component (there's only one) is being selected
  293. //
  294. CBsString cwsExpanded;
  295. if (!cwsExpanded.ExpandEnvironmentStrings(s_cwsComRegDBTargetDir))
  296. {
  297. ft.TranslateError(VSSDBG_WRITER, HRESULT_FROM_WIN32(::GetLastError()),
  298. L"CBsString::ExpandEnvironmentStrings");
  299. }
  300. // make sure that we don't mix up old stuff with the new restore in the COM+ RegDB backup dir
  301. ft.hr = ::RemoveDirectoryTree(cwsExpanded);
  302. ft.CheckForErrorInternal(VSSDBG_WRITER, L"::RemoveDirectoryTree");
  303. ft.hr = ::CreateTargetPath(cwsExpanded);
  304. ft.CheckForErrorInternal(VSSDBG_WRITER, L"::CreateTargetPath");
  305. }
  306. VSS_STANDARD_CATCH( ft );
  307. TranslateWriterError(ft.hr);
  308. ft.Trace(VSSDBG_WRITER, L"CComRegDBWriter returning with hresult 0x%08lx", ft.hr);
  309. return ft.HrSucceeded();
  310. }
  311. // Restore files using a legacy COM+ API
  312. // implements CVssWriter::OnPostRestore
  313. bool STDMETHODCALLTYPE CComRegDBWriter::OnPostRestore(IVssWriterComponents *pComponent)
  314. {
  315. UNREFERENCED_PARAMETER(pComponent);
  316. CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::OnPostRestore");
  317. HMODULE hRegDbDll = NULL;
  318. try
  319. {
  320. //
  321. // Note: There is no need to check for selected components here since the writer is being
  322. // called during restore only if its component (there's only one) is being selected
  323. //
  324. CBsString cwsExpanded;
  325. if (!cwsExpanded.ExpandEnvironmentStrings(s_cwsComRegDBTargetDir))
  326. {
  327. ft.TranslateError(VSSDBG_WRITER, HRESULT_FROM_WIN32(::GetLastError()),
  328. L"CBsString::ExpandEnvironmentStrings");
  329. }
  330. // Add file-name to backup path
  331. cwsExpanded += s_cwsBackupFilename;
  332. CComBSTR bstrExpanded = cwsExpanded;
  333. if (bstrExpanded.Length() == 0)
  334. ft.Throw(VSSDBG_WRITER, E_OUTOFMEMORY, L"Memory allocation error");
  335. //
  336. // Restore the Com+ RegDB
  337. // Use similar approach to ICOMAdminCatalog::RestoreREGDB (implemented by CCatalog2::RestoreREGDB)
  338. //
  339. // Load DLL and Get restore-func address
  340. hRegDbDll = LoadLibrary(L"catsrvut.dll");
  341. if (hRegDbDll == NULL)
  342. ft.TranslateError(VSSDBG_WRITER, HRESULT_FROM_WIN32(::GetLastError()),
  343. L"LoadLibrary catsrvut.dll");
  344. PF_REG_DB_API pfRegDBRestore;
  345. pfRegDBRestore = (PF_REG_DB_API)GetProcAddress(hRegDbDll, "RegDBRestore");
  346. if (pfRegDBRestore == NULL)
  347. ft.TranslateError(VSSDBG_WRITER, HRESULT_FROM_WIN32(::GetLastError()),
  348. L"GetProcAddress RegDBRestore catsrvut.dll");
  349. // Stop the COMSysApp service (best effort - continue if we fail)
  350. StopCOMSysAppService(); // should we log a warning here?
  351. // Call restore function
  352. for (int iRetry = 0; iRetry < 5; iRetry++) {
  353. ft.hr = pfRegDBRestore(bstrExpanded);
  354. if (ft.HrSucceeded())
  355. break;
  356. else if (ft.hr != HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS))
  357. ft.TranslateGenericError
  358. (
  359. VSSDBG_WRITER,
  360. ft.hr,
  361. L"::RegDBRestore(%s)",
  362. bstrExpanded
  363. );
  364. Sleep(1000);
  365. }
  366. if (! ft.HrSucceeded())
  367. ft.TranslateGenericError
  368. (
  369. VSSDBG_WRITER,
  370. ft.hr,
  371. L"::RegDBRestore(%s)",
  372. bstrExpanded
  373. );
  374. }
  375. VSS_STANDARD_CATCH( ft );
  376. TranslateWriterError(ft.hr);
  377. ft.Trace(VSSDBG_WRITER, L"CComRegDBWriter returning with hresult 0x%08lx", ft.hr);
  378. return ft.HrSucceeded();
  379. }
  380. #endif
  381. // translate a sql writer error code into a writer error
  382. void CComRegDBWriter::TranslateWriterError( HRESULT hr )
  383. {
  384. //
  385. // To DO: Check whether the spit backup/restore methods return any "RETRYABLE" errors
  386. //
  387. switch(hr)
  388. {
  389. default:
  390. SetWriterFailure(VSS_E_WRITERERROR_NONRETRYABLE);
  391. break;
  392. case S_OK:
  393. break;
  394. case E_OUTOFMEMORY:
  395. case HRESULT_FROM_WIN32(ERROR_DISK_FULL):
  396. case HRESULT_FROM_WIN32(ERROR_TOO_MANY_OPEN_FILES):
  397. case HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY):
  398. case HRESULT_FROM_WIN32(ERROR_NO_MORE_USER_HANDLES):
  399. SetWriterFailure(VSS_E_WRITERERROR_OUTOFRESOURCES);
  400. break;
  401. }
  402. }
  403. // Creates one instance of the Event Log writer class. If one already exists, it simple returns.
  404. // Note: This method is not MT-safe - it is not expected that it will be called twice at the same time
  405. HRESULT CComRegDBWriter::CreateWriter()
  406. {
  407. CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::CreateWriter");
  408. if ( sm_pWriter != NULL )
  409. return S_OK;
  410. //
  411. // Create and initialize a writer
  412. //
  413. try
  414. {
  415. sm_pWriter = new CComRegDBWriter;
  416. if (sm_pWriter == NULL)
  417. ft.Throw(VSSDBG_WRITER, E_OUTOFMEMORY, L"Allocation of CComRegDBWriter object failed.");
  418. ft.hr = sm_pWriter->Initialize();
  419. if (! ft.HrSucceeded())
  420. ft.Throw(VSSDBG_WRITER, ft.hr, L"Initialization of CComRegDBWriter object failed.");
  421. }
  422. VSS_STANDARD_CATCH(ft);
  423. return ft.hr;
  424. }
  425. // static
  426. void CComRegDBWriter::DestroyWriter()
  427. {
  428. CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::DestroyWriter");
  429. if (sm_pWriter)
  430. {
  431. sm_pWriter->Uninitialize();
  432. delete sm_pWriter;
  433. sm_pWriter = NULL;
  434. }
  435. }
  436. //
  437. // Stop the COMSysApp service if exists
  438. //
  439. HRESULT STDMETHODCALLTYPE CComRegDBWriter::StopCOMSysAppService()
  440. {
  441. CVssFunctionTracer ft(VSSDBG_WRITER, L"CComRegDBWriter::StopCOMSysAppService");
  442. SC_HANDLE hSCM = NULL;
  443. SC_HANDLE hService = NULL;
  444. try
  445. {
  446. SERVICE_STATUS svcStatus;
  447. BOOL bWait = TRUE;
  448. BOOL bStop = TRUE;
  449. hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
  450. if (hSCM == NULL)
  451. ft.TranslateError(VSSDBG_WRITER, HRESULT_FROM_WIN32(GetLastError()), L"::OpenSCManager");
  452. hService = OpenService(hSCM, s_wszCOMSysAppServiceName, (SERVICE_STOP | SERVICE_QUERY_STATUS));
  453. if (hService == NULL) {
  454. DWORD dwErr = GetLastError();
  455. switch (dwErr) {
  456. case ERROR_SERVICE_DOES_NOT_EXIST:
  457. // No such service (maybe ASR mode, service is not registered yet), no need to stop or wait
  458. bStop = FALSE;
  459. bWait = FALSE;
  460. break;
  461. default:
  462. // Any other error is unexpected
  463. ft.TranslateError(VSSDBG_WRITER, HRESULT_FROM_WIN32(dwErr), L"::OpenService");
  464. break;
  465. }
  466. }
  467. // Stop the service
  468. if (bStop) {
  469. // Retry couple of times if the service caanot accept controls
  470. for (int iRetry = 0; iRetry < 5; iRetry++) {
  471. memset(&svcStatus, 0, sizeof(svcStatus));
  472. if (! ControlService(hService, SERVICE_CONTROL_STOP, &svcStatus)) {
  473. DWORD dwErr = GetLastError();
  474. switch (dwErr) {
  475. case ERROR_SERVICE_NOT_ACTIVE:
  476. // Service is not running, no need to wait
  477. bWait = FALSE;
  478. break;
  479. case ERROR_SERVICE_CANNOT_ACCEPT_CTRL:
  480. // Service may be at STOP_PENDING or START_PENDING, retry
  481. break;
  482. default:
  483. // Any other error is unexpected
  484. ft.TranslateError(VSSDBG_WRITER, HRESULT_FROM_WIN32(dwErr), L"::ControlService");
  485. break;
  486. }
  487. if (! bWait)
  488. // Service not active
  489. break;
  490. // Sleep before retry
  491. Sleep(2000);
  492. } else {
  493. // Success, no need to retry
  494. break;
  495. }
  496. }
  497. }
  498. if (bWait) {
  499. // Wait till service is stopped. 30 seconds max
  500. DWORD dwWaitTime = 0;
  501. while (TRUE) {
  502. memset(&svcStatus, 0, sizeof(svcStatus));
  503. if (! QueryServiceStatus(hService, &svcStatus))
  504. ft.TranslateError(VSSDBG_WRITER, HRESULT_FROM_WIN32(GetLastError()), L"::QueryServiceStatus");
  505. if (svcStatus.dwCurrentState == SERVICE_STOPPED)
  506. break;
  507. Sleep(1000);
  508. dwWaitTime += 1000;
  509. if (dwWaitTime >= (30 * 1000))
  510. ft.TranslateGenericError(
  511. VSSDBG_WRITER,
  512. HRESULT_FROM_WIN32(ERROR_SERVICE_CANNOT_ACCEPT_CTRL),
  513. L"ControlService (%s, SERVICE_CONTROL_STOP)",
  514. s_wszCOMSysAppServiceName
  515. );
  516. }
  517. }
  518. }
  519. VSS_STANDARD_CATCH( ft );
  520. if (hService != NULL) {
  521. CloseServiceHandle(hService);
  522. hService = NULL;
  523. }
  524. if (hSCM != NULL) {
  525. CloseServiceHandle(hSCM);
  526. hSCM = NULL;
  527. }
  528. return (ft.hr);
  529. }