VirtualBox

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

Last change on this file since 93920 was 93589, checked in by vboxsync, 3 years ago

Main/Unattended: Detect windows server versions in parseVersionElement. bugref:9781

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