VirtualBox

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

Last change on this file since 97261 was 97261, checked in by vboxsync, 2 years ago

Changed the operator[] to function at() to avoid inserting new empty element in case if the requested key is absent in the map.

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

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