static HRESULT OpenPolicyKey( __in_z LPCWSTR wzPolicyPath, __out HKEY* phk ) { HRESULT hr = S_OK; LPWSTR sczPath = NULL; hr = PathConcat(REGISTRY_POLICIES_KEY, wzPolicyPath, &sczPath); ExitOnFailure(hr, "Failed to combine logging path with root path."); hr = RegOpen(HKEY_LOCAL_MACHINE, sczPath, KEY_READ, phk); ExitOnFailure(hr, "Failed to open policy registry key."); LExit: ReleaseStr(sczPath); return hr; }
/******************************************************************** RegDelete - deletes a registry key (and optionally it's whole tree). *********************************************************************/ extern "C" HRESULT DAPI RegDelete( __in HKEY hkRoot, __in_z LPCWSTR wzSubKey, __in REG_KEY_BITNESS kbKeyBitness, __in BOOL fDeleteTree ) { HRESULT hr = S_OK; DWORD er = ERROR_SUCCESS; LPWSTR pszEnumeratedSubKey = NULL; LPWSTR pszRecursiveSubKey = NULL; HKEY hkKey = NULL; REGSAM samDesired = 0; if (fDeleteTree) { hr = RegOpen(hkRoot, wzSubKey, KEY_READ, &hkKey); if (E_FILENOTFOUND == hr) { ExitFunction1(hr = S_OK); } ExitOnFailure1(hr, "Failed to open this key for enumerating subkeys", wzSubKey); // Yes, keep enumerating the 0th item, because we're deleting it every time while (E_NOMOREITEMS != (hr = RegKeyEnum(hkKey, 0, &pszEnumeratedSubKey))) { ExitOnFailure(hr, "Failed to enumerate key 0"); hr = PathConcat(wzSubKey, pszEnumeratedSubKey, &pszRecursiveSubKey); ExitOnFailure2(hr, "Failed to concatenate paths while recursively deleting subkeys. Path1: %ls, Path2: %ls", wzSubKey, pszEnumeratedSubKey); hr = RegDelete(hkRoot, pszRecursiveSubKey, kbKeyBitness, fDeleteTree); ExitOnFailure1(hr, "Failed to recursively delete subkey: %ls", pszRecursiveSubKey); } hr = S_OK; } if (!vfRegInitialized && REG_KEY_DEFAULT != kbKeyBitness) { hr = E_INVALIDARG; ExitOnFailure(hr, "RegInitialize must be called first in order to RegDelete() a key with non-default bit attributes!"); } switch (kbKeyBitness) { case REG_KEY_32BIT: samDesired = KEY_WOW64_32KEY; break; case REG_KEY_64BIT: samDesired = KEY_WOW64_64KEY; break; case REG_KEY_DEFAULT: // Nothing to do break; } if (NULL != vpfnRegDeleteKeyExW) { er = vpfnRegDeleteKeyExW(hkRoot, wzSubKey, samDesired, 0); if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) { ExitFunction1(hr = E_FILENOTFOUND); } ExitOnWin32Error(er, hr, "Failed to delete registry key (ex)."); } else { er = vpfnRegDeleteKeyW(hkRoot, wzSubKey); if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) { ExitFunction1(hr = E_FILENOTFOUND); } ExitOnWin32Error(er, hr, "Failed to delete registry key."); } LExit: ReleaseRegKey(hkKey); ReleaseStr(pszEnumeratedSubKey); ReleaseStr(pszRecursiveSubKey); 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 MspEngineExecutePackage( __in_opt HWND hwndParent, __in BURN_EXECUTE_ACTION* pExecuteAction, __in BURN_VARIABLES* pVariables, __in BOOL fRollback, __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, __in LPVOID pvContext, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ) { HRESULT hr = S_OK; INSTALLUILEVEL uiLevel = pExecuteAction->mspTarget.pPackage->Msp.fDisplayInternalUI ? INSTALLUILEVEL_DEFAULT : static_cast<INSTALLUILEVEL>(INSTALLUILEVEL_NONE | INSTALLUILEVEL_SOURCERESONLY); WIU_MSI_EXECUTE_CONTEXT context = { }; WIU_RESTART restart = WIU_RESTART_NONE; LPWSTR sczCachedDirectory = NULL; LPWSTR sczMspPath = NULL; LPWSTR sczPatches = NULL; LPWSTR sczProperties = NULL; LPWSTR sczObfuscatedProperties = NULL; // default to "verbose" logging DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE; // get cached MSP paths for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i) { LPCWSTR wzAppend = NULL; BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage; AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Invalid package type added to ordered patches."); if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->mspTarget.action) { hr = CacheGetCompletedPath(pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory); ExitOnFailure(hr, "Failed to get cached path for MSP package: %ls", pMspPackage->sczId); // Best effort to set the execute package cache folder variable. VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE); hr = PathConcat(sczCachedDirectory, pMspPackage->rgPayloads[0].pPayload->sczFilePath, &sczMspPath); ExitOnFailure(hr, "Failed to build MSP path."); wzAppend = sczMspPath; } else // uninstall { wzAppend = pMspPackage->Msp.sczPatchCode; } if (NULL != sczPatches) { hr = StrAllocConcat(&sczPatches, L";", 0); ExitOnFailure(hr, "Failed to semi-colon delimit patches."); } hr = StrAllocConcat(&sczPatches, wzAppend, 0); ExitOnFailure(hr, "Failed to append patch."); } // Wire up the external UI handler and logging. hr = WiuInitializeExternalUI(pfnMessageHandler, uiLevel, hwndParent, pvContext, fRollback, &context); ExitOnFailure(hr, "Failed to initialize external UI handler."); //if (BURN_LOGGING_LEVEL_DEBUG == logLevel) //{ // dwLogMode | INSTALLLOGMODE_EXTRADEBUG; //} if (pExecuteAction->mspTarget.sczLogPath && *pExecuteAction->mspTarget.sczLogPath) { hr = WiuEnableLog(dwLogMode, pExecuteAction->mspTarget.sczLogPath, 0); ExitOnFailure(hr, "Failed to enable logging for package: %ls to: %ls", pExecuteAction->mspTarget.pPackage->sczId, pExecuteAction->mspTarget.sczLogPath); } // set up properties hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczProperties, FALSE); ExitOnFailure(hr, "Failed to add properties to argument string."); hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE); ExitOnFailure(hr, "Failed to add properties to obfuscated argument string."); LogId(REPORT_STANDARD, MSG_APPLYING_PATCH_PACKAGE, pExecuteAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pExecuteAction->mspTarget.action), sczPatches, sczObfuscatedProperties, pExecuteAction->mspTarget.sczTargetProductCode); // // Do the actual action. // switch (pExecuteAction->mspTarget.action) { case BOOTSTRAPPER_ACTION_STATE_INSTALL: __fallthrough; case BOOTSTRAPPER_ACTION_STATE_REPAIR: hr = StrAllocConcatSecure(&sczProperties, L" PATCH=\"", 0); ExitOnFailure(hr, "Failed to add PATCH property on install."); hr = StrAllocConcatSecure(&sczProperties, sczPatches, 0); ExitOnFailure(hr, "Failed to add patches to PATCH property on install."); hr = StrAllocConcatSecure(&sczProperties, L"\" REBOOT=ReallySuppress", 0); ExitOnFailure(hr, "Failed to add reboot suppression property on install."); hr = WiuConfigureProductEx(pExecuteAction->mspTarget.sczTargetProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, sczProperties, &restart); ExitOnFailure(hr, "Failed to install MSP package."); break; case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0); ExitOnFailure(hr, "Failed to add reboot suppression property on uninstall."); // Ignore all dependencies, since the Burn engine already performed the check. hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES); ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties."); hr = WiuRemovePatches(sczPatches, pExecuteAction->mspTarget.sczTargetProductCode, sczProperties, &restart); ExitOnFailure(hr, "Failed to uninstall MSP package."); break; } LExit: WiuUninitializeExternalUI(&context); ReleaseStr(sczCachedDirectory); ReleaseStr(sczMspPath); StrSecureZeroFreeString(sczProperties); ReleaseStr(sczObfuscatedProperties); ReleaseStr(sczPatches); switch (restart) { case WIU_RESTART_NONE: *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; break; case WIU_RESTART_REQUIRED: *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; break; case WIU_RESTART_INITIATED: *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; break; } // 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 DAPI LocProbeForFile( __in_z LPCWSTR wzBasePath, __in_z LPCWSTR wzLocFileName, __in_z_opt LPCWSTR wzLanguage, __inout LPWSTR* psczPath ) { HRESULT hr = S_OK; LPWSTR sczProbePath = NULL; LANGID langid = 0; LPWSTR sczLangIdFile = NULL; // If a language was specified, look for a loc file in that as a directory. if (wzLanguage && *wzLanguage) { hr = PathConcat(wzBasePath, wzLanguage, &sczProbePath); ExitOnFailure(hr, "Failed to concat base path to language."); hr = PathConcat(sczProbePath, wzLocFileName, &sczProbePath); ExitOnFailure(hr, "Failed to concat loc file name to probe path."); if (FileExistsEx(sczProbePath, NULL)) { ExitFunction(); } } langid = ::GetUserDefaultLangID(); hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); ExitOnFailure(hr, "Failed to format user langid."); hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); ExitOnFailure(hr, "Failed to concat user langid file name to base path."); if (FileExistsEx(sczProbePath, NULL)) { ExitFunction(); } if (MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT) != langid) { langid = MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT); hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); ExitOnFailure(hr, "Failed to format user langid (default sublang)."); hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); ExitOnFailure(hr, "Failed to concat user langid file name to base path (default sublang)."); if (FileExistsEx(sczProbePath, NULL)) { ExitFunction(); } } langid = ::GetSystemDefaultUILanguage(); hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); ExitOnFailure(hr, "Failed to format system langid."); hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); ExitOnFailure(hr, "Failed to concat system langid file name to base path."); if (FileExistsEx(sczProbePath, NULL)) { ExitFunction(); } if (MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT) != langid) { langid = MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT); hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); ExitOnFailure(hr, "Failed to format user langid (default sublang)."); hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); ExitOnFailure(hr, "Failed to concat user langid file name to base path (default sublang)."); if (FileExistsEx(sczProbePath, NULL)) { ExitFunction(); } } // Finally, look for the loc file in the base path. hr = PathConcat(wzBasePath, wzLocFileName, &sczProbePath); ExitOnFailure(hr, "Failed to concat loc file name to base path."); if (!FileExistsEx(sczProbePath, NULL)) { hr = E_FILENOTFOUND; } LExit: if (SUCCEEDED(hr)) { hr = StrAllocString(psczPath, sczProbePath, 0); } ReleaseStr(sczLangIdFile); ReleaseStr(sczProbePath); return hr; }
static HRESULT DeleteEmptyDirectoryChildren( __in_z LPCWSTR wzPath ) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; WIN32_FIND_DATAW wfd = { }; HANDLE hFind = NULL; LPWSTR sczSubDirWithWildcard = NULL; LPWSTR sczPath = NULL; hr = PathConcat(wzPath, L"*", &sczSubDirWithWildcard); ExitOnFailure(hr, "Failed to concatenate wildcard character to directory name for search"); hFind = ::FindFirstFileW(sczSubDirWithWildcard, &wfd); if (INVALID_HANDLE_VALUE == hFind) { er = ::GetLastError(); hr = HRESULT_FROM_WIN32(er); if (E_PATHNOTFOUND == hr) { ExitFunction(); } ExitWithLastError(hr, "Failed to find first file with query: %ls", sczSubDirWithWildcard); } do { // Safety / silence code analysis tools wfd.cFileName[MAX_PATH - 1] = L'\0'; // Don't use "." or ".." if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, wfd.cFileName, -1, L".", -1)) { continue; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, wfd.cFileName, -1, L"..", -1)) { continue; } hr = PathConcat(wzPath, wfd.cFileName, &sczPath); ExitOnFailure(hr, "Failed to concat filename '%ls' to directory: %ls", wfd.cFileName, wzPath); // If we found a directory, recurse! if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { hr = PathBackslashTerminate(&sczPath); ExitOnFailure(hr, "Failed to ensure path is backslash terminated: %ls", sczPath); hr = DeleteEmptyDirectoryChildren(sczPath); if (HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY) == hr) { hr = S_OK; } else { ExitOnFailure(hr, "Failed to recurse to directory: %ls", sczPath); hr = DirEnsureDelete(sczPath, FALSE, FALSE); if (HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY) == hr) { hr = S_OK; } ExitOnFailure(hr, "Failed to delete directory: %ls", sczPath); } } else { continue; } } while (::FindNextFileW(hFind, &wfd)); er = ::GetLastError(); if (ERROR_NO_MORE_FILES == er) { hr = S_OK; } else { ExitWithLastError(hr, "Failed while looping through files in directory: %ls", wzPath); } LExit: // There was nothing to read, it's still success if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr) { hr = S_OK; } if (NULL != hFind) { FindClose(hFind); } ReleaseStr(sczSubDirWithWildcard); ReleaseStr(sczPath); 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 CleanConflictedDatabases( __in LPCWSTR wzDbDir, __in LPCWSTR wzDbPath ) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; LPCWSTR wzDbFile = NULL; LPWSTR sczQuery = NULL; LPWSTR sczPath = NULL; WIN32_FIND_DATAW wfd = { }; HANDLE hFind = NULL; wzDbFile = PathFile(wzDbPath); hr = PathConcat(wzDbDir, L"*", &sczQuery); ExitOnFailure(hr, "Failed to generate query path to delete conflicted databases"); hFind = ::FindFirstFileW(sczQuery, &wfd); if (INVALID_HANDLE_VALUE == hFind) { er = ::GetLastError(); hr = HRESULT_FROM_WIN32(er); if (E_PATHNOTFOUND == hr) { ExitFunction(); } ExitWithLastError(hr, "Failed to find first file with query: %ls", sczQuery); } do { // Safety / silence code analysis tools wfd.cFileName[MAX_PATH - 1] = L'\0'; // Don't use "." or ".." if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, wfd.cFileName, -1, L".", -1)) { continue; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, wfd.cFileName, -1, L"..", -1)) { continue; } // Don't delete the actual database file else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT | NORM_IGNORECASE, 0, wfd.cFileName, -1, wzDbFile, -1)) { continue; } if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { hr = PathConcat(wzDbDir, wfd.cFileName, &sczPath); ExitOnFailure(hr, "Failed to concat filename '%ls' to directory: %ls", wfd.cFileName, wzDbDir); hr = FileEnsureDelete(sczPath); TraceError(hr, "Failed to delete remote database file %ls, continuing", sczPath); hr = S_OK; } } while (::FindNextFileW(hFind, &wfd)); LExit: if (NULL != hFind) { FindClose(hFind); } ReleaseStr(sczQuery); ReleaseStr(sczPath); return hr; }
void HandleUnlock( __inout CFGDB_STRUCT *pcdb ) { HRESULT hr = S_OK; FILETIME ftRemote = { }; LPWSTR sczTempRemotePath = NULL; BOOL fCopyRemoteBack = FALSE; if (1 < pcdb->dwLockRefCount) { ExitFunction1(hr = S_OK); } Assert(0 < pcdb->dwLockRefCount); // Disconnect from database, if it's a connected remote database if (pcdb->fRemote && NULL != pcdb->psceDb) { fCopyRemoteBack = SceDatabaseChanged(pcdb->psceDb); hr = SceCloseDatabase(pcdb->psceDb); ExitOnFailure(hr, "Failed to close remote database"); pcdb->psceDb = NULL; if (fCopyRemoteBack) { hr = FileGetTime(pcdb->sczDbPath, NULL, NULL, &ftRemote); ExitOnFailure(hr, "Failed to get modified time of actual remote %ls", &ftRemote); // Since DB file wasn't locked, we have to verify that nobody changed it in the meantime. // Do it once before we try uploading to the remote (because uploading could be a lengthy operation on a slow connection to remote path) if (0 != ::CompareFileTime(&ftRemote, &pcdb->ftBeforeModify)) { hr = HRESULT_FROM_WIN32(ERROR_LOCK_VIOLATION); ExitOnFailure(hr, "database %ls was modified (before copy), we can't overwrite it!", pcdb->sczDbPath); } // Get it on the volume first which may take time // TODO: in some cases such as crashes or connection lost to network remote, // we'll leave a file behind here. We need a feature to look for and cleanup old files. hr = PathConcat(pcdb->sczDbDir, pcdb->sczGuid, &sczTempRemotePath); ExitOnFailure(hr, "Failed to get temp path in remote directory"); hr = FileEnsureCopy(pcdb->sczDbCopiedPath, sczTempRemotePath, TRUE); ExitOnFailure(hr, "Failed to copy remote database back to remote location (from %ls to %ls) due to changes", pcdb->sczDbCopiedPath, pcdb->sczDbPath); // Now do it again after the upload right before we do the actual move hr = FileGetTime(pcdb->sczDbPath, NULL, NULL, &ftRemote); ExitOnFailure(hr, "Failed to get modified time of original remote (again) %ls", &ftRemote); if (0 != ::CompareFileTime(&ftRemote, &pcdb->ftBeforeModify)) { hr = HRESULT_FROM_WIN32(ERROR_LOCK_VIOLATION); ExitOnFailure(hr, "database %ls was modified (after copy), we can't overwrite it!", pcdb->sczDbPath); } // Use MoveFile to ensure it's done as an atomic operation, so remote can never be left not existing. // There is a tiny chance we're reverting someone else's changes here if some other machine just moved the file between // the last timestamp check and this MoveFile call. I don't believe there is a way to fix that (we could open a lock on the file, // but then we can't use atomic MoveFile() API, meaning we could leave a partial file around in some cases, a HUGE no-no) // However, inadvertently overwriting a just-written db file is not a problem - Autosync on all machines will notice the fact // that the DB changed, re-sync it, at which time we will try again to re-propagate the changes. if (!::MoveFileExW(sczTempRemotePath, pcdb->sczDbPath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) { ExitWithLastError(hr, "Failed to move uploaded database path back to original remote location %ls", pcdb->sczDbPath); } } if (pcdb->fUpdateLastModified) { hr = FileGetTime(pcdb->sczDbCopiedPath, NULL, NULL, &pcdb->ftLastModified); if (E_FILENOTFOUND == hr || E_NOTFOUND == hr) { hr = S_OK; } ExitOnFailure(hr, "Failed to get modified time of copied db: %ls", pcdb->sczDbCopiedPath); } hr = FileEnsureDelete(pcdb->sczDbCopiedPath); ExitOnFailure(hr, "Failed to delete copied remote database from %ls", pcdb->sczDbCopiedPath); ReleaseNullStr(pcdb->sczDbCopiedPath); } for (DWORD i = 0; i < pcdb->cStreamsToDelete; ++i) { DeleteStream(pcdb->rgsczStreamsToDelete[i]); } ReleaseNullStrArray(pcdb->rgsczStreamsToDelete, pcdb->cStreamsToDelete); pcdb->fUpdateLastModified = FALSE; LExit: --pcdb->dwLockRefCount; ::LeaveCriticalSection(&pcdb->cs); if (FAILED(hr) && sczTempRemotePath) { // In case we left a temp file around, try to delete it before exiting (ignoring failure) FileEnsureDelete(sczTempRemotePath); } ReleaseStr(sczTempRemotePath); }
std::string PathConcat(const string &path1, const string &path2) { return PathConcat({path1, path2}); }
void PathConcat(PathBuffer* buffer, const char* other) { PathBuffer buf; PathInit(&buf, other); PathConcat(buffer, &buf); }
extern "C" HRESULT PayloadExtractFromContainer( __in BURN_PAYLOADS* pPayloads, __in_opt BURN_CONTAINER* pContainer, __in BURN_CONTAINER_CONTEXT* pContainerContext, __in_z LPCWSTR wzTargetDir ) { HRESULT hr = S_OK; LPWSTR sczStreamName = NULL; LPWSTR sczDirectory = NULL; BURN_PAYLOAD* pPayload = NULL; // extract all payloads for (;;) { // get next stream hr = ContainerNextStream(pContainerContext, &sczStreamName); if (E_NOMOREITEMS == hr) { hr = S_OK; break; } ExitOnFailure(hr, "Failed to get next stream."); // find payload by stream name hr = FindEmbeddedBySourcePath(pPayloads, pContainer, sczStreamName, &pPayload); ExitOnFailure(hr, "Failed to find embedded payload: %ls", sczStreamName); // make file path hr = PathConcat(wzTargetDir, pPayload->sczFilePath, &pPayload->sczLocalFilePath); ExitOnFailure(hr, "Failed to concat file paths."); // extract file hr = PathGetDirectory(pPayload->sczLocalFilePath, &sczDirectory); ExitOnFailure(hr, "Failed to get directory portion of local file path"); hr = DirEnsureExists(sczDirectory, NULL); ExitOnFailure(hr, "Failed to ensure directory exists"); hr = ContainerStreamToFile(pContainerContext, pPayload->sczLocalFilePath); ExitOnFailure(hr, "Failed to extract file."); // flag that the payload has been acquired pPayload->state = BURN_PAYLOAD_STATE_ACQUIRED; } // locate any payloads that were not extracted for (DWORD i = 0; i < pPayloads->cPayloads; ++i) { pPayload = &pPayloads->rgPayloads[i]; // if the payload is part of the container if (!pContainer || pPayload->pContainer == pContainer) { // if the payload has not been acquired if (BURN_PAYLOAD_STATE_ACQUIRED > pPayload->state) { hr = E_INVALIDDATA; ExitOnRootFailure(hr, "Payload was not found in container: %ls", pPayload->sczKey); } } } LExit: ReleaseStr(sczStreamName); ReleaseStr(sczDirectory); return hr; }