Changeset 24299 in vbox
- Timestamp:
- Nov 3, 2009 6:45:34 PM (15 years ago)
- Location:
- trunk/src/VBox/Main
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/SnapshotImpl.cpp
r24298 r24299 1056 1056 1057 1057 /** 1058 * Abstract base class for SessionMachine:: DeleteSnapshotTask and1059 * SessionMachine:: RestoreSnapshotTask. This is necessary since1058 * Abstract base class for SessionMachine::RestoreSnapshotTask and 1059 * SessionMachine::DeleteSnapshotTask. This is necessary since 1060 1060 * RTThreadCreate cannot call a method as its thread function, so 1061 1061 * instead we have it call the static SessionMachine::taskHandler, … … 1087 1087 }; 1088 1088 1089 /** Discard snapshot task */1090 struct SessionMachine::DeleteSnapshotTask1091 : public SessionMachine::SnapshotTask1092 {1093 DeleteSnapshotTask(SessionMachine *m,1094 Progress *p,1095 Snapshot *s)1096 : SnapshotTask(m, p, s)1097 {}1098 1099 void handler()1100 {1101 pMachine->deleteSnapshotHandler(*this);1102 }1103 1104 private:1105 DeleteSnapshotTask(const SnapshotTask &task)1106 : SnapshotTask(task)1107 {}1108 };1109 1110 1089 /** Restore snapshot state task */ 1111 1090 struct SessionMachine::RestoreSnapshotTask … … 1128 1107 }; 1129 1108 1109 /** Discard snapshot task */ 1110 struct SessionMachine::DeleteSnapshotTask 1111 : public SessionMachine::SnapshotTask 1112 { 1113 DeleteSnapshotTask(SessionMachine *m, 1114 Progress *p, 1115 Snapshot *s) 1116 : SnapshotTask(m, p, s) 1117 {} 1118 1119 void handler() 1120 { 1121 pMachine->deleteSnapshotHandler(*this); 1122 } 1123 1124 private: 1125 DeleteSnapshotTask(const SnapshotTask &task) 1126 : SnapshotTask(task) 1127 {} 1128 }; 1129 1130 /** 1131 * Static SessionMachine method that can get passed to RTThreadCreate to 1132 * have a thread started for a SnapshotTask. See SnapshotTask above. 1133 * 1134 * This calls either RestoreSnapshotTask::handler() or DeleteSnapshotTask::handler(). 1135 */ 1136 1137 /* static */ DECLCALLBACK(int) SessionMachine::taskHandler(RTTHREAD /* thread */, void *pvUser) 1138 { 1139 AssertReturn(pvUser, VERR_INVALID_POINTER); 1140 1141 SnapshotTask *task = static_cast<SnapshotTask*>(pvUser); 1142 task->handler(); 1143 1144 // it's our responsibility to delete the task 1145 delete task; 1146 1147 return 0; 1148 } 1149 1130 1150 //////////////////////////////////////////////////////////////////////////////// 1131 1151 // 1132 // SessionMachine public internal methods1152 // TakeSnapshot methods (SessionMachine and related tasks) 1133 1153 // 1134 1154 //////////////////////////////////////////////////////////////////////////////// 1135 1155 1136 1156 /** 1157 * Implementation for IInternalMachineControl::beginTakingSnapshot(). 1158 * 1159 * Gets called indirectly from Console::TakeSnapshot, which creates a 1160 * progress object in the client and then starts a thread 1161 * (Console::fntTakeSnapshotWorker) which then calls this. 1162 * 1163 * In other words, the asynchronous work for taking snapshots takes place 1164 * on the _client_ (in the Console). This is different from restoring 1165 * or deleting snapshots, which start threads on the server. 1166 * 1167 * This does the server-side work of taking a snapshot: it creates diffencing 1168 * images for all hard disks attached to the machine and then creates a 1169 * Snapshot object with a corresponding SnapshotMachine to save the VM settings. 1170 * 1171 * The client's fntTakeSnapshotWorker() blocks while this takes place. 1172 * After this returns successfully, fntTakeSnapshotWorker() will begin 1173 * saving the machine state to the snapshot object and reconfigure the 1174 * hard disks. 1175 * 1176 * When the console is done, it calls SessionMachine::EndTakingSnapshot(). 1177 * 1137 1178 * @note Locks mParent + this object for writing. 1179 * 1180 * @param aInitiator in: The console on which Console::TakeSnapshot was called. 1181 * @param aName in: The name for the new snapshot. 1182 * @param aDescription in: A description for the new snapshot. 1183 * @param aConsoleProgress in: The console's (client's) progress object. 1184 * @param fTakingSnapshotOnline in: True if an online snapshot is being taken (i.e. machine is running). 1185 * @param aStateFilePath out: name of file in snapshots folder to which the console should write the VM state. 1186 * @return 1138 1187 */ 1139 1188 STDMETHODIMP SessionMachine::BeginTakingSnapshot(IConsole *aInitiator, … … 1280 1329 1281 1330 /** 1331 * Implementation for IInternalMachineControl::beginTakingSnapshot(). 1332 * 1333 * Called by the Console when it's done saving the VM state into the snapshot 1334 * (if online) and reconfiguring the hard disks. See BeginTakingSnapshot() above. 1335 * 1336 * This also gets called if the console part of snapshotting failed after the 1337 * BeginTakingSnapshot() call, to clean up the server side. 1338 * 1282 1339 * @note Locks this object for writing. 1340 * 1341 * @param aSuccess Whether Console was successful with the client-side snapshot things. 1342 * @return 1283 1343 */ 1284 1344 STDMETHODIMP SessionMachine::EndTakingSnapshot(BOOL aSuccess) … … 1311 1371 1312 1372 /** 1313 * @note Locks mParent + this + children objects for writing! 1314 */ 1315 STDMETHODIMP SessionMachine::DeleteSnapshot(IConsole *aInitiator, 1316 IN_BSTR aId, 1317 MachineState_T *aMachineState, 1318 IProgress **aProgress) 1373 * Internal helper method to finalize taking a snapshot. Gets called from 1374 * SessionMachine::EndTakingSnapshot() to finalize the server-side 1375 * parts of snapshotting. 1376 * 1377 * This also gets called from SessionMachine::uninit() if an untaken 1378 * snapshot needs cleaning up. 1379 * 1380 * Expected to be called after completing *all* the tasks related to 1381 * taking the snapshot, either successfully or unsuccessfilly. 1382 * 1383 * @param aSuccess TRUE if the snapshot has been taken successfully. 1384 * 1385 * @note Locks this objects for writing. 1386 */ 1387 HRESULT SessionMachine::endTakingSnapshot(BOOL aSuccess) 1319 1388 { 1320 1389 LogFlowThisFuncEnter(); 1321 1390 1322 Guid id(aId);1323 AssertReturn(aInitiator && !id.isEmpty(), E_INVALIDARG);1324 AssertReturn(aMachineState && aProgress, E_POINTER);1325 1326 1391 AutoCaller autoCaller(this); 1327 1392 AssertComRCReturn (autoCaller.rc(), autoCaller.rc()); 1328 1393 1329 /* saveSettings() needs mParent lock */1330 1394 AutoMultiWriteLock2 alock(mParent, this); 1331 1332 ComAssertRet (!Global::IsOnlineOrTransient (mData->mMachineState), E_FAIL); 1333 1334 AutoWriteLock treeLock(snapshotsTreeLockHandle()); 1335 1336 ComObjPtr<Snapshot> snapshot; 1337 HRESULT rc = findSnapshot(id, snapshot, true /* aSetError */); 1338 CheckComRCReturnRC(rc); 1339 1340 AutoWriteLock snapshotLock(snapshot); 1341 1342 size_t childrenCount = snapshot->getChildrenCount(); 1343 if (childrenCount > 1) 1344 return setError(VBOX_E_INVALID_OBJECT_STATE, 1345 tr("Snapshot '%s' of the machine '%ls' cannot be deleted because it has has more than one child snapshot (%d)"), 1346 snapshot->getName().c_str(), 1347 mUserData->mName.raw(), 1348 childrenCount); 1349 1350 /* If the snapshot being discarded is the current one, ensure current 1351 * settings are committed and saved. 1352 */ 1353 if (snapshot == mData->mCurrentSnapshot) 1354 { 1355 if (isModified()) 1356 { 1357 rc = saveSettings(); 1358 CheckComRCReturnRC(rc); 1359 } 1360 } 1361 1362 /* create a progress object. The number of operations is: 1363 * 1 (preparing) + # of hard disks + 1 if the snapshot is online 1364 */ 1365 ComObjPtr<Progress> progress; 1366 progress.createObject(); 1367 rc = progress->init(mParent, aInitiator, 1368 BstrFmt(tr("Discarding snapshot '%s'"), 1369 snapshot->getName().c_str()), 1370 FALSE /* aCancelable */, 1371 1 + (ULONG)snapshot->getSnapshotMachine()->mMediaData->mAttachments.size() 1372 + (snapshot->stateFilePath().length() ? 1 : 0), 1373 Bstr(tr("Preparing to discard snapshot"))); 1374 AssertComRCReturn(rc, rc); 1375 1376 /* create and start the task on a separate thread */ 1377 DeleteSnapshotTask *task = new DeleteSnapshotTask(this, progress, snapshot); 1378 int vrc = RTThreadCreate(NULL, 1379 taskHandler, 1380 (void*)task, 1381 0, 1382 RTTHREADTYPE_MAIN_WORKER, 1383 0, 1384 "DeleteSnapshot"); 1385 if (RT_FAILURE(vrc)) 1386 { 1387 delete task; 1388 return E_FAIL; 1389 } 1390 1391 /* set the proper machine state (note: after creating a Task instance) */ 1392 setMachineState(MachineState_DeletingSnapshot); 1393 1394 /* return the progress to the caller */ 1395 progress.queryInterfaceTo(aProgress); 1396 1397 /* return the new state to the caller */ 1398 *aMachineState = mData->mMachineState; 1395 // saveSettings needs VirtualBox lock 1396 1397 AssertReturn(!mSnapshotData.mSnapshot.isNull(), E_FAIL); 1398 1399 MultiResult rc(S_OK); 1400 1401 ComObjPtr<Snapshot> pOldFirstSnap = mData->mFirstSnapshot; 1402 ComObjPtr<Snapshot> pOldCurrentSnap = mData->mCurrentSnapshot; 1403 1404 bool fOnline = Global::IsOnline(mSnapshotData.mLastState); 1405 1406 if (aSuccess) 1407 { 1408 // new snapshot becomes the current one 1409 mData->mCurrentSnapshot = mSnapshotData.mSnapshot; 1410 1411 /* memorize the first snapshot if necessary */ 1412 if (!mData->mFirstSnapshot) 1413 mData->mFirstSnapshot = mData->mCurrentSnapshot; 1414 1415 if (!fOnline) 1416 /* the machine was powered off or saved when taking a snapshot, so 1417 * reset the mCurrentStateModified flag */ 1418 mData->mCurrentStateModified = FALSE; 1419 1420 rc = saveSettings(); 1421 } 1422 1423 if (aSuccess && SUCCEEDED(rc)) 1424 { 1425 /* associate old hard disks with the snapshot and do locking/unlocking*/ 1426 fixupMedia(true /* aCommit */, fOnline); 1427 1428 /* inform callbacks */ 1429 mParent->onSnapshotTaken(mData->mUuid, 1430 mSnapshotData.mSnapshot->getId()); 1431 } 1432 else 1433 { 1434 /* delete all differencing hard disks created (this will also attach 1435 * their parents back by rolling back mMediaData) */ 1436 fixupMedia(false /* aCommit */); 1437 1438 mData->mFirstSnapshot = pOldFirstSnap; // might have been changed above 1439 mData->mCurrentSnapshot = pOldCurrentSnap; // might have been changed above 1440 1441 /* delete the saved state file (it might have been already created) */ 1442 if (mSnapshotData.mSnapshot->stateFilePath().length()) 1443 RTFileDelete(mSnapshotData.mSnapshot->stateFilePath().c_str()); 1444 1445 mSnapshotData.mSnapshot->uninit(); 1446 } 1447 1448 /* clear out the snapshot data */ 1449 mSnapshotData.mLastState = MachineState_Null; 1450 mSnapshotData.mSnapshot.setNull(); 1399 1451 1400 1452 LogFlowThisFuncLeave(); 1401 1402 return S_OK; 1403 } 1404 1405 /** 1406 * @note Locks this + children objects for writing! 1453 return rc; 1454 } 1455 1456 //////////////////////////////////////////////////////////////////////////////// 1457 // 1458 // RestoreSnapshot methods (SessionMachine and related tasks) 1459 // 1460 //////////////////////////////////////////////////////////////////////////////// 1461 1462 /** 1463 * Implementation for IInternalMachineControl::restoreSnapshot(). 1464 * 1465 * Gets called from Console::RestoreSnapshot(), and that's basically the 1466 * only thing Console does. Restoring a snapshot happens entirely on the 1467 * server side since the machine cannot be running. 1468 * 1469 * This creates a new thread that does the work and returns a progress 1470 * object to the client which is then returned to the caller of 1471 * Console::RestoreSnapshot(). 1472 * 1473 * Actual work then takes place in RestoreSnapshotTask::handler(). 1474 * 1475 * @note Locks this + children objects for writing! 1476 * 1477 * @param aInitiator in: rhe console on which Console::RestoreSnapshot was called. 1478 * @param aSnapshot in: the snapshot to restore. 1479 * @param aMachineState in: client-side machine state. 1480 * @param aProgress out: progress object to monitor restore thread. 1481 * @return 1407 1482 */ 1408 1483 STDMETHODIMP SessionMachine::RestoreSnapshot(IConsole *aInitiator, … … 1421 1496 AutoWriteLock alock(this); 1422 1497 1498 // machine must not be running 1423 1499 ComAssertRet(!Global::IsOnlineOrTransient(mData->mMachineState), 1424 1500 E_FAIL); … … 1426 1502 ComObjPtr<Snapshot> pSnapshot(static_cast<Snapshot*>(aSnapshot)); 1427 1503 1428 / * create a progress object. The number of operations is: 1 (preparing) + #1429 *of hard disks + 1 (if we need to copy the saved state file) */1504 // create a progress object. The number of operations is: 1505 // 1 (preparing) + # of hard disks + 1 (if we need to copy the saved state file) */ 1430 1506 ComObjPtr<Progress> progress; 1431 1507 progress.createObject(); … … 1511 1587 } 1512 1588 1589 /** 1590 * Worker method for the restore snapshot thread created by SessionMachine::RestoreSnapshot(). 1591 * This method gets called indirectly through SessionMachine::taskHandler() which then 1592 * calls RestoreSnapshotTask::handler(). 1593 * 1594 * The RestoreSnapshotTask contains the progress object returned to the console by 1595 * SessionMachine::RestoreSnapshot, through which progress and results are reported. 1596 * 1597 * @note Locks mParent + this object for writing. 1598 * 1599 * @param aTask Task data. 1600 */ 1601 void SessionMachine::restoreSnapshotHandler(RestoreSnapshotTask &aTask) 1602 { 1603 LogFlowThisFuncEnter(); 1604 1605 AutoCaller autoCaller(this); 1606 1607 LogFlowThisFunc(("state=%d\n", autoCaller.state())); 1608 if (!autoCaller.isOk()) 1609 { 1610 /* we might have been uninitialized because the session was accidentally 1611 * closed by the client, so don't assert */ 1612 aTask.pProgress->notifyComplete(E_FAIL, 1613 COM_IIDOF(IMachine), 1614 getComponentName(), 1615 tr("The session has been accidentally closed")); 1616 1617 LogFlowThisFuncLeave(); 1618 return; 1619 } 1620 1621 /* saveSettings() needs mParent lock */ 1622 AutoWriteLock vboxLock(mParent); 1623 1624 /* @todo We don't need mParent lock so far so unlock() it. Better is to 1625 * provide an AutoWriteLock argument that lets create a non-locking 1626 * instance */ 1627 vboxLock.unlock(); 1628 1629 AutoWriteLock alock(this); 1630 1631 /* discard all current changes to mUserData (name, OSType etc.) (note that 1632 * the machine is powered off, so there is no need to inform the direct 1633 * session) */ 1634 if (isModified()) 1635 rollback(false /* aNotify */); 1636 1637 HRESULT rc = S_OK; 1638 1639 bool stateRestored = false; 1640 1641 try 1642 { 1643 /* discard the saved state file if the machine was Saved prior to this 1644 * operation */ 1645 if (aTask.machineStateBackup == MachineState_Saved) 1646 { 1647 Assert(!mSSData->mStateFilePath.isEmpty()); 1648 RTFileDelete(mSSData->mStateFilePath.c_str()); 1649 mSSData->mStateFilePath.setNull(); 1650 aTask.modifyBackedUpState(MachineState_PoweredOff); 1651 rc = saveStateSettings(SaveSTS_StateFilePath); 1652 CheckComRCThrowRC(rc); 1653 } 1654 1655 RTTIMESPEC snapshotTimeStamp; 1656 RTTimeSpecSetMilli(&snapshotTimeStamp, 0); 1657 1658 { 1659 AutoReadLock snapshotLock(aTask.pSnapshot); 1660 1661 /* remember the timestamp of the snapshot we're restoring from */ 1662 snapshotTimeStamp = aTask.pSnapshot->getTimeStamp(); 1663 1664 ComPtr<SnapshotMachine> pSnapshotMachine(aTask.pSnapshot->getSnapshotMachine()); 1665 1666 /* copy all hardware data from the snapshot */ 1667 copyFrom(pSnapshotMachine); 1668 1669 LogFlowThisFunc(("Restoring hard disks from the snapshot...\n")); 1670 1671 /* restore the attachments from the snapshot */ 1672 mMediaData.backup(); 1673 mMediaData->mAttachments = pSnapshotMachine->mMediaData->mAttachments; 1674 1675 /* leave the locks before the potentially lengthy operation */ 1676 snapshotLock.unlock(); 1677 alock.leave(); 1678 1679 rc = createImplicitDiffs(mUserData->mSnapshotFolderFull, 1680 aTask.pProgress, 1681 1, 1682 false /* aOnline */); 1683 1684 alock.enter(); 1685 snapshotLock.lock(); 1686 1687 CheckComRCThrowRC(rc); 1688 1689 /* Note: on success, current (old) hard disks will be 1690 * deassociated/deleted on #commit() called from #saveSettings() at 1691 * the end. On failure, newly created implicit diffs will be 1692 * deleted by #rollback() at the end. */ 1693 1694 /* should not have a saved state file associated at this point */ 1695 Assert(mSSData->mStateFilePath.isEmpty()); 1696 1697 if (!aTask.pSnapshot->stateFilePath().isEmpty()) 1698 { 1699 Utf8Str snapStateFilePath = aTask.pSnapshot->stateFilePath(); 1700 1701 Utf8Str stateFilePath = Utf8StrFmt("%ls%c{%RTuuid}.sav", 1702 mUserData->mSnapshotFolderFull.raw(), 1703 RTPATH_DELIMITER, 1704 mData->mUuid.raw()); 1705 1706 LogFlowThisFunc(("Copying saved state file from '%s' to '%s'...\n", 1707 snapStateFilePath.raw(), stateFilePath.raw())); 1708 1709 aTask.pProgress->SetNextOperation(Bstr(tr("Restoring the execution state")), 1710 aTask.m_ulStateFileSizeMB); // weight 1711 1712 /* leave the lock before the potentially lengthy operation */ 1713 snapshotLock.unlock(); 1714 alock.leave(); 1715 1716 /* copy the state file */ 1717 int vrc = RTFileCopyEx(snapStateFilePath.c_str(), 1718 stateFilePath.c_str(), 1719 0, 1720 progressCallback, 1721 aTask.pProgress); 1722 1723 alock.enter(); 1724 snapshotLock.lock(); 1725 1726 if (RT_SUCCESS(vrc)) 1727 mSSData->mStateFilePath = stateFilePath; 1728 else 1729 throw setError(E_FAIL, 1730 tr("Could not copy the state file '%s' to '%s' (%Rrc)"), 1731 snapStateFilePath.raw(), 1732 stateFilePath.raw(), 1733 vrc); 1734 } 1735 1736 LogFlowThisFunc(("Setting new current snapshot {%RTuuid}\n", aTask.pSnapshot->getId().raw())); 1737 /* make the snapshot we restored from the current snapshot */ 1738 mData->mCurrentSnapshot = aTask.pSnapshot; 1739 } 1740 1741 /* grab differencing hard disks from the old attachments that will 1742 * become unused and need to be auto-deleted */ 1743 1744 std::list< ComObjPtr<MediumAttachment> > llDiffAttachmentsToDelete; 1745 1746 for (MediaData::AttachmentList::const_iterator it = mMediaData.backedUpData()->mAttachments.begin(); 1747 it != mMediaData.backedUpData()->mAttachments.end(); 1748 ++it) 1749 { 1750 ComObjPtr<MediumAttachment> pAttach = *it; 1751 ComObjPtr<Medium> pMedium = pAttach->medium(); 1752 1753 /* while the hard disk is attached, the number of children or the 1754 * parent cannot change, so no lock */ 1755 if ( !pMedium.isNull() 1756 && pAttach->type() == DeviceType_HardDisk 1757 && !pMedium->parent().isNull() 1758 && pMedium->children().size() == 0 1759 ) 1760 { 1761 LogFlowThisFunc(("Picked differencing image '%s' for deletion\n", pMedium->name().raw())); 1762 1763 llDiffAttachmentsToDelete.push_back(pAttach); 1764 } 1765 } 1766 1767 int saveFlags = 0; 1768 1769 /* @todo saveSettings() below needs a VirtualBox write lock and we need 1770 * to leave this object's lock to do this to follow the {parent-child} 1771 * locking rule. This is the last chance to do that while we are still 1772 * in a protective state which allows us to temporarily leave the lock*/ 1773 alock.unlock(); 1774 vboxLock.lock(); 1775 alock.lock(); 1776 1777 /* we have already discarded the current state, so set the execution 1778 * state accordingly no matter of the discard snapshot result */ 1779 if (!mSSData->mStateFilePath.isEmpty()) 1780 setMachineState(MachineState_Saved); 1781 else 1782 setMachineState(MachineState_PoweredOff); 1783 1784 updateMachineStateOnClient(); 1785 stateRestored = true; 1786 1787 /* assign the timestamp from the snapshot */ 1788 Assert(RTTimeSpecGetMilli (&snapshotTimeStamp) != 0); 1789 mData->mLastStateChange = snapshotTimeStamp; 1790 1791 // detach the current-state diffs that we detected above and build a list of 1792 // images to delete _after_ saveSettings() 1793 1794 std::list< ComObjPtr<Medium> > llDiffsToDelete; 1795 1796 for (std::list< ComObjPtr<MediumAttachment> >::iterator it = llDiffAttachmentsToDelete.begin(); 1797 it != llDiffAttachmentsToDelete.end(); 1798 ++it) 1799 { 1800 ComObjPtr<MediumAttachment> pAttach = *it; // guaranteed to have only attachments where medium != NULL 1801 ComObjPtr<Medium> pMedium = pAttach->medium(); 1802 1803 AutoWriteLock mlock(pMedium); 1804 1805 LogFlowThisFunc(("Detaching old current state in differencing image '%s'\n", pMedium->name().raw())); 1806 1807 mMediaData->mAttachments.remove(pAttach); 1808 pMedium->detachFrom(mData->mUuid); 1809 1810 llDiffsToDelete.push_back(pMedium); 1811 } 1812 1813 // save all settings, reset the modified flag and commit; 1814 rc = saveSettings(SaveS_ResetCurStateModified | saveFlags); 1815 CheckComRCThrowRC(rc); 1816 // from here on we cannot roll back on failure any more 1817 1818 for (std::list< ComObjPtr<Medium> >::iterator it = llDiffsToDelete.begin(); 1819 it != llDiffsToDelete.end(); 1820 ++it) 1821 { 1822 ComObjPtr<Medium> &pMedium = *it; 1823 LogFlowThisFunc(("Deleting old current state in differencing image '%s'\n", pMedium->name().raw())); 1824 1825 HRESULT rc2 = pMedium->deleteStorageAndWait(); 1826 // ignore errors here because we cannot roll back after saveSettings() above 1827 if (SUCCEEDED(rc2)) 1828 pMedium->uninit(); 1829 } 1830 } 1831 catch (HRESULT aRC) 1832 { 1833 rc = aRC; 1834 } 1835 1836 if (FAILED(rc)) 1837 { 1838 /* preserve existing error info */ 1839 ErrorInfoKeeper eik; 1840 1841 /* undo all changes on failure */ 1842 rollback(false /* aNotify */); 1843 1844 if (!stateRestored) 1845 { 1846 /* restore the machine state */ 1847 setMachineState(aTask.machineStateBackup); 1848 updateMachineStateOnClient(); 1849 } 1850 } 1851 1852 /* set the result (this will try to fetch current error info on failure) */ 1853 aTask.pProgress->notifyComplete(rc); 1854 1855 if (SUCCEEDED(rc)) 1856 mParent->onSnapshotDeleted(mData->mUuid, Guid()); 1857 1858 LogFlowThisFunc(("Done restoring snapshot (rc=%08X)\n", rc)); 1859 1860 LogFlowThisFuncLeave(); 1861 } 1862 1513 1863 //////////////////////////////////////////////////////////////////////////////// 1514 1864 // 1515 // SessionMachine public internal methods related to snapshots1865 // DeleteSnapshot methods (SessionMachine and related tasks) 1516 1866 // 1517 1867 //////////////////////////////////////////////////////////////////////////////// 1518 1868 1519 /* static */ 1520 DECLCALLBACK(int) SessionMachine::taskHandler(RTTHREAD /* thread */, void *pvUser) 1521 { 1522 AssertReturn(pvUser, VERR_INVALID_POINTER); 1523 1524 SnapshotTask *task = static_cast<SnapshotTask*>(pvUser); 1525 task->handler(); 1526 1527 // it's our responsibility to delete the task 1528 delete task; 1529 1530 return 0; 1531 } 1532 1533 /** 1534 * Helper method to finalize taking a snapshot. Gets called to finalize the 1535 * "take snapshot" procedure, either from the public SessionMachine::EndTakingSnapshot() 1536 * if taking the snapshot failed/was aborted or from the takeSnapshotHandler thread 1537 * when taking the snapshot succeeded. 1538 * 1539 * Expected to be called after completing *all* the tasks related to taking the 1540 * snapshot, either successfully or unsuccessfilly. 1541 * 1542 * @param aSuccess TRUE if the snapshot has been taken successfully. 1543 * 1544 * @note Locks this objects for writing. 1545 */ 1546 HRESULT SessionMachine::endTakingSnapshot(BOOL aSuccess) 1869 /** 1870 * Implementation for IInternalMachineControl::deleteSnapshot(). 1871 * 1872 * Gets called from Console::DeleteSnapshot(), and that's basically the 1873 * only thing Console does. Deleting a snapshot happens entirely on the 1874 * server side since the machine cannot be running. 1875 * 1876 * This creates a new thread that does the work and returns a progress 1877 * object to the client which is then returned to the caller of 1878 * Console::DeleteSnapshot(). 1879 * 1880 * Actual work then takes place in DeleteSnapshotTask::handler(). 1881 * 1882 * @note Locks mParent + this + children objects for writing! 1883 */ 1884 STDMETHODIMP SessionMachine::DeleteSnapshot(IConsole *aInitiator, 1885 IN_BSTR aId, 1886 MachineState_T *aMachineState, 1887 IProgress **aProgress) 1547 1888 { 1548 1889 LogFlowThisFuncEnter(); 1549 1890 1891 Guid id(aId); 1892 AssertReturn(aInitiator && !id.isEmpty(), E_INVALIDARG); 1893 AssertReturn(aMachineState && aProgress, E_POINTER); 1894 1550 1895 AutoCaller autoCaller(this); 1551 1896 AssertComRCReturn (autoCaller.rc(), autoCaller.rc()); 1552 1897 1898 /* saveSettings() needs mParent lock */ 1553 1899 AutoMultiWriteLock2 alock(mParent, this); 1554 // saveSettings needs VirtualBox lock 1555 1556 AssertReturn(!mSnapshotData.mSnapshot.isNull(), E_FAIL); 1557 1558 MultiResult rc(S_OK); 1559 1560 ComObjPtr<Snapshot> pOldFirstSnap = mData->mFirstSnapshot; 1561 ComObjPtr<Snapshot> pOldCurrentSnap = mData->mCurrentSnapshot; 1562 1563 bool fOnline = Global::IsOnline(mSnapshotData.mLastState); 1564 1565 if (aSuccess) 1566 { 1567 // new snapshot becomes the current one 1568 mData->mCurrentSnapshot = mSnapshotData.mSnapshot; 1569 1570 /* memorize the first snapshot if necessary */ 1571 if (!mData->mFirstSnapshot) 1572 mData->mFirstSnapshot = mData->mCurrentSnapshot; 1573 1574 if (!fOnline) 1575 /* the machine was powered off or saved when taking a snapshot, so 1576 * reset the mCurrentStateModified flag */ 1577 mData->mCurrentStateModified = FALSE; 1578 1579 rc = saveSettings(); 1580 } 1581 1582 if (aSuccess && SUCCEEDED(rc)) 1583 { 1584 /* associate old hard disks with the snapshot and do locking/unlocking*/ 1585 fixupMedia(true /* aCommit */, fOnline); 1586 1587 /* inform callbacks */ 1588 mParent->onSnapshotTaken(mData->mUuid, 1589 mSnapshotData.mSnapshot->getId()); 1590 } 1591 else 1592 { 1593 /* delete all differencing hard disks created (this will also attach 1594 * their parents back by rolling back mMediaData) */ 1595 fixupMedia(false /* aCommit */); 1596 1597 mData->mFirstSnapshot = pOldFirstSnap; // might have been changed above 1598 mData->mCurrentSnapshot = pOldCurrentSnap; // might have been changed above 1599 1600 /* delete the saved state file (it might have been already created) */ 1601 if (mSnapshotData.mSnapshot->stateFilePath().length()) 1602 RTFileDelete(mSnapshotData.mSnapshot->stateFilePath().c_str()); 1603 1604 mSnapshotData.mSnapshot->uninit(); 1605 } 1606 1607 /* clear out the snapshot data */ 1608 mSnapshotData.mLastState = MachineState_Null; 1609 mSnapshotData.mSnapshot.setNull(); 1900 1901 // machine must not be running 1902 ComAssertRet(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL); 1903 1904 AutoWriteLock treeLock(snapshotsTreeLockHandle()); 1905 1906 ComObjPtr<Snapshot> snapshot; 1907 HRESULT rc = findSnapshot(id, snapshot, true /* aSetError */); 1908 CheckComRCReturnRC(rc); 1909 1910 AutoWriteLock snapshotLock(snapshot); 1911 1912 size_t childrenCount = snapshot->getChildrenCount(); 1913 if (childrenCount > 1) 1914 return setError(VBOX_E_INVALID_OBJECT_STATE, 1915 tr("Snapshot '%s' of the machine '%ls' cannot be deleted. because it has %d child snapshots, which is more than the one snapshot allowed for deletion"), 1916 snapshot->getName().c_str(), 1917 mUserData->mName.raw(), 1918 childrenCount); 1919 1920 /* If the snapshot being discarded is the current one, ensure current 1921 * settings are committed and saved. 1922 */ 1923 if (snapshot == mData->mCurrentSnapshot) 1924 { 1925 if (isModified()) 1926 { 1927 rc = saveSettings(); 1928 CheckComRCReturnRC(rc); 1929 } 1930 } 1931 1932 /* create a progress object. The number of operations is: 1933 * 1 (preparing) + # of hard disks + 1 if the snapshot is online 1934 */ 1935 ComObjPtr<Progress> progress; 1936 progress.createObject(); 1937 rc = progress->init(mParent, aInitiator, 1938 BstrFmt(tr("Discarding snapshot '%s'"), 1939 snapshot->getName().c_str()), 1940 FALSE /* aCancelable */, 1941 1 + (ULONG)snapshot->getSnapshotMachine()->mMediaData->mAttachments.size() 1942 + (snapshot->stateFilePath().length() ? 1 : 0), 1943 Bstr(tr("Preparing to discard snapshot"))); 1944 AssertComRCReturn(rc, rc); 1945 1946 /* create and start the task on a separate thread */ 1947 DeleteSnapshotTask *task = new DeleteSnapshotTask(this, progress, snapshot); 1948 int vrc = RTThreadCreate(NULL, 1949 taskHandler, 1950 (void*)task, 1951 0, 1952 RTTHREADTYPE_MAIN_WORKER, 1953 0, 1954 "DeleteSnapshot"); 1955 if (RT_FAILURE(vrc)) 1956 { 1957 delete task; 1958 return E_FAIL; 1959 } 1960 1961 /* set the proper machine state (note: after creating a Task instance) */ 1962 setMachineState(MachineState_DeletingSnapshot); 1963 1964 /* return the progress to the caller */ 1965 progress.queryInterfaceTo(aProgress); 1966 1967 /* return the new state to the caller */ 1968 *aMachineState = mData->mMachineState; 1610 1969 1611 1970 LogFlowThisFuncLeave(); 1612 return rc; 1971 1972 return S_OK; 1613 1973 } 1614 1974 … … 1634 1994 const Guid &aSnapshotId) 1635 1995 : hd(aHd), 1636 chain 1996 chain(aChain), 1637 1997 replaceHd(aReplaceHd), 1638 1998 replaceHda(aReplaceHda), … … 1651 2011 1652 2012 /** 1653 * Discard snapshot task handler. Must be called only by1654 * DeleteSnapshotTask::handler()!1655 * 1656 * When aTask.subTask is true, the associated progress object is left1657 * uncompleted on success. On failure, the progress is marked as completed1658 * regardless of this parameter.2013 * Worker method for the delete snapshot thread created by SessionMachine::DeleteSnapshot(). 2014 * This method gets called indirectly through SessionMachine::taskHandler() which then 2015 * calls DeleteSnapshotTask::handler(). 2016 * 2017 * The DeleteSnapshotTask contains the progress object returned to the console by 2018 * SessionMachine::DeleteSnapshot, through which progress and results are reported. 1659 2019 * 1660 2020 * @note Locks mParent + this + child objects for writing! 2021 * 2022 * @param aTask Task data. 1661 2023 */ 1662 2024 void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask) … … 1933 2295 } 1934 2296 1935 /**1936 * Restore snapshot state task handler. Must be called only by1937 * RestoreSnapshotTask::handler()!1938 *1939 * @note Locks mParent + this object for writing.1940 */1941 void SessionMachine::restoreSnapshotHandler(RestoreSnapshotTask &aTask)1942 {1943 LogFlowThisFuncEnter();1944 1945 AutoCaller autoCaller(this);1946 1947 LogFlowThisFunc(("state=%d\n", autoCaller.state()));1948 if (!autoCaller.isOk())1949 {1950 /* we might have been uninitialized because the session was accidentally1951 * closed by the client, so don't assert */1952 aTask.pProgress->notifyComplete(E_FAIL,1953 COM_IIDOF(IMachine),1954 getComponentName(),1955 tr("The session has been accidentally closed"));1956 1957 LogFlowThisFuncLeave();1958 return;1959 }1960 1961 /* saveSettings() needs mParent lock */1962 AutoWriteLock vboxLock(mParent);1963 1964 /* @todo We don't need mParent lock so far so unlock() it. Better is to1965 * provide an AutoWriteLock argument that lets create a non-locking1966 * instance */1967 vboxLock.unlock();1968 1969 AutoWriteLock alock(this);1970 1971 /* discard all current changes to mUserData (name, OSType etc.) (note that1972 * the machine is powered off, so there is no need to inform the direct1973 * session) */1974 if (isModified())1975 rollback(false /* aNotify */);1976 1977 HRESULT rc = S_OK;1978 1979 bool stateRestored = false;1980 1981 try1982 {1983 /* discard the saved state file if the machine was Saved prior to this1984 * operation */1985 if (aTask.machineStateBackup == MachineState_Saved)1986 {1987 Assert(!mSSData->mStateFilePath.isEmpty());1988 RTFileDelete(mSSData->mStateFilePath.c_str());1989 mSSData->mStateFilePath.setNull();1990 aTask.modifyBackedUpState(MachineState_PoweredOff);1991 rc = saveStateSettings(SaveSTS_StateFilePath);1992 CheckComRCThrowRC(rc);1993 }1994 1995 RTTIMESPEC snapshotTimeStamp;1996 RTTimeSpecSetMilli(&snapshotTimeStamp, 0);1997 1998 {1999 AutoReadLock snapshotLock(aTask.pSnapshot);2000 2001 /* remember the timestamp of the snapshot we're restoring from */2002 snapshotTimeStamp = aTask.pSnapshot->getTimeStamp();2003 2004 ComPtr<SnapshotMachine> pSnapshotMachine(aTask.pSnapshot->getSnapshotMachine());2005 2006 /* copy all hardware data from the snapshot */2007 copyFrom(pSnapshotMachine);2008 2009 LogFlowThisFunc(("Restoring hard disks from the snapshot...\n"));2010 2011 /* restore the attachments from the snapshot */2012 mMediaData.backup();2013 mMediaData->mAttachments = pSnapshotMachine->mMediaData->mAttachments;2014 2015 /* leave the locks before the potentially lengthy operation */2016 snapshotLock.unlock();2017 alock.leave();2018 2019 rc = createImplicitDiffs(mUserData->mSnapshotFolderFull,2020 aTask.pProgress,2021 1,2022 false /* aOnline */);2023 2024 alock.enter();2025 snapshotLock.lock();2026 2027 CheckComRCThrowRC(rc);2028 2029 /* Note: on success, current (old) hard disks will be2030 * deassociated/deleted on #commit() called from #saveSettings() at2031 * the end. On failure, newly created implicit diffs will be2032 * deleted by #rollback() at the end. */2033 2034 /* should not have a saved state file associated at this point */2035 Assert(mSSData->mStateFilePath.isEmpty());2036 2037 if (!aTask.pSnapshot->stateFilePath().isEmpty())2038 {2039 Utf8Str snapStateFilePath = aTask.pSnapshot->stateFilePath();2040 2041 Utf8Str stateFilePath = Utf8StrFmt("%ls%c{%RTuuid}.sav",2042 mUserData->mSnapshotFolderFull.raw(),2043 RTPATH_DELIMITER,2044 mData->mUuid.raw());2045 2046 LogFlowThisFunc(("Copying saved state file from '%s' to '%s'...\n",2047 snapStateFilePath.raw(), stateFilePath.raw()));2048 2049 aTask.pProgress->SetNextOperation(Bstr(tr("Restoring the execution state")),2050 aTask.m_ulStateFileSizeMB); // weight2051 2052 /* leave the lock before the potentially lengthy operation */2053 snapshotLock.unlock();2054 alock.leave();2055 2056 /* copy the state file */2057 int vrc = RTFileCopyEx(snapStateFilePath.c_str(),2058 stateFilePath.c_str(),2059 0,2060 progressCallback,2061 aTask.pProgress);2062 2063 alock.enter();2064 snapshotLock.lock();2065 2066 if (RT_SUCCESS(vrc))2067 mSSData->mStateFilePath = stateFilePath;2068 else2069 throw setError(E_FAIL,2070 tr("Could not copy the state file '%s' to '%s' (%Rrc)"),2071 snapStateFilePath.raw(),2072 stateFilePath.raw(),2073 vrc);2074 }2075 2076 LogFlowThisFunc(("Setting new current snapshot {%RTuuid}\n", aTask.pSnapshot->getId().raw()));2077 /* make the snapshot we restored from the current snapshot */2078 mData->mCurrentSnapshot = aTask.pSnapshot;2079 }2080 2081 /* grab differencing hard disks from the old attachments that will2082 * become unused and need to be auto-deleted */2083 2084 std::list< ComObjPtr<MediumAttachment> > llDiffAttachmentsToDelete;2085 2086 for (MediaData::AttachmentList::const_iterator it = mMediaData.backedUpData()->mAttachments.begin();2087 it != mMediaData.backedUpData()->mAttachments.end();2088 ++it)2089 {2090 ComObjPtr<MediumAttachment> pAttach = *it;2091 ComObjPtr<Medium> pMedium = pAttach->medium();2092 2093 /* while the hard disk is attached, the number of children or the2094 * parent cannot change, so no lock */2095 if ( !pMedium.isNull()2096 && pAttach->type() == DeviceType_HardDisk2097 && !pMedium->parent().isNull()2098 && pMedium->children().size() == 02099 )2100 {2101 LogFlowThisFunc(("Picked differencing image '%s' for deletion\n", pMedium->name().raw()));2102 2103 llDiffAttachmentsToDelete.push_back(pAttach);2104 }2105 }2106 2107 int saveFlags = 0;2108 2109 /* @todo saveSettings() below needs a VirtualBox write lock and we need2110 * to leave this object's lock to do this to follow the {parent-child}2111 * locking rule. This is the last chance to do that while we are still2112 * in a protective state which allows us to temporarily leave the lock*/2113 alock.unlock();2114 vboxLock.lock();2115 alock.lock();2116 2117 /* we have already discarded the current state, so set the execution2118 * state accordingly no matter of the discard snapshot result */2119 if (!mSSData->mStateFilePath.isEmpty())2120 setMachineState(MachineState_Saved);2121 else2122 setMachineState(MachineState_PoweredOff);2123 2124 updateMachineStateOnClient();2125 stateRestored = true;2126 2127 /* assign the timestamp from the snapshot */2128 Assert(RTTimeSpecGetMilli (&snapshotTimeStamp) != 0);2129 mData->mLastStateChange = snapshotTimeStamp;2130 2131 // detach the current-state diffs that we detected above and build a list of2132 // images to delete _after_ saveSettings()2133 2134 std::list< ComObjPtr<Medium> > llDiffsToDelete;2135 2136 for (std::list< ComObjPtr<MediumAttachment> >::iterator it = llDiffAttachmentsToDelete.begin();2137 it != llDiffAttachmentsToDelete.end();2138 ++it)2139 {2140 ComObjPtr<MediumAttachment> pAttach = *it; // guaranteed to have only attachments where medium != NULL2141 ComObjPtr<Medium> pMedium = pAttach->medium();2142 2143 AutoWriteLock mlock(pMedium);2144 2145 LogFlowThisFunc(("Detaching old current state in differencing image '%s'\n", pMedium->name().raw()));2146 2147 mMediaData->mAttachments.remove(pAttach);2148 pMedium->detachFrom(mData->mUuid);2149 2150 llDiffsToDelete.push_back(pMedium);2151 }2152 2153 // save all settings, reset the modified flag and commit;2154 rc = saveSettings(SaveS_ResetCurStateModified | saveFlags);2155 CheckComRCThrowRC(rc);2156 // from here on we cannot roll back on failure any more2157 2158 for (std::list< ComObjPtr<Medium> >::iterator it = llDiffsToDelete.begin();2159 it != llDiffsToDelete.end();2160 ++it)2161 {2162 ComObjPtr<Medium> &pMedium = *it;2163 LogFlowThisFunc(("Deleting old current state in differencing image '%s'\n", pMedium->name().raw()));2164 2165 HRESULT rc2 = pMedium->deleteStorageAndWait();2166 // ignore errors here because we cannot roll back after saveSettings() above2167 if (SUCCEEDED(rc2))2168 pMedium->uninit();2169 }2170 }2171 catch (HRESULT aRC)2172 {2173 rc = aRC;2174 }2175 2176 if (FAILED(rc))2177 {2178 /* preserve existing error info */2179 ErrorInfoKeeper eik;2180 2181 /* undo all changes on failure */2182 rollback(false /* aNotify */);2183 2184 if (!stateRestored)2185 {2186 /* restore the machine state */2187 setMachineState(aTask.machineStateBackup);2188 updateMachineStateOnClient();2189 }2190 }2191 2192 /* set the result (this will try to fetch current error info on failure) */2193 aTask.pProgress->notifyComplete(rc);2194 2195 if (SUCCEEDED(rc))2196 mParent->onSnapshotDeleted(mData->mUuid, Guid());2197 2198 LogFlowThisFunc(("Done restoring snapshot (rc=%08X)\n", rc));2199 2200 LogFlowThisFuncLeave();2201 }2202 -
trunk/src/VBox/Main/include/ConsoleImpl.h
r24255 r24299 143 143 STDMETHOD(CreateSharedFolder) (IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable); 144 144 STDMETHOD(RemoveSharedFolder) (IN_BSTR aName); 145 STDMETHOD(TakeSnapshot) 146 145 STDMETHOD(TakeSnapshot)(IN_BSTR aName, IN_BSTR aDescription, 146 IProgress **aProgress); 147 147 STDMETHOD(DeleteSnapshot)(IN_BSTR aId, IProgress **aProgress); 148 148 STDMETHOD(RestoreSnapshot)(ISnapshot *aSnapshot, IProgress **aProgress);
Note:
See TracChangeset
for help on using the changeset viewer.