VirtualBox

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

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

Main/Unattended: bugref:9781. Parsing XML metadata from install.wim file of Windows ISOs to glean image collection data.

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

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