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.

1659 lines
41 KiB

  1. // ***************************************************************************
  2. // Copyright (C) 2000- Microsoft Corporation.
  3. // @File: snapsql.cpp
  4. //
  5. // PURPOSE:
  6. //
  7. // Implement the SQLServer Volume Snapshot Writer.
  8. //
  9. // NOTES:
  10. //
  11. //
  12. // HISTORY:
  13. //
  14. // @Version: Whistler/Yukon
  15. // 90690 SRS 10/10/01 Minor sqlwriter changes
  16. // 85581 SRS 08/15/01 Event security
  17. // 76910 SRS 08/08/01 Rollforward from VSS snapshot
  18. // 68228 12/05/00 ntsnap work
  19. // 66601 srs 10/05/00 NTSNAP improvements
  20. //
  21. //
  22. // @EndHeader@
  23. // ***************************************************************************
  24. #if HIDE_WARNINGS
  25. #pragma warning( disable : 4786)
  26. #endif
  27. #include <stdafx.h>
  28. #include <new.h>
  29. #include "vdierror.h"
  30. ////////////////////////////////////////////////////////////////////////
  31. // Standard foo for file name aliasing. This code block must be after
  32. // all includes of VSS header files.
  33. //
  34. #ifdef VSS_FILE_ALIAS
  35. #undef VSS_FILE_ALIAS
  36. #endif
  37. #define VSS_FILE_ALIAS "SQLSNAPC"
  38. //
  39. ////////////////////////////////////////////////////////////////////////
  40. //----------------------------------------------------------------------
  41. // Database status bits (from ntdbms/ntinc/database.h)
  42. //
  43. const long DBT_CLOSED = 0x2; // database is uninitialized because
  44. // nobody is in it (see DBT_CLOSE_ON_EXIT)
  45. const long DBT_NOTREC = 0x40; /* set for each db by recovery before recovering
  46. any of them */
  47. const long DBT_INRECOVER = 0x80; /* set by recovery as seach db is recovered */
  48. const long DBT_CKPT = 0x2000; /* database being checkpointed */
  49. const long DBT_SHUTDOWN = 0x40000; // database hasn't been bootstrapped yet
  50. const long DBT_INLDDB = 0x20; /* set by loaddb - tells recovery not to recover
  51. this database */
  52. const long DBT_SUSPECT = 0x100; /* database not recovered successfully */
  53. const long DBT_DETACHED = 0x80000; // This database has been detached
  54. const long DBT_STANDBY = 0x200000; // DB is online readonly with RESTORE LOG
  55. // allowed. This state is set by RECOVERDB.
  56. /* set by user - saved in Sysdatabases - moved to DBTABLE at checkpoint */
  57. const long DBT_CLOSE_ON_EXIT = 0x1; // shutdown if you were last user in
  58. // this DB
  59. // WARNING: note that DBT_CLOSED is 0x2
  60. const long DBT_SELBULK = 0x4;
  61. const long DBT_AUTOTRUNC = 0x8;
  62. const long DBT_TORNPAGE = 0x10; // enable torn page detection
  63. // 0x10 is available (used to be no checkpoint on recovery)
  64. // WARNING: note that DBT_INLDDB is 0x20
  65. // WARNING: note that DBT_NOTREC is 0x40
  66. // WARNING: note that DBT_INRECOVER is 0x80
  67. // WARNING: note that DBT_SUSPECT is 0x100
  68. const long DBT_OFFLINE = 0x200; /* database is currently offline */
  69. const long DBT_RDONLY = 0x400; /* database is read only */
  70. const long DBT_DBO = 0x800; /* only available to owner, dbcreator of db and sa */
  71. const long DBT_SINGLE = 0x1000; /* single user only */
  72. // WARNING: note that DBT_CKPT is 0x2000
  73. const long DBT_PENDING_UPGRADE = 0x4000; // RESERVED: We are using this bit in Sphinx
  74. // but not sure if we'll need it in Shiloh.
  75. // DO NOT take it without consultation.
  76. const long DBT_USE_NOTREC = 0x8000; /* emergency mode - set to allow db to be not
  77. recovered but usable */
  78. // WARNING: note that DBT_SHUTDOWN is 0x40000
  79. // WARNING: note that DBT_DETACHED is 0x80000
  80. // WARNING: note that DBT_STANDBY is 0x200000
  81. const long DBT_AUTOSHRINK = 0x400000; /* autoshrink is enable for the database */
  82. // WARNING: in utables 0x8000000 is being added in u_tables.cql to indicate 'table lock on bulk load'
  83. const long DBT_CLEANLY_SHUTDOWN = 0x40000000; //This database was shutdown in a
  84. // clean manner with no open
  85. // transactions and all writes
  86. // flushed to disk
  87. const long DBT_MINIMAL_LOG_IN_DB = 0x10000000; // The database contains pages marked
  88. // changed due to minimally logged ops.
  89. const long DBT_MINIMAL_LOG_AFTER_BACKUP = 0x20000000; // The database contains pages marked
  90. // changed due to
  91. //--------------------------------------------------------------------------------
  92. // Build a literal string from for an identifier.
  93. // We need to provide database names as strings in some T-SQL contexts.
  94. // This routine ensures that we handle them all the same way.
  95. // The output buffer should be SysNameBufferLen in size.
  96. //
  97. void
  98. FormStringForName (WCHAR* pString, const WCHAR* pName)
  99. {
  100. pString [0] = 'N'; // unicode prefix
  101. pString [1] = '\''; // string delimiter
  102. UINT ix = 2;
  103. while (*pName && ix < SysNameBufferLen-3)
  104. {
  105. if (*pName == '\'')
  106. {
  107. // need to double all quotes
  108. //
  109. pString [ix++] = '\'';
  110. }
  111. pString [ix++] = *pName;
  112. pName++;
  113. }
  114. pString [ix++] = '\'';
  115. pString [ix] = 0;
  116. }
  117. //--------------------------------------------------------------------------------
  118. // Build a delimited identifier from an identifier.
  119. // We need to handle special characters in database names via delimited identifiers.
  120. // This routine ensures that we handle them all the same way.
  121. // The output buffer should be SysNameBufferLen in size.
  122. //
  123. void
  124. FormDelimitedIdentifier (WCHAR* pString, const WCHAR* pName)
  125. {
  126. pString [0] = '['; // unicode prefix
  127. UINT ix = 1;
  128. while (*pName && ix < SysNameBufferLen-3)
  129. {
  130. if (*pName == ']')
  131. {
  132. // need to double embedded brackets
  133. //
  134. pString [ix++] = ']';
  135. }
  136. pString [ix++] = *pName;
  137. pName++;
  138. }
  139. pString [ix++] = ']';
  140. pString [ix] = 0;
  141. }
  142. //--------------------------------------------------------------------------------------------------
  143. // A handler to call when out of memory.
  144. //
  145. int __cdecl out_of_store(size_t size)
  146. {
  147. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"out_of_store");
  148. ft.Trace(VSSDBG_SQLLIB, L"out of memory");
  149. throw HRESULT (E_OUTOFMEMORY);
  150. return 0;
  151. }
  152. class AutoNewHandler
  153. {
  154. public:
  155. AutoNewHandler ()
  156. {
  157. m_oldHandler = _set_new_handler (out_of_store);
  158. }
  159. ~AutoNewHandler ()
  160. {
  161. _set_new_handler (m_oldHandler);
  162. }
  163. private:
  164. _PNH m_oldHandler;
  165. };
  166. //-------------------------------------------------------------------------
  167. // Handle enviroment stuff:
  168. // - tracing/error logging
  169. // - mem alloc
  170. //
  171. IMalloc * g_pIMalloc = NULL;
  172. HRESULT
  173. InitSQLEnvironment()
  174. {
  175. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"InitSqlEnvironment");
  176. try
  177. {
  178. ft.hr = CoGetMalloc(1, &g_pIMalloc);
  179. if (ft.HrFailed())
  180. ft.Trace(VSSDBG_SQLLIB, L"Failed to get task allocator: hr=0x%X", ft.hr);
  181. }
  182. catch (...)
  183. {
  184. ft.hr = E_SQLLIB_GENERIC;
  185. }
  186. return ft.hr;
  187. }
  188. //-------------------------------------------------------------------------
  189. // Return TRUE if the database properties are retrieved:
  190. // simple: TRUE if using the simple recovery model.
  191. // online: TRUE if the database is available for backup usable and currently open
  192. //
  193. // This is only intended to work for SQL70 databases.
  194. //
  195. void
  196. FrozenServer::GetDatabaseProperties70 (const WString& dbName,
  197. BOOL* pSimple,
  198. BOOL* pOnline)
  199. {
  200. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"FrozenServer::GetDatabaseProperties");
  201. // We use status bit 0x40000000 (1073741824) to identify
  202. // clean-shutdown databases which are "offline".
  203. //
  204. WCHAR stringName[SysNameBufferLen];
  205. FormStringForName (stringName, dbName.c_str ());
  206. WString query =
  207. L"select status "
  208. L"from master..sysdatabases where name = " + WString (stringName);
  209. m_Connection.SetCommand (query);
  210. m_Connection.ExecCommand ();
  211. if (!m_Connection.FetchFirst ())
  212. {
  213. LPCWSTR wsz = dbName.c_str();
  214. ft.LogError(VSS_ERROR_SQLLIB_DATABASE_NOT_IN_SYSDATABASES, VSSDBG_SQLLIB << wsz);
  215. THROW_GENERIC;
  216. }
  217. UINT32 status = (*(UINT32*)m_Connection.AccessColumn (1));
  218. if (status & (DBT_INLDDB | DBT_NOTREC | DBT_INRECOVER | DBT_SUSPECT |
  219. DBT_OFFLINE | DBT_USE_NOTREC | DBT_SHUTDOWN | DBT_DETACHED | DBT_STANDBY))
  220. {
  221. *pOnline = FALSE;
  222. }
  223. else
  224. {
  225. *pOnline = TRUE;
  226. }
  227. }
  228. //------------------------------------------------------------------------------
  229. // Build the list of databases to BACKUP for a SQL2000 server for
  230. // the case of a non-component-based backup.
  231. //
  232. // Only volumes are identified.
  233. // Any database with a file on those volumes is included.
  234. // "Torn" checking is performed.
  235. // "Autoclosed" databases are skipped (assume they will stay closed).
  236. // Only simple recovery is allowed.
  237. // Skip databases which aren't freezable.
  238. //
  239. // Called only by "FindDatabasesToFreeze" to implement a smart access strategy:
  240. // - use sysaltfiles to qualify the databases.
  241. // This avoids access to shutdown or damaged databases.
  242. //
  243. // Autoclose databases which are not started are left out of the freeze-list.
  244. // We do this to avoid scaling problems, especially on desktop systems.
  245. // However, such db's are still evaluated to see if they are "torn".
  246. //
  247. // The 'model' database is allowed to be a full recovery database, since only
  248. // database backups are sensible for it. It is set to full recovery only
  249. // to provide defaults for new databases.
  250. //
  251. // "Freezable" databases are those in a state suitable for BACKUP.
  252. // This excludes databases which are not in an full-ONLINE state
  253. // (due to damage, partially restored, warm standby, etc).
  254. //
  255. // UNDONE:
  256. // In Yukon, the autoclose determination was unstable. Re-check it.
  257. //
  258. BOOL
  259. FrozenServer::FindDatabases2000 (
  260. CCheckPath* checker)
  261. {
  262. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"FrozenServer::FindDatabases2000");
  263. // Query the databases on this server, looking at properties:
  264. // (dbname, filename, simpleRecovery, online, inStandby, AutoClose, Autoclosed)
  265. //
  266. // We use status bit 0x40000000 (1073741824) to identify
  267. // clean-shutdown databases which are not really online.
  268. //
  269. m_Connection.SetCommand (
  270. L"select db_name(af.dbid), "
  271. L"rtrim(af.filename), "
  272. L"case databasepropertyex(db_name(af.dbid),'recovery') "
  273. L"when 'SIMPLE' then 1 "
  274. L"else 0 end, "
  275. L"case databasepropertyex(db_name(af.dbid),'Status') "
  276. L"when 'ONLINE' then 1 "
  277. L"else 0 end, "
  278. L"convert (int, databasepropertyex(db_name(af.dbid),'IsInStandby')), "
  279. L"convert (int, databasepropertyex(db_name(af.dbid),'IsAutoClose')), "
  280. L"case db.status & 1073741824 "
  281. L"when 1073741824 then 1 "
  282. L"else 0 end "
  283. L"from master..sysaltfiles af, master..sysdatabases db "
  284. L"where af.dbid = db.dbid and af.dbid != db_id('tempdb') "
  285. L"order by af.dbid"
  286. );
  287. m_Connection.ExecCommand ();
  288. // Results of the query
  289. //
  290. WCHAR* pDbName;
  291. WCHAR* pFileName;
  292. int* pIsSimple;
  293. int* pIsOnline;
  294. int* pIsInStandby;
  295. int* pIsAutoClose;
  296. int* pIsClosed;
  297. // Track transitions between databases/database files
  298. //
  299. bool firstFile = true;
  300. bool done = false;
  301. // Info about the current database being examined
  302. //
  303. WString currDbName;
  304. bool currDbInSnapshot;
  305. bool currDbIsFreezable;
  306. bool currDbIsSimple;
  307. bool currDbIsClosed;
  308. if (!m_Connection.FetchFirst ())
  309. {
  310. ft.LogError(VSS_ERROR_SQLLIB_SYSALTFILESEMPTY, VSSDBG_SQLLIB);
  311. THROW_GENERIC;
  312. }
  313. pDbName = (WCHAR*) m_Connection.AccessColumn (1);
  314. pFileName = (WCHAR*) m_Connection.AccessColumn (2);
  315. pIsSimple = (int*) m_Connection.AccessColumn (3);
  316. pIsOnline = (int*) m_Connection.AccessColumn (4);
  317. pIsInStandby = (int*) m_Connection.AccessColumn (5);
  318. pIsAutoClose = (int*) m_Connection.AccessColumn (6);
  319. pIsClosed = (int*) m_Connection.AccessColumn (7);
  320. while (!done)
  321. {
  322. bool fileInSnap = checker->IsPathInSnapshot (pFileName);
  323. // Trace what's happening
  324. //
  325. if (firstFile)
  326. {
  327. ft.Trace(VSSDBG_SQLLIB,
  328. L"Examining database <%s>\nSimpleRecovery:%d Online:%d Standby:%d AutoClose:%d Closed:%d\n",
  329. pDbName, *pIsSimple, *pIsOnline, *pIsInStandby, *pIsAutoClose, *pIsClosed);
  330. }
  331. ft.Trace(VSSDBG_SQLLIB, L"InSnap(%d): %s\n", (int)fileInSnap, pFileName);
  332. if (firstFile)
  333. {
  334. firstFile = FALSE;
  335. // Remember some facts about this database
  336. //
  337. currDbName = WString (pDbName);
  338. currDbIsSimple = (*pIsSimple || wcscmp (L"model", pDbName) == 0);
  339. currDbIsFreezable = (*pIsOnline && !*pIsInStandby);
  340. currDbIsClosed = *pIsAutoClose && *pIsClosed;
  341. currDbInSnapshot = fileInSnap;
  342. // We can check recovery model and snapshot configuration now
  343. //
  344. if (currDbInSnapshot && !currDbIsSimple && currDbIsFreezable)
  345. {
  346. ft.LogError(VSS_ERROR_SQLLIB_DATABASENOTSIMPLE, VSSDBG_SQLLIB << pDbName);
  347. throw HRESULT (E_SQLLIB_NONSIMPLE);
  348. }
  349. }
  350. else
  351. {
  352. if (currDbInSnapshot ^ fileInSnap)
  353. {
  354. ft.LogError(VSS_ERROR_SQLLIB_DATABASEISTORN, VSSDBG_SQLLIB);
  355. throw HRESULT (E_SQLLIB_TORN_DB);
  356. }
  357. }
  358. if (!m_Connection.FetchNext ())
  359. {
  360. done = true;
  361. }
  362. else if (currDbName.compare (pDbName))
  363. {
  364. firstFile = TRUE;
  365. }
  366. if (done || firstFile)
  367. {
  368. // To be part of the BACKUP, the database must:
  369. // - be covered by the snapshot
  370. // - in a freezeable state
  371. //
  372. // IsSimpleOnly implicitly selects all open databases.
  373. // Non-open databases are also part of the volume snapshot,
  374. // but there is no need to freeze them, since they aren't
  375. // changing.
  376. //
  377. if (currDbInSnapshot && currDbIsFreezable && !currDbIsClosed)
  378. {
  379. m_FrozenDatabases.push_back (currDbName);
  380. }
  381. }
  382. }
  383. return m_FrozenDatabases.size () > 0;
  384. }
  385. //------------------------------------------------------------------------------
  386. // Determine if there are databases which qualify for a freeze on this server.
  387. // Returns TRUE if so.
  388. //
  389. // Processing varies based on the type of snapshot:
  390. // 1) ComponentBased
  391. // The requestor explicitly identifies databases of interest.
  392. // All recovery models are allowed.
  393. // "Torn" checking isn't performed (database filenames are irrelevant)
  394. //
  395. // 2) NonComponentBased
  396. // Throws if any qualified databases are any of:
  397. // - "torn" (not fully covered by the snapshot)
  398. // - hosted by a server which can't support freeze
  399. // - are not "simple" databases
  400. //
  401. BOOL
  402. FrozenServer::FindDatabasesToFreeze (
  403. CCheckPath* checker)
  404. {
  405. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"FrozenServer::FindDatabasesToFreeze");
  406. m_Connection.Connect (m_Name);
  407. m_FrozenDatabases.clear ();
  408. if (checker->IsComponentBased ())
  409. {
  410. PCWSTR dbName;
  411. UINT nextIndex = 0;
  412. while (dbName = checker->EnumerateSelectedDatabases (m_Name.c_str (), &nextIndex))
  413. {
  414. m_FrozenDatabases.push_back (WString (dbName));
  415. }
  416. return m_FrozenDatabases.size () > 0;
  417. }
  418. // Handle non-component-based snapshot
  419. //
  420. if (m_Connection.GetServerVersion () > 7)
  421. {
  422. // SQL2000 allows us to use a better access strategy.
  423. //
  424. return FindDatabases2000 (checker);
  425. }
  426. m_Connection.SetCommand (L"select name from sysdatabases where name != 'tempdb'");
  427. m_Connection.ExecCommand ();
  428. std::auto_ptr<StringVector> dbList (m_Connection.GetStringColumn ());
  429. BOOL masterLast = FALSE;
  430. for (StringVectorIter i = dbList->begin (); i != dbList->end (); i++)
  431. {
  432. // We'll avoid freezing shutdown db's, but we don't avoid
  433. // enumerating their files (they might be torn)
  434. //
  435. // Note the [] around the dbname to handle non-trivial dbnames.
  436. //
  437. WCHAR stringName[SysNameBufferLen];
  438. FormDelimitedIdentifier (stringName, (*i).c_str ());
  439. WString command = L"select rtrim(filename) from "
  440. + WString (stringName) + L"..sysfiles";
  441. m_Connection.SetCommand (command);
  442. try
  443. {
  444. m_Connection.ExecCommand ();
  445. }
  446. catch (...)
  447. {
  448. // We've decided to be optimistic:
  449. // If we can't get the list of files, ignore this database.
  450. //
  451. ft.Trace(VSSDBG_SQLLIB, L"Failed to get db files for %s\n", i->c_str ());
  452. continue;
  453. }
  454. std::auto_ptr<StringVector> fileList (m_Connection.GetStringColumn ());
  455. BOOL first=TRUE;
  456. BOOL shouldFreeze;
  457. for (StringVectorIter iFile = fileList->begin ();
  458. iFile != fileList->end (); iFile++)
  459. {
  460. BOOL fileInSnap = checker->IsPathInSnapshot (iFile->c_str ());
  461. if (first)
  462. {
  463. shouldFreeze = fileInSnap;
  464. }
  465. else
  466. {
  467. if (shouldFreeze ^ fileInSnap)
  468. {
  469. ft.LogError(VSS_ERROR_SQLLIB_DATABASEISTORN, VSSDBG_SQLLIB << i->c_str());
  470. throw HRESULT (E_SQLLIB_TORN_DB);
  471. }
  472. }
  473. }
  474. if (shouldFreeze)
  475. {
  476. BOOL simple, online;
  477. GetDatabaseProperties70 (i->c_str (), &simple, &online);
  478. if (!simple && L"model" != *i)
  479. {
  480. ft.LogError(VSS_ERROR_SQLLIB_DATABASENOTSIMPLE, VSSDBG_SQLLIB << i->c_str ());
  481. throw HRESULT (E_SQLLIB_NONSIMPLE);
  482. }
  483. if (online)
  484. {
  485. if (L"master" == *i)
  486. {
  487. masterLast = TRUE;
  488. }
  489. else
  490. {
  491. m_FrozenDatabases.push_back (*i);
  492. }
  493. }
  494. }
  495. }
  496. if (masterLast)
  497. {
  498. m_FrozenDatabases.push_back (L"master");
  499. }
  500. return m_FrozenDatabases.size () > 0;
  501. }
  502. //-------------------------------------------------------------------
  503. // Prep the server for the freeze.
  504. // For SQL2000, start a BACKUP WITH SNAPSHOT.
  505. // For SQL7, issuing checkpoints to each database.
  506. // This minimizes the recovery processing needed when the snapshot is restored.
  507. //
  508. BOOL
  509. FrozenServer::Prepare ()
  510. {
  511. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"FrozenServer::Prepare");
  512. if (m_Connection.GetServerVersion () > 7)
  513. {
  514. m_pFreeze2000 = new Freeze2000 (m_Name, m_FrozenDatabases.size ());
  515. // Release the connection, we won't need it anymore
  516. //
  517. m_Connection.Disconnect ();
  518. for (StringListIter i=m_FrozenDatabases.begin ();
  519. i != m_FrozenDatabases.end (); i++)
  520. {
  521. m_pFreeze2000->PrepareDatabase (*i);
  522. }
  523. m_pFreeze2000->WaitForPrepare ();
  524. }
  525. else
  526. {
  527. WString command;
  528. for (StringListIter i=m_FrozenDatabases.begin ();
  529. i != m_FrozenDatabases.end (); i++)
  530. {
  531. WCHAR stringName[SysNameBufferLen];
  532. FormDelimitedIdentifier (stringName, (*i).c_str ());
  533. command += L"use " + WString (stringName) + L"\ncheckpoint\n";
  534. }
  535. m_Connection.SetCommand (command);
  536. m_Connection.ExecCommand ();
  537. }
  538. return TRUE;
  539. }
  540. //---------------------------------------------
  541. // Freeze the server by issuing freeze commands
  542. // to each database.
  543. // Returns an exception if any failure occurs.
  544. //
  545. BOOL
  546. FrozenServer::Freeze ()
  547. {
  548. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"FrozenServer::Freeze");
  549. if (m_pFreeze2000)
  550. {
  551. m_pFreeze2000->Freeze ();
  552. }
  553. else
  554. {
  555. WString command;
  556. for (StringListIter i=m_FrozenDatabases.begin ();
  557. i != m_FrozenDatabases.end (); i++)
  558. {
  559. WCHAR stringName[SysNameBufferLen];
  560. FormStringForName (stringName, (*i).c_str ());
  561. command += L"dbcc freeze_io (" + WString (stringName) + L")\n";
  562. }
  563. m_Connection.SetCommand (command);
  564. m_Connection.ExecCommand ();
  565. }
  566. return TRUE;
  567. }
  568. //---------------------------------------------
  569. // Thaw the server by issuing thaw commands
  570. // to each database.
  571. // For SQL7, we can't tell if the database was
  572. // already thawn.
  573. // But for SQL2000, we'll return TRUE only if
  574. // the databases were still all frozen at the thaw time.
  575. BOOL
  576. FrozenServer::Thaw ()
  577. {
  578. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"FrozenServer::Thaw");
  579. if (m_pFreeze2000)
  580. {
  581. return m_pFreeze2000->Thaw ();
  582. }
  583. WString command;
  584. for (StringListIter i=m_FrozenDatabases.begin ();
  585. i != m_FrozenDatabases.end (); i++)
  586. {
  587. WCHAR stringName[SysNameBufferLen];
  588. FormStringForName (stringName, (*i).c_str ());
  589. command += L"dbcc thaw_io (" + WString (stringName) + L")\n";
  590. }
  591. m_Connection.SetCommand (command);
  592. m_Connection.ExecCommand ();
  593. return TRUE;
  594. }
  595. void
  596. FrozenServer::GetDatabaseInfo (UINT dbIndex, FrozenDatabaseInfo* pInfo)
  597. {
  598. FrozenDatabase* pDb = &m_pFreeze2000->m_pDBContext [dbIndex];
  599. pInfo->serverName = m_Name.c_str ();
  600. pInfo->databaseName = pDb->m_DbName.c_str ();
  601. //pInfo->isSimpleRecovery = pDb->m_IsSimpleModel;
  602. pInfo->pMetaData = pDb->m_MetaData.GetImage (&pInfo->metaDataSize);
  603. }
  604. //-------------------------------------------------------------------------
  605. // Create an object to handle the SQL end of the snapshot.
  606. //
  607. CSqlSnapshot*
  608. CreateSqlSnapshot () throw ()
  609. {
  610. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"CreateSqlSnapshot");
  611. try
  612. {
  613. return new Snapshot;
  614. }
  615. catch (...)
  616. {
  617. ft.Trace(VSSDBG_SQLLIB, L"Out of memory");
  618. }
  619. return NULL;
  620. }
  621. //---------------------------------------------------------------
  622. // Move to an uninitialized state.
  623. //
  624. void
  625. Snapshot::Deinitialize ()
  626. {
  627. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Snapshot::Deinitialize");
  628. if (m_Status == Frozen)
  629. {
  630. Thaw ();
  631. }
  632. for (ServerIter i=m_FrozenServers.begin ();
  633. i != m_FrozenServers.end (); i++)
  634. {
  635. delete *i;
  636. }
  637. m_FrozenServers.clear ();
  638. m_Status = NotInitialized;
  639. }
  640. Snapshot::~Snapshot ()
  641. {
  642. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Snapshot::~Snapshot");
  643. try
  644. {
  645. ft.Trace(VSSDBG_SQLLIB, L"\n~CSqlSnapshot called\n");
  646. Deinitialize ();
  647. }
  648. catch (...)
  649. {
  650. // swallow!
  651. }
  652. }
  653. //---------------------------------------------------------------------------------------
  654. // Prepare for the snapshot:
  655. // - identify the installed servers
  656. // - for each server that is "up":
  657. // - identify databases affected by the snapshot
  658. // - if there are such databases, fail the snapshot if:
  659. // - the server doesn't support snapshots
  660. // - the database isn't a SIMPLE database
  661. // - the database is "torn" (not all files in the snapshot)
  662. //
  663. //
  664. HRESULT
  665. Snapshot::Prepare (CCheckPath* checker) throw ()
  666. {
  667. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Snapshot::Prepare");
  668. HRESULT hr = S_OK;
  669. try
  670. {
  671. AutoNewHandler t;
  672. if (m_Status != NotInitialized)
  673. {
  674. Deinitialize ();
  675. }
  676. // The state moves immediately to enumerated, indicating
  677. // that the frozen server list may be non-empty.
  678. //
  679. m_Status = Enumerated;
  680. // Build a list of servers on this machine.
  681. //
  682. {
  683. std::auto_ptr<StringVector> servers (EnumerateServers ());
  684. // Scan over the servers, picking out the online ones.
  685. //
  686. for (UINT i=0; i < servers->size (); i++)
  687. {
  688. FrozenServer* p = new FrozenServer ((*servers)[i]);
  689. m_FrozenServers.push_back (p);
  690. }
  691. }
  692. // Evaulate the server databases to find those which need to freeze.
  693. //
  694. ServerIter i=m_FrozenServers.begin ();
  695. while (i != m_FrozenServers.end ())
  696. {
  697. if (!(**i).FindDatabasesToFreeze (checker))
  698. {
  699. ft.Trace(VSSDBG_SQLLIB, L"Server %s has no databases to freeze\n", ((**i).GetName ()).c_str ());
  700. // Forget about this server, it's got nothing to do.
  701. //
  702. delete *i;
  703. i = m_FrozenServers.erase (i);
  704. }
  705. else
  706. {
  707. i++;
  708. }
  709. }
  710. // Prep the servers for the freeze
  711. //
  712. for (i=m_FrozenServers.begin (); i != m_FrozenServers.end (); i++)
  713. {
  714. (*i)->Prepare ();
  715. }
  716. m_Status = Prepared;
  717. }
  718. catch (HRESULT& e)
  719. {
  720. hr = e;
  721. }
  722. catch (...)
  723. {
  724. hr = E_SQLLIB_GENERIC;
  725. }
  726. return hr;
  727. }
  728. //---------------------------------------------------------------------------------------
  729. // Freeze any prepared servers
  730. //
  731. HRESULT
  732. Snapshot::Freeze () throw ()
  733. {
  734. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Snapshot::Freeze");
  735. HRESULT hr = S_OK;
  736. if (m_Status != Prepared)
  737. {
  738. return E_SQLLIB_PROTO;
  739. }
  740. try
  741. {
  742. AutoNewHandler t;
  743. // If any server is frozen, we are frozen.
  744. //
  745. m_Status = Frozen;
  746. // Ask the servers to freeze
  747. //
  748. for (ServerIter i=m_FrozenServers.begin (); i != m_FrozenServers.end (); i++)
  749. {
  750. (*i)->Freeze ();
  751. }
  752. }
  753. catch (...)
  754. {
  755. hr = E_SQLLIB_GENERIC;
  756. }
  757. return hr;
  758. }
  759. //-----------------------------------------------
  760. // Thaw all the servers.
  761. // This routine must not throw. It's safe in a destructor
  762. //
  763. // DISCUSS WITH BRIAN....WE MUST RETURN "SUCCESS" only if the
  764. // servers were all still frozen. Otherwise the snapshot must
  765. // have been cancelled.
  766. //
  767. HRESULT
  768. Snapshot::Thaw () throw ()
  769. {
  770. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Snapshot::Thaw");
  771. HRESULT hr = S_OK;
  772. AutoNewHandler t;
  773. // Ask the servers to thaw
  774. //
  775. for (ServerIter i=m_FrozenServers.begin (); i != m_FrozenServers.end (); i++)
  776. {
  777. try
  778. {
  779. if (!(*i)->Thaw ())
  780. {
  781. hr = E_SQLLIB_GENERIC;
  782. }
  783. }
  784. catch (...)
  785. {
  786. hr = E_SQLLIB_GENERIC;
  787. ft.LogError(VSS_ERROR_SQLLIB_ERRORTHAWSERVER, VSSDBG_SQLLIB << ((**i).GetName ()).c_str ());
  788. }
  789. }
  790. // We still have the original list of servers.
  791. // The snapshot object is reusable if another "Prepare" is done, which will
  792. // re-enumerate the servers.
  793. //
  794. m_Status = Enumerated;
  795. return hr;
  796. }
  797. // Fetch info about the first interesting database
  798. //
  799. HRESULT
  800. Snapshot::GetFirstDatabase (
  801. FrozenDatabaseInfo* pInfo) throw ()
  802. {
  803. m_DbIndex = 0;
  804. m_ServerIter = m_FrozenServers.begin ();
  805. return GetNextDatabase (pInfo);
  806. }
  807. //---------------------------------------------------------------
  808. // We don't return any info about SQL7 databases since
  809. // the purpose here is to retrieve VDI metadata needed for
  810. // BACKUP/RESTORE WITH SNAPSHOT.
  811. //
  812. HRESULT
  813. Snapshot::GetNextDatabase (
  814. FrozenDatabaseInfo* pInfo) throw ()
  815. {
  816. while (m_ServerIter != m_FrozenServers.end ())
  817. {
  818. FrozenServer* pSrv = *m_ServerIter;
  819. if (pSrv->m_pFreeze2000 &&
  820. m_DbIndex < pSrv->m_pFreeze2000->m_NumDatabases)
  821. {
  822. pSrv->GetDatabaseInfo (m_DbIndex, pInfo);
  823. m_DbIndex++;
  824. return NOERROR;
  825. }
  826. m_DbIndex = 0;
  827. m_ServerIter++;
  828. }
  829. return DB_S_ENDOFROWSET;
  830. }
  831. //---------------------------------------------------------------------
  832. // Setup some try/catch/handlers for our interface...
  833. // The invoker defines "hr" which is set if an exception
  834. // occurs.
  835. //
  836. #define TRY_SQLLIB \
  837. try {\
  838. AutoNewHandler _myNewHandler;
  839. #define END_SQLLIB \
  840. } catch (HRESULT& e)\
  841. {\
  842. ft.hr = e;\
  843. }\
  844. catch (...)\
  845. {\
  846. ft.hr = E_SQLLIB_GENERIC;\
  847. }
  848. //-------------------------------------------------------------------------
  849. // Create an object to handle the SQL end of the snapshot.
  850. //
  851. CSqlRestore*
  852. CreateSqlRestore () throw ()
  853. {
  854. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"CreateSqlRestore");
  855. try
  856. {
  857. return new RestoreHandler;
  858. }
  859. catch (...)
  860. {
  861. ft.Trace(VSSDBG_SQLLIB, L"Out of memory");
  862. }
  863. return NULL;
  864. }
  865. RestoreHandler::RestoreHandler ()
  866. {
  867. // This GUID is used as the name for the VDSet
  868. // We can reuse it for multiple restores, since only one
  869. // will run at a time.
  870. //
  871. CoCreateGuid (&m_VDSId);
  872. }
  873. // Inform SQLServer that data laydown is desired on the full database.
  874. // Performs a DETACH, preventing SQLServer from touching the files.
  875. //
  876. HRESULT
  877. RestoreHandler::PrepareToRestore (
  878. const WCHAR* pInstance,
  879. const WCHAR* pDatabase)
  880. throw ()
  881. {
  882. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"RestoreHandler::PrepareToRestore");
  883. TRY_SQLLIB
  884. {
  885. m_Connection.Connect (pInstance);
  886. WCHAR stringName[SysNameBufferLen];
  887. FormStringForName (stringName, pDatabase);
  888. WString command =
  889. L"if exists (select name from sysdatabases where name=" +
  890. WString(stringName) +
  891. L") ALTER DATABASE ";
  892. FormDelimitedIdentifier (stringName, pDatabase);
  893. command += WString (stringName) + L" SET OFFLINE WITH ROLLBACK IMMEDIATE";
  894. m_Connection.SetCommand (command);
  895. m_Connection.ExecCommand ();
  896. }
  897. END_SQLLIB
  898. return ft.hr;
  899. }
  900. //-------------------------------------------
  901. // Map the voids and proc call stuff to the real
  902. // thread routine.
  903. //
  904. DWORD WINAPI RestoreVDProc(
  905. LPVOID lpParameter ) // thread data
  906. {
  907. ((RestoreHandler*)lpParameter)->RestoreVD ();
  908. return 0;
  909. }
  910. //----------------------------------------------------------------------
  911. // Feed the MD back to SQLServer
  912. // Our caller setup the VDS, but we've got to finish the open processing.
  913. //
  914. void
  915. RestoreHandler::RestoreVD ()
  916. {
  917. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"RestoreHandler::RestoreVD");
  918. VDC_Command * cmd;
  919. DWORD completionCode;
  920. DWORD bytesTransferred;
  921. HRESULT hr;
  922. const BYTE *pCurData = m_pMetaData;
  923. VDConfig config;
  924. hr = m_pIVDSet->GetConfiguration (INFINITE, &config);
  925. if (FAILED (hr))
  926. {
  927. ft.Trace (VSSDBG_SQLLIB, L"Unexpected GetConfiguration hr: x%X\n", hr);
  928. m_pIVDSet->SignalAbort ();
  929. return;
  930. }
  931. hr = m_pIVDSet->OpenDevice (m_SetName, &m_pIVD);
  932. if (FAILED (hr))
  933. {
  934. ft.Trace (VSSDBG_SQLLIB, L"Unexpected OpenDevice hr: x%X\n", hr);
  935. m_pIVDSet->SignalAbort ();
  936. return;
  937. }
  938. while (SUCCEEDED (hr=m_pIVD->GetCommand (INFINITE, &cmd)))
  939. {
  940. bytesTransferred = 0;
  941. switch (cmd->commandCode)
  942. {
  943. case VDC_Read:
  944. if (pCurData+cmd->size > m_pMetaData+m_MetaDataSize)
  945. {
  946. // attempting to read past end of data.
  947. //
  948. completionCode = ERROR_HANDLE_EOF;
  949. }
  950. else
  951. {
  952. memcpy (cmd->buffer, pCurData, cmd->size);
  953. pCurData+= cmd->size;
  954. bytesTransferred = cmd->size;
  955. }
  956. case VDC_ClearError:
  957. completionCode = ERROR_SUCCESS;
  958. break;
  959. case VDC_MountSnapshot:
  960. // There is nothing to do here, since the snapshot
  961. // is already mounted.
  962. //
  963. completionCode = ERROR_SUCCESS;
  964. break;
  965. default:
  966. // If command is unknown...
  967. completionCode = ERROR_NOT_SUPPORTED;
  968. }
  969. hr = m_pIVD->CompleteCommand (cmd, completionCode, bytesTransferred, 0);
  970. if (!SUCCEEDED (hr))
  971. {
  972. break;
  973. }
  974. }
  975. if (hr == VD_E_CLOSE)
  976. {
  977. ft.hr = NOERROR;
  978. }
  979. else
  980. {
  981. ft.Trace (VSSDBG_SQLLIB, L"Unexpected VD termination: x%X\n", hr);
  982. ft.hr = hr;
  983. }
  984. }
  985. // After data is laid down, this performs RESTORE WITH SNAPSHOT[,NORECOVERY]
  986. //
  987. HRESULT
  988. RestoreHandler::FinalizeRestore (
  989. const WCHAR* pInstance,
  990. const WCHAR* pDatabase,
  991. bool compositeRestore, // true if WITH NORECOVERY desired
  992. const BYTE* pMetadata, // metadata obtained from BACKUP
  993. unsigned int dataLen) // size of metadata (in bytes)
  994. throw ()
  995. {
  996. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"RestoreHandler::FinalizeRestore");
  997. if (!MetaData::IsValidImage (pMetadata, dataLen))
  998. {
  999. // Brian, do we want to log something here?
  1000. // This shouldn't happen, but I add a csum just to be sure....
  1001. //
  1002. ft.Trace (VSSDBG_SQLLIB, L"Bad metadata for database %s\\%s", pInstance, pDatabase);
  1003. return E_SQLLIB_GENERIC;
  1004. }
  1005. m_pIVDSet = NULL;
  1006. m_pIVD = NULL;
  1007. m_pMetaData = pMetadata;
  1008. m_MetaDataSize = dataLen - sizeof(UINT); // chop off the checksum
  1009. m_hThread = NULL;
  1010. TRY_SQLLIB
  1011. {
  1012. // Make sure we have a connection to the server
  1013. //
  1014. m_Connection.Connect (pInstance);
  1015. // Build a VDS for the RESTORE
  1016. //
  1017. #ifdef TESTDRV
  1018. ft.hr = CoCreateInstance (
  1019. CLSID_MSSQL_ClientVirtualDeviceSet,
  1020. NULL,
  1021. CLSCTX_INPROC_SERVER,
  1022. IID_IClientVirtualDeviceSet2,
  1023. (void**)&m_pIVDSet);
  1024. #else
  1025. ft.CoCreateInstanceWithLog (
  1026. VSSDBG_SQLLIB,
  1027. CLSID_MSSQL_ClientVirtualDeviceSet,
  1028. L"MSSQL_ClientVirtualDeviceSet",
  1029. CLSCTX_INPROC_SERVER,
  1030. IID_IClientVirtualDeviceSet2,
  1031. (IUnknown**)&(m_pIVDSet));
  1032. #endif
  1033. if (ft.HrFailed())
  1034. {
  1035. ft.LogError(VSS_ERROR_SQLLIB_CANTCREATEVDS, VSSDBG_SQLLIB << ft.hr);
  1036. ft.Throw
  1037. (
  1038. VSSDBG_SQLLIB,
  1039. ft.hr,
  1040. L"Failed to create VDS object. hr = 0x%08lx",
  1041. ft.hr
  1042. );
  1043. }
  1044. VDConfig config;
  1045. memset (&config, 0, sizeof(config));
  1046. config.deviceCount = 1;
  1047. StringFromGUID2 (m_VDSId, m_SetName, sizeof (m_SetName)/sizeof(WCHAR));
  1048. // A "\" indicates a named instance; we need the "raw" instance name
  1049. //
  1050. WCHAR* pShortInstance = wcschr (pInstance, L'\\');
  1051. if (pShortInstance)
  1052. {
  1053. pShortInstance++; // step over the separator
  1054. }
  1055. // Create the virtual device set
  1056. //
  1057. ft.hr = m_pIVDSet->CreateEx (pShortInstance, m_SetName, &config);
  1058. if (ft.HrFailed())
  1059. {
  1060. ft.LogError(VSS_ERROR_SQLLIB_CANTCREATEVDS, VSSDBG_SQLLIB << ft.hr);
  1061. ft.Throw
  1062. (
  1063. VSSDBG_SQLLIB,
  1064. ft.hr,
  1065. L"Failed to create VDS object. hr = 0x%08lx",
  1066. ft.hr
  1067. );
  1068. }
  1069. // Spawn a thread to feed the VD metadata....
  1070. //
  1071. m_hThread = CreateThread (NULL, 0,
  1072. RestoreVDProc, this, 0, NULL);
  1073. if (m_hThread == NULL)
  1074. {
  1075. ft.hr = HRESULT_FROM_WIN32(GetLastError());
  1076. ft.CheckForError(VSSDBG_SQLLIB, L"CreateThread");
  1077. }
  1078. // Send the RESTORE command, which will cause the VD metadata
  1079. // to be consumed.
  1080. //
  1081. WCHAR stringName[SysNameBufferLen];
  1082. FormDelimitedIdentifier (stringName, pDatabase);
  1083. WString command =
  1084. L"RESTORE DATABASE " + WString(stringName) + L" FROM VIRTUAL_DEVICE='" +
  1085. m_SetName + L"' WITH SNAPSHOT,BUFFERCOUNT=1,BLOCKSIZE=1024";
  1086. if (compositeRestore)
  1087. {
  1088. command += L",NORECOVERY";
  1089. }
  1090. m_Connection.SetCommand (command);
  1091. m_Connection.ExecCommand ();
  1092. // Unless an exception is thrown, we were sucessful.
  1093. //
  1094. ft.hr = NOERROR;
  1095. }
  1096. END_SQLLIB
  1097. if (m_pIVDSet)
  1098. {
  1099. // If we hit an error, we'll need to clean up
  1100. //
  1101. if (ft.hr != NOERROR)
  1102. {
  1103. m_pIVDSet->SignalAbort ();
  1104. }
  1105. if (m_hThread)
  1106. {
  1107. // We gotta wait for our thread, since it's using our resources.
  1108. //
  1109. DWORD status = WaitForSingleObjectEx (m_hThread, INFINITE, TRUE);
  1110. if (status != WAIT_OBJECT_0)
  1111. {
  1112. ft.Trace (VSSDBG_SQLLIB, L"Unexpected thread-wait status: x%x", status);
  1113. }
  1114. CloseHandle (m_hThread);
  1115. }
  1116. m_pIVDSet->Close ();
  1117. m_pIVDSet->Release ();
  1118. }
  1119. return ft.hr;
  1120. }
  1121. //-------------------------------------------------------------------------
  1122. // Create an object to handle enumerations
  1123. //
  1124. CSqlEnumerator*
  1125. CreateSqlEnumerator () throw ()
  1126. {
  1127. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"CreateSqlEnumerator");
  1128. try
  1129. {
  1130. return new SqlEnumerator;
  1131. }
  1132. catch (...)
  1133. {
  1134. ft.Trace(VSSDBG_SQLLIB, L"Out of memory");
  1135. }
  1136. return NULL;
  1137. }
  1138. //-------------------------------------------------------------------------
  1139. //
  1140. SqlEnumerator::~SqlEnumerator ()
  1141. {
  1142. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlEnumerator::~SqlEnumerator");
  1143. if (m_pServers)
  1144. delete m_pServers;
  1145. }
  1146. //-------------------------------------------------------------------------
  1147. // Begin retrieval of the servers.
  1148. //
  1149. HRESULT
  1150. SqlEnumerator::FirstServer (ServerInfo* pSrv) throw ()
  1151. {
  1152. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlEnumerator::FirstServer");
  1153. if (m_pServers)
  1154. {
  1155. delete m_pServers;
  1156. m_pServers = NULL;
  1157. }
  1158. m_CurrServer = 0;
  1159. TRY_SQLLIB
  1160. {
  1161. m_pServers = EnumerateServers ();
  1162. if (m_pServers->size () == 0)
  1163. {
  1164. ft.hr = DB_S_ENDOFROWSET;
  1165. }
  1166. else
  1167. {
  1168. wcscpy (pSrv->name, (*m_pServers)[0].c_str ());
  1169. pSrv->isOnline = true;
  1170. // Bummer, the enumeration is just a list of strings.....
  1171. //pSrv->supportsCompositeRestore = true;
  1172. m_CurrServer = 1;
  1173. ft.hr = NOERROR;
  1174. }
  1175. }
  1176. END_SQLLIB
  1177. return ft.hr;
  1178. }
  1179. //-------------------------------------------------------------------------
  1180. // Continue retrieval of the servers.
  1181. //
  1182. HRESULT
  1183. SqlEnumerator::NextServer (ServerInfo* pSrv) throw ()
  1184. {
  1185. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlEnumerator::NextServer");
  1186. if (!m_pServers)
  1187. {
  1188. ft.hr = E_SQLLIB_PROTO;
  1189. }
  1190. else
  1191. {
  1192. TRY_SQLLIB
  1193. {
  1194. if (m_CurrServer >= m_pServers->size ())
  1195. {
  1196. ft.hr = DB_S_ENDOFROWSET;
  1197. }
  1198. else
  1199. {
  1200. wcscpy (pSrv->name, (*m_pServers)[m_CurrServer].c_str ());
  1201. m_CurrServer++;
  1202. pSrv->isOnline = true;
  1203. //pSrv->supportsCompositeRestore = true;
  1204. ft.hr = NOERROR;
  1205. }
  1206. }
  1207. END_SQLLIB
  1208. }
  1209. return ft.hr;
  1210. }
  1211. //-------------------------------------------------------------------------
  1212. // Copy out the info from the result set
  1213. //
  1214. void
  1215. SqlEnumerator::SetupDatabaseInfo (DatabaseInfo* pDbInfo)
  1216. {
  1217. WCHAR *pDbName = (WCHAR*)m_Connection.AccessColumn (1);
  1218. UINT status = *(int*)m_Connection.AccessColumn (2);
  1219. wcscpy (pDbInfo->name, pDbName);
  1220. pDbInfo->isSimpleRecovery = (DBT_AUTOTRUNC & status) ? true : false;
  1221. pDbInfo->status = status;
  1222. pDbInfo->supportsFreeze = false;
  1223. if (wcscmp (pDbName, L"tempdb") != 0)
  1224. {
  1225. // Databases not fully online are not eligible for backup.
  1226. //
  1227. if (!(status & (DBT_INLDDB | DBT_NOTREC | DBT_INRECOVER | DBT_SUSPECT |
  1228. DBT_OFFLINE | DBT_USE_NOTREC | DBT_SHUTDOWN | DBT_DETACHED | DBT_STANDBY)))
  1229. {
  1230. pDbInfo->supportsFreeze = true;
  1231. }
  1232. }
  1233. }
  1234. //-------------------------------------------------------------------------
  1235. // Begin retrieval of the databases
  1236. //
  1237. HRESULT
  1238. SqlEnumerator::FirstDatabase (const WCHAR *pServerName, DatabaseInfo* pDbInfo) throw ()
  1239. {
  1240. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlEnumerator::FirstDatabase");
  1241. TRY_SQLLIB
  1242. {
  1243. m_Connection.Connect (pServerName);
  1244. m_Connection.SetCommand (
  1245. L"select name,convert (int,status) from master.dbo.sysdatabases");
  1246. m_Connection.ExecCommand ();
  1247. if (!m_Connection.FetchFirst ())
  1248. {
  1249. ft.LogError(VSS_ERROR_SQLLIB_NORESULTFORSYSDB, VSSDBG_SQLLIB);
  1250. THROW_GENERIC;
  1251. }
  1252. SetupDatabaseInfo (pDbInfo);
  1253. m_State = DatabaseQueryActive;
  1254. ft.hr = NOERROR;
  1255. }
  1256. END_SQLLIB
  1257. return ft.hr;
  1258. }
  1259. //-------------------------------------------------------------------------
  1260. // Continue retrieval of the databases
  1261. //
  1262. HRESULT
  1263. SqlEnumerator::NextDatabase (DatabaseInfo* pDbInfo) throw ()
  1264. {
  1265. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlEnumerator::NextDatabase");
  1266. if (m_State != DatabaseQueryActive)
  1267. {
  1268. ft.hr = E_SQLLIB_PROTO;
  1269. }
  1270. else
  1271. {
  1272. TRY_SQLLIB
  1273. {
  1274. if (!m_Connection.FetchNext ())
  1275. {
  1276. ft.hr = DB_S_ENDOFROWSET;
  1277. }
  1278. else
  1279. {
  1280. SetupDatabaseInfo (pDbInfo);
  1281. ft.hr = NOERROR;
  1282. }
  1283. }
  1284. END_SQLLIB
  1285. }
  1286. return ft.hr;
  1287. }
  1288. //-------------------------------------------------------------------------
  1289. // Begin retrieval of the database files
  1290. //
  1291. HRESULT
  1292. SqlEnumerator::FirstFile (
  1293. const WCHAR* pServerName,
  1294. const WCHAR* pDbName,
  1295. DatabaseFileInfo* pFileInfo) throw ()
  1296. {
  1297. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlEnumerator::FirstFile");
  1298. TRY_SQLLIB
  1299. {
  1300. m_Connection.Connect (pServerName);
  1301. WString query;
  1302. if (m_Connection.GetServerVersion () >= 8)
  1303. {
  1304. WCHAR stringName[SysNameBufferLen];
  1305. FormStringForName (stringName, pDbName);
  1306. query = L"select rtrim(filename),status & 64 from sysaltfiles where DB_ID("
  1307. + WString(stringName) + L") = dbid";
  1308. }
  1309. else
  1310. {
  1311. WCHAR stringName[SysNameBufferLen];
  1312. FormDelimitedIdentifier (stringName, pDbName);
  1313. query = L"select rtrim(filename),status & 64 from "
  1314. + WString(stringName) + L"..sysfiles";
  1315. }
  1316. m_Connection.SetCommand (query);
  1317. m_Connection.ExecCommand ();
  1318. if (!m_Connection.FetchFirst ())
  1319. {
  1320. ft.LogError(VSS_ERROR_SQLLIB_NORESULTFORSYSDB, VSSDBG_SQLLIB);
  1321. THROW_GENERIC;
  1322. }
  1323. WCHAR* pName = (WCHAR*)m_Connection.AccessColumn (1);
  1324. int* pLogFile = (int*)m_Connection.AccessColumn (2);
  1325. wcscpy (pFileInfo->name, pName);
  1326. pFileInfo->isLogFile = (*pLogFile != 0);
  1327. m_State = FileQueryActive;
  1328. ft.hr = NOERROR;
  1329. }
  1330. END_SQLLIB
  1331. return ft.hr;
  1332. }
  1333. //-------------------------------------------------------------------------
  1334. // Continue retrieval of the files
  1335. //
  1336. HRESULT
  1337. SqlEnumerator::NextFile (DatabaseFileInfo* pFileInfo) throw ()
  1338. {
  1339. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlEnumerator::NextFile");
  1340. if (m_State != FileQueryActive)
  1341. {
  1342. ft.hr = E_SQLLIB_PROTO;
  1343. }
  1344. else
  1345. {
  1346. TRY_SQLLIB
  1347. {
  1348. if (!m_Connection.FetchNext ())
  1349. {
  1350. ft.hr = DB_S_ENDOFROWSET;
  1351. }
  1352. else
  1353. {
  1354. WCHAR* pName = (WCHAR*)m_Connection.AccessColumn (1);
  1355. int* pLogFile = (int*)m_Connection.AccessColumn (2);
  1356. wcscpy (pFileInfo->name, pName);
  1357. pFileInfo->isLogFile = (*pLogFile != 0);
  1358. ft.hr = NOERROR;
  1359. }
  1360. }
  1361. END_SQLLIB
  1362. }
  1363. return ft.hr;
  1364. }
  1365. //-------------------------------------------------------------------------
  1366. // Provide a simple container for BACKUP metadata.
  1367. //
  1368. MetaData::MetaData ()
  1369. {
  1370. m_UsedLength = 0;
  1371. m_AllocatedLength = 0x2000; // 8K will represent any small database
  1372. m_pData = new BYTE [m_AllocatedLength];
  1373. }
  1374. MetaData::~MetaData ()
  1375. {
  1376. if (m_pData)
  1377. {
  1378. delete[] m_pData;
  1379. }
  1380. }
  1381. void
  1382. MetaData::Append (const BYTE* pData, UINT length)
  1383. {
  1384. // We don't need to handle misalignment for the csum.
  1385. //
  1386. DBG_ASSERT (length % sizeof(UINT) == 0);
  1387. if (m_UsedLength + length > m_AllocatedLength)
  1388. {
  1389. BYTE* pNew = new BYTE [m_AllocatedLength*2];
  1390. memcpy (pNew, m_pData, m_UsedLength);
  1391. delete[] m_pData;
  1392. m_pData = pNew;
  1393. m_AllocatedLength *= 2;
  1394. }
  1395. memcpy (m_pData+m_UsedLength, pData, length);
  1396. m_UsedLength += length;
  1397. }
  1398. void
  1399. MetaData::Finalize ()
  1400. {
  1401. UINT csum = Checksum (m_pData, m_UsedLength);
  1402. Append ((BYTE*)&csum, sizeof(csum));
  1403. }
  1404. const BYTE*
  1405. MetaData::GetImage (UINT *pLength)
  1406. {
  1407. *pLength = m_UsedLength;
  1408. return m_pData;
  1409. }
  1410. BOOL
  1411. MetaData::IsValidImage (const BYTE* pData, UINT length)
  1412. {
  1413. return (0 == Checksum (pData, length));
  1414. }
  1415. UINT
  1416. MetaData::Checksum (const BYTE* pData, UINT length)
  1417. {
  1418. UINT csum = 0;
  1419. UINT nwords = length/sizeof(csum);
  1420. UINT* pWord = (UINT*)pData;
  1421. while (nwords>0)
  1422. {
  1423. csum ^= *pWord;
  1424. pWord++;
  1425. nwords--;
  1426. }
  1427. return csum;
  1428. }