1224
1328
if (!biosSettings)
1225
1329
return E_POINTER;
1227
AutoCaller autoCaller (this);
1228
CheckComRCReturnRC (autoCaller.rc());
1331
AutoCaller autoCaller(this);
1332
CheckComRCReturnRC(autoCaller.rc());
1230
1334
/* mBIOSSettings is constant during life time, no need to lock */
1231
mBIOSSettings.queryInterfaceTo (biosSettings);
1236
STDMETHODIMP Machine::COMGETTER(HWVirtExEnabled)(BOOL *enabled)
1241
AutoCaller autoCaller (this);
1242
CheckComRCReturnRC (autoCaller.rc());
1244
AutoReadLock alock (this);
1246
*enabled = mHWData->mHWVirtExEnabled;
1251
STDMETHODIMP Machine::COMSETTER(HWVirtExEnabled)(BOOL enable)
1253
AutoCaller autoCaller (this);
1254
CheckComRCReturnRC (autoCaller.rc());
1256
AutoWriteLock alock (this);
1258
HRESULT rc = checkStateDependency (MutableStateDep);
1259
CheckComRCReturnRC (rc);
1261
/** @todo check validity! */
1264
mHWData->mHWVirtExEnabled = enable;
1269
STDMETHODIMP Machine::COMGETTER(HWVirtExNestedPagingEnabled)(BOOL *enabled)
1274
AutoCaller autoCaller (this);
1275
CheckComRCReturnRC (autoCaller.rc());
1277
AutoReadLock alock (this);
1279
*enabled = mHWData->mHWVirtExNestedPagingEnabled;
1284
STDMETHODIMP Machine::COMSETTER(HWVirtExNestedPagingEnabled)(BOOL enable)
1286
AutoCaller autoCaller (this);
1287
CheckComRCReturnRC (autoCaller.rc());
1289
AutoWriteLock alock (this);
1291
HRESULT rc = checkStateDependency (MutableStateDep);
1292
CheckComRCReturnRC (rc);
1294
/** @todo check validity! */
1297
mHWData->mHWVirtExNestedPagingEnabled = enable;
1302
STDMETHODIMP Machine::COMGETTER(HWVirtExVPIDEnabled)(BOOL *enabled)
1307
AutoCaller autoCaller (this);
1308
CheckComRCReturnRC (autoCaller.rc());
1310
AutoReadLock alock (this);
1312
*enabled = mHWData->mHWVirtExVPIDEnabled;
1317
STDMETHODIMP Machine::COMSETTER(HWVirtExVPIDEnabled)(BOOL enable)
1319
AutoCaller autoCaller (this);
1320
CheckComRCReturnRC (autoCaller.rc());
1322
AutoWriteLock alock (this);
1324
HRESULT rc = checkStateDependency (MutableStateDep);
1325
CheckComRCReturnRC (rc);
1327
/** @todo check validity! */
1330
mHWData->mHWVirtExVPIDEnabled = enable;
1335
STDMETHODIMP Machine::COMGETTER(PAEEnabled)(BOOL *enabled)
1340
AutoCaller autoCaller (this);
1341
CheckComRCReturnRC (autoCaller.rc());
1343
AutoReadLock alock (this);
1345
*enabled = mHWData->mPAEEnabled;
1350
STDMETHODIMP Machine::COMSETTER(PAEEnabled)(BOOL enable)
1352
AutoCaller autoCaller (this);
1353
CheckComRCReturnRC (autoCaller.rc());
1355
AutoWriteLock alock (this);
1357
HRESULT rc = checkStateDependency (MutableStateDep);
1358
CheckComRCReturnRC (rc);
1360
/** @todo check validity! */
1363
mHWData->mPAEEnabled = enable;
1335
mBIOSSettings.queryInterfaceTo(biosSettings);
1340
STDMETHODIMP Machine::GetCpuProperty(CpuPropertyType_T property, BOOL *aVal)
1345
AutoCaller autoCaller(this);
1346
CheckComRCReturnRC(autoCaller.rc());
1348
AutoReadLock alock(this);
1352
case CpuPropertyType_PAE:
1353
*aVal = mHWData->mPAEEnabled;
1356
case CpuPropertyType_Synthetic:
1357
*aVal = mHWData->mSyntheticCpu;
1361
return E_INVALIDARG;
1366
STDMETHODIMP Machine::SetCpuProperty(CpuPropertyType_T property, BOOL aVal)
1368
AutoCaller autoCaller(this);
1369
CheckComRCReturnRC(autoCaller.rc());
1371
AutoWriteLock alock(this);
1373
HRESULT rc = checkStateDependency(MutableStateDep);
1374
CheckComRCReturnRC(rc);
1378
case CpuPropertyType_PAE:
1379
mHWData->mPAEEnabled = !!aVal;
1382
case CpuPropertyType_Synthetic:
1383
mHWData->mSyntheticCpu = !!aVal;
1387
return E_INVALIDARG;
1392
STDMETHODIMP Machine::GetCpuIdLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
1394
CheckComArgOutPointerValid(aValEax);
1395
CheckComArgOutPointerValid(aValEbx);
1396
CheckComArgOutPointerValid(aValEcx);
1397
CheckComArgOutPointerValid(aValEdx);
1399
AutoCaller autoCaller(this);
1400
CheckComRCReturnRC(autoCaller.rc());
1402
AutoReadLock alock(this);
1417
if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
1418
return setError(E_INVALIDARG, tr("CpuId override leaf %#x is not set"), aId);
1420
*aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
1421
*aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
1422
*aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
1423
*aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
1437
if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
1438
return setError(E_INVALIDARG, tr("CpuId override leaf %#x is not set"), aId);
1440
*aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
1441
*aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
1442
*aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
1443
*aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
1447
return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
1452
STDMETHODIMP Machine::SetCpuIdLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
1454
AutoCaller autoCaller(this);
1455
CheckComRCReturnRC(autoCaller.rc());
1457
AutoWriteLock alock(this);
1459
HRESULT rc = checkStateDependency(MutableStateDep);
1460
CheckComRCReturnRC(rc);
1475
AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xA);
1476
AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
1477
mHWData->mCpuIdStdLeafs[aId].ulId = aId;
1478
mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
1479
mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
1480
mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
1481
mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
1495
AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xA);
1496
AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
1497
mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
1498
mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
1499
mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
1500
mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
1501
mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
1505
return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
1510
STDMETHODIMP Machine::RemoveCpuIdLeaf(ULONG aId)
1512
AutoCaller autoCaller(this);
1513
CheckComRCReturnRC(autoCaller.rc());
1515
AutoWriteLock alock(this);
1517
HRESULT rc = checkStateDependency(MutableStateDep);
1518
CheckComRCReturnRC(rc);
1533
AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xA);
1534
AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
1535
/* Invalidate leaf. */
1536
mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
1550
AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xA);
1551
AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
1552
/* Invalidate leaf. */
1553
mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
1557
return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
1562
STDMETHODIMP Machine::RemoveAllCpuIdLeafs()
1564
AutoCaller autoCaller(this);
1565
CheckComRCReturnRC(autoCaller.rc());
1567
AutoWriteLock alock(this);
1569
HRESULT rc = checkStateDependency(MutableStateDep);
1570
CheckComRCReturnRC(rc);
1572
/* Invalidate all standard leafs. */
1573
for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
1574
mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
1576
/* Invalidate all extended leafs. */
1577
for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
1578
mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
1583
STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
1588
AutoCaller autoCaller(this);
1589
CheckComRCReturnRC(autoCaller.rc());
1591
AutoReadLock alock(this);
1595
case HWVirtExPropertyType_Enabled:
1596
*aVal = mHWData->mHWVirtExEnabled;
1599
case HWVirtExPropertyType_Exclusive:
1600
*aVal = mHWData->mHWVirtExExclusive;
1603
case HWVirtExPropertyType_VPID:
1604
*aVal = mHWData->mHWVirtExVPIDEnabled;
1607
case HWVirtExPropertyType_NestedPaging:
1608
*aVal = mHWData->mHWVirtExNestedPagingEnabled;
1612
return E_INVALIDARG;
1617
STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
1619
AutoCaller autoCaller(this);
1620
CheckComRCReturnRC(autoCaller.rc());
1622
AutoWriteLock alock(this);
1624
HRESULT rc = checkStateDependency(MutableStateDep);
1625
CheckComRCReturnRC(rc);
1629
case HWVirtExPropertyType_Enabled:
1631
mHWData->mHWVirtExEnabled = !!aVal;
1634
case HWVirtExPropertyType_Exclusive:
1636
mHWData->mHWVirtExExclusive = !!aVal;
1639
case HWVirtExPropertyType_VPID:
1641
mHWData->mHWVirtExVPIDEnabled = !!aVal;
1644
case HWVirtExPropertyType_NestedPaging:
1646
mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
1650
return E_INVALIDARG;
1368
1655
STDMETHODIMP Machine::COMGETTER(SnapshotFolder) (BSTR *aSnapshotFolder)
1370
CheckComArgOutPointerValid (aSnapshotFolder);
1372
AutoCaller autoCaller (this);
1373
CheckComRCReturnRC (autoCaller.rc());
1375
AutoReadLock alock (this);
1377
mUserData->mSnapshotFolderFull.cloneTo (aSnapshotFolder);
1657
CheckComArgOutPointerValid(aSnapshotFolder);
1659
AutoCaller autoCaller(this);
1660
CheckComRCReturnRC(autoCaller.rc());
1662
AutoReadLock alock(this);
1664
mUserData->mSnapshotFolderFull.cloneTo(aSnapshotFolder);
1938
2337
|| (aDevice < 0)
1939
2338
|| (aDevice >= (LONG)devicesPerPort)
1941
return setError (E_INVALIDARG,
1942
tr ("The port and/or count parameter are out of range [%lu:%lu]"),
1943
portCount, devicesPerPort);
2340
return setError(E_INVALIDARG,
2341
tr("The port and/or count parameter are out of range [%lu:%lu]"),
1945
2345
/* check if the device slot is already busy */
1946
HDData::AttachmentList::const_iterator it =
1947
std::find_if (mHDData->mAttachments.begin(),
1948
mHDData->mAttachments.end(),
1949
HardDiskAttachment::EqualsTo (aControllerName, aControllerPort, aDevice));
1951
if (it != mHDData->mAttachments.end())
1953
ComObjPtr<HardDisk> hd = (*it)->hardDisk();
1954
AutoReadLock hdLock (hd);
1955
return setError (VBOX_E_OBJECT_IN_USE,
1956
tr ("Hard disk '%ls' is already attached to device slot %d on "
1957
"port %d of controller '%ls' of this virtual machine"),
1958
hd->locationFull().raw(), aDevice, aControllerPort, aControllerName);
1963
/* find a hard disk by UUID */
1964
ComObjPtr<HardDisk> hd;
1965
rc = mParent->findHardDisk(&id, NULL, true /* aSetError */, &hd);
1966
CheckComRCReturnRC (rc);
1968
AutoCaller hdCaller (hd);
1969
CheckComRCReturnRC (hdCaller.rc());
1971
AutoWriteLock hdLock (hd);
1973
if (std::find_if (mHDData->mAttachments.begin(),
1974
mHDData->mAttachments.end(),
1975
HardDiskAttachment::RefersTo (hd)) !=
1976
mHDData->mAttachments.end())
1978
return setError (VBOX_E_OBJECT_IN_USE,
1979
tr ("Hard disk '%ls' is already attached to this virtual machine"),
1980
hd->locationFull().raw());
1983
bool indirect = hd->isReadOnly();
2346
MediumAttachment *pAttachTemp;
2347
if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
2352
Medium *pMedium = pAttachTemp->getMedium();
2355
AutoReadLock mediumLock(pMedium);
2356
return setError(VBOX_E_OBJECT_IN_USE,
2357
tr("Medium '%s' is already attached to device slot %d on port %d of controller '%ls' of this virtual machine"),
2358
pMedium->getLocationFull().raw(),
2364
return setError(VBOX_E_OBJECT_IN_USE,
2365
tr("Device is already attached to slot %d on port %d of controller '%ls' of this virtual machine"),
2366
aDevice, aControllerPort, aControllerName);
2371
ComObjPtr<Medium> medium;
2374
case DeviceType_HardDisk:
2375
/* find a hard disk by UUID */
2376
rc = mParent->findHardDisk(&uuid, NULL, true /* aSetError */, &medium);
2377
CheckComRCReturnRC(rc);
2380
case DeviceType_DVD:
2381
if (!uuid.isEmpty())
2383
/* first search for host drive */
2384
SafeIfaceArray<IMedium> drivevec;
2385
rc = mParent->host()->COMGETTER(DVDDrives)(ComSafeArrayAsOutParam(drivevec));
2388
for (size_t i = 0; i < drivevec.size(); ++i)
2390
/// @todo eliminate this conversion
2391
ComObjPtr<Medium> med = (Medium *)drivevec[i];
2392
if (med->getId() == uuid)
2400
if (medium.isNull())
2402
/* find a DVD image by UUID */
2403
rc = mParent->findDVDImage(&uuid, NULL, true /* aSetError */, &medium);
2404
CheckComRCReturnRC(rc);
2409
/* null UUID means null medium, which needs no code */
2413
case DeviceType_Floppy:
2414
if (!uuid.isEmpty())
2416
/* first search for host drive */
2417
SafeIfaceArray<IMedium> drivevec;
2418
rc = mParent->host()->COMGETTER(FloppyDrives)(ComSafeArrayAsOutParam(drivevec));
2421
for (size_t i = 0; i < drivevec.size(); ++i)
2423
/// @todo eliminate this conversion
2424
ComObjPtr<Medium> med = (Medium *)drivevec[i];
2425
if (med->getId() == uuid)
2433
if (medium.isNull())
2435
/* find a floppy image by UUID */
2436
rc = mParent->findFloppyImage(&uuid, NULL, true /* aSetError */, &medium);
2437
CheckComRCReturnRC(rc);
2442
/* null UUID means null medium, which needs no code */
2447
return setError(E_INVALIDARG,
2448
tr("The device type %d is not recognized"),
2452
AutoCaller mediumCaller(medium);
2453
CheckComRCReturnRC(mediumCaller.rc());
2455
AutoWriteLock mediumLock(medium);
2457
if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
2458
&& !medium.isNull())
2460
return setError(VBOX_E_OBJECT_IN_USE,
2461
tr("Medium '%s' is already attached to this virtual machine"),
2462
medium->getLocationFull().raw());
2465
bool indirect = false;
2466
if (!medium.isNull())
2467
indirect = medium->isReadOnly();
1984
2468
bool associate = true;
1988
if (mHDData.isBackedUp())
2472
if (aType == DeviceType_HardDisk && mMediaData.isBackedUp())
1990
const HDData::AttachmentList &oldAtts =
1991
mHDData.backedUpData()->mAttachments;
2474
const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
1993
/* check if the hard disk was attached to the VM before we started
1994
* changing attachemnts in which case the attachment just needs to
2476
/* check if the medium was attached to the VM before we started
2477
* changing attachments in which case the attachment just needs to
1995
2478
* be restored */
1996
HDData::AttachmentList::const_iterator it =
1997
std::find_if (oldAtts.begin(), oldAtts.end(),
1998
HardDiskAttachment::RefersTo (hd));
1999
if (it != oldAtts.end())
2479
if ((pAttachTemp = findAttachment(oldAtts, medium)))
2001
AssertReturn (!indirect, E_FAIL);
2481
AssertReturn(!indirect, E_FAIL);
2003
2483
/* see if it's the same bus/channel/device */
2004
if ((*it)->device() == aDevice &&
2005
(*it)->port() == aControllerPort &&
2006
(*it)->controller() == aControllerName)
2484
if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
2008
2486
/* the simplest case: restore the whole attachment
2009
* and return, nothing else to do */
2010
mHDData->mAttachments.push_back (*it);
2487
* and return, nothing else to do */
2488
mMediaData->mAttachments.push_back(pAttachTemp);
2014
2492
/* bus/channel/device differ; we need a new attachment object,
2015
* but don't try to associate it again */
2493
* but don't try to associate it again */
2016
2494
associate = false;
2148
2634
/* found a suitable diff, use it as a base */
2149
2635
if (!base.isNull())
2152
hdCaller.attach (hd);
2153
CheckComRCReturnRC (hdCaller.rc());
2638
mediumCaller.attach(medium);
2639
CheckComRCReturnRC(mediumCaller.rc());
2640
mediumLock.attach(medium);
2158
ComObjPtr<HardDisk> diff;
2644
ComObjPtr<Medium> diff;
2159
2645
diff.createObject();
2160
2646
rc = diff->init(mParent,
2161
hd->preferredDiffFormat(),
2162
BstrFmt ("%ls"RTPATH_SLASH_STR,
2163
mUserData->mSnapshotFolderFull.raw()));
2164
CheckComRCReturnRC (rc);
2647
medium->preferredDiffFormat().raw(),
2648
BstrFmt("%ls"RTPATH_SLASH_STR,
2649
mUserData->mSnapshotFolderFull.raw()).raw());
2650
CheckComRCReturnRC(rc);
2166
2652
/* make sure the hard disk is not modified before createDiffStorage() */
2167
rc = hd->LockRead (NULL);
2168
CheckComRCReturnRC (rc);
2653
rc = medium->LockRead(NULL);
2654
CheckComRCReturnRC(rc);
2170
2656
/* will leave the lock before the potentially lengthy operation, so
2171
2657
* protect with the special state */
2172
2658
MachineState_T oldState = mData->mMachineState;
2173
setMachineState (MachineState_SettingUp);
2659
setMachineState(MachineState_SettingUp);
2179
rc = hd->createDiffStorageAndWait (diff, HardDiskVariant_Standard);
2664
rc = medium->createDiffStorageAndWait(diff, MediumVariant_Standard);
2184
setMachineState (oldState);
2186
hd->UnlockRead (NULL);
2188
CheckComRCReturnRC (rc);
2669
setMachineState(oldState);
2671
medium->UnlockRead(NULL);
2673
CheckComRCReturnRC(rc);
2190
2675
/* use the created diff for the actual attachment */
2192
hdCaller.attach (hd);
2193
CheckComRCReturnRC (hdCaller.rc());
2677
mediumCaller.attach(medium);
2678
CheckComRCReturnRC(mediumCaller.rc());
2679
mediumLock.attach(medium);
2198
ComObjPtr<HardDiskAttachment> attachment;
2683
ComObjPtr<MediumAttachment> attachment;
2199
2684
attachment.createObject();
2200
rc = attachment->init (hd, aControllerName, aControllerPort, aDevice, indirect);
2201
CheckComRCReturnRC (rc);
2685
rc = attachment->init(this, medium, aControllerName, aControllerPort, aDevice, aType, indirect);
2686
CheckComRCReturnRC(rc);
2688
if (associate && !medium.isNull())
2205
/* as the last step, associate the hard disk to the VM */
2206
rc = hd->attachTo (mData->mUuid);
2690
/* as the last step, associate the medium to the VM */
2691
rc = medium->attachTo(mData->mUuid);
2207
2692
/* here we can fail because of Deleting, or being in process of
2208
2693
* creating a Diff */
2209
CheckComRCReturnRC (rc);
2694
CheckComRCReturnRC(rc);
2212
/* sucsess: finally remember the attachment */
2214
mHDData->mAttachments.push_back (attachment);
2697
/* success: finally remember the attachment */
2698
mMediaData.backup();
2699
mMediaData->mAttachments.push_back(attachment);
2219
STDMETHODIMP Machine::GetHardDisk(IN_BSTR aControllerName, LONG aControllerPort,
2220
LONG aDevice, IHardDisk **aHardDisk)
2222
LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
2223
aControllerName, aControllerPort, aDevice));
2225
CheckComArgNotNull (aControllerName);
2226
CheckComArgOutPointerValid (aHardDisk);
2228
AutoCaller autoCaller (this);
2229
CheckComRCReturnRC (autoCaller.rc());
2231
AutoReadLock alock (this);
2235
HDData::AttachmentList::const_iterator it =
2236
std::find_if (mHDData->mAttachments.begin(),
2237
mHDData->mAttachments.end(),
2238
HardDiskAttachment::EqualsTo (aControllerName, aControllerPort, aDevice));
2240
if (it == mHDData->mAttachments.end())
2241
return setError (VBOX_E_OBJECT_NOT_FOUND,
2242
tr ("No hard disk attached to device slot %d on port %d of controller '%ls'"),
2243
aDevice, aControllerPort, aControllerName);
2245
(*it)->hardDisk().queryInterfaceTo (aHardDisk);
2250
STDMETHODIMP Machine::DetachHardDisk(IN_BSTR aControllerName, LONG aControllerPort,
2253
CheckComArgNotNull (aControllerName);
2255
LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
2256
aControllerName, aControllerPort, aDevice));
2258
AutoCaller autoCaller (this);
2259
CheckComRCReturnRC (autoCaller.rc());
2261
AutoWriteLock alock (this);
2263
HRESULT rc = checkStateDependency (MutableStateDep);
2264
CheckComRCReturnRC (rc);
2266
AssertReturn (mData->mMachineState != MachineState_Saved, E_FAIL);
2268
if (Global::IsOnlineOrTransient (mData->mMachineState))
2269
return setError (VBOX_E_INVALID_VM_STATE,
2270
tr ("Invalid machine state: %d"), mData->mMachineState);
2272
HDData::AttachmentList::const_iterator it =
2273
std::find_if (mHDData->mAttachments.begin(),
2274
mHDData->mAttachments.end(),
2275
HardDiskAttachment::EqualsTo (aControllerName, aControllerPort, aDevice));
2277
if (it == mHDData->mAttachments.end())
2278
return setError (VBOX_E_OBJECT_NOT_FOUND,
2279
tr ("No hard disk attached to device slot %d on port %d of controller '%ls'"),
2280
aDevice, aControllerPort, aControllerName);
2282
ComObjPtr<HardDiskAttachment> hda = *it;
2283
ComObjPtr<HardDisk> hd = hda->hardDisk();
2285
if (hda->isImplicit())
2704
STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
2707
CheckComArgNotNull(aControllerName);
2709
LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
2710
aControllerName, aControllerPort, aDevice));
2712
AutoCaller autoCaller(this);
2713
CheckComRCReturnRC(autoCaller.rc());
2715
AutoWriteLock alock(this);
2717
HRESULT rc = checkStateDependency(MutableStateDep);
2718
CheckComRCReturnRC(rc);
2720
AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
2722
if (Global::IsOnlineOrTransient(mData->mMachineState))
2723
return setError(VBOX_E_INVALID_VM_STATE,
2724
tr("Invalid machine state: %s"),
2725
Global::stringifyMachineState(mData->mMachineState));
2727
MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
2732
return setError(VBOX_E_OBJECT_NOT_FOUND,
2733
tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
2734
aDevice, aControllerPort, aControllerName);
2736
ComObjPtr<Medium> oldmedium = pAttach->getMedium();
2737
DeviceType_T mediumType = pAttach->getType();
2739
if (pAttach->isImplicit())
2287
2741
/* attempt to implicitly delete the implicitly created diff */
2289
/// @todo move the implicit flag from HardDiskAttachment to HardDisk
2743
/// @todo move the implicit flag from MediumAttachment to Medium
2290
2744
/// and forbid any hard disk operation when it is implicit. Or maybe
2291
2745
/// a special media state for it to make it even more simple.
2293
Assert (mHDData.isBackedUp());
2747
Assert(mMediaData.isBackedUp());
2295
2749
/* will leave the lock before the potentially lengthy operation, so
2296
2750
* protect with the special state */
2297
2751
MachineState_T oldState = mData->mMachineState;
2298
setMachineState (MachineState_SettingUp);
2752
setMachineState(MachineState_SettingUp);
2302
rc = hd->deleteStorageAndWait();
2756
rc = oldmedium->deleteStorageAndWait();
2306
setMachineState (oldState);
2760
setMachineState(oldState);
2308
CheckComRCReturnRC (rc);
2762
CheckComRCReturnRC(rc);
2765
mMediaData.backup();
2313
2767
/* we cannot use erase (it) below because backup() above will create
2314
2768
* a copy of the list and make this copy active, but the iterator
2315
2769
* still refers to the original and is not valid for the copy */
2316
mHDData->mAttachments.remove (hda);
2770
mMediaData->mAttachments.remove(pAttach);
2772
/* For non-hard disk media, detach straight away. */
2773
if (mediumType != DeviceType_HardDisk && !oldmedium.isNull())
2774
oldmedium->detachFrom(mData->mUuid);
2779
STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
2780
LONG aDevice, BOOL aPassthrough)
2782
CheckComArgNotNull(aControllerName);
2784
LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld aPassthrough=%d\n",
2785
aControllerName, aControllerPort, aDevice, aPassthrough));
2787
AutoCaller autoCaller(this);
2788
CheckComRCReturnRC(autoCaller.rc());
2790
AutoWriteLock alock(this);
2792
HRESULT rc = checkStateDependency(MutableStateDep);
2793
CheckComRCReturnRC(rc);
2795
AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
2797
if (Global::IsOnlineOrTransient(mData->mMachineState))
2798
return setError(VBOX_E_INVALID_VM_STATE,
2799
tr("Invalid machine state: %s"),
2800
Global::stringifyMachineState(mData->mMachineState));
2802
MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
2807
return setError(VBOX_E_OBJECT_NOT_FOUND,
2808
tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
2809
aDevice, aControllerPort, aControllerName);
2812
mMediaData.backup();
2814
AutoWriteLock attLock(pAttach);
2816
if (pAttach->getType() != DeviceType_DVD)
2817
return setError(E_INVALIDARG,
2818
tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
2819
aDevice, aControllerPort, aControllerName);
2820
pAttach->updatePassthrough(!!aPassthrough);
2825
STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
2826
LONG aControllerPort,
2832
LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld aForce=%d\n",
2833
aControllerName, aControllerPort, aDevice, aForce));
2835
CheckComArgNotNull(aControllerName);
2836
CheckComArgNotNull(aId);
2838
AutoCaller autoCaller(this);
2839
CheckComRCReturnRC(autoCaller.rc());
2841
AutoWriteLock alock(this);
2843
ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
2847
if (pAttach.isNull())
2848
return setError(VBOX_E_OBJECT_NOT_FOUND,
2849
tr("No drive attached to device slot %d on port %d of controller '%ls'"),
2850
aDevice, aControllerPort, aControllerName);
2852
/* Remember previously mounted medium. The medium before taking the
2853
* backup is not necessarily the same thing. */
2854
ComObjPtr<Medium> oldmedium;
2855
oldmedium = pAttach->getMedium();
2858
ComObjPtr<Medium> medium;
2859
DeviceType_T mediumType = pAttach->getType();
2862
case DeviceType_DVD:
2863
if (!uuid.isEmpty())
2865
/* find a DVD by host device UUID */
2866
SafeIfaceArray<IMedium> drivevec;
2867
rc = mParent->host()->COMGETTER(DVDDrives)(ComSafeArrayAsOutParam(drivevec));
2870
for (size_t i = 0; i < drivevec.size(); ++i)
2872
/// @todo eliminate this conversion
2873
ComObjPtr<Medium> med = (Medium *)drivevec[i];
2874
if (uuid == med->getId())
2881
/* find a DVD by UUID */
2882
if (medium.isNull())
2883
rc = mParent->findDVDImage(&uuid, NULL, true /* aDoSetError */, &medium);
2885
CheckComRCReturnRC(rc);
2887
case DeviceType_Floppy:
2888
if (!uuid.isEmpty())
2890
/* find a Floppy by host device UUID */
2891
SafeIfaceArray<IMedium> drivevec;
2892
rc = mParent->host()->COMGETTER(FloppyDrives)(ComSafeArrayAsOutParam(drivevec));
2895
for (size_t i = 0; i < drivevec.size(); ++i)
2897
/// @todo eliminate this conversion
2898
ComObjPtr<Medium> med = (Medium *)drivevec[i];
2899
if (uuid == med->getId())
2906
/* find a Floppy by UUID */
2907
if (medium.isNull())
2908
rc = mParent->findFloppyImage(&uuid, NULL, true /* aDoSetError */, &medium);
2910
CheckComRCReturnRC(rc);
2913
return setError(VBOX_E_INVALID_OBJECT_STATE,
2914
tr("Cannot change medium attached to device slot %d on port %d of controller '%ls'"),
2915
aDevice, aControllerPort, aControllerName);
2921
mMediaData.backup();
2922
/* The backup operation makes the pAttach reference point to the
2923
* old settings. Re-get the correct reference. */
2924
pAttach = findAttachment(mMediaData->mAttachments,
2928
AutoWriteLock attLock(pAttach);
2929
/* For non-hard disk media, detach straight away. */
2930
if (mediumType != DeviceType_HardDisk && !oldmedium.isNull())
2931
oldmedium->detachFrom(mData->mUuid);
2932
if (!medium.isNull())
2933
medium->attachTo(mData->mUuid);
2934
pAttach->updateMedium(medium, false /* aImplicit */);
2938
rc = onMediumChange(pAttach, aForce);
2941
/* On error roll back this change only. */
2944
if (!medium.isNull())
2945
medium->detachFrom(mData->mUuid);
2946
pAttach = findAttachment(mMediaData->mAttachments,
2950
/* If the attachment is gone in the mean time, bail out. */
2951
if (pAttach.isNull())
2953
AutoWriteLock attLock(pAttach);
2954
/* For non-hard disk media, re-attach straight away. */
2955
if (mediumType != DeviceType_HardDisk && !oldmedium.isNull())
2956
oldmedium->attachTo(mData->mUuid);
2957
pAttach->updateMedium(oldmedium, false /* aImplicit */);
2963
STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
2964
LONG aControllerPort,
2968
LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
2969
aControllerName, aControllerPort, aDevice));
2971
CheckComArgNotNull(aControllerName);
2972
CheckComArgOutPointerValid(aMedium);
2974
AutoCaller autoCaller(this);
2975
CheckComRCReturnRC(autoCaller.rc());
2977
AutoReadLock alock(this);
2981
ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
2985
if (pAttach.isNull())
2986
return setError(VBOX_E_OBJECT_NOT_FOUND,
2987
tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
2988
aDevice, aControllerPort, aControllerName);
2990
pAttach->getMedium().queryInterfaceTo(aMedium);
2321
2995
STDMETHODIMP Machine::GetSerialPort (ULONG slot, ISerialPort **port)
2323
CheckComArgOutPointerValid (port);
2997
CheckComArgOutPointerValid(port);
2324
2998
CheckComArgExpr (slot, slot < RT_ELEMENTS (mSerialPorts));
2326
AutoCaller autoCaller (this);
2327
CheckComRCReturnRC (autoCaller.rc());
2329
AutoReadLock alock (this);
2331
mSerialPorts [slot].queryInterfaceTo (port);
3000
AutoCaller autoCaller(this);
3001
CheckComRCReturnRC(autoCaller.rc());
3003
AutoReadLock alock(this);
3005
mSerialPorts [slot].queryInterfaceTo(port);
2336
3010
STDMETHODIMP Machine::GetParallelPort (ULONG slot, IParallelPort **port)
2338
CheckComArgOutPointerValid (port);
3012
CheckComArgOutPointerValid(port);
2339
3013
CheckComArgExpr (slot, slot < RT_ELEMENTS (mParallelPorts));
2341
AutoCaller autoCaller (this);
2342
CheckComRCReturnRC (autoCaller.rc());
2344
AutoReadLock alock (this);
2346
mParallelPorts [slot].queryInterfaceTo (port);
3015
AutoCaller autoCaller(this);
3016
CheckComRCReturnRC(autoCaller.rc());
3018
AutoReadLock alock(this);
3020
mParallelPorts [slot].queryInterfaceTo(port);
2351
3025
STDMETHODIMP Machine::GetNetworkAdapter (ULONG slot, INetworkAdapter **adapter)
2353
CheckComArgOutPointerValid (adapter);
3027
CheckComArgOutPointerValid(adapter);
2354
3028
CheckComArgExpr (slot, slot < RT_ELEMENTS (mNetworkAdapters));
2356
AutoCaller autoCaller (this);
2357
CheckComRCReturnRC (autoCaller.rc());
2359
AutoReadLock alock (this);
2361
mNetworkAdapters [slot].queryInterfaceTo (adapter);
2367
* @note Locks this object for reading.
2369
STDMETHODIMP Machine::GetNextExtraDataKey (IN_BSTR aKey, BSTR *aNextKey, BSTR *aNextValue)
2371
CheckComArgOutPointerValid (aNextKey);
2373
AutoCaller autoCaller (this);
2374
CheckComRCReturnRC (autoCaller.rc());
2376
/* start with nothing found */
2377
Bstr("").cloneTo(aNextKey);
2379
Bstr("").cloneTo(aNextValue);
2381
/* if we're ready and isConfigLocked() is FALSE then it means
2382
* that no config file exists yet, so return shortly */
2383
if (!isConfigLocked())
2388
Bstr bstrInKey(aKey);
2390
/* serialize file access (prevent writes) */
2391
AutoReadLock alock (this);
2395
using namespace settings;
2396
using namespace xml;
2398
/* load the settings file (we don't reuse the existing handle but
2399
* request a new one to allow for concurrent multithreaded reads) */
2400
File file (File::Mode_Read, Utf8Str (mData->mConfigFileFull));
2401
XmlTreeBackend tree;
2403
rc = VirtualBox::loadSettingsTree_Again (tree, file);
2404
CheckComRCReturnRC (rc);
2406
Key machineNode = tree.rootKey().key ("Machine");
2407
Key extraDataNode = machineNode.findKey ("ExtraData");
2409
if (!extraDataNode.isNull())
2411
Key::List items = extraDataNode.keys ("ExtraDataItem");
2414
for (Key::List::const_iterator it = items.begin();
2415
it != items.end(); ++ it)
2417
Bstr key = (*it).stringValue ("name");
2419
/* if we're supposed to return the first one */
2420
if (bstrInKey.isEmpty())
2422
key.cloneTo (aNextKey);
2425
Bstr val = (*it).stringValue ("value");
2426
val.cloneTo (aNextValue);
2431
/* did we find the key we're looking for? */
2432
if (key == bstrInKey)
2435
/* is there another item? */
2436
if (it != items.end())
2438
Bstr key = (*it).stringValue ("name");
2439
key.cloneTo (aNextKey);
2442
Bstr val = (*it).stringValue ("value");
2443
val.cloneTo (aNextValue);
2446
/* else it's the last one, arguments are already NULL */
2453
/* Here we are when a) there are no items at all or b) there are items
2454
* but none of them equals the requested non-NULL key. b) is an
2455
* error as well as a) if the key is non-NULL. When the key is NULL
2456
* (which is the case only when there are no items), we just fall
2457
* through to return NULLs and S_OK. */
2459
if (!bstrInKey.isEmpty())
2460
return setError (VBOX_E_OBJECT_NOT_FOUND,
2461
tr ("Could not find the extra data key '%ls'"), bstrInKey.raw());
2465
rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
2472
* @note Locks this object for reading.
2474
STDMETHODIMP Machine::GetExtraData (IN_BSTR aKey, BSTR *aValue)
2476
CheckComArgNotNull (aKey);
2477
CheckComArgOutPointerValid (aValue);
2479
AutoCaller autoCaller (this);
2480
CheckComRCReturnRC (autoCaller.rc());
2482
/* serialize file access (prevent writes) */
2483
AutoReadLock alock (this);
2485
/* start with nothing found */
2486
Bstr("").cloneTo(aValue);
2488
/* if we're ready and isConfigLocked() is FALSE then it means
2489
* that no config file exists yet, so return shortly */
2490
if (!isConfigLocked())
2494
HRESULT rc = getExtraData(Utf8Str(aKey), val);
2496
val.cloneTo (aValue);
2501
HRESULT Machine::getExtraData(const Utf8Str &aKey, Utf8Str &aValue)
2507
using namespace settings;
2508
using namespace xml;
2510
/* load the settings file (we don't reuse the existing handle but
2511
* request a new one to allow for concurrent multithreaded reads) */
2512
File file (File::Mode_Read, Utf8Str (mData->mConfigFileFull));
2513
XmlTreeBackend tree;
2515
rc = VirtualBox::loadSettingsTree_Again (tree, file);
2516
CheckComRCReturnRC (rc);
2518
Key machineNode = tree.rootKey().key ("Machine");
2519
Key extraDataNode = machineNode.findKey ("ExtraData");
2521
if (!extraDataNode.isNull())
2523
/* check if the key exists */
2524
Key::List items = extraDataNode.keys ("ExtraDataItem");
2525
for (Key::List::const_iterator it = items.begin();
2526
it != items.end(); ++ it)
2528
if (aKey == (*it).stringValue ("name"))
2530
aValue = (*it).stringValue ("value");
2538
rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
2545
* @note Locks mParent for writing + this object for writing.
2547
STDMETHODIMP Machine::SetExtraData (IN_BSTR aKey, IN_BSTR aValue)
2549
CheckComArgNotNull (aKey);
2551
AutoCaller autoCaller (this);
2552
CheckComRCReturnRC (autoCaller.rc());
2554
/* VirtualBox::onExtraDataCanChange() and saveSettings() need mParent
2555
* lock (saveSettings() needs a write one). This object's write lock is
2556
* also necessary to serialize file access (prevent concurrent reads and
2558
AutoMultiWriteLock2 alock (mParent, this);
2560
if (mType == IsSnapshotMachine)
2562
HRESULT rc = checkStateDependency (MutableStateDep);
2563
CheckComRCReturnRC (rc);
2568
val = (const char *)"";
2573
bool changed = false;
2576
/* If we're ready and isConfigLocked() is FALSE then it means that no
2577
* config file exists yet, so call saveSettings() to create one. */
2578
if (!isConfigLocked())
2580
rc = saveSettings();
2581
CheckComRCReturnRC (rc);
2586
using namespace settings;
2587
using namespace xml;
2589
/* load the settings file */
2590
File file (mData->mHandleCfgFile, Utf8Str (mData->mConfigFileFull));
2591
XmlTreeBackend tree;
2593
rc = VirtualBox::loadSettingsTree_ForUpdate (tree, file);
2594
CheckComRCReturnRC (rc);
2596
const Utf8Str key = aKey;
2599
Key machineNode = tree.rootKey().key ("Machine");
2600
Key extraDataNode = machineNode.createKey ("ExtraData");
2601
Key extraDataItemNode;
2603
Key::List items = extraDataNode.keys ("ExtraDataItem");
2604
for (Key::List::const_iterator it = items.begin();
2605
it != items.end(); ++ it)
2607
if (key == (*it).stringValue ("name"))
2609
extraDataItemNode = *it;
2610
oldVal = (*it).stringValue ("value");
2615
/* When no key is found, oldVal is empty string */
2616
changed = oldVal != val;
2620
/* ask for permission from all listeners */
2622
if (!mParent->onExtraDataCanChange (mData->mUuid, aKey, val, error))
2624
const char *sep = error.isEmpty() ? "" : ": ";
2625
CBSTR err = error.isNull() ? (CBSTR) L"" : error.raw();
2626
LogWarningFunc (("Someone vetoed! Change refused%s%ls\n",
2628
return setError (E_ACCESSDENIED,
2629
tr ("Could not set extra data because someone refused "
2630
"the requested change of '%ls' to '%ls'%s%ls"),
2631
aKey, val.raw(), sep, err);
2636
if (extraDataItemNode.isNull())
2638
extraDataItemNode = extraDataNode.appendKey ("ExtraDataItem");
2639
extraDataItemNode.setStringValue ("name", key);
2641
extraDataItemNode.setStringValue ("value", Utf8Str (aValue));
2645
/* an old value does for sure exist here (XML schema
2646
* guarantees that "value" may not absent in the
2647
* <ExtraDataItem> element) */
2648
Assert (!extraDataItemNode.isNull());
2649
extraDataItemNode.zap();
2652
/* save settings on success */
2653
rc = VirtualBox::saveSettingsTree (tree, file,
2654
mData->mSettingsFileVersion);
3030
AutoCaller autoCaller(this);
3031
CheckComRCReturnRC(autoCaller.rc());
3033
AutoReadLock alock(this);
3035
mNetworkAdapters[slot].queryInterfaceTo(adapter);
3040
STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
3042
if (ComSafeArrayOutIsNull(aKeys))
3045
AutoCaller autoCaller (this);
3046
CheckComRCReturnRC (autoCaller.rc());
3048
AutoReadLock alock (this);
3050
com::SafeArray<BSTR> saKeys(mData->m_pMachineConfigFile->mapExtraDataItems.size());
3052
for (settings::ExtraDataItemsMap::const_iterator it = mData->m_pMachineConfigFile->mapExtraDataItems.begin();
3053
it != mData->m_pMachineConfigFile->mapExtraDataItems.end();
3056
const Utf8Str &strKey = it->first;
3057
strKey.cloneTo(&saKeys[i]);
3059
saKeys.detachTo(ComSafeArrayOutArg(aKeys));
3065
* @note Locks this object for reading.
3067
STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
3070
CheckComArgNotNull(aKey);
3071
CheckComArgOutPointerValid(aValue);
3073
AutoCaller autoCaller (this);
3074
CheckComRCReturnRC (autoCaller.rc());
3076
/* start with nothing found */
3077
Bstr bstrResult("");
3079
AutoReadLock alock (this);
3081
settings::ExtraDataItemsMap::const_iterator it = mData->m_pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
3082
if (it != mData->m_pMachineConfigFile->mapExtraDataItems.end())
3084
bstrResult = it->second; // source is a Utf8Str
3086
/* return the result to caller (may be empty) */
3087
bstrResult.cloneTo(aValue);
3093
* @note Locks mParent for writing + this object for writing.
3095
STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
3097
CheckComArgNotNull(aKey);
3099
AutoCaller autoCaller(this);
3100
CheckComRCReturnRC (autoCaller.rc());
3102
Utf8Str strKey(aKey);
3103
Utf8Str strValue(aValue);
3104
Utf8Str strOldValue; // empty
3106
// locking note: we only hold the read lock briefly to look up the old value,
3107
// then release it and call the onExtraCanChange callbacks. There is a small
3108
// chance of a race insofar as the callback might be called twice if two callers
3109
// change the same key at the same time, but that's a much better solution
3110
// than the deadlock we had here before. The actual changing of the extradata
3111
// is then performed under the write lock and race-free.
3113
// look up the old value first; if nothing's changed then we need not do anything
3115
AutoReadLock alock(this); // hold read lock only while looking up
3116
settings::ExtraDataItemsMap::const_iterator it = mData->m_pMachineConfigFile->mapExtraDataItems.find(strKey);
3117
if (it != mData->m_pMachineConfigFile->mapExtraDataItems.end())
3118
strOldValue = it->second;
3122
if ((fChanged = (strOldValue != strValue)))
3124
// ask for permission from all listeners outside the locks;
3125
// onExtraDataCanChange() only briefly requests the VirtualBox
3126
// lock to copy the list of callbacks to invoke
3132
bstrValue = (const char *)"";
3134
if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue, error))
3136
const char *sep = error.isEmpty() ? "" : ": ";
3137
CBSTR err = error.isNull() ? (CBSTR) L"" : error.raw();
3138
LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
3140
return setError(E_ACCESSDENIED,
3141
tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
3148
// data is changing and change not vetoed: then write it out under the locks
3150
// saveSettings() needs VirtualBox write lock
3151
AutoMultiWriteLock2 alock(mParent, this);
3153
if (mType == IsSnapshotMachine)
3155
HRESULT rc = checkStateDependency(MutableStateDep);
2655
3156
CheckComRCReturnRC (rc);
2660
rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
2663
/* fire a notification */
2664
if (SUCCEEDED (rc) && changed)
2665
mParent->onExtraDataChange (mData->mUuid, aKey, aValue);
3159
if (strValue.isEmpty())
3160
mData->m_pMachineConfigFile->mapExtraDataItems.erase(strKey);
3162
mData->m_pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
3163
// creates a new key if needed
3165
/* save settings on success */
3166
HRESULT rc = saveSettings();
3167
CheckComRCReturnRC (rc);
3170
// fire notification outside the lock
3172
mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
2670
3177
STDMETHODIMP Machine::SaveSettings()
2672
AutoCaller autoCaller (this);
2673
CheckComRCReturnRC (autoCaller.rc());
2675
/* saveSettings() needs mParent lock */
2676
AutoMultiWriteLock2 alock (mParent, this);
2678
/* when there was auto-conversion, we want to save the file even if
2679
* the VM is saved */
2680
StateDependency dep = mData->mSettingsFileVersion != VBOX_XML_VERSION_FULL ?
2681
MutableOrSavedStateDep : MutableStateDep;
2683
HRESULT rc = checkStateDependency (dep);
2684
CheckComRCReturnRC (rc);
2686
/* the settings file path may never be null */
2687
ComAssertRet (mData->mConfigFileFull, E_FAIL);
2689
/* save all VM data excluding snapshots */
2690
return saveSettings();
2693
STDMETHODIMP Machine::SaveSettingsWithBackup (BSTR *aBakFileName)
2695
CheckComArgOutPointerValid (aBakFileName);
2697
AutoCaller autoCaller (this);
2698
CheckComRCReturnRC (autoCaller.rc());
2700
/* saveSettings() needs mParent lock */
2701
AutoMultiWriteLock2 alock (mParent, this);
2703
/* when there was auto-conversion, we want to save the file even if
2704
* the VM is saved */
2705
StateDependency dep = mData->mSettingsFileVersion != VBOX_XML_VERSION_FULL ?
2706
MutableOrSavedStateDep : MutableStateDep;
2708
HRESULT rc = checkStateDependency (dep);
2709
CheckComRCReturnRC (rc);
2711
/* the settings file path may never be null */
2712
ComAssertRet (mData->mConfigFileFull, E_FAIL);
2714
/* perform backup only when there was auto-conversion */
2715
if (mData->mSettingsFileVersion != VBOX_XML_VERSION_FULL)
2719
HRESULT rc = VirtualBox::backupSettingsFile (mData->mConfigFileFull,
2720
mData->mSettingsFileVersion,
2722
CheckComRCReturnRC (rc);
2724
bakFileName.cloneTo (aBakFileName);
3179
AutoCaller autoCaller(this);
3180
CheckComRCReturnRC(autoCaller.rc());
3182
/* saveSettings() needs mParent lock */
3183
AutoMultiWriteLock2 alock(mParent, this);
3185
/* when there was auto-conversion, we want to save the file even if
3186
* the VM is saved */
3187
HRESULT rc = checkStateDependency(MutableStateDep);
3188
CheckComRCReturnRC(rc);
3190
/* the settings file path may never be null */
3191
ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
2727
3193
/* save all VM data excluding snapshots */
2728
3194
return saveSettings();
3052
3524
return GetGuestProperty (aName, &dummyValue, aTimestamp, &dummyFlags);
3055
STDMETHODIMP Machine::SetGuestProperty (IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
3527
STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName,
3057
3531
#if !defined (VBOX_WITH_GUEST_PROPS)
3058
3532
ReturnComNotImplemented();
3060
3534
using namespace guestProp;
3062
CheckComArgNotNull (aName);
3063
CheckComArgNotNull (aValue);
3536
CheckComArgNotNull(aName);
3537
CheckComArgNotNull(aValue);
3064
3538
if ((aFlags != NULL) && !VALID_PTR (aFlags))
3065
3539
return E_INVALIDARG;
3067
Utf8Str utf8Name (aName);
3068
Utf8Str utf8Flags (aFlags);
3069
Utf8Str utf8Patterns (mHWData->mGuestPropertyNotificationPatterns);
3070
if ( utf8Name.isNull()
3071
|| ((aFlags != NULL) && utf8Flags.isNull())
3072
|| utf8Patterns.isNull()
3074
return E_OUTOFMEMORY;
3075
bool matchAll = false;
3076
if (0 == utf8Patterns.length())
3079
uint32_t fFlags = NILFLAG;
3080
if ((aFlags != NULL) && RT_FAILURE (validateFlags (utf8Flags.raw(), &fFlags))
3082
return setError (E_INVALIDARG, tr ("Invalid flag values: '%ls'"),
3085
AutoCaller autoCaller (this);
3086
CheckComRCReturnRC (autoCaller.rc());
3088
AutoWriteLock alock (this);
3090
HRESULT rc = checkStateDependency (MutableStateDep);
3091
CheckComRCReturnRC (rc);
3095
if (!mHWData->mPropertyServiceActive)
3098
HWData::GuestProperty property;
3099
property.mFlags = NILFLAG;
3545
Utf8Str utf8Name(aName);
3546
Utf8Str utf8Flags(aFlags);
3548
AutoCaller autoCaller(this);
3549
CheckComRCReturnRC(autoCaller.rc());
3551
AutoWriteLock alock(this);
3553
rc = checkStateDependency(MutableStateDep);
3554
CheckComRCReturnRC(rc);
3557
uint32_t fFlags = NILFLAG;
3558
if ( (aFlags != NULL)
3559
&& RT_FAILURE(validateFlags (utf8Flags.raw(), &fFlags))
3561
return setError(E_INVALIDARG,
3562
tr("Invalid flag values: '%ls'"),
3565
if (!mHWData->mPropertyServiceActive)
3102
for (HWData::GuestPropertyList::iterator it =
3103
mHWData->mGuestProperties.begin();
3104
it != mHWData->mGuestProperties.end(); ++ it)
3105
if (it->mName == aName)
3568
HWData::GuestProperty property;
3569
property.mFlags = NILFLAG;
3571
/** @todo r=bird: see efficiency rant in PushGuestProperty. (Yeah, I know,
3572
* this is simple and do an OK job atm.) */
3573
for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
3574
it != mHWData->mGuestProperties.end();
3576
if (it->strName == utf8Name)
3107
3578
property = *it;
3108
3579
if (it->mFlags & (RDONLYHOST))
3109
rc = setError (E_ACCESSDENIED,
3110
tr ("The property '%ls' cannot be changed by the host"),
3580
rc = setError(E_ACCESSDENIED,
3581
tr("The property '%ls' cannot be changed by the host"),
3114
3585
mHWData.backup();
3115
3586
/* The backup() operation invalidates our iterator, so
3117
3588
for (it = mHWData->mGuestProperties.begin();
3118
it->mName != aName; ++ it)
3589
it->strName != utf8Name;
3120
3592
mHWData->mGuestProperties.erase (it);
3126
if (found && SUCCEEDED (rc))
3597
if (found && SUCCEEDED(rc))
3602
property.strValue = aValue;
3603
property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
3605
property.mFlags = fFlags;
3606
mHWData->mGuestProperties.push_back (property);
3609
else if (SUCCEEDED(rc) && *aValue)
3130
3611
RTTIMESPEC time;
3131
property.mValue = aValue;
3132
property.mTimestamp = RTTimeSpecGetNano (RTTimeNow (&time));
3134
property.mFlags = fFlags;
3613
property.strName = aName;
3614
property.strValue = aValue;
3615
property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
3616
property.mFlags = fFlags;
3135
3617
mHWData->mGuestProperties.push_back (property);
3620
&& ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
3621
|| RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.raw(), RTSTR_MAX,
3622
utf8Name.raw(), RTSTR_MAX, NULL) )
3625
/** @todo r=bird: Why aren't we leaving the lock here? The
3626
* same code in PushGuestProperty does... */
3627
mParent->onGuestPropertyChange(mData->mUuid, aName, aValue, aFlags);
3138
else if (SUCCEEDED (rc) && *aValue)
3142
property.mName = aName;
3143
property.mValue = aValue;
3144
property.mTimestamp = RTTimeSpecGetNano (RTTimeNow (&time));
3145
property.mFlags = fFlags;
3146
mHWData->mGuestProperties.push_back (property);
3632
ComPtr<IInternalSessionControl> directControl =
3633
mData->mSession.mDirectControl;
3635
/* just be on the safe side when calling another process */
3643
rc = directControl->AccessGuestProperty(aName,
3644
*aValue ? aValue : NULL, /** @todo Fix when adding DeleteGuestProperty(), see defect. */
3646
true /* isSetter */,
3647
&dummy, &dummy64, &dummy);
3150
|| RTStrSimplePatternMultiMatch (utf8Patterns.raw(), RTSTR_MAX,
3151
utf8Name.raw(), RTSTR_MAX, NULL)
3154
mParent->onGuestPropertyChange (mData->mUuid, aName, aValue, aFlags);
3650
catch (std::bad_alloc &)
3158
ComPtr <IInternalSessionControl> directControl =
3159
mData->mSession.mDirectControl;
3161
/* just be on the safe side when calling another process */
3169
rc = directControl->AccessGuestProperty (aName, aValue, aFlags,
3170
true /* isSetter */,
3171
&dummy, &dummy64, &dummy);
3174
3656
#endif /* else !defined (VBOX_WITH_GUEST_PROPS) */
3947
/* @todo where is the right place for this? */
3948
#define sSSMDisplayScreenshotVer 0x00010001
3950
static int readSavedDisplayScreenshot(Utf8Str *pStateFilePath, uint32_t u32Type, uint8_t **ppu8Data, uint32_t *pcbData, uint32_t *pu32Width, uint32_t *pu32Height)
3952
LogFlowFunc(("u32Type = %d [%s]\n", u32Type, pStateFilePath->raw()));
3954
/* @todo cache read data */
3955
if (pStateFilePath->isEmpty())
3957
/* No saved state data. */
3958
return VERR_NOT_SUPPORTED;
3961
uint8_t *pu8Data = NULL;
3962
uint32_t cbData = 0;
3963
uint32_t u32Width = 0;
3964
uint32_t u32Height = 0;
3967
int rc = SSMR3Open(pStateFilePath->raw(), 0 /*fFlags*/, &pSSM);
3971
rc = SSMR3Seek(pSSM, "DisplayScreenshot", 1100 /*iInstance*/, &uVersion);
3974
if (uVersion == sSSMDisplayScreenshotVer)
3977
rc = SSMR3GetU32(pSSM, &cBlocks);
3978
AssertRCReturn(rc, rc);
3980
for (uint32_t i = 0; i < cBlocks; i++)
3983
rc = SSMR3GetU32(pSSM, &cbBlock);
3986
uint32_t typeOfBlock;
3987
rc = SSMR3GetU32(pSSM, &typeOfBlock);
3990
LogFlowFunc(("[%d] type %d, size %d bytes\n", i, typeOfBlock, cbBlock));
3992
if (typeOfBlock == u32Type)
3994
if (cbBlock > 2 * sizeof (uint32_t))
3996
cbData = cbBlock - 2 * sizeof (uint32_t);
3997
pu8Data = (uint8_t *)RTMemAlloc(cbData);
3998
if (pu8Data == NULL)
4000
rc = VERR_NO_MEMORY;
4004
rc = SSMR3GetU32(pSSM, &u32Width);
4006
rc = SSMR3GetU32(pSSM, &u32Height);
4008
rc = SSMR3GetMem(pSSM, pu8Data, cbData);
4013
/* No saved state data. */
4014
rc = VERR_NOT_SUPPORTED;
4023
rc = SSMR3Skip(pSSM, cbBlock);
4031
rc = VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
4040
if (u32Type == 0 && cbData % 4 != 0)
4042
/* Bitmap is 32bpp, so data is invalid. */
4043
rc = VERR_SSM_UNEXPECTED_DATA;
4049
*ppu8Data = pu8Data;
4051
*pu32Width = u32Width;
4052
*pu32Height = u32Height;
4053
LogFlowFunc(("cbData %d, u32Width %d, u32Height %d\n", cbData, u32Width, u32Height));
4056
LogFlowFunc(("rc %Rrc\n", rc));
4060
static void freeSavedDisplayScreenshot(uint8_t *pu8Data)
4062
/* @todo not necessary when caching is implemented. */
4066
STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
4068
LogFlowThisFunc(("\n"));
4070
CheckComArgNotNull(aSize);
4071
CheckComArgNotNull(aWidth);
4072
CheckComArgNotNull(aHeight);
4074
AutoCaller autoCaller(this);
4075
CheckComRCReturnRC(autoCaller.rc());
4077
AutoReadLock alock(this);
4079
uint8_t *pu8Data = NULL;
4080
uint32_t cbData = 0;
4081
uint32_t u32Width = 0;
4082
uint32_t u32Height = 0;
4084
int vrc = readSavedDisplayScreenshot(&mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
4086
if (RT_FAILURE(vrc))
4087
return setError (VBOX_E_IPRT_ERROR,
4088
tr("Saved screenshot data is not available (%Rrc)"), vrc);
4092
*aHeight = u32Height;
4094
freeSavedDisplayScreenshot(pu8Data);
4099
STDMETHODIMP Machine::ReadSavedThumbnailToArray(BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
4101
LogFlowThisFunc(("\n"));
4103
CheckComArgNotNull(aWidth);
4104
CheckComArgNotNull(aHeight);
4105
CheckComArgExpr(aData, !ComSafeArrayOutIsNull(aData));
4107
AutoCaller autoCaller(this);
4108
CheckComRCReturnRC(autoCaller.rc());
4110
AutoReadLock alock(this);
4112
uint8_t *pu8Data = NULL;
4113
uint32_t cbData = 0;
4114
uint32_t u32Width = 0;
4115
uint32_t u32Height = 0;
4117
int vrc = readSavedDisplayScreenshot(&mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
4119
if (RT_FAILURE(vrc))
4120
return setError (VBOX_E_IPRT_ERROR,
4121
tr("Saved screenshot data is not available (%Rrc)"), vrc);
4124
*aHeight = u32Height;
4126
com::SafeArray<BYTE> bitmap(cbData);
4127
/* Convert pixels to format expected by the API caller. */
4130
/* [0] B, [1] G, [2] R, [3] A. */
4131
for (unsigned i = 0; i < cbData; i += 4)
4133
bitmap[i] = pu8Data[i];
4134
bitmap[i + 1] = pu8Data[i + 1];
4135
bitmap[i + 2] = pu8Data[i + 2];
4136
bitmap[i + 3] = 0xff;
4141
/* [0] R, [1] G, [2] B, [3] A. */
4142
for (unsigned i = 0; i < cbData; i += 4)
4144
bitmap[i] = pu8Data[i + 2];
4145
bitmap[i + 1] = pu8Data[i + 1];
4146
bitmap[i + 2] = pu8Data[i];
4147
bitmap[i + 3] = 0xff;
4150
bitmap.detachTo(ComSafeArrayOutArg(aData));
4152
freeSavedDisplayScreenshot(pu8Data);
4157
STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
4159
LogFlowThisFunc(("\n"));
4161
CheckComArgNotNull(aSize);
4162
CheckComArgNotNull(aWidth);
4163
CheckComArgNotNull(aHeight);
4165
AutoCaller autoCaller(this);
4166
CheckComRCReturnRC(autoCaller.rc());
4168
AutoReadLock alock(this);
4170
uint8_t *pu8Data = NULL;
4171
uint32_t cbData = 0;
4172
uint32_t u32Width = 0;
4173
uint32_t u32Height = 0;
4175
int vrc = readSavedDisplayScreenshot(&mSSData->mStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
4177
if (RT_FAILURE(vrc))
4178
return setError (VBOX_E_IPRT_ERROR,
4179
tr("Saved screenshot data is not available (%Rrc)"), vrc);
4183
*aHeight = u32Height;
4185
freeSavedDisplayScreenshot(pu8Data);
4190
STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
4192
LogFlowThisFunc(("\n"));
4194
CheckComArgNotNull(aWidth);
4195
CheckComArgNotNull(aHeight);
4196
CheckComArgExpr(aData, !ComSafeArrayOutIsNull(aData));
4198
AutoCaller autoCaller(this);
4199
CheckComRCReturnRC(autoCaller.rc());
4201
AutoReadLock alock(this);
4203
uint8_t *pu8Data = NULL;
4204
uint32_t cbData = 0;
4205
uint32_t u32Width = 0;
4206
uint32_t u32Height = 0;
4208
int vrc = readSavedDisplayScreenshot(&mSSData->mStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
4210
if (RT_FAILURE(vrc))
4211
return setError (VBOX_E_IPRT_ERROR,
4212
tr("Saved screenshot data is not available (%Rrc)"), vrc);
4215
*aHeight = u32Height;
4217
com::SafeArray<BYTE> png(cbData);
4218
for (unsigned i = 0; i < cbData; i++)
4219
png[i] = pu8Data[i];
4220
png.detachTo(ComSafeArrayOutArg(aData));
4222
freeSavedDisplayScreenshot(pu8Data);
3398
4227
// public methods for internal purposes
3399
4228
/////////////////////////////////////////////////////////////////////////////
5141
5906
* @param aNode <Hardware> node.
5143
HRESULT Machine::loadHardware (const settings::Key &aNode)
5908
HRESULT Machine::loadHardware(const settings::Hardware &data)
5145
using namespace settings;
5147
AssertReturn (!aNode.isNull(), E_INVALIDARG);
5148
AssertReturn (mType == IsMachine || mType == IsSnapshotMachine, E_FAIL);
5910
AssertReturn(mType == IsMachine || mType == IsSnapshotMachine, E_FAIL);
5150
5912
HRESULT rc = S_OK;
5152
/* The hardware version attribute (optional). */
5153
mHWData->mHWVersion = aNode.stringValue ("version");
5155
/* CPU node (currently not required) */
5157
/* default value in case the node is not there */
5158
mHWData->mHWVirtExEnabled = true;
5159
mHWData->mHWVirtExNestedPagingEnabled = false;
5160
mHWData->mHWVirtExVPIDEnabled = false;
5161
mHWData->mPAEEnabled = false;
5163
Key cpuNode = aNode.findKey ("CPU");
5164
if (!cpuNode.isNull())
5916
/* The hardware version attribute (optional). */
5917
mHWData->mHWVersion = data.strVersion;
5918
mHWData->mHardwareUUID = data.uuid;
5920
mHWData->mHWVirtExEnabled = data.fHardwareVirt;
5921
mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
5922
mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
5923
mHWData->mHWVirtExVPIDEnabled = data.fVPID;
5924
mHWData->mPAEEnabled = data.fPAE;
5925
mHWData->mSyntheticCpu = data.fSyntheticCpu;
5927
mHWData->mCPUCount = data.cCPUs;
5930
for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
5931
it != data.llCpuIdLeafs.end();
5166
Key hwVirtExNode = cpuNode.key ("HardwareVirtEx");
5167
if (!hwVirtExNode.isNull())
5169
const char *enabled = hwVirtExNode.stringValue ("enabled");
5170
if (strcmp (enabled, "true") == 0)
5171
mHWData->mHWVirtExEnabled = true;
5173
mHWData->mHWVirtExEnabled = false;
5175
/* HardwareVirtExNestedPaging (optional, default is false) */
5176
Key HWVirtExNestedPagingNode = cpuNode.findKey ("HardwareVirtExNestedPaging");
5177
if (!HWVirtExNestedPagingNode.isNull())
5179
mHWData->mHWVirtExNestedPagingEnabled = HWVirtExNestedPagingNode.value <bool> ("enabled");
5182
/* HardwareVirtExVPID (optional, default is false) */
5183
Key HWVirtExVPIDNode = cpuNode.findKey ("HardwareVirtExVPID");
5184
if (!HWVirtExVPIDNode.isNull())
5186
mHWData->mHWVirtExVPIDEnabled = HWVirtExVPIDNode.value <bool> ("enabled");
5189
/* PAE (optional, default is false) */
5190
Key PAENode = cpuNode.findKey ("PAE");
5191
if (!PAENode.isNull())
5193
mHWData->mPAEEnabled = PAENode.value <bool> ("enabled");
5196
/* CPUCount (optional, default is 1) */
5197
mHWData->mCPUCount = cpuNode.value <ULONG> ("count");
5934
const settings::CpuIdLeaf &leaf = *it;
5949
mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
5963
mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
5201
/* Memory node (required) */
5203
Key memoryNode = aNode.key ("Memory");
5205
mHWData->mMemorySize = memoryNode.value <ULONG> ("RAMSize");
5208
/* Boot node (required) */
5210
/* reset all boot order positions to NoDevice */
5211
for (size_t i = 0; i < RT_ELEMENTS (mHWData->mBootOrder); i++)
5212
mHWData->mBootOrder [i] = DeviceType_Null;
5214
Key bootNode = aNode.key ("Boot");
5216
Key::List orderNodes = bootNode.keys ("Order");
5217
for (Key::List::const_iterator it = orderNodes.begin();
5218
it != orderNodes.end(); ++ it)
5972
mHWData->mMemorySize = data.ulMemorySizeMB;
5976
i < RT_ELEMENTS(mHWData->mBootOrder);
5220
/* position (required) */
5221
/* position unicity is guaranteed by XML Schema */
5222
uint32_t position = (*it).value <uint32_t> ("position");
5224
Assert (position < RT_ELEMENTS (mHWData->mBootOrder));
5226
/* device (required) */
5227
const char *device = (*it).stringValue ("device");
5228
if (strcmp (device, "None") == 0)
5229
mHWData->mBootOrder [position] = DeviceType_Null;
5230
else if (strcmp (device, "Floppy") == 0)
5231
mHWData->mBootOrder [position] = DeviceType_Floppy;
5232
else if (strcmp (device, "DVD") == 0)
5233
mHWData->mBootOrder [position] = DeviceType_DVD;
5234
else if (strcmp (device, "HardDisk") == 0)
5235
mHWData->mBootOrder [position] = DeviceType_HardDisk;
5236
else if (strcmp (device, "Network") == 0)
5237
mHWData->mBootOrder [position] = DeviceType_Network;
5979
settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
5980
if (it == data.mapBootOrder.end())
5981
mHWData->mBootOrder[i] = DeviceType_Null;
5239
ComAssertMsgFailed (("Invalid device: %s", device));
5983
mHWData->mBootOrder[i] = it->second;
5243
/* Display node (required) */
5245
Key displayNode = aNode.key ("Display");
5247
mHWData->mVRAMSize = displayNode.value <ULONG> ("VRAMSize");
5248
mHWData->mMonitorCount = displayNode.value <ULONG> ("monitorCount");
5249
mHWData->mAccelerate3DEnabled = displayNode.value <bool> ("accelerate3D");
5986
mHWData->mVRAMSize = data.ulVRAMSizeMB;
5987
mHWData->mMonitorCount = data.cMonitors;
5988
mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
5989
mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
5990
mHWData->mFirmwareType = data.firmwareType;
5252
5992
#ifdef VBOX_WITH_VRDP
5254
rc = mVRDPServer->loadSettings (aNode);
5255
CheckComRCReturnRC (rc);
5994
rc = mVRDPServer->loadSettings(data.vrdpSettings);
5995
CheckComRCReturnRC (rc);
5259
rc = mBIOSSettings->loadSettings (aNode);
5260
CheckComRCReturnRC (rc);
5263
rc = mDVDDrive->loadSettings (aNode);
5264
CheckComRCReturnRC (rc);
5267
rc = mFloppyDrive->loadSettings (aNode);
5268
CheckComRCReturnRC (rc);
5270
/* USB Controller */
5271
rc = mUSBController->loadSettings (aNode);
5272
CheckComRCReturnRC (rc);
5274
/* Network node (required) */
5276
/* we assume that all network adapters are initially disabled
5279
Key networkNode = aNode.key ("Network");
5283
Key::List adapters = networkNode.keys ("Adapter");
5284
for (Key::List::const_iterator it = adapters.begin();
5285
it != adapters.end(); ++ it)
5287
/* slot number (required) */
5288
/* slot unicity is guaranteed by XML Schema */
5289
uint32_t slot = (*it).value <uint32_t> ("slot");
5290
AssertBreak (slot < RT_ELEMENTS (mNetworkAdapters));
5292
rc = mNetworkAdapters [slot]->loadSettings (*it);
5293
CheckComRCReturnRC (rc);
5297
/* Serial node (required) */
5299
Key serialNode = aNode.key ("UART");
5303
Key::List ports = serialNode.keys ("Port");
5304
for (Key::List::const_iterator it = ports.begin();
5305
it != ports.end(); ++ it)
5307
/* slot number (required) */
5308
/* slot unicity is guaranteed by XML Schema */
5309
uint32_t slot = (*it).value <uint32_t> ("slot");
5310
AssertBreak (slot < RT_ELEMENTS (mSerialPorts));
5312
rc = mSerialPorts [slot]->loadSettings (*it);
5313
CheckComRCReturnRC (rc);
5317
/* Parallel node (optional) */
5319
Key parallelNode = aNode.key ("LPT");
5323
Key::List ports = parallelNode.keys ("Port");
5324
for (Key::List::const_iterator it = ports.begin();
5325
it != ports.end(); ++ it)
5327
/* slot number (required) */
5328
/* slot unicity is guaranteed by XML Schema */
5329
uint32_t slot = (*it).value <uint32_t> ("slot");
5330
AssertBreak (slot < RT_ELEMENTS (mSerialPorts));
5332
rc = mParallelPorts [slot]->loadSettings (*it);
5333
CheckComRCReturnRC (rc);
5338
rc = mAudioAdapter->loadSettings (aNode);
5339
CheckComRCReturnRC (rc);
5341
/* Shared folders (required) */
5343
Key sharedFoldersNode = aNode.key ("SharedFolders");
5347
Key::List folders = sharedFoldersNode.keys ("SharedFolder");
5348
for (Key::List::const_iterator it = folders.begin();
5349
it != folders.end(); ++ it)
5351
/* folder logical name (required) */
5352
Bstr name = (*it).stringValue ("name");
5353
/* folder host path (required) */
5354
Bstr hostPath = (*it).stringValue ("hostPath");
5356
bool writable = (*it).value <bool> ("writable");
5358
rc = CreateSharedFolder (name, hostPath, writable);
5359
CheckComRCReturnRC (rc);
5363
/* Clipboard node (required) */
5365
Key clipNode = aNode.key ("Clipboard");
5367
const char *mode = clipNode.stringValue ("mode");
5368
if (strcmp (mode, "Disabled") == 0)
5369
mHWData->mClipboardMode = ClipboardMode_Disabled;
5370
else if (strcmp (mode, "HostToGuest") == 0)
5371
mHWData->mClipboardMode = ClipboardMode_HostToGuest;
5372
else if (strcmp (mode, "GuestToHost") == 0)
5373
mHWData->mClipboardMode = ClipboardMode_GuestToHost;
5374
else if (strcmp (mode, "Bidirectional") == 0)
5375
mHWData->mClipboardMode = ClipboardMode_Bidirectional;
5377
AssertMsgFailed (("Invalid clipboard mode '%s'\n", mode));
5380
/* Guest node (required) */
5382
Key guestNode = aNode.key ("Guest");
5384
/* optional, defaults to 0 */
5385
mHWData->mMemoryBalloonSize =
5386
guestNode.value <ULONG> ("memoryBalloonSize");
5387
/* optional, defaults to 0 */
5388
mHWData->mStatisticsUpdateInterval =
5389
guestNode.value <ULONG> ("statisticsUpdateInterval");
5999
rc = mBIOSSettings->loadSettings(data.biosSettings);
6000
CheckComRCReturnRC (rc);
6002
/* USB Controller */
6003
rc = mUSBController->loadSettings(data.usbController);
6004
CheckComRCReturnRC (rc);
6007
for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
6008
it != data.llNetworkAdapters.end();
6011
const settings::NetworkAdapter &nic = *it;
6013
/* slot unicity is guaranteed by XML Schema */
6014
AssertBreak(nic.ulSlot < RT_ELEMENTS(mNetworkAdapters));
6015
rc = mNetworkAdapters[nic.ulSlot]->loadSettings(nic);
6016
CheckComRCReturnRC (rc);
6020
for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
6021
it != data.llSerialPorts.end();
6024
const settings::SerialPort &s = *it;
6026
AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
6027
rc = mSerialPorts[s.ulSlot]->loadSettings(s);
6028
CheckComRCReturnRC (rc);
6031
// parallel ports (optional)
6032
for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
6033
it != data.llParallelPorts.end();
6036
const settings::ParallelPort &p = *it;
6038
AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
6039
rc = mParallelPorts[p.ulSlot]->loadSettings(p);
6040
CheckComRCReturnRC (rc);
6044
rc = mAudioAdapter->loadSettings(data.audioAdapter);
6045
CheckComRCReturnRC (rc);
6047
for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
6048
it != data.llSharedFolders.end();
6051
const settings::SharedFolder &sf = *it;
6052
rc = CreateSharedFolder(Bstr(sf.strName), Bstr(sf.strHostPath), sf.fWritable);
6053
CheckComRCReturnRC (rc);
6057
mHWData->mClipboardMode = data.clipboardMode;
6060
mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
6061
mHWData->mStatisticsUpdateInterval = data.ulStatisticsUpdateInterval;
5392
6063
#ifdef VBOX_WITH_GUEST_PROPS
5393
/* Guest properties (optional) */
5395
using namespace guestProp;
5397
Key guestPropertiesNode = aNode.findKey ("GuestProperties");
5398
Bstr notificationPatterns (""); /* We catch allocation failure below. */
5399
if (!guestPropertiesNode.isNull())
6064
/* Guest properties (optional) */
6065
for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
6066
it != data.llGuestProperties.end();
5401
Key::List properties = guestPropertiesNode.keys ("GuestProperty");
5402
for (Key::List::const_iterator it = properties.begin();
5403
it != properties.end(); ++ it)
5405
uint32_t fFlags = NILFLAG;
5407
/* property name (required) */
5408
Bstr name = (*it).stringValue ("name");
5409
/* property value (required) */
5410
Bstr value = (*it).stringValue ("value");
5411
/* property timestamp (optional, defaults to 0) */
5412
ULONG64 timestamp = (*it).value<ULONG64> ("timestamp");
5413
/* property flags (optional, defaults to empty) */
5414
Bstr flags = (*it).stringValue ("flags");
5415
Utf8Str utf8Flags (flags);
5416
if (utf8Flags.isNull ())
5417
return E_OUTOFMEMORY;
5418
validateFlags (utf8Flags.raw(), &fFlags);
5419
HWData::GuestProperty property = { name, value, timestamp, fFlags };
5420
mHWData->mGuestProperties.push_back (property);
5421
/* This is just sanity, as the push_back() will probably have thrown
5422
* an exception if we are out of memory. Note that if we run out
5423
* allocating the Bstrs above, this will be caught here as well. */
5424
if ( mHWData->mGuestProperties.back().mName.isNull ()
5425
|| mHWData->mGuestProperties.back().mValue.isNull ()
5427
return E_OUTOFMEMORY;
5429
notificationPatterns = guestPropertiesNode.stringValue ("notificationPatterns");
6069
const settings::GuestProperty &prop = *it;
6070
uint32_t fFlags = guestProp::NILFLAG;
6071
guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
6072
HWData::GuestProperty property = { prop.strName, prop.strValue, prop.timestamp, fFlags };
6073
mHWData->mGuestProperties.push_back(property);
5431
6076
mHWData->mPropertyServiceActive = false;
5432
mHWData->mGuestPropertyNotificationPatterns = notificationPatterns;
5433
if (mHWData->mGuestPropertyNotificationPatterns.isNull ())
5434
return E_OUTOFMEMORY;
6077
mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
5436
6078
#endif /* VBOX_WITH_GUEST_PROPS defined */
6080
catch(std::bad_alloc &)
6082
return E_OUTOFMEMORY;
5443
* @param aNode <StorageControllers> node.
5445
HRESULT Machine::loadStorageControllers (const settings::Key &aNode, bool aRegistered,
5446
const Guid *aSnapshotId /* = NULL */)
6090
* @param aNode <StorageControllers> node.
6092
HRESULT Machine::loadStorageControllers(const settings::Storage &data,
6094
const Guid *aSnapshotId /* = NULL */)
5448
using namespace settings;
5450
AssertReturn (!aNode.isNull(), E_INVALIDARG);
5451
6096
AssertReturn (mType == IsMachine || mType == IsSnapshotMachine, E_FAIL);
5453
6098
HRESULT rc = S_OK;
5455
Key::List children = aNode.keys ("StorageController");
5457
6100
/* Make sure the attached hard disks don't get unregistered until we
5458
6101
* associate them with tis machine (important for VMs loaded (opened) after
5459
6102
* VirtualBox startup) */
5460
AutoReadLock vboxLock (mParent);
6103
AutoReadLock vboxLock(mParent);
5462
for (Key::List::const_iterator it = children.begin();
5463
it != children.end(); ++ it)
6105
for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
6106
it != data.llStorageControllers.end();
5465
Bstr controllerName = (*it).stringValue ("name");
5466
const char *controllerType = (*it).stringValue ("type");
5467
ULONG portCount = (*it).value <ULONG> ("PortCount");
5468
StorageControllerType_T controller;
5469
StorageBus_T connection;
5471
if (strcmp (controllerType, "AHCI") == 0)
5473
connection = StorageBus_SATA;
5474
controller = StorageControllerType_IntelAhci;
5476
else if (strcmp (controllerType, "LsiLogic") == 0)
5478
connection = StorageBus_SCSI;
5479
controller = StorageControllerType_LsiLogic;
5481
else if (strcmp (controllerType, "BusLogic") == 0)
5483
connection = StorageBus_SCSI;
5484
controller = StorageControllerType_BusLogic;
5486
else if (strcmp (controllerType, "PIIX3") == 0)
5488
connection = StorageBus_IDE;
5489
controller = StorageControllerType_PIIX3;
5491
else if (strcmp (controllerType, "PIIX4") == 0)
5493
connection = StorageBus_IDE;
5494
controller = StorageControllerType_PIIX4;
5496
else if (strcmp (controllerType, "ICH6") == 0)
5498
connection = StorageBus_IDE;
5499
controller = StorageControllerType_ICH6;
5502
AssertFailedReturn (E_FAIL);
5504
ComObjPtr<StorageController> ctl;
6109
const settings::StorageController &ctlData = *it;
6111
ComObjPtr<StorageController> pCtl;
5505
6112
/* Try to find one with the name first. */
5506
rc = getStorageControllerByName (controllerName, ctl, false /* aSetError */);
5508
return setError (VBOX_E_OBJECT_IN_USE,
5509
tr ("Storage controller named '%ls' already exists"), controllerName.raw());
5512
rc = ctl->init (this, controllerName, connection);
5513
CheckComRCReturnRC (rc);
5515
mStorageControllers->push_back (ctl);
5517
rc = ctl->COMSETTER(ControllerType)(controller);
5518
CheckComRCReturnRC(rc);
5520
rc = ctl->COMSETTER(PortCount)(portCount);
5521
CheckComRCReturnRC(rc);
6113
rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
6115
return setError(VBOX_E_OBJECT_IN_USE,
6116
tr("Storage controller named '%s' already exists"),
6117
ctlData.strName.raw());
6119
pCtl.createObject();
6120
rc = pCtl->init(this,
6123
ctlData.ulInstance);
6124
CheckComRCReturnRC (rc);
6126
mStorageControllers->push_back(pCtl);
6128
rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
6129
CheckComRCReturnRC (rc);
6131
rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
6132
CheckComRCReturnRC (rc);
5523
6134
/* Set IDE emulation settings (only for AHCI controller). */
5524
if (controller == StorageControllerType_IntelAhci)
6135
if (ctlData.controllerType == StorageControllerType_IntelAhci)
5528
/* ide emulation settings (optional, default to 0,1,2,3 respectively) */
5529
val = (*it).valueOr <ULONG> ("IDE0MasterEmulationPort", 0);
5530
rc = ctl->SetIDEEmulationPort(0, val);
5531
CheckComRCReturnRC(rc);
5532
val = (*it).valueOr <ULONG> ("IDE0SlaveEmulationPort", 1);
5533
rc = ctl->SetIDEEmulationPort(1, val);
5534
CheckComRCReturnRC(rc);
5535
val = (*it).valueOr <ULONG> ("IDE1MasterEmulationPort", 2);
5536
rc = ctl->SetIDEEmulationPort(2, val);
5537
CheckComRCReturnRC(rc);
5538
val = (*it).valueOr <ULONG> ("IDE1SlaveEmulationPort", 3);
5539
rc = ctl->SetIDEEmulationPort(3, val);
5540
CheckComRCReturnRC(rc);
6137
if ( (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
6138
|| (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
6139
|| (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
6140
|| (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
5543
6145
/* Load the attached devices now. */
5544
rc = loadStorageDevices(ctl, (*it),
5545
aRegistered, aSnapshotId);
5546
CheckComRCReturnRC(rc);
6146
rc = loadStorageDevices(pCtl,
6150
CheckComRCReturnRC (rc);
5553
6157
* @param aNode <HardDiskAttachments> node.
5554
6158
* @param aRegistered true when the machine is being loaded on VirtualBox
5555
* startup, or when a snapshot is being loaded (wchich
5556
* currently can happen on startup only)
6159
* startup, or when a snapshot is being loaded (wchich
6160
* currently can happen on startup only)
5557
6161
* @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
5559
* @note Lock mParent for reading and hard disks for writing before calling.
6163
* @note Lock mParent for reading and hard disks for writing before calling.
5561
HRESULT Machine::loadStorageDevices (ComObjPtr<StorageController> aStorageController,
5562
const settings::Key &aNode, bool aRegistered,
5563
const Guid *aSnapshotId /* = NULL */)
6165
HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
6166
const settings::StorageController &data,
6168
const Guid *aSnapshotId /*= NULL*/)
5565
using namespace settings;
5567
AssertReturn (!aNode.isNull(), E_INVALIDARG);
5568
6170
AssertReturn ((mType == IsMachine && aSnapshotId == NULL) ||
5569
6171
(mType == IsSnapshotMachine && aSnapshotId != NULL), E_FAIL);
5571
6173
HRESULT rc = S_OK;
5573
Key::List children = aNode.keys ("AttachedDevice");
5575
if (!aRegistered && children.size() > 0)
6175
if (!aRegistered && data.llAttachedDevices.size() > 0)
5577
6176
/* when the machine is being loaded (opened) from a file, it cannot
5578
6177
* have hard disks attached (this should not happen normally,
5579
6178
* because we don't allow to attach hard disks to an unregistered
5581
return setError (E_FAIL,
5582
tr ("Unregistered machine '%ls' cannot have hard disks attached "
5583
"(found %d hard disk attachments)"),
5584
mUserData->mName.raw(), children.size());
6180
return setError(E_FAIL,
6181
tr("Unregistered machine '%ls' cannot have storage devices attached (found %d attachments)"),
6182
mUserData->mName.raw(),
6183
data.llAttachedDevices.size());
6185
/* paranoia: detect duplicate attachments */
6186
for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
6187
it != data.llAttachedDevices.end();
6190
for (settings::AttachedDevicesList::const_iterator it2 = it;
6191
it2 != data.llAttachedDevices.end();
6197
if ( (*it).lPort == (*it2).lPort
6198
&& (*it).lDevice == (*it2).lDevice)
6200
return setError(E_FAIL,
6201
tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%ls'"),
6202
aStorageController->name().raw(), (*it).lPort, (*it).lDevice, mUserData->mName.raw());
5587
for (Key::List::const_iterator it = children.begin();
5588
it != children.end(); ++ it)
6207
for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
6208
it != data.llAttachedDevices.end();
5590
Key idKey = (*it).key ("Image");
5591
/* hard disk uuid (required) */
5592
Guid uuid = idKey.value <Guid> ("uuid");
5593
/* device type (required) */
5594
const char *deviceType = (*it).stringValue ("type");
5595
/* channel (required) */
5596
LONG port = (*it).value <LONG> ("port");
5597
/* device (required) */
5598
LONG device = (*it).value <LONG> ("device");
5600
/* We support only hard disk types at the moment.
5601
* @todo: Implement support for CD/DVD drives.
5603
if (strcmp(deviceType, "HardDisk") != 0)
5604
return setError (E_FAIL,
5605
tr ("Device at position %lu:%lu is not a hard disk: %s"),
5606
port, device, deviceType);
5608
/* find a hard disk by UUID */
5609
ComObjPtr<HardDisk> hd;
5610
rc = mParent->findHardDisk(&uuid, NULL, true /* aDoSetError */, &hd);
5611
CheckComRCReturnRC (rc);
5613
AutoWriteLock hdLock (hd);
5615
if (hd->type() == HardDiskType_Immutable)
6211
const settings::AttachedDevice &dev = *it;
6212
ComObjPtr<Medium> medium;
6214
switch (dev.deviceType)
6216
case DeviceType_Floppy:
6217
/* find a floppy by UUID */
6218
if (!dev.uuid.isEmpty())
6219
rc = mParent->findFloppyImage(&dev.uuid, NULL, true /* aDoSetError */, &medium);
6220
/* find a floppy by host device name */
6221
else if (!dev.strHostDriveSrc.isEmpty())
6223
SafeIfaceArray<IMedium> drivevec;
6224
rc = mParent->host()->COMGETTER(FloppyDrives)(ComSafeArrayAsOutParam(drivevec));
6227
for (size_t i = 0; i < drivevec.size(); ++i)
6229
/// @todo eliminate this conversion
6230
ComObjPtr<Medium> med = (Medium *)drivevec[i];
6231
if ( dev.strHostDriveSrc == med->getName()
6232
|| dev.strHostDriveSrc == med->getLocation())
6242
case DeviceType_DVD:
6243
/* find a DVD by UUID */
6244
if (!dev.uuid.isEmpty())
6245
rc = mParent->findDVDImage(&dev.uuid, NULL, true /* aDoSetError */, &medium);
6246
/* find a DVD by host device name */
6247
else if (!dev.strHostDriveSrc.isEmpty())
6249
SafeIfaceArray<IMedium> drivevec;
6250
rc = mParent->host()->COMGETTER(DVDDrives)(ComSafeArrayAsOutParam(drivevec));
6253
for (size_t i = 0; i < drivevec.size(); ++i)
6255
Bstr hostDriveSrc(dev.strHostDriveSrc);
6256
/// @todo eliminate this conversion
6257
ComObjPtr<Medium> med = (Medium *)drivevec[i];
6258
if ( hostDriveSrc == med->getName()
6259
|| hostDriveSrc == med->getLocation())
6269
case DeviceType_HardDisk:
6271
/* find a hard disk by UUID */
6272
rc = mParent->findHardDisk(&dev.uuid, NULL, true /* aDoSetError */, &medium);
6275
if (mType == IsSnapshotMachine)
6277
// wrap another error message around the "cannot find hard disk" set by findHardDisk
6278
// so the user knows that the bad disk is in a snapshot somewhere
6279
com::ErrorInfo info;
6280
return setError(E_FAIL,
6281
tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
6283
info.getText().raw());
6289
AutoWriteLock hdLock(medium);
6291
if (medium->getType() == MediumType_Immutable)
6293
if (mType == IsSnapshotMachine)
6294
return setError(E_FAIL,
6295
tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
6296
"of the virtual machine '%ls' ('%s')"),
6297
medium->getLocationFull().raw(),
6300
mUserData->mName.raw(),
6301
mData->m_strConfigFileFull.raw());
6303
return setError(E_FAIL,
6304
tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%ls' ('%s')"),
6305
medium->getLocationFull().raw(),
6307
mUserData->mName.raw(),
6308
mData->m_strConfigFileFull.raw());
6311
if ( mType != IsSnapshotMachine
6312
&& medium->getChildren().size() != 0
6314
return setError(E_FAIL,
6315
tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%ls' ('%s') "
6316
"because it has %d differencing child hard disks"),
6317
medium->getLocationFull().raw(),
6319
mUserData->mName.raw(),
6320
mData->m_strConfigFileFull.raw(),
6321
medium->getChildren().size());
6323
if (findAttachment(mMediaData->mAttachments,
6325
return setError(E_FAIL,
6326
tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%ls' ('%s')"),
6327
medium->getLocationFull().raw(),
6329
mUserData->mName.raw(),
6330
mData->m_strConfigFileFull.raw());
6336
return setError(E_FAIL,
6337
tr("Device with unknown type is attached to the virtual machine '%s' ('%s')"),
6338
medium->getLocationFull().raw(),
6339
mUserData->mName.raw(),
6340
mData->m_strConfigFileFull.raw());
6346
const Bstr controllerName = aStorageController->name();
6347
ComObjPtr<MediumAttachment> pAttachment;
6348
pAttachment.createObject();
6349
rc = pAttachment->init(this,
6356
CheckComRCBreakRC(rc);
6358
/* associate the medium with this machine and snapshot */
6359
if (!medium.isNull())
5617
6361
if (mType == IsSnapshotMachine)
5618
return setError (E_FAIL,
5619
tr ("Immutable hard disk '%ls' with UUID {%RTuuid} cannot be "
5620
"directly attached to snapshot with UUID {%RTuuid} "
5621
"of the virtual machine '%ls' ('%ls')"),
5622
hd->locationFull().raw(), uuid.raw(),
5624
mUserData->mName.raw(), mData->mConfigFileFull.raw());
5626
return setError (E_FAIL,
5627
tr ("Immutable hard disk '%ls' with UUID {%RTuuid} cannot be "
5628
"directly attached to the virtual machine '%ls' ('%ls')"),
5629
hd->locationFull().raw(), uuid.raw(),
5630
mUserData->mName.raw(), mData->mConfigFileFull.raw());
5633
if (mType != IsSnapshotMachine && hd->children().size() != 0)
5634
return setError (E_FAIL,
5635
tr ("Hard disk '%ls' with UUID {%RTuuid} cannot be directly "
5636
"attached to the virtual machine '%ls' ('%ls') "
5637
"because it has %d differencing child hard disks"),
5638
hd->locationFull().raw(), uuid.raw(),
5639
mUserData->mName.raw(), mData->mConfigFileFull.raw(),
5640
hd->children().size());
5642
if (std::find_if (mHDData->mAttachments.begin(),
5643
mHDData->mAttachments.end(),
5644
HardDiskAttachment::RefersTo (hd)) !=
5645
mHDData->mAttachments.end())
5647
return setError (E_FAIL,
5648
tr ("Hard disk '%ls' with UUID {%RTuuid} is already attached "
5649
"to the virtual machine '%ls' ('%ls')"),
5650
hd->locationFull().raw(), uuid.raw(),
5651
mUserData->mName.raw(), mData->mConfigFileFull.raw());
5654
const Bstr controllerName = aStorageController->name();
5655
ComObjPtr<HardDiskAttachment> attachment;
5656
attachment.createObject();
5657
rc = attachment->init (hd, controllerName, port, device);
5658
CheckComRCBreakRC (rc);
5660
/* associate the hard disk with this machine and snapshot */
5661
if (mType == IsSnapshotMachine)
5662
rc = hd->attachTo (mData->mUuid, *aSnapshotId);
5664
rc = hd->attachTo (mData->mUuid);
5666
AssertComRCBreakRC (rc);
5668
/* backup mHDData to let registeredInit() properly rollback on failure
6362
rc = medium->attachTo(mData->mUuid, *aSnapshotId);
6364
rc = medium->attachTo(mData->mUuid);
6369
/* backup mMediaData to let registeredInit() properly rollback on failure
5669
6370
* (= limited accessibility) */
5672
mHDData->mAttachments.push_back (attachment);
6372
mMediaData.backup();
6373
mMediaData->mAttachments.push_back(pAttachment);
5679
* Searches for a <Snapshot> node for the given snapshot.
5680
* If the search is successful, \a aSnapshotNode will contain the found node.
5681
* In this case, \a aSnapshotsNode can be NULL meaning the found node is a
5682
* direct child of \a aMachineNode.
5684
* If the search fails, a failure is returned and both \a aSnapshotsNode and
5685
* \a aSnapshotNode are set to 0.
5687
* @param aSnapshot Snapshot to search for.
5688
* @param aMachineNode <Machine> node to start from.
5689
* @param aSnapshotsNode <Snapshots> node containing the found <Snapshot> node
5690
* (may be NULL if the caller is not interested).
5691
* @param aSnapshotNode Found <Snapshot> node.
5693
HRESULT Machine::findSnapshotNode (Snapshot *aSnapshot, settings::Key &aMachineNode,
5694
settings::Key *aSnapshotsNode,
5695
settings::Key *aSnapshotNode)
5697
using namespace settings;
5699
AssertReturn (aSnapshot && !aMachineNode.isNull()
5700
&& aSnapshotNode != NULL, E_FAIL);
5703
aSnapshotsNode->setNull();
5704
aSnapshotNode->setNull();
5706
// build the full uuid path (from the top parent to the given snapshot)
5707
std::list <Guid> path;
5709
ComObjPtr <Snapshot> parent = aSnapshot;
5712
path.push_front (parent->data().mId);
5713
parent = parent->parent();
5717
Key snapshotsNode = aMachineNode;
5720
for (std::list <Guid>::const_iterator it = path.begin();
5724
if (!snapshotNode.isNull())
5726
/* proceed to the nested <Snapshots> node */
5727
snapshotsNode = snapshotNode.key ("Snapshots");
5728
snapshotNode.setNull();
5731
AssertReturn (!snapshotsNode.isNull(), E_FAIL);
5733
Key::List children = snapshotsNode.keys ("Snapshot");
5734
for (Key::List::const_iterator ch = children.begin();
5735
ch != children.end();
5738
Guid id = (*ch).value <Guid> ("uuid");
5741
/* pass over to the outer loop */
5747
if (!snapshotNode.isNull())
5750
/* the next uuid is not found, no need to continue... */
5751
AssertFailedBreak();
5754
// we must always succesfully find the node
5755
AssertReturn (!snapshotNode.isNull(), E_FAIL);
5756
AssertReturn (!snapshotsNode.isNull(), E_FAIL);
5758
if (aSnapshotsNode && (snapshotsNode != aMachineNode))
5759
*aSnapshotsNode = snapshotsNode;
5760
*aSnapshotNode = snapshotNode;
5766
* Returns the snapshot with the given UUID or fails of no such snapshot.
6380
* Returns the snapshot with the given UUID or fails of no such snapshot exists.
5768
6382
* @param aId snapshot UUID to find (empty UUID refers the first snapshot)
5769
6383
* @param aSnapshot where to return the found snapshot
5770
6384
* @param aSetError true to set extended error info on failure
5772
HRESULT Machine::findSnapshot (const Guid &aId, ComObjPtr <Snapshot> &aSnapshot,
5773
bool aSetError /* = false */)
6386
HRESULT Machine::findSnapshot(const Guid &aId,
6387
ComObjPtr<Snapshot> &aSnapshot,
6388
bool aSetError /* = false */)
6390
AutoReadLock chlock(snapshotsTreeLockHandle());
5775
6392
if (!mData->mFirstSnapshot)
5778
return setError (E_FAIL,
5779
tr ("This machine does not have any snapshots"));
6395
return setError(E_FAIL,
6396
tr("This machine does not have any snapshots"));
5783
6400
if (aId.isEmpty())
5784
6401
aSnapshot = mData->mFirstSnapshot;
5786
aSnapshot = mData->mFirstSnapshot->findChildOrSelf (aId);
6403
aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId);
5788
6405
if (!aSnapshot)
5791
return setError (E_FAIL,
5792
tr ("Could not find a snapshot with UUID {%s}"),
5793
aId.toString().raw());
6408
return setError(E_FAIL,
6409
tr("Could not find a snapshot with UUID {%s}"),
6410
aId.toString().raw());
6168
6773
* creating a new settings file if this is a new machine. */
6169
6774
bool isRenamed = false;
6170
6775
bool isNew = false;
6171
rc = prepareSaveSettings (isRenamed, isNew);
6172
CheckComRCReturnRC (rc);
6776
rc = prepareSaveSettings(isRenamed, isNew);
6777
CheckComRCReturnRC(rc);
6176
using namespace settings;
6177
using namespace xml;
6179
/* this object is locked for writing to prevent concurrent reads and writes */
6180
File file (mData->mHandleCfgFile, Utf8Str (mData->mConfigFileFull));
6181
XmlTreeBackend tree;
6183
/* The newly created settings file is incomplete therefore we turn off
6184
* validation. The rest is like in loadSettingsTree_ForUpdate().*/
6185
rc = VirtualBox::loadSettingsTree (tree, file,
6186
!isNew /* aValidate */,
6187
false /* aCatchLoadErrors */,
6188
false /* aAddDefaults */);
6189
CheckComRCThrowRC (rc);
6191
Key machineNode = tree.rootKey().createKey ("Machine");
6193
/* uuid (required) */
6194
Assert (!mData->mUuid.isEmpty());
6195
machineNode.setValue <Guid> ("uuid", mData->mUuid);
6197
/* name (required) */
6198
Assert (!mUserData->mName.isEmpty());
6199
machineNode.setValue <Bstr> ("name", mUserData->mName);
6201
/* nameSync (optional, default is true) */
6202
machineNode.setValueOr <bool> ("nameSync", !!mUserData->mNameSync, true);
6204
/* Description node (optional) */
6205
if (!mUserData->mDescription.isNull())
6207
Key descNode = machineNode.createKey ("Description");
6208
descNode.setKeyValue <Bstr> (mUserData->mDescription);
6212
Key descNode = machineNode.findKey ("Description");
6213
if (!descNode.isNull())
6217
/* OSType (required) */
6218
machineNode.setValue <Bstr> ("OSType", mUserData->mOSTypeId);
6220
/* stateFile (optional) */
6221
/// @todo The reason for MachineState_Restoring below:
6222
/// PushGuestProperties() is always called from Console::powerDown()
6223
/// (including the case when restoring from the saved state fails) and
6224
/// calls SaveSettings() to save guest properties. Since the saved state
6225
/// file is still present there (and should be kept), we must save it
6226
/// while in Restoring state too. However, calling SaveSettings() from
6227
/// PushGuestProperties() is wrong in the first place. A proper way is
6228
/// to only save guest properties node and not involve the whole save
6230
if (mData->mMachineState == MachineState_Saved ||
6231
mData->mMachineState == MachineState_Restoring)
6233
Assert (!mSSData->mStateFilePath.isEmpty());
6781
mData->m_pMachineConfigFile->uuid = mData->mUuid;
6782
mData->m_pMachineConfigFile->strName = mUserData->mName;
6783
mData->m_pMachineConfigFile->fNameSync = !!mUserData->mNameSync;
6784
mData->m_pMachineConfigFile->strDescription = mUserData->mDescription;
6785
mData->m_pMachineConfigFile->strOsType = mUserData->mOSTypeId;
6787
if ( mData->mMachineState == MachineState_Saved
6788
|| mData->mMachineState == MachineState_Restoring
6789
// when deleting a snapshot we may or may not have a saved state in the current state,
6790
// so let's not assert here please
6791
|| ( (mData->mMachineState == MachineState_DeletingSnapshot)
6792
&& (!mSSData->mStateFilePath.isEmpty())
6796
Assert(!mSSData->mStateFilePath.isEmpty());
6234
6797
/* try to make the file name relative to the settings file dir */
6235
Utf8Str stateFilePath = mSSData->mStateFilePath;
6236
calculateRelativePath (stateFilePath, stateFilePath);
6237
machineNode.setStringValue ("stateFile", stateFilePath);
6241
Assert (mSSData->mStateFilePath.isNull());
6242
machineNode.zapValue ("stateFile");
6245
/* currentSnapshot ID (optional) */
6246
if (!mData->mCurrentSnapshot.isNull())
6248
Assert (!mData->mFirstSnapshot.isNull());
6249
machineNode.setValue <Guid> ("currentSnapshot",
6250
mData->mCurrentSnapshot->data().mId);
6254
Assert (mData->mFirstSnapshot.isNull());
6255
machineNode.zapValue ("currentSnapshot");
6258
/* snapshotFolder (optional) */
6259
/// @todo use the Bstr::NullOrEmpty constant and setValueOr
6260
if (!mUserData->mSnapshotFolder.isEmpty())
6261
machineNode.setValue <Bstr> ("snapshotFolder", mUserData->mSnapshotFolder);
6263
machineNode.zapValue ("snapshotFolder");
6265
/* currentStateModified (optional, default is true) */
6266
machineNode.setValueOr <bool> ("currentStateModified",
6267
!!currentStateModified, true);
6269
/* lastStateChange */
6270
machineNode.setValue <RTTIMESPEC> ("lastStateChange",
6271
mData->mLastStateChange);
6273
/* set the aborted attribute when appropriate, defaults to false */
6274
machineNode.setValueOr <bool> ("aborted",
6275
mData->mMachineState == MachineState_Aborted,
6278
/* Hardware node (required) */
6280
/* first, delete the entire node if exists */
6281
Key hwNode = machineNode.findKey ("Hardware");
6282
if (!hwNode.isNull())
6284
/* then recreate it */
6285
hwNode = machineNode.createKey ("Hardware");
6287
rc = saveHardware (hwNode);
6288
CheckComRCThrowRC (rc);
6291
/* StorageControllers node (required) */
6293
/* first, delete the entire node if exists */
6294
Key storageNode = machineNode.findKey ("StorageControllers");
6295
if (!storageNode.isNull())
6297
/* then recreate it */
6298
storageNode = machineNode.createKey ("StorageControllers");
6300
rc = saveStorageControllers (storageNode);
6301
CheckComRCThrowRC (rc);
6304
/* ask to save all snapshots when the machine name was changed since
6305
* it may affect saved state file paths for online snapshots (see
6306
* #openConfigLoader() for details) */
6309
rc = saveSnapshotSettingsWorker (machineNode, NULL,
6310
SaveSS_UpdateAllOp);
6311
CheckComRCThrowRC (rc);
6314
/* save the settings on success */
6315
rc = VirtualBox::saveSettingsTree (tree, file,
6316
mData->mSettingsFileVersion);
6317
CheckComRCThrowRC (rc);
6798
calculateRelativePath(mSSData->mStateFilePath, mData->m_pMachineConfigFile->strStateFile);
6802
Assert(mSSData->mStateFilePath.isEmpty());
6803
mData->m_pMachineConfigFile->strStateFile.setNull();
6806
if (mData->mCurrentSnapshot)
6807
mData->m_pMachineConfigFile->uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
6809
mData->m_pMachineConfigFile->uuidCurrentSnapshot.clear();
6811
mData->m_pMachineConfigFile->strSnapshotFolder = mUserData->mSnapshotFolder;
6812
mData->m_pMachineConfigFile->fCurrentStateModified = !!currentStateModified;
6813
mData->m_pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
6814
mData->m_pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
6815
/// @todo Live Migration: mData->m_pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
6817
mData->m_pMachineConfigFile->fTeleporterEnabled = !!mUserData->mTeleporterEnabled;
6818
mData->m_pMachineConfigFile->uTeleporterPort = mUserData->mTeleporterPort;
6819
mData->m_pMachineConfigFile->strTeleporterAddress = mUserData->mTeleporterAddress;
6820
mData->m_pMachineConfigFile->strTeleporterPassword = mUserData->mTeleporterPassword;
6822
rc = saveHardware(mData->m_pMachineConfigFile->hardwareMachine);
6823
CheckComRCThrowRC(rc);
6825
rc = saveStorageControllers(mData->m_pMachineConfigFile->storageMachine);
6826
CheckComRCThrowRC(rc);
6829
rc = saveAllSnapshots();
6830
CheckComRCThrowRC(rc);
6832
// now spit it all out
6833
mData->m_pMachineConfigFile->write(mData->m_strConfigFileFull);
6319
6835
catch (HRESULT err)
6411
* Performs the specified operation on the given snapshot
6412
* in the settings file represented by \a aMachineNode.
6414
* If \a aOpFlags = SaveSS_UpdateAllOp, \a aSnapshot can be NULL to indicate
6415
* that the whole tree of the snapshots should be updated in <Machine>.
6416
* One particular case is when the last (and the only) snapshot should be
6417
* removed (it is so when both mCurrentSnapshot and mFirstSnapshot are NULL).
6419
* \a aOp may be just SaveSS_UpdateCurrentId if only the currentSnapshot
6420
* attribute of <Machine> needs to be updated.
6422
* @param aMachineNode <Machine> node in the opened settings file.
6423
* @param aSnapshot Snapshot to operate on.
6424
* @param aOpFlags Operation to perform, one of SaveSS_NoOp, SaveSS_AddOp
6425
* or SaveSS_UpdateAttrsOp possibly combined with
6426
* SaveSS_UpdateCurrentId.
6428
* @note Must be called with this object locked for writing.
6429
* Locks child objects.
6431
HRESULT Machine::saveSnapshotSettingsWorker (settings::Key &aMachineNode,
6432
Snapshot *aSnapshot, int aOpFlags)
6434
using namespace settings;
6436
AssertReturn (!aMachineNode.isNull(), E_FAIL);
6438
AssertReturn (isWriteLockOnCurrentThread(), E_FAIL);
6440
int op = aOpFlags & SaveSS_OpMask;
6442
(aSnapshot && (op == SaveSS_AddOp || op == SaveSS_UpdateAttrsOp ||
6443
op == SaveSS_UpdateAllOp)) ||
6444
(!aSnapshot && ((op == SaveSS_NoOp && (aOpFlags & SaveSS_CurrentId)) ||
6445
op == SaveSS_UpdateAllOp)),
6450
bool recreateWholeTree = false;
6454
if (op == SaveSS_NoOp)
6457
/* quick path: recreate the whole tree of the snapshots */
6458
if (op == SaveSS_UpdateAllOp && aSnapshot == NULL)
6460
/* first, delete the entire root snapshot node if it exists */
6461
Key snapshotNode = aMachineNode.findKey ("Snapshot");
6462
if (!snapshotNode.isNull())
6465
/* second, if we have any snapshots left, substitute aSnapshot
6466
* with the first snapshot to recreate the whole tree, otherwise
6468
if (mData->mFirstSnapshot)
6470
aSnapshot = mData->mFirstSnapshot;
6471
recreateWholeTree = true;
6477
Assert (!!aSnapshot);
6478
ComObjPtr <Snapshot> parent = aSnapshot->parent();
6480
if (op == SaveSS_AddOp)
6486
rc = findSnapshotNode (parent, aMachineNode, NULL, &parentNode);
6487
CheckComRCBreakRC (rc);
6489
ComAssertBreak (!parentNode.isNull(), rc = E_FAIL);
6496
if (!parentNode.isNull())
6497
snapshotsNode = parentNode.createKey ("Snapshots");
6499
snapshotsNode = aMachineNode;
6502
Key snapshotNode = snapshotsNode.appendKey ("Snapshot");
6503
rc = saveSnapshot (snapshotNode, aSnapshot, false /* aAttrsOnly */);
6504
CheckComRCBreakRC (rc);
6506
/* when a new snapshot is added, this means diffs were created
6507
* for every normal/immutable hard disk of the VM, so we need to
6508
* save the current hard disk attachments */
6510
Key storageNode = aMachineNode.findKey ("StorageControllers");
6511
if (!storageNode.isNull())
6513
storageNode = aMachineNode.createKey ("StorageControllers");
6515
rc = saveStorageControllers (storageNode);
6516
CheckComRCBreakRC (rc);
6518
if (mHDData->mAttachments.size() != 0)
6520
/* If we have one or more attachments then we definitely
6521
* created diffs for them and associated new diffs with
6522
* current settngs. So, since we don't use saveSettings(),
6523
* we need to inform callbacks manually. */
6524
if (mType == IsSessionMachine)
6525
mParent->onMachineDataChange (mData->mUuid);
6535
Assert ((op == SaveSS_UpdateAttrsOp && !recreateWholeTree) ||
6536
op == SaveSS_UpdateAllOp);
6541
if (!recreateWholeTree)
6543
rc = findSnapshotNode (aSnapshot, aMachineNode,
6544
&snapshotsNode, &snapshotNode);
6545
CheckComRCBreakRC (rc);
6548
if (snapshotsNode.isNull())
6549
snapshotsNode = aMachineNode;
6551
if (op == SaveSS_UpdateAttrsOp)
6552
rc = saveSnapshot (snapshotNode, aSnapshot, true /* aAttrsOnly */);
6555
if (!snapshotNode.isNull())
6558
snapshotNode = snapshotsNode.appendKey ("Snapshot");
6559
rc = saveSnapshot (snapshotNode, aSnapshot, false /* aAttrsOnly */);
6560
CheckComRCBreakRC (rc);
6567
/* update currentSnapshot when appropriate */
6568
if (aOpFlags & SaveSS_CurrentId)
6570
if (!mData->mCurrentSnapshot.isNull())
6571
aMachineNode.setValue <Guid> ("currentSnapshot",
6572
mData->mCurrentSnapshot->data().mId);
6574
aMachineNode.zapValue ("currentSnapshot");
6576
if (aOpFlags & SaveSS_CurStateModified)
6578
/* defaults to true */
6579
aMachineNode.setValueOr <bool> ("currentStateModified",
6580
!!mData->mCurrentStateModified, true);
6588
* Saves the given snapshot and all its children (unless \a aAttrsOnly is true).
6589
* It is assumed that the given node is empty (unless \a aAttrsOnly is true).
6591
* @param aNode <Snapshot> node to save the snapshot to.
6592
* @param aSnapshot Snapshot to save.
6593
* @param aAttrsOnly If true, only updatge user-changeable attrs.
6595
HRESULT Machine::saveSnapshot (settings::Key &aNode, Snapshot *aSnapshot, bool aAttrsOnly)
6597
using namespace settings;
6599
AssertReturn (!aNode.isNull() && aSnapshot, E_INVALIDARG);
6600
AssertReturn (mType == IsMachine || mType == IsSessionMachine, E_FAIL);
6602
/* uuid (required) */
6604
aNode.setValue <Guid> ("uuid", aSnapshot->data().mId);
6606
/* name (required) */
6607
aNode.setValue <Bstr> ("name", aSnapshot->data().mName);
6609
/* timeStamp (required) */
6610
aNode.setValue <RTTIMESPEC> ("timeStamp", aSnapshot->data().mTimeStamp);
6612
/* Description node (optional) */
6613
if (!aSnapshot->data().mDescription.isNull())
6615
Key descNode = aNode.createKey ("Description");
6616
descNode.setKeyValue <Bstr> (aSnapshot->data().mDescription);
6620
Key descNode = aNode.findKey ("Description");
6621
if (!descNode.isNull())
6628
/* stateFile (optional) */
6629
if (aSnapshot->stateFilePath())
6631
/* try to make the file name relative to the settings file dir */
6632
Utf8Str stateFilePath = aSnapshot->stateFilePath();
6633
calculateRelativePath (stateFilePath, stateFilePath);
6634
aNode.setStringValue ("stateFile", stateFilePath);
6638
ComObjPtr <SnapshotMachine> snapshotMachine = aSnapshot->data().mMachine;
6639
ComAssertRet (!snapshotMachine.isNull(), E_FAIL);
6643
Key hwNode = aNode.createKey ("Hardware");
6644
HRESULT rc = snapshotMachine->saveHardware (hwNode);
6645
CheckComRCReturnRC (rc);
6648
/* save hard disks. */
6650
Key storageNode = aNode.createKey ("StorageControllers");
6651
HRESULT rc = snapshotMachine->saveStorageControllers (storageNode);
6652
CheckComRCReturnRC (rc);
6658
AutoWriteLock listLock (aSnapshot->childrenLock ());
6660
if (aSnapshot->children().size())
6662
Key snapshotsNode = aNode.createKey ("Snapshots");
6666
for (Snapshot::SnapshotList::const_iterator it = aSnapshot->children().begin();
6667
it != aSnapshot->children().end();
6670
Key snapshotNode = snapshotsNode.createKey ("Snapshot");
6671
rc = saveSnapshot (snapshotNode, (*it), aAttrsOnly);
6672
CheckComRCReturnRC (rc);
6681
6910
* Saves the VM hardware configuration. It is assumed that the
6682
6911
* given node is empty.
6684
6913
* @param aNode <Hardware> node to save the VM hardware confguration to.
6686
HRESULT Machine::saveHardware (settings::Key &aNode)
6915
HRESULT Machine::saveHardware(settings::Hardware &data)
6688
using namespace settings;
6690
AssertReturn (!aNode.isNull(), E_INVALIDARG);
6692
6917
HRESULT rc = S_OK;
6694
/* The hardware version attribute (optional).
6695
Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
6697
Utf8Str hwVersion = mHWData->mHWVersion;
6698
if ( hwVersion.compare ("1") == 0
6699
&& mSSData->mStateFilePath.isEmpty())
6700
mHWData->mHWVersion = hwVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some other point needs to be found where this can be done. */
6701
if (hwVersion.compare ("2") == 0) /** @todo get the default from the schema if possible. */
6702
aNode.zapValue ("version");
6704
aNode.setStringValue ("version", hwVersion.raw());
6707
/* CPU (optional, but always created atm) */
6709
Key cpuNode = aNode.createKey ("CPU");
6710
Key hwVirtExNode = cpuNode.createKey ("HardwareVirtEx");
6711
const char *value = NULL;
6712
if (mHWData->mHWVirtExEnabled)
6716
hwVirtExNode.setStringValue ("enabled", value);
6718
/* Nested paging (optional, default is false) */
6719
if (mHWData->mHWVirtExNestedPagingEnabled)
6721
Key HWVirtExNestedPagingNode = cpuNode.createKey ("HardwareVirtExNestedPaging");
6722
HWVirtExNestedPagingNode.setValue <bool> ("enabled", true);
6725
/* VPID (optional, default is false) */
6726
if (mHWData->mHWVirtExVPIDEnabled)
6728
Key HWVirtExVPIDNode = cpuNode.createKey ("HardwareVirtExVPID");
6729
HWVirtExVPIDNode.setValue <bool> ("enabled", true);
6732
/* PAE (optional, default is false) */
6733
if (mHWData->mPAEEnabled)
6735
Key PAENode = cpuNode.createKey ("PAE");
6736
PAENode.setValue <bool> ("enabled", true);
6740
cpuNode.setValue <ULONG> ("count", mHWData->mCPUCount);
6743
/* memory (required) */
6745
Key memoryNode = aNode.createKey ("Memory");
6746
memoryNode.setValue <ULONG> ("RAMSize", mHWData->mMemorySize);
6749
/* boot (required) */
6751
Key bootNode = aNode.createKey ("Boot");
6753
for (ULONG pos = 0; pos < RT_ELEMENTS (mHWData->mBootOrder); ++ pos)
6755
const char *device = NULL;
6756
switch (mHWData->mBootOrder [pos])
6758
case DeviceType_Null:
6759
/* skip, this is allowed for <Order> nodes
6760
* when loading, the default value NoDevice will remain */
6762
case DeviceType_Floppy: device = "Floppy"; break;
6763
case DeviceType_DVD: device = "DVD"; break;
6764
case DeviceType_HardDisk: device = "HardDisk"; break;
6765
case DeviceType_Network: device = "Network"; break;
6768
ComAssertMsgFailedRet (("Invalid boot device: %d",
6769
mHWData->mBootOrder [pos]),
6774
Key orderNode = bootNode.appendKey ("Order");
6775
orderNode.setValue <ULONG> ("position", pos + 1);
6776
orderNode.setStringValue ("device", device);
6780
/* display (required) */
6782
Key displayNode = aNode.createKey ("Display");
6783
displayNode.setValue <ULONG> ("VRAMSize", mHWData->mVRAMSize);
6784
displayNode.setValue <ULONG> ("monitorCount", mHWData->mMonitorCount);
6785
displayNode.setValue <bool> ("accelerate3D", !!mHWData->mAccelerate3DEnabled);
6921
/* The hardware version attribute (optional).
6922
Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
6923
if ( mHWData->mHWVersion == "1"
6924
&& mSSData->mStateFilePath.isEmpty()
6926
mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some other point needs to be found where this can be done. */
6928
data.strVersion = mHWData->mHWVersion;
6929
data.uuid = mHWData->mHardwareUUID;
6932
data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
6933
data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
6934
data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
6935
data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
6936
data.fPAE = !!mHWData->mPAEEnabled;
6937
data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
6939
/* Standard and Extended CPUID leafs. */
6940
data.llCpuIdLeafs.clear();
6941
for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
6943
if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
6944
data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
6946
for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
6948
if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
6949
data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
6952
data.cCPUs = mHWData->mCPUCount;
6955
data.ulMemorySizeMB = mHWData->mMemorySize;
6958
data.firmwareType = mHWData->mFirmwareType;
6961
data.mapBootOrder.clear();
6963
i < RT_ELEMENTS(mHWData->mBootOrder);
6965
data.mapBootOrder[i] = mHWData->mBootOrder[i];
6968
data.ulVRAMSizeMB = mHWData->mVRAMSize;
6969
data.cMonitors = mHWData->mMonitorCount;
6970
data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
6971
data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
6788
6973
#ifdef VBOX_WITH_VRDP
6789
/* VRDP settings (optional) */
6790
rc = mVRDPServer->saveSettings (aNode);
6791
CheckComRCReturnRC (rc);
6974
/* VRDP settings (optional) */
6975
rc = mVRDPServer->saveSettings(data.vrdpSettings);
6976
CheckComRCThrowRC(rc);
6794
/* BIOS (required) */
6795
rc = mBIOSSettings->saveSettings (aNode);
6796
CheckComRCReturnRC (rc);
6798
/* DVD drive (required) */
6799
rc = mDVDDrive->saveSettings (aNode);
6800
CheckComRCReturnRC (rc);
6802
/* Flooppy drive (required) */
6803
rc = mFloppyDrive->saveSettings (aNode);
6804
CheckComRCReturnRC (rc);
6806
/* USB Controller (required) */
6807
rc = mUSBController->saveSettings (aNode);
6808
CheckComRCReturnRC (rc);
6810
/* Network adapters (required) */
6812
Key nwNode = aNode.createKey ("Network");
6814
for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); ++ slot)
6816
Key adapterNode = nwNode.appendKey ("Adapter");
6818
adapterNode.setValue <ULONG> ("slot", slot);
6820
rc = mNetworkAdapters [slot]->saveSettings (adapterNode);
6821
CheckComRCReturnRC (rc);
6827
Key serialNode = aNode.createKey ("UART");
6829
for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); ++ slot)
6831
Key portNode = serialNode.appendKey ("Port");
6833
portNode.setValue <ULONG> ("slot", slot);
6835
rc = mSerialPorts [slot]->saveSettings (portNode);
6836
CheckComRCReturnRC (rc);
6840
/* Parallel ports */
6842
Key parallelNode = aNode.createKey ("LPT");
6844
for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); ++ slot)
6846
Key portNode = parallelNode.appendKey ("Port");
6848
portNode.setValue <ULONG> ("slot", slot);
6850
rc = mParallelPorts [slot]->saveSettings (portNode);
6851
CheckComRCReturnRC (rc);
6856
rc = mAudioAdapter->saveSettings (aNode);
6857
CheckComRCReturnRC (rc);
6859
/* Shared folders */
6861
Key sharedFoldersNode = aNode.createKey ("SharedFolders");
6979
/* BIOS (required) */
6980
rc = mBIOSSettings->saveSettings(data.biosSettings);
6981
CheckComRCThrowRC(rc);
6983
/* USB Controller (required) */
6984
rc = mUSBController->saveSettings(data.usbController);
6985
CheckComRCThrowRC(rc);
6987
/* Network adapters (required) */
6988
data.llNetworkAdapters.clear();
6989
for (ULONG slot = 0;
6990
slot < RT_ELEMENTS(mNetworkAdapters);
6993
settings::NetworkAdapter nic;
6995
rc = mNetworkAdapters[slot]->saveSettings(nic);
6996
CheckComRCThrowRC(rc);
6998
data.llNetworkAdapters.push_back(nic);
7002
data.llSerialPorts.clear();
7003
for (ULONG slot = 0;
7004
slot < RT_ELEMENTS(mSerialPorts);
7007
settings::SerialPort s;
7009
rc = mSerialPorts[slot]->saveSettings(s);
7010
CheckComRCReturnRC (rc);
7012
data.llSerialPorts.push_back(s);
7015
/* Parallel ports */
7016
data.llParallelPorts.clear();
7017
for (ULONG slot = 0;
7018
slot < RT_ELEMENTS(mParallelPorts);
7021
settings::ParallelPort p;
7023
rc = mParallelPorts[slot]->saveSettings(p);
7024
CheckComRCReturnRC (rc);
7026
data.llParallelPorts.push_back(p);
7030
rc = mAudioAdapter->saveSettings(data.audioAdapter);
7031
CheckComRCReturnRC (rc);
7033
/* Shared folders */
7034
data.llSharedFolders.clear();
6863
7035
for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
6864
it != mHWData->mSharedFolders.end();
6867
ComObjPtr <SharedFolder> folder = *it;
6869
Key folderNode = sharedFoldersNode.appendKey ("SharedFolder");
6871
/* all are mandatory */
6872
folderNode.setValue <Bstr> ("name", folder->name());
6873
folderNode.setValue <Bstr> ("hostPath", folder->hostPath());
6874
folderNode.setValue <bool> ("writable", !!folder->writable());
6880
Key clipNode = aNode.createKey ("Clipboard");
6882
const char *modeStr = "Disabled";
6883
switch (mHWData->mClipboardMode)
6885
case ClipboardMode_Disabled:
6886
/* already assigned */
6888
case ClipboardMode_HostToGuest:
6889
modeStr = "HostToGuest";
6891
case ClipboardMode_GuestToHost:
6892
modeStr = "GuestToHost";
6894
case ClipboardMode_Bidirectional:
6895
modeStr = "Bidirectional";
6898
ComAssertMsgFailedRet (("Clipboard mode %d is invalid",
6899
mHWData->mClipboardMode),
6902
clipNode.setStringValue ("mode", modeStr);
6907
Key guestNode = aNode.createKey ("Guest");
6909
guestNode.setValue <ULONG> ("memoryBalloonSize",
6910
mHWData->mMemoryBalloonSize);
6911
guestNode.setValue <ULONG> ("statisticsUpdateInterval",
6912
mHWData->mStatisticsUpdateInterval);
7036
it != mHWData->mSharedFolders.end();
7039
ComObjPtr<SharedFolder> pFolder = *it;
7040
settings::SharedFolder sf;
7041
sf.strName = pFolder->getName();
7042
sf.strHostPath = pFolder->getHostPath();
7043
sf.fWritable = !!pFolder->isWritable();
7045
data.llSharedFolders.push_back(sf);
7049
data.clipboardMode = mHWData->mClipboardMode;
7052
data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
7053
data.ulStatisticsUpdateInterval = mHWData->mStatisticsUpdateInterval;
7056
data.llGuestProperties.clear();
6915
7057
#ifdef VBOX_WITH_GUEST_PROPS
6916
/* Guest properties */
6919
using namespace guestProp;
6921
Key guestPropertiesNode = aNode.createKey ("GuestProperties");
6923
7058
for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
6924
it != mHWData->mGuestProperties.end(); ++it)
7059
it != mHWData->mGuestProperties.end();
6926
7062
HWData::GuestProperty property = *it;
6928
Key propertyNode = guestPropertiesNode.appendKey ("GuestProperty");
6929
char szFlags[MAX_FLAGS_LEN + 1];
7064
settings::GuestProperty prop;
7065
prop.strName = property.strName;
7066
prop.strValue = property.strValue;
7067
prop.timestamp = property.mTimestamp;
7068
char szFlags[guestProp::MAX_FLAGS_LEN + 1];
7069
guestProp::writeFlags(property.mFlags, szFlags);
7070
prop.strFlags = szFlags;
6931
propertyNode.setValue <Bstr> ("name", property.mName);
6932
propertyNode.setValue <Bstr> ("value", property.mValue);
6933
propertyNode.setValue <ULONG64> ("timestamp", property.mTimestamp);
6934
writeFlags (property.mFlags, szFlags);
6935
Bstr flags (szFlags);
6937
return E_OUTOFMEMORY;
6938
propertyNode.setValue <Bstr> ("flags", flags);
7072
data.llGuestProperties.push_back(prop);
6941
if (emptyStr.isNull())
6942
return E_OUTOFMEMORY;
6943
guestPropertiesNode.setValueOr <Bstr> ("notificationPatterns",
6944
mHWData->mGuestPropertyNotificationPatterns,
7075
data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
7076
#endif /* VBOX_WITH_GUEST_PROPS defined */
6947
catch (xml::ENoMemory e)
7078
catch(std::bad_alloc &)
6949
7080
return E_OUTOFMEMORY;
6951
#endif /* VBOX_WITH_GUEST_PROPS defined */
7435
7651
* @note Locks this object for writing!
7437
void Machine::fixupHardDisks(bool aCommit, bool aOnline /*= false*/)
7653
void Machine::fixupMedia(bool aCommit, bool aOnline /*= false*/)
7439
AutoCaller autoCaller (this);
7655
AutoCaller autoCaller(this);
7440
7656
AssertComRCReturnVoid (autoCaller.rc());
7442
AutoWriteLock alock (this);
7658
AutoWriteLock alock(this);
7660
LogFlowThisFunc(("Entering, aCommit=%d, aOnline=%d\n", aCommit, aOnline));
7444
7664
/* no attach/detach operations -- nothing to do */
7445
if (!mHDData.isBackedUp())
7665
if (!mMediaData.isBackedUp())
7452
HDData::AttachmentList &oldAtts =
7453
mHDData.backedUpData()->mAttachments;
7670
MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
7455
7672
/* enumerate new attachments */
7456
for (HDData::AttachmentList::const_iterator
7457
it = mHDData->mAttachments.begin();
7458
it != mHDData->mAttachments.end(); ++ it)
7673
for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7674
it != mMediaData->mAttachments.end();
7460
ComObjPtr<HardDisk> hd = (*it)->hardDisk();
7462
if ((*it)->isImplicit())
7677
MediumAttachment *pAttach = *it;
7681
Medium* pMedium = pAttach->getMedium();
7682
bool fImplicit = pAttach->isImplicit();
7684
LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
7685
(pMedium) ? pMedium->getName().raw() : "NULL",
7688
/** @todo convert all this Machine-based voodoo to MediumAttachment
7689
* based commit logic. */
7464
7692
/* convert implicit attachment to normal */
7465
(*it)->setImplicit (false);
7693
pAttach->setImplicit(false);
7697
&& pAttach->getType() == DeviceType_HardDisk
7469
rc = hd->LockWrite (NULL);
7700
rc = pMedium->LockWrite(NULL);
7472
mData->mSession.mLockedMedia.push_back (
7473
Data::Session::LockedMedia::value_type (
7474
ComPtr <IHardDisk> (hd), true));
7703
mData->mSession.mLockedMedia.push_back(
7704
Data::Session::LockedMedia::value_type(
7705
ComPtr<IMedium>(pMedium), true));
7476
7707
/* also, relock the old hard disk which is a base for the
7477
* new diff for reading if the VM is online */
7708
* new diff for reading if the VM is online */
7479
ComObjPtr<HardDisk> parent = hd->parent();
7710
ComObjPtr<Medium> parent = pMedium->getParent();
7480
7711
/* make the relock atomic */
7481
7712
AutoWriteLock parentLock (parent);
7482
rc = parent->UnlockWrite (NULL);
7484
rc = parent->LockRead (NULL);
7713
rc = parent->UnlockWrite(NULL);
7715
rc = parent->LockRead(NULL);
7487
7718
/* XXX actually we should replace the old entry in that
7488
* vector (write lock => read lock) but this would take
7489
* some effort. So lets just ignore the error code in
7490
* SessionMachine::unlockMedia(). */
7491
mData->mSession.mLockedMedia.push_back (
7719
* vector (write lock => read lock) but this would take
7720
* some effort. So lets just ignore the error code in
7721
* SessionMachine::unlockMedia(). */
7722
mData->mSession.mLockedMedia.push_back(
7492
7723
Data::Session::LockedMedia::value_type (
7493
ComPtr <IHardDisk> (parent), false));
7724
ComPtr<IMedium>(parent), false));
7499
/* was this hard disk attached before? */
7500
HDData::AttachmentList::iterator oldIt =
7501
std::find_if (oldAtts.begin(), oldAtts.end(),
7502
HardDiskAttachment::RefersTo (hd));
7503
if (oldIt != oldAtts.end())
7505
/* yes: remove from old to avoid de-association */
7506
oldAtts.erase (oldIt);
7732
/* was this medium attached before? */
7733
for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
7734
oldIt != oldAtts.end();
7737
MediumAttachment *pOldAttach = *oldIt;
7738
if (pOldAttach->getMedium().equalsTo(pMedium))
7740
LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().raw()));
7742
/* yes: remove from old to avoid de-association */
7743
oldAtts.erase(oldIt);
7510
7750
/* enumerate remaining old attachments and de-associate from the
7511
7751
* current machine state */
7512
for (HDData::AttachmentList::const_iterator it = oldAtts.begin();
7513
it != oldAtts.end(); ++ it)
7752
for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
7753
it != oldAtts.end();
7515
ComObjPtr<HardDisk> hd = (*it)->hardDisk();
7517
/* now de-associate from the current machine state */
7518
rc = hd->detachFrom (mData->mUuid);
7756
MediumAttachment *pAttach = *it;
7757
Medium* pMedium = pAttach->getMedium();
7759
/* Detach only hard disks, since DVD/floppy media is detached
7760
* instantly in MountMedium. */
7761
if (pAttach->getType() == DeviceType_HardDisk && pMedium)
7523
/* unlock since not used anymore */
7525
rc = hd->UnlockWrite (&state);
7526
/* the disk may be alredy relocked for reading above */
7527
Assert (SUCCEEDED (rc) || state == MediaState_LockedRead);
7763
LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().raw()));
7765
/* now de-associate from the current machine state */
7766
rc = pMedium->detachFrom(mData->mUuid);
7770
&& pAttach->getType() == DeviceType_HardDisk)
7772
/* unlock since not used anymore */
7773
MediumState_T state;
7774
rc = pMedium->UnlockWrite(&state);
7775
/* the disk may be alredy relocked for reading above */
7776
Assert (SUCCEEDED(rc) || state == MediumState_LockedRead);
7531
7781
/* commit the hard disk changes */
7782
mMediaData.commit();
7534
7784
if (mType == IsSessionMachine)
7536
7786
/* attach new data to the primary machine and reshare it */
7537
mPeer->mHDData.attach (mHDData);
7787
mPeer->mMediaData.attach(mMediaData);
7792
/* enumerate new attachments */
7793
for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7794
it != mMediaData->mAttachments.end();
7797
MediumAttachment *pAttach = *it;
7798
/* Fix up the backrefs for DVD/floppy media. */
7799
if (pAttach->getType() != DeviceType_HardDisk)
7801
Medium* pMedium = pAttach->getMedium();
7804
rc = pMedium->detachFrom(mData->mUuid);
7812
/* Fix up the backrefs for DVD/floppy media. */
7813
if (pAttach->getType() != DeviceType_HardDisk)
7815
Medium* pMedium = pAttach->getMedium();
7818
rc = pMedium->attachTo(mData->mUuid);
7824
/** @todo convert all this Machine-based voodoo to MediumAttachment
7825
* based rollback logic. */
7826
// @todo r=dj the below totally fails if this gets called from Machine::rollback(),
7827
// which gets called if Machine::registeredInit() fails...
7542
7828
deleteImplicitDiffs();
9055
9194
return setMachineState (MachineState_Saved);
9059
* @note Locks mParent + this object for writing.
9061
STDMETHODIMP SessionMachine::BeginTakingSnapshot (
9062
IConsole *aInitiator, IN_BSTR aName, IN_BSTR aDescription,
9063
IProgress *aProgress, BSTR *aStateFilePath,
9064
IProgress **aServerProgress)
9066
LogFlowThisFuncEnter();
9068
AssertReturn (aInitiator && aName, E_INVALIDARG);
9069
AssertReturn (aStateFilePath && aServerProgress, E_POINTER);
9071
LogFlowThisFunc (("aName='%ls'\n", aName));
9073
AutoCaller autoCaller (this);
9074
AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9076
/* saveSettings() needs mParent lock */
9077
AutoMultiWriteLock2 alock (mParent, this);
9079
AssertReturn ((!Global::IsOnlineOrTransient (mData->mMachineState) ||
9080
mData->mMachineState == MachineState_Paused) &&
9081
mSnapshotData.mLastState == MachineState_Null &&
9082
mSnapshotData.mSnapshot.isNull() &&
9083
mSnapshotData.mServerProgress.isNull() &&
9084
mSnapshotData.mCombinedProgress.isNull(),
9087
bool takingSnapshotOnline = mData->mMachineState == MachineState_Paused;
9089
if (!takingSnapshotOnline && mData->mMachineState != MachineState_Saved)
9091
/* save all current settings to ensure current changes are committed and
9092
* hard disks are fixed up */
9093
HRESULT rc = saveSettings();
9094
CheckComRCReturnRC (rc);
9097
/// @todo NEWMEDIA so far, we decided to allow for Writhethrough hard disks
9098
/// when taking snapshots putting all the responsibility to the user...
9100
/* check that there are no Writethrough hard disks attached */
9101
for (HDData::AttachmentList::const_iterator
9102
it = mHDData->mAttachments.begin();
9103
it != mHDData->mAttachments.end();
9106
ComObjPtr <HardDisk> hd = (*it)->hardDisk();
9107
AutoReadLock hdLock (hd);
9108
if (hd->type() == HardDiskType_Writethrough)
9109
return setError (E_FAIL,
9110
tr ("Cannot take a snapshot because the Writethrough hard disk "
9111
"'%ls' is attached to this virtual machine"),
9112
hd->locationFull().raw());
9116
AssertReturn (aProgress || !takingSnapshotOnline, E_FAIL);
9118
/* create an ID for the snapshot */
9120
snapshotId.create();
9123
/* stateFilePath is null when the machine is not online nor saved */
9124
if (takingSnapshotOnline || mData->mMachineState == MachineState_Saved)
9125
stateFilePath = Utf8StrFmt ("%ls%c{%RTuuid}.sav",
9126
mUserData->mSnapshotFolderFull.raw(),
9130
/* ensure the directory for the saved state file exists */
9133
HRESULT rc = VirtualBox::ensureFilePathExists (Utf8Str (stateFilePath));
9134
CheckComRCReturnRC (rc);
9137
/* create a snapshot machine object */
9138
ComObjPtr <SnapshotMachine> snapshotMachine;
9139
snapshotMachine.createObject();
9140
HRESULT rc = snapshotMachine->init (this, snapshotId, stateFilePath);
9141
AssertComRCReturn (rc, rc);
9143
Bstr progressDesc = BstrFmt (tr ("Taking snapshot of virtual machine '%ls'"),
9144
mUserData->mName.raw());
9145
Bstr firstOpDesc = Bstr (tr ("Preparing to take snapshot"));
9147
/* create a server-side progress object (it will be descriptionless when we
9148
* need to combine it with the VM-side progress, i.e. when we're taking a
9149
* snapshot online). The number of operations is: 1 (preparing) + # of
9150
* hard disks + 1 (if the state is saved so we need to copy it)
9152
ComObjPtr <Progress> serverProgress;
9153
serverProgress.createObject();
9155
ULONG opCount = 1 + (ULONG)mHDData->mAttachments.size();
9156
if (mData->mMachineState == MachineState_Saved)
9158
if (takingSnapshotOnline)
9159
rc = serverProgress->init (FALSE, opCount, firstOpDesc);
9161
rc = serverProgress->init (mParent, aInitiator, progressDesc, FALSE,
9162
opCount, firstOpDesc);
9163
AssertComRCReturn (rc, rc);
9166
/* create a combined server-side progress object when necessary */
9167
ComObjPtr <CombinedProgress> combinedProgress;
9168
if (takingSnapshotOnline)
9170
combinedProgress.createObject();
9171
rc = combinedProgress->init (mParent, aInitiator, progressDesc,
9172
serverProgress, aProgress);
9173
AssertComRCReturn (rc, rc);
9176
/* create a snapshot object */
9178
ComObjPtr <Snapshot> snapshot;
9179
snapshot.createObject();
9180
rc = snapshot->init (snapshotId, aName, aDescription,
9181
*RTTimeNow (&time), snapshotMachine,
9182
mData->mCurrentSnapshot);
9183
AssertComRCReturnRC (rc);
9185
/* create and start the task on a separate thread (note that it will not
9186
* start working until we release alock) */
9187
TakeSnapshotTask *task = new TakeSnapshotTask (this);
9188
int vrc = RTThreadCreate (NULL, taskHandler,
9190
0, RTTHREADTYPE_MAIN_WORKER, 0, "TakeSnapshot");
9191
if (RT_FAILURE (vrc))
9195
ComAssertRCRet (vrc, E_FAIL);
9198
/* fill in the snapshot data */
9199
mSnapshotData.mLastState = mData->mMachineState;
9200
mSnapshotData.mSnapshot = snapshot;
9201
mSnapshotData.mServerProgress = serverProgress;
9202
mSnapshotData.mCombinedProgress = combinedProgress;
9204
/* set the state to Saving (this is expected by Console::TakeSnapshot()) */
9205
setMachineState (MachineState_Saving);
9207
if (takingSnapshotOnline)
9208
stateFilePath.cloneTo (aStateFilePath);
9210
*aStateFilePath = NULL;
9212
serverProgress.queryInterfaceTo (aServerProgress);
9214
LogFlowThisFuncLeave();
9219
* @note Locks this object for writing.
9221
STDMETHODIMP SessionMachine::EndTakingSnapshot (BOOL aSuccess)
9223
LogFlowThisFunc (("\n"));
9225
AutoCaller autoCaller (this);
9226
AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9228
AutoWriteLock alock (this);
9230
AssertReturn (!aSuccess ||
9231
(mData->mMachineState == MachineState_Saving &&
9232
mSnapshotData.mLastState != MachineState_Null &&
9233
!mSnapshotData.mSnapshot.isNull() &&
9234
!mSnapshotData.mServerProgress.isNull() &&
9235
!mSnapshotData.mCombinedProgress.isNull()),
9238
/* set the state to the state we had when BeginTakingSnapshot() was called
9239
* (this is expected by Console::TakeSnapshot() and
9240
* Console::saveStateThread()) */
9241
setMachineState (mSnapshotData.mLastState);
9243
return endTakingSnapshot (aSuccess);
9247
* @note Locks mParent + this + children objects for writing!
9249
STDMETHODIMP SessionMachine::DiscardSnapshot (
9250
IConsole *aInitiator, IN_BSTR aId,
9251
MachineState_T *aMachineState, IProgress **aProgress)
9253
LogFlowThisFunc (("\n"));
9256
AssertReturn (aInitiator && !id.isEmpty(), E_INVALIDARG);
9257
AssertReturn (aMachineState && aProgress, E_POINTER);
9259
AutoCaller autoCaller (this);
9260
AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9262
/* saveSettings() needs mParent lock */
9263
AutoMultiWriteLock2 alock (mParent, this);
9265
ComAssertRet (!Global::IsOnlineOrTransient (mData->mMachineState), E_FAIL);
9267
ComObjPtr <Snapshot> snapshot;
9268
HRESULT rc = findSnapshot (id, snapshot, true /* aSetError */);
9269
CheckComRCReturnRC (rc);
9271
AutoWriteLock snapshotLock (snapshot);
9274
AutoWriteLock chLock (snapshot->childrenLock());
9275
size_t childrenCount = snapshot->children().size();
9276
if (childrenCount > 1)
9277
return setError (VBOX_E_INVALID_OBJECT_STATE,
9278
tr ("Snapshot '%ls' of the machine '%ls' has more than one "
9279
"child snapshot (%d)"),
9280
snapshot->data().mName.raw(), mUserData->mName.raw(),
9284
/* If the snapshot being discarded is the current one, ensure current
9285
* settings are committed and saved.
9287
if (snapshot == mData->mCurrentSnapshot)
9291
rc = saveSettings();
9292
CheckComRCReturnRC (rc);
9296
/* create a progress object. The number of operations is:
9297
* 1 (preparing) + # of hard disks + 1 if the snapshot is online
9299
ComObjPtr <Progress> progress;
9300
progress.createObject();
9301
rc = progress->init (mParent, aInitiator,
9302
Bstr (Utf8StrFmt (tr ("Discarding snapshot '%ls'"),
9303
snapshot->data().mName.raw())),
9304
FALSE /* aCancelable */,
9305
1 + (ULONG)snapshot->data().mMachine->mHDData->mAttachments.size() +
9306
(snapshot->stateFilePath().isNull() ? 0 : 1),
9307
Bstr (tr ("Preparing to discard snapshot")));
9308
AssertComRCReturn (rc, rc);
9310
/* create and start the task on a separate thread */
9311
DiscardSnapshotTask *task = new DiscardSnapshotTask (this, progress, snapshot);
9312
int vrc = RTThreadCreate (NULL, taskHandler,
9314
0, RTTHREADTYPE_MAIN_WORKER, 0, "DiscardSnapshot");
9315
if (RT_FAILURE (vrc))
9317
ComAssertRCRet (vrc, E_FAIL);
9319
/* set the proper machine state (note: after creating a Task instance) */
9320
setMachineState (MachineState_Discarding);
9322
/* return the progress to the caller */
9323
progress.queryInterfaceTo (aProgress);
9325
/* return the new state to the caller */
9326
*aMachineState = mData->mMachineState;
9332
* @note Locks this + children objects for writing!
9334
STDMETHODIMP SessionMachine::DiscardCurrentState (
9335
IConsole *aInitiator, MachineState_T *aMachineState, IProgress **aProgress)
9337
LogFlowThisFunc (("\n"));
9339
AssertReturn (aInitiator, E_INVALIDARG);
9340
AssertReturn (aMachineState && aProgress, E_POINTER);
9342
AutoCaller autoCaller (this);
9343
AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9345
AutoWriteLock alock (this);
9347
ComAssertRet (!Global::IsOnlineOrTransient (mData->mMachineState), E_FAIL);
9349
if (mData->mCurrentSnapshot.isNull())
9350
return setError (VBOX_E_INVALID_OBJECT_STATE,
9351
tr ("Could not discard the current state of the machine '%ls' "
9352
"because it doesn't have any snapshots"),
9353
mUserData->mName.raw());
9355
/* create a progress object. The number of operations is: 1 (preparing) + #
9356
* of hard disks + 1 (if we need to copy the saved state file) */
9357
ComObjPtr <Progress> progress;
9358
progress.createObject();
9360
ULONG opCount = 1 + (ULONG)mData->mCurrentSnapshot->data()
9361
.mMachine->mHDData->mAttachments.size();
9362
if (mData->mCurrentSnapshot->stateFilePath())
9364
progress->init (mParent, aInitiator,
9365
Bstr (tr ("Discarding current machine state")),
9366
FALSE /* aCancelable */, opCount,
9367
Bstr (tr ("Preparing to discard current state")));
9370
/* create and start the task on a separate thread (note that it will not
9371
* start working until we release alock) */
9372
DiscardCurrentStateTask *task =
9373
new DiscardCurrentStateTask (this, progress, false /* discardCurSnapshot */);
9374
int vrc = RTThreadCreate (NULL, taskHandler,
9376
0, RTTHREADTYPE_MAIN_WORKER, 0, "DiscardCurState");
9377
if (RT_FAILURE (vrc))
9380
ComAssertRCRet (vrc, E_FAIL);
9383
/* set the proper machine state (note: after creating a Task instance) */
9384
setMachineState (MachineState_Discarding);
9386
/* return the progress to the caller */
9387
progress.queryInterfaceTo (aProgress);
9389
/* return the new state to the caller */
9390
*aMachineState = mData->mMachineState;
9396
* @note Locks thos object for writing!
9398
STDMETHODIMP SessionMachine::DiscardCurrentSnapshotAndState (
9399
IConsole *aInitiator, MachineState_T *aMachineState, IProgress **aProgress)
9401
LogFlowThisFunc (("\n"));
9403
AssertReturn (aInitiator, E_INVALIDARG);
9404
AssertReturn (aMachineState && aProgress, E_POINTER);
9406
AutoCaller autoCaller (this);
9407
AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9409
AutoWriteLock alock (this);
9411
ComAssertRet (!Global::IsOnlineOrTransient (mData->mMachineState), E_FAIL);
9413
if (mData->mCurrentSnapshot.isNull())
9414
return setError (VBOX_E_INVALID_OBJECT_STATE,
9415
tr ("Could not discard the current state of the machine '%ls' "
9416
"because it doesn't have any snapshots"),
9417
mUserData->mName.raw());
9419
/* create a progress object. The number of operations is:
9420
* 1 (preparing) + # of hard disks in the current snapshot +
9421
* # of hard disks in the previous snapshot +
9422
* 1 if we need to copy the saved state file of the previous snapshot +
9423
* 1 if the current snapshot is online
9424
* or (if there is no previous snapshot):
9425
* 1 (preparing) + # of hard disks in the current snapshot * 2 +
9426
* 1 if we need to copy the saved state file of the current snapshot * 2
9428
ComObjPtr <Progress> progress;
9429
progress.createObject();
9431
ComObjPtr <Snapshot> curSnapshot = mData->mCurrentSnapshot;
9432
ComObjPtr <Snapshot> prevSnapshot = mData->mCurrentSnapshot->parent();
9437
opCount += (ULONG)curSnapshot->data().mMachine->mHDData->mAttachments.size();
9438
opCount += (ULONG)prevSnapshot->data().mMachine->mHDData->mAttachments.size();
9439
if (prevSnapshot->stateFilePath())
9441
if (curSnapshot->stateFilePath())
9447
(ULONG)curSnapshot->data().mMachine->mHDData->mAttachments.size() * 2;
9448
if (curSnapshot->stateFilePath())
9452
progress->init (mParent, aInitiator,
9453
Bstr (tr ("Discarding current machine snapshot and state")),
9454
FALSE /* aCancelable */, opCount,
9455
Bstr (tr ("Preparing to discard current snapshot and state")));
9458
/* create and start the task on a separate thread */
9459
DiscardCurrentStateTask *task =
9460
new DiscardCurrentStateTask (this, progress, true /* discardCurSnapshot */);
9461
int vrc = RTThreadCreate (NULL, taskHandler,
9463
0, RTTHREADTYPE_MAIN_WORKER, 0, "DiscardCurStSnp");
9464
if (RT_FAILURE (vrc))
9467
ComAssertRCRet (vrc, E_FAIL);
9470
/* set the proper machine state (note: after creating a Task instance) */
9471
setMachineState (MachineState_Discarding);
9473
/* return the progress to the caller */
9474
progress.queryInterfaceTo (aProgress);
9476
/* return the new state to the caller */
9477
*aMachineState = mData->mMachineState;
9482
STDMETHODIMP SessionMachine::
9483
PullGuestProperties (ComSafeArrayOut (BSTR, aNames),
9484
ComSafeArrayOut (BSTR, aValues),
9485
ComSafeArrayOut (ULONG64, aTimestamps),
9486
ComSafeArrayOut (BSTR, aFlags))
9488
LogFlowThisFunc (("\n"));
9197
STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
9198
ComSafeArrayOut(BSTR, aValues),
9199
ComSafeArrayOut(ULONG64, aTimestamps),
9200
ComSafeArrayOut(BSTR, aFlags))
9202
LogFlowThisFunc(("\n"));
9490
9204
#ifdef VBOX_WITH_GUEST_PROPS
9491
9205
using namespace guestProp;
9493
AutoCaller autoCaller (this);
9207
AutoCaller autoCaller(this);
9494
9208
AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9496
AutoReadLock alock (this);
9210
AutoReadLock alock(this);
9498
AssertReturn (!ComSafeArrayOutIsNull (aNames), E_POINTER);
9499
AssertReturn (!ComSafeArrayOutIsNull (aValues), E_POINTER);
9500
AssertReturn (!ComSafeArrayOutIsNull (aTimestamps), E_POINTER);
9501
AssertReturn (!ComSafeArrayOutIsNull (aFlags), E_POINTER);
9212
AssertReturn(!ComSafeArrayOutIsNull(aNames), E_POINTER);
9213
AssertReturn(!ComSafeArrayOutIsNull(aValues), E_POINTER);
9214
AssertReturn(!ComSafeArrayOutIsNull(aTimestamps), E_POINTER);
9215
AssertReturn(!ComSafeArrayOutIsNull(aFlags), E_POINTER);
9503
9217
size_t cEntries = mHWData->mGuestProperties.size();
9504
com::SafeArray <BSTR> names (cEntries);
9505
com::SafeArray <BSTR> values (cEntries);
9506
com::SafeArray <ULONG64> timestamps (cEntries);
9507
com::SafeArray <BSTR> flags (cEntries);
9218
com::SafeArray<BSTR> names (cEntries);
9219
com::SafeArray<BSTR> values (cEntries);
9220
com::SafeArray<ULONG64> timestamps (cEntries);
9221
com::SafeArray<BSTR> flags (cEntries);
9508
9222
unsigned i = 0;
9509
9223
for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
9510
it != mHWData->mGuestProperties.end(); ++it)
9224
it != mHWData->mGuestProperties.end();
9512
9227
char szFlags[MAX_FLAGS_LEN + 1];
9513
it->mName.cloneTo (&names[i]);
9514
it->mValue.cloneTo (&values[i]);
9228
it->strName.cloneTo(&names[i]);
9229
it->strValue.cloneTo(&values[i]);
9515
9230
timestamps[i] = it->mTimestamp;
9516
writeFlags (it->mFlags, szFlags);
9517
Bstr (szFlags).cloneTo (&flags[i]);
9231
/* If it is NULL, keep it NULL. */
9234
writeFlags(it->mFlags, szFlags);
9235
Bstr(szFlags).cloneTo(&flags[i]);
9520
names.detachTo (ComSafeArrayOutArg (aNames));
9521
values.detachTo (ComSafeArrayOutArg (aValues));
9522
timestamps.detachTo (ComSafeArrayOutArg (aTimestamps));
9523
flags.detachTo (ComSafeArrayOutArg (aFlags));
9241
names.detachTo(ComSafeArrayOutArg(aNames));
9242
values.detachTo(ComSafeArrayOutArg(aValues));
9243
timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
9244
flags.detachTo(ComSafeArrayOutArg(aFlags));
9524
9245
mHWData->mPropertyServiceActive = true;
9531
STDMETHODIMP SessionMachine::PushGuestProperties (ComSafeArrayIn (IN_BSTR, aNames),
9532
ComSafeArrayIn (IN_BSTR, aValues),
9533
ComSafeArrayIn (ULONG64, aTimestamps),
9534
ComSafeArrayIn (IN_BSTR, aFlags))
9252
STDMETHODIMP SessionMachine::PushGuestProperties(ComSafeArrayIn(IN_BSTR, aNames),
9253
ComSafeArrayIn(IN_BSTR, aValues),
9254
ComSafeArrayIn(ULONG64, aTimestamps),
9255
ComSafeArrayIn(IN_BSTR, aFlags))
9536
LogFlowThisFunc (("\n"));
9257
LogFlowThisFunc(("\n"));
9538
9259
#ifdef VBOX_WITH_GUEST_PROPS
9539
9260
using namespace guestProp;
9541
AutoCaller autoCaller (this);
9542
AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9544
AutoWriteLock alock (this);
9546
/* Temporarily reset the registered flag, so that our machine state
9547
* changes (i.e. mHWData.backup()) succeed. (isMutable() used in
9548
* all setters will return FALSE for a Machine instance if mRegistered
9549
* is TRUE). This is copied from registeredInit(), and may or may not be
9550
* the right way to handle this. */
9262
AssertReturn(!ComSafeArrayInIsNull(aNames), E_POINTER);
9263
AssertReturn(!ComSafeArrayInIsNull(aValues), E_POINTER);
9264
AssertReturn(!ComSafeArrayInIsNull(aTimestamps), E_POINTER);
9265
AssertReturn(!ComSafeArrayInIsNull(aFlags), E_POINTER);
9267
AutoCaller autoCaller(this);
9268
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9270
AutoWriteLock alock(this);
9273
* Temporarily reset the registered flag, so that our machine state
9274
* changes (i.e. mHWData.backup()) succeed. (isMutable() used in all
9275
* setters will return FALSE for a Machine instance if mRegistered is TRUE).
9277
* This is copied from registeredInit(), and may or may not be the right
9278
* way to handle this.
9280
Assert(mData->mRegistered);
9551
9281
mData->mRegistered = FALSE;
9552
HRESULT rc = checkStateDependency (MutableStateDep);
9553
LogRel (("checkStateDependency (MutableStateDep) returned 0x%x\n", rc));
9554
CheckComRCReturnRC (rc);
9556
// ComAssertRet (mData->mMachineState < MachineState_Running, E_FAIL);
9558
AssertReturn (!ComSafeArrayInIsNull (aNames), E_POINTER);
9559
AssertReturn (!ComSafeArrayInIsNull (aValues), E_POINTER);
9560
AssertReturn (!ComSafeArrayInIsNull (aTimestamps), E_POINTER);
9561
AssertReturn (!ComSafeArrayInIsNull (aFlags), E_POINTER);
9563
com::SafeArray <IN_BSTR> names (ComSafeArrayInArg (aNames));
9564
com::SafeArray <IN_BSTR> values (ComSafeArrayInArg (aValues));
9565
com::SafeArray <ULONG64> timestamps (ComSafeArrayInArg (aTimestamps));
9566
com::SafeArray <IN_BSTR> flags (ComSafeArrayInArg (aFlags));
9283
HRESULT rc = checkStateDependency(MutableStateDep);
9284
AssertLogRelMsgReturn(SUCCEEDED(rc), ("%Rhrc\n", rc), rc);
9286
com::SafeArray<IN_BSTR> names( ComSafeArrayInArg(aNames));
9287
com::SafeArray<IN_BSTR> values( ComSafeArrayInArg(aValues));
9288
com::SafeArray<ULONG64> timestamps(ComSafeArrayInArg(aTimestamps));
9289
com::SafeArray<IN_BSTR> flags( ComSafeArrayInArg(aFlags));
9567
9291
DiscardSettings();
9568
9292
mHWData.backup();
9569
mHWData->mGuestProperties.erase (mHWData->mGuestProperties.begin(),
9294
mHWData->mGuestProperties.erase(mHWData->mGuestProperties.begin(),
9570
9295
mHWData->mGuestProperties.end());
9571
9296
for (unsigned i = 0; i < names.size(); ++i)
9573
9298
uint32_t fFlags = NILFLAG;
9574
validateFlags (Utf8Str (flags[i]).raw(), &fFlags);
9299
validateFlags(Utf8Str(flags[i]).raw(), &fFlags);
9575
9300
HWData::GuestProperty property = { names[i], values[i], timestamps[i], fFlags };
9576
mHWData->mGuestProperties.push_back (property);
9301
mHWData->mGuestProperties.push_back(property);
9578
9304
mHWData->mPropertyServiceActive = false;
9579
9306
alock.unlock();
9580
9307
SaveSettings();
9581
9309
/* Restore the mRegistered flag. */
9582
9311
mData->mRegistered = TRUE;
9585
9315
ReturnComNotImplemented();
9589
STDMETHODIMP SessionMachine::PushGuestProperty (IN_BSTR aName, IN_BSTR aValue,
9590
ULONG64 aTimestamp, IN_BSTR aFlags)
9319
STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
9592
LogFlowThisFunc (("\n"));
9324
LogFlowThisFunc(("\n"));
9594
9326
#ifdef VBOX_WITH_GUEST_PROPS
9595
9327
using namespace guestProp;
9597
CheckComArgNotNull (aName);
9598
if ((aValue != NULL) && (!VALID_PTR (aValue) || !VALID_PTR (aFlags)))
9329
CheckComArgNotNull(aName);
9330
if (aValue != NULL && (!VALID_PTR(aValue) || !VALID_PTR(aFlags)))
9599
9331
return E_POINTER; /* aValue can be NULL to indicate deletion */
9601
Utf8Str utf8Name (aName);
9602
Utf8Str utf8Flags (aFlags);
9603
Utf8Str utf8Patterns (mHWData->mGuestPropertyNotificationPatterns);
9604
if ( utf8Name.isNull()
9605
|| ((aFlags != NULL) && utf8Flags.isNull())
9606
|| utf8Patterns.isNull()
9608
return E_OUTOFMEMORY;
9610
uint32_t fFlags = NILFLAG;
9611
if ((aFlags != NULL) && RT_FAILURE (validateFlags (utf8Flags.raw(), &fFlags)))
9612
return E_INVALIDARG;
9614
bool matchAll = false;
9615
if (utf8Patterns.length() == 0)
9618
AutoCaller autoCaller (this);
9619
CheckComRCReturnRC (autoCaller.rc());
9621
AutoWriteLock alock (this);
9623
HRESULT rc = checkStateDependency (MutableStateDep);
9624
CheckComRCReturnRC (rc);
9627
for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
9628
iter != mHWData->mGuestProperties.end(); ++iter)
9629
if (aName == iter->mName)
9631
mHWData->mGuestProperties.erase (iter);
9636
HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
9637
mHWData->mGuestProperties.push_back (property);
9640
/* send a callback notification if appropriate */
9643
|| RTStrSimplePatternMultiMatch (utf8Patterns.raw(), RTSTR_MAX,
9644
utf8Name.raw(), RTSTR_MAX, NULL)
9646
mParent->onGuestPropertyChange (mData->mUuid, aName, aValue, aFlags);
9336
* Convert input up front.
9338
Utf8Str utf8Name(aName);
9339
uint32_t fFlags = NILFLAG;
9342
Utf8Str utf8Flags(aFlags);
9343
int vrc = validateFlags(utf8Flags.raw(), &fFlags);
9344
AssertRCReturn(vrc, E_INVALIDARG);
9348
* Now grab the object lock, validate the state and do the update.
9350
AutoCaller autoCaller(this);
9351
CheckComRCReturnRC(autoCaller.rc());
9353
AutoWriteLock alock(this);
9355
AssertReturn(mHWData->mPropertyServiceActive, VBOX_E_INVALID_OBJECT_STATE);
9356
switch (mData->mMachineState)
9358
case MachineState_Paused:
9359
case MachineState_Running:
9360
case MachineState_Teleporting:
9361
case MachineState_TeleportingPausedVM:
9362
case MachineState_LiveSnapshotting:
9363
case MachineState_Saving:
9367
AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
9368
VBOX_E_INVALID_VM_STATE);
9373
/** @todo r=bird: The careful memory handling doesn't work out here because
9374
* the catch block won't undo any damange we've done. So, if push_back throws
9375
* bad_alloc then you've lost the value.
9377
* Another thing. Doing a linear search here isn't extremely efficient, esp.
9378
* since values that changes actually bubbles to the end of the list. Using
9379
* something that has an efficient lookup and can tollerate a bit of updates
9380
* would be nice. RTStrSpace is one suggestion (it's not perfect). Some
9381
* combination of RTStrCache (for sharing names and getting uniqueness into
9382
* the bargain) and hash/tree is another. */
9383
for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
9384
iter != mHWData->mGuestProperties.end();
9386
if (utf8Name == iter->strName)
9388
mHWData->mGuestProperties.erase(iter);
9393
HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
9394
mHWData->mGuestProperties.push_back(property);
9398
* Send a callback notification if appropriate
9400
if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
9401
|| RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.raw(),
9409
mParent->onGuestPropertyChange(mData->mUuid,
9417
return VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
9650
9421
ReturnComNotImplemented();
10098
* Helper method to finalize taking a snapshot. Gets called to finalize the
10099
* "take snapshot" procedure.
10101
* Expected to be called after completing *all* the tasks related to taking the
10102
* snapshot, either successfully or unsuccessfilly.
10104
* @param aSuccess TRUE if the snapshot has been taken successfully.
10106
* @note Locks this objects for writing.
10108
HRESULT SessionMachine::endTakingSnapshot (BOOL aSuccess)
10110
LogFlowThisFuncEnter();
10112
AutoCaller autoCaller (this);
10113
AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
10115
AutoWriteLock alock (this);
10117
AssertReturn (!mSnapshotData.mSnapshot.isNull(), E_FAIL);
10119
MultiResult rc (S_OK);
10123
/* the server progress must be completed on success */
10124
Assert (mSnapshotData.mServerProgress->completed());
10126
mData->mCurrentSnapshot = mSnapshotData.mSnapshot;
10128
/* memorize the first snapshot if necessary */
10129
if (!mData->mFirstSnapshot)
10130
mData->mFirstSnapshot = mData->mCurrentSnapshot;
10132
int opFlags = SaveSS_AddOp | SaveSS_CurrentId;
10133
if (!Global::IsOnline (mSnapshotData.mLastState))
10135
/* the machine was powered off or saved when taking a snapshot, so
10136
* reset the mCurrentStateModified flag */
10137
mData->mCurrentStateModified = FALSE;
10138
opFlags |= SaveSS_CurStateModified;
10141
rc = saveSnapshotSettings (mSnapshotData.mSnapshot, opFlags);
10144
if (aSuccess && SUCCEEDED (rc))
10146
bool online = Global::IsOnline (mSnapshotData.mLastState);
10148
/* associate old hard disks with the snapshot and do locking/unlocking*/
10149
fixupHardDisks(true /* aCommit */, online);
10151
/* inform callbacks */
10152
mParent->onSnapshotTaken (mData->mUuid,
10153
mSnapshotData.mSnapshot->data().mId);
10157
/* wait for the completion of the server progress (diff VDI creation) */
10158
/// @todo (dmik) later, we will definitely want to cancel it instead
10159
// (when the cancel function is implemented)
10160
mSnapshotData.mServerProgress->WaitForCompletion (-1);
10162
/* delete all differencing hard disks created (this will also attach
10163
* their parents back by rolling back mHDData) */
10164
fixupHardDisks(false /* aCommit */);
10166
/* delete the saved state file (it might have been already created) */
10167
if (mSnapshotData.mSnapshot->stateFilePath())
10168
RTFileDelete (Utf8Str (mSnapshotData.mSnapshot->stateFilePath()));
10170
mSnapshotData.mSnapshot->uninit();
10173
/* clear out the snapshot data */
10174
mSnapshotData.mLastState = MachineState_Null;
10175
mSnapshotData.mSnapshot.setNull();
10176
mSnapshotData.mServerProgress.setNull();
10178
/* uninitialize the combined progress (to remove it from the VBox collection) */
10179
if (!mSnapshotData.mCombinedProgress.isNull())
10181
mSnapshotData.mCombinedProgress->uninit();
10182
mSnapshotData.mCombinedProgress.setNull();
10185
LogFlowThisFuncLeave();
10190
* Take snapshot task handler. Must be called only by
10191
* TakeSnapshotTask::handler()!
10193
* The sole purpose of this task is to asynchronously create differencing VDIs
10194
* and copy the saved state file (when necessary). The VM process will wait for
10195
* this task to complete using the mSnapshotData.mServerProgress returned to it.
10197
* @note Locks this object for writing.
10199
void SessionMachine::takeSnapshotHandler (TakeSnapshotTask & /* aTask */)
10201
LogFlowThisFuncEnter();
10203
AutoCaller autoCaller (this);
10205
LogFlowThisFunc (("state=%d\n", autoCaller.state()));
10206
if (!autoCaller.isOk())
10208
/* we might have been uninitialized because the session was accidentally
10209
* closed by the client, so don't assert */
10210
LogFlowThisFuncLeave();
10214
AutoWriteLock alock (this);
10218
bool online = Global::IsOnline (mSnapshotData.mLastState);
10220
LogFlowThisFunc (("Creating differencing hard disks (online=%d)...\n",
10225
/* create new differencing hard disks and attach them to this machine */
10226
rc = createImplicitDiffs (mUserData->mSnapshotFolderFull,
10227
mSnapshotData.mServerProgress,
10230
if (SUCCEEDED (rc) && mSnapshotData.mLastState == MachineState_Saved)
10232
Utf8Str stateFrom = mSSData->mStateFilePath;
10233
Utf8Str stateTo = mSnapshotData.mSnapshot->stateFilePath();
10235
LogFlowThisFunc (("Copying the execution state from '%s' to '%s'...\n",
10236
stateFrom.raw(), stateTo.raw()));
10238
mSnapshotData.mServerProgress->setNextOperation(Bstr(tr("Copying the execution state")),
10241
/* Leave the lock before a lengthy operation (mMachineState is
10242
* MachineState_Saving here) */
10246
/* copy the state file */
10247
int vrc = RTFileCopyEx (stateFrom, stateTo, 0, progressCallback,
10248
static_cast <Progress *> (mSnapshotData.mServerProgress));
10252
if (RT_FAILURE (vrc))
10253
rc = setError (E_FAIL,
10254
tr ("Could not copy the state file '%s' to '%s' (%Rrc)"),
10255
stateFrom.raw(), stateTo.raw(), vrc);
10258
/* we have to call endTakingSnapshot() ourselves if the snapshot was taken
10259
* offline because the VM process will not do it in this case
10263
LogFlowThisFunc (("Finalizing the taken snapshot (rc=%Rhrc)...\n", rc));
10266
ErrorInfoKeeper eik;
10268
setMachineState (mSnapshotData.mLastState);
10269
updateMachineStateOnClient();
10272
/* finalize the progress after setting the state, for consistency */
10273
mSnapshotData.mServerProgress->notifyComplete (rc);
10275
endTakingSnapshot (SUCCEEDED (rc));
10279
mSnapshotData.mServerProgress->notifyComplete (rc);
10282
LogFlowThisFuncLeave();
10286
* Helper struct for SessionMachine::discardSnapshotHandler().
10288
struct HardDiskDiscardRec
10290
HardDiskDiscardRec() : chain (NULL) {}
10292
HardDiskDiscardRec (const ComObjPtr<HardDisk> &aHd,
10293
HardDisk::MergeChain *aChain = NULL)
10294
: hd (aHd), chain (aChain) {}
10296
HardDiskDiscardRec (const ComObjPtr<HardDisk> &aHd,
10297
HardDisk::MergeChain *aChain,
10298
const ComObjPtr<HardDisk> &aReplaceHd,
10299
const ComObjPtr<HardDiskAttachment> &aReplaceHda,
10300
const Guid &aSnapshotId)
10301
: hd (aHd), chain (aChain)
10302
, replaceHd (aReplaceHd), replaceHda (aReplaceHda)
10303
, snapshotId (aSnapshotId) {}
10305
ComObjPtr<HardDisk> hd;
10306
HardDisk::MergeChain *chain;
10307
/* these are for the replace hard disk case: */
10308
ComObjPtr<HardDisk> replaceHd;
10309
ComObjPtr<HardDiskAttachment> replaceHda;
10313
typedef std::list <HardDiskDiscardRec> HardDiskDiscardRecList;
10316
* Discard snapshot task handler. Must be called only by
10317
* DiscardSnapshotTask::handler()!
10319
* When aTask.subTask is true, the associated progress object is left
10320
* uncompleted on success. On failure, the progress is marked as completed
10321
* regardless of this parameter.
10323
* @note Locks mParent + this + child objects for writing!
10325
void SessionMachine::discardSnapshotHandler (DiscardSnapshotTask &aTask)
10327
LogFlowThisFuncEnter();
10329
AutoCaller autoCaller (this);
10331
LogFlowThisFunc (("state=%d\n", autoCaller.state()));
10332
if (!autoCaller.isOk())
10334
/* we might have been uninitialized because the session was accidentally
10335
* closed by the client, so don't assert */
10336
aTask.progress->notifyComplete (
10337
E_FAIL, COM_IIDOF (IMachine), getComponentName(),
10338
tr ("The session has been accidentally closed"));
10340
LogFlowThisFuncLeave();
10344
/* saveSettings() needs mParent lock */
10345
AutoWriteLock vboxLock (mParent);
10347
/* @todo We don't need mParent lock so far so unlock() it. Better is to
10348
* provide an AutoWriteLock argument that lets create a non-locking
10352
/* Preseve the {parent, child} lock order for this and snapshot stuff */
10353
AutoMultiWriteLock3 alock (this->lockHandle(),
10354
aTask.snapshot->lockHandle(),
10355
aTask.snapshot->childrenLock());
10357
ComObjPtr <SnapshotMachine> sm = aTask.snapshot->data().mMachine;
10358
/* no need to lock the snapshot machine since it is const by definiton */
10362
/* save the snapshot ID (for callbacks) */
10363
Guid snapshotId = aTask.snapshot->data().mId;
10365
HardDiskDiscardRecList toDiscard;
10367
bool settingsChanged = false;
10372
LogFlowThisFunc (("1: Checking hard disk merge prerequisites...\n"));
10374
for (HDData::AttachmentList::const_iterator it =
10375
sm->mHDData->mAttachments.begin();
10376
it != sm->mHDData->mAttachments.end();
10379
ComObjPtr<HardDiskAttachment> hda = *it;
10380
ComObjPtr<HardDisk> hd = hda->hardDisk();
10382
/* HardDisk::prepareDiscard() reqiuires a write lock */
10383
AutoWriteLock hdLock (hd);
10385
if (hd->type() != HardDiskType_Normal)
10387
/* skip writethrough hard disks */
10389
Assert (hd->type() == HardDiskType_Writethrough);
10391
rc = aTask.progress->setNextOperation(BstrFmt(tr("Skipping writethrough hard disk '%s'"),
10392
hd->root()->name().raw()),
10394
CheckComRCThrowRC (rc);
10399
HardDisk::MergeChain *chain = NULL;
10401
/* needs to be discarded (merged with the child if any), check
10403
rc = hd->prepareDiscard (chain);
10404
CheckComRCThrowRC (rc);
10406
if (hd->parent().isNull() && chain != NULL)
10408
/* it's a base hard disk so it will be a backward merge of its
10409
* only child to it (prepareDiscard() does necessary checks). We
10410
* need then to update the attachment that refers to the child
10411
* to refer to the parent insead. Don't forget to detach the
10412
* child (otherwise mergeTo() called by discard() will assert
10413
* because it will be going to delete the child) */
10415
/* The below assert would be nice but I don't want to move
10416
* HardDisk::MergeChain to the header just for that
10417
* Assert (!chain->isForward()); */
10419
Assert (hd->children().size() == 1);
10421
ComObjPtr<HardDisk> replaceHd = hd->children().front();
10423
Assert (replaceHd->backRefs().front().machineId == mData->mUuid);
10424
Assert (replaceHd->backRefs().front().snapshotIds.size() <= 1);
10427
if (replaceHd->backRefs().front().snapshotIds.size() == 1)
10428
snapshotId = replaceHd->backRefs().front().snapshotIds.front();
10430
HRESULT rc2 = S_OK;
10432
/* adjust back references */
10433
rc2 = replaceHd->detachFrom (mData->mUuid, snapshotId);
10436
rc2 = hd->attachTo (mData->mUuid, snapshotId);
10439
/* replace the hard disk in the attachment object */
10440
HDData::AttachmentList::iterator it;
10441
if (snapshotId.isEmpty())
10443
/* in current state */
10444
it = std::find_if (mHDData->mAttachments.begin(),
10445
mHDData->mAttachments.end(),
10446
HardDiskAttachment::RefersTo (replaceHd));
10447
AssertBreak (it != mHDData->mAttachments.end());
10452
ComObjPtr <Snapshot> snapshot;
10453
rc2 = findSnapshot (snapshotId, snapshot);
10456
/* don't lock the snapshot; cannot be modified outside */
10457
HDData::AttachmentList &snapAtts =
10458
snapshot->data().mMachine->mHDData->mAttachments;
10459
it = std::find_if (snapAtts.begin(),
10461
HardDiskAttachment::RefersTo (replaceHd));
10462
AssertBreak (it != snapAtts.end());
10465
AutoWriteLock attLock (*it);
10466
(*it)->updateHardDisk (hd, false /* aImplicit */);
10468
toDiscard.push_back (HardDiskDiscardRec (hd, chain, replaceHd,
10473
toDiscard.push_back (HardDiskDiscardRec (hd, chain));
10476
/* Now we checked that we can successfully merge all normal hard disks
10477
* (unless a runtime error like end-of-disc happens). Prior to
10478
* performing the actual merge, we want to discard the snapshot itself
10479
* and remove it from the XML file to make sure that a possible merge
10480
* ruintime error will not make this snapshot inconsistent because of
10481
* the partially merged or corrupted hard disks */
10484
LogFlowThisFunc (("2: Discarding snapshot...\n"));
10487
/* for now, the snapshot must have only one child when discarded,
10488
* or no children at all */
10489
ComAssertThrow (aTask.snapshot->children().size() <= 1, E_FAIL);
10491
ComObjPtr <Snapshot> parentSnapshot = aTask.snapshot->parent();
10494
// when we introduce clones later, discarding the snapshot
10495
// will affect the current and first snapshots of clones, if they are
10496
// direct children of this snapshot. So we will need to lock machines
10497
// associated with child snapshots as well and update mCurrentSnapshot
10498
// and/or mFirstSnapshot fields.
10500
if (aTask.snapshot == mData->mCurrentSnapshot)
10502
/* currently, the parent snapshot must refer to the same machine */
10503
/// @todo NEWMEDIA not really clear why
10506
parentSnapshot->data().mMachine->mData->mUuid == mData->mUuid,
10508
mData->mCurrentSnapshot = parentSnapshot;
10510
/* we've changed the base of the current state so mark it as
10511
* modified as it no longer guaranteed to be its copy */
10512
mData->mCurrentStateModified = TRUE;
10515
if (aTask.snapshot == mData->mFirstSnapshot)
10517
if (aTask.snapshot->children().size() == 1)
10519
ComObjPtr <Snapshot> childSnapshot =
10520
aTask.snapshot->children().front();
10522
childSnapshot->data().mMachine->mData->mUuid == mData->mUuid,
10524
mData->mFirstSnapshot = childSnapshot;
10527
mData->mFirstSnapshot.setNull();
10530
Bstr stateFilePath = aTask.snapshot->stateFilePath();
10532
/* Note that discarding the snapshot will deassociate it from the
10533
* hard disks which will allow the merge+delete operation for them*/
10534
aTask.snapshot->discard();
10536
rc = saveSnapshotSettings (parentSnapshot, SaveSS_UpdateAllOp |
10538
SaveSS_CurStateModified);
10539
CheckComRCThrowRC (rc);
10542
// if we implement some warning mechanism later, we'll have
10543
// to return a warning if the state file path cannot be deleted
10546
aTask.progress->setNextOperation(Bstr(tr("Discarding the execution state")),
10549
RTFileDelete (Utf8Str (stateFilePath));
10552
/// @todo NEWMEDIA to provide a good level of fauilt tolerance, we
10553
/// should restore the shapshot in the snapshot tree if
10554
/// saveSnapshotSettings fails. Actually, we may call
10555
/// #saveSnapshotSettings() with a special flag that will tell it to
10556
/// skip the given snapshot as if it would have been discarded and
10557
/// only actually discard it if the save operation succeeds.
10560
/* here we come when we've irrevesibly discarded the snapshot which
10561
* means that the VM settigns (our relevant changes to mData) need to be
10563
/// @todo NEWMEDIA maybe save everything in one operation in place of
10564
/// saveSnapshotSettings() above
10565
settingsChanged = true;
10568
LogFlowThisFunc (("3: Performing actual hard disk merging...\n"));
10570
/* leave the locks before the potentially lengthy operation */
10573
/// @todo NEWMEDIA turn the following errors into warnings because the
10574
/// snapshot itself has been already deleted (and interpret these
10575
/// warnings properly on the GUI side)
10577
for (HardDiskDiscardRecList::iterator it = toDiscard.begin();
10578
it != toDiscard.end();)
10580
rc = it->hd->discard (aTask.progress, it->chain);
10581
CheckComRCBreakRC (rc);
10583
/* prevent from calling cancelDiscard() */
10584
it = toDiscard.erase (it);
10589
CheckComRCThrowRC (rc);
10591
catch (HRESULT aRC) { rc = aRC; }
10595
HRESULT rc2 = S_OK;
10597
/* un-prepare the remaining hard disks */
10598
for (HardDiskDiscardRecList::const_iterator it = toDiscard.begin();
10599
it != toDiscard.end(); ++ it)
10601
it->hd->cancelDiscard (it->chain);
10603
if (!it->replaceHd.isNull())
10605
/* undo hard disk replacement */
10607
rc2 = it->replaceHd->attachTo (mData->mUuid, it->snapshotId);
10610
rc2 = it->hd->detachFrom (mData->mUuid, it->snapshotId);
10613
AutoWriteLock attLock (it->replaceHda);
10614
it->replaceHda->updateHardDisk (it->replaceHd, false /* aImplicit */);
10619
if (!aTask.subTask || FAILED (rc))
10621
if (!aTask.subTask)
10623
/* saveSettings() below needs a VirtualBox write lock and we need to
10624
* leave this object's lock to do this to follow the {parent-child}
10625
* locking rule. This is the last chance to do that while we are
10626
* still in a protective state which allows us to temporarily leave
10632
/* preserve existing error info */
10633
ErrorInfoKeeper eik;
10635
/* restore the machine state */
10636
setMachineState (aTask.state);
10637
updateMachineStateOnClient();
10639
if (settingsChanged)
10640
saveSettings (SaveS_InformCallbacksAnyway);
10643
/* set the result (this will try to fetch current error info on failure) */
10644
aTask.progress->notifyComplete (rc);
10647
if (SUCCEEDED (rc))
10648
mParent->onSnapshotDiscarded (mData->mUuid, snapshotId);
10650
LogFlowThisFunc (("Done discarding snapshot (rc=%08X)\n", rc));
10651
LogFlowThisFuncLeave();
10655
* Discard current state task handler. Must be called only by
10656
* DiscardCurrentStateTask::handler()!
10658
* @note Locks mParent + this object for writing.
10660
void SessionMachine::discardCurrentStateHandler (DiscardCurrentStateTask &aTask)
10662
LogFlowThisFuncEnter();
10664
AutoCaller autoCaller (this);
10666
LogFlowThisFunc (("state=%d\n", autoCaller.state()));
10667
if (!autoCaller.isOk())
10669
/* we might have been uninitialized because the session was accidentally
10670
* closed by the client, so don't assert */
10671
aTask.progress->notifyComplete (
10672
E_FAIL, COM_IIDOF (IMachine), getComponentName(),
10673
tr ("The session has been accidentally closed"));
10675
LogFlowThisFuncLeave();
10679
/* saveSettings() needs mParent lock */
10680
AutoWriteLock vboxLock (mParent);
10682
/* @todo We don't need mParent lock so far so unlock() it. Better is to
10683
* provide an AutoWriteLock argument that lets create a non-locking
10687
AutoWriteLock alock (this);
10689
/* discard all current changes to mUserData (name, OSType etc.) (note that
10690
* the machine is powered off, so there is no need to inform the direct
10693
rollback (false /* aNotify */);
10697
bool errorInSubtask = false;
10698
bool stateRestored = false;
10700
const bool isLastSnapshot = mData->mCurrentSnapshot->parent().isNull();
10704
/* discard the saved state file if the machine was Saved prior to this
10706
if (aTask.state == MachineState_Saved)
10708
Assert (!mSSData->mStateFilePath.isEmpty());
10709
RTFileDelete (Utf8Str (mSSData->mStateFilePath));
10710
mSSData->mStateFilePath.setNull();
10711
aTask.modifyLastState (MachineState_PoweredOff);
10712
rc = saveStateSettings (SaveSTS_StateFilePath);
10713
CheckComRCThrowRC (rc);
10716
if (aTask.discardCurrentSnapshot && !isLastSnapshot)
10718
/* the "discard current snapshot and state" task is in action, the
10719
* current snapshot is not the last one. Discard the current
10720
* snapshot first */
10722
DiscardSnapshotTask subTask (aTask, mData->mCurrentSnapshot);
10723
subTask.subTask = true;
10724
discardSnapshotHandler (subTask);
10726
AutoCaller progressCaller (aTask.progress);
10727
AutoReadLock progressLock (aTask.progress);
10728
if (aTask.progress->completed())
10730
/* the progress can be completed by a subtask only if there was
10732
rc = aTask.progress->resultCode();
10733
Assert (FAILED (rc));
10734
errorInSubtask = true;
10739
RTTIMESPEC snapshotTimeStamp;
10740
RTTimeSpecSetMilli (&snapshotTimeStamp, 0);
10743
ComObjPtr <Snapshot> curSnapshot = mData->mCurrentSnapshot;
10744
AutoReadLock snapshotLock (curSnapshot);
10746
/* remember the timestamp of the snapshot we're restoring from */
10747
snapshotTimeStamp = curSnapshot->data().mTimeStamp;
10749
/* copy all hardware data from the current snapshot */
10750
copyFrom (curSnapshot->data().mMachine);
10752
LogFlowThisFunc (("Restoring hard disks from the snapshot...\n"));
10754
/* restore the attachmends from the snapshot */
10756
mHDData->mAttachments =
10757
curSnapshot->data().mMachine->mHDData->mAttachments;
10759
/* leave the locks before the potentially lengthy operation */
10760
snapshotLock.unlock();
10763
rc = createImplicitDiffs (mUserData->mSnapshotFolderFull,
10765
false /* aOnline */);
10768
snapshotLock.lock();
10770
CheckComRCThrowRC (rc);
10772
/* Note: on success, current (old) hard disks will be
10773
* deassociated/deleted on #commit() called from #saveSettings() at
10774
* the end. On failure, newly created implicit diffs will be
10775
* deleted by #rollback() at the end. */
10777
/* should not have a saved state file associated at this point */
10778
Assert (mSSData->mStateFilePath.isNull());
10780
if (curSnapshot->stateFilePath())
10782
Utf8Str snapStateFilePath = curSnapshot->stateFilePath();
10784
Utf8Str stateFilePath = Utf8StrFmt ("%ls%c{%RTuuid}.sav",
10785
mUserData->mSnapshotFolderFull.raw(),
10786
RTPATH_DELIMITER, mData->mUuid.raw());
10788
LogFlowThisFunc (("Copying saved state file from '%s' to '%s'...\n",
10789
snapStateFilePath.raw(), stateFilePath.raw()));
10791
aTask.progress->setNextOperation(Bstr(tr("Restoring the execution state")),
10794
/* leave the lock before the potentially lengthy operation */
10795
snapshotLock.unlock();
10798
/* copy the state file */
10799
int vrc = RTFileCopyEx (snapStateFilePath, stateFilePath,
10800
0, progressCallback, aTask.progress);
10803
snapshotLock.lock();
10805
if (RT_SUCCESS (vrc))
10807
mSSData->mStateFilePath = stateFilePath;
10811
throw setError (E_FAIL,
10812
tr ("Could not copy the state file '%s' to '%s' (%Rrc)"),
10813
snapStateFilePath.raw(), stateFilePath.raw(), vrc);
10818
/* grab differencing hard disks from the old attachments that will
10819
* become unused and need to be auto-deleted */
10821
std::list< ComObjPtr<HardDisk> > diffs;
10823
for (HDData::AttachmentList::const_iterator
10824
it = mHDData.backedUpData()->mAttachments.begin();
10825
it != mHDData.backedUpData()->mAttachments.end(); ++ it)
10827
ComObjPtr<HardDisk> hd = (*it)->hardDisk();
10829
/* while the hard disk is attached, the number of children or the
10830
* parent cannot change, so no lock */
10831
if (!hd->parent().isNull() && hd->children().size() == 0)
10832
diffs.push_back (hd);
10837
if (aTask.discardCurrentSnapshot && isLastSnapshot)
10839
/* commit changes to have unused diffs deassociated from this
10840
* machine before deletion (see below) */
10843
/* delete the unused diffs now (and uninit them) because discard
10844
* may fail otherwise (too many children of the hard disk to be
10846
for (std::list< ComObjPtr<HardDisk> >::const_iterator
10847
it = diffs.begin(); it != diffs.end(); ++ it)
10849
/// @todo for now, we ignore errors since we've already
10850
/// and therefore cannot fail. Later, we may want to report a
10851
/// warning through the Progress object
10852
HRESULT rc2 = (*it)->deleteStorageAndWait();
10853
if (SUCCEEDED (rc2))
10857
/* prevent further deletion */
10860
/* discard the current snapshot and state task is in action, the
10861
* current snapshot is the last one. Discard the current snapshot
10862
* after discarding the current state. */
10864
DiscardSnapshotTask subTask (aTask, mData->mCurrentSnapshot);
10865
subTask.subTask = true;
10866
discardSnapshotHandler (subTask);
10868
AutoCaller progressCaller (aTask.progress);
10869
AutoReadLock progressLock (aTask.progress);
10870
if (aTask.progress->completed())
10872
/* the progress can be completed by a subtask only if there
10874
rc = aTask.progress->resultCode();
10875
Assert (FAILED (rc));
10876
errorInSubtask = true;
10879
/* we've committed already, so inform callbacks anyway to ensure
10880
* they don't miss some change */
10881
/// @todo NEWMEDIA check if we need this informCallbacks at all
10882
/// after updating discardCurrentSnapshot functionality
10883
saveFlags |= SaveS_InformCallbacksAnyway;
10886
/* @todo saveSettings() below needs a VirtualBox write lock and we need
10887
* to leave this object's lock to do this to follow the {parent-child}
10888
* locking rule. This is the last chance to do that while we are still
10889
* in a protective state which allows us to temporarily leave the lock*/
10894
/* we have already discarded the current state, so set the execution
10895
* state accordingly no matter of the discard snapshot result */
10896
if (mSSData->mStateFilePath)
10897
setMachineState (MachineState_Saved);
10899
setMachineState (MachineState_PoweredOff);
10901
updateMachineStateOnClient();
10902
stateRestored = true;
10904
/* assign the timestamp from the snapshot */
10905
Assert (RTTimeSpecGetMilli (&snapshotTimeStamp) != 0);
10906
mData->mLastStateChange = snapshotTimeStamp;
10908
/* save all settings, reset the modified flag and commit. Note that we
10909
* do so even if the subtask failed (errorInSubtask=true) because we've
10910
* already committed machine data and deleted old diffs before
10911
* discarding the current snapshot so there is no way to rollback */
10912
HRESULT rc2 = saveSettings (SaveS_ResetCurStateModified | saveFlags);
10914
/// @todo NEWMEDIA return multiple errors
10915
if (errorInSubtask)
10920
if (SUCCEEDED (rc))
10922
/* now, delete the unused diffs (only on success!) and uninit them*/
10923
for (std::list< ComObjPtr<HardDisk> >::const_iterator
10924
it = diffs.begin(); it != diffs.end(); ++ it)
10926
/// @todo for now, we ignore errors since we've already
10927
/// discarded and therefore cannot fail. Later, we may want to
10928
/// report a warning through the Progress object
10929
HRESULT rc2 = (*it)->deleteStorageAndWait();
10930
if (SUCCEEDED (rc2))
10935
catch (HRESULT aRC) { rc = aRC; }
10939
/* preserve existing error info */
10940
ErrorInfoKeeper eik;
10942
if (!errorInSubtask)
10944
/* undo all changes on failure unless the subtask has done so */
10945
rollback (false /* aNotify */);
10948
if (!stateRestored)
10950
/* restore the machine state */
10951
setMachineState (aTask.state);
10952
updateMachineStateOnClient();
10956
if (!errorInSubtask)
10958
/* set the result (this will try to fetch current error info on failure) */
10959
aTask.progress->notifyComplete (rc);
10962
if (SUCCEEDED (rc))
10963
mParent->onSnapshotDiscarded (mData->mUuid, Guid());
10965
LogFlowThisFunc (("Done discarding current state (rc=%08X)\n", rc));
10967
LogFlowThisFuncLeave();
10971
9849
* Locks the attached media.
10973
9851
* All attached hard disks are locked for writing and DVD/floppy are locked for
10990
9868
HRESULT SessionMachine::lockMedia()
10992
AutoCaller autoCaller (this);
9870
AutoCaller autoCaller(this);
10993
9871
AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
10995
AutoWriteLock alock (this);
10997
AssertReturn (mData->mMachineState == MachineState_Starting ||
10998
mData->mMachineState == MachineState_Restoring, E_FAIL);
11000
typedef std::list <ComPtr <IMedium> > MediaList;
11001
MediaList mediaToCheck;
11002
MediaState_T mediaState;
9873
AutoWriteLock alock(this);
9875
AssertReturn( mData->mMachineState == MachineState_Starting
9876
|| mData->mMachineState == MachineState_Restoring
9877
|| mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
9879
typedef std::list <ComPtr<IMedium> > MediaList;
11006
9883
HRESULT rc = S_OK;
11008
/* lock hard disks */
11009
for (HDData::AttachmentList::const_iterator it =
11010
mHDData->mAttachments.begin();
11011
it != mHDData->mAttachments.end(); ++ it)
9885
ErrorInfoKeeper eik(true /* aIsNull */);
9886
MultiResult mrc(S_OK);
9888
/* Lock all medium objects attached to the VM.
9889
* Get status for inaccessible media as well. */
9890
for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
9891
it != mMediaData->mAttachments.end();
11013
ComObjPtr<HardDisk> hd = (*it)->hardDisk();
9894
DeviceType_T devType = (*it)->getType();
9895
ComObjPtr<Medium> medium = (*it)->getMedium();
11015
9897
bool first = true;
11017
9899
/** @todo split out the media locking, and put it into
11018
* HardDiskImpl.cpp, as it needs this functionality too. */
11019
while (!hd.isNull())
9900
* MediumImpl.cpp, as it needs this functionality too. */
9901
while (!medium.isNull())
9903
MediumState_T mediumState = medium->getState();
9905
/* accessibility check must be first, otherwise locking
9906
* interferes with getting the medium state. */
9907
if (mediumState == MediumState_Inaccessible)
9909
rc = medium->RefreshState(&mediumState);
9910
CheckComRCThrowRC(rc);
9912
if (mediumState == MediumState_Inaccessible)
9915
rc = medium->COMGETTER(LastAccessError)(error.asOutParam());
9916
CheckComRCThrowRC(rc);
9919
rc = medium->COMGETTER(Location)(loc.asOutParam());
9920
CheckComRCThrowRC(rc);
9922
/* collect multiple errors */
9925
/* be in sync with MediumBase::setStateError() */
9926
Assert(!error.isEmpty());
9927
mrc = setError(E_FAIL,
9928
tr("Medium '%ls' is not accessible. %ls"),
11023
rc = hd->LockWrite (&mediaState);
11024
CheckComRCThrowRC (rc);
9938
if (devType != DeviceType_DVD)
9940
/* HardDisk and Floppy medium must be locked for writing */
9941
rc = medium->LockWrite(NULL);
9942
CheckComRCThrowRC(rc);
9946
/* DVD medium must be locked for reading */
9947
rc = medium->LockRead(NULL);
9948
CheckComRCThrowRC(rc);
11026
mData->mSession.mLockedMedia.push_back (
11027
Data::Session::LockedMedia::value_type (
11028
ComPtr <IHardDisk> (hd), true));
9951
mData->mSession.mLockedMedia.push_back(
9952
Data::Session::LockedMedia::value_type(
9953
ComPtr<IMedium>(medium), true));
11030
9955
first = false;
11034
rc = hd->LockRead (&mediaState);
11035
CheckComRCThrowRC (rc);
9959
rc = medium->LockRead(NULL);
9960
CheckComRCThrowRC(rc);
11037
mData->mSession.mLockedMedia.push_back (
11038
Data::Session::LockedMedia::value_type (
11039
ComPtr <IHardDisk> (hd), false));
9962
mData->mSession.mLockedMedia.push_back(
9963
Data::Session::LockedMedia::value_type(
9964
ComPtr<IMedium>(medium), false));
11042
if (mediaState == MediaState_Inaccessible)
11043
mediaToCheck.push_back (ComPtr <IHardDisk> (hd));
11045
9968
/* no locks or callers here since there should be no way to
11046
9969
* change the hard disk parent at this point (as it is still
11047
9970
* attached to the machine) */
11052
/* lock the DVD image for reading if mounted */
11054
AutoReadLock driveLock (mDVDDrive);
11055
if (mDVDDrive->data()->state == DriveState_ImageMounted)
11057
ComObjPtr <DVDImage> image = mDVDDrive->data()->image;
11059
rc = image->LockRead (&mediaState);
11060
CheckComRCThrowRC (rc);
11062
mData->mSession.mLockedMedia.push_back (
11063
Data::Session::LockedMedia::value_type (
11064
ComPtr <IDVDImage> (image), false));
11066
if (mediaState == MediaState_Inaccessible)
11067
mediaToCheck.push_back (ComPtr <IDVDImage> (image));
11071
/* lock the floppy image for reading if mounted */
11073
AutoReadLock driveLock (mFloppyDrive);
11074
if (mFloppyDrive->data()->state == DriveState_ImageMounted)
11076
ComObjPtr <FloppyImage> image = mFloppyDrive->data()->image;
11078
rc = image->LockRead (&mediaState);
11079
CheckComRCThrowRC (rc);
11081
mData->mSession.mLockedMedia.push_back (
11082
Data::Session::LockedMedia::value_type (
11083
ComPtr <IFloppyImage> (image), false));
11085
if (mediaState == MediaState_Inaccessible)
11086
mediaToCheck.push_back (ComPtr <IFloppyImage> (image));
11090
/* SUCCEEDED locking all media, now check accessibility */
11092
ErrorInfoKeeper eik (true /* aIsNull */);
11093
MultiResult mrc (S_OK);
11095
/* perform a check of inaccessible media deferred above */
11096
for (MediaList::const_iterator
11097
it = mediaToCheck.begin();
11098
it != mediaToCheck.end(); ++ it)
11100
MediaState_T mediaState;
11101
rc = (*it)->COMGETTER(State) (&mediaState);
11102
CheckComRCThrowRC (rc);
11104
Assert (mediaState == MediaState_LockedRead ||
11105
mediaState == MediaState_LockedWrite);
11107
/* Note that we locked the medium already, so use the error
11108
* value to see if there was an accessibility failure */
11111
rc = (*it)->COMGETTER(LastAccessError) (error.asOutParam());
11112
CheckComRCThrowRC (rc);
11114
if (!error.isEmpty())
11117
rc = (*it)->COMGETTER(Location) (loc.asOutParam());
11118
CheckComRCThrowRC (rc);
11120
/* collect multiple errors */
11123
/* be in sync with MediumBase::setStateError() */
11124
Assert (!error.isEmpty());
11125
mrc = setError (E_FAIL,
11126
tr ("Medium '%ls' is not accessible. %ls"),
11127
loc.raw(), error.raw());
9971
medium = medium->getParent();
11133
9975
eik.restore();
11134
CheckComRCThrowRC ((HRESULT) mrc);
9976
CheckComRCThrowRC((HRESULT)mrc);
11136
9978
catch (HRESULT aRC)
11365
10241
if (mData->mSession.mState == SessionState_Closing)
11368
AssertReturn (!directControl.isNull(), E_FAIL);
10244
AssertReturn(!directControl.isNull(), E_FAIL);
11371
10247
return directControl->UpdateMachineState (mData->mMachineState);
11375
DECLCALLBACK(int) SessionMachine::taskHandler (RTTHREAD /* thread */, void *pvUser)
11377
AssertReturn (pvUser, VERR_INVALID_POINTER);
11379
Task *task = static_cast <Task *> (pvUser);
11382
// it's our responsibility to delete the task
11388
/////////////////////////////////////////////////////////////////////////////
11389
// SnapshotMachine class
11390
/////////////////////////////////////////////////////////////////////////////
11392
DEFINE_EMPTY_CTOR_DTOR (SnapshotMachine)
11394
HRESULT SnapshotMachine::FinalConstruct()
11396
LogFlowThisFunc (("\n"));
11398
/* set the proper type to indicate we're the SnapshotMachine instance */
11399
unconst (mType) = IsSnapshotMachine;
11404
void SnapshotMachine::FinalRelease()
11406
LogFlowThisFunc (("\n"));
11412
* Initializes the SnapshotMachine object when taking a snapshot.
11414
* @param aSessionMachine machine to take a snapshot from
11415
* @param aSnapshotId snapshot ID of this snapshot machine
11416
* @param aStateFilePath file where the execution state will be later saved
11417
* (or NULL for the offline snapshot)
11419
* @note The aSessionMachine must be locked for writing.
11421
HRESULT SnapshotMachine::init (SessionMachine *aSessionMachine,
11422
IN_GUID aSnapshotId,
11423
IN_BSTR aStateFilePath)
11425
LogFlowThisFuncEnter();
11426
LogFlowThisFunc (("mName={%ls}\n", aSessionMachine->mUserData->mName.raw()));
11428
AssertReturn (aSessionMachine && !Guid (aSnapshotId).isEmpty(), E_INVALIDARG);
11430
/* Enclose the state transition NotReady->InInit->Ready */
11431
AutoInitSpan autoInitSpan (this);
11432
AssertReturn (autoInitSpan.isOk(), E_FAIL);
11434
AssertReturn (aSessionMachine->isWriteLockOnCurrentThread(), E_FAIL);
11436
mSnapshotId = aSnapshotId;
11438
/* memorize the primary Machine instance (i.e. not SessionMachine!) */
11439
unconst (mPeer) = aSessionMachine->mPeer;
11440
/* share the parent pointer */
11441
unconst (mParent) = mPeer->mParent;
11443
/* take the pointer to Data to share */
11444
mData.share (mPeer->mData);
11446
/* take the pointer to UserData to share (our UserData must always be the
11447
* same as Machine's data) */
11448
mUserData.share (mPeer->mUserData);
11449
/* make a private copy of all other data (recent changes from SessionMachine) */
11450
mHWData.attachCopy (aSessionMachine->mHWData);
11451
mHDData.attachCopy (aSessionMachine->mHDData);
11453
/* SSData is always unique for SnapshotMachine */
11454
mSSData.allocate();
11455
mSSData->mStateFilePath = aStateFilePath;
11459
/* create copies of all shared folders (mHWData after attiching a copy
11460
* contains just references to original objects) */
11461
for (HWData::SharedFolderList::iterator
11462
it = mHWData->mSharedFolders.begin();
11463
it != mHWData->mSharedFolders.end();
11466
ComObjPtr <SharedFolder> folder;
11467
folder.createObject();
11468
rc = folder->initCopy (this, *it);
11469
CheckComRCReturnRC (rc);
11473
/* associate hard disks with the snapshot
11474
* (Machine::uninitDataAndChildObjects() will deassociate at destruction) */
11475
for (HDData::AttachmentList::const_iterator
11476
it = mHDData->mAttachments.begin();
11477
it != mHDData->mAttachments.end();
11480
rc = (*it)->hardDisk()->attachTo (mData->mUuid, mSnapshotId);
11484
/* create copies of all storage controllers (mStorageControllerData
11485
* after attaching a copy contains just references to original objects) */
11486
mStorageControllers.allocate();
11487
for (StorageControllerList::const_iterator
11488
it = aSessionMachine->mStorageControllers->begin();
11489
it != aSessionMachine->mStorageControllers->end();
11492
ComObjPtr <StorageController> ctrl;
11493
ctrl.createObject();
11494
ctrl->initCopy (this, *it);
11495
mStorageControllers->push_back(ctrl);
11498
/* create all other child objects that will be immutable private copies */
11500
unconst (mBIOSSettings).createObject();
11501
mBIOSSettings->initCopy (this, mPeer->mBIOSSettings);
11503
#ifdef VBOX_WITH_VRDP
11504
unconst (mVRDPServer).createObject();
11505
mVRDPServer->initCopy (this, mPeer->mVRDPServer);
11508
unconst (mDVDDrive).createObject();
11509
mDVDDrive->initCopy (this, mPeer->mDVDDrive);
11511
unconst (mFloppyDrive).createObject();
11512
mFloppyDrive->initCopy (this, mPeer->mFloppyDrive);
11514
unconst (mAudioAdapter).createObject();
11515
mAudioAdapter->initCopy (this, mPeer->mAudioAdapter);
11517
unconst (mUSBController).createObject();
11518
mUSBController->initCopy (this, mPeer->mUSBController);
11520
for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
11522
unconst (mNetworkAdapters [slot]).createObject();
11523
mNetworkAdapters [slot]->initCopy (this, mPeer->mNetworkAdapters [slot]);
11526
for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
11528
unconst (mSerialPorts [slot]).createObject();
11529
mSerialPorts [slot]->initCopy (this, mPeer->mSerialPorts [slot]);
11532
for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
11534
unconst (mParallelPorts [slot]).createObject();
11535
mParallelPorts [slot]->initCopy (this, mPeer->mParallelPorts [slot]);
11538
/* Confirm a successful initialization when it's the case */
11539
autoInitSpan.setSucceeded();
11541
LogFlowThisFuncLeave();
11546
* Initializes the SnapshotMachine object when loading from the settings file.
11548
* @param aMachine machine the snapshot belngs to
11549
* @param aHWNode <Hardware> node
11550
* @param aHDAsNode <HardDiskAttachments> node
11551
* @param aSnapshotId snapshot ID of this snapshot machine
11552
* @param aStateFilePath file where the execution state is saved
11553
* (or NULL for the offline snapshot)
11555
* @note Doesn't lock anything.
11557
HRESULT SnapshotMachine::init (Machine *aMachine,
11558
const settings::Key &aHWNode,
11559
const settings::Key &aHDAsNode,
11560
IN_GUID aSnapshotId, IN_BSTR aStateFilePath)
11562
LogFlowThisFuncEnter();
11563
LogFlowThisFunc (("mName={%ls}\n", aMachine->mUserData->mName.raw()));
11565
AssertReturn (aMachine && !aHWNode.isNull() && !aHDAsNode.isNull() &&
11566
!Guid (aSnapshotId).isEmpty(),
11569
/* Enclose the state transition NotReady->InInit->Ready */
11570
AutoInitSpan autoInitSpan (this);
11571
AssertReturn (autoInitSpan.isOk(), E_FAIL);
11573
/* Don't need to lock aMachine when VirtualBox is starting up */
11575
mSnapshotId = aSnapshotId;
11577
/* memorize the primary Machine instance */
11578
unconst (mPeer) = aMachine;
11579
/* share the parent pointer */
11580
unconst (mParent) = mPeer->mParent;
11582
/* take the pointer to Data to share */
11583
mData.share (mPeer->mData);
11585
* take the pointer to UserData to share
11586
* (our UserData must always be the same as Machine's data)
11588
mUserData.share (mPeer->mUserData);
11589
/* allocate private copies of all other data (will be loaded from settings) */
11590
mHWData.allocate();
11591
mHDData.allocate();
11592
mStorageControllers.allocate();
11594
/* SSData is always unique for SnapshotMachine */
11595
mSSData.allocate();
11596
mSSData->mStateFilePath = aStateFilePath;
11598
/* create all other child objects that will be immutable private copies */
11600
unconst (mBIOSSettings).createObject();
11601
mBIOSSettings->init (this);
11603
#ifdef VBOX_WITH_VRDP
11604
unconst (mVRDPServer).createObject();
11605
mVRDPServer->init (this);
11608
unconst (mDVDDrive).createObject();
11609
mDVDDrive->init (this);
11611
unconst (mFloppyDrive).createObject();
11612
mFloppyDrive->init (this);
11614
unconst (mAudioAdapter).createObject();
11615
mAudioAdapter->init (this);
11617
unconst (mUSBController).createObject();
11618
mUSBController->init (this);
11620
for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
11622
unconst (mNetworkAdapters [slot]).createObject();
11623
mNetworkAdapters [slot]->init (this, slot);
11626
for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
11628
unconst (mSerialPorts [slot]).createObject();
11629
mSerialPorts [slot]->init (this, slot);
11632
for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
11634
unconst (mParallelPorts [slot]).createObject();
11635
mParallelPorts [slot]->init (this, slot);
11638
/* load hardware and harddisk settings */
11640
HRESULT rc = loadHardware (aHWNode);
11641
if (SUCCEEDED (rc))
11642
rc = loadStorageControllers (aHDAsNode, true /* aRegistered */, &mSnapshotId);
11644
if (SUCCEEDED (rc))
11646
/* commit all changes made during the initialization */
11650
/* Confirm a successful initialization when it's the case */
11651
if (SUCCEEDED (rc))
11652
autoInitSpan.setSucceeded();
11654
LogFlowThisFuncLeave();
11659
* Uninitializes this SnapshotMachine object.
11661
void SnapshotMachine::uninit()
11663
LogFlowThisFuncEnter();
11665
/* Enclose the state transition Ready->InUninit->NotReady */
11666
AutoUninitSpan autoUninitSpan (this);
11667
if (autoUninitSpan.uninitDone())
11670
uninitDataAndChildObjects();
11672
/* free the essential data structure last */
11675
unconst (mParent).setNull();
11676
unconst (mPeer).setNull();
11678
LogFlowThisFuncLeave();
11681
// util::Lockable interface
11682
////////////////////////////////////////////////////////////////////////////////
11685
* Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
11686
* with the primary Machine instance (mPeer).
11688
RWLockHandle *SnapshotMachine::lockHandle() const
11690
AssertReturn (!mPeer.isNull(), NULL);
11691
return mPeer->lockHandle();
11694
// public methods only for internal purposes
11695
////////////////////////////////////////////////////////////////////////////////
11698
* Called by the snapshot object associated with this SnapshotMachine when
11699
* snapshot data such as name or description is changed.
11701
* @note Locks this object for writing.
11703
HRESULT SnapshotMachine::onSnapshotChange (Snapshot *aSnapshot)
11705
AutoWriteLock alock (this);
11707
mPeer->saveSnapshotSettings (aSnapshot, SaveSS_UpdateAttrsOp);
11709
/* inform callbacks */
11710
mParent->onSnapshotChange (mData->mUuid, aSnapshot->data().mId);
11714
/* vi: set tabstop=4 shiftwidth=4 expandtab: */