VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/ApplianceImpl.cpp@ 72900

Last change on this file since 72900 was 72900, checked in by vboxsync, 7 years ago

ApplianceImpl.cpp: Replaced the supportedStandardsURI std::map by a more C-ish construct that doesn't leak memory nor introduces any race conditions during initialization and works correctly after an init failure.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 60.0 KB
Line 
1/* $Id: ApplianceImpl.cpp 72900 2018-07-04 19:42:18Z vboxsync $ */
2/** @file
3 * IAppliance and IVirtualSystem COM class implementations.
4 */
5
6/*
7 * Copyright (C) 2008-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include <iprt/path.h>
19#include <iprt/cpp/utils.h>
20#include <VBox/com/array.h>
21#include <map>
22
23#include "ApplianceImpl.h"
24#include "VFSExplorerImpl.h"
25#include "VirtualBoxImpl.h"
26#include "GuestOSTypeImpl.h"
27#include "Global.h"
28#include "ProgressImpl.h"
29#include "MachineImpl.h"
30#include "SystemPropertiesImpl.h"
31#include "AutoCaller.h"
32#include "Logging.h"
33#include "CertificateImpl.h"
34
35#include "ApplianceImplPrivate.h"
36
37using namespace std;
38
39
40/*********************************************************************************************************************************
41* Global Variables *
42*********************************************************************************************************************************/
43static const char * const g_pszISOURI = "http://www.ecma-international.org/publications/standards/Ecma-119.htm";
44static const char * const g_pszVMDKStreamURI = "http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized";
45static const char * const g_pszVMDKSparseURI = "http://www.vmware.com/specifications/vmdk.html#sparse";
46static const char * const g_pszVMDKCompressedURI = "http://www.vmware.com/specifications/vmdk.html#compressed";
47static const char * const g_pszVMDKCompressedURI2 = "http://www.vmware.com/interfaces/specifications/vmdk.html#compressed";
48static const char * const g_pszrVHDURI = "http://go.microsoft.com/fwlink/?LinkId=137171";
49static char g_szIsoBackend[128];
50static char g_szVmdkBackend[128];
51static char g_szVhdBackend[128];
52/** Set after the g_szXxxxBackend variables has been initialized. */
53static bool volatile g_fInitializedBackendNames = false;
54
55static struct
56{
57 const char *pszUri, *pszBackend;
58} const g_aUriToBackend[] =
59{
60 { g_pszISOURI, g_szIsoBackend },
61 { g_pszVMDKStreamURI, g_szVmdkBackend },
62 { g_pszVMDKSparseURI, g_szVmdkBackend },
63 { g_pszVMDKCompressedURI, g_szVmdkBackend },
64 { g_pszVMDKCompressedURI2, g_szVmdkBackend },
65 { g_pszrVHDURI, g_szVhdBackend },
66};
67
68static std::map<Utf8Str, Utf8Str> supportedStandardsURI;
69
70static struct
71{
72 ovf::CIMOSType_T cim;
73 VBOXOSTYPE osType;
74} const g_aOsTypes[] =
75{
76 { ovf::CIMOSType_CIMOS_Unknown, VBOXOSTYPE_Unknown },
77 { ovf::CIMOSType_CIMOS_OS2, VBOXOSTYPE_OS2 },
78 { ovf::CIMOSType_CIMOS_OS2, VBOXOSTYPE_OS2Warp3 },
79 { ovf::CIMOSType_CIMOS_OS2, VBOXOSTYPE_OS2Warp4 },
80 { ovf::CIMOSType_CIMOS_OS2, VBOXOSTYPE_OS2Warp45 },
81 { ovf::CIMOSType_CIMOS_MSDOS, VBOXOSTYPE_DOS },
82 { ovf::CIMOSType_CIMOS_WIN3x, VBOXOSTYPE_Win31 },
83 { ovf::CIMOSType_CIMOS_WIN95, VBOXOSTYPE_Win95 },
84 { ovf::CIMOSType_CIMOS_WIN98, VBOXOSTYPE_Win98 },
85 { ovf::CIMOSType_CIMOS_WINNT, VBOXOSTYPE_WinNT },
86 { ovf::CIMOSType_CIMOS_WINNT, VBOXOSTYPE_WinNT4 },
87 { ovf::CIMOSType_CIMOS_WINNT, VBOXOSTYPE_WinNT3x },
88 { ovf::CIMOSType_CIMOS_NetWare, VBOXOSTYPE_Netware },
89 { ovf::CIMOSType_CIMOS_NovellOES, VBOXOSTYPE_Netware },
90 { ovf::CIMOSType_CIMOS_Solaris, VBOXOSTYPE_Solaris },
91 { ovf::CIMOSType_CIMOS_SunOS, VBOXOSTYPE_Solaris },
92 { ovf::CIMOSType_CIMOS_FreeBSD, VBOXOSTYPE_FreeBSD },
93 { ovf::CIMOSType_CIMOS_NetBSD, VBOXOSTYPE_NetBSD },
94 { ovf::CIMOSType_CIMOS_QNX, VBOXOSTYPE_QNX },
95 { ovf::CIMOSType_CIMOS_Windows2000, VBOXOSTYPE_Win2k },
96 { ovf::CIMOSType_CIMOS_WindowsMe, VBOXOSTYPE_WinMe },
97 { ovf::CIMOSType_CIMOS_OpenBSD, VBOXOSTYPE_OpenBSD },
98 { ovf::CIMOSType_CIMOS_WindowsXP, VBOXOSTYPE_WinXP },
99 { ovf::CIMOSType_CIMOS_WindowsXPEmbedded, VBOXOSTYPE_WinXP },
100 { ovf::CIMOSType_CIMOS_WindowsEmbeddedforPointofService, VBOXOSTYPE_WinXP },
101 { ovf::CIMOSType_CIMOS_MicrosoftWindowsServer2003, VBOXOSTYPE_Win2k3 },
102 { ovf::CIMOSType_CIMOS_MicrosoftWindowsServer2003_64, VBOXOSTYPE_Win2k3_x64 },
103 { ovf::CIMOSType_CIMOS_WindowsXP_64, VBOXOSTYPE_WinXP_x64 },
104 { ovf::CIMOSType_CIMOS_WindowsVista, VBOXOSTYPE_WinVista },
105 { ovf::CIMOSType_CIMOS_WindowsVista_64, VBOXOSTYPE_WinVista_x64 },
106 { ovf::CIMOSType_CIMOS_MicrosoftWindowsServer2008, VBOXOSTYPE_Win2k8 },
107 { ovf::CIMOSType_CIMOS_MicrosoftWindowsServer2008_64, VBOXOSTYPE_Win2k8_x64 },
108 { ovf::CIMOSType_CIMOS_FreeBSD_64, VBOXOSTYPE_FreeBSD_x64 },
109 { ovf::CIMOSType_CIMOS_MACOS, VBOXOSTYPE_MacOS },
110 { ovf::CIMOSType_CIMOS_MACOS, VBOXOSTYPE_MacOS_x64 }, // there is no CIM 64-bit type for this
111 { ovf::CIMOSType_CIMOS_MACOS, VBOXOSTYPE_MacOS106 },
112 { ovf::CIMOSType_CIMOS_MACOS, VBOXOSTYPE_MacOS106_x64 },
113 { ovf::CIMOSType_CIMOS_MACOS, VBOXOSTYPE_MacOS107_x64 },
114 { ovf::CIMOSType_CIMOS_MACOS, VBOXOSTYPE_MacOS108_x64 },
115 { ovf::CIMOSType_CIMOS_MACOS, VBOXOSTYPE_MacOS109_x64 },
116 { ovf::CIMOSType_CIMOS_MACOS, VBOXOSTYPE_MacOS1010_x64 },
117 { ovf::CIMOSType_CIMOS_MACOS, VBOXOSTYPE_MacOS1011_x64 },
118 { ovf::CIMOSType_CIMOS_MACOS, VBOXOSTYPE_MacOS1012_x64 },
119 { ovf::CIMOSType_CIMOS_MACOS, VBOXOSTYPE_MacOS1013_x64 },
120
121 // Linuxes
122 { ovf::CIMOSType_CIMOS_RedHatEnterpriseLinux, VBOXOSTYPE_RedHat },
123 { ovf::CIMOSType_CIMOS_RedHatEnterpriseLinux_64, VBOXOSTYPE_RedHat_x64 },
124 { ovf::CIMOSType_CIMOS_Solaris_64, VBOXOSTYPE_Solaris_x64 },
125 { ovf::CIMOSType_CIMOS_SUSE, VBOXOSTYPE_OpenSUSE },
126 { ovf::CIMOSType_CIMOS_SLES, VBOXOSTYPE_OpenSUSE },
127 { ovf::CIMOSType_CIMOS_NovellLinuxDesktop, VBOXOSTYPE_OpenSUSE },
128 { ovf::CIMOSType_CIMOS_SUSE_64, VBOXOSTYPE_OpenSUSE_x64 },
129 { ovf::CIMOSType_CIMOS_SLES_64, VBOXOSTYPE_OpenSUSE_x64 },
130 { ovf::CIMOSType_CIMOS_LINUX, VBOXOSTYPE_Linux },
131 { ovf::CIMOSType_CIMOS_LINUX, VBOXOSTYPE_Linux22 },
132 { ovf::CIMOSType_CIMOS_SunJavaDesktopSystem, VBOXOSTYPE_Linux },
133 { ovf::CIMOSType_CIMOS_TurboLinux, VBOXOSTYPE_Turbolinux },
134 { ovf::CIMOSType_CIMOS_TurboLinux_64, VBOXOSTYPE_Turbolinux_x64 },
135 { ovf::CIMOSType_CIMOS_Mandriva, VBOXOSTYPE_Mandriva },
136 { ovf::CIMOSType_CIMOS_Mandriva_64, VBOXOSTYPE_Mandriva_x64 },
137 { ovf::CIMOSType_CIMOS_Ubuntu, VBOXOSTYPE_Ubuntu },
138 { ovf::CIMOSType_CIMOS_Ubuntu_64, VBOXOSTYPE_Ubuntu_x64 },
139 { ovf::CIMOSType_CIMOS_Debian, VBOXOSTYPE_Debian },
140 { ovf::CIMOSType_CIMOS_Debian_64, VBOXOSTYPE_Debian_x64 },
141 { ovf::CIMOSType_CIMOS_Linux_2_4_x, VBOXOSTYPE_Linux24 },
142 { ovf::CIMOSType_CIMOS_Linux_2_4_x_64, VBOXOSTYPE_Linux24_x64 },
143 { ovf::CIMOSType_CIMOS_Linux_2_6_x, VBOXOSTYPE_Linux26 },
144 { ovf::CIMOSType_CIMOS_Linux_2_6_x_64, VBOXOSTYPE_Linux26_x64 },
145 { ovf::CIMOSType_CIMOS_Linux_64, VBOXOSTYPE_Linux26_x64 },
146
147 // types that we have support for but CIM doesn't
148 { ovf::CIMOSType_CIMOS_Linux_2_6_x, VBOXOSTYPE_ArchLinux },
149 { ovf::CIMOSType_CIMOS_Linux_2_6_x_64, VBOXOSTYPE_ArchLinux_x64 },
150 { ovf::CIMOSType_CIMOS_Linux_2_6_x, VBOXOSTYPE_FedoraCore },
151 { ovf::CIMOSType_CIMOS_Linux_2_6_x_64, VBOXOSTYPE_FedoraCore_x64 },
152 { ovf::CIMOSType_CIMOS_Linux_2_6_x, VBOXOSTYPE_Gentoo },
153 { ovf::CIMOSType_CIMOS_Linux_2_6_x_64, VBOXOSTYPE_Gentoo_x64 },
154 { ovf::CIMOSType_CIMOS_Linux_2_6_x, VBOXOSTYPE_Xandros },
155 { ovf::CIMOSType_CIMOS_Linux_2_6_x_64, VBOXOSTYPE_Xandros_x64 },
156 { ovf::CIMOSType_CIMOS_Solaris, VBOXOSTYPE_OpenSolaris },
157 { ovf::CIMOSType_CIMOS_Solaris_64, VBOXOSTYPE_OpenSolaris_x64 },
158
159 // types added with CIM 2.25.0 follow:
160 { ovf::CIMOSType_CIMOS_WindowsServer2008R2, VBOXOSTYPE_Win2k8 }, // duplicate, see above
161// { ovf::CIMOSType_CIMOS_VMwareESXi = 104, // we can't run ESX in a VM
162 { ovf::CIMOSType_CIMOS_Windows7, VBOXOSTYPE_Win7 },
163 { ovf::CIMOSType_CIMOS_Windows7, VBOXOSTYPE_Win7_x64 }, // there is no
164 // CIM 64-bit type for this
165 { ovf::CIMOSType_CIMOS_CentOS, VBOXOSTYPE_RedHat },
166 { ovf::CIMOSType_CIMOS_CentOS_64, VBOXOSTYPE_RedHat_x64 },
167 { ovf::CIMOSType_CIMOS_OracleEnterpriseLinux, VBOXOSTYPE_Oracle },
168 { ovf::CIMOSType_CIMOS_OracleEnterpriseLinux_64, VBOXOSTYPE_Oracle_x64 },
169 { ovf::CIMOSType_CIMOS_eComStation, VBOXOSTYPE_ECS }
170
171 // there are no CIM types for these, so these turn to "other" on export:
172 // VBOXOSTYPE_OpenBSD
173 // VBOXOSTYPE_OpenBSD_x64
174 // VBOXOSTYPE_NetBSD
175 // VBOXOSTYPE_NetBSD_x64
176
177};
178
179/* Pattern structure for matching the OS type description field */
180struct osTypePattern
181{
182 const char *pcszPattern;
183 VBOXOSTYPE osType;
184};
185
186/* These are the 32-Bit ones. They are sorted by priority. */
187static const osTypePattern g_aOsTypesPattern[] =
188{
189 {"Windows NT", VBOXOSTYPE_WinNT4},
190 {"Windows XP", VBOXOSTYPE_WinXP},
191 {"Windows 2000", VBOXOSTYPE_Win2k},
192 {"Windows 2003", VBOXOSTYPE_Win2k3},
193 {"Windows Vista", VBOXOSTYPE_WinVista},
194 {"Windows 2008", VBOXOSTYPE_Win2k8},
195 {"SUSE", VBOXOSTYPE_OpenSUSE},
196 {"Novell", VBOXOSTYPE_OpenSUSE},
197 {"Red Hat", VBOXOSTYPE_RedHat},
198 {"Mandriva", VBOXOSTYPE_Mandriva},
199 {"Ubuntu", VBOXOSTYPE_Ubuntu},
200 {"Debian", VBOXOSTYPE_Debian},
201 {"QNX", VBOXOSTYPE_QNX},
202 {"Linux 2.4", VBOXOSTYPE_Linux24},
203 {"Linux 2.6", VBOXOSTYPE_Linux26},
204 {"Linux", VBOXOSTYPE_Linux},
205 {"OpenSolaris", VBOXOSTYPE_OpenSolaris},
206 {"Solaris", VBOXOSTYPE_OpenSolaris},
207 {"FreeBSD", VBOXOSTYPE_FreeBSD},
208 {"NetBSD", VBOXOSTYPE_NetBSD},
209 {"Windows 95", VBOXOSTYPE_Win95},
210 {"Windows 98", VBOXOSTYPE_Win98},
211 {"Windows Me", VBOXOSTYPE_WinMe},
212 {"Windows 3.", VBOXOSTYPE_Win31},
213 {"DOS", VBOXOSTYPE_DOS},
214 {"OS2", VBOXOSTYPE_OS2}
215};
216
217/* These are the 64-Bit ones. They are sorted by priority. */
218static const osTypePattern g_aOsTypesPattern64[] =
219{
220 {"Windows XP", VBOXOSTYPE_WinXP_x64},
221 {"Windows 2003", VBOXOSTYPE_Win2k3_x64},
222 {"Windows Vista", VBOXOSTYPE_WinVista_x64},
223 {"Windows 2008", VBOXOSTYPE_Win2k8_x64},
224 {"SUSE", VBOXOSTYPE_OpenSUSE_x64},
225 {"Novell", VBOXOSTYPE_OpenSUSE_x64},
226 {"Red Hat", VBOXOSTYPE_RedHat_x64},
227 {"Mandriva", VBOXOSTYPE_Mandriva_x64},
228 {"Ubuntu", VBOXOSTYPE_Ubuntu_x64},
229 {"Debian", VBOXOSTYPE_Debian_x64},
230 {"Linux 2.4", VBOXOSTYPE_Linux24_x64},
231 {"Linux 2.6", VBOXOSTYPE_Linux26_x64},
232 {"Linux", VBOXOSTYPE_Linux26_x64},
233 {"OpenSolaris", VBOXOSTYPE_OpenSolaris_x64},
234 {"Solaris", VBOXOSTYPE_OpenSolaris_x64},
235 {"FreeBSD", VBOXOSTYPE_FreeBSD_x64},
236};
237
238/**
239 * Private helper func that suggests a VirtualBox guest OS type
240 * for the given OVF operating system type.
241 * @param strType
242 * @param c
243 * @param cStr
244 */
245void convertCIMOSType2VBoxOSType(Utf8Str &strType, ovf::CIMOSType_T c, const Utf8Str &cStr)
246{
247 /* First check if the type is other/other_64 */
248 if (c == ovf::CIMOSType_CIMOS_Other)
249 {
250 for (size_t i=0; i < RT_ELEMENTS(g_aOsTypesPattern); ++i)
251 if (cStr.contains (g_aOsTypesPattern[i].pcszPattern, Utf8Str::CaseInsensitive))
252 {
253 strType = Global::OSTypeId(g_aOsTypesPattern[i].osType);
254 return;
255 }
256 }
257 else if (c == ovf::CIMOSType_CIMOS_Other_64)
258 {
259 for (size_t i=0; i < RT_ELEMENTS(g_aOsTypesPattern64); ++i)
260 if (cStr.contains (g_aOsTypesPattern64[i].pcszPattern, Utf8Str::CaseInsensitive))
261 {
262 strType = Global::OSTypeId(g_aOsTypesPattern64[i].osType);
263 return;
264 }
265 }
266
267 for (size_t i = 0; i < RT_ELEMENTS(g_aOsTypes); ++i)
268 {
269 if (c == g_aOsTypes[i].cim)
270 {
271 strType = Global::OSTypeId(g_aOsTypes[i].osType);
272 return;
273 }
274 }
275
276 if (c == ovf::CIMOSType_CIMOS_Other_64)
277 strType = Global::OSTypeId(VBOXOSTYPE_Unknown_x64);
278 else
279 strType = Global::OSTypeId(VBOXOSTYPE_Unknown);
280}
281
282/**
283 * Private helper func that suggests a VirtualBox guest OS type
284 * for the given OVF operating system type.
285 * @returns CIM OS type.
286 * @param pcszVBox Our guest OS type identifier string.
287 * @param fLongMode Whether long mode is enabled and a 64-bit CIM type is
288 * preferred even if the VBox guest type isn't 64-bit.
289 */
290ovf::CIMOSType_T convertVBoxOSType2CIMOSType(const char *pcszVBox, BOOL fLongMode)
291{
292 for (size_t i = 0; i < RT_ELEMENTS(g_aOsTypes); ++i)
293 {
294 if (!RTStrICmp(pcszVBox, Global::OSTypeId(g_aOsTypes[i].osType)))
295 {
296 if (fLongMode && !(g_aOsTypes[i].osType & VBOXOSTYPE_x64))
297 {
298 VBOXOSTYPE enmDesiredOsType = (VBOXOSTYPE)((int)g_aOsTypes[i].osType | (int)VBOXOSTYPE_x64);
299 size_t j = i;
300 while (++j < RT_ELEMENTS(g_aOsTypes))
301 if (g_aOsTypes[j].osType == enmDesiredOsType)
302 return g_aOsTypes[j].cim;
303 j = i;
304 while (--j > 0)
305 if (g_aOsTypes[j].osType == enmDesiredOsType)
306 return g_aOsTypes[j].cim;
307 /* Not all OSes have 64-bit versions, so just return the 32-bit variant. */
308 }
309 return g_aOsTypes[i].cim;
310 }
311 }
312
313 return fLongMode ? ovf::CIMOSType_CIMOS_Other_64 : ovf::CIMOSType_CIMOS_Other;
314}
315
316Utf8Str convertNetworkAttachmentTypeToString(NetworkAttachmentType_T type)
317{
318 Utf8Str strType;
319 switch (type)
320 {
321 case NetworkAttachmentType_NAT: strType = "NAT"; break;
322 case NetworkAttachmentType_Bridged: strType = "Bridged"; break;
323 case NetworkAttachmentType_Internal: strType = "Internal"; break;
324 case NetworkAttachmentType_HostOnly: strType = "HostOnly"; break;
325 case NetworkAttachmentType_Generic: strType = "Generic"; break;
326 case NetworkAttachmentType_NATNetwork: strType = "NATNetwork"; break;
327 case NetworkAttachmentType_Null: strType = "Null"; break;
328 }
329 return strType;
330}
331
332
333////////////////////////////////////////////////////////////////////////////////
334//
335// Appliance constructor / destructor
336//
337// ////////////////////////////////////////////////////////////////////////////////
338
339DEFINE_EMPTY_CTOR_DTOR(VirtualSystemDescription)
340
341HRESULT VirtualSystemDescription::FinalConstruct()
342{
343 return BaseFinalConstruct();
344}
345
346void VirtualSystemDescription::FinalRelease()
347{
348 uninit();
349
350 BaseFinalRelease();
351}
352
353Appliance::Appliance()
354 : mVirtualBox(NULL)
355{
356}
357
358Appliance::~Appliance()
359{
360}
361
362
363HRESULT Appliance::FinalConstruct()
364{
365 return BaseFinalConstruct();
366}
367
368void Appliance::FinalRelease()
369{
370 uninit();
371
372 BaseFinalRelease();
373}
374
375
376////////////////////////////////////////////////////////////////////////////////
377//
378// Internal helpers
379//
380////////////////////////////////////////////////////////////////////////////////
381
382
383////////////////////////////////////////////////////////////////////////////////
384//
385// IVirtualBox public methods
386//
387////////////////////////////////////////////////////////////////////////////////
388
389// This code is here so we won't have to include the appliance headers in the
390// IVirtualBox implementation.
391
392/**
393 * Implementation for IVirtualBox::createAppliance.
394 *
395 * @param aAppliance IAppliance object created if S_OK is returned.
396 * @return S_OK or error.
397 */
398HRESULT VirtualBox::createAppliance(ComPtr<IAppliance> &aAppliance)
399{
400 ComObjPtr<Appliance> appliance;
401 HRESULT hrc = appliance.createObject();
402 if (SUCCEEDED(hrc))
403 {
404 hrc = appliance->init(this);
405 if (SUCCEEDED(hrc))
406 hrc = appliance.queryInterfaceTo(aAppliance.asOutParam());
407 }
408 return hrc;
409}
410
411/**
412 * Appliance COM initializer.
413 * @param aVirtualBox The VirtualBox object.
414 */
415HRESULT Appliance::init(VirtualBox *aVirtualBox)
416{
417 HRESULT rc = S_OK;
418 /* Enclose the state transition NotReady->InInit->Ready */
419 AutoInitSpan autoInitSpan(this);
420 AssertReturn(autoInitSpan.isOk(), E_FAIL);
421
422 /* Weak reference to a VirtualBox object */
423 unconst(mVirtualBox) = aVirtualBox;
424
425 // initialize data
426 m = new Data;
427 m->m_pSecretKeyStore = new SecretKeyStore(false /* fRequireNonPageable*/);
428 AssertReturn(m->m_pSecretKeyStore, E_FAIL);
429
430 rc = i_initBackendNames();
431
432 /* Confirm a successful initialization */
433 autoInitSpan.setSucceeded();
434
435 return rc;
436}
437
438/**
439 * Appliance COM uninitializer.
440 */
441void Appliance::uninit()
442{
443 /* Enclose the state transition Ready->InUninit->NotReady */
444 AutoUninitSpan autoUninitSpan(this);
445 if (autoUninitSpan.uninitDone())
446 return;
447
448 if (m->m_pSecretKeyStore)
449 delete m->m_pSecretKeyStore;
450
451 delete m;
452 m = NULL;
453}
454
455////////////////////////////////////////////////////////////////////////////////
456//
457// IAppliance public methods
458//
459////////////////////////////////////////////////////////////////////////////////
460
461/**
462 * Public method implementation.
463 */
464HRESULT Appliance::getPath(com::Utf8Str &aPath)
465{
466 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
467
468 if (!i_isApplianceIdle())
469 return E_ACCESSDENIED;
470
471 aPath = m->locInfo.strPath;
472
473 return S_OK;
474}
475
476/**
477 * Public method implementation.
478 */
479HRESULT Appliance::getDisks(std::vector<com::Utf8Str> &aDisks)
480{
481 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
482
483 aDisks.resize(0);
484 if (!i_isApplianceIdle())
485 return E_ACCESSDENIED;
486
487 if (m->pReader) // OVFReader instantiated?
488 {
489 aDisks.resize(m->pReader->m_mapDisks.size());
490
491 ovf::DiskImagesMap::const_iterator it;
492 size_t i = 0;
493 for (it = m->pReader->m_mapDisks.begin();
494 it != m->pReader->m_mapDisks.end();
495 ++it, ++i)
496 {
497 // create a string representing this disk
498 const ovf::DiskImage &d = it->second;
499 char *psz = NULL;
500 RTStrAPrintf(&psz,
501 "%s\t"
502 "%RI64\t"
503 "%RI64\t"
504 "%s\t"
505 "%s\t"
506 "%RI64\t"
507 "%RI64\t"
508 "%s",
509 d.strDiskId.c_str(),
510 d.iCapacity,
511 d.iPopulatedSize,
512 d.strFormat.c_str(),
513 d.strHref.c_str(),
514 d.iSize,
515 d.iChunkSize,
516 d.strCompression.c_str());
517 Utf8Str utf(psz);
518 aDisks[i] = utf;
519 RTStrFree(psz);
520 }
521 }
522
523 return S_OK;
524}
525
526/**
527 * Public method implementation.
528 */
529HRESULT Appliance::getCertificate(ComPtr<ICertificate> &aCertificateInfo)
530{
531 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
532
533 if (!i_isApplianceIdle())
534 return E_ACCESSDENIED;
535
536 /* Can be NULL at this point, queryInterfaceto handles that. */
537 m->ptrCertificateInfo.queryInterfaceTo(aCertificateInfo.asOutParam());
538 return S_OK;
539}
540
541/**
542 * Public method implementation.
543 */
544HRESULT Appliance::getVirtualSystemDescriptions(std::vector<ComPtr<IVirtualSystemDescription> > &aVirtualSystemDescriptions)
545{
546 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
547
548 if (!i_isApplianceIdle())
549 return E_ACCESSDENIED;
550
551 aVirtualSystemDescriptions.resize(m->virtualSystemDescriptions.size());
552 std::list< ComObjPtr<VirtualSystemDescription> > vsds(m->virtualSystemDescriptions);
553 size_t i = 0;
554 for (std::list< ComObjPtr<VirtualSystemDescription> >::iterator it = vsds.begin(); it != vsds.end(); ++it, ++i)
555 {
556 (*it).queryInterfaceTo(aVirtualSystemDescriptions[i].asOutParam());
557 }
558 return S_OK;
559}
560
561/**
562 * Public method implementation.
563 */
564HRESULT Appliance::getMachines(std::vector<com::Utf8Str> &aMachines)
565{
566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
567
568 if (!i_isApplianceIdle())
569 return E_ACCESSDENIED;
570
571 aMachines.resize(m->llGuidsMachinesCreated.size());
572 size_t i = 0;
573 for (std::list<Guid>::const_iterator it = m->llGuidsMachinesCreated.begin();
574 it != m->llGuidsMachinesCreated.end();
575 ++it, ++i)
576 {
577 const Guid &uuid = *it;
578 aMachines[i] = uuid.toUtf16();
579 }
580 return S_OK;
581}
582
583HRESULT Appliance::createVFSExplorer(const com::Utf8Str &aURI, ComPtr<IVFSExplorer> &aExplorer)
584{
585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
586
587 ComObjPtr<VFSExplorer> explorer;
588 HRESULT rc = S_OK;
589 try
590 {
591 Utf8Str uri(aURI);
592 /* Check which kind of export the user has requested */
593 LocationInfo li;
594 i_parseURI(aURI, li);
595 /* Create the explorer object */
596 explorer.createObject();
597 rc = explorer->init(li.storageType, li.strPath, li.strHostname, li.strUsername, li.strPassword, mVirtualBox);
598 }
599 catch (HRESULT aRC)
600 {
601 rc = aRC;
602 }
603
604 if (SUCCEEDED(rc))
605 /* Return explorer to the caller */
606 explorer.queryInterfaceTo(aExplorer.asOutParam());
607
608 return rc;
609}
610
611/**
612 * Public method implementation.
613 */
614HRESULT Appliance::getWarnings(std::vector<com::Utf8Str> &aWarnings)
615{
616 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
617
618 aWarnings.resize(m->llWarnings.size());
619
620 list<Utf8Str>::const_iterator it;
621 size_t i = 0;
622 for (it = m->llWarnings.begin();
623 it != m->llWarnings.end();
624 ++it, ++i)
625 {
626 aWarnings[i] = *it;
627 }
628
629 return S_OK;
630}
631
632HRESULT Appliance::getPasswordIds(std::vector<com::Utf8Str> &aIdentifiers)
633{
634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
635
636 aIdentifiers = m->m_vecPasswordIdentifiers;
637 return S_OK;
638}
639
640HRESULT Appliance::getMediumIdsForPasswordId(const com::Utf8Str &aPasswordId, std::vector<com::Guid> &aIdentifiers)
641{
642 HRESULT hrc = S_OK;
643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
644
645 std::map<com::Utf8Str, GUIDVEC>::const_iterator it = m->m_mapPwIdToMediumIds.find(aPasswordId);
646 if (it != m->m_mapPwIdToMediumIds.end())
647 aIdentifiers = it->second;
648 else
649 hrc = setError(E_FAIL, tr("The given password identifier is not associated with any medium"));
650
651 return hrc;
652}
653
654HRESULT Appliance::addPasswords(const std::vector<com::Utf8Str> &aIdentifiers,
655 const std::vector<com::Utf8Str> &aPasswords)
656{
657 HRESULT hrc = S_OK;
658
659 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
660
661 /* Check that the IDs do not exist already before changing anything. */
662 for (unsigned i = 0; i < aIdentifiers.size(); i++)
663 {
664 SecretKey *pKey = NULL;
665 int rc = m->m_pSecretKeyStore->retainSecretKey(aIdentifiers[i], &pKey);
666 if (rc != VERR_NOT_FOUND)
667 {
668 AssertPtr(pKey);
669 if (pKey)
670 pKey->release();
671 return setError(VBOX_E_OBJECT_IN_USE, tr("A password with the given ID already exists"));
672 }
673 }
674
675 for (unsigned i = 0; i < aIdentifiers.size() && SUCCEEDED(hrc); i++)
676 {
677 size_t cbKey = aPasswords[i].length() + 1; /* Include terminator */
678 const uint8_t *pbKey = (const uint8_t *)aPasswords[i].c_str();
679
680 int rc = m->m_pSecretKeyStore->addSecretKey(aIdentifiers[i], pbKey, cbKey);
681 if (RT_SUCCESS(rc))
682 m->m_cPwProvided++;
683 else if (rc == VERR_NO_MEMORY)
684 hrc = setError(E_OUTOFMEMORY, tr("Failed to allocate enough secure memory for the key"));
685 else
686 hrc = setError(E_FAIL, tr("Unknown error happened while adding a password (%Rrc)"), rc);
687 }
688
689 return hrc;
690}
691
692////////////////////////////////////////////////////////////////////////////////
693//
694// Appliance private methods
695//
696////////////////////////////////////////////////////////////////////////////////
697
698HRESULT Appliance::i_initBackendNames()
699{
700 HRESULT hrc = S_OK;
701 if (!g_fInitializedBackendNames)
702 {
703 /*
704 * Use the system properties to translate file extensions into
705 * storage backend names.
706 */
707 static struct
708 {
709 const char *pszExt; /**< extension */
710 char *pszBackendName;
711 size_t cbBackendName;
712 } const s_aFormats[] =
713 {
714 { "iso", g_szIsoBackend, sizeof(g_szIsoBackend) },
715 { "vmdk", g_szVmdkBackend, sizeof(g_szVmdkBackend) },
716 { "vhd", g_szVhdBackend, sizeof(g_szVhdBackend) },
717 };
718 SystemProperties *pSysProps = mVirtualBox->i_getSystemProperties();
719 for (unsigned i = 0; i < RT_ELEMENTS(s_aFormats); i++)
720 {
721 ComObjPtr<MediumFormat> trgFormat = pSysProps->i_mediumFormatFromExtension(s_aFormats[i].pszExt);
722 if (trgFormat.isNotNull())
723 {
724 const char *pszName = trgFormat->i_getName().c_str();
725 int vrc = RTStrCopy(s_aFormats[i].pszBackendName, s_aFormats[i].cbBackendName, pszName);
726 AssertRCStmt(vrc, hrc = setError(E_FAIL, "Unexpected storage backend name copy error %Rrc for %s.", vrc, pszName));
727 }
728 else
729 hrc = setError(E_FAIL, tr("Can't find appropriate medium format for ISO type of a virtual disk."));
730 }
731
732 if (SUCCEEDED(hrc))
733 g_fInitializedBackendNames = true;
734 }
735
736 return hrc;
737}
738
739Utf8Str Appliance::i_typeOfVirtualDiskFormatFromURI(Utf8Str uri) const
740{
741 Assert(g_fInitializedBackendNames);
742
743 unsigned i = RT_ELEMENTS(g_aUriToBackend);
744 while (i-- > 0)
745 if (RTStrICmp(g_aUriToBackend[i].pszUri, uri.c_str()) == 0)
746 return Utf8Str(g_aUriToBackend[i].pszBackend);
747 return Utf8Str();
748}
749
750std::set<Utf8Str> Appliance::i_URIFromTypeOfVirtualDiskFormat(Utf8Str type)
751{
752 Assert(g_fInitializedBackendNames);
753
754 std::set<Utf8Str> UriSet;
755 unsigned i = RT_ELEMENTS(g_aUriToBackend);
756 while (i-- > 0)
757 if (RTStrICmp(g_aUriToBackend[i].pszBackend, type.c_str()) == 0)
758 UriSet.insert(g_aUriToBackend[i].pszUri);
759 return UriSet;
760}
761
762/**
763 * Returns a medium format object corresponding to the given
764 * disk image or null if no such format.
765 *
766 * @param di Disk Image
767 * @param mf Medium Format
768 *
769 * @return ComObjPtr<MediumFormat>
770 */
771HRESULT Appliance::i_findMediumFormatFromDiskImage(const ovf::DiskImage &di, ComObjPtr<MediumFormat>& mf)
772{
773 HRESULT rc = S_OK;
774
775 /* Get the system properties. */
776 SystemProperties *pSysProps = mVirtualBox->i_getSystemProperties();
777
778 /* We need a proper source format description */
779 /* Which format to use? */
780 Utf8Str strSrcFormat = i_typeOfVirtualDiskFormatFromURI(di.strFormat);
781
782 /*
783 * fallback, if we can't determine virtual disk format using URI from the attribute ovf:format
784 * in the corresponding section <Disk> in the OVF file.
785 */
786 if (strSrcFormat.isEmpty())
787 {
788 strSrcFormat = di.strHref;
789
790 /* check either file gzipped or not
791 * if "yes" then remove last extension,
792 * i.e. "image.vmdk.gz"->"image.vmdk"
793 */
794 if (di.strCompression == "gzip")
795 {
796 if (RTPathHasSuffix(strSrcFormat.c_str()))
797 {
798 strSrcFormat.stripSuffix();
799 }
800 else
801 {
802 mf.setNull();
803 rc = setError(E_FAIL,
804 tr("Internal inconsistency looking up medium format for the disk image '%s'"),
805 di.strHref.c_str());
806 return rc;
807 }
808 }
809 /* Figure out from extension which format the image of disk has. */
810 if (RTPathHasSuffix(strSrcFormat.c_str()))
811 {
812 const char *pszExt = RTPathSuffix(strSrcFormat.c_str());
813 if (pszExt)
814 pszExt++;
815 mf = pSysProps->i_mediumFormatFromExtension(pszExt);
816 }
817 else
818 mf.setNull();
819 }
820 else
821 mf = pSysProps->i_mediumFormat(strSrcFormat);
822
823 if (mf.isNull())
824 rc = setError(E_FAIL, tr("Internal inconsistency looking up medium format for the disk image '%s'"),
825 di.strHref.c_str());
826
827 return rc;
828}
829
830/**
831 * Setup automatic I/O stream digest calculation, adding it to hOurManifest.
832 *
833 * @returns Passthru I/O stream, of @a hVfsIos if no digest calc needed.
834 * @param hVfsIos The stream to wrap. Always consumed.
835 * @param pszManifestEntry The manifest entry.
836 * @param fRead Set if read stream, clear if write.
837 * @throws Nothing.
838 */
839RTVFSIOSTREAM Appliance::i_manifestSetupDigestCalculationForGivenIoStream(RTVFSIOSTREAM hVfsIos, const char *pszManifestEntry,
840 bool fRead /*= true */)
841{
842 int vrc;
843 Assert(!RTManifestPtIosIsInstanceOf(hVfsIos));
844
845 if (m->fDigestTypes == 0)
846 return hVfsIos;
847
848 /* Create the manifest if necessary. */
849 if (m->hOurManifest == NIL_RTMANIFEST)
850 {
851 vrc = RTManifestCreate(0 /*fFlags*/, &m->hOurManifest);
852 AssertRCReturnStmt(vrc, RTVfsIoStrmRelease(hVfsIos), NIL_RTVFSIOSTREAM);
853 }
854
855 /* Setup the stream. */
856 RTVFSIOSTREAM hVfsIosPt;
857 vrc = RTManifestEntryAddPassthruIoStream(m->hOurManifest, hVfsIos, pszManifestEntry, m->fDigestTypes, fRead, &hVfsIosPt);
858
859 RTVfsIoStrmRelease(hVfsIos); /* always consumed! */
860 if (RT_SUCCESS(vrc))
861 return hVfsIosPt;
862
863 setErrorVrc(vrc, "RTManifestEntryAddPassthruIoStream failed with rc=%Rrc", vrc);
864 return NIL_RTVFSIOSTREAM;
865}
866
867/**
868 * Returns true if the appliance is in "idle" state. This should always be the
869 * case unless an import or export is currently in progress. Similar to machine
870 * states, this permits the Appliance implementation code to let go of the
871 * Appliance object lock while a time-consuming disk conversion is in progress
872 * without exposing the appliance to conflicting calls.
873 *
874 * This sets an error on "this" (the appliance) and returns false if the appliance
875 * is busy. The caller should then return E_ACCESSDENIED.
876 *
877 * Must be called from under the object lock!
878 */
879bool Appliance::i_isApplianceIdle()
880{
881 if (m->state == Data::ApplianceImporting)
882 setError(VBOX_E_INVALID_OBJECT_STATE, tr("The appliance is busy importing files"));
883 else if (m->state == Data::ApplianceExporting)
884 setError(VBOX_E_INVALID_OBJECT_STATE, tr("The appliance is busy exporting files"));
885 else
886 return true;
887
888 return false;
889}
890
891HRESULT Appliance::i_searchUniqueVMName(Utf8Str& aName) const
892{
893 IMachine *machine = NULL;
894 char *tmpName = RTStrDup(aName.c_str());
895 int i = 1;
896 /** @todo Maybe too cost-intensive; try to find a lighter way */
897 while (mVirtualBox->FindMachine(Bstr(tmpName).raw(), &machine) != VBOX_E_OBJECT_NOT_FOUND)
898 {
899 RTStrFree(tmpName);
900 RTStrAPrintf(&tmpName, "%s %d", aName.c_str(), i);
901 ++i;
902 }
903 aName = tmpName;
904 RTStrFree(tmpName);
905
906 return S_OK;
907}
908
909HRESULT Appliance::i_searchUniqueDiskImageFilePath(const Utf8Str &aMachineFolder, Utf8Str &aName) const
910{
911 IMedium *harddisk = NULL;
912 char *tmpName = RTStrDup(aName.c_str());
913 char *tmpAbsName = RTPathAbsExDup(aMachineFolder.c_str(), tmpName);
914 int i = 1;
915 /* Check if the file exists or if a file with this path is registered
916 * already */
917 /** @todo Maybe too cost-intensive; try to find a lighter way */
918 while ( RTPathExists(tmpAbsName)
919 || mVirtualBox->OpenMedium(Bstr(tmpAbsName).raw(), DeviceType_HardDisk, AccessMode_ReadWrite,
920 FALSE /* fForceNewUuid */, &harddisk) != VBOX_E_OBJECT_NOT_FOUND)
921 {
922 RTStrFree(tmpAbsName);
923 char *tmpDir = RTStrDup(aName.c_str());
924 RTPathStripFilename(tmpDir);
925 char *tmpFile = RTStrDup(RTPathFilename(aName.c_str()));
926 RTPathStripSuffix(tmpFile);
927 const char *pszTmpSuff = RTPathSuffix(aName.c_str());
928 RTStrAPrintf(&tmpName, "%s%c%s_%d%s", tmpDir, RTPATH_DELIMITER, tmpFile, i, pszTmpSuff);
929 tmpAbsName = RTPathAbsExDup(aMachineFolder.c_str(), tmpName);
930 RTStrFree(tmpFile);
931 RTStrFree(tmpDir);
932 ++i;
933 }
934 aName = tmpName;
935 RTStrFree(tmpName);
936
937 return S_OK;
938}
939
940/**
941 * Called from Appliance::importImpl() and Appliance::writeImpl() to set up a
942 * progress object with the proper weights and maximum progress values.
943 */
944HRESULT Appliance::i_setUpProgress(ComObjPtr<Progress> &pProgress,
945 const Utf8Str &strDescription,
946 SetUpProgressMode mode)
947{
948 HRESULT rc;
949
950 /* Create the progress object */
951 pProgress.createObject();
952
953 // compute the disks weight (this sets ulTotalDisksMB and cDisks in the instance data)
954 i_disksWeight();
955
956 m->ulWeightForManifestOperation = 0;
957
958 ULONG cOperations;
959 ULONG ulTotalOperationsWeight;
960
961 cOperations = 1 // one for XML setup
962 + m->cDisks; // plus one per disk
963 if (m->ulTotalDisksMB)
964 {
965 m->ulWeightForXmlOperation = (ULONG)((double)m->ulTotalDisksMB * 1 / 100); // use 1% of the progress for the XML
966 ulTotalOperationsWeight = m->ulTotalDisksMB + m->ulWeightForXmlOperation;
967 }
968 else
969 {
970 // no disks to export:
971 m->ulWeightForXmlOperation = 1;
972 ulTotalOperationsWeight = 1;
973 }
974
975 switch (mode)
976 {
977 case ImportFile:
978 {
979 break;
980 }
981 case WriteFile:
982 {
983 // assume that creating the manifest will take .1% of the time it takes to export the disks
984 if (m->fManifest)
985 {
986 ++cOperations; // another one for creating the manifest
987
988 m->ulWeightForManifestOperation = (ULONG)((double)m->ulTotalDisksMB * .1 / 100); // use .5% of the
989 // progress for the manifest
990 ulTotalOperationsWeight += m->ulWeightForManifestOperation;
991 }
992 break;
993 }
994 case ImportS3:
995 {
996 cOperations += 1 + 1; // another one for the manifest file & another one for the import
997 ulTotalOperationsWeight = m->ulTotalDisksMB;
998 if (!m->ulTotalDisksMB)
999 // no disks to export:
1000 ulTotalOperationsWeight = 1;
1001
1002 ULONG ulImportWeight = (ULONG)((double)ulTotalOperationsWeight * 50 / 100); // use 50% for import
1003 ulTotalOperationsWeight += ulImportWeight;
1004
1005 m->ulWeightForXmlOperation = ulImportWeight; /* save for using later */
1006
1007 ULONG ulInitWeight = (ULONG)((double)ulTotalOperationsWeight * 0.1 / 100); // use 0.1% for init
1008 ulTotalOperationsWeight += ulInitWeight;
1009 break;
1010 }
1011 case WriteS3:
1012 {
1013 cOperations += 1 + 1; // another one for the mf & another one for temporary creation
1014
1015 if (m->ulTotalDisksMB)
1016 {
1017 m->ulWeightForXmlOperation = (ULONG)((double)m->ulTotalDisksMB * 1 / 100); // use 1% of the progress
1018 // for OVF file upload
1019 // (we didn't know the
1020 // size at this point)
1021 ulTotalOperationsWeight = m->ulTotalDisksMB + m->ulWeightForXmlOperation;
1022 }
1023 else
1024 {
1025 // no disks to export:
1026 ulTotalOperationsWeight = 1;
1027 m->ulWeightForXmlOperation = 1;
1028 }
1029 ULONG ulOVFCreationWeight = (ULONG)((double)ulTotalOperationsWeight * 50.0 / 100.0); /* Use 50% for the
1030 creation of the OVF
1031 & the disks */
1032 ulTotalOperationsWeight += ulOVFCreationWeight;
1033 break;
1034 }
1035 }
1036 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightForXmlOperation = %d\n",
1037 m->ulTotalDisksMB, m->cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightForXmlOperation));
1038
1039 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
1040 Bstr(strDescription).raw(),
1041 TRUE /* aCancelable */,
1042 cOperations, // ULONG cOperations,
1043 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight,
1044 Bstr(strDescription).raw(), // CBSTR bstrFirstOperationDescription,
1045 m->ulWeightForXmlOperation); // ULONG ulFirstOperationWeight,
1046 return rc;
1047}
1048
1049/**
1050 * Called from the import and export background threads to synchronize the second
1051 * background disk thread's progress object with the current progress object so
1052 * that the user interface sees progress correctly and that cancel signals are
1053 * passed on to the second thread.
1054 *
1055 * @param pProgressThis Progress object of the current thread.
1056 * @param pProgressAsync Progress object of asynchronous task running in background.
1057 */
1058void Appliance::i_waitForAsyncProgress(ComObjPtr<Progress> &pProgressThis,
1059 ComPtr<IProgress> &pProgressAsync)
1060{
1061 HRESULT rc;
1062
1063 // now loop until the asynchronous operation completes and then report its result
1064 BOOL fCompleted;
1065 BOOL fCanceled;
1066 ULONG currentPercent;
1067 ULONG cOp = 0;
1068 while (SUCCEEDED(pProgressAsync->COMGETTER(Completed(&fCompleted))))
1069 {
1070 rc = pProgressThis->COMGETTER(Canceled)(&fCanceled);
1071 if (FAILED(rc)) throw rc;
1072 if (fCanceled)
1073 pProgressAsync->Cancel();
1074 /* Check if the current operation has changed. It is also possible
1075 that in the meantime more than one async operation was finished. So
1076 we have to loop as long as we reached the same operation count. */
1077 ULONG curOp;
1078 for (;;)
1079 {
1080 rc = pProgressAsync->COMGETTER(Operation(&curOp));
1081 if (FAILED(rc)) throw rc;
1082 if (cOp != curOp)
1083 {
1084 Bstr bstr;
1085 ULONG currentWeight;
1086 rc = pProgressAsync->COMGETTER(OperationDescription(bstr.asOutParam()));
1087 if (FAILED(rc)) throw rc;
1088 rc = pProgressAsync->COMGETTER(OperationWeight(&currentWeight));
1089 if (FAILED(rc)) throw rc;
1090 rc = pProgressThis->SetNextOperation(bstr.raw(), currentWeight);
1091 if (FAILED(rc)) throw rc;
1092 ++cOp;
1093 }
1094 else
1095 break;
1096 }
1097
1098 rc = pProgressAsync->COMGETTER(OperationPercent(&currentPercent));
1099 if (FAILED(rc)) throw rc;
1100 pProgressThis->SetCurrentOperationProgress(currentPercent);
1101 if (fCompleted)
1102 break;
1103
1104 /* Make sure the loop is not too tight */
1105 rc = pProgressAsync->WaitForCompletion(100);
1106 if (FAILED(rc)) throw rc;
1107 }
1108 // report result of asynchronous operation
1109 LONG iRc;
1110 rc = pProgressAsync->COMGETTER(ResultCode)(&iRc);
1111 if (FAILED(rc)) throw rc;
1112
1113
1114 // if the thread of the progress object has an error, then
1115 // retrieve the error info from there, or it'll be lost
1116 if (FAILED(iRc))
1117 {
1118 ProgressErrorInfo info(pProgressAsync);
1119 Utf8Str str(info.getText());
1120 const char *pcsz = str.c_str();
1121 HRESULT rc2 = setError(iRc, pcsz);
1122 throw rc2;
1123 }
1124}
1125
1126void Appliance::i_addWarning(const char* aWarning, ...)
1127{
1128 try
1129 {
1130 va_list args;
1131 va_start(args, aWarning);
1132 Utf8Str str(aWarning, args);
1133 va_end(args);
1134 m->llWarnings.push_back(str);
1135 }
1136 catch (...)
1137 {
1138 AssertFailed();
1139 }
1140}
1141
1142/**
1143 * Refreshes the cDisks and ulTotalDisksMB members in the instance data.
1144 * Requires that virtual system descriptions are present.
1145 */
1146void Appliance::i_disksWeight()
1147{
1148 m->ulTotalDisksMB = 0;
1149 m->cDisks = 0;
1150 // weigh the disk images according to their sizes
1151 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
1152 for (it = m->virtualSystemDescriptions.begin();
1153 it != m->virtualSystemDescriptions.end();
1154 ++it)
1155 {
1156 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
1157 /* One for every hard disk of the Virtual System */
1158 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->i_findByType(VirtualSystemDescriptionType_HardDiskImage);
1159 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
1160 for (itH = avsdeHDs.begin();
1161 itH != avsdeHDs.end();
1162 ++itH)
1163 {
1164 const VirtualSystemDescriptionEntry *pHD = *itH;
1165 m->ulTotalDisksMB += pHD->ulSizeMB;
1166 ++m->cDisks;
1167 }
1168
1169 avsdeHDs = vsdescThis->i_findByType(VirtualSystemDescriptionType_CDROM);
1170 for (itH = avsdeHDs.begin();
1171 itH != avsdeHDs.end();
1172 ++itH)
1173 {
1174 const VirtualSystemDescriptionEntry *pHD = *itH;
1175 m->ulTotalDisksMB += pHD->ulSizeMB;
1176 ++m->cDisks;
1177 }
1178 }
1179
1180}
1181
1182void Appliance::i_parseBucket(Utf8Str &aPath, Utf8Str &aBucket)
1183{
1184 /* Buckets are S3 specific. So parse the bucket out of the file path */
1185 if (!aPath.startsWith("/"))
1186 throw setError(E_INVALIDARG,
1187 tr("The path '%s' must start with /"), aPath.c_str());
1188 size_t bpos = aPath.find("/", 1);
1189 if (bpos != Utf8Str::npos)
1190 {
1191 aBucket = aPath.substr(1, bpos - 1); /* The bucket without any slashes */
1192 aPath = aPath.substr(bpos); /* The rest of the file path */
1193 }
1194 /* If there is no bucket name provided reject it */
1195 if (aBucket.isEmpty())
1196 throw setError(E_INVALIDARG,
1197 tr("You doesn't provide a bucket name in the URI '%s'"), aPath.c_str());
1198}
1199
1200/**
1201 * Worker for TaskOVF::handler.
1202 *
1203 * The TaskOVF is started in Appliance::readImpl() and Appliance::importImpl()
1204 * and Appliance::writeImpl().
1205 *
1206 * This will in turn call Appliance::readFS() or Appliance::importFS() or
1207 * Appliance::writeFS().
1208 *
1209 * @thread pTask The task.
1210 */
1211/* static */ void Appliance::i_importOrExportThreadTask(TaskOVF *pTask)
1212{
1213 LogFlowFuncEnter();
1214 AssertReturnVoid(pTask);
1215
1216 Appliance *pAppliance = pTask->pAppliance;
1217 LogFlowFunc(("Appliance %p taskType=%d\n", pAppliance, pTask->taskType));
1218
1219 switch (pTask->taskType)
1220 {
1221 case TaskOVF::Read:
1222 pAppliance->m->resetReadData();
1223 if (pTask->locInfo.storageType == VFSType_File)
1224 pTask->rc = pAppliance->i_readFS(pTask);
1225 else
1226 pTask->rc = E_NOTIMPL;
1227 break;
1228
1229 case TaskOVF::Import:
1230 /** @todo allow overriding these? */
1231 if (!pAppliance->m->fSignatureValid && pAppliance->m->pbSignedDigest)
1232 pTask->rc = pAppliance->setError(E_FAIL, tr("The manifest signature for '%s' is not valid"),
1233 pTask->locInfo.strPath.c_str());
1234 else if (!pAppliance->m->fCertificateValid && pAppliance->m->pbSignedDigest)
1235 {
1236 if (pAppliance->m->strCertError.isNotEmpty())
1237 pTask->rc = pAppliance->setError(E_FAIL, tr("The certificate used to signed '%s' is not valid: %s"),
1238 pTask->locInfo.strPath.c_str(), pAppliance->m->strCertError.c_str());
1239 else
1240 pTask->rc = pAppliance->setError(E_FAIL, tr("The certificate used to signed '%s' is not valid"),
1241 pTask->locInfo.strPath.c_str());
1242 }
1243 // fusion does not consider this a show stopper (we've filed a warning during read).
1244 //else if (pAppliance->m->fCertificateMissingPath && pAppliance->m->pbSignedDigest)
1245 // pTask->rc = pAppliance->setError(E_FAIL, tr("The certificate used to signed '%s' is does not have a valid CA path"),
1246 // pTask->locInfo.strPath.c_str());
1247 else
1248 {
1249 if (pTask->locInfo.storageType == VFSType_File)
1250 pTask->rc = pAppliance->i_importFS(pTask);
1251 else
1252 pTask->rc = E_NOTIMPL;
1253 }
1254 break;
1255
1256 case TaskOVF::Write:
1257 if (pTask->locInfo.storageType == VFSType_File)
1258 pTask->rc = pAppliance->i_writeFS(pTask);
1259 else
1260 pTask->rc = E_NOTIMPL;
1261 break;
1262
1263 default:
1264 AssertFailed();
1265 pTask->rc = E_FAIL;
1266 break;
1267 }
1268
1269 if (!pTask->pProgress.isNull())
1270 pTask->pProgress->i_notifyComplete(pTask->rc);
1271
1272 LogFlowFuncLeave();
1273}
1274
1275/* static */ DECLCALLBACK(int) Appliance::TaskOVF::updateProgress(unsigned uPercent, void *pvUser)
1276{
1277 Appliance::TaskOVF* pTask = *(Appliance::TaskOVF**)pvUser;
1278
1279 if ( pTask
1280 && !pTask->pProgress.isNull())
1281 {
1282 BOOL fCanceled;
1283 pTask->pProgress->COMGETTER(Canceled)(&fCanceled);
1284 if (fCanceled)
1285 return -1;
1286 pTask->pProgress->SetCurrentOperationProgress(uPercent);
1287 }
1288 return VINF_SUCCESS;
1289}
1290
1291void i_parseURI(Utf8Str strUri, LocationInfo &locInfo)
1292{
1293 /* Check the URI for the protocol */
1294 if (strUri.startsWith("file://", Utf8Str::CaseInsensitive)) /* File based */
1295 {
1296 locInfo.storageType = VFSType_File;
1297 strUri = strUri.substr(sizeof("file://") - 1);
1298 }
1299 else if (strUri.startsWith("SunCloud://", Utf8Str::CaseInsensitive)) /* Sun Cloud service */
1300 {
1301 locInfo.storageType = VFSType_S3;
1302 strUri = strUri.substr(sizeof("SunCloud://") - 1);
1303 }
1304 else if (strUri.startsWith("S3://", Utf8Str::CaseInsensitive)) /* S3 service */
1305 {
1306 locInfo.storageType = VFSType_S3;
1307 strUri = strUri.substr(sizeof("S3://") - 1);
1308 }
1309 else if (strUri.startsWith("webdav://", Utf8Str::CaseInsensitive)) /* webdav service */
1310 throw E_NOTIMPL;
1311
1312 /* Not necessary on a file based URI */
1313 if (locInfo.storageType != VFSType_File)
1314 {
1315 size_t uppos = strUri.find("@"); /* username:password combo */
1316 if (uppos != Utf8Str::npos)
1317 {
1318 locInfo.strUsername = strUri.substr(0, uppos);
1319 strUri = strUri.substr(uppos + 1);
1320 size_t upos = locInfo.strUsername.find(":");
1321 if (upos != Utf8Str::npos)
1322 {
1323 locInfo.strPassword = locInfo.strUsername.substr(upos + 1);
1324 locInfo.strUsername = locInfo.strUsername.substr(0, upos);
1325 }
1326 }
1327 size_t hpos = strUri.find("/"); /* hostname part */
1328 if (hpos != Utf8Str::npos)
1329 {
1330 locInfo.strHostname = strUri.substr(0, hpos);
1331 strUri = strUri.substr(hpos);
1332 }
1333 }
1334
1335 locInfo.strPath = strUri;
1336}
1337
1338
1339////////////////////////////////////////////////////////////////////////////////
1340//
1341// IVirtualSystemDescription constructor / destructor
1342//
1343////////////////////////////////////////////////////////////////////////////////
1344
1345/**
1346 * COM initializer.
1347 * @return
1348 */
1349HRESULT VirtualSystemDescription::init()
1350{
1351 /* Enclose the state transition NotReady->InInit->Ready */
1352 AutoInitSpan autoInitSpan(this);
1353 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1354
1355 /* Initialize data */
1356 m = new Data();
1357 m->pConfig = NULL;
1358
1359 /* Confirm a successful initialization */
1360 autoInitSpan.setSucceeded();
1361 return S_OK;
1362}
1363
1364/**
1365* COM uninitializer.
1366*/
1367
1368void VirtualSystemDescription::uninit()
1369{
1370 if (m->pConfig)
1371 delete m->pConfig;
1372 delete m;
1373 m = NULL;
1374}
1375
1376
1377////////////////////////////////////////////////////////////////////////////////
1378//
1379// IVirtualSystemDescription public methods
1380//
1381////////////////////////////////////////////////////////////////////////////////
1382
1383/**
1384 * Public method implementation.
1385 */
1386HRESULT VirtualSystemDescription::getCount(ULONG *aCount)
1387{
1388 if (!aCount)
1389 return E_POINTER;
1390
1391 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1392
1393 *aCount = (ULONG)m->maDescriptions.size();
1394 return S_OK;
1395}
1396
1397/**
1398 * Public method implementation.
1399 */
1400HRESULT VirtualSystemDescription::getDescription(std::vector<VirtualSystemDescriptionType_T> &aTypes,
1401 std::vector<com::Utf8Str> &aRefs,
1402 std::vector<com::Utf8Str> &aOVFValues,
1403 std::vector<com::Utf8Str> &aVBoxValues,
1404 std::vector<com::Utf8Str> &aExtraConfigValues)
1405
1406{
1407 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1408 size_t c = m->maDescriptions.size();
1409 aTypes.resize(c);
1410 aRefs.resize(c);
1411 aOVFValues.resize(c);
1412 aVBoxValues.resize(c);
1413 aExtraConfigValues.resize(c);
1414
1415 for (size_t i = 0; i < c; i++)
1416 {
1417 const VirtualSystemDescriptionEntry &vsde = m->maDescriptions[i];
1418 aTypes[i] = vsde.type;
1419 aRefs[i] = vsde.strRef;
1420 aOVFValues[i] = vsde.strOvf;
1421 aVBoxValues[i] = vsde.strVBoxCurrent;
1422 aExtraConfigValues[i] = vsde.strExtraConfigCurrent;
1423 }
1424 return S_OK;
1425}
1426
1427/**
1428 * Public method implementation.
1429 */
1430HRESULT VirtualSystemDescription::getDescriptionByType(VirtualSystemDescriptionType_T aType,
1431 std::vector<VirtualSystemDescriptionType_T> &aTypes,
1432 std::vector<com::Utf8Str> &aRefs,
1433 std::vector<com::Utf8Str> &aOVFValues,
1434 std::vector<com::Utf8Str> &aVBoxValues,
1435 std::vector<com::Utf8Str> &aExtraConfigValues)
1436{
1437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1438 std::list<VirtualSystemDescriptionEntry*> vsd = i_findByType(aType);
1439
1440 size_t c = vsd.size();
1441 aTypes.resize(c);
1442 aRefs.resize(c);
1443 aOVFValues.resize(c);
1444 aVBoxValues.resize(c);
1445 aExtraConfigValues.resize(c);
1446
1447 size_t i = 0;
1448 for (list<VirtualSystemDescriptionEntry*>::const_iterator it = vsd.begin(); it != vsd.end(); ++it, ++i)
1449 {
1450 const VirtualSystemDescriptionEntry *vsde = (*it);
1451 aTypes[i] = vsde->type;
1452 aRefs[i] = vsde->strRef;
1453 aOVFValues[i] = vsde->strOvf;
1454 aVBoxValues[i] = vsde->strVBoxCurrent;
1455 aExtraConfigValues[i] = vsde->strExtraConfigCurrent;
1456 }
1457
1458 return S_OK;
1459}
1460
1461/**
1462 * Public method implementation.
1463 */
1464HRESULT VirtualSystemDescription::getValuesByType(VirtualSystemDescriptionType_T aType,
1465 VirtualSystemDescriptionValueType_T aWhich,
1466 std::vector<com::Utf8Str> &aValues)
1467{
1468 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1469
1470 std::list<VirtualSystemDescriptionEntry*> vsd = i_findByType (aType);
1471 aValues.resize((ULONG)vsd.size());
1472
1473 list<VirtualSystemDescriptionEntry*>::const_iterator it;
1474 size_t i = 0;
1475 for (it = vsd.begin();
1476 it != vsd.end();
1477 ++it, ++i)
1478 {
1479 const VirtualSystemDescriptionEntry *vsde = (*it);
1480
1481 Bstr bstr;
1482 switch (aWhich)
1483 {
1484 case VirtualSystemDescriptionValueType_Reference: aValues[i] = vsde->strRef; break;
1485 case VirtualSystemDescriptionValueType_Original: aValues[i] = vsde->strOvf; break;
1486 case VirtualSystemDescriptionValueType_Auto: aValues[i] = vsde->strVBoxCurrent; break;
1487 case VirtualSystemDescriptionValueType_ExtraConfig: aValues[i] = vsde->strExtraConfigCurrent; break;
1488 }
1489 }
1490
1491 return S_OK;
1492}
1493
1494/**
1495 * Public method implementation.
1496 */
1497HRESULT VirtualSystemDescription::setFinalValues(const std::vector<BOOL> &aEnabled,
1498 const std::vector<com::Utf8Str> &aVBoxValues,
1499 const std::vector<com::Utf8Str> &aExtraConfigValues)
1500{
1501#ifndef RT_OS_WINDOWS
1502 // NOREF(aEnabledSize);
1503#endif /* RT_OS_WINDOWS */
1504 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1505
1506 if ( (aEnabled.size() != m->maDescriptions.size())
1507 || (aVBoxValues.size() != m->maDescriptions.size())
1508 || (aExtraConfigValues.size() != m->maDescriptions.size())
1509 )
1510 return E_INVALIDARG;
1511
1512 size_t i = 0;
1513 for (vector<VirtualSystemDescriptionEntry>::iterator it = m->maDescriptions.begin();
1514 it != m->maDescriptions.end();
1515 ++it, ++i)
1516 {
1517 VirtualSystemDescriptionEntry& vsde = *it;
1518
1519 if (aEnabled[i])
1520 {
1521 vsde.strVBoxCurrent = aVBoxValues[i];
1522 vsde.strExtraConfigCurrent = aExtraConfigValues[i];
1523 }
1524 else
1525 vsde.type = VirtualSystemDescriptionType_Ignore;
1526 }
1527
1528 return S_OK;
1529}
1530
1531/**
1532 * Public method implementation.
1533 */
1534HRESULT VirtualSystemDescription::addDescription(VirtualSystemDescriptionType_T aType,
1535 const com::Utf8Str &aVBoxValue,
1536 const com::Utf8Str &aExtraConfigValue)
1537
1538{
1539 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1540 i_addEntry(aType, "", aVBoxValue, aVBoxValue, 0, aExtraConfigValue);
1541 return S_OK;
1542}
1543
1544/**
1545 * Internal method; adds a new description item to the member list.
1546 * @param aType Type of description for the new item.
1547 * @param strRef Reference item; only used with hard disk controllers.
1548 * @param aOvfValue Corresponding original value from OVF.
1549 * @param aVBoxValue Initial configuration value (can be overridden by caller with setFinalValues).
1550 * @param ulSizeMB Weight for IProgress
1551 * @param strExtraConfig Extra configuration; meaning dependent on type.
1552 */
1553void VirtualSystemDescription::i_addEntry(VirtualSystemDescriptionType_T aType,
1554 const Utf8Str &strRef,
1555 const Utf8Str &aOvfValue,
1556 const Utf8Str &aVBoxValue,
1557 uint32_t ulSizeMB,
1558 const Utf8Str &strExtraConfig /*= ""*/)
1559{
1560 VirtualSystemDescriptionEntry vsde;
1561 vsde.ulIndex = (uint32_t)m->maDescriptions.size(); // each entry gets an index so the client side can reference them
1562 vsde.type = aType;
1563 vsde.strRef = strRef;
1564 vsde.strOvf = aOvfValue;
1565 vsde.strVBoxSuggested // remember original value
1566 = vsde.strVBoxCurrent // and set current value which can be overridden by setFinalValues()
1567 = aVBoxValue;
1568 vsde.strExtraConfigSuggested
1569 = vsde.strExtraConfigCurrent
1570 = strExtraConfig;
1571 vsde.ulSizeMB = ulSizeMB;
1572
1573 vsde.skipIt = false;
1574
1575 m->maDescriptions.push_back(vsde);
1576}
1577
1578/**
1579 * Private method; returns a list of description items containing all the items from the member
1580 * description items of this virtual system that match the given type.
1581 */
1582std::list<VirtualSystemDescriptionEntry*> VirtualSystemDescription::i_findByType(VirtualSystemDescriptionType_T aType)
1583{
1584 std::list<VirtualSystemDescriptionEntry*> vsd;
1585 for (vector<VirtualSystemDescriptionEntry>::iterator it = m->maDescriptions.begin();
1586 it != m->maDescriptions.end();
1587 ++it)
1588 {
1589 if (it->type == aType)
1590 vsd.push_back(&(*it));
1591 }
1592
1593 return vsd;
1594}
1595
1596/* Private method; delete all records from the list
1597 * m->llDescriptions that match the given type.
1598 */
1599void VirtualSystemDescription::i_removeByType(VirtualSystemDescriptionType_T aType)
1600{
1601 std::vector<VirtualSystemDescriptionEntry>::iterator it = m->maDescriptions.begin();
1602 while (it != m->maDescriptions.end())
1603 {
1604 if (it->type == aType)
1605 it = m->maDescriptions.erase(it);
1606 else
1607 ++it;
1608 }
1609}
1610
1611/**
1612 * Private method; looks thru the member hardware items for the IDE, SATA, or SCSI controller with
1613 * the given reference ID. Useful when needing the controller for a particular
1614 * virtual disk.
1615 */
1616const VirtualSystemDescriptionEntry* VirtualSystemDescription::i_findControllerFromID(uint32_t id)
1617{
1618 Utf8Str strRef = Utf8StrFmt("%RI32", id);
1619 vector<VirtualSystemDescriptionEntry>::const_iterator it;
1620 for (it = m->maDescriptions.begin();
1621 it != m->maDescriptions.end();
1622 ++it)
1623 {
1624 const VirtualSystemDescriptionEntry &d = *it;
1625 switch (d.type)
1626 {
1627 case VirtualSystemDescriptionType_HardDiskControllerIDE:
1628 case VirtualSystemDescriptionType_HardDiskControllerSATA:
1629 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
1630 case VirtualSystemDescriptionType_HardDiskControllerSAS:
1631 if (d.strRef == strRef)
1632 return &d;
1633 break;
1634 default: break; /* Shut up MSC. */
1635 }
1636 }
1637
1638 return NULL;
1639}
1640
1641/**
1642 * Method called from Appliance::Interpret() if the source OVF for a virtual system
1643 * contains a <vbox:Machine> element. This method then attempts to parse that and
1644 * create a MachineConfigFile instance from it which is stored in this instance data
1645 * and can then be used to create a machine.
1646 *
1647 * This must only be called once per instance.
1648 *
1649 * This rethrows all XML and logic errors from MachineConfigFile.
1650 *
1651 * @param elmMachine <vbox:Machine> element with attributes and subelements from some
1652 * DOM tree.
1653 */
1654void VirtualSystemDescription::i_importVBoxMachineXML(const xml::ElementNode &elmMachine)
1655{
1656 settings::MachineConfigFile *pConfig = NULL;
1657
1658 Assert(m->pConfig == NULL);
1659
1660 try
1661 {
1662 pConfig = new settings::MachineConfigFile(NULL);
1663 pConfig->importMachineXML(elmMachine);
1664
1665 m->pConfig = pConfig;
1666 }
1667 catch (...)
1668 {
1669 if (pConfig)
1670 delete pConfig;
1671 throw;
1672 }
1673}
1674
1675/**
1676 * Returns the machine config created by importVBoxMachineXML() or NULL if there's none.
1677 */
1678const settings::MachineConfigFile* VirtualSystemDescription::i_getMachineConfig() const
1679{
1680 return m->pConfig;
1681}
1682
Note: See TracBrowser for help on using the repository browser.

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