VirtualBox

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

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

Main/Unattended: ​​bugref:9781. Return false from IUnattended::isUnattendedSupported() for Ubuntus < 11.04.

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

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