VirtualBox

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

Last change on this file since 49386 was 49103, checked in by vboxsync, 11 years ago

pr6927. ISO images are skipped by default during export. The command "VBoxManage export" has a new argument "--iso" to explicitly export ISO images.

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