VirtualBox

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

Last change on this file since 47894 was 47716, checked in by vboxsync, 12 years ago

pr6022. 3rd variant (using VFS streaming feature) of GZIP support for reading the gzipped storage images from OVA/OVF package has been added.

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