VirtualBox

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

Last change on this file since 78227 was 78104, checked in by vboxsync, 6 years ago

IPRT,*: Added fFlags to RTPathAbsExDup so it matches the new RTPAthAbsEx. bugref:9172

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