VirtualBox

Ignore:
Timestamp:
Feb 24, 2011 3:38:25 PM (14 years ago)
Author:
vboxsync
Message:

Main: implement sharing saved state files between snapshots and machines in 'saved' state; this dramatically speeds up restoring snapshots as well as taking snapshots of 'saved' machines

File:
1 edited

Legend:

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

    r35984 r36074  
    428428    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    429429
    430     *aOnline = !stateFilePath().isEmpty();
     430    *aOnline = getStateFilePath().isNotEmpty();
    431431    return S_OK;
    432432}
     
    504504 *      Must be called from under the object's lock!
    505505 */
    506 const Utf8Str& Snapshot::stateFilePath() const
    507 {
    508     return m->pMachine->mSSData->mStateFilePath;
    509 }
    510 
    511 /**
    512  *  @note
    513  *      Must be called from under the object's write lock!
    514  */
    515 HRESULT Snapshot::deleteStateFile()
    516 {
    517     int vrc = RTFileDelete(m->pMachine->mSSData->mStateFilePath.c_str());
    518     if (RT_SUCCESS(vrc))
    519         m->pMachine->mSSData->mStateFilePath.setNull();
    520     return RT_SUCCESS(vrc) ? S_OK : E_FAIL;
     506const Utf8Str& Snapshot::getStateFilePath() const
     507{
     508    return m->pMachine->mSSData->strStateFilePath;
    521509}
    522510
     
    688676    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    689677
    690     const Utf8Str &path = m->pMachine->mSSData->mStateFilePath;
     678    const Utf8Str &path = m->pMachine->mSSData->strStateFilePath;
    691679    LogFlowThisFunc(("Snap[%s].statePath={%s}\n", m->strName.c_str(), path.c_str()));
    692680
     
    696684       )
    697685    {
    698         m->pMachine->mSSData->mStateFilePath = Utf8StrFmt("%s%s",
    699                                                           strNewPath.c_str(),
    700                                                           path.c_str() + strOldPath.length());
     686        m->pMachine->mSSData->strStateFilePath = Utf8StrFmt("%s%s",
     687                                                            strNewPath.c_str(),
     688                                                            path.c_str() + strOldPath.length());
    701689        LogFlowThisFunc(("-> updated: {%s}\n", path.c_str()));
    702690    }
     
    710698    }
    711699}
     700
     701/**
     702 * Returns true if this snapshot or one of its children uses the given file,
     703 * whose path must be fully qualified, as its saved state. When invoked on a
     704 * machine's first snapshot, this can be used to check if a saved state file
     705 * is shared with any snapshots.
     706 *
     707 * Caller must hold the machine lock, which protects the snapshots tree.
     708 *
     709 * @param strPath
     710 * @param pSnapshotToIgnore If != NULL, this snapshot is ignored during the checks.
     711 * @return
     712 */
     713bool Snapshot::sharesSavedStateFile(const Utf8Str &strPath,
     714                                    Snapshot *pSnapshotToIgnore)
     715{
     716    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     717    const Utf8Str &path = m->pMachine->mSSData->strStateFilePath;
     718
     719    if (!pSnapshotToIgnore || pSnapshotToIgnore != this)
     720        if (path.isNotEmpty())
     721            if (path == strPath)
     722                return true;        // no need to recurse then
     723
     724    // but otherwise we must check children
     725    for (SnapshotsList::const_iterator it = m->llChildren.begin();
     726         it != m->llChildren.end();
     727         ++it)
     728    {
     729        Snapshot *pChild = *it;
     730        if (!pSnapshotToIgnore || pSnapshotToIgnore != pChild)
     731            if (pChild->sharesSavedStateFile(strPath, pSnapshotToIgnore))
     732                return true;
     733    }
     734
     735    return false;
     736}
     737
    712738
    713739/**
     
    757783        return S_OK;
    758784
    759     /* stateFile (optional) */
    760     if (!stateFilePath().isEmpty())
    761         m->pMachine->copyPathRelativeToMachine(stateFilePath(), data.strStateFile);
     785    // state file (only if this snapshot is online)
     786    if (getStateFilePath().isNotEmpty())
     787        m->pMachine->copyPathRelativeToMachine(getStateFilePath(), data.strStateFile);
    762788    else
    763789        data.strStateFile.setNull();
     
    870896        return rc;
    871897
    872     // now report the saved state file
    873     if (!m->pMachine->mSSData->mStateFilePath.isEmpty())
    874         llFilenames.push_back(m->pMachine->mSSData->mStateFilePath);
     898    // report the saved state file if it's not on the list yet
     899    if (!m->pMachine->mSSData->strStateFilePath.isEmpty())
     900    {
     901        bool fFound = false;
     902        for (std::list<Utf8Str>::const_iterator it = llFilenames.begin();
     903             it != llFilenames.end();
     904             ++it)
     905        {
     906            const Utf8Str &str = *it;
     907            if (str == m->pMachine->mSSData->strStateFilePath)
     908            {
     909                fFound = true;
     910                break;
     911            }
     912        }
     913        if (!fFound)
     914            llFilenames.push_back(m->pMachine->mSSData->strStateFilePath);
     915    }
    875916
    876917    this->beginSnapshotDelete();
     
    952993    /* SSData is always unique for SnapshotMachine */
    953994    mSSData.allocate();
    954     mSSData->mStateFilePath = aStateFilePath;
     995    mSSData->strStateFilePath = aStateFilePath;
    955996
    956997    HRESULT rc = S_OK;
     
    10901131    /* SSData is always unique for SnapshotMachine */
    10911132    mSSData.allocate();
    1092     mSSData->mStateFilePath = aStateFilePath;
     1133    mSSData->strStateFilePath = aStateFilePath;
    10931134
    10941135    /* create all other child objects that will be immutable private copies */
     
    12621303    RestoreSnapshotTask(SessionMachine *m,
    12631304                        Progress *p,
    1264                         Snapshot *s,
    1265                         ULONG ulStateFileSizeMB)
    1266         : SnapshotTask(m, p, s),
    1267           m_ulStateFileSizeMB(ulStateFileSizeMB)
     1305                        Snapshot *s)
     1306        : SnapshotTask(m, p, s)
    12681307    {}
    12691308
     
    12721311        pMachine->restoreSnapshotHandler(*this);
    12731312    }
    1274 
    1275     ULONG       m_ulStateFileSizeMB;
    12761313};
    12771314
     
    13991436    Utf8Str strStateFilePath;
    14001437    /* stateFilePath is null when the machine is not online nor saved */
    1401     if (    fTakingSnapshotOnline
    1402          || mData->mMachineState == MachineState_Saved)
    1403     {
    1404         Utf8Str strFullSnapshotFolder;
    1405         calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
    1406         strStateFilePath = Utf8StrFmt("%s%c{%RTuuid}.sav",
    1407                                       strFullSnapshotFolder.c_str(),
    1408                                       RTPATH_DELIMITER,
    1409                                       snapshotId.raw());
    1410         /* ensure the directory for the saved state file exists */
     1438    if (fTakingSnapshotOnline)
     1439        // creating a new online snapshot: then we need a fresh saved state file
     1440        composeSavedStateFilename(strStateFilePath);
     1441    else if (mData->mMachineState == MachineState_Saved)
     1442        // taking an online snapshot from machine in "saved" state: then use existing state file
     1443        strStateFilePath = mSSData->strStateFilePath;
     1444
     1445    if (strStateFilePath.isNotEmpty())
     1446    {
     1447        // ensure the directory for the saved state file exists
    14111448        HRESULT rc = VirtualBox::ensureFilePathExists(strStateFilePath);
    14121449        if (FAILED(rc)) return rc;
     
    14641501            throw rc;
    14651502
    1466         if (mConsoleTaskData.mLastState == MachineState_Saved)
    1467         {
    1468             Utf8Str stateFrom = mSSData->mStateFilePath;
    1469             Utf8Str stateTo = mConsoleTaskData.mSnapshot->stateFilePath();
    1470 
    1471             LogFlowThisFunc(("Copying the execution state from '%s' to '%s'...\n",
    1472                              stateFrom.c_str(), stateTo.c_str()));
    1473 
    1474             aConsoleProgress->SetNextOperation(Bstr(tr("Copying the execution state")).raw(),
    1475                                                1);        // weight
    1476 
    1477             /* Leave the lock before a lengthy operation (machine is protected
    1478              * by "Saving" machine state now) */
    1479             alock.release();
    1480 
    1481             /* copy the state file */
    1482             int vrc = RTFileCopyEx(stateFrom.c_str(),
    1483                                    stateTo.c_str(),
    1484                                    0,
    1485                                    progressCallback,
    1486                                    aConsoleProgress);
    1487             alock.acquire();
    1488 
    1489             if (RT_FAILURE(vrc))
    1490                 /** @todo r=bird: Delete stateTo when appropriate. */
    1491                 throw setError(E_FAIL,
    1492                                tr("Could not copy the state file '%s' to '%s' (%Rrc)"),
    1493                                stateFrom.c_str(),
    1494                                stateTo.c_str(),
    1495                                vrc);
    1496         }
    1497 
    14981503        // if we got this far without an error, then save the media registries
    14991504        // that got modified for the diff images
     
    15961601
    15971602        rc = saveSettings(NULL, flSaveSettings);
    1598                 // no need to change for whether VirtualBox.xml needs saving since
    1599                 // we'll save the global settings below anyway
    16001603    }
    16011604
     
    16181621        mData->mCurrentSnapshot = pOldCurrentSnap;      // might have been changed above
    16191622
    1620         /* delete the saved state file (it might have been already created) */
    1621         if (mConsoleTaskData.mSnapshot->stateFilePath().length())
    1622             RTFileDelete(mConsoleTaskData.mSnapshot->stateFilePath().c_str());
     1623        // delete the saved state file (it might have been already created)
     1624        if (fOnline)
     1625            // no need to test for whether the saved state file is shared: an online
     1626            // snapshot means that a new saved state file was created, which we must
     1627            // clean up now
     1628            RTFileDelete(mConsoleTaskData.mSnapshot->getStateFilePath().c_str());
    16231629
    16241630        mConsoleTaskData.mSnapshot->uninit();
     
    16281634    mConsoleTaskData.mLastState = MachineState_Null;
    16291635    mConsoleTaskData.mSnapshot.setNull();
    1630 
    1631     // save VirtualBox.xml (media registry most probably changed with diff image);
    1632     // for that we should hold only the VirtualBox lock
    1633     machineLock.release();
    1634     AutoWriteLock vboxLock(mParent COMMA_LOCKVAL_SRC_POS);
    1635     mParent->saveSettings();
    16361636
    16371637    return rc;
     
    17081708    }
    17091709
    1710     ULONG ulStateFileSizeMB = 0;
    1711     if (pSnapshot->stateFilePath().length())
    1712     {
    1713         ++ulOpCount;      // one for the saved state
    1714 
    1715         uint64_t ullSize;
    1716         int irc = RTFileQuerySize(pSnapshot->stateFilePath().c_str(), &ullSize);
    1717         if (!RT_SUCCESS(irc))
    1718             // if we can't access the file here, then we'll be doomed later also, so fail right away
    1719             setError(E_FAIL, tr("Cannot access state file '%s', runtime error, %Rra"), pSnapshot->stateFilePath().c_str(), irc);
    1720         if (ullSize == 0) // avoid division by zero
    1721             ullSize = _1M;
    1722 
    1723         ulStateFileSizeMB = (ULONG)(ullSize / _1M);
    1724         LogFlowThisFunc(("op %d: saved state file '%s' has %RI64 bytes (%d MB)\n",
    1725                          ulOpCount, pSnapshot->stateFilePath().c_str(), ullSize, ulStateFileSizeMB));
    1726 
    1727         ulTotalWeight += ulStateFileSizeMB;
    1728     }
    1729 
    17301710    ComObjPtr<Progress> pProgress;
    17311711    pProgress.createObject();
     
    17421722    RestoreSnapshotTask *task = new RestoreSnapshotTask(this,
    17431723                                                        pProgress,
    1744                                                         pSnapshot,
    1745                                                         ulStateFileSizeMB);
     1724                                                        pSnapshot);
    17461725    int vrc = RTThreadCreate(NULL,
    17471726                             taskHandler,
     
    18221801        if (aTask.machineStateBackup == MachineState_Saved)
    18231802        {
    1824             Assert(!mSSData->mStateFilePath.isEmpty());
    1825             RTFileDelete(mSSData->mStateFilePath.c_str());
    1826             mSSData->mStateFilePath.setNull();
     1803            Assert(!mSSData->strStateFilePath.isEmpty());
     1804
     1805            // release the saved state file AFTER unsetting the member variable
     1806            // so that releaseSavedStateFile() won't think it's still in use
     1807            Utf8Str strStateFile(mSSData->strStateFilePath);
     1808            mSSData->strStateFilePath.setNull();
     1809            releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
     1810
    18271811            aTask.modifyBackedUpState(MachineState_PoweredOff);
     1812
    18281813            rc = saveStateSettings(SaveSTS_StateFilePath);
    18291814            if (FAILED(rc))
     
    18721857
    18731858            /* should not have a saved state file associated at this point */
    1874             Assert(mSSData->mStateFilePath.isEmpty());
    1875 
    1876             if (!aTask.pSnapshot->stateFilePath().isEmpty())
    1877             {
    1878                 Utf8Str snapStateFilePath = aTask.pSnapshot->stateFilePath();
    1879 
    1880                 Utf8Str strFullSnapshotFolder;
    1881                 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
    1882                 Utf8Str stateFilePath = Utf8StrFmt("%s%c{%RTuuid}.sav",
    1883                                                    strFullSnapshotFolder.c_str(),
    1884                                                    RTPATH_DELIMITER,
    1885                                                    mData->mUuid.raw());
    1886 
    1887                 LogFlowThisFunc(("Copying saved state file from '%s' to '%s'...\n",
    1888                                   snapStateFilePath.c_str(), stateFilePath.c_str()));
    1889 
    1890                 aTask.pProgress->SetNextOperation(Bstr(tr("Restoring the execution state")).raw(),
    1891                                                   aTask.m_ulStateFileSizeMB);        // weight
    1892 
    1893                 /* leave the lock before the potentially lengthy operation */
    1894                 snapshotLock.release();
    1895                 alock.leave();
    1896 
    1897                 /* copy the state file */
    1898                 RTFileDelete(stateFilePath.c_str());
    1899                 int vrc = RTFileCopyEx(snapStateFilePath.c_str(),
    1900                                        stateFilePath.c_str(),
    1901                                        0,
    1902                                        progressCallback,
    1903                                        static_cast<IProgress*>(aTask.pProgress));
    1904 
    1905                 alock.enter();
    1906                 snapshotLock.acquire();
    1907 
    1908                 if (RT_SUCCESS(vrc))
    1909                     mSSData->mStateFilePath = stateFilePath;
    1910                 else
    1911                     throw setError(E_FAIL,
    1912                                    tr("Could not copy the state file '%s' to '%s' (%Rrc)"),
    1913                                    snapStateFilePath.c_str(),
    1914                                    stateFilePath.c_str(),
    1915                                    vrc);
    1916             }
     1859            Assert(mSSData->strStateFilePath.isEmpty());
     1860
     1861            const Utf8Str &strSnapshotStateFile = aTask.pSnapshot->getStateFilePath();
     1862
     1863            if (strSnapshotStateFile.isNotEmpty())
     1864                // online snapshot: then share the state file
     1865                mSSData->strStateFilePath = strSnapshotStateFile;
    19171866
    19181867            LogFlowThisFunc(("Setting new current snapshot {%RTuuid}\n", aTask.pSnapshot->getId().raw()));
     
    19501899        /* we have already deleted the current state, so set the execution
    19511900         * state accordingly no matter of the delete snapshot result */
    1952         if (!mSSData->mStateFilePath.isEmpty())
     1901        if (mSSData->strStateFilePath.isNotEmpty())
    19531902            setMachineState(MachineState_Saved);
    19541903        else
     
    21442093    ULONG ulTotalWeight = 1;        // one for preparations
    21452094
    2146     if (pSnapshot->stateFilePath().length())
     2095    if (pSnapshot->getStateFilePath().length())
    21472096    {
    21482097        ++ulOpCount;
     
    25292478            AutoWriteLock machineLock(this COMMA_LOCKVAL_SRC_POS);
    25302479
    2531             Utf8Str stateFilePath = aTask.pSnapshot->stateFilePath();
     2480            Utf8Str stateFilePath = aTask.pSnapshot->getStateFilePath();
    25322481            if (!stateFilePath.isEmpty())
    25332482            {
     
    25352484                                                  1);        // weight
    25362485
    2537                 aTask.pSnapshot->deleteStateFile();
    2538                 // machine needs saving now
     2486                releaseSavedStateFile(stateFilePath, aTask.pSnapshot /* pSnapshotToIgnore */);
     2487
     2488                // machine will need saving now
    25392489                mParent->addGuidToListUniquely(llRegistriesThatNeedSaving, getId());
    25402490            }
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