// 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 #include #include #include #include #include "mdsync.h" #include "stdafx.h" #include #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 #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"//Cert11" ); wcscpy( achCreatePath+dwL, L"/" ); wcscpy( achCreateObject, L"Cert11" ); dwSerialId = MD_SERIAL_ALL_CERT11; md.dwMDIdentifier = dwSerialId; break; case MD_SERIAL_DIGEST: wcscpy( achPath+dwL, L"//Digest" ); wcscpy( achCreatePath+dwL, L"/" ); 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; }