VirtualBox

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

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

Main/Unattended: Set IMAGE/WINDOWS/EDITIONID (or IMAGE/FLAGS absent) as detectedOSFlavor for windows ISOs with install.wim/XML. bugref:9781

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

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