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.
2966 lines
71 KiB
2966 lines
71 KiB
// 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;
|
|
}
|