VirtualBox

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

Last change on this file since 51171 was 51171, checked in by vboxsync, 11 years ago

Build fix.

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