VirtualBox

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

Last change on this file since 27842 was 27829, checked in by vboxsync, 15 years ago

Main/OVF: sort code into several files + document export code

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 127.2 KB
Line 
1/* $Id: ApplianceImpl.cpp 27829 2010-03-30 13:54:07Z vboxsync $ */
2/** @file
3 *
4 * IAppliance and IVirtualSystem COM class implementations.
5 */
6
7/*
8 * Copyright (C) 2008-2010 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23#include <iprt/stream.h>
24#include <iprt/path.h>
25#include <iprt/dir.h>
26#include <iprt/file.h>
27#include <iprt/s3.h>
28#include <iprt/sha.h>
29#include <iprt/manifest.h>
30
31#include <VBox/param.h>
32#include <VBox/version.h>
33
34#include "ApplianceImpl.h"
35#include "VFSExplorerImpl.h"
36#include "VirtualBoxImpl.h"
37#include "GuestOSTypeImpl.h"
38#include "ProgressImpl.h"
39#include "MachineImpl.h"
40#include "MediumImpl.h"
41
42#include "HostNetworkInterfaceImpl.h"
43
44#include "AutoCaller.h"
45#include "Logging.h"
46
47#include "ApplianceImplPrivate.h"
48
49using namespace std;
50
51////////////////////////////////////////////////////////////////////////////////
52//
53// Internal helpers
54//
55////////////////////////////////////////////////////////////////////////////////
56
57static const struct
58{
59 CIMOSType_T cim;
60 const char *pcszVbox;
61}
62g_osTypes[] =
63{
64 { CIMOSType_CIMOS_Unknown, SchemaDefs_OSTypeId_Other },
65 { CIMOSType_CIMOS_OS2, SchemaDefs_OSTypeId_OS2 },
66 { CIMOSType_CIMOS_MSDOS, SchemaDefs_OSTypeId_DOS },
67 { CIMOSType_CIMOS_WIN3x, SchemaDefs_OSTypeId_Windows31 },
68 { CIMOSType_CIMOS_WIN95, SchemaDefs_OSTypeId_Windows95 },
69 { CIMOSType_CIMOS_WIN98, SchemaDefs_OSTypeId_Windows98 },
70 { CIMOSType_CIMOS_WINNT, SchemaDefs_OSTypeId_WindowsNT4 },
71 { CIMOSType_CIMOS_NetWare, SchemaDefs_OSTypeId_Netware },
72 { CIMOSType_CIMOS_NovellOES, SchemaDefs_OSTypeId_Netware },
73 { CIMOSType_CIMOS_Solaris, SchemaDefs_OSTypeId_OpenSolaris },
74 { CIMOSType_CIMOS_SunOS, SchemaDefs_OSTypeId_OpenSolaris },
75 { CIMOSType_CIMOS_FreeBSD, SchemaDefs_OSTypeId_FreeBSD },
76 { CIMOSType_CIMOS_NetBSD, SchemaDefs_OSTypeId_NetBSD },
77 { CIMOSType_CIMOS_QNX, SchemaDefs_OSTypeId_QNX },
78 { CIMOSType_CIMOS_Windows2000, SchemaDefs_OSTypeId_Windows2000 },
79 { CIMOSType_CIMOS_WindowsMe, SchemaDefs_OSTypeId_WindowsMe },
80 { CIMOSType_CIMOS_OpenBSD, SchemaDefs_OSTypeId_OpenBSD },
81 { CIMOSType_CIMOS_WindowsXP, SchemaDefs_OSTypeId_WindowsXP },
82 { CIMOSType_CIMOS_WindowsXPEmbedded, SchemaDefs_OSTypeId_WindowsXP },
83 { CIMOSType_CIMOS_WindowsEmbeddedforPointofService, SchemaDefs_OSTypeId_WindowsXP },
84 { CIMOSType_CIMOS_MicrosoftWindowsServer2003, SchemaDefs_OSTypeId_Windows2003 },
85 { CIMOSType_CIMOS_MicrosoftWindowsServer2003_64, SchemaDefs_OSTypeId_Windows2003_64 },
86 { CIMOSType_CIMOS_WindowsXP_64, SchemaDefs_OSTypeId_WindowsXP_64 },
87 { CIMOSType_CIMOS_WindowsVista, SchemaDefs_OSTypeId_WindowsVista },
88 { CIMOSType_CIMOS_WindowsVista_64, SchemaDefs_OSTypeId_WindowsVista_64 },
89 { CIMOSType_CIMOS_MicrosoftWindowsServer2008, SchemaDefs_OSTypeId_Windows2008 },
90 { CIMOSType_CIMOS_MicrosoftWindowsServer2008_64, SchemaDefs_OSTypeId_Windows2008_64 },
91 { CIMOSType_CIMOS_FreeBSD_64, SchemaDefs_OSTypeId_FreeBSD_64 },
92 { CIMOSType_CIMOS_RedHatEnterpriseLinux, SchemaDefs_OSTypeId_RedHat },
93 { CIMOSType_CIMOS_RedHatEnterpriseLinux_64, SchemaDefs_OSTypeId_RedHat_64 },
94 { CIMOSType_CIMOS_Solaris_64, SchemaDefs_OSTypeId_OpenSolaris_64 },
95 { CIMOSType_CIMOS_SUSE, SchemaDefs_OSTypeId_OpenSUSE },
96 { CIMOSType_CIMOS_SLES, SchemaDefs_OSTypeId_OpenSUSE },
97 { CIMOSType_CIMOS_NovellLinuxDesktop, SchemaDefs_OSTypeId_OpenSUSE },
98 { CIMOSType_CIMOS_SUSE_64, SchemaDefs_OSTypeId_OpenSUSE_64 },
99 { CIMOSType_CIMOS_SLES_64, SchemaDefs_OSTypeId_OpenSUSE_64 },
100 { CIMOSType_CIMOS_LINUX, SchemaDefs_OSTypeId_Linux },
101 { CIMOSType_CIMOS_SunJavaDesktopSystem, SchemaDefs_OSTypeId_Linux },
102 { CIMOSType_CIMOS_TurboLinux, SchemaDefs_OSTypeId_Linux},
103
104 // { CIMOSType_CIMOS_TurboLinux_64, },
105
106 { CIMOSType_CIMOS_Mandriva, SchemaDefs_OSTypeId_Mandriva },
107 { CIMOSType_CIMOS_Mandriva_64, SchemaDefs_OSTypeId_Mandriva_64 },
108 { CIMOSType_CIMOS_Ubuntu, SchemaDefs_OSTypeId_Ubuntu },
109 { CIMOSType_CIMOS_Ubuntu_64, SchemaDefs_OSTypeId_Ubuntu_64 },
110 { CIMOSType_CIMOS_Debian, SchemaDefs_OSTypeId_Debian },
111 { CIMOSType_CIMOS_Debian_64, SchemaDefs_OSTypeId_Debian_64 },
112 { CIMOSType_CIMOS_Linux_2_4_x, SchemaDefs_OSTypeId_Linux24 },
113 { CIMOSType_CIMOS_Linux_2_4_x_64, SchemaDefs_OSTypeId_Linux24_64 },
114 { CIMOSType_CIMOS_Linux_2_6_x, SchemaDefs_OSTypeId_Linux26 },
115 { CIMOSType_CIMOS_Linux_2_6_x_64, SchemaDefs_OSTypeId_Linux26_64 },
116 { CIMOSType_CIMOS_Linux_64, SchemaDefs_OSTypeId_Linux26_64 }
117};
118
119/* Pattern structure for matching the OS type description field */
120struct osTypePattern
121{
122 const char *pcszPattern;
123 const char *pcszVbox;
124};
125
126/* These are the 32-Bit ones. They are sorted by priority. */
127static const osTypePattern g_osTypesPattern[] =
128{
129 {"Windows NT", SchemaDefs_OSTypeId_WindowsNT4},
130 {"Windows XP", SchemaDefs_OSTypeId_WindowsXP},
131 {"Windows 2000", SchemaDefs_OSTypeId_Windows2000},
132 {"Windows 2003", SchemaDefs_OSTypeId_Windows2003},
133 {"Windows Vista", SchemaDefs_OSTypeId_WindowsVista},
134 {"Windows 2008", SchemaDefs_OSTypeId_Windows2008},
135 {"SUSE", SchemaDefs_OSTypeId_OpenSUSE},
136 {"Novell", SchemaDefs_OSTypeId_OpenSUSE},
137 {"Red Hat", SchemaDefs_OSTypeId_RedHat},
138 {"Mandriva", SchemaDefs_OSTypeId_Mandriva},
139 {"Ubuntu", SchemaDefs_OSTypeId_Ubuntu},
140 {"Debian", SchemaDefs_OSTypeId_Debian},
141 {"QNX", SchemaDefs_OSTypeId_QNX},
142 {"Linux 2.4", SchemaDefs_OSTypeId_Linux24},
143 {"Linux 2.6", SchemaDefs_OSTypeId_Linux26},
144 {"Linux", SchemaDefs_OSTypeId_Linux},
145 {"OpenSolaris", SchemaDefs_OSTypeId_OpenSolaris},
146 {"Solaris", SchemaDefs_OSTypeId_OpenSolaris},
147 {"FreeBSD", SchemaDefs_OSTypeId_FreeBSD},
148 {"NetBSD", SchemaDefs_OSTypeId_NetBSD},
149 {"Windows 95", SchemaDefs_OSTypeId_Windows95},
150 {"Windows 98", SchemaDefs_OSTypeId_Windows98},
151 {"Windows Me", SchemaDefs_OSTypeId_WindowsMe},
152 {"Windows 3.", SchemaDefs_OSTypeId_Windows31},
153 {"DOS", SchemaDefs_OSTypeId_DOS},
154 {"OS2", SchemaDefs_OSTypeId_OS2}
155};
156
157/* These are the 64-Bit ones. They are sorted by priority. */
158static const osTypePattern g_osTypesPattern64[] =
159{
160 {"Windows XP", SchemaDefs_OSTypeId_WindowsXP_64},
161 {"Windows 2003", SchemaDefs_OSTypeId_Windows2003_64},
162 {"Windows Vista", SchemaDefs_OSTypeId_WindowsVista_64},
163 {"Windows 2008", SchemaDefs_OSTypeId_Windows2008_64},
164 {"SUSE", SchemaDefs_OSTypeId_OpenSUSE_64},
165 {"Novell", SchemaDefs_OSTypeId_OpenSUSE_64},
166 {"Red Hat", SchemaDefs_OSTypeId_RedHat_64},
167 {"Mandriva", SchemaDefs_OSTypeId_Mandriva_64},
168 {"Ubuntu", SchemaDefs_OSTypeId_Ubuntu_64},
169 {"Debian", SchemaDefs_OSTypeId_Debian_64},
170 {"Linux 2.4", SchemaDefs_OSTypeId_Linux24_64},
171 {"Linux 2.6", SchemaDefs_OSTypeId_Linux26_64},
172 {"Linux", SchemaDefs_OSTypeId_Linux26_64},
173 {"OpenSolaris", SchemaDefs_OSTypeId_OpenSolaris_64},
174 {"Solaris", SchemaDefs_OSTypeId_OpenSolaris_64},
175 {"FreeBSD", SchemaDefs_OSTypeId_FreeBSD_64},
176};
177
178/**
179 * Private helper func that suggests a VirtualBox guest OS type
180 * for the given OVF operating system type.
181 * @param osTypeVBox
182 * @param c
183 * @param cStr
184 */
185void convertCIMOSType2VBoxOSType(Utf8Str &strType, CIMOSType_T c, const Utf8Str &cStr)
186{
187 /* First check if the type is other/other_64 */
188 if (c == CIMOSType_CIMOS_Other)
189 {
190 for (size_t i=0; i < RT_ELEMENTS(g_osTypesPattern); ++i)
191 if (cStr.contains (g_osTypesPattern[i].pcszPattern, Utf8Str::CaseInsensitive))
192 {
193 strType = g_osTypesPattern[i].pcszVbox;
194 return;
195 }
196 }
197 else if (c == CIMOSType_CIMOS_Other_64)
198 {
199 for (size_t i=0; i < RT_ELEMENTS(g_osTypesPattern64); ++i)
200 if (cStr.contains (g_osTypesPattern64[i].pcszPattern, Utf8Str::CaseInsensitive))
201 {
202 strType = g_osTypesPattern64[i].pcszVbox;
203 return;
204 }
205 }
206
207 for (size_t i = 0; i < RT_ELEMENTS(g_osTypes); ++i)
208 {
209 if (c == g_osTypes[i].cim)
210 {
211 strType = g_osTypes[i].pcszVbox;
212 return;
213 }
214 }
215
216 strType = SchemaDefs_OSTypeId_Other;
217}
218
219/**
220 * Private helper func that suggests a VirtualBox guest OS type
221 * for the given OVF operating system type.
222 * @param osTypeVBox
223 * @param c
224 */
225CIMOSType_T convertVBoxOSType2CIMOSType(const char *pcszVbox)
226{
227 for (size_t i = 0; i < RT_ELEMENTS(g_osTypes); ++i)
228 {
229 if (!RTStrICmp(pcszVbox, g_osTypes[i].pcszVbox))
230 return g_osTypes[i].cim;
231 }
232
233 return CIMOSType_CIMOS_Other;
234}
235
236////////////////////////////////////////////////////////////////////////////////
237//
238// IVirtualBox public methods
239//
240////////////////////////////////////////////////////////////////////////////////
241
242// This code is here so we won't have to include the appliance headers in the
243// IVirtualBox implementation.
244
245/**
246 * Implementation for IVirtualBox::createAppliance.
247 *
248 * @param anAppliance IAppliance object created if S_OK is returned.
249 * @return S_OK or error.
250 */
251STDMETHODIMP VirtualBox::CreateAppliance(IAppliance** anAppliance)
252{
253 HRESULT rc;
254
255 ComObjPtr<Appliance> appliance;
256 appliance.createObject();
257 rc = appliance->init(this);
258
259 if (SUCCEEDED(rc))
260 appliance.queryInterfaceTo(anAppliance);
261
262 return rc;
263}
264
265////////////////////////////////////////////////////////////////////////////////
266//
267// Appliance constructor / destructor
268//
269////////////////////////////////////////////////////////////////////////////////
270
271Appliance::Appliance()
272 : mVirtualBox(NULL)
273{
274}
275
276Appliance::~Appliance()
277{
278}
279
280/**
281 * Appliance COM initializer.
282 * @param
283 * @return
284 */
285HRESULT Appliance::init(VirtualBox *aVirtualBox)
286{
287 /* Enclose the state transition NotReady->InInit->Ready */
288 AutoInitSpan autoInitSpan(this);
289 AssertReturn(autoInitSpan.isOk(), E_FAIL);
290
291 /* Weak reference to a VirtualBox object */
292 unconst(mVirtualBox) = aVirtualBox;
293
294 // initialize data
295 m = new Data;
296
297 /* Confirm a successful initialization */
298 autoInitSpan.setSucceeded();
299
300 return S_OK;
301}
302
303/**
304 * Appliance COM uninitializer.
305 * @return
306 */
307void Appliance::uninit()
308{
309 /* Enclose the state transition Ready->InUninit->NotReady */
310 AutoUninitSpan autoUninitSpan(this);
311 if (autoUninitSpan.uninitDone())
312 return;
313
314 delete m;
315 m = NULL;
316}
317
318////////////////////////////////////////////////////////////////////////////////
319//
320// IAppliance public methods
321//
322////////////////////////////////////////////////////////////////////////////////
323
324/**
325 * Public method implementation.
326 * @param
327 * @return
328 */
329STDMETHODIMP Appliance::COMGETTER(Path)(BSTR *aPath)
330{
331 if (!aPath)
332 return E_POINTER;
333
334 AutoCaller autoCaller(this);
335 if (FAILED(autoCaller.rc())) return autoCaller.rc();
336
337 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
338
339 if (!isApplianceIdle())
340 return E_ACCESSDENIED;
341
342 Bstr bstrPath(m->locInfo.strPath);
343 bstrPath.cloneTo(aPath);
344
345 return S_OK;
346}
347
348/**
349 * Public method implementation.
350 * @param
351 * @return
352 */
353STDMETHODIMP Appliance::COMGETTER(Disks)(ComSafeArrayOut(BSTR, aDisks))
354{
355 CheckComArgOutSafeArrayPointerValid(aDisks);
356
357 AutoCaller autoCaller(this);
358 if (FAILED(autoCaller.rc())) return autoCaller.rc();
359
360 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
361
362 if (!isApplianceIdle())
363 return E_ACCESSDENIED;
364
365 if (m->pReader) // OVFReader instantiated?
366 {
367 size_t c = m->pReader->m_mapDisks.size();
368 com::SafeArray<BSTR> sfaDisks(c);
369
370 DiskImagesMap::const_iterator it;
371 size_t i = 0;
372 for (it = m->pReader->m_mapDisks.begin();
373 it != m->pReader->m_mapDisks.end();
374 ++it, ++i)
375 {
376 // create a string representing this disk
377 const DiskImage &d = it->second;
378 char *psz = NULL;
379 RTStrAPrintf(&psz,
380 "%s\t"
381 "%RI64\t"
382 "%RI64\t"
383 "%s\t"
384 "%s\t"
385 "%RI64\t"
386 "%RI64\t"
387 "%s",
388 d.strDiskId.c_str(),
389 d.iCapacity,
390 d.iPopulatedSize,
391 d.strFormat.c_str(),
392 d.strHref.c_str(),
393 d.iSize,
394 d.iChunkSize,
395 d.strCompression.c_str());
396 Utf8Str utf(psz);
397 Bstr bstr(utf);
398 // push to safearray
399 bstr.cloneTo(&sfaDisks[i]);
400 RTStrFree(psz);
401 }
402
403 sfaDisks.detachTo(ComSafeArrayOutArg(aDisks));
404 }
405
406 return S_OK;
407}
408
409/**
410 * Public method implementation.
411 * @param
412 * @return
413 */
414STDMETHODIMP Appliance::COMGETTER(VirtualSystemDescriptions)(ComSafeArrayOut(IVirtualSystemDescription*, aVirtualSystemDescriptions))
415{
416 CheckComArgOutSafeArrayPointerValid(aVirtualSystemDescriptions);
417
418 AutoCaller autoCaller(this);
419 if (FAILED(autoCaller.rc())) return autoCaller.rc();
420
421 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
422
423 if (!isApplianceIdle())
424 return E_ACCESSDENIED;
425
426 SafeIfaceArray<IVirtualSystemDescription> sfaVSD(m->virtualSystemDescriptions);
427 sfaVSD.detachTo(ComSafeArrayOutArg(aVirtualSystemDescriptions));
428
429 return S_OK;
430}
431
432/**
433 * Public method implementation.
434 * @param path
435 * @return
436 */
437STDMETHODIMP Appliance::Read(IN_BSTR path, IProgress **aProgress)
438{
439 if (!path) return E_POINTER;
440 CheckComArgOutPointerValid(aProgress);
441
442 AutoCaller autoCaller(this);
443 if (FAILED(autoCaller.rc())) return autoCaller.rc();
444
445 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
446
447 if (!isApplianceIdle())
448 return E_ACCESSDENIED;
449
450 if (m->pReader)
451 {
452 delete m->pReader;
453 m->pReader = NULL;
454 }
455
456 // see if we can handle this file; for now we insist it has an ".ovf" extension
457 Utf8Str strPath (path);
458 if (!strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
459 return setError(VBOX_E_FILE_ERROR,
460 tr("Appliance file must have .ovf extension"));
461
462 ComObjPtr<Progress> progress;
463 HRESULT rc = S_OK;
464 try
465 {
466 /* Parse all necessary info out of the URI */
467 parseURI(strPath, m->locInfo);
468 rc = readImpl(m->locInfo, progress);
469 }
470 catch (HRESULT aRC)
471 {
472 rc = aRC;
473 }
474
475 if (SUCCEEDED(rc))
476 /* Return progress to the caller */
477 progress.queryInterfaceTo(aProgress);
478
479 return S_OK;
480}
481
482/**
483 * Public method implementation.
484 * @return
485 */
486STDMETHODIMP Appliance::Interpret()
487{
488 // @todo:
489 // - don't use COM methods but the methods directly (faster, but needs appropriate locking of that objects itself (s. HardDisk))
490 // - Appropriate handle errors like not supported file formats
491 AutoCaller autoCaller(this);
492 if (FAILED(autoCaller.rc())) return autoCaller.rc();
493
494 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
495
496 if (!isApplianceIdle())
497 return E_ACCESSDENIED;
498
499 HRESULT rc = S_OK;
500
501 /* Clear any previous virtual system descriptions */
502 m->virtualSystemDescriptions.clear();
503
504 /* We need the default path for storing disk images */
505 ComPtr<ISystemProperties> systemProps;
506 rc = mVirtualBox->COMGETTER(SystemProperties)(systemProps.asOutParam());
507 if (FAILED(rc)) return rc;
508 Bstr bstrDefaultHardDiskLocation;
509 rc = systemProps->COMGETTER(DefaultHardDiskFolder)(bstrDefaultHardDiskLocation.asOutParam());
510 if (FAILED(rc)) return rc;
511
512 if (!m->pReader)
513 return setError(E_FAIL,
514 tr("Cannot interpret appliance without reading it first (call read() before interpret())"));
515
516 // Change the appliance state so we can safely leave the lock while doing time-consuming
517 // disk imports; also the below method calls do all kinds of locking which conflicts with
518 // the appliance object lock
519 m->state = Data::ApplianceImporting;
520 alock.release();
521
522 /* Try/catch so we can clean up on error */
523 try
524 {
525 list<VirtualSystem>::const_iterator it;
526 /* Iterate through all virtual systems */
527 for (it = m->pReader->m_llVirtualSystems.begin();
528 it != m->pReader->m_llVirtualSystems.end();
529 ++it)
530 {
531 const VirtualSystem &vsysThis = *it;
532
533 ComObjPtr<VirtualSystemDescription> pNewDesc;
534 rc = pNewDesc.createObject();
535 if (FAILED(rc)) throw rc;
536 rc = pNewDesc->init();
537 if (FAILED(rc)) throw rc;
538
539 /* Guest OS type */
540 Utf8Str strOsTypeVBox,
541 strCIMOSType = Utf8StrFmt("%RI32", (uint32_t)vsysThis.cimos);
542 convertCIMOSType2VBoxOSType(strOsTypeVBox, vsysThis.cimos, vsysThis.strCimosDesc);
543 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,
544 "",
545 strCIMOSType,
546 strOsTypeVBox);
547
548 /* VM name */
549 /* If the there isn't any name specified create a default one out of
550 * the OS type */
551 Utf8Str nameVBox = vsysThis.strName;
552 if (nameVBox.isEmpty())
553 nameVBox = strOsTypeVBox;
554 searchUniqueVMName(nameVBox);
555 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,
556 "",
557 vsysThis.strName,
558 nameVBox);
559
560 /* VM Product */
561 if (!vsysThis.strProduct.isEmpty())
562 pNewDesc->addEntry(VirtualSystemDescriptionType_Product,
563 "",
564 vsysThis.strProduct,
565 vsysThis.strProduct);
566
567 /* VM Vendor */
568 if (!vsysThis.strVendor.isEmpty())
569 pNewDesc->addEntry(VirtualSystemDescriptionType_Vendor,
570 "",
571 vsysThis.strVendor,
572 vsysThis.strVendor);
573
574 /* VM Version */
575 if (!vsysThis.strVersion.isEmpty())
576 pNewDesc->addEntry(VirtualSystemDescriptionType_Version,
577 "",
578 vsysThis.strVersion,
579 vsysThis.strVersion);
580
581 /* VM ProductUrl */
582 if (!vsysThis.strProductUrl.isEmpty())
583 pNewDesc->addEntry(VirtualSystemDescriptionType_ProductUrl,
584 "",
585 vsysThis.strProductUrl,
586 vsysThis.strProductUrl);
587
588 /* VM VendorUrl */
589 if (!vsysThis.strVendorUrl.isEmpty())
590 pNewDesc->addEntry(VirtualSystemDescriptionType_VendorUrl,
591 "",
592 vsysThis.strVendorUrl,
593 vsysThis.strVendorUrl);
594
595 /* VM description */
596 if (!vsysThis.strDescription.isEmpty())
597 pNewDesc->addEntry(VirtualSystemDescriptionType_Description,
598 "",
599 vsysThis.strDescription,
600 vsysThis.strDescription);
601
602 /* VM license */
603 if (!vsysThis.strLicenseText.isEmpty())
604 pNewDesc->addEntry(VirtualSystemDescriptionType_License,
605 "",
606 vsysThis.strLicenseText,
607 vsysThis.strLicenseText);
608
609 /* Now that we know the OS type, get our internal defaults based on that. */
610 ComPtr<IGuestOSType> pGuestOSType;
611 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), pGuestOSType.asOutParam());
612 if (FAILED(rc)) throw rc;
613
614 /* CPU count */
615 ULONG cpuCountVBox = vsysThis.cCPUs;
616 /* Check for the constrains */
617 if (cpuCountVBox > SchemaDefs::MaxCPUCount)
618 {
619 addWarning(tr("The virtual system \"%s\" claims support for %u CPU's, but VirtualBox has support for max %u CPU's only."),
620 vsysThis.strName.c_str(), cpuCountVBox, SchemaDefs::MaxCPUCount);
621 cpuCountVBox = SchemaDefs::MaxCPUCount;
622 }
623 if (vsysThis.cCPUs == 0)
624 cpuCountVBox = 1;
625 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
626 "",
627 Utf8StrFmt("%RI32", (uint32_t)vsysThis.cCPUs),
628 Utf8StrFmt("%RI32", (uint32_t)cpuCountVBox));
629
630 /* RAM */
631 uint64_t ullMemSizeVBox = vsysThis.ullMemorySize / _1M;
632 /* Check for the constrains */
633 if (ullMemSizeVBox != 0 &&
634 (ullMemSizeVBox < MM_RAM_MIN_IN_MB ||
635 ullMemSizeVBox > MM_RAM_MAX_IN_MB))
636 {
637 addWarning(tr("The virtual system \"%s\" claims support for %llu MB RAM size, but VirtualBox has support for min %u & max %u MB RAM size only."),
638 vsysThis.strName.c_str(), ullMemSizeVBox, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
639 ullMemSizeVBox = RT_MIN(RT_MAX(ullMemSizeVBox, MM_RAM_MIN_IN_MB), MM_RAM_MAX_IN_MB);
640 }
641 if (vsysThis.ullMemorySize == 0)
642 {
643 /* If the RAM of the OVF is zero, use our predefined values */
644 ULONG memSizeVBox2;
645 rc = pGuestOSType->COMGETTER(RecommendedRAM)(&memSizeVBox2);
646 if (FAILED(rc)) throw rc;
647 /* VBox stores that in MByte */
648 ullMemSizeVBox = (uint64_t)memSizeVBox2;
649 }
650 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
651 "",
652 Utf8StrFmt("%RI64", (uint64_t)vsysThis.ullMemorySize),
653 Utf8StrFmt("%RI64", (uint64_t)ullMemSizeVBox));
654
655 /* Audio */
656 if (!vsysThis.strSoundCardType.isEmpty())
657 /* Currently we set the AC97 always.
658 @todo: figure out the hardware which could be possible */
659 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,
660 "",
661 vsysThis.strSoundCardType,
662 Utf8StrFmt("%RI32", (uint32_t)AudioControllerType_AC97));
663
664#ifdef VBOX_WITH_USB
665 /* USB Controller */
666 if (vsysThis.fHasUsbController)
667 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
668#endif /* VBOX_WITH_USB */
669
670 /* Network Controller */
671 size_t cEthernetAdapters = vsysThis.llEthernetAdapters.size();
672 if (cEthernetAdapters > 0)
673 {
674 /* Check for the constrains */
675 if (cEthernetAdapters > SchemaDefs::NetworkAdapterCount)
676 addWarning(tr("The virtual system \"%s\" claims support for %zu network adapters, but VirtualBox has support for max %u network adapter only."),
677 vsysThis.strName.c_str(), cEthernetAdapters, SchemaDefs::NetworkAdapterCount);
678
679 /* Get the default network adapter type for the selected guest OS */
680 NetworkAdapterType_T defaultAdapterVBox = NetworkAdapterType_Am79C970A;
681 rc = pGuestOSType->COMGETTER(AdapterType)(&defaultAdapterVBox);
682 if (FAILED(rc)) throw rc;
683
684 EthernetAdaptersList::const_iterator itEA;
685 /* Iterate through all abstract networks. We support 8 network
686 * adapters at the maximum, so the first 8 will be added only. */
687 size_t a = 0;
688 for (itEA = vsysThis.llEthernetAdapters.begin();
689 itEA != vsysThis.llEthernetAdapters.end() && a < SchemaDefs::NetworkAdapterCount;
690 ++itEA, ++a)
691 {
692 const EthernetAdapter &ea = *itEA; // logical network to connect to
693 Utf8Str strNetwork = ea.strNetworkName;
694 // make sure it's one of these two
695 if ( (strNetwork.compare("Null", Utf8Str::CaseInsensitive))
696 && (strNetwork.compare("NAT", Utf8Str::CaseInsensitive))
697 && (strNetwork.compare("Bridged", Utf8Str::CaseInsensitive))
698 && (strNetwork.compare("Internal", Utf8Str::CaseInsensitive))
699 && (strNetwork.compare("HostOnly", Utf8Str::CaseInsensitive))
700 )
701 strNetwork = "Bridged"; // VMware assumes this is the default apparently
702
703 /* Figure out the hardware type */
704 NetworkAdapterType_T nwAdapterVBox = defaultAdapterVBox;
705 if (!ea.strAdapterType.compare("PCNet32", Utf8Str::CaseInsensitive))
706 {
707 /* If the default adapter is already one of the two
708 * PCNet adapters use the default one. If not use the
709 * Am79C970A as fallback. */
710 if (!(defaultAdapterVBox == NetworkAdapterType_Am79C970A ||
711 defaultAdapterVBox == NetworkAdapterType_Am79C973))
712 nwAdapterVBox = NetworkAdapterType_Am79C970A;
713 }
714#ifdef VBOX_WITH_E1000
715 /* VMWare accidentally write this with VirtualCenter 3.5,
716 so make sure in this case always to use the VMWare one */
717 else if (!ea.strAdapterType.compare("E10000", Utf8Str::CaseInsensitive))
718 nwAdapterVBox = NetworkAdapterType_I82545EM;
719 else if (!ea.strAdapterType.compare("E1000", Utf8Str::CaseInsensitive))
720 {
721 /* Check if this OVF was written by VirtualBox */
722 if (Utf8Str(vsysThis.strVirtualSystemType).contains("virtualbox", Utf8Str::CaseInsensitive))
723 {
724 /* If the default adapter is already one of the three
725 * E1000 adapters use the default one. If not use the
726 * I82545EM as fallback. */
727 if (!(defaultAdapterVBox == NetworkAdapterType_I82540EM ||
728 defaultAdapterVBox == NetworkAdapterType_I82543GC ||
729 defaultAdapterVBox == NetworkAdapterType_I82545EM))
730 nwAdapterVBox = NetworkAdapterType_I82540EM;
731 }
732 else
733 /* Always use this one since it's what VMware uses */
734 nwAdapterVBox = NetworkAdapterType_I82545EM;
735 }
736#endif /* VBOX_WITH_E1000 */
737
738 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
739 "", // ref
740 ea.strNetworkName, // orig
741 Utf8StrFmt("%RI32", (uint32_t)nwAdapterVBox), // conf
742 0,
743 Utf8StrFmt("type=%s", strNetwork.c_str())); // extra conf
744 }
745 }
746
747 /* Floppy Drive */
748 if (vsysThis.fHasFloppyDrive)
749 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");
750
751 /* CD Drive */
752 if (vsysThis.fHasCdromDrive)
753 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");
754
755 /* Hard disk Controller */
756 uint16_t cIDEused = 0;
757 uint16_t cSATAused = 0; NOREF(cSATAused);
758 uint16_t cSCSIused = 0; NOREF(cSCSIused);
759 ControllersMap::const_iterator hdcIt;
760 /* Iterate through all hard disk controllers */
761 for (hdcIt = vsysThis.mapControllers.begin();
762 hdcIt != vsysThis.mapControllers.end();
763 ++hdcIt)
764 {
765 const HardDiskController &hdc = hdcIt->second;
766 Utf8Str strControllerID = Utf8StrFmt("%RI32", (uint32_t)hdc.idController);
767
768 switch (hdc.system)
769 {
770 case HardDiskController::IDE:
771 {
772 /* Check for the constrains */
773 /* @todo: I'm very confused! Are these bits *one* controller or
774 is every port/bus declared as an extra controller. */
775 if (cIDEused < 4)
776 {
777 // @todo: figure out the IDE types
778 /* Use PIIX4 as default */
779 Utf8Str strType = "PIIX4";
780 if (!hdc.strControllerType.compare("PIIX3", Utf8Str::CaseInsensitive))
781 strType = "PIIX3";
782 else if (!hdc.strControllerType.compare("ICH6", Utf8Str::CaseInsensitive))
783 strType = "ICH6";
784 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
785 strControllerID,
786 hdc.strControllerType,
787 strType);
788 }
789 else
790 {
791 /* Warn only once */
792 if (cIDEused == 1)
793 addWarning(tr("The virtual \"%s\" system requests support for more than one IDE controller, but VirtualBox has support for only one."),
794 vsysThis.strName.c_str());
795
796 }
797 ++cIDEused;
798 break;
799 }
800
801 case HardDiskController::SATA:
802 {
803#ifdef VBOX_WITH_AHCI
804 /* Check for the constrains */
805 if (cSATAused < 1)
806 {
807 // @todo: figure out the SATA types
808 /* We only support a plain AHCI controller, so use them always */
809 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
810 strControllerID,
811 hdc.strControllerType,
812 "AHCI");
813 }
814 else
815 {
816 /* Warn only once */
817 if (cSATAused == 1)
818 addWarning(tr("The virtual system \"%s\" requests support for more than one SATA controller, but VirtualBox has support for only one"),
819 vsysThis.strName.c_str());
820
821 }
822 ++cSATAused;
823 break;
824#else /* !VBOX_WITH_AHCI */
825 addWarning(tr("The virtual system \"%s\" requests at least one SATA controller but this version of VirtualBox does not provide a SATA controller emulation"),
826 vsysThis.strName.c_str());
827#endif /* !VBOX_WITH_AHCI */
828 }
829
830 case HardDiskController::SCSI:
831 {
832#ifdef VBOX_WITH_LSILOGIC
833 /* Check for the constrains */
834 if (cSCSIused < 1)
835 {
836 Utf8Str hdcController = "LsiLogic";
837 if (!hdc.strControllerType.compare("BusLogic", Utf8Str::CaseInsensitive))
838 hdcController = "BusLogic";
839 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,
840 strControllerID,
841 hdc.strControllerType,
842 hdcController);
843 }
844 else
845 addWarning(tr("The virtual system \"%s\" requests support for an additional SCSI controller of type \"%s\" with ID %s, but VirtualBox presently supports only one SCSI controller."),
846 vsysThis.strName.c_str(),
847 hdc.strControllerType.c_str(),
848 strControllerID.c_str());
849 ++cSCSIused;
850 break;
851#else /* !VBOX_WITH_LSILOGIC */
852 addWarning(tr("The virtual system \"%s\" requests at least one SATA controller but this version of VirtualBox does not provide a SCSI controller emulation"),
853 vsysThis.strName.c_str());
854#endif /* !VBOX_WITH_LSILOGIC */
855 }
856 }
857 }
858
859 /* Hard disks */
860 if (vsysThis.mapVirtualDisks.size() > 0)
861 {
862 VirtualDisksMap::const_iterator itVD;
863 /* Iterate through all hard disks ()*/
864 for (itVD = vsysThis.mapVirtualDisks.begin();
865 itVD != vsysThis.mapVirtualDisks.end();
866 ++itVD)
867 {
868 const VirtualDisk &hd = itVD->second;
869 /* Get the associated disk image */
870 const DiskImage &di = m->pReader->m_mapDisks[hd.strDiskId];
871
872 // @todo:
873 // - figure out all possible vmdk formats we also support
874 // - figure out if there is a url specifier for vhd already
875 // - we need a url specifier for the vdi format
876 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)
877 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive))
878 {
879 /* If the href is empty use the VM name as filename */
880 Utf8Str strFilename = di.strHref;
881 if (!strFilename.length())
882 strFilename = Utf8StrFmt("%s.vmdk", nameVBox.c_str());
883 /* Construct a unique target path */
884 Utf8StrFmt strPath("%ls%c%s",
885 bstrDefaultHardDiskLocation.raw(),
886 RTPATH_DELIMITER,
887 strFilename.c_str());
888 searchUniqueDiskImageFilePath(strPath);
889
890 /* find the description for the hard disk controller
891 * that has the same ID as hd.idController */
892 const VirtualSystemDescriptionEntry *pController;
893 if (!(pController = pNewDesc->findControllerFromID(hd.idController)))
894 throw setError(E_FAIL,
895 tr("Cannot find hard disk controller with OVF instance ID %RI32 to which disk \"%s\" should be attached"),
896 hd.idController,
897 di.strHref.c_str());
898
899 /* controller to attach to, and the bus within that controller */
900 Utf8StrFmt strExtraConfig("controller=%RI16;channel=%RI16",
901 pController->ulIndex,
902 hd.ulAddressOnParent);
903 ULONG ulSize = 0;
904 if (di.iCapacity != -1)
905 ulSize = (ULONG)(di.iCapacity / _1M);
906 else if (di.iPopulatedSize != -1)
907 ulSize = (ULONG)(di.iPopulatedSize / _1M);
908 else if (di.iSize != -1)
909 ulSize = (ULONG)(di.iSize / _1M);
910 if (ulSize == 0)
911 ulSize = 10000; // assume 10 GB, this is for the progress bar only anyway
912 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
913 hd.strDiskId,
914 di.strHref,
915 strPath,
916 ulSize,
917 strExtraConfig);
918 }
919 else
920 throw setError(VBOX_E_FILE_ERROR,
921 tr("Unsupported format for virtual disk image in OVF: \"%s\"", di.strFormat.c_str()));
922 }
923 }
924
925 m->virtualSystemDescriptions.push_back(pNewDesc);
926 }
927 }
928 catch (HRESULT aRC)
929 {
930 /* On error we clear the list & return */
931 m->virtualSystemDescriptions.clear();
932 rc = aRC;
933 }
934
935 // reset the appliance state
936 alock.acquire();
937 m->state = Data::ApplianceIdle;
938
939 return rc;
940}
941
942/**
943 * Public method implementation.
944 * @param aProgress
945 * @return
946 */
947STDMETHODIMP Appliance::ImportMachines(IProgress **aProgress)
948{
949 CheckComArgOutPointerValid(aProgress);
950
951 AutoCaller autoCaller(this);
952 if (FAILED(autoCaller.rc())) return autoCaller.rc();
953
954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
955
956 // do not allow entering this method if the appliance is busy reading or writing
957 if (!isApplianceIdle())
958 return E_ACCESSDENIED;
959
960 if (!m->pReader)
961 return setError(E_FAIL,
962 tr("Cannot import machines without reading it first (call read() before importMachines())"));
963
964 ComObjPtr<Progress> progress;
965 HRESULT rc = S_OK;
966 try
967 {
968 rc = importImpl(m->locInfo, progress);
969 }
970 catch (HRESULT aRC)
971 {
972 rc = aRC;
973 }
974
975 if (SUCCEEDED(rc))
976 /* Return progress to the caller */
977 progress.queryInterfaceTo(aProgress);
978
979 return rc;
980}
981
982STDMETHODIMP Appliance::CreateVFSExplorer(IN_BSTR aURI, IVFSExplorer **aExplorer)
983{
984 CheckComArgOutPointerValid(aExplorer);
985
986 AutoCaller autoCaller(this);
987 if (FAILED(autoCaller.rc())) return autoCaller.rc();
988
989 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
990
991 ComObjPtr<VFSExplorer> explorer;
992 HRESULT rc = S_OK;
993 try
994 {
995 Utf8Str uri(aURI);
996 /* Check which kind of export the user has requested */
997 LocationInfo li;
998 parseURI(uri, li);
999 /* Create the explorer object */
1000 explorer.createObject();
1001 rc = explorer->init(li.storageType, li.strPath, li.strHostname, li.strUsername, li.strPassword, mVirtualBox);
1002 }
1003 catch (HRESULT aRC)
1004 {
1005 rc = aRC;
1006 }
1007
1008 if (SUCCEEDED(rc))
1009 /* Return explorer to the caller */
1010 explorer.queryInterfaceTo(aExplorer);
1011
1012 return rc;
1013}
1014
1015/**
1016* Public method implementation.
1017 * @return
1018 */
1019STDMETHODIMP Appliance::GetWarnings(ComSafeArrayOut(BSTR, aWarnings))
1020{
1021 if (ComSafeArrayOutIsNull(aWarnings))
1022 return E_POINTER;
1023
1024 AutoCaller autoCaller(this);
1025 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1026
1027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1028
1029 com::SafeArray<BSTR> sfaWarnings(m->llWarnings.size());
1030
1031 list<Utf8Str>::const_iterator it;
1032 size_t i = 0;
1033 for (it = m->llWarnings.begin();
1034 it != m->llWarnings.end();
1035 ++it, ++i)
1036 {
1037 Bstr bstr = *it;
1038 bstr.cloneTo(&sfaWarnings[i]);
1039 }
1040
1041 sfaWarnings.detachTo(ComSafeArrayOutArg(aWarnings));
1042
1043 return S_OK;
1044}
1045
1046////////////////////////////////////////////////////////////////////////////////
1047//
1048// Appliance private methods
1049//
1050////////////////////////////////////////////////////////////////////////////////
1051
1052/**
1053 * Returns true if the appliance is in "idle" state. This should always be the
1054 * case unless an import or export is currently in progress. Similar to machine
1055 * states, this permits the Appliance implementation code to let go of the
1056 * Appliance object lock while a time-consuming disk conversion is in progress
1057 * without exposing the appliance to conflicting calls.
1058 *
1059 * This sets an error on "this" (the appliance) and returns false if the appliance
1060 * is busy. The caller should then return E_ACCESSDENIED.
1061 *
1062 * Must be called from under the object lock!
1063 *
1064 * @return
1065 */
1066bool Appliance::isApplianceIdle() const
1067{
1068 if (m->state == Data::ApplianceImporting)
1069 setError(VBOX_E_INVALID_OBJECT_STATE, "The appliance is busy importing files");
1070 else if (m->state == Data::ApplianceExporting)
1071 setError(VBOX_E_INVALID_OBJECT_STATE, "The appliance is busy exporting files");
1072 else
1073 return true;
1074
1075 return false;
1076}
1077
1078HRESULT Appliance::searchUniqueVMName(Utf8Str& aName) const
1079{
1080 IMachine *machine = NULL;
1081 char *tmpName = RTStrDup(aName.c_str());
1082 int i = 1;
1083 /* @todo: Maybe too cost-intensive; try to find a lighter way */
1084 while (mVirtualBox->FindMachine(Bstr(tmpName), &machine) != VBOX_E_OBJECT_NOT_FOUND)
1085 {
1086 RTStrFree(tmpName);
1087 RTStrAPrintf(&tmpName, "%s_%d", aName.c_str(), i);
1088 ++i;
1089 }
1090 aName = tmpName;
1091 RTStrFree(tmpName);
1092
1093 return S_OK;
1094}
1095
1096HRESULT Appliance::searchUniqueDiskImageFilePath(Utf8Str& aName) const
1097{
1098 IMedium *harddisk = NULL;
1099 char *tmpName = RTStrDup(aName.c_str());
1100 int i = 1;
1101 /* Check if the file exists or if a file with this path is registered
1102 * already */
1103 /* @todo: Maybe too cost-intensive; try to find a lighter way */
1104 while (RTPathExists(tmpName) ||
1105 mVirtualBox->FindHardDisk(Bstr(tmpName), &harddisk) != VBOX_E_OBJECT_NOT_FOUND)
1106 {
1107 RTStrFree(tmpName);
1108 char *tmpDir = RTStrDup(aName.c_str());
1109 RTPathStripFilename(tmpDir);;
1110 char *tmpFile = RTStrDup(RTPathFilename(aName.c_str()));
1111 RTPathStripExt(tmpFile);
1112 const char *tmpExt = RTPathExt(aName.c_str());
1113 RTStrAPrintf(&tmpName, "%s%c%s_%d%s", tmpDir, RTPATH_DELIMITER, tmpFile, i, tmpExt);
1114 RTStrFree(tmpFile);
1115 RTStrFree(tmpDir);
1116 ++i;
1117 }
1118 aName = tmpName;
1119 RTStrFree(tmpName);
1120
1121 return S_OK;
1122}
1123
1124/**
1125 * Called from the import and export background threads to synchronize the second
1126 * background disk thread's progress object with the current progress object so
1127 * that the user interface sees progress correctly and that cancel signals are
1128 * passed on to the second thread.
1129 * @param pProgressThis Progress object of the current thread.
1130 * @param pProgressAsync Progress object of asynchronous task running in background.
1131 */
1132void Appliance::waitForAsyncProgress(ComObjPtr<Progress> &pProgressThis,
1133 ComPtr<IProgress> &pProgressAsync)
1134{
1135 HRESULT rc;
1136
1137 // now loop until the asynchronous operation completes and then report its result
1138 BOOL fCompleted;
1139 BOOL fCanceled;
1140 ULONG currentPercent;
1141 while (SUCCEEDED(pProgressAsync->COMGETTER(Completed(&fCompleted))))
1142 {
1143 rc = pProgressThis->COMGETTER(Canceled)(&fCanceled);
1144 if (FAILED(rc)) throw rc;
1145 if (fCanceled)
1146 {
1147 pProgressAsync->Cancel();
1148 break;
1149 }
1150
1151 rc = pProgressAsync->COMGETTER(Percent(&currentPercent));
1152 if (FAILED(rc)) throw rc;
1153 if (!pProgressThis.isNull())
1154 pProgressThis->SetCurrentOperationProgress(currentPercent);
1155 if (fCompleted)
1156 break;
1157
1158 /* Make sure the loop is not too tight */
1159 rc = pProgressAsync->WaitForCompletion(100);
1160 if (FAILED(rc)) throw rc;
1161 }
1162 // report result of asynchronous operation
1163 LONG iRc;
1164 rc = pProgressAsync->COMGETTER(ResultCode)(&iRc);
1165 if (FAILED(rc)) throw rc;
1166
1167
1168 // if the thread of the progress object has an error, then
1169 // retrieve the error info from there, or it'll be lost
1170 if (FAILED(iRc))
1171 {
1172 ProgressErrorInfo info(pProgressAsync);
1173 Utf8Str str(info.getText());
1174 const char *pcsz = str.c_str();
1175 HRESULT rc2 = setError(iRc, pcsz);
1176 throw rc2;
1177 }
1178}
1179
1180void Appliance::addWarning(const char* aWarning, ...)
1181{
1182 va_list args;
1183 va_start(args, aWarning);
1184 Utf8StrFmtVA str(aWarning, args);
1185 va_end(args);
1186 m->llWarnings.push_back(str);
1187}
1188
1189void Appliance::disksWeight(uint32_t &ulTotalMB, uint32_t &cDisks) const
1190{
1191 ulTotalMB = 0;
1192 cDisks = 0;
1193 /* Weigh the disk images according to their sizes */
1194 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
1195 for (it = m->virtualSystemDescriptions.begin();
1196 it != m->virtualSystemDescriptions.end();
1197 ++it)
1198 {
1199 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
1200 /* One for every hard disk of the Virtual System */
1201 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
1202 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
1203 for (itH = avsdeHDs.begin();
1204 itH != avsdeHDs.end();
1205 ++itH)
1206 {
1207 const VirtualSystemDescriptionEntry *pHD = *itH;
1208 ulTotalMB += pHD->ulSizeMB;
1209 ++cDisks;
1210 }
1211 }
1212
1213}
1214
1215HRESULT Appliance::setUpProgressFS(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription)
1216{
1217 HRESULT rc;
1218
1219 /* Create the progress object */
1220 pProgress.createObject();
1221
1222 /* Weigh the disk images according to their sizes */
1223 uint32_t ulTotalMB;
1224 uint32_t cDisks;
1225 disksWeight(ulTotalMB, cDisks);
1226
1227 ULONG cOperations = 1 + cDisks; // one op per disk plus 1 for the XML
1228
1229 ULONG ulTotalOperationsWeight;
1230 if (ulTotalMB)
1231 {
1232 m->ulWeightPerOperation = (ULONG)((double)ulTotalMB * 1 / 100); // use 1% of the progress for the XML
1233 ulTotalOperationsWeight = ulTotalMB + m->ulWeightPerOperation;
1234 }
1235 else
1236 {
1237 // no disks to export:
1238 ulTotalOperationsWeight = 1;
1239 m->ulWeightPerOperation = 1;
1240 }
1241
1242 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightPerOperation = %d\n",
1243 ulTotalMB, cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightPerOperation));
1244
1245 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
1246 bstrDescription,
1247 TRUE /* aCancelable */,
1248 cOperations, // ULONG cOperations,
1249 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight,
1250 bstrDescription, // CBSTR bstrFirstOperationDescription,
1251 m->ulWeightPerOperation); // ULONG ulFirstOperationWeight,
1252 return rc;
1253}
1254
1255HRESULT Appliance::setUpProgressImportS3(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription)
1256{
1257 HRESULT rc;
1258
1259 /* Create the progress object */
1260 pProgress.createObject();
1261
1262 /* Weigh the disk images according to their sizes */
1263 uint32_t ulTotalMB;
1264 uint32_t cDisks;
1265 disksWeight(ulTotalMB, cDisks);
1266
1267 ULONG cOperations = 1 + 1 + 1 + cDisks; // one op per disk plus 1 for init, plus 1 for the manifest file & 1 plus for the import */
1268
1269 ULONG ulTotalOperationsWeight = ulTotalMB;
1270 if (!ulTotalOperationsWeight)
1271 // no disks to export:
1272 ulTotalOperationsWeight = 1;
1273
1274 ULONG ulImportWeight = (ULONG)((double)ulTotalOperationsWeight * 50 / 100); // use 50% for import
1275 ulTotalOperationsWeight += ulImportWeight;
1276
1277 m->ulWeightPerOperation = ulImportWeight; /* save for using later */
1278
1279 ULONG ulInitWeight = (ULONG)((double)ulTotalOperationsWeight * 0.1 / 100); // use 0.1% for init
1280 ulTotalOperationsWeight += ulInitWeight;
1281
1282 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightPerOperation = %d\n",
1283 ulTotalMB, cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightPerOperation));
1284
1285 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
1286 bstrDescription,
1287 TRUE /* aCancelable */,
1288 cOperations, // ULONG cOperations,
1289 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight,
1290 Bstr(tr("Init")), // CBSTR bstrFirstOperationDescription,
1291 ulInitWeight); // ULONG ulFirstOperationWeight,
1292 return rc;
1293}
1294
1295HRESULT Appliance::setUpProgressWriteS3(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription)
1296{
1297 HRESULT rc;
1298
1299 /* Create the progress object */
1300 pProgress.createObject();
1301
1302 /* Weigh the disk images according to their sizes */
1303 uint32_t ulTotalMB;
1304 uint32_t cDisks;
1305 disksWeight(ulTotalMB, cDisks);
1306
1307 ULONG cOperations = 1 + 1 + 1 + cDisks; // one op per disk plus 1 for the OVF, plus 1 for the mf & 1 plus to the temporary creation */
1308
1309 ULONG ulTotalOperationsWeight;
1310 if (ulTotalMB)
1311 {
1312 m->ulWeightPerOperation = (ULONG)((double)ulTotalMB * 1 / 100); // use 1% of the progress for OVF file upload (we didn't know the size at this point)
1313 ulTotalOperationsWeight = ulTotalMB + m->ulWeightPerOperation;
1314 }
1315 else
1316 {
1317 // no disks to export:
1318 ulTotalOperationsWeight = 1;
1319 m->ulWeightPerOperation = 1;
1320 }
1321 ULONG ulOVFCreationWeight = (ULONG)((double)ulTotalOperationsWeight * 50.0 / 100.0); /* Use 50% for the creation of the OVF & the disks */
1322 ulTotalOperationsWeight += ulOVFCreationWeight;
1323
1324 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightPerOperation = %d\n",
1325 ulTotalMB, cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightPerOperation));
1326
1327 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
1328 bstrDescription,
1329 TRUE /* aCancelable */,
1330 cOperations, // ULONG cOperations,
1331 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight,
1332 bstrDescription, // CBSTR bstrFirstOperationDescription,
1333 ulOVFCreationWeight); // ULONG ulFirstOperationWeight,
1334 return rc;
1335}
1336
1337void Appliance::parseURI(Utf8Str strUri, LocationInfo &locInfo) const
1338{
1339 /* Check the URI for the protocol */
1340 if (strUri.startsWith("file://", Utf8Str::CaseInsensitive)) /* File based */
1341 {
1342 locInfo.storageType = VFSType_File;
1343 strUri = strUri.substr(sizeof("file://") - 1);
1344 }
1345 else if (strUri.startsWith("SunCloud://", Utf8Str::CaseInsensitive)) /* Sun Cloud service */
1346 {
1347 locInfo.storageType = VFSType_S3;
1348 strUri = strUri.substr(sizeof("SunCloud://") - 1);
1349 }
1350 else if (strUri.startsWith("S3://", Utf8Str::CaseInsensitive)) /* S3 service */
1351 {
1352 locInfo.storageType = VFSType_S3;
1353 strUri = strUri.substr(sizeof("S3://") - 1);
1354 }
1355 else if (strUri.startsWith("webdav://", Utf8Str::CaseInsensitive)) /* webdav service */
1356 throw E_NOTIMPL;
1357
1358 /* Not necessary on a file based URI */
1359 if (locInfo.storageType != VFSType_File)
1360 {
1361 size_t uppos = strUri.find("@"); /* username:password combo */
1362 if (uppos != Utf8Str::npos)
1363 {
1364 locInfo.strUsername = strUri.substr(0, uppos);
1365 strUri = strUri.substr(uppos + 1);
1366 size_t upos = locInfo.strUsername.find(":");
1367 if (upos != Utf8Str::npos)
1368 {
1369 locInfo.strPassword = locInfo.strUsername.substr(upos + 1);
1370 locInfo.strUsername = locInfo.strUsername.substr(0, upos);
1371 }
1372 }
1373 size_t hpos = strUri.find("/"); /* hostname part */
1374 if (hpos != Utf8Str::npos)
1375 {
1376 locInfo.strHostname = strUri.substr(0, hpos);
1377 strUri = strUri.substr(hpos);
1378 }
1379 }
1380
1381 locInfo.strPath = strUri;
1382}
1383
1384void Appliance::parseBucket(Utf8Str &aPath, Utf8Str &aBucket) const
1385{
1386 /* Buckets are S3 specific. So parse the bucket out of the file path */
1387 if (!aPath.startsWith("/"))
1388 throw setError(E_INVALIDARG,
1389 tr("The path '%s' must start with /"), aPath.c_str());
1390 size_t bpos = aPath.find("/", 1);
1391 if (bpos != Utf8Str::npos)
1392 {
1393 aBucket = aPath.substr(1, bpos - 1); /* The bucket without any slashes */
1394 aPath = aPath.substr(bpos); /* The rest of the file path */
1395 }
1396 /* If there is no bucket name provided reject it */
1397 if (aBucket.isEmpty())
1398 throw setError(E_INVALIDARG,
1399 tr("You doesn't provide a bucket name in the URI '%s'"), aPath.c_str());
1400}
1401
1402Utf8Str Appliance::manifestFileName(Utf8Str aPath) const
1403{
1404 /* Get the name part */
1405 char *pszMfName = RTStrDup(RTPathFilename(aPath.c_str()));
1406 /* Strip any extensions */
1407 RTPathStripExt(pszMfName);
1408 /* Path without the filename */
1409 aPath.stripFilename();
1410 /* Format the manifest path */
1411 Utf8StrFmt strMfFile("%s/%s.mf", aPath.c_str(), pszMfName);
1412 RTStrFree(pszMfName);
1413 return strMfFile;
1414}
1415
1416/* static */
1417int Appliance::TaskOVF::updateProgress(unsigned uPercent, void *pvUser)
1418{
1419 Appliance::TaskOVF* pTask = *(Appliance::TaskOVF**)pvUser;
1420
1421 if ( pTask
1422 && !pTask->progress.isNull())
1423 {
1424 BOOL fCanceled;
1425 pTask->progress->COMGETTER(Canceled)(&fCanceled);
1426 if (fCanceled)
1427 return -1;
1428 pTask->progress->SetCurrentOperationProgress(uPercent);
1429 }
1430 return VINF_SUCCESS;
1431}
1432
1433HRESULT Appliance::readImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)
1434{
1435 /* Initialize our worker task */
1436 std::auto_ptr<TaskImportOVF> task(new TaskImportOVF(this));
1437 /* What should the task do */
1438 task->taskType = TaskImportOVF::Read;
1439 /* Copy the current location info to the task */
1440 task->locInfo = aLocInfo;
1441
1442 BstrFmt bstrDesc = BstrFmt(tr("Read appliance '%s'"),
1443 aLocInfo.strPath.c_str());
1444 HRESULT rc;
1445 /* Create the progress object */
1446 aProgress.createObject();
1447 if (task->locInfo.storageType == VFSType_File)
1448 {
1449 /* 1 operation only */
1450 rc = aProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
1451 bstrDesc,
1452 TRUE /* aCancelable */);
1453 }
1454 else
1455 {
1456 /* 4/5 is downloading, 1/5 is reading */
1457 rc = aProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
1458 bstrDesc,
1459 TRUE /* aCancelable */,
1460 2, // ULONG cOperations,
1461 5, // ULONG ulTotalOperationsWeight,
1462 BstrFmt(tr("Download appliance '%s'"),
1463 aLocInfo.strPath.c_str()), // CBSTR bstrFirstOperationDescription,
1464 4); // ULONG ulFirstOperationWeight,
1465 }
1466 if (FAILED(rc)) throw rc;
1467
1468 task->progress = aProgress;
1469
1470 rc = task->startThread();
1471 if (FAILED(rc)) throw rc;
1472
1473 /* Don't destruct on success */
1474 task.release();
1475
1476 return rc;
1477}
1478
1479/**
1480 * Implementation of the import code. This gets called from the public Appliance::ImportMachines()
1481 * method as well as Appliance::importS3().
1482 * @param aLocInfo
1483 * @param aProgress
1484 * @return
1485 */
1486HRESULT Appliance::importImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)
1487{
1488 /* Initialize our worker task */
1489 std::auto_ptr<TaskImportOVF> task(new TaskImportOVF(this));
1490 /* What should the task do */
1491 task->taskType = TaskImportOVF::Import;
1492 /* Copy the current location info to the task */
1493 task->locInfo = aLocInfo;
1494
1495 Bstr progressDesc = BstrFmt(tr("Import appliance '%s'"),
1496 aLocInfo.strPath.c_str());
1497
1498 HRESULT rc = S_OK;
1499
1500 /* todo: This progress init stuff should be done a little bit more generic */
1501 if (task->locInfo.storageType == VFSType_File)
1502 rc = setUpProgressFS(aProgress, progressDesc);
1503 else
1504 rc = setUpProgressImportS3(aProgress, progressDesc);
1505 if (FAILED(rc)) throw rc;
1506
1507 task->progress = aProgress;
1508
1509 rc = task->startThread();
1510 if (FAILED(rc)) throw rc;
1511
1512 /* Don't destruct on success */
1513 task.release();
1514
1515 return rc;
1516}
1517
1518/**
1519 * Worker thread implementation for Read() (ovf reader).
1520 * @param aThread
1521 * @param pvUser
1522 */
1523/* static */
1524DECLCALLBACK(int) Appliance::taskThreadImportOVF(RTTHREAD /* aThread */, void *pvUser)
1525{
1526 std::auto_ptr<TaskImportOVF> task(static_cast<TaskImportOVF*>(pvUser));
1527 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
1528
1529 Appliance *pAppliance = task->pAppliance;
1530
1531 LogFlowFuncEnter();
1532 LogFlowFunc(("Appliance %p\n", pAppliance));
1533
1534 HRESULT rc = S_OK;
1535
1536 switch (task->taskType)
1537 {
1538 case TaskImportOVF::Read:
1539 {
1540 if (task->locInfo.storageType == VFSType_File)
1541 rc = pAppliance->readFS(task.get());
1542 else if (task->locInfo.storageType == VFSType_S3)
1543 rc = pAppliance->readS3(task.get());
1544 break;
1545 }
1546 case TaskImportOVF::Import:
1547 {
1548 if (task->locInfo.storageType == VFSType_File)
1549 rc = pAppliance->importFS(task.get());
1550 else if (task->locInfo.storageType == VFSType_S3)
1551 rc = pAppliance->importS3(task.get());
1552 break;
1553 }
1554 }
1555
1556 LogFlowFunc(("rc=%Rhrc\n", rc));
1557 LogFlowFuncLeave();
1558
1559 return VINF_SUCCESS;
1560}
1561
1562int Appliance::TaskImportOVF::startThread()
1563{
1564 int vrc = RTThreadCreate(NULL, Appliance::taskThreadImportOVF, this,
1565 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
1566 "Appliance::Task");
1567
1568 ComAssertMsgRCRet(vrc,
1569 ("Could not create taskThreadImportOVF (%Rrc)\n", vrc), E_FAIL);
1570
1571 return S_OK;
1572}
1573
1574int Appliance::readFS(TaskImportOVF *pTask)
1575{
1576 LogFlowFuncEnter();
1577 LogFlowFunc(("Appliance %p\n", this));
1578
1579 AutoCaller autoCaller(this);
1580 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1581
1582 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
1583
1584 HRESULT rc = S_OK;
1585
1586 try
1587 {
1588 /* Read & parse the XML structure of the OVF file */
1589 m->pReader = new OVFReader(pTask->locInfo.strPath);
1590 /* Create the SHA1 sum of the OVF file for later validation */
1591 char *pszDigest;
1592 int vrc = RTSha1Digest(pTask->locInfo.strPath.c_str(), &pszDigest);
1593 if (RT_FAILURE(vrc))
1594 throw setError(VBOX_E_FILE_ERROR,
1595 tr("Couldn't calculate SHA1 digest for file '%s' (%Rrc)"),
1596 RTPathFilename(pTask->locInfo.strPath.c_str()), vrc);
1597 m->strOVFSHA1Digest = pszDigest;
1598 RTStrFree(pszDigest);
1599 }
1600 catch(xml::Error &x)
1601 {
1602 rc = setError(VBOX_E_FILE_ERROR,
1603 x.what());
1604 }
1605 catch(HRESULT aRC)
1606 {
1607 rc = aRC;
1608 }
1609
1610 pTask->rc = rc;
1611
1612 if (!pTask->progress.isNull())
1613 pTask->progress->notifyComplete(rc);
1614
1615 LogFlowFunc(("rc=%Rhrc\n", rc));
1616 LogFlowFuncLeave();
1617
1618 return VINF_SUCCESS;
1619}
1620
1621int Appliance::readS3(TaskImportOVF *pTask)
1622{
1623 LogFlowFuncEnter();
1624 LogFlowFunc(("Appliance %p\n", this));
1625
1626 AutoCaller autoCaller(this);
1627 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1628
1629 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
1630
1631 HRESULT rc = S_OK;
1632 int vrc = VINF_SUCCESS;
1633 RTS3 hS3 = NIL_RTS3;
1634 char szOSTmpDir[RTPATH_MAX];
1635 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));
1636 /* The template for the temporary directory created below */
1637 char *pszTmpDir;
1638 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir);
1639 list< pair<Utf8Str, ULONG> > filesList;
1640 Utf8Str strTmpOvf;
1641
1642 try
1643 {
1644 /* Extract the bucket */
1645 Utf8Str tmpPath = pTask->locInfo.strPath;
1646 Utf8Str bucket;
1647 parseBucket(tmpPath, bucket);
1648
1649 /* We need a temporary directory which we can put the OVF file & all
1650 * disk images in */
1651 vrc = RTDirCreateTemp(pszTmpDir);
1652 if (RT_FAILURE(vrc))
1653 throw setError(VBOX_E_FILE_ERROR,
1654 tr("Cannot create temporary directory '%s'"), pszTmpDir);
1655
1656 /* The temporary name of the target OVF file */
1657 strTmpOvf = Utf8StrFmt("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str()));
1658
1659 /* Next we have to download the OVF */
1660 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);
1661 if(RT_FAILURE(vrc))
1662 throw setError(VBOX_E_IPRT_ERROR,
1663 tr("Cannot create S3 service handler"));
1664 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask);
1665
1666 /* Get it */
1667 char *pszFilename = RTPathFilename(strTmpOvf.c_str());
1668 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strTmpOvf.c_str());
1669 if (RT_FAILURE(vrc))
1670 {
1671 if(vrc == VERR_S3_CANCELED)
1672 throw S_OK; /* todo: !!!!!!!!!!!!! */
1673 else if(vrc == VERR_S3_ACCESS_DENIED)
1674 throw setError(E_ACCESSDENIED,
1675 tr("Cannot download file '%s' from S3 storage server (Access denied). Make sure that your credentials are right. Also check that your host clock is properly synced"), pszFilename);
1676 else if(vrc == VERR_S3_NOT_FOUND)
1677 throw setError(VBOX_E_FILE_ERROR,
1678 tr("Cannot download file '%s' from S3 storage server (File not found)"), pszFilename);
1679 else
1680 throw setError(VBOX_E_IPRT_ERROR,
1681 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc);
1682 }
1683
1684 /* Close the connection early */
1685 RTS3Destroy(hS3);
1686 hS3 = NIL_RTS3;
1687
1688 if (!pTask->progress.isNull())
1689 pTask->progress->SetNextOperation(Bstr(tr("Reading")), 1);
1690
1691 /* Prepare the temporary reading of the OVF */
1692 ComObjPtr<Progress> progress;
1693 LocationInfo li;
1694 li.strPath = strTmpOvf;
1695 /* Start the reading from the fs */
1696 rc = readImpl(li, progress);
1697 if (FAILED(rc)) throw rc;
1698
1699 /* Unlock the appliance for the reading thread */
1700 appLock.release();
1701 /* Wait until the reading is done, but report the progress back to the
1702 caller */
1703 ComPtr<IProgress> progressInt(progress);
1704 waitForAsyncProgress(pTask->progress, progressInt); /* Any errors will be thrown */
1705
1706 /* Again lock the appliance for the next steps */
1707 appLock.acquire();
1708 }
1709 catch(HRESULT aRC)
1710 {
1711 rc = aRC;
1712 }
1713 /* Cleanup */
1714 RTS3Destroy(hS3);
1715 /* Delete all files which where temporary created */
1716 if (RTPathExists(strTmpOvf.c_str()))
1717 {
1718 vrc = RTFileDelete(strTmpOvf.c_str());
1719 if(RT_FAILURE(vrc))
1720 rc = setError(VBOX_E_FILE_ERROR,
1721 tr("Cannot delete file '%s' (%Rrc)"), strTmpOvf.c_str(), vrc);
1722 }
1723 /* Delete the temporary directory */
1724 if (RTPathExists(pszTmpDir))
1725 {
1726 vrc = RTDirRemove(pszTmpDir);
1727 if(RT_FAILURE(vrc))
1728 rc = setError(VBOX_E_FILE_ERROR,
1729 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);
1730 }
1731 if (pszTmpDir)
1732 RTStrFree(pszTmpDir);
1733
1734 pTask->rc = rc;
1735
1736 if (!pTask->progress.isNull())
1737 pTask->progress->notifyComplete(rc);
1738
1739 LogFlowFunc(("rc=%Rhrc\n", rc));
1740 LogFlowFuncLeave();
1741
1742 return VINF_SUCCESS;
1743}
1744
1745int Appliance::importFS(TaskImportOVF *pTask)
1746{
1747 LogFlowFuncEnter();
1748 LogFlowFunc(("Appliance %p\n", this));
1749
1750 AutoCaller autoCaller(this);
1751 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1752
1753 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
1754
1755 if (!isApplianceIdle())
1756 return VERR_ACCESS_DENIED;
1757
1758 // Change the appliance state so we can safely leave the lock while doing time-consuming
1759 // disk imports; also the below method calls do all kinds of locking which conflicts with
1760 // the appliance object lock
1761 m->state = Data::ApplianceImporting;
1762 appLock.release();
1763
1764 HRESULT rc = S_OK;
1765
1766 // rollback for errors:
1767 // a list of images that we created/imported
1768 list<MyHardDiskAttachment> llHardDiskAttachments;
1769 list< ComPtr<IMedium> > llHardDisksCreated;
1770 list<Bstr> llMachinesRegistered; // list of string UUIDs
1771
1772 ComPtr<ISession> session;
1773 bool fSessionOpen = false;
1774 rc = session.createInprocObject(CLSID_Session);
1775 if (FAILED(rc)) return rc;
1776
1777 const OVFReader &reader = *m->pReader;
1778 // this is safe to access because this thread only gets started
1779 // if pReader != NULL
1780
1781 /* If an manifest file exists, verify the content. Therefore we need all
1782 * files which are referenced by the OVF & the OVF itself */
1783 Utf8Str strMfFile = manifestFileName(pTask->locInfo.strPath);
1784 list<Utf8Str> filesList;
1785 if (RTPathExists(strMfFile.c_str()))
1786 {
1787 Utf8Str strSrcDir(pTask->locInfo.strPath);
1788 strSrcDir.stripFilename();
1789 /* Add every disks of every virtual system to an internal list */
1790 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
1791 for (it = m->virtualSystemDescriptions.begin();
1792 it != m->virtualSystemDescriptions.end();
1793 ++it)
1794 {
1795 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
1796 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
1797 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
1798 for (itH = avsdeHDs.begin();
1799 itH != avsdeHDs.end();
1800 ++itH)
1801 {
1802 VirtualSystemDescriptionEntry *vsdeHD = *itH;
1803 /* Find the disk from the OVF's disk list */
1804 DiskImagesMap::const_iterator itDiskImage = reader.m_mapDisks.find(vsdeHD->strRef);
1805 const DiskImage &di = itDiskImage->second;
1806 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str());
1807 filesList.push_back(strSrcFilePath);
1808 }
1809 }
1810 /* Create the test list */
1811 PRTMANIFESTTEST pTestList = (PRTMANIFESTTEST)RTMemAllocZ(sizeof(RTMANIFESTTEST)*(filesList.size()+1));
1812 pTestList[0].pszTestFile = (char*)pTask->locInfo.strPath.c_str();
1813 pTestList[0].pszTestDigest = (char*)m->strOVFSHA1Digest.c_str();
1814 int vrc = VINF_SUCCESS;
1815 size_t i = 1;
1816 list<Utf8Str>::const_iterator it1;
1817 for (it1 = filesList.begin();
1818 it1 != filesList.end();
1819 ++it1, ++i)
1820 {
1821 char* pszDigest;
1822 vrc = RTSha1Digest((*it1).c_str(), &pszDigest);
1823 pTestList[i].pszTestFile = (char*)(*it1).c_str();
1824 pTestList[i].pszTestDigest = pszDigest;
1825 }
1826 size_t cIndexOnError;
1827 vrc = RTManifestVerify(strMfFile.c_str(), pTestList, filesList.size() + 1, &cIndexOnError);
1828 if (vrc == VERR_MANIFEST_DIGEST_MISMATCH)
1829 rc = setError(VBOX_E_FILE_ERROR,
1830 tr("The SHA1 digest of '%s' doesn't match to the one in '%s'"),
1831 RTPathFilename(pTestList[cIndexOnError].pszTestFile),
1832 RTPathFilename(strMfFile.c_str()));
1833 else if (RT_FAILURE(vrc))
1834 rc = setError(VBOX_E_FILE_ERROR,
1835 tr("Couldn't verify the content of '%s' against the available files (%Rrc)"),
1836 RTPathFilename(strMfFile.c_str()),
1837 vrc);
1838 /* Cleanup */
1839 for (size_t j = 1;
1840 j < filesList.size();
1841 ++j)
1842 RTStrFree(pTestList[j].pszTestDigest);
1843 RTMemFree(pTestList);
1844 if (FAILED(rc))
1845 {
1846 /* Return on error */
1847 pTask->rc = rc;
1848
1849 if (!pTask->progress.isNull())
1850 pTask->progress->notifyComplete(rc);
1851 return rc;
1852 }
1853 }
1854
1855 list<VirtualSystem>::const_iterator it;
1856 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1;
1857 /* Iterate through all virtual systems of that appliance */
1858 size_t i = 0;
1859 for (it = reader.m_llVirtualSystems.begin(),
1860 it1 = m->virtualSystemDescriptions.begin();
1861 it != reader.m_llVirtualSystems.end();
1862 ++it, ++it1, ++i)
1863 {
1864 const VirtualSystem &vsysThis = *it;
1865 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it1);
1866
1867 ComPtr<IMachine> pNewMachine;
1868
1869 /* Catch possible errors */
1870 try
1871 {
1872 /* Guest OS type */
1873 std::list<VirtualSystemDescriptionEntry*> vsdeOS;
1874 vsdeOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
1875 if (vsdeOS.size() < 1)
1876 throw setError(VBOX_E_FILE_ERROR,
1877 tr("Missing guest OS type"));
1878 const Utf8Str &strOsTypeVBox = vsdeOS.front()->strVbox;
1879
1880 /* Now that we know the base system get our internal defaults based on that. */
1881 ComPtr<IGuestOSType> osType;
1882 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), osType.asOutParam());
1883 if (FAILED(rc)) throw rc;
1884
1885 /* Create the machine */
1886 /* First get the name */
1887 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
1888 if (vsdeName.size() < 1)
1889 throw setError(VBOX_E_FILE_ERROR,
1890 tr("Missing VM name"));
1891 const Utf8Str &strNameVBox = vsdeName.front()->strVbox;
1892 rc = mVirtualBox->CreateMachine(Bstr(strNameVBox), Bstr(strOsTypeVBox),
1893 Bstr(), Bstr(), FALSE,
1894 pNewMachine.asOutParam());
1895 if (FAILED(rc)) throw rc;
1896
1897 // and the description
1898 std::list<VirtualSystemDescriptionEntry*> vsdeDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description);
1899 if (vsdeDescription.size())
1900 {
1901 const Utf8Str &strDescription = vsdeDescription.front()->strVbox;
1902 rc = pNewMachine->COMSETTER(Description)(Bstr(strDescription));
1903 if (FAILED(rc)) throw rc;
1904 }
1905
1906 /* CPU count */
1907 std::list<VirtualSystemDescriptionEntry*> vsdeCPU = vsdescThis->findByType(VirtualSystemDescriptionType_CPU);
1908 ComAssertMsgThrow(vsdeCPU.size() == 1, ("CPU count missing"), E_FAIL);
1909 const Utf8Str &cpuVBox = vsdeCPU.front()->strVbox;
1910 ULONG tmpCount = (ULONG)RTStrToUInt64(cpuVBox.c_str());
1911 rc = pNewMachine->COMSETTER(CPUCount)(tmpCount);
1912 if (FAILED(rc)) throw rc;
1913 bool fEnableIOApic = false;
1914 /* We need HWVirt & IO-APIC if more than one CPU is requested */
1915 if (tmpCount > 1)
1916 {
1917 rc = pNewMachine->SetHWVirtExProperty(HWVirtExPropertyType_Enabled, TRUE);
1918 if (FAILED(rc)) throw rc;
1919
1920 fEnableIOApic = true;
1921 }
1922
1923 /* RAM */
1924 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory);
1925 ComAssertMsgThrow(vsdeRAM.size() == 1, ("RAM size missing"), E_FAIL);
1926 const Utf8Str &memoryVBox = vsdeRAM.front()->strVbox;
1927 ULONG tt = (ULONG)RTStrToUInt64(memoryVBox.c_str());
1928 rc = pNewMachine->COMSETTER(MemorySize)(tt);
1929 if (FAILED(rc)) throw rc;
1930
1931 /* VRAM */
1932 /* Get the recommended VRAM for this guest OS type */
1933 ULONG vramVBox;
1934 rc = osType->COMGETTER(RecommendedVRAM)(&vramVBox);
1935 if (FAILED(rc)) throw rc;
1936
1937 /* Set the VRAM */
1938 rc = pNewMachine->COMSETTER(VRAMSize)(vramVBox);
1939 if (FAILED(rc)) throw rc;
1940
1941 /* I/O APIC: so far we have no setting for this. Enable it if we
1942 import a Windows VM because if if Windows was installed without IOAPIC,
1943 it will not mind finding an one later on, but if Windows was installed
1944 _with_ an IOAPIC, it will bluescreen if it's not found */
1945 Bstr bstrFamilyId;
1946 rc = osType->COMGETTER(FamilyId)(bstrFamilyId.asOutParam());
1947 if (FAILED(rc)) throw rc;
1948
1949 Utf8Str strFamilyId(bstrFamilyId);
1950 if (strFamilyId == "Windows")
1951 fEnableIOApic = true;
1952
1953 /* If IP-APIC should be enabled could be have different reasons.
1954 See CPU count & the Win test above. Here we enable it if it was
1955 previously requested. */
1956 if (fEnableIOApic)
1957 {
1958 ComPtr<IBIOSSettings> pBIOSSettings;
1959 rc = pNewMachine->COMGETTER(BIOSSettings)(pBIOSSettings.asOutParam());
1960 if (FAILED(rc)) throw rc;
1961
1962 rc = pBIOSSettings->COMSETTER(IOAPICEnabled)(TRUE);
1963 if (FAILED(rc)) throw rc;
1964 }
1965
1966 /* Audio Adapter */
1967 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard);
1968 /* @todo: we support one audio adapter only */
1969 if (vsdeAudioAdapter.size() > 0)
1970 {
1971 const Utf8Str& audioAdapterVBox = vsdeAudioAdapter.front()->strVbox;
1972 if (audioAdapterVBox.compare("null", Utf8Str::CaseInsensitive) != 0)
1973 {
1974 uint32_t audio = RTStrToUInt32(audioAdapterVBox.c_str());
1975 ComPtr<IAudioAdapter> audioAdapter;
1976 rc = pNewMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
1977 if (FAILED(rc)) throw rc;
1978 rc = audioAdapter->COMSETTER(Enabled)(true);
1979 if (FAILED(rc)) throw rc;
1980 rc = audioAdapter->COMSETTER(AudioController)(static_cast<AudioControllerType_T>(audio));
1981 if (FAILED(rc)) throw rc;
1982 }
1983 }
1984
1985#ifdef VBOX_WITH_USB
1986 /* USB Controller */
1987 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController = vsdescThis->findByType(VirtualSystemDescriptionType_USBController);
1988 // USB support is enabled if there's at least one such entry; to disable USB support,
1989 // the type of the USB item would have been changed to "ignore"
1990 bool fUSBEnabled = vsdeUSBController.size() > 0;
1991
1992 ComPtr<IUSBController> usbController;
1993 rc = pNewMachine->COMGETTER(USBController)(usbController.asOutParam());
1994 if (FAILED(rc)) throw rc;
1995 rc = usbController->COMSETTER(Enabled)(fUSBEnabled);
1996 if (FAILED(rc)) throw rc;
1997#endif /* VBOX_WITH_USB */
1998
1999 /* Change the network adapters */
2000 std::list<VirtualSystemDescriptionEntry*> vsdeNW = vsdescThis->findByType(VirtualSystemDescriptionType_NetworkAdapter);
2001 if (vsdeNW.size() == 0)
2002 {
2003 /* No network adapters, so we have to disable our default one */
2004 ComPtr<INetworkAdapter> nwVBox;
2005 rc = pNewMachine->GetNetworkAdapter(0, nwVBox.asOutParam());
2006 if (FAILED(rc)) throw rc;
2007 rc = nwVBox->COMSETTER(Enabled)(false);
2008 if (FAILED(rc)) throw rc;
2009 }
2010 else
2011 {
2012 list<VirtualSystemDescriptionEntry*>::const_iterator nwIt;
2013 /* Iterate through all network cards. We support 8 network adapters
2014 * at the maximum. (@todo: warn if there are more!) */
2015 size_t a = 0;
2016 for (nwIt = vsdeNW.begin();
2017 (nwIt != vsdeNW.end() && a < SchemaDefs::NetworkAdapterCount);
2018 ++nwIt, ++a)
2019 {
2020 const VirtualSystemDescriptionEntry* pvsys = *nwIt;
2021
2022 const Utf8Str &nwTypeVBox = pvsys->strVbox;
2023 uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str());
2024 ComPtr<INetworkAdapter> pNetworkAdapter;
2025 rc = pNewMachine->GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
2026 if (FAILED(rc)) throw rc;
2027 /* Enable the network card & set the adapter type */
2028 rc = pNetworkAdapter->COMSETTER(Enabled)(true);
2029 if (FAILED(rc)) throw rc;
2030 rc = pNetworkAdapter->COMSETTER(AdapterType)(static_cast<NetworkAdapterType_T>(tt1));
2031 if (FAILED(rc)) throw rc;
2032
2033 // default is NAT; change to "bridged" if extra conf says so
2034 if (!pvsys->strExtraConfig.compare("type=Bridged", Utf8Str::CaseInsensitive))
2035 {
2036 /* Attach to the right interface */
2037 rc = pNetworkAdapter->AttachToBridgedInterface();
2038 if (FAILED(rc)) throw rc;
2039 ComPtr<IHost> host;
2040 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam());
2041 if (FAILED(rc)) throw rc;
2042 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;
2043 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));
2044 if (FAILED(rc)) throw rc;
2045 /* We search for the first host network interface which
2046 * is usable for bridged networking */
2047 for (size_t j = 0;
2048 j < nwInterfaces.size();
2049 ++j)
2050 {
2051 HostNetworkInterfaceType_T itype;
2052 rc = nwInterfaces[j]->COMGETTER(InterfaceType)(&itype);
2053 if (FAILED(rc)) throw rc;
2054 if (itype == HostNetworkInterfaceType_Bridged)
2055 {
2056 Bstr name;
2057 rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam());
2058 if (FAILED(rc)) throw rc;
2059 /* Set the interface name to attach to */
2060 pNetworkAdapter->COMSETTER(HostInterface)(name);
2061 if (FAILED(rc)) throw rc;
2062 break;
2063 }
2064 }
2065 }
2066 /* Next test for host only interfaces */
2067 else if (!pvsys->strExtraConfig.compare("type=HostOnly", Utf8Str::CaseInsensitive))
2068 {
2069 /* Attach to the right interface */
2070 rc = pNetworkAdapter->AttachToHostOnlyInterface();
2071 if (FAILED(rc)) throw rc;
2072 ComPtr<IHost> host;
2073 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam());
2074 if (FAILED(rc)) throw rc;
2075 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;
2076 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));
2077 if (FAILED(rc)) throw rc;
2078 /* We search for the first host network interface which
2079 * is usable for host only networking */
2080 for (size_t j = 0;
2081 j < nwInterfaces.size();
2082 ++j)
2083 {
2084 HostNetworkInterfaceType_T itype;
2085 rc = nwInterfaces[j]->COMGETTER(InterfaceType)(&itype);
2086 if (FAILED(rc)) throw rc;
2087 if (itype == HostNetworkInterfaceType_HostOnly)
2088 {
2089 Bstr name;
2090 rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam());
2091 if (FAILED(rc)) throw rc;
2092 /* Set the interface name to attach to */
2093 pNetworkAdapter->COMSETTER(HostInterface)(name);
2094 if (FAILED(rc)) throw rc;
2095 break;
2096 }
2097 }
2098 }
2099 }
2100 }
2101
2102 /* Hard disk controller IDE */
2103 std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerIDE);
2104 if (vsdeHDCIDE.size() > 1)
2105 throw setError(VBOX_E_FILE_ERROR,
2106 tr("Too many IDE controllers in OVF; import facility only supports one"));
2107 if (vsdeHDCIDE.size() == 1)
2108 {
2109 ComPtr<IStorageController> pController;
2110 rc = pNewMachine->AddStorageController(Bstr("IDE Controller"), StorageBus_IDE, pController.asOutParam());
2111 if (FAILED(rc)) throw rc;
2112
2113 const char *pcszIDEType = vsdeHDCIDE.front()->strVbox.c_str();
2114 if (!strcmp(pcszIDEType, "PIIX3"))
2115 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX3);
2116 else if (!strcmp(pcszIDEType, "PIIX4"))
2117 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX4);
2118 else if (!strcmp(pcszIDEType, "ICH6"))
2119 rc = pController->COMSETTER(ControllerType)(StorageControllerType_ICH6);
2120 else
2121 throw setError(VBOX_E_FILE_ERROR,
2122 tr("Invalid IDE controller type \"%s\""),
2123 pcszIDEType);
2124 if (FAILED(rc)) throw rc;
2125 }
2126#ifdef VBOX_WITH_AHCI
2127 /* Hard disk controller SATA */
2128 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSATA = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSATA);
2129 if (vsdeHDCSATA.size() > 1)
2130 throw setError(VBOX_E_FILE_ERROR,
2131 tr("Too many SATA controllers in OVF; import facility only supports one"));
2132 if (vsdeHDCSATA.size() > 0)
2133 {
2134 ComPtr<IStorageController> pController;
2135 const Utf8Str &hdcVBox = vsdeHDCSATA.front()->strVbox;
2136 if (hdcVBox == "AHCI")
2137 {
2138 rc = pNewMachine->AddStorageController(Bstr("SATA Controller"), StorageBus_SATA, pController.asOutParam());
2139 if (FAILED(rc)) throw rc;
2140 }
2141 else
2142 throw setError(VBOX_E_FILE_ERROR,
2143 tr("Invalid SATA controller type \"%s\""),
2144 hdcVBox.c_str());
2145 }
2146#endif /* VBOX_WITH_AHCI */
2147
2148#ifdef VBOX_WITH_LSILOGIC
2149 /* Hard disk controller SCSI */
2150 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSCSI = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSCSI);
2151 if (vsdeHDCSCSI.size() > 1)
2152 throw setError(VBOX_E_FILE_ERROR,
2153 tr("Too many SCSI controllers in OVF; import facility only supports one"));
2154 if (vsdeHDCSCSI.size() > 0)
2155 {
2156 ComPtr<IStorageController> pController;
2157 StorageControllerType_T controllerType;
2158 const Utf8Str &hdcVBox = vsdeHDCSCSI.front()->strVbox;
2159 if (hdcVBox == "LsiLogic")
2160 controllerType = StorageControllerType_LsiLogic;
2161 else if (hdcVBox == "BusLogic")
2162 controllerType = StorageControllerType_BusLogic;
2163 else
2164 throw setError(VBOX_E_FILE_ERROR,
2165 tr("Invalid SCSI controller type \"%s\""),
2166 hdcVBox.c_str());
2167
2168 rc = pNewMachine->AddStorageController(Bstr("SCSI Controller"), StorageBus_SCSI, pController.asOutParam());
2169 if (FAILED(rc)) throw rc;
2170 rc = pController->COMSETTER(ControllerType)(controllerType);
2171 if (FAILED(rc)) throw rc;
2172 }
2173#endif /* VBOX_WITH_LSILOGIC */
2174
2175 /* Now its time to register the machine before we add any hard disks */
2176 rc = mVirtualBox->RegisterMachine(pNewMachine);
2177 if (FAILED(rc)) throw rc;
2178
2179 Bstr bstrNewMachineId;
2180 rc = pNewMachine->COMGETTER(Id)(bstrNewMachineId.asOutParam());
2181 if (FAILED(rc)) throw rc;
2182
2183 // store new machine for roll-back in case of errors
2184 llMachinesRegistered.push_back(bstrNewMachineId);
2185
2186 // Add floppies and CD-ROMs to the appropriate controllers.
2187 std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsdescThis->findByType(VirtualSystemDescriptionType_Floppy);
2188 if (vsdeFloppy.size() > 1)
2189 throw setError(VBOX_E_FILE_ERROR,
2190 tr("Too many floppy controllers in OVF; import facility only supports one"));
2191 std::list<VirtualSystemDescriptionEntry*> vsdeCDROM = vsdescThis->findByType(VirtualSystemDescriptionType_CDROM);
2192 if ( (vsdeFloppy.size() > 0)
2193 || (vsdeCDROM.size() > 0)
2194 )
2195 {
2196 // If there's an error here we need to close the session, so
2197 // we need another try/catch block.
2198
2199 try
2200 {
2201 /* In order to attach things we need to open a session
2202 * for the new machine */
2203 rc = mVirtualBox->OpenSession(session, bstrNewMachineId);
2204 if (FAILED(rc)) throw rc;
2205 fSessionOpen = true;
2206
2207 ComPtr<IMachine> sMachine;
2208 rc = session->COMGETTER(Machine)(sMachine.asOutParam());
2209 if (FAILED(rc)) throw rc;
2210
2211 // floppy first
2212 if (vsdeFloppy.size() == 1)
2213 {
2214 ComPtr<IStorageController> pController;
2215 rc = sMachine->AddStorageController(Bstr("Floppy Controller"), StorageBus_Floppy, pController.asOutParam());
2216 if (FAILED(rc)) throw rc;
2217
2218 Bstr bstrName;
2219 rc = pController->COMGETTER(Name)(bstrName.asOutParam());
2220 if (FAILED(rc)) throw rc;
2221
2222 // this is for rollback later
2223 MyHardDiskAttachment mhda;
2224 mhda.bstrUuid = bstrNewMachineId;
2225 mhda.pMachine = pNewMachine;
2226 mhda.controllerType = bstrName;
2227 mhda.lChannel = 0;
2228 mhda.lDevice = 0;
2229
2230 Log(("Attaching floppy\n"));
2231
2232 rc = sMachine->AttachDevice(mhda.controllerType,
2233 mhda.lChannel,
2234 mhda.lDevice,
2235 DeviceType_Floppy,
2236 NULL);
2237 if (FAILED(rc)) throw rc;
2238
2239 llHardDiskAttachments.push_back(mhda);
2240 }
2241
2242
2243 // CD-ROMs next
2244 for (std::list<VirtualSystemDescriptionEntry*>::const_iterator jt = vsdeCDROM.begin();
2245 jt != vsdeCDROM.end();
2246 ++jt)
2247 {
2248 // for now always attach to secondary master on IDE controller;
2249 // there seems to be no useful information in OVF where else to
2250 // attach jt (@todo test with latest versions of OVF software)
2251
2252 // find the IDE controller
2253 const HardDiskController *pController = NULL;
2254 for (ControllersMap::const_iterator kt = vsysThis.mapControllers.begin();
2255 kt != vsysThis.mapControllers.end();
2256 ++kt)
2257 {
2258 if (kt->second.system == HardDiskController::IDE)
2259 {
2260 pController = &kt->second;
2261 }
2262 }
2263
2264 if (!pController)
2265 throw setError(VBOX_E_FILE_ERROR,
2266 tr("OVF wants a CD-ROM drive but cannot find IDE controller, which is required in this version of VirtualBox"));
2267
2268 // this is for rollback later
2269 MyHardDiskAttachment mhda;
2270 mhda.bstrUuid = bstrNewMachineId;
2271 mhda.pMachine = pNewMachine;
2272
2273 ConvertDiskAttachmentValues(*pController,
2274 2, // interpreted as secondary master
2275 mhda.controllerType, // Bstr
2276 mhda.lChannel,
2277 mhda.lDevice);
2278
2279 Log(("Attaching CD-ROM to channel %d on device %d\n", mhda.lChannel, mhda.lDevice));
2280
2281 rc = sMachine->AttachDevice(mhda.controllerType,
2282 mhda.lChannel,
2283 mhda.lDevice,
2284 DeviceType_DVD,
2285 NULL);
2286 if (FAILED(rc)) throw rc;
2287
2288 llHardDiskAttachments.push_back(mhda);
2289 } // end for (itHD = avsdeHDs.begin();
2290
2291 rc = sMachine->SaveSettings();
2292 if (FAILED(rc)) throw rc;
2293
2294 // only now that we're done with all disks, close the session
2295 rc = session->Close();
2296 if (FAILED(rc)) throw rc;
2297 fSessionOpen = false;
2298 }
2299 catch(HRESULT /* aRC */)
2300 {
2301 if (fSessionOpen)
2302 session->Close();
2303
2304 throw;
2305 }
2306 }
2307
2308 /* Create the hard disks & connect them to the appropriate controllers. */
2309 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
2310 if (avsdeHDs.size() > 0)
2311 {
2312 // If there's an error here we need to close the session, so
2313 // we need another try/catch block.
2314 ComPtr<IMedium> srcHdVBox;
2315 bool fSourceHdNeedsClosing = false;
2316
2317 try
2318 {
2319 /* In order to attach hard disks we need to open a session
2320 * for the new machine */
2321 rc = mVirtualBox->OpenSession(session, bstrNewMachineId);
2322 if (FAILED(rc)) throw rc;
2323 fSessionOpen = true;
2324
2325 /* The disk image has to be on the same place as the OVF file. So
2326 * strip the filename out of the full file path. */
2327 Utf8Str strSrcDir(pTask->locInfo.strPath);
2328 strSrcDir.stripFilename();
2329
2330 /* Iterate over all given disk images */
2331 list<VirtualSystemDescriptionEntry*>::const_iterator itHD;
2332 for (itHD = avsdeHDs.begin();
2333 itHD != avsdeHDs.end();
2334 ++itHD)
2335 {
2336 VirtualSystemDescriptionEntry *vsdeHD = *itHD;
2337
2338 /* Check if the destination file exists already or the
2339 * destination path is empty. */
2340 if ( vsdeHD->strVbox.isEmpty()
2341 || RTPathExists(vsdeHD->strVbox.c_str())
2342 )
2343 /* This isn't allowed */
2344 throw setError(VBOX_E_FILE_ERROR,
2345 tr("Destination file '%s' exists",
2346 vsdeHD->strVbox.c_str()));
2347
2348 /* Find the disk from the OVF's disk list */
2349 DiskImagesMap::const_iterator itDiskImage = reader.m_mapDisks.find(vsdeHD->strRef);
2350 /* vsdeHD->strRef contains the disk identifier (e.g. "vmdisk1"), which should exist
2351 in the virtual system's disks map under that ID and also in the global images map. */
2352 VirtualDisksMap::const_iterator itVirtualDisk = vsysThis.mapVirtualDisks.find(vsdeHD->strRef);
2353
2354 if ( itDiskImage == reader.m_mapDisks.end()
2355 || itVirtualDisk == vsysThis.mapVirtualDisks.end()
2356 )
2357 throw setError(E_FAIL,
2358 tr("Internal inconsistency looking up disk images."));
2359
2360 const DiskImage &di = itDiskImage->second;
2361 const VirtualDisk &vd = itVirtualDisk->second;
2362
2363 /* Make sure all target directories exists */
2364 rc = VirtualBox::ensureFilePathExists(vsdeHD->strVbox.c_str());
2365 if (FAILED(rc))
2366 throw rc;
2367
2368 // subprogress object for hard disk
2369 ComPtr<IProgress> pProgress2;
2370
2371 ComPtr<IMedium> dstHdVBox;
2372 /* If strHref is empty we have to create a new file */
2373 if (di.strHref.isEmpty())
2374 {
2375 /* Which format to use? */
2376 Bstr srcFormat = L"VDI";
2377 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)
2378 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive))
2379 srcFormat = L"VMDK";
2380 /* Create an empty hard disk */
2381 rc = mVirtualBox->CreateHardDisk(srcFormat, Bstr(vsdeHD->strVbox), dstHdVBox.asOutParam());
2382 if (FAILED(rc)) throw rc;
2383
2384 /* Create a dynamic growing disk image with the given capacity */
2385 rc = dstHdVBox->CreateBaseStorage(di.iCapacity / _1M, MediumVariant_Standard, pProgress2.asOutParam());
2386 if (FAILED(rc)) throw rc;
2387
2388 /* Advance to the next operation */
2389 if (!pTask->progress.isNull())
2390 pTask->progress->SetNextOperation(BstrFmt(tr("Creating virtual disk image '%s'"), vsdeHD->strVbox.c_str()),
2391 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally
2392 }
2393 else
2394 {
2395 /* Construct the source file path */
2396 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str());
2397 /* Check if the source file exists */
2398 if (!RTPathExists(strSrcFilePath.c_str()))
2399 /* This isn't allowed */
2400 throw setError(VBOX_E_FILE_ERROR,
2401 tr("Source virtual disk image file '%s' doesn't exist"),
2402 strSrcFilePath.c_str());
2403
2404 /* Clone the disk image (this is necessary cause the id has
2405 * to be recreated for the case the same hard disk is
2406 * attached already from a previous import) */
2407
2408 /* First open the existing disk image */
2409 rc = mVirtualBox->OpenHardDisk(Bstr(strSrcFilePath),
2410 AccessMode_ReadOnly,
2411 false,
2412 NULL,
2413 false,
2414 NULL,
2415 srcHdVBox.asOutParam());
2416 if (FAILED(rc)) throw rc;
2417 fSourceHdNeedsClosing = true;
2418
2419 /* We need the format description of the source disk image */
2420 Bstr srcFormat;
2421 rc = srcHdVBox->COMGETTER(Format)(srcFormat.asOutParam());
2422 if (FAILED(rc)) throw rc;
2423 /* Create a new hard disk interface for the destination disk image */
2424 rc = mVirtualBox->CreateHardDisk(srcFormat, Bstr(vsdeHD->strVbox), dstHdVBox.asOutParam());
2425 if (FAILED(rc)) throw rc;
2426 /* Clone the source disk image */
2427 rc = srcHdVBox->CloneTo(dstHdVBox, MediumVariant_Standard, NULL, pProgress2.asOutParam());
2428 if (FAILED(rc)) throw rc;
2429
2430 /* Advance to the next operation */
2431 if (!pTask->progress.isNull())
2432 pTask->progress->SetNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), strSrcFilePath.c_str()),
2433 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally);
2434 }
2435
2436 // now wait for the background disk operation to complete; this throws HRESULTs on error
2437 waitForAsyncProgress(pTask->progress, pProgress2);
2438
2439 if (fSourceHdNeedsClosing)
2440 {
2441 rc = srcHdVBox->Close();
2442 if (FAILED(rc)) throw rc;
2443 fSourceHdNeedsClosing = false;
2444 }
2445
2446 llHardDisksCreated.push_back(dstHdVBox);
2447 /* Now use the new uuid to attach the disk image to our new machine */
2448 ComPtr<IMachine> sMachine;
2449 rc = session->COMGETTER(Machine)(sMachine.asOutParam());
2450 if (FAILED(rc)) throw rc;
2451 Bstr hdId;
2452 rc = dstHdVBox->COMGETTER(Id)(hdId.asOutParam());
2453 if (FAILED(rc)) throw rc;
2454
2455 /* For now we assume we have one controller of every type only */
2456 HardDiskController hdc = (*vsysThis.mapControllers.find(vd.idController)).second;
2457
2458 // this is for rollback later
2459 MyHardDiskAttachment mhda;
2460 mhda.bstrUuid = bstrNewMachineId;
2461 mhda.pMachine = pNewMachine;
2462
2463 ConvertDiskAttachmentValues(hdc,
2464 vd.ulAddressOnParent,
2465 mhda.controllerType, // Bstr
2466 mhda.lChannel,
2467 mhda.lDevice);
2468
2469 Log(("Attaching disk %s to channel %d on device %d\n", vsdeHD->strVbox.c_str(), mhda.lChannel, mhda.lDevice));
2470
2471 rc = sMachine->AttachDevice(mhda.controllerType,
2472 mhda.lChannel,
2473 mhda.lDevice,
2474 DeviceType_HardDisk,
2475 hdId);
2476 if (FAILED(rc)) throw rc;
2477
2478 llHardDiskAttachments.push_back(mhda);
2479
2480 rc = sMachine->SaveSettings();
2481 if (FAILED(rc)) throw rc;
2482 } // end for (itHD = avsdeHDs.begin();
2483
2484 // only now that we're done with all disks, close the session
2485 rc = session->Close();
2486 if (FAILED(rc)) throw rc;
2487 fSessionOpen = false;
2488 }
2489 catch(HRESULT /* aRC */)
2490 {
2491 if (fSourceHdNeedsClosing)
2492 srcHdVBox->Close();
2493
2494 if (fSessionOpen)
2495 session->Close();
2496
2497 throw;
2498 }
2499 }
2500 }
2501 catch(HRESULT aRC)
2502 {
2503 rc = aRC;
2504 }
2505
2506 if (FAILED(rc))
2507 break;
2508
2509 } // for (it = pAppliance->m->llVirtualSystems.begin(),
2510
2511 if (FAILED(rc))
2512 {
2513 // with _whatever_ error we've had, do a complete roll-back of
2514 // machines and disks we've created; unfortunately this is
2515 // not so trivially done...
2516
2517 HRESULT rc2;
2518 // detach all hard disks from all machines we created
2519 list<MyHardDiskAttachment>::iterator itM;
2520 for (itM = llHardDiskAttachments.begin();
2521 itM != llHardDiskAttachments.end();
2522 ++itM)
2523 {
2524 const MyHardDiskAttachment &mhda = *itM;
2525 Bstr bstrUuid(mhda.bstrUuid); // make a copy, Windows can't handle const Bstr
2526 rc2 = mVirtualBox->OpenSession(session, bstrUuid);
2527 if (SUCCEEDED(rc2))
2528 {
2529 ComPtr<IMachine> sMachine;
2530 rc2 = session->COMGETTER(Machine)(sMachine.asOutParam());
2531 if (SUCCEEDED(rc2))
2532 {
2533 rc2 = sMachine->DetachDevice(Bstr(mhda.controllerType), mhda.lChannel, mhda.lDevice);
2534 rc2 = sMachine->SaveSettings();
2535 }
2536 session->Close();
2537 }
2538 }
2539
2540 // now clean up all hard disks we created
2541 list< ComPtr<IMedium> >::iterator itHD;
2542 for (itHD = llHardDisksCreated.begin();
2543 itHD != llHardDisksCreated.end();
2544 ++itHD)
2545 {
2546 ComPtr<IMedium> pDisk = *itHD;
2547 ComPtr<IProgress> pProgress;
2548 rc2 = pDisk->DeleteStorage(pProgress.asOutParam());
2549 rc2 = pProgress->WaitForCompletion(-1);
2550 }
2551
2552 // finally, deregister and remove all machines
2553 list<Bstr>::iterator itID;
2554 for (itID = llMachinesRegistered.begin();
2555 itID != llMachinesRegistered.end();
2556 ++itID)
2557 {
2558 Bstr bstrGuid = *itID; // make a copy, Windows can't handle const Bstr
2559 ComPtr<IMachine> failedMachine;
2560 rc2 = mVirtualBox->UnregisterMachine(bstrGuid, failedMachine.asOutParam());
2561 if (SUCCEEDED(rc2))
2562 rc2 = failedMachine->DeleteSettings();
2563 }
2564 }
2565
2566 // restore the appliance state
2567 appLock.acquire();
2568 m->state = Data::ApplianceIdle;
2569
2570 pTask->rc = rc;
2571
2572 if (!pTask->progress.isNull())
2573 pTask->progress->notifyComplete(rc);
2574
2575 LogFlowFunc(("rc=%Rhrc\n", rc));
2576 LogFlowFuncLeave();
2577
2578 return VINF_SUCCESS;
2579}
2580
2581/**
2582 * Helper that converts VirtualSystem attachment values into VirtualBox attachment values.
2583 * Throws HRESULT values on errors!
2584 *
2585 * @param hdc
2586 * @param vd
2587 * @param mhda
2588 */
2589void Appliance::ConvertDiskAttachmentValues(const HardDiskController &hdc,
2590 uint32_t ulAddressOnParent,
2591 Bstr &controllerType,
2592 int32_t &lChannel,
2593 int32_t &lDevice)
2594{
2595 switch (hdc.system)
2596 {
2597 case HardDiskController::IDE:
2598 // For the IDE bus, the channel parameter can be either 0 or 1, to specify the primary
2599 // or secondary IDE controller, respectively. For the primary controller of the IDE bus,
2600 // the device number can be either 0 or 1, to specify the master or the slave device,
2601 // respectively. For the secondary IDE controller, the device number is always 1 because
2602 // the master device is reserved for the CD-ROM drive.
2603 controllerType = Bstr("IDE Controller");
2604 switch (ulAddressOnParent)
2605 {
2606 case 0: // interpret this as primary master
2607 lChannel = (long)0;
2608 lDevice = (long)0;
2609 break;
2610
2611 case 1: // interpret this as primary slave
2612 lChannel = (long)0;
2613 lDevice = (long)1;
2614 break;
2615
2616 case 2: // interpret this as secondary master
2617 lChannel = (long)1;
2618 lDevice = (long)0;
2619 break;
2620
2621 case 3: // interpret this as secondary slave
2622 lChannel = (long)1;
2623 lDevice = (long)1;
2624 break;
2625
2626 default:
2627 throw setError(VBOX_E_NOT_SUPPORTED,
2628 tr("Invalid channel %RI16 specified; IDE controllers support only 0, 1 or 2"), ulAddressOnParent);
2629 break;
2630 }
2631 break;
2632
2633 case HardDiskController::SATA:
2634 controllerType = Bstr("SATA Controller");
2635 lChannel = (long)ulAddressOnParent;
2636 lDevice = (long)0;
2637 break;
2638
2639 case HardDiskController::SCSI:
2640 controllerType = Bstr("SCSI Controller");
2641 lChannel = (long)ulAddressOnParent;
2642 lDevice = (long)0;
2643 break;
2644
2645 default: break;
2646 }
2647}
2648
2649int Appliance::importS3(TaskImportOVF *pTask)
2650{
2651 LogFlowFuncEnter();
2652 LogFlowFunc(("Appliance %p\n", this));
2653
2654 AutoCaller autoCaller(this);
2655 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2656
2657 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
2658
2659 int vrc = VINF_SUCCESS;
2660 RTS3 hS3 = NIL_RTS3;
2661 char szOSTmpDir[RTPATH_MAX];
2662 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));
2663 /* The template for the temporary directory created below */
2664 char *pszTmpDir;
2665 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir);
2666 list< pair<Utf8Str, ULONG> > filesList;
2667
2668 HRESULT rc = S_OK;
2669 try
2670 {
2671 /* Extract the bucket */
2672 Utf8Str tmpPath = pTask->locInfo.strPath;
2673 Utf8Str bucket;
2674 parseBucket(tmpPath, bucket);
2675
2676 /* We need a temporary directory which we can put the all disk images
2677 * in */
2678 vrc = RTDirCreateTemp(pszTmpDir);
2679 if (RT_FAILURE(vrc))
2680 throw setError(VBOX_E_FILE_ERROR,
2681 tr("Cannot create temporary directory '%s'"), pszTmpDir);
2682
2683 /* Add every disks of every virtual system to an internal list */
2684 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
2685 for (it = m->virtualSystemDescriptions.begin();
2686 it != m->virtualSystemDescriptions.end();
2687 ++it)
2688 {
2689 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
2690 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
2691 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
2692 for (itH = avsdeHDs.begin();
2693 itH != avsdeHDs.end();
2694 ++itH)
2695 {
2696 const Utf8Str &strTargetFile = (*itH)->strOvf;
2697 if (!strTargetFile.isEmpty())
2698 {
2699 /* The temporary name of the target disk file */
2700 Utf8StrFmt strTmpDisk("%s/%s", pszTmpDir, RTPathFilename(strTargetFile.c_str()));
2701 filesList.push_back(pair<Utf8Str, ULONG>(strTmpDisk, (*itH)->ulSizeMB));
2702 }
2703 }
2704 }
2705
2706 /* Next we have to download the disk images */
2707 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);
2708 if(RT_FAILURE(vrc))
2709 throw setError(VBOX_E_IPRT_ERROR,
2710 tr("Cannot create S3 service handler"));
2711 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask);
2712
2713 /* Download all files */
2714 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
2715 {
2716 const pair<Utf8Str, ULONG> &s = (*it1);
2717 const Utf8Str &strSrcFile = s.first;
2718 /* Construct the source file name */
2719 char *pszFilename = RTPathFilename(strSrcFile.c_str());
2720 /* Advance to the next operation */
2721 if (!pTask->progress.isNull())
2722 pTask->progress->SetNextOperation(BstrFmt(tr("Downloading file '%s'"), pszFilename), s.second);
2723
2724 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strSrcFile.c_str());
2725 if (RT_FAILURE(vrc))
2726 {
2727 if(vrc == VERR_S3_CANCELED)
2728 throw S_OK; /* todo: !!!!!!!!!!!!! */
2729 else if(vrc == VERR_S3_ACCESS_DENIED)
2730 throw setError(E_ACCESSDENIED,
2731 tr("Cannot download file '%s' from S3 storage server (Access denied). Make sure that your credentials are right. Also check that your host clock is properly synced"), pszFilename);
2732 else if(vrc == VERR_S3_NOT_FOUND)
2733 throw setError(VBOX_E_FILE_ERROR,
2734 tr("Cannot download file '%s' from S3 storage server (File not found)"), pszFilename);
2735 else
2736 throw setError(VBOX_E_IPRT_ERROR,
2737 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc);
2738 }
2739 }
2740
2741 /* Provide a OVF file (haven't to exist) so the import routine can
2742 * figure out where the disk images/manifest file are located. */
2743 Utf8StrFmt strTmpOvf("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str()));
2744 /* Now check if there is an manifest file. This is optional. */
2745 Utf8Str strManifestFile = manifestFileName(strTmpOvf);
2746 char *pszFilename = RTPathFilename(strManifestFile.c_str());
2747 if (!pTask->progress.isNull())
2748 pTask->progress->SetNextOperation(BstrFmt(tr("Downloading file '%s'"), pszFilename), 1);
2749
2750 /* Try to download it. If the error is VERR_S3_NOT_FOUND, it isn't fatal. */
2751 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strManifestFile.c_str());
2752 if (RT_SUCCESS(vrc))
2753 filesList.push_back(pair<Utf8Str, ULONG>(strManifestFile, 0));
2754 else if (RT_FAILURE(vrc))
2755 {
2756 if(vrc == VERR_S3_CANCELED)
2757 throw S_OK; /* todo: !!!!!!!!!!!!! */
2758 else if(vrc == VERR_S3_NOT_FOUND)
2759 vrc = VINF_SUCCESS; /* Not found is ok */
2760 else if(vrc == VERR_S3_ACCESS_DENIED)
2761 throw setError(E_ACCESSDENIED,
2762 tr("Cannot download file '%s' from S3 storage server (Access denied). Make sure that your credentials are right. Also check that your host clock is properly synced"), pszFilename);
2763 else
2764 throw setError(VBOX_E_IPRT_ERROR,
2765 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc);
2766 }
2767
2768 /* Close the connection early */
2769 RTS3Destroy(hS3);
2770 hS3 = NIL_RTS3;
2771
2772 if (!pTask->progress.isNull())
2773 pTask->progress->SetNextOperation(BstrFmt(tr("Importing appliance")), m->ulWeightPerOperation);
2774
2775 ComObjPtr<Progress> progress;
2776 /* Import the whole temporary OVF & the disk images */
2777 LocationInfo li;
2778 li.strPath = strTmpOvf;
2779 rc = importImpl(li, progress);
2780 if (FAILED(rc)) throw rc;
2781
2782 /* Unlock the appliance for the fs import thread */
2783 appLock.release();
2784 /* Wait until the import is done, but report the progress back to the
2785 caller */
2786 ComPtr<IProgress> progressInt(progress);
2787 waitForAsyncProgress(pTask->progress, progressInt); /* Any errors will be thrown */
2788
2789 /* Again lock the appliance for the next steps */
2790 appLock.acquire();
2791 }
2792 catch(HRESULT aRC)
2793 {
2794 rc = aRC;
2795 }
2796 /* Cleanup */
2797 RTS3Destroy(hS3);
2798 /* Delete all files which where temporary created */
2799 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
2800 {
2801 const char *pszFilePath = (*it1).first.c_str();
2802 if (RTPathExists(pszFilePath))
2803 {
2804 vrc = RTFileDelete(pszFilePath);
2805 if(RT_FAILURE(vrc))
2806 rc = setError(VBOX_E_FILE_ERROR,
2807 tr("Cannot delete file '%s' (%Rrc)"), pszFilePath, vrc);
2808 }
2809 }
2810 /* Delete the temporary directory */
2811 if (RTPathExists(pszTmpDir))
2812 {
2813 vrc = RTDirRemove(pszTmpDir);
2814 if(RT_FAILURE(vrc))
2815 rc = setError(VBOX_E_FILE_ERROR,
2816 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);
2817 }
2818 if (pszTmpDir)
2819 RTStrFree(pszTmpDir);
2820
2821 pTask->rc = rc;
2822
2823 if (!pTask->progress.isNull())
2824 pTask->progress->notifyComplete(rc);
2825
2826 LogFlowFunc(("rc=%Rhrc\n", rc));
2827 LogFlowFuncLeave();
2828
2829 return VINF_SUCCESS;
2830}
2831
2832int Appliance::TaskExportOVF::startThread()
2833{
2834 int vrc = RTThreadCreate(NULL, Appliance::taskThreadWriteOVF, this,
2835 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
2836 "Appliance::Task");
2837
2838 ComAssertMsgRCRet(vrc,
2839 ("Could not create taskThreadWriteOVF (%Rrc)\n", vrc), E_FAIL);
2840
2841 return S_OK;
2842}
2843
2844////////////////////////////////////////////////////////////////////////////////
2845//
2846// IVirtualSystemDescription constructor / destructor
2847//
2848////////////////////////////////////////////////////////////////////////////////
2849
2850DEFINE_EMPTY_CTOR_DTOR(VirtualSystemDescription)
2851
2852/**
2853 * COM initializer.
2854 * @return
2855 */
2856HRESULT VirtualSystemDescription::init()
2857{
2858 /* Enclose the state transition NotReady->InInit->Ready */
2859 AutoInitSpan autoInitSpan(this);
2860 AssertReturn(autoInitSpan.isOk(), E_FAIL);
2861
2862 /* Initialize data */
2863 m = new Data();
2864
2865 /* Confirm a successful initialization */
2866 autoInitSpan.setSucceeded();
2867 return S_OK;
2868}
2869
2870/**
2871* COM uninitializer.
2872*/
2873
2874void VirtualSystemDescription::uninit()
2875{
2876 delete m;
2877 m = NULL;
2878}
2879
2880////////////////////////////////////////////////////////////////////////////////
2881//
2882// IVirtualSystemDescription public methods
2883//
2884////////////////////////////////////////////////////////////////////////////////
2885
2886/**
2887 * Public method implementation.
2888 * @param
2889 * @return
2890 */
2891STDMETHODIMP VirtualSystemDescription::COMGETTER(Count)(ULONG *aCount)
2892{
2893 if (!aCount)
2894 return E_POINTER;
2895
2896 AutoCaller autoCaller(this);
2897 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2898
2899 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2900
2901 *aCount = (ULONG)m->llDescriptions.size();
2902
2903 return S_OK;
2904}
2905
2906/**
2907 * Public method implementation.
2908 * @return
2909 */
2910STDMETHODIMP VirtualSystemDescription::GetDescription(ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),
2911 ComSafeArrayOut(BSTR, aRefs),
2912 ComSafeArrayOut(BSTR, aOrigValues),
2913 ComSafeArrayOut(BSTR, aVboxValues),
2914 ComSafeArrayOut(BSTR, aExtraConfigValues))
2915{
2916 if (ComSafeArrayOutIsNull(aTypes) ||
2917 ComSafeArrayOutIsNull(aRefs) ||
2918 ComSafeArrayOutIsNull(aOrigValues) ||
2919 ComSafeArrayOutIsNull(aVboxValues) ||
2920 ComSafeArrayOutIsNull(aExtraConfigValues))
2921 return E_POINTER;
2922
2923 AutoCaller autoCaller(this);
2924 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2925
2926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2927
2928 ULONG c = (ULONG)m->llDescriptions.size();
2929 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);
2930 com::SafeArray<BSTR> sfaRefs(c);
2931 com::SafeArray<BSTR> sfaOrigValues(c);
2932 com::SafeArray<BSTR> sfaVboxValues(c);
2933 com::SafeArray<BSTR> sfaExtraConfigValues(c);
2934
2935 list<VirtualSystemDescriptionEntry>::const_iterator it;
2936 size_t i = 0;
2937 for (it = m->llDescriptions.begin();
2938 it != m->llDescriptions.end();
2939 ++it, ++i)
2940 {
2941 const VirtualSystemDescriptionEntry &vsde = (*it);
2942
2943 sfaTypes[i] = vsde.type;
2944
2945 Bstr bstr = vsde.strRef;
2946 bstr.cloneTo(&sfaRefs[i]);
2947
2948 bstr = vsde.strOvf;
2949 bstr.cloneTo(&sfaOrigValues[i]);
2950
2951 bstr = vsde.strVbox;
2952 bstr.cloneTo(&sfaVboxValues[i]);
2953
2954 bstr = vsde.strExtraConfig;
2955 bstr.cloneTo(&sfaExtraConfigValues[i]);
2956 }
2957
2958 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));
2959 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));
2960 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));
2961 sfaVboxValues.detachTo(ComSafeArrayOutArg(aVboxValues));
2962 sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues));
2963
2964 return S_OK;
2965}
2966
2967/**
2968 * Public method implementation.
2969 * @return
2970 */
2971STDMETHODIMP VirtualSystemDescription::GetDescriptionByType(VirtualSystemDescriptionType_T aType,
2972 ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),
2973 ComSafeArrayOut(BSTR, aRefs),
2974 ComSafeArrayOut(BSTR, aOrigValues),
2975 ComSafeArrayOut(BSTR, aVboxValues),
2976 ComSafeArrayOut(BSTR, aExtraConfigValues))
2977{
2978 if (ComSafeArrayOutIsNull(aTypes) ||
2979 ComSafeArrayOutIsNull(aRefs) ||
2980 ComSafeArrayOutIsNull(aOrigValues) ||
2981 ComSafeArrayOutIsNull(aVboxValues) ||
2982 ComSafeArrayOutIsNull(aExtraConfigValues))
2983 return E_POINTER;
2984
2985 AutoCaller autoCaller(this);
2986 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2987
2988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2989
2990 std::list<VirtualSystemDescriptionEntry*> vsd = findByType (aType);
2991 ULONG c = (ULONG)vsd.size();
2992 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);
2993 com::SafeArray<BSTR> sfaRefs(c);
2994 com::SafeArray<BSTR> sfaOrigValues(c);
2995 com::SafeArray<BSTR> sfaVboxValues(c);
2996 com::SafeArray<BSTR> sfaExtraConfigValues(c);
2997
2998 list<VirtualSystemDescriptionEntry*>::const_iterator it;
2999 size_t i = 0;
3000 for (it = vsd.begin();
3001 it != vsd.end();
3002 ++it, ++i)
3003 {
3004 const VirtualSystemDescriptionEntry *vsde = (*it);
3005
3006 sfaTypes[i] = vsde->type;
3007
3008 Bstr bstr = vsde->strRef;
3009 bstr.cloneTo(&sfaRefs[i]);
3010
3011 bstr = vsde->strOvf;
3012 bstr.cloneTo(&sfaOrigValues[i]);
3013
3014 bstr = vsde->strVbox;
3015 bstr.cloneTo(&sfaVboxValues[i]);
3016
3017 bstr = vsde->strExtraConfig;
3018 bstr.cloneTo(&sfaExtraConfigValues[i]);
3019 }
3020
3021 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));
3022 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));
3023 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));
3024 sfaVboxValues.detachTo(ComSafeArrayOutArg(aVboxValues));
3025 sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues));
3026
3027 return S_OK;
3028}
3029
3030/**
3031 * Public method implementation.
3032 * @return
3033 */
3034STDMETHODIMP VirtualSystemDescription::GetValuesByType(VirtualSystemDescriptionType_T aType,
3035 VirtualSystemDescriptionValueType_T aWhich,
3036 ComSafeArrayOut(BSTR, aValues))
3037{
3038 if (ComSafeArrayOutIsNull(aValues))
3039 return E_POINTER;
3040
3041 AutoCaller autoCaller(this);
3042 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3043
3044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3045
3046 std::list<VirtualSystemDescriptionEntry*> vsd = findByType (aType);
3047 com::SafeArray<BSTR> sfaValues((ULONG)vsd.size());
3048
3049 list<VirtualSystemDescriptionEntry*>::const_iterator it;
3050 size_t i = 0;
3051 for (it = vsd.begin();
3052 it != vsd.end();
3053 ++it, ++i)
3054 {
3055 const VirtualSystemDescriptionEntry *vsde = (*it);
3056
3057 Bstr bstr;
3058 switch (aWhich)
3059 {
3060 case VirtualSystemDescriptionValueType_Reference: bstr = vsde->strRef; break;
3061 case VirtualSystemDescriptionValueType_Original: bstr = vsde->strOvf; break;
3062 case VirtualSystemDescriptionValueType_Auto: bstr = vsde->strVbox; break;
3063 case VirtualSystemDescriptionValueType_ExtraConfig: bstr = vsde->strExtraConfig; break;
3064 }
3065
3066 bstr.cloneTo(&sfaValues[i]);
3067 }
3068
3069 sfaValues.detachTo(ComSafeArrayOutArg(aValues));
3070
3071 return S_OK;
3072}
3073
3074/**
3075 * Public method implementation.
3076 * @return
3077 */
3078STDMETHODIMP VirtualSystemDescription::SetFinalValues(ComSafeArrayIn(BOOL, aEnabled),
3079 ComSafeArrayIn(IN_BSTR, argVboxValues),
3080 ComSafeArrayIn(IN_BSTR, argExtraConfigValues))
3081{
3082#ifndef RT_OS_WINDOWS
3083 NOREF(aEnabledSize);
3084#endif /* RT_OS_WINDOWS */
3085
3086 CheckComArgSafeArrayNotNull(aEnabled);
3087 CheckComArgSafeArrayNotNull(argVboxValues);
3088 CheckComArgSafeArrayNotNull(argExtraConfigValues);
3089
3090 AutoCaller autoCaller(this);
3091 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3092
3093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3094
3095 com::SafeArray<BOOL> sfaEnabled(ComSafeArrayInArg(aEnabled));
3096 com::SafeArray<IN_BSTR> sfaVboxValues(ComSafeArrayInArg(argVboxValues));
3097 com::SafeArray<IN_BSTR> sfaExtraConfigValues(ComSafeArrayInArg(argExtraConfigValues));
3098
3099 if ( (sfaEnabled.size() != m->llDescriptions.size())
3100 || (sfaVboxValues.size() != m->llDescriptions.size())
3101 || (sfaExtraConfigValues.size() != m->llDescriptions.size())
3102 )
3103 return E_INVALIDARG;
3104
3105 list<VirtualSystemDescriptionEntry>::iterator it;
3106 size_t i = 0;
3107 for (it = m->llDescriptions.begin();
3108 it != m->llDescriptions.end();
3109 ++it, ++i)
3110 {
3111 VirtualSystemDescriptionEntry& vsde = *it;
3112
3113 if (sfaEnabled[i])
3114 {
3115 vsde.strVbox = sfaVboxValues[i];
3116 vsde.strExtraConfig = sfaExtraConfigValues[i];
3117 }
3118 else
3119 vsde.type = VirtualSystemDescriptionType_Ignore;
3120 }
3121
3122 return S_OK;
3123}
3124
3125/**
3126 * Public method implementation.
3127 * @return
3128 */
3129STDMETHODIMP VirtualSystemDescription::AddDescription(VirtualSystemDescriptionType_T aType,
3130 IN_BSTR aVboxValue,
3131 IN_BSTR aExtraConfigValue)
3132{
3133 AutoCaller autoCaller(this);
3134 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3135
3136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3137
3138 addEntry(aType, "", aVboxValue, aVboxValue, 0, aExtraConfigValue);
3139
3140 return S_OK;
3141}
3142
3143/**
3144 * Internal method; adds a new description item to the member list.
3145 * @param aType Type of description for the new item.
3146 * @param strRef Reference item; only used with hard disk controllers.
3147 * @param aOrigValue Corresponding original value from OVF.
3148 * @param aAutoValue Initial configuration value (can be overridden by caller with setFinalValues).
3149 * @param ulSizeMB Weight for IProgress
3150 * @param strExtraConfig Extra configuration; meaning dependent on type.
3151 */
3152void VirtualSystemDescription::addEntry(VirtualSystemDescriptionType_T aType,
3153 const Utf8Str &strRef,
3154 const Utf8Str &aOrigValue,
3155 const Utf8Str &aAutoValue,
3156 uint32_t ulSizeMB,
3157 const Utf8Str &strExtraConfig /*= ""*/)
3158{
3159 VirtualSystemDescriptionEntry vsde;
3160 vsde.ulIndex = (uint32_t)m->llDescriptions.size(); // each entry gets an index so the client side can reference them
3161 vsde.type = aType;
3162 vsde.strRef = strRef;
3163 vsde.strOvf = aOrigValue;
3164 vsde.strVbox = aAutoValue;
3165 vsde.strExtraConfig = strExtraConfig;
3166 vsde.ulSizeMB = ulSizeMB;
3167
3168 m->llDescriptions.push_back(vsde);
3169}
3170
3171/**
3172 * Private method; returns a list of description items containing all the items from the member
3173 * description items of this virtual system that match the given type.
3174 * @param aType
3175 * @return
3176 */
3177std::list<VirtualSystemDescriptionEntry*> VirtualSystemDescription::findByType(VirtualSystemDescriptionType_T aType)
3178{
3179 std::list<VirtualSystemDescriptionEntry*> vsd;
3180
3181 list<VirtualSystemDescriptionEntry>::iterator it;
3182 for (it = m->llDescriptions.begin();
3183 it != m->llDescriptions.end();
3184 ++it)
3185 {
3186 if (it->type == aType)
3187 vsd.push_back(&(*it));
3188 }
3189
3190 return vsd;
3191}
3192
3193/**
3194 * Private method; looks thru the member hardware items for the IDE, SATA, or SCSI controller with
3195 * the given reference ID. Useful when needing the controller for a particular
3196 * virtual disk.
3197 * @param id
3198 * @return
3199 */
3200const VirtualSystemDescriptionEntry* VirtualSystemDescription::findControllerFromID(uint32_t id)
3201{
3202 Utf8Str strRef = Utf8StrFmt("%RI32", id);
3203 list<VirtualSystemDescriptionEntry>::const_iterator it;
3204 for (it = m->llDescriptions.begin();
3205 it != m->llDescriptions.end();
3206 ++it)
3207 {
3208 const VirtualSystemDescriptionEntry &d = *it;
3209 switch (d.type)
3210 {
3211 case VirtualSystemDescriptionType_HardDiskControllerIDE:
3212 case VirtualSystemDescriptionType_HardDiskControllerSATA:
3213 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
3214 if (d.strRef == strRef)
3215 return &d;
3216 break;
3217 }
3218 }
3219
3220 return NULL;
3221}
3222
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