VirtualBox

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

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

Main/Unattended: Made the OS/2 detection code return S_OK instead of S_FALSE for ACP2.

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

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