Repository.mk | 1 RepositoryExternal.mk | 4 config_host.mk.in | 3 configure.ac | 44 external/msc-externals/Module_msc-externals.mk | 6 external/msc-externals/Package_ucrt.mk | 11 instsetoo_native/inc_openoffice/windows/msi_templates/Property.idt | 2 postprocess/signing/no_signing.txt | 8 scp2/InstallModule_windows.mk | 9 scp2/source/ooo/ucrt.scp | 154 ++ scp2/source/ooo/vc_redist.scp | 23 setup_native/Library_inst_msu_msi.mk | 40 setup_native/Module_setup_native.mk | 1 setup_native/source/win32/customactions/inst_msu/inst_msu.cxx | 515 ++++++++++ setup_native/source/win32/customactions/inst_msu/inst_msu_msi.def | 5 solenv/bin/modules/installer/windows/idtglobal.pm | 11 solenv/bin/modules/installer/windows/upgrade.pm | 12 17 files changed, 768 insertions(+), 81 deletions(-)
New commits: commit b84244378d411b83355b8763d3505031dd5ec324 Author: Mike Kaganski <mike.kagan...@collabora.com> Date: Sun Apr 15 23:24:42 2018 +0300 Install UCRT from MSUs, not using nested VC Redist install Using nested install is bad because (1) MS advises against it (though it most possibly doesn't relate to our specific case, when we install the vc redist exe package in UI part, so actually only a single MSI session is active at any time); (2) because it adds some extra interactions (user sees something "unrelated" being installed, which raises concerns; additional admin authentication required); and (3) because it runs in InstallUISequence, thus only installing the UCRT when doing interactive installation (unattended installs, including GPO, need to install UCRT separately). This patch aims to incorporate the original UCRT MSU (Windows Update) packages (https://support.microsoft.com/en-us/help/2999226) available as a zip archive from https://www.microsoft.com/en-us/download/details.aspx?id=48234 - the same as used in VC redists for VS 2015 and 2017. This obsoletes the separate installation of the redist; since we also have the redist as merge module in our MSI, that is enough (and removes redundancy). The MSUs are installed using wusa.exe in a custom action (deferred, non-impersonating). As a small bonus, embedding MSUs instead of redist EXE allows us to shrink the size of installer a little (~10 MB). As deferred custom actions cannot access current installer database, we workaround this by using initial immediate impersonating action to extract the binaries into a temporary location. To ensure that the file gets removed upon completion (both successful and failed), we use an additional cleanup action. Commit 61b1d631331551b43bc7d619be33bfbfeff7cad6 is effectively reverted. Change-Id: I1529356fdcc67ff24b232c01ddf8bb3a31bb00bd Reviewed-on: https://gerrit.libreoffice.org/52923 Tested-by: Jenkins <c...@libreoffice.org> Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> diff --git a/Repository.mk b/Repository.mk index 69659692d042..01c51c7a9db0 100644 --- a/Repository.mk +++ b/Repository.mk @@ -672,6 +672,7 @@ endif $(eval $(call gb_Helper_register_libraries_for_install,PLAINLIBS_OOO,ooobinarytable, \ $(if $(WINDOWS_SDK_HOME),\ instooofiltmsi \ + inst_msu_msi \ qslnkmsi \ reg4allmsdoc \ sdqsmsi \ diff --git a/RepositoryExternal.mk b/RepositoryExternal.mk index aecbd71b8b43..514dc485a8f6 100644 --- a/RepositoryExternal.mk +++ b/RepositoryExternal.mk @@ -4165,8 +4165,8 @@ $(eval $(call gb_Helper_register_libraries_for_install,OOOLIBS,ooo,\ )) endif -$(eval $(call gb_Helper_register_packages_for_install,vcredist_exe_binarytable,\ - $(if $(VCREDIST_DIR),vcredist_exe) \ +$(eval $(call gb_Helper_register_packages_for_install,ucrt_binarytable,\ + $(if $(UCRT_REDISTDIR),ucrt) \ )) # vim: set noet sw=4 ts=4: diff --git a/config_host.mk.in b/config_host.mk.in index a031f962a3da..70a428378050 100644 --- a/config_host.mk.in +++ b/config_host.mk.in @@ -591,8 +591,7 @@ export TMPDIR=@TEMP_DIRECTORY@ export TOUCH=@TOUCH@ export UCRTSDKDIR=@UCRTSDKDIR@ export UCRTVERSION=@UCRTVERSION@ -export VCREDIST_DIR=@VCREDIST_DIR@ -export VCREDIST_EXE=@VCREDIST_EXE@ +export UCRT_REDISTDIR=@UCRT_REDISTDIR@ export UNOWINREG_DLL=@UNOWINREG_DLL@ export USE_LIBRARY_BIN_TAR=@USE_LIBRARY_BIN_TAR@ export USE_XINERAMA=@USE_XINERAMA@ diff --git a/configure.ac b/configure.ac index d0240806303f..7ccc9f1997c6 100644 --- a/configure.ac +++ b/configure.ac @@ -6640,13 +6640,21 @@ fi AC_SUBST([JITC_PROCESSOR_TYPE]) # Misc Windows Stuff -AC_ARG_WITH(vcredist-dir, - AS_HELP_STRING([--with-vcredist-dir], - [path to the directory with the arch-specific executables (vc_redist.x64.exe, vc_redist.x86.exe) - for packaging into the installsets (without those the target system needs to install - the Visual C++ Runtimes manually)]), +AC_ARG_WITH(ucrt-dir, + AS_HELP_STRING([--with-ucrt-dir], + [path to the directory with the arch-specific MSU packages of the Windows Universal CRT redistributables + (MS KB 2999226) for packaging into the installsets (without those the target system needs to install + the UCRT or Visual C++ Runtimes manually). The directory must contain the following 6 files: + Windows6.1-KB2999226-x64.msu + Windows6.1-KB2999226-x86.msu + Windows8.1-KB2999226-x64.msu + Windows8.1-KB2999226-x86.msu + Windows8-RT-KB2999226-x64.msu + Windows8-RT-KB2999226-x86.msu + A zip archive including those files is available from Microsoft site: + https://www.microsoft.com/en-us/download/details.aspx?id=48234]), ,) -VCREDIST_DIR="$with_vcredist_dir" +UCRT_REDISTDIR="$with_ucrt_dir" if test $_os = "WINNT"; then find_msvc_x64_dlls find_msms @@ -6659,20 +6667,23 @@ if test $_os = "WINNT"; then else SCPDEFS="$SCPDEFS -DWITH_VC${VCVER}_REDIST" fi - if test "$VCREDIST_DIR" = "no"; then + if test "$UCRT_REDISTDIR" = "no"; then dnl explicitly disabled - VCREDIST_DIR="" - else - if test -f "$VCREDIST_DIR/vc_redist.$WINDOWS_SDK_ARCH.exe"; then - VCREDIST_EXE="vc_redist.$WINDOWS_SDK_ARCH.exe" - else - VCREDIST_DIR="" + UCRT_REDISTDIR="" + else + if ! test -f "$UCRT_REDISTDIR/Windows6.1-KB2999226-x64.msu" -a \ + -f "$UCRT_REDISTDIR/Windows6.1-KB2999226-x86.msu" -a \ + -f "$UCRT_REDISTDIR/Windows8.1-KB2999226-x64.msu" -a \ + -f "$UCRT_REDISTDIR/Windows8.1-KB2999226-x86.msu" -a \ + -f "$UCRT_REDISTDIR/Windows8-RT-KB2999226-x64.msu" -a \ + -f "$UCRT_REDISTDIR/Windows8-RT-KB2999226-x86.msu"; then + UCRT_REDISTDIR="" if test -n "$PKGFORMAT"; then for i in $PKGFORMAT; do case "$i" in msi) - AC_MSG_WARN([--without-vcredist-dir not specified or exe not found - installer will have runtime dependency]) - add_warning "--without-vcredist-dir not specified or exe not found - installer will have runtime dependency" + AC_MSG_WARN([--without-ucrt-dir not specified or MSUs not found - installer will have runtime dependency]) + add_warning "--without-ucrt-dir not specified or MSUs not found - installer will have runtime dependency" ;; esac done @@ -6681,8 +6692,7 @@ if test $_os = "WINNT"; then fi fi -AC_SUBST(VCREDIST_DIR) -AC_SUBST(VCREDIST_EXE) +AC_SUBST(UCRT_REDISTDIR) AC_SUBST(MSVC_DLL_PATH) AC_SUBST(MSVC_DLLS) AC_SUBST(MSM_PATH) diff --git a/external/msc-externals/Module_msc-externals.mk b/external/msc-externals/Module_msc-externals.mk index b7f7f47034f9..07ea3878a106 100644 --- a/external/msc-externals/Module_msc-externals.mk +++ b/external/msc-externals/Module_msc-externals.mk @@ -17,11 +17,11 @@ $(eval $(call gb_Module_add_targets,msc-externals,\ endif -# Install the universal crts and VC runtimes (tdf#108580) -ifneq ($(VCREDIST_DIR),) +# Install the universal crts (tdf#108580) +ifneq ($(UCRT_REDISTDIR),) $(eval $(call gb_Module_add_targets,msc-externals,\ - Package_vcredist_exe \ + Package_ucrt \ )) endif diff --git a/external/msc-externals/Package_vcredist_exe.mk b/external/msc-externals/Package_ucrt.mk similarity index 52% rename from external/msc-externals/Package_vcredist_exe.mk rename to external/msc-externals/Package_ucrt.mk index bf9ef632285c..52e6f0cbae97 100644 --- a/external/msc-externals/Package_vcredist_exe.mk +++ b/external/msc-externals/Package_ucrt.mk @@ -7,10 +7,15 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # -$(eval $(call gb_Package_Package,vcredist_exe,$(VCREDIST_DIR))) +$(eval $(call gb_Package_Package,ucrt,$(UCRT_REDISTDIR))) -$(eval $(call gb_Package_add_files,vcredist_exe,$(LIBO_ETC_FOLDER),\ - $(VCREDIST_EXE) \ +$(eval $(call gb_Package_add_files,ucrt,$(LIBO_ETC_FOLDER),\ + Windows6.1-KB2999226-x64.msu \ + Windows6.1-KB2999226-x86.msu \ + Windows8.1-KB2999226-x64.msu \ + Windows8.1-KB2999226-x86.msu \ + Windows8-RT-KB2999226-x64.msu \ + Windows8-RT-KB2999226-x86.msu \ )) # vim:set shiftwidth=4 tabstop=4 noexpandtab: diff --git a/instsetoo_native/inc_openoffice/windows/msi_templates/Property.idt b/instsetoo_native/inc_openoffice/windows/msi_templates/Property.idt index 12ba10f300c9..32ada67082b4 100644 --- a/instsetoo_native/inc_openoffice/windows/msi_templates/Property.idt +++ b/instsetoo_native/inc_openoffice/windows/msi_templates/Property.idt @@ -43,7 +43,7 @@ ProgressType3 installs Quickstarterlinkname QUICKSTARTERLINKNAMETEMPLATE RebootYesNo Yes ReinstallModeText omus -SecureCustomProperties NEWPRODUCTS;OLDPRODUCTS;VCRUNTIME_DETECTED +SecureCustomProperties NEWPRODUCTS;OLDPRODUCTS SetupType Typical SELECT_WORD 0 SELECT_EXCEL 0 diff --git a/postprocess/signing/no_signing.txt b/postprocess/signing/no_signing.txt index 2fa129a80bab..6de68e2e8cc2 100644 --- a/postprocess/signing/no_signing.txt +++ b/postprocess/signing/no_signing.txt @@ -8,5 +8,9 @@ policy.1.0.cli_oootypes.dll policy.1.0.cli_ure.dll policy.1.0.cli_cppuhelper.dll policy.1.0.cli_basetypes.dll -vc_redist.x64.exe -vc_redist.x86.exe +Windows6.1-KB2999226-x64.msu +Windows6.1-KB2999226-x86.msu +Windows8.1-KB2999226-x64.msu +Windows8.1-KB2999226-x86.msu +Windows8-RT-KB2999226-x64.msu +Windows8-RT-KB2999226-x86.msu diff --git a/scp2/InstallModule_windows.mk b/scp2/InstallModule_windows.mk index ecba37c42bca..bd6e478e7785 100644 --- a/scp2/InstallModule_windows.mk +++ b/scp2/InstallModule_windows.mk @@ -17,17 +17,12 @@ $(eval $(call gb_InstallModule_add_defs,scp2/windows,\ $(if $(WINDOWS_SDK_HOME),\ -DHAVE_WINDOWS_SDK \ ) \ - $(if $(MSM_PATH),\ - -DMSM_PATH \ - ) \ - $(if $(VCREDIST_DIR),\ - -DVCREDIST_EXE_NAME="$(VCREDIST_EXE)" \ - ) \ )) $(eval $(call gb_InstallModule_add_scpfiles,scp2/windows,\ scp2/source/ooo/folder_ooo \ - scp2/source/ooo/vc_redist \ + $(if $(MSM_PATH),scp2/source/ooo/vc_redist) \ + $(if $(UCRT_REDISTDIR),scp2/source/ooo/ucrt) \ scp2/source/ooo/windowscustomaction_ooo \ )) diff --git a/scp2/source/ooo/ucrt.scp b/scp2/source/ooo/ucrt.scp new file mode 100644 index 000000000000..4a13309f6364 --- /dev/null +++ b/scp2/source/ooo/ucrt.scp @@ -0,0 +1,154 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "macros.inc" + +File gid_File_Windows6_1_KB2999226_x64_msu + Name = "Windows6.1-KB2999226-x64.msu"; + Dir = gid_Brand_Dir_Program; + Styles = (PACKED, BINARYTABLE, BINARYTABLE_ONLY); +End + +File gid_File_Windows8_RT_KB2999226_x64_msu + Name = "Windows8-RT-KB2999226-x64.msu"; + Dir = gid_Brand_Dir_Program; + Styles = (PACKED, BINARYTABLE, BINARYTABLE_ONLY); +End + +File gid_File_Windows8_1_KB2999226_x64_msu + Name = "Windows8.1-KB2999226-x64.msu"; + Dir = gid_Brand_Dir_Program; + Styles = (PACKED, BINARYTABLE, BINARYTABLE_ONLY); +End + +#ifndef WINDOWS_X64 + +File gid_File_Windows6_1_KB2999226_x86_msu + Name = "Windows6.1-KB2999226-x86.msu"; + Dir = gid_Brand_Dir_Program; + Styles = (PACKED, BINARYTABLE, BINARYTABLE_ONLY); +End + +File gid_File_Windows8_RT_KB2999226_x86_msu + Name = "Windows8-RT-KB2999226-x86.msu"; + Dir = gid_Brand_Dir_Program; + Styles = (PACKED, BINARYTABLE, BINARYTABLE_ONLY); +End + +File gid_File_Windows8_1_KB2999226_x86_msu + Name = "Windows8.1-KB2999226-x86.msu"; + Dir = gid_Brand_Dir_Program; + Styles = (PACKED, BINARYTABLE, BINARYTABLE_ONLY); +End + +#endif /* WINDOWS_X64 */ + +/* A deferred not-impersonated action that will call wusa.exe to actually install + msu. Since deferred actions don't have access to current DB, the action depends + on immediate-executed action inst_ucrt (see below) that precedes it, unpacks + the binary to a temp file, and sets this action's CustomActionData property. +*/ +WindowsCustomAction gid_Customaction_inst_msu + Name = "inst_msu"; + Typ = "3073"; + Source = "inst_msu_msi.dll"; + Target = "InstallMSU"; + Inbinarytable = 1; + Assignment1 = ("InstallExecuteSequence", "Not Installed And inst_msu", "InstallFiles"); +End + +/* An immediately-executed action that will unpack a binary, which name in binary table is set + in "InstMSUBinary" property, to a temporary file, and sets "inst_msu" and "cleanup_msu" props. +*/ +WindowsCustomAction gid_Customaction_unpack_msu + Name = "unpack_msu"; + Typ = "1"; + Source = "inst_msu_msi.dll"; + Target = "UnpackMSUForInstall"; + Inbinarytable = 1; + Assignment1 = ("InstallExecuteSequence", "Not Installed And InstMSUBinary", "cleanup_msu"); +End + +/* A rollback action that removes temp file. It must precede inst_msu. +*/ +WindowsCustomAction gid_Customaction_cleanup_msu + Name = "cleanup_msu"; + Typ = "1345"; + Source = "inst_msu_msi.dll"; + Target = "CleanupMSU"; + Inbinarytable = 1; + Assignment1 = ("InstallExecuteSequence", "Not Installed And cleanup_msu", "inst_msu"); +End + +WindowsCustomAction gid_Customaction_check_win7x64_ucrt + Name = "check_win7x64_ucrt"; + Typ = "51"; + Source = "InstMSUBinary"; + Target = "Windows61-KB2999226-x64msu"; + Inbinarytable = 0; + Assignment1 = ("InstallExecuteSequence", "Not Installed And VersionNT = 601 And VersionNT64 And Not UCRT_DETECTED", "FileCost"); + Styles = "NO_FILE"; +End + +WindowsCustomAction gid_Customaction_check_win8x64_ucrt + Name = "check_win8x64_ucrt"; + Typ = "51"; + Source = "InstMSUBinary"; + Target = "Windows8-RT-KB2999226-x64msu"; + Inbinarytable = 0; + Assignment1 = ("InstallExecuteSequence", "Not Installed And VersionNT = 602 And VersionNT64 And Not UCRT_DETECTED", "check_win7x64_ucrt"); + Styles = "NO_FILE"; +End + +WindowsCustomAction gid_Customaction_check_win81x64_ucrt + Name = "check_win81x64_ucrt"; + Typ = "51"; + Source = "InstMSUBinary"; + Target = "Windows81-KB2999226-x64msu"; + Inbinarytable = 0; + Assignment1 = ("InstallExecuteSequence", "Not Installed And VersionNT = 603 And VersionNT64 And Not UCRT_DETECTED", "check_win8x64_ucrt"); + Styles = "NO_FILE"; +End + +#ifndef WINDOWS_X64 + +/* 32-bit installer must be prepared to run on both 32- and 64-bit Windows. So, it might need to + install either 32-bit or 64-bit UCRT package, depending on OS bitness. +*/ + +WindowsCustomAction gid_Customaction_check_win7x32_ucrt + Name = "check_win7x32_ucrt"; + Typ = "51"; + Source = "InstMSUBinary"; + Target = "Windows61-KB2999226-x86msu"; + Inbinarytable = 0; + Assignment1 = ("InstallExecuteSequence", "Not Installed And VersionNT = 601 And Not VersionNT64 And Not UCRT_DETECTED", "check_win81x64_ucrt"); + Styles = "NO_FILE"; +End + +WindowsCustomAction gid_Customaction_check_win8x32_ucrt + Name = "check_win8x32_ucrt"; + Typ = "51"; + Source = "InstMSUBinary"; + Target = "Windows8-RT-KB2999226-x86msu"; + Inbinarytable = 0; + Assignment1 = ("InstallExecuteSequence", "Not Installed And VersionNT = 602 And Not VersionNT64 And Not UCRT_DETECTED", "check_win7x32_ucrt"); + Styles = "NO_FILE"; +End + +WindowsCustomAction gid_Customaction_check_win81x32_ucrt + Name = "check_win81x32_ucrt"; + Typ = "51"; + Source = "InstMSUBinary"; + Target = "Windows81-KB2999226-x86msu"; + Inbinarytable = 0; + Assignment1 = ("InstallExecuteSequence", "Not Installed And VersionNT = 603 And Not VersionNT64 And Not UCRT_DETECTED", "check_win8x32_ucrt"); + Styles = "NO_FILE"; +End + +#endif /* WINDOWS_X64 */ diff --git a/scp2/source/ooo/vc_redist.scp b/scp2/source/ooo/vc_redist.scp index 921a9433e820..727c13b27615 100644 --- a/scp2/source/ooo/vc_redist.scp +++ b/scp2/source/ooo/vc_redist.scp @@ -18,8 +18,6 @@ #include "macros.inc" -#if defined(MSM_PATH) - #if defined(WITH_VC140_REDIST) #if defined WINDOWS_X64 @@ -76,24 +74,3 @@ MergeModule gid_MergeModule_Microsoft_VC141_CRT_x86 End #endif - -#endif // MSM_PATH - -#if defined(VCREDIST_EXE_NAME) - -File gid_File_Vcredist_Exe - Name = VCREDIST_EXE_NAME; - Dir = gid_Brand_Dir_Program; - Styles = (PACKED, BINARYTABLE, BINARYTABLE_ONLY); -End - -WindowsCustomAction gid_Customaction_InstallVCRedist - Name = "InstallVCRedist"; - Typ = "66"; /* 0x02 = exe in a binary table stream; 0x40 = ignore exit code and continue */ - Source = VCREDIST_EXE_NAME; - Target = "/repair /norestart /passive"; - Inbinarytable = 1; - Assignment1 = ("InstallUISequence", "Not Installed And Not ( VCRUNTIME_DETECTED And UCRT_DETECTED ) And VC_REDIST = 1", "behind_ExecuteAction"); -End - -#endif diff --git a/setup_native/Library_inst_msu_msi.mk b/setup_native/Library_inst_msu_msi.mk new file mode 100644 index 000000000000..d423b5168697 --- /dev/null +++ b/setup_native/Library_inst_msu_msi.mk @@ -0,0 +1,40 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_Library_Library,inst_msu_msi)) + +$(eval $(call gb_Library_add_defs,inst_msu_msi,\ + -U_DLL \ +)) + +$(eval $(call gb_Library_add_cxxflags,inst_msu_msi,\ + $(if $(MSVC_USE_DEBUG_RUNTIME),/MTd,/MT) \ +)) + +$(eval $(call gb_Library_add_ldflags,inst_msu_msi,\ + /DEF:$(SRCDIR)/setup_native/source/win32/customactions/inst_msu/inst_msu_msi.def \ + /NODEFAULTLIB \ +)) + +$(eval $(call gb_Library_add_exception_objects,inst_msu_msi,\ + setup_native/source/win32/customactions/inst_msu/inst_msu \ +)) + +$(eval $(call gb_Library_use_system_win32_libs,inst_msu_msi,\ + libcmt \ + libcpmt \ + libucrt \ + libvcruntime \ + kernel32 \ + Ole32 \ + Shell32 \ + Msi \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/setup_native/Module_setup_native.mk b/setup_native/Module_setup_native.mk index 8e11f4d61425..1009c53dcb2d 100644 --- a/setup_native/Module_setup_native.mk +++ b/setup_native/Module_setup_native.mk @@ -23,6 +23,7 @@ $(eval $(call gb_Module_add_targets,setup_native,\ ifeq ($(OS),WNT) $(eval $(call gb_Module_add_targets,setup_native,\ Library_instooofiltmsi \ + Library_inst_msu_msi \ Library_qslnkmsi \ Library_reg4allmsdoc \ Library_regactivex \ diff --git a/setup_native/source/win32/customactions/inst_msu/inst_msu.cxx b/setup_native/source/win32/customactions/inst_msu/inst_msu.cxx new file mode 100644 index 000000000000..b03d3cf3791c --- /dev/null +++ b/setup_native/source/win32/customactions/inst_msu/inst_msu.cxx @@ -0,0 +1,515 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <memory> +#include <string> +#include <sstream> +#include <iomanip> + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <Shlobj.h> +#include <Wuerror.h> +#include <msiquery.h> + +namespace +{ +template <typename IntType> std::string Num2Hex(IntType n) +{ + std::stringstream sMsg; + sMsg << "0x" << std::uppercase << std::setfill('0') << std::setw(sizeof(n) * 2) << std::hex + << n; + return sMsg.str(); +} + +template <typename IntType> std::string Num2Dec(IntType n) +{ + std::stringstream sMsg; + sMsg << n; + return sMsg.str(); +} + +void ThrowHResult(const char* sFunc, HRESULT hr) +{ + std::stringstream sMsg; + sMsg << sFunc << " failed (HRESULT = " << Num2Hex(hr) << ")!"; + + throw std::exception(sMsg.str().c_str()); +} + +void CheckHResult(const char* sFunc, HRESULT hr) +{ + if (FAILED(hr)) + ThrowHResult(sFunc, hr); +} + +void ThrowWin32Error(const char* sFunc, DWORD nWin32Error) +{ + std::stringstream sMsg; + sMsg << sFunc << " failed with Win32 error code " << Num2Hex(nWin32Error) << "!"; + + throw std::exception(sMsg.str().c_str()); +} + +void ThrowLastError(const char* sFunc) { ThrowWin32Error(sFunc, GetLastError()); } + +void CheckWin32Error(const char* sFunc, DWORD nWin32Error) +{ + if (nWin32Error != ERROR_SUCCESS) + ThrowWin32Error(sFunc, nWin32Error); +} + +std::wstring GetKnownFolder(const KNOWNFOLDERID& rfid) +{ + PWSTR sPath = nullptr; + HRESULT hr = SHGetKnownFolderPath(rfid, KF_FLAG_DEFAULT, nullptr, &sPath); + CheckHResult("SHGetKnownFolderPath", hr); + std::wstring sResult(sPath); + CoTaskMemFree(sPath); + return sResult; +} + +void WriteLogElem(MSIHANDLE hInst, MSIHANDLE hRecord, std::ostringstream& sTmpl, UINT) +{ + MsiRecordSetStringA(hRecord, 0, sTmpl.str().c_str()); + MsiProcessMessage(hInst, INSTALLMESSAGE_INFO, hRecord); +} + +void RecSetString(MSIHANDLE hRec, UINT nField, LPCSTR sVal) +{ + MsiRecordSetStringA(hRec, nField, sVal); +} + +void RecSetString(MSIHANDLE hRec, UINT nField, LPCWSTR sVal) +{ + MsiRecordSetStringW(hRec, nField, sVal); +} + +template <class Ch, class... SOther> +void WriteLogElem(MSIHANDLE hInst, MSIHANDLE hRec, std::ostringstream& sTmpl, UINT nField, + const Ch* elem, const SOther&... others) +{ + sTmpl << " [" << nField << "]"; + RecSetString(hRec, nField, elem); + WriteLogElem(hInst, hRec, sTmpl, nField + 1, others...); +} + +template <class S1, class... SOther> +void WriteLogElem(MSIHANDLE hInst, MSIHANDLE hRec, std::ostringstream& sTmpl, UINT nField, + const S1& elem, const SOther&... others) +{ + WriteLogElem(hInst, hRec, sTmpl, nField, elem.c_str(), others...); +} + +static std::string sLogPrefix; + +template <class... StrType> void WriteLog(MSIHANDLE hInst, const StrType&... elements) +{ + PMSIHANDLE hRec = MsiCreateRecord(sizeof...(elements)); + if (!hRec) + return; + + std::ostringstream sTemplate; + sTemplate << sLogPrefix; + WriteLogElem(hInst, hRec, sTemplate, 1, elements...); +} + +typedef std::unique_ptr<void, decltype(&CloseHandle)> CloseHandleGuard; +CloseHandleGuard Guard(HANDLE h) { return CloseHandleGuard(h, CloseHandle); } + +typedef std::unique_ptr<const wchar_t, decltype(&DeleteFileW)> DeleteFileGuard; +DeleteFileGuard Guard(const wchar_t* sFileName) { return DeleteFileGuard(sFileName, DeleteFileW); } + +typedef std::unique_ptr<SC_HANDLE__, decltype(&CloseServiceHandle)> CloseServiceHandleGuard; +CloseServiceHandleGuard Guard(SC_HANDLE h) +{ + return CloseServiceHandleGuard(h, CloseServiceHandle); +} + +std::wstring GetTempFile() +{ + wchar_t sPath[MAX_PATH + 1]; + DWORD nResult = GetTempPathW(sizeof(sPath) / sizeof(*sPath), sPath); + if (!nResult) + ThrowLastError("GetTempPathW"); + + wchar_t sFile[MAX_PATH + 1]; + nResult = GetTempFileNameW(sPath, L"TMP", 0, sFile); + if (!nResult) + ThrowLastError("GetTempFileNameW"); + return sFile; +} + +bool IsWow64Process() +{ +#if !defined _WIN64 + BOOL bResult = FALSE; + typedef BOOL(WINAPI * LPFN_ISWOW64PROCESS)(HANDLE, PBOOL); + LPFN_ISWOW64PROCESS fnIsWow64Process = reinterpret_cast<LPFN_ISWOW64PROCESS>( + GetProcAddress(GetModuleHandleW(L"kernel32"), "IsWow64Process")); + + if (fnIsWow64Process) + { + if (!fnIsWow64Process(GetCurrentProcess(), &bResult)) + ThrowLastError("IsWow64Process"); + } + + return bResult; +#else + return false; +#endif +} + +// Checks if Windows Update service is disabled, and if it is, enables it temporarily. +class WUServiceEnabler +{ +public: + WUServiceEnabler(MSIHANDLE hInstall) + : mhInstall(hInstall) + , mhService(EnableWUService(hInstall)) + { + } + + ~WUServiceEnabler() + { + try + { + if (mhService) + { + EnsureServiceEnabled(mhInstall, mhService.get(), false); + StopService(mhInstall, mhService.get()); + } + } + catch (std::exception& e) + { + WriteLog(mhInstall, e.what()); + } + } + +private: + static CloseServiceHandleGuard EnableWUService(MSIHANDLE hInstall) + { + auto hSCM = Guard(OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS)); + if (!hSCM) + ThrowLastError("OpenSCManagerW"); + WriteLog(hInstall, "Opened service control manager"); + + auto hService = Guard(OpenServiceW(hSCM.get(), L"wuauserv", + SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG + | SERVICE_QUERY_STATUS | SERVICE_STOP)); + if (!hService) + ThrowLastError("OpenServiceW"); + WriteLog(hInstall, "Obtained WU service handle"); + + if (ServiceStatus(hInstall, hService.get()) == SERVICE_RUNNING + || !EnsureServiceEnabled(hInstall, hService.get(), true)) + { + // No need to restore anything back, since we didn't change config + hService.reset(); + WriteLog(hInstall, "Service configuration is unchanged"); + } + + return hService; + } + + // Returns if the service configuration was actually changed + static bool EnsureServiceEnabled(MSIHANDLE hInstall, SC_HANDLE hService, bool bEnabled) + { + bool bConfigChanged = false; + + DWORD nCbRequired = 0; + if (!QueryServiceConfigW(hService, nullptr, 0, &nCbRequired)) + { + DWORD nError = GetLastError(); + if (nError != ERROR_INSUFFICIENT_BUFFER) + ThrowLastError("QueryServiceConfigW"); + } + std::unique_ptr<char[]> pBuf(new char[nCbRequired]); + LPQUERY_SERVICE_CONFIGW pConfig = reinterpret_cast<LPQUERY_SERVICE_CONFIGW>(pBuf.get()); + if (!QueryServiceConfigW(hService, pConfig, nCbRequired, &nCbRequired)) + ThrowLastError("QueryServiceConfigW"); + WriteLog(hInstall, "Obtained service config"); + + DWORD eNewStartType = 0; + if (bEnabled && pConfig->dwStartType == SERVICE_DISABLED) + { + bConfigChanged = true; + eNewStartType = SERVICE_DEMAND_START; + WriteLog(hInstall, "Service is disabled, and requested to enable"); + } + else if (!bEnabled && pConfig->dwStartType != SERVICE_DISABLED) + { + bConfigChanged = true; + eNewStartType = SERVICE_DISABLED; + WriteLog(hInstall, "Service is enabled, and requested to disable"); + } + + if (bConfigChanged) + { + if (!ChangeServiceConfigW(hService, SERVICE_NO_CHANGE, eNewStartType, SERVICE_NO_CHANGE, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr)) + ThrowLastError("ChangeServiceConfigW"); + WriteLog(hInstall, "WU service config successfully changed"); + } + else + WriteLog(hInstall, "No need to modify service config"); + + return bConfigChanged; + } + + static DWORD ServiceStatus(MSIHANDLE hInstall, SC_HANDLE hService) + { + SERVICE_STATUS aServiceStatus{}; + if (!QueryServiceStatus(hService, &aServiceStatus)) + ThrowLastError("QueryServiceStatus"); + + std::string sStatus; + switch (aServiceStatus.dwCurrentState) + { + case SERVICE_STOPPED: + sStatus = "SERVICE_STOPPED"; + break; + case SERVICE_START_PENDING: + sStatus = "SERVICE_START_PENDING"; + break; + case SERVICE_STOP_PENDING: + sStatus = "SERVICE_STOP_PENDING"; + break; + case SERVICE_RUNNING: + sStatus = "SERVICE_RUNNING"; + break; + case SERVICE_CONTINUE_PENDING: + sStatus = "SERVICE_CONTINUE_PENDING"; + break; + case SERVICE_PAUSE_PENDING: + sStatus = "SERVICE_PAUSE_PENDING"; + break; + case SERVICE_PAUSED: + sStatus = "SERVICE_PAUSED"; + break; + default: + sStatus = Num2Hex(aServiceStatus.dwCurrentState); + } + WriteLog(hInstall, "Service status is", sStatus); + + return aServiceStatus.dwCurrentState; + } + + static void StopService(MSIHANDLE hInstall, SC_HANDLE hService) + { + if (ServiceStatus(hInstall, hService) != SERVICE_STOPPED) + { + SERVICE_STATUS aServiceStatus{}; + if (!ControlService(hService, SERVICE_CONTROL_STOP, &aServiceStatus)) + ThrowLastError("ControlService"); + WriteLog(hInstall, + "Successfully sent SERVICE_CONTROL_STOP code to Windows Update service"); + // No need to wait for the service stopped + } + else + WriteLog(hInstall, "Windows Update service is not running"); + } + + MSIHANDLE mhInstall; + CloseServiceHandleGuard mhService; +}; +} + +// Immediate action "unpack_msu" that has access to installation database and properties; checks +// "InstMSUBinary" property and unpacks the binary with that name to a temporary file; sets +// "cleanup_msu" and "inst_msu" properties to the full name of the extracted temporary file. These +// properties will become "CustomActionData" property inside relevant deferred actions. +extern "C" UINT __stdcall UnpackMSUForInstall(MSIHANDLE hInstall) +{ + try + { + sLogPrefix = "UnpackMSUForInstall:"; + WriteLog(hInstall, "started"); + + WriteLog(hInstall, "Checking value of InstMSUBinary"); + wchar_t sBinaryName[MAX_PATH + 1]; + DWORD nCCh = sizeof(sBinaryName) / sizeof(*sBinaryName); + CheckWin32Error("MsiGetPropertyW", + MsiGetPropertyW(hInstall, L"InstMSUBinary", sBinaryName, &nCCh)); + WriteLog(hInstall, "Got InstMSUBinary value:", sBinaryName); + + PMSIHANDLE hDatabase = MsiGetActiveDatabase(hInstall); + if (!hDatabase) + ThrowLastError("MsiGetActiveDatabase"); + WriteLog(hInstall, "MsiGetActiveDatabase succeeded"); + + std::wstringstream sQuery; + sQuery << "SELECT `Data` FROM `Binary` WHERE `Name`='" << sBinaryName << "'"; + + PMSIHANDLE hBinaryView; + CheckWin32Error("MsiDatabaseOpenViewW", + MsiDatabaseOpenViewW(hDatabase, sQuery.str().c_str(), &hBinaryView)); + WriteLog(hInstall, "MsiDatabaseOpenViewW succeeded"); + + CheckWin32Error("MsiViewExecute", MsiViewExecute(hBinaryView, 0)); + WriteLog(hInstall, "MsiViewExecute succeeded"); + + PMSIHANDLE hBinaryRecord; + CheckWin32Error("MsiViewFetch", MsiViewFetch(hBinaryView, &hBinaryRecord)); + WriteLog(hInstall, "MsiViewFetch succeeded"); + + const std::wstring sBinary = GetTempFile(); + auto aDeleteFileGuard(Guard(sBinary.c_str())); + WriteLog(hInstall, "Temp file path:", sBinary.c_str()); + + CheckWin32Error("MsiSetPropertyW", + MsiSetPropertyW(hInstall, L"cleanup_msu", sBinary.c_str())); + + { + HANDLE hFile = CreateFileW(sBinary.c_str(), GENERIC_WRITE, 0, 0, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, 0); + if (hFile == INVALID_HANDLE_VALUE) + ThrowLastError("CreateFileW"); + auto aFileHandleGuard(Guard(hFile)); + + const DWORD nBufSize = 1024 * 1024; + std::unique_ptr<char[]> buf(new char[nBufSize]); + DWORD nTotal = 0; + DWORD nRead; + do + { + nRead = nBufSize; + CheckWin32Error("MsiRecordReadStream", + MsiRecordReadStream(hBinaryRecord, 1, buf.get(), &nRead)); + + if (nRead > 0) + { + DWORD nWritten; + if (!WriteFile(hFile, buf.get(), nRead, &nWritten, nullptr)) + ThrowLastError("WriteFile"); + nTotal += nWritten; + } + } while (nRead == nBufSize); + + WriteLog(hInstall, "Successfully wrote", Num2Dec(nTotal), "bytes"); + } + + CheckWin32Error("MsiSetPropertyW", MsiSetPropertyW(hInstall, L"inst_msu", sBinary.c_str())); + + // Don't delete the file: it will be done by following actions (inst_msu or cleanup_msu) + aDeleteFileGuard.release(); + return ERROR_SUCCESS; + } + catch (std::exception& e) + { + WriteLog(hInstall, e.what()); + } + return ERROR_INSTALL_FAILURE; +} + +// Deferred action "inst_msu" that must be run from system account. Receives the tempfile name from +// "CustomActionData" property, and runs wusa.exe to install it. Waits for it and checks exit code. +extern "C" UINT __stdcall InstallMSU(MSIHANDLE hInstall) +{ + try + { + sLogPrefix = "InstallMSU:"; + WriteLog(hInstall, "started"); + + WriteLog(hInstall, "Checking value of CustomActionData"); + wchar_t sBinaryName[MAX_PATH + 1]; + DWORD nCCh = sizeof(sBinaryName) / sizeof(*sBinaryName); + CheckWin32Error("MsiGetPropertyW", + MsiGetPropertyW(hInstall, L"CustomActionData", sBinaryName, &nCCh)); + WriteLog(hInstall, "Got CustomActionData value:", sBinaryName); + auto aDeleteFileGuard(Guard(sBinaryName)); + + // In case the Windows Update service is disabled, we temporarily enable it here + WUServiceEnabler aWUServiceEnabler(hInstall); + + const bool bWow64Process = IsWow64Process(); + WriteLog(hInstall, "Is Wow64 Process:", bWow64Process ? "YES" : "NO"); + std::wstring sWUSAPath = bWow64Process ? GetKnownFolder(FOLDERID_Windows) + L"\\SysNative" + : GetKnownFolder(FOLDERID_System); + sWUSAPath += L"\\wusa.exe"; + WriteLog(hInstall, "Prepared wusa path:", sWUSAPath); + + std::wstring sWUSACmd + = L"\"" + sWUSAPath + L"\" \"" + sBinaryName + L"\" /quiet /norestart"; + WriteLog(hInstall, "Prepared wusa command:", sWUSACmd); + + STARTUPINFOW si{}; + si.cb = sizeof(si); + PROCESS_INFORMATION pi{}; + if (!CreateProcessW(sWUSAPath.c_str(), const_cast<LPWSTR>(sWUSACmd.c_str()), nullptr, + nullptr, FALSE, CREATE_NO_WINDOW, nullptr, nullptr, &si, &pi)) + ThrowLastError("CreateProcessW"); + auto aCloseProcHandleGuard(Guard(pi.hProcess)); + WriteLog(hInstall, "CreateProcessW succeeded"); + + DWORD nWaitResult = WaitForSingleObject(pi.hProcess, INFINITE); + if (nWaitResult != WAIT_OBJECT_0) + ThrowWin32Error("WaitForSingleObject", nWaitResult); + + DWORD nExitCode = 0; + if (!GetExitCodeProcess(pi.hProcess, &nExitCode)) + ThrowLastError("GetExitCodeProcess"); + + HRESULT hr = static_cast<HRESULT>(nExitCode); + if (hr == HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED)) + hr = WU_S_REBOOT_REQUIRED; + + switch (hr) + { + case S_OK: + case S_FALSE: + case WU_S_ALREADY_INSTALLED: + case WU_E_NOT_APPLICABLE: // Windows could lie us about its version, etc. + case ERROR_SUCCESS_REBOOT_REQUIRED: + case WU_S_REBOOT_REQUIRED: + WriteLog(hInstall, "wusa.exe succeeded with exit code", Num2Hex(nExitCode)); + return ERROR_SUCCESS; + + default: + ThrowWin32Error("Execution of wusa.exe", nExitCode); + } + } + catch (std::exception& e) + { + WriteLog(hInstall, e.what()); + } + return ERROR_INSTALL_FAILURE; +} + +// Rollback deferred action "cleanup_msu" that is executed on error or cancel. +// It removes the temporary file created by UnpackMSUForInstall action. +// MUST be placed IMMEDIATELY AFTER "unpack_msu" in execute sequence. +extern "C" UINT __stdcall CleanupMSU(MSIHANDLE hInstall) +{ + try + { + sLogPrefix = "CleanupMSU:"; + WriteLog(hInstall, "started"); + + WriteLog(hInstall, "Checking value of CustomActionData"); + wchar_t sBinaryName[MAX_PATH + 1]; + DWORD nCCh = sizeof(sBinaryName) / sizeof(*sBinaryName); + CheckWin32Error("MsiGetPropertyW", + MsiGetPropertyW(hInstall, L"CustomActionData", sBinaryName, &nCCh)); + WriteLog(hInstall, "Got CustomActionData value:", sBinaryName); + + if (!DeleteFileW(sBinaryName)) + ThrowLastError("DeleteFileW"); + WriteLog(hInstall, "File successfully removed"); + } + catch (std::exception& e) + { + WriteLog(hInstall, e.what()); + } + // Always return success - we don't want rollback to fail. + return ERROR_SUCCESS; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/setup_native/source/win32/customactions/inst_msu/inst_msu_msi.def b/setup_native/source/win32/customactions/inst_msu/inst_msu_msi.def new file mode 100644 index 000000000000..49ade9c0169e --- /dev/null +++ b/setup_native/source/win32/customactions/inst_msu/inst_msu_msi.def @@ -0,0 +1,5 @@ +LIBRARY "inst_msu_msi.dll" +EXPORTS + UnpackMSUForInstall + InstallMSU + CleanupMSU \ No newline at end of file diff --git a/solenv/bin/modules/installer/windows/idtglobal.pm b/solenv/bin/modules/installer/windows/idtglobal.pm index 3cf086c7a206..db92aac5bd55 100644 --- a/solenv/bin/modules/installer/windows/idtglobal.pm +++ b/solenv/bin/modules/installer/windows/idtglobal.pm @@ -1084,9 +1084,8 @@ sub add_custom_action_to_install_table my $actionposition = 0; - if ( $position eq "end" ) { $actionposition = get_last_position_in_sequencetable($installtable) + 25; } - elsif ( $position =~ /^\s*behind_/ ) { $actionposition = get_position_in_sequencetable($position, $installtable) + 2; } - else { $actionposition = get_position_in_sequencetable($position, $installtable) - 2; } + if ( $position =~ /^\s*\d+\s*$/ ) { $actionposition = $position; } # setting the position directly, number defined in scp2 + else { $actionposition = "POSITIONTEMPLATE_" . $position; } my $line = $actionname . "\t" . $actioncondition . "\t" . $actionposition . "\n"; push(@{$installtable}, $line); @@ -1129,12 +1128,6 @@ sub add_custom_action_to_install_table $actioncondition =~ s/FEATURETEMPLATE/$feature/g; # only execute Custom Action, if feature of the file is installed -# my $actionposition = 0; -# if ( $position eq "end" ) { $actionposition = get_last_position_in_sequencetable($installtable) + 25; } -# elsif ( $position =~ /^\s*behind_/ ) { $actionposition = get_position_in_sequencetable($position, $installtable) + 2; } -# else { $actionposition = get_position_in_sequencetable($position, $installtable) - 2; } -# my $line = $actionname . "\t" . $actioncondition . "\t" . $actionposition . "\n"; - my $positiontemplate = ""; if ( $position =~ /^\s*\d+\s*$/ ) { $positiontemplate = $position; } # setting the position directly, number defined in scp2 else { $positiontemplate = "POSITIONTEMPLATE_" . $position; } diff --git a/solenv/bin/modules/installer/windows/upgrade.pm b/solenv/bin/modules/installer/windows/upgrade.pm index 6c02f99fe0f4..54838212404c 100644 --- a/solenv/bin/modules/installer/windows/upgrade.pm +++ b/solenv/bin/modules/installer/windows/upgrade.pm @@ -45,18 +45,6 @@ sub create_upgrade_table $newline = $installer::globals::upgradecode . "\t" . $installer::globals::msiproductversion . "\t" . "\t" . "\t" . "2" . "\t" . "\t" . "NEWPRODUCTS" . "\n"; push(@upgradetable, $newline); - # Detecting if VC Runtime is installed on system - $newline = "VCRUNTIME_UPGRADE_CODE" . "\t" . "14.0.24215" . "\t" . "15.0.0" . "\t" . "" . "\t" . "258" . "\t" . "" . "\t" . "VCRUNTIME_DETECTED" . "\n"; - if ( $installer::globals::cpuname eq 'X86_64' ) - { - $newline =~ s/VCRUNTIME_UPGRADE_CODE/{36F68A90-239C-34DF-B58C-64B30153CE35}/; - } - else - { - $newline =~ s/VCRUNTIME_UPGRADE_CODE/{65E5BD06-6392-3027-8C26-853107D3CF1A}/; - } - push(@upgradetable, $newline); - # Saving the file my $upgradetablename = $basedir . $installer::globals::separator . "Upgrade.idt"; _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits