VirtualBox

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

Last change on this file since 73167 was 73132, checked in by vboxsync, 6 years ago

Main/Appliance: fix a regression caused by introducing the custom VM folder feature (which only happened because there was seriously misplaced code), and additionally move code for importing images to the right place, eliminating a lot of unreadable code duplication.

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

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