VirtualBox

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

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

Main: attempt at USB locking fixes

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

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