VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/UnattendedOs2Installer.cpp

Last change on this file was 99775, checked in by vboxsync, 12 months ago

*: Mark functions as static if not used outside of a given compilation unit. Enables the compiler to optimize inlining, reduces the symbol tables, exposes unused functions and in some rare cases exposes mismtaches between function declarations and definitions, but most importantly reduces the number of parfait reports for the extern-function-no-forward-declaration category. This should not result in any functional changes, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 58.4 KB
Line 
1/* $Id: UnattendedOs2Installer.cpp 99775 2023-05-12 12:21:58Z vboxsync $ */
2/** @file
3 * UnattendedOs2Installer implementation.
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 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_MAIN_UNATTENDED
33#include "LoggingNew.h"
34#include "VirtualBoxBase.h"
35#include "VirtualBoxErrorInfoImpl.h"
36#include "AutoCaller.h"
37#include <VBox/com/ErrorInfo.h>
38
39#include "UnattendedImpl.h"
40#include "UnattendedInstaller.h"
41#include "UnattendedScript.h"
42
43#include <VBox/err.h>
44#include <iprt/ctype.h>
45#include <iprt/fsisomaker.h>
46#include <iprt/fsvfs.h>
47#include <iprt/file.h>
48#include <iprt/path.h>
49#include <iprt/stream.h>
50#include <iprt/vfs.h>
51#ifdef RT_OS_SOLARIS
52# undef ES /* Workaround for someone dragging the namespace pollutor sys/regset.h. Sigh. */
53#endif
54#include <iprt/formats/fat.h>
55#include <iprt/cpp/path.h>
56
57
58using namespace std;
59
60
61
62UnattendedOs2Installer::UnattendedOs2Installer(Unattended *pParent, Utf8Str const &rStrHints)
63 : UnattendedInstaller(pParent,
64 "os2_response_files.rsp", "os2_cid_install.cmd",
65 "os2_response_files.rsp", "VBOXCID.CMD",
66 DeviceType_Floppy)
67{
68 Assert(!isOriginalIsoNeeded());
69 Assert(isAuxiliaryFloppyNeeded());
70 Assert(isAuxiliaryIsoIsVISO());
71 Assert(bootFromAuxiliaryIso());
72 mStrAuxiliaryInstallDir = "S:\\";
73
74 /* Extract the info from the hints variable: */
75 RTCList<RTCString, RTCString *> Pairs = rStrHints.split(" ");
76 size_t i = Pairs.size();
77 Assert(i > 0);
78 while (i -- > 0)
79 {
80 RTCString const rStr = Pairs[i];
81 if (rStr.startsWith("OS2SE20.SRC="))
82 mStrOs2Images = rStr.substr(sizeof("OS2SE20.SRC=") - 1);
83 else
84 AssertMsgFailed(("Unknown hint: %s\n", rStr.c_str()));
85 }
86}
87
88
89HRESULT UnattendedOs2Installer::replaceAuxFloppyImageBootSector(RTVFSFILE hVfsFile) RT_NOEXCEPT
90{
91 /*
92 * Find the bootsector. Because the ArcaOS ISOs doesn't contain any floppy
93 * images, we cannot just lift it off one of those. Instead we'll locate it
94 * in the SYSINSTX.COM utility, i.e. the tool which installs it onto floppies
95 * and harddisks. The SYSINSTX.COM utility is a NE executable and we don't
96 * have issues with compressed pages like with LX images.
97 *
98 * The utility seems always to be located on disk 0.
99 */
100 RTVFS hVfsOrgIso;
101 HRESULT hrc = openInstallIsoImage(&hVfsOrgIso);
102 if (SUCCEEDED(hrc))
103 {
104 char szPath[256];
105 int vrc = RTPathJoin(szPath, sizeof(szPath), mStrOs2Images.c_str(), "DISK_0/SYSINSTX.COM");
106 if (RT_SUCCESS(vrc))
107 {
108 RTVFSFILE hVfsSysInstX;
109 vrc = RTVfsFileOpen(hVfsOrgIso, szPath, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, &hVfsSysInstX);
110 if (RT_SUCCESS(vrc))
111 {
112 /*
113 * Scan the image looking for a 512 block ending with a DOS signature
114 * and starting with a three byte jump followed by an OEM name string.
115 */
116 uint8_t *pbBootSector = NULL;
117 RTFOFF off = 0;
118 bool fEof = false;
119 uint8_t abBuf[_8K] = {0};
120 do
121 {
122 /* Read the next chunk. */
123 memmove(abBuf, &abBuf[sizeof(abBuf) - 512], 512); /* Move up the last 512 (all zero the first time around). */
124 size_t cbRead = 0;
125 vrc = RTVfsFileReadAt(hVfsSysInstX, off, &abBuf[512], sizeof(abBuf) - 512, &cbRead);
126 if (RT_FAILURE(vrc))
127 break;
128 fEof = cbRead != sizeof(abBuf) - 512;
129 off += cbRead;
130
131 /* Scan it. */
132 size_t cbLeft = sizeof(abBuf);
133 uint8_t *pbCur = abBuf;
134 while (cbLeft >= 512)
135 {
136 /* Look for the DOS signature (0x55 0xaa) at the end of the sector: */
137 uint8_t *pbHit = (uint8_t *)memchr(pbCur + 510, 0x55, cbLeft - 510 - 1);
138 if (!pbHit)
139 break;
140 if (pbHit[1] == 0xaa)
141 {
142 uint8_t *pbStart = pbHit - 510;
143 if ( pbStart[0] == 0xeb /* JMP imm8 */
144 && pbStart[1] >= 3 + 8 + sizeof(FATEBPB) - 2 /* must jump after the FATEBPB */
145 && RT_C_IS_ALNUM(pbStart[3]) /* ASSUME OEM string starts with two letters (typically 'IBM x.y')*/
146 && RT_C_IS_ALNUM(pbStart[4]))
147 {
148 FATEBPB *pBpb = (FATEBPB *)&pbStart[3 + 8];
149 if ( pBpb->bExtSignature == FATEBPB_SIGNATURE
150 && ( memcmp(pBpb->achType, "FAT ", sizeof(pBpb->achType)) == 0
151 || memcmp(pBpb->achType, FATEBPB_TYPE_FAT12, sizeof(pBpb->achType)) == 0))
152 {
153 pbBootSector = pbStart;
154 break;
155 }
156 }
157 }
158
159 /* skip */
160 pbCur = pbHit - 510 + 1;
161 cbLeft = (uintptr_t)&abBuf[sizeof(abBuf)] - (uintptr_t)pbCur;
162 }
163 } while (!fEof);
164
165 if (pbBootSector)
166 {
167 if (pbBootSector != abBuf)
168 pbBootSector = (uint8_t *)memmove(abBuf, pbBootSector, 512);
169
170 /*
171 * We've now got a bootsector. So, we need to copy the EBPB
172 * from the destination image before replacing it.
173 */
174 vrc = RTVfsFileReadAt(hVfsFile, 0, &abBuf[512], 512, NULL);
175 if (RT_SUCCESS(vrc))
176 {
177 memcpy(&pbBootSector[3 + 8], &abBuf[512 + 3 + 8], sizeof(FATEBPB));
178
179 /*
180 * Write it.
181 */
182 vrc = RTVfsFileWriteAt(hVfsFile, 0, pbBootSector, 512, NULL);
183 if (RT_SUCCESS(vrc))
184 {
185 LogFlowFunc(("Successfully installed new bootsector\n"));
186 hrc = S_OK;
187 }
188 else
189 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to write bootsector: %Rrc"), vrc);
190 }
191 else
192 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to read bootsector: %Rrc"), vrc);
193 }
194 else if (RT_FAILURE(vrc))
195 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error reading SYSINSTX.COM: %Rrc"), vrc);
196 else
197 hrc = mpParent->setErrorBoth(E_FAIL, VERR_NOT_FOUND,
198 tr("Unable to locate bootsector template in SYSINSTX.COM"));
199 RTVfsFileRelease(hVfsSysInstX);
200 }
201 else
202 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to open SYSINSTX.COM: %Rrc"), vrc);
203 }
204 else
205 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to construct SYSINSTX.COM path"));
206 RTVfsRelease(hVfsOrgIso);
207 }
208 return hrc;
209
210}
211
212HRESULT UnattendedOs2Installer::newAuxFloppyImage(const char *pszFilename, bool fOverwrite, PRTVFSFILE phVfsFile)
213{
214 /*
215 * Open the image file.
216 */
217 HRESULT hrc;
218 RTVFSFILE hVfsFile;
219 uint64_t fOpen = RTFILE_O_READWRITE | RTFILE_O_DENY_ALL | (0660 << RTFILE_O_CREATE_MODE_SHIFT);
220 if (fOverwrite)
221 fOpen |= RTFILE_O_CREATE_REPLACE;
222 else
223 fOpen |= RTFILE_O_OPEN;
224 int vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
225 if (RT_SUCCESS(vrc))
226 {
227 /*
228 * Format it.
229 */
230 vrc = RTFsFatVolFormat288(hVfsFile, false /*fQuick*/);
231 if (RT_SUCCESS(vrc))
232 {
233 /*
234 * Now we install the OS/2 boot sector on it.
235 */
236 hrc = replaceAuxFloppyImageBootSector(hVfsFile);
237 if (SUCCEEDED(hrc))
238 {
239 *phVfsFile = hVfsFile;
240 LogRelFlow(("UnattendedInstaller::newAuxFloppyImage: created and formatted '%s'\n", pszFilename));
241 return S_OK;
242 }
243 }
244 else
245 hrc = mpParent->setErrorBoth(E_FAIL, vrc, tr("Failed to format floppy image '%s': %Rrc"), pszFilename, vrc);
246 RTVfsFileRelease(hVfsFile);
247 RTFileDelete(pszFilename);
248 }
249 else
250 hrc = mpParent->setErrorBoth(E_FAIL, vrc, tr("Failed to create floppy image '%s': %Rrc"), pszFilename, vrc);
251 return hrc;
252}
253
254
255HRESULT UnattendedOs2Installer::splitResponseFile() RT_NOEXCEPT
256{
257 if (mVecSplitFiles.size() == 0)
258 {
259#if 0
260 Utf8Str strResponseFile;
261 int vrc = strResponseFile.assignNoThrow(mpParent->i_getAuxiliaryBasePath());
262 if (RT_SUCCESS(vrc))
263 vrc = strResponseFile.appendNoThrow(mMainScript.getDefaultFilename());
264 if (RT_SUCCESS(vrc))
265 return splitFile(strResponseFile.c_str(), mVecSplitFiles);
266 return mpParent->setErrorVrc(vrc);
267#else
268 return splitFile(&mMainScript, mVecSplitFiles);
269#endif
270 }
271 return S_OK;
272}
273
274/**
275 * OS/2 code pattern.
276 */
277typedef struct OS2CODEPATTERN
278{
279 /** The code pattern. */
280 uint8_t const *pbPattern;
281 /** The mask to apply when using the pattern (ignore 0x00 bytes, compare 0xff
282 * bytes). */
283 uint8_t const *pbMask;
284 /** The pattern size. */
285 size_t cb;
286 /** User info \#1. */
287 uintptr_t uUser1;
288 /** User info \#2. */
289 uint32_t uUser2;
290 /** User info \#3. */
291 uint32_t uUser3;
292 /** User info \#4. */
293 uint32_t uUser4;
294 /** User info \#5. */
295 uint32_t uUser5;
296} OS2CODEPATTERN;
297/** Pointer to an OS/2 code pattern. */
298typedef OS2CODEPATTERN const *PCOS2CODEPATTERN;
299
300
301/**
302 * Search @a pbCode for the code patterns in @a paPatterns.
303 *
304 * @returns pointer within @a pbCode to matching code, NULL if no match.
305 */
306static uint8_t *findCodePattern(PCOS2CODEPATTERN paPatterns, size_t cPatterns, uint8_t *pbCode, size_t cbCode,
307 PCOS2CODEPATTERN *ppMatch)
308{
309 for (size_t i = 0; i < cPatterns; i++)
310 {
311 size_t const cbPattern = paPatterns[i].cb;
312 uint8_t const *pbPattern = paPatterns[i].pbPattern;
313 uint8_t const *pbMask = paPatterns[i].pbMask;
314 Assert(pbMask[0] == 0xff); /* ASSUME the we can use the first byte with memchr. */
315 uint8_t const bFirst = *pbPattern;
316 size_t off = 0;
317 while (off + cbPattern <= cbCode)
318 {
319 uint8_t *pbHit = (uint8_t *)memchr(&pbCode[off], bFirst, cbCode - off - cbPattern + 1);
320 if (!pbHit)
321 break;
322
323 size_t offPattern = 1;
324 while ( offPattern < cbPattern
325 && (pbPattern[offPattern] & pbMask[offPattern]) == (pbHit[offPattern] & pbMask[offPattern]))
326 offPattern++;
327 if (offPattern == cbPattern)
328 {
329 *ppMatch = &paPatterns[i];
330 return pbHit;
331 }
332
333 /* next */
334 off++;
335 }
336 }
337 return NULL;
338}
339
340#if 0
341/**
342 * Patcher callback for TESTCFG.SYS.
343 *
344 * This is for debugging a mysterious DS corruption issue happening on an AMD
345 * 3990x host.
346 *
347 * @verbatim
348dbgf event/0: xcpt_gp - #GP (general protection fault)! arg=0x1d8
349VBoxDbg> r
350eax=00000001 ebx=00dc0000 ecx=56d80000 edx=178b0000 esi=ffde0100 edi=feff44e4
351eip=00000124 esp=00000f76 ebp=0000dbf3 iopl=3 nv up ei pl nz na po nc
352cs=0763 ds=01db es=0130 fs=0000 gs=0000 ss=001f eflags=00003206
3530763:00000124 cb retf
354VBoxDbg> dw ss:sp
355001f:00000f76: 0549 075b 03e4 0000-0fb8 04b9 44e4 0130
356VBoxDbg> u cs:fc
3570763:000000fc 55 push bp
3580763:000000fd 8b ec mov bp, sp
3590763:000000ff 53 push bx
3600763:00000100 51 push cx
3610763:00000101 52 push dx
3620763:00000102 1e push DS
3630763:00000103 33 c9 xor cx, cx
3640763:00000105 b0 10 mov AL, 010h
3650763:00000107 b2 24 mov DL, 024h
3660763:00000109 ff 1e 22 00 call far [00022h]
3670763:0000010d 72 0e jc +00eh (0011dh)
3680763:0000010f 50 push ax
3690763:00000110 1f pop DS
3700763:00000111 f7 47 06 03 00 test word [bx+006h], 00003h
3710763:00000116 74 05 je +005h (0011dh)
3720763:00000118 b8 01 00 mov ax, 00001h
3730763:0000011b eb 02 jmp +002h (0011fh)
3740763:0000011d 33 c0 xor ax, ax
3750763:0000011f 1f pop DS
3760763:00000120 5a pop dx
3770763:00000121 59 pop cx
3780763:00000122 5b pop bx
3790763:00000123 5d pop bp
3800763:00000124 cb retf
381VBoxDbg> dw ss:sp - 5*2 L8
382001f:00000f6c: 0750 082a 220e 44e4-0f7e 0549 075b 03e4
383 * @endverbatim
384 *
385 * We end up with a \#GP on the RETF, but the stack frame is a valid 075b:0549
386 * return address (in TESTCFG's first code segment). The error code is 0x1d8,
387 * which makes no sense. DS contains 0x1db, which could be related, however it
388 * is the *wrong* value as seen by the stack restore frame above, it was just
389 * restored as 0750 (TESTCFG data segment).
390 *
391 * The patching here aim at modifying to code to try figure out what might
392 * trigger the bogus DS and \#GP(0x1d8).
393 *
394 * P.S. There are no exits or event injections taking place when DS gets
395 * corrupt, the last exit was a CR0 read in OS2KRNL's DOSSEG (0120:1798)
396 * probably related to we comming back to protected mode from real mode as we
397 * just made an APM BIOS call.
398 *
399 * Update: The values loaded off the stack aren't the ones ending up the
400 * registers, so that might explain why this goes south.
401 *
402 * @sa ticketref:20625
403 */
404/*static*/
405int UnattendedOs2Installer::patchTestCfg(uint8_t *pbFile, size_t cbFile, const char *pszFilename, UnattendedOs2Installer *pThis)
406{
407 RT_NOREF(pThis, pszFilename);
408
409 static uint8_t const s_abVariant1[] =
410 {
411 /*0763:00fc*/ 0x55, /* push bp */
412 /*0763:00fd*/ 0x8b, 0xec, /* mov bp, sp */
413 /*0763:00ff*/ 0x53, /* push bx */
414 /*0763:0100*/ 0x51, /* push cx */
415 /*0763:0101*/ 0x52, /* push dx */
416 /*0763:0102*/ 0x1e, /* push DS */
417 /*0763:0103*/ 0x33, 0xc9, /* xor cx, cx */
418 /*0763:0105*/ 0xb0, 0x10, /* mov AL, 010h */
419 /*0763:0107*/ 0xb2, 0x24, /* mov DL, 024h */
420 /*0763:0109*/ 0xff, 0x1e, 0x22, 0x00, /* call far [00022h] */
421 /*0763:010d*/ 0x72, 0x0e, /* jc +00eh (0011dh) */
422 /*0763:010f*/ 0x50, /* push ax */
423 /*0763:0110*/ 0x1f, /* pop DS */
424 /*0763:0111*/ 0xf7, 0x47, 0x06, 0x03, 0x00, /* test word [bx+006h], 00003h */
425 /*0763:0116*/ 0x74, 0x05, /* je +005h (0011dh) */
426 /*0763:0118*/ 0xb8, 0x01, 0x00, /* mov ax, 00001h */
427 /*0763:011b*/ 0xeb, 0x02, /* jmp +002h (0011fh) */
428 /*0763:011d*/ 0x33, 0xc0, /* xor ax, ax */
429 /*0763:011f*/ 0x1f, /* pop DS */
430 /*0763:0120*/ 0x5a, /* pop dx */
431 /*0763:0121*/ 0x59, /* pop cx */
432 /*0763:0122*/ 0x5b, /* pop bx */
433 /*0763:0123*/ 0x5d, /* pop bp */
434 /*0763:0124*/ 0xcb, /* retf */
435 };
436 static uint8_t const s_abVariant1Mask[] =
437 {
438 /*0763:00fc*/ 0xff, /* push bp */
439 /*0763:00fd*/ 0xff, 0xec, /* mov bp, sp */
440 /*0763:00ff*/ 0xff, /* push bx */
441 /*0763:0100*/ 0xff, /* push cx */
442 /*0763:0101*/ 0xff, /* push dx */
443 /*0763:0102*/ 0xff, /* push DS */
444 /*0763:0103*/ 0xff, 0xff, /* xor cx, cx */
445 /*0763:0105*/ 0xff, 0xff, /* mov AL, 010h */
446 /*0763:0107*/ 0xff, 0xff, /* mov DL, 024h */
447 /*0763:0109*/ 0xff, 0xff, 0x00, 0x00, /* call far [00022h] */
448 /*0763:010d*/ 0xff, 0xff, /* jc +00eh (0011dh) */
449 /*0763:010f*/ 0xff, /* push ax */
450 /*0763:0110*/ 0xff, /* pop DS */
451 /*0763:0111*/ 0xff, 0xff, 0xff, 0xff, 0xff, /* test word [bx+006h], 00003h */
452 /*0763:0116*/ 0xff, 0xff, /* je +005h (0011dh) */
453 /*0763:0118*/ 0xff, 0xff, 0xff, /* mov ax, 00001h */
454 /*0763:011b*/ 0xff, 0xff, /* jmp +002h (0011fh) */
455 /*0763:011d*/ 0xff, 0xff, /* xor ax, ax */
456 /*0763:011f*/ 0xff, /* pop DS */
457 /*0763:0120*/ 0xff, /* pop dx */
458 /*0763:0121*/ 0xff, /* pop cx */
459 /*0763:0122*/ 0xff, /* pop bx */
460 /*0763:0123*/ 0xff, /* pop bp */
461 /*0763:0124*/ 0xff, /* retf */
462 };
463 AssertCompile(sizeof(s_abVariant1Mask) == sizeof(s_abVariant1));
464
465 /* uUser1 = off to start modifying the code; */
466 static const OS2CODEPATTERN s_aPatterns[] =
467 {
468 { s_abVariant1, s_abVariant1Mask, sizeof(s_abVariant1Mask), 0x010d - 0x00fc, 0, 0, 0, 0 },
469 };
470
471 PCOS2CODEPATTERN pPattern;
472 uint8_t *pbHit = findCodePattern(&s_aPatterns[0], RT_ELEMENTS(s_aPatterns), pbFile, cbFile, &pPattern);
473 if (pPattern)
474 {
475 /* We've got */
476 uint8_t *pbPatch = &pbHit[pPattern->uUser1];
477#if 0 /* this seems to fix the issue */
478 *pbPatch++ = 0xe6; /* out 78h, al - triggers an exit */
479 *pbPatch++ = 0x78;
480#elif 0 /* this seems to fix it too */
481 *pbPatch++ = 0xf3; /* pause */
482 *pbPatch++ = 0x90;
483#elif 0 /* still reproducible with normal nops. */
484 *pbPatch++ = 0x90;
485 *pbPatch++ = 0x90;
486#else
487# if 0
488 /*0763:010d*/ 0x72, 0x0e, /* jc +00eh (0011dh) */
489 /*0763:010f*/ 0x50, /* push ax */
490 /*0763:0110*/ 0x1f, /* pop DS */
491 /*0763:0111*/ 0xf7, 0x47, 0x06, 0x03, 0x00, /* test word [bx+006h], 00003h */
492 /*0763:0116*/ 0x74, 0x05, /* je +005h (0011dh) */
493 /*0763:0118*/ 0xb8, 0x01, 0x00, /* mov ax, 00001h */
494 /*0763:011b*/ 0xeb, 0x02, /* jmp +002h (0011fh) */
495 /*0763:011d*/ 0x33, 0xc0, /* xor ax, ax */
496 /*0763:011f*/ 0x1f, /* pop DS */
497 /*0763:0120*/ 0x5a, /* pop dx */
498 /*0763:0121*/ 0x59, /* pop cx */
499 /*0763:0122*/ 0x5b, /* pop bx */
500 /*0763:0123*/ 0x5d, /* pop bp */
501 /*0763:0124*/ 0xcb, /* retf */
502# endif
503 /* Try straigthen out the code and mabye load DS into AX (we don't care about the return value) */
504 *pbPatch++ = 0x50; /* push ax */
505 *pbPatch++ = 0x1f; /* pop DS */
506
507 *pbPatch++ = 0xf7; /* test word [bx+006h], 00003h */
508 *pbPatch++ = 0x47;
509 *pbPatch++ = 0x06;
510 *pbPatch++ = 0x03;
511 *pbPatch++ = 0x00;
512 /* not je */
513 *pbPatch++ = 0xb8; /* mov ax, 00001h */
514 *pbPatch++ = 0x01;
515 *pbPatch++ = 0x00;
516
517# if 0 /* try reload SS */
518 *pbPatch++ = 0x8c; /* mov ax, ss */
519 *pbPatch++ = 0xd0;
520 *pbPatch++ = 0x8e; /* mov ss, ax */
521 *pbPatch++ = 0xd0;
522# endif
523# if 0 /* try reload CR3 to flush everything - not possible, we're in ring-3 */
524 *pbPatch++ = 0x0f; /* mov eax, cr3 */
525 *pbPatch++ = 0x20;
526 *pbPatch++ = 0xd8;
527 *pbPatch++ = 0x0f; /* mov cr3, eax */
528 *pbPatch++ = 0x22;
529 *pbPatch++ = 0xd8;
530# endif
531
532 *pbPatch++ = 0x1f; /* pop DS */
533# if 0
534 *pbPatch++ = 0x8c; /* mov ax, ds */
535 *pbPatch++ = 0xd8;
536# endif
537 *pbPatch++ = 0x5a; /* pop dx */
538 *pbPatch++ = 0x59; /* pop cx */
539 *pbPatch++ = 0x5b; /* pop bx */
540 *pbPatch++ = 0x5d; /* pop bp */
541 *pbPatch++ = 0xcb; /* retf */
542
543#endif
544 }
545 else
546 {
547 LogRelFunc(("No patch pattern match!\n"));
548 return VERR_NOT_FOUND;
549 }
550
551 return VINF_SUCCESS;
552}
553#endif
554
555
556/**
557 * Patcher callback for OS2LDR.
558 *
559 * There are one or two delay calibration loops here that doesn't work well on
560 * fast CPUs. Typically ends up with division by chainsaw, which in a BIOS
561 * context means an unending loop as the BIOS \#DE handler doesn't do much.
562 *
563 * The patching is simplictic, in that it just returns a constant value. We
564 * could rewrite this to use RDTSC and some secret MSR/whatever for converting
565 * that to a decent loop count.
566 */
567/*static*/
568int UnattendedOs2Installer::patchOs2Ldr(uint8_t *pbFile, size_t cbFile, const char *pszFilename, UnattendedOs2Installer *pThis)
569{
570 RT_NOREF(pThis, pszFilename);
571
572 /*
573 * This first variant is from ACP2:
574 *
575 * VBoxDbg> r
576 * eax=00001000 ebx=00010000 ecx=56d8ffd5 edx=178b0000 esi=00000000 edi=0000b750
577 * eip=0000847a esp=0000cfe8 ebp=00000000 iopl=0 nv up ei pl zr na po nc
578 * cs=2000 ds=2000 es=2000 fs=0000 gs=0000 ss=2000 eflags=00000246
579 * 2000:0000847a f7 fb idiv bx
580 * VBoxDbg> ucfg 2000:840a
581 *
582 * This is a little annoying because it stores the result in a global variable,
583 * so we cannot just do an early return, instead we have to have to jump to the
584 * end of the function so it can be stored correctly.
585 */
586 static uint8_t const s_abVariant1[] =
587 {
588 /*2000:840a*/ 0x60, /* pushaw */
589 /*2000:840b*/ 0x1e, /* push DS */
590 /*2000:840c*/ 0x0e, /* push CS */
591 /*2000:840d*/ 0x1f, /* pop DS */
592 /*2000:840e*/ 0x9c, /* pushfw */
593 /*2000:840f*/ 0xfa, /* cli */
594 /*2000:8410*/ 0xb0, 0x34, /* mov AL, 034h */
595 /*2000:8412*/ 0xe6, 0x43, /* out 043h, AL */
596 /*2000:8414*/ 0xe8, 0x75, 0xfc, /* call 0808ch */
597 /*2000:8417*/ 0x32, 0xc0, /* xor al, al */
598 /*2000:8419*/ 0xe6, 0x40, /* out 040h, AL */
599 /*2000:841b*/ 0xe8, 0x6e, 0xfc, /* call 0808ch */
600 /*2000:841e*/ 0xe6, 0x40, /* out 040h, AL */
601 /*2000:8420*/ 0xe8, 0x69, 0xfc, /* call 0808ch */
602 /*2000:8423*/ 0xb0, 0x00, /* mov AL, 000h */
603 /*2000:8425*/ 0xe6, 0x43, /* out 043h, AL */
604 /*2000:8427*/ 0xe8, 0x62, 0xfc, /* call 0808ch */
605 /*2000:842a*/ 0xe4, 0x40, /* in AL, 040h */
606 /*2000:842c*/ 0xe8, 0x5d, 0xfc, /* call 0808ch */
607 /*2000:842f*/ 0x8a, 0xd8, /* mov bl, al */
608 /*2000:8431*/ 0xe4, 0x40, /* in AL, 040h */
609 /*2000:8433*/ 0x8a, 0xf8, /* mov bh, al */
610 /*2000:8435*/ 0xb0, 0x00, /* mov AL, 000h */
611 /*2000:8437*/ 0xe6, 0x43, /* out 043h, AL */
612 /*2000:8439*/ 0xe8, 0x50, 0xfc, /* call 0808ch */
613 /*2000:843c*/ 0xe4, 0x40, /* in AL, 040h */
614 /*2000:843e*/ 0xe8, 0x4b, 0xfc, /* call 0808ch */
615 /*2000:8441*/ 0x8a, 0xc8, /* mov cl, al */
616 /*2000:8443*/ 0xe4, 0x40, /* in AL, 040h */
617 /*2000:8445*/ 0x8a, 0xe8, /* mov ch, al */
618 /*2000:8447*/ 0xbe, 0x00, 0x10, /* mov si, 01000h */
619 /*2000:844a*/ 0x87, 0xdb, /* xchg bx, bx */
620 /*2000:844c*/ 0x4e, /* dec si */
621 /*2000:844d*/ 0x75, 0xfd, /* jne -003h (0844ch) */
622 /*2000:844f*/ 0xb0, 0x00, /* mov AL, 000h */
623 /*2000:8451*/ 0xe6, 0x43, /* out 043h, AL */
624 /*2000:8453*/ 0xe8, 0x36, 0xfc, /* call 0808ch */
625 /*2000:8456*/ 0xe4, 0x40, /* in AL, 040h */
626 /*2000:8458*/ 0xe8, 0x31, 0xfc, /* call 0808ch */
627 /*2000:845b*/ 0x8a, 0xd0, /* mov dl, al */
628 /*2000:845d*/ 0xe4, 0x40, /* in AL, 040h */
629 /*2000:845f*/ 0x8a, 0xf0, /* mov dh, al */
630 /*2000:8461*/ 0x9d, /* popfw */
631 /*2000:8462*/ 0x2b, 0xd9, /* sub bx, cx */
632 /*2000:8464*/ 0x2b, 0xca, /* sub cx, dx */
633 /*2000:8466*/ 0x2b, 0xcb, /* sub cx, bx */
634 /*2000:8468*/ 0x87, 0xca, /* xchg dx, cx */
635 /*2000:846a*/ 0xb8, 0x28, 0x00, /* mov ax, 00028h */
636 /*2000:846d*/ 0xf7, 0xea, /* imul dx */
637 /*2000:846f*/ 0xbb, 0x18, 0x00, /* mov bx, 00018h */
638 /*2000:8472*/ 0xf7, 0xfb, /* idiv bx */
639 /*2000:8474*/ 0x33, 0xd2, /* xor dx, dx */
640 /*2000:8476*/ 0xbb, 0x00, 0x10, /* mov bx, 01000h */
641 /*2000:8479*/ 0x93, /* xchg bx, ax */
642 /*2000:847a*/ 0xf7, 0xfb, /* idiv bx */
643 /*2000:847c*/ 0x0b, 0xd2, /* or dx, dx */
644 /*2000:847e*/ 0x74, 0x01, /* je +001h (08481h) */
645 /*2000:8480*/ 0x40, /* inc ax */
646 /*2000:8481*/ 0x40, /* inc ax */
647 /*2000:8482*/ 0xa3, 0x4d, 0xac, /* mov word [0ac4dh], ax */
648 /*2000:8485*/ 0x1f, /* pop DS */
649 /*2000:8486*/ 0x61, /* popaw */
650 /*2000:8487*/ 0xc3, /* retn */
651 };
652 static uint8_t const s_abVariant1Mask[] =
653 {
654 /*2000:840a*/ 0xff, /* pushaw */
655 /*2000:840b*/ 0xff, /* push DS */
656 /*2000:840c*/ 0xff, /* push CS */
657 /*2000:840d*/ 0xff, /* pop DS */
658 /*2000:840e*/ 0xff, /* pushfw */
659 /*2000:840f*/ 0xff, /* cli */
660 /*2000:8410*/ 0xff, 0xff, /* mov AL, 034h */
661 /*2000:8412*/ 0xff, 0xff, /* out 043h, AL */
662 /*2000:8414*/ 0xff, 0x00, 0x00, /* call 0808ch - ignore offset */
663 /*2000:8417*/ 0xff, 0xff, /* xor al, al */
664 /*2000:8419*/ 0xff, 0xff, /* out 040h, AL */
665 /*2000:841b*/ 0xff, 0x00, 0x00, /* call 0808ch - ignore offset */
666 /*2000:841e*/ 0xff, 0xff, /* out 040h, AL */
667 /*2000:8420*/ 0xff, 0x00, 0x00, /* call 0808ch - ignore offset */
668 /*2000:8423*/ 0xff, 0xff, /* mov AL, 000h */
669 /*2000:8425*/ 0xff, 0xff, /* out 043h, AL */
670 /*2000:8427*/ 0xff, 0x00, 0x00, /* call 0808ch - ignore offset */
671 /*2000:842a*/ 0xff, 0xff, /* in AL, 040h */
672 /*2000:842c*/ 0xff, 0x00, 0x00, /* call 0808ch - ignore offset */
673 /*2000:842f*/ 0xff, 0xff, /* mov bl, al */
674 /*2000:8431*/ 0xff, 0xff, /* in AL, 040h */
675 /*2000:8433*/ 0xff, 0xff, /* mov bh, al */
676 /*2000:8435*/ 0xff, 0xff, /* mov AL, 000h */
677 /*2000:8437*/ 0xff, 0xff, /* out 043h, AL */
678 /*2000:8439*/ 0xff, 0x00, 0x00, /* call 0808ch - ignore offset */
679 /*2000:843c*/ 0xff, 0x40, /* in AL, 040h */
680 /*2000:843e*/ 0xff, 0x00, 0x00, /* call 0808ch - ignore offset */
681 /*2000:8441*/ 0xff, 0xff, /* mov cl, al */
682 /*2000:8443*/ 0xff, 0xff, /* in AL, 040h */
683 /*2000:8445*/ 0xff, 0xff, /* mov ch, al */
684 /*2000:8447*/ 0xff, 0x00, 0x00, /* mov si, 01000h - ignore loop count */
685 /*2000:844a*/ 0xff, 0xff, /* xchg bx, bx */
686 /*2000:844c*/ 0xff, /* dec si */
687 /*2000:844d*/ 0xff, 0xfd, /* jne -003h (0844ch) */
688 /*2000:844f*/ 0xff, 0xff, /* mov AL, 000h */
689 /*2000:8451*/ 0xff, 0xff, /* out 043h, AL */
690 /*2000:8453*/ 0xff, 0x00, 0x00, /* call 0808ch - ignore offset */
691 /*2000:8456*/ 0xff, 0xff, /* in AL, 040h */
692 /*2000:8458*/ 0xff, 0x00, 0x00, /* call 0808ch - ignore offset */
693 /*2000:845b*/ 0xff, 0xff, /* mov dl, al */
694 /*2000:845d*/ 0xff, 0xff, /* in AL, 040h */
695 /*2000:845f*/ 0xff, 0xff, /* mov dh, al */
696 /*2000:8461*/ 0xff, /* popfw */
697 /*2000:8462*/ 0xff, 0xff, /* sub bx, cx */
698 /*2000:8464*/ 0xff, 0xff, /* sub cx, dx */
699 /*2000:8466*/ 0xff, 0xff, /* sub cx, bx */
700 /*2000:8468*/ 0xff, 0xff, /* xchg dx, cx */
701 /*2000:846a*/ 0xff, 0xff, 0xff, /* mov ax, 00028h */
702 /*2000:846d*/ 0xff, 0xff, /* imul dx */
703 /*2000:846f*/ 0xff, 0xff, 0xff, /* mov bx, 00018h */
704 /*2000:8472*/ 0xff, 0xff, /* idiv bx */
705 /*2000:8474*/ 0xff, 0xff, /* xor dx, dx */
706 /*2000:8476*/ 0xff, 0x00, 0x00, /* mov bx, 01000h - ignore loop count */
707 /*2000:8479*/ 0xff, /* xchg bx, ax */
708 /*2000:847a*/ 0xff, 0xff, /* idiv bx */
709 /*2000:847c*/ 0xff, 0xff, /* or dx, dx */
710 /*2000:847e*/ 0xff, 0xff, /* je +001h (08481h) */
711 /*2000:8480*/ 0xff, /* inc ax */
712 /*2000:8481*/ 0xff, /* inc ax */
713 /*2000:8482*/ 0xff, 0x00, 0x00, /* mov word [0ac4dh], ax */
714 /*2000:8485*/ 0xff, /* pop DS */
715 /*2000:8486*/ 0xff, /* popaw */
716 /*2000:8487*/ 0xff, /* retn */
717 };
718 AssertCompile(sizeof(s_abVariant1Mask) == sizeof(s_abVariant1));
719
720 /* uUser1 = off to start injecting code; uUser2 = jump target offset from start of pattern */
721 static const OS2CODEPATTERN s_aPatterns[] =
722 {
723 { s_abVariant1, s_abVariant1Mask, sizeof(s_abVariant1Mask), 0x840e - 0x840a, 0x8482 - 0x840a, 0, 0, 0 },
724 };
725
726 PCOS2CODEPATTERN pPattern;
727 uint8_t *pbHit = findCodePattern(&s_aPatterns[0], RT_ELEMENTS(s_aPatterns), pbFile, cbFile, &pPattern);
728 if (pPattern)
729 {
730 uint8_t *pbJmpTarget = &pbHit[pPattern->uUser2];
731 uint8_t *pbPatch = &pbHit[pPattern->uUser1];
732 *pbPatch++ = 0xb8; /* mov ax, 01000h */
733 *pbPatch++ = 0x00;
734 *pbPatch++ = 0x10;
735#if 0
736 *pbPatch++ = 0xfa; /* cli */
737 *pbPatch++ = 0xf4; /* hlt */
738#endif
739 uint16_t offRel16 = (uint16_t)(pbJmpTarget - &pbPatch[3]);
740 *pbPatch++ = 0xe9; /* jmp rel16 */
741 *pbPatch++ = (uint8_t)offRel16;
742 *pbPatch++ = (uint8_t)(offRel16 >> 8);
743 *pbPatch++ = 0xcc;
744 *pbPatch++ = 0xcc;
745 }
746 else
747 LogRelFunc(("No patch pattern match!\n"));
748
749 return VINF_SUCCESS;
750}
751
752HRESULT UnattendedOs2Installer::copyFilesToAuxFloppyImage(RTVFS hVfs)
753{
754 /*
755 * Make sure we've split the files already.
756 */
757 HRESULT hrc = splitResponseFile();
758 if (FAILED(hrc))
759 return hrc;
760
761 /*
762 * We need to copy over the files needed to boot OS/2.
763 */
764 static struct
765 {
766 bool fMandatory;
767 const char *apszNames[2]; /**< Will always copy it over using the first name. */
768 const char *apszDisks[3];
769 const char *pszMinVer;
770 const char *pszMaxVer;
771 int (*pfnPatcher)(uint8_t *pbFile, size_t cbFile, const char *pszFilename, UnattendedOs2Installer *pThis);
772 } const s_aFiles[] =
773 {
774 { true, { "OS2BOOT", NULL }, { "DISK_0", NULL, NULL }, "2.1", NULL, NULL }, /* 2.0 did not have OS2BOOT */
775 { true, { "OS2LDR", NULL }, { "DISK_0", NULL, NULL }, NULL, NULL, patchOs2Ldr },
776 { true, { "OS2LDR.MSG", NULL }, { "DISK_0", NULL, NULL }, NULL, NULL, NULL },
777 { true, { "OS2KRNL", "OS2KRNLI" }, { "DISK_0", NULL, NULL }, NULL, NULL, NULL }, /* OS2KRNLI seems to trigger question for 2nd floppy */
778 { true, { "OS2DUMP", NULL }, { "DISK_0", NULL, NULL }, NULL, NULL, NULL },
779
780 { true, { "ANSICALL.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
781 { true, { "BKSCALLS.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
782 { true, { "BMSCALLS.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
783 { true, { "BVHINIT.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
784 { true, { "BVSCALLS.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
785 { true, { "CDFS.IFS", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
786 { true, { "CLOCK01.SYS", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
787 { true, { "COUNT437.SYS", "COUNTRY.SYS" }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
788 { true, { "DOS.SYS", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
789 { true, { "DOSCALL1.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
790 { true, { "IBM1FLPY.ADD", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
791 { true, { "IBM1S506.ADD", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
792 { true, { "IBMIDECD.FLT", NULL }, { "DISK_1", "DISK_2", NULL }, "4.0", NULL, NULL }, /* not in 2.1 & Warp3 */
793 { true, { "IBMKBD.SYS", "KBD01.SYS"/*?*/}, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
794#if 1 /* Sometimes takes forever. (Bad IODelay count? Fixed by OS2LDR patching?) Removing seems to cause testcfg.sys to crash. */
795 { true, { "ISAPNP.SNP", NULL }, { "DISK_1", "DISK_2", NULL }, "4.0", NULL, NULL }, /* not in 2.1 */
796#endif
797 { true, { "KBDBASE.SYS", NULL }, { "DISK_1", "DISK_2", NULL }, "3.0", NULL, NULL }, /* not in 2.1 */
798 { true, { "KBDCALLS.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
799 { true, { "KEYBOARD.DCP", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
800 { true, { "MOUCALLS.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
801 { true, { "MSG.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
802 { true, { "NAMPIPES.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
803 { true, { "NLS.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
804 { true, { "OS2CDROM.DMD", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
805 { true, { "OS2CHAR.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
806 { true, { "OS2DASD.DMD", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
807 { true, { "OS2LVM.DMD", NULL }, { "DISK_1", "DISK_2", NULL }, "4.5", NULL, NULL },
808 { true, { "OS2VER", NULL }, { "DISK_0", NULL, NULL }, NULL, NULL, NULL },
809 { true, { "PNP.SYS", NULL }, { "DISK_1", "DISK_2", NULL }, "4.0", NULL, NULL },
810 { true, { "QUECALLS.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
811 { true, { "RESOURCE.SYS", NULL }, { "DISK_1", "DISK_2", NULL }, "3.0", NULL, NULL }, /* not in 2.1*/
812 { true, { "SCREEN01.SYS", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
813 { true, { "SESMGR.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
814 { true, { "TESTCFG.SYS", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL, /*patchTestCfg*/ },
815 { true, { "VIO437.DCP", "VTBL850.DCP" }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
816 { true, { "VIOCALLS.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL, NULL },
817 };
818
819
820 RTVFS hVfsOrgIso;
821 hrc = openInstallIsoImage(&hVfsOrgIso);
822 if (SUCCEEDED(hrc))
823 {
824 for (size_t i = 0; i < RT_ELEMENTS(s_aFiles); i++)
825 {
826 bool fCopied = false;
827 for (size_t iDisk = 0; iDisk < RT_ELEMENTS(s_aFiles[i].apszDisks) && s_aFiles[i].apszDisks[iDisk] && !fCopied; iDisk++)
828 {
829 for (size_t iName = 0; iName < RT_ELEMENTS(s_aFiles[i].apszNames) && s_aFiles[i].apszNames[iName]; iName++)
830 {
831 char szPath[256];
832 int vrc = RTPathJoin(szPath, sizeof(szPath), mStrOs2Images.c_str(), s_aFiles[i].apszDisks[iDisk]);
833 if (RT_SUCCESS(vrc))
834 vrc = RTPathAppend(szPath, sizeof(szPath), s_aFiles[i].apszNames[iName]);
835 AssertRCBreakStmt(vrc, hrc = mpParent->setErrorBoth(E_FAIL, vrc, tr("RTPathJoin/Append failed for %s: %Rrc"),
836 s_aFiles[i].apszNames[iName], vrc));
837 RTVFSFILE hVfsSrc;
838 vrc = RTVfsFileOpen(hVfsOrgIso, szPath, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, &hVfsSrc);
839 if (RT_SUCCESS(vrc))
840 {
841 RTVFSFILE hVfsDst;
842 vrc = RTVfsFileOpen(hVfs, s_aFiles[i].apszNames[0],
843 RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE
844 | (0755 << RTFILE_O_CREATE_MODE_SHIFT), &hVfsDst);
845 if (RT_SUCCESS(vrc))
846 {
847 if (!s_aFiles[i].pfnPatcher)
848 {
849 /*
850 * Not patching this file, so just pump it thru and close it.
851 */
852 RTVFSIOSTREAM hVfsIosSrc = RTVfsFileToIoStream(hVfsSrc);
853 RTVFSIOSTREAM hVfsIosDst = RTVfsFileToIoStream(hVfsDst);
854 vrc = RTVfsUtilPumpIoStreams(hVfsIosSrc, hVfsIosDst, 0);
855 RTVfsIoStrmRelease(hVfsIosDst);
856 RTVfsIoStrmRelease(hVfsIosSrc);
857 if (RT_FAILURE(vrc))
858 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc,
859 tr("Failed to write %s to the floppy: %Rrc"),
860 s_aFiles[i].apszNames, vrc);
861 }
862 else
863 {
864 /*
865 * Read the file into memory, do the patching and writed
866 * the patched content to the floppy.
867 */
868 uint64_t cbFile = 0;
869 vrc = RTVfsFileQuerySize(hVfsSrc, &cbFile);
870 if (RT_SUCCESS(vrc) && cbFile < _32M)
871 {
872 uint8_t *pbFile = (uint8_t *)RTMemTmpAllocZ((size_t)cbFile);
873 if (pbFile)
874 {
875 vrc = RTVfsFileRead(hVfsSrc, pbFile, (size_t)cbFile, NULL);
876 if (RT_SUCCESS(vrc))
877 {
878 vrc = s_aFiles[i].pfnPatcher(pbFile, (size_t)cbFile, s_aFiles[i].apszNames[0], this);
879 if (RT_SUCCESS(vrc))
880 {
881 vrc = RTVfsFileWrite(hVfsDst, pbFile, (size_t)cbFile, NULL);
882 if (RT_FAILURE(vrc))
883 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc,
884 tr("Failed to write %s to the floppy: %Rrc"),
885 s_aFiles[i].apszNames, vrc);
886 }
887 else
888 hrc = mpParent->setErrorBoth(E_FAIL, vrc, tr("Patcher failed for '%s': %Rrc"),
889 s_aFiles[i].apszNames, vrc);
890 }
891 else
892 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc,
893 tr("Error reading '%s' into memory for patching: %Rrc"),
894 s_aFiles[i].apszNames, vrc);
895 RTMemTmpFree(pbFile);
896 }
897 else
898 hrc = mpParent->setError(E_OUTOFMEMORY, tr("Failed to allocate %zu bytes for '%s'"),
899 (size_t)cbFile, s_aFiles[i].apszNames);
900 }
901 else if (RT_FAILURE(vrc))
902 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc,
903 tr("Failed to query the size of '%s': %Rrc"),
904 s_aFiles[i].apszNames, vrc);
905 else
906 hrc = mpParent->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE, tr("File too big to patch: '%s'"),
907 s_aFiles[i].apszNames);
908 }
909 RTVfsFileRelease(hVfsDst);
910 }
911 else
912 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to open %s on floppy: %Rrc"),
913 s_aFiles[i].apszNames, vrc);
914
915 RTVfsFileRelease(hVfsSrc);
916 fCopied = true;
917 break;
918 }
919 }
920 }
921 if (FAILED(hrc))
922 break;
923 if (!fCopied)
924 {
925 /** @todo do version filtering. */
926 hrc = mpParent->setErrorBoth(E_FAIL, VERR_FILE_NOT_FOUND,
927 tr("Failed to locate '%s' needed for the install floppy"), s_aFiles[i].apszNames[0]);
928 break;
929 }
930 }
931 RTVfsRelease(hVfsOrgIso);
932 }
933
934 /*
935 * In addition, we need to add a CONFIG.SYS and the startup script.
936 */
937 if (SUCCEEDED(hrc))
938 {
939 Utf8Str strSrc;
940 try
941 {
942 strSrc = mpParent->i_getAuxiliaryBasePath();
943 strSrc.append("CONFIG.SYS");
944 }
945 catch (std::bad_alloc &)
946 {
947 return E_OUTOFMEMORY;
948 }
949 hrc = addFileToFloppyImage(hVfs, strSrc.c_str(), "CONFIG.SYS");
950 }
951
952 /*
953 * We also want a ALTF2ON.$$$ file so we can see which drivers are loaded
954 * and where it might get stuck.
955 */
956 if (SUCCEEDED(hrc))
957 {
958 RTVFSFILE hVfsFile;
959 int vrc = RTVfsFileOpen(hVfs, "ALTF2ON.$$$",
960 RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE
961 | (0755 << RTFILE_O_CREATE_MODE_SHIFT), &hVfsFile);
962 if (RT_SUCCESS(vrc))
963 {
964 /** @todo buggy fat vfs: cannot write empty files */
965 RTVfsFileWrite(hVfsFile, RT_STR_TUPLE("\r\n"), NULL);
966 RTVfsFileRelease(hVfsFile);
967 }
968 else
969 hrc = mpParent->setErrorBoth(E_FAIL, VERR_FILE_NOT_FOUND, tr("Failed to create 'ALTF2ON.$$$' on the install floppy"));
970 }
971
972 return hrc;
973}
974
975HRESULT UnattendedOs2Installer::addFilesToAuxVisoVectors(RTCList<RTCString> &rVecArgs, RTCList<RTCString> &rVecFiles,
976 RTVFS hVfsOrgIso, bool fOverwrite)
977{
978 /*
979 * Make sure we've split the files already.
980 */
981 HRESULT hrc = splitResponseFile();
982 if (FAILED(hrc))
983 return hrc;
984
985 /*
986 * Add our stuff to the vectors.
987 *
988 * Note! Typcially OS/2 ISOs are without joliet or UDF namespaces, given
989 * their age and tools used to produce them, but more recent ones
990 * like ArcaOS have joliet present. So, to avoid ending up with an
991 * almost empty CDROM in Phase2 because UDF.IFS is loaded and
992 * presenting the joliet namespace, the --name-setup-from-import
993 * option was added to the ISO maker. It will look at the files that
994 * were imported and adjust the --name-setup accordingly (logged).
995 */
996 try
997 {
998 /* Remaster ISO. */
999 rVecArgs.append() = "--no-file-mode";
1000 rVecArgs.append() = "--no-dir-mode";
1001
1002 rVecArgs.append() = "--import-iso";
1003 rVecArgs.append(mpParent->i_getIsoPath());
1004 rVecArgs.append() = "--name-setup-from-import"; /* */
1005
1006 /** @todo these enables rock-ridge... */
1007 rVecArgs.append() = "--file-mode=0444";
1008 rVecArgs.append() = "--dir-mode=0555";
1009
1010 /* Add the boot floppy to the ISO: */
1011 rVecArgs.append() = "--eltorito-new-entry";
1012 rVecArgs.append() = "--eltorito-add-image";
1013 rVecArgs.append().assign("VBoxBootFloppy.img=").append(mStrAuxiliaryFloppyFilePath);
1014 rVecArgs.append() = "--eltorito-floppy-288";
1015
1016 /* Add the response files and postinstall files to the ISO: */
1017 Utf8Str const &rStrAuxPrefix = mpParent->i_getAuxiliaryBasePath();
1018 size_t i = mVecSplitFiles.size();
1019 while (i-- > 0)
1020 {
1021 RTCString const &rStrFile = mVecSplitFiles[i];
1022 rVecArgs.append().assign("VBoxCID/").append(rStrFile).append('=').append(rStrAuxPrefix).append(rStrFile);
1023 }
1024
1025 /* Add the os2_util.exe to the ISO: */
1026 Utf8Str strUnattendedTemplates;
1027 int vrc = RTPathAppPrivateNoArchCxx(strUnattendedTemplates);
1028 AssertRCReturn(vrc, mpParent->setErrorVrc(vrc));
1029 vrc = RTPathAppendCxx(strUnattendedTemplates, "UnattendedTemplates");
1030 AssertRCReturn(vrc, mpParent->setErrorVrc(vrc));
1031 rVecArgs.append().assign("VBoxCID/os2_util.exe=").append(strUnattendedTemplates).append("/os2_util.exe");
1032 }
1033 catch (std::bad_alloc &)
1034 {
1035 return E_OUTOFMEMORY;
1036 }
1037
1038 /*
1039 * Call parent.
1040 */
1041 return UnattendedInstaller::addFilesToAuxVisoVectors(rVecArgs, rVecFiles, hVfsOrgIso, fOverwrite);
1042}
1043
1044/**
1045 * Helper for splitFile.
1046 */
1047static const char *splitFileLocateSubstring(const char *pszSrc, size_t cchSrc, const char *pszSubstring, size_t cchSubstring)
1048{
1049 char const ch0 = *pszSubstring;
1050 while (cchSrc >= cchSubstring)
1051 {
1052 const char *pszHit0 = (const char *)memchr(pszSrc, ch0, cchSrc - cchSubstring + 1);
1053 if (pszHit0)
1054 {
1055 if (memcmp(pszHit0, pszSubstring, cchSubstring) == 0)
1056 return pszHit0;
1057 }
1058 else
1059 break;
1060 cchSrc -= (size_t)(pszHit0 - pszSrc) + 1;
1061 pszSrc = pszHit0 + 1;
1062 }
1063 return NULL;
1064}
1065
1066/**
1067 * Worker for splitFile().
1068 */
1069HRESULT UnattendedOs2Installer::splitFileInner(const char *pszFileToSplit, RTCList<RTCString> &rVecSplitFiles,
1070 const char *pszSrc, size_t cbLeft) RT_NOEXCEPT
1071{
1072 static const char s_szPrefix[] = "@@VBOX_SPLITTER_";
1073 const char * const pszStart = pszSrc;
1074 const char * const pszEnd = &pszSrc[cbLeft];
1075 while (cbLeft > 0)
1076 {
1077 /*
1078 * Locate the next split start marker (everything before it is ignored).
1079 */
1080 const char *pszMarker = splitFileLocateSubstring(pszSrc, cbLeft, s_szPrefix, sizeof(s_szPrefix) - 1);
1081 if (pszMarker)
1082 pszMarker += sizeof(s_szPrefix) - 1;
1083 else
1084 break;
1085 if (strncmp(pszMarker, RT_STR_TUPLE("START[")) != 0)
1086 return mpParent->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
1087 tr("Unexpected splitter tag in '%s' at offset %p: @@VBOX_SPLITTER_%.64s"),
1088 pszFileToSplit, pszMarker - pszStart, pszMarker);
1089 pszMarker += sizeof("START[") - 1;
1090 const char *pszTail = splitFileLocateSubstring(pszMarker, (size_t)(pszEnd - pszMarker), RT_STR_TUPLE("]@@"));
1091 size_t const cchFilename = (size_t)(pszTail - pszMarker);
1092 if ( !pszTail
1093 || cchFilename > 64
1094 || memchr(pszMarker, '\\', cchFilename)
1095 || memchr(pszMarker, '/', cchFilename)
1096 || memchr(pszMarker, ':', cchFilename)
1097 || memchr(pszMarker, '\0', cchFilename) )
1098 return mpParent->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
1099 tr("Malformed splitter tag in '%s' at offset %p: @@VBOX_SPLITTER_START[%.64s"),
1100 pszFileToSplit, cchFilename, pszMarker);
1101 int vrc = RTStrValidateEncodingEx(pszMarker, cchFilename, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
1102 if (RT_FAILURE(vrc))
1103 return mpParent->setErrorBoth(E_FAIL, vrc,
1104 tr("Malformed splitter tag in '%s' at offset %p: @@VBOX_SPLITTER_START[%.*Rhxs"),
1105 pszFileToSplit, cchFilename, pszTail - pszMarker, pszMarker);
1106 const char *pszFilename;
1107 try
1108 {
1109 pszFilename = rVecSplitFiles.append().assign(pszMarker, cchFilename).c_str();
1110 }
1111 catch (std::bad_alloc &)
1112 {
1113 return E_OUTOFMEMORY;
1114 }
1115 const char *pszDocStart = pszTail + sizeof("]@@") - 1;
1116 while (RT_C_IS_SPACE(*pszDocStart))
1117 if (*pszDocStart++ == '\n')
1118 break;
1119
1120 /* Advance. */
1121 pszSrc = pszDocStart;
1122 cbLeft = (size_t)(pszEnd - pszDocStart);
1123
1124 /*
1125 * Locate the matching end marker (there cannot be any other markers inbetween).
1126 */
1127 const char * const pszDocEnd = pszMarker = splitFileLocateSubstring(pszSrc, cbLeft, s_szPrefix, sizeof(s_szPrefix) - 1);
1128 if (pszMarker)
1129 pszMarker += sizeof(s_szPrefix) - 1;
1130 else
1131 return mpParent->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
1132 tr("No END splitter tag for '%s' in '%s'"), pszFilename, pszFileToSplit);
1133 if (strncmp(pszMarker, RT_STR_TUPLE("END[")) != 0)
1134 return mpParent->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
1135 tr("Unexpected splitter tag in '%s' at offset %p: @@VBOX_SPLITTER_%.64s"),
1136 pszFileToSplit, (size_t)(pszEnd - pszMarker), pszMarker);
1137 pszMarker += sizeof("END[") - 1;
1138 if ( strncmp(pszMarker, pszFilename, cchFilename) != 0
1139 || pszMarker[cchFilename] != ']'
1140 || pszMarker[cchFilename + 1] != '@'
1141 || pszMarker[cchFilename + 2] != '@')
1142 return mpParent->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
1143 tr("Mismatching splitter tag for '%s' in '%s' at offset %p: @@VBOX_SPLITTER_END[%.64Rhxs"),
1144 pszFilename, pszFileToSplit, (size_t)(pszEnd - pszMarker), pszMarker);
1145
1146 /* Advance. */
1147 pszSrc = pszMarker + cchFilename + sizeof("]@@") - 1;
1148 cbLeft = (size_t)(pszEnd - pszSrc);
1149
1150 /*
1151 * Write out the file.
1152 */
1153 Utf8Str strDstFilename;
1154 vrc = strDstFilename.assignNoThrow(mpParent->i_getAuxiliaryBasePath());
1155 if (RT_SUCCESS(vrc))
1156 vrc = strDstFilename.appendNoThrow(pszFilename);
1157 if (RT_SUCCESS(vrc))
1158 {
1159 RTFILE hFile;
1160 vrc = RTFileOpen(&hFile, strDstFilename.c_str(), RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
1161 if (RT_SUCCESS(vrc))
1162 {
1163 vrc = RTFileWrite(hFile, pszDocStart, (size_t)(pszDocEnd - pszDocStart), NULL);
1164 if (RT_SUCCESS(vrc))
1165 vrc = RTFileClose(hFile);
1166 else
1167 RTFileClose(hFile);
1168 if (RT_FAILURE(vrc))
1169 return mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error writing '%s' (split out from '%s'): %Rrc"),
1170 strDstFilename.c_str(), pszFileToSplit, vrc);
1171 }
1172 else
1173 return mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc,
1174 tr("File splitter failed to open output file '%s' in '%s': %Rrc (%s)"),
1175 pszFilename, pszFileToSplit, vrc, strDstFilename.c_str());
1176 }
1177 else
1178 return mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc,
1179 tr("File splitter failed to construct path for '%s' in '%s': %Rrc"),
1180 pszFilename, pszFileToSplit, vrc);
1181 }
1182
1183 return S_OK;
1184}
1185
1186HRESULT UnattendedOs2Installer::splitFile(const char *pszFileToSplit, RTCList<RTCString> &rVecSplitFiles) RT_NOEXCEPT
1187{
1188 /*
1189 * Read the whole source file into memory, making sure it's zero terminated.
1190 */
1191 HRESULT hrc;
1192 void *pvSrc;
1193 size_t cbSrc;
1194 int vrc = RTFileReadAllEx(pszFileToSplit, 0 /*off*/, _16M /*cbMax*/,
1195 RTFILE_RDALL_F_TRAILING_ZERO_BYTE | RTFILE_RDALL_F_FAIL_ON_MAX_SIZE | RTFILE_RDALL_O_DENY_WRITE,
1196 &pvSrc, &cbSrc);
1197 if (RT_SUCCESS(vrc))
1198 {
1199 /*
1200 * Do the actual splitting in a worker function to avoid needing to
1201 * thing about calling RTFileReadAllFree in error paths.
1202 */
1203 hrc = splitFileInner(pszFileToSplit, rVecSplitFiles, (const char *)pvSrc, cbSrc);
1204 RTFileReadAllFree(pvSrc, cbSrc);
1205 }
1206 else
1207 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to read '%s' for splitting up: %Rrc"),
1208 pszFileToSplit, vrc);
1209 return hrc;
1210}
1211
1212HRESULT UnattendedOs2Installer::splitFile(BaseTextScript *pEditor, RTCList<RTCString> &rVecSplitFiles) RT_NOEXCEPT
1213{
1214 /*
1215 * Get the output from the editor.
1216 */
1217 Utf8Str strSrc;
1218 HRESULT hrc = pEditor->saveToString(strSrc);
1219 if (SUCCEEDED(hrc))
1220 {
1221 /*
1222 * Do the actual splitting.
1223 */
1224 hrc = splitFileInner(pEditor->getDefaultFilename(), rVecSplitFiles, strSrc.c_str(), strSrc.length());
1225 }
1226 return hrc;
1227}
1228
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use