VirtualBox

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

最後變更 在這個檔案從33779是 33699,由 vboxsync 提交於 14 年 前

Main-OVF: fix win burn

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

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