VirtualBox

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

Last change on this file since 39694 was 39248, checked in by vboxsync, 13 years ago

Runtime: new guest OS type for Solaris 11
Frontends/VirtualBox: add new patterns for Solaris 11 guest OS type, reuse the icon
Frontends/VBoxManage: more details for "list ostypes"
Main/xml: make guest OS type in config file an arbitrary string (still validated/mapped in the old way in the settings code), remove hardcoded limit of 8 network adapters
Main/Global: move list of valid guest OS types into a single place, add function to get the network adapter limit for each chipset type
Main/Console+Machine+Snapshot+NetworkAdapter+Appliance+VirtualBox+Guest+SystemProperties: consistently use the appropriate network adapter limit so that ICH9 chipset can use 36 network adapters, adapt to cleaned up guest OS type handling, remove leftover rendundant guest OS mapping, whitespace
Network/NAT: release log message cosmetics, allow unlimited number of instances, fix maxconn clamping
Network/PCNet+VirtioNet+E1000: allow unlimited number of instances

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