VirtualBox

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

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

pr7091. Fixed: OVF import of VM with type "LsiLogicSas" of SCSI controller

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