VirtualBox

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

最後變更 在這個檔案從76416是 76370,由 vboxsync 提交於 6 年 前

*: string.h sans err.h fix. bugref:9344

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

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