VirtualBox

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

Last change on this file since 78513 was 78430, checked in by vboxsync, 6 years ago

bugref:9416. scm fixes.

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