VirtualBox

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

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

Main/Unattended: Extract the languages from the XML too. bugref:9781

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

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