VirtualBox

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

Last change on this file since 75839 was 75766, checked in by vboxsync, 6 years ago

bugref:9152. Replaced the word "oci" by "cloud" in the names in the Appliance code.

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

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