VirtualBox

Changeset 24299 in vbox


Ignore:
Timestamp:
Nov 3, 2009 6:45:34 PM (15 years ago)
Author:
vboxsync
Message:

Main: reorganize and document take/restore/delete snapshot code (no functional changes)

Location:
trunk/src/VBox/Main
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/SnapshotImpl.cpp

    r24298 r24299  
    10561056
    10571057/**
    1058  * Abstract base class for SessionMachine::DeleteSnapshotTask and
    1059  * SessionMachine::RestoreSnapshotTask. This is necessary since
     1058 * Abstract base class for SessionMachine::RestoreSnapshotTask and
     1059 * SessionMachine::DeleteSnapshotTask. This is necessary since
    10601060 * RTThreadCreate cannot call a method as its thread function, so
    10611061 * instead we have it call the static SessionMachine::taskHandler,
     
    10871087};
    10881088
    1089 /** Discard snapshot task */
    1090 struct SessionMachine::DeleteSnapshotTask
    1091     : public SessionMachine::SnapshotTask
    1092 {
    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 
    11101089/** Restore snapshot state task */
    11111090struct SessionMachine::RestoreSnapshotTask
     
    11281107};
    11291108
     1109/** Discard snapshot task */
     1110struct 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
     1124private:
     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
    11301150////////////////////////////////////////////////////////////////////////////////
    11311151//
    1132 // SessionMachine public internal methods
     1152// TakeSnapshot methods (SessionMachine and related tasks)
    11331153//
    11341154////////////////////////////////////////////////////////////////////////////////
    11351155
    11361156/**
     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 *
    11371178 *  @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
    11381187 */
    11391188STDMETHODIMP SessionMachine::BeginTakingSnapshot(IConsole *aInitiator,
     
    12801329
    12811330/**
     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 *
    12821339 * @note Locks this object for writing.
     1340 *
     1341 * @param aSuccess Whether Console was successful with the client-side snapshot things.
     1342 * @return
    12831343 */
    12841344STDMETHODIMP SessionMachine::EndTakingSnapshot(BOOL aSuccess)
     
    13111371
    13121372/**
    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 */
     1387HRESULT SessionMachine::endTakingSnapshot(BOOL aSuccess)
    13191388{
    13201389    LogFlowThisFuncEnter();
    13211390
    1322     Guid id(aId);
    1323     AssertReturn(aInitiator && !id.isEmpty(), E_INVALIDARG);
    1324     AssertReturn(aMachineState && aProgress, E_POINTER);
    1325 
    13261391    AutoCaller autoCaller(this);
    13271392    AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
    13281393
    1329     /* saveSettings() needs mParent lock */
    13301394    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();
    13991451
    14001452    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
    14071482 */
    14081483STDMETHODIMP SessionMachine::RestoreSnapshot(IConsole *aInitiator,
     
    14211496    AutoWriteLock alock(this);
    14221497
     1498    // machine must not be running
    14231499    ComAssertRet(!Global::IsOnlineOrTransient(mData->mMachineState),
    14241500                 E_FAIL);
     
    14261502    ComObjPtr<Snapshot> pSnapshot(static_cast<Snapshot*>(aSnapshot));
    14271503
    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) */
    14301506    ComObjPtr<Progress> progress;
    14311507    progress.createObject();
     
    15111587}
    15121588
     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 */
     1601void 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
    15131863////////////////////////////////////////////////////////////////////////////////
    15141864//
    1515 // SessionMachine public internal methods related to snapshots
     1865// DeleteSnapshot methods (SessionMachine and related tasks)
    15161866//
    15171867////////////////////////////////////////////////////////////////////////////////
    15181868
    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 */
     1884STDMETHODIMP SessionMachine::DeleteSnapshot(IConsole *aInitiator,
     1885                                            IN_BSTR aId,
     1886                                            MachineState_T *aMachineState,
     1887                                            IProgress **aProgress)
    15471888{
    15481889    LogFlowThisFuncEnter();
    15491890
     1891    Guid id(aId);
     1892    AssertReturn(aInitiator && !id.isEmpty(), E_INVALIDARG);
     1893    AssertReturn(aMachineState && aProgress, E_POINTER);
     1894
    15501895    AutoCaller autoCaller(this);
    15511896    AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
    15521897
     1898    /* saveSettings() needs mParent lock */
    15531899    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;
    16101969
    16111970    LogFlowThisFuncLeave();
    1612     return rc;
     1971
     1972    return S_OK;
    16131973}
    16141974
     
    16341994                     const Guid &aSnapshotId)
    16351995        : hd(aHd),
    1636           chain (aChain),
     1996          chain(aChain),
    16371997          replaceHd(aReplaceHd),
    16381998          replaceHda(aReplaceHda),
     
    16512011
    16522012/**
    1653  * Discard snapshot task handler. Must be called only by
    1654  * DeleteSnapshotTask::handler()!
    1655  *
    1656  * When aTask.subTask is true, the associated progress object is left
    1657  * uncompleted on success. On failure, the progress is marked as completed
    1658  * 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.
    16592019 *
    16602020 * @note Locks mParent + this + child objects for writing!
     2021 *
     2022 * @param aTask Task data.
    16612023 */
    16622024void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask)
     
    19332295}
    19342296
    1935 /**
    1936  * Restore snapshot state task handler. Must be called only by
    1937  * 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 accidentally
    1951          * 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 to
    1965      * provide an AutoWriteLock argument that lets create a non-locking
    1966      * instance */
    1967     vboxLock.unlock();
    1968 
    1969     AutoWriteLock alock(this);
    1970 
    1971     /* discard all current changes to mUserData (name, OSType etc.) (note that
    1972      * the machine is powered off, so there is no need to inform the direct
    1973      * session) */
    1974     if (isModified())
    1975         rollback(false /* aNotify */);
    1976 
    1977     HRESULT rc = S_OK;
    1978 
    1979     bool stateRestored = false;
    1980 
    1981     try
    1982     {
    1983         /* discard the saved state file if the machine was Saved prior to this
    1984          * 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 be
    2030              * deassociated/deleted on #commit() called from #saveSettings() at
    2031              * the end. On failure, newly created implicit diffs will be
    2032              * 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);        // weight
    2051 
    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                 else
    2069                     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 will
    2082          * 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 the
    2094              * parent cannot change, so no lock */
    2095             if (    !pMedium.isNull()
    2096                  && pAttach->type() == DeviceType_HardDisk
    2097                  && !pMedium->parent().isNull()
    2098                  && pMedium->children().size() == 0
    2099                )
    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 need
    2110          * 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 still
    2112          * 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 execution
    2118          * state accordingly no matter of the discard snapshot result */
    2119         if (!mSSData->mStateFilePath.isEmpty())
    2120             setMachineState(MachineState_Saved);
    2121         else
    2122             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 of
    2132         // 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 != NULL
    2141             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 more
    2157 
    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() above
    2167             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  
    143143    STDMETHOD(CreateSharedFolder) (IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable);
    144144    STDMETHOD(RemoveSharedFolder) (IN_BSTR aName);
    145     STDMETHOD(TakeSnapshot) (IN_BSTR aName, IN_BSTR aDescription,
    146                              IProgress **aProgress);
     145    STDMETHOD(TakeSnapshot)(IN_BSTR aName, IN_BSTR aDescription,
     146                            IProgress **aProgress);
    147147    STDMETHOD(DeleteSnapshot)(IN_BSTR aId, IProgress **aProgress);
    148148    STDMETHOD(RestoreSnapshot)(ISnapshot *aSnapshot, IProgress **aProgress);
Note: See TracChangeset for help on using the changeset viewer.

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