VirtualBox

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

Last change on this file since 55747 was 55505, checked in by vboxsync, 10 years ago

Main: Add method to return list of medium identifiers assoicated with a given password ID. Required by the GUI to verify entered passwords during appliance export

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