VirtualBox

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

Last change on this file since 79862 was 79606, checked in by vboxsync, 5 years ago

fixed the call of ICloudClient::importInstance() in the Appliance::i_importCloudImpl().

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

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