VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/ApplianceImplImport.cpp@ 78671

Last change on this file since 78671 was 78630, checked in by vboxsync, 6 years ago

bugref:9416. small leftover removed.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 217.9 KB
Line 
1/* $Id: ApplianceImplImport.cpp 78630 2019-05-21 13:09:47Z vboxsync $ */
2/** @file
3 * IAppliance and IVirtualSystem COM class implementations.
4 */
5
6/*
7 * Copyright (C) 2008-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.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#define LOG_GROUP LOG_GROUP_MAIN_APPLIANCE
19#include <iprt/alloca.h>
20#include <iprt/path.h>
21#include <iprt/cpp/path.h>
22#include <iprt/dir.h>
23#include <iprt/file.h>
24#include <iprt/s3.h>
25#include <iprt/sha.h>
26#include <iprt/manifest.h>
27#include <iprt/tar.h>
28#include <iprt/zip.h>
29#include <iprt/stream.h>
30#include <iprt/crypto/digest.h>
31#include <iprt/crypto/pkix.h>
32#include <iprt/crypto/store.h>
33#include <iprt/crypto/x509.h>
34
35#include <VBox/vd.h>
36#include <VBox/com/array.h>
37
38#include "ApplianceImpl.h"
39#include "VirtualBoxImpl.h"
40#include "GuestOSTypeImpl.h"
41#include "ProgressImpl.h"
42#include "MachineImpl.h"
43#include "MediumImpl.h"
44#include "MediumFormatImpl.h"
45#include "SystemPropertiesImpl.h"
46#include "HostImpl.h"
47
48#include "AutoCaller.h"
49#include "LoggingNew.h"
50
51#include "ApplianceImplPrivate.h"
52#include "CertificateImpl.h"
53#include "ovfreader.h"
54
55#include <VBox/param.h>
56#include <VBox/version.h>
57#include <VBox/settings.h>
58
59#include <set>
60
61using namespace std;
62
63////////////////////////////////////////////////////////////////////////////////
64//
65// IAppliance public methods
66//
67////////////////////////////////////////////////////////////////////////////////
68
69/**
70 * Public method implementation. This opens the OVF with ovfreader.cpp.
71 * Thread implementation is in Appliance::readImpl().
72 *
73 * @param aFile File to read the appliance from.
74 * @param aProgress Progress object.
75 * @return
76 */
77HRESULT Appliance::read(const com::Utf8Str &aFile,
78 ComPtr<IProgress> &aProgress)
79{
80 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
81
82 if (!i_isApplianceIdle())
83 return E_ACCESSDENIED;
84
85 if (m->pReader)
86 {
87 delete m->pReader;
88 m->pReader = NULL;
89 }
90
91 ComObjPtr<Progress> progress;
92 try
93 {
94 /* Parse all necessary info out of the URI */
95 i_parseURI(aFile, m->locInfo);
96
97 // see if we can handle this file; for now we insist it has an ovf/ova extension
98 if ( m->locInfo.storageType == VFSType_File
99 && !aFile.endsWith(".ovf", Utf8Str::CaseInsensitive)
100 && !aFile.endsWith(".ova", Utf8Str::CaseInsensitive))
101 return setError(VBOX_E_FILE_ERROR, tr("Appliance file must have .ovf or .ova extension"));
102
103 i_readImpl(m->locInfo, progress);
104 }
105 catch (HRESULT aRC)
106 {
107 return aRC;
108 }
109
110 /* Return progress to the caller */
111 progress.queryInterfaceTo(aProgress.asOutParam());
112 return S_OK;
113}
114
115/**
116 * Public method implementation. This looks at the output of ovfreader.cpp and creates
117 * VirtualSystemDescription instances.
118 * @return
119 */
120HRESULT Appliance::interpret()
121{
122 /// @todo
123 // - don't use COM methods but the methods directly (faster, but needs appropriate
124 // locking of that objects itself (s. HardDisk))
125 // - Appropriate handle errors like not supported file formats
126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
127
128 if (!i_isApplianceIdle())
129 return E_ACCESSDENIED;
130
131 HRESULT rc = S_OK;
132
133 /* Clear any previous virtual system descriptions */
134 m->virtualSystemDescriptions.clear();
135
136 if (!m->pReader)
137 return setError(E_FAIL,
138 tr("Cannot interpret appliance without reading it first (call read() before interpret())"));
139
140 // Change the appliance state so we can safely leave the lock while doing time-consuming
141 // medium imports; also the below method calls do all kinds of locking which conflicts with
142 // the appliance object lock
143 m->state = Data::ApplianceImporting;
144 alock.release();
145
146 /* Try/catch so we can clean up on error */
147 try
148 {
149 list<ovf::VirtualSystem>::const_iterator it;
150 /* Iterate through all virtual systems */
151 for (it = m->pReader->m_llVirtualSystems.begin();
152 it != m->pReader->m_llVirtualSystems.end();
153 ++it)
154 {
155 const ovf::VirtualSystem &vsysThis = *it;
156
157 ComObjPtr<VirtualSystemDescription> pNewDesc;
158 rc = pNewDesc.createObject();
159 if (FAILED(rc)) throw rc;
160 rc = pNewDesc->init();
161 if (FAILED(rc)) throw rc;
162
163 // if the virtual system in OVF had a <vbox:Machine> element, have the
164 // VirtualBox settings code parse that XML now
165 if (vsysThis.pelmVBoxMachine)
166 pNewDesc->i_importVBoxMachineXML(*vsysThis.pelmVBoxMachine);
167
168 // Guest OS type
169 // This is taken from one of three places, in this order:
170 Utf8Str strOsTypeVBox;
171 Utf8StrFmt strCIMOSType("%RU32", (uint32_t)vsysThis.cimos);
172 // 1) If there is a <vbox:Machine>, then use the type from there.
173 if ( vsysThis.pelmVBoxMachine
174 && pNewDesc->m->pConfig->machineUserData.strOsType.isNotEmpty()
175 )
176 strOsTypeVBox = pNewDesc->m->pConfig->machineUserData.strOsType;
177 // 2) Otherwise, if there is OperatingSystemSection/vbox:OSType, use that one.
178 else if (vsysThis.strTypeVBox.isNotEmpty()) // OVFReader has found vbox:OSType
179 strOsTypeVBox = vsysThis.strTypeVBox;
180 // 3) Otherwise, make a best guess what the vbox type is from the OVF (CIM) OS type.
181 else
182 convertCIMOSType2VBoxOSType(strOsTypeVBox, vsysThis.cimos, vsysThis.strCimosDesc);
183 pNewDesc->i_addEntry(VirtualSystemDescriptionType_OS,
184 "",
185 strCIMOSType,
186 strOsTypeVBox);
187
188 /* VM name */
189 Utf8Str nameVBox;
190 /* If there is a <vbox:Machine>, we always prefer the setting from there. */
191 if ( vsysThis.pelmVBoxMachine
192 && pNewDesc->m->pConfig->machineUserData.strName.isNotEmpty())
193 nameVBox = pNewDesc->m->pConfig->machineUserData.strName;
194 else
195 nameVBox = vsysThis.strName;
196 /* If there isn't any name specified create a default one out
197 * of the OS type */
198 if (nameVBox.isEmpty())
199 nameVBox = strOsTypeVBox;
200 i_searchUniqueVMName(nameVBox);
201 pNewDesc->i_addEntry(VirtualSystemDescriptionType_Name,
202 "",
203 vsysThis.strName,
204 nameVBox);
205
206 /* VM Primary Group */
207 Utf8Str strPrimaryGroup;
208 if ( vsysThis.pelmVBoxMachine
209 && pNewDesc->m->pConfig->machineUserData.llGroups.size())
210 strPrimaryGroup = pNewDesc->m->pConfig->machineUserData.llGroups.front();
211 if (strPrimaryGroup.isEmpty())
212 strPrimaryGroup = "/";
213 pNewDesc->i_addEntry(VirtualSystemDescriptionType_PrimaryGroup,
214 "",
215 "" /* no direct OVF correspondence */,
216 strPrimaryGroup);
217
218 /* Based on the VM name, create a target machine path. */
219 Bstr bstrSettingsFilename;
220 rc = mVirtualBox->ComposeMachineFilename(Bstr(nameVBox).raw(),
221 Bstr(strPrimaryGroup).raw(),
222 NULL /* aCreateFlags */,
223 NULL /* aBaseFolder */,
224 bstrSettingsFilename.asOutParam());
225 if (FAILED(rc)) throw rc;
226 Utf8Str strMachineFolder(bstrSettingsFilename);
227 strMachineFolder.stripFilename();
228
229#if 1
230 /* The import logic should work exactly the same whether the
231 * following 2 items are present or not, but of course it may have
232 * an influence on the exact presentation of the import settings
233 * of an API client. */
234 Utf8Str strSettingsFilename(bstrSettingsFilename);
235 pNewDesc->i_addEntry(VirtualSystemDescriptionType_SettingsFile,
236 "",
237 "" /* no direct OVF correspondence */,
238 strSettingsFilename);
239 Utf8Str strBaseFolder;
240 mVirtualBox->i_getDefaultMachineFolder(strBaseFolder);
241 pNewDesc->i_addEntry(VirtualSystemDescriptionType_BaseFolder,
242 "",
243 "" /* no direct OVF correspondence */,
244 strBaseFolder);
245#endif
246
247 /* VM Product */
248 if (!vsysThis.strProduct.isEmpty())
249 pNewDesc->i_addEntry(VirtualSystemDescriptionType_Product,
250 "",
251 vsysThis.strProduct,
252 vsysThis.strProduct);
253
254 /* VM Vendor */
255 if (!vsysThis.strVendor.isEmpty())
256 pNewDesc->i_addEntry(VirtualSystemDescriptionType_Vendor,
257 "",
258 vsysThis.strVendor,
259 vsysThis.strVendor);
260
261 /* VM Version */
262 if (!vsysThis.strVersion.isEmpty())
263 pNewDesc->i_addEntry(VirtualSystemDescriptionType_Version,
264 "",
265 vsysThis.strVersion,
266 vsysThis.strVersion);
267
268 /* VM ProductUrl */
269 if (!vsysThis.strProductUrl.isEmpty())
270 pNewDesc->i_addEntry(VirtualSystemDescriptionType_ProductUrl,
271 "",
272 vsysThis.strProductUrl,
273 vsysThis.strProductUrl);
274
275 /* VM VendorUrl */
276 if (!vsysThis.strVendorUrl.isEmpty())
277 pNewDesc->i_addEntry(VirtualSystemDescriptionType_VendorUrl,
278 "",
279 vsysThis.strVendorUrl,
280 vsysThis.strVendorUrl);
281
282 /* VM description */
283 if (!vsysThis.strDescription.isEmpty())
284 pNewDesc->i_addEntry(VirtualSystemDescriptionType_Description,
285 "",
286 vsysThis.strDescription,
287 vsysThis.strDescription);
288
289 /* VM license */
290 if (!vsysThis.strLicenseText.isEmpty())
291 pNewDesc->i_addEntry(VirtualSystemDescriptionType_License,
292 "",
293 vsysThis.strLicenseText,
294 vsysThis.strLicenseText);
295
296 /* Now that we know the OS type, get our internal defaults based on
297 * that, if it is known (otherwise pGuestOSType will be NULL). */
298 ComPtr<IGuestOSType> pGuestOSType;
299 mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox).raw(), pGuestOSType.asOutParam());
300
301 /* CPU count */
302 ULONG cpuCountVBox;
303 /* If there is a <vbox:Machine>, we always prefer the setting from there. */
304 if ( vsysThis.pelmVBoxMachine
305 && pNewDesc->m->pConfig->hardwareMachine.cCPUs)
306 cpuCountVBox = pNewDesc->m->pConfig->hardwareMachine.cCPUs;
307 else
308 cpuCountVBox = vsysThis.cCPUs;
309 /* Check for the constraints */
310 if (cpuCountVBox > SchemaDefs::MaxCPUCount)
311 {
312 i_addWarning(tr("The virtual system \"%s\" claims support for %u CPU's, but VirtualBox has support for "
313 "max %u CPU's only."),
314 vsysThis.strName.c_str(), cpuCountVBox, SchemaDefs::MaxCPUCount);
315 cpuCountVBox = SchemaDefs::MaxCPUCount;
316 }
317 if (vsysThis.cCPUs == 0)
318 cpuCountVBox = 1;
319 pNewDesc->i_addEntry(VirtualSystemDescriptionType_CPU,
320 "",
321 Utf8StrFmt("%RU32", (uint32_t)vsysThis.cCPUs),
322 Utf8StrFmt("%RU32", (uint32_t)cpuCountVBox));
323
324 /* RAM */
325 uint64_t ullMemSizeVBox;
326 /* If there is a <vbox:Machine>, we always prefer the setting from there. */
327 if ( vsysThis.pelmVBoxMachine
328 && pNewDesc->m->pConfig->hardwareMachine.ulMemorySizeMB)
329 ullMemSizeVBox = pNewDesc->m->pConfig->hardwareMachine.ulMemorySizeMB;
330 else
331 ullMemSizeVBox = vsysThis.ullMemorySize / _1M;
332 /* Check for the constraints */
333 if ( ullMemSizeVBox != 0
334 && ( ullMemSizeVBox < MM_RAM_MIN_IN_MB
335 || ullMemSizeVBox > MM_RAM_MAX_IN_MB
336 )
337 )
338 {
339 i_addWarning(tr("The virtual system \"%s\" claims support for %llu MB RAM size, but VirtualBox has "
340 "support for min %u & max %u MB RAM size only."),
341 vsysThis.strName.c_str(), ullMemSizeVBox, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
342 ullMemSizeVBox = RT_MIN(RT_MAX(ullMemSizeVBox, MM_RAM_MIN_IN_MB), MM_RAM_MAX_IN_MB);
343 }
344 if (vsysThis.ullMemorySize == 0)
345 {
346 /* If the RAM of the OVF is zero, use our predefined values */
347 ULONG memSizeVBox2;
348 if (!pGuestOSType.isNull())
349 {
350 rc = pGuestOSType->COMGETTER(RecommendedRAM)(&memSizeVBox2);
351 if (FAILED(rc)) throw rc;
352 }
353 else
354 memSizeVBox2 = 1024;
355 /* VBox stores that in MByte */
356 ullMemSizeVBox = (uint64_t)memSizeVBox2;
357 }
358 pNewDesc->i_addEntry(VirtualSystemDescriptionType_Memory,
359 "",
360 Utf8StrFmt("%RU64", (uint64_t)vsysThis.ullMemorySize),
361 Utf8StrFmt("%RU64", (uint64_t)ullMemSizeVBox));
362
363 /* Audio */
364 Utf8Str strSoundCard;
365 Utf8Str strSoundCardOrig;
366 /* If there is a <vbox:Machine>, we always prefer the setting from there. */
367 if ( vsysThis.pelmVBoxMachine
368 && pNewDesc->m->pConfig->hardwareMachine.audioAdapter.fEnabled)
369 {
370 strSoundCard = Utf8StrFmt("%RU32",
371 (uint32_t)pNewDesc->m->pConfig->hardwareMachine.audioAdapter.controllerType);
372 }
373 else if (vsysThis.strSoundCardType.isNotEmpty())
374 {
375 /* Set the AC97 always for the simple OVF case.
376 * @todo: figure out the hardware which could be possible */
377 strSoundCard = Utf8StrFmt("%RU32", (uint32_t)AudioControllerType_AC97);
378 strSoundCardOrig = vsysThis.strSoundCardType;
379 }
380 if (strSoundCard.isNotEmpty())
381 pNewDesc->i_addEntry(VirtualSystemDescriptionType_SoundCard,
382 "",
383 strSoundCardOrig,
384 strSoundCard);
385
386#ifdef VBOX_WITH_USB
387 /* USB Controller */
388 /* If there is a <vbox:Machine>, we always prefer the setting from there. */
389 if ( ( vsysThis.pelmVBoxMachine
390 && pNewDesc->m->pConfig->hardwareMachine.usbSettings.llUSBControllers.size() > 0)
391 || vsysThis.fHasUsbController)
392 pNewDesc->i_addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
393#endif /* VBOX_WITH_USB */
394
395 /* Network Controller */
396 /* If there is a <vbox:Machine>, we always prefer the setting from there. */
397 if (vsysThis.pelmVBoxMachine)
398 {
399 uint32_t maxNetworkAdapters = Global::getMaxNetworkAdapters(pNewDesc->m->pConfig->hardwareMachine.chipsetType);
400
401 const settings::NetworkAdaptersList &llNetworkAdapters = pNewDesc->m->pConfig->hardwareMachine.llNetworkAdapters;
402 /* Check for the constrains */
403 if (llNetworkAdapters.size() > maxNetworkAdapters)
404 i_addWarning(tr("The virtual system \"%s\" claims support for %zu network adapters, but VirtualBox "
405 "has support for max %u network adapter only."),
406 vsysThis.strName.c_str(), llNetworkAdapters.size(), maxNetworkAdapters);
407 /* Iterate through all network adapters. */
408 settings::NetworkAdaptersList::const_iterator it1;
409 size_t a = 0;
410 for (it1 = llNetworkAdapters.begin();
411 it1 != llNetworkAdapters.end() && a < maxNetworkAdapters;
412 ++it1, ++a)
413 {
414 if (it1->fEnabled)
415 {
416 Utf8Str strMode = convertNetworkAttachmentTypeToString(it1->mode);
417 pNewDesc->i_addEntry(VirtualSystemDescriptionType_NetworkAdapter,
418 "", // ref
419 strMode, // orig
420 Utf8StrFmt("%RU32", (uint32_t)it1->type), // conf
421 0,
422 Utf8StrFmt("slot=%RU32;type=%s", it1->ulSlot, strMode.c_str())); // extra conf
423 }
424 }
425 }
426 /* else we use the ovf configuration. */
427 else if (vsysThis.llEthernetAdapters.size() > 0)
428 {
429 size_t cEthernetAdapters = vsysThis.llEthernetAdapters.size();
430 uint32_t maxNetworkAdapters = Global::getMaxNetworkAdapters(ChipsetType_PIIX3);
431
432 /* Check for the constrains */
433 if (cEthernetAdapters > maxNetworkAdapters)
434 i_addWarning(tr("The virtual system \"%s\" claims support for %zu network adapters, but VirtualBox "
435 "has support for max %u network adapter only."),
436 vsysThis.strName.c_str(), cEthernetAdapters, maxNetworkAdapters);
437
438 /* Get the default network adapter type for the selected guest OS */
439 NetworkAdapterType_T defaultAdapterVBox = NetworkAdapterType_Am79C970A;
440 if (!pGuestOSType.isNull())
441 {
442 rc = pGuestOSType->COMGETTER(AdapterType)(&defaultAdapterVBox);
443 if (FAILED(rc)) throw rc;
444 }
445 else
446 {
447#ifdef VBOX_WITH_E1000
448 defaultAdapterVBox = NetworkAdapterType_I82540EM;
449#else
450 defaultAdapterVBox = NetworkAdapterType_Am79C973A;
451#endif
452 }
453
454 ovf::EthernetAdaptersList::const_iterator itEA;
455 /* Iterate through all abstract networks. Ignore network cards
456 * which exceed the limit of VirtualBox. */
457 size_t a = 0;
458 for (itEA = vsysThis.llEthernetAdapters.begin();
459 itEA != vsysThis.llEthernetAdapters.end() && a < maxNetworkAdapters;
460 ++itEA, ++a)
461 {
462 const ovf::EthernetAdapter &ea = *itEA; // logical network to connect to
463 Utf8Str strNetwork = ea.strNetworkName;
464 // make sure it's one of these two
465 if ( (strNetwork.compare("Null", Utf8Str::CaseInsensitive))
466 && (strNetwork.compare("NAT", Utf8Str::CaseInsensitive))
467 && (strNetwork.compare("Bridged", Utf8Str::CaseInsensitive))
468 && (strNetwork.compare("Internal", Utf8Str::CaseInsensitive))
469 && (strNetwork.compare("HostOnly", Utf8Str::CaseInsensitive))
470 && (strNetwork.compare("Generic", Utf8Str::CaseInsensitive))
471 )
472 strNetwork = "Bridged"; // VMware assumes this is the default apparently
473
474 /* Figure out the hardware type */
475 NetworkAdapterType_T nwAdapterVBox = defaultAdapterVBox;
476 if (!ea.strAdapterType.compare("PCNet32", Utf8Str::CaseInsensitive))
477 {
478 /* If the default adapter is already one of the two
479 * PCNet adapters use the default one. If not use the
480 * Am79C970A as fallback. */
481 if (!(defaultAdapterVBox == NetworkAdapterType_Am79C970A ||
482 defaultAdapterVBox == NetworkAdapterType_Am79C973))
483 nwAdapterVBox = NetworkAdapterType_Am79C970A;
484 }
485#ifdef VBOX_WITH_E1000
486 /* VMWare accidentally write this with VirtualCenter 3.5,
487 so make sure in this case always to use the VMWare one */
488 else if (!ea.strAdapterType.compare("E10000", Utf8Str::CaseInsensitive))
489 nwAdapterVBox = NetworkAdapterType_I82545EM;
490 else if (!ea.strAdapterType.compare("E1000", Utf8Str::CaseInsensitive))
491 {
492 /* Check if this OVF was written by VirtualBox */
493 if (Utf8Str(vsysThis.strVirtualSystemType).contains("virtualbox", Utf8Str::CaseInsensitive))
494 {
495 /* If the default adapter is already one of the three
496 * E1000 adapters use the default one. If not use the
497 * I82545EM as fallback. */
498 if (!(defaultAdapterVBox == NetworkAdapterType_I82540EM ||
499 defaultAdapterVBox == NetworkAdapterType_I82543GC ||
500 defaultAdapterVBox == NetworkAdapterType_I82545EM))
501 nwAdapterVBox = NetworkAdapterType_I82540EM;
502 }
503 else
504 /* Always use this one since it's what VMware uses */
505 nwAdapterVBox = NetworkAdapterType_I82545EM;
506 }
507#endif /* VBOX_WITH_E1000 */
508
509 pNewDesc->i_addEntry(VirtualSystemDescriptionType_NetworkAdapter,
510 "", // ref
511 ea.strNetworkName, // orig
512 Utf8StrFmt("%RU32", (uint32_t)nwAdapterVBox), // conf
513 0,
514 Utf8StrFmt("type=%s", strNetwork.c_str())); // extra conf
515 }
516 }
517
518 /* If there is a <vbox:Machine>, we always prefer the setting from there. */
519 bool fFloppy = false;
520 bool fDVD = false;
521 if (vsysThis.pelmVBoxMachine)
522 {
523 settings::StorageControllersList &llControllers = pNewDesc->m->pConfig->hardwareMachine.storage.llStorageControllers;
524 settings::StorageControllersList::iterator it3;
525 for (it3 = llControllers.begin();
526 it3 != llControllers.end();
527 ++it3)
528 {
529 settings::AttachedDevicesList &llAttachments = it3->llAttachedDevices;
530 settings::AttachedDevicesList::iterator it4;
531 for (it4 = llAttachments.begin();
532 it4 != llAttachments.end();
533 ++it4)
534 {
535 fDVD |= it4->deviceType == DeviceType_DVD;
536 fFloppy |= it4->deviceType == DeviceType_Floppy;
537 if (fFloppy && fDVD)
538 break;
539 }
540 if (fFloppy && fDVD)
541 break;
542 }
543 }
544 else
545 {
546 fFloppy = vsysThis.fHasFloppyDrive;
547 fDVD = vsysThis.fHasCdromDrive;
548 }
549 /* Floppy Drive */
550 if (fFloppy)
551 pNewDesc->i_addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");
552 /* CD Drive */
553 if (fDVD)
554 pNewDesc->i_addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");
555
556 /* Storage Controller */
557 uint16_t cIDEused = 0;
558 uint16_t cSATAused = 0; NOREF(cSATAused);
559 uint16_t cSCSIused = 0; NOREF(cSCSIused);
560 ovf::ControllersMap::const_iterator hdcIt;
561 /* Iterate through all storage controllers */
562 for (hdcIt = vsysThis.mapControllers.begin();
563 hdcIt != vsysThis.mapControllers.end();
564 ++hdcIt)
565 {
566 const ovf::HardDiskController &hdc = hdcIt->second;
567 Utf8Str strControllerID = Utf8StrFmt("%RI32", (uint32_t)hdc.idController);
568
569 switch (hdc.system)
570 {
571 case ovf::HardDiskController::IDE:
572 /* Check for the constrains */
573 if (cIDEused < 4)
574 {
575 /// @todo figure out the IDE types
576 /* Use PIIX4 as default */
577 Utf8Str strType = "PIIX4";
578 if (!hdc.strControllerType.compare("PIIX3", Utf8Str::CaseInsensitive))
579 strType = "PIIX3";
580 else if (!hdc.strControllerType.compare("ICH6", Utf8Str::CaseInsensitive))
581 strType = "ICH6";
582 pNewDesc->i_addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
583 strControllerID, // strRef
584 hdc.strControllerType, // aOvfValue
585 strType); // aVBoxValue
586 }
587 else
588 /* Warn only once */
589 if (cIDEused == 2)
590 i_addWarning(tr("The virtual \"%s\" system requests support for more than two "
591 "IDE controller channels, but VirtualBox supports only two."),
592 vsysThis.strName.c_str());
593
594 ++cIDEused;
595 break;
596
597 case ovf::HardDiskController::SATA:
598 /* Check for the constrains */
599 if (cSATAused < 1)
600 {
601 /// @todo figure out the SATA types
602 /* We only support a plain AHCI controller, so use them always */
603 pNewDesc->i_addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
604 strControllerID,
605 hdc.strControllerType,
606 "AHCI");
607 }
608 else
609 {
610 /* Warn only once */
611 if (cSATAused == 1)
612 i_addWarning(tr("The virtual system \"%s\" requests support for more than one "
613 "SATA controller, but VirtualBox has support for only one"),
614 vsysThis.strName.c_str());
615
616 }
617 ++cSATAused;
618 break;
619
620 case ovf::HardDiskController::SCSI:
621 /* Check for the constrains */
622 if (cSCSIused < 1)
623 {
624 VirtualSystemDescriptionType_T vsdet = VirtualSystemDescriptionType_HardDiskControllerSCSI;
625 Utf8Str hdcController = "LsiLogic";
626 if (!hdc.strControllerType.compare("lsilogicsas", Utf8Str::CaseInsensitive))
627 {
628 // OVF considers SAS a variant of SCSI but VirtualBox considers it a class of its own
629 vsdet = VirtualSystemDescriptionType_HardDiskControllerSAS;
630 hdcController = "LsiLogicSas";
631 }
632 else if (!hdc.strControllerType.compare("BusLogic", Utf8Str::CaseInsensitive))
633 hdcController = "BusLogic";
634 pNewDesc->i_addEntry(vsdet,
635 strControllerID,
636 hdc.strControllerType,
637 hdcController);
638 }
639 else
640 i_addWarning(tr("The virtual system \"%s\" requests support for an additional "
641 "SCSI controller of type \"%s\" with ID %s, but VirtualBox presently "
642 "supports only one SCSI controller."),
643 vsysThis.strName.c_str(),
644 hdc.strControllerType.c_str(),
645 strControllerID.c_str());
646 ++cSCSIused;
647 break;
648 }
649 }
650
651 /* Storage devices (hard disks/DVDs/...) */
652 if (vsysThis.mapVirtualDisks.size() > 0)
653 {
654 ovf::VirtualDisksMap::const_iterator itVD;
655 /* Iterate through all storage devices */
656 for (itVD = vsysThis.mapVirtualDisks.begin();
657 itVD != vsysThis.mapVirtualDisks.end();
658 ++itVD)
659 {
660 const ovf::VirtualDisk &hd = itVD->second;
661 /* Get the associated image */
662 ovf::DiskImage di;
663 std::map<RTCString, ovf::DiskImage>::iterator foundDisk;
664
665 foundDisk = m->pReader->m_mapDisks.find(hd.strDiskId);
666 if (foundDisk == m->pReader->m_mapDisks.end())
667 continue;
668 else
669 {
670 di = foundDisk->second;
671 }
672
673 /*
674 * Figure out from URI which format the image has.
675 * There is no strict mapping of image URI to image format.
676 * It's possible we aren't able to recognize some URIs.
677 */
678
679 ComObjPtr<MediumFormat> mediumFormat;
680 rc = i_findMediumFormatFromDiskImage(di, mediumFormat);
681 if (FAILED(rc))
682 throw rc;
683
684 Bstr bstrFormatName;
685 rc = mediumFormat->COMGETTER(Name)(bstrFormatName.asOutParam());
686 if (FAILED(rc))
687 throw rc;
688 Utf8Str vdf = Utf8Str(bstrFormatName);
689
690 /// @todo
691 // - figure out all possible vmdk formats we also support
692 // - figure out if there is a url specifier for vhd already
693 // - we need a url specifier for the vdi format
694
695 Utf8Str strFilename = di.strHref;
696 DeviceType_T devType = DeviceType_Null;
697 if (vdf.compare("VMDK", Utf8Str::CaseInsensitive) == 0)
698 {
699 /* If the href is empty use the VM name as filename */
700 if (!strFilename.length())
701 strFilename = Utf8StrFmt("%s.vmdk", hd.strDiskId.c_str());
702 devType = DeviceType_HardDisk;
703 }
704 else if (vdf.compare("RAW", Utf8Str::CaseInsensitive) == 0)
705 {
706 /* If the href is empty use the VM name as filename */
707 if (!strFilename.length())
708 strFilename = Utf8StrFmt("%s.iso", hd.strDiskId.c_str());
709 devType = DeviceType_DVD;
710 }
711 else
712 throw setError(VBOX_E_FILE_ERROR,
713 tr("Unsupported format for virtual disk image %s in OVF: \"%s\""),
714 di.strHref.c_str(),
715 di.strFormat.c_str());
716
717 /*
718 * Remove last extension from the file name if the file is compressed
719 */
720 if (di.strCompression.compare("gzip", Utf8Str::CaseInsensitive)==0)
721 strFilename.stripSuffix();
722
723 i_searchUniqueImageFilePath(strMachineFolder, devType, strFilename); /** @todo check the return code! */
724
725 /* find the description for the storage controller
726 * that has the same ID as hd.idController */
727 const VirtualSystemDescriptionEntry *pController;
728 if (!(pController = pNewDesc->i_findControllerFromID(hd.idController)))
729 throw setError(E_FAIL,
730 tr("Cannot find storage controller with OVF instance ID %RI32 "
731 "to which medium \"%s\" should be attached"),
732 hd.idController,
733 di.strHref.c_str());
734
735 /* controller to attach to, and the bus within that controller */
736 Utf8StrFmt strExtraConfig("controller=%RI16;channel=%RI16",
737 pController->ulIndex,
738 hd.ulAddressOnParent);
739 pNewDesc->i_addEntry(VirtualSystemDescriptionType_HardDiskImage,
740 hd.strDiskId,
741 di.strHref,
742 strFilename,
743 di.ulSuggestedSizeMB,
744 strExtraConfig);
745 }
746 }
747
748 m->virtualSystemDescriptions.push_back(pNewDesc);
749 }
750 }
751 catch (HRESULT aRC)
752 {
753 /* On error we clear the list & return */
754 m->virtualSystemDescriptions.clear();
755 rc = aRC;
756 }
757
758 // reset the appliance state
759 alock.acquire();
760 m->state = Data::ApplianceIdle;
761
762 return rc;
763}
764
765/**
766 * Public method implementation. This creates one or more new machines according to the
767 * VirtualSystemScription instances created by Appliance::Interpret().
768 * Thread implementation is in Appliance::i_importImpl().
769 * @param aOptions Import options.
770 * @param aProgress Progress object.
771 * @return
772 */
773HRESULT Appliance::importMachines(const std::vector<ImportOptions_T> &aOptions,
774 ComPtr<IProgress> &aProgress)
775{
776 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
777
778 if (aOptions.size())
779 {
780 m->optListImport.setCapacity(aOptions.size());
781 for (size_t i = 0; i < aOptions.size(); ++i)
782 {
783 m->optListImport.insert(i, aOptions[i]);
784 }
785 }
786
787 AssertReturn(!( m->optListImport.contains(ImportOptions_KeepAllMACs)
788 && m->optListImport.contains(ImportOptions_KeepNATMACs) )
789 , E_INVALIDARG);
790
791 // do not allow entering this method if the appliance is busy reading or writing
792 if (!i_isApplianceIdle())
793 return E_ACCESSDENIED;
794
795 //check for the local import only. For import from the Cloud m->pReader is always NULL.
796 if (m->locInfo.storageType == VFSType_File && !m->pReader)
797 return setError(E_FAIL,
798 tr("Cannot import machines without reading it first (call read() before i_importMachines())"));
799
800 ComObjPtr<Progress> progress;
801 HRESULT rc = S_OK;
802 try
803 {
804 rc = i_importImpl(m->locInfo, progress);
805 }
806 catch (HRESULT aRC)
807 {
808 rc = aRC;
809 }
810
811 if (SUCCEEDED(rc))
812 /* Return progress to the caller */
813 progress.queryInterfaceTo(aProgress.asOutParam());
814
815 return rc;
816}
817
818////////////////////////////////////////////////////////////////////////////////
819//
820// Appliance private methods
821//
822////////////////////////////////////////////////////////////////////////////////
823
824/**
825 * Ensures that there is a look-ahead object ready.
826 *
827 * @returns true if there's an object handy, false if end-of-stream.
828 * @throws HRESULT if the next object isn't a regular file. Sets error info
829 * (which is why it's a method on Appliance and not the
830 * ImportStack).
831 */
832bool Appliance::i_importEnsureOvaLookAhead(ImportStack &stack)
833{
834 Assert(stack.hVfsFssOva != NULL);
835 if (stack.hVfsIosOvaLookAhead == NIL_RTVFSIOSTREAM)
836 {
837 RTStrFree(stack.pszOvaLookAheadName);
838 stack.pszOvaLookAheadName = NULL;
839
840 RTVFSOBJTYPE enmType;
841 RTVFSOBJ hVfsObj;
842 int vrc = RTVfsFsStrmNext(stack.hVfsFssOva, &stack.pszOvaLookAheadName, &enmType, &hVfsObj);
843 if (RT_SUCCESS(vrc))
844 {
845 stack.hVfsIosOvaLookAhead = RTVfsObjToIoStream(hVfsObj);
846 RTVfsObjRelease(hVfsObj);
847 if ( ( enmType != RTVFSOBJTYPE_FILE
848 && enmType != RTVFSOBJTYPE_IO_STREAM)
849 || stack.hVfsIosOvaLookAhead == NIL_RTVFSIOSTREAM)
850 throw setError(VBOX_E_FILE_ERROR,
851 tr("Malformed OVA. '%s' is not a regular file (%d)."), stack.pszOvaLookAheadName, enmType);
852 }
853 else if (vrc == VERR_EOF)
854 return false;
855 else
856 throw setErrorVrc(vrc, tr("RTVfsFsStrmNext failed (%Rrc)"), vrc);
857 }
858 return true;
859}
860
861HRESULT Appliance::i_preCheckImageAvailability(ImportStack &stack)
862{
863 if (i_importEnsureOvaLookAhead(stack))
864 return S_OK;
865 throw setError(VBOX_E_FILE_ERROR, tr("Unexpected end of OVA package"));
866 /** @todo r=bird: dunno why this bother returning a value and the caller
867 * having a special 'continue' case for it. It always threw all non-OK
868 * status codes. It's possibly to handle out of order stuff, so that
869 * needs adding to the testcase! */
870}
871
872/**
873 * Opens a source file (for reading obviously).
874 *
875 * @param stack
876 * @param rstrSrcPath The source file to open.
877 * @param pszManifestEntry The manifest entry of the source file. This is
878 * used when constructing our manifest using a pass
879 * thru.
880 * @returns I/O stream handle to the source file.
881 * @throws HRESULT error status, error info set.
882 */
883RTVFSIOSTREAM Appliance::i_importOpenSourceFile(ImportStack &stack, Utf8Str const &rstrSrcPath, const char *pszManifestEntry)
884{
885 /*
886 * Open the source file. Special considerations for OVAs.
887 */
888 RTVFSIOSTREAM hVfsIosSrc;
889 if (stack.hVfsFssOva != NIL_RTVFSFSSTREAM)
890 {
891 for (uint32_t i = 0;; i++)
892 {
893 if (!i_importEnsureOvaLookAhead(stack))
894 throw setErrorBoth(VBOX_E_FILE_ERROR, VERR_EOF,
895 tr("Unexpected end of OVA / internal error - missing '%s' (skipped %u)"),
896 rstrSrcPath.c_str(), i);
897 if (RTStrICmp(stack.pszOvaLookAheadName, rstrSrcPath.c_str()) == 0)
898 break;
899
900 /* release the current object, loop to get the next. */
901 RTVfsIoStrmRelease(stack.claimOvaLookAHead());
902 }
903 hVfsIosSrc = stack.claimOvaLookAHead();
904 }
905 else
906 {
907 int vrc = RTVfsIoStrmOpenNormal(rstrSrcPath.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosSrc);
908 if (RT_FAILURE(vrc))
909 throw setErrorVrc(vrc, tr("Error opening '%s' for reading (%Rrc)"), rstrSrcPath.c_str(), vrc);
910 }
911
912 /*
913 * Digest calculation filtering.
914 */
915 hVfsIosSrc = i_manifestSetupDigestCalculationForGivenIoStream(hVfsIosSrc, pszManifestEntry);
916 if (hVfsIosSrc == NIL_RTVFSIOSTREAM)
917 throw E_FAIL;
918
919 return hVfsIosSrc;
920}
921
922/**
923 * Creates the destination file and fills it with bytes from the source stream.
924 *
925 * This assumes that we digest the source when fDigestTypes is non-zero, and
926 * thus calls RTManifestPtIosAddEntryNow when done.
927 *
928 * @param rstrDstPath The path to the destination file. Missing path
929 * components will be created.
930 * @param hVfsIosSrc The source I/O stream.
931 * @param rstrSrcLogNm The name of the source for logging and error
932 * messages.
933 * @returns COM status code.
934 * @throws Nothing (as the caller has VFS handles to release).
935 */
936HRESULT Appliance::i_importCreateAndWriteDestinationFile(Utf8Str const &rstrDstPath, RTVFSIOSTREAM hVfsIosSrc,
937 Utf8Str const &rstrSrcLogNm)
938{
939 int vrc;
940
941 /*
942 * Create the output file, including necessary paths.
943 * Any existing file will be overwritten.
944 */
945 HRESULT hrc = VirtualBox::i_ensureFilePathExists(rstrDstPath, true /*fCreate*/);
946 if (SUCCEEDED(hrc))
947 {
948 RTVFSIOSTREAM hVfsIosDst;
949 vrc = RTVfsIoStrmOpenNormal(rstrDstPath.c_str(),
950 RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_ALL,
951 &hVfsIosDst);
952 if (RT_SUCCESS(vrc))
953 {
954 /*
955 * Pump the bytes thru. If we fail, delete the output file.
956 */
957 vrc = RTVfsUtilPumpIoStreams(hVfsIosSrc, hVfsIosDst, 0);
958 if (RT_SUCCESS(vrc))
959 hrc = S_OK;
960 else
961 hrc = setErrorVrc(vrc, tr("Error occured decompressing '%s' to '%s' (%Rrc)"),
962 rstrSrcLogNm.c_str(), rstrDstPath.c_str(), vrc);
963 uint32_t cRefs = RTVfsIoStrmRelease(hVfsIosDst);
964 AssertMsg(cRefs == 0, ("cRefs=%u\n", cRefs)); NOREF(cRefs);
965 if (RT_FAILURE(vrc))
966 RTFileDelete(rstrDstPath.c_str());
967 }
968 else
969 hrc = setErrorVrc(vrc, tr("Error opening destionation image '%s' for writing (%Rrc)"), rstrDstPath.c_str(), vrc);
970 }
971 return hrc;
972}
973
974
975/**
976 *
977 * @param stack Import stack.
978 * @param rstrSrcPath Source path.
979 * @param rstrDstPath Destination path.
980 * @param pszManifestEntry The manifest entry of the source file. This is
981 * used when constructing our manifest using a pass
982 * thru.
983 * @throws HRESULT error status, error info set.
984 */
985void Appliance::i_importCopyFile(ImportStack &stack, Utf8Str const &rstrSrcPath, Utf8Str const &rstrDstPath,
986 const char *pszManifestEntry)
987{
988 /*
989 * Open the file (throws error) and add a read ahead thread so we can do
990 * concurrent reads (+digest) and writes.
991 */
992 RTVFSIOSTREAM hVfsIosSrc = i_importOpenSourceFile(stack, rstrSrcPath, pszManifestEntry);
993 RTVFSIOSTREAM hVfsIosReadAhead;
994 int vrc = RTVfsCreateReadAheadForIoStream(hVfsIosSrc, 0 /*fFlags*/, 0 /*cBuffers=default*/, 0 /*cbBuffers=default*/,
995 &hVfsIosReadAhead);
996 if (RT_FAILURE(vrc))
997 {
998 RTVfsIoStrmRelease(hVfsIosSrc);
999 throw setErrorVrc(vrc, tr("Error initializing read ahead thread for '%s' (%Rrc)"), rstrSrcPath.c_str(), vrc);
1000 }
1001
1002 /*
1003 * Write the destination file (nothrow).
1004 */
1005 HRESULT hrc = i_importCreateAndWriteDestinationFile(rstrDstPath, hVfsIosReadAhead, rstrSrcPath);
1006 RTVfsIoStrmRelease(hVfsIosReadAhead);
1007
1008 /*
1009 * Before releasing the source stream, make sure we've successfully added
1010 * the digest to our manifest.
1011 */
1012 if (SUCCEEDED(hrc) && m->fDigestTypes)
1013 {
1014 vrc = RTManifestPtIosAddEntryNow(hVfsIosSrc);
1015 if (RT_FAILURE(vrc))
1016 hrc = setErrorVrc(vrc, tr("RTManifestPtIosAddEntryNow failed with %Rrc"), vrc);
1017 }
1018
1019 uint32_t cRefs = RTVfsIoStrmRelease(hVfsIosSrc);
1020 AssertMsg(cRefs == 0, ("cRefs=%u\n", cRefs)); NOREF(cRefs);
1021 if (SUCCEEDED(hrc))
1022 return;
1023 throw hrc;
1024}
1025
1026/**
1027 *
1028 * @param stack
1029 * @param rstrSrcPath
1030 * @param rstrDstPath
1031 * @param pszManifestEntry The manifest entry of the source file. This is
1032 * used when constructing our manifest using a pass
1033 * thru.
1034 * @throws HRESULT error status, error info set.
1035 */
1036void Appliance::i_importDecompressFile(ImportStack &stack, Utf8Str const &rstrSrcPath, Utf8Str const &rstrDstPath,
1037 const char *pszManifestEntry)
1038{
1039 RTVFSIOSTREAM hVfsIosSrcCompressed = i_importOpenSourceFile(stack, rstrSrcPath, pszManifestEntry);
1040
1041 /*
1042 * Add a read ahead thread here. This means reading and digest calculation
1043 * is done on one thread, while unpacking and writing is one on this thread.
1044 */
1045 RTVFSIOSTREAM hVfsIosReadAhead;
1046 int vrc = RTVfsCreateReadAheadForIoStream(hVfsIosSrcCompressed, 0 /*fFlags*/, 0 /*cBuffers=default*/,
1047 0 /*cbBuffers=default*/, &hVfsIosReadAhead);
1048 if (RT_FAILURE(vrc))
1049 {
1050 RTVfsIoStrmRelease(hVfsIosSrcCompressed);
1051 throw setErrorVrc(vrc, tr("Error initializing read ahead thread for '%s' (%Rrc)"), rstrSrcPath.c_str(), vrc);
1052 }
1053
1054 /*
1055 * Add decompression step.
1056 */
1057 RTVFSIOSTREAM hVfsIosSrc;
1058 vrc = RTZipGzipDecompressIoStream(hVfsIosReadAhead, 0, &hVfsIosSrc);
1059 RTVfsIoStrmRelease(hVfsIosReadAhead);
1060 if (RT_FAILURE(vrc))
1061 {
1062 RTVfsIoStrmRelease(hVfsIosSrcCompressed);
1063 throw setErrorVrc(vrc, tr("Error initializing gzip decompression for '%s' (%Rrc)"), rstrSrcPath.c_str(), vrc);
1064 }
1065
1066 /*
1067 * Write the stream to the destination file (nothrow).
1068 */
1069 HRESULT hrc = i_importCreateAndWriteDestinationFile(rstrDstPath, hVfsIosSrc, rstrSrcPath);
1070
1071 /*
1072 * Before releasing the source stream, make sure we've successfully added
1073 * the digest to our manifest.
1074 */
1075 if (SUCCEEDED(hrc) && m->fDigestTypes)
1076 {
1077 vrc = RTManifestPtIosAddEntryNow(hVfsIosSrcCompressed);
1078 if (RT_FAILURE(vrc))
1079 hrc = setErrorVrc(vrc, tr("RTManifestPtIosAddEntryNow failed with %Rrc"), vrc);
1080 }
1081
1082 uint32_t cRefs = RTVfsIoStrmRelease(hVfsIosSrc);
1083 AssertMsg(cRefs == 0, ("cRefs=%u\n", cRefs)); NOREF(cRefs);
1084
1085 cRefs = RTVfsIoStrmRelease(hVfsIosSrcCompressed);
1086 AssertMsg(cRefs == 0, ("cRefs=%u\n", cRefs)); NOREF(cRefs);
1087
1088 if (SUCCEEDED(hrc))
1089 return;
1090 throw hrc;
1091}
1092
1093/*******************************************************************************
1094 * Read stuff
1095 ******************************************************************************/
1096
1097/**
1098 * Implementation for reading an OVF (via task).
1099 *
1100 * This starts a new thread which will call
1101 * Appliance::taskThreadImportOrExport() which will then call readFS(). This
1102 * will then open the OVF with ovfreader.cpp.
1103 *
1104 * This is in a separate private method because it is used from two locations:
1105 *
1106 * 1) from the public Appliance::Read().
1107 *
1108 * 2) in a second worker thread; in that case, Appliance::ImportMachines() called Appliance::i_importImpl(), which
1109 * called Appliance::readFSOVA(), which called Appliance::i_importImpl(), which then called this again.
1110 *
1111 * @param aLocInfo The OVF location.
1112 * @param aProgress Where to return the progress object.
1113 * @throws COM error codes will be thrown.
1114 */
1115void Appliance::i_readImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)
1116{
1117 BstrFmt bstrDesc = BstrFmt(tr("Reading appliance '%s'"),
1118 aLocInfo.strPath.c_str());
1119 HRESULT rc;
1120 /* Create the progress object */
1121 aProgress.createObject();
1122 if (aLocInfo.storageType == VFSType_File)
1123 /* 1 operation only */
1124 rc = aProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
1125 bstrDesc.raw(),
1126 TRUE /* aCancelable */);
1127
1128 else if (aLocInfo.storageType == VFSType_Cloud)
1129 /* 1 operation only for now */
1130 rc = aProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
1131 "Getting cloud instance information...",
1132 TRUE /* aCancelable */);
1133 else
1134 /* 4/5 is downloading, 1/5 is reading */
1135 rc = aProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
1136 bstrDesc.raw(),
1137 TRUE /* aCancelable */,
1138 2, // ULONG cOperations,
1139 5, // ULONG ulTotalOperationsWeight,
1140 BstrFmt(tr("Download appliance '%s'"),
1141 aLocInfo.strPath.c_str()).raw(), // CBSTR bstrFirstOperationDescription,
1142 4); // ULONG ulFirstOperationWeight,
1143 if (FAILED(rc)) throw rc;
1144
1145 /* Initialize our worker task */
1146 TaskOVF *ovfTask = NULL;
1147 TaskCloud *cloudTask = NULL;
1148 try
1149 {
1150 if (aLocInfo.storageType == VFSType_File)
1151 {
1152 ovfTask = new TaskOVF(this, TaskOVF::Read, aLocInfo, aProgress);
1153 rc = ovfTask->createThread();
1154 if (FAILED(rc)) throw rc;
1155 }
1156 else if (aLocInfo.storageType == VFSType_Cloud)
1157 {
1158 cloudTask = new TaskCloud(this, TaskCloud::ReadData, aLocInfo, aProgress);
1159 rc = cloudTask->createThread();
1160 if (FAILED(rc)) throw rc;
1161 }
1162 }
1163 catch (HRESULT aRc)
1164 {
1165 rc = aRc;
1166 }
1167 catch (...)
1168 {
1169 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1170 tr("Could not create a task object for reading the Appliance data"));
1171 }
1172
1173 if (FAILED(rc))
1174 {
1175 if (ovfTask)
1176 delete ovfTask;
1177 if (cloudTask)
1178 delete cloudTask;
1179 throw rc;
1180 }
1181}
1182
1183HRESULT Appliance::i_gettingCloudData(TaskCloud *pTask)
1184{
1185 LogFlowFuncEnter();
1186 LogFlowFunc(("Appliance %p\n", this));
1187
1188 AutoCaller autoCaller(this);
1189 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1190
1191 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
1192
1193 HRESULT hrc = S_OK;
1194
1195 try
1196 {
1197 Utf8Str strBasename(pTask->locInfo.strPath);
1198 RTCList<RTCString, RTCString *> parts = strBasename.split("/" );
1199 if (parts.size() != 2)//profile + instance id
1200 {
1201 return setErrorVrc(VERR_MISMATCH, tr("%s: The profile name or instance id are absent or"
1202 "contain unsupported characters.", __FUNCTION__));
1203 }
1204
1205 //Get information about the passed cloud instance
1206 ComPtr<ICloudProviderManager> cpm;
1207 hrc = mVirtualBox->COMGETTER(CloudProviderManager)(cpm.asOutParam());
1208 if (FAILED(hrc))
1209 return setErrorVrc(VERR_COM_OBJECT_NOT_FOUND, tr("%s: Cloud provider manager object wasn't found", __FUNCTION__));
1210
1211 Utf8Str strProviderName = pTask->locInfo.strProvider;
1212 ComPtr<ICloudProvider> cloudProvider;
1213 ComPtr<ICloudProfile> cloudProfile;
1214 hrc = cpm->GetProviderByShortName(Bstr(strProviderName.c_str()).raw(), cloudProvider.asOutParam());
1215
1216 if (FAILED(hrc))
1217 return setErrorVrc(VERR_COM_OBJECT_NOT_FOUND, tr("%s: Cloud provider object wasn't found", __FUNCTION__));
1218
1219 Utf8Str profileName(parts.at(0));//profile
1220 if (profileName.isEmpty())
1221 return setErrorVrc(VBOX_E_OBJECT_NOT_FOUND, tr("%s: Cloud user profile name wasn't found", __FUNCTION__));
1222
1223 hrc = cloudProvider->GetProfileByName(Bstr(parts.at(0)).raw(), cloudProfile.asOutParam());
1224 if (FAILED(hrc))
1225 return setErrorVrc(VERR_COM_OBJECT_NOT_FOUND, tr("%s: Cloud profile object wasn't found", __FUNCTION__));
1226
1227 ComObjPtr<ICloudClient> cloudClient;
1228 hrc = cloudProfile->CreateCloudClient(cloudClient.asOutParam());
1229 if (FAILED(hrc))
1230 return setErrorVrc(VERR_COM_OBJECT_NOT_FOUND, tr("%s: Cloud client object wasn't found", __FUNCTION__));
1231
1232 m->virtualSystemDescriptions.clear();//clear all for assurance before creating new
1233 std::vector<ComPtr<IVirtualSystemDescription> > vsdArray;
1234 ULONG requestedVSDnums = 1;
1235 ULONG newVSDnums = 0;
1236 hrc = createVirtualSystemDescriptions(requestedVSDnums, &newVSDnums);
1237 if (FAILED(hrc)) throw hrc;
1238 if (requestedVSDnums != newVSDnums)
1239 throw setErrorVrc(VERR_MISMATCH, tr("%s: Requested and created numbers of VSD are differ.", __FUNCTION__));
1240
1241 hrc = getVirtualSystemDescriptions(vsdArray);
1242 if (FAILED(hrc)) throw hrc;
1243 ComPtr<IVirtualSystemDescription> instanceDescription = vsdArray[0];
1244
1245 LogRel(("%s: calling CloudClient::GetInstanceInfo()\n", __FUNCTION__));
1246 hrc = cloudClient->GetInstanceInfo(Bstr(parts.at(1)).raw(), instanceDescription);//instance id
1247 if (FAILED(hrc)) throw hrc;
1248
1249 // set cloud profile
1250 instanceDescription->AddDescription(VirtualSystemDescriptionType_CloudProfileName,
1251 Bstr(profileName).raw(),
1252 Bstr(profileName).raw());
1253
1254 Utf8StrFmt strSetting("VM with id %s imported from the cloud provider %s",
1255 parts.at(1).c_str(), strProviderName.c_str());
1256 // set description
1257 instanceDescription->AddDescription(VirtualSystemDescriptionType_Description,
1258 Bstr(strSetting).raw(),
1259 Bstr(strSetting).raw());
1260 }
1261 catch (HRESULT arc)
1262 {
1263 LogFlowFunc(("arc=%Rhrc\n", arc));
1264 hrc = arc;
1265 }
1266
1267 LogFlowFunc(("rc=%Rhrc\n", hrc));
1268 LogFlowFuncLeave();
1269
1270 return hrc;
1271}
1272
1273/**
1274 * Actual worker code for import from the Cloud
1275 *
1276 * @param pTask
1277 * @return
1278 */
1279HRESULT Appliance::i_importCloudImpl(TaskCloud *pTask)
1280{
1281 LogFlowFuncEnter();
1282 LogFlowFunc(("Appliance %p\n", this));
1283
1284 int vrc = VINF_SUCCESS;
1285 HRESULT hrc = S_OK;
1286 Utf8Str strLastActualErrorDesc("No errors");
1287
1288 /* Change the appliance state so we can safely leave the lock while doing
1289 * time-consuming operations; also the below method calls do all kinds of
1290 * locking which conflicts with the appliance object lock. */
1291 AutoWriteLock writeLock(this COMMA_LOCKVAL_SRC_POS);
1292
1293 /* Check if the appliance is currently busy. */
1294 if (!i_isApplianceIdle())
1295 return E_ACCESSDENIED;
1296 /* Set the internal state to importing. */
1297 m->state = Data::ApplianceImporting;
1298 writeLock.release();
1299
1300 /* Clear the list of imported machines, if any */
1301 m->llGuidsMachinesCreated.clear();
1302
1303 ComPtr<ICloudProviderManager> cpm;
1304 hrc = mVirtualBox->COMGETTER(CloudProviderManager)(cpm.asOutParam());
1305 if (FAILED(hrc))
1306 return setErrorVrc(VERR_COM_OBJECT_NOT_FOUND, tr("%s: Cloud provider manager object wasn't found", __FUNCTION__));
1307
1308 Utf8Str strProviderName = pTask->locInfo.strProvider;
1309 ComPtr<ICloudProvider> cloudProvider;
1310 ComPtr<ICloudProfile> cloudProfile;
1311 hrc = cpm->GetProviderByShortName(Bstr(strProviderName.c_str()).raw(), cloudProvider.asOutParam());
1312
1313 if (FAILED(hrc))
1314 return setErrorVrc(VERR_COM_OBJECT_NOT_FOUND, tr("%s: Cloud provider object wasn't found", __FUNCTION__));
1315
1316 /* Get the actual VSD, only one VSD object can be there for now so just call the function front() */
1317 ComPtr<IVirtualSystemDescription> vsd = m->virtualSystemDescriptions.front();
1318
1319 Utf8Str vsdData;
1320 com::SafeArray<VirtualSystemDescriptionType_T> retTypes;
1321 com::SafeArray<BSTR> aRefs;
1322 com::SafeArray<BSTR> aOvfValues;
1323 com::SafeArray<BSTR> aVBoxValues;
1324 com::SafeArray<BSTR> aExtraConfigValues;
1325
1326/*
1327 * local #define for better reading the code
1328 * uses only the previously locally declared variable names
1329 * set hrc as the result of operation
1330 */
1331#define GET_VSD_DESCRIPTION_BY_TYPE(aParamType) \
1332 retTypes.setNull(); \
1333 aRefs.setNull(); \
1334 aOvfValues.setNull(); \
1335 aVBoxValues.setNull(); \
1336 aExtraConfigValues.setNull(); \
1337 vsd->GetDescriptionByType(aParamType, \
1338 ComSafeArrayAsOutParam(retTypes), \
1339 ComSafeArrayAsOutParam(aRefs), \
1340 ComSafeArrayAsOutParam(aOvfValues), \
1341 ComSafeArrayAsOutParam(aVBoxValues), \
1342 ComSafeArrayAsOutParam(aExtraConfigValues)); \
1343
1344
1345 GET_VSD_DESCRIPTION_BY_TYPE(VirtualSystemDescriptionType_CloudProfileName)
1346 if (aVBoxValues.size() == 0)
1347 return setErrorVrc(VERR_NOT_FOUND, tr("%s: Cloud user profile name wasn't found", __FUNCTION__));
1348
1349 Utf8Str profileName(aVBoxValues[0]);
1350 if (profileName.isEmpty())
1351 return setErrorVrc(VERR_INVALID_STATE, tr("%s: Cloud user profile name is empty", __FUNCTION__));
1352
1353 hrc = cloudProvider->GetProfileByName(aVBoxValues[0], cloudProfile.asOutParam());
1354 if (FAILED(hrc))
1355 return setErrorVrc(VERR_COM_OBJECT_NOT_FOUND, tr("%s: Cloud profile object wasn't found", __FUNCTION__));
1356
1357 ComObjPtr<ICloudClient> cloudClient;
1358 hrc = cloudProfile->CreateCloudClient(cloudClient.asOutParam());
1359 if (FAILED(hrc))
1360 return setErrorVrc(VERR_COM_OBJECT_NOT_FOUND, tr("%s: Cloud client object wasn't found", __FUNCTION__));
1361
1362 ComPtr<IProgress> pProgress;
1363 hrc = pTask->pProgress.queryInterfaceTo(pProgress.asOutParam());
1364 if (FAILED(hrc))
1365 return hrc;
1366
1367 Utf8Str strOsType;
1368 ComPtr<IGuestOSType> pGuestOSType;
1369 {
1370 GET_VSD_DESCRIPTION_BY_TYPE(VirtualSystemDescriptionType_OS)//aVBoxValues is set in this #define
1371 if (aVBoxValues.size() != 0)
1372 strOsType = aVBoxValues[0];
1373 else
1374 return setErrorVrc(VERR_NOT_FOUND, "%s: OS type wasn't found", __FUNCTION__);
1375 LogRel(("%s: OS type is %s\n", __FUNCTION__, strOsType.c_str()));
1376
1377 /* Check the OS type and get the structure with the some default settings for this OS type */
1378 uint32_t const idxOSType = Global::getOSTypeIndexFromId(strOsType.c_str());
1379 VBOXOSTYPE guestOsType = idxOSType < Global::cOSTypes ? Global::sOSTypes[idxOSType].osType : VBOXOSTYPE_Unknown;
1380 if (guestOsType == VBOXOSTYPE_Unknown)
1381 return setErrorVrc(VERR_NOT_SUPPORTED, "%s: OS type is unknown", __FUNCTION__);
1382
1383 /* We can get some default settings from GuestOSType when it's needed */
1384 mVirtualBox->GetGuestOSType(Bstr(strOsType).raw(), pGuestOSType.asOutParam());
1385 }
1386
1387 /* Should be defined here because it's used later, at least when ComposeMachineFilename() is called */
1388 Utf8Str strVMName("VM_exported_from_cloud");
1389
1390 if (m->virtualSystemDescriptions.size() == 1)
1391 {
1392 do
1393 {
1394 ComPtr<IVirtualBox> VBox(mVirtualBox);
1395
1396 {
1397 GET_VSD_DESCRIPTION_BY_TYPE(VirtualSystemDescriptionType_Name)//aVBoxValues is set in this #define
1398 if (aVBoxValues.size() != 0)//paranoia but anyway...
1399 strVMName = aVBoxValues[0];
1400 LogRel(("%s: VM name is %s\n", __FUNCTION__, strVMName.c_str()));
1401 }
1402
1403// i_searchUniqueVMName(strVMName);//internally calls setError() in the case of absent the registered VM with such name
1404
1405 ComPtr<IMachine> machine;
1406 hrc = mVirtualBox->FindMachine(Bstr(strVMName.c_str()).raw(), machine.asOutParam());
1407 if (SUCCEEDED(hrc))
1408 {
1409 //what to do? create a new name from the old one with some suffix?
1410 com::Guid newId;
1411 newId.create();
1412 strVMName.append("__").append(newId.toString());
1413 vsd->RemoveDescriptionByType(VirtualSystemDescriptionType_Name);
1414 vsd->AddDescription(VirtualSystemDescriptionType_Name,
1415 Bstr(strVMName).raw(),
1416 Bstr(strVMName).raw());
1417 //No check again because it would be weird if a VM with such unique name exists
1418 }
1419
1420 Utf8Str strInsId;
1421 GET_VSD_DESCRIPTION_BY_TYPE(VirtualSystemDescriptionType_CloudInstanceId)//aVBoxValues is set in this #define
1422 if (aVBoxValues.size() == 0)
1423 {
1424 hrc = setErrorVrc(VERR_NOT_FOUND, "%s: Cloud Instance Id wasn't found", __FUNCTION__);
1425 break;
1426 }
1427 strInsId = aVBoxValues[0];
1428
1429 LogRel(("%s: calling CloudClient::ImportInstance\n", __FUNCTION__));
1430
1431 /* Here it's strongly supposed that cloud import produces ONE object on the disk.
1432 Because it much easier to manage one object in any case.
1433 In the case when cloud import creates several object on the disk all of them
1434 must be combined together into one object by cloud client.
1435 The most simple way is to create a TAR archive. */
1436 hrc = cloudClient->ImportInstance(m->virtualSystemDescriptions.front(),
1437 aVBoxValues[0],
1438 VBox,
1439 pProgress);
1440
1441 if (FAILED(hrc))
1442 {
1443 strLastActualErrorDesc = Utf8StrFmt("%s: Cloud import (cloud phase) failed. "
1444 "Used cloud instance is \'%s\'\n", __FUNCTION__, strInsId.c_str());
1445
1446 LogRel((strLastActualErrorDesc.c_str()));
1447 hrc = setError(hrc, strLastActualErrorDesc.c_str());
1448 break;
1449 }
1450
1451 } while (0);
1452 }
1453 else
1454 {
1455 hrc = setErrorVrc(VERR_NOT_SUPPORTED, tr("Import from Cloud isn't supported for more than one VM instance."));
1456
1457 /* Reset the state so others can call methods again */
1458 m->state = Data::ApplianceIdle;
1459 return hrc;
1460 }
1461
1462 if (FAILED(hrc))
1463 {
1464 HRESULT temp_hrc = hrc;//save the original result
1465 Utf8Str generalRollBackErrorMessage("Rollback action for Import Cloud operation failed."
1466 "Some leavings may exist on the local disk or in the Cloud.");
1467 /*
1468 * Roll-back actions.
1469 * we finish here if:
1470 * 1. The getting the object form the Cloud has been failed.
1471 * 2. Something is wrong with getting data from ComPtr<IVirtualSystemDescription> vsd.
1472 * 3. More than 1 VirtualSystemDescription is presented in the list m->virtualSystemDescriptions.
1473 * Maximum what we have there are:
1474 * 1. The downloaded object, so just check the presence and delete it if one exists
1475 * 2. Some leftovers in the Cloud. Also delete them too if it's possible.
1476 * Should they be deleted in the OCICloudClient::importInstance()?
1477 * Because deleting them here is not easy as it in the importInstance().
1478 */
1479
1480 {
1481 /* small explanation here, the image here points out to the whole downloaded object (not to the image only)
1482 * filled during the first cloud import stage (in the ICloudClient::importInstance()) */
1483 GET_VSD_DESCRIPTION_BY_TYPE(VirtualSystemDescriptionType_HardDiskImage)//aVBoxValues is set in this #define
1484 if (aVBoxValues.size() == 0)
1485 hrc = setErrorVrc(VERR_NOT_FOUND, generalRollBackErrorMessage.c_str());
1486 else
1487 {
1488 vsdData = aVBoxValues[0];
1489 //try to delete the downloaded object
1490 bool fExist = RTPathExists(vsdData.c_str());
1491 if (fExist)
1492 {
1493 vrc = RTFileDelete(vsdData.c_str());
1494 if (RT_FAILURE(vrc))
1495 {
1496 hrc = setErrorVrc(vrc, generalRollBackErrorMessage.c_str());
1497 LogRel(("%s: Rollback action - the object %s hasn't been deleted\n", __FUNCTION__, vsdData.c_str()));
1498 }
1499 else
1500 LogRel(("%s: Rollback action - the object %s has been deleted\n", __FUNCTION__, vsdData.c_str()));
1501 }
1502 }
1503 }
1504
1505 {
1506 GET_VSD_DESCRIPTION_BY_TYPE(VirtualSystemDescriptionType_CloudInstanceId)//aVBoxValues is set in this #define
1507 if (aVBoxValues.size() == 0)
1508 hrc = setErrorVrc(VERR_NOT_FOUND, generalRollBackErrorMessage.c_str());
1509 else
1510 {
1511 vsdData = aVBoxValues[0];
1512 //hrc = cloud.EliminateImportLeavings(aVBoxValues[0], pProgress);//future function
1513 if (FAILED(hrc))
1514 {
1515 hrc = setErrorVrc(VERR_INVALID_STATE, generalRollBackErrorMessage.c_str());
1516 LogRel(("%s: Rollback action - the leavings in the %s after import the "
1517 "instance %s may not have been deleted\n",
1518 __FUNCTION__, strProviderName.c_str(), vsdData.c_str()));
1519 }
1520 else
1521 LogRel(("%s: Rollback action - the leavings in the %s after import the "
1522 "instance %s have been deleted\n",
1523 __FUNCTION__, strProviderName.c_str(), vsdData.c_str()));
1524 }
1525 }
1526
1527 /* Because during the rollback phase the hrc may have the good result
1528 * Thus we restore the original error in the case when the rollback phase was successful
1529 * Otherwise we return not the original error but the last error in the rollback phase */
1530 if (SUCCEEDED(hrc))
1531 hrc = setError(temp_hrc, strLastActualErrorDesc.c_str());//restore the original result
1532 }
1533 else
1534 {
1535
1536 /* Small explanation here, the image here points out to the whole downloaded object (not to the image only)
1537 * filled during the first cloud import stage (in the ICloudClient::importInstance()) */
1538 GET_VSD_DESCRIPTION_BY_TYPE(VirtualSystemDescriptionType_HardDiskImage)//aVBoxValues is set in this #define
1539 if (aVBoxValues.size() == 0)
1540 throw setErrorVrc(VERR_NOT_FOUND, "%s: The description of the downloaded object wasn't found", __FUNCTION__);
1541
1542 Utf8Str strAbsSrcPath(aVBoxValues[0]);
1543
1544 /* Put all VFS* declaration here because they are needed to be release by the corresponding
1545 RTVfs***Release functions in the case of exception */
1546 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1547 RTVFSFSSTREAM hVfsFssObject = NIL_RTVFSFSSTREAM;
1548 RTVFSIOSTREAM hVfsIosCurr = NIL_RTVFSIOSTREAM;
1549
1550 /* Continue and create new VM using data from VSD and downloaded object.
1551 * The downloaded images should be converted to VDI/VMDK if they have another format */
1552 try
1553 {
1554 Utf8Str strInstId("default cloud instance id");
1555 {
1556 GET_VSD_DESCRIPTION_BY_TYPE(VirtualSystemDescriptionType_CloudInstanceId)//aVBoxValues is set in this #define
1557 if (aVBoxValues.size() != 0)//paranoia but anyway...
1558 strInstId = aVBoxValues[0];
1559 LogRel(("%s: Importing cloud instance %s\n", __FUNCTION__, strInstId.c_str()));
1560 }
1561
1562 Utf8Str strGroup("/");//default VM group
1563 Utf8Str strTargetFormat("VMDK");//default image format
1564
1565 /* Based on the VM name, create a target machine path. */
1566 Bstr bstrSettingsFilename;
1567 hrc = mVirtualBox->ComposeMachineFilename(Bstr(strVMName).raw(),
1568 Bstr(strGroup).raw(),
1569 NULL /* aCreateFlags */,
1570 NULL /* aBaseFolder */,
1571 bstrSettingsFilename.asOutParam());
1572 if (FAILED(hrc)) throw hrc;
1573
1574 Utf8Str strMachineFolder(bstrSettingsFilename);
1575 strMachineFolder.stripFilename();
1576
1577 /* Get the system properties. */
1578 SystemProperties *pSysProps = mVirtualBox->i_getSystemProperties();
1579
1580 ComObjPtr<MediumFormat> trgFormat;
1581 trgFormat = pSysProps->i_mediumFormatFromExtension(strTargetFormat);
1582
1583 /* Processing the downloaded object (prepare for the local import) */
1584 RTVFSIOSTREAM hVfsIosSrc;
1585 vrc = RTVfsIoStrmOpenNormal(strAbsSrcPath.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosSrc);
1586 if (RT_FAILURE(vrc))
1587 {
1588 strLastActualErrorDesc = Utf8StrFmt("Error opening '%s' for reading (%Rrc)\n", strAbsSrcPath.c_str(), vrc);
1589 throw setErrorVrc(vrc, strLastActualErrorDesc.c_str());
1590 }
1591
1592 vrc = RTZipTarFsStreamFromIoStream(hVfsIosSrc, 0 /*fFlags*/, &hVfsFssObject);
1593 RTVfsIoStrmRelease(hVfsIosSrc);
1594 if (RT_FAILURE(vrc))
1595 {
1596 strLastActualErrorDesc = Utf8StrFmt("Error reading the downloaded file '%s' (%Rrc)", strAbsSrcPath.c_str(), vrc);
1597 throw setErrorVrc(vrc, strLastActualErrorDesc.c_str());
1598 }
1599
1600 /* Create an empty ovf::OVFReader for manual filling it.
1601 * It's not a normal usage case, but we try to re-use some OVF stuff to friend
1602 * the cloud import with OVF import.
1603 * In the standard case the ovf::OVFReader is created earlier.*/
1604 m->pReader = new ovf::OVFReader();
1605
1606 /* Create a new virtual system and work directly on the list copy. */
1607 m->pReader->m_llVirtualSystems.push_back(ovf::VirtualSystem());
1608 ovf::VirtualSystem &vsys = m->pReader->m_llVirtualSystems.back();
1609
1610 /* Try to re-use some OVF stuff here */
1611 {
1612 vsys.strName = strVMName;
1613 uint32_t cpus = 1;
1614 {
1615 GET_VSD_DESCRIPTION_BY_TYPE(VirtualSystemDescriptionType_CPU)//aVBoxValues is set in this #define
1616 if (aVBoxValues.size() != 0)
1617 {
1618 vsdData = aVBoxValues[0];
1619 cpus = vsdData.toUInt32();
1620 }
1621 vsys.cCPUs = (uint16_t)cpus;
1622 LogRel(("%s: Number of CPUs is %s\n", __FUNCTION__, vsdData.c_str()));
1623 }
1624
1625 ULONG memory;//Mb
1626 pGuestOSType->COMGETTER(RecommendedRAM)(&memory);
1627 {
1628 GET_VSD_DESCRIPTION_BY_TYPE(VirtualSystemDescriptionType_Memory)//aVBoxValues is set in this #define
1629 if (aVBoxValues.size() != 0)
1630 {
1631 vsdData = aVBoxValues[0];
1632 memory = vsdData.toUInt32();
1633 }
1634 vsys.ullMemorySize = memory;
1635 LogRel(("%s: Size of RAM is %sMB\n", __FUNCTION__, vsdData.c_str()));
1636 }
1637
1638 {
1639 GET_VSD_DESCRIPTION_BY_TYPE(VirtualSystemDescriptionType_Description)//aVBoxValues is set in this #define
1640 if (aVBoxValues.size() != 0)
1641 {
1642 vsdData = aVBoxValues[0];
1643 vsys.strDescription = vsdData;
1644 }
1645 LogRel(("%s: VM description \'%s\'\n", __FUNCTION__, vsdData.c_str()));
1646 }
1647
1648 {
1649 GET_VSD_DESCRIPTION_BY_TYPE(VirtualSystemDescriptionType_OS)//aVBoxValues is set in this #define
1650 if (aVBoxValues.size() != 0)
1651 strOsType = aVBoxValues[0];
1652 vsys.strTypeVBox = strOsType;
1653 LogRel(("%s: OS type is %s\n", __FUNCTION__, strOsType.c_str()));
1654 }
1655
1656 ovf::EthernetAdapter ea;
1657 {
1658 GET_VSD_DESCRIPTION_BY_TYPE(VirtualSystemDescriptionType_NetworkAdapter)//aVBoxValues is set in this #define
1659 if (aVBoxValues.size() != 0)
1660 {
1661 ea.strAdapterType = (Utf8Str)(aVBoxValues[0]);
1662 ea.strNetworkName = "NAT";//default
1663 vsys.llEthernetAdapters.push_back(ea);
1664 LogRel(("%s: Network adapter type is %s\n", __FUNCTION__, ea.strAdapterType.c_str()));
1665 }
1666 else
1667 {
1668 NetworkAdapterType_T defaultAdapterType = NetworkAdapterType_Am79C970A;
1669 pGuestOSType->COMGETTER(AdapterType)(&defaultAdapterType);
1670 Utf8StrFmt dat("%RU32", (uint32_t)defaultAdapterType);
1671 vsd->AddDescription(VirtualSystemDescriptionType_NetworkAdapter,
1672 Bstr(dat).raw(),
1673 Bstr(Utf8Str("NAT")).raw());
1674 }
1675 }
1676
1677 ovf::HardDiskController hdc;
1678 {
1679 //It's thought that SATA is supported by any OS types
1680 hdc.system = ovf::HardDiskController::SATA;
1681 hdc.idController = 0;
1682
1683 GET_VSD_DESCRIPTION_BY_TYPE(VirtualSystemDescriptionType_HardDiskControllerSATA)//aVBoxValues is set in this #define
1684 if (aVBoxValues.size() != 0)
1685 hdc.strControllerType = (Utf8Str)(aVBoxValues[0]);
1686 else
1687 hdc.strControllerType = "AHCI";
1688
1689 LogRel(("%s: Hard disk controller type is %s\n", __FUNCTION__, hdc.strControllerType.c_str()));
1690 vsys.mapControllers[hdc.idController] = hdc;
1691
1692 if (aVBoxValues.size() == 0)
1693 {
1694 /* we should do it here because it'll be used later in the OVF logic (inside i_importMachines()) */
1695 vsd->AddDescription(VirtualSystemDescriptionType_HardDiskControllerSATA,
1696 Bstr(hdc.strControllerType).raw(),
1697 Bstr(hdc.strControllerType).raw());
1698 }
1699 }
1700
1701 {
1702 GET_VSD_DESCRIPTION_BY_TYPE(VirtualSystemDescriptionType_SoundCard)//aVBoxValues is set in this #define
1703 if (aVBoxValues.size() != 0)
1704 vsys.strSoundCardType = (Utf8Str)(aVBoxValues[0]);
1705 else
1706 {
1707 AudioControllerType_T defaultAudioController;
1708 pGuestOSType->COMGETTER(RecommendedAudioController)(&defaultAudioController);
1709 vsys.strSoundCardType = Utf8StrFmt("%RU32", (uint32_t)defaultAudioController);//"ensoniq1371";//"AC97";
1710 vsd->AddDescription(VirtualSystemDescriptionType_SoundCard,
1711 Bstr(vsys.strSoundCardType).raw(),
1712 Bstr(vsys.strSoundCardType).raw());
1713 }
1714
1715 LogRel(("%s: Sound card is %s\n", __FUNCTION__, vsys.strSoundCardType.c_str()));
1716 }
1717
1718 vsys.fHasFloppyDrive = false;
1719 vsys.fHasCdromDrive = false;
1720 vsys.fHasUsbController = true;
1721 }
1722
1723 unsigned currImageObjectNum = 0;
1724 hrc = S_OK;
1725 do
1726 {
1727 char *pszName = NULL;
1728 RTVFSOBJTYPE enmType;
1729 vrc = RTVfsFsStrmNext(hVfsFssObject, &pszName, &enmType, &hVfsObj);
1730 if (RT_FAILURE(vrc))
1731 {
1732 if (vrc != VERR_EOF)
1733 {
1734 hrc = setErrorVrc(vrc, tr("%s: Error reading '%s' (%Rrc)"), __FUNCTION__, strAbsSrcPath.c_str(), vrc);
1735 throw hrc;
1736 }
1737 break;
1738 }
1739
1740 /* We only care about entries that are files. Get the I/O stream handle for them. */
1741 if ( enmType == RTVFSOBJTYPE_IO_STREAM
1742 || enmType == RTVFSOBJTYPE_FILE)
1743 {
1744 /* Find the suffix and check if this is a possibly interesting file. */
1745 char *pszSuffix = RTStrToLower(strrchr(pszName, '.'));
1746
1747 /* Get the I/O stream. */
1748 hVfsIosCurr = RTVfsObjToIoStream(hVfsObj);
1749 Assert(hVfsIosCurr != NIL_RTVFSIOSTREAM);
1750
1751 /* Get the source medium format */
1752 ComObjPtr<MediumFormat> srcFormat;
1753 srcFormat = pSysProps->i_mediumFormatFromExtension(pszSuffix + 1);
1754
1755 /* unknown image format so just extract a file without any processing */
1756 if (srcFormat == NULL)
1757 {
1758 /* Read the file into a memory buffer */
1759 void *pvBuffered;
1760 size_t cbBuffered;
1761 RTVFSFILE hVfsDstFile = NIL_RTVFSFILE;
1762 try
1763 {
1764 vrc = RTVfsIoStrmReadAll(hVfsIosCurr, &pvBuffered, &cbBuffered);
1765 RTVfsIoStrmRelease(hVfsIosCurr);
1766 hVfsIosCurr = NIL_RTVFSIOSTREAM;
1767 if (RT_FAILURE(vrc))
1768 throw setErrorVrc(vrc, tr("Could not read the file '%s' (%Rrc)"), strAbsSrcPath.c_str(), vrc);
1769
1770 Utf8StrFmt strAbsDstPath("%s%s%s", strMachineFolder.c_str(), RTPATH_SLASH_STR, pszName);
1771
1772 /* Simple logic - just try to get dir info, in case of absent try to create one.
1773 No deep errors analysis */
1774 RTFSOBJINFO dirInfo;
1775 vrc = RTPathQueryInfo(strMachineFolder.c_str(), &dirInfo, RTFSOBJATTRADD_NOTHING);
1776 if (RT_FAILURE(vrc))
1777 {
1778 if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
1779 {
1780 vrc = RTDirCreate(strMachineFolder.c_str(), 0755, 0);
1781 if (RT_FAILURE(vrc))
1782 throw setErrorVrc(vrc, tr("Could not create the directory '%s' (%Rrc)"),
1783 strMachineFolder.c_str(), vrc);
1784 }
1785 else
1786 throw setErrorVrc(vrc, tr("Error during getting info about the directory '%s' (%Rrc)"),
1787 strMachineFolder.c_str(), vrc);
1788 }
1789
1790 /* Write the file on the disk */
1791 vrc = RTVfsFileOpenNormal(strAbsDstPath.c_str(),
1792 RTFILE_O_WRITE | RTFILE_O_DENY_ALL | RTFILE_O_CREATE,
1793 &hVfsDstFile);
1794 if (RT_FAILURE(vrc))
1795 throw setErrorVrc(vrc, tr("Could not create the file '%s' (%Rrc)"), strAbsDstPath.c_str(), vrc);
1796
1797 size_t cbWritten;
1798 RTVfsFileWrite(hVfsDstFile, pvBuffered, cbBuffered, &cbWritten);
1799 if (RT_FAILURE(vrc))
1800 throw setErrorVrc(vrc, tr("Could not write into the file '%s' (%Rrc)"), strAbsDstPath.c_str(), vrc);
1801 }
1802 catch (HRESULT aRc)
1803 {
1804 hrc = aRc;
1805 }
1806 catch (...)
1807 {
1808 hrc = VERR_UNEXPECTED_EXCEPTION;
1809 }
1810
1811 /* Don't forget to release all resources */
1812 RTVfsFileRelease(hVfsDstFile);
1813 RTVfsIoStrmReadAllFree(pvBuffered, cbBuffered);
1814
1815 /*re-throw the error */
1816 if (FAILED(hrc))
1817 throw hrc;
1818
1819 }
1820 else
1821 {
1822 /* Image format is supported by VBox so extract the file and try to convert
1823 * one to the default format (which is VMDK for now) */
1824 Utf8Str z(bstrSettingsFilename);
1825 Utf8StrFmt strAbsDstPath("%s_%d.%s",
1826 z.stripSuffix().c_str(),
1827 currImageObjectNum,
1828 strTargetFormat.c_str());
1829
1830 hrc = mVirtualBox->i_findHardDiskByLocation(strAbsDstPath, false, NULL);
1831 if (SUCCEEDED(hrc))
1832 throw setError(VERR_ALREADY_EXISTS, tr("The hard disk '%s' already exists."), strAbsDstPath.c_str());
1833
1834 /* Create an IMedium object. */
1835 ComObjPtr<Medium> pTargetMedium;
1836 pTargetMedium.createObject();
1837 hrc = pTargetMedium->init(mVirtualBox,
1838 strTargetFormat,
1839 strAbsDstPath,
1840 Guid::Empty /* media registry: none yet */,
1841 DeviceType_HardDisk);
1842 if (FAILED(hrc))
1843 throw hrc;
1844
1845 pTask->pProgress->SetNextOperation(BstrFmt(tr("Importing virtual disk image '%s'",pszName)).raw(),
1846 200);
1847 ComObjPtr<Medium> nullParent;
1848 ComPtr<IProgress> pProgressImport;
1849 ComObjPtr<Progress> pProgressImportTmp;
1850 hrc = pProgressImportTmp.createObject();
1851 if (FAILED(hrc))
1852 throw hrc;
1853
1854 hrc = pProgressImportTmp->init(mVirtualBox,
1855 static_cast<IAppliance*>(this),
1856 Utf8StrFmt(tr("Importing medium '%s'"),
1857 pszName),
1858 TRUE);
1859 if (FAILED(hrc))
1860 throw hrc;
1861
1862 pProgressImportTmp.queryInterfaceTo(pProgressImport.asOutParam());
1863
1864 hrc = pTargetMedium->i_importFile(pszName,
1865 srcFormat,
1866 MediumVariant_Standard,
1867 hVfsIosCurr,
1868 nullParent,
1869 pProgressImportTmp,
1870 true /* aNotify */);
1871 RTVfsIoStrmRelease(hVfsIosCurr);
1872 hVfsIosCurr = NIL_RTVFSIOSTREAM;
1873 /* Now wait for the background import operation to complete;
1874 * this throws HRESULTs on error. */
1875 pTask->pProgress->WaitForOtherProgressCompletion(pProgressImport, 0 /* indefinite wait */);
1876
1877 /* Try to re-use some OVF stuff here */
1878 {
1879 /* Small trick here.
1880 * We add new item into the actual VSD after successful conversion.
1881 * There is no need to delete any previous records describing the images in the VSD
1882 * because later in the code the search of the images in the VSD will use such records
1883 * with the actual image id (d.strDiskId = pTargetMedium->i_getId().toString()) which is
1884 * used as a key for m->pReader->m_mapDisks, vsys.mapVirtualDisks.
1885 * So all 3 objects are tied via the image id.
1886 * In the OVF case we already have all such records in the VSD after reading OVF
1887 * description file (read() and interpret() functions).*/
1888 ovf::DiskImage d;
1889 d.strDiskId = pTargetMedium->i_getId().toString();
1890 d.strHref = pTargetMedium->i_getLocationFull();
1891 d.strFormat = pTargetMedium->i_getFormat();
1892 d.iSize = pTargetMedium->i_getSize();
1893 d.ulSuggestedSizeMB = (uint32_t)(pTargetMedium->i_getSize()/_1M);
1894
1895 m->pReader->m_mapDisks[d.strDiskId] = d;
1896
1897 ComObjPtr<VirtualSystemDescription> vsdescThis = m->virtualSystemDescriptions.front();
1898
1899 /* It's needed here to use the internal function i_addEntry() instead of the API function
1900 * addDescription() because we should pass the d.strDiskId for the proper handling this
1901 * disk later in the i_importMachineGeneric():
1902 * - find the line like this "if (vsdeHD->strRef == diCurrent.strDiskId)".
1903 * if those code can be eliminated then addDescription() will be used. */
1904 vsdescThis->i_addEntry(VirtualSystemDescriptionType_HardDiskImage,
1905 d.strDiskId,
1906 d.strHref,
1907 d.strHref,
1908 d.ulSuggestedSizeMB);
1909
1910 ovf::VirtualDisk vd;
1911 vd.idController = vsys.mapControllers[0].idController;
1912 vd.ulAddressOnParent = 0;
1913 vd.strDiskId = d.strDiskId;
1914 vsys.mapVirtualDisks[vd.strDiskId] = vd;
1915
1916 }
1917
1918 ++currImageObjectNum;
1919 }
1920
1921 RTVfsIoStrmRelease(hVfsIosCurr);
1922 hVfsIosCurr = NIL_RTVFSIOSTREAM;
1923 }
1924
1925 RTVfsObjRelease(hVfsObj);
1926 hVfsObj = NIL_RTVFSOBJ;
1927
1928 RTStrFree(pszName);
1929
1930 } while (SUCCEEDED(hrc));
1931
1932 RTVfsFsStrmRelease(hVfsFssObject);
1933 hVfsFssObject = NIL_RTVFSFSSTREAM;
1934
1935 pTask->pProgress->SetNextOperation(BstrFmt(tr("Creating new VM '%s'", strVMName.c_str())).raw(), 50);
1936 /* Create the import stack to comply OVF logic.
1937 * Before we filled some other data structures which are needed by OVF logic too.*/
1938 ImportStack stack(pTask->locInfo, m->pReader->m_mapDisks, pTask->pProgress, NIL_RTVFSFSSTREAM);
1939 i_importMachines(stack);
1940
1941 }
1942 catch (HRESULT aRc)
1943 {
1944 LogRel(("%s: Cloud import (local phase) - exception occured (%Rrc).\n", __FUNCTION__, aRc));
1945 hrc = aRc;
1946 }
1947 catch (...)
1948 {
1949 hrc = VERR_UNRESOLVED_ERROR;
1950 LogRel(("%s: Cloud import (local phase) - Unknown exception occured.\n", __FUNCTION__));
1951 }
1952
1953 if (FAILED(hrc))
1954 {
1955 /* Try to free VFS stuff because some of them might not be released due to the exception */
1956 if (hVfsIosCurr != NIL_RTVFSIOSTREAM)
1957 RTVfsIoStrmRelease(hVfsIosCurr);
1958 if (hVfsObj != NIL_RTVFSOBJ)
1959 RTVfsObjRelease(hVfsObj);
1960 if (hVfsFssObject != NIL_RTVFSFSSTREAM)
1961 RTVfsFsStrmRelease(hVfsFssObject);
1962
1963 LogRel(("%s: Cloud import (local phase): original error was \'%s\'.\n", __FUNCTION__, strLastActualErrorDesc.c_str()));
1964
1965 /* What to do here?
1966 * Delete or not the downloaded object?
1967 * For now:
1968 * - check the list of imported images, detach them and next delete.
1969 * - check the registration of created VM and delete one.
1970 * - check some other leavings if they may exist and delete them too.
1971 */
1972
1973 /* Small explanation here.
1974 * After adding extracted files into the actual VSD the returned list will contain not only the
1975 * record about the downloaded object but also the records about the extracted files from this object.
1976 * It's needed to go through this list to find the record about the downloaded object.
1977 * But it was the first record added into the list, so aVBoxValues[0] should be correct here.
1978 */
1979 GET_VSD_DESCRIPTION_BY_TYPE(VirtualSystemDescriptionType_HardDiskImage)//aVBoxValues is set in this #define
1980 if (aVBoxValues.size() != 0)
1981 hrc = setError(hrc, "%s: Cloud import (local phase) failed. Find the root cause in the log file. "
1982 "But the first phase was successful and the downloaded object was stored as \'%s\'\n",
1983 __FUNCTION__, Utf8Str(aVBoxValues[0]).c_str());
1984 }
1985 }
1986
1987 /* Reset the state so others can call methods again */
1988 m->state = Data::ApplianceIdle;
1989
1990 LogFlowFunc(("rc=%Rhrc\n", hrc));
1991 LogFlowFuncLeave();
1992 return hrc;
1993}
1994
1995/**
1996 * Actual worker code for reading an OVF from disk. This is called from Appliance::taskThreadImportOrExport()
1997 * and therefore runs on the OVF read worker thread. This opens the OVF with ovfreader.cpp.
1998 *
1999 * This runs in one context:
2000 *
2001 * 1) in a first worker thread; in that case, Appliance::Read() called Appliance::readImpl();
2002 *
2003 * @param pTask
2004 * @return
2005 */
2006HRESULT Appliance::i_readFS(TaskOVF *pTask)
2007{
2008 LogFlowFuncEnter();
2009 LogFlowFunc(("Appliance %p\n", this));
2010
2011 AutoCaller autoCaller(this);
2012 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2013
2014 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
2015
2016 HRESULT rc;
2017 if (pTask->locInfo.strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
2018 rc = i_readFSOVF(pTask);
2019 else
2020 rc = i_readFSOVA(pTask);
2021
2022 LogFlowFunc(("rc=%Rhrc\n", rc));
2023 LogFlowFuncLeave();
2024
2025 return rc;
2026}
2027
2028HRESULT Appliance::i_readFSOVF(TaskOVF *pTask)
2029{
2030 LogFlowFunc(("'%s'\n", pTask->locInfo.strPath.c_str()));
2031
2032 /*
2033 * Allocate a buffer for filenames and prep it for suffix appending.
2034 */
2035 char *pszNameBuf = (char *)alloca(pTask->locInfo.strPath.length() + 16);
2036 AssertReturn(pszNameBuf, VERR_NO_TMP_MEMORY);
2037 memcpy(pszNameBuf, pTask->locInfo.strPath.c_str(), pTask->locInfo.strPath.length() + 1);
2038 RTPathStripSuffix(pszNameBuf);
2039 size_t const cchBaseName = strlen(pszNameBuf);
2040
2041 /*
2042 * Open the OVF file first since that is what this is all about.
2043 */
2044 RTVFSIOSTREAM hIosOvf;
2045 int vrc = RTVfsIoStrmOpenNormal(pTask->locInfo.strPath.c_str(),
2046 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hIosOvf);
2047 if (RT_FAILURE(vrc))
2048 return setErrorVrc(vrc, tr("Failed to open OVF file '%s' (%Rrc)"), pTask->locInfo.strPath.c_str(), vrc);
2049
2050 HRESULT hrc = i_readOVFFile(pTask, hIosOvf, RTPathFilename(pTask->locInfo.strPath.c_str())); /* consumes hIosOvf */
2051 if (FAILED(hrc))
2052 return hrc;
2053
2054 /*
2055 * Try open the manifest file (for signature purposes and to determine digest type(s)).
2056 */
2057 RTVFSIOSTREAM hIosMf;
2058 strcpy(&pszNameBuf[cchBaseName], ".mf");
2059 vrc = RTVfsIoStrmOpenNormal(pszNameBuf, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hIosMf);
2060 if (RT_SUCCESS(vrc))
2061 {
2062 const char * const pszFilenamePart = RTPathFilename(pszNameBuf);
2063 hrc = i_readManifestFile(pTask, hIosMf /*consumed*/, pszFilenamePart);
2064 if (FAILED(hrc))
2065 return hrc;
2066
2067 /*
2068 * Check for the signature file.
2069 */
2070 RTVFSIOSTREAM hIosCert;
2071 strcpy(&pszNameBuf[cchBaseName], ".cert");
2072 vrc = RTVfsIoStrmOpenNormal(pszNameBuf, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hIosCert);
2073 if (RT_SUCCESS(vrc))
2074 {
2075 hrc = i_readSignatureFile(pTask, hIosCert /*consumed*/, pszFilenamePart);
2076 if (FAILED(hrc))
2077 return hrc;
2078 }
2079 else if (vrc != VERR_FILE_NOT_FOUND && vrc != VERR_PATH_NOT_FOUND)
2080 return setErrorVrc(vrc, tr("Failed to open the signature file '%s' (%Rrc)"), pszNameBuf, vrc);
2081
2082 }
2083 else if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
2084 {
2085 m->fDeterminedDigestTypes = true;
2086 m->fDigestTypes = 0;
2087 }
2088 else
2089 return setErrorVrc(vrc, tr("Failed to open the manifest file '%s' (%Rrc)"), pszNameBuf, vrc);
2090
2091 /*
2092 * Do tail processing (check the signature).
2093 */
2094 hrc = i_readTailProcessing(pTask);
2095
2096 LogFlowFunc(("returns %Rhrc\n", hrc));
2097 return hrc;
2098}
2099
2100HRESULT Appliance::i_readFSOVA(TaskOVF *pTask)
2101{
2102 LogFlowFunc(("'%s'\n", pTask->locInfo.strPath.c_str()));
2103
2104 /*
2105 * Open the tar file as file stream.
2106 */
2107 RTVFSIOSTREAM hVfsIosOva;
2108 int vrc = RTVfsIoStrmOpenNormal(pTask->locInfo.strPath.c_str(),
2109 RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsIosOva);
2110 if (RT_FAILURE(vrc))
2111 return setErrorVrc(vrc, tr("Error opening the OVA file '%s' (%Rrc)"), pTask->locInfo.strPath.c_str(), vrc);
2112
2113 RTVFSFSSTREAM hVfsFssOva;
2114 vrc = RTZipTarFsStreamFromIoStream(hVfsIosOva, 0 /*fFlags*/, &hVfsFssOva);
2115 RTVfsIoStrmRelease(hVfsIosOva);
2116 if (RT_FAILURE(vrc))
2117 return setErrorVrc(vrc, tr("Error reading the OVA file '%s' (%Rrc)"), pTask->locInfo.strPath.c_str(), vrc);
2118
2119 /*
2120 * Since jumping thru an OVA file with seekable disk backing is rather
2121 * efficient, we can process .ovf, .mf and .cert files here without any
2122 * strict ordering restrictions.
2123 *
2124 * (Technically, the .ovf-file comes first, while the manifest and its
2125 * optional signature file either follows immediately or at the very end of
2126 * the OVA. The manifest is optional.)
2127 */
2128 char *pszOvfNameBase = NULL;
2129 size_t cchOvfNameBase = 0; NOREF(cchOvfNameBase);
2130 unsigned cLeftToFind = 3;
2131 HRESULT hrc = S_OK;
2132 do
2133 {
2134 char *pszName = NULL;
2135 RTVFSOBJTYPE enmType;
2136 RTVFSOBJ hVfsObj;
2137 vrc = RTVfsFsStrmNext(hVfsFssOva, &pszName, &enmType, &hVfsObj);
2138 if (RT_FAILURE(vrc))
2139 {
2140 if (vrc != VERR_EOF)
2141 hrc = setErrorVrc(vrc, tr("Error reading OVA '%s' (%Rrc)"), pTask->locInfo.strPath.c_str(), vrc);
2142 break;
2143 }
2144
2145 /* We only care about entries that are files. Get the I/O stream handle for them. */
2146 if ( enmType == RTVFSOBJTYPE_IO_STREAM
2147 || enmType == RTVFSOBJTYPE_FILE)
2148 {
2149 /* Find the suffix and check if this is a possibly interesting file. */
2150 char *pszSuffix = strrchr(pszName, '.');
2151 if ( pszSuffix
2152 && ( RTStrICmp(pszSuffix + 1, "ovf") == 0
2153 || RTStrICmp(pszSuffix + 1, "mf") == 0
2154 || RTStrICmp(pszSuffix + 1, "cert") == 0) )
2155 {
2156 /* Match the OVF base name. */
2157 *pszSuffix = '\0';
2158 if ( pszOvfNameBase == NULL
2159 || RTStrICmp(pszName, pszOvfNameBase) == 0)
2160 {
2161 *pszSuffix = '.';
2162
2163 /* Since we're pretty sure we'll be processing this file, get the I/O stream. */
2164 RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
2165 Assert(hVfsIos != NIL_RTVFSIOSTREAM);
2166
2167 /* Check for the OVF (should come first). */
2168 if (RTStrICmp(pszSuffix + 1, "ovf") == 0)
2169 {
2170 if (pszOvfNameBase == NULL)
2171 {
2172 hrc = i_readOVFFile(pTask, hVfsIos, pszName);
2173 hVfsIos = NIL_RTVFSIOSTREAM;
2174
2175 /* Set the base name. */
2176 *pszSuffix = '\0';
2177 pszOvfNameBase = pszName;
2178 cchOvfNameBase = strlen(pszName);
2179 pszName = NULL;
2180 cLeftToFind--;
2181 }
2182 else
2183 LogRel(("i_readFSOVA: '%s' contains more than one OVF file ('%s'), picking the first one\n",
2184 pTask->locInfo.strPath.c_str(), pszName));
2185 }
2186 /* Check for manifest. */
2187 else if (RTStrICmp(pszSuffix + 1, "mf") == 0)
2188 {
2189 if (m->hMemFileTheirManifest == NIL_RTVFSFILE)
2190 {
2191 hrc = i_readManifestFile(pTask, hVfsIos, pszName);
2192 hVfsIos = NIL_RTVFSIOSTREAM; /*consumed*/
2193 cLeftToFind--;
2194 }
2195 else
2196 LogRel(("i_readFSOVA: '%s' contains more than one manifest file ('%s'), picking the first one\n",
2197 pTask->locInfo.strPath.c_str(), pszName));
2198 }
2199 /* Check for signature. */
2200 else if (RTStrICmp(pszSuffix + 1, "cert") == 0)
2201 {
2202 if (!m->fSignerCertLoaded)
2203 {
2204 hrc = i_readSignatureFile(pTask, hVfsIos, pszName);
2205 hVfsIos = NIL_RTVFSIOSTREAM; /*consumed*/
2206 cLeftToFind--;
2207 }
2208 else
2209 LogRel(("i_readFSOVA: '%s' contains more than one signature file ('%s'), picking the first one\n",
2210 pTask->locInfo.strPath.c_str(), pszName));
2211 }
2212 else
2213 AssertFailed();
2214 if (hVfsIos != NIL_RTVFSIOSTREAM)
2215 RTVfsIoStrmRelease(hVfsIos);
2216 }
2217 }
2218 }
2219 RTVfsObjRelease(hVfsObj);
2220 RTStrFree(pszName);
2221 } while (cLeftToFind > 0 && SUCCEEDED(hrc));
2222
2223 RTVfsFsStrmRelease(hVfsFssOva);
2224 RTStrFree(pszOvfNameBase);
2225
2226 /*
2227 * Check that we found and OVF file.
2228 */
2229 if (SUCCEEDED(hrc) && !pszOvfNameBase)
2230 hrc = setError(VBOX_E_FILE_ERROR, tr("OVA '%s' does not contain an .ovf-file"), pTask->locInfo.strPath.c_str());
2231 if (SUCCEEDED(hrc))
2232 {
2233 /*
2234 * Do tail processing (check the signature).
2235 */
2236 hrc = i_readTailProcessing(pTask);
2237 }
2238 LogFlowFunc(("returns %Rhrc\n", hrc));
2239 return hrc;
2240}
2241
2242/**
2243 * Reads & parses the OVF file.
2244 *
2245 * @param pTask The read task.
2246 * @param hVfsIosOvf The I/O stream for the OVF. The reference is
2247 * always consumed.
2248 * @param pszManifestEntry The manifest entry name.
2249 * @returns COM status code, error info set.
2250 * @throws Nothing
2251 */
2252HRESULT Appliance::i_readOVFFile(TaskOVF *pTask, RTVFSIOSTREAM hVfsIosOvf, const char *pszManifestEntry)
2253{
2254 LogFlowFunc(("%s[%s]\n", pTask->locInfo.strPath.c_str(), pszManifestEntry));
2255
2256 /*
2257 * Set the OVF manifest entry name (needed for tweaking the manifest
2258 * validation during import).
2259 */
2260 try { m->strOvfManifestEntry = pszManifestEntry; }
2261 catch (...) { return E_OUTOFMEMORY; }
2262
2263 /*
2264 * Set up digest calculation.
2265 */
2266 hVfsIosOvf = i_manifestSetupDigestCalculationForGivenIoStream(hVfsIosOvf, pszManifestEntry);
2267 if (hVfsIosOvf == NIL_RTVFSIOSTREAM)
2268 return VBOX_E_FILE_ERROR;
2269
2270 /*
2271 * Read the OVF into a memory buffer and parse it.
2272 */
2273 void *pvBufferedOvf;
2274 size_t cbBufferedOvf;
2275 int vrc = RTVfsIoStrmReadAll(hVfsIosOvf, &pvBufferedOvf, &cbBufferedOvf);
2276 uint32_t cRefs = RTVfsIoStrmRelease(hVfsIosOvf); /* consumes stream handle. */
2277 NOREF(cRefs);
2278 Assert(cRefs == 0);
2279 if (RT_FAILURE(vrc))
2280 return setErrorVrc(vrc, tr("Could not read the OVF file for '%s' (%Rrc)"), pTask->locInfo.strPath.c_str(), vrc);
2281
2282 HRESULT hrc;
2283 try
2284 {
2285 m->pReader = new ovf::OVFReader(pvBufferedOvf, cbBufferedOvf, pTask->locInfo.strPath);
2286 hrc = S_OK;
2287 }
2288 catch (RTCError &rXcpt) // includes all XML exceptions
2289 {
2290 hrc = setError(VBOX_E_FILE_ERROR, rXcpt.what());
2291 }
2292 catch (HRESULT aRC)
2293 {
2294 hrc = aRC;
2295 }
2296 catch (...)
2297 {
2298 hrc = E_FAIL;
2299 }
2300 LogFlowFunc(("OVFReader(%s) -> rc=%Rhrc\n", pTask->locInfo.strPath.c_str(), hrc));
2301
2302 RTVfsIoStrmReadAllFree(pvBufferedOvf, cbBufferedOvf);
2303 if (SUCCEEDED(hrc))
2304 {
2305 /*
2306 * If we see an OVF v2.0 envelope, select only the SHA-256 digest.
2307 */
2308 if ( !m->fDeterminedDigestTypes
2309 && m->pReader->m_envelopeData.getOVFVersion() == ovf::OVFVersion_2_0)
2310 m->fDigestTypes &= ~RTMANIFEST_ATTR_SHA256;
2311 }
2312
2313 return hrc;
2314}
2315
2316/**
2317 * Reads & parses the manifest file.
2318 *
2319 * @param pTask The read task.
2320 * @param hVfsIosMf The I/O stream for the manifest file. The
2321 * reference is always consumed.
2322 * @param pszSubFileNm The manifest filename (no path) for error
2323 * messages and logging.
2324 * @returns COM status code, error info set.
2325 * @throws Nothing
2326 */
2327HRESULT Appliance::i_readManifestFile(TaskOVF *pTask, RTVFSIOSTREAM hVfsIosMf, const char *pszSubFileNm)
2328{
2329 LogFlowFunc(("%s[%s]\n", pTask->locInfo.strPath.c_str(), pszSubFileNm));
2330
2331 /*
2332 * Copy the manifest into a memory backed file so we can later do signature
2333 * validation indepentend of the algorithms used by the signature.
2334 */
2335 int vrc = RTVfsMemorizeIoStreamAsFile(hVfsIosMf, RTFILE_O_READ, &m->hMemFileTheirManifest);
2336 RTVfsIoStrmRelease(hVfsIosMf); /* consumes stream handle. */
2337 if (RT_FAILURE(vrc))
2338 return setErrorVrc(vrc, tr("Error reading the manifest file '%s' for '%s' (%Rrc)"),
2339 pszSubFileNm, pTask->locInfo.strPath.c_str(), vrc);
2340
2341 /*
2342 * Parse the manifest.
2343 */
2344 Assert(m->hTheirManifest == NIL_RTMANIFEST);
2345 vrc = RTManifestCreate(0 /*fFlags*/, &m->hTheirManifest);
2346 AssertStmt(RT_SUCCESS(vrc), Global::vboxStatusCodeToCOM(vrc));
2347
2348 char szErr[256];
2349 RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(m->hMemFileTheirManifest);
2350 vrc = RTManifestReadStandardEx(m->hTheirManifest, hVfsIos, szErr, sizeof(szErr));
2351 RTVfsIoStrmRelease(hVfsIos);
2352 if (RT_FAILURE(vrc))
2353 throw setErrorVrc(vrc, tr("Failed to parse manifest file '%s' for '%s' (%Rrc): %s"),
2354 pszSubFileNm, pTask->locInfo.strPath.c_str(), vrc, szErr);
2355
2356 /*
2357 * Check which digest files are used.
2358 * Note! the file could be empty, in which case fDigestTypes is set to 0.
2359 */
2360 vrc = RTManifestQueryAllAttrTypes(m->hTheirManifest, true /*fEntriesOnly*/, &m->fDigestTypes);
2361 AssertRCReturn(vrc, Global::vboxStatusCodeToCOM(vrc));
2362 m->fDeterminedDigestTypes = true;
2363
2364 return S_OK;
2365}
2366
2367/**
2368 * Reads the signature & certificate file.
2369 *
2370 * @param pTask The read task.
2371 * @param hVfsIosCert The I/O stream for the signature file. The
2372 * reference is always consumed.
2373 * @param pszSubFileNm The signature filename (no path) for error
2374 * messages and logging. Used to construct
2375 * .mf-file name.
2376 * @returns COM status code, error info set.
2377 * @throws Nothing
2378 */
2379HRESULT Appliance::i_readSignatureFile(TaskOVF *pTask, RTVFSIOSTREAM hVfsIosCert, const char *pszSubFileNm)
2380{
2381 LogFlowFunc(("%s[%s]\n", pTask->locInfo.strPath.c_str(), pszSubFileNm));
2382
2383 /*
2384 * Construct the manifest filename from pszSubFileNm.
2385 */
2386 Utf8Str strManifestName;
2387 try
2388 {
2389 const char *pszSuffix = strrchr(pszSubFileNm, '.');
2390 AssertReturn(pszSuffix, E_FAIL);
2391 strManifestName = Utf8Str(pszSubFileNm, pszSuffix - pszSubFileNm);
2392 strManifestName.append(".mf");
2393 }
2394 catch (...)
2395 {
2396 return E_OUTOFMEMORY;
2397 }
2398
2399 /*
2400 * Copy the manifest into a memory buffer. We'll do the signature processing
2401 * later to not force any specific order in the OVAs or any other archive we
2402 * may be accessing later.
2403 */
2404 void *pvSignature;
2405 size_t cbSignature;
2406 int vrc = RTVfsIoStrmReadAll(hVfsIosCert, &pvSignature, &cbSignature);
2407 RTVfsIoStrmRelease(hVfsIosCert); /* consumes stream handle. */
2408 if (RT_FAILURE(vrc))
2409 return setErrorVrc(vrc, tr("Error reading the signature file '%s' for '%s' (%Rrc)"),
2410 pszSubFileNm, pTask->locInfo.strPath.c_str(), vrc);
2411
2412 /*
2413 * Parse the signing certificate. Unlike the manifest parser we use below,
2414 * this API ignores parse of the file that aren't relevant.
2415 */
2416 RTERRINFOSTATIC StaticErrInfo;
2417 vrc = RTCrX509Certificate_ReadFromBuffer(&m->SignerCert, pvSignature, cbSignature,
2418 RTCRX509CERT_READ_F_PEM_ONLY,
2419 &g_RTAsn1DefaultAllocator, RTErrInfoInitStatic(&StaticErrInfo), pszSubFileNm);
2420 HRESULT hrc;
2421 if (RT_SUCCESS(vrc))
2422 {
2423 m->fSignerCertLoaded = true;
2424 m->fCertificateIsSelfSigned = RTCrX509Certificate_IsSelfSigned(&m->SignerCert);
2425
2426 /*
2427 * Find the start of the certificate part of the file, so we can avoid
2428 * upsetting the manifest parser with it.
2429 */
2430 char *pszSplit = (char *)RTCrPemFindFirstSectionInContent(pvSignature, cbSignature,
2431 g_aRTCrX509CertificateMarkers, g_cRTCrX509CertificateMarkers);
2432 if (pszSplit)
2433 while ( pszSplit != (char *)pvSignature
2434 && pszSplit[-1] != '\n'
2435 && pszSplit[-1] != '\r')
2436 pszSplit--;
2437 else
2438 {
2439 AssertLogRelMsgFailed(("Failed to find BEGIN CERTIFICATE markers in '%s'::'%s' - impossible unless it's a DER encoded certificate!",
2440 pTask->locInfo.strPath.c_str(), pszSubFileNm));
2441 pszSplit = (char *)pvSignature + cbSignature;
2442 }
2443 *pszSplit = '\0';
2444
2445 /*
2446 * Now, read the manifest part. We use the IPRT manifest reader here
2447 * to avoid duplicating code and be somewhat flexible wrt the digest
2448 * type choosen by the signer.
2449 */
2450 RTMANIFEST hSignedDigestManifest;
2451 vrc = RTManifestCreate(0 /*fFlags*/, &hSignedDigestManifest);
2452 if (RT_SUCCESS(vrc))
2453 {
2454 RTVFSIOSTREAM hVfsIosTmp;
2455 vrc = RTVfsIoStrmFromBuffer(RTFILE_O_READ, pvSignature, pszSplit - (char *)pvSignature, &hVfsIosTmp);
2456 if (RT_SUCCESS(vrc))
2457 {
2458 vrc = RTManifestReadStandardEx(hSignedDigestManifest, hVfsIosTmp, StaticErrInfo.szMsg, sizeof(StaticErrInfo.szMsg));
2459 RTVfsIoStrmRelease(hVfsIosTmp);
2460 if (RT_SUCCESS(vrc))
2461 {
2462 /*
2463 * Get signed digest, we prefer SHA-2, so explicitly query those first.
2464 */
2465 uint32_t fDigestType;
2466 char szSignedDigest[_8K + 1];
2467 vrc = RTManifestEntryQueryAttr(hSignedDigestManifest, strManifestName.c_str(), NULL,
2468 RTMANIFEST_ATTR_SHA512 | RTMANIFEST_ATTR_SHA256,
2469 szSignedDigest, sizeof(szSignedDigest), &fDigestType);
2470 if (vrc == VERR_MANIFEST_ATTR_TYPE_NOT_FOUND)
2471 vrc = RTManifestEntryQueryAttr(hSignedDigestManifest, strManifestName.c_str(), NULL,
2472 RTMANIFEST_ATTR_ANY, szSignedDigest, sizeof(szSignedDigest), &fDigestType);
2473 if (RT_SUCCESS(vrc))
2474 {
2475 const char *pszSignedDigest = RTStrStrip(szSignedDigest);
2476 size_t cbSignedDigest = strlen(pszSignedDigest) / 2;
2477 uint8_t abSignedDigest[sizeof(szSignedDigest) / 2];
2478 vrc = RTStrConvertHexBytes(szSignedDigest, abSignedDigest, cbSignedDigest, 0 /*fFlags*/);
2479 if (RT_SUCCESS(vrc))
2480 {
2481 /*
2482 * Convert it to RTDIGESTTYPE_XXX and save the binary value for later use.
2483 */
2484 switch (fDigestType)
2485 {
2486 case RTMANIFEST_ATTR_SHA1: m->enmSignedDigestType = RTDIGESTTYPE_SHA1; break;
2487 case RTMANIFEST_ATTR_SHA256: m->enmSignedDigestType = RTDIGESTTYPE_SHA256; break;
2488 case RTMANIFEST_ATTR_SHA512: m->enmSignedDigestType = RTDIGESTTYPE_SHA512; break;
2489 case RTMANIFEST_ATTR_MD5: m->enmSignedDigestType = RTDIGESTTYPE_MD5; break;
2490 default: AssertFailed(); m->enmSignedDigestType = RTDIGESTTYPE_INVALID; break;
2491 }
2492 if (m->enmSignedDigestType != RTDIGESTTYPE_INVALID)
2493 {
2494 m->pbSignedDigest = (uint8_t *)RTMemDup(abSignedDigest, cbSignedDigest);
2495 m->cbSignedDigest = cbSignedDigest;
2496 hrc = S_OK;
2497 }
2498 else
2499 hrc = setError(E_FAIL, tr("Unsupported signed digest type (%#x)"), fDigestType);
2500 }
2501 else
2502 hrc = setErrorVrc(vrc, tr("Error reading signed manifest digest: %Rrc"), vrc);
2503 }
2504 else if (vrc == VERR_NOT_FOUND)
2505 hrc = setErrorVrc(vrc, tr("Could not locate signed digest for '%s' in the cert-file for '%s'"),
2506 strManifestName.c_str(), pTask->locInfo.strPath.c_str());
2507 else
2508 hrc = setErrorVrc(vrc, tr("RTManifestEntryQueryAttr failed unexpectedly: %Rrc"), vrc);
2509 }
2510 else
2511 hrc = setErrorVrc(vrc, tr("Error parsing the .cert-file for '%s': %s"),
2512 pTask->locInfo.strPath.c_str(), StaticErrInfo.szMsg);
2513 }
2514 else
2515 hrc = E_OUTOFMEMORY;
2516 RTManifestRelease(hSignedDigestManifest);
2517 }
2518 else
2519 hrc = E_OUTOFMEMORY;
2520 }
2521 else if (vrc == VERR_NOT_FOUND || vrc == VERR_EOF)
2522 hrc = setErrorBoth(E_FAIL, vrc, tr("Malformed .cert-file for '%s': Signer's certificate not found (%Rrc)"),
2523 pTask->locInfo.strPath.c_str(), vrc);
2524 else
2525 hrc = setErrorVrc(vrc, tr("Error reading the signer's certificate from '%s' for '%s' (%Rrc): %s"),
2526 pszSubFileNm, pTask->locInfo.strPath.c_str(), vrc, StaticErrInfo.Core.pszMsg);
2527
2528 RTVfsIoStrmReadAllFree(pvSignature, cbSignature);
2529 LogFlowFunc(("returns %Rhrc (%Rrc)\n", hrc, vrc));
2530 return hrc;
2531}
2532
2533
2534/**
2535 * Does tail processing after the files have been read in.
2536 *
2537 * @param pTask The read task.
2538 * @returns COM status.
2539 * @throws Nothing!
2540 */
2541HRESULT Appliance::i_readTailProcessing(TaskOVF *pTask)
2542{
2543 /*
2544 * Parse and validate the signature file.
2545 *
2546 * The signature file has two parts, manifest part and a PEM encoded
2547 * certificate. The former contains an entry for the manifest file with a
2548 * digest that is encrypted with the certificate in the latter part.
2549 */
2550 if (m->pbSignedDigest)
2551 {
2552 /* Since we're validating the digest of the manifest, there have to be
2553 a manifest. We cannot allow a the manifest to be missing. */
2554 if (m->hMemFileTheirManifest == NIL_RTVFSFILE)
2555 return setError(VBOX_E_FILE_ERROR, tr("Found .cert-file but no .mf-file for '%s'"), pTask->locInfo.strPath.c_str());
2556
2557 /*
2558 * Validate the signed digest.
2559 *
2560 * It's possible we should allow the user to ignore signature
2561 * mismatches, but for now it is a solid show stopper.
2562 */
2563 HRESULT hrc;
2564 RTERRINFOSTATIC StaticErrInfo;
2565
2566 /* Calc the digest of the manifest using the algorithm found above. */
2567 RTCRDIGEST hDigest;
2568 int vrc = RTCrDigestCreateByType(&hDigest, m->enmSignedDigestType);
2569 if (RT_SUCCESS(vrc))
2570 {
2571 vrc = RTCrDigestUpdateFromVfsFile(hDigest, m->hMemFileTheirManifest, true /*fRewindFile*/);
2572 if (RT_SUCCESS(vrc))
2573 {
2574 /* Compare the signed digest with the one we just calculated. (This
2575 API will do the verification twice, once using IPRT's own crypto
2576 and once using OpenSSL. Both must OK it for success.) */
2577 vrc = RTCrPkixPubKeyVerifySignedDigestByCertPubKeyInfo(&m->SignerCert.TbsCertificate.SubjectPublicKeyInfo,
2578 m->pbSignedDigest, m->cbSignedDigest, hDigest,
2579 RTErrInfoInitStatic(&StaticErrInfo));
2580 if (RT_SUCCESS(vrc))
2581 {
2582 m->fSignatureValid = true;
2583 hrc = S_OK;
2584 }
2585 else if (vrc == VERR_CR_PKIX_SIGNATURE_MISMATCH)
2586 hrc = setErrorVrc(vrc, tr("The manifest signature does not match"));
2587 else
2588 hrc = setErrorVrc(vrc,
2589 tr("Error validating the manifest signature (%Rrc, %s)"), vrc, StaticErrInfo.Core.pszMsg);
2590 }
2591 else
2592 hrc = setErrorVrc(vrc, tr("RTCrDigestUpdateFromVfsFile failed: %Rrc"), vrc);
2593 RTCrDigestRelease(hDigest);
2594 }
2595 else
2596 hrc = setErrorVrc(vrc, tr("RTCrDigestCreateByType failed: %Rrc"), vrc);
2597
2598 /*
2599 * Validate the certificate.
2600 *
2601 * We don't fail here on if we cannot validate the certificate, we postpone
2602 * that till the import stage, so that we can allow the user to ignore it.
2603 *
2604 * The certificate validity time is deliberately left as warnings as the
2605 * OVF specification does not provision for any timestamping of the
2606 * signature. This is course a security concern, but the whole signing
2607 * of OVFs is currently weirdly trusting (self signed * certs), so this
2608 * is the least of our current problems.
2609 *
2610 * While we try build and verify certificate paths properly, the
2611 * "neighbours" quietly ignores this and seems only to check the signature
2612 * and not whether the certificate is trusted. Also, we don't currently
2613 * complain about self-signed certificates either (ditto "neighbours").
2614 * The OVF creator is also a bit restricted wrt to helping us build the
2615 * path as he cannot supply intermediate certificates. Anyway, we issue
2616 * warnings (goes to /dev/null, am I right?) for self-signed certificates
2617 * and certificates we cannot build and verify a root path for.
2618 *
2619 * (The OVF sillibuggers should've used PKCS#7, CMS or something else
2620 * that's already been standardized instead of combining manifests with
2621 * certificate PEM files in some very restrictive manner! I wonder if
2622 * we could add a PKCS#7 section to the .cert file in addition to the CERT
2623 * and manifest stuff dictated by the standard. Would depend on how others
2624 * deal with it.)
2625 */
2626 Assert(!m->fCertificateValid);
2627 Assert(m->fCertificateMissingPath);
2628 Assert(!m->fCertificateValidTime);
2629 Assert(m->strCertError.isEmpty());
2630 Assert(m->fCertificateIsSelfSigned == RTCrX509Certificate_IsSelfSigned(&m->SignerCert));
2631
2632 HRESULT hrc2 = S_OK;
2633 if (m->fCertificateIsSelfSigned)
2634 {
2635 /*
2636 * It's a self signed certificate. We assume the frontend will
2637 * present this fact to the user and give a choice whether this
2638 * is acceptible. But, first make sure it makes internal sense.
2639 */
2640 m->fCertificateMissingPath = true; /** @todo need to check if the certificate is trusted by the system! */
2641 vrc = RTCrX509Certificate_VerifySignatureSelfSigned(&m->SignerCert, RTErrInfoInitStatic(&StaticErrInfo));
2642 if (RT_SUCCESS(vrc))
2643 {
2644 m->fCertificateValid = true;
2645
2646 /* Check whether the certificate is currently valid, just warn if not. */
2647 RTTIMESPEC Now;
2648 if (RTCrX509Validity_IsValidAtTimeSpec(&m->SignerCert.TbsCertificate.Validity, RTTimeNow(&Now)))
2649 {
2650 m->fCertificateValidTime = true;
2651 i_addWarning(tr("A self signed certificate was used to sign '%s'"), pTask->locInfo.strPath.c_str());
2652 }
2653 else
2654 i_addWarning(tr("Self signed certificate used to sign '%s' is not currently valid"),
2655 pTask->locInfo.strPath.c_str());
2656
2657 /* Just warn if it's not a CA. Self-signed certificates are
2658 hardly trustworthy to start with without the user's consent. */
2659 if ( !m->SignerCert.TbsCertificate.T3.pBasicConstraints
2660 || !m->SignerCert.TbsCertificate.T3.pBasicConstraints->CA.fValue)
2661 i_addWarning(tr("Self signed certificate used to sign '%s' is not marked as certificate authority (CA)"),
2662 pTask->locInfo.strPath.c_str());
2663 }
2664 else
2665 {
2666 try { m->strCertError = Utf8StrFmt(tr("Verification of the self signed certificate failed (%Rrc, %s)"),
2667 vrc, StaticErrInfo.Core.pszMsg); }
2668 catch (...) { AssertFailed(); }
2669 i_addWarning(tr("Verification of the self signed certificate used to sign '%s' failed (%Rrc): %s"),
2670 pTask->locInfo.strPath.c_str(), vrc, StaticErrInfo.Core.pszMsg);
2671 }
2672 }
2673 else
2674 {
2675 /*
2676 * The certificate is not self-signed. Use the system certificate
2677 * stores to try build a path that validates successfully.
2678 */
2679 RTCRX509CERTPATHS hCertPaths;
2680 vrc = RTCrX509CertPathsCreate(&hCertPaths, &m->SignerCert);
2681 if (RT_SUCCESS(vrc))
2682 {
2683 /* Get trusted certificates from the system and add them to the path finding mission. */
2684 RTCRSTORE hTrustedCerts;
2685 vrc = RTCrStoreCreateSnapshotOfUserAndSystemTrustedCAsAndCerts(&hTrustedCerts,
2686 RTErrInfoInitStatic(&StaticErrInfo));
2687 if (RT_SUCCESS(vrc))
2688 {
2689 vrc = RTCrX509CertPathsSetTrustedStore(hCertPaths, hTrustedCerts);
2690 if (RT_FAILURE(vrc))
2691 hrc2 = setErrorBoth(E_FAIL, vrc, tr("RTCrX509CertPathsSetTrustedStore failed (%Rrc)"), vrc);
2692 RTCrStoreRelease(hTrustedCerts);
2693 }
2694 else
2695 hrc2 = setErrorBoth(E_FAIL, vrc,
2696 tr("Failed to query trusted CAs and Certificates from the system and for the current user (%Rrc, %s)"),
2697 vrc, StaticErrInfo.Core.pszMsg);
2698
2699 /* Add untrusted intermediate certificates. */
2700 if (RT_SUCCESS(vrc))
2701 {
2702 /// @todo RTCrX509CertPathsSetUntrustedStore(hCertPaths, hAdditionalCerts);
2703 /// By scanning for additional certificates in the .cert file? It would be
2704 /// convenient to be able to supply intermediate certificates for the user,
2705 /// right? Or would that be unacceptable as it may weaken security?
2706 ///
2707 /// Anyway, we should look for intermediate certificates on the system, at
2708 /// least.
2709 }
2710 if (RT_SUCCESS(vrc))
2711 {
2712 /*
2713 * Do the building and verification of certificate paths.
2714 */
2715 vrc = RTCrX509CertPathsBuild(hCertPaths, RTErrInfoInitStatic(&StaticErrInfo));
2716 if (RT_SUCCESS(vrc))
2717 {
2718 vrc = RTCrX509CertPathsValidateAll(hCertPaths, NULL, RTErrInfoInitStatic(&StaticErrInfo));
2719 if (RT_SUCCESS(vrc))
2720 {
2721 /*
2722 * Mark the certificate as good.
2723 */
2724 /** @todo check the certificate purpose? If so, share with self-signed. */
2725 m->fCertificateValid = true;
2726 m->fCertificateMissingPath = false;
2727
2728 /*
2729 * We add a warning if the certificate path isn't valid at the current
2730 * time. Since the time is only considered during path validation and we
2731 * can repeat the validation process (but not building), it's easy to check.
2732 */
2733 RTTIMESPEC Now;
2734 vrc = RTCrX509CertPathsSetValidTimeSpec(hCertPaths, RTTimeNow(&Now));
2735 if (RT_SUCCESS(vrc))
2736 {
2737 vrc = RTCrX509CertPathsValidateAll(hCertPaths, NULL, RTErrInfoInitStatic(&StaticErrInfo));
2738 if (RT_SUCCESS(vrc))
2739 m->fCertificateValidTime = true;
2740 else
2741 i_addWarning(tr("The certificate used to sign '%s' (or a certificate in the path) is not currently valid (%Rrc)"),
2742 pTask->locInfo.strPath.c_str(), vrc);
2743 }
2744 else
2745 hrc2 = setErrorVrc(vrc, "RTCrX509CertPathsSetValidTimeSpec failed: %Rrc", vrc);
2746 }
2747 else if (vrc == VERR_CR_X509_CPV_NO_TRUSTED_PATHS)
2748 {
2749 m->fCertificateValid = true;
2750 i_addWarning(tr("No trusted certificate paths"));
2751
2752 /* Add another warning if the pathless certificate is not valid at present. */
2753 RTTIMESPEC Now;
2754 if (RTCrX509Validity_IsValidAtTimeSpec(&m->SignerCert.TbsCertificate.Validity, RTTimeNow(&Now)))
2755 m->fCertificateValidTime = true;
2756 else
2757 i_addWarning(tr("The certificate used to sign '%s' is not currently valid"),
2758 pTask->locInfo.strPath.c_str());
2759 }
2760 else
2761 hrc2 = setErrorBoth(E_FAIL, vrc, tr("Certificate path validation failed (%Rrc, %s)"),
2762 vrc, StaticErrInfo.Core.pszMsg);
2763 }
2764 else
2765 hrc2 = setErrorBoth(E_FAIL, vrc, tr("Certificate path building failed (%Rrc, %s)"),
2766 vrc, StaticErrInfo.Core.pszMsg);
2767 }
2768 RTCrX509CertPathsRelease(hCertPaths);
2769 }
2770 else
2771 hrc2 = setErrorVrc(vrc, tr("RTCrX509CertPathsCreate failed: %Rrc"), vrc);
2772 }
2773
2774 /* Merge statuses from signature and certificate validation, prefering the signature one. */
2775 if (SUCCEEDED(hrc) && FAILED(hrc2))
2776 hrc = hrc2;
2777 if (FAILED(hrc))
2778 return hrc;
2779 }
2780
2781 /** @todo provide details about the signatory, signature, etc. */
2782 if (m->fSignerCertLoaded)
2783 {
2784 m->ptrCertificateInfo.createObject();
2785 m->ptrCertificateInfo->initCertificate(&m->SignerCert,
2786 m->fCertificateValid && !m->fCertificateMissingPath,
2787 !m->fCertificateValidTime);
2788 }
2789
2790 /*
2791 * If there is a manifest, check that the OVF digest matches up (if present).
2792 */
2793
2794 NOREF(pTask);
2795 return S_OK;
2796}
2797
2798
2799
2800/*******************************************************************************
2801 * Import stuff
2802 ******************************************************************************/
2803
2804/**
2805 * Implementation for importing OVF data into VirtualBox. This starts a new thread which will call
2806 * Appliance::taskThreadImportOrExport().
2807 *
2808 * This creates one or more new machines according to the VirtualSystemScription instances created by
2809 * Appliance::Interpret().
2810 *
2811 * This is in a separate private method because it is used from one location:
2812 *
2813 * 1) from the public Appliance::ImportMachines().
2814 *
2815 * @param locInfo
2816 * @param progress
2817 * @return
2818 */
2819HRESULT Appliance::i_importImpl(const LocationInfo &locInfo,
2820 ComObjPtr<Progress> &progress)
2821{
2822 HRESULT rc = S_OK;
2823
2824 SetUpProgressMode mode;
2825 if (locInfo.storageType == VFSType_File)
2826 mode = ImportFile;
2827 else if (locInfo.storageType == VFSType_Cloud)
2828 mode = ImportCloud;
2829 else
2830 mode = ImportS3;
2831
2832 /* Initialize our worker task */
2833 TaskOVF* ovfTask = NULL;
2834 TaskCloud* cloudTask = NULL;
2835 try
2836 {
2837 if (mode == ImportFile)
2838 {
2839 rc = i_setUpProgress(progress,
2840 BstrFmt(tr("Importing appliance '%s'"), locInfo.strPath.c_str()),
2841 mode);
2842 if (FAILED(rc)) throw rc;
2843
2844 ovfTask = new TaskOVF(this, TaskOVF::Import, locInfo, progress);
2845 rc = ovfTask->createThread();
2846 }
2847 else if (mode == ImportCloud)
2848 {
2849 progress.createObject();
2850 if (locInfo.strProvider.equals("OCI"))
2851 {
2852 /*
2853 * 1. Create a custom image from the instance
2854 * - 2 operations (starting and waiting)
2855 * 2. Import the custom image into the Object Storage (OCI format - TAR file with QCOW2 image and JSON file)
2856 * - 2 operations (starting and waiting)
2857 * 3. Download the object from the Object Storage
2858 * - 2 operations (starting and waiting)
2859 * 4. Open the object, extract QCOW2 image and convert one QCOW2->VDI
2860 * - 1 operation (extracting and conversion are piped)
2861 * 5. Create VM with user settings and attach the converted image to VM
2862 * - 1 operation.
2863 * sum up = 2+2+2+1+1 = 8 op
2864 */
2865
2866 /*
2867 * See src/VBox/ExtPacks/Puel/OCI/OCICloudClient.h.
2868 * Weight of cloud import operations (1-3 items from above):
2869 * Total = 750 = 10+40+50+50+200x2+200.
2870 *
2871 * Weight of local import operations (4-5 items from above):
2872 * Total = 250 = 200 (extract and convert) + 50 (create VM, attach disks)
2873 */
2874 progress->init(mVirtualBox, static_cast<IAppliance*>(this),
2875 Bstr("Importing VM from Cloud...").raw(),
2876 TRUE /* aCancelable */,
2877 8, // ULONG cOperations,
2878 1000, // ULONG ulTotalOperationsWeight,
2879 Bstr("Importing VM from Cloud...").raw(), // aFirstOperationDescription
2880 10); // ULONG ulFirstOperationWeight
2881 }
2882 else
2883 return setErrorVrc(VBOX_E_NOT_SUPPORTED,
2884 tr("Only \"OCI\" cloud provider is supported for now. \"%s\" isn't supported."),
2885 locInfo.strProvider.c_str());
2886
2887 cloudTask = new TaskCloud(this, TaskCloud::Import, locInfo, progress);
2888 rc = cloudTask->createThread();
2889 if (FAILED(rc)) throw rc;
2890 }
2891 }
2892 catch (HRESULT aRc)
2893 {
2894 rc = aRc;
2895 }
2896 catch (...)
2897 {
2898 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
2899 tr("Could not create a task object for importing appliance into VirtualBox"));
2900 }
2901
2902 if (FAILED(rc))
2903 {
2904 if (ovfTask)
2905 delete ovfTask;
2906 if (cloudTask)
2907 delete cloudTask;
2908 throw rc;
2909 }
2910
2911 return rc;
2912}
2913
2914/**
2915 * Actual worker code for importing OVF data into VirtualBox.
2916 *
2917 * This is called from Appliance::taskThreadImportOrExport() and therefore runs
2918 * on the OVF import worker thread. This creates one or more new machines
2919 * according to the VirtualSystemScription instances created by
2920 * Appliance::Interpret().
2921 *
2922 * This runs in two contexts:
2923 *
2924 * 1) in a first worker thread; in that case, Appliance::ImportMachines() called
2925 * Appliance::i_importImpl();
2926 *
2927 * 2) in a second worker thread; in that case, Appliance::ImportMachines()
2928 * called Appliance::i_importImpl(), which called Appliance::i_importFSOVA(),
2929 * which called Appliance::i_importImpl(), which then called this again.
2930 *
2931 * @param pTask The OVF task data.
2932 * @return COM status code.
2933 */
2934HRESULT Appliance::i_importFS(TaskOVF *pTask)
2935{
2936 LogFlowFuncEnter();
2937 LogFlowFunc(("Appliance %p\n", this));
2938
2939 /* Change the appliance state so we can safely leave the lock while doing
2940 * time-consuming image imports; also the below method calls do all kinds of
2941 * locking which conflicts with the appliance object lock. */
2942 AutoWriteLock writeLock(this COMMA_LOCKVAL_SRC_POS);
2943 /* Check if the appliance is currently busy. */
2944 if (!i_isApplianceIdle())
2945 return E_ACCESSDENIED;
2946 /* Set the internal state to importing. */
2947 m->state = Data::ApplianceImporting;
2948
2949 HRESULT rc = S_OK;
2950
2951 /* Clear the list of imported machines, if any */
2952 m->llGuidsMachinesCreated.clear();
2953
2954 if (pTask->locInfo.strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
2955 rc = i_importFSOVF(pTask, writeLock);
2956 else
2957 rc = i_importFSOVA(pTask, writeLock);
2958 if (FAILED(rc))
2959 {
2960 /* With _whatever_ error we've had, do a complete roll-back of
2961 * machines and images we've created */
2962 writeLock.release();
2963 ErrorInfoKeeper eik;
2964 for (list<Guid>::iterator itID = m->llGuidsMachinesCreated.begin();
2965 itID != m->llGuidsMachinesCreated.end();
2966 ++itID)
2967 {
2968 Guid guid = *itID;
2969 Bstr bstrGuid = guid.toUtf16();
2970 ComPtr<IMachine> failedMachine;
2971 HRESULT rc2 = mVirtualBox->FindMachine(bstrGuid.raw(), failedMachine.asOutParam());
2972 if (SUCCEEDED(rc2))
2973 {
2974 SafeIfaceArray<IMedium> aMedia;
2975 rc2 = failedMachine->Unregister(CleanupMode_DetachAllReturnHardDisksOnly, ComSafeArrayAsOutParam(aMedia));
2976 ComPtr<IProgress> pProgress2;
2977 rc2 = failedMachine->DeleteConfig(ComSafeArrayAsInParam(aMedia), pProgress2.asOutParam());
2978 pProgress2->WaitForCompletion(-1);
2979 }
2980 }
2981 writeLock.acquire();
2982 }
2983
2984 /* Reset the state so others can call methods again */
2985 m->state = Data::ApplianceIdle;
2986
2987 LogFlowFunc(("rc=%Rhrc\n", rc));
2988 LogFlowFuncLeave();
2989 return rc;
2990}
2991
2992HRESULT Appliance::i_importFSOVF(TaskOVF *pTask, AutoWriteLockBase &rWriteLock)
2993{
2994 return i_importDoIt(pTask, rWriteLock);
2995}
2996
2997HRESULT Appliance::i_importFSOVA(TaskOVF *pTask, AutoWriteLockBase &rWriteLock)
2998{
2999 LogFlowFuncEnter();
3000
3001 /*
3002 * Open the tar file as file stream.
3003 */
3004 RTVFSIOSTREAM hVfsIosOva;
3005 int vrc = RTVfsIoStrmOpenNormal(pTask->locInfo.strPath.c_str(),
3006 RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsIosOva);
3007 if (RT_FAILURE(vrc))
3008 return setErrorVrc(vrc, tr("Error opening the OVA file '%s' (%Rrc)"), pTask->locInfo.strPath.c_str(), vrc);
3009
3010 RTVFSFSSTREAM hVfsFssOva;
3011 vrc = RTZipTarFsStreamFromIoStream(hVfsIosOva, 0 /*fFlags*/, &hVfsFssOva);
3012 RTVfsIoStrmRelease(hVfsIosOva);
3013 if (RT_FAILURE(vrc))
3014 return setErrorVrc(vrc, tr("Error reading the OVA file '%s' (%Rrc)"), pTask->locInfo.strPath.c_str(), vrc);
3015
3016 /*
3017 * Join paths with the i_importFSOVF code.
3018 *
3019 * Note! We don't need to skip the OVF, manifest or signature files, as the
3020 * i_importMachineGeneric, i_importVBoxMachine and i_importOpenSourceFile
3021 * code will deal with this (as there could be other files in the OVA
3022 * that we don't process, like 'de-DE-resources.xml' in EXAMPLE 1,
3023 * Appendix D.1, OVF v2.1.0).
3024 */
3025 HRESULT hrc = i_importDoIt(pTask, rWriteLock, hVfsFssOva);
3026
3027 RTVfsFsStrmRelease(hVfsFssOva);
3028
3029 LogFlowFunc(("returns %Rhrc\n", hrc));
3030 return hrc;
3031}
3032
3033/**
3034 * Does the actual importing after the caller has made the source accessible.
3035 *
3036 * @param pTask The import task.
3037 * @param rWriteLock The write lock the caller's caller is holding,
3038 * will be released for some reason.
3039 * @param hVfsFssOva The file system stream if OVA, NIL if not.
3040 * @returns COM status code.
3041 * @throws Nothing.
3042 */
3043HRESULT Appliance::i_importDoIt(TaskOVF *pTask, AutoWriteLockBase &rWriteLock, RTVFSFSSTREAM hVfsFssOva /*= NIL_RTVFSFSSTREAM*/)
3044{
3045 rWriteLock.release();
3046
3047 HRESULT hrc = E_FAIL;
3048 try
3049 {
3050 /*
3051 * Create the import stack for the rollback on errors.
3052 */
3053 ImportStack stack(pTask->locInfo, m->pReader->m_mapDisks, pTask->pProgress, hVfsFssOva);
3054
3055 try
3056 {
3057 /* Do the importing. */
3058 i_importMachines(stack);
3059
3060 /* We should've processed all the files now, so compare. */
3061 hrc = i_verifyManifestFile(stack);
3062
3063 /* If everything was successful so far check if some extension
3064 * pack wants to do file sanity checking. */
3065 if (SUCCEEDED(hrc))
3066 {
3067 /** @todo */;
3068 }
3069 }
3070 catch (HRESULT hrcXcpt)
3071 {
3072 hrc = hrcXcpt;
3073 }
3074 catch (...)
3075 {
3076 AssertFailed();
3077 hrc = E_FAIL;
3078 }
3079 if (FAILED(hrc))
3080 {
3081 /*
3082 * Restoring original UUID from OVF description file.
3083 * During import VBox creates new UUIDs for imported images and
3084 * assigns them to the images. In case of failure we have to restore
3085 * the original UUIDs because those new UUIDs are obsolete now and
3086 * won't be used anymore.
3087 */
3088 ErrorInfoKeeper eik; /* paranoia */
3089 list< ComObjPtr<VirtualSystemDescription> >::const_iterator itvsd;
3090 /* Iterate through all virtual systems of that appliance */
3091 for (itvsd = m->virtualSystemDescriptions.begin();
3092 itvsd != m->virtualSystemDescriptions.end();
3093 ++itvsd)
3094 {
3095 ComObjPtr<VirtualSystemDescription> vsdescThis = (*itvsd);
3096 settings::MachineConfigFile *pConfig = vsdescThis->m->pConfig;
3097 if(vsdescThis->m->pConfig!=NULL)
3098 stack.restoreOriginalUUIDOfAttachedDevice(pConfig);
3099 }
3100 }
3101 }
3102 catch (...)
3103 {
3104 hrc = E_FAIL;
3105 AssertFailed();
3106 }
3107
3108 rWriteLock.acquire();
3109 return hrc;
3110}
3111
3112/**
3113 * Undocumented, you figure it from the name.
3114 *
3115 * @returns Undocumented
3116 * @param stack Undocumented.
3117 */
3118HRESULT Appliance::i_verifyManifestFile(ImportStack &stack)
3119{
3120 LogFlowThisFuncEnter();
3121 HRESULT hrc;
3122 int vrc;
3123
3124 /*
3125 * No manifest is fine, it always matches.
3126 */
3127 if (m->hTheirManifest == NIL_RTMANIFEST)
3128 hrc = S_OK;
3129 else
3130 {
3131 /*
3132 * Hack: If the manifest we just read doesn't have a digest for the OVF, copy
3133 * it from the manifest we got from the caller.
3134 * @bugref{6022#c119}
3135 */
3136 if ( !RTManifestEntryExists(m->hTheirManifest, m->strOvfManifestEntry.c_str())
3137 && RTManifestEntryExists(m->hOurManifest, m->strOvfManifestEntry.c_str()) )
3138 {
3139 uint32_t fType = 0;
3140 char szDigest[512 + 1];
3141 vrc = RTManifestEntryQueryAttr(m->hOurManifest, m->strOvfManifestEntry.c_str(), NULL, RTMANIFEST_ATTR_ANY,
3142 szDigest, sizeof(szDigest), &fType);
3143 if (RT_SUCCESS(vrc))
3144 vrc = RTManifestEntrySetAttr(m->hTheirManifest, m->strOvfManifestEntry.c_str(),
3145 NULL /*pszAttr*/, szDigest, fType);
3146 if (RT_FAILURE(vrc))
3147 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Error fudging missing OVF digest in manifest: %Rrc"), vrc);
3148 }
3149
3150 /*
3151 * Compare with the digests we've created while read/processing the import.
3152 *
3153 * We specify the RTMANIFEST_EQUALS_IGN_MISSING_ATTRS to ignore attributes
3154 * (SHA1, SHA256, etc) that are only present in one of the manifests, as long
3155 * as each entry has at least one common attribute that we can check. This
3156 * is important for the OVF in OVAs, for which we generates several digests
3157 * since we don't know which are actually used in the manifest (OVF comes
3158 * first in an OVA, then manifest).
3159 */
3160 char szErr[256];
3161 vrc = RTManifestEqualsEx(m->hTheirManifest, m->hOurManifest, NULL /*papszIgnoreEntries*/,
3162 NULL /*papszIgnoreAttrs*/,
3163 RTMANIFEST_EQUALS_IGN_MISSING_ATTRS | RTMANIFEST_EQUALS_IGN_MISSING_ENTRIES_2ND,
3164 szErr, sizeof(szErr));
3165 if (RT_SUCCESS(vrc))
3166 hrc = S_OK;
3167 else
3168 hrc = setErrorVrc(vrc, tr("Digest mismatch (%Rrc): %s"), vrc, szErr);
3169 }
3170
3171 NOREF(stack);
3172 LogFlowThisFunc(("returns %Rhrc\n", hrc));
3173 return hrc;
3174}
3175
3176/**
3177 * Helper that converts VirtualSystem attachment values into VirtualBox attachment values.
3178 * Throws HRESULT values on errors!
3179 *
3180 * @param hdc in: the HardDiskController structure to attach to.
3181 * @param ulAddressOnParent in: the AddressOnParent parameter from OVF.
3182 * @param controllerName out: the name of the storage controller to attach to (e.g. "IDE").
3183 * @param lControllerPort out: the channel (controller port) of the controller to attach to.
3184 * @param lDevice out: the device number to attach to.
3185 */
3186void Appliance::i_convertDiskAttachmentValues(const ovf::HardDiskController &hdc,
3187 uint32_t ulAddressOnParent,
3188 Utf8Str &controllerName,
3189 int32_t &lControllerPort,
3190 int32_t &lDevice)
3191{
3192 Log(("Appliance::i_convertDiskAttachmentValues: hdc.system=%d, hdc.fPrimary=%d, ulAddressOnParent=%d\n",
3193 hdc.system,
3194 hdc.fPrimary,
3195 ulAddressOnParent));
3196
3197 switch (hdc.system)
3198 {
3199 case ovf::HardDiskController::IDE:
3200 // For the IDE bus, the port parameter can be either 0 or 1, to specify the primary
3201 // or secondary IDE controller, respectively. For the primary controller of the IDE bus,
3202 // the device number can be either 0 or 1, to specify the master or the slave device,
3203 // respectively. For the secondary IDE controller, the device number is always 1 because
3204 // the master device is reserved for the CD-ROM drive.
3205 controllerName = "IDE";
3206 switch (ulAddressOnParent)
3207 {
3208 case 0: // master
3209 if (!hdc.fPrimary)
3210 {
3211 // secondary master
3212 lControllerPort = (long)1;
3213 lDevice = (long)0;
3214 }
3215 else // primary master
3216 {
3217 lControllerPort = (long)0;
3218 lDevice = (long)0;
3219 }
3220 break;
3221
3222 case 1: // slave
3223 if (!hdc.fPrimary)
3224 {
3225 // secondary slave
3226 lControllerPort = (long)1;
3227 lDevice = (long)1;
3228 }
3229 else // primary slave
3230 {
3231 lControllerPort = (long)0;
3232 lDevice = (long)1;
3233 }
3234 break;
3235
3236 // used by older VBox exports
3237 case 2: // interpret this as secondary master
3238 lControllerPort = (long)1;
3239 lDevice = (long)0;
3240 break;
3241
3242 // used by older VBox exports
3243 case 3: // interpret this as secondary slave
3244 lControllerPort = (long)1;
3245 lDevice = (long)1;
3246 break;
3247
3248 default:
3249 throw setError(VBOX_E_NOT_SUPPORTED,
3250 tr("Invalid channel %RI16 specified; IDE controllers support only 0, 1 or 2"),
3251 ulAddressOnParent);
3252 break;
3253 }
3254 break;
3255
3256 case ovf::HardDiskController::SATA:
3257 controllerName = "SATA";
3258 lControllerPort = (long)ulAddressOnParent;
3259 lDevice = (long)0;
3260 break;
3261
3262 case ovf::HardDiskController::SCSI:
3263 {
3264 if(hdc.strControllerType.compare("lsilogicsas")==0)
3265 controllerName = "SAS";
3266 else
3267 controllerName = "SCSI";
3268 lControllerPort = (long)ulAddressOnParent;
3269 lDevice = (long)0;
3270 break;
3271 }
3272
3273 default: break;
3274 }
3275
3276 Log(("=> lControllerPort=%d, lDevice=%d\n", lControllerPort, lDevice));
3277}
3278
3279/**
3280 * Imports one image.
3281 *
3282 * This is common code shared between
3283 * -- i_importMachineGeneric() for the OVF case; in that case the information comes from
3284 * the OVF virtual systems;
3285 * -- i_importVBoxMachine(); in that case, the information comes from the <vbox:Machine>
3286 * tag.
3287 *
3288 * Both ways of describing machines use the OVF disk references section, so in both cases
3289 * the caller needs to pass in the ovf::DiskImage structure from ovfreader.cpp.
3290 *
3291 * As a result, in both cases, if di.strHref is empty, we create a new image as per the OVF
3292 * spec, even though this cannot really happen in the vbox:Machine case since such data
3293 * would never have been exported.
3294 *
3295 * This advances stack.pProgress by one operation with the image's weight.
3296 *
3297 * @param di ovfreader.cpp structure describing the image from the OVF that is to be imported
3298 * @param strDstPath Where to create the target image.
3299 * @param pTargetMedium out: The newly created target medium. This also gets pushed on stack.llHardDisksCreated for cleanup.
3300 * @param stack
3301 *
3302 * @throws HRESULT
3303 */
3304void Appliance::i_importOneDiskImage(const ovf::DiskImage &di,
3305 const Utf8Str &strDstPath,
3306 ComObjPtr<Medium> &pTargetMedium,
3307 ImportStack &stack)
3308{
3309 HRESULT rc;
3310
3311 Utf8Str strAbsDstPath;
3312 int vrc = RTPathAbsExCxx(strAbsDstPath, stack.strMachineFolder, strDstPath);
3313 AssertRCStmt(vrc, throw Global::vboxStatusCodeToCOM(vrc));
3314
3315 /* Get the system properties. */
3316 SystemProperties *pSysProps = mVirtualBox->i_getSystemProperties();
3317
3318 /* Keep the source file ref handy for later. */
3319 const Utf8Str &strSourceOVF = di.strHref;
3320
3321 /* Construct source file path */
3322 Utf8Str strSrcFilePath;
3323 if (stack.hVfsFssOva != NIL_RTVFSFSSTREAM)
3324 strSrcFilePath = strSourceOVF;
3325 else
3326 {
3327 strSrcFilePath = stack.strSourceDir;
3328 strSrcFilePath.append(RTPATH_SLASH_STR);
3329 strSrcFilePath.append(strSourceOVF);
3330 }
3331
3332 /* First of all check if the original (non-absolute) destination path is
3333 * a valid medium UUID. If so, the user wants to import the image into
3334 * an existing path. This is useful for iSCSI for example. */
3335 /** @todo r=klaus the code structure after this point is totally wrong,
3336 * full of unnecessary code duplication and other issues. 4.2 still had
3337 * the right structure for importing into existing medium objects, which
3338 * the current code can't possibly handle. */
3339 RTUUID uuid;
3340 vrc = RTUuidFromStr(&uuid, strDstPath.c_str());
3341 if (vrc == VINF_SUCCESS)
3342 {
3343 rc = mVirtualBox->i_findHardDiskById(Guid(uuid), true, &pTargetMedium);
3344 if (FAILED(rc)) throw rc;
3345 }
3346 else
3347 {
3348 RTVFSIOSTREAM hVfsIosSrc = NIL_RTVFSIOSTREAM;
3349
3350 /* check read file to GZIP compression */
3351 bool const fGzipped = di.strCompression.compare("gzip", Utf8Str::CaseInsensitive) == 0;
3352 Utf8Str strDeleteTemp;
3353 try
3354 {
3355 Utf8Str strTrgFormat = "VMDK";
3356 ComObjPtr<MediumFormat> trgFormat;
3357 Bstr bstrFormatName;
3358 ULONG lCabs = 0;
3359
3360 char *pszSuff = RTPathSuffix(strAbsDstPath.c_str());
3361 if (pszSuff != NULL)
3362 {
3363 /*
3364 * Figure out which format the user like to have. Default is VMDK
3365 * or it can be VDI if according command-line option is set
3366 */
3367
3368 /*
3369 * We need a proper target format
3370 * if target format has been changed by user via GUI import wizard
3371 * or via VBoxManage import command (option --importtovdi)
3372 * then we need properly process such format like ISO
3373 * Because there is no conversion ISO to VDI
3374 */
3375 trgFormat = pSysProps->i_mediumFormatFromExtension(++pszSuff);
3376 if (trgFormat.isNull())
3377 throw setError(E_FAIL, tr("Unsupported medium format for disk image '%s'"), di.strHref.c_str());
3378
3379 rc = trgFormat->COMGETTER(Name)(bstrFormatName.asOutParam());
3380 if (FAILED(rc)) throw rc;
3381
3382 strTrgFormat = Utf8Str(bstrFormatName);
3383
3384 if ( m->optListImport.contains(ImportOptions_ImportToVDI)
3385 && strTrgFormat.compare("RAW", Utf8Str::CaseInsensitive) != 0)
3386 {
3387 /* change the target extension */
3388 strTrgFormat = "vdi";
3389 trgFormat = pSysProps->i_mediumFormatFromExtension(strTrgFormat);
3390 strAbsDstPath.stripSuffix();
3391 strAbsDstPath.append(".");
3392 strAbsDstPath.append(strTrgFormat.c_str());
3393 }
3394
3395 /* Check the capabilities. We need create capabilities. */
3396 lCabs = 0;
3397 com::SafeArray <MediumFormatCapabilities_T> mediumFormatCap;
3398 rc = trgFormat->COMGETTER(Capabilities)(ComSafeArrayAsOutParam(mediumFormatCap));
3399
3400 if (FAILED(rc))
3401 throw rc;
3402
3403 for (ULONG j = 0; j < mediumFormatCap.size(); j++)
3404 lCabs |= mediumFormatCap[j];
3405
3406 if ( !(lCabs & MediumFormatCapabilities_CreateFixed)
3407 && !(lCabs & MediumFormatCapabilities_CreateDynamic) )
3408 throw setError(VBOX_E_NOT_SUPPORTED,
3409 tr("Could not find a valid medium format for the target disk '%s'"),
3410 strAbsDstPath.c_str());
3411 }
3412 else
3413 {
3414 throw setError(VBOX_E_FILE_ERROR,
3415 tr("The target disk '%s' has no extension "),
3416 strAbsDstPath.c_str(), VERR_INVALID_NAME);
3417 }
3418
3419 /*CD/DVD case*/
3420 if (strTrgFormat.compare("RAW", Utf8Str::CaseInsensitive) == 0)
3421 {
3422 try
3423 {
3424 if (fGzipped)
3425 i_importDecompressFile(stack, strSrcFilePath, strAbsDstPath, strSourceOVF.c_str());
3426 else
3427 i_importCopyFile(stack, strSrcFilePath, strAbsDstPath, strSourceOVF.c_str());
3428
3429 ComPtr<IMedium> pTmp;
3430 rc = mVirtualBox->OpenMedium(Bstr(strAbsDstPath).raw(),
3431 DeviceType_DVD,
3432 AccessMode_ReadWrite,
3433 false,
3434 pTmp.asOutParam());
3435 if (FAILED(rc))
3436 throw rc;
3437
3438 IMedium *iM = pTmp;
3439 pTargetMedium = static_cast<Medium*>(iM);
3440 }
3441 catch (HRESULT /*arc*/)
3442 {
3443 throw;
3444 }
3445
3446 /* Advance to the next operation. */
3447 /* operation's weight, as set up with the IProgress originally */
3448 stack.pProgress->SetNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"),
3449 RTPathFilename(strSourceOVF.c_str())).raw(),
3450 di.ulSuggestedSizeMB);
3451 }
3452 else/* HDD case*/
3453 {
3454 /* Create an IMedium object. */
3455 pTargetMedium.createObject();
3456
3457 rc = pTargetMedium->init(mVirtualBox,
3458 strTrgFormat,
3459 strAbsDstPath,
3460 Guid::Empty /* media registry: none yet */,
3461 DeviceType_HardDisk);
3462 if (FAILED(rc)) throw rc;
3463
3464 ComPtr<IProgress> pProgressImport;
3465 /* If strHref is empty we have to create a new file. */
3466 if (strSourceOVF.isEmpty())
3467 {
3468 com::SafeArray<MediumVariant_T> mediumVariant;
3469 mediumVariant.push_back(MediumVariant_Standard);
3470
3471 /* Kick off the creation of a dynamic growing disk image with the given capacity. */
3472 rc = pTargetMedium->CreateBaseStorage(di.iCapacity / _1M,
3473 ComSafeArrayAsInParam(mediumVariant),
3474 pProgressImport.asOutParam());
3475 if (FAILED(rc)) throw rc;
3476
3477 /* Advance to the next operation. */
3478 /* operation's weight, as set up with the IProgress originally */
3479 stack.pProgress->SetNextOperation(BstrFmt(tr("Creating disk image '%s'"),
3480 strAbsDstPath.c_str()).raw(),
3481 di.ulSuggestedSizeMB);
3482 }
3483 else
3484 {
3485 /* We need a proper source format description */
3486 /* Which format to use? */
3487 ComObjPtr<MediumFormat> srcFormat;
3488 rc = i_findMediumFormatFromDiskImage(di, srcFormat);
3489 if (FAILED(rc))
3490 throw setError(VBOX_E_NOT_SUPPORTED,
3491 tr("Could not find a valid medium format for the source disk '%s' "
3492 "Check correctness of the image format URL in the OVF description file "
3493 "or extension of the image"),
3494 RTPathFilename(strSourceOVF.c_str()));
3495
3496 /* If gzipped, decompress the GZIP file and save a new file in the target path */
3497 if (fGzipped)
3498 {
3499 Utf8Str strTargetFilePath(strAbsDstPath);
3500 strTargetFilePath.stripFilename();
3501 strTargetFilePath.append(RTPATH_SLASH_STR);
3502 strTargetFilePath.append("temp_");
3503 strTargetFilePath.append(RTPathFilename(strSrcFilePath.c_str()));
3504 strDeleteTemp = strTargetFilePath;
3505
3506 i_importDecompressFile(stack, strSrcFilePath, strTargetFilePath, strSourceOVF.c_str());
3507
3508 /* Correct the source and the target with the actual values */
3509 strSrcFilePath = strTargetFilePath;
3510
3511 /* Open the new source file. */
3512 vrc = RTVfsIoStrmOpenNormal(strSrcFilePath.c_str(), RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
3513 &hVfsIosSrc);
3514 if (RT_FAILURE(vrc))
3515 throw setErrorVrc(vrc, tr("Error opening decompressed image file '%s' (%Rrc)"),
3516 strSrcFilePath.c_str(), vrc);
3517 }
3518 else
3519 hVfsIosSrc = i_importOpenSourceFile(stack, strSrcFilePath, strSourceOVF.c_str());
3520
3521 /* Add a read ahead thread to try speed things up with concurrent reads and
3522 writes going on in different threads. */
3523 RTVFSIOSTREAM hVfsIosReadAhead;
3524 vrc = RTVfsCreateReadAheadForIoStream(hVfsIosSrc, 0 /*fFlags*/, 0 /*cBuffers=default*/,
3525 0 /*cbBuffers=default*/, &hVfsIosReadAhead);
3526 RTVfsIoStrmRelease(hVfsIosSrc);
3527 if (RT_FAILURE(vrc))
3528 throw setErrorVrc(vrc, tr("Error initializing read ahead thread for '%s' (%Rrc)"),
3529 strSrcFilePath.c_str(), vrc);
3530
3531 /* Start the source image cloning operation. */
3532 ComObjPtr<Medium> nullParent;
3533 ComObjPtr<Progress> pProgressImportTmp;
3534 rc = pProgressImportTmp.createObject();
3535 if (FAILED(rc)) throw rc;
3536 rc = pProgressImportTmp->init(mVirtualBox,
3537 static_cast<IAppliance*>(this),
3538 Utf8StrFmt(tr("Importing medium '%s'"),
3539 strAbsDstPath.c_str()),
3540 TRUE);
3541 if (FAILED(rc)) throw rc;
3542 pProgressImportTmp.queryInterfaceTo(pProgressImport.asOutParam());
3543 /* pProgressImportTmp is in parameter for Medium::i_importFile,
3544 * which is somewhat unusual and might be changed later. */
3545 rc = pTargetMedium->i_importFile(strSrcFilePath.c_str(),
3546 srcFormat,
3547 MediumVariant_Standard,
3548 hVfsIosReadAhead,
3549 nullParent,
3550 pProgressImportTmp,
3551 true /* aNotify */);
3552 RTVfsIoStrmRelease(hVfsIosReadAhead);
3553 hVfsIosSrc = NIL_RTVFSIOSTREAM;
3554 if (FAILED(rc))
3555 throw rc;
3556
3557 /* Advance to the next operation. */
3558 /* operation's weight, as set up with the IProgress originally */
3559 stack.pProgress->SetNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"),
3560 RTPathFilename(strSourceOVF.c_str())).raw(),
3561 di.ulSuggestedSizeMB);
3562 }
3563
3564 /* Now wait for the background import operation to complete; this throws
3565 * HRESULTs on error. */
3566 stack.pProgress->WaitForOtherProgressCompletion(pProgressImport, 0 /* indefinite wait */);
3567 }
3568 }
3569 catch (...)
3570 {
3571 if (strDeleteTemp.isNotEmpty())
3572 RTFileDelete(strDeleteTemp.c_str());
3573 throw;
3574 }
3575
3576 /* Make sure the source file is closed. */
3577 if (hVfsIosSrc != NIL_RTVFSIOSTREAM)
3578 RTVfsIoStrmRelease(hVfsIosSrc);
3579
3580 /*
3581 * Delete the temp gunzip result, if any.
3582 */
3583 if (strDeleteTemp.isNotEmpty())
3584 {
3585 vrc = RTFileDelete(strSrcFilePath.c_str());
3586 if (RT_FAILURE(vrc))
3587 setWarning(VBOX_E_FILE_ERROR,
3588 tr("Failed to delete the temporary file '%s' (%Rrc)"), strSrcFilePath.c_str(), vrc);
3589 }
3590 }
3591}
3592
3593/**
3594 * Imports one OVF virtual system (described by the given ovf::VirtualSystem and VirtualSystemDescription)
3595 * into VirtualBox by creating an IMachine instance, which is returned.
3596 *
3597 * This throws HRESULT error codes for anything that goes wrong, in which case the caller must clean
3598 * up any leftovers from this function. For this, the given ImportStack instance has received information
3599 * about what needs cleaning up (to support rollback).
3600 *
3601 * @param vsysThis OVF virtual system (machine) to import.
3602 * @param vsdescThis Matching virtual system description (machine) to import.
3603 * @param pNewMachine out: Newly created machine.
3604 * @param stack Cleanup stack for when this throws.
3605 */
3606void Appliance::i_importMachineGeneric(const ovf::VirtualSystem &vsysThis,
3607 ComObjPtr<VirtualSystemDescription> &vsdescThis,
3608 ComPtr<IMachine> &pNewMachine,
3609 ImportStack &stack)
3610{
3611 LogFlowFuncEnter();
3612 HRESULT rc;
3613
3614 // Get the instance of IGuestOSType which matches our string guest OS type so we
3615 // can use recommended defaults for the new machine where OVF doesn't provide any
3616 ComPtr<IGuestOSType> osType;
3617 rc = mVirtualBox->GetGuestOSType(Bstr(stack.strOsTypeVBox).raw(), osType.asOutParam());
3618 if (FAILED(rc)) throw rc;
3619
3620 /* Create the machine */
3621 SafeArray<BSTR> groups; /* no groups, or maybe one group... */
3622 if (!stack.strPrimaryGroup.isEmpty() && stack.strPrimaryGroup != "/")
3623 Bstr(stack.strPrimaryGroup).detachTo(groups.appendedRaw());
3624 rc = mVirtualBox->CreateMachine(Bstr(stack.strSettingsFilename).raw(),
3625 Bstr(stack.strNameVBox).raw(),
3626 ComSafeArrayAsInParam(groups),
3627 Bstr(stack.strOsTypeVBox).raw(),
3628 NULL, /* aCreateFlags */
3629 pNewMachine.asOutParam());
3630 if (FAILED(rc)) throw rc;
3631
3632 // set the description
3633 if (!stack.strDescription.isEmpty())
3634 {
3635 rc = pNewMachine->COMSETTER(Description)(Bstr(stack.strDescription).raw());
3636 if (FAILED(rc)) throw rc;
3637 }
3638
3639 // CPU count
3640 rc = pNewMachine->COMSETTER(CPUCount)(stack.cCPUs);
3641 if (FAILED(rc)) throw rc;
3642
3643 if (stack.fForceHWVirt)
3644 {
3645 rc = pNewMachine->SetHWVirtExProperty(HWVirtExPropertyType_Enabled, TRUE);
3646 if (FAILED(rc)) throw rc;
3647 }
3648
3649 // RAM
3650 rc = pNewMachine->COMSETTER(MemorySize)(stack.ulMemorySizeMB);
3651 if (FAILED(rc)) throw rc;
3652
3653 /* VRAM */
3654 /* Get the recommended VRAM for this guest OS type */
3655 ULONG vramVBox;
3656 rc = osType->COMGETTER(RecommendedVRAM)(&vramVBox);
3657 if (FAILED(rc)) throw rc;
3658
3659 /* Set the VRAM */
3660 rc = pNewMachine->COMSETTER(VRAMSize)(vramVBox);
3661 if (FAILED(rc)) throw rc;
3662
3663 // I/O APIC: Generic OVF has no setting for this. Enable it if we
3664 // import a Windows VM because if if Windows was installed without IOAPIC,
3665 // it will not mind finding an one later on, but if Windows was installed
3666 // _with_ an IOAPIC, it will bluescreen if it's not found
3667 if (!stack.fForceIOAPIC)
3668 {
3669 Bstr bstrFamilyId;
3670 rc = osType->COMGETTER(FamilyId)(bstrFamilyId.asOutParam());
3671 if (FAILED(rc)) throw rc;
3672 if (bstrFamilyId == "Windows")
3673 stack.fForceIOAPIC = true;
3674 }
3675
3676 if (stack.fForceIOAPIC)
3677 {
3678 ComPtr<IBIOSSettings> pBIOSSettings;
3679 rc = pNewMachine->COMGETTER(BIOSSettings)(pBIOSSettings.asOutParam());
3680 if (FAILED(rc)) throw rc;
3681
3682 rc = pBIOSSettings->COMSETTER(IOAPICEnabled)(TRUE);
3683 if (FAILED(rc)) throw rc;
3684 }
3685
3686 if (!stack.strAudioAdapter.isEmpty())
3687 if (stack.strAudioAdapter.compare("null", Utf8Str::CaseInsensitive) != 0)
3688 {
3689 uint32_t audio = RTStrToUInt32(stack.strAudioAdapter.c_str()); // should be 0 for AC97
3690 ComPtr<IAudioAdapter> audioAdapter;
3691 rc = pNewMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
3692 if (FAILED(rc)) throw rc;
3693 rc = audioAdapter->COMSETTER(Enabled)(true);
3694 if (FAILED(rc)) throw rc;
3695 rc = audioAdapter->COMSETTER(AudioController)(static_cast<AudioControllerType_T>(audio));
3696 if (FAILED(rc)) throw rc;
3697 }
3698
3699#ifdef VBOX_WITH_USB
3700 /* USB Controller */
3701 if (stack.fUSBEnabled)
3702 {
3703 ComPtr<IUSBController> usbController;
3704 rc = pNewMachine->AddUSBController(Bstr("OHCI").raw(), USBControllerType_OHCI, usbController.asOutParam());
3705 if (FAILED(rc)) throw rc;
3706 }
3707#endif /* VBOX_WITH_USB */
3708
3709 /* Change the network adapters */
3710 uint32_t maxNetworkAdapters = Global::getMaxNetworkAdapters(ChipsetType_PIIX3);
3711
3712 std::list<VirtualSystemDescriptionEntry*> vsdeNW = vsdescThis->i_findByType(VirtualSystemDescriptionType_NetworkAdapter);
3713 if (vsdeNW.empty())
3714 {
3715 /* No network adapters, so we have to disable our default one */
3716 ComPtr<INetworkAdapter> nwVBox;
3717 rc = pNewMachine->GetNetworkAdapter(0, nwVBox.asOutParam());
3718 if (FAILED(rc)) throw rc;
3719 rc = nwVBox->COMSETTER(Enabled)(false);
3720 if (FAILED(rc)) throw rc;
3721 }
3722 else if (vsdeNW.size() > maxNetworkAdapters)
3723 throw setError(VBOX_E_FILE_ERROR,
3724 tr("Too many network adapters: OVF requests %d network adapters, "
3725 "but VirtualBox only supports %d"),
3726 vsdeNW.size(), maxNetworkAdapters);
3727 else
3728 {
3729 list<VirtualSystemDescriptionEntry*>::const_iterator nwIt;
3730 size_t a = 0;
3731 for (nwIt = vsdeNW.begin();
3732 nwIt != vsdeNW.end();
3733 ++nwIt, ++a)
3734 {
3735 const VirtualSystemDescriptionEntry* pvsys = *nwIt;
3736
3737 const Utf8Str &nwTypeVBox = pvsys->strVBoxCurrent;
3738 uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str());
3739 ComPtr<INetworkAdapter> pNetworkAdapter;
3740 rc = pNewMachine->GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
3741 if (FAILED(rc)) throw rc;
3742 /* Enable the network card & set the adapter type */
3743 rc = pNetworkAdapter->COMSETTER(Enabled)(true);
3744 if (FAILED(rc)) throw rc;
3745 rc = pNetworkAdapter->COMSETTER(AdapterType)(static_cast<NetworkAdapterType_T>(tt1));
3746 if (FAILED(rc)) throw rc;
3747
3748 // default is NAT; change to "bridged" if extra conf says so
3749 if (pvsys->strExtraConfigCurrent.endsWith("type=Bridged", Utf8Str::CaseInsensitive))
3750 {
3751 /* Attach to the right interface */
3752 rc = pNetworkAdapter->COMSETTER(AttachmentType)(NetworkAttachmentType_Bridged);
3753 if (FAILED(rc)) throw rc;
3754 ComPtr<IHost> host;
3755 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam());
3756 if (FAILED(rc)) throw rc;
3757 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;
3758 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));
3759 if (FAILED(rc)) throw rc;
3760 // We search for the first host network interface which
3761 // is usable for bridged networking
3762 for (size_t j = 0;
3763 j < nwInterfaces.size();
3764 ++j)
3765 {
3766 HostNetworkInterfaceType_T itype;
3767 rc = nwInterfaces[j]->COMGETTER(InterfaceType)(&itype);
3768 if (FAILED(rc)) throw rc;
3769 if (itype == HostNetworkInterfaceType_Bridged)
3770 {
3771 Bstr name;
3772 rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam());
3773 if (FAILED(rc)) throw rc;
3774 /* Set the interface name to attach to */
3775 rc = pNetworkAdapter->COMSETTER(BridgedInterface)(name.raw());
3776 if (FAILED(rc)) throw rc;
3777 break;
3778 }
3779 }
3780 }
3781 /* Next test for host only interfaces */
3782 else if (pvsys->strExtraConfigCurrent.endsWith("type=HostOnly", Utf8Str::CaseInsensitive))
3783 {
3784 /* Attach to the right interface */
3785 rc = pNetworkAdapter->COMSETTER(AttachmentType)(NetworkAttachmentType_HostOnly);
3786 if (FAILED(rc)) throw rc;
3787 ComPtr<IHost> host;
3788 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam());
3789 if (FAILED(rc)) throw rc;
3790 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;
3791 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));
3792 if (FAILED(rc)) throw rc;
3793 // We search for the first host network interface which
3794 // is usable for host only networking
3795 for (size_t j = 0;
3796 j < nwInterfaces.size();
3797 ++j)
3798 {
3799 HostNetworkInterfaceType_T itype;
3800 rc = nwInterfaces[j]->COMGETTER(InterfaceType)(&itype);
3801 if (FAILED(rc)) throw rc;
3802 if (itype == HostNetworkInterfaceType_HostOnly)
3803 {
3804 Bstr name;
3805 rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam());
3806 if (FAILED(rc)) throw rc;
3807 /* Set the interface name to attach to */
3808 rc = pNetworkAdapter->COMSETTER(HostOnlyInterface)(name.raw());
3809 if (FAILED(rc)) throw rc;
3810 break;
3811 }
3812 }
3813 }
3814 /* Next test for internal interfaces */
3815 else if (pvsys->strExtraConfigCurrent.endsWith("type=Internal", Utf8Str::CaseInsensitive))
3816 {
3817 /* Attach to the right interface */
3818 rc = pNetworkAdapter->COMSETTER(AttachmentType)(NetworkAttachmentType_Internal);
3819 if (FAILED(rc)) throw rc;
3820 }
3821 /* Next test for Generic interfaces */
3822 else if (pvsys->strExtraConfigCurrent.endsWith("type=Generic", Utf8Str::CaseInsensitive))
3823 {
3824 /* Attach to the right interface */
3825 rc = pNetworkAdapter->COMSETTER(AttachmentType)(NetworkAttachmentType_Generic);
3826 if (FAILED(rc)) throw rc;
3827 }
3828
3829 /* Next test for NAT network interfaces */
3830 else if (pvsys->strExtraConfigCurrent.endsWith("type=NATNetwork", Utf8Str::CaseInsensitive))
3831 {
3832 /* Attach to the right interface */
3833 rc = pNetworkAdapter->COMSETTER(AttachmentType)(NetworkAttachmentType_NATNetwork);
3834 if (FAILED(rc)) throw rc;
3835 com::SafeIfaceArray<INATNetwork> nwNATNetworks;
3836 rc = mVirtualBox->COMGETTER(NATNetworks)(ComSafeArrayAsOutParam(nwNATNetworks));
3837 if (FAILED(rc)) throw rc;
3838 // Pick the first NAT network (if there is any)
3839 if (nwNATNetworks.size())
3840 {
3841 Bstr name;
3842 rc = nwNATNetworks[0]->COMGETTER(NetworkName)(name.asOutParam());
3843 if (FAILED(rc)) throw rc;
3844 /* Set the NAT network name to attach to */
3845 rc = pNetworkAdapter->COMSETTER(NATNetwork)(name.raw());
3846 if (FAILED(rc)) throw rc;
3847 break;
3848 }
3849 }
3850 }
3851 }
3852
3853 // Storage controller IDE
3854 std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE =
3855 vsdescThis->i_findByType(VirtualSystemDescriptionType_HardDiskControllerIDE);
3856 /*
3857 * In OVF (at least VMware's version of it), an IDE controller has two ports,
3858 * so VirtualBox's single IDE controller with two channels and two ports each counts as
3859 * two OVF IDE controllers -- so we accept one or two such IDE controllers
3860 */
3861 size_t cIDEControllers = vsdeHDCIDE.size();
3862 if (cIDEControllers > 2)
3863 throw setError(VBOX_E_FILE_ERROR,
3864 tr("Too many IDE controllers in OVF; import facility only supports two"));
3865 if (!vsdeHDCIDE.empty())
3866 {
3867 // one or two IDE controllers present in OVF: add one VirtualBox controller
3868 ComPtr<IStorageController> pController;
3869 rc = pNewMachine->AddStorageController(Bstr("IDE").raw(), StorageBus_IDE, pController.asOutParam());
3870 if (FAILED(rc)) throw rc;
3871
3872 const char *pcszIDEType = vsdeHDCIDE.front()->strVBoxCurrent.c_str();
3873 if (!strcmp(pcszIDEType, "PIIX3"))
3874 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX3);
3875 else if (!strcmp(pcszIDEType, "PIIX4"))
3876 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX4);
3877 else if (!strcmp(pcszIDEType, "ICH6"))
3878 rc = pController->COMSETTER(ControllerType)(StorageControllerType_ICH6);
3879 else
3880 throw setError(VBOX_E_FILE_ERROR,
3881 tr("Invalid IDE controller type \"%s\""),
3882 pcszIDEType);
3883 if (FAILED(rc)) throw rc;
3884 }
3885
3886 /* Storage controller SATA */
3887 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSATA =
3888 vsdescThis->i_findByType(VirtualSystemDescriptionType_HardDiskControllerSATA);
3889 if (vsdeHDCSATA.size() > 1)
3890 throw setError(VBOX_E_FILE_ERROR,
3891 tr("Too many SATA controllers in OVF; import facility only supports one"));
3892 if (!vsdeHDCSATA.empty())
3893 {
3894 ComPtr<IStorageController> pController;
3895 const Utf8Str &hdcVBox = vsdeHDCSATA.front()->strVBoxCurrent;
3896 if (hdcVBox == "AHCI")
3897 {
3898 rc = pNewMachine->AddStorageController(Bstr("SATA").raw(),
3899 StorageBus_SATA,
3900 pController.asOutParam());
3901 if (FAILED(rc)) throw rc;
3902 }
3903 else
3904 throw setError(VBOX_E_FILE_ERROR,
3905 tr("Invalid SATA controller type \"%s\""),
3906 hdcVBox.c_str());
3907 }
3908
3909 /* Storage controller SCSI */
3910 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSCSI =
3911 vsdescThis->i_findByType(VirtualSystemDescriptionType_HardDiskControllerSCSI);
3912 if (vsdeHDCSCSI.size() > 1)
3913 throw setError(VBOX_E_FILE_ERROR,
3914 tr("Too many SCSI controllers in OVF; import facility only supports one"));
3915 if (!vsdeHDCSCSI.empty())
3916 {
3917 ComPtr<IStorageController> pController;
3918 Utf8Str strName("SCSI");
3919 StorageBus_T busType = StorageBus_SCSI;
3920 StorageControllerType_T controllerType;
3921 const Utf8Str &hdcVBox = vsdeHDCSCSI.front()->strVBoxCurrent;
3922 if (hdcVBox == "LsiLogic")
3923 controllerType = StorageControllerType_LsiLogic;
3924 else if (hdcVBox == "LsiLogicSas")
3925 {
3926 // OVF treats LsiLogicSas as a SCSI controller but VBox considers it a class of its own
3927 strName = "SAS";
3928 busType = StorageBus_SAS;
3929 controllerType = StorageControllerType_LsiLogicSas;
3930 }
3931 else if (hdcVBox == "BusLogic")
3932 controllerType = StorageControllerType_BusLogic;
3933 else
3934 throw setError(VBOX_E_FILE_ERROR,
3935 tr("Invalid SCSI controller type \"%s\""),
3936 hdcVBox.c_str());
3937
3938 rc = pNewMachine->AddStorageController(Bstr(strName).raw(), busType, pController.asOutParam());
3939 if (FAILED(rc)) throw rc;
3940 rc = pController->COMSETTER(ControllerType)(controllerType);
3941 if (FAILED(rc)) throw rc;
3942 }
3943
3944 /* Storage controller SAS */
3945 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSAS =
3946 vsdescThis->i_findByType(VirtualSystemDescriptionType_HardDiskControllerSAS);
3947 if (vsdeHDCSAS.size() > 1)
3948 throw setError(VBOX_E_FILE_ERROR,
3949 tr("Too many SAS controllers in OVF; import facility only supports one"));
3950 if (!vsdeHDCSAS.empty())
3951 {
3952 ComPtr<IStorageController> pController;
3953 rc = pNewMachine->AddStorageController(Bstr(L"SAS").raw(),
3954 StorageBus_SAS,
3955 pController.asOutParam());
3956 if (FAILED(rc)) throw rc;
3957 rc = pController->COMSETTER(ControllerType)(StorageControllerType_LsiLogicSas);
3958 if (FAILED(rc)) throw rc;
3959 }
3960
3961 /* Now its time to register the machine before we add any storage devices */
3962 rc = mVirtualBox->RegisterMachine(pNewMachine);
3963 if (FAILED(rc)) throw rc;
3964
3965 // store new machine for roll-back in case of errors
3966 Bstr bstrNewMachineId;
3967 rc = pNewMachine->COMGETTER(Id)(bstrNewMachineId.asOutParam());
3968 if (FAILED(rc)) throw rc;
3969 Guid uuidNewMachine(bstrNewMachineId);
3970 m->llGuidsMachinesCreated.push_back(uuidNewMachine);
3971
3972 // Add floppies and CD-ROMs to the appropriate controllers.
3973 std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsdescThis->i_findByType(VirtualSystemDescriptionType_Floppy);
3974 if (vsdeFloppy.size() > 1)
3975 throw setError(VBOX_E_FILE_ERROR,
3976 tr("Too many floppy controllers in OVF; import facility only supports one"));
3977 std::list<VirtualSystemDescriptionEntry*> vsdeCDROM = vsdescThis->i_findByType(VirtualSystemDescriptionType_CDROM);
3978 if ( !vsdeFloppy.empty()
3979 || !vsdeCDROM.empty()
3980 )
3981 {
3982 // If there's an error here we need to close the session, so
3983 // we need another try/catch block.
3984
3985 try
3986 {
3987 // to attach things we need to open a session for the new machine
3988 rc = pNewMachine->LockMachine(stack.pSession, LockType_Write);
3989 if (FAILED(rc)) throw rc;
3990 stack.fSessionOpen = true;
3991
3992 ComPtr<IMachine> sMachine;
3993 rc = stack.pSession->COMGETTER(Machine)(sMachine.asOutParam());
3994 if (FAILED(rc)) throw rc;
3995
3996 // floppy first
3997 if (vsdeFloppy.size() == 1)
3998 {
3999 ComPtr<IStorageController> pController;
4000 rc = sMachine->AddStorageController(Bstr("Floppy").raw(),
4001 StorageBus_Floppy,
4002 pController.asOutParam());
4003 if (FAILED(rc)) throw rc;
4004
4005 Bstr bstrName;
4006 rc = pController->COMGETTER(Name)(bstrName.asOutParam());
4007 if (FAILED(rc)) throw rc;
4008
4009 // this is for rollback later
4010 MyHardDiskAttachment mhda;
4011 mhda.pMachine = pNewMachine;
4012 mhda.controllerName = bstrName;
4013 mhda.lControllerPort = 0;
4014 mhda.lDevice = 0;
4015
4016 Log(("Attaching floppy\n"));
4017
4018 rc = sMachine->AttachDevice(Bstr(mhda.controllerName).raw(),
4019 mhda.lControllerPort,
4020 mhda.lDevice,
4021 DeviceType_Floppy,
4022 NULL);
4023 if (FAILED(rc)) throw rc;
4024
4025 stack.llHardDiskAttachments.push_back(mhda);
4026 }
4027
4028 rc = sMachine->SaveSettings();
4029 if (FAILED(rc)) throw rc;
4030
4031 // only now that we're done with all storage devices, close the session
4032 rc = stack.pSession->UnlockMachine();
4033 if (FAILED(rc)) throw rc;
4034 stack.fSessionOpen = false;
4035 }
4036 catch(HRESULT aRC)
4037 {
4038 com::ErrorInfo info;
4039
4040 if (stack.fSessionOpen)
4041 stack.pSession->UnlockMachine();
4042
4043 if (info.isFullAvailable())
4044 throw setError(aRC, Utf8Str(info.getText()).c_str());
4045 else
4046 throw setError(aRC, "Unknown error during OVF import");
4047 }
4048 }
4049
4050 // create the storage devices & connect them to the appropriate controllers
4051 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->i_findByType(VirtualSystemDescriptionType_HardDiskImage);
4052 if (!avsdeHDs.empty())
4053 {
4054 // If there's an error here we need to close the session, so
4055 // we need another try/catch block.
4056 try
4057 {
4058#ifdef LOG_ENABLED
4059 if (LogIsEnabled())
4060 {
4061 size_t i = 0;
4062 for (list<VirtualSystemDescriptionEntry*>::const_iterator itHD = avsdeHDs.begin();
4063 itHD != avsdeHDs.end(); ++itHD, i++)
4064 Log(("avsdeHDs[%zu]: strRef=%s strOvf=%s\n", i, (*itHD)->strRef.c_str(), (*itHD)->strOvf.c_str()));
4065 i = 0;
4066 for (ovf::DiskImagesMap::const_iterator itDisk = stack.mapDisks.begin(); itDisk != stack.mapDisks.end(); ++itDisk)
4067 Log(("mapDisks[%zu]: strDiskId=%s strHref=%s\n",
4068 i, itDisk->second.strDiskId.c_str(), itDisk->second.strHref.c_str()));
4069
4070 }
4071#endif
4072
4073 // to attach things we need to open a session for the new machine
4074 rc = pNewMachine->LockMachine(stack.pSession, LockType_Write);
4075 if (FAILED(rc)) throw rc;
4076 stack.fSessionOpen = true;
4077
4078 /* get VM name from virtual system description. Only one record is possible (size of list is equal 1). */
4079 std::list<VirtualSystemDescriptionEntry*> vmName = vsdescThis->i_findByType(VirtualSystemDescriptionType_Name);
4080 std::list<VirtualSystemDescriptionEntry*>::iterator vmNameIt = vmName.begin();
4081 VirtualSystemDescriptionEntry* vmNameEntry = *vmNameIt;
4082
4083
4084 ovf::DiskImagesMap::const_iterator oit = stack.mapDisks.begin();
4085 std::set<RTCString> disksResolvedNames;
4086
4087 uint32_t cImportedDisks = 0;
4088
4089 while (oit != stack.mapDisks.end() && cImportedDisks != avsdeHDs.size())
4090 {
4091/** @todo r=bird: Most of the code here is duplicated in the other machine
4092 * import method, factor out. */
4093 ovf::DiskImage diCurrent = oit->second;
4094
4095 Log(("diCurrent.strDiskId=%s diCurrent.strHref=%s\n", diCurrent.strDiskId.c_str(), diCurrent.strHref.c_str()));
4096 /* Iterate over all given images of the virtual system
4097 * description. We need to find the target image path,
4098 * which could be changed by the user. */
4099 VirtualSystemDescriptionEntry *vsdeTargetHD = NULL;
4100 for (list<VirtualSystemDescriptionEntry*>::const_iterator itHD = avsdeHDs.begin();
4101 itHD != avsdeHDs.end();
4102 ++itHD)
4103 {
4104 VirtualSystemDescriptionEntry *vsdeHD = *itHD;
4105 if (vsdeHD->strRef == diCurrent.strDiskId)
4106 {
4107 vsdeTargetHD = vsdeHD;
4108 break;
4109 }
4110 }
4111 if (!vsdeTargetHD)
4112 {
4113 /* possible case if an image belongs to other virtual system (OVF package with multiple VMs inside) */
4114 Log1Warning(("OVA/OVF import: Disk image %s was missed during import of VM %s\n",
4115 oit->first.c_str(), vmNameEntry->strOvf.c_str()));
4116 NOREF(vmNameEntry);
4117 ++oit;
4118 continue;
4119 }
4120
4121 //diCurrent.strDiskId contains the image identifier (e.g. "vmdisk1"), which should exist
4122 //in the virtual system's images map under that ID and also in the global images map
4123 ovf::VirtualDisksMap::const_iterator itVDisk = vsysThis.mapVirtualDisks.find(diCurrent.strDiskId);
4124 if (itVDisk == vsysThis.mapVirtualDisks.end())
4125 throw setError(E_FAIL,
4126 tr("Internal inconsistency looking up disk image '%s'"),
4127 diCurrent.strHref.c_str());
4128
4129 /*
4130 * preliminary check availability of the image
4131 * This step is useful if image is placed in the OVA (TAR) package
4132 */
4133 if (stack.hVfsFssOva != NIL_RTVFSFSSTREAM)
4134 {
4135 /* It means that we possibly have imported the storage earlier on the previous loop steps*/
4136 std::set<RTCString>::const_iterator h = disksResolvedNames.find(diCurrent.strHref);
4137 if (h != disksResolvedNames.end())
4138 {
4139 /* Yes, image name was found, we can skip it*/
4140 ++oit;
4141 continue;
4142 }
4143l_skipped:
4144 rc = i_preCheckImageAvailability(stack);
4145 if (SUCCEEDED(rc))
4146 {
4147 /* current opened file isn't the same as passed one */
4148 if (RTStrICmp(diCurrent.strHref.c_str(), stack.pszOvaLookAheadName) != 0)
4149 {
4150 /* availableImage contains the image file reference (e.g. "disk1.vmdk"), which should
4151 * exist in the global images map.
4152 * And find the image from the OVF's disk list */
4153 ovf::DiskImagesMap::const_iterator itDiskImage;
4154 for (itDiskImage = stack.mapDisks.begin();
4155 itDiskImage != stack.mapDisks.end();
4156 itDiskImage++)
4157 if (itDiskImage->second.strHref.compare(stack.pszOvaLookAheadName,
4158 Utf8Str::CaseInsensitive) == 0)
4159 break;
4160 if (itDiskImage == stack.mapDisks.end())
4161 {
4162 LogFunc(("Skipping '%s'\n", stack.pszOvaLookAheadName));
4163 RTVfsIoStrmRelease(stack.claimOvaLookAHead());
4164 goto l_skipped;
4165 }
4166
4167 /* replace with a new found image */
4168 diCurrent = *(&itDiskImage->second);
4169
4170 /*
4171 * Again iterate over all given images of the virtual system
4172 * description using the found image
4173 */
4174 for (list<VirtualSystemDescriptionEntry*>::const_iterator itHD = avsdeHDs.begin();
4175 itHD != avsdeHDs.end();
4176 ++itHD)
4177 {
4178 VirtualSystemDescriptionEntry *vsdeHD = *itHD;
4179 if (vsdeHD->strRef == diCurrent.strDiskId)
4180 {
4181 vsdeTargetHD = vsdeHD;
4182 break;
4183 }
4184 }
4185
4186 /*
4187 * in this case it's an error because something is wrong with the OVF description file.
4188 * May be VBox imports OVA package with wrong file sequence inside the archive.
4189 */
4190 if (!vsdeTargetHD)
4191 throw setError(E_FAIL,
4192 tr("Internal inconsistency looking up disk image '%s'"),
4193 diCurrent.strHref.c_str());
4194
4195 itVDisk = vsysThis.mapVirtualDisks.find(diCurrent.strDiskId);
4196 if (itVDisk == vsysThis.mapVirtualDisks.end())
4197 throw setError(E_FAIL,
4198 tr("Internal inconsistency looking up disk image '%s'"),
4199 diCurrent.strHref.c_str());
4200 }
4201 else
4202 {
4203 ++oit;
4204 }
4205 }
4206 else
4207 {
4208 ++oit;
4209 continue;
4210 }
4211 }
4212 else
4213 {
4214 /* just continue with normal files */
4215 ++oit;
4216 }
4217
4218 /* very important to store image name for the next checks */
4219 disksResolvedNames.insert(diCurrent.strHref);
4220////// end of duplicated code.
4221 const ovf::VirtualDisk &ovfVdisk = itVDisk->second;
4222
4223 ComObjPtr<Medium> pTargetMedium;
4224 if (stack.locInfo.storageType == VFSType_Cloud)
4225 {
4226 /* We have already all disks prepared (converted and registered in the VBox)
4227 * and in the correct place (VM machine folder).
4228 * so what is needed is to get the disk uuid from VirtualDisk::strDiskId
4229 * and find the Medium object with this uuid.
4230 * next just attach the Medium object to new VM.
4231 * VirtualDisk::strDiskId is filled in the */
4232
4233 Guid id(ovfVdisk.strDiskId);
4234 rc = mVirtualBox->i_findHardDiskById(id, false, &pTargetMedium);
4235 if (FAILED(rc))
4236 throw rc;
4237 }
4238 else
4239 {
4240 i_importOneDiskImage(diCurrent,
4241 vsdeTargetHD->strVBoxCurrent,
4242 pTargetMedium,
4243 stack);
4244 }
4245
4246 // now use the new uuid to attach the medium to our new machine
4247 ComPtr<IMachine> sMachine;
4248 rc = stack.pSession->COMGETTER(Machine)(sMachine.asOutParam());
4249 if (FAILED(rc))
4250 throw rc;
4251
4252 // find the hard disk controller to which we should attach
4253 ovf::HardDiskController hdc = (*vsysThis.mapControllers.find(ovfVdisk.idController)).second;
4254
4255 // this is for rollback later
4256 MyHardDiskAttachment mhda;
4257 mhda.pMachine = pNewMachine;
4258
4259 i_convertDiskAttachmentValues(hdc,
4260 ovfVdisk.ulAddressOnParent,
4261 mhda.controllerName,
4262 mhda.lControllerPort,
4263 mhda.lDevice);
4264
4265 Log(("Attaching disk %s to port %d on device %d\n",
4266 vsdeTargetHD->strVBoxCurrent.c_str(), mhda.lControllerPort, mhda.lDevice));
4267
4268 DeviceType_T devType = DeviceType_Null;
4269 rc = pTargetMedium->COMGETTER(DeviceType)(&devType);
4270 if (FAILED(rc))
4271 throw rc;
4272
4273 rc = sMachine->AttachDevice(Bstr(mhda.controllerName).raw(),// name
4274 mhda.lControllerPort, // long controllerPort
4275 mhda.lDevice, // long device
4276 devType, // DeviceType_T type
4277 pTargetMedium);
4278 if (FAILED(rc))
4279 throw rc;
4280
4281 stack.llHardDiskAttachments.push_back(mhda);
4282
4283 rc = sMachine->SaveSettings();
4284 if (FAILED(rc))
4285 throw rc;
4286
4287 ++cImportedDisks;
4288
4289 } // end while(oit != stack.mapDisks.end())
4290
4291 /*
4292 * quantity of the imported disks isn't equal to the size of the avsdeHDs list.
4293 */
4294 if(cImportedDisks < avsdeHDs.size())
4295 {
4296 Log1Warning(("Not all disk images were imported for VM %s. Check OVF description file.",
4297 vmNameEntry->strOvf.c_str()));
4298 }
4299
4300 // only now that we're done with all disks, close the session
4301 rc = stack.pSession->UnlockMachine();
4302 if (FAILED(rc))
4303 throw rc;
4304 stack.fSessionOpen = false;
4305 }
4306 catch(HRESULT aRC)
4307 {
4308 com::ErrorInfo info;
4309 if (stack.fSessionOpen)
4310 stack.pSession->UnlockMachine();
4311
4312 if (info.isFullAvailable())
4313 throw setError(aRC, Utf8Str(info.getText()).c_str());
4314 else
4315 throw setError(aRC, "Unknown error during OVF import");
4316 }
4317 }
4318 LogFlowFuncLeave();
4319}
4320
4321/**
4322 * Imports one OVF virtual system (described by a vbox:Machine tag represented by the given config
4323 * structure) into VirtualBox by creating an IMachine instance, which is returned.
4324 *
4325 * This throws HRESULT error codes for anything that goes wrong, in which case the caller must clean
4326 * up any leftovers from this function. For this, the given ImportStack instance has received information
4327 * about what needs cleaning up (to support rollback).
4328 *
4329 * The machine config stored in the settings::MachineConfigFile structure contains the UUIDs of
4330 * the disk attachments used by the machine when it was exported. We also add vbox:uuid attributes
4331 * to the OVF disks sections so we can look them up. While importing these UUIDs into a second host
4332 * will most probably work, reimporting them into the same host will cause conflicts, so we always
4333 * generate new ones on import. This involves the following:
4334 *
4335 * 1) Scan the machine config for disk attachments.
4336 *
4337 * 2) For each disk attachment found, look up the OVF disk image from the disk references section
4338 * and import the disk into VirtualBox, which creates a new UUID for it. In the machine config,
4339 * replace the old UUID with the new one.
4340 *
4341 * 3) Change the machine config according to the OVF virtual system descriptions, in case the
4342 * caller has modified them using setFinalValues().
4343 *
4344 * 4) Create the VirtualBox machine with the modfified machine config.
4345 *
4346 * @param vsdescThis
4347 * @param pReturnNewMachine
4348 * @param stack
4349 */
4350void Appliance::i_importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThis,
4351 ComPtr<IMachine> &pReturnNewMachine,
4352 ImportStack &stack)
4353{
4354 LogFlowFuncEnter();
4355 Assert(vsdescThis->m->pConfig);
4356
4357 HRESULT rc = S_OK;
4358
4359 settings::MachineConfigFile &config = *vsdescThis->m->pConfig;
4360
4361 /*
4362 * step 1): modify machine config according to OVF config, in case the user
4363 * has modified them using setFinalValues()
4364 */
4365
4366 /* OS Type */
4367 config.machineUserData.strOsType = stack.strOsTypeVBox;
4368 /* Groups */
4369 if (stack.strPrimaryGroup.isEmpty() || stack.strPrimaryGroup == "/")
4370 {
4371 config.machineUserData.llGroups.clear();
4372 config.machineUserData.llGroups.push_back("/");
4373 }
4374 else
4375 {
4376 /* Replace the primary group if there is one, otherwise add it. */
4377 if (config.machineUserData.llGroups.size())
4378 config.machineUserData.llGroups.pop_front();
4379 config.machineUserData.llGroups.push_front(stack.strPrimaryGroup);
4380 }
4381 /* Description */
4382 config.machineUserData.strDescription = stack.strDescription;
4383 /* CPU count & extented attributes */
4384 config.hardwareMachine.cCPUs = stack.cCPUs;
4385 if (stack.fForceIOAPIC)
4386 config.hardwareMachine.fHardwareVirt = true;
4387 if (stack.fForceIOAPIC)
4388 config.hardwareMachine.biosSettings.fIOAPICEnabled = true;
4389 /* RAM size */
4390 config.hardwareMachine.ulMemorySizeMB = stack.ulMemorySizeMB;
4391
4392/*
4393 <const name="HardDiskControllerIDE" value="14" />
4394 <const name="HardDiskControllerSATA" value="15" />
4395 <const name="HardDiskControllerSCSI" value="16" />
4396 <const name="HardDiskControllerSAS" value="17" />
4397*/
4398
4399#ifdef VBOX_WITH_USB
4400 /* USB controller */
4401 if (stack.fUSBEnabled)
4402 {
4403 /** @todo r=klaus add support for arbitrary USB controller types, this can't handle
4404 * multiple controllers due to its design anyway */
4405 /* Usually the OHCI controller is enabled already, need to check. But
4406 * do this only if there is no xHCI controller. */
4407 bool fOHCIEnabled = false;
4408 bool fXHCIEnabled = false;
4409 settings::USBControllerList &llUSBControllers = config.hardwareMachine.usbSettings.llUSBControllers;
4410 settings::USBControllerList::iterator it;
4411 for (it = llUSBControllers.begin(); it != llUSBControllers.end(); ++it)
4412 {
4413 if (it->enmType == USBControllerType_OHCI)
4414 fOHCIEnabled = true;
4415 if (it->enmType == USBControllerType_XHCI)
4416 fXHCIEnabled = true;
4417 }
4418
4419 if (!fXHCIEnabled && !fOHCIEnabled)
4420 {
4421 settings::USBController ctrl;
4422 ctrl.strName = "OHCI";
4423 ctrl.enmType = USBControllerType_OHCI;
4424
4425 llUSBControllers.push_back(ctrl);
4426 }
4427 }
4428 else
4429 config.hardwareMachine.usbSettings.llUSBControllers.clear();
4430#endif
4431 /* Audio adapter */
4432 if (stack.strAudioAdapter.isNotEmpty())
4433 {
4434 config.hardwareMachine.audioAdapter.fEnabled = true;
4435 config.hardwareMachine.audioAdapter.controllerType = (AudioControllerType_T)stack.strAudioAdapter.toUInt32();
4436 }
4437 else
4438 config.hardwareMachine.audioAdapter.fEnabled = false;
4439 /* Network adapter */
4440 settings::NetworkAdaptersList &llNetworkAdapters = config.hardwareMachine.llNetworkAdapters;
4441 /* First disable all network cards, they will be enabled below again. */
4442 settings::NetworkAdaptersList::iterator it1;
4443 bool fKeepAllMACs = m->optListImport.contains(ImportOptions_KeepAllMACs);
4444 bool fKeepNATMACs = m->optListImport.contains(ImportOptions_KeepNATMACs);
4445 for (it1 = llNetworkAdapters.begin(); it1 != llNetworkAdapters.end(); ++it1)
4446 {
4447 it1->fEnabled = false;
4448 if (!( fKeepAllMACs
4449 || (fKeepNATMACs && it1->mode == NetworkAttachmentType_NAT)
4450 || (fKeepNATMACs && it1->mode == NetworkAttachmentType_NATNetwork)))
4451 /* Force generation of new MAC address below. */
4452 it1->strMACAddress.setNull();
4453 }
4454 /* Now iterate over all network entries. */
4455 std::list<VirtualSystemDescriptionEntry*> avsdeNWs = vsdescThis->i_findByType(VirtualSystemDescriptionType_NetworkAdapter);
4456 if (!avsdeNWs.empty())
4457 {
4458 /* Iterate through all network adapter entries and search for the
4459 * corresponding one in the machine config. If one is found, configure
4460 * it based on the user settings. */
4461 list<VirtualSystemDescriptionEntry*>::const_iterator itNW;
4462 for (itNW = avsdeNWs.begin();
4463 itNW != avsdeNWs.end();
4464 ++itNW)
4465 {
4466 VirtualSystemDescriptionEntry *vsdeNW = *itNW;
4467 if ( vsdeNW->strExtraConfigCurrent.startsWith("slot=", Utf8Str::CaseInsensitive)
4468 && vsdeNW->strExtraConfigCurrent.length() > 6)
4469 {
4470 uint32_t iSlot = vsdeNW->strExtraConfigCurrent.substr(5).toUInt32();
4471 /* Iterate through all network adapters in the machine config. */
4472 for (it1 = llNetworkAdapters.begin();
4473 it1 != llNetworkAdapters.end();
4474 ++it1)
4475 {
4476 /* Compare the slots. */
4477 if (it1->ulSlot == iSlot)
4478 {
4479 it1->fEnabled = true;
4480 if (it1->strMACAddress.isEmpty())
4481 Host::i_generateMACAddress(it1->strMACAddress);
4482 it1->type = (NetworkAdapterType_T)vsdeNW->strVBoxCurrent.toUInt32();
4483 break;
4484 }
4485 }
4486 }
4487 }
4488 }
4489
4490 /* Floppy controller */
4491 bool fFloppy = vsdescThis->i_findByType(VirtualSystemDescriptionType_Floppy).size() > 0;
4492 /* DVD controller */
4493 bool fDVD = vsdescThis->i_findByType(VirtualSystemDescriptionType_CDROM).size() > 0;
4494 /* Iterate over all storage controller check the attachments and remove
4495 * them when necessary. Also detect broken configs with more than one
4496 * attachment. Old VirtualBox versions (prior to 3.2.10) had all disk
4497 * attachments pointing to the last hard disk image, which causes import
4498 * failures. A long fixed bug, however the OVF files are long lived. */
4499 settings::StorageControllersList &llControllers = config.hardwareMachine.storage.llStorageControllers;
4500 Guid hdUuid;
4501 uint32_t cDisks = 0;
4502 bool fInconsistent = false;
4503 bool fRepairDuplicate = false;
4504 settings::StorageControllersList::iterator it3;
4505 for (it3 = llControllers.begin();
4506 it3 != llControllers.end();
4507 ++it3)
4508 {
4509 settings::AttachedDevicesList &llAttachments = it3->llAttachedDevices;
4510 settings::AttachedDevicesList::iterator it4 = llAttachments.begin();
4511 while (it4 != llAttachments.end())
4512 {
4513 if ( ( !fDVD
4514 && it4->deviceType == DeviceType_DVD)
4515 ||
4516 ( !fFloppy
4517 && it4->deviceType == DeviceType_Floppy))
4518 {
4519 it4 = llAttachments.erase(it4);
4520 continue;
4521 }
4522 else if (it4->deviceType == DeviceType_HardDisk)
4523 {
4524 const Guid &thisUuid = it4->uuid;
4525 cDisks++;
4526 if (cDisks == 1)
4527 {
4528 if (hdUuid.isZero())
4529 hdUuid = thisUuid;
4530 else
4531 fInconsistent = true;
4532 }
4533 else
4534 {
4535 if (thisUuid.isZero())
4536 fInconsistent = true;
4537 else if (thisUuid == hdUuid)
4538 fRepairDuplicate = true;
4539 }
4540 }
4541 ++it4;
4542 }
4543 }
4544 /* paranoia... */
4545 if (fInconsistent || cDisks == 1)
4546 fRepairDuplicate = false;
4547
4548 /*
4549 * step 2: scan the machine config for media attachments
4550 */
4551 /* get VM name from virtual system description. Only one record is possible (size of list is equal 1). */
4552 std::list<VirtualSystemDescriptionEntry*> vmName = vsdescThis->i_findByType(VirtualSystemDescriptionType_Name);
4553 std::list<VirtualSystemDescriptionEntry*>::iterator vmNameIt = vmName.begin();
4554 VirtualSystemDescriptionEntry* vmNameEntry = *vmNameIt;
4555
4556 /* Get all hard disk descriptions. */
4557 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->i_findByType(VirtualSystemDescriptionType_HardDiskImage);
4558 std::list<VirtualSystemDescriptionEntry*>::iterator avsdeHDsIt = avsdeHDs.begin();
4559 /* paranoia - if there is no 1:1 match do not try to repair. */
4560 if (cDisks != avsdeHDs.size())
4561 fRepairDuplicate = false;
4562
4563 // there must be an image in the OVF disk structs with the same UUID
4564
4565 ovf::DiskImagesMap::const_iterator oit = stack.mapDisks.begin();
4566 std::set<RTCString> disksResolvedNames;
4567
4568 uint32_t cImportedDisks = 0;
4569
4570 while (oit != stack.mapDisks.end() && cImportedDisks != avsdeHDs.size())
4571 {
4572/** @todo r=bird: Most of the code here is duplicated in the other machine
4573 * import method, factor out. */
4574 ovf::DiskImage diCurrent = oit->second;
4575
4576 Log(("diCurrent.strDiskId=%s diCurrent.strHref=%s\n", diCurrent.strDiskId.c_str(), diCurrent.strHref.c_str()));
4577
4578 /* Iterate over all given disk images of the virtual system
4579 * disks description. We need to find the target disk path,
4580 * which could be changed by the user. */
4581 VirtualSystemDescriptionEntry *vsdeTargetHD = NULL;
4582 for (list<VirtualSystemDescriptionEntry*>::const_iterator itHD = avsdeHDs.begin();
4583 itHD != avsdeHDs.end();
4584 ++itHD)
4585 {
4586 VirtualSystemDescriptionEntry *vsdeHD = *itHD;
4587 if (vsdeHD->strRef == oit->first)
4588 {
4589 vsdeTargetHD = vsdeHD;
4590 break;
4591 }
4592 }
4593 if (!vsdeTargetHD)
4594 {
4595 /* possible case if a disk image belongs to other virtual system (OVF package with multiple VMs inside) */
4596 Log1Warning(("OVA/OVF import: Disk image %s was missed during import of VM %s\n",
4597 oit->first.c_str(), vmNameEntry->strOvf.c_str()));
4598 NOREF(vmNameEntry);
4599 ++oit;
4600 continue;
4601 }
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611 /*
4612 * preliminary check availability of the image
4613 * This step is useful if image is placed in the OVA (TAR) package
4614 */
4615 if (stack.hVfsFssOva != NIL_RTVFSFSSTREAM)
4616 {
4617 /* It means that we possibly have imported the storage earlier on a previous loop step. */
4618 std::set<RTCString>::const_iterator h = disksResolvedNames.find(diCurrent.strHref);
4619 if (h != disksResolvedNames.end())
4620 {
4621 /* Yes, disk name was found, we can skip it*/
4622 ++oit;
4623 continue;
4624 }
4625l_skipped:
4626 rc = i_preCheckImageAvailability(stack);
4627 if (SUCCEEDED(rc))
4628 {
4629 /* current opened file isn't the same as passed one */
4630 if (RTStrICmp(diCurrent.strHref.c_str(), stack.pszOvaLookAheadName) != 0)
4631 {
4632 // availableImage contains the disk identifier (e.g. "vmdisk1"), which should exist
4633 // in the virtual system's disks map under that ID and also in the global images map
4634 // and find the disk from the OVF's disk list
4635 ovf::DiskImagesMap::const_iterator itDiskImage;
4636 for (itDiskImage = stack.mapDisks.begin();
4637 itDiskImage != stack.mapDisks.end();
4638 itDiskImage++)
4639 if (itDiskImage->second.strHref.compare(stack.pszOvaLookAheadName,
4640 Utf8Str::CaseInsensitive) == 0)
4641 break;
4642 if (itDiskImage == stack.mapDisks.end())
4643 {
4644 LogFunc(("Skipping '%s'\n", stack.pszOvaLookAheadName));
4645 RTVfsIoStrmRelease(stack.claimOvaLookAHead());
4646 goto l_skipped;
4647 }
4648 //throw setError(E_FAIL,
4649 // tr("Internal inconsistency looking up disk image '%s'. "
4650 // "Check compliance OVA package structure and file names "
4651 // "references in the section <References> in the OVF file."),
4652 // stack.pszOvaLookAheadName);
4653
4654 /* replace with a new found disk image */
4655 diCurrent = *(&itDiskImage->second);
4656
4657 /*
4658 * Again iterate over all given disk images of the virtual system
4659 * disks description using the found disk image
4660 */
4661 vsdeTargetHD = NULL;
4662 for (list<VirtualSystemDescriptionEntry*>::const_iterator itHD = avsdeHDs.begin();
4663 itHD != avsdeHDs.end();
4664 ++itHD)
4665 {
4666 VirtualSystemDescriptionEntry *vsdeHD = *itHD;
4667 if (vsdeHD->strRef == diCurrent.strDiskId)
4668 {
4669 vsdeTargetHD = vsdeHD;
4670 break;
4671 }
4672 }
4673
4674 /*
4675 * in this case it's an error because something is wrong with the OVF description file.
4676 * May be VBox imports OVA package with wrong file sequence inside the archive.
4677 */
4678 if (!vsdeTargetHD)
4679 throw setError(E_FAIL,
4680 tr("Internal inconsistency looking up disk image '%s'"),
4681 diCurrent.strHref.c_str());
4682
4683
4684
4685
4686
4687 }
4688 else
4689 {
4690 ++oit;
4691 }
4692 }
4693 else
4694 {
4695 ++oit;
4696 continue;
4697 }
4698 }
4699 else
4700 {
4701 /* just continue with normal files*/
4702 ++oit;
4703 }
4704
4705 /* Important! to store disk name for the next checks */
4706 disksResolvedNames.insert(diCurrent.strHref);
4707////// end of duplicated code.
4708 // there must be an image in the OVF disk structs with the same UUID
4709 bool fFound = false;
4710 Utf8Str strUuid;
4711
4712 // for each storage controller...
4713 for (settings::StorageControllersList::iterator sit = config.hardwareMachine.storage.llStorageControllers.begin();
4714 sit != config.hardwareMachine.storage.llStorageControllers.end();
4715 ++sit)
4716 {
4717 settings::StorageController &sc = *sit;
4718
4719 // for each medium attachment to this controller...
4720 for (settings::AttachedDevicesList::iterator dit = sc.llAttachedDevices.begin();
4721 dit != sc.llAttachedDevices.end();
4722 ++dit)
4723 {
4724 settings::AttachedDevice &d = *dit;
4725
4726 if (d.uuid.isZero())
4727 // empty DVD and floppy media
4728 continue;
4729
4730 // When repairing a broken VirtualBox xml config section (written
4731 // by VirtualBox versions earlier than 3.2.10) assume the disks
4732 // show up in the same order as in the OVF description.
4733 if (fRepairDuplicate)
4734 {
4735 VirtualSystemDescriptionEntry *vsdeHD = *avsdeHDsIt;
4736 ovf::DiskImagesMap::const_iterator itDiskImage = stack.mapDisks.find(vsdeHD->strRef);
4737 if (itDiskImage != stack.mapDisks.end())
4738 {
4739 const ovf::DiskImage &di = itDiskImage->second;
4740 d.uuid = Guid(di.uuidVBox);
4741 }
4742 ++avsdeHDsIt;
4743 }
4744
4745 // convert the Guid to string
4746 strUuid = d.uuid.toString();
4747
4748 if (diCurrent.uuidVBox != strUuid)
4749 {
4750 continue;
4751 }
4752
4753 /*
4754 * step 3: import disk
4755 */
4756 ComObjPtr<Medium> pTargetMedium;
4757 i_importOneDiskImage(diCurrent,
4758 vsdeTargetHD->strVBoxCurrent,
4759 pTargetMedium,
4760 stack);
4761
4762 // ... and replace the old UUID in the machine config with the one of
4763 // the imported disk that was just created
4764 Bstr hdId;
4765 rc = pTargetMedium->COMGETTER(Id)(hdId.asOutParam());
4766 if (FAILED(rc)) throw rc;
4767
4768 /*
4769 * 1. saving original UUID for restoring in case of failure.
4770 * 2. replacement of original UUID by new UUID in the current VM config (settings::MachineConfigFile).
4771 */
4772 {
4773 rc = stack.saveOriginalUUIDOfAttachedDevice(d, Utf8Str(hdId));
4774 d.uuid = hdId;
4775 }
4776
4777 fFound = true;
4778 break;
4779 } // for (settings::AttachedDevicesList::const_iterator dit = sc.llAttachedDevices.begin();
4780 } // for (settings::StorageControllersList::const_iterator sit = config.hardwareMachine.storage.llStorageControllers.begin();
4781
4782 // no disk with such a UUID found:
4783 if (!fFound)
4784 throw setError(E_FAIL,
4785 tr("<vbox:Machine> element in OVF contains a medium attachment for the disk image %s "
4786 "but the OVF describes no such image"),
4787 strUuid.c_str());
4788
4789 ++cImportedDisks;
4790
4791 }// while(oit != stack.mapDisks.end())
4792
4793
4794 /*
4795 * quantity of the imported disks isn't equal to the size of the avsdeHDs list.
4796 */
4797 if(cImportedDisks < avsdeHDs.size())
4798 {
4799 Log1Warning(("Not all disk images were imported for VM %s. Check OVF description file.",
4800 vmNameEntry->strOvf.c_str()));
4801 }
4802
4803 /*
4804 * step 4): create the machine and have it import the config
4805 */
4806
4807 ComObjPtr<Machine> pNewMachine;
4808 rc = pNewMachine.createObject();
4809 if (FAILED(rc)) throw rc;
4810
4811 // this magic constructor fills the new machine object with the MachineConfig
4812 // instance that we created from the vbox:Machine
4813 rc = pNewMachine->init(mVirtualBox,
4814 stack.strNameVBox,// name from OVF preparations; can be suffixed to avoid duplicates
4815 stack.strSettingsFilename,
4816 config); // the whole machine config
4817 if (FAILED(rc)) throw rc;
4818
4819 pReturnNewMachine = ComPtr<IMachine>(pNewMachine);
4820
4821 // and register it
4822 rc = mVirtualBox->RegisterMachine(pNewMachine);
4823 if (FAILED(rc)) throw rc;
4824
4825 // store new machine for roll-back in case of errors
4826 Bstr bstrNewMachineId;
4827 rc = pNewMachine->COMGETTER(Id)(bstrNewMachineId.asOutParam());
4828 if (FAILED(rc)) throw rc;
4829 m->llGuidsMachinesCreated.push_back(Guid(bstrNewMachineId));
4830
4831 LogFlowFuncLeave();
4832}
4833
4834/**
4835 * @throws HRESULT errors.
4836 */
4837void Appliance::i_importMachines(ImportStack &stack)
4838{
4839 // this is safe to access because this thread only gets started
4840 const ovf::OVFReader &reader = *m->pReader;
4841
4842 // create a session for the machine + disks we manipulate below
4843 HRESULT rc = stack.pSession.createInprocObject(CLSID_Session);
4844 ComAssertComRCThrowRC(rc);
4845
4846 list<ovf::VirtualSystem>::const_iterator it;
4847 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1;
4848 /* Iterate through all virtual systems of that appliance */
4849 size_t i = 0;
4850 for (it = reader.m_llVirtualSystems.begin(), it1 = m->virtualSystemDescriptions.begin();
4851 it != reader.m_llVirtualSystems.end() && it1 != m->virtualSystemDescriptions.end();
4852 ++it, ++it1, ++i)
4853 {
4854 const ovf::VirtualSystem &vsysThis = *it;
4855 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it1);
4856
4857 ComPtr<IMachine> pNewMachine;
4858
4859 // there are two ways in which we can create a vbox machine from OVF:
4860 // -- either this OVF was written by vbox 3.2 or later, in which case there is a <vbox:Machine> element
4861 // in the <VirtualSystem>; then the VirtualSystemDescription::Data has a settings::MachineConfigFile
4862 // with all the machine config pretty-parsed;
4863 // -- or this is an OVF from an older vbox or an external source, and then we need to translate the
4864 // VirtualSystemDescriptionEntry and do import work
4865
4866 // Even for the vbox:Machine case, there are a number of configuration items that will be taken from
4867 // the OVF because otherwise the "override import parameters" mechanism in the GUI won't work.
4868
4869 // VM name
4870 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->i_findByType(VirtualSystemDescriptionType_Name);
4871 if (vsdeName.size() < 1)
4872 throw setError(VBOX_E_FILE_ERROR,
4873 tr("Missing VM name"));
4874 stack.strNameVBox = vsdeName.front()->strVBoxCurrent;
4875
4876 // Primary group, which is entirely optional.
4877 stack.strPrimaryGroup.setNull();
4878 std::list<VirtualSystemDescriptionEntry*> vsdePrimaryGroup = vsdescThis->i_findByType(VirtualSystemDescriptionType_PrimaryGroup);
4879 if (vsdePrimaryGroup.size() >= 1)
4880 {
4881 stack.strPrimaryGroup = vsdePrimaryGroup.front()->strVBoxCurrent;
4882 if (stack.strPrimaryGroup.isEmpty())
4883 stack.strPrimaryGroup = "/";
4884 }
4885
4886 // Draw the right conclusions from the (possibly modified) VM settings
4887 // file name and base folder. If the VM settings file name is modified,
4888 // it takes precedence, otherwise it is recreated from the base folder
4889 // and the primary group.
4890 stack.strSettingsFilename.setNull();
4891 std::list<VirtualSystemDescriptionEntry*> vsdeSettingsFile = vsdescThis->i_findByType(VirtualSystemDescriptionType_SettingsFile);
4892 if (vsdeSettingsFile.size() >= 1)
4893 {
4894 VirtualSystemDescriptionEntry *vsdeSF1 = vsdeSettingsFile.front();
4895 if (vsdeSF1->strVBoxCurrent != vsdeSF1->strVBoxSuggested)
4896 stack.strSettingsFilename = vsdeSF1->strVBoxCurrent;
4897 }
4898 if (stack.strSettingsFilename.isEmpty())
4899 {
4900 Utf8Str strBaseFolder;
4901 std::list<VirtualSystemDescriptionEntry*> vsdeBaseFolder = vsdescThis->i_findByType(VirtualSystemDescriptionType_BaseFolder);
4902 if (vsdeBaseFolder.size() >= 1)
4903 strBaseFolder = vsdeBaseFolder.front()->strVBoxCurrent;
4904 Bstr bstrSettingsFilename;
4905 rc = mVirtualBox->ComposeMachineFilename(Bstr(stack.strNameVBox).raw(),
4906 Bstr(stack.strPrimaryGroup).raw(),
4907 NULL /* aCreateFlags */,
4908 Bstr(strBaseFolder).raw(),
4909 bstrSettingsFilename.asOutParam());
4910 if (FAILED(rc)) throw rc;
4911 stack.strSettingsFilename = bstrSettingsFilename;
4912 }
4913
4914 // Determine the machine folder from the settings file.
4915 LogFunc(("i=%zu strName=%s strSettingsFilename=%s\n", i, stack.strNameVBox.c_str(), stack.strSettingsFilename.c_str()));
4916 stack.strMachineFolder = stack.strSettingsFilename;
4917 stack.strMachineFolder.stripFilename();
4918
4919 // guest OS type
4920 std::list<VirtualSystemDescriptionEntry*> vsdeOS;
4921 vsdeOS = vsdescThis->i_findByType(VirtualSystemDescriptionType_OS);
4922 if (vsdeOS.size() < 1)
4923 throw setError(VBOX_E_FILE_ERROR,
4924 tr("Missing guest OS type"));
4925 stack.strOsTypeVBox = vsdeOS.front()->strVBoxCurrent;
4926
4927 // CPU count
4928 std::list<VirtualSystemDescriptionEntry*> vsdeCPU = vsdescThis->i_findByType(VirtualSystemDescriptionType_CPU);
4929 if (vsdeCPU.size() != 1)
4930 throw setError(VBOX_E_FILE_ERROR, tr("CPU count missing"));
4931
4932 stack.cCPUs = vsdeCPU.front()->strVBoxCurrent.toUInt32();
4933 // We need HWVirt & IO-APIC if more than one CPU is requested
4934 if (stack.cCPUs > 1)
4935 {
4936 stack.fForceHWVirt = true;
4937 stack.fForceIOAPIC = true;
4938 }
4939
4940 // RAM
4941 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->i_findByType(VirtualSystemDescriptionType_Memory);
4942 if (vsdeRAM.size() != 1)
4943 throw setError(VBOX_E_FILE_ERROR, tr("RAM size missing"));
4944 stack.ulMemorySizeMB = (ULONG)vsdeRAM.front()->strVBoxCurrent.toUInt64();
4945
4946#ifdef VBOX_WITH_USB
4947 // USB controller
4948 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController =
4949 vsdescThis->i_findByType(VirtualSystemDescriptionType_USBController);
4950 // USB support is enabled if there's at least one such entry; to disable USB support,
4951 // the type of the USB item would have been changed to "ignore"
4952 stack.fUSBEnabled = !vsdeUSBController.empty();
4953#endif
4954 // audio adapter
4955 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter =
4956 vsdescThis->i_findByType(VirtualSystemDescriptionType_SoundCard);
4957 /** @todo we support one audio adapter only */
4958 if (!vsdeAudioAdapter.empty())
4959 stack.strAudioAdapter = vsdeAudioAdapter.front()->strVBoxCurrent;
4960
4961 // for the description of the new machine, always use the OVF entry, the user may have changed it in the import config
4962 std::list<VirtualSystemDescriptionEntry*> vsdeDescription =
4963 vsdescThis->i_findByType(VirtualSystemDescriptionType_Description);
4964 if (!vsdeDescription.empty())
4965 stack.strDescription = vsdeDescription.front()->strVBoxCurrent;
4966
4967 // import vbox:machine or OVF now
4968 if (vsdescThis->m->pConfig)
4969 // vbox:Machine config
4970 i_importVBoxMachine(vsdescThis, pNewMachine, stack);
4971 else
4972 // generic OVF config
4973 i_importMachineGeneric(vsysThis, vsdescThis, pNewMachine, stack);
4974
4975 } // for (it = pAppliance->m->llVirtualSystems.begin() ...
4976}
4977
4978HRESULT Appliance::ImportStack::saveOriginalUUIDOfAttachedDevice(settings::AttachedDevice &device,
4979 const Utf8Str &newlyUuid)
4980{
4981 HRESULT rc = S_OK;
4982
4983 /* save for restoring */
4984 mapNewUUIDsToOriginalUUIDs.insert(std::make_pair(newlyUuid, device.uuid.toString()));
4985
4986 return rc;
4987}
4988
4989HRESULT Appliance::ImportStack::restoreOriginalUUIDOfAttachedDevice(settings::MachineConfigFile *config)
4990{
4991 HRESULT rc = S_OK;
4992
4993 settings::StorageControllersList &llControllers = config->hardwareMachine.storage.llStorageControllers;
4994 settings::StorageControllersList::iterator itscl;
4995 for (itscl = llControllers.begin();
4996 itscl != llControllers.end();
4997 ++itscl)
4998 {
4999 settings::AttachedDevicesList &llAttachments = itscl->llAttachedDevices;
5000 settings::AttachedDevicesList::iterator itadl = llAttachments.begin();
5001 while (itadl != llAttachments.end())
5002 {
5003 std::map<Utf8Str , Utf8Str>::iterator it =
5004 mapNewUUIDsToOriginalUUIDs.find(itadl->uuid.toString());
5005 if(it!=mapNewUUIDsToOriginalUUIDs.end())
5006 {
5007 Utf8Str uuidOriginal = it->second;
5008 itadl->uuid = Guid(uuidOriginal);
5009 mapNewUUIDsToOriginalUUIDs.erase(it->first);
5010 }
5011 ++itadl;
5012 }
5013 }
5014
5015 return rc;
5016}
5017
5018/**
5019 * @throws Nothing
5020 */
5021RTVFSIOSTREAM Appliance::ImportStack::claimOvaLookAHead(void)
5022{
5023 RTVFSIOSTREAM hVfsIos = this->hVfsIosOvaLookAhead;
5024 this->hVfsIosOvaLookAhead = NIL_RTVFSIOSTREAM;
5025 /* We don't free the name since it may be referenced in error messages and such. */
5026 return hVfsIos;
5027}
5028
Note: See TracBrowser for help on using the repository browser.

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