VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/UnattendedInstaller.cpp@ 93089

Last change on this file since 93089 was 93089, checked in by vboxsync, 3 years ago

Main/Unattended: Some more work on the OS/2 unattended installation. [implicit signed clang annoyances]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 75.4 KB
Line 
1/* $Id: UnattendedInstaller.cpp 93089 2021-12-27 11:20:36Z vboxsync $ */
2/** @file
3 * UnattendedInstaller class and it's descendants implementation
4 */
5
6/*
7 * Copyright (C) 2006-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_MAIN_UNATTENDED
23#include "LoggingNew.h"
24#include "VirtualBoxBase.h"
25#include "VirtualBoxErrorInfoImpl.h"
26#include "AutoCaller.h"
27#include <VBox/com/ErrorInfo.h>
28
29#include "UnattendedImpl.h"
30#include "UnattendedInstaller.h"
31#include "UnattendedScript.h"
32
33#include <VBox/err.h>
34#include <iprt/ctype.h>
35#include <iprt/fsisomaker.h>
36#include <iprt/fsvfs.h>
37#include <iprt/getopt.h>
38#include <iprt/file.h>
39#include <iprt/path.h>
40#include <iprt/stream.h>
41#include <iprt/vfs.h>
42#ifdef RT_OS_SOLARIS
43# undef ES /* Workaround for someone dragging the namespace pollutor sys/regset.h. Sigh. */
44#endif
45#include <iprt/formats/iso9660.h>
46#include <iprt/formats/fat.h>
47#include <iprt/cpp/path.h>
48
49
50using namespace std;
51
52
53/* static */ UnattendedInstaller *UnattendedInstaller::createInstance(VBOXOSTYPE enmOsType,
54 const Utf8Str &strGuestOsType,
55 const Utf8Str &strDetectedOSVersion,
56 const Utf8Str &strDetectedOSFlavor,
57 const Utf8Str &strDetectedOSHints,
58 Unattended *pParent)
59{
60 UnattendedInstaller *pUinstaller = NULL;
61
62 if (strGuestOsType.find("Windows") != RTCString::npos)
63 {
64 if (enmOsType >= VBOXOSTYPE_WinVista)
65 pUinstaller = new UnattendedWindowsXmlInstaller(pParent);
66 else
67 pUinstaller = new UnattendedWindowsSifInstaller(pParent);
68 }
69 else if (enmOsType >= VBOXOSTYPE_OS2 && enmOsType < VBOXOSTYPE_Linux)
70 pUinstaller = new UnattendedOs2Installer(pParent, strDetectedOSHints);
71 else
72 {
73 if (enmOsType == VBOXOSTYPE_Debian || enmOsType == VBOXOSTYPE_Debian_x64)
74 pUinstaller = new UnattendedDebianInstaller(pParent);
75 else if (enmOsType >= VBOXOSTYPE_Ubuntu && enmOsType <= VBOXOSTYPE_Ubuntu_x64)
76 pUinstaller = new UnattendedUbuntuInstaller(pParent);
77 else if (enmOsType >= VBOXOSTYPE_RedHat && enmOsType <= VBOXOSTYPE_RedHat_x64)
78 {
79 if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "8") >= 0)
80 pUinstaller = new UnattendedRhel8Installer(pParent);
81 else if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "7") >= 0)
82 pUinstaller = new UnattendedRhel7Installer(pParent);
83 else if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "6") >= 0)
84 pUinstaller = new UnattendedRhel6Installer(pParent);
85 else if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "5") >= 0)
86 pUinstaller = new UnattendedRhel5Installer(pParent);
87 else if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "4") >= 0)
88 pUinstaller = new UnattendedRhel4Installer(pParent);
89 else if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "3") >= 0)
90 pUinstaller = new UnattendedRhel3Installer(pParent);
91 else
92 pUinstaller = new UnattendedRhel6Installer(pParent);
93 }
94 else if (enmOsType >= VBOXOSTYPE_FedoraCore && enmOsType <= VBOXOSTYPE_FedoraCore_x64)
95 pUinstaller = new UnattendedFedoraInstaller(pParent);
96 else if (enmOsType >= VBOXOSTYPE_Oracle && enmOsType <= VBOXOSTYPE_Oracle_x64)
97 {
98 if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "8") >= 0)
99 pUinstaller = new UnattendedOracleLinux8Installer(pParent);
100 else if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "7") >= 0)
101 pUinstaller = new UnattendedOracleLinux7Installer(pParent);
102 else if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "6") >= 0)
103 pUinstaller = new UnattendedOracleLinux6Installer(pParent);
104 else
105 pUinstaller = new UnattendedOracleLinux6Installer(pParent);
106 }
107#if 0 /* doesn't work, so convert later. */
108 else if (enmOsType == VBOXOSTYPE_OpenSUSE || enmOsType == VBOXOSTYPE_OpenSUSE_x64)
109 pUinstaller = new UnattendedSuseInstaller(new UnattendedSUSEXMLScript(pParent), pParent);
110#endif
111 }
112 RT_NOREF_PV(strDetectedOSFlavor);
113 RT_NOREF_PV(strDetectedOSHints);
114 return pUinstaller;
115}
116
117
118//////////////////////////////////////////////////////////////////////////////////////////////////////
119/*
120*
121*
122* Implementation Unattended functions
123*
124*/
125//////////////////////////////////////////////////////////////////////////////////////////////////////
126
127/*
128 *
129 * UnattendedInstaller public methods
130 *
131 */
132UnattendedInstaller::UnattendedInstaller(Unattended *pParent,
133 const char *pszMainScriptTemplateName, const char *pszPostScriptTemplateName,
134 const char *pszMainScriptFilename, const char *pszPostScriptFilename,
135 DeviceType_T enmBootDevice /*= DeviceType_DVD */)
136 : mMainScript(pParent, pszMainScriptTemplateName, pszMainScriptFilename)
137 , mPostScript(pParent, pszPostScriptTemplateName, pszPostScriptFilename)
138 , mpParent(pParent)
139 , meBootDevice(enmBootDevice)
140{
141 AssertPtr(pParent);
142 Assert(*pszMainScriptTemplateName);
143 Assert(*pszMainScriptFilename);
144 Assert(*pszPostScriptTemplateName);
145 Assert(*pszPostScriptFilename);
146 Assert(enmBootDevice == DeviceType_DVD || enmBootDevice == DeviceType_Floppy);
147}
148
149UnattendedInstaller::~UnattendedInstaller()
150{
151 mpParent = NULL;
152}
153
154HRESULT UnattendedInstaller::initInstaller()
155{
156 /*
157 * Calculate the full main script template location.
158 */
159 if (mpParent->i_getScriptTemplatePath().isNotEmpty())
160 mStrMainScriptTemplate = mpParent->i_getScriptTemplatePath();
161 else
162 {
163 int vrc = RTPathAppPrivateNoArchCxx(mStrMainScriptTemplate);
164 if (RT_SUCCESS(vrc))
165 vrc = RTPathAppendCxx(mStrMainScriptTemplate, "UnattendedTemplates");
166 if (RT_SUCCESS(vrc))
167 vrc = RTPathAppendCxx(mStrMainScriptTemplate, mMainScript.getDefaultTemplateFilename());
168 if (RT_FAILURE(vrc))
169 return mpParent->setErrorBoth(E_FAIL, vrc,
170 tr("Failed to construct path to the unattended installer script templates (%Rrc)"),
171 vrc);
172 }
173
174 /*
175 * Calculate the full post script template location.
176 */
177 if (mpParent->i_getPostInstallScriptTemplatePath().isNotEmpty())
178 mStrPostScriptTemplate = mpParent->i_getPostInstallScriptTemplatePath();
179 else
180 {
181 int vrc = RTPathAppPrivateNoArchCxx(mStrPostScriptTemplate);
182 if (RT_SUCCESS(vrc))
183 vrc = RTPathAppendCxx(mStrPostScriptTemplate, "UnattendedTemplates");
184 if (RT_SUCCESS(vrc))
185 vrc = RTPathAppendCxx(mStrPostScriptTemplate, mPostScript.getDefaultTemplateFilename());
186 if (RT_FAILURE(vrc))
187 return mpParent->setErrorBoth(E_FAIL, vrc,
188 tr("Failed to construct path to the unattended installer script templates (%Rrc)"),
189 vrc);
190 }
191
192 /*
193 * Construct paths we need.
194 */
195 if (isAuxiliaryFloppyNeeded())
196 {
197 mStrAuxiliaryFloppyFilePath = mpParent->i_getAuxiliaryBasePath();
198 mStrAuxiliaryFloppyFilePath.append("aux-floppy.img");
199 }
200 if (isAuxiliaryIsoNeeded())
201 {
202 mStrAuxiliaryIsoFilePath = mpParent->i_getAuxiliaryBasePath();
203 if (!isAuxiliaryIsoIsVISO())
204 mStrAuxiliaryIsoFilePath.append("aux-iso.iso");
205 else
206 mStrAuxiliaryIsoFilePath.append("aux-iso.viso");
207 }
208
209 /*
210 * Check that we've got the minimum of data available.
211 */
212 if (mpParent->i_getIsoPath().isEmpty())
213 return mpParent->setError(E_INVALIDARG, tr("Cannot proceed with an empty installation ISO path"));
214 if (mpParent->i_getUser().isEmpty())
215 return mpParent->setError(E_INVALIDARG, tr("Empty user name is not allowed"));
216 if (mpParent->i_getPassword().isEmpty())
217 return mpParent->setError(E_INVALIDARG, tr("Empty password is not allowed"));
218
219 LogRelFunc(("UnattendedInstaller::savePassedData(): \n"));
220 return S_OK;
221}
222
223#if 0 /* Always in AUX ISO */
224bool UnattendedInstaller::isAdditionsIsoNeeded() const
225{
226 /* In the VISO case, we'll add the additions to the VISO in a subdir. */
227 return !isAuxiliaryIsoIsVISO() && mpParent->i_getInstallGuestAdditions();
228}
229
230bool UnattendedInstaller::isValidationKitIsoNeeded() const
231{
232 /* In the VISO case, we'll add the validation kit to the VISO in a subdir. */
233 return !isAuxiliaryIsoIsVISO() && mpParent->i_getInstallTestExecService();
234}
235#endif
236
237bool UnattendedInstaller::isAuxiliaryIsoNeeded() const
238{
239 /* In the VISO case we use the AUX ISO for GAs and TXS. */
240 return isAuxiliaryIsoIsVISO()
241 && ( mpParent->i_getInstallGuestAdditions()
242 || mpParent->i_getInstallTestExecService());
243}
244
245
246HRESULT UnattendedInstaller::prepareUnattendedScripts()
247{
248 LogFlow(("UnattendedInstaller::prepareUnattendedScripts()\n"));
249
250 /*
251 * The script template editor calls setError, so status codes just needs to
252 * be passed on to the caller. Do the same for both scripts.
253 */
254 HRESULT hrc = mMainScript.read(getTemplateFilePath());
255 if (SUCCEEDED(hrc))
256 {
257 hrc = mMainScript.parse();
258 if (SUCCEEDED(hrc))
259 {
260 /* Ditto for the post script. */
261 hrc = mPostScript.read(getPostTemplateFilePath());
262 if (SUCCEEDED(hrc))
263 {
264 hrc = mPostScript.parse();
265 if (SUCCEEDED(hrc))
266 {
267 LogFlow(("UnattendedInstaller::prepareUnattendedScripts: returns S_OK\n"));
268 return S_OK;
269 }
270 LogFlow(("UnattendedInstaller::prepareUnattendedScripts: parse failed on post script (%Rhrc)\n", hrc));
271 }
272 else
273 LogFlow(("UnattendedInstaller::prepareUnattendedScripts: error reading post install script template file (%Rhrc)\n", hrc));
274 }
275 else
276 LogFlow(("UnattendedInstaller::prepareUnattendedScripts: parse failed (%Rhrc)\n", hrc));
277 }
278 else
279 LogFlow(("UnattendedInstaller::prepareUnattendedScripts: error reading installation script template file (%Rhrc)\n", hrc));
280 return hrc;
281}
282
283HRESULT UnattendedInstaller::prepareMedia(bool fOverwrite /*=true*/)
284{
285 LogRelFlow(("UnattendedInstaller::prepareMedia:\n"));
286 HRESULT hrc = S_OK;
287 if (isAuxiliaryFloppyNeeded())
288 hrc = prepareAuxFloppyImage(fOverwrite);
289 if (SUCCEEDED(hrc))
290 {
291 if (isAuxiliaryIsoNeeded())
292 {
293 hrc = prepareAuxIsoImage(fOverwrite);
294 if (FAILED(hrc))
295 {
296 LogRelFlow(("UnattendedInstaller::prepareMedia: prepareAuxIsoImage failed\n"));
297
298 /* Delete the floppy image if we created one. */
299 if (isAuxiliaryFloppyNeeded())
300 RTFileDelete(getAuxiliaryFloppyFilePath().c_str());
301 }
302 }
303 }
304 LogRelFlow(("UnattendedInstaller::prepareMedia: returns %Rrc\n", hrc));
305 return hrc;
306}
307
308/*
309 *
310 * UnattendedInstaller protected methods
311 *
312 */
313HRESULT UnattendedInstaller::prepareAuxFloppyImage(bool fOverwrite)
314{
315 Assert(isAuxiliaryFloppyNeeded());
316
317 /*
318 * Create the image.
319 */
320 RTVFSFILE hVfsFile;
321 HRESULT hrc = newAuxFloppyImage(getAuxiliaryFloppyFilePath().c_str(), fOverwrite, &hVfsFile);
322 if (SUCCEEDED(hrc))
323 {
324 /*
325 * Open the FAT file system so we can copy files onto the floppy.
326 */
327 RTERRINFOSTATIC ErrInfo;
328 RTVFS hVfs;
329 int vrc = RTFsFatVolOpen(hVfsFile, false /*fReadOnly*/, 0 /*offBootSector*/, &hVfs, RTErrInfoInitStatic(&ErrInfo));
330 RTVfsFileRelease(hVfsFile);
331 if (RT_SUCCESS(vrc))
332 {
333 /*
334 * Call overridable method to copies the files onto it.
335 */
336 hrc = copyFilesToAuxFloppyImage(hVfs);
337
338 /*
339 * Release the VFS. On failure, delete the floppy image so the operation can
340 * be repeated in non-overwrite mode and so that we don't leave any mess behind.
341 */
342 RTVfsRelease(hVfs);
343 }
344 else if (RTErrInfoIsSet(&ErrInfo.Core))
345 hrc = mpParent->setErrorBoth(E_FAIL, vrc,
346 tr("Failed to open FAT file system on newly created floppy image '%s': %Rrc: %s"),
347 getAuxiliaryFloppyFilePath().c_str(), vrc, ErrInfo.Core.pszMsg);
348 else
349 hrc = mpParent->setErrorBoth(E_FAIL, vrc,
350 tr("Failed to open FAT file system onnewly created floppy image '%s': %Rrc"),
351 getAuxiliaryFloppyFilePath().c_str(), vrc);
352 if (FAILED(hrc))
353 RTFileDelete(getAuxiliaryFloppyFilePath().c_str());
354 }
355 return hrc;
356}
357
358HRESULT UnattendedInstaller::newAuxFloppyImage(const char *pszFilename, bool fOverwrite, PRTVFSFILE phVfsFile)
359{
360 /*
361 * Open the image file.
362 */
363 HRESULT hrc;
364 RTVFSFILE hVfsFile;
365 uint64_t fOpen = RTFILE_O_READWRITE | RTFILE_O_DENY_ALL | (0660 << RTFILE_O_CREATE_MODE_SHIFT);
366 if (fOverwrite)
367 fOpen |= RTFILE_O_CREATE_REPLACE;
368 else
369 fOpen |= RTFILE_O_OPEN;
370 int vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
371 if (RT_SUCCESS(vrc))
372 {
373 /*
374 * Format it.
375 */
376 vrc = RTFsFatVolFormat144(hVfsFile, false /*fQuick*/);
377 if (RT_SUCCESS(vrc))
378 {
379 *phVfsFile = hVfsFile;
380 LogRelFlow(("UnattendedInstaller::newAuxFloppyImage: created and formatted '%s'\n", pszFilename));
381 return S_OK;
382 }
383
384 hrc = mpParent->setErrorBoth(E_FAIL, vrc, tr("Failed to format floppy image '%s': %Rrc"), pszFilename, vrc);
385 RTVfsFileRelease(hVfsFile);
386 RTFileDelete(pszFilename);
387 }
388 else
389 hrc = mpParent->setErrorBoth(E_FAIL, vrc, tr("Failed to create floppy image '%s': %Rrc"), pszFilename, vrc);
390 return hrc;
391}
392
393HRESULT UnattendedInstaller::copyFilesToAuxFloppyImage(RTVFS hVfs)
394{
395 HRESULT hrc = addScriptToFloppyImage(&mMainScript, hVfs);
396 if (SUCCEEDED(hrc))
397 hrc = addScriptToFloppyImage(&mPostScript, hVfs);
398 return hrc;
399}
400
401HRESULT UnattendedInstaller::addScriptToFloppyImage(BaseTextScript *pEditor, RTVFS hVfs)
402{
403 /*
404 * Open the destination file.
405 */
406 HRESULT hrc;
407 RTVFSFILE hVfsFileDst;
408 int vrc = RTVfsFileOpen(hVfs, pEditor->getDefaultFilename(),
409 RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_ALL
410 | (0660 << RTFILE_O_CREATE_MODE_SHIFT),
411 &hVfsFileDst);
412 if (RT_SUCCESS(vrc))
413 {
414 /*
415 * Save the content to a string.
416 */
417 Utf8Str strScript;
418 hrc = pEditor->saveToString(strScript);
419 if (SUCCEEDED(hrc))
420 {
421 /*
422 * Write the string.
423 */
424 vrc = RTVfsFileWrite(hVfsFileDst, strScript.c_str(), strScript.length(), NULL);
425 if (RT_SUCCESS(vrc))
426 hrc = S_OK; /* done */
427 else
428 hrc = mpParent->setErrorBoth(E_FAIL, vrc,
429 tr("Error writing %zu bytes to '%s' in floppy image '%s': %Rrc",
430 "", strScript.length()),
431 strScript.length(), pEditor->getDefaultFilename(),
432 getAuxiliaryFloppyFilePath().c_str());
433 }
434 RTVfsFileRelease(hVfsFileDst);
435 }
436 else
437 hrc = mpParent->setErrorBoth(E_FAIL, vrc,
438 tr("Error creating '%s' in floppy image '%s': %Rrc"),
439 pEditor->getDefaultFilename(), getAuxiliaryFloppyFilePath().c_str());
440 return hrc;
441}
442
443HRESULT UnattendedInstaller::addFileToFloppyImage(RTVFS hVfs, const char *pszSrc, const char *pszDst)
444{
445 HRESULT hrc;
446
447 /*
448 * Open the source file.
449 */
450 RTVFSIOSTREAM hVfsIosSrc;
451 int vrc = RTVfsIoStrmOpenNormal(pszSrc, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsIosSrc);
452 if (RT_SUCCESS(vrc))
453 {
454 /*
455 * Open the destination file.
456 */
457 RTVFSFILE hVfsFileDst;
458 vrc = RTVfsFileOpen(hVfs, pszDst,
459 RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_ALL | (0660 << RTFILE_O_CREATE_MODE_SHIFT),
460 &hVfsFileDst);
461 if (RT_SUCCESS(vrc))
462 {
463 /*
464 * Do the copying.
465 */
466 RTVFSIOSTREAM hVfsIosDst = RTVfsFileToIoStream(hVfsFileDst);
467 vrc = RTVfsUtilPumpIoStreams(hVfsIosSrc, hVfsIosDst, 0);
468 if (RT_SUCCESS(vrc))
469 hrc = S_OK;
470 else
471 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error writing copying '%s' to floppy image '%s': %Rrc"),
472 pszSrc, getAuxiliaryFloppyFilePath().c_str(), vrc);
473 RTVfsIoStrmRelease(hVfsIosDst);
474 RTVfsFileRelease(hVfsFileDst);
475 }
476 else
477 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error opening '%s' on floppy image '%s' for writing: %Rrc"),
478 pszDst, getAuxiliaryFloppyFilePath().c_str(), vrc);
479
480 RTVfsIoStrmRelease(hVfsIosSrc);
481 }
482 else
483 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error opening '%s' for copying onto floppy image '%s': %Rrc"),
484 pszSrc, getAuxiliaryFloppyFilePath().c_str(), vrc);
485 return hrc;
486}
487
488HRESULT UnattendedInstaller::prepareAuxIsoImage(bool fOverwrite)
489{
490 /*
491 * Open the original installation ISO.
492 */
493 RTVFS hVfsOrgIso;
494 HRESULT hrc = openInstallIsoImage(&hVfsOrgIso);
495 if (SUCCEEDED(hrc))
496 {
497 /*
498 * The next steps depends on the kind of image we're making.
499 */
500 if (!isAuxiliaryIsoIsVISO())
501 {
502 RTFSISOMAKER hIsoMaker;
503 hrc = newAuxIsoImageMaker(&hIsoMaker);
504 if (SUCCEEDED(hrc))
505 {
506 hrc = addFilesToAuxIsoImageMaker(hIsoMaker, hVfsOrgIso);
507 if (SUCCEEDED(hrc))
508 hrc = finalizeAuxIsoImage(hIsoMaker, getAuxiliaryIsoFilePath().c_str(), fOverwrite);
509 RTFsIsoMakerRelease(hIsoMaker);
510 }
511 }
512 else
513 {
514 RTCList<RTCString> vecFiles(0);
515 RTCList<RTCString> vecArgs(0);
516 try
517 {
518 vecArgs.append() = "--iprt-iso-maker-file-marker-bourne-sh";
519 RTUUID Uuid;
520 int vrc = RTUuidCreate(&Uuid); AssertRC(vrc);
521 char szTmp[RTUUID_STR_LENGTH + 1];
522 vrc = RTUuidToStr(&Uuid, szTmp, sizeof(szTmp)); AssertRC(vrc);
523 vecArgs.append() = szTmp;
524 vecArgs.append() = "--file-mode=0444";
525 vecArgs.append() = "--dir-mode=0555";
526 }
527 catch (std::bad_alloc &)
528 {
529 hrc = E_OUTOFMEMORY;
530 }
531 if (SUCCEEDED(hrc))
532 {
533 hrc = addFilesToAuxVisoVectors(vecArgs, vecFiles, hVfsOrgIso, fOverwrite);
534 if (SUCCEEDED(hrc))
535 hrc = finalizeAuxVisoFile(vecArgs, getAuxiliaryIsoFilePath().c_str(), fOverwrite);
536
537 if (FAILED(hrc))
538 for (size_t i = 0; i < vecFiles.size(); i++)
539 RTFileDelete(vecFiles[i].c_str());
540 }
541 }
542 RTVfsRelease(hVfsOrgIso);
543 }
544 return hrc;
545}
546
547HRESULT UnattendedInstaller::openInstallIsoImage(PRTVFS phVfsIso, uint32_t fFlags /*= 0*/)
548{
549 /* Open the file. */
550 const char *pszIsoPath = mpParent->i_getIsoPath().c_str();
551 RTVFSFILE hOrgIsoFile;
552 int vrc = RTVfsFileOpenNormal(pszIsoPath, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hOrgIsoFile);
553 if (RT_FAILURE(vrc))
554 return mpParent->setErrorBoth(E_FAIL, vrc, tr("Failed to open ISO image '%s' (%Rrc)"), pszIsoPath, vrc);
555
556 /* Pass the file to the ISO file system interpreter. */
557 RTERRINFOSTATIC ErrInfo;
558 vrc = RTFsIso9660VolOpen(hOrgIsoFile, fFlags, phVfsIso, RTErrInfoInitStatic(&ErrInfo));
559 RTVfsFileRelease(hOrgIsoFile);
560 if (RT_SUCCESS(vrc))
561 return S_OK;
562 if (RTErrInfoIsSet(&ErrInfo.Core))
563 return mpParent->setErrorBoth(E_FAIL, vrc, tr("ISO reader fail to open '%s' (%Rrc): %s"),
564 pszIsoPath, vrc, ErrInfo.Core.pszMsg);
565 return mpParent->setErrorBoth(E_FAIL, vrc, tr("ISO reader fail to open '%s' (%Rrc)"), pszIsoPath, vrc);
566}
567
568HRESULT UnattendedInstaller::newAuxIsoImageMaker(PRTFSISOMAKER phIsoMaker)
569{
570 int vrc = RTFsIsoMakerCreate(phIsoMaker);
571 if (RT_SUCCESS(vrc))
572 return S_OK;
573 return mpParent->setErrorBoth(E_FAIL, vrc, tr("RTFsIsoMakerCreate failed (%Rrc)"), vrc);
574}
575
576HRESULT UnattendedInstaller::addFilesToAuxIsoImageMaker(RTFSISOMAKER hIsoMaker, RTVFS hVfsOrgIso)
577{
578 RT_NOREF(hVfsOrgIso);
579
580 /*
581 * Add the two scripts to the image with default names.
582 */
583 HRESULT hrc = addScriptToIsoMaker(&mMainScript, hIsoMaker);
584 if (SUCCEEDED(hrc))
585 hrc = addScriptToIsoMaker(&mPostScript, hIsoMaker);
586 return hrc;
587}
588
589HRESULT UnattendedInstaller::addScriptToIsoMaker(BaseTextScript *pEditor, RTFSISOMAKER hIsoMaker,
590 const char *pszDstFilename /*= NULL*/)
591{
592 /*
593 * Calc default destination filename if desired.
594 */
595 RTCString strDstNameBuf;
596 if (!pszDstFilename)
597 {
598 try
599 {
600 strDstNameBuf = RTPATH_SLASH_STR;
601 strDstNameBuf.append(pEditor->getDefaultTemplateFilename());
602 pszDstFilename = strDstNameBuf.c_str();
603 }
604 catch (std::bad_alloc &)
605 {
606 return E_OUTOFMEMORY;
607 }
608 }
609
610 /*
611 * Create a memory file for the script.
612 */
613 Utf8Str strScript;
614 HRESULT hrc = pEditor->saveToString(strScript);
615 if (SUCCEEDED(hrc))
616 {
617 RTVFSFILE hVfsScriptFile;
618 size_t cchScript = strScript.length();
619 int vrc = RTVfsFileFromBuffer(RTFILE_O_READ, strScript.c_str(), strScript.length(), &hVfsScriptFile);
620 strScript.setNull();
621 if (RT_SUCCESS(vrc))
622 {
623 /*
624 * Add it to the ISO.
625 */
626 vrc = RTFsIsoMakerAddFileWithVfsFile(hIsoMaker, pszDstFilename, hVfsScriptFile, NULL);
627 RTVfsFileRelease(hVfsScriptFile);
628 if (RT_SUCCESS(vrc))
629 hrc = S_OK;
630 else
631 hrc = mpParent->setErrorBoth(E_FAIL, vrc,
632 tr("RTFsIsoMakerAddFileWithVfsFile failed on the script '%s' (%Rrc)"),
633 pszDstFilename, vrc);
634 }
635 else
636 hrc = mpParent->setErrorBoth(E_FAIL, vrc,
637 tr("RTVfsFileFromBuffer failed on the %zu byte script '%s' (%Rrc)", "", cchScript),
638 cchScript, pszDstFilename, vrc);
639 }
640 return hrc;
641}
642
643HRESULT UnattendedInstaller::finalizeAuxIsoImage(RTFSISOMAKER hIsoMaker, const char *pszFilename, bool fOverwrite)
644{
645 /*
646 * Finalize the image.
647 */
648 int vrc = RTFsIsoMakerFinalize(hIsoMaker);
649 if (RT_FAILURE(vrc))
650 return mpParent->setErrorBoth(E_FAIL, vrc, tr("RTFsIsoMakerFinalize failed (%Rrc)"), vrc);
651
652 /*
653 * Open the destination file.
654 */
655 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_ALL;
656 if (fOverwrite)
657 fOpen |= RTFILE_O_CREATE_REPLACE;
658 else
659 fOpen |= RTFILE_O_CREATE;
660 RTVFSFILE hVfsDstFile;
661 vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsDstFile);
662 if (RT_FAILURE(vrc))
663 {
664 if (vrc == VERR_ALREADY_EXISTS)
665 return mpParent->setErrorBoth(E_FAIL, vrc, tr("The auxiliary ISO image file '%s' already exists"),
666 pszFilename);
667 return mpParent->setErrorBoth(E_FAIL, vrc, tr("Failed to open the auxiliary ISO image file '%s' for writing (%Rrc)"),
668 pszFilename, vrc);
669 }
670
671 /*
672 * Get the source file from the image maker.
673 */
674 HRESULT hrc;
675 RTVFSFILE hVfsSrcFile;
676 vrc = RTFsIsoMakerCreateVfsOutputFile(hIsoMaker, &hVfsSrcFile);
677 if (RT_SUCCESS(vrc))
678 {
679 RTVFSIOSTREAM hVfsSrcIso = RTVfsFileToIoStream(hVfsSrcFile);
680 RTVFSIOSTREAM hVfsDstIso = RTVfsFileToIoStream(hVfsDstFile);
681 if ( hVfsSrcIso != NIL_RTVFSIOSTREAM
682 && hVfsDstIso != NIL_RTVFSIOSTREAM)
683 {
684 vrc = RTVfsUtilPumpIoStreams(hVfsSrcIso, hVfsDstIso, 0 /*cbBufHint*/);
685 if (RT_SUCCESS(vrc))
686 hrc = S_OK;
687 else
688 hrc = mpParent->setErrorBoth(E_FAIL, vrc, tr("Error writing auxiliary ISO image '%s' (%Rrc)"),
689 pszFilename, vrc);
690 }
691 else
692 hrc = mpParent->setErrorBoth(E_FAIL, VERR_INTERNAL_ERROR_2,
693 tr("Internal Error: Failed to case VFS file to VFS I/O stream"));
694 RTVfsIoStrmRelease(hVfsSrcIso);
695 RTVfsIoStrmRelease(hVfsDstIso);
696 }
697 else
698 hrc = mpParent->setErrorBoth(E_FAIL, vrc, tr("RTFsIsoMakerCreateVfsOutputFile failed (%Rrc)"), vrc);
699 RTVfsFileRelease(hVfsSrcFile);
700 RTVfsFileRelease(hVfsDstFile);
701 if (FAILED(hrc))
702 RTFileDelete(pszFilename);
703 return hrc;
704}
705
706HRESULT UnattendedInstaller::addFilesToAuxVisoVectors(RTCList<RTCString> &rVecArgs, RTCList<RTCString> &rVecFiles,
707 RTVFS hVfsOrgIso, bool fOverwrite)
708{
709 RT_NOREF(hVfsOrgIso);
710
711 /*
712 * Save and add the scripts.
713 */
714 HRESULT hrc = addScriptToVisoVectors(&mMainScript, rVecArgs, rVecFiles, fOverwrite);
715 if (SUCCEEDED(hrc))
716 hrc = addScriptToVisoVectors(&mPostScript, rVecArgs, rVecFiles, fOverwrite);
717 if (SUCCEEDED(hrc))
718 {
719 try
720 {
721 /*
722 * If we've got a Guest Additions ISO, add its content to a /vboxadditions dir.
723 */
724 if (mpParent->i_getInstallGuestAdditions())
725 {
726 rVecArgs.append().append("--push-iso=").append(mpParent->i_getAdditionsIsoPath());
727 rVecArgs.append() = "/vboxadditions=/";
728 rVecArgs.append() = "--pop";
729 }
730
731 /*
732 * If we've got a Validation Kit ISO, add its content to a /vboxvalidationkit dir.
733 */
734 if (mpParent->i_getInstallTestExecService())
735 {
736 rVecArgs.append().append("--push-iso=").append(mpParent->i_getValidationKitIsoPath());
737 rVecArgs.append() = "/vboxvalidationkit=/";
738 rVecArgs.append() = "--pop";
739 }
740 }
741 catch (std::bad_alloc &)
742 {
743 hrc = E_OUTOFMEMORY;
744 }
745 }
746 return hrc;
747}
748
749HRESULT UnattendedInstaller::addScriptToVisoVectors(BaseTextScript *pEditor, RTCList<RTCString> &rVecArgs,
750 RTCList<RTCString> &rVecFiles, bool fOverwrite)
751{
752 /*
753 * Calc the aux script file name.
754 */
755 RTCString strScriptName;
756 try
757 {
758 strScriptName = mpParent->i_getAuxiliaryBasePath();
759 strScriptName.append(pEditor->getDefaultFilename());
760 }
761 catch (std::bad_alloc &)
762 {
763 return E_OUTOFMEMORY;
764 }
765
766 /*
767 * Save it.
768 */
769 HRESULT hrc = pEditor->save(strScriptName.c_str(), fOverwrite);
770 if (SUCCEEDED(hrc))
771 {
772 /*
773 * Add it to the vectors.
774 */
775 try
776 {
777 rVecArgs.append().append('/').append(pEditor->getDefaultFilename()).append('=').append(strScriptName);
778 rVecFiles.append(strScriptName);
779 }
780 catch (std::bad_alloc &)
781 {
782 RTFileDelete(strScriptName.c_str());
783 hrc = E_OUTOFMEMORY;
784 }
785 }
786 return hrc;
787}
788
789HRESULT UnattendedInstaller::finalizeAuxVisoFile(RTCList<RTCString> const &rVecArgs, const char *pszFilename, bool fOverwrite)
790{
791 /*
792 * Create a C-style argument vector and turn that into a command line string.
793 */
794 size_t const cArgs = rVecArgs.size();
795 const char **papszArgs = (const char **)RTMemTmpAlloc((cArgs + 1) * sizeof(const char *));
796 if (!papszArgs)
797 return E_OUTOFMEMORY;
798 for (size_t i = 0; i < cArgs; i++)
799 papszArgs[i] = rVecArgs[i].c_str();
800 papszArgs[cArgs] = NULL;
801
802 char *pszCmdLine;
803 int vrc = RTGetOptArgvToString(&pszCmdLine, papszArgs, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
804 RTMemTmpFree(papszArgs);
805 if (RT_FAILURE(vrc))
806 return mpParent->setErrorBoth(E_FAIL, vrc, tr("RTGetOptArgvToString failed (%Rrc)"), vrc);
807
808 /*
809 * Open the file.
810 */
811 HRESULT hrc;
812 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_DENY_READ;
813 if (fOverwrite)
814 fOpen |= RTFILE_O_CREATE_REPLACE;
815 else
816 fOpen |= RTFILE_O_CREATE;
817 RTFILE hFile;
818 vrc = RTFileOpen(&hFile, pszFilename, fOpen);
819 if (RT_SUCCESS(vrc))
820 {
821 vrc = RTFileWrite(hFile, pszCmdLine, strlen(pszCmdLine), NULL);
822 if (RT_SUCCESS(vrc))
823 vrc = RTFileClose(hFile);
824 else
825 RTFileClose(hFile);
826 if (RT_SUCCESS(vrc))
827 hrc = S_OK;
828 else
829 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error writing '%s' (%Rrc)"), pszFilename, vrc);
830 }
831 else
832 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to create '%s' (%Rrc)"), pszFilename, vrc);
833
834 RTStrFree(pszCmdLine);
835 return hrc;
836}
837
838HRESULT UnattendedInstaller::loadAndParseFileFromIso(RTVFS hVfsOrgIso, const char *pszFilename, AbstractScript *pEditor)
839{
840 HRESULT hrc;
841 RTVFSFILE hVfsFile;
842 int vrc = RTVfsFileOpen(hVfsOrgIso, pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, &hVfsFile);
843 if (RT_SUCCESS(vrc))
844 {
845 hrc = pEditor->readFromHandle(hVfsFile, pszFilename);
846 RTVfsFileRelease(hVfsFile);
847 if (SUCCEEDED(hrc))
848 hrc = pEditor->parse();
849 }
850 else
851 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to open '%s' on the ISO '%s' (%Rrc)"),
852 pszFilename, mpParent->i_getIsoPath().c_str(), vrc);
853 return hrc;
854}
855
856
857
858/*********************************************************************************************************************************
859* Implementation of UnattendedOs2Installer *
860*********************************************************************************************************************************/
861
862UnattendedOs2Installer::UnattendedOs2Installer(Unattended *pParent, Utf8Str const &rStrHints)
863 : UnattendedInstaller(pParent,
864 "os2_response_files.rsp", "os2_postinstall.cmd",
865 "OS2.RSP", "VBOXPOST.CMD",
866 DeviceType_Floppy)
867{
868 Assert(!isOriginalIsoNeeded());
869 Assert(isAuxiliaryFloppyNeeded());
870 Assert(isAuxiliaryIsoIsVISO());
871 Assert(bootFromAuxiliaryIso());
872 mStrAuxiliaryInstallDir = "S:\\";
873
874 /* Extract the info from the hints variable: */
875 RTCList<RTCString, RTCString *> Pairs = rStrHints.split(" ");
876 size_t i = Pairs.size();
877 Assert(i > 0);
878 while (i -- > 0)
879 {
880 RTCString const rStr = Pairs[i];
881 if (rStr.startsWith("OS2SE20.SRC="))
882 mStrOs2Images = rStr.substr(sizeof("OS2SE20.SRC=") - 1);
883 else
884 AssertMsgFailed(("Unknown hint: %s\n", rStr.c_str()));
885 }
886}
887
888
889HRESULT UnattendedOs2Installer::replaceAuxFloppyImageBootSector(RTVFSFILE hVfsFile) RT_NOEXCEPT
890{
891 /*
892 * Find the bootsector. Because the ArcaOS ISOs doesn't contain any floppy
893 * images, we cannot just lift it off one of those. Instead we'll locate it
894 * in the SYSINSTX.COM utility, i.e. the tool which installs it onto floppies
895 * and harddisks. The SYSINSTX.COM utility is a NE executable and we don't
896 * have issues with compressed pages like with LX images.
897 *
898 * The utility seems always to be located on disk 0.
899 */
900 RTVFS hVfsOrgIso;
901 HRESULT hrc = openInstallIsoImage(&hVfsOrgIso);
902 if (SUCCEEDED(hrc))
903 {
904 char szPath[256];
905 int vrc = RTPathJoin(szPath, sizeof(szPath), mStrOs2Images.c_str(), "DISK_0/SYSINSTX.COM");
906 if (RT_SUCCESS(vrc))
907 {
908 RTVFSFILE hVfsSysInstX;
909 vrc = RTVfsFileOpen(hVfsOrgIso, szPath, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, &hVfsSysInstX);
910 if (RT_SUCCESS(vrc))
911 {
912 /*
913 * Scan the image looking for a 512 block ending with a DOS signature
914 * and starting with a three byte jump followed by an OEM name string.
915 */
916 uint8_t *pbBootSector = NULL;
917 RTFOFF off = 0;
918 bool fEof = false;
919 uint8_t abBuf[_8K] = {0};
920 do
921 {
922 /* Read the next chunk. */
923 memmove(abBuf, &abBuf[sizeof(abBuf) - 512], 512); /* Move up the last 512 (all zero the first time around). */
924 size_t cbRead = 0;
925 vrc = RTVfsFileReadAt(hVfsSysInstX, off, &abBuf[512], sizeof(abBuf) - 512, &cbRead);
926 if (RT_FAILURE(vrc))
927 break;
928 fEof = cbRead != sizeof(abBuf) - 512;
929 off += cbRead;
930
931 /* Scan it. */
932 size_t cbLeft = sizeof(abBuf);
933 uint8_t *pbCur = abBuf;
934 while (cbLeft >= 512)
935 {
936 /* Look for the DOS signature (0x55 0xaa) at the end of the sector: */
937 uint8_t *pbHit = (uint8_t *)memchr(pbCur + 510, 0x55, cbLeft - 510 - 1);
938 if (!pbHit)
939 break;
940 if (pbHit[1] == 0xaa)
941 {
942 uint8_t *pbStart = pbHit - 510;
943 if ( pbStart[0] == 0xeb /* JMP imm8 */
944 && pbStart[1] >= 3 + 8 + sizeof(FATEBPB) - 2 /* must jump after the FATEBPB */
945 && RT_C_IS_ALNUM(pbStart[3]) /* ASSUME OEM string starts with two letters (typically 'IBM x.y')*/
946 && RT_C_IS_ALNUM(pbStart[4]))
947 {
948 FATEBPB *pBpb = (FATEBPB *)&pbStart[3 + 8];
949 if ( pBpb->bExtSignature == FATEBPB_SIGNATURE
950 && ( memcmp(pBpb->achType, "FAT ", sizeof(pBpb->achType)) == 0
951 || memcmp(pBpb->achType, FATEBPB_TYPE_FAT12, sizeof(pBpb->achType)) == 0))
952 {
953 pbBootSector = pbStart;
954 break;
955 }
956 }
957 }
958
959 /* skip */
960 pbCur = pbHit - 510 + 1;
961 cbLeft = (uintptr_t)&abBuf[sizeof(abBuf)] - (uintptr_t)pbCur;
962 }
963 } while (!fEof);
964
965 if (pbBootSector)
966 {
967 if (pbBootSector != abBuf)
968 pbBootSector = (uint8_t *)memmove(abBuf, pbBootSector, 512);
969
970 /*
971 * We've now got a bootsector. So, we need to copy the EBPB
972 * from the destination image before replacing it.
973 */
974 vrc = RTVfsFileReadAt(hVfsFile, 0, &abBuf[512], 512, NULL);
975 if (RT_SUCCESS(vrc))
976 {
977 memcpy(&pbBootSector[3 + 8], &abBuf[512 + 3 + 8], sizeof(FATEBPB));
978
979 /*
980 * Write it.
981 */
982 vrc = RTVfsFileWriteAt(hVfsFile, 0, pbBootSector, 512, NULL);
983 if (RT_SUCCESS(vrc))
984 {
985 LogFlowFunc(("Successfully installed new bootsector\n"));
986 hrc = S_OK;
987 }
988 else
989 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to write bootsector: %Rrc"), vrc);
990 }
991 else
992 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to read bootsector: %Rrc"), vrc);
993 }
994 else if (RT_FAILURE(vrc))
995 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error reading SYSINSTX.COM: %Rrc"), vrc);
996 else
997 hrc = mpParent->setErrorBoth(E_FAIL, VERR_NOT_FOUND,
998 tr("Unable to locate bootsector template in SYSINSTX.COM"));
999 RTVfsFileRelease(hVfsSysInstX);
1000 }
1001 else
1002 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to open SYSINSTX.COM: %Rrc"), vrc);
1003 }
1004 else
1005 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to construct SYSINSTX.COM path"));
1006 RTVfsRelease(hVfsOrgIso);
1007 }
1008 return hrc;
1009
1010}
1011
1012HRESULT UnattendedOs2Installer::newAuxFloppyImage(const char *pszFilename, bool fOverwrite, PRTVFSFILE phVfsFile)
1013{
1014 /*
1015 * Open the image file.
1016 */
1017 HRESULT hrc;
1018 RTVFSFILE hVfsFile;
1019 uint64_t fOpen = RTFILE_O_READWRITE | RTFILE_O_DENY_ALL | (0660 << RTFILE_O_CREATE_MODE_SHIFT);
1020 if (fOverwrite)
1021 fOpen |= RTFILE_O_CREATE_REPLACE;
1022 else
1023 fOpen |= RTFILE_O_OPEN;
1024 int vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
1025 if (RT_SUCCESS(vrc))
1026 {
1027 /*
1028 * Format it.
1029 */
1030 vrc = RTFsFatVolFormat288(hVfsFile, false /*fQuick*/);
1031 if (RT_SUCCESS(vrc))
1032 {
1033 /*
1034 * Now we install the OS/2 boot sector on it.
1035 */
1036 hrc = replaceAuxFloppyImageBootSector(hVfsFile);
1037 if (SUCCEEDED(hrc))
1038 {
1039 *phVfsFile = hVfsFile;
1040 LogRelFlow(("UnattendedInstaller::newAuxFloppyImage: created and formatted '%s'\n", pszFilename));
1041 return S_OK;
1042 }
1043 }
1044 else
1045 hrc = mpParent->setErrorBoth(E_FAIL, vrc, tr("Failed to format floppy image '%s': %Rrc"), pszFilename, vrc);
1046 RTVfsFileRelease(hVfsFile);
1047 RTFileDelete(pszFilename);
1048 }
1049 else
1050 hrc = mpParent->setErrorBoth(E_FAIL, vrc, tr("Failed to create floppy image '%s': %Rrc"), pszFilename, vrc);
1051 return hrc;
1052}
1053
1054
1055HRESULT UnattendedOs2Installer::splitResponseFile() RT_NOEXCEPT
1056{
1057 if (mVecSplitFiles.size() == 0)
1058 {
1059#if 0
1060 Utf8Str strResponseFile;
1061 int vrc = strResponseFile.assignNoThrow(mpParent->i_getAuxiliaryBasePath());
1062 if (RT_SUCCESS(vrc))
1063 vrc = strResponseFile.appendNoThrow(mMainScript.getDefaultFilename());
1064 if (RT_SUCCESS(vrc))
1065 return splitFile(strResponseFile.c_str(), mVecSplitFiles);
1066 return mpParent->setErrorVrc(vrc);
1067#else
1068 return splitFile(&mMainScript, mVecSplitFiles);
1069#endif
1070 }
1071 return S_OK;
1072}
1073
1074HRESULT UnattendedOs2Installer::copyFilesToAuxFloppyImage(RTVFS hVfs)
1075{
1076 /*
1077 * Make sure we've split the files already.
1078 */
1079 HRESULT hrc = splitResponseFile();
1080 if (FAILED(hrc))
1081 return hrc;
1082
1083 /*
1084 * We need to copy over the files needed to boot OS/2.
1085 */
1086 static struct
1087 {
1088 bool fMandatory;
1089 const char *apszNames[2];
1090 const char *apszDisks[3];
1091 const char *pszMinVer;
1092 const char *pszMaxVer;
1093 } const s_aFiles[] =
1094 {
1095 { true, { "OS2BOOT", NULL }, { "DISK_0", NULL, NULL }, "2.1", NULL }, /* 2.0 did not have OS2BOOT */
1096 { true, { "OS2LDR", NULL }, { "DISK_0", NULL, NULL }, NULL, NULL },
1097 { true, { "OS2LDR.MSG", NULL }, { "DISK_0", NULL, NULL }, NULL, NULL },
1098 { true, { "OS2KRNL", "OS2KRNLI" }, { "DISK_0", NULL, NULL }, NULL, NULL },
1099 { true, { "OS2DUMP", NULL }, { "DISK_0", NULL, NULL }, NULL, NULL },
1100 //
1101 { true, { "ANSICALL.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1102 { true, { "BKSCALLS.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1103 { true, { "BMSCALLS.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1104 { true, { "BVHINIT.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1105 { true, { "BVSCALLS.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1106 { true, { "CDFS.IFS", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1107 { true, { "CLOCK01.SYS", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1108 { true, { "COUNT437.SYS", "COUNTRY.SYS" }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1109 { true, { "DOS.SYS", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1110 { true, { "DOSCALL1.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1111 { true, { "IBM1FLPY.ADD", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1112 { true, { "IBM1S506.ADD", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1113 { true, { "IBMIDECD.FLT", NULL }, { "DISK_1", "DISK_2", NULL }, "4.0", NULL }, /* not in 2.1 & Warp3 */
1114 { true, { "IBMKBD.SYS", "KBD01.SYS"/*?*/}, { "DISK_1", "DISK_2", NULL }, NULL, NULL},
1115 { true, { "ISAPNP.SNP", NULL }, { "DISK_1", "DISK_2", NULL }, "4.0", NULL }, /* not in 2.1 */
1116 { true, { "KBDBASE.SYS", NULL }, { "DISK_1", "DISK_2", NULL }, "3.0", NULL }, /* not in 2.1 */
1117 { true, { "KBDCALLS.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1118 { true, { "KEYBOARD.DCP", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1119 { true, { "MOUCALLS.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1120 { true, { "MSG.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1121 { true, { "NAMPIPES.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1122 { true, { "NLS.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1123 { true, { "OS2CDROM.DMD", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1124 { true, { "OS2CHAR.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1125 { true, { "OS2DASD.DMD", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1126 { true, { "OS2LVM.DMD", NULL }, { "DISK_1", "DISK_2", NULL }, "4.5", NULL },
1127 { true, { "OS2VER", NULL }, { "DISK_0", NULL, NULL }, NULL, NULL },
1128 { true, { "PNP.SYS", NULL }, { "DISK_1", "DISK_2", NULL }, "4.0", NULL },
1129 { true, { "QUECALLS.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1130 { true, { "RESOURCE.SYS", NULL }, { "DISK_1", "DISK_2", NULL }, "3.0", NULL }, /* not in 2.1*/
1131 { true, { "SCREEN01.SYS", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1132 { true, { "SESMGR.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1133 { true, { "TESTCFG.SYS", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1134 { true, { "VIO437.DCP", "VTBL850.DCP" }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1135 { true, { "VIOCALLS.DLL", NULL }, { "DISK_1", "DISK_2", NULL }, NULL, NULL },
1136 };
1137
1138
1139 RTVFS hVfsOrgIso;
1140 hrc = openInstallIsoImage(&hVfsOrgIso);
1141 if (SUCCEEDED(hrc))
1142 {
1143 for (size_t i = 0; i < RT_ELEMENTS(s_aFiles); i++)
1144 {
1145 bool fCopied = false;
1146 for (size_t iDisk = 0; iDisk < RT_ELEMENTS(s_aFiles[i].apszDisks) && s_aFiles[i].apszDisks[iDisk] && !fCopied; iDisk++)
1147 {
1148 for (size_t iName = 0; iName < RT_ELEMENTS(s_aFiles[i].apszNames) && s_aFiles[i].apszNames[iName]; iName++)
1149 {
1150 char szPath[256];
1151 int vrc = RTPathJoin(szPath, sizeof(szPath), mStrOs2Images.c_str(), s_aFiles[i].apszDisks[iDisk]);
1152 if (RT_SUCCESS(vrc))
1153 vrc = RTPathAppend(szPath, sizeof(szPath), s_aFiles[i].apszNames[iName]);
1154 AssertRCBreakStmt(vrc, hrc = mpParent->setErrorBoth(E_FAIL, vrc, tr("RTPathJoin/Append failed for %s: %Rrc"),
1155 s_aFiles[i].apszNames[iName], vrc));
1156 RTVFSFILE hVfsSrc;
1157 vrc = RTVfsFileOpen(hVfsOrgIso, szPath, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, &hVfsSrc);
1158 if (RT_SUCCESS(vrc))
1159 {
1160 RTVFSFILE hVfsDst;
1161 vrc = RTVfsFileOpen(hVfs, s_aFiles[i].apszNames[0],
1162 RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE
1163 | (0755 << RTFILE_O_CREATE_MODE_SHIFT), &hVfsDst);
1164 if (RT_SUCCESS(vrc))
1165 {
1166 RTVFSIOSTREAM hVfsIosSrc = RTVfsFileToIoStream(hVfsSrc);
1167 RTVFSIOSTREAM hVfsIosDst = RTVfsFileToIoStream(hVfsDst);
1168 vrc = RTVfsUtilPumpIoStreams(hVfsIosSrc, hVfsIosDst, 0);
1169 RTVfsIoStrmRelease(hVfsIosDst);
1170 RTVfsFileRelease(hVfsDst);
1171 RTVfsIoStrmRelease(hVfsIosSrc);
1172 if (RT_FAILURE(vrc))
1173 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to write %s to the floppy: %Rrc"),
1174 s_aFiles[i].apszNames, vrc);
1175 }
1176 else
1177 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to open %s on floppy: %Rrc"),
1178 s_aFiles[i].apszNames, vrc);
1179
1180 RTVfsFileRelease(hVfsSrc);
1181 fCopied = true;
1182 break;
1183 }
1184 }
1185 }
1186 if (FAILED(hrc))
1187 break;
1188 if (!fCopied)
1189 {
1190 /** @todo do version filtering. */
1191 hrc = mpParent->setErrorBoth(E_FAIL, VERR_FILE_NOT_FOUND,
1192 tr("Failed to locate '%s' needed for the install floppy"), s_aFiles[i].apszNames[0]);
1193 break;
1194 }
1195 }
1196 RTVfsRelease(hVfsOrgIso);
1197 }
1198
1199 /*
1200 * In addition, we need to add a CONFIG.SYS and the startup script.
1201 */
1202 if (SUCCEEDED(hrc))
1203 {
1204 Utf8Str strSrc;
1205 try
1206 {
1207 strSrc = mpParent->i_getAuxiliaryBasePath();
1208 strSrc.append("CONFIG.SYS");
1209 }
1210 catch (std::bad_alloc &)
1211 {
1212 return E_OUTOFMEMORY;
1213 }
1214 hrc = addFileToFloppyImage(hVfs, strSrc.c_str(), "CONFIG.SYS");
1215 }
1216
1217 /*
1218 * We also want a ALTF2ON.$$$ file so we can see which drivers are loaded
1219 * and where it might get stuck.
1220 */
1221 if (SUCCEEDED(hrc))
1222 {
1223 RTVFSFILE hVfsFile;
1224 int vrc = RTVfsFileOpen(hVfs, "ALTF2ON.$$$",
1225 RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE
1226 | (0755 << RTFILE_O_CREATE_MODE_SHIFT), &hVfsFile);
1227 if (RT_SUCCESS(vrc))
1228 {
1229 /** @todo buggy fat vfs: cannot write empty files */
1230 RTVfsFileWrite(hVfsFile, RT_STR_TUPLE("\r\n"), NULL);
1231 RTVfsFileRelease(hVfsFile);
1232 }
1233 else
1234 hrc = mpParent->setErrorBoth(E_FAIL, VERR_FILE_NOT_FOUND, tr("Failed to create 'ALTF2ON.$$$' on the install floppy"));
1235 }
1236
1237 return hrc;
1238}
1239
1240HRESULT UnattendedOs2Installer::addFilesToAuxVisoVectors(RTCList<RTCString> &rVecArgs, RTCList<RTCString> &rVecFiles,
1241 RTVFS hVfsOrgIso, bool fOverwrite)
1242{
1243 /*
1244 * Make sure we've split the files already.
1245 */
1246 HRESULT hrc = splitResponseFile();
1247 if (FAILED(hrc))
1248 return hrc;
1249
1250 /*
1251 * Add our stuff to the vectors.
1252 */
1253 try
1254 {
1255 /* Remaster ISO. */
1256 rVecArgs.append() = "--no-file-mode";
1257 rVecArgs.append() = "--no-dir-mode";
1258
1259 rVecArgs.append() = "--import-iso";
1260 rVecArgs.append(mpParent->i_getIsoPath());
1261
1262 rVecArgs.append() = "--file-mode=0444";
1263 rVecArgs.append() = "--dir-mode=0555";
1264
1265 /* Add the boot floppy to the ISO: */
1266 rVecArgs.append() = "--eltorito-new-entry";
1267 rVecArgs.append() = "--eltorito-add-image";
1268 rVecArgs.append(mStrAuxiliaryFloppyFilePath);
1269 rVecArgs.append() = "--eltorito-floppy-288";
1270
1271
1272 /* Add the response files and postinstall files to the ISO: */
1273 Utf8Str const &rStrAuxPrefix = mpParent->i_getAuxiliaryBasePath();
1274 size_t i = mVecSplitFiles.size();
1275 while (i-- > 0)
1276 {
1277 RTCString const &rStrFile = mVecSplitFiles[i];
1278 rVecArgs.append().assign("VBoxCID/").append(rStrFile).append('=').append(rStrAuxPrefix).append(rStrFile);
1279 }
1280 }
1281 catch (std::bad_alloc &)
1282 {
1283 return E_OUTOFMEMORY;
1284 }
1285
1286 /*
1287 * Call parent.
1288 */
1289 return UnattendedInstaller::addFilesToAuxVisoVectors(rVecArgs, rVecFiles, hVfsOrgIso, fOverwrite);
1290}
1291
1292/**
1293 * Helper for splitFile.
1294 */
1295const char *splitFileLocateSubstring(const char *pszSrc, size_t cchSrc, const char *pszSubstring, size_t cchSubstring)
1296{
1297 char const ch0 = *pszSubstring;
1298 while (cchSrc >= cchSubstring)
1299 {
1300 const char *pszHit0 = (const char *)memchr(pszSrc, ch0, cchSrc - cchSubstring + 1);
1301 if (pszHit0)
1302 {
1303 if (memcmp(pszHit0, pszSubstring, cchSubstring) == 0)
1304 return pszHit0;
1305 }
1306 else
1307 break;
1308 cchSrc -= (size_t)(pszHit0 - pszSrc) + 1;
1309 pszSrc = pszHit0 + 1;
1310 }
1311 return NULL;
1312}
1313
1314/**
1315 * Worker for splitFile().
1316 */
1317HRESULT UnattendedOs2Installer::splitFileInner(const char *pszFileToSplit, RTCList<RTCString> &rVecSplitFiles,
1318 const char *pszSrc, size_t cbLeft) RT_NOEXCEPT
1319{
1320 static const char s_szPrefix[] = "@@VBOX_SPLITTER_";
1321 const char * const pszStart = pszSrc;
1322 const char * const pszEnd = &pszSrc[cbLeft];
1323 while (cbLeft > 0)
1324 {
1325 /*
1326 * Locate the next split start marker (everything before it is ignored).
1327 */
1328 const char *pszMarker = splitFileLocateSubstring(pszSrc, cbLeft, s_szPrefix, sizeof(s_szPrefix) - 1);
1329 if (pszMarker)
1330 pszMarker += sizeof(s_szPrefix) - 1;
1331 else
1332 break;
1333 if (strncmp(pszMarker, RT_STR_TUPLE("START[")) != 0)
1334 return mpParent->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
1335 tr("Unexpected splitter tag in '%s' at offset %p: @@VBOX_SPLITTER_%.64s"),
1336 pszFileToSplit, pszMarker - pszStart, pszMarker);
1337 pszMarker += sizeof("START[") - 1;
1338 const char *pszTail = splitFileLocateSubstring(pszMarker, (size_t)(pszEnd - pszMarker), RT_STR_TUPLE("]@@"));
1339 if ( !pszTail
1340 || pszTail - pszMarker > 64
1341 || memchr(pszMarker, '\\', pszTail - pszMarker)
1342 || memchr(pszMarker, '/', pszTail - pszMarker)
1343 || memchr(pszMarker, ':', pszTail - pszMarker)
1344 )
1345 return mpParent->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
1346 tr("Malformed splitter tag in '%s' at offset %p: @@VBOX_SPLITTER_START[%.64s"),
1347 pszFileToSplit, (size_t)(pszEnd - pszMarker), pszMarker);
1348 int vrc = RTStrValidateEncodingEx(pszMarker, pszTail - pszMarker, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
1349 if (RT_FAILURE(vrc))
1350 return mpParent->setErrorBoth(E_FAIL, vrc,
1351 tr("Malformed splitter tag in '%s' at offset %p: @@VBOX_SPLITTER_START[%.*Rhxs"),
1352 pszFileToSplit, (size_t)(pszEnd - pszMarker), pszTail - pszMarker, pszMarker);
1353 const char *pszFilename;
1354 try
1355 {
1356 pszFilename = rVecSplitFiles.append().assign(pszMarker, pszTail - pszMarker).c_str();
1357 }
1358 catch (std::bad_alloc &)
1359 {
1360 return E_OUTOFMEMORY;
1361 }
1362 const char *pszDocStart = pszTail + sizeof("]@@") - 1;
1363 while (RT_C_IS_SPACE(*pszDocStart))
1364 if (*pszDocStart++ == '\n')
1365 break;
1366
1367 /* Advance. */
1368 pszSrc = pszDocStart;
1369 cbLeft = pszEnd - pszDocStart;
1370
1371 /*
1372 * Locate the matching end marker (there cannot be any other markers inbetween).
1373 */
1374 const char * const pszDocEnd = pszMarker = splitFileLocateSubstring(pszSrc, cbLeft, s_szPrefix, sizeof(s_szPrefix) - 1);
1375 if (pszMarker)
1376 pszMarker += sizeof(s_szPrefix) - 1;
1377 else
1378 return mpParent->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
1379 tr("No END splitter tag for '%s' in '%s'"), pszFilename, pszFileToSplit);
1380 if (strncmp(pszMarker, RT_STR_TUPLE("END[")) != 0)
1381 return mpParent->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
1382 tr("Unexpected splitter tag in '%s' at offset %p: @@VBOX_SPLITTER_%.64s"),
1383 pszFileToSplit, (size_t)(pszEnd - pszMarker), pszMarker);
1384 pszMarker += sizeof("END[") - 1;
1385 size_t const cchFilename = strlen(pszFilename);
1386 if ( strncmp(pszMarker, pszFilename, cchFilename) != 0
1387 || pszMarker[cchFilename] != ']'
1388 || pszMarker[cchFilename + 1] != '@'
1389 || pszMarker[cchFilename + 2] != '@')
1390 return mpParent->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
1391 tr("Mismatching splitter tag for '%s' in '%s' at offset %p: @@VBOX_SPLITTER_END[%.64Rhxs"),
1392 pszFilename, pszFileToSplit, (size_t)(pszEnd - pszMarker), pszMarker);
1393
1394 /* Advance. */
1395 pszSrc = pszMarker + cchFilename + sizeof("]@@") - 1;
1396 cbLeft = (size_t)(pszEnd - pszSrc);
1397
1398 /*
1399 * Write out the file.
1400 */
1401 Utf8Str strDstFilename;
1402 vrc = strDstFilename.assignNoThrow(mpParent->i_getAuxiliaryBasePath());
1403 if (RT_SUCCESS(vrc))
1404 vrc = strDstFilename.appendNoThrow(pszFilename);
1405 if (RT_SUCCESS(vrc))
1406 {
1407 RTFILE hFile;
1408 vrc = RTFileOpen(&hFile, strDstFilename.c_str(), RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
1409 if (RT_SUCCESS(vrc))
1410 {
1411 vrc = RTFileWrite(hFile, pszDocStart, (size_t)(pszDocEnd - pszDocStart), NULL);
1412 if (RT_SUCCESS(vrc))
1413 vrc = RTFileClose(hFile);
1414 else
1415 RTFileClose(hFile);
1416 if (RT_FAILURE(vrc))
1417 return mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error writing '%s' (split out from '%s'): %Rrc"),
1418 strDstFilename.c_str(), pszFileToSplit, vrc);
1419 }
1420 else
1421 return mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc,
1422 tr("File splitter failed to open output file '%s' in '%s': %Rrc (%s)"),
1423 pszFilename, pszFileToSplit, vrc, strDstFilename.c_str());
1424 }
1425 else
1426 return mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc,
1427 tr("File splitter failed to construct path for '%s' in '%s': %Rrc"),
1428 pszFilename, pszFileToSplit, vrc);
1429 }
1430
1431 return S_OK;
1432}
1433
1434HRESULT UnattendedOs2Installer::splitFile(const char *pszFileToSplit, RTCList<RTCString> &rVecSplitFiles) RT_NOEXCEPT
1435{
1436 /*
1437 * Read the whole source file into memory, making sure it's zero terminated.
1438 */
1439 HRESULT hrc;
1440 void *pvSrc;
1441 size_t cbSrc;
1442 int vrc = RTFileReadAllEx(pszFileToSplit, 0 /*off*/, _16M /*cbMax*/,
1443 RTFILE_RDALL_F_TRAILING_ZERO_BYTE | RTFILE_RDALL_F_FAIL_ON_MAX_SIZE | RTFILE_RDALL_O_DENY_WRITE,
1444 &pvSrc, &cbSrc);
1445 if (RT_SUCCESS(vrc))
1446 {
1447 /*
1448 * Do the actual splitting in a worker function to avoid needing to
1449 * thing about calling RTFileReadAllFree in error paths.
1450 */
1451 hrc = splitFileInner(pszFileToSplit, rVecSplitFiles, (const char *)pvSrc, cbSrc);
1452 RTFileReadAllFree(pvSrc, cbSrc);
1453 }
1454 else
1455 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to read '%s' for splitting up: %Rrc"),
1456 pszFileToSplit, vrc);
1457 return hrc;
1458}
1459
1460HRESULT UnattendedOs2Installer::splitFile(BaseTextScript *pEditor, RTCList<RTCString> &rVecSplitFiles) RT_NOEXCEPT
1461{
1462 /*
1463 * Get the output from the editor.
1464 */
1465 Utf8Str strSrc;
1466 HRESULT hrc = pEditor->saveToString(strSrc);
1467 if (SUCCEEDED(hrc))
1468 {
1469 /*
1470 * Do the actual splitting.
1471 */
1472 hrc = splitFileInner(pEditor->getDefaultFilename(), rVecSplitFiles, strSrc.c_str(), strSrc.length());
1473 }
1474 return hrc;
1475}
1476
1477
1478
1479//////////////////////////////////////////////////////////////////////////////////////////////////////
1480/*
1481*
1482*
1483* Implementation UnattendedLinuxInstaller functions
1484*
1485*/
1486//////////////////////////////////////////////////////////////////////////////////////////////////////
1487HRESULT UnattendedLinuxInstaller::editIsoLinuxCfg(GeneralTextScript *pEditor)
1488{
1489 try
1490 {
1491 /* Set timeouts to 10 seconds. */
1492 std::vector<size_t> vecLineNumbers = pEditor->findTemplate("timeout", RTCString::CaseInsensitive);
1493 for (size_t i = 0; i < vecLineNumbers.size(); ++i)
1494 if (pEditor->getContentOfLine(vecLineNumbers[i]).startsWithWord("timeout", RTCString::CaseInsensitive))
1495 {
1496 HRESULT hrc = pEditor->setContentOfLine(vecLineNumbers.at(i), "timeout 10");
1497 if (FAILED(hrc))
1498 return hrc;
1499 }
1500
1501 /* Comment out 'display <filename>' directives that's used for displaying files at boot time. */
1502 vecLineNumbers = pEditor->findTemplate("display", RTCString::CaseInsensitive);
1503 for (size_t i = 0; i < vecLineNumbers.size(); ++i)
1504 if (pEditor->getContentOfLine(vecLineNumbers[i]).startsWithWord("display", RTCString::CaseInsensitive))
1505 {
1506 HRESULT hrc = pEditor->prependToLine(vecLineNumbers.at(i), "#");
1507 if (FAILED(hrc))
1508 return hrc;
1509 }
1510
1511 /* Modify kernel parameters. */
1512 vecLineNumbers = pEditor->findTemplate("append", RTCString::CaseInsensitive);
1513 if (vecLineNumbers.size() > 0)
1514 {
1515 Utf8Str const &rStrAppend = mpParent->i_getExtraInstallKernelParameters().isNotEmpty()
1516 ? mpParent->i_getExtraInstallKernelParameters()
1517 : mStrDefaultExtraInstallKernelParameters;
1518
1519 for (size_t i = 0; i < vecLineNumbers.size(); ++i)
1520 if (pEditor->getContentOfLine(vecLineNumbers[i]).startsWithWord("append", RTCString::CaseInsensitive))
1521 {
1522 Utf8Str strLine = pEditor->getContentOfLine(vecLineNumbers[i]);
1523
1524 /* Do removals. */
1525 if (mArrStrRemoveInstallKernelParameters.size() > 0)
1526 {
1527 size_t offStart = strLine.find("append") + 5;
1528 while (offStart < strLine.length() && !RT_C_IS_SPACE(strLine[offStart]))
1529 offStart++;
1530 while (offStart < strLine.length() && RT_C_IS_SPACE(strLine[offStart]))
1531 offStart++;
1532 if (offStart < strLine.length())
1533 {
1534 for (size_t iRemove = 0; iRemove < mArrStrRemoveInstallKernelParameters.size(); iRemove++)
1535 {
1536 RTCString const &rStrRemove = mArrStrRemoveInstallKernelParameters[iRemove];
1537 for (size_t off = offStart; off < strLine.length(); )
1538 {
1539 Assert(!RT_C_IS_SPACE(strLine[off]));
1540
1541 /* Find the end of word. */
1542 size_t offEnd = off + 1;
1543 while (offEnd < strLine.length() && !RT_C_IS_SPACE(strLine[offEnd]))
1544 offEnd++;
1545
1546 /* Check if it matches. */
1547 if (RTStrSimplePatternNMatch(rStrRemove.c_str(), rStrRemove.length(),
1548 strLine.c_str() + off, offEnd - off))
1549 {
1550 while (off > 0 && RT_C_IS_SPACE(strLine[off - 1]))
1551 off--;
1552 strLine.erase(off, offEnd - off);
1553 }
1554
1555 /* Advance to the next word. */
1556 off = offEnd;
1557 while (off < strLine.length() && RT_C_IS_SPACE(strLine[off]))
1558 off++;
1559 }
1560 }
1561 }
1562 }
1563
1564 /* Do the appending. */
1565 if (rStrAppend.isNotEmpty())
1566 {
1567 if (!rStrAppend.startsWith(" ") && !strLine.endsWith(" "))
1568 strLine.append(' ');
1569 strLine.append(rStrAppend);
1570 }
1571
1572 /* Update line. */
1573 HRESULT hrc = pEditor->setContentOfLine(vecLineNumbers.at(i), strLine);
1574 if (FAILED(hrc))
1575 return hrc;
1576 }
1577 }
1578 }
1579 catch (std::bad_alloc &)
1580 {
1581 return E_OUTOFMEMORY;
1582 }
1583 return S_OK;
1584}
1585
1586
1587//////////////////////////////////////////////////////////////////////////////////////////////////////
1588/*
1589*
1590*
1591* Implementation UnattendedDebianInstaller functions
1592*
1593*/
1594//////////////////////////////////////////////////////////////////////////////////////////////////////
1595
1596/**
1597 * Helper for checking if a file exists.
1598 * @todo promote to IPRT?
1599 */
1600static bool hlpVfsFileExists(RTVFS hVfs, const char *pszPath)
1601{
1602 RTFSOBJINFO ObjInfo;
1603 int vrc = RTVfsQueryPathInfo(hVfs, pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
1604 return RT_SUCCESS(vrc) && RTFS_IS_FILE(ObjInfo.Attr.fMode);
1605}
1606
1607HRESULT UnattendedDebianInstaller::addFilesToAuxVisoVectors(RTCList<RTCString> &rVecArgs, RTCList<RTCString> &rVecFiles,
1608 RTVFS hVfsOrgIso, bool fOverwrite)
1609{
1610 /*
1611 * The txt.cfg file used to be called isolinux.txt (ubuntu 4.10
1612 * and possible others).
1613 */
1614 /** @todo Ubuntu 4.10 does not work, as we generate too long command lines
1615 * and the kernel crashes immediately. */
1616 const char *pszIsoLinuxTxtCfg = "/isolinux/txt.cfg";
1617 if ( !hlpVfsFileExists(hVfsOrgIso, pszIsoLinuxTxtCfg)
1618 && hlpVfsFileExists(hVfsOrgIso, "/isolinux/isolinux.txt"))
1619 pszIsoLinuxTxtCfg = "/isolinux/isolinux.txt";
1620
1621 /*
1622 * VISO bits and filenames.
1623 */
1624 RTCString strIsoLinuxCfg;
1625 RTCString strTxtCfg;
1626 try
1627 {
1628 /* Remaster ISO. */
1629 rVecArgs.append() = "--no-file-mode";
1630 rVecArgs.append() = "--no-dir-mode";
1631
1632 rVecArgs.append() = "--import-iso";
1633 rVecArgs.append(mpParent->i_getIsoPath());
1634
1635 rVecArgs.append() = "--file-mode=0444";
1636 rVecArgs.append() = "--dir-mode=0555";
1637
1638 /* Remove the two isolinux configure files we'll be replacing. */
1639 rVecArgs.append() = "isolinux/isolinux.cfg=:must-remove:";
1640 rVecArgs.append().assign(&pszIsoLinuxTxtCfg[1]).append("=:must-remove:");
1641
1642 /* Add the replacement files. */
1643 strIsoLinuxCfg = mpParent->i_getAuxiliaryBasePath();
1644 strIsoLinuxCfg.append("isolinux-isolinux.cfg");
1645 rVecArgs.append().append("isolinux/isolinux.cfg=").append(strIsoLinuxCfg);
1646
1647 strTxtCfg = mpParent->i_getAuxiliaryBasePath();
1648 strTxtCfg.append("isolinux-txt.cfg");
1649 rVecArgs.append().assign(&pszIsoLinuxTxtCfg[1]).append("=").append(strTxtCfg);
1650 }
1651 catch (std::bad_alloc &)
1652 {
1653 return E_OUTOFMEMORY;
1654 }
1655
1656 /*
1657 * Edit the isolinux.cfg file.
1658 */
1659 {
1660 GeneralTextScript Editor(mpParent);
1661 HRESULT hrc = loadAndParseFileFromIso(hVfsOrgIso, "/isolinux/isolinux.cfg", &Editor);
1662 if (SUCCEEDED(hrc))
1663 hrc = editIsoLinuxCfg(&Editor);
1664 if (SUCCEEDED(hrc))
1665 {
1666 hrc = Editor.save(strIsoLinuxCfg, fOverwrite);
1667 if (SUCCEEDED(hrc))
1668 {
1669 try
1670 {
1671 rVecFiles.append(strIsoLinuxCfg);
1672 }
1673 catch (std::bad_alloc &)
1674 {
1675 RTFileDelete(strIsoLinuxCfg.c_str());
1676 hrc = E_OUTOFMEMORY;
1677 }
1678 }
1679 }
1680 if (FAILED(hrc))
1681 return hrc;
1682 }
1683
1684 /*
1685 * Edit the txt.cfg file.
1686 */
1687 {
1688 GeneralTextScript Editor(mpParent);
1689 HRESULT hrc = loadAndParseFileFromIso(hVfsOrgIso, pszIsoLinuxTxtCfg, &Editor);
1690 if (SUCCEEDED(hrc))
1691 hrc = editDebianTxtCfg(&Editor);
1692 if (SUCCEEDED(hrc))
1693 {
1694 hrc = Editor.save(strTxtCfg, fOverwrite);
1695 if (SUCCEEDED(hrc))
1696 {
1697 try
1698 {
1699 rVecFiles.append(strTxtCfg);
1700 }
1701 catch (std::bad_alloc &)
1702 {
1703 RTFileDelete(strTxtCfg.c_str());
1704 hrc = E_OUTOFMEMORY;
1705 }
1706 }
1707 }
1708 if (FAILED(hrc))
1709 return hrc;
1710 }
1711
1712 /*
1713 * Call parent to add the preseed file from mAlg.
1714 */
1715 return UnattendedLinuxInstaller::addFilesToAuxVisoVectors(rVecArgs, rVecFiles, hVfsOrgIso, fOverwrite);
1716}
1717
1718HRESULT UnattendedDebianInstaller::editDebianTxtCfg(GeneralTextScript *pEditor)
1719{
1720 try
1721 {
1722 /** @todo r=bird: Add some comments saying wtf you're actually up to here.
1723 * Repeating what's clear from function calls and boasting the
1724 * inteligence of the code isn't helpful. */
1725 //find all lines with "label" inside
1726 std::vector<size_t> vecLineNumbers = pEditor->findTemplate("label", RTCString::CaseInsensitive);
1727 for (size_t i = 0; i < vecLineNumbers.size(); ++i)
1728 {
1729 RTCString const &rContent = pEditor->getContentOfLine(vecLineNumbers[i]);
1730
1731 // ASSUME: suppose general string looks like "label install", two words separated by " ".
1732 RTCList<RTCString> vecPartsOfcontent = rContent.split(" ");
1733 if (vecPartsOfcontent.size() > 1 && vecPartsOfcontent[1].contains("install")) /** @todo r=bird: case insensitive? */
1734 {
1735 std::vector<size_t> vecDefaultLineNumbers = pEditor->findTemplate("default", RTCString::CaseInsensitive);
1736 //handle the lines more intelligently
1737 for (size_t j = 0; j < vecDefaultLineNumbers.size(); ++j)
1738 {
1739 Utf8Str strNewContent("default ");
1740 strNewContent.append(vecPartsOfcontent[1]);
1741 HRESULT hrc = pEditor->setContentOfLine(vecDefaultLineNumbers[j], strNewContent);
1742 if (FAILED(hrc))
1743 return hrc;
1744 }
1745 }
1746 }
1747 }
1748 catch (std::bad_alloc &)
1749 {
1750 return E_OUTOFMEMORY;
1751 }
1752 return UnattendedLinuxInstaller::editIsoLinuxCfg(pEditor);
1753}
1754
1755
1756//////////////////////////////////////////////////////////////////////////////////////////////////////
1757/*
1758*
1759*
1760* Implementation UnattendedRhel6Installer functions
1761*
1762*/
1763//////////////////////////////////////////////////////////////////////////////////////////////////////
1764HRESULT UnattendedRhel6Installer::addFilesToAuxVisoVectors(RTCList<RTCString> &rVecArgs, RTCList<RTCString> &rVecFiles,
1765 RTVFS hVfsOrgIso, bool fOverwrite)
1766{
1767 Utf8Str strIsoLinuxCfg;
1768 try
1769 {
1770#if 1
1771 /* Remaster ISO. */
1772 rVecArgs.append() = "--no-file-mode";
1773 rVecArgs.append() = "--no-dir-mode";
1774
1775 rVecArgs.append() = "--import-iso";
1776 rVecArgs.append(mpParent->i_getIsoPath());
1777
1778 rVecArgs.append() = "--file-mode=0444";
1779 rVecArgs.append() = "--dir-mode=0555";
1780
1781 /* We replace isolinux.cfg with our edited version (see further down). */
1782 rVecArgs.append() = "isolinux/isolinux.cfg=:must-remove:";
1783 strIsoLinuxCfg = mpParent->i_getAuxiliaryBasePath();
1784 strIsoLinuxCfg.append("isolinux-isolinux.cfg");
1785 rVecArgs.append().append("isolinux/isolinux.cfg=").append(strIsoLinuxCfg);
1786
1787#else
1788 /** @todo Maybe we should just remaster the ISO for redhat derivatives too?
1789 * One less CDROM to mount. */
1790 /* Name the ISO. */
1791 rVecArgs.append() = "--volume-id=VBox Unattended Boot";
1792
1793 /* Copy the isolinux directory from the original install ISO. */
1794 rVecArgs.append().append("--push-iso=").append(mpParent->i_getIsoPath());
1795 rVecArgs.append() = "/isolinux=/isolinux";
1796 rVecArgs.append() = "--pop";
1797
1798 /* We replace isolinux.cfg with our edited version (see further down). */
1799 rVecArgs.append() = "/isolinux/isolinux.cfg=:must-remove:";
1800
1801 strIsoLinuxCfg = mpParent->i_getAuxiliaryBasePath();
1802 strIsoLinuxCfg.append("isolinux-isolinux.cfg");
1803 rVecArgs.append().append("/isolinux/isolinux.cfg=").append(strIsoLinuxCfg);
1804
1805 /* Configure booting /isolinux/isolinux.bin. */
1806 rVecArgs.append() = "--eltorito-boot";
1807 rVecArgs.append() = "/isolinux/isolinux.bin";
1808 rVecArgs.append() = "--no-emulation-boot";
1809 rVecArgs.append() = "--boot-info-table";
1810 rVecArgs.append() = "--boot-load-seg=0x07c0";
1811 rVecArgs.append() = "--boot-load-size=4";
1812
1813 /* Make the boot catalog visible in the file system. */
1814 rVecArgs.append() = "--boot-catalog=/isolinux/vboxboot.cat";
1815#endif
1816 }
1817 catch (std::bad_alloc &)
1818 {
1819 return E_OUTOFMEMORY;
1820 }
1821
1822 /*
1823 * Edit isolinux.cfg and save it.
1824 */
1825 {
1826 GeneralTextScript Editor(mpParent);
1827 HRESULT hrc = loadAndParseFileFromIso(hVfsOrgIso, "/isolinux/isolinux.cfg", &Editor);
1828 if (SUCCEEDED(hrc))
1829 hrc = editIsoLinuxCfg(&Editor);
1830 if (SUCCEEDED(hrc))
1831 {
1832 hrc = Editor.save(strIsoLinuxCfg, fOverwrite);
1833 if (SUCCEEDED(hrc))
1834 {
1835 try
1836 {
1837 rVecFiles.append(strIsoLinuxCfg);
1838 }
1839 catch (std::bad_alloc &)
1840 {
1841 RTFileDelete(strIsoLinuxCfg.c_str());
1842 hrc = E_OUTOFMEMORY;
1843 }
1844 }
1845 }
1846 if (FAILED(hrc))
1847 return hrc;
1848 }
1849
1850 /*
1851 * Call parent to add the ks.cfg file from mAlg.
1852 */
1853 return UnattendedLinuxInstaller::addFilesToAuxVisoVectors(rVecArgs, rVecFiles, hVfsOrgIso, fOverwrite);
1854}
1855
1856
1857//////////////////////////////////////////////////////////////////////////////////////////////////////
1858/*
1859*
1860*
1861* Implementation UnattendedSuseInstaller functions
1862*
1863*/
1864//////////////////////////////////////////////////////////////////////////////////////////////////////
1865#if 0 /* doesn't work, so convert later */
1866/*
1867 *
1868 * UnattendedSuseInstaller protected methods
1869 *
1870*/
1871HRESULT UnattendedSuseInstaller::setUserData()
1872{
1873 HRESULT rc = S_OK;
1874 //here base class function must be called first
1875 //because user home directory is set after user name
1876 rc = UnattendedInstaller::setUserData();
1877
1878 rc = mAlg->setField(USERHOMEDIR_ID, "");
1879 if (FAILED(rc))
1880 return rc;
1881
1882 return rc;
1883}
1884
1885/*
1886 *
1887 * UnattendedSuseInstaller private methods
1888 *
1889*/
1890
1891HRESULT UnattendedSuseInstaller::iv_initialPhase()
1892{
1893 Assert(isAuxiliaryIsoNeeded());
1894 if (mParent->i_isGuestOs64Bit())
1895 mFilesAndDirsToExtractFromIso.append("boot/x86_64/loader/ ");
1896 else
1897 mFilesAndDirsToExtractFromIso.append("boot/i386/loader/ ");
1898 return extractOriginalIso(mFilesAndDirsToExtractFromIso);
1899}
1900
1901
1902HRESULT UnattendedSuseInstaller::setupScriptOnAuxiliaryCD(const Utf8Str &path)
1903{
1904 HRESULT rc = S_OK;
1905
1906 GeneralTextScript isoSuseCfgScript(mpParent);
1907 rc = isoSuseCfgScript.read(path);
1908 rc = isoSuseCfgScript.parse();
1909 //fix linux core bootable parameters: add path to the preseed script
1910
1911 std::vector<size_t> listOfLines = isoSuseCfgScript.findTemplate("append");
1912 for(unsigned int i=0; i<listOfLines.size(); ++i)
1913 {
1914 isoSuseCfgScript.appendToLine(listOfLines.at(i),
1915 " auto=true priority=critical autoyast=default instmode=cd quiet splash noprompt noshell --");
1916 }
1917
1918 //find all lines with "label" inside
1919 listOfLines = isoSuseCfgScript.findTemplate("label");
1920 for(unsigned int i=0; i<listOfLines.size(); ++i)
1921 {
1922 Utf8Str content = isoSuseCfgScript.getContentOfLine(listOfLines.at(i));
1923
1924 //suppose general string looks like "label linux", two words separated by " ".
1925 RTCList<RTCString> partsOfcontent = content.split(" ");
1926
1927 if (partsOfcontent.at(1).contains("linux"))
1928 {
1929 std::vector<size_t> listOfDefault = isoSuseCfgScript.findTemplate("default");
1930 //handle the lines more intelligently
1931 for(unsigned int j=0; j<listOfDefault.size(); ++j)
1932 {
1933 Utf8Str newContent("default ");
1934 newContent.append(partsOfcontent.at(1));
1935 isoSuseCfgScript.setContentOfLine(listOfDefault.at(j), newContent);
1936 }
1937 }
1938 }
1939
1940 rc = isoSuseCfgScript.save(path, true);
1941
1942 LogRelFunc(("UnattendedSuseInstaller::setupScriptsOnAuxiliaryCD(): The file %s has been changed\n", path.c_str()));
1943
1944 return rc;
1945}
1946#endif
1947
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