VirtualBox

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

Last change on this file since 91503 was 91503, checked in by vboxsync, 4 years ago

Main: bugref:1909: Added missed translation marks, removed redundant ones. Expanded one macro to make the lupdate get string correctly. Removed GuestBase::setErrorExternal and changed calls from it to setErrorBoth to handle translation correctly.

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

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