VirtualBox

source: vbox/trunk/src/VBox/Main/ApplianceImpl.cpp@ 33061

Last change on this file since 33061 was 33060, checked in by vboxsync, 14 years ago

Main;OVF/OVA: online calculation of the SHA1 sum; directly stream into the ova (no temporary files anymore); cache writing

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 51.8 KB
Line 
1/* $Id: ApplianceImpl.cpp 33060 2010-10-12 12:17:49Z vboxsync $ */
2/** @file
3 *
4 * IAppliance and IVirtualSystem COM class implementations.
5 */
6
7/*
8 * Copyright (C) 2008-2010 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include <iprt/path.h>
20#include <iprt/cpp/utils.h>
21#include <iprt/tar.h>
22
23#include <VBox/com/array.h>
24
25#include "ApplianceImpl.h"
26#include "VFSExplorerImpl.h"
27#include "VirtualBoxImpl.h"
28#include "GuestOSTypeImpl.h"
29#include "ProgressImpl.h"
30#include "MachineImpl.h"
31
32#include "AutoCaller.h"
33#include "Logging.h"
34
35#include "ApplianceImplPrivate.h"
36
37using namespace std;
38
39////////////////////////////////////////////////////////////////////////////////
40//
41// Internal helpers
42//
43////////////////////////////////////////////////////////////////////////////////
44
45static const struct
46{
47 ovf::CIMOSType_T cim;
48 const char *pcszVbox;
49}
50g_osTypes[] =
51{
52 { ovf::CIMOSType_CIMOS_Unknown, SchemaDefs_OSTypeId_Other },
53 { ovf::CIMOSType_CIMOS_OS2, SchemaDefs_OSTypeId_OS2 },
54 { ovf::CIMOSType_CIMOS_OS2, SchemaDefs_OSTypeId_OS2Warp3 },
55 { ovf::CIMOSType_CIMOS_OS2, SchemaDefs_OSTypeId_OS2Warp4 },
56 { ovf::CIMOSType_CIMOS_OS2, SchemaDefs_OSTypeId_OS2Warp45 },
57 { ovf::CIMOSType_CIMOS_MSDOS, SchemaDefs_OSTypeId_DOS },
58 { ovf::CIMOSType_CIMOS_WIN3x, SchemaDefs_OSTypeId_Windows31 },
59 { ovf::CIMOSType_CIMOS_WIN95, SchemaDefs_OSTypeId_Windows95 },
60 { ovf::CIMOSType_CIMOS_WIN98, SchemaDefs_OSTypeId_Windows98 },
61 { ovf::CIMOSType_CIMOS_WINNT, SchemaDefs_OSTypeId_WindowsNT },
62 { ovf::CIMOSType_CIMOS_WINNT, SchemaDefs_OSTypeId_WindowsNT4 },
63 { ovf::CIMOSType_CIMOS_NetWare, SchemaDefs_OSTypeId_Netware },
64 { ovf::CIMOSType_CIMOS_NovellOES, SchemaDefs_OSTypeId_Netware },
65 { ovf::CIMOSType_CIMOS_Solaris, SchemaDefs_OSTypeId_Solaris },
66 { ovf::CIMOSType_CIMOS_SunOS, SchemaDefs_OSTypeId_Solaris },
67 { ovf::CIMOSType_CIMOS_FreeBSD, SchemaDefs_OSTypeId_FreeBSD },
68 { ovf::CIMOSType_CIMOS_NetBSD, SchemaDefs_OSTypeId_NetBSD },
69 { ovf::CIMOSType_CIMOS_QNX, SchemaDefs_OSTypeId_QNX },
70 { ovf::CIMOSType_CIMOS_Windows2000, SchemaDefs_OSTypeId_Windows2000 },
71 { ovf::CIMOSType_CIMOS_WindowsMe, SchemaDefs_OSTypeId_WindowsMe },
72 { ovf::CIMOSType_CIMOS_OpenBSD, SchemaDefs_OSTypeId_OpenBSD },
73 { ovf::CIMOSType_CIMOS_WindowsXP, SchemaDefs_OSTypeId_WindowsXP },
74 { ovf::CIMOSType_CIMOS_WindowsXPEmbedded, SchemaDefs_OSTypeId_WindowsXP },
75 { ovf::CIMOSType_CIMOS_WindowsEmbeddedforPointofService, SchemaDefs_OSTypeId_WindowsXP },
76 { ovf::CIMOSType_CIMOS_MicrosoftWindowsServer2003, SchemaDefs_OSTypeId_Windows2003 },
77 { ovf::CIMOSType_CIMOS_MicrosoftWindowsServer2003_64, SchemaDefs_OSTypeId_Windows2003_64 },
78 { ovf::CIMOSType_CIMOS_WindowsXP_64, SchemaDefs_OSTypeId_WindowsXP_64 },
79 { ovf::CIMOSType_CIMOS_WindowsVista, SchemaDefs_OSTypeId_WindowsVista },
80 { ovf::CIMOSType_CIMOS_WindowsVista_64, SchemaDefs_OSTypeId_WindowsVista_64 },
81 { ovf::CIMOSType_CIMOS_MicrosoftWindowsServer2008, SchemaDefs_OSTypeId_Windows2008 },
82 { ovf::CIMOSType_CIMOS_MicrosoftWindowsServer2008_64, SchemaDefs_OSTypeId_Windows2008_64 },
83 { ovf::CIMOSType_CIMOS_FreeBSD_64, SchemaDefs_OSTypeId_FreeBSD_64 },
84 { ovf::CIMOSType_CIMOS_MACOS, SchemaDefs_OSTypeId_MacOS },
85 { ovf::CIMOSType_CIMOS_MACOS, SchemaDefs_OSTypeId_MacOS_64 }, // there is no CIM 64-bit type for this
86
87 // Linuxes
88 { ovf::CIMOSType_CIMOS_RedHatEnterpriseLinux, SchemaDefs_OSTypeId_RedHat },
89 { ovf::CIMOSType_CIMOS_RedHatEnterpriseLinux_64, SchemaDefs_OSTypeId_RedHat_64 },
90 { ovf::CIMOSType_CIMOS_Solaris_64, SchemaDefs_OSTypeId_Solaris_64 },
91 { ovf::CIMOSType_CIMOS_SUSE, SchemaDefs_OSTypeId_OpenSUSE },
92 { ovf::CIMOSType_CIMOS_SLES, SchemaDefs_OSTypeId_OpenSUSE },
93 { ovf::CIMOSType_CIMOS_NovellLinuxDesktop, SchemaDefs_OSTypeId_OpenSUSE },
94 { ovf::CIMOSType_CIMOS_SUSE_64, SchemaDefs_OSTypeId_OpenSUSE_64 },
95 { ovf::CIMOSType_CIMOS_SLES_64, SchemaDefs_OSTypeId_OpenSUSE_64 },
96 { ovf::CIMOSType_CIMOS_LINUX, SchemaDefs_OSTypeId_Linux },
97 { ovf::CIMOSType_CIMOS_LINUX, SchemaDefs_OSTypeId_Linux22 },
98 { ovf::CIMOSType_CIMOS_SunJavaDesktopSystem, SchemaDefs_OSTypeId_Linux },
99 { ovf::CIMOSType_CIMOS_TurboLinux, SchemaDefs_OSTypeId_Turbolinux },
100 { ovf::CIMOSType_CIMOS_TurboLinux_64, SchemaDefs_OSTypeId_Turbolinux_64 },
101 { ovf::CIMOSType_CIMOS_Mandriva, SchemaDefs_OSTypeId_Mandriva },
102 { ovf::CIMOSType_CIMOS_Mandriva_64, SchemaDefs_OSTypeId_Mandriva_64 },
103 { ovf::CIMOSType_CIMOS_Ubuntu, SchemaDefs_OSTypeId_Ubuntu },
104 { ovf::CIMOSType_CIMOS_Ubuntu_64, SchemaDefs_OSTypeId_Ubuntu_64 },
105 { ovf::CIMOSType_CIMOS_Debian, SchemaDefs_OSTypeId_Debian },
106 { ovf::CIMOSType_CIMOS_Debian_64, SchemaDefs_OSTypeId_Debian_64 },
107 { ovf::CIMOSType_CIMOS_Linux_2_4_x, SchemaDefs_OSTypeId_Linux24 },
108 { ovf::CIMOSType_CIMOS_Linux_2_4_x_64, SchemaDefs_OSTypeId_Linux24_64 },
109 { ovf::CIMOSType_CIMOS_Linux_2_6_x, SchemaDefs_OSTypeId_Linux26 },
110 { ovf::CIMOSType_CIMOS_Linux_2_6_x_64, SchemaDefs_OSTypeId_Linux26_64 },
111 { ovf::CIMOSType_CIMOS_Linux_64, SchemaDefs_OSTypeId_Linux26_64 },
112
113 // types that we have support for but CIM doesnt
114 { ovf::CIMOSType_CIMOS_Linux_2_6_x, SchemaDefs_OSTypeId_ArchLinux },
115 { ovf::CIMOSType_CIMOS_Linux_2_6_x_64, SchemaDefs_OSTypeId_ArchLinux_64 },
116 { ovf::CIMOSType_CIMOS_Linux_2_6_x, SchemaDefs_OSTypeId_Fedora },
117 { ovf::CIMOSType_CIMOS_Linux_2_6_x_64, SchemaDefs_OSTypeId_Fedora_64 },
118 { ovf::CIMOSType_CIMOS_Linux_2_6_x, SchemaDefs_OSTypeId_Gentoo },
119 { ovf::CIMOSType_CIMOS_Linux_2_6_x_64, SchemaDefs_OSTypeId_Gentoo_64 },
120 { ovf::CIMOSType_CIMOS_Linux_2_6_x, SchemaDefs_OSTypeId_Xandros },
121 { ovf::CIMOSType_CIMOS_Linux_2_6_x_64, SchemaDefs_OSTypeId_Xandros_64 },
122 { ovf::CIMOSType_CIMOS_Solaris, SchemaDefs_OSTypeId_OpenSolaris },
123 { ovf::CIMOSType_CIMOS_Solaris_64, SchemaDefs_OSTypeId_OpenSolaris_64 },
124
125 // types added with CIM 2.25.0 follow:
126 { ovf::CIMOSType_CIMOS_WindowsServer2008R2, SchemaDefs_OSTypeId_Windows2008 }, // duplicate, see above
127// { ovf::CIMOSType_CIMOS_VMwareESXi = 104, // we can't run ESX in a VM
128 { ovf::CIMOSType_CIMOS_Windows7, SchemaDefs_OSTypeId_Windows7 },
129 { ovf::CIMOSType_CIMOS_Windows7, SchemaDefs_OSTypeId_Windows7_64 }, // there is no CIM 64-bit type for this
130 { ovf::CIMOSType_CIMOS_CentOS, SchemaDefs_OSTypeId_RedHat },
131 { ovf::CIMOSType_CIMOS_CentOS_64, SchemaDefs_OSTypeId_RedHat_64 },
132 { ovf::CIMOSType_CIMOS_OracleEnterpriseLinux, SchemaDefs_OSTypeId_Oracle },
133 { ovf::CIMOSType_CIMOS_OracleEnterpriseLinux_64, SchemaDefs_OSTypeId_Oracle_64 },
134 { ovf::CIMOSType_CIMOS_eComStation, SchemaDefs_OSTypeId_OS2eCS }
135
136 // there are no CIM types for these, so these turn to "other" on export:
137 // SchemaDefs_OSTypeId_OpenBSD
138 // SchemaDefs_OSTypeId_OpenBSD_64
139 // SchemaDefs_OSTypeId_NetBSD
140 // SchemaDefs_OSTypeId_NetBSD_64
141
142};
143
144/* Pattern structure for matching the OS type description field */
145struct osTypePattern
146{
147 const char *pcszPattern;
148 const char *pcszVbox;
149};
150
151/* These are the 32-Bit ones. They are sorted by priority. */
152static const osTypePattern g_osTypesPattern[] =
153{
154 {"Windows NT", SchemaDefs_OSTypeId_WindowsNT4},
155 {"Windows XP", SchemaDefs_OSTypeId_WindowsXP},
156 {"Windows 2000", SchemaDefs_OSTypeId_Windows2000},
157 {"Windows 2003", SchemaDefs_OSTypeId_Windows2003},
158 {"Windows Vista", SchemaDefs_OSTypeId_WindowsVista},
159 {"Windows 2008", SchemaDefs_OSTypeId_Windows2008},
160 {"SUSE", SchemaDefs_OSTypeId_OpenSUSE},
161 {"Novell", SchemaDefs_OSTypeId_OpenSUSE},
162 {"Red Hat", SchemaDefs_OSTypeId_RedHat},
163 {"Mandriva", SchemaDefs_OSTypeId_Mandriva},
164 {"Ubuntu", SchemaDefs_OSTypeId_Ubuntu},
165 {"Debian", SchemaDefs_OSTypeId_Debian},
166 {"QNX", SchemaDefs_OSTypeId_QNX},
167 {"Linux 2.4", SchemaDefs_OSTypeId_Linux24},
168 {"Linux 2.6", SchemaDefs_OSTypeId_Linux26},
169 {"Linux", SchemaDefs_OSTypeId_Linux},
170 {"OpenSolaris", SchemaDefs_OSTypeId_OpenSolaris},
171 {"Solaris", SchemaDefs_OSTypeId_OpenSolaris},
172 {"FreeBSD", SchemaDefs_OSTypeId_FreeBSD},
173 {"NetBSD", SchemaDefs_OSTypeId_NetBSD},
174 {"Windows 95", SchemaDefs_OSTypeId_Windows95},
175 {"Windows 98", SchemaDefs_OSTypeId_Windows98},
176 {"Windows Me", SchemaDefs_OSTypeId_WindowsMe},
177 {"Windows 3.", SchemaDefs_OSTypeId_Windows31},
178 {"DOS", SchemaDefs_OSTypeId_DOS},
179 {"OS2", SchemaDefs_OSTypeId_OS2}
180};
181
182/* These are the 64-Bit ones. They are sorted by priority. */
183static const osTypePattern g_osTypesPattern64[] =
184{
185 {"Windows XP", SchemaDefs_OSTypeId_WindowsXP_64},
186 {"Windows 2003", SchemaDefs_OSTypeId_Windows2003_64},
187 {"Windows Vista", SchemaDefs_OSTypeId_WindowsVista_64},
188 {"Windows 2008", SchemaDefs_OSTypeId_Windows2008_64},
189 {"SUSE", SchemaDefs_OSTypeId_OpenSUSE_64},
190 {"Novell", SchemaDefs_OSTypeId_OpenSUSE_64},
191 {"Red Hat", SchemaDefs_OSTypeId_RedHat_64},
192 {"Mandriva", SchemaDefs_OSTypeId_Mandriva_64},
193 {"Ubuntu", SchemaDefs_OSTypeId_Ubuntu_64},
194 {"Debian", SchemaDefs_OSTypeId_Debian_64},
195 {"Linux 2.4", SchemaDefs_OSTypeId_Linux24_64},
196 {"Linux 2.6", SchemaDefs_OSTypeId_Linux26_64},
197 {"Linux", SchemaDefs_OSTypeId_Linux26_64},
198 {"OpenSolaris", SchemaDefs_OSTypeId_OpenSolaris_64},
199 {"Solaris", SchemaDefs_OSTypeId_OpenSolaris_64},
200 {"FreeBSD", SchemaDefs_OSTypeId_FreeBSD_64},
201};
202
203/**
204 * Private helper func that suggests a VirtualBox guest OS type
205 * for the given OVF operating system type.
206 * @param osTypeVBox
207 * @param c
208 * @param cStr
209 */
210void convertCIMOSType2VBoxOSType(Utf8Str &strType, ovf::CIMOSType_T c, const Utf8Str &cStr)
211{
212 /* First check if the type is other/other_64 */
213 if (c == ovf::CIMOSType_CIMOS_Other)
214 {
215 for (size_t i=0; i < RT_ELEMENTS(g_osTypesPattern); ++i)
216 if (cStr.contains (g_osTypesPattern[i].pcszPattern, Utf8Str::CaseInsensitive))
217 {
218 strType = g_osTypesPattern[i].pcszVbox;
219 return;
220 }
221 }
222 else if (c == ovf::CIMOSType_CIMOS_Other_64)
223 {
224 for (size_t i=0; i < RT_ELEMENTS(g_osTypesPattern64); ++i)
225 if (cStr.contains (g_osTypesPattern64[i].pcszPattern, Utf8Str::CaseInsensitive))
226 {
227 strType = g_osTypesPattern64[i].pcszVbox;
228 return;
229 }
230 }
231
232 for (size_t i = 0; i < RT_ELEMENTS(g_osTypes); ++i)
233 {
234 if (c == g_osTypes[i].cim)
235 {
236 strType = g_osTypes[i].pcszVbox;
237 return;
238 }
239 }
240
241 strType = SchemaDefs_OSTypeId_Other;
242}
243
244/**
245 * Private helper func that suggests a VirtualBox guest OS type
246 * for the given OVF operating system type.
247 * @param osTypeVBox
248 * @param c
249 */
250ovf::CIMOSType_T convertVBoxOSType2CIMOSType(const char *pcszVbox)
251{
252 for (size_t i = 0; i < RT_ELEMENTS(g_osTypes); ++i)
253 {
254 if (!RTStrICmp(pcszVbox, g_osTypes[i].pcszVbox))
255 return g_osTypes[i].cim;
256 }
257
258 return ovf::CIMOSType_CIMOS_Other;
259}
260
261////////////////////////////////////////////////////////////////////////////////
262//
263// IVirtualBox public methods
264//
265////////////////////////////////////////////////////////////////////////////////
266
267// This code is here so we won't have to include the appliance headers in the
268// IVirtualBox implementation.
269
270/**
271 * Implementation for IVirtualBox::createAppliance.
272 *
273 * @param anAppliance IAppliance object created if S_OK is returned.
274 * @return S_OK or error.
275 */
276STDMETHODIMP VirtualBox::CreateAppliance(IAppliance** anAppliance)
277{
278 HRESULT rc;
279
280 ComObjPtr<Appliance> appliance;
281 appliance.createObject();
282 rc = appliance->init(this);
283
284 if (SUCCEEDED(rc))
285 appliance.queryInterfaceTo(anAppliance);
286
287 return rc;
288}
289
290////////////////////////////////////////////////////////////////////////////////
291//
292// Appliance constructor / destructor
293//
294////////////////////////////////////////////////////////////////////////////////
295
296Appliance::Appliance()
297 : mVirtualBox(NULL)
298{
299}
300
301Appliance::~Appliance()
302{
303}
304
305/**
306 * Appliance COM initializer.
307 * @param
308 * @return
309 */
310HRESULT Appliance::init(VirtualBox *aVirtualBox)
311{
312 /* Enclose the state transition NotReady->InInit->Ready */
313 AutoInitSpan autoInitSpan(this);
314 AssertReturn(autoInitSpan.isOk(), E_FAIL);
315
316 /* Weak reference to a VirtualBox object */
317 unconst(mVirtualBox) = aVirtualBox;
318
319 // initialize data
320 m = new Data;
321
322 /* Confirm a successful initialization */
323 autoInitSpan.setSucceeded();
324
325 return S_OK;
326}
327
328/**
329 * Appliance COM uninitializer.
330 * @return
331 */
332void Appliance::uninit()
333{
334 /* Enclose the state transition Ready->InUninit->NotReady */
335 AutoUninitSpan autoUninitSpan(this);
336 if (autoUninitSpan.uninitDone())
337 return;
338
339 delete m;
340 m = NULL;
341}
342
343////////////////////////////////////////////////////////////////////////////////
344//
345// IAppliance public methods
346//
347////////////////////////////////////////////////////////////////////////////////
348
349/**
350 * Public method implementation.
351 * @param
352 * @return
353 */
354STDMETHODIMP Appliance::COMGETTER(Path)(BSTR *aPath)
355{
356 if (!aPath)
357 return E_POINTER;
358
359 AutoCaller autoCaller(this);
360 if (FAILED(autoCaller.rc())) return autoCaller.rc();
361
362 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
363
364 if (!isApplianceIdle())
365 return E_ACCESSDENIED;
366
367 Bstr bstrPath(m->locInfo.strPath);
368 bstrPath.cloneTo(aPath);
369
370 return S_OK;
371}
372
373/**
374 * Public method implementation.
375 * @param
376 * @return
377 */
378STDMETHODIMP Appliance::COMGETTER(Disks)(ComSafeArrayOut(BSTR, aDisks))
379{
380 CheckComArgOutSafeArrayPointerValid(aDisks);
381
382 AutoCaller autoCaller(this);
383 if (FAILED(autoCaller.rc())) return autoCaller.rc();
384
385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
386
387 if (!isApplianceIdle())
388 return E_ACCESSDENIED;
389
390 if (m->pReader) // OVFReader instantiated?
391 {
392 size_t c = m->pReader->m_mapDisks.size();
393 com::SafeArray<BSTR> sfaDisks(c);
394
395 ovf::DiskImagesMap::const_iterator it;
396 size_t i = 0;
397 for (it = m->pReader->m_mapDisks.begin();
398 it != m->pReader->m_mapDisks.end();
399 ++it, ++i)
400 {
401 // create a string representing this disk
402 const ovf::DiskImage &d = it->second;
403 char *psz = NULL;
404 RTStrAPrintf(&psz,
405 "%s\t"
406 "%RI64\t"
407 "%RI64\t"
408 "%s\t"
409 "%s\t"
410 "%RI64\t"
411 "%RI64\t"
412 "%s",
413 d.strDiskId.c_str(),
414 d.iCapacity,
415 d.iPopulatedSize,
416 d.strFormat.c_str(),
417 d.strHref.c_str(),
418 d.iSize,
419 d.iChunkSize,
420 d.strCompression.c_str());
421 Utf8Str utf(psz);
422 Bstr bstr(utf);
423 // push to safearray
424 bstr.cloneTo(&sfaDisks[i]);
425 RTStrFree(psz);
426 }
427
428 sfaDisks.detachTo(ComSafeArrayOutArg(aDisks));
429 }
430
431 return S_OK;
432}
433
434/**
435 * Public method implementation.
436 * @param
437 * @return
438 */
439STDMETHODIMP Appliance::COMGETTER(VirtualSystemDescriptions)(ComSafeArrayOut(IVirtualSystemDescription*, aVirtualSystemDescriptions))
440{
441 CheckComArgOutSafeArrayPointerValid(aVirtualSystemDescriptions);
442
443 AutoCaller autoCaller(this);
444 if (FAILED(autoCaller.rc())) return autoCaller.rc();
445
446 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
447
448 if (!isApplianceIdle())
449 return E_ACCESSDENIED;
450
451 SafeIfaceArray<IVirtualSystemDescription> sfaVSD(m->virtualSystemDescriptions);
452 sfaVSD.detachTo(ComSafeArrayOutArg(aVirtualSystemDescriptions));
453
454 return S_OK;
455}
456
457/**
458 * Public method implementation.
459 * @param aDisks
460 * @return
461 */
462STDMETHODIMP Appliance::COMGETTER(Machines)(ComSafeArrayOut(BSTR, aMachines))
463{
464 CheckComArgOutSafeArrayPointerValid(aMachines);
465
466 AutoCaller autoCaller(this);
467 if (FAILED(autoCaller.rc())) return autoCaller.rc();
468
469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
470
471 if (!isApplianceIdle())
472 return E_ACCESSDENIED;
473
474 com::SafeArray<BSTR> sfaMachines(m->llGuidsMachinesCreated.size());
475 size_t u = 0;
476 for (std::list<Guid>::const_iterator it = m->llGuidsMachinesCreated.begin();
477 it != m->llGuidsMachinesCreated.end();
478 ++it)
479 {
480 const Guid &uuid = *it;
481 Bstr bstr(uuid.toUtf16());
482 bstr.detachTo(&sfaMachines[u]);
483 ++u;
484 }
485
486 sfaMachines.detachTo(ComSafeArrayOutArg(aMachines));
487
488 return S_OK;
489}
490
491STDMETHODIMP Appliance::CreateVFSExplorer(IN_BSTR aURI, IVFSExplorer **aExplorer)
492{
493 CheckComArgOutPointerValid(aExplorer);
494
495 AutoCaller autoCaller(this);
496 if (FAILED(autoCaller.rc())) return autoCaller.rc();
497
498 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
499
500 ComObjPtr<VFSExplorer> explorer;
501 HRESULT rc = S_OK;
502 try
503 {
504 Utf8Str uri(aURI);
505 /* Check which kind of export the user has requested */
506 LocationInfo li;
507 parseURI(uri, li);
508 /* Create the explorer object */
509 explorer.createObject();
510 rc = explorer->init(li.storageType, li.strPath, li.strHostname, li.strUsername, li.strPassword, mVirtualBox);
511 }
512 catch (HRESULT aRC)
513 {
514 rc = aRC;
515 }
516
517 if (SUCCEEDED(rc))
518 /* Return explorer to the caller */
519 explorer.queryInterfaceTo(aExplorer);
520
521 return rc;
522}
523
524/**
525* Public method implementation.
526 * @return
527 */
528STDMETHODIMP Appliance::GetWarnings(ComSafeArrayOut(BSTR, aWarnings))
529{
530 if (ComSafeArrayOutIsNull(aWarnings))
531 return E_POINTER;
532
533 AutoCaller autoCaller(this);
534 if (FAILED(autoCaller.rc())) return autoCaller.rc();
535
536 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
537
538 com::SafeArray<BSTR> sfaWarnings(m->llWarnings.size());
539
540 list<Utf8Str>::const_iterator it;
541 size_t i = 0;
542 for (it = m->llWarnings.begin();
543 it != m->llWarnings.end();
544 ++it, ++i)
545 {
546 Bstr bstr = *it;
547 bstr.cloneTo(&sfaWarnings[i]);
548 }
549
550 sfaWarnings.detachTo(ComSafeArrayOutArg(aWarnings));
551
552 return S_OK;
553}
554
555////////////////////////////////////////////////////////////////////////////////
556//
557// Appliance private methods
558//
559////////////////////////////////////////////////////////////////////////////////
560
561/**
562 * Returns true if the appliance is in "idle" state. This should always be the
563 * case unless an import or export is currently in progress. Similar to machine
564 * states, this permits the Appliance implementation code to let go of the
565 * Appliance object lock while a time-consuming disk conversion is in progress
566 * without exposing the appliance to conflicting calls.
567 *
568 * This sets an error on "this" (the appliance) and returns false if the appliance
569 * is busy. The caller should then return E_ACCESSDENIED.
570 *
571 * Must be called from under the object lock!
572 *
573 * @return
574 */
575bool Appliance::isApplianceIdle()
576{
577 if (m->state == Data::ApplianceImporting)
578 setError(VBOX_E_INVALID_OBJECT_STATE, tr("The appliance is busy importing files"));
579 else if (m->state == Data::ApplianceExporting)
580 setError(VBOX_E_INVALID_OBJECT_STATE, tr("The appliance is busy exporting files"));
581 else
582 return true;
583
584 return false;
585}
586
587HRESULT Appliance::searchUniqueVMName(Utf8Str& aName) const
588{
589 IMachine *machine = NULL;
590 char *tmpName = RTStrDup(aName.c_str());
591 int i = 1;
592 /* @todo: Maybe too cost-intensive; try to find a lighter way */
593 while (mVirtualBox->FindMachine(Bstr(tmpName).raw(), &machine) != VBOX_E_OBJECT_NOT_FOUND)
594 {
595 RTStrFree(tmpName);
596 RTStrAPrintf(&tmpName, "%s_%d", aName.c_str(), i);
597 ++i;
598 }
599 aName = tmpName;
600 RTStrFree(tmpName);
601
602 return S_OK;
603}
604
605HRESULT Appliance::searchUniqueDiskImageFilePath(Utf8Str& aName) const
606{
607 IMedium *harddisk = NULL;
608 char *tmpName = RTStrDup(aName.c_str());
609 int i = 1;
610 /* Check if the file exists or if a file with this path is registered
611 * already */
612 /* @todo: Maybe too cost-intensive; try to find a lighter way */
613 while ( RTPathExists(tmpName)
614 || mVirtualBox->FindMedium(Bstr(tmpName).raw(), DeviceType_HardDisk, &harddisk) != VBOX_E_OBJECT_NOT_FOUND
615 )
616 {
617 RTStrFree(tmpName);
618 char *tmpDir = RTStrDup(aName.c_str());
619 RTPathStripFilename(tmpDir);;
620 char *tmpFile = RTStrDup(RTPathFilename(aName.c_str()));
621 RTPathStripExt(tmpFile);
622 const char *tmpExt = RTPathExt(aName.c_str());
623 RTStrAPrintf(&tmpName, "%s%c%s_%d%s", tmpDir, RTPATH_DELIMITER, tmpFile, i, tmpExt);
624 RTStrFree(tmpFile);
625 RTStrFree(tmpDir);
626 ++i;
627 }
628 aName = tmpName;
629 RTStrFree(tmpName);
630
631 return S_OK;
632}
633
634/**
635 * Little shortcut to SystemProperties::DefaultHardDiskFolder.
636 * @param str
637 * @return
638 */
639HRESULT Appliance::getDefaultHardDiskFolder(Utf8Str &str) const
640{
641 /* We need the default path for storing disk images */
642 ComPtr<ISystemProperties> systemProps;
643 HRESULT rc = mVirtualBox->COMGETTER(SystemProperties)(systemProps.asOutParam());
644 if (FAILED(rc)) return rc;
645 Bstr bstrDefaultHardDiskFolder;
646 rc = systemProps->COMGETTER(DefaultHardDiskFolder)(bstrDefaultHardDiskFolder.asOutParam());
647 if (FAILED(rc)) return rc;
648 str = bstrDefaultHardDiskFolder;
649
650 return S_OK;
651}
652
653/**
654 * Called from the import and export background threads to synchronize the second
655 * background disk thread's progress object with the current progress object so
656 * that the user interface sees progress correctly and that cancel signals are
657 * passed on to the second thread.
658 * @param pProgressThis Progress object of the current thread.
659 * @param pProgressAsync Progress object of asynchronous task running in background.
660 */
661void Appliance::waitForAsyncProgress(ComObjPtr<Progress> &pProgressThis,
662 ComPtr<IProgress> &pProgressAsync)
663{
664 HRESULT rc;
665
666 // now loop until the asynchronous operation completes and then report its result
667 BOOL fCompleted;
668 BOOL fCanceled;
669 ULONG currentPercent;
670 ULONG cOp = 0;
671 while (SUCCEEDED(pProgressAsync->COMGETTER(Completed(&fCompleted))))
672 {
673 rc = pProgressThis->COMGETTER(Canceled)(&fCanceled);
674 if (FAILED(rc)) throw rc;
675 if (fCanceled)
676 {
677 pProgressAsync->Cancel();
678 break;
679 }
680 /* Check if the current operation has changed. It is also possible
681 that in the meantime more than one async operation was finished. So
682 we have to loop as long as we reached the same operation count. */
683 ULONG curOp;
684 for(;;)
685 {
686 rc = pProgressAsync->COMGETTER(Operation(&curOp));
687 if (FAILED(rc)) throw rc;
688 if (cOp != curOp)
689 {
690 Bstr bstr;
691 ULONG currentWeight;
692 rc = pProgressAsync->COMGETTER(OperationDescription(bstr.asOutParam()));
693 if (FAILED(rc)) throw rc;
694 rc = pProgressAsync->COMGETTER(OperationWeight(&currentWeight));
695 if (FAILED(rc)) throw rc;
696 rc = pProgressThis->SetNextOperation(bstr.raw(), currentWeight);
697 if (FAILED(rc)) throw rc;
698 ++cOp;
699 }else
700 break;
701 }
702
703 rc = pProgressAsync->COMGETTER(OperationPercent(&currentPercent));
704 if (FAILED(rc)) throw rc;
705 pProgressThis->SetCurrentOperationProgress(currentPercent);
706 if (fCompleted)
707 break;
708
709 /* Make sure the loop is not too tight */
710 rc = pProgressAsync->WaitForCompletion(100);
711 if (FAILED(rc)) throw rc;
712 }
713 // report result of asynchronous operation
714 LONG iRc;
715 rc = pProgressAsync->COMGETTER(ResultCode)(&iRc);
716 if (FAILED(rc)) throw rc;
717
718
719 // if the thread of the progress object has an error, then
720 // retrieve the error info from there, or it'll be lost
721 if (FAILED(iRc))
722 {
723 ProgressErrorInfo info(pProgressAsync);
724 Utf8Str str(info.getText());
725 const char *pcsz = str.c_str();
726 HRESULT rc2 = setError(iRc, pcsz);
727 throw rc2;
728 }
729}
730
731void Appliance::addWarning(const char* aWarning, ...)
732{
733 va_list args;
734 va_start(args, aWarning);
735 Utf8StrFmtVA str(aWarning, args);
736 va_end(args);
737 m->llWarnings.push_back(str);
738}
739
740/**
741 * Refreshes the cDisks and ulTotalDisksMB members in the instance data.
742 * Requires that virtual system descriptions are present.
743 */
744void Appliance::disksWeight()
745{
746 m->ulTotalDisksMB = 0;
747 m->cDisks = 0;
748 // weigh the disk images according to their sizes
749 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
750 for (it = m->virtualSystemDescriptions.begin();
751 it != m->virtualSystemDescriptions.end();
752 ++it)
753 {
754 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
755 /* One for every hard disk of the Virtual System */
756 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
757 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
758 for (itH = avsdeHDs.begin();
759 itH != avsdeHDs.end();
760 ++itH)
761 {
762 const VirtualSystemDescriptionEntry *pHD = *itH;
763 m->ulTotalDisksMB += pHD->ulSizeMB;
764 ++m->cDisks;
765 }
766 }
767
768}
769
770/**
771 * Called from Appliance::importImpl() and Appliance::writeImpl() to set up a
772 * progress object with the proper weights and maximum progress values.
773 *
774 * @param pProgress
775 * @param bstrDescription
776 * @param mode
777 * @return
778 */
779HRESULT Appliance::setUpProgress(const LocationInfo &locInfo,
780 ComObjPtr<Progress> &pProgress,
781 const Bstr &bstrDescription,
782 SetUpProgressMode mode)
783{
784 HRESULT rc;
785
786 /* Create the progress object */
787 pProgress.createObject();
788
789 // compute the disks weight (this sets ulTotalDisksMB and cDisks in the instance data)
790 disksWeight();
791
792 m->ulWeightForManifestOperation = 0;
793
794 ULONG cOperations;
795 ULONG ulTotalOperationsWeight;
796
797 cOperations = 1 // one for XML setup
798 + m->cDisks; // plus one per disk
799 if (m->ulTotalDisksMB)
800 {
801 m->ulWeightForXmlOperation = (ULONG)((double)m->ulTotalDisksMB * 1 / 100); // use 1% of the progress for the XML
802 ulTotalOperationsWeight = m->ulTotalDisksMB + m->ulWeightForXmlOperation;
803 }
804 else
805 {
806 // no disks to export:
807 m->ulWeightForXmlOperation = 1;
808 ulTotalOperationsWeight = 1;
809 }
810
811 bool fOVA = locInfo.strPath.endsWith(".ova", Utf8Str::CaseInsensitive);
812 switch (mode)
813 {
814 case ImportFileNoManifest:
815 {
816 if (fOVA)
817 {
818 // Another operation for packing
819 ++cOperations;
820
821 // assume that packing the files into the archive has the same weight than creating all files in the ovf exporting step
822 ulTotalOperationsWeight += m->ulTotalDisksMB;
823 }
824 break;
825 }
826 case ImportFileWithManifest:
827 {
828 ++cOperations; // another one for creating the manifest
829
830 // assume that creating the manifest will take 10% of the time it takes to export the disks
831 m->ulWeightForManifestOperation = m->ulTotalDisksMB / 10;
832 ulTotalOperationsWeight += m->ulWeightForManifestOperation;
833 if (fOVA)
834 {
835 // Another operation for packing
836 ++cOperations;
837
838 // assume that packing the files into the archive has the same weight than creating all files in the ovf exporting step
839 ulTotalOperationsWeight += m->ulTotalDisksMB;
840 }
841 break;
842 }
843 case WriteFile:
844 {
845 // assume that creating the manifest will take .1% of the time it takes to export the disks
846 if (m->fManifest)
847 {
848 ++cOperations; // another one for creating the manifest
849
850 m->ulWeightForManifestOperation = (ULONG)((double)m->ulTotalDisksMB * .1 / 100); // use .5% of the progress for the manifest
851 ulTotalOperationsWeight += m->ulWeightForManifestOperation;
852 }
853 break;
854 }
855 case ImportS3:
856 {
857 cOperations += 1 + 1; // another one for the manifest file & another one for the import
858 ulTotalOperationsWeight = m->ulTotalDisksMB;
859 if (!m->ulTotalDisksMB)
860 // no disks to export:
861 ulTotalOperationsWeight = 1;
862
863 ULONG ulImportWeight = (ULONG)((double)ulTotalOperationsWeight * 50 / 100); // use 50% for import
864 ulTotalOperationsWeight += ulImportWeight;
865
866 m->ulWeightForXmlOperation = ulImportWeight; /* save for using later */
867
868 ULONG ulInitWeight = (ULONG)((double)ulTotalOperationsWeight * 0.1 / 100); // use 0.1% for init
869 ulTotalOperationsWeight += ulInitWeight;
870 break;
871 }
872 case WriteS3:
873 {
874 cOperations += 1 + 1; // another one for the mf & another one for temporary creation
875
876 if (m->ulTotalDisksMB)
877 {
878 m->ulWeightForXmlOperation = (ULONG)((double)m->ulTotalDisksMB * 1 / 100); // use 1% of the progress for OVF file upload (we didn't know the size at this point)
879 ulTotalOperationsWeight = m->ulTotalDisksMB + m->ulWeightForXmlOperation;
880 }
881 else
882 {
883 // no disks to export:
884 ulTotalOperationsWeight = 1;
885 m->ulWeightForXmlOperation = 1;
886 }
887 ULONG ulOVFCreationWeight = (ULONG)((double)ulTotalOperationsWeight * 50.0 / 100.0); /* Use 50% for the creation of the OVF & the disks */
888 ulTotalOperationsWeight += ulOVFCreationWeight;
889 break;
890 }
891 }
892
893 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightForXmlOperation = %d\n",
894 m->ulTotalDisksMB, m->cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightForXmlOperation));
895
896 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
897 bstrDescription.raw(),
898 TRUE /* aCancelable */,
899 cOperations, // ULONG cOperations,
900 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight,
901 bstrDescription.raw(), // CBSTR bstrFirstOperationDescription,
902 m->ulWeightForXmlOperation); // ULONG ulFirstOperationWeight,
903 return rc;
904}
905
906void Appliance::parseURI(Utf8Str strUri, LocationInfo &locInfo) const
907{
908 /* Check the URI for the protocol */
909 if (strUri.startsWith("file://", Utf8Str::CaseInsensitive)) /* File based */
910 {
911 locInfo.storageType = VFSType_File;
912 strUri = strUri.substr(sizeof("file://") - 1);
913 }
914 else if (strUri.startsWith("SunCloud://", Utf8Str::CaseInsensitive)) /* Sun Cloud service */
915 {
916 locInfo.storageType = VFSType_S3;
917 strUri = strUri.substr(sizeof("SunCloud://") - 1);
918 }
919 else if (strUri.startsWith("S3://", Utf8Str::CaseInsensitive)) /* S3 service */
920 {
921 locInfo.storageType = VFSType_S3;
922 strUri = strUri.substr(sizeof("S3://") - 1);
923 }
924 else if (strUri.startsWith("webdav://", Utf8Str::CaseInsensitive)) /* webdav service */
925 throw E_NOTIMPL;
926
927 /* Not necessary on a file based URI */
928 if (locInfo.storageType != VFSType_File)
929 {
930 size_t uppos = strUri.find("@"); /* username:password combo */
931 if (uppos != Utf8Str::npos)
932 {
933 locInfo.strUsername = strUri.substr(0, uppos);
934 strUri = strUri.substr(uppos + 1);
935 size_t upos = locInfo.strUsername.find(":");
936 if (upos != Utf8Str::npos)
937 {
938 locInfo.strPassword = locInfo.strUsername.substr(upos + 1);
939 locInfo.strUsername = locInfo.strUsername.substr(0, upos);
940 }
941 }
942 size_t hpos = strUri.find("/"); /* hostname part */
943 if (hpos != Utf8Str::npos)
944 {
945 locInfo.strHostname = strUri.substr(0, hpos);
946 strUri = strUri.substr(hpos);
947 }
948 }
949
950 locInfo.strPath = strUri;
951}
952
953void Appliance::parseBucket(Utf8Str &aPath, Utf8Str &aBucket)
954{
955 /* Buckets are S3 specific. So parse the bucket out of the file path */
956 if (!aPath.startsWith("/"))
957 throw setError(E_INVALIDARG,
958 tr("The path '%s' must start with /"), aPath.c_str());
959 size_t bpos = aPath.find("/", 1);
960 if (bpos != Utf8Str::npos)
961 {
962 aBucket = aPath.substr(1, bpos - 1); /* The bucket without any slashes */
963 aPath = aPath.substr(bpos); /* The rest of the file path */
964 }
965 /* If there is no bucket name provided reject it */
966 if (aBucket.isEmpty())
967 throw setError(E_INVALIDARG,
968 tr("You doesn't provide a bucket name in the URI '%s'"), aPath.c_str());
969}
970
971Utf8Str Appliance::manifestFileName(const Utf8Str& aPath) const
972{
973 Utf8Str strTmpPath = aPath;
974 /* Get the name part */
975 char *pszMfName = RTStrDup(RTPathFilename(strTmpPath.c_str()));
976 /* Strip any extensions */
977 RTPathStripExt(pszMfName);
978 /* Path without the filename */
979 strTmpPath.stripFilename();
980 /* Format the manifest path */
981 Utf8StrFmt strMfFile("%s/%s.mf", strTmpPath.c_str(), pszMfName);
982 RTStrFree(pszMfName);
983 return strMfFile;
984}
985
986/**
987 *
988 * @return
989 */
990int Appliance::TaskOVF::startThread()
991{
992 int vrc = RTThreadCreate(NULL, Appliance::taskThreadImportOrExport, this,
993 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
994 "Appliance::Task");
995
996 if (RT_FAILURE(vrc))
997 return Appliance::setErrorStatic(E_FAIL,
998 Utf8StrFmt("Could not create OVF task thread (%Rrc)\n", vrc));
999
1000 return S_OK;
1001}
1002
1003/**
1004 * Thread function for the thread started in Appliance::readImpl() and Appliance::importImpl()
1005 * and Appliance::writeImpl().
1006 * This will in turn call Appliance::readFS() or Appliance::readS3() or Appliance::importFS()
1007 * or Appliance::importS3() or Appliance::writeFS() or Appliance::writeS3().
1008 *
1009 * @param aThread
1010 * @param pvUser
1011 */
1012/* static */
1013DECLCALLBACK(int) Appliance::taskThreadImportOrExport(RTTHREAD /* aThread */, void *pvUser)
1014{
1015 std::auto_ptr<TaskOVF> task(static_cast<TaskOVF*>(pvUser));
1016 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
1017
1018 Appliance *pAppliance = task->pAppliance;
1019
1020 LogFlowFuncEnter();
1021 LogFlowFunc(("Appliance %p\n", pAppliance));
1022
1023 HRESULT taskrc = S_OK;
1024
1025 switch (task->taskType)
1026 {
1027 case TaskOVF::Read:
1028 if (task->locInfo.storageType == VFSType_File)
1029 taskrc = pAppliance->readFS(task->locInfo, task->pProgress);
1030 else if (task->locInfo.storageType == VFSType_S3)
1031 taskrc = pAppliance->readS3(task.get());
1032 break;
1033
1034 case TaskOVF::Import:
1035 if (task->locInfo.storageType == VFSType_File)
1036 taskrc = pAppliance->importFS(task.get());
1037 else if (task->locInfo.storageType == VFSType_S3)
1038 taskrc = pAppliance->importS3(task.get());
1039 break;
1040
1041 case TaskOVF::Write:
1042 if (task->locInfo.storageType == VFSType_File)
1043 taskrc = pAppliance->writeFS(task.get());
1044 else if (task->locInfo.storageType == VFSType_S3)
1045 taskrc = pAppliance->writeS3(task.get());
1046 break;
1047 }
1048
1049 task->rc = taskrc;
1050
1051 if (!task->pProgress.isNull())
1052 task->pProgress->notifyComplete(taskrc);
1053
1054 LogFlowFuncLeave();
1055
1056 return VINF_SUCCESS;
1057}
1058
1059/* static */
1060int Appliance::TaskOVF::updateProgress(unsigned uPercent, void *pvUser)
1061{
1062 Appliance::TaskOVF* pTask = *(Appliance::TaskOVF**)pvUser;
1063
1064 if ( pTask
1065 && !pTask->pProgress.isNull())
1066 {
1067 BOOL fCanceled;
1068 pTask->pProgress->COMGETTER(Canceled)(&fCanceled);
1069 if (fCanceled)
1070 return -1;
1071 pTask->pProgress->SetCurrentOperationProgress(uPercent);
1072 }
1073 return VINF_SUCCESS;
1074}
1075
1076////////////////////////////////////////////////////////////////////////////////
1077//
1078// IVirtualSystemDescription constructor / destructor
1079//
1080////////////////////////////////////////////////////////////////////////////////
1081
1082DEFINE_EMPTY_CTOR_DTOR(VirtualSystemDescription)
1083
1084/**
1085 * COM initializer.
1086 * @return
1087 */
1088HRESULT VirtualSystemDescription::init()
1089{
1090 /* Enclose the state transition NotReady->InInit->Ready */
1091 AutoInitSpan autoInitSpan(this);
1092 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1093
1094 /* Initialize data */
1095 m = new Data();
1096 m->pConfig = NULL;
1097
1098 /* Confirm a successful initialization */
1099 autoInitSpan.setSucceeded();
1100 return S_OK;
1101}
1102
1103/**
1104* COM uninitializer.
1105*/
1106
1107void VirtualSystemDescription::uninit()
1108{
1109 if (m->pConfig)
1110 delete m->pConfig;
1111 delete m;
1112 m = NULL;
1113}
1114
1115////////////////////////////////////////////////////////////////////////////////
1116//
1117// IVirtualSystemDescription public methods
1118//
1119////////////////////////////////////////////////////////////////////////////////
1120
1121/**
1122 * Public method implementation.
1123 * @param
1124 * @return
1125 */
1126STDMETHODIMP VirtualSystemDescription::COMGETTER(Count)(ULONG *aCount)
1127{
1128 if (!aCount)
1129 return E_POINTER;
1130
1131 AutoCaller autoCaller(this);
1132 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1133
1134 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1135
1136 *aCount = (ULONG)m->llDescriptions.size();
1137
1138 return S_OK;
1139}
1140
1141/**
1142 * Public method implementation.
1143 * @return
1144 */
1145STDMETHODIMP VirtualSystemDescription::GetDescription(ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),
1146 ComSafeArrayOut(BSTR, aRefs),
1147 ComSafeArrayOut(BSTR, aOrigValues),
1148 ComSafeArrayOut(BSTR, aVboxValues),
1149 ComSafeArrayOut(BSTR, aExtraConfigValues))
1150{
1151 if (ComSafeArrayOutIsNull(aTypes) ||
1152 ComSafeArrayOutIsNull(aRefs) ||
1153 ComSafeArrayOutIsNull(aOrigValues) ||
1154 ComSafeArrayOutIsNull(aVboxValues) ||
1155 ComSafeArrayOutIsNull(aExtraConfigValues))
1156 return E_POINTER;
1157
1158 AutoCaller autoCaller(this);
1159 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1160
1161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1162
1163 ULONG c = (ULONG)m->llDescriptions.size();
1164 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);
1165 com::SafeArray<BSTR> sfaRefs(c);
1166 com::SafeArray<BSTR> sfaOrigValues(c);
1167 com::SafeArray<BSTR> sfaVboxValues(c);
1168 com::SafeArray<BSTR> sfaExtraConfigValues(c);
1169
1170 list<VirtualSystemDescriptionEntry>::const_iterator it;
1171 size_t i = 0;
1172 for (it = m->llDescriptions.begin();
1173 it != m->llDescriptions.end();
1174 ++it, ++i)
1175 {
1176 const VirtualSystemDescriptionEntry &vsde = (*it);
1177
1178 sfaTypes[i] = vsde.type;
1179
1180 Bstr bstr = vsde.strRef;
1181 bstr.cloneTo(&sfaRefs[i]);
1182
1183 bstr = vsde.strOvf;
1184 bstr.cloneTo(&sfaOrigValues[i]);
1185
1186 bstr = vsde.strVboxCurrent;
1187 bstr.cloneTo(&sfaVboxValues[i]);
1188
1189 bstr = vsde.strExtraConfigCurrent;
1190 bstr.cloneTo(&sfaExtraConfigValues[i]);
1191 }
1192
1193 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));
1194 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));
1195 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));
1196 sfaVboxValues.detachTo(ComSafeArrayOutArg(aVboxValues));
1197 sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues));
1198
1199 return S_OK;
1200}
1201
1202/**
1203 * Public method implementation.
1204 * @return
1205 */
1206STDMETHODIMP VirtualSystemDescription::GetDescriptionByType(VirtualSystemDescriptionType_T aType,
1207 ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),
1208 ComSafeArrayOut(BSTR, aRefs),
1209 ComSafeArrayOut(BSTR, aOrigValues),
1210 ComSafeArrayOut(BSTR, aVboxValues),
1211 ComSafeArrayOut(BSTR, aExtraConfigValues))
1212{
1213 if (ComSafeArrayOutIsNull(aTypes) ||
1214 ComSafeArrayOutIsNull(aRefs) ||
1215 ComSafeArrayOutIsNull(aOrigValues) ||
1216 ComSafeArrayOutIsNull(aVboxValues) ||
1217 ComSafeArrayOutIsNull(aExtraConfigValues))
1218 return E_POINTER;
1219
1220 AutoCaller autoCaller(this);
1221 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1222
1223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1224
1225 std::list<VirtualSystemDescriptionEntry*> vsd = findByType (aType);
1226 ULONG c = (ULONG)vsd.size();
1227 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);
1228 com::SafeArray<BSTR> sfaRefs(c);
1229 com::SafeArray<BSTR> sfaOrigValues(c);
1230 com::SafeArray<BSTR> sfaVboxValues(c);
1231 com::SafeArray<BSTR> sfaExtraConfigValues(c);
1232
1233 list<VirtualSystemDescriptionEntry*>::const_iterator it;
1234 size_t i = 0;
1235 for (it = vsd.begin();
1236 it != vsd.end();
1237 ++it, ++i)
1238 {
1239 const VirtualSystemDescriptionEntry *vsde = (*it);
1240
1241 sfaTypes[i] = vsde->type;
1242
1243 Bstr bstr = vsde->strRef;
1244 bstr.cloneTo(&sfaRefs[i]);
1245
1246 bstr = vsde->strOvf;
1247 bstr.cloneTo(&sfaOrigValues[i]);
1248
1249 bstr = vsde->strVboxCurrent;
1250 bstr.cloneTo(&sfaVboxValues[i]);
1251
1252 bstr = vsde->strExtraConfigCurrent;
1253 bstr.cloneTo(&sfaExtraConfigValues[i]);
1254 }
1255
1256 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));
1257 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));
1258 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));
1259 sfaVboxValues.detachTo(ComSafeArrayOutArg(aVboxValues));
1260 sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues));
1261
1262 return S_OK;
1263}
1264
1265/**
1266 * Public method implementation.
1267 * @return
1268 */
1269STDMETHODIMP VirtualSystemDescription::GetValuesByType(VirtualSystemDescriptionType_T aType,
1270 VirtualSystemDescriptionValueType_T aWhich,
1271 ComSafeArrayOut(BSTR, aValues))
1272{
1273 if (ComSafeArrayOutIsNull(aValues))
1274 return E_POINTER;
1275
1276 AutoCaller autoCaller(this);
1277 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1278
1279 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1280
1281 std::list<VirtualSystemDescriptionEntry*> vsd = findByType (aType);
1282 com::SafeArray<BSTR> sfaValues((ULONG)vsd.size());
1283
1284 list<VirtualSystemDescriptionEntry*>::const_iterator it;
1285 size_t i = 0;
1286 for (it = vsd.begin();
1287 it != vsd.end();
1288 ++it, ++i)
1289 {
1290 const VirtualSystemDescriptionEntry *vsde = (*it);
1291
1292 Bstr bstr;
1293 switch (aWhich)
1294 {
1295 case VirtualSystemDescriptionValueType_Reference: bstr = vsde->strRef; break;
1296 case VirtualSystemDescriptionValueType_Original: bstr = vsde->strOvf; break;
1297 case VirtualSystemDescriptionValueType_Auto: bstr = vsde->strVboxCurrent; break;
1298 case VirtualSystemDescriptionValueType_ExtraConfig: bstr = vsde->strExtraConfigCurrent; break;
1299 }
1300
1301 bstr.cloneTo(&sfaValues[i]);
1302 }
1303
1304 sfaValues.detachTo(ComSafeArrayOutArg(aValues));
1305
1306 return S_OK;
1307}
1308
1309/**
1310 * Public method implementation.
1311 * @return
1312 */
1313STDMETHODIMP VirtualSystemDescription::SetFinalValues(ComSafeArrayIn(BOOL, aEnabled),
1314 ComSafeArrayIn(IN_BSTR, argVboxValues),
1315 ComSafeArrayIn(IN_BSTR, argExtraConfigValues))
1316{
1317#ifndef RT_OS_WINDOWS
1318 NOREF(aEnabledSize);
1319#endif /* RT_OS_WINDOWS */
1320
1321 CheckComArgSafeArrayNotNull(aEnabled);
1322 CheckComArgSafeArrayNotNull(argVboxValues);
1323 CheckComArgSafeArrayNotNull(argExtraConfigValues);
1324
1325 AutoCaller autoCaller(this);
1326 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1327
1328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1329
1330 com::SafeArray<BOOL> sfaEnabled(ComSafeArrayInArg(aEnabled));
1331 com::SafeArray<IN_BSTR> sfaVboxValues(ComSafeArrayInArg(argVboxValues));
1332 com::SafeArray<IN_BSTR> sfaExtraConfigValues(ComSafeArrayInArg(argExtraConfigValues));
1333
1334 if ( (sfaEnabled.size() != m->llDescriptions.size())
1335 || (sfaVboxValues.size() != m->llDescriptions.size())
1336 || (sfaExtraConfigValues.size() != m->llDescriptions.size())
1337 )
1338 return E_INVALIDARG;
1339
1340 list<VirtualSystemDescriptionEntry>::iterator it;
1341 size_t i = 0;
1342 for (it = m->llDescriptions.begin();
1343 it != m->llDescriptions.end();
1344 ++it, ++i)
1345 {
1346 VirtualSystemDescriptionEntry& vsde = *it;
1347
1348 if (sfaEnabled[i])
1349 {
1350 vsde.strVboxCurrent = sfaVboxValues[i];
1351 vsde.strExtraConfigCurrent = sfaExtraConfigValues[i];
1352 }
1353 else
1354 vsde.type = VirtualSystemDescriptionType_Ignore;
1355 }
1356
1357 return S_OK;
1358}
1359
1360/**
1361 * Public method implementation.
1362 * @return
1363 */
1364STDMETHODIMP VirtualSystemDescription::AddDescription(VirtualSystemDescriptionType_T aType,
1365 IN_BSTR aVboxValue,
1366 IN_BSTR aExtraConfigValue)
1367{
1368 AutoCaller autoCaller(this);
1369 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1370
1371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1372
1373 addEntry(aType, "", aVboxValue, aVboxValue, 0, aExtraConfigValue);
1374
1375 return S_OK;
1376}
1377
1378/**
1379 * Internal method; adds a new description item to the member list.
1380 * @param aType Type of description for the new item.
1381 * @param strRef Reference item; only used with hard disk controllers.
1382 * @param aOrigValue Corresponding original value from OVF.
1383 * @param aAutoValue Initial configuration value (can be overridden by caller with setFinalValues).
1384 * @param ulSizeMB Weight for IProgress
1385 * @param strExtraConfig Extra configuration; meaning dependent on type.
1386 */
1387void VirtualSystemDescription::addEntry(VirtualSystemDescriptionType_T aType,
1388 const Utf8Str &strRef,
1389 const Utf8Str &aOvfValue,
1390 const Utf8Str &aVboxValue,
1391 uint32_t ulSizeMB,
1392 const Utf8Str &strExtraConfig /*= ""*/)
1393{
1394 VirtualSystemDescriptionEntry vsde;
1395 vsde.ulIndex = (uint32_t)m->llDescriptions.size(); // each entry gets an index so the client side can reference them
1396 vsde.type = aType;
1397 vsde.strRef = strRef;
1398 vsde.strOvf = aOvfValue;
1399 vsde.strVboxSuggested // remember original value
1400 = vsde.strVboxCurrent // and set current value which can be overridden by setFinalValues()
1401 = aVboxValue;
1402 vsde.strExtraConfigSuggested
1403 = vsde.strExtraConfigCurrent
1404 = strExtraConfig;
1405 vsde.ulSizeMB = ulSizeMB;
1406
1407 m->llDescriptions.push_back(vsde);
1408}
1409
1410/**
1411 * Private method; returns a list of description items containing all the items from the member
1412 * description items of this virtual system that match the given type.
1413 * @param aType
1414 * @return
1415 */
1416std::list<VirtualSystemDescriptionEntry*> VirtualSystemDescription::findByType(VirtualSystemDescriptionType_T aType)
1417{
1418 std::list<VirtualSystemDescriptionEntry*> vsd;
1419
1420 list<VirtualSystemDescriptionEntry>::iterator it;
1421 for (it = m->llDescriptions.begin();
1422 it != m->llDescriptions.end();
1423 ++it)
1424 {
1425 if (it->type == aType)
1426 vsd.push_back(&(*it));
1427 }
1428
1429 return vsd;
1430}
1431
1432/**
1433 * Private method; looks thru the member hardware items for the IDE, SATA, or SCSI controller with
1434 * the given reference ID. Useful when needing the controller for a particular
1435 * virtual disk.
1436 * @param id
1437 * @return
1438 */
1439const VirtualSystemDescriptionEntry* VirtualSystemDescription::findControllerFromID(uint32_t id)
1440{
1441 Utf8Str strRef = Utf8StrFmt("%RI32", id);
1442 list<VirtualSystemDescriptionEntry>::const_iterator it;
1443 for (it = m->llDescriptions.begin();
1444 it != m->llDescriptions.end();
1445 ++it)
1446 {
1447 const VirtualSystemDescriptionEntry &d = *it;
1448 switch (d.type)
1449 {
1450 case VirtualSystemDescriptionType_HardDiskControllerIDE:
1451 case VirtualSystemDescriptionType_HardDiskControllerSATA:
1452 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
1453 case VirtualSystemDescriptionType_HardDiskControllerSAS:
1454 if (d.strRef == strRef)
1455 return &d;
1456 break;
1457 }
1458 }
1459
1460 return NULL;
1461}
1462
1463/**
1464 * Method called from Appliance::Interpret() if the source OVF for a virtual system
1465 * contains a <vbox:Machine> element. This method then attempts to parse that and
1466 * create a MachineConfigFile instance from it which is stored in this instance data
1467 * and can then be used to create a machine.
1468 *
1469 * This must only be called once per instance.
1470 *
1471 * This rethrows all XML and logic errors from MachineConfigFile.
1472 *
1473 * @param elmMachine <vbox:Machine> element with attributes and subelements from some
1474 * DOM tree.
1475 */
1476void VirtualSystemDescription::importVboxMachineXML(const xml::ElementNode &elmMachine)
1477{
1478 settings::MachineConfigFile *pConfig = NULL;
1479
1480 Assert(m->pConfig == NULL);
1481
1482 try
1483 {
1484 pConfig = new settings::MachineConfigFile(NULL);
1485 pConfig->importMachineXML(elmMachine);
1486
1487 m->pConfig = pConfig;
1488 }
1489 catch (...)
1490 {
1491 if (pConfig)
1492 delete pConfig;
1493 throw;
1494 }
1495}
1496
1497/**
1498 * Returns the machine config created by importVboxMachineXML() or NULL if there's none.
1499 * @return
1500 */
1501const settings::MachineConfigFile* VirtualSystemDescription::getMachineConfig() const
1502{
1503 return m->pConfig;
1504}
1505
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