Source code of Windows XP (NT5)
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.

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