VirtualBox

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

Last change on this file since 72200 was 72200, checked in by vboxsync, 7 years ago

Main/Appliance: very minimal start where later hooks for checking imported appliance should be placed

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