VirtualBox

Changeset 24279 in vbox


Ignore:
Timestamp:
Nov 2, 2009 9:51:36 PM (15 years ago)
Author:
vboxsync
Message:

Main: move huge amounts of snapshot code from MachineImpl.cpp to SnapshotImpl.cpp; no functional change

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

Legend:

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

    r24277 r24279  
    9191// defines / prototypes
    9292/////////////////////////////////////////////////////////////////////////////
    93 
    94 // globals
    95 /////////////////////////////////////////////////////////////////////////////
    96 
    97 /**
    98  *  Progress callback handler for lengthy operations
    99  *  (corresponds to the FNRTPROGRESS typedef).
    100  *
    101  *  @param uPercentage  Completetion precentage (0-100).
    102  *  @param pvUser       Pointer to the Progress instance.
    103  */
    104 static DECLCALLBACK(int) progressCallback(unsigned uPercentage, void *pvUser)
    105 {
    106     Progress *progress = static_cast<Progress*>(pvUser);
    107 
    108     /* update the progress object */
    109     if (progress)
    110         progress->SetCurrentOperationProgress(uPercentage);
    111 
    112     return VINF_SUCCESS;
    113 }
    11493
    11594/////////////////////////////////////////////////////////////////////////////
     
    77017680
    77027681
    7703 /////////////////////////////////////////////////////////////////////////////
    7704 // SessionMachine class
    7705 /////////////////////////////////////////////////////////////////////////////
    7706 
    7707 /** Task structure for asynchronous VM operations */
    7708 struct SessionMachine::Task
    7709 {
    7710     Task (SessionMachine *m, Progress *p)
    7711         : machine (m), progress (p)
    7712         , state (m->mData->mMachineState) // save the current machine state
    7713         , subTask (false)
    7714     {}
    7715 
    7716     void modifyLastState (MachineState_T s)
    7717     {
    7718         *const_cast <MachineState_T *> (&state) = s;
    7719     }
    7720 
    7721     virtual void handler() = 0;
    7722 
    7723     ComObjPtr<SessionMachine> machine;
    7724     ComObjPtr<Progress> progress;
    7725     const MachineState_T state;
    7726 
    7727     bool subTask : 1;
    7728 };
    7729 
    7730 /** Discard snapshot task */
    7731 struct SessionMachine::DeleteSnapshotTask
    7732     : public SessionMachine::Task
    7733 {
    7734     DeleteSnapshotTask(SessionMachine *m, Progress *p, Snapshot *s)
    7735         : Task(m, p),
    7736           snapshot(s)
    7737     {}
    7738 
    7739     DeleteSnapshotTask (const Task &task, Snapshot *s)
    7740         : Task(task)
    7741         , snapshot(s)
    7742     {}
    7743 
    7744     void handler()
    7745     {
    7746         machine->deleteSnapshotHandler(*this);
    7747     }
    7748 
    7749     ComObjPtr<Snapshot> snapshot;
    7750 };
    7751 
    7752 /** Restore snapshot state task */
    7753 struct SessionMachine::RestoreSnapshotTask
    7754     : public SessionMachine::Task
    7755 {
    7756     RestoreSnapshotTask(SessionMachine *m,
    7757                         ComObjPtr<Snapshot> &aSnapshot,
    7758                         Progress *p,
    7759                         ULONG ulStateFileSizeMB)
    7760         : Task(m, p),
    7761           m_pSnapshot(aSnapshot),
    7762           m_ulStateFileSizeMB(ulStateFileSizeMB)
    7763     {}
    7764 
    7765     void handler()
    7766     {
    7767         machine->restoreSnapshotHandler(*this);
    7768     }
    7769 
    7770     ComObjPtr<Snapshot> m_pSnapshot;
    7771     ULONG m_ulStateFileSizeMB;
    7772 };
    7773 
    77747682////////////////////////////////////////////////////////////////////////////////
    77757683
     
    86078515}
    86088516
    8609 /**
    8610  *  @note Locks mParent + this object for writing.
    8611  */
    8612 STDMETHODIMP SessionMachine::BeginTakingSnapshot(IConsole *aInitiator,
    8613                                                  IN_BSTR aName,
    8614                                                  IN_BSTR aDescription,
    8615                                                  IProgress *aConsoleProgress,
    8616                                                  BOOL fTakingSnapshotOnline,
    8617                                                  BSTR *aStateFilePath)
    8618 {
    8619     LogFlowThisFuncEnter();
    8620 
    8621     AssertReturn(aInitiator && aName, E_INVALIDARG);
    8622     AssertReturn(aStateFilePath, E_POINTER);
    8623 
    8624     LogFlowThisFunc(("aName='%ls'\n", aName));
    8625 
    8626     AutoCaller autoCaller(this);
    8627     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    8628 
    8629     /* saveSettings() needs mParent lock */
    8630     AutoMultiWriteLock2 alock(mParent, this);
    8631 
    8632     AssertReturn(    !Global::IsOnlineOrTransient(mData->mMachineState)
    8633                   || mData->mMachineState == MachineState_Running
    8634                   || mData->mMachineState == MachineState_Paused, E_FAIL);
    8635     AssertReturn(mSnapshotData.mLastState == MachineState_Null, E_FAIL);
    8636     AssertReturn(mSnapshotData.mSnapshot.isNull(), E_FAIL);
    8637 
    8638     if (    !fTakingSnapshotOnline
    8639          && mData->mMachineState != MachineState_Saved
    8640        )
    8641     {
    8642         /* save all current settings to ensure current changes are committed and
    8643          * hard disks are fixed up */
    8644         HRESULT rc = saveSettings();
    8645         CheckComRCReturnRC(rc);
    8646     }
    8647 
    8648     /* create an ID for the snapshot */
    8649     Guid snapshotId;
    8650     snapshotId.create();
    8651 
    8652     Utf8Str strStateFilePath;
    8653     /* stateFilePath is null when the machine is not online nor saved */
    8654     if (    fTakingSnapshotOnline
    8655          || mData->mMachineState == MachineState_Saved)
    8656     {
    8657         strStateFilePath = Utf8StrFmt("%ls%c{%RTuuid}.sav",
    8658                                       mUserData->mSnapshotFolderFull.raw(),
    8659                                       RTPATH_DELIMITER,
    8660                                       snapshotId.ptr());
    8661         /* ensure the directory for the saved state file exists */
    8662         HRESULT rc = VirtualBox::ensureFilePathExists(strStateFilePath);
    8663         CheckComRCReturnRC(rc);
    8664     }
    8665 
    8666     /* create a snapshot machine object */
    8667     ComObjPtr<SnapshotMachine> snapshotMachine;
    8668     snapshotMachine.createObject();
    8669     HRESULT rc = snapshotMachine->init(this, snapshotId, strStateFilePath);
    8670     AssertComRCReturn(rc, rc);
    8671 
    8672     /* create a snapshot object */
    8673     RTTIMESPEC time;
    8674     ComObjPtr<Snapshot> pSnapshot;
    8675     pSnapshot.createObject();
    8676     rc = pSnapshot->init(mParent,
    8677                          snapshotId,
    8678                          aName,
    8679                          aDescription,
    8680                          *RTTimeNow(&time),
    8681                          snapshotMachine,
    8682                          mData->mCurrentSnapshot);
    8683     AssertComRCReturnRC(rc);
    8684 
    8685     /* fill in the snapshot data */
    8686     mSnapshotData.mLastState = mData->mMachineState;
    8687     mSnapshotData.mSnapshot = pSnapshot;
    8688 
    8689     try
    8690     {
    8691         LogFlowThisFunc(("Creating differencing hard disks (online=%d)...\n",
    8692                         fTakingSnapshotOnline));
    8693 
    8694         // backup the media data so we can recover if things goes wrong along the day;
    8695         // the matching commit() is in fixupMedia() during endSnapshot()
    8696         mMediaData.backup();
    8697 
    8698         /* set the state to Saving (this is expected by Console::TakeSnapshot()) */
    8699         setMachineState(MachineState_Saving);
    8700 
    8701         /* create new differencing hard disks and attach them to this machine */
    8702         rc = createImplicitDiffs(mUserData->mSnapshotFolderFull,
    8703                                  aConsoleProgress,
    8704                                  1,            // operation weight; must be the same as in Console::TakeSnapshot()
    8705                                  !!fTakingSnapshotOnline);
    8706 
    8707         if (SUCCEEDED(rc) && mSnapshotData.mLastState == MachineState_Saved)
    8708         {
    8709             Utf8Str stateFrom = mSSData->mStateFilePath;
    8710             Utf8Str stateTo = mSnapshotData.mSnapshot->stateFilePath();
    8711 
    8712             LogFlowThisFunc(("Copying the execution state from '%s' to '%s'...\n",
    8713                             stateFrom.raw(), stateTo.raw()));
    8714 
    8715             aConsoleProgress->SetNextOperation(Bstr(tr("Copying the execution state")),
    8716                                                1);        // weight
    8717 
    8718             /* Leave the lock before a lengthy operation (mMachineState is
    8719             * MachineState_Saving here) */
    8720             alock.leave();
    8721 
    8722             /* copy the state file */
    8723             int vrc = RTFileCopyEx(stateFrom.c_str(),
    8724                                    stateTo.c_str(),
    8725                                    0,
    8726                                    progressCallback,
    8727                                    aConsoleProgress);
    8728             alock.enter();
    8729 
    8730             if (RT_FAILURE(vrc))
    8731                 throw setError(E_FAIL,
    8732                                tr("Could not copy the state file '%s' to '%s' (%Rrc)"),
    8733                                stateFrom.raw(),
    8734                                stateTo.raw(),
    8735                                vrc);
    8736         }
    8737     }
    8738     catch (HRESULT hrc)
    8739     {
    8740         pSnapshot->uninit();
    8741         pSnapshot.setNull();
    8742         rc = hrc;
    8743     }
    8744 
    8745     if (fTakingSnapshotOnline)
    8746         strStateFilePath.cloneTo(aStateFilePath);
    8747     else
    8748         *aStateFilePath = NULL;
    8749 
    8750     LogFlowThisFuncLeave();
    8751     return rc;
    8752 }
    8753 
    8754 /**
    8755  * @note Locks this object for writing.
    8756  */
    8757 STDMETHODIMP SessionMachine::EndTakingSnapshot(BOOL aSuccess)
    8758 {
    8759     LogFlowThisFunc(("\n"));
    8760 
    8761     AutoCaller autoCaller(this);
    8762     AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
    8763 
    8764     AutoWriteLock alock(this);
    8765 
    8766     AssertReturn(!aSuccess ||
    8767                   (mData->mMachineState == MachineState_Saving &&
    8768                    mSnapshotData.mLastState != MachineState_Null &&
    8769                    !mSnapshotData.mSnapshot.isNull()),
    8770                   E_FAIL);
    8771 
    8772     /*
    8773      * Restore the state we had when BeginTakingSnapshot() was called,
    8774      * Console::fntTakeSnapshotWorker restores its local copy when we return.
    8775      * If the state was Running, then let Console::fntTakeSnapshotWorker it
    8776      * all via Console::Resume().
    8777      */
    8778     if (   mData->mMachineState != mSnapshotData.mLastState
    8779         && mSnapshotData.mLastState != MachineState_Running)
    8780         setMachineState(mSnapshotData.mLastState);
    8781 
    8782     return endTakingSnapshot(aSuccess);
    8783 }
    8784 
    8785 /**
    8786  *  @note Locks mParent + this + children objects for writing!
    8787  */
    8788 STDMETHODIMP SessionMachine::DeleteSnapshot(IConsole *aInitiator,
    8789                                             IN_BSTR aId,
    8790                                             MachineState_T *aMachineState,
    8791                                             IProgress **aProgress)
    8792 {
    8793     LogFlowThisFuncEnter();
    8794 
    8795     Guid id(aId);
    8796     AssertReturn(aInitiator && !id.isEmpty(), E_INVALIDARG);
    8797     AssertReturn(aMachineState && aProgress, E_POINTER);
    8798 
    8799     AutoCaller autoCaller(this);
    8800     AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
    8801 
    8802     /* saveSettings() needs mParent lock */
    8803     AutoMultiWriteLock2 alock(mParent, this);
    8804 
    8805     ComAssertRet (!Global::IsOnlineOrTransient (mData->mMachineState), E_FAIL);
    8806 
    8807     AutoWriteLock treeLock(snapshotsTreeLockHandle());
    8808 
    8809     ComObjPtr<Snapshot> snapshot;
    8810     HRESULT rc = findSnapshot(id, snapshot, true /* aSetError */);
    8811     CheckComRCReturnRC(rc);
    8812 
    8813     AutoWriteLock snapshotLock(snapshot);
    8814 
    8815     size_t childrenCount = snapshot->getChildrenCount();
    8816     if (childrenCount > 1)
    8817         return setError(VBOX_E_INVALID_OBJECT_STATE,
    8818                         tr("Snapshot '%s' of the machine '%ls' cannot be deleted because it has has more than one child snapshot (%d)"),
    8819                         snapshot->getName().c_str(),
    8820                         mUserData->mName.raw(),
    8821                         childrenCount);
    8822 
    8823     /* If the snapshot being discarded is the current one, ensure current
    8824      * settings are committed and saved.
    8825      */
    8826     if (snapshot == mData->mCurrentSnapshot)
    8827     {
    8828         if (isModified())
    8829         {
    8830             rc = saveSettings();
    8831             CheckComRCReturnRC(rc);
    8832         }
    8833     }
    8834 
    8835     /* create a progress object. The number of operations is:
    8836      *   1 (preparing) + # of hard disks + 1 if the snapshot is online
    8837      */
    8838     ComObjPtr<Progress> progress;
    8839     progress.createObject();
    8840     rc = progress->init(mParent, aInitiator,
    8841                         BstrFmt(tr("Discarding snapshot '%s'"),
    8842                                 snapshot->getName().c_str()),
    8843                         FALSE /* aCancelable */,
    8844                         1 + (ULONG)snapshot->getSnapshotMachine()->mMediaData->mAttachments.size()
    8845                           + (snapshot->stateFilePath().length() ? 1 : 0),
    8846                         Bstr(tr("Preparing to discard snapshot")));
    8847     AssertComRCReturn(rc, rc);
    8848 
    8849     /* create and start the task on a separate thread */
    8850     DeleteSnapshotTask *task = new DeleteSnapshotTask(this, progress, snapshot);
    8851     int vrc = RTThreadCreate(NULL,
    8852                              taskHandler,
    8853                              (void*)task,
    8854                              0,
    8855                              RTTHREADTYPE_MAIN_WORKER,
    8856                              0,
    8857                              "DeleteSnapshot");
    8858     if (RT_FAILURE(vrc))
    8859     {
    8860         delete task;
    8861         return E_FAIL;
    8862     }
    8863 
    8864     /* set the proper machine state (note: after creating a Task instance) */
    8865     setMachineState(MachineState_DeletingSnapshot);
    8866 
    8867     /* return the progress to the caller */
    8868     progress.queryInterfaceTo(aProgress);
    8869 
    8870     /* return the new state to the caller */
    8871     *aMachineState = mData->mMachineState;
    8872 
    8873     LogFlowThisFuncLeave();
    8874 
    8875     return S_OK;
    8876 }
    8877 
    8878 /**
    8879  *  @note Locks this + children objects for writing!
    8880  */
    8881 STDMETHODIMP SessionMachine::RestoreSnapshot(IConsole *aInitiator,
    8882                                              ISnapshot *aSnapshot,
    8883                                              MachineState_T *aMachineState,
    8884                                              IProgress **aProgress)
    8885 {
    8886     LogFlowThisFuncEnter();
    8887 
    8888     AssertReturn(aInitiator, E_INVALIDARG);
    8889     AssertReturn(aSnapshot && aMachineState && aProgress, E_POINTER);
    8890 
    8891     AutoCaller autoCaller(this);
    8892     AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
    8893 
    8894     AutoWriteLock alock(this);
    8895 
    8896     ComAssertRet(!Global::IsOnlineOrTransient(mData->mMachineState),
    8897                  E_FAIL);
    8898 
    8899     ComObjPtr<Snapshot> pSnapshot(static_cast<Snapshot*>(aSnapshot));
    8900 
    8901     /* create a progress object. The number of operations is: 1 (preparing) + #
    8902      * of hard disks + 1 (if we need to copy the saved state file) */
    8903     ComObjPtr<Progress> progress;
    8904     progress.createObject();
    8905 
    8906     LogFlowThisFunc(("Going thru snapshot machine attachments to determine progress setup\n"));
    8907 
    8908     ULONG ulOpCount = 1;            // one for preparations
    8909     ULONG ulTotalWeight = 1;        // one for preparations
    8910     for (MediaData::AttachmentList::iterator it = pSnapshot->getSnapshotMachine()->mMediaData->mAttachments.begin();
    8911          it != pSnapshot->getSnapshotMachine()->mMediaData->mAttachments.end();
    8912          ++it)
    8913     {
    8914         ComObjPtr<MediumAttachment> &pAttach = *it;
    8915         AutoReadLock attachLock(pAttach);
    8916         if (pAttach->type() == DeviceType_HardDisk)
    8917         {
    8918             ++ulOpCount;
    8919             ++ulTotalWeight;         // assume one MB weight for each differencing hard disk to manage
    8920             Assert(pAttach->medium());
    8921             LogFlowThisFunc(("op %d: considering hard disk attachment %s\n", ulOpCount, pAttach->medium()->name().c_str()));
    8922         }
    8923     }
    8924 
    8925     ULONG ulStateFileSizeMB = 0;
    8926     if (pSnapshot->stateFilePath().length())
    8927     {
    8928         ++ulOpCount;      // one for the saved state
    8929 
    8930         uint64_t ullSize;
    8931         int irc = RTFileQuerySize(pSnapshot->stateFilePath().c_str(), &ullSize);
    8932         if (!RT_SUCCESS(irc))
    8933             // if we can't access the file here, then we'll be doomed later also, so fail right away
    8934             setError(E_FAIL, tr("Cannot access state file '%s', runtime error, %Rra"), pSnapshot->stateFilePath().c_str(), irc);
    8935         if (ullSize == 0) // avoid division by zero
    8936             ullSize = _1M;
    8937 
    8938         ulStateFileSizeMB = (ULONG)(ullSize / _1M);
    8939         LogFlowThisFunc(("op %d: saved state file '%s' has %RI64 bytes (%d MB)\n",
    8940                          ulOpCount, pSnapshot->stateFilePath().raw(), ullSize, ulStateFileSizeMB));
    8941 
    8942         ulTotalWeight += ulStateFileSizeMB;
    8943     }
    8944 
    8945     progress->init(mParent, aInitiator,
    8946                    Bstr(tr("Restoring snapshot")),
    8947                    FALSE /* aCancelable */,
    8948                    ulOpCount,
    8949                    ulTotalWeight,
    8950                    Bstr(tr("Restoring machine settings")),
    8951                    1);
    8952 
    8953     /* create and start the task on a separate thread (note that it will not
    8954      * start working until we release alock) */
    8955     RestoreSnapshotTask *task = new RestoreSnapshotTask(this, pSnapshot, progress, ulStateFileSizeMB);
    8956     int vrc = RTThreadCreate(NULL,
    8957                              taskHandler,
    8958                              (void*)task,
    8959                              0,
    8960                              RTTHREADTYPE_MAIN_WORKER,
    8961                              0,
    8962                              "RestoreSnap");
    8963     if (RT_FAILURE(vrc))
    8964     {
    8965         delete task;
    8966         ComAssertRCRet(vrc, E_FAIL);
    8967     }
    8968 
    8969     /* set the proper machine state (note: after creating a Task instance) */
    8970     setMachineState(MachineState_RestoringSnapshot);
    8971 
    8972     /* return the progress to the caller */
    8973     progress.queryInterfaceTo(aProgress);
    8974 
    8975     /* return the new state to the caller */
    8976     *aMachineState = mData->mMachineState;
    8977 
    8978     LogFlowThisFuncLeave();
    8979 
    8980     return S_OK;
    8981 }
    8982 
    89838517STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
    89848518                                                 ComSafeArrayOut(BSTR, aValues),
     
    95879121    LogFlowThisFuncLeave();
    95889122    return rc;
    9589 }
    9590 
    9591 /**
    9592  * Helper method to finalize taking a snapshot. Gets called to finalize the
    9593  * "take snapshot" procedure, either from the public SessionMachine::EndTakingSnapshot()
    9594  * if taking the snapshot failed/was aborted or from the takeSnapshotHandler thread
    9595  * when taking the snapshot succeeded.
    9596  *
    9597  * Expected to be called after completing *all* the tasks related to taking the
    9598  * snapshot, either successfully or unsuccessfilly.
    9599  *
    9600  * @param aSuccess  TRUE if the snapshot has been taken successfully.
    9601  *
    9602  * @note Locks this objects for writing.
    9603  */
    9604 HRESULT SessionMachine::endTakingSnapshot(BOOL aSuccess)
    9605 {
    9606     LogFlowThisFuncEnter();
    9607 
    9608     AutoCaller autoCaller(this);
    9609     AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
    9610 
    9611     AutoMultiWriteLock2 alock(mParent, this);
    9612             // saveSettings needs VirtualBox lock
    9613 
    9614     AssertReturn(!mSnapshotData.mSnapshot.isNull(), E_FAIL);
    9615 
    9616     MultiResult rc(S_OK);
    9617 
    9618     ComObjPtr<Snapshot> pOldFirstSnap = mData->mFirstSnapshot;
    9619     ComObjPtr<Snapshot> pOldCurrentSnap = mData->mCurrentSnapshot;
    9620 
    9621     bool fOnline = Global::IsOnline(mSnapshotData.mLastState);
    9622 
    9623     if (aSuccess)
    9624     {
    9625         // new snapshot becomes the current one
    9626         mData->mCurrentSnapshot = mSnapshotData.mSnapshot;
    9627 
    9628         /* memorize the first snapshot if necessary */
    9629         if (!mData->mFirstSnapshot)
    9630             mData->mFirstSnapshot = mData->mCurrentSnapshot;
    9631 
    9632         if (!fOnline)
    9633             /* the machine was powered off or saved when taking a snapshot, so
    9634              * reset the mCurrentStateModified flag */
    9635             mData->mCurrentStateModified = FALSE;
    9636 
    9637         rc = saveSettings();
    9638     }
    9639 
    9640     if (aSuccess && SUCCEEDED(rc))
    9641     {
    9642         /* associate old hard disks with the snapshot and do locking/unlocking*/
    9643         fixupMedia(true /* aCommit */, fOnline);
    9644 
    9645         /* inform callbacks */
    9646         mParent->onSnapshotTaken(mData->mUuid,
    9647                                  mSnapshotData.mSnapshot->getId());
    9648     }
    9649     else
    9650     {
    9651         /* delete all differencing hard disks created (this will also attach
    9652          * their parents back by rolling back mMediaData) */
    9653         fixupMedia(false /* aCommit */);
    9654 
    9655         mData->mFirstSnapshot = pOldFirstSnap;      // might have been changed above
    9656         mData->mCurrentSnapshot = pOldCurrentSnap;      // might have been changed above
    9657 
    9658         /* delete the saved state file (it might have been already created) */
    9659         if (mSnapshotData.mSnapshot->stateFilePath().length())
    9660             RTFileDelete(mSnapshotData.mSnapshot->stateFilePath().c_str());
    9661 
    9662         mSnapshotData.mSnapshot->uninit();
    9663     }
    9664 
    9665     /* clear out the snapshot data */
    9666     mSnapshotData.mLastState = MachineState_Null;
    9667     mSnapshotData.mSnapshot.setNull();
    9668 
    9669     LogFlowThisFuncLeave();
    9670     return rc;
    9671 }
    9672 
    9673 /**
    9674  * Helper struct for SessionMachine::deleteSnapshotHandler().
    9675  */
    9676 struct MediumDiscardRec
    9677 {
    9678     MediumDiscardRec() : chain (NULL) {}
    9679 
    9680     MediumDiscardRec (const ComObjPtr<Medium> &aHd,
    9681                       Medium::MergeChain *aChain = NULL)
    9682         : hd (aHd), chain (aChain) {}
    9683 
    9684     MediumDiscardRec (const ComObjPtr<Medium> &aHd,
    9685                       Medium::MergeChain *aChain,
    9686                       const ComObjPtr<Medium> &aReplaceHd,
    9687                       const ComObjPtr<MediumAttachment> &aReplaceHda,
    9688                       const Guid &aSnapshotId)
    9689         : hd (aHd), chain (aChain)
    9690         , replaceHd (aReplaceHd), replaceHda (aReplaceHda)
    9691         , snapshotId (aSnapshotId) {}
    9692 
    9693     ComObjPtr<Medium> hd;
    9694     Medium::MergeChain *chain;
    9695     /* these are for the replace hard disk case: */
    9696     ComObjPtr<Medium> replaceHd;
    9697     ComObjPtr<MediumAttachment> replaceHda;
    9698     Guid snapshotId;
    9699 };
    9700 
    9701 typedef std::list <MediumDiscardRec> MediumDiscardRecList;
    9702 
    9703 /**
    9704  * Discard snapshot task handler. Must be called only by
    9705  * DeleteSnapshotTask::handler()!
    9706  *
    9707  * When aTask.subTask is true, the associated progress object is left
    9708  * uncompleted on success. On failure, the progress is marked as completed
    9709  * regardless of this parameter.
    9710  *
    9711  * @note Locks mParent + this + child objects for writing!
    9712  */
    9713 void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask)
    9714 {
    9715     LogFlowThisFuncEnter();
    9716 
    9717     AutoCaller autoCaller(this);
    9718 
    9719     LogFlowThisFunc(("state=%d\n", autoCaller.state()));
    9720     if (!autoCaller.isOk())
    9721     {
    9722         /* we might have been uninitialized because the session was accidentally
    9723          * closed by the client, so don't assert */
    9724         aTask.progress->notifyComplete(E_FAIL,
    9725                                        COM_IIDOF(IMachine),
    9726                                        getComponentName(),
    9727                                        tr("The session has been accidentally closed"));
    9728         LogFlowThisFuncLeave();
    9729         return;
    9730     }
    9731 
    9732     /* Locking order:  */
    9733     AutoMultiWriteLock3 alock(this->lockHandle(),
    9734                               this->snapshotsTreeLockHandle(),
    9735                               aTask.snapshot->lockHandle());
    9736 
    9737     ComPtr<SnapshotMachine> sm = aTask.snapshot->getSnapshotMachine();
    9738     /* no need to lock the snapshot machine since it is const by definiton */
    9739 
    9740     HRESULT rc = S_OK;
    9741 
    9742     /* save the snapshot ID (for callbacks) */
    9743     Guid snapshotId = aTask.snapshot->getId();
    9744 
    9745     MediumDiscardRecList toDiscard;
    9746 
    9747     bool settingsChanged = false;
    9748 
    9749     try
    9750     {
    9751         /* first pass: */
    9752         LogFlowThisFunc(("1: Checking hard disk merge prerequisites...\n"));
    9753 
    9754         for (MediaData::AttachmentList::const_iterator it = sm->mMediaData->mAttachments.begin();
    9755              it != sm->mMediaData->mAttachments.end();
    9756              ++it)
    9757         {
    9758             ComObjPtr<MediumAttachment> hda = *it;
    9759             ComObjPtr<Medium> hd = hda->medium();
    9760 
    9761             // medium can be NULL only for non-hard-disk types
    9762             Assert(    !hd.isNull()
    9763                     || hda->type() != DeviceType_HardDisk);
    9764             if (hd.isNull())
    9765                 continue;
    9766 
    9767             /* Medium::prepareDiscard() reqiuires a write lock */
    9768             AutoWriteLock hdLock(hd);
    9769 
    9770             if (hd->type() != MediumType_Normal)
    9771             {
    9772                 /* skip writethrough hard disks */
    9773                 Assert(hd->type() == MediumType_Writethrough);
    9774                 rc = aTask.progress->SetNextOperation(BstrFmt(tr("Skipping writethrough hard disk '%s'"),
    9775                                                               hd->base()->name().raw()),
    9776                                                       1); // weight
    9777                 CheckComRCThrowRC(rc);
    9778                 continue;
    9779             }
    9780 
    9781             Medium::MergeChain *chain = NULL;
    9782 
    9783             /* needs to be discarded (merged with the child if any), check
    9784              * prerequisites */
    9785             rc = hd->prepareDiscard(chain);
    9786             CheckComRCThrowRC(rc);
    9787 
    9788             if (hd->parent().isNull() && chain != NULL)
    9789             {
    9790                 /* it's a base hard disk so it will be a backward merge of its
    9791                  * only child to it (prepareDiscard() does necessary checks). We
    9792                  * need then to update the attachment that refers to the child
    9793                  * to refer to the parent instead. Don't forget to detach the
    9794                  * child (otherwise mergeTo() called by discard() will assert
    9795                  * because it will be going to delete the child) */
    9796 
    9797                 /* The below assert would be nice but I don't want to move
    9798                  * Medium::MergeChain to the header just for that
    9799                  * Assert (!chain->isForward()); */
    9800 
    9801                 Assert(hd->children().size() == 1);
    9802 
    9803                 ComObjPtr<Medium> replaceHd = hd->children().front();
    9804 
    9805                 const Guid *pReplaceMachineId = replaceHd->getFirstMachineBackrefId();
    9806                 Assert(pReplaceMachineId  && *pReplaceMachineId == mData->mUuid);
    9807 
    9808                 Guid snapshotId;
    9809                 const Guid *pSnapshotId = replaceHd->getFirstMachineBackrefSnapshotId();
    9810                 if (pSnapshotId)
    9811                     snapshotId = *pSnapshotId;
    9812 
    9813                 HRESULT rc2 = S_OK;
    9814 
    9815                 /* adjust back references */
    9816                 rc2 = replaceHd->detachFrom (mData->mUuid, snapshotId);
    9817                 AssertComRC(rc2);
    9818 
    9819                 rc2 = hd->attachTo (mData->mUuid, snapshotId);
    9820                 AssertComRC(rc2);
    9821 
    9822                 /* replace the hard disk in the attachment object */
    9823                 if (snapshotId.isEmpty())
    9824                 {
    9825                     /* in current state */
    9826                     AssertBreak(hda = findAttachment(mMediaData->mAttachments, replaceHd));
    9827                 }
    9828                 else
    9829                 {
    9830                     /* in snapshot */
    9831                     ComObjPtr<Snapshot> snapshot;
    9832                     rc2 = findSnapshot(snapshotId, snapshot);
    9833                     AssertComRC(rc2);
    9834 
    9835                     /* don't lock the snapshot; cannot be modified outside */
    9836                     MediaData::AttachmentList &snapAtts = snapshot->getSnapshotMachine()->mMediaData->mAttachments;
    9837                     AssertBreak(hda = findAttachment(snapAtts, replaceHd));
    9838                 }
    9839 
    9840                 AutoWriteLock attLock(hda);
    9841                 hda->updateMedium(hd, false /* aImplicit */);
    9842 
    9843                 toDiscard.push_back(MediumDiscardRec(hd,
    9844                                                      chain,
    9845                                                      replaceHd,
    9846                                                      hda,
    9847                                                      snapshotId));
    9848                 continue;
    9849             }
    9850 
    9851             toDiscard.push_back(MediumDiscardRec(hd, chain));
    9852         }
    9853 
    9854         /* Now we checked that we can successfully merge all normal hard disks
    9855          * (unless a runtime error like end-of-disc happens). Prior to
    9856          * performing the actual merge, we want to discard the snapshot itself
    9857          * and remove it from the XML file to make sure that a possible merge
    9858          * ruintime error will not make this snapshot inconsistent because of
    9859          * the partially merged or corrupted hard disks */
    9860 
    9861         /* second pass: */
    9862         LogFlowThisFunc(("2: Discarding snapshot...\n"));
    9863 
    9864         {
    9865             ComObjPtr<Snapshot> parentSnapshot = aTask.snapshot->parent();
    9866             Bstr stateFilePath = aTask.snapshot->stateFilePath();
    9867 
    9868             /* Note that discarding the snapshot will deassociate it from the
    9869              * hard disks which will allow the merge+delete operation for them*/
    9870             aTask.snapshot->beginDiscard();
    9871             aTask.snapshot->uninit();
    9872 
    9873             rc = saveAllSnapshots();
    9874             CheckComRCThrowRC(rc);
    9875 
    9876             /// @todo (dmik)
    9877             //  if we implement some warning mechanism later, we'll have
    9878             //  to return a warning if the state file path cannot be deleted
    9879             if (stateFilePath)
    9880             {
    9881                 aTask.progress->SetNextOperation(Bstr(tr("Discarding the execution state")),
    9882                                                  1);        // weight
    9883 
    9884                 RTFileDelete(Utf8Str(stateFilePath).c_str());
    9885             }
    9886 
    9887             /// @todo NEWMEDIA to provide a good level of fauilt tolerance, we
    9888             /// should restore the shapshot in the snapshot tree if
    9889             /// saveSnapshotSettings fails. Actually, we may call
    9890             /// #saveSnapshotSettings() with a special flag that will tell it to
    9891             /// skip the given snapshot as if it would have been discarded and
    9892             /// only actually discard it if the save operation succeeds.
    9893         }
    9894 
    9895         /* here we come when we've irrevesibly discarded the snapshot which
    9896          * means that the VM settigns (our relevant changes to mData) need to be
    9897          * saved too */
    9898         /// @todo NEWMEDIA maybe save everything in one operation in place of
    9899         ///  saveSnapshotSettings() above
    9900         settingsChanged = true;
    9901 
    9902         /* third pass: */
    9903         LogFlowThisFunc(("3: Performing actual hard disk merging...\n"));
    9904 
    9905         /* leave the locks before the potentially lengthy operation */
    9906         alock.leave();
    9907 
    9908         /// @todo NEWMEDIA turn the following errors into warnings because the
    9909         /// snapshot itself has been already deleted (and interpret these
    9910         /// warnings properly on the GUI side)
    9911 
    9912         for (MediumDiscardRecList::iterator it = toDiscard.begin();
    9913              it != toDiscard.end();)
    9914         {
    9915             rc = it->hd->discard (aTask.progress, it->chain);
    9916             CheckComRCBreakRC(rc);
    9917 
    9918             /* prevent from calling cancelDiscard() */
    9919             it = toDiscard.erase (it);
    9920         }
    9921 
    9922         alock.enter();
    9923 
    9924         CheckComRCThrowRC(rc);
    9925     }
    9926     catch (HRESULT aRC) { rc = aRC; }
    9927 
    9928     if (FAILED(rc))
    9929     {
    9930         HRESULT rc2 = S_OK;
    9931 
    9932         /* un-prepare the remaining hard disks */
    9933         for (MediumDiscardRecList::const_iterator it = toDiscard.begin();
    9934              it != toDiscard.end(); ++it)
    9935         {
    9936             it->hd->cancelDiscard (it->chain);
    9937 
    9938             if (!it->replaceHd.isNull())
    9939             {
    9940                 /* undo hard disk replacement */
    9941 
    9942                 rc2 = it->replaceHd->attachTo (mData->mUuid, it->snapshotId);
    9943                 AssertComRC(rc2);
    9944 
    9945                 rc2 = it->hd->detachFrom (mData->mUuid, it->snapshotId);
    9946                 AssertComRC(rc2);
    9947 
    9948                 AutoWriteLock attLock (it->replaceHda);
    9949                 it->replaceHda->updateMedium(it->replaceHd, false /* aImplicit */);
    9950             }
    9951         }
    9952     }
    9953 
    9954     if (!aTask.subTask || FAILED(rc))
    9955     {
    9956         if (!aTask.subTask)
    9957         {
    9958             /* saveSettings() below needs a VirtualBox write lock and we need to
    9959              * leave this object's lock to do this to follow the {parent-child}
    9960              * locking rule. This is the last chance to do that while we are
    9961              * still in a protective state which allows us to temporarily leave
    9962              * the lock */
    9963             alock.unlock();
    9964             AutoWriteLock vboxLock(mParent);
    9965             alock.lock();
    9966 
    9967             /* preserve existing error info */
    9968             ErrorInfoKeeper eik;
    9969 
    9970             /* restore the machine state */
    9971             setMachineState(aTask.state);
    9972             updateMachineStateOnClient();
    9973 
    9974             if (settingsChanged)
    9975                 saveSettings(SaveS_InformCallbacksAnyway);
    9976         }
    9977 
    9978         /* set the result (this will try to fetch current error info on failure) */
    9979         aTask.progress->notifyComplete (rc);
    9980     }
    9981 
    9982     if (SUCCEEDED(rc))
    9983         mParent->onSnapshotDiscarded (mData->mUuid, snapshotId);
    9984 
    9985     LogFlowThisFunc(("Done discarding snapshot (rc=%08X)\n", rc));
    9986     LogFlowThisFuncLeave();
    9987 }
    9988 
    9989 /**
    9990  * Restore snapshot state task handler. Must be called only by
    9991  * RestoreSnapshotTask::handler()!
    9992  *
    9993  * @note Locks mParent + this object for writing.
    9994  */
    9995 void SessionMachine::restoreSnapshotHandler(RestoreSnapshotTask &aTask)
    9996 {
    9997     LogFlowThisFuncEnter();
    9998 
    9999     AutoCaller autoCaller(this);
    10000 
    10001     LogFlowThisFunc(("state=%d\n", autoCaller.state()));
    10002     if (!autoCaller.isOk())
    10003     {
    10004         /* we might have been uninitialized because the session was accidentally
    10005          * closed by the client, so don't assert */
    10006         aTask.progress->notifyComplete(E_FAIL,
    10007                                        COM_IIDOF(IMachine),
    10008                                        getComponentName(),
    10009                                        tr("The session has been accidentally closed"));
    10010 
    10011         LogFlowThisFuncLeave();
    10012         return;
    10013     }
    10014 
    10015     /* saveSettings() needs mParent lock */
    10016     AutoWriteLock vboxLock(mParent);
    10017 
    10018     /* @todo We don't need mParent lock so far so unlock() it. Better is to
    10019      * provide an AutoWriteLock argument that lets create a non-locking
    10020      * instance */
    10021     vboxLock.unlock();
    10022 
    10023     AutoWriteLock alock(this);
    10024 
    10025     /* discard all current changes to mUserData (name, OSType etc.) (note that
    10026      * the machine is powered off, so there is no need to inform the direct
    10027      * session) */
    10028     if (isModified())
    10029         rollback(false /* aNotify */);
    10030 
    10031     HRESULT rc = S_OK;
    10032 
    10033     bool stateRestored = false;
    10034 
    10035     try
    10036     {
    10037         /* discard the saved state file if the machine was Saved prior to this
    10038          * operation */
    10039         if (aTask.state == MachineState_Saved)
    10040         {
    10041             Assert(!mSSData->mStateFilePath.isEmpty());
    10042             RTFileDelete(mSSData->mStateFilePath.c_str());
    10043             mSSData->mStateFilePath.setNull();
    10044             aTask.modifyLastState(MachineState_PoweredOff);
    10045             rc = saveStateSettings(SaveSTS_StateFilePath);
    10046             CheckComRCThrowRC(rc);
    10047         }
    10048 
    10049         RTTIMESPEC snapshotTimeStamp;
    10050         RTTimeSpecSetMilli(&snapshotTimeStamp, 0);
    10051 
    10052         {
    10053             AutoReadLock snapshotLock(aTask.m_pSnapshot);
    10054 
    10055             /* remember the timestamp of the snapshot we're restoring from */
    10056             snapshotTimeStamp = aTask.m_pSnapshot->getTimeStamp();
    10057 
    10058             ComPtr<SnapshotMachine> pSnapshotMachine(aTask.m_pSnapshot->getSnapshotMachine());
    10059 
    10060             /* copy all hardware data from the snapshot */
    10061             copyFrom(pSnapshotMachine);
    10062 
    10063             LogFlowThisFunc(("Restoring hard disks from the snapshot...\n"));
    10064 
    10065             /* restore the attachments from the snapshot */
    10066             mMediaData.backup();
    10067             mMediaData->mAttachments = pSnapshotMachine->mMediaData->mAttachments;
    10068 
    10069             /* leave the locks before the potentially lengthy operation */
    10070             snapshotLock.unlock();
    10071             alock.leave();
    10072 
    10073             rc = createImplicitDiffs(mUserData->mSnapshotFolderFull,
    10074                                      aTask.progress,
    10075                                      1,
    10076                                      false /* aOnline */);
    10077 
    10078             alock.enter();
    10079             snapshotLock.lock();
    10080 
    10081             CheckComRCThrowRC(rc);
    10082 
    10083             /* Note: on success, current (old) hard disks will be
    10084              * deassociated/deleted on #commit() called from #saveSettings() at
    10085              * the end. On failure, newly created implicit diffs will be
    10086              * deleted by #rollback() at the end. */
    10087 
    10088             /* should not have a saved state file associated at this point */
    10089             Assert(mSSData->mStateFilePath.isEmpty());
    10090 
    10091             if (!aTask.m_pSnapshot->stateFilePath().isEmpty())
    10092             {
    10093                 Utf8Str snapStateFilePath = aTask.m_pSnapshot->stateFilePath();
    10094 
    10095                 Utf8Str stateFilePath = Utf8StrFmt("%ls%c{%RTuuid}.sav",
    10096                                                    mUserData->mSnapshotFolderFull.raw(),
    10097                                                    RTPATH_DELIMITER,
    10098                                                    mData->mUuid.raw());
    10099 
    10100                 LogFlowThisFunc(("Copying saved state file from '%s' to '%s'...\n",
    10101                                   snapStateFilePath.raw(), stateFilePath.raw()));
    10102 
    10103                 aTask.progress->SetNextOperation(Bstr(tr("Restoring the execution state")),
    10104                                                  aTask.m_ulStateFileSizeMB);        // weight
    10105 
    10106                 /* leave the lock before the potentially lengthy operation */
    10107                 snapshotLock.unlock();
    10108                 alock.leave();
    10109 
    10110                 /* copy the state file */
    10111                 int vrc = RTFileCopyEx(snapStateFilePath.c_str(),
    10112                                        stateFilePath.c_str(),
    10113                                        0,
    10114                                        progressCallback,
    10115                                        aTask.progress);
    10116 
    10117                 alock.enter();
    10118                 snapshotLock.lock();
    10119 
    10120                 if (RT_SUCCESS(vrc))
    10121                     mSSData->mStateFilePath = stateFilePath;
    10122                 else
    10123                     throw setError(E_FAIL,
    10124                                    tr("Could not copy the state file '%s' to '%s' (%Rrc)"),
    10125                                    snapStateFilePath.raw(),
    10126                                    stateFilePath.raw(),
    10127                                    vrc);
    10128             }
    10129 
    10130             LogFlowThisFunc(("Setting new current snapshot {%RTuuid}\n", aTask.m_pSnapshot->getId().raw()));
    10131             /* make the snapshot we restored from the current snapshot */
    10132             mData->mCurrentSnapshot = aTask.m_pSnapshot;
    10133         }
    10134 
    10135         /* grab differencing hard disks from the old attachments that will
    10136          * become unused and need to be auto-deleted */
    10137 
    10138         std::list< ComObjPtr<MediumAttachment> > llDiffAttachmentsToDelete;
    10139 
    10140         for (MediaData::AttachmentList::const_iterator it = mMediaData.backedUpData()->mAttachments.begin();
    10141              it != mMediaData.backedUpData()->mAttachments.end();
    10142              ++it)
    10143         {
    10144             ComObjPtr<MediumAttachment> pAttach = *it;
    10145             ComObjPtr<Medium> pMedium = pAttach->medium();
    10146 
    10147             /* while the hard disk is attached, the number of children or the
    10148              * parent cannot change, so no lock */
    10149             if (    !pMedium.isNull()
    10150                  && pAttach->type() == DeviceType_HardDisk
    10151                  && !pMedium->parent().isNull()
    10152                  && pMedium->children().size() == 0
    10153                )
    10154             {
    10155                 LogFlowThisFunc(("Picked differencing image '%s' for deletion\n", pMedium->name().raw()));
    10156 
    10157                 llDiffAttachmentsToDelete.push_back(pAttach);
    10158             }
    10159         }
    10160 
    10161         int saveFlags = 0;
    10162 
    10163         /* @todo saveSettings() below needs a VirtualBox write lock and we need
    10164          * to leave this object's lock to do this to follow the {parent-child}
    10165          * locking rule. This is the last chance to do that while we are still
    10166          * in a protective state which allows us to temporarily leave the lock*/
    10167         alock.unlock();
    10168         vboxLock.lock();
    10169         alock.lock();
    10170 
    10171         /* we have already discarded the current state, so set the execution
    10172          * state accordingly no matter of the discard snapshot result */
    10173         if (!mSSData->mStateFilePath.isEmpty())
    10174             setMachineState(MachineState_Saved);
    10175         else
    10176             setMachineState(MachineState_PoweredOff);
    10177 
    10178         updateMachineStateOnClient();
    10179         stateRestored = true;
    10180 
    10181         /* assign the timestamp from the snapshot */
    10182         Assert(RTTimeSpecGetMilli (&snapshotTimeStamp) != 0);
    10183         mData->mLastStateChange = snapshotTimeStamp;
    10184 
    10185         // detach the current-state diffs that we detected above and build a list of
    10186         // images to delete _after_ saveSettings()
    10187 
    10188         std::list< ComObjPtr<Medium> > llDiffsToDelete;
    10189 
    10190         for (std::list< ComObjPtr<MediumAttachment> >::iterator it = llDiffAttachmentsToDelete.begin();
    10191              it != llDiffAttachmentsToDelete.end();
    10192              ++it)
    10193         {
    10194             ComObjPtr<MediumAttachment> pAttach = *it;        // guaranteed to have only attachments where medium != NULL
    10195             ComObjPtr<Medium> pMedium = pAttach->medium();
    10196 
    10197             AutoWriteLock mlock(pMedium);
    10198 
    10199             LogFlowThisFunc(("Detaching old current state in differencing image '%s'\n", pMedium->name().raw()));
    10200 
    10201             mMediaData->mAttachments.remove(pAttach);
    10202             pMedium->detachFrom(mData->mUuid);
    10203 
    10204             llDiffsToDelete.push_back(pMedium);
    10205         }
    10206 
    10207         // save all settings, reset the modified flag and commit;
    10208         // from here on we cannot roll back on failure any more
    10209         rc = saveSettings(SaveS_ResetCurStateModified | saveFlags);
    10210 
    10211         for (std::list< ComObjPtr<Medium> >::iterator it = llDiffsToDelete.begin();
    10212              it != llDiffsToDelete.end();
    10213              ++it)
    10214         {
    10215             ComObjPtr<Medium> &pMedium = *it;
    10216             LogFlowThisFunc(("Deleting old current state in differencing image '%s'\n", pMedium->name().raw()));
    10217 
    10218             HRESULT rc2 = pMedium->deleteStorageAndWait();
    10219             // ignore errors here because we cannot roll back after saveSettings() above
    10220             if (SUCCEEDED(rc2))
    10221                 pMedium->uninit();
    10222         }
    10223     }
    10224     catch (HRESULT aRC)
    10225     {
    10226         rc = aRC;
    10227     }
    10228 
    10229     if (FAILED(rc))
    10230     {
    10231         /* preserve existing error info */
    10232         ErrorInfoKeeper eik;
    10233 
    10234         /* undo all changes on failure */
    10235         rollback(false /* aNotify */);
    10236 
    10237         if (!stateRestored)
    10238         {
    10239             /* restore the machine state */
    10240             setMachineState(aTask.state);
    10241             updateMachineStateOnClient();
    10242         }
    10243     }
    10244 
    10245     /* set the result (this will try to fetch current error info on failure) */
    10246     aTask.progress->notifyComplete(rc);
    10247 
    10248     if (SUCCEEDED(rc))
    10249         mParent->onSnapshotDiscarded(mData->mUuid, Guid());
    10250 
    10251     LogFlowThisFunc(("Done restoring snapshot (rc=%08X)\n", rc));
    10252 
    10253     LogFlowThisFuncLeave();
    102549123}
    102559124
     
    106279496    return directControl->UpdateMachineState (mData->mMachineState);
    106289497}
    10629 
    10630 /* static */
    10631 DECLCALLBACK(int) SessionMachine::taskHandler (RTTHREAD /* thread */, void *pvUser)
    10632 {
    10633     AssertReturn(pvUser, VERR_INVALID_POINTER);
    10634 
    10635     Task *task = static_cast <Task *> (pvUser);
    10636     task->handler();
    10637 
    10638     // it's our responsibility to delete the task
    10639     delete task;
    10640 
    10641     return 0;
    10642 }
    10643 
    10644 /////////////////////////////////////////////////////////////////////////////
    10645 // SnapshotMachine class
    10646 /////////////////////////////////////////////////////////////////////////////
    10647 
    10648 DEFINE_EMPTY_CTOR_DTOR (SnapshotMachine)
    10649 
    10650 HRESULT SnapshotMachine::FinalConstruct()
    10651 {
    10652     LogFlowThisFunc(("\n"));
    10653 
    10654     /* set the proper type to indicate we're the SnapshotMachine instance */
    10655     unconst(mType) = IsSnapshotMachine;
    10656 
    10657     return S_OK;
    10658 }
    10659 
    10660 void SnapshotMachine::FinalRelease()
    10661 {
    10662     LogFlowThisFunc(("\n"));
    10663 
    10664     uninit();
    10665 }
    10666 
    10667 /**
    10668  *  Initializes the SnapshotMachine object when taking a snapshot.
    10669  *
    10670  *  @param aSessionMachine  machine to take a snapshot from
    10671  *  @param aSnapshotId      snapshot ID of this snapshot machine
    10672  *  @param aStateFilePath   file where the execution state will be later saved
    10673  *                          (or NULL for the offline snapshot)
    10674  *
    10675  *  @note The aSessionMachine must be locked for writing.
    10676  */
    10677 HRESULT SnapshotMachine::init(SessionMachine *aSessionMachine,
    10678                               IN_GUID aSnapshotId,
    10679                               const Utf8Str &aStateFilePath)
    10680 {
    10681     LogFlowThisFuncEnter();
    10682     LogFlowThisFunc(("mName={%ls}\n", aSessionMachine->mUserData->mName.raw()));
    10683 
    10684     AssertReturn(aSessionMachine && !Guid (aSnapshotId).isEmpty(), E_INVALIDARG);
    10685 
    10686     /* Enclose the state transition NotReady->InInit->Ready */
    10687     AutoInitSpan autoInitSpan(this);
    10688     AssertReturn(autoInitSpan.isOk(), E_FAIL);
    10689 
    10690     AssertReturn(aSessionMachine->isWriteLockOnCurrentThread(), E_FAIL);
    10691 
    10692     mSnapshotId = aSnapshotId;
    10693 
    10694     /* memorize the primary Machine instance (i.e. not SessionMachine!) */
    10695     unconst(mPeer) = aSessionMachine->mPeer;
    10696     /* share the parent pointer */
    10697     unconst(mParent) = mPeer->mParent;
    10698 
    10699     /* take the pointer to Data to share */
    10700     mData.share (mPeer->mData);
    10701 
    10702     /* take the pointer to UserData to share (our UserData must always be the
    10703      * same as Machine's data) */
    10704     mUserData.share (mPeer->mUserData);
    10705     /* make a private copy of all other data (recent changes from SessionMachine) */
    10706     mHWData.attachCopy (aSessionMachine->mHWData);
    10707     mMediaData.attachCopy(aSessionMachine->mMediaData);
    10708 
    10709     /* SSData is always unique for SnapshotMachine */
    10710     mSSData.allocate();
    10711     mSSData->mStateFilePath = aStateFilePath;
    10712 
    10713     HRESULT rc = S_OK;
    10714 
    10715     /* create copies of all shared folders (mHWData after attiching a copy
    10716      * contains just references to original objects) */
    10717     for (HWData::SharedFolderList::iterator
    10718          it = mHWData->mSharedFolders.begin();
    10719          it != mHWData->mSharedFolders.end();
    10720          ++it)
    10721     {
    10722         ComObjPtr<SharedFolder> folder;
    10723         folder.createObject();
    10724         rc = folder->initCopy (this, *it);
    10725         CheckComRCReturnRC(rc);
    10726         *it = folder;
    10727     }
    10728 
    10729     /* associate hard disks with the snapshot
    10730      * (Machine::uninitDataAndChildObjects() will deassociate at destruction) */
    10731     for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
    10732          it != mMediaData->mAttachments.end();
    10733          ++it)
    10734     {
    10735         MediumAttachment *pAtt = *it;
    10736         Medium *pMedium = pAtt->medium();
    10737         if (pMedium) // can be NULL for non-harddisk
    10738         {
    10739             rc = pMedium->attachTo(mData->mUuid, mSnapshotId);
    10740             AssertComRC(rc);
    10741         }
    10742     }
    10743 
    10744     /* create copies of all storage controllers (mStorageControllerData
    10745      * after attaching a copy contains just references to original objects) */
    10746     mStorageControllers.allocate();
    10747     for (StorageControllerList::const_iterator
    10748          it = aSessionMachine->mStorageControllers->begin();
    10749          it != aSessionMachine->mStorageControllers->end();
    10750          ++it)
    10751     {
    10752         ComObjPtr<StorageController> ctrl;
    10753         ctrl.createObject();
    10754         ctrl->initCopy (this, *it);
    10755         mStorageControllers->push_back(ctrl);
    10756     }
    10757 
    10758     /* create all other child objects that will be immutable private copies */
    10759 
    10760     unconst(mBIOSSettings).createObject();
    10761     mBIOSSettings->initCopy (this, mPeer->mBIOSSettings);
    10762 
    10763 #ifdef VBOX_WITH_VRDP
    10764     unconst(mVRDPServer).createObject();
    10765     mVRDPServer->initCopy (this, mPeer->mVRDPServer);
    10766 #endif
    10767 
    10768     unconst(mAudioAdapter).createObject();
    10769     mAudioAdapter->initCopy (this, mPeer->mAudioAdapter);
    10770 
    10771     unconst(mUSBController).createObject();
    10772     mUSBController->initCopy (this, mPeer->mUSBController);
    10773 
    10774     for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
    10775     {
    10776         unconst(mNetworkAdapters [slot]).createObject();
    10777         mNetworkAdapters [slot]->initCopy (this, mPeer->mNetworkAdapters [slot]);
    10778     }
    10779 
    10780     for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
    10781     {
    10782         unconst(mSerialPorts [slot]).createObject();
    10783         mSerialPorts [slot]->initCopy (this, mPeer->mSerialPorts [slot]);
    10784     }
    10785 
    10786     for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
    10787     {
    10788         unconst(mParallelPorts [slot]).createObject();
    10789         mParallelPorts [slot]->initCopy (this, mPeer->mParallelPorts [slot]);
    10790     }
    10791 
    10792     /* Confirm a successful initialization when it's the case */
    10793     autoInitSpan.setSucceeded();
    10794 
    10795     LogFlowThisFuncLeave();
    10796     return S_OK;
    10797 }
    10798 
    10799 /**
    10800  *  Initializes the SnapshotMachine object when loading from the settings file.
    10801  *
    10802  *  @param aMachine machine the snapshot belngs to
    10803  *  @param aHWNode          <Hardware> node
    10804  *  @param aHDAsNode        <HardDiskAttachments> node
    10805  *  @param aSnapshotId      snapshot ID of this snapshot machine
    10806  *  @param aStateFilePath   file where the execution state is saved
    10807  *                          (or NULL for the offline snapshot)
    10808  *
    10809  *  @note Doesn't lock anything.
    10810  */
    10811 HRESULT SnapshotMachine::init(Machine *aMachine,
    10812                               const settings::Hardware &hardware,
    10813                               const settings::Storage &storage,
    10814                               IN_GUID aSnapshotId,
    10815                               const Utf8Str &aStateFilePath)
    10816 {
    10817     LogFlowThisFuncEnter();
    10818     LogFlowThisFunc(("mName={%ls}\n", aMachine->mUserData->mName.raw()));
    10819 
    10820     AssertReturn(aMachine &&  !Guid(aSnapshotId).isEmpty(), E_INVALIDARG);
    10821 
    10822     /* Enclose the state transition NotReady->InInit->Ready */
    10823     AutoInitSpan autoInitSpan(this);
    10824     AssertReturn(autoInitSpan.isOk(), E_FAIL);
    10825 
    10826     /* Don't need to lock aMachine when VirtualBox is starting up */
    10827 
    10828     mSnapshotId = aSnapshotId;
    10829 
    10830     /* memorize the primary Machine instance */
    10831     unconst(mPeer) = aMachine;
    10832     /* share the parent pointer */
    10833     unconst(mParent) = mPeer->mParent;
    10834 
    10835     /* take the pointer to Data to share */
    10836     mData.share (mPeer->mData);
    10837     /*
    10838      *  take the pointer to UserData to share
    10839      *  (our UserData must always be the same as Machine's data)
    10840      */
    10841     mUserData.share (mPeer->mUserData);
    10842     /* allocate private copies of all other data (will be loaded from settings) */
    10843     mHWData.allocate();
    10844     mMediaData.allocate();
    10845     mStorageControllers.allocate();
    10846 
    10847     /* SSData is always unique for SnapshotMachine */
    10848     mSSData.allocate();
    10849     mSSData->mStateFilePath = aStateFilePath;
    10850 
    10851     /* create all other child objects that will be immutable private copies */
    10852 
    10853     unconst(mBIOSSettings).createObject();
    10854     mBIOSSettings->init (this);
    10855 
    10856 #ifdef VBOX_WITH_VRDP
    10857     unconst(mVRDPServer).createObject();
    10858     mVRDPServer->init (this);
    10859 #endif
    10860 
    10861     unconst(mAudioAdapter).createObject();
    10862     mAudioAdapter->init (this);
    10863 
    10864     unconst(mUSBController).createObject();
    10865     mUSBController->init (this);
    10866 
    10867     for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
    10868     {
    10869         unconst(mNetworkAdapters [slot]).createObject();
    10870         mNetworkAdapters [slot]->init (this, slot);
    10871     }
    10872 
    10873     for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
    10874     {
    10875         unconst(mSerialPorts [slot]).createObject();
    10876         mSerialPorts [slot]->init (this, slot);
    10877     }
    10878 
    10879     for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
    10880     {
    10881         unconst(mParallelPorts [slot]).createObject();
    10882         mParallelPorts [slot]->init (this, slot);
    10883     }
    10884 
    10885     /* load hardware and harddisk settings */
    10886 
    10887     HRESULT rc = loadHardware(hardware);
    10888     if (SUCCEEDED(rc))
    10889         rc = loadStorageControllers(storage, true /* aRegistered */, &mSnapshotId);
    10890 
    10891     if (SUCCEEDED(rc))
    10892         /* commit all changes made during the initialization */
    10893         commit();
    10894 
    10895     /* Confirm a successful initialization when it's the case */
    10896     if (SUCCEEDED(rc))
    10897         autoInitSpan.setSucceeded();
    10898 
    10899     LogFlowThisFuncLeave();
    10900     return rc;
    10901 }
    10902 
    10903 /**
    10904  *  Uninitializes this SnapshotMachine object.
    10905  */
    10906 void SnapshotMachine::uninit()
    10907 {
    10908     LogFlowThisFuncEnter();
    10909 
    10910     /* Enclose the state transition Ready->InUninit->NotReady */
    10911     AutoUninitSpan autoUninitSpan(this);
    10912     if (autoUninitSpan.uninitDone())
    10913         return;
    10914 
    10915     uninitDataAndChildObjects();
    10916 
    10917     /* free the essential data structure last */
    10918     mData.free();
    10919 
    10920     unconst(mParent).setNull();
    10921     unconst(mPeer).setNull();
    10922 
    10923     LogFlowThisFuncLeave();
    10924 }
    10925 
    10926 // util::Lockable interface
    10927 ////////////////////////////////////////////////////////////////////////////////
    10928 
    10929 /**
    10930  *  Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
    10931  *  with the primary Machine instance (mPeer).
    10932  */
    10933 RWLockHandle *SnapshotMachine::lockHandle() const
    10934 {
    10935     AssertReturn(!mPeer.isNull(), NULL);
    10936     return mPeer->lockHandle();
    10937 }
    10938 
    10939 // public methods only for internal purposes
    10940 ////////////////////////////////////////////////////////////////////////////////
    10941 
    10942 /**
    10943  *  Called by the snapshot object associated with this SnapshotMachine when
    10944  *  snapshot data such as name or description is changed.
    10945  *
    10946  *  @note Locks this object for writing.
    10947  */
    10948 HRESULT SnapshotMachine::onSnapshotChange (Snapshot *aSnapshot)
    10949 {
    10950     AutoWriteLock alock(this);
    10951 
    10952     //     mPeer->saveAllSnapshots();  @todo
    10953 
    10954     /* inform callbacks */
    10955     mParent->onSnapshotChange(mData->mUuid, aSnapshot->getId());
    10956 
    10957     return S_OK;
    10958 }
    10959 /* vi: set tabstop=4 shiftwidth=4 expandtab: */
  • trunk/src/VBox/Main/SnapshotImpl.cpp

    r24196 r24279  
    11/** @file
    22 *
    3  * VirtualBox COM class implementation
     3 * COM class implementation for Snapshot and SnapshotMachine.
    44 */
    55
     
    2323
    2424#include "MachineImpl.h"
     25#include "Global.h"
     26
     27// @todo these three includes are required for about one or two lines, try
     28// to remove them and put that code in shared code in MachineImplcpp
     29#include "SharedFolderImpl.h"
     30#include "USBControllerImpl.h"
     31#include "VirtualBoxImpl.h"
     32
    2533#include "Logging.h"
    2634
     
    2937#include <VBox/err.h>
    3038
    31 #include <list>
    32 
    3339#include <VBox/settings.h>
     40
     41////////////////////////////////////////////////////////////////////////////////
     42//
     43// Globals
     44//
     45////////////////////////////////////////////////////////////////////////////////
     46
     47/**
     48 *  Progress callback handler for lengthy operations
     49 *  (corresponds to the FNRTPROGRESS typedef).
     50 *
     51 *  @param uPercentage  Completetion precentage (0-100).
     52 *  @param pvUser       Pointer to the Progress instance.
     53 */
     54static DECLCALLBACK(int) progressCallback(unsigned uPercentage, void *pvUser)
     55{
     56    Progress *progress = static_cast<Progress*>(pvUser);
     57
     58    /* update the progress object */
     59    if (progress)
     60        progress->SetCurrentOperationProgress(uPercentage);
     61
     62    return VINF_SUCCESS;
     63}
    3464
    3565////////////////////////////////////////////////////////////////////////////////
     
    410440}
    411441
    412 // public methods only for internal purposes
     442////////////////////////////////////////////////////////////////////////////////
     443//
     444// Snapshot public internal methods
     445//
    413446////////////////////////////////////////////////////////////////////////////////
    414447
     
    699732}
    700733
     734////////////////////////////////////////////////////////////////////////////////
     735//
     736// SnapshotMachine implementation
     737//
     738////////////////////////////////////////////////////////////////////////////////
     739
     740DEFINE_EMPTY_CTOR_DTOR (SnapshotMachine)
     741
     742HRESULT SnapshotMachine::FinalConstruct()
     743{
     744    LogFlowThisFunc(("\n"));
     745
     746    /* set the proper type to indicate we're the SnapshotMachine instance */
     747    unconst(mType) = IsSnapshotMachine;
     748
     749    return S_OK;
     750}
     751
     752void SnapshotMachine::FinalRelease()
     753{
     754    LogFlowThisFunc(("\n"));
     755
     756    uninit();
     757}
     758
     759/**
     760 *  Initializes the SnapshotMachine object when taking a snapshot.
     761 *
     762 *  @param aSessionMachine  machine to take a snapshot from
     763 *  @param aSnapshotId      snapshot ID of this snapshot machine
     764 *  @param aStateFilePath   file where the execution state will be later saved
     765 *                          (or NULL for the offline snapshot)
     766 *
     767 *  @note The aSessionMachine must be locked for writing.
     768 */
     769HRESULT SnapshotMachine::init(SessionMachine *aSessionMachine,
     770                              IN_GUID aSnapshotId,
     771                              const Utf8Str &aStateFilePath)
     772{
     773    LogFlowThisFuncEnter();
     774    LogFlowThisFunc(("mName={%ls}\n", aSessionMachine->mUserData->mName.raw()));
     775
     776    AssertReturn(aSessionMachine && !Guid (aSnapshotId).isEmpty(), E_INVALIDARG);
     777
     778    /* Enclose the state transition NotReady->InInit->Ready */
     779    AutoInitSpan autoInitSpan(this);
     780    AssertReturn(autoInitSpan.isOk(), E_FAIL);
     781
     782    AssertReturn(aSessionMachine->isWriteLockOnCurrentThread(), E_FAIL);
     783
     784    mSnapshotId = aSnapshotId;
     785
     786    /* memorize the primary Machine instance (i.e. not SessionMachine!) */
     787    unconst(mPeer) = aSessionMachine->mPeer;
     788    /* share the parent pointer */
     789    unconst(mParent) = mPeer->mParent;
     790
     791    /* take the pointer to Data to share */
     792    mData.share (mPeer->mData);
     793
     794    /* take the pointer to UserData to share (our UserData must always be the
     795     * same as Machine's data) */
     796    mUserData.share (mPeer->mUserData);
     797    /* make a private copy of all other data (recent changes from SessionMachine) */
     798    mHWData.attachCopy (aSessionMachine->mHWData);
     799    mMediaData.attachCopy(aSessionMachine->mMediaData);
     800
     801    /* SSData is always unique for SnapshotMachine */
     802    mSSData.allocate();
     803    mSSData->mStateFilePath = aStateFilePath;
     804
     805    HRESULT rc = S_OK;
     806
     807    /* create copies of all shared folders (mHWData after attiching a copy
     808     * contains just references to original objects) */
     809    for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
     810         it != mHWData->mSharedFolders.end();
     811         ++it)
     812    {
     813        ComObjPtr<SharedFolder> folder;
     814        folder.createObject();
     815        rc = folder->initCopy (this, *it);
     816        CheckComRCReturnRC(rc);
     817        *it = folder;
     818    }
     819
     820    /* associate hard disks with the snapshot
     821     * (Machine::uninitDataAndChildObjects() will deassociate at destruction) */
     822    for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
     823         it != mMediaData->mAttachments.end();
     824         ++it)
     825    {
     826        MediumAttachment *pAtt = *it;
     827        Medium *pMedium = pAtt->medium();
     828        if (pMedium) // can be NULL for non-harddisk
     829        {
     830            rc = pMedium->attachTo(mData->mUuid, mSnapshotId);
     831            AssertComRC(rc);
     832        }
     833    }
     834
     835    /* create copies of all storage controllers (mStorageControllerData
     836     * after attaching a copy contains just references to original objects) */
     837    mStorageControllers.allocate();
     838    for (StorageControllerList::const_iterator
     839         it = aSessionMachine->mStorageControllers->begin();
     840         it != aSessionMachine->mStorageControllers->end();
     841         ++it)
     842    {
     843        ComObjPtr<StorageController> ctrl;
     844        ctrl.createObject();
     845        ctrl->initCopy (this, *it);
     846        mStorageControllers->push_back(ctrl);
     847    }
     848
     849    /* create all other child objects that will be immutable private copies */
     850
     851    unconst(mBIOSSettings).createObject();
     852    mBIOSSettings->initCopy (this, mPeer->mBIOSSettings);
     853
     854#ifdef VBOX_WITH_VRDP
     855    unconst(mVRDPServer).createObject();
     856    mVRDPServer->initCopy (this, mPeer->mVRDPServer);
     857#endif
     858
     859    unconst(mAudioAdapter).createObject();
     860    mAudioAdapter->initCopy (this, mPeer->mAudioAdapter);
     861
     862    unconst(mUSBController).createObject();
     863    mUSBController->initCopy (this, mPeer->mUSBController);
     864
     865    for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
     866    {
     867        unconst(mNetworkAdapters [slot]).createObject();
     868        mNetworkAdapters [slot]->initCopy (this, mPeer->mNetworkAdapters [slot]);
     869    }
     870
     871    for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
     872    {
     873        unconst(mSerialPorts [slot]).createObject();
     874        mSerialPorts [slot]->initCopy (this, mPeer->mSerialPorts [slot]);
     875    }
     876
     877    for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
     878    {
     879        unconst(mParallelPorts [slot]).createObject();
     880        mParallelPorts [slot]->initCopy (this, mPeer->mParallelPorts [slot]);
     881    }
     882
     883    /* Confirm a successful initialization when it's the case */
     884    autoInitSpan.setSucceeded();
     885
     886    LogFlowThisFuncLeave();
     887    return S_OK;
     888}
     889
     890/**
     891 *  Initializes the SnapshotMachine object when loading from the settings file.
     892 *
     893 *  @param aMachine machine the snapshot belngs to
     894 *  @param aHWNode          <Hardware> node
     895 *  @param aHDAsNode        <HardDiskAttachments> node
     896 *  @param aSnapshotId      snapshot ID of this snapshot machine
     897 *  @param aStateFilePath   file where the execution state is saved
     898 *                          (or NULL for the offline snapshot)
     899 *
     900 *  @note Doesn't lock anything.
     901 */
     902HRESULT SnapshotMachine::init(Machine *aMachine,
     903                              const settings::Hardware &hardware,
     904                              const settings::Storage &storage,
     905                              IN_GUID aSnapshotId,
     906                              const Utf8Str &aStateFilePath)
     907{
     908    LogFlowThisFuncEnter();
     909    LogFlowThisFunc(("mName={%ls}\n", aMachine->mUserData->mName.raw()));
     910
     911    AssertReturn(aMachine &&  !Guid(aSnapshotId).isEmpty(), E_INVALIDARG);
     912
     913    /* Enclose the state transition NotReady->InInit->Ready */
     914    AutoInitSpan autoInitSpan(this);
     915    AssertReturn(autoInitSpan.isOk(), E_FAIL);
     916
     917    /* Don't need to lock aMachine when VirtualBox is starting up */
     918
     919    mSnapshotId = aSnapshotId;
     920
     921    /* memorize the primary Machine instance */
     922    unconst(mPeer) = aMachine;
     923    /* share the parent pointer */
     924    unconst(mParent) = mPeer->mParent;
     925
     926    /* take the pointer to Data to share */
     927    mData.share (mPeer->mData);
     928    /*
     929     *  take the pointer to UserData to share
     930     *  (our UserData must always be the same as Machine's data)
     931     */
     932    mUserData.share (mPeer->mUserData);
     933    /* allocate private copies of all other data (will be loaded from settings) */
     934    mHWData.allocate();
     935    mMediaData.allocate();
     936    mStorageControllers.allocate();
     937
     938    /* SSData is always unique for SnapshotMachine */
     939    mSSData.allocate();
     940    mSSData->mStateFilePath = aStateFilePath;
     941
     942    /* create all other child objects that will be immutable private copies */
     943
     944    unconst(mBIOSSettings).createObject();
     945    mBIOSSettings->init (this);
     946
     947#ifdef VBOX_WITH_VRDP
     948    unconst(mVRDPServer).createObject();
     949    mVRDPServer->init (this);
     950#endif
     951
     952    unconst(mAudioAdapter).createObject();
     953    mAudioAdapter->init (this);
     954
     955    unconst(mUSBController).createObject();
     956    mUSBController->init (this);
     957
     958    for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
     959    {
     960        unconst(mNetworkAdapters [slot]).createObject();
     961        mNetworkAdapters [slot]->init (this, slot);
     962    }
     963
     964    for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
     965    {
     966        unconst(mSerialPorts [slot]).createObject();
     967        mSerialPorts [slot]->init (this, slot);
     968    }
     969
     970    for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
     971    {
     972        unconst(mParallelPorts [slot]).createObject();
     973        mParallelPorts [slot]->init (this, slot);
     974    }
     975
     976    /* load hardware and harddisk settings */
     977
     978    HRESULT rc = loadHardware(hardware);
     979    if (SUCCEEDED(rc))
     980        rc = loadStorageControllers(storage, true /* aRegistered */, &mSnapshotId);
     981
     982    if (SUCCEEDED(rc))
     983        /* commit all changes made during the initialization */
     984        commit();
     985
     986    /* Confirm a successful initialization when it's the case */
     987    if (SUCCEEDED(rc))
     988        autoInitSpan.setSucceeded();
     989
     990    LogFlowThisFuncLeave();
     991    return rc;
     992}
     993
     994/**
     995 *  Uninitializes this SnapshotMachine object.
     996 */
     997void SnapshotMachine::uninit()
     998{
     999    LogFlowThisFuncEnter();
     1000
     1001    /* Enclose the state transition Ready->InUninit->NotReady */
     1002    AutoUninitSpan autoUninitSpan(this);
     1003    if (autoUninitSpan.uninitDone())
     1004        return;
     1005
     1006    uninitDataAndChildObjects();
     1007
     1008    /* free the essential data structure last */
     1009    mData.free();
     1010
     1011    unconst(mParent).setNull();
     1012    unconst(mPeer).setNull();
     1013
     1014    LogFlowThisFuncLeave();
     1015}
     1016
     1017/**
     1018 *  Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
     1019 *  with the primary Machine instance (mPeer).
     1020 */
     1021RWLockHandle *SnapshotMachine::lockHandle() const
     1022{
     1023    AssertReturn(!mPeer.isNull(), NULL);
     1024    return mPeer->lockHandle();
     1025}
     1026
     1027////////////////////////////////////////////////////////////////////////////////
     1028//
     1029// SnapshotMachine public internal methods
     1030//
     1031////////////////////////////////////////////////////////////////////////////////
     1032
     1033/**
     1034 *  Called by the snapshot object associated with this SnapshotMachine when
     1035 *  snapshot data such as name or description is changed.
     1036 *
     1037 *  @note Locks this object for writing.
     1038 */
     1039HRESULT SnapshotMachine::onSnapshotChange (Snapshot *aSnapshot)
     1040{
     1041    AutoWriteLock alock(this);
     1042
     1043    //     mPeer->saveAllSnapshots();  @todo
     1044
     1045    /* inform callbacks */
     1046    mParent->onSnapshotChange(mData->mUuid, aSnapshot->getId());
     1047
     1048    return S_OK;
     1049}
     1050
     1051////////////////////////////////////////////////////////////////////////////////
     1052//
     1053// SessionMachine task records
     1054//
     1055////////////////////////////////////////////////////////////////////////////////
     1056
     1057/** Task structure for asynchronous VM operations */
     1058struct SessionMachine::Task
     1059{
     1060    Task (SessionMachine *m, Progress *p)
     1061        : machine (m), progress (p)
     1062        , state (m->mData->mMachineState) // save the current machine state
     1063        , subTask (false)
     1064    {}
     1065
     1066    void modifyLastState (MachineState_T s)
     1067    {
     1068        *const_cast <MachineState_T *> (&state) = s;
     1069    }
     1070
     1071    virtual void handler() = 0;
     1072
     1073    ComObjPtr<SessionMachine> machine;
     1074    ComObjPtr<Progress> progress;
     1075    const MachineState_T state;
     1076
     1077    bool subTask : 1;
     1078};
     1079
     1080/** Discard snapshot task */
     1081struct SessionMachine::DeleteSnapshotTask
     1082    : public SessionMachine::Task
     1083{
     1084    DeleteSnapshotTask(SessionMachine *m, Progress *p, Snapshot *s)
     1085        : Task(m, p),
     1086          snapshot(s)
     1087    {}
     1088
     1089    DeleteSnapshotTask (const Task &task, Snapshot *s)
     1090        : Task(task)
     1091        , snapshot(s)
     1092    {}
     1093
     1094    void handler()
     1095    {
     1096        machine->deleteSnapshotHandler(*this);
     1097    }
     1098
     1099    ComObjPtr<Snapshot> snapshot;
     1100};
     1101
     1102/** Restore snapshot state task */
     1103struct SessionMachine::RestoreSnapshotTask
     1104    : public SessionMachine::Task
     1105{
     1106    RestoreSnapshotTask(SessionMachine *m,
     1107                        ComObjPtr<Snapshot> &aSnapshot,
     1108                        Progress *p,
     1109                        ULONG ulStateFileSizeMB)
     1110        : Task(m, p),
     1111          m_pSnapshot(aSnapshot),
     1112          m_ulStateFileSizeMB(ulStateFileSizeMB)
     1113    {}
     1114
     1115    void handler()
     1116    {
     1117        machine->restoreSnapshotHandler(*this);
     1118    }
     1119
     1120    ComObjPtr<Snapshot> m_pSnapshot;
     1121    ULONG m_ulStateFileSizeMB;
     1122};
     1123
     1124////////////////////////////////////////////////////////////////////////////////
     1125//
     1126// SessionMachine public internal methods
     1127//
     1128////////////////////////////////////////////////////////////////////////////////
     1129
     1130/**
     1131 *  @note Locks mParent + this object for writing.
     1132 */
     1133STDMETHODIMP SessionMachine::BeginTakingSnapshot(IConsole *aInitiator,
     1134                                                 IN_BSTR aName,
     1135                                                 IN_BSTR aDescription,
     1136                                                 IProgress *aConsoleProgress,
     1137                                                 BOOL fTakingSnapshotOnline,
     1138                                                 BSTR *aStateFilePath)
     1139{
     1140    LogFlowThisFuncEnter();
     1141
     1142    AssertReturn(aInitiator && aName, E_INVALIDARG);
     1143    AssertReturn(aStateFilePath, E_POINTER);
     1144
     1145    LogFlowThisFunc(("aName='%ls'\n", aName));
     1146
     1147    AutoCaller autoCaller(this);
     1148    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
     1149
     1150    /* saveSettings() needs mParent lock */
     1151    AutoMultiWriteLock2 alock(mParent, this);
     1152
     1153    AssertReturn(    !Global::IsOnlineOrTransient(mData->mMachineState)
     1154                  || mData->mMachineState == MachineState_Running
     1155                  || mData->mMachineState == MachineState_Paused, E_FAIL);
     1156    AssertReturn(mSnapshotData.mLastState == MachineState_Null, E_FAIL);
     1157    AssertReturn(mSnapshotData.mSnapshot.isNull(), E_FAIL);
     1158
     1159    if (    !fTakingSnapshotOnline
     1160         && mData->mMachineState != MachineState_Saved
     1161       )
     1162    {
     1163        /* save all current settings to ensure current changes are committed and
     1164         * hard disks are fixed up */
     1165        HRESULT rc = saveSettings();
     1166        CheckComRCReturnRC(rc);
     1167    }
     1168
     1169    /* create an ID for the snapshot */
     1170    Guid snapshotId;
     1171    snapshotId.create();
     1172
     1173    Utf8Str strStateFilePath;
     1174    /* stateFilePath is null when the machine is not online nor saved */
     1175    if (    fTakingSnapshotOnline
     1176         || mData->mMachineState == MachineState_Saved)
     1177    {
     1178        strStateFilePath = Utf8StrFmt("%ls%c{%RTuuid}.sav",
     1179                                      mUserData->mSnapshotFolderFull.raw(),
     1180                                      RTPATH_DELIMITER,
     1181                                      snapshotId.ptr());
     1182        /* ensure the directory for the saved state file exists */
     1183        HRESULT rc = VirtualBox::ensureFilePathExists(strStateFilePath);
     1184        CheckComRCReturnRC(rc);
     1185    }
     1186
     1187    /* create a snapshot machine object */
     1188    ComObjPtr<SnapshotMachine> snapshotMachine;
     1189    snapshotMachine.createObject();
     1190    HRESULT rc = snapshotMachine->init(this, snapshotId, strStateFilePath);
     1191    AssertComRCReturn(rc, rc);
     1192
     1193    /* create a snapshot object */
     1194    RTTIMESPEC time;
     1195    ComObjPtr<Snapshot> pSnapshot;
     1196    pSnapshot.createObject();
     1197    rc = pSnapshot->init(mParent,
     1198                         snapshotId,
     1199                         aName,
     1200                         aDescription,
     1201                         *RTTimeNow(&time),
     1202                         snapshotMachine,
     1203                         mData->mCurrentSnapshot);
     1204    AssertComRCReturnRC(rc);
     1205
     1206    /* fill in the snapshot data */
     1207    mSnapshotData.mLastState = mData->mMachineState;
     1208    mSnapshotData.mSnapshot = pSnapshot;
     1209
     1210    try
     1211    {
     1212        LogFlowThisFunc(("Creating differencing hard disks (online=%d)...\n",
     1213                        fTakingSnapshotOnline));
     1214
     1215        // backup the media data so we can recover if things goes wrong along the day;
     1216        // the matching commit() is in fixupMedia() during endSnapshot()
     1217        mMediaData.backup();
     1218
     1219        /* set the state to Saving (this is expected by Console::TakeSnapshot()) */
     1220        setMachineState(MachineState_Saving);
     1221
     1222        /* create new differencing hard disks and attach them to this machine */
     1223        rc = createImplicitDiffs(mUserData->mSnapshotFolderFull,
     1224                                 aConsoleProgress,
     1225                                 1,            // operation weight; must be the same as in Console::TakeSnapshot()
     1226                                 !!fTakingSnapshotOnline);
     1227
     1228        if (SUCCEEDED(rc) && mSnapshotData.mLastState == MachineState_Saved)
     1229        {
     1230            Utf8Str stateFrom = mSSData->mStateFilePath;
     1231            Utf8Str stateTo = mSnapshotData.mSnapshot->stateFilePath();
     1232
     1233            LogFlowThisFunc(("Copying the execution state from '%s' to '%s'...\n",
     1234                            stateFrom.raw(), stateTo.raw()));
     1235
     1236            aConsoleProgress->SetNextOperation(Bstr(tr("Copying the execution state")),
     1237                                               1);        // weight
     1238
     1239            /* Leave the lock before a lengthy operation (mMachineState is
     1240            * MachineState_Saving here) */
     1241            alock.leave();
     1242
     1243            /* copy the state file */
     1244            int vrc = RTFileCopyEx(stateFrom.c_str(),
     1245                                   stateTo.c_str(),
     1246                                   0,
     1247                                   progressCallback,
     1248                                   aConsoleProgress);
     1249            alock.enter();
     1250
     1251            if (RT_FAILURE(vrc))
     1252                throw setError(E_FAIL,
     1253                               tr("Could not copy the state file '%s' to '%s' (%Rrc)"),
     1254                               stateFrom.raw(),
     1255                               stateTo.raw(),
     1256                               vrc);
     1257        }
     1258    }
     1259    catch (HRESULT hrc)
     1260    {
     1261        pSnapshot->uninit();
     1262        pSnapshot.setNull();
     1263        rc = hrc;
     1264    }
     1265
     1266    if (fTakingSnapshotOnline)
     1267        strStateFilePath.cloneTo(aStateFilePath);
     1268    else
     1269        *aStateFilePath = NULL;
     1270
     1271    LogFlowThisFuncLeave();
     1272    return rc;
     1273}
     1274
     1275/**
     1276 * @note Locks this object for writing.
     1277 */
     1278STDMETHODIMP SessionMachine::EndTakingSnapshot(BOOL aSuccess)
     1279{
     1280    LogFlowThisFunc(("\n"));
     1281
     1282    AutoCaller autoCaller(this);
     1283    AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
     1284
     1285    AutoWriteLock alock(this);
     1286
     1287    AssertReturn(!aSuccess ||
     1288                  (mData->mMachineState == MachineState_Saving &&
     1289                   mSnapshotData.mLastState != MachineState_Null &&
     1290                   !mSnapshotData.mSnapshot.isNull()),
     1291                  E_FAIL);
     1292
     1293    /*
     1294     * Restore the state we had when BeginTakingSnapshot() was called,
     1295     * Console::fntTakeSnapshotWorker restores its local copy when we return.
     1296     * If the state was Running, then let Console::fntTakeSnapshotWorker it
     1297     * all via Console::Resume().
     1298     */
     1299    if (   mData->mMachineState != mSnapshotData.mLastState
     1300        && mSnapshotData.mLastState != MachineState_Running)
     1301        setMachineState(mSnapshotData.mLastState);
     1302
     1303    return endTakingSnapshot(aSuccess);
     1304}
     1305
     1306/**
     1307 *  @note Locks mParent + this + children objects for writing!
     1308 */
     1309STDMETHODIMP SessionMachine::DeleteSnapshot(IConsole *aInitiator,
     1310                                            IN_BSTR aId,
     1311                                            MachineState_T *aMachineState,
     1312                                            IProgress **aProgress)
     1313{
     1314    LogFlowThisFuncEnter();
     1315
     1316    Guid id(aId);
     1317    AssertReturn(aInitiator && !id.isEmpty(), E_INVALIDARG);
     1318    AssertReturn(aMachineState && aProgress, E_POINTER);
     1319
     1320    AutoCaller autoCaller(this);
     1321    AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
     1322
     1323    /* saveSettings() needs mParent lock */
     1324    AutoMultiWriteLock2 alock(mParent, this);
     1325
     1326    ComAssertRet (!Global::IsOnlineOrTransient (mData->mMachineState), E_FAIL);
     1327
     1328    AutoWriteLock treeLock(snapshotsTreeLockHandle());
     1329
     1330    ComObjPtr<Snapshot> snapshot;
     1331    HRESULT rc = findSnapshot(id, snapshot, true /* aSetError */);
     1332    CheckComRCReturnRC(rc);
     1333
     1334    AutoWriteLock snapshotLock(snapshot);
     1335
     1336    size_t childrenCount = snapshot->getChildrenCount();
     1337    if (childrenCount > 1)
     1338        return setError(VBOX_E_INVALID_OBJECT_STATE,
     1339                        tr("Snapshot '%s' of the machine '%ls' cannot be deleted because it has has more than one child snapshot (%d)"),
     1340                        snapshot->getName().c_str(),
     1341                        mUserData->mName.raw(),
     1342                        childrenCount);
     1343
     1344    /* If the snapshot being discarded is the current one, ensure current
     1345     * settings are committed and saved.
     1346     */
     1347    if (snapshot == mData->mCurrentSnapshot)
     1348    {
     1349        if (isModified())
     1350        {
     1351            rc = saveSettings();
     1352            CheckComRCReturnRC(rc);
     1353        }
     1354    }
     1355
     1356    /* create a progress object. The number of operations is:
     1357     *   1 (preparing) + # of hard disks + 1 if the snapshot is online
     1358     */
     1359    ComObjPtr<Progress> progress;
     1360    progress.createObject();
     1361    rc = progress->init(mParent, aInitiator,
     1362                        BstrFmt(tr("Discarding snapshot '%s'"),
     1363                                snapshot->getName().c_str()),
     1364                        FALSE /* aCancelable */,
     1365                        1 + (ULONG)snapshot->getSnapshotMachine()->mMediaData->mAttachments.size()
     1366                          + (snapshot->stateFilePath().length() ? 1 : 0),
     1367                        Bstr(tr("Preparing to discard snapshot")));
     1368    AssertComRCReturn(rc, rc);
     1369
     1370    /* create and start the task on a separate thread */
     1371    DeleteSnapshotTask *task = new DeleteSnapshotTask(this, progress, snapshot);
     1372    int vrc = RTThreadCreate(NULL,
     1373                             taskHandler,
     1374                             (void*)task,
     1375                             0,
     1376                             RTTHREADTYPE_MAIN_WORKER,
     1377                             0,
     1378                             "DeleteSnapshot");
     1379    if (RT_FAILURE(vrc))
     1380    {
     1381        delete task;
     1382        return E_FAIL;
     1383    }
     1384
     1385    /* set the proper machine state (note: after creating a Task instance) */
     1386    setMachineState(MachineState_DeletingSnapshot);
     1387
     1388    /* return the progress to the caller */
     1389    progress.queryInterfaceTo(aProgress);
     1390
     1391    /* return the new state to the caller */
     1392    *aMachineState = mData->mMachineState;
     1393
     1394    LogFlowThisFuncLeave();
     1395
     1396    return S_OK;
     1397}
     1398
     1399/**
     1400 *  @note Locks this + children objects for writing!
     1401 */
     1402STDMETHODIMP SessionMachine::RestoreSnapshot(IConsole *aInitiator,
     1403                                             ISnapshot *aSnapshot,
     1404                                             MachineState_T *aMachineState,
     1405                                             IProgress **aProgress)
     1406{
     1407    LogFlowThisFuncEnter();
     1408
     1409    AssertReturn(aInitiator, E_INVALIDARG);
     1410    AssertReturn(aSnapshot && aMachineState && aProgress, E_POINTER);
     1411
     1412    AutoCaller autoCaller(this);
     1413    AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
     1414
     1415    AutoWriteLock alock(this);
     1416
     1417    ComAssertRet(!Global::IsOnlineOrTransient(mData->mMachineState),
     1418                 E_FAIL);
     1419
     1420    ComObjPtr<Snapshot> pSnapshot(static_cast<Snapshot*>(aSnapshot));
     1421
     1422    /* create a progress object. The number of operations is: 1 (preparing) + #
     1423     * of hard disks + 1 (if we need to copy the saved state file) */
     1424    ComObjPtr<Progress> progress;
     1425    progress.createObject();
     1426
     1427    LogFlowThisFunc(("Going thru snapshot machine attachments to determine progress setup\n"));
     1428
     1429    ULONG ulOpCount = 1;            // one for preparations
     1430    ULONG ulTotalWeight = 1;        // one for preparations
     1431    for (MediaData::AttachmentList::iterator it = pSnapshot->getSnapshotMachine()->mMediaData->mAttachments.begin();
     1432         it != pSnapshot->getSnapshotMachine()->mMediaData->mAttachments.end();
     1433         ++it)
     1434    {
     1435        ComObjPtr<MediumAttachment> &pAttach = *it;
     1436        AutoReadLock attachLock(pAttach);
     1437        if (pAttach->type() == DeviceType_HardDisk)
     1438        {
     1439            ++ulOpCount;
     1440            ++ulTotalWeight;         // assume one MB weight for each differencing hard disk to manage
     1441            Assert(pAttach->medium());
     1442            LogFlowThisFunc(("op %d: considering hard disk attachment %s\n", ulOpCount, pAttach->medium()->name().c_str()));
     1443        }
     1444    }
     1445
     1446    ULONG ulStateFileSizeMB = 0;
     1447    if (pSnapshot->stateFilePath().length())
     1448    {
     1449        ++ulOpCount;      // one for the saved state
     1450
     1451        uint64_t ullSize;
     1452        int irc = RTFileQuerySize(pSnapshot->stateFilePath().c_str(), &ullSize);
     1453        if (!RT_SUCCESS(irc))
     1454            // if we can't access the file here, then we'll be doomed later also, so fail right away
     1455            setError(E_FAIL, tr("Cannot access state file '%s', runtime error, %Rra"), pSnapshot->stateFilePath().c_str(), irc);
     1456        if (ullSize == 0) // avoid division by zero
     1457            ullSize = _1M;
     1458
     1459        ulStateFileSizeMB = (ULONG)(ullSize / _1M);
     1460        LogFlowThisFunc(("op %d: saved state file '%s' has %RI64 bytes (%d MB)\n",
     1461                         ulOpCount, pSnapshot->stateFilePath().raw(), ullSize, ulStateFileSizeMB));
     1462
     1463        ulTotalWeight += ulStateFileSizeMB;
     1464    }
     1465
     1466    progress->init(mParent, aInitiator,
     1467                   Bstr(tr("Restoring snapshot")),
     1468                   FALSE /* aCancelable */,
     1469                   ulOpCount,
     1470                   ulTotalWeight,
     1471                   Bstr(tr("Restoring machine settings")),
     1472                   1);
     1473
     1474    /* create and start the task on a separate thread (note that it will not
     1475     * start working until we release alock) */
     1476    RestoreSnapshotTask *task = new RestoreSnapshotTask(this, pSnapshot, progress, ulStateFileSizeMB);
     1477    int vrc = RTThreadCreate(NULL,
     1478                             taskHandler,
     1479                             (void*)task,
     1480                             0,
     1481                             RTTHREADTYPE_MAIN_WORKER,
     1482                             0,
     1483                             "RestoreSnap");
     1484    if (RT_FAILURE(vrc))
     1485    {
     1486        delete task;
     1487        ComAssertRCRet(vrc, E_FAIL);
     1488    }
     1489
     1490    /* set the proper machine state (note: after creating a Task instance) */
     1491    setMachineState(MachineState_RestoringSnapshot);
     1492
     1493    /* return the progress to the caller */
     1494    progress.queryInterfaceTo(aProgress);
     1495
     1496    /* return the new state to the caller */
     1497    *aMachineState = mData->mMachineState;
     1498
     1499    LogFlowThisFuncLeave();
     1500
     1501    return S_OK;
     1502}
     1503
     1504////////////////////////////////////////////////////////////////////////////////
     1505//
     1506// SessionMachine public internal methods related to snapshots
     1507//
     1508////////////////////////////////////////////////////////////////////////////////
     1509
     1510/* static */
     1511DECLCALLBACK(int) SessionMachine::taskHandler (RTTHREAD /* thread */, void *pvUser)
     1512{
     1513    AssertReturn(pvUser, VERR_INVALID_POINTER);
     1514
     1515    Task *task = static_cast <Task *> (pvUser);
     1516    task->handler();
     1517
     1518    // it's our responsibility to delete the task
     1519    delete task;
     1520
     1521    return 0;
     1522}
     1523
     1524/**
     1525 * Helper method to finalize taking a snapshot. Gets called to finalize the
     1526 * "take snapshot" procedure, either from the public SessionMachine::EndTakingSnapshot()
     1527 * if taking the snapshot failed/was aborted or from the takeSnapshotHandler thread
     1528 * when taking the snapshot succeeded.
     1529 *
     1530 * Expected to be called after completing *all* the tasks related to taking the
     1531 * snapshot, either successfully or unsuccessfilly.
     1532 *
     1533 * @param aSuccess  TRUE if the snapshot has been taken successfully.
     1534 *
     1535 * @note Locks this objects for writing.
     1536 */
     1537HRESULT SessionMachine::endTakingSnapshot(BOOL aSuccess)
     1538{
     1539    LogFlowThisFuncEnter();
     1540
     1541    AutoCaller autoCaller(this);
     1542    AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
     1543
     1544    AutoMultiWriteLock2 alock(mParent, this);
     1545            // saveSettings needs VirtualBox lock
     1546
     1547    AssertReturn(!mSnapshotData.mSnapshot.isNull(), E_FAIL);
     1548
     1549    MultiResult rc(S_OK);
     1550
     1551    ComObjPtr<Snapshot> pOldFirstSnap = mData->mFirstSnapshot;
     1552    ComObjPtr<Snapshot> pOldCurrentSnap = mData->mCurrentSnapshot;
     1553
     1554    bool fOnline = Global::IsOnline(mSnapshotData.mLastState);
     1555
     1556    if (aSuccess)
     1557    {
     1558        // new snapshot becomes the current one
     1559        mData->mCurrentSnapshot = mSnapshotData.mSnapshot;
     1560
     1561        /* memorize the first snapshot if necessary */
     1562        if (!mData->mFirstSnapshot)
     1563            mData->mFirstSnapshot = mData->mCurrentSnapshot;
     1564
     1565        if (!fOnline)
     1566            /* the machine was powered off or saved when taking a snapshot, so
     1567             * reset the mCurrentStateModified flag */
     1568            mData->mCurrentStateModified = FALSE;
     1569
     1570        rc = saveSettings();
     1571    }
     1572
     1573    if (aSuccess && SUCCEEDED(rc))
     1574    {
     1575        /* associate old hard disks with the snapshot and do locking/unlocking*/
     1576        fixupMedia(true /* aCommit */, fOnline);
     1577
     1578        /* inform callbacks */
     1579        mParent->onSnapshotTaken(mData->mUuid,
     1580                                 mSnapshotData.mSnapshot->getId());
     1581    }
     1582    else
     1583    {
     1584        /* delete all differencing hard disks created (this will also attach
     1585         * their parents back by rolling back mMediaData) */
     1586        fixupMedia(false /* aCommit */);
     1587
     1588        mData->mFirstSnapshot = pOldFirstSnap;      // might have been changed above
     1589        mData->mCurrentSnapshot = pOldCurrentSnap;      // might have been changed above
     1590
     1591        /* delete the saved state file (it might have been already created) */
     1592        if (mSnapshotData.mSnapshot->stateFilePath().length())
     1593            RTFileDelete(mSnapshotData.mSnapshot->stateFilePath().c_str());
     1594
     1595        mSnapshotData.mSnapshot->uninit();
     1596    }
     1597
     1598    /* clear out the snapshot data */
     1599    mSnapshotData.mLastState = MachineState_Null;
     1600    mSnapshotData.mSnapshot.setNull();
     1601
     1602    LogFlowThisFuncLeave();
     1603    return rc;
     1604}
     1605
     1606/**
     1607 * Helper struct for SessionMachine::deleteSnapshotHandler().
     1608 */
     1609struct MediumDiscardRec
     1610{
     1611    MediumDiscardRec() : chain (NULL) {}
     1612
     1613    MediumDiscardRec (const ComObjPtr<Medium> &aHd,
     1614                      Medium::MergeChain *aChain = NULL)
     1615        : hd (aHd), chain (aChain) {}
     1616
     1617    MediumDiscardRec (const ComObjPtr<Medium> &aHd,
     1618                      Medium::MergeChain *aChain,
     1619                      const ComObjPtr<Medium> &aReplaceHd,
     1620                      const ComObjPtr<MediumAttachment> &aReplaceHda,
     1621                      const Guid &aSnapshotId)
     1622        : hd (aHd), chain (aChain)
     1623        , replaceHd (aReplaceHd), replaceHda (aReplaceHda)
     1624        , snapshotId (aSnapshotId) {}
     1625
     1626    ComObjPtr<Medium> hd;
     1627    Medium::MergeChain *chain;
     1628    /* these are for the replace hard disk case: */
     1629    ComObjPtr<Medium> replaceHd;
     1630    ComObjPtr<MediumAttachment> replaceHda;
     1631    Guid snapshotId;
     1632};
     1633
     1634typedef std::list <MediumDiscardRec> MediumDiscardRecList;
     1635
     1636/**
     1637 * Discard snapshot task handler. Must be called only by
     1638 * DeleteSnapshotTask::handler()!
     1639 *
     1640 * When aTask.subTask is true, the associated progress object is left
     1641 * uncompleted on success. On failure, the progress is marked as completed
     1642 * regardless of this parameter.
     1643 *
     1644 * @note Locks mParent + this + child objects for writing!
     1645 */
     1646void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask)
     1647{
     1648    LogFlowThisFuncEnter();
     1649
     1650    AutoCaller autoCaller(this);
     1651
     1652    LogFlowThisFunc(("state=%d\n", autoCaller.state()));
     1653    if (!autoCaller.isOk())
     1654    {
     1655        /* we might have been uninitialized because the session was accidentally
     1656         * closed by the client, so don't assert */
     1657        aTask.progress->notifyComplete(E_FAIL,
     1658                                       COM_IIDOF(IMachine),
     1659                                       getComponentName(),
     1660                                       tr("The session has been accidentally closed"));
     1661        LogFlowThisFuncLeave();
     1662        return;
     1663    }
     1664
     1665    /* Locking order:  */
     1666    AutoMultiWriteLock3 alock(this->lockHandle(),
     1667                              this->snapshotsTreeLockHandle(),
     1668                              aTask.snapshot->lockHandle());
     1669
     1670    ComPtr<SnapshotMachine> sm = aTask.snapshot->getSnapshotMachine();
     1671    /* no need to lock the snapshot machine since it is const by definiton */
     1672
     1673    HRESULT rc = S_OK;
     1674
     1675    /* save the snapshot ID (for callbacks) */
     1676    Guid snapshotId = aTask.snapshot->getId();
     1677
     1678    MediumDiscardRecList toDiscard;
     1679
     1680    bool settingsChanged = false;
     1681
     1682    try
     1683    {
     1684        /* first pass: */
     1685        LogFlowThisFunc(("1: Checking hard disk merge prerequisites...\n"));
     1686
     1687        for (MediaData::AttachmentList::const_iterator it = sm->mMediaData->mAttachments.begin();
     1688             it != sm->mMediaData->mAttachments.end();
     1689             ++it)
     1690        {
     1691            ComObjPtr<MediumAttachment> hda = *it;
     1692            ComObjPtr<Medium> hd = hda->medium();
     1693
     1694            // medium can be NULL only for non-hard-disk types
     1695            Assert(    !hd.isNull()
     1696                    || hda->type() != DeviceType_HardDisk);
     1697            if (hd.isNull())
     1698                continue;
     1699
     1700            /* Medium::prepareDiscard() reqiuires a write lock */
     1701            AutoWriteLock hdLock(hd);
     1702
     1703            if (hd->type() != MediumType_Normal)
     1704            {
     1705                /* skip writethrough hard disks */
     1706                Assert(hd->type() == MediumType_Writethrough);
     1707                rc = aTask.progress->SetNextOperation(BstrFmt(tr("Skipping writethrough hard disk '%s'"),
     1708                                                              hd->base()->name().raw()),
     1709                                                      1); // weight
     1710                CheckComRCThrowRC(rc);
     1711                continue;
     1712            }
     1713
     1714            Medium::MergeChain *chain = NULL;
     1715
     1716            /* needs to be discarded (merged with the child if any), check
     1717             * prerequisites */
     1718            rc = hd->prepareDiscard(chain);
     1719            CheckComRCThrowRC(rc);
     1720
     1721            if (hd->parent().isNull() && chain != NULL)
     1722            {
     1723                /* it's a base hard disk so it will be a backward merge of its
     1724                 * only child to it (prepareDiscard() does necessary checks). We
     1725                 * need then to update the attachment that refers to the child
     1726                 * to refer to the parent instead. Don't forget to detach the
     1727                 * child (otherwise mergeTo() called by discard() will assert
     1728                 * because it will be going to delete the child) */
     1729
     1730                /* The below assert would be nice but I don't want to move
     1731                 * Medium::MergeChain to the header just for that
     1732                 * Assert (!chain->isForward()); */
     1733
     1734                Assert(hd->children().size() == 1);
     1735
     1736                ComObjPtr<Medium> replaceHd = hd->children().front();
     1737
     1738                const Guid *pReplaceMachineId = replaceHd->getFirstMachineBackrefId();
     1739                Assert(pReplaceMachineId  && *pReplaceMachineId == mData->mUuid);
     1740
     1741                Guid snapshotId;
     1742                const Guid *pSnapshotId = replaceHd->getFirstMachineBackrefSnapshotId();
     1743                if (pSnapshotId)
     1744                    snapshotId = *pSnapshotId;
     1745
     1746                HRESULT rc2 = S_OK;
     1747
     1748                /* adjust back references */
     1749                rc2 = replaceHd->detachFrom (mData->mUuid, snapshotId);
     1750                AssertComRC(rc2);
     1751
     1752                rc2 = hd->attachTo (mData->mUuid, snapshotId);
     1753                AssertComRC(rc2);
     1754
     1755                /* replace the hard disk in the attachment object */
     1756                if (snapshotId.isEmpty())
     1757                {
     1758                    /* in current state */
     1759                    AssertBreak(hda = findAttachment(mMediaData->mAttachments, replaceHd));
     1760                }
     1761                else
     1762                {
     1763                    /* in snapshot */
     1764                    ComObjPtr<Snapshot> snapshot;
     1765                    rc2 = findSnapshot(snapshotId, snapshot);
     1766                    AssertComRC(rc2);
     1767
     1768                    /* don't lock the snapshot; cannot be modified outside */
     1769                    MediaData::AttachmentList &snapAtts = snapshot->getSnapshotMachine()->mMediaData->mAttachments;
     1770                    AssertBreak(hda = findAttachment(snapAtts, replaceHd));
     1771                }
     1772
     1773                AutoWriteLock attLock(hda);
     1774                hda->updateMedium(hd, false /* aImplicit */);
     1775
     1776                toDiscard.push_back(MediumDiscardRec(hd,
     1777                                                     chain,
     1778                                                     replaceHd,
     1779                                                     hda,
     1780                                                     snapshotId));
     1781                continue;
     1782            }
     1783
     1784            toDiscard.push_back(MediumDiscardRec(hd, chain));
     1785        }
     1786
     1787        /* Now we checked that we can successfully merge all normal hard disks
     1788         * (unless a runtime error like end-of-disc happens). Prior to
     1789         * performing the actual merge, we want to discard the snapshot itself
     1790         * and remove it from the XML file to make sure that a possible merge
     1791         * ruintime error will not make this snapshot inconsistent because of
     1792         * the partially merged or corrupted hard disks */
     1793
     1794        /* second pass: */
     1795        LogFlowThisFunc(("2: Discarding snapshot...\n"));
     1796
     1797        {
     1798            ComObjPtr<Snapshot> parentSnapshot = aTask.snapshot->parent();
     1799            Bstr stateFilePath = aTask.snapshot->stateFilePath();
     1800
     1801            /* Note that discarding the snapshot will deassociate it from the
     1802             * hard disks which will allow the merge+delete operation for them*/
     1803            aTask.snapshot->beginDiscard();
     1804            aTask.snapshot->uninit();
     1805
     1806            rc = saveAllSnapshots();
     1807            CheckComRCThrowRC(rc);
     1808
     1809            /// @todo (dmik)
     1810            //  if we implement some warning mechanism later, we'll have
     1811            //  to return a warning if the state file path cannot be deleted
     1812            if (stateFilePath)
     1813            {
     1814                aTask.progress->SetNextOperation(Bstr(tr("Discarding the execution state")),
     1815                                                 1);        // weight
     1816
     1817                RTFileDelete(Utf8Str(stateFilePath).c_str());
     1818            }
     1819
     1820            /// @todo NEWMEDIA to provide a good level of fauilt tolerance, we
     1821            /// should restore the shapshot in the snapshot tree if
     1822            /// saveSnapshotSettings fails. Actually, we may call
     1823            /// #saveSnapshotSettings() with a special flag that will tell it to
     1824            /// skip the given snapshot as if it would have been discarded and
     1825            /// only actually discard it if the save operation succeeds.
     1826        }
     1827
     1828        /* here we come when we've irrevesibly discarded the snapshot which
     1829         * means that the VM settigns (our relevant changes to mData) need to be
     1830         * saved too */
     1831        /// @todo NEWMEDIA maybe save everything in one operation in place of
     1832        ///  saveSnapshotSettings() above
     1833        settingsChanged = true;
     1834
     1835        /* third pass: */
     1836        LogFlowThisFunc(("3: Performing actual hard disk merging...\n"));
     1837
     1838        /* leave the locks before the potentially lengthy operation */
     1839        alock.leave();
     1840
     1841        /// @todo NEWMEDIA turn the following errors into warnings because the
     1842        /// snapshot itself has been already deleted (and interpret these
     1843        /// warnings properly on the GUI side)
     1844
     1845        for (MediumDiscardRecList::iterator it = toDiscard.begin();
     1846             it != toDiscard.end();)
     1847        {
     1848            rc = it->hd->discard (aTask.progress, it->chain);
     1849            CheckComRCBreakRC(rc);
     1850
     1851            /* prevent from calling cancelDiscard() */
     1852            it = toDiscard.erase (it);
     1853        }
     1854
     1855        alock.enter();
     1856
     1857        CheckComRCThrowRC(rc);
     1858    }
     1859    catch (HRESULT aRC) { rc = aRC; }
     1860
     1861    if (FAILED(rc))
     1862    {
     1863        HRESULT rc2 = S_OK;
     1864
     1865        /* un-prepare the remaining hard disks */
     1866        for (MediumDiscardRecList::const_iterator it = toDiscard.begin();
     1867             it != toDiscard.end(); ++it)
     1868        {
     1869            it->hd->cancelDiscard (it->chain);
     1870
     1871            if (!it->replaceHd.isNull())
     1872            {
     1873                /* undo hard disk replacement */
     1874
     1875                rc2 = it->replaceHd->attachTo (mData->mUuid, it->snapshotId);
     1876                AssertComRC(rc2);
     1877
     1878                rc2 = it->hd->detachFrom (mData->mUuid, it->snapshotId);
     1879                AssertComRC(rc2);
     1880
     1881                AutoWriteLock attLock (it->replaceHda);
     1882                it->replaceHda->updateMedium(it->replaceHd, false /* aImplicit */);
     1883            }
     1884        }
     1885    }
     1886
     1887    if (!aTask.subTask || FAILED(rc))
     1888    {
     1889        if (!aTask.subTask)
     1890        {
     1891            /* saveSettings() below needs a VirtualBox write lock and we need to
     1892             * leave this object's lock to do this to follow the {parent-child}
     1893             * locking rule. This is the last chance to do that while we are
     1894             * still in a protective state which allows us to temporarily leave
     1895             * the lock */
     1896            alock.unlock();
     1897            AutoWriteLock vboxLock(mParent);
     1898            alock.lock();
     1899
     1900            /* preserve existing error info */
     1901            ErrorInfoKeeper eik;
     1902
     1903            /* restore the machine state */
     1904            setMachineState(aTask.state);
     1905            updateMachineStateOnClient();
     1906
     1907            if (settingsChanged)
     1908                saveSettings(SaveS_InformCallbacksAnyway);
     1909        }
     1910
     1911        /* set the result (this will try to fetch current error info on failure) */
     1912        aTask.progress->notifyComplete (rc);
     1913    }
     1914
     1915    if (SUCCEEDED(rc))
     1916        mParent->onSnapshotDiscarded (mData->mUuid, snapshotId);
     1917
     1918    LogFlowThisFunc(("Done discarding snapshot (rc=%08X)\n", rc));
     1919    LogFlowThisFuncLeave();
     1920}
     1921
     1922/**
     1923 * Restore snapshot state task handler. Must be called only by
     1924 * RestoreSnapshotTask::handler()!
     1925 *
     1926 * @note Locks mParent + this object for writing.
     1927 */
     1928void SessionMachine::restoreSnapshotHandler(RestoreSnapshotTask &aTask)
     1929{
     1930    LogFlowThisFuncEnter();
     1931
     1932    AutoCaller autoCaller(this);
     1933
     1934    LogFlowThisFunc(("state=%d\n", autoCaller.state()));
     1935    if (!autoCaller.isOk())
     1936    {
     1937        /* we might have been uninitialized because the session was accidentally
     1938         * closed by the client, so don't assert */
     1939        aTask.progress->notifyComplete(E_FAIL,
     1940                                       COM_IIDOF(IMachine),
     1941                                       getComponentName(),
     1942                                       tr("The session has been accidentally closed"));
     1943
     1944        LogFlowThisFuncLeave();
     1945        return;
     1946    }
     1947
     1948    /* saveSettings() needs mParent lock */
     1949    AutoWriteLock vboxLock(mParent);
     1950
     1951    /* @todo We don't need mParent lock so far so unlock() it. Better is to
     1952     * provide an AutoWriteLock argument that lets create a non-locking
     1953     * instance */
     1954    vboxLock.unlock();
     1955
     1956    AutoWriteLock alock(this);
     1957
     1958    /* discard all current changes to mUserData (name, OSType etc.) (note that
     1959     * the machine is powered off, so there is no need to inform the direct
     1960     * session) */
     1961    if (isModified())
     1962        rollback(false /* aNotify */);
     1963
     1964    HRESULT rc = S_OK;
     1965
     1966    bool stateRestored = false;
     1967
     1968    try
     1969    {
     1970        /* discard the saved state file if the machine was Saved prior to this
     1971         * operation */
     1972        if (aTask.state == MachineState_Saved)
     1973        {
     1974            Assert(!mSSData->mStateFilePath.isEmpty());
     1975            RTFileDelete(mSSData->mStateFilePath.c_str());
     1976            mSSData->mStateFilePath.setNull();
     1977            aTask.modifyLastState(MachineState_PoweredOff);
     1978            rc = saveStateSettings(SaveSTS_StateFilePath);
     1979            CheckComRCThrowRC(rc);
     1980        }
     1981
     1982        RTTIMESPEC snapshotTimeStamp;
     1983        RTTimeSpecSetMilli(&snapshotTimeStamp, 0);
     1984
     1985        {
     1986            AutoReadLock snapshotLock(aTask.m_pSnapshot);
     1987
     1988            /* remember the timestamp of the snapshot we're restoring from */
     1989            snapshotTimeStamp = aTask.m_pSnapshot->getTimeStamp();
     1990
     1991            ComPtr<SnapshotMachine> pSnapshotMachine(aTask.m_pSnapshot->getSnapshotMachine());
     1992
     1993            /* copy all hardware data from the snapshot */
     1994            copyFrom(pSnapshotMachine);
     1995
     1996            LogFlowThisFunc(("Restoring hard disks from the snapshot...\n"));
     1997
     1998            /* restore the attachments from the snapshot */
     1999            mMediaData.backup();
     2000            mMediaData->mAttachments = pSnapshotMachine->mMediaData->mAttachments;
     2001
     2002            /* leave the locks before the potentially lengthy operation */
     2003            snapshotLock.unlock();
     2004            alock.leave();
     2005
     2006            rc = createImplicitDiffs(mUserData->mSnapshotFolderFull,
     2007                                     aTask.progress,
     2008                                     1,
     2009                                     false /* aOnline */);
     2010
     2011            alock.enter();
     2012            snapshotLock.lock();
     2013
     2014            CheckComRCThrowRC(rc);
     2015
     2016            /* Note: on success, current (old) hard disks will be
     2017             * deassociated/deleted on #commit() called from #saveSettings() at
     2018             * the end. On failure, newly created implicit diffs will be
     2019             * deleted by #rollback() at the end. */
     2020
     2021            /* should not have a saved state file associated at this point */
     2022            Assert(mSSData->mStateFilePath.isEmpty());
     2023
     2024            if (!aTask.m_pSnapshot->stateFilePath().isEmpty())
     2025            {
     2026                Utf8Str snapStateFilePath = aTask.m_pSnapshot->stateFilePath();
     2027
     2028                Utf8Str stateFilePath = Utf8StrFmt("%ls%c{%RTuuid}.sav",
     2029                                                   mUserData->mSnapshotFolderFull.raw(),
     2030                                                   RTPATH_DELIMITER,
     2031                                                   mData->mUuid.raw());
     2032
     2033                LogFlowThisFunc(("Copying saved state file from '%s' to '%s'...\n",
     2034                                  snapStateFilePath.raw(), stateFilePath.raw()));
     2035
     2036                aTask.progress->SetNextOperation(Bstr(tr("Restoring the execution state")),
     2037                                                 aTask.m_ulStateFileSizeMB);        // weight
     2038
     2039                /* leave the lock before the potentially lengthy operation */
     2040                snapshotLock.unlock();
     2041                alock.leave();
     2042
     2043                /* copy the state file */
     2044                int vrc = RTFileCopyEx(snapStateFilePath.c_str(),
     2045                                       stateFilePath.c_str(),
     2046                                       0,
     2047                                       progressCallback,
     2048                                       aTask.progress);
     2049
     2050                alock.enter();
     2051                snapshotLock.lock();
     2052
     2053                if (RT_SUCCESS(vrc))
     2054                    mSSData->mStateFilePath = stateFilePath;
     2055                else
     2056                    throw setError(E_FAIL,
     2057                                   tr("Could not copy the state file '%s' to '%s' (%Rrc)"),
     2058                                   snapStateFilePath.raw(),
     2059                                   stateFilePath.raw(),
     2060                                   vrc);
     2061            }
     2062
     2063            LogFlowThisFunc(("Setting new current snapshot {%RTuuid}\n", aTask.m_pSnapshot->getId().raw()));
     2064            /* make the snapshot we restored from the current snapshot */
     2065            mData->mCurrentSnapshot = aTask.m_pSnapshot;
     2066        }
     2067
     2068        /* grab differencing hard disks from the old attachments that will
     2069         * become unused and need to be auto-deleted */
     2070
     2071        std::list< ComObjPtr<MediumAttachment> > llDiffAttachmentsToDelete;
     2072
     2073        for (MediaData::AttachmentList::const_iterator it = mMediaData.backedUpData()->mAttachments.begin();
     2074             it != mMediaData.backedUpData()->mAttachments.end();
     2075             ++it)
     2076        {
     2077            ComObjPtr<MediumAttachment> pAttach = *it;
     2078            ComObjPtr<Medium> pMedium = pAttach->medium();
     2079
     2080            /* while the hard disk is attached, the number of children or the
     2081             * parent cannot change, so no lock */
     2082            if (    !pMedium.isNull()
     2083                 && pAttach->type() == DeviceType_HardDisk
     2084                 && !pMedium->parent().isNull()
     2085                 && pMedium->children().size() == 0
     2086               )
     2087            {
     2088                LogFlowThisFunc(("Picked differencing image '%s' for deletion\n", pMedium->name().raw()));
     2089
     2090                llDiffAttachmentsToDelete.push_back(pAttach);
     2091            }
     2092        }
     2093
     2094        int saveFlags = 0;
     2095
     2096        /* @todo saveSettings() below needs a VirtualBox write lock and we need
     2097         * to leave this object's lock to do this to follow the {parent-child}
     2098         * locking rule. This is the last chance to do that while we are still
     2099         * in a protective state which allows us to temporarily leave the lock*/
     2100        alock.unlock();
     2101        vboxLock.lock();
     2102        alock.lock();
     2103
     2104        /* we have already discarded the current state, so set the execution
     2105         * state accordingly no matter of the discard snapshot result */
     2106        if (!mSSData->mStateFilePath.isEmpty())
     2107            setMachineState(MachineState_Saved);
     2108        else
     2109            setMachineState(MachineState_PoweredOff);
     2110
     2111        updateMachineStateOnClient();
     2112        stateRestored = true;
     2113
     2114        /* assign the timestamp from the snapshot */
     2115        Assert(RTTimeSpecGetMilli (&snapshotTimeStamp) != 0);
     2116        mData->mLastStateChange = snapshotTimeStamp;
     2117
     2118        // detach the current-state diffs that we detected above and build a list of
     2119        // images to delete _after_ saveSettings()
     2120
     2121        std::list< ComObjPtr<Medium> > llDiffsToDelete;
     2122
     2123        for (std::list< ComObjPtr<MediumAttachment> >::iterator it = llDiffAttachmentsToDelete.begin();
     2124             it != llDiffAttachmentsToDelete.end();
     2125             ++it)
     2126        {
     2127            ComObjPtr<MediumAttachment> pAttach = *it;        // guaranteed to have only attachments where medium != NULL
     2128            ComObjPtr<Medium> pMedium = pAttach->medium();
     2129
     2130            AutoWriteLock mlock(pMedium);
     2131
     2132            LogFlowThisFunc(("Detaching old current state in differencing image '%s'\n", pMedium->name().raw()));
     2133
     2134            mMediaData->mAttachments.remove(pAttach);
     2135            pMedium->detachFrom(mData->mUuid);
     2136
     2137            llDiffsToDelete.push_back(pMedium);
     2138        }
     2139
     2140        // save all settings, reset the modified flag and commit;
     2141        rc = saveSettings(SaveS_ResetCurStateModified | saveFlags);
     2142        CheckComRCThrowRC(rc);
     2143        // from here on we cannot roll back on failure any more
     2144
     2145        for (std::list< ComObjPtr<Medium> >::iterator it = llDiffsToDelete.begin();
     2146             it != llDiffsToDelete.end();
     2147             ++it)
     2148        {
     2149            ComObjPtr<Medium> &pMedium = *it;
     2150            LogFlowThisFunc(("Deleting old current state in differencing image '%s'\n", pMedium->name().raw()));
     2151
     2152            HRESULT rc2 = pMedium->deleteStorageAndWait();
     2153            // ignore errors here because we cannot roll back after saveSettings() above
     2154            if (SUCCEEDED(rc2))
     2155                pMedium->uninit();
     2156        }
     2157    }
     2158    catch (HRESULT aRC)
     2159    {
     2160        rc = aRC;
     2161    }
     2162
     2163    if (FAILED(rc))
     2164    {
     2165        /* preserve existing error info */
     2166        ErrorInfoKeeper eik;
     2167
     2168        /* undo all changes on failure */
     2169        rollback(false /* aNotify */);
     2170
     2171        if (!stateRestored)
     2172        {
     2173            /* restore the machine state */
     2174            setMachineState(aTask.state);
     2175            updateMachineStateOnClient();
     2176        }
     2177    }
     2178
     2179    /* set the result (this will try to fetch current error info on failure) */
     2180    aTask.progress->notifyComplete(rc);
     2181
     2182    if (SUCCEEDED(rc))
     2183        mParent->onSnapshotDiscarded(mData->mUuid, Guid());
     2184
     2185    LogFlowThisFunc(("Done restoring snapshot (rc=%08X)\n", rc));
     2186
     2187    LogFlowThisFuncLeave();
     2188}
     2189
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