VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/bootsectors/bs3kit/VBoxBs3Linker.cpp@ 101362

Last change on this file since 101362 was 99632, checked in by vboxsync, 22 months ago

VvalidationKit/VBoxBs3Linker: Fix harmless file leak, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.6 KB
Line 
1/* $Id: VBoxBs3Linker.cpp 99632 2023-05-05 12:49:31Z vboxsync $ */
2/** @file
3 * VirtualBox Validation Kit - Boot Sector 3 "linker".
4 */
5
6/*
7 * Copyright (C) 2006-2023 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 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <stdio.h>
42#include <string.h>
43#include <stdlib.h>
44#include <iprt/types.h>
45#include <iprt/assert.h>
46
47
48/*********************************************************************************************************************************
49* Structures and Typedefs *
50*********************************************************************************************************************************/
51#pragma pack(1)
52typedef struct BS3BOOTSECTOR
53{
54 uint8_t abJmp[3];
55 char abOemId[8];
56 /** @name EBPB, DOS 4.0 style.
57 * @{ */
58 uint16_t cBytesPerSector; /**< 00bh */
59 uint8_t cSectorsPerCluster; /**< 00dh */
60 uint16_t cReservedSectors; /**< 00eh */
61 uint8_t cFATs; /**< 010h */
62 uint16_t cRootDirEntries; /**< 011h */
63 uint16_t cTotalSectors; /**< 013h */
64 uint8_t bMediaDescriptor; /**< 015h */
65 uint16_t cSectorsPerFAT; /**< 016h */
66 uint16_t cPhysSectorsPerTrack; /**< 018h */
67 uint16_t cHeads; /**< 01ah */
68 uint32_t cHiddentSectors; /**< 01ch */
69 uint32_t cLargeTotalSectors; /**< 020h - We (ab)use this to indicate the number of sectors to load. */
70 uint8_t bBootDrv; /**< 024h */
71 uint8_t bFlagsEtc; /**< 025h */
72 uint8_t bExtendedSignature; /**< 026h */
73 uint32_t dwSerialNumber; /**< 027h */
74 char abLabel[11]; /**< 02bh */
75 char abFSType[8]; /**< 036h */
76 /** @} */
77} BS3BOOTSECTOR;
78#pragma pack()
79typedef BS3BOOTSECTOR *PBS3BOOTSECTOR;
80
81AssertCompileMemberOffset(BS3BOOTSECTOR, cLargeTotalSectors, 0x20);
82AssertCompileMemberOffset(BS3BOOTSECTOR, abLabel, 0x2b);
83AssertCompileMemberOffset(BS3BOOTSECTOR, abFSType, 0x36);
84
85#define BS3_OEMID "BS3Kit\n\n"
86#define BS3_FSTYPE "RawCode\n"
87#define BS3_LABEL "VirtualBox\n"
88#define BS3_MAX_SIZE UINT32_C(491520) /* 480KB */
89
90
91int main(int argc, char **argv)
92{
93 const char *pszOutput = NULL;
94 struct BS3LNKINPUT
95 {
96 const char *pszFile;
97 FILE *pFile;
98 uint32_t cbFile;
99 } *paInputs = (struct BS3LNKINPUT *)calloc(sizeof(paInputs[0]), argc);
100 unsigned cInputs = 0;
101 uint32_t cSectors = 0;
102
103 /*
104 * Scan the arguments.
105 */
106 for (int i = 1; i < argc; i++)
107 {
108 if (argv[i][0] == '-')
109 {
110 const char *pszOpt = &argv[i][1];
111 if (*pszOpt == '-')
112 {
113 /* Convert long options to short ones. */
114 pszOpt--;
115 if (!strcmp(pszOpt, "--output"))
116 pszOpt = "o";
117 else if (!strcmp(pszOpt, "--version"))
118 pszOpt = "V";
119 else if (!strcmp(pszOpt, "--help"))
120 pszOpt = "h";
121 else
122 {
123 fprintf(stderr, "syntax errro: Unknown options '%s'\n", pszOpt);
124 free(paInputs);
125 return 2;
126 }
127 }
128
129 /* Process the list of short options. */
130 while (*pszOpt)
131 {
132 switch (*pszOpt++)
133 {
134 case 'o':
135 {
136 const char *pszValue = pszOpt;
137 pszOpt = strchr(pszOpt, '\0');
138 if (*pszValue == '=')
139 pszValue++;
140 else if (!*pszValue)
141 {
142 if (i + 1 >= argc)
143 {
144 fprintf(stderr, "syntax error: The --output option expects a filename.\n");
145 free(paInputs);
146 return 12;
147 }
148 pszValue = argv[++i];
149 }
150 if (pszOutput)
151 {
152 fprintf(stderr, "Only one output file is allowed. You've specified '%s' and '%s'\n",
153 pszOutput, pszValue);
154 free(paInputs);
155 return 2;
156 }
157 pszOutput = pszValue;
158 pszOpt = "";
159 break;
160 }
161
162 case 'V':
163 printf("%s\n", "$Revision: 99632 $");
164 free(paInputs);
165 return 0;
166
167 case '?':
168 case 'h':
169 printf("usage: %s [options] -o <output> <input1> [input2 ... [inputN]]\n",
170 argv[0]);
171 free(paInputs);
172 return 0;
173 }
174 }
175 }
176 else
177 {
178 /*
179 * Add to input file collection.
180 */
181 paInputs[cInputs].pszFile = argv[i];
182#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
183 FILE *pFile = fopen(paInputs[cInputs].pszFile, "rb");
184#else
185 FILE *pFile = fopen(paInputs[cInputs].pszFile, "r");
186#endif
187 if (pFile)
188 {
189 if (fseek(pFile, 0, SEEK_END) == 0)
190 {
191 paInputs[cInputs].cbFile = (uint32_t)ftell(pFile);
192 if (fseek(pFile, 0, SEEK_SET) == 0)
193 {
194 if (cInputs != 0 || paInputs[cInputs].cbFile == 512)
195 {
196 cSectors += RT_ALIGN_32(paInputs[cInputs].cbFile, 512) / 512;
197 if (cSectors <= BS3_MAX_SIZE / 512)
198 {
199 if (cSectors > 0)
200 {
201 paInputs[cInputs].pFile = pFile;
202 pFile = NULL;
203 }
204 else
205 fprintf(stderr, "error: empty input file: '%s'\n", paInputs[cInputs].pszFile);
206 }
207 else
208 fprintf(stderr, "error: input is too big: %u bytes, %u sectors (max %u bytes, %u sectors)\n"
209 "info: detected loading '%s'\n",
210 cSectors * 512, cSectors, BS3_MAX_SIZE, BS3_MAX_SIZE / 512,
211 paInputs[cInputs].pszFile);
212 }
213 else
214 fprintf(stderr, "error: first input file (%s) must be exactly 512 bytes\n", paInputs[cInputs].pszFile);
215 }
216 else
217 fprintf(stderr, "error: seeking to start of '%s' failed\n", paInputs[cInputs].pszFile);
218 }
219 else
220 fprintf(stderr, "error: seeking to end of '%s' failed\n", paInputs[cInputs].pszFile);
221 }
222 else
223 fprintf(stderr, "error: Failed to open input file '%s' for reading\n", paInputs[cInputs].pszFile);
224 if (pFile)
225 {
226 free(paInputs);
227 fclose(pFile);
228 return 1;
229 }
230 cInputs++;
231 }
232 }
233
234 if (!pszOutput)
235 {
236 fprintf(stderr, "syntax error: No output file was specified (-o or --output).\n");
237 free(paInputs);
238 return 2;
239 }
240 if (cInputs == 0)
241 {
242 fprintf(stderr, "syntax error: No input files was specified.\n");
243 free(paInputs);
244 return 2;
245 }
246
247 /*
248 * Do the job.
249 */
250 /* Open the output file. */
251#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
252 FILE *pOutput = fopen(pszOutput, "wb");
253#else
254 FILE *pOutput = fopen(pszOutput, "w");
255#endif
256 if (!pOutput)
257 {
258 fprintf(stderr, "error: Failed to open output file '%s' for writing\n", pszOutput);
259 free(paInputs);
260 return 1;
261 }
262
263 /* Copy the input files to the output file, with sector padding applied. */
264 int rcExit = 0;
265 size_t off = 0;
266 for (unsigned i = 0; i < cInputs && rcExit == 0; i++)
267 {
268 uint8_t abBuf[4096]; /* Must be multiple of 512! */
269 uint32_t cbToRead = paInputs[i].cbFile;
270 while (cbToRead > 0)
271 {
272 /* Read a block from the input file. */
273 uint32_t const cbThisRead = RT_MIN(cbToRead, sizeof(abBuf));
274 size_t cbRead = fread(abBuf, sizeof(uint8_t), cbThisRead, paInputs[i].pFile);
275 if (cbRead != cbThisRead)
276 {
277 fprintf(stderr, "error: Error reading '%s' (got %d bytes, wanted %u).\n",
278 paInputs[i].pszFile, (int)cbRead, (unsigned)cbThisRead);
279 rcExit = 1;
280 break;
281 }
282 cbToRead -= cbThisRead;
283
284 /* Padd the end of the file if necessary. */
285 if ((cbRead & 0x1ff) != 0)
286 {
287 memset(&abBuf[cbRead], 0, 4096 - cbRead);
288 cbRead = (cbRead + 0x1ff) & ~0x1ffU;
289 }
290
291 /* Patch the BPB of the first file. */
292 if (off == 0)
293 {
294 PBS3BOOTSECTOR pBs = (PBS3BOOTSECTOR)&abBuf[0];
295 if ( memcmp(pBs->abLabel, RT_STR_TUPLE(BS3_LABEL)) == 0
296 && memcmp(pBs->abFSType, RT_STR_TUPLE(BS3_FSTYPE)) == 0
297 && memcmp(pBs->abOemId, RT_STR_TUPLE(BS3_OEMID)) == 0)
298 pBs->cLargeTotalSectors = cSectors;
299 else
300 {
301 fprintf(stderr, "error: Didn't find magic strings in the first file (%s).\n", paInputs[i].pszFile);
302 rcExit = 1;
303 }
304 }
305
306 /* Write the block to the output file. */
307 if (fwrite(abBuf, sizeof(uint8_t), cbRead, pOutput) == cbRead)
308 off += cbRead;
309 else
310 {
311 fprintf(stderr, "error: fwrite failed\n");
312 rcExit = 1;
313 break;
314 }
315 }
316
317 if (ferror(paInputs[i].pFile))
318 {
319 fprintf(stderr, "error: Error reading '%s'.\n", paInputs[i].pszFile);
320 rcExit = 1;
321 }
322 }
323
324 /* Close the input files. */
325 for (unsigned i = 0; i < cInputs && rcExit == 0; i++)
326 fclose(paInputs[i].pFile);
327 free(paInputs);
328
329 /* Avoid output sizes that makes the FDC code think it's a single sided
330 floppy. The BIOS always report double sided floppies, and even if we
331 the bootsector adjust it's bMaxHeads value when getting a 20h error
332 we end up with a garbaged image (seems somewhere in the BIOS/FDC it is
333 still treated as a double sided floppy and we get half the data we want
334 and with gaps).
335
336 Similarly, if the size is 320KB or 360KB the FDC detects it as a double
337 sided 5.25" floppy with 40 tracks, while the BIOS keeps reporting a
338 1.44MB 3.5" floppy. So, just avoid those sizes too. */
339 uint32_t cbOutput = ftell(pOutput);
340 if ( cbOutput == 512 * 8 * 40 * 1 /* 160kB 5"1/4 SS */
341 || cbOutput == 512 * 9 * 40 * 1 /* 180kB 5"1/4 SS */
342 || cbOutput == 512 * 8 * 40 * 2 /* 320kB 5"1/4 DS */
343 || cbOutput == 512 * 9 * 40 * 2 /* 360kB 5"1/4 DS */ )
344 {
345 static uint8_t const s_abZeroSector[512] = { 0 };
346 if (fwrite(s_abZeroSector, sizeof(uint8_t), sizeof(s_abZeroSector), pOutput) != sizeof(s_abZeroSector))
347 {
348 fprintf(stderr, "error: fwrite failed (padding)\n");
349 rcExit = 1;
350 }
351 }
352
353 /* Finally, close the output file (can fail because of buffered data). */
354 if (fclose(pOutput) != 0)
355 {
356 fprintf(stderr, "error: Error closing '%s'.\n", pszOutput);
357 rcExit = 1;
358 }
359
360 fclose(stderr);
361 return rcExit;
362}
363
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