VirtualBox

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

Last change on this file since 85192 was 84535, checked in by vboxsync, 5 years ago

Main/Appliance::i_importCloudImpl: %ul is wrong. Just use %RU64 and combine the two appends into a single call. Check status code. If you want to restore a previous hrc value, you must also restore the associated error info or we'll have some really confusing error messages. The first hrc restoring after cleanups looks wrong, as it's ignoring cleanup errors. bugref:9416

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