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