VirtualBox

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

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

pr7072. Search an appropriate medium format using, firstly, URL given in the OVF description file and, secondly, using an image extension.

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

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