VirtualBox

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

Last change on this file since 46971 was 46971, checked in by vboxsync, 11 years ago

pr6022. Support handling directories in the TAR has been added. Added several useful checks during import OVA package.

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

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