|
|
/******************************************************************************
Copyright (c) 2000 Microsoft Corporation
Module Name: romgr.cpp
Abstract: This file contains the implementation of CRestoreOperationManager class and ::CreateRestoreOperationManager.
Revision History: Seong Kook Khang (SKKhang) 06/20/00 created
******************************************************************************/
#include "stdwin.h"
#include "rstrcore.h"
#include "resource.h"
#include "srdefs.h"
#include "utils.h"
#include "..\snapshot\snappatch.h"
//
// global variable for mfex marker
//
DWORD g_dwExistingMFEXMarker;
CSRClientLoader g_CSRClientLoader;
#define STR_REGPATH_SESSIONMANAGER L"System\\CurrentControlSet\\Control\\Session Manager"
#define STR_REGVAL_MOVEFILEEX L"PendingFileRenameOperations"
void SetRestoreStatusFailed() { TraceFunctEnter("SetRestoreStatusFailed");
if (!::SRSetRegDword(HKEY_LOCAL_MACHINE,s_cszSRRegKey,s_cszRestoreStatus,0)) { // ignore the error since this is not a fatal error
ErrorTrace(0,"SRSetRegDword failed.ec=%d", GetLastError()); } TraceFunctLeave(); }
DWORD RestoreRIDs (WCHAR *pszSamPath);
/////////////////////////////////////////////////////////////////////////////
// CRestoreOperationManager construction / destruction
CRestoreOperationManager::CRestoreOperationManager() { m_fFullRestore = TRUE; m_szMapFile[0] = L'\0'; m_pLogFile = NULL; m_pProgress = NULL; m_dwRPNum = 0; m_paryEnt = NULL; m_fRebuildCatalogDb = FALSE; }
/////////////////////////////////////////////////////////////////////////////
CRestoreOperationManager::~CRestoreOperationManager() { SAFE_RELEASE(m_pLogFile); SAFE_RELEASE(m_pProgress); }
/////////////////////////////////////////////////////////////////////////////
// CRestoreOperationManager - methods
#define TIMEOUT_RESTORETHREAD 5000
BOOL CRestoreOperationManager::Run( BOOL fFull ) { TraceFunctEnter("CRestoreOperationManager::Run"); BOOL fRet = FALSE; HANDLE hThread; DWORD dwRet;
// Create progress window
if ( !m_pProgress->Create() ) goto Exit;
m_fFullRestore = fFull;
// Create secondary thread for main restore operation
hThread = ::CreateThread( NULL, 0, ExtThreadProc, this, 0, NULL ); if ( hThread == NULL ) { LPCWSTR cszErr = ::GetSysErrStr(); ErrorTrace(0, "::CreateThread failed - %ls", cszErr);
//NOTE: should I try to run restore in the context of current thread?
//
goto Exit; }
// Message loop, wait until restore thread closes progress window
if ( !m_pProgress->Run() ) goto Exit;
// Double check if thread has been terminated
dwRet = ::WaitForSingleObject( hThread, TIMEOUT_RESTORETHREAD ); if ( dwRet == WAIT_FAILED ) { LPCWSTR cszErr = ::GetSysErrStr(); ErrorTrace(0, "::WaitForSingleObject failed - %ls", cszErr); goto Exit; } else if ( dwRet == WAIT_TIMEOUT ) { ErrorTrace(0, "Timeout while waiting for the restore thread finishes..."); goto Exit; } ::CloseHandle( hThread );
fRet = TRUE; Exit: // Close method is safe to call even if window is not open, so calling it
// unconditionally.
m_pProgress->Close();
TraceFunctLeave(); return( fRet ); }
/////////////////////////////////////////////////////////////////////////////
//
// If fChecKSrc is TRUE, it means the dependency is for the result of the
// original operation (e.g. delete, rename FROM, etc.). If it's FALSE, it
// means the dependency is for the location which is being free'ed by the
// original operation (e.g. add, rename TO, etc.).
//
BOOL CRestoreOperationManager::FindDependentMapEntry( LPCWSTR cszPath, BOOL fCheckObj, CRestoreMapEntry **ppEnt ) { TraceFunctEnter("CRestoreOperationManager::FindDependentMapEntry"); BOOL fRet = FALSE; int nEntAll; int nEnt; CRestoreMapEntry *pEnt; DWORD dwOpr; LPCWSTR cszDep;
if ( ppEnt != NULL ) *ppEnt = NULL;
nEntAll = m_paryEnt[m_nDrv].GetSize(); for ( nEnt = m_nEnt+1; nEnt < nEntAll; nEnt++ ) { pEnt = m_paryEnt[m_nDrv][nEnt]; dwOpr = pEnt->GetOpCode(); cszDep = NULL;
if ( fCheckObj ) { switch ( dwOpr ) { case OPR_DIR_RENAME : case OPR_FILE_RENAME : cszDep = pEnt->GetPath2(); break;
case OPR_DIR_DELETE : case OPR_FILE_DELETE : case OPR_FILE_MODIFY : //
// ISSUE: SetAttrib and SetAcl cannot be processed by Session Manager.
// In order to properly handle them, there should be post processing
// just before displaying the result page. However, it might be
// possible that the target file/dir is in use at that moment.
//
case OPR_SETATTRIB : case OPR_SETACL : cszDep = pEnt->GetPath1(); break; } } else { switch ( dwOpr ) { case OPR_DIR_CREATE : case OPR_DIR_RENAME : case OPR_FILE_ADD : case OPR_FILE_RENAME : cszDep = pEnt->GetPath1(); break; } }
if ( cszDep != NULL ) if ( ::StrCmpI( cszPath, cszDep ) == 0 ) break; }
if ( nEnt >= nEntAll ) goto Exit;
// Dependent Node Found
if ( ppEnt != NULL ) *ppEnt = pEnt;
fRet = TRUE; Exit: TraceFunctLeave(); return( fRet ); }
/////////////////////////////////////////////////////////////////////////////
BOOL CRestoreOperationManager::GetNextMapEntry( CRestoreMapEntry **ppEnt ) { TraceFunctEnter("CRestoreOperationManager::GetNextMapEntry"); BOOL fRet = FALSE;
if ( ppEnt != NULL ) *ppEnt = NULL;
if ( m_nEnt >= m_paryEnt[m_nDrv].GetUpperBound() ) goto Exit;
if ( ppEnt != NULL ) *ppEnt = m_paryEnt[m_nDrv][m_nEnt+1];
fRet = TRUE; Exit: TraceFunctLeave(); return( fRet ); } /////////////////////////////////////////////////////////////////////////////
BOOL CRestoreOperationManager::Release() { TraceFunctEnter("CRestoreOperationManager::Release"); delete this; TraceFunctLeave(); return( TRUE ); }
/////////////////////////////////////////////////////////////////////////////
// CRestoreOperationManager operations
static LPCWSTR s_cszMapFile = L"%SystemRoot%\\system32\\restore\\rstrmap.dat";
BOOL CRestoreOperationManager::Init() { TraceFunctEnter("CRestoreOperationManager::Init"); BOOL fRet = FALSE; SRstrLogHdrV3 sRPInfo;
// Construct internal file pathes
if ( ::ExpandEnvironmentStrings( s_cszMapFile, m_szMapFile, MAX_PATH ) == 0 ) { LPCWSTR cszErr = ::GetSysErrStr(); ErrorTrace(0, "::ExpandEnvironmentStrings failed - %s", cszErr); goto Exit; }
// Open the log file and read restore point info
if ( !::OpenRestoreLogFile( &m_pLogFile ) ) goto Exit; if ( !m_pLogFile->ReadHeader( &sRPInfo, m_aryDrv ) ) goto Exit; m_dwRPNum = sRPInfo.dwRPNum; m_dwRPNew = sRPInfo.dwRPNew;
// Create progress window object
if ( !::CreateRestoreProgressWindow( &m_pProgress ) ) goto Exit;
fRet = TRUE; Exit: if ( !fRet ) { SAFE_RELEASE(m_pLogFile); SAFE_RELEASE(m_pProgress); }
TraceFunctLeave(); return( fRet ); }
/////////////////////////////////////////////////////////////////////////////
// CRestoreOperationManager operations - worker thread
DWORD WINAPI CRestoreOperationManager::ExtThreadProc( LPVOID lpParam ) { TraceFunctEnter("CRestoreOperationManager::ExtThreadProc"); DWORD dwRet; CRestoreOperationManager *pROMgr; pROMgr = (CRestoreOperationManager*)lpParam; dwRet = pROMgr->ROThreadProc();
TraceFunctLeave(); return( dwRet ); }
/////////////////////////////////////////////////////////////////////////////
DWORD CRestoreOperationManager::ROThreadProc() { TraceFunctEnter("CRestoreOperationManager::ROThreadProc"); DWORD dwRes; CSRLockFile cLock; // Lock/load designated files/dirs during a restore.
// In order to simulate it as realistic as possible,
// locking will be in effect during the entire
// restoration.
CSnapshot cSS; WCHAR szSysDrv[MAX_SYS_DRIVE]; // System Drive
WCHAR szRPDir[MAX_RP_PATH]; // Restore Point Directory ("RPn")
WCHAR szSSPath[MAX_PATH]; // Full path of Restore Point Directory
// 1. Initialization.
dwRes = T2Initialize(); if ( dwRes != ERROR_SUCCESS ) goto Exit;
// 2. Create restore map and read it.
m_pProgress->SetStage( RPS_PREPARE, 0 ); dwRes = T2CreateMap(); if ( dwRes != ERROR_SUCCESS ) goto Exit;
// 3. Preprocessing. (?)
//
// Do snapshot init here to prevent low disk conditions after
// restore is done.
//
::GetSystemDrive( szSysDrv ); ::wsprintf( szRPDir, L"%s%d", s_cszRPDir, m_dwRPNum ); ::MakeRestorePath( szSSPath, szSysDrv, szRPDir );
//
// unpatch the snapshot if need be
// if the snapshot has not been patched, this should be a no-op
//
lstrcat(szSSPath, SNAPSHOT_DIR_NAME); dwRes = PatchReconstructOriginal(szSSPath, // original/patched snapshot path
szSSPath); // reconstruct in same directory
if ( dwRes != ERROR_SUCCESS ) { ErrorTrace(0, "! PatchReconstructOriginal : %ld", dwRes); goto Exit; }
::MakeRestorePath( szSSPath, szSysDrv, szRPDir ); dwRes = cSS.InitRestoreSnapshot( szSSPath ); if ( dwRes != ERROR_SUCCESS ) { LPCWSTR cszErr = NULL; cszErr = ::GetSysErrStr( dwRes ); ErrorTrace(0, "cSS.InitResourceSnapshot failed - %ls", cszErr); goto Exit; }
// 4. Restore.
m_pProgress->SetStage( RPS_RESTORE, m_dwTotalEntry ); dwRes = T2DoRestore( FALSE ); if ( dwRes != ERROR_SUCCESS ) { goto Exit; }
// 5. Postprocessing. (?)
// 6. Snapshot handling.
m_pProgress->SetStage( RPS_SNAPSHOT, 0 ); if ( m_fFullRestore ) { dwRes = T2HandleSnapshot( cSS, szSSPath ); if ( dwRes != ERROR_SUCCESS ) { m_pLogFile->WriteMarker(RSTRLOGID_SNAPSHOTFAIL, dwRes); T2UndoForFail(); goto Exit; } } Exit:
// Regardless of whether the restore succeeded or failed, give
// the user the impression that system restore is done with all
// the things it had to do. So call SetStage to set the progress
// bar to 90%. After that we will call Increment to make it go to
// 100%
m_pProgress->SetStage( RPS_SNAPSHOT, 0 ); m_pProgress->Increment(); Sleep(1000);
T2CleanUp();
m_pLogFile->WriteMarker( RSTRLOGID_ENDOFMAP, 0 ); // ignore error...
m_pLogFile->Close(); m_pProgress->Close();
if (dwRes != ERROR_SUCCESS) SetRestoreStatusFailed();
TraceFunctLeave(); return( dwRes ); }
/////////////////////////////////////////////////////////////////////////////
DWORD CRestoreOperationManager::T2Initialize() { TraceFunctEnter("CRestoreOperationManager::T2Initialize"); // Reset the registry flag to clear the disk full error
_VERIFY(TRUE==SetRestoreError(ERROR_SUCCESS)); // clear this error
TraceFunctLeave(); return( ERROR_SUCCESS ); }
/////////////////////////////////////////////////////////////////////////////
/*
// NOTE - 8/1/00 - skkhang
//
// Commented out to incorporate excluding restore map logic.
// But DO NOT delete this until we are comfortable 100% about removing
// restore map.
//
DWORD CRestoreOperationManager::T2CreateMap() { TraceFunctEnter("CRestoreOperationManager::T2CreateMap"); DWORD dwRet = ERROR_INTERNAL_ERROR; LPCWSTR cszErr; DWORD dwLE; HANDLE hfMap = INVALID_HANDLE_VALUE; DWORD dwLastPos = 0; LPCWSTR cszDrv; WCHAR szDSPath[MAX_PATH]; RestoreMapEntry *pRME = NULL; CRestoreMapEntry *pEnt = NULL; int i; SRstrLogHdrV3Ex sHdrEx;
hfMap = ::CreateFile( m_szMapFile, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL ); if ( hfMap == INVALID_HANDLE_VALUE ) { dwRet = ::GetLastError(); //LOGLOG - CreateFile failed...
goto Exit; }
for ( i = 0; i < m_aryDrv.GetSize(); i++ ) { if ( m_aryDrv[i]->IsOffline() || m_aryDrv[i]->IsFrozen() || m_aryDrv[i]->IsExcluded() ) continue;
// set cszDrv to proper drive letters...
cszDrv = m_aryDrv[i]->GetMount(); ::MakeRestorePath( szDSPath, cszDrv, NULL ); DebugTrace(0, "Drive #%d: Drv='%ls', DS='%ls'", i, cszDrv, szDSPath);
dwLastPos = ::SetFilePointer( hfMap, 0, NULL, FILE_CURRENT ); //??? should I check error from this???
dwLE = ::CreateRestoreMap( (LPWSTR)cszDrv, m_dwRPNum, hfMap ); if ( dwLE != ERROR_SUCCESS ) { if ( dwLE != ERROR_NO_MORE_ITEMS ) { cszErr = ::GetSysErrStr( dwLE ); ErrorTrace(0, "::CreateRestoreMap failed - %ls", cszErr); dwRet = dwLE; goto Exit; }
DebugTrace(0, "Nothing to restore in this drive..."); // Some drive might not have any changes in there.
// So gracefully ignore and move to the next drive.
continue; }
::SetFilePointer( hfMap, dwLastPos, NULL, FILE_BEGIN ); dwLE = ::GetLastError(); if ( dwLE != NO_ERROR ) { cszErr = ::GetSysErrStr( dwLE ); ErrorTrace(0, "::SetFilePointer failed - %ls", cszErr); dwRet = dwLE; goto Exit; }
while ( ::ReadRestoreMapEntry( hfMap, &pRME ) == ERROR_SUCCESS ) { pEnt = ::CreateRestoreMapEntry( pRME, cszDrv, szDSPath ); if ( pEnt == NULL ) goto Exit;
if ( !m_aryEnt.AddItem( pEnt ) ) goto Exit; pEnt = NULL; } }
::FreeRestoreMapEntry(pRME); pRME = NULL;
//sHdrEx.dwCount = m_aryEnt.GetSize();
//m_pLogFile->AppendHeader( &sHdrEx );
dwRet = ERROR_SUCCESS; Exit: // clean up the list if necessary...
if ( dwRet != ERROR_SUCCESS ) if ( pRME != NULL ) ::FreeRestoreMapEntry(pRME);
if ( hfMap != INVALID_HANDLE_VALUE ) ::CloseHandle( hfMap ); TraceFunctLeave(); return( dwRet ); } */
DWORD CRestoreOperationManager::T2CreateMap() { TraceFunctEnter("CRestoreOperationManager::T2CreateMap"); DWORD dwRet = ERROR_INTERNAL_ERROR; LPCWSTR cszErr; int nDrv; WCHAR szDrv[MAX_PATH]; WCHAR szDSPath[MAX_PATH]; int i;
m_dwTotalEntry = 0; nDrv = m_aryDrv.GetSize();
if ( nDrv > 0 ) { m_paryEnt = new CRMEArray[nDrv]; if ( m_paryEnt == NULL ) { FatalTrace(0, "Insufficient memory..."); dwRet = ERROR_NOT_ENOUGH_MEMORY; goto Exit; } }
for ( i = 0; i < m_aryDrv.GetSize(); i++ ) { if ( m_aryDrv[i]->IsOffline() || m_aryDrv[i]->IsFrozen() || m_aryDrv[i]->IsExcluded() ) continue;
// use the volume guid for each volume
// we cannot use mountpoint paths because
// restore might delete mount points before
// the operations on that volume are restored
::lstrcpy( szDrv, m_aryDrv[i]->GetID() );
//cszDrv = m_aryDrv[i]->GetMount();
::MakeRestorePath( szDSPath, szDrv, NULL ); DebugTrace(0, "Drive #%d: Drv='%ls', DS='%ls'", i, szDrv, szDSPath);
// Following code assumes descructor of CChangeLogEntryEnum calls FindClose
// automatically.
CChangeLogEntryEnum cEnum( szDrv, 0, m_dwRPNum, FALSE ); CChangeLogEntry cCLE;
dwRet = cEnum.FindFirstChangeLogEntry( cCLE ); if ( dwRet == ERROR_NO_MORE_ITEMS ) continue;
if ( dwRet != ERROR_SUCCESS ) { cszErr = ::GetSysErrStr( dwRet ); ErrorTrace(0, "FindFirstChangeLogEntry failed - %ls", cszErr); goto Exit; }
while ( dwRet == ERROR_SUCCESS ) { if ( !::CreateRestoreMapEntryFromChgLog( &cCLE, szDrv, szDSPath, m_paryEnt[i] ) ) goto Exit;
// Update progress bar.
m_pProgress->Increment();
dwRet = cEnum.FindNextChangeLogEntry( cCLE ); }
if ( dwRet != ERROR_NO_MORE_ITEMS ) { cszErr = ::GetSysErrStr( dwRet ); ErrorTrace(0, "FindNextChangeLogEntry failed - %ls", cszErr); goto Exit; }
m_dwTotalEntry += m_paryEnt[i].GetSize(); }
dwRet = ERROR_SUCCESS; Exit: // clean up the list if necessary...
TraceFunctLeave(); return( dwRet ); }
/////////////////////////////////////////////////////////////////////////////
DWORD CRestoreOperationManager::T2DoRestore( BOOL fUndo ) { TraceFunctEnter("CRestoreOperationManager::T2DoRestore"); DWORD dwRet = ERROR_SUCCESS; DWORD dwRes; DWORD dwErr; ULARGE_INTEGER ulTotalFreeBytes;
for ( m_nDrv = 0; m_nDrv < m_aryDrv.GetSize(); m_nDrv++ ) { for ( m_nEnt = 0; m_nEnt < m_paryEnt[m_nDrv].GetSize(); m_nEnt++ ) { CRestoreMapEntry *pEnt = m_paryEnt[m_nDrv][m_nEnt];
//
// BUGBUG - this code is a workaround for filter logging
// acl ops on FAT drives
//
// check if this volume supports acls
// if not, no-op
//
if (pEnt->GetOpCode() == SrEventAclChange) { WCHAR szLabel[MAX_PATH]; DWORD dwFlags = 0, dwRc; if (::GetVolumeInformation(m_aryDrv[m_nDrv]->GetID(), szLabel, MAX_PATH, NULL, NULL, &dwFlags, NULL, 0)) { if (! (dwFlags & FS_PERSISTENT_ACLS)) { DebugTrace(0, "Ignoring ACL change on non-NTFS drive"); continue; } } else { dwRc = GetLastError(); DebugTrace(0, "! GetVolumeInformation : %ld", dwRc); } }
// Skip dependent entries of locked files
if ( pEnt->GetResult() == RSTRRES_LOCKED ) continue;
// if any .CAT files are modified in the CATROOT directory,
// we need to rebuild the catalog db later
if (StrStrI(pEnt->GetPath1(), L"CatRoot") && StrStrI(pEnt->GetPath1(), L".CAT")) { m_fRebuildCatalogDb = TRUE; } else if (pEnt->GetPath2() != NULL && StrStrI(pEnt->GetPath2(), L"CatRoot") && StrStrI(pEnt->GetPath2(), L".CAT")) { m_fRebuildCatalogDb = TRUE; } //
// check if we have more than 60mb freespace
// if we don't, pre-emptively undo the restore
// 60mb was chosen so that there is a buffer between
// the freeze threshold 50mb and the restore threshold -
// this will avoid the case where we successfully restore
// and freeze immediately after the reboot
//
if (FALSE == GetDiskFreeSpaceEx(m_aryDrv[m_nDrv]->GetID(), NULL, NULL, &ulTotalFreeBytes)) { dwRet = GetLastError(); ErrorTrace(0, "! GetDiskFreeSpaceEx : %ld - ignoring", dwRet); } else { if (ulTotalFreeBytes.QuadPart <= THRESHOLD_RESTORE_DISKSPACE * MEGABYTE) { DebugTrace(0, "***Less than 60MB free - initiating fifo***"); dwRet = T2Fifo( m_nDrv, m_dwRPNum ); if ( dwRet != ERROR_SUCCESS ) { ErrorTrace(0, "! T2Fifo : %ld - ignoring", dwRet); }
//
// get free space again - if still below 60mb, bail
//
if (FALSE == GetDiskFreeSpaceEx(m_aryDrv[m_nDrv]->GetID(), NULL, NULL, &ulTotalFreeBytes)) { dwRet = GetLastError(); ErrorTrace(0, "! GetDiskFreeSpaceEx : %ld - ignoring", dwRet); } else { if (ulTotalFreeBytes.QuadPart <= THRESHOLD_RESTORE_DISKSPACE * MEGABYTE) { DebugTrace(0, "***Still less than 60MB free***");
// if disk is indeed full, set the registry flag to indicate this
// error
_VERIFY(TRUE==SetRestoreError(ERROR_DISK_FULL)); // set this error
if ( !fUndo ) { ErrorTrace(0, "***Initiating Undo***"); T2UndoForFail(); dwRet = ERROR_INTERNAL_ERROR; goto Exit; } } } } } // RESTORE RESTORE RESTORE!!!
pEnt->Restore( this ); dwRes = pEnt->GetResult(); dwErr = pEnt->GetError(); DebugTrace(0, "Res=%d, Err=%d", dwRes, dwErr);
if ( ( dwRes == RSTRRES_FAIL ) && ( dwErr == ERROR_DISK_FULL ) ) { DebugTrace(0, "Disk full, initiating fifo to clean up memory..."); dwRet = T2Fifo( m_nDrv, m_dwRPNum ); if ( dwRet != ERROR_SUCCESS ) goto Exit;
// Try again...
pEnt->Restore( this ); dwRes = pEnt->GetResult(); dwErr = pEnt->GetError(); DebugTrace(0, "Res=%d, Err=%d", dwRes, dwErr); }
// if disk is indeed full, set the registry flag to indicate this
// error
if ( ( dwRes == RSTRRES_FAIL ) && ( dwErr == ERROR_DISK_FULL ) ) { DebugTrace(0, "Restore failed agin because of Disk full. Setting Error"); _VERIFY(TRUE==SetRestoreError(ERROR_DISK_FULL)); // set this error
}
// Locked or Locked_Alt should be handled after finishing normal entries...
if ( ( dwRes == RSTRRES_LOCKED ) || ( dwRes == RSTRRES_LOCKED_ALT ) ) continue;
// if there was a file-directory collision, record the file rename entry first
// so that it will displayed on the results screen
if ( ( pEnt->GetOpCode() == OPR_DIR_CREATE || pEnt->GetOpCode() == OPR_DIR_RENAME || pEnt->GetOpCode() == OPR_FILE_ADD || pEnt->GetOpCode() == OPR_FILE_RENAME ) && ( pEnt->GetResult() == RSTRRES_COLLISION ) ) { // add collision log entry
m_pLogFile->WriteCollisionEntry( pEnt->GetPath1(), pEnt->GetAltPath(), m_aryDrv[m_nDrv]->GetMount() ); pEnt->SetResults(RSTRRES_OK, ERROR_SUCCESS); } // Write log entry
if ( !m_pLogFile->WriteEntry( m_nEnt, pEnt, m_aryDrv[m_nDrv]->GetMount() ) ) goto Exit;
if ( !fUndo && ( pEnt->GetResult() == RSTRRES_FAIL ) ) { ErrorTrace(0, "Failure detected, initiating Undo..."); T2UndoForFail(); dwRet = ERROR_INTERNAL_ERROR; goto Exit; }
// Temporary Hack for directory collision, scan forward.
if ( ( pEnt->GetOpCode() == OPR_DIR_DELETE ) && ( pEnt->GetResult() == RSTRRES_IGNORE ) ) { LPCWSTR cszSrc = pEnt->GetPath1();
for ( int j = m_nEnt+1; j < m_paryEnt[m_nDrv].GetSize(); j++ ) { CRestoreMapEntry *pEnt2 = m_paryEnt[m_nDrv][j]; DWORD dwOpr = pEnt2->GetOpCode();
if ( ( dwOpr == OPR_DIR_CREATE ) || ( dwOpr == OPR_DIR_RENAME ) || ( dwOpr == OPR_FILE_RENAME ) || ( dwOpr == OPR_FILE_ADD ) ) if ( ::StrCmpIW( cszSrc, pEnt2->GetPath1() ) == 0 ) break; } if ( j < m_paryEnt[m_nDrv].GetSize() ) { // found dependent node, current node should be renamed
WCHAR szAlt[SR_MAX_FILENAME_LENGTH];
if ( !::SRGetAltFileName( cszSrc, szAlt ) ) { // Fatal, possible only when total disk failure.
ErrorTrace(0, "Fatal failure, initiating Undo..."); T2UndoForFail(); dwRet = ERROR_INTERNAL_ERROR; goto Exit; }
if ( !::MoveFile( cszSrc, szAlt ) ) { // Failed to rename the directory, so the dependent opr will fail.
// Abort the restore.
LPCWSTR cszErr;
pEnt->SetResults( RSTRRES_FAIL, ::GetLastError() ); cszErr = ::GetSysErrStr( pEnt->GetError() ); ErrorTrace(0, "::MoveFile failed - %s", cszErr); ErrorTrace(0, " Src=%ls", cszSrc); ErrorTrace(0, " New=%ls", szAlt); goto Exit; }
// add collision log entry
m_pLogFile->WriteCollisionEntry( cszSrc, szAlt, m_aryDrv[m_nDrv]->GetMount() ); } }
// Update progress bar.
m_pProgress->Increment(); } }
//
// get the size of the existing movefileex entries
// so that we can skip these when we transfer restore's movefileex entries
// to the old registry
//
DWORD dwType; g_dwExistingMFEXMarker = 0; if (ERROR_SUCCESS != SHGetValue( HKEY_LOCAL_MACHINE, STR_REGPATH_SESSIONMANAGER, STR_REGVAL_MOVEFILEEX, &dwType, NULL, &g_dwExistingMFEXMarker )) { g_dwExistingMFEXMarker = 0; }
trace(0, "g_dwExistingMFEXMarker = %ld", g_dwExistingMFEXMarker);
// Handles locked cases...
for ( m_nDrv = 0; m_nDrv < m_aryDrv.GetSize(); m_nDrv++ ) { for ( m_nEnt = 0; m_nEnt < m_paryEnt[m_nDrv].GetSize(); m_nEnt++ ) { CRestoreMapEntry *pEnt = m_paryEnt[m_nDrv][m_nEnt]; dwRes = pEnt->GetResult(); if ( dwRes == RSTRRES_LOCKED_ALT ) { // Add MoveFileEx entry to delete alt object.
pEnt->ProcessLockedAlt(); } else if ( dwRes == RSTRRES_LOCKED ) { // Add MoveFileEx entry.
pEnt->ProcessLocked(); } else continue;
// Write log entry
if ( !m_pLogFile->WriteEntry( m_nEnt, pEnt, m_aryDrv[m_nDrv]->GetMount() ) ) goto Exit;
// Update progress bar.
m_pProgress->Increment(); } }
Exit: TraceFunctLeave(); return( dwRet ); }
/////////////////////////////////////////////////////////////////////////////
static LPCWSTR s_cszRunOnceValueName = L"*Restore"; static LPCWSTR s_cszRestoreUIPath = L"%SystemRoot%\\system32\\restore\\rstrui.exe"; static LPCWSTR s_cszRunOnceOptNormal = L" -c"; static LPCWSTR s_cszRunOnceOptSilent = L" -b"; static LPCWSTR s_cszCatTimeStamp = L"%SystemRoot%\\system32\\catroot\\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}\\timestamp"; static LPCWSTR s_cszRegLMSWRunOnce = L"Microsoft\\Windows\\CurrentVersion\\RunOnce"; static LPCWSTR s_cszRegLMSWWinLogon = L"Microsoft\\Windows NT\\CurrentVersion\\Winlogon"; static LPCWSTR s_cszRegSystemRestore = L"Microsoft\\Windows NT\\CurrentVersion\\SystemRestore"; static LPCWSTR s_cszRegValSfcScan = L"SfcScan"; static LPCWSTR s_cszRegValAllowProtectedRenames = L"AllowProtectedRenames"; static LPCWSTR s_cszTZKeyInHive = L"CurrentControlSet\\Control\\TimeZoneInformation"; static LPCWSTR s_cszTZKeyInRegistry = L"System\\CurrentControlSet\\Control\\TimeZoneInformation";
#define VALIDATE_DWRET(str) \
if ( dwRet != ERROR_SUCCESS ) \ { \ cszErr = ::GetSysErrStr( dwRet ); \ ErrorTrace(0, str " failed - %ls", cszErr); \ goto Exit; \ } \
DWORD FindDriveMapping(HKEY hk, LPBYTE pSig, DWORD dwSig, LPWSTR pszDrive) { DWORD dwIndex = 0; DWORD dwRet = ERROR_SUCCESS; DWORD dwType, dwSize = MAX_PATH; BYTE rgbSig[1024]; DWORD cbSig = sizeof(rgbSig); LPCWSTR cszErr;
TENTER("FindDriveMapping"); while (ERROR_SUCCESS == (dwRet = RegEnumValue( hk, dwIndex++, pszDrive, &dwSize, NULL, &dwType, rgbSig, &cbSig ))) { if (0 == wcsncmp(pszDrive, L"\\DosDevice", 10)) { if (cbSig == dwSig && (0 == memcmp(rgbSig, pSig, cbSig))) break; } dwSize = MAX_PATH; cbSig = sizeof(rgbSig); }
TLEAVE(); return dwRet; }
DWORD KeepMountedDevices(HKEY hkMount) { HKEY hkNew = NULL, hkOld = NULL; DWORD dwIndex = 0; WCHAR szValue[MAX_PATH], szDrive[MAX_PATH]; BYTE rgbSig[1024]; DWORD cbSig; DWORD dwSize, dwType; DWORD dwRet = ERROR_SUCCESS; LPCWSTR cszErr;
TENTER("KeepMountedDevices"); //
// open the old and new MountedDevices
//
dwRet = ::RegOpenKey( hkMount, L"MountedDevices", &hkOld ); VALIDATE_DWRET("::RegOpenKey");
dwRet = ::RegOpenKey( HKEY_LOCAL_MACHINE, L"System\\MountedDevices", &hkNew ); VALIDATE_DWRET("::RegOpenKey");
//
// enumerate the old devices
// delete volumes that don't exist in the new (i.e. current)
//
dwSize = MAX_PATH; cbSig = sizeof(rgbSig); while (ERROR_SUCCESS == (dwRet = RegEnumValue( hkOld, dwIndex++, szValue, &dwSize, NULL, &dwType, rgbSig, &cbSig ))) { if (0 == wcsncmp(szValue, L"\\??\\Volume", 10)) { //
// this is a Volume -> Signature mapping
// check if the volume exists in the new
//
trace(0, "Old Volume = %S", szValue);
dwSize = sizeof(rgbSig); dwRet = RegQueryValueEx(hkNew, szValue, NULL, &dwType, rgbSig, &dwSize); if (ERROR_SUCCESS != dwRet) { //
// nope
// so delete the volume and driveletter mapping from old
//
DWORD dwSave = FindDriveMapping(hkOld, rgbSig, cbSig, szDrive); dwRet = RegDeleteValue(hkOld, szValue); VALIDATE_DWRET("RegDeleteValue"); if (dwSave == ERROR_SUCCESS) { dwIndex--; // hack to make RegEnumValueEx work
dwRet = RegDeleteValue(hkOld, szDrive); VALIDATE_DWRET("RegDeleteValue"); }
trace(0, "Deleted old volume"); } } else if (szValue[0] == L'#') { trace(0, "Old Mountpoint = %S", szValue); } else if (0 == wcsncmp(szValue, L"\\DosDevice", 10)) { trace(0, "Old Drive = %S", szValue); } else { trace(0, "Old Unknown = %S", szValue); }
dwSize = MAX_PATH; cbSig = sizeof(rgbSig); } if (dwRet != ERROR_NO_MORE_ITEMS) VALIDATE_DWRET("::RegEnumValue");
//
// now enumerate the current (new) devices
//
dwIndex = 0; dwSize = MAX_PATH; cbSig = sizeof(rgbSig); while (ERROR_SUCCESS == (dwRet = RegEnumValue( hkNew, dwIndex++, szValue, &dwSize, NULL, &dwType, rgbSig, &cbSig ))) { if (0 == wcsncmp(szValue, L"\\??\\Volume", 10)) { //
// this is a Volume -> Signature mapping
// copy the new volume to the old
//
trace(0, "New Volume = %S", szValue);
DWORD dwSave = FindDriveMapping(hkOld, rgbSig, cbSig, szDrive);
dwRet = RegSetValueEx(hkOld, szValue, NULL, REG_BINARY, rgbSig, cbSig); VALIDATE_DWRET("::RegSetValueEx");
if (dwSave == ERROR_NO_MORE_ITEMS) { //
// there is no driveletter for this volume in the old registry
// so copy the new one to the old if it exists
//
if (ERROR_SUCCESS == FindDriveMapping(hkNew, rgbSig, cbSig, szDrive)) { dwRet = RegSetValueEx(hkOld, szDrive, NULL, REG_BINARY, rgbSig, cbSig); VALIDATE_DWRET("::RegSetValueEx"); trace(0, "Copied new driveletter %S to old", szDrive); } } else { //
// preserve the old driveletter
//
trace(0, "Preserving old driveletter %S", szDrive); } } else if (szValue[0] == L'#') { //
// this is a mountpoint specification
// don't touch these
//
trace(0, "New Mountpoint = %S", szValue); } else if (0 == wcsncmp(szValue, L"\\DosDevice", 10)) { //
// this is a Driveletter -> Signature mapping
// don't touch these
//
trace(0, "New Drive = %S", szValue); } else { trace(0, "New Unknown = %S", szValue); } dwSize = MAX_PATH; cbSig = sizeof(rgbSig); } if (dwRet == ERROR_NO_MORE_ITEMS) dwRet = ERROR_SUCCESS; VALIDATE_DWRET("::RegEnumValue");
Exit: if (hkOld) RegCloseKey(hkOld);
if (hkNew) RegCloseKey(hkNew); TLEAVE(); return dwRet; }
BOOL DeleteRegKey(HKEY hkOpenKey, const WCHAR * pszKeyNameToDelete) { TraceFunctEnter("DeleteRegKey"); BOOL fRet=FALSE; DWORD dwRet;
// this recursively deletes the key and all its subkeys
dwRet = SHDeleteKey( hkOpenKey, // handle to open key
pszKeyNameToDelete); // subkey name
if (dwRet != ERROR_SUCCESS) { // key does not exist - this is not an error case.
DebugTrace(0, "RegDeleteKey of %S failed ec=%d. Not an error.", pszKeyNameToDelete, dwRet); goto cleanup; }
DebugTrace(0, "RegDeleteKey of %S succeeded", pszKeyNameToDelete); fRet = TRUE; cleanup: TraceFunctLeave(); return fRet; }
DWORD PersistRegKeys( HKEY hkMountedHive, const WCHAR * pszKeyNameInHive, HKEY hkOpenKeyInRegistry, const WCHAR * pszKeyNameInRegistry, const WCHAR * pszKeyBackupFile, WCHAR * pszSnapshotPath) { TraceFunctEnter("PersistRegKeys"); HKEY hKey=NULL; WCHAR szDataFile[MAX_PATH]; LPCWSTR cszErr; DWORD dwRet=ERROR_INTERNAL_ERROR; BOOL fKeySaved; DWORD dwDisposition; // construct the name of the file that stores the backup we will
// construct the name such that the file will get deleted after
// the restore.
wsprintf(szDataFile, L"%s%s\\%s.%s",pszSnapshotPath,SNAPSHOT_DIR_NAME, pszKeyBackupFile, s_cszRegHiveCopySuffix); DeleteFile(szDataFile); // delete the file if it exists
// first load the DRM key to a file
// open the DRM key
dwRet= RegOpenKeyEx(hkOpenKeyInRegistry, // handle to open key
pszKeyNameInRegistry, // name of subkey to open
0, // reserved
KEY_READ, // security access mask
&hKey); // handle to open key
if (dwRet != ERROR_SUCCESS) { // key does not exist - this is not an error case.
DebugTrace(0, "RegOpenKey of %S failed ec=%d", pszKeyNameInRegistry, dwRet); fKeySaved = FALSE; } else { // key exist
dwRet = RegSaveKey( hKey, // handle to key
szDataFile, // data file
NULL); // SD
if (dwRet != ERROR_SUCCESS) { // key does not exist - this is not an error case.
DebugTrace(0, "RegSaveKey of %S failed ec=%d", pszKeyNameInRegistry, dwRet); fKeySaved = FALSE; } else { DebugTrace(0, "Current DRM Key %S saved successfully", pszKeyNameInRegistry); fKeySaved = TRUE; } }
// close the key
if (hKey) { RegCloseKey(hKey); hKey = NULL; } // now replace the snapshotted DRM key with the new key
// first delete the existing key
DeleteRegKey(hkMountedHive, pszKeyNameInHive);
// now check to see if the key existing in the old registry in
// the first place
if (fKeySaved == FALSE) { DebugTrace(0, "Current key %S did not exist. Leaving", pszKeyNameInRegistry); goto Exit; }
// Create the new DRM key
dwRet = RegCreateKeyEx( hkMountedHive, // handle to open key
pszKeyNameInHive, // subkey name
0, // reserved
NULL, // class string
REG_OPTION_NON_VOLATILE, // special options
KEY_ALL_ACCESS, // desired security access
NULL, // inheritance
&hKey, // key handle
&dwDisposition); // disposition value buffer
VALIDATE_DWRET("::RegCreateKeyEx"); _VERIFY(dwDisposition == REG_CREATED_NEW_KEY); dwRet= RegRestoreKey( hKey, // handle to key where restore begins
szDataFile, // registry file
REG_FORCE_RESTORE|REG_NO_LAZY_FLUSH); // options
VALIDATE_DWRET("::RegRestoreKey");
DebugTrace(0, "Successfully kept key %S", pszKeyNameInRegistry); dwRet = ERROR_SUCCESS; Exit: if (hKey) RegCloseKey(hKey); DeleteFile(szDataFile); // delete the file if it exists
TraceFunctLeave(); return dwRet; }
//
// return the next string in multisz pszBuffer
// if no string, will return empty
//
LPWSTR GetNextMszString(LPWSTR pszBuffer) { return pszBuffer + lstrlen(pszBuffer) + 1; }
//
// read regvalue pszString in current registry,
// and replace it in the old registry
//
DWORD ValueReplace(HKEY hkOldSystem, LPWSTR pszOldString, HKEY hkNewSystem, LPWSTR pszNewString) { tenter("ValueReplace"); WCHAR szBuffer[MAX_PATH]; BYTE *pData = NULL; DWORD dwType, dwSize, dwRet = ERROR_SUCCESS; LPWSTR pszValue = NULL; LPCWSTR cszErr; // split up the key and value in pszNewString
lstrcpy(szBuffer, pszNewString); pszValue = wcsrchr(szBuffer, L'\\'); if (! pszValue) { trace(0, "No value in %S", pszNewString); goto Exit; } *pszValue=L'\0'; pszValue++; trace(0, "New Key=%S, Value=%S", szBuffer, pszValue);
// get the value size
dwRet = SHGetValue(hkNewSystem, szBuffer, pszValue, &dwType, NULL, &dwSize); VALIDATE_DWRET("SHGetValue"); pData = (BYTE *) SRMemAlloc(dwSize); if (! pData) { trace(0, "! SRMemAlloc"); dwRet = ERROR_OUTOFMEMORY; goto Exit; }
// get the value
dwRet = SHGetValue(hkNewSystem, szBuffer, pszValue, &dwType, pData, &dwSize); VALIDATE_DWRET("SHGetValue");
// split up the key and value in pszOldString
lstrcpy(szBuffer, pszOldString); pszValue = wcsrchr(szBuffer, L'\\'); if (! pszValue) { trace(0, "No value in %S", pszOldString); goto Exit; } *pszValue=L'\0'; pszValue++; trace(0, "Old Key=%S, Value=%S", szBuffer, pszValue);
// set the value in the old registry
SHSetValue(hkOldSystem, szBuffer, pszValue, dwType, pData, dwSize); VALIDATE_DWRET("SHGetValue");
Exit: if (pData) { SRMemFree(pData); } tleave(); return dwRet; }
//
// list of keys in KeysNotToRestore that we should ignore
//
LPWSTR g_rgKeysToRestore[] = { L"Installed Services", L"Mount Manager", L"Pending Rename Operations", L"Session Manager", L"Plug & Play" }; int g_nKeysToRestore = 5;
//
// check if pszKey is to be preserved or restored
//
BOOL IsKeyToBeRestored(LPWSTR pszKey) { for (int i=0; i < g_nKeysToRestore; i++) { if (lstrcmpi(g_rgKeysToRestore[i], pszKey) == 0) return TRUE; }
return FALSE; }
//
// copy the keys listed in System\CurrentControlSet\Control\BackupRestore\KeysNotToRestore
// from the current registry to the old registry
// -- with some exceptions
//
DWORD PreserveKeysNotToRestore(HKEY hkOldSystem, LPWSTR pszSnapshotPath) { HKEY hkNewSystem = NULL; DWORD dwIndex = 0; WCHAR szName[MAX_PATH], szKey[MAX_PATH]; BYTE *pMszString = NULL; DWORD dwSize, dwType, cbValue; DWORD dwRet = ERROR_SUCCESS; LPCWSTR cszErr; HKEY hkKNTR = NULL; TENTER("PreserveKeysNotToRestore"); //
// open the new system hive
//
dwRet = ::RegOpenKeyEx( HKEY_LOCAL_MACHINE, L"System", 0, KEY_ALL_ACCESS, &hkNewSystem ); VALIDATE_DWRET("::RegOpenKey");
//
// enumerate KeysNotToRestore
//
dwRet = ::RegOpenKeyEx( hkNewSystem, L"CurrentControlSet\\Control\\BackupRestore\\KeysNotToRestore", 0, KEY_READ, &hkKNTR ); VALIDATE_DWRET("::RegOpenKey"); dwSize = MAX_PATH; cbValue = 0; while (ERROR_SUCCESS == (dwRet = RegEnumValue(hkKNTR, dwIndex++, szName, &dwSize, NULL, &dwType, NULL, &cbValue ))) { trace(0, "Name=%S", szName); if (FALSE == IsKeyToBeRestored(szName)) { //
// should preserve all the keys specified in this multisz value
//
LPWSTR pszString = NULL; pMszString = (BYTE *) SRMemAlloc(cbValue); if (NULL == pMszString) { trace(0, "! SRMemAlloc"); dwRet = ERROR_OUTOFMEMORY; goto Exit; }
// read the multisz string
dwRet = RegQueryValueEx(hkKNTR, szName, NULL, &dwType, pMszString, &cbValue); VALIDATE_DWRET("RegQueryValueEx");
// process each element in the multisz string
pszString = (LPWSTR) pMszString; do { // stop on null or empty string
if (! pszString || ! *pszString) break; trace(0, "Key = %S", pszString);
// replace based on the last character of each key
// if '\', then the whole key and subkeys are to be replaced in the old registry
// if '*', it should be merged with the old -- we don't support this and will ignore
// otherwise, it is a value to be replaced
switch (pszString[lstrlen(pszString)-1]) { case L'*' : trace(0, "Merge key - ignoring"); break;
case L'\\': trace(0, "Replacing key"); lstrcpy(szKey, pszString); szKey[lstrlen(szKey)-1]=L'\0'; ChangeCCS(hkOldSystem, szKey); PersistRegKeys(hkOldSystem, // mounted hive
szKey, // key name in hive
hkNewSystem, // open key in registry
pszString, // key name in registry
s_cszDRMKeyBackupFile, // name of backup file
pszSnapshotPath); // snapshot path
break; default: trace(0, "Replacing value"); lstrcpy(szKey, pszString); ChangeCCS(hkOldSystem, szKey); ValueReplace(hkOldSystem, szKey, hkNewSystem, pszString); break; } } while (pszString = GetNextMszString(pszString));
SRMemFree(pMszString); pMszString = NULL; } dwSize = MAX_PATH; cbValue = 0; }
Exit: if (hkNewSystem) RegCloseKey(hkNewSystem);
if (pMszString) { SRMemFree(pMszString); }
if (hkKNTR) { RegCloseKey(hkKNTR); } TLEAVE(); return dwRet; }
DWORD RestorePendingRenames(LPWSTR pwcBuffer, LPWSTR pszSSPath) { TraceFunctEnter("RestorePendingRenames"); WCHAR szSrc[MAX_PATH]; DWORD dwRc = ERROR_SUCCESS; int iFirst = 0; int iSecond = 0; int iFile = 1;
while (pwcBuffer[iFirst] != L'\0') { iSecond = iFirst + lstrlenW(&pwcBuffer[iFirst]) + 1; DebugTrace(0, "Src : %S, Dest : %S", &pwcBuffer[iFirst], &pwcBuffer[iSecond]); if (pwcBuffer[iSecond] != L'\0') { // restore the snapshot file MFEX-i.DAT in the snapshot dir
// to the source file
wsprintf(szSrc, L"%s%s\\MFEX-%d.DAT", pszSSPath, SNAPSHOT_DIR_NAME, iFile++); DebugTrace(0, "%S -> %S", szSrc, &pwcBuffer[iFirst+4]); SRCopyFile(szSrc, &pwcBuffer[iFirst+4]); } iFirst = iSecond + lstrlenW(&pwcBuffer[iSecond]) + 1; }
TraceFunctLeave(); return dwRc; }
static DWORD HandleSoftwareHive( LPCWSTR cszDat, WCHAR * pszSnapshotPath ) { TraceFunctEnter("HandleSoftwareHive"); DWORD dwRet,dwSafeMode; LPCWSTR cszErr; BOOL fRegLoaded = FALSE; HKEY hkMount = NULL; // HKEY of temporary mount point of registry file
WCHAR szUIPath[MAX_PATH]=L"";
// 1. Load registry-to-be-restored temporarily.
dwRet = ::RegLoadKey( HKEY_LOCAL_MACHINE, s_cszRegHiveTmp, cszDat ); VALIDATE_DWRET("::RegLoadKey"); fRegLoaded = TRUE; dwRet = ::RegOpenKey( HKEY_LOCAL_MACHINE, s_cszRegHiveTmp, &hkMount ); VALIDATE_DWRET("::RegOpenKey");
// 2.1 Set RunOnce key for result page.
::ExpandEnvironmentStrings( s_cszRestoreUIPath, szUIPath, MAX_PATH ); ::lstrcat( szUIPath, s_cszRunOnceOptNormal ); if ( !::SRSetRegStr( hkMount, s_cszRegLMSWRunOnce, s_cszRunOnceValueName, szUIPath ) ) goto Exit;
#if 0
// 2.2 Set SfcScan key to initiate WFP scanning after the restore.
if ( !::SRSetRegDword( hkMount, s_cszRegLMSWWinLogon, s_cszRegValSfcScan, 2 ) ) goto Exit; #endif
// 2.3 Set RestoreStatus value to 1 to denote restore success
// test tools can use this when invoking silent restores to check success
if ( !::SRSetRegDword( hkMount, s_cszRegSystemRestore, s_cszRestoreStatus, 1 ) ) { // ignore the error since this is not a fatal error
ErrorTrace(0,"SRSetRegDword failed.ec=%d", GetLastError()); }
// Write in the registry whether we are doing a restore from safe mode.
if (0 != GetSystemMetrics(SM_CLEANBOOT)) { TRACE(0, "Restore from safemode"); dwSafeMode=1; } else { dwSafeMode=0; } // now write in the new registry about the status
if ( !::SRSetRegDword( hkMount, s_cszRegSystemRestore, s_cszRestoreSafeModeStatus, dwSafeMode ) ) { // ignore the error since this is not a fatal error
ErrorTrace(0,"SRSetRegDword of safe mode status failed.ec=%d", GetLastError()); } // 3. also set the new DRM keys
{ WCHAR szDRMKeyNameInHive[MAX_PATH]; // ignore the error code since this is not a fatal error
wsprintf(szDRMKeyNameInHive, L"Classes\\%s",s_cszDRMKey1); PersistRegKeys(hkMount, // mounted hive
szDRMKeyNameInHive, // key name in hive
HKEY_CLASSES_ROOT, // open key in registry
s_cszDRMKey1, // key name in registry
s_cszDRMKeyBackupFile, // name of backup file
pszSnapshotPath); // snapshot path
wsprintf(szDRMKeyNameInHive, L"Classes\\%s",s_cszDRMKey2); PersistRegKeys(hkMount, // mounted hive
szDRMKeyNameInHive, // key name in hive
HKEY_CLASSES_ROOT, // open key in registry
s_cszDRMKey2, // key name in registry
s_cszDRMKeyBackupFile, // name of backup file
pszSnapshotPath); // snapshot path
} // also ignore the Remote assistance reg key
{ WCHAR szRAKeyInRegistry[MAX_PATH]; wsprintf(szRAKeyInRegistry, L"%s\\%s",s_cszSoftwareHiveName, s_cszRemoteAssistanceKey); PersistRegKeys(hkMount, // mounted hive
s_cszRemoteAssistanceKey, // key name in hive
HKEY_LOCAL_MACHINE, // open key in registry
szRAKeyInRegistry, // key name in registry
s_cszDRMKeyBackupFile, // name of backup file
pszSnapshotPath); // snapshot path
}
// also ignore the Password Hints key
{ WCHAR szHintKeyInRegistry[MAX_PATH];
wsprintf(szHintKeyInRegistry, L"%s\\%s",s_cszSoftwareHiveName, s_cszPasswordHints);
PersistRegKeys(hkMount, // mounted hive
s_cszPasswordHints, // key name in hive
HKEY_LOCAL_MACHINE, // open key in registry
szHintKeyInRegistry, // key name in registry
s_cszDRMKeyBackupFile, // name of backup file
pszSnapshotPath); // snapshot path
} // also ignore the IE Content Advisor key
{ WCHAR szContentAdvisorKeyInRegistry[MAX_PATH];
wsprintf(szContentAdvisorKeyInRegistry, L"%s\\%s", s_cszSoftwareHiveName, s_cszContentAdvisor);
PersistRegKeys(hkMount, // mounted hive
s_cszContentAdvisor, // key name in hive
HKEY_LOCAL_MACHINE, // open key in registry
szContentAdvisorKeyInRegistry,// key name in registry
s_cszDRMKeyBackupFile, // name of backup file
pszSnapshotPath); // snapshot path
}
// 4. Save the LSA secrets
GetLsaRestoreState (hkMount); Exit: if ( hkMount != NULL ) (void)::RegCloseKey( hkMount ); if ( fRegLoaded ) (void)::RegUnLoadKey( HKEY_LOCAL_MACHINE, s_cszRegHiveTmp );
TraceFunctLeave(); return( dwRet ); }
static DWORD HandleSystemHive( LPCWSTR cszDat, LPWSTR pszSSPath ) { TraceFunctEnter("HandleSystemHive"); DWORD dwRet; LPCWSTR cszErr; WCHAR szWPAKeyNameInHive[MAX_PATH]; BOOL fRegLoaded = FALSE; HKEY hkMount = NULL; // HKEY of temporary mount point of registry file
LPWSTR szRestoreMFE = NULL, szOldMFE = NULL; BYTE * pszData = NULL, *pNewPos = NULL; DWORD cbData1=0, cbData2=0, cbData=0; DWORD dwCurrent = 1; WCHAR szSessionManager[MAX_PATH]; // 1. Load registry-to-be-restored temporarily.
dwRet = ::RegLoadKey( HKEY_LOCAL_MACHINE, s_cszRegHiveTmp, cszDat ); VALIDATE_DWRET("::RegLoadKey"); fRegLoaded = TRUE; dwRet = ::RegOpenKey( HKEY_LOCAL_MACHINE, s_cszRegHiveTmp, &hkMount ); VALIDATE_DWRET("::RegOpenKey");
// get the session manager regkey
lstrcpy(szSessionManager, s_cszRegLMSYSSessionMan); ChangeCCS(hkMount, szSessionManager);
//persist WPA registry keys
wsprintf(szWPAKeyNameInHive, L"%s", s_cszWPAKeyRelative); PersistRegKeys( hkMount,// mounted hive
szWPAKeyNameInHive,// key name in hive
HKEY_LOCAL_MACHINE,// open key in registry
s_cszWPAKey,// key name in registry
s_cszDRMKeyBackupFile,// name of backup file
pszSSPath); // snapshot path
//
// process KeysNotToRestore key
// and transfer listed keys to old system hive
//
PreserveKeysNotToRestore(hkMount, pszSSPath);
// process movefileex entries from old registry
szOldMFE = ::SRGetRegMultiSz( hkMount, szSessionManager, SRREG_VAL_MOVEFILEEX, &cbData1 ); if (szOldMFE != NULL) { dwRet = RestorePendingRenames(szOldMFE, pszSSPath); VALIDATE_DWRET("RestorePendingRenames"); } // copy restore's movefileex entries
//
// skip entries that were already there before restore began
//
szRestoreMFE = ::SRGetRegMultiSz( HKEY_LOCAL_MACHINE, SRREG_PATH_SESSIONMGR, SRREG_VAL_MOVEFILEEX, &cbData2 ); if ( cbData2 > g_dwExistingMFEXMarker && szRestoreMFE != NULL ) { trace(0, "Restore MFE entries exist");
if (g_dwExistingMFEXMarker > 0) { szRestoreMFE = (LPWSTR) ((BYTE *) szRestoreMFE + g_dwExistingMFEXMarker - sizeof(WCHAR)); cbData2 -= g_dwExistingMFEXMarker - sizeof(WCHAR); } DebugTrace(0, "RestoreMFE:%S, cbData2:%ld", szRestoreMFE, cbData2); // allocate memory for old and new entries
pszData = (BYTE *) malloc(cbData1 + cbData2); if (! pszData) { ErrorTrace(0, "! malloc"); dwRet = ERROR_OUTOFMEMORY; goto Exit; }
// append old entries AFTER restore's entries
cbData = 0; if (szRestoreMFE != NULL) { memcpy(pszData, szRestoreMFE, cbData2);
// truncate last '\0' if more to append
if (szOldMFE != NULL) { cbData = cbData2 - sizeof(WCHAR); pNewPos = pszData + cbData; } else { cbData = cbData2; pNewPos = pszData; } }
if (szOldMFE != NULL) { memcpy(pNewPos, szOldMFE, cbData1); cbData += cbData1; } if ( !::SRSetRegMultiSz( hkMount, szSessionManager, SRREG_VAL_MOVEFILEEX, (LPWSTR) pszData, cbData ) ) { ErrorTrace(0, "! SRSetRegMultiSz"); goto Exit; }
free(pszData); // Set AllowProtectedRenames key for MoveFileEx.
if ( !::SRSetRegDword( hkMount, szSessionManager, s_cszRegValAllowProtectedRenames, 1 ) ) goto Exit; }
// get the timezone regkey
lstrcpy(szSessionManager, s_cszTZKeyInHive); ChangeCCS(hkMount, szSessionManager); //
// transfer timezone information from new registry to old registry
// ie. don't restore timezone, since we can't restore time
//
PersistRegKeys( hkMount, szSessionManager, HKEY_LOCAL_MACHINE, s_cszTZKeyInRegistry, s_cszDRMKeyBackupFile, pszSSPath);
//
// use the currently existing mounted devices info
// i.e. all current volumes will be put back into the old registry
// however for volumes that existed in the old registry,
// the driveletter mapping from the old registry will be used
//
// dwRet = KeepMountedDevices(hkMount);
// VALIDATE_DWRET("KeepMountedDevices");
// Register password filter DLL to set old->new passwords
dwRet = RegisterNotificationDLL (hkMount, TRUE); VALIDATE_DWRET("RegisterNotificationDLL"); Exit: if ( szRestoreMFE != NULL ) delete [] szRestoreMFE; if ( szOldMFE != NULL ) delete [] szOldMFE; if ( hkMount != NULL ) (void)::RegCloseKey( hkMount ); if ( fRegLoaded ) (void)::RegUnLoadKey( HKEY_LOCAL_MACHINE, s_cszRegHiveTmp );
TraceFunctLeave(); return( dwRet ); }
DWORD CRestoreOperationManager::T2HandleSnapshot( CSnapshot & cSS, WCHAR * szSSPath ) { TraceFunctEnter("CRestoreOperationManager::T2HandleSnapshot"); DWORD dwRet; LPCWSTR cszErr; WCHAR szRegHive[MAX_PATH]; WCHAR szCatTSPath[MAX_PATH]; // Full path of CatRoot\TimeStamp
// 1. Initialize Snapshot Handling Module. ( already done by caller )
// 2. Manipulate HKLM\SOFTWARE Hive.
dwRet = cSS.GetSoftwareHivePath( szSSPath, szRegHive, MAX_PATH ); VALIDATE_DWRET("CSnapshot::GetSoftwareHivePath"); LogDSFileTrace(0,L"SWHive: ", szRegHive);
dwRet = ::HandleSoftwareHive( szRegHive, szSSPath); if ( dwRet != ERROR_SUCCESS ) goto Exit;
// 3. Manipulate HKLM\SYSTEM Hive.
dwRet = cSS.GetSystemHivePath( szSSPath, szRegHive, MAX_PATH ); VALIDATE_DWRET("CSnapshot::GetSystemHivePath"); LogDSFileTrace(0,L"SysHive: ", szRegHive);
dwRet = ::HandleSystemHive( szRegHive, szSSPath ); if ( dwRet != ERROR_SUCCESS ) goto Exit;
// 3.5 Manipulate the HKLM\SAM Hive.
dwRet = cSS.GetSamHivePath ( szSSPath, szRegHive, MAX_PATH ); VALIDATE_DWRET("CSnapshot::GetSamHivePath"); LogDSFileTrace(0,L"SamHive: ", szRegHive);
dwRet = RestoreRIDs ( szRegHive ); if (dwRet != ERROR_SUCCESS) goto Exit;
// 4. Restore the Snapshot.
dwRet = cSS.RestoreSnapshot( szSSPath ); //VALIDATE_DWRET("CSnapshot::RestoreSnapshot");
// 5. Cleanup Snapshot Handling Module.
//dwRet = ::CleanupAfterRestore( szSSPath );
//VALIDATE_DWRET("CSnapshot::CleanupAfterRestore");
// 6. Delete timestamp file for WFP
if (m_fRebuildCatalogDb == TRUE) { (void)::ExpandEnvironmentStrings( s_cszCatTimeStamp, szCatTSPath, MAX_PATH ); if ( !::DeleteFile( szCatTSPath ) ) { cszErr = ::GetSysErrStr(); DebugTrace(0, "::DeleteFile(timestamp) failed - %ls", cszErr); // ignore error...
} } Exit: TraceFunctLeave(); return( dwRet ); }
/////////////////////////////////////////////////////////////////////////////
DWORD CRestoreOperationManager::T2CleanUp() { TraceFunctEnter("CRestoreOperationManager::T2CleanUp"); int i;
for ( i = m_aryDrv.GetUpperBound(); i >= 0; i-- ) m_paryEnt[i].ReleaseAll(); delete [] m_paryEnt;
m_aryDrv.DeleteAll();
TraceFunctLeave(); return( ERROR_SUCCESS ); }
DWORD WriteFifoLog(LPWSTR pszLog, LPWSTR pwszDir, LPWSTR pwszDrive) { FILE *f = NULL; WCHAR szLog[MAX_PATH]; DWORD dwRc = ERROR_INTERNAL_ERROR; WCHAR wszTime[MAX_PATH] = L""; WCHAR wszDate[MAX_PATH] = L""; CDataStore *pds = NULL; TENTER("WriteFifoLog");
TRACE(0, "Fifoed %S on drive %S", pwszDir, pwszDrive);
f = (FILE *) _wfopen(szLog, L"a"); if (f) { _wstrdate(wszDate); _wstrtime(wszTime); fwprintf(f, L"%s-%s : Fifoed %s on drive %s\n", wszDate, wszTime, pwszDir, pwszDrive); fclose(f); dwRc = ERROR_SUCCESS; } else { TRACE(0, "_wfopen failed on %s", szLog); } TLEAVE(); return dwRc; }
/////////////////////////////////////////////////////////////////////////////
DWORD CRestoreOperationManager::T2Fifo( int nDrv, DWORD dwRpNum ) { TraceFunctEnter("CRestoreOperationManager::T2Fifo"); DWORD dwErr = ERROR_SUCCESS; CDriveTable dt; CDataStore *pds = NULL; BOOL fFifoed = FALSE; DWORD dwLastFifoedRp; WCHAR szFifoedRpPath[MAX_PATH]; CDataStore *pdsLead = NULL, *pdsSys = NULL; BOOL fFirstIteration; SDriveTableEnumContext dtec = {NULL, 0}; WCHAR szPath[MAX_PATH], szSys[MAX_PATH]; WCHAR szFifoedPath[MAX_PATH], szRpPath[MAX_PATH]; DWORD dwTargetRPNum = 0; WCHAR szLog[MAX_PATH]; ::GetSystemDrive(szSys); MakeRestorePath(szPath, szSys, s_cszDriveTable); CHECKERR(dt.LoadDriveTable(szPath), L"LoadDriveTable");
pdsSys = dt.FindSystemDrive(); if (pdsSys == NULL) { TRACE(0, "! FindSystemDrive"); goto Err; } MakeRestorePath(szLog, pdsSys->GetDrive(), s_cszFifoLog); pdsLead = NULL; fFirstIteration = TRUE; pds = dt.FindDriveInTable((LPWSTR) m_aryDrv[nDrv]->GetID()); while (pds) { fFifoed = FALSE; //
// skip the drive we fifoed first
//
if (pds != pdsLead) { //
// enum forward, don't skip last
//
CRestorePointEnum rpe( pds->GetDrive(), TRUE, FALSE ); CRestorePoint rp;
//
// blow away any obsolete "Fifoed" directories
//
MakeRestorePath(szFifoedRpPath, pds->GetDrive(), s_cszFifoedRpDir); CHECKERR( Delnode_Recurse(szFifoedRpPath, TRUE, NULL), "Delnode_Recurse");
//
// blow away any obsolete "RP0" directories
//
MakeRestorePath(szFifoedRpPath, pds->GetDrive(), L"RP0"); CHECKERR( Delnode_Recurse(szFifoedRpPath, TRUE, NULL), "Delnode_Recurse");
//
// loop through restore points on this drive
//
dwErr = rpe.FindFirstRestorePoint (rp);
//
// enumeration can return ERROR_FILE_NOT_FOUND for restorepoints
// that are missing rp.log
// we will just continue in this case
//
while (dwErr == ERROR_SUCCESS || dwErr == ERROR_FILE_NOT_FOUND) { //
// check if we've reached the target RP num
//
if (dwTargetRPNum) { if (rp.GetNum() > dwTargetRPNum) { TRACE(0, "Target restore point reached"); break; } } //
// check if we've reached the selected rp
//
if (rp.GetNum() >= dwRpNum) { //
// don't fifo current rp
//
trace(0, "No more rps to fifo"); break; }
//
// throw away this restore point on this drive
//
// move the rp dir to a temp dir "Fifoed"
// this is to make the fifo of a single rp atomic
// to take care of unclean shutdowns
MakeRestorePath(szRpPath, pds->GetDrive(), rp.GetDir()); MakeRestorePath(szFifoedPath, pds->GetDrive(), s_cszFifoedRpDir); if (! MoveFile(szRpPath, szFifoedPath)) { dwErr = GetLastError(); TRACE(0, "! MoveFile from %S to %S : %ld", szRpPath, szFifoedPath, dwErr); goto Err; }
// blow away the temp fifoed directory
CHECKERR(Delnode_Recurse(szFifoedPath, TRUE, NULL), L"Delnode_Recurse"); dwLastFifoedRp = rp.GetNum(); fFifoed = TRUE;
//
// write to the fifo log
//
WriteFifoLog(szLog, rp.GetDir(), pds->GetDrive()); dwErr = rpe.FindNextRestorePoint(rp); } }
//
// go to next drive
//
if (fFirstIteration) { if (! fFifoed) // we did not fifo anything
{ break; } pdsLead = pds; pds = dt.FindFirstDrive(dtec); fFirstIteration = FALSE; dwTargetRPNum = dwLastFifoedRp; // fifo till what we fifoed just now
} else { pds = dt.FindNextDrive(dtec); } }
dwErr = ERROR_SUCCESS; Err: TraceFunctLeave(); return( dwErr ); }
DWORD DeleteAllChangeLogs(WCHAR * pszRestorePointPath) { TraceFunctEnter("DeleteAllFilesBySuffix"); DWORD dwErr, dwReturn=ERROR_INTERNAL_ERROR; WCHAR szFindFileData[MAX_PATH]; // first construct the prefix of the file that stores the HKLM registry
// snapshot.
wsprintf(szFindFileData, L"%s\\%s*", pszRestorePointPath, s_cszCurrentChangeLog); dwErr = ProcessGivenFiles(pszRestorePointPath, DeleteGivenFile, szFindFileData); if (ERROR_SUCCESS != dwErr) { ErrorTrace(0, "Deleting files failed error %ld", dwErr); dwReturn = dwErr; goto cleanup; } dwReturn = ERROR_SUCCESS; cleanup: TraceFunctLeave(); return dwReturn; }
/////////////////////////////////////////////////////////////////////////////
// Starting from the new "Restore" type restore point, enumerate
// change log entries and undo them. The order should be reverse (from
// the latest operation to the earliest operation.)
DWORD CRestoreOperationManager::T2UndoForFail() { TraceFunctEnter("CRestoreOperationManager::T2UndoForFail"); DWORD dwRet = ERROR_INTERNAL_ERROR; LPCWSTR cszErr; HANDLE hFilter = NULL; WCHAR szDrv[MAX_PATH]; WCHAR szRestorePointPath[MAX_PATH]; WCHAR szDSPath[MAX_PATH]; int i;
// Cleanup m_aryEnt to free up as much memory as possible and prepare
// to get the list of operations to be undone.
for ( i = m_aryDrv.GetUpperBound(); i >= 0; i-- ) m_paryEnt[i].ReleaseAll();
m_pLogFile->WriteMarker( RSTRLOGID_STARTUNDO, 0 );
// Stop filter from monitoring
dwRet = ::SrCreateControlHandle( SR_OPTION_OVERLAPPED, &hFilter ); if ( dwRet != ERROR_SUCCESS ) { ErrorTrace(0, "::SrCreateControlHandle failed - %d", dwRet); //One reason this can happen is if the SR service is still running.
// Stop the service and try again
if (IsSRServiceRunning() ) { StopSRService(TRUE); // wait until service is stopped
dwRet = ::SrCreateControlHandle( SR_OPTION_OVERLAPPED, &hFilter ); if ( dwRet != ERROR_SUCCESS ) { ErrorTrace(0, "::SrCreateControlHandle failed again - %d", dwRet); } } if ( dwRet != ERROR_SUCCESS ) { //ISSUE - should I abort or continue???
goto Exit; } } dwRet = ::SrStopMonitoring( hFilter ); if ( dwRet != ERROR_SUCCESS ) { ErrorTrace(0, "::SrStopMonitoring failed - %ls", ::GetSysErrStr(dwRet)); //ISSUE - should I abort or continue???
goto Exit; }
// Get change log entries to be undone
for ( i = 0; i < m_aryDrv.GetSize(); i++ ) { if ( m_aryDrv[i]->IsOffline() || m_aryDrv[i]->IsFrozen() || m_aryDrv[i]->IsExcluded() ) continue;
// use the volume guid for each volume
// we cannot use mountpoint paths because
// restore might delete mount points before
// the operations on that volume are restored
::lstrcpy( szDrv, m_aryDrv[i]->GetID() ); //cszDrv = m_aryDrv[i]->GetMount();
::MakeRestorePath( szDSPath, szDrv, NULL ); DebugTrace(0, "Drive #%d: Drv='%ls', DS='%ls'", i, szDrv, szDSPath);
CChangeLogEntryEnum cEnum( szDrv, 0, m_dwRPNew, TRUE ); CChangeLogEntry cCLE;
dwRet = cEnum.FindFirstChangeLogEntry( cCLE ); if ( dwRet == ERROR_NO_MORE_ITEMS ) goto EndOfChgLog;
if ( dwRet != ERROR_SUCCESS ) { cszErr = ::GetSysErrStr( dwRet ); ErrorTrace(0, "FindFirstChangeLogEntry failed - %ls", cszErr); // even in case of error, try to revert as many operations as possible...
goto EndOfChgLog; }
while ( dwRet == ERROR_SUCCESS ) { if ( !::CreateRestoreMapEntryFromChgLog( &cCLE, szDrv, szDSPath, m_paryEnt[i] ) ) { // even in case of error, try to revert as many operations as possible...
goto EndOfChgLog; }
dwRet = cEnum.FindNextChangeLogEntry( cCLE ); }
if ( dwRet != ERROR_NO_MORE_ITEMS ) { cszErr = ::GetSysErrStr( dwRet ); ErrorTrace(0, "FindNextChangeLogEntry failed - %ls", cszErr); // even in case of error, try to revert as many operations as possible...
goto EndOfChgLog; }
EndOfChgLog: cEnum.FindClose(); }
// UNDO!!!
dwRet = T2DoRestore( TRUE ); if ( dwRet != ERROR_SUCCESS ) goto Exit;
// Nuke everything in the RP directory
// Get change logs to be deleted
for ( i = 0; i < m_aryDrv.GetSize(); i++ ) { if ( m_aryDrv[i]->IsOffline() || m_aryDrv[i]->IsFrozen() || m_aryDrv[i]->IsExcluded() ) continue;
// set cszDrv to proper drive letters...
::lstrcpy( szDrv, m_aryDrv[i]->GetMount() ); if ( szDrv[2] == L'\0' ) { szDrv[2] = L'\\'; szDrv[3] = L'\0'; } ::MakeRestorePath( szDSPath, szDrv, NULL ); wsprintf(szRestorePointPath, L"%s\\%s%d",szDSPath, s_cszRPDir, m_dwRPNew); LogDSFileTrace(0, L"Deleting changelogs from ", szRestorePointPath); DeleteAllChangeLogs(szRestorePointPath); }
//
// change restorestatus in the registry to indicate that revert happened
// successfully
//
SetRestoreStatusFailed(); dwRet = ERROR_SUCCESS; Exit: m_pLogFile->WriteMarker( RSTRLOGID_ENDOFUNDO, 0 );
TraceFunctLeave(); return( dwRet ); }
/////////////////////////////////////////////////////////////////////////////
//
// CreateRestoreOperationManager function
//
/////////////////////////////////////////////////////////////////////////////
BOOL CreateRestoreOperationManager( CRestoreOperationManager **ppROMgr ) { TraceFunctEnter("CreateRestoreOperationManager"); BOOL fRet = FALSE; CRestoreOperationManager *pROMgr=NULL;
if ( ppROMgr == NULL ) { FatalTrace(0, "Invalid parameter, ppROMgr is NULL."); goto Exit; } *ppROMgr = NULL;
pROMgr = new CRestoreOperationManager; if ( pROMgr == NULL ) { FatalTrace(0, "Insufficient memory..."); goto Exit; }
if ( !pROMgr->Init() ) goto Exit;
*ppROMgr = pROMgr;
fRet = TRUE; Exit: if ( !fRet ) SAFE_RELEASE(pROMgr); TraceFunctLeave(); return( fRet ); }
// end of file
|