VirtualBox

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

Last change on this file was 102534, checked in by vboxsync, 5 months ago

Main/Unattended: Renamed the attribute "IUnattended::password" to "IUnattended::userPassword". Added new getter/setter attribute "IUnattended::adminPassword", to set a dedicated admin / root password. If not specified explicitly, the password from "IUnattended::userPassword" will be used. Extended testcases. bugref:10554

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

© 2023 Oracle
ContactPrivacy policyTerms of Use