VirtualBox

source: vbox/trunk/src/VBox/Main/ApplianceImplImport.cpp@ 33868

Last change on this file since 33868 was 33788, checked in by vboxsync, 14 years ago

Main-OVF: one more error check; more specific error messages

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 116.6 KB
Line 
1/* $Id: ApplianceImplImport.cpp 33788 2010-11-05 11:37:50Z vboxsync $ */
2/** @file
3 *
4 * IAppliance and IVirtualSystem COM class implementations.
5 */
6
7/*
8 * Copyright (C) 2008-2010 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include <iprt/path.h>
20#include <iprt/dir.h>
21#include <iprt/file.h>
22#include <iprt/s3.h>
23#include <iprt/sha.h>
24#include <iprt/manifest.h>
25#include <iprt/tar.h>
26#include <iprt/stream.h>
27
28#include <VBox/vd.h>
29#include <VBox/com/array.h>
30
31#include "ApplianceImpl.h"
32#include "VirtualBoxImpl.h"
33#include "GuestOSTypeImpl.h"
34#include "ProgressImpl.h"
35#include "MachineImpl.h"
36#include "MediumImpl.h"
37#include "MediumFormatImpl.h"
38#include "SystemPropertiesImpl.h"
39
40#include "AutoCaller.h"
41#include "Logging.h"
42
43#include "ApplianceImplPrivate.h"
44
45#include <VBox/param.h>
46#include <VBox/version.h>
47#include <VBox/settings.h>
48
49using namespace std;
50
51////////////////////////////////////////////////////////////////////////////////
52//
53// IAppliance public methods
54//
55////////////////////////////////////////////////////////////////////////////////
56
57/**
58 * Public method implementation. This opens the OVF with ovfreader.cpp.
59 * Thread implementation is in Appliance::readImpl().
60 *
61 * @param path
62 * @return
63 */
64STDMETHODIMP Appliance::Read(IN_BSTR path, IProgress **aProgress)
65{
66 if (!path) return E_POINTER;
67 CheckComArgOutPointerValid(aProgress);
68
69 AutoCaller autoCaller(this);
70 if (FAILED(autoCaller.rc())) return autoCaller.rc();
71
72 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
73
74 if (!isApplianceIdle())
75 return E_ACCESSDENIED;
76
77 if (m->pReader)
78 {
79 delete m->pReader;
80 m->pReader = NULL;
81 }
82
83 // see if we can handle this file; for now we insist it has an ovf/ova extension
84 Utf8Str strPath (path);
85 if (!( strPath.endsWith(".ovf", Utf8Str::CaseInsensitive)
86 || strPath.endsWith(".ova", Utf8Str::CaseInsensitive)))
87 return setError(VBOX_E_FILE_ERROR,
88 tr("Appliance file must have .ovf extension"));
89
90 ComObjPtr<Progress> progress;
91 HRESULT rc = S_OK;
92 try
93 {
94 /* Parse all necessary info out of the URI */
95 parseURI(strPath, m->locInfo);
96 rc = readImpl(m->locInfo, progress);
97 }
98 catch (HRESULT aRC)
99 {
100 rc = aRC;
101 }
102
103 if (SUCCEEDED(rc))
104 /* Return progress to the caller */
105 progress.queryInterfaceTo(aProgress);
106
107 return S_OK;
108}
109
110/**
111 * Public method implementation. This looks at the output of ovfreader.cpp and creates
112 * VirtualSystemDescription instances.
113 * @return
114 */
115STDMETHODIMP Appliance::Interpret()
116{
117 // @todo:
118 // - don't use COM methods but the methods directly (faster, but needs appropriate locking of that objects itself (s. HardDisk))
119 // - Appropriate handle errors like not supported file formats
120 AutoCaller autoCaller(this);
121 if (FAILED(autoCaller.rc())) return autoCaller.rc();
122
123 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
124
125 if (!isApplianceIdle())
126 return E_ACCESSDENIED;
127
128 HRESULT rc = S_OK;
129
130 /* Clear any previous virtual system descriptions */
131 m->virtualSystemDescriptions.clear();
132
133 if (!m->pReader)
134 return setError(E_FAIL,
135 tr("Cannot interpret appliance without reading it first (call read() before interpret())"));
136
137 // Change the appliance state so we can safely leave the lock while doing time-consuming
138 // disk imports; also the below method calls do all kinds of locking which conflicts with
139 // the appliance object lock
140 m->state = Data::ApplianceImporting;
141 alock.release();
142
143 /* Try/catch so we can clean up on error */
144 try
145 {
146 list<ovf::VirtualSystem>::const_iterator it;
147 /* Iterate through all virtual systems */
148 for (it = m->pReader->m_llVirtualSystems.begin();
149 it != m->pReader->m_llVirtualSystems.end();
150 ++it)
151 {
152 const ovf::VirtualSystem &vsysThis = *it;
153
154 ComObjPtr<VirtualSystemDescription> pNewDesc;
155 rc = pNewDesc.createObject();
156 if (FAILED(rc)) throw rc;
157 rc = pNewDesc->init();
158 if (FAILED(rc)) throw rc;
159
160 // if the virtual system in OVF had a <vbox:Machine> element, have the
161 // VirtualBox settings code parse that XML now
162 if (vsysThis.pelmVboxMachine)
163 pNewDesc->importVboxMachineXML(*vsysThis.pelmVboxMachine);
164
165 /* Guest OS type */
166 Utf8Str strOsTypeVBox;
167 Utf8Str strCIMOSType = Utf8StrFmt("%RU32", (uint32_t)vsysThis.cimos);
168 /* If there is a vbox.xml, we always prefer the ostype settings
169 * from there, cause OVF doesn't know all types VBox know. */
170 if ( vsysThis.pelmVboxMachine
171 && pNewDesc->m->pConfig->machineUserData.strOsType.isNotEmpty())
172 strOsTypeVBox = pNewDesc->m->pConfig->machineUserData.strOsType;
173 else
174 convertCIMOSType2VBoxOSType(strOsTypeVBox, vsysThis.cimos, vsysThis.strCimosDesc);
175 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,
176 "",
177 strCIMOSType,
178 strOsTypeVBox);
179
180 /* VM name */
181 Utf8Str nameVBox;
182 /* If there is a vbox.xml, we always prefer the setting from there. */
183 if ( vsysThis.pelmVboxMachine
184 && pNewDesc->m->pConfig->machineUserData.strName.isNotEmpty())
185 nameVBox = pNewDesc->m->pConfig->machineUserData.strName;
186 else
187 nameVBox = vsysThis.strName;
188 /* If the there isn't any name specified create a default one out
189 * of the OS type */
190 if (nameVBox.isEmpty())
191 nameVBox = strOsTypeVBox;
192 searchUniqueVMName(nameVBox);
193 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,
194 "",
195 vsysThis.strName,
196 nameVBox);
197
198 /* Based on the VM name, create a target machine path. */
199 Bstr bstrMachineFilename;
200 rc = mVirtualBox->ComposeMachineFilename(Bstr(nameVBox).raw(),
201 NULL,
202 bstrMachineFilename.asOutParam());
203 if (FAILED(rc)) throw rc;
204 /* Determine the machine folder from that */
205 Utf8Str strMachineFolder = Utf8Str(bstrMachineFilename).stripFilename();
206
207 /* VM Product */
208 if (!vsysThis.strProduct.isEmpty())
209 pNewDesc->addEntry(VirtualSystemDescriptionType_Product,
210 "",
211 vsysThis.strProduct,
212 vsysThis.strProduct);
213
214 /* VM Vendor */
215 if (!vsysThis.strVendor.isEmpty())
216 pNewDesc->addEntry(VirtualSystemDescriptionType_Vendor,
217 "",
218 vsysThis.strVendor,
219 vsysThis.strVendor);
220
221 /* VM Version */
222 if (!vsysThis.strVersion.isEmpty())
223 pNewDesc->addEntry(VirtualSystemDescriptionType_Version,
224 "",
225 vsysThis.strVersion,
226 vsysThis.strVersion);
227
228 /* VM ProductUrl */
229 if (!vsysThis.strProductUrl.isEmpty())
230 pNewDesc->addEntry(VirtualSystemDescriptionType_ProductUrl,
231 "",
232 vsysThis.strProductUrl,
233 vsysThis.strProductUrl);
234
235 /* VM VendorUrl */
236 if (!vsysThis.strVendorUrl.isEmpty())
237 pNewDesc->addEntry(VirtualSystemDescriptionType_VendorUrl,
238 "",
239 vsysThis.strVendorUrl,
240 vsysThis.strVendorUrl);
241
242 /* VM description */
243 if (!vsysThis.strDescription.isEmpty())
244 pNewDesc->addEntry(VirtualSystemDescriptionType_Description,
245 "",
246 vsysThis.strDescription,
247 vsysThis.strDescription);
248
249 /* VM license */
250 if (!vsysThis.strLicenseText.isEmpty())
251 pNewDesc->addEntry(VirtualSystemDescriptionType_License,
252 "",
253 vsysThis.strLicenseText,
254 vsysThis.strLicenseText);
255
256 /* Now that we know the OS type, get our internal defaults based on that. */
257 ComPtr<IGuestOSType> pGuestOSType;
258 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox).raw(), pGuestOSType.asOutParam());
259 if (FAILED(rc)) throw rc;
260
261 /* CPU count */
262 ULONG cpuCountVBox;
263 /* If there is a vbox.xml, we always prefer the setting from there. */
264 if ( vsysThis.pelmVboxMachine
265 && pNewDesc->m->pConfig->hardwareMachine.cCPUs)
266 cpuCountVBox = pNewDesc->m->pConfig->hardwareMachine.cCPUs;
267 else
268 cpuCountVBox = vsysThis.cCPUs;
269 /* Check for the constraints */
270 if (cpuCountVBox > SchemaDefs::MaxCPUCount)
271 {
272 addWarning(tr("The virtual system \"%s\" claims support for %u CPU's, but VirtualBox has support for max %u CPU's only."),
273 vsysThis.strName.c_str(), cpuCountVBox, SchemaDefs::MaxCPUCount);
274 cpuCountVBox = SchemaDefs::MaxCPUCount;
275 }
276 if (vsysThis.cCPUs == 0)
277 cpuCountVBox = 1;
278 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
279 "",
280 Utf8StrFmt("%RU32", (uint32_t)vsysThis.cCPUs),
281 Utf8StrFmt("%RU32", (uint32_t)cpuCountVBox));
282
283 /* RAM */
284 uint64_t ullMemSizeVBox;
285 /* If there is a vbox.xml, we always prefer the setting from there. */
286 if ( vsysThis.pelmVboxMachine
287 && pNewDesc->m->pConfig->hardwareMachine.ulMemorySizeMB)
288 ullMemSizeVBox = pNewDesc->m->pConfig->hardwareMachine.ulMemorySizeMB;
289 else
290 ullMemSizeVBox = vsysThis.ullMemorySize / _1M;
291 /* Check for the constraints */
292 if ( ullMemSizeVBox != 0
293 && ( ullMemSizeVBox < MM_RAM_MIN_IN_MB
294 || ullMemSizeVBox > MM_RAM_MAX_IN_MB
295 )
296 )
297 {
298 addWarning(tr("The virtual system \"%s\" claims support for %llu MB RAM size, but VirtualBox has support for min %u & max %u MB RAM size only."),
299 vsysThis.strName.c_str(), ullMemSizeVBox, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
300 ullMemSizeVBox = RT_MIN(RT_MAX(ullMemSizeVBox, MM_RAM_MIN_IN_MB), MM_RAM_MAX_IN_MB);
301 }
302 if (vsysThis.ullMemorySize == 0)
303 {
304 /* If the RAM of the OVF is zero, use our predefined values */
305 ULONG memSizeVBox2;
306 rc = pGuestOSType->COMGETTER(RecommendedRAM)(&memSizeVBox2);
307 if (FAILED(rc)) throw rc;
308 /* VBox stores that in MByte */
309 ullMemSizeVBox = (uint64_t)memSizeVBox2;
310 }
311 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
312 "",
313 Utf8StrFmt("%RU64", (uint64_t)vsysThis.ullMemorySize),
314 Utf8StrFmt("%RU64", (uint64_t)ullMemSizeVBox));
315
316 /* Audio */
317 Utf8Str strSoundCard;
318 Utf8Str strSoundCardOrig;
319 /* If there is a vbox.xml, we always prefer the setting from there. */
320 if ( vsysThis.pelmVboxMachine
321 && pNewDesc->m->pConfig->hardwareMachine.audioAdapter.fEnabled)
322 strSoundCard = Utf8StrFmt("%RU32", (uint32_t)pNewDesc->m->pConfig->hardwareMachine.audioAdapter.controllerType);
323 else if (vsysThis.strSoundCardType.isNotEmpty())
324 {
325 /* Set the AC97 always for the simple OVF case.
326 * @todo: figure out the hardware which could be possible */
327 strSoundCard = Utf8StrFmt("%RU32", (uint32_t)AudioControllerType_AC97);
328 strSoundCardOrig = vsysThis.strSoundCardType;
329 }
330 if (strSoundCard.isNotEmpty())
331 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,
332 "",
333 strSoundCardOrig,
334 strSoundCard);
335
336#ifdef VBOX_WITH_USB
337 /* USB Controller */
338 /* If there is a vbox.xml, we always prefer the setting from there. */
339 if ( ( vsysThis.pelmVboxMachine
340 && pNewDesc->m->pConfig->hardwareMachine.usbController.fEnabled)
341 || vsysThis.fHasUsbController)
342 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
343#endif /* VBOX_WITH_USB */
344
345 /* Network Controller */
346 /* If there is a vbox.xml, we always prefer the setting from there. */
347 if (vsysThis.pelmVboxMachine)
348 {
349 const settings::NetworkAdaptersList &llNetworkAdapters = pNewDesc->m->pConfig->hardwareMachine.llNetworkAdapters;
350 /* Check for the constrains */
351 if (llNetworkAdapters.size() > SchemaDefs::NetworkAdapterCount)
352 addWarning(tr("The virtual system \"%s\" claims support for %zu network adapters, but VirtualBox has support for max %u network adapter only."),
353 vsysThis.strName.c_str(), llNetworkAdapters.size(), SchemaDefs::NetworkAdapterCount);
354 /* Iterate through all network adapters. */
355 settings::NetworkAdaptersList::const_iterator it1;
356 size_t a = 0;
357 for (it1 = llNetworkAdapters.begin();
358 it1 != llNetworkAdapters.end() && a < SchemaDefs::NetworkAdapterCount;
359 ++it1, ++a)
360 {
361 if (it1->fEnabled)
362 {
363 Utf8Str strMode = convertNetworkAttachmentTypeToString(it1->mode);
364 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
365 "", // ref
366 strMode, // orig
367 Utf8StrFmt("%RU32", (uint32_t)it1->type), // conf
368 0,
369 Utf8StrFmt("slot=%RU32;type=%s", it1->ulSlot, strMode.c_str())); // extra conf
370 }
371 }
372 }
373 /* else we use the ovf configuration. */
374 else if (size_t cEthernetAdapters = vsysThis.llEthernetAdapters.size() > 0)
375 {
376 /* Check for the constrains */
377 if (cEthernetAdapters > SchemaDefs::NetworkAdapterCount)
378 addWarning(tr("The virtual system \"%s\" claims support for %zu network adapters, but VirtualBox has support for max %u network adapter only."),
379 vsysThis.strName.c_str(), cEthernetAdapters, SchemaDefs::NetworkAdapterCount);
380
381 /* Get the default network adapter type for the selected guest OS */
382 NetworkAdapterType_T defaultAdapterVBox = NetworkAdapterType_Am79C970A;
383 rc = pGuestOSType->COMGETTER(AdapterType)(&defaultAdapterVBox);
384 if (FAILED(rc)) throw rc;
385
386 ovf::EthernetAdaptersList::const_iterator itEA;
387 /* Iterate through all abstract networks. We support 8 network
388 * adapters at the maximum, so the first 8 will be added only. */
389 size_t a = 0;
390 for (itEA = vsysThis.llEthernetAdapters.begin();
391 itEA != vsysThis.llEthernetAdapters.end() && a < SchemaDefs::NetworkAdapterCount;
392 ++itEA, ++a)
393 {
394 const ovf::EthernetAdapter &ea = *itEA; // logical network to connect to
395 Utf8Str strNetwork = ea.strNetworkName;
396 // make sure it's one of these two
397 if ( (strNetwork.compare("Null", Utf8Str::CaseInsensitive))
398 && (strNetwork.compare("NAT", Utf8Str::CaseInsensitive))
399 && (strNetwork.compare("Bridged", Utf8Str::CaseInsensitive))
400 && (strNetwork.compare("Internal", Utf8Str::CaseInsensitive))
401 && (strNetwork.compare("HostOnly", Utf8Str::CaseInsensitive))
402 && (strNetwork.compare("VDE", Utf8Str::CaseInsensitive))
403 )
404 strNetwork = "Bridged"; // VMware assumes this is the default apparently
405
406 /* Figure out the hardware type */
407 NetworkAdapterType_T nwAdapterVBox = defaultAdapterVBox;
408 if (!ea.strAdapterType.compare("PCNet32", Utf8Str::CaseInsensitive))
409 {
410 /* If the default adapter is already one of the two
411 * PCNet adapters use the default one. If not use the
412 * Am79C970A as fallback. */
413 if (!(defaultAdapterVBox == NetworkAdapterType_Am79C970A ||
414 defaultAdapterVBox == NetworkAdapterType_Am79C973))
415 nwAdapterVBox = NetworkAdapterType_Am79C970A;
416 }
417#ifdef VBOX_WITH_E1000
418 /* VMWare accidentally write this with VirtualCenter 3.5,
419 so make sure in this case always to use the VMWare one */
420 else if (!ea.strAdapterType.compare("E10000", Utf8Str::CaseInsensitive))
421 nwAdapterVBox = NetworkAdapterType_I82545EM;
422 else if (!ea.strAdapterType.compare("E1000", Utf8Str::CaseInsensitive))
423 {
424 /* Check if this OVF was written by VirtualBox */
425 if (Utf8Str(vsysThis.strVirtualSystemType).contains("virtualbox", Utf8Str::CaseInsensitive))
426 {
427 /* If the default adapter is already one of the three
428 * E1000 adapters use the default one. If not use the
429 * I82545EM as fallback. */
430 if (!(defaultAdapterVBox == NetworkAdapterType_I82540EM ||
431 defaultAdapterVBox == NetworkAdapterType_I82543GC ||
432 defaultAdapterVBox == NetworkAdapterType_I82545EM))
433 nwAdapterVBox = NetworkAdapterType_I82540EM;
434 }
435 else
436 /* Always use this one since it's what VMware uses */
437 nwAdapterVBox = NetworkAdapterType_I82545EM;
438 }
439#endif /* VBOX_WITH_E1000 */
440
441 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
442 "", // ref
443 ea.strNetworkName, // orig
444 Utf8StrFmt("%RU32", (uint32_t)nwAdapterVBox), // conf
445 0,
446 Utf8StrFmt("type=%s", strNetwork.c_str())); // extra conf
447 }
448 }
449
450 /* If there is a vbox.xml, we always prefer the setting from there. */
451 bool fFloppy = false;
452 bool fDVD = false;
453 if (vsysThis.pelmVboxMachine)
454 {
455 settings::StorageControllersList &llControllers = pNewDesc->m->pConfig->storageMachine.llStorageControllers;
456 settings::StorageControllersList::iterator it3;
457 for (it3 = llControllers.begin();
458 it3 != llControllers.end();
459 ++it3)
460 {
461 settings::AttachedDevicesList &llAttachments = it3->llAttachedDevices;
462 settings::AttachedDevicesList::iterator it4;
463 for (it4 = llAttachments.begin();
464 it4 != llAttachments.end();
465 ++it4)
466 {
467 fDVD |= it4->deviceType == DeviceType_DVD;
468 fFloppy |= it4->deviceType == DeviceType_Floppy;
469 if (fFloppy && fDVD)
470 break;
471 }
472 if (fFloppy && fDVD)
473 break;
474 }
475 }
476 else
477 {
478 fFloppy = vsysThis.fHasFloppyDrive;
479 fDVD = vsysThis.fHasCdromDrive;
480 }
481 /* Floppy Drive */
482 if (fFloppy)
483 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");
484 /* CD Drive */
485 if (fDVD)
486 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");
487
488 /* Hard disk Controller */
489 uint16_t cIDEused = 0;
490 uint16_t cSATAused = 0; NOREF(cSATAused);
491 uint16_t cSCSIused = 0; NOREF(cSCSIused);
492 ovf::ControllersMap::const_iterator hdcIt;
493 /* Iterate through all hard disk controllers */
494 for (hdcIt = vsysThis.mapControllers.begin();
495 hdcIt != vsysThis.mapControllers.end();
496 ++hdcIt)
497 {
498 const ovf::HardDiskController &hdc = hdcIt->second;
499 Utf8Str strControllerID = Utf8StrFmt("%RI32", (uint32_t)hdc.idController);
500
501 switch (hdc.system)
502 {
503 case ovf::HardDiskController::IDE:
504 /* Check for the constrains */
505 if (cIDEused < 4)
506 {
507 // @todo: figure out the IDE types
508 /* Use PIIX4 as default */
509 Utf8Str strType = "PIIX4";
510 if (!hdc.strControllerType.compare("PIIX3", Utf8Str::CaseInsensitive))
511 strType = "PIIX3";
512 else if (!hdc.strControllerType.compare("ICH6", Utf8Str::CaseInsensitive))
513 strType = "ICH6";
514 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
515 strControllerID, // strRef
516 hdc.strControllerType, // aOvfValue
517 strType); // aVboxValue
518 }
519 else
520 /* Warn only once */
521 if (cIDEused == 2)
522 addWarning(tr("The virtual \"%s\" system requests support for more than two IDE controller channels, but VirtualBox supports only two."),
523 vsysThis.strName.c_str());
524
525 ++cIDEused;
526 break;
527
528 case ovf::HardDiskController::SATA:
529 /* Check for the constrains */
530 if (cSATAused < 1)
531 {
532 // @todo: figure out the SATA types
533 /* We only support a plain AHCI controller, so use them always */
534 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
535 strControllerID,
536 hdc.strControllerType,
537 "AHCI");
538 }
539 else
540 {
541 /* Warn only once */
542 if (cSATAused == 1)
543 addWarning(tr("The virtual system \"%s\" requests support for more than one SATA controller, but VirtualBox has support for only one"),
544 vsysThis.strName.c_str());
545
546 }
547 ++cSATAused;
548 break;
549
550 case ovf::HardDiskController::SCSI:
551 /* Check for the constrains */
552 if (cSCSIused < 1)
553 {
554 VirtualSystemDescriptionType_T vsdet = VirtualSystemDescriptionType_HardDiskControllerSCSI;
555 Utf8Str hdcController = "LsiLogic";
556 if (!hdc.strControllerType.compare("lsilogicsas", Utf8Str::CaseInsensitive))
557 {
558 // OVF considers SAS a variant of SCSI but VirtualBox considers it a class of its own
559 vsdet = VirtualSystemDescriptionType_HardDiskControllerSAS;
560 hdcController = "LsiLogicSas";
561 }
562 else if (!hdc.strControllerType.compare("BusLogic", Utf8Str::CaseInsensitive))
563 hdcController = "BusLogic";
564 pNewDesc->addEntry(vsdet,
565 strControllerID,
566 hdc.strControllerType,
567 hdcController);
568 }
569 else
570 addWarning(tr("The virtual system \"%s\" requests support for an additional SCSI controller of type \"%s\" with ID %s, but VirtualBox presently supports only one SCSI controller."),
571 vsysThis.strName.c_str(),
572 hdc.strControllerType.c_str(),
573 strControllerID.c_str());
574 ++cSCSIused;
575 break;
576 }
577 }
578
579 /* Hard disks */
580 if (vsysThis.mapVirtualDisks.size() > 0)
581 {
582 ovf::VirtualDisksMap::const_iterator itVD;
583 /* Iterate through all hard disks ()*/
584 for (itVD = vsysThis.mapVirtualDisks.begin();
585 itVD != vsysThis.mapVirtualDisks.end();
586 ++itVD)
587 {
588 const ovf::VirtualDisk &hd = itVD->second;
589 /* Get the associated disk image */
590 const ovf::DiskImage &di = m->pReader->m_mapDisks[hd.strDiskId];
591
592 // @todo:
593 // - figure out all possible vmdk formats we also support
594 // - figure out if there is a url specifier for vhd already
595 // - we need a url specifier for the vdi format
596 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)
597 || di.strFormat.compare("http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized", Utf8Str::CaseInsensitive)
598 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive)
599 || di.strFormat.compare("http://www.vmware.com/interfaces/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive)
600 )
601 {
602 /* If the href is empty use the VM name as filename */
603 Utf8Str strFilename = di.strHref;
604 if (!strFilename.length())
605 strFilename = Utf8StrFmt("%s.vmdk", nameVBox.c_str());
606
607 Utf8Str strTargetPath = Utf8Str(strMachineFolder)
608 .append(RTPATH_DELIMITER)
609 .append(di.strHref);
610 searchUniqueDiskImageFilePath(strTargetPath);
611
612 /* find the description for the hard disk controller
613 * that has the same ID as hd.idController */
614 const VirtualSystemDescriptionEntry *pController;
615 if (!(pController = pNewDesc->findControllerFromID(hd.idController)))
616 throw setError(E_FAIL,
617 tr("Cannot find hard disk controller with OVF instance ID %RI32 to which disk \"%s\" should be attached"),
618 hd.idController,
619 di.strHref.c_str());
620
621 /* controller to attach to, and the bus within that controller */
622 Utf8StrFmt strExtraConfig("controller=%RI16;channel=%RI16",
623 pController->ulIndex,
624 hd.ulAddressOnParent);
625 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
626 hd.strDiskId,
627 di.strHref,
628 strTargetPath,
629 di.ulSuggestedSizeMB,
630 strExtraConfig);
631 }
632 else
633 throw setError(VBOX_E_FILE_ERROR,
634 tr("Unsupported format for virtual disk image in OVF: \"%s\"", di.strFormat.c_str()));
635 }
636 }
637
638 m->virtualSystemDescriptions.push_back(pNewDesc);
639 }
640 }
641 catch (HRESULT aRC)
642 {
643 /* On error we clear the list & return */
644 m->virtualSystemDescriptions.clear();
645 rc = aRC;
646 }
647
648 // reset the appliance state
649 alock.acquire();
650 m->state = Data::ApplianceIdle;
651
652 return rc;
653}
654
655/**
656 * Public method implementation. This creates one or more new machines according to the
657 * VirtualSystemScription instances created by Appliance::Interpret().
658 * Thread implementation is in Appliance::importImpl().
659 * @param aProgress
660 * @return
661 */
662STDMETHODIMP Appliance::ImportMachines(IProgress **aProgress)
663{
664 CheckComArgOutPointerValid(aProgress);
665
666 AutoCaller autoCaller(this);
667 if (FAILED(autoCaller.rc())) return autoCaller.rc();
668
669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
670
671 // do not allow entering this method if the appliance is busy reading or writing
672 if (!isApplianceIdle())
673 return E_ACCESSDENIED;
674
675 if (!m->pReader)
676 return setError(E_FAIL,
677 tr("Cannot import machines without reading it first (call read() before importMachines())"));
678
679 ComObjPtr<Progress> progress;
680 HRESULT rc = S_OK;
681 try
682 {
683 rc = importImpl(m->locInfo, progress);
684 }
685 catch (HRESULT aRC)
686 {
687 rc = aRC;
688 }
689
690 if (SUCCEEDED(rc))
691 /* Return progress to the caller */
692 progress.queryInterfaceTo(aProgress);
693
694 return rc;
695}
696
697////////////////////////////////////////////////////////////////////////////////
698//
699// Appliance private methods
700//
701////////////////////////////////////////////////////////////////////////////////
702
703
704/*******************************************************************************
705 * Read stuff
706 ******************************************************************************/
707
708/**
709 * Implementation for reading an OVF. This starts a new thread which will call
710 * Appliance::taskThreadImportOrExport() which will then call readFS() or readS3().
711 * This will then open the OVF with ovfreader.cpp.
712 *
713 * This is in a separate private method because it is used from three locations:
714 *
715 * 1) from the public Appliance::Read().
716 *
717 * 2) in a second worker thread; in that case, Appliance::ImportMachines() called Appliance::importImpl(), which
718 * called Appliance::readFSOVA(), which called Appliance::importImpl(), which then called this again.
719 *
720 * 3) from Appliance::readS3(), which got called from a previous instance of Appliance::taskThreadImportOrExport().
721 *
722 * @param aLocInfo
723 * @param aProgress
724 * @return
725 */
726HRESULT Appliance::readImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)
727{
728 BstrFmt bstrDesc = BstrFmt(tr("Reading appliance '%s'"),
729 aLocInfo.strPath.c_str());
730 HRESULT rc;
731 /* Create the progress object */
732 aProgress.createObject();
733 if (aLocInfo.storageType == VFSType_File)
734 /* 1 operation only */
735 rc = aProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
736 bstrDesc.raw(),
737 TRUE /* aCancelable */);
738 else
739 /* 4/5 is downloading, 1/5 is reading */
740 rc = aProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
741 bstrDesc.raw(),
742 TRUE /* aCancelable */,
743 2, // ULONG cOperations,
744 5, // ULONG ulTotalOperationsWeight,
745 BstrFmt(tr("Download appliance '%s'"),
746 aLocInfo.strPath.c_str()).raw(), // CBSTR bstrFirstOperationDescription,
747 4); // ULONG ulFirstOperationWeight,
748 if (FAILED(rc)) throw rc;
749
750 /* Initialize our worker task */
751 std::auto_ptr<TaskOVF> task(new TaskOVF(this, TaskOVF::Read, aLocInfo, aProgress));
752
753 rc = task->startThread();
754 if (FAILED(rc)) throw rc;
755
756 /* Don't destruct on success */
757 task.release();
758
759 return rc;
760}
761
762/**
763 * Actual worker code for reading an OVF from disk. This is called from Appliance::taskThreadImportOrExport()
764 * and therefore runs on the OVF read worker thread. This opens the OVF with ovfreader.cpp.
765 *
766 * This runs in two contexts:
767 *
768 * 1) in a first worker thread; in that case, Appliance::Read() called Appliance::readImpl();
769 *
770 * 2) in a second worker thread; in that case, Appliance::Read() called Appliance::readImpl(), which
771 * called Appliance::readS3(), which called Appliance::readImpl(), which then called this.
772 *
773 * @param pTask
774 * @return
775 */
776HRESULT Appliance::readFS(TaskOVF *pTask)
777{
778 LogFlowFuncEnter();
779 LogFlowFunc(("Appliance %p\n", this));
780
781 AutoCaller autoCaller(this);
782 if (FAILED(autoCaller.rc())) return autoCaller.rc();
783
784 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
785
786 HRESULT rc = S_OK;
787
788 if (pTask->locInfo.strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
789 rc = readFSOVF(pTask);
790 else
791 rc = readFSOVA(pTask);
792
793 LogFlowFunc(("rc=%Rhrc\n", rc));
794 LogFlowFuncLeave();
795
796 return rc;
797}
798
799HRESULT Appliance::readFSOVF(TaskOVF *pTask)
800{
801 LogFlowFuncEnter();
802
803 HRESULT rc = S_OK;
804
805 PVDINTERFACEIO pRTSha1Callbacks = 0;
806 PVDINTERFACEIO pRTFileCallbacks = 0;
807 do
808 {
809 pRTSha1Callbacks = RTSha1CreateInterface();
810 if (!pRTSha1Callbacks)
811 {
812 rc = E_OUTOFMEMORY;
813 break;
814 }
815 pRTFileCallbacks = RTFileCreateInterface();
816 if (!pRTFileCallbacks)
817 {
818 rc = E_OUTOFMEMORY;
819 break;
820 }
821 VDINTERFACE VDInterfaceIO;
822 RTSHA1STORAGE storage;
823 RT_ZERO(storage);
824 int vrc = VDInterfaceAdd(&VDInterfaceIO, "Appliance::IORTFile",
825 VDINTERFACETYPE_IO, pRTFileCallbacks,
826 0, &storage.pVDImageIfaces);
827 if (RT_FAILURE(vrc))
828 {
829 rc = E_FAIL;
830 break;
831 }
832 rc = readFSImpl(pTask, pRTSha1Callbacks, &storage);
833 }while(0);
834
835 /* Cleanup */
836 if (pRTSha1Callbacks)
837 RTMemFree(pRTSha1Callbacks);
838 if (pRTFileCallbacks)
839 RTMemFree(pRTFileCallbacks);
840
841 LogFlowFunc(("rc=%Rhrc\n", rc));
842 LogFlowFuncLeave();
843
844 return rc;
845}
846
847HRESULT Appliance::readFSOVA(TaskOVF *pTask)
848{
849 LogFlowFuncEnter();
850
851 RTTAR tar;
852 int vrc = RTTarOpen(&tar, pTask->locInfo.strPath.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, true);
853 if (RT_FAILURE(vrc))
854 return setError(VBOX_E_FILE_ERROR,
855 tr("Could not open OVA file '%s' (%Rrc)"),
856 pTask->locInfo.strPath.c_str(), vrc);
857
858 HRESULT rc = S_OK;
859
860 PVDINTERFACEIO pRTSha1Callbacks = 0;
861 PVDINTERFACEIO pRTTarCallbacks = 0;
862 do
863 {
864 pRTSha1Callbacks = RTSha1CreateInterface();
865 if (!pRTSha1Callbacks)
866 {
867 rc = E_OUTOFMEMORY;
868 break;
869 }
870 pRTTarCallbacks = RTTarCreateInterface();
871 if (!pRTTarCallbacks)
872 {
873 rc = E_OUTOFMEMORY;
874 break;
875 }
876 VDINTERFACE VDInterfaceIO;
877 RTSHA1STORAGE storage;
878 RT_ZERO(storage);
879 vrc = VDInterfaceAdd(&VDInterfaceIO, "Appliance::IORTTar",
880 VDINTERFACETYPE_IO, pRTTarCallbacks,
881 tar, &storage.pVDImageIfaces);
882 if (RT_FAILURE(vrc))
883 {
884 rc = E_FAIL;
885 break;
886 }
887 rc = readFSImpl(pTask, pRTSha1Callbacks, &storage);
888 }while(0);
889
890 RTTarClose(tar);
891
892 /* Cleanup */
893 if (pRTSha1Callbacks)
894 RTMemFree(pRTSha1Callbacks);
895 if (pRTTarCallbacks)
896 RTMemFree(pRTTarCallbacks);
897
898 LogFlowFunc(("rc=%Rhrc\n", rc));
899 LogFlowFuncLeave();
900
901 return rc;
902}
903
904HRESULT Appliance::readFSImpl(TaskOVF *pTask, PVDINTERFACEIO pCallbacks, PRTSHA1STORAGE pStorage)
905{
906 LogFlowFuncEnter();
907
908 HRESULT rc = S_OK;
909
910 pStorage->fCreateDigest = true;
911
912 void *pvTmpBuf = 0;
913 try
914 {
915 Utf8Str strOvfFile = Utf8Str(pTask->locInfo.strPath).stripExt().append(".ovf");
916 /* Read the OVF into a memory buffer */
917 size_t cbSize = 0;
918 int vrc = RTSha1ReadBuf(strOvfFile.c_str(), &pvTmpBuf, &cbSize, pCallbacks, pStorage);
919 if ( RT_FAILURE(vrc)
920 || !pvTmpBuf)
921 throw setError(VBOX_E_FILE_ERROR,
922 tr("Could not read OVF file '%s' (%Rrc)"),
923 RTPathFilename(strOvfFile.c_str()), vrc);
924 /* Copy the SHA1 sum of the OVF file for later validation */
925 m->strOVFSHA1Digest = pStorage->strDigest;
926 /* Read & parse the XML structure of the OVF file */
927 m->pReader = new ovf::OVFReader(pvTmpBuf, cbSize, pTask->locInfo.strPath);
928 }
929 catch (iprt::Error &x) // includes all XML exceptions
930 {
931 rc = setError(VBOX_E_FILE_ERROR,
932 x.what());
933 }
934 catch (HRESULT aRC)
935 {
936 rc = aRC;
937 }
938
939 /* Cleanup */
940 if (pvTmpBuf)
941 RTMemFree(pvTmpBuf);
942
943 LogFlowFunc(("rc=%Rhrc\n", rc));
944 LogFlowFuncLeave();
945
946 return rc;
947}
948
949/**
950 * Worker code for reading OVF from the cloud. This is called from Appliance::taskThreadImportOrExport()
951 * in S3 mode and therefore runs on the OVF read worker thread. This then starts a second worker
952 * thread to create temporary files (see Appliance::readFS()).
953 *
954 * @param pTask
955 * @return
956 */
957HRESULT Appliance::readS3(TaskOVF *pTask)
958{
959 LogFlowFuncEnter();
960 LogFlowFunc(("Appliance %p\n", this));
961
962 AutoCaller autoCaller(this);
963 if (FAILED(autoCaller.rc())) return autoCaller.rc();
964
965 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
966
967 HRESULT rc = S_OK;
968 int vrc = VINF_SUCCESS;
969 RTS3 hS3 = NIL_RTS3;
970 char szOSTmpDir[RTPATH_MAX];
971 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));
972 /* The template for the temporary directory created below */
973 char *pszTmpDir = RTPathJoinA(szOSTmpDir, "vbox-ovf-XXXXXX");
974 list< pair<Utf8Str, ULONG> > filesList;
975 Utf8Str strTmpOvf;
976
977 try
978 {
979 /* Extract the bucket */
980 Utf8Str tmpPath = pTask->locInfo.strPath;
981 Utf8Str bucket;
982 parseBucket(tmpPath, bucket);
983
984 /* We need a temporary directory which we can put the OVF file & all
985 * disk images in */
986 vrc = RTDirCreateTemp(pszTmpDir);
987 if (RT_FAILURE(vrc))
988 throw setError(VBOX_E_FILE_ERROR,
989 tr("Cannot create temporary directory '%s'"), pszTmpDir);
990
991 /* The temporary name of the target OVF file */
992 strTmpOvf = Utf8StrFmt("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str()));
993
994 /* Next we have to download the OVF */
995 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);
996 if (RT_FAILURE(vrc))
997 throw setError(VBOX_E_IPRT_ERROR,
998 tr("Cannot create S3 service handler"));
999 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask);
1000
1001 /* Get it */
1002 char *pszFilename = RTPathFilename(strTmpOvf.c_str());
1003 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strTmpOvf.c_str());
1004 if (RT_FAILURE(vrc))
1005 {
1006 if (vrc == VERR_S3_CANCELED)
1007 throw S_OK; /* todo: !!!!!!!!!!!!! */
1008 else if (vrc == VERR_S3_ACCESS_DENIED)
1009 throw setError(E_ACCESSDENIED,
1010 tr("Cannot download file '%s' from S3 storage server (Access denied). Make sure that your credentials are right."
1011 "Also check that your host clock is properly synced"),
1012 pszFilename);
1013 else if (vrc == VERR_S3_NOT_FOUND)
1014 throw setError(VBOX_E_FILE_ERROR,
1015 tr("Cannot download file '%s' from S3 storage server (File not found)"), pszFilename);
1016 else
1017 throw setError(VBOX_E_IPRT_ERROR,
1018 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc);
1019 }
1020
1021 /* Close the connection early */
1022 RTS3Destroy(hS3);
1023 hS3 = NIL_RTS3;
1024
1025 pTask->pProgress->SetNextOperation(Bstr(tr("Reading")).raw(), 1);
1026
1027 /* Prepare the temporary reading of the OVF */
1028 ComObjPtr<Progress> progress;
1029 LocationInfo li;
1030 li.strPath = strTmpOvf;
1031 /* Start the reading from the fs */
1032 rc = readImpl(li, progress);
1033 if (FAILED(rc)) throw rc;
1034
1035 /* Unlock the appliance for the reading thread */
1036 appLock.release();
1037 /* Wait until the reading is done, but report the progress back to the
1038 caller */
1039 ComPtr<IProgress> progressInt(progress);
1040 waitForAsyncProgress(pTask->pProgress, progressInt); /* Any errors will be thrown */
1041
1042 /* Again lock the appliance for the next steps */
1043 appLock.acquire();
1044 }
1045 catch(HRESULT aRC)
1046 {
1047 rc = aRC;
1048 }
1049 /* Cleanup */
1050 RTS3Destroy(hS3);
1051 /* Delete all files which where temporary created */
1052 if (RTPathExists(strTmpOvf.c_str()))
1053 {
1054 vrc = RTFileDelete(strTmpOvf.c_str());
1055 if (RT_FAILURE(vrc))
1056 rc = setError(VBOX_E_FILE_ERROR,
1057 tr("Cannot delete file '%s' (%Rrc)"), strTmpOvf.c_str(), vrc);
1058 }
1059 /* Delete the temporary directory */
1060 if (RTPathExists(pszTmpDir))
1061 {
1062 vrc = RTDirRemove(pszTmpDir);
1063 if (RT_FAILURE(vrc))
1064 rc = setError(VBOX_E_FILE_ERROR,
1065 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);
1066 }
1067 if (pszTmpDir)
1068 RTStrFree(pszTmpDir);
1069
1070 LogFlowFunc(("rc=%Rhrc\n", rc));
1071 LogFlowFuncLeave();
1072
1073 return rc;
1074}
1075
1076/*******************************************************************************
1077 * Import stuff
1078 ******************************************************************************/
1079
1080/**
1081 * Implementation for importing OVF data into VirtualBox. This starts a new thread which will call
1082 * Appliance::taskThreadImportOrExport().
1083 *
1084 * This creates one or more new machines according to the VirtualSystemScription instances created by
1085 * Appliance::Interpret().
1086 *
1087 * This is in a separate private method because it is used from two locations:
1088 *
1089 * 1) from the public Appliance::ImportMachines().
1090 * 2) from Appliance::importS3(), which got called from a previous instance of Appliance::taskThreadImportOrExport().
1091 *
1092 * @param aLocInfo
1093 * @param aProgress
1094 * @return
1095 */
1096HRESULT Appliance::importImpl(const LocationInfo &locInfo,
1097 ComObjPtr<Progress> &progress)
1098{
1099 HRESULT rc = S_OK;
1100
1101 SetUpProgressMode mode;
1102 if (locInfo.storageType == VFSType_File)
1103 mode = ImportFile;
1104 else
1105 mode = ImportS3;
1106
1107 rc = setUpProgress(progress,
1108 BstrFmt(tr("Importing appliance '%s'"), locInfo.strPath.c_str()),
1109 mode);
1110 if (FAILED(rc)) throw rc;
1111
1112 /* Initialize our worker task */
1113 std::auto_ptr<TaskOVF> task(new TaskOVF(this, TaskOVF::Import, locInfo, progress));
1114
1115 rc = task->startThread();
1116 if (FAILED(rc)) throw rc;
1117
1118 /* Don't destruct on success */
1119 task.release();
1120
1121 return rc;
1122}
1123
1124/**
1125 * Actual worker code for importing OVF data into VirtualBox. This is called from Appliance::taskThreadImportOrExport()
1126 * and therefore runs on the OVF import worker thread. This creates one or more new machines according to the
1127 * VirtualSystemScription instances created by Appliance::Interpret().
1128 *
1129 * This runs in three contexts:
1130 *
1131 * 1) in a first worker thread; in that case, Appliance::ImportMachines() called Appliance::importImpl();
1132 *
1133 * 2) in a second worker thread; in that case, Appliance::ImportMachines() called Appliance::importImpl(), which
1134 * called Appliance::importFSOVA(), which called Appliance::importImpl(), which then called this again.
1135 *
1136 * 3) in a second worker thread; in that case, Appliance::ImportMachines() called Appliance::importImpl(), which
1137 * called Appliance::importS3(), which called Appliance::importImpl(), which then called this again.
1138 *
1139 * @param pTask
1140 * @return
1141 */
1142HRESULT Appliance::importFS(TaskOVF *pTask)
1143{
1144
1145 LogFlowFuncEnter();
1146 LogFlowFunc(("Appliance %p\n", this));
1147
1148 AutoCaller autoCaller(this);
1149 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1150
1151 /* Change the appliance state so we can safely leave the lock while doing
1152 * time-consuming disk imports; also the below method calls do all kinds of
1153 * locking which conflicts with the appliance object lock. */
1154 AutoWriteLock writeLock(this COMMA_LOCKVAL_SRC_POS);
1155 /* Check if the appliance is currently busy. */
1156 if (!isApplianceIdle())
1157 return E_ACCESSDENIED;
1158 /* Set the internal state to importing. */
1159 m->state = Data::ApplianceImporting;
1160
1161 HRESULT rc = S_OK;
1162
1163 /* Clear the list of imported machines, if any */
1164 m->llGuidsMachinesCreated.clear();
1165
1166 if (pTask->locInfo.strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
1167 rc = importFSOVF(pTask, writeLock);
1168 else
1169 rc = importFSOVA(pTask, writeLock);
1170
1171 if (FAILED(rc))
1172 {
1173 /* With _whatever_ error we've had, do a complete roll-back of
1174 * machines and disks we've created */
1175 writeLock.release();
1176 for (list<Guid>::iterator itID = m->llGuidsMachinesCreated.begin();
1177 itID != m->llGuidsMachinesCreated.end();
1178 ++itID)
1179 {
1180 Guid guid = *itID;
1181 Bstr bstrGuid = guid.toUtf16();
1182 ComPtr<IMachine> failedMachine;
1183 HRESULT rc2 = mVirtualBox->FindMachine(bstrGuid.raw(), failedMachine.asOutParam());
1184 if (SUCCEEDED(rc2))
1185 {
1186 SafeIfaceArray<IMedium> aMedia;
1187 rc2 = failedMachine->Unregister(CleanupMode_DetachAllReturnHardDisksOnly, ComSafeArrayAsOutParam(aMedia));
1188 ComPtr<IProgress> pProgress2;
1189 rc2 = failedMachine->Delete(ComSafeArrayAsInParam(aMedia), pProgress2.asOutParam());
1190 pProgress2->WaitForCompletion(-1);
1191 }
1192 }
1193 writeLock.acquire();
1194 }
1195
1196 /* Reset the state so others can call methods again */
1197 m->state = Data::ApplianceIdle;
1198
1199 LogFlowFunc(("rc=%Rhrc\n", rc));
1200 LogFlowFuncLeave();
1201
1202 return rc;
1203}
1204
1205HRESULT Appliance::importFSOVF(TaskOVF *pTask, AutoWriteLockBase& writeLock)
1206{
1207 LogFlowFuncEnter();
1208
1209 HRESULT rc = S_OK;
1210
1211 PVDINTERFACEIO pRTSha1Callbacks = 0;
1212 PVDINTERFACEIO pRTFileCallbacks = 0;
1213 void *pvMfBuf = 0;
1214 writeLock.release();
1215 try
1216 {
1217 /* Create the necessary file access interfaces. */
1218 pRTSha1Callbacks = RTSha1CreateInterface();
1219 if (!pRTSha1Callbacks)
1220 throw E_OUTOFMEMORY;
1221 pRTFileCallbacks = RTFileCreateInterface();
1222 if (!pRTFileCallbacks)
1223 throw E_OUTOFMEMORY;
1224
1225 VDINTERFACE VDInterfaceIO;
1226 RTSHA1STORAGE storage;
1227 RT_ZERO(storage);
1228 storage.fCreateDigest = true;
1229 int vrc = VDInterfaceAdd(&VDInterfaceIO, "Appliance::IORTFile",
1230 VDINTERFACETYPE_IO, pRTFileCallbacks,
1231 0, &storage.pVDImageIfaces);
1232 if (RT_FAILURE(vrc))
1233 throw E_FAIL;
1234
1235 size_t cbMfSize = 0;
1236 Utf8Str strMfFile = Utf8Str(pTask->locInfo.strPath).stripExt().append(".mf");
1237 /* Create the import stack for the rollback on errors. */
1238 ImportStack stack(pTask->locInfo, m->pReader->m_mapDisks, pTask->pProgress);
1239 /* Do we need the digest information? */
1240 storage.fCreateDigest = RTFileExists(strMfFile.c_str());
1241 /* Now import the appliance. */
1242 importMachines(stack, pRTSha1Callbacks, &storage);
1243 /* Read & verify the manifest file, if there is one. */
1244 if (storage.fCreateDigest)
1245 {
1246 /* Add the ovf file to the digest list. */
1247 stack.llSrcDisksDigest.push_front(STRPAIR(pTask->locInfo.strPath, m->strOVFSHA1Digest));
1248 rc = readManifestFile(strMfFile, &pvMfBuf, &cbMfSize, pRTSha1Callbacks, &storage);
1249 if (FAILED(rc)) throw rc;
1250 rc = verifyManifestFile(strMfFile, stack, pvMfBuf, cbMfSize);
1251 if (FAILED(rc)) throw rc;
1252 }
1253 }
1254 catch (HRESULT rc2)
1255 {
1256 rc = rc2;
1257 }
1258 writeLock.acquire();
1259
1260 /* Cleanup */
1261 if (pvMfBuf)
1262 RTMemFree(pvMfBuf);
1263 if (pRTSha1Callbacks)
1264 RTMemFree(pRTSha1Callbacks);
1265 if (pRTFileCallbacks)
1266 RTMemFree(pRTFileCallbacks);
1267
1268 LogFlowFunc(("rc=%Rhrc\n", rc));
1269 LogFlowFuncLeave();
1270
1271 return rc;
1272}
1273
1274HRESULT Appliance::importFSOVA(TaskOVF *pTask, AutoWriteLockBase& writeLock)
1275{
1276 LogFlowFuncEnter();
1277
1278 RTTAR tar;
1279 int vrc = RTTarOpen(&tar, pTask->locInfo.strPath.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, true);
1280 if (RT_FAILURE(vrc))
1281 return setError(VBOX_E_FILE_ERROR,
1282 tr("Could not open OVA file '%s' (%Rrc)"),
1283 pTask->locInfo.strPath.c_str(), vrc);
1284
1285 HRESULT rc = S_OK;
1286
1287 PVDINTERFACEIO pRTSha1Callbacks = 0;
1288 PVDINTERFACEIO pRTTarCallbacks = 0;
1289 void *pvMfBuf = 0;
1290 writeLock.release();
1291 try
1292 {
1293 /* Create the necessary file access interfaces. */
1294 pRTSha1Callbacks = RTSha1CreateInterface();
1295 if (!pRTSha1Callbacks)
1296 throw E_OUTOFMEMORY;
1297 pRTTarCallbacks = RTTarCreateInterface();
1298 if (!pRTTarCallbacks)
1299 throw E_OUTOFMEMORY;
1300
1301 VDINTERFACE VDInterfaceIO;
1302 RTSHA1STORAGE storage;
1303 RT_ZERO(storage);
1304 vrc = VDInterfaceAdd(&VDInterfaceIO, "Appliance::IORTTar",
1305 VDINTERFACETYPE_IO, pRTTarCallbacks,
1306 tar, &storage.pVDImageIfaces);
1307 if (RT_FAILURE(vrc))
1308 throw setError(E_FAIL,
1309 tr("Internal error (%Rrc)"), vrc);
1310
1311 /* Skip the OVF file, cause this was read in IAppliance::Read already. */
1312 vrc = RTTarSeekNextFile(tar);
1313 if ( RT_FAILURE(vrc)
1314 && vrc != VERR_TAR_END_OF_FILE)
1315 throw setError(E_FAIL,
1316 tr("Internal error (%Rrc)"), vrc);
1317
1318 PVDINTERFACEIO pCallbacks = pRTSha1Callbacks;
1319 PRTSHA1STORAGE pStorage = &storage;
1320
1321 /* We always need to create the digest, cause we didn't know if there
1322 * is a manifest file in the stream. */
1323 pStorage->fCreateDigest = true;
1324
1325 size_t cbMfSize = 0;
1326 Utf8Str strMfFile = Utf8Str(pTask->locInfo.strPath).stripExt().append(".mf");
1327 /* Create the import stack for the rollback on errors. */
1328 ImportStack stack(pTask->locInfo, m->pReader->m_mapDisks, pTask->pProgress);
1329 /*
1330 * Try to read the manifest file. First try.
1331 *
1332 * Note: This isn't fatal if the file is not found. The standard
1333 * defines 3 cases.
1334 * 1. no manifest file
1335 * 2. manifest file after the OVF file
1336 * 3. manifest file after all disk files
1337 * If we want streaming capabilities, we can't check if it is there by
1338 * searching for it. We have to try to open it on all possible places.
1339 * If it fails here, we will try it again after all disks where read.
1340 */
1341 rc = readTarManifestFile(tar, strMfFile, &pvMfBuf, &cbMfSize, pCallbacks, pStorage);
1342 if (FAILED(rc)) throw rc;
1343 /* Now import the appliance. */
1344 importMachines(stack, pCallbacks, pStorage);
1345 /* Try to read the manifest file. Second try. */
1346 if (!pvMfBuf)
1347 {
1348 rc = readTarManifestFile(tar, strMfFile, &pvMfBuf, &cbMfSize, pCallbacks, pStorage);
1349 if (FAILED(rc)) throw rc;
1350 }
1351 /* If we were able to read a manifest file we can check it now. */
1352 if (pvMfBuf)
1353 {
1354 /* Add the ovf file to the digest list. */
1355 stack.llSrcDisksDigest.push_front(STRPAIR(Utf8Str(pTask->locInfo.strPath).stripExt().append(".ovf"), m->strOVFSHA1Digest));
1356 rc = verifyManifestFile(strMfFile, stack, pvMfBuf, cbMfSize);
1357 if (FAILED(rc)) throw rc;
1358 }
1359 }
1360 catch (HRESULT rc2)
1361 {
1362 rc = rc2;
1363 }
1364 writeLock.acquire();
1365
1366 RTTarClose(tar);
1367
1368 /* Cleanup */
1369 if (pvMfBuf)
1370 RTMemFree(pvMfBuf);
1371 if (pRTSha1Callbacks)
1372 RTMemFree(pRTSha1Callbacks);
1373 if (pRTTarCallbacks)
1374 RTMemFree(pRTTarCallbacks);
1375
1376 LogFlowFunc(("rc=%Rhrc\n", rc));
1377 LogFlowFuncLeave();
1378
1379 return rc;
1380}
1381
1382/**
1383 * Worker code for importing OVF from the cloud. This is called from Appliance::taskThreadImportOrExport()
1384 * in S3 mode and therefore runs on the OVF import worker thread. This then starts a second worker
1385 * thread to import from temporary files (see Appliance::importFS()).
1386 * @param pTask
1387 * @return
1388 */
1389HRESULT Appliance::importS3(TaskOVF *pTask)
1390{
1391 LogFlowFuncEnter();
1392 LogFlowFunc(("Appliance %p\n", this));
1393
1394 AutoCaller autoCaller(this);
1395 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1396
1397 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
1398
1399 int vrc = VINF_SUCCESS;
1400 RTS3 hS3 = NIL_RTS3;
1401 char szOSTmpDir[RTPATH_MAX];
1402 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));
1403 /* The template for the temporary directory created below */
1404 char *pszTmpDir = RTPathJoinA(szOSTmpDir, "vbox-ovf-XXXXXX");
1405 list< pair<Utf8Str, ULONG> > filesList;
1406
1407 HRESULT rc = S_OK;
1408 try
1409 {
1410 /* Extract the bucket */
1411 Utf8Str tmpPath = pTask->locInfo.strPath;
1412 Utf8Str bucket;
1413 parseBucket(tmpPath, bucket);
1414
1415 /* We need a temporary directory which we can put the all disk images
1416 * in */
1417 vrc = RTDirCreateTemp(pszTmpDir);
1418 if (RT_FAILURE(vrc))
1419 throw setError(VBOX_E_FILE_ERROR,
1420 tr("Cannot create temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);
1421
1422 /* Add every disks of every virtual system to an internal list */
1423 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
1424 for (it = m->virtualSystemDescriptions.begin();
1425 it != m->virtualSystemDescriptions.end();
1426 ++it)
1427 {
1428 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
1429 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
1430 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
1431 for (itH = avsdeHDs.begin();
1432 itH != avsdeHDs.end();
1433 ++itH)
1434 {
1435 const Utf8Str &strTargetFile = (*itH)->strOvf;
1436 if (!strTargetFile.isEmpty())
1437 {
1438 /* The temporary name of the target disk file */
1439 Utf8StrFmt strTmpDisk("%s/%s", pszTmpDir, RTPathFilename(strTargetFile.c_str()));
1440 filesList.push_back(pair<Utf8Str, ULONG>(strTmpDisk, (*itH)->ulSizeMB));
1441 }
1442 }
1443 }
1444
1445 /* Next we have to download the disk images */
1446 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);
1447 if (RT_FAILURE(vrc))
1448 throw setError(VBOX_E_IPRT_ERROR,
1449 tr("Cannot create S3 service handler"));
1450 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask);
1451
1452 /* Download all files */
1453 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
1454 {
1455 const pair<Utf8Str, ULONG> &s = (*it1);
1456 const Utf8Str &strSrcFile = s.first;
1457 /* Construct the source file name */
1458 char *pszFilename = RTPathFilename(strSrcFile.c_str());
1459 /* Advance to the next operation */
1460 if (!pTask->pProgress.isNull())
1461 pTask->pProgress->SetNextOperation(BstrFmt(tr("Downloading file '%s'"), pszFilename).raw(), s.second);
1462
1463 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strSrcFile.c_str());
1464 if (RT_FAILURE(vrc))
1465 {
1466 if (vrc == VERR_S3_CANCELED)
1467 throw S_OK; /* todo: !!!!!!!!!!!!! */
1468 else if (vrc == VERR_S3_ACCESS_DENIED)
1469 throw setError(E_ACCESSDENIED,
1470 tr("Cannot download file '%s' from S3 storage server (Access denied). "
1471 "Make sure that your credentials are right. Also check that your host clock is properly synced"),
1472 pszFilename);
1473 else if (vrc == VERR_S3_NOT_FOUND)
1474 throw setError(VBOX_E_FILE_ERROR,
1475 tr("Cannot download file '%s' from S3 storage server (File not found)"),
1476 pszFilename);
1477 else
1478 throw setError(VBOX_E_IPRT_ERROR,
1479 tr("Cannot download file '%s' from S3 storage server (%Rrc)"),
1480 pszFilename, vrc);
1481 }
1482 }
1483
1484 /* Provide a OVF file (haven't to exist) so the import routine can
1485 * figure out where the disk images/manifest file are located. */
1486 Utf8StrFmt strTmpOvf("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str()));
1487 /* Now check if there is an manifest file. This is optional. */
1488 Utf8Str strManifestFile; //= queryManifestFileName(strTmpOvf);
1489// Utf8Str strManifestFile = queryManifestFileName(strTmpOvf);
1490 char *pszFilename = RTPathFilename(strManifestFile.c_str());
1491 if (!pTask->pProgress.isNull())
1492 pTask->pProgress->SetNextOperation(BstrFmt(tr("Downloading file '%s'"), pszFilename).raw(), 1);
1493
1494 /* Try to download it. If the error is VERR_S3_NOT_FOUND, it isn't fatal. */
1495 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strManifestFile.c_str());
1496 if (RT_SUCCESS(vrc))
1497 filesList.push_back(pair<Utf8Str, ULONG>(strManifestFile, 0));
1498 else if (RT_FAILURE(vrc))
1499 {
1500 if (vrc == VERR_S3_CANCELED)
1501 throw S_OK; /* todo: !!!!!!!!!!!!! */
1502 else if (vrc == VERR_S3_NOT_FOUND)
1503 vrc = VINF_SUCCESS; /* Not found is ok */
1504 else if (vrc == VERR_S3_ACCESS_DENIED)
1505 throw setError(E_ACCESSDENIED,
1506 tr("Cannot download file '%s' from S3 storage server (Access denied)."
1507 "Make sure that your credentials are right. Also check that your host clock is properly synced"),
1508 pszFilename);
1509 else
1510 throw setError(VBOX_E_IPRT_ERROR,
1511 tr("Cannot download file '%s' from S3 storage server (%Rrc)"),
1512 pszFilename, vrc);
1513 }
1514
1515 /* Close the connection early */
1516 RTS3Destroy(hS3);
1517 hS3 = NIL_RTS3;
1518
1519 pTask->pProgress->SetNextOperation(BstrFmt(tr("Importing appliance")).raw(), m->ulWeightForXmlOperation);
1520
1521 ComObjPtr<Progress> progress;
1522 /* Import the whole temporary OVF & the disk images */
1523 LocationInfo li;
1524 li.strPath = strTmpOvf;
1525 rc = importImpl(li, progress);
1526 if (FAILED(rc)) throw rc;
1527
1528 /* Unlock the appliance for the fs import thread */
1529 appLock.release();
1530 /* Wait until the import is done, but report the progress back to the
1531 caller */
1532 ComPtr<IProgress> progressInt(progress);
1533 waitForAsyncProgress(pTask->pProgress, progressInt); /* Any errors will be thrown */
1534
1535 /* Again lock the appliance for the next steps */
1536 appLock.acquire();
1537 }
1538 catch(HRESULT aRC)
1539 {
1540 rc = aRC;
1541 }
1542 /* Cleanup */
1543 RTS3Destroy(hS3);
1544 /* Delete all files which where temporary created */
1545 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
1546 {
1547 const char *pszFilePath = (*it1).first.c_str();
1548 if (RTPathExists(pszFilePath))
1549 {
1550 vrc = RTFileDelete(pszFilePath);
1551 if (RT_FAILURE(vrc))
1552 rc = setError(VBOX_E_FILE_ERROR,
1553 tr("Cannot delete file '%s' (%Rrc)"), pszFilePath, vrc);
1554 }
1555 }
1556 /* Delete the temporary directory */
1557 if (RTPathExists(pszTmpDir))
1558 {
1559 vrc = RTDirRemove(pszTmpDir);
1560 if (RT_FAILURE(vrc))
1561 rc = setError(VBOX_E_FILE_ERROR,
1562 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);
1563 }
1564 if (pszTmpDir)
1565 RTStrFree(pszTmpDir);
1566
1567 LogFlowFunc(("rc=%Rhrc\n", rc));
1568 LogFlowFuncLeave();
1569
1570 return rc;
1571}
1572
1573HRESULT Appliance::readManifestFile(const Utf8Str &strFile, void **ppvBuf, size_t *pcbSize, PVDINTERFACEIO pCallbacks, PRTSHA1STORAGE pStorage)
1574{
1575 HRESULT rc = S_OK;
1576
1577 bool fOldDigest = pStorage->fCreateDigest;
1578 pStorage->fCreateDigest = false; /* No digest for the manifest file */
1579 int vrc = RTSha1ReadBuf(strFile.c_str(), ppvBuf, pcbSize, pCallbacks, pStorage);
1580 if ( RT_FAILURE(vrc)
1581 && vrc != VERR_FILE_NOT_FOUND)
1582 rc = setError(VBOX_E_FILE_ERROR,
1583 tr("Could not read manifest file '%s' (%Rrc)"),
1584 RTPathFilename(strFile.c_str()), vrc);
1585 pStorage->fCreateDigest = fOldDigest; /* Restore the old digest creation behavior again. */
1586
1587 return rc;
1588}
1589
1590HRESULT Appliance::readTarManifestFile(RTTAR tar, const Utf8Str &strFile, void **ppvBuf, size_t *pcbSize, PVDINTERFACEIO pCallbacks, PRTSHA1STORAGE pStorage)
1591{
1592 HRESULT rc = S_OK;
1593
1594 char *pszCurFile;
1595 int vrc = RTTarCurrentFile(tar, &pszCurFile);
1596 if (RT_SUCCESS(vrc))
1597 {
1598 if (!strcmp(pszCurFile, RTPathFilename(strFile.c_str())))
1599 rc = readManifestFile(strFile, ppvBuf, pcbSize, pCallbacks, pStorage);
1600 RTStrFree(pszCurFile);
1601 }
1602 else if (vrc != VERR_TAR_END_OF_FILE)
1603 rc = E_FAIL;
1604
1605 return rc;
1606}
1607
1608HRESULT Appliance::verifyManifestFile(const Utf8Str &strFile, ImportStack &stack, void *pvBuf, size_t cbSize)
1609{
1610 HRESULT rc = S_OK;
1611
1612 PRTMANIFESTTEST paTests = (PRTMANIFESTTEST)RTMemAlloc(sizeof(RTMANIFESTTEST) * stack.llSrcDisksDigest.size());
1613 if (!paTests)
1614 return E_OUTOFMEMORY;
1615
1616 size_t i = 0;
1617 list<STRPAIR>::const_iterator it1;
1618 for (it1 = stack.llSrcDisksDigest.begin();
1619 it1 != stack.llSrcDisksDigest.end();
1620 ++it1, ++i)
1621 {
1622 paTests[i].pszTestFile = (*it1).first.c_str();
1623 paTests[i].pszTestDigest = (*it1).second.c_str();
1624 }
1625 size_t iFailed;
1626 int vrc = RTManifestVerifyFilesBuf(pvBuf, cbSize, paTests, stack.llSrcDisksDigest.size(), &iFailed);
1627 if (RT_UNLIKELY(vrc == VERR_MANIFEST_DIGEST_MISMATCH))
1628 rc = setError(VBOX_E_FILE_ERROR,
1629 tr("The SHA1 digest of '%s' does not match the one in '%s' (%Rrc)"),
1630 RTPathFilename(paTests[iFailed].pszTestFile), RTPathFilename(strFile.c_str()), vrc);
1631 else if (RT_FAILURE(vrc))
1632 rc = setError(VBOX_E_FILE_ERROR,
1633 tr("Could not verify the content of '%s' against the available files (%Rrc)"),
1634 RTPathFilename(strFile.c_str()), vrc);
1635
1636 RTMemFree(paTests);
1637
1638 return rc;
1639}
1640
1641
1642/**
1643 * Helper that converts VirtualSystem attachment values into VirtualBox attachment values.
1644 * Throws HRESULT values on errors!
1645 *
1646 * @param hdc in: the HardDiskController structure to attach to.
1647 * @param ulAddressOnParent in: the AddressOnParent parameter from OVF.
1648 * @param controllerType out: the name of the hard disk controller to attach to (e.g. "IDE Controller").
1649 * @param lControllerPort out: the channel (controller port) of the controller to attach to.
1650 * @param lDevice out: the device number to attach to.
1651 */
1652void Appliance::convertDiskAttachmentValues(const ovf::HardDiskController &hdc,
1653 uint32_t ulAddressOnParent,
1654 Bstr &controllerType,
1655 int32_t &lControllerPort,
1656 int32_t &lDevice)
1657{
1658 Log(("Appliance::convertDiskAttachmentValues: hdc.system=%d, hdc.fPrimary=%d, ulAddressOnParent=%d\n", hdc.system, hdc.fPrimary, ulAddressOnParent));
1659
1660 switch (hdc.system)
1661 {
1662 case ovf::HardDiskController::IDE:
1663 // For the IDE bus, the port parameter can be either 0 or 1, to specify the primary
1664 // or secondary IDE controller, respectively. For the primary controller of the IDE bus,
1665 // the device number can be either 0 or 1, to specify the master or the slave device,
1666 // respectively. For the secondary IDE controller, the device number is always 1 because
1667 // the master device is reserved for the CD-ROM drive.
1668 controllerType = Bstr("IDE Controller");
1669 switch (ulAddressOnParent)
1670 {
1671 case 0: // master
1672 if (!hdc.fPrimary)
1673 {
1674 // secondary master
1675 lControllerPort = (long)1;
1676 lDevice = (long)0;
1677 }
1678 else // primary master
1679 {
1680 lControllerPort = (long)0;
1681 lDevice = (long)0;
1682 }
1683 break;
1684
1685 case 1: // slave
1686 if (!hdc.fPrimary)
1687 {
1688 // secondary slave
1689 lControllerPort = (long)1;
1690 lDevice = (long)1;
1691 }
1692 else // primary slave
1693 {
1694 lControllerPort = (long)0;
1695 lDevice = (long)1;
1696 }
1697 break;
1698
1699 // used by older VBox exports
1700 case 2: // interpret this as secondary master
1701 lControllerPort = (long)1;
1702 lDevice = (long)0;
1703 break;
1704
1705 // used by older VBox exports
1706 case 3: // interpret this as secondary slave
1707 lControllerPort = (long)1;
1708 lDevice = (long)1;
1709 break;
1710
1711 default:
1712 throw setError(VBOX_E_NOT_SUPPORTED,
1713 tr("Invalid channel %RI16 specified; IDE controllers support only 0, 1 or 2"),
1714 ulAddressOnParent);
1715 break;
1716 }
1717 break;
1718
1719 case ovf::HardDiskController::SATA:
1720 controllerType = Bstr("SATA Controller");
1721 lControllerPort = (long)ulAddressOnParent;
1722 lDevice = (long)0;
1723 break;
1724
1725 case ovf::HardDiskController::SCSI:
1726 controllerType = Bstr("SCSI Controller");
1727 lControllerPort = (long)ulAddressOnParent;
1728 lDevice = (long)0;
1729 break;
1730
1731 default: break;
1732 }
1733
1734 Log(("=> lControllerPort=%d, lDevice=%d\n", lControllerPort, lDevice));
1735}
1736
1737/**
1738 * Imports one disk image. This is common code shared between
1739 * -- importMachineGeneric() for the OVF case; in that case the information comes from
1740 * the OVF virtual systems;
1741 * -- importVBoxMachine(); in that case, the information comes from the <vbox:Machine>
1742 * tag.
1743 *
1744 * Both ways of describing machines use the OVF disk references section, so in both cases
1745 * the caller needs to pass in the ovf::DiskImage structure from ovfreader.cpp.
1746 *
1747 * As a result, in both cases, if di.strHref is empty, we create a new disk as per the OVF
1748 * spec, even though this cannot really happen in the vbox:Machine case since such data
1749 * would never have been exported.
1750 *
1751 * This advances stack.pProgress by one operation with the disk's weight.
1752 *
1753 * @param di ovfreader.cpp structure describing the disk image from the OVF that is to be imported
1754 * @param ulSizeMB Size of the disk image (for progress reporting)
1755 * @param strTargetPath Where to create the target image.
1756 * @param pTargetHD out: The newly created target disk. This also gets pushed on stack.llHardDisksCreated for cleanup.
1757 * @param stack
1758 */
1759void Appliance::importOneDiskImage(const ovf::DiskImage &di,
1760 const Utf8Str &strTargetPath,
1761 ComObjPtr<Medium> &pTargetHD,
1762 ImportStack &stack,
1763 PVDINTERFACEIO pCallbacks,
1764 PRTSHA1STORAGE pStorage)
1765{
1766 ComObjPtr<Progress> pProgress;
1767 pProgress.createObject();
1768 HRESULT rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this), BstrFmt(tr("Creating medium '%s'"), strTargetPath.c_str()).raw(), TRUE);
1769 if (FAILED(rc)) throw rc;
1770
1771 /* Get the system properties. */
1772 SystemProperties *pSysProps = mVirtualBox->getSystemProperties();
1773
1774 /* First of all check if the path is an UUID. If so, the user like to
1775 * import the disk into an existing path. This is useful for iSCSI for
1776 * example. */
1777 RTUUID uuid;
1778 int vrc = RTUuidFromStr(&uuid, strTargetPath.c_str());
1779 if (vrc == VINF_SUCCESS)
1780 {
1781 rc = mVirtualBox->findHardDiskById(Guid(uuid), true, &pTargetHD);
1782 if (FAILED(rc)) throw rc;
1783 }
1784 else
1785 {
1786 Utf8Str strTrgFormat = "VMDK";
1787 if (RTPathHaveExt(strTargetPath.c_str()))
1788 {
1789 char *pszExt = RTPathExt(strTargetPath.c_str());
1790 /* Figure out which format the user like to have. Default is VMDK. */
1791 ComObjPtr<MediumFormat> trgFormat = pSysProps->mediumFormatFromExtension(&pszExt[1]);
1792 if (trgFormat.isNull())
1793 throw setError(VBOX_E_NOT_SUPPORTED,
1794 tr("Could not find a valid medium format for the target disk '%s'"),
1795 strTargetPath.c_str());
1796 /* Check the capabilities. We need create capabilities. */
1797 ULONG lCabs = 0;
1798 rc = trgFormat->COMGETTER(Capabilities)(&lCabs);
1799 if (FAILED(rc)) throw rc;
1800 if (!( ((lCabs & MediumFormatCapabilities_CreateFixed) == MediumFormatCapabilities_CreateFixed)
1801 || ((lCabs & MediumFormatCapabilities_CreateDynamic) == MediumFormatCapabilities_CreateDynamic)))
1802 throw setError(VBOX_E_NOT_SUPPORTED,
1803 tr("Could not find a valid medium format for the target disk '%s'"),
1804 strTargetPath.c_str());
1805 Bstr bstrFormatName;
1806 rc = trgFormat->COMGETTER(Name)(bstrFormatName.asOutParam());
1807 if (FAILED(rc)) throw rc;
1808 strTrgFormat = Utf8Str(bstrFormatName);
1809 }
1810
1811 bool fNeedsGlobalSaveSettings;
1812 /* Create an IMedium object. */
1813 pTargetHD.createObject();
1814 rc = pTargetHD->init(mVirtualBox,
1815 strTrgFormat,
1816 strTargetPath,
1817 Guid::Empty, // media registry
1818 &fNeedsGlobalSaveSettings);
1819 if (FAILED(rc)) throw rc;
1820
1821 /* Now create an empty hard disk. */
1822 rc = mVirtualBox->CreateHardDisk(NULL,
1823 Bstr(strTargetPath).raw(),
1824 ComPtr<IMedium>(pTargetHD).asOutParam());
1825 if (FAILED(rc)) throw rc;
1826 }
1827
1828 const Utf8Str &strSourceOVF = di.strHref;
1829 /* Construct source file path */
1830 Utf8StrFmt strSrcFilePath("%s%c%s", stack.strSourceDir.c_str(), RTPATH_DELIMITER, strSourceOVF.c_str());
1831
1832 /* If strHref is empty we have to create a new file. */
1833 if (strSourceOVF.isEmpty())
1834 {
1835 /* Create a dynamic growing disk image with the given capacity. */
1836 rc = pTargetHD->CreateBaseStorage(di.iCapacity / _1M, MediumVariant_Standard, ComPtr<IProgress>(pProgress).asOutParam());
1837 if (FAILED(rc)) throw rc;
1838
1839 /* Advance to the next operation. */
1840 stack.pProgress->SetNextOperation(BstrFmt(tr("Creating disk image '%s'"), strTargetPath.c_str()).raw(),
1841 di.ulSuggestedSizeMB); // operation's weight, as set up with the IProgress originally
1842 }
1843 else
1844 {
1845 /* We need a proper source format description */
1846 ComObjPtr<MediumFormat> srcFormat;
1847 /* Which format to use? */
1848 Utf8Str strSrcFormat = "VDI";
1849 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)
1850 || di.strFormat.compare("http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized", Utf8Str::CaseInsensitive)
1851 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive)
1852 || di.strFormat.compare("http://www.vmware.com/interfaces/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive)
1853 )
1854 strSrcFormat = "VMDK";
1855 srcFormat = pSysProps->mediumFormat(strSrcFormat);
1856 if (srcFormat.isNull())
1857 throw setError(VBOX_E_NOT_SUPPORTED,
1858 tr("Could not find a valid medium format for the source disk '%s'"),
1859 RTPathFilename(strSrcFilePath.c_str()));
1860
1861 /* Clone the source disk image */
1862 ComObjPtr<Medium> nullParent;
1863 rc = pTargetHD->importFile(strSrcFilePath.c_str(),
1864 srcFormat,
1865 MediumVariant_Standard,
1866 pCallbacks, pStorage,
1867 nullParent,
1868 pProgress);
1869 if (FAILED(rc)) throw rc;
1870
1871 /* Advance to the next operation. */
1872 stack.pProgress->SetNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), RTPathFilename(strSrcFilePath.c_str())).raw(),
1873 di.ulSuggestedSizeMB); // operation's weight, as set up with the IProgress originally);
1874 }
1875
1876 /* Now wait for the background disk operation to complete; this throws
1877 * HRESULTs on error. */
1878 ComPtr<IProgress> pp(pProgress);
1879 waitForAsyncProgress(stack.pProgress, pp);
1880
1881 /* Add the newly create disk path + a corresponding digest the our list for
1882 * later manifest verification. */
1883 stack.llSrcDisksDigest.push_back(STRPAIR(strSrcFilePath, pStorage->strDigest));
1884}
1885
1886/**
1887 * Imports one OVF virtual system (described by the given ovf::VirtualSystem and VirtualSystemDescription)
1888 * into VirtualBox by creating an IMachine instance, which is returned.
1889 *
1890 * This throws HRESULT error codes for anything that goes wrong, in which case the caller must clean
1891 * up any leftovers from this function. For this, the given ImportStack instance has received information
1892 * about what needs cleaning up (to support rollback).
1893 *
1894 * @param vsysThis OVF virtual system (machine) to import.
1895 * @param vsdescThis Matching virtual system description (machine) to import.
1896 * @param pNewMachine out: Newly created machine.
1897 * @param stack Cleanup stack for when this throws.
1898 */
1899void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis,
1900 ComObjPtr<VirtualSystemDescription> &vsdescThis,
1901 ComPtr<IMachine> &pNewMachine,
1902 ImportStack &stack,
1903 PVDINTERFACEIO pCallbacks,
1904 PRTSHA1STORAGE pStorage)
1905{
1906 HRESULT rc;
1907
1908 // Get the instance of IGuestOSType which matches our string guest OS type so we
1909 // can use recommended defaults for the new machine where OVF doesn't provide any
1910 ComPtr<IGuestOSType> osType;
1911 rc = mVirtualBox->GetGuestOSType(Bstr(stack.strOsTypeVBox).raw(), osType.asOutParam());
1912 if (FAILED(rc)) throw rc;
1913
1914 /* Create the machine */
1915 rc = mVirtualBox->CreateMachine(NULL, /* machine name: use default */
1916 Bstr(stack.strNameVBox).raw(),
1917 Bstr(stack.strOsTypeVBox).raw(),
1918 NULL, /* uuid */
1919 FALSE, /* fForceOverwrite */
1920 pNewMachine.asOutParam());
1921 if (FAILED(rc)) throw rc;
1922
1923 // set the description
1924 if (!stack.strDescription.isEmpty())
1925 {
1926 rc = pNewMachine->COMSETTER(Description)(Bstr(stack.strDescription).raw());
1927 if (FAILED(rc)) throw rc;
1928 }
1929
1930 // CPU count
1931 rc = pNewMachine->COMSETTER(CPUCount)(stack.cCPUs);
1932 if (FAILED(rc)) throw rc;
1933
1934 if (stack.fForceHWVirt)
1935 {
1936 rc = pNewMachine->SetHWVirtExProperty(HWVirtExPropertyType_Enabled, TRUE);
1937 if (FAILED(rc)) throw rc;
1938 }
1939
1940 // RAM
1941 rc = pNewMachine->COMSETTER(MemorySize)(stack.ulMemorySizeMB);
1942 if (FAILED(rc)) throw rc;
1943
1944 /* VRAM */
1945 /* Get the recommended VRAM for this guest OS type */
1946 ULONG vramVBox;
1947 rc = osType->COMGETTER(RecommendedVRAM)(&vramVBox);
1948 if (FAILED(rc)) throw rc;
1949
1950 /* Set the VRAM */
1951 rc = pNewMachine->COMSETTER(VRAMSize)(vramVBox);
1952 if (FAILED(rc)) throw rc;
1953
1954 // I/O APIC: Generic OVF has no setting for this. Enable it if we
1955 // import a Windows VM because if if Windows was installed without IOAPIC,
1956 // it will not mind finding an one later on, but if Windows was installed
1957 // _with_ an IOAPIC, it will bluescreen if it's not found
1958 if (!stack.fForceIOAPIC)
1959 {
1960 Bstr bstrFamilyId;
1961 rc = osType->COMGETTER(FamilyId)(bstrFamilyId.asOutParam());
1962 if (FAILED(rc)) throw rc;
1963 if (bstrFamilyId == "Windows")
1964 stack.fForceIOAPIC = true;
1965 }
1966
1967 if (stack.fForceIOAPIC)
1968 {
1969 ComPtr<IBIOSSettings> pBIOSSettings;
1970 rc = pNewMachine->COMGETTER(BIOSSettings)(pBIOSSettings.asOutParam());
1971 if (FAILED(rc)) throw rc;
1972
1973 rc = pBIOSSettings->COMSETTER(IOAPICEnabled)(TRUE);
1974 if (FAILED(rc)) throw rc;
1975 }
1976
1977 if (!stack.strAudioAdapter.isEmpty())
1978 if (stack.strAudioAdapter.compare("null", Utf8Str::CaseInsensitive) != 0)
1979 {
1980 uint32_t audio = RTStrToUInt32(stack.strAudioAdapter.c_str()); // should be 0 for AC97
1981 ComPtr<IAudioAdapter> audioAdapter;
1982 rc = pNewMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
1983 if (FAILED(rc)) throw rc;
1984 rc = audioAdapter->COMSETTER(Enabled)(true);
1985 if (FAILED(rc)) throw rc;
1986 rc = audioAdapter->COMSETTER(AudioController)(static_cast<AudioControllerType_T>(audio));
1987 if (FAILED(rc)) throw rc;
1988 }
1989
1990#ifdef VBOX_WITH_USB
1991 /* USB Controller */
1992 ComPtr<IUSBController> usbController;
1993 rc = pNewMachine->COMGETTER(USBController)(usbController.asOutParam());
1994 if (FAILED(rc)) throw rc;
1995 rc = usbController->COMSETTER(Enabled)(stack.fUSBEnabled);
1996 if (FAILED(rc)) throw rc;
1997#endif /* VBOX_WITH_USB */
1998
1999 /* Change the network adapters */
2000 std::list<VirtualSystemDescriptionEntry*> vsdeNW = vsdescThis->findByType(VirtualSystemDescriptionType_NetworkAdapter);
2001 if (vsdeNW.size() == 0)
2002 {
2003 /* No network adapters, so we have to disable our default one */
2004 ComPtr<INetworkAdapter> nwVBox;
2005 rc = pNewMachine->GetNetworkAdapter(0, nwVBox.asOutParam());
2006 if (FAILED(rc)) throw rc;
2007 rc = nwVBox->COMSETTER(Enabled)(false);
2008 if (FAILED(rc)) throw rc;
2009 }
2010 else if (vsdeNW.size() > SchemaDefs::NetworkAdapterCount)
2011 throw setError(VBOX_E_FILE_ERROR,
2012 tr("Too many network adapters: OVF requests %d network adapters, but VirtualBox only supports %d"),
2013 vsdeNW.size(), SchemaDefs::NetworkAdapterCount);
2014 else
2015 {
2016 list<VirtualSystemDescriptionEntry*>::const_iterator nwIt;
2017 size_t a = 0;
2018 for (nwIt = vsdeNW.begin();
2019 nwIt != vsdeNW.end();
2020 ++nwIt, ++a)
2021 {
2022 const VirtualSystemDescriptionEntry* pvsys = *nwIt;
2023
2024 const Utf8Str &nwTypeVBox = pvsys->strVboxCurrent;
2025 uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str());
2026 ComPtr<INetworkAdapter> pNetworkAdapter;
2027 rc = pNewMachine->GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
2028 if (FAILED(rc)) throw rc;
2029 /* Enable the network card & set the adapter type */
2030 rc = pNetworkAdapter->COMSETTER(Enabled)(true);
2031 if (FAILED(rc)) throw rc;
2032 rc = pNetworkAdapter->COMSETTER(AdapterType)(static_cast<NetworkAdapterType_T>(tt1));
2033 if (FAILED(rc)) throw rc;
2034
2035 // default is NAT; change to "bridged" if extra conf says so
2036 if (pvsys->strExtraConfigCurrent.endsWith("type=Bridged", Utf8Str::CaseInsensitive))
2037 {
2038 /* Attach to the right interface */
2039 rc = pNetworkAdapter->AttachToBridgedInterface();
2040 if (FAILED(rc)) throw rc;
2041 ComPtr<IHost> host;
2042 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam());
2043 if (FAILED(rc)) throw rc;
2044 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;
2045 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));
2046 if (FAILED(rc)) throw rc;
2047 // We search for the first host network interface which
2048 // is usable for bridged networking
2049 for (size_t j = 0;
2050 j < nwInterfaces.size();
2051 ++j)
2052 {
2053 HostNetworkInterfaceType_T itype;
2054 rc = nwInterfaces[j]->COMGETTER(InterfaceType)(&itype);
2055 if (FAILED(rc)) throw rc;
2056 if (itype == HostNetworkInterfaceType_Bridged)
2057 {
2058 Bstr name;
2059 rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam());
2060 if (FAILED(rc)) throw rc;
2061 /* Set the interface name to attach to */
2062 pNetworkAdapter->COMSETTER(HostInterface)(name.raw());
2063 if (FAILED(rc)) throw rc;
2064 break;
2065 }
2066 }
2067 }
2068 /* Next test for host only interfaces */
2069 else if (pvsys->strExtraConfigCurrent.endsWith("type=HostOnly", Utf8Str::CaseInsensitive))
2070 {
2071 /* Attach to the right interface */
2072 rc = pNetworkAdapter->AttachToHostOnlyInterface();
2073 if (FAILED(rc)) throw rc;
2074 ComPtr<IHost> host;
2075 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam());
2076 if (FAILED(rc)) throw rc;
2077 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;
2078 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));
2079 if (FAILED(rc)) throw rc;
2080 // We search for the first host network interface which
2081 // is usable for host only networking
2082 for (size_t j = 0;
2083 j < nwInterfaces.size();
2084 ++j)
2085 {
2086 HostNetworkInterfaceType_T itype;
2087 rc = nwInterfaces[j]->COMGETTER(InterfaceType)(&itype);
2088 if (FAILED(rc)) throw rc;
2089 if (itype == HostNetworkInterfaceType_HostOnly)
2090 {
2091 Bstr name;
2092 rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam());
2093 if (FAILED(rc)) throw rc;
2094 /* Set the interface name to attach to */
2095 pNetworkAdapter->COMSETTER(HostInterface)(name.raw());
2096 if (FAILED(rc)) throw rc;
2097 break;
2098 }
2099 }
2100 }
2101 /* Next test for internal interfaces */
2102 else if (pvsys->strExtraConfigCurrent.endsWith("type=Internal", Utf8Str::CaseInsensitive))
2103 {
2104 /* Attach to the right interface */
2105 rc = pNetworkAdapter->AttachToInternalNetwork();
2106 if (FAILED(rc)) throw rc;
2107 }
2108 /* Next test for VDE interfaces */
2109 else if (pvsys->strExtraConfigCurrent.endsWith("type=VDE", Utf8Str::CaseInsensitive))
2110 {
2111 /* Attach to the right interface */
2112 rc = pNetworkAdapter->AttachToVDE();
2113 if (FAILED(rc)) throw rc;
2114 }
2115 }
2116 }
2117
2118 // IDE Hard disk controller
2119 std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerIDE);
2120 // In OVF (at least VMware's version of it), an IDE controller has two ports, so VirtualBox's single IDE controller
2121 // with two channels and two ports each counts as two OVF IDE controllers -- so we accept one or two such IDE controllers
2122 size_t cIDEControllers = vsdeHDCIDE.size();
2123 if (cIDEControllers > 2)
2124 throw setError(VBOX_E_FILE_ERROR,
2125 tr("Too many IDE controllers in OVF; import facility only supports two"));
2126 if (vsdeHDCIDE.size() > 0)
2127 {
2128 // one or two IDE controllers present in OVF: add one VirtualBox controller
2129 ComPtr<IStorageController> pController;
2130 rc = pNewMachine->AddStorageController(Bstr("IDE Controller").raw(), StorageBus_IDE, pController.asOutParam());
2131 if (FAILED(rc)) throw rc;
2132
2133 const char *pcszIDEType = vsdeHDCIDE.front()->strVboxCurrent.c_str();
2134 if (!strcmp(pcszIDEType, "PIIX3"))
2135 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX3);
2136 else if (!strcmp(pcszIDEType, "PIIX4"))
2137 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX4);
2138 else if (!strcmp(pcszIDEType, "ICH6"))
2139 rc = pController->COMSETTER(ControllerType)(StorageControllerType_ICH6);
2140 else
2141 throw setError(VBOX_E_FILE_ERROR,
2142 tr("Invalid IDE controller type \"%s\""),
2143 pcszIDEType);
2144 if (FAILED(rc)) throw rc;
2145 }
2146
2147 /* Hard disk controller SATA */
2148 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSATA = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSATA);
2149 if (vsdeHDCSATA.size() > 1)
2150 throw setError(VBOX_E_FILE_ERROR,
2151 tr("Too many SATA controllers in OVF; import facility only supports one"));
2152 if (vsdeHDCSATA.size() > 0)
2153 {
2154 ComPtr<IStorageController> pController;
2155 const Utf8Str &hdcVBox = vsdeHDCSATA.front()->strVboxCurrent;
2156 if (hdcVBox == "AHCI")
2157 {
2158 rc = pNewMachine->AddStorageController(Bstr("SATA Controller").raw(), StorageBus_SATA, pController.asOutParam());
2159 if (FAILED(rc)) throw rc;
2160 }
2161 else
2162 throw setError(VBOX_E_FILE_ERROR,
2163 tr("Invalid SATA controller type \"%s\""),
2164 hdcVBox.c_str());
2165 }
2166
2167 /* Hard disk controller SCSI */
2168 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSCSI = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSCSI);
2169 if (vsdeHDCSCSI.size() > 1)
2170 throw setError(VBOX_E_FILE_ERROR,
2171 tr("Too many SCSI controllers in OVF; import facility only supports one"));
2172 if (vsdeHDCSCSI.size() > 0)
2173 {
2174 ComPtr<IStorageController> pController;
2175 Bstr bstrName(L"SCSI Controller");
2176 StorageBus_T busType = StorageBus_SCSI;
2177 StorageControllerType_T controllerType;
2178 const Utf8Str &hdcVBox = vsdeHDCSCSI.front()->strVboxCurrent;
2179 if (hdcVBox == "LsiLogic")
2180 controllerType = StorageControllerType_LsiLogic;
2181 else if (hdcVBox == "LsiLogicSas")
2182 {
2183 // OVF treats LsiLogicSas as a SCSI controller but VBox considers it a class of its own
2184 bstrName = L"SAS Controller";
2185 busType = StorageBus_SAS;
2186 controllerType = StorageControllerType_LsiLogicSas;
2187 }
2188 else if (hdcVBox == "BusLogic")
2189 controllerType = StorageControllerType_BusLogic;
2190 else
2191 throw setError(VBOX_E_FILE_ERROR,
2192 tr("Invalid SCSI controller type \"%s\""),
2193 hdcVBox.c_str());
2194
2195 rc = pNewMachine->AddStorageController(bstrName.raw(), busType, pController.asOutParam());
2196 if (FAILED(rc)) throw rc;
2197 rc = pController->COMSETTER(ControllerType)(controllerType);
2198 if (FAILED(rc)) throw rc;
2199 }
2200
2201 /* Hard disk controller SAS */
2202 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSAS = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSAS);
2203 if (vsdeHDCSAS.size() > 1)
2204 throw setError(VBOX_E_FILE_ERROR,
2205 tr("Too many SAS controllers in OVF; import facility only supports one"));
2206 if (vsdeHDCSAS.size() > 0)
2207 {
2208 ComPtr<IStorageController> pController;
2209 rc = pNewMachine->AddStorageController(Bstr(L"SAS Controller").raw(), StorageBus_SAS, pController.asOutParam());
2210 if (FAILED(rc)) throw rc;
2211 rc = pController->COMSETTER(ControllerType)(StorageControllerType_LsiLogicSas);
2212 if (FAILED(rc)) throw rc;
2213 }
2214
2215 /* Now its time to register the machine before we add any hard disks */
2216 rc = mVirtualBox->RegisterMachine(pNewMachine);
2217 if (FAILED(rc)) throw rc;
2218
2219 // store new machine for roll-back in case of errors
2220 Bstr bstrNewMachineId;
2221 rc = pNewMachine->COMGETTER(Id)(bstrNewMachineId.asOutParam());
2222 if (FAILED(rc)) throw rc;
2223 Guid uuidNewMachine(bstrNewMachineId);
2224 m->llGuidsMachinesCreated.push_back(uuidNewMachine);
2225
2226 // Add floppies and CD-ROMs to the appropriate controllers.
2227 std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsdescThis->findByType(VirtualSystemDescriptionType_Floppy);
2228 if (vsdeFloppy.size() > 1)
2229 throw setError(VBOX_E_FILE_ERROR,
2230 tr("Too many floppy controllers in OVF; import facility only supports one"));
2231 std::list<VirtualSystemDescriptionEntry*> vsdeCDROM = vsdescThis->findByType(VirtualSystemDescriptionType_CDROM);
2232 if ( (vsdeFloppy.size() > 0)
2233 || (vsdeCDROM.size() > 0)
2234 )
2235 {
2236 // If there's an error here we need to close the session, so
2237 // we need another try/catch block.
2238
2239 try
2240 {
2241 // to attach things we need to open a session for the new machine
2242 rc = pNewMachine->LockMachine(stack.pSession, LockType_Write);
2243 if (FAILED(rc)) throw rc;
2244 stack.fSessionOpen = true;
2245
2246 ComPtr<IMachine> sMachine;
2247 rc = stack.pSession->COMGETTER(Machine)(sMachine.asOutParam());
2248 if (FAILED(rc)) throw rc;
2249
2250 // floppy first
2251 if (vsdeFloppy.size() == 1)
2252 {
2253 ComPtr<IStorageController> pController;
2254 rc = sMachine->AddStorageController(Bstr("Floppy Controller").raw(), StorageBus_Floppy, pController.asOutParam());
2255 if (FAILED(rc)) throw rc;
2256
2257 Bstr bstrName;
2258 rc = pController->COMGETTER(Name)(bstrName.asOutParam());
2259 if (FAILED(rc)) throw rc;
2260
2261 // this is for rollback later
2262 MyHardDiskAttachment mhda;
2263 mhda.pMachine = pNewMachine;
2264 mhda.controllerType = bstrName;
2265 mhda.lControllerPort = 0;
2266 mhda.lDevice = 0;
2267
2268 Log(("Attaching floppy\n"));
2269
2270 rc = sMachine->AttachDevice(mhda.controllerType.raw(),
2271 mhda.lControllerPort,
2272 mhda.lDevice,
2273 DeviceType_Floppy,
2274 NULL);
2275 if (FAILED(rc)) throw rc;
2276
2277 stack.llHardDiskAttachments.push_back(mhda);
2278 }
2279
2280 // CD-ROMs next
2281 for (std::list<VirtualSystemDescriptionEntry*>::const_iterator jt = vsdeCDROM.begin();
2282 jt != vsdeCDROM.end();
2283 ++jt)
2284 {
2285 // for now always attach to secondary master on IDE controller;
2286 // there seems to be no useful information in OVF where else to
2287 // attach it (@todo test with latest versions of OVF software)
2288
2289 // find the IDE controller
2290 const ovf::HardDiskController *pController = NULL;
2291 for (ovf::ControllersMap::const_iterator kt = vsysThis.mapControllers.begin();
2292 kt != vsysThis.mapControllers.end();
2293 ++kt)
2294 {
2295 if (kt->second.system == ovf::HardDiskController::IDE)
2296 {
2297 pController = &kt->second;
2298 break;
2299 }
2300 }
2301
2302 if (!pController)
2303 throw setError(VBOX_E_FILE_ERROR,
2304 tr("OVF wants a CD-ROM drive but cannot find IDE controller, which is required in this version of VirtualBox"));
2305
2306 // this is for rollback later
2307 MyHardDiskAttachment mhda;
2308 mhda.pMachine = pNewMachine;
2309
2310 convertDiskAttachmentValues(*pController,
2311 2, // interpreted as secondary master
2312 mhda.controllerType, // Bstr
2313 mhda.lControllerPort,
2314 mhda.lDevice);
2315
2316 Log(("Attaching CD-ROM to port %d on device %d\n", mhda.lControllerPort, mhda.lDevice));
2317
2318 rc = sMachine->AttachDevice(mhda.controllerType.raw(),
2319 mhda.lControllerPort,
2320 mhda.lDevice,
2321 DeviceType_DVD,
2322 NULL);
2323 if (FAILED(rc)) throw rc;
2324
2325 stack.llHardDiskAttachments.push_back(mhda);
2326 } // end for (itHD = avsdeHDs.begin();
2327
2328 rc = sMachine->SaveSettings();
2329 if (FAILED(rc)) throw rc;
2330
2331 // only now that we're done with all disks, close the session
2332 rc = stack.pSession->UnlockMachine();
2333 if (FAILED(rc)) throw rc;
2334 stack.fSessionOpen = false;
2335 }
2336 catch(HRESULT /* aRC */)
2337 {
2338 if (stack.fSessionOpen)
2339 stack.pSession->UnlockMachine();
2340
2341 throw;
2342 }
2343 }
2344
2345 // create the hard disks & connect them to the appropriate controllers
2346 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
2347 if (avsdeHDs.size() > 0)
2348 {
2349 // If there's an error here we need to close the session, so
2350 // we need another try/catch block.
2351 try
2352 {
2353 // to attach things we need to open a session for the new machine
2354 rc = pNewMachine->LockMachine(stack.pSession, LockType_Write);
2355 if (FAILED(rc)) throw rc;
2356 stack.fSessionOpen = true;
2357
2358 /* Iterate over all given disk images */
2359 list<VirtualSystemDescriptionEntry*>::const_iterator itHD;
2360 for (itHD = avsdeHDs.begin();
2361 itHD != avsdeHDs.end();
2362 ++itHD)
2363 {
2364 VirtualSystemDescriptionEntry *vsdeHD = *itHD;
2365
2366 // vsdeHD->strRef contains the disk identifier (e.g. "vmdisk1"), which should exist
2367 // in the virtual system's disks map under that ID and also in the global images map
2368 ovf::VirtualDisksMap::const_iterator itVirtualDisk = vsysThis.mapVirtualDisks.find(vsdeHD->strRef);
2369 // and find the disk from the OVF's disk list
2370 ovf::DiskImagesMap::const_iterator itDiskImage = stack.mapDisks.find(vsdeHD->strRef);
2371 if ( (itVirtualDisk == vsysThis.mapVirtualDisks.end())
2372 || (itDiskImage == stack.mapDisks.end())
2373 )
2374 throw setError(E_FAIL,
2375 tr("Internal inconsistency looking up disk image '%s'"),
2376 vsdeHD->strRef.c_str());
2377
2378 const ovf::DiskImage &ovfDiskImage = itDiskImage->second;
2379 const ovf::VirtualDisk &ovfVdisk = itVirtualDisk->second;
2380
2381 ComObjPtr<Medium> pTargetHD;
2382 importOneDiskImage(ovfDiskImage,
2383 vsdeHD->strVboxCurrent,
2384 pTargetHD,
2385 stack,
2386 pCallbacks,
2387 pStorage);
2388
2389 // now use the new uuid to attach the disk image to our new machine
2390 ComPtr<IMachine> sMachine;
2391 rc = stack.pSession->COMGETTER(Machine)(sMachine.asOutParam());
2392 if (FAILED(rc)) throw rc;
2393
2394 // find the hard disk controller to which we should attach
2395 ovf::HardDiskController hdc = (*vsysThis.mapControllers.find(ovfVdisk.idController)).second;
2396
2397 // this is for rollback later
2398 MyHardDiskAttachment mhda;
2399 mhda.pMachine = pNewMachine;
2400
2401 convertDiskAttachmentValues(hdc,
2402 ovfVdisk.ulAddressOnParent,
2403 mhda.controllerType, // Bstr
2404 mhda.lControllerPort,
2405 mhda.lDevice);
2406
2407 Log(("Attaching disk %s to port %d on device %d\n", vsdeHD->strVboxCurrent.c_str(), mhda.lControllerPort, mhda.lDevice));
2408
2409 rc = sMachine->AttachDevice(mhda.controllerType.raw(), // wstring name
2410 mhda.lControllerPort, // long controllerPort
2411 mhda.lDevice, // long device
2412 DeviceType_HardDisk, // DeviceType_T type
2413 pTargetHD);
2414 if (FAILED(rc)) throw rc;
2415
2416 stack.llHardDiskAttachments.push_back(mhda);
2417
2418 rc = sMachine->SaveSettings();
2419 if (FAILED(rc)) throw rc;
2420 } // end for (itHD = avsdeHDs.begin();
2421
2422 // only now that we're done with all disks, close the session
2423 rc = stack.pSession->UnlockMachine();
2424 if (FAILED(rc)) throw rc;
2425 stack.fSessionOpen = false;
2426 }
2427 catch(HRESULT /* aRC */)
2428 {
2429 if (stack.fSessionOpen)
2430 stack.pSession->UnlockMachine();
2431
2432 throw;
2433 }
2434 }
2435}
2436
2437/**
2438 * Imports one OVF virtual system (described by a vbox:Machine tag represented by the given config
2439 * structure) into VirtualBox by creating an IMachine instance, which is returned.
2440 *
2441 * This throws HRESULT error codes for anything that goes wrong, in which case the caller must clean
2442 * up any leftovers from this function. For this, the given ImportStack instance has received information
2443 * about what needs cleaning up (to support rollback).
2444 *
2445 * The machine config stored in the settings::MachineConfigFile structure contains the UUIDs of
2446 * the disk attachments used by the machine when it was exported. We also add vbox:uuid attributes
2447 * to the OVF disks sections so we can look them up. While importing these UUIDs into a second host
2448 * will most probably work, reimporting them into the same host will cause conflicts, so we always
2449 * generate new ones on import. This involves the following:
2450 *
2451 * 1) Scan the machine config for disk attachments.
2452 *
2453 * 2) For each disk attachment found, look up the OVF disk image from the disk references section
2454 * and import the disk into VirtualBox, which creates a new UUID for it. In the machine config,
2455 * replace the old UUID with the new one.
2456 *
2457 * 3) Change the machine config according to the OVF virtual system descriptions, in case the
2458 * caller has modified them using setFinalValues().
2459 *
2460 * 4) Create the VirtualBox machine with the modfified machine config.
2461 *
2462 * @param config
2463 * @param pNewMachine
2464 * @param stack
2465 */
2466void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThis,
2467 ComPtr<IMachine> &pReturnNewMachine,
2468 ImportStack &stack,
2469 PVDINTERFACEIO pCallbacks,
2470 PRTSHA1STORAGE pStorage)
2471{
2472 Assert(vsdescThis->m->pConfig);
2473
2474 HRESULT rc = S_OK;
2475
2476 settings::MachineConfigFile &config = *vsdescThis->m->pConfig;
2477
2478 /*
2479 *
2480 * step 1): modify machine config according to OVF config, in case the user
2481 * has modified them using setFinalValues()
2482 *
2483 */
2484
2485 /* OS Type */
2486 config.machineUserData.strOsType = stack.strOsTypeVBox;
2487 /* Description */
2488 config.machineUserData.strDescription = stack.strDescription;
2489 /* CPU count & extented attributes */
2490 config.hardwareMachine.cCPUs = stack.cCPUs;
2491 if (stack.fForceIOAPIC)
2492 config.hardwareMachine.fHardwareVirt = true;
2493 if (stack.fForceIOAPIC)
2494 config.hardwareMachine.biosSettings.fIOAPICEnabled = true;
2495 /* RAM size */
2496 config.hardwareMachine.ulMemorySizeMB = stack.ulMemorySizeMB;
2497
2498/*
2499 <const name="HardDiskControllerIDE" value="14" />
2500 <const name="HardDiskControllerSATA" value="15" />
2501 <const name="HardDiskControllerSCSI" value="16" />
2502 <const name="HardDiskControllerSAS" value="17" />
2503*/
2504
2505#ifdef VBOX_WITH_USB
2506 /* USB controller */
2507 config.hardwareMachine.usbController.fEnabled = stack.fUSBEnabled;
2508#endif
2509 /* Audio adapter */
2510 if (stack.strAudioAdapter.isNotEmpty())
2511 {
2512 config.hardwareMachine.audioAdapter.fEnabled = true;
2513 config.hardwareMachine.audioAdapter.controllerType = (AudioControllerType_T)stack.strAudioAdapter.toUInt32();
2514 }
2515 else
2516 config.hardwareMachine.audioAdapter.fEnabled = false;
2517 /* Network adapter */
2518 settings::NetworkAdaptersList &llNetworkAdapters = config.hardwareMachine.llNetworkAdapters;
2519 /* First disable all network cards, they will be enabled below again. */
2520 settings::NetworkAdaptersList::iterator it1;
2521 for (it1 = llNetworkAdapters.begin(); it1 != llNetworkAdapters.end(); ++it1)
2522 it1->fEnabled = false;
2523 /* Now iterate over all network entries. */
2524 std::list<VirtualSystemDescriptionEntry*> avsdeNWs = vsdescThis->findByType(VirtualSystemDescriptionType_NetworkAdapter);
2525 if (avsdeNWs.size() > 0)
2526 {
2527 /* Iterate through all network adapter entries and search for the
2528 * corrosponding one in the machine config. If one is found, configure
2529 * it based on the user settings. */
2530 list<VirtualSystemDescriptionEntry*>::const_iterator itNW;
2531 for (itNW = avsdeNWs.begin();
2532 itNW != avsdeNWs.end();
2533 ++itNW)
2534 {
2535 VirtualSystemDescriptionEntry *vsdeNW = *itNW;
2536 if ( vsdeNW->strExtraConfigCurrent.startsWith("slot=", Utf8Str::CaseInsensitive)
2537 && vsdeNW->strExtraConfigCurrent.length() > 6)
2538 {
2539 uint32_t iSlot = vsdeNW->strExtraConfigCurrent.substr(5, 1).toUInt32();
2540 /* Iterate through all network adapters in the machine config. */
2541 for (it1 = llNetworkAdapters.begin();
2542 it1 != llNetworkAdapters.end();
2543 ++it1)
2544 {
2545 /* Compare the slots. */
2546 if (it1->ulSlot == iSlot)
2547 {
2548 it1->fEnabled = true;
2549 it1->type = (NetworkAdapterType_T)vsdeNW->strVboxCurrent.toUInt32();
2550 break;
2551 }
2552 }
2553 }
2554 }
2555 }
2556
2557 /* Floppy controller */
2558 bool fFloppy = vsdescThis->findByType(VirtualSystemDescriptionType_Floppy).size() > 0;
2559 /* DVD controller */
2560 bool fDVD = vsdescThis->findByType(VirtualSystemDescriptionType_CDROM).size() > 0;
2561 /* Iterate over all storage controller check the attachments and remove
2562 * them when necessary. */
2563 settings::StorageControllersList &llControllers = config.storageMachine.llStorageControllers;
2564 settings::StorageControllersList::iterator it3;
2565 for (it3 = llControllers.begin();
2566 it3 != llControllers.end();
2567 ++it3)
2568 {
2569 settings::AttachedDevicesList &llAttachments = it3->llAttachedDevices;
2570 settings::AttachedDevicesList::iterator it4;
2571 for (it4 = llAttachments.begin();
2572 it4 != llAttachments.end();
2573 ++it4)
2574 {
2575 if ( ( !fDVD
2576 && it4->deviceType == DeviceType_DVD)
2577 ||
2578 ( !fFloppy
2579 && it4->deviceType == DeviceType_Floppy))
2580 llAttachments.erase(it4++);
2581 }
2582 }
2583
2584
2585 /*
2586 *
2587 * step 2: scan the machine config for media attachments
2588 *
2589 */
2590
2591 // for each storage controller...
2592 for (settings::StorageControllersList::iterator sit = config.storageMachine.llStorageControllers.begin();
2593 sit != config.storageMachine.llStorageControllers.end();
2594 ++sit)
2595 {
2596 settings::StorageController &sc = *sit;
2597
2598 // find the OVF virtual system description entry for this storage controller
2599 switch (sc.storageBus)
2600 {
2601 case StorageBus_SATA:
2602 break;
2603 case StorageBus_SCSI:
2604 break;
2605 case StorageBus_IDE:
2606 break;
2607 case StorageBus_SAS:
2608 break;
2609 }
2610
2611 /* Get all hard disk descriptions. */
2612 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
2613
2614 // for each medium attachment to this controller...
2615 for (settings::AttachedDevicesList::iterator dit = sc.llAttachedDevices.begin();
2616 dit != sc.llAttachedDevices.end();
2617 ++dit)
2618 {
2619 settings::AttachedDevice &d = *dit;
2620
2621 if (d.uuid.isEmpty())
2622 // empty DVD and floppy media
2623 continue;
2624
2625 // convert the Guid to string
2626 Utf8Str strUuid = d.uuid.toString();
2627
2628
2629 // there must be an image in the OVF disk structs with the same UUID
2630 bool fFound = false;
2631 for (ovf::DiskImagesMap::const_iterator oit = stack.mapDisks.begin();
2632 oit != stack.mapDisks.end();
2633 ++oit)
2634 {
2635 const ovf::DiskImage &di = oit->second;
2636
2637 if (di.uuidVbox == strUuid)
2638 {
2639 VirtualSystemDescriptionEntry *vsdeTargetHD = 0;
2640
2641 /* Iterate over all given disk images of the virtual system
2642 * disks description. We need to find the target disk path,
2643 * which could be changed by the user. */
2644 list<VirtualSystemDescriptionEntry*>::const_iterator itHD;
2645 for (itHD = avsdeHDs.begin();
2646 itHD != avsdeHDs.end();
2647 ++itHD)
2648 {
2649 VirtualSystemDescriptionEntry *vsdeHD = *itHD;
2650 if (vsdeHD->strRef == oit->first)
2651 {
2652 vsdeTargetHD = vsdeHD;
2653 break;
2654 }
2655 }
2656 if (!vsdeTargetHD)
2657 throw setError(E_FAIL,
2658 tr("Internal inconsistency looking up disk image '%s'"),
2659 oit->first.c_str());
2660
2661 /*
2662 *
2663 * step 3: import disk
2664 *
2665 */
2666 ComObjPtr<Medium> pTargetHD;
2667 importOneDiskImage(di,
2668 vsdeTargetHD->strVboxCurrent,
2669 pTargetHD,
2670 stack,
2671 pCallbacks,
2672 pStorage);
2673
2674 // ... and replace the old UUID in the machine config with the one of
2675 // the imported disk that was just created
2676 Bstr hdId;
2677 rc = pTargetHD->COMGETTER(Id)(hdId.asOutParam());
2678 if (FAILED(rc)) throw rc;
2679
2680 d.uuid = hdId;
2681
2682 fFound = true;
2683 break;
2684 }
2685 }
2686
2687 // no disk with such a UUID found:
2688 if (!fFound)
2689 throw setError(E_FAIL,
2690 tr("<vbox:Machine> element in OVF contains a medium attachment for the disk image %s but the OVF describes no such image"),
2691 strUuid.c_str());
2692 } // for (settings::AttachedDevicesList::const_iterator dit = sc.llAttachedDevices.begin();
2693 } // for (settings::StorageControllersList::const_iterator sit = config.storageMachine.llStorageControllers.begin();
2694
2695 /*
2696 *
2697 * step 4): create the machine and have it import the config
2698 *
2699 */
2700
2701 ComObjPtr<Machine> pNewMachine;
2702 rc = pNewMachine.createObject();
2703 if (FAILED(rc)) throw rc;
2704
2705 // this magic constructor fills the new machine object with the MachineConfig
2706 // instance that we created from the vbox:Machine
2707 rc = pNewMachine->init(mVirtualBox,
2708 stack.strNameVBox, // name from OVF preparations; can be suffixed to avoid duplicates, or changed by user
2709 config); // the whole machine config
2710 if (FAILED(rc)) throw rc;
2711
2712 pReturnNewMachine = ComPtr<IMachine>(pNewMachine);
2713
2714 // and register it
2715 rc = mVirtualBox->RegisterMachine(pNewMachine);
2716 if (FAILED(rc)) throw rc;
2717
2718 // store new machine for roll-back in case of errors
2719 Bstr bstrNewMachineId;
2720 rc = pNewMachine->COMGETTER(Id)(bstrNewMachineId.asOutParam());
2721 if (FAILED(rc)) throw rc;
2722 m->llGuidsMachinesCreated.push_back(Guid(bstrNewMachineId));
2723}
2724
2725void Appliance::importMachines(ImportStack &stack,
2726 PVDINTERFACEIO pCallbacks,
2727 PRTSHA1STORAGE pStorage)
2728{
2729 HRESULT rc = S_OK;
2730
2731 // this is safe to access because this thread only gets started
2732 // if pReader != NULL
2733 const ovf::OVFReader &reader = *m->pReader;
2734
2735 // create a session for the machine + disks we manipulate below
2736 rc = stack.pSession.createInprocObject(CLSID_Session);
2737 if (FAILED(rc)) throw rc;
2738
2739 list<ovf::VirtualSystem>::const_iterator it;
2740 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1;
2741 /* Iterate through all virtual systems of that appliance */
2742 size_t i = 0;
2743 for (it = reader.m_llVirtualSystems.begin(),
2744 it1 = m->virtualSystemDescriptions.begin();
2745 it != reader.m_llVirtualSystems.end();
2746 ++it, ++it1, ++i)
2747 {
2748 const ovf::VirtualSystem &vsysThis = *it;
2749 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it1);
2750
2751 ComPtr<IMachine> pNewMachine;
2752
2753 // there are two ways in which we can create a vbox machine from OVF:
2754 // -- either this OVF was written by vbox 3.2 or later, in which case there is a <vbox:Machine> element
2755 // in the <VirtualSystem>; then the VirtualSystemDescription::Data has a settings::MachineConfigFile
2756 // with all the machine config pretty-parsed;
2757 // -- or this is an OVF from an older vbox or an external source, and then we need to translate the
2758 // VirtualSystemDescriptionEntry and do import work
2759
2760 // Even for the vbox:Machine case, there are a number of configuration items that will be taken from
2761 // the OVF because otherwise the "override import parameters" mechanism in the GUI won't work.
2762
2763 // VM name
2764 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
2765 if (vsdeName.size() < 1)
2766 throw setError(VBOX_E_FILE_ERROR,
2767 tr("Missing VM name"));
2768 stack.strNameVBox = vsdeName.front()->strVboxCurrent;
2769
2770 // have VirtualBox suggest where the filename would be placed so we can
2771 // put the disk images in the same directory
2772 Bstr bstrMachineFilename;
2773 rc = mVirtualBox->ComposeMachineFilename(Bstr(stack.strNameVBox).raw(),
2774 NULL,
2775 bstrMachineFilename.asOutParam());
2776 if (FAILED(rc)) throw rc;
2777 // and determine the machine folder from that
2778 stack.strMachineFolder = bstrMachineFilename;
2779 stack.strMachineFolder.stripFilename();
2780
2781 // guest OS type
2782 std::list<VirtualSystemDescriptionEntry*> vsdeOS;
2783 vsdeOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
2784 if (vsdeOS.size() < 1)
2785 throw setError(VBOX_E_FILE_ERROR,
2786 tr("Missing guest OS type"));
2787 stack.strOsTypeVBox = vsdeOS.front()->strVboxCurrent;
2788
2789 // CPU count
2790 std::list<VirtualSystemDescriptionEntry*> vsdeCPU = vsdescThis->findByType(VirtualSystemDescriptionType_CPU);
2791 if (vsdeCPU.size() != 1)
2792 throw setError(VBOX_E_FILE_ERROR, tr("CPU count missing"));
2793
2794 stack.cCPUs = vsdeCPU.front()->strVboxCurrent.toUInt32();
2795 // We need HWVirt & IO-APIC if more than one CPU is requested
2796 if (stack.cCPUs > 1)
2797 {
2798 stack.fForceHWVirt = true;
2799 stack.fForceIOAPIC = true;
2800 }
2801
2802 // RAM
2803 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory);
2804 if (vsdeRAM.size() != 1)
2805 throw setError(VBOX_E_FILE_ERROR, tr("RAM size missing"));
2806 stack.ulMemorySizeMB = (ULONG)vsdeRAM.front()->strVboxCurrent.toUInt64();
2807
2808#ifdef VBOX_WITH_USB
2809 // USB controller
2810 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController = vsdescThis->findByType(VirtualSystemDescriptionType_USBController);
2811 // USB support is enabled if there's at least one such entry; to disable USB support,
2812 // the type of the USB item would have been changed to "ignore"
2813 stack.fUSBEnabled = vsdeUSBController.size() > 0;
2814#endif
2815 // audio adapter
2816 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard);
2817 /* @todo: we support one audio adapter only */
2818 if (vsdeAudioAdapter.size() > 0)
2819 stack.strAudioAdapter = vsdeAudioAdapter.front()->strVboxCurrent;
2820
2821 // for the description of the new machine, always use the OVF entry, the user may have changed it in the import config
2822 std::list<VirtualSystemDescriptionEntry*> vsdeDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description);
2823 if (vsdeDescription.size())
2824 stack.strDescription = vsdeDescription.front()->strVboxCurrent;
2825
2826 // import vbox:machine or OVF now
2827 if (vsdescThis->m->pConfig)
2828 // vbox:Machine config
2829 importVBoxMachine(vsdescThis, pNewMachine, stack, pCallbacks, pStorage);
2830 else
2831 // generic OVF config
2832 importMachineGeneric(vsysThis, vsdescThis, pNewMachine, stack, pCallbacks, pStorage);
2833
2834 } // for (it = pAppliance->m->llVirtualSystems.begin() ...
2835}
2836
Note: See TracBrowser for help on using the repository browser.

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