Changeset 24279 in vbox
- Timestamp:
- Nov 2, 2009 9:51:36 PM (15 years ago)
- Location:
- trunk/src/VBox/Main
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/MachineImpl.cpp
r24277 r24279 91 91 // defines / prototypes 92 92 ///////////////////////////////////////////////////////////////////////////// 93 94 // globals95 /////////////////////////////////////////////////////////////////////////////96 97 /**98 * Progress callback handler for lengthy operations99 * (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 }114 93 115 94 ///////////////////////////////////////////////////////////////////////////// … … 7701 7680 7702 7681 7703 /////////////////////////////////////////////////////////////////////////////7704 // SessionMachine class7705 /////////////////////////////////////////////////////////////////////////////7706 7707 /** Task structure for asynchronous VM operations */7708 struct SessionMachine::Task7709 {7710 Task (SessionMachine *m, Progress *p)7711 : machine (m), progress (p)7712 , state (m->mData->mMachineState) // save the current machine state7713 , 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::DeleteSnapshotTask7732 : public SessionMachine::Task7733 {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::RestoreSnapshotTask7754 : public SessionMachine::Task7755 {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 7774 7682 //////////////////////////////////////////////////////////////////////////////// 7775 7683 … … 8607 8515 } 8608 8516 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_Running8634 || mData->mMachineState == MachineState_Paused, E_FAIL);8635 AssertReturn(mSnapshotData.mLastState == MachineState_Null, E_FAIL);8636 AssertReturn(mSnapshotData.mSnapshot.isNull(), E_FAIL);8637 8638 if ( !fTakingSnapshotOnline8639 && mData->mMachineState != MachineState_Saved8640 )8641 {8642 /* save all current settings to ensure current changes are committed and8643 * 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 ( fTakingSnapshotOnline8655 || 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 try8690 {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); // weight8717 8718 /* Leave the lock before a lengthy operation (mMachineState is8719 * 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 else8748 *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 it8776 * all via Console::Resume().8777 */8778 if ( mData->mMachineState != mSnapshotData.mLastState8779 && 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 current8824 * 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 online8837 */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 preparations8909 ULONG ulTotalWeight = 1; // one for preparations8910 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 manage8920 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 state8929 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 away8934 setError(E_FAIL, tr("Cannot access state file '%s', runtime error, %Rra"), pSnapshot->stateFilePath().c_str(), irc);8935 if (ullSize == 0) // avoid division by zero8936 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 not8954 * 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 8983 8517 STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames), 8984 8518 ComSafeArrayOut(BSTR, aValues), … … 9587 9121 LogFlowThisFuncLeave(); 9588 9122 return rc; 9589 }9590 9591 /**9592 * Helper method to finalize taking a snapshot. Gets called to finalize the9593 * "take snapshot" procedure, either from the public SessionMachine::EndTakingSnapshot()9594 * if taking the snapshot failed/was aborted or from the takeSnapshotHandler thread9595 * when taking the snapshot succeeded.9596 *9597 * Expected to be called after completing *all* the tasks related to taking the9598 * 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 lock9613 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 one9626 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, so9634 * 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 else9650 {9651 /* delete all differencing hard disks created (this will also attach9652 * their parents back by rolling back mMediaData) */9653 fixupMedia(false /* aCommit */);9654 9655 mData->mFirstSnapshot = pOldFirstSnap; // might have been changed above9656 mData->mCurrentSnapshot = pOldCurrentSnap; // might have been changed above9657 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 MediumDiscardRec9677 {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 by9705 * DeleteSnapshotTask::handler()!9706 *9707 * When aTask.subTask is true, the associated progress object is left9708 * uncompleted on success. On failure, the progress is marked as completed9709 * 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 accidentally9723 * 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 try9750 {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 types9762 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); // weight9777 CheckComRCThrowRC(rc);9778 continue;9779 }9780 9781 Medium::MergeChain *chain = NULL;9782 9783 /* needs to be discarded (merged with the child if any), check9784 * 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 its9791 * only child to it (prepareDiscard() does necessary checks). We9792 * need then to update the attachment that refers to the child9793 * to refer to the parent instead. Don't forget to detach the9794 * child (otherwise mergeTo() called by discard() will assert9795 * because it will be going to delete the child) */9796 9797 /* The below assert would be nice but I don't want to move9798 * Medium::MergeChain to the header just for that9799 * 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 else9829 {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 disks9855 * (unless a runtime error like end-of-disc happens). Prior to9856 * performing the actual merge, we want to discard the snapshot itself9857 * and remove it from the XML file to make sure that a possible merge9858 * ruintime error will not make this snapshot inconsistent because of9859 * 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 the9869 * 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 have9878 // to return a warning if the state file path cannot be deleted9879 if (stateFilePath)9880 {9881 aTask.progress->SetNextOperation(Bstr(tr("Discarding the execution state")),9882 1); // weight9883 9884 RTFileDelete(Utf8Str(stateFilePath).c_str());9885 }9886 9887 /// @todo NEWMEDIA to provide a good level of fauilt tolerance, we9888 /// should restore the shapshot in the snapshot tree if9889 /// saveSnapshotSettings fails. Actually, we may call9890 /// #saveSnapshotSettings() with a special flag that will tell it to9891 /// skip the given snapshot as if it would have been discarded and9892 /// only actually discard it if the save operation succeeds.9893 }9894 9895 /* here we come when we've irrevesibly discarded the snapshot which9896 * means that the VM settigns (our relevant changes to mData) need to be9897 * saved too */9898 /// @todo NEWMEDIA maybe save everything in one operation in place of9899 /// saveSnapshotSettings() above9900 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 the9909 /// snapshot itself has been already deleted (and interpret these9910 /// 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 to9959 * 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 are9961 * still in a protective state which allows us to temporarily leave9962 * 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 by9991 * 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 accidentally10005 * 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 to10019 * provide an AutoWriteLock argument that lets create a non-locking10020 * instance */10021 vboxLock.unlock();10022 10023 AutoWriteLock alock(this);10024 10025 /* discard all current changes to mUserData (name, OSType etc.) (note that10026 * the machine is powered off, so there is no need to inform the direct10027 * session) */10028 if (isModified())10029 rollback(false /* aNotify */);10030 10031 HRESULT rc = S_OK;10032 10033 bool stateRestored = false;10034 10035 try10036 {10037 /* discard the saved state file if the machine was Saved prior to this10038 * 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 be10084 * deassociated/deleted on #commit() called from #saveSettings() at10085 * the end. On failure, newly created implicit diffs will be10086 * 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); // weight10105 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 else10123 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 will10136 * 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 the10148 * parent cannot change, so no lock */10149 if ( !pMedium.isNull()10150 && pAttach->type() == DeviceType_HardDisk10151 && !pMedium->parent().isNull()10152 && pMedium->children().size() == 010153 )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 need10164 * 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 still10166 * 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 execution10172 * state accordingly no matter of the discard snapshot result */10173 if (!mSSData->mStateFilePath.isEmpty())10174 setMachineState(MachineState_Saved);10175 else10176 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 of10186 // 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 != NULL10195 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 more10209 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() above10220 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();10254 9123 } 10255 9124 … … 10627 9496 return directControl->UpdateMachineState (mData->mMachineState); 10628 9497 } 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 task10639 delete task;10640 10641 return 0;10642 }10643 10644 /////////////////////////////////////////////////////////////////////////////10645 // SnapshotMachine class10646 /////////////////////////////////////////////////////////////////////////////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 from10671 * @param aSnapshotId snapshot ID of this snapshot machine10672 * @param aStateFilePath file where the execution state will be later saved10673 * (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 the10703 * 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 copy10716 * contains just references to original objects) */10717 for (HWData::SharedFolderList::iterator10718 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 snapshot10730 * (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-harddisk10738 {10739 rc = pMedium->attachTo(mData->mUuid, mSnapshotId);10740 AssertComRC(rc);10741 }10742 }10743 10744 /* create copies of all storage controllers (mStorageControllerData10745 * after attaching a copy contains just references to original objects) */10746 mStorageControllers.allocate();10747 for (StorageControllerList::const_iterator10748 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_VRDP10764 unconst(mVRDPServer).createObject();10765 mVRDPServer->initCopy (this, mPeer->mVRDPServer);10766 #endif10767 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 to10803 * @param aHWNode <Hardware> node10804 * @param aHDAsNode <HardDiskAttachments> node10805 * @param aSnapshotId snapshot ID of this snapshot machine10806 * @param aStateFilePath file where the execution state is saved10807 * (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 share10839 * (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_VRDP10857 unconst(mVRDPServer).createObject();10858 mVRDPServer->init (this);10859 #endif10860 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 interface10927 ////////////////////////////////////////////////////////////////////////////////10928 10929 /**10930 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle10931 * with the primary Machine instance (mPeer).10932 */10933 RWLockHandle *SnapshotMachine::lockHandle() const10934 {10935 AssertReturn(!mPeer.isNull(), NULL);10936 return mPeer->lockHandle();10937 }10938 10939 // public methods only for internal purposes10940 ////////////////////////////////////////////////////////////////////////////////10941 10942 /**10943 * Called by the snapshot object associated with this SnapshotMachine when10944 * 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(); @todo10953 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 1 1 /** @file 2 2 * 3 * VirtualBox COM class implementation3 * COM class implementation for Snapshot and SnapshotMachine. 4 4 */ 5 5 … … 23 23 24 24 #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 25 33 #include "Logging.h" 26 34 … … 29 37 #include <VBox/err.h> 30 38 31 #include <list>32 33 39 #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 */ 54 static 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 } 34 64 35 65 //////////////////////////////////////////////////////////////////////////////// … … 410 440 } 411 441 412 // public methods only for internal purposes 442 //////////////////////////////////////////////////////////////////////////////// 443 // 444 // Snapshot public internal methods 445 // 413 446 //////////////////////////////////////////////////////////////////////////////// 414 447 … … 699 732 } 700 733 734 //////////////////////////////////////////////////////////////////////////////// 735 // 736 // SnapshotMachine implementation 737 // 738 //////////////////////////////////////////////////////////////////////////////// 739 740 DEFINE_EMPTY_CTOR_DTOR (SnapshotMachine) 741 742 HRESULT 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 752 void 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 */ 769 HRESULT 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 */ 902 HRESULT 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 */ 997 void 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 */ 1021 RWLockHandle *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 */ 1039 HRESULT 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 */ 1058 struct 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 */ 1081 struct 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 */ 1103 struct 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 */ 1133 STDMETHODIMP 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 */ 1278 STDMETHODIMP 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 */ 1309 STDMETHODIMP 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 */ 1402 STDMETHODIMP 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 */ 1511 DECLCALLBACK(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 */ 1537 HRESULT 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 */ 1609 struct 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 1634 typedef 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 */ 1646 void 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 */ 1928 void 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.