VirtualBox

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

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

Main: Working on reducing RTTar to a write-only interface. Not there yet, though.

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