VirtualBox

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

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

Main/VirtualBox: new parameter for overriding VM file existence check on creation.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 199.5 KB
Line 
1/* $Id: ApplianceImpl.cpp 27677 2010-03-24 17:21:51Z 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
47using namespace std;
48
49////////////////////////////////////////////////////////////////////////////////
50//
51// Appliance data definition
52//
53////////////////////////////////////////////////////////////////////////////////
54
55/* Describe a location for the import/export. The location could be a file on a
56 * local hard disk or a remote target based on the supported inet protocols. */
57struct Appliance::LocationInfo
58{
59 LocationInfo()
60 : storageType(VFSType_File) {}
61 VFSType_T storageType; /* Which type of storage should be handled */
62 Utf8Str strPath; /* File path for the import/export */
63 Utf8Str strHostname; /* Hostname on remote storage locations (could be empty) */
64 Utf8Str strUsername; /* Username on remote storage locations (could be empty) */
65 Utf8Str strPassword; /* Password on remote storage locations (could be empty) */
66};
67
68// opaque private instance data of Appliance class
69struct Appliance::Data
70{
71 Data()
72 : pReader(NULL) {}
73
74 ~Data()
75 {
76 if (pReader)
77 {
78 delete pReader;
79 pReader = NULL;
80 }
81 }
82
83 LocationInfo locInfo; /* The location info for the currently processed OVF */
84
85 OVFReader *pReader;
86
87 list< ComObjPtr<VirtualSystemDescription> > virtualSystemDescriptions;
88
89 list<Utf8Str> llWarnings;
90
91 ULONG ulWeightPerOperation;
92 Utf8Str strOVFSHA1Digest;
93};
94
95struct VirtualSystemDescription::Data
96{
97 list<VirtualSystemDescriptionEntry> llDescriptions;
98};
99
100////////////////////////////////////////////////////////////////////////////////
101//
102// internal helpers
103//
104////////////////////////////////////////////////////////////////////////////////
105
106static const struct
107{
108 CIMOSType_T cim;
109 const char *pcszVbox;
110}
111g_osTypes[] =
112{
113 { CIMOSType_CIMOS_Unknown, SchemaDefs_OSTypeId_Other },
114 { CIMOSType_CIMOS_OS2, SchemaDefs_OSTypeId_OS2 },
115 { CIMOSType_CIMOS_MSDOS, SchemaDefs_OSTypeId_DOS },
116 { CIMOSType_CIMOS_WIN3x, SchemaDefs_OSTypeId_Windows31 },
117 { CIMOSType_CIMOS_WIN95, SchemaDefs_OSTypeId_Windows95 },
118 { CIMOSType_CIMOS_WIN98, SchemaDefs_OSTypeId_Windows98 },
119 { CIMOSType_CIMOS_WINNT, SchemaDefs_OSTypeId_WindowsNT4 },
120 { CIMOSType_CIMOS_NetWare, SchemaDefs_OSTypeId_Netware },
121 { CIMOSType_CIMOS_NovellOES, SchemaDefs_OSTypeId_Netware },
122 { CIMOSType_CIMOS_Solaris, SchemaDefs_OSTypeId_OpenSolaris },
123 { CIMOSType_CIMOS_SunOS, SchemaDefs_OSTypeId_OpenSolaris },
124 { CIMOSType_CIMOS_FreeBSD, SchemaDefs_OSTypeId_FreeBSD },
125 { CIMOSType_CIMOS_NetBSD, SchemaDefs_OSTypeId_NetBSD },
126 { CIMOSType_CIMOS_QNX, SchemaDefs_OSTypeId_QNX },
127 { CIMOSType_CIMOS_Windows2000, SchemaDefs_OSTypeId_Windows2000 },
128 { CIMOSType_CIMOS_WindowsMe, SchemaDefs_OSTypeId_WindowsMe },
129 { CIMOSType_CIMOS_OpenBSD, SchemaDefs_OSTypeId_OpenBSD },
130 { CIMOSType_CIMOS_WindowsXP, SchemaDefs_OSTypeId_WindowsXP },
131 { CIMOSType_CIMOS_WindowsXPEmbedded, SchemaDefs_OSTypeId_WindowsXP },
132 { CIMOSType_CIMOS_WindowsEmbeddedforPointofService, SchemaDefs_OSTypeId_WindowsXP },
133 { CIMOSType_CIMOS_MicrosoftWindowsServer2003, SchemaDefs_OSTypeId_Windows2003 },
134 { CIMOSType_CIMOS_MicrosoftWindowsServer2003_64, SchemaDefs_OSTypeId_Windows2003_64 },
135 { CIMOSType_CIMOS_WindowsXP_64, SchemaDefs_OSTypeId_WindowsXP_64 },
136 { CIMOSType_CIMOS_WindowsVista, SchemaDefs_OSTypeId_WindowsVista },
137 { CIMOSType_CIMOS_WindowsVista_64, SchemaDefs_OSTypeId_WindowsVista_64 },
138 { CIMOSType_CIMOS_MicrosoftWindowsServer2008, SchemaDefs_OSTypeId_Windows2008 },
139 { CIMOSType_CIMOS_MicrosoftWindowsServer2008_64, SchemaDefs_OSTypeId_Windows2008_64 },
140 { CIMOSType_CIMOS_FreeBSD_64, SchemaDefs_OSTypeId_FreeBSD_64 },
141 { CIMOSType_CIMOS_RedHatEnterpriseLinux, SchemaDefs_OSTypeId_RedHat },
142 { CIMOSType_CIMOS_RedHatEnterpriseLinux_64, SchemaDefs_OSTypeId_RedHat_64 },
143 { CIMOSType_CIMOS_Solaris_64, SchemaDefs_OSTypeId_OpenSolaris_64 },
144 { CIMOSType_CIMOS_SUSE, SchemaDefs_OSTypeId_OpenSUSE },
145 { CIMOSType_CIMOS_SLES, SchemaDefs_OSTypeId_OpenSUSE },
146 { CIMOSType_CIMOS_NovellLinuxDesktop, SchemaDefs_OSTypeId_OpenSUSE },
147 { CIMOSType_CIMOS_SUSE_64, SchemaDefs_OSTypeId_OpenSUSE_64 },
148 { CIMOSType_CIMOS_SLES_64, SchemaDefs_OSTypeId_OpenSUSE_64 },
149 { CIMOSType_CIMOS_LINUX, SchemaDefs_OSTypeId_Linux },
150 { CIMOSType_CIMOS_SunJavaDesktopSystem, SchemaDefs_OSTypeId_Linux },
151 { CIMOSType_CIMOS_TurboLinux, SchemaDefs_OSTypeId_Linux},
152
153 // { CIMOSType_CIMOS_TurboLinux_64, },
154
155 { CIMOSType_CIMOS_Mandriva, SchemaDefs_OSTypeId_Mandriva },
156 { CIMOSType_CIMOS_Mandriva_64, SchemaDefs_OSTypeId_Mandriva_64 },
157 { CIMOSType_CIMOS_Ubuntu, SchemaDefs_OSTypeId_Ubuntu },
158 { CIMOSType_CIMOS_Ubuntu_64, SchemaDefs_OSTypeId_Ubuntu_64 },
159 { CIMOSType_CIMOS_Debian, SchemaDefs_OSTypeId_Debian },
160 { CIMOSType_CIMOS_Debian_64, SchemaDefs_OSTypeId_Debian_64 },
161 { CIMOSType_CIMOS_Linux_2_4_x, SchemaDefs_OSTypeId_Linux24 },
162 { CIMOSType_CIMOS_Linux_2_4_x_64, SchemaDefs_OSTypeId_Linux24_64 },
163 { CIMOSType_CIMOS_Linux_2_6_x, SchemaDefs_OSTypeId_Linux26 },
164 { CIMOSType_CIMOS_Linux_2_6_x_64, SchemaDefs_OSTypeId_Linux26_64 },
165 { CIMOSType_CIMOS_Linux_64, SchemaDefs_OSTypeId_Linux26_64 }
166};
167
168/* Pattern structure for matching the OS type description field */
169struct osTypePattern
170{
171 const char *pcszPattern;
172 const char *pcszVbox;
173};
174
175/* These are the 32-Bit ones. They are sorted by priority. */
176static const osTypePattern g_osTypesPattern[] =
177{
178 {"Windows NT", SchemaDefs_OSTypeId_WindowsNT4},
179 {"Windows XP", SchemaDefs_OSTypeId_WindowsXP},
180 {"Windows 2000", SchemaDefs_OSTypeId_Windows2000},
181 {"Windows 2003", SchemaDefs_OSTypeId_Windows2003},
182 {"Windows Vista", SchemaDefs_OSTypeId_WindowsVista},
183 {"Windows 2008", SchemaDefs_OSTypeId_Windows2008},
184 {"SUSE", SchemaDefs_OSTypeId_OpenSUSE},
185 {"Novell", SchemaDefs_OSTypeId_OpenSUSE},
186 {"Red Hat", SchemaDefs_OSTypeId_RedHat},
187 {"Mandriva", SchemaDefs_OSTypeId_Mandriva},
188 {"Ubuntu", SchemaDefs_OSTypeId_Ubuntu},
189 {"Debian", SchemaDefs_OSTypeId_Debian},
190 {"QNX", SchemaDefs_OSTypeId_QNX},
191 {"Linux 2.4", SchemaDefs_OSTypeId_Linux24},
192 {"Linux 2.6", SchemaDefs_OSTypeId_Linux26},
193 {"Linux", SchemaDefs_OSTypeId_Linux},
194 {"OpenSolaris", SchemaDefs_OSTypeId_OpenSolaris},
195 {"Solaris", SchemaDefs_OSTypeId_OpenSolaris},
196 {"FreeBSD", SchemaDefs_OSTypeId_FreeBSD},
197 {"NetBSD", SchemaDefs_OSTypeId_NetBSD},
198 {"Windows 95", SchemaDefs_OSTypeId_Windows95},
199 {"Windows 98", SchemaDefs_OSTypeId_Windows98},
200 {"Windows Me", SchemaDefs_OSTypeId_WindowsMe},
201 {"Windows 3.", SchemaDefs_OSTypeId_Windows31},
202 {"DOS", SchemaDefs_OSTypeId_DOS},
203 {"OS2", SchemaDefs_OSTypeId_OS2}
204};
205
206/* These are the 64-Bit ones. They are sorted by priority. */
207static const osTypePattern g_osTypesPattern64[] =
208{
209 {"Windows XP", SchemaDefs_OSTypeId_WindowsXP_64},
210 {"Windows 2003", SchemaDefs_OSTypeId_Windows2003_64},
211 {"Windows Vista", SchemaDefs_OSTypeId_WindowsVista_64},
212 {"Windows 2008", SchemaDefs_OSTypeId_Windows2008_64},
213 {"SUSE", SchemaDefs_OSTypeId_OpenSUSE_64},
214 {"Novell", SchemaDefs_OSTypeId_OpenSUSE_64},
215 {"Red Hat", SchemaDefs_OSTypeId_RedHat_64},
216 {"Mandriva", SchemaDefs_OSTypeId_Mandriva_64},
217 {"Ubuntu", SchemaDefs_OSTypeId_Ubuntu_64},
218 {"Debian", SchemaDefs_OSTypeId_Debian_64},
219 {"Linux 2.4", SchemaDefs_OSTypeId_Linux24_64},
220 {"Linux 2.6", SchemaDefs_OSTypeId_Linux26_64},
221 {"Linux", SchemaDefs_OSTypeId_Linux26_64},
222 {"OpenSolaris", SchemaDefs_OSTypeId_OpenSolaris_64},
223 {"Solaris", SchemaDefs_OSTypeId_OpenSolaris_64},
224 {"FreeBSD", SchemaDefs_OSTypeId_FreeBSD_64},
225};
226
227/**
228 * Private helper func that suggests a VirtualBox guest OS type
229 * for the given OVF operating system type.
230 * @param osTypeVBox
231 * @param c
232 * @param cStr
233 */
234static void convertCIMOSType2VBoxOSType(Utf8Str &strType, CIMOSType_T c, const Utf8Str &cStr)
235{
236 /* First check if the type is other/other_64 */
237 if (c == CIMOSType_CIMOS_Other)
238 {
239 for (size_t i=0; i < RT_ELEMENTS(g_osTypesPattern); ++i)
240 if (cStr.contains (g_osTypesPattern[i].pcszPattern, Utf8Str::CaseInsensitive))
241 {
242 strType = g_osTypesPattern[i].pcszVbox;
243 return;
244 }
245 }
246 else if (c == CIMOSType_CIMOS_Other_64)
247 {
248 for (size_t i=0; i < RT_ELEMENTS(g_osTypesPattern64); ++i)
249 if (cStr.contains (g_osTypesPattern64[i].pcszPattern, Utf8Str::CaseInsensitive))
250 {
251 strType = g_osTypesPattern64[i].pcszVbox;
252 return;
253 }
254 }
255
256 for (size_t i = 0; i < RT_ELEMENTS(g_osTypes); ++i)
257 {
258 if (c == g_osTypes[i].cim)
259 {
260 strType = g_osTypes[i].pcszVbox;
261 return;
262 }
263 }
264
265 strType = SchemaDefs_OSTypeId_Other;
266}
267
268/**
269 * Private helper func that suggests a VirtualBox guest OS type
270 * for the given OVF operating system type.
271 * @param osTypeVBox
272 * @param c
273 */
274static CIMOSType_T convertVBoxOSType2CIMOSType(const char *pcszVbox)
275{
276 for (size_t i = 0; i < RT_ELEMENTS(g_osTypes); ++i)
277 {
278 if (!RTStrICmp(pcszVbox, g_osTypes[i].pcszVbox))
279 return g_osTypes[i].cim;
280 }
281
282 return CIMOSType_CIMOS_Other;
283}
284
285////////////////////////////////////////////////////////////////////////////////
286//
287// IVirtualBox public methods
288//
289////////////////////////////////////////////////////////////////////////////////
290
291// This code is here so we won't have to include the appliance headers in the
292// IVirtualBox implementation.
293
294/**
295 * Implementation for IVirtualBox::createAppliance.
296 *
297 * @param anAppliance IAppliance object created if S_OK is returned.
298 * @return S_OK or error.
299 */
300STDMETHODIMP VirtualBox::CreateAppliance(IAppliance** anAppliance)
301{
302 HRESULT rc;
303
304 ComObjPtr<Appliance> appliance;
305 appliance.createObject();
306 rc = appliance->init(this);
307
308 if (SUCCEEDED(rc))
309 appliance.queryInterfaceTo(anAppliance);
310
311 return rc;
312}
313
314////////////////////////////////////////////////////////////////////////////////
315//
316// Appliance constructor / destructor
317//
318////////////////////////////////////////////////////////////////////////////////
319
320Appliance::Appliance()
321 : mVirtualBox(NULL)
322{
323}
324
325Appliance::~Appliance()
326{
327}
328
329/**
330 * Appliance COM initializer.
331 * @param
332 * @return
333 */
334HRESULT Appliance::init(VirtualBox *aVirtualBox)
335{
336 /* Enclose the state transition NotReady->InInit->Ready */
337 AutoInitSpan autoInitSpan(this);
338 AssertReturn(autoInitSpan.isOk(), E_FAIL);
339
340 /* Weak reference to a VirtualBox object */
341 unconst(mVirtualBox) = aVirtualBox;
342
343 // initialize data
344 m = new Data;
345
346 /* Confirm a successful initialization */
347 autoInitSpan.setSucceeded();
348
349 return S_OK;
350}
351
352/**
353 * Appliance COM uninitializer.
354 * @return
355 */
356void Appliance::uninit()
357{
358 /* Enclose the state transition Ready->InUninit->NotReady */
359 AutoUninitSpan autoUninitSpan(this);
360 if (autoUninitSpan.uninitDone())
361 return;
362
363 delete m;
364 m = NULL;
365}
366
367////////////////////////////////////////////////////////////////////////////////
368//
369// Appliance private methods
370//
371////////////////////////////////////////////////////////////////////////////////
372
373HRESULT Appliance::searchUniqueVMName(Utf8Str& aName) const
374{
375 IMachine *machine = NULL;
376 char *tmpName = RTStrDup(aName.c_str());
377 int i = 1;
378 /* @todo: Maybe too cost-intensive; try to find a lighter way */
379 while (mVirtualBox->FindMachine(Bstr(tmpName), &machine) != VBOX_E_OBJECT_NOT_FOUND)
380 {
381 RTStrFree(tmpName);
382 RTStrAPrintf(&tmpName, "%s_%d", aName.c_str(), i);
383 ++i;
384 }
385 aName = tmpName;
386 RTStrFree(tmpName);
387
388 return S_OK;
389}
390
391HRESULT Appliance::searchUniqueDiskImageFilePath(Utf8Str& aName) const
392{
393 IMedium *harddisk = NULL;
394 char *tmpName = RTStrDup(aName.c_str());
395 int i = 1;
396 /* Check if the file exists or if a file with this path is registered
397 * already */
398 /* @todo: Maybe too cost-intensive; try to find a lighter way */
399 while (RTPathExists(tmpName) ||
400 mVirtualBox->FindHardDisk(Bstr(tmpName), &harddisk) != VBOX_E_OBJECT_NOT_FOUND)
401 {
402 RTStrFree(tmpName);
403 char *tmpDir = RTStrDup(aName.c_str());
404 RTPathStripFilename(tmpDir);;
405 char *tmpFile = RTStrDup(RTPathFilename(aName.c_str()));
406 RTPathStripExt(tmpFile);
407 const char *tmpExt = RTPathExt(aName.c_str());
408 RTStrAPrintf(&tmpName, "%s%c%s_%d%s", tmpDir, RTPATH_DELIMITER, tmpFile, i, tmpExt);
409 RTStrFree(tmpFile);
410 RTStrFree(tmpDir);
411 ++i;
412 }
413 aName = tmpName;
414 RTStrFree(tmpName);
415
416 return S_OK;
417}
418
419/**
420 * Called from the import and export background threads to synchronize the second
421 * background disk thread's progress object with the current progress object so
422 * that the user interface sees progress correctly and that cancel signals are
423 * passed on to the second thread.
424 * @param pProgressThis Progress object of the current thread.
425 * @param pProgressAsync Progress object of asynchronous task running in background.
426 */
427void Appliance::waitForAsyncProgress(ComObjPtr<Progress> &pProgressThis,
428 ComPtr<IProgress> &pProgressAsync)
429{
430 HRESULT rc;
431
432 // now loop until the asynchronous operation completes and then report its result
433 BOOL fCompleted;
434 BOOL fCanceled;
435 ULONG currentPercent;
436 while (SUCCEEDED(pProgressAsync->COMGETTER(Completed(&fCompleted))))
437 {
438 rc = pProgressThis->COMGETTER(Canceled)(&fCanceled);
439 if (FAILED(rc)) throw rc;
440 if (fCanceled)
441 {
442 pProgressAsync->Cancel();
443 break;
444 }
445
446 rc = pProgressAsync->COMGETTER(Percent(&currentPercent));
447 if (FAILED(rc)) throw rc;
448 if (!pProgressThis.isNull())
449 pProgressThis->SetCurrentOperationProgress(currentPercent);
450 if (fCompleted)
451 break;
452
453 /* Make sure the loop is not too tight */
454 rc = pProgressAsync->WaitForCompletion(100);
455 if (FAILED(rc)) throw rc;
456 }
457 // report result of asynchronous operation
458 LONG iRc;
459 rc = pProgressAsync->COMGETTER(ResultCode)(&iRc);
460 if (FAILED(rc)) throw rc;
461
462
463 // if the thread of the progress object has an error, then
464 // retrieve the error info from there, or it'll be lost
465 if (FAILED(iRc))
466 {
467 ProgressErrorInfo info(pProgressAsync);
468 Utf8Str str(info.getText());
469 const char *pcsz = str.c_str();
470 HRESULT rc2 = setError(iRc, pcsz);
471 throw rc2;
472 }
473}
474
475void Appliance::addWarning(const char* aWarning, ...)
476{
477 va_list args;
478 va_start(args, aWarning);
479 Utf8StrFmtVA str(aWarning, args);
480 va_end(args);
481 m->llWarnings.push_back(str);
482}
483
484void Appliance::disksWeight(uint32_t &ulTotalMB, uint32_t &cDisks) const
485{
486 ulTotalMB = 0;
487 cDisks = 0;
488 /* Weigh the disk images according to their sizes */
489 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
490 for (it = m->virtualSystemDescriptions.begin();
491 it != m->virtualSystemDescriptions.end();
492 ++it)
493 {
494 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
495 /* One for every hard disk of the Virtual System */
496 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
497 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
498 for (itH = avsdeHDs.begin();
499 itH != avsdeHDs.end();
500 ++itH)
501 {
502 const VirtualSystemDescriptionEntry *pHD = *itH;
503 ulTotalMB += pHD->ulSizeMB;
504 ++cDisks;
505 }
506 }
507
508}
509
510HRESULT Appliance::setUpProgressFS(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription)
511{
512 HRESULT rc;
513
514 /* Create the progress object */
515 pProgress.createObject();
516
517 /* Weigh the disk images according to their sizes */
518 uint32_t ulTotalMB;
519 uint32_t cDisks;
520 disksWeight(ulTotalMB, cDisks);
521
522 ULONG cOperations = 1 + cDisks; // one op per disk plus 1 for the XML
523
524 ULONG ulTotalOperationsWeight;
525 if (ulTotalMB)
526 {
527 m->ulWeightPerOperation = (ULONG)((double)ulTotalMB * 1 / 100); // use 1% of the progress for the XML
528 ulTotalOperationsWeight = ulTotalMB + m->ulWeightPerOperation;
529 }
530 else
531 {
532 // no disks to export:
533 ulTotalOperationsWeight = 1;
534 m->ulWeightPerOperation = 1;
535 }
536
537 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightPerOperation = %d\n",
538 ulTotalMB, cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightPerOperation));
539
540 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
541 bstrDescription,
542 TRUE /* aCancelable */,
543 cOperations, // ULONG cOperations,
544 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight,
545 bstrDescription, // CBSTR bstrFirstOperationDescription,
546 m->ulWeightPerOperation); // ULONG ulFirstOperationWeight,
547 return rc;
548}
549
550HRESULT Appliance::setUpProgressImportS3(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription)
551{
552 HRESULT rc;
553
554 /* Create the progress object */
555 pProgress.createObject();
556
557 /* Weigh the disk images according to their sizes */
558 uint32_t ulTotalMB;
559 uint32_t cDisks;
560 disksWeight(ulTotalMB, cDisks);
561
562 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 */
563
564 ULONG ulTotalOperationsWeight = ulTotalMB;
565 if (!ulTotalOperationsWeight)
566 // no disks to export:
567 ulTotalOperationsWeight = 1;
568
569 ULONG ulImportWeight = (ULONG)((double)ulTotalOperationsWeight * 50 / 100); // use 50% for import
570 ulTotalOperationsWeight += ulImportWeight;
571
572 m->ulWeightPerOperation = ulImportWeight; /* save for using later */
573
574 ULONG ulInitWeight = (ULONG)((double)ulTotalOperationsWeight * 0.1 / 100); // use 0.1% for init
575 ulTotalOperationsWeight += ulInitWeight;
576
577 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightPerOperation = %d\n",
578 ulTotalMB, cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightPerOperation));
579
580 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
581 bstrDescription,
582 TRUE /* aCancelable */,
583 cOperations, // ULONG cOperations,
584 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight,
585 Bstr(tr("Init")), // CBSTR bstrFirstOperationDescription,
586 ulInitWeight); // ULONG ulFirstOperationWeight,
587 return rc;
588}
589
590HRESULT Appliance::setUpProgressWriteS3(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription)
591{
592 HRESULT rc;
593
594 /* Create the progress object */
595 pProgress.createObject();
596
597 /* Weigh the disk images according to their sizes */
598 uint32_t ulTotalMB;
599 uint32_t cDisks;
600 disksWeight(ulTotalMB, cDisks);
601
602 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 */
603
604 ULONG ulTotalOperationsWeight;
605 if (ulTotalMB)
606 {
607 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)
608 ulTotalOperationsWeight = ulTotalMB + m->ulWeightPerOperation;
609 }
610 else
611 {
612 // no disks to export:
613 ulTotalOperationsWeight = 1;
614 m->ulWeightPerOperation = 1;
615 }
616 ULONG ulOVFCreationWeight = (ULONG)((double)ulTotalOperationsWeight * 50.0 / 100.0); /* Use 50% for the creation of the OVF & the disks */
617 ulTotalOperationsWeight += ulOVFCreationWeight;
618
619 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightPerOperation = %d\n",
620 ulTotalMB, cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightPerOperation));
621
622 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
623 bstrDescription,
624 TRUE /* aCancelable */,
625 cOperations, // ULONG cOperations,
626 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight,
627 bstrDescription, // CBSTR bstrFirstOperationDescription,
628 ulOVFCreationWeight); // ULONG ulFirstOperationWeight,
629 return rc;
630}
631
632void Appliance::parseURI(Utf8Str strUri, LocationInfo &locInfo) const
633{
634 /* Check the URI for the protocol */
635 if (strUri.startsWith("file://", Utf8Str::CaseInsensitive)) /* File based */
636 {
637 locInfo.storageType = VFSType_File;
638 strUri = strUri.substr(sizeof("file://") - 1);
639 }
640 else if (strUri.startsWith("SunCloud://", Utf8Str::CaseInsensitive)) /* Sun Cloud service */
641 {
642 locInfo.storageType = VFSType_S3;
643 strUri = strUri.substr(sizeof("SunCloud://") - 1);
644 }
645 else if (strUri.startsWith("S3://", Utf8Str::CaseInsensitive)) /* S3 service */
646 {
647 locInfo.storageType = VFSType_S3;
648 strUri = strUri.substr(sizeof("S3://") - 1);
649 }
650 else if (strUri.startsWith("webdav://", Utf8Str::CaseInsensitive)) /* webdav service */
651 throw E_NOTIMPL;
652
653 /* Not necessary on a file based URI */
654 if (locInfo.storageType != VFSType_File)
655 {
656 size_t uppos = strUri.find("@"); /* username:password combo */
657 if (uppos != Utf8Str::npos)
658 {
659 locInfo.strUsername = strUri.substr(0, uppos);
660 strUri = strUri.substr(uppos + 1);
661 size_t upos = locInfo.strUsername.find(":");
662 if (upos != Utf8Str::npos)
663 {
664 locInfo.strPassword = locInfo.strUsername.substr(upos + 1);
665 locInfo.strUsername = locInfo.strUsername.substr(0, upos);
666 }
667 }
668 size_t hpos = strUri.find("/"); /* hostname part */
669 if (hpos != Utf8Str::npos)
670 {
671 locInfo.strHostname = strUri.substr(0, hpos);
672 strUri = strUri.substr(hpos);
673 }
674 }
675
676 locInfo.strPath = strUri;
677}
678
679void Appliance::parseBucket(Utf8Str &aPath, Utf8Str &aBucket) const
680{
681 /* Buckets are S3 specific. So parse the bucket out of the file path */
682 if (!aPath.startsWith("/"))
683 throw setError(E_INVALIDARG,
684 tr("The path '%s' must start with /"), aPath.c_str());
685 size_t bpos = aPath.find("/", 1);
686 if (bpos != Utf8Str::npos)
687 {
688 aBucket = aPath.substr(1, bpos - 1); /* The bucket without any slashes */
689 aPath = aPath.substr(bpos); /* The rest of the file path */
690 }
691 /* If there is no bucket name provided reject it */
692 if (aBucket.isEmpty())
693 throw setError(E_INVALIDARG,
694 tr("You doesn't provide a bucket name in the URI '%s'"), aPath.c_str());
695}
696
697Utf8Str Appliance::manifestFileName(Utf8Str aPath) const
698{
699 /* Get the name part */
700 char *pszMfName = RTStrDup(RTPathFilename(aPath.c_str()));
701 /* Strip any extensions */
702 RTPathStripExt(pszMfName);
703 /* Path without the filename */
704 aPath.stripFilename();
705 /* Format the manifest path */
706 Utf8StrFmt strMfFile("%s/%s.mf", aPath.c_str(), pszMfName);
707 RTStrFree(pszMfName);
708 return strMfFile;
709}
710
711struct Appliance::TaskOVF
712{
713 TaskOVF(Appliance *aThat)
714 : pAppliance(aThat)
715 , rc(S_OK) {}
716
717 static int updateProgress(unsigned uPercent, void *pvUser);
718
719 LocationInfo locInfo;
720 Appliance *pAppliance;
721 ComObjPtr<Progress> progress;
722 HRESULT rc;
723};
724
725struct Appliance::TaskImportOVF: Appliance::TaskOVF
726{
727 enum TaskType
728 {
729 Read,
730 Import
731 };
732
733 TaskImportOVF(Appliance *aThat)
734 : TaskOVF(aThat)
735 , taskType(Read) {}
736
737 int startThread();
738
739 TaskType taskType;
740};
741
742struct Appliance::TaskExportOVF: Appliance::TaskOVF
743{
744 enum OVFFormat
745 {
746 unspecified,
747 OVF_0_9,
748 OVF_1_0
749 };
750 enum TaskType
751 {
752 Write
753 };
754
755 TaskExportOVF(Appliance *aThat)
756 : TaskOVF(aThat)
757 , taskType(Write) {}
758
759 int startThread();
760
761 TaskType taskType;
762 OVFFormat enFormat;
763};
764
765struct MyHardDiskAttachment
766{
767 Bstr bstrUuid;
768 ComPtr<IMachine> pMachine;
769 Bstr controllerType;
770 int32_t lChannel;
771 int32_t lDevice;
772};
773
774/* static */
775int Appliance::TaskOVF::updateProgress(unsigned uPercent, void *pvUser)
776{
777 Appliance::TaskOVF* pTask = *(Appliance::TaskOVF**)pvUser;
778
779 if (pTask &&
780 !pTask->progress.isNull())
781 {
782 BOOL fCanceled;
783 pTask->progress->COMGETTER(Canceled)(&fCanceled);
784 if (fCanceled)
785 return -1;
786 pTask->progress->SetCurrentOperationProgress(uPercent);
787 }
788 return VINF_SUCCESS;
789}
790
791HRESULT Appliance::readImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)
792{
793 /* Initialize our worker task */
794 std::auto_ptr<TaskImportOVF> task(new TaskImportOVF(this));
795 /* What should the task do */
796 task->taskType = TaskImportOVF::Read;
797 /* Copy the current location info to the task */
798 task->locInfo = aLocInfo;
799
800 BstrFmt bstrDesc = BstrFmt(tr("Read appliance '%s'"),
801 aLocInfo.strPath.c_str());
802 HRESULT rc;
803 /* Create the progress object */
804 aProgress.createObject();
805 if (task->locInfo.storageType == VFSType_File)
806 {
807 /* 1 operation only */
808 rc = aProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
809 bstrDesc,
810 TRUE /* aCancelable */);
811 }
812 else
813 {
814 /* 4/5 is downloading, 1/5 is reading */
815 rc = aProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
816 bstrDesc,
817 TRUE /* aCancelable */,
818 2, // ULONG cOperations,
819 5, // ULONG ulTotalOperationsWeight,
820 BstrFmt(tr("Download appliance '%s'"),
821 aLocInfo.strPath.c_str()), // CBSTR bstrFirstOperationDescription,
822 4); // ULONG ulFirstOperationWeight,
823 }
824 if (FAILED(rc)) throw rc;
825
826 task->progress = aProgress;
827
828 rc = task->startThread();
829 if (FAILED(rc)) throw rc;
830
831 /* Don't destruct on success */
832 task.release();
833
834 return rc;
835}
836
837HRESULT Appliance::importImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)
838{
839 /* Initialize our worker task */
840 std::auto_ptr<TaskImportOVF> task(new TaskImportOVF(this));
841 /* What should the task do */
842 task->taskType = TaskImportOVF::Import;
843 /* Copy the current location info to the task */
844 task->locInfo = aLocInfo;
845
846 Bstr progressDesc = BstrFmt(tr("Import appliance '%s'"),
847 aLocInfo.strPath.c_str());
848
849 HRESULT rc = S_OK;
850
851 /* todo: This progress init stuff should be done a little bit more generic */
852 if (task->locInfo.storageType == VFSType_File)
853 rc = setUpProgressFS(aProgress, progressDesc);
854 else
855 rc = setUpProgressImportS3(aProgress, progressDesc);
856 if (FAILED(rc)) throw rc;
857
858 task->progress = aProgress;
859
860 rc = task->startThread();
861 if (FAILED(rc)) throw rc;
862
863 /* Don't destruct on success */
864 task.release();
865
866 return rc;
867}
868
869/**
870 * Worker thread implementation for Read() (ovf reader).
871 * @param aThread
872 * @param pvUser
873 */
874/* static */
875DECLCALLBACK(int) Appliance::taskThreadImportOVF(RTTHREAD /* aThread */, void *pvUser)
876{
877 std::auto_ptr<TaskImportOVF> task(static_cast<TaskImportOVF*>(pvUser));
878 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
879
880 Appliance *pAppliance = task->pAppliance;
881
882 LogFlowFuncEnter();
883 LogFlowFunc(("Appliance %p\n", pAppliance));
884
885 HRESULT rc = S_OK;
886
887 switch(task->taskType)
888 {
889 case TaskImportOVF::Read:
890 {
891 if (task->locInfo.storageType == VFSType_File)
892 rc = pAppliance->readFS(task.get());
893 else if (task->locInfo.storageType == VFSType_S3)
894 rc = pAppliance->readS3(task.get());
895 break;
896 }
897 case TaskImportOVF::Import:
898 {
899 if (task->locInfo.storageType == VFSType_File)
900 rc = pAppliance->importFS(task.get());
901 else if (task->locInfo.storageType == VFSType_S3)
902 rc = pAppliance->importS3(task.get());
903 break;
904 }
905 }
906
907 LogFlowFunc(("rc=%Rhrc\n", rc));
908 LogFlowFuncLeave();
909
910 return VINF_SUCCESS;
911}
912
913int Appliance::TaskImportOVF::startThread()
914{
915 int vrc = RTThreadCreate(NULL, Appliance::taskThreadImportOVF, this,
916 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
917 "Appliance::Task");
918
919 ComAssertMsgRCRet(vrc,
920 ("Could not create taskThreadImportOVF (%Rrc)\n", vrc), E_FAIL);
921
922 return S_OK;
923}
924
925int Appliance::readFS(TaskImportOVF *pTask)
926{
927 LogFlowFuncEnter();
928 LogFlowFunc(("Appliance %p\n", this));
929
930 AutoCaller autoCaller(this);
931 if (FAILED(autoCaller.rc())) return autoCaller.rc();
932
933 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
934
935 HRESULT rc = S_OK;
936
937 try
938 {
939 /* Read & parse the XML structure of the OVF file */
940 m->pReader = new OVFReader(pTask->locInfo.strPath);
941 /* Create the SHA1 sum of the OVF file for later validation */
942 char *pszDigest;
943 int vrc = RTSha1Digest(pTask->locInfo.strPath.c_str(), &pszDigest);
944 if (RT_FAILURE(vrc))
945 throw setError(VBOX_E_FILE_ERROR,
946 tr("Couldn't calculate SHA1 digest for file '%s' (%Rrc)"),
947 RTPathFilename(pTask->locInfo.strPath.c_str()), vrc);
948 m->strOVFSHA1Digest = pszDigest;
949 RTStrFree(pszDigest);
950 }
951 catch(xml::Error &x)
952 {
953 rc = setError(VBOX_E_FILE_ERROR,
954 x.what());
955 }
956 catch(HRESULT aRC)
957 {
958 rc = aRC;
959 }
960
961 pTask->rc = rc;
962
963 if (!pTask->progress.isNull())
964 pTask->progress->notifyComplete(rc);
965
966 LogFlowFunc(("rc=%Rhrc\n", rc));
967 LogFlowFuncLeave();
968
969 return VINF_SUCCESS;
970}
971
972int Appliance::readS3(TaskImportOVF *pTask)
973{
974 LogFlowFuncEnter();
975 LogFlowFunc(("Appliance %p\n", this));
976
977 AutoCaller autoCaller(this);
978 if (FAILED(autoCaller.rc())) return autoCaller.rc();
979
980 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
981
982 HRESULT rc = S_OK;
983 int vrc = VINF_SUCCESS;
984 RTS3 hS3 = NIL_RTS3;
985 char szOSTmpDir[RTPATH_MAX];
986 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));
987 /* The template for the temporary directory created below */
988 char *pszTmpDir;
989 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir);
990 list< pair<Utf8Str, ULONG> > filesList;
991 Utf8Str strTmpOvf;
992
993 try
994 {
995 /* Extract the bucket */
996 Utf8Str tmpPath = pTask->locInfo.strPath;
997 Utf8Str bucket;
998 parseBucket(tmpPath, bucket);
999
1000 /* We need a temporary directory which we can put the OVF file & all
1001 * disk images in */
1002 vrc = RTDirCreateTemp(pszTmpDir);
1003 if (RT_FAILURE(vrc))
1004 throw setError(VBOX_E_FILE_ERROR,
1005 tr("Cannot create temporary directory '%s'"), pszTmpDir);
1006
1007 /* The temporary name of the target OVF file */
1008 strTmpOvf = Utf8StrFmt("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str()));
1009
1010 /* Next we have to download the OVF */
1011 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);
1012 if(RT_FAILURE(vrc))
1013 throw setError(VBOX_E_IPRT_ERROR,
1014 tr("Cannot create S3 service handler"));
1015 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask);
1016
1017 /* Get it */
1018 char *pszFilename = RTPathFilename(strTmpOvf.c_str());
1019 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strTmpOvf.c_str());
1020 if (RT_FAILURE(vrc))
1021 {
1022 if(vrc == VERR_S3_CANCELED)
1023 throw S_OK; /* todo: !!!!!!!!!!!!! */
1024 else if(vrc == VERR_S3_ACCESS_DENIED)
1025 throw setError(E_ACCESSDENIED,
1026 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);
1027 else if(vrc == VERR_S3_NOT_FOUND)
1028 throw setError(VBOX_E_FILE_ERROR,
1029 tr("Cannot download file '%s' from S3 storage server (File not found)"), pszFilename);
1030 else
1031 throw setError(VBOX_E_IPRT_ERROR,
1032 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc);
1033 }
1034
1035 /* Close the connection early */
1036 RTS3Destroy(hS3);
1037 hS3 = NIL_RTS3;
1038
1039 if (!pTask->progress.isNull())
1040 pTask->progress->SetNextOperation(Bstr(tr("Reading")), 1);
1041
1042 /* Prepare the temporary reading of the OVF */
1043 ComObjPtr<Progress> progress;
1044 LocationInfo li;
1045 li.strPath = strTmpOvf;
1046 /* Start the reading from the fs */
1047 rc = readImpl(li, progress);
1048 if (FAILED(rc)) throw rc;
1049
1050 /* Unlock the appliance for the reading thread */
1051 appLock.release();
1052 /* Wait until the reading is done, but report the progress back to the
1053 caller */
1054 ComPtr<IProgress> progressInt(progress);
1055 waitForAsyncProgress(pTask->progress, progressInt); /* Any errors will be thrown */
1056
1057 /* Again lock the appliance for the next steps */
1058 appLock.acquire();
1059 }
1060 catch(HRESULT aRC)
1061 {
1062 rc = aRC;
1063 }
1064 /* Cleanup */
1065 RTS3Destroy(hS3);
1066 /* Delete all files which where temporary created */
1067 if (RTPathExists(strTmpOvf.c_str()))
1068 {
1069 vrc = RTFileDelete(strTmpOvf.c_str());
1070 if(RT_FAILURE(vrc))
1071 rc = setError(VBOX_E_FILE_ERROR,
1072 tr("Cannot delete file '%s' (%Rrc)"), strTmpOvf.c_str(), vrc);
1073 }
1074 /* Delete the temporary directory */
1075 if (RTPathExists(pszTmpDir))
1076 {
1077 vrc = RTDirRemove(pszTmpDir);
1078 if(RT_FAILURE(vrc))
1079 rc = setError(VBOX_E_FILE_ERROR,
1080 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);
1081 }
1082 if (pszTmpDir)
1083 RTStrFree(pszTmpDir);
1084
1085 pTask->rc = rc;
1086
1087 if (!pTask->progress.isNull())
1088 pTask->progress->notifyComplete(rc);
1089
1090 LogFlowFunc(("rc=%Rhrc\n", rc));
1091 LogFlowFuncLeave();
1092
1093 return VINF_SUCCESS;
1094}
1095
1096int Appliance::importFS(TaskImportOVF *pTask)
1097{
1098 LogFlowFuncEnter();
1099 LogFlowFunc(("Appliance %p\n", this));
1100
1101 AutoCaller autoCaller(this);
1102 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1103
1104 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
1105
1106 HRESULT rc = S_OK;
1107
1108 // rollback for errors:
1109 // a list of images that we created/imported
1110 list<MyHardDiskAttachment> llHardDiskAttachments;
1111 list< ComPtr<IMedium> > llHardDisksCreated;
1112 list<Bstr> llMachinesRegistered; // list of string UUIDs
1113
1114 ComPtr<ISession> session;
1115 bool fSessionOpen = false;
1116 rc = session.createInprocObject(CLSID_Session);
1117 if (FAILED(rc)) return rc;
1118
1119 const OVFReader &reader = *m->pReader;
1120 // this is safe to access because this thread only gets started
1121 // if pReader != NULL
1122
1123 /* If an manifest file exists, verify the content. Therefore we need all
1124 * files which are referenced by the OVF & the OVF itself */
1125 Utf8Str strMfFile = manifestFileName(pTask->locInfo.strPath);
1126 list<Utf8Str> filesList;
1127 if (RTPathExists(strMfFile.c_str()))
1128 {
1129 Utf8Str strSrcDir(pTask->locInfo.strPath);
1130 strSrcDir.stripFilename();
1131 /* Add every disks of every virtual system to an internal list */
1132 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
1133 for (it = m->virtualSystemDescriptions.begin();
1134 it != m->virtualSystemDescriptions.end();
1135 ++it)
1136 {
1137 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
1138 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
1139 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
1140 for (itH = avsdeHDs.begin();
1141 itH != avsdeHDs.end();
1142 ++itH)
1143 {
1144 VirtualSystemDescriptionEntry *vsdeHD = *itH;
1145 /* Find the disk from the OVF's disk list */
1146 DiskImagesMap::const_iterator itDiskImage = reader.m_mapDisks.find(vsdeHD->strRef);
1147 const DiskImage &di = itDiskImage->second;
1148 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str());
1149 filesList.push_back(strSrcFilePath);
1150 }
1151 }
1152 /* Create the test list */
1153 PRTMANIFESTTEST pTestList = (PRTMANIFESTTEST)RTMemAllocZ(sizeof(RTMANIFESTTEST)*(filesList.size()+1));
1154 pTestList[0].pszTestFile = (char*)pTask->locInfo.strPath.c_str();
1155 pTestList[0].pszTestDigest = (char*)m->strOVFSHA1Digest.c_str();
1156 int vrc = VINF_SUCCESS;
1157 size_t i = 1;
1158 list<Utf8Str>::const_iterator it1;
1159 for (it1 = filesList.begin();
1160 it1 != filesList.end();
1161 ++it1, ++i)
1162 {
1163 char* pszDigest;
1164 vrc = RTSha1Digest((*it1).c_str(), &pszDigest);
1165 pTestList[i].pszTestFile = (char*)(*it1).c_str();
1166 pTestList[i].pszTestDigest = pszDigest;
1167 }
1168 size_t cIndexOnError;
1169 vrc = RTManifestVerify(strMfFile.c_str(), pTestList, filesList.size() + 1, &cIndexOnError);
1170 if (vrc == VERR_MANIFEST_DIGEST_MISMATCH)
1171 rc = setError(VBOX_E_FILE_ERROR,
1172 tr("The SHA1 digest of '%s' doesn't match to the one in '%s'"),
1173 RTPathFilename(pTestList[cIndexOnError].pszTestFile),
1174 RTPathFilename(strMfFile.c_str()));
1175 else if (RT_FAILURE(vrc))
1176 rc = setError(VBOX_E_FILE_ERROR,
1177 tr("Couldn't verify the content of '%s' against the available files (%Rrc)"),
1178 RTPathFilename(strMfFile.c_str()),
1179 vrc);
1180 /* Cleanup */
1181 for (size_t j = 1;
1182 j < filesList.size();
1183 ++j)
1184 RTStrFree(pTestList[j].pszTestDigest);
1185 RTMemFree(pTestList);
1186 if (FAILED(rc))
1187 {
1188 /* Return on error */
1189 pTask->rc = rc;
1190
1191 if (!pTask->progress.isNull())
1192 pTask->progress->notifyComplete(rc);
1193 return rc;
1194 }
1195 }
1196
1197 list<VirtualSystem>::const_iterator it;
1198 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1;
1199 /* Iterate through all virtual systems of that appliance */
1200 size_t i = 0;
1201 for (it = reader.m_llVirtualSystems.begin(),
1202 it1 = m->virtualSystemDescriptions.begin();
1203 it != reader.m_llVirtualSystems.end();
1204 ++it, ++it1, ++i)
1205 {
1206 const VirtualSystem &vsysThis = *it;
1207 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it1);
1208
1209 ComPtr<IMachine> pNewMachine;
1210
1211 /* Catch possible errors */
1212 try
1213 {
1214 /* Guest OS type */
1215 std::list<VirtualSystemDescriptionEntry*> vsdeOS;
1216 vsdeOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
1217 if (vsdeOS.size() < 1)
1218 throw setError(VBOX_E_FILE_ERROR,
1219 tr("Missing guest OS type"));
1220 const Utf8Str &strOsTypeVBox = vsdeOS.front()->strVbox;
1221
1222 /* Now that we know the base system get our internal defaults based on that. */
1223 ComPtr<IGuestOSType> osType;
1224 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), osType.asOutParam());
1225 if (FAILED(rc)) throw rc;
1226
1227 /* Create the machine */
1228 /* First get the name */
1229 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
1230 if (vsdeName.size() < 1)
1231 throw setError(VBOX_E_FILE_ERROR,
1232 tr("Missing VM name"));
1233 const Utf8Str &strNameVBox = vsdeName.front()->strVbox;
1234 rc = mVirtualBox->CreateMachine(Bstr(strNameVBox), Bstr(strOsTypeVBox),
1235 Bstr(), Bstr(), FALSE,
1236 pNewMachine.asOutParam());
1237 if (FAILED(rc)) throw rc;
1238
1239 // and the description
1240 std::list<VirtualSystemDescriptionEntry*> vsdeDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description);
1241 if (vsdeDescription.size())
1242 {
1243 const Utf8Str &strDescription = vsdeDescription.front()->strVbox;
1244 rc = pNewMachine->COMSETTER(Description)(Bstr(strDescription));
1245 if (FAILED(rc)) throw rc;
1246 }
1247
1248 /* CPU count */
1249 std::list<VirtualSystemDescriptionEntry*> vsdeCPU = vsdescThis->findByType(VirtualSystemDescriptionType_CPU);
1250 ComAssertMsgThrow(vsdeCPU.size() == 1, ("CPU count missing"), E_FAIL);
1251 const Utf8Str &cpuVBox = vsdeCPU.front()->strVbox;
1252 ULONG tmpCount = (ULONG)RTStrToUInt64(cpuVBox.c_str());
1253 rc = pNewMachine->COMSETTER(CPUCount)(tmpCount);
1254 if (FAILED(rc)) throw rc;
1255 bool fEnableIOApic = false;
1256 /* We need HWVirt & IO-APIC if more than one CPU is requested */
1257 if (tmpCount > 1)
1258 {
1259 rc = pNewMachine->SetHWVirtExProperty(HWVirtExPropertyType_Enabled, TRUE);
1260 if (FAILED(rc)) throw rc;
1261
1262 fEnableIOApic = true;
1263 }
1264
1265 /* RAM */
1266 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory);
1267 ComAssertMsgThrow(vsdeRAM.size() == 1, ("RAM size missing"), E_FAIL);
1268 const Utf8Str &memoryVBox = vsdeRAM.front()->strVbox;
1269 ULONG tt = (ULONG)RTStrToUInt64(memoryVBox.c_str());
1270 rc = pNewMachine->COMSETTER(MemorySize)(tt);
1271 if (FAILED(rc)) throw rc;
1272
1273 /* VRAM */
1274 /* Get the recommended VRAM for this guest OS type */
1275 ULONG vramVBox;
1276 rc = osType->COMGETTER(RecommendedVRAM)(&vramVBox);
1277 if (FAILED(rc)) throw rc;
1278
1279 /* Set the VRAM */
1280 rc = pNewMachine->COMSETTER(VRAMSize)(vramVBox);
1281 if (FAILED(rc)) throw rc;
1282
1283 /* I/O APIC: so far we have no setting for this. Enable it if we
1284 import a Windows VM because if if Windows was installed without IOAPIC,
1285 it will not mind finding an one later on, but if Windows was installed
1286 _with_ an IOAPIC, it will bluescreen if it's not found */
1287 Bstr bstrFamilyId;
1288 rc = osType->COMGETTER(FamilyId)(bstrFamilyId.asOutParam());
1289 if (FAILED(rc)) throw rc;
1290
1291 Utf8Str strFamilyId(bstrFamilyId);
1292 if (strFamilyId == "Windows")
1293 fEnableIOApic = true;
1294
1295 /* If IP-APIC should be enabled could be have different reasons.
1296 See CPU count & the Win test above. Here we enable it if it was
1297 previously requested. */
1298 if (fEnableIOApic)
1299 {
1300 ComPtr<IBIOSSettings> pBIOSSettings;
1301 rc = pNewMachine->COMGETTER(BIOSSettings)(pBIOSSettings.asOutParam());
1302 if (FAILED(rc)) throw rc;
1303
1304 rc = pBIOSSettings->COMSETTER(IOAPICEnabled)(TRUE);
1305 if (FAILED(rc)) throw rc;
1306 }
1307
1308 /* Audio Adapter */
1309 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard);
1310 /* @todo: we support one audio adapter only */
1311 if (vsdeAudioAdapter.size() > 0)
1312 {
1313 const Utf8Str& audioAdapterVBox = vsdeAudioAdapter.front()->strVbox;
1314 if (audioAdapterVBox.compare("null", Utf8Str::CaseInsensitive) != 0)
1315 {
1316 uint32_t audio = RTStrToUInt32(audioAdapterVBox.c_str());
1317 ComPtr<IAudioAdapter> audioAdapter;
1318 rc = pNewMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
1319 if (FAILED(rc)) throw rc;
1320 rc = audioAdapter->COMSETTER(Enabled)(true);
1321 if (FAILED(rc)) throw rc;
1322 rc = audioAdapter->COMSETTER(AudioController)(static_cast<AudioControllerType_T>(audio));
1323 if (FAILED(rc)) throw rc;
1324 }
1325 }
1326
1327#ifdef VBOX_WITH_USB
1328 /* USB Controller */
1329 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController = vsdescThis->findByType(VirtualSystemDescriptionType_USBController);
1330 // USB support is enabled if there's at least one such entry; to disable USB support,
1331 // the type of the USB item would have been changed to "ignore"
1332 bool fUSBEnabled = vsdeUSBController.size() > 0;
1333
1334 ComPtr<IUSBController> usbController;
1335 rc = pNewMachine->COMGETTER(USBController)(usbController.asOutParam());
1336 if (FAILED(rc)) throw rc;
1337 rc = usbController->COMSETTER(Enabled)(fUSBEnabled);
1338 if (FAILED(rc)) throw rc;
1339#endif /* VBOX_WITH_USB */
1340
1341 /* Change the network adapters */
1342 std::list<VirtualSystemDescriptionEntry*> vsdeNW = vsdescThis->findByType(VirtualSystemDescriptionType_NetworkAdapter);
1343 if (vsdeNW.size() == 0)
1344 {
1345 /* No network adapters, so we have to disable our default one */
1346 ComPtr<INetworkAdapter> nwVBox;
1347 rc = pNewMachine->GetNetworkAdapter(0, nwVBox.asOutParam());
1348 if (FAILED(rc)) throw rc;
1349 rc = nwVBox->COMSETTER(Enabled)(false);
1350 if (FAILED(rc)) throw rc;
1351 }
1352 else
1353 {
1354 list<VirtualSystemDescriptionEntry*>::const_iterator nwIt;
1355 /* Iterate through all network cards. We support 8 network adapters
1356 * at the maximum. (@todo: warn if there are more!) */
1357 size_t a = 0;
1358 for (nwIt = vsdeNW.begin();
1359 (nwIt != vsdeNW.end() && a < SchemaDefs::NetworkAdapterCount);
1360 ++nwIt, ++a)
1361 {
1362 const VirtualSystemDescriptionEntry* pvsys = *nwIt;
1363
1364 const Utf8Str &nwTypeVBox = pvsys->strVbox;
1365 uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str());
1366 ComPtr<INetworkAdapter> pNetworkAdapter;
1367 rc = pNewMachine->GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
1368 if (FAILED(rc)) throw rc;
1369 /* Enable the network card & set the adapter type */
1370 rc = pNetworkAdapter->COMSETTER(Enabled)(true);
1371 if (FAILED(rc)) throw rc;
1372 rc = pNetworkAdapter->COMSETTER(AdapterType)(static_cast<NetworkAdapterType_T>(tt1));
1373 if (FAILED(rc)) throw rc;
1374
1375 // default is NAT; change to "bridged" if extra conf says so
1376 if (!pvsys->strExtraConfig.compare("type=Bridged", Utf8Str::CaseInsensitive))
1377 {
1378 /* Attach to the right interface */
1379 rc = pNetworkAdapter->AttachToBridgedInterface();
1380 if (FAILED(rc)) throw rc;
1381 ComPtr<IHost> host;
1382 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam());
1383 if (FAILED(rc)) throw rc;
1384 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;
1385 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));
1386 if (FAILED(rc)) throw rc;
1387 /* We search for the first host network interface which
1388 * is usable for bridged networking */
1389 for (size_t j = 0;
1390 j < nwInterfaces.size();
1391 ++j)
1392 {
1393 HostNetworkInterfaceType_T itype;
1394 rc = nwInterfaces[j]->COMGETTER(InterfaceType)(&itype);
1395 if (FAILED(rc)) throw rc;
1396 if (itype == HostNetworkInterfaceType_Bridged)
1397 {
1398 Bstr name;
1399 rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam());
1400 if (FAILED(rc)) throw rc;
1401 /* Set the interface name to attach to */
1402 pNetworkAdapter->COMSETTER(HostInterface)(name);
1403 if (FAILED(rc)) throw rc;
1404 break;
1405 }
1406 }
1407 }
1408 /* Next test for host only interfaces */
1409 else if (!pvsys->strExtraConfig.compare("type=HostOnly", Utf8Str::CaseInsensitive))
1410 {
1411 /* Attach to the right interface */
1412 rc = pNetworkAdapter->AttachToHostOnlyInterface();
1413 if (FAILED(rc)) throw rc;
1414 ComPtr<IHost> host;
1415 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam());
1416 if (FAILED(rc)) throw rc;
1417 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;
1418 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));
1419 if (FAILED(rc)) throw rc;
1420 /* We search for the first host network interface which
1421 * is usable for host only networking */
1422 for (size_t j = 0;
1423 j < nwInterfaces.size();
1424 ++j)
1425 {
1426 HostNetworkInterfaceType_T itype;
1427 rc = nwInterfaces[j]->COMGETTER(InterfaceType)(&itype);
1428 if (FAILED(rc)) throw rc;
1429 if (itype == HostNetworkInterfaceType_HostOnly)
1430 {
1431 Bstr name;
1432 rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam());
1433 if (FAILED(rc)) throw rc;
1434 /* Set the interface name to attach to */
1435 pNetworkAdapter->COMSETTER(HostInterface)(name);
1436 if (FAILED(rc)) throw rc;
1437 break;
1438 }
1439 }
1440 }
1441 }
1442 }
1443
1444 /* Hard disk controller IDE */
1445 std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerIDE);
1446 if (vsdeHDCIDE.size() > 1)
1447 throw setError(VBOX_E_FILE_ERROR,
1448 tr("Too many IDE controllers in OVF; import facility only supports one"));
1449 if (vsdeHDCIDE.size() == 1)
1450 {
1451 ComPtr<IStorageController> pController;
1452 rc = pNewMachine->AddStorageController(Bstr("IDE Controller"), StorageBus_IDE, pController.asOutParam());
1453 if (FAILED(rc)) throw rc;
1454
1455 const char *pcszIDEType = vsdeHDCIDE.front()->strVbox.c_str();
1456 if (!strcmp(pcszIDEType, "PIIX3"))
1457 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX3);
1458 else if (!strcmp(pcszIDEType, "PIIX4"))
1459 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX4);
1460 else if (!strcmp(pcszIDEType, "ICH6"))
1461 rc = pController->COMSETTER(ControllerType)(StorageControllerType_ICH6);
1462 else
1463 throw setError(VBOX_E_FILE_ERROR,
1464 tr("Invalid IDE controller type \"%s\""),
1465 pcszIDEType);
1466 if (FAILED(rc)) throw rc;
1467 }
1468#ifdef VBOX_WITH_AHCI
1469 /* Hard disk controller SATA */
1470 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSATA = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSATA);
1471 if (vsdeHDCSATA.size() > 1)
1472 throw setError(VBOX_E_FILE_ERROR,
1473 tr("Too many SATA controllers in OVF; import facility only supports one"));
1474 if (vsdeHDCSATA.size() > 0)
1475 {
1476 ComPtr<IStorageController> pController;
1477 const Utf8Str &hdcVBox = vsdeHDCSATA.front()->strVbox;
1478 if (hdcVBox == "AHCI")
1479 {
1480 rc = pNewMachine->AddStorageController(Bstr("SATA Controller"), StorageBus_SATA, pController.asOutParam());
1481 if (FAILED(rc)) throw rc;
1482 }
1483 else
1484 throw setError(VBOX_E_FILE_ERROR,
1485 tr("Invalid SATA controller type \"%s\""),
1486 hdcVBox.c_str());
1487 }
1488#endif /* VBOX_WITH_AHCI */
1489
1490#ifdef VBOX_WITH_LSILOGIC
1491 /* Hard disk controller SCSI */
1492 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSCSI = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSCSI);
1493 if (vsdeHDCSCSI.size() > 1)
1494 throw setError(VBOX_E_FILE_ERROR,
1495 tr("Too many SCSI controllers in OVF; import facility only supports one"));
1496 if (vsdeHDCSCSI.size() > 0)
1497 {
1498 ComPtr<IStorageController> pController;
1499 StorageControllerType_T controllerType;
1500 const Utf8Str &hdcVBox = vsdeHDCSCSI.front()->strVbox;
1501 if (hdcVBox == "LsiLogic")
1502 controllerType = StorageControllerType_LsiLogic;
1503 else if (hdcVBox == "BusLogic")
1504 controllerType = StorageControllerType_BusLogic;
1505 else
1506 throw setError(VBOX_E_FILE_ERROR,
1507 tr("Invalid SCSI controller type \"%s\""),
1508 hdcVBox.c_str());
1509
1510 rc = pNewMachine->AddStorageController(Bstr("SCSI Controller"), StorageBus_SCSI, pController.asOutParam());
1511 if (FAILED(rc)) throw rc;
1512 rc = pController->COMSETTER(ControllerType)(controllerType);
1513 if (FAILED(rc)) throw rc;
1514 }
1515#endif /* VBOX_WITH_LSILOGIC */
1516
1517 /* Now its time to register the machine before we add any hard disks */
1518 rc = mVirtualBox->RegisterMachine(pNewMachine);
1519 if (FAILED(rc)) throw rc;
1520
1521 Bstr bstrNewMachineId;
1522 rc = pNewMachine->COMGETTER(Id)(bstrNewMachineId.asOutParam());
1523 if (FAILED(rc)) throw rc;
1524
1525 // store new machine for roll-back in case of errors
1526 llMachinesRegistered.push_back(bstrNewMachineId);
1527
1528 // Add floppies and CD-ROMs to the appropriate controllers.
1529 std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsdescThis->findByType(VirtualSystemDescriptionType_Floppy);
1530 if (vsdeFloppy.size() > 1)
1531 throw setError(VBOX_E_FILE_ERROR,
1532 tr("Too many floppy controllers in OVF; import facility only supports one"));
1533 std::list<VirtualSystemDescriptionEntry*> vsdeCDROM = vsdescThis->findByType(VirtualSystemDescriptionType_CDROM);
1534 if ( (vsdeFloppy.size() > 0)
1535 || (vsdeCDROM.size() > 0)
1536 )
1537 {
1538 // If there's an error here we need to close the session, so
1539 // we need another try/catch block.
1540
1541 try
1542 {
1543 /* In order to attach things we need to open a session
1544 * for the new machine */
1545 rc = mVirtualBox->OpenSession(session, bstrNewMachineId);
1546 if (FAILED(rc)) throw rc;
1547 fSessionOpen = true;
1548
1549 ComPtr<IMachine> sMachine;
1550 rc = session->COMGETTER(Machine)(sMachine.asOutParam());
1551 if (FAILED(rc)) throw rc;
1552
1553 // floppy first
1554 if (vsdeFloppy.size() == 1)
1555 {
1556 ComPtr<IStorageController> pController;
1557 rc = sMachine->AddStorageController(Bstr("Floppy Controller"), StorageBus_Floppy, pController.asOutParam());
1558 if (FAILED(rc)) throw rc;
1559
1560 Bstr bstrName;
1561 rc = pController->COMGETTER(Name)(bstrName.asOutParam());
1562 if (FAILED(rc)) throw rc;
1563
1564 // this is for rollback later
1565 MyHardDiskAttachment mhda;
1566 mhda.bstrUuid = bstrNewMachineId;
1567 mhda.pMachine = pNewMachine;
1568 mhda.controllerType = bstrName;
1569 mhda.lChannel = 0;
1570 mhda.lDevice = 0;
1571
1572 Log(("Attaching floppy\n"));
1573
1574 rc = sMachine->AttachDevice(mhda.controllerType,
1575 mhda.lChannel,
1576 mhda.lDevice,
1577 DeviceType_Floppy,
1578 NULL);
1579 if (FAILED(rc)) throw rc;
1580
1581 llHardDiskAttachments.push_back(mhda);
1582 }
1583
1584
1585 // CD-ROMs next
1586 for (std::list<VirtualSystemDescriptionEntry*>::const_iterator jt = vsdeCDROM.begin();
1587 jt != vsdeCDROM.end();
1588 ++jt)
1589 {
1590 // for now always attach to secondary master on IDE controller;
1591 // there seems to be no useful information in OVF where else to
1592 // attach jt (@todo test with latest versions of OVF software)
1593
1594 // find the IDE controller
1595 const HardDiskController *pController = NULL;
1596 for (ControllersMap::const_iterator kt = vsysThis.mapControllers.begin();
1597 kt != vsysThis.mapControllers.end();
1598 ++kt)
1599 {
1600 if (kt->second.system == HardDiskController::IDE)
1601 {
1602 pController = &kt->second;
1603 }
1604 }
1605
1606 if (!pController)
1607 throw setError(VBOX_E_FILE_ERROR,
1608 tr("OVF wants a CD-ROM drive but cannot find IDE controller, which is required in this version of VirtualBox"));
1609
1610 // this is for rollback later
1611 MyHardDiskAttachment mhda;
1612 mhda.bstrUuid = bstrNewMachineId;
1613 mhda.pMachine = pNewMachine;
1614
1615 ConvertDiskAttachmentValues(*pController,
1616 2, // interpreted as secondary master
1617 mhda.controllerType, // Bstr
1618 mhda.lChannel,
1619 mhda.lDevice);
1620
1621 Log(("Attaching CD-ROM to channel %d on device %d\n", mhda.lChannel, mhda.lDevice));
1622
1623 rc = sMachine->AttachDevice(mhda.controllerType,
1624 mhda.lChannel,
1625 mhda.lDevice,
1626 DeviceType_DVD,
1627 NULL);
1628 if (FAILED(rc)) throw rc;
1629
1630 llHardDiskAttachments.push_back(mhda);
1631 } // end for (itHD = avsdeHDs.begin();
1632
1633 rc = sMachine->SaveSettings();
1634 if (FAILED(rc)) throw rc;
1635
1636 // only now that we're done with all disks, close the session
1637 rc = session->Close();
1638 if (FAILED(rc)) throw rc;
1639 fSessionOpen = false;
1640 }
1641 catch(HRESULT /* aRC */)
1642 {
1643 if (fSessionOpen)
1644 session->Close();
1645
1646 throw;
1647 }
1648 }
1649
1650 /* Create the hard disks & connect them to the appropriate controllers. */
1651 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
1652 if (avsdeHDs.size() > 0)
1653 {
1654 // If there's an error here we need to close the session, so
1655 // we need another try/catch block.
1656 ComPtr<IMedium> srcHdVBox;
1657 bool fSourceHdNeedsClosing = false;
1658
1659 try
1660 {
1661 /* In order to attach hard disks we need to open a session
1662 * for the new machine */
1663 rc = mVirtualBox->OpenSession(session, bstrNewMachineId);
1664 if (FAILED(rc)) throw rc;
1665 fSessionOpen = true;
1666
1667 /* The disk image has to be on the same place as the OVF file. So
1668 * strip the filename out of the full file path. */
1669 Utf8Str strSrcDir(pTask->locInfo.strPath);
1670 strSrcDir.stripFilename();
1671
1672 /* Iterate over all given disk images */
1673 list<VirtualSystemDescriptionEntry*>::const_iterator itHD;
1674 for (itHD = avsdeHDs.begin();
1675 itHD != avsdeHDs.end();
1676 ++itHD)
1677 {
1678 VirtualSystemDescriptionEntry *vsdeHD = *itHD;
1679
1680 /* Check if the destination file exists already or the
1681 * destination path is empty. */
1682 if ( vsdeHD->strVbox.isEmpty()
1683 || RTPathExists(vsdeHD->strVbox.c_str())
1684 )
1685 /* This isn't allowed */
1686 throw setError(VBOX_E_FILE_ERROR,
1687 tr("Destination file '%s' exists",
1688 vsdeHD->strVbox.c_str()));
1689
1690 /* Find the disk from the OVF's disk list */
1691 DiskImagesMap::const_iterator itDiskImage = reader.m_mapDisks.find(vsdeHD->strRef);
1692 /* vsdeHD->strRef contains the disk identifier (e.g. "vmdisk1"), which should exist
1693 in the virtual system's disks map under that ID and also in the global images map. */
1694 VirtualDisksMap::const_iterator itVirtualDisk = vsysThis.mapVirtualDisks.find(vsdeHD->strRef);
1695
1696 if ( itDiskImage == reader.m_mapDisks.end()
1697 || itVirtualDisk == vsysThis.mapVirtualDisks.end()
1698 )
1699 throw setError(E_FAIL,
1700 tr("Internal inconsistency looking up disk images."));
1701
1702 const DiskImage &di = itDiskImage->second;
1703 const VirtualDisk &vd = itVirtualDisk->second;
1704
1705 /* Make sure all target directories exists */
1706 rc = VirtualBox::ensureFilePathExists(vsdeHD->strVbox.c_str());
1707 if (FAILED(rc))
1708 throw rc;
1709
1710 // subprogress object for hard disk
1711 ComPtr<IProgress> pProgress2;
1712
1713 ComPtr<IMedium> dstHdVBox;
1714 /* If strHref is empty we have to create a new file */
1715 if (di.strHref.isEmpty())
1716 {
1717 /* Which format to use? */
1718 Bstr srcFormat = L"VDI";
1719 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)
1720 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive))
1721 srcFormat = L"VMDK";
1722 /* Create an empty hard disk */
1723 rc = mVirtualBox->CreateHardDisk(srcFormat, Bstr(vsdeHD->strVbox), dstHdVBox.asOutParam());
1724 if (FAILED(rc)) throw rc;
1725
1726 /* Create a dynamic growing disk image with the given capacity */
1727 rc = dstHdVBox->CreateBaseStorage(di.iCapacity / _1M, MediumVariant_Standard, pProgress2.asOutParam());
1728 if (FAILED(rc)) throw rc;
1729
1730 /* Advance to the next operation */
1731 if (!pTask->progress.isNull())
1732 pTask->progress->SetNextOperation(BstrFmt(tr("Creating virtual disk image '%s'"), vsdeHD->strVbox.c_str()),
1733 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally
1734 }
1735 else
1736 {
1737 /* Construct the source file path */
1738 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str());
1739 /* Check if the source file exists */
1740 if (!RTPathExists(strSrcFilePath.c_str()))
1741 /* This isn't allowed */
1742 throw setError(VBOX_E_FILE_ERROR,
1743 tr("Source virtual disk image file '%s' doesn't exist"),
1744 strSrcFilePath.c_str());
1745
1746 /* Clone the disk image (this is necessary cause the id has
1747 * to be recreated for the case the same hard disk is
1748 * attached already from a previous import) */
1749
1750 /* First open the existing disk image */
1751 rc = mVirtualBox->OpenHardDisk(Bstr(strSrcFilePath),
1752 AccessMode_ReadOnly,
1753 false,
1754 NULL,
1755 false,
1756 NULL,
1757 srcHdVBox.asOutParam());
1758 if (FAILED(rc)) throw rc;
1759 fSourceHdNeedsClosing = true;
1760
1761 /* We need the format description of the source disk image */
1762 Bstr srcFormat;
1763 rc = srcHdVBox->COMGETTER(Format)(srcFormat.asOutParam());
1764 if (FAILED(rc)) throw rc;
1765 /* Create a new hard disk interface for the destination disk image */
1766 rc = mVirtualBox->CreateHardDisk(srcFormat, Bstr(vsdeHD->strVbox), dstHdVBox.asOutParam());
1767 if (FAILED(rc)) throw rc;
1768 /* Clone the source disk image */
1769 rc = srcHdVBox->CloneTo(dstHdVBox, MediumVariant_Standard, NULL, pProgress2.asOutParam());
1770 if (FAILED(rc)) throw rc;
1771
1772 /* Advance to the next operation */
1773 if (!pTask->progress.isNull())
1774 pTask->progress->SetNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), strSrcFilePath.c_str()),
1775 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally);
1776 }
1777
1778 // now wait for the background disk operation to complete; this throws HRESULTs on error
1779 waitForAsyncProgress(pTask->progress, pProgress2);
1780
1781 if (fSourceHdNeedsClosing)
1782 {
1783 rc = srcHdVBox->Close();
1784 if (FAILED(rc)) throw rc;
1785 fSourceHdNeedsClosing = false;
1786 }
1787
1788 llHardDisksCreated.push_back(dstHdVBox);
1789 /* Now use the new uuid to attach the disk image to our new machine */
1790 ComPtr<IMachine> sMachine;
1791 rc = session->COMGETTER(Machine)(sMachine.asOutParam());
1792 if (FAILED(rc)) throw rc;
1793 Bstr hdId;
1794 rc = dstHdVBox->COMGETTER(Id)(hdId.asOutParam());
1795 if (FAILED(rc)) throw rc;
1796
1797 /* For now we assume we have one controller of every type only */
1798 HardDiskController hdc = (*vsysThis.mapControllers.find(vd.idController)).second;
1799
1800 // this is for rollback later
1801 MyHardDiskAttachment mhda;
1802 mhda.bstrUuid = bstrNewMachineId;
1803 mhda.pMachine = pNewMachine;
1804
1805 ConvertDiskAttachmentValues(hdc,
1806 vd.ulAddressOnParent,
1807 mhda.controllerType, // Bstr
1808 mhda.lChannel,
1809 mhda.lDevice);
1810
1811 Log(("Attaching disk %s to channel %d on device %d\n", vsdeHD->strVbox.c_str(), mhda.lChannel, mhda.lDevice));
1812
1813 rc = sMachine->AttachDevice(mhda.controllerType,
1814 mhda.lChannel,
1815 mhda.lDevice,
1816 DeviceType_HardDisk,
1817 hdId);
1818 if (FAILED(rc)) throw rc;
1819
1820 llHardDiskAttachments.push_back(mhda);
1821
1822 rc = sMachine->SaveSettings();
1823 if (FAILED(rc)) throw rc;
1824 } // end for (itHD = avsdeHDs.begin();
1825
1826 // only now that we're done with all disks, close the session
1827 rc = session->Close();
1828 if (FAILED(rc)) throw rc;
1829 fSessionOpen = false;
1830 }
1831 catch(HRESULT /* aRC */)
1832 {
1833 if (fSourceHdNeedsClosing)
1834 srcHdVBox->Close();
1835
1836 if (fSessionOpen)
1837 session->Close();
1838
1839 throw;
1840 }
1841 }
1842 }
1843 catch(HRESULT aRC)
1844 {
1845 rc = aRC;
1846 }
1847
1848 if (FAILED(rc))
1849 break;
1850
1851 } // for (it = pAppliance->m->llVirtualSystems.begin(),
1852
1853 if (FAILED(rc))
1854 {
1855 // with _whatever_ error we've had, do a complete roll-back of
1856 // machines and disks we've created; unfortunately this is
1857 // not so trivially done...
1858
1859 HRESULT rc2;
1860 // detach all hard disks from all machines we created
1861 list<MyHardDiskAttachment>::iterator itM;
1862 for (itM = llHardDiskAttachments.begin();
1863 itM != llHardDiskAttachments.end();
1864 ++itM)
1865 {
1866 const MyHardDiskAttachment &mhda = *itM;
1867 Bstr bstrUuid(mhda.bstrUuid); // make a copy, Windows can't handle const Bstr
1868 rc2 = mVirtualBox->OpenSession(session, bstrUuid);
1869 if (SUCCEEDED(rc2))
1870 {
1871 ComPtr<IMachine> sMachine;
1872 rc2 = session->COMGETTER(Machine)(sMachine.asOutParam());
1873 if (SUCCEEDED(rc2))
1874 {
1875 rc2 = sMachine->DetachDevice(Bstr(mhda.controllerType), mhda.lChannel, mhda.lDevice);
1876 rc2 = sMachine->SaveSettings();
1877 }
1878 session->Close();
1879 }
1880 }
1881
1882 // now clean up all hard disks we created
1883 list< ComPtr<IMedium> >::iterator itHD;
1884 for (itHD = llHardDisksCreated.begin();
1885 itHD != llHardDisksCreated.end();
1886 ++itHD)
1887 {
1888 ComPtr<IMedium> pDisk = *itHD;
1889 ComPtr<IProgress> pProgress;
1890 rc2 = pDisk->DeleteStorage(pProgress.asOutParam());
1891 rc2 = pProgress->WaitForCompletion(-1);
1892 }
1893
1894 // finally, deregister and remove all machines
1895 list<Bstr>::iterator itID;
1896 for (itID = llMachinesRegistered.begin();
1897 itID != llMachinesRegistered.end();
1898 ++itID)
1899 {
1900 Bstr bstrGuid = *itID; // make a copy, Windows can't handle const Bstr
1901 ComPtr<IMachine> failedMachine;
1902 rc2 = mVirtualBox->UnregisterMachine(bstrGuid, failedMachine.asOutParam());
1903 if (SUCCEEDED(rc2))
1904 rc2 = failedMachine->DeleteSettings();
1905 }
1906 }
1907
1908 pTask->rc = rc;
1909
1910 if (!pTask->progress.isNull())
1911 pTask->progress->notifyComplete(rc);
1912
1913 LogFlowFunc(("rc=%Rhrc\n", rc));
1914 LogFlowFuncLeave();
1915
1916 return VINF_SUCCESS;
1917}
1918
1919/**
1920 * Helper that converts VirtualSystem attachment values into VirtualBox attachment values.
1921 * Throws HRESULT values on errors!
1922 *
1923 * @param hdc
1924 * @param vd
1925 * @param mhda
1926 */
1927void Appliance::ConvertDiskAttachmentValues(const HardDiskController &hdc,
1928 uint32_t ulAddressOnParent,
1929 Bstr &controllerType,
1930 int32_t &lChannel,
1931 int32_t &lDevice)
1932{
1933 switch (hdc.system)
1934 {
1935 case HardDiskController::IDE:
1936 // For the IDE bus, the channel parameter can be either 0 or 1, to specify the primary
1937 // or secondary IDE controller, respectively. For the primary controller of the IDE bus,
1938 // the device number can be either 0 or 1, to specify the master or the slave device,
1939 // respectively. For the secondary IDE controller, the device number is always 1 because
1940 // the master device is reserved for the CD-ROM drive.
1941 controllerType = Bstr("IDE Controller");
1942 switch (ulAddressOnParent)
1943 {
1944 case 0: // interpret this as primary master
1945 lChannel = (long)0;
1946 lDevice = (long)0;
1947 break;
1948
1949 case 1: // interpret this as primary slave
1950 lChannel = (long)0;
1951 lDevice = (long)1;
1952 break;
1953
1954 case 2: // interpret this as secondary master
1955 lChannel = (long)1;
1956 lDevice = (long)0;
1957 break;
1958
1959 case 3: // interpret this as secondary slave
1960 lChannel = (long)1;
1961 lDevice = (long)1;
1962 break;
1963
1964 default:
1965 throw setError(VBOX_E_NOT_SUPPORTED,
1966 tr("Invalid channel %RI16 specified; IDE controllers support only 0, 1 or 2"), ulAddressOnParent);
1967 break;
1968 }
1969 break;
1970
1971 case HardDiskController::SATA:
1972 controllerType = Bstr("SATA Controller");
1973 lChannel = (long)ulAddressOnParent;
1974 lDevice = (long)0;
1975 break;
1976
1977 case HardDiskController::SCSI:
1978 controllerType = Bstr("SCSI Controller");
1979 lChannel = (long)ulAddressOnParent;
1980 lDevice = (long)0;
1981 break;
1982
1983 default: break;
1984 }
1985}
1986
1987int Appliance::importS3(TaskImportOVF *pTask)
1988{
1989 LogFlowFuncEnter();
1990 LogFlowFunc(("Appliance %p\n", this));
1991
1992 AutoCaller autoCaller(this);
1993 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1994
1995 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
1996
1997 int vrc = VINF_SUCCESS;
1998 RTS3 hS3 = NIL_RTS3;
1999 char szOSTmpDir[RTPATH_MAX];
2000 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));
2001 /* The template for the temporary directory created below */
2002 char *pszTmpDir;
2003 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir);
2004 list< pair<Utf8Str, ULONG> > filesList;
2005
2006 HRESULT rc = S_OK;
2007 try
2008 {
2009 /* Extract the bucket */
2010 Utf8Str tmpPath = pTask->locInfo.strPath;
2011 Utf8Str bucket;
2012 parseBucket(tmpPath, bucket);
2013
2014 /* We need a temporary directory which we can put the all disk images
2015 * in */
2016 vrc = RTDirCreateTemp(pszTmpDir);
2017 if (RT_FAILURE(vrc))
2018 throw setError(VBOX_E_FILE_ERROR,
2019 tr("Cannot create temporary directory '%s'"), pszTmpDir);
2020
2021 /* Add every disks of every virtual system to an internal list */
2022 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
2023 for (it = m->virtualSystemDescriptions.begin();
2024 it != m->virtualSystemDescriptions.end();
2025 ++it)
2026 {
2027 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
2028 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
2029 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
2030 for (itH = avsdeHDs.begin();
2031 itH != avsdeHDs.end();
2032 ++itH)
2033 {
2034 const Utf8Str &strTargetFile = (*itH)->strOvf;
2035 if (!strTargetFile.isEmpty())
2036 {
2037 /* The temporary name of the target disk file */
2038 Utf8StrFmt strTmpDisk("%s/%s", pszTmpDir, RTPathFilename(strTargetFile.c_str()));
2039 filesList.push_back(pair<Utf8Str, ULONG>(strTmpDisk, (*itH)->ulSizeMB));
2040 }
2041 }
2042 }
2043
2044 /* Next we have to download the disk images */
2045 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);
2046 if(RT_FAILURE(vrc))
2047 throw setError(VBOX_E_IPRT_ERROR,
2048 tr("Cannot create S3 service handler"));
2049 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask);
2050
2051 /* Download all files */
2052 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
2053 {
2054 const pair<Utf8Str, ULONG> &s = (*it1);
2055 const Utf8Str &strSrcFile = s.first;
2056 /* Construct the source file name */
2057 char *pszFilename = RTPathFilename(strSrcFile.c_str());
2058 /* Advance to the next operation */
2059 if (!pTask->progress.isNull())
2060 pTask->progress->SetNextOperation(BstrFmt(tr("Downloading file '%s'"), pszFilename), s.second);
2061
2062 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strSrcFile.c_str());
2063 if (RT_FAILURE(vrc))
2064 {
2065 if(vrc == VERR_S3_CANCELED)
2066 throw S_OK; /* todo: !!!!!!!!!!!!! */
2067 else if(vrc == VERR_S3_ACCESS_DENIED)
2068 throw setError(E_ACCESSDENIED,
2069 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);
2070 else if(vrc == VERR_S3_NOT_FOUND)
2071 throw setError(VBOX_E_FILE_ERROR,
2072 tr("Cannot download file '%s' from S3 storage server (File not found)"), pszFilename);
2073 else
2074 throw setError(VBOX_E_IPRT_ERROR,
2075 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc);
2076 }
2077 }
2078
2079 /* Provide a OVF file (haven't to exist) so the import routine can
2080 * figure out where the disk images/manifest file are located. */
2081 Utf8StrFmt strTmpOvf("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str()));
2082 /* Now check if there is an manifest file. This is optional. */
2083 Utf8Str strManifestFile = manifestFileName(strTmpOvf);
2084 char *pszFilename = RTPathFilename(strManifestFile.c_str());
2085 if (!pTask->progress.isNull())
2086 pTask->progress->SetNextOperation(BstrFmt(tr("Downloading file '%s'"), pszFilename), 1);
2087
2088 /* Try to download it. If the error is VERR_S3_NOT_FOUND, it isn't fatal. */
2089 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strManifestFile.c_str());
2090 if (RT_SUCCESS(vrc))
2091 filesList.push_back(pair<Utf8Str, ULONG>(strManifestFile, 0));
2092 else if (RT_FAILURE(vrc))
2093 {
2094 if(vrc == VERR_S3_CANCELED)
2095 throw S_OK; /* todo: !!!!!!!!!!!!! */
2096 else if(vrc == VERR_S3_NOT_FOUND)
2097 vrc = VINF_SUCCESS; /* Not found is ok */
2098 else if(vrc == VERR_S3_ACCESS_DENIED)
2099 throw setError(E_ACCESSDENIED,
2100 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);
2101 else
2102 throw setError(VBOX_E_IPRT_ERROR,
2103 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc);
2104 }
2105
2106 /* Close the connection early */
2107 RTS3Destroy(hS3);
2108 hS3 = NIL_RTS3;
2109
2110 if (!pTask->progress.isNull())
2111 pTask->progress->SetNextOperation(BstrFmt(tr("Importing appliance")), m->ulWeightPerOperation);
2112
2113 ComObjPtr<Progress> progress;
2114 /* Import the whole temporary OVF & the disk images */
2115 LocationInfo li;
2116 li.strPath = strTmpOvf;
2117 rc = importImpl(li, progress);
2118 if (FAILED(rc)) throw rc;
2119
2120 /* Unlock the appliance for the fs import thread */
2121 appLock.release();
2122 /* Wait until the import is done, but report the progress back to the
2123 caller */
2124 ComPtr<IProgress> progressInt(progress);
2125 waitForAsyncProgress(pTask->progress, progressInt); /* Any errors will be thrown */
2126
2127 /* Again lock the appliance for the next steps */
2128 appLock.acquire();
2129 }
2130 catch(HRESULT aRC)
2131 {
2132 rc = aRC;
2133 }
2134 /* Cleanup */
2135 RTS3Destroy(hS3);
2136 /* Delete all files which where temporary created */
2137 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
2138 {
2139 const char *pszFilePath = (*it1).first.c_str();
2140 if (RTPathExists(pszFilePath))
2141 {
2142 vrc = RTFileDelete(pszFilePath);
2143 if(RT_FAILURE(vrc))
2144 rc = setError(VBOX_E_FILE_ERROR,
2145 tr("Cannot delete file '%s' (%Rrc)"), pszFilePath, vrc);
2146 }
2147 }
2148 /* Delete the temporary directory */
2149 if (RTPathExists(pszTmpDir))
2150 {
2151 vrc = RTDirRemove(pszTmpDir);
2152 if(RT_FAILURE(vrc))
2153 rc = setError(VBOX_E_FILE_ERROR,
2154 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);
2155 }
2156 if (pszTmpDir)
2157 RTStrFree(pszTmpDir);
2158
2159 pTask->rc = rc;
2160
2161 if (!pTask->progress.isNull())
2162 pTask->progress->notifyComplete(rc);
2163
2164 LogFlowFunc(("rc=%Rhrc\n", rc));
2165 LogFlowFuncLeave();
2166
2167 return VINF_SUCCESS;
2168}
2169
2170HRESULT Appliance::writeImpl(int aFormat, const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)
2171{
2172 HRESULT rc = S_OK;
2173 try
2174 {
2175 /* Initialize our worker task */
2176 std::auto_ptr<TaskExportOVF> task(new TaskExportOVF(this));
2177 /* What should the task do */
2178 task->taskType = TaskExportOVF::Write;
2179 /* The OVF version to write */
2180 task->enFormat = (TaskExportOVF::OVFFormat)aFormat;
2181 /* Copy the current location info to the task */
2182 task->locInfo = aLocInfo;
2183
2184 Bstr progressDesc = BstrFmt(tr("Export appliance '%s'"),
2185 task->locInfo.strPath.c_str());
2186
2187 /* todo: This progress init stuff should be done a little bit more generic */
2188 if (task->locInfo.storageType == VFSType_File)
2189 rc = setUpProgressFS(aProgress, progressDesc);
2190 else
2191 rc = setUpProgressWriteS3(aProgress, progressDesc);
2192
2193 task->progress = aProgress;
2194
2195 rc = task->startThread();
2196 if (FAILED(rc)) throw rc;
2197
2198 /* Don't destruct on success */
2199 task.release();
2200 }
2201 catch (HRESULT aRC)
2202 {
2203 rc = aRC;
2204 }
2205
2206 return rc;
2207}
2208
2209DECLCALLBACK(int) Appliance::taskThreadWriteOVF(RTTHREAD /* aThread */, void *pvUser)
2210{
2211 std::auto_ptr<TaskExportOVF> task(static_cast<TaskExportOVF*>(pvUser));
2212 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
2213
2214 Appliance *pAppliance = task->pAppliance;
2215
2216 LogFlowFuncEnter();
2217 LogFlowFunc(("Appliance %p\n", pAppliance));
2218
2219 HRESULT rc = S_OK;
2220
2221 switch(task->taskType)
2222 {
2223 case TaskExportOVF::Write:
2224 {
2225 if (task->locInfo.storageType == VFSType_File)
2226 rc = pAppliance->writeFS(task.get());
2227 else if (task->locInfo.storageType == VFSType_S3)
2228 rc = pAppliance->writeS3(task.get());
2229 break;
2230 }
2231 }
2232
2233 LogFlowFunc(("rc=%Rhrc\n", rc));
2234 LogFlowFuncLeave();
2235
2236 return VINF_SUCCESS;
2237}
2238
2239int Appliance::TaskExportOVF::startThread()
2240{
2241 int vrc = RTThreadCreate(NULL, Appliance::taskThreadWriteOVF, this,
2242 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
2243 "Appliance::Task");
2244
2245 ComAssertMsgRCRet(vrc,
2246 ("Could not create taskThreadWriteOVF (%Rrc)\n", vrc), E_FAIL);
2247
2248 return S_OK;
2249}
2250
2251int Appliance::writeFS(TaskExportOVF *pTask)
2252{
2253 LogFlowFuncEnter();
2254 LogFlowFunc(("Appliance %p\n", this));
2255
2256 AutoCaller autoCaller(this);
2257 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2258
2259 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
2260
2261 HRESULT rc = S_OK;
2262
2263 try
2264 {
2265 xml::Document doc;
2266 xml::ElementNode *pelmRoot = doc.createRootElement("Envelope");
2267
2268 pelmRoot->setAttribute("ovf:version", (pTask->enFormat == TaskExportOVF::OVF_1_0) ? "1.0" : "0.9");
2269 pelmRoot->setAttribute("xml:lang", "en-US");
2270
2271 Utf8Str strNamespace = (pTask->enFormat == TaskExportOVF::OVF_0_9)
2272 ? "http://www.vmware.com/schema/ovf/1/envelope" // 0.9
2273 : "http://schemas.dmtf.org/ovf/envelope/1"; // 1.0
2274 pelmRoot->setAttribute("xmlns", strNamespace);
2275 pelmRoot->setAttribute("xmlns:ovf", strNamespace);
2276
2277// pelmRoot->setAttribute("xmlns:ovfstr", "http://schema.dmtf.org/ovf/strings/1");
2278 pelmRoot->setAttribute("xmlns:rasd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData");
2279 pelmRoot->setAttribute("xmlns:vssd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData");
2280 pelmRoot->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
2281// pelmRoot->setAttribute("xsi:schemaLocation", "http://schemas.dmtf.org/ovf/envelope/1 ../ovf-envelope.xsd");
2282
2283 // <Envelope>/<References>
2284 xml::ElementNode *pelmReferences = pelmRoot->createChild("References"); // 0.9 and 1.0
2285
2286 /* <Envelope>/<DiskSection>:
2287 <DiskSection>
2288 <Info>List of the virtual disks used in the package</Info>
2289 <Disk ovf:capacity="4294967296" ovf:diskId="lamp" ovf:format="http://www.vmware.com/specifications/vmdk.html#compressed" ovf:populatedSize="1924967692"/>
2290 </DiskSection> */
2291 xml::ElementNode *pelmDiskSection;
2292 if (pTask->enFormat == TaskExportOVF::OVF_0_9)
2293 {
2294 // <Section xsi:type="ovf:DiskSection_Type">
2295 pelmDiskSection = pelmRoot->createChild("Section");
2296 pelmDiskSection->setAttribute("xsi:type", "ovf:DiskSection_Type");
2297 }
2298 else
2299 pelmDiskSection = pelmRoot->createChild("DiskSection");
2300
2301 xml::ElementNode *pelmDiskSectionInfo = pelmDiskSection->createChild("Info");
2302 pelmDiskSectionInfo->addContent("List of the virtual disks used in the package");
2303 // for now, set up a map so we have a list of unique disk names (to make
2304 // sure the same disk name is only added once)
2305 map<Utf8Str, const VirtualSystemDescriptionEntry*> mapDisks;
2306
2307 /* <Envelope>/<NetworkSection>:
2308 <NetworkSection>
2309 <Info>Logical networks used in the package</Info>
2310 <Network ovf:name="VM Network">
2311 <Description>The network that the LAMP Service will be available on</Description>
2312 </Network>
2313 </NetworkSection> */
2314 xml::ElementNode *pelmNetworkSection;
2315 if (pTask->enFormat == TaskExportOVF::OVF_0_9)
2316 {
2317 // <Section xsi:type="ovf:NetworkSection_Type">
2318 pelmNetworkSection = pelmRoot->createChild("Section");
2319 pelmNetworkSection->setAttribute("xsi:type", "ovf:NetworkSection_Type");
2320 }
2321 else
2322 pelmNetworkSection = pelmRoot->createChild("NetworkSection");
2323
2324 xml::ElementNode *pelmNetworkSectionInfo = pelmNetworkSection->createChild("Info");
2325 pelmNetworkSectionInfo->addContent("Logical networks used in the package");
2326 // for now, set up a map so we have a list of unique network names (to make
2327 // sure the same network name is only added once)
2328 map<Utf8Str, bool> mapNetworks;
2329 // we fill this later below when we iterate over the networks
2330
2331 // and here come the virtual systems:
2332
2333 // write a collection if we have more than one virtual system _and_ we're
2334 // writing OVF 1.0; otherwise fail since ovftool can't import more than
2335 // one machine, it seems
2336 xml::ElementNode *pelmToAddVirtualSystemsTo;
2337 if (m->virtualSystemDescriptions.size() > 1)
2338 {
2339 if (pTask->enFormat == TaskExportOVF::OVF_0_9)
2340 throw setError(VBOX_E_FILE_ERROR,
2341 tr("Cannot export more than one virtual system with OVF 0.9, use OVF 1.0"));
2342
2343 pelmToAddVirtualSystemsTo = pelmRoot->createChild("VirtualSystemCollection");
2344 /* xml::AttributeNode *pattrVirtualSystemCollectionId = */ pelmToAddVirtualSystemsTo->setAttribute("ovf:name", "ExportedVirtualBoxMachines"); // whatever
2345 }
2346 else
2347 pelmToAddVirtualSystemsTo = pelmRoot; // add virtual system directly under root element
2348
2349 uint32_t cDisks = 0;
2350
2351 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
2352 /* Iterate through all virtual systems of that appliance */
2353 for (it = m->virtualSystemDescriptions.begin();
2354 it != m->virtualSystemDescriptions.end();
2355 ++it)
2356 {
2357 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
2358
2359 xml::ElementNode *pelmVirtualSystem;
2360 if (pTask->enFormat == TaskExportOVF::OVF_0_9)
2361 {
2362 // <Section xsi:type="ovf:NetworkSection_Type">
2363 pelmVirtualSystem = pelmToAddVirtualSystemsTo->createChild("Content");
2364 pelmVirtualSystem->setAttribute("xsi:type", "ovf:VirtualSystem_Type");
2365 }
2366 else
2367 pelmVirtualSystem = pelmToAddVirtualSystemsTo->createChild("VirtualSystem");
2368
2369 /*xml::ElementNode *pelmVirtualSystemInfo =*/ pelmVirtualSystem->createChild("Info")->addContent("A virtual machine");
2370
2371 std::list<VirtualSystemDescriptionEntry*> llName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
2372 if (llName.size() != 1)
2373 throw setError(VBOX_E_NOT_SUPPORTED,
2374 tr("Missing VM name"));
2375 Utf8Str &strVMName = llName.front()->strVbox;
2376 pelmVirtualSystem->setAttribute("ovf:id", strVMName);
2377
2378 // product info
2379 std::list<VirtualSystemDescriptionEntry*> llProduct = vsdescThis->findByType(VirtualSystemDescriptionType_Product);
2380 std::list<VirtualSystemDescriptionEntry*> llProductUrl = vsdescThis->findByType(VirtualSystemDescriptionType_ProductUrl);
2381 std::list<VirtualSystemDescriptionEntry*> llVendor = vsdescThis->findByType(VirtualSystemDescriptionType_Vendor);
2382 std::list<VirtualSystemDescriptionEntry*> llVendorUrl = vsdescThis->findByType(VirtualSystemDescriptionType_VendorUrl);
2383 std::list<VirtualSystemDescriptionEntry*> llVersion = vsdescThis->findByType(VirtualSystemDescriptionType_Version);
2384 bool fProduct = llProduct.size() && !llProduct.front()->strVbox.isEmpty();
2385 bool fProductUrl = llProductUrl.size() && !llProductUrl.front()->strVbox.isEmpty();
2386 bool fVendor = llVendor.size() && !llVendor.front()->strVbox.isEmpty();
2387 bool fVendorUrl = llVendorUrl.size() && !llVendorUrl.front()->strVbox.isEmpty();
2388 bool fVersion = llVersion.size() && !llVersion.front()->strVbox.isEmpty();
2389 if (fProduct ||
2390 fProductUrl ||
2391 fVersion ||
2392 fVendorUrl ||
2393 fVersion)
2394 {
2395 /* <Section ovf:required="false" xsi:type="ovf:ProductSection_Type">
2396 <Info>Meta-information about the installed software</Info>
2397 <Product>VAtest</Product>
2398 <Vendor>SUN Microsystems</Vendor>
2399 <Version>10.0</Version>
2400 <ProductUrl>http://blogs.sun.com/VirtualGuru</ProductUrl>
2401 <VendorUrl>http://www.sun.com</VendorUrl>
2402 </Section> */
2403 xml::ElementNode *pelmAnnotationSection;
2404 if (pTask->enFormat == TaskExportOVF::OVF_0_9)
2405 {
2406 // <Section ovf:required="false" xsi:type="ovf:ProductSection_Type">
2407 pelmAnnotationSection = pelmVirtualSystem->createChild("Section");
2408 pelmAnnotationSection->setAttribute("xsi:type", "ovf:ProductSection_Type");
2409 }
2410 else
2411 pelmAnnotationSection = pelmVirtualSystem->createChild("ProductSection");
2412
2413 pelmAnnotationSection->createChild("Info")->addContent("Meta-information about the installed software");
2414 if (fProduct)
2415 pelmAnnotationSection->createChild("Product")->addContent(llProduct.front()->strVbox);
2416 if (fVendor)
2417 pelmAnnotationSection->createChild("Vendor")->addContent(llVendor.front()->strVbox);
2418 if (fVersion)
2419 pelmAnnotationSection->createChild("Version")->addContent(llVersion.front()->strVbox);
2420 if (fProductUrl)
2421 pelmAnnotationSection->createChild("ProductUrl")->addContent(llProductUrl.front()->strVbox);
2422 if (fVendorUrl)
2423 pelmAnnotationSection->createChild("VendorUrl")->addContent(llVendorUrl.front()->strVbox);
2424 }
2425
2426 // description
2427 std::list<VirtualSystemDescriptionEntry*> llDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description);
2428 if (llDescription.size() &&
2429 !llDescription.front()->strVbox.isEmpty())
2430 {
2431 /* <Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type">
2432 <Info>A human-readable annotation</Info>
2433 <Annotation>Plan 9</Annotation>
2434 </Section> */
2435 xml::ElementNode *pelmAnnotationSection;
2436 if (pTask->enFormat == TaskExportOVF::OVF_0_9)
2437 {
2438 // <Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type">
2439 pelmAnnotationSection = pelmVirtualSystem->createChild("Section");
2440 pelmAnnotationSection->setAttribute("xsi:type", "ovf:AnnotationSection_Type");
2441 }
2442 else
2443 pelmAnnotationSection = pelmVirtualSystem->createChild("AnnotationSection");
2444
2445 pelmAnnotationSection->createChild("Info")->addContent("A human-readable annotation");
2446 pelmAnnotationSection->createChild("Annotation")->addContent(llDescription.front()->strVbox);
2447 }
2448
2449 // license
2450 std::list<VirtualSystemDescriptionEntry*> llLicense = vsdescThis->findByType(VirtualSystemDescriptionType_License);
2451 if (llLicense.size() &&
2452 !llLicense.front()->strVbox.isEmpty())
2453 {
2454 /* <EulaSection>
2455 <Info ovf:msgid="6">License agreement for the Virtual System.</Info>
2456 <License ovf:msgid="1">License terms can go in here.</License>
2457 </EulaSection> */
2458 xml::ElementNode *pelmEulaSection;
2459 if (pTask->enFormat == TaskExportOVF::OVF_0_9)
2460 {
2461 pelmEulaSection = pelmVirtualSystem->createChild("Section");
2462 pelmEulaSection->setAttribute("xsi:type", "ovf:EulaSection_Type");
2463 }
2464 else
2465 pelmEulaSection = pelmVirtualSystem->createChild("EulaSection");
2466
2467 pelmEulaSection->createChild("Info")->addContent("License agreement for the virtual system");
2468 pelmEulaSection->createChild("License")->addContent(llLicense.front()->strVbox);
2469 }
2470
2471 // operating system
2472 std::list<VirtualSystemDescriptionEntry*> llOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
2473 if (llOS.size() != 1)
2474 throw setError(VBOX_E_NOT_SUPPORTED,
2475 tr("Missing OS type"));
2476 /* <OperatingSystemSection ovf:id="82">
2477 <Info>Guest Operating System</Info>
2478 <Description>Linux 2.6.x</Description>
2479 </OperatingSystemSection> */
2480 xml::ElementNode *pelmOperatingSystemSection;
2481 if (pTask->enFormat == TaskExportOVF::OVF_0_9)
2482 {
2483 pelmOperatingSystemSection = pelmVirtualSystem->createChild("Section");
2484 pelmOperatingSystemSection->setAttribute("xsi:type", "ovf:OperatingSystemSection_Type");
2485 }
2486 else
2487 pelmOperatingSystemSection = pelmVirtualSystem->createChild("OperatingSystemSection");
2488
2489 pelmOperatingSystemSection->setAttribute("ovf:id", llOS.front()->strOvf);
2490 pelmOperatingSystemSection->createChild("Info")->addContent("The kind of installed guest operating system");
2491 Utf8Str strOSDesc;
2492 convertCIMOSType2VBoxOSType(strOSDesc, (CIMOSType_T)llOS.front()->strOvf.toInt32(), "");
2493 pelmOperatingSystemSection->createChild("Description")->addContent(strOSDesc);
2494
2495 // <VirtualHardwareSection ovf:id="hw1" ovf:transport="iso">
2496 xml::ElementNode *pelmVirtualHardwareSection;
2497 if (pTask->enFormat == TaskExportOVF::OVF_0_9)
2498 {
2499 // <Section xsi:type="ovf:VirtualHardwareSection_Type">
2500 pelmVirtualHardwareSection = pelmVirtualSystem->createChild("Section");
2501 pelmVirtualHardwareSection->setAttribute("xsi:type", "ovf:VirtualHardwareSection_Type");
2502 }
2503 else
2504 pelmVirtualHardwareSection = pelmVirtualSystem->createChild("VirtualHardwareSection");
2505
2506 pelmVirtualHardwareSection->createChild("Info")->addContent("Virtual hardware requirements for a virtual machine");
2507
2508 /* <System>
2509 <vssd:Description>Description of the virtual hardware section.</vssd:Description>
2510 <vssd:ElementName>vmware</vssd:ElementName>
2511 <vssd:InstanceID>1</vssd:InstanceID>
2512 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
2513 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
2514 </System> */
2515 xml::ElementNode *pelmSystem = pelmVirtualHardwareSection->createChild("System");
2516
2517 pelmSystem->createChild("vssd:ElementName")->addContent("Virtual Hardware Family"); // required OVF 1.0
2518
2519 // <vssd:InstanceId>0</vssd:InstanceId>
2520 if (pTask->enFormat == TaskExportOVF::OVF_0_9)
2521 pelmSystem->createChild("vssd:InstanceId")->addContent("0");
2522 else // capitalization changed...
2523 pelmSystem->createChild("vssd:InstanceID")->addContent("0");
2524
2525 // <vssd:VirtualSystemIdentifier>VAtest</vssd:VirtualSystemIdentifier>
2526 pelmSystem->createChild("vssd:VirtualSystemIdentifier")->addContent(strVMName);
2527 // <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
2528 const char *pcszHardware = "virtualbox-2.2";
2529 if (pTask->enFormat == TaskExportOVF::OVF_0_9)
2530 // pretend to be vmware compatible then
2531 pcszHardware = "vmx-6";
2532 pelmSystem->createChild("vssd:VirtualSystemType")->addContent(pcszHardware);
2533
2534 // loop thru all description entries twice; once to write out all
2535 // devices _except_ disk images, and a second time to assign the
2536 // disk images; this is because disk images need to reference
2537 // IDE controllers, and we can't know their instance IDs without
2538 // assigning them first
2539
2540 uint32_t idIDEController = 0;
2541 int32_t lIDEControllerIndex = 0;
2542 uint32_t idSATAController = 0;
2543 int32_t lSATAControllerIndex = 0;
2544 uint32_t idSCSIController = 0;
2545 int32_t lSCSIControllerIndex = 0;
2546
2547 uint32_t ulInstanceID = 1;
2548
2549 for (size_t uLoop = 1;
2550 uLoop <= 2;
2551 ++uLoop)
2552 {
2553 int32_t lIndexThis = 0;
2554 list<VirtualSystemDescriptionEntry>::const_iterator itD;
2555 for (itD = vsdescThis->m->llDescriptions.begin();
2556 itD != vsdescThis->m->llDescriptions.end();
2557 ++itD, ++lIndexThis)
2558 {
2559 const VirtualSystemDescriptionEntry &desc = *itD;
2560
2561 OVFResourceType_T type = (OVFResourceType_T)0; // if this becomes != 0 then we do stuff
2562 Utf8Str strResourceSubType;
2563
2564 Utf8Str strDescription; // results in <rasd:Description>...</rasd:Description> block
2565 Utf8Str strCaption; // results in <rasd:Caption>...</rasd:Caption> block
2566
2567 uint32_t ulParent = 0;
2568
2569 int32_t lVirtualQuantity = -1;
2570 Utf8Str strAllocationUnits;
2571
2572 int32_t lAddress = -1;
2573 int32_t lBusNumber = -1;
2574 int32_t lAddressOnParent = -1;
2575
2576 int32_t lAutomaticAllocation = -1; // 0 means "false", 1 means "true"
2577 Utf8Str strConnection; // results in <rasd:Connection>...</rasd:Connection> block
2578 Utf8Str strHostResource;
2579
2580 uint64_t uTemp;
2581
2582 switch (desc.type)
2583 {
2584 case VirtualSystemDescriptionType_CPU:
2585 /* <Item>
2586 <rasd:Caption>1 virtual CPU</rasd:Caption>
2587 <rasd:Description>Number of virtual CPUs</rasd:Description>
2588 <rasd:ElementName>virtual CPU</rasd:ElementName>
2589 <rasd:InstanceID>1</rasd:InstanceID>
2590 <rasd:ResourceType>3</rasd:ResourceType>
2591 <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
2592 </Item> */
2593 if (uLoop == 1)
2594 {
2595 strDescription = "Number of virtual CPUs";
2596 type = OVFResourceType_Processor; // 3
2597 desc.strVbox.toInt(uTemp);
2598 lVirtualQuantity = (int32_t)uTemp;
2599 strCaption = Utf8StrFmt("%d virtual CPU", lVirtualQuantity); // without this ovftool won't eat the item
2600 }
2601 break;
2602
2603 case VirtualSystemDescriptionType_Memory:
2604 /* <Item>
2605 <rasd:AllocationUnits>MegaBytes</rasd:AllocationUnits>
2606 <rasd:Caption>256 MB of memory</rasd:Caption>
2607 <rasd:Description>Memory Size</rasd:Description>
2608 <rasd:ElementName>Memory</rasd:ElementName>
2609 <rasd:InstanceID>2</rasd:InstanceID>
2610 <rasd:ResourceType>4</rasd:ResourceType>
2611 <rasd:VirtualQuantity>256</rasd:VirtualQuantity>
2612 </Item> */
2613 if (uLoop == 1)
2614 {
2615 strDescription = "Memory Size";
2616 type = OVFResourceType_Memory; // 4
2617 desc.strVbox.toInt(uTemp);
2618 lVirtualQuantity = (int32_t)(uTemp / _1M);
2619 strAllocationUnits = "MegaBytes";
2620 strCaption = Utf8StrFmt("%d MB of memory", lVirtualQuantity); // without this ovftool won't eat the item
2621 }
2622 break;
2623
2624 case VirtualSystemDescriptionType_HardDiskControllerIDE:
2625 /* <Item>
2626 <rasd:Caption>ideController1</rasd:Caption>
2627 <rasd:Description>IDE Controller</rasd:Description>
2628 <rasd:InstanceId>5</rasd:InstanceId>
2629 <rasd:ResourceType>5</rasd:ResourceType>
2630 <rasd:Address>1</rasd:Address>
2631 <rasd:BusNumber>1</rasd:BusNumber>
2632 </Item> */
2633 if (uLoop == 1)
2634 {
2635 strDescription = "IDE Controller";
2636 strCaption = "ideController0";
2637 type = OVFResourceType_IDEController; // 5
2638 strResourceSubType = desc.strVbox;
2639 // it seems that OVFTool always writes these two, and since we can only
2640 // have one IDE controller, we'll use this as well
2641 lAddress = 1;
2642 lBusNumber = 1;
2643
2644 // remember this ID
2645 idIDEController = ulInstanceID;
2646 lIDEControllerIndex = lIndexThis;
2647 }
2648 break;
2649
2650 case VirtualSystemDescriptionType_HardDiskControllerSATA:
2651 /* <Item>
2652 <rasd:Caption>sataController0</rasd:Caption>
2653 <rasd:Description>SATA Controller</rasd:Description>
2654 <rasd:InstanceId>4</rasd:InstanceId>
2655 <rasd:ResourceType>20</rasd:ResourceType>
2656 <rasd:ResourceSubType>ahci</rasd:ResourceSubType>
2657 <rasd:Address>0</rasd:Address>
2658 <rasd:BusNumber>0</rasd:BusNumber>
2659 </Item>
2660 */
2661 if (uLoop == 1)
2662 {
2663 strDescription = "SATA Controller";
2664 strCaption = "sataController0";
2665 type = OVFResourceType_OtherStorageDevice; // 20
2666 // it seems that OVFTool always writes these two, and since we can only
2667 // have one SATA controller, we'll use this as well
2668 lAddress = 0;
2669 lBusNumber = 0;
2670
2671 if ( desc.strVbox.isEmpty() // AHCI is the default in VirtualBox
2672 || (!desc.strVbox.compare("ahci", Utf8Str::CaseInsensitive))
2673 )
2674 strResourceSubType = "AHCI";
2675 else
2676 throw setError(VBOX_E_NOT_SUPPORTED,
2677 tr("Invalid config string \"%s\" in SATA controller"), desc.strVbox.c_str());
2678
2679 // remember this ID
2680 idSATAController = ulInstanceID;
2681 lSATAControllerIndex = lIndexThis;
2682 }
2683 break;
2684
2685 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
2686 /* <Item>
2687 <rasd:Caption>scsiController0</rasd:Caption>
2688 <rasd:Description>SCSI Controller</rasd:Description>
2689 <rasd:InstanceId>4</rasd:InstanceId>
2690 <rasd:ResourceType>6</rasd:ResourceType>
2691 <rasd:ResourceSubType>buslogic</rasd:ResourceSubType>
2692 <rasd:Address>0</rasd:Address>
2693 <rasd:BusNumber>0</rasd:BusNumber>
2694 </Item>
2695 */
2696 if (uLoop == 1)
2697 {
2698 strDescription = "SCSI Controller";
2699 strCaption = "scsiController0";
2700 type = OVFResourceType_ParallelSCSIHBA; // 6
2701 // it seems that OVFTool always writes these two, and since we can only
2702 // have one SATA controller, we'll use this as well
2703 lAddress = 0;
2704 lBusNumber = 0;
2705
2706 if ( desc.strVbox.isEmpty() // LsiLogic is the default in VirtualBox
2707 || (!desc.strVbox.compare("lsilogic", Utf8Str::CaseInsensitive))
2708 )
2709 strResourceSubType = "lsilogic";
2710 else if (!desc.strVbox.compare("buslogic", Utf8Str::CaseInsensitive))
2711 strResourceSubType = "buslogic";
2712 else
2713 throw setError(VBOX_E_NOT_SUPPORTED,
2714 tr("Invalid config string \"%s\" in SCSI controller"), desc.strVbox.c_str());
2715
2716 // remember this ID
2717 idSCSIController = ulInstanceID;
2718 lSCSIControllerIndex = lIndexThis;
2719 }
2720 break;
2721
2722 case VirtualSystemDescriptionType_HardDiskImage:
2723 /* <Item>
2724 <rasd:Caption>disk1</rasd:Caption>
2725 <rasd:InstanceId>8</rasd:InstanceId>
2726 <rasd:ResourceType>17</rasd:ResourceType>
2727 <rasd:HostResource>/disk/vmdisk1</rasd:HostResource>
2728 <rasd:Parent>4</rasd:Parent>
2729 <rasd:AddressOnParent>0</rasd:AddressOnParent>
2730 </Item> */
2731 if (uLoop == 2)
2732 {
2733 Utf8Str strDiskID = Utf8StrFmt("vmdisk%RI32", ++cDisks);
2734
2735 strDescription = "Disk Image";
2736 strCaption = Utf8StrFmt("disk%RI32", cDisks); // this is not used for anything else
2737 type = OVFResourceType_HardDisk; // 17
2738
2739 // the following references the "<Disks>" XML block
2740 strHostResource = Utf8StrFmt("/disk/%s", strDiskID.c_str());
2741
2742 // controller=<index>;channel=<c>
2743 size_t pos1 = desc.strExtraConfig.find("controller=");
2744 size_t pos2 = desc.strExtraConfig.find("channel=");
2745 if (pos1 != Utf8Str::npos)
2746 {
2747 int32_t lControllerIndex = -1;
2748 RTStrToInt32Ex(desc.strExtraConfig.c_str() + pos1 + 11, NULL, 0, &lControllerIndex);
2749 if (lControllerIndex == lIDEControllerIndex)
2750 ulParent = idIDEController;
2751 else if (lControllerIndex == lSCSIControllerIndex)
2752 ulParent = idSCSIController;
2753 else if (lControllerIndex == lSATAControllerIndex)
2754 ulParent = idSATAController;
2755 }
2756 if (pos2 != Utf8Str::npos)
2757 RTStrToInt32Ex(desc.strExtraConfig.c_str() + pos2 + 8, NULL, 0, &lAddressOnParent);
2758
2759 if ( !ulParent
2760 || lAddressOnParent == -1
2761 )
2762 throw setError(VBOX_E_NOT_SUPPORTED,
2763 tr("Missing or bad extra config string in hard disk image: \"%s\""), desc.strExtraConfig.c_str());
2764
2765 mapDisks[strDiskID] = &desc;
2766 }
2767 break;
2768
2769 case VirtualSystemDescriptionType_Floppy:
2770 if (uLoop == 1)
2771 {
2772 strDescription = "Floppy Drive";
2773 strCaption = "floppy0"; // this is what OVFTool writes
2774 type = OVFResourceType_FloppyDrive; // 14
2775 lAutomaticAllocation = 0;
2776 lAddressOnParent = 0; // this is what OVFTool writes
2777 }
2778 break;
2779
2780 case VirtualSystemDescriptionType_CDROM:
2781 if (uLoop == 2)
2782 {
2783 // we can't have a CD without an IDE controller
2784 if (!idIDEController)
2785 throw setError(VBOX_E_NOT_SUPPORTED,
2786 tr("Can't have CD-ROM without IDE controller"));
2787
2788 strDescription = "CD-ROM Drive";
2789 strCaption = "cdrom1"; // this is what OVFTool writes
2790 type = OVFResourceType_CDDrive; // 15
2791 lAutomaticAllocation = 1;
2792 ulParent = idIDEController;
2793 lAddressOnParent = 0; // this is what OVFTool writes
2794 }
2795 break;
2796
2797 case VirtualSystemDescriptionType_NetworkAdapter:
2798 /* <Item>
2799 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
2800 <rasd:Caption>Ethernet adapter on 'VM Network'</rasd:Caption>
2801 <rasd:Connection>VM Network</rasd:Connection>
2802 <rasd:ElementName>VM network</rasd:ElementName>
2803 <rasd:InstanceID>3</rasd:InstanceID>
2804 <rasd:ResourceType>10</rasd:ResourceType>
2805 </Item> */
2806 if (uLoop == 1)
2807 {
2808 lAutomaticAllocation = 1;
2809 strCaption = Utf8StrFmt("Ethernet adapter on '%s'", desc.strOvf.c_str());
2810 type = OVFResourceType_EthernetAdapter; // 10
2811 /* Set the hardware type to something useful.
2812 * To be compatible with vmware & others we set
2813 * PCNet32 for our PCNet types & E1000 for the
2814 * E1000 cards. */
2815 switch (desc.strVbox.toInt32())
2816 {
2817 case NetworkAdapterType_Am79C970A:
2818 case NetworkAdapterType_Am79C973: strResourceSubType = "PCNet32"; break;
2819#ifdef VBOX_WITH_E1000
2820 case NetworkAdapterType_I82540EM:
2821 case NetworkAdapterType_I82545EM:
2822 case NetworkAdapterType_I82543GC: strResourceSubType = "E1000"; break;
2823#endif /* VBOX_WITH_E1000 */
2824 }
2825 strConnection = desc.strOvf;
2826
2827 mapNetworks[desc.strOvf] = true;
2828 }
2829 break;
2830
2831 case VirtualSystemDescriptionType_USBController:
2832 /* <Item ovf:required="false">
2833 <rasd:Caption>usb</rasd:Caption>
2834 <rasd:Description>USB Controller</rasd:Description>
2835 <rasd:InstanceId>3</rasd:InstanceId>
2836 <rasd:ResourceType>23</rasd:ResourceType>
2837 <rasd:Address>0</rasd:Address>
2838 <rasd:BusNumber>0</rasd:BusNumber>
2839 </Item> */
2840 if (uLoop == 1)
2841 {
2842 strDescription = "USB Controller";
2843 strCaption = "usb";
2844 type = OVFResourceType_USBController; // 23
2845 lAddress = 0; // this is what OVFTool writes
2846 lBusNumber = 0; // this is what OVFTool writes
2847 }
2848 break;
2849
2850 case VirtualSystemDescriptionType_SoundCard:
2851 /* <Item ovf:required="false">
2852 <rasd:Caption>sound</rasd:Caption>
2853 <rasd:Description>Sound Card</rasd:Description>
2854 <rasd:InstanceId>10</rasd:InstanceId>
2855 <rasd:ResourceType>35</rasd:ResourceType>
2856 <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
2857 <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
2858 <rasd:AddressOnParent>3</rasd:AddressOnParent>
2859 </Item> */
2860 if (uLoop == 1)
2861 {
2862 strDescription = "Sound Card";
2863 strCaption = "sound";
2864 type = OVFResourceType_SoundCard; // 35
2865 strResourceSubType = desc.strOvf; // e.g. ensoniq1371
2866 lAutomaticAllocation = 0;
2867 lAddressOnParent = 3; // what gives? this is what OVFTool writes
2868 }
2869 break;
2870 }
2871
2872 if (type)
2873 {
2874 xml::ElementNode *pItem;
2875
2876 pItem = pelmVirtualHardwareSection->createChild("Item");
2877
2878 // NOTE: do not change the order of these items without good reason! While we don't care
2879 // about ordering, VMware's ovftool does and fails if the items are not written in
2880 // exactly this order, as stupid as it seems.
2881
2882 if (!strCaption.isEmpty())
2883 {
2884 pItem->createChild("rasd:Caption")->addContent(strCaption);
2885 if (pTask->enFormat == TaskExportOVF::OVF_1_0)
2886 pItem->createChild("rasd:ElementName")->addContent(strCaption);
2887 }
2888
2889 if (!strDescription.isEmpty())
2890 pItem->createChild("rasd:Description")->addContent(strDescription);
2891
2892 // <rasd:InstanceID>1</rasd:InstanceID>
2893 xml::ElementNode *pelmInstanceID;
2894 if (pTask->enFormat == TaskExportOVF::OVF_0_9)
2895 pelmInstanceID = pItem->createChild("rasd:InstanceId");
2896 else
2897 pelmInstanceID = pItem->createChild("rasd:InstanceID"); // capitalization changed...
2898 pelmInstanceID->addContent(Utf8StrFmt("%d", ulInstanceID++));
2899
2900 // <rasd:ResourceType>3</rasd:ResourceType>
2901 pItem->createChild("rasd:ResourceType")->addContent(Utf8StrFmt("%d", type));
2902 if (!strResourceSubType.isEmpty())
2903 pItem->createChild("rasd:ResourceSubType")->addContent(strResourceSubType);
2904
2905 if (!strHostResource.isEmpty())
2906 pItem->createChild("rasd:HostResource")->addContent(strHostResource);
2907
2908 if (!strAllocationUnits.isEmpty())
2909 pItem->createChild("rasd:AllocationUnits")->addContent(strAllocationUnits);
2910
2911 // <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
2912 if (lVirtualQuantity != -1)
2913 pItem->createChild("rasd:VirtualQuantity")->addContent(Utf8StrFmt("%d", lVirtualQuantity));
2914
2915 if (lAutomaticAllocation != -1)
2916 pItem->createChild("rasd:AutomaticAllocation")->addContent( (lAutomaticAllocation) ? "true" : "false" );
2917
2918 if (!strConnection.isEmpty())
2919 pItem->createChild("rasd:Connection")->addContent(strConnection);
2920
2921 if (lAddress != -1)
2922 pItem->createChild("rasd:Address")->addContent(Utf8StrFmt("%d", lAddress));
2923
2924 if (lBusNumber != -1)
2925 if (pTask->enFormat == TaskExportOVF::OVF_0_9) // BusNumber is invalid OVF 1.0 so only write it in 0.9 mode for OVFTool compatibility
2926 pItem->createChild("rasd:BusNumber")->addContent(Utf8StrFmt("%d", lBusNumber));
2927
2928 if (ulParent)
2929 pItem->createChild("rasd:Parent")->addContent(Utf8StrFmt("%d", ulParent));
2930 if (lAddressOnParent != -1)
2931 pItem->createChild("rasd:AddressOnParent")->addContent(Utf8StrFmt("%d", lAddressOnParent));
2932 }
2933 }
2934 } // for (size_t uLoop = 0; ...
2935 }
2936
2937 // finally, fill in the network section we set up empty above according
2938 // to the networks we found with the hardware items
2939 map<Utf8Str, bool>::const_iterator itN;
2940 for (itN = mapNetworks.begin();
2941 itN != mapNetworks.end();
2942 ++itN)
2943 {
2944 const Utf8Str &strNetwork = itN->first;
2945 xml::ElementNode *pelmNetwork = pelmNetworkSection->createChild("Network");
2946 pelmNetwork->setAttribute("ovf:name", strNetwork.c_str());
2947 pelmNetwork->createChild("Description")->addContent("Logical network used by this appliance.");
2948 }
2949
2950 list<Utf8Str> diskList;
2951 map<Utf8Str, const VirtualSystemDescriptionEntry*>::const_iterator itS;
2952 uint32_t ulFile = 1;
2953 for (itS = mapDisks.begin();
2954 itS != mapDisks.end();
2955 ++itS)
2956 {
2957 const Utf8Str &strDiskID = itS->first;
2958 const VirtualSystemDescriptionEntry *pDiskEntry = itS->second;
2959
2960 // source path: where the VBox image is
2961 const Utf8Str &strSrcFilePath = pDiskEntry->strVbox;
2962 Bstr bstrSrcFilePath(strSrcFilePath);
2963 if (!RTPathExists(strSrcFilePath.c_str()))
2964 /* This isn't allowed */
2965 throw setError(VBOX_E_FILE_ERROR,
2966 tr("Source virtual disk image file '%s' doesn't exist"),
2967 strSrcFilePath.c_str());
2968
2969 // output filename
2970 const Utf8Str &strTargetFileNameOnly = pDiskEntry->strOvf;
2971 // target path needs to be composed from where the output OVF is
2972 Utf8Str strTargetFilePath(pTask->locInfo.strPath);
2973 strTargetFilePath.stripFilename();
2974 strTargetFilePath.append("/");
2975 strTargetFilePath.append(strTargetFileNameOnly);
2976
2977 // clone the disk:
2978 ComPtr<IMedium> pSourceDisk;
2979 ComPtr<IMedium> pTargetDisk;
2980 ComPtr<IProgress> pProgress2;
2981
2982 Log(("Finding source disk \"%ls\"\n", bstrSrcFilePath.raw()));
2983 rc = mVirtualBox->FindHardDisk(bstrSrcFilePath, pSourceDisk.asOutParam());
2984 if (FAILED(rc)) throw rc;
2985
2986 /* We are always exporting to vmdfk stream optimized for now */
2987 Bstr bstrSrcFormat = L"VMDK";
2988
2989 // create a new hard disk interface for the destination disk image
2990 Log(("Creating target disk \"%s\"\n", strTargetFilePath.raw()));
2991 rc = mVirtualBox->CreateHardDisk(bstrSrcFormat, Bstr(strTargetFilePath), pTargetDisk.asOutParam());
2992 if (FAILED(rc)) throw rc;
2993
2994 // the target disk is now registered and needs to be removed again,
2995 // both after successful cloning or if anything goes bad!
2996 try
2997 {
2998 // create a flat copy of the source disk image
2999 rc = pSourceDisk->CloneTo(pTargetDisk, MediumVariant_VmdkStreamOptimized, NULL, pProgress2.asOutParam());
3000 if (FAILED(rc)) throw rc;
3001
3002 // advance to the next operation
3003 if (!pTask->progress.isNull())
3004 pTask->progress->SetNextOperation(BstrFmt(tr("Exporting virtual disk image '%s'"), strSrcFilePath.c_str()),
3005 pDiskEntry->ulSizeMB); // operation's weight, as set up with the IProgress originally);
3006
3007 // now wait for the background disk operation to complete; this throws HRESULTs on error
3008 waitForAsyncProgress(pTask->progress, pProgress2);
3009 }
3010 catch (HRESULT rc3)
3011 {
3012 // upon error after registering, close the disk or
3013 // it'll stick in the registry forever
3014 pTargetDisk->Close();
3015 throw rc3;
3016 }
3017 diskList.push_back(strTargetFilePath);
3018
3019 // we need the following for the XML
3020 uint64_t cbFile = 0; // actual file size
3021 rc = pTargetDisk->COMGETTER(Size)(&cbFile);
3022 if (FAILED(rc)) throw rc;
3023
3024 ULONG64 cbCapacity = 0; // size reported to guest
3025 rc = pTargetDisk->COMGETTER(LogicalSize)(&cbCapacity);
3026 if (FAILED(rc)) throw rc;
3027 // capacity is reported in megabytes, so...
3028 cbCapacity *= _1M;
3029
3030 // upon success, close the disk as well
3031 rc = pTargetDisk->Close();
3032 if (FAILED(rc)) throw rc;
3033
3034 // now handle the XML for the disk:
3035 Utf8StrFmt strFileRef("file%RI32", ulFile++);
3036 // <File ovf:href="WindowsXpProfessional-disk1.vmdk" ovf:id="file1" ovf:size="1710381056"/>
3037 xml::ElementNode *pelmFile = pelmReferences->createChild("File");
3038 pelmFile->setAttribute("ovf:href", strTargetFileNameOnly);
3039 pelmFile->setAttribute("ovf:id", strFileRef);
3040 pelmFile->setAttribute("ovf:size", Utf8StrFmt("%RI64", cbFile).c_str());
3041
3042 // add disk to XML Disks section
3043 // <Disk ovf:capacity="8589934592" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/specifications/vmdk.html#sparse"/>
3044 xml::ElementNode *pelmDisk = pelmDiskSection->createChild("Disk");
3045 pelmDisk->setAttribute("ovf:capacity", Utf8StrFmt("%RI64", cbCapacity).c_str());
3046 pelmDisk->setAttribute("ovf:diskId", strDiskID);
3047 pelmDisk->setAttribute("ovf:fileRef", strFileRef);
3048 pelmDisk->setAttribute("ovf:format", "http://www.vmware.com/specifications/vmdk.html#sparse"); // must be sparse or ovftool chokes
3049 }
3050
3051 // now go write the XML
3052 xml::XmlFileWriter writer(doc);
3053 writer.write(pTask->locInfo.strPath.c_str());
3054
3055 /* Create & write the manifest file */
3056 const char** ppManifestFiles = (const char**)RTMemAlloc(sizeof(char*)*diskList.size() + 1);
3057 ppManifestFiles[0] = pTask->locInfo.strPath.c_str();
3058 list<Utf8Str>::const_iterator it1;
3059 size_t i = 1;
3060 for (it1 = diskList.begin();
3061 it1 != diskList.end();
3062 ++it1, ++i)
3063 ppManifestFiles[i] = (*it1).c_str();
3064 Utf8Str strMfFile = manifestFileName(pTask->locInfo.strPath.c_str());
3065 int vrc = RTManifestWriteFiles(strMfFile.c_str(), ppManifestFiles, diskList.size()+1);
3066 if (RT_FAILURE(vrc))
3067 throw setError(VBOX_E_FILE_ERROR,
3068 tr("Couldn't create manifest file '%s' (%Rrc)"),
3069 RTPathFilename(strMfFile.c_str()), vrc);
3070 RTMemFree(ppManifestFiles);
3071 }
3072 catch(xml::Error &x)
3073 {
3074 rc = setError(VBOX_E_FILE_ERROR,
3075 x.what());
3076 }
3077 catch(HRESULT aRC)
3078 {
3079 rc = aRC;
3080 }
3081
3082 pTask->rc = rc;
3083
3084 if (!pTask->progress.isNull())
3085 pTask->progress->notifyComplete(rc);
3086
3087 LogFlowFunc(("rc=%Rhrc\n", rc));
3088 LogFlowFuncLeave();
3089
3090 return VINF_SUCCESS;
3091}
3092
3093int Appliance::writeS3(TaskExportOVF *pTask)
3094{
3095 LogFlowFuncEnter();
3096 LogFlowFunc(("Appliance %p\n", this));
3097
3098 AutoCaller autoCaller(this);
3099 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3100
3101 HRESULT rc = S_OK;
3102
3103 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
3104
3105 int vrc = VINF_SUCCESS;
3106 RTS3 hS3 = NIL_RTS3;
3107 char szOSTmpDir[RTPATH_MAX];
3108 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));
3109 /* The template for the temporary directory created below */
3110 char *pszTmpDir;
3111 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir);
3112 list< pair<Utf8Str, ULONG> > filesList;
3113
3114 // todo:
3115 // - usable error codes
3116 // - seems snapshot filenames are problematic {uuid}.vdi
3117 try
3118 {
3119 /* Extract the bucket */
3120 Utf8Str tmpPath = pTask->locInfo.strPath;
3121 Utf8Str bucket;
3122 parseBucket(tmpPath, bucket);
3123
3124 /* We need a temporary directory which we can put the OVF file & all
3125 * disk images in */
3126 vrc = RTDirCreateTemp(pszTmpDir);
3127 if (RT_FAILURE(vrc))
3128 throw setError(VBOX_E_FILE_ERROR,
3129 tr("Cannot create temporary directory '%s'"), pszTmpDir);
3130
3131 /* The temporary name of the target OVF file */
3132 Utf8StrFmt strTmpOvf("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str()));
3133
3134 /* Prepare the temporary writing of the OVF */
3135 ComObjPtr<Progress> progress;
3136 /* Create a temporary file based location info for the sub task */
3137 LocationInfo li;
3138 li.strPath = strTmpOvf;
3139 rc = writeImpl(pTask->enFormat, li, progress);
3140 if (FAILED(rc)) throw rc;
3141
3142 /* Unlock the appliance for the writing thread */
3143 appLock.release();
3144 /* Wait until the writing is done, but report the progress back to the
3145 caller */
3146 ComPtr<IProgress> progressInt(progress);
3147 waitForAsyncProgress(pTask->progress, progressInt); /* Any errors will be thrown */
3148
3149 /* Again lock the appliance for the next steps */
3150 appLock.acquire();
3151
3152 vrc = RTPathExists(strTmpOvf.c_str()); /* Paranoid check */
3153 if(RT_FAILURE(vrc))
3154 throw setError(VBOX_E_FILE_ERROR,
3155 tr("Cannot find source file '%s'"), strTmpOvf.c_str());
3156 /* Add the OVF file */
3157 filesList.push_back(pair<Utf8Str, ULONG>(strTmpOvf, m->ulWeightPerOperation)); /* Use 1% of the total for the OVF file upload */
3158 Utf8Str strMfFile = manifestFileName(strTmpOvf);
3159 filesList.push_back(pair<Utf8Str, ULONG>(strMfFile , m->ulWeightPerOperation)); /* Use 1% of the total for the manifest file upload */
3160
3161 /* Now add every disks of every virtual system */
3162 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
3163 for (it = m->virtualSystemDescriptions.begin();
3164 it != m->virtualSystemDescriptions.end();
3165 ++it)
3166 {
3167 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
3168 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
3169 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
3170 for (itH = avsdeHDs.begin();
3171 itH != avsdeHDs.end();
3172 ++itH)
3173 {
3174 const Utf8Str &strTargetFileNameOnly = (*itH)->strOvf;
3175 /* Target path needs to be composed from where the output OVF is */
3176 Utf8Str strTargetFilePath(strTmpOvf);
3177 strTargetFilePath.stripFilename();
3178 strTargetFilePath.append("/");
3179 strTargetFilePath.append(strTargetFileNameOnly);
3180 vrc = RTPathExists(strTargetFilePath.c_str()); /* Paranoid check */
3181 if(RT_FAILURE(vrc))
3182 throw setError(VBOX_E_FILE_ERROR,
3183 tr("Cannot find source file '%s'"), strTargetFilePath.c_str());
3184 filesList.push_back(pair<Utf8Str, ULONG>(strTargetFilePath, (*itH)->ulSizeMB));
3185 }
3186 }
3187 /* Next we have to upload the OVF & all disk images */
3188 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);
3189 if(RT_FAILURE(vrc))
3190 throw setError(VBOX_E_IPRT_ERROR,
3191 tr("Cannot create S3 service handler"));
3192 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask);
3193
3194 /* Upload all files */
3195 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
3196 {
3197 const pair<Utf8Str, ULONG> &s = (*it1);
3198 char *pszFilename = RTPathFilename(s.first.c_str());
3199 /* Advance to the next operation */
3200 if (!pTask->progress.isNull())
3201 pTask->progress->SetNextOperation(BstrFmt(tr("Uploading file '%s'"), pszFilename), s.second);
3202 vrc = RTS3PutKey(hS3, bucket.c_str(), pszFilename, s.first.c_str());
3203 if (RT_FAILURE(vrc))
3204 {
3205 if(vrc == VERR_S3_CANCELED)
3206 break;
3207 else if(vrc == VERR_S3_ACCESS_DENIED)
3208 throw setError(E_ACCESSDENIED,
3209 tr("Cannot upload file '%s' to S3 storage server (Access denied). Make sure that your credentials are right. Also check that your host clock is properly synced"), pszFilename);
3210 else if(vrc == VERR_S3_NOT_FOUND)
3211 throw setError(VBOX_E_FILE_ERROR,
3212 tr("Cannot upload file '%s' to S3 storage server (File not found)"), pszFilename);
3213 else
3214 throw setError(VBOX_E_IPRT_ERROR,
3215 tr("Cannot upload file '%s' to S3 storage server (%Rrc)"), pszFilename, vrc);
3216 }
3217 }
3218 }
3219 catch(HRESULT aRC)
3220 {
3221 rc = aRC;
3222 }
3223 /* Cleanup */
3224 RTS3Destroy(hS3);
3225 /* Delete all files which where temporary created */
3226 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
3227 {
3228 const char *pszFilePath = (*it1).first.c_str();
3229 if (RTPathExists(pszFilePath))
3230 {
3231 vrc = RTFileDelete(pszFilePath);
3232 if(RT_FAILURE(vrc))
3233 rc = setError(VBOX_E_FILE_ERROR,
3234 tr("Cannot delete file '%s' (%Rrc)"), pszFilePath, vrc);
3235 }
3236 }
3237 /* Delete the temporary directory */
3238 if (RTPathExists(pszTmpDir))
3239 {
3240 vrc = RTDirRemove(pszTmpDir);
3241 if(RT_FAILURE(vrc))
3242 rc = setError(VBOX_E_FILE_ERROR,
3243 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);
3244 }
3245 if (pszTmpDir)
3246 RTStrFree(pszTmpDir);
3247
3248 pTask->rc = rc;
3249
3250 if (!pTask->progress.isNull())
3251 pTask->progress->notifyComplete(rc);
3252
3253 LogFlowFunc(("rc=%Rhrc\n", rc));
3254 LogFlowFuncLeave();
3255
3256 return VINF_SUCCESS;
3257}
3258
3259////////////////////////////////////////////////////////////////////////////////
3260//
3261// IAppliance public methods
3262//
3263////////////////////////////////////////////////////////////////////////////////
3264
3265/**
3266 * Public method implementation.
3267 * @param
3268 * @return
3269 */
3270STDMETHODIMP Appliance::COMGETTER(Path)(BSTR *aPath)
3271{
3272 if (!aPath)
3273 return E_POINTER;
3274
3275 AutoCaller autoCaller(this);
3276 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3277
3278 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3279
3280 Bstr bstrPath(m->locInfo.strPath);
3281 bstrPath.cloneTo(aPath);
3282
3283 return S_OK;
3284}
3285
3286/**
3287 * Public method implementation.
3288 * @param
3289 * @return
3290 */
3291STDMETHODIMP Appliance::COMGETTER(Disks)(ComSafeArrayOut(BSTR, aDisks))
3292{
3293 CheckComArgOutSafeArrayPointerValid(aDisks);
3294
3295 AutoCaller autoCaller(this);
3296 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3297
3298 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3299
3300 if (m->pReader) // OVFReader instantiated?
3301 {
3302 size_t c = m->pReader->m_mapDisks.size();
3303 com::SafeArray<BSTR> sfaDisks(c);
3304
3305 DiskImagesMap::const_iterator it;
3306 size_t i = 0;
3307 for (it = m->pReader->m_mapDisks.begin();
3308 it != m->pReader->m_mapDisks.end();
3309 ++it, ++i)
3310 {
3311 // create a string representing this disk
3312 const DiskImage &d = it->second;
3313 char *psz = NULL;
3314 RTStrAPrintf(&psz,
3315 "%s\t"
3316 "%RI64\t"
3317 "%RI64\t"
3318 "%s\t"
3319 "%s\t"
3320 "%RI64\t"
3321 "%RI64\t"
3322 "%s",
3323 d.strDiskId.c_str(),
3324 d.iCapacity,
3325 d.iPopulatedSize,
3326 d.strFormat.c_str(),
3327 d.strHref.c_str(),
3328 d.iSize,
3329 d.iChunkSize,
3330 d.strCompression.c_str());
3331 Utf8Str utf(psz);
3332 Bstr bstr(utf);
3333 // push to safearray
3334 bstr.cloneTo(&sfaDisks[i]);
3335 RTStrFree(psz);
3336 }
3337
3338 sfaDisks.detachTo(ComSafeArrayOutArg(aDisks));
3339 }
3340
3341 return S_OK;
3342}
3343
3344/**
3345 * Public method implementation.
3346 * @param
3347 * @return
3348 */
3349STDMETHODIMP Appliance::COMGETTER(VirtualSystemDescriptions)(ComSafeArrayOut(IVirtualSystemDescription*, aVirtualSystemDescriptions))
3350{
3351 CheckComArgOutSafeArrayPointerValid(aVirtualSystemDescriptions);
3352
3353 AutoCaller autoCaller(this);
3354 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3355
3356 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3357
3358 SafeIfaceArray<IVirtualSystemDescription> sfaVSD(m->virtualSystemDescriptions);
3359 sfaVSD.detachTo(ComSafeArrayOutArg(aVirtualSystemDescriptions));
3360
3361 return S_OK;
3362}
3363
3364/**
3365 * Public method implementation.
3366 * @param path
3367 * @return
3368 */
3369STDMETHODIMP Appliance::Read(IN_BSTR path, IProgress **aProgress)
3370{
3371 if (!path) return E_POINTER;
3372 CheckComArgOutPointerValid(aProgress);
3373
3374 AutoCaller autoCaller(this);
3375 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3376
3377 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3378
3379 if (m->pReader)
3380 {
3381 delete m->pReader;
3382 m->pReader = NULL;
3383 }
3384
3385 // see if we can handle this file; for now we insist it has an ".ovf" extension
3386 Utf8Str strPath (path);
3387 if (!strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
3388 return setError(VBOX_E_FILE_ERROR,
3389 tr("Appliance file must have .ovf extension"));
3390
3391 ComObjPtr<Progress> progress;
3392 HRESULT rc = S_OK;
3393 try
3394 {
3395 /* Parse all necessary info out of the URI */
3396 parseURI(strPath, m->locInfo);
3397 rc = readImpl(m->locInfo, progress);
3398 }
3399 catch (HRESULT aRC)
3400 {
3401 rc = aRC;
3402 }
3403
3404 if (SUCCEEDED(rc))
3405 /* Return progress to the caller */
3406 progress.queryInterfaceTo(aProgress);
3407
3408 return S_OK;
3409}
3410
3411/**
3412 * Public method implementation.
3413 * @return
3414 */
3415STDMETHODIMP Appliance::Interpret()
3416{
3417 // @todo:
3418 // - don't use COM methods but the methods directly (faster, but needs appropriate locking of that objects itself (s. HardDisk))
3419 // - Appropriate handle errors like not supported file formats
3420 AutoCaller autoCaller(this);
3421 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3422
3423 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3424
3425 HRESULT rc = S_OK;
3426
3427 /* Clear any previous virtual system descriptions */
3428 m->virtualSystemDescriptions.clear();
3429
3430 /* We need the default path for storing disk images */
3431 ComPtr<ISystemProperties> systemProps;
3432 rc = mVirtualBox->COMGETTER(SystemProperties)(systemProps.asOutParam());
3433 if (FAILED(rc)) return rc;
3434 Bstr bstrDefaultHardDiskLocation;
3435 rc = systemProps->COMGETTER(DefaultHardDiskFolder)(bstrDefaultHardDiskLocation.asOutParam());
3436 if (FAILED(rc)) return rc;
3437
3438 if (!m->pReader)
3439 return setError(E_FAIL,
3440 tr("Cannot interpret appliance without reading it first (call read() before interpret())"));
3441
3442 /* Try/catch so we can clean up on error */
3443 try
3444 {
3445 list<VirtualSystem>::const_iterator it;
3446 /* Iterate through all virtual systems */
3447 for (it = m->pReader->m_llVirtualSystems.begin();
3448 it != m->pReader->m_llVirtualSystems.end();
3449 ++it)
3450 {
3451 const VirtualSystem &vsysThis = *it;
3452
3453 ComObjPtr<VirtualSystemDescription> pNewDesc;
3454 rc = pNewDesc.createObject();
3455 if (FAILED(rc)) throw rc;
3456 rc = pNewDesc->init();
3457 if (FAILED(rc)) throw rc;
3458
3459 /* Guest OS type */
3460 Utf8Str strOsTypeVBox,
3461 strCIMOSType = Utf8StrFmt("%RI32", (uint32_t)vsysThis.cimos);
3462 convertCIMOSType2VBoxOSType(strOsTypeVBox, vsysThis.cimos, vsysThis.strCimosDesc);
3463 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,
3464 "",
3465 strCIMOSType,
3466 strOsTypeVBox);
3467
3468 /* VM name */
3469 /* If the there isn't any name specified create a default one out of
3470 * the OS type */
3471 Utf8Str nameVBox = vsysThis.strName;
3472 if (nameVBox.isEmpty())
3473 nameVBox = strOsTypeVBox;
3474 searchUniqueVMName(nameVBox);
3475 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,
3476 "",
3477 vsysThis.strName,
3478 nameVBox);
3479
3480 /* VM Product */
3481 if (!vsysThis.strProduct.isEmpty())
3482 pNewDesc->addEntry(VirtualSystemDescriptionType_Product,
3483 "",
3484 vsysThis.strProduct,
3485 vsysThis.strProduct);
3486
3487 /* VM Vendor */
3488 if (!vsysThis.strVendor.isEmpty())
3489 pNewDesc->addEntry(VirtualSystemDescriptionType_Vendor,
3490 "",
3491 vsysThis.strVendor,
3492 vsysThis.strVendor);
3493
3494 /* VM Version */
3495 if (!vsysThis.strVersion.isEmpty())
3496 pNewDesc->addEntry(VirtualSystemDescriptionType_Version,
3497 "",
3498 vsysThis.strVersion,
3499 vsysThis.strVersion);
3500
3501 /* VM ProductUrl */
3502 if (!vsysThis.strProductUrl.isEmpty())
3503 pNewDesc->addEntry(VirtualSystemDescriptionType_ProductUrl,
3504 "",
3505 vsysThis.strProductUrl,
3506 vsysThis.strProductUrl);
3507
3508 /* VM VendorUrl */
3509 if (!vsysThis.strVendorUrl.isEmpty())
3510 pNewDesc->addEntry(VirtualSystemDescriptionType_VendorUrl,
3511 "",
3512 vsysThis.strVendorUrl,
3513 vsysThis.strVendorUrl);
3514
3515 /* VM description */
3516 if (!vsysThis.strDescription.isEmpty())
3517 pNewDesc->addEntry(VirtualSystemDescriptionType_Description,
3518 "",
3519 vsysThis.strDescription,
3520 vsysThis.strDescription);
3521
3522 /* VM license */
3523 if (!vsysThis.strLicenseText.isEmpty())
3524 pNewDesc->addEntry(VirtualSystemDescriptionType_License,
3525 "",
3526 vsysThis.strLicenseText,
3527 vsysThis.strLicenseText);
3528
3529 /* Now that we know the OS type, get our internal defaults based on that. */
3530 ComPtr<IGuestOSType> pGuestOSType;
3531 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), pGuestOSType.asOutParam());
3532 if (FAILED(rc)) throw rc;
3533
3534 /* CPU count */
3535 ULONG cpuCountVBox = vsysThis.cCPUs;
3536 /* Check for the constrains */
3537 if (cpuCountVBox > SchemaDefs::MaxCPUCount)
3538 {
3539 addWarning(tr("The virtual system \"%s\" claims support for %u CPU's, but VirtualBox has support for max %u CPU's only."),
3540 vsysThis.strName.c_str(), cpuCountVBox, SchemaDefs::MaxCPUCount);
3541 cpuCountVBox = SchemaDefs::MaxCPUCount;
3542 }
3543 if (vsysThis.cCPUs == 0)
3544 cpuCountVBox = 1;
3545 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
3546 "",
3547 Utf8StrFmt("%RI32", (uint32_t)vsysThis.cCPUs),
3548 Utf8StrFmt("%RI32", (uint32_t)cpuCountVBox));
3549
3550 /* RAM */
3551 uint64_t ullMemSizeVBox = vsysThis.ullMemorySize / _1M;
3552 /* Check for the constrains */
3553 if (ullMemSizeVBox != 0 &&
3554 (ullMemSizeVBox < MM_RAM_MIN_IN_MB ||
3555 ullMemSizeVBox > MM_RAM_MAX_IN_MB))
3556 {
3557 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."),
3558 vsysThis.strName.c_str(), ullMemSizeVBox, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
3559 ullMemSizeVBox = RT_MIN(RT_MAX(ullMemSizeVBox, MM_RAM_MIN_IN_MB), MM_RAM_MAX_IN_MB);
3560 }
3561 if (vsysThis.ullMemorySize == 0)
3562 {
3563 /* If the RAM of the OVF is zero, use our predefined values */
3564 ULONG memSizeVBox2;
3565 rc = pGuestOSType->COMGETTER(RecommendedRAM)(&memSizeVBox2);
3566 if (FAILED(rc)) throw rc;
3567 /* VBox stores that in MByte */
3568 ullMemSizeVBox = (uint64_t)memSizeVBox2;
3569 }
3570 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
3571 "",
3572 Utf8StrFmt("%RI64", (uint64_t)vsysThis.ullMemorySize),
3573 Utf8StrFmt("%RI64", (uint64_t)ullMemSizeVBox));
3574
3575 /* Audio */
3576 if (!vsysThis.strSoundCardType.isEmpty())
3577 /* Currently we set the AC97 always.
3578 @todo: figure out the hardware which could be possible */
3579 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,
3580 "",
3581 vsysThis.strSoundCardType,
3582 Utf8StrFmt("%RI32", (uint32_t)AudioControllerType_AC97));
3583
3584#ifdef VBOX_WITH_USB
3585 /* USB Controller */
3586 if (vsysThis.fHasUsbController)
3587 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
3588#endif /* VBOX_WITH_USB */
3589
3590 /* Network Controller */
3591 size_t cEthernetAdapters = vsysThis.llEthernetAdapters.size();
3592 if (cEthernetAdapters > 0)
3593 {
3594 /* Check for the constrains */
3595 if (cEthernetAdapters > SchemaDefs::NetworkAdapterCount)
3596 addWarning(tr("The virtual system \"%s\" claims support for %zu network adapters, but VirtualBox has support for max %u network adapter only."),
3597 vsysThis.strName.c_str(), cEthernetAdapters, SchemaDefs::NetworkAdapterCount);
3598
3599 /* Get the default network adapter type for the selected guest OS */
3600 NetworkAdapterType_T defaultAdapterVBox = NetworkAdapterType_Am79C970A;
3601 rc = pGuestOSType->COMGETTER(AdapterType)(&defaultAdapterVBox);
3602 if (FAILED(rc)) throw rc;
3603
3604 EthernetAdaptersList::const_iterator itEA;
3605 /* Iterate through all abstract networks. We support 8 network
3606 * adapters at the maximum, so the first 8 will be added only. */
3607 size_t a = 0;
3608 for (itEA = vsysThis.llEthernetAdapters.begin();
3609 itEA != vsysThis.llEthernetAdapters.end() && a < SchemaDefs::NetworkAdapterCount;
3610 ++itEA, ++a)
3611 {
3612 const EthernetAdapter &ea = *itEA; // logical network to connect to
3613 Utf8Str strNetwork = ea.strNetworkName;
3614 // make sure it's one of these two
3615 if ( (strNetwork.compare("Null", Utf8Str::CaseInsensitive))
3616 && (strNetwork.compare("NAT", Utf8Str::CaseInsensitive))
3617 && (strNetwork.compare("Bridged", Utf8Str::CaseInsensitive))
3618 && (strNetwork.compare("Internal", Utf8Str::CaseInsensitive))
3619 && (strNetwork.compare("HostOnly", Utf8Str::CaseInsensitive))
3620 )
3621 strNetwork = "Bridged"; // VMware assumes this is the default apparently
3622
3623 /* Figure out the hardware type */
3624 NetworkAdapterType_T nwAdapterVBox = defaultAdapterVBox;
3625 if (!ea.strAdapterType.compare("PCNet32", Utf8Str::CaseInsensitive))
3626 {
3627 /* If the default adapter is already one of the two
3628 * PCNet adapters use the default one. If not use the
3629 * Am79C970A as fallback. */
3630 if (!(defaultAdapterVBox == NetworkAdapterType_Am79C970A ||
3631 defaultAdapterVBox == NetworkAdapterType_Am79C973))
3632 nwAdapterVBox = NetworkAdapterType_Am79C970A;
3633 }
3634#ifdef VBOX_WITH_E1000
3635 /* VMWare accidentally write this with VirtualCenter 3.5,
3636 so make sure in this case always to use the VMWare one */
3637 else if (!ea.strAdapterType.compare("E10000", Utf8Str::CaseInsensitive))
3638 nwAdapterVBox = NetworkAdapterType_I82545EM;
3639 else if (!ea.strAdapterType.compare("E1000", Utf8Str::CaseInsensitive))
3640 {
3641 /* Check if this OVF was written by VirtualBox */
3642 if (Utf8Str(vsysThis.strVirtualSystemType).contains("virtualbox", Utf8Str::CaseInsensitive))
3643 {
3644 /* If the default adapter is already one of the three
3645 * E1000 adapters use the default one. If not use the
3646 * I82545EM as fallback. */
3647 if (!(defaultAdapterVBox == NetworkAdapterType_I82540EM ||
3648 defaultAdapterVBox == NetworkAdapterType_I82543GC ||
3649 defaultAdapterVBox == NetworkAdapterType_I82545EM))
3650 nwAdapterVBox = NetworkAdapterType_I82540EM;
3651 }
3652 else
3653 /* Always use this one since it's what VMware uses */
3654 nwAdapterVBox = NetworkAdapterType_I82545EM;
3655 }
3656#endif /* VBOX_WITH_E1000 */
3657
3658 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
3659 "", // ref
3660 ea.strNetworkName, // orig
3661 Utf8StrFmt("%RI32", (uint32_t)nwAdapterVBox), // conf
3662 0,
3663 Utf8StrFmt("type=%s", strNetwork.c_str())); // extra conf
3664 }
3665 }
3666
3667 /* Floppy Drive */
3668 if (vsysThis.fHasFloppyDrive)
3669 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");
3670
3671 /* CD Drive */
3672 if (vsysThis.fHasCdromDrive)
3673 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");
3674
3675 /* Hard disk Controller */
3676 uint16_t cIDEused = 0;
3677 uint16_t cSATAused = 0; NOREF(cSATAused);
3678 uint16_t cSCSIused = 0; NOREF(cSCSIused);
3679 ControllersMap::const_iterator hdcIt;
3680 /* Iterate through all hard disk controllers */
3681 for (hdcIt = vsysThis.mapControllers.begin();
3682 hdcIt != vsysThis.mapControllers.end();
3683 ++hdcIt)
3684 {
3685 const HardDiskController &hdc = hdcIt->second;
3686 Utf8Str strControllerID = Utf8StrFmt("%RI32", (uint32_t)hdc.idController);
3687
3688 switch (hdc.system)
3689 {
3690 case HardDiskController::IDE:
3691 {
3692 /* Check for the constrains */
3693 /* @todo: I'm very confused! Are these bits *one* controller or
3694 is every port/bus declared as an extra controller. */
3695 if (cIDEused < 4)
3696 {
3697 // @todo: figure out the IDE types
3698 /* Use PIIX4 as default */
3699 Utf8Str strType = "PIIX4";
3700 if (!hdc.strControllerType.compare("PIIX3", Utf8Str::CaseInsensitive))
3701 strType = "PIIX3";
3702 else if (!hdc.strControllerType.compare("ICH6", Utf8Str::CaseInsensitive))
3703 strType = "ICH6";
3704 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
3705 strControllerID,
3706 hdc.strControllerType,
3707 strType);
3708 }
3709 else
3710 {
3711 /* Warn only once */
3712 if (cIDEused == 1)
3713 addWarning(tr("The virtual \"%s\" system requests support for more than one IDE controller, but VirtualBox has support for only one."),
3714 vsysThis.strName.c_str());
3715
3716 }
3717 ++cIDEused;
3718 break;
3719 }
3720
3721 case HardDiskController::SATA:
3722 {
3723#ifdef VBOX_WITH_AHCI
3724 /* Check for the constrains */
3725 if (cSATAused < 1)
3726 {
3727 // @todo: figure out the SATA types
3728 /* We only support a plain AHCI controller, so use them always */
3729 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
3730 strControllerID,
3731 hdc.strControllerType,
3732 "AHCI");
3733 }
3734 else
3735 {
3736 /* Warn only once */
3737 if (cSATAused == 1)
3738 addWarning(tr("The virtual system \"%s\" requests support for more than one SATA controller, but VirtualBox has support for only one"),
3739 vsysThis.strName.c_str());
3740
3741 }
3742 ++cSATAused;
3743 break;
3744#else /* !VBOX_WITH_AHCI */
3745 addWarning(tr("The virtual system \"%s\" requests at least one SATA controller but this version of VirtualBox does not provide a SATA controller emulation"),
3746 vsysThis.strName.c_str());
3747#endif /* !VBOX_WITH_AHCI */
3748 }
3749
3750 case HardDiskController::SCSI:
3751 {
3752#ifdef VBOX_WITH_LSILOGIC
3753 /* Check for the constrains */
3754 if (cSCSIused < 1)
3755 {
3756 Utf8Str hdcController = "LsiLogic";
3757 if (!hdc.strControllerType.compare("BusLogic", Utf8Str::CaseInsensitive))
3758 hdcController = "BusLogic";
3759 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,
3760 strControllerID,
3761 hdc.strControllerType,
3762 hdcController);
3763 }
3764 else
3765 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."),
3766 vsysThis.strName.c_str(),
3767 hdc.strControllerType.c_str(),
3768 strControllerID.c_str());
3769 ++cSCSIused;
3770 break;
3771#else /* !VBOX_WITH_LSILOGIC */
3772 addWarning(tr("The virtual system \"%s\" requests at least one SATA controller but this version of VirtualBox does not provide a SCSI controller emulation"),
3773 vsysThis.strName.c_str());
3774#endif /* !VBOX_WITH_LSILOGIC */
3775 }
3776 }
3777 }
3778
3779 /* Hard disks */
3780 if (vsysThis.mapVirtualDisks.size() > 0)
3781 {
3782 VirtualDisksMap::const_iterator itVD;
3783 /* Iterate through all hard disks ()*/
3784 for (itVD = vsysThis.mapVirtualDisks.begin();
3785 itVD != vsysThis.mapVirtualDisks.end();
3786 ++itVD)
3787 {
3788 const VirtualDisk &hd = itVD->second;
3789 /* Get the associated disk image */
3790 const DiskImage &di = m->pReader->m_mapDisks[hd.strDiskId];
3791
3792 // @todo:
3793 // - figure out all possible vmdk formats we also support
3794 // - figure out if there is a url specifier for vhd already
3795 // - we need a url specifier for the vdi format
3796 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)
3797 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive))
3798 {
3799 /* If the href is empty use the VM name as filename */
3800 Utf8Str strFilename = di.strHref;
3801 if (!strFilename.length())
3802 strFilename = Utf8StrFmt("%s.vmdk", nameVBox.c_str());
3803 /* Construct a unique target path */
3804 Utf8StrFmt strPath("%ls%c%s",
3805 bstrDefaultHardDiskLocation.raw(),
3806 RTPATH_DELIMITER,
3807 strFilename.c_str());
3808 searchUniqueDiskImageFilePath(strPath);
3809
3810 /* find the description for the hard disk controller
3811 * that has the same ID as hd.idController */
3812 const VirtualSystemDescriptionEntry *pController;
3813 if (!(pController = pNewDesc->findControllerFromID(hd.idController)))
3814 throw setError(E_FAIL,
3815 tr("Cannot find hard disk controller with OVF instance ID %RI32 to which disk \"%s\" should be attached"),
3816 hd.idController,
3817 di.strHref.c_str());
3818
3819 /* controller to attach to, and the bus within that controller */
3820 Utf8StrFmt strExtraConfig("controller=%RI16;channel=%RI16",
3821 pController->ulIndex,
3822 hd.ulAddressOnParent);
3823 ULONG ulSize = 0;
3824 if (di.iCapacity != -1)
3825 ulSize = (ULONG)(di.iCapacity / _1M);
3826 else if (di.iPopulatedSize != -1)
3827 ulSize = (ULONG)(di.iPopulatedSize / _1M);
3828 else if (di.iSize != -1)
3829 ulSize = (ULONG)(di.iSize / _1M);
3830 if (ulSize == 0)
3831 ulSize = 10000; // assume 10 GB, this is for the progress bar only anyway
3832 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
3833 hd.strDiskId,
3834 di.strHref,
3835 strPath,
3836 ulSize,
3837 strExtraConfig);
3838 }
3839 else
3840 throw setError(VBOX_E_FILE_ERROR,
3841 tr("Unsupported format for virtual disk image in OVF: \"%s\"", di.strFormat.c_str()));
3842 }
3843 }
3844
3845 m->virtualSystemDescriptions.push_back(pNewDesc);
3846 }
3847 }
3848 catch (HRESULT aRC)
3849 {
3850 /* On error we clear the list & return */
3851 m->virtualSystemDescriptions.clear();
3852 rc = aRC;
3853 }
3854
3855 return rc;
3856}
3857
3858/**
3859 * Public method implementation.
3860 * @param aProgress
3861 * @return
3862 */
3863STDMETHODIMP Appliance::ImportMachines(IProgress **aProgress)
3864{
3865 CheckComArgOutPointerValid(aProgress);
3866
3867 AutoCaller autoCaller(this);
3868 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3869
3870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3871
3872 if (!m->pReader)
3873 return setError(E_FAIL,
3874 tr("Cannot import machines without reading it first (call read() before importMachines())"));
3875
3876 ComObjPtr<Progress> progress;
3877 HRESULT rc = S_OK;
3878 try
3879 {
3880 rc = importImpl(m->locInfo, progress);
3881 }
3882 catch (HRESULT aRC)
3883 {
3884 rc = aRC;
3885 }
3886
3887 if (SUCCEEDED(rc))
3888 /* Return progress to the caller */
3889 progress.queryInterfaceTo(aProgress);
3890
3891 return rc;
3892}
3893
3894STDMETHODIMP Appliance::CreateVFSExplorer(IN_BSTR aURI, IVFSExplorer **aExplorer)
3895{
3896 CheckComArgOutPointerValid(aExplorer);
3897
3898 AutoCaller autoCaller(this);
3899 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3900
3901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3902
3903 ComObjPtr<VFSExplorer> explorer;
3904 HRESULT rc = S_OK;
3905 try
3906 {
3907 Utf8Str uri(aURI);
3908 /* Check which kind of export the user has requested */
3909 LocationInfo li;
3910 parseURI(uri, li);
3911 /* Create the explorer object */
3912 explorer.createObject();
3913 rc = explorer->init(li.storageType, li.strPath, li.strHostname, li.strUsername, li.strPassword, mVirtualBox);
3914 }
3915 catch (HRESULT aRC)
3916 {
3917 rc = aRC;
3918 }
3919
3920 if (SUCCEEDED(rc))
3921 /* Return explorer to the caller */
3922 explorer.queryInterfaceTo(aExplorer);
3923
3924 return rc;
3925}
3926
3927STDMETHODIMP Appliance::Write(IN_BSTR format, IN_BSTR path, IProgress **aProgress)
3928{
3929 if (!path) return E_POINTER;
3930 CheckComArgOutPointerValid(aProgress);
3931
3932 AutoCaller autoCaller(this);
3933 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3934
3935 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3936
3937 // see if we can handle this file; for now we insist it has an ".ovf" extension
3938 Utf8Str strPath = path;
3939 if (!strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
3940 return setError(VBOX_E_FILE_ERROR,
3941 tr("Appliance file must have .ovf extension"));
3942
3943 Utf8Str strFormat(format);
3944 TaskExportOVF::OVFFormat ovfF;
3945 if (strFormat == "ovf-0.9")
3946 ovfF = TaskExportOVF::OVF_0_9;
3947 else if (strFormat == "ovf-1.0")
3948 ovfF = TaskExportOVF::OVF_1_0;
3949 else
3950 return setError(VBOX_E_FILE_ERROR,
3951 tr("Invalid format \"%s\" specified"), strFormat.c_str());
3952
3953 ComObjPtr<Progress> progress;
3954 HRESULT rc = S_OK;
3955 try
3956 {
3957 /* Parse all necessary info out of the URI */
3958 parseURI(strPath, m->locInfo);
3959 rc = writeImpl(ovfF, m->locInfo, progress);
3960 }
3961 catch (HRESULT aRC)
3962 {
3963 rc = aRC;
3964 }
3965
3966 if (SUCCEEDED(rc))
3967 /* Return progress to the caller */
3968 progress.queryInterfaceTo(aProgress);
3969
3970 return rc;
3971}
3972
3973/**
3974* Public method implementation.
3975 * @return
3976 */
3977STDMETHODIMP Appliance::GetWarnings(ComSafeArrayOut(BSTR, aWarnings))
3978{
3979 if (ComSafeArrayOutIsNull(aWarnings))
3980 return E_POINTER;
3981
3982 AutoCaller autoCaller(this);
3983 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3984
3985 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3986
3987 com::SafeArray<BSTR> sfaWarnings(m->llWarnings.size());
3988
3989 list<Utf8Str>::const_iterator it;
3990 size_t i = 0;
3991 for (it = m->llWarnings.begin();
3992 it != m->llWarnings.end();
3993 ++it, ++i)
3994 {
3995 Bstr bstr = *it;
3996 bstr.cloneTo(&sfaWarnings[i]);
3997 }
3998
3999 sfaWarnings.detachTo(ComSafeArrayOutArg(aWarnings));
4000
4001 return S_OK;
4002}
4003
4004////////////////////////////////////////////////////////////////////////////////
4005//
4006// IVirtualSystemDescription constructor / destructor
4007//
4008////////////////////////////////////////////////////////////////////////////////
4009
4010DEFINE_EMPTY_CTOR_DTOR(VirtualSystemDescription)
4011
4012/**
4013 * COM initializer.
4014 * @return
4015 */
4016HRESULT VirtualSystemDescription::init()
4017{
4018 /* Enclose the state transition NotReady->InInit->Ready */
4019 AutoInitSpan autoInitSpan(this);
4020 AssertReturn(autoInitSpan.isOk(), E_FAIL);
4021
4022 /* Initialize data */
4023 m = new Data();
4024
4025 /* Confirm a successful initialization */
4026 autoInitSpan.setSucceeded();
4027 return S_OK;
4028}
4029
4030/**
4031* COM uninitializer.
4032*/
4033
4034void VirtualSystemDescription::uninit()
4035{
4036 delete m;
4037 m = NULL;
4038}
4039
4040////////////////////////////////////////////////////////////////////////////////
4041//
4042// IVirtualSystemDescription public methods
4043//
4044////////////////////////////////////////////////////////////////////////////////
4045
4046/**
4047 * Public method implementation.
4048 * @param
4049 * @return
4050 */
4051STDMETHODIMP VirtualSystemDescription::COMGETTER(Count)(ULONG *aCount)
4052{
4053 if (!aCount)
4054 return E_POINTER;
4055
4056 AutoCaller autoCaller(this);
4057 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4058
4059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4060
4061 *aCount = (ULONG)m->llDescriptions.size();
4062
4063 return S_OK;
4064}
4065
4066/**
4067 * Public method implementation.
4068 * @return
4069 */
4070STDMETHODIMP VirtualSystemDescription::GetDescription(ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),
4071 ComSafeArrayOut(BSTR, aRefs),
4072 ComSafeArrayOut(BSTR, aOrigValues),
4073 ComSafeArrayOut(BSTR, aVboxValues),
4074 ComSafeArrayOut(BSTR, aExtraConfigValues))
4075{
4076 if (ComSafeArrayOutIsNull(aTypes) ||
4077 ComSafeArrayOutIsNull(aRefs) ||
4078 ComSafeArrayOutIsNull(aOrigValues) ||
4079 ComSafeArrayOutIsNull(aVboxValues) ||
4080 ComSafeArrayOutIsNull(aExtraConfigValues))
4081 return E_POINTER;
4082
4083 AutoCaller autoCaller(this);
4084 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4085
4086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4087
4088 ULONG c = (ULONG)m->llDescriptions.size();
4089 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);
4090 com::SafeArray<BSTR> sfaRefs(c);
4091 com::SafeArray<BSTR> sfaOrigValues(c);
4092 com::SafeArray<BSTR> sfaVboxValues(c);
4093 com::SafeArray<BSTR> sfaExtraConfigValues(c);
4094
4095 list<VirtualSystemDescriptionEntry>::const_iterator it;
4096 size_t i = 0;
4097 for (it = m->llDescriptions.begin();
4098 it != m->llDescriptions.end();
4099 ++it, ++i)
4100 {
4101 const VirtualSystemDescriptionEntry &vsde = (*it);
4102
4103 sfaTypes[i] = vsde.type;
4104
4105 Bstr bstr = vsde.strRef;
4106 bstr.cloneTo(&sfaRefs[i]);
4107
4108 bstr = vsde.strOvf;
4109 bstr.cloneTo(&sfaOrigValues[i]);
4110
4111 bstr = vsde.strVbox;
4112 bstr.cloneTo(&sfaVboxValues[i]);
4113
4114 bstr = vsde.strExtraConfig;
4115 bstr.cloneTo(&sfaExtraConfigValues[i]);
4116 }
4117
4118 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));
4119 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));
4120 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));
4121 sfaVboxValues.detachTo(ComSafeArrayOutArg(aVboxValues));
4122 sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues));
4123
4124 return S_OK;
4125}
4126
4127/**
4128 * Public method implementation.
4129 * @return
4130 */
4131STDMETHODIMP VirtualSystemDescription::GetDescriptionByType(VirtualSystemDescriptionType_T aType,
4132 ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),
4133 ComSafeArrayOut(BSTR, aRefs),
4134 ComSafeArrayOut(BSTR, aOrigValues),
4135 ComSafeArrayOut(BSTR, aVboxValues),
4136 ComSafeArrayOut(BSTR, aExtraConfigValues))
4137{
4138 if (ComSafeArrayOutIsNull(aTypes) ||
4139 ComSafeArrayOutIsNull(aRefs) ||
4140 ComSafeArrayOutIsNull(aOrigValues) ||
4141 ComSafeArrayOutIsNull(aVboxValues) ||
4142 ComSafeArrayOutIsNull(aExtraConfigValues))
4143 return E_POINTER;
4144
4145 AutoCaller autoCaller(this);
4146 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4147
4148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4149
4150 std::list<VirtualSystemDescriptionEntry*> vsd = findByType (aType);
4151 ULONG c = (ULONG)vsd.size();
4152 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);
4153 com::SafeArray<BSTR> sfaRefs(c);
4154 com::SafeArray<BSTR> sfaOrigValues(c);
4155 com::SafeArray<BSTR> sfaVboxValues(c);
4156 com::SafeArray<BSTR> sfaExtraConfigValues(c);
4157
4158 list<VirtualSystemDescriptionEntry*>::const_iterator it;
4159 size_t i = 0;
4160 for (it = vsd.begin();
4161 it != vsd.end();
4162 ++it, ++i)
4163 {
4164 const VirtualSystemDescriptionEntry *vsde = (*it);
4165
4166 sfaTypes[i] = vsde->type;
4167
4168 Bstr bstr = vsde->strRef;
4169 bstr.cloneTo(&sfaRefs[i]);
4170
4171 bstr = vsde->strOvf;
4172 bstr.cloneTo(&sfaOrigValues[i]);
4173
4174 bstr = vsde->strVbox;
4175 bstr.cloneTo(&sfaVboxValues[i]);
4176
4177 bstr = vsde->strExtraConfig;
4178 bstr.cloneTo(&sfaExtraConfigValues[i]);
4179 }
4180
4181 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));
4182 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));
4183 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));
4184 sfaVboxValues.detachTo(ComSafeArrayOutArg(aVboxValues));
4185 sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues));
4186
4187 return S_OK;
4188}
4189
4190/**
4191 * Public method implementation.
4192 * @return
4193 */
4194STDMETHODIMP VirtualSystemDescription::GetValuesByType(VirtualSystemDescriptionType_T aType,
4195 VirtualSystemDescriptionValueType_T aWhich,
4196 ComSafeArrayOut(BSTR, aValues))
4197{
4198 if (ComSafeArrayOutIsNull(aValues))
4199 return E_POINTER;
4200
4201 AutoCaller autoCaller(this);
4202 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4203
4204 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4205
4206 std::list<VirtualSystemDescriptionEntry*> vsd = findByType (aType);
4207 com::SafeArray<BSTR> sfaValues((ULONG)vsd.size());
4208
4209 list<VirtualSystemDescriptionEntry*>::const_iterator it;
4210 size_t i = 0;
4211 for (it = vsd.begin();
4212 it != vsd.end();
4213 ++it, ++i)
4214 {
4215 const VirtualSystemDescriptionEntry *vsde = (*it);
4216
4217 Bstr bstr;
4218 switch (aWhich)
4219 {
4220 case VirtualSystemDescriptionValueType_Reference: bstr = vsde->strRef; break;
4221 case VirtualSystemDescriptionValueType_Original: bstr = vsde->strOvf; break;
4222 case VirtualSystemDescriptionValueType_Auto: bstr = vsde->strVbox; break;
4223 case VirtualSystemDescriptionValueType_ExtraConfig: bstr = vsde->strExtraConfig; break;
4224 }
4225
4226 bstr.cloneTo(&sfaValues[i]);
4227 }
4228
4229 sfaValues.detachTo(ComSafeArrayOutArg(aValues));
4230
4231 return S_OK;
4232}
4233
4234/**
4235 * Public method implementation.
4236 * @return
4237 */
4238STDMETHODIMP VirtualSystemDescription::SetFinalValues(ComSafeArrayIn(BOOL, aEnabled),
4239 ComSafeArrayIn(IN_BSTR, argVboxValues),
4240 ComSafeArrayIn(IN_BSTR, argExtraConfigValues))
4241{
4242#ifndef RT_OS_WINDOWS
4243 NOREF(aEnabledSize);
4244#endif /* RT_OS_WINDOWS */
4245
4246 CheckComArgSafeArrayNotNull(aEnabled);
4247 CheckComArgSafeArrayNotNull(argVboxValues);
4248 CheckComArgSafeArrayNotNull(argExtraConfigValues);
4249
4250 AutoCaller autoCaller(this);
4251 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4252
4253 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4254
4255 com::SafeArray<BOOL> sfaEnabled(ComSafeArrayInArg(aEnabled));
4256 com::SafeArray<IN_BSTR> sfaVboxValues(ComSafeArrayInArg(argVboxValues));
4257 com::SafeArray<IN_BSTR> sfaExtraConfigValues(ComSafeArrayInArg(argExtraConfigValues));
4258
4259 if ( (sfaEnabled.size() != m->llDescriptions.size())
4260 || (sfaVboxValues.size() != m->llDescriptions.size())
4261 || (sfaExtraConfigValues.size() != m->llDescriptions.size())
4262 )
4263 return E_INVALIDARG;
4264
4265 list<VirtualSystemDescriptionEntry>::iterator it;
4266 size_t i = 0;
4267 for (it = m->llDescriptions.begin();
4268 it != m->llDescriptions.end();
4269 ++it, ++i)
4270 {
4271 VirtualSystemDescriptionEntry& vsde = *it;
4272
4273 if (sfaEnabled[i])
4274 {
4275 vsde.strVbox = sfaVboxValues[i];
4276 vsde.strExtraConfig = sfaExtraConfigValues[i];
4277 }
4278 else
4279 vsde.type = VirtualSystemDescriptionType_Ignore;
4280 }
4281
4282 return S_OK;
4283}
4284
4285/**
4286 * Public method implementation.
4287 * @return
4288 */
4289STDMETHODIMP VirtualSystemDescription::AddDescription(VirtualSystemDescriptionType_T aType,
4290 IN_BSTR aVboxValue,
4291 IN_BSTR aExtraConfigValue)
4292{
4293 AutoCaller autoCaller(this);
4294 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4295
4296 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4297
4298 addEntry(aType, "", aVboxValue, aVboxValue, 0, aExtraConfigValue);
4299
4300 return S_OK;
4301}
4302
4303/**
4304 * Internal method; adds a new description item to the member list.
4305 * @param aType Type of description for the new item.
4306 * @param strRef Reference item; only used with hard disk controllers.
4307 * @param aOrigValue Corresponding original value from OVF.
4308 * @param aAutoValue Initial configuration value (can be overridden by caller with setFinalValues).
4309 * @param ulSizeMB Weight for IProgress
4310 * @param strExtraConfig Extra configuration; meaning dependent on type.
4311 */
4312void VirtualSystemDescription::addEntry(VirtualSystemDescriptionType_T aType,
4313 const Utf8Str &strRef,
4314 const Utf8Str &aOrigValue,
4315 const Utf8Str &aAutoValue,
4316 uint32_t ulSizeMB,
4317 const Utf8Str &strExtraConfig /*= ""*/)
4318{
4319 VirtualSystemDescriptionEntry vsde;
4320 vsde.ulIndex = (uint32_t)m->llDescriptions.size(); // each entry gets an index so the client side can reference them
4321 vsde.type = aType;
4322 vsde.strRef = strRef;
4323 vsde.strOvf = aOrigValue;
4324 vsde.strVbox = aAutoValue;
4325 vsde.strExtraConfig = strExtraConfig;
4326 vsde.ulSizeMB = ulSizeMB;
4327
4328 m->llDescriptions.push_back(vsde);
4329}
4330
4331/**
4332 * Private method; returns a list of description items containing all the items from the member
4333 * description items of this virtual system that match the given type.
4334 * @param aType
4335 * @return
4336 */
4337std::list<VirtualSystemDescriptionEntry*> VirtualSystemDescription::findByType(VirtualSystemDescriptionType_T aType)
4338{
4339 std::list<VirtualSystemDescriptionEntry*> vsd;
4340
4341 list<VirtualSystemDescriptionEntry>::iterator it;
4342 for (it = m->llDescriptions.begin();
4343 it != m->llDescriptions.end();
4344 ++it)
4345 {
4346 if (it->type == aType)
4347 vsd.push_back(&(*it));
4348 }
4349
4350 return vsd;
4351}
4352
4353/**
4354 * Private method; looks thru the member hardware items for the IDE, SATA, or SCSI controller with
4355 * the given reference ID. Useful when needing the controller for a particular
4356 * virtual disk.
4357 * @param id
4358 * @return
4359 */
4360const VirtualSystemDescriptionEntry* VirtualSystemDescription::findControllerFromID(uint32_t id)
4361{
4362 Utf8Str strRef = Utf8StrFmt("%RI32", id);
4363 list<VirtualSystemDescriptionEntry>::const_iterator it;
4364 for (it = m->llDescriptions.begin();
4365 it != m->llDescriptions.end();
4366 ++it)
4367 {
4368 const VirtualSystemDescriptionEntry &d = *it;
4369 switch (d.type)
4370 {
4371 case VirtualSystemDescriptionType_HardDiskControllerIDE:
4372 case VirtualSystemDescriptionType_HardDiskControllerSATA:
4373 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
4374 if (d.strRef == strRef)
4375 return &d;
4376 break;
4377 }
4378 }
4379
4380 return NULL;
4381}
4382
4383////////////////////////////////////////////////////////////////////////////////
4384//
4385// IMachine public methods
4386//
4387////////////////////////////////////////////////////////////////////////////////
4388
4389// This code is here so we won't have to include the appliance headers in the
4390// IMachine implementation, and we also need to access private appliance data.
4391
4392/**
4393* Public method implementation.
4394* @param appliance
4395* @return
4396*/
4397
4398STDMETHODIMP Machine::Export(IAppliance *aAppliance, IVirtualSystemDescription **aDescription)
4399{
4400 HRESULT rc = S_OK;
4401
4402 if (!aAppliance)
4403 return E_POINTER;
4404
4405 AutoCaller autoCaller(this);
4406 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4407
4408 AutoReadLock alock1(this COMMA_LOCKVAL_SRC_POS);
4409
4410 ComObjPtr<VirtualSystemDescription> pNewDesc;
4411
4412 try
4413 {
4414 Bstr bstrName1;
4415 Bstr bstrDescription;
4416 Bstr bstrGuestOSType;
4417 uint32_t cCPUs;
4418 uint32_t ulMemSizeMB;
4419 BOOL fUSBEnabled;
4420 BOOL fAudioEnabled;
4421 AudioControllerType_T audioController;
4422
4423 ComPtr<IUSBController> pUsbController;
4424 ComPtr<IAudioAdapter> pAudioAdapter;
4425
4426 // get name
4427 bstrName1 = mUserData->mName;
4428 // get description
4429 bstrDescription = mUserData->mDescription;
4430 // get guest OS
4431 bstrGuestOSType = mUserData->mOSTypeId;
4432 // CPU count
4433 cCPUs = mHWData->mCPUCount;
4434 // memory size in MB
4435 ulMemSizeMB = mHWData->mMemorySize;
4436 // VRAM size?
4437 // BIOS settings?
4438 // 3D acceleration enabled?
4439 // hardware virtualization enabled?
4440 // nested paging enabled?
4441 // HWVirtExVPIDEnabled?
4442 // PAEEnabled?
4443 // snapshotFolder?
4444 // VRDPServer?
4445
4446 // this is more tricky so use the COM method
4447 rc = COMGETTER(USBController)(pUsbController.asOutParam());
4448 if (FAILED(rc))
4449 fUSBEnabled = false;
4450 else
4451 rc = pUsbController->COMGETTER(Enabled)(&fUSBEnabled);
4452
4453 pAudioAdapter = mAudioAdapter;
4454 rc = pAudioAdapter->COMGETTER(Enabled)(&fAudioEnabled);
4455 if (FAILED(rc)) throw rc;
4456 rc = pAudioAdapter->COMGETTER(AudioController)(&audioController);
4457 if (FAILED(rc)) throw rc;
4458
4459 // create a new virtual system
4460 rc = pNewDesc.createObject();
4461 if (FAILED(rc)) throw rc;
4462 rc = pNewDesc->init();
4463 if (FAILED(rc)) throw rc;
4464
4465 /* Guest OS type */
4466 Utf8Str strOsTypeVBox(bstrGuestOSType);
4467 CIMOSType_T cim = convertVBoxOSType2CIMOSType(strOsTypeVBox.c_str());
4468 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,
4469 "",
4470 Utf8StrFmt("%RI32", cim),
4471 strOsTypeVBox);
4472
4473 /* VM name */
4474 Utf8Str strVMName(bstrName1);
4475 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,
4476 "",
4477 strVMName,
4478 strVMName);
4479
4480 // description
4481 Utf8Str strDescription(bstrDescription);
4482 pNewDesc->addEntry(VirtualSystemDescriptionType_Description,
4483 "",
4484 strDescription,
4485 strDescription);
4486
4487 /* CPU count*/
4488 Utf8Str strCpuCount = Utf8StrFmt("%RI32", cCPUs);
4489 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
4490 "",
4491 strCpuCount,
4492 strCpuCount);
4493
4494 /* Memory */
4495 Utf8Str strMemory = Utf8StrFmt("%RI64", (uint64_t)ulMemSizeMB * _1M);
4496 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
4497 "",
4498 strMemory,
4499 strMemory);
4500
4501 int32_t lIDEControllerIndex = 0;
4502 int32_t lSATAControllerIndex = 0;
4503 int32_t lSCSIControllerIndex = 0;
4504
4505 /* Fetch all available storage controllers */
4506 com::SafeIfaceArray<IStorageController> nwControllers;
4507 rc = COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(nwControllers));
4508 if (FAILED(rc)) throw rc;
4509
4510 ComPtr<IStorageController> pIDEController;
4511#ifdef VBOX_WITH_AHCI
4512 ComPtr<IStorageController> pSATAController;
4513#endif /* VBOX_WITH_AHCI */
4514#ifdef VBOX_WITH_LSILOGIC
4515 ComPtr<IStorageController> pSCSIController;
4516#endif /* VBOX_WITH_LSILOGIC */
4517 for (size_t j = 0; j < nwControllers.size(); ++j)
4518 {
4519 StorageBus_T eType;
4520 rc = nwControllers[j]->COMGETTER(Bus)(&eType);
4521 if (FAILED(rc)) throw rc;
4522 if ( eType == StorageBus_IDE
4523 && pIDEController.isNull())
4524 pIDEController = nwControllers[j];
4525#ifdef VBOX_WITH_AHCI
4526 else if ( eType == StorageBus_SATA
4527 && pSATAController.isNull())
4528 pSATAController = nwControllers[j];
4529#endif /* VBOX_WITH_AHCI */
4530#ifdef VBOX_WITH_LSILOGIC
4531 else if ( eType == StorageBus_SCSI
4532 && pSATAController.isNull())
4533 pSCSIController = nwControllers[j];
4534#endif /* VBOX_WITH_LSILOGIC */
4535 }
4536
4537// <const name="HardDiskControllerIDE" value="6" />
4538 if (!pIDEController.isNull())
4539 {
4540 Utf8Str strVbox;
4541 StorageControllerType_T ctlr;
4542 rc = pIDEController->COMGETTER(ControllerType)(&ctlr);
4543 if (FAILED(rc)) throw rc;
4544 switch(ctlr)
4545 {
4546 case StorageControllerType_PIIX3: strVbox = "PIIX3"; break;
4547 case StorageControllerType_PIIX4: strVbox = "PIIX4"; break;
4548 case StorageControllerType_ICH6: strVbox = "ICH6"; break;
4549 }
4550
4551 if (strVbox.length())
4552 {
4553 lIDEControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
4554 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
4555 Utf8StrFmt("%d", lIDEControllerIndex),
4556 strVbox,
4557 strVbox);
4558 }
4559 }
4560
4561#ifdef VBOX_WITH_AHCI
4562// <const name="HardDiskControllerSATA" value="7" />
4563 if (!pSATAController.isNull())
4564 {
4565 Utf8Str strVbox = "AHCI";
4566 lSATAControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
4567 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
4568 Utf8StrFmt("%d", lSATAControllerIndex),
4569 strVbox,
4570 strVbox);
4571 }
4572#endif // VBOX_WITH_AHCI
4573
4574#ifdef VBOX_WITH_LSILOGIC
4575// <const name="HardDiskControllerSCSI" value="8" />
4576 if (!pSCSIController.isNull())
4577 {
4578 StorageControllerType_T ctlr;
4579 rc = pSCSIController->COMGETTER(ControllerType)(&ctlr);
4580 if (SUCCEEDED(rc))
4581 {
4582 Utf8Str strVbox = "LsiLogic"; // the default in VBox
4583 switch(ctlr)
4584 {
4585 case StorageControllerType_LsiLogic: strVbox = "LsiLogic"; break;
4586 case StorageControllerType_BusLogic: strVbox = "BusLogic"; break;
4587 }
4588 lSCSIControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
4589 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,
4590 Utf8StrFmt("%d", lSCSIControllerIndex),
4591 strVbox,
4592 strVbox);
4593 }
4594 else
4595 throw rc;
4596 }
4597#endif // VBOX_WITH_LSILOGIC
4598
4599// <const name="HardDiskImage" value="9" />
4600// <const name="Floppy" value="18" />
4601// <const name="CDROM" value="19" />
4602
4603 MediaData::AttachmentList::iterator itA;
4604 for (itA = mMediaData->mAttachments.begin();
4605 itA != mMediaData->mAttachments.end();
4606 ++itA)
4607 {
4608 ComObjPtr<MediumAttachment> pHDA = *itA;
4609
4610 // the attachment's data
4611 ComPtr<IMedium> pMedium;
4612 ComPtr<IStorageController> ctl;
4613 Bstr controllerName;
4614
4615 rc = pHDA->COMGETTER(Controller)(controllerName.asOutParam());
4616 if (FAILED(rc)) throw rc;
4617
4618 rc = GetStorageControllerByName(controllerName, ctl.asOutParam());
4619 if (FAILED(rc)) throw rc;
4620
4621 StorageBus_T storageBus;
4622 DeviceType_T deviceType;
4623 LONG lChannel;
4624 LONG lDevice;
4625
4626 rc = ctl->COMGETTER(Bus)(&storageBus);
4627 if (FAILED(rc)) throw rc;
4628
4629 rc = pHDA->COMGETTER(Type)(&deviceType);
4630 if (FAILED(rc)) throw rc;
4631
4632 rc = pHDA->COMGETTER(Medium)(pMedium.asOutParam());
4633 if (FAILED(rc)) throw rc;
4634
4635 rc = pHDA->COMGETTER(Port)(&lChannel);
4636 if (FAILED(rc)) throw rc;
4637
4638 rc = pHDA->COMGETTER(Device)(&lDevice);
4639 if (FAILED(rc)) throw rc;
4640
4641 Utf8Str strTargetVmdkName;
4642 Utf8Str strLocation;
4643 ULONG64 ullSize = 0;
4644
4645 if ( deviceType == DeviceType_HardDisk
4646 && pMedium
4647 )
4648 {
4649 Bstr bstrLocation;
4650 rc = pMedium->COMGETTER(Location)(bstrLocation.asOutParam());
4651 if (FAILED(rc)) throw rc;
4652 strLocation = bstrLocation;
4653
4654 Bstr bstrName;
4655 rc = pMedium->COMGETTER(Name)(bstrName.asOutParam());
4656 if (FAILED(rc)) throw rc;
4657
4658 strTargetVmdkName = bstrName;
4659 strTargetVmdkName.stripExt();
4660 strTargetVmdkName.append(".vmdk");
4661
4662 // we need the size of the image so we can give it to addEntry();
4663 // later, on export, the progress weight will be based on this.
4664 // pMedium can be a differencing image though; in that case, we
4665 // need to use the size of the base instead.
4666 ComPtr<IMedium> pBaseMedium;
4667 rc = pMedium->COMGETTER(Base)(pBaseMedium.asOutParam());
4668 // returns pMedium if there are no diff images
4669 if (FAILED(rc)) throw rc;
4670
4671 // force reading state, or else size will be returned as 0
4672 MediumState_T ms;
4673 rc = pBaseMedium->RefreshState(&ms);
4674 if (FAILED(rc)) throw rc;
4675
4676 rc = pBaseMedium->COMGETTER(Size)(&ullSize);
4677 if (FAILED(rc)) throw rc;
4678 }
4679
4680 // and how this translates to the virtual system
4681 int32_t lControllerVsys = 0;
4682 LONG lChannelVsys;
4683
4684 switch (storageBus)
4685 {
4686 case StorageBus_IDE:
4687 // this is the exact reverse to what we're doing in Appliance::taskThreadImportMachines,
4688 // and it must be updated when that is changed!
4689
4690 if (lChannel == 0 && lDevice == 0) // primary master
4691 lChannelVsys = 0;
4692 else if (lChannel == 0 && lDevice == 1) // primary slave
4693 lChannelVsys = 1;
4694 else if (lChannel == 1 && lDevice == 0) // secondary master; by default this is the CD-ROM but as of VirtualBox 3.1 that can change
4695 lChannelVsys = 2;
4696 else if (lChannel == 1 && lDevice == 1) // secondary slave
4697 lChannelVsys = 3;
4698 else
4699 throw setError(VBOX_E_NOT_SUPPORTED,
4700 tr("Cannot handle medium attachment: channel is %d, device is %d"), lChannel, lDevice);
4701
4702 lControllerVsys = lIDEControllerIndex;
4703 break;
4704
4705 case StorageBus_SATA:
4706 lChannelVsys = lChannel; // should be between 0 and 29
4707 lControllerVsys = lSATAControllerIndex;
4708 break;
4709
4710 case StorageBus_SCSI:
4711 lChannelVsys = lChannel; // should be between 0 and 15
4712 lControllerVsys = lSCSIControllerIndex;
4713 break;
4714
4715 case StorageBus_Floppy:
4716 lChannelVsys = 0;
4717 lControllerVsys = 0;
4718 break;
4719
4720 default:
4721 throw setError(VBOX_E_NOT_SUPPORTED,
4722 tr("Cannot handle medium attachment: storageBus is %d, channel is %d, device is %d"), storageBus, lChannel, lDevice);
4723 break;
4724 }
4725
4726 Utf8StrFmt strExtra("controller=%RI32;channel=%RI32", lControllerVsys, lChannelVsys);
4727 Utf8Str strEmpty;
4728
4729 switch (deviceType)
4730 {
4731 case DeviceType_HardDisk:
4732 Log(("Adding VirtualSystemDescriptionType_HardDiskImage, disk size: %RI64\n", ullSize));
4733 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
4734 strTargetVmdkName, // disk ID: let's use the name
4735 strTargetVmdkName, // OVF value:
4736 strLocation, // vbox value: media path
4737 (uint32_t)(ullSize / _1M),
4738 strExtra);
4739 break;
4740
4741 case DeviceType_DVD:
4742 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM,
4743 strEmpty, // disk ID
4744 strEmpty, // OVF value
4745 strEmpty, // vbox value
4746 1, // ulSize
4747 strExtra);
4748 break;
4749
4750 case DeviceType_Floppy:
4751 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy,
4752 strEmpty, // disk ID
4753 strEmpty, // OVF value
4754 strEmpty, // vbox value
4755 1, // ulSize
4756 strExtra);
4757 break;
4758 }
4759 }
4760
4761// <const name="NetworkAdapter" />
4762 size_t a;
4763 for (a = 0;
4764 a < SchemaDefs::NetworkAdapterCount;
4765 ++a)
4766 {
4767 ComPtr<INetworkAdapter> pNetworkAdapter;
4768 BOOL fEnabled;
4769 NetworkAdapterType_T adapterType;
4770 NetworkAttachmentType_T attachmentType;
4771
4772 rc = GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
4773 if (FAILED(rc)) throw rc;
4774 /* Enable the network card & set the adapter type */
4775 rc = pNetworkAdapter->COMGETTER(Enabled)(&fEnabled);
4776 if (FAILED(rc)) throw rc;
4777
4778 if (fEnabled)
4779 {
4780 Utf8Str strAttachmentType;
4781
4782 rc = pNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
4783 if (FAILED(rc)) throw rc;
4784
4785 rc = pNetworkAdapter->COMGETTER(AttachmentType)(&attachmentType);
4786 if (FAILED(rc)) throw rc;
4787
4788 switch (attachmentType)
4789 {
4790 case NetworkAttachmentType_Null:
4791 strAttachmentType = "Null";
4792 break;
4793
4794 case NetworkAttachmentType_NAT:
4795 strAttachmentType = "NAT";
4796 break;
4797
4798 case NetworkAttachmentType_Bridged:
4799 strAttachmentType = "Bridged";
4800 break;
4801
4802 case NetworkAttachmentType_Internal:
4803 strAttachmentType = "Internal";
4804 break;
4805
4806 case NetworkAttachmentType_HostOnly:
4807 strAttachmentType = "HostOnly";
4808 break;
4809 }
4810
4811 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
4812 "", // ref
4813 strAttachmentType, // orig
4814 Utf8StrFmt("%RI32", (uint32_t)adapterType), // conf
4815 0,
4816 Utf8StrFmt("type=%s", strAttachmentType.c_str())); // extra conf
4817 }
4818 }
4819
4820// <const name="USBController" />
4821#ifdef VBOX_WITH_USB
4822 if (fUSBEnabled)
4823 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
4824#endif /* VBOX_WITH_USB */
4825
4826// <const name="SoundCard" />
4827 if (fAudioEnabled)
4828 {
4829 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,
4830 "",
4831 "ensoniq1371", // this is what OVFTool writes and VMware supports
4832 Utf8StrFmt("%RI32", audioController));
4833 }
4834
4835 // finally, add the virtual system to the appliance
4836 Appliance *pAppliance = static_cast<Appliance*>(aAppliance);
4837 AutoCaller autoCaller1(pAppliance);
4838 if (FAILED(autoCaller1.rc())) return autoCaller1.rc();
4839
4840 /* We return the new description to the caller */
4841 ComPtr<IVirtualSystemDescription> copy(pNewDesc);
4842 copy.queryInterfaceTo(aDescription);
4843
4844 AutoWriteLock alock(pAppliance COMMA_LOCKVAL_SRC_POS);
4845
4846 pAppliance->m->virtualSystemDescriptions.push_back(pNewDesc);
4847 }
4848 catch(HRESULT arc)
4849 {
4850 rc = arc;
4851 }
4852
4853 return rc;
4854}
4855
4856/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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