VirtualBox

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

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

Main/VirtualBox: new parameter for overriding VM file existence check on creation (2nd try)

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette