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.

1663 lines
57 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Abstract:
  4. @doc
  5. @module stswriter.cpp | Implementation of Sharepoint Team Services Writer
  6. @end
  7. Author:
  8. Brian Berkowitz [brianb] 08/17/2001
  9. TBD:
  10. Revision History:
  11. Name Date Comments
  12. brianb 08/17/2001 created
  13. --*/
  14. #include "stdafx.hxx"
  15. #include "vs_inc.hxx"
  16. #include "vs_reg.hxx"
  17. #include "vssmsg.h"
  18. #include "iadmw.h"
  19. #include "iiscnfg.h"
  20. #include "mdmsg.h"
  21. #include "stssites.hxx"
  22. #include "vswriter.h"
  23. #include "stswriter.h"
  24. #include "vs_seh.hxx"
  25. #include "vs_trace.hxx"
  26. #include "vs_debug.hxx"
  27. #include "bsstring.hxx"
  28. #include "wrtcommon.hxx"
  29. #include "allerror.h"
  30. #include "sqlwrtguid.h"
  31. #include "sqlsnap.h"
  32. ////////////////////////////////////////////////////////////////////////
  33. // Standard foo for file name aliasing. This code block must be after
  34. // all includes of VSS header files.
  35. //
  36. #ifdef VSS_FILE_ALIAS
  37. #undef VSS_FILE_ALIAS
  38. #endif
  39. #define VSS_FILE_ALIAS "STSWRTRC"
  40. //
  41. ////////////////////////////////////////////////////////////////////////
  42. // writer id
  43. static GUID s_writerId =
  44. {
  45. 0x4dd6f8dd, 0xbf50, 0x4585, 0x95, 0xde, 0xfb, 0x43, 0x7c, 0x08, 0x31, 0xa6
  46. };
  47. // writer name
  48. static LPCWSTR s_wszWriterName = L"SharepointTSWriter";
  49. STDMETHODCALLTYPE CSTSWriter::~CSTSWriter()
  50. {
  51. Uninitialize();
  52. delete [] m_rgiSites;
  53. delete m_pSites;
  54. }
  55. // initialize and subscribe the writer
  56. HRESULT STDMETHODCALLTYPE CSTSWriter::Initialize()
  57. {
  58. CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSWriter::Initialize");
  59. try
  60. {
  61. // only initialize the writer if the correct version of sharepoint
  62. // is running on the system
  63. m_pSites = new CSTSSites;
  64. if (m_pSites == NULL)
  65. ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"out of memory");
  66. if (m_pSites->ValidateSharepointVersion())
  67. {
  68. if (!m_pSites->Initialize())
  69. {
  70. ft.Throw (VSSDBG_STSWRITER, E_UNEXPECTED,
  71. L"Failed to initialize the SharepointTS writer.");
  72. }
  73. ft.hr = CVssWriter::Initialize
  74. (
  75. s_writerId, // writer id
  76. s_wszWriterName, // writer name
  77. VSS_UT_USERDATA, // writer handles user data
  78. VSS_ST_OTHER, // not a database
  79. VSS_APP_FRONT_END, // sql server freezes after us
  80. 60000 // 60 second freeze timeout
  81. );
  82. if (ft.HrFailed())
  83. ft.Throw
  84. (
  85. VSSDBG_STSWRITER,
  86. E_UNEXPECTED,
  87. L"Failed to initialize the SharepointTS writer. hr = 0x%08lx",
  88. ft.hr
  89. );
  90. // subscribe the writer for COM+ events
  91. ft.hr = Subscribe();
  92. if (ft.HrFailed())
  93. ft.Throw
  94. (
  95. VSSDBG_STSWRITER,
  96. E_UNEXPECTED,
  97. L"Subscribing the SharepointTS server writer failed. hr = %0x08lx",
  98. ft.hr
  99. );
  100. // indicate that th writer is successfully subscribed
  101. m_bSubscribed = true;
  102. }
  103. }
  104. VSS_STANDARD_CATCH(ft)
  105. return ft.hr;
  106. }
  107. // uninitialize the writer. This means unsubscribing the writer
  108. HRESULT STDMETHODCALLTYPE CSTSWriter::Uninitialize()
  109. {
  110. CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSWriter::Uninitialize");
  111. // unsubscribe writer if it is already subscribed
  112. if (m_bSubscribed)
  113. return Unsubscribe();
  114. return ft.hr;
  115. }
  116. // handle OnPrepareBackup event. Determine whether components selected for
  117. // backup are valid and if so store some metadata in them that is used
  118. // to verify that restore properly restores the sites data to its original
  119. // locations. Keep
  120. bool STDMETHODCALLTYPE CSTSWriter::OnPrepareBackup
  121. (
  122. IN IVssWriterComponents *pComponents
  123. )
  124. {
  125. CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSWriter::OnPrepareBackup");
  126. VSS_PWSZ wszMetadataForSite = NULL;
  127. try
  128. {
  129. // count of components
  130. UINT cComponents = 0;
  131. // clear array of sites being operated on
  132. delete m_rgiSites;
  133. m_rgiSites = NULL;
  134. m_cSites = 0;
  135. // determine if we are doing a component or volume based backup
  136. m_bVolumeBackup = !AreComponentsSelected();
  137. if (!m_bVolumeBackup)
  138. {
  139. // get count of components
  140. ft.hr = pComponents->GetComponentCount(&cComponents);
  141. ft.CheckForErrorInternal(VSSDBG_STSWRITER, L"IVssWriterComponents::GetComponentCount");
  142. // allocate array of sites
  143. m_rgiSites = new DWORD[cComponents];
  144. if (m_rgiSites == NULL)
  145. ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"out of memory");
  146. // loop through components
  147. for(UINT iComponent = 0; iComponent < cComponents; iComponent++)
  148. {
  149. // get component
  150. CComPtr<IVssComponent> pComponent;
  151. ft.hr = pComponents->GetComponent(iComponent, &pComponent);
  152. ft.CheckForErrorInternal(VSSDBG_STSWRITER, L"IVssWriterComponents::GetComponent");
  153. CComBSTR bstrLogicalPath;
  154. CComBSTR bstrSiteName;
  155. // get logal path and component name
  156. ft.hr = pComponent->GetLogicalPath(&bstrLogicalPath);
  157. ft.CheckForErrorInternal(VSSDBG_STSWRITER, L"IVssComponent::GetLogicalPath");
  158. ft.hr = pComponent->GetComponentName(&bstrSiteName);
  159. ft.CheckForErrorInternal(VSSDBG_STSWRITER, L"IVssComponent::GetComponentName");
  160. // logical paths are not supported for STS components
  161. if (bstrLogicalPath && wcslen(bstrLogicalPath) != 0)
  162. ft.Throw(VSSDBG_STSWRITER, VSS_E_OBJECT_NOT_FOUND, L"STS components do not have logical paths");
  163. // try parsing the component name as a site name
  164. DWORD iSite;
  165. STSSITEPROBLEM problem;
  166. if (!ParseComponentName(bstrSiteName, iSite, problem))
  167. ft.Throw(VSSDBG_STSWRITER, VSS_E_OBJECT_NOT_FOUND, L"sites name is not valid");
  168. // see if site is already in array of sites being backed up
  169. for(DWORD iC = 0; iC < iComponent; iC++)
  170. {
  171. if (m_rgiSites[iC] == iSite)
  172. break;
  173. }
  174. // if site already exists then throw an error
  175. if (iC < iComponent)
  176. ft.Throw(VSSDBG_STSWRITER, VSS_E_OBJECT_ALREADY_EXISTS, L"site backed up twice");
  177. // build backup metadata for site
  178. wszMetadataForSite = BuildSiteMetadata(iSite);
  179. // save backup metadata for site
  180. ft.hr = pComponent->SetBackupMetadata(wszMetadataForSite);
  181. ft.CheckForError(VSSDBG_STSWRITER, L"IVssComponent::SetBackupMetadata");
  182. // free allocated metadat for site
  183. CoTaskMemFree(wszMetadataForSite);
  184. wszMetadataForSite = NULL;
  185. // save site to be backed up
  186. m_rgiSites[iComponent] = iSite;
  187. }
  188. }
  189. // number of sites to be backed up is number of components
  190. m_cSites = cComponents;
  191. }
  192. VSS_STANDARD_CATCH(ft)
  193. // free dangling metadata for site if operation failed
  194. CoTaskMemFree(wszMetadataForSite);
  195. TranslateWriterError(ft.hr);
  196. return !ft.HrFailed();
  197. }
  198. // parse a component name to see if it refers to a valid site
  199. // the component name is comment_[instanceId] where comment is
  200. // the server comment for the site and the instance id is the
  201. // IIS instance id
  202. bool CSTSWriter::ParseComponentName
  203. (
  204. LPCWSTR wszComponentName,
  205. DWORD &iSite,
  206. STSSITEPROBLEM &problem
  207. )
  208. {
  209. CVssFunctionTracer(VSSDBG_STSWRITER, L"CSTSWriter::ParseComponentName");
  210. // pointer to CSTSSites object should already be initialized
  211. BS_ASSERT(m_pSites);
  212. // compute length of component name
  213. DWORD cwc = (DWORD) wcslen(wszComponentName);
  214. // assume site name is properly parsed
  215. problem = STSP_SUCCESS;
  216. // search for last underline site name looks like
  217. // servercomment_instanceid where servercomment is the
  218. // IIS server comment field for the virtual web and instance id
  219. // is the IIS instance id for the virtual web
  220. LPWSTR wszId = wcsrchr(wszComponentName, L'_');
  221. if (wszId == NULL || wcslen(wszId) < 4)
  222. return false;
  223. // scan for instance id of site
  224. DWORD siteId;
  225. DWORD cFields = swscanf(wszId, L"_[%d]", &siteId);
  226. if (cFields == 0)
  227. {
  228. // if instance id doesn't parse then there is a syntax error
  229. problem = STSP_SYNTAXERROR;
  230. return false;
  231. }
  232. // get # of sites on the current machine
  233. DWORD cSites = m_pSites->GetSiteCount();
  234. // loop through sites
  235. for(iSite = 0; iSite < cSites; iSite++)
  236. {
  237. // break out of loop if site id matches
  238. if (m_pSites->GetSiteId(iSite) == siteId)
  239. break;
  240. }
  241. // if site id is not found then return false
  242. if (iSite == cSites)
  243. {
  244. problem = STSP_SITENOTFOUND;
  245. return false;
  246. }
  247. // get site comment
  248. VSS_PWSZ wszComment = m_pSites->GetSiteComment(iSite);
  249. // validate that comment matches prefix of component name
  250. bool bValid = wcslen(wszComment) == cwc - wcslen(wszId) &&
  251. _wcsnicmp(wszComment, wszComponentName, wcslen(wszComment)) == 0;
  252. // free site comment
  253. CoTaskMemFree(wszComment);
  254. if (!bValid)
  255. {
  256. problem = STSP_SITENAMEMISMATCH;
  257. return false;
  258. }
  259. // validate that site can be backed up.
  260. return ValidateSiteValidity(iSite, problem);
  261. }
  262. // validate site validity to be backed up and restored. This means
  263. // that all files and the database are local to the current machine
  264. bool CSTSWriter::ValidateSiteValidity(DWORD iSite, STSSITEPROBLEM &problem)
  265. {
  266. CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSWriter::ValidateSiteValidity");
  267. BS_ASSERT(m_pSites);
  268. // co task strings that need to be freed if function throws
  269. VSS_PWSZ wszDsn = NULL;
  270. VSS_PWSZ wszContentRoot = NULL;
  271. VSS_PWSZ wszConfigRoot = NULL;
  272. try
  273. {
  274. // get dsn for site
  275. wszDsn = m_pSites->GetSiteDSN(iSite);
  276. LPWSTR wszServer, wszInstance, wszDb;
  277. // parse the dsn into server name, instance name and database name
  278. if (!ParseDsn(wszDsn, wszServer, wszInstance, wszDb))
  279. {
  280. // site DSN is invalid
  281. problem = STSP_SITEDSNINVALID;
  282. CoTaskMemFree(wszDsn);
  283. return false;
  284. }
  285. // verify that the server is local
  286. if (!ValidateServerIsLocal(wszServer))
  287. {
  288. problem = STSP_SQLSERVERNOTLOCAL;
  289. CoTaskMemFree(wszDsn);
  290. return false;
  291. }
  292. // free up dsn. we are done with it
  293. CoTaskMemFree(wszDsn);
  294. wszDsn = NULL;
  295. // get content root of the site
  296. wszContentRoot = m_pSites->GetSiteRoot(iSite);
  297. // validate that path to root is on the local machine
  298. if (!ValidatePathIsLocal(wszContentRoot))
  299. {
  300. problem = STSP_CONTENTNOTLOCAL;
  301. CoTaskMemFree(wszContentRoot);
  302. return false;
  303. }
  304. // free up content root
  305. CoTaskMemFree(wszContentRoot);
  306. wszContentRoot = NULL;
  307. // get configuration root of the site
  308. wszConfigRoot = m_pSites->GetSiteRoles(iSite);
  309. // validate that configuration path is local
  310. if (!ValidatePathIsLocal(wszConfigRoot))
  311. {
  312. problem = STSP_CONFIGNOTLOCAL;
  313. CoTaskMemFree(wszConfigRoot);
  314. return false;
  315. }
  316. // free up configuration root
  317. CoTaskMemFree(wszConfigRoot);
  318. return true;
  319. }
  320. catch(...)
  321. {
  322. // free allocated memory and rethrow error
  323. CoTaskMemFree(wszDsn);
  324. CoTaskMemFree(wszContentRoot);
  325. CoTaskMemFree(wszConfigRoot);
  326. throw;
  327. }
  328. }
  329. // build metadata for the site
  330. // the metadata is used to validate that the site can be restored properly
  331. // it consists of the content root of the site, the configuration root
  332. // of the site, and the sql database. All need to match
  333. VSS_PWSZ CSTSWriter::BuildSiteMetadata(DWORD iSite)
  334. {
  335. CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSWriter::BuildSiteMetadata");
  336. BS_ASSERT(m_pSites);
  337. // co task allocated strings that need to be freed if the function throws
  338. VSS_PWSZ wszDsn = NULL;
  339. VSS_PWSZ wszContentRoot = NULL;
  340. VSS_PWSZ wszConfigRoot = NULL;
  341. VSS_PWSZ wszMetadata = NULL;
  342. try
  343. {
  344. // get dsn for site
  345. wszDsn = m_pSites->GetSiteDSN(iSite);
  346. LPWSTR wszInstance, wszDb, wszServer;
  347. // break dsn into server, instance, and database names
  348. if (!ParseDsn(wszDsn, wszServer, wszInstance, wszDb))
  349. {
  350. // since we already parsed the dsn once, we don't expect
  351. // parsing it to fail when we try a second time
  352. BS_ASSERT(FALSE && L"shouldn't get here");
  353. ft.Throw(VSSDBG_STSWRITER, E_UNEXPECTED, L"unexpected failure parsing the DSN");
  354. }
  355. // get the content root of the site
  356. wszContentRoot = m_pSites->GetSiteRoot(iSite);
  357. // get the configuration path for the site
  358. wszConfigRoot = m_pSites->GetSiteRoles(iSite);
  359. // compute size of metadata string. the format of the string is
  360. // servername\instancename1111dbname2222siteroot3333configroot where
  361. // 1111 is the length fo the database name, 2222 is the length of the site
  362. // root, and 3333 is the length of th econfiguration root. The lengths
  363. // are all 4 digit hex numbers
  364. DWORD cwc = (DWORD) ((wszServer ? wcslen(wszServer) : 0) + (wszInstance ? wcslen(wszInstance) : 0) + wcslen(wszDb) + wcslen(wszContentRoot) + wcslen(wszConfigRoot) + (3 * 4) + 3);
  365. wszMetadata = (VSS_PWSZ) CoTaskMemAlloc(cwc * sizeof(WCHAR));
  366. if (wszMetadata == NULL)
  367. ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"out of memory");
  368. // create server and instance parts of path
  369. if (wszServer && wszInstance)
  370. swprintf(wszMetadata, L"%s\\%s", wszServer, wszInstance);
  371. else if (wszServer)
  372. swprintf(wszMetadata, L"%s\\", wszServer);
  373. else if (wszInstance)
  374. wcscpy(wszMetadata, wszInstance);
  375. // include database name, site root, and configuration root
  376. swprintf
  377. (
  378. wszMetadata + wcslen(wszMetadata),
  379. L";%04x%s%04x%s%04x%s",
  380. wcslen(wszDb),
  381. wszDb,
  382. wcslen(wszContentRoot),
  383. wszContentRoot,
  384. wcslen(wszConfigRoot),
  385. wszConfigRoot
  386. );
  387. // free up dsn, config root and content root
  388. CoTaskMemFree(wszDsn);
  389. CoTaskMemFree(wszConfigRoot);
  390. CoTaskMemFree(wszContentRoot);
  391. return wszMetadata;
  392. }
  393. catch(...)
  394. {
  395. // free memory and rethrow error
  396. CoTaskMemFree(wszDsn);
  397. CoTaskMemFree(wszConfigRoot);
  398. CoTaskMemFree(wszContentRoot);
  399. CoTaskMemFree(wszMetadata);
  400. throw;
  401. }
  402. }
  403. // determine if a database is on a snapshotted device. If it is partially
  404. // on a snapshotted device throw VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT
  405. bool CSTSWriter::IsDatabaseAffected(LPCWSTR wszInstance, LPCWSTR wszDb)
  406. {
  407. CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSWriter::IsDatabaseAffected");
  408. CSqlEnumerator *pEnumServers = NULL;
  409. CSqlEnumerator *pEnumDatabases = NULL;
  410. CSqlEnumerator *pEnumFiles = NULL;
  411. try
  412. {
  413. ServerInfo server;
  414. DatabaseInfo database;
  415. DatabaseFileInfo file;
  416. // create enumerator for sql server instances
  417. pEnumServers = CreateSqlEnumerator();
  418. if (pEnumServers == NULL)
  419. ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"Failed to create CSqlEnumerator");
  420. // find first server
  421. ft.hr = pEnumServers->FirstServer(&server);
  422. while(ft.hr != DB_S_ENDOFROWSET)
  423. {
  424. // check for error code
  425. if (ft.HrFailed())
  426. ft.Throw
  427. (
  428. VSSDBG_STSWRITER,
  429. E_UNEXPECTED,
  430. L"Enumerating database servers failed. hr = 0x%08lx",
  431. ft.hr
  432. );
  433. if (server.isOnline &&
  434. (wszInstance == NULL && wcslen(server.name) == 0) ||
  435. _wcsicmp(server.name, wszInstance) == 0)
  436. {
  437. // if instance name matches, then try finding the
  438. // database by creating the database enumerator
  439. pEnumDatabases = CreateSqlEnumerator();
  440. if (pEnumDatabases == NULL)
  441. ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"Failed to create CSqlEnumerator");
  442. // find first database
  443. ft.hr = pEnumDatabases->FirstDatabase(server.name, &database);
  444. while(ft.hr != DB_S_ENDOFROWSET)
  445. {
  446. // check for error
  447. if (ft.HrFailed())
  448. ft.Throw
  449. (
  450. VSSDBG_GEN,
  451. E_UNEXPECTED,
  452. L"Enumerating databases failed. hr = 0x%08lx",
  453. ft.hr
  454. );
  455. // if database name matches. then scan files
  456. // to see what volumes they are on
  457. if (_wcsicmp(database.name, wszDb) == 0 && database.supportsFreeze)
  458. {
  459. bool fAffected = false;
  460. DWORD cFiles = 0;
  461. // recreate enumerator for files
  462. BS_ASSERT(pEnumFiles == NULL);
  463. pEnumFiles = CreateSqlEnumerator();
  464. if (pEnumFiles == NULL)
  465. ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"Failed to create CSqlEnumerator");
  466. // findfirst database file
  467. ft.hr = pEnumFiles->FirstFile(server.name, database.name, &file);
  468. while(ft.hr != DB_S_ENDOFROWSET)
  469. {
  470. // check for error
  471. if (ft.HrFailed())
  472. ft.Throw
  473. (
  474. VSSDBG_GEN,
  475. E_UNEXPECTED,
  476. L"Enumerating database files failed. hr = 0x%08lx",
  477. ft.hr
  478. );
  479. // determine if database file is included in the
  480. // backup
  481. if (IsPathAffected(file.name))
  482. {
  483. // if it is and other files aren't then
  484. // the snapshot is inconsistent
  485. if (!fAffected && cFiles > 0)
  486. ft.Throw(VSSDBG_STSWRITER, HRESULT_FROM_WIN32(E_SQLLIB_TORN_DB), L"some database files are snapshot and some aren't");
  487. fAffected = true;
  488. }
  489. else
  490. {
  491. // if it isn't and other files are, then
  492. // the snapshot is inconsistent
  493. if (fAffected)
  494. ft.Throw(VSSDBG_STSWRITER, HRESULT_FROM_WIN32(E_SQLLIB_TORN_DB), L"some database files are snapshot and some aren't");
  495. }
  496. // continue at next file
  497. ft.hr = pEnumFiles->NextFile(&file);
  498. cFiles++;
  499. }
  500. delete pEnumFiles;
  501. pEnumFiles = NULL;
  502. delete pEnumDatabases;
  503. pEnumDatabases = NULL;
  504. delete pEnumServers;
  505. pEnumServers = NULL;
  506. return fAffected;
  507. }
  508. // continue at next database
  509. ft.hr = pEnumDatabases->NextDatabase(&database);
  510. }
  511. // done with database enumerator
  512. delete pEnumDatabases;
  513. pEnumDatabases = NULL;
  514. }
  515. // continue at next server
  516. ft.hr = pEnumServers->NextServer(&server);
  517. }
  518. ft.Throw(VSSDBG_STSWRITER, E_UNEXPECTED, L"database is not found %s\\%s", server.name, database.name);
  519. }
  520. catch(...)
  521. {
  522. // delete enumerators and rethrow error
  523. delete pEnumFiles;
  524. delete pEnumServers;
  525. delete pEnumDatabases;
  526. throw;
  527. }
  528. // we won't really ever get here. This is just to keep the compiler
  529. // happy
  530. return false;
  531. }
  532. // determine if a site is completely contained in the set of volumes being
  533. // snapshotted. If it is partially contained then throw
  534. // VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT.
  535. bool CSTSWriter::IsSiteSnapshotted(DWORD iSite)
  536. {
  537. CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSWriter::IsSiteSnapshotted");
  538. BS_ASSERT(m_pSites);
  539. // co task allocated strings that need to be freed
  540. // in the case of a failure
  541. VSS_PWSZ wszDsn = NULL;
  542. VSS_PWSZ wszContentRoot = NULL;
  543. VSS_PWSZ wszConfigRoot = NULL;
  544. VSS_PWSZ wszInstanceName = NULL;
  545. try
  546. {
  547. // get dsn for site
  548. wszDsn = m_pSites->GetSiteDSN(iSite);
  549. // get content root for the site
  550. wszContentRoot = m_pSites->GetSiteRoot(iSite);
  551. // gt configuration root for the site
  552. wszConfigRoot = m_pSites->GetSiteRoles(iSite);
  553. LPWSTR wszServer, wszInstance, wszDb;
  554. // parse the site dsn into server, instance, and database
  555. if (!ParseDsn(wszDsn, wszServer, wszInstance, wszDb))
  556. {
  557. // shouldn't get here since we previously parsed
  558. // the site's dsn
  559. BS_ASSERT(FALSE && L"shouldn't get here");
  560. ft.Throw(VSSDBG_STSWRITER, E_UNEXPECTED, L"dsn is invalid");
  561. }
  562. // compute instance name as server\\instance
  563. wszInstanceName = (VSS_PWSZ) CoTaskMemAlloc(((wszServer ? wcslen(wszServer) : 0) + (wszInstance ? wcslen(wszInstance) : 0) + 2) * sizeof(WCHAR));
  564. if (wszInstanceName == NULL)
  565. ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"out of memory");
  566. if (wszServer)
  567. {
  568. wcscpy(wszInstanceName, wszServer);
  569. wcscat(wszInstanceName, L"\\");
  570. if (wszInstance)
  571. wcscat(wszInstanceName, wszInstance);
  572. }
  573. else if (wszInstance)
  574. wcscpy(wszInstanceName, wszInstance);
  575. else
  576. wszInstanceName[0] = L'\0';
  577. // determine if database is snapshotted
  578. bool bDbAffected = IsDatabaseAffected(wszInstanceName, wszDb);
  579. // determine if content root is snapshotted
  580. bool bContentAffected = IsPathAffected(wszContentRoot);
  581. // determine if configuration root is snapshotted
  582. bool bConfigAffected = IsPathAffected(wszConfigRoot);
  583. // free up memory for dsn, content root, and configuration root
  584. CoTaskMemFree(wszDsn);
  585. CoTaskMemFree(wszContentRoot);
  586. CoTaskMemFree(wszConfigRoot);
  587. wszDsn = NULL;
  588. wszContentRoot = NULL;
  589. wszConfigRoot = NULL;
  590. if (bDbAffected && bContentAffected && bConfigAffected)
  591. // if all are snapshotted then return true
  592. return true;
  593. else if (bDbAffected || bContentAffected || bConfigAffected)
  594. // if some but not all are snapshotted, then indicate
  595. // the inconsistency
  596. ft.Throw
  597. (
  598. VSSDBG_STSWRITER,
  599. VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT,
  600. L"site %d partially affected by snapshot",
  601. m_pSites->GetSiteId(iSite)
  602. );
  603. else
  604. // if none are snapshotted, then return false
  605. return false;
  606. }
  607. catch(...)
  608. {
  609. // free memory and rethrow exception
  610. CoTaskMemFree(wszDsn);
  611. CoTaskMemFree(wszConfigRoot);
  612. CoTaskMemFree(wszContentRoot);
  613. CoTaskMemFree(wszInstanceName);
  614. throw;
  615. }
  616. // will not get here. Just here to keep the compiler happy
  617. return false;
  618. }
  619. // lockdown all sites that are on volumes being snapshotted. If any sites
  620. // are both on volumes being snapshotted and not being snapshot then
  621. // indicate tht the snapshot is inconsistent. If the quota database is
  622. // on a volume being snapshoted then lock it as well
  623. void CSTSWriter::LockdownAffectedSites()
  624. {
  625. CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSWriter::LockdownAffectedSites");
  626. // co task string that needs to be freed in the case of exception
  627. VSS_PWSZ wszQuotaDbPath = NULL;
  628. BS_ASSERT(m_pSites);
  629. try
  630. {
  631. // determine if bootable system state is not being backed up. If so,
  632. // then the quota database is locked if its path is being snapshotted.
  633. // if bootable system state is already being backed up, the the
  634. // quota database is already locked
  635. if (!IsBootableSystemStateBackedUp())
  636. {
  637. // determine if quota database is being snapshotted
  638. wszQuotaDbPath = m_pSites->GetQuotaDatabase();
  639. if (IsPathAffected(wszQuotaDbPath))
  640. // if so then lock it
  641. m_pSites->LockQuotaDatabase();
  642. // free memory for quota db path
  643. CoTaskMemFree(wszQuotaDbPath);
  644. wszQuotaDbPath = NULL;
  645. }
  646. // get count of sites
  647. DWORD cSites = m_pSites->GetSiteCount();
  648. // loop through sites
  649. for(DWORD iSite = 0; iSite < cSites; iSite++)
  650. {
  651. // if site is snapshotted lock it
  652. if (IsSiteSnapshotted(iSite))
  653. m_pSites->LockSiteContents(iSite);
  654. }
  655. }
  656. catch(...)
  657. {
  658. // free memory and rethrow error
  659. CoTaskMemFree(wszQuotaDbPath);
  660. throw;
  661. }
  662. }
  663. // handle prepare snapshot event. Lock any sites that need to be
  664. // locked based on components document or on volumes being snapshotted
  665. bool STDMETHODCALLTYPE CSTSWriter::OnPrepareSnapshot()
  666. {
  667. CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSWriter::OnPrepareSnapshot");
  668. BS_ASSERT(m_pSites);
  669. try
  670. {
  671. // lock quota database if bootable system state is being backed up
  672. if (IsBootableSystemStateBackedUp())
  673. m_pSites->LockQuotaDatabase();
  674. if (m_bVolumeBackup)
  675. // if volume backup, then lock sites based on whether they
  676. // are fully on the snapshotted volumes.
  677. LockdownAffectedSites();
  678. else
  679. {
  680. // loop through sites being backed up
  681. for (DWORD i = 0; i < m_cSites; i++)
  682. {
  683. DWORD iSite = m_rgiSites[i];
  684. // validate that site is on volumes being snapshotted
  685. if (!IsSiteSnapshotted(iSite))
  686. // the site is in selected to be backed up. it should
  687. // be snapshotted as well
  688. ft.Throw
  689. (
  690. VSSDBG_STSWRITER,
  691. VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT,
  692. L"a site is selected but is on volumes that are not snapshot"
  693. );
  694. // lock site
  695. m_pSites->LockSiteContents(iSite);
  696. }
  697. }
  698. }
  699. VSS_STANDARD_CATCH(ft)
  700. if (ft.HrFailed())
  701. {
  702. // unlock anything that was locked if operation fails
  703. m_pSites->UnlockSites();
  704. m_pSites->UnlockQuotaDatabase();
  705. TranslateWriterError(ft.hr);
  706. }
  707. return !ft.HrFailed();
  708. }
  709. // freeze operation. Nothing is done here since all the work is done
  710. // during the prepare phase
  711. bool STDMETHODCALLTYPE CSTSWriter::OnFreeze()
  712. {
  713. CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSWriter::OnFreeze");
  714. return true;
  715. }
  716. // unlock everything at thaw
  717. bool STDMETHODCALLTYPE CSTSWriter::OnThaw()
  718. {
  719. CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSWriter::OnThaw");
  720. BS_ASSERT(m_pSites);
  721. m_pSites->UnlockSites();
  722. m_pSites->UnlockQuotaDatabase();
  723. return true;
  724. }
  725. // unlock everything at abort
  726. bool STDMETHODCALLTYPE CSTSWriter::OnAbort()
  727. {
  728. CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSWriter::OnAbort");
  729. BS_ASSERT(m_pSites);
  730. m_pSites->UnlockQuotaDatabase();
  731. m_pSites->UnlockSites();
  732. return true;
  733. }
  734. // prefix for dsn strings as stored in registry
  735. static LPCWSTR s_wszDsnPrefix = L"Provider=sqloledb;Server=";
  736. // separtor between fields in DSN string
  737. const WCHAR x_wcDsnSeparator = L';';
  738. // prefix for database name
  739. static LPCWSTR s_wszDsnDbPrefix = L";Database=";
  740. const DWORD x_cwcWriterIdPrefix = 32 + 2 + 4 + 1; // 32 nibbles + 2 braces + 4 dashes + 1 colon
  741. // {12345678-1234-1234-1234-123456789abc}:
  742. // check validity of dsn and break it up into its components.
  743. bool CSTSWriter::ParseDsn
  744. (
  745. LPWSTR wszDsn,
  746. LPWSTR &wszServer, // server name [out]
  747. LPWSTR &wszInstance, // instance name [out]
  748. LPWSTR &wszDb // database name [out]
  749. )
  750. {
  751. // check validity of beginning of dsn
  752. if (wcslen(wszDsn) <= wcslen(s_wszDsnPrefix) ||
  753. _wcsnicmp(wszDsn, s_wszDsnPrefix, wcslen(s_wszDsnPrefix)) != 0)
  754. return false;
  755. // skip to start of server name
  756. wszServer = wszDsn + wcslen(s_wszDsnPrefix);
  757. // search for next semicolon which is the start of the database name
  758. LPWSTR wszDbSection = wcschr(wszServer, x_wcDsnSeparator);
  759. // if not found, then dsn is invalid
  760. if (wszServer == NULL)
  761. return false;
  762. // make sure form of name is Database=foo
  763. if (wcslen(wszDbSection) <= wcslen(s_wszDsnDbPrefix) ||
  764. _wcsnicmp(wszDbSection, s_wszDsnDbPrefix, wcslen(s_wszDsnDbPrefix)) != 0)
  765. return false;
  766. // skip to beginning of database name
  767. wszDb = wszDbSection + wcslen(s_wszDsnDbPrefix);
  768. if (wcslen(wszDb) == 0)
  769. return false;
  770. // setup separator for server name, i.e., null out the semicolon
  771. // before the Database=...
  772. *wszDbSection = L'\0';
  773. // search for instance name. Server name is form machine\instance
  774. wszInstance = wcschr(wszServer, L'\\');
  775. if (wszInstance != NULL)
  776. {
  777. // null out server name and update instance pointer
  778. // to point after backslash
  779. *wszInstance = L'\0';
  780. wszInstance++;
  781. // set instance to NULL if it is 0 length
  782. if (wcslen(wszInstance) == 0)
  783. wszInstance = NULL;
  784. }
  785. return true;
  786. }
  787. // handle request for WRITER_METADATA
  788. // implements CVssWriter::OnIdentify
  789. bool STDMETHODCALLTYPE CSTSWriter::OnIdentify(IVssCreateWriterMetadata *pMetadata)
  790. {
  791. CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSWriter::OnIdentify");
  792. BS_ASSERT(m_pSites);
  793. // co task strings that need to be freed if exception is thrown
  794. VSS_PWSZ wszSiteName = NULL;
  795. VSS_PWSZ wszComponentName = NULL;
  796. VSS_PWSZ wszDsn = NULL;
  797. VSS_PWSZ wszContentRoot = NULL;
  798. VSS_PWSZ wszConfigRoot = NULL;
  799. VSS_PWSZ wszDbComponentPath = NULL;
  800. try
  801. {
  802. // setup restore method to restore if can replace
  803. ft.hr = pMetadata->SetRestoreMethod
  804. (
  805. VSS_RME_RESTORE_IF_CAN_REPLACE,
  806. NULL,
  807. NULL,
  808. VSS_WRE_ALWAYS,
  809. false
  810. );
  811. ft.CheckForErrorInternal(VSSDBG_STSWRITER, L"IVssCreateWriterMetadata::SetRestoreMethod");
  812. // loop through sites adding one component for each site
  813. DWORD cSites = m_pSites->GetSiteCount();
  814. for(DWORD iSite = 0; iSite < cSites; iSite++)
  815. {
  816. do
  817. {
  818. // component name is server comment concatenated with
  819. // _[instance id] so if server comment for site is foo
  820. // and the instance id is 69105 then the component
  821. // name is foo_[69105]
  822. //
  823. DWORD siteId = m_pSites->GetSiteId(iSite);
  824. wszSiteName = m_pSites->GetSiteComment(iSite);
  825. WCHAR buf[32];
  826. swprintf(buf, L"_[%d]", siteId);
  827. // allocate string for component name
  828. wszComponentName = (VSS_PWSZ) CoTaskMemAlloc((wcslen(wszSiteName) + wcslen(buf) + 1) * sizeof(WCHAR));
  829. if (wszComponentName == NULL)
  830. ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"out of memory");
  831. // construct component name
  832. wcscpy(wszComponentName, wszSiteName);
  833. wcscat(wszComponentName, buf);
  834. // get site dsn and parse it
  835. wszDsn = m_pSites->GetSiteDSN(iSite);
  836. LPWSTR wszServer, wszDb, wszInstance;
  837. // if site dsn is not valid, then skip component
  838. if (!ParseDsn(wszDsn, wszServer, wszInstance, wszDb))
  839. continue;
  840. // only include component if server name refers to the
  841. // local machine
  842. bool bServerIsLocal = ValidateServerIsLocal(wszServer);
  843. // compute size of funky file name for database component
  844. DWORD cwcDbComponentPath = (DWORD) (wszServer ? wcslen(wszServer) : 0) + 2 + x_cwcWriterIdPrefix;
  845. if (wszInstance)
  846. cwcDbComponentPath += (DWORD) wcslen(wszInstance) + 1;
  847. // allocate component name
  848. wszDbComponentPath = (VSS_PWSZ) CoTaskMemAlloc(cwcDbComponentPath * sizeof(WCHAR));
  849. if (wszDbComponentPath == NULL)
  850. ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"out of memory");
  851. // fill in component path name
  852. // {sql id}:server\instance or
  853. // {sql id}:server\ or
  854. // {sql id}:\instance or
  855. // {sql id}:\
  856. //
  857. if (wszServer && wszInstance)
  858. swprintf
  859. (
  860. wszDbComponentPath,
  861. WSTR_GUID_FMT L":\\%s\\%s",
  862. GUID_PRINTF_ARG(WRITERID_SqlWriter),
  863. wszServer,
  864. wszInstance
  865. );
  866. else if (wszServer && wszInstance == NULL)
  867. swprintf
  868. (
  869. wszDbComponentPath,
  870. WSTR_GUID_FMT L":\\%s\\",
  871. GUID_PRINTF_ARG(WRITERID_SqlWriter),
  872. wszServer
  873. );
  874. else if (wszInstance)
  875. swprintf
  876. (
  877. wszDbComponentPath,
  878. WSTR_GUID_FMT L":\\%s",
  879. GUID_PRINTF_ARG(WRITERID_SqlWriter),
  880. wszInstance
  881. );
  882. else
  883. swprintf
  884. (
  885. wszDbComponentPath,
  886. WSTR_GUID_FMT L":\\",
  887. GUID_PRINTF_ARG(WRITERID_SqlWriter)
  888. );
  889. // get content root of the site
  890. wszContentRoot = m_pSites->GetSiteRoot(iSite);
  891. bool bContentIsLocal = ValidatePathIsLocal(wszContentRoot);
  892. // get configuration root of the site
  893. wszConfigRoot = m_pSites->GetSiteRoles(iSite);
  894. bool bConfigIsLocal = ValidatePathIsLocal(wszConfigRoot);
  895. bool bNonLocal = !bServerIsLocal || !bContentIsLocal || !bConfigIsLocal;
  896. // add component to medatadata. comment indicates
  897. // whether site is local or not. Non-local sites may not
  898. // be backed up
  899. ft.hr = pMetadata->AddComponent
  900. (
  901. VSS_CT_FILEGROUP, // component type
  902. NULL, // logical path
  903. wszComponentName, // component name
  904. bNonLocal ? L"!!non-local-site!!" : NULL, // caption
  905. NULL, // icon
  906. 0, // length of icon
  907. TRUE, // restore metadata
  908. FALSE, // notify on backup complete
  909. TRUE // selectable
  910. );
  911. ft.CheckForErrorInternal(VSSDBG_STSWRITER, L"IVssCreateWriterMetadata::AddComponent");
  912. // add database as recursive component
  913. ft.hr = pMetadata->AddFilesToFileGroup
  914. (
  915. NULL,
  916. wszComponentName,
  917. wszDbComponentPath,
  918. wszDb,
  919. false,
  920. NULL
  921. );
  922. ft.CheckForErrorInternal(VSSDBG_STSWRITER, L"IVssCreateWriterMetadata::AddFilesToFileGroup");
  923. // add all files under the content root
  924. ft.hr = pMetadata->AddFilesToFileGroup
  925. (
  926. NULL,
  927. wszComponentName,
  928. wszContentRoot,
  929. L"*",
  930. true,
  931. NULL
  932. );
  933. ft.CheckForErrorInternal(VSSDBG_STSWRITER, L"IVssCreateWriterMetadata::AddFilesToFileGroup");
  934. // add all files under the appropriate directory in
  935. // Documents and Settings
  936. ft.hr = pMetadata->AddFilesToFileGroup
  937. (
  938. NULL,
  939. wszComponentName,
  940. wszConfigRoot,
  941. L"*",
  942. true,
  943. NULL
  944. );
  945. ft.CheckForErrorInternal(VSSDBG_STSWRITER, L"IVssCreateWriterMetadata::AddFilesToFileGroup");
  946. } while(FALSE);
  947. // free up memory allocated in this iteration
  948. VssFreeString(wszContentRoot);
  949. VssFreeString(wszConfigRoot);
  950. VssFreeString(wszDbComponentPath);
  951. VssFreeString(wszDsn);
  952. VssFreeString(wszComponentName);
  953. VssFreeString(wszSiteName);
  954. }
  955. }
  956. VSS_STANDARD_CATCH(ft)
  957. // free up memory in case of failure
  958. VssFreeString(wszContentRoot);
  959. VssFreeString(wszConfigRoot);
  960. VssFreeString(wszDbComponentPath);
  961. VssFreeString(wszDsn);
  962. VssFreeString(wszComponentName);
  963. VssFreeString(wszSiteName);
  964. if (ft.HrFailed())
  965. {
  966. TranslateWriterError(ft.hr);
  967. return false;
  968. }
  969. return true;
  970. }
  971. // translate a sql writer error code into a writer error
  972. void CSTSWriter::TranslateWriterError(HRESULT hr)
  973. {
  974. switch(hr)
  975. {
  976. default:
  977. // all other errors are treated as non-retryable
  978. SetWriterFailure(VSS_E_WRITERERROR_NONRETRYABLE);
  979. break;
  980. case S_OK:
  981. break;
  982. case E_OUTOFMEMORY:
  983. case HRESULT_FROM_WIN32(ERROR_DISK_FULL):
  984. case HRESULT_FROM_WIN32(ERROR_TOO_MANY_OPEN_FILES):
  985. case HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY):
  986. case HRESULT_FROM_WIN32(ERROR_NO_MORE_USER_HANDLES):
  987. // out of resource errors
  988. SetWriterFailure(VSS_E_WRITERERROR_OUTOFRESOURCES);
  989. break;
  990. case VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT:
  991. case E_SQLLIB_TORN_DB:
  992. case VSS_E_OBJECT_NOT_FOUND:
  993. case VSS_E_OBJECT_ALREADY_EXISTS:
  994. // inconsistencies and other errors by the requestor
  995. SetWriterFailure(VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT);
  996. break;
  997. }
  998. }
  999. // handle pre restore event
  1000. bool STDMETHODCALLTYPE CSTSWriter::OnPreRestore
  1001. (
  1002. IN IVssWriterComponents *pWriter
  1003. )
  1004. {
  1005. CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSWriter::OnPreRestore");
  1006. BS_ASSERT(m_pSites);
  1007. // co task allocated strings that need to be freed if an
  1008. // exception is thrown
  1009. VSS_PWSZ wszMetadataForSite = NULL;
  1010. VSS_PWSZ wszContentRoot = NULL;
  1011. // component is at toplevel scope since it will be used to set
  1012. // failure message in failure case
  1013. CComPtr<IVssComponent> pComponent;
  1014. try
  1015. {
  1016. UINT cComponents;
  1017. ft.hr = pWriter->GetComponentCount(&cComponents);
  1018. ft.CheckForErrorInternal(VSSDBG_STSWRITER, L"IVssWriterComponents::GetComponentCount");
  1019. // if no components, then just return immediately
  1020. if (cComponents == 0)
  1021. return true;
  1022. // loop through components
  1023. for(UINT iComponent = 0; iComponent < cComponents; iComponent++)
  1024. {
  1025. ft.hr = pWriter->GetComponent(iComponent, &pComponent);
  1026. ft.CheckForErrorInternal(VSSDBG_STSWRITER, L"IVssWriterComponents::GetComponent");
  1027. bool bSelectedForRestore;
  1028. ft.hr = pComponent->IsSelectedForRestore(&bSelectedForRestore);
  1029. ft.CheckForErrorInternal(VSSDBG_STSWRITER, L"IVssComponent::IsSelectedForRestore");
  1030. if (!bSelectedForRestore)
  1031. {
  1032. // if component is not selected for restore, then
  1033. // skip it
  1034. pComponent = NULL;
  1035. continue;
  1036. }
  1037. // validate component type
  1038. VSS_COMPONENT_TYPE ct;
  1039. ft.hr = pComponent->GetComponentType(&ct);
  1040. ft.CheckForErrorInternal(VSSDBG_STSWRITER, L"IVssComponent::GetComponentType");
  1041. if (ct != VSS_CT_FILEGROUP)
  1042. ft.Throw(VSSDBG_STSWRITER, VSS_E_WRITERERROR_NONRETRYABLE, L"requesting a non-database component");
  1043. CComBSTR bstrLogicalPath;
  1044. CComBSTR bstrComponentName;
  1045. ft.hr = pComponent->GetLogicalPath(&bstrLogicalPath);
  1046. ft.CheckForErrorInternal(VSSDBG_STSWRITER, L"IVssComponent::GetLogicalPath");
  1047. // validate that no logical path is provided
  1048. if (bstrLogicalPath && wcslen(bstrLogicalPath) != 0)
  1049. ft.Throw(VSSDBG_STSWRITER, VSS_E_OBJECT_NOT_FOUND, L"STS components do not have logical paths");
  1050. // get component name
  1051. ft.hr = pComponent->GetComponentName(&bstrComponentName);
  1052. ft.CheckForErrorInternal(VSSDBG_STSWRITER, L"IVssComponent::GetComponentName");
  1053. DWORD iSite;
  1054. STSSITEPROBLEM problem;
  1055. // validate that the component name is valid
  1056. if (!ParseComponentName(bstrComponentName, iSite, problem))
  1057. SetSiteInvalid(pComponent, bstrComponentName, problem);
  1058. // build metadata for the site
  1059. wszMetadataForSite = BuildSiteMetadata(iSite);
  1060. CComBSTR bstrMetadataForComponent;
  1061. // get metadata for site saved when the site was backed up
  1062. ft.hr = pComponent->GetBackupMetadata(&bstrMetadataForComponent);
  1063. ft.CheckForErrorInternal(VSSDBG_STSWRITER, L"IVssComponent::GetBackupMetadata");
  1064. // validate that metadata is identical. If not, then figure
  1065. // out what changed
  1066. if (_wcsicmp(wszMetadataForSite, bstrMetadataForComponent) != 0)
  1067. SetSiteMetadataMismatch(pComponent, bstrMetadataForComponent, wszMetadataForSite);
  1068. // get content root for site
  1069. wszContentRoot = m_pSites->GetSiteRoot(iSite);
  1070. // try emptying contents from the content root
  1071. ft.hr = RemoveDirectoryTree(wszContentRoot);
  1072. if (ft.HrFailed())
  1073. SetRemoveFailure(pComponent, wszContentRoot, ft.hr);
  1074. // set component to null in preparation of moving to next
  1075. // component
  1076. pComponent = NULL;
  1077. }
  1078. }
  1079. VSS_STANDARD_CATCH(ft)
  1080. CoTaskMemFree(wszContentRoot);
  1081. CoTaskMemFree(wszMetadataForSite);
  1082. if (ft.HrFailed() && pComponent != NULL)
  1083. SetPreRestoreFailure(pComponent, ft.hr);
  1084. return !ft.HrFailed();
  1085. }
  1086. // indicate that a site cannot be restored because the site referred to is invalid
  1087. void CSTSWriter::SetSiteInvalid
  1088. (
  1089. IVssComponent *pComponent,
  1090. LPCWSTR wszSiteName,
  1091. STSSITEPROBLEM problem
  1092. )
  1093. {
  1094. CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSWriter::SetSiteInvalid");
  1095. WCHAR buf[512];
  1096. LPCWSTR wszSiteError;
  1097. switch(problem)
  1098. {
  1099. default:
  1100. case STSP_SYNTAXERROR:
  1101. wszSiteError = L"Syntax error in site name";
  1102. break;
  1103. case STSP_SITENOTFOUND:
  1104. wszSiteError = L"Site does not exist on this machine";
  1105. break;
  1106. case STSP_SITENAMEMISMATCH:
  1107. wszSiteError = L"Site name does not match the server comment for the IIS Web Server: ";
  1108. break;
  1109. case STSP_SITEDSNINVALID:
  1110. wszSiteError = L"Site has an invalid Database DSN: ";
  1111. break;
  1112. case STSP_SQLSERVERNOTLOCAL:
  1113. wszSiteError = L"Database for the site is not local: ";
  1114. break;
  1115. case STSP_CONTENTNOTLOCAL:
  1116. wszSiteError = L"IIS Web Server root is not local: ";
  1117. break;
  1118. case STSP_CONFIGNOTLOCAL:
  1119. wszSiteError = L"Sharepoint Site Configuration is not local: ";
  1120. break;
  1121. }
  1122. wcscpy(buf, L"Problem with site specified in component -- ");
  1123. wcscat(buf, wszSiteError);
  1124. if (wcslen(wszSiteName) < 256)
  1125. wcscat(buf, wszSiteName);
  1126. else
  1127. {
  1128. DWORD cwc = (DWORD) wcslen(buf);
  1129. memcpy(buf + cwc, wszSiteName, 256 * sizeof(WCHAR));
  1130. *(buf + cwc + 256) = L'\0';
  1131. wcscat(buf, L"...");
  1132. }
  1133. pComponent->SetPreRestoreFailureMsg(buf);
  1134. SetWriterFailure(VSS_E_WRITERERROR_NONRETRYABLE);
  1135. ft.Throw(VSSDBG_STSWRITER, VSS_E_WRITERERROR_NONRETRYABLE, L"site can't be restored");
  1136. }
  1137. // indicate that a site cannot be restored because its DSN, content,
  1138. // or config roots mismatch
  1139. void CSTSWriter::SetSiteMetadataMismatch
  1140. (
  1141. IVssComponent *pComponent,
  1142. LPWSTR wszMetadataBackup,
  1143. LPWSTR wszMetadataRestore
  1144. )
  1145. {
  1146. CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSWriter::SetSiteMetadataMismatch");
  1147. // seach for end of server name in metadata
  1148. LPWSTR pwcB = wcschr(wszMetadataBackup, L';');
  1149. LPWSTR pwcR = wcschr(wszMetadataRestore, L';');
  1150. try
  1151. {
  1152. if (pwcB == NULL)
  1153. ft.Throw(VSSDBG_STSWRITER, VSS_ERROR_CORRUPTXMLDOCUMENT_MISSING_ATTRIBUTE, L"backup metadata is corrupt");
  1154. BS_ASSERT(pwcR != NULL);
  1155. // compute size of server name
  1156. DWORD cwcB = (DWORD) (pwcB - wszMetadataBackup);
  1157. DWORD cwcR = (DWORD) (pwcR - wszMetadataRestore);
  1158. do
  1159. {
  1160. if (cwcB != cwcR ||
  1161. _wcsnicmp(wszMetadataBackup, wszMetadataRestore, cwcB) != 0)
  1162. {
  1163. // server/instance name differs
  1164. LPWSTR wsz = new WCHAR[cwcB + cwcR + 256];
  1165. if (wsz == NULL)
  1166. // memory allocation failure, just try saving a simple error message
  1167. pComponent->SetPreRestoreFailureMsg(L"Mismatch between backup and restore [Server/Instance].");
  1168. else
  1169. {
  1170. // indicate that server/instance name mismatches
  1171. wcscpy(wsz, L"Mismatch between backup and restore[Server/Instance]: Backup=");
  1172. DWORD cwc1 = (DWORD) wcslen(wsz);
  1173. // copy in server/instance from backup components document
  1174. memcpy(wsz + cwc1, wszMetadataBackup, cwcB * sizeof(WCHAR));
  1175. wsz[cwc1 + cwcB] = L'\0';
  1176. // copy in server/instance from current site
  1177. wcscat(wsz, L", Restore=");
  1178. cwc1 = (DWORD) wcslen(wsz);
  1179. memcpy(wsz + cwc1, wszMetadataRestore, cwcR * sizeof(WCHAR));
  1180. wsz[cwc1 + cwcR] = L'\0';
  1181. pComponent->SetPreRestoreFailureMsg(wsz);
  1182. delete wsz;
  1183. }
  1184. continue;
  1185. }
  1186. pwcB++;
  1187. pwcR++;
  1188. if (!compareNextMetadataString
  1189. (
  1190. pComponent,
  1191. pwcB,
  1192. pwcR,
  1193. L"Sharepoint database name"
  1194. ))
  1195. continue;
  1196. if (!compareNextMetadataString
  1197. (
  1198. pComponent,
  1199. pwcB,
  1200. pwcR,
  1201. L"IIS Web site root"
  1202. ))
  1203. continue;
  1204. compareNextMetadataString
  1205. (
  1206. pComponent,
  1207. pwcB,
  1208. pwcR,
  1209. L"Sharepoint site configuration"
  1210. );
  1211. }while (false);
  1212. }
  1213. VSS_STANDARD_CATCH(ft)
  1214. if (ft.hr == VSS_ERROR_CORRUPTXMLDOCUMENT_MISSING_ATTRIBUTE)
  1215. {
  1216. // indication that the backup metadata is corrupt
  1217. WCHAR *pwcT = new WCHAR[64 + wcslen(wszMetadataBackup)];
  1218. if (pwcT == NULL)
  1219. pComponent->SetPreRestoreFailureMsg(L"Backup metadata is corrupt.");
  1220. else
  1221. {
  1222. // if we are able to allocate room for metadata, then include it
  1223. // in string
  1224. wcscpy(pwcT, L"Backup metadata is corrupt. Metadata = ");
  1225. wcscat(pwcT, wszMetadataBackup);
  1226. pComponent->SetPreRestoreFailureMsg(pwcT);
  1227. delete pwcT;
  1228. }
  1229. }
  1230. // indicate that the error is not-retryable since the site has changed
  1231. SetWriterFailure(VSS_E_WRITERERROR_NONRETRYABLE);
  1232. ft.Throw(VSSDBG_STSWRITER, VSS_E_WRITERERROR_NONRETRYABLE, L"site can't be restored");
  1233. }
  1234. // compare a component of the metadata string. Each component begins
  1235. // with a 4 digit hex number which is the length of the component string
  1236. // that follows.
  1237. bool CSTSWriter::compareNextMetadataString
  1238. (
  1239. IVssComponent *pComponent,
  1240. LPWSTR &pwcB,
  1241. LPWSTR &pwcR,
  1242. LPCWSTR wszMetadataComponent
  1243. )
  1244. {
  1245. CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSWriter::compareNextMetadataString");
  1246. DWORD cwcB, cwcR;
  1247. if (swscanf(pwcB, L"%04x", &cwcB) != 1)
  1248. ft.Throw(VSSDBG_STSWRITER, VSS_ERROR_CORRUPTXMLDOCUMENT_MISSING_ATTRIBUTE, L"invalid backup metadata");
  1249. BS_VERIFY(swscanf(pwcR, L"%04x", &cwcR) == 1);
  1250. if (cwcR != cwcB ||
  1251. _wcsnicmp(pwcB + 4, pwcR + 4, cwcB) != 0)
  1252. {
  1253. LPWSTR wsz = new WCHAR[cwcB + cwcR + wcslen(wszMetadataComponent) + 256];
  1254. if (wsz == NULL)
  1255. {
  1256. WCHAR buf[256];
  1257. swprintf(buf, L"Mismatch between backup and restore[%s]", wszMetadataComponent);
  1258. pComponent->SetPreRestoreFailureMsg(buf);
  1259. }
  1260. else
  1261. {
  1262. swprintf(wsz, L"Mismatch between backup and restore[%s]: Backup=", wszMetadataComponent);
  1263. DWORD cwc1 = (DWORD) wcslen(wsz);
  1264. // copy in backup component value
  1265. memcpy(wsz + cwc1, pwcB + 4, cwcB * sizeof(WCHAR));
  1266. wsz[cwc1 + cwcB] = L'\0';
  1267. wcscat(wsz, L", Restore=");
  1268. cwc1 = (DWORD) wcslen(wsz);
  1269. // copy in restore component value
  1270. memcpy(wsz + cwc1, pwcR + 4, cwcR * sizeof(WCHAR));
  1271. wsz[cwc1 + cwcR] = L'\0';
  1272. pComponent->SetPreRestoreFailureMsg(wsz);
  1273. delete wsz;
  1274. }
  1275. return false;
  1276. }
  1277. // skip past component name
  1278. pwcB += 4 + cwcB;
  1279. pwcR += 4 + cwcR;
  1280. return true;
  1281. }
  1282. // indicate that a site could not be restored because its content root
  1283. // could not be completely deleted.
  1284. void CSTSWriter::SetRemoveFailure
  1285. (
  1286. IVssComponent *pComponent,
  1287. LPCWSTR wszContentRoot,
  1288. HRESULT hr
  1289. )
  1290. {
  1291. CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSWriter::SetRemoveFailure");
  1292. WCHAR buf[256];
  1293. wprintf(buf, L"PreRestore failed due to error removing files from the IIS Web Site Root %s due to error: hr = 0x%08lx", wszContentRoot, hr);
  1294. pComponent->SetPreRestoreFailureMsg(buf);
  1295. SetWriterFailure(VSS_E_WRITERERROR_NONRETRYABLE);
  1296. ft.Throw(VSSDBG_STSWRITER, VSS_E_WRITERERROR_NONRETRYABLE, L"site can't be restored");
  1297. }
  1298. // indicate a general failure that causes the PreRestore of a component
  1299. // to fail
  1300. void CSTSWriter::SetPreRestoreFailure(IVssComponent *pComponent, HRESULT hr)
  1301. {
  1302. CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSWriter::SetPreRestoreFailure");
  1303. // if error is set to NONRETRYABLE then we have already set the
  1304. // prerestore failure message and are done
  1305. if (ft.hr != VSS_E_WRITERERROR_NONRETRYABLE)
  1306. return;
  1307. CComBSTR bstr;
  1308. ft.hr = pComponent->GetPreRestoreFailureMsg(&bstr);
  1309. if (!bstr)
  1310. {
  1311. WCHAR buf[256];
  1312. swprintf(buf, L"PreRestore failed with error. hr = 0x%08lx", hr);
  1313. ft.hr = pComponent->SetPreRestoreFailureMsg(buf);
  1314. }
  1315. SetWriterFailure(VSS_E_WRITERERROR_NONRETRYABLE);
  1316. }
  1317. const DWORD x_cFormats = 8;
  1318. static const COMPUTER_NAME_FORMAT s_rgFormats[x_cFormats] =
  1319. {
  1320. ComputerNameNetBIOS,
  1321. ComputerNameDnsHostname,
  1322. ComputerNameDnsDomain,
  1323. ComputerNameDnsFullyQualified,
  1324. ComputerNamePhysicalNetBIOS,
  1325. ComputerNamePhysicalDnsHostname,
  1326. ComputerNamePhysicalDnsDomain,
  1327. ComputerNamePhysicalDnsFullyQualified
  1328. };
  1329. // determine if a SQL Server is on the local machine
  1330. bool CSTSWriter::ValidateServerIsLocal(LPCWSTR wszServer)
  1331. {
  1332. CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSWriter::ValidateServerIsLocal");
  1333. if (_wcsicmp(wszServer, L"local") == 0 ||
  1334. _wcsicmp(wszServer, L"(local)") == 0)
  1335. return true;
  1336. LPWSTR wsz = new WCHAR[MAX_COMPUTERNAME_LENGTH];
  1337. if (wsz == NULL)
  1338. ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"out of memory");
  1339. DWORD cwc = MAX_COMPUTERNAME_LENGTH;
  1340. for(DWORD iFormat = 0; iFormat < x_cFormats; iFormat++)
  1341. {
  1342. if (!GetComputerNameEx(s_rgFormats[iFormat], wsz, &cwc))
  1343. {
  1344. if (GetLastError() != ERROR_MORE_DATA)
  1345. continue;
  1346. delete wsz;
  1347. wsz = new WCHAR[cwc + 1];
  1348. if (wsz == NULL)
  1349. ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"out of memory");
  1350. if (!GetComputerNameEx(s_rgFormats[iFormat], wsz, &cwc))
  1351. continue;
  1352. }
  1353. if (_wcsicmp(wsz, wszServer) == 0)
  1354. {
  1355. delete wsz;
  1356. return true;
  1357. }
  1358. }
  1359. delete wsz;
  1360. return false;
  1361. }
  1362. // determine whether a path is on the local machine or not
  1363. bool CSTSWriter::ValidatePathIsLocal(LPCWSTR wszPath)
  1364. {
  1365. CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSWriter::ValidatePathIsLocal");
  1366. // get full path from supplied path
  1367. ULONG ulMountpointBufferLength = GetFullPathName (wszPath, 0, NULL, NULL);
  1368. LPWSTR pwszMountPointName = new WCHAR[ulMountpointBufferLength * sizeof (WCHAR)];
  1369. if (pwszMountPointName == NULL)
  1370. ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"out of memory");
  1371. BOOL fSuccess = FALSE;
  1372. if (GetVolumePathName(wszPath, pwszMountPointName, ulMountpointBufferLength))
  1373. {
  1374. WCHAR wszVolumeName[MAX_PATH];
  1375. fSuccess = GetVolumeNameForVolumeMountPoint(pwszMountPointName, wszVolumeName, sizeof (wszVolumeName) / sizeof (WCHAR));
  1376. }
  1377. delete pwszMountPointName;
  1378. return fSuccess ? true : false;
  1379. }