2814 lines
70 KiB
C++
2814 lines
70 KiB
C++
/******************************************************************************
|
|
*
|
|
* Copyright (c) 2000 Microsoft Corporation
|
|
*
|
|
* Module Name:
|
|
* evthandler.cpp
|
|
*
|
|
* Abstract:
|
|
* CEventHandler class methods
|
|
*
|
|
* Revision History:
|
|
* Brijesh Krishnaswami (brijeshk) 03/17/2000
|
|
* created
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#include "..\rstrcore\resource.h"
|
|
#include "ntservmsg.h"
|
|
|
|
#ifdef THIS_FILE
|
|
#undef THIS_FILE
|
|
#endif
|
|
static char __szTraceSourceFile[] = __FILE__;
|
|
#define THIS_FILE __szTraceSourceFile
|
|
|
|
#define IDLE_STACKSIZE 32768 // 32K stack for idle thread
|
|
|
|
CEventHandler *g_pEventHandler;
|
|
|
|
// constructor
|
|
|
|
CEventHandler::CEventHandler()
|
|
{
|
|
m_hTimerQueue = m_hTimer = NULL;
|
|
m_hIdle = NULL;
|
|
m_fNoRpOnSystem = TRUE;
|
|
m_fIdleSrvStarted = FALSE;
|
|
m_ftFreeze.dwLowDateTime = 0;
|
|
m_ftFreeze.dwHighDateTime = 0;
|
|
m_nNestedCallCount = 0;
|
|
m_hCOMDll = NULL;
|
|
m_hIdleRequestHandle = NULL;
|
|
m_hIdleStartHandle = NULL;
|
|
m_hIdleStopHandle = NULL;
|
|
m_fCreateRpASAP = FALSE;
|
|
}
|
|
|
|
|
|
// destructor
|
|
|
|
CEventHandler::~CEventHandler()
|
|
{
|
|
}
|
|
|
|
|
|
// the RPC API
|
|
|
|
DWORD
|
|
CEventHandler::DisableSRS(LPWSTR pszDrive)
|
|
{
|
|
DWORD dwRc = ERROR_SUCCESS;
|
|
BOOL fHaveLock = FALSE;
|
|
HANDLE hEventSource = NULL;
|
|
|
|
tenter("CEventHandler::DisableSRS");
|
|
|
|
LOCKORLEAVE(fHaveLock);
|
|
|
|
ASSERT(g_pDataStoreMgr && g_pSRConfig);
|
|
|
|
// if whole of SR is disabled, then
|
|
// - set firstrun and cleanup flag to yes
|
|
// - set stop event
|
|
|
|
if (! pszDrive || IsSystemDrive(pszDrive))
|
|
{
|
|
trace(0, "Disabling all of SR");
|
|
|
|
dwRc = SrStopMonitoring(g_pSRConfig->GetFilter());
|
|
if (dwRc != ERROR_SUCCESS)
|
|
{
|
|
trace(0, "! SrStopMonitoring : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
dwRc = g_pSRConfig->SetFirstRun(SR_FIRSTRUN_YES);
|
|
if (dwRc != ERROR_SUCCESS)
|
|
{
|
|
trace(0, "! SetFirstRun : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
g_pDataStoreMgr->DestroyDataStore(NULL);
|
|
if (dwRc != ERROR_SUCCESS)
|
|
{
|
|
trace(0, "! DestroyDataStore : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
// set the filter start to disabled only if this is a
|
|
// real disable
|
|
// if it's a reset, filter needs to start the next boot
|
|
|
|
if (g_pSRConfig->GetResetFlag() == FALSE)
|
|
{
|
|
dwRc = SetServiceStartup(s_cszFilterName, SERVICE_DISABLED);
|
|
if (ERROR_SUCCESS != dwRc)
|
|
{
|
|
trace(0, "! SetServiceStartup : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
// done, we are disabled
|
|
|
|
dwRc = g_pSRConfig->SetDisableFlag(TRUE);
|
|
if (dwRc != ERROR_SUCCESS)
|
|
{
|
|
trace(0, "! SetDisableFlag : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
// set the stop event
|
|
// this will bring us down gracefully
|
|
|
|
SignalStop();
|
|
|
|
if (g_pSRConfig->m_dwTestBroadcast)
|
|
PostTestMessage(g_pSRConfig->m_uiTMDisable, NULL, NULL);
|
|
|
|
// write to event log
|
|
hEventSource = RegisterEventSource(NULL, s_cszServiceName);
|
|
if (hEventSource != NULL)
|
|
{
|
|
SRLogEvent (hEventSource, EVENTLOG_INFORMATION_TYPE, EVMSG_SYSDRIVE_DISABLED,
|
|
NULL, 0, NULL, NULL, NULL);
|
|
DeregisterEventSource(hEventSource);
|
|
}
|
|
|
|
trace(0, "SR disabled");
|
|
}
|
|
else
|
|
{
|
|
trace(0, "Disabling drive %S", pszDrive);
|
|
|
|
// first tell filter to stop monitoring,
|
|
// then build _filelst.cfg and pass down
|
|
|
|
dwRc = g_pDataStoreMgr->MonitorDrive(pszDrive, FALSE);
|
|
if (ERROR_SUCCESS != dwRc)
|
|
{
|
|
trace(0, "! g_pDataStoreMgr->MonitorDrive for %s : %ld", pszDrive, dwRc);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
UNLOCK(fHaveLock);
|
|
tleave();
|
|
return dwRc;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
CEventHandler::EnableSRS(LPWSTR pszDrive)
|
|
{
|
|
tenter("CEventHandler::EnableSRS");
|
|
BOOL fHaveLock = FALSE;
|
|
DWORD dwRc = ERROR_SUCCESS;
|
|
|
|
LOCKORLEAVE(fHaveLock);
|
|
|
|
trace(0, "EnableSRS");
|
|
|
|
ASSERT(g_pSRConfig);
|
|
|
|
if (! pszDrive || IsSystemDrive(pszDrive))
|
|
{
|
|
//
|
|
// if safe mode, then don't
|
|
//
|
|
|
|
if (TRUE == g_pSRConfig->GetSafeMode())
|
|
{
|
|
DebugTrace(0, "Cannot enable SR in safemode");
|
|
dwRc = ERROR_BAD_ENVIRONMENT;
|
|
goto done;
|
|
}
|
|
|
|
// system drive
|
|
|
|
g_pSRConfig->SetDisableFlag(FALSE);
|
|
|
|
dwRc = SetServiceStartup(s_cszFilterName, SERVICE_BOOT_START);
|
|
if (ERROR_SUCCESS != dwRc)
|
|
{
|
|
trace(0, "! SetServiceStartup : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
dwRc = SetServiceStartup(s_cszServiceName, SERVICE_AUTO_START);
|
|
if (ERROR_SUCCESS != dwRc)
|
|
{
|
|
trace(0, "! SetServiceStartup : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(g_pDataStoreMgr);
|
|
|
|
// build _filelst.cfg and pass down
|
|
|
|
dwRc = g_pDataStoreMgr->MonitorDrive(pszDrive, TRUE);
|
|
if (ERROR_SUCCESS != dwRc)
|
|
{
|
|
trace(0, "! g_pDataStoreMgr->MonitorDrive for %s : %ld", pszDrive, dwRc);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
UNLOCK(fHaveLock);
|
|
tleave();
|
|
return dwRc;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
CEventHandler::DisableFIFOS(DWORD dwRPNum)
|
|
{
|
|
tenter("CEventHandler::DisableFIFOS");
|
|
BOOL fHaveLock = FALSE;
|
|
DWORD dwRc = ERROR_SUCCESS;
|
|
|
|
LOCKORLEAVE(fHaveLock);
|
|
|
|
ASSERT(g_pSRConfig);
|
|
|
|
g_pSRConfig->SetFifoDisabledNum(dwRPNum);
|
|
trace(0, "Disabled FIFO from RP%ld", dwRPNum);
|
|
|
|
done:
|
|
UNLOCK(fHaveLock);
|
|
tleave();
|
|
return dwRc;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
CEventHandler::EnableFIFOS()
|
|
{
|
|
tenter("CEventHandler::EnableFIFOS");
|
|
BOOL fHaveLock = FALSE;
|
|
DWORD dwRc = ERROR_SUCCESS;
|
|
|
|
LOCKORLEAVE(fHaveLock);
|
|
|
|
ASSERT(g_pSRConfig);
|
|
|
|
g_pSRConfig->SetFifoDisabledNum(0);
|
|
trace(0, "Reenabled FIFO");
|
|
|
|
done:
|
|
UNLOCK(fHaveLock);
|
|
tleave();
|
|
return dwRc;
|
|
}
|
|
|
|
|
|
// API and internal method to create a new restore point -
|
|
// this will ask filter to create a restore point folder,
|
|
// take the system snapshot, and write the restore point log
|
|
|
|
BOOL
|
|
CEventHandler::SRSetRestorePointS(
|
|
PRESTOREPOINTINFOW pRPInfo,
|
|
PSTATEMGRSTATUS pSmgrStatus )
|
|
{
|
|
tenter("CEventHandler::SRSetRestorePointS");
|
|
|
|
DWORD dwRc = ERROR_SUCCESS;
|
|
WCHAR szRPDir[MAX_RP_PATH];
|
|
DWORD dwRPNum = 1;
|
|
BOOL fHaveLock = FALSE;
|
|
HKEY hKey = NULL;
|
|
CRestorePoint rpLast;
|
|
BOOL fSnapshot = TRUE;
|
|
DWORD dwSaveType;
|
|
BOOL fUpdateMonitoredList = FALSE;
|
|
DWORD dwSnapshotResult = ERROR_SUCCESS;
|
|
BOOL fSerialized;
|
|
|
|
|
|
if (! pRPInfo || ! pSmgrStatus)
|
|
{
|
|
trace(0, "Invalid arguments");
|
|
dwRc = ERROR_INVALID_DATA;
|
|
goto done;
|
|
}
|
|
|
|
if (pRPInfo->dwRestorePtType > MAX_RPT)
|
|
{
|
|
trace(0, "Restore point type out of valid range");
|
|
dwRc = ERROR_INVALID_DATA;
|
|
goto done;
|
|
}
|
|
|
|
if (pRPInfo->dwEventType < MIN_EVENT ||
|
|
pRPInfo->dwEventType > MAX_EVENT)
|
|
{
|
|
trace(0, "Event type out of valid range");
|
|
dwRc = ERROR_INVALID_DATA;
|
|
goto done;
|
|
}
|
|
|
|
LOCKORLEAVE(fHaveLock);
|
|
|
|
ASSERT(g_pDataStoreMgr && g_pSRConfig);
|
|
|
|
//
|
|
// special processing for FIRSTRUN checkpoint
|
|
//
|
|
|
|
if (pRPInfo->dwRestorePtType == FIRSTRUN)
|
|
{
|
|
// first remove the Run key if it exists
|
|
// the function run from the Run entry in srclient.dll may not have been
|
|
// able to delete itself if it was run in non-admin context
|
|
// so we will make sure we delete it here
|
|
|
|
HKEY hKey;
|
|
if (ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE,
|
|
L"Software\\Microsoft\\Windows\\CurrentVersion\\Run",
|
|
&hKey))
|
|
{
|
|
RegDeleteValue(hKey, L"SRFirstRun");
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
|
|
// if this is really the first checkpoint
|
|
// then allow it no matter who's trying to create it
|
|
// if not, then bail
|
|
|
|
if (m_fNoRpOnSystem == FALSE)
|
|
{
|
|
trace(0, "Trying to create FirstRun rp when an rp already exists");
|
|
dwRc = ERROR_ALREADY_EXISTS;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
//
|
|
// if this is a restore restore point or system checkpoint,
|
|
// then erase any nested rp context
|
|
// this will make sure that restore can happen
|
|
// even if some erratic client failed to call END_NESTED
|
|
//
|
|
|
|
if (pRPInfo->dwRestorePtType == RESTORE ||
|
|
pRPInfo->dwRestorePtType == CHECKPOINT ||
|
|
pRPInfo->dwRestorePtType == FIRSTRUN)
|
|
{
|
|
trace(0, "Resetting nested refcount to 0");
|
|
m_nNestedCallCount = 0;
|
|
}
|
|
|
|
|
|
//
|
|
// get the current rp number
|
|
// dwRPNum will be overwritten if a new restore point is created
|
|
// after all the prelim checks
|
|
//
|
|
|
|
dwRPNum = (m_fNoRpOnSystem == FALSE) ? m_CurRp.GetNum() : 0;
|
|
|
|
|
|
//
|
|
// if this is a nested call
|
|
// then don't create nested rps
|
|
//
|
|
|
|
if (pRPInfo->dwEventType == END_NESTED_SYSTEM_CHANGE)
|
|
{
|
|
// adjust refcount only if called for the current restore point
|
|
|
|
if (pRPInfo->llSequenceNumber == 0 ||
|
|
pRPInfo->llSequenceNumber == dwRPNum)
|
|
{
|
|
dwRc = ERROR_SUCCESS;
|
|
if (m_nNestedCallCount > 0)
|
|
m_nNestedCallCount--;
|
|
}
|
|
else if (pRPInfo->llSequenceNumber < dwRPNum)
|
|
{
|
|
dwRc = ERROR_SUCCESS;
|
|
trace(0, "END_NESTED called for older rp - not adjusting refcount");
|
|
}
|
|
else
|
|
{
|
|
dwRc = ERROR_INVALID_DATA;
|
|
trace(0, "END_NESTED called for non-existent rp - not adjusting refcount");
|
|
}
|
|
|
|
if (pRPInfo->dwRestorePtType != CANCELLED_OPERATION)
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
}
|
|
else if (pRPInfo->dwEventType == BEGIN_NESTED_SYSTEM_CHANGE)
|
|
{
|
|
if (m_nNestedCallCount > 0)
|
|
{
|
|
dwRc = ERROR_SUCCESS;
|
|
m_nNestedCallCount++;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
|
|
// check if this is a request to remove restore point
|
|
// provided for backward compat only
|
|
// new clients should use SRRemoveRestorePoint
|
|
|
|
if (pRPInfo->dwEventType == END_SYSTEM_CHANGE ||
|
|
pRPInfo->dwEventType == END_NESTED_SYSTEM_CHANGE)
|
|
{
|
|
if (pRPInfo->dwRestorePtType == CANCELLED_OPERATION)
|
|
{
|
|
dwRc = SRRemoveRestorePointS((DWORD) pRPInfo->llSequenceNumber);
|
|
goto done;
|
|
}
|
|
else
|
|
{
|
|
dwRc = ERROR_SUCCESS;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
// if this is safe mode, don't create restore point
|
|
//
|
|
// however, allow restore UI to be able to create a hidden restore point in safemode
|
|
//
|
|
|
|
if (g_pSRConfig->GetSafeMode() == TRUE)
|
|
{
|
|
if (pRPInfo->dwRestorePtType == CANCELLED_OPERATION)
|
|
{
|
|
// we need this rp only for undo in case of failure
|
|
// so we don't need snapshot (snapshotting will fail in safemode)
|
|
|
|
trace(0, "Restore rp - creating snapshot in safemode");
|
|
}
|
|
else
|
|
{
|
|
trace(0, "Cannot create restore point in safemode");
|
|
dwRc = ERROR_BAD_ENVIRONMENT;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
//
|
|
// if system drive is frozen,
|
|
// then see if it can be thawed
|
|
// if not, then cannot create rp
|
|
//
|
|
|
|
if (g_pDataStoreMgr->IsDriveFrozen(g_pSRConfig->GetSystemDrive()))
|
|
{
|
|
if (ERROR_SUCCESS != g_pDataStoreMgr->ThawDrives(TRUE))
|
|
{
|
|
trace(0, "Cannot create rp when system drive is frozen");
|
|
dwRc = ERROR_DISK_FULL;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if (hKey)
|
|
RegCloseKey(hKey);
|
|
|
|
// ask filter to create restore point
|
|
// filter will return the restore point number - i for RPi - in dwRPNum
|
|
|
|
dwRc = SrCreateRestorePoint( g_pSRConfig->GetFilter(), &dwRPNum );
|
|
if (ERROR_SUCCESS != dwRc)
|
|
{
|
|
trace(0, "! SrCreateRestorePoint : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
wsprintf( szRPDir, L"%s%ld", s_cszRPDir, dwRPNum );
|
|
|
|
|
|
//
|
|
// update the current restore point object
|
|
// write rp.log with cancelled restorepoint type
|
|
//
|
|
|
|
if (m_fNoRpOnSystem == FALSE)
|
|
{
|
|
rpLast.SetDir(m_CurRp.GetDir());
|
|
}
|
|
|
|
m_CurRp.SetDir(szRPDir);
|
|
dwSaveType = pRPInfo->dwRestorePtType;
|
|
pRPInfo->dwRestorePtType = CANCELLED_OPERATION;
|
|
m_CurRp.Load(pRPInfo);
|
|
dwRc = m_CurRp.WriteLog();
|
|
if ( ERROR_SUCCESS != dwRc )
|
|
{
|
|
trace(0, "! WriteLog : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
// create system snapshot
|
|
// if there is no explicit regkey that disabled it
|
|
|
|
if (fSnapshot)
|
|
{
|
|
WCHAR szFullPath[MAX_PATH];
|
|
CSnapshot Snapshot;
|
|
|
|
if (m_hCOMDll == NULL)
|
|
{
|
|
m_hCOMDll = LoadLibrary(s_cszCOMDllName);
|
|
|
|
if (NULL == m_hCOMDll)
|
|
{
|
|
dwRc = GetLastError();
|
|
trace(0, "LoadLibrary of %S failed ec=%d", s_cszCOMDllName, dwRc);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
// BUGBUG - this does not seem to make any difference
|
|
// so remove it
|
|
#if 0
|
|
if (FALSE == SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL))
|
|
{
|
|
trace(0, "! SetThreadPriority first");
|
|
}
|
|
#endif
|
|
|
|
if (dwSaveType == RESTORE ||
|
|
dwSaveType == CANCELLED_OPERATION)
|
|
{
|
|
fSerialized = TRUE;
|
|
trace(0, "Setting fSerialized to TRUE");
|
|
}
|
|
else
|
|
{
|
|
fSerialized = FALSE;
|
|
trace(0, "Setting fSerialized to FALSE");
|
|
}
|
|
|
|
MakeRestorePath (szFullPath, g_pSRConfig->GetSystemDrive(), szRPDir);
|
|
dwRc = Snapshot.CreateSnapshot(szFullPath,
|
|
m_hCOMDll,
|
|
m_fNoRpOnSystem ? NULL : rpLast.GetDir(),
|
|
fSerialized);
|
|
|
|
#if 0
|
|
if (FALSE == SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL))
|
|
{
|
|
trace(0, "! SetThreadPriority second");
|
|
}
|
|
#endif
|
|
dwSnapshotResult = dwRc;
|
|
}
|
|
|
|
|
|
// ask the datastoremgr to persist drivetable for old restore point
|
|
// and reset per-rp flags for the new restore point
|
|
|
|
dwRc = g_pDataStoreMgr->SwitchRestorePoint(m_fNoRpOnSystem ? NULL : &rpLast);
|
|
if (dwRc != ERROR_SUCCESS)
|
|
{
|
|
trace(0, "! SwitchRestorePoint : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
m_fNoRpOnSystem = FALSE;
|
|
|
|
|
|
//
|
|
// restore point is fully created
|
|
// write rp.log again
|
|
// this time with the real restorepoint type
|
|
//
|
|
|
|
if (dwSnapshotResult == ERROR_SUCCESS)
|
|
{
|
|
pRPInfo->dwRestorePtType = dwSaveType;
|
|
m_CurRp.Load(pRPInfo);
|
|
dwRc = m_CurRp.WriteLog();
|
|
if ( ERROR_SUCCESS != dwRc )
|
|
{
|
|
trace(0, "! WriteLog : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
trace(0, "****Created %S %S****", szRPDir, pRPInfo->szDescription);
|
|
}
|
|
else
|
|
{
|
|
trace(0, "****Cancelled %S - snapshot failed", szRPDir);
|
|
}
|
|
|
|
|
|
// if drives need to be thawed, then recreate blob
|
|
// and deactivate thaw timer
|
|
|
|
if ( TRUE == g_pDataStoreMgr->IsDriveFrozen(NULL) )
|
|
{
|
|
if (ERROR_SUCCESS == g_pDataStoreMgr->ThawDrives(FALSE))
|
|
{
|
|
m_ftFreeze.dwLowDateTime = 0;
|
|
m_ftFreeze.dwHighDateTime = 0;
|
|
fUpdateMonitoredList = TRUE;
|
|
}
|
|
else
|
|
{
|
|
dwRc = ERROR_DISK_FULL;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
// Also update the filter monitored list blob if this is an idle
|
|
// time restore point or if this is the first run restore
|
|
// point. We update the monitored list at first run since the
|
|
// initial blob is created before the first user logs on to the
|
|
// machine and before the first user's profile exists. So we want
|
|
// to update rhe monitored list at first run since by now the
|
|
// user's profile has been created.
|
|
|
|
if (fUpdateMonitoredList ||
|
|
(pRPInfo->dwRestorePtType == CHECKPOINT) ||
|
|
(pRPInfo->dwRestorePtType == FIRSTRUN) )
|
|
{
|
|
dwRc = SRUpdateMonitoredListS(NULL);
|
|
}
|
|
|
|
|
|
//
|
|
// if rp creation succeeded,
|
|
// and this is the outermost nested call
|
|
// then bump refcount to 1
|
|
//
|
|
|
|
if (dwRc == ERROR_SUCCESS &&
|
|
pRPInfo->dwEventType == BEGIN_NESTED_SYSTEM_CHANGE)
|
|
{
|
|
m_nNestedCallCount = 1;
|
|
}
|
|
|
|
//
|
|
// send thaw complete test message
|
|
//
|
|
|
|
if (fUpdateMonitoredList)
|
|
{
|
|
if (g_pSRConfig->m_dwTestBroadcast)
|
|
PostTestMessage(g_pSRConfig->m_uiTMThaw, NULL, NULL);
|
|
}
|
|
|
|
|
|
// if WMI is serialized, then check fifo conditions here
|
|
// else this would happen in DoWMISnapshot
|
|
|
|
if (fSerialized)
|
|
{
|
|
g_pDataStoreMgr->TriggerFreezeOrFifo();
|
|
}
|
|
|
|
done:
|
|
trace(0, "Nest level : %d", m_nNestedCallCount);
|
|
|
|
if (dwSnapshotResult != ERROR_SUCCESS)
|
|
dwRc = dwSnapshotResult;
|
|
|
|
// populate return struct
|
|
|
|
if (pSmgrStatus)
|
|
{
|
|
pSmgrStatus->nStatus = dwRc;
|
|
pSmgrStatus->llSequenceNumber = (INT64) dwRPNum;
|
|
}
|
|
|
|
UNLOCK( fHaveLock );
|
|
tleave();
|
|
return ( dwRc == ERROR_SUCCESS ) ? TRUE : FALSE;
|
|
}
|
|
|
|
|
|
// this api is provided to remove a restore point
|
|
// removing a restore point simply takes away the ability to restore
|
|
// to this point - all the changes in this restore point are preserved
|
|
|
|
DWORD
|
|
CEventHandler::SRRemoveRestorePointS(
|
|
DWORD dwRPNum)
|
|
{
|
|
tenter("CEventHandler::SRRemoveRestorePointS");
|
|
|
|
BOOL fHaveLock = FALSE;
|
|
WCHAR szRPDir[MAX_PATH];
|
|
WCHAR szFullPath[MAX_PATH];
|
|
DWORD dwRc = ERROR_SUCCESS;
|
|
CSnapshot Snapshot;
|
|
CRestorePoint rp;
|
|
CDataStore *pds = NULL;
|
|
INT64 llOld, llNew;
|
|
|
|
if (dwRPNum < 1)
|
|
{
|
|
dwRc = ERROR_INVALID_DATA;
|
|
goto done;
|
|
}
|
|
|
|
LOCKORLEAVE(fHaveLock);
|
|
|
|
ASSERT(g_pSRConfig);
|
|
|
|
// if there is no rp, then no-op
|
|
|
|
if (m_fNoRpOnSystem)
|
|
{
|
|
dwRc = ERROR_INVALID_DATA;
|
|
goto done;
|
|
}
|
|
|
|
|
|
wsprintf(szRPDir, L"%s%ld", s_cszRPDir, dwRPNum);
|
|
|
|
// read the restore point log
|
|
|
|
rp.SetDir(szRPDir);
|
|
dwRc = rp.ReadLog();
|
|
if (ERROR_SUCCESS != dwRc)
|
|
{
|
|
trace(0, "! rp.ReadLog : %ld", dwRc);
|
|
dwRc = ERROR_INVALID_DATA;
|
|
goto done;
|
|
}
|
|
|
|
// delete snapshot
|
|
|
|
MakeRestorePath (szFullPath, g_pSRConfig->GetSystemDrive(), szRPDir);
|
|
dwRc = Snapshot.DeleteSnapshot(szFullPath);
|
|
if (dwRc != ERROR_SUCCESS)
|
|
goto done;
|
|
|
|
|
|
// cancel this restore point
|
|
|
|
rp.Cancel();
|
|
|
|
//
|
|
// adjust the restorepointsize file
|
|
// and the in-memory counters in the service
|
|
//
|
|
|
|
pds = g_pDataStoreMgr->GetDriveTable()->FindSystemDrive();
|
|
if (! pds)
|
|
{
|
|
trace(0, "! FindSystemDrive");
|
|
goto done;
|
|
}
|
|
|
|
llOld = 0;
|
|
dwRc = rp.ReadSize(g_pSRConfig->GetSystemDrive(), &llOld);
|
|
if (dwRc != ERROR_SUCCESS)
|
|
{
|
|
trace(0, "! rp.ReadSize : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
llNew = 0;
|
|
dwRc = pds->CalculateRpUsage(&rp, &llNew, TRUE, FALSE);
|
|
if (dwRc != ERROR_SUCCESS)
|
|
{
|
|
trace(0, "! CalculateRpUsage : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
trace(0, "llOld = %I64d, llNew = %I64d", llOld, llNew);
|
|
|
|
//
|
|
// now update the correct variable in the correct object
|
|
//
|
|
pds->UpdateDataStoreUsage (llNew - llOld, rp.GetNum() == m_CurRp.GetNum());
|
|
|
|
done:
|
|
UNLOCK(fHaveLock);
|
|
tleave();
|
|
return dwRc;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
CEventHandler::SRUpdateMonitoredListS(
|
|
LPWSTR pszXMLFile)
|
|
{
|
|
tenter("CEventHandler::SRUpdateMonitoredListS");
|
|
DWORD dwRc = ERROR_INTERNAL_ERROR;
|
|
BOOL fHaveLock = FALSE;
|
|
|
|
LOCKORLEAVE(fHaveLock);
|
|
|
|
ASSERT(g_pDataStoreMgr && g_pSRConfig);
|
|
|
|
// convert xml to blob
|
|
|
|
dwRc = XmlToBlob(pszXMLFile);
|
|
if (ERROR_SUCCESS != dwRc)
|
|
goto done;
|
|
|
|
// reload to filter
|
|
|
|
dwRc = SrReloadConfiguration(g_pSRConfig->GetFilter());
|
|
if (ERROR_SUCCESS != dwRc)
|
|
{
|
|
trace(0, "! SrReloadConfiguration : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
trace(0, "****Reloaded config file****");
|
|
|
|
done:
|
|
UNLOCK(fHaveLock);
|
|
tleave();
|
|
return dwRc;
|
|
}
|
|
|
|
|
|
DWORD
|
|
CEventHandler::SRUpdateDSSizeS(LPWSTR pwszVolumeGuid, UINT64 ullSizeLimit)
|
|
{
|
|
tenter("CEventHandler::SRUpdateDSSizeS");
|
|
|
|
UINT64 ullTemp;
|
|
DWORD dwRc = ERROR_SUCCESS;
|
|
CDataStore *pds = NULL;
|
|
BOOL fHaveLock = FALSE;
|
|
BOOL fSystem;
|
|
|
|
LOCKORLEAVE(fHaveLock);
|
|
|
|
ASSERT(g_pDataStoreMgr);
|
|
|
|
pds = g_pDataStoreMgr->GetDriveTable()->FindDriveInTable(pwszVolumeGuid);
|
|
if (! pds)
|
|
{
|
|
trace(0, "Volume not in drivetable : %S", pwszVolumeGuid);
|
|
dwRc = ERROR_INVALID_DRIVE;
|
|
goto done;
|
|
}
|
|
|
|
fSystem = pds->GetFlags() & SR_DRIVE_SYSTEM;
|
|
if (ullSizeLimit < (g_pSRConfig ? g_pSRConfig->GetDSMin(fSystem) :
|
|
(fSystem ? SR_DEFAULT_DSMIN:SR_DEFAULT_DSMIN_NONSYSTEM)
|
|
* MEGABYTE))
|
|
{
|
|
trace(0, "SRUpdateDSSizeS %I64d less than dwDSMin", ullSizeLimit);
|
|
dwRc = ERROR_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
|
|
ullTemp = pds->GetSizeLimit(); // save previous size
|
|
pds->SetSizeLimit(0); // reset the datastore size
|
|
pds->UpdateDiskFree (NULL); // calculate the default size
|
|
|
|
if (ullSizeLimit > pds->GetSizeLimit())
|
|
{
|
|
pds->SetSizeLimit (ullTemp);
|
|
trace(0, "SRUpdateDSSizeS %I64d greater than limit", ullSizeLimit);
|
|
dwRc = ERROR_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
|
|
pds->SetSizeLimit(ullSizeLimit);
|
|
|
|
g_pDataStoreMgr->GetDriveTable()->SaveDriveTable((CRestorePoint *) NULL);
|
|
|
|
//
|
|
// this might change fifo conditions
|
|
// so check and trigger fifo if necessary
|
|
//
|
|
|
|
g_pDataStoreMgr->TriggerFreezeOrFifo();
|
|
|
|
done:
|
|
UNLOCK(fHaveLock);
|
|
tleave();
|
|
return dwRc;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
CEventHandler::SRSwitchLogS()
|
|
{
|
|
tenter("CEventHandler::SRSwitchLogS");
|
|
|
|
DWORD dwRc = ERROR_SUCCESS;
|
|
BOOL fHaveLock;
|
|
|
|
LOCKORLEAVE(fHaveLock);
|
|
|
|
ASSERT(g_pSRConfig);
|
|
|
|
dwRc = SrSwitchAllLogs(g_pSRConfig->GetFilter());
|
|
if (ERROR_SUCCESS != dwRc)
|
|
trace(0, "! SrSwitchLog : %ld", dwRc);
|
|
|
|
done:
|
|
UNLOCK(fHaveLock);
|
|
tleave();
|
|
return dwRc;
|
|
}
|
|
|
|
|
|
DWORD
|
|
CEventHandler::XmlToBlob(LPWSTR pszwXml)
|
|
{
|
|
tenter("CEventHandler::XmlToBlob");
|
|
|
|
DWORD dwRc = ERROR_INTERNAL_ERROR;
|
|
WCHAR szwDat[MAX_PATH], szwXml[MAX_PATH];
|
|
CFLDatBuilder FLDatBuilder;
|
|
|
|
ASSERT(g_pSRConfig);
|
|
|
|
MakeRestorePath(szwDat, g_pSRConfig->GetSystemDrive(), s_cszFilelistDat);
|
|
|
|
if (0 == ExpandEnvironmentStrings(s_cszWinRestDir, szwXml, sizeof(szwXml) / sizeof(WCHAR)))
|
|
{
|
|
dwRc = GetLastError();
|
|
trace(0, "! ExpandEnvironmentStrings");
|
|
goto done;
|
|
}
|
|
lstrcat(szwXml, s_cszFilelistXml);
|
|
|
|
if ( ! pszwXml )
|
|
{
|
|
pszwXml = szwXml;
|
|
}
|
|
|
|
if (FALSE == FLDatBuilder.BuildTree(pszwXml, szwDat))
|
|
{
|
|
trace(0, "! FLDatBuilder.BuildTree");
|
|
goto done;
|
|
}
|
|
|
|
if (pszwXml && pszwXml != szwXml && 0 != lstrcmpi(pszwXml, szwXml))
|
|
{
|
|
// copy the new filelist
|
|
SetFileAttributes(szwXml, FILE_ATTRIBUTE_NORMAL);
|
|
if (FALSE == CopyFile(pszwXml, szwXml, FALSE))
|
|
{
|
|
dwRc = GetLastError();
|
|
trace(0, "! CopyFile : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
// set filelist.xml to be S+H+R
|
|
SetFileAttributes(szwXml, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY);
|
|
dwRc = ERROR_SUCCESS;
|
|
|
|
|
|
done:
|
|
tleave();
|
|
return dwRc;
|
|
}
|
|
|
|
|
|
// SR ACTIONS
|
|
|
|
|
|
DWORD
|
|
CEventHandler::OnFirstRun()
|
|
{
|
|
tenter("CEventHandler::OnFirstRun");
|
|
|
|
DWORD dwRc = ERROR_SUCCESS;
|
|
RESTOREPOINTINFO RPInfo;
|
|
STATEMGRSTATUS SmgrStatus;
|
|
LPSTR pszDat = NULL, pszXml = NULL;
|
|
WCHAR szwDat[MAX_PATH], szwXml[MAX_PATH];
|
|
|
|
trace(0, "Firstrun detected");
|
|
|
|
dwRc = XmlToBlob(NULL);
|
|
if (ERROR_SUCCESS != dwRc)
|
|
goto done;
|
|
|
|
// ask filter to start monitoring
|
|
|
|
dwRc = SrStartMonitoring(g_pSRConfig->GetFilter());
|
|
if (ERROR_SUCCESS != dwRc)
|
|
{
|
|
trace(0, "! SrStartMonitoring : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
// change firstrun in the registry
|
|
|
|
dwRc = g_pSRConfig->SetFirstRun(SR_FIRSTRUN_NO);
|
|
if ( dwRc != ERROR_SUCCESS )
|
|
{
|
|
trace(0, "! g_pSRConfig->SetFirstRun : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
// create firstrun restore point
|
|
|
|
if (! g_pDataStoreMgr->IsDriveFrozen(g_pSRConfig->GetSystemDrive()) &&
|
|
g_pSRConfig->GetCreateFirstRunRp() != 0)
|
|
{
|
|
RPInfo.dwEventType = BEGIN_SYSTEM_CHANGE;
|
|
RPInfo.dwRestorePtType = FIRSTRUN;
|
|
if (ERROR_SUCCESS != SRLoadString(L"srrstr.dll", IDS_SYSTEM_CHECKPOINT_TEXT, RPInfo.szDescription, MAX_PATH))
|
|
{
|
|
trace(0, "Using default hardcoded text");
|
|
lstrcpy(RPInfo.szDescription, s_cszSystemCheckpointName);
|
|
}
|
|
|
|
if ( FALSE == SRSetRestorePointS( &RPInfo, &SmgrStatus ))
|
|
{
|
|
//
|
|
// even if this fails
|
|
// keep the service running
|
|
//
|
|
trace(0, "Cannot create firstrun restore point : %ld", SmgrStatus.nStatus);
|
|
}
|
|
}
|
|
|
|
//
|
|
// in future re-enables, service should create firstrun rp
|
|
//
|
|
|
|
if (g_pSRConfig->m_dwCreateFirstRunRp == 0)
|
|
g_pSRConfig->SetCreateFirstRunRp(TRUE);
|
|
|
|
done:
|
|
tleave();
|
|
return dwRc;
|
|
}
|
|
|
|
|
|
// stuff to do at boot
|
|
// read in all the config values from registry
|
|
// initialize communication with filter
|
|
// call OnFirstRun if necessary
|
|
// setup timer & idle detection
|
|
// start RPC server
|
|
|
|
DWORD
|
|
CEventHandler::OnBoot()
|
|
{
|
|
BOOL fHaveLock = FALSE;
|
|
DWORD dwRc = ERROR_INTERNAL_ERROR;
|
|
BOOL fSendEnableMessage = FALSE;
|
|
DWORD dwFlags;
|
|
|
|
tenter("CEventHandler::OnBoot");
|
|
|
|
dwRc = m_DSLock.Init();
|
|
if (dwRc != ERROR_SUCCESS)
|
|
{
|
|
trace(0, "m_DSLock.Init() : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
LOCKORLEAVE(fHaveLock);
|
|
|
|
// initialize the counter
|
|
|
|
dwRc = m_Counter.Init();
|
|
if ( ERROR_SUCCESS != dwRc )
|
|
{
|
|
trace(0, "! CCounter::Init : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
// read all values from registry
|
|
// create global events
|
|
|
|
g_pSRConfig = new CSRConfig;
|
|
if ( ! g_pSRConfig )
|
|
{
|
|
dwRc = ERROR_NOT_ENOUGH_MEMORY;
|
|
trace(0, "Out of Memory");
|
|
goto done;
|
|
}
|
|
dwRc = g_pSRConfig->Initialize();
|
|
if ( ERROR_SUCCESS != dwRc )
|
|
{
|
|
trace(0, "! g_pSRConfig->Initialize : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
trace(0, "SRBoottask: SRConfig initialized");
|
|
|
|
if ( g_pSRConfig->GetDisableFlag() == TRUE )
|
|
{
|
|
// check if we're forced to enable
|
|
|
|
if ( g_pSRConfig->GetDisableFlag_GroupPolicy() == FALSE )
|
|
{
|
|
dwRc = EnableSRS(NULL);
|
|
if (ERROR_SUCCESS != dwRc)
|
|
{
|
|
trace(0, "! EnableSRS : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// we are not forced to enable
|
|
// so we don't need to check if group policy is not configured or is disabling us
|
|
// since we are disabled anyway
|
|
|
|
trace(0, "SR is disabled - stopping");
|
|
dwRc = ERROR_SERVICE_DISABLED;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
// open the filter handle
|
|
// this will load the filter if not already loaded
|
|
|
|
dwRc = g_pSRConfig->OpenFilter();
|
|
if ( ERROR_SUCCESS != dwRc )
|
|
{
|
|
trace(0, "! g_pSRConfig->OpenFilter : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
trace(0, "SRBoottask: Filter handle opened");
|
|
|
|
//
|
|
// we might do a firstrun if the datastore is corrupted
|
|
// (_filelst.cfg missing)
|
|
// in this case, the filter might be ON
|
|
// turn off the filter
|
|
//
|
|
|
|
if ( g_pSRConfig->GetFirstRun() == SR_FIRSTRUN_YES )
|
|
{
|
|
dwRc = SrStopMonitoring(g_pSRConfig->GetFilter());
|
|
trace(0, "SrStopMonitoring returned : %ld", dwRc);
|
|
}
|
|
|
|
// initialize the datastore
|
|
|
|
g_pDataStoreMgr = new CDataStoreMgr;
|
|
if ( ! g_pDataStoreMgr )
|
|
{
|
|
trace(0, "Out of Memory");
|
|
dwRc = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto done;
|
|
}
|
|
dwRc = g_pDataStoreMgr->Initialize (g_pSRConfig->GetFirstRun() == SR_FIRSTRUN_YES);
|
|
if ( ERROR_SUCCESS != dwRc )
|
|
{
|
|
trace(0, "! g_pDataStore.Initialize : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
trace(0, "SRBoottask: Datastore initialized");
|
|
|
|
// check if we are newly disabled from group policy
|
|
|
|
if ( g_pSRConfig->GetDisableFlag_GroupPolicy() == TRUE &&
|
|
g_pSRConfig->GetDisableFlag() == FALSE )
|
|
{
|
|
DisableSRS (NULL);
|
|
dwRc = ERROR_SERVICE_DISABLED;
|
|
goto done;
|
|
}
|
|
|
|
// check if this is first run
|
|
|
|
if ( g_pSRConfig->GetFirstRun() == SR_FIRSTRUN_YES )
|
|
{
|
|
fSendEnableMessage = TRUE;
|
|
dwRc = OnFirstRun( );
|
|
if ( ERROR_SUCCESS != dwRc )
|
|
{
|
|
trace(0, "! OnFirstRun : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
trace(0, "SRBoottask: FirstRun completed");
|
|
}
|
|
|
|
// remember the latest restore point
|
|
|
|
RefreshCurrentRp(TRUE);
|
|
|
|
if (ERROR_SUCCESS == g_pDataStoreMgr->GetFlags(g_pSRConfig->GetSystemDrive(), &dwFlags))
|
|
{
|
|
if (dwFlags & SR_DRIVE_ERROR)
|
|
{
|
|
// a volume error happened in the last session
|
|
// we should create a restore point at next idle time
|
|
|
|
m_fCreateRpASAP = TRUE;
|
|
trace(0, "Volume error occurred in last session - create rp at next idle");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
trace(0, "! g_pDataStoreMgr->GetFlags()");
|
|
}
|
|
|
|
|
|
// register filter ioctls
|
|
|
|
if (! QueueUserWorkItem(PostFilterIo, (PVOID) MAX_IOCTLS, WT_EXECUTEDEFAULT))
|
|
{
|
|
dwRc = GetLastError();
|
|
trace(0, "! QueueUserWorkItem : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
|
|
// start idle time detection
|
|
|
|
// register idle callback
|
|
|
|
if (FALSE == RegisterWaitForSingleObject(&m_hIdleRequestHandle,
|
|
g_pSRConfig->m_hIdleRequestEvent,
|
|
(WAITORTIMERCALLBACK) IdleRequestCallback,
|
|
NULL,
|
|
g_pSRConfig->m_dwIdleInterval*1000,
|
|
WT_EXECUTEDEFAULT))
|
|
{
|
|
dwRc = GetLastError();
|
|
trace(0, "! RegisterWaitForSingleObject : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
|
|
// now request for idle
|
|
|
|
SetEvent(g_pSRConfig->m_hIdleRequestEvent);
|
|
|
|
|
|
//
|
|
// if there are no mounted drives
|
|
// shell will give us all the notifications
|
|
// so don't start timer thread
|
|
//
|
|
|
|
// BUGBUG - keep this?
|
|
// don't start timer at all
|
|
|
|
// if (FALSE == g_pDataStoreMgr->GetDriveTable()->AnyMountedDrives())
|
|
// {
|
|
g_pSRConfig->m_dwTimerInterval = 0;
|
|
// }
|
|
|
|
// set up timer
|
|
|
|
dwRc = InitTimer();
|
|
if ( ERROR_SUCCESS != dwRc )
|
|
{
|
|
trace(0, "! InitTimer : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
|
|
// start rpc server
|
|
|
|
dwRc = RpcServerStart();
|
|
if (ERROR_SUCCESS != dwRc)
|
|
{
|
|
trace(0, "! RpcServerStart : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
|
|
// all initialization complete
|
|
|
|
SetEvent( g_pSRConfig->m_hSRInitEvent );
|
|
|
|
if (fSendEnableMessage)
|
|
{
|
|
// write to event log
|
|
|
|
HANDLE hEventSource = RegisterEventSource(NULL, s_cszServiceName);
|
|
if (hEventSource != NULL)
|
|
{
|
|
SRLogEvent (hEventSource, EVENTLOG_INFORMATION_TYPE, EVMSG_SYSDRIVE_ENABLED,
|
|
NULL, 0, NULL, NULL, NULL);
|
|
DeregisterEventSource(hEventSource);
|
|
}
|
|
|
|
if (g_pSRConfig->m_dwTestBroadcast)
|
|
PostTestMessage(g_pSRConfig->m_uiTMEnable, NULL, NULL);
|
|
}
|
|
|
|
done:
|
|
UNLOCK(fHaveLock);
|
|
tleave( );
|
|
return dwRc;
|
|
}
|
|
|
|
|
|
// method to shutdown the service gracefully
|
|
|
|
void
|
|
CEventHandler::OnStop()
|
|
{
|
|
DWORD dwRc;
|
|
|
|
tenter("CEventHandler::OnStop");
|
|
|
|
if (g_pSRConfig == NULL)
|
|
{
|
|
trace(0, "g_pSRConfig = NULL");
|
|
goto Err;
|
|
}
|
|
|
|
// stop everything
|
|
// BUGBUG - do we need to take the lock here?
|
|
// since all the stops are blocking in themselves
|
|
// and this has to preempt any running activity,
|
|
// blocking here is not such a good idea
|
|
|
|
|
|
// stop the rpc server
|
|
|
|
RpcServerShutdown();
|
|
trace(0, "SRShutdowntask: RPC server shutdown");
|
|
|
|
// kill the timer and timer queue
|
|
|
|
EndTimer();
|
|
trace(0, "SRShutdownTask: Timer stopped");
|
|
|
|
//
|
|
// blocking calls to unregister idle event callbacks
|
|
//
|
|
if (m_hIdleRequestHandle != NULL)
|
|
{
|
|
if (FALSE == UnregisterWaitEx(m_hIdleRequestHandle, INVALID_HANDLE_VALUE))
|
|
{
|
|
trace(0, "! UnregisterWaitEx : %ld", GetLastError());
|
|
}
|
|
m_hIdleRequestHandle = NULL;
|
|
}
|
|
|
|
if (m_hIdleStartHandle != NULL)
|
|
{
|
|
if (FALSE == UnregisterWaitEx(m_hIdleStartHandle, INVALID_HANDLE_VALUE))
|
|
{
|
|
trace(0, "! UnregisterWaitEx : %ld", GetLastError());
|
|
}
|
|
m_hIdleStartHandle = NULL;
|
|
}
|
|
|
|
if (m_hIdleStopHandle != NULL)
|
|
{
|
|
if (FALSE == UnregisterWaitEx(m_hIdleStopHandle, INVALID_HANDLE_VALUE))
|
|
{
|
|
trace(0, "! UnregisterWaitEx : %ld", GetLastError());
|
|
}
|
|
m_hIdleStopHandle = NULL;
|
|
}
|
|
|
|
|
|
|
|
// we are done with the filter
|
|
|
|
g_pSRConfig->CloseFilter();
|
|
|
|
|
|
trace(0, "Filter handle closed");
|
|
|
|
// wait for any queued user work items and pending IOCTLs to complete
|
|
|
|
m_Counter.WaitForZero();
|
|
trace(0, "SRShutdownTask: Pending ioctls + work items completed");
|
|
|
|
|
|
//
|
|
// free the COM+ db dll
|
|
//
|
|
|
|
if (NULL != m_hCOMDll)
|
|
{
|
|
_VERIFY(TRUE==FreeLibrary(m_hCOMDll));
|
|
m_hCOMDll = NULL;
|
|
}
|
|
|
|
|
|
// kill the datastoremgr
|
|
|
|
if (g_pDataStoreMgr)
|
|
{
|
|
g_pDataStoreMgr->SignalStop();
|
|
delete g_pDataStoreMgr;
|
|
g_pDataStoreMgr = NULL;
|
|
}
|
|
|
|
// kill SRConfig
|
|
|
|
if (g_pSRConfig)
|
|
{
|
|
delete g_pSRConfig;
|
|
g_pSRConfig = NULL;
|
|
}
|
|
|
|
Err:
|
|
tleave();
|
|
return;
|
|
}
|
|
|
|
|
|
DWORD
|
|
CEventHandler::OnFreeze( LPWSTR pszDrive )
|
|
{
|
|
tenter("CEventHandler::OnFreeze");
|
|
|
|
DWORD dwRc = ERROR_INTERNAL_ERROR;
|
|
BOOL fHaveLock;
|
|
|
|
LOCKORLEAVE(fHaveLock);
|
|
|
|
ASSERT(g_pDataStoreMgr);
|
|
|
|
//
|
|
// if drive is already frozen, no-op
|
|
//
|
|
|
|
if (g_pDataStoreMgr->IsDriveFrozen(pszDrive))
|
|
{
|
|
dwRc = ERROR_SUCCESS;
|
|
goto done;
|
|
}
|
|
|
|
dwRc = g_pDataStoreMgr->FreezeDrive( pszDrive );
|
|
if ( ERROR_SUCCESS != dwRc )
|
|
{
|
|
trace(0, "! g_pDataStoreMgr->FreezeDrive : %ld", dwRc);
|
|
}
|
|
|
|
done:
|
|
UNLOCK( fHaveLock );
|
|
tleave();
|
|
return dwRc;
|
|
}
|
|
|
|
|
|
DWORD
|
|
CEventHandler::OnReset(LPWSTR pszDrive)
|
|
{
|
|
tenter("CEventHandler::OnReset");
|
|
BOOL fHaveLock;
|
|
DWORD dwRc = ERROR_INTERNAL_ERROR;
|
|
|
|
ASSERT(g_pSRConfig);
|
|
|
|
LOCKORLEAVE(fHaveLock);
|
|
|
|
g_pSRConfig->SetResetFlag(TRUE);
|
|
|
|
dwRc = DisableSRS(pszDrive);
|
|
if (ERROR_SUCCESS != dwRc)
|
|
goto done;
|
|
|
|
// if not system drive, enable this drive
|
|
// else, the service will stop
|
|
// and do a firstrun the next boot
|
|
|
|
if (pszDrive && ! IsSystemDrive(pszDrive))
|
|
{
|
|
dwRc = EnableSRS(pszDrive);
|
|
}
|
|
|
|
done:
|
|
UNLOCK(fHaveLock);
|
|
tleave();
|
|
return dwRc;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
CEventHandler::OnFifo(
|
|
LPWSTR pszDrive,
|
|
DWORD dwTargetRp,
|
|
int nTargetPercent,
|
|
BOOL fIncludeCurrentRp,
|
|
BOOL fFifoAtleastOneRp)
|
|
{
|
|
tenter("CEventHandler::OnFifo");
|
|
BOOL fHaveLock;
|
|
DWORD dwRc = ERROR_INTERNAL_ERROR;
|
|
|
|
LOCKORLEAVE(fHaveLock);
|
|
|
|
ASSERT(g_pDataStoreMgr);
|
|
|
|
dwRc = g_pDataStoreMgr->Fifo(pszDrive, dwTargetRp, nTargetPercent, fIncludeCurrentRp, fFifoAtleastOneRp);
|
|
if (dwRc != ERROR_SUCCESS)
|
|
{
|
|
trace(0, "! g_pDataStoreMgr->Fifo : %ld", dwRc);
|
|
}
|
|
|
|
done:
|
|
UNLOCK(fHaveLock);
|
|
tleave();
|
|
return dwRc;
|
|
}
|
|
|
|
|
|
DWORD
|
|
CEventHandler::OnCompress(LPWSTR pszDrive)
|
|
{
|
|
tenter("CEventHandler::OnCompress");
|
|
BOOL fHaveLock;
|
|
DWORD dwRc = ERROR_INTERNAL_ERROR;
|
|
|
|
LOCKORLEAVE(fHaveLock);
|
|
|
|
ASSERT(g_pDataStoreMgr && g_pSRConfig);
|
|
|
|
dwRc = g_pDataStoreMgr->Compress(pszDrive,
|
|
g_pSRConfig->m_dwCompressionBurst);
|
|
if (ERROR_SUCCESS != dwRc)
|
|
{
|
|
trace(0, "! g_pDataStoreMgr->Compress : %ld", dwRc);
|
|
}
|
|
|
|
done:
|
|
UNLOCK(fHaveLock);
|
|
tleave();
|
|
return dwRc;
|
|
}
|
|
|
|
|
|
DWORD
|
|
CEventHandler::SRPrintStateS()
|
|
{
|
|
tenter("CEventHandler::SRPrintStateS");
|
|
BOOL fHaveLock;
|
|
DWORD dwRc = ERROR_SUCCESS;
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
WCHAR wcsPath[MAX_PATH];
|
|
|
|
LOCKORLEAVE(fHaveLock);
|
|
|
|
ASSERT(g_pDataStoreMgr);
|
|
|
|
if (0 == ExpandEnvironmentStrings(L"%temp%\\sr.txt", wcsPath, MAX_PATH))
|
|
{
|
|
dwRc = GetLastError();
|
|
trace(0, "! ExpandEnvironmentStrings : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
hFile = CreateFileW (wcsPath, // file name
|
|
GENERIC_WRITE, // file access
|
|
0, // share mode
|
|
NULL, // SD
|
|
CREATE_ALWAYS, // how to create
|
|
0, // file attributes
|
|
NULL); // handle to template file
|
|
|
|
if (INVALID_HANDLE_VALUE == hFile)
|
|
{
|
|
dwRc = GetLastError();
|
|
trace(0, "! CreateFileW : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
trace(0, "**** SR State ****");
|
|
|
|
dwRc = g_pDataStoreMgr->GetDriveTable()->ForAllDrives(CDataStore::Print, (LONG_PTR) hFile);
|
|
|
|
trace(0, "**** SR State ****");
|
|
|
|
done:
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
CloseHandle(hFile);
|
|
|
|
UNLOCK(fHaveLock);
|
|
tleave();
|
|
return dwRc;
|
|
}
|
|
|
|
|
|
// timer
|
|
// this needs to monitor datastore size and free disk space on all drives
|
|
// and trigger fifo/freeze if needed
|
|
|
|
DWORD
|
|
CEventHandler::OnTimer(
|
|
LPVOID lpParam,
|
|
BOOL fTimeout)
|
|
{
|
|
DWORD dwRc = ERROR_SUCCESS;
|
|
LPWSTR pszDrive = NULL;
|
|
DWORD dwFlags;
|
|
BOOL fHaveLock;
|
|
SDriveTableEnumContext dtec = {NULL, 0};
|
|
|
|
tenter("CEventHandler::OnTimer");
|
|
|
|
// get the lock within 5 seconds
|
|
// if we can't get the lock, then don't block
|
|
// we shall come back 2 minutes later and try again
|
|
|
|
// the wait times are such that idle callback has a somewhat
|
|
// higher priority than timer to get the lock
|
|
|
|
LOCKORLEAVE_EX(fHaveLock, 5000);
|
|
|
|
// got the lock - no one else is doing anything
|
|
|
|
ASSERT(g_pDataStoreMgr && g_pSRConfig);
|
|
|
|
|
|
// trigger freeze or fifo on each drive
|
|
// this will :
|
|
// a. check free space and trigger freeze or fifo
|
|
// b. check datastore usage percent and trigger fifo
|
|
|
|
g_pDataStoreMgr->TriggerFreezeOrFifo();
|
|
|
|
done:
|
|
UNLOCK(fHaveLock);
|
|
tleave();
|
|
return dwRc;
|
|
}
|
|
|
|
|
|
|
|
// open filter handle and register ioctls
|
|
|
|
DWORD WINAPI
|
|
PostFilterIo(PVOID pNum)
|
|
{
|
|
tenter("CEventHandler::SendIOCTLs");
|
|
|
|
DWORD dwRc = ERROR_SUCCESS;
|
|
INT index;
|
|
|
|
ASSERT(g_pSRConfig && g_pEventHandler);
|
|
|
|
//
|
|
// if shutting down, don't bother to post
|
|
//
|
|
|
|
if (IsStopSignalled(g_pSRConfig->m_hSRStopEvent))
|
|
{
|
|
trace(0, "Stop signalled - not posting io requests");
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// bind the completion to a callback
|
|
//
|
|
|
|
if ( ! BindIoCompletionCallback(g_pSRConfig->GetFilter(),
|
|
IoCompletionCallback,
|
|
0) )
|
|
{
|
|
dwRc = GetLastError();
|
|
trace(0, "! BindIoCompletionCallback : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
|
|
//
|
|
// post io completion requests
|
|
//
|
|
|
|
for (index = 0; index < (INT_PTR) pNum; index++)
|
|
{
|
|
CHAR pszEventName[MAX_PATH];
|
|
LPSR_OVERLAPPED pOverlap = NULL;
|
|
DWORD nBytes =0 ;
|
|
|
|
pOverlap = (LPSR_OVERLAPPED) SRMemAlloc( sizeof(SR_OVERLAPPED) );
|
|
if (! pOverlap)
|
|
{
|
|
trace(0, "! Out of memory");
|
|
dwRc = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto done;
|
|
}
|
|
|
|
// create an event, a handle, and put it in the completion port.
|
|
|
|
memset( &pOverlap->m_overlapped, 0, sizeof(OVERLAPPED) );
|
|
|
|
pOverlap->m_dwRecordLength = sizeof(SR_NOTIFICATION_RECORD)
|
|
+ (SR_MAX_FILENAME_LENGTH*sizeof(WCHAR));
|
|
|
|
pOverlap->m_pRecord =
|
|
(PSR_NOTIFICATION_RECORD) SRMemAlloc(pOverlap->m_dwRecordLength);
|
|
|
|
ASSERT(g_pSRConfig);
|
|
|
|
pOverlap->m_hDriver = g_pSRConfig->GetFilter();
|
|
|
|
|
|
// post ioctl - this should return ERROR_IO_PENDING
|
|
|
|
dwRc = SrWaitForNotification( pOverlap->m_hDriver,
|
|
pOverlap->m_pRecord ,
|
|
pOverlap->m_dwRecordLength,
|
|
(LPOVERLAPPED) pOverlap );
|
|
|
|
if ( dwRc != 0 && dwRc != ERROR_IO_PENDING )
|
|
{
|
|
trace(0, "! SrWaitForNotification : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
g_pEventHandler->GetCounter()->Up( ); // one more pending ioctl
|
|
}
|
|
|
|
trace(0, "Filter Io posted");
|
|
|
|
done:
|
|
tleave();
|
|
return dwRc;
|
|
}
|
|
|
|
|
|
// FILTER NOTIFICATION HANDLERS
|
|
|
|
// generic notification handler
|
|
|
|
extern "C" void CALLBACK
|
|
IoCompletionCallback(
|
|
DWORD dwErrorCode,
|
|
DWORD dwBytesTrns,
|
|
LPOVERLAPPED pOverlapped )
|
|
{
|
|
ULONG uError = 0;
|
|
LPSR_OVERLAPPED pSROverlapped = (LPSR_OVERLAPPED) pOverlapped;
|
|
BOOL fResubmit = FALSE;
|
|
WCHAR szVolumeGuid[MAX_PATH], szTemp[MAX_PATH];
|
|
|
|
tenter("IoCompletionCallback");
|
|
|
|
if (! pSROverlapped || pSROverlapped->m_hDriver == INVALID_HANDLE_VALUE)
|
|
{
|
|
trace(0, "! Null overlapped or driver handle");
|
|
goto done;
|
|
}
|
|
|
|
trace(0, "Received filter notification : errorcode=%08x, type=%08x",
|
|
dwErrorCode, pSROverlapped->m_pRecord->NotificationType);
|
|
|
|
if ( dwErrorCode != 0 ) // we cancelled it
|
|
{
|
|
trace(0, "Cancelled operation");
|
|
goto done;
|
|
}
|
|
|
|
|
|
UnicodeStringToWchar(pSROverlapped->m_pRecord->VolumeName, szTemp);
|
|
wsprintf(szVolumeGuid, L"\\\\?\\Volume%s\\", szTemp);
|
|
|
|
|
|
// handle notification
|
|
|
|
ASSERT(g_pEventHandler);
|
|
ASSERT(g_pSRConfig);
|
|
if (! g_pEventHandler || ! g_pSRConfig)
|
|
{
|
|
trace(0, "global is NULL");
|
|
goto done;
|
|
}
|
|
|
|
switch( pSROverlapped->m_pRecord->NotificationType )
|
|
{
|
|
case SrNotificationVolumeFirstWrite:
|
|
g_pEventHandler->OnFirstWrite_Notification(szVolumeGuid);
|
|
break;
|
|
|
|
case SrNotificationVolume25MbWritten:
|
|
g_pEventHandler->OnSize_Notification(szVolumeGuid,
|
|
pSROverlapped->m_pRecord->Context);
|
|
break;
|
|
|
|
case SrNotificationVolumeError:
|
|
g_pEventHandler->OnVolumeError_Notification(szVolumeGuid,
|
|
pSROverlapped->m_pRecord->Context);
|
|
break;
|
|
|
|
default:
|
|
trace(0, "Unknown notification");
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
// check for stop signal
|
|
|
|
ASSERT(g_pSRConfig);
|
|
|
|
if (IsStopSignalled(g_pSRConfig->m_hSRStopEvent))
|
|
goto done;
|
|
|
|
// re-submit the ioctl to the driver
|
|
|
|
memset( &pSROverlapped->m_overlapped, 0, sizeof(OVERLAPPED) );
|
|
pSROverlapped->m_dwRecordLength = sizeof(SR_NOTIFICATION_RECORD)
|
|
+ (SR_MAX_FILENAME_LENGTH*sizeof(WCHAR));
|
|
memset( pSROverlapped->m_pRecord, 0, pSROverlapped->m_dwRecordLength);
|
|
pSROverlapped->m_hDriver = g_pSRConfig->GetFilter();
|
|
|
|
uError = SrWaitForNotification( pSROverlapped->m_hDriver,
|
|
pSROverlapped->m_pRecord ,
|
|
pSROverlapped->m_dwRecordLength,
|
|
(LPOVERLAPPED) pSROverlapped );
|
|
|
|
if ( uError != 0 && uError != ERROR_IO_PENDING )
|
|
{
|
|
trace(0, "! SrWaitForNotification : %ld", uError);
|
|
goto done;
|
|
}
|
|
|
|
fResubmit = TRUE;
|
|
|
|
done:
|
|
// if we didn't resubmit, there is one less io request pending
|
|
|
|
if (FALSE == fResubmit && g_pEventHandler != NULL)
|
|
g_pEventHandler->GetCounter()->Down();
|
|
|
|
tleave();
|
|
return;
|
|
}
|
|
|
|
|
|
// first write notification handler
|
|
// this will be sent when the first monitored op happens on a new drive
|
|
// or a newly created restore point
|
|
// RESPONSE: update the drive table to indicate that this is a new drive
|
|
// and/or that this drive is a participant in this restore point
|
|
|
|
void
|
|
CEventHandler::OnFirstWrite_Notification(LPWSTR pszGuid)
|
|
{
|
|
DWORD dwRc = ERROR_SUCCESS;
|
|
WCHAR szMount[MAX_PATH];
|
|
BOOL fHaveLock;
|
|
CDataStore *pdsNew = NULL, *pds=NULL;
|
|
|
|
tenter("CEventHandler::OnFirstWrite_Notification");
|
|
|
|
trace(0, "First write on %S", pszGuid);
|
|
|
|
LOCKORLEAVE(fHaveLock);
|
|
|
|
ASSERT(g_pDataStoreMgr);
|
|
ASSERT(g_pSRConfig);
|
|
|
|
dwRc = g_pDataStoreMgr->GetDriveTable()->FindMountPoint(pszGuid, szMount);
|
|
if (ERROR_BAD_PATHNAME == dwRc)
|
|
{
|
|
// the mountpoint path is too long for us to support
|
|
// so disable the filter on this volume
|
|
CDataStore ds(NULL);
|
|
ds.LoadDataStore(NULL, pszGuid, NULL, 0, 0, 0);
|
|
dwRc = SrDisableVolume(g_pSRConfig->GetFilter(), ds.GetNTName());
|
|
if (dwRc != ERROR_SUCCESS)
|
|
{
|
|
trace(0, "! SrDisableVolume : %ld", dwRc);
|
|
}
|
|
else
|
|
{
|
|
WCHAR wcsPath[MAX_PATH];
|
|
MakeRestorePath (wcsPath, pszGuid, L"");
|
|
|
|
// delete the restore directory
|
|
dwRc = Delnode_Recurse (wcsPath, TRUE,
|
|
g_pDataStoreMgr->GetStopFlag());
|
|
if (dwRc != ERROR_SUCCESS)
|
|
{
|
|
trace(0, "! Delnode_Recurse : %ld", dwRc);
|
|
}
|
|
trace(0, "Mountpoint too long - disabled volume %S", pszGuid);
|
|
}
|
|
goto done;
|
|
}
|
|
|
|
if (ERROR_SUCCESS != dwRc)
|
|
{
|
|
trace(0, "! FindMountPoint on %S : %ld", pszGuid, dwRc);
|
|
goto done;
|
|
}
|
|
|
|
pdsNew = g_pDataStoreMgr->GetDriveTable()->FindDriveInTable(pszGuid);
|
|
|
|
dwRc = g_pDataStoreMgr->GetDriveTable()->AddDriveToTable(szMount, pszGuid);
|
|
if (ERROR_SUCCESS != dwRc)
|
|
{
|
|
trace(0, "! AddDriveToTable on %S", pszGuid);
|
|
goto done;
|
|
}
|
|
|
|
if (ERROR_SUCCESS != g_pDataStoreMgr->SetDriveParticipation (pszGuid, TRUE))
|
|
trace(0, "! SetDriveParticipation on %S", pszGuid);
|
|
|
|
|
|
//
|
|
// if less than 50mb free, or if SR is already frozen, then freeze
|
|
//
|
|
|
|
pds = g_pDataStoreMgr->GetDriveTable()->FindDriveInTable(pszGuid);
|
|
if (pds)
|
|
{
|
|
// update the active bit too
|
|
pds->SetActive(TRUE);
|
|
|
|
// then check diskfree
|
|
pds->UpdateDiskFree(NULL);
|
|
if ( (pds->GetDiskFree() <= THRESHOLD_FREEZE_DISKSPACE * MEGABYTE) ||
|
|
(g_pDataStoreMgr->IsDriveFrozen(g_pSRConfig->GetSystemDrive())) )
|
|
{
|
|
g_pDataStoreMgr->FreezeDrive(pszGuid);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// we just added the drive, so should never get here
|
|
//
|
|
|
|
ASSERT(0);
|
|
}
|
|
|
|
done:
|
|
UNLOCK(fHaveLock);
|
|
tleave();
|
|
return;
|
|
}
|
|
|
|
|
|
// 25MB notification handler
|
|
// this will be sent when the filter has copied 25MB of data to the datastore
|
|
// on some drive
|
|
// RESPONSE: update the datastore size and check fifo conditions
|
|
|
|
void
|
|
CEventHandler::OnSize_Notification(LPWSTR pszGuid, ULONG ulRp)
|
|
{
|
|
tenter("CEventHandler::OnSize_Notification");
|
|
|
|
int nPercent = 0;
|
|
BOOL fHaveLock;
|
|
DWORD dwRc = ERROR_SUCCESS;
|
|
|
|
LOCKORLEAVE(fHaveLock);
|
|
|
|
trace(0, "25mb copied on drive %S", pszGuid);
|
|
trace(0, "for RP%ld", ulRp);
|
|
|
|
if ((DWORD) ulRp != m_CurRp.GetNum())
|
|
{
|
|
trace(0, "This is an obsolete notification");
|
|
goto done;
|
|
}
|
|
|
|
ASSERT(g_pDataStoreMgr);
|
|
|
|
g_pDataStoreMgr->UpdateDataStoreUsage(pszGuid, SR_NOTIFY_BYTE_COUNT);
|
|
|
|
if ( ERROR_SUCCESS == g_pDataStoreMgr->GetUsagePercent(pszGuid, &nPercent)
|
|
&& nPercent >= THRESHOLD_FIFO_PERCENT )
|
|
{
|
|
OnFifo(pszGuid,
|
|
0, // no target rp
|
|
TARGET_FIFO_PERCENT, // target percent
|
|
TRUE, // fifo current rp if necessary (freeze)
|
|
FALSE);
|
|
}
|
|
|
|
done:
|
|
UNLOCK(fHaveLock);
|
|
tleave();
|
|
return;
|
|
}
|
|
|
|
|
|
// disk full notification handler
|
|
// this will be sent when the filter encounters an error on a volume
|
|
// ideally, this should never be sent
|
|
// if diskfull, freeze SR on this drive
|
|
// else disable SR on this drive
|
|
|
|
void
|
|
CEventHandler::OnVolumeError_Notification(LPWSTR pszGuid, ULONG ulError)
|
|
{
|
|
tenter("CEventHandler::OnVolumeError_Notification");
|
|
BOOL fHaveLock;
|
|
DWORD dwRc = ERROR_SUCCESS;
|
|
|
|
LOCKORLEAVE(fHaveLock);
|
|
|
|
trace(0, "Volume Error on %S", pszGuid);
|
|
trace(0, "Error : %ld", ulError);
|
|
|
|
ASSERT(g_pDataStoreMgr);
|
|
ASSERT(g_pSRConfig);
|
|
|
|
if (ulError == ERROR_DISK_FULL)
|
|
{
|
|
// no more disk space - freeze
|
|
// NOTE: we don't check to see if the drive is already
|
|
// frozen here. If for some reason we are out of sync with
|
|
// the driver, this will fix it
|
|
|
|
g_pDataStoreMgr->FreezeDrive(pszGuid);
|
|
}
|
|
else
|
|
{
|
|
// fifo all restore points prior to the current one
|
|
|
|
dwRc = g_pDataStoreMgr->Fifo(g_pSRConfig->GetSystemDrive(), 0, 0, FALSE, FALSE);
|
|
if (dwRc != ERROR_SUCCESS)
|
|
{
|
|
trace(0, "! Fifo : %ld", dwRc);
|
|
}
|
|
|
|
// make the current rp a cancelled rp
|
|
// so that UI will not display it
|
|
|
|
if (! m_fNoRpOnSystem)
|
|
{
|
|
SRRemoveRestorePointS(m_CurRp.GetNum());
|
|
// m_CurRp.Cancel();
|
|
}
|
|
|
|
// log the error in the drivetable
|
|
|
|
dwRc = g_pDataStoreMgr->SetDriveError(pszGuid);
|
|
if (dwRc != ERROR_SUCCESS)
|
|
{
|
|
trace(0, "! SetDriveError : %ld", dwRc);
|
|
}
|
|
}
|
|
|
|
done:
|
|
UNLOCK(fHaveLock);
|
|
tleave();
|
|
return;
|
|
}
|
|
|
|
|
|
// disk space notifications sent by the shell
|
|
|
|
DWORD WINAPI
|
|
OnDiskFree_200(PVOID pszDrive)
|
|
{
|
|
// thaw
|
|
|
|
ASSERT(g_pEventHandler);
|
|
|
|
(g_pEventHandler->GetCounter())->Down();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
DWORD WINAPI
|
|
OnDiskFree_80(PVOID pszDrive)
|
|
{
|
|
// fifo
|
|
|
|
ASSERT(g_pEventHandler);
|
|
|
|
g_pEventHandler->OnFifo((LPWSTR) pszDrive,
|
|
0, // no target rp
|
|
TARGET_FIFO_PERCENT, // target percent
|
|
TRUE, // fifo current rp if necessary (freeze)
|
|
TRUE); // fifo atleast one restore point
|
|
|
|
(g_pEventHandler->GetCounter())->Down();
|
|
|
|
return 0;
|
|
}
|
|
|
|
DWORD WINAPI
|
|
OnDiskFree_50(PVOID pszDrive)
|
|
{
|
|
TENTER("OnDiskFree_50");
|
|
|
|
DWORD dwRc = ERROR_SUCCESS;
|
|
|
|
// freeze
|
|
|
|
ASSERT(g_pEventHandler);
|
|
ASSERT(g_pDataStoreMgr);
|
|
|
|
//
|
|
// check if there is some rp directory
|
|
// if none, then don't bother
|
|
//
|
|
|
|
CRestorePointEnum *prpe = new CRestorePointEnum((LPWSTR) pszDrive, FALSE, FALSE); // backward, include current
|
|
CRestorePoint *prp = new CRestorePoint;
|
|
|
|
|
|
if (!prpe || !prp)
|
|
{
|
|
trace(0, "Cannot allocate memory for restore point enum");
|
|
goto done;
|
|
}
|
|
|
|
dwRc = prpe->FindFirstRestorePoint(*prp);
|
|
if (dwRc == ERROR_SUCCESS || dwRc == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
g_pEventHandler->OnFreeze((LPWSTR) pszDrive);
|
|
}
|
|
else
|
|
{
|
|
trace(0, "Nothing in datastore -- so not freezing");
|
|
}
|
|
|
|
if (prpe)
|
|
delete prpe;
|
|
if (prp)
|
|
delete prp;
|
|
|
|
(g_pEventHandler->GetCounter())->Down();
|
|
|
|
done:
|
|
TLEAVE();
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
// stop event management
|
|
|
|
void
|
|
CEventHandler::SignalStop()
|
|
{
|
|
if ( g_pSRConfig )
|
|
{
|
|
SetEvent( g_pSRConfig->m_hSRStopEvent );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
CEventHandler::WaitForStop()
|
|
{
|
|
if ( g_pSRConfig )
|
|
{
|
|
WaitForSingleObject( g_pSRConfig->m_hSRStopEvent, INFINITE );
|
|
return g_pSRConfig->GetResetFlag() ? ERROR_NO_SHUTDOWN_IN_PROGRESS : ERROR_SHUTDOWN_IN_PROGRESS;
|
|
}
|
|
else
|
|
return ERROR_INTERNAL_ERROR;
|
|
}
|
|
|
|
|
|
//
|
|
// perform idle tasks
|
|
//
|
|
DWORD
|
|
CEventHandler::OnIdle()
|
|
{
|
|
DWORD dwThawStatus = ERROR_NO_MORE_ITEMS;
|
|
DWORD dwRc = ERROR_NO_MORE_ITEMS;
|
|
BOOL fCreateAuto = FALSE;
|
|
ULARGE_INTEGER *pulFreeze = NULL;
|
|
|
|
tenter("CEventHandler::OnIdle");
|
|
|
|
trace(0, "Idleness detected");
|
|
|
|
ASSERT(g_pSRConfig);
|
|
ASSERT(g_pDataStoreMgr);
|
|
|
|
//
|
|
// check thaw timer to see if
|
|
// there are frozen drives
|
|
//
|
|
|
|
pulFreeze = (ULARGE_INTEGER *) &m_ftFreeze;
|
|
if (pulFreeze->QuadPart != 0)
|
|
{
|
|
FILETIME ftNow;
|
|
ULARGE_INTEGER *pulNow;
|
|
|
|
GetSystemTimeAsFileTime(&ftNow);
|
|
pulNow = (ULARGE_INTEGER *) &ftNow;
|
|
|
|
//
|
|
// if more than 15 minutes since freeze happened
|
|
// try to thaw
|
|
//
|
|
|
|
if (pulNow->QuadPart - pulFreeze->QuadPart >=
|
|
((INT64) g_pSRConfig->m_dwThawInterval * 1000 * 1000 * 10))
|
|
{
|
|
dwThawStatus = g_pDataStoreMgr->ThawDrives(TRUE);
|
|
if (dwThawStatus != ERROR_SUCCESS)
|
|
{
|
|
trace(0, "Cannot thaw drives yet");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fCreateAuto = IsTimeForAutoRp();
|
|
}
|
|
|
|
|
|
// make periodic checkpoint if it is time to make an auto-rp or
|
|
// time to thaw drives or
|
|
// a volume error happened in the previous session
|
|
|
|
if ( dwThawStatus == ERROR_SUCCESS ||
|
|
fCreateAuto == TRUE ||
|
|
m_fCreateRpASAP == TRUE )
|
|
{
|
|
RESTOREPOINTINFO RPInfo;
|
|
STATEMGRSTATUS SmgrStatus;
|
|
|
|
RPInfo.dwEventType = BEGIN_SYSTEM_CHANGE;
|
|
RPInfo.dwRestorePtType = m_fNoRpOnSystem ? FIRSTRUN : CHECKPOINT;
|
|
if (ERROR_SUCCESS != SRLoadString(L"srrstr.dll", IDS_SYSTEM_CHECKPOINT_TEXT, RPInfo.szDescription, MAX_PATH))
|
|
{
|
|
lstrcpy(RPInfo.szDescription, s_cszSystemCheckpointName);
|
|
}
|
|
SRSetRestorePointS(&RPInfo, &SmgrStatus);
|
|
|
|
dwRc = SmgrStatus.nStatus;
|
|
if (dwRc != ERROR_SUCCESS)
|
|
goto done;
|
|
|
|
m_fCreateRpASAP = FALSE;
|
|
|
|
// we made a restore point and perhaps thawed some drives
|
|
// let's not push it any further
|
|
// compress on next idle opportunity
|
|
}
|
|
else
|
|
{
|
|
// if system is running on battery
|
|
// skip these tasks
|
|
|
|
if (g_pSRConfig->IsSystemOnBattery())
|
|
{
|
|
trace(0, "System on battery -- skipping idle tasks");
|
|
goto done;
|
|
}
|
|
|
|
// fifo restore points older than a specified age
|
|
// if the fifo age is set to 0, that means this feature
|
|
// is turned off
|
|
|
|
if (g_pSRConfig->m_dwRPLifeInterval > 0)
|
|
{
|
|
g_pDataStoreMgr->FifoOldRps(g_pSRConfig->m_dwRPLifeInterval);
|
|
}
|
|
|
|
// compress backed up files - pick any drive
|
|
|
|
dwRc = OnCompress( NULL );
|
|
|
|
//
|
|
// if we have more to compress, request idle again
|
|
//
|
|
|
|
if (dwRc == ERROR_OPERATION_ABORTED)
|
|
{
|
|
SetEvent(g_pSRConfig->m_hIdleRequestEvent);
|
|
}
|
|
}
|
|
|
|
done:
|
|
tleave();
|
|
return dwRc;
|
|
}
|
|
|
|
|
|
extern "C" void CALLBACK
|
|
IdleRequestCallback(PVOID pContext, BOOLEAN fTimerFired)
|
|
{
|
|
BOOL fRegistered = FALSE;
|
|
HANDLE *pWaitHandle = NULL;
|
|
DWORD dwErr = ERROR_SUCCESS;
|
|
BOOL fHaveLock = FALSE;
|
|
|
|
tenter("CEventHandler::IdleRequestCallback");
|
|
|
|
ASSERT(g_pEventHandler);
|
|
ASSERT(g_pSRConfig);
|
|
|
|
if (g_pEventHandler == NULL || g_pSRConfig == NULL)
|
|
{
|
|
trace(0, "global is Null");
|
|
goto Err;
|
|
}
|
|
|
|
fHaveLock = g_pEventHandler->GetLock()->Lock(CLock::TIMEOUT);
|
|
if (! fHaveLock)
|
|
{
|
|
trace(0, "Cannot get lock");
|
|
goto Err;
|
|
}
|
|
|
|
//
|
|
// first off, if the stop event is triggered
|
|
// and we are here for some reason,
|
|
// bail blindly
|
|
//
|
|
|
|
if (IsStopSignalled(g_pSRConfig->m_hSRStopEvent))
|
|
{
|
|
trace(0, "Stop event signalled - bailing out of idle");
|
|
goto Err;
|
|
}
|
|
|
|
//
|
|
// idleness is requested or timer fired
|
|
// re-register for idle again
|
|
//
|
|
|
|
if (fTimerFired)
|
|
trace(0, "Timed out");
|
|
else
|
|
trace(0, "Idle request event received");
|
|
|
|
//
|
|
// if already registered for idle
|
|
// then do nothing
|
|
//
|
|
|
|
if (g_pEventHandler->m_hIdleStartHandle != NULL)
|
|
{
|
|
trace(0, "Already registered for idle");
|
|
goto Err;
|
|
}
|
|
|
|
dwErr = RegisterIdleTask(ItSystemRestoreIdleTaskId,
|
|
&(g_pSRConfig->m_hIdle),
|
|
&(g_pSRConfig->m_hIdleStartEvent),
|
|
&(g_pSRConfig->m_hIdleStopEvent));
|
|
if (dwErr != ERROR_SUCCESS)
|
|
{
|
|
trace(0, "! RegisterIdleTask : %ld", dwErr);
|
|
}
|
|
else
|
|
{
|
|
trace(0, "Registered for idle");
|
|
|
|
//
|
|
// register idle callback
|
|
//
|
|
if (FALSE == RegisterWaitForSingleObject(&g_pEventHandler->m_hIdleStartHandle,
|
|
g_pSRConfig->m_hIdleStartEvent,
|
|
(WAITORTIMERCALLBACK) IdleStartCallback,
|
|
NULL,
|
|
INFINITE,
|
|
WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE))
|
|
{
|
|
dwErr = GetLastError();
|
|
trace(0, "! RegisterWaitForSingleObject for startidle: %ld", dwErr);
|
|
goto Err;
|
|
}
|
|
|
|
if (FALSE == RegisterWaitForSingleObject(&g_pEventHandler->m_hIdleStopHandle,
|
|
g_pSRConfig->m_hIdleStopEvent,
|
|
(WAITORTIMERCALLBACK) IdleStopCallback,
|
|
NULL,
|
|
INFINITE,
|
|
WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE))
|
|
{
|
|
dwErr = GetLastError();
|
|
trace(0, "! RegisterWaitForSingleObject for stopidle: %ld", dwErr);
|
|
goto Err;
|
|
}
|
|
}
|
|
|
|
|
|
Err:
|
|
if (g_pEventHandler)
|
|
{
|
|
if (fHaveLock)
|
|
g_pEventHandler->GetLock()->Unlock();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
extern "C" void CALLBACK
|
|
IdleStartCallback(PVOID pContext, BOOLEAN fTimerFired)
|
|
{
|
|
DWORD dwErr = ERROR_SUCCESS;
|
|
BOOL fHaveLock = FALSE;
|
|
|
|
tenter("CEventHandler::IdleStartCallback");
|
|
|
|
ASSERT(g_pEventHandler);
|
|
ASSERT(g_pSRConfig);
|
|
|
|
if (g_pEventHandler == NULL || g_pSRConfig == NULL)
|
|
{
|
|
trace(0, "global is Null");
|
|
goto Err;
|
|
}
|
|
|
|
fHaveLock = g_pEventHandler->GetLock()->Lock(CLock::TIMEOUT);
|
|
if (! fHaveLock)
|
|
{
|
|
trace(0, "Cannot get lock");
|
|
goto Err;
|
|
}
|
|
|
|
//
|
|
// first off, if the stop event is triggered
|
|
// and we are here for some reason,
|
|
// bail blindly
|
|
//
|
|
|
|
if (IsStopSignalled(g_pSRConfig->m_hSRStopEvent))
|
|
{
|
|
trace(0, "Stop event signalled - bailing out of idle");
|
|
goto Err;
|
|
}
|
|
|
|
//
|
|
// idleness occurred
|
|
//
|
|
|
|
trace(0, "fTimerFired = %d", fTimerFired);
|
|
|
|
g_pEventHandler->OnIdle();
|
|
|
|
dwErr = UnregisterIdleTask(g_pSRConfig->m_hIdle,
|
|
g_pSRConfig->m_hIdleStartEvent,
|
|
g_pSRConfig->m_hIdleStopEvent);
|
|
if (dwErr != ERROR_SUCCESS)
|
|
{
|
|
trace(0, "! UnregisterIdleTask : %ld", dwErr);
|
|
}
|
|
else
|
|
{
|
|
trace(0, "Unregistered from idle");
|
|
}
|
|
|
|
//
|
|
// we are done - record this
|
|
// since we registered for this callback only once,
|
|
// we don't have to call UnregisterWait on this handle -
|
|
// or so I hope
|
|
//
|
|
|
|
g_pEventHandler->m_hIdleStartHandle = NULL;
|
|
|
|
Err:
|
|
if (g_pEventHandler)
|
|
{
|
|
if (fHaveLock)
|
|
g_pEventHandler->GetLock()->Unlock();
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
extern "C" void CALLBACK
|
|
IdleStopCallback(PVOID pContext, BOOLEAN fTimerFired)
|
|
{
|
|
tenter("IdleStopCallback");
|
|
|
|
BOOL fHaveLock = FALSE;
|
|
|
|
if (g_pEventHandler == NULL)
|
|
{
|
|
trace(0, "global is Null");
|
|
goto Err;
|
|
}
|
|
|
|
fHaveLock = g_pEventHandler->GetLock()->Lock(CLock::TIMEOUT);
|
|
if (! fHaveLock)
|
|
{
|
|
trace(0, "Cannot get lock");
|
|
goto Err;
|
|
}
|
|
|
|
trace(0, "Idle Stop event signalled");
|
|
|
|
g_pEventHandler->m_hIdleStopHandle = NULL;
|
|
|
|
Err:
|
|
if (g_pEventHandler)
|
|
{
|
|
if (fHaveLock)
|
|
g_pEventHandler->GetLock()->Unlock();
|
|
}
|
|
tleave();
|
|
}
|
|
|
|
|
|
// set up timer
|
|
|
|
DWORD
|
|
CEventHandler::InitTimer()
|
|
{
|
|
DWORD dwRc = ERROR_SUCCESS;
|
|
|
|
tenter("CEventHandler::InitTimer");
|
|
|
|
ASSERT(g_pSRConfig);
|
|
|
|
//
|
|
// if the timer interval is specified as 0,
|
|
// then don't create timer
|
|
//
|
|
|
|
if (g_pSRConfig->m_dwTimerInterval == 0)
|
|
{
|
|
trace(0, "Not starting timer");
|
|
goto done;
|
|
}
|
|
|
|
m_hTimerQueue = CreateTimerQueue();
|
|
if (! m_hTimerQueue)
|
|
{
|
|
dwRc = GetLastError();
|
|
trace(0, " ! CreateTimerQueue : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
if (FALSE == CreateTimerQueueTimer(&m_hTimer,
|
|
m_hTimerQueue,
|
|
TimerCallback,
|
|
NULL,
|
|
g_pSRConfig->m_dwTimerInterval * 1000, // milliseconds
|
|
g_pSRConfig->m_dwTimerInterval * 1000, // periodic
|
|
WT_EXECUTEINIOTHREAD))
|
|
{
|
|
dwRc = GetLastError();
|
|
trace(0, "! CreateTimerQueueTimer : %ld", dwRc);
|
|
goto done;
|
|
}
|
|
|
|
trace(0, "SRBoottask: Timer started");
|
|
|
|
done:
|
|
tleave();
|
|
return dwRc;
|
|
}
|
|
|
|
|
|
// end timer
|
|
|
|
BOOL
|
|
CEventHandler::EndTimer()
|
|
{
|
|
DWORD dwRc;
|
|
BOOL fRc = TRUE;
|
|
|
|
tenter("CEventHandler::EndTimer");
|
|
|
|
if ( ! m_hTimerQueue )
|
|
{
|
|
trace(0 , "! m_hTimerQueue = NULL");
|
|
goto done;
|
|
}
|
|
|
|
// delete timer queue should wait for current timer tasks to end
|
|
|
|
if (FALSE == (fRc = DeleteTimerQueueEx( m_hTimerQueue, INVALID_HANDLE_VALUE )))
|
|
{
|
|
trace(0, "! DeleteTimerQueueEx : %ld", GetLastError());
|
|
}
|
|
|
|
m_hTimerQueue = NULL;
|
|
m_hTimer = NULL;
|
|
|
|
done:
|
|
tleave( );
|
|
return fRc;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CEventHandler::IsTimeForAutoRp()
|
|
{
|
|
tenter("CEventHandler::IsTimeForAutoRp");
|
|
|
|
FILETIME *pftRp, ftNow;
|
|
ULARGE_INTEGER *pulRp, *pulNow;
|
|
BOOL fRc = FALSE;
|
|
INT64 llInterval, llSession;
|
|
|
|
ASSERT(g_pSRConfig && g_pDataStoreMgr);
|
|
|
|
if (m_fNoRpOnSystem)
|
|
{
|
|
// if SR is frozen, we will create a restore point via the thaw codepath in OnIdle
|
|
// we will get here ONLY if we get idle time before we have created the firstrun checkpoint -
|
|
// we won't create an idle checkpoint before the firstrun checkpoint if we have a Run key waiting
|
|
// to create one
|
|
|
|
HKEY hKey;
|
|
DWORD dwRet = RegOpenKey(HKEY_LOCAL_MACHINE,
|
|
L"Software\\Microsoft\\Windows\\CurrentVersion\\Run",
|
|
&hKey);
|
|
if (dwRet == ERROR_SUCCESS)
|
|
{
|
|
dwRet = RegQueryValueEx(hKey, L"SRFirstRun", NULL, NULL, NULL, NULL);
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
if (dwRet == ERROR_SUCCESS)
|
|
{
|
|
trace(0, "Run entry exists to create firstrun checkpoint - not creating idle checkpoint");
|
|
fRc = FALSE;
|
|
goto done;
|
|
}
|
|
else
|
|
{
|
|
fRc = TRUE;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
// get the last restore point creation time and the current time
|
|
|
|
pftRp = m_CurRp.GetTime();
|
|
GetSystemTimeAsFileTime(&ftNow);
|
|
|
|
pulRp = (ULARGE_INTEGER *) pftRp;
|
|
pulNow = (ULARGE_INTEGER *) &ftNow;
|
|
|
|
|
|
// check the last restore point time with current time
|
|
// if the difference is greater than GlobalInterval, it's time to make a new one
|
|
// all comparisions in filetime units - i.e. 100's of nanoseconds
|
|
|
|
// if GlobalInterval is 0, this is turned off
|
|
|
|
llInterval = (INT64) g_pSRConfig->m_dwRPGlobalInterval * 10 * 1000 * 1000;
|
|
if ( llInterval > 0 &&
|
|
pulNow->QuadPart - pulRp->QuadPart >= llInterval )
|
|
{
|
|
trace(0, "24 hrs elapsed since last restore point");
|
|
fRc = TRUE;
|
|
goto done;
|
|
}
|
|
|
|
// if the last restore point was more than 10hrs ago,
|
|
// and the current session began more than 10hrs ago,
|
|
// then we haven't made a restore point for the last 10hrs in the current session
|
|
// again, it's time to make a new one
|
|
// this will ensure that we keep making checkpoints every 10hrs of session time,
|
|
// idleness permitting
|
|
|
|
// if SessionInterval is 0, this is turned off
|
|
|
|
// if system is on battery, skip creating session rp
|
|
|
|
if (g_pSRConfig->IsSystemOnBattery())
|
|
{
|
|
trace(0, "System on battery -- skipping session rp check");
|
|
goto done;
|
|
}
|
|
|
|
llSession = (INT64) GetTickCount() * 10 * 1000;
|
|
llInterval = (INT64) g_pSRConfig->m_dwRPSessionInterval * 10 * 1000 * 1000;
|
|
if ( llInterval > 0 &&
|
|
llSession >= llInterval &&
|
|
pulNow->QuadPart - pulRp->QuadPart >= llInterval )
|
|
{
|
|
trace(0, "10 hrs elapsed in current session since last restore point");
|
|
fRc = TRUE;
|
|
goto done;
|
|
}
|
|
|
|
// if we reach here, no restore point needs to be created now
|
|
// fRc is already FALSE
|
|
|
|
done:
|
|
tleave();
|
|
return fRc;
|
|
}
|
|
|
|
|
|
void
|
|
CEventHandler::RefreshCurrentRp(BOOL fScanAllDrives)
|
|
{
|
|
tenter("CEventHandler::RefreshCurrentRp");
|
|
|
|
DWORD dwErr;
|
|
SDriveTableEnumContext dtec = {NULL, 0};
|
|
CDataStore *pds = NULL;
|
|
|
|
ASSERT(g_pSRConfig && g_pDataStoreMgr);
|
|
|
|
//
|
|
// get the most recent valid restore point
|
|
// cancelled restore points are considered valid as well
|
|
// if rp.log is missing, we will enumerate back up to the point where it exists
|
|
// and consider that the most recent restore point
|
|
//
|
|
|
|
CRestorePointEnum *prpe = new CRestorePointEnum(g_pSRConfig->GetSystemDrive(), FALSE, FALSE);
|
|
if (!prpe)
|
|
{
|
|
trace(0, "Cannot allocate memory for restore point enum");
|
|
goto done;
|
|
}
|
|
|
|
dwErr = prpe->FindFirstRestorePoint(m_CurRp);
|
|
while (dwErr == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
fScanAllDrives = FALSE;
|
|
dwErr = prpe->FindNextRestorePoint(m_CurRp);
|
|
}
|
|
|
|
if (dwErr == ERROR_SUCCESS)
|
|
{
|
|
trace(0, "Current Restore Point: %S", m_CurRp.GetDir());
|
|
m_fNoRpOnSystem = FALSE;
|
|
|
|
// update the participate bits on each datastore -
|
|
// we need to do this every time we come up
|
|
// because we might have missed filter firstwrite
|
|
// notifications
|
|
|
|
if (fScanAllDrives)
|
|
{
|
|
dwErr = g_pDataStoreMgr->UpdateDriveParticipation(NULL, m_CurRp.GetDir());
|
|
if (dwErr != ERROR_SUCCESS)
|
|
{
|
|
trace(0, "UpdateDriveParticipation : %ld", dwErr);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
trace(0, "No live restore points on system");
|
|
m_fNoRpOnSystem = TRUE;
|
|
}
|
|
|
|
//
|
|
// if any drive is newly frozen,
|
|
// record freeze time
|
|
//
|
|
|
|
if (m_ftFreeze.dwLowDateTime == 0 &&
|
|
m_ftFreeze.dwHighDateTime == 0 &&
|
|
g_pDataStoreMgr->IsDriveFrozen(NULL))
|
|
{
|
|
GetSystemTimeAsFileTime(&m_ftFreeze);
|
|
}
|
|
else // not frozen
|
|
{
|
|
m_ftFreeze.dwLowDateTime = 0;
|
|
m_ftFreeze.dwHighDateTime = 0;
|
|
}
|
|
|
|
prpe->FindClose ();
|
|
delete prpe;
|
|
|
|
done:
|
|
tleave();
|
|
}
|
|
|
|
|
|
// queue a work item to a thread from the thread pool
|
|
// keep a count of all such queued items
|
|
|
|
DWORD
|
|
CEventHandler::QueueWorkItem(WORKITEMFUNC pFunc, PVOID pv)
|
|
{
|
|
m_Counter.Up();
|
|
if (! QueueUserWorkItem(pFunc, pv, WT_EXECUTELONGFUNCTION))
|
|
m_Counter.Down();
|
|
|
|
return GetLastError();
|
|
}
|
|
|
|
|
|
// CALLBACK functions
|
|
// calls through to eventhandler methods
|
|
|
|
// timer
|
|
|
|
extern "C" void CALLBACK
|
|
TimerCallback(
|
|
PVOID lpParam,
|
|
BOOLEAN fTimeout)
|
|
{
|
|
if ( g_pEventHandler )
|
|
g_pEventHandler->OnTimer( lpParam, fTimeout );
|
|
}
|
|
|
|
|
|
|