VirtualBox

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

Last change on this file since 95512 was 95423, checked in by vboxsync, 3 years ago

Audio/Main: Bigger revamp of the audio interface(s) to later also support host audio device enumeration and selection for individual VMs. The audio settings now live in a dedicated (per-VM) IAudioSettings interface (audio adapter + audio host device stuff), to further tidy up the IMachine interface. Also added stubs for IAudioDevice + IHostAudioDevice, plus enmuerations, left for further implementation. Added a new IHostAudioDeviceChangedEvent that can also be used later by API clients. bugref:10050

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

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