VirtualBox

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

Last change on this file since 72902 was 72902, checked in by vboxsync, 6 years ago

Appliance::i_URIFromTypeOfVirtualDiskFormat: is unused

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

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