VirtualBox

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

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

Main/Unattended: Correction to r149171, only make detectIsoOS return E_NOTIMPL when i_innerDetectIsoOS return S_FALSE. Since the previous we return S_OK when i_innerDetectIsoOSWindows is happy with the install.wim XML parsing output. bugref:9781

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 148.5 KB
 
1/* $Id: UnattendedImpl.cpp 93585 2022-02-03 14:51:59Z vboxsync $ */
2/** @file
3 * Unattended class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.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