Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2476 lines
77 KiB

/******************************************************************************
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