VirtualBox

source: vbox/trunk/src/VBox/Installer/win/Stub/VBoxStub.cpp@ 59527

Last change on this file since 59527 was 59404, checked in by vboxsync, 9 years ago

Forward-ported r104938, r104943, r104950, r104952, r104953, r104987, r104988, r104990 from 5.0:

  • VBoxStub: Lazy import DLLs not in the KnownDlls list (and then some) and check that we don't accidentally add new imports as time goes by.
  • VBoxStub: the dlls have export names, so use them instead of the ordinals.
  • VBoxDef2LazyLoad fixes and error reporting, fixed missing VBoxCheckImports dependency during use in VBoxStub.
  • iprt/initterm.h,vbox-img,VBoxStub: Introduced RTR3INIT_FLAGS_STANDALONE_APP for statically linked applications that are expected to run outside the normal VirtualBox installation directory and need to be on their toes wrt dynamic library search paths. (Only windows, really.) Also, _always_ initialize IPRT first, don't ever do anything before that unless it 104% must be done there.
  • RTR3INIT_FLAGS_STANDALONE_APP trumps concerns about Vista GUI
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.4 KB
Line 
1/* $Id: VBoxStub.cpp 59404 2016-01-19 10:10:39Z vboxsync $ */
2/** @file
3 * VBoxStub - VirtualBox's Windows installer stub.
4 */
5
6/*
7 * Copyright (C) 2010-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0501
23# undef _WIN32_WINNT
24# define _WIN32_WINNT 0x0501 /* AttachConsole() / FreeConsole(). */
25#endif
26
27#include <Windows.h>
28#include <commctrl.h>
29#include <fcntl.h>
30#include <io.h>
31#include <lmerr.h>
32#include <msiquery.h>
33#include <objbase.h>
34
35#include <shlobj.h>
36#include <stdlib.h>
37#include <stdio.h>
38#include <string.h>
39#include <strsafe.h>
40
41#include <VBox/version.h>
42
43#include <iprt/assert.h>
44#include <iprt/dir.h>
45#include <iprt/file.h>
46#include <iprt/getopt.h>
47#include <iprt/initterm.h>
48#include <iprt/list.h>
49#include <iprt/mem.h>
50#include <iprt/message.h>
51#include <iprt/param.h>
52#include <iprt/path.h>
53#include <iprt/stream.h>
54#include <iprt/string.h>
55#include <iprt/thread.h>
56
57#include "VBoxStub.h"
58#include "../StubBld/VBoxStubBld.h"
59#include "resource.h"
60
61#ifdef VBOX_WITH_CODE_SIGNING
62# include "VBoxStubCertUtil.h"
63# include "VBoxStubPublicCert.h"
64#endif
65
66#ifndef TARGET_NT4
67/* Use an own console window if run in verbose mode. */
68# define VBOX_STUB_WITH_OWN_CONSOLE
69#endif
70
71
72/*********************************************************************************************************************************
73* Defined Constants And Macros *
74*********************************************************************************************************************************/
75#define MY_UNICODE_SUB(str) L ##str
76#define MY_UNICODE(str) MY_UNICODE_SUB(str)
77
78
79/*********************************************************************************************************************************
80* Structures and Typedefs *
81*********************************************************************************************************************************/
82/**
83 * Cleanup record.
84 */
85typedef struct STUBCLEANUPREC
86{
87 /** List entry. */
88 RTLISTNODE ListEntry;
89 /** True if file, false if directory. */
90 bool fFile;
91 /** The path to the file or directory to clean up. */
92 char szPath[1];
93} STUBCLEANUPREC;
94/** Pointer to a cleanup record. */
95typedef STUBCLEANUPREC *PSTUBCLEANUPREC;
96
97
98/*********************************************************************************************************************************
99* Global Variables *
100*********************************************************************************************************************************/
101/** Whether it's a silent or interactive GUI driven install. */
102static bool g_fSilent = false;
103/** List of temporary files. */
104static RTLISTANCHOR g_TmpFiles;
105/** Verbosity flag. */
106static int g_iVerbosity = 0;
107
108
109
110/**
111 * Shows an error message box with a printf() style formatted string.
112 *
113 * @returns RTEXITCODE_FAILURE
114 * @param pszFmt Printf-style format string to show in the message box body.
115 *
116 */
117static RTEXITCODE ShowError(const char *pszFmt, ...)
118{
119 char *pszMsg;
120 va_list va;
121
122 va_start(va, pszFmt);
123 if (RTStrAPrintfV(&pszMsg, pszFmt, va))
124 {
125 if (g_fSilent)
126 RTMsgError("%s", pszMsg);
127 else
128 {
129 PRTUTF16 pwszMsg;
130 int rc = RTStrToUtf16(pszMsg, &pwszMsg);
131 if (RT_SUCCESS(rc))
132 {
133 MessageBoxW(GetDesktopWindow(), pwszMsg, MY_UNICODE(VBOX_STUB_TITLE), MB_ICONERROR);
134 RTUtf16Free(pwszMsg);
135 }
136 else
137 MessageBoxA(GetDesktopWindow(), pszMsg, VBOX_STUB_TITLE, MB_ICONERROR);
138 }
139 RTStrFree(pszMsg);
140 }
141 else /* Should never happen! */
142 AssertMsgFailed(("Failed to format error text of format string: %s!\n", pszFmt));
143 va_end(va);
144 return RTEXITCODE_FAILURE;
145}
146
147
148/**
149 * Shows a message box with a printf() style formatted string.
150 *
151 * @param uType Type of the message box (see MSDN).
152 * @param pszFmt Printf-style format string to show in the message box body.
153 *
154 */
155static void ShowInfo(const char *pszFmt, ...)
156{
157 char *pszMsg;
158 va_list va;
159 va_start(va, pszFmt);
160 int rc = RTStrAPrintfV(&pszMsg, pszFmt, va);
161 va_end(va);
162 if (rc >= 0)
163 {
164 if (g_fSilent)
165 RTPrintf("%s\n", pszMsg);
166 else
167 {
168 PRTUTF16 pwszMsg;
169 int rc = RTStrToUtf16(pszMsg, &pwszMsg);
170 if (RT_SUCCESS(rc))
171 {
172 MessageBoxW(GetDesktopWindow(), pwszMsg, MY_UNICODE(VBOX_STUB_TITLE), MB_ICONINFORMATION);
173 RTUtf16Free(pwszMsg);
174 }
175 else
176 MessageBoxA(GetDesktopWindow(), pszMsg, VBOX_STUB_TITLE, MB_ICONINFORMATION);
177 }
178 }
179 else /* Should never happen! */
180 AssertMsgFailed(("Failed to format error text of format string: %s!\n", pszFmt));
181 RTStrFree(pszMsg);
182}
183
184
185/**
186 * Finds the specified in the resource section of the executable.
187 *
188 * @returns IPRT status code.
189 *
190 * @param pszDataName Name of resource to read.
191 * @param ppvResource Where to return the pointer to the data.
192 * @param pdwSize Where to return the size of the data (if found).
193 * Optional.
194 */
195static int FindData(const char *pszDataName, PVOID *ppvResource, DWORD *pdwSize)
196{
197 AssertReturn(pszDataName, VERR_INVALID_PARAMETER);
198 HINSTANCE hInst = NULL; /* indicates the executable image */
199
200 /* Find our resource. */
201 PRTUTF16 pwszDataName;
202 int rc = RTStrToUtf16(pszDataName, &pwszDataName);
203 AssertRCReturn(rc, rc);
204 HRSRC hRsrc = FindResourceExW(hInst,
205 (LPWSTR)RT_RCDATA,
206 pwszDataName,
207 MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
208 RTUtf16Free(pwszDataName);
209 AssertReturn(hRsrc, VERR_IO_GEN_FAILURE);
210
211 /* Get resource size. */
212 DWORD cb = SizeofResource(hInst, hRsrc);
213 AssertReturn(cb > 0, VERR_NO_DATA);
214 if (pdwSize)
215 *pdwSize = cb;
216
217 /* Get pointer to resource. */
218 HGLOBAL hData = LoadResource(hInst, hRsrc);
219 AssertReturn(hData, VERR_IO_GEN_FAILURE);
220
221 /* Lock resource. */
222 *ppvResource = LockResource(hData);
223 AssertReturn(*ppvResource, VERR_IO_GEN_FAILURE);
224 return VINF_SUCCESS;
225}
226
227
228/**
229 * Finds the header for the given package.
230 *
231 * @returns Pointer to the package header on success. On failure NULL is
232 * returned after ShowError has been invoked.
233 * @param iPackage The package number.
234 */
235static PVBOXSTUBPKG FindPackageHeader(unsigned iPackage)
236{
237 char szHeaderName[32];
238 RTStrPrintf(szHeaderName, sizeof(szHeaderName), "HDR_%02d", iPackage);
239
240 PVBOXSTUBPKG pPackage;
241 int rc = FindData(szHeaderName, (PVOID *)&pPackage, NULL);
242 if (RT_FAILURE(rc))
243 {
244 ShowError("Internal error: Could not find package header #%u: %Rrc", iPackage, rc);
245 return NULL;
246 }
247
248 /** @todo validate it. */
249 return pPackage;
250}
251
252
253
254/**
255 * Constructs a full temporary file path from the given parameters.
256 *
257 * @returns iprt status code.
258 *
259 * @param pszTempPath The pure path to use for construction.
260 * @param pszTargetFileName The pure file name to use for construction.
261 * @param ppszTempFile Pointer to the constructed string. Must be freed
262 * using RTStrFree().
263 */
264static int GetTempFileAlloc(const char *pszTempPath,
265 const char *pszTargetFileName,
266 char **ppszTempFile)
267{
268 if (RTStrAPrintf(ppszTempFile, "%s\\%s", pszTempPath, pszTargetFileName) >= 0)
269 return VINF_SUCCESS;
270 return VERR_NO_STR_MEMORY;
271}
272
273
274/**
275 * Extracts a built-in resource to disk.
276 *
277 * @returns iprt status code.
278 *
279 * @param pszResourceName The resource name to extract.
280 * @param pszTempFile The full file path + name to extract the resource to.
281 *
282 */
283static int ExtractFile(const char *pszResourceName,
284 const char *pszTempFile)
285{
286 int rc;
287 RTFILE fh;
288 BOOL bCreatedFile = FALSE;
289
290 do
291 {
292 AssertMsgBreak(pszResourceName, ("Resource pointer invalid!\n"));
293 AssertMsgBreak(pszTempFile, ("Temp file pointer invalid!"));
294
295 /* Read the data of the built-in resource. */
296 PVOID pvData = NULL;
297 DWORD dwDataSize = 0;
298 rc = FindData(pszResourceName, &pvData, &dwDataSize);
299 AssertMsgRCBreak(rc, ("Could not read resource data!\n"));
300
301 /* Create new (and replace an old) file. */
302 rc = RTFileOpen(&fh, pszTempFile,
303 RTFILE_O_CREATE_REPLACE
304 | RTFILE_O_WRITE
305 | RTFILE_O_DENY_NOT_DELETE
306 | RTFILE_O_DENY_WRITE);
307 AssertMsgRCBreak(rc, ("Could not open file for writing!\n"));
308 bCreatedFile = TRUE;
309
310 /* Write contents to new file. */
311 size_t cbWritten = 0;
312 rc = RTFileWrite(fh, pvData, dwDataSize, &cbWritten);
313 AssertMsgRCBreak(rc, ("Could not open file for writing!\n"));
314 AssertMsgBreak(dwDataSize == cbWritten, ("File was not extracted completely! Disk full?\n"));
315
316 } while (0);
317
318 if (RTFileIsValid(fh))
319 RTFileClose(fh);
320
321 if (RT_FAILURE(rc))
322 {
323 if (bCreatedFile)
324 RTFileDelete(pszTempFile);
325 }
326 return rc;
327}
328
329
330/**
331 * Extracts a built-in resource to disk.
332 *
333 * @returns iprt status code.
334 *
335 * @param pPackage Pointer to a VBOXSTUBPKG struct that contains the resource.
336 * @param pszTempFile The full file path + name to extract the resource to.
337 *
338 */
339static int Extract(const PVBOXSTUBPKG pPackage,
340 const char *pszTempFile)
341{
342 return ExtractFile(pPackage->szResourceName,
343 pszTempFile);
344}
345
346
347/**
348 * Detects whether we're running on a 32- or 64-bit platform and returns the result.
349 *
350 * @returns TRUE if we're running on a 64-bit OS, FALSE if not.
351 *
352 */
353static BOOL IsWow64(void)
354{
355 BOOL bIsWow64 = TRUE;
356 fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process");
357 if (NULL != fnIsWow64Process)
358 {
359 if (!fnIsWow64Process(GetCurrentProcess(), &bIsWow64))
360 {
361 /* Error in retrieving process type - assume that we're running on 32bit. */
362 return FALSE;
363 }
364 }
365 return bIsWow64;
366}
367
368
369/**
370 * Decides whether we need a specified package to handle or not.
371 *
372 * @returns @c true if we need to handle the specified package, @c false if not.
373 *
374 * @param pPackage Pointer to a VBOXSTUBPKG struct that contains the resource.
375 *
376 */
377static bool PackageIsNeeded(PVBOXSTUBPKG pPackage)
378{
379 if (pPackage->byArch == VBOXSTUBPKGARCH_ALL)
380 return true;
381 VBOXSTUBPKGARCH enmArch = IsWow64() ? VBOXSTUBPKGARCH_AMD64 : VBOXSTUBPKGARCH_X86;
382 return pPackage->byArch == enmArch;
383}
384
385
386/**
387 * Adds a cleanup record.
388 *
389 * @returns Fully complained boolean success indicator.
390 * @param pszPath The path to the file or directory to clean up.
391 * @param fFile @c true if file, @c false if directory.
392 */
393static bool AddCleanupRec(const char *pszPath, bool fFile)
394{
395 size_t cchPath = strlen(pszPath); Assert(cchPath > 0);
396 PSTUBCLEANUPREC pRec = (PSTUBCLEANUPREC)RTMemAlloc(RT_OFFSETOF(STUBCLEANUPREC, szPath[cchPath + 1]));
397 if (!pRec)
398 {
399 ShowError("Out of memory!");
400 return false;
401 }
402 pRec->fFile = fFile;
403 memcpy(pRec->szPath, pszPath, cchPath + 1);
404
405 RTListPrepend(&g_TmpFiles, &pRec->ListEntry);
406 return true;
407}
408
409
410/**
411 * Cleans up all the extracted files and optionally removes the package
412 * directory.
413 *
414 * @param cPackages The number of packages.
415 * @param pszPkgDir The package directory, NULL if it shouldn't be
416 * removed.
417 */
418static void CleanUp(unsigned cPackages, const char *pszPkgDir)
419{
420 for (int i = 0; i < 5; i++)
421 {
422 int rc;
423 bool fFinalTry = i == 4;
424
425 PSTUBCLEANUPREC pCur, pNext;
426 RTListForEachSafe(&g_TmpFiles, pCur, pNext, STUBCLEANUPREC, ListEntry)
427 {
428 if (pCur->fFile)
429 rc = RTFileDelete(pCur->szPath);
430 else
431 {
432 rc = RTDirRemoveRecursive(pCur->szPath, RTDIRRMREC_F_CONTENT_AND_DIR);
433 if (rc == VERR_DIR_NOT_EMPTY && fFinalTry)
434 rc = VINF_SUCCESS;
435 }
436 if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
437 rc = VINF_SUCCESS;
438 if (RT_SUCCESS(rc))
439 {
440 RTListNodeRemove(&pCur->ListEntry);
441 RTMemFree(pCur);
442 }
443 else if (fFinalTry)
444 {
445 if (pCur->fFile)
446 ShowError("Failed to delete temporary file '%s': %Rrc", pCur->szPath, rc);
447 else
448 ShowError("Failed to delete temporary directory '%s': %Rrc", pCur->szPath, rc);
449 }
450 }
451
452 if (RTListIsEmpty(&g_TmpFiles) || fFinalTry)
453 {
454 if (!pszPkgDir)
455 return;
456 rc = RTDirRemove(pszPkgDir);
457 if (RT_SUCCESS(rc) || rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND || fFinalTry)
458 return;
459 }
460
461 /* Delay a little and try again. */
462 RTThreadSleep(i == 0 ? 100 : 3000);
463 }
464}
465
466
467/**
468 * Processes an MSI package.
469 *
470 * @returns Fully complained exit code.
471 * @param pszMsi The path to the MSI to process.
472 * @param pszMsiArgs Any additional installer (MSI) argument
473 * @param fLogging Whether to enable installer logging.
474 */
475static RTEXITCODE ProcessMsiPackage(const char *pszMsi, const char *pszMsiArgs, bool fLogging)
476{
477 int rc;
478
479 /*
480 * Set UI level.
481 */
482 INSTALLUILEVEL enmDesiredUiLevel = g_fSilent ? INSTALLUILEVEL_NONE : INSTALLUILEVEL_FULL;
483 INSTALLUILEVEL enmRet = MsiSetInternalUI(enmDesiredUiLevel, NULL);
484 if (enmRet == INSTALLUILEVEL_NOCHANGE /* means error */)
485 return ShowError("Internal error: MsiSetInternalUI failed.");
486
487 /*
488 * Enable logging?
489 */
490 if (fLogging)
491 {
492 char szLogFile[RTPATH_MAX];
493 rc = RTStrCopy(szLogFile, sizeof(szLogFile), pszMsi);
494 if (RT_SUCCESS(rc))
495 {
496 RTPathStripFilename(szLogFile);
497 rc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxInstallLog.txt");
498 }
499 if (RT_FAILURE(rc))
500 return ShowError("Internal error: Filename path too long.");
501
502 PRTUTF16 pwszLogFile;
503 rc = RTStrToUtf16(szLogFile, &pwszLogFile);
504 if (RT_FAILURE(rc))
505 return ShowError("RTStrToUtf16 failed on '%s': %Rrc", szLogFile, rc);
506
507 UINT uLogLevel = MsiEnableLogW(INSTALLLOGMODE_VERBOSE,
508 pwszLogFile,
509 INSTALLLOGATTRIBUTES_FLUSHEACHLINE);
510 RTUtf16Free(pwszLogFile);
511 if (uLogLevel != ERROR_SUCCESS)
512 return ShowError("MsiEnableLogW failed");
513 }
514
515 /*
516 * Initialize the common controls (extended version). This is necessary to
517 * run the actual .MSI installers with the new fancy visual control
518 * styles (XP+). Also, an integrated manifest is required.
519 */
520 INITCOMMONCONTROLSEX ccEx;
521 ccEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
522 ccEx.dwICC = ICC_LINK_CLASS | ICC_LISTVIEW_CLASSES | ICC_PAGESCROLLER_CLASS |
523 ICC_PROGRESS_CLASS | ICC_STANDARD_CLASSES | ICC_TAB_CLASSES | ICC_TREEVIEW_CLASSES |
524 ICC_UPDOWN_CLASS | ICC_USEREX_CLASSES | ICC_WIN95_CLASSES;
525 InitCommonControlsEx(&ccEx); /* Ignore failure. */
526
527 /*
528 * Convert both strings to UTF-16 and start the installation.
529 */
530 PRTUTF16 pwszMsi;
531 rc = RTStrToUtf16(pszMsi, &pwszMsi);
532 if (RT_FAILURE(rc))
533 return ShowError("RTStrToUtf16 failed on '%s': %Rrc", pszMsi, rc);
534 PRTUTF16 pwszMsiArgs;
535 rc = RTStrToUtf16(pszMsiArgs, &pwszMsiArgs);
536 if (RT_FAILURE(rc))
537 {
538 RTUtf16Free(pwszMsi);
539 return ShowError("RTStrToUtf16 failed on '%s': %Rrc", pszMsi, rc);
540 }
541
542 UINT uStatus = MsiInstallProductW(pwszMsi, pwszMsiArgs);
543 RTUtf16Free(pwszMsi);
544 RTUtf16Free(pwszMsiArgs);
545
546 if (uStatus == ERROR_SUCCESS)
547 return RTEXITCODE_SUCCESS;
548 if (uStatus == ERROR_SUCCESS_REBOOT_REQUIRED)
549 return RTEXITCODE_SUCCESS; /* we currently don't indicate this */
550
551 /*
552 * Installation failed. Figure out what to say.
553 */
554 switch (uStatus)
555 {
556 case ERROR_INSTALL_USEREXIT:
557 /* Don't say anything? */
558 break;
559
560 case ERROR_INSTALL_PACKAGE_VERSION:
561 ShowError("This installation package cannot be installed by the Windows Installer service.\n"
562 "You must install a Windows service pack that contains a newer version of the Windows Installer service.");
563 break;
564
565 case ERROR_INSTALL_PLATFORM_UNSUPPORTED:
566 ShowError("This installation package is not supported on this platform.");
567 break;
568
569 default:
570 {
571 /*
572 * Try get windows to format the message.
573 */
574 DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER
575 | FORMAT_MESSAGE_IGNORE_INSERTS
576 | FORMAT_MESSAGE_FROM_SYSTEM;
577 HMODULE hModule = NULL;
578 if (uStatus >= NERR_BASE && uStatus <= MAX_NERR)
579 {
580 hModule = LoadLibraryExW(L"netmsg.dll",
581 NULL,
582 LOAD_LIBRARY_AS_DATAFILE);
583 if (hModule != NULL)
584 dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE;
585 }
586
587 PWSTR pwszMsg;
588 if (FormatMessageW(dwFormatFlags,
589 hModule, /* If NULL, load system stuff. */
590 uStatus,
591 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
592 (PWSTR)&pwszMsg,
593 0,
594 NULL) > 0)
595 {
596 ShowError("Installation failed! Error: %ls", pwszMsg);
597 LocalFree(pwszMsg);
598 }
599 else /* If text lookup failed, show at least the error number. */
600 ShowError("Installation failed! Error: %u", uStatus);
601
602 if (hModule)
603 FreeLibrary(hModule);
604 break;
605 }
606 }
607
608 return RTEXITCODE_FAILURE;
609}
610
611
612/**
613 * Processes a package.
614 *
615 * @returns Fully complained exit code.
616 * @param iPackage The package number.
617 * @param pszPkgDir The package directory (aka extraction dir).
618 * @param pszMsiArgs Any additional installer (MSI) argument
619 * @param fLogging Whether to enable installer logging.
620 */
621static RTEXITCODE ProcessPackage(unsigned iPackage, const char *pszPkgDir, const char *pszMsiArgs, bool fLogging)
622{
623 /*
624 * Get the package header and check if it's needed.
625 */
626 PVBOXSTUBPKG pPackage = FindPackageHeader(iPackage);
627 if (pPackage == NULL)
628 return RTEXITCODE_FAILURE;
629
630 if (!PackageIsNeeded(pPackage))
631 return RTEXITCODE_SUCCESS;
632
633 /*
634 * Deal with the file based on it's extension.
635 */
636 char szPkgFile[RTPATH_MAX];
637 int rc = RTPathJoin(szPkgFile, sizeof(szPkgFile), pszPkgDir, pPackage->szFileName);
638 if (RT_FAILURE(rc))
639 return ShowError("Internal error: RTPathJoin failed: %Rrc", rc);
640 RTPathChangeToDosSlashes(szPkgFile, true /* Force conversion. */); /* paranoia */
641
642 RTEXITCODE rcExit;
643 const char *pszSuff = RTPathSuffix(szPkgFile);
644 if (RTStrICmp(pszSuff, ".msi") == 0)
645 rcExit = ProcessMsiPackage(szPkgFile, pszMsiArgs, fLogging);
646 else if (RTStrICmp(pszSuff, ".cab") == 0)
647 rcExit = RTEXITCODE_SUCCESS; /* Ignore .cab files, they're generally referenced by other files. */
648 else
649 rcExit = ShowError("Internal error: Do not know how to handle file '%s'.", pPackage->szFileName);
650
651 return rcExit;
652}
653
654
655#ifdef VBOX_WITH_CODE_SIGNING
656/**
657 * Install the public certificate into TrustedPublishers so the installer won't
658 * prompt the user during silent installs.
659 *
660 * @returns Fully complained exit code.
661 */
662static RTEXITCODE InstallCertificate(void)
663{
664 if (addCertToStore(CERT_SYSTEM_STORE_LOCAL_MACHINE,
665 "TrustedPublisher",
666 g_ab_VBoxStubPublicCert,
667 sizeof(g_ab_VBoxStubPublicCert)))
668 return RTEXITCODE_SUCCESS;
669 return ShowError("Failed to construct install certificate.");
670}
671#endif /* VBOX_WITH_CODE_SIGNING */
672
673
674/**
675 * Copies the "<exepath>.custom" directory to the extraction path if it exists.
676 *
677 * This is used by the MSI packages from the resource section.
678 *
679 * @returns Fully complained exit code.
680 * @param pszDstDir The destination directory.
681 */
682static RTEXITCODE CopyCustomDir(const char *pszDstDir)
683{
684 char szSrcDir[RTPATH_MAX];
685 int rc = RTPathExecDir(szSrcDir, sizeof(szSrcDir));
686 if (RT_SUCCESS(rc))
687 rc = RTPathAppend(szSrcDir, sizeof(szSrcDir), ".custom");
688 if (RT_FAILURE(rc))
689 return ShowError("Failed to construct '.custom' dir path: %Rrc", rc);
690
691 if (RTDirExists(szSrcDir))
692 {
693 /*
694 * Use SHFileOperation w/ FO_COPY to do the job. This API requires an
695 * extra zero at the end of both source and destination paths.
696 */
697 size_t cwc;
698 RTUTF16 wszSrcDir[RTPATH_MAX + 1];
699 PRTUTF16 pwszSrcDir = wszSrcDir;
700 rc = RTStrToUtf16Ex(szSrcDir, RTSTR_MAX, &pwszSrcDir, RTPATH_MAX, &cwc);
701 if (RT_FAILURE(rc))
702 return ShowError("RTStrToUtf16Ex failed on '%s': %Rrc", szSrcDir, rc);
703 wszSrcDir[cwc] = '\0';
704
705 RTUTF16 wszDstDir[RTPATH_MAX + 1];
706 PRTUTF16 pwszDstDir = wszSrcDir;
707 rc = RTStrToUtf16Ex(pszDstDir, RTSTR_MAX, &pwszDstDir, RTPATH_MAX, &cwc);
708 if (RT_FAILURE(rc))
709 return ShowError("RTStrToUtf16Ex failed on '%s': %Rrc", pszDstDir, rc);
710 wszDstDir[cwc] = '\0';
711
712 SHFILEOPSTRUCTW FileOp;
713 RT_ZERO(FileOp); /* paranoia */
714 FileOp.hwnd = NULL;
715 FileOp.wFunc = FO_COPY;
716 FileOp.pFrom = wszSrcDir;
717 FileOp.pTo = wszDstDir;
718 FileOp.fFlags = FOF_SILENT
719 | FOF_NOCONFIRMATION
720 | FOF_NOCONFIRMMKDIR
721 | FOF_NOERRORUI;
722 FileOp.fAnyOperationsAborted = FALSE;
723 FileOp.hNameMappings = NULL;
724 FileOp.lpszProgressTitle = NULL;
725
726 rc = SHFileOperationW(&FileOp);
727 if (rc != 0) /* Not a Win32 status code! */
728 return ShowError("Copying the '.custom' dir failed: %#x", rc);
729
730 /*
731 * Add a cleanup record for recursively deleting the destination
732 * .custom directory. We should actually add this prior to calling
733 * SHFileOperationW since it may partially succeed...
734 */
735 char *pszDstSubDir = RTPathJoinA(pszDstDir, ".custom");
736 if (!pszDstSubDir)
737 return ShowError("Out of memory!");
738 bool fRc = AddCleanupRec(pszDstSubDir, false /*fFile*/);
739 RTStrFree(pszDstSubDir);
740 if (!fRc)
741 return RTEXITCODE_FAILURE;
742 }
743
744 return RTEXITCODE_SUCCESS;
745}
746
747
748static RTEXITCODE ExtractFiles(unsigned cPackages, const char *pszDstDir, bool fExtractOnly, bool *pfCreatedExtractDir)
749{
750 int rc;
751
752 /*
753 * Make sure the directory exists.
754 */
755 *pfCreatedExtractDir = false;
756 if (!RTDirExists(pszDstDir))
757 {
758 rc = RTDirCreate(pszDstDir, 0700, 0);
759 if (RT_FAILURE(rc))
760 return ShowError("Failed to create extraction path '%s': %Rrc", pszDstDir, rc);
761 *pfCreatedExtractDir = true;
762 }
763
764 /*
765 * Extract files.
766 */
767 for (unsigned k = 0; k < cPackages; k++)
768 {
769 PVBOXSTUBPKG pPackage = FindPackageHeader(k);
770 if (!pPackage)
771 return RTEXITCODE_FAILURE; /* Done complaining already. */
772
773 if (fExtractOnly || PackageIsNeeded(pPackage))
774 {
775 char szDstFile[RTPATH_MAX];
776 rc = RTPathJoin(szDstFile, sizeof(szDstFile), pszDstDir, pPackage->szFileName);
777 if (RT_FAILURE(rc))
778 return ShowError("Internal error: RTPathJoin failed: %Rrc", rc);
779
780 rc = Extract(pPackage, szDstFile);
781 if (RT_FAILURE(rc))
782 return ShowError("Error extracting package #%u: %Rrc", k, rc);
783
784 if (!fExtractOnly && !AddCleanupRec(szDstFile, true /*fFile*/))
785 {
786 RTFileDelete(szDstFile);
787 return RTEXITCODE_FAILURE;
788 }
789 }
790 }
791
792 return RTEXITCODE_SUCCESS;
793}
794
795
796int WINAPI WinMain(HINSTANCE hInstance,
797 HINSTANCE hPrevInstance,
798 char *lpCmdLine,
799 int nCmdShow)
800{
801 char **argv = __argv;
802 int argc = __argc;
803
804 /*
805 * Init IPRT. This is _always_ the very first thing we do.
806 */
807 int vrc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_STANDALONE_APP);
808 if (RT_FAILURE(vrc))
809 return RTMsgInitFailure(vrc);
810
811 /*
812 * Check if we're already running and jump out if so.
813 *
814 * Note! Do not use a global namespace ("Global\\") for mutex name here,
815 * will blow up NT4 compatibility!
816 */
817 HANDLE hMutexAppRunning = CreateMutex(NULL, FALSE, "VBoxStubInstaller");
818 if ( hMutexAppRunning != NULL
819 && GetLastError() == ERROR_ALREADY_EXISTS)
820 {
821 /* Close the mutex for this application instance. */
822 CloseHandle(hMutexAppRunning);
823 hMutexAppRunning = NULL;
824 return RTEXITCODE_FAILURE;
825 }
826
827 /*
828 * Parse arguments.
829 */
830
831 /* Parameter variables. */
832 bool fExtractOnly = false;
833 bool fEnableLogging = false;
834#ifdef VBOX_WITH_CODE_SIGNING
835 bool fEnableSilentCert = true;
836#endif
837 char szExtractPath[RTPATH_MAX] = {0};
838 char szMSIArgs[_4K] = {0};
839
840 /* Parameter definitions. */
841 static const RTGETOPTDEF s_aOptions[] =
842 {
843 /** @todo Replace short parameters with enums since they're not
844 * used (and not documented to the public). */
845 { "--extract", 'x', RTGETOPT_REQ_NOTHING },
846 { "-extract", 'x', RTGETOPT_REQ_NOTHING },
847 { "/extract", 'x', RTGETOPT_REQ_NOTHING },
848 { "--silent", 's', RTGETOPT_REQ_NOTHING },
849 { "-silent", 's', RTGETOPT_REQ_NOTHING },
850 { "/silent", 's', RTGETOPT_REQ_NOTHING },
851#ifdef VBOX_WITH_CODE_SIGNING
852 { "--no-silent-cert", 'c', RTGETOPT_REQ_NOTHING },
853 { "-no-silent-cert", 'c', RTGETOPT_REQ_NOTHING },
854 { "/no-silent-cert", 'c', RTGETOPT_REQ_NOTHING },
855#endif
856 { "--logging", 'l', RTGETOPT_REQ_NOTHING },
857 { "-logging", 'l', RTGETOPT_REQ_NOTHING },
858 { "/logging", 'l', RTGETOPT_REQ_NOTHING },
859 { "--path", 'p', RTGETOPT_REQ_STRING },
860 { "-path", 'p', RTGETOPT_REQ_STRING },
861 { "/path", 'p', RTGETOPT_REQ_STRING },
862 { "--msiparams", 'm', RTGETOPT_REQ_STRING },
863 { "-msiparams", 'm', RTGETOPT_REQ_STRING },
864 { "--reinstall", 'f', RTGETOPT_REQ_NOTHING },
865 { "-reinstall", 'f', RTGETOPT_REQ_NOTHING },
866 { "/reinstall", 'f', RTGETOPT_REQ_NOTHING },
867 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
868 { "-verbose", 'v', RTGETOPT_REQ_NOTHING },
869 { "/verbose", 'v', RTGETOPT_REQ_NOTHING },
870 { "--version", 'V', RTGETOPT_REQ_NOTHING },
871 { "-version", 'V', RTGETOPT_REQ_NOTHING },
872 { "/version", 'V', RTGETOPT_REQ_NOTHING },
873 { "-v", 'V', RTGETOPT_REQ_NOTHING },
874 { "--help", 'h', RTGETOPT_REQ_NOTHING },
875 { "-help", 'h', RTGETOPT_REQ_NOTHING },
876 { "/help", 'h', RTGETOPT_REQ_NOTHING },
877 { "/?", 'h', RTGETOPT_REQ_NOTHING },
878 };
879
880 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
881
882 /* Parse the parameters. */
883 int ch;
884 bool fExitEarly = false;
885 RTGETOPTUNION ValueUnion;
886 RTGETOPTSTATE GetState;
887 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
888 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
889 && rcExit == RTEXITCODE_SUCCESS
890 && !fExitEarly)
891 {
892 switch (ch)
893 {
894 case 'f': /* Force re-installation. */
895 if (szMSIArgs[0])
896 vrc = RTStrCat(szMSIArgs, sizeof(szMSIArgs), " ");
897 if (RT_SUCCESS(vrc))
898 vrc = RTStrCat(szMSIArgs, sizeof(szMSIArgs),
899 "REINSTALLMODE=vomus REINSTALL=ALL");
900 if (RT_FAILURE(vrc))
901 rcExit = ShowError("MSI parameters are too long.");
902 break;
903
904 case 'x':
905 fExtractOnly = true;
906 break;
907
908 case 's':
909 g_fSilent = true;
910 break;
911
912#ifdef VBOX_WITH_CODE_SIGNING
913 case 'c':
914 fEnableSilentCert = false;
915 break;
916#endif
917 case 'l':
918 fEnableLogging = true;
919 break;
920
921 case 'p':
922 vrc = RTStrCopy(szExtractPath, sizeof(szExtractPath), ValueUnion.psz);
923 if (RT_FAILURE(vrc))
924 rcExit = ShowError("Extraction path is too long.");
925 break;
926
927 case 'm':
928 if (szMSIArgs[0])
929 vrc = RTStrCat(szMSIArgs, sizeof(szMSIArgs), " ");
930 if (RT_SUCCESS(vrc))
931 vrc = RTStrCat(szMSIArgs, sizeof(szMSIArgs), ValueUnion.psz);
932 if (RT_FAILURE(vrc))
933 rcExit = ShowError("MSI parameters are too long.");
934 break;
935
936 case 'V':
937 ShowInfo("Version: %d.%d.%d.%d",
938 VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD,
939 VBOX_SVN_REV);
940 fExitEarly = true;
941 break;
942
943 case 'v':
944 g_iVerbosity++;
945 break;
946
947 case 'h':
948 ShowInfo("-- %s v%d.%d.%d.%d --\n"
949 "\n"
950 "Command Line Parameters:\n\n"
951 "--extract - Extract file contents to temporary directory\n"
952 "--help - Print this help and exit\n"
953 "--logging - Enables installer logging\n"
954 "--msiparams <parameters> - Specifies extra parameters for the MSI installers\n"
955 "--no-silent-cert - Do not install VirtualBox Certificate automatically when --silent option is specified\n"
956 "--path - Sets the path of the extraction directory\n"
957 "--reinstall - Forces VirtualBox to get re-installed\n"
958 "--silent - Enables silent mode installation\n"
959 "--version - Print version number and exit\n\n"
960 "Examples:\n"
961 "%s --msiparams INSTALLDIR=C:\\VBox\n"
962 "%s --extract -path C:\\VBox",
963 VBOX_STUB_TITLE, VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV,
964 argv[0], argv[0]);
965 fExitEarly = true;
966 break;
967
968 case VINF_GETOPT_NOT_OPTION:
969 /* Are (optional) MSI parameters specified and this is the last
970 * parameter? Append everything to the MSI parameter list then. */
971 if (szMSIArgs[0])
972 {
973 vrc = RTStrCat(szMSIArgs, sizeof(szMSIArgs), " ");
974 if (RT_SUCCESS(vrc))
975 vrc = RTStrCat(szMSIArgs, sizeof(szMSIArgs), ValueUnion.psz);
976 if (RT_FAILURE(vrc))
977 rcExit = ShowError("MSI parameters are too long.");
978 continue;
979 }
980 /* Fall through is intentional. */
981
982 default:
983 if (g_fSilent)
984 rcExit = RTGetOptPrintError(ch, &ValueUnion);
985 if (ch == VERR_GETOPT_UNKNOWN_OPTION)
986 rcExit = ShowError("Unknown option \"%s\"\n"
987 "Please refer to the command line help by specifying \"/?\"\n"
988 "to get more information.", ValueUnion.psz);
989 else
990 rcExit = ShowError("Parameter parsing error: %Rrc\n"
991 "Please refer to the command line help by specifying \"/?\"\n"
992 "to get more information.", ch);
993 break;
994 }
995 }
996
997 /* Check if we can bail out early. */
998 if (fExitEarly)
999 return rcExit;
1000
1001 if (rcExit != RTEXITCODE_SUCCESS)
1002 vrc = VERR_PARSE_ERROR;
1003
1004#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0501
1005# ifdef VBOX_STUB_WITH_OWN_CONSOLE /* Use an own console window if run in debug mode. */
1006 if ( RT_SUCCESS(vrc)
1007 && g_iVerbosity)
1008 {
1009 if (!AllocConsole())
1010 {
1011 DWORD dwErr = GetLastError();
1012 ShowError("Unable to allocate console, error = %ld\n",
1013 dwErr);
1014
1015 /* Close the mutex for this application instance. */
1016 CloseHandle(hMutexAppRunning);
1017 hMutexAppRunning = NULL;
1018 return RTEXITCODE_FAILURE;
1019 }
1020
1021 freopen("CONOUT$", "w", stdout);
1022 setvbuf(stdout, NULL, _IONBF, 0);
1023
1024 freopen("CONOUT$", "w", stderr);
1025 }
1026# endif /* VBOX_STUB_WITH_OWN_CONSOLE */
1027#endif
1028
1029 if ( RT_SUCCESS(vrc)
1030 && g_iVerbosity)
1031 {
1032 RTPrintf("Silent installation : %RTbool\n", g_fSilent);
1033 RTPrintf("Logging enabled : %RTbool\n", fEnableLogging);
1034#ifdef VBOX_WITH_CODE_SIGNING
1035 RTPrintf("Certificate installation : %RTbool\n", fEnableSilentCert);
1036#endif
1037 RTPrintf("Additional MSI parameters: %s\n",
1038 szMSIArgs[0] ? szMSIArgs : "<None>");
1039 }
1040
1041 if (RT_SUCCESS(vrc))
1042 {
1043 /*
1044 * Determine the extration path if not given by the user, and gather some
1045 * other bits we'll be needing later.
1046 */
1047 if (szExtractPath[0] == '\0')
1048 {
1049 vrc = RTPathTemp(szExtractPath, sizeof(szExtractPath));
1050 if (RT_SUCCESS(vrc))
1051 vrc = RTPathAppend(szExtractPath, sizeof(szExtractPath), "VirtualBox");
1052 if (RT_FAILURE(vrc))
1053 ShowError("Failed to determine extraction path (%Rrc)", vrc);
1054
1055 }
1056 else
1057 {
1058 /** @todo should check if there is a .custom subdirectory there or not. */
1059 }
1060 RTPathChangeToDosSlashes(szExtractPath,
1061 true /* Force conversion. */); /* MSI requirement. */
1062 }
1063
1064 /* Read our manifest. */
1065 PVBOXSTUBPKGHEADER pHeader;
1066 if (RT_SUCCESS(vrc))
1067 {
1068 vrc = FindData("MANIFEST", (PVOID *)&pHeader, NULL);
1069 if (RT_FAILURE(vrc))
1070 rcExit = ShowError("Internal package error: Manifest not found (%Rrc)", vrc);
1071 }
1072 if (RT_SUCCESS(vrc))
1073 {
1074 /** @todo If we could, we should validate the header. Only the magic isn't
1075 * commonly defined, nor the version number... */
1076
1077 RTListInit(&g_TmpFiles);
1078
1079 /*
1080 * Up to this point, we haven't done anything that requires any cleanup.
1081 * From here on, we do everything in function so we can counter clean up.
1082 */
1083 bool fCreatedExtractDir;
1084 rcExit = ExtractFiles(pHeader->byCntPkgs, szExtractPath,
1085 fExtractOnly, &fCreatedExtractDir);
1086 if (rcExit == RTEXITCODE_SUCCESS)
1087 {
1088 if (fExtractOnly)
1089 ShowInfo("Files were extracted to: %s", szExtractPath);
1090 else
1091 {
1092 rcExit = CopyCustomDir(szExtractPath);
1093#ifdef VBOX_WITH_CODE_SIGNING
1094 if (rcExit == RTEXITCODE_SUCCESS && fEnableSilentCert && g_fSilent)
1095 rcExit = InstallCertificate();
1096#endif
1097 unsigned iPackage = 0;
1098 while ( iPackage < pHeader->byCntPkgs
1099 && rcExit == RTEXITCODE_SUCCESS)
1100 {
1101 rcExit = ProcessPackage(iPackage, szExtractPath,
1102 szMSIArgs, fEnableLogging);
1103 iPackage++;
1104 }
1105
1106 /* Don't fail if cleanup fail. At least for now. */
1107 CleanUp(pHeader->byCntPkgs,
1108 !fEnableLogging
1109 && fCreatedExtractDir ? szExtractPath : NULL);
1110 }
1111 }
1112
1113 /* Free any left behind cleanup records (not strictly needed). */
1114 PSTUBCLEANUPREC pCur, pNext;
1115 RTListForEachSafe(&g_TmpFiles, pCur, pNext, STUBCLEANUPREC, ListEntry)
1116 {
1117 RTListNodeRemove(&pCur->ListEntry);
1118 RTMemFree(pCur);
1119 }
1120 }
1121
1122#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0501
1123# ifdef VBOX_STUB_WITH_OWN_CONSOLE
1124 if (g_iVerbosity)
1125 FreeConsole();
1126# endif /* VBOX_STUB_WITH_OWN_CONSOLE */
1127#endif
1128
1129 /*
1130 * Release instance mutex.
1131 */
1132 if (hMutexAppRunning != NULL)
1133 {
1134 CloseHandle(hMutexAppRunning);
1135 hMutexAppRunning = NULL;
1136 }
1137
1138 return rcExit;
1139}
1140
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette