VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

  • 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 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * UnattendedOs2Installer implementation.
4 */
5
6/*
7 * Copyright (C) 2006-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#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 = NULL;
727 uint8_t *pbHit = findCodePattern(&s_aPatterns[0], RT_ELEMENTS(s_aPatterns), pbFile, cbFile, &pPattern);
728 if (pbHit)
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.

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