VirtualBox

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

Last change on this file since 67209 was 67209, checked in by vboxsync, 8 years ago

Main: Appliance: todo

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

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