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.

1545 lines
60 KiB

  1. /*
  2. **++
  3. **
  4. ** Copyright (c) 2002 Microsoft Corporation
  5. **
  6. **
  7. ** Module Name:
  8. **
  9. ** swriter.cpp
  10. **
  11. **
  12. ** Abstract:
  13. **
  14. ** Test program to to register a Writer with various properties
  15. **
  16. ** Author:
  17. **
  18. ** Reuven Lax [reuvenl] 04-June-2002
  19. **
  20. **
  21. ** Revision History:
  22. **
  23. **--
  24. */
  25. ///////////////////////////////////////////////////////////////////////////////
  26. // Includes
  27. #include "stdafx.h"
  28. #include "swriter.h"
  29. #include "utility.h"
  30. #include "writerconfig.h"
  31. #include <string>
  32. #include <sstream>
  33. #include <functional>
  34. #include <algorithm>
  35. #include <queue>
  36. ///////////////////////////////////////////////////////////////////////////////
  37. // Declarations and Definitions
  38. using std::wstring;
  39. using std::string;
  40. using std::wstringstream;
  41. using std::exception;
  42. using std::vector;
  43. using Utility::checkReturn;
  44. using Utility::warnReturn;
  45. using Utility::printStatus;
  46. static const wchar_t* const BackupString = L"BACKUP";
  47. static const wchar_t* const RestoreString = L"RESTORE";
  48. ///////////////////////////////////////////////////////////////////////////////
  49. // Initialize the test writer
  50. HRESULT STDMETHODCALLTYPE TestWriter::Initialize()
  51. {
  52. WriterConfiguration* config = WriterConfiguration::instance();
  53. printStatus(L"Initializing Writer", Utility::high);
  54. HRESULT hr = CVssWriter::Initialize(TestWriterId, // WriterID
  55. TestWriterName, // wszWriterName
  56. config->usage(), // ut
  57. VSS_ST_OTHER); // st
  58. checkReturn(hr, L"CVssWriter::Initialize");
  59. hr = Subscribe();
  60. checkReturn(hr, L"CVssWriter::Subscribe");
  61. return S_OK;
  62. }
  63. // OnIdentify is called as a result of the requestor calling GatherWriterMetadata
  64. // Here we report the writer metadata using the passed-in interface
  65. bool STDMETHODCALLTYPE TestWriter::OnIdentify(IN IVssCreateWriterMetadata *pMetadata)
  66. try
  67. {
  68. enterEvent(Utility::Identify);
  69. WriterConfiguration* config = WriterConfiguration::instance();
  70. // set the restore method properly
  71. RestoreMethod method = config->restoreMethod();
  72. HRESULT hr = pMetadata->SetRestoreMethod(method.m_method,
  73. method.m_service.c_str(),
  74. NULL,
  75. method.m_writerRestore,
  76. method.m_rebootRequired);
  77. checkReturn(hr, L"IVssCreateWriterMetadata::SetRestoreMethod");
  78. printStatus(L"\nSet restore method: ", Utility::high);
  79. printStatus(method.toString(), Utility::high);
  80. // set the alternate-location list
  81. RestoreMethod::AlternateList::iterator currentAlt = method.m_alternateLocations.begin();
  82. while (currentAlt != method.m_alternateLocations.end()) {
  83. hr = pMetadata->AddAlternateLocationMapping(currentAlt->m_path. c_str(),
  84. currentAlt->m_filespec.c_str(),
  85. currentAlt->m_recursive,
  86. currentAlt->m_alternatePath.substr(0, currentAlt->m_alternatePath.size()-1).c_str());
  87. checkReturn(hr, L"IVssCreateWriterMetadata::AddAlternateLocationMapping");
  88. printStatus(L"\nAdded Alternate Location Mapping");
  89. printStatus(currentAlt->toString());
  90. ++currentAlt;
  91. }
  92. // set the exclude-file list
  93. WriterConfiguration::ExcludeFileList::iterator currentExclude = config->excludeFiles().begin();
  94. while (currentExclude != config->excludeFiles().end()) {
  95. hr = pMetadata->AddExcludeFiles(currentExclude->m_path.c_str(),
  96. currentExclude->m_filespec.c_str(),
  97. currentExclude->m_recursive);
  98. checkReturn(hr, L"IVssCreateWriterMetadata::AddExcludeFiles");
  99. printStatus(L"\nAdded exclude filespec");
  100. printStatus(currentExclude->toString());
  101. ++currentExclude;
  102. }
  103. // add all necessary components
  104. WriterConfiguration::ComponentList::iterator currentComponent = config->components().begin();
  105. while (currentComponent != config->components().end()) {
  106. addComponent(*currentComponent, pMetadata);
  107. ++currentComponent;
  108. }
  109. return true;
  110. }
  111. catch(const exception& thrown)
  112. {
  113. printStatus(string("Failure in Identify event: ") + thrown.what(), Utility::low);
  114. return false;
  115. }
  116. catch(HRESULT error)
  117. {
  118. Utility::TestWriterException e(error);
  119. printStatus(e.what(), Utility::low);
  120. return false;
  121. };
  122. // This function is called as a result of the requestor calling PrepareForBackup
  123. // Here we do some checking to ensure that the requestor selected components properly
  124. bool STDMETHODCALLTYPE TestWriter::OnPrepareBackup(IN IVssWriterComponents *pComponents)
  125. try
  126. {
  127. enterEvent(Utility::PrepareForBackup);
  128. WriterConfiguration* config = WriterConfiguration::instance();
  129. // get the number of components
  130. UINT numComponents = 0;
  131. HRESULT hr = pComponents->GetComponentCount(&numComponents);
  132. checkReturn(hr, L"IVssWriterComponents::GetComponentCount");
  133. // we haven't defined CUSTOM restore method for this writer. consequently, backup apps should
  134. // ignore it.
  135. if ((config->restoreMethod().m_method == VSS_RME_CUSTOM) &&
  136. numComponents > 0) {
  137. throw Utility::TestWriterException(L"Components were selected for backup when CUSTOM restore"
  138. L" method was used. This is incorrect");
  139. }
  140. m_selectedComponents.clear();
  141. // for each component that was added
  142. for (unsigned int x = 0; x < numComponents; x++) {
  143. // --- get the relevant information
  144. CComPtr<IVssComponent> pComponent;
  145. hr = pComponents->GetComponent(x, &pComponent);
  146. checkReturn(hr, L"IVssWriterComponents::GetComponent");
  147. writeBackupMetadata(pComponent); // --- write private metadata
  148. // --- find the component in the metadata document.
  149. // --- this component may actually be a supercomponent of something
  150. // --- listed in the metadata document, so we must handle that case.
  151. // --- this is no longer true with the new interface changes... now, only components
  152. // --- in the metadata doc can be added
  153. ComponentBase identity(getPath(pComponent), getName(pComponent));
  154. WriterConfiguration::ComponentList::iterator found =
  155. std::find(config->components().begin(),
  156. config->components().end(),
  157. identity);
  158. if (found == config->components().end()) {
  159. wstringstream msg;
  160. msg << L"Component with logical path: " << identity.m_logicalPath <<
  161. L" and name: " << identity.m_name << L" was added to the document" << std::endl <<
  162. L", but does not appear in the writer metadata";
  163. printStatus(msg.str());
  164. } else if (!addableComponent(*found)) {
  165. wstringstream msg;
  166. msg << L"Component with logical path: " << identity.m_logicalPath <<
  167. L" and name: " << identity.m_name << L" was added to the document" << std::endl <<
  168. L", but is not a selectable component";
  169. printStatus(msg.str());
  170. } else {
  171. m_selectedComponents.push_back(*found);
  172. }
  173. }
  174. // any non-selectable component with no selectable ancestor must be added. Check this.
  175. vector<Component> mustAddComponents;
  176. buildContainer_if(config->components().begin(),
  177. config->components().end(),
  178. std::back_inserter(mustAddComponents),
  179. Utility::and1(std::not1(std::ptr_fun(isComponentSelectable)),
  180. std::ptr_fun(addableComponent)));
  181. vector<Component>::iterator currentMust = mustAddComponents.begin();
  182. while (currentMust != mustAddComponents.end()) {
  183. if (std::find(m_selectedComponents.begin(),
  184. m_selectedComponents.end(),
  185. *currentMust) == m_selectedComponents.end()) {
  186. wstringstream msg;
  187. msg << L"\nComponent with logical path: " << currentMust->m_logicalPath <<
  188. L" and name: " << currentMust->m_name <<
  189. L" is a non-selectable component with no selectable ancestor, and therefore " <<
  190. L" must be added to the document. However, it was not added";
  191. printStatus(msg.str());
  192. }
  193. ++currentMust;
  194. }
  195. return true;
  196. }
  197. catch(const exception& thrown)
  198. {
  199. printStatus(string("Failure in PrepareForBackup event: ") + thrown.what(), Utility::low);
  200. return false;
  201. }
  202. catch(HRESULT error)
  203. {
  204. Utility::TestWriterException e(error);
  205. printStatus(e.what(), Utility::low);
  206. return false;
  207. };
  208. // This function is called after a requestor calls DoSnapshotSet
  209. // Here we ensure that the requestor has added the appropriate volumes to the
  210. // snapshot set. If a spit directory is specified, the spit is done here as well.
  211. bool STDMETHODCALLTYPE TestWriter::OnPrepareSnapshot()
  212. try
  213. {
  214. enterEvent(Utility::PrepareForSnapshot);
  215. // build the list of all files being backed up
  216. vector<TargetedFile> componentFiles;
  217. std::pointer_to_binary_function<Component, std::back_insert_iterator<vector<TargetedFile> >, void>
  218. ptrFun(buildComponentFiles);
  219. std::for_each(m_selectedComponents.begin(),
  220. m_selectedComponents.end(),
  221. std::bind2nd(ptrFun, std::back_inserter(componentFiles)));
  222. // for every file being backed up
  223. vector<TargetedFile>::iterator currentFile = componentFiles.begin();
  224. while (currentFile != componentFiles.end()) {
  225. // --- ensure the filespec has been snapshot, taking care of mount points
  226. if (!checkPathAffected(*currentFile)) {
  227. wstringstream msg;
  228. msg << L"Filespec " << currentFile->m_path << currentFile->m_filespec <<
  229. L"is selected for backup but contains files that have not been snapshot" << std::endl;
  230. printStatus(msg.str());
  231. }
  232. // --- if a spit is needed, spit the file to the proper directory
  233. if (!currentFile->m_alternatePath.empty())
  234. spitFiles(*currentFile);
  235. ++currentFile;
  236. }
  237. return true;
  238. }
  239. catch(const exception& thrown)
  240. {
  241. printStatus(string("Failure in PrepareForSnapshot event: ") + thrown.what(), Utility::low);
  242. return false;
  243. }
  244. catch(HRESULT error)
  245. {
  246. Utility::TestWriterException e(error);
  247. printStatus(e.what(), Utility::low);
  248. return false;
  249. }
  250. // This function is called after a requestor calls DoSnapshotSet
  251. // Currently, we don't do much here that is interesting.
  252. bool STDMETHODCALLTYPE TestWriter::OnFreeze()
  253. try
  254. {
  255. enterEvent(Utility::Freeze);
  256. return true;
  257. }
  258. catch(const exception& thrown)
  259. {
  260. printStatus(string("Failure in Freeze event: ") + thrown.what(), Utility::low);
  261. return false;
  262. }
  263. catch(HRESULT error)
  264. {
  265. Utility::TestWriterException e(error);
  266. printStatus(e.what(), Utility::low);
  267. return false;
  268. }
  269. // This function is called after a requestor calls DoSnapshotSet
  270. // Currently, we don't do much here that is interesting.
  271. bool STDMETHODCALLTYPE TestWriter::OnThaw()
  272. try
  273. {
  274. enterEvent(Utility::Thaw);
  275. return true;
  276. }
  277. catch(const exception& thrown)
  278. {
  279. printStatus(string("Failure in Thaw event: ") + thrown.what(), Utility::low);
  280. return false;
  281. }
  282. catch(HRESULT error)
  283. {
  284. Utility::TestWriterException e(error);
  285. printStatus(e.what(), Utility::low);
  286. return false;
  287. }
  288. // This function is called after a requestor calls DoSnapshotSet
  289. // Here we cleanup the files that were spit in OnPrepareSnapshot
  290. // and do some basic sanity checking
  291. bool STDMETHODCALLTYPE TestWriter::OnPostSnapshot(IN IVssWriterComponents *pComponents)
  292. try
  293. {
  294. enterEvent(Utility::PostSnapshot);
  295. cleanupFiles();
  296. // get the number of components
  297. UINT numComponents = 0;
  298. HRESULT hr = pComponents->GetComponentCount(&numComponents);
  299. checkReturn(hr, L"IVssWriterComponents::GetComponentCount");
  300. // for each component that was added
  301. for (unsigned int x = 0; x < numComponents; x++) {
  302. // --- get the relevant information
  303. CComPtr<IVssComponent> pComponent;
  304. hr = pComponents->GetComponent(x, &pComponent);
  305. checkReturn(hr, L"IVssWriterComponents::GetComponent");
  306. // --- ensure that the component was backed up
  307. ComponentBase identity(getPath(pComponent), getName(pComponent));
  308. vector<Component>::iterator found = std::find(m_selectedComponents.begin(),
  309. m_selectedComponents.end(),
  310. identity);
  311. if (found == m_selectedComponents.end()) {
  312. wstringstream msg;
  313. msg << L"Component with logical path: " << identity.m_logicalPath <<
  314. L"and name: " << identity.m_name <<
  315. L"was selected in PostSnapshot, but was not selected in PrepareForSnapshot";
  316. printStatus(msg.str(), Utility::low);
  317. continue;
  318. }
  319. if (!verifyBackupMetadata(pComponent)) {
  320. wstringstream msg;
  321. msg << L"Component with logical path: " << identity.m_logicalPath <<
  322. L"and name: " << identity.m_name <<
  323. L" has been corrupted in PostSnapshot";
  324. printStatus(msg.str(), Utility::low);
  325. }
  326. }
  327. m_selectedComponents.clear();
  328. return true;
  329. }
  330. catch(const exception& thrown)
  331. {
  332. printStatus(string("Failure in PostSnapshot event: ") + thrown.what(), Utility::low);
  333. return false;
  334. }
  335. catch(HRESULT error)
  336. {
  337. Utility::TestWriterException e(error);
  338. printStatus(e.what(), Utility::low);
  339. return false;
  340. }
  341. // This function is called to abort the writer's backup sequence.
  342. // If the writer has a spit component, spit files are cleaned up here.
  343. bool STDMETHODCALLTYPE TestWriter::OnAbort()
  344. try
  345. {
  346. enterEvent(Utility::Abort);
  347. m_selectedComponents.clear();
  348. cleanupFiles();
  349. return true;
  350. }
  351. catch(const exception& thrown)
  352. {
  353. printStatus(string("Failure in Abort event: ") + thrown.what(), Utility::low);
  354. return false;
  355. }
  356. catch(HRESULT error)
  357. {
  358. Utility::TestWriterException e(error);
  359. printStatus(e.what(), Utility::low);
  360. return false;
  361. }
  362. // This function is called as a result of the requestor calling BackupComplete
  363. // Once again we do sanity checking, and we also verify that the metadata we
  364. // wrote in PrepareForBackup has remained the same
  365. bool STDMETHODCALLTYPE TestWriter::OnBackupComplete(IN IVssWriterComponents *pComponents)
  366. try
  367. {
  368. enterEvent(Utility::BackupComplete);
  369. WriterConfiguration* config = WriterConfiguration::instance();
  370. // get the number of components
  371. UINT numComponents = 0;
  372. HRESULT hr = pComponents->GetComponentCount(&numComponents);
  373. checkReturn(hr, L"IVssWriterComponents::GetComponentCount");
  374. // for each component that was added
  375. for (unsigned int x = 0; x < numComponents; x++) {
  376. // --- get the relevant information
  377. CComPtr<IVssComponent> pComponent;
  378. hr = pComponents->GetComponent(x, &pComponent);
  379. checkReturn(hr, L"IVssWriterComponents::GetComponent");
  380. // --- ensure that the component is valid
  381. ComponentBase identity(getPath(pComponent), getName(pComponent));
  382. WriterConfiguration::ComponentList::iterator found =
  383. std::find(config->components().begin(),
  384. config->components().end(),
  385. identity);
  386. if (found == config->components().end()) {
  387. wstringstream msg;
  388. msg << L"Component with logical path: " << identity.m_logicalPath <<
  389. L"and name: " << identity.m_name <<
  390. L" is selected in BackupComplete, but does not appear in the writer metadata";
  391. printStatus(msg.str(), Utility::low);
  392. continue;
  393. }
  394. if (!verifyBackupMetadata(pComponent)) {
  395. wstringstream msg;
  396. msg << L"Component with logical path: " << identity.m_logicalPath <<
  397. L"and name: " << identity.m_name <<
  398. L" has been corrupted in BackupComplete";
  399. printStatus(msg.str(), Utility::low);
  400. }
  401. // check that the backup succeeded
  402. bool backupSucceeded = false;
  403. hr = pComponent->GetBackupSucceeded(&backupSucceeded);
  404. if (!backupSucceeded) {
  405. wstringstream msg;
  406. msg << L"Component with logical path: " << identity.m_logicalPath <<
  407. L"and name: " << identity.m_name <<
  408. L" was not marked as successfully backed up.";
  409. }
  410. }
  411. return true;
  412. }
  413. catch(const exception& thrown)
  414. {
  415. printStatus(string("Failure in BackupComplete event: ") + thrown.what(), Utility::low);
  416. return false;
  417. }
  418. catch(HRESULT error)
  419. {
  420. Utility::TestWriterException e(error);
  421. printStatus(e.what(), Utility::low);
  422. return false;
  423. }
  424. // This function is called at the end of the backup process. This may happen as a result
  425. // of the requestor shutting down, or it may happen as a result of abnormal termination
  426. // of the requestor.
  427. bool STDMETHODCALLTYPE TestWriter::OnBackupShutdown(IN VSS_ID SnapshotSetId)
  428. try
  429. {
  430. UNREFERENCED_PARAMETER(SnapshotSetId);
  431. enterEvent(Utility::BackupShutdown);
  432. return true;
  433. }
  434. catch(const exception& thrown)
  435. {
  436. printStatus(string("Failure in BackupShutdown event: ") + thrown.what(), Utility::low);
  437. return false;
  438. }
  439. // This function is called as a result of the requestor calling PreRestore
  440. // We check that component selection has been done properly, verify the
  441. // backup metadata, and set targets appropriately.
  442. bool STDMETHODCALLTYPE TestWriter::OnPreRestore(IN IVssWriterComponents *pComponents)
  443. try
  444. {
  445. enterEvent(Utility::PreRestore);
  446. WriterConfiguration* config = WriterConfiguration::instance();
  447. // get the number of components
  448. UINT numComponents = 0;
  449. HRESULT hr = pComponents->GetComponentCount(&numComponents);
  450. checkReturn(hr, L"IVssWriterComponents::GetComponentCount");
  451. m_selectedRestoreComponents.clear();
  452. // for each component that was added
  453. for (unsigned int x = 0; x < numComponents; x++) {
  454. // --- get the relevant information
  455. CComPtr<IVssComponent> pComponent;
  456. hr = pComponents->GetComponent(x, &pComponent);
  457. checkReturn(hr, L"IVssWriterComponents::GetComponent");
  458. // --- ensure that the component is valid
  459. ComponentBase identity(getPath(pComponent), getName(pComponent));
  460. WriterConfiguration::ComponentList::iterator found =
  461. std::find(config->components().begin(),
  462. config->components().end(),
  463. identity);
  464. if (found == config->components().end()) {
  465. wstringstream msg;
  466. msg << L"Component with logical path: " << identity.m_logicalPath <<
  467. L"and name: " << identity.m_name <<
  468. L" is selected in PreRestore, but does not appear in the writer metadata";
  469. pComponent->SetPreRestoreFailureMsg(msg.str().c_str());
  470. printStatus(msg.str(), Utility::low);
  471. continue;
  472. }
  473. // only process those component that are selected for restore
  474. bool selectedForRestore = false;
  475. hr = pComponent->IsSelectedForRestore(&selectedForRestore);
  476. checkReturn(hr, L"IVssComponent::IsSelectedForRestore");
  477. if (!selectedForRestore)
  478. continue;
  479. m_selectedRestoreComponents.push_back(*found);
  480. if (!verifyBackupMetadata(pComponent)) { // --- verify the backup metadata
  481. wstringstream msg;
  482. msg << L"Component with logical path: " << identity.m_logicalPath <<
  483. L"and name: " << identity.m_name <<
  484. L" has been corrupted in PreRestore";
  485. pComponent->SetPreRestoreFailureMsg(msg.str().c_str());
  486. printStatus(msg.str(), Utility::low);
  487. }
  488. writeRestoreMetadata(pComponent); // --- write restore metadata
  489. // --- set the target appropriately
  490. if (found->m_restoreTarget != VSS_RT_UNDEFINED) {
  491. HRESULT hr =pComponent->SetRestoreTarget(found->m_restoreTarget);
  492. checkReturn(hr, L"IVssComponent::SetRestoreTarget");
  493. printStatus(wstring(L"Set Restore Target: ") +
  494. Utility::toString(found->m_restoreTarget), Utility::high);
  495. }
  496. }
  497. return true;
  498. }
  499. catch(const exception& thrown)
  500. {
  501. printStatus(string("Failure in PreRestore event: ") + thrown.what(), Utility::low);
  502. return false;
  503. }
  504. catch(HRESULT error)
  505. {
  506. Utility::TestWriterException e(error);
  507. printStatus(e.what(), Utility::low);
  508. return false;
  509. }
  510. // This function is called as a result of the requestor calling PreRestore
  511. // We do some sanity checking, and then check to see if files have indeed
  512. // been restored
  513. bool STDMETHODCALLTYPE TestWriter::OnPostRestore(IN IVssWriterComponents *pComponents)
  514. try
  515. {
  516. enterEvent(Utility::PostRestore);
  517. // get the number of components
  518. UINT numComponents = 0;
  519. HRESULT hr = pComponents->GetComponentCount(&numComponents);
  520. checkReturn(hr, L"IVssWriterComponents::GetComponentCount");
  521. // for each component
  522. for (unsigned int x = 0; x < numComponents; x++) {
  523. // --- get the relevant information
  524. CComPtr<IVssComponent> pComponent;
  525. hr = pComponents->GetComponent(x, &pComponent);
  526. checkReturn(hr, L"I VssWriterComponents::GetComponent");
  527. // --- ensure that the component is valid
  528. ComponentBase identity(getPath(pComponent), getName(pComponent));
  529. vector<Component>::iterator found = std::find(m_selectedRestoreComponents.begin(),
  530. m_selectedRestoreComponents.end(),
  531. identity);
  532. if (found == m_selectedRestoreComponents.end()) {
  533. wstringstream msg;
  534. msg << L"Component with logical path: " << identity.m_logicalPath <<
  535. L"and name: " << identity.m_name <<
  536. L" is selected in PostRestore, but was not selected in PreRestore";
  537. pComponent->SetPostRestoreFailureMsg(msg.str().c_str());
  538. printStatus(msg.str(), Utility::low);
  539. continue;
  540. }
  541. // only process those component that are selected for restore
  542. bool selectedForRestore = false;
  543. hr = pComponent->IsSelectedForRestore(&selectedForRestore);
  544. checkReturn(hr, L"IVssComponent::IsSelectedForRestore");
  545. if (!selectedForRestore)
  546. continue;
  547. if (!verifyRestoreMetadata(pComponent)) {
  548. wstringstream msg;
  549. msg << L"Component with logical path: " << identity.m_logicalPath <<
  550. L"and name: " << identity.m_name <<
  551. L" has been corrupted in PostRestore";
  552. pComponent->SetPostRestoreFailureMsg(msg.str().c_str());
  553. printStatus(msg.str(), Utility::low);
  554. continue;
  555. }
  556. VSS_FILE_RESTORE_STATUS rStatus;
  557. hr = pComponent->GetFileRestoreStatus(&rStatus);
  558. checkReturn(hr, L"IVssComponent::GetFileRestoreStatus");
  559. if (rStatus != VSS_RS_ALL) {
  560. wstringstream msg;
  561. msg << L"Component with logical path: " << identity.m_logicalPath <<
  562. L"and name: " << identity.m_name <<
  563. L" was not marked as having been successfully restored";
  564. printStatus(msg.str(), Utility::low);
  565. continue;
  566. }
  567. updateNewTargets(pComponent, *found);
  568. verifyFilesRestored(pComponent, *found);
  569. }
  570. return true;
  571. }
  572. catch(const exception& thrown)
  573. {
  574. printStatus(string("Failure in PostRestore event: ") + thrown.what(), Utility::low);
  575. return false;
  576. }
  577. catch(HRESULT error)
  578. {
  579. Utility::TestWriterException e(error);
  580. printStatus(e.what(), Utility::low);
  581. return false;
  582. }
  583. // This function is called at the entry to all writer events.
  584. // A status message is printed to the console, and the event is failed if necessary.
  585. void TestWriter::enterEvent(Utility::Events event)
  586. {
  587. static HRESULT errors[] = { VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT,
  588. VSS_E_WRITERERROR_OUTOFRESOURCES,
  589. VSS_E_WRITERERROR_TIMEOUT,
  590. VSS_E_WRITERERROR_RETRYABLE
  591. };
  592. printStatus(wstring(L"\nReceived event: ") + Utility::toString(event));
  593. WriterConfiguration* config = WriterConfiguration::instance();
  594. // figure out whether we should fail this event
  595. WriterEvent writerEvent(event);
  596. WriterConfiguration::FailEventList::iterator found = std::find(config->failEvents().begin(),
  597. config->failEvents().end(),
  598. writerEvent);
  599. // if so, then fail it unless failures have run out
  600. if (found != config->failEvents().end()) {
  601. bool failEvent = !found->m_retryable || (m_failures[event] < found->m_numFailures);
  602. bool setFailure = inSequence(event);
  603. if (!found->m_retryable && setFailure)
  604. SetWriterFailure(VSS_E_WRITERERROR_NONRETRYABLE);
  605. else if (failEvent && setFailure)
  606. SetWriterFailure(errors[rand() % (sizeof(errors) / sizeof(errors[0]))]);
  607. if (failEvent) {
  608. ++m_failures[event];
  609. wstringstream msg;
  610. msg << L"Failure Requested in Event: " << Utility::toString(event) <<
  611. L" Failing for the " << m_failures[event] << L" time";
  612. throw Utility::TestWriterException(msg.str());
  613. }
  614. }
  615. }
  616. // This helper function adds a component to the writer-metadata document
  617. void TestWriter::addComponent(const Component& component, IVssCreateWriterMetadata* pMetadata)
  618. {
  619. const wchar_t* logicalPath = component.m_logicalPath.empty() ? NULL : component.m_logicalPath.c_str();
  620. // add the component to the document
  621. HRESULT hr = pMetadata->AddComponent(component.m_componentType, // ct
  622. logicalPath, // logicalwszLogicalPath
  623. component.m_name.c_str(), // wszComponentName
  624. NULL, // wszCaption
  625. NULL, // pbIcon
  626. 0, // cbIcon
  627. true, // bRestoreMetadata
  628. true, // bNotifyOnBackupComplete
  629. component.m_selectable, // bSelectable
  630. component.m_selectableForRestore // bSelectableForRestore
  631. );
  632. checkReturn(hr, L"IVssCreateWriterMetadata::AddComponent");
  633. printStatus(L"\nAdded component: ", Utility::high);
  634. printStatus(component.toString(), Utility::high);
  635. // add all of the files to the component. NOTE: we don't allow distinctions between database files
  636. // and database log files in the VSS_CT_DATABASE case.
  637. // we sometimes put a '\' on the end and sometimes not to keep requestors honest.
  638. Component::ComponentFileList::iterator current = component.m_files.begin();
  639. while (current != component.m_files.end()) {
  640. if (component.m_componentType == VSS_CT_FILEGROUP) {
  641. const wchar_t* alternate = current->m_alternatePath.empty() ? NULL :
  642. current->m_alternatePath.c_str();
  643. hr = pMetadata->AddFilesToFileGroup(logicalPath,
  644. component.m_name.c_str(),
  645. current->m_path.substr(0, current->m_path.size()-1).c_str(),
  646. current->m_filespec.c_str(),
  647. current->m_recursive,
  648. alternate);
  649. checkReturn(hr, L"IVssCreateWriterMetadata::AddFilesToFileGroup");
  650. } else if (component.m_componentType == VSS_CT_DATABASE) {
  651. hr = pMetadata->AddDatabaseFiles(logicalPath,
  652. component.m_name.c_str(),
  653. current->m_path.c_str(),
  654. current->m_filespec.c_str());
  655. checkReturn(hr, L"IVssCreateWriterMetadata::AddDatabaseFiles");
  656. } else {
  657. assert(false);
  658. }
  659. printStatus(L"\nAdded Component Filespec: ");
  660. printStatus(current->toString());
  661. ++current;
  662. }
  663. // add all dependencies to the dependency list for the writer
  664. Component::DependencyList::iterator currentDependency = component.m_dependencies.begin();
  665. while (currentDependency != component.m_dependencies.end()) {
  666. hr = pMetadata->AddComponentDependency(logicalPath,
  667. component.m_name.c_str(), // wszForLogicalPath
  668. currentDependency->m_writerId, // wszForComponentName
  669. currentDependency->m_logicalPath.c_str(), // wszOnLogicalPath
  670. currentDependency->m_componentName.c_str() // wszOnComponentName
  671. );
  672. checkReturn(hr, L"IVssCreateWriterMetadata::AddComponentDependency");
  673. printStatus(L"\nAdded Component Dependency: ");
  674. printStatus(currentDependency->toString());
  675. ++currentDependency;
  676. }
  677. }
  678. // This helper function spits all files in a file specification to an alternate location
  679. void TestWriter::spitFiles(const TargetedFile& file)
  680. {
  681. assert(!file.m_path.empty());
  682. assert(file.m_path[file.m_path.size() - 1] == L'\\');
  683. assert(!file.m_alternatePath.empty());
  684. assert(file.m_alternatePath[file.m_alternatePath.size() - 1] == L'\\');
  685. // ensure that both the source and target directories exist
  686. DWORD attributes = ::GetFileAttributes(file.m_path.c_str());
  687. if ((attributes == INVALID_FILE_ATTRIBUTES) ||
  688. !(attributes & FILE_ATTRIBUTE_DIRECTORY)) {
  689. wstringstream msg;
  690. msg << L"The source path " << file.m_path << L" does not exist";
  691. throw Utility::TestWriterException(msg.str());
  692. }
  693. attributes = ::GetFileAttributes(file.m_alternatePath.c_str());
  694. if ((attributes == INVALID_FILE_ATTRIBUTES) ||
  695. !(attributes & FILE_ATTRIBUTE_DIRECTORY)) {
  696. wstringstream msg;
  697. msg << L"The target path " << file.m_alternatePath << L" does not exist";
  698. throw Utility::TestWriterException(msg.str());
  699. }
  700. // start by copying files from the specified root directory
  701. std::queue<wstring> paths;
  702. paths.push(file.m_path);
  703. // walk through in breadth-first order. It's less resource intensive than depth-first, and
  704. // potentially more performant
  705. while (!paths.empty()) {
  706. // --- grab the next path off the queue
  707. wstring currentPath = paths.front();
  708. paths.pop();
  709. // --- start walking all files in the directory
  710. WIN32_FIND_DATA findData;
  711. Utility::AutoFindFileHandle findHandle = ::FindFirstFile((currentPath + L'*').c_str(), &findData);
  712. if (findHandle == INVALID_HANDLE_VALUE)
  713. continue;
  714. do {
  715. wstring currentName = findData.cFileName;
  716. if (currentName == L"." ||
  717. currentName == L"..")
  718. continue;
  719. std::transform(currentName.begin(), currentName.end(), currentName.begin(), towupper);
  720. // --- if we've hit a direcctory and we care to do a recursive spit
  721. if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
  722. file.m_recursive) {
  723. assert(!currentName.empty());
  724. if (currentName[currentName.size() - 1] != L'\\')
  725. currentName += L"\\";
  726. // figure out where the target for this new directory is
  727. assert(currentPath.find(file.m_path) == 0);
  728. wstring extraDirectory = currentPath.substr(file.m_path.size());
  729. wstring alternateLocation = file.m_alternatePath + extraDirectory + currentName;
  730. // create a target directory to hold the copied files.
  731. if (!::CreateDirectory(alternateLocation.c_str(), NULL) &&
  732. ::GetLastError() != ERROR_ALREADY_EXISTS)
  733. checkReturn(HRESULT_FROM_WIN32(::GetLastError()), L"CreateDirectory");
  734. m_directoriesToRemove.push(alternateLocation.c_str());
  735. // push the directory on the queue so it gets processed as well
  736. paths.push(currentPath + currentName);
  737. continue;
  738. }
  739. // --- if we've hit a regular file with a matching filespec
  740. if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
  741. wildcardMatches(currentName, file.m_filespec)) {
  742. // figure out where the new target location is
  743. assert(currentPath.find(file.m_path) == 0);
  744. wstring extraDirectory = currentPath.substr(file.m_path.size());
  745. wstring alternateLocation = file.m_alternatePath + extraDirectory + currentName;
  746. wstringstream msg;
  747. msg << L"Spitting File: " << currentPath + currentName <<
  748. L" To location: " << alternateLocation;
  749. printStatus(msg.str() , Utility::high);
  750. // copy the file over
  751. if (!::CopyFile((currentPath + currentName).c_str(), alternateLocation.c_str(), FALSE))
  752. checkReturn(HRESULT_FROM_WIN32(::GetLastError()), L"CopyFile");
  753. else
  754. m_toDelete.push_back(alternateLocation);
  755. }
  756. } while (::FindNextFile(findHandle, &findData));
  757. }
  758. }
  759. // extract the component name from an interface pointer
  760. wstring TestWriter::getName(IVssComponent* pComponent)
  761. {
  762. CComBSTR name;
  763. HRESULT hr = pComponent->GetComponentName(&name);
  764. checkReturn(hr, L"IVssComponent::GetComponentName");
  765. assert(name != NULL); // this should never happen
  766. return (BSTR)name;
  767. }
  768. // extract the component logical path from an interface pointer
  769. wstring TestWriter::getPath(IVssComponent* pComponent)
  770. {
  771. CComBSTR path;
  772. HRESULT hr = pComponent->GetLogicalPath(&path);
  773. checkReturn(hr, L"IVssComponent::GetLogicalPath");
  774. // GetLogicalPath can indeed return NULL so be careful
  775. return (path.Length() > 0) ? (BSTR)path : L"";
  776. }
  777. // write a backup metadata stamp to the component
  778. void TestWriter::writeBackupMetadata(IVssComponent* pComponent)
  779. {
  780. HRESULT hr = pComponent->SetBackupMetadata(metadata(pComponent, BackupString).c_str());
  781. checkReturn(hr, L"IVssComponent::SetBackupMetadata");
  782. printStatus(wstring(L"Writing backup metadata: ") + metadata(pComponent, BackupString),
  783. Utility::high);
  784. }
  785. // verify that a backup metadata stamp is intact
  786. bool TestWriter::verifyBackupMetadata(IVssComponent* pComponent)
  787. {
  788. CComBSTR data;
  789. HRESULT hr = pComponent->GetBackupMetadata(&data);
  790. checkReturn(hr, L"IVssComponent::GetBackupMetadata");
  791. printStatus(wstring(L"\nComparing metadata: ") + (data.Length() ? (BSTR)data : L"") +
  792. wstring(L" Against expected string: ") + metadata(pComponent, BackupString),
  793. Utility::high);
  794. if (data.Length() == 0 || metadata(pComponent, BackupString) != (BSTR)data)
  795. return false;
  796. return true;
  797. }
  798. // write a restore metadata stamp to the component
  799. void TestWriter::writeRestoreMetadata(IVssComponent* pComponent)
  800. {
  801. HRESULT hr = pComponent->SetRestoreMetadata(metadata(pComponent, RestoreString).c_str());
  802. checkReturn(hr, L"IVssComponent::SetRestoreMetadata");
  803. printStatus(wstring(L"Writing restore metadata: ") + metadata(pComponent, RestoreString),
  804. Utility::high);
  805. }
  806. // verify that a restore metadata stamp is intact
  807. bool TestWriter::verifyRestoreMetadata(IVssComponent* pComponent)
  808. {
  809. CComBSTR data;
  810. HRESULT hr = pComponent->GetRestoreMetadata(&data);
  811. checkReturn(hr, L"IVssComponent::GetRestoreMetadata");
  812. printStatus(wstring(L"Comparing metadata: ") + (data.Length() ? (BSTR)data : L"") +
  813. wstring(L" Against expected string: ") + metadata(pComponent, RestoreString),
  814. Utility::high);
  815. if (data.Length() == 0 || metadata(pComponent, RestoreString) != (BSTR)data)
  816. return false;
  817. return true;
  818. }
  819. // check to see if the specified file (or files) are all in the current snapshot set
  820. // doesn't check directory junctions... this will not be changed anytime soon.
  821. // recursive mount points are also not handled very well
  822. bool TestWriter::checkPathAffected(const TargetedFile& file)
  823. {
  824. wstring backupPath = file.m_alternatePath.empty() ? file.m_path : file.m_alternatePath;
  825. // if the path in question isn't snapshot, then return false
  826. if (!IsPathAffected(backupPath.c_str()))
  827. return false;
  828. // if the filespec isn't recursive, then we're done
  829. if (!file.m_recursive)
  830. return true;
  831. // get the name of the volume we live on
  832. wchar_t volumeMount[MAX_PATH];
  833. if(!::GetVolumePathName(backupPath.c_str(), volumeMount, MAX_PATH))
  834. checkReturn(HRESULT_FROM_WIN32(::GetLastError()), L"GetVolumePathName");
  835. assert(backupPath.find(volumeMount) == 0);
  836. wchar_t volumeName[MAX_PATH];
  837. if (!::GetVolumeNameForVolumeMountPoint(volumeMount, volumeName, MAX_PATH))
  838. checkReturn(HRESULT_FROM_WIN32(::GetLastError()), L"GetVolumeNameForVolumeMountPoint");
  839. // start off with the volume name and starting directory on a worklist.
  840. std::queue<std::pair<wstring, wstring> > worklist;
  841. worklist.push(std::make_pair(wstring(volumeName), backupPath.substr(wcslen(volumeMount))));
  842. while (!worklist.empty()) {
  843. // get the current volume and directory off the worklist
  844. wstring currentVolume = worklist.front().first;
  845. wstring currentPath = worklist.front().second;
  846. worklist.pop();
  847. // now, enumerate all mount points on the volume
  848. Utility::AutoFindMountHandle findHandle = ::FindFirstVolumeMountPoint(currentVolume.c_str(), volumeMount, MAX_PATH);
  849. if (findHandle == INVALID_HANDLE_VALUE)
  850. continue;
  851. do {
  852. std::transform(volumeMount, volumeMount + wcslen(volumeMount), volumeMount, towupper);
  853. wstring mountPoint = currentVolume + volumeMount;
  854. // if this mount point is included in the file specification, the volume better be included in the snapshot set
  855. if ((mountPoint.find(currentVolume + currentPath) == 0) &&
  856. !IsPathAffected(mountPoint.c_str()))
  857. return false;
  858. if (!::GetVolumeNameForVolumeMountPoint(volumeMount, volumeName, MAX_PATH))
  859. checkReturn(HRESULT_FROM_WIN32(::GetLastError()), L"GetVolumeNameForVolumeMountPoint");
  860. // put this volume on the worklist so it gets processed as well
  861. // Mount points always point to the root of a volume, so pass in
  862. // an empty second argument. When junctions are supported, we
  863. // will pass in the target directory as the second argument.
  864. worklist.push(std::make_pair(wstring(volumeName), wstring())); // this line will change when we support junctions
  865. } while (::FindNextVolumeMountPoint(findHandle, volumeMount, MAX_PATH) == TRUE);
  866. }
  867. return true;
  868. }
  869. // delete all files and directories created in PrepareForSnapshot
  870. void TestWriter::cleanupFiles()
  871. {
  872. // delete all created files
  873. vector<wstring>::iterator currentFile = m_toDelete.begin();
  874. while (currentFile != m_toDelete.end()) {
  875. if (!::DeleteFile(currentFile->c_str()))
  876. warnReturn(HRESULT_FROM_WIN32(::GetLastError()), L"DeleteFile");
  877. ++currentFile;
  878. }
  879. m_toDelete.clear();
  880. // remove all created directories in the proper order
  881. while (!m_directoriesToRemove.empty()) {
  882. wstring dir = m_directoriesToRemove.top();
  883. if (!::RemoveDirectory(dir.c_str()))
  884. warnReturn(HRESULT_FROM_WIN32(::GetLastError()), L"RemoveDirectory");
  885. m_directoriesToRemove.pop();
  886. }
  887. }
  888. // check to see if the requestor has added any new targets, and add them to the
  889. // Component structure
  890. void TestWriter::updateNewTargets(IVssComponent* pComponent, Component& writerComponent)
  891. {
  892. HRESULT hr = S_OK;
  893. UINT newTargetCount = 0;
  894. hr = pComponent->GetNewTargetCount(&newTargetCount);
  895. checkReturn(hr, L"IVssComponent::GetNewTargetCount");
  896. writerComponent.m_newTargets.clear();
  897. for (UINT x = 0; x < newTargetCount; x++) {
  898. // get information about the new target
  899. CComPtr<IVssWMFiledesc> newTarget;
  900. hr = pComponent->GetNewTarget(x, &newTarget);
  901. checkReturn(hr, L"IVssComponent::GetNewTarget");
  902. CComBSTR path, filespec, alternateLocation;
  903. bool recursive = false;
  904. hr = newTarget->GetPath(&path);
  905. checkReturn(hr, L"IVssComponent:GetPath");
  906. hr = newTarget->GetFilespec(&filespec);
  907. checkReturn(hr, L"IVssComponent:GetFilespec");
  908. hr = newTarget->GetRecursive(&recursive);
  909. checkReturn(hr, L"IVssComponent:GetRecursive");
  910. hr = newTarget->GetAlternateLocation(&alternateLocation);
  911. checkReturn(hr, L"IVssComponent:GetAlternateLocation");
  912. // add it to the new-target list
  913. writerComponent.m_newTargets.push_back(TargetedFile(wstring(path),
  914. wstring(filespec),
  915. recursive,
  916. wstring(alternateLocation)));
  917. }
  918. }
  919. // verify that files in the component were restored properly.
  920. // assumption is that the directory being restored to is empty if the checkExcluded parameter is true.
  921. // currently, we have a very simple-minded approach to handle the wildcard case
  922. // a more general solution will involve hashing files, and will be implemented if time is found
  923. void TestWriter::verifyFilesRestored(IVssComponent* pComponent, const Component& writerComponent)
  924. {
  925. WriterConfiguration* config = WriterConfiguration::instance();
  926. // no checking is being done. Don't do anything
  927. if (!config->checkIncludes() && !config->checkExcludes())
  928. return;
  929. // for each file in the component
  930. VSS_RESTORE_TARGET target = writerComponent.m_restoreTarget;
  931. VSS_RESTOREMETHOD_ENUM method = config->restoreMethod().m_method;
  932. // build the list of all filespecs that need restoring
  933. vector<TargetedFile> componentFiles;
  934. buildComponentFiles(writerComponent, std::back_inserter(componentFiles));
  935. for (vector<TargetedFile>::iterator currentFile = componentFiles.begin();
  936. currentFile != componentFiles.end();
  937. ++currentFile) {
  938. // --- figure out if there are any matching exclude files
  939. vector<File> excludeFiles;
  940. if (config->checkExcludes()) {
  941. buildContainer_if(config->excludeFiles().begin(),
  942. config->excludeFiles().end(),
  943. std::back_inserter(excludeFiles),
  944. std::bind2nd(std::ptr_fun(targetMatches), *currentFile));
  945. }
  946. // if there's no checking to be done for this filespec, continue
  947. if (excludeFiles.empty() && !config->checkIncludes())
  948. continue;
  949. // --- if there are new targets, look for one that references our file
  950. // --- if we find such a target, ensure that the file was restored there
  951. // --- NOTE: after the interface changes, there should be at most one matching
  952. // --- target.
  953. vector<TargetedFile> targets;
  954. buildContainer_if(writerComponent.m_newTargets.begin(),
  955. writerComponent.m_newTargets.end(),
  956. std::back_inserter(targets),
  957. std::bind2nd(std::equal_to<File>(), *currentFile));
  958. if (targets.size() > 1) {
  959. wstringstream msg;
  960. msg << L"More than one new target matched filespec " <<
  961. currentFile->toString() << std::endl << L"This is an illegal configuration";
  962. printStatus(msg.str());
  963. }
  964. if (!targets.empty()) {
  965. // create a function object to use for verification
  966. VerifyFileAtLocation locationChecker(excludeFiles, pComponent, false);
  967. locationChecker(targets[0], *currentFile); // TODO: no longer need this fancy functor, since we're not doing a for_each
  968. }
  969. vector<TargetedFile> alternateLocations;
  970. buildContainer_if(config->restoreMethod().m_alternateLocations.begin(),
  971. config->restoreMethod().m_alternateLocations.end(),
  972. std::back_inserter(alternateLocations),
  973. std::bind2nd(std::equal_to<File>(), *currentFile));
  974. // --- NOTE: once again, interface changes mean we expect at most one
  975. assert(alternateLocations.size() <= 1);
  976. bool alternateRestore = !alternateLocations.empty() &&
  977. ((target == VSS_RT_ALTERNATE) || (method == VSS_RME_RESTORE_TO_ALTERNATE_LOCATION));
  978. if ((method == VSS_RME_RESTORE_IF_CAN_REPLACE) ||
  979. (method == VSS_RME_RESTORE_IF_NOT_THERE) ||
  980. alternateRestore) {
  981. // --- in all of these cases, the backup application may restore to an alternate location.
  982. // if we're not in either of the following two states, then the alternate location should only be used if there's
  983. // a matching element in the backup document. Check to see if this is true
  984. // create a function object to use for verification
  985. // TODO: we no longer need this fancy functor object since we're not doing a for_each
  986. if (!alternateLocations.empty()) {
  987. VerifyFileAtLocation locationChecker(excludeFiles, pComponent,
  988. (target != VSS_RT_ALTERNATE) && (method != VSS_RME_RESTORE_TO_ALTERNATE_LOCATION));
  989. // check to ensure that the file has been restored to each matching alternate location
  990. // once again, this isn't quite correct, but good enough for now. More complicated
  991. // test scenarios will eventually break this.
  992. locationChecker(alternateLocations[0], *currentFile);
  993. }
  994. }
  995. // none of the above cases are true. We need to check to see that the file is restored to its original location
  996. if ((method != VSS_RME_RESTORE_AT_REBOOT) && (method != VSS_RME_RESTORE_AT_REBOOT_IF_CANNOT_REPLACE) &&
  997. !alternateRestore) {
  998. // create a function object to use for verification
  999. VerifyFileAtLocation locationChecker(excludeFiles, pComponent, false);
  1000. locationChecker(TargetedFile(currentFile->m_path,
  1001. currentFile->m_filespec,
  1002. currentFile->m_recursive,
  1003. currentFile->m_path),
  1004. *currentFile);
  1005. }
  1006. }
  1007. }
  1008. bool __cdecl TestWriter::isSubcomponent(ComponentBase sub, ComponentBase super)
  1009. {
  1010. // if the components are the same, then return true
  1011. if (super == sub)
  1012. return true;
  1013. wstring path = super.m_logicalPath;
  1014. if (!path.empty() && path[path.size() - 1] != L'\\')
  1015. path+= L"\\";
  1016. path += super.m_name;
  1017. // if the supercomponent full path is the same as the subcomponent logical path, then true
  1018. if (path == sub.m_logicalPath)
  1019. return true;
  1020. // otherwise, check for partial match
  1021. return sub.m_logicalPath.find(path + L"\\") == 0;
  1022. }
  1023. bool __cdecl TestWriter::targetMatches (File target, File file)
  1024. {
  1025. assert(!file.m_filespec.empty());
  1026. assert(!target.m_filespec.empty());
  1027. // the filespec must match first of all
  1028. if (!wildcardMatches(file.m_filespec, target.m_filespec))
  1029. return false;
  1030. // check the path
  1031. if (file.m_recursive) {
  1032. if (!target.m_recursive)
  1033. return target.m_path.find(file.m_path) == 0;
  1034. else
  1035. return (target.m_path.find(file.m_path) == 0) ||(file.m_path.find(target.m_path) == 0);
  1036. } else {
  1037. if (!target.m_recursive)
  1038. return file.m_path == target.m_path;
  1039. else
  1040. return file.m_path.find(target.m_path) == 0;
  1041. }
  1042. }
  1043. // This helper function tests whether a component can be legally added to the backup document
  1044. bool __cdecl TestWriter::addableComponent(Component toAdd)
  1045. {
  1046. WriterConfiguration* config = WriterConfiguration::instance();
  1047. if (toAdd.m_selectable)
  1048. return true;
  1049. // see if there are any selectable ancestors
  1050. vector<Component> ancestors;
  1051. buildContainer_if(config->components().begin(),
  1052. config->components().end(),
  1053. std::back_inserter(ancestors),
  1054. Utility::and1(std::bind2nd(std::ptr_fun(isSupercomponent), toAdd),
  1055. std::ptr_fun(isComponentSelectable)));
  1056. return ancestors.empty();
  1057. }
  1058. // check to see if two wildcards match.
  1059. // specifically, check to see whether the set of expansions of the first wildcard has a
  1060. // non-empty intersection with the set of expansions of the second wildcard.
  1061. // This function is not terribly efficient, but wildcards tend to be fairly short.
  1062. bool TestWriter::wildcardMatches(const wstring& first, const wstring& second)
  1063. {
  1064. // if both string are empty, then they surely match
  1065. if (first.empty() && second.empty())
  1066. return true;
  1067. // if we're done with the component, the wildcard better be terminated with '*' characters
  1068. if (first.empty())
  1069. return (second[0] == L'*') && wildcardMatches(first, second.substr(1));
  1070. if (second.empty())
  1071. return (first[0] == L'*') && wildcardMatches(first.substr(1), second);
  1072. switch(first[0]) {
  1073. case L'?':
  1074. if (second[0] == L'*') {
  1075. return wildcardMatches(first.substr(1), second) || // '*' matches character
  1076. wildcardMatches(first, second.substr(1)); // '*' matches nothing
  1077. }
  1078. // otherwise, the rest of the strings must match
  1079. return wildcardMatches(first.substr(1), second.substr(1));
  1080. case L'*':
  1081. return wildcardMatches(first, second.substr(1)) || // '*' matches character
  1082. wildcardMatches(first.substr(1), second); // '*' matches nothing
  1083. default:
  1084. switch(second[0]) {
  1085. case L'?':
  1086. return wildcardMatches(first.substr(1), second.substr(1));
  1087. case L'*':
  1088. return wildcardMatches(first.substr(1), second) || // '*' matches character
  1089. wildcardMatches(first, second.substr(1)); // '*' matches nothing
  1090. default:
  1091. return (first[0] == second[0]) &&
  1092. wildcardMatches(first.substr(1), second.substr(1));
  1093. }
  1094. }
  1095. }
  1096. wstring TestWriter::VerifyFileAtLocation::verifyFileAtLocation(const File& file, const TargetedFile& location) const
  1097. {
  1098. WriterConfiguration* config = WriterConfiguration::instance();
  1099. // complicated set of assertions.
  1100. assert(!(file.m_recursive && !location.m_recursive) ||
  1101. (location.m_path.find(file.m_path) == 0));
  1102. assert(!(location.m_recursive && !file.m_recursive) ||
  1103. (file.m_path.find(location.m_path) == 0));
  1104. assert(!(file.m_recursive && location.m_recursive) ||
  1105. ((file.m_path.find(location.m_path) == 0) || (location.m_path.find(file.m_path) == 0)));
  1106. assert(!m_excluded.empty() || config->checkIncludes());
  1107. assert(m_excluded.empty() || config->checkExcludes());
  1108. // performant case where we don't have to walk any directory trees
  1109. if (!file.m_recursive && !location.m_recursive && isExact(file.m_filespec)) {
  1110. assert(m_excluded.size() <= 1); // if not, the config file isn't set up right
  1111. // --- if this is an alternate location mapping, only process it if there's a matching alternate location
  1112. // --- in the backup document
  1113. if (m_verifyAlternateLocation &&
  1114. !verifyAlternateLocation(TargetedFile(file.m_path, file.m_filespec, false, location.m_alternatePath))) {
  1115. return L"";
  1116. }
  1117. // --- ensure that the file has been restored, unless the file is excluded
  1118. printStatus(wstring(L"\nChecking file ") +
  1119. location.m_alternatePath + file.m_filespec,
  1120. Utility::high);
  1121. // check for error cases
  1122. if (m_excluded.empty()) {
  1123. if (::GetFileAttributes((location.m_alternatePath + file.m_filespec).c_str()) == INVALID_FILE_ATTRIBUTES) {
  1124. wstringstream msg;
  1125. msg << L"\nThe file: " << std::endl << file.toString() << std::endl <<
  1126. L"was not restored to location " << location.m_alternatePath;
  1127. printStatus(msg.str(), Utility::low);
  1128. return msg.str();
  1129. }
  1130. } else if (::GetFileAttributes((location.m_alternatePath + file.m_filespec).c_str()) != INVALID_FILE_ATTRIBUTES) {
  1131. wstringstream msg;
  1132. msg << L"\nThe file: " << file.m_path << file.m_filespec <<
  1133. L" should have been excluded, but appears in location " << location.m_alternatePath;
  1134. printStatus(msg.str(), Utility::low);
  1135. return msg.str();
  1136. }
  1137. return L"";
  1138. }
  1139. std::queue<wstring> paths;
  1140. // figure out what directory to start looking from
  1141. wstring startPath = location.m_alternatePath;
  1142. if (location.m_recursive && (file.m_path.find(location.m_path) == 0))
  1143. startPath += file.m_path.substr(location.m_path.size());
  1144. paths.push(startPath);
  1145. // in the recursive case, files will hopefully be backed up high in the directory tree
  1146. // consequently, we're going to walk the tree breadth-first
  1147. printStatus(L"\nChecking that filespec was restored:", Utility::high);
  1148. while (!paths.empty()) {
  1149. wstring currentPath = paths.front();
  1150. paths.pop();
  1151. printStatus(wstring(L" Checking directory: ") + currentPath,
  1152. Utility::high);
  1153. // for every file in the current directory (can't pass in filespec since we want to match all directories)
  1154. WIN32_FIND_DATA findData;
  1155. Utility::AutoFindFileHandle findHandle = ::FindFirstFile((currentPath + L"*").c_str(), &findData);
  1156. if (findHandle == INVALID_HANDLE_VALUE)
  1157. continue;
  1158. do {
  1159. wstring currentName = findData.cFileName;
  1160. std::transform(currentName.begin(), currentName.end(), currentName.begin(), towupper);
  1161. if (currentName == L"." ||
  1162. currentName == L"..")
  1163. continue;
  1164. // --- if the file is a directory
  1165. if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  1166. assert(!currentName.empty());
  1167. if (currentName[currentName.size() - 1] != L'\\')
  1168. currentName += L"\\";
  1169. // add it if necessary
  1170. if (file.m_recursive)
  1171. paths.push(currentPath + currentName);
  1172. continue; // skip to next file
  1173. }
  1174. printStatus(wstring(L" Checking file: ") + currentName);
  1175. // --- translate the path to what it would have been in the original tree
  1176. assert(currentPath.find(location.m_alternatePath) == 0);
  1177. wstring originalPath = file.m_path;
  1178. if (file.m_recursive && (location.m_path.find(file.m_path) == 0))
  1179. originalPath += location.m_path.substr(file.m_path.size());
  1180. originalPath += currentPath.substr(location.m_alternatePath.size());
  1181. // --- if this is an alternate location mapping, only process it if there's a matching
  1182. // --- alternate location mapping in the backup document
  1183. if (m_verifyAlternateLocation &&
  1184. !verifyAlternateLocation(TargetedFile(originalPath, currentName, false, currentPath))) {
  1185. continue;
  1186. }
  1187. // --- find an exclude item that matches
  1188. // --- if !config->checkExcluded(), m_excluded will be an empty container, and
  1189. // --- std::find_if will return the end iterator
  1190. vector<File>::const_iterator found =
  1191. std::find_if(m_excluded.begin(),
  1192. m_excluded.end(),
  1193. std::bind2nd(std::ptr_fun(targetMatches), File(originalPath, currentName, false)));
  1194. // --- return if this is either an excluded file, or if we've found at least one matching include file
  1195. if (found != m_excluded.end()) {
  1196. wstringstream msg;
  1197. msg << L"The file " << originalPath << currentName <<
  1198. L" should have been excluded, but appears in location " << currentPath;
  1199. printStatus(msg.str(), Utility::low);
  1200. return msg.str();
  1201. } else if (config->checkIncludes() &&
  1202. wildcardMatches(currentName, file.m_filespec)) {
  1203. return L""; // declare success in cheesy case
  1204. }
  1205. } while (::FindNextFile(findHandle, &findData));
  1206. }
  1207. if (config->checkIncludes()) {
  1208. wstringstream msg;
  1209. msg << L"None of the files specified by " << std::endl << file.toString() << std::endl <<
  1210. L" were restored to location " << location.m_alternatePath;
  1211. printStatus(msg.str(), Utility::low);
  1212. return msg.str();
  1213. }
  1214. // we're only checking excludes, and we didn't find any violations
  1215. return L"";
  1216. }
  1217. // verify that an alternate location mapping appears in the backup document
  1218. bool TestWriter::VerifyFileAtLocation::verifyAlternateLocation(const TargetedFile& writerAlt) const
  1219. {
  1220. assert (isExact(writerAlt.m_filespec));
  1221. assert(!writerAlt.m_recursive);
  1222. unsigned int mappings = 0;
  1223. HRESULT hr = m_pComponent->GetAlternateLocationMappingCount(&mappings);
  1224. checkReturn(hr, L"IVssComponent::GetAlternateLocationMappingCount");
  1225. for (unsigned int x = 0; x < mappings; x++) {
  1226. // get the current alternate location mapping
  1227. CComPtr<IVssWMFiledesc> filedesc;
  1228. hr = m_pComponent->GetAlternateLocationMapping(x, &filedesc);
  1229. checkReturn(hr, L"IVssComponent::GetAlternateLocationMapping");
  1230. // grab all relevant fields
  1231. CComBSTR bstrPath, bstrFilespec, bstrAlternateLocation;
  1232. hr = filedesc->GetPath(&bstrPath);
  1233. checkReturn(hr, L"IVssComponent::GetPath");
  1234. if (bstrPath.Length() == 0) {
  1235. printStatus(L"An Alternate Location Mapping with an empty path was added to the backup document",
  1236. Utility::low);
  1237. continue;
  1238. }
  1239. hr = filedesc->GetFilespec(&bstrFilespec);
  1240. checkReturn(hr, L"IVssComponent::GetFilespec");
  1241. if (bstrFilespec.Length() == 0) {
  1242. printStatus(L"An Alternate Location Mapping with an empty filespec was added to the backup document",
  1243. Utility::low);
  1244. continue;
  1245. }
  1246. hr = filedesc->GetAlternateLocation(&bstrAlternateLocation);
  1247. checkReturn(hr, L"IVssComponent::GetAlternateLocation");
  1248. if (bstrAlternateLocation.Length() == 0) {
  1249. printStatus(L"An Alternate Location Mapping with an empty alternateLocation was added to the backup document",
  1250. Utility::low);
  1251. continue;
  1252. }
  1253. bool recursive;
  1254. hr = filedesc->GetRecursive(&recursive);
  1255. checkReturn(hr, L"IVssComponent::GetRecursive");
  1256. // convert the fields to uppercase and ensure paths are '\' terminated
  1257. wstring path = bstrPath;
  1258. std::transform(path.begin(), path.end(), path.begin(), towupper);
  1259. if (path[path.size() - 1] != L'\\')
  1260. path += L'\\';
  1261. wstring filespec = bstrFilespec;
  1262. std::transform(filespec.begin(), filespec.end(), filespec.begin(), towupper);
  1263. wstring alternatePath = bstrAlternateLocation;
  1264. std::transform(alternatePath.begin(), alternatePath.end(), alternatePath.begin(), towupper);
  1265. if (alternatePath[alternatePath.size() - 1] != L'\\')
  1266. alternatePath += L'\\';
  1267. // check to see if that passed-in mapping is encompassed by the one in the backup document
  1268. if (targetMatches(File(path, filespec, recursive), writerAlt)) {
  1269. if (recursive) {
  1270. if (writerAlt.m_alternatePath.find(alternatePath) != 0)
  1271. return false;
  1272. assert(writerAlt.m_path.find(path) == 0);
  1273. alternatePath += writerAlt.m_path.substr(path.size());
  1274. }
  1275. return alternatePath == writerAlt.m_alternatePath;
  1276. }
  1277. }
  1278. return false;
  1279. }
  1280. // add the current error message to the PostRestoreFailureMsg
  1281. void TestWriter::VerifyFileAtLocation::saveErrorMessage(const wstring& message) const
  1282. {
  1283. if (!message.empty()) {
  1284. CComBSTR old;
  1285. m_pComponent->GetPostRestoreFailureMsg(&old);
  1286. wstring oldMessage = (old.Length() > 0) ? (BSTR)old : L"";
  1287. m_pComponent->SetPostRestoreFailureMsg((oldMessage + wstring(L"\n") + message).c_str());
  1288. }
  1289. }