/******************************************************************* PipeCreateNameAndSecret - *******************************************************************/ extern "C" HRESULT PipeCreateNameAndSecret( __out_z LPWSTR *psczConnectionName, __out_z LPWSTR *psczSecret ) { HRESULT hr = S_OK; WCHAR wzGuid[GUID_STRING_LENGTH]; LPWSTR sczConnectionName = NULL; LPWSTR sczSecret = NULL; // Create the unique pipe name. hr = GuidFixedCreate(wzGuid); ExitOnRootFailure(hr, "Failed to create pipe guid."); hr = StrAllocFormatted(&sczConnectionName, L"BurnPipe.%s", wzGuid); ExitOnFailure(hr, "Failed to allocate pipe name."); // Create the unique client secret. hr = GuidFixedCreate(wzGuid); ExitOnRootFailure(hr, "Failed to create pipe secret."); hr = StrAllocString(&sczSecret, wzGuid, 0); ExitOnFailure(hr, "Failed to allocate pipe secret."); *psczConnectionName = sczConnectionName; sczConnectionName = NULL; *psczSecret = sczSecret; sczSecret = NULL; LExit: ReleaseStr(sczSecret); ReleaseStr(sczConnectionName); return hr; }
/******************************************************************* PipeCreateNameAndSecret - *******************************************************************/ extern "C" HRESULT PipeCreateNameAndSecret( __out_z LPWSTR *psczConnectionName, __out_z LPWSTR *psczSecret ) { HRESULT hr = S_OK; RPC_STATUS rs = RPC_S_OK; UUID guid = { }; WCHAR wzGuid[39]; LPWSTR sczConnectionName = NULL; LPWSTR sczSecret = NULL; // Create the unique pipe name. rs = ::UuidCreate(&guid); hr = HRESULT_FROM_RPC(rs); ExitOnFailure(hr, "Failed to create pipe guid."); if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid))) { hr = E_OUTOFMEMORY; ExitOnRootFailure(hr, "Failed to convert pipe guid into string."); } hr = StrAllocFormatted(&sczConnectionName, L"BurnPipe.%s", wzGuid); ExitOnFailure(hr, "Failed to allocate pipe name."); // Create the unique client secret. rs = ::UuidCreate(&guid); hr = HRESULT_FROM_RPC(rs); ExitOnRootFailure(hr, "Failed to create pipe guid."); if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid))) { hr = E_OUTOFMEMORY; ExitOnRootFailure(hr, "Failed to convert pipe guid into string."); } hr = StrAllocString(&sczSecret, wzGuid, 0); ExitOnFailure(hr, "Failed to allocate pipe secret."); *psczConnectionName = sczConnectionName; sczConnectionName = NULL; *psczSecret = sczSecret; sczSecret = NULL; LExit: ReleaseStr(sczSecret); ReleaseStr(sczConnectionName); return hr; }
DAPIV_(HRESULT) BalLog( __in BOOTSTRAPPER_LOG_LEVEL level, __in_z __format_string LPCSTR szFormat, ... ) { HRESULT hr = S_OK; va_list args; LPSTR sczFormattedAnsi = NULL; LPWSTR sczMessage = NULL; if (!vpEngine) { hr = E_POINTER; ExitOnRootFailure(hr, "BalInitialize() must be called first."); } va_start(args, szFormat); hr = StrAnsiAllocFormattedArgs(&sczFormattedAnsi, szFormat, args); va_end(args); ExitOnFailure(hr, "Failed to format log string."); hr = StrAllocStringAnsi(&sczMessage, sczFormattedAnsi, 0, CP_UTF8); ExitOnFailure(hr, "Failed to convert log string to Unicode."); hr = vpEngine->Log(level, sczMessage); LExit: ReleaseStr(sczMessage); ReleaseStr(sczFormattedAnsi); return hr; }
DAPIV_(HRESULT) BalLogError( __in HRESULT hrError, __in_z __format_string LPCSTR szFormat, ... ) { HRESULT hr = S_OK; va_list args; LPSTR sczFormattedAnsi = NULL; LPWSTR sczMessage = NULL; if (!vpEngine) { hr = E_POINTER; ExitOnRootFailure(hr, "BalInitialize() must be called first."); } va_start(args, szFormat); hr = StrAnsiAllocFormattedArgs(&sczFormattedAnsi, szFormat, args); va_end(args); ExitOnFailure(hr, "Failed to format error log string."); hr = StrAllocFormatted(&sczMessage, L"Error 0x%08x: %S", hrError, sczFormattedAnsi); ExitOnFailure(hr, "Failed to prepend error number to error log string."); hr = vpEngine->Log(BOOTSTRAPPER_LOG_LEVEL_ERROR, sczMessage); LExit: ReleaseStr(sczMessage); ReleaseStr(sczFormattedAnsi); return hr; }
// The contents of psczOut may be sensitive, should keep encrypted and SecureZeroFree. DAPI_(HRESULT) BalFormatString( __in_z LPCWSTR wzFormat, __inout LPWSTR* psczOut ) { HRESULT hr = S_OK; DWORD cch = 0; if (!vpEngine) { hr = E_POINTER; ExitOnRootFailure(hr, "BalInitialize() must be called first."); } if (*psczOut) { hr = StrMaxLength(*psczOut, reinterpret_cast<DWORD_PTR*>(&cch)); ExitOnFailure(hr, "Failed to determine length of value."); } hr = vpEngine->FormatString(wzFormat, *psczOut, &cch); if (E_MOREDATA == hr) { ++cch; hr = StrAllocSecure(psczOut, cch); ExitOnFailure(hr, "Failed to allocate value."); hr = vpEngine->FormatString(wzFormat, *psczOut, &cch); } LExit: return hr; }
/******************************************************************* LoadSystemLibraryWithPath - Fully qualifies the path to a module in the Windows system directory and loads it and returns the path Returns E_MODNOTFOUND - The module could not be found. * - Another error occured. ********************************************************************/ extern "C" HRESULT DAPI LoadSystemLibraryWithPath( __in_z LPCWSTR wzModuleName, __out HMODULE *phModule, __deref_out_z_opt LPWSTR* psczPath ) { HRESULT hr = S_OK; DWORD cch = 0; WCHAR wzPath[MAX_PATH] = { }; cch = ::GetSystemDirectoryW(wzPath, MAX_PATH); ExitOnNullWithLastError(cch, hr, "Failed to get the Windows system directory."); if (L'\\' != wzPath[cch - 1]) { hr = ::StringCchCatNW(wzPath, MAX_PATH, L"\\", 1); ExitOnRootFailure(hr, "Failed to terminate the string with a backslash."); } hr = ::StringCchCatW(wzPath, MAX_PATH, wzModuleName); ExitOnRootFailure1(hr, "Failed to create the fully-qualified path to %ls.", wzModuleName); *phModule = ::LoadLibraryW(wzPath); ExitOnNullWithLastError1(*phModule, hr, "Failed to load the library %ls.", wzModuleName); if (psczPath) { hr = StrAllocString(psczPath, wzPath, MAX_PATH); ExitOnFailure(hr, "Failed to copy the path to library."); } LExit: return hr; }
static HRESULT DoEnd( __in JSON_WRITER* pWriter, __in JSON_TOKEN tokenEnd, __in_z LPCWSTR wzEndString ) { HRESULT hr = S_OK; ::EnterCriticalSection(&pWriter->cs); if (!pWriter->rgTokenStack || 0 == pWriter->cTokens) { hr = E_UNEXPECTED; ExitOnRootFailure(hr, "Failure to pop token because the stack is empty."); } else { JSON_TOKEN token = pWriter->rgTokenStack[pWriter->cTokens - 1]; if ((JSON_TOKEN_ARRAY_END == tokenEnd && JSON_TOKEN_ARRAY_START != token && JSON_TOKEN_ARRAY_VALUE != token) || (JSON_TOKEN_OBJECT_END == tokenEnd && JSON_TOKEN_OBJECT_START != token && JSON_TOKEN_OBJECT_VALUE != token)) { hr = E_UNEXPECTED; ExitOnRootFailure1(hr, "Failure to pop token because the stack did not match the expected token: %d", tokenEnd); } } hr = StrAllocConcat(&pWriter->sczJson, wzEndString, 0); ExitOnFailure(hr, "Failed to end JSON array or object."); --pWriter->cTokens; LExit: ::LeaveCriticalSection(&pWriter->cs); return hr; }
HRESULT HandleEnsureSummaryDataTable( __in CFGDB_STRUCT *pcdb ) { HRESULT hr = S_OK; BOOL fInSceTransaction = FALSE; BOOL fEmpty = FALSE; SCE_ROW_HANDLE sceRow = NULL; hr = SceGetFirstRow(pcdb->psceDb, SUMMARY_DATA_TABLE, &sceRow); if (E_NOTFOUND == hr) { fEmpty = TRUE; hr = S_OK; } ExitOnFailure(hr, "Failed to get first row of summary data table"); if (fEmpty) { hr = GuidCreate(&pcdb->sczGuid); ExitOnRootFailure(hr, "Failed to generate guid string"); hr = SceBeginTransaction(pcdb->psceDb); ExitOnFailure(hr, "Failed to begin transaction"); fInSceTransaction = TRUE; hr = ScePrepareInsert(pcdb->psceDb, SUMMARY_DATA_TABLE, &sceRow); ExitOnFailure(hr, "Failed to prepare for insert"); hr = SceSetColumnString(sceRow, SUMMARY_GUID, pcdb->sczGuid); ExitOnFailure(hr, "Failed to set column string of summary data table guid"); hr = SceFinishUpdate(sceRow); ExitOnFailure(hr, "Failed to finish insert into summary data table"); hr = SceCommitTransaction(pcdb->psceDb); ExitOnFailure(hr, "Failed to commit transaction"); fInSceTransaction = FALSE; ExitFunction1(hr = S_OK); } ExitOnFailure(hr, "Failed to move to first row in SummaryData table"); hr = SceGetColumnString(sceRow, SUMMARY_GUID, &pcdb->sczGuid); ExitOnFailure(hr, "Failed to get GUID from summary data table"); LExit: ReleaseSceRow(sceRow); if (fInSceTransaction) { SceRollbackTransaction(pcdb->psceDb); } if (FAILED(hr)) { ReleaseNullStr(pcdb->sczGuid); } return hr; }
extern "C" HRESULT DAPI WuaRestartRequired( __out BOOL* pfRestartRequired ) { HRESULT hr = S_OK; ISystemInformation* pSystemInformation = NULL; VARIANT_BOOL bRestartRequired; hr = ::CoCreateInstance(__uuidof(SystemInformation), NULL, CLSCTX_INPROC_SERVER, __uuidof(ISystemInformation), reinterpret_cast<LPVOID*>(&pSystemInformation)); ExitOnRootFailure(hr, "Failed to get WUA system information interface."); hr = pSystemInformation->get_RebootRequired(&bRestartRequired); ExitOnRootFailure(hr, "Failed to determine if restart is required from WUA."); *pfRestartRequired = (VARIANT_FALSE != bRestartRequired); LExit: ReleaseObject(pSystemInformation); return hr; }
static HRESULT Restart() { HRESULT hr = S_OK; HANDLE hProcessToken = NULL; TOKEN_PRIVILEGES priv = { }; DWORD dwRetries = 0; if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcessToken)) { ExitWithLastError(hr, "Failed to get process token."); } priv.PrivilegeCount = 1; priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!::LookupPrivilegeValueW(NULL, L"SeShutdownPrivilege", &priv.Privileges[0].Luid)) { ExitWithLastError(hr, "Failed to get shutdown privilege LUID."); } if (!::AdjustTokenPrivileges(hProcessToken, FALSE, &priv, sizeof(TOKEN_PRIVILEGES), NULL, 0)) { ExitWithLastError(hr, "Failed to adjust token to add shutdown privileges."); } do { hr = S_OK; // Wait a second to let the companion process (assuming we did an elevated install) to get to the // point where it too is thinking about restarting the computer. Only one will schedule the restart // but both will have their log files closed and otherwise be ready to exit. // // On retry, we'll also wait a second to let the OS try to get to a place where the restart can // be initiated. ::Sleep(1000); if (!vpfnInitiateSystemShutdownExW(NULL, NULL, 0, FALSE, TRUE, SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_INSTALLATION | SHTDN_REASON_FLAG_PLANNED)) { hr = HRESULT_FROM_WIN32(::GetLastError()); } } while (dwRetries++ < RESTART_RETRIES && (HRESULT_FROM_WIN32(ERROR_MACHINE_LOCKED) == hr || HRESULT_FROM_WIN32(ERROR_NOT_READY) == hr)); ExitOnRootFailure(hr, "Failed to schedule restart."); LExit: ReleaseHandle(hProcessToken); return hr; }
static HRESULT DoKey( __in JSON_WRITER* pWriter, __in_z LPCWSTR wzKey ) { HRESULT hr = S_OK; JSON_TOKEN token = JSON_TOKEN_NONE; BOOL fNeedComma = FALSE; ::EnterCriticalSection(&pWriter->cs); hr = EnsureTokenStack(pWriter); ExitOnFailure(hr, "Failed to ensure token stack for key."); token = pWriter->rgTokenStack[pWriter->cTokens - 1]; switch (token) { case JSON_TOKEN_OBJECT_START: token = JSON_TOKEN_OBJECT_KEY; break; case JSON_TOKEN_OBJECT_VALUE: token = JSON_TOKEN_OBJECT_KEY; fNeedComma = TRUE; break; default: // everything else is not allowed. hr = E_UNEXPECTED; break; } ExitOnRootFailure(hr, "Cannot add key to JSON serializer now."); if (fNeedComma) { hr = StrAllocConcat(&pWriter->sczJson, L",", 0); ExitOnFailure(hr, "Failed to add comma for key to JSON."); } hr = StrAllocConcat(&pWriter->sczJson, wzKey, 0); ExitOnFailure(hr, "Failed to add key to JSON."); pWriter->rgTokenStack[pWriter->cTokens - 1] = token; LExit: ::LeaveCriticalSection(&pWriter->cs); return hr; }
// The contents of pllValue may be sensitive, if variable is hidden should keep value encrypted and SecureZeroMemory. DAPI_(HRESULT) BalGetNumericVariable( __in_z LPCWSTR wzVariable, __out LONGLONG* pllValue ) { HRESULT hr = S_OK; if (!vpEngine) { hr = E_POINTER; ExitOnRootFailure(hr, "BalInitialize() must be called first."); } hr = vpEngine->GetVariableNumeric(wzVariable, pllValue); LExit: return hr; }
DAPI_(BOOL) BalStringVariableExists( __in_z LPCWSTR wzVariable ) { HRESULT hr = S_OK; DWORD cch = 0; if (!vpEngine) { hr = E_POINTER; ExitOnRootFailure(hr, "BalInitialize() must be called first."); } hr = vpEngine->GetVariableString(wzVariable, NULL, &cch); LExit: return E_MOREDATA == hr; // string exists only if there are more than zero characters in the variable. }
// // ProcessCommandLine - process the provided command line arguments. // static HRESULT ProcessCommandLine( __in_z_opt LPCWSTR wzCommandLine, __out_z LPWSTR* psczThemeFile ) { HRESULT hr = S_OK; int argc = 0; LPWSTR* argv = NULL; if (wzCommandLine && *wzCommandLine) { argv = ::CommandLineToArgvW(wzCommandLine, &argc); ExitOnNullWithLastError(argv, hr, "Failed to get command line."); for (int i = 0; i < argc; ++i) { if (argv[i][0] == L'-' || argv[i][0] == L'/') { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"lang", -1)) { if (i + 1 >= argc) { ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a language."); } ++i; } } else { hr = StrAllocString(psczThemeFile, argv[i], 0); ExitOnFailure(hr, "Failed to copy path to theme file."); } } } LExit: if (argv) { ::LocalFree(argv); } return hr; }
extern "C" HRESULT DetectUpdate( __in_z LPCWSTR wzBundleId, __in BURN_USER_EXPERIENCE* pUX, __in BURN_UPDATE* pUpdate ) { HRESULT hr = S_OK; int nResult = IDNOACTION; BOOL fBeginCalled = FALSE; // If no update source was specified, skip update detection. if (!pUpdate->sczUpdateSource || !*pUpdate->sczUpdateSource) { ExitFunction(); } fBeginCalled = TRUE; nResult = pUX->pUserExperience->OnDetectUpdateBegin(pUpdate->sczUpdateSource, IDNOACTION); hr = UserExperienceInterpretResult(pUX, MB_OKCANCEL, nResult); ExitOnRootFailure(hr, "UX aborted detect update begin."); if (IDNOACTION == nResult) { //pUpdate->fUpdateAvailable = FALSE; } else if (IDOK == nResult) { hr = DetectAtomFeedUpdate(wzBundleId, pUX, pUpdate); ExitOnFailure(hr, "Failed to detect atom feed update."); } LExit: if (fBeginCalled) { pUX->pUserExperience->OnDetectUpdateComplete(hr, pUpdate->fUpdateAvailable ? pUpdate->sczUpdateSource : NULL); } return hr; }
static HRESULT DoStart( __in JSON_WRITER* pWriter, __in JSON_TOKEN tokenStart, __in_z LPCWSTR wzStartString ) { Assert(JSON_TOKEN_ARRAY_START == tokenStart || JSON_TOKEN_OBJECT_START == tokenStart); HRESULT hr = S_OK; JSON_TOKEN token = JSON_TOKEN_NONE; BOOL fNeedComma = FALSE; BOOL fPushToken = TRUE; ::EnterCriticalSection(&pWriter->cs); hr = EnsureTokenStack(pWriter); ExitOnFailure(hr, "Failed to ensure token stack for start."); token = pWriter->rgTokenStack[pWriter->cTokens - 1]; switch (token) { case JSON_TOKEN_NONE: token = tokenStart; fPushToken = FALSE; break; case JSON_TOKEN_ARRAY_START: // array start changes to array value. token = JSON_TOKEN_ARRAY_VALUE; break; case JSON_TOKEN_ARRAY_VALUE: case JSON_TOKEN_ARRAY_END: case JSON_TOKEN_OBJECT_END: fNeedComma = TRUE; break; default: // everything else is not allowed. hr = E_UNEXPECTED; break; } ExitOnRootFailure(hr, "Cannot start array or object to JSON serializer now."); if (fNeedComma) { hr = StrAllocConcat(&pWriter->sczJson, L",", 0); ExitOnFailure(hr, "Failed to add comma for start array or object to JSON."); } hr = StrAllocConcat(&pWriter->sczJson, wzStartString, 0); ExitOnFailure(hr, "Failed to start JSON array or object."); pWriter->rgTokenStack[pWriter->cTokens - 1] = token; if (fPushToken) { pWriter->rgTokenStack[pWriter->cTokens] = tokenStart; ++pWriter->cTokens; } LExit: ::LeaveCriticalSection(&pWriter->cs); return hr; }
extern "C" HRESULT NetFxRunChainer( __in LPCWSTR wzExecutablePath, __in LPCWSTR wzArguments, __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, __in LPVOID pvContext, __out DWORD* pdwExitCode ) { HRESULT hr = S_OK; DWORD er = 0; UUID guid = { }; WCHAR wzGuid[39]; RPC_STATUS rs = RPC_S_OK; LPWSTR sczEventName = NULL; LPWSTR sczSectionName = NULL; LPWSTR sczCommand = NULL; NetFxChainer* pNetfxChainer = NULL; STARTUPINFOW si = { }; PROCESS_INFORMATION pi = { }; HRESULT hrInternalError = 0; // Create the unique name suffix. rs = ::UuidCreate(&guid); hr = HRESULT_FROM_RPC(rs); ExitOnFailure(hr, "Failed to create netfx chainer guid."); if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid))) { hr = E_OUTOFMEMORY; ExitOnRootFailure(hr, "Failed to convert netfx chainer guid into string."); } hr = StrAllocFormatted(&sczSectionName, L"NetFxSection.%ls", wzGuid); ExitOnFailure(hr, "Failed to allocate section name."); hr = StrAllocFormatted(&sczEventName, L"NetFxEvent.%ls", wzGuid); ExitOnFailure(hr, "Failed to allocate event name."); hr = CreateNetFxChainer(sczSectionName, sczEventName, &pNetfxChainer); ExitOnFailure(hr, "Failed to create netfx chainer."); hr = StrAllocFormatted(&sczCommand, L"%ls /pipe %ls", wzArguments, sczSectionName); ExitOnFailure(hr, "Failed to allocate netfx chainer arguments."); si.cb = sizeof(si); if (!::CreateProcessW(wzExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { ExitWithLastError1(hr, "Failed to CreateProcess on path: %ls", wzExecutablePath); } HANDLE handles[2] = { pi.hProcess, pNetfxChainer->hEventChaineeSend }; for (;;) { er = ::WaitForMultipleObjects(2, handles, FALSE, 100); if (WAIT_OBJECT_0 == er) { // Process has exited *pdwExitCode = NetFxGetResult(pNetfxChainer, &hrInternalError); if (E_PENDING == *pdwExitCode) { if (!::GetExitCodeProcess(pi.hProcess, pdwExitCode)) { ExitWithLastError(hr, "Failed to get netfx return code."); } } else if (FAILED(hrInternalError)) { // push internal error message OnNetFxError(pNetfxChainer, hrInternalError, pfnGenericMessageHandler, pvContext); ExitOnFailure(hr, "Failed to send internal error message from netfx chainer."); } break; } else if (WAIT_OBJECT_0 + 1 == er) { // Chainee has notified us of a change. hr = ProcessNetFxMessage(pNetfxChainer, pfnGenericMessageHandler, pvContext); ExitOnFailure(hr, "Failed to process netfx chainer message."); } else if (WAIT_FAILED == er) { ExitWithLastError(hr, "Failed to wait for netfx chainer process to complete"); } } LExit: ReleaseStr(sczSectionName); ReleaseStr(sczEventName); ReleaseStr(sczCommand); DestroyNetFxChainer(pNetfxChainer); ReleaseHandle(pi.hThread); ReleaseHandle(pi.hProcess); return hr; }
/******************************************************************* PipeWaitForChildConnect - *******************************************************************/ extern "C" HRESULT PipeWaitForChildConnect( __in BURN_PIPE_CONNECTION* pConnection ) { HRESULT hr = S_OK; HANDLE hPipes[2] = { pConnection->hPipe, pConnection->hCachePipe}; LPCWSTR wzSecret = pConnection->sczSecret; DWORD cbSecret = lstrlenW(wzSecret) * sizeof(WCHAR); DWORD dwCurrentProcessId = ::GetCurrentProcessId(); DWORD dwAck = 0; DWORD cb = 0; for (DWORD i = 0; i < countof(hPipes) && INVALID_HANDLE_VALUE != hPipes[i]; ++i) { HANDLE hPipe = hPipes[i]; DWORD dwPipeState = PIPE_READMODE_BYTE | PIPE_NOWAIT; // Temporarily make the pipe non-blocking so we will not get stuck in ::ConnectNamedPipe() forever // if the child decides not to show up. if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL)) { ExitWithLastError(hr, "Failed to set pipe to non-blocking."); } // Loop for a while waiting for a connection from child process. DWORD cRetry = 0; do { if (!::ConnectNamedPipe(hPipe, NULL)) { DWORD er = ::GetLastError(); if (ERROR_PIPE_CONNECTED == er) { hr = S_OK; break; } else if (ERROR_PIPE_LISTENING == er) { if (cRetry < PIPE_RETRY_FOR_CONNECTION) { hr = HRESULT_FROM_WIN32(er); } else { hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); break; } ++cRetry; ::Sleep(PIPE_WAIT_FOR_CONNECTION); } else { hr = HRESULT_FROM_WIN32(er); break; } } } while (HRESULT_FROM_WIN32(ERROR_PIPE_LISTENING) == hr); ExitOnRootFailure(hr, "Failed to wait for child to connect to pipe."); // Put the pipe back in blocking mode. dwPipeState = PIPE_READMODE_BYTE | PIPE_WAIT; if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL)) { ExitWithLastError(hr, "Failed to reset pipe to blocking."); } // Prove we are the one that created the elevated process by passing the secret. if (!::WriteFile(hPipe, &cbSecret, sizeof(cbSecret), &cb, NULL)) { ExitWithLastError(hr, "Failed to write secret length to pipe."); } if (!::WriteFile(hPipe, wzSecret, cbSecret, &cb, NULL)) { ExitWithLastError(hr, "Failed to write secret to pipe."); } if (!::WriteFile(hPipe, &dwCurrentProcessId, sizeof(dwCurrentProcessId), &cb, NULL)) { ExitWithLastError(hr, "Failed to write our process id to pipe."); } // Wait until the elevated process responds that it is ready to go. if (!::ReadFile(hPipe, &dwAck, sizeof(dwAck), &cb, NULL)) { ExitWithLastError(hr, "Failed to read ACK from pipe."); } // The ACK should match out expected child process id. //if (pConnection->dwProcessId != dwAck) //{ // hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); // ExitOnRootFailure1(hr, "Incorrect ACK from elevated pipe: %u", dwAck); //} } LExit: return hr; }
static HRESULT DetectAtomFeedUpdate( __in_z LPCWSTR wzBundleId, __in BURN_USER_EXPERIENCE* pUX, __in BURN_UPDATE* pUpdate ) { Assert(pUpdate && pUpdate->sczUpdateSource && *pUpdate->sczUpdateSource); #ifdef DEBUG LogLine(REPORT_STANDARD, "DetectAtomFeedUpdate() - update location: %ls", pUpdate->sczUpdateSource); #endif HRESULT hr = S_OK; int nResult = IDNOACTION; LPWSTR sczUpdateFeedTempFile = NULL; ATOM_FEED* pAtomFeed = NULL; APPLICATION_UPDATE_CHAIN* pApupChain = NULL; hr = AtomInitialize(); ExitOnFailure(hr, "Failed to initialize Atom."); hr = DownloadUpdateFeed(wzBundleId, pUX, pUpdate, &sczUpdateFeedTempFile); ExitOnFailure(hr, "Failed to download update feed."); hr = AtomParseFromFile(sczUpdateFeedTempFile, &pAtomFeed); ExitOnFailure1(hr, "Failed to parse update atom feed: %ls.", sczUpdateFeedTempFile); hr = ApupAllocChainFromAtom(pAtomFeed, &pApupChain); ExitOnFailure(hr, "Failed to allocate update chain from atom feed."); if (0 < pApupChain->cEntries) { for (DWORD i = 0; i < pApupChain->cEntries; ++i) { APPLICATION_UPDATE_ENTRY* pAppUpdateEntry = &pApupChain->rgEntries[i]; nResult = pUX->pUserExperience->OnDetectUpdate(pAppUpdateEntry->rgEnclosures ? pAppUpdateEntry->rgEnclosures->wzUrl : NULL, pAppUpdateEntry->rgEnclosures ? pAppUpdateEntry->rgEnclosures->dw64Size : 0, pAppUpdateEntry->dw64Version, pAppUpdateEntry->wzTitle, pAppUpdateEntry->wzSummary, pAppUpdateEntry->wzContentType, pAppUpdateEntry->wzContent, IDNOACTION); switch (nResult) { case IDNOACTION: hr = S_OK; continue; case IDOK: pUpdate->fUpdateAvailable = TRUE; hr = S_OK; break; case IDCANCEL: hr = S_FALSE; break; default: ExitOnRootFailure(hr = E_INVALIDDATA, "UX aborted detect update begin."); } break; } } LExit: if (sczUpdateFeedTempFile && *sczUpdateFeedTempFile) { FileEnsureDelete(sczUpdateFeedTempFile); } ApupFreeChain(pApupChain); AtomFreeFeed(pAtomFeed); ReleaseStr(sczUpdateFeedTempFile); AtomUninitialize(); return hr; }
/******************************************************************* PipePumpMessages - *******************************************************************/ extern "C" HRESULT PipePumpMessages( __in HANDLE hPipe, __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, __in_opt LPVOID pvContext, __in BURN_PIPE_RESULT* pResult ) { HRESULT hr = S_OK; BURN_PIPE_MESSAGE msg = { }; SIZE_T iData = 0; LPSTR sczMessage = NULL; DWORD dwResult = 0; // Pump messages from child process. while (S_OK == (hr = GetPipeMessage(hPipe, &msg))) { switch (msg.dwMessage) { case BURN_PIPE_MESSAGE_TYPE_LOG: iData = 0; hr = BuffReadStringAnsi((BYTE*)msg.pvData, msg.cbData, &iData, &sczMessage); ExitOnFailure(hr, "Failed to read log message."); hr = LogStringWorkRaw(sczMessage); ExitOnFailure1(hr, "Failed to write log message:'%hs'.", sczMessage); dwResult = static_cast<DWORD>(hr); break; case BURN_PIPE_MESSAGE_TYPE_COMPLETE: if (!msg.pvData || sizeof(DWORD) != msg.cbData) { hr = E_INVALIDARG; ExitOnRootFailure(hr, "No status returned to PipePumpMessages()"); } pResult->dwResult = *static_cast<DWORD*>(msg.pvData); ExitFunction1(hr = S_OK); // exit loop. case BURN_PIPE_MESSAGE_TYPE_TERMINATE: iData = 0; hr = BuffReadNumber(static_cast<BYTE*>(msg.pvData), msg.cbData, &iData, &pResult->dwResult); ExitOnFailure(hr, "Failed to read returned result to PipePumpMessages()"); if (sizeof(DWORD) * 2 == msg.cbData) { hr = BuffReadNumber(static_cast<BYTE*>(msg.pvData), msg.cbData, &iData, (DWORD*)&pResult->fRestart); ExitOnFailure(hr, "Failed to read returned restart to PipePumpMessages()"); } ExitFunction1(hr = S_OK); // exit loop. default: if (pfnCallback) { hr = pfnCallback(&msg, pvContext, &dwResult); } else { hr = E_INVALIDARG; } ExitOnFailure1(hr, "Failed to process message: %u", msg.dwMessage); break; } // post result hr = WritePipeMessage(hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_COMPLETE), &dwResult, sizeof(dwResult)); ExitOnFailure(hr, "Failed to post result to child process."); FreePipeMessage(&msg); } ExitOnFailure(hr, "Failed to get message over pipe"); if (S_FALSE == hr) { hr = S_OK; } LExit: ReleaseStr(sczMessage); FreePipeMessage(&msg); return hr; }
extern "C" HRESULT ExeEngineExecutePackage( __in BURN_EXECUTE_ACTION* pExecuteAction, __in BURN_VARIABLES* pVariables, __in BOOL fRollback, __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, __in LPVOID pvContext, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ) { HRESULT hr = S_OK; WCHAR wzCurrentDirectory[MAX_PATH] = { }; BOOL fChangedCurrentDirectory = FALSE; int nResult = IDNOACTION; LPCWSTR wzArguments = NULL; LPWSTR sczArgumentsFormatted = NULL; LPWSTR sczArgumentsObfuscated = NULL; LPWSTR sczCachedDirectory = NULL; LPWSTR sczExecutablePath = NULL; LPWSTR sczCommand = NULL; LPWSTR sczCommandObfuscated = NULL; STARTUPINFOW si = { }; PROCESS_INFORMATION pi = { }; DWORD dwExitCode = 0; GENERIC_EXECUTE_MESSAGE message = { }; // get cached executable path hr = CacheGetCompletedPath(pExecuteAction->exePackage.pPackage->fPerMachine, pExecuteAction->exePackage.pPackage->sczCacheId, &sczCachedDirectory); ExitOnFailure1(hr, "Failed to get cached path for package: %ls", pExecuteAction->exePackage.pPackage->sczId); // Best effort to set the execute package cache folder variable. VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE); hr = PathConcat(sczCachedDirectory, pExecuteAction->exePackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczExecutablePath); ExitOnFailure(hr, "Failed to build executable path."); // pick arguments switch (pExecuteAction->exePackage.action) { case BOOTSTRAPPER_ACTION_STATE_INSTALL: wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczInstallArguments; break; case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczUninstallArguments; break; case BOOTSTRAPPER_ACTION_STATE_REPAIR: wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczRepairArguments; break; default: hr = E_UNEXPECTED; ExitOnFailure(hr, "Failed to get action arguments for executable package."); } // build command if (wzArguments && *wzArguments) { hr = VariableFormatString(pVariables, wzArguments, &sczArgumentsFormatted, NULL); ExitOnFailure(hr, "Failed to format argument string."); hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", sczExecutablePath, sczArgumentsFormatted); ExitOnFailure(hr, "Failed to create executable command."); hr = VariableFormatStringObfuscated(pVariables, wzArguments, &sczArgumentsObfuscated, NULL); ExitOnFailure(hr, "Failed to format obfuscated argument string."); hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", sczExecutablePath, sczArgumentsObfuscated); } else { hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", sczExecutablePath); ExitOnFailure(hr, "Failed to create executable command."); hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", sczExecutablePath); } ExitOnFailure(hr, "Failed to create obfuscated executable command."); if (BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol) { // Add the list of dependencies to ignore, if any, to the burn command line. if (pExecuteAction->exePackage.sczIgnoreDependencies && BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol) { hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies); ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line."); hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies); ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the obfuscated command line."); } // Add the list of ancestors, if any, to the burn command line. if (pExecuteAction->exePackage.sczAncestors) { hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors); ExitOnFailure(hr, "Failed to append the list of ancestors to the command line."); hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors); ExitOnFailure(hr, "Failed to append the list of ancestors to the obfuscated command line."); } } // Log before we add the secret pipe name and client token for embedded processes. LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->exePackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->exePackage.action), sczExecutablePath, sczCommandObfuscated); if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol) { hr = EmbeddedRunBundle(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode); ExitOnFailure1(hr, "Failed to run bundle as embedded from path: %ls", sczExecutablePath); } else if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_NETFX4 == pExecuteAction->exePackage.pPackage->Exe.protocol) { hr = NetFxRunChainer(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode); ExitOnFailure1(hr, "Failed to run netfx chainer: %ls", sczExecutablePath); } else // create and wait for the executable process while sending fake progress to allow cancel. { // Make the cache location of the executable the current directory to help those executables // that expect stuff to be relative to them. if (::GetCurrentDirectoryW(countof(wzCurrentDirectory), wzCurrentDirectory)) { fChangedCurrentDirectory = ::SetCurrentDirectoryW(sczCachedDirectory); } si.cb = sizeof(si); // TODO: hookup the stdin/stdout/stderr pipes for logging purposes? if (!::CreateProcessW(sczExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { ExitWithLastError1(hr, "Failed to CreateProcess on path: %ls", sczExecutablePath); } if (pExecuteAction->exePackage.fFireAndForget) { ::WaitForInputIdle(pi.hProcess, 5000); ExitFunction(); } do { message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; message.dwAllowedResults = MB_OKCANCEL; message.progress.dwPercentage = 50; nResult = pfnGenericMessageHandler(&message, pvContext); hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); ExitOnRootFailure(hr, "Bootstrapper application aborted during EXE progress."); hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode); if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr) { ExitOnFailure1(hr, "Failed to wait for executable to complete: %ls", sczExecutablePath); } } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr); } hr = HandleExitCode(pExecuteAction->exePackage.pPackage, dwExitCode, pRestart); ExitOnRootFailure1(hr, "Process returned error: 0x%x", dwExitCode); LExit: if (fChangedCurrentDirectory) { ::SetCurrentDirectoryW(wzCurrentDirectory); } StrSecureZeroFreeString(sczArgumentsFormatted); ReleaseStr(sczArgumentsObfuscated); ReleaseStr(sczCachedDirectory); ReleaseStr(sczExecutablePath); StrSecureZeroFreeString(sczCommand); ReleaseStr(sczCommandObfuscated); ReleaseHandle(pi.hThread); ReleaseHandle(pi.hProcess); // Best effort to clear the execute package cache folder variable. VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE); return hr; }
extern "C" HRESULT MsuEngineExecutePackage( __in BURN_EXECUTE_ACTION* pExecuteAction, __in BOOL fRollback, __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, __in LPVOID pvContext, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ) { HRESULT hr = S_OK; int nResult = IDNOACTION; LPWSTR sczCachedDirectory = NULL; LPWSTR sczMsuPath = NULL; LPWSTR sczSystemPath = NULL; LPWSTR sczWusaPath = NULL; LPWSTR sczCommand = NULL; SC_HANDLE schWu = NULL; BOOL fWuWasDisabled = FALSE; STARTUPINFOW si = { }; PROCESS_INFORMATION pi = { }; GENERIC_EXECUTE_MESSAGE message = { }; DWORD dwExitCode = 0; BOOL fDoDependency = FALSE; // get wusa.exe path hr = PathGetKnownFolder(CSIDL_SYSTEM, &sczSystemPath); ExitOnFailure(hr, "Failed to find System32 directory."); hr = PathConcat(sczSystemPath, L"wusa.exe", &sczWusaPath); ExitOnFailure(hr, "Failed to allocate WUSA.exe path."); // build command switch (pExecuteAction->msuPackage.action) { case BOOTSTRAPPER_ACTION_STATE_INSTALL: // get cached executable path hr = CacheGetCompletedPath(TRUE, pExecuteAction->msuPackage.pPackage->sczCacheId, &sczCachedDirectory); ExitOnFailure1(hr, "Failed to get cached path for package: %ls", pExecuteAction->msuPackage.pPackage->sczId); hr = PathConcat(sczCachedDirectory, pExecuteAction->msuPackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczMsuPath); ExitOnFailure(hr, "Failed to build executable path."); // format command hr = StrAllocFormatted(&sczCommand, L"\"%ls\" \"%ls\" /quiet /norestart", sczWusaPath, sczMsuPath); ExitOnFailure(hr, "Failed to format MSU install command."); break; case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: // format command hr = StrAllocFormatted(&sczCommand, L"\"%ls\" /uninstall /kb:%ls /quiet /norestart", sczWusaPath, pExecuteAction->msuPackage.pPackage->Msu.sczKB); ExitOnFailure(hr, "Failed to format MSU uninstall command."); break; default: hr = E_UNEXPECTED; ExitOnFailure(hr, "Failed to get action arguments for MSU package."); } if (pExecuteAction->msuPackage.sczLogPath && *pExecuteAction->msuPackage.sczLogPath) { hr = StrAllocConcat(&sczCommand, L" /log:", 0); ExitOnFailure(hr, "Failed to append log switch to MSU command-line."); hr = StrAllocConcat(&sczCommand, pExecuteAction->msuPackage.sczLogPath, 0); ExitOnFailure(hr, "Failed to append log path to MSU command-line."); } LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->msuPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msuPackage.action), sczMsuPath ? sczMsuPath : pExecuteAction->msuPackage.pPackage->Msu.sczKB, sczCommand); hr = EnsureWUServiceEnabled(&schWu, &fWuWasDisabled); ExitOnFailure(hr, "Failed to ensure WU service was enabled to install MSU package."); // create process si.cb = sizeof(si); if (!::CreateProcessW(sczWusaPath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { ExitWithLastError1(hr, "Failed to CreateProcess on path: %ls", sczWusaPath); } do { message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; message.dwAllowedResults = MB_OKCANCEL; message.progress.dwPercentage = 50; nResult = pfnGenericMessageHandler(&message, pvContext); hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); ExitOnRootFailure(hr, "Bootstrapper application aborted during MSU progress."); // wait for process to terminate hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode); if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr) { ExitOnFailure1(hr, "Failed to wait for executable to complete: %ls", sczWusaPath); } } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr); // get process exit code if (!::GetExitCodeProcess(pi.hProcess, &dwExitCode)) { ExitWithLastError(hr, "Failed to get process exit code."); } // We'll normalize the restart required error code from wusa.exe just in case. Most likely // that on reboot we'll actually get WU_S_REBOOT_REQUIRED. if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == static_cast<HRESULT>(dwExitCode)) { dwExitCode = ERROR_SUCCESS_REBOOT_REQUIRED; } // handle exit code switch (dwExitCode) { case S_OK: __fallthrough; case WU_S_ALREADY_INSTALLED: fDoDependency = TRUE; __fallthrough; case S_FALSE: __fallthrough; case WU_E_NOT_APPLICABLE: *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; hr = S_OK; break; case ERROR_SUCCESS_REBOOT_REQUIRED: __fallthrough; case WU_S_REBOOT_REQUIRED: fDoDependency = TRUE; *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; hr = S_OK; break; default: hr = E_UNEXPECTED; break; } if (fDoDependency) { if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->msuPackage.action) { hr = DependencyRegisterPackage(pExecuteAction->msuPackage.pPackage); ExitOnFailure(hr, "Failed to register the package dependency providers."); } else if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->msuPackage.action) { hr = DependencyUnregisterPackage(pExecuteAction->msuPackage.pPackage); ExitOnFailure(hr, "Failed to unregister the package dependency providers."); } } LExit: ReleaseStr(sczCachedDirectory); ReleaseStr(sczMsuPath); ReleaseStr(sczSystemPath); ReleaseStr(sczWusaPath); ReleaseStr(sczCommand); ReleaseHandle(pi.hProcess); ReleaseHandle(pi.hThread); if (fWuWasDisabled) { SetServiceStartType(schWu, SERVICE_DISABLED); } return hr; }
static HRESULT RunApplication( __in BURN_ENGINE_STATE* pEngineState, __out BOOL* pfReloadApp ) { HRESULT hr = S_OK; DWORD dwThreadId = 0; IBootstrapperEngine* pEngineForApplication = NULL; BOOL fStartupCalled = FALSE; BOOL fRet = FALSE; MSG msg = { }; ::PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); dwThreadId = ::GetCurrentThreadId(); // Load the bootstrapper application. hr = EngineForApplicationCreate(pEngineState, dwThreadId, &pEngineForApplication); ExitOnFailure(hr, "Failed to create engine for UX."); hr = UserExperienceLoad(&pEngineState->userExperience, pEngineForApplication, &pEngineState->command); ExitOnFailure(hr, "Failed to load UX."); fStartupCalled = TRUE; hr = pEngineState->userExperience.pUserExperience->OnStartup(); ExitOnFailure(hr, "Failed to start bootstrapper application."); // Enter the message pump. while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) { if (-1 == fRet) { hr = E_UNEXPECTED; ExitOnRootFailure(hr, "Unexpected return value from message pump."); } else { ProcessMessage(pEngineState, &msg); } } // get exit code pEngineState->userExperience.dwExitCode = (DWORD)msg.wParam; LExit: if (fStartupCalled) { int nResult = pEngineState->userExperience.pUserExperience->OnShutdown(); if (IDRESTART == nResult) { LogId(REPORT_STANDARD, MSG_BA_REQUESTED_RESTART, LoggingBoolToString(pEngineState->fRestart)); pEngineState->fRestart = TRUE; } else if (IDRELOAD_BOOTSTRAPPER == nResult) { LogId(REPORT_STANDARD, MSG_BA_REQUESTED_RELOAD); *pfReloadApp = TRUE; } } // unload UX UserExperienceUnload(&pEngineState->userExperience); ReleaseObject(pEngineForApplication); return hr; }
extern "C" HRESULT DetectForwardCompatibleBundle( __in BURN_USER_EXPERIENCE* pUX, __in BOOTSTRAPPER_COMMAND* pCommand, __in BURN_REGISTRATION* pRegistration ) { HRESULT hr = S_OK; int nRecommendation = IDNOACTION; if (pRegistration->sczDetectedProviderKeyBundleId && CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRegistration->sczId, -1)) { // Only change the recommendation if an parent was provided. if (pRegistration->sczActiveParent && *pRegistration->sczActiveParent) { // On install, recommend running the forward compatible bundle because there is an active parent. This // will essentially register the parent with the forward compatible bundle. if (BOOTSTRAPPER_ACTION_INSTALL == pCommand->action) { nRecommendation = IDOK; } else if (BOOTSTRAPPER_ACTION_UNINSTALL == pCommand->action || BOOTSTRAPPER_ACTION_MODIFY == pCommand->action || BOOTSTRAPPER_ACTION_REPAIR == pCommand->action) { // When modifying the bundle, only recommend running the forward compatible bundle if the parent // is already registered as a dependent of the provider key. if (DependencyDependentExists(pRegistration, pRegistration->sczActiveParent)) { nRecommendation = IDOK; } } } for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle) { BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle; if (BOOTSTRAPPER_RELATION_UPGRADE == pRelatedBundle->relationType && pRegistration->qwVersion <= pRelatedBundle->qwVersion && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRelatedBundle->package.sczId, -1)) { int nResult = pUX->pUserExperience->OnDetectForwardCompatibleBundle(pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->qwVersion, nRecommendation); hr = UserExperienceInterpretResult(pUX, MB_OKCANCEL, nResult); ExitOnRootFailure(hr, "BA aborted detect forward compatible bundle."); if (IDOK == nResult) { hr = PseudoBundleInitializePassthrough(&pRegistration->forwardCompatibleBundle, pCommand, NULL, pRegistration->sczActiveParent, pRegistration->sczAncestors, &pRelatedBundle->package); ExitOnFailure(hr, "Failed to initialize update bundle."); pRegistration->fEnabledForwardCompatibleBundle = TRUE; } LogId(REPORT_STANDARD, MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), LoggingVersionToString(pRelatedBundle->qwVersion), LoggingBoolToString(pRegistration->fEnabledForwardCompatibleBundle)); break; } } } LExit: return hr; }
// // PlanCalculate - calculates the execute and rollback state for the requested package state. // extern "C" HRESULT MspEnginePlanCalculatePackage( __in BURN_PACKAGE* pPackage, __in BURN_USER_EXPERIENCE* pUserExperience, __out BOOL* pfBARequestedCache ) { HRESULT hr = S_OK; BOOL fBARequestedCache = FALSE; for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) { BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; BOOTSTRAPPER_REQUEST_STATE requested = pPackage->requested; BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; int nResult = pUserExperience->pUserExperience->OnPlanTargetMsiPackage(pPackage->sczId, pTargetProduct->wzTargetProductCode, &requested); hr = UserExperienceInterpretResult(pUserExperience, MB_OKCANCEL, nResult); ExitOnRootFailure(hr, "UX aborted plan target MSI package."); // Calculate the execute action. switch (pTargetProduct->patchPackageState) { case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: switch (requested) { case BOOTSTRAPPER_REQUEST_STATE_REPAIR: execute = BOOTSTRAPPER_ACTION_STATE_REPAIR; break; case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_CACHE: execute = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; break; case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; break; default: execute = BOOTSTRAPPER_ACTION_STATE_NONE; break; } break; case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: switch (requested) { case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; break; case BOOTSTRAPPER_REQUEST_STATE_CACHE: execute = BOOTSTRAPPER_ACTION_STATE_NONE; fBARequestedCache = TRUE; break; default: execute = BOOTSTRAPPER_ACTION_STATE_NONE; break; } break; } // Calculate the rollback action if there is an execute action. if (BOOTSTRAPPER_ACTION_STATE_NONE != execute) { switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState) { case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: switch (requested) { case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_ABSENT: rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL; break; default: rollback = BOOTSTRAPPER_ACTION_STATE_NONE; break; } break; case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough; case BOOTSTRAPPER_PACKAGE_STATE_CACHED: switch (requested) { case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: rollback = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; break; default: rollback = BOOTSTRAPPER_ACTION_STATE_NONE; break; } break; default: rollback = BOOTSTRAPPER_ACTION_STATE_NONE; break; } } pTargetProduct->execute = execute; pTargetProduct->rollback = rollback; // The highest aggregate action state found will be returned. if (pPackage->execute < execute) { pPackage->execute = execute; } if (pPackage->rollback < rollback) { pPackage->rollback = rollback; } } if (pfBARequestedCache) { *pfBARequestedCache = fBARequestedCache; } LExit: return hr; }
extern "C" HRESULT MspEngineDetectPackage( __in BURN_PACKAGE* pPackage, __in BURN_USER_EXPERIENCE* pUserExperience ) { HRESULT hr = S_OK; int nResult = IDOK; LPWSTR sczState = NULL; if (0 == pPackage->Msp.cTargetProductCodes) { pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; } else { // Start the package state at the the highest state then loop through all the // target product codes and end up setting the current state to the lowest // package state applied to the the target product codes. pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) { BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; hr = WiuGetPatchInfoEx(pPackage->Msp.sczPatchCode, pTargetProduct->wzTargetProductCode, NULL, pTargetProduct->context, INSTALLPROPERTY_PATCHSTATE, &sczState); if (SUCCEEDED(hr)) { switch (*sczState) { case '1': pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; break; case '2': pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; break; case '4': pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE; break; default: pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; break; } } else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PATCH) == hr) { pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; hr = S_OK; } ExitOnFailure(hr, "Failed to get patch information for patch code: %ls, target product code: %ls", pPackage->Msp.sczPatchCode, pTargetProduct->wzTargetProductCode); if (pPackage->currentState > pTargetProduct->patchPackageState) { pPackage->currentState = pTargetProduct->patchPackageState; } nResult = pUserExperience->pUserExperience->OnDetectTargetMsiPackage(pPackage->sczId, pTargetProduct->wzTargetProductCode, pTargetProduct->patchPackageState); hr = UserExperienceInterpretResult(pUserExperience, MB_OKCANCEL, nResult); ExitOnRootFailure(hr, "UX aborted detect target MSI package."); } } LExit: ReleaseStr(sczState); return hr; }
extern "C" HRESULT DetectReportRelatedBundles( __in BURN_USER_EXPERIENCE* pUX, __in BURN_REGISTRATION* pRegistration, __in BOOTSTRAPPER_RELATION_TYPE relationType, __in BOOTSTRAPPER_ACTION action ) { HRESULT hr = S_OK; for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle) { const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle; BOOTSTRAPPER_RELATED_OPERATION operation = BOOTSTRAPPER_RELATED_OPERATION_NONE; switch (pRelatedBundle->relationType) { case BOOTSTRAPPER_RELATION_UPGRADE: if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL < action) { if (pRegistration->qwVersion > pRelatedBundle->qwVersion) { operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE; } else if (pRegistration->qwVersion < pRelatedBundle->qwVersion) { operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; } } break; case BOOTSTRAPPER_RELATION_PATCH: __fallthrough; case BOOTSTRAPPER_RELATION_ADDON: if (BOOTSTRAPPER_ACTION_UNINSTALL == action) { operation = BOOTSTRAPPER_RELATED_OPERATION_REMOVE; } else if (BOOTSTRAPPER_ACTION_INSTALL == action || BOOTSTRAPPER_ACTION_MODIFY == action) { operation = BOOTSTRAPPER_RELATED_OPERATION_INSTALL; } else if (BOOTSTRAPPER_ACTION_REPAIR == action) { operation = BOOTSTRAPPER_RELATED_OPERATION_REPAIR; } break; case BOOTSTRAPPER_RELATION_DETECT: __fallthrough; case BOOTSTRAPPER_RELATION_DEPENDENT: break; default: hr = E_FAIL; ExitOnRootFailure1(hr, "Unexpected relation type encountered: %d", pRelatedBundle->relationType); break; } LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), LoggingVersionToString(pRelatedBundle->qwVersion), LoggingRelatedOperationToString(operation)); int nResult = pUX->pUserExperience->OnDetectRelatedBundle(pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->qwVersion, operation); hr = UserExperienceInterpretResult(pUX, MB_OKCANCEL, nResult); ExitOnRootFailure(hr, "BA aborted detect related bundle."); } LExit: return hr; }
// // PlanCalculate - calculates the execute and rollback state for the requested package state. // extern "C" HRESULT ExeEnginePlanCalculatePackage( __in BURN_PACKAGE* pPackage ) { HRESULT hr = S_OK; //BOOL fCondition = FALSE; //BOOTSTRAPPER_PACKAGE_STATE expected = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; //// evaluate rollback install condition //if (pPackage->sczRollbackInstallCondition) //{ // hr = ConditionEvaluate(pVariables, pPackage->sczRollbackInstallCondition, &fCondition); // ExitOnFailure(hr, "Failed to evaluate rollback install condition."); // expected = fCondition ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT; //} // execute action switch (pPackage->currentState) { case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: switch (pPackage->requested) { case BOOTSTRAPPER_REQUEST_STATE_PRESENT: execute = pPackage->Exe.fPseudoBundle ? BOOTSTRAPPER_ACTION_STATE_INSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; break; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: execute = pPackage->Exe.fRepairable ? BOOTSTRAPPER_ACTION_STATE_REPAIR : BOOTSTRAPPER_ACTION_STATE_NONE; break; case BOOTSTRAPPER_REQUEST_STATE_ABSENT: execute = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; break; case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; break; default: execute = BOOTSTRAPPER_ACTION_STATE_NONE; } break; case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: switch (pPackage->requested) { case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; break; case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_ABSENT: execute = BOOTSTRAPPER_ACTION_STATE_NONE; break; default: execute = BOOTSTRAPPER_ACTION_STATE_NONE; } break; default: hr = E_INVALIDARG; ExitOnRootFailure1(hr, "Invalid package current state: %d.", pPackage->currentState); } // Calculate the rollback action if there is an execute action. if (BOOTSTRAPPER_ACTION_STATE_NONE != execute) { switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState) { case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: switch (pPackage->requested) { case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: rollback = BOOTSTRAPPER_ACTION_STATE_NONE; break; case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_ABSENT: rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL; break; default: rollback = BOOTSTRAPPER_ACTION_STATE_NONE; break; } break; case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: switch (pPackage->requested) { case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: rollback = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; break; case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_ABSENT: rollback = BOOTSTRAPPER_ACTION_STATE_NONE; break; default: rollback = BOOTSTRAPPER_ACTION_STATE_NONE; break; } break; default: hr = E_INVALIDARG; ExitOnRootFailure(hr, "Invalid package expected state."); } } // return values pPackage->execute = execute; pPackage->rollback = rollback; LExit: return hr; }
/******************************************************************* PipeChildConnect - Called from the child process to connect back to the pipe provided by the parent process. *******************************************************************/ extern "C" HRESULT PipeChildConnect( __in BURN_PIPE_CONNECTION* pConnection, __in BOOL fConnectCachePipe ) { Assert(pConnection->sczName); Assert(pConnection->sczSecret); Assert(!pConnection->hProcess); Assert(INVALID_HANDLE_VALUE == pConnection->hPipe); Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe); HRESULT hr = S_OK; LPWSTR sczPipeName = NULL; // Try to connect to the parent. hr = StrAllocFormatted(&sczPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName); ExitOnFailure(hr, "Failed to allocate name of parent pipe."); hr = E_UNEXPECTED; for (DWORD cRetry = 0; FAILED(hr) && cRetry < PIPE_RETRY_FOR_CONNECTION; ++cRetry) { pConnection->hPipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE == pConnection->hPipe) { hr = HRESULT_FROM_WIN32(::GetLastError()); if (E_FILENOTFOUND == hr) // if the pipe isn't created, call it a timeout waiting on the parent. { hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); } ::Sleep(PIPE_WAIT_FOR_CONNECTION); } else // we have a connection, go with it. { hr = S_OK; } } ExitOnRootFailure(hr, "Failed to open parent pipe: %ls", sczPipeName) // Verify the parent and notify it that the child connected. hr = ChildPipeConnected(pConnection->hPipe, pConnection->sczSecret, &pConnection->dwProcessId); ExitOnFailure(hr, "Failed to verify parent pipe: %ls", sczPipeName); if (fConnectCachePipe) { // Connect to the parent for the cache pipe. hr = StrAllocFormatted(&sczPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName); ExitOnFailure(hr, "Failed to allocate name of parent cache pipe."); pConnection->hCachePipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE == pConnection->hCachePipe) { ExitWithLastError(hr, "Failed to open parent pipe: %ls", sczPipeName) } // Verify the parent and notify it that the child connected. hr = ChildPipeConnected(pConnection->hCachePipe, pConnection->sczSecret, &pConnection->dwProcessId); ExitOnFailure(hr, "Failed to verify parent pipe: %ls", sczPipeName); } pConnection->hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, pConnection->dwProcessId); ExitOnNullWithLastError(pConnection->hProcess, hr, "Failed to open companion process with PID: %u", pConnection->dwProcessId); LExit: ReleaseStr(sczPipeName); return hr; }
// // PlanCalculate - calculates the execute and rollback state for the requested package state. // extern "C" HRESULT MsuEnginePlanCalculatePackage( __in BURN_PACKAGE* pPackage ) { HRESULT hr = S_OK; BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; BOOL fAllowUninstall = FALSE; OS_VERSION osVersion = OS_VERSION_UNKNOWN; DWORD dwServicePack = 0; // We can only uninstall MSU packages if they have a KB and we are on Win7 or newer. OsGetVersion(&osVersion, &dwServicePack); fAllowUninstall = pPackage->fUninstallable && (pPackage->Msu.sczKB && *pPackage->Msu.sczKB) && OS_VERSION_WIN7 <= osVersion; // execute action switch (pPackage->currentState) { case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: switch (pPackage->requested) { case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: execute = BOOTSTRAPPER_ACTION_STATE_NONE; break; case BOOTSTRAPPER_REQUEST_STATE_ABSENT: execute = fAllowUninstall ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; break; default: execute = BOOTSTRAPPER_ACTION_STATE_NONE; } break; case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: switch (pPackage->requested) { case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; break; default: execute = BOOTSTRAPPER_ACTION_STATE_NONE; } break; default: hr = E_INVALIDARG; ExitOnRootFailure(hr, "Invalid package state."); } // Calculate the rollback action if there is an execute action. if (BOOTSTRAPPER_ACTION_STATE_NONE != execute) { switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState) { case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: switch (pPackage->requested) { case BOOTSTRAPPER_REQUEST_STATE_ABSENT: rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL; break; default: rollback = BOOTSTRAPPER_ACTION_STATE_NONE; break; } break; case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: switch (pPackage->requested) { case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: rollback = fAllowUninstall ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; break; default: rollback = BOOTSTRAPPER_ACTION_STATE_NONE; break; } break; default: hr = E_INVALIDARG; ExitOnRootFailure(hr, "Invalid package expected state."); } } // return values pPackage->execute = execute; pPackage->rollback = rollback; LExit: return hr; }