windows-nt/Source/XPSP1/NT/base/fs/hsm/idb/wsbdbsys.cpp

1689 lines
53 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
<EFBFBD> 1998 Seagate Software, Inc. All rights reserved.
Module Name:
Wsbdbsys.cpp
Abstract:
CWsbDbSys class.
Author:
Ron White [ronw] 1-Jul-1997
Revision History:
--*/
#include "stdafx.h"
#include "rsevents.h"
#include "wsbdbsys.h"
#include "wsbdbses.h"
#include <mbstring.h>
#include <limits.h>
#define MAX_ATTACHED_DB 6 // Set by ESE/JET engine (suppose to be 7)
#if !defined(BACKUP_TEST_TIMES)
// Normal values
#define DEFAULT_AUTOBACKUP_INTERVAL (3 * 60 * 60 * 1000) // 3 hours
#define DEFAULT_AUTOBACKUP_IDLE_MINUTES 5
#define DEFAULT_AUTOBACKUP_COUNT_MIN 100
#define DEFAULT_AUTOBACKUP_LOG_COUNT 10
#else
// Test values
#define DEFAULT_AUTOBACKUP_INTERVAL (4 * 60 * 1000) // 4 minutes
#define DEFAULT_AUTOBACKUP_IDLE_MINUTES 1
#define DEFAULT_AUTOBACKUP_COUNT_MIN 5
#define DEFAULT_AUTOBACKUP_LOG_COUNT 4
#endif
#define DEFAULT_AUTOBACKUP_COUNT_MAX 500
// Local stuff
// ATTACHED_DB_DATA holds information about currently attached DBs
typedef struct {
CWsbStringPtr Name; // Database name
LONG LastOpen; // Sequence number of last open
} ATTACHED_DB_DATA;
// This static data manages a list of attached databases for this process.
// (Future: If we want this list to be managed on a per instance basis, all of
// this data should become class members and handled appropriately)
static ATTACHED_DB_DATA Attached[MAX_ATTACHED_DB];
static LONG AttachedCount = 0;
static CRITICAL_SECTION AttachedCritSect;
static BOOL AttachedInit = FALSE;
static SHORT AttachedCritSectUsers = 0;
static CComCreator< CComObject<CWsbDbSession> > SessionFactory;
// Local functions
static HRESULT AddExtension(OLECHAR** pPath, OLECHAR* Ext);
static HRESULT ClearDirectory(const OLECHAR* DirPath);
static HRESULT CopyDirectory(const OLECHAR* DirSource, const OLECHAR* DirTarget);
static HRESULT DirectoryHasFullBackup(const OLECHAR* DirPath);
static HRESULT FileCount(const OLECHAR* DirPath, const OLECHAR* Pattern,
ULONG* Count);
static HRESULT RenameDirectory(const OLECHAR* OldDir, const OLECHAR* NewDir);
// Non-member function initially called for autobackup thread
static DWORD WsbDbSysStartAutoBackup(
void* pVoid
)
{
return(((CWsbDbSys*) pVoid)->AutoBackup());
}
HRESULT
CWsbDbSys::AutoBackup(
void
)
/*++
Routine Description:
Implements an auto-backup loop.
Arguments:
None.
Return Value:
Doesn't matter.
--*/
{
HRESULT hr = S_OK;
try {
ULONG SleepPeriod = DEFAULT_AUTOBACKUP_INTERVAL;
BOOL exitLoop = FALSE;
while (! exitLoop) {
// Wait for termination event, if timeout occurs, check the sleep period criteria
switch (WaitForSingleObject(m_terminateEvent, SleepPeriod)) {
case WAIT_OBJECT_0:
// Need to terminate
WsbTrace(OLESTR("CWsbDbSys::AutoBackup: signaled to terminate\n"));
exitLoop = TRUE;
break;
case WAIT_TIMEOUT:
// Check if backup need to be performed
WsbTrace(OLESTR("CWsbDbSys::AutoBackup awakened, ChangeCount = %ld\n"), m_ChangeCount);
// Don't do a backup if there hasn't been much activity
if (DEFAULT_AUTOBACKUP_COUNT_MIN < m_ChangeCount) {
LONG DiffMinutes;
FILETIME ftNow;
LONGLONG NowMinutes;
LONGLONG ThenMinutes;
// Wait for an idle time
GetSystemTimeAsFileTime(&ftNow);
NowMinutes = WsbFTtoLL(ftNow) / WSB_FT_TICKS_PER_MINUTE;
ThenMinutes = WsbFTtoLL(m_LastChange) / WSB_FT_TICKS_PER_MINUTE;
DiffMinutes = static_cast<LONG>(NowMinutes - ThenMinutes);
WsbTrace(OLESTR("CWsbDbSys::AutoBackup idle minutes = %ld\n"),
DiffMinutes);
if (DEFAULT_AUTOBACKUP_IDLE_MINUTES < DiffMinutes ||
DEFAULT_AUTOBACKUP_COUNT_MAX < m_ChangeCount) {
hr = Backup(NULL, 0);
if (S_OK != hr) {
// Just trace and go back to wait for the next round...
WsbTrace(OLESTR("CWsbDbSys::AutoBackup: Backup failed, hr=<%ls>\n"), WsbHrAsString(hr));
}
SleepPeriod = DEFAULT_AUTOBACKUP_INTERVAL;;
} else {
// Reduce the sleep time so we catch the next idle time
ULONG SleepMinutes = SleepPeriod / (1000 * 60);
if (SleepMinutes > (DEFAULT_AUTOBACKUP_IDLE_MINUTES * 2)) {
SleepPeriod /= 2;
}
}
}
break; // end of timeout case
case WAIT_FAILED:
default:
WsbTrace(OLESTR("CWsbDbSys::AutoBackup: WaitForSingleObject returned error %lu\n"), GetLastError());
exitLoop = TRUE;
break;
} // end of switch
} // end of while
} WsbCatch(hr);
return(hr);
}
HRESULT
CWsbDbSys::Backup(
IN OLECHAR* path,
IN ULONG flags
)
/*++
Implements:
IWsbDbSys::Backup
--*/
{
HRESULT hr = S_OK;
char* backup_path = NULL;
WsbTraceIn(OLESTR("CWsbDbSys::Backup"), OLESTR("path = <%ls>, flags = %lx"),
path, flags);
try {
CWsbStringPtr BackupDir;
JET_ERR jstat = JET_errSuccess;
WsbAffirm(m_jet_initialized, WSB_E_NOT_INITIALIZED);
// Set and save the backup path; make sure it exists
if (NULL != path) {
m_BackupPath = path;
}
CreateDirectory(m_BackupPath, NULL);
// Start the automatic backup thread if requested
if (flags & IDB_BACKUP_FLAG_AUTO) {
// Don't start AutoBackup thread if it's already running
if (0 == m_AutoThread) {
DWORD threadId;
// Create termination event for auto-backup thread
WsbAffirmHandle(m_terminateEvent = CreateEvent(NULL, TRUE, FALSE, NULL));
WsbAffirm((m_AutoThread = CreateThread(0, 0, WsbDbSysStartAutoBackup,
(void*) this, 0, &threadId)) != 0, HRESULT_FROM_WIN32(GetLastError()));
}
// Do a full backup to a temporary directory
} else if (flags & IDB_BACKUP_FLAG_FORCE_FULL) {
BOOL UsedTempDir = FALSE;
// Don't wipe out an existing backup -- if the normal backup
// directory contains a full backup, do the full backup to
// the .ful directory
BackupDir = m_BackupPath;
WsbAffirm(0 != (WCHAR *)BackupDir, E_OUTOFMEMORY);
if (S_OK == DirectoryHasFullBackup(BackupDir)) {
WsbAffirmHr(AddExtension(&BackupDir, L".ful"));
UsedTempDir = TRUE;
}
// Make sure the directory exists (should check for errors?)
CreateDirectory(BackupDir, NULL);
// Make sure the directory is empty (the call to JetBackup will
// fail if it's not)
WsbAffirmHr(ClearDirectory(BackupDir));
// Convert to narrow char string for parameter
WsbAffirmHr(wsb_db_jet_fix_path(BackupDir, NULL, &backup_path));
WsbTrace(OLESTR("CWsbDbSys::Backup: backup_path = <%hs>\n"), backup_path);
// Do backup
WsbAffirm(NULL != m_BackupEvent, WSB_E_IDB_WRONG_BACKUP_SETTINGS);
DWORD status = WaitForSingleObject(m_BackupEvent, EVENT_WAIT_TIMEOUT);
DWORD errWait;
switch(status) {
case WAIT_OBJECT_0:
// Expected case - do Backup
jstat = JetBackupInstance(m_jet_instance, backup_path, 0, NULL);
if (! SetEvent(m_BackupEvent)) {
// Don't abort, just trace error
WsbTraceAlways(OLESTR("CWsbDbSys::Backup: SetEvent returned unexpected error %lu\n"), GetLastError());
}
WsbAffirmHr(jet_error(jstat));
break;
case WAIT_TIMEOUT:
// Timeout - don't do backup
WsbTraceAlways(OLESTR("CWsbDbSys::Backup, Wait for Single Object timed out after %lu ms\n"), EVENT_WAIT_TIMEOUT);
WsbThrow(E_ABORT);
break;
case WAIT_FAILED:
errWait = GetLastError();
WsbTraceAlways(OLESTR("CWsbDbSys::Backup, Wait for Single Object returned error %lu\n"), errWait);
WsbThrow(HRESULT_FROM_WIN32(errWait));
break;
default:
WsbTraceAlways(OLESTR("CWsbDbSys::Backup, Wait for Single Object returned unexpected status %lu\n"), status);
WsbThrow(E_UNEXPECTED);
break;
}
// Full backup worked -- copy to real backup directory
if (UsedTempDir) {
try {
WsbAffirmHr(ClearDirectory(m_BackupPath));
WsbAffirmHr(CopyDirectory(BackupDir, m_BackupPath));
WsbAffirmHr(ClearDirectory(BackupDir));
// Try to delete temporary directory (may fail)
DeleteFile(BackupDir);
BackupDir = m_BackupPath;
} WsbCatch(hr);
}
WsbLogEvent(WSB_MESSAGE_IDB_BACKUP_FULL, 0, NULL,
WsbAbbreviatePath(BackupDir, 120), NULL);
m_ChangeCount = 0;
WsbAffirmHr(hr);
// Try an incremental backup
} else {
ULONG LogCount;
BOOL TryFullBackup = FALSE;
WsbAffirmHr(FileCount(m_BackupPath, L"*.log", &LogCount));
if (LogCount > DEFAULT_AUTOBACKUP_LOG_COUNT ||
S_FALSE == DirectoryHasFullBackup(m_BackupPath)) {
// Do a full backup instead of the incremental if there
// are already too many log files, or there's no full
// backup in the backup directory (which means the incremental
// wouldn't work anyway)
TryFullBackup = TRUE;
} else {
WsbTrace(OLESTR("CWsbDbSys::Backup, trying incremental backup\n"));
// Convert to narrow char string for parameter
WsbAffirmHr(wsb_db_jet_fix_path(m_BackupPath, NULL, &backup_path));
WsbTrace(OLESTR("CWsbDbSys::Backup: backup_path = <%hs>\n"), backup_path);
WsbAffirm(NULL != m_BackupEvent, WSB_E_IDB_WRONG_BACKUP_SETTINGS);
DWORD status = WaitForSingleObject(m_BackupEvent, EVENT_WAIT_TIMEOUT);
DWORD errWait;
switch(status) {
case WAIT_OBJECT_0:
// Expected case - do Backup
jstat = JetBackupInstance(m_jet_instance, backup_path, JET_bitBackupIncremental, NULL);
if (! SetEvent(m_BackupEvent)) {
// Don't abort, just trace error
WsbTraceAlways(OLESTR("CWsbDbSys::Backup: SetEvent returned unexpected error %lu\n"), GetLastError());
}
break;
case WAIT_TIMEOUT:
// Timeout - don't do backup
WsbTraceAlways(OLESTR("CWsbDbSys::Backup, Wait for Single Object timed out after %lu ms\n"), EVENT_WAIT_TIMEOUT);
WsbThrow(E_ABORT);
break;
case WAIT_FAILED:
errWait = GetLastError();
WsbTraceAlways(OLESTR("CWsbDbSys::Backup, Wait for Single Object returned error %lu\n"), errWait);
WsbThrow(HRESULT_FROM_WIN32(errWait));
break;
default:
WsbTraceAlways(OLESTR("CWsbDbSys::Backup, Wait for Single Object returned unexpected status %lu\n"), status);
WsbThrow(E_UNEXPECTED);
break;
}
// Check for an error.
if (JET_errSuccess != jstat) {
if (JET_errMissingFullBackup == jstat) {
// Full backup need to be performed
WsbLogEvent(WSB_MESSAGE_IDB_MISSING_FULL_BACKUP, 0, NULL,
WsbAbbreviatePath(m_BackupPath, 120), NULL);
} else {
// Unknown error of incremental backup. Try a full backup anyway
WsbLogEvent(WSB_MESSAGE_IDB_INCREMENTAL_BACKUP_FAILED, 0, NULL,
WsbAbbreviatePath(m_BackupPath, 120),
WsbLongAsString(jstat), NULL );
}
TryFullBackup = TRUE;
} else {
// The incremental backup worked
WsbLogEvent(WSB_MESSAGE_IDB_BACKUP_INCREMENTAL, 0, NULL,
WsbAbbreviatePath(m_BackupPath, 120), NULL);
m_ChangeCount = 0;
}
}
// Try full backup?
if (TryFullBackup) {
WsbAffirmHr(Backup(NULL, IDB_BACKUP_FLAG_FORCE_FULL));
}
}
} WsbCatchAndDo(hr,
WsbLogEvent(WSB_MESSAGE_IDB_BACKUP_FAILED, 0, NULL,
WsbAbbreviatePath(m_BackupPath, 120), NULL);
);
if (NULL != backup_path) {
WsbFree(backup_path);
}
WsbTraceOut(OLESTR("CWsbDbSys::Backup"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
return(hr);
}
HRESULT
CWsbDbSys::FinalConstruct(
void
)
/*++
Implements:
CComObjectRoot::FinalConstruct
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CWsbDbSys::FinalConstruct"), OLESTR("") );
try {
m_AutoThread = 0;
m_terminateEvent = NULL;
m_ChangeCount = 0;
m_jet_initialized = FALSE;
m_jet_instance = JET_instanceNil;
m_BackupEvent = NULL;
try {
// Initialize critical sections (global resource, so init only for first user)
if (AttachedCritSectUsers == 0) {
WsbAffirmStatus(InitializeCriticalSectionAndSpinCount (&AttachedCritSect, 1000));
}
AttachedCritSectUsers++;
} catch(DWORD status) {
AttachedCritSectUsers--;
WsbLogEvent(status, 0, NULL, NULL);
switch (status) {
case STATUS_NO_MEMORY:
WsbThrow(E_OUTOFMEMORY);
break;
default:
WsbThrow(E_UNEXPECTED);
break;
}
}
} WsbCatch(hr);
WsbTraceOut(OLESTR("CWsbDbSys::FinalConstruct"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
return(hr);
}
void
CWsbDbSys::FinalRelease(
void
)
/*++
Implements:
CComObjectRoot::FinalRelease
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CWsbDbSys::FinalRelease"), OLESTR(""));
try {
// Make sure that Terminate was called
if (m_jet_initialized == TRUE) {
WsbAffirmHr(Terminate());
}
} WsbCatch(hr);
// Global resource, so delete only for last user
AttachedCritSectUsers--;
if (AttachedCritSectUsers == 0) {
DeleteCriticalSection(&AttachedCritSect);
}
WsbTraceOut(OLESTR("CWsbDbSys::FinalRelease"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
}
HRESULT
CWsbDbSys::Init(
IN OLECHAR* path,
IN ULONG flags
)
/*++
Implements:
IWsbDbSys::Init
--*/
{
HRESULT hr = S_OK;
char* log_path = NULL;
static BOOL bFirstTime = TRUE;
static int nInstance = 0;
WsbTraceIn(OLESTR("CWsbDbSys::Init"), OLESTR("path = <%ls>"), path);
try {
CWsbStringPtr dir;
JET_ERR jstat = JET_errSuccess;
// Initialize the Jet engine just once per Jet instance
WsbAffirm(!m_jet_initialized, E_FAIL);
// Initialize backup event, unless Jet backup is not required for this isntance
if (! (flags & IDB_SYS_INIT_FLAG_NO_BACKUP)) {
WsbAffirmHandle(m_BackupEvent = CreateEvent(NULL, FALSE, TRUE, HSM_IDB_STATE_EVENT));
}
// WsbDbSys represents one Jet instance.
// However, some Jet initialization should be done only once per process,
// before the first instance is being created.
if (bFirstTime) {
bFirstTime = FALSE;
// Increase the default number of maximum Jet sesions for the process
// TEMPORARY: Can this be set separately per instance?
jstat = JetSetSystemParameter(0, 0, JET_paramCacheSizeMin , (IDB_MAX_NOF_SESSIONS*4), NULL);
WsbTrace(OLESTR("CWsbDbSys::Init, JetSetSystemParameter(CacheSizeMax) = %ld\n"), jstat);
WsbAffirmHr(jet_error(jstat));
jstat = JetSetSystemParameter(0, 0, JET_paramMaxSessions, IDB_MAX_NOF_SESSIONS, NULL);
WsbTrace(OLESTR("CWsbDbSys::Init, JetSetSystemParameter(MaxSessions) = %ld\n"), jstat);
WsbAffirmHr(jet_error(jstat));
// Tell Jet we are going to use multiple instances
jstat = JetEnableMultiInstance(NULL, 0, NULL);
WsbAffirmHr(jet_error(jstat));
}
// Here start the per-instance initialization.
// First step is creating the instance
// Use a numeric counter as instance name - we care only that the name is unique
WsbAssert(JET_instanceNil == m_jet_instance, E_FAIL);
nInstance++;
char szInstance[10];
sprintf(szInstance, "%d", nInstance);
WsbTrace(OLESTR("CWsbDbSys::Init, Jet instance name = <%hs>\n"), szInstance);
jstat = JetCreateInstance(&m_jet_instance, szInstance);
WsbAffirmHr(jet_error(jstat));
// Set some per-instance parameters:
// Create path for log directory (same path is also used for system files and temp files)
WsbAffirm(NULL != path, E_INVALIDARG);
m_InitPath = path;
m_BackupPath = m_InitPath;
WsbAffirmHr(AddExtension(&m_BackupPath, L".bak"));
WsbTrace(OLESTR("CWsbDbSys::Init, BackupPath = <%ls>\n"), (WCHAR *)m_BackupPath);
WsbAffirmHr(wsb_db_jet_fix_path(path, OLESTR(""), &log_path));
dir = log_path; // Convert to WCHAR
// Make sure the directory exists.
WsbTrace(OLESTR("CWsbDbSys::Init, Creating dir = <%ls>\n"), (WCHAR *)dir);
if (! CreateDirectory(dir, NULL)) {
DWORD status = GetLastError();
if ((status == ERROR_ALREADY_EXISTS) || (status == ERROR_FILE_EXISTS)) {
status = NO_ERROR;
}
WsbAffirmNoError(status);
}
ULONG checkpointDepth;
ULONG logFileSize = 128; // In kilobytes
if (! (flags & IDB_SYS_INIT_FLAG_NO_LOGGING)) {
WsbTrace(OLESTR("CWsbDbSys::Init, LogFilePath = <%hs>\n"), log_path);
jstat = JetSetSystemParameter(&m_jet_instance, 0, JET_paramLogFilePath, 0, log_path);
WsbTrace(OLESTR("CWsbDbSys::Init, JetSetSystemParameter(LogFilePath) = %ld\n"), jstat);
WsbAffirmHr(jet_error(jstat));
// Use circular logging for "limited" logging
if (flags & IDB_SYS_INIT_FLAG_LIMITED_LOGGING) {
logFileSize = 512; // Increase the log file size
jstat = JetSetSystemParameter(&m_jet_instance, 0, JET_paramCircularLog, 1, NULL);
WsbAffirmHr(jet_error(jstat));
WsbTrace(OLESTR("CWsbDbSys::Init: set circular logging\n"));
// Set the amount of logging allowed before a check point
// to allow about 4 log files
// (the check point depth is set in bytes.)
checkpointDepth = 4 * logFileSize * 1024;
jstat = JetSetSystemParameter(&m_jet_instance, 0, JET_paramCheckpointDepthMax,
checkpointDepth, NULL);
WsbAffirmHr(jet_error(jstat));
WsbTrace(OLESTR("CWsbDbSys::Init: set CheckpointDepthMax = %ld\n"), checkpointDepth);
}
} else {
jstat = JetSetSystemParameter(&m_jet_instance, 0, JET_paramRecovery,
0, "off");
WsbAffirmHr(jet_error(jstat));
WsbTrace(OLESTR("CWsbDbSys::Init: set JET_paramRecovery to 0 (no logging)\n"));
}
// Set parameters for where to put auxiliary data
WsbTrace(OLESTR("CWsbDbSys::Init, SystemPath = <%hs>\n"), log_path);
jstat = JetSetSystemParameter(&m_jet_instance, 0, JET_paramSystemPath, 0, log_path);
WsbAffirmHr(jet_error(jstat));
// The next one, for some unknown reason, needs a file name at the end of the path
WsbAffirmHr(dir.Append("\\temp.edb"));
WsbAffirmHr(dir.CopyTo(&log_path));
WsbTrace(OLESTR("CWsbDbSys::Init, TempPath = <%hs>\n"), log_path);
jstat = JetSetSystemParameter(&m_jet_instance, 0, JET_paramTempPath, 0, log_path);
WsbAffirmHr(jet_error(jstat));
if (! (flags & IDB_SYS_INIT_FLAG_NO_LOGGING)) {
// Set the log file size (in KB). The minimum seems to be 128KB.
jstat = JetSetSystemParameter(&m_jet_instance, 0, JET_paramLogFileSize,
logFileSize, NULL);
WsbAffirmHr(jet_error(jstat));
WsbTrace(OLESTR("CWsbDbSys::Init: set logFileSize to %ld Kb\n"), logFileSize);
}
// Set parameter for deleting out-of-range log files.
// These files may exist after a restore from a db backup without clearing the db directory first
if (! (flags & IDB_SYS_INIT_FLAG_NO_BACKUP)) {
jstat = JetSetSystemParameter(&m_jet_instance, 0, JET_paramDeleteOutOfRangeLogs, 1, NULL);
WsbAffirmHr(jet_error(jstat));
WsbTrace(OLESTR("CWsbDbSys::Init: set delete out-of-range logs\n"));
}
// Initialize the DB instance
jstat = JetInit(&m_jet_instance);
hr = jet_error(jstat);
// If this failed, report the error
if (!SUCCEEDED(hr)) {
if (flags & IDB_SYS_INIT_FLAG_SPECIAL_ERROR_MSG) {
// Special message for FSA
WsbLogEvent(WSB_E_IDB_DELETABLE_DATABASE_CORRUPT, 0, NULL, NULL);
WsbThrow(WSB_E_RESOURCE_UNAVAILABLE);
} else {
WsbThrow(hr);
}
}
WsbTrace(OLESTR("CWsbDbSys::Init: jet instance = %p\n"), (LONG_PTR)m_jet_instance);
m_jet_initialized = TRUE;
// Create a session for internal use of this instance
WsbAffirmHr(NewSession(&m_pWsbDbSession));
WsbTrace(OLESTR("CWsbDbSys::Init, m_pWsbDbSession = %p\n"), (IWsbDbSession*)m_pWsbDbSession);
} WsbCatchAndDo(hr,
WsbLogEvent(WSB_MESSAGE_IDB_INIT_FAILED, 0, NULL,
WsbAbbreviatePath(m_InitPath, 120), NULL);
);
if (NULL != log_path) {
WsbFree(log_path);
}
WsbTraceOut(OLESTR("CWsbDbSys::Init"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
return(hr);
}
HRESULT
CWsbDbSys::Terminate(
void
)
/*++
Implements:
IWsbDbSys::Terminate
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CWsbDbSys::Terminate"), OLESTR(""));
try {
// If wasn't initialized or alreday cleaned up - just get out
if (m_jet_initialized == FALSE) {
WsbTrace(OLESTR("CWsbDbSys::Terminate - this insatnce is not initialized"));
WsbThrow(S_OK);
}
// Terminate the auto-backup thread
if (m_AutoThread) {
// Signal thread to terminate
SetEvent(m_terminateEvent);
// Wait for the thread, if it doesn't terminate gracefully - kill it
switch (WaitForSingleObject(m_AutoThread, 20000)) {
case WAIT_FAILED: {
WsbTrace(OLESTR("CWsbDbSys::Terminate: WaitForSingleObject returned error %lu\n"), GetLastError());
}
// fall through...
case WAIT_TIMEOUT: {
WsbTrace(OLESTR("CWsbDbSys::Terminate: force terminating of auto-backup thread.\n"));
DWORD dwExitCode;
if (GetExitCodeThread( m_AutoThread, &dwExitCode)) {
if (dwExitCode == STILL_ACTIVE) { // thread still active
if (!TerminateThread (m_AutoThread, 0)) {
WsbTrace(OLESTR("CWsbDbSys::Terminate: TerminateThread returned error %lu\n"), GetLastError());
}
}
} else {
WsbTrace(OLESTR("CWsbDbSys::Terminate: GetExitCodeThread returned error %lu\n"), GetLastError());
}
break;
}
default:
// Thread terminated gracefully
break;
}
// Best effort done for terminating auto-backup thread
CloseHandle(m_AutoThread);
m_AutoThread = 0;
}
if (m_terminateEvent != NULL) {
CloseHandle(m_terminateEvent);
m_terminateEvent = NULL;
}
// Detach DBs before exiting so they don't automatically get
// reattached the next time we start up
if (m_pWsbDbSession) {
JET_SESID sid;
CComQIPtr<IWsbDbSessionPriv, &IID_IWsbDbSessionPriv> pSessionPriv = m_pWsbDbSession;
WsbAffirmPointer(pSessionPriv);
WsbAffirmHr(pSessionPriv->GetJetId(&sid));
// Clean up the Attached data
if (AttachedInit) {
EnterCriticalSection(&AttachedCritSect);
for (int i = 0; i < MAX_ATTACHED_DB; i++) {
Attached[i].Name.Free();
Attached[i].LastOpen = 0;
}
JetDetachDatabase(sid, NULL);
AttachedInit = FALSE;
LeaveCriticalSection(&AttachedCritSect);
}
// Release the global session for this instance
m_pWsbDbSession = 0;
}
// Terminate Jet
JetTerm(m_jet_instance);
m_jet_initialized = FALSE;
m_jet_instance = JET_instanceNil;
} WsbCatch(hr);
if (m_BackupEvent) {
CloseHandle(m_BackupEvent);
m_BackupEvent = NULL;
}
WsbTraceOut(OLESTR("CWsbDbSys::Terminate"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
return (hr);
}
HRESULT
CWsbDbSys::NewSession(
OUT IWsbDbSession** ppSession
)
/*++
Implements:
IWsbDbSys::NewSession
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CWsbDbSys::NewSession"), OLESTR(""));
try {
WsbAffirm(0 != ppSession, E_POINTER);
WsbAffirmHr(SessionFactory.CreateInstance(NULL, IID_IWsbDbSession,
(void**)ppSession));
CComQIPtr<IWsbDbSessionPriv, &IID_IWsbDbSessionPriv> pSessionPriv = *ppSession;
WsbAffirmPointer(pSessionPriv);
WsbAffirmHr(pSessionPriv->Init(&m_jet_instance));
} WsbCatch(hr);
WsbTraceOut(OLESTR("CWsbDbSys::NewSession"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
return(hr);
}
HRESULT CWsbDbSys::GetGlobalSession(
OUT IWsbDbSession** ppSession
)
/*++
Implements:
IWsbDbSys::GetGlobalSession
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CWsbDbSys::GetGlobalSession"), OLESTR(""));
//
// If the Task Manager has been created, return the pointer. Otherwise,
// fail.
try {
WsbAssert(0 != ppSession, E_POINTER);
*ppSession = m_pWsbDbSession;
WsbAffirm(m_pWsbDbSession != 0, E_FAIL);
m_pWsbDbSession->AddRef();
} WsbCatch(hr);
WsbTraceOut(OLESTR("CWsbDbSys::GetGlobalSession"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
return (hr);
}
HRESULT
CWsbDbSys::Restore(
IN OLECHAR* fromPath,
IN OLECHAR* toPath
)
/*++
Implements:
IWsbDbSys::Restore
--*/
{
HRESULT hr = S_OK;
char* backup_path = NULL;
char* restore_path = NULL;
WsbTraceIn(OLESTR("CWsbDbSys::Restore"), OLESTR("fromPath = <%ls>, toPath = <%ls>"),
fromPath, toPath);
try {
CWsbStringPtr dir;
JET_ERR jstat;
// This is only allowed before Init
WsbAffirm(!m_jet_initialized, E_UNEXPECTED);
WsbAffirm(NULL != fromPath, E_POINTER);
WsbAffirm(NULL != toPath, E_POINTER);
// Convert pathes
WsbAffirmHr(wsb_db_jet_fix_path(fromPath, OLESTR(""), &backup_path));
WsbAffirmHr(wsb_db_jet_fix_path(toPath, OLESTR(""), &restore_path));
// Make sure the target directory exists. Should check for error.
dir = restore_path;
CreateDirectory(dir, NULL);
jstat = JetRestoreInstance(m_jet_instance, backup_path, restore_path, NULL);
WsbAffirmHr(jet_error(jstat));
} WsbCatch(hr);
if (NULL != backup_path) {
WsbFree(backup_path);
}
if (NULL != restore_path) {
WsbFree(restore_path);
}
WsbTraceOut(OLESTR("CWsbDbSys::Restore"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
return(hr);
}
HRESULT
CWsbDbSys::IncrementChangeCount(
void
)
/*++
Implements:
IWsbDbSysPriv::IncrementChangeCount
Routine Description:
Increments the write count used by AutoBackup.
Arguments:
None.
Return Value:
S_OK
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CWsbDbSys::IncrementChangeCount"),
OLESTR("count = %ld"), m_ChangeCount);
try {
m_ChangeCount++;
GetSystemTimeAsFileTime(&m_LastChange);
} WsbCatch(hr);
WsbTraceOut(OLESTR("CWsbDbSys::IncrementChangeCount"),
OLESTR("count = %ld"), m_ChangeCount);
return(hr);
}
HRESULT
CWsbDbSys::DbAttachedAdd(
OLECHAR* name,
BOOL attach)
/*++
Implements:
IWsbDbSysPriv::DbAttachedAdd
Routine Description:
Make sure DB is attached and update the last-used count.
--*/
{
HRESULT hr = S_OK;
char* jet_name = NULL;
WsbTraceIn(OLESTR("CWsbDbSys::DbAttachedAdd"), OLESTR("name = %ls, attach = %ls"),
name, WsbBoolAsString(attach));
try {
int i;
int i_empty = -1;
int i_found = -1;
LONG min_count = AttachedCount + 1;
CWsbStringPtr match_name;
WsbAssert(name, E_POINTER);
// Make sure the list is initialized
if (!AttachedInit) {
WsbAffirmHr(DbAttachedInit());
}
// Convert the name
WsbAffirmHr(wsb_db_jet_fix_path(name, L"." IDB_DB_FILE_SUFFIX, &jet_name));
match_name = jet_name;
// See if it's already in the list; look for an empty slot; find the
// least-recently used DB
EnterCriticalSection(&AttachedCritSect);
for (i = 0; i < MAX_ATTACHED_DB; i++) {
// Empty slot?
if (!Attached[i].Name) {
if (-1 == i_empty) {
// Save the first one found
i_empty = i;
}
} else {
// Gather some data for later
if (Attached[i].LastOpen < min_count) {
min_count = Attached[i].LastOpen;
}
// Already in list?
if (match_name.IsEqual(Attached[i].Name)) {
i_found = i;
}
}
}
// Make sure the count isn't going to overflow
if (LONG_MAX == AttachedCount + 1) {
WsbAffirm(0 < min_count, E_FAIL);
// Adjust counts down to avoid overflow
for (i = 0; i < MAX_ATTACHED_DB; i++) {
if (min_count <= Attached[i].LastOpen) {
Attached[i].LastOpen -= min_count;
}
}
AttachedCount -= min_count;
}
AttachedCount++;
// If it's already in the list, update the info
if (-1 != i_found) {
WsbTrace(OLESTR("CWsbDbSys::DbAttachedAdd: i_found = %d\n"), i_found);
Attached[i_found].LastOpen = AttachedCount;
// If there's an empty slot, use it
} else if (-1 != i_empty) {
WsbTrace(OLESTR("CWsbDbSys::DbAttachedAdd: i_empty = %d\n"), i_empty);
if (attach) {
JET_ERR jstat;
JET_SESID sid;
WsbAffirm(m_pWsbDbSession, WSB_E_NOT_INITIALIZED);
CComQIPtr<IWsbDbSessionPriv, &IID_IWsbDbSessionPriv> pSessionPriv = m_pWsbDbSession;
WsbAffirmPointer(pSessionPriv);
WsbAffirmHr(pSessionPriv->GetJetId(&sid));
jstat = JetAttachDatabase(sid, jet_name, 0);
if (JET_errFileNotFound == jstat) {
WsbThrow(STG_E_FILENOTFOUND);
} else if (JET_wrnDatabaseAttached == jstat) {
WsbTrace(OLESTR("CWsbDbSys::DbAttachedAdd: DB is already attached\n"));
// No problem
} else {
WsbAffirmHr(jet_error(jstat));
}
}
Attached[i_empty].Name = match_name;
Attached[i_empty].LastOpen = AttachedCount;
// Try to detach the oldest DB first
} else {
WsbAffirmHr(DbAttachedEmptySlot());
WsbAffirmHr(DbAttachedAdd(name, attach));
}
} WsbCatch(hr);
if (jet_name) {
WsbFree(jet_name);
}
LeaveCriticalSection(&AttachedCritSect);
WsbTraceOut(OLESTR("CWsbDbSys::DbAttachedAdd"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
return(hr);
}
HRESULT
CWsbDbSys::DbAttachedEmptySlot(
void)
/*++
Implements:
IWsbDbSysPriv::DbAttachedEmptySlot
Routine Description:
Force an empty slot in the attached list even if this means detaching a DB.
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CWsbDbSys::DbAttachedEmptySlot"), OLESTR(""));
// Don't worry about it if we're not initialized yet --
// all the slots are empty
if (AttachedInit) {
EnterCriticalSection(&AttachedCritSect);
try {
BOOL has_empty = FALSE;
int i;
int i_oldest;
LONG oldest_count;
// Find an empty slot or the oldest that is not currently open
reloop:
i_oldest = -1;
oldest_count = AttachedCount;
for (i = 0; i < MAX_ATTACHED_DB; i++) {
if (!Attached[i].Name) {
has_empty = TRUE;
break;
} else if (Attached[i].LastOpen < oldest_count) {
i_oldest = i;
oldest_count = Attached[i].LastOpen;
}
}
// If there's no empty slot, try detaching the oldest
WsbTrace(OLESTR("CWsbDbSys::DbAttachedEmptySlot: has_empty = %ls, i = %d, i_oldest = %d\n"),
WsbBoolAsString(has_empty), i, i_oldest);
if (!has_empty) {
JET_ERR jstat;
char* name;
JET_SESID sid;
WsbAffirm(m_pWsbDbSession, WSB_E_NOT_INITIALIZED);
CComQIPtr<IWsbDbSessionPriv, &IID_IWsbDbSessionPriv> pSessionPriv = m_pWsbDbSession;
WsbAffirmPointer(pSessionPriv);
WsbAffirmHr(pSessionPriv->GetJetId(&sid));
WsbAffirm(-1 != i_oldest, WSB_E_IDB_TOO_MANY_DB);
WsbAffirmHr(wsb_db_jet_fix_path(Attached[i_oldest].Name, L"." IDB_DB_FILE_SUFFIX, &name));
jstat = JetDetachDatabase(sid, name);
WsbFree(name);
WsbTrace(OLESTR("CWsbDbSys::DbAttachedEmptySlot: JetDetachDatabase = %ld\n"),
(LONG)jstat);
if (JET_errDatabaseInUse == jstat) {
WsbTrace(OLESTR("CWsbDbSys::DbAttachedEmptySlot: DB in use; try again\n"));
Attached[i_oldest].LastOpen = AttachedCount;
goto reloop;
} else if (JET_errDatabaseNotFound != jstat) {
WsbAffirmHr(jet_error(jstat));
}
Attached[i_oldest].Name.Free();
Attached[i_oldest].LastOpen = 0;
}
} WsbCatch(hr);
LeaveCriticalSection(&AttachedCritSect);
}
WsbTraceOut(OLESTR("CWsbDbSys::DbAttachedEmptySlot"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
return(hr);
}
HRESULT
CWsbDbSys::DbAttachedInit(
void)
/*++
Implements:
IWsbDbSysPriv::DbAttachedInit
Routine Description:
Initialize the attached-DB-list data.
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CWsbDbSys::DbAttachedInit"), OLESTR(""));
EnterCriticalSection(&AttachedCritSect);
try {
if (!AttachedInit) {
ULONG actual = 0;
int i;
JET_ERR jstat;
JET_SESID sid;
WsbAffirm(m_pWsbDbSession, WSB_E_NOT_INITIALIZED);
CComQIPtr<IWsbDbSessionPriv, &IID_IWsbDbSessionPriv> pSessionPriv = m_pWsbDbSession;
WsbAffirmPointer(pSessionPriv);
WsbAffirmHr(pSessionPriv->GetJetId(&sid));
// Initialize data
for (i = 0; i < MAX_ATTACHED_DB; i++) {
Attached[i].Name.Free();
Attached[i].LastOpen = 0;
}
// Make sure there aren't pre-attached DBs
jstat = JetDetachDatabase(sid, NULL);
WsbTrace(OLESTR("CWsbDbSys::DbAttachedInit: JetDetachDatabase(NULL) = %ld\n"), (LONG)jstat);
WsbAffirmHr(jet_error(jstat));
AttachedInit = TRUE;
}
} WsbCatch(hr);
LeaveCriticalSection(&AttachedCritSect);
WsbTraceOut(OLESTR("CWsbDbSys::DbAttachedInit"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
return(hr);
}
HRESULT
CWsbDbSys::DbAttachedRemove(
OLECHAR* name)
/*++
Implements:
IWsbDbSysPriv::DbAttachedRemove
Routine Description:
Detach a DB (if attached).
--*/
{
HRESULT hr = S_FALSE;
char* jet_name = NULL;
WsbTraceIn(OLESTR("CWsbDbSys::DbAttachedRemove"), OLESTR("name = %ls"),
name);
try {
int i;
CWsbStringPtr match_name;
WsbAssert(name, E_POINTER);
WsbAffirm(AttachedInit, S_FALSE);
// Convert the name
WsbAffirmHr(wsb_db_jet_fix_path(name, L"." IDB_DB_FILE_SUFFIX, &jet_name));
match_name = jet_name;
// See if it's in the list
EnterCriticalSection(&AttachedCritSect);
for (i = 0; i < MAX_ATTACHED_DB; i++) {
if (Attached[i].Name) {
if (match_name.IsEqual(Attached[i].Name)) {
JET_ERR jstat;
JET_SESID sid;
WsbTrace(OLESTR("CWsbDbSys::DbAttachedRemove: found DB, index = %d\n"), i);
WsbAffirm(m_pWsbDbSession, WSB_E_NOT_INITIALIZED);
CComQIPtr<IWsbDbSessionPriv, &IID_IWsbDbSessionPriv> pSessionPriv = m_pWsbDbSession;
WsbAffirmPointer(pSessionPriv);
WsbAffirmHr(pSessionPriv->GetJetId(&sid));
jstat = JetDetachDatabase(sid, jet_name);
WsbTrace(OLESTR("CWsbDbSys::DbAttachedRemove: JetDetachDatabase = %ld\n"),
(LONG)jstat);
if (JET_errDatabaseNotFound != jstat) {
WsbAffirmHr(jet_error(jstat));
hr = S_OK;
}
Attached[i].Name.Free();
Attached[i].LastOpen = 0;
break;
}
}
}
} WsbCatch(hr);
if (jet_name) {
WsbFree(jet_name);
}
LeaveCriticalSection(&AttachedCritSect);
WsbTraceOut(OLESTR("CWsbDbSys::DbAttachedRemove"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
return(hr);
}
// wsb_db_jet_check_error - check for a jet error; return S_OK for no error;
// print error to trace otherwise
HRESULT wsb_db_jet_check_error(LONG jstat, char *fileName, DWORD lineNo)
{
HRESULT hr = S_OK;
if (jstat != JET_errSuccess) {
WsbTrace(OLESTR("Jet error = %ld (%hs line %ld)\n"), jstat,
fileName, lineNo);
// Convert JET error to IDB error for some common values
switch (jstat) {
case JET_errDiskFull:
case JET_errLogDiskFull:
hr = WSB_E_IDB_DISK_FULL;
break;
case JET_errDatabaseNotFound:
hr = WSB_E_IDB_FILE_NOT_FOUND;
break;
case JET_errDatabaseInconsistent:
case JET_errPageNotInitialized:
case JET_errReadVerifyFailure:
case JET_errDatabaseCorrupted:
case JET_errBadLogSignature:
case JET_errBadDbSignature:
case JET_errBadCheckpointSignature:
case JET_errCheckpointCorrupt:
case JET_errMissingPatchPage:
case JET_errBadPatchPage:
hr = WSB_E_IDB_DATABASE_CORRUPT;
break;
case JET_errWriteConflict:
hr = WSB_E_IDB_UPDATE_CONFLICT;
break;
default:
hr = WSB_E_IDB_IMP_ERROR;
break;
}
// Log this error in the event log
if (g_WsbLogLevel) {
CWsbStringPtr str;
WsbSetEventInfo(fileName, lineNo, VER_PRODUCTBUILD, RS_BUILD_VERSION); \
str = WsbLongAsString(jstat);
if (WSB_E_IDB_IMP_ERROR != hr) {
str.Prepend(" (");
str.Prepend(WsbHrAsString(hr));
str.Append(")");
}
WsbTraceAndLogEvent(WSB_MESSAGE_IDB_ERROR, 0, NULL,
static_cast<OLECHAR *>(str), NULL);
}
}
return(hr);
}
// wsb_db_jet_fix_path - convert database path name from OLESTR to char*,
// change (or add) extension.
// Returns HRESULT
//
// NOTE: OLECHAR* is passed in, but char* is returned
HRESULT
wsb_db_jet_fix_path(OLECHAR* path, OLECHAR* ext, char** new_path)
{
HRESULT hr = S_OK;
try {
CWsbStringPtr string;
int tlen;
WsbAssertPointer(path);
WsbAssertPointer(new_path);
// Add extension if given
string = path;
WsbAffirm(0 != (WCHAR *)string, E_OUTOFMEMORY);
if (ext) {
WsbAffirmHr(AddExtension(&string, ext));
}
// Allocate char string
tlen = (wcslen(string) + 1) * sizeof(OLECHAR);
*new_path = (char*)WsbAlloc(tlen);
WsbAffirm(*new_path, E_OUTOFMEMORY);
// Convert from wide char to char
if (wcstombs(*new_path, string, tlen) == (size_t)-1) {
WsbFree(*new_path);
*new_path = NULL;
WsbThrow(WSB_E_STRING_CONVERSION);
}
} WsbCatch(hr);
return(hr);
}
// Local functions
// AddExtension - add (or replace) the file extension to the path.
// If Ext is NULL, remove the existing extension.
//
// Return S_OK if no errors occurred.
static HRESULT AddExtension(OLECHAR** pPath, OLECHAR* Ext)
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("AddExtension(wsbdbsys)"), OLESTR("Path = \"%ls\", Ext = \"%ls\""),
WsbAbbreviatePath(*pPath, 120), Ext );
try {
int elen;
int len;
OLECHAR* new_path;
OLECHAR* pc;
OLECHAR* pc2;
int tlen;
WsbAssertPointer(pPath);
WsbAssertPointer(*pPath);
// Allocate string and copy path
len = wcslen(*pPath);
if (Ext) {
elen = wcslen(Ext);
} else {
elen = 0;
}
tlen = (len + elen + 1) * sizeof(OLECHAR);
new_path = static_cast<OLECHAR*>(WsbAlloc(tlen));
WsbAffirm(new_path, E_OUTOFMEMORY);
wcscpy(new_path, *pPath);
// Remove old extension (if there)
pc = wcsrchr(new_path, L'.');
pc2 = wcsrchr(new_path, L'\\');
if (pc && (!pc2 || pc2 < pc)) {
*pc = L'\0';
}
// Add the new extension (if given)
if (Ext) {
wcscat(new_path, Ext);
}
// Return the new path
WsbFree(*pPath);
*pPath = new_path;
} WsbCatch(hr);
WsbTraceOut(OLESTR("AddExtension(wsbdbsys)"), OLESTR("hr =<%ls>, new path = \"%ls\""),
WsbHrAsString(hr), WsbAbbreviatePath(*pPath, 120));
return(hr);
}
// ClearDirectory - delete all files in a directory
// Return S_OK if no errors occurred.
static HRESULT ClearDirectory(const OLECHAR* DirPath)
{
DWORD err;
WIN32_FIND_DATA FindData;
HANDLE hFind = 0;
HRESULT hr = S_OK;
int nDeleted = 0;
int nSkipped = 0;
CWsbStringPtr SearchPath;
WsbTraceIn(OLESTR("ClearDirectory(wsbdbsys)"), OLESTR("Path = <%ls>"),
WsbAbbreviatePath(DirPath, 120));
try {
SearchPath = DirPath;
SearchPath.Append("\\*");
hFind = FindFirstFile(SearchPath, &FindData);
if (INVALID_HANDLE_VALUE == hFind) {
hFind = 0;
err = GetLastError();
WsbTrace(OLESTR("ClearDirectory(wsbdbsys): FindFirstFile(%ls) failed, error = %ld\n"),
static_cast<OLECHAR*>(SearchPath), err);
WsbThrow(HRESULT_FROM_WIN32(err));
}
while (TRUE) {
if (FindData.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY |
FILE_ATTRIBUTE_HIDDEN)) {
nSkipped++;
} else {
CWsbStringPtr DeletePath;
DeletePath = DirPath;
DeletePath.Append("\\");
DeletePath.Append(FindData.cFileName);
if (!DeleteFile(DeletePath)) {
err = GetLastError();
WsbTrace(OLESTR("ClearDirectory(wsbdbsys): DeleteFile(%ls) failed, error = %ld\n"),
static_cast<OLECHAR*>(DeletePath), err);
WsbThrow(HRESULT_FROM_WIN32(err));
}
nDeleted++;
}
if (!FindNextFile(hFind, &FindData)) {
err = GetLastError();
if (ERROR_NO_MORE_FILES == err) break;
WsbTrace(OLESTR("ClearDirectory(wsbdbsys): FindNextFile failed, error = %ld\n"),
err);
WsbThrow(HRESULT_FROM_WIN32(err));
}
}
} WsbCatch(hr);
if (0 != hFind) {
FindClose(hFind);
}
WsbTraceOut(OLESTR("ClearDirectory(wsbdbsys)"), OLESTR("hr =<%ls>, # deleted = %d, # skipped = %d"),
WsbHrAsString(hr), nDeleted, nSkipped);
return(hr);
}
// CopyDirectory - copy files from one directory to another
// Return S_OK if no errors occurred.
static HRESULT CopyDirectory(const OLECHAR* DirSource, const OLECHAR* DirTarget)
{
DWORD err;
WIN32_FIND_DATA FindData;
HANDLE hFind = 0;
HRESULT hr = S_OK;
int nCopied = 0;
int nSkipped = 0;
CWsbStringPtr SearchPath;
WsbTraceIn(OLESTR("CopyDirectory(wsbdbsys)"), OLESTR("OldPath = \"%ls\", NewPath = \"%ls\""),
WsbQuickString(WsbAbbreviatePath(DirSource, 120)),
WsbQuickString(WsbAbbreviatePath(DirTarget, 120)));
try {
SearchPath = DirSource;
SearchPath.Append("\\*");
hFind = FindFirstFile(SearchPath, &FindData);
if (INVALID_HANDLE_VALUE == hFind) {
hFind = 0;
err = GetLastError();
WsbTrace(OLESTR("ClearDirectory(wsbdbsys): FindFirstFile(%ls) failed, error = %ld\n"),
static_cast<OLECHAR*>(SearchPath), err);
WsbThrow(HRESULT_FROM_WIN32(err));
}
while (TRUE) {
if (FindData.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY |
FILE_ATTRIBUTE_HIDDEN)) {
nSkipped++;
} else {
CWsbStringPtr NewPath;
CWsbStringPtr OldPath;
OldPath = DirSource;
OldPath.Append("\\");
OldPath.Append(FindData.cFileName);
NewPath = DirTarget;
NewPath.Append("\\");
NewPath.Append(FindData.cFileName);
if (!CopyFile(OldPath, NewPath, FALSE)) {
err = GetLastError();
WsbTrace(OLESTR("ClearDirectory(wsbdbsys): CopyFile(%ls, %ls) failed, error = %ld\n"),
static_cast<OLECHAR*>(OldPath),
static_cast<OLECHAR*>(NewPath), err);
WsbThrow(HRESULT_FROM_WIN32(err));
}
nCopied++;
}
if (!FindNextFile(hFind, &FindData)) {
err = GetLastError();
if (ERROR_NO_MORE_FILES == err) break;
WsbTrace(OLESTR("ClearDirectory(wsbdbsys): FindNextFile failed, error = %ld\n"),
err);
WsbThrow(HRESULT_FROM_WIN32(err));
}
}
} WsbCatch(hr);
if (0 != hFind) {
FindClose(hFind);
}
WsbTraceOut(OLESTR("CopyDirectory(wsbdbsys)"), OLESTR("hr =<%ls>, copied = %ld, skipped = %ld"),
WsbHrAsString(hr), nCopied, nSkipped);
return(hr);
}
// DirectoryHasFullBackup - try to determine if the directory contains a full backup
// Return
// S_OK if it contains a full backup
// S_FALSE if it doesn't
// E_* on errors
//
// The technique use here is somewhat ad hoc since it expects the full backup
// filename to end in IDB_DB_FILE_SUFFIX
static HRESULT DirectoryHasFullBackup(const OLECHAR* DirPath)
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("DirectoryHasFullBackup(wsbdbsys)"), OLESTR("Path = <%ls>"),
WsbAbbreviatePath(DirPath, 120));
try {
ULONG Count;
WsbAffirmHr(FileCount(DirPath, L"*." IDB_DB_FILE_SUFFIX, &Count));
if (0 == Count) {
hr = S_FALSE;
}
} WsbCatch(hr);
WsbTraceOut(OLESTR("DirectoryHasFullBackup(wsbdbsys)"), OLESTR("hr =<%ls>"),
WsbHrAsString(hr));
return(hr);
}
// FileCount - count all files in a directory matching a pattern. Skip
// directories and hidden files.
// Return S_OK if no errors occurred.
static HRESULT FileCount(const OLECHAR* DirPath, const OLECHAR* Pattern,
ULONG* Count)
{
DWORD err;
WIN32_FIND_DATA FindData;
HANDLE hFind = 0;
HRESULT hr = S_OK;
int lCount = 0;
int nSkipped = 0;
CWsbStringPtr SearchPath;
WsbTraceIn(OLESTR("FileCount(wsbdbsys)"), OLESTR("Path = <%ls>"),
WsbAbbreviatePath(DirPath, 120));
try {
SearchPath = DirPath;
SearchPath.Append("\\");
SearchPath.Append(Pattern);
*Count = 0;
hFind = FindFirstFile(SearchPath, &FindData);
if (INVALID_HANDLE_VALUE == hFind) {
hFind = 0;
err = GetLastError();
if (ERROR_FILE_NOT_FOUND == err) WsbThrow(S_OK);
WsbTrace(OLESTR("FileCount(wsbdbsys): FindFirstFile(%ls) failed, error = %ld\n"),
static_cast<OLECHAR*>(SearchPath), err);
WsbThrow(HRESULT_FROM_WIN32(err));
}
while (TRUE) {
if (FindData.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY |
FILE_ATTRIBUTE_HIDDEN)) {
nSkipped++;
} else {
lCount++;
}
if (!FindNextFile(hFind, &FindData)) {
err = GetLastError();
if (ERROR_NO_MORE_FILES == err) break;
WsbTrace(OLESTR("FileCount(wsbdbsys): FindNextFile failed, error = %ld\n"),
err);
WsbThrow(HRESULT_FROM_WIN32(err));
}
}
} WsbCatch(hr);
if (0 != hFind) {
FindClose(hFind);
}
if (S_OK == hr) {
*Count = lCount;
}
WsbTraceOut(OLESTR("FileCount(wsbdbsys)"), OLESTR("hr =<%ls>, # skipped = %d, Count = %ld"),
WsbHrAsString(hr), nSkipped, *Count);
return(hr);
}
// RenameDirectory - rename a directory
// Return S_OK if no errors occurred.
static HRESULT RenameDirectory(const OLECHAR* OldDir, const OLECHAR* NewDir)
{
DWORD err;
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("RenameDirectory(wsbdbsys)"), OLESTR("OldPath = \"%ls\", NewPath = \"%ls\""),
WsbQuickString(WsbAbbreviatePath(OldDir, 120)),
WsbQuickString(WsbAbbreviatePath(NewDir, 120)));
try {
if (!MoveFile(OldDir, NewDir)) {
err = GetLastError();
WsbTrace(OLESTR("RenameDirectory(wsbdbsys): MoveFile failed, error = %ld\n"), err);
WsbThrow(HRESULT_FROM_WIN32(err));
}
} WsbCatch(hr);
WsbTraceOut(OLESTR("RenameDirectory(wsbdbsys)"), OLESTR("hr =<%ls>"),
WsbHrAsString(hr));
return(hr);
}