windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/wp/sync/mdsync.cpp
2020-09-26 16:20:57 +08:00

2967 lines
68 KiB
C++

// MdSync.cpp : Implementation of CSyncApp and DLL registration.
extern "C" {
#include "nt.h"
#include "ntrtl.h"
#include "nturtl.h"
#include "windows.h"
#include "stdio.h"
#include "stdlib.h"
} // extern "C"
#include <limits.h>
#include <ole2.h>
#include <wincrypt.h>
#include <dbgutil.h>
#include <buffer.hxx>
#include "mdsync.h"
#include "stdafx.h"
#include <iadmext.h>
#define ADMEX
#if defined(ADMEX)
#undef DEFINE_GUID
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
EXTERN_C const GUID name \
= { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
#include <admex.h>
#undef DEFINE_GUID
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
EXTERN_C const GUID FAR name
#endif
#include "mdsync.hxx"
//
#include "comrepl_i.c"
#include "comrepl.h"
//
// Global Functions
//
HRESULT
MTS_Propagate2
(
/* [in] */ DWORD dwBufferSize,
/* [size_is][in] */ unsigned char __RPC_FAR *pszBuffer,
/* [in] */ DWORD dwSignatureMismatch
);
//
// Globals
//
DWORD g_dwFalse = FALSE;
const INT COMPUTER_CHARACTER_SIZE = 64;
/////////////////////////////////////////////////////////////////////////////
//
CProps::CProps(
)
/*++
Routine Description:
Property list constructor
Arguments:
None
Returns:
Nothing
--*/
{
m_Props = NULL;
m_dwProps = m_dwLenProps = 0;
m_lRefCount = 0;
}
CProps::~CProps(
)
/*++
Routine Description:
Property list destructor
Arguments:
None
Returns:
Nothing
--*/
{
if ( m_Props )
{
LocalFree( m_Props );
}
}
CNodeDesc::CNodeDesc(
CSync* pSync
)
/*++
Routine Description:
Metabase node descriptor constructor
Arguments:
pSync - ptr to synchronizer object
Returns:
Nothing
--*/
{
InitializeListHead(&m_ChildHead);
m_pszPath = NULL;
m_pSync = pSync;
m_fHasProps = FALSE;
m_fHasObjs = FALSE;
}
CNodeDesc::~CNodeDesc(
)
/*++
Routine Description:
Metabase node descriptor destructor
Arguments:
None
Returns:
Nothing
--*/
{
LIST_ENTRY* pChild;
CNodeDesc* pNode;
if ( m_pszPath )
{
LocalFree( m_pszPath );
}
while ( !IsListEmpty( &m_ChildHead ))
{
pNode = CONTAINING_RECORD( m_ChildHead.Flink,
CNodeDesc,
m_ChildList );
RemoveEntryList( &pNode->m_ChildList );
delete pNode;
}
}
BOOL
CNodeDesc::BuildChildObjectsList(
CMdIf* pMd,
LPWSTR pszPath
)
/*++
Routine Description:
Build list of child object of this node
Arguments:
pMd - metabase admin interface
pszPath - path of current node
Returns:
Nothing
--*/
{
CNodeDesc* pChild;
WCHAR achPath[METADATA_MAX_NAME_LEN*2];
WCHAR achSrcPath[METADATA_MAX_NAME_LEN];
DWORD dwP = wcslen( pszPath );
UINT i;
DWORD dwRequired;
//
// Ugly path trick : metabase will remove trailing '/',
// so to specify an empty directory at the end of path
// must add an additional trailing '/'
//
memcpy( achSrcPath, pszPath, (dwP + 1) * sizeof(WCHAR) );
if ( dwP && pszPath[dwP-1] == L'/' )
{
achSrcPath[dwP] = L'/';
achSrcPath[dwP+1] = L'\0';
}
memcpy( achPath, pszPath, dwP * sizeof(WCHAR) );
achPath[dwP++] = L'/';
//
// enumerate child
//
for ( i = 0 ; ; ++i )
{
if ( pMd->Enum( achSrcPath, i, achPath+dwP ) )
{
if ( pChild = new CNodeDesc( m_pSync ) )
{
pChild->SetPath( achPath );
InsertHeadList( &m_ChildHead, &pChild->m_ChildList );
}
else
{
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
return FALSE;
}
}
else if ( GetLastError() != ERROR_NO_MORE_ITEMS )
{
return FALSE;
}
else
{
break;
}
}
return TRUE;
}
BOOL
CProps::GetAll(
CMdIf* pMd,
LPWSTR pszPath
)
/*++
Routine Description:
Get all properties for this node
Arguments:
pMd - metabase admin interface
pszPath - path of current node
Returns:
Nothing
--*/
{
DWORD dwRec;
DWORD dwDataSet;
BYTE abBuff[4096];
DWORD dwRequired;
if ( pMd->GetAllData( pszPath, &dwRec, &dwDataSet, abBuff, sizeof(abBuff), &dwRequired ) )
{
//
// MetaBase does not update dwRequired supplied buffer is big enough
// we must assume the whole buffer was used.
//
dwRequired = sizeof(abBuff);
m_Props = (LPBYTE)LocalAlloc( LMEM_FIXED, dwRequired );
if ( !m_Props )
{
return FALSE;
}
m_dwProps = dwRec;
m_dwLenProps = dwRequired;
memcpy( m_Props, abBuff, dwRequired );
return TRUE;
}
else if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
{
m_Props = (LPBYTE)LocalAlloc( LMEM_FIXED, dwRequired );
if ( !m_Props )
{
return FALSE;
}
if ( pMd->GetAllData( pszPath, &dwRec, &dwDataSet, m_Props, dwRequired, &dwRequired ) )
{
m_dwLenProps = dwRequired;
m_dwProps = dwRec;
return TRUE;
}
LocalFree( m_Props );
m_Props = NULL;
}
return FALSE;
}
BOOL
CSync::GetProp(
LPWSTR pszPath,
DWORD dwPropId,
DWORD dwUserType,
DWORD dwDataType,
LPBYTE* ppBuf,
LPDWORD pdwLen
)
/*++
Routine Description:
Get property for path
Arguments:
pszPath - path of current node
dwPropId - metadata property ID
dwUserType - metadata user type
dwDataType - metadata data type
ppBuf - update with ptr to LocalAlloc'ed buffer or NULL if error
pdwLen - updated with length
Returns:
Nothing
--*/
{
DWORD dwRec;
DWORD dwDataSet;
DWORD dwRequired;
METADATA_RECORD md;
memset( &md, '\0', sizeof(md) );
md.dwMDDataType = dwDataType;
md.dwMDUserType = dwUserType;
md.dwMDIdentifier = dwPropId;
md.dwMDDataLen = 0;
if ( !wcsncmp( pszPath, L"LM/", 3 ) )
{
pszPath += 3;
}
if ( !m_Source.GetData( pszPath, &md, NULL, &dwRequired ) &&
GetLastError() == ERROR_INSUFFICIENT_BUFFER )
{
*ppBuf = (LPBYTE)LocalAlloc( LMEM_FIXED, dwRequired );
if ( !*ppBuf )
{
return FALSE;
}
*pdwLen = md.dwMDDataLen = dwRequired;
if ( m_Source.GetData( pszPath, &md, *ppBuf, &dwRequired ) )
{
return TRUE;
}
LocalFree( *ppBuf );
}
*ppBuf = NULL;
return FALSE;
}
CSync::CSync(
)
/*++
Routine Description:
Synchronizer constructor
Arguments:
None
Returns:
Nothing
--*/
{
m_pRoot = NULL;
m_pTargets = NULL;
m_dwTargets = 0;
m_fCancel = FALSE;
InitializeListHead( &m_QueuedRequestsHead );
INITIALIZE_CRITICAL_SECTION( &m_csQueuedRequestsList );
INITIALIZE_CRITICAL_SECTION( &m_csLock );
m_fInScan = FALSE;
m_cbSeed = SEED_MD_DATA_SIZE;
memset( m_rgbSeed, 0, m_cbSeed );
}
CSync::~CSync(
)
/*++
Routine Description:
Synchronizer destructor
Arguments:
None
Returns:
Nothing
--*/
{
UINT i;
if ( m_pTargets )
{
for ( i = 0 ; i < m_dwTargets ; ++i )
{
if ( m_pTargets[i] )
{
delete m_pTargets[i];
}
}
LocalFree( m_pTargets );
}
LIST_ENTRY* pChild;
CNseRequest* pReq;
while ( !IsListEmpty( &m_QueuedRequestsHead ))
{
pReq = CONTAINING_RECORD( m_QueuedRequestsHead.Flink,
CNseRequest,
m_QueuedRequestsList );
RemoveEntryList( &pReq->m_QueuedRequestsList );
delete pReq;
}
DeleteCriticalSection( &m_csQueuedRequestsList );
DeleteCriticalSection( &m_csLock );
}
VOID
CSync::SetTargetError(
DWORD dwTarget,
DWORD dwError
)
/*++
Routine Description:
Set error status for specified target
Arguments:
dwTarget - target ID
dwError - error code
Returns:
Nothing
--*/
{
m_TargetStatus.SetStatus( dwTarget, dwError );
}
DWORD
WINAPI
ScanThread(
LPVOID pV
)
/*++
Routine Description:
thread scanning a target for synchronization
Arguments:
pV - ptr to scan context
Returns:
Error code, 0 if success
--*/
{
HRESULT hr = CoInitializeEx( NULL, COINIT_MULTITHREADED );
if (FAILED(hr))
{
return HRESULTTOWIN32(hr);
}
THREAD_CONTEXT * pThreadContext = (THREAD_CONTEXT *)pV;
CSync * pSync = (CSync *) pThreadContext->pvContext;
if ( !( pSync->ScanTarget( pThreadContext->dwIndex)))
{
CoUninitialize();
return GetLastError();
}
CoUninitialize();
return 0;
}
BOOL
CSync::ScanTarget(
DWORD dwI
)
/*++
Routine Description:
Scan target for synchronization
Arguments:
dwI - target ID
Returns:
TRUE if success, otherwise FALSE
--*/
{
BOOL fSt;
fSt = m_pRoot->ScanTarget( dwI );
InterlockedDecrement( &m_lThreads );
return fSt;
}
BOOL
CSync::GenerateKeySeed( )
/*++
Routine Description:
Generate the seed which will be used to derive the session key for encryption and
write it to the metabase
Arguments:
Returns:
TRUE if successful, FALSE if not
--*/
{
#ifdef NO_ENCRYPTION
return TRUE;
#else
HCRYPTPROV hProv = NULL;
BOOL fOk = TRUE;
ALG_ID aiAlg = CALG_MD5;
DWORD i = 0;
if ( !m_Source.Open( L"/LM/W3SVC", METADATA_PERMISSION_WRITE ) )
{
return FALSE;
}
//
// Seed header with version information, hash algorithm used and size of
// seed used to generate the session key
//
m_rgbSeed[i++] = IIS_SEED_MAJOR_VERSION;
m_rgbSeed[i++] = IIS_SEED_MINOR_VERSION;
memcpy( m_rgbSeed + i, &aiAlg, sizeof( ALG_ID ) );
i += sizeof( ALG_ID );
m_rgbSeed[i++] = RANDOM_SEED_SIZE;
DBG_ASSERT( i == SEED_HEADER_SIZE );
//
// Generate the seed
//
if ( ( fOk = CryptAcquireContext( &hProv,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT ) ) &&
( fOk = CryptGenRandom( hProv,
RANDOM_SEED_SIZE,
m_rgbSeed + SEED_HEADER_SIZE ) ) )
{
//
// Write the seed to the metabase
//
METADATA_RECORD mdr;
MD_SET_DATA_RECORD( &mdr,
MD_SSL_REPLICATION_INFO,
METADATA_SECURE,
IIS_MD_UT_SERVER,
BINARY_METADATA,
m_cbSeed,
m_rgbSeed );
fOk = m_Source.SetData( MB_REPLICATION_PATH,
&mdr,
(LPVOID) m_rgbSeed );
}
if ( hProv )
{
CryptReleaseContext( hProv,
0 );
}
m_Source.Close();
return fOk;
#endif // NO_ENCRYPTION
}
BOOL CSync::PropagateKeySeed( VOID )
/*++
Routine Description:
Propagate the session key seed to all the remote machines
Arguments:
None
Returns:
TRUE if successful, FALSE if not
--*/
{
#ifdef NO_ENCRYPTION
return TRUE;
#else
HRESULT hRes = S_OK;
for ( DWORD dwIndex = 0; dwIndex < m_dwTargets; dwIndex++ )
{
if ( m_bmIsRemote.GetFlag( dwIndex ))
{
if ( !m_pTargets[dwIndex]->Open( L"/LM/W3SVC",
METADATA_PERMISSION_WRITE ) )
{
if ( GetLastError() == ERROR_SUCCESS )
{
SetLastError( RPC_S_SERVER_UNAVAILABLE );
}
m_TargetStatus.SetStatus( dwIndex, GetLastError() );
}
else
{
//
// Write the seed to the remote metabase
//
METADATA_RECORD mdr;
MD_SET_DATA_RECORD( &mdr,
MD_SSL_REPLICATION_INFO,
METADATA_SECURE,
IIS_MD_UT_SERVER,
BINARY_METADATA,
m_cbSeed,
m_rgbSeed );
if ( !m_pTargets[dwIndex]->SetData( MB_REPLICATION_PATH,
&mdr,
(LPVOID) m_rgbSeed ) )
{
m_TargetStatus.SetStatus( dwIndex, GetLastError() );
}
m_pTargets[dwIndex]->Close() ;
}
} // if ( m_bmIsRemote
} // for ( DWORD dwIndex
return TRUE;
#endif //NO_ENCRYPTION
} //::PropagateKeySeed
BOOL CSync::DeleteKeySeed( VOID )
/*++
Routine Description:
Deletes the session key seed from the MB
Arguments:
None
Returns:
TRUE if successful, FALSE if not
--*/
{
#ifdef NO_ENCRYPTION
return TRUE;
#else
BOOL fOk = TRUE;
if ( !m_Source.Open( L"/LM/W3SVC", METADATA_PERMISSION_WRITE ) )
{
return FALSE;
}
METADATA_RECORD mdr;
MD_SET_DATA_RECORD( &mdr,
MD_SSL_REPLICATION_INFO,
METADATA_SECURE,
IIS_MD_UT_SERVER,
BINARY_METADATA,
0,
NULL );
fOk = m_Source.DeleteProp( MB_REPLICATION_PATH,
&mdr );
m_Source.Close();
return fOk;
#endif //NO_ENCRYPTION
}
HRESULT
CSync::Sync(
LPSTR pszTargets,
LPDWORD pdwResults,
DWORD dwFlags,
SYNC_STAT* pStat
)
/*++
Routine Description:
Synchronize targets with source
Arguments:
pszTargets - multisz of target computer names
can include local computer, will be ignored during synchro
pdwResults - updated with error code for each target
dwFlags - flags, no flag defined for now. Should be 0.
pStat - ptr to stat struct
Returns:
TRUE if success, otherwise FALSE
--*/
{
LPSTR p;
HRESULT hRes = S_OK;
CHAR achLocalComputer[MAX_COMPUTERNAME_LENGTH+1];
BOOL fIsError;
DWORD dwSize;
UINT i;
LPWSTR pClsidList;
BOOL fGotSeed = FALSE;
m_fInScan = FALSE;
if ( m_pRoot )
{
return RETURNCODETOHRESULT(ERROR_IO_PENDING);
}
if ( !(m_pRoot = new CNodeDesc( this )) )
{
return RETURNCODETOHRESULT( ERROR_NOT_ENOUGH_MEMORY );
}
//
// Init MetaData COM I/F on local system
//
m_dwThreads = 0;
dwSize = sizeof(achLocalComputer);
if ( !m_Source.Init( NULL ) ||
!GetComputerName( achLocalComputer, &dwSize ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto Exit;
}
//
// Generate seed for session key used during replication
//
if ( !GenerateKeySeed() )
{
DBGPRINTF((DBG_CONTEXT,
"GenerateKeySeed() failed : 0x%x\n",
GetLastError() ));
hRes = RETURNCODETOHRESULT( GetLastError() );
goto Exit;
}
else
{
fGotSeed = TRUE;
}
//
// For the rest of the replication, we need an open read handle to the metabase; we open
// the read handle -after- we've generated and written the seed for the session key to the
// metabase so as not to cause lock
//
if ( !m_Source.Open( L"/LM/", METADATA_PERMISSION_READ ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto Exit;
}
//
// Get CLSIDs of extensions
//
if ( !GetProp( IISADMIN_EXTENSIONS_CLSID_MD_KEYW,
IISADMIN_EXTENSIONS_CLSID_MD_ID,
IIS_MD_UT_SERVER,
MULTISZ_METADATA,
(LPBYTE*)&pClsidList,
&dwSize ) )
{
pClsidList = NULL;
}
//
// Allocate ptr to target systems
//
for ( m_dwTargets = 0, p = pszTargets ; *p ; p += strlen(p)+1, ++m_dwTargets )
{
}
if ( !(m_pTargets = (CMdIf**)LocalAlloc( LMEM_ZEROINIT|LMEM_FIXED,
sizeof(CMdIf*)*m_dwTargets)) )
{
hRes = RETURNCODETOHRESULT( ERROR_NOT_ENOUGH_MEMORY );
goto Exit;
}
if ( !m_TargetStatus.Init( m_dwTargets ) ||
!m_bmIsRemote.Init( m_dwTargets ) ||
!m_ThreadHandle.Init( m_dwTargets ) ||
!m_ThreadContext.Init( m_dwTargets ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto Exit;
}
//
// Init MetaData COM I/F to targets system
//
for ( i = 0, p = pszTargets ; *p ; p += strlen(p)+1, ++i )
{
if ( !(m_pTargets[i] = new CMdIf()) )
{
hRes = RETURNCODETOHRESULT( ERROR_NOT_ENOUGH_MEMORY );
goto Exit;
}
//
// set flag indicating whether it's a remote machine
//
if ( !_stricmp( p, achLocalComputer ) )
{
m_bmIsRemote.SetFlag( i, FALSE );
}
//
// if it's a remote machine, actually get an interface pointer
//
if ( m_bmIsRemote.GetFlag( i ) )
{
if ( !m_pTargets[i]->Init( p ) )
{
if ( GetLastError() == ERROR_SUCCESS )
{
SetLastError( RPC_S_SERVER_UNAVAILABLE );
}
m_TargetStatus.SetStatus( i, GetLastError() );
}
}
}
m_dwFlags = dwFlags;
//
// Copy session key seed to remote machines
//
PropagateKeySeed();
//
// Process replication extensions ( phase 1 )
//
if ( pClsidList )
{
if ( !ProcessAdminExReplication( pClsidList, pszTargets, AER_PHASE1 ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
}
}
//
// Open MetaData on targets system
//
for ( i = 0, p = pszTargets ; *p ; p += strlen(p)+1, ++i )
{
if ( m_bmIsRemote.GetFlag( i ) )
{
if ( !m_pTargets[i]->Open( L"/LM/",
METADATA_PERMISSION_READ |
METADATA_PERMISSION_WRITE ) )
{
if ( GetLastError() == ERROR_SUCCESS )
{
SetLastError( RPC_S_SERVER_UNAVAILABLE );
}
m_TargetStatus.SetStatus( i, GetLastError() );
}
}
}
//
// Create thread pool
//
m_lThreads = 0;
for ( i = 0 ; i < m_dwTargets ; ++i )
{
THREAD_CONTEXT threadContext;
DWORD dwId;
HANDLE hSem;
#if IIS_NAMED_WIN32_OBJECTS
CHAR objName[sizeof("CSync::m_ThreadContext( 1234567890*3+2 )")];
#else
LPSTR objName = NULL;
#endif
threadContext.pvContext = this;
threadContext.dwIndex = i;
#if IIS_NAMED_WIN32_OBJECTS
wsprintfA(
objName,
"CSync::m_ThreadContext( %u*3+2 )",
i
);
#endif
hSem = IIS_CREATE_SEMAPHORE(
objName,
this,
0,
INT_MAX
);
threadContext.hSemaphore = hSem;
m_ThreadContext.SetStatus( i, threadContext );
if ( NULL == hSem )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
break;
}
m_ThreadHandle.SetStatus( i,
CreateThread( NULL,
0,
(LPTHREAD_START_ROUTINE)::ScanThread,
m_ThreadContext.GetPtr( i ),
0,
&dwId )
);
if ( m_ThreadHandle.GetStatus( i ) )
{
InterlockedIncrement( &m_lThreads );
++m_dwThreads;
}
else
{
CloseHandle( m_ThreadContext.GetPtr( i )->hSemaphore );
hRes = RETURNCODETOHRESULT( GetLastError() );
break;
}
}
m_fInScan = TRUE;
//
// Launch scan
//
m_pStat = pStat;
m_pStat->m_dwSourceScan = 0;
m_pStat->m_fSourceComplete = FALSE;
memset( m_pStat->m_adwTargets, '\0', sizeof(DWORD)*2*m_dwTargets );
if ( hRes == S_OK )
{
if ( !m_pRoot->SetPath( L"" ) ||
!m_pRoot->Scan( this ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
Cancel();
}
else
{
SetSourceComplete();
}
}
else
{
Cancel();
}
//
// wait for all threads to exit
//
for ( ;; )
{
if ( m_lThreads == 0 )
{
break;
}
Sleep( 1000 );
}
//
// Wait for all threads to be terminated
//
WaitForMultipleObjects( m_dwThreads, m_ThreadHandle.GetPtr(0), TRUE, 5000 );
m_fInScan = FALSE;
for ( i = 0 ; i < m_dwThreads ; ++i )
{
DWORD dwS;
if ( !GetExitCodeThread( m_ThreadHandle.GetStatus(i), &dwS ) )
{
dwS = GetLastError();
}
if ( hRes == S_OK && dwS )
{
hRes = RETURNCODETOHRESULT( dwS );
}
CloseHandle( m_ThreadHandle.GetStatus(i) );
CloseHandle( m_ThreadContext.GetPtr(i)->hSemaphore );
}
//
// Process replication extensions ( phase 2 )
//
if ( pClsidList )
{
if ( !ProcessAdminExReplication( pClsidList, pszTargets, AER_PHASE2 ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
}
LocalFree( pClsidList );
}
//
// Close metadata
//
m_Source.Close();
for ( i = 0 ; i < m_dwTargets ; ++i )
{
if ( m_bmIsRemote.GetFlag( i ) )
{
m_pTargets[i]->Close();
}
}
//
// Process queued update requests
//
if ( !ProcessQueuedRequest() )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
}
//
// Terminate target machine metadata objects
//
for ( i = 0 ; i < m_dwTargets ; ++i )
{
m_pTargets[i]->Terminate();
}
//
// Scan for errors on targets
//
for ( fIsError = FALSE, i = 0 ; i < m_dwTargets ; ++i )
{
pdwResults[i] = m_TargetStatus.GetStatus( i );
if ( pdwResults[i] )
{
fIsError = TRUE;
}
}
if ( hRes == S_OK && m_fCancel )
{
hRes = RETURNCODETOHRESULT( ERROR_CANCELLED );
}
if ( hRes == S_OK &&
fIsError )
{
hRes = E_FAIL;
}
Exit:
//
// Clean up session key seed
//
if ( fGotSeed )
{
DeleteKeySeed();
}
//
// Terminate source machine metadata object
//
m_Source.Terminate();
delete m_pRoot;
m_pRoot = NULL;
m_fInScan = FALSE;
return hRes;
}
BOOL
CMdIf::Init(
LPSTR pszComputer
)
/*++
Routine Description:
Initialize metabase admin interface :
get interface pointer, call Initialize()
Arguments:
pszComputer - computer name, NULL for local computer
Returns:
TRUE if success, otherwise FALSE
--*/
{
IClassFactory * pcsfFactory;
COSERVERINFO csiMachineName;
HRESULT hresError;
BOOL fSt = FALSE;
WCHAR awchComputer[COMPUTER_CHARACTER_SIZE];
WCHAR* pwchComputer = NULL;
m_fModified = FALSE;
//fill the structure for CoGetClassObject
ZeroMemory( &csiMachineName, sizeof(csiMachineName) );
// csiMachineName.pAuthInfo = NULL;
// csiMachineName.dwFlags = 0;
// csiMachineName.pServerInfoExt = NULL;
if ( pszComputer )
{
if ( !MultiByteToWideChar( CP_ACP,
MB_PRECOMPOSED,
pszComputer,
-1,
awchComputer,
COMPUTER_CHARACTER_SIZE ) )
{
return FALSE;
}
pwchComputer = awchComputer;
}
csiMachineName.pwszName = pwchComputer;
hresError = CoGetClassObject(
CLSID_MSAdminBase_W,
CLSCTX_SERVER,
&csiMachineName,
IID_IClassFactory,
(void**) &pcsfFactory );
if ( SUCCEEDED(hresError) )
{
hresError = pcsfFactory->CreateInstance(NULL, IID_IMSAdminBase_W, (void **) &m_pcAdmCom);
if (SUCCEEDED(hresError) )
{
fSt = TRUE;
}
else
{
SetLastError( HRESULTTOWIN32(hresError) );
m_pcAdmCom = NULL;
}
pcsfFactory->Release();
}
else
{
if ( hresError == REGDB_E_CLASSNOTREG )
{
SetLastError( ERROR_SERVICE_DOES_NOT_EXIST );
}
else
{
SetLastError( HRESULTTOWIN32(hresError) );
}
m_pcAdmCom = NULL;
}
return fSt;
}
BOOL
CMdIf::Open(
LPWSTR pszOpenPath,
DWORD dwPermission
)
/*++
Routine Description:
Open path in metabase
Arguments:
pszOpenPath - path in metadata
dwPermission - metadata permission
Returns:
TRUE if success, otherwise FALSE
--*/
{
HRESULT hresError;
if (NULL == m_pcAdmCom)
{
SetLastError(E_NOINTERFACE);
return FALSE;
}
hresError = m_pcAdmCom->OpenKey( METADATA_MASTER_ROOT_HANDLE,
pszOpenPath, dwPermission, TIMEOUT_VALUE, &m_hmd );
if ( FAILED(hresError) )
{
m_hmd = NULL;
SetLastError( HRESULTTOWIN32(hresError) );
return FALSE;
}
return TRUE;
}
BOOL
CMdIf::Close(
)
/*++
Routine Description:
Close path in metabase
Arguments:
None
Returns:
TRUE if success, otherwise FALSE
--*/
{
if ( m_pcAdmCom && m_hmd )
{
m_pcAdmCom->CloseKey(m_hmd);
}
m_hmd = NULL;
return TRUE;
}
BOOL
CMdIf::Terminate(
)
/*++
Routine Description:
Terminate metabase admin interface :
call Terminate, release interface pointer
Arguments:
None
Returns:
TRUE if success, otherwise FALSE
--*/
{
if ( m_pcAdmCom )
{
if ( m_fModified )
{
m_pcAdmCom->SaveData();
}
m_pcAdmCom->Release();
m_hmd = NULL;
m_pcAdmCom = NULL;
}
return TRUE;
}
#if defined(ADMEX)
BOOL
CRpIf::Init(
LPSTR pszComputer,
CLSID* pClsid
)
/*++
Routine Description:
Initialize metabase admin interface :
get interface pointer, call Initialize()
Arguments:
pszComputer - computer name, NULL for local computer
Returns:
TRUE if success, otherwise FALSE
--*/
{
IClassFactory * pcsfFactory;
COSERVERINFO csiMachineName;
HRESULT hresError;
BOOL fSt = FALSE;
WCHAR awchComputer[COMPUTER_CHARACTER_SIZE];
WCHAR* pwchComputer = NULL;
//fill the structure for CoGetClassObject
ZeroMemory( &csiMachineName, sizeof(csiMachineName) );
// csiMachineName.pAuthInfo = NULL;
// csiMachineName.dwFlags = 0;
// csiMachineName.pServerInfoExt = NULL;
if ( pszComputer )
{
if ( !MultiByteToWideChar( CP_ACP,
MB_PRECOMPOSED,
pszComputer,
-1,
awchComputer,
COMPUTER_CHARACTER_SIZE ) )
{
return FALSE;
}
pwchComputer = awchComputer;
}
csiMachineName.pwszName = pwchComputer;
hresError = CoGetClassObject(
*pClsid,
CLSCTX_SERVER,
&csiMachineName,
IID_IClassFactory,
(void**) &pcsfFactory );
if ( SUCCEEDED(hresError) )
{
hresError = pcsfFactory->CreateInstance(NULL, IID_IMSAdminReplication, (void **) &m_pcAdmCom);
if (SUCCEEDED(hresError) )
{
fSt = TRUE;
}
else
{
SetLastError( HRESULTTOWIN32(hresError) );
m_pcAdmCom = NULL;
}
pcsfFactory->Release();
}
else
{
if ( hresError == REGDB_E_CLASSNOTREG )
{
SetLastError( ERROR_SERVICE_DOES_NOT_EXIST );
}
else
{
SetLastError( HRESULTTOWIN32(hresError) );
}
m_pcAdmCom = NULL;
}
return fSt;
}
BOOL
CRpIf::Terminate(
)
/*++
Routine Description:
Terminate metabase admin interface :
call Terminate, release interface pointer
Arguments:
None
Returns:
TRUE if success, otherwise FALSE
--*/
{
if ( m_pcAdmCom )
{
m_pcAdmCom->Release();
m_pcAdmCom = NULL;
}
return TRUE;
}
#endif
BOOL
CNodeDesc::Scan(
CSync* pSync
)
/*++
Routine Description:
Scan subtree for nodes & properties
Signal each node availability for target synchronization
after scanning it.
Arguments:
pSync - synchronizer
Returns:
TRUE if success, otherwise FALSE
--*/
{
DWORD dwTarget;
if ( m_pSync->IsCancelled() )
{
return FALSE;
}
// get local props
if ( !m_Props.GetAll( m_pSync->GetSourceIf(), m_pszPath ) )
{
return FALSE;
}
m_pSync->IncrementSourceScan();
if ( !BuildChildObjectsList( m_pSync->GetSourceIf(), m_pszPath ) )
{
return FALSE;
}
m_Props.SetRefCount( m_pSync->GetTargetCount() ); // when 0, free props
for ( dwTarget = 0 ; dwTarget < m_pSync->GetTargetCount() ; ++dwTarget )
{
m_pSync->SignalWorkItem( dwTarget );
}
LIST_ENTRY* pSourceEntry;
CNodeDesc* pSourceDir;
//
// recursively scan children
//
for ( pSourceEntry = m_ChildHead.Flink;
pSourceEntry != &m_ChildHead ;
pSourceEntry = pSourceEntry->Flink )
{
pSourceDir = CONTAINING_RECORD( pSourceEntry,
CNodeDesc,
m_ChildList );
if ( !pSourceDir->Scan( pSync ) )
{
return FALSE;
}
}
return TRUE;
}
BOOL
CNodeDesc::ScanTarget(
DWORD dwTarget
)
/*++
Routine Description:
Scan target subtree for nodes and properties,
synchronizing with source. Wait for source scan
to be complete before synchronizing.
Arguments:
dwTarget - target ID
Returns:
TRUE if success, otherwise FALSE
--*/
{
m_pSync->WaitForWorkItem( dwTarget );
if ( m_pSync->IsCancelled() )
{
return FALSE;
}
if ( !DoWork( SCANMODE_SYNC_PROPS, dwTarget ) )
{
return FALSE;
}
LIST_ENTRY* pSourceEntry;
CNodeDesc* pSourceDir;
//
// recursively scan children
//
for ( pSourceEntry = m_ChildHead.Flink;
pSourceEntry != &m_ChildHead ;
pSourceEntry = pSourceEntry->Flink )
{
pSourceDir = CONTAINING_RECORD( pSourceEntry,
CNodeDesc,
m_ChildList );
if ( !pSourceDir->ScanTarget( dwTarget ) )
{
return FALSE;
}
}
return TRUE;
}
BOOL
CNodeDesc::DoWork(
SCANMODE sm,
DWORD dwTarget
)
/*++
Routine Description:
synchronize target node with source node :
add/delete/update properties as needed,
add/delete nodes as needed.
Arguments:
sm - scan operation to perform
only SCANMODE_SYNC_PROPS is defined for now.
dwTarget - target ID
Returns:
TRUE if success, otherwise FALSE
--*/
{
CProps Props;
CNodeDesc TargetDir( m_pSync );
LIST_ENTRY* pSourceEntry;
CNodeDesc* pSourceDir;
LIST_ENTRY* pTargetEntry;
CNodeDesc* pTargetDir;
BOOL fMatch;
PMETADATA_RECORD pSourceProps;
PMETADATA_RECORD pTargetProps;
PBYTE pSourceData;
PBYTE pTargetData;
DWORD dwSourceProps;
DWORD dwTargetProps;
LPBYTE pbExists;
DWORD dwSourceObjs;
FILETIME ftSource;
FILETIME ftTarget;
BOOL fModified = FALSE;
UINT iS;
UINT iT;
BOOL fNeedUpdate;
BOOL fExists;
BOOL fDoNotSetTimeModif = FALSE;
//
// if target already in error, do not process request
//
if ( m_pSync->GetTargetError( dwTarget ) ||
m_pSync->IsLocal( dwTarget ) )
{
return TRUE;
}
switch ( sm )
{
case SCANMODE_SYNC_PROPS:
//
// Check date/time last modification.
// If identical on source & target, do not update
//
m_pSync->IncrementTargetScan( dwTarget );
if ( !(m_pSync->GetSourceIf())->GetLastChangeTime( m_pszPath, &ftSource ) )
{
return FALSE;
}
if ( m_pSync->GetTargetIf( dwTarget )->GetLastChangeTime( m_pszPath, &ftTarget ) )
{
if ( !memcmp( &ftSource, &ftTarget, sizeof(FILETIME) ) )
{
return TRUE;
}
}
else
{
m_pSync->SetTargetError( dwTarget, GetLastError() );
return TRUE;
}
// get props on target, set / delete as appropriate
if ( Props.GetAll( m_pSync->GetTargetIf(dwTarget), m_pszPath ) )
{
pSourceProps = (PMETADATA_RECORD)m_Props.GetProps();
dwSourceProps = m_Props.GetPropsCount();
dwTargetProps = Props.GetPropsCount();
if ( !(pbExists = (LPBYTE)LocalAlloc( LMEM_FIXED, dwTargetProps )) )
{
return FALSE;
}
memset( pbExists, '\x0', dwTargetProps );
for ( iS = 0 ; iS < dwSourceProps ; ++iS,++pSourceProps )
{
pSourceData = (LPBYTE)m_Props.GetProps() + (UINT_PTR)pSourceProps->pbMDData;
pTargetProps = (PMETADATA_RECORD)Props.GetProps();
fNeedUpdate = TRUE;
fExists = FALSE;
for ( iT = 0 ; iT < dwTargetProps ; ++iT,++pTargetProps )
{
if ( pSourceProps->dwMDIdentifier ==
pTargetProps->dwMDIdentifier )
{
pbExists[ iT ] = '\x1';
pTargetData = (LPBYTE)Props.GetProps() + (UINT_PTR)pTargetProps->pbMDData;
if ( m_Props.IsNse( pSourceProps->dwMDIdentifier ) )
{
fNeedUpdate = m_Props.NseIsDifferent( pSourceProps->dwMDIdentifier, pSourceData, pSourceProps->dwMDDataLen, pTargetData, pTargetProps->dwMDDataLen, m_pszPath, dwTarget );
}
else if ( pSourceProps->dwMDDataType == pTargetProps->dwMDDataType &&
pSourceProps->dwMDUserType == pTargetProps->dwMDUserType )
{
fExists = TRUE;
if( pSourceProps->dwMDDataLen == pTargetProps->dwMDDataLen &&
!memcmp(pSourceData, pTargetData, pSourceProps->dwMDDataLen ) )
{
fNeedUpdate = FALSE;
}
else if ( pSourceProps->dwMDIdentifier == MD_SERVER_STATE ||
pSourceProps->dwMDIdentifier == MD_WIN32_ERROR ||
pSourceProps->dwMDIdentifier == MD_SERVER_COMMAND ||
pSourceProps->dwMDIdentifier == MD_CLUSTER_SERVER_COMMAND ||
pSourceProps->dwMDIdentifier == MD_ANONYMOUS_USER_NAME ||
pSourceProps->dwMDIdentifier == MD_ANONYMOUS_PWD ||
pSourceProps->dwMDIdentifier == MD_WAM_USER_NAME ||
pSourceProps->dwMDIdentifier == MD_WAM_PWD
)
{
fNeedUpdate = FALSE;
}
#if defined(METADATA_LOCAL_MACHINE_ONLY)
else if ( pSourceProps->dwMDAttributes
& METADATA_LOCAL_MACHINE_ONLY )
{
fNeedUpdate = FALSE;
}
#endif
}
}
}
if ( fNeedUpdate )
{
if ( m_Props.IsNse( pSourceProps->dwMDIdentifier ) )
{
if ( !m_pSync->QueueRequest(
pSourceProps->dwMDIdentifier,
m_pszPath,
dwTarget,
&ftSource ) )
{
m_pSync->SetTargetError( dwTarget, GetLastError() );
}
else
{
//
// differ updating time last modif
// until NSE update processed
//
fDoNotSetTimeModif = TRUE;
}
}
else
{
METADATA_RECORD md;
md = *pSourceProps;
if ( !(m_pSync->QueryFlags() & MD_SYNC_FLAG_REPLICATE_AUTOSTART) &&
md.dwMDIdentifier == MD_SERVER_AUTOSTART )
{
if ( fExists )
{
fNeedUpdate = FALSE;
}
else
{
//
// create as FALSE ( server won't autostart )
//
pSourceData = (LPBYTE)&g_dwFalse;
md.dwMDDataLen = sizeof(DWORD);
md.dwMDDataType = DWORD_METADATA;
}
}
if ( !(m_pSync->QueryFlags() & MD_SYNC_FLAG_DONT_PRESERVE_IP_BINDINGS) &&
(md.dwMDIdentifier == MD_SERVER_BINDINGS ||
md.dwMDIdentifier == MD_SECURE_BINDINGS) )
{
if ( fExists )
{
fNeedUpdate = FALSE;
}
}
if ( fNeedUpdate )
{
if ( !m_pSync->GetTargetIf(dwTarget)->SetData( m_pszPath, &md, pSourceData ) )
{
m_pSync->SetTargetError( dwTarget, GetLastError() );
}
}
}
m_pSync->SetModified( dwTarget );
fModified = TRUE;
}
}
// delete prop not in src
pTargetProps = (PMETADATA_RECORD)Props.GetProps();
for ( iT = 0 ; iT < dwTargetProps ; ++iT,++pTargetProps )
{
if ( !pbExists[ iT ] )
{
if ( !m_pSync->GetTargetIf(dwTarget)->DeleteProp( m_pszPath, pTargetProps ) )
{
m_pSync->SetTargetError( dwTarget, GetLastError() );
}
m_pSync->SetModified( dwTarget );
fModified = TRUE;
}
}
LocalFree( pbExists );
}
// enum objects on target, delete sub-tree as appropriate
if ( TargetDir.BuildChildObjectsList( m_pSync->GetTargetIf(dwTarget), m_pszPath ) )
{
for ( dwSourceObjs = 0, pSourceEntry = m_ChildHead.Flink;
pSourceEntry != &m_ChildHead ;
++dwSourceObjs, pSourceEntry = pSourceEntry->Flink )
{
}
if ( !(pbExists = (LPBYTE)LocalAlloc( LMEM_FIXED, dwSourceObjs )) )
{
return FALSE;
}
memset( pbExists, '\x0', dwSourceObjs );
for ( pTargetEntry = TargetDir.m_ChildHead.Flink;
pTargetEntry != &TargetDir.m_ChildHead ;
pTargetEntry = pTargetEntry->Flink )
{
pTargetDir = CONTAINING_RECORD( pTargetEntry,
CNodeDesc,
m_ChildList );
fMatch = FALSE;
for ( iS = 0, pSourceEntry = m_ChildHead.Flink;
pSourceEntry != &m_ChildHead ;
++iS, pSourceEntry = pSourceEntry->Flink )
{
pSourceDir = CONTAINING_RECORD( pSourceEntry,
CNodeDesc,
m_ChildList );
if ( !_wcsicmp( pTargetDir->GetPath(), pSourceDir->GetPath() ) )
{
pbExists[ iS ] = '\x1';
fMatch = TRUE;
break;
}
}
if ( !fMatch )
{
if ( !m_pSync->GetTargetIf(dwTarget)->DeleteSubTree( pTargetDir->GetPath() ) )
{
m_pSync->SetTargetError( dwTarget, GetLastError() );
}
m_pSync->SetModified( dwTarget );
fModified = TRUE;
}
}
//
// Add node if does not exist on target
//
for ( iS = 0, pSourceEntry = m_ChildHead.Flink;
pSourceEntry != &m_ChildHead ;
++iS, pSourceEntry = pSourceEntry->Flink )
{
if ( !pbExists[iS] )
{
pSourceDir = CONTAINING_RECORD( pSourceEntry,
CNodeDesc,
m_ChildList );
if ( !m_pSync->GetTargetIf(dwTarget)->AddNode( pSourceDir->GetPath() ) )
{
m_pSync->SetTargetError( dwTarget, GetLastError() );
}
m_pSync->SetModified( dwTarget );
fModified = TRUE;
}
}
LocalFree( pbExists );
}
else
{
// not error if does not exist on target
}
if ( fModified &&
!fDoNotSetTimeModif &&
!m_pSync->GetTargetError( dwTarget ) &&
!m_pSync->GetTargetIf( dwTarget )->SetLastChangeTime( m_pszPath, &ftSource ) )
{
m_pSync->SetTargetError( dwTarget, GetLastError() );
}
if ( fModified )
{
m_pSync->IncrementTargetTouched( dwTarget );
}
m_Props.Dereference();
break;
}
return TRUE;
}
BOOL
CProps::NseIsDifferent(
DWORD dwId,
LPBYTE pSourceData,
DWORD dwSourceLen,
LPBYTE pTargetData,
DWORD dwTargetLen,
LPWSTR pszPath,
DWORD dwTarget
)
/*++
Routine Description:
Check if two NSE properties are different
Arguments:
dwId - property ID
pSourceData - ptr to source data for this property
dwSourceLen - # of bytes in pSourceData
pTargetData - ptr to target data for this property
dwTargetLen - # of bytes in pTargetData
pszPath - path to property
dwTarget - target ID
Returns:
TRUE if properties different, FALSE if identical
--*/
{
switch ( dwId )
{
case MD_SERIAL_CERT11:
case MD_SERIAL_DIGEST:
//
// serialized format is (DWORD)len, string, then MD5 signature ( 16 bytes )
//
//
// skip string
//
if ( *(LPDWORD)pSourceData < dwSourceLen )
{
pSourceData += sizeof(DWORD) + *(LPDWORD)pSourceData;
}
if ( *(LPDWORD)pTargetData < dwTargetLen )
{
pTargetData += sizeof(DWORD) + *(LPDWORD)pTargetData;
}
//
// compare MD5 signature
//
return memcmp( pSourceData, pTargetData, 16 );
}
//
// Don't know how to handle, do not replicate
//
return FALSE;
}
CNseRequest::CNseRequest(
)
/*++
Routine Description:
NSE request constructor
Arguments:
None
Returns:
Nothing
--*/
{
m_pszPath = NULL;
m_pszModifPath = NULL;
m_pbData = NULL;
}
CNseRequest::~CNseRequest(
)
/*++
Routine Description:
NSE request destructor
Arguments:
None
Returns:
Nothing
--*/
{
if ( m_pszPath )
{
LocalFree( m_pszPath );
}
if ( m_pszModifPath )
{
LocalFree( m_pszModifPath );
}
if ( m_pszCreatePath )
{
LocalFree( m_pszCreatePath );
}
if ( m_pszCreateObject )
{
LocalFree( m_pszCreateObject );
}
if ( m_pbData )
{
LocalFree( m_pbData );
}
}
BOOL
CNseRequest::Init(
LPWSTR pszPath,
LPWSTR pszCreatePath,
LPWSTR pszCreateObject,
DWORD dwId,
DWORD dwTargetCount,
LPWSTR pszModifPath,
FILETIME* pftModif,
METADATA_RECORD* pMd
)
/*++
Routine Description:
Initialize NSE request
Arguments:
pszPath - NSE path to property
pszCreatePath - NSE path where to create object if open object fails
pszCreateObject - name of object to create if open object fails
dwId - property ID
dwTargetCount - # of potential targets
pszModifPath - path where to update last date/time modification
on success
pftModif - last date/time modification to set on success
pMD - metadata record to set on target
Returns:
Nothing
--*/
{
m_dwTargetCount = dwTargetCount;
m_dwId = dwId;
if ( !(m_pszPath = (LPWSTR)LocalAlloc( LMEM_FIXED, (wcslen(pszPath)+1)*sizeof(WCHAR) )) )
{
return FALSE;
}
wcscpy( m_pszPath, pszPath );
if ( !(m_pszModifPath = (LPWSTR)LocalAlloc( LMEM_FIXED, (wcslen(pszModifPath)+1)*sizeof(WCHAR) )) )
{
LocalFree( m_pszPath );
return FALSE;
}
wcscpy( m_pszModifPath, pszModifPath );
if ( !(m_pszCreatePath = (LPWSTR)LocalAlloc( LMEM_FIXED, (wcslen(pszCreatePath)+1)*sizeof(WCHAR) )) )
{
LocalFree( m_pszModifPath );
LocalFree( m_pszPath );
return FALSE;
}
wcscpy( m_pszCreatePath, pszCreatePath );
if ( !(m_pszCreateObject = (LPWSTR)LocalAlloc( LMEM_FIXED, (wcslen(pszCreateObject)+1)*sizeof(WCHAR) )) )
{
LocalFree( m_pszCreatePath );
LocalFree( m_pszModifPath );
LocalFree( m_pszPath );
return FALSE;
}
wcscpy( m_pszCreateObject, pszCreateObject );
m_ftModif = *pftModif;
m_md = *pMd;
return m_bmTarget.Init( dwTargetCount, FALSE );
}
BOOL
CNseRequest::Process(
CSync* pSync
)
/*++
Routine Description:
Process a NSE request :
replicate source property to designated targets
Arguments:
pSync - synchronizer
Returns:
TRUE if success, otherwise FALSE
--*/
{
CMdIf* pSource = pSync->GetSourceIf();
CMdIf* pTarget;
UINT i;
DWORD dwRequired;
int retry;
if ( !pSource )
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
if ( pSource->Open( m_pszPath, METADATA_PERMISSION_READ ) )
{
m_md.pbMDData = NULL;
m_md.dwMDDataLen = 0;
if ( !pSource->GetData( L"", &m_md, NULL, &dwRequired) )
{
if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
{
if ( !(m_pbData = (LPBYTE)LocalAlloc( LMEM_FIXED, dwRequired )) )
{
pSource->Close();
return FALSE;
}
m_md.pbMDData = m_pbData;
m_md.dwMDDataLen = dwRequired;
if ( !pSource->GetData( L"", &m_md, m_pbData, &dwRequired) )
{
pSource->Close();
return FALSE;
}
}
else
{
pSource->Close();
return FALSE;
}
}
for ( i = 0 ; i < m_dwTargetCount ; ++i )
{
if ( m_bmTarget.GetFlag(i) &&
!pSync->GetTargetError( i ) )
{
pTarget = pSync->GetTargetIf( i );
//
// Insure object exist by creating it
// Open path w/o last component, Add last component
//
LPWSTR pLast = m_pszPath + wcslen( m_pszPath ) - 1;
while ( *pLast != L'/' )
{
--pLast;
}
*pLast = L'\0';
if ( pTarget->Open( m_pszPath, METADATA_PERMISSION_WRITE ) )
{
pTarget->AddNode( pLast + 1 );
pTarget->Close();
}
*pLast = L'/';
//
// Set serialized data
//
if ( pTarget->Open( m_pszPath, METADATA_PERMISSION_WRITE ) )
{
if ( !pTarget->SetData( L"", &m_md, m_pbData ) )
{
pSync->SetTargetError( i, GetLastError() );
}
pSync->SetModified( i );
pTarget->Close();
//
// set date/time last modif
//
if ( !pSync->GetTargetError( i ) &&
pTarget->Open( L"/LM", METADATA_PERMISSION_WRITE ) )
{
if ( !pTarget->SetLastChangeTime( m_pszModifPath, &m_ftModif ) )
{
pSync->SetTargetError( i, GetLastError() );
}
pTarget->Close();
}
break;
}
else
{
pSync->SetTargetError( i, GetLastError() );
break;
}
}
}
pSource->Close();
}
else
{
return FALSE;
}
return TRUE;
}
BOOL
CSync::ProcessQueuedRequest(
)
/*++
Routine Description:
Process all queued NSE requests
Arguments:
None
Returns:
TRUE if success, otherwise FALSE
--*/
{
LIST_ENTRY* pChild;
CNseRequest* pReq;
BOOL fSt = TRUE;
while ( !IsListEmpty( &m_QueuedRequestsHead ))
{
pReq = CONTAINING_RECORD( m_QueuedRequestsHead.Flink,
CNseRequest,
m_QueuedRequestsList );
if ( IsCancelled() ||
!pReq->Process( this ) )
{
fSt = FALSE;
}
RemoveEntryList( &pReq->m_QueuedRequestsList );
delete pReq;
}
return fSt;
}
BOOL
CSync::QueueRequest(
DWORD dwId,
LPWSTR pszPath,
DWORD dwTarget,
FILETIME* pftModif
)
/*++
Routine Description:
Queue a NSE request
We cannot process then inline as we need to open a different
path to NSE, which will open a path in metabase space, which will
conflict with the current open.
So we queue requests to be processed after closing all opened
metabase paths.
Arguments:
dwId - property ID
pszPath - NSE path
dwTarget - target ID
pftModif - date/time last modification to set on targets if success
Returns:
TRUE if success, otherwise FALSE
--*/
{
WCHAR achPath[MAX_PATH];
WCHAR achCreatePath[MAX_PATH];
WCHAR achCreateObject[MAX_PATH];
DWORD dwL = wcslen( pszPath );
BOOL fSt = TRUE;
DWORD dwSerialId;
METADATA_RECORD md;
memcpy( achPath, L"/LM", sizeof(L"/LM") - 1*sizeof(WCHAR) );
memcpy( (LPBYTE)achPath + sizeof(L"/LM") - 1*sizeof(WCHAR), pszPath, dwL*sizeof(WCHAR) );
dwL += wcslen(L"/LM");
memcpy( achCreatePath, achPath, dwL*sizeof(WCHAR) );
memset( &md, '\0', sizeof(md) );
md.dwMDAttributes = 0;
md.dwMDUserType = IIS_MD_UT_SERVER;
md.dwMDDataType = BINARY_METADATA;
md.dwMDDataTag = 0;
switch ( dwId )
{
case MD_SERIAL_CERT11:
wcscpy( achPath+dwL, L"/<nsepm>/Cert11" );
wcscpy( achCreatePath+dwL, L"/<nsepm>" );
wcscpy( achCreateObject, L"Cert11" );
dwSerialId = MD_SERIAL_ALL_CERT11;
md.dwMDIdentifier = dwSerialId;
break;
case MD_SERIAL_DIGEST:
wcscpy( achPath+dwL, L"/<nsepm>/Digest" );
wcscpy( achCreatePath+dwL, L"/<nsepm>" );
wcscpy( achCreateObject, L"Digest" );
dwSerialId = MD_SERIAL_ALL_DIGEST;
md.dwMDIdentifier = dwSerialId;
break;
default:
return FALSE;
}
EnterCriticalSection( &m_csQueuedRequestsList );
// locate path in list, add entry if not exists
// set target bit
LIST_ENTRY* pEntry;
CNseRequest* pReq;
BOOL fFound = FALSE;
for ( pEntry = m_QueuedRequestsHead.Flink;
pEntry != &m_QueuedRequestsHead ;
pEntry = pEntry->Flink )
{
pReq = CONTAINING_RECORD( pEntry,
CNseRequest,
m_QueuedRequestsList );
if ( pReq->Match( achPath, dwSerialId ) )
{
fFound = TRUE;
break;
}
}
if ( !fFound )
{
if ( !(pReq = new CNseRequest()) )
{
fSt = FALSE;
}
else if ( !pReq->Init( achPath,
achCreatePath,
achCreateObject,
dwSerialId,
GetTargetCount(),
pszPath,
pftModif,
&md ) )
{
delete pReq;
fSt = FALSE;
}
else
{
InsertHeadList( &m_QueuedRequestsHead, &pReq->m_QueuedRequestsList );
}
}
if ( fSt )
{
pReq->AddTarget( dwTarget );
}
LeaveCriticalSection( &m_csQueuedRequestsList );
return fSt;
}
BOOL
CSync::ProcessAdminExReplication(
LPWSTR pszClsids,
LPSTR pszTargets,
DWORD dwPhase
)
/*++
Routine Description:
Process replication using admin extensions
Arguments:
pszClsids - multi-sz of ClsIds for admin extensions
pszTargets - multi-sz of target computers ( computer names )
dwPhase - phase 1 or 2
Returns:
TRUE if success, otherwise FALSE
--*/
{
#if defined(ADMEX)
CRpIf **pTargets;
CRpIf Source;
UINT i;
LPWSTR pw;
LPSTR p;
BOOL fSt = TRUE;
BUFFER buSourceSignature;
BUFFER buTargetSignature;
BUFFER buSerialize;
DWORD dwSourceSignature;
DWORD dwTargetSignature;
DWORD dwSerialize;
BOOL fHasSource;
CLSID clsid;
DWORD iC;
BOOL fFirstPhase2Clsid = TRUE;
HRESULT hr;
if ( !(pTargets = (CRpIf**)LocalAlloc( LMEM_FIXED|LMEM_ZEROINIT, sizeof(CRpIf*)*m_dwTargets)) )
{
return FALSE;
}
for ( i = 0, p = pszTargets ; *p ; p += strlen(p)+1, ++i )
{
if ( m_bmIsRemote.GetFlag( i ) )
{
if ( !(pTargets[i] = new CRpIf()) )
{
goto Exit;
}
}
}
// build TargetSignatureMismatch array if phase 1
if ( dwPhase == AER_PHASE1 )
{
for ( pw = pszClsids, iC = 0 ; *pw ; pw += wcslen(pw)+1, ++iC )
{
}
m_bmTargetSignatureMismatch.Init( m_dwTargets * iC, FALSE );
}
// enumerate all CLSID for extensions
for ( pw = pszClsids, iC = 0 ; *pw ; pw += wcslen(pw)+1, ++iC )
{
// if Source.Init fails skip to next one : replication I/F not available for
// this CLSID
if ( SUCCEEDED( CLSIDFromString( pw, &clsid ) ) &&
Source.Init( NULL, &clsid ) )
{
fHasSource = FALSE;
// for each one, get source signature
if ( !Source.GetSignature( &buSourceSignature, &dwSourceSignature ) )
{
fSt = FALSE;
goto Exit;
}
// enumerate targets, get signature, if <> source Serialize if not already available
// and propagate to target
for ( i = 0, p = pszTargets ; *p ; p += strlen(p)+1, ++i )
{
if ( IsCancelled() )
{
fSt = FALSE;
goto Exit;
}
if ( pTargets[i] &&
!GetTargetError( i ) &&
pTargets[i]->Init( p, &clsid ) )
{
switch ( dwPhase )
{
case AER_PHASE1:
if ( !pTargets[i]->GetSignature( &buTargetSignature,
&dwTargetSignature ) )
{
SetTargetError( i, GetLastError() );
}
else if ( dwSourceSignature != dwTargetSignature ||
memcmp( buSourceSignature.QueryPtr(),
buTargetSignature.QueryPtr(),
dwTargetSignature ) )
{
if ( !fHasSource &&
!Source.Serialize( &buSerialize, &dwSerialize ) )
{
fSt = FALSE;
goto Exit;
}
fHasSource = TRUE;
SetTargetSignatureMismatch( i, iC, TRUE );
if ( !Source.Propagate( p, strlen(p)+1 ) )
{
SetTargetError( i, GetLastError() );
}
else if( !pTargets[i]->DeSerialize( &buSerialize, dwSerialize ) )
{
SetTargetError( i, GetLastError() );
}
}
break;
case AER_PHASE2:
if ( fFirstPhase2Clsid )
{
if ( FAILED( hr = MTS_Propagate2( strlen(p)+1, (PBYTE)p, TRUE ) ) )
{
SetTargetError( i, HRESULTTOWIN32(hr) );
}
}
if ( !Source.Propagate2( p,
strlen(p)+1,
GetTargetSignatureMismatch( i, iC ) ) )
{
SetTargetError( i, GetLastError() );
}
if ( QueryFlags() & MD_SYNC_FLAG_CHECK_ADMINEX_SIGNATURE )
{
if ( !pTargets[i]->GetSignature( &buTargetSignature, &dwTargetSignature ) )
{
SetTargetError( i, GetLastError() );
}
else if ( dwSourceSignature != dwTargetSignature ||
memcmp( buSourceSignature.QueryPtr(), buTargetSignature.QueryPtr(), dwTargetSignature ) )
{
SetTargetError( i, ERROR_REVISION_MISMATCH );
}
}
break;
}
pTargets[i]->Terminate();
}
}
Source.Terminate();
}
fFirstPhase2Clsid = FALSE;
}
Exit:
Source.Terminate();
if ( pTargets )
{
for ( i = 0 ; i < m_dwTargets ; ++i )
{
if ( pTargets[i] )
{
if ( m_bmIsRemote.GetFlag( i ) )
{
pTargets[i]->Terminate();
}
delete pTargets[i];
}
}
LocalFree( pTargets );
}
return fSt;
#else
return TRUE;
#endif
}
HRESULT
MdSync::Synchronize(
LPSTR pszTargets,
LPDWORD pdwResults,
DWORD dwFlags,
LPDWORD pStat
)
/*++
Routine Description:
Entry point for synchronize COM method
Arguments:
pszTargets - multisz of target computer names
can include local computer, will be ignored during synchro
pdwResults - updated with error code for each target
dwFlags - flags, no flag defined for now. Should be 0.
pStat - ptr to stat struct
Returns:
status of request
--*/
{
return m_Sync.Sync( pszTargets, pdwResults, dwFlags, (SYNC_STAT*)pStat );
}
HRESULT
MdSync::Cancel(
)
/*++
Routine Description:
Entry point for cancel COM method
Arguments:
None
Returns:
status of request
--*/
{
return m_Sync.Cancel();
}
HRESULT
MTS_Propagate2
(
/* [in] */ DWORD dwBufferSize,
/* [size_is][in] */ unsigned char __RPC_FAR *pszBuffer,
/* [in] */ DWORD dwSignatureMismatch
)
{
HRESULT hr = NOERROR;
BSTR bstrSourceMachineName = NULL;
BSTR bstrTargetMachineName = NULL;
CHAR pszComputerName[MAX_COMPUTERNAME_LENGTH+1];
DWORD cch = MAX_COMPUTERNAME_LENGTH+1;
//pszBuffer Contains TargetMachineName(ANSI)
DBG_ASSERT(pszBuffer);
if ((BOOL)dwSignatureMismatch == FALSE)
{
DBGPRINTF((DBG_CONTEXT, "Signature is identical, MTS replication is not triggered.\n"));
return hr;
}
if (GetComputerName(pszComputerName, &cch))
{
WCHAR wszMachineName[MAX_COMPUTERNAME_LENGTH+1];
DWORD dwSuccess = 0;
dwSuccess = MultiByteToWideChar(0, 0, pszComputerName, -1, wszMachineName, MAX_COMPUTERNAME_LENGTH+1);
DBG_ASSERT(dwSuccess);
bstrSourceMachineName = SysAllocString(wszMachineName);
dwSuccess = MultiByteToWideChar(0, 0, (LPCSTR)pszBuffer, dwBufferSize, wszMachineName, MAX_COMPUTERNAME_LENGTH+1);
DBG_ASSERT(dwSuccess);
bstrTargetMachineName = SysAllocString(wszMachineName);
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
DBGPRINTF((DBG_CONTEXT, "GetComputerName failed, hr = %08x\n",
hr));
}
if (SUCCEEDED(hr))
{
ICOMReplicate* piReplCat = NULL;
DBG_ASSERT(bstrSourceMachineName != NULL && bstrTargetMachineName != NULL);
hr = CoCreateInstance(CLSID_ReplicateCatalog,
NULL,
CLSCTX_INPROC_SERVER,
IID_ICOMReplicate,
(void**)&piReplCat);
if (SUCCEEDED(hr))
{
DBG_ASSERT(piReplCat);
//
// For now, just call the replication methods in a row.
//
//
// EBK 5/8/2000 Whistler #83172
// Removed bug comment from this. According to NT Bug 37371
// the best solution we came up with is the solution that is implemented
// here, so no more work or investigation is required.
//
// Replication of the iis com apps is not working. The problem
// is that com will not replicate iis applciations unless we
// tell it to (using the COMREPL_OPTION_REPLICATE_IIS_APPS flag).
// But if we tell it to replicate our apps, then com requires
// that the activation identity (IWAM_*) be the same on both
// machines. In order to do that we would need to replicate the
// IWAM_ account. There are a number of problems with this, not
// the least of which is encrypting the password during transfer.
// So to get this working at least reasonably well, I'm going to
// continue passing 0 here. And delete/recreate the isolated
// apps on the target in wamreg during the first phase of
// replication.
//
// See NT Bug 378371 for more details
//
hr = piReplCat->Initialize( bstrSourceMachineName, 0 );
if ( FAILED( hr ) )
{
DBGPRINTF(( DBG_CONTEXT,
"Initialize() failed with hr = %08x\n",
hr ));
piReplCat->Release();
goto Finished;
}
hr = piReplCat->ExportSourceCatalogFiles();
if ( FAILED( hr ) )
{
DBGPRINTF(( DBG_CONTEXT,
"ExportSourceCatalogFiles() failed with hr = %08x\n",
hr ));
piReplCat->CleanupSourceShares();
piReplCat->Release();
goto Finished;
}
hr = piReplCat->CopyFilesToTarget( bstrTargetMachineName );
if ( FAILED( hr ) )
{
DBGPRINTF(( DBG_CONTEXT,
"CopyCatalogFilesToTarget() failed with hr = %08x\n",
hr ));
piReplCat->CleanupSourceShares();
piReplCat->Release();
goto Finished;
}
hr = piReplCat->InstallTarget( bstrTargetMachineName );
if (FAILED(hr))
{
DBGPRINTF(( DBG_CONTEXT,
"InstallCatalogOnTarget() failed with hr = %08x\n",
hr ));
piReplCat->CleanupSourceShares();
piReplCat->Release();
goto Finished;
}
piReplCat->CleanupSourceShares();
piReplCat->Release();
}
else
{
DBGPRINTF((DBG_CONTEXT, "Failed to CoCreateInstance of CLSID_ReplicateCatalog, hr = %08x\n",
hr));
}
}
Finished:
if (bstrSourceMachineName)
{
SysFreeString(bstrSourceMachineName);
bstrSourceMachineName = NULL;
}
if (bstrTargetMachineName)
{
SysFreeString(bstrTargetMachineName);
bstrTargetMachineName = NULL;
}
return hr;
}