VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/UnattendedImpl.cpp@ 93571

最後變更 在這個檔案從93571是 93571,由 vboxsync 提交於 3 年 前

Main/Unattended: Only valid imageIndex if we detect images on the ISO, fail not a valid index. bugref:9781

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 135.5 KB
 
1/* $Id: UnattendedImpl.cpp 93571 2022-02-03 11:02:30Z vboxsync $ */
2/** @file
3 * Unattended class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_MAIN_UNATTENDED
23#include "LoggingNew.h"
24#include "VirtualBoxBase.h"
25#include "UnattendedImpl.h"
26#include "UnattendedInstaller.h"
27#include "UnattendedScript.h"
28#include "VirtualBoxImpl.h"
29#include "SystemPropertiesImpl.h"
30#include "MachineImpl.h"
31#include "Global.h"
32#include "StringifyEnums.h"
33
34#include <VBox/err.h>
35#include <iprt/cpp/xml.h>
36#include <iprt/ctype.h>
37#include <iprt/file.h>
38#include <iprt/formats/wim.h>
39#include <iprt/fsvfs.h>
40#include <iprt/inifile.h>
41#include <iprt/locale.h>
42#include <iprt/path.h>
43#include <iprt/vfs.h>
44
45using namespace std;
46
47
48/*********************************************************************************************************************************
49* Structures and Typedefs *
50*********************************************************************************************************************************/
51/**
52 * Controller slot for a DVD drive.
53 *
54 * The slot can be free and needing a drive to be attached along with the ISO
55 * image, or it may already be there and only need mounting the ISO. The
56 * ControllerSlot::fFree member indicates which it is.
57 */
58struct ControllerSlot
59{
60 StorageBus_T enmBus;
61 Utf8Str strControllerName;
62 LONG iPort;
63 LONG iDevice;
64 bool fFree;
65
66 ControllerSlot(StorageBus_T a_enmBus, const Utf8Str &a_rName, LONG a_iPort, LONG a_iDevice, bool a_fFree)
67 : enmBus(a_enmBus), strControllerName(a_rName), iPort(a_iPort), iDevice(a_iDevice), fFree(a_fFree)
68 {}
69
70 bool operator<(const ControllerSlot &rThat) const
71 {
72 if (enmBus == rThat.enmBus)
73 {
74 if (strControllerName == rThat.strControllerName)
75 {
76 if (iPort == rThat.iPort)
77 return iDevice < rThat.iDevice;
78 return iPort < rThat.iPort;
79 }
80 return strControllerName < rThat.strControllerName;
81 }
82
83 /*
84 * Bus comparsion in boot priority order.
85 */
86 /* IDE first. */
87 if (enmBus == StorageBus_IDE)
88 return true;
89 if (rThat.enmBus == StorageBus_IDE)
90 return false;
91 /* SATA next */
92 if (enmBus == StorageBus_SATA)
93 return true;
94 if (rThat.enmBus == StorageBus_SATA)
95 return false;
96 /* SCSI next */
97 if (enmBus == StorageBus_SCSI)
98 return true;
99 if (rThat.enmBus == StorageBus_SCSI)
100 return false;
101 /* numerical */
102 return (int)enmBus < (int)rThat.enmBus;
103 }
104
105 bool operator==(const ControllerSlot &rThat) const
106 {
107 return enmBus == rThat.enmBus
108 && strControllerName == rThat.strControllerName
109 && iPort == rThat.iPort
110 && iDevice == rThat.iDevice;
111 }
112};
113
114/**
115 * Installation disk.
116 *
117 * Used when reconfiguring the VM.
118 */
119typedef struct UnattendedInstallationDisk
120{
121 StorageBus_T enmBusType; /**< @todo nobody is using this... */
122 Utf8Str strControllerName;
123 DeviceType_T enmDeviceType;
124 AccessMode_T enmAccessType;
125 LONG iPort;
126 LONG iDevice;
127 bool fMountOnly;
128 Utf8Str strImagePath;
129
130 UnattendedInstallationDisk(StorageBus_T a_enmBusType, Utf8Str const &a_rBusName, DeviceType_T a_enmDeviceType,
131 AccessMode_T a_enmAccessType, LONG a_iPort, LONG a_iDevice, bool a_fMountOnly,
132 Utf8Str const &a_rImagePath)
133 : enmBusType(a_enmBusType), strControllerName(a_rBusName), enmDeviceType(a_enmDeviceType), enmAccessType(a_enmAccessType)
134 , iPort(a_iPort), iDevice(a_iDevice), fMountOnly(a_fMountOnly), strImagePath(a_rImagePath)
135 {
136 Assert(strControllerName.length() > 0);
137 }
138
139 UnattendedInstallationDisk(std::list<ControllerSlot>::const_iterator const &itDvdSlot, Utf8Str const &a_rImagePath)
140 : enmBusType(itDvdSlot->enmBus), strControllerName(itDvdSlot->strControllerName), enmDeviceType(DeviceType_DVD)
141 , enmAccessType(AccessMode_ReadOnly), iPort(itDvdSlot->iPort), iDevice(itDvdSlot->iDevice)
142 , fMountOnly(!itDvdSlot->fFree), strImagePath(a_rImagePath)
143 {
144 Assert(strControllerName.length() > 0);
145 }
146} UnattendedInstallationDisk;
147
148
149/**
150 * OS/2 syslevel file header.
151 */
152#pragma pack(1)
153typedef struct OS2SYSLEVELHDR
154{
155 uint16_t uMinusOne; /**< 0x00: UINT16_MAX */
156 char achSignature[8]; /**< 0x02: "SYSLEVEL" */
157 uint8_t abReserved1[5]; /**< 0x0a: Usually zero. Ignore. */
158 uint16_t uSyslevelFileVer; /**< 0x0f: The syslevel file version: 1. */
159 uint8_t abReserved2[16]; /**< 0x11: Zero. Ignore. */
160 uint32_t offTable; /**< 0x21: Offset of the syslevel table. */
161} OS2SYSLEVELHDR;
162#pragma pack()
163AssertCompileSize(OS2SYSLEVELHDR, 0x25);
164
165/**
166 * OS/2 syslevel table entry.
167 */
168#pragma pack(1)
169typedef struct OS2SYSLEVELENTRY
170{
171 uint16_t id; /**< 0x00: ? */
172 uint8_t bEdition; /**< 0x02: The OS/2 edition: 0=standard, 1=extended, x=component defined */
173 uint8_t bVersion; /**< 0x03: 0x45 = 4.5 */
174 uint8_t bModify; /**< 0x04: Lower nibble is added to bVersion, so 0x45 0x02 => 4.52 */
175 uint8_t abReserved1[2]; /**< 0x05: Zero. Ignore. */
176 char achCsdLevel[8]; /**< 0x07: The current CSD level. */
177 char achCsdPrior[8]; /**< 0x0f: The prior CSD level. */
178 char szName[80]; /**< 0x5f: System/component name. */
179 char achId[9]; /**< 0x67: System/component ID. */
180 uint8_t bRefresh; /**< 0x70: Single digit refresh version, ignored if zero. */
181 char szType[9]; /**< 0x71: Some kind of type string. Optional */
182 uint8_t abReserved2[6]; /**< 0x7a: Zero. Ignore. */
183} OS2SYSLEVELENTRY;
184#pragma pack()
185AssertCompileSize(OS2SYSLEVELENTRY, 0x80);
186
187/**
188 * Concatenate image name and version strings and return.
189 * A possible output would be Windows 10 Home 10-0-19041
190 *
191 * @returns Concatenated name and version strings
192 */
193Utf8Str WIMImage::getNameAndVersion() const
194{
195 Utf8Str strNameAndVersion(mName);
196 /* Append the major version number if it is not empty. */
197 if (mVersionMajor.isEmpty())
198 return strNameAndVersion;
199 strNameAndVersion.appendPrintfNoThrow(" %s", mVersionMajor.c_str());
200 /* Same for the minor version number and build number. */
201 if (mVersionMinor.isEmpty())
202 return strNameAndVersion;
203 strNameAndVersion.appendPrintfNoThrow("-%s", mVersionMinor.c_str());
204 if (mVersionBuild.isEmpty())
205 return strNameAndVersion;
206 strNameAndVersion.appendPrintfNoThrow("-%s", mVersionBuild.c_str());
207 return strNameAndVersion;
208}
209
210//////////////////////////////////////////////////////////////////////////////////////////////////////
211/*
212*
213*
214* Implementation Unattended functions
215*
216*/
217//////////////////////////////////////////////////////////////////////////////////////////////////////
218
219Unattended::Unattended()
220 : mhThreadReconfigureVM(NIL_RTNATIVETHREAD), mfRtcUseUtc(false), mfGuestOs64Bit(false)
221 , mpInstaller(NULL), mpTimeZoneInfo(NULL), mfIsDefaultAuxiliaryBasePath(true), mfDoneDetectIsoOS(false)
222{ }
223
224Unattended::~Unattended()
225{
226 if (mpInstaller)
227 {
228 delete mpInstaller;
229 mpInstaller = NULL;
230 }
231}
232
233HRESULT Unattended::FinalConstruct()
234{
235 return BaseFinalConstruct();
236}
237
238void Unattended::FinalRelease()
239{
240 uninit();
241
242 BaseFinalRelease();
243}
244
245void Unattended::uninit()
246{
247 /* Enclose the state transition Ready->InUninit->NotReady */
248 AutoUninitSpan autoUninitSpan(this);
249 if (autoUninitSpan.uninitDone())
250 return;
251
252 unconst(mParent) = NULL;
253 mMachine.setNull();
254}
255
256/**
257 * Initializes the unattended object.
258 *
259 * @param aParent Pointer to the parent object.
260 */
261HRESULT Unattended::initUnattended(VirtualBox *aParent)
262{
263 LogFlowThisFunc(("aParent=%p\n", aParent));
264 ComAssertRet(aParent, E_INVALIDARG);
265
266 /* Enclose the state transition NotReady->InInit->Ready */
267 AutoInitSpan autoInitSpan(this);
268 AssertReturn(autoInitSpan.isOk(), E_FAIL);
269
270 unconst(mParent) = aParent;
271
272 /*
273 * Fill public attributes (IUnattended) with useful defaults.
274 */
275 try
276 {
277 mStrUser = "vboxuser";
278 mStrPassword = "changeme";
279 mfInstallGuestAdditions = false;
280 mfInstallTestExecService = false;
281 midxImage = 1;
282
283 HRESULT hrc = mParent->i_getSystemProperties()->i_getDefaultAdditionsISO(mStrAdditionsIsoPath);
284 ComAssertComRCRet(hrc, hrc);
285 }
286 catch (std::bad_alloc &)
287 {
288 return E_OUTOFMEMORY;
289 }
290
291 /*
292 * Confirm a successful initialization
293 */
294 autoInitSpan.setSucceeded();
295
296 return S_OK;
297}
298
299HRESULT Unattended::detectIsoOS()
300{
301 HRESULT hrc;
302 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
303
304/** @todo once UDF is implemented properly and we've tested this code a lot
305 * more, replace E_NOTIMPL with E_FAIL. */
306
307
308 /*
309 * Open the ISO.
310 */
311 RTVFSFILE hVfsFileIso;
312 int vrc = RTVfsFileOpenNormal(mStrIsoPath.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsFileIso);
313 if (RT_FAILURE(vrc))
314 return setErrorBoth(E_NOTIMPL, vrc, tr("Failed to open '%s' (%Rrc)"), mStrIsoPath.c_str(), vrc);
315
316 RTERRINFOSTATIC ErrInfo;
317 RTVFS hVfsIso;
318 vrc = RTFsIso9660VolOpen(hVfsFileIso, 0 /*fFlags*/, &hVfsIso, RTErrInfoInitStatic(&ErrInfo));
319 if (RT_SUCCESS(vrc))
320 {
321 /*
322 * Try do the detection. Repeat for different file system variations (nojoliet, noudf).
323 */
324 hrc = i_innerDetectIsoOS(hVfsIso);
325
326 RTVfsRelease(hVfsIso);
327 if (hrc != S_FALSE) /** @todo Finish the linux and windows detection code. Only OS/2 returns S_OK right now. */
328 hrc = E_NOTIMPL;
329 }
330 else if (RTErrInfoIsSet(&ErrInfo.Core))
331 hrc = setErrorBoth(E_NOTIMPL, vrc, tr("Failed to open '%s' as ISO FS (%Rrc) - %s"),
332 mStrIsoPath.c_str(), vrc, ErrInfo.Core.pszMsg);
333 else
334 hrc = setErrorBoth(E_NOTIMPL, vrc, tr("Failed to open '%s' as ISO FS (%Rrc)"), mStrIsoPath.c_str(), vrc);
335 RTVfsFileRelease(hVfsFileIso);
336
337 /*
338 * Just fake up some windows installation media locale (for <UILanguage>).
339 * Note! The translation here isn't perfect. Feel free to send us a patch.
340 */
341 if (mDetectedOSLanguages.size() == 0)
342 {
343 char szTmp[16];
344 const char *pszFilename = RTPathFilename(mStrIsoPath.c_str());
345 if ( pszFilename
346 && RT_C_IS_ALPHA(pszFilename[0])
347 && RT_C_IS_ALPHA(pszFilename[1])
348 && (pszFilename[2] == '-' || pszFilename[2] == '_') )
349 {
350 szTmp[0] = (char)RT_C_TO_LOWER(pszFilename[0]);
351 szTmp[1] = (char)RT_C_TO_LOWER(pszFilename[1]);
352 szTmp[2] = '-';
353 if (szTmp[0] == 'e' && szTmp[1] == 'n')
354 strcpy(&szTmp[3], "US");
355 else if (szTmp[0] == 'a' && szTmp[1] == 'r')
356 strcpy(&szTmp[3], "SA");
357 else if (szTmp[0] == 'd' && szTmp[1] == 'a')
358 strcpy(&szTmp[3], "DK");
359 else if (szTmp[0] == 'e' && szTmp[1] == 't')
360 strcpy(&szTmp[3], "EE");
361 else if (szTmp[0] == 'e' && szTmp[1] == 'l')
362 strcpy(&szTmp[3], "GR");
363 else if (szTmp[0] == 'h' && szTmp[1] == 'e')
364 strcpy(&szTmp[3], "IL");
365 else if (szTmp[0] == 'j' && szTmp[1] == 'a')
366 strcpy(&szTmp[3], "JP");
367 else if (szTmp[0] == 's' && szTmp[1] == 'v')
368 strcpy(&szTmp[3], "SE");
369 else if (szTmp[0] == 'u' && szTmp[1] == 'k')
370 strcpy(&szTmp[3], "UA");
371 else if (szTmp[0] == 'c' && szTmp[1] == 's')
372 strcpy(szTmp, "cs-CZ");
373 else if (szTmp[0] == 'n' && szTmp[1] == 'o')
374 strcpy(szTmp, "nb-NO");
375 else if (szTmp[0] == 'p' && szTmp[1] == 'p')
376 strcpy(szTmp, "pt-PT");
377 else if (szTmp[0] == 'p' && szTmp[1] == 't')
378 strcpy(szTmp, "pt-BR");
379 else if (szTmp[0] == 'c' && szTmp[1] == 'n')
380 strcpy(szTmp, "zh-CN");
381 else if (szTmp[0] == 'h' && szTmp[1] == 'k')
382 strcpy(szTmp, "zh-HK");
383 else if (szTmp[0] == 't' && szTmp[1] == 'w')
384 strcpy(szTmp, "zh-TW");
385 else if (szTmp[0] == 's' && szTmp[1] == 'r')
386 strcpy(szTmp, "sr-Latn-CS"); /* hmm */
387 else
388 {
389 szTmp[3] = (char)RT_C_TO_UPPER(pszFilename[0]);
390 szTmp[4] = (char)RT_C_TO_UPPER(pszFilename[1]);
391 szTmp[5] = '\0';
392 }
393 }
394 else
395 strcpy(szTmp, "en-US");
396 try
397 {
398 mDetectedOSLanguages.append(szTmp);
399 }
400 catch (std::bad_alloc &)
401 {
402 return E_OUTOFMEMORY;
403 }
404 }
405
406 /** @todo implement actual detection logic. */
407 return hrc;
408}
409
410HRESULT Unattended::i_innerDetectIsoOS(RTVFS hVfsIso)
411{
412 DETECTBUFFER uBuf;
413 VBOXOSTYPE enmOsType = VBOXOSTYPE_Unknown;
414 HRESULT hrc = i_innerDetectIsoOSWindows(hVfsIso, &uBuf, &enmOsType);
415 if (hrc == S_FALSE && enmOsType == VBOXOSTYPE_Unknown)
416 hrc = i_innerDetectIsoOSLinux(hVfsIso, &uBuf, &enmOsType);
417 if (hrc == S_FALSE && enmOsType == VBOXOSTYPE_Unknown)
418 hrc = i_innerDetectIsoOSOs2(hVfsIso, &uBuf, &enmOsType);
419 if (enmOsType != VBOXOSTYPE_Unknown)
420 {
421 try { mStrDetectedOSTypeId = Global::OSTypeId(enmOsType); }
422 catch (std::bad_alloc &) { hrc = E_OUTOFMEMORY; }
423 }
424 return hrc;
425}
426
427/**
428 * Parses XML Node assuming a structure as follows
429 * @verbatim
430 * <VERSION>
431 * <MAJOR>10</MAJOR>
432 * <MINOR>0</MINOR>
433 * <BUILD>19041</BUILD>
434 * ......
435 * </VERSION>
436 * @endverbatim
437 * @param pNode Points to the vesion XML node,
438 * @param image Out reference to an WIMImage instance.
439 */
440static void parseVersionElement(const xml::ElementNode *pNode, WIMImage &image)
441{
442 /* Major part. */
443 const ElementNode *pMajorNode = pNode->findChildElement("MAJOR");
444 if (!pMajorNode)
445 pMajorNode = pNode->findChildElement("major");
446 if (pMajorNode)
447 image.mVersionMajor = pMajorNode->getValue();
448
449 /* Minor part. */
450 const ElementNode *pMinorNode = pNode->findChildElement("MINOR");
451 if (!pMinorNode)
452 pMinorNode = pNode->findChildElement("minor");
453 if (pMinorNode)
454 image.mVersionMinor = pMinorNode->getValue();
455
456 /* Build part. */
457 const ElementNode *pBuildNode = pNode->findChildElement("BUILD");
458 if (!pBuildNode)
459 pBuildNode = pNode->findChildElement("build");
460 if (pBuildNode)
461 image.mVersionBuild = pBuildNode->getValue();
462}
463
464
465/**
466 * Parses XML tree assuming th following structure
467 * @verbatim
468 * <WIM>
469 * ....
470 * <IMAGE INDEX="1">
471 * ....
472 * <DISPLAYNAME>Windows 10 Home</DISPLAYNAME>
473 * <VERSION>
474 * ....
475 * </VERSION>
476 * </IMAGE>
477 * </WIM>
478 * @endverbatim
479 *
480 * @param pElmRoot Pointer to the root node of the tree,
481 * @param imageList Detected images are appended to this list.
482 */
483static void parseWimXMLData(const xml::ElementNode *pElmRoot, RTCList<WIMImage> &imageList)
484{
485 if (!pElmRoot)
486 return;
487
488 ElementNodesList children;
489 int cChildren = pElmRoot->getChildElements(children, "IMAGE");
490 if (cChildren == 0)
491 cChildren = pElmRoot->getChildElements(children, "image");
492
493 for (ElementNodesList::iterator iterator = children.begin(); iterator != children.end(); ++iterator)
494 {
495 const ElementNode *pChild = *(iterator);
496 if (!pChild)
497 continue;
498
499 const char *pszIndex = pChild->findAttributeValue("INDEX");
500 if (!pszIndex)
501 pszIndex = pChild->findAttributeValue("index");
502 if (!pszIndex)
503 continue;
504 uint32_t pu32 = 0;
505 int vrc = RTStrToUInt32Full(pszIndex, 10 /* uBase */, &pu32);
506
507 if (!RT_SUCCESS(vrc))
508 continue;
509
510 const ElementNode *pDisplayDescriptionNode = pChild->findChildElement("DISPLAYNAME");
511 if (!pDisplayDescriptionNode)
512 pDisplayDescriptionNode = pChild->findChildElement("displayname");
513 if (!pDisplayDescriptionNode)
514 continue;
515 WIMImage newImage;
516 newImage.mName = pDisplayDescriptionNode->getValue();
517 if (newImage.mName.isEmpty())
518 continue;
519
520 newImage.mImageIndex = pu32;
521 const ElementNode *pVersionElement = pChild->findChildElement("VERSION");
522 if (!pVersionElement)
523 pVersionElement = pChild->findChildElement("version");
524 if (pVersionElement)
525 parseVersionElement(pVersionElement, newImage);
526 imageList.append(newImage);
527 }
528}
529
530/**
531 * Detect Windows ISOs.
532 *
533 * @returns COM status code.
534 * @retval S_OK if detected
535 * @retval S_FALSE if not fully detected.
536 *
537 * @param hVfsIso The ISO file system.
538 * @param pBuf Read buffer.
539 * @param penmOsType Where to return the OS type. This is initialized to
540 * VBOXOSTYPE_Unknown.
541 */
542HRESULT Unattended::i_innerDetectIsoOSWindows(RTVFS hVfsIso, DETECTBUFFER *pBuf, VBOXOSTYPE *penmOsType)
543{
544 /** @todo The 'sources/' path can differ. */
545
546 // globalinstallorder.xml - vista beta2
547 // sources/idwbinfo.txt - ditto.
548 // sources/lang.ini - ditto.
549
550 RTVFSFILE hVfsFile;
551 /** @todo look at the install.wim file too, extracting the XML (easy) and
552 * figure out the available image numbers and such. The format is
553 * documented. It would also provide really accurate Windows
554 * version information without the need to guess. The current
555 * content of mStrDetectedOSVersion is mostly useful for human
556 * consumption. ~~Long term it should be possible to have version
557 * conditionals (expr style, please) in the templates, which
558 * would make them a lot easier to write and more flexible at the
559 * same time. - done already~~
560 *
561 * Here is how to list images inside an install.wim file from powershell:
562 * https://docs.microsoft.com/en-us/powershell/module/dism/get-windowsimage?view=windowsserver2022-ps
563 *
564 * Unfortunately, powershell is not available by default on non-windows hosts, so we
565 * have to do it ourselves of course, but this can help when coding & testing.
566 */
567
568 int vrc = RTVfsFileOpen(hVfsIso, "sources/install.wim", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
569 if (RT_SUCCESS(vrc))
570 {
571 WIMHEADERV1 header;
572 size_t cbRead = 0;
573 vrc = RTVfsFileRead(hVfsFile, &header, sizeof(header), &cbRead);
574 if (RT_SUCCESS(vrc) && cbRead == sizeof(header))
575 {
576 /* If the xml data is not compressed, xml data is not empty, and not too big. */
577 if ( (header.XmlData.bFlags & RESHDR_FLAGS_METADATA)
578 && !(header.XmlData.bFlags & RESHDR_FLAGS_COMPRESSED)
579 && header.XmlData.cbOrginal != 0
580 && header.XmlData.cbOrginal < _32M
581 && header.XmlData.cbOrginal == header.XmlData.cb)
582 {
583 char *pXmlBuf = (char*)RTMemAlloc(header.XmlData.cbOrginal);
584 if (pXmlBuf)
585 {
586 vrc = RTVfsFileReadAt(hVfsFile, (RTFOFF)header.XmlData.off, pXmlBuf, (size_t)header.XmlData.cbOrginal, NULL);
587 if (RT_SUCCESS(vrc))
588 {
589 xml::Document doc;
590 xml::XmlMemParser parser;
591 RTCString fileName = "dump";
592 try
593 {
594 parser.read(pXmlBuf, header.XmlData.cbOrginal, fileName, doc);
595 }
596 catch (xml::XmlError &rErr)
597 {
598 LogRel(("Unattended: An error has occured during XML parsing: %s\n", rErr.what()));
599 vrc = VERR_XAR_TOC_XML_PARSE_ERROR;
600 }
601 catch (...)
602 {
603 LogRel(("Unattended: An unknown error has occured during XML parsing.\n"));
604 vrc = VERR_UNEXPECTED_EXCEPTION;
605 }
606 if (RT_SUCCESS(vrc))
607 {
608 xml::ElementNode *pElmRoot = doc.getRootElement();
609 if (pElmRoot)
610 {
611 mDetectedImages.clear();
612 parseWimXMLData(pElmRoot, mDetectedImages);
613 }
614 else
615 LogRel(("Unattended: No root element found in XML Metadata of install.wim\n"));
616 }
617 }
618 else
619 LogRel(("Unattended: Failed during reading XML Metadata out of install.wim\n"));
620 RTMemFree(pXmlBuf);
621 }
622 }
623 else
624 LogRel(("Unattended: XML Metadata of install.wim is either compressed, empty, or too big\n"));
625 }
626 RTVfsFileRelease(hVfsFile);
627 }
628
629 const char *pszVersion = NULL;
630 const char *pszProduct = NULL;
631 /*
632 * Try look for the 'sources/idwbinfo.txt' file containing windows build info.
633 * This file appeared with Vista beta 2 from what we can tell. Before windows 10
634 * it contains easily decodable branch names, after that things goes weird.
635 */
636 vrc = RTVfsFileOpen(hVfsIso, "sources/idwbinfo.txt", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
637 if (RT_SUCCESS(vrc))
638 {
639 *penmOsType = VBOXOSTYPE_WinNT_x64;
640
641 RTINIFILE hIniFile;
642 vrc = RTIniFileCreateFromVfsFile(&hIniFile, hVfsFile, RTINIFILE_F_READONLY);
643 RTVfsFileRelease(hVfsFile);
644 if (RT_SUCCESS(vrc))
645 {
646 vrc = RTIniFileQueryValue(hIniFile, "BUILDINFO", "BuildArch", pBuf->sz, sizeof(*pBuf), NULL);
647 if (RT_SUCCESS(vrc))
648 {
649 LogRelFlow(("Unattended: sources/idwbinfo.txt: BuildArch=%s\n", pBuf->sz));
650 if ( RTStrNICmp(pBuf->sz, RT_STR_TUPLE("amd64")) == 0
651 || RTStrNICmp(pBuf->sz, RT_STR_TUPLE("x64")) == 0 /* just in case */ )
652 *penmOsType = VBOXOSTYPE_WinNT_x64;
653 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("x86")) == 0)
654 *penmOsType = VBOXOSTYPE_WinNT;
655 else
656 {
657 LogRel(("Unattended: sources/idwbinfo.txt: Unknown: BuildArch=%s\n", pBuf->sz));
658 *penmOsType = VBOXOSTYPE_WinNT_x64;
659 }
660 }
661
662 vrc = RTIniFileQueryValue(hIniFile, "BUILDINFO", "BuildBranch", pBuf->sz, sizeof(*pBuf), NULL);
663 if (RT_SUCCESS(vrc))
664 {
665 LogRelFlow(("Unattended: sources/idwbinfo.txt: BuildBranch=%s\n", pBuf->sz));
666 if ( RTStrNICmp(pBuf->sz, RT_STR_TUPLE("vista")) == 0
667 || RTStrNICmp(pBuf->sz, RT_STR_TUPLE("winmain_beta")) == 0)
668 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_WinVista);
669 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("lh_sp2rtm")) == 0)
670 {
671 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_WinVista);
672 pszVersion = "sp2";
673 }
674 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("longhorn_rtm")) == 0)
675 {
676 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_WinVista);
677 pszVersion = "sp1";
678 }
679 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("win7")) == 0)
680 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win7);
681 else if ( RTStrNICmp(pBuf->sz, RT_STR_TUPLE("winblue")) == 0
682 || RTStrNICmp(pBuf->sz, RT_STR_TUPLE("winmain_blue")) == 0
683 || RTStrNICmp(pBuf->sz, RT_STR_TUPLE("win81")) == 0 /* not seen, but just in case its out there */ )
684 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win81);
685 else if ( RTStrNICmp(pBuf->sz, RT_STR_TUPLE("win8")) == 0
686 || RTStrNICmp(pBuf->sz, RT_STR_TUPLE("winmain_win8")) == 0 )
687 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win8);
688 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("th1")) == 0)
689 {
690 pszVersion = "1507"; // aka. GA, retroactively 1507
691 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
692 }
693 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("th2")) == 0)
694 {
695 pszVersion = "1511"; // aka. threshold 2
696 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
697 }
698 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("rs1_release")) == 0)
699 {
700 pszVersion = "1607"; // aka. anniversay update; rs=redstone
701 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
702 }
703 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("rs2_release")) == 0)
704 {
705 pszVersion = "1703"; // aka. creators update
706 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
707 }
708 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("rs3_release")) == 0)
709 {
710 pszVersion = "1709"; // aka. fall creators update
711 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
712 }
713 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("rs4_release")) == 0)
714 {
715 pszVersion = "1803";
716 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
717 }
718 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("rs5_release")) == 0)
719 {
720 pszVersion = "1809";
721 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
722 }
723 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("19h1_release")) == 0)
724 {
725 pszVersion = "1903";
726 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
727 }
728 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("19h2_release")) == 0)
729 {
730 pszVersion = "1909"; // ??
731 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
732 }
733 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("20h1_release")) == 0)
734 {
735 pszVersion = "2003"; // ??
736 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
737 }
738 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("vb_release")) == 0)
739 {
740 pszVersion = "2004"; // ?? vb=Vibranium
741 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
742 }
743 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("20h2_release")) == 0)
744 {
745 pszVersion = "2009"; // ??
746 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
747 }
748 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("21h1_release")) == 0)
749 {
750 pszVersion = "2103"; // ??
751 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
752 }
753 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("21h2_release")) == 0)
754 {
755 pszVersion = "2109"; // ??
756 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
757 }
758 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("co_release")) == 0)
759 {
760 pszVersion = "21H2"; // ??
761 *penmOsType = VBOXOSTYPE_Win11_x64;
762 }
763 else
764 LogRel(("Unattended: sources/idwbinfo.txt: Unknown: BuildBranch=%s\n", pBuf->sz));
765 }
766 RTIniFileRelease(hIniFile);
767 }
768 }
769 bool fClarifyProd = false;
770 if (RT_FAILURE(vrc))
771 {
772 /*
773 * Check a INF file with a DriverVer that is updated with each service pack.
774 * DriverVer=10/01/2002,5.2.3790.3959
775 */
776 vrc = RTVfsFileOpen(hVfsIso, "AMD64/HIVESYS.INF", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
777 if (RT_SUCCESS(vrc))
778 *penmOsType = VBOXOSTYPE_WinNT_x64;
779 else
780 {
781 vrc = RTVfsFileOpen(hVfsIso, "I386/HIVESYS.INF", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
782 if (RT_SUCCESS(vrc))
783 *penmOsType = VBOXOSTYPE_WinNT;
784 }
785 if (RT_SUCCESS(vrc))
786 {
787 RTINIFILE hIniFile;
788 vrc = RTIniFileCreateFromVfsFile(&hIniFile, hVfsFile, RTINIFILE_F_READONLY);
789 RTVfsFileRelease(hVfsFile);
790 if (RT_SUCCESS(vrc))
791 {
792 vrc = RTIniFileQueryValue(hIniFile, "Version", "DriverVer", pBuf->sz, sizeof(*pBuf), NULL);
793 if (RT_SUCCESS(vrc))
794 {
795 LogRelFlow(("Unattended: HIVESYS.INF: DriverVer=%s\n", pBuf->sz));
796 const char *psz = strchr(pBuf->sz, ',');
797 psz = psz ? psz + 1 : pBuf->sz;
798 if (RTStrVersionCompare(psz, "6.0.0") >= 0)
799 LogRel(("Unattended: HIVESYS.INF: unknown: DriverVer=%s\n", psz));
800 else if (RTStrVersionCompare(psz, "5.2.0") >= 0) /* W2K3, XP64 */
801 {
802 fClarifyProd = true;
803 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win2k3);
804 if (RTStrVersionCompare(psz, "5.2.3790.3959") >= 0)
805 pszVersion = "sp2";
806 else if (RTStrVersionCompare(psz, "5.2.3790.1830") >= 0)
807 pszVersion = "sp1";
808 }
809 else if (RTStrVersionCompare(psz, "5.1.0") >= 0) /* XP */
810 {
811 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_WinXP);
812 if (RTStrVersionCompare(psz, "5.1.2600.5512") >= 0)
813 pszVersion = "sp3";
814 else if (RTStrVersionCompare(psz, "5.1.2600.2180") >= 0)
815 pszVersion = "sp2";
816 else if (RTStrVersionCompare(psz, "5.1.2600.1105") >= 0)
817 pszVersion = "sp1";
818 }
819 else if (RTStrVersionCompare(psz, "5.0.0") >= 0)
820 {
821 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win2k);
822 if (RTStrVersionCompare(psz, "5.0.2195.6717") >= 0)
823 pszVersion = "sp4";
824 else if (RTStrVersionCompare(psz, "5.0.2195.5438") >= 0)
825 pszVersion = "sp3";
826 else if (RTStrVersionCompare(psz, "5.0.2195.1620") >= 0)
827 pszVersion = "sp1";
828 }
829 else
830 LogRel(("Unattended: HIVESYS.INF: unknown: DriverVer=%s\n", psz));
831 }
832 RTIniFileRelease(hIniFile);
833 }
834 }
835 }
836 if (RT_FAILURE(vrc) || fClarifyProd)
837 {
838 /*
839 * NT 4 and older does not have DriverVer entries, we consult the PRODSPEC.INI, which
840 * works for NT4 & W2K. It does usually not reflect the service pack.
841 */
842 vrc = RTVfsFileOpen(hVfsIso, "AMD64/PRODSPEC.INI", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
843 if (RT_SUCCESS(vrc))
844 *penmOsType = VBOXOSTYPE_WinNT_x64;
845 else
846 {
847 vrc = RTVfsFileOpen(hVfsIso, "I386/PRODSPEC.INI", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
848 if (RT_SUCCESS(vrc))
849 *penmOsType = VBOXOSTYPE_WinNT;
850 }
851 if (RT_SUCCESS(vrc))
852 {
853
854 RTINIFILE hIniFile;
855 vrc = RTIniFileCreateFromVfsFile(&hIniFile, hVfsFile, RTINIFILE_F_READONLY);
856 RTVfsFileRelease(hVfsFile);
857 if (RT_SUCCESS(vrc))
858 {
859 vrc = RTIniFileQueryValue(hIniFile, "Product Specification", "Version", pBuf->sz, sizeof(*pBuf), NULL);
860 if (RT_SUCCESS(vrc))
861 {
862 LogRelFlow(("Unattended: PRODSPEC.INI: Version=%s\n", pBuf->sz));
863 if (RTStrVersionCompare(pBuf->sz, "5.1") >= 0) /* Shipped with XP + W2K3, but version stuck at 5.0. */
864 LogRel(("Unattended: PRODSPEC.INI: unknown: DriverVer=%s\n", pBuf->sz));
865 else if (RTStrVersionCompare(pBuf->sz, "5.0") >= 0) /* 2000 */
866 {
867 vrc = RTIniFileQueryValue(hIniFile, "Product Specification", "Product", pBuf->sz, sizeof(*pBuf), NULL);
868 if (RT_SUCCESS(vrc) && RTStrNICmp(pBuf->sz, RT_STR_TUPLE("Windows XP")) == 0)
869 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_WinXP);
870 else if (RT_SUCCESS(vrc) && RTStrNICmp(pBuf->sz, RT_STR_TUPLE("Windows Server 2003")) == 0)
871 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win2k3);
872 else
873 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win2k);
874
875 if (RT_SUCCESS(vrc) && (strstr(pBuf->sz, "Server") || strstr(pBuf->sz, "server")))
876 pszProduct = "Server";
877 }
878 else if (RTStrVersionCompare(pBuf->sz, "4.0") >= 0) /* NT4 */
879 *penmOsType = VBOXOSTYPE_WinNT4;
880 else
881 LogRel(("Unattended: PRODSPEC.INI: unknown: DriverVer=%s\n", pBuf->sz));
882
883 vrc = RTIniFileQueryValue(hIniFile, "Product Specification", "ProductType", pBuf->sz, sizeof(*pBuf), NULL);
884 if (RT_SUCCESS(vrc))
885 pszProduct = strcmp(pBuf->sz, "0") == 0 ? "Workstation" : /* simplification: */ "Server";
886 }
887 RTIniFileRelease(hIniFile);
888 }
889 }
890 if (fClarifyProd)
891 vrc = VINF_SUCCESS;
892 }
893 if (RT_FAILURE(vrc))
894 {
895 /*
896 * NT 3.x we look at the LoadIdentifier (boot manager) string in TXTSETUP.SIF/TXT.
897 */
898 vrc = RTVfsFileOpen(hVfsIso, "I386/TXTSETUP.SIF", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
899 if (RT_FAILURE(vrc))
900 vrc = RTVfsFileOpen(hVfsIso, "I386/TXTSETUP.INF", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
901 if (RT_SUCCESS(vrc))
902 {
903 *penmOsType = VBOXOSTYPE_WinNT;
904
905 RTINIFILE hIniFile;
906 vrc = RTIniFileCreateFromVfsFile(&hIniFile, hVfsFile, RTINIFILE_F_READONLY);
907 RTVfsFileRelease(hVfsFile);
908 if (RT_SUCCESS(vrc))
909 {
910 vrc = RTIniFileQueryValue(hIniFile, "SetupData", "ProductType", pBuf->sz, sizeof(*pBuf), NULL);
911 if (RT_SUCCESS(vrc))
912 pszProduct = strcmp(pBuf->sz, "0") == 0 ? "Workstation" : /* simplification: */ "Server";
913
914 vrc = RTIniFileQueryValue(hIniFile, "SetupData", "LoadIdentifier", pBuf->sz, sizeof(*pBuf), NULL);
915 if (RT_SUCCESS(vrc))
916 {
917 LogRelFlow(("Unattended: TXTSETUP.SIF: LoadIdentifier=%s\n", pBuf->sz));
918 char *psz = pBuf->sz;
919 while (!RT_C_IS_DIGIT(*psz) && *psz)
920 psz++;
921 char *psz2 = psz;
922 while (RT_C_IS_DIGIT(*psz2) || *psz2 == '.')
923 psz2++;
924 *psz2 = '\0';
925 if (RTStrVersionCompare(psz, "6.0") >= 0)
926 LogRel(("Unattended: TXTSETUP.SIF: unknown: LoadIdentifier=%s\n", pBuf->sz));
927 else if (RTStrVersionCompare(psz, "4.0") >= 0)
928 *penmOsType = VBOXOSTYPE_WinNT4;
929 else if (RTStrVersionCompare(psz, "3.1") >= 0)
930 {
931 *penmOsType = VBOXOSTYPE_WinNT3x;
932 pszVersion = psz;
933 }
934 else
935 LogRel(("Unattended: TXTSETUP.SIF: unknown: LoadIdentifier=%s\n", pBuf->sz));
936 }
937 RTIniFileRelease(hIniFile);
938 }
939 }
940 }
941
942 if (pszVersion)
943 try { mStrDetectedOSVersion = pszVersion; }
944 catch (std::bad_alloc &) { return E_OUTOFMEMORY; }
945 if (pszProduct)
946 try { mStrDetectedOSFlavor = pszProduct; }
947 catch (std::bad_alloc &) { return E_OUTOFMEMORY; }
948
949 /*
950 * Look for sources/lang.ini and try parse it to get the languages out of it.
951 */
952 /** @todo We could also check sources/??-* and boot/??-* if lang.ini is not
953 * found or unhelpful. */
954 vrc = RTVfsFileOpen(hVfsIso, "sources/lang.ini", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
955 if (RT_SUCCESS(vrc))
956 {
957 RTINIFILE hIniFile;
958 vrc = RTIniFileCreateFromVfsFile(&hIniFile, hVfsFile, RTINIFILE_F_READONLY);
959 RTVfsFileRelease(hVfsFile);
960 if (RT_SUCCESS(vrc))
961 {
962 mDetectedOSLanguages.clear();
963
964 uint32_t idxPair;
965 for (idxPair = 0; idxPair < 256; idxPair++)
966 {
967 size_t cbHalf = sizeof(*pBuf) / 2;
968 char *pszKey = pBuf->sz;
969 char *pszValue = &pBuf->sz[cbHalf];
970 vrc = RTIniFileQueryPair(hIniFile, "Available UI Languages", idxPair,
971 pszKey, cbHalf, NULL, pszValue, cbHalf, NULL);
972 if (RT_SUCCESS(vrc))
973 {
974 try
975 {
976 mDetectedOSLanguages.append(pszKey);
977 }
978 catch (std::bad_alloc &)
979 {
980 RTIniFileRelease(hIniFile);
981 return E_OUTOFMEMORY;
982 }
983 }
984 else if (vrc == VERR_NOT_FOUND)
985 break;
986 else
987 Assert(vrc == VERR_BUFFER_OVERFLOW);
988 }
989 if (idxPair == 0)
990 LogRel(("Unattended: Warning! Empty 'Available UI Languages' section in sources/lang.ini\n"));
991 RTIniFileRelease(hIniFile);
992 }
993 }
994
995 return S_FALSE;
996}
997
998/**
999 * Detects linux architecture.
1000 *
1001 * @returns true if detected, false if not.
1002 * @param pszArch The architecture string.
1003 * @param penmOsType Where to return the arch and type on success.
1004 * @param enmBaseOsType The base (x86) OS type to return.
1005 */
1006static bool detectLinuxArch(const char *pszArch, VBOXOSTYPE *penmOsType, VBOXOSTYPE enmBaseOsType)
1007{
1008 if ( RTStrNICmp(pszArch, RT_STR_TUPLE("amd64")) == 0
1009 || RTStrNICmp(pszArch, RT_STR_TUPLE("x86_64")) == 0
1010 || RTStrNICmp(pszArch, RT_STR_TUPLE("x86-64")) == 0 /* just in case */
1011 || RTStrNICmp(pszArch, RT_STR_TUPLE("x64")) == 0 /* ditto */ )
1012 {
1013 *penmOsType = (VBOXOSTYPE)(enmBaseOsType | VBOXOSTYPE_x64);
1014 return true;
1015 }
1016
1017 if ( RTStrNICmp(pszArch, RT_STR_TUPLE("x86")) == 0
1018 || RTStrNICmp(pszArch, RT_STR_TUPLE("i386")) == 0
1019 || RTStrNICmp(pszArch, RT_STR_TUPLE("i486")) == 0
1020 || RTStrNICmp(pszArch, RT_STR_TUPLE("i586")) == 0
1021 || RTStrNICmp(pszArch, RT_STR_TUPLE("i686")) == 0
1022 || RTStrNICmp(pszArch, RT_STR_TUPLE("i786")) == 0
1023 || RTStrNICmp(pszArch, RT_STR_TUPLE("i886")) == 0
1024 || RTStrNICmp(pszArch, RT_STR_TUPLE("i986")) == 0)
1025 {
1026 *penmOsType = enmBaseOsType;
1027 return true;
1028 }
1029
1030 /** @todo check for 'noarch' since source CDs have been seen to use that. */
1031 return false;
1032}
1033
1034/**
1035 * Detects linux architecture by searching for the architecture substring in @p pszArch.
1036 *
1037 * @returns true if detected, false if not.
1038 * @param pszArch The architecture string.
1039 * @param penmOsType Where to return the arch and type on success.
1040 * @param enmBaseOsType The base (x86) OS type to return.
1041 */
1042static bool detectLinuxArchII(const char *pszArch, VBOXOSTYPE *penmOsType, VBOXOSTYPE enmBaseOsType)
1043{
1044 if ( RTStrIStr(pszArch, "amd64") != NULL
1045 || RTStrIStr(pszArch, "x86_64") != NULL
1046 || RTStrIStr(pszArch, "x86-64") != NULL /* just in case */
1047 || RTStrIStr(pszArch, "x64") != NULL /* ditto */ )
1048 {
1049 *penmOsType = (VBOXOSTYPE)(enmBaseOsType | VBOXOSTYPE_x64);
1050 return true;
1051 }
1052
1053 if ( RTStrIStr(pszArch, "x86") != NULL
1054 || RTStrIStr(pszArch, "i386") != NULL
1055 || RTStrIStr(pszArch, "i486") != NULL
1056 || RTStrIStr(pszArch, "i586") != NULL
1057 || RTStrIStr(pszArch, "i686") != NULL
1058 || RTStrIStr(pszArch, "i786") != NULL
1059 || RTStrIStr(pszArch, "i886") != NULL
1060 || RTStrIStr(pszArch, "i986") != NULL)
1061 {
1062 *penmOsType = enmBaseOsType;
1063 return true;
1064 }
1065 return false;
1066}
1067
1068static bool detectLinuxDistroName(const char *pszOsAndVersion, VBOXOSTYPE *penmOsType, const char **ppszNext)
1069{
1070 bool fRet = true;
1071
1072 if ( RTStrNICmp(pszOsAndVersion, RT_STR_TUPLE("Red")) == 0
1073 && !RT_C_IS_ALNUM(pszOsAndVersion[3]))
1074
1075 {
1076 pszOsAndVersion = RTStrStripL(pszOsAndVersion + 3);
1077 if ( RTStrNICmp(pszOsAndVersion, RT_STR_TUPLE("Hat")) == 0
1078 && !RT_C_IS_ALNUM(pszOsAndVersion[3]))
1079 {
1080 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_RedHat);
1081 pszOsAndVersion = RTStrStripL(pszOsAndVersion + 3);
1082 }
1083 else
1084 fRet = false;
1085 }
1086 else if ( RTStrNICmp(pszOsAndVersion, RT_STR_TUPLE("Oracle")) == 0
1087 && !RT_C_IS_ALNUM(pszOsAndVersion[6]))
1088 {
1089 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Oracle);
1090 pszOsAndVersion = RTStrStripL(pszOsAndVersion + 6);
1091 }
1092 else if ( RTStrNICmp(pszOsAndVersion, RT_STR_TUPLE("CentOS")) == 0
1093 && !RT_C_IS_ALNUM(pszOsAndVersion[6]))
1094 {
1095 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_RedHat);
1096 pszOsAndVersion = RTStrStripL(pszOsAndVersion + 6);
1097 }
1098 else if ( RTStrNICmp(pszOsAndVersion, RT_STR_TUPLE("Fedora")) == 0
1099 && !RT_C_IS_ALNUM(pszOsAndVersion[6]))
1100 {
1101 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_FedoraCore);
1102 pszOsAndVersion = RTStrStripL(pszOsAndVersion + 6);
1103 }
1104 else if ( RTStrNICmp(pszOsAndVersion, RT_STR_TUPLE("Ubuntu")) == 0
1105 && !RT_C_IS_ALNUM(pszOsAndVersion[6]))
1106 {
1107 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Ubuntu);
1108 pszOsAndVersion = RTStrStripL(pszOsAndVersion + 6);
1109 }
1110 else if ( ( RTStrNICmp(pszOsAndVersion, RT_STR_TUPLE("Xubuntu")) == 0
1111 || RTStrNICmp(pszOsAndVersion, RT_STR_TUPLE("Kubuntu")) == 0)
1112 && !RT_C_IS_ALNUM(pszOsAndVersion[7]))
1113 {
1114 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Ubuntu);
1115 pszOsAndVersion = RTStrStripL(pszOsAndVersion + 7);
1116 }
1117 else if ( RTStrNICmp(pszOsAndVersion, RT_STR_TUPLE("Debian")) == 0
1118 && !RT_C_IS_ALNUM(pszOsAndVersion[6]))
1119 {
1120 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Debian);
1121 pszOsAndVersion = RTStrStripL(pszOsAndVersion + 6);
1122 }
1123 else
1124 fRet = false;
1125
1126 /*
1127 * Skip forward till we get a number.
1128 */
1129 if (ppszNext)
1130 {
1131 *ppszNext = pszOsAndVersion;
1132 char ch;
1133 for (const char *pszVersion = pszOsAndVersion; (ch = *pszVersion) != '\0'; pszVersion++)
1134 if (RT_C_IS_DIGIT(ch))
1135 {
1136 *ppszNext = pszVersion;
1137 break;
1138 }
1139 }
1140 return fRet;
1141}
1142
1143
1144/**
1145 * Detect Linux distro ISOs.
1146 *
1147 * @returns COM status code.
1148 * @retval S_OK if detected
1149 * @retval S_FALSE if not fully detected.
1150 *
1151 * @param hVfsIso The ISO file system.
1152 * @param pBuf Read buffer.
1153 * @param penmOsType Where to return the OS type. This is initialized to
1154 * VBOXOSTYPE_Unknown.
1155 */
1156HRESULT Unattended::i_innerDetectIsoOSLinux(RTVFS hVfsIso, DETECTBUFFER *pBuf, VBOXOSTYPE *penmOsType)
1157{
1158 /*
1159 * Redhat and derivatives may have a .treeinfo (ini-file style) with useful info
1160 * or at least a barebone .discinfo file.
1161 */
1162
1163 /*
1164 * Start with .treeinfo: https://release-engineering.github.io/productmd/treeinfo-1.0.html
1165 */
1166 RTVFSFILE hVfsFile;
1167 int vrc = RTVfsFileOpen(hVfsIso, ".treeinfo", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
1168 if (RT_SUCCESS(vrc))
1169 {
1170 RTINIFILE hIniFile;
1171 vrc = RTIniFileCreateFromVfsFile(&hIniFile, hVfsFile, RTINIFILE_F_READONLY);
1172 RTVfsFileRelease(hVfsFile);
1173 if (RT_SUCCESS(vrc))
1174 {
1175 /* Try figure the architecture first (like with windows). */
1176 vrc = RTIniFileQueryValue(hIniFile, "tree", "arch", pBuf->sz, sizeof(*pBuf), NULL);
1177 if (RT_FAILURE(vrc) || !pBuf->sz[0])
1178 vrc = RTIniFileQueryValue(hIniFile, "general", "arch", pBuf->sz, sizeof(*pBuf), NULL);
1179 if (RT_FAILURE(vrc))
1180 LogRel(("Unattended: .treeinfo: No 'arch' property.\n"));
1181 else
1182 {
1183 LogRelFlow(("Unattended: .treeinfo: arch=%s\n", pBuf->sz));
1184 if (detectLinuxArch(pBuf->sz, penmOsType, VBOXOSTYPE_RedHat))
1185 {
1186 /* Try figure the release name, it doesn't have to be redhat. */
1187 vrc = RTIniFileQueryValue(hIniFile, "release", "name", pBuf->sz, sizeof(*pBuf), NULL);
1188 if (RT_FAILURE(vrc) || !pBuf->sz[0])
1189 vrc = RTIniFileQueryValue(hIniFile, "product", "name", pBuf->sz, sizeof(*pBuf), NULL);
1190 if (RT_FAILURE(vrc) || !pBuf->sz[0])
1191 vrc = RTIniFileQueryValue(hIniFile, "general", "family", pBuf->sz, sizeof(*pBuf), NULL);
1192 if (RT_SUCCESS(vrc))
1193 {
1194 LogRelFlow(("Unattended: .treeinfo: name/family=%s\n", pBuf->sz));
1195 if (!detectLinuxDistroName(pBuf->sz, penmOsType, NULL))
1196 {
1197 LogRel(("Unattended: .treeinfo: Unknown: name/family='%s', assuming Red Hat\n", pBuf->sz));
1198 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_RedHat);
1199 }
1200 }
1201
1202 /* Try figure the version. */
1203 vrc = RTIniFileQueryValue(hIniFile, "release", "version", pBuf->sz, sizeof(*pBuf), NULL);
1204 if (RT_FAILURE(vrc) || !pBuf->sz[0])
1205 vrc = RTIniFileQueryValue(hIniFile, "product", "version", pBuf->sz, sizeof(*pBuf), NULL);
1206 if (RT_FAILURE(vrc) || !pBuf->sz[0])
1207 vrc = RTIniFileQueryValue(hIniFile, "general", "version", pBuf->sz, sizeof(*pBuf), NULL);
1208 if (RT_SUCCESS(vrc))
1209 {
1210 LogRelFlow(("Unattended: .treeinfo: version=%s\n", pBuf->sz));
1211 try { mStrDetectedOSVersion = RTStrStrip(pBuf->sz); }
1212 catch (std::bad_alloc &) { return E_OUTOFMEMORY; }
1213 }
1214 }
1215 else
1216 LogRel(("Unattended: .treeinfo: Unknown: arch='%s'\n", pBuf->sz));
1217 }
1218
1219 RTIniFileRelease(hIniFile);
1220 }
1221
1222 if (*penmOsType != VBOXOSTYPE_Unknown)
1223 return S_FALSE;
1224 }
1225
1226 /*
1227 * Try .discinfo next: https://release-engineering.github.io/productmd/discinfo-1.0.html
1228 * We will probably need additional info here...
1229 */
1230 vrc = RTVfsFileOpen(hVfsIso, ".discinfo", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
1231 if (RT_SUCCESS(vrc))
1232 {
1233 size_t cchIgn;
1234 vrc = RTVfsFileRead(hVfsFile, pBuf->sz, sizeof(*pBuf) - 1, &cchIgn);
1235 pBuf->sz[RT_SUCCESS(vrc) ? cchIgn : 0] = '\0';
1236 RTVfsFileRelease(hVfsFile);
1237
1238 /* Parse and strip the first 5 lines. */
1239 const char *apszLines[5];
1240 char *psz = pBuf->sz;
1241 for (unsigned i = 0; i < RT_ELEMENTS(apszLines); i++)
1242 {
1243 apszLines[i] = psz;
1244 if (*psz)
1245 {
1246 char *pszEol = (char *)strchr(psz, '\n');
1247 if (!pszEol)
1248 psz = strchr(psz, '\0');
1249 else
1250 {
1251 *pszEol = '\0';
1252 apszLines[i] = RTStrStrip(psz);
1253 psz = pszEol + 1;
1254 }
1255 }
1256 }
1257
1258 /* Do we recognize the architecture? */
1259 LogRelFlow(("Unattended: .discinfo: arch=%s\n", apszLines[2]));
1260 if (detectLinuxArch(apszLines[2], penmOsType, VBOXOSTYPE_RedHat))
1261 {
1262 /* Do we recognize the release string? */
1263 LogRelFlow(("Unattended: .discinfo: product+version=%s\n", apszLines[1]));
1264 const char *pszVersion = NULL;
1265 if (!detectLinuxDistroName(apszLines[1], penmOsType, &pszVersion))
1266 LogRel(("Unattended: .discinfo: Unknown: release='%s'\n", apszLines[1]));
1267
1268 if (*pszVersion)
1269 {
1270 LogRelFlow(("Unattended: .discinfo: version=%s\n", pszVersion));
1271 try { mStrDetectedOSVersion = RTStrStripL(pszVersion); }
1272 catch (std::bad_alloc &) { return E_OUTOFMEMORY; }
1273
1274 /* CentOS likes to call their release 'Final' without mentioning the actual version
1275 number (e.g. CentOS-4.7-x86_64-binDVD.iso), so we need to go look elsewhere.
1276 This is only important for centos 4.x and 3.x releases. */
1277 if (RTStrNICmp(pszVersion, RT_STR_TUPLE("Final")) == 0)
1278 {
1279 static const char * const s_apszDirs[] = { "CentOS/RPMS/", "RedHat/RPMS", "Server", "Workstation" };
1280 for (unsigned iDir = 0; iDir < RT_ELEMENTS(s_apszDirs); iDir++)
1281 {
1282 RTVFSDIR hVfsDir;
1283 vrc = RTVfsDirOpen(hVfsIso, s_apszDirs[iDir], 0, &hVfsDir);
1284 if (RT_FAILURE(vrc))
1285 continue;
1286 char szRpmDb[128];
1287 char szReleaseRpm[128];
1288 szRpmDb[0] = '\0';
1289 szReleaseRpm[0] = '\0';
1290 for (;;)
1291 {
1292 RTDIRENTRYEX DirEntry;
1293 size_t cbDirEntry = sizeof(DirEntry);
1294 vrc = RTVfsDirReadEx(hVfsDir, &DirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING);
1295 if (RT_FAILURE(vrc))
1296 break;
1297
1298 /* redhat-release-4WS-2.4.i386.rpm
1299 centos-release-4-7.x86_64.rpm, centos-release-4-4.3.i386.rpm
1300 centos-release-5-3.el5.centos.1.x86_64.rpm */
1301 if ( (psz = strstr(DirEntry.szName, "-release-")) != NULL
1302 || (psz = strstr(DirEntry.szName, "-RELEASE-")) != NULL)
1303 {
1304 psz += 9;
1305 if (RT_C_IS_DIGIT(*psz))
1306 RTStrCopy(szReleaseRpm, sizeof(szReleaseRpm), psz);
1307 }
1308 /* rpmdb-redhat-4WS-2.4.i386.rpm,
1309 rpmdb-CentOS-4.5-0.20070506.i386.rpm,
1310 rpmdb-redhat-3.9-0.20070703.i386.rpm. */
1311 else if ( ( RTStrStartsWith(DirEntry.szName, "rpmdb-")
1312 || RTStrStartsWith(DirEntry.szName, "RPMDB-"))
1313 && RT_C_IS_DIGIT(DirEntry.szName[6]) )
1314 RTStrCopy(szRpmDb, sizeof(szRpmDb), &DirEntry.szName[6]);
1315 }
1316 RTVfsDirRelease(hVfsDir);
1317
1318 /* Did we find anything relvant? */
1319 psz = szRpmDb;
1320 if (!RT_C_IS_DIGIT(*psz))
1321 psz = szReleaseRpm;
1322 if (RT_C_IS_DIGIT(*psz))
1323 {
1324 /* Convert '-' to '.' and strip stuff which doesn't look like a version string. */
1325 char *pszCur = psz + 1;
1326 for (char ch = *pszCur; ch != '\0'; ch = *++pszCur)
1327 if (ch == '-')
1328 *pszCur = '.';
1329 else if (ch != '.' && !RT_C_IS_DIGIT(ch))
1330 {
1331 *pszCur = '\0';
1332 break;
1333 }
1334 while (&pszCur[-1] != psz && pszCur[-1] == '.')
1335 *--pszCur = '\0';
1336
1337 /* Set it and stop looking. */
1338 try { mStrDetectedOSVersion = psz; }
1339 catch (std::bad_alloc &) { return E_OUTOFMEMORY; }
1340 break;
1341 }
1342 }
1343 }
1344 }
1345 }
1346 else
1347 LogRel(("Unattended: .discinfo: Unknown: arch='%s'\n", apszLines[2]));
1348
1349 if (*penmOsType != VBOXOSTYPE_Unknown)
1350 return S_FALSE;
1351 }
1352
1353 /*
1354 * Ubuntu has a README.diskdefins file on their ISO (already on 4.10 / warty warthog).
1355 * Example content:
1356 * #define DISKNAME Ubuntu 4.10 "Warty Warthog" - Preview amd64 Binary-1
1357 * #define TYPE binary
1358 * #define TYPEbinary 1
1359 * #define ARCH amd64
1360 * #define ARCHamd64 1
1361 * #define DISKNUM 1
1362 * #define DISKNUM1 1
1363 * #define TOTALNUM 1
1364 * #define TOTALNUM1 1
1365 */
1366 vrc = RTVfsFileOpen(hVfsIso, "README.diskdefines", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
1367 if (RT_SUCCESS(vrc))
1368 {
1369 size_t cchIgn;
1370 vrc = RTVfsFileRead(hVfsFile, pBuf->sz, sizeof(*pBuf) - 1, &cchIgn);
1371 pBuf->sz[RT_SUCCESS(vrc) ? cchIgn : 0] = '\0';
1372 RTVfsFileRelease(hVfsFile);
1373
1374 /* Find the DISKNAME and ARCH defines. */
1375 const char *pszDiskName = NULL;
1376 const char *pszArch = NULL;
1377 char *psz = pBuf->sz;
1378 for (unsigned i = 0; *psz != '\0'; i++)
1379 {
1380 while (RT_C_IS_BLANK(*psz))
1381 psz++;
1382
1383 /* Match #define: */
1384 static const char s_szDefine[] = "#define";
1385 if ( strncmp(psz, s_szDefine, sizeof(s_szDefine) - 1) == 0
1386 && RT_C_IS_BLANK(psz[sizeof(s_szDefine) - 1]))
1387 {
1388 psz = &psz[sizeof(s_szDefine) - 1];
1389 while (RT_C_IS_BLANK(*psz))
1390 psz++;
1391
1392 /* Match the identifier: */
1393 char *pszIdentifier = psz;
1394 if (RT_C_IS_ALPHA(*psz) || *psz == '_')
1395 {
1396 do
1397 psz++;
1398 while (RT_C_IS_ALNUM(*psz) || *psz == '_');
1399 size_t cchIdentifier = (size_t)(psz - pszIdentifier);
1400
1401 /* Skip to the value. */
1402 while (RT_C_IS_BLANK(*psz))
1403 psz++;
1404 char *pszValue = psz;
1405
1406 /* Skip to EOL and strip the value. */
1407 char *pszEol = psz = strchr(psz, '\n');
1408 if (psz)
1409 *psz++ = '\0';
1410 else
1411 pszEol = strchr(pszValue, '\0');
1412 while (pszEol > pszValue && RT_C_IS_SPACE(pszEol[-1]))
1413 *--pszEol = '\0';
1414
1415 LogRelFlow(("Unattended: README.diskdefines: %.*s=%s\n", cchIdentifier, pszIdentifier, pszValue));
1416
1417 /* Do identifier matching: */
1418 if (cchIdentifier == sizeof("DISKNAME") - 1 && strncmp(pszIdentifier, RT_STR_TUPLE("DISKNAME")) == 0)
1419 pszDiskName = pszValue;
1420 else if (cchIdentifier == sizeof("ARCH") - 1 && strncmp(pszIdentifier, RT_STR_TUPLE("ARCH")) == 0)
1421 pszArch = pszValue;
1422 else
1423 continue;
1424 if (pszDiskName == NULL || pszArch == NULL)
1425 continue;
1426 break;
1427 }
1428 }
1429
1430 /* Next line: */
1431 psz = strchr(psz, '\n');
1432 if (!psz)
1433 break;
1434 psz++;
1435 }
1436
1437 /* Did we find both of them? */
1438 if (pszDiskName && pszArch)
1439 {
1440 if (detectLinuxArch(pszArch, penmOsType, VBOXOSTYPE_Ubuntu))
1441 {
1442 const char *pszVersion = NULL;
1443 if (detectLinuxDistroName(pszDiskName, penmOsType, &pszVersion))
1444 {
1445 LogRelFlow(("Unattended: README.diskdefines: version=%s\n", pszVersion));
1446 try { mStrDetectedOSVersion = RTStrStripL(pszVersion); }
1447 catch (std::bad_alloc &) { return E_OUTOFMEMORY; }
1448 }
1449 else
1450 LogRel(("Unattended: README.diskdefines: Unknown: diskname='%s'\n", pszDiskName));
1451 }
1452 else
1453 LogRel(("Unattended: README.diskdefines: Unknown: arch='%s'\n", pszArch));
1454 }
1455 else
1456 LogRel(("Unattended: README.diskdefines: Did not find both DISKNAME and ARCH. :-/\n"));
1457
1458 if (*penmOsType != VBOXOSTYPE_Unknown)
1459 return S_FALSE;
1460 }
1461
1462 /*
1463 * All of the debian based distro versions I checked have a single line ./disk/info file.
1464 * Only info I could find related to .disk folder is: https://lists.debian.org/debian-cd/2004/01/msg00069.html
1465 * Some example content from several install ISOs is as follows:
1466 * Ubuntu 4.10 "Warty Warthog" - Preview amd64 Binary-1 (20041020)
1467 * Linux Mint 20.3 "Una" - Release amd64 20220104
1468 * Debian GNU/Linux 11.2.0 "Bullseye" - Official amd64 NETINST 20211218-11:12
1469 * Debian GNU/Linux 9.13.0 "Stretch" - Official amd64 DVD Binary-1 20200718-11:07
1470 * Xubuntu 20.04.2.0 LTS "Focal Fossa" - Release amd64 (20210209.1)
1471 * Ubuntu 17.10 "Artful Aardvark" - Release amd64 (20180105.1)
1472 * Ubuntu 16.04.6 LTS "Xenial Xerus" - Release i386 (20190227.1)
1473 * Debian GNU/Linux 8.11.1 "Jessie" - Official amd64 CD Binary-1 20190211-02:10
1474 * Kali GNU/Linux 2021.3a "Kali-last-snapshot" - Official amd64 BD Binary-1 with firmware 20211015-16:55
1475 */
1476 vrc = RTVfsFileOpen(hVfsIso, ".disk/info", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
1477 if (RT_SUCCESS(vrc))
1478 {
1479 size_t cchIgn;
1480 vrc = RTVfsFileRead(hVfsFile, pBuf->sz, sizeof(*pBuf) - 1, &cchIgn);
1481 pBuf->sz[RT_SUCCESS(vrc) ? cchIgn : 0] = '\0';
1482
1483 pBuf->sz[sizeof(*pBuf) - 1] = '\0';
1484 RTVfsFileRelease(hVfsFile);
1485
1486 char *psz = pBuf->sz;
1487 char *pszDiskName = psz;
1488 char *pszArch = NULL;
1489
1490 /* Only care about the first line of the file even if it is multi line and assume disk name ended with ' - '.*/
1491 psz = RTStrStr(pBuf->sz, " - ");
1492 if (psz && memchr(pBuf->sz, '\n', (size_t)(psz - pBuf->sz)) == NULL)
1493 {
1494 *psz = '\0';
1495 psz += 3;
1496 if (*psz)
1497 pszArch = psz;
1498 }
1499
1500 if (pszDiskName && pszArch)
1501 {
1502 if (!detectLinuxArchII(pszArch, penmOsType, VBOXOSTYPE_Ubuntu))
1503 LogRel(("Unattended: README.diskdefines: Unknown: arch='%s'\n", pszArch));
1504
1505 const char *pszVersion = NULL;
1506 if (detectLinuxDistroName(pszDiskName, penmOsType, &pszVersion))
1507 {
1508 LogRelFlow(("Unattended: README.diskdefines: version=%s\n", pszVersion));
1509 try { mStrDetectedOSVersion = RTStrStripL(pszVersion); }
1510 catch (std::bad_alloc &) { return E_OUTOFMEMORY; }
1511 }
1512 else
1513 LogRel(("Unattended: README.diskdefines: Unknown: diskname='%s'\n", pszDiskName));
1514 }
1515 else
1516 LogRel(("Unattended: README.diskdefines: Did not find both DISKNAME and ARCH. :-/\n"));
1517
1518 if (*penmOsType != VBOXOSTYPE_Unknown)
1519 return S_FALSE;
1520 }
1521
1522 return S_FALSE;
1523}
1524
1525
1526/**
1527 * Detect OS/2 installation ISOs.
1528 *
1529 * Mainly aiming at ACP2/MCP2 as that's what we currently use in our testing.
1530 *
1531 * @returns COM status code.
1532 * @retval S_OK if detected
1533 * @retval S_FALSE if not fully detected.
1534 *
1535 * @param hVfsIso The ISO file system.
1536 * @param pBuf Read buffer.
1537 * @param penmOsType Where to return the OS type. This is initialized to
1538 * VBOXOSTYPE_Unknown.
1539 */
1540HRESULT Unattended::i_innerDetectIsoOSOs2(RTVFS hVfsIso, DETECTBUFFER *pBuf, VBOXOSTYPE *penmOsType)
1541{
1542 /*
1543 * The OS2SE20.SRC contains the location of the tree with the diskette
1544 * images, typically "\OS2IMAGE".
1545 */
1546 RTVFSFILE hVfsFile;
1547 int vrc = RTVfsFileOpen(hVfsIso, "OS2SE20.SRC", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
1548 if (RT_SUCCESS(vrc))
1549 {
1550 size_t cbRead = 0;
1551 vrc = RTVfsFileRead(hVfsFile, pBuf->sz, sizeof(pBuf->sz) - 1, &cbRead);
1552 RTVfsFileRelease(hVfsFile);
1553 if (RT_SUCCESS(vrc))
1554 {
1555 pBuf->sz[cbRead] = '\0';
1556 RTStrStrip(pBuf->sz);
1557 vrc = RTStrValidateEncoding(pBuf->sz);
1558 if (RT_SUCCESS(vrc))
1559 LogRelFlow(("Unattended: OS2SE20.SRC=%s\n", pBuf->sz));
1560 else
1561 LogRel(("Unattended: OS2SE20.SRC invalid encoding: %Rrc, %.*Rhxs\n", vrc, cbRead, pBuf->sz));
1562 }
1563 else
1564 LogRel(("Unattended: Error reading OS2SE20.SRC: %\n", vrc));
1565 }
1566 /*
1567 * ArcaOS has dropped the file, assume it's \OS2IMAGE and see if it's there.
1568 */
1569 else if (vrc == VERR_FILE_NOT_FOUND)
1570 RTStrCopy(pBuf->sz, sizeof(pBuf->sz), "\\OS2IMAGE");
1571 else
1572 return S_FALSE;
1573
1574 /*
1575 * Check that the directory directory exists and has a DISK_0 under it
1576 * with an OS2LDR on it.
1577 */
1578 size_t const cchOs2Image = strlen(pBuf->sz);
1579 vrc = RTPathAppend(pBuf->sz, sizeof(pBuf->sz), "DISK_0/OS2LDR");
1580 RTFSOBJINFO ObjInfo = {0};
1581 vrc = RTVfsQueryPathInfo(hVfsIso, pBuf->sz, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1582 if (vrc == VERR_FILE_NOT_FOUND)
1583 {
1584 RTStrCat(pBuf->sz, sizeof(pBuf->sz), "."); /* eCS 2.0 image includes the dot from the 8.3 name. */
1585 vrc = RTVfsQueryPathInfo(hVfsIso, pBuf->sz, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1586 }
1587 if ( RT_FAILURE(vrc)
1588 || !RTFS_IS_FILE(ObjInfo.Attr.fMode))
1589 {
1590 LogRel(("Unattended: RTVfsQueryPathInfo(, '%s' (from OS2SE20.SRC),) -> %Rrc, fMode=%#x\n",
1591 pBuf->sz, vrc, ObjInfo.Attr.fMode));
1592 return S_FALSE;
1593 }
1594
1595 /*
1596 * So, it's some kind of OS/2 2.x or later ISO alright.
1597 */
1598 *penmOsType = VBOXOSTYPE_OS2;
1599 mStrDetectedOSHints.printf("OS2SE20.SRC=%.*s", cchOs2Image, pBuf->sz);
1600
1601 /*
1602 * ArcaOS ISOs seems to have a AOSBOOT dir on them.
1603 * This contains a ARCANOAE.FLG file with content we can use for the version:
1604 * ArcaOS 5.0.7 EN
1605 * Built 2021-12-07 18:34:34
1606 * We drop the "ArcaOS" bit, as it's covered by penmOsType. Then we pull up
1607 * the second line.
1608 *
1609 * Note! Yet to find a way to do unattended install of ArcaOS, as it comes
1610 * with no CD-boot floppy images, only simple .PF archive files for
1611 * unpacking onto the ram disk or whatever. Modifying these is
1612 * possible (ibsen's aPLib v0.36 compression with some simple custom
1613 * headers), but it would probably be a royal pain. Could perhaps
1614 * cook something from OS2IMAGE\DISK_0 thru 3...
1615 */
1616 vrc = RTVfsQueryPathInfo(hVfsIso, "AOSBOOT", &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1617 if ( RT_SUCCESS(vrc)
1618 && RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
1619 {
1620 *penmOsType = VBOXOSTYPE_ArcaOS;
1621
1622 /* Read the version file: */
1623 vrc = RTVfsFileOpen(hVfsIso, "SYS/ARCANOAE.FLG", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
1624 if (RT_SUCCESS(vrc))
1625 {
1626 size_t cbRead = 0;
1627 vrc = RTVfsFileRead(hVfsFile, pBuf->sz, sizeof(pBuf->sz) - 1, &cbRead);
1628 RTVfsFileRelease(hVfsFile);
1629 pBuf->sz[cbRead] = '\0';
1630 if (RT_SUCCESS(vrc))
1631 {
1632 /* Strip the OS name: */
1633 char *pszVersion = RTStrStrip(pBuf->sz);
1634 static char s_szArcaOS[] = "ArcaOS";
1635 if (RTStrStartsWith(pszVersion, s_szArcaOS))
1636 pszVersion = RTStrStripL(pszVersion + sizeof(s_szArcaOS) - 1);
1637
1638 /* Pull up the 2nd line if it, condensing the \r\n into a single space. */
1639 char *pszNewLine = strchr(pszVersion, '\n');
1640 if (pszNewLine && RTStrStartsWith(pszNewLine + 1, "Built 20"))
1641 {
1642 size_t offRemove = 0;
1643 while (RT_C_IS_SPACE(pszNewLine[-1 - (ssize_t)offRemove]))
1644 offRemove++;
1645 if (offRemove > 0)
1646 {
1647 pszNewLine -= offRemove;
1648 memmove(pszNewLine, pszNewLine + offRemove, strlen(pszNewLine + offRemove) - 1);
1649 }
1650 *pszNewLine = ' ';
1651 }
1652
1653 /* Drop any additional lines: */
1654 pszNewLine = strchr(pszVersion, '\n');
1655 if (pszNewLine)
1656 *pszNewLine = '\0';
1657 RTStrStripR(pszVersion);
1658
1659 /* Done (hope it makes some sense). */
1660 mStrDetectedOSVersion = pszVersion;
1661 }
1662 else
1663 LogRel(("Unattended: failed to read AOSBOOT/ARCANOAE.FLG: %Rrc\n", vrc));
1664 }
1665 else
1666 LogRel(("Unattended: failed to open AOSBOOT/ARCANOAE.FLG for reading: %Rrc\n", vrc));
1667 }
1668 /*
1669 * Similarly, eCS has an ECS directory and it typically contains a
1670 * ECS_INST.FLG file with the version info. Content differs a little:
1671 * eComStation 2.0 EN_US Thu May 13 10:27:54 pm 2010
1672 * Built on ECS60441318
1673 * Here we drop the "eComStation" bit and leave the 2nd line as it.
1674 *
1675 * Note! At least 2.0 has a DISKIMGS folder with what looks like boot
1676 * disks, so we could probably get something going here without
1677 * needing to write an OS2 boot sector...
1678 */
1679 else
1680 {
1681 vrc = RTVfsQueryPathInfo(hVfsIso, "ECS", &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1682 if ( RT_SUCCESS(vrc)
1683 && RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
1684 {
1685 *penmOsType = VBOXOSTYPE_ECS;
1686
1687 /* Read the version file: */
1688 vrc = RTVfsFileOpen(hVfsIso, "ECS/ECS_INST.FLG", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
1689 if (RT_SUCCESS(vrc))
1690 {
1691 size_t cbRead = 0;
1692 vrc = RTVfsFileRead(hVfsFile, pBuf->sz, sizeof(pBuf->sz) - 1, &cbRead);
1693 RTVfsFileRelease(hVfsFile);
1694 pBuf->sz[cbRead] = '\0';
1695 if (RT_SUCCESS(vrc))
1696 {
1697 /* Strip the OS name: */
1698 char *pszVersion = RTStrStrip(pBuf->sz);
1699 static char s_szECS[] = "eComStation";
1700 if (RTStrStartsWith(pszVersion, s_szECS))
1701 pszVersion = RTStrStripL(pszVersion + sizeof(s_szECS) - 1);
1702
1703 /* Drop any additional lines: */
1704 char *pszNewLine = strchr(pszVersion, '\n');
1705 if (pszNewLine)
1706 *pszNewLine = '\0';
1707 RTStrStripR(pszVersion);
1708
1709 /* Done (hope it makes some sense). */
1710 mStrDetectedOSVersion = pszVersion;
1711 }
1712 else
1713 LogRel(("Unattended: failed to read ECS/ECS_INST.FLG: %Rrc\n", vrc));
1714 }
1715 else
1716 LogRel(("Unattended: failed to open ECS/ECS_INST.FLG for reading: %Rrc\n", vrc));
1717 }
1718 else
1719 {
1720 /*
1721 * Official IBM OS/2 builds doesn't have any .FLG file on them,
1722 * so need to pry the information out in some other way. Best way
1723 * is to read the SYSLEVEL.OS2 file, which is typically on disk #2,
1724 * though on earlier versions (warp3) it was disk #1.
1725 */
1726 vrc = RTPathJoin(pBuf->sz, sizeof(pBuf->sz), strchr(mStrDetectedOSHints.c_str(), '=') + 1,
1727 "/DISK_2/SYSLEVEL.OS2");
1728 if (RT_SUCCESS(vrc))
1729 {
1730 vrc = RTVfsFileOpen(hVfsIso, pBuf->sz, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
1731 if (vrc == VERR_FILE_NOT_FOUND)
1732 {
1733 RTPathJoin(pBuf->sz, sizeof(pBuf->sz), strchr(mStrDetectedOSHints.c_str(), '=') + 1, "/DISK_1/SYSLEVEL.OS2");
1734 vrc = RTVfsFileOpen(hVfsIso, pBuf->sz, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
1735 }
1736 if (RT_SUCCESS(vrc))
1737 {
1738 RT_ZERO(pBuf->ab);
1739 size_t cbRead = 0;
1740 vrc = RTVfsFileRead(hVfsFile, pBuf->ab, sizeof(pBuf->ab), &cbRead);
1741 RTVfsFileRelease(hVfsFile);
1742 if (RT_SUCCESS(vrc))
1743 {
1744 /* Check the header. */
1745 OS2SYSLEVELHDR const *pHdr = (OS2SYSLEVELHDR const *)&pBuf->ab[0];
1746 if ( pHdr->uMinusOne == UINT16_MAX
1747 && pHdr->uSyslevelFileVer == 1
1748 && memcmp(pHdr->achSignature, RT_STR_TUPLE("SYSLEVEL")) == 0
1749 && pHdr->offTable < cbRead
1750 && pHdr->offTable + sizeof(OS2SYSLEVELENTRY) <= cbRead)
1751 {
1752 OS2SYSLEVELENTRY *pEntry = (OS2SYSLEVELENTRY *)&pBuf->ab[pHdr->offTable];
1753 if ( RT_SUCCESS(RTStrValidateEncodingEx(pEntry->szName, sizeof(pEntry->szName),
1754 RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED))
1755 && RT_SUCCESS(RTStrValidateEncodingEx(pEntry->achCsdLevel, sizeof(pEntry->achCsdLevel), 0))
1756 && pEntry->bVersion != 0
1757 && ((pEntry->bVersion >> 4) & 0xf) < 10
1758 && (pEntry->bVersion & 0xf) < 10
1759 && pEntry->bModify < 10
1760 && pEntry->bRefresh < 10)
1761 {
1762 /* Flavor: */
1763 char *pszName = RTStrStrip(pEntry->szName);
1764 if (pszName)
1765 mStrDetectedOSFlavor = pszName;
1766
1767 /* Version: */
1768 if (pEntry->bRefresh != 0)
1769 mStrDetectedOSVersion.printf("%d.%d%d.%d", pEntry->bVersion >> 4, pEntry->bVersion & 0xf,
1770 pEntry->bModify, pEntry->bRefresh);
1771 else
1772 mStrDetectedOSVersion.printf("%d.%d%d", pEntry->bVersion >> 4, pEntry->bVersion & 0xf,
1773 pEntry->bModify);
1774 pEntry->achCsdLevel[sizeof(pEntry->achCsdLevel) - 1] = '\0';
1775 char *pszCsd = RTStrStrip(pEntry->achCsdLevel);
1776 if (*pszCsd != '\0')
1777 {
1778 mStrDetectedOSVersion.append(' ');
1779 mStrDetectedOSVersion.append(pszCsd);
1780 }
1781 if (RTStrVersionCompare(mStrDetectedOSVersion.c_str(), "4.50") >= 0)
1782 *penmOsType = VBOXOSTYPE_OS2Warp45;
1783 else if (RTStrVersionCompare(mStrDetectedOSVersion.c_str(), "4.00") >= 0)
1784 *penmOsType = VBOXOSTYPE_OS2Warp4;
1785 else if (RTStrVersionCompare(mStrDetectedOSVersion.c_str(), "3.00") >= 0)
1786 *penmOsType = VBOXOSTYPE_OS2Warp3;
1787 }
1788 else
1789 LogRel(("Unattended: bogus SYSLEVEL.OS2 file entry: %.128Rhxd\n", pEntry));
1790 }
1791 else
1792 LogRel(("Unattended: bogus SYSLEVEL.OS2 file header: uMinusOne=%#x uSyslevelFileVer=%#x achSignature=%.8Rhxs offTable=%#x vs cbRead=%#zx\n",
1793 pHdr->uMinusOne, pHdr->uSyslevelFileVer, pHdr->achSignature, pHdr->offTable, cbRead));
1794 }
1795 else
1796 LogRel(("Unattended: failed to read SYSLEVEL.OS2: %Rrc\n", vrc));
1797 }
1798 else
1799 LogRel(("Unattended: failed to open '%s' for reading: %Rrc\n", pBuf->sz, vrc));
1800 }
1801 }
1802 }
1803
1804 /** @todo language detection? */
1805
1806 /*
1807 * Only tested ACP2, so only return S_OK for it.
1808 */
1809 if ( *penmOsType == VBOXOSTYPE_OS2Warp45
1810 && RTStrVersionCompare(mStrDetectedOSVersion.c_str(), "4.52") >= 0
1811 && mStrDetectedOSFlavor.contains("Server", RTCString::CaseInsensitive))
1812 return S_OK;
1813
1814 return S_FALSE;
1815}
1816
1817
1818HRESULT Unattended::prepare()
1819{
1820 LogFlow(("Unattended::prepare: enter\n"));
1821
1822 /*
1823 * Must have a machine.
1824 */
1825 ComPtr<Machine> ptrMachine;
1826 Guid MachineUuid;
1827 {
1828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1829 ptrMachine = mMachine;
1830 if (ptrMachine.isNull())
1831 return setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("No machine associated with this IUnatteded instance"));
1832 MachineUuid = mMachineUuid;
1833 }
1834
1835 /*
1836 * Before we write lock ourselves, we must get stuff from Machine and
1837 * VirtualBox because their locks have higher priorities than ours.
1838 */
1839 Utf8Str strGuestOsTypeId;
1840 Utf8Str strMachineName;
1841 Utf8Str strDefaultAuxBasePath;
1842 HRESULT hrc;
1843 try
1844 {
1845 Bstr bstrTmp;
1846 hrc = ptrMachine->COMGETTER(OSTypeId)(bstrTmp.asOutParam());
1847 if (SUCCEEDED(hrc))
1848 {
1849 strGuestOsTypeId = bstrTmp;
1850 hrc = ptrMachine->COMGETTER(Name)(bstrTmp.asOutParam());
1851 if (SUCCEEDED(hrc))
1852 strMachineName = bstrTmp;
1853 }
1854 int vrc = ptrMachine->i_calculateFullPath(Utf8StrFmt("Unattended-%RTuuid-", MachineUuid.raw()), strDefaultAuxBasePath);
1855 if (RT_FAILURE(vrc))
1856 return setErrorBoth(E_FAIL, vrc);
1857 }
1858 catch (std::bad_alloc &)
1859 {
1860 return E_OUTOFMEMORY;
1861 }
1862 bool const fIs64Bit = i_isGuestOSArchX64(strGuestOsTypeId);
1863
1864 BOOL fRtcUseUtc = FALSE;
1865 hrc = ptrMachine->COMGETTER(RTCUseUTC)(&fRtcUseUtc);
1866 if (FAILED(hrc))
1867 return hrc;
1868
1869 FirmwareType_T enmFirmware = FirmwareType_BIOS;
1870 hrc = ptrMachine->COMGETTER(FirmwareType)(&enmFirmware);
1871 if (FAILED(hrc))
1872 return hrc;
1873
1874 /*
1875 * Write lock this object and set attributes we got from IMachine.
1876 */
1877 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1878
1879 mStrGuestOsTypeId = strGuestOsTypeId;
1880 mfGuestOs64Bit = fIs64Bit;
1881 mfRtcUseUtc = RT_BOOL(fRtcUseUtc);
1882 menmFirmwareType = enmFirmware;
1883
1884 /*
1885 * Do some state checks.
1886 */
1887 if (mpInstaller != NULL)
1888 return setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("The prepare method has been called (must call done to restart)"));
1889 if ((Machine *)ptrMachine != (Machine *)mMachine)
1890 return setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("The 'machine' while we were using it - please don't do that"));
1891
1892 /*
1893 * Check if the specified ISOs and files exist.
1894 */
1895 if (!RTFileExists(mStrIsoPath.c_str()))
1896 return setErrorBoth(E_FAIL, VERR_FILE_NOT_FOUND, tr("Could not locate the installation ISO file '%s'"),
1897 mStrIsoPath.c_str());
1898 if (mfInstallGuestAdditions && !RTFileExists(mStrAdditionsIsoPath.c_str()))
1899 return setErrorBoth(E_FAIL, VERR_FILE_NOT_FOUND, tr("Could not locate the Guest Additions ISO file '%s'"),
1900 mStrAdditionsIsoPath.c_str());
1901 if (mfInstallTestExecService && !RTFileExists(mStrValidationKitIsoPath.c_str()))
1902 return setErrorBoth(E_FAIL, VERR_FILE_NOT_FOUND, tr("Could not locate the validation kit ISO file '%s'"),
1903 mStrValidationKitIsoPath.c_str());
1904 if (mStrScriptTemplatePath.isNotEmpty() && !RTFileExists(mStrScriptTemplatePath.c_str()))
1905 return setErrorBoth(E_FAIL, VERR_FILE_NOT_FOUND, tr("Could not locate unattended installation script template '%s'"),
1906 mStrScriptTemplatePath.c_str());
1907
1908 /*
1909 * Do media detection if it haven't been done yet.
1910 */
1911 if (!mfDoneDetectIsoOS)
1912 {
1913 hrc = detectIsoOS();
1914 if (FAILED(hrc) && hrc != E_NOTIMPL)
1915 return hrc;
1916 }
1917
1918 /*
1919 * We can now check midxImage against mDetectedImages, since the latter is
1920 * populated during the detectIsoOS call. We ignore midxImage if no images
1921 * were detected, assuming that it's not relevant or used for different purposes.
1922 */
1923 if (mDetectedImages.size() > 0)
1924 {
1925 bool fImageFound = false;
1926 for (size_t i = 0; i < mDetectedImages.size(); ++i)
1927 if (midxImage == mDetectedImages[i].mImageIndex)
1928 {
1929 fImageFound = true;
1930 /** @todo Replace / amend the detected version? */
1931 break;
1932 }
1933 if (!fImageFound)
1934 return setErrorBoth(E_FAIL, VERR_NOT_FOUND, tr("imageIndex value %u not found in detectedImageIndices"), midxImage);
1935 }
1936
1937 /*
1938 * Get the ISO's detect guest OS type info and make it's a known one (just
1939 * in case the above step doesn't work right).
1940 */
1941 uint32_t const idxIsoOSType = Global::getOSTypeIndexFromId(mStrDetectedOSTypeId.c_str());
1942 VBOXOSTYPE const enmIsoOSType = idxIsoOSType < Global::cOSTypes ? Global::sOSTypes[idxIsoOSType].osType : VBOXOSTYPE_Unknown;
1943 if ((enmIsoOSType & VBOXOSTYPE_OsTypeMask) == VBOXOSTYPE_Unknown)
1944 return setError(E_FAIL, tr("The supplied ISO file does not contain an OS currently supported for unattended installation"));
1945
1946 /*
1947 * Get the VM's configured guest OS type info.
1948 */
1949 uint32_t const idxMachineOSType = Global::getOSTypeIndexFromId(mStrGuestOsTypeId.c_str());
1950 VBOXOSTYPE const enmMachineOSType = idxMachineOSType < Global::cOSTypes
1951 ? Global::sOSTypes[idxMachineOSType].osType : VBOXOSTYPE_Unknown;
1952
1953 /*
1954 * Check that the detected guest OS type for the ISO is compatible with
1955 * that of the VM, boardly speaking.
1956 */
1957 if (idxMachineOSType != idxIsoOSType)
1958 {
1959 /* Check that the architecture is compatible: */
1960 if ( (enmIsoOSType & VBOXOSTYPE_ArchitectureMask) != (enmMachineOSType & VBOXOSTYPE_ArchitectureMask)
1961 && ( (enmIsoOSType & VBOXOSTYPE_ArchitectureMask) != VBOXOSTYPE_x86
1962 || (enmMachineOSType & VBOXOSTYPE_ArchitectureMask) != VBOXOSTYPE_x64))
1963 return setError(E_FAIL, tr("The supplied ISO file is incompatible with the guest OS type of the VM: CPU architecture mismatch"));
1964
1965 /** @todo check BIOS/EFI requirement */
1966 }
1967
1968 /*
1969 * Do some default property stuff and check other properties.
1970 */
1971 try
1972 {
1973 char szTmp[128];
1974
1975 if (mStrLocale.isEmpty())
1976 {
1977 int vrc = RTLocaleQueryNormalizedBaseLocaleName(szTmp, sizeof(szTmp));
1978 if ( RT_SUCCESS(vrc)
1979 && RTLOCALE_IS_LANGUAGE2_UNDERSCORE_COUNTRY2(szTmp))
1980 mStrLocale.assign(szTmp, 5);
1981 else
1982 mStrLocale = "en_US";
1983 Assert(RTLOCALE_IS_LANGUAGE2_UNDERSCORE_COUNTRY2(mStrLocale));
1984 }
1985
1986 if (mStrLanguage.isEmpty())
1987 {
1988 if (mDetectedOSLanguages.size() > 0)
1989 mStrLanguage = mDetectedOSLanguages[0];
1990 else
1991 mStrLanguage.assign(mStrLocale).findReplace('_', '-');
1992 }
1993
1994 if (mStrCountry.isEmpty())
1995 {
1996 int vrc = RTLocaleQueryUserCountryCode(szTmp);
1997 if (RT_SUCCESS(vrc))
1998 mStrCountry = szTmp;
1999 else if ( mStrLocale.isNotEmpty()
2000 && RTLOCALE_IS_LANGUAGE2_UNDERSCORE_COUNTRY2(mStrLocale))
2001 mStrCountry.assign(mStrLocale, 3, 2);
2002 else
2003 mStrCountry = "US";
2004 }
2005
2006 if (mStrTimeZone.isEmpty())
2007 {
2008 int vrc = RTTimeZoneGetCurrent(szTmp, sizeof(szTmp));
2009 if ( RT_SUCCESS(vrc)
2010 && strcmp(szTmp, "localtime") != 0 /* Typcial solaris TZ that isn't very helpful. */)
2011 mStrTimeZone = szTmp;
2012 else
2013 mStrTimeZone = "Etc/UTC";
2014 Assert(mStrTimeZone.isNotEmpty());
2015 }
2016 mpTimeZoneInfo = RTTimeZoneGetInfoByUnixName(mStrTimeZone.c_str());
2017 if (!mpTimeZoneInfo)
2018 mpTimeZoneInfo = RTTimeZoneGetInfoByWindowsName(mStrTimeZone.c_str());
2019 Assert(mpTimeZoneInfo || mStrTimeZone != "Etc/UTC");
2020 if (!mpTimeZoneInfo)
2021 LogRel(("Unattended::prepare: warning: Unknown time zone '%s'\n", mStrTimeZone.c_str()));
2022
2023 if (mStrHostname.isEmpty())
2024 {
2025 /* Mangle the VM name into a valid hostname. */
2026 for (size_t i = 0; i < strMachineName.length(); i++)
2027 {
2028 char ch = strMachineName[i];
2029 if ( (unsigned)ch < 127
2030 && RT_C_IS_ALNUM(ch))
2031 mStrHostname.append(ch);
2032 else if (mStrHostname.isNotEmpty() && RT_C_IS_PUNCT(ch) && !mStrHostname.endsWith("-"))
2033 mStrHostname.append('-');
2034 }
2035 if (mStrHostname.length() == 0)
2036 mStrHostname.printf("%RTuuid-vm", MachineUuid.raw());
2037 else if (mStrHostname.length() < 3)
2038 mStrHostname.append("-vm");
2039 mStrHostname.append(".myguest.virtualbox.org");
2040 }
2041
2042 if (mStrAuxiliaryBasePath.isEmpty())
2043 {
2044 mStrAuxiliaryBasePath = strDefaultAuxBasePath;
2045 mfIsDefaultAuxiliaryBasePath = true;
2046 }
2047 }
2048 catch (std::bad_alloc &)
2049 {
2050 return E_OUTOFMEMORY;
2051 }
2052
2053 /*
2054 * Instatiate the guest installer matching the ISO.
2055 */
2056 mpInstaller = UnattendedInstaller::createInstance(enmIsoOSType, mStrDetectedOSTypeId, mStrDetectedOSVersion,
2057 mStrDetectedOSFlavor, mStrDetectedOSHints, this);
2058 if (mpInstaller != NULL)
2059 {
2060 hrc = mpInstaller->initInstaller();
2061 if (SUCCEEDED(hrc))
2062 {
2063 /*
2064 * Do the script preps (just reads them).
2065 */
2066 hrc = mpInstaller->prepareUnattendedScripts();
2067 if (SUCCEEDED(hrc))
2068 {
2069 LogFlow(("Unattended::prepare: returns S_OK\n"));
2070 return S_OK;
2071 }
2072 }
2073
2074 /* Destroy the installer instance. */
2075 delete mpInstaller;
2076 mpInstaller = NULL;
2077 }
2078 else
2079 hrc = setErrorBoth(E_FAIL, VERR_NOT_FOUND,
2080 tr("Unattended installation is not supported for guest type '%s'"), mStrGuestOsTypeId.c_str());
2081 LogRelFlow(("Unattended::prepare: failed with %Rhrc\n", hrc));
2082 return hrc;
2083}
2084
2085HRESULT Unattended::constructMedia()
2086{
2087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2088
2089 LogFlow(("===========================================================\n"));
2090 LogFlow(("Call Unattended::constructMedia()\n"));
2091
2092 if (mpInstaller == NULL)
2093 return setErrorBoth(E_FAIL, VERR_WRONG_ORDER, "prepare() not yet called");
2094
2095 return mpInstaller->prepareMedia();
2096}
2097
2098HRESULT Unattended::reconfigureVM()
2099{
2100 LogFlow(("===========================================================\n"));
2101 LogFlow(("Call Unattended::reconfigureVM()\n"));
2102
2103 /*
2104 * Interrogate VirtualBox/IGuestOSType before we lock stuff and create ordering issues.
2105 */
2106 StorageBus_T enmRecommendedStorageBus = StorageBus_IDE;
2107 {
2108 Bstr bstrGuestOsTypeId;
2109 Bstr bstrDetectedOSTypeId;
2110 {
2111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2112 if (mpInstaller == NULL)
2113 return setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("prepare() not yet called"));
2114 bstrGuestOsTypeId = mStrGuestOsTypeId;
2115 bstrDetectedOSTypeId = mStrDetectedOSTypeId;
2116 }
2117 ComPtr<IGuestOSType> ptrGuestOSType;
2118 HRESULT hrc = mParent->GetGuestOSType(bstrGuestOsTypeId.raw(), ptrGuestOSType.asOutParam());
2119 if (SUCCEEDED(hrc))
2120 {
2121 if (!ptrGuestOSType.isNull())
2122 hrc = ptrGuestOSType->COMGETTER(RecommendedDVDStorageBus)(&enmRecommendedStorageBus);
2123 }
2124 if (FAILED(hrc))
2125 return hrc;
2126
2127 /* If the detected guest OS type differs, log a warning if their DVD storage
2128 bus recommendations differ. */
2129 if (bstrGuestOsTypeId != bstrDetectedOSTypeId)
2130 {
2131 StorageBus_T enmRecommendedStorageBus2 = StorageBus_IDE;
2132 hrc = mParent->GetGuestOSType(bstrDetectedOSTypeId.raw(), ptrGuestOSType.asOutParam());
2133 if (SUCCEEDED(hrc) && !ptrGuestOSType.isNull())
2134 hrc = ptrGuestOSType->COMGETTER(RecommendedDVDStorageBus)(&enmRecommendedStorageBus2);
2135 if (FAILED(hrc))
2136 return hrc;
2137
2138 if (enmRecommendedStorageBus != enmRecommendedStorageBus2)
2139 LogRel(("Unattended::reconfigureVM: DVD storage bus recommendations differs for the VM and the ISO guest OS types: VM: %s (%ls), ISO: %s (%ls)\n",
2140 ::stringifyStorageBus(enmRecommendedStorageBus), bstrGuestOsTypeId.raw(),
2141 ::stringifyStorageBus(enmRecommendedStorageBus2), bstrDetectedOSTypeId.raw() ));
2142 }
2143 }
2144
2145 /*
2146 * Take write lock (for lock order reasons, write lock our parent object too)
2147 * then make sure we're the only caller of this method.
2148 */
2149 AutoMultiWriteLock2 alock(mMachine, this COMMA_LOCKVAL_SRC_POS);
2150 HRESULT hrc;
2151 if (mhThreadReconfigureVM == NIL_RTNATIVETHREAD)
2152 {
2153 RTNATIVETHREAD const hNativeSelf = RTThreadNativeSelf();
2154 mhThreadReconfigureVM = hNativeSelf;
2155
2156 /*
2157 * Create a new session, lock the machine and get the session machine object.
2158 * Do the locking without pinning down the write locks, just to be on the safe side.
2159 */
2160 ComPtr<ISession> ptrSession;
2161 try
2162 {
2163 hrc = ptrSession.createInprocObject(CLSID_Session);
2164 }
2165 catch (std::bad_alloc &)
2166 {
2167 hrc = E_OUTOFMEMORY;
2168 }
2169 if (SUCCEEDED(hrc))
2170 {
2171 alock.release();
2172 hrc = mMachine->LockMachine(ptrSession, LockType_Shared);
2173 alock.acquire();
2174 if (SUCCEEDED(hrc))
2175 {
2176 ComPtr<IMachine> ptrSessionMachine;
2177 hrc = ptrSession->COMGETTER(Machine)(ptrSessionMachine.asOutParam());
2178 if (SUCCEEDED(hrc))
2179 {
2180 /*
2181 * Hand the session to the inner work and let it do it job.
2182 */
2183 try
2184 {
2185 hrc = i_innerReconfigureVM(alock, enmRecommendedStorageBus, ptrSessionMachine);
2186 }
2187 catch (...)
2188 {
2189 hrc = E_UNEXPECTED;
2190 }
2191 }
2192
2193 /* Paranoia: release early in case we it a bump below. */
2194 Assert(mhThreadReconfigureVM == hNativeSelf);
2195 mhThreadReconfigureVM = NIL_RTNATIVETHREAD;
2196
2197 /*
2198 * While unlocking the machine we'll have to drop the locks again.
2199 */
2200 alock.release();
2201
2202 ptrSessionMachine.setNull();
2203 HRESULT hrc2 = ptrSession->UnlockMachine();
2204 AssertLogRelMsg(SUCCEEDED(hrc2), ("UnlockMachine -> %Rhrc\n", hrc2));
2205
2206 ptrSession.setNull();
2207
2208 alock.acquire();
2209 }
2210 else
2211 mhThreadReconfigureVM = NIL_RTNATIVETHREAD;
2212 }
2213 else
2214 mhThreadReconfigureVM = NIL_RTNATIVETHREAD;
2215 }
2216 else
2217 hrc = setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("reconfigureVM running on other thread"));
2218 return hrc;
2219}
2220
2221
2222HRESULT Unattended::i_innerReconfigureVM(AutoMultiWriteLock2 &rAutoLock, StorageBus_T enmRecommendedStorageBus,
2223 ComPtr<IMachine> const &rPtrSessionMachine)
2224{
2225 if (mpInstaller == NULL)
2226 return setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("prepare() not yet called"));
2227
2228 // Fetch all available storage controllers
2229 com::SafeIfaceArray<IStorageController> arrayOfControllers;
2230 HRESULT hrc = rPtrSessionMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(arrayOfControllers));
2231 AssertComRCReturn(hrc, hrc);
2232
2233 /*
2234 * Figure out where the images are to be mounted, adding controllers/ports as needed.
2235 */
2236 std::vector<UnattendedInstallationDisk> vecInstallationDisks;
2237 if (mpInstaller->isAuxiliaryFloppyNeeded())
2238 {
2239 hrc = i_reconfigureFloppy(arrayOfControllers, vecInstallationDisks, rPtrSessionMachine, rAutoLock);
2240 if (FAILED(hrc))
2241 return hrc;
2242 }
2243
2244 hrc = i_reconfigureIsos(arrayOfControllers, vecInstallationDisks, rPtrSessionMachine, rAutoLock, enmRecommendedStorageBus);
2245 if (FAILED(hrc))
2246 return hrc;
2247
2248 /*
2249 * Mount the images.
2250 */
2251 for (size_t idxImage = 0; idxImage < vecInstallationDisks.size(); idxImage++)
2252 {
2253 UnattendedInstallationDisk const *pImage = &vecInstallationDisks.at(idxImage);
2254 Assert(pImage->strImagePath.isNotEmpty());
2255 hrc = i_attachImage(pImage, rPtrSessionMachine, rAutoLock);
2256 if (FAILED(hrc))
2257 return hrc;
2258 }
2259
2260 /*
2261 * Set the boot order.
2262 *
2263 * ASSUME that the HD isn't bootable when we start out, but it will be what
2264 * we boot from after the first stage of the installation is done. Setting
2265 * it first prevents endless reboot cylces.
2266 */
2267 /** @todo consider making 100% sure the disk isn't bootable (edit partition
2268 * table active bits and EFI stuff). */
2269 Assert( mpInstaller->getBootableDeviceType() == DeviceType_DVD
2270 || mpInstaller->getBootableDeviceType() == DeviceType_Floppy);
2271 hrc = rPtrSessionMachine->SetBootOrder(1, DeviceType_HardDisk);
2272 if (SUCCEEDED(hrc))
2273 hrc = rPtrSessionMachine->SetBootOrder(2, mpInstaller->getBootableDeviceType());
2274 if (SUCCEEDED(hrc))
2275 hrc = rPtrSessionMachine->SetBootOrder(3, mpInstaller->getBootableDeviceType() == DeviceType_DVD
2276 ? DeviceType_Floppy : DeviceType_DVD);
2277 if (FAILED(hrc))
2278 return hrc;
2279
2280 /*
2281 * Essential step.
2282 *
2283 * HACK ALERT! We have to release the lock here or we'll get into trouble with
2284 * the VirtualBox lock (via i_saveHardware/NetworkAdaptger::i_hasDefaults/VirtualBox::i_findGuestOSType).
2285 */
2286 if (SUCCEEDED(hrc))
2287 {
2288 rAutoLock.release();
2289 hrc = rPtrSessionMachine->SaveSettings();
2290 rAutoLock.acquire();
2291 }
2292
2293 return hrc;
2294}
2295
2296/**
2297 * Makes sure we've got a floppy drive attached to a floppy controller, adding
2298 * the auxiliary floppy image to the installation disk vector.
2299 *
2300 * @returns COM status code.
2301 * @param rControllers The existing controllers.
2302 * @param rVecInstallatationDisks The list of image to mount.
2303 * @param rPtrSessionMachine The session machine smart pointer.
2304 * @param rAutoLock The lock.
2305 */
2306HRESULT Unattended::i_reconfigureFloppy(com::SafeIfaceArray<IStorageController> &rControllers,
2307 std::vector<UnattendedInstallationDisk> &rVecInstallatationDisks,
2308 ComPtr<IMachine> const &rPtrSessionMachine,
2309 AutoMultiWriteLock2 &rAutoLock)
2310{
2311 Assert(mpInstaller->isAuxiliaryFloppyNeeded());
2312
2313 /*
2314 * Look for a floppy controller with a primary drive (A:) we can "insert"
2315 * the auxiliary floppy image. Add a controller and/or a drive if necessary.
2316 */
2317 bool fFoundPort0Dev0 = false;
2318 Bstr bstrControllerName;
2319 Utf8Str strControllerName;
2320
2321 for (size_t i = 0; i < rControllers.size(); ++i)
2322 {
2323 StorageBus_T enmStorageBus;
2324 HRESULT hrc = rControllers[i]->COMGETTER(Bus)(&enmStorageBus);
2325 AssertComRCReturn(hrc, hrc);
2326 if (enmStorageBus == StorageBus_Floppy)
2327 {
2328
2329 /*
2330 * Found a floppy controller.
2331 */
2332 hrc = rControllers[i]->COMGETTER(Name)(bstrControllerName.asOutParam());
2333 AssertComRCReturn(hrc, hrc);
2334
2335 /*
2336 * Check the attchments to see if we've got a device 0 attached on port 0.
2337 *
2338 * While we're at it we eject flppies from all floppy drives we encounter,
2339 * we don't want any confusion at boot or during installation.
2340 */
2341 com::SafeIfaceArray<IMediumAttachment> arrayOfMediumAttachments;
2342 hrc = rPtrSessionMachine->GetMediumAttachmentsOfController(bstrControllerName.raw(),
2343 ComSafeArrayAsOutParam(arrayOfMediumAttachments));
2344 AssertComRCReturn(hrc, hrc);
2345 strControllerName = bstrControllerName;
2346 AssertLogRelReturn(strControllerName.isNotEmpty(), setErrorBoth(E_UNEXPECTED, VERR_INTERNAL_ERROR_2));
2347
2348 for (size_t j = 0; j < arrayOfMediumAttachments.size(); j++)
2349 {
2350 LONG iPort = -1;
2351 hrc = arrayOfMediumAttachments[j]->COMGETTER(Port)(&iPort);
2352 AssertComRCReturn(hrc, hrc);
2353
2354 LONG iDevice = -1;
2355 hrc = arrayOfMediumAttachments[j]->COMGETTER(Device)(&iDevice);
2356 AssertComRCReturn(hrc, hrc);
2357
2358 DeviceType_T enmType;
2359 hrc = arrayOfMediumAttachments[j]->COMGETTER(Type)(&enmType);
2360 AssertComRCReturn(hrc, hrc);
2361
2362 if (enmType == DeviceType_Floppy)
2363 {
2364 ComPtr<IMedium> ptrMedium;
2365 hrc = arrayOfMediumAttachments[j]->COMGETTER(Medium)(ptrMedium.asOutParam());
2366 AssertComRCReturn(hrc, hrc);
2367
2368 if (ptrMedium.isNotNull())
2369 {
2370 ptrMedium.setNull();
2371 rAutoLock.release();
2372 hrc = rPtrSessionMachine->UnmountMedium(bstrControllerName.raw(), iPort, iDevice, TRUE /*fForce*/);
2373 rAutoLock.acquire();
2374 }
2375
2376 if (iPort == 0 && iDevice == 0)
2377 fFoundPort0Dev0 = true;
2378 }
2379 else if (iPort == 0 && iDevice == 0)
2380 return setError(E_FAIL,
2381 tr("Found non-floppy device attached to port 0 device 0 on the floppy controller '%ls'"),
2382 bstrControllerName.raw());
2383 }
2384 }
2385 }
2386
2387 /*
2388 * Add a floppy controller if we need to.
2389 */
2390 if (strControllerName.isEmpty())
2391 {
2392 bstrControllerName = strControllerName = "Floppy";
2393 ComPtr<IStorageController> ptrControllerIgnored;
2394 HRESULT hrc = rPtrSessionMachine->AddStorageController(bstrControllerName.raw(), StorageBus_Floppy,
2395 ptrControllerIgnored.asOutParam());
2396 LogRelFunc(("Machine::addStorageController(Floppy) -> %Rhrc \n", hrc));
2397 if (FAILED(hrc))
2398 return hrc;
2399 }
2400
2401 /*
2402 * Adding a floppy drive (if needed) and mounting the auxiliary image is
2403 * done later together with the ISOs.
2404 */
2405 rVecInstallatationDisks.push_back(UnattendedInstallationDisk(StorageBus_Floppy, strControllerName,
2406 DeviceType_Floppy, AccessMode_ReadWrite,
2407 0, 0,
2408 fFoundPort0Dev0 /*fMountOnly*/,
2409 mpInstaller->getAuxiliaryFloppyFilePath()));
2410 return S_OK;
2411}
2412
2413/**
2414 * Reconfigures DVD drives of the VM to mount all the ISOs we need.
2415 *
2416 * This will umount all DVD media.
2417 *
2418 * @returns COM status code.
2419 * @param rControllers The existing controllers.
2420 * @param rVecInstallatationDisks The list of image to mount.
2421 * @param rPtrSessionMachine The session machine smart pointer.
2422 * @param rAutoLock The lock.
2423 * @param enmRecommendedStorageBus The recommended storage bus type for adding
2424 * DVD drives on.
2425 */
2426HRESULT Unattended::i_reconfigureIsos(com::SafeIfaceArray<IStorageController> &rControllers,
2427 std::vector<UnattendedInstallationDisk> &rVecInstallatationDisks,
2428 ComPtr<IMachine> const &rPtrSessionMachine,
2429 AutoMultiWriteLock2 &rAutoLock, StorageBus_T enmRecommendedStorageBus)
2430{
2431 /*
2432 * Enumerate the attachements of every controller, looking for DVD drives,
2433 * ASSUMEING all drives are bootable.
2434 *
2435 * Eject the medium from all the drives (don't want any confusion) and look
2436 * for the recommended storage bus in case we need to add more drives.
2437 */
2438 HRESULT hrc;
2439 std::list<ControllerSlot> lstControllerDvdSlots;
2440 Utf8Str strRecommendedControllerName; /* non-empty if recommended bus found. */
2441 Utf8Str strControllerName;
2442 Bstr bstrControllerName;
2443 for (size_t i = 0; i < rControllers.size(); ++i)
2444 {
2445 hrc = rControllers[i]->COMGETTER(Name)(bstrControllerName.asOutParam());
2446 AssertComRCReturn(hrc, hrc);
2447 strControllerName = bstrControllerName;
2448
2449 /* Look for recommended storage bus. */
2450 StorageBus_T enmStorageBus;
2451 hrc = rControllers[i]->COMGETTER(Bus)(&enmStorageBus);
2452 AssertComRCReturn(hrc, hrc);
2453 if (enmStorageBus == enmRecommendedStorageBus)
2454 {
2455 strRecommendedControllerName = bstrControllerName;
2456 AssertLogRelReturn(strControllerName.isNotEmpty(), setErrorBoth(E_UNEXPECTED, VERR_INTERNAL_ERROR_2));
2457 }
2458
2459 /* Scan the controller attachments. */
2460 com::SafeIfaceArray<IMediumAttachment> arrayOfMediumAttachments;
2461 hrc = rPtrSessionMachine->GetMediumAttachmentsOfController(bstrControllerName.raw(),
2462 ComSafeArrayAsOutParam(arrayOfMediumAttachments));
2463 AssertComRCReturn(hrc, hrc);
2464
2465 for (size_t j = 0; j < arrayOfMediumAttachments.size(); j++)
2466 {
2467 DeviceType_T enmType;
2468 hrc = arrayOfMediumAttachments[j]->COMGETTER(Type)(&enmType);
2469 AssertComRCReturn(hrc, hrc);
2470 if (enmType == DeviceType_DVD)
2471 {
2472 LONG iPort = -1;
2473 hrc = arrayOfMediumAttachments[j]->COMGETTER(Port)(&iPort);
2474 AssertComRCReturn(hrc, hrc);
2475
2476 LONG iDevice = -1;
2477 hrc = arrayOfMediumAttachments[j]->COMGETTER(Device)(&iDevice);
2478 AssertComRCReturn(hrc, hrc);
2479
2480 /* Remeber it. */
2481 lstControllerDvdSlots.push_back(ControllerSlot(enmStorageBus, strControllerName, iPort, iDevice, false /*fFree*/));
2482
2483 /* Eject the medium, if any. */
2484 ComPtr<IMedium> ptrMedium;
2485 hrc = arrayOfMediumAttachments[j]->COMGETTER(Medium)(ptrMedium.asOutParam());
2486 AssertComRCReturn(hrc, hrc);
2487 if (ptrMedium.isNotNull())
2488 {
2489 ptrMedium.setNull();
2490
2491 rAutoLock.release();
2492 hrc = rPtrSessionMachine->UnmountMedium(bstrControllerName.raw(), iPort, iDevice, TRUE /*fForce*/);
2493 rAutoLock.acquire();
2494 }
2495 }
2496 }
2497 }
2498
2499 /*
2500 * How many drives do we need? Add more if necessary.
2501 */
2502 ULONG cDvdDrivesNeeded = 0;
2503 if (mpInstaller->isAuxiliaryIsoNeeded())
2504 cDvdDrivesNeeded++;
2505 if (mpInstaller->isOriginalIsoNeeded())
2506 cDvdDrivesNeeded++;
2507#if 0 /* These are now in the AUX VISO. */
2508 if (mpInstaller->isAdditionsIsoNeeded())
2509 cDvdDrivesNeeded++;
2510 if (mpInstaller->isValidationKitIsoNeeded())
2511 cDvdDrivesNeeded++;
2512#endif
2513 Assert(cDvdDrivesNeeded > 0);
2514 if (cDvdDrivesNeeded > lstControllerDvdSlots.size())
2515 {
2516 /* Do we need to add the recommended controller? */
2517 if (strRecommendedControllerName.isEmpty())
2518 {
2519 switch (enmRecommendedStorageBus)
2520 {
2521 case StorageBus_IDE: strRecommendedControllerName = "IDE"; break;
2522 case StorageBus_SATA: strRecommendedControllerName = "SATA"; break;
2523 case StorageBus_SCSI: strRecommendedControllerName = "SCSI"; break;
2524 case StorageBus_SAS: strRecommendedControllerName = "SAS"; break;
2525 case StorageBus_USB: strRecommendedControllerName = "USB"; break;
2526 case StorageBus_PCIe: strRecommendedControllerName = "PCIe"; break;
2527 default:
2528 return setError(E_FAIL, tr("Support for recommended storage bus %d not implemented"),
2529 (int)enmRecommendedStorageBus);
2530 }
2531 ComPtr<IStorageController> ptrControllerIgnored;
2532 hrc = rPtrSessionMachine->AddStorageController(Bstr(strRecommendedControllerName).raw(), enmRecommendedStorageBus,
2533 ptrControllerIgnored.asOutParam());
2534 LogRelFunc(("Machine::addStorageController(%s) -> %Rhrc \n", strRecommendedControllerName.c_str(), hrc));
2535 if (FAILED(hrc))
2536 return hrc;
2537 }
2538
2539 /* Add free controller slots, maybe raising the port limit on the controller if we can. */
2540 hrc = i_findOrCreateNeededFreeSlots(strRecommendedControllerName, enmRecommendedStorageBus, rPtrSessionMachine,
2541 cDvdDrivesNeeded, lstControllerDvdSlots);
2542 if (FAILED(hrc))
2543 return hrc;
2544 if (cDvdDrivesNeeded > lstControllerDvdSlots.size())
2545 {
2546 /* We could in many cases create another controller here, but it's not worth the effort. */
2547 return setError(E_FAIL, tr("Not enough free slots on controller '%s' to add %u DVD drive(s)", "",
2548 cDvdDrivesNeeded - lstControllerDvdSlots.size()),
2549 strRecommendedControllerName.c_str(), cDvdDrivesNeeded - lstControllerDvdSlots.size());
2550 }
2551 Assert(cDvdDrivesNeeded == lstControllerDvdSlots.size());
2552 }
2553
2554 /*
2555 * Sort the DVD slots in boot order.
2556 */
2557 lstControllerDvdSlots.sort();
2558
2559 /*
2560 * Prepare ISO mounts.
2561 *
2562 * Boot order depends on bootFromAuxiliaryIso() and we must grab DVD slots
2563 * according to the boot order.
2564 */
2565 std::list<ControllerSlot>::const_iterator itDvdSlot = lstControllerDvdSlots.begin();
2566 if (mpInstaller->isAuxiliaryIsoNeeded() && mpInstaller->bootFromAuxiliaryIso())
2567 {
2568 rVecInstallatationDisks.push_back(UnattendedInstallationDisk(itDvdSlot, mpInstaller->getAuxiliaryIsoFilePath()));
2569 ++itDvdSlot;
2570 }
2571
2572 if (mpInstaller->isOriginalIsoNeeded())
2573 {
2574 rVecInstallatationDisks.push_back(UnattendedInstallationDisk(itDvdSlot, i_getIsoPath()));
2575 ++itDvdSlot;
2576 }
2577
2578 if (mpInstaller->isAuxiliaryIsoNeeded() && !mpInstaller->bootFromAuxiliaryIso())
2579 {
2580 rVecInstallatationDisks.push_back(UnattendedInstallationDisk(itDvdSlot, mpInstaller->getAuxiliaryIsoFilePath()));
2581 ++itDvdSlot;
2582 }
2583
2584#if 0 /* These are now in the AUX VISO. */
2585 if (mpInstaller->isAdditionsIsoNeeded())
2586 {
2587 rVecInstallatationDisks.push_back(UnattendedInstallationDisk(itDvdSlot, i_getAdditionsIsoPath()));
2588 ++itDvdSlot;
2589 }
2590
2591 if (mpInstaller->isValidationKitIsoNeeded())
2592 {
2593 rVecInstallatationDisks.push_back(UnattendedInstallationDisk(itDvdSlot, i_getValidationKitIsoPath()));
2594 ++itDvdSlot;
2595 }
2596#endif
2597
2598 return S_OK;
2599}
2600
2601/**
2602 * Used to find more free slots for DVD drives during VM reconfiguration.
2603 *
2604 * This may modify the @a portCount property of the given controller.
2605 *
2606 * @returns COM status code.
2607 * @param rStrControllerName The name of the controller to find/create
2608 * free slots on.
2609 * @param enmStorageBus The storage bus type.
2610 * @param rPtrSessionMachine Reference to the session machine.
2611 * @param cSlotsNeeded Total slots needed (including those we've
2612 * already found).
2613 * @param rDvdSlots The slot collection for DVD drives to add
2614 * free slots to as we find/create them.
2615 */
2616HRESULT Unattended::i_findOrCreateNeededFreeSlots(const Utf8Str &rStrControllerName, StorageBus_T enmStorageBus,
2617 ComPtr<IMachine> const &rPtrSessionMachine, uint32_t cSlotsNeeded,
2618 std::list<ControllerSlot> &rDvdSlots)
2619{
2620 Assert(cSlotsNeeded > rDvdSlots.size());
2621
2622 /*
2623 * Get controlleer stats.
2624 */
2625 ComPtr<IStorageController> pController;
2626 HRESULT hrc = rPtrSessionMachine->GetStorageControllerByName(Bstr(rStrControllerName).raw(), pController.asOutParam());
2627 AssertComRCReturn(hrc, hrc);
2628
2629 ULONG cMaxDevicesPerPort = 1;
2630 hrc = pController->COMGETTER(MaxDevicesPerPortCount)(&cMaxDevicesPerPort);
2631 AssertComRCReturn(hrc, hrc);
2632 AssertLogRelReturn(cMaxDevicesPerPort > 0, E_UNEXPECTED);
2633
2634 ULONG cPorts = 0;
2635 hrc = pController->COMGETTER(PortCount)(&cPorts);
2636 AssertComRCReturn(hrc, hrc);
2637
2638 /*
2639 * Get the attachment list and turn into an internal list for lookup speed.
2640 */
2641 com::SafeIfaceArray<IMediumAttachment> arrayOfMediumAttachments;
2642 hrc = rPtrSessionMachine->GetMediumAttachmentsOfController(Bstr(rStrControllerName).raw(),
2643 ComSafeArrayAsOutParam(arrayOfMediumAttachments));
2644 AssertComRCReturn(hrc, hrc);
2645
2646 std::vector<ControllerSlot> arrayOfUsedSlots;
2647 for (size_t i = 0; i < arrayOfMediumAttachments.size(); i++)
2648 {
2649 LONG iPort = -1;
2650 hrc = arrayOfMediumAttachments[i]->COMGETTER(Port)(&iPort);
2651 AssertComRCReturn(hrc, hrc);
2652
2653 LONG iDevice = -1;
2654 hrc = arrayOfMediumAttachments[i]->COMGETTER(Device)(&iDevice);
2655 AssertComRCReturn(hrc, hrc);
2656
2657 arrayOfUsedSlots.push_back(ControllerSlot(enmStorageBus, Utf8Str::Empty, iPort, iDevice, false /*fFree*/));
2658 }
2659
2660 /*
2661 * Iterate thru all possible slots, adding those not found in arrayOfUsedSlots.
2662 */
2663 for (int32_t iPort = 0; iPort < (int32_t)cPorts; iPort++)
2664 for (int32_t iDevice = 0; iDevice < (int32_t)cMaxDevicesPerPort; iDevice++)
2665 {
2666 bool fFound = false;
2667 for (size_t i = 0; i < arrayOfUsedSlots.size(); i++)
2668 if ( arrayOfUsedSlots[i].iPort == iPort
2669 && arrayOfUsedSlots[i].iDevice == iDevice)
2670 {
2671 fFound = true;
2672 break;
2673 }
2674 if (!fFound)
2675 {
2676 rDvdSlots.push_back(ControllerSlot(enmStorageBus, rStrControllerName, iPort, iDevice, true /*fFree*/));
2677 if (rDvdSlots.size() >= cSlotsNeeded)
2678 return S_OK;
2679 }
2680 }
2681
2682 /*
2683 * Okay we still need more ports. See if increasing the number of controller
2684 * ports would solve it.
2685 */
2686 ULONG cMaxPorts = 1;
2687 hrc = pController->COMGETTER(MaxPortCount)(&cMaxPorts);
2688 AssertComRCReturn(hrc, hrc);
2689 if (cMaxPorts <= cPorts)
2690 return S_OK;
2691 size_t cNewPortsNeeded = (cSlotsNeeded - rDvdSlots.size() + cMaxDevicesPerPort - 1) / cMaxDevicesPerPort;
2692 if (cPorts + cNewPortsNeeded > cMaxPorts)
2693 return S_OK;
2694
2695 /*
2696 * Raise the port count and add the free slots we've just created.
2697 */
2698 hrc = pController->COMSETTER(PortCount)(cPorts + (ULONG)cNewPortsNeeded);
2699 AssertComRCReturn(hrc, hrc);
2700 int32_t const cPortsNew = (int32_t)(cPorts + cNewPortsNeeded);
2701 for (int32_t iPort = (int32_t)cPorts; iPort < cPortsNew; iPort++)
2702 for (int32_t iDevice = 0; iDevice < (int32_t)cMaxDevicesPerPort; iDevice++)
2703 {
2704 rDvdSlots.push_back(ControllerSlot(enmStorageBus, rStrControllerName, iPort, iDevice, true /*fFree*/));
2705 if (rDvdSlots.size() >= cSlotsNeeded)
2706 return S_OK;
2707 }
2708
2709 /* We should not get here! */
2710 AssertLogRelFailedReturn(E_UNEXPECTED);
2711}
2712
2713HRESULT Unattended::done()
2714{
2715 LogFlow(("Unattended::done\n"));
2716 if (mpInstaller)
2717 {
2718 LogRelFlow(("Unattended::done: Deleting installer object (%p)\n", mpInstaller));
2719 delete mpInstaller;
2720 mpInstaller = NULL;
2721 }
2722 return S_OK;
2723}
2724
2725HRESULT Unattended::getIsoPath(com::Utf8Str &isoPath)
2726{
2727 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2728 isoPath = mStrIsoPath;
2729 return S_OK;
2730}
2731
2732HRESULT Unattended::setIsoPath(const com::Utf8Str &isoPath)
2733{
2734 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2735 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2736 mStrIsoPath = isoPath;
2737 mfDoneDetectIsoOS = false;
2738 return S_OK;
2739}
2740
2741HRESULT Unattended::getUser(com::Utf8Str &user)
2742{
2743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2744 user = mStrUser;
2745 return S_OK;
2746}
2747
2748
2749HRESULT Unattended::setUser(const com::Utf8Str &user)
2750{
2751 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2752 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2753 mStrUser = user;
2754 return S_OK;
2755}
2756
2757HRESULT Unattended::getPassword(com::Utf8Str &password)
2758{
2759 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2760 password = mStrPassword;
2761 return S_OK;
2762}
2763
2764HRESULT Unattended::setPassword(const com::Utf8Str &password)
2765{
2766 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2767 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2768 mStrPassword = password;
2769 return S_OK;
2770}
2771
2772HRESULT Unattended::getFullUserName(com::Utf8Str &fullUserName)
2773{
2774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2775 fullUserName = mStrFullUserName;
2776 return S_OK;
2777}
2778
2779HRESULT Unattended::setFullUserName(const com::Utf8Str &fullUserName)
2780{
2781 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2782 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2783 mStrFullUserName = fullUserName;
2784 return S_OK;
2785}
2786
2787HRESULT Unattended::getProductKey(com::Utf8Str &productKey)
2788{
2789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2790 productKey = mStrProductKey;
2791 return S_OK;
2792}
2793
2794HRESULT Unattended::setProductKey(const com::Utf8Str &productKey)
2795{
2796 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2797 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2798 mStrProductKey = productKey;
2799 return S_OK;
2800}
2801
2802HRESULT Unattended::getAdditionsIsoPath(com::Utf8Str &additionsIsoPath)
2803{
2804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2805 additionsIsoPath = mStrAdditionsIsoPath;
2806 return S_OK;
2807}
2808
2809HRESULT Unattended::setAdditionsIsoPath(const com::Utf8Str &additionsIsoPath)
2810{
2811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2812 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2813 mStrAdditionsIsoPath = additionsIsoPath;
2814 return S_OK;
2815}
2816
2817HRESULT Unattended::getInstallGuestAdditions(BOOL *installGuestAdditions)
2818{
2819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2820 *installGuestAdditions = mfInstallGuestAdditions;
2821 return S_OK;
2822}
2823
2824HRESULT Unattended::setInstallGuestAdditions(BOOL installGuestAdditions)
2825{
2826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2827 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2828 mfInstallGuestAdditions = installGuestAdditions != FALSE;
2829 return S_OK;
2830}
2831
2832HRESULT Unattended::getValidationKitIsoPath(com::Utf8Str &aValidationKitIsoPath)
2833{
2834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2835 aValidationKitIsoPath = mStrValidationKitIsoPath;
2836 return S_OK;
2837}
2838
2839HRESULT Unattended::setValidationKitIsoPath(const com::Utf8Str &aValidationKitIsoPath)
2840{
2841 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2842 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2843 mStrValidationKitIsoPath = aValidationKitIsoPath;
2844 return S_OK;
2845}
2846
2847HRESULT Unattended::getInstallTestExecService(BOOL *aInstallTestExecService)
2848{
2849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2850 *aInstallTestExecService = mfInstallTestExecService;
2851 return S_OK;
2852}
2853
2854HRESULT Unattended::setInstallTestExecService(BOOL aInstallTestExecService)
2855{
2856 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2857 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2858 mfInstallTestExecService = aInstallTestExecService != FALSE;
2859 return S_OK;
2860}
2861
2862HRESULT Unattended::getTimeZone(com::Utf8Str &aTimeZone)
2863{
2864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2865 aTimeZone = mStrTimeZone;
2866 return S_OK;
2867}
2868
2869HRESULT Unattended::setTimeZone(const com::Utf8Str &aTimezone)
2870{
2871 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2872 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2873 mStrTimeZone = aTimezone;
2874 return S_OK;
2875}
2876
2877HRESULT Unattended::getLocale(com::Utf8Str &aLocale)
2878{
2879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2880 aLocale = mStrLocale;
2881 return S_OK;
2882}
2883
2884HRESULT Unattended::setLocale(const com::Utf8Str &aLocale)
2885{
2886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2887 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2888 if ( aLocale.isEmpty() /* use default */
2889 || ( aLocale.length() == 5
2890 && RT_C_IS_LOWER(aLocale[0])
2891 && RT_C_IS_LOWER(aLocale[1])
2892 && aLocale[2] == '_'
2893 && RT_C_IS_UPPER(aLocale[3])
2894 && RT_C_IS_UPPER(aLocale[4])) )
2895 {
2896 mStrLocale = aLocale;
2897 return S_OK;
2898 }
2899 return setError(E_INVALIDARG, tr("Expected two lower cased letters, an underscore, and two upper cased letters"));
2900}
2901
2902HRESULT Unattended::getLanguage(com::Utf8Str &aLanguage)
2903{
2904 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2905 aLanguage = mStrLanguage;
2906 return S_OK;
2907}
2908
2909HRESULT Unattended::setLanguage(const com::Utf8Str &aLanguage)
2910{
2911 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2912 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2913 mStrLanguage = aLanguage;
2914 return S_OK;
2915}
2916
2917HRESULT Unattended::getCountry(com::Utf8Str &aCountry)
2918{
2919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2920 aCountry = mStrCountry;
2921 return S_OK;
2922}
2923
2924HRESULT Unattended::setCountry(const com::Utf8Str &aCountry)
2925{
2926 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2927 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2928 if ( aCountry.isEmpty()
2929 || ( aCountry.length() == 2
2930 && RT_C_IS_UPPER(aCountry[0])
2931 && RT_C_IS_UPPER(aCountry[1])) )
2932 {
2933 mStrCountry = aCountry;
2934 return S_OK;
2935 }
2936 return setError(E_INVALIDARG, tr("Expected two upper cased letters"));
2937}
2938
2939HRESULT Unattended::getProxy(com::Utf8Str &aProxy)
2940{
2941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2942 aProxy = mStrProxy; /// @todo turn schema map into string or something.
2943 return S_OK;
2944}
2945
2946HRESULT Unattended::setProxy(const com::Utf8Str &aProxy)
2947{
2948 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2949 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2950 if (aProxy.isEmpty())
2951 {
2952 /* set default proxy */
2953 /** @todo BUGBUG! implement this */
2954 }
2955 else if (aProxy.equalsIgnoreCase("none"))
2956 {
2957 /* clear proxy config */
2958 mStrProxy.setNull();
2959 }
2960 else
2961 {
2962 /** @todo Parse and set proxy config into a schema map or something along those lines. */
2963 /** @todo BUGBUG! implement this */
2964 // return E_NOTIMPL;
2965 mStrProxy = aProxy;
2966 }
2967 return S_OK;
2968}
2969
2970HRESULT Unattended::getPackageSelectionAdjustments(com::Utf8Str &aPackageSelectionAdjustments)
2971{
2972 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2973 aPackageSelectionAdjustments = RTCString::join(mPackageSelectionAdjustments, ";");
2974 return S_OK;
2975}
2976
2977HRESULT Unattended::setPackageSelectionAdjustments(const com::Utf8Str &aPackageSelectionAdjustments)
2978{
2979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2980 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2981 if (aPackageSelectionAdjustments.isEmpty())
2982 mPackageSelectionAdjustments.clear();
2983 else
2984 {
2985 RTCList<RTCString, RTCString *> arrayStrSplit = aPackageSelectionAdjustments.split(";");
2986 for (size_t i = 0; i < arrayStrSplit.size(); i++)
2987 {
2988 if (arrayStrSplit[i].equals("minimal"))
2989 { /* okay */ }
2990 else
2991 return setError(E_INVALIDARG, tr("Unknown keyword: %s"), arrayStrSplit[i].c_str());
2992 }
2993 mPackageSelectionAdjustments = arrayStrSplit;
2994 }
2995 return S_OK;
2996}
2997
2998HRESULT Unattended::getHostname(com::Utf8Str &aHostname)
2999{
3000 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3001 aHostname = mStrHostname;
3002 return S_OK;
3003}
3004
3005HRESULT Unattended::setHostname(const com::Utf8Str &aHostname)
3006{
3007 /*
3008 * Validate input.
3009 */
3010 if (aHostname.length() > (aHostname.endsWith(".") ? 254U : 253U))
3011 return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
3012 tr("Hostname '%s' is %zu bytes long, max is 253 (excluding trailing dot)", "", aHostname.length()),
3013 aHostname.c_str(), aHostname.length());
3014 size_t cLabels = 0;
3015 const char *pszSrc = aHostname.c_str();
3016 for (;;)
3017 {
3018 size_t cchLabel = 1;
3019 char ch = *pszSrc++;
3020 if (RT_C_IS_ALNUM(ch))
3021 {
3022 cLabels++;
3023 while ((ch = *pszSrc++) != '.' && ch != '\0')
3024 {
3025 if (RT_C_IS_ALNUM(ch) || ch == '-')
3026 {
3027 if (cchLabel < 63)
3028 cchLabel++;
3029 else
3030 return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
3031 tr("Invalid hostname '%s' - label %u is too long, max is 63."),
3032 aHostname.c_str(), cLabels);
3033 }
3034 else
3035 return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
3036 tr("Invalid hostname '%s' - illegal char '%c' at position %zu"),
3037 aHostname.c_str(), ch, pszSrc - aHostname.c_str() - 1);
3038 }
3039 if (cLabels == 1 && cchLabel < 2)
3040 return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
3041 tr("Invalid hostname '%s' - the name part must be at least two characters long"),
3042 aHostname.c_str());
3043 if (ch == '\0')
3044 break;
3045 }
3046 else if (ch != '\0')
3047 return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
3048 tr("Invalid hostname '%s' - illegal lead char '%c' at position %zu"),
3049 aHostname.c_str(), ch, pszSrc - aHostname.c_str() - 1);
3050 else
3051 return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
3052 tr("Invalid hostname '%s' - trailing dot not permitted"), aHostname.c_str());
3053 }
3054 if (cLabels < 2)
3055 return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
3056 tr("Incomplete hostname '%s' - must include both a name and a domain"), aHostname.c_str());
3057
3058 /*
3059 * Make the change.
3060 */
3061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3062 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
3063 mStrHostname = aHostname;
3064 return S_OK;
3065}
3066
3067HRESULT Unattended::getAuxiliaryBasePath(com::Utf8Str &aAuxiliaryBasePath)
3068{
3069 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3070 aAuxiliaryBasePath = mStrAuxiliaryBasePath;
3071 return S_OK;
3072}
3073
3074HRESULT Unattended::setAuxiliaryBasePath(const com::Utf8Str &aAuxiliaryBasePath)
3075{
3076 if (aAuxiliaryBasePath.isEmpty())
3077 return setError(E_INVALIDARG, tr("Empty base path is not allowed"));
3078 if (!RTPathStartsWithRoot(aAuxiliaryBasePath.c_str()))
3079 return setError(E_INVALIDARG, tr("Base path must be absolute"));
3080
3081 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3082 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
3083 mStrAuxiliaryBasePath = aAuxiliaryBasePath;
3084 mfIsDefaultAuxiliaryBasePath = mStrAuxiliaryBasePath.isEmpty();
3085 return S_OK;
3086}
3087
3088HRESULT Unattended::getImageIndex(ULONG *index)
3089{
3090 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3091 *index = midxImage;
3092 return S_OK;
3093}
3094
3095HRESULT Unattended::setImageIndex(ULONG index)
3096{
3097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3098 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
3099 midxImage = index;
3100 return S_OK;
3101}
3102
3103HRESULT Unattended::getMachine(ComPtr<IMachine> &aMachine)
3104{
3105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3106 return mMachine.queryInterfaceTo(aMachine.asOutParam());
3107}
3108
3109HRESULT Unattended::setMachine(const ComPtr<IMachine> &aMachine)
3110{
3111 /*
3112 * Lookup the VM so we can safely get the Machine instance.
3113 * (Don't want to test how reliable XPCOM and COM are with finding
3114 * the local object instance when a client passes a stub back.)
3115 */
3116 Bstr bstrUuidMachine;
3117 HRESULT hrc = aMachine->COMGETTER(Id)(bstrUuidMachine.asOutParam());
3118 if (SUCCEEDED(hrc))
3119 {
3120 Guid UuidMachine(bstrUuidMachine);
3121 ComObjPtr<Machine> ptrMachine;
3122 hrc = mParent->i_findMachine(UuidMachine, false /*fPermitInaccessible*/, true /*aSetError*/, &ptrMachine);
3123 if (SUCCEEDED(hrc))
3124 {
3125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3126 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER,
3127 tr("Cannot change after prepare() has been called")));
3128 mMachine = ptrMachine;
3129 mMachineUuid = UuidMachine;
3130 if (mfIsDefaultAuxiliaryBasePath)
3131 mStrAuxiliaryBasePath.setNull();
3132 hrc = S_OK;
3133 }
3134 }
3135 return hrc;
3136}
3137
3138HRESULT Unattended::getScriptTemplatePath(com::Utf8Str &aScriptTemplatePath)
3139{
3140 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3141 if ( mStrScriptTemplatePath.isNotEmpty()
3142 || mpInstaller == NULL)
3143 aScriptTemplatePath = mStrScriptTemplatePath;
3144 else
3145 aScriptTemplatePath = mpInstaller->getTemplateFilePath();
3146 return S_OK;
3147}
3148
3149HRESULT Unattended::setScriptTemplatePath(const com::Utf8Str &aScriptTemplatePath)
3150{
3151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3152 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
3153 mStrScriptTemplatePath = aScriptTemplatePath;
3154 return S_OK;
3155}
3156
3157HRESULT Unattended::getPostInstallScriptTemplatePath(com::Utf8Str &aPostInstallScriptTemplatePath)
3158{
3159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3160 if ( mStrPostInstallScriptTemplatePath.isNotEmpty()
3161 || mpInstaller == NULL)
3162 aPostInstallScriptTemplatePath = mStrPostInstallScriptTemplatePath;
3163 else
3164 aPostInstallScriptTemplatePath = mpInstaller->getPostTemplateFilePath();
3165 return S_OK;
3166}
3167
3168HRESULT Unattended::setPostInstallScriptTemplatePath(const com::Utf8Str &aPostInstallScriptTemplatePath)
3169{
3170 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3171 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
3172 mStrPostInstallScriptTemplatePath = aPostInstallScriptTemplatePath;
3173 return S_OK;
3174}
3175
3176HRESULT Unattended::getPostInstallCommand(com::Utf8Str &aPostInstallCommand)
3177{
3178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3179 aPostInstallCommand = mStrPostInstallCommand;
3180 return S_OK;
3181}
3182
3183HRESULT Unattended::setPostInstallCommand(const com::Utf8Str &aPostInstallCommand)
3184{
3185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3186 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
3187 mStrPostInstallCommand = aPostInstallCommand;
3188 return S_OK;
3189}
3190
3191HRESULT Unattended::getExtraInstallKernelParameters(com::Utf8Str &aExtraInstallKernelParameters)
3192{
3193 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3194 if ( mStrExtraInstallKernelParameters.isNotEmpty()
3195 || mpInstaller == NULL)
3196 aExtraInstallKernelParameters = mStrExtraInstallKernelParameters;
3197 else
3198 aExtraInstallKernelParameters = mpInstaller->getDefaultExtraInstallKernelParameters();
3199 return S_OK;
3200}
3201
3202HRESULT Unattended::setExtraInstallKernelParameters(const com::Utf8Str &aExtraInstallKernelParameters)
3203{
3204 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3205 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
3206 mStrExtraInstallKernelParameters = aExtraInstallKernelParameters;
3207 return S_OK;
3208}
3209
3210HRESULT Unattended::getDetectedOSTypeId(com::Utf8Str &aDetectedOSTypeId)
3211{
3212 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3213 aDetectedOSTypeId = mStrDetectedOSTypeId;
3214 return S_OK;
3215}
3216
3217HRESULT Unattended::getDetectedOSVersion(com::Utf8Str &aDetectedOSVersion)
3218{
3219 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3220 aDetectedOSVersion = mStrDetectedOSVersion;
3221 return S_OK;
3222}
3223
3224HRESULT Unattended::getDetectedOSFlavor(com::Utf8Str &aDetectedOSFlavor)
3225{
3226 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3227 aDetectedOSFlavor = mStrDetectedOSFlavor;
3228 return S_OK;
3229}
3230
3231HRESULT Unattended::getDetectedOSLanguages(com::Utf8Str &aDetectedOSLanguages)
3232{
3233 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3234 aDetectedOSLanguages = RTCString::join(mDetectedOSLanguages, " ");
3235 return S_OK;
3236}
3237
3238HRESULT Unattended::getDetectedOSHints(com::Utf8Str &aDetectedOSHints)
3239{
3240 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3241 aDetectedOSHints = mStrDetectedOSHints;
3242 return S_OK;
3243}
3244
3245HRESULT Unattended::getDetectedImageNames(std::vector<com::Utf8Str> &aDetectedImageNames)
3246{
3247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3248 aDetectedImageNames.clear();
3249 for (size_t i = 0; i < mDetectedImages.size(); ++i)
3250 aDetectedImageNames.push_back(mDetectedImages[i].getNameAndVersion());
3251 return S_OK;
3252}
3253
3254HRESULT Unattended::getDetectedImageIndices(std::vector<ULONG> &aDetectedImageIndices)
3255{
3256 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3257 aDetectedImageIndices.clear();
3258 for (size_t i = 0; i < mDetectedImages.size(); ++i)
3259 aDetectedImageIndices.push_back(mDetectedImages[i].mImageIndex);
3260 return S_OK;
3261}
3262
3263/*
3264 * Getters that the installer and script classes can use.
3265 */
3266Utf8Str const &Unattended::i_getIsoPath() const
3267{
3268 Assert(isReadLockedOnCurrentThread());
3269 return mStrIsoPath;
3270}
3271
3272Utf8Str const &Unattended::i_getUser() const
3273{
3274 Assert(isReadLockedOnCurrentThread());
3275 return mStrUser;
3276}
3277
3278Utf8Str const &Unattended::i_getPassword() const
3279{
3280 Assert(isReadLockedOnCurrentThread());
3281 return mStrPassword;
3282}
3283
3284Utf8Str const &Unattended::i_getFullUserName() const
3285{
3286 Assert(isReadLockedOnCurrentThread());
3287 return mStrFullUserName.isNotEmpty() ? mStrFullUserName : mStrUser;
3288}
3289
3290Utf8Str const &Unattended::i_getProductKey() const
3291{
3292 Assert(isReadLockedOnCurrentThread());
3293 return mStrProductKey;
3294}
3295
3296Utf8Str const &Unattended::i_getProxy() const
3297{
3298 Assert(isReadLockedOnCurrentThread());
3299 return mStrProxy;
3300}
3301
3302Utf8Str const &Unattended::i_getAdditionsIsoPath() const
3303{
3304 Assert(isReadLockedOnCurrentThread());
3305 return mStrAdditionsIsoPath;
3306}
3307
3308bool Unattended::i_getInstallGuestAdditions() const
3309{
3310 Assert(isReadLockedOnCurrentThread());
3311 return mfInstallGuestAdditions;
3312}
3313
3314Utf8Str const &Unattended::i_getValidationKitIsoPath() const
3315{
3316 Assert(isReadLockedOnCurrentThread());
3317 return mStrValidationKitIsoPath;
3318}
3319
3320bool Unattended::i_getInstallTestExecService() const
3321{
3322 Assert(isReadLockedOnCurrentThread());
3323 return mfInstallTestExecService;
3324}
3325
3326Utf8Str const &Unattended::i_getTimeZone() const
3327{
3328 Assert(isReadLockedOnCurrentThread());
3329 return mStrTimeZone;
3330}
3331
3332PCRTTIMEZONEINFO Unattended::i_getTimeZoneInfo() const
3333{
3334 Assert(isReadLockedOnCurrentThread());
3335 return mpTimeZoneInfo;
3336}
3337
3338Utf8Str const &Unattended::i_getLocale() const
3339{
3340 Assert(isReadLockedOnCurrentThread());
3341 return mStrLocale;
3342}
3343
3344Utf8Str const &Unattended::i_getLanguage() const
3345{
3346 Assert(isReadLockedOnCurrentThread());
3347 return mStrLanguage;
3348}
3349
3350Utf8Str const &Unattended::i_getCountry() const
3351{
3352 Assert(isReadLockedOnCurrentThread());
3353 return mStrCountry;
3354}
3355
3356bool Unattended::i_isMinimalInstallation() const
3357{
3358 size_t i = mPackageSelectionAdjustments.size();
3359 while (i-- > 0)
3360 if (mPackageSelectionAdjustments[i].equals("minimal"))
3361 return true;
3362 return false;
3363}
3364
3365Utf8Str const &Unattended::i_getHostname() const
3366{
3367 Assert(isReadLockedOnCurrentThread());
3368 return mStrHostname;
3369}
3370
3371Utf8Str const &Unattended::i_getAuxiliaryBasePath() const
3372{
3373 Assert(isReadLockedOnCurrentThread());
3374 return mStrAuxiliaryBasePath;
3375}
3376
3377ULONG Unattended::i_getImageIndex() const
3378{
3379 Assert(isReadLockedOnCurrentThread());
3380 return midxImage;
3381}
3382
3383Utf8Str const &Unattended::i_getScriptTemplatePath() const
3384{
3385 Assert(isReadLockedOnCurrentThread());
3386 return mStrScriptTemplatePath;
3387}
3388
3389Utf8Str const &Unattended::i_getPostInstallScriptTemplatePath() const
3390{
3391 Assert(isReadLockedOnCurrentThread());
3392 return mStrPostInstallScriptTemplatePath;
3393}
3394
3395Utf8Str const &Unattended::i_getPostInstallCommand() const
3396{
3397 Assert(isReadLockedOnCurrentThread());
3398 return mStrPostInstallCommand;
3399}
3400
3401Utf8Str const &Unattended::i_getAuxiliaryInstallDir() const
3402{
3403 Assert(isReadLockedOnCurrentThread());
3404 /* Only the installer knows, forward the call. */
3405 AssertReturn(mpInstaller != NULL, Utf8Str::Empty);
3406 return mpInstaller->getAuxiliaryInstallDir();
3407}
3408
3409Utf8Str const &Unattended::i_getExtraInstallKernelParameters() const
3410{
3411 Assert(isReadLockedOnCurrentThread());
3412 return mStrExtraInstallKernelParameters;
3413}
3414
3415bool Unattended::i_isRtcUsingUtc() const
3416{
3417 Assert(isReadLockedOnCurrentThread());
3418 return mfRtcUseUtc;
3419}
3420
3421bool Unattended::i_isGuestOs64Bit() const
3422{
3423 Assert(isReadLockedOnCurrentThread());
3424 return mfGuestOs64Bit;
3425}
3426
3427bool Unattended::i_isFirmwareEFI() const
3428{
3429 Assert(isReadLockedOnCurrentThread());
3430 return menmFirmwareType != FirmwareType_BIOS;
3431}
3432
3433Utf8Str const &Unattended::i_getDetectedOSVersion()
3434{
3435 Assert(isReadLockedOnCurrentThread());
3436 return mStrDetectedOSVersion;
3437}
3438
3439HRESULT Unattended::i_attachImage(UnattendedInstallationDisk const *pImage, ComPtr<IMachine> const &rPtrSessionMachine,
3440 AutoMultiWriteLock2 &rLock)
3441{
3442 /*
3443 * Attach the disk image
3444 * HACK ALERT! Temporarily release the Unattended lock.
3445 */
3446 rLock.release();
3447
3448 ComPtr<IMedium> ptrMedium;
3449 HRESULT rc = mParent->OpenMedium(Bstr(pImage->strImagePath).raw(),
3450 pImage->enmDeviceType,
3451 pImage->enmAccessType,
3452 true,
3453 ptrMedium.asOutParam());
3454 LogRelFlowFunc(("VirtualBox::openMedium -> %Rhrc\n", rc));
3455 if (SUCCEEDED(rc))
3456 {
3457 if (pImage->fMountOnly)
3458 {
3459 // mount the opened disk image
3460 rc = rPtrSessionMachine->MountMedium(Bstr(pImage->strControllerName).raw(), pImage->iPort,
3461 pImage->iDevice, ptrMedium, TRUE /*fForce*/);
3462 LogRelFlowFunc(("Machine::MountMedium -> %Rhrc\n", rc));
3463 }
3464 else
3465 {
3466 //attach the opened disk image to the controller
3467 rc = rPtrSessionMachine->AttachDevice(Bstr(pImage->strControllerName).raw(), pImage->iPort,
3468 pImage->iDevice, pImage->enmDeviceType, ptrMedium);
3469 LogRelFlowFunc(("Machine::AttachDevice -> %Rhrc\n", rc));
3470 }
3471 }
3472
3473 rLock.acquire();
3474 return rc;
3475}
3476
3477bool Unattended::i_isGuestOSArchX64(Utf8Str const &rStrGuestOsTypeId)
3478{
3479 ComPtr<IGuestOSType> pGuestOSType;
3480 HRESULT hrc = mParent->GetGuestOSType(Bstr(rStrGuestOsTypeId).raw(), pGuestOSType.asOutParam());
3481 if (SUCCEEDED(hrc))
3482 {
3483 BOOL fIs64Bit = FALSE;
3484 if (!pGuestOSType.isNull())
3485 hrc = pGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit);
3486 if (SUCCEEDED(hrc))
3487 return fIs64Bit != FALSE;
3488 }
3489 return false;
3490}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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