/******************************************************************************
 *
 *  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 );
}