VirtualBox

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

最後變更 在這個檔案從99175是 98782,由 vboxsync 提交於 2 年 前

bugref:9782. The default values for VirtualSystemDescriptionType_CPU, VirtualSystemDescriptionType_Memory, VirtualSystemDescriptionType_SoundCard, VirtualSystemDescriptionType_Description are used now. Checked for VirtualSystemDescriptionType_SettingsFile in VirtualSystemDescription to get the path to the target machine.

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

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