VirtualBox

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

Last change on this file since 102342 was 102342, checked in by vboxsync, 18 months ago

Main/Unattended: Implemented support for subiquity-/cloud-init-based installers. This enables installing guest newer Linux-based OSes like Ubuntu >= 22.10 in Unattended mode. More detection code for other variants will follow. Extended testcases [build fix, Windows installer does not accept dashes (-) as file keys]. bugref:10551

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 63.2 KB
Line 
1/* $Id: UnattendedInstaller.cpp 102342 2023-11-27 17:56:10Z vboxsync $ */
2/** @file
3 * UnattendedInstaller class and it's descendants implementation
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.215389.xyz.
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/getopt.h>
48#include <iprt/file.h>
49#include <iprt/path.h>
50#include <iprt/rand.h>
51#include <iprt/sha.h>
52#include <iprt/stream.h>
53#include <iprt/vfs.h>
54#ifdef RT_OS_SOLARIS
55# undef ES /* Workaround for someone dragging the namespace pollutor sys/regset.h. Sigh. */
56#endif
57#include <iprt/formats/iso9660.h>
58#include <iprt/cpp/path.h>
59
60
61using namespace std;
62
63
64/* static */ UnattendedInstaller *
65UnattendedInstaller::createInstance(VBOXOSTYPE enmDetectedOSType, const Utf8Str &strDetectedOSType,
66 const Utf8Str &strDetectedOSVersion, const Utf8Str &strDetectedOSFlavor,
67 const Utf8Str &strDetectedOSHints, Unattended *pParent)
68{
69 UnattendedInstaller *pUinstaller = NULL;
70
71 if (strDetectedOSType.find("Windows") != RTCString::npos)
72 {
73 if (enmDetectedOSType >= VBOXOSTYPE_WinVista)
74 pUinstaller = new UnattendedWindowsXmlInstaller(pParent);
75 else
76 pUinstaller = new UnattendedWindowsSifInstaller(pParent);
77 }
78 else if (enmDetectedOSType >= VBOXOSTYPE_OS2 && enmDetectedOSType < VBOXOSTYPE_Linux)
79 pUinstaller = new UnattendedOs2Installer(pParent, strDetectedOSHints);
80 else
81 {
82 if ( enmDetectedOSType >= VBOXOSTYPE_Debian
83 && ( enmDetectedOSType <= VBOXOSTYPE_Debian_latest_x64
84 || enmDetectedOSType <= VBOXOSTYPE_Debian_latest_arm64))
85 pUinstaller = new UnattendedDebianInstaller(pParent);
86 else if ( enmDetectedOSType >= VBOXOSTYPE_Ubuntu
87 && ( enmDetectedOSType <= VBOXOSTYPE_Ubuntu_latest_x64
88 || enmDetectedOSType <= VBOXOSTYPE_Ubuntu_latest_arm64))
89 {
90 /*
91 * Here we have to decide, based on the Ubuntu version, which exact installer flavor we have to use:
92 * - The preseed installer for older Ubuntu distros, or
93 * - The autoinstall installer for newer Ubuntu desktop or Ubuntu server versions.
94 */
95 if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "22.10") >= 0)
96 pUinstaller = new UnattendedUbuntuAutoInstallInstaller(pParent);
97 /// @todo Check for Server >= 20.04 and others.
98 else
99 pUinstaller = new UnattendedUbuntuPreseedInstaller(pParent);
100 }
101 else if (enmDetectedOSType >= VBOXOSTYPE_RedHat && enmDetectedOSType <= VBOXOSTYPE_RedHat_latest_x64)
102 {
103 if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "8") >= 0)
104 pUinstaller = new UnattendedRhel8Installer(pParent);
105 else if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "7") >= 0)
106 pUinstaller = new UnattendedRhel7Installer(pParent);
107 else if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "6") >= 0)
108 pUinstaller = new UnattendedRhel6Installer(pParent);
109 else if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "5") >= 0)
110 pUinstaller = new UnattendedRhel5Installer(pParent);
111 else if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "4") >= 0)
112 pUinstaller = new UnattendedRhel4Installer(pParent);
113 else if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "3") >= 0)
114 pUinstaller = new UnattendedRhel3Installer(pParent);
115 else
116 pUinstaller = new UnattendedRhel6Installer(pParent);
117 }
118 else if (enmDetectedOSType >= VBOXOSTYPE_FedoraCore && enmDetectedOSType <= VBOXOSTYPE_FedoraCore_x64)
119 pUinstaller = new UnattendedFedoraInstaller(pParent);
120 else if ( enmDetectedOSType >= VBOXOSTYPE_Oracle
121 && ( enmDetectedOSType <= VBOXOSTYPE_Oracle_latest_x64
122 || enmDetectedOSType <= VBOXOSTYPE_Oracle_latest_arm64))
123 {
124 if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "9") >= 0)
125 pUinstaller = new UnattendedOracleLinux9Installer(pParent);
126 else if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "8") >= 0)
127 pUinstaller = new UnattendedOracleLinux8Installer(pParent);
128 else if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "7") >= 0)
129 pUinstaller = new UnattendedOracleLinux7Installer(pParent);
130 else if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "6") >= 0)
131 pUinstaller = new UnattendedOracleLinux6Installer(pParent);
132 else
133 pUinstaller = new UnattendedOracleLinux6Installer(pParent);
134 }
135 else if ( enmDetectedOSType >= VBOXOSTYPE_FreeBSD
136 && ( enmDetectedOSType <= VBOXOSTYPE_FreeBSD_x64
137 || enmDetectedOSType <= VBOXOSTYPE_FreeBSD_arm64))
138 pUinstaller = new UnattendedFreeBsdInstaller(pParent);
139#if 0 /* doesn't work, so convert later. */
140 else if (enmDetectedOSType == VBOXOSTYPE_OpenSUSE || enmDetectedOSType == VBOXOSTYPE_OpenSUSE_x64)
141 pUinstaller = new UnattendedSuseInstaller(new UnattendedSUSEXMLScript(pParent), pParent);
142#endif
143 }
144 RT_NOREF_PV(strDetectedOSFlavor);
145 RT_NOREF_PV(strDetectedOSHints);
146 return pUinstaller;
147}
148
149
150//////////////////////////////////////////////////////////////////////////////////////////////////////
151/*
152*
153*
154* Implementation Unattended functions
155*
156*/
157//////////////////////////////////////////////////////////////////////////////////////////////////////
158
159/*
160 *
161 * UnattendedInstaller public methods
162 *
163 */
164UnattendedInstaller::UnattendedInstaller(Unattended *pParent,
165 const char *pszMainScriptTemplateName, const char *pszPostScriptTemplateName,
166 const char *pszMainScriptFilename, const char *pszPostScriptFilename,
167 DeviceType_T enmBootDevice /*= DeviceType_DVD */)
168 : mMainScript(pParent, pszMainScriptTemplateName, pszMainScriptFilename)
169 , mPostScript(pParent, pszPostScriptTemplateName, pszPostScriptFilename)
170 , mpParent(pParent)
171 , meBootDevice(enmBootDevice)
172{
173 AssertPtr(pParent);
174 Assert(*pszMainScriptTemplateName);
175 Assert(*pszMainScriptFilename);
176 Assert(*pszPostScriptTemplateName);
177 Assert(*pszPostScriptFilename);
178 Assert(enmBootDevice == DeviceType_DVD || enmBootDevice == DeviceType_Floppy);
179}
180
181UnattendedInstaller::~UnattendedInstaller()
182{
183 mpParent = NULL;
184}
185
186HRESULT UnattendedInstaller::initInstaller()
187{
188 /*
189 * Calculate the full main script template location.
190 */
191 if (mpParent->i_getScriptTemplatePath().isNotEmpty())
192 mStrMainScriptTemplate = mpParent->i_getScriptTemplatePath();
193 else
194 {
195 int vrc = RTPathAppPrivateNoArchCxx(mStrMainScriptTemplate);
196 if (RT_SUCCESS(vrc))
197 vrc = RTPathAppendCxx(mStrMainScriptTemplate, "UnattendedTemplates");
198 if (RT_SUCCESS(vrc))
199 vrc = RTPathAppendCxx(mStrMainScriptTemplate, mMainScript.getDefaultTemplateFilename());
200 if (RT_FAILURE(vrc))
201 return mpParent->setErrorBoth(E_FAIL, vrc,
202 tr("Failed to construct path to the unattended installer script templates (%Rrc)"),
203 vrc);
204 }
205
206 /*
207 * Calculate the full post script template location.
208 */
209 if (mpParent->i_getPostInstallScriptTemplatePath().isNotEmpty())
210 mStrPostScriptTemplate = mpParent->i_getPostInstallScriptTemplatePath();
211 else
212 {
213 int vrc = RTPathAppPrivateNoArchCxx(mStrPostScriptTemplate);
214 if (RT_SUCCESS(vrc))
215 vrc = RTPathAppendCxx(mStrPostScriptTemplate, "UnattendedTemplates");
216 if (RT_SUCCESS(vrc))
217 vrc = RTPathAppendCxx(mStrPostScriptTemplate, mPostScript.getDefaultTemplateFilename());
218 if (RT_FAILURE(vrc))
219 return mpParent->setErrorBoth(E_FAIL, vrc,
220 tr("Failed to construct path to the unattended installer script templates (%Rrc)"),
221 vrc);
222 }
223
224 /*
225 * Construct paths we need.
226 */
227 if (isAuxiliaryFloppyNeeded())
228 {
229 mStrAuxiliaryFloppyFilePath = mpParent->i_getAuxiliaryBasePath();
230 mStrAuxiliaryFloppyFilePath.append("aux-floppy.img");
231 }
232 if (isAuxiliaryIsoNeeded())
233 {
234 mStrAuxiliaryIsoFilePath = mpParent->i_getAuxiliaryBasePath();
235 if (!isAuxiliaryIsoIsVISO())
236 mStrAuxiliaryIsoFilePath.append("aux-iso.iso");
237 else
238 mStrAuxiliaryIsoFilePath.append("aux-iso.viso");
239 }
240
241 /*
242 * Check that we've got the minimum of data available.
243 */
244 if (mpParent->i_getIsoPath().isEmpty())
245 return mpParent->setError(E_INVALIDARG, tr("Cannot proceed with an empty installation ISO path"));
246 if (mpParent->i_getUser().isEmpty())
247 return mpParent->setError(E_INVALIDARG, tr("Empty user name is not allowed"));
248 if (mpParent->i_getPassword().isEmpty())
249 return mpParent->setError(E_INVALIDARG, tr("Empty password is not allowed"));
250
251 LogRelFunc(("UnattendedInstaller::savePassedData(): \n"));
252 return S_OK;
253}
254
255#if 0 /* Always in AUX ISO */
256bool UnattendedInstaller::isAdditionsIsoNeeded() const
257{
258 /* In the VISO case, we'll add the additions to the VISO in a subdir. */
259 return !isAuxiliaryIsoIsVISO() && mpParent->i_getInstallGuestAdditions();
260}
261
262bool UnattendedInstaller::isValidationKitIsoNeeded() const
263{
264 /* In the VISO case, we'll add the validation kit to the VISO in a subdir. */
265 return !isAuxiliaryIsoIsVISO() && mpParent->i_getInstallTestExecService();
266}
267#endif
268
269bool UnattendedInstaller::isAuxiliaryIsoNeeded() const
270{
271 /* In the VISO case we use the AUX ISO for GAs, TXS, and User Payloads. */
272 return isAuxiliaryIsoIsVISO()
273 && ( mpParent->i_getInstallGuestAdditions()
274 || mpParent->i_getInstallTestExecService()
275 || mpParent->i_getInstallUserPayload());
276}
277
278
279HRESULT UnattendedInstaller::prepareUnattendedScripts()
280{
281 LogFlow(("UnattendedInstaller::prepareUnattendedScripts()\n"));
282
283 /*
284 * The script template editor calls setError, so status codes just needs to
285 * be passed on to the caller. Do the same for both scripts.
286 */
287 HRESULT hrc = mMainScript.read(getTemplateFilePath());
288 if (SUCCEEDED(hrc))
289 {
290 hrc = mMainScript.parse();
291 if (SUCCEEDED(hrc))
292 {
293 /* Ditto for the post script. */
294 hrc = mPostScript.read(getPostTemplateFilePath());
295 if (SUCCEEDED(hrc))
296 {
297 hrc = mPostScript.parse();
298 if (SUCCEEDED(hrc))
299 {
300 LogFlow(("UnattendedInstaller::prepareUnattendedScripts: returns S_OK\n"));
301 return S_OK;
302 }
303 LogFlow(("UnattendedInstaller::prepareUnattendedScripts: parse failed on post script (%Rhrc)\n", hrc));
304 }
305 else
306 LogFlow(("UnattendedInstaller::prepareUnattendedScripts: error reading post install script template file (%Rhrc)\n", hrc));
307 }
308 else
309 LogFlow(("UnattendedInstaller::prepareUnattendedScripts: parse failed (%Rhrc)\n", hrc));
310 }
311 else
312 LogFlow(("UnattendedInstaller::prepareUnattendedScripts: error reading installation script template file (%Rhrc)\n", hrc));
313 return hrc;
314}
315
316HRESULT UnattendedInstaller::prepareMedia(bool fOverwrite /*=true*/)
317{
318 LogRelFlow(("UnattendedInstaller::prepareMedia:\n"));
319 HRESULT hrc = S_OK;
320 if (isAuxiliaryFloppyNeeded())
321 hrc = prepareAuxFloppyImage(fOverwrite);
322 if (SUCCEEDED(hrc))
323 {
324 if (isAuxiliaryIsoNeeded())
325 {
326 hrc = prepareAuxIsoImage(fOverwrite);
327 if (FAILED(hrc))
328 {
329 LogRelFlow(("UnattendedInstaller::prepareMedia: prepareAuxIsoImage failed\n"));
330
331 /* Delete the floppy image if we created one. */
332 if (isAuxiliaryFloppyNeeded())
333 RTFileDelete(getAuxiliaryFloppyFilePath().c_str());
334 }
335 }
336 }
337 LogRelFlow(("UnattendedInstaller::prepareMedia: returns %Rrc\n", hrc));
338 return hrc;
339}
340
341/*
342 *
343 * UnattendedInstaller protected methods
344 *
345 */
346HRESULT UnattendedInstaller::prepareAuxFloppyImage(bool fOverwrite)
347{
348 Assert(isAuxiliaryFloppyNeeded());
349
350 /*
351 * Create the image.
352 */
353 RTVFSFILE hVfsFile;
354 HRESULT hrc = newAuxFloppyImage(getAuxiliaryFloppyFilePath().c_str(), fOverwrite, &hVfsFile);
355 if (SUCCEEDED(hrc))
356 {
357 /*
358 * Open the FAT file system so we can copy files onto the floppy.
359 */
360 RTERRINFOSTATIC ErrInfo;
361 RTVFS hVfs;
362 int vrc = RTFsFatVolOpen(hVfsFile, false /*fReadOnly*/, 0 /*offBootSector*/, &hVfs, RTErrInfoInitStatic(&ErrInfo));
363 RTVfsFileRelease(hVfsFile);
364 if (RT_SUCCESS(vrc))
365 {
366 /*
367 * Call overridable method to copies the files onto it.
368 */
369 hrc = copyFilesToAuxFloppyImage(hVfs);
370
371 /*
372 * Release the VFS. On failure, delete the floppy image so the operation can
373 * be repeated in non-overwrite mode and so that we don't leave any mess behind.
374 */
375 RTVfsRelease(hVfs);
376 }
377 else if (RTErrInfoIsSet(&ErrInfo.Core))
378 hrc = mpParent->setErrorBoth(E_FAIL, vrc,
379 tr("Failed to open FAT file system on newly created floppy image '%s': %Rrc: %s"),
380 getAuxiliaryFloppyFilePath().c_str(), vrc, ErrInfo.Core.pszMsg);
381 else
382 hrc = mpParent->setErrorBoth(E_FAIL, vrc,
383 tr("Failed to open FAT file system onnewly created floppy image '%s': %Rrc"),
384 getAuxiliaryFloppyFilePath().c_str(), vrc);
385 if (FAILED(hrc))
386 RTFileDelete(getAuxiliaryFloppyFilePath().c_str());
387 }
388 return hrc;
389}
390
391HRESULT UnattendedInstaller::newAuxFloppyImage(const char *pszFilename, bool fOverwrite, PRTVFSFILE phVfsFile)
392{
393 /*
394 * Open the image file.
395 */
396 HRESULT hrc;
397 RTVFSFILE hVfsFile;
398 uint64_t fOpen = RTFILE_O_READWRITE | RTFILE_O_DENY_ALL | (0660 << RTFILE_O_CREATE_MODE_SHIFT);
399 if (fOverwrite)
400 fOpen |= RTFILE_O_CREATE_REPLACE;
401 else
402 fOpen |= RTFILE_O_OPEN;
403 int vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
404 if (RT_SUCCESS(vrc))
405 {
406 /*
407 * Format it.
408 */
409 vrc = RTFsFatVolFormat144(hVfsFile, false /*fQuick*/);
410 if (RT_SUCCESS(vrc))
411 {
412 *phVfsFile = hVfsFile;
413 LogRelFlow(("UnattendedInstaller::newAuxFloppyImage: created and formatted '%s'\n", pszFilename));
414 return S_OK;
415 }
416
417 hrc = mpParent->setErrorBoth(E_FAIL, vrc, tr("Failed to format floppy image '%s': %Rrc"), pszFilename, vrc);
418 RTVfsFileRelease(hVfsFile);
419 RTFileDelete(pszFilename);
420 }
421 else
422 hrc = mpParent->setErrorBoth(E_FAIL, vrc, tr("Failed to create floppy image '%s': %Rrc"), pszFilename, vrc);
423 return hrc;
424}
425
426HRESULT UnattendedInstaller::copyFilesToAuxFloppyImage(RTVFS hVfs)
427{
428 HRESULT hrc = addScriptToFloppyImage(&mMainScript, hVfs);
429 if (SUCCEEDED(hrc))
430 hrc = addScriptToFloppyImage(&mPostScript, hVfs);
431 return hrc;
432}
433
434HRESULT UnattendedInstaller::addScriptToFloppyImage(BaseTextScript *pEditor, RTVFS hVfs)
435{
436 /*
437 * Open the destination file.
438 */
439 HRESULT hrc;
440 RTVFSFILE hVfsFileDst;
441 int vrc = RTVfsFileOpen(hVfs, pEditor->getDefaultFilename(),
442 RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_ALL
443 | (0660 << RTFILE_O_CREATE_MODE_SHIFT),
444 &hVfsFileDst);
445 if (RT_SUCCESS(vrc))
446 {
447 /*
448 * Save the content to a string.
449 */
450 Utf8Str strScript;
451 hrc = pEditor->saveToString(strScript);
452 if (SUCCEEDED(hrc))
453 {
454 /*
455 * Write the string.
456 */
457 vrc = RTVfsFileWrite(hVfsFileDst, strScript.c_str(), strScript.length(), NULL);
458 if (RT_SUCCESS(vrc))
459 hrc = S_OK; /* done */
460 else
461 hrc = mpParent->setErrorBoth(E_FAIL, vrc,
462 tr("Error writing %zu bytes to '%s' in floppy image '%s': %Rrc",
463 "", strScript.length()),
464 strScript.length(), pEditor->getDefaultFilename(),
465 getAuxiliaryFloppyFilePath().c_str());
466 }
467 RTVfsFileRelease(hVfsFileDst);
468 }
469 else
470 hrc = mpParent->setErrorBoth(E_FAIL, vrc,
471 tr("Error creating '%s' in floppy image '%s': %Rrc"),
472 pEditor->getDefaultFilename(), getAuxiliaryFloppyFilePath().c_str());
473 return hrc;
474}
475
476HRESULT UnattendedInstaller::addFileToFloppyImage(RTVFS hVfs, const char *pszSrc, const char *pszDst)
477{
478 HRESULT hrc;
479
480 /*
481 * Open the source file.
482 */
483 RTVFSIOSTREAM hVfsIosSrc;
484 int vrc = RTVfsIoStrmOpenNormal(pszSrc, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsIosSrc);
485 if (RT_SUCCESS(vrc))
486 {
487 /*
488 * Open the destination file.
489 */
490 RTVFSFILE hVfsFileDst;
491 vrc = RTVfsFileOpen(hVfs, pszDst,
492 RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_ALL | (0660 << RTFILE_O_CREATE_MODE_SHIFT),
493 &hVfsFileDst);
494 if (RT_SUCCESS(vrc))
495 {
496 /*
497 * Do the copying.
498 */
499 RTVFSIOSTREAM hVfsIosDst = RTVfsFileToIoStream(hVfsFileDst);
500 vrc = RTVfsUtilPumpIoStreams(hVfsIosSrc, hVfsIosDst, 0);
501 if (RT_SUCCESS(vrc))
502 hrc = S_OK;
503 else
504 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error writing copying '%s' to floppy image '%s': %Rrc"),
505 pszSrc, getAuxiliaryFloppyFilePath().c_str(), vrc);
506 RTVfsIoStrmRelease(hVfsIosDst);
507 RTVfsFileRelease(hVfsFileDst);
508 }
509 else
510 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error opening '%s' on floppy image '%s' for writing: %Rrc"),
511 pszDst, getAuxiliaryFloppyFilePath().c_str(), vrc);
512
513 RTVfsIoStrmRelease(hVfsIosSrc);
514 }
515 else
516 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error opening '%s' for copying onto floppy image '%s': %Rrc"),
517 pszSrc, getAuxiliaryFloppyFilePath().c_str(), vrc);
518 return hrc;
519}
520
521HRESULT UnattendedInstaller::prepareAuxIsoImage(bool fOverwrite)
522{
523 /*
524 * Open the original installation ISO.
525 */
526 RTVFS hVfsOrgIso;
527 HRESULT hrc = openInstallIsoImage(&hVfsOrgIso);
528 if (SUCCEEDED(hrc))
529 {
530 /*
531 * The next steps depends on the kind of image we're making.
532 */
533 if (!isAuxiliaryIsoIsVISO())
534 {
535 RTFSISOMAKER hIsoMaker;
536 hrc = newAuxIsoImageMaker(&hIsoMaker);
537 if (SUCCEEDED(hrc))
538 {
539 hrc = addFilesToAuxIsoImageMaker(hIsoMaker, hVfsOrgIso);
540 if (SUCCEEDED(hrc))
541 hrc = finalizeAuxIsoImage(hIsoMaker, getAuxiliaryIsoFilePath().c_str(), fOverwrite);
542 RTFsIsoMakerRelease(hIsoMaker);
543 }
544 }
545 else
546 {
547 RTCList<RTCString> vecFiles(0);
548 RTCList<RTCString> vecArgs(0);
549 try
550 {
551 vecArgs.append() = "--iprt-iso-maker-file-marker-bourne-sh";
552 RTUUID Uuid;
553 int vrc = RTUuidCreate(&Uuid); AssertRC(vrc);
554 char szTmp[RTUUID_STR_LENGTH + 1];
555 vrc = RTUuidToStr(&Uuid, szTmp, sizeof(szTmp)); AssertRC(vrc);
556 vecArgs.append() = szTmp;
557 vecArgs.append() = "--file-mode=0444";
558 vecArgs.append() = "--dir-mode=0555";
559 }
560 catch (std::bad_alloc &)
561 {
562 hrc = E_OUTOFMEMORY;
563 }
564 if (SUCCEEDED(hrc))
565 {
566 hrc = addFilesToAuxVisoVectors(vecArgs, vecFiles, hVfsOrgIso, fOverwrite);
567 if (SUCCEEDED(hrc))
568 hrc = finalizeAuxVisoFile(vecArgs, getAuxiliaryIsoFilePath().c_str(), fOverwrite);
569
570 if (FAILED(hrc))
571 for (size_t i = 0; i < vecFiles.size(); i++)
572 RTFileDelete(vecFiles[i].c_str());
573 }
574 }
575 RTVfsRelease(hVfsOrgIso);
576 }
577 return hrc;
578}
579
580HRESULT UnattendedInstaller::openInstallIsoImage(PRTVFS phVfsIso, uint32_t fFlags /*= 0*/)
581{
582 /* Open the file. */
583 const char *pszIsoPath = mpParent->i_getIsoPath().c_str();
584 RTVFSFILE hOrgIsoFile;
585 int vrc = RTVfsFileOpenNormal(pszIsoPath, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hOrgIsoFile);
586 if (RT_FAILURE(vrc))
587 return mpParent->setErrorBoth(E_FAIL, vrc, tr("Failed to open ISO image '%s' (%Rrc)"), pszIsoPath, vrc);
588
589 /* Pass the file to the ISO file system interpreter. */
590 RTERRINFOSTATIC ErrInfo;
591 vrc = RTFsIso9660VolOpen(hOrgIsoFile, fFlags, phVfsIso, RTErrInfoInitStatic(&ErrInfo));
592 RTVfsFileRelease(hOrgIsoFile);
593 if (RT_SUCCESS(vrc))
594 return S_OK;
595 if (RTErrInfoIsSet(&ErrInfo.Core))
596 return mpParent->setErrorBoth(E_FAIL, vrc, tr("ISO reader fail to open '%s' (%Rrc): %s"),
597 pszIsoPath, vrc, ErrInfo.Core.pszMsg);
598 return mpParent->setErrorBoth(E_FAIL, vrc, tr("ISO reader fail to open '%s' (%Rrc)"), pszIsoPath, vrc);
599}
600
601HRESULT UnattendedInstaller::newAuxIsoImageMaker(PRTFSISOMAKER phIsoMaker)
602{
603 int vrc = RTFsIsoMakerCreate(phIsoMaker);
604 if (RT_SUCCESS(vrc))
605 return S_OK;
606 return mpParent->setErrorBoth(E_FAIL, vrc, tr("RTFsIsoMakerCreate failed (%Rrc)"), vrc);
607}
608
609HRESULT UnattendedInstaller::addFilesToAuxIsoImageMaker(RTFSISOMAKER hIsoMaker, RTVFS hVfsOrgIso)
610{
611 RT_NOREF(hVfsOrgIso);
612
613 /*
614 * Add the two scripts to the image with default names.
615 */
616 HRESULT hrc = addScriptToIsoMaker(&mMainScript, hIsoMaker);
617 if (SUCCEEDED(hrc))
618 hrc = addScriptToIsoMaker(&mPostScript, hIsoMaker);
619 return hrc;
620}
621
622HRESULT UnattendedInstaller::addScriptToIsoMaker(BaseTextScript *pEditor, RTFSISOMAKER hIsoMaker,
623 const char *pszDstFilename /*= NULL*/)
624{
625 /*
626 * Calc default destination filename if desired.
627 */
628 RTCString strDstNameBuf;
629 if (!pszDstFilename)
630 {
631 try
632 {
633 strDstNameBuf = RTPATH_SLASH_STR;
634 strDstNameBuf.append(pEditor->getDefaultTemplateFilename());
635 pszDstFilename = strDstNameBuf.c_str();
636 }
637 catch (std::bad_alloc &)
638 {
639 return E_OUTOFMEMORY;
640 }
641 }
642
643 /*
644 * Create a memory file for the script.
645 */
646 Utf8Str strScript;
647 HRESULT hrc = pEditor->saveToString(strScript);
648 if (SUCCEEDED(hrc))
649 {
650 RTVFSFILE hVfsScriptFile;
651 size_t cchScript = strScript.length();
652 int vrc = RTVfsFileFromBuffer(RTFILE_O_READ, strScript.c_str(), strScript.length(), &hVfsScriptFile);
653 strScript.setNull();
654 if (RT_SUCCESS(vrc))
655 {
656 /*
657 * Add it to the ISO.
658 */
659 vrc = RTFsIsoMakerAddFileWithVfsFile(hIsoMaker, pszDstFilename, hVfsScriptFile, NULL);
660 RTVfsFileRelease(hVfsScriptFile);
661 if (RT_SUCCESS(vrc))
662 hrc = S_OK;
663 else
664 hrc = mpParent->setErrorBoth(E_FAIL, vrc,
665 tr("RTFsIsoMakerAddFileWithVfsFile failed on the script '%s' (%Rrc)"),
666 pszDstFilename, vrc);
667 }
668 else
669 hrc = mpParent->setErrorBoth(E_FAIL, vrc,
670 tr("RTVfsFileFromBuffer failed on the %zu byte script '%s' (%Rrc)", "", cchScript),
671 cchScript, pszDstFilename, vrc);
672 }
673 return hrc;
674}
675
676HRESULT UnattendedInstaller::finalizeAuxIsoImage(RTFSISOMAKER hIsoMaker, const char *pszFilename, bool fOverwrite)
677{
678 /*
679 * Finalize the image.
680 */
681 int vrc = RTFsIsoMakerFinalize(hIsoMaker);
682 if (RT_FAILURE(vrc))
683 return mpParent->setErrorBoth(E_FAIL, vrc, tr("RTFsIsoMakerFinalize failed (%Rrc)"), vrc);
684
685 /*
686 * Open the destination file.
687 */
688 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_ALL;
689 if (fOverwrite)
690 fOpen |= RTFILE_O_CREATE_REPLACE;
691 else
692 fOpen |= RTFILE_O_CREATE;
693 RTVFSFILE hVfsDstFile;
694 vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsDstFile);
695 if (RT_FAILURE(vrc))
696 {
697 if (vrc == VERR_ALREADY_EXISTS)
698 return mpParent->setErrorBoth(E_FAIL, vrc, tr("The auxiliary ISO image file '%s' already exists"),
699 pszFilename);
700 return mpParent->setErrorBoth(E_FAIL, vrc, tr("Failed to open the auxiliary ISO image file '%s' for writing (%Rrc)"),
701 pszFilename, vrc);
702 }
703
704 /*
705 * Get the source file from the image maker.
706 */
707 HRESULT hrc;
708 RTVFSFILE hVfsSrcFile;
709 vrc = RTFsIsoMakerCreateVfsOutputFile(hIsoMaker, &hVfsSrcFile);
710 if (RT_SUCCESS(vrc))
711 {
712 RTVFSIOSTREAM hVfsSrcIso = RTVfsFileToIoStream(hVfsSrcFile);
713 RTVFSIOSTREAM hVfsDstIso = RTVfsFileToIoStream(hVfsDstFile);
714 if ( hVfsSrcIso != NIL_RTVFSIOSTREAM
715 && hVfsDstIso != NIL_RTVFSIOSTREAM)
716 {
717 vrc = RTVfsUtilPumpIoStreams(hVfsSrcIso, hVfsDstIso, 0 /*cbBufHint*/);
718 if (RT_SUCCESS(vrc))
719 hrc = S_OK;
720 else
721 hrc = mpParent->setErrorBoth(E_FAIL, vrc, tr("Error writing auxiliary ISO image '%s' (%Rrc)"),
722 pszFilename, vrc);
723 }
724 else
725 hrc = mpParent->setErrorBoth(E_FAIL, VERR_INTERNAL_ERROR_2,
726 tr("Internal Error: Failed to case VFS file to VFS I/O stream"));
727 RTVfsIoStrmRelease(hVfsSrcIso);
728 RTVfsIoStrmRelease(hVfsDstIso);
729 }
730 else
731 hrc = mpParent->setErrorBoth(E_FAIL, vrc, tr("RTFsIsoMakerCreateVfsOutputFile failed (%Rrc)"), vrc);
732 RTVfsFileRelease(hVfsSrcFile);
733 RTVfsFileRelease(hVfsDstFile);
734 if (FAILED(hrc))
735 RTFileDelete(pszFilename);
736 return hrc;
737}
738
739HRESULT UnattendedInstaller::addFilesToAuxVisoVectors(RTCList<RTCString> &rVecArgs, RTCList<RTCString> &rVecFiles,
740 RTVFS hVfsOrgIso, bool fOverwrite)
741{
742 RT_NOREF(hVfsOrgIso);
743
744 /*
745 * Save and add the scripts.
746 */
747 HRESULT hrc = addScriptToVisoVectors(&mMainScript, rVecArgs, rVecFiles, fOverwrite);
748 if (SUCCEEDED(hrc))
749 hrc = addScriptToVisoVectors(&mPostScript, rVecArgs, rVecFiles, fOverwrite);
750 if (SUCCEEDED(hrc))
751 {
752 try
753 {
754 /*
755 * If we've got a Guest Additions ISO, add its content to a /vboxadditions dir.
756 */
757 if (mpParent->i_getInstallGuestAdditions())
758 {
759 rVecArgs.append().append("--push-iso=").append(mpParent->i_getAdditionsIsoPath());
760 rVecArgs.append() = "/vboxadditions=/";
761 rVecArgs.append() = "--pop";
762 }
763
764 /*
765 * If we've got a Validation Kit ISO, add its content to a /vboxvalidationkit dir.
766 */
767 if (mpParent->i_getInstallTestExecService())
768 {
769 rVecArgs.append().append("--push-iso=").append(mpParent->i_getValidationKitIsoPath());
770 rVecArgs.append() = "/vboxvalidationkit=/";
771 rVecArgs.append() = "--pop";
772 }
773
774 /*
775 * If we've got a User Payload ISO, add its content to a /vboxuserpayload dir.
776 */
777 if (mpParent->i_getInstallUserPayload())
778 {
779 rVecArgs.append().append("--push-iso=").append(mpParent->i_getUserPayloadIsoPath());
780 rVecArgs.append() = "/vboxuserpayload=/";
781 rVecArgs.append() = "--pop";
782 }
783 }
784 catch (std::bad_alloc &)
785 {
786 hrc = E_OUTOFMEMORY;
787 }
788 }
789 return hrc;
790}
791
792HRESULT UnattendedInstaller::addScriptToVisoVectors(BaseTextScript *pEditor, RTCList<RTCString> &rVecArgs,
793 RTCList<RTCString> &rVecFiles, bool fOverwrite)
794{
795 /*
796 * Calc the aux script file name.
797 */
798 RTCString strScriptName;
799 try
800 {
801 strScriptName = mpParent->i_getAuxiliaryBasePath();
802 strScriptName.append(pEditor->getDefaultFilename());
803 }
804 catch (std::bad_alloc &)
805 {
806 return E_OUTOFMEMORY;
807 }
808
809 /*
810 * Save it.
811 */
812 HRESULT hrc = pEditor->save(strScriptName.c_str(), fOverwrite);
813 if (SUCCEEDED(hrc))
814 {
815 /*
816 * Add it to the vectors.
817 */
818 try
819 {
820 rVecArgs.append().append('/').append(pEditor->getDefaultFilename()).append('=').append(strScriptName);
821 rVecFiles.append(strScriptName);
822 }
823 catch (std::bad_alloc &)
824 {
825 RTFileDelete(strScriptName.c_str());
826 hrc = E_OUTOFMEMORY;
827 }
828 }
829 return hrc;
830}
831
832HRESULT UnattendedInstaller::finalizeAuxVisoFile(RTCList<RTCString> const &rVecArgs, const char *pszFilename, bool fOverwrite)
833{
834 /*
835 * Create a C-style argument vector and turn that into a command line string.
836 */
837 size_t const cArgs = rVecArgs.size();
838 const char **papszArgs = (const char **)RTMemTmpAlloc((cArgs + 1) * sizeof(const char *));
839 if (!papszArgs)
840 return E_OUTOFMEMORY;
841 for (size_t i = 0; i < cArgs; i++)
842 papszArgs[i] = rVecArgs[i].c_str();
843 papszArgs[cArgs] = NULL;
844
845 char *pszCmdLine;
846 int vrc = RTGetOptArgvToString(&pszCmdLine, papszArgs, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
847 RTMemTmpFree(papszArgs);
848 if (RT_FAILURE(vrc))
849 return mpParent->setErrorBoth(E_FAIL, vrc, tr("RTGetOptArgvToString failed (%Rrc)"), vrc);
850
851 /*
852 * Open the file.
853 */
854 HRESULT hrc;
855 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_DENY_READ;
856 if (fOverwrite)
857 fOpen |= RTFILE_O_CREATE_REPLACE;
858 else
859 fOpen |= RTFILE_O_CREATE;
860 RTFILE hFile;
861 vrc = RTFileOpen(&hFile, pszFilename, fOpen);
862 if (RT_SUCCESS(vrc))
863 {
864 vrc = RTFileWrite(hFile, pszCmdLine, strlen(pszCmdLine), NULL);
865 if (RT_SUCCESS(vrc))
866 vrc = RTFileClose(hFile);
867 else
868 RTFileClose(hFile);
869 if (RT_SUCCESS(vrc))
870 hrc = S_OK;
871 else
872 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error writing '%s' (%Rrc)"), pszFilename, vrc);
873 }
874 else
875 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to create '%s' (%Rrc)"), pszFilename, vrc);
876
877 RTStrFree(pszCmdLine);
878 return hrc;
879}
880
881HRESULT UnattendedInstaller::loadAndParseFileFromIso(RTVFS hVfsOrgIso, const char *pszFilename, AbstractScript *pEditor)
882{
883 HRESULT hrc;
884 RTVFSFILE hVfsFile;
885 int vrc = RTVfsFileOpen(hVfsOrgIso, pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, &hVfsFile);
886 if (RT_SUCCESS(vrc))
887 {
888 hrc = pEditor->readFromHandle(hVfsFile, pszFilename);
889 RTVfsFileRelease(hVfsFile);
890 if (SUCCEEDED(hrc))
891 hrc = pEditor->parse();
892 }
893 else
894 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to open '%s' on the ISO '%s' (%Rrc)"),
895 pszFilename, mpParent->i_getIsoPath().c_str(), vrc);
896 return hrc;
897}
898
899
900
901//////////////////////////////////////////////////////////////////////////////////////////////////////
902/*
903*
904*
905* Implementation UnattendedLinuxInstaller functions
906*
907*/
908//////////////////////////////////////////////////////////////////////////////////////////////////////
909HRESULT UnattendedLinuxInstaller::editIsoLinuxCfg(GeneralTextScript *pEditor)
910{
911 try
912 {
913 /* Comment out 'display <filename>' directives that's used for displaying files at boot time. */
914 std::vector<size_t> vecLineNumbers = pEditor->findTemplate("display", RTCString::CaseInsensitive);
915 for (size_t i = 0; i < vecLineNumbers.size(); ++i)
916 if (pEditor->getContentOfLine(vecLineNumbers[i]).startsWithWord("display", RTCString::CaseInsensitive))
917 {
918 HRESULT hrc = pEditor->prependToLine(vecLineNumbers.at(i), "#");
919 if (FAILED(hrc))
920 return hrc;
921 }
922 }
923 catch (std::bad_alloc &)
924 {
925 return E_OUTOFMEMORY;
926 }
927 return editIsoLinuxCommon(pEditor);
928}
929
930HRESULT UnattendedLinuxInstaller::editIsoLinuxCommon(GeneralTextScript *pEditor)
931{
932 try
933 {
934 /* Set timeouts to 4 seconds. */
935 std::vector<size_t> vecLineNumbers = pEditor->findTemplate("timeout", RTCString::CaseInsensitive);
936 for (size_t i = 0; i < vecLineNumbers.size(); ++i)
937 if (pEditor->getContentOfLine(vecLineNumbers[i]).startsWithWord("timeout", RTCString::CaseInsensitive))
938 {
939 HRESULT hrc = pEditor->setContentOfLine(vecLineNumbers.at(i), "timeout 4");
940 if (FAILED(hrc))
941 return hrc;
942 }
943
944 /* Modify kernel parameters. */
945 vecLineNumbers = pEditor->findTemplate("append", RTCString::CaseInsensitive);
946 if (vecLineNumbers.size() > 0)
947 {
948 Utf8Str const &rStrAppend = mpParent->i_getExtraInstallKernelParameters().isNotEmpty()
949 ? mpParent->i_getExtraInstallKernelParameters()
950 : mStrDefaultExtraInstallKernelParameters;
951
952 for (size_t i = 0; i < vecLineNumbers.size(); ++i)
953 if (pEditor->getContentOfLine(vecLineNumbers[i]).startsWithWord("append", RTCString::CaseInsensitive))
954 {
955 Utf8Str strLine = pEditor->getContentOfLine(vecLineNumbers[i]);
956
957 /* Do removals. */
958 if (mArrStrRemoveInstallKernelParameters.size() > 0)
959 {
960 size_t offStart = strLine.find("append") + 5;
961 while (offStart < strLine.length() && !RT_C_IS_SPACE(strLine[offStart]))
962 offStart++;
963 while (offStart < strLine.length() && RT_C_IS_SPACE(strLine[offStart]))
964 offStart++;
965 if (offStart < strLine.length())
966 {
967 for (size_t iRemove = 0; iRemove < mArrStrRemoveInstallKernelParameters.size(); iRemove++)
968 {
969 RTCString const &rStrRemove = mArrStrRemoveInstallKernelParameters[iRemove];
970 for (size_t off = offStart; off < strLine.length(); )
971 {
972 Assert(!RT_C_IS_SPACE(strLine[off]));
973
974 /* Find the end of word. */
975 size_t offEnd = off + 1;
976 while (offEnd < strLine.length() && !RT_C_IS_SPACE(strLine[offEnd]))
977 offEnd++;
978
979 /* Check if it matches. */
980 if (RTStrSimplePatternNMatch(rStrRemove.c_str(), rStrRemove.length(),
981 strLine.c_str() + off, offEnd - off))
982 {
983 while (off > 0 && RT_C_IS_SPACE(strLine[off - 1]))
984 off--;
985 strLine.erase(off, offEnd - off);
986 }
987
988 /* Advance to the next word. */
989 off = offEnd;
990 while (off < strLine.length() && RT_C_IS_SPACE(strLine[off]))
991 off++;
992 }
993 }
994 }
995 }
996
997 /* Do the appending. */
998 if (rStrAppend.isNotEmpty())
999 {
1000 if (!rStrAppend.startsWith(" ") && !strLine.endsWith(" "))
1001 strLine.append(' ');
1002 strLine.append(rStrAppend);
1003 }
1004
1005 /* Update line. */
1006 HRESULT hrc = pEditor->setContentOfLine(vecLineNumbers.at(i), strLine);
1007 if (FAILED(hrc))
1008 return hrc;
1009 }
1010 }
1011 }
1012 catch (std::bad_alloc &)
1013 {
1014 return E_OUTOFMEMORY;
1015 }
1016 return S_OK;
1017}
1018
1019
1020HRESULT UnattendedLinuxInstaller::editGrubCfg(GeneralTextScript *pEditor)
1021{
1022 /* Default menu entry of grub.cfg is set in /etc/deafult/grub file. */
1023 try
1024 {
1025 /* Set timeouts to 4 seconds. */
1026 std::vector<size_t> vecLineNumbers = pEditor->findTemplate("set timeout", RTCString::CaseInsensitive);
1027 if (vecLineNumbers.size() > 0)
1028 {
1029 for (size_t i = 0; i < vecLineNumbers.size(); ++i)
1030 if (pEditor->getContentOfLine(vecLineNumbers[i]).startsWithWord("set timeout", RTCString::CaseInsensitive))
1031 {
1032 HRESULT hrc = pEditor->setContentOfLine(vecLineNumbers.at(i), "set timeout=4");
1033 if (FAILED(hrc))
1034 return hrc;
1035 }
1036 }
1037 else
1038 {
1039 /* Append timeout if not set (happens with arm64 iso images at least). */
1040 HRESULT hrc = pEditor->appendLine("set timeout=4");
1041 if (FAILED(hrc))
1042 return hrc;
1043 }
1044
1045 /* Modify kernel lines assuming that they starts with 'linux' keyword and 2nd word is the kernel command.*
1046 * we remove whatever comes after command and add our own command line options. */
1047 vecLineNumbers = pEditor->findTemplate("linux", RTCString::CaseInsensitive);
1048 if (vecLineNumbers.size() > 0)
1049 {
1050 Utf8Str const &rStrAppend = mpParent->i_getExtraInstallKernelParameters().isNotEmpty()
1051 ? mpParent->i_getExtraInstallKernelParameters()
1052 : mStrDefaultExtraInstallKernelParameters;
1053
1054 for (size_t i = 0; i < vecLineNumbers.size(); ++i)
1055 {
1056 HRESULT hrc = S_OK;
1057 if (pEditor->getContentOfLine(vecLineNumbers[i]).startsWithWord("linux", RTCString::CaseInsensitive))
1058 {
1059 Utf8Str strLine = pEditor->getContentOfLine(vecLineNumbers[i]);
1060 size_t cbPos = strLine.find("linux") + strlen("linux");
1061 bool fSecondWord = false;
1062 /* Find the end of 2nd word assuming that it is kernel command. */
1063 while (cbPos < strLine.length())
1064 {
1065 if (!fSecondWord)
1066 {
1067 if (strLine[cbPos] != '\t' && strLine[cbPos] != ' ')
1068 fSecondWord = true;
1069 }
1070 else
1071 {
1072 if (strLine[cbPos] == '\t' || strLine[cbPos] == ' ')
1073 break;
1074 }
1075 ++cbPos;
1076 }
1077 if (!fSecondWord)
1078 hrc = E_FAIL;
1079
1080 if (SUCCEEDED(hrc))
1081 {
1082 strLine.erase(cbPos, strLine.length() - cbPos);
1083
1084 /* Do the appending. */
1085 if (rStrAppend.isNotEmpty())
1086 {
1087 if (!rStrAppend.startsWith(" ") && !strLine.endsWith(" "))
1088 strLine.append(' ');
1089 strLine.append(rStrAppend);
1090 }
1091
1092 /* Update line. */
1093 hrc = pEditor->setContentOfLine(vecLineNumbers.at(i), strLine);
1094 }
1095 if (FAILED(hrc))
1096 return hrc;
1097 }
1098 }
1099 }
1100 }
1101 catch (std::bad_alloc &)
1102 {
1103 return E_OUTOFMEMORY;
1104 }
1105 return S_OK;
1106}
1107
1108
1109//////////////////////////////////////////////////////////////////////////////////////////////////////
1110/*
1111*
1112*
1113* Implementation UnattendedDebianInstaller functions
1114*
1115*/
1116//////////////////////////////////////////////////////////////////////////////////////////////////////
1117
1118/**
1119 * Helper for checking if a file exists.
1120 * @todo promote to IPRT?
1121 */
1122static bool hlpVfsFileExists(RTVFS hVfs, const char *pszPath)
1123{
1124 RTFSOBJINFO ObjInfo;
1125 int vrc = RTVfsQueryPathInfo(hVfs, pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
1126 return RT_SUCCESS(vrc) && RTFS_IS_FILE(ObjInfo.Attr.fMode);
1127}
1128
1129HRESULT UnattendedDebianInstaller::addFilesToAuxVisoVectors(RTCList<RTCString> &rVecArgs, RTCList<RTCString> &rVecFiles,
1130 RTVFS hVfsOrgIso, bool fOverwrite)
1131{
1132 /*
1133 * Figure out the name of the menu config file that we have to edit.
1134 */
1135 bool fMenuConfigIsGrub = false;
1136 const char *pszMenuConfigFilename = "/isolinux/txt.cfg";
1137 if (!hlpVfsFileExists(hVfsOrgIso, pszMenuConfigFilename))
1138 {
1139 /* On Debian Live ISOs (at least from 9 to 11) the there is only menu.cfg. */
1140 if (hlpVfsFileExists(hVfsOrgIso, "/isolinux/menu.cfg"))
1141 pszMenuConfigFilename = "/isolinux/menu.cfg";
1142 /* On Linux Mint 20.3, 21, and 19 (at least) there is only isolinux.cfg. */
1143 else if (hlpVfsFileExists(hVfsOrgIso, "/isolinux/isolinux.cfg"))
1144 pszMenuConfigFilename = "/isolinux/isolinux.cfg";
1145 /* Ubuntus 21.10+ are UEFI only. No isolinux directory. We modify grub.cfg. */
1146 else if (hlpVfsFileExists(hVfsOrgIso, "/boot/grub/grub.cfg"))
1147 {
1148 pszMenuConfigFilename = "/boot/grub/grub.cfg";
1149 fMenuConfigIsGrub = true;
1150 }
1151 }
1152
1153 /* Check for existence of isolinux.cfg since UEFI-only ISOs do not have this file. */
1154 bool const fIsoLinuxCfgExists = hlpVfsFileExists(hVfsOrgIso, "isolinux/isolinux.cfg");
1155 Assert(!fIsoLinuxCfgExists || !fMenuConfigIsGrub); /** @todo r=bird: Perhaps prefix the hlpVfsFileExists call with 'fIsoLinuxCfgExists &&' above ? */
1156
1157 /*
1158 * VISO bits and filenames.
1159 */
1160 RTCString strIsoLinuxCfg;
1161 RTCString strTxtCfg;
1162 try
1163 {
1164 /* Remaster ISO. */
1165 rVecArgs.append() = "--no-file-mode";
1166 rVecArgs.append() = "--no-dir-mode";
1167
1168 rVecArgs.append() = "--import-iso";
1169 rVecArgs.append(mpParent->i_getIsoPath());
1170
1171 rVecArgs.append() = "--file-mode=0444";
1172 rVecArgs.append() = "--dir-mode=0555";
1173
1174 /* Replace the isolinux.cfg configuration file. */
1175 if (fIsoLinuxCfgExists)
1176 {
1177 /* First remove. */
1178 rVecArgs.append() = "isolinux/isolinux.cfg=:must-remove:";
1179 /* Then add the modified file. */
1180 strIsoLinuxCfg = mpParent->i_getAuxiliaryBasePath();
1181 strIsoLinuxCfg.append("isolinux-isolinux.cfg");
1182 rVecArgs.append().append("isolinux/isolinux.cfg=").append(strIsoLinuxCfg);
1183 }
1184
1185 /*
1186 * Replace menu configuration file as well.
1187 * Some distros (Linux Mint) has only isolinux.cfg. No menu.cfg or txt.cfg.
1188 */
1189 if (RTStrICmp(pszMenuConfigFilename, "/isolinux/isolinux.cfg") != 0)
1190 {
1191
1192 /* Replace menu configuration file as well. */
1193 rVecArgs.append().assign(pszMenuConfigFilename).append("=:must-remove:");
1194 strTxtCfg = mpParent->i_getAuxiliaryBasePath();
1195 if (fMenuConfigIsGrub)
1196 strTxtCfg.append("grub.cfg");
1197 else
1198 strTxtCfg.append("isolinux-txt.cfg");
1199 rVecArgs.append().assign(pszMenuConfigFilename).append("=").append(strTxtCfg);
1200 }
1201 }
1202 catch (std::bad_alloc &)
1203 {
1204 return E_OUTOFMEMORY;
1205 }
1206
1207 /*
1208 * Edit the isolinux.cfg file if it is there.
1209 */
1210 if (fIsoLinuxCfgExists)
1211 {
1212 GeneralTextScript Editor(mpParent);
1213 HRESULT hrc = loadAndParseFileFromIso(hVfsOrgIso, "/isolinux/isolinux.cfg", &Editor);
1214 if (SUCCEEDED(hrc))
1215 hrc = editIsoLinuxCfg(&Editor, RTPathFilename(pszMenuConfigFilename));
1216 if (SUCCEEDED(hrc))
1217 {
1218 hrc = Editor.save(strIsoLinuxCfg, fOverwrite);
1219 if (SUCCEEDED(hrc))
1220 {
1221 try
1222 {
1223 rVecFiles.append(strIsoLinuxCfg);
1224 }
1225 catch (std::bad_alloc &)
1226 {
1227 RTFileDelete(strIsoLinuxCfg.c_str());
1228 hrc = E_OUTOFMEMORY;
1229 }
1230 }
1231 }
1232 if (FAILED(hrc))
1233 return hrc;
1234 }
1235
1236 /*
1237 * Edit the menu config file.
1238 * Some distros (Linux Mint) has only isolinux.cfg. No menu.cfg or txt.cfg.
1239 */
1240 if (RTStrICmp(pszMenuConfigFilename, "/isolinux/isolinux.cfg") != 0)
1241 {
1242 GeneralTextScript Editor(mpParent);
1243 HRESULT hrc = loadAndParseFileFromIso(hVfsOrgIso, pszMenuConfigFilename, &Editor);
1244 if (SUCCEEDED(hrc))
1245 {
1246 if (fMenuConfigIsGrub)
1247 hrc = editGrubCfg(&Editor);
1248 else
1249 hrc = editDebianMenuCfg(&Editor);
1250 if (SUCCEEDED(hrc))
1251 {
1252 hrc = Editor.save(strTxtCfg, fOverwrite);
1253 if (SUCCEEDED(hrc))
1254 {
1255 try
1256 {
1257 rVecFiles.append(strTxtCfg);
1258 }
1259 catch (std::bad_alloc &)
1260 {
1261 RTFileDelete(strTxtCfg.c_str());
1262 hrc = E_OUTOFMEMORY;
1263 }
1264 }
1265 }
1266 }
1267 if (FAILED(hrc))
1268 return hrc;
1269 }
1270
1271 /*
1272 * Call parent to add the preseed file from mAlg.
1273 */
1274 return UnattendedLinuxInstaller::addFilesToAuxVisoVectors(rVecArgs, rVecFiles, hVfsOrgIso, fOverwrite);
1275}
1276
1277HRESULT UnattendedDebianInstaller::editIsoLinuxCfg(GeneralTextScript *pEditor, const char *pszMenuConfigFileName)
1278{
1279 try
1280 {
1281 /* Include menu config file. Since it can be txt.cfg, menu.cfg or something else we need to parametrize this. */
1282 if (pszMenuConfigFileName && pszMenuConfigFileName[0] != '\0')
1283 {
1284 std::vector<size_t> vecLineNumbers = pEditor->findTemplate("include", RTCString::CaseInsensitive);
1285 for (size_t i = 0; i < vecLineNumbers.size(); ++i)
1286 {
1287 if (pEditor->getContentOfLine(vecLineNumbers[i]).startsWithWord("include", RTCString::CaseInsensitive))
1288 {
1289 Utf8Str strIncludeLine("include ");
1290 strIncludeLine.append(pszMenuConfigFileName);
1291 HRESULT hrc = pEditor->setContentOfLine(vecLineNumbers.at(i), strIncludeLine);
1292 if (FAILED(hrc))
1293 return hrc;
1294 }
1295 }
1296 }
1297
1298 /* Comment out default directives since in Debian case default is handled in menu config file. */
1299 std::vector<size_t> vecLineNumbers = pEditor->findTemplate("default", RTCString::CaseInsensitive);
1300 for (size_t i = 0; i < vecLineNumbers.size(); ++i)
1301 if (pEditor->getContentOfLine(vecLineNumbers[i]).startsWithWord("default", RTCString::CaseInsensitive)
1302 && !pEditor->getContentOfLine(vecLineNumbers[i]).contains("default vesa", RTCString::CaseInsensitive))
1303 {
1304 HRESULT hrc = pEditor->prependToLine(vecLineNumbers.at(i), "#");
1305 if (FAILED(hrc))
1306 return hrc;
1307 }
1308
1309 /* Comment out "ui gfxboot bootlogo" line as it somehow messes things up on Kubuntu 20.04 (possibly others as well). */
1310 vecLineNumbers = pEditor->findTemplate("ui gfxboot", RTCString::CaseInsensitive);
1311 for (size_t i = 0; i < vecLineNumbers.size(); ++i)
1312 if (pEditor->getContentOfLine(vecLineNumbers[i]).startsWithWord("ui gfxboot", RTCString::CaseInsensitive))
1313 {
1314 HRESULT hrc = pEditor->prependToLine(vecLineNumbers.at(i), "#");
1315 if (FAILED(hrc))
1316 return hrc;
1317 }
1318 }
1319 catch (std::bad_alloc &)
1320 {
1321 return E_OUTOFMEMORY;
1322 }
1323 return UnattendedLinuxInstaller::editIsoLinuxCfg(pEditor);
1324}
1325
1326HRESULT UnattendedDebianInstaller::editDebianMenuCfg(GeneralTextScript *pEditor)
1327{
1328 /*
1329 * Unlike Redhats, Debian variants define boot menu not in isolinux.cfg but some other
1330 * menu configuration files. They are mostly called txt.cfg and/or menu.cfg (and possibly some other names)
1331 * In this functions we attempt to set menu's default label (default menu item) to the one containing the word 'install',
1332 * failing to find such a label (on Kubuntu 20.04 for example) we pick the first label with name 'live'.
1333 */
1334 try
1335 {
1336 HRESULT hrc = S_OK;
1337 std::vector<size_t> vecLineNumbers = pEditor->findTemplate("label", RTCString::CaseInsensitive);
1338 const char *pszNewLabelName = "VBoxUnatendedInstall";
1339 bool fLabelFound = modifyLabelLine(pEditor, vecLineNumbers, "install", pszNewLabelName);
1340 if (!fLabelFound)
1341 fLabelFound = modifyLabelLine(pEditor, vecLineNumbers, "live", pszNewLabelName);
1342
1343 if (!fLabelFound)
1344 hrc = E_FAIL;;
1345
1346 if (SUCCEEDED(hrc))
1347 {
1348 /* Modify the content of default lines so that they point to label we have chosen above. */
1349 Utf8Str strNewContent("default ");
1350 strNewContent.append(pszNewLabelName);
1351
1352 std::vector<size_t> vecDefaultLineNumbers = pEditor->findTemplate("default", RTCString::CaseInsensitive);
1353 if (!vecDefaultLineNumbers.empty())
1354 {
1355 for (size_t j = 0; j < vecDefaultLineNumbers.size(); ++j)
1356 {
1357 hrc = pEditor->setContentOfLine(vecDefaultLineNumbers[j], strNewContent);
1358 if (FAILED(hrc))
1359 break;
1360 }
1361 }
1362 /* Add a defaul label line. */
1363 else
1364 hrc = pEditor->appendLine(strNewContent);
1365 }
1366 if (FAILED(hrc))
1367 return hrc;
1368 }
1369 catch (std::bad_alloc &)
1370 {
1371 return E_OUTOFMEMORY;
1372 }
1373 return UnattendedLinuxInstaller::editIsoLinuxCommon(pEditor);
1374}
1375
1376bool UnattendedDebianInstaller::modifyLabelLine(GeneralTextScript *pEditor, const std::vector<size_t> &vecLineNumbers,
1377 const char *pszKeyWord, const char *pszNewLabelName)
1378{
1379 if (!pEditor)
1380 return false;
1381 Utf8Str strNewLabel("label ");
1382 strNewLabel.append(pszNewLabelName);
1383 HRESULT hrc = S_OK;
1384 for (size_t i = 0; i < vecLineNumbers.size(); ++i)
1385 {
1386 RTCString const &rContent = pEditor->getContentOfLine(vecLineNumbers[i]);
1387 /* Skip this line if it does not start with the word 'label'. */
1388 if (!RTStrIStartsWith(rContent.c_str(), "label"))
1389 continue;
1390 /* Use the first menu item starting with word label and includes pszKeyWord.*/
1391 if (RTStrIStr(rContent.c_str(), pszKeyWord) != NULL)
1392 {
1393 /* Set the content of the line. It looks like multiple word labels (like label Debian Installer)
1394 * does not work very well in some cases. */
1395 hrc = pEditor->setContentOfLine(vecLineNumbers[i], strNewLabel);
1396 if (SUCCEEDED(hrc))
1397 return true;
1398 }
1399 }
1400 return false;
1401}
1402
1403
1404//////////////////////////////////////////////////////////////////////////////////////////////////////
1405/*
1406*
1407*
1408* Implementation UnattendedUbuntuAutoInstallInstaller functions
1409*
1410*/
1411//////////////////////////////////////////////////////////////////////////////////////////////////////
1412HRESULT UnattendedUbuntuAutoInstallInstaller::addFilesToAuxVisoVectors(RTCList<RTCString> &rVecArgs, RTCList<RTCString> &rVecFiles,
1413 RTVFS hVfsOrgIso, bool fOverwrite)
1414{
1415 try
1416 {
1417 /* Add the (empty) meta-data file to the ISO: */
1418 Utf8Str strUnattendedTemplates;
1419 int vrc = RTPathAppPrivateNoArchCxx(strUnattendedTemplates);
1420 AssertRCReturn(vrc, mpParent->setErrorVrc(vrc));
1421 vrc = RTPathAppendCxx(strUnattendedTemplates, "UnattendedTemplates");
1422 AssertRCReturn(vrc, mpParent->setErrorVrc(vrc));
1423 rVecArgs.append().assign("/meta-data=").append(strUnattendedTemplates).append("/ubuntu_autoinstall_meta_data");
1424 }
1425 catch (std::bad_alloc &)
1426 {
1427 return E_OUTOFMEMORY;
1428 }
1429
1430 /*
1431 * Call parent to add default Debian-based stuff.
1432 */
1433 return UnattendedDebianInstaller::addFilesToAuxVisoVectors(rVecArgs, rVecFiles, hVfsOrgIso, fOverwrite);
1434}
1435
1436
1437//////////////////////////////////////////////////////////////////////////////////////////////////////
1438/*
1439*
1440*
1441* Implementation UnattendedRhel6Installer functions
1442*
1443*/
1444//////////////////////////////////////////////////////////////////////////////////////////////////////
1445HRESULT UnattendedRhelInstaller::addFilesToAuxVisoVectors(RTCList<RTCString> &rVecArgs, RTCList<RTCString> &rVecFiles,
1446 RTVFS hVfsOrgIso, bool fOverwrite)
1447{
1448 /*
1449 * Figure out the name of the menu config file that we have to edit.
1450 */
1451 bool fMenuConfigIsGrub = false;
1452 const char *pszMenuConfigFilename = "/isolinux/isolinux.cfg";
1453 if (!hlpVfsFileExists(hVfsOrgIso, pszMenuConfigFilename))
1454 {
1455 /* arm64 variants of Oracle Linux 9 have grub. */
1456 if (hlpVfsFileExists(hVfsOrgIso, "/EFI/BOOT/grub.cfg"))
1457 {
1458 pszMenuConfigFilename = "/EFI/BOOT/grub.cfg";
1459 fMenuConfigIsGrub = true;
1460 }
1461 else
1462 AssertFailed();
1463 }
1464
1465 /*
1466 * VISO bits and filenames.
1467 */
1468 RTCString strBootCfg;
1469 try
1470 {
1471#if 1
1472 /* Remaster ISO. */
1473 rVecArgs.append() = "--no-file-mode";
1474 rVecArgs.append() = "--no-dir-mode";
1475
1476 rVecArgs.append() = "--import-iso";
1477 rVecArgs.append(mpParent->i_getIsoPath());
1478
1479 rVecArgs.append() = "--file-mode=0444";
1480 rVecArgs.append() = "--dir-mode=0555";
1481
1482 /* Replace the grub.cfg/isolinux.cfg configuration file. */
1483 if (fMenuConfigIsGrub)
1484 {
1485 /* Replace menu configuration file as well. */
1486 rVecArgs.append().assign(pszMenuConfigFilename).append("=:must-remove:");
1487 strBootCfg = mpParent->i_getAuxiliaryBasePath();
1488 strBootCfg.append("grub.cfg");
1489 rVecArgs.append().assign(pszMenuConfigFilename).append("=").append(strBootCfg);
1490 }
1491 else
1492 {
1493 /* First remove. */
1494 rVecArgs.append() = "isolinux/isolinux.cfg=:must-remove:";
1495 strBootCfg = mpParent->i_getAuxiliaryBasePath();
1496 strBootCfg.append("isolinux-isolinux.cfg");
1497 rVecArgs.append().append("isolinux/isolinux.cfg=").append(strBootCfg);
1498 }
1499#else
1500 /** @todo Maybe we should just remaster the ISO for redhat derivatives too?
1501 * One less CDROM to mount. */
1502 /* Name the ISO. */
1503 rVecArgs.append() = "--volume-id=VBox Unattended Boot";
1504
1505 /* Copy the isolinux directory from the original install ISO. */
1506 rVecArgs.append().append("--push-iso=").append(mpParent->i_getIsoPath());
1507 rVecArgs.append() = "/isolinux=/isolinux";
1508 rVecArgs.append() = "--pop";
1509
1510 /* We replace isolinux.cfg with our edited version (see further down). */
1511 rVecArgs.append() = "/isolinux/isolinux.cfg=:must-remove:";
1512
1513 strBootCfg = mpParent->i_getAuxiliaryBasePath();
1514 strBootCfg.append("isolinux-isolinux.cfg");
1515 rVecArgs.append().append("/isolinux/isolinux.cfg=").append(strBootCfg);
1516
1517 /* Configure booting /isolinux/isolinux.bin. */
1518 rVecArgs.append() = "--eltorito-boot";
1519 rVecArgs.append() = "/isolinux/isolinux.bin";
1520 rVecArgs.append() = "--no-emulation-boot";
1521 rVecArgs.append() = "--boot-info-table";
1522 rVecArgs.append() = "--boot-load-seg=0x07c0";
1523 rVecArgs.append() = "--boot-load-size=4";
1524
1525 /* Make the boot catalog visible in the file system. */
1526 rVecArgs.append() = "--boot-catalog=/isolinux/vboxboot.cat";
1527#endif
1528 }
1529 catch (std::bad_alloc &)
1530 {
1531 return E_OUTOFMEMORY;
1532 }
1533
1534 {
1535 GeneralTextScript Editor(mpParent);
1536 HRESULT hrc = loadAndParseFileFromIso(hVfsOrgIso, pszMenuConfigFilename, &Editor);
1537 if (SUCCEEDED(hrc))
1538 {
1539 if (fMenuConfigIsGrub)
1540 hrc = editGrubCfg(&Editor);
1541 else
1542 hrc = editIsoLinuxCfg(&Editor);
1543 if (SUCCEEDED(hrc))
1544 {
1545 hrc = Editor.save(strBootCfg, fOverwrite);
1546 if (SUCCEEDED(hrc))
1547 {
1548 try
1549 {
1550 rVecFiles.append(strBootCfg);
1551 }
1552 catch (std::bad_alloc &)
1553 {
1554 RTFileDelete(strBootCfg.c_str());
1555 hrc = E_OUTOFMEMORY;
1556 }
1557 }
1558 }
1559 }
1560 if (FAILED(hrc))
1561 return hrc;
1562 }
1563
1564 /*
1565 * Call parent to add the ks.cfg file from mAlg.
1566 */
1567 return UnattendedLinuxInstaller::addFilesToAuxVisoVectors(rVecArgs, rVecFiles, hVfsOrgIso, fOverwrite);
1568}
1569
1570
1571//////////////////////////////////////////////////////////////////////////////////////////////////////
1572/*
1573*
1574*
1575* Implementation UnattendedSuseInstaller functions
1576*
1577*/
1578//////////////////////////////////////////////////////////////////////////////////////////////////////
1579#if 0 /* doesn't work, so convert later */
1580/*
1581 *
1582 * UnattendedSuseInstaller protected methods
1583 *
1584*/
1585HRESULT UnattendedSuseInstaller::setUserData()
1586{
1587 HRESULT hrc = S_OK;
1588 //here base class function must be called first
1589 //because user home directory is set after user name
1590 hrc = UnattendedInstaller::setUserData();
1591
1592 hrc = mAlg->setField(USERHOMEDIR_ID, "");
1593 if (FAILED(hrc))
1594 return hrc;
1595
1596 return hrc;
1597}
1598
1599/*
1600 *
1601 * UnattendedSuseInstaller private methods
1602 *
1603*/
1604
1605HRESULT UnattendedSuseInstaller::iv_initialPhase()
1606{
1607 Assert(isAuxiliaryIsoNeeded());
1608 if (mParent->i_isGuestOs64Bit())
1609 mFilesAndDirsToExtractFromIso.append("boot/x86_64/loader/ ");
1610 else
1611 mFilesAndDirsToExtractFromIso.append("boot/i386/loader/ ");
1612 return extractOriginalIso(mFilesAndDirsToExtractFromIso);
1613}
1614
1615
1616HRESULT UnattendedSuseInstaller::setupScriptOnAuxiliaryCD(const Utf8Str &path)
1617{
1618 HRESULT hrc = S_OK;
1619
1620 GeneralTextScript isoSuseCfgScript(mpParent);
1621 hrc = isoSuseCfgScript.read(path);
1622 hrc = isoSuseCfgScript.parse();
1623 //fix linux core bootable parameters: add path to the preseed script
1624
1625 std::vector<size_t> listOfLines = isoSuseCfgScript.findTemplate("append");
1626 for(unsigned int i=0; i<listOfLines.size(); ++i)
1627 {
1628 isoSuseCfgScript.appendToLine(listOfLines.at(i),
1629 " auto=true priority=critical autoyast=default instmode=cd quiet splash noprompt noshell --");
1630 }
1631
1632 //find all lines with "label" inside
1633 listOfLines = isoSuseCfgScript.findTemplate("label");
1634 for(unsigned int i=0; i<listOfLines.size(); ++i)
1635 {
1636 Utf8Str content = isoSuseCfgScript.getContentOfLine(listOfLines.at(i));
1637
1638 //suppose general string looks like "label linux", two words separated by " ".
1639 RTCList<RTCString> partsOfcontent = content.split(" ");
1640
1641 if (partsOfcontent.at(1).contains("linux"))
1642 {
1643 std::vector<size_t> listOfDefault = isoSuseCfgScript.findTemplate("default");
1644 //handle the lines more intelligently
1645 for(unsigned int j=0; j<listOfDefault.size(); ++j)
1646 {
1647 Utf8Str newContent("default ");
1648 newContent.append(partsOfcontent.at(1));
1649 isoSuseCfgScript.setContentOfLine(listOfDefault.at(j), newContent);
1650 }
1651 }
1652 }
1653
1654 hrc = isoSuseCfgScript.save(path, true);
1655
1656 LogRelFunc(("UnattendedSuseInstaller::setupScriptsOnAuxiliaryCD(): The file %s has been changed\n", path.c_str()));
1657
1658 return hrc;
1659}
1660#endif
1661
1662
1663//////////////////////////////////////////////////////////////////////////////////////////////////////
1664/*
1665*
1666*
1667* Implementation UnattendedFreeBsdInstaller functions
1668*
1669*/
1670//////////////////////////////////////////////////////////////////////////////////////////////////////
1671HRESULT UnattendedFreeBsdInstaller::addFilesToAuxVisoVectors(RTCList<RTCString> &rVecArgs, RTCList<RTCString> &rVecFiles,
1672 RTVFS hVfsOrgIso, bool fOverwrite)
1673{
1674 try
1675 {
1676 RTCString strScriptName;
1677 strScriptName = mpParent->i_getAuxiliaryBasePath();
1678 strScriptName.append(mMainScript.getDefaultFilename());
1679
1680 /* Need to retain the original file permissions for executables. */
1681 rVecArgs.append() = "--no-file-mode";
1682 rVecArgs.append() = "--no-dir-mode";
1683
1684 rVecArgs.append() = "--import-iso";
1685 rVecArgs.append(mpParent->i_getIsoPath());
1686
1687 rVecArgs.append() = "--file-mode=0444";
1688 rVecArgs.append() = "--dir-mode=0555";
1689
1690 /* Remaster ISO, the installer config has to go into /etc. */
1691 rVecArgs.append().append("/etc/installerconfig=").append(strScriptName);
1692 }
1693 catch (std::bad_alloc &)
1694 {
1695 return E_OUTOFMEMORY;
1696 }
1697
1698 /*
1699 * Call parent to add the remaining files
1700 */
1701 return UnattendedInstaller::addFilesToAuxVisoVectors(rVecArgs, rVecFiles, hVfsOrgIso, fOverwrite);
1702}
Note: See TracBrowser for help on using the repository browser.

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