|
|
// Copyright (c) 1997-1999 Microsoft Corporation
//
// DS API wrappers
//
// 12-16-97 sburns
#include "headers.hxx"
#include "ds.hpp"
#include "resource.h"
#include "state.hpp"
#include "common.hpp"
#include "ProgressDialog.hpp"
// CODEWORK: remove the exception throwing architecture.
DS::Error::Error(HRESULT hr_, int summaryResID) : Win::Error(hr_, summaryResID) { }
DS::Error::Error(HRESULT hr_, const String& msg, const String& sum) : Win::Error(hr_, msg, sum) { }
bool DS::IsDomainNameInUse(const String& domainName) { LOG_FUNCTION(DS::IsDomainNameInUse); ASSERT(!domainName.empty());
bool result = false; if (!domainName.empty()) { HRESULT hr = MyNetValidateName(domainName, ::NetSetupNonExistentDomain); if (hr == Win32ToHresult(ERROR_DUP_NAME)) { result = true; } }
LOG( String::format( L"The domain name %1 %2 in use.", domainName.c_str(), result ? L"is" : L"is NOT"));
return result; }
bool DS::DisjoinDomain() throw (DS::Error) { LOG_FUNCTION(DS::DisjoinDomain);
// make 1st attempt assuming that the current user has account
// deletion priv on the domain.
LOG(L"Calling NetUnjoinDomain (w/ account delete)");
HRESULT hr = Win32ToHresult( ::NetUnjoinDomain( 0, // this server
0, // current account,
0, // current password
NETSETUP_ACCT_DELETE));
LOG_HRESULT(hr);
if (FAILED(hr)) { // make another attempt, not removing the computer account
LOG(L"Calling NetUnjoinDomain again, w/o account delete");
hr = Win32ToHresult(::NetUnjoinDomain(0, 0, 0, 0));
LOG_HRESULT(hr);
if (SUCCEEDED(hr)) { // the unjoin was successful, but the computer account was
// left behind.
return false; } }
if (FAILED(hr)) { throw DS::Error(hr, IDS_DISJOIN_DOMAIN_FAILED); }
return true; }
void DS::JoinDomain( const String& domainDNSName, const String& dcName, const String& userName, const EncryptedString& password, const String& userDomainName) throw (DS::Error) { LOG_FUNCTION(DS::JoinDomain); ASSERT(!domainDNSName.empty()); ASSERT(!userName.empty());
// password may be empty
ULONG flags = NETSETUP_JOIN_DOMAIN | NETSETUP_ACCT_CREATE | NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_ACCT_DELETE;
String massagedUserName = MassageUserName(userDomainName, userName); String domain = domainDNSName;
if (!dcName.empty()) { domain += L"\\" + dcName; }
HRESULT hr = MyNetJoinDomain( domain.c_str(), massagedUserName.c_str(), password, flags);
LOG_HRESULT(hr);
if (FAILED(hr)) { State& state = State::GetInstance(); state.SetOperationResultsMessage( String::format(IDS_UNABLE_TO_JOIN_DOMAIN, domainDNSName.c_str()));
throw DS::Error(hr, IDS_JOIN_DOMAIN_FAILED); } }
DWORD MyDsRoleCancel(DSROLE_SERVEROP_HANDLE& handle) { LOG(L"Calling DsRoleCancel"); LOG(L"lpServer : (null)");
DWORD status = ::DsRoleCancel(0, handle);
LOG(String::format(L"Error 0x%1!X! (!0 => error)", status));
return status; }
DWORD MyDsRoleGetDcOperationProgress( DSROLE_SERVEROP_HANDLE& handle, DSROLE_SERVEROP_STATUS*& status) { // LOG(L"Calling DsRoleGetDcOperationProgress");
status = 0; DWORD err = ::DsRoleGetDcOperationProgress(0, handle, &status);
// LOG(
// String::format(
// L"Error 0x%1!X! (!0 => error, 0x%2!X! = ERROR_IO_PENDING)",
// err,
// ERROR_IO_PENDING));
// if (status)
// {
// LOG(
// String::format(
// L"OperationStatus : 0x%1!X!",
// status->OperationStatus));
// LOG(status->CurrentOperationDisplayString);
// }
// DWORD err = ERROR_IO_PENDING;
// status = new DSROLE_SERVEROP_STATUS;
// status->CurrentOperationDisplayString = L"proceeding";
// status->OperationStatus = 0;
return err; }
void DoProgressLoop( DSROLE_SERVEROP_HANDLE& handle, ProgressDialog& progressDialog) throw (DS::Error) { LOG_FUNCTION(DoProgressLoop);
State& state = State::GetInstance();
if (state.GetOperation() == State::DEMOTE) { // not cancelable -- turn off the cancel button.
progressDialog.UpdateButton(String()); } else { // turn on the cancel button.
progressDialog.UpdateButton(IDS_PROGRESS_CANCEL); }
DWORD netErr = 0; bool criticalComplete = false; bool buttonUpdatedToFinishLater = false; String lastMessage; ProgressDialog::WaitCode cancelButton;
do { // wait 1500ms or for the user to hit cancel
cancelButton = progressDialog.WaitForButton(1500);
// get the current status of the operation
DSROLE_SERVEROP_STATUS* status = 0; netErr = MyDsRoleGetDcOperationProgress(handle, status);
if (netErr != ERROR_SUCCESS && netErr != ERROR_IO_PENDING) { // operation complete
break; }
if (!status) { LOG(L"Operation status not returned!"); ASSERT(false); continue; }
// update the message display
String message = status->CurrentOperationDisplayString; if (message != lastMessage) { progressDialog.UpdateText(message); lastMessage = message; }
// save the status flags for later use.
ULONG statusFlags = status->OperationStatus;
::DsRoleFreeMemory(status);
do { if (cancelButton != ProgressDialog::PRESSED) { break; }
// if we make it here, user pressed the cancel button
LOG(L"DoProgressLoop: handling cancel"); ASSERT(state.GetOperation() != State::DEMOTE);
if (criticalComplete) { // inform the user that the install is done, and that they're
// cancelling the non-critical replication.
popup.Info( progressDialog.GetHWND(), String::load(IDS_CANCEL_NON_CRITICAL_REPLICATION));
// this should return ERROR_SUCCESS, and the promotion will
// be considered complete.
progressDialog.UpdateText(IDS_CANCELLING_REPLICATION); progressDialog.UpdateAnimation(IDR_AVI_DEMOTE); netErr = MyDsRoleCancel(handle);
// fall out of the inner, then the outer, loop. Then we will
// get the operation results, which should indicate that the
// promotion is complete, and non-critical replication was
// canceled.
break; }
// Still doing promote, verify that the user really wants to roll
// back to server state.
if ( popup.MessageBox( progressDialog.GetHWND(), String::load(IDS_CANCEL_PROMOTE), MB_YESNO | MB_ICONWARNING) == IDYES) { // this should return ERROR_SUCCESS, and the promotion will
// be rolled back.
progressDialog.UpdateText(IDS_CANCELLING); progressDialog.UpdateAnimation(IDR_AVI_DEMOTE); netErr = MyDsRoleCancel(handle);
// fall out of the inner, then the outer, loop. Then we will
// get the operation results, which should indicate that the
// promotion was cancelled as a failure code. We handle this
// failure code in the same manner as all others.
break; }
// user decided to press on. reset the cancel button
progressDialog.UpdateButton(IDS_PROGRESS_CANCEL); progressDialog.RevertToOriginalAnimation(); buttonUpdatedToFinishLater = false; } while (0);
criticalComplete = criticalComplete || statusFlags & DSROLE_CRITICAL_OPERATIONS_COMPLETED;
if (criticalComplete) { if (cancelButton == ProgressDialog::PRESSED) { // we add this message without actually checking the operation
// results flags because for all we know, the replication will
// finish before we get around to checking. It is still correct
// to say the the replication has stopped, and will start after
// reboot. This is always the case.
state.AddFinishMessage( String::load(IDS_NON_CRITICAL_REPLICATION_CANCELED)); } else { if (!buttonUpdatedToFinishLater) { progressDialog.UpdateButton(IDS_FINISH_REPLICATION_LATER); buttonUpdatedToFinishLater = true; } } } } while (netErr == ERROR_IO_PENDING);
progressDialog.UpdateButton(String()); buttonUpdatedToFinishLater = false;
LOG(L"Progress loop complete.");
if (netErr == ERROR_SUCCESS) { // we successfully endured the wait. let's see how it turned out.
DSROLE_SERVEROP_RESULTS* results;
LOG(L"Calling DsRoleGetDcOperationResults");
netErr = ::DsRoleGetDcOperationResults(0, handle, &results);
LOG(String::format(L"Error 0x%1!X! (!0 => error)", netErr));
if (netErr == ERROR_SUCCESS) { // we got the results
ASSERT(results); if (results) { LOG(L"Operation results:"); LOG( String::format( L"OperationStatus : 0x%1!X! !0 => error", results->OperationStatus)); LOG( String::format( L"DisplayString : %1", results->OperationStatusDisplayString)); LOG( String::format( L"ServerInstalledSite : %1", results->ServerInstalledSite)); LOG( String::format( L"OperationResultsFlags: 0x%1!X!", results->OperationResultsFlags));
netErr = results->OperationStatus;
// here, netErr will be some error code if the promote was
// cancelled and successfully rolled back. Since it may be
// possible that the cancel was too late (e.g. the user took
// too long to confirm the cancel), the promote may have
// finished. If that's the case, tell the user that the cancel
// failed.
if ( netErr == ERROR_SUCCESS && cancelButton == ProgressDialog::PRESSED) { // the promote finished, and the cancel button was pressed.
if (!criticalComplete) // 363590
{ // we didn't find out if the non-critical replication phase
// started. So the cancel button still said 'Cancel', and
// yet, the operation finished. so, this means that the
// promote simply completed before the cancel was received.
popup.Info( progressDialog.GetHWND(), IDS_CANCEL_TOO_LATE); } }
String message = results->OperationStatusDisplayString ? results->OperationStatusDisplayString : L""; String site = results->ServerInstalledSite ? results->ServerInstalledSite : L"";
progressDialog.UpdateText(message);
if (!site.empty()) { state.SetInstalledSite(site); } if (!message.empty()) { state.SetOperationResultsMessage(message); }
state.SetOperationResultsFlags(results->OperationResultsFlags); if ( results->OperationResultsFlags & DSROLE_NON_FATAL_ERROR_OCCURRED) { state.AddFinishMessage( String::load(IDS_NON_FATAL_ERRORS_OCCURRED)); } if ( (results->OperationResultsFlags & DSROLE_NON_CRITICAL_REPL_NOT_FINISHED) && cancelButton != ProgressDialog::PRESSED ) { // cancel not pressed and critial replication bombed
state.AddFinishMessage( String::load(IDS_NON_CRITICAL_REPL_FAILED)); } if ( results->OperationResultsFlags & DSROLE_IFM_RESTORED_DATABASE_FILES_MOVED) { LOG(L"restored files were moved");
if (netErr != ERROR_SUCCESS) { // only need to mention this in the case of a fatal failure;
// e.g. don't add this finish text if non-fatal errors
// occurred.
// NTRAID#NTBUG9-330378-2001/02/28-sburns
state.AddFinishMessage( String::load(IDS_MUST_RESTORE_IFM_FILES_AGAIN)); } }
::DsRoleFreeMemory(results); } } }
if (netErr != ERROR_SUCCESS) { // couldn't get progress updates, or couldn't get results,
// or result was a failure
throw DS::Error(Win32ToHresult(netErr), IDS_WIZARD_TITLE); } }
void EmptyFolder(const String& path) throw (DS::Error) { LOG_FUNCTION2(EmptyFolder, path); ASSERT(FS::PathExists(path));
// check for files/subfolders once again (we checked the first time on
// validating the path), in case something has written to the folder
// since we validated it last.
if (!FS::IsFolderEmpty(path)) { // nuke the files in the directory
LOG(String::format(L"Emptying %1", path.c_str()));
String wild = path;
// REVIEW: wild[wild.length() - 1] is the same as *(wild.rbegin())
// which is cheaper?
if (wild[wild.length() - 1] != L'\\') { wild += L"\\"; }
wild += L"*.*";
FS::Iterator iter( wild, FS::Iterator::INCLUDE_FILES | FS::Iterator::RETURN_FULL_PATHS);
HRESULT hr = S_OK; String current;
while ((hr = iter.GetCurrent(current)) == S_OK) { LOG(String::format(L"Deleting %1", current.c_str()));
hr = Win::DeleteFile(current); if (FAILED(hr)) { int msgId = IDS_EMPTY_DIR_FAILED;
if (hr == Win32ToHresult(ERROR_ACCESS_DENIED)) { msgId = IDS_EMPTY_DIR_FAILED_ACCESS_DENIED; }
throw DS::Error( S_OK, // so as not to trigger credentials dialog
String::format( msgId, GetErrorMessage(hr).c_str(), path.c_str()), String::load(IDS_ERROR_PREPARING_OPERATION)); }
hr = iter.Increment(); BREAK_ON_FAILED_HRESULT(hr); } } }
HRESULT SetupPaths() { LOG_FUNCTION(SetupPaths);
State& state = State::GetInstance(); String dbPath = state.GetDatabasePath(); String logPath = state.GetLogPath(); String sysvolPath = state.GetSYSVOLPath();
ASSERT(!dbPath.empty()); ASSERT(!logPath.empty()); ASSERT(!sysvolPath.empty());
HRESULT hr = S_OK;
do { if (FS::PathExists(dbPath)) { EmptyFolder(dbPath); } else { hr = FS::CreateFolder(dbPath); BREAK_ON_FAILED_HRESULT(hr); }
if (FS::PathExists(logPath)) { EmptyFolder(logPath); } else { hr = FS::CreateFolder(logPath); BREAK_ON_FAILED_HRESULT(hr); }
if (FS::PathExists(sysvolPath)) { EmptyFolder(sysvolPath); } else { hr = FS::CreateFolder(sysvolPath); BREAK_ON_FAILED_HRESULT(hr); } } while (0);
return hr; }
// Sets the promotion flags based on options set in the unattended execution
// answerfile.
//
// state - IN reference to the global State object
//
// flags - IN/OUT promote API flags, may be modified on exit
void SetAnswerFilePromoteFlags( State& state, ULONG& flags) { LOG_FUNCTION(SetAnswerFilePromoteFlags);
if (state.UsingAnswerFile()) { // set flags based on unattended execution options
// if the safe mode admin password is not specified, then we set a
// flag to cause the promote APIs to copy the current local admin
// password.
EncryptedString safeModePassword = state.GetSafeModeAdminPassword();
if (safeModePassword.IsEmpty() && state.RunHiddenUnattended()) { // user did not supply a safemode password, and he did not have
// an opportunity to do so (if the wizard went interactive)
if (!state.IsSafeModeAdminPwdOptionPresent()) { // the safe mode pwd key is not present in the answerfile
flags |= DSROLE_DC_DEFAULT_REPAIR_PWD; } }
String option = state.GetAnswerFileOption( AnswerFile::OPTION_CRITICAL_REPLICATION_ONLY); if (option.icompare(AnswerFile::VALUE_YES) == 0) { flags |= DSROLE_DC_CRITICAL_REPLICATION_ONLY; } }
LOG(String::format(L"0x%1!X!", flags)); }
void DS::CreateReplica(ProgressDialog& progressDialog, bool invokeForUpgrade) throw (DS::Error) { LOG_FUNCTION(DS::CreateReplica);
State& state = State::GetInstance();
String domain = state.GetReplicaDomainDNSName(); String dbPath = state.GetDatabasePath(); String logPath = state.GetLogPath(); String sysvolPath = state.GetSYSVOLPath(); String site = state.GetSiteName(); String username = state.GetUsername(); String replicationDc = state.GetReplicationPartnerDC(); String sourcePath = state.GetReplicationSourcePath(); bool useSourcePath = state.ReplicateFromMedia();
EncryptedString syskey = state.GetSyskey(); EncryptedString safeModePassword = state.GetSafeModeAdminPassword(); EncryptedString password = state.GetPassword();
bool useCurrentUserCreds = username.empty(); ULONG flags = DSROLE_DC_FORCE_TIME_SYNC | DSROLE_DC_CREATE_TRUST_AS_REQUIRED;
if (invokeForUpgrade) { flags |= DSROLE_DC_DOWNLEVEL_UPGRADE; } if (state.GetDomainControllerReinstallFlag()) { flags |= DSROLE_DC_ALLOW_DC_REINSTALL; }
SetAnswerFilePromoteFlags(state, flags);
if (useSourcePath) { if (state.GetRestoreGc()) { flags |= DSROLE_DC_REQUEST_GC; }
}
ASSERT(!domain.empty());
if (!useCurrentUserCreds) { String user_domain = state.GetUserDomainName(); username = MassageUserName(user_domain, username); }
HRESULT hr = S_OK; do { hr = SetupPaths(); BREAK_ON_FAILED_HRESULT(hr);
LOG(L"Calling DsRoleDcAsReplica"); LOG( L"lpServer : (null)"); LOG(String::format(L"lpDnsDomainName : %1", domain.c_str())); LOG(String::format(L"lpReplicaServer : %1", replicationDc.empty() ? L"(null)" : replicationDc.c_str())); LOG(String::format(L"lpSiteName : %1", site.empty() ? L"(null)" : site.c_str())); LOG(String::format(L"lpDsDatabasePath : %1", dbPath.c_str())); LOG(String::format(L"lpDsLogPath : %1", logPath.c_str())); LOG(String::format(L"lpRestorePath : %1", useSourcePath ? sourcePath.c_str() : L"(null)")); LOG(String::format(L"lpSystemVolumeRootPath : %1", sysvolPath.c_str())); LOG(String::format(L"lpAccount : %1", useCurrentUserCreds ? L"(null)" : username.c_str())); LOG(String::format(L"Options : 0x%1!X!", flags));
WCHAR* safeModePasswordCopy = 0; if (!safeModePassword.IsEmpty()) { safeModePasswordCopy = safeModePassword.GetClearTextCopy(); } WCHAR* passwordCopy = 0; if (!useCurrentUserCreds) { passwordCopy = password.GetClearTextCopy(); } // The api wants to scribble over the syskey, so we make a copy for
// it to do so.
WCHAR* syskeyCopy = 0; if (useSourcePath && !syskey.IsEmpty()) { syskeyCopy = syskey.GetClearTextCopy(); }
DSROLE_SERVEROP_HANDLE handle = 0; hr = Win32ToHresult( ::DsRoleDcAsReplica( 0, // this server
domain.c_str(),
// possibly empty, e.g. if we didn't join a domain...
replicationDc.empty() ? 0 : replicationDc.c_str(), site.empty() ? 0 : site.c_str(), dbPath.c_str(), logPath.c_str(), useSourcePath ? sourcePath.c_str() : 0, sysvolPath.c_str(), syskeyCopy, (useCurrentUserCreds ? 0 : username.c_str()), passwordCopy, safeModePasswordCopy, flags, &handle));
if (safeModePasswordCopy) { safeModePassword.DestroyClearTextCopy(safeModePasswordCopy); }
if (passwordCopy) { password.DestroyClearTextCopy(passwordCopy); }
if (syskeyCopy) { syskey.DestroyClearTextCopy(syskeyCopy); }
BREAK_ON_FAILED_HRESULT(hr);
DoProgressLoop(handle, progressDialog); } while (0);
if (FAILED(hr)) { throw DS::Error(hr, IDS_WIZARD_TITLE); } }
void DS::CreateNewDomain(ProgressDialog& progressDialog) throw (DS::Error) { LOG_FUNCTION(DS::CreateNewDomain);
State& state = State::GetInstance();
String domain = state.GetNewDomainDNSName(); String flatName = state.GetNewDomainNetbiosName(); String site = state.GetSiteName(); String dbPath = state.GetDatabasePath(); String logPath = state.GetLogPath(); String sysvolPath = state.GetSYSVOLPath(); String parent = state.GetParentDomainDnsName(); String username = state.GetUsername();
EncryptedString password = state.GetPassword(); EncryptedString safeModePassword = state.GetSafeModeAdminPassword(); EncryptedString adminPassword = state.GetAdminPassword();
State::Operation operation = state.GetOperation(); bool useParent = ( operation == State::TREE || operation == State::CHILD); bool useCurrentUserCreds = username.empty();
ULONG flags = DSROLE_DC_CREATE_TRUST_AS_REQUIRED | DSROLE_DC_FORCE_TIME_SYNC;
if (state.GetDomainReinstallFlag()) { flags |= DSROLE_DC_ALLOW_DOMAIN_REINSTALL; }
if (state.GetDomainControllerReinstallFlag()) { flags |= DSROLE_DC_ALLOW_DC_REINSTALL; }
if (operation == State::TREE) { flags |= DSROLE_DC_TRUST_AS_ROOT;
ASSERT(!parent.empty()); }
SetAnswerFilePromoteFlags(state, flags);
if (state.ShouldAllowAnonymousAccess()) { flags |= DSROLE_DC_ALLOW_ANONYMOUS_ACCESS; }
if (operation == State::FOREST) { flags |= DSROLE_DC_NO_NET;
ASSERT(!site.empty()); }
#ifdef DBG
else if (operation == State::CHILD) { ASSERT(!parent.empty()); }
ASSERT(!domain.empty()); ASSERT(!flatName.empty());
// parent may be empty
#endif
if (!useCurrentUserCreds) { String userDomain = state.GetUserDomainName(); username = MassageUserName(userDomain, username); }
HRESULT hr = S_OK; do { hr = SetupPaths(); BREAK_ON_FAILED_HRESULT(hr);
LOG(L"Calling DsRoleDcAsDc"); LOG( L"lpServer : (null)"); LOG(String::format(L"lpDnsDomainName : %1", domain.c_str())); LOG(String::format(L"lpFlatDomainName : %1", flatName.c_str())); LOG(String::format(L"lpSiteName : %1", site.empty() ? L"(null)" : site.c_str())); LOG(String::format(L"lpDsDatabasePath : %1", dbPath.c_str())); LOG(String::format(L"lpDsLogPath : %1", logPath.c_str())); LOG(String::format(L"lpSystemVolumeRootPath : %1", sysvolPath.c_str())); LOG(String::format(L"lpParentDnsDomainName : %1", useParent ? parent.c_str() : L"(null)")); LOG( L"lpParentServer : (null)"); LOG(String::format(L"lpAccount : %1", useCurrentUserCreds ? L"(null)" : username.c_str())); LOG(String::format(L"Options : 0x%1!X!", flags));
WCHAR* safeModePasswordCopy = 0; if (!safeModePassword.IsEmpty()) { safeModePasswordCopy = safeModePassword.GetClearTextCopy(); }
WCHAR* adminPasswordCopy = 0; if (!adminPassword.IsEmpty()) { adminPasswordCopy = adminPassword.GetClearTextCopy(); }
WCHAR* passwordCopy = 0; if (!useCurrentUserCreds) { passwordCopy = password.GetClearTextCopy(); } DSROLE_SERVEROP_HANDLE handle = 0; hr = Win32ToHresult( DsRoleDcAsDc( 0, // this server
domain.c_str(), flatName.c_str(), adminPasswordCopy, site.empty() ? 0 : site.c_str(), dbPath.c_str(), logPath.c_str(), sysvolPath.c_str(), (useParent ? parent.c_str() : 0), 0, // let API pick a server
(useCurrentUserCreds ? 0 : username.c_str()), passwordCopy, safeModePasswordCopy, flags, &handle)); BREAK_ON_FAILED_HRESULT(hr);
if (safeModePasswordCopy) { safeModePassword.DestroyClearTextCopy(safeModePasswordCopy); }
if (adminPasswordCopy) { adminPassword.DestroyClearTextCopy(adminPasswordCopy); }
if (passwordCopy) { password.DestroyClearTextCopy(passwordCopy); }
DoProgressLoop(handle, progressDialog); } while (0);
if (FAILED(hr)) { throw DS::Error(hr, IDS_WIZARD_TITLE); } }
void DS::UpgradeBDC(ProgressDialog& progressDialog) throw (DS::Error) { LOG_FUNCTION(DS::UpgradeBDC);
// seems non-intuitive to abort the upgrade to do the upgrade, but here
// the abort removes dcpromo autostart, and turns the machine into a
// standalone server. Then we proceed to make it a replica.
DS::AbortBDCUpgrade(true); DS::CreateReplica(progressDialog, true); }
void DS::UpgradePDC(ProgressDialog& progressDialog) throw (DS::Error) { LOG_FUNCTION(DS::UpgradePDC);
State& state = State::GetInstance(); ASSERT(state.GetRunContext() == State::PDC_UPGRADE);
String domain = state.GetNewDomainDNSName(); String site = state.GetSiteName(); String dbPath = state.GetDatabasePath(); String logPath = state.GetLogPath(); String sysvolPath = state.GetSYSVOLPath(); String parent = state.GetParentDomainDnsName(); String username = state.GetUsername();
EncryptedString password = state.GetPassword(); EncryptedString safeModePassword = state.GetSafeModeAdminPassword();
State::Operation operation = state.GetOperation(); bool useParent = ( operation == State::TREE || operation == State::CHILD); bool useCurrentUserCreds = username.empty();
ULONG flags = DSROLE_DC_CREATE_TRUST_AS_REQUIRED;
if (state.GetDomainReinstallFlag()) { flags |= DSROLE_DC_ALLOW_DOMAIN_REINSTALL; }
if (state.GetDomainControllerReinstallFlag()) { flags |= DSROLE_DC_ALLOW_DC_REINSTALL; }
if (state.GetSetForestVersionFlag()) { flags |= DSROLE_DC_SET_FOREST_CURRENT; }
if (operation == State::TREE) { flags |= DSROLE_DC_TRUST_AS_ROOT | DSROLE_DC_FORCE_TIME_SYNC; ASSERT(!parent.empty()); } else if (operation == State::CHILD) { flags |= DSROLE_DC_FORCE_TIME_SYNC; ASSERT(!parent.empty()); }
SetAnswerFilePromoteFlags(state, flags);
if (state.ShouldAllowAnonymousAccess()) { flags |= DSROLE_DC_ALLOW_ANONYMOUS_ACCESS; }
#ifdef DBG
ASSERT(!domain.empty());
// parent may be empty
if (operation == State::FOREST) { ASSERT(!site.empty()); } #endif
if (!useCurrentUserCreds) { String userDomain = state.GetUserDomainName(); username = MassageUserName(userDomain, username); }
HRESULT hr = S_OK; do { hr = SetupPaths(); BREAK_ON_FAILED_HRESULT(hr);
LOG(L"Calling DsRoleUpgradeDownlevelServer"); LOG(String::format(L"lpDnsDomainName : %1", domain.c_str())); LOG(String::format(L"lpSiteName : %1", site.empty() ? L"(null)" : site.c_str())); LOG(String::format(L"lpDsDatabasePath : %1", dbPath.c_str())); LOG(String::format(L"lpDsLogPath : %1", logPath.c_str())); LOG(String::format(L"lpSystemVolumeRootPath : %1", sysvolPath.c_str())); LOG(String::format(L"lpParentDnsDomainName : %1", useParent ? parent.c_str() : L"(null)")); LOG( L"lpParentServer : (null)"); LOG(String::format(L"lpAccount : %1", useCurrentUserCreds ? L"(null)" : username.c_str())); LOG(String::format(L"Options : 0x%1!X!", flags));
WCHAR* safeModePasswordCopy = 0; if (!safeModePassword.IsEmpty()) { safeModePasswordCopy = safeModePassword.GetClearTextCopy(); } WCHAR* passwordCopy = 0; if (!useCurrentUserCreds) { passwordCopy = password.GetClearTextCopy(); }
DSROLE_SERVEROP_HANDLE handle = 0; hr = Win32ToHresult( ::DsRoleUpgradeDownlevelServer( domain.c_str(), site.empty() ? 0 : site.c_str(), dbPath.c_str(), logPath.c_str(), sysvolPath.c_str(), (useParent ? parent.c_str() : 0), 0, // let API pick a server
(useCurrentUserCreds ? 0 : username.c_str()), passwordCopy, safeModePasswordCopy, flags, &handle)); BREAK_ON_FAILED_HRESULT(hr);
if (safeModePasswordCopy) { safeModePassword.DestroyClearTextCopy(safeModePasswordCopy); } if (passwordCopy) { password.DestroyClearTextCopy(passwordCopy); }
DoProgressLoop(handle, progressDialog); } while (0);
if (FAILED(hr)) { throw DS::Error(hr, IDS_UPGRADE_DC_FAILED); } }
// Converts the list of ndncs into a form more easily digestible by the demote
// API: an array of null-terminated arrays of WCHAR, with the last element a
// null. The result must be deallocated by DeallocateAppPartitionList.
//
// entryCount - out, receieves the number of strings allocated.
PCWSTR* AllocateAppPartitionList(int& entryCount) { LOG_FUNCTION(AllocateAppPartitionList);
const StringList& ndncList = State::GetInstance().GetAppPartitionList(); entryCount = 0;
if (!ndncList.size()) { // empty list
return 0; }
PWSTR* result = new PWSTR[ndncList.size() + 1];
// REVIEWED-2002/02/26-sburns correct byte count passed.
::ZeroMemory(result, (ndncList.size() + 1) * sizeof PWSTR); for ( StringList::iterator i = ndncList.begin(); i != ndncList.end(); ++i) { ASSERT(i->length()); size_t len = i->length() + 1; result[entryCount] = new WCHAR[len];
// REVIEWED-2002/02/26-sburns correct byte count passed.
::ZeroMemory(result[entryCount], len * sizeof WCHAR);
i->copy(result[entryCount], len - 1); ++entryCount; }
return const_cast<PCWSTR*>(result); }
void DeallocateAppPartitionList(PCWSTR*& partitionList) { LOG_FUNCTION(DeallocateAppPartitionList);
if (partitionList) { PCWSTR* listCopy = partitionList;
while (*listCopy) { delete *listCopy; // *listCopy = 0;
++listCopy; } delete partitionList; partitionList = 0; } }
void DS::DemoteDC(ProgressDialog& progressDialog) throw (DS::Error) { LOG_FUNCTION(DS::DemoteDC);
State& state = State::GetInstance();
String username = state.GetUsername(); bool useCurrentUserCreds = username.empty(); bool isLastDc = state.IsLastDCInDomain(); bool isForcedDemotion = state.IsForcedDemotion(); EncryptedString adminPassword= state.GetAdminPassword(); EncryptedString password = state.GetPassword();
if (!useCurrentUserCreds) { String userDomain = state.GetUserDomainName(); username = MassageUserName(userDomain, username); }
ULONG options = DSROLE_DC_CREATE_TRUST_AS_REQUIRED; if (isLastDc) { options |= DSROLE_DC_DELETE_PARENT_TRUST; }
if (isForcedDemotion) { // Update our list of application partitions so that we can remove
// all of them
state.IsLastAppPartitionReplica(); options |= DSROLE_DC_FORCE_DEMOTE;
// pretend to be the last DC if we're forcing the demotion.
isLastDc = true; }
int appPartitionCount = 0; PCWSTR* appPartitionList = AllocateAppPartitionList(appPartitionCount); #ifdef LOGGING_BUILD
LOG(L"Calling DsRoleDemoteDc"); LOG( L"lpServer : (null)"); LOG( L"lpDnsDomainName : (null)"); LOG(String::format(L"ServerRole : %1", isLastDc ? L"DsRoleServerStandalone" : L"DsRoleServerMember")); LOG(String::format(L"lpAccount : %1", useCurrentUserCreds ? L"(null)" : username.c_str())); LOG(String::format(L"Options : 0x%1!X!", options)); LOG(String::format(L"fLastDcInDomain : %1", isLastDc ? L"true" : L"false")); LOG(String::format(L"cRemoteNCs : %1!d!", appPartitionCount));
for (int i = 0; i < appPartitionCount; ++i) { LOG(String::format(L"pszRemoveNCs : %1", appPartitionList[i])); } #endif // LOGGING_BUILD
WCHAR* adminPasswordCopy = 0; if (!adminPassword.IsEmpty()) { adminPasswordCopy = adminPassword.GetClearTextCopy(); } WCHAR* passwordCopy = 0; if (!useCurrentUserCreds) { passwordCopy = password.GetClearTextCopy(); }
DSROLE_SERVEROP_HANDLE handle = 0; HRESULT hr = Win32ToHresult( ::DsRoleDemoteDc( 0, // this server
0, // "default" domain hosted by this server
isLastDc ? DsRoleServerStandalone : DsRoleServerMember, (useCurrentUserCreds ? 0 : username.c_str()), passwordCopy, options, isLastDc ? TRUE : FALSE, appPartitionCount, appPartitionList, adminPasswordCopy, &handle)); LOG_HRESULT(hr);
DeallocateAppPartitionList(appPartitionList);
if (adminPasswordCopy) { adminPassword.DestroyClearTextCopy(adminPasswordCopy); } if (passwordCopy) { password.DestroyClearTextCopy(passwordCopy); }
if (SUCCEEDED(hr)) { DoProgressLoop(handle, progressDialog); } else { throw DS::Error(hr, IDS_DEMOTE_DC_FAILED); } }
void DS::AbortBDCUpgrade(bool abortForReplica) throw (DS::Error) { LOG_FUNCTION(DS::AbortBDCUpgrade);
State& state = State::GetInstance(); ASSERT(state.GetRunContext() == State::BDC_UPGRADE);
String username = state.GetUsername(); bool useCurrentUserCreds = username.empty();
EncryptedString adminPassword = state.GetAdminPassword(); EncryptedString password = state.GetPassword(); if (!useCurrentUserCreds) { String userDomain = state.GetUserDomainName(); username = MassageUserName(userDomain, username); }
ULONG options = abortForReplica ? DSROLEP_ABORT_FOR_REPLICA_INSTALL : 0;
LOG(L"Calling DsRoleAbortDownlevelServerUpgrade"); LOG(String::format(L"lpAccount : %1", useCurrentUserCreds ? L"(null)" : username.c_str())); LOG(String::format(L"Options : 0x%1!X!", options));
WCHAR* adminPasswordCopy = 0; if (!adminPassword.IsEmpty()) { adminPasswordCopy = adminPassword.GetClearTextCopy(); } WCHAR* passwordCopy = 0; if (!useCurrentUserCreds) { passwordCopy = password.GetClearTextCopy(); }
HRESULT hr = Win32ToHresult( ::DsRoleAbortDownlevelServerUpgrade( adminPasswordCopy, (useCurrentUserCreds ? 0 : username.c_str()), passwordCopy, options)); LOG_HRESULT(hr);
if (adminPasswordCopy) { adminPassword.DestroyClearTextCopy(adminPasswordCopy); } if (passwordCopy) { password.DestroyClearTextCopy(passwordCopy); } if (FAILED(hr)) { throw DS::Error(hr, IDS_ABORT_UPGRADE_FAILED); } }
DS::PriorServerRole DS::GetPriorServerRole(bool& isUpgrade) { LOG_FUNCTION(DS::GetPriorServerRole);
isUpgrade = false; DSROLE_UPGRADE_STATUS_INFO* info = 0;
HRESULT hr = MyDsRoleGetPrimaryDomainInformation(0, info); if (SUCCEEDED(hr) && info) { isUpgrade = ( (info->OperationState & DSROLE_UPGRADE_IN_PROGRESS) ? true : false ); DSROLE_SERVER_STATE state = info->PreviousServerState;
::DsRoleFreeMemory(info);
switch (state) { case DsRoleServerPrimary: { return PDC; } case DsRoleServerBackup: { return BDC; } case DsRoleServerUnknown: default: { return UNKNOWN; } } }
return UNKNOWN; }
|