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.

1691 lines
55 KiB

  1. /*++
  2. 1998 Seagate Software, Inc. All rights reserved.
  3. Module Name:
  4. Wsbdbsys.cpp
  5. Abstract:
  6. CWsbDbSys class.
  7. Author:
  8. Ron White [ronw] 1-Jul-1997
  9. Revision History:
  10. --*/
  11. #include "stdafx.h"
  12. #include "rsevents.h"
  13. #include "wsbdbsys.h"
  14. #include "wsbdbses.h"
  15. #include <mbstring.h>
  16. #include <limits.h>
  17. #define MAX_ATTACHED_DB 6 // Set by ESE/JET engine (suppose to be 7)
  18. #if !defined(BACKUP_TEST_TIMES)
  19. // Normal values
  20. #define DEFAULT_AUTOBACKUP_INTERVAL (3 * 60 * 60 * 1000) // 3 hours
  21. #define DEFAULT_AUTOBACKUP_IDLE_MINUTES 5
  22. #define DEFAULT_AUTOBACKUP_COUNT_MIN 100
  23. #define DEFAULT_AUTOBACKUP_LOG_COUNT 10
  24. #else
  25. // Test values
  26. #define DEFAULT_AUTOBACKUP_INTERVAL (4 * 60 * 1000) // 4 minutes
  27. #define DEFAULT_AUTOBACKUP_IDLE_MINUTES 1
  28. #define DEFAULT_AUTOBACKUP_COUNT_MIN 5
  29. #define DEFAULT_AUTOBACKUP_LOG_COUNT 4
  30. #endif
  31. #define DEFAULT_AUTOBACKUP_COUNT_MAX 500
  32. // Local stuff
  33. // ATTACHED_DB_DATA holds information about currently attached DBs
  34. typedef struct {
  35. CWsbStringPtr Name; // Database name
  36. LONG LastOpen; // Sequence number of last open
  37. } ATTACHED_DB_DATA;
  38. // This static data manages a list of attached databases for this process.
  39. // (Future: If we want this list to be managed on a per instance basis, all of
  40. // this data should become class members and handled appropriately)
  41. static ATTACHED_DB_DATA Attached[MAX_ATTACHED_DB];
  42. static LONG AttachedCount = 0;
  43. static CRITICAL_SECTION AttachedCritSect;
  44. static BOOL CritSectCreated = FALSE;
  45. static BOOL AttachedInit = FALSE;
  46. static SHORT AttachedCritSectUsers = 0;
  47. static CComCreator< CComObject<CWsbDbSession> > SessionFactory;
  48. // Local functions
  49. static HRESULT AddExtension(OLECHAR** pPath, OLECHAR* Ext);
  50. static HRESULT ClearDirectory(const OLECHAR* DirPath);
  51. static HRESULT CopyDirectory(const OLECHAR* DirSource, const OLECHAR* DirTarget);
  52. static HRESULT DirectoryHasFullBackup(const OLECHAR* DirPath);
  53. static HRESULT FileCount(const OLECHAR* DirPath, const OLECHAR* Pattern,
  54. ULONG* Count);
  55. static HRESULT RenameDirectory(const OLECHAR* OldDir, const OLECHAR* NewDir);
  56. // Non-member function initially called for autobackup thread
  57. static DWORD WsbDbSysStartAutoBackup(
  58. void* pVoid
  59. )
  60. {
  61. return(((CWsbDbSys*) pVoid)->AutoBackup());
  62. }
  63. HRESULT
  64. CWsbDbSys::AutoBackup(
  65. void
  66. )
  67. /*++
  68. Routine Description:
  69. Implements an auto-backup loop.
  70. Arguments:
  71. None.
  72. Return Value:
  73. Doesn't matter.
  74. --*/
  75. {
  76. HRESULT hr = S_OK;
  77. try {
  78. ULONG SleepPeriod = DEFAULT_AUTOBACKUP_INTERVAL;
  79. BOOL exitLoop = FALSE;
  80. while (! exitLoop) {
  81. // Wait for termination event, if timeout occurs, check the sleep period criteria
  82. switch (WaitForSingleObject(m_terminateEvent, SleepPeriod)) {
  83. case WAIT_OBJECT_0:
  84. // Need to terminate
  85. WsbTrace(OLESTR("CWsbDbSys::AutoBackup: signaled to terminate\n"));
  86. exitLoop = TRUE;
  87. break;
  88. case WAIT_TIMEOUT:
  89. // Check if backup need to be performed
  90. WsbTrace(OLESTR("CWsbDbSys::AutoBackup awakened, ChangeCount = %ld\n"), m_ChangeCount);
  91. // Don't do a backup if there hasn't been much activity
  92. if (DEFAULT_AUTOBACKUP_COUNT_MIN < m_ChangeCount) {
  93. LONG DiffMinutes;
  94. FILETIME ftNow;
  95. LONGLONG NowMinutes;
  96. LONGLONG ThenMinutes;
  97. // Wait for an idle time
  98. GetSystemTimeAsFileTime(&ftNow);
  99. NowMinutes = WsbFTtoLL(ftNow) / WSB_FT_TICKS_PER_MINUTE;
  100. ThenMinutes = WsbFTtoLL(m_LastChange) / WSB_FT_TICKS_PER_MINUTE;
  101. DiffMinutes = static_cast<LONG>(NowMinutes - ThenMinutes);
  102. WsbTrace(OLESTR("CWsbDbSys::AutoBackup idle minutes = %ld\n"),
  103. DiffMinutes);
  104. if (DEFAULT_AUTOBACKUP_IDLE_MINUTES < DiffMinutes ||
  105. DEFAULT_AUTOBACKUP_COUNT_MAX < m_ChangeCount) {
  106. hr = Backup(NULL, 0);
  107. if (S_OK != hr) {
  108. // Just trace and go back to wait for the next round...
  109. WsbTrace(OLESTR("CWsbDbSys::AutoBackup: Backup failed, hr=<%ls>\n"), WsbHrAsString(hr));
  110. }
  111. SleepPeriod = DEFAULT_AUTOBACKUP_INTERVAL;;
  112. } else {
  113. // Reduce the sleep time so we catch the next idle time
  114. ULONG SleepMinutes = SleepPeriod / (1000 * 60);
  115. if (SleepMinutes > (DEFAULT_AUTOBACKUP_IDLE_MINUTES * 2)) {
  116. SleepPeriod /= 2;
  117. }
  118. }
  119. }
  120. break; // end of timeout case
  121. case WAIT_FAILED:
  122. default:
  123. WsbTrace(OLESTR("CWsbDbSys::AutoBackup: WaitForSingleObject returned error %lu\n"), GetLastError());
  124. exitLoop = TRUE;
  125. break;
  126. } // end of switch
  127. } // end of while
  128. } WsbCatch(hr);
  129. return(hr);
  130. }
  131. HRESULT
  132. CWsbDbSys::Backup(
  133. IN OLECHAR* path,
  134. IN ULONG flags
  135. )
  136. /*++
  137. Implements:
  138. IWsbDbSys::Backup
  139. --*/
  140. {
  141. HRESULT hr = S_OK;
  142. char* backup_path = NULL;
  143. WsbTraceIn(OLESTR("CWsbDbSys::Backup"), OLESTR("path = <%ls>, flags = %lx"),
  144. path, flags);
  145. try {
  146. CWsbStringPtr BackupDir;
  147. JET_ERR jstat = JET_errSuccess;
  148. WsbAffirm(m_jet_initialized, WSB_E_NOT_INITIALIZED);
  149. // Set and save the backup path; make sure it exists
  150. if (NULL != path) {
  151. m_BackupPath = path;
  152. }
  153. CreateDirectory(m_BackupPath, NULL);
  154. // Start the automatic backup thread if requested
  155. if (flags & IDB_BACKUP_FLAG_AUTO) {
  156. // Don't start AutoBackup thread if it's already running
  157. if (0 == m_AutoThread) {
  158. DWORD threadId;
  159. // Create termination event for auto-backup thread
  160. WsbAffirmHandle(m_terminateEvent = CreateEvent(NULL, TRUE, FALSE, NULL));
  161. WsbAffirm((m_AutoThread = CreateThread(0, 0, WsbDbSysStartAutoBackup,
  162. (void*) this, 0, &threadId)) != 0, HRESULT_FROM_WIN32(GetLastError()));
  163. }
  164. // Do a full backup to a temporary directory
  165. } else if (flags & IDB_BACKUP_FLAG_FORCE_FULL) {
  166. BOOL UsedTempDir = FALSE;
  167. // Don't wipe out an existing backup -- if the normal backup
  168. // directory contains a full backup, do the full backup to
  169. // the .ful directory
  170. BackupDir = m_BackupPath;
  171. WsbAffirm(0 != (WCHAR *)BackupDir, E_OUTOFMEMORY);
  172. if (S_OK == DirectoryHasFullBackup(BackupDir)) {
  173. WsbAffirmHr(AddExtension(&BackupDir, L".ful"));
  174. UsedTempDir = TRUE;
  175. }
  176. // Make sure the directory exists (should check for errors?)
  177. CreateDirectory(BackupDir, NULL);
  178. // Make sure the directory is empty (the call to JetBackup will
  179. // fail if it's not)
  180. WsbAffirmHr(ClearDirectory(BackupDir));
  181. // Convert to narrow char string for parameter
  182. WsbAffirmHr(wsb_db_jet_fix_path(BackupDir, NULL, &backup_path));
  183. WsbTrace(OLESTR("CWsbDbSys::Backup: backup_path = <%hs>\n"), backup_path);
  184. // Do backup
  185. WsbAffirm(NULL != m_BackupEvent, WSB_E_IDB_WRONG_BACKUP_SETTINGS);
  186. DWORD status = WaitForSingleObject(m_BackupEvent, EVENT_WAIT_TIMEOUT);
  187. DWORD errWait;
  188. switch(status) {
  189. case WAIT_OBJECT_0:
  190. // Expected case - do Backup
  191. jstat = JetBackupInstance(m_jet_instance, backup_path, 0, NULL);
  192. if (! SetEvent(m_BackupEvent)) {
  193. // Don't abort, just trace error
  194. WsbTraceAlways(OLESTR("CWsbDbSys::Backup: SetEvent returned unexpected error %lu\n"), GetLastError());
  195. }
  196. WsbAffirmHr(jet_error(jstat));
  197. break;
  198. case WAIT_TIMEOUT:
  199. // Timeout - don't do backup
  200. WsbTraceAlways(OLESTR("CWsbDbSys::Backup, Wait for Single Object timed out after %lu ms\n"), EVENT_WAIT_TIMEOUT);
  201. WsbThrow(E_ABORT);
  202. break;
  203. case WAIT_FAILED:
  204. errWait = GetLastError();
  205. WsbTraceAlways(OLESTR("CWsbDbSys::Backup, Wait for Single Object returned error %lu\n"), errWait);
  206. WsbThrow(HRESULT_FROM_WIN32(errWait));
  207. break;
  208. default:
  209. WsbTraceAlways(OLESTR("CWsbDbSys::Backup, Wait for Single Object returned unexpected status %lu\n"), status);
  210. WsbThrow(E_UNEXPECTED);
  211. break;
  212. }
  213. // Full backup worked -- copy to real backup directory
  214. if (UsedTempDir) {
  215. try {
  216. WsbAffirmHr(ClearDirectory(m_BackupPath));
  217. WsbAffirmHr(CopyDirectory(BackupDir, m_BackupPath));
  218. WsbAffirmHr(ClearDirectory(BackupDir));
  219. // Try to delete temporary directory (may fail)
  220. DeleteFile(BackupDir);
  221. BackupDir = m_BackupPath;
  222. } WsbCatch(hr);
  223. }
  224. WsbLogEvent(WSB_MESSAGE_IDB_BACKUP_FULL, 0, NULL,
  225. WsbAbbreviatePath(BackupDir, 120), NULL);
  226. m_ChangeCount = 0;
  227. WsbAffirmHr(hr);
  228. // Try an incremental backup
  229. } else {
  230. ULONG LogCount;
  231. BOOL TryFullBackup = FALSE;
  232. WsbAffirmHr(FileCount(m_BackupPath, L"*.log", &LogCount));
  233. if (LogCount > DEFAULT_AUTOBACKUP_LOG_COUNT ||
  234. S_FALSE == DirectoryHasFullBackup(m_BackupPath)) {
  235. // Do a full backup instead of the incremental if there
  236. // are already too many log files, or there's no full
  237. // backup in the backup directory (which means the incremental
  238. // wouldn't work anyway)
  239. TryFullBackup = TRUE;
  240. } else {
  241. WsbTrace(OLESTR("CWsbDbSys::Backup, trying incremental backup\n"));
  242. // Convert to narrow char string for parameter
  243. WsbAffirmHr(wsb_db_jet_fix_path(m_BackupPath, NULL, &backup_path));
  244. WsbTrace(OLESTR("CWsbDbSys::Backup: backup_path = <%hs>\n"), backup_path);
  245. WsbAffirm(NULL != m_BackupEvent, WSB_E_IDB_WRONG_BACKUP_SETTINGS);
  246. DWORD status = WaitForSingleObject(m_BackupEvent, EVENT_WAIT_TIMEOUT);
  247. DWORD errWait;
  248. switch(status) {
  249. case WAIT_OBJECT_0:
  250. // Expected case - do Backup
  251. jstat = JetBackupInstance(m_jet_instance, backup_path, JET_bitBackupIncremental, NULL);
  252. if (! SetEvent(m_BackupEvent)) {
  253. // Don't abort, just trace error
  254. WsbTraceAlways(OLESTR("CWsbDbSys::Backup: SetEvent returned unexpected error %lu\n"), GetLastError());
  255. }
  256. break;
  257. case WAIT_TIMEOUT:
  258. // Timeout - don't do backup
  259. WsbTraceAlways(OLESTR("CWsbDbSys::Backup, Wait for Single Object timed out after %lu ms\n"), EVENT_WAIT_TIMEOUT);
  260. WsbThrow(E_ABORT);
  261. break;
  262. case WAIT_FAILED:
  263. errWait = GetLastError();
  264. WsbTraceAlways(OLESTR("CWsbDbSys::Backup, Wait for Single Object returned error %lu\n"), errWait);
  265. WsbThrow(HRESULT_FROM_WIN32(errWait));
  266. break;
  267. default:
  268. WsbTraceAlways(OLESTR("CWsbDbSys::Backup, Wait for Single Object returned unexpected status %lu\n"), status);
  269. WsbThrow(E_UNEXPECTED);
  270. break;
  271. }
  272. // Check for an error.
  273. if (JET_errSuccess != jstat) {
  274. if (JET_errMissingFullBackup == jstat) {
  275. // Full backup need to be performed
  276. WsbLogEvent(WSB_MESSAGE_IDB_MISSING_FULL_BACKUP, 0, NULL,
  277. WsbAbbreviatePath(m_BackupPath, 120), NULL);
  278. } else {
  279. // Unknown error of incremental backup. Try a full backup anyway
  280. WsbLogEvent(WSB_MESSAGE_IDB_INCREMENTAL_BACKUP_FAILED, 0, NULL,
  281. WsbAbbreviatePath(m_BackupPath, 120),
  282. WsbLongAsString(jstat), NULL );
  283. }
  284. TryFullBackup = TRUE;
  285. } else {
  286. // The incremental backup worked
  287. WsbLogEvent(WSB_MESSAGE_IDB_BACKUP_INCREMENTAL, 0, NULL,
  288. WsbAbbreviatePath(m_BackupPath, 120), NULL);
  289. m_ChangeCount = 0;
  290. }
  291. }
  292. // Try full backup?
  293. if (TryFullBackup) {
  294. WsbAffirmHr(Backup(NULL, IDB_BACKUP_FLAG_FORCE_FULL));
  295. }
  296. }
  297. } WsbCatchAndDo(hr,
  298. WsbLogEvent(WSB_MESSAGE_IDB_BACKUP_FAILED, 0, NULL,
  299. WsbAbbreviatePath(m_BackupPath, 120), NULL);
  300. );
  301. if (NULL != backup_path) {
  302. WsbFree(backup_path);
  303. }
  304. WsbTraceOut(OLESTR("CWsbDbSys::Backup"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
  305. return(hr);
  306. }
  307. HRESULT
  308. CWsbDbSys::FinalConstruct(
  309. void
  310. )
  311. /*++
  312. Implements:
  313. CComObjectRoot::FinalConstruct
  314. --*/
  315. {
  316. HRESULT hr = S_OK;
  317. WsbTraceIn(OLESTR("CWsbDbSys::FinalConstruct"), OLESTR("") );
  318. try {
  319. m_AutoThread = 0;
  320. m_terminateEvent = NULL;
  321. m_ChangeCount = 0;
  322. m_jet_initialized = FALSE;
  323. m_jet_instance = JET_instanceNil;
  324. m_BackupEvent = NULL;
  325. try {
  326. // Initialize critical sections (global resource, so init only for first user)
  327. if (AttachedCritSectUsers == 0) {
  328. WsbAffirmStatus(InitializeCriticalSectionAndSpinCount (&AttachedCritSect, 1000));
  329. CritSectCreated = TRUE;
  330. }
  331. AttachedCritSectUsers++;
  332. } catch(DWORD status) {
  333. AttachedCritSectUsers--;
  334. WsbLogEvent(status, 0, NULL, NULL);
  335. switch (status) {
  336. case STATUS_NO_MEMORY:
  337. WsbThrow(E_OUTOFMEMORY);
  338. break;
  339. default:
  340. WsbThrow(E_UNEXPECTED);
  341. break;
  342. }
  343. }
  344. } WsbCatch(hr);
  345. WsbTraceOut(OLESTR("CWsbDbSys::FinalConstruct"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
  346. return(hr);
  347. }
  348. void
  349. CWsbDbSys::FinalRelease(
  350. void
  351. )
  352. /*++
  353. Implements:
  354. CComObjectRoot::FinalRelease
  355. --*/
  356. {
  357. HRESULT hr = S_OK;
  358. WsbTraceIn(OLESTR("CWsbDbSys::FinalRelease"), OLESTR(""));
  359. try {
  360. // Make sure that Terminate was called
  361. if (m_jet_initialized == TRUE) {
  362. WsbAffirmHr(Terminate());
  363. }
  364. } WsbCatch(hr);
  365. // Global resource, so delete only for last user
  366. AttachedCritSectUsers--;
  367. if ((AttachedCritSectUsers == 0) && CritSectCreated) {
  368. DeleteCriticalSection(&AttachedCritSect);
  369. }
  370. WsbTraceOut(OLESTR("CWsbDbSys::FinalRelease"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
  371. }
  372. HRESULT
  373. CWsbDbSys::Init(
  374. IN OLECHAR* path,
  375. IN ULONG flags
  376. )
  377. /*++
  378. Implements:
  379. IWsbDbSys::Init
  380. --*/
  381. {
  382. HRESULT hr = S_OK;
  383. char* log_path = NULL;
  384. static BOOL bFirstTime = TRUE;
  385. static int nInstance = 0;
  386. WsbTraceIn(OLESTR("CWsbDbSys::Init"), OLESTR("path = <%ls>"), path);
  387. try {
  388. CWsbStringPtr dir;
  389. JET_ERR jstat = JET_errSuccess;
  390. // Initialize the Jet engine just once per Jet instance
  391. WsbAffirm(!m_jet_initialized, E_FAIL);
  392. // Initialize backup event, unless Jet backup is not required for this isntance
  393. if (! (flags & IDB_SYS_INIT_FLAG_NO_BACKUP)) {
  394. // Event should already exist - it is created in the CRssJetWriter constructor
  395. WsbAffirmHandle(m_BackupEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, HSM_IDB_STATE_EVENT));
  396. }
  397. // WsbDbSys represents one Jet instance.
  398. // However, some Jet initialization should be done only once per process,
  399. // before the first instance is being created.
  400. if (bFirstTime) {
  401. bFirstTime = FALSE;
  402. // Increase the default number of maximum Jet sesions for the process
  403. // TEMPORARY: Can this be set separately per instance?
  404. jstat = JetSetSystemParameter(0, 0, JET_paramCacheSizeMin , (IDB_MAX_NOF_SESSIONS*4), NULL);
  405. WsbTrace(OLESTR("CWsbDbSys::Init, JetSetSystemParameter(CacheSizeMax) = %ld\n"), jstat);
  406. WsbAffirmHr(jet_error(jstat));
  407. jstat = JetSetSystemParameter(0, 0, JET_paramMaxSessions, IDB_MAX_NOF_SESSIONS, NULL);
  408. WsbTrace(OLESTR("CWsbDbSys::Init, JetSetSystemParameter(MaxSessions) = %ld\n"), jstat);
  409. WsbAffirmHr(jet_error(jstat));
  410. // Tell Jet we are going to use multiple instances
  411. jstat = JetEnableMultiInstance(NULL, 0, NULL);
  412. WsbAffirmHr(jet_error(jstat));
  413. }
  414. // Here start the per-instance initialization.
  415. // First step is creating the instance
  416. // Use a numeric counter as instance name - we care only that the name is unique
  417. WsbAssert(JET_instanceNil == m_jet_instance, E_FAIL);
  418. nInstance++;
  419. char szInstance[10];
  420. sprintf(szInstance, "%d", nInstance);
  421. WsbTrace(OLESTR("CWsbDbSys::Init, Jet instance name = <%hs>\n"), szInstance);
  422. jstat = JetCreateInstance(&m_jet_instance, szInstance);
  423. WsbAffirmHr(jet_error(jstat));
  424. // Set some per-instance parameters:
  425. // Create path for log directory (same path is also used for system files and temp files)
  426. WsbAffirm(NULL != path, E_INVALIDARG);
  427. m_InitPath = path;
  428. m_BackupPath = m_InitPath;
  429. WsbAffirmHr(AddExtension(&m_BackupPath, L".bak"));
  430. WsbTrace(OLESTR("CWsbDbSys::Init, BackupPath = <%ls>\n"), (WCHAR *)m_BackupPath);
  431. WsbAffirmHr(wsb_db_jet_fix_path(path, OLESTR(""), &log_path));
  432. dir = log_path; // Convert to WCHAR
  433. // Make sure the directory exists.
  434. WsbTrace(OLESTR("CWsbDbSys::Init, Creating dir = <%ls>\n"), (WCHAR *)dir);
  435. if (! CreateDirectory(dir, NULL)) {
  436. DWORD status = GetLastError();
  437. if ((status == ERROR_ALREADY_EXISTS) || (status == ERROR_FILE_EXISTS)) {
  438. status = NO_ERROR;
  439. }
  440. WsbAffirmNoError(status);
  441. }
  442. ULONG checkpointDepth;
  443. ULONG logFileSize = 128; // In kilobytes
  444. if (! (flags & IDB_SYS_INIT_FLAG_NO_LOGGING)) {
  445. WsbTrace(OLESTR("CWsbDbSys::Init, LogFilePath = <%hs>\n"), log_path);
  446. jstat = JetSetSystemParameter(&m_jet_instance, 0, JET_paramLogFilePath, 0, log_path);
  447. WsbTrace(OLESTR("CWsbDbSys::Init, JetSetSystemParameter(LogFilePath) = %ld\n"), jstat);
  448. WsbAffirmHr(jet_error(jstat));
  449. // Use circular logging for "limited" logging
  450. if (flags & IDB_SYS_INIT_FLAG_LIMITED_LOGGING) {
  451. logFileSize = 512; // Increase the log file size
  452. jstat = JetSetSystemParameter(&m_jet_instance, 0, JET_paramCircularLog, 1, NULL);
  453. WsbAffirmHr(jet_error(jstat));
  454. WsbTrace(OLESTR("CWsbDbSys::Init: set circular logging\n"));
  455. // Set the amount of logging allowed before a check point
  456. // to allow about 4 log files
  457. // (the check point depth is set in bytes.)
  458. checkpointDepth = 4 * logFileSize * 1024;
  459. jstat = JetSetSystemParameter(&m_jet_instance, 0, JET_paramCheckpointDepthMax,
  460. checkpointDepth, NULL);
  461. WsbAffirmHr(jet_error(jstat));
  462. WsbTrace(OLESTR("CWsbDbSys::Init: set CheckpointDepthMax = %ld\n"), checkpointDepth);
  463. }
  464. } else {
  465. jstat = JetSetSystemParameter(&m_jet_instance, 0, JET_paramRecovery,
  466. 0, "off");
  467. WsbAffirmHr(jet_error(jstat));
  468. WsbTrace(OLESTR("CWsbDbSys::Init: set JET_paramRecovery to 0 (no logging)\n"));
  469. }
  470. // Set parameters for where to put auxiliary data
  471. WsbTrace(OLESTR("CWsbDbSys::Init, SystemPath = <%hs>\n"), log_path);
  472. jstat = JetSetSystemParameter(&m_jet_instance, 0, JET_paramSystemPath, 0, log_path);
  473. WsbAffirmHr(jet_error(jstat));
  474. // The next one, for some unknown reason, needs a file name at the end of the path
  475. WsbAffirmHr(dir.Append("\\temp.edb"));
  476. WsbAffirmHr(dir.CopyTo(&log_path));
  477. WsbTrace(OLESTR("CWsbDbSys::Init, TempPath = <%hs>\n"), log_path);
  478. jstat = JetSetSystemParameter(&m_jet_instance, 0, JET_paramTempPath, 0, log_path);
  479. WsbAffirmHr(jet_error(jstat));
  480. if (! (flags & IDB_SYS_INIT_FLAG_NO_LOGGING)) {
  481. // Set the log file size (in KB). The minimum seems to be 128KB.
  482. jstat = JetSetSystemParameter(&m_jet_instance, 0, JET_paramLogFileSize,
  483. logFileSize, NULL);
  484. WsbAffirmHr(jet_error(jstat));
  485. WsbTrace(OLESTR("CWsbDbSys::Init: set logFileSize to %ld Kb\n"), logFileSize);
  486. }
  487. // Set parameter for deleting out-of-range log files.
  488. // These files may exist after a restore from a db backup without clearing the db directory first
  489. if (! (flags & IDB_SYS_INIT_FLAG_NO_BACKUP)) {
  490. jstat = JetSetSystemParameter(&m_jet_instance, 0, JET_paramDeleteOutOfRangeLogs, 1, NULL);
  491. WsbAffirmHr(jet_error(jstat));
  492. WsbTrace(OLESTR("CWsbDbSys::Init: set delete out-of-range logs\n"));
  493. }
  494. // Initialize the DB instance
  495. jstat = JetInit(&m_jet_instance);
  496. hr = jet_error(jstat);
  497. // If this failed, report the error
  498. if (!SUCCEEDED(hr)) {
  499. if (flags & IDB_SYS_INIT_FLAG_SPECIAL_ERROR_MSG) {
  500. // Special message for FSA
  501. WsbLogEvent(WSB_E_IDB_DELETABLE_DATABASE_CORRUPT, 0, NULL, NULL);
  502. WsbThrow(WSB_E_RESOURCE_UNAVAILABLE);
  503. } else {
  504. WsbThrow(hr);
  505. }
  506. }
  507. WsbTrace(OLESTR("CWsbDbSys::Init: jet instance = %p\n"), (LONG_PTR)m_jet_instance);
  508. m_jet_initialized = TRUE;
  509. // Create a session for internal use of this instance
  510. WsbAffirmHr(NewSession(&m_pWsbDbSession));
  511. WsbTrace(OLESTR("CWsbDbSys::Init, m_pWsbDbSession = %p\n"), (IWsbDbSession*)m_pWsbDbSession);
  512. } WsbCatchAndDo(hr,
  513. WsbLogEvent(WSB_MESSAGE_IDB_INIT_FAILED, 0, NULL,
  514. WsbAbbreviatePath(m_InitPath, 120), NULL);
  515. );
  516. if (NULL != log_path) {
  517. WsbFree(log_path);
  518. }
  519. WsbTraceOut(OLESTR("CWsbDbSys::Init"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
  520. return(hr);
  521. }
  522. HRESULT
  523. CWsbDbSys::Terminate(
  524. void
  525. )
  526. /*++
  527. Implements:
  528. IWsbDbSys::Terminate
  529. --*/
  530. {
  531. HRESULT hr = S_OK;
  532. WsbTraceIn(OLESTR("CWsbDbSys::Terminate"), OLESTR(""));
  533. try {
  534. // If wasn't initialized or alreday cleaned up - just get out
  535. if (m_jet_initialized == FALSE) {
  536. WsbTrace(OLESTR("CWsbDbSys::Terminate - this insatnce is not initialized"));
  537. WsbThrow(S_OK);
  538. }
  539. // Terminate the auto-backup thread
  540. if (m_AutoThread) {
  541. // Signal thread to terminate
  542. SetEvent(m_terminateEvent);
  543. // Wait for the thread, if it doesn't terminate gracefully - kill it
  544. switch (WaitForSingleObject(m_AutoThread, 20000)) {
  545. case WAIT_FAILED: {
  546. WsbTrace(OLESTR("CWsbDbSys::Terminate: WaitForSingleObject returned error %lu\n"), GetLastError());
  547. }
  548. // fall through...
  549. case WAIT_TIMEOUT: {
  550. WsbTrace(OLESTR("CWsbDbSys::Terminate: force terminating of auto-backup thread.\n"));
  551. DWORD dwExitCode;
  552. if (GetExitCodeThread( m_AutoThread, &dwExitCode)) {
  553. if (dwExitCode == STILL_ACTIVE) { // thread still active
  554. if (!TerminateThread (m_AutoThread, 0)) {
  555. WsbTrace(OLESTR("CWsbDbSys::Terminate: TerminateThread returned error %lu\n"), GetLastError());
  556. }
  557. }
  558. } else {
  559. WsbTrace(OLESTR("CWsbDbSys::Terminate: GetExitCodeThread returned error %lu\n"), GetLastError());
  560. }
  561. break;
  562. }
  563. default:
  564. // Thread terminated gracefully
  565. break;
  566. }
  567. // Best effort done for terminating auto-backup thread
  568. CloseHandle(m_AutoThread);
  569. m_AutoThread = 0;
  570. }
  571. if (m_terminateEvent != NULL) {
  572. CloseHandle(m_terminateEvent);
  573. m_terminateEvent = NULL;
  574. }
  575. // Detach DBs before exiting so they don't automatically get
  576. // reattached the next time we start up
  577. if (m_pWsbDbSession) {
  578. JET_SESID sid;
  579. CComQIPtr<IWsbDbSessionPriv, &IID_IWsbDbSessionPriv> pSessionPriv = m_pWsbDbSession;
  580. WsbAffirmPointer(pSessionPriv);
  581. WsbAffirmHr(pSessionPriv->GetJetId(&sid));
  582. // Clean up the Attached data
  583. if (AttachedInit) {
  584. EnterCriticalSection(&AttachedCritSect);
  585. for (int i = 0; i < MAX_ATTACHED_DB; i++) {
  586. Attached[i].Name.Free();
  587. Attached[i].LastOpen = 0;
  588. }
  589. JetDetachDatabase(sid, NULL);
  590. AttachedInit = FALSE;
  591. LeaveCriticalSection(&AttachedCritSect);
  592. }
  593. // Release the global session for this instance
  594. m_pWsbDbSession = 0;
  595. }
  596. // Terminate Jet
  597. JetTerm(m_jet_instance);
  598. m_jet_initialized = FALSE;
  599. m_jet_instance = JET_instanceNil;
  600. } WsbCatch(hr);
  601. if (m_BackupEvent) {
  602. CloseHandle(m_BackupEvent);
  603. m_BackupEvent = NULL;
  604. }
  605. WsbTraceOut(OLESTR("CWsbDbSys::Terminate"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
  606. return (hr);
  607. }
  608. HRESULT
  609. CWsbDbSys::NewSession(
  610. OUT IWsbDbSession** ppSession
  611. )
  612. /*++
  613. Implements:
  614. IWsbDbSys::NewSession
  615. --*/
  616. {
  617. HRESULT hr = S_OK;
  618. WsbTraceIn(OLESTR("CWsbDbSys::NewSession"), OLESTR(""));
  619. try {
  620. WsbAffirm(0 != ppSession, E_POINTER);
  621. WsbAffirmHr(SessionFactory.CreateInstance(NULL, IID_IWsbDbSession,
  622. (void**)ppSession));
  623. CComQIPtr<IWsbDbSessionPriv, &IID_IWsbDbSessionPriv> pSessionPriv = *ppSession;
  624. WsbAffirmPointer(pSessionPriv);
  625. WsbAffirmHr(pSessionPriv->Init(&m_jet_instance));
  626. } WsbCatch(hr);
  627. WsbTraceOut(OLESTR("CWsbDbSys::NewSession"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
  628. return(hr);
  629. }
  630. HRESULT CWsbDbSys::GetGlobalSession(
  631. OUT IWsbDbSession** ppSession
  632. )
  633. /*++
  634. Implements:
  635. IWsbDbSys::GetGlobalSession
  636. --*/
  637. {
  638. HRESULT hr = S_OK;
  639. WsbTraceIn(OLESTR("CWsbDbSys::GetGlobalSession"), OLESTR(""));
  640. //
  641. // If the Task Manager has been created, return the pointer. Otherwise,
  642. // fail.
  643. try {
  644. WsbAssert(0 != ppSession, E_POINTER);
  645. *ppSession = m_pWsbDbSession;
  646. WsbAffirm(m_pWsbDbSession != 0, E_FAIL);
  647. m_pWsbDbSession.p->AddRef();
  648. } WsbCatch(hr);
  649. WsbTraceOut(OLESTR("CWsbDbSys::GetGlobalSession"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
  650. return (hr);
  651. }
  652. HRESULT
  653. CWsbDbSys::Restore(
  654. IN OLECHAR* fromPath,
  655. IN OLECHAR* toPath
  656. )
  657. /*++
  658. Implements:
  659. IWsbDbSys::Restore
  660. --*/
  661. {
  662. HRESULT hr = S_OK;
  663. char* backup_path = NULL;
  664. char* restore_path = NULL;
  665. WsbTraceIn(OLESTR("CWsbDbSys::Restore"), OLESTR("fromPath = <%ls>, toPath = <%ls>"),
  666. fromPath, toPath);
  667. try {
  668. CWsbStringPtr dir;
  669. JET_ERR jstat;
  670. // This is only allowed before Init
  671. WsbAffirm(!m_jet_initialized, E_UNEXPECTED);
  672. WsbAffirm(NULL != fromPath, E_POINTER);
  673. WsbAffirm(NULL != toPath, E_POINTER);
  674. // Convert pathes
  675. WsbAffirmHr(wsb_db_jet_fix_path(fromPath, OLESTR(""), &backup_path));
  676. WsbAffirmHr(wsb_db_jet_fix_path(toPath, OLESTR(""), &restore_path));
  677. // Make sure the target directory exists. Should check for error.
  678. dir = restore_path;
  679. CreateDirectory(dir, NULL);
  680. jstat = JetRestoreInstance(m_jet_instance, backup_path, restore_path, NULL);
  681. WsbAffirmHr(jet_error(jstat));
  682. } WsbCatch(hr);
  683. if (NULL != backup_path) {
  684. WsbFree(backup_path);
  685. }
  686. if (NULL != restore_path) {
  687. WsbFree(restore_path);
  688. }
  689. WsbTraceOut(OLESTR("CWsbDbSys::Restore"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
  690. return(hr);
  691. }
  692. HRESULT
  693. CWsbDbSys::IncrementChangeCount(
  694. void
  695. )
  696. /*++
  697. Implements:
  698. IWsbDbSysPriv::IncrementChangeCount
  699. Routine Description:
  700. Increments the write count used by AutoBackup.
  701. Arguments:
  702. None.
  703. Return Value:
  704. S_OK
  705. --*/
  706. {
  707. HRESULT hr = S_OK;
  708. WsbTraceIn(OLESTR("CWsbDbSys::IncrementChangeCount"),
  709. OLESTR("count = %ld"), m_ChangeCount);
  710. try {
  711. m_ChangeCount++;
  712. GetSystemTimeAsFileTime(&m_LastChange);
  713. } WsbCatch(hr);
  714. WsbTraceOut(OLESTR("CWsbDbSys::IncrementChangeCount"),
  715. OLESTR("count = %ld"), m_ChangeCount);
  716. return(hr);
  717. }
  718. HRESULT
  719. CWsbDbSys::DbAttachedAdd(
  720. OLECHAR* name,
  721. BOOL attach)
  722. /*++
  723. Implements:
  724. IWsbDbSysPriv::DbAttachedAdd
  725. Routine Description:
  726. Make sure DB is attached and update the last-used count.
  727. --*/
  728. {
  729. HRESULT hr = S_OK;
  730. char* jet_name = NULL;
  731. WsbTraceIn(OLESTR("CWsbDbSys::DbAttachedAdd"), OLESTR("name = %ls, attach = %ls"),
  732. name, WsbBoolAsString(attach));
  733. try {
  734. int i;
  735. int i_empty = -1;
  736. int i_found = -1;
  737. LONG min_count = AttachedCount + 1;
  738. CWsbStringPtr match_name;
  739. WsbAssert(name, E_POINTER);
  740. // Make sure the list is initialized
  741. if (!AttachedInit) {
  742. WsbAffirmHr(DbAttachedInit());
  743. }
  744. // Convert the name
  745. WsbAffirmHr(wsb_db_jet_fix_path(name, L"." IDB_DB_FILE_SUFFIX, &jet_name));
  746. match_name = jet_name;
  747. // See if it's already in the list; look for an empty slot; find the
  748. // least-recently used DB
  749. EnterCriticalSection(&AttachedCritSect);
  750. for (i = 0; i < MAX_ATTACHED_DB; i++) {
  751. // Empty slot?
  752. if (!Attached[i].Name) {
  753. if (-1 == i_empty) {
  754. // Save the first one found
  755. i_empty = i;
  756. }
  757. } else {
  758. // Gather some data for later
  759. if (Attached[i].LastOpen < min_count) {
  760. min_count = Attached[i].LastOpen;
  761. }
  762. // Already in list?
  763. if (match_name.IsEqual(Attached[i].Name)) {
  764. i_found = i;
  765. }
  766. }
  767. }
  768. // Make sure the count isn't going to overflow
  769. if (LONG_MAX == AttachedCount + 1) {
  770. WsbAffirm(0 < min_count, E_FAIL);
  771. // Adjust counts down to avoid overflow
  772. for (i = 0; i < MAX_ATTACHED_DB; i++) {
  773. if (min_count <= Attached[i].LastOpen) {
  774. Attached[i].LastOpen -= min_count;
  775. }
  776. }
  777. AttachedCount -= min_count;
  778. }
  779. AttachedCount++;
  780. // If it's already in the list, update the info
  781. if (-1 != i_found) {
  782. WsbTrace(OLESTR("CWsbDbSys::DbAttachedAdd: i_found = %d\n"), i_found);
  783. Attached[i_found].LastOpen = AttachedCount;
  784. // If there's an empty slot, use it
  785. } else if (-1 != i_empty) {
  786. WsbTrace(OLESTR("CWsbDbSys::DbAttachedAdd: i_empty = %d\n"), i_empty);
  787. if (attach) {
  788. JET_ERR jstat;
  789. JET_SESID sid;
  790. WsbAffirm(m_pWsbDbSession, WSB_E_NOT_INITIALIZED);
  791. CComQIPtr<IWsbDbSessionPriv, &IID_IWsbDbSessionPriv> pSessionPriv = m_pWsbDbSession;
  792. WsbAffirmPointer(pSessionPriv);
  793. WsbAffirmHr(pSessionPriv->GetJetId(&sid));
  794. jstat = JetAttachDatabase(sid, jet_name, 0);
  795. if (JET_errFileNotFound == jstat) {
  796. WsbThrow(STG_E_FILENOTFOUND);
  797. } else if (JET_wrnDatabaseAttached == jstat) {
  798. WsbTrace(OLESTR("CWsbDbSys::DbAttachedAdd: DB is already attached\n"));
  799. // No problem
  800. } else {
  801. WsbAffirmHr(jet_error(jstat));
  802. }
  803. }
  804. Attached[i_empty].Name = match_name;
  805. Attached[i_empty].LastOpen = AttachedCount;
  806. // Try to detach the oldest DB first
  807. } else {
  808. WsbAffirmHr(DbAttachedEmptySlot());
  809. WsbAffirmHr(DbAttachedAdd(name, attach));
  810. }
  811. } WsbCatch(hr);
  812. if (jet_name) {
  813. WsbFree(jet_name);
  814. }
  815. LeaveCriticalSection(&AttachedCritSect);
  816. WsbTraceOut(OLESTR("CWsbDbSys::DbAttachedAdd"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
  817. return(hr);
  818. }
  819. HRESULT
  820. CWsbDbSys::DbAttachedEmptySlot(
  821. void)
  822. /*++
  823. Implements:
  824. IWsbDbSysPriv::DbAttachedEmptySlot
  825. Routine Description:
  826. Force an empty slot in the attached list even if this means detaching a DB.
  827. --*/
  828. {
  829. HRESULT hr = S_OK;
  830. WsbTraceIn(OLESTR("CWsbDbSys::DbAttachedEmptySlot"), OLESTR(""));
  831. // Don't worry about it if we're not initialized yet --
  832. // all the slots are empty
  833. if (AttachedInit) {
  834. EnterCriticalSection(&AttachedCritSect);
  835. try {
  836. BOOL has_empty = FALSE;
  837. int i;
  838. int i_oldest;
  839. LONG oldest_count;
  840. // Find an empty slot or the oldest that is not currently open
  841. reloop:
  842. i_oldest = -1;
  843. oldest_count = AttachedCount;
  844. for (i = 0; i < MAX_ATTACHED_DB; i++) {
  845. if (!Attached[i].Name) {
  846. has_empty = TRUE;
  847. break;
  848. } else if (Attached[i].LastOpen < oldest_count) {
  849. i_oldest = i;
  850. oldest_count = Attached[i].LastOpen;
  851. }
  852. }
  853. // If there's no empty slot, try detaching the oldest
  854. WsbTrace(OLESTR("CWsbDbSys::DbAttachedEmptySlot: has_empty = %ls, i = %d, i_oldest = %d\n"),
  855. WsbBoolAsString(has_empty), i, i_oldest);
  856. if (!has_empty) {
  857. JET_ERR jstat;
  858. char* name;
  859. JET_SESID sid;
  860. WsbAffirm(m_pWsbDbSession, WSB_E_NOT_INITIALIZED);
  861. CComQIPtr<IWsbDbSessionPriv, &IID_IWsbDbSessionPriv> pSessionPriv = m_pWsbDbSession;
  862. WsbAffirmPointer(pSessionPriv);
  863. WsbAffirmHr(pSessionPriv->GetJetId(&sid));
  864. WsbAffirm(-1 != i_oldest, WSB_E_IDB_TOO_MANY_DB);
  865. WsbAffirmHr(wsb_db_jet_fix_path(Attached[i_oldest].Name, L"." IDB_DB_FILE_SUFFIX, &name));
  866. jstat = JetDetachDatabase(sid, name);
  867. WsbFree(name);
  868. WsbTrace(OLESTR("CWsbDbSys::DbAttachedEmptySlot: JetDetachDatabase = %ld\n"),
  869. (LONG)jstat);
  870. if (JET_errDatabaseInUse == jstat) {
  871. WsbTrace(OLESTR("CWsbDbSys::DbAttachedEmptySlot: DB in use; try again\n"));
  872. Attached[i_oldest].LastOpen = AttachedCount;
  873. goto reloop;
  874. } else if (JET_errDatabaseNotFound != jstat) {
  875. WsbAffirmHr(jet_error(jstat));
  876. }
  877. Attached[i_oldest].Name.Free();
  878. Attached[i_oldest].LastOpen = 0;
  879. }
  880. } WsbCatch(hr);
  881. LeaveCriticalSection(&AttachedCritSect);
  882. }
  883. WsbTraceOut(OLESTR("CWsbDbSys::DbAttachedEmptySlot"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
  884. return(hr);
  885. }
  886. HRESULT
  887. CWsbDbSys::DbAttachedInit(
  888. void)
  889. /*++
  890. Implements:
  891. IWsbDbSysPriv::DbAttachedInit
  892. Routine Description:
  893. Initialize the attached-DB-list data.
  894. --*/
  895. {
  896. HRESULT hr = S_OK;
  897. WsbTraceIn(OLESTR("CWsbDbSys::DbAttachedInit"), OLESTR(""));
  898. EnterCriticalSection(&AttachedCritSect);
  899. try {
  900. if (!AttachedInit) {
  901. ULONG actual = 0;
  902. int i;
  903. JET_ERR jstat;
  904. JET_SESID sid;
  905. WsbAffirm(m_pWsbDbSession, WSB_E_NOT_INITIALIZED);
  906. CComQIPtr<IWsbDbSessionPriv, &IID_IWsbDbSessionPriv> pSessionPriv = m_pWsbDbSession;
  907. WsbAffirmPointer(pSessionPriv);
  908. WsbAffirmHr(pSessionPriv->GetJetId(&sid));
  909. // Initialize data
  910. for (i = 0; i < MAX_ATTACHED_DB; i++) {
  911. Attached[i].Name.Free();
  912. Attached[i].LastOpen = 0;
  913. }
  914. // Make sure there aren't pre-attached DBs
  915. jstat = JetDetachDatabase(sid, NULL);
  916. WsbTrace(OLESTR("CWsbDbSys::DbAttachedInit: JetDetachDatabase(NULL) = %ld\n"), (LONG)jstat);
  917. WsbAffirmHr(jet_error(jstat));
  918. AttachedInit = TRUE;
  919. }
  920. } WsbCatch(hr);
  921. LeaveCriticalSection(&AttachedCritSect);
  922. WsbTraceOut(OLESTR("CWsbDbSys::DbAttachedInit"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
  923. return(hr);
  924. }
  925. HRESULT
  926. CWsbDbSys::DbAttachedRemove(
  927. OLECHAR* name)
  928. /*++
  929. Implements:
  930. IWsbDbSysPriv::DbAttachedRemove
  931. Routine Description:
  932. Detach a DB (if attached).
  933. --*/
  934. {
  935. HRESULT hr = S_FALSE;
  936. char* jet_name = NULL;
  937. WsbTraceIn(OLESTR("CWsbDbSys::DbAttachedRemove"), OLESTR("name = %ls"),
  938. name);
  939. try {
  940. int i;
  941. CWsbStringPtr match_name;
  942. WsbAssert(name, E_POINTER);
  943. WsbAffirm(AttachedInit, S_FALSE);
  944. // Convert the name
  945. WsbAffirmHr(wsb_db_jet_fix_path(name, L"." IDB_DB_FILE_SUFFIX, &jet_name));
  946. match_name = jet_name;
  947. // See if it's in the list
  948. EnterCriticalSection(&AttachedCritSect);
  949. for (i = 0; i < MAX_ATTACHED_DB; i++) {
  950. if (Attached[i].Name) {
  951. if (match_name.IsEqual(Attached[i].Name)) {
  952. JET_ERR jstat;
  953. JET_SESID sid;
  954. WsbTrace(OLESTR("CWsbDbSys::DbAttachedRemove: found DB, index = %d\n"), i);
  955. WsbAffirm(m_pWsbDbSession, WSB_E_NOT_INITIALIZED);
  956. CComQIPtr<IWsbDbSessionPriv, &IID_IWsbDbSessionPriv> pSessionPriv = m_pWsbDbSession;
  957. WsbAffirmPointer(pSessionPriv);
  958. WsbAffirmHr(pSessionPriv->GetJetId(&sid));
  959. jstat = JetDetachDatabase(sid, jet_name);
  960. WsbTrace(OLESTR("CWsbDbSys::DbAttachedRemove: JetDetachDatabase = %ld\n"),
  961. (LONG)jstat);
  962. if (JET_errDatabaseNotFound != jstat) {
  963. WsbAffirmHr(jet_error(jstat));
  964. hr = S_OK;
  965. }
  966. Attached[i].Name.Free();
  967. Attached[i].LastOpen = 0;
  968. break;
  969. }
  970. }
  971. }
  972. } WsbCatch(hr);
  973. if (jet_name) {
  974. WsbFree(jet_name);
  975. }
  976. LeaveCriticalSection(&AttachedCritSect);
  977. WsbTraceOut(OLESTR("CWsbDbSys::DbAttachedRemove"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
  978. return(hr);
  979. }
  980. // wsb_db_jet_check_error - check for a jet error; return S_OK for no error;
  981. // print error to trace otherwise
  982. HRESULT wsb_db_jet_check_error(LONG jstat, char *fileName, DWORD lineNo)
  983. {
  984. HRESULT hr = S_OK;
  985. if (jstat != JET_errSuccess) {
  986. WsbTrace(OLESTR("Jet error = %ld (%hs line %ld)\n"), jstat,
  987. fileName, lineNo);
  988. // Convert JET error to IDB error for some common values
  989. switch (jstat) {
  990. case JET_errDiskFull:
  991. case JET_errLogDiskFull:
  992. hr = WSB_E_IDB_DISK_FULL;
  993. break;
  994. case JET_errDatabaseNotFound:
  995. hr = WSB_E_IDB_FILE_NOT_FOUND;
  996. break;
  997. case JET_errDatabaseInconsistent:
  998. case JET_errPageNotInitialized:
  999. case JET_errReadVerifyFailure:
  1000. case JET_errDatabaseCorrupted:
  1001. case JET_errBadLogSignature:
  1002. case JET_errBadDbSignature:
  1003. case JET_errBadCheckpointSignature:
  1004. case JET_errCheckpointCorrupt:
  1005. case JET_errMissingPatchPage:
  1006. case JET_errBadPatchPage:
  1007. hr = WSB_E_IDB_DATABASE_CORRUPT;
  1008. break;
  1009. case JET_errWriteConflict:
  1010. hr = WSB_E_IDB_UPDATE_CONFLICT;
  1011. break;
  1012. default:
  1013. hr = WSB_E_IDB_IMP_ERROR;
  1014. break;
  1015. }
  1016. // Log this error in the event log
  1017. if (g_WsbLogLevel) {
  1018. CWsbStringPtr str;
  1019. WsbSetEventInfo(fileName, lineNo, VER_PRODUCTBUILD, RS_BUILD_VERSION); \
  1020. str = WsbLongAsString(jstat);
  1021. if (WSB_E_IDB_IMP_ERROR != hr) {
  1022. str.Prepend(" (");
  1023. str.Prepend(WsbHrAsString(hr));
  1024. str.Append(")");
  1025. }
  1026. WsbTraceAndLogEvent(WSB_MESSAGE_IDB_ERROR, 0, NULL,
  1027. static_cast<OLECHAR *>(str), NULL);
  1028. }
  1029. }
  1030. return(hr);
  1031. }
  1032. // wsb_db_jet_fix_path - convert database path name from OLESTR to char*,
  1033. // change (or add) extension.
  1034. // Returns HRESULT
  1035. //
  1036. // NOTE: OLECHAR* is passed in, but char* is returned
  1037. HRESULT
  1038. wsb_db_jet_fix_path(OLECHAR* path, OLECHAR* ext, char** new_path)
  1039. {
  1040. HRESULT hr = S_OK;
  1041. try {
  1042. CWsbStringPtr string;
  1043. int tlen;
  1044. WsbAssertPointer(path);
  1045. WsbAssertPointer(new_path);
  1046. // Add extension if given
  1047. string = path;
  1048. WsbAffirm(0 != (WCHAR *)string, E_OUTOFMEMORY);
  1049. if (ext) {
  1050. WsbAffirmHr(AddExtension(&string, ext));
  1051. }
  1052. // Allocate char string
  1053. tlen = (wcslen(string) + 1) * sizeof(OLECHAR);
  1054. *new_path = (char*)WsbAlloc(tlen);
  1055. WsbAffirm(*new_path, E_OUTOFMEMORY);
  1056. // Convert from wide char to char
  1057. if (wcstombs(*new_path, string, tlen) == (size_t)-1) {
  1058. WsbFree(*new_path);
  1059. *new_path = NULL;
  1060. WsbThrow(WSB_E_STRING_CONVERSION);
  1061. }
  1062. } WsbCatch(hr);
  1063. return(hr);
  1064. }
  1065. // Local functions
  1066. // AddExtension - add (or replace) the file extension to the path.
  1067. // If Ext is NULL, remove the existing extension.
  1068. //
  1069. // Return S_OK if no errors occurred.
  1070. static HRESULT AddExtension(OLECHAR** pPath, OLECHAR* Ext)
  1071. {
  1072. HRESULT hr = S_OK;
  1073. WsbTraceIn(OLESTR("AddExtension(wsbdbsys)"), OLESTR("Path = \"%ls\", Ext = \"%ls\""),
  1074. WsbAbbreviatePath(*pPath, 120), Ext );
  1075. try {
  1076. int elen;
  1077. int len;
  1078. OLECHAR* new_path;
  1079. OLECHAR* pc;
  1080. OLECHAR* pc2;
  1081. int tlen;
  1082. WsbAssertPointer(pPath);
  1083. WsbAssertPointer(*pPath);
  1084. // Allocate string and copy path
  1085. len = wcslen(*pPath);
  1086. if (Ext) {
  1087. elen = wcslen(Ext);
  1088. } else {
  1089. elen = 0;
  1090. }
  1091. tlen = (len + elen + 1) * sizeof(OLECHAR);
  1092. new_path = static_cast<OLECHAR*>(WsbAlloc(tlen));
  1093. WsbAffirm(new_path, E_OUTOFMEMORY);
  1094. wcscpy(new_path, *pPath);
  1095. // Remove old extension (if there)
  1096. pc = wcsrchr(new_path, L'.');
  1097. pc2 = wcsrchr(new_path, L'\\');
  1098. if (pc && (!pc2 || pc2 < pc)) {
  1099. *pc = L'\0';
  1100. }
  1101. // Add the new extension (if given)
  1102. if (Ext) {
  1103. wcscat(new_path, Ext);
  1104. }
  1105. // Return the new path
  1106. WsbFree(*pPath);
  1107. *pPath = new_path;
  1108. } WsbCatch(hr);
  1109. WsbTraceOut(OLESTR("AddExtension(wsbdbsys)"), OLESTR("hr =<%ls>, new path = \"%ls\""),
  1110. WsbHrAsString(hr), WsbAbbreviatePath(*pPath, 120));
  1111. return(hr);
  1112. }
  1113. // ClearDirectory - delete all files in a directory
  1114. // Return S_OK if no errors occurred.
  1115. static HRESULT ClearDirectory(const OLECHAR* DirPath)
  1116. {
  1117. DWORD err;
  1118. WIN32_FIND_DATA FindData;
  1119. HANDLE hFind = 0;
  1120. HRESULT hr = S_OK;
  1121. int nDeleted = 0;
  1122. int nSkipped = 0;
  1123. CWsbStringPtr SearchPath;
  1124. WsbTraceIn(OLESTR("ClearDirectory(wsbdbsys)"), OLESTR("Path = <%ls>"),
  1125. WsbAbbreviatePath(DirPath, 120));
  1126. try {
  1127. SearchPath = DirPath;
  1128. SearchPath.Append("\\*");
  1129. hFind = FindFirstFile(SearchPath, &FindData);
  1130. if (INVALID_HANDLE_VALUE == hFind) {
  1131. hFind = 0;
  1132. err = GetLastError();
  1133. WsbTrace(OLESTR("ClearDirectory(wsbdbsys): FindFirstFile(%ls) failed, error = %ld\n"),
  1134. static_cast<OLECHAR*>(SearchPath), err);
  1135. WsbThrow(HRESULT_FROM_WIN32(err));
  1136. }
  1137. while (TRUE) {
  1138. if (FindData.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY |
  1139. FILE_ATTRIBUTE_HIDDEN)) {
  1140. nSkipped++;
  1141. } else {
  1142. CWsbStringPtr DeletePath;
  1143. DeletePath = DirPath;
  1144. DeletePath.Append("\\");
  1145. DeletePath.Append(FindData.cFileName);
  1146. if (!DeleteFile(DeletePath)) {
  1147. err = GetLastError();
  1148. WsbTrace(OLESTR("ClearDirectory(wsbdbsys): DeleteFile(%ls) failed, error = %ld\n"),
  1149. static_cast<OLECHAR*>(DeletePath), err);
  1150. WsbThrow(HRESULT_FROM_WIN32(err));
  1151. }
  1152. nDeleted++;
  1153. }
  1154. if (!FindNextFile(hFind, &FindData)) {
  1155. err = GetLastError();
  1156. if (ERROR_NO_MORE_FILES == err) break;
  1157. WsbTrace(OLESTR("ClearDirectory(wsbdbsys): FindNextFile failed, error = %ld\n"),
  1158. err);
  1159. WsbThrow(HRESULT_FROM_WIN32(err));
  1160. }
  1161. }
  1162. } WsbCatch(hr);
  1163. if (0 != hFind) {
  1164. FindClose(hFind);
  1165. }
  1166. WsbTraceOut(OLESTR("ClearDirectory(wsbdbsys)"), OLESTR("hr =<%ls>, # deleted = %d, # skipped = %d"),
  1167. WsbHrAsString(hr), nDeleted, nSkipped);
  1168. return(hr);
  1169. }
  1170. // CopyDirectory - copy files from one directory to another
  1171. // Return S_OK if no errors occurred.
  1172. static HRESULT CopyDirectory(const OLECHAR* DirSource, const OLECHAR* DirTarget)
  1173. {
  1174. DWORD err;
  1175. WIN32_FIND_DATA FindData;
  1176. HANDLE hFind = 0;
  1177. HRESULT hr = S_OK;
  1178. int nCopied = 0;
  1179. int nSkipped = 0;
  1180. CWsbStringPtr SearchPath;
  1181. WsbTraceIn(OLESTR("CopyDirectory(wsbdbsys)"), OLESTR("OldPath = \"%ls\", NewPath = \"%ls\""),
  1182. WsbQuickString(WsbAbbreviatePath(DirSource, 120)),
  1183. WsbQuickString(WsbAbbreviatePath(DirTarget, 120)));
  1184. try {
  1185. SearchPath = DirSource;
  1186. SearchPath.Append("\\*");
  1187. hFind = FindFirstFile(SearchPath, &FindData);
  1188. if (INVALID_HANDLE_VALUE == hFind) {
  1189. hFind = 0;
  1190. err = GetLastError();
  1191. WsbTrace(OLESTR("ClearDirectory(wsbdbsys): FindFirstFile(%ls) failed, error = %ld\n"),
  1192. static_cast<OLECHAR*>(SearchPath), err);
  1193. WsbThrow(HRESULT_FROM_WIN32(err));
  1194. }
  1195. while (TRUE) {
  1196. if (FindData.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY |
  1197. FILE_ATTRIBUTE_HIDDEN)) {
  1198. nSkipped++;
  1199. } else {
  1200. CWsbStringPtr NewPath;
  1201. CWsbStringPtr OldPath;
  1202. OldPath = DirSource;
  1203. OldPath.Append("\\");
  1204. OldPath.Append(FindData.cFileName);
  1205. NewPath = DirTarget;
  1206. NewPath.Append("\\");
  1207. NewPath.Append(FindData.cFileName);
  1208. if (!CopyFile(OldPath, NewPath, FALSE)) {
  1209. err = GetLastError();
  1210. WsbTrace(OLESTR("ClearDirectory(wsbdbsys): CopyFile(%ls, %ls) failed, error = %ld\n"),
  1211. static_cast<OLECHAR*>(OldPath),
  1212. static_cast<OLECHAR*>(NewPath), err);
  1213. WsbThrow(HRESULT_FROM_WIN32(err));
  1214. }
  1215. nCopied++;
  1216. }
  1217. if (!FindNextFile(hFind, &FindData)) {
  1218. err = GetLastError();
  1219. if (ERROR_NO_MORE_FILES == err) break;
  1220. WsbTrace(OLESTR("ClearDirectory(wsbdbsys): FindNextFile failed, error = %ld\n"),
  1221. err);
  1222. WsbThrow(HRESULT_FROM_WIN32(err));
  1223. }
  1224. }
  1225. } WsbCatch(hr);
  1226. if (0 != hFind) {
  1227. FindClose(hFind);
  1228. }
  1229. WsbTraceOut(OLESTR("CopyDirectory(wsbdbsys)"), OLESTR("hr =<%ls>, copied = %ld, skipped = %ld"),
  1230. WsbHrAsString(hr), nCopied, nSkipped);
  1231. return(hr);
  1232. }
  1233. // DirectoryHasFullBackup - try to determine if the directory contains a full backup
  1234. // Return
  1235. // S_OK if it contains a full backup
  1236. // S_FALSE if it doesn't
  1237. // E_* on errors
  1238. //
  1239. // The technique use here is somewhat ad hoc since it expects the full backup
  1240. // filename to end in IDB_DB_FILE_SUFFIX
  1241. static HRESULT DirectoryHasFullBackup(const OLECHAR* DirPath)
  1242. {
  1243. HRESULT hr = S_OK;
  1244. WsbTraceIn(OLESTR("DirectoryHasFullBackup(wsbdbsys)"), OLESTR("Path = <%ls>"),
  1245. WsbAbbreviatePath(DirPath, 120));
  1246. try {
  1247. ULONG Count;
  1248. WsbAffirmHr(FileCount(DirPath, L"*." IDB_DB_FILE_SUFFIX, &Count));
  1249. if (0 == Count) {
  1250. hr = S_FALSE;
  1251. }
  1252. } WsbCatch(hr);
  1253. WsbTraceOut(OLESTR("DirectoryHasFullBackup(wsbdbsys)"), OLESTR("hr =<%ls>"),
  1254. WsbHrAsString(hr));
  1255. return(hr);
  1256. }
  1257. // FileCount - count all files in a directory matching a pattern. Skip
  1258. // directories and hidden files.
  1259. // Return S_OK if no errors occurred.
  1260. static HRESULT FileCount(const OLECHAR* DirPath, const OLECHAR* Pattern,
  1261. ULONG* Count)
  1262. {
  1263. DWORD err;
  1264. WIN32_FIND_DATA FindData;
  1265. HANDLE hFind = 0;
  1266. HRESULT hr = S_OK;
  1267. int lCount = 0;
  1268. int nSkipped = 0;
  1269. CWsbStringPtr SearchPath;
  1270. WsbTraceIn(OLESTR("FileCount(wsbdbsys)"), OLESTR("Path = <%ls>"),
  1271. WsbAbbreviatePath(DirPath, 120));
  1272. try {
  1273. SearchPath = DirPath;
  1274. SearchPath.Append("\\");
  1275. SearchPath.Append(Pattern);
  1276. *Count = 0;
  1277. hFind = FindFirstFile(SearchPath, &FindData);
  1278. if (INVALID_HANDLE_VALUE == hFind) {
  1279. hFind = 0;
  1280. err = GetLastError();
  1281. if (ERROR_FILE_NOT_FOUND == err) WsbThrow(S_OK);
  1282. WsbTrace(OLESTR("FileCount(wsbdbsys): FindFirstFile(%ls) failed, error = %ld\n"),
  1283. static_cast<OLECHAR*>(SearchPath), err);
  1284. WsbThrow(HRESULT_FROM_WIN32(err));
  1285. }
  1286. while (TRUE) {
  1287. if (FindData.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY |
  1288. FILE_ATTRIBUTE_HIDDEN)) {
  1289. nSkipped++;
  1290. } else {
  1291. lCount++;
  1292. }
  1293. if (!FindNextFile(hFind, &FindData)) {
  1294. err = GetLastError();
  1295. if (ERROR_NO_MORE_FILES == err) break;
  1296. WsbTrace(OLESTR("FileCount(wsbdbsys): FindNextFile failed, error = %ld\n"),
  1297. err);
  1298. WsbThrow(HRESULT_FROM_WIN32(err));
  1299. }
  1300. }
  1301. } WsbCatch(hr);
  1302. if (0 != hFind) {
  1303. FindClose(hFind);
  1304. }
  1305. if (S_OK == hr) {
  1306. *Count = lCount;
  1307. }
  1308. WsbTraceOut(OLESTR("FileCount(wsbdbsys)"), OLESTR("hr =<%ls>, # skipped = %d, Count = %ld"),
  1309. WsbHrAsString(hr), nSkipped, *Count);
  1310. return(hr);
  1311. }
  1312. // RenameDirectory - rename a directory
  1313. // Return S_OK if no errors occurred.
  1314. static HRESULT RenameDirectory(const OLECHAR* OldDir, const OLECHAR* NewDir)
  1315. {
  1316. DWORD err;
  1317. HRESULT hr = S_OK;
  1318. WsbTraceIn(OLESTR("RenameDirectory(wsbdbsys)"), OLESTR("OldPath = \"%ls\", NewPath = \"%ls\""),
  1319. WsbQuickString(WsbAbbreviatePath(OldDir, 120)),
  1320. WsbQuickString(WsbAbbreviatePath(NewDir, 120)));
  1321. try {
  1322. if (!MoveFile(OldDir, NewDir)) {
  1323. err = GetLastError();
  1324. WsbTrace(OLESTR("RenameDirectory(wsbdbsys): MoveFile failed, error = %ld\n"), err);
  1325. WsbThrow(HRESULT_FROM_WIN32(err));
  1326. }
  1327. } WsbCatch(hr);
  1328. WsbTraceOut(OLESTR("RenameDirectory(wsbdbsys)"), OLESTR("hr =<%ls>"),
  1329. WsbHrAsString(hr));
  1330. return(hr);
  1331. }