VirtualBox

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

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

pr7074. import logic for work with multiple VMs in OVF appliance was fixed in the function importMachineGeneric().

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