VirtualBox

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

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

Main/Unattended: Fix for en_windows_vista_enterprise_x64_dvd_vl_x13-17316.iso and other early images w/o DISPLAYNAME. Avoid one copy when formatting the image name. bugref:9781

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

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