1 | /* $Id: VBoxStubBld.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * VBoxStubBld - VirtualBox's Windows installer stub builder.
|
---|
4 | */
|
---|
5 |
|
---|
6 | /*
|
---|
7 | * Copyright (C) 2009-2024 Oracle and/or its affiliates.
|
---|
8 | *
|
---|
9 | * This file is part of VirtualBox base platform packages, as
|
---|
10 | * available from https://www.virtualbox.org.
|
---|
11 | *
|
---|
12 | * This program is free software; you can redistribute it and/or
|
---|
13 | * modify it under the terms of the GNU General Public License
|
---|
14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
15 | * License.
|
---|
16 | *
|
---|
17 | * This program is distributed in the hope that it will be useful, but
|
---|
18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
20 | * General Public License for more details.
|
---|
21 | *
|
---|
22 | * You should have received a copy of the GNU General Public License
|
---|
23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
24 | *
|
---|
25 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
26 | */
|
---|
27 |
|
---|
28 |
|
---|
29 | /*********************************************************************************************************************************
|
---|
30 | * Header Files *
|
---|
31 | *********************************************************************************************************************************/
|
---|
32 | #include <iprt/win/windows.h>
|
---|
33 | #include <shellapi.h>
|
---|
34 | #include <strsafe.h>
|
---|
35 |
|
---|
36 | #include <VBox/version.h>
|
---|
37 | #include <iprt/types.h>
|
---|
38 |
|
---|
39 | #include "VBoxStubBld.h"
|
---|
40 |
|
---|
41 |
|
---|
42 | static HRESULT GetFile(const char *pszFilePath, HANDLE *phFile, DWORD *pcbFile)
|
---|
43 | {
|
---|
44 | HANDLE hFile = CreateFileA(pszFilePath, GENERIC_READ, FILE_SHARE_READ, NULL,
|
---|
45 | OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
---|
46 | if (hFile != INVALID_HANDLE_VALUE)
|
---|
47 | {
|
---|
48 | SetLastError(NO_ERROR);
|
---|
49 | LARGE_INTEGER cbFile;
|
---|
50 | if (GetFileSizeEx(hFile, &cbFile))
|
---|
51 | {
|
---|
52 | if (cbFile.HighPart == 0)
|
---|
53 | {
|
---|
54 | *pcbFile = cbFile.LowPart;
|
---|
55 | *phFile = hFile;
|
---|
56 | return S_OK;
|
---|
57 | }
|
---|
58 | fprintf(stderr, "error: File '%s' is too large: %llu bytes\n", pszFilePath, cbFile.QuadPart);
|
---|
59 | }
|
---|
60 | else
|
---|
61 | fprintf(stderr, "error: GetFileSizeEx failed on '%s': %lu\n", pszFilePath, GetLastError());
|
---|
62 | CloseHandle(hFile);
|
---|
63 | }
|
---|
64 | else
|
---|
65 | fprintf(stderr, "error: CreateFileA failed on '%s': %lu\n", pszFilePath, GetLastError());
|
---|
66 | *phFile = INVALID_HANDLE_VALUE;
|
---|
67 | return E_FAIL;
|
---|
68 | }
|
---|
69 |
|
---|
70 | static HRESULT MyUpdateResource(HANDLE hFile, DWORD cbFile, HANDLE hResourceUpdate,
|
---|
71 | const char *pszResourceType, const char *pszResourceId)
|
---|
72 | {
|
---|
73 | HRESULT hr = E_FAIL;
|
---|
74 | HANDLE hMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
---|
75 | if (hMap != NULL)
|
---|
76 | {
|
---|
77 | PVOID pvFile = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, cbFile);
|
---|
78 | if (pvFile)
|
---|
79 | {
|
---|
80 | if (UpdateResourceA(hResourceUpdate, pszResourceType, pszResourceId,
|
---|
81 | MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), pvFile, cbFile))
|
---|
82 | hr = S_OK;
|
---|
83 | else
|
---|
84 | fprintf(stderr, "error: UpdateResourceA failed: %lu\n", GetLastError());
|
---|
85 |
|
---|
86 | UnmapViewOfFile(pvFile);
|
---|
87 | }
|
---|
88 | else
|
---|
89 | fprintf(stderr, "error: MapViewOfFile failed: %lu\n", GetLastError());
|
---|
90 | CloseHandle(hMap);
|
---|
91 | }
|
---|
92 | else
|
---|
93 | fprintf(stderr, "error: CreateFileMappingW failed: %lu\n", GetLastError());
|
---|
94 | return hr;
|
---|
95 | }
|
---|
96 |
|
---|
97 | static HRESULT IntegrateFile(HANDLE hResourceUpdate, const char *pszResourceType,
|
---|
98 | const char *pszResourceId, const char *pszFilePath)
|
---|
99 | {
|
---|
100 | HANDLE hFile = INVALID_HANDLE_VALUE;
|
---|
101 | DWORD cbFile = 0;
|
---|
102 | HRESULT hr = GetFile(pszFilePath, &hFile, &cbFile);
|
---|
103 | if (SUCCEEDED(hr))
|
---|
104 | {
|
---|
105 | hr = MyUpdateResource(hFile, cbFile, hResourceUpdate, pszResourceType, pszResourceId);
|
---|
106 | if (FAILED(hr))
|
---|
107 | printf("ERROR: Error updating resource for file %s!", pszFilePath);
|
---|
108 | CloseHandle(hFile);
|
---|
109 | }
|
---|
110 | else
|
---|
111 | hr = HRESULT_FROM_WIN32(GetLastError());
|
---|
112 | return hr;
|
---|
113 | }
|
---|
114 |
|
---|
115 | static char *MyPathFilename(const char *pszPath)
|
---|
116 | {
|
---|
117 | const char *pszName = pszPath;
|
---|
118 | for (const char *psz = pszPath;; psz++)
|
---|
119 | {
|
---|
120 | switch (*psz)
|
---|
121 | {
|
---|
122 | /* handle separators. */
|
---|
123 | case ':':
|
---|
124 | pszName = psz + 1;
|
---|
125 | break;
|
---|
126 |
|
---|
127 | case '\\':
|
---|
128 | case '/':
|
---|
129 | pszName = psz + 1;
|
---|
130 | break;
|
---|
131 |
|
---|
132 | /* the end */
|
---|
133 | case '\0':
|
---|
134 | if (*pszName)
|
---|
135 | return (char *)(void *)pszName;
|
---|
136 | return NULL;
|
---|
137 | }
|
---|
138 | }
|
---|
139 |
|
---|
140 | /* will never get here */
|
---|
141 | }
|
---|
142 |
|
---|
143 |
|
---|
144 | int main(int argc, char* argv[])
|
---|
145 | {
|
---|
146 | /*
|
---|
147 | * Parse arguments.
|
---|
148 | */
|
---|
149 | const char *pszSetupStub = "VBoxStub.exe";
|
---|
150 | const char *pszOutput = "VirtualBox-MultiArch.exe";
|
---|
151 |
|
---|
152 | printf(VBOX_PRODUCT " Stub Builder v%d.%d.%d.%d\n",
|
---|
153 | VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV);
|
---|
154 |
|
---|
155 | struct VBOXSTUBBUILDPKG
|
---|
156 | {
|
---|
157 | const char *pszSrcPath;
|
---|
158 | VBOXSTUBPKGARCH enmArch;
|
---|
159 | } aBuildPkgs[VBOXSTUB_MAX_PACKAGES] = { { NULL } };
|
---|
160 | VBOXSTUBPKGHEADER StubHdr =
|
---|
161 | {
|
---|
162 | /*.szMagic = */ VBOXSTUBPKGHEADER_MAGIC_SZ,
|
---|
163 | /*.cPackages = */ 0
|
---|
164 | };
|
---|
165 |
|
---|
166 | for (int i = 1; i < argc; i++)
|
---|
167 | {
|
---|
168 | const char *pszArg = argv[i];
|
---|
169 | if ( strcmp(pszArg, "--help") == 0
|
---|
170 | || strcmp(pszArg, "-help") == 0
|
---|
171 | || strcmp(pszArg, "-h") == 0
|
---|
172 | || strcmp(pszArg, "-?") == 0)
|
---|
173 | {
|
---|
174 | printf("usage: %s -out <installer.exe> -stub <stub.exe> [-target-all <file>] [-target-<arch> <file>]\n", argv[0]);
|
---|
175 | return RTEXITCODE_SUCCESS;
|
---|
176 | }
|
---|
177 |
|
---|
178 | /* The remaining options all take a while. */
|
---|
179 | if ( strcmp(pszArg, "-out")
|
---|
180 | && strcmp(pszArg, "-stub")
|
---|
181 | && strcmp(pszArg, "-target-all")
|
---|
182 | && strcmp(pszArg, "-target-x86")
|
---|
183 | && strcmp(pszArg, "-target-amd64"))
|
---|
184 | {
|
---|
185 | fprintf(stderr, "syntax error: Invalid parameter: %s\n", argv[i]);
|
---|
186 | return RTEXITCODE_SYNTAX;
|
---|
187 | }
|
---|
188 |
|
---|
189 | i++;
|
---|
190 | if (i >= argc)
|
---|
191 | {
|
---|
192 | fprintf(stderr, "syntax error: Option '%s' takes a value argument!\n", pszArg);
|
---|
193 | return RTEXITCODE_SYNTAX;
|
---|
194 | }
|
---|
195 | const char *pszValue = argv[i];
|
---|
196 |
|
---|
197 | /* Process the individual options. */
|
---|
198 | if (strcmp(pszArg, "-out") == 0)
|
---|
199 | pszOutput = pszValue;
|
---|
200 | else if (strcmp(pszArg, "-stub") == 0)
|
---|
201 | pszSetupStub = pszValue;
|
---|
202 | else
|
---|
203 | {
|
---|
204 | if (StubHdr.cPackages >= RT_ELEMENTS(aBuildPkgs))
|
---|
205 | {
|
---|
206 | fprintf(stderr, "error: Too many packages specified!\n");
|
---|
207 | return RTEXITCODE_FAILURE;
|
---|
208 | }
|
---|
209 | aBuildPkgs[StubHdr.cPackages].pszSrcPath = pszValue;
|
---|
210 | if (strcmp(pszArg, "-target-all") == 0)
|
---|
211 | aBuildPkgs[StubHdr.cPackages].enmArch = VBOXSTUBPKGARCH_ALL;
|
---|
212 | else if (strcmp(pszArg, "-target-amd64") == 0)
|
---|
213 | aBuildPkgs[StubHdr.cPackages].enmArch = VBOXSTUBPKGARCH_AMD64;
|
---|
214 | else if (strcmp(pszArg, "-target-x86") == 0)
|
---|
215 | aBuildPkgs[StubHdr.cPackages].enmArch = VBOXSTUBPKGARCH_X86;
|
---|
216 | else
|
---|
217 | {
|
---|
218 | fprintf(stderr, "internal error: %u\n", __LINE__);
|
---|
219 | return RTEXITCODE_FAILURE;
|
---|
220 | }
|
---|
221 | StubHdr.cPackages++;
|
---|
222 | }
|
---|
223 | }
|
---|
224 |
|
---|
225 | if (StubHdr.cPackages == 0)
|
---|
226 | {
|
---|
227 | fprintf(stderr, "syntax error: No packages specified! Exiting.\n");
|
---|
228 | return RTEXITCODE_SYNTAX;
|
---|
229 | }
|
---|
230 |
|
---|
231 | printf("Stub: %s\n", pszSetupStub);
|
---|
232 | printf("Output: %s\n", pszOutput);
|
---|
233 | printf("# Packages: %u\n", StubHdr.cPackages);
|
---|
234 |
|
---|
235 | /*
|
---|
236 | * Copy the stub over the output file.
|
---|
237 | */
|
---|
238 | if (!CopyFile(pszSetupStub, pszOutput, FALSE))
|
---|
239 | {
|
---|
240 | fprintf(stderr, "ERROR: Could not copy the stub loader: %lu\n", GetLastError());
|
---|
241 | return RTEXITCODE_SYNTAX;
|
---|
242 | }
|
---|
243 |
|
---|
244 | /*
|
---|
245 | * Start updating the resources of the output file.
|
---|
246 | */
|
---|
247 | HANDLE hUpdate = BeginUpdateResourceA(pszOutput, FALSE);
|
---|
248 | if (hUpdate)
|
---|
249 | {
|
---|
250 | /*
|
---|
251 | * Add the file one by one to the output file.
|
---|
252 | */
|
---|
253 | HRESULT hrc = S_OK;
|
---|
254 | for (BYTE i = 0; i < StubHdr.cPackages; i++)
|
---|
255 | {
|
---|
256 | printf("Integrating (Platform %d): %s\n", aBuildPkgs[i].enmArch, aBuildPkgs[i].pszSrcPath);
|
---|
257 |
|
---|
258 | /*
|
---|
259 | * Create the package header.
|
---|
260 | */
|
---|
261 | VBOXSTUBPKG Package = {0};
|
---|
262 | Package.enmArch = aBuildPkgs[i].enmArch;
|
---|
263 |
|
---|
264 | /* The resource name */
|
---|
265 | hrc = StringCchPrintf(Package.szResourceName, sizeof(Package.szResourceName), "BIN_%02d", i);
|
---|
266 | if (FAILED(hrc))
|
---|
267 | {
|
---|
268 | fprintf(stderr, "Internal error: %u\n", __LINE__);
|
---|
269 | break;
|
---|
270 | }
|
---|
271 |
|
---|
272 | /* Construct final name used when extracting. */
|
---|
273 | hrc = StringCchCopy(Package.szFilename, sizeof(Package.szFilename), MyPathFilename(aBuildPkgs[i].pszSrcPath));
|
---|
274 | if (FAILED(hrc))
|
---|
275 | {
|
---|
276 | fprintf(stderr, "ERROR: Filename is too long: %s\n", aBuildPkgs[i].pszSrcPath);
|
---|
277 | break;
|
---|
278 | }
|
---|
279 |
|
---|
280 | /*
|
---|
281 | * Add the package header to the binary.
|
---|
282 | */
|
---|
283 | char szHeaderName[32];
|
---|
284 | hrc = StringCchPrintf(szHeaderName, sizeof(szHeaderName), "HDR_%02d", i);
|
---|
285 | if (FAILED(hrc))
|
---|
286 | {
|
---|
287 | fprintf(stderr, "Internal error: %u\n", __LINE__);
|
---|
288 | break;
|
---|
289 | }
|
---|
290 |
|
---|
291 | if (!UpdateResourceA(hUpdate, RT_RCDATA, szHeaderName, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
|
---|
292 | &Package, sizeof(Package)))
|
---|
293 | {
|
---|
294 | fprintf(stderr, "ERROR: UpdateResourceA failed for the package header: %lu\n", GetLastError());
|
---|
295 | hrc = E_FAIL;
|
---|
296 | break;
|
---|
297 | }
|
---|
298 |
|
---|
299 | /*
|
---|
300 | * Add the file content under the BIN_xx resource name.
|
---|
301 | */
|
---|
302 | hrc = IntegrateFile(hUpdate, RT_RCDATA, Package.szResourceName, aBuildPkgs[i].pszSrcPath);
|
---|
303 | if (FAILED(hrc))
|
---|
304 | break;
|
---|
305 | }
|
---|
306 | if (SUCCEEDED(hrc))
|
---|
307 | {
|
---|
308 | /*
|
---|
309 | * Now add the header/manifest and complete the operation.
|
---|
310 | */
|
---|
311 | if (UpdateResourceA(hUpdate, RT_RCDATA, "MANIFEST", MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
|
---|
312 | &StubHdr, sizeof(StubHdr)))
|
---|
313 | {
|
---|
314 | if (EndUpdateResourceA(hUpdate, FALSE /*fDiscard*/))
|
---|
315 | {
|
---|
316 | printf("Successfully created the installer\n");
|
---|
317 | return RTEXITCODE_SUCCESS;
|
---|
318 | }
|
---|
319 | fprintf(stderr, "ERROR: EndUpdateResourceA failed: %lu\n", GetLastError());
|
---|
320 | }
|
---|
321 | else
|
---|
322 | fprintf(stderr, "ERROR: UpdateResourceA failed for the installer header/manifest: %lu\n", GetLastError());
|
---|
323 | }
|
---|
324 |
|
---|
325 | EndUpdateResourceA(hUpdate, TRUE /*fDiscard*/);
|
---|
326 | hUpdate = NULL;
|
---|
327 | }
|
---|
328 | else
|
---|
329 | fprintf(stderr, "error: BeginUpdateResource failed: %lu\n", GetLastError());
|
---|
330 | return RTEXITCODE_FAILURE;
|
---|
331 | }
|
---|