VirtualBox

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

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

ApplianceImpl.cpp: Removed empty doxygen return and param tags.

  • 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 72901 2018-07-04 19:42:50Z 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 */
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
747std::set<Utf8Str> Appliance::i_URIFromTypeOfVirtualDiskFormat(Utf8Str type)
748{
749 Assert(g_fInitializedBackendNames);
750
751 std::set<Utf8Str> UriSet;
752 unsigned i = RT_ELEMENTS(g_aUriToBackend);
753 while (i-- > 0)
754 if (RTStrICmp(g_aUriToBackend[i].pszBackend, type.c_str()) == 0)
755 UriSet.insert(g_aUriToBackend[i].pszUri);
756 return UriSet;
757}
758
759/**
760 * Returns a medium format object corresponding to the given
761 * disk image or null if no such format.
762 *
763 * @param di Disk Image
764 * @param mf Medium Format
765 *
766 * @return ComObjPtr<MediumFormat>
767 */
768HRESULT Appliance::i_findMediumFormatFromDiskImage(const ovf::DiskImage &di, ComObjPtr<MediumFormat>& mf)
769{
770 HRESULT rc = S_OK;
771
772 /* Get the system properties. */
773 SystemProperties *pSysProps = mVirtualBox->i_getSystemProperties();
774
775 /* We need a proper source format description */
776 /* Which format to use? */
777 Utf8Str strSrcFormat = i_typeOfVirtualDiskFormatFromURI(di.strFormat);
778
779 /*
780 * fallback, if we can't determine virtual disk format using URI from the attribute ovf:format
781 * in the corresponding section <Disk> in the OVF file.
782 */
783 if (strSrcFormat.isEmpty())
784 {
785 strSrcFormat = di.strHref;
786
787 /* check either file gzipped or not
788 * if "yes" then remove last extension,
789 * i.e. "image.vmdk.gz"->"image.vmdk"
790 */
791 if (di.strCompression == "gzip")
792 {
793 if (RTPathHasSuffix(strSrcFormat.c_str()))
794 {
795 strSrcFormat.stripSuffix();
796 }
797 else
798 {
799 mf.setNull();
800 rc = setError(E_FAIL,
801 tr("Internal inconsistency looking up medium format for the disk image '%s'"),
802 di.strHref.c_str());
803 return rc;
804 }
805 }
806 /* Figure out from extension which format the image of disk has. */
807 if (RTPathHasSuffix(strSrcFormat.c_str()))
808 {
809 const char *pszExt = RTPathSuffix(strSrcFormat.c_str());
810 if (pszExt)
811 pszExt++;
812 mf = pSysProps->i_mediumFormatFromExtension(pszExt);
813 }
814 else
815 mf.setNull();
816 }
817 else
818 mf = pSysProps->i_mediumFormat(strSrcFormat);
819
820 if (mf.isNull())
821 rc = setError(E_FAIL, tr("Internal inconsistency looking up medium format for the disk image '%s'"),
822 di.strHref.c_str());
823
824 return rc;
825}
826
827/**
828 * Setup automatic I/O stream digest calculation, adding it to hOurManifest.
829 *
830 * @returns Passthru I/O stream, of @a hVfsIos if no digest calc needed.
831 * @param hVfsIos The stream to wrap. Always consumed.
832 * @param pszManifestEntry The manifest entry.
833 * @param fRead Set if read stream, clear if write.
834 * @throws Nothing.
835 */
836RTVFSIOSTREAM Appliance::i_manifestSetupDigestCalculationForGivenIoStream(RTVFSIOSTREAM hVfsIos, const char *pszManifestEntry,
837 bool fRead /*= true */)
838{
839 int vrc;
840 Assert(!RTManifestPtIosIsInstanceOf(hVfsIos));
841
842 if (m->fDigestTypes == 0)
843 return hVfsIos;
844
845 /* Create the manifest if necessary. */
846 if (m->hOurManifest == NIL_RTMANIFEST)
847 {
848 vrc = RTManifestCreate(0 /*fFlags*/, &m->hOurManifest);
849 AssertRCReturnStmt(vrc, RTVfsIoStrmRelease(hVfsIos), NIL_RTVFSIOSTREAM);
850 }
851
852 /* Setup the stream. */
853 RTVFSIOSTREAM hVfsIosPt;
854 vrc = RTManifestEntryAddPassthruIoStream(m->hOurManifest, hVfsIos, pszManifestEntry, m->fDigestTypes, fRead, &hVfsIosPt);
855
856 RTVfsIoStrmRelease(hVfsIos); /* always consumed! */
857 if (RT_SUCCESS(vrc))
858 return hVfsIosPt;
859
860 setErrorVrc(vrc, "RTManifestEntryAddPassthruIoStream failed with rc=%Rrc", vrc);
861 return NIL_RTVFSIOSTREAM;
862}
863
864/**
865 * Returns true if the appliance is in "idle" state. This should always be the
866 * case unless an import or export is currently in progress. Similar to machine
867 * states, this permits the Appliance implementation code to let go of the
868 * Appliance object lock while a time-consuming disk conversion is in progress
869 * without exposing the appliance to conflicting calls.
870 *
871 * This sets an error on "this" (the appliance) and returns false if the appliance
872 * is busy. The caller should then return E_ACCESSDENIED.
873 *
874 * Must be called from under the object lock!
875 */
876bool Appliance::i_isApplianceIdle()
877{
878 if (m->state == Data::ApplianceImporting)
879 setError(VBOX_E_INVALID_OBJECT_STATE, tr("The appliance is busy importing files"));
880 else if (m->state == Data::ApplianceExporting)
881 setError(VBOX_E_INVALID_OBJECT_STATE, tr("The appliance is busy exporting files"));
882 else
883 return true;
884
885 return false;
886}
887
888HRESULT Appliance::i_searchUniqueVMName(Utf8Str& aName) const
889{
890 IMachine *machine = NULL;
891 char *tmpName = RTStrDup(aName.c_str());
892 int i = 1;
893 /** @todo Maybe too cost-intensive; try to find a lighter way */
894 while (mVirtualBox->FindMachine(Bstr(tmpName).raw(), &machine) != VBOX_E_OBJECT_NOT_FOUND)
895 {
896 RTStrFree(tmpName);
897 RTStrAPrintf(&tmpName, "%s %d", aName.c_str(), i);
898 ++i;
899 }
900 aName = tmpName;
901 RTStrFree(tmpName);
902
903 return S_OK;
904}
905
906HRESULT Appliance::i_searchUniqueDiskImageFilePath(const Utf8Str &aMachineFolder, Utf8Str &aName) const
907{
908 IMedium *harddisk = NULL;
909 char *tmpName = RTStrDup(aName.c_str());
910 char *tmpAbsName = RTPathAbsExDup(aMachineFolder.c_str(), tmpName);
911 int i = 1;
912 /* Check if the file exists or if a file with this path is registered
913 * already */
914 /** @todo Maybe too cost-intensive; try to find a lighter way */
915 while ( RTPathExists(tmpAbsName)
916 || mVirtualBox->OpenMedium(Bstr(tmpAbsName).raw(), DeviceType_HardDisk, AccessMode_ReadWrite,
917 FALSE /* fForceNewUuid */, &harddisk) != VBOX_E_OBJECT_NOT_FOUND)
918 {
919 RTStrFree(tmpAbsName);
920 char *tmpDir = RTStrDup(aName.c_str());
921 RTPathStripFilename(tmpDir);
922 char *tmpFile = RTStrDup(RTPathFilename(aName.c_str()));
923 RTPathStripSuffix(tmpFile);
924 const char *pszTmpSuff = RTPathSuffix(aName.c_str());
925 RTStrAPrintf(&tmpName, "%s%c%s_%d%s", tmpDir, RTPATH_DELIMITER, tmpFile, i, pszTmpSuff);
926 tmpAbsName = RTPathAbsExDup(aMachineFolder.c_str(), tmpName);
927 RTStrFree(tmpFile);
928 RTStrFree(tmpDir);
929 ++i;
930 }
931 aName = tmpName;
932 RTStrFree(tmpName);
933
934 return S_OK;
935}
936
937/**
938 * Called from Appliance::importImpl() and Appliance::writeImpl() to set up a
939 * progress object with the proper weights and maximum progress values.
940 */
941HRESULT Appliance::i_setUpProgress(ComObjPtr<Progress> &pProgress,
942 const Utf8Str &strDescription,
943 SetUpProgressMode mode)
944{
945 HRESULT rc;
946
947 /* Create the progress object */
948 pProgress.createObject();
949
950 // compute the disks weight (this sets ulTotalDisksMB and cDisks in the instance data)
951 i_disksWeight();
952
953 m->ulWeightForManifestOperation = 0;
954
955 ULONG cOperations;
956 ULONG ulTotalOperationsWeight;
957
958 cOperations = 1 // one for XML setup
959 + m->cDisks; // plus one per disk
960 if (m->ulTotalDisksMB)
961 {
962 m->ulWeightForXmlOperation = (ULONG)((double)m->ulTotalDisksMB * 1 / 100); // use 1% of the progress for the XML
963 ulTotalOperationsWeight = m->ulTotalDisksMB + m->ulWeightForXmlOperation;
964 }
965 else
966 {
967 // no disks to export:
968 m->ulWeightForXmlOperation = 1;
969 ulTotalOperationsWeight = 1;
970 }
971
972 switch (mode)
973 {
974 case ImportFile:
975 {
976 break;
977 }
978 case WriteFile:
979 {
980 // assume that creating the manifest will take .1% of the time it takes to export the disks
981 if (m->fManifest)
982 {
983 ++cOperations; // another one for creating the manifest
984
985 m->ulWeightForManifestOperation = (ULONG)((double)m->ulTotalDisksMB * .1 / 100); // use .5% of the
986 // progress for the manifest
987 ulTotalOperationsWeight += m->ulWeightForManifestOperation;
988 }
989 break;
990 }
991 case ImportS3:
992 {
993 cOperations += 1 + 1; // another one for the manifest file & another one for the import
994 ulTotalOperationsWeight = m->ulTotalDisksMB;
995 if (!m->ulTotalDisksMB)
996 // no disks to export:
997 ulTotalOperationsWeight = 1;
998
999 ULONG ulImportWeight = (ULONG)((double)ulTotalOperationsWeight * 50 / 100); // use 50% for import
1000 ulTotalOperationsWeight += ulImportWeight;
1001
1002 m->ulWeightForXmlOperation = ulImportWeight; /* save for using later */
1003
1004 ULONG ulInitWeight = (ULONG)((double)ulTotalOperationsWeight * 0.1 / 100); // use 0.1% for init
1005 ulTotalOperationsWeight += ulInitWeight;
1006 break;
1007 }
1008 case WriteS3:
1009 {
1010 cOperations += 1 + 1; // another one for the mf & another one for temporary creation
1011
1012 if (m->ulTotalDisksMB)
1013 {
1014 m->ulWeightForXmlOperation = (ULONG)((double)m->ulTotalDisksMB * 1 / 100); // use 1% of the progress
1015 // for OVF file upload
1016 // (we didn't know the
1017 // size at this point)
1018 ulTotalOperationsWeight = m->ulTotalDisksMB + m->ulWeightForXmlOperation;
1019 }
1020 else
1021 {
1022 // no disks to export:
1023 ulTotalOperationsWeight = 1;
1024 m->ulWeightForXmlOperation = 1;
1025 }
1026 ULONG ulOVFCreationWeight = (ULONG)((double)ulTotalOperationsWeight * 50.0 / 100.0); /* Use 50% for the
1027 creation of the OVF
1028 & the disks */
1029 ulTotalOperationsWeight += ulOVFCreationWeight;
1030 break;
1031 }
1032 }
1033 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightForXmlOperation = %d\n",
1034 m->ulTotalDisksMB, m->cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightForXmlOperation));
1035
1036 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
1037 Bstr(strDescription).raw(),
1038 TRUE /* aCancelable */,
1039 cOperations, // ULONG cOperations,
1040 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight,
1041 Bstr(strDescription).raw(), // CBSTR bstrFirstOperationDescription,
1042 m->ulWeightForXmlOperation); // ULONG ulFirstOperationWeight,
1043 return rc;
1044}
1045
1046/**
1047 * Called from the import and export background threads to synchronize the second
1048 * background disk thread's progress object with the current progress object so
1049 * that the user interface sees progress correctly and that cancel signals are
1050 * passed on to the second thread.
1051 *
1052 * @param pProgressThis Progress object of the current thread.
1053 * @param pProgressAsync Progress object of asynchronous task running in background.
1054 */
1055void Appliance::i_waitForAsyncProgress(ComObjPtr<Progress> &pProgressThis,
1056 ComPtr<IProgress> &pProgressAsync)
1057{
1058 HRESULT rc;
1059
1060 // now loop until the asynchronous operation completes and then report its result
1061 BOOL fCompleted;
1062 BOOL fCanceled;
1063 ULONG currentPercent;
1064 ULONG cOp = 0;
1065 while (SUCCEEDED(pProgressAsync->COMGETTER(Completed(&fCompleted))))
1066 {
1067 rc = pProgressThis->COMGETTER(Canceled)(&fCanceled);
1068 if (FAILED(rc)) throw rc;
1069 if (fCanceled)
1070 pProgressAsync->Cancel();
1071 /* Check if the current operation has changed. It is also possible
1072 that in the meantime more than one async operation was finished. So
1073 we have to loop as long as we reached the same operation count. */
1074 ULONG curOp;
1075 for (;;)
1076 {
1077 rc = pProgressAsync->COMGETTER(Operation(&curOp));
1078 if (FAILED(rc)) throw rc;
1079 if (cOp != curOp)
1080 {
1081 Bstr bstr;
1082 ULONG currentWeight;
1083 rc = pProgressAsync->COMGETTER(OperationDescription(bstr.asOutParam()));
1084 if (FAILED(rc)) throw rc;
1085 rc = pProgressAsync->COMGETTER(OperationWeight(&currentWeight));
1086 if (FAILED(rc)) throw rc;
1087 rc = pProgressThis->SetNextOperation(bstr.raw(), currentWeight);
1088 if (FAILED(rc)) throw rc;
1089 ++cOp;
1090 }
1091 else
1092 break;
1093 }
1094
1095 rc = pProgressAsync->COMGETTER(OperationPercent(&currentPercent));
1096 if (FAILED(rc)) throw rc;
1097 pProgressThis->SetCurrentOperationProgress(currentPercent);
1098 if (fCompleted)
1099 break;
1100
1101 /* Make sure the loop is not too tight */
1102 rc = pProgressAsync->WaitForCompletion(100);
1103 if (FAILED(rc)) throw rc;
1104 }
1105 // report result of asynchronous operation
1106 LONG iRc;
1107 rc = pProgressAsync->COMGETTER(ResultCode)(&iRc);
1108 if (FAILED(rc)) throw rc;
1109
1110
1111 // if the thread of the progress object has an error, then
1112 // retrieve the error info from there, or it'll be lost
1113 if (FAILED(iRc))
1114 {
1115 ProgressErrorInfo info(pProgressAsync);
1116 Utf8Str str(info.getText());
1117 const char *pcsz = str.c_str();
1118 HRESULT rc2 = setError(iRc, pcsz);
1119 throw rc2;
1120 }
1121}
1122
1123void Appliance::i_addWarning(const char* aWarning, ...)
1124{
1125 try
1126 {
1127 va_list args;
1128 va_start(args, aWarning);
1129 Utf8Str str(aWarning, args);
1130 va_end(args);
1131 m->llWarnings.push_back(str);
1132 }
1133 catch (...)
1134 {
1135 AssertFailed();
1136 }
1137}
1138
1139/**
1140 * Refreshes the cDisks and ulTotalDisksMB members in the instance data.
1141 * Requires that virtual system descriptions are present.
1142 */
1143void Appliance::i_disksWeight()
1144{
1145 m->ulTotalDisksMB = 0;
1146 m->cDisks = 0;
1147 // weigh the disk images according to their sizes
1148 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
1149 for (it = m->virtualSystemDescriptions.begin();
1150 it != m->virtualSystemDescriptions.end();
1151 ++it)
1152 {
1153 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
1154 /* One for every hard disk of the Virtual System */
1155 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->i_findByType(VirtualSystemDescriptionType_HardDiskImage);
1156 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
1157 for (itH = avsdeHDs.begin();
1158 itH != avsdeHDs.end();
1159 ++itH)
1160 {
1161 const VirtualSystemDescriptionEntry *pHD = *itH;
1162 m->ulTotalDisksMB += pHD->ulSizeMB;
1163 ++m->cDisks;
1164 }
1165
1166 avsdeHDs = vsdescThis->i_findByType(VirtualSystemDescriptionType_CDROM);
1167 for (itH = avsdeHDs.begin();
1168 itH != avsdeHDs.end();
1169 ++itH)
1170 {
1171 const VirtualSystemDescriptionEntry *pHD = *itH;
1172 m->ulTotalDisksMB += pHD->ulSizeMB;
1173 ++m->cDisks;
1174 }
1175 }
1176
1177}
1178
1179void Appliance::i_parseBucket(Utf8Str &aPath, Utf8Str &aBucket)
1180{
1181 /* Buckets are S3 specific. So parse the bucket out of the file path */
1182 if (!aPath.startsWith("/"))
1183 throw setError(E_INVALIDARG,
1184 tr("The path '%s' must start with /"), aPath.c_str());
1185 size_t bpos = aPath.find("/", 1);
1186 if (bpos != Utf8Str::npos)
1187 {
1188 aBucket = aPath.substr(1, bpos - 1); /* The bucket without any slashes */
1189 aPath = aPath.substr(bpos); /* The rest of the file path */
1190 }
1191 /* If there is no bucket name provided reject it */
1192 if (aBucket.isEmpty())
1193 throw setError(E_INVALIDARG,
1194 tr("You doesn't provide a bucket name in the URI '%s'"), aPath.c_str());
1195}
1196
1197/**
1198 * Worker for TaskOVF::handler.
1199 *
1200 * The TaskOVF is started in Appliance::readImpl() and Appliance::importImpl()
1201 * and Appliance::writeImpl().
1202 *
1203 * This will in turn call Appliance::readFS() or Appliance::importFS() or
1204 * Appliance::writeFS().
1205 *
1206 * @thread pTask The task.
1207 */
1208/* static */ void Appliance::i_importOrExportThreadTask(TaskOVF *pTask)
1209{
1210 LogFlowFuncEnter();
1211 AssertReturnVoid(pTask);
1212
1213 Appliance *pAppliance = pTask->pAppliance;
1214 LogFlowFunc(("Appliance %p taskType=%d\n", pAppliance, pTask->taskType));
1215
1216 switch (pTask->taskType)
1217 {
1218 case TaskOVF::Read:
1219 pAppliance->m->resetReadData();
1220 if (pTask->locInfo.storageType == VFSType_File)
1221 pTask->rc = pAppliance->i_readFS(pTask);
1222 else
1223 pTask->rc = E_NOTIMPL;
1224 break;
1225
1226 case TaskOVF::Import:
1227 /** @todo allow overriding these? */
1228 if (!pAppliance->m->fSignatureValid && pAppliance->m->pbSignedDigest)
1229 pTask->rc = pAppliance->setError(E_FAIL, tr("The manifest signature for '%s' is not valid"),
1230 pTask->locInfo.strPath.c_str());
1231 else if (!pAppliance->m->fCertificateValid && pAppliance->m->pbSignedDigest)
1232 {
1233 if (pAppliance->m->strCertError.isNotEmpty())
1234 pTask->rc = pAppliance->setError(E_FAIL, tr("The certificate used to signed '%s' is not valid: %s"),
1235 pTask->locInfo.strPath.c_str(), pAppliance->m->strCertError.c_str());
1236 else
1237 pTask->rc = pAppliance->setError(E_FAIL, tr("The certificate used to signed '%s' is not valid"),
1238 pTask->locInfo.strPath.c_str());
1239 }
1240 // fusion does not consider this a show stopper (we've filed a warning during read).
1241 //else if (pAppliance->m->fCertificateMissingPath && pAppliance->m->pbSignedDigest)
1242 // pTask->rc = pAppliance->setError(E_FAIL, tr("The certificate used to signed '%s' is does not have a valid CA path"),
1243 // pTask->locInfo.strPath.c_str());
1244 else
1245 {
1246 if (pTask->locInfo.storageType == VFSType_File)
1247 pTask->rc = pAppliance->i_importFS(pTask);
1248 else
1249 pTask->rc = E_NOTIMPL;
1250 }
1251 break;
1252
1253 case TaskOVF::Write:
1254 if (pTask->locInfo.storageType == VFSType_File)
1255 pTask->rc = pAppliance->i_writeFS(pTask);
1256 else
1257 pTask->rc = E_NOTIMPL;
1258 break;
1259
1260 default:
1261 AssertFailed();
1262 pTask->rc = E_FAIL;
1263 break;
1264 }
1265
1266 if (!pTask->pProgress.isNull())
1267 pTask->pProgress->i_notifyComplete(pTask->rc);
1268
1269 LogFlowFuncLeave();
1270}
1271
1272/* static */ DECLCALLBACK(int) Appliance::TaskOVF::updateProgress(unsigned uPercent, void *pvUser)
1273{
1274 Appliance::TaskOVF* pTask = *(Appliance::TaskOVF**)pvUser;
1275
1276 if ( pTask
1277 && !pTask->pProgress.isNull())
1278 {
1279 BOOL fCanceled;
1280 pTask->pProgress->COMGETTER(Canceled)(&fCanceled);
1281 if (fCanceled)
1282 return -1;
1283 pTask->pProgress->SetCurrentOperationProgress(uPercent);
1284 }
1285 return VINF_SUCCESS;
1286}
1287
1288void i_parseURI(Utf8Str strUri, LocationInfo &locInfo)
1289{
1290 /* Check the URI for the protocol */
1291 if (strUri.startsWith("file://", Utf8Str::CaseInsensitive)) /* File based */
1292 {
1293 locInfo.storageType = VFSType_File;
1294 strUri = strUri.substr(sizeof("file://") - 1);
1295 }
1296 else if (strUri.startsWith("SunCloud://", Utf8Str::CaseInsensitive)) /* Sun Cloud service */
1297 {
1298 locInfo.storageType = VFSType_S3;
1299 strUri = strUri.substr(sizeof("SunCloud://") - 1);
1300 }
1301 else if (strUri.startsWith("S3://", Utf8Str::CaseInsensitive)) /* S3 service */
1302 {
1303 locInfo.storageType = VFSType_S3;
1304 strUri = strUri.substr(sizeof("S3://") - 1);
1305 }
1306 else if (strUri.startsWith("webdav://", Utf8Str::CaseInsensitive)) /* webdav service */
1307 throw E_NOTIMPL;
1308
1309 /* Not necessary on a file based URI */
1310 if (locInfo.storageType != VFSType_File)
1311 {
1312 size_t uppos = strUri.find("@"); /* username:password combo */
1313 if (uppos != Utf8Str::npos)
1314 {
1315 locInfo.strUsername = strUri.substr(0, uppos);
1316 strUri = strUri.substr(uppos + 1);
1317 size_t upos = locInfo.strUsername.find(":");
1318 if (upos != Utf8Str::npos)
1319 {
1320 locInfo.strPassword = locInfo.strUsername.substr(upos + 1);
1321 locInfo.strUsername = locInfo.strUsername.substr(0, upos);
1322 }
1323 }
1324 size_t hpos = strUri.find("/"); /* hostname part */
1325 if (hpos != Utf8Str::npos)
1326 {
1327 locInfo.strHostname = strUri.substr(0, hpos);
1328 strUri = strUri.substr(hpos);
1329 }
1330 }
1331
1332 locInfo.strPath = strUri;
1333}
1334
1335
1336////////////////////////////////////////////////////////////////////////////////
1337//
1338// IVirtualSystemDescription constructor / destructor
1339//
1340////////////////////////////////////////////////////////////////////////////////
1341
1342/**
1343 * COM initializer.
1344 * @return
1345 */
1346HRESULT VirtualSystemDescription::init()
1347{
1348 /* Enclose the state transition NotReady->InInit->Ready */
1349 AutoInitSpan autoInitSpan(this);
1350 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1351
1352 /* Initialize data */
1353 m = new Data();
1354 m->pConfig = NULL;
1355
1356 /* Confirm a successful initialization */
1357 autoInitSpan.setSucceeded();
1358 return S_OK;
1359}
1360
1361/**
1362* COM uninitializer.
1363*/
1364
1365void VirtualSystemDescription::uninit()
1366{
1367 if (m->pConfig)
1368 delete m->pConfig;
1369 delete m;
1370 m = NULL;
1371}
1372
1373
1374////////////////////////////////////////////////////////////////////////////////
1375//
1376// IVirtualSystemDescription public methods
1377//
1378////////////////////////////////////////////////////////////////////////////////
1379
1380/**
1381 * Public method implementation.
1382 */
1383HRESULT VirtualSystemDescription::getCount(ULONG *aCount)
1384{
1385 if (!aCount)
1386 return E_POINTER;
1387
1388 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1389
1390 *aCount = (ULONG)m->maDescriptions.size();
1391 return S_OK;
1392}
1393
1394/**
1395 * Public method implementation.
1396 */
1397HRESULT VirtualSystemDescription::getDescription(std::vector<VirtualSystemDescriptionType_T> &aTypes,
1398 std::vector<com::Utf8Str> &aRefs,
1399 std::vector<com::Utf8Str> &aOVFValues,
1400 std::vector<com::Utf8Str> &aVBoxValues,
1401 std::vector<com::Utf8Str> &aExtraConfigValues)
1402
1403{
1404 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1405 size_t c = m->maDescriptions.size();
1406 aTypes.resize(c);
1407 aRefs.resize(c);
1408 aOVFValues.resize(c);
1409 aVBoxValues.resize(c);
1410 aExtraConfigValues.resize(c);
1411
1412 for (size_t i = 0; i < c; i++)
1413 {
1414 const VirtualSystemDescriptionEntry &vsde = m->maDescriptions[i];
1415 aTypes[i] = vsde.type;
1416 aRefs[i] = vsde.strRef;
1417 aOVFValues[i] = vsde.strOvf;
1418 aVBoxValues[i] = vsde.strVBoxCurrent;
1419 aExtraConfigValues[i] = vsde.strExtraConfigCurrent;
1420 }
1421 return S_OK;
1422}
1423
1424/**
1425 * Public method implementation.
1426 */
1427HRESULT VirtualSystemDescription::getDescriptionByType(VirtualSystemDescriptionType_T aType,
1428 std::vector<VirtualSystemDescriptionType_T> &aTypes,
1429 std::vector<com::Utf8Str> &aRefs,
1430 std::vector<com::Utf8Str> &aOVFValues,
1431 std::vector<com::Utf8Str> &aVBoxValues,
1432 std::vector<com::Utf8Str> &aExtraConfigValues)
1433{
1434 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1435 std::list<VirtualSystemDescriptionEntry*> vsd = i_findByType(aType);
1436
1437 size_t c = vsd.size();
1438 aTypes.resize(c);
1439 aRefs.resize(c);
1440 aOVFValues.resize(c);
1441 aVBoxValues.resize(c);
1442 aExtraConfigValues.resize(c);
1443
1444 size_t i = 0;
1445 for (list<VirtualSystemDescriptionEntry*>::const_iterator it = vsd.begin(); it != vsd.end(); ++it, ++i)
1446 {
1447 const VirtualSystemDescriptionEntry *vsde = (*it);
1448 aTypes[i] = vsde->type;
1449 aRefs[i] = vsde->strRef;
1450 aOVFValues[i] = vsde->strOvf;
1451 aVBoxValues[i] = vsde->strVBoxCurrent;
1452 aExtraConfigValues[i] = vsde->strExtraConfigCurrent;
1453 }
1454
1455 return S_OK;
1456}
1457
1458/**
1459 * Public method implementation.
1460 */
1461HRESULT VirtualSystemDescription::getValuesByType(VirtualSystemDescriptionType_T aType,
1462 VirtualSystemDescriptionValueType_T aWhich,
1463 std::vector<com::Utf8Str> &aValues)
1464{
1465 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1466
1467 std::list<VirtualSystemDescriptionEntry*> vsd = i_findByType (aType);
1468 aValues.resize((ULONG)vsd.size());
1469
1470 list<VirtualSystemDescriptionEntry*>::const_iterator it;
1471 size_t i = 0;
1472 for (it = vsd.begin();
1473 it != vsd.end();
1474 ++it, ++i)
1475 {
1476 const VirtualSystemDescriptionEntry *vsde = (*it);
1477
1478 Bstr bstr;
1479 switch (aWhich)
1480 {
1481 case VirtualSystemDescriptionValueType_Reference: aValues[i] = vsde->strRef; break;
1482 case VirtualSystemDescriptionValueType_Original: aValues[i] = vsde->strOvf; break;
1483 case VirtualSystemDescriptionValueType_Auto: aValues[i] = vsde->strVBoxCurrent; break;
1484 case VirtualSystemDescriptionValueType_ExtraConfig: aValues[i] = vsde->strExtraConfigCurrent; break;
1485 }
1486 }
1487
1488 return S_OK;
1489}
1490
1491/**
1492 * Public method implementation.
1493 */
1494HRESULT VirtualSystemDescription::setFinalValues(const std::vector<BOOL> &aEnabled,
1495 const std::vector<com::Utf8Str> &aVBoxValues,
1496 const std::vector<com::Utf8Str> &aExtraConfigValues)
1497{
1498#ifndef RT_OS_WINDOWS
1499 // NOREF(aEnabledSize);
1500#endif /* RT_OS_WINDOWS */
1501 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1502
1503 if ( (aEnabled.size() != m->maDescriptions.size())
1504 || (aVBoxValues.size() != m->maDescriptions.size())
1505 || (aExtraConfigValues.size() != m->maDescriptions.size())
1506 )
1507 return E_INVALIDARG;
1508
1509 size_t i = 0;
1510 for (vector<VirtualSystemDescriptionEntry>::iterator it = m->maDescriptions.begin();
1511 it != m->maDescriptions.end();
1512 ++it, ++i)
1513 {
1514 VirtualSystemDescriptionEntry& vsde = *it;
1515
1516 if (aEnabled[i])
1517 {
1518 vsde.strVBoxCurrent = aVBoxValues[i];
1519 vsde.strExtraConfigCurrent = aExtraConfigValues[i];
1520 }
1521 else
1522 vsde.type = VirtualSystemDescriptionType_Ignore;
1523 }
1524
1525 return S_OK;
1526}
1527
1528/**
1529 * Public method implementation.
1530 */
1531HRESULT VirtualSystemDescription::addDescription(VirtualSystemDescriptionType_T aType,
1532 const com::Utf8Str &aVBoxValue,
1533 const com::Utf8Str &aExtraConfigValue)
1534
1535{
1536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1537 i_addEntry(aType, "", aVBoxValue, aVBoxValue, 0, aExtraConfigValue);
1538 return S_OK;
1539}
1540
1541/**
1542 * Internal method; adds a new description item to the member list.
1543 * @param aType Type of description for the new item.
1544 * @param strRef Reference item; only used with hard disk controllers.
1545 * @param aOvfValue Corresponding original value from OVF.
1546 * @param aVBoxValue Initial configuration value (can be overridden by caller with setFinalValues).
1547 * @param ulSizeMB Weight for IProgress
1548 * @param strExtraConfig Extra configuration; meaning dependent on type.
1549 */
1550void VirtualSystemDescription::i_addEntry(VirtualSystemDescriptionType_T aType,
1551 const Utf8Str &strRef,
1552 const Utf8Str &aOvfValue,
1553 const Utf8Str &aVBoxValue,
1554 uint32_t ulSizeMB,
1555 const Utf8Str &strExtraConfig /*= ""*/)
1556{
1557 VirtualSystemDescriptionEntry vsde;
1558 vsde.ulIndex = (uint32_t)m->maDescriptions.size(); // each entry gets an index so the client side can reference them
1559 vsde.type = aType;
1560 vsde.strRef = strRef;
1561 vsde.strOvf = aOvfValue;
1562 vsde.strVBoxSuggested // remember original value
1563 = vsde.strVBoxCurrent // and set current value which can be overridden by setFinalValues()
1564 = aVBoxValue;
1565 vsde.strExtraConfigSuggested
1566 = vsde.strExtraConfigCurrent
1567 = strExtraConfig;
1568 vsde.ulSizeMB = ulSizeMB;
1569
1570 vsde.skipIt = false;
1571
1572 m->maDescriptions.push_back(vsde);
1573}
1574
1575/**
1576 * Private method; returns a list of description items containing all the items from the member
1577 * description items of this virtual system that match the given type.
1578 */
1579std::list<VirtualSystemDescriptionEntry*> VirtualSystemDescription::i_findByType(VirtualSystemDescriptionType_T aType)
1580{
1581 std::list<VirtualSystemDescriptionEntry*> vsd;
1582 for (vector<VirtualSystemDescriptionEntry>::iterator it = m->maDescriptions.begin();
1583 it != m->maDescriptions.end();
1584 ++it)
1585 {
1586 if (it->type == aType)
1587 vsd.push_back(&(*it));
1588 }
1589
1590 return vsd;
1591}
1592
1593/* Private method; delete all records from the list
1594 * m->llDescriptions that match the given type.
1595 */
1596void VirtualSystemDescription::i_removeByType(VirtualSystemDescriptionType_T aType)
1597{
1598 std::vector<VirtualSystemDescriptionEntry>::iterator it = m->maDescriptions.begin();
1599 while (it != m->maDescriptions.end())
1600 {
1601 if (it->type == aType)
1602 it = m->maDescriptions.erase(it);
1603 else
1604 ++it;
1605 }
1606}
1607
1608/**
1609 * Private method; looks thru the member hardware items for the IDE, SATA, or SCSI controller with
1610 * the given reference ID. Useful when needing the controller for a particular
1611 * virtual disk.
1612 */
1613const VirtualSystemDescriptionEntry* VirtualSystemDescription::i_findControllerFromID(uint32_t id)
1614{
1615 Utf8Str strRef = Utf8StrFmt("%RI32", id);
1616 vector<VirtualSystemDescriptionEntry>::const_iterator it;
1617 for (it = m->maDescriptions.begin();
1618 it != m->maDescriptions.end();
1619 ++it)
1620 {
1621 const VirtualSystemDescriptionEntry &d = *it;
1622 switch (d.type)
1623 {
1624 case VirtualSystemDescriptionType_HardDiskControllerIDE:
1625 case VirtualSystemDescriptionType_HardDiskControllerSATA:
1626 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
1627 case VirtualSystemDescriptionType_HardDiskControllerSAS:
1628 if (d.strRef == strRef)
1629 return &d;
1630 break;
1631 default: break; /* Shut up MSC. */
1632 }
1633 }
1634
1635 return NULL;
1636}
1637
1638/**
1639 * Method called from Appliance::Interpret() if the source OVF for a virtual system
1640 * contains a <vbox:Machine> element. This method then attempts to parse that and
1641 * create a MachineConfigFile instance from it which is stored in this instance data
1642 * and can then be used to create a machine.
1643 *
1644 * This must only be called once per instance.
1645 *
1646 * This rethrows all XML and logic errors from MachineConfigFile.
1647 *
1648 * @param elmMachine <vbox:Machine> element with attributes and subelements from some
1649 * DOM tree.
1650 */
1651void VirtualSystemDescription::i_importVBoxMachineXML(const xml::ElementNode &elmMachine)
1652{
1653 settings::MachineConfigFile *pConfig = NULL;
1654
1655 Assert(m->pConfig == NULL);
1656
1657 try
1658 {
1659 pConfig = new settings::MachineConfigFile(NULL);
1660 pConfig->importMachineXML(elmMachine);
1661
1662 m->pConfig = pConfig;
1663 }
1664 catch (...)
1665 {
1666 if (pConfig)
1667 delete pConfig;
1668 throw;
1669 }
1670}
1671
1672/**
1673 * Returns the machine config created by importVBoxMachineXML() or NULL if there's none.
1674 */
1675const settings::MachineConfigFile* VirtualSystemDescription::i_getMachineConfig() const
1676{
1677 return m->pConfig;
1678}
1679
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