VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/ApplianceImplExport.cpp@ 54966

Last change on this file since 54966 was 52856, checked in by vboxsync, 10 years ago

Main/ApplianceImplExport: fixed typo, thanks PVS

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 109.6 KB
Line 
1/* $Id: ApplianceImplExport.cpp 52856 2014-09-25 11:13:00Z vboxsync $ */
2/** @file
3 * IAppliance and IVirtualSystem COM class implementations.
4 */
5
6/*
7 * Copyright (C) 2008-2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include <iprt/path.h>
19#include <iprt/dir.h>
20#include <iprt/param.h>
21#include <iprt/s3.h>
22#include <iprt/manifest.h>
23#include <iprt/tar.h>
24#include <iprt/stream.h>
25
26#include <VBox/version.h>
27
28#include "ApplianceImpl.h"
29#include "VirtualBoxImpl.h"
30
31#include "ProgressImpl.h"
32#include "MachineImpl.h"
33#include "MediumImpl.h"
34#include "MediumFormatImpl.h"
35#include "Global.h"
36#include "SystemPropertiesImpl.h"
37
38#include "AutoCaller.h"
39#include "Logging.h"
40
41#include "ApplianceImplPrivate.h"
42
43using namespace std;
44
45////////////////////////////////////////////////////////////////////////////////
46//
47// IMachine public methods
48//
49////////////////////////////////////////////////////////////////////////////////
50
51// This code is here so we won't have to include the appliance headers in the
52// IMachine implementation, and we also need to access private appliance data.
53
54/**
55* Public method implementation.
56* @param appliance
57* @return
58*/
59HRESULT Machine::exportTo(const ComPtr<IAppliance> &aAppliance, const com::Utf8Str &aLocation,
60 ComPtr<IVirtualSystemDescription> &aDescription)
61{
62 HRESULT rc = S_OK;
63
64 if (!aAppliance)
65 return E_POINTER;
66
67 ComObjPtr<VirtualSystemDescription> pNewDesc;
68
69 try
70 {
71 IAppliance *iAppliance = aAppliance;
72 Appliance *pAppliance = static_cast<Appliance*>(iAppliance);
73
74 LocationInfo locInfo;
75 i_parseURI(aLocation, locInfo);
76 // create a new virtual system to store in the appliance
77 rc = pNewDesc.createObject();
78 if (FAILED(rc)) throw rc;
79 rc = pNewDesc->init();
80 if (FAILED(rc)) throw rc;
81
82 // store the machine object so we can dump the XML in Appliance::Write()
83 pNewDesc->m->pMachine = this;
84
85 // first, call the COM methods, as they request locks
86 BOOL fUSBEnabled = FALSE;
87 com::SafeIfaceArray<IUSBController> usbControllers;
88 rc = COMGETTER(USBControllers)(ComSafeArrayAsOutParam(usbControllers));
89 if (SUCCEEDED(rc))
90 {
91 for (unsigned i = 0; i < usbControllers.size(); ++i)
92 {
93 USBControllerType_T enmType;
94
95 rc = usbControllers[i]->COMGETTER(Type)(&enmType);
96 if (FAILED(rc)) throw rc;
97
98 if (enmType == USBControllerType_OHCI)
99 fUSBEnabled = TRUE;
100 }
101 }
102
103 // request the machine lock while accessing internal members
104 AutoReadLock alock1(this COMMA_LOCKVAL_SRC_POS);
105
106 ComPtr<IAudioAdapter> pAudioAdapter = mAudioAdapter;
107 BOOL fAudioEnabled;
108 rc = pAudioAdapter->COMGETTER(Enabled)(&fAudioEnabled);
109 if (FAILED(rc)) throw rc;
110 AudioControllerType_T audioController;
111 rc = pAudioAdapter->COMGETTER(AudioController)(&audioController);
112 if (FAILED(rc)) throw rc;
113
114 // get name
115 Utf8Str strVMName = mUserData->s.strName;
116 // get description
117 Utf8Str strDescription = mUserData->s.strDescription;
118 // get guest OS
119 Utf8Str strOsTypeVBox = mUserData->s.strOsType;
120 // CPU count
121 uint32_t cCPUs = mHWData->mCPUCount;
122 // memory size in MB
123 uint32_t ulMemSizeMB = mHWData->mMemorySize;
124 // VRAM size?
125 // BIOS settings?
126 // 3D acceleration enabled?
127 // hardware virtualization enabled?
128 // nested paging enabled?
129 // HWVirtExVPIDEnabled?
130 // PAEEnabled?
131 // Long mode enabled?
132 BOOL fLongMode;
133 rc = GetCPUProperty(CPUPropertyType_LongMode, &fLongMode);
134 if (FAILED(rc)) throw rc;
135
136 // snapshotFolder?
137 // VRDPServer?
138
139 /* Guest OS type */
140 ovf::CIMOSType_T cim = convertVBoxOSType2CIMOSType(strOsTypeVBox.c_str(), fLongMode);
141 pNewDesc->i_addEntry(VirtualSystemDescriptionType_OS,
142 "",
143 Utf8StrFmt("%RI32", cim),
144 strOsTypeVBox);
145
146 /* VM name */
147 pNewDesc->i_addEntry(VirtualSystemDescriptionType_Name,
148 "",
149 strVMName,
150 strVMName);
151
152 // description
153 pNewDesc->i_addEntry(VirtualSystemDescriptionType_Description,
154 "",
155 strDescription,
156 strDescription);
157
158 /* CPU count*/
159 Utf8Str strCpuCount = Utf8StrFmt("%RI32", cCPUs);
160 pNewDesc->i_addEntry(VirtualSystemDescriptionType_CPU,
161 "",
162 strCpuCount,
163 strCpuCount);
164
165 /* Memory */
166 Utf8Str strMemory = Utf8StrFmt("%RI64", (uint64_t)ulMemSizeMB * _1M);
167 pNewDesc->i_addEntry(VirtualSystemDescriptionType_Memory,
168 "",
169 strMemory,
170 strMemory);
171
172 // the one VirtualBox IDE controller has two channels with two ports each, which is
173 // considered two IDE controllers with two ports each by OVF, so export it as two
174 int32_t lIDEControllerPrimaryIndex = 0;
175 int32_t lIDEControllerSecondaryIndex = 0;
176 int32_t lSATAControllerIndex = 0;
177 int32_t lSCSIControllerIndex = 0;
178
179 /* Fetch all available storage controllers */
180 com::SafeIfaceArray<IStorageController> nwControllers;
181 rc = COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(nwControllers));
182 if (FAILED(rc)) throw rc;
183
184 ComPtr<IStorageController> pIDEController;
185 ComPtr<IStorageController> pSATAController;
186 ComPtr<IStorageController> pSCSIController;
187 ComPtr<IStorageController> pSASController;
188 for (size_t j = 0; j < nwControllers.size(); ++j)
189 {
190 StorageBus_T eType;
191 rc = nwControllers[j]->COMGETTER(Bus)(&eType);
192 if (FAILED(rc)) throw rc;
193 if ( eType == StorageBus_IDE
194 && pIDEController.isNull())
195 pIDEController = nwControllers[j];
196 else if ( eType == StorageBus_SATA
197 && pSATAController.isNull())
198 pSATAController = nwControllers[j];
199 else if ( eType == StorageBus_SCSI
200 && pSATAController.isNull())
201 pSCSIController = nwControllers[j];
202 else if ( eType == StorageBus_SAS
203 && pSASController.isNull())
204 pSASController = nwControllers[j];
205 }
206
207// <const name="HardDiskControllerIDE" value="6" />
208 if (!pIDEController.isNull())
209 {
210 Utf8Str strVBox;
211 StorageControllerType_T ctlr;
212 rc = pIDEController->COMGETTER(ControllerType)(&ctlr);
213 if (FAILED(rc)) throw rc;
214 switch(ctlr)
215 {
216 case StorageControllerType_PIIX3: strVBox = "PIIX3"; break;
217 case StorageControllerType_PIIX4: strVBox = "PIIX4"; break;
218 case StorageControllerType_ICH6: strVBox = "ICH6"; break;
219 }
220
221 if (strVBox.length())
222 {
223 lIDEControllerPrimaryIndex = (int32_t)pNewDesc->m->maDescriptions.size();
224 pNewDesc->i_addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
225 Utf8StrFmt("%d", lIDEControllerPrimaryIndex), // strRef
226 strVBox, // aOvfValue
227 strVBox); // aVBoxValue
228 lIDEControllerSecondaryIndex = lIDEControllerPrimaryIndex + 1;
229 pNewDesc->i_addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
230 Utf8StrFmt("%d", lIDEControllerSecondaryIndex),
231 strVBox,
232 strVBox);
233 }
234 }
235
236// <const name="HardDiskControllerSATA" value="7" />
237 if (!pSATAController.isNull())
238 {
239 Utf8Str strVBox = "AHCI";
240 lSATAControllerIndex = (int32_t)pNewDesc->m->maDescriptions.size();
241 pNewDesc->i_addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
242 Utf8StrFmt("%d", lSATAControllerIndex),
243 strVBox,
244 strVBox);
245 }
246
247// <const name="HardDiskControllerSCSI" value="8" />
248 if (!pSCSIController.isNull())
249 {
250 StorageControllerType_T ctlr;
251 rc = pSCSIController->COMGETTER(ControllerType)(&ctlr);
252 if (SUCCEEDED(rc))
253 {
254 Utf8Str strVBox = "LsiLogic"; // the default in VBox
255 switch(ctlr)
256 {
257 case StorageControllerType_LsiLogic: strVBox = "LsiLogic"; break;
258 case StorageControllerType_BusLogic: strVBox = "BusLogic"; break;
259 }
260 lSCSIControllerIndex = (int32_t)pNewDesc->m->maDescriptions.size();
261 pNewDesc->i_addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,
262 Utf8StrFmt("%d", lSCSIControllerIndex),
263 strVBox,
264 strVBox);
265 }
266 else
267 throw rc;
268 }
269
270 if (!pSASController.isNull())
271 {
272 // VirtualBox considers the SAS controller a class of its own but in OVF
273 // it should be a SCSI controller
274 Utf8Str strVBox = "LsiLogicSas";
275 lSCSIControllerIndex = (int32_t)pNewDesc->m->maDescriptions.size();
276 pNewDesc->i_addEntry(VirtualSystemDescriptionType_HardDiskControllerSAS,
277 Utf8StrFmt("%d", lSCSIControllerIndex),
278 strVBox,
279 strVBox);
280 }
281
282// <const name="HardDiskImage" value="9" />
283// <const name="Floppy" value="18" />
284// <const name="CDROM" value="19" />
285
286 MediaData::AttachmentList::iterator itA;
287 for (itA = mMediaData->mAttachments.begin();
288 itA != mMediaData->mAttachments.end();
289 ++itA)
290 {
291 ComObjPtr<MediumAttachment> pHDA = *itA;
292
293 // the attachment's data
294 ComPtr<IMedium> pMedium;
295 ComPtr<IStorageController> ctl;
296 Bstr controllerName;
297
298 rc = pHDA->COMGETTER(Controller)(controllerName.asOutParam());
299 if (FAILED(rc)) throw rc;
300
301 rc = GetStorageControllerByName(controllerName.raw(), ctl.asOutParam());
302 if (FAILED(rc)) throw rc;
303
304 StorageBus_T storageBus;
305 DeviceType_T deviceType;
306 LONG lChannel;
307 LONG lDevice;
308
309 rc = ctl->COMGETTER(Bus)(&storageBus);
310 if (FAILED(rc)) throw rc;
311
312 rc = pHDA->COMGETTER(Type)(&deviceType);
313 if (FAILED(rc)) throw rc;
314
315 rc = pHDA->COMGETTER(Medium)(pMedium.asOutParam());
316 if (FAILED(rc)) throw rc;
317
318 rc = pHDA->COMGETTER(Port)(&lChannel);
319 if (FAILED(rc)) throw rc;
320
321 rc = pHDA->COMGETTER(Device)(&lDevice);
322 if (FAILED(rc)) throw rc;
323
324 Utf8Str strTargetImageName;
325 Utf8Str strLocation;
326 LONG64 llSize = 0;
327
328 if ( deviceType == DeviceType_HardDisk
329 && pMedium)
330 {
331 Bstr bstrLocation;
332
333 rc = pMedium->COMGETTER(Location)(bstrLocation.asOutParam());
334 if (FAILED(rc)) throw rc;
335 strLocation = bstrLocation;
336
337 // find the source's base medium for two things:
338 // 1) we'll use its name to determine the name of the target disk, which is readable,
339 // as opposed to the UUID filename of a differencing image, if pMedium is one
340 // 2) we need the size of the base image so we can give it to addEntry(), and later
341 // on export, the progress will be based on that (and not the diff image)
342 ComPtr<IMedium> pBaseMedium;
343 rc = pMedium->COMGETTER(Base)(pBaseMedium.asOutParam());
344 // returns pMedium if there are no diff images
345 if (FAILED(rc)) throw rc;
346
347 Utf8Str strName = Utf8Str(locInfo.strPath).stripPath().stripSuffix();
348 strTargetImageName = Utf8StrFmt("%s-disk%d.vmdk", strName.c_str(), ++pAppliance->m->cDisks);
349 if (strTargetImageName.length() > RTTAR_NAME_MAX)
350 throw setError(VBOX_E_NOT_SUPPORTED,
351 tr("Cannot attach disk '%s' -- file name too long"), strTargetImageName.c_str());
352
353 // force reading state, or else size will be returned as 0
354 MediumState_T ms;
355 rc = pBaseMedium->RefreshState(&ms);
356 if (FAILED(rc)) throw rc;
357
358 rc = pBaseMedium->COMGETTER(Size)(&llSize);
359 if (FAILED(rc)) throw rc;
360 }
361 else if ( deviceType == DeviceType_DVD
362 && pMedium)
363 {
364 /*
365 * check the minimal rules to grant access to export an image
366 * 1. no host drive CD/DVD image
367 * 2. the image must be accessible and readable
368 * 3. only ISO image is exported
369 */
370
371 //1. no host drive CD/DVD image
372 BOOL fHostDrive = false;
373 rc = pMedium->COMGETTER(HostDrive)(&fHostDrive);
374 if (FAILED(rc)) throw rc;
375
376 if(fHostDrive)
377 continue;
378
379 //2. the image must be accessible and readable
380 MediumState_T ms;
381 rc = pMedium->RefreshState(&ms);
382 if (FAILED(rc)) throw rc;
383
384 if (ms != MediumState_Created)
385 continue;
386
387 //3. only ISO image is exported
388 Bstr bstrLocation;
389 rc = pMedium->COMGETTER(Location)(bstrLocation.asOutParam());
390 if (FAILED(rc)) throw rc;
391
392 strLocation = bstrLocation;
393
394 Utf8Str ext = strLocation;
395 ext.assignEx(RTPathSuffix(ext.c_str()));//returns extension with dot (".iso")
396
397 int eq = ext.compare(".iso", Utf8Str::CaseInsensitive);
398 if (eq != 0)
399 continue;
400
401 Utf8Str strName = Utf8Str(locInfo.strPath).stripPath().stripSuffix();
402 strTargetImageName = Utf8StrFmt("%s-disk%d.iso", strName.c_str(), ++pAppliance->m->cDisks);
403 if (strTargetImageName.length() > RTTAR_NAME_MAX)
404 throw setError(VBOX_E_NOT_SUPPORTED,
405 tr("Cannot attach image '%s' -- file name too long"), strTargetImageName.c_str());
406
407 rc = pMedium->COMGETTER(Size)(&llSize);
408 if (FAILED(rc)) throw rc;
409 }
410 // and how this translates to the virtual system
411 int32_t lControllerVsys = 0;
412 LONG lChannelVsys;
413
414 switch (storageBus)
415 {
416 case StorageBus_IDE:
417 // this is the exact reverse to what we're doing in Appliance::taskThreadImportMachines,
418 // and it must be updated when that is changed!
419 // Before 3.2 we exported one IDE controller with channel 0-3, but we now maintain
420 // compatibility with what VMware does and export two IDE controllers with two channels each
421
422 if (lChannel == 0 && lDevice == 0) // primary master
423 {
424 lControllerVsys = lIDEControllerPrimaryIndex;
425 lChannelVsys = 0;
426 }
427 else if (lChannel == 0 && lDevice == 1) // primary slave
428 {
429 lControllerVsys = lIDEControllerPrimaryIndex;
430 lChannelVsys = 1;
431 }
432 else if (lChannel == 1 && lDevice == 0) // secondary master; by default this is the CD-ROM but
433 // as of VirtualBox 3.1 that can change
434 {
435 lControllerVsys = lIDEControllerSecondaryIndex;
436 lChannelVsys = 0;
437 }
438 else if (lChannel == 1 && lDevice == 1) // secondary slave
439 {
440 lControllerVsys = lIDEControllerSecondaryIndex;
441 lChannelVsys = 1;
442 }
443 else
444 throw setError(VBOX_E_NOT_SUPPORTED,
445 tr("Cannot handle medium attachment: channel is %d, device is %d"), lChannel, lDevice);
446 break;
447
448 case StorageBus_SATA:
449 lChannelVsys = lChannel; // should be between 0 and 29
450 lControllerVsys = lSATAControllerIndex;
451 break;
452
453 case StorageBus_SCSI:
454 case StorageBus_SAS:
455 lChannelVsys = lChannel; // should be between 0 and 15
456 lControllerVsys = lSCSIControllerIndex;
457 break;
458
459 case StorageBus_Floppy:
460 lChannelVsys = 0;
461 lControllerVsys = 0;
462 break;
463
464 default:
465 throw setError(VBOX_E_NOT_SUPPORTED,
466 tr("Cannot handle medium attachment: storageBus is %d, channel is %d, device is %d"),
467 storageBus, lChannel, lDevice);
468 break;
469 }
470
471 Utf8StrFmt strExtra("controller=%RI32;channel=%RI32", lControllerVsys, lChannelVsys);
472 Utf8Str strEmpty;
473
474 switch (deviceType)
475 {
476 case DeviceType_HardDisk:
477 Log(("Adding VirtualSystemDescriptionType_HardDiskImage, disk size: %RI64\n", llSize));
478 pNewDesc->i_addEntry(VirtualSystemDescriptionType_HardDiskImage,
479 strTargetImageName, // disk ID: let's use the name
480 strTargetImageName, // OVF value:
481 strLocation, // vbox value: media path
482 (uint32_t)(llSize / _1M),
483 strExtra);
484 break;
485
486 case DeviceType_DVD:
487 Log(("Adding VirtualSystemDescriptionType_CDROM, disk size: %RI64\n", llSize));
488 pNewDesc->i_addEntry(VirtualSystemDescriptionType_CDROM,
489 strTargetImageName, // disk ID
490 strTargetImageName, // OVF value
491 strLocation, // vbox value
492 (uint32_t)(llSize / _1M),// ulSize
493 strExtra);
494 break;
495
496 case DeviceType_Floppy:
497 pNewDesc->i_addEntry(VirtualSystemDescriptionType_Floppy,
498 strEmpty, // disk ID
499 strEmpty, // OVF value
500 strEmpty, // vbox value
501 1, // ulSize
502 strExtra);
503 break;
504 }
505 }
506
507// <const name="NetworkAdapter" />
508 uint32_t maxNetworkAdapters = Global::getMaxNetworkAdapters(i_getChipsetType());
509 size_t a;
510 for (a = 0; a < maxNetworkAdapters; ++a)
511 {
512 ComPtr<INetworkAdapter> pNetworkAdapter;
513 BOOL fEnabled;
514 NetworkAdapterType_T adapterType;
515 NetworkAttachmentType_T attachmentType;
516
517 rc = GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
518 if (FAILED(rc)) throw rc;
519 /* Enable the network card & set the adapter type */
520 rc = pNetworkAdapter->COMGETTER(Enabled)(&fEnabled);
521 if (FAILED(rc)) throw rc;
522
523 if (fEnabled)
524 {
525 rc = pNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
526 if (FAILED(rc)) throw rc;
527
528 rc = pNetworkAdapter->COMGETTER(AttachmentType)(&attachmentType);
529 if (FAILED(rc)) throw rc;
530
531 Utf8Str strAttachmentType = convertNetworkAttachmentTypeToString(attachmentType);
532 pNewDesc->i_addEntry(VirtualSystemDescriptionType_NetworkAdapter,
533 "", // ref
534 strAttachmentType, // orig
535 Utf8StrFmt("%RI32", (uint32_t)adapterType), // conf
536 0,
537 Utf8StrFmt("type=%s", strAttachmentType.c_str())); // extra conf
538 }
539 }
540
541// <const name="USBController" />
542#ifdef VBOX_WITH_USB
543 if (fUSBEnabled)
544 pNewDesc->i_addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
545#endif /* VBOX_WITH_USB */
546
547// <const name="SoundCard" />
548 if (fAudioEnabled)
549 pNewDesc->i_addEntry(VirtualSystemDescriptionType_SoundCard,
550 "",
551 "ensoniq1371", // this is what OVFTool writes and VMware supports
552 Utf8StrFmt("%RI32", audioController));
553
554 /* We return the new description to the caller */
555 ComPtr<IVirtualSystemDescription> copy(pNewDesc);
556 copy.queryInterfaceTo(aDescription.asOutParam());
557
558 AutoWriteLock alock(pAppliance COMMA_LOCKVAL_SRC_POS);
559 // finally, add the virtual system to the appliance
560 pAppliance->m->virtualSystemDescriptions.push_back(pNewDesc);
561 }
562 catch(HRESULT arc)
563 {
564 rc = arc;
565 }
566
567 return rc;
568}
569
570////////////////////////////////////////////////////////////////////////////////
571//
572// IAppliance public methods
573//
574////////////////////////////////////////////////////////////////////////////////
575
576/**
577 * Public method implementation.
578 * @param format
579 * @param options
580 * @param path
581 * @param aProgress
582 * @return
583 */
584HRESULT Appliance::write(const com::Utf8Str &aFormat,
585 const std::vector<ExportOptions_T> &aOptions,
586 const com::Utf8Str &aPath,
587 ComPtr<IProgress> &aProgress)
588{
589 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
590
591 m->optListExport.clear();
592 if (aOptions.size())
593 {
594 for (size_t i = 0; i < aOptions.size(); ++i)
595 {
596 m->optListExport.insert(i, aOptions[i]);
597 }
598 }
599
600// AssertReturn(!(m->optListExport.contains(ExportOptions_CreateManifest)
601// && m->optListExport.contains(ExportOptions_ExportDVDImages)), E_INVALIDARG);
602
603 m->fExportISOImages = m->optListExport.contains(ExportOptions_ExportDVDImages);
604
605 if (!m->fExportISOImages)/* remove all ISO images from VirtualSystemDescription */
606 {
607 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
608 for (it = m->virtualSystemDescriptions.begin();
609 it != m->virtualSystemDescriptions.end();
610 ++it)
611 {
612 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
613 std::list<VirtualSystemDescriptionEntry*> skipped = vsdescThis->i_findByType(VirtualSystemDescriptionType_CDROM);
614 std::list<VirtualSystemDescriptionEntry*>:: iterator pItSkipped = skipped.begin();
615 while (pItSkipped != skipped.end())
616 {
617 (*pItSkipped)->skipIt = true;
618 ++pItSkipped;
619 }
620 }
621 }
622
623 // do not allow entering this method if the appliance is busy reading or writing
624 if (!i_isApplianceIdle())
625 return E_ACCESSDENIED;
626
627 // see if we can handle this file; for now we insist it has an ".ovf" extension
628 if (!( aPath.endsWith(".ovf", Utf8Str::CaseInsensitive)
629 || aPath.endsWith(".ova", Utf8Str::CaseInsensitive)))
630 return setError(VBOX_E_FILE_ERROR,
631 tr("Appliance file must have .ovf or .ova extension"));
632
633 m->fManifest = m->optListExport.contains(ExportOptions_CreateManifest);
634
635 ovf::OVFVersion_T ovfF;
636 if (aFormat == "ovf-0.9")
637 {
638 ovfF = ovf::OVFVersion_0_9;
639 }
640 else if (aFormat == "ovf-1.0")
641 {
642 ovfF = ovf::OVFVersion_1_0;
643 }
644 else if (aFormat == "ovf-2.0")
645 {
646 ovfF = ovf::OVFVersion_2_0;
647 }
648 else
649 return setError(VBOX_E_FILE_ERROR,
650 tr("Invalid format \"%s\" specified"), aFormat.c_str());
651
652 /* as of OVF 2.0 we have to use SHA256 */
653 m->fSha256 = ovfF >= ovf::OVFVersion_2_0;
654
655 ComObjPtr<Progress> progress;
656 HRESULT rc = S_OK;
657 try
658 {
659 /* Parse all necessary info out of the URI */
660 i_parseURI(aPath, m->locInfo);
661 rc = i_writeImpl(ovfF, m->locInfo, progress);
662 }
663 catch (HRESULT aRC)
664 {
665 rc = aRC;
666 }
667
668 if (SUCCEEDED(rc))
669 /* Return progress to the caller */
670 progress.queryInterfaceTo(aProgress.asOutParam());
671
672 return rc;
673}
674
675////////////////////////////////////////////////////////////////////////////////
676//
677// Appliance private methods
678//
679////////////////////////////////////////////////////////////////////////////////
680
681/*******************************************************************************
682 * Export stuff
683 ******************************************************************************/
684
685/**
686 * Implementation for writing out the OVF to disk. This starts a new thread which will call
687 * Appliance::taskThreadWriteOVF().
688 *
689 * This is in a separate private method because it is used from two locations:
690 *
691 * 1) from the public Appliance::Write().
692 *
693 * 2) in a second worker thread; in that case, Appliance::Write() called Appliance::i_writeImpl(), which
694 * called Appliance::i_writeFSOVA(), which called Appliance::i_writeImpl(), which then called this again.
695 *
696 * 3) from Appliance::i_writeS3(), which got called from a previous instance of Appliance::taskThreadWriteOVF().
697 *
698 * @param aFormat
699 * @param aLocInfo
700 * @param aProgress
701 * @return
702 */
703HRESULT Appliance::i_writeImpl(ovf::OVFVersion_T aFormat, const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)
704{
705 HRESULT rc = S_OK;
706 try
707 {
708 rc = i_setUpProgress(aProgress,
709 BstrFmt(tr("Export appliance '%s'"), aLocInfo.strPath.c_str()),
710 (aLocInfo.storageType == VFSType_File) ? WriteFile : WriteS3);
711
712 /* Initialize our worker task */
713 std::auto_ptr<TaskOVF> task(new TaskOVF(this, TaskOVF::Write, aLocInfo, aProgress));
714 /* The OVF version to write */
715 task->enFormat = aFormat;
716
717 rc = task->startThread();
718 if (FAILED(rc)) throw rc;
719
720 /* Don't destruct on success */
721 task.release();
722 }
723 catch (HRESULT aRC)
724 {
725 rc = aRC;
726 }
727
728 return rc;
729}
730
731/**
732 * Called from Appliance::i_writeFS() for creating a XML document for this
733 * Appliance.
734 *
735 * @param writeLock The current write lock.
736 * @param doc The xml document to fill.
737 * @param stack Structure for temporary private
738 * data shared with caller.
739 * @param strPath Path to the target OVF.
740 * instance for which to write XML.
741 * @param enFormat OVF format (0.9 or 1.0).
742 */
743void Appliance::i_buildXML(AutoWriteLockBase& writeLock,
744 xml::Document &doc,
745 XMLStack &stack,
746 const Utf8Str &strPath,
747 ovf::OVFVersion_T enFormat)
748{
749 xml::ElementNode *pelmRoot = doc.createRootElement("Envelope");
750
751 pelmRoot->setAttribute("ovf:version", enFormat == ovf::OVFVersion_2_0 ? "2.0"
752 : enFormat == ovf::OVFVersion_1_0 ? "1.0"
753 : "0.9");
754 pelmRoot->setAttribute("xml:lang", "en-US");
755
756 Utf8Str strNamespace;
757
758 if (enFormat == ovf::OVFVersion_0_9)
759 {
760 strNamespace = ovf::OVF09_URI_string;
761 }
762 else if (enFormat == ovf::OVFVersion_1_0)
763 {
764 strNamespace = ovf::OVF10_URI_string;
765 }
766 else
767 {
768 strNamespace = ovf::OVF20_URI_string;
769 }
770
771 pelmRoot->setAttribute("xmlns", strNamespace);
772 pelmRoot->setAttribute("xmlns:ovf", strNamespace);
773
774 // pelmRoot->setAttribute("xmlns:ovfstr", "http://schema.dmtf.org/ovf/strings/1");
775 pelmRoot->setAttribute("xmlns:rasd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData");
776 pelmRoot->setAttribute("xmlns:vssd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData");
777 pelmRoot->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
778 pelmRoot->setAttribute("xmlns:vbox", "http://www.virtualbox.org/ovf/machine");
779 // pelmRoot->setAttribute("xsi:schemaLocation", "http://schemas.dmtf.org/ovf/envelope/1 ../ovf-envelope.xsd");
780
781 if (enFormat == ovf::OVFVersion_2_0)
782 {
783 pelmRoot->setAttribute("xmlns:epasd",
784 "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_EthernetPortAllocationSettingData.xsd");
785 pelmRoot->setAttribute("xmlns:sasd",
786 "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_StorageAllocationSettingData.xsd");
787 }
788
789 // <Envelope>/<References>
790 xml::ElementNode *pelmReferences = pelmRoot->createChild("References"); // 0.9 and 1.0
791
792 /* <Envelope>/<DiskSection>:
793 <DiskSection>
794 <Info>List of the virtual disks used in the package</Info>
795 <Disk ovf:capacity="4294967296" ovf:diskId="lamp" ovf:format="..." ovf:populatedSize="1924967692"/>
796 </DiskSection> */
797 xml::ElementNode *pelmDiskSection;
798 if (enFormat == ovf::OVFVersion_0_9)
799 {
800 // <Section xsi:type="ovf:DiskSection_Type">
801 pelmDiskSection = pelmRoot->createChild("Section");
802 pelmDiskSection->setAttribute("xsi:type", "ovf:DiskSection_Type");
803 }
804 else
805 pelmDiskSection = pelmRoot->createChild("DiskSection");
806
807 xml::ElementNode *pelmDiskSectionInfo = pelmDiskSection->createChild("Info");
808 pelmDiskSectionInfo->addContent("List of the virtual disks used in the package");
809
810 /* <Envelope>/<NetworkSection>:
811 <NetworkSection>
812 <Info>Logical networks used in the package</Info>
813 <Network ovf:name="VM Network">
814 <Description>The network that the LAMP Service will be available on</Description>
815 </Network>
816 </NetworkSection> */
817 xml::ElementNode *pelmNetworkSection;
818 if (enFormat == ovf::OVFVersion_0_9)
819 {
820 // <Section xsi:type="ovf:NetworkSection_Type">
821 pelmNetworkSection = pelmRoot->createChild("Section");
822 pelmNetworkSection->setAttribute("xsi:type", "ovf:NetworkSection_Type");
823 }
824 else
825 pelmNetworkSection = pelmRoot->createChild("NetworkSection");
826
827 xml::ElementNode *pelmNetworkSectionInfo = pelmNetworkSection->createChild("Info");
828 pelmNetworkSectionInfo->addContent("Logical networks used in the package");
829
830 // and here come the virtual systems:
831
832 // write a collection if we have more than one virtual system _and_ we're
833 // writing OVF 1.0; otherwise fail since ovftool can't import more than
834 // one machine, it seems
835 xml::ElementNode *pelmToAddVirtualSystemsTo;
836 if (m->virtualSystemDescriptions.size() > 1)
837 {
838 if (enFormat == ovf::OVFVersion_0_9)
839 throw setError(VBOX_E_FILE_ERROR,
840 tr("Cannot export more than one virtual system with OVF 0.9, use OVF 1.0"));
841
842 pelmToAddVirtualSystemsTo = pelmRoot->createChild("VirtualSystemCollection");
843 pelmToAddVirtualSystemsTo->setAttribute("ovf:name", "ExportedVirtualBoxMachines"); // whatever
844 }
845 else
846 pelmToAddVirtualSystemsTo = pelmRoot; // add virtual system directly under root element
847
848 // this list receives pointers to the XML elements in the machine XML which
849 // might have UUIDs that need fixing after we know the UUIDs of the exported images
850 std::list<xml::ElementNode*> llElementsWithUuidAttributes;
851
852 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
853 /* Iterate through all virtual systems of that appliance */
854 for (it = m->virtualSystemDescriptions.begin();
855 it != m->virtualSystemDescriptions.end();
856 ++it)
857 {
858 ComObjPtr<VirtualSystemDescription> vsdescThis = *it;
859 i_buildXMLForOneVirtualSystem(writeLock,
860 *pelmToAddVirtualSystemsTo,
861 &llElementsWithUuidAttributes,
862 vsdescThis,
863 enFormat,
864 stack); // disks and networks stack
865 }
866
867 // now, fill in the network section we set up empty above according
868 // to the networks we found with the hardware items
869 map<Utf8Str, bool>::const_iterator itN;
870 for (itN = stack.mapNetworks.begin();
871 itN != stack.mapNetworks.end();
872 ++itN)
873 {
874 const Utf8Str &strNetwork = itN->first;
875 xml::ElementNode *pelmNetwork = pelmNetworkSection->createChild("Network");
876 pelmNetwork->setAttribute("ovf:name", strNetwork.c_str());
877 pelmNetwork->createChild("Description")->addContent("Logical network used by this appliance.");
878 }
879
880 // Finally, write out the disk info
881 list<Utf8Str> diskList;
882 map<Utf8Str, const VirtualSystemDescriptionEntry*>::const_iterator itS;
883 uint32_t ulFile = 1;
884 for (itS = stack.mapDisks.begin();
885 itS != stack.mapDisks.end();
886 ++itS)
887 {
888 const Utf8Str &strDiskID = itS->first;
889 const VirtualSystemDescriptionEntry *pDiskEntry = itS->second;
890
891 // source path: where the VBox image is
892 const Utf8Str &strSrcFilePath = pDiskEntry->strVBoxCurrent;
893 Bstr bstrSrcFilePath(strSrcFilePath);
894
895 //skip empty Medium. There are no information to add into section <References> or <DiskSection>
896 if (strSrcFilePath.isEmpty() ||
897 pDiskEntry->skipIt == true)
898 continue;
899
900 // Do NOT check here whether the file exists. FindMedium will figure
901 // that out, and filesystem-based tests are simply wrong in the
902 // general case (think of iSCSI).
903
904 // We need some info from the source disks
905 ComPtr<IMedium> pSourceDisk;
906 //DeviceType_T deviceType = DeviceType_HardDisk;// by default
907
908 Log(("Finding source disk \"%ls\"\n", bstrSrcFilePath.raw()));
909
910 HRESULT rc;
911
912 if (pDiskEntry->type == VirtualSystemDescriptionType_HardDiskImage)
913 {
914 rc = mVirtualBox->OpenMedium(bstrSrcFilePath.raw(),
915 DeviceType_HardDisk,
916 AccessMode_ReadWrite,
917 FALSE /* fForceNewUuid */,
918 pSourceDisk.asOutParam());
919 if (FAILED(rc))
920 throw rc;
921 }
922 else if (pDiskEntry->type == VirtualSystemDescriptionType_CDROM)//may be, this is CD/DVD
923 {
924 rc = mVirtualBox->OpenMedium(bstrSrcFilePath.raw(),
925 DeviceType_DVD,
926 AccessMode_ReadOnly,
927 FALSE,
928 pSourceDisk.asOutParam());
929 if (FAILED(rc))
930 throw rc;
931 }
932
933 Bstr uuidSource;
934 rc = pSourceDisk->COMGETTER(Id)(uuidSource.asOutParam());
935 if (FAILED(rc)) throw rc;
936 Guid guidSource(uuidSource);
937
938 // output filename
939 const Utf8Str &strTargetFileNameOnly = pDiskEntry->strOvf;
940 // target path needs to be composed from where the output OVF is
941 Utf8Str strTargetFilePath(strPath);
942 strTargetFilePath.stripFilename();
943 strTargetFilePath.append("/");
944 strTargetFilePath.append(strTargetFileNameOnly);
945
946 // We are always exporting to VMDK stream optimized for now
947 //Bstr bstrSrcFormat = L"VMDK";//not used
948
949 diskList.push_back(strTargetFilePath);
950
951 LONG64 cbCapacity = 0; // size reported to guest
952 rc = pSourceDisk->COMGETTER(LogicalSize)(&cbCapacity);
953 if (FAILED(rc)) throw rc;
954 // Todo r=poetzsch: wrong it is reported in bytes ...
955 // capacity is reported in megabytes, so...
956 //cbCapacity *= _1M;
957
958 Guid guidTarget; /* Creates a new uniq number for the target disk. */
959 guidTarget.create();
960
961 // now handle the XML for the disk:
962 Utf8StrFmt strFileRef("file%RI32", ulFile++);
963 // <File ovf:href="WindowsXpProfessional-disk1.vmdk" ovf:id="file1" ovf:size="1710381056"/>
964 xml::ElementNode *pelmFile = pelmReferences->createChild("File");
965 pelmFile->setAttribute("ovf:href", strTargetFileNameOnly);
966 pelmFile->setAttribute("ovf:id", strFileRef);
967 // Todo: the actual size is not available at this point of time,
968 // cause the disk will be compressed. The 1.0 standard says this is
969 // optional! 1.1 isn't fully clear if the "gzip" format is used.
970 // Need to be checked. */
971 // pelmFile->setAttribute("ovf:size", Utf8StrFmt("%RI64", cbFile).c_str());
972
973 // add disk to XML Disks section
974 // <Disk ovf:capacity="8589934592" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="..."/>
975 xml::ElementNode *pelmDisk = pelmDiskSection->createChild("Disk");
976 pelmDisk->setAttribute("ovf:capacity", Utf8StrFmt("%RI64", cbCapacity).c_str());
977 pelmDisk->setAttribute("ovf:diskId", strDiskID);
978 pelmDisk->setAttribute("ovf:fileRef", strFileRef);
979
980 if (pDiskEntry->type == VirtualSystemDescriptionType_HardDiskImage)//deviceType == DeviceType_HardDisk
981 {
982 pelmDisk->setAttribute("ovf:format",
983 (enFormat == ovf::OVFVersion_0_9)
984 ? "http://www.vmware.com/specifications/vmdk.html#sparse" // must be sparse or ovftool ch
985 : "http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized"
986 // correct string as communicated to us by VMware (public bug #6612)
987 );
988 }
989 else //pDiskEntry->type == VirtualSystemDescriptionType_CDROM, deviceType == DeviceType_DVD
990 {
991 pelmDisk->setAttribute("ovf:format",
992 "http://www.ecma-international.org/publications/standards/Ecma-119.htm"
993 );
994 }
995
996 // add the UUID of the newly target image to the OVF disk element, but in the
997 // vbox: namespace since it's not part of the standard
998 pelmDisk->setAttribute("vbox:uuid", Utf8StrFmt("%RTuuid", guidTarget.raw()).c_str());
999
1000 // now, we might have other XML elements from vbox:Machine pointing to this image,
1001 // but those would refer to the UUID of the _source_ image (which we created the
1002 // export image from); those UUIDs need to be fixed to the export image
1003 Utf8Str strGuidSourceCurly = guidSource.toStringCurly();
1004 for (std::list<xml::ElementNode*>::iterator eit = llElementsWithUuidAttributes.begin();
1005 eit != llElementsWithUuidAttributes.end();
1006 ++eit)
1007 {
1008 xml::ElementNode *pelmImage = *eit;
1009 Utf8Str strUUID;
1010 pelmImage->getAttributeValue("uuid", strUUID);
1011 if (strUUID == strGuidSourceCurly)
1012 // overwrite existing uuid attribute
1013 pelmImage->setAttribute("uuid", guidTarget.toStringCurly());
1014 }
1015 }
1016}
1017
1018/**
1019 * Called from Appliance::i_buildXML() for each virtual system (machine) that
1020 * needs XML written out.
1021 *
1022 * @param writeLock The current write lock.
1023 * @param elmToAddVirtualSystemsTo XML element to append elements to.
1024 * @param pllElementsWithUuidAttributes out: list of XML elements produced here
1025 * with UUID attributes for quick
1026 * fixing by caller later
1027 * @param vsdescThis The IVirtualSystemDescription
1028 * instance for which to write XML.
1029 * @param enFormat OVF format (0.9 or 1.0).
1030 * @param stack Structure for temporary private
1031 * data shared with caller.
1032 */
1033void Appliance::i_buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock,
1034 xml::ElementNode &elmToAddVirtualSystemsTo,
1035 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes,
1036 ComObjPtr<VirtualSystemDescription> &vsdescThis,
1037 ovf::OVFVersion_T enFormat,
1038 XMLStack &stack)
1039{
1040 LogFlowFunc(("ENTER appliance %p\n", this));
1041
1042 xml::ElementNode *pelmVirtualSystem;
1043 if (enFormat == ovf::OVFVersion_0_9)
1044 {
1045 // <Section xsi:type="ovf:NetworkSection_Type">
1046 pelmVirtualSystem = elmToAddVirtualSystemsTo.createChild("Content");
1047 pelmVirtualSystem->setAttribute("xsi:type", "ovf:VirtualSystem_Type");
1048 }
1049 else
1050 pelmVirtualSystem = elmToAddVirtualSystemsTo.createChild("VirtualSystem");
1051
1052 /*xml::ElementNode *pelmVirtualSystemInfo =*/ pelmVirtualSystem->createChild("Info")->addContent("A virtual machine");
1053
1054 std::list<VirtualSystemDescriptionEntry*> llName = vsdescThis->i_findByType(VirtualSystemDescriptionType_Name);
1055 if (!llName.size())
1056 throw setError(VBOX_E_NOT_SUPPORTED, tr("Missing VM name"));
1057 Utf8Str &strVMName = llName.back()->strVBoxCurrent;
1058 pelmVirtualSystem->setAttribute("ovf:id", strVMName);
1059
1060 // product info
1061 std::list<VirtualSystemDescriptionEntry*> llProduct = vsdescThis->i_findByType(VirtualSystemDescriptionType_Product);
1062 std::list<VirtualSystemDescriptionEntry*> llProductUrl = vsdescThis->i_findByType(VirtualSystemDescriptionType_ProductUrl);
1063 std::list<VirtualSystemDescriptionEntry*> llVendor = vsdescThis->i_findByType(VirtualSystemDescriptionType_Vendor);
1064 std::list<VirtualSystemDescriptionEntry*> llVendorUrl = vsdescThis->i_findByType(VirtualSystemDescriptionType_VendorUrl);
1065 std::list<VirtualSystemDescriptionEntry*> llVersion = vsdescThis->i_findByType(VirtualSystemDescriptionType_Version);
1066 bool fProduct = llProduct.size() && !llProduct.back()->strVBoxCurrent.isEmpty();
1067 bool fProductUrl = llProductUrl.size() && !llProductUrl.back()->strVBoxCurrent.isEmpty();
1068 bool fVendor = llVendor.size() && !llVendor.back()->strVBoxCurrent.isEmpty();
1069 bool fVendorUrl = llVendorUrl.size() && !llVendorUrl.back()->strVBoxCurrent.isEmpty();
1070 bool fVersion = llVersion.size() && !llVersion.back()->strVBoxCurrent.isEmpty();
1071 if (fProduct || fProductUrl || fVendor || fVendorUrl || fVersion)
1072 {
1073 /* <Section ovf:required="false" xsi:type="ovf:ProductSection_Type">
1074 <Info>Meta-information about the installed software</Info>
1075 <Product>VAtest</Product>
1076 <Vendor>SUN Microsystems</Vendor>
1077 <Version>10.0</Version>
1078 <ProductUrl>http://blogs.sun.com/VirtualGuru</ProductUrl>
1079 <VendorUrl>http://www.sun.com</VendorUrl>
1080 </Section> */
1081 xml::ElementNode *pelmAnnotationSection;
1082 if (enFormat == ovf::OVFVersion_0_9)
1083 {
1084 // <Section ovf:required="false" xsi:type="ovf:ProductSection_Type">
1085 pelmAnnotationSection = pelmVirtualSystem->createChild("Section");
1086 pelmAnnotationSection->setAttribute("xsi:type", "ovf:ProductSection_Type");
1087 }
1088 else
1089 pelmAnnotationSection = pelmVirtualSystem->createChild("ProductSection");
1090
1091 pelmAnnotationSection->createChild("Info")->addContent("Meta-information about the installed software");
1092 if (fProduct)
1093 pelmAnnotationSection->createChild("Product")->addContent(llProduct.back()->strVBoxCurrent);
1094 if (fVendor)
1095 pelmAnnotationSection->createChild("Vendor")->addContent(llVendor.back()->strVBoxCurrent);
1096 if (fVersion)
1097 pelmAnnotationSection->createChild("Version")->addContent(llVersion.back()->strVBoxCurrent);
1098 if (fProductUrl)
1099 pelmAnnotationSection->createChild("ProductUrl")->addContent(llProductUrl.back()->strVBoxCurrent);
1100 if (fVendorUrl)
1101 pelmAnnotationSection->createChild("VendorUrl")->addContent(llVendorUrl.back()->strVBoxCurrent);
1102 }
1103
1104 // description
1105 std::list<VirtualSystemDescriptionEntry*> llDescription = vsdescThis->i_findByType(VirtualSystemDescriptionType_Description);
1106 if (llDescription.size() &&
1107 !llDescription.back()->strVBoxCurrent.isEmpty())
1108 {
1109 /* <Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type">
1110 <Info>A human-readable annotation</Info>
1111 <Annotation>Plan 9</Annotation>
1112 </Section> */
1113 xml::ElementNode *pelmAnnotationSection;
1114 if (enFormat == ovf::OVFVersion_0_9)
1115 {
1116 // <Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type">
1117 pelmAnnotationSection = pelmVirtualSystem->createChild("Section");
1118 pelmAnnotationSection->setAttribute("xsi:type", "ovf:AnnotationSection_Type");
1119 }
1120 else
1121 pelmAnnotationSection = pelmVirtualSystem->createChild("AnnotationSection");
1122
1123 pelmAnnotationSection->createChild("Info")->addContent("A human-readable annotation");
1124 pelmAnnotationSection->createChild("Annotation")->addContent(llDescription.back()->strVBoxCurrent);
1125 }
1126
1127 // license
1128 std::list<VirtualSystemDescriptionEntry*> llLicense = vsdescThis->i_findByType(VirtualSystemDescriptionType_License);
1129 if (llLicense.size() &&
1130 !llLicense.back()->strVBoxCurrent.isEmpty())
1131 {
1132 /* <EulaSection>
1133 <Info ovf:msgid="6">License agreement for the Virtual System.</Info>
1134 <License ovf:msgid="1">License terms can go in here.</License>
1135 </EulaSection> */
1136 xml::ElementNode *pelmEulaSection;
1137 if (enFormat == ovf::OVFVersion_0_9)
1138 {
1139 pelmEulaSection = pelmVirtualSystem->createChild("Section");
1140 pelmEulaSection->setAttribute("xsi:type", "ovf:EulaSection_Type");
1141 }
1142 else
1143 pelmEulaSection = pelmVirtualSystem->createChild("EulaSection");
1144
1145 pelmEulaSection->createChild("Info")->addContent("License agreement for the virtual system");
1146 pelmEulaSection->createChild("License")->addContent(llLicense.back()->strVBoxCurrent);
1147 }
1148
1149 // operating system
1150 std::list<VirtualSystemDescriptionEntry*> llOS = vsdescThis->i_findByType(VirtualSystemDescriptionType_OS);
1151 if (!llOS.size())
1152 throw setError(VBOX_E_NOT_SUPPORTED, tr("Missing OS type"));
1153 /* <OperatingSystemSection ovf:id="82">
1154 <Info>Guest Operating System</Info>
1155 <Description>Linux 2.6.x</Description>
1156 </OperatingSystemSection> */
1157 VirtualSystemDescriptionEntry *pvsdeOS = llOS.back();
1158 xml::ElementNode *pelmOperatingSystemSection;
1159 if (enFormat == ovf::OVFVersion_0_9)
1160 {
1161 pelmOperatingSystemSection = pelmVirtualSystem->createChild("Section");
1162 pelmOperatingSystemSection->setAttribute("xsi:type", "ovf:OperatingSystemSection_Type");
1163 }
1164 else
1165 pelmOperatingSystemSection = pelmVirtualSystem->createChild("OperatingSystemSection");
1166
1167 pelmOperatingSystemSection->setAttribute("ovf:id", pvsdeOS->strOvf);
1168 pelmOperatingSystemSection->createChild("Info")->addContent("The kind of installed guest operating system");
1169 Utf8Str strOSDesc;
1170 convertCIMOSType2VBoxOSType(strOSDesc, (ovf::CIMOSType_T)pvsdeOS->strOvf.toInt32(), "");
1171 pelmOperatingSystemSection->createChild("Description")->addContent(strOSDesc);
1172 // add the VirtualBox ostype in a custom tag in a different namespace
1173 xml::ElementNode *pelmVBoxOSType = pelmOperatingSystemSection->createChild("vbox:OSType");
1174 pelmVBoxOSType->setAttribute("ovf:required", "false");
1175 pelmVBoxOSType->addContent(pvsdeOS->strVBoxCurrent);
1176
1177 // <VirtualHardwareSection ovf:id="hw1" ovf:transport="iso">
1178 xml::ElementNode *pelmVirtualHardwareSection;
1179 if (enFormat == ovf::OVFVersion_0_9)
1180 {
1181 // <Section xsi:type="ovf:VirtualHardwareSection_Type">
1182 pelmVirtualHardwareSection = pelmVirtualSystem->createChild("Section");
1183 pelmVirtualHardwareSection->setAttribute("xsi:type", "ovf:VirtualHardwareSection_Type");
1184 }
1185 else
1186 pelmVirtualHardwareSection = pelmVirtualSystem->createChild("VirtualHardwareSection");
1187
1188 pelmVirtualHardwareSection->createChild("Info")->addContent("Virtual hardware requirements for a virtual machine");
1189
1190 /* <System>
1191 <vssd:Description>Description of the virtual hardware section.</vssd:Description>
1192 <vssd:ElementName>vmware</vssd:ElementName>
1193 <vssd:InstanceID>1</vssd:InstanceID>
1194 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
1195 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
1196 </System> */
1197 xml::ElementNode *pelmSystem = pelmVirtualHardwareSection->createChild("System");
1198
1199 pelmSystem->createChild("vssd:ElementName")->addContent("Virtual Hardware Family"); // required OVF 1.0
1200
1201 // <vssd:InstanceId>0</vssd:InstanceId>
1202 if (enFormat == ovf::OVFVersion_0_9)
1203 pelmSystem->createChild("vssd:InstanceId")->addContent("0");
1204 else // capitalization changed...
1205 pelmSystem->createChild("vssd:InstanceID")->addContent("0");
1206
1207 // <vssd:VirtualSystemIdentifier>VAtest</vssd:VirtualSystemIdentifier>
1208 pelmSystem->createChild("vssd:VirtualSystemIdentifier")->addContent(strVMName);
1209 // <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
1210 const char *pcszHardware = "virtualbox-2.2";
1211 if (enFormat == ovf::OVFVersion_0_9)
1212 // pretend to be vmware compatible then
1213 pcszHardware = "vmx-6";
1214 pelmSystem->createChild("vssd:VirtualSystemType")->addContent(pcszHardware);
1215
1216 // loop thru all description entries twice; once to write out all
1217 // devices _except_ disk images, and a second time to assign the
1218 // disk images; this is because disk images need to reference
1219 // IDE controllers, and we can't know their instance IDs without
1220 // assigning them first
1221
1222 uint32_t idIDEPrimaryController = 0;
1223 int32_t lIDEPrimaryControllerIndex = 0;
1224 uint32_t idIDESecondaryController = 0;
1225 int32_t lIDESecondaryControllerIndex = 0;
1226 uint32_t idSATAController = 0;
1227 int32_t lSATAControllerIndex = 0;
1228 uint32_t idSCSIController = 0;
1229 int32_t lSCSIControllerIndex = 0;
1230
1231 uint32_t ulInstanceID = 1;
1232
1233 uint32_t cDVDs = 0;
1234
1235 for (size_t uLoop = 1; uLoop <= 2; ++uLoop)
1236 {
1237 int32_t lIndexThis = 0;
1238 vector<VirtualSystemDescriptionEntry>::const_iterator itD;
1239 for (itD = vsdescThis->m->maDescriptions.begin();
1240 itD != vsdescThis->m->maDescriptions.end();
1241 ++itD, ++lIndexThis)
1242 {
1243 const VirtualSystemDescriptionEntry &desc = *itD;
1244
1245 LogFlowFunc(("Loop %u: handling description entry ulIndex=%u, type=%s, strRef=%s, strOvf=%s, strVBox=%s, strExtraConfig=%s\n",
1246 uLoop,
1247 desc.ulIndex,
1248 ( desc.type == VirtualSystemDescriptionType_HardDiskControllerIDE ? "HardDiskControllerIDE"
1249 : desc.type == VirtualSystemDescriptionType_HardDiskControllerSATA ? "HardDiskControllerSATA"
1250 : desc.type == VirtualSystemDescriptionType_HardDiskControllerSCSI ? "HardDiskControllerSCSI"
1251 : desc.type == VirtualSystemDescriptionType_HardDiskControllerSAS ? "HardDiskControllerSAS"
1252 : desc.type == VirtualSystemDescriptionType_HardDiskImage ? "HardDiskImage"
1253 : Utf8StrFmt("%d", desc.type).c_str()),
1254 desc.strRef.c_str(),
1255 desc.strOvf.c_str(),
1256 desc.strVBoxCurrent.c_str(),
1257 desc.strExtraConfigCurrent.c_str()));
1258
1259 ovf::ResourceType_T type = (ovf::ResourceType_T)0; // if this becomes != 0 then we do stuff
1260 Utf8Str strResourceSubType;
1261
1262 Utf8Str strDescription; // results in <rasd:Description>...</rasd:Description> block
1263 Utf8Str strCaption; // results in <rasd:Caption>...</rasd:Caption> block
1264
1265 uint32_t ulParent = 0;
1266
1267 int32_t lVirtualQuantity = -1;
1268 Utf8Str strAllocationUnits;
1269
1270 int32_t lAddress = -1;
1271 int32_t lBusNumber = -1;
1272 int32_t lAddressOnParent = -1;
1273
1274 int32_t lAutomaticAllocation = -1; // 0 means "false", 1 means "true"
1275 Utf8Str strConnection; // results in <rasd:Connection>...</rasd:Connection> block
1276 Utf8Str strHostResource;
1277
1278 uint64_t uTemp;
1279
1280 ovf::VirtualHardwareItem vhi;
1281 ovf::StorageItem si;
1282 ovf::EthernetPortItem epi;
1283
1284 switch (desc.type)
1285 {
1286 case VirtualSystemDescriptionType_CPU:
1287 /* <Item>
1288 <rasd:Caption>1 virtual CPU</rasd:Caption>
1289 <rasd:Description>Number of virtual CPUs</rasd:Description>
1290 <rasd:ElementName>virtual CPU</rasd:ElementName>
1291 <rasd:InstanceID>1</rasd:InstanceID>
1292 <rasd:ResourceType>3</rasd:ResourceType>
1293 <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
1294 </Item> */
1295 if (uLoop == 1)
1296 {
1297 strDescription = "Number of virtual CPUs";
1298 type = ovf::ResourceType_Processor; // 3
1299 desc.strVBoxCurrent.toInt(uTemp);
1300 lVirtualQuantity = (int32_t)uTemp;
1301 strCaption = Utf8StrFmt("%d virtual CPU", lVirtualQuantity); // without this ovftool
1302 // won't eat the item
1303 }
1304 break;
1305
1306 case VirtualSystemDescriptionType_Memory:
1307 /* <Item>
1308 <rasd:AllocationUnits>MegaBytes</rasd:AllocationUnits>
1309 <rasd:Caption>256 MB of memory</rasd:Caption>
1310 <rasd:Description>Memory Size</rasd:Description>
1311 <rasd:ElementName>Memory</rasd:ElementName>
1312 <rasd:InstanceID>2</rasd:InstanceID>
1313 <rasd:ResourceType>4</rasd:ResourceType>
1314 <rasd:VirtualQuantity>256</rasd:VirtualQuantity>
1315 </Item> */
1316 if (uLoop == 1)
1317 {
1318 strDescription = "Memory Size";
1319 type = ovf::ResourceType_Memory; // 4
1320 desc.strVBoxCurrent.toInt(uTemp);
1321 lVirtualQuantity = (int32_t)(uTemp / _1M);
1322 strAllocationUnits = "MegaBytes";
1323 strCaption = Utf8StrFmt("%d MB of memory", lVirtualQuantity); // without this ovftool
1324 // won't eat the item
1325 }
1326 break;
1327
1328 case VirtualSystemDescriptionType_HardDiskControllerIDE:
1329 /* <Item>
1330 <rasd:Caption>ideController1</rasd:Caption>
1331 <rasd:Description>IDE Controller</rasd:Description>
1332 <rasd:InstanceId>5</rasd:InstanceId>
1333 <rasd:ResourceType>5</rasd:ResourceType>
1334 <rasd:Address>1</rasd:Address>
1335 <rasd:BusNumber>1</rasd:BusNumber>
1336 </Item> */
1337 if (uLoop == 1)
1338 {
1339 strDescription = "IDE Controller";
1340 type = ovf::ResourceType_IDEController; // 5
1341 strResourceSubType = desc.strVBoxCurrent;
1342
1343 if (!lIDEPrimaryControllerIndex)
1344 {
1345 // first IDE controller:
1346 strCaption = "ideController0";
1347 lAddress = 0;
1348 lBusNumber = 0;
1349 // remember this ID
1350 idIDEPrimaryController = ulInstanceID;
1351 lIDEPrimaryControllerIndex = lIndexThis;
1352 }
1353 else
1354 {
1355 // second IDE controller:
1356 strCaption = "ideController1";
1357 lAddress = 1;
1358 lBusNumber = 1;
1359 // remember this ID
1360 idIDESecondaryController = ulInstanceID;
1361 lIDESecondaryControllerIndex = lIndexThis;
1362 }
1363 }
1364 break;
1365
1366 case VirtualSystemDescriptionType_HardDiskControllerSATA:
1367 /* <Item>
1368 <rasd:Caption>sataController0</rasd:Caption>
1369 <rasd:Description>SATA Controller</rasd:Description>
1370 <rasd:InstanceId>4</rasd:InstanceId>
1371 <rasd:ResourceType>20</rasd:ResourceType>
1372 <rasd:ResourceSubType>ahci</rasd:ResourceSubType>
1373 <rasd:Address>0</rasd:Address>
1374 <rasd:BusNumber>0</rasd:BusNumber>
1375 </Item>
1376 */
1377 if (uLoop == 1)
1378 {
1379 strDescription = "SATA Controller";
1380 strCaption = "sataController0";
1381 type = ovf::ResourceType_OtherStorageDevice; // 20
1382 // it seems that OVFTool always writes these two, and since we can only
1383 // have one SATA controller, we'll use this as well
1384 lAddress = 0;
1385 lBusNumber = 0;
1386
1387 if ( desc.strVBoxCurrent.isEmpty() // AHCI is the default in VirtualBox
1388 || (!desc.strVBoxCurrent.compare("ahci", Utf8Str::CaseInsensitive))
1389 )
1390 strResourceSubType = "AHCI";
1391 else
1392 throw setError(VBOX_E_NOT_SUPPORTED,
1393 tr("Invalid config string \"%s\" in SATA controller"), desc.strVBoxCurrent.c_str());
1394
1395 // remember this ID
1396 idSATAController = ulInstanceID;
1397 lSATAControllerIndex = lIndexThis;
1398 }
1399 break;
1400
1401 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
1402 case VirtualSystemDescriptionType_HardDiskControllerSAS:
1403 /* <Item>
1404 <rasd:Caption>scsiController0</rasd:Caption>
1405 <rasd:Description>SCSI Controller</rasd:Description>
1406 <rasd:InstanceId>4</rasd:InstanceId>
1407 <rasd:ResourceType>6</rasd:ResourceType>
1408 <rasd:ResourceSubType>buslogic</rasd:ResourceSubType>
1409 <rasd:Address>0</rasd:Address>
1410 <rasd:BusNumber>0</rasd:BusNumber>
1411 </Item>
1412 */
1413 if (uLoop == 1)
1414 {
1415 strDescription = "SCSI Controller";
1416 strCaption = "scsiController0";
1417 type = ovf::ResourceType_ParallelSCSIHBA; // 6
1418 // it seems that OVFTool always writes these two, and since we can only
1419 // have one SATA controller, we'll use this as well
1420 lAddress = 0;
1421 lBusNumber = 0;
1422
1423 if ( desc.strVBoxCurrent.isEmpty() // LsiLogic is the default in VirtualBox
1424 || (!desc.strVBoxCurrent.compare("lsilogic", Utf8Str::CaseInsensitive))
1425 )
1426 strResourceSubType = "lsilogic";
1427 else if (!desc.strVBoxCurrent.compare("buslogic", Utf8Str::CaseInsensitive))
1428 strResourceSubType = "buslogic";
1429 else if (!desc.strVBoxCurrent.compare("lsilogicsas", Utf8Str::CaseInsensitive))
1430 strResourceSubType = "lsilogicsas";
1431 else
1432 throw setError(VBOX_E_NOT_SUPPORTED,
1433 tr("Invalid config string \"%s\" in SCSI/SAS controller"),
1434 desc.strVBoxCurrent.c_str());
1435
1436 // remember this ID
1437 idSCSIController = ulInstanceID;
1438 lSCSIControllerIndex = lIndexThis;
1439 }
1440 break;
1441
1442 case VirtualSystemDescriptionType_HardDiskImage:
1443 /* <Item>
1444 <rasd:Caption>disk1</rasd:Caption>
1445 <rasd:InstanceId>8</rasd:InstanceId>
1446 <rasd:ResourceType>17</rasd:ResourceType>
1447 <rasd:HostResource>/disk/vmdisk1</rasd:HostResource>
1448 <rasd:Parent>4</rasd:Parent>
1449 <rasd:AddressOnParent>0</rasd:AddressOnParent>
1450 </Item> */
1451 if (uLoop == 2)
1452 {
1453 uint32_t cDisks = (uint32_t)stack.mapDisks.size();
1454 Utf8Str strDiskID = Utf8StrFmt("vmdisk%RI32", ++cDisks);
1455
1456 strDescription = "Disk Image";
1457 strCaption = Utf8StrFmt("disk%RI32", cDisks); // this is not used for anything else
1458 type = ovf::ResourceType_HardDisk; // 17
1459
1460 // the following references the "<Disks>" XML block
1461 strHostResource = Utf8StrFmt("/disk/%s", strDiskID.c_str());
1462
1463 // controller=<index>;channel=<c>
1464 size_t pos1 = desc.strExtraConfigCurrent.find("controller=");
1465 size_t pos2 = desc.strExtraConfigCurrent.find("channel=");
1466 int32_t lControllerIndex = -1;
1467 if (pos1 != Utf8Str::npos)
1468 {
1469 RTStrToInt32Ex(desc.strExtraConfigCurrent.c_str() + pos1 + 11, NULL, 0, &lControllerIndex);
1470 if (lControllerIndex == lIDEPrimaryControllerIndex)
1471 ulParent = idIDEPrimaryController;
1472 else if (lControllerIndex == lIDESecondaryControllerIndex)
1473 ulParent = idIDESecondaryController;
1474 else if (lControllerIndex == lSCSIControllerIndex)
1475 ulParent = idSCSIController;
1476 else if (lControllerIndex == lSATAControllerIndex)
1477 ulParent = idSATAController;
1478 }
1479 if (pos2 != Utf8Str::npos)
1480 RTStrToInt32Ex(desc.strExtraConfigCurrent.c_str() + pos2 + 8, NULL, 0, &lAddressOnParent);
1481
1482 LogFlowFunc(("HardDiskImage details: pos1=%d, pos2=%d, lControllerIndex=%d, lIDEPrimaryControllerIndex=%d, lIDESecondaryControllerIndex=%d, ulParent=%d, lAddressOnParent=%d\n",
1483 pos1, pos2, lControllerIndex, lIDEPrimaryControllerIndex, lIDESecondaryControllerIndex,
1484 ulParent, lAddressOnParent));
1485
1486 if ( !ulParent
1487 || lAddressOnParent == -1
1488 )
1489 throw setError(VBOX_E_NOT_SUPPORTED,
1490 tr("Missing or bad extra config string in hard disk image: \"%s\""),
1491 desc.strExtraConfigCurrent.c_str());
1492
1493 stack.mapDisks[strDiskID] = &desc;
1494 }
1495 break;
1496
1497 case VirtualSystemDescriptionType_Floppy:
1498 if (uLoop == 1)
1499 {
1500 strDescription = "Floppy Drive";
1501 strCaption = "floppy0"; // this is what OVFTool writes
1502 type = ovf::ResourceType_FloppyDrive; // 14
1503 lAutomaticAllocation = 0;
1504 lAddressOnParent = 0; // this is what OVFTool writes
1505 }
1506 break;
1507
1508 case VirtualSystemDescriptionType_CDROM:
1509 /* <Item>
1510 <rasd:Caption>cdrom1</rasd:Caption>
1511 <rasd:InstanceId>8</rasd:InstanceId>
1512 <rasd:ResourceType>15</rasd:ResourceType>
1513 <rasd:HostResource>/disk/cdrom1</rasd:HostResource>
1514 <rasd:Parent>4</rasd:Parent>
1515 <rasd:AddressOnParent>0</rasd:AddressOnParent>
1516 </Item> */
1517 if (uLoop == 2)
1518 {
1519 //uint32_t cDisks = stack.mapDisks.size();
1520 Utf8Str strDiskID = Utf8StrFmt("iso%RI32", ++cDVDs);
1521
1522 strDescription = "CD-ROM Drive";
1523 strCaption = Utf8StrFmt("cdrom%RI32", cDVDs); // OVFTool starts with 1
1524 type = ovf::ResourceType_CDDrive; // 15
1525 lAutomaticAllocation = 1;
1526
1527 //skip empty Medium. There are no information to add into section <References> or <DiskSection>
1528 if (desc.strVBoxCurrent.isNotEmpty() &&
1529 desc.skipIt == false)
1530 {
1531 // the following references the "<Disks>" XML block
1532 strHostResource = Utf8StrFmt("/disk/%s", strDiskID.c_str());
1533 }
1534
1535 // controller=<index>;channel=<c>
1536 size_t pos1 = desc.strExtraConfigCurrent.find("controller=");
1537 size_t pos2 = desc.strExtraConfigCurrent.find("channel=");
1538 int32_t lControllerIndex = -1;
1539 if (pos1 != Utf8Str::npos)
1540 {
1541 RTStrToInt32Ex(desc.strExtraConfigCurrent.c_str() + pos1 + 11, NULL, 0, &lControllerIndex);
1542 if (lControllerIndex == lIDEPrimaryControllerIndex)
1543 ulParent = idIDEPrimaryController;
1544 else if (lControllerIndex == lIDESecondaryControllerIndex)
1545 ulParent = idIDESecondaryController;
1546 else if (lControllerIndex == lSCSIControllerIndex)
1547 ulParent = idSCSIController;
1548 else if (lControllerIndex == lSATAControllerIndex)
1549 ulParent = idSATAController;
1550 }
1551 if (pos2 != Utf8Str::npos)
1552 RTStrToInt32Ex(desc.strExtraConfigCurrent.c_str() + pos2 + 8, NULL, 0, &lAddressOnParent);
1553
1554 LogFlowFunc(("DVD drive details: pos1=%d, pos2=%d, lControllerIndex=%d, lIDEPrimaryControllerIndex=%d, lIDESecondaryControllerIndex=%d, ulParent=%d, lAddressOnParent=%d\n",
1555 pos1, pos2, lControllerIndex, lIDEPrimaryControllerIndex,
1556 lIDESecondaryControllerIndex, ulParent, lAddressOnParent));
1557
1558 if ( !ulParent
1559 || lAddressOnParent == -1
1560 )
1561 throw setError(VBOX_E_NOT_SUPPORTED,
1562 tr("Missing or bad extra config string in DVD drive medium: \"%s\""),
1563 desc.strExtraConfigCurrent.c_str());
1564
1565 stack.mapDisks[strDiskID] = &desc;
1566 // there is no DVD drive map to update because it is
1567 // handled completely with this entry.
1568 }
1569 break;
1570
1571 case VirtualSystemDescriptionType_NetworkAdapter:
1572 /* <Item>
1573 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
1574 <rasd:Caption>Ethernet adapter on 'VM Network'</rasd:Caption>
1575 <rasd:Connection>VM Network</rasd:Connection>
1576 <rasd:ElementName>VM network</rasd:ElementName>
1577 <rasd:InstanceID>3</rasd:InstanceID>
1578 <rasd:ResourceType>10</rasd:ResourceType>
1579 </Item> */
1580 if (uLoop == 2)
1581 {
1582 lAutomaticAllocation = 1;
1583 strCaption = Utf8StrFmt("Ethernet adapter on '%s'", desc.strOvf.c_str());
1584 type = ovf::ResourceType_EthernetAdapter; // 10
1585 /* Set the hardware type to something useful.
1586 * To be compatible with vmware & others we set
1587 * PCNet32 for our PCNet types & E1000 for the
1588 * E1000 cards. */
1589 switch (desc.strVBoxCurrent.toInt32())
1590 {
1591 case NetworkAdapterType_Am79C970A:
1592 case NetworkAdapterType_Am79C973: strResourceSubType = "PCNet32"; break;
1593#ifdef VBOX_WITH_E1000
1594 case NetworkAdapterType_I82540EM:
1595 case NetworkAdapterType_I82545EM:
1596 case NetworkAdapterType_I82543GC: strResourceSubType = "E1000"; break;
1597#endif /* VBOX_WITH_E1000 */
1598 }
1599 strConnection = desc.strOvf;
1600
1601 stack.mapNetworks[desc.strOvf] = true;
1602 }
1603 break;
1604
1605 case VirtualSystemDescriptionType_USBController:
1606 /* <Item ovf:required="false">
1607 <rasd:Caption>usb</rasd:Caption>
1608 <rasd:Description>USB Controller</rasd:Description>
1609 <rasd:InstanceId>3</rasd:InstanceId>
1610 <rasd:ResourceType>23</rasd:ResourceType>
1611 <rasd:Address>0</rasd:Address>
1612 <rasd:BusNumber>0</rasd:BusNumber>
1613 </Item> */
1614 if (uLoop == 1)
1615 {
1616 strDescription = "USB Controller";
1617 strCaption = "usb";
1618 type = ovf::ResourceType_USBController; // 23
1619 lAddress = 0; // this is what OVFTool writes
1620 lBusNumber = 0; // this is what OVFTool writes
1621 }
1622 break;
1623
1624 case VirtualSystemDescriptionType_SoundCard:
1625 /* <Item ovf:required="false">
1626 <rasd:Caption>sound</rasd:Caption>
1627 <rasd:Description>Sound Card</rasd:Description>
1628 <rasd:InstanceId>10</rasd:InstanceId>
1629 <rasd:ResourceType>35</rasd:ResourceType>
1630 <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
1631 <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
1632 <rasd:AddressOnParent>3</rasd:AddressOnParent>
1633 </Item> */
1634 if (uLoop == 1)
1635 {
1636 strDescription = "Sound Card";
1637 strCaption = "sound";
1638 type = ovf::ResourceType_SoundCard; // 35
1639 strResourceSubType = desc.strOvf; // e.g. ensoniq1371
1640 lAutomaticAllocation = 0;
1641 lAddressOnParent = 3; // what gives? this is what OVFTool writes
1642 }
1643 break;
1644 }
1645
1646 if (type)
1647 {
1648 xml::ElementNode *pItem;
1649 xml::ElementNode *pItemHelper;
1650 RTCString itemElement;
1651 RTCString itemElementHelper;
1652
1653 if (enFormat == ovf::OVFVersion_2_0)
1654 {
1655 if(uLoop == 2)
1656 {
1657 if (desc.type == VirtualSystemDescriptionType_NetworkAdapter)
1658 {
1659 itemElement = "epasd:";
1660 pItem = pelmVirtualHardwareSection->createChild("EthernetPortItem");
1661 }
1662 else if (desc.type == VirtualSystemDescriptionType_CDROM ||
1663 desc.type == VirtualSystemDescriptionType_HardDiskImage)
1664 {
1665 itemElement = "sasd:";
1666 pItem = pelmVirtualHardwareSection->createChild("StorageItem");
1667 }
1668 else
1669 pItem = NULL;
1670 }
1671 else
1672 {
1673 itemElement = "rasd:";
1674 pItem = pelmVirtualHardwareSection->createChild("Item");
1675 }
1676 }
1677 else
1678 {
1679 itemElement = "rasd:";
1680 pItem = pelmVirtualHardwareSection->createChild("Item");
1681 }
1682
1683 // NOTE: DO NOT CHANGE THE ORDER of these items! The OVF standards prescribes that
1684 // the elements from the rasd: namespace must be sorted by letter, and VMware
1685 // actually requires this as well (see public bug #6612)
1686
1687 if (lAddress != -1)
1688 {
1689 //pItem->createChild("rasd:Address")->addContent(Utf8StrFmt("%d", lAddress));
1690 itemElementHelper = itemElement;
1691 pItemHelper = pItem->createChild(itemElementHelper.append("Address").c_str());
1692 pItemHelper->addContent(Utf8StrFmt("%d", lAddress));
1693 }
1694
1695 if (lAddressOnParent != -1)
1696 {
1697 //pItem->createChild("rasd:AddressOnParent")->addContent(Utf8StrFmt("%d", lAddressOnParent));
1698 itemElementHelper = itemElement;
1699 pItemHelper = pItem->createChild(itemElementHelper.append("AddressOnParent").c_str());
1700 pItemHelper->addContent(Utf8StrFmt("%d", lAddressOnParent));
1701 }
1702
1703 if (!strAllocationUnits.isEmpty())
1704 {
1705 //pItem->createChild("rasd:AllocationUnits")->addContent(strAllocationUnits);
1706 itemElementHelper = itemElement;
1707 pItemHelper = pItem->createChild(itemElementHelper.append("AllocationUnits").c_str());
1708 pItemHelper->addContent(strAllocationUnits);
1709 }
1710
1711 if (lAutomaticAllocation != -1)
1712 {
1713 //pItem->createChild("rasd:AutomaticAllocation")->addContent( (lAutomaticAllocation) ? "true" : "false" );
1714 itemElementHelper = itemElement;
1715 pItemHelper = pItem->createChild(itemElementHelper.append("AutomaticAllocation").c_str());
1716 pItemHelper->addContent((lAutomaticAllocation) ? "true" : "false" );
1717 }
1718
1719 if (lBusNumber != -1)
1720 {
1721 if (enFormat == ovf::OVFVersion_0_9)
1722 {
1723 // BusNumber is invalid OVF 1.0 so only write it in 0.9 mode for OVFTool
1724 //pItem->createChild("rasd:BusNumber")->addContent(Utf8StrFmt("%d", lBusNumber));
1725 itemElementHelper = itemElement;
1726 pItemHelper = pItem->createChild(itemElementHelper.append("BusNumber").c_str());
1727 pItemHelper->addContent(Utf8StrFmt("%d", lBusNumber));
1728 }
1729 }
1730
1731 if (!strCaption.isEmpty())
1732 {
1733 //pItem->createChild("rasd:Caption")->addContent(strCaption);
1734 itemElementHelper = itemElement;
1735 pItemHelper = pItem->createChild(itemElementHelper.append("Caption").c_str());
1736 pItemHelper->addContent(strCaption);
1737 }
1738
1739 if (!strConnection.isEmpty())
1740 {
1741 //pItem->createChild("rasd:Connection")->addContent(strConnection);
1742 itemElementHelper = itemElement;
1743 pItemHelper = pItem->createChild(itemElementHelper.append("Connection").c_str());
1744 pItemHelper->addContent(strConnection);
1745 }
1746
1747 if (!strDescription.isEmpty())
1748 {
1749 //pItem->createChild("rasd:Description")->addContent(strDescription);
1750 itemElementHelper = itemElement;
1751 pItemHelper = pItem->createChild(itemElementHelper.append("Description").c_str());
1752 pItemHelper->addContent(strDescription);
1753 }
1754
1755 if (!strCaption.isEmpty())
1756 {
1757 if (enFormat == ovf::OVFVersion_1_0)
1758 {
1759 //pItem->createChild("rasd:ElementName")->addContent(strCaption);
1760 itemElementHelper = itemElement;
1761 pItemHelper = pItem->createChild(itemElementHelper.append("ElementName").c_str());
1762 pItemHelper->addContent(strCaption);
1763 }
1764 }
1765
1766 if (!strHostResource.isEmpty())
1767 {
1768 //pItem->createChild("rasd:HostResource")->addContent(strHostResource);
1769 itemElementHelper = itemElement;
1770 pItemHelper = pItem->createChild(itemElementHelper.append("HostResource").c_str());
1771 pItemHelper->addContent(strHostResource);
1772 }
1773
1774 {
1775 // <rasd:InstanceID>1</rasd:InstanceID>
1776 itemElementHelper = itemElement;
1777 if (enFormat == ovf::OVFVersion_0_9)
1778 //pelmInstanceID = pItem->createChild("rasd:InstanceId");
1779 pItemHelper = pItem->createChild(itemElementHelper.append("InstanceId").c_str());
1780 else
1781 //pelmInstanceID = pItem->createChild("rasd:InstanceID"); // capitalization changed...
1782 pItemHelper = pItem->createChild(itemElementHelper.append("InstanceID").c_str());
1783
1784 pItemHelper->addContent(Utf8StrFmt("%d", ulInstanceID++));
1785 }
1786
1787 if (ulParent)
1788 {
1789 //pItem->createChild("rasd:Parent")->addContent(Utf8StrFmt("%d", ulParent));
1790 itemElementHelper = itemElement;
1791 pItemHelper = pItem->createChild(itemElementHelper.append("Parent").c_str());
1792 pItemHelper->addContent(Utf8StrFmt("%d", ulParent));
1793 }
1794
1795 if (!strResourceSubType.isEmpty())
1796 {
1797 //pItem->createChild("rasd:ResourceSubType")->addContent(strResourceSubType);
1798 itemElementHelper = itemElement;
1799 pItemHelper = pItem->createChild(itemElementHelper.append("ResourceSubType").c_str());
1800 pItemHelper->addContent(strResourceSubType);
1801 }
1802
1803 {
1804 // <rasd:ResourceType>3</rasd:ResourceType>
1805 //pItem->createChild("rasd:ResourceType")->addContent(Utf8StrFmt("%d", type));
1806 itemElementHelper = itemElement;
1807 pItemHelper = pItem->createChild(itemElementHelper.append("ResourceType").c_str());
1808 pItemHelper->addContent(Utf8StrFmt("%d", type));
1809 }
1810
1811 // <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
1812 if (lVirtualQuantity != -1)
1813 {
1814 //pItem->createChild("rasd:VirtualQuantity")->addContent(Utf8StrFmt("%d", lVirtualQuantity));
1815 itemElementHelper = itemElement;
1816 pItemHelper = pItem->createChild(itemElementHelper.append("VirtualQuantity").c_str());
1817 pItemHelper->addContent(Utf8StrFmt("%d", lVirtualQuantity));
1818 }
1819 }
1820 }
1821 } // for (size_t uLoop = 1; uLoop <= 2; ++uLoop)
1822
1823 // now that we're done with the official OVF <Item> tags under <VirtualSystem>, write out VirtualBox XML
1824 // under the vbox: namespace
1825 xml::ElementNode *pelmVBoxMachine = pelmVirtualSystem->createChild("vbox:Machine");
1826 // ovf:required="false" tells other OVF parsers that they can ignore this thing
1827 pelmVBoxMachine->setAttribute("ovf:required", "false");
1828 // ovf:Info element is required or VMware will bail out on the vbox:Machine element
1829 pelmVBoxMachine->createChild("ovf:Info")->addContent("Complete VirtualBox machine configuration in VirtualBox format");
1830
1831 // create an empty machine config
1832 settings::MachineConfigFile *pConfig = new settings::MachineConfigFile(NULL);
1833
1834 writeLock.release();
1835 try
1836 {
1837 AutoWriteLock machineLock(vsdescThis->m->pMachine COMMA_LOCKVAL_SRC_POS);
1838 // fill the machine config
1839 vsdescThis->m->pMachine->i_copyMachineDataToSettings(*pConfig);
1840
1841 // Apply export tweaks to machine settings
1842 bool fStripAllMACs = m->optListExport.contains(ExportOptions_StripAllMACs);
1843 bool fStripAllNonNATMACs = m->optListExport.contains(ExportOptions_StripAllNonNATMACs);
1844 if (fStripAllMACs || fStripAllNonNATMACs)
1845 {
1846 for (settings::NetworkAdaptersList::iterator it = pConfig->hardwareMachine.llNetworkAdapters.begin();
1847 it != pConfig->hardwareMachine.llNetworkAdapters.end();
1848 ++it)
1849 {
1850 settings::NetworkAdapter &nic = *it;
1851 if (fStripAllMACs || (fStripAllNonNATMACs && nic.mode != NetworkAttachmentType_NAT))
1852 nic.strMACAddress.setNull();
1853 }
1854 }
1855
1856 // write the machine config to the vbox:Machine element
1857 pConfig->buildMachineXML(*pelmVBoxMachine,
1858 settings::MachineConfigFile::BuildMachineXML_WriteVBoxVersionAttribute
1859 /*| settings::MachineConfigFile::BuildMachineXML_SkipRemovableMedia*/
1860 | settings::MachineConfigFile::BuildMachineXML_SuppressSavedState,
1861 // but not BuildMachineXML_IncludeSnapshots nor BuildMachineXML_MediaRegistry
1862 pllElementsWithUuidAttributes);
1863 delete pConfig;
1864 }
1865 catch (...)
1866 {
1867 writeLock.acquire();
1868 delete pConfig;
1869 throw;
1870 }
1871 writeLock.acquire();
1872}
1873
1874/**
1875 * Actual worker code for writing out OVF/OVA to disk. This is called from Appliance::taskThreadWriteOVF()
1876 * and therefore runs on the OVF/OVA write worker thread. This runs in two contexts:
1877 *
1878 * 1) in a first worker thread; in that case, Appliance::Write() called Appliance::i_writeImpl();
1879 *
1880 * 2) in a second worker thread; in that case, Appliance::Write() called Appliance::i_writeImpl(), which
1881 * called Appliance::i_writeS3(), which called Appliance::i_writeImpl(), which then called this. In other
1882 * words, to write to the cloud, the first worker thread first starts a second worker thread to create
1883 * temporary files and then uploads them to the S3 cloud server.
1884 *
1885 * @param pTask
1886 * @return
1887 */
1888HRESULT Appliance::i_writeFS(TaskOVF *pTask)
1889{
1890 LogFlowFuncEnter();
1891 LogFlowFunc(("ENTER appliance %p\n", this));
1892
1893 AutoCaller autoCaller(this);
1894 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1895
1896 HRESULT rc = S_OK;
1897
1898 // Lock the media tree early to make sure nobody else tries to make changes
1899 // to the tree. Also lock the IAppliance object for writing.
1900 AutoMultiWriteLock2 multiLock(&mVirtualBox->i_getMediaTreeLockHandle(), this->lockHandle() COMMA_LOCKVAL_SRC_POS);
1901 // Additional protect the IAppliance object, cause we leave the lock
1902 // when starting the disk export and we don't won't block other
1903 // callers on this lengthy operations.
1904 m->state = Data::ApplianceExporting;
1905
1906 if (pTask->locInfo.strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
1907 rc = i_writeFSOVF(pTask, multiLock);
1908 else
1909 rc = i_writeFSOVA(pTask, multiLock);
1910
1911 // reset the state so others can call methods again
1912 m->state = Data::ApplianceIdle;
1913
1914 LogFlowFunc(("rc=%Rhrc\n", rc));
1915 LogFlowFuncLeave();
1916 return rc;
1917}
1918
1919HRESULT Appliance::i_writeFSOVF(TaskOVF *pTask, AutoWriteLockBase& writeLock)
1920{
1921 LogFlowFuncEnter();
1922
1923 HRESULT rc = S_OK;
1924
1925 PVDINTERFACEIO pShaIo = 0;
1926 PVDINTERFACEIO pFileIo = 0;
1927 do
1928 {
1929 pShaIo = ShaCreateInterface();
1930 if (!pShaIo)
1931 {
1932 rc = E_OUTOFMEMORY;
1933 break;
1934 }
1935 pFileIo = FileCreateInterface();
1936 if (!pFileIo)
1937 {
1938 rc = E_OUTOFMEMORY;
1939 break;
1940 }
1941
1942 SHASTORAGE storage;
1943 RT_ZERO(storage);
1944 storage.fCreateDigest = m->fManifest;
1945 storage.fSha256 = m->fSha256;
1946
1947
1948 Utf8Str name = i_applianceIOName(applianceIOFile);
1949
1950 int vrc = VDInterfaceAdd(&pFileIo->Core, name.c_str(),
1951 VDINTERFACETYPE_IO, 0, sizeof(VDINTERFACEIO),
1952 &storage.pVDImageIfaces);
1953 if (RT_FAILURE(vrc))
1954 {
1955 rc = E_FAIL;
1956 break;
1957 }
1958 rc = i_writeFSImpl(pTask, writeLock, pShaIo, &storage);
1959 } while (0);
1960
1961 /* Cleanup */
1962 if (pShaIo)
1963 RTMemFree(pShaIo);
1964 if (pFileIo)
1965 RTMemFree(pFileIo);
1966
1967 LogFlowFuncLeave();
1968 return rc;
1969}
1970
1971HRESULT Appliance::i_writeFSOVA(TaskOVF *pTask, AutoWriteLockBase& writeLock)
1972{
1973 LogFlowFuncEnter();
1974
1975 RTTAR tar;
1976 int vrc = RTTarOpen(&tar, pTask->locInfo.strPath.c_str(), RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_ALL);
1977 if (RT_FAILURE(vrc))
1978 return setError(VBOX_E_FILE_ERROR,
1979 tr("Could not create OVA file '%s' (%Rrc)"),
1980 pTask->locInfo.strPath.c_str(), vrc);
1981
1982 HRESULT rc = S_OK;
1983
1984 PVDINTERFACEIO pShaIo = 0;
1985 PVDINTERFACEIO pTarIo = 0;
1986 do
1987 {
1988 pShaIo = ShaCreateInterface();
1989 if (!pShaIo)
1990 {
1991 rc = E_OUTOFMEMORY;
1992 break;
1993 }
1994 pTarIo = tarWriterCreateInterface();
1995 if (!pTarIo)
1996 {
1997 rc = E_OUTOFMEMORY;
1998 break;
1999 }
2000 SHASTORAGE storage;
2001 RT_ZERO(storage);
2002 storage.fCreateDigest = m->fManifest;
2003 storage.fSha256 = m->fSha256;
2004
2005 Utf8Str name = i_applianceIOName(applianceIOTar);
2006
2007 vrc = VDInterfaceAdd(&pTarIo->Core, name.c_str(),
2008 VDINTERFACETYPE_IO, tar, sizeof(VDINTERFACEIO),
2009 &storage.pVDImageIfaces);
2010
2011 if (RT_FAILURE(vrc))
2012 {
2013 rc = E_FAIL;
2014 break;
2015 }
2016 rc = i_writeFSImpl(pTask, writeLock, pShaIo, &storage);
2017 } while (0);
2018
2019 RTTarClose(tar);
2020
2021 /* Cleanup */
2022 if (pShaIo)
2023 RTMemFree(pShaIo);
2024 if (pTarIo)
2025 RTMemFree(pTarIo);
2026
2027 /* Delete ova file on error */
2028 if (FAILED(rc))
2029 RTFileDelete(pTask->locInfo.strPath.c_str());
2030
2031 LogFlowFuncLeave();
2032 return rc;
2033}
2034
2035HRESULT Appliance::i_writeFSImpl(TaskOVF *pTask, AutoWriteLockBase& writeLock, PVDINTERFACEIO pIfIo, PSHASTORAGE pStorage)
2036{
2037 LogFlowFuncEnter();
2038
2039 HRESULT rc = S_OK;
2040
2041 list<STRPAIR> fileList;
2042 try
2043 {
2044 int vrc;
2045 // the XML stack contains two maps for disks and networks, which allows us to
2046 // a) have a list of unique disk names (to make sure the same disk name is only added once)
2047 // and b) keep a list of all networks
2048 XMLStack stack;
2049 // Scope this to free the memory as soon as this is finished
2050 {
2051 // Create a xml document
2052 xml::Document doc;
2053 // Now fully build a valid ovf document in memory
2054 i_buildXML(writeLock, doc, stack, pTask->locInfo.strPath, pTask->enFormat);
2055 /* Extract the OVA file name */
2056 Utf8Str strOvaFile = pTask->locInfo.strPath;
2057 /* Extract the path */
2058 Utf8Str strOvfFile = strOvaFile.stripSuffix().append(".ovf");
2059 // Create a memory buffer containing the XML. */
2060 void *pvBuf = 0;
2061 size_t cbSize;
2062 xml::XmlMemWriter writer;
2063 writer.write(doc, &pvBuf, &cbSize);
2064 if (RT_UNLIKELY(!pvBuf))
2065 throw setError(VBOX_E_FILE_ERROR,
2066 tr("Could not create OVF file '%s'"),
2067 strOvfFile.c_str());
2068 /* Write the ovf file to disk. */
2069 vrc = writeBufferToFile(strOvfFile.c_str(), pvBuf, cbSize, pIfIo, pStorage);
2070 if (RT_FAILURE(vrc))
2071 throw setError(VBOX_E_FILE_ERROR,
2072 tr("Could not create OVF file '%s' (%Rrc)"),
2073 strOvfFile.c_str(), vrc);
2074 fileList.push_back(STRPAIR(strOvfFile, pStorage->strDigest));
2075 }
2076
2077 // We need a proper format description
2078 ComObjPtr<MediumFormat> formatTemp;
2079
2080 ComObjPtr<MediumFormat> format;
2081 // Scope for the AutoReadLock
2082 {
2083 SystemProperties *pSysProps = mVirtualBox->i_getSystemProperties();
2084 AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
2085 // We are always exporting to VMDK stream optimized for now
2086 formatTemp = pSysProps->i_mediumFormatFromExtension("iso");
2087
2088 format = pSysProps->i_mediumFormat("VMDK");
2089 if (format.isNull())
2090 throw setError(VBOX_E_NOT_SUPPORTED,
2091 tr("Invalid medium storage format"));
2092 }
2093
2094 // Finally, write out the disks!
2095 map<Utf8Str, const VirtualSystemDescriptionEntry*>::const_iterator itS;
2096 for (itS = stack.mapDisks.begin();
2097 itS != stack.mapDisks.end();
2098 ++itS)
2099 {
2100 const VirtualSystemDescriptionEntry *pDiskEntry = itS->second;
2101
2102 // source path: where the VBox image is
2103 const Utf8Str &strSrcFilePath = pDiskEntry->strVBoxCurrent;
2104
2105 //skip empty Medium. In common, It's may be empty CD/DVD
2106 if (strSrcFilePath.isEmpty() ||
2107 pDiskEntry->skipIt == true)
2108 continue;
2109
2110 // Do NOT check here whether the file exists. findHardDisk will
2111 // figure that out, and filesystem-based tests are simply wrong
2112 // in the general case (think of iSCSI).
2113
2114 // clone the disk:
2115 ComObjPtr<Medium> pSourceDisk;
2116
2117 Log(("Finding source disk \"%s\"\n", strSrcFilePath.c_str()));
2118
2119 if (pDiskEntry->type == VirtualSystemDescriptionType_HardDiskImage)
2120 {
2121 rc = mVirtualBox->i_findHardDiskByLocation(strSrcFilePath, true, &pSourceDisk);
2122 if (FAILED(rc)) throw rc;
2123 }
2124 else//may be CD or DVD
2125 {
2126 rc = mVirtualBox->i_findDVDOrFloppyImage(DeviceType_DVD,
2127 NULL,
2128 strSrcFilePath,
2129 true,
2130 &pSourceDisk);
2131 if (FAILED(rc)) throw rc;
2132 }
2133
2134 Bstr uuidSource;
2135 rc = pSourceDisk->COMGETTER(Id)(uuidSource.asOutParam());
2136 if (FAILED(rc)) throw rc;
2137 Guid guidSource(uuidSource);
2138
2139 // output filename
2140 const Utf8Str &strTargetFileNameOnly = pDiskEntry->strOvf;
2141 // target path needs to be composed from where the output OVF is
2142 Utf8Str strTargetFilePath(pTask->locInfo.strPath);
2143 strTargetFilePath.stripFilename()
2144 .append("/")
2145 .append(strTargetFileNameOnly);
2146
2147 // The exporting requests a lock on the media tree. So leave our lock temporary.
2148 writeLock.release();
2149 try
2150 {
2151 // advance to the next operation
2152 pTask->pProgress->SetNextOperation(BstrFmt(tr("Exporting to disk image '%s'"),
2153 RTPathFilename(strTargetFilePath.c_str())).raw(),
2154 pDiskEntry->ulSizeMB); // operation's weight, as set up
2155 // with the IProgress originally
2156
2157 // create a flat copy of the source disk image
2158 if (pDiskEntry->type == VirtualSystemDescriptionType_HardDiskImage)
2159 {
2160 ComObjPtr<Progress> pProgress2;
2161 pProgress2.createObject();
2162 rc = pProgress2->init(mVirtualBox, static_cast<IAppliance*>(this),
2163 BstrFmt(tr("Creating medium '%s'"),
2164 strTargetFilePath.c_str()).raw(), TRUE);
2165 if (FAILED(rc)) throw rc;
2166
2167 rc = pSourceDisk->i_exportFile(strTargetFilePath.c_str(),
2168 format,
2169 MediumVariant_VmdkStreamOptimized,
2170 pIfIo,
2171 pStorage,
2172 pProgress2);
2173 if (FAILED(rc)) throw rc;
2174
2175 ComPtr<IProgress> pProgress3(pProgress2);
2176 // now wait for the background disk operation to complete; this throws HRESULTs on error
2177 i_waitForAsyncProgress(pTask->pProgress, pProgress3);
2178 }
2179 else
2180 {
2181 //copy/clone CD/DVD image
2182 Assert(pDiskEntry->type == VirtualSystemDescriptionType_CDROM);
2183
2184 /* Read the ISO file and add one to OVA/OVF package */
2185 {
2186 void *pvStorage;
2187 RTFILE pFile = NULL;
2188 void *pvUser = pStorage;
2189
2190 vrc = pIfIo->pfnOpen(pvUser, strTargetFilePath.c_str(),
2191 RTFILE_O_OPEN_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE,
2192 0,
2193 &pvStorage);
2194 if (RT_FAILURE(vrc))
2195 throw setError(VBOX_E_FILE_ERROR,
2196 tr("Could not create or open file '%s' (%Rrc)"),
2197 strTargetFilePath.c_str(), vrc);
2198
2199 vrc = RTFileOpen(&pFile,
2200 strSrcFilePath.c_str(),
2201 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
2202
2203 if (RT_FAILURE(vrc) || pFile == NULL)
2204 {
2205 pIfIo->pfnClose(pvUser, pvStorage);
2206 throw setError(VBOX_E_FILE_ERROR,
2207 tr("Could not create or open file '%s' (%Rrc)"),
2208 strSrcFilePath.c_str(), vrc);
2209 }
2210
2211 uint64_t cbFile = 0;
2212 vrc = RTFileGetSize(pFile, &cbFile);
2213 if (RT_SUCCESS(vrc))
2214 {
2215 size_t const cbTmpSize = _1M;
2216 void *pvTmpBuf = RTMemAlloc(cbTmpSize);
2217 if (pvTmpBuf)
2218 {
2219 /* The copy loop. */
2220 uint64_t offDstFile = 0;
2221 for (;;)
2222 {
2223 size_t cbChunk = 0;
2224 vrc = RTFileRead(pFile, pvTmpBuf, cbTmpSize, &cbChunk);
2225 if (RT_FAILURE(vrc) || cbChunk == 0)
2226 break;
2227
2228 size_t cbWritten = 0;
2229 vrc = pIfIo->pfnWriteSync(pvUser,
2230 pvStorage,
2231 offDstFile,
2232 pvTmpBuf,
2233 cbChunk,
2234 &cbWritten);
2235 if (RT_FAILURE(vrc))
2236 break;
2237 Assert(cbWritten == cbChunk);
2238
2239 offDstFile += cbWritten;
2240 }
2241
2242 RTMemFree(pvTmpBuf);
2243 }
2244 else
2245 vrc = VERR_NO_MEMORY;
2246 }
2247
2248 pIfIo->pfnClose(pvUser, pvStorage);
2249 RTFileClose(pFile);
2250
2251 if (RT_FAILURE(vrc))
2252 {
2253 if (vrc == VERR_EOF)
2254 vrc = VINF_SUCCESS;
2255 else
2256 throw setError(VBOX_E_FILE_ERROR,
2257 tr("Error during copy CD/DVD image '%s' (%Rrc)"),
2258 strSrcFilePath.c_str(), vrc);
2259 }
2260 }
2261 }
2262 }
2263 catch (HRESULT rc3)
2264 {
2265 writeLock.acquire();
2266 // Todo: file deletion on error? If not, we can remove that whole try/catch block.
2267 throw rc3;
2268 }
2269 // Finished, lock again (so nobody mess around with the medium tree
2270 // in the meantime)
2271 writeLock.acquire();
2272 fileList.push_back(STRPAIR(strTargetFilePath, pStorage->strDigest));
2273 }
2274
2275 if (m->fManifest)
2276 {
2277 // Create & write the manifest file
2278 Utf8Str strMfFilePath = Utf8Str(pTask->locInfo.strPath).stripSuffix().append(".mf");
2279 Utf8Str strMfFileName = Utf8Str(strMfFilePath).stripPath();
2280 pTask->pProgress->SetNextOperation(BstrFmt(tr("Creating manifest file '%s'"), strMfFileName.c_str()).raw(),
2281 m->ulWeightForManifestOperation); // operation's weight, as set up
2282 // with the IProgress originally);
2283 PRTMANIFESTTEST paManifestFiles = (PRTMANIFESTTEST)RTMemAlloc(sizeof(RTMANIFESTTEST) * fileList.size());
2284 size_t i = 0;
2285 list<STRPAIR>::const_iterator it1;
2286 for (it1 = fileList.begin();
2287 it1 != fileList.end();
2288 ++it1, ++i)
2289 {
2290 paManifestFiles[i].pszTestFile = (*it1).first.c_str();
2291 paManifestFiles[i].pszTestDigest = (*it1).second.c_str();
2292 }
2293 void *pvBuf;
2294 size_t cbSize;
2295 vrc = RTManifestWriteFilesBuf(&pvBuf, &cbSize, m->fSha256 ? RTDIGESTTYPE_SHA256 : RTDIGESTTYPE_SHA1,
2296 paManifestFiles, fileList.size());
2297 RTMemFree(paManifestFiles);
2298 if (RT_FAILURE(vrc))
2299 throw setError(VBOX_E_FILE_ERROR,
2300 tr("Could not create manifest file '%s' (%Rrc)"),
2301 strMfFileName.c_str(), vrc);
2302 /* Disable digest creation for the manifest file. */
2303 pStorage->fCreateDigest = false;
2304 /* Write the manifest file to disk. */
2305 vrc = writeBufferToFile(strMfFilePath.c_str(), pvBuf, cbSize, pIfIo, pStorage);
2306 RTMemFree(pvBuf);
2307 if (RT_FAILURE(vrc))
2308 throw setError(VBOX_E_FILE_ERROR,
2309 tr("Could not create manifest file '%s' (%Rrc)"),
2310 strMfFilePath.c_str(), vrc);
2311 }
2312 }
2313 catch (RTCError &x) // includes all XML exceptions
2314 {
2315 rc = setError(VBOX_E_FILE_ERROR,
2316 x.what());
2317 }
2318 catch (HRESULT aRC)
2319 {
2320 rc = aRC;
2321 }
2322
2323 /* Cleanup on error */
2324 if (FAILED(rc))
2325 {
2326 list<STRPAIR>::const_iterator it1;
2327 for (it1 = fileList.begin();
2328 it1 != fileList.end();
2329 ++it1)
2330 pIfIo->pfnDelete(pStorage, (*it1).first.c_str());
2331 }
2332
2333 LogFlowFunc(("rc=%Rhrc\n", rc));
2334 LogFlowFuncLeave();
2335
2336 return rc;
2337}
2338
2339#ifdef VBOX_WITH_S3
2340/**
2341 * Worker code for writing out OVF to the cloud. This is called from Appliance::taskThreadWriteOVF()
2342 * in S3 mode and therefore runs on the OVF write worker thread. This then starts a second worker
2343 * thread to create temporary files (see Appliance::i_writeFS()).
2344 *
2345 * @param pTask
2346 * @return
2347 */
2348HRESULT Appliance::i_writeS3(TaskOVF *pTask)
2349{
2350 LogFlowFuncEnter();
2351 LogFlowFunc(("Appliance %p\n", this));
2352
2353 AutoCaller autoCaller(this);
2354 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2355
2356 HRESULT rc = S_OK;
2357
2358 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
2359
2360 int vrc = VINF_SUCCESS;
2361 RTS3 hS3 = NIL_RTS3;
2362 char szOSTmpDir[RTPATH_MAX];
2363 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));
2364 /* The template for the temporary directory created below */
2365 char *pszTmpDir = RTPathJoinA(szOSTmpDir, "vbox-ovf-XXXXXX");
2366 list< pair<Utf8Str, ULONG> > filesList;
2367
2368 // todo:
2369 // - usable error codes
2370 // - seems snapshot filenames are problematic {uuid}.vdi
2371 try
2372 {
2373 /* Extract the bucket */
2374 Utf8Str tmpPath = pTask->locInfo.strPath;
2375 Utf8Str bucket;
2376 i_parseBucket(tmpPath, bucket);
2377
2378 /* We need a temporary directory which we can put the OVF file & all
2379 * disk images in */
2380 vrc = RTDirCreateTemp(pszTmpDir, 0700);
2381 if (RT_FAILURE(vrc))
2382 throw setError(VBOX_E_FILE_ERROR,
2383 tr("Cannot create temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);
2384
2385 /* The temporary name of the target OVF file */
2386 Utf8StrFmt strTmpOvf("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str()));
2387
2388 /* Prepare the temporary writing of the OVF */
2389 ComObjPtr<Progress> progress;
2390 /* Create a temporary file based location info for the sub task */
2391 LocationInfo li;
2392 li.strPath = strTmpOvf;
2393 rc = i_writeImpl(pTask->enFormat, li, progress);
2394 if (FAILED(rc)) throw rc;
2395
2396 /* Unlock the appliance for the writing thread */
2397 appLock.release();
2398 /* Wait until the writing is done, but report the progress back to the
2399 caller */
2400 ComPtr<IProgress> progressInt(progress);
2401 i_waitForAsyncProgress(pTask->pProgress, progressInt); /* Any errors will be thrown */
2402
2403 /* Again lock the appliance for the next steps */
2404 appLock.acquire();
2405
2406 vrc = RTPathExists(strTmpOvf.c_str()); /* Paranoid check */
2407 if (RT_FAILURE(vrc))
2408 throw setError(VBOX_E_FILE_ERROR,
2409 tr("Cannot find source file '%s' (%Rrc)"), strTmpOvf.c_str(), vrc);
2410 /* Add the OVF file */
2411 filesList.push_back(pair<Utf8Str, ULONG>(strTmpOvf, m->ulWeightForXmlOperation)); /* Use 1% of the
2412 total for the OVF file upload */
2413 /* Add the manifest file */
2414 if (m->fManifest)
2415 {
2416 Utf8Str strMfFile = Utf8Str(strTmpOvf).stripSuffix().append(".mf");
2417 filesList.push_back(pair<Utf8Str, ULONG>(strMfFile , m->ulWeightForXmlOperation)); /* Use 1% of the total
2418 for the manifest file upload */
2419 }
2420
2421 /* Now add every disks of every virtual system */
2422 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
2423 for (it = m->virtualSystemDescriptions.begin();
2424 it != m->virtualSystemDescriptions.end();
2425 ++it)
2426 {
2427 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
2428 std::list<VirtualSystemDescriptionEntry*> avsdeHDs =
2429 vsdescThis->i_findByType(VirtualSystemDescriptionType_HardDiskImage);
2430 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
2431 for (itH = avsdeHDs.begin();
2432 itH != avsdeHDs.end();
2433 ++itH)
2434 {
2435 const Utf8Str &strTargetFileNameOnly = (*itH)->strOvf;
2436 /* Target path needs to be composed from where the output OVF is */
2437 Utf8Str strTargetFilePath(strTmpOvf);
2438 strTargetFilePath.stripFilename();
2439 strTargetFilePath.append("/");
2440 strTargetFilePath.append(strTargetFileNameOnly);
2441 vrc = RTPathExists(strTargetFilePath.c_str()); /* Paranoid check */
2442 if (RT_FAILURE(vrc))
2443 throw setError(VBOX_E_FILE_ERROR,
2444 tr("Cannot find source file '%s' (%Rrc)"), strTargetFilePath.c_str(), vrc);
2445 filesList.push_back(pair<Utf8Str, ULONG>(strTargetFilePath, (*itH)->ulSizeMB));
2446 }
2447 }
2448 /* Next we have to upload the OVF & all disk images */
2449 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(),
2450 pTask->locInfo.strHostname.c_str(), "virtualbox-agent/" VBOX_VERSION_STRING);
2451 if (RT_FAILURE(vrc))
2452 throw setError(VBOX_E_IPRT_ERROR,
2453 tr("Cannot create S3 service handler"));
2454 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask);
2455
2456 /* Upload all files */
2457 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
2458 {
2459 const pair<Utf8Str, ULONG> &s = (*it1);
2460 char *pszFilename = RTPathFilename(s.first.c_str());
2461 /* Advance to the next operation */
2462 pTask->pProgress->SetNextOperation(BstrFmt(tr("Uploading file '%s'"), pszFilename).raw(), s.second);
2463 vrc = RTS3PutKey(hS3, bucket.c_str(), pszFilename, s.first.c_str());
2464 if (RT_FAILURE(vrc))
2465 {
2466 if (vrc == VERR_S3_CANCELED)
2467 break;
2468 else if (vrc == VERR_S3_ACCESS_DENIED)
2469 throw setError(E_ACCESSDENIED,
2470 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);
2471 else if (vrc == VERR_S3_NOT_FOUND)
2472 throw setError(VBOX_E_FILE_ERROR,
2473 tr("Cannot upload file '%s' to S3 storage server (File not found)"), pszFilename);
2474 else
2475 throw setError(VBOX_E_IPRT_ERROR,
2476 tr("Cannot upload file '%s' to S3 storage server (%Rrc)"), pszFilename, vrc);
2477 }
2478 }
2479 }
2480 catch(HRESULT aRC)
2481 {
2482 rc = aRC;
2483 }
2484 /* Cleanup */
2485 RTS3Destroy(hS3);
2486 /* Delete all files which where temporary created */
2487 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
2488 {
2489 const char *pszFilePath = (*it1).first.c_str();
2490 if (RTPathExists(pszFilePath))
2491 {
2492 vrc = RTFileDelete(pszFilePath);
2493 if (RT_FAILURE(vrc))
2494 rc = setError(VBOX_E_FILE_ERROR,
2495 tr("Cannot delete file '%s' (%Rrc)"), pszFilePath, vrc);
2496 }
2497 }
2498 /* Delete the temporary directory */
2499 if (RTPathExists(pszTmpDir))
2500 {
2501 vrc = RTDirRemove(pszTmpDir);
2502 if (RT_FAILURE(vrc))
2503 rc = setError(VBOX_E_FILE_ERROR,
2504 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);
2505 }
2506 if (pszTmpDir)
2507 RTStrFree(pszTmpDir);
2508
2509 LogFlowFunc(("rc=%Rhrc\n", rc));
2510 LogFlowFuncLeave();
2511
2512 return rc;
2513}
2514#endif /* VBOX_WITH_S3 */
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