VirtualBox

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

Last change on this file since 82941 was 81682, checked in by vboxsync, 5 years ago

Main/Console+ConsoleVRDPServer+ApplianceExport+Host+Machine+USBDeviceFilters: fix missing RT_NOREF(...) if VBOX_WITH_USB is undefined

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