VirtualBox

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

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

Main/Unattended: Update detectedOS* attributes when setting imageIndex and we've detectedImages info handy. Ditto for prepare() and detectIsoOS(). Parse LANGUAGES from the XML data in install.xim. Stop windows detection if we seems to have found the necessary info in the XML section of install.wim. bugref:9781

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

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