VirtualBox

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

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

bugref:9416. Fixed uninitialized variables.

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