VirtualBox

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

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

Main/Unattended: Do mixcase element searching for everything. Added OSType to WIMImage and made parseVersionElement and parseArchElement update it. bugref:9781

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

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