VirtualBox

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

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

Main/Appliance: use space instead of underscore as separator when adding a number for making the VM name unique, i.e. "imported VM 2" instead of "imported VM_2". We already use spaces a lot for similar purposes, having this different doesn't make sense to anyone but command line users who dislike filenames with spaces (and these are hard to avoid, when the default VM folder is "VirtualBox VMs").

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