VirtualBox

source: vbox/trunk/src/VBox/Main/SnapshotImpl.cpp@ 25414

Last change on this file since 25414 was 25310, checked in by vboxsync, 15 years ago

Main: lock validator, first batch: implement per-thread stack to trace locking (disabled by default, use VBOX_WITH_LOCK_VALIDATOR, but that WILL FAIL presently)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 79.0 KB
RevLine 
[1]1/** @file
2 *
[24279]3 * COM class implementation for Snapshot and SnapshotMachine.
[1]4 */
5
6/*
[8155]7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
[1]8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
[5999]12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
[8155]16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
[1]20 */
21
22#include "SnapshotImpl.h"
23
24#include "MachineImpl.h"
[25200]25#include "MediumImpl.h"
[24279]26#include "Global.h"
27
28// @todo these three includes are required for about one or two lines, try
29// to remove them and put that code in shared code in MachineImplcpp
30#include "SharedFolderImpl.h"
31#include "USBControllerImpl.h"
32#include "VirtualBoxImpl.h"
33
[1]34#include "Logging.h"
35
36#include <iprt/path.h>
37#include <VBox/param.h>
38#include <VBox/err.h>
39
[21823]40#include <VBox/settings.h>
41
[1]42////////////////////////////////////////////////////////////////////////////////
[23223]43//
[24279]44// Globals
45//
46////////////////////////////////////////////////////////////////////////////////
47
48/**
49 * Progress callback handler for lengthy operations
50 * (corresponds to the FNRTPROGRESS typedef).
51 *
52 * @param uPercentage Completetion precentage (0-100).
53 * @param pvUser Pointer to the Progress instance.
54 */
55static DECLCALLBACK(int) progressCallback(unsigned uPercentage, void *pvUser)
56{
[24556]57 IProgress *progress = static_cast<IProgress*>(pvUser);
[24279]58
59 /* update the progress object */
60 if (progress)
61 progress->SetCurrentOperationProgress(uPercentage);
62
63 return VINF_SUCCESS;
64}
65
66////////////////////////////////////////////////////////////////////////////////
67//
[23257]68// Snapshot private data definition
[23223]69//
70////////////////////////////////////////////////////////////////////////////////
[1]71
[21835]72typedef std::list< ComObjPtr<Snapshot> > SnapshotsList;
[21823]73
74struct Snapshot::Data
[1]75{
[21823]76 Data()
77 {
78 RTTimeSpecSetMilli(&timeStamp, 0);
79 };
[1]80
[21823]81 ~Data()
82 {}
83
[22173]84 Guid uuid;
85 Utf8Str strName;
86 Utf8Str strDescription;
[21823]87 RTTIMESPEC timeStamp;
88 ComObjPtr<SnapshotMachine> pMachine;
89
[25152]90 /** weak VirtualBox parent */
91 const ComObjPtr<VirtualBox, ComWeakRef> pVirtualBox;
92
93 // pParent and llChildren are protected by Machine::snapshotsTreeLockHandle()
94 ComObjPtr<Snapshot> pParent;
95 SnapshotsList llChildren;
[1]96};
97
[21823]98////////////////////////////////////////////////////////////////////////////////
[23223]99//
100// Constructor / destructor
101//
102////////////////////////////////////////////////////////////////////////////////
[21823]103
[1]104HRESULT Snapshot::FinalConstruct()
105{
106 LogFlowMember (("Snapshot::FinalConstruct()\n"));
107 return S_OK;
108}
109
110void Snapshot::FinalRelease()
111{
112 LogFlowMember (("Snapshot::FinalRelease()\n"));
113 uninit();
114}
115
116/**
117 * Initializes the instance
118 *
119 * @param aId id of the snapshot
120 * @param aName name of the snapshot
121 * @param aDescription name of the snapshot (NULL if no description)
122 * @param aTimeStamp timestamp of the snapshot, in ms since 1970-01-01 UTC
123 * @param aMachine machine associated with this snapshot
124 * @param aParent parent snapshot (NULL if no parent)
125 */
[21823]126HRESULT Snapshot::init(VirtualBox *aVirtualBox,
127 const Guid &aId,
[22173]128 const Utf8Str &aName,
129 const Utf8Str &aDescription,
130 const RTTIMESPEC &aTimeStamp,
[21823]131 SnapshotMachine *aMachine,
132 Snapshot *aParent)
[1]133{
[22173]134 LogFlowMember(("Snapshot::init(uuid: %s, aParent->uuid=%s)\n", aId.toString().c_str(), (aParent) ? aParent->m->uuid.toString().c_str() : ""));
[1]135
[22173]136 ComAssertRet (!aId.isEmpty() && !aName.isEmpty() && aMachine, E_INVALIDARG);
[1]137
[21823]138 /* Enclose the state transition NotReady->InInit->Ready */
139 AutoInitSpan autoInitSpan(this);
140 AssertReturn(autoInitSpan.isOk(), E_FAIL);
[1]141
[21823]142 m = new Data;
143
144 /* share parent weakly */
[25152]145 unconst(m->pVirtualBox) = aVirtualBox;
[21823]146
[25152]147 m->pParent = aParent;
[1]148
[22173]149 m->uuid = aId;
150 m->strName = aName;
151 m->strDescription = aDescription;
[21823]152 m->timeStamp = aTimeStamp;
153 m->pMachine = aMachine;
[1]154
155 if (aParent)
[21823]156 aParent->m->llChildren.push_back(this);
[1]157
[21823]158 /* Confirm a successful initialization when it's the case */
159 autoInitSpan.setSucceeded();
[1]160
161 return S_OK;
162}
163
164/**
165 * Uninitializes the instance and sets the ready flag to FALSE.
166 * Called either from FinalRelease(), by the parent when it gets destroyed,
167 * or by a third party when it decides this object is no more valid.
168 */
169void Snapshot::uninit()
170{
171 LogFlowMember (("Snapshot::uninit()\n"));
172
[21823]173 /* Enclose the state transition Ready->InUninit->NotReady */
[21878]174 AutoUninitSpan autoUninitSpan(this);
[21823]175 if (autoUninitSpan.uninitDone())
[21686]176 return;
177
[1]178 // uninit all children
[21823]179 SnapshotsList::iterator it;
180 for (it = m->llChildren.begin();
181 it != m->llChildren.end();
[21824]182 ++it)
[21823]183 {
184 Snapshot *pChild = *it;
[25152]185 pChild->m->pParent.setNull();
[21823]186 pChild->uninit();
187 }
188 m->llChildren.clear(); // this unsets all the ComPtrs and probably calls delete
[1]189
[25152]190 if (m->pParent)
191 deparent();
[21823]192
193 if (m->pMachine)
[1]194 {
[21823]195 m->pMachine->uninit();
196 m->pMachine.setNull();
[1]197 }
[21835]198
199 delete m;
200 m = NULL;
[1]201}
202
203/**
204 * Discards the current snapshot by removing it from the tree of snapshots
205 * and reparenting its children.
[21823]206 *
207 * After this, the caller must call uninit() on the snapshot. We can't call
208 * that from here because if we do, the AutoUninitSpan waits forever for
209 * the number of callers to become 0 (it is 1 because of the AutoCaller in here).
210 *
211 * NOTE: this does NOT lock the snapshot, it is assumed that the caller has
[21835]212 * locked a) the machine and b) the snapshots tree in write mode!
[1]213 */
[21823]214void Snapshot::beginDiscard()
[1]215{
[21823]216 AutoCaller autoCaller(this);
217 if (FAILED(autoCaller.rc()))
218 return;
[1]219
[21823]220 /* for now, the snapshot must have only one child when discarded,
221 * or no children at all */
222 AssertReturnVoid(m->llChildren.size() <= 1);
[1]223
[25152]224 ComObjPtr<Snapshot> parentSnapshot = m->pParent;
[21823]225
226 /// @todo (dmik):
227 // when we introduce clones later, discarding the snapshot
228 // will affect the current and first snapshots of clones, if they are
229 // direct children of this snapshot. So we will need to lock machines
230 // associated with child snapshots as well and update mCurrentSnapshot
231 // and/or mFirstSnapshot fields.
232
233 if (this == m->pMachine->mData->mCurrentSnapshot)
[1]234 {
[21823]235 m->pMachine->mData->mCurrentSnapshot = parentSnapshot;
[1]236
[21823]237 /* we've changed the base of the current state so mark it as
238 * modified as it no longer guaranteed to be its copy */
239 m->pMachine->mData->mCurrentStateModified = TRUE;
240 }
241
242 if (this == m->pMachine->mData->mFirstSnapshot)
243 {
244 if (m->llChildren.size() == 1)
[1]245 {
[21835]246 ComObjPtr<Snapshot> childSnapshot = m->llChildren.front();
[21823]247 m->pMachine->mData->mFirstSnapshot = childSnapshot;
[1]248 }
[21823]249 else
250 m->pMachine->mData->mFirstSnapshot.setNull();
[1]251 }
252
[21823]253 // reparent our children
254 for (SnapshotsList::const_iterator it = m->llChildren.begin();
255 it != m->llChildren.end();
256 ++it)
257 {
[21835]258 ComObjPtr<Snapshot> child = *it;
[25310]259 AutoWriteLock childLock(child COMMA_LOCKVAL_SRC_POS);
[1]260
[25152]261 child->m->pParent = m->pParent;
262 if (m->pParent)
263 m->pParent->m->llChildren.push_back(child);
[21823]264 }
265
266 // clear our own children list (since we reparented the children)
267 m->llChildren.clear();
[1]268}
269
[25152]270/**
271 * Internal helper that removes "this" from the list of children of its
272 * parent. Used in uninit() and other places when reparenting is necessary.
273 *
274 * The caller must hold the snapshots tree lock!
275 */
276void Snapshot::deparent()
277{
278 SnapshotsList &llParent = m->pParent->m->llChildren;
279 for (SnapshotsList::iterator it = llParent.begin();
280 it != llParent.end();
281 ++it)
282 {
283 Snapshot *pParentsChild = *it;
284 if (this == pParentsChild)
285 {
286 llParent.erase(it);
287 break;
288 }
289 }
290
291 m->pParent.setNull();
292}
293
[1]294////////////////////////////////////////////////////////////////////////////////
[23223]295//
296// ISnapshot public methods
297//
298////////////////////////////////////////////////////////////////////////////////
[1]299
[19239]300STDMETHODIMP Snapshot::COMGETTER(Id) (BSTR *aId)
[1]301{
[14972]302 CheckComArgOutPointerValid(aId);
[1]303
[21823]304 AutoCaller autoCaller(this);
[25149]305 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[1]306
[25310]307 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
[21823]308
[22173]309 m->uuid.toUtf16().cloneTo(aId);
[1]310 return S_OK;
311}
312
313STDMETHODIMP Snapshot::COMGETTER(Name) (BSTR *aName)
314{
[14972]315 CheckComArgOutPointerValid(aName);
[1]316
[21823]317 AutoCaller autoCaller(this);
[25149]318 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[1]319
[25310]320 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
[21823]321
[22173]322 m->strName.cloneTo(aName);
[1]323 return S_OK;
324}
325
326/**
327 * @note Locks this object for writing, then calls Machine::onSnapshotChange()
328 * (see its lock requirements).
329 */
[22173]330STDMETHODIMP Snapshot::COMSETTER(Name)(IN_BSTR aName)
[1]331{
[14972]332 CheckComArgNotNull(aName);
[1]333
[21823]334 AutoCaller autoCaller(this);
[25149]335 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[1]336
[22173]337 Utf8Str strName(aName);
338
[25310]339 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[21823]340
[22173]341 if (m->strName != strName)
[1]342 {
[22173]343 m->strName = strName;
[1]344
345 alock.leave(); /* Important! (child->parent locks are forbidden) */
346
[21823]347 return m->pMachine->onSnapshotChange(this);
[1]348 }
349
350 return S_OK;
351}
352
353STDMETHODIMP Snapshot::COMGETTER(Description) (BSTR *aDescription)
354{
[14972]355 CheckComArgOutPointerValid(aDescription);
[1]356
[21823]357 AutoCaller autoCaller(this);
[25149]358 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[1]359
[25310]360 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
[21823]361
[22173]362 m->strDescription.cloneTo(aDescription);
[1]363 return S_OK;
364}
365
[15051]366STDMETHODIMP Snapshot::COMSETTER(Description) (IN_BSTR aDescription)
[1]367{
[14972]368 CheckComArgNotNull(aDescription);
[1]369
[21823]370 AutoCaller autoCaller(this);
[25149]371 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[1]372
[22173]373 Utf8Str strDescription(aDescription);
374
[25310]375 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[21823]376
[22173]377 if (m->strDescription != strDescription)
[1]378 {
[22173]379 m->strDescription = strDescription;
[1]380
381 alock.leave(); /* Important! (child->parent locks are forbidden) */
382
[21823]383 return m->pMachine->onSnapshotChange(this);
[1]384 }
385
386 return S_OK;
387}
388
389STDMETHODIMP Snapshot::COMGETTER(TimeStamp) (LONG64 *aTimeStamp)
390{
[14972]391 CheckComArgOutPointerValid(aTimeStamp);
[1]392
[21823]393 AutoCaller autoCaller(this);
[25149]394 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[1]395
[25310]396 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
[21823]397
398 *aTimeStamp = RTTimeSpecGetMilli(&m->timeStamp);
[1]399 return S_OK;
400}
401
[22624]402STDMETHODIMP Snapshot::COMGETTER(Online)(BOOL *aOnline)
[1]403{
[14972]404 CheckComArgOutPointerValid(aOnline);
[1]405
[21823]406 AutoCaller autoCaller(this);
[25149]407 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[1]408
[25310]409 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
[21823]410
[24196]411 *aOnline = !stateFilePath().isEmpty();
[1]412 return S_OK;
413}
414
415STDMETHODIMP Snapshot::COMGETTER(Machine) (IMachine **aMachine)
416{
[14972]417 CheckComArgOutPointerValid(aMachine);
[1]418
[21823]419 AutoCaller autoCaller(this);
[25149]420 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[1]421
[25310]422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
[21823]423
424 m->pMachine.queryInterfaceTo(aMachine);
[1]425 return S_OK;
426}
427
428STDMETHODIMP Snapshot::COMGETTER(Parent) (ISnapshot **aParent)
429{
[14972]430 CheckComArgOutPointerValid(aParent);
[1]431
[21823]432 AutoCaller autoCaller(this);
[25149]433 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[1]434
[25310]435 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
[21823]436
[25152]437 m->pParent.queryInterfaceTo(aParent);
[1]438 return S_OK;
439}
440
[21878]441STDMETHODIMP Snapshot::COMGETTER(Children) (ComSafeArrayOut(ISnapshot *, aChildren))
[1]442{
[17260]443 CheckComArgOutSafeArrayPointerValid(aChildren);
[1]444
[21823]445 AutoCaller autoCaller(this);
[25149]446 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[1]447
[25310]448 AutoReadLock alock(m->pMachine->snapshotsTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
449 AutoReadLock block(this->lockHandle() COMMA_LOCKVAL_SRC_POS);
[1]450
[21823]451 SafeIfaceArray<ISnapshot> collection(m->llChildren);
452 collection.detachTo(ComSafeArrayOutArg(aChildren));
[1]453
454 return S_OK;
455}
456
457////////////////////////////////////////////////////////////////////////////////
[24279]458//
459// Snapshot public internal methods
460//
461////////////////////////////////////////////////////////////////////////////////
[1]462
463/**
[25152]464 * Returns the parent snapshot or NULL if there's none. Must have caller + locking!
465 * @return
466 */
467const ComObjPtr<Snapshot>& Snapshot::getParent() const
468{
469 return m->pParent;
470}
471
472/**
[1]473 * @note
474 * Must be called from under the object's lock!
475 */
[24196]476const Utf8Str& Snapshot::stateFilePath() const
[1]477{
[21823]478 return m->pMachine->mSSData->mStateFilePath;
[1]479}
480
[21835]481/**
482 * Returns the number of direct child snapshots, without grandchildren.
483 * Does not recurse.
484 * @return
485 */
[21826]486ULONG Snapshot::getChildrenCount()
[1]487{
[21823]488 AutoCaller autoCaller(this);
489 AssertComRC(autoCaller.rc());
[1]490
[25310]491 AutoReadLock treeLock(m->pMachine->snapshotsTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
[21826]492 return (ULONG)m->llChildren.size();
493}
494
[21835]495/**
496 * Implementation method for getAllChildrenCount() so we request the
497 * tree lock only once before recursing. Don't call directly.
498 * @return
499 */
[21826]500ULONG Snapshot::getAllChildrenCountImpl()
501{
502 AutoCaller autoCaller(this);
503 AssertComRC(autoCaller.rc());
504
[21823]505 ULONG count = (ULONG)m->llChildren.size();
506 for (SnapshotsList::const_iterator it = m->llChildren.begin();
507 it != m->llChildren.end();
508 ++it)
[1]509 {
[21826]510 count += (*it)->getAllChildrenCountImpl();
[1]511 }
512
513 return count;
514}
515
[21835]516/**
517 * Returns the number of child snapshots including all grandchildren.
518 * Recurses into the snapshots tree.
519 * @return
520 */
[21826]521ULONG Snapshot::getAllChildrenCount()
522{
523 AutoCaller autoCaller(this);
524 AssertComRC(autoCaller.rc());
525
[25310]526 AutoReadLock treeLock(m->pMachine->snapshotsTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
[21826]527 return getAllChildrenCountImpl();
528}
529
[21835]530/**
531 * Returns the SnapshotMachine that this snapshot belongs to.
532 * Caller must hold the snapshot's object lock!
533 * @return
534 */
[25152]535const ComObjPtr<SnapshotMachine>& Snapshot::getSnapshotMachine() const
[21823]536{
[25152]537 return m->pMachine;
[21823]538}
539
[21835]540/**
541 * Returns the UUID of this snapshot.
542 * Caller must hold the snapshot's object lock!
543 * @return
544 */
[21823]545Guid Snapshot::getId() const
546{
[22173]547 return m->uuid;
[21823]548}
549
[21835]550/**
551 * Returns the name of this snapshot.
552 * Caller must hold the snapshot's object lock!
553 * @return
554 */
[22173]555const Utf8Str& Snapshot::getName() const
[21823]556{
[22173]557 return m->strName;
[21823]558}
559
[21835]560/**
561 * Returns the time stamp of this snapshot.
562 * Caller must hold the snapshot's object lock!
563 * @return
564 */
[21823]565RTTIMESPEC Snapshot::getTimeStamp() const
566{
567 return m->timeStamp;
568}
569
[1]570/**
571 * Searches for a snapshot with the given ID among children, grand-children,
572 * etc. of this snapshot. This snapshot itself is also included in the search.
[21835]573 * Caller must hold the snapshots tree lock!
[1]574 */
[21823]575ComObjPtr<Snapshot> Snapshot::findChildOrSelf(IN_GUID aId)
[1]576{
[21823]577 ComObjPtr<Snapshot> child;
[1]578
[21823]579 AutoCaller autoCaller(this);
580 AssertComRC(autoCaller.rc());
[1]581
[25310]582 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
[21823]583
[22173]584 if (m->uuid == aId)
[1]585 child = this;
586 else
587 {
[25279]588 alock.release();
[21823]589 for (SnapshotsList::const_iterator it = m->llChildren.begin();
590 it != m->llChildren.end();
591 ++it)
[1]592 {
[21823]593 if ((child = (*it)->findChildOrSelf(aId)))
594 break;
[1]595 }
596 }
597
598 return child;
599}
600
601/**
602 * Searches for a first snapshot with the given name among children,
603 * grand-children, etc. of this snapshot. This snapshot itself is also included
604 * in the search.
[21835]605 * Caller must hold the snapshots tree lock!
[1]606 */
[22173]607ComObjPtr<Snapshot> Snapshot::findChildOrSelf(const Utf8Str &aName)
[1]608{
[21878]609 ComObjPtr<Snapshot> child;
[22173]610 AssertReturn(!aName.isEmpty(), child);
[1]611
[21823]612 AutoCaller autoCaller(this);
613 AssertComRC(autoCaller.rc());
[1]614
[25310]615 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
[21823]616
[22173]617 if (m->strName == aName)
[1]618 child = this;
619 else
620 {
[25279]621 alock.release();
[21823]622 for (SnapshotsList::const_iterator it = m->llChildren.begin();
623 it != m->llChildren.end();
624 ++it)
[1]625 {
[21823]626 if ((child = (*it)->findChildOrSelf(aName)))
627 break;
[1]628 }
629 }
630
631 return child;
632}
633
634/**
[21823]635 * Internal implementation for Snapshot::updateSavedStatePaths (below).
636 * @param aOldPath
637 * @param aNewPath
638 */
639void Snapshot::updateSavedStatePathsImpl(const char *aOldPath, const char *aNewPath)
640{
[25310]641 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[21823]642
[24196]643 const Utf8Str &path = m->pMachine->mSSData->mStateFilePath;
[22778]644 LogFlowThisFunc(("Snap[%s].statePath={%s}\n", m->strName.c_str(), path.c_str()));
[21823]645
646 /* state file may be NULL (for offline snapshots) */
647 if ( path.length()
[22173]648 && RTPathStartsWith(path.c_str(), aOldPath)
[21823]649 )
650 {
[24196]651 m->pMachine->mSSData->mStateFilePath = Utf8StrFmt("%s%s", aNewPath, path.raw() + strlen(aOldPath));
[21823]652
653 LogFlowThisFunc(("-> updated: {%s}\n", path.raw()));
654 }
655
656 for (SnapshotsList::const_iterator it = m->llChildren.begin();
657 it != m->llChildren.end();
658 ++it)
659 {
[22778]660 Snapshot *pChild = *it;
661 pChild->updateSavedStatePathsImpl(aOldPath, aNewPath);
[21823]662 }
663}
664
665/**
[1]666 * Checks if the specified path change affects the saved state file path of
667 * this snapshot or any of its (grand-)children and updates it accordingly.
668 *
669 * Intended to be called by Machine::openConfigLoader() only.
670 *
671 * @param aOldPath old path (full)
672 * @param aNewPath new path (full)
673 *
674 * @note Locks this object + children for writing.
675 */
[21823]676void Snapshot::updateSavedStatePaths(const char *aOldPath, const char *aNewPath)
[1]677{
[21823]678 LogFlowThisFunc(("aOldPath={%s} aNewPath={%s}\n", aOldPath, aNewPath));
[1]679
[21823]680 AssertReturnVoid(aOldPath);
681 AssertReturnVoid(aNewPath);
[1]682
[21823]683 AutoCaller autoCaller(this);
684 AssertComRC(autoCaller.rc());
[1]685
[25310]686 AutoWriteLock chLock(m->pMachine->snapshotsTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
[21823]687 // call the implementation under the tree lock
688 updateSavedStatePathsImpl(aOldPath, aNewPath);
689}
[1]690
[21823]691/**
692 * Internal implementation for Snapshot::saveSnapshot (below).
693 * @param aNode
694 * @param aAttrsOnly
695 * @return
696 */
[22173]697HRESULT Snapshot::saveSnapshotImpl(settings::Snapshot &data, bool aAttrsOnly)
[21823]698{
[25310]699 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
[21835]700
[22173]701 data.uuid = m->uuid;
702 data.strName = m->strName;
703 data.timestamp = m->timeStamp;
704 data.strDescription = m->strDescription;
[21823]705
706 if (aAttrsOnly)
707 return S_OK;
708
709 /* stateFile (optional) */
[24196]710 if (!stateFilePath().isEmpty())
[21823]711 /* try to make the file name relative to the settings file dir */
[24196]712 m->pMachine->calculateRelativePath(stateFilePath(), data.strStateFile);
[22173]713 else
714 data.strStateFile.setNull();
[1]715
[22173]716 HRESULT rc = m->pMachine->saveHardware(data.hardware);
[25149]717 if (FAILED(rc)) return rc;
[21823]718
[22173]719 rc = m->pMachine->saveStorageControllers(data.storage);
[25149]720 if (FAILED(rc)) return rc;
[21823]721
[25279]722 alock.release();
[21835]723
[22173]724 data.llChildSnapshots.clear();
725
[21823]726 if (m->llChildren.size())
727 {
728 for (SnapshotsList::const_iterator it = m->llChildren.begin();
729 it != m->llChildren.end();
730 ++it)
731 {
[22173]732 settings::Snapshot snap;
733 rc = (*it)->saveSnapshotImpl(snap, aAttrsOnly);
[25149]734 if (FAILED(rc)) return rc;
[22173]735
736 data.llChildSnapshots.push_back(snap);
[21823]737 }
738 }
739
740 return S_OK;
[1]741}
742
[21823]743/**
744 * Saves the given snapshot and all its children (unless \a aAttrsOnly is true).
745 * It is assumed that the given node is empty (unless \a aAttrsOnly is true).
746 *
747 * @param aNode <Snapshot> node to save the snapshot to.
748 * @param aSnapshot Snapshot to save.
749 * @param aAttrsOnly If true, only updatge user-changeable attrs.
750 */
[22173]751HRESULT Snapshot::saveSnapshot(settings::Snapshot &data, bool aAttrsOnly)
[21823]752{
[25310]753 AutoWriteLock listLock(m->pMachine->snapshotsTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
[21823]754
[22173]755 return saveSnapshotImpl(data, aAttrsOnly);
[21823]756}
757
[24279]758////////////////////////////////////////////////////////////////////////////////
759//
760// SnapshotMachine implementation
761//
762////////////////////////////////////////////////////////////////////////////////
763
764DEFINE_EMPTY_CTOR_DTOR (SnapshotMachine)
765
766HRESULT SnapshotMachine::FinalConstruct()
767{
768 LogFlowThisFunc(("\n"));
769
770 /* set the proper type to indicate we're the SnapshotMachine instance */
771 unconst(mType) = IsSnapshotMachine;
772
773 return S_OK;
774}
775
776void SnapshotMachine::FinalRelease()
777{
778 LogFlowThisFunc(("\n"));
779
780 uninit();
781}
782
783/**
784 * Initializes the SnapshotMachine object when taking a snapshot.
785 *
786 * @param aSessionMachine machine to take a snapshot from
787 * @param aSnapshotId snapshot ID of this snapshot machine
788 * @param aStateFilePath file where the execution state will be later saved
789 * (or NULL for the offline snapshot)
790 *
791 * @note The aSessionMachine must be locked for writing.
792 */
793HRESULT SnapshotMachine::init(SessionMachine *aSessionMachine,
794 IN_GUID aSnapshotId,
795 const Utf8Str &aStateFilePath)
796{
797 LogFlowThisFuncEnter();
798 LogFlowThisFunc(("mName={%ls}\n", aSessionMachine->mUserData->mName.raw()));
799
800 AssertReturn(aSessionMachine && !Guid (aSnapshotId).isEmpty(), E_INVALIDARG);
801
802 /* Enclose the state transition NotReady->InInit->Ready */
803 AutoInitSpan autoInitSpan(this);
804 AssertReturn(autoInitSpan.isOk(), E_FAIL);
805
806 AssertReturn(aSessionMachine->isWriteLockOnCurrentThread(), E_FAIL);
807
808 mSnapshotId = aSnapshotId;
809
810 /* memorize the primary Machine instance (i.e. not SessionMachine!) */
811 unconst(mPeer) = aSessionMachine->mPeer;
812 /* share the parent pointer */
813 unconst(mParent) = mPeer->mParent;
814
815 /* take the pointer to Data to share */
816 mData.share (mPeer->mData);
817
818 /* take the pointer to UserData to share (our UserData must always be the
819 * same as Machine's data) */
820 mUserData.share (mPeer->mUserData);
821 /* make a private copy of all other data (recent changes from SessionMachine) */
822 mHWData.attachCopy (aSessionMachine->mHWData);
823 mMediaData.attachCopy(aSessionMachine->mMediaData);
824
825 /* SSData is always unique for SnapshotMachine */
826 mSSData.allocate();
827 mSSData->mStateFilePath = aStateFilePath;
828
829 HRESULT rc = S_OK;
830
831 /* create copies of all shared folders (mHWData after attiching a copy
832 * contains just references to original objects) */
833 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
834 it != mHWData->mSharedFolders.end();
835 ++it)
836 {
837 ComObjPtr<SharedFolder> folder;
838 folder.createObject();
839 rc = folder->initCopy (this, *it);
[25149]840 if (FAILED(rc)) return rc;
[24279]841 *it = folder;
842 }
843
844 /* associate hard disks with the snapshot
845 * (Machine::uninitDataAndChildObjects() will deassociate at destruction) */
846 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
847 it != mMediaData->mAttachments.end();
848 ++it)
849 {
850 MediumAttachment *pAtt = *it;
[24989]851 Medium *pMedium = pAtt->getMedium();
[24279]852 if (pMedium) // can be NULL for non-harddisk
853 {
854 rc = pMedium->attachTo(mData->mUuid, mSnapshotId);
855 AssertComRC(rc);
856 }
857 }
858
859 /* create copies of all storage controllers (mStorageControllerData
860 * after attaching a copy contains just references to original objects) */
861 mStorageControllers.allocate();
862 for (StorageControllerList::const_iterator
863 it = aSessionMachine->mStorageControllers->begin();
864 it != aSessionMachine->mStorageControllers->end();
865 ++it)
866 {
867 ComObjPtr<StorageController> ctrl;
868 ctrl.createObject();
869 ctrl->initCopy (this, *it);
870 mStorageControllers->push_back(ctrl);
871 }
872
873 /* create all other child objects that will be immutable private copies */
874
875 unconst(mBIOSSettings).createObject();
876 mBIOSSettings->initCopy (this, mPeer->mBIOSSettings);
877
878#ifdef VBOX_WITH_VRDP
879 unconst(mVRDPServer).createObject();
880 mVRDPServer->initCopy (this, mPeer->mVRDPServer);
881#endif
882
883 unconst(mAudioAdapter).createObject();
884 mAudioAdapter->initCopy (this, mPeer->mAudioAdapter);
885
886 unconst(mUSBController).createObject();
[25194]887 mUSBController->init(this, mPeer->mUSBController);
[24279]888
[25194]889 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot++)
[24279]890 {
[25194]891 unconst(mNetworkAdapters[slot]).createObject();
892 mNetworkAdapters[slot]->initCopy (this, mPeer->mNetworkAdapters [slot]);
[24279]893 }
894
[25194]895 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot++)
[24279]896 {
897 unconst(mSerialPorts [slot]).createObject();
[25194]898 mSerialPorts[slot]->initCopy (this, mPeer->mSerialPorts[slot]);
[24279]899 }
900
[25194]901 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot++)
[24279]902 {
[25194]903 unconst(mParallelPorts[slot]).createObject();
904 mParallelPorts[slot]->initCopy (this, mPeer->mParallelPorts[slot]);
[24279]905 }
906
907 /* Confirm a successful initialization when it's the case */
908 autoInitSpan.setSucceeded();
909
910 LogFlowThisFuncLeave();
911 return S_OK;
912}
913
914/**
915 * Initializes the SnapshotMachine object when loading from the settings file.
916 *
917 * @param aMachine machine the snapshot belngs to
918 * @param aHWNode <Hardware> node
919 * @param aHDAsNode <HardDiskAttachments> node
920 * @param aSnapshotId snapshot ID of this snapshot machine
921 * @param aStateFilePath file where the execution state is saved
922 * (or NULL for the offline snapshot)
923 *
924 * @note Doesn't lock anything.
925 */
926HRESULT SnapshotMachine::init(Machine *aMachine,
927 const settings::Hardware &hardware,
928 const settings::Storage &storage,
929 IN_GUID aSnapshotId,
930 const Utf8Str &aStateFilePath)
931{
932 LogFlowThisFuncEnter();
933 LogFlowThisFunc(("mName={%ls}\n", aMachine->mUserData->mName.raw()));
934
935 AssertReturn(aMachine && !Guid(aSnapshotId).isEmpty(), E_INVALIDARG);
936
937 /* Enclose the state transition NotReady->InInit->Ready */
938 AutoInitSpan autoInitSpan(this);
939 AssertReturn(autoInitSpan.isOk(), E_FAIL);
940
941 /* Don't need to lock aMachine when VirtualBox is starting up */
942
943 mSnapshotId = aSnapshotId;
944
945 /* memorize the primary Machine instance */
946 unconst(mPeer) = aMachine;
947 /* share the parent pointer */
948 unconst(mParent) = mPeer->mParent;
949
950 /* take the pointer to Data to share */
951 mData.share (mPeer->mData);
952 /*
953 * take the pointer to UserData to share
954 * (our UserData must always be the same as Machine's data)
955 */
956 mUserData.share (mPeer->mUserData);
957 /* allocate private copies of all other data (will be loaded from settings) */
958 mHWData.allocate();
959 mMediaData.allocate();
960 mStorageControllers.allocate();
961
962 /* SSData is always unique for SnapshotMachine */
963 mSSData.allocate();
964 mSSData->mStateFilePath = aStateFilePath;
965
966 /* create all other child objects that will be immutable private copies */
967
968 unconst(mBIOSSettings).createObject();
969 mBIOSSettings->init (this);
970
971#ifdef VBOX_WITH_VRDP
972 unconst(mVRDPServer).createObject();
973 mVRDPServer->init (this);
974#endif
975
976 unconst(mAudioAdapter).createObject();
977 mAudioAdapter->init (this);
978
979 unconst(mUSBController).createObject();
980 mUSBController->init (this);
981
982 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
983 {
984 unconst(mNetworkAdapters [slot]).createObject();
985 mNetworkAdapters [slot]->init (this, slot);
986 }
987
988 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
989 {
990 unconst(mSerialPorts [slot]).createObject();
991 mSerialPorts [slot]->init (this, slot);
992 }
993
994 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
995 {
996 unconst(mParallelPorts [slot]).createObject();
997 mParallelPorts [slot]->init (this, slot);
998 }
999
1000 /* load hardware and harddisk settings */
1001
1002 HRESULT rc = loadHardware(hardware);
1003 if (SUCCEEDED(rc))
1004 rc = loadStorageControllers(storage, true /* aRegistered */, &mSnapshotId);
1005
1006 if (SUCCEEDED(rc))
1007 /* commit all changes made during the initialization */
1008 commit();
1009
1010 /* Confirm a successful initialization when it's the case */
1011 if (SUCCEEDED(rc))
1012 autoInitSpan.setSucceeded();
1013
1014 LogFlowThisFuncLeave();
1015 return rc;
1016}
1017
1018/**
1019 * Uninitializes this SnapshotMachine object.
1020 */
1021void SnapshotMachine::uninit()
1022{
1023 LogFlowThisFuncEnter();
1024
1025 /* Enclose the state transition Ready->InUninit->NotReady */
1026 AutoUninitSpan autoUninitSpan(this);
1027 if (autoUninitSpan.uninitDone())
1028 return;
1029
1030 uninitDataAndChildObjects();
1031
1032 /* free the essential data structure last */
1033 mData.free();
1034
1035 unconst(mParent).setNull();
1036 unconst(mPeer).setNull();
1037
1038 LogFlowThisFuncLeave();
1039}
1040
1041/**
1042 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
1043 * with the primary Machine instance (mPeer).
1044 */
1045RWLockHandle *SnapshotMachine::lockHandle() const
1046{
1047 AssertReturn(!mPeer.isNull(), NULL);
1048 return mPeer->lockHandle();
1049}
1050
1051////////////////////////////////////////////////////////////////////////////////
1052//
1053// SnapshotMachine public internal methods
1054//
1055////////////////////////////////////////////////////////////////////////////////
1056
1057/**
1058 * Called by the snapshot object associated with this SnapshotMachine when
1059 * snapshot data such as name or description is changed.
1060 *
1061 * @note Locks this object for writing.
1062 */
1063HRESULT SnapshotMachine::onSnapshotChange (Snapshot *aSnapshot)
1064{
[25310]1065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[24279]1066
1067 // mPeer->saveAllSnapshots(); @todo
1068
1069 /* inform callbacks */
1070 mParent->onSnapshotChange(mData->mUuid, aSnapshot->getId());
1071
1072 return S_OK;
1073}
1074
1075////////////////////////////////////////////////////////////////////////////////
1076//
1077// SessionMachine task records
1078//
1079////////////////////////////////////////////////////////////////////////////////
1080
[24298]1081/**
[24299]1082 * Abstract base class for SessionMachine::RestoreSnapshotTask and
1083 * SessionMachine::DeleteSnapshotTask. This is necessary since
[24298]1084 * RTThreadCreate cannot call a method as its thread function, so
1085 * instead we have it call the static SessionMachine::taskHandler,
1086 * which can then call the handler() method in here (implemented
1087 * by the children).
1088 */
1089struct SessionMachine::SnapshotTask
[24279]1090{
[24298]1091 SnapshotTask(SessionMachine *m,
1092 Progress *p,
1093 Snapshot *s)
1094 : pMachine(m),
1095 pProgress(p),
1096 machineStateBackup(m->mData->mMachineState), // save the current machine state
1097 pSnapshot(s)
[24279]1098 {}
1099
[24298]1100 void modifyBackedUpState(MachineState_T s)
[24279]1101 {
[24298]1102 *const_cast<MachineState_T*>(&machineStateBackup) = s;
[24279]1103 }
1104
1105 virtual void handler() = 0;
1106
[24298]1107 ComObjPtr<SessionMachine> pMachine;
1108 ComObjPtr<Progress> pProgress;
1109 const MachineState_T machineStateBackup;
1110 ComObjPtr<Snapshot> pSnapshot;
[24279]1111};
1112
[24299]1113/** Restore snapshot state task */
1114struct SessionMachine::RestoreSnapshotTask
1115 : public SessionMachine::SnapshotTask
1116{
1117 RestoreSnapshotTask(SessionMachine *m,
1118 Progress *p,
1119 Snapshot *s,
1120 ULONG ulStateFileSizeMB)
1121 : SnapshotTask(m, p, s),
1122 m_ulStateFileSizeMB(ulStateFileSizeMB)
1123 {}
1124
1125 void handler()
1126 {
1127 pMachine->restoreSnapshotHandler(*this);
1128 }
1129
1130 ULONG m_ulStateFileSizeMB;
1131};
1132
[24279]1133/** Discard snapshot task */
1134struct SessionMachine::DeleteSnapshotTask
[24298]1135 : public SessionMachine::SnapshotTask
[24279]1136{
[24298]1137 DeleteSnapshotTask(SessionMachine *m,
1138 Progress *p,
1139 Snapshot *s)
1140 : SnapshotTask(m, p, s)
[24279]1141 {}
1142
1143 void handler()
1144 {
[24298]1145 pMachine->deleteSnapshotHandler(*this);
[24279]1146 }
1147
[24298]1148private:
1149 DeleteSnapshotTask(const SnapshotTask &task)
1150 : SnapshotTask(task)
1151 {}
[24279]1152};
1153
[24299]1154/**
1155 * Static SessionMachine method that can get passed to RTThreadCreate to
1156 * have a thread started for a SnapshotTask. See SnapshotTask above.
1157 *
1158 * This calls either RestoreSnapshotTask::handler() or DeleteSnapshotTask::handler().
1159 */
1160
1161/* static */ DECLCALLBACK(int) SessionMachine::taskHandler(RTTHREAD /* thread */, void *pvUser)
[24279]1162{
[24299]1163 AssertReturn(pvUser, VERR_INVALID_POINTER);
[24279]1164
[24299]1165 SnapshotTask *task = static_cast<SnapshotTask*>(pvUser);
1166 task->handler();
[24279]1167
[24299]1168 // it's our responsibility to delete the task
1169 delete task;
[24279]1170
[24299]1171 return 0;
1172}
1173
[24279]1174////////////////////////////////////////////////////////////////////////////////
1175//
[24299]1176// TakeSnapshot methods (SessionMachine and related tasks)
[24279]1177//
1178////////////////////////////////////////////////////////////////////////////////
1179
1180/**
[24299]1181 * Implementation for IInternalMachineControl::beginTakingSnapshot().
1182 *
1183 * Gets called indirectly from Console::TakeSnapshot, which creates a
1184 * progress object in the client and then starts a thread
1185 * (Console::fntTakeSnapshotWorker) which then calls this.
1186 *
1187 * In other words, the asynchronous work for taking snapshots takes place
1188 * on the _client_ (in the Console). This is different from restoring
1189 * or deleting snapshots, which start threads on the server.
1190 *
1191 * This does the server-side work of taking a snapshot: it creates diffencing
1192 * images for all hard disks attached to the machine and then creates a
1193 * Snapshot object with a corresponding SnapshotMachine to save the VM settings.
1194 *
1195 * The client's fntTakeSnapshotWorker() blocks while this takes place.
1196 * After this returns successfully, fntTakeSnapshotWorker() will begin
1197 * saving the machine state to the snapshot object and reconfigure the
1198 * hard disks.
1199 *
1200 * When the console is done, it calls SessionMachine::EndTakingSnapshot().
1201 *
[24279]1202 * @note Locks mParent + this object for writing.
[24299]1203 *
1204 * @param aInitiator in: The console on which Console::TakeSnapshot was called.
1205 * @param aName in: The name for the new snapshot.
1206 * @param aDescription in: A description for the new snapshot.
1207 * @param aConsoleProgress in: The console's (client's) progress object.
1208 * @param fTakingSnapshotOnline in: True if an online snapshot is being taken (i.e. machine is running).
1209 * @param aStateFilePath out: name of file in snapshots folder to which the console should write the VM state.
1210 * @return
[24279]1211 */
1212STDMETHODIMP SessionMachine::BeginTakingSnapshot(IConsole *aInitiator,
1213 IN_BSTR aName,
1214 IN_BSTR aDescription,
1215 IProgress *aConsoleProgress,
1216 BOOL fTakingSnapshotOnline,
1217 BSTR *aStateFilePath)
1218{
1219 LogFlowThisFuncEnter();
1220
1221 AssertReturn(aInitiator && aName, E_INVALIDARG);
1222 AssertReturn(aStateFilePath, E_POINTER);
1223
[24920]1224 LogFlowThisFunc(("aName='%ls' fTakingSnapshotOnline=%RTbool\n", aName, fTakingSnapshotOnline));
[24279]1225
1226 AutoCaller autoCaller(this);
1227 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
1228
1229 /* saveSettings() needs mParent lock */
[25310]1230 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
[24279]1231
1232 AssertReturn( !Global::IsOnlineOrTransient(mData->mMachineState)
1233 || mData->mMachineState == MachineState_Running
1234 || mData->mMachineState == MachineState_Paused, E_FAIL);
1235 AssertReturn(mSnapshotData.mLastState == MachineState_Null, E_FAIL);
1236 AssertReturn(mSnapshotData.mSnapshot.isNull(), E_FAIL);
1237
1238 if ( !fTakingSnapshotOnline
1239 && mData->mMachineState != MachineState_Saved
1240 )
1241 {
1242 /* save all current settings to ensure current changes are committed and
1243 * hard disks are fixed up */
1244 HRESULT rc = saveSettings();
[25149]1245 if (FAILED(rc)) return rc;
[24279]1246 }
1247
1248 /* create an ID for the snapshot */
1249 Guid snapshotId;
1250 snapshotId.create();
1251
1252 Utf8Str strStateFilePath;
1253 /* stateFilePath is null when the machine is not online nor saved */
1254 if ( fTakingSnapshotOnline
1255 || mData->mMachineState == MachineState_Saved)
1256 {
1257 strStateFilePath = Utf8StrFmt("%ls%c{%RTuuid}.sav",
1258 mUserData->mSnapshotFolderFull.raw(),
1259 RTPATH_DELIMITER,
1260 snapshotId.ptr());
1261 /* ensure the directory for the saved state file exists */
1262 HRESULT rc = VirtualBox::ensureFilePathExists(strStateFilePath);
[25149]1263 if (FAILED(rc)) return rc;
[24279]1264 }
1265
1266 /* create a snapshot machine object */
1267 ComObjPtr<SnapshotMachine> snapshotMachine;
1268 snapshotMachine.createObject();
1269 HRESULT rc = snapshotMachine->init(this, snapshotId, strStateFilePath);
1270 AssertComRCReturn(rc, rc);
1271
1272 /* create a snapshot object */
1273 RTTIMESPEC time;
1274 ComObjPtr<Snapshot> pSnapshot;
1275 pSnapshot.createObject();
1276 rc = pSnapshot->init(mParent,
1277 snapshotId,
1278 aName,
1279 aDescription,
1280 *RTTimeNow(&time),
1281 snapshotMachine,
1282 mData->mCurrentSnapshot);
1283 AssertComRCReturnRC(rc);
1284
1285 /* fill in the snapshot data */
1286 mSnapshotData.mLastState = mData->mMachineState;
1287 mSnapshotData.mSnapshot = pSnapshot;
1288
1289 try
1290 {
1291 LogFlowThisFunc(("Creating differencing hard disks (online=%d)...\n",
[24920]1292 fTakingSnapshotOnline));
[24279]1293
1294 // backup the media data so we can recover if things goes wrong along the day;
1295 // the matching commit() is in fixupMedia() during endSnapshot()
1296 mMediaData.backup();
1297
[24301]1298 /* Console::fntTakeSnapshotWorker and friends expects this. */
1299 if (mSnapshotData.mLastState == MachineState_Running)
1300 setMachineState(MachineState_LiveSnapshotting);
1301 else
1302 setMachineState(MachineState_Saving); /** @todo Confusing! Saving is used for both online and offline snapshots. */
[24279]1303
1304 /* create new differencing hard disks and attach them to this machine */
1305 rc = createImplicitDiffs(mUserData->mSnapshotFolderFull,
1306 aConsoleProgress,
1307 1, // operation weight; must be the same as in Console::TakeSnapshot()
1308 !!fTakingSnapshotOnline);
[24920]1309 if (FAILED(rc))
1310 throw rc;
[24279]1311
[24920]1312 if (mSnapshotData.mLastState == MachineState_Saved)
[24279]1313 {
1314 Utf8Str stateFrom = mSSData->mStateFilePath;
1315 Utf8Str stateTo = mSnapshotData.mSnapshot->stateFilePath();
1316
1317 LogFlowThisFunc(("Copying the execution state from '%s' to '%s'...\n",
[24920]1318 stateFrom.raw(), stateTo.raw()));
[24279]1319
1320 aConsoleProgress->SetNextOperation(Bstr(tr("Copying the execution state")),
1321 1); // weight
1322
1323 /* Leave the lock before a lengthy operation (mMachineState is
1324 * MachineState_Saving here) */
1325 alock.leave();
1326
1327 /* copy the state file */
1328 int vrc = RTFileCopyEx(stateFrom.c_str(),
1329 stateTo.c_str(),
1330 0,
1331 progressCallback,
[24556]1332 aConsoleProgress);
[24279]1333 alock.enter();
1334
1335 if (RT_FAILURE(vrc))
[24920]1336 {
1337 /** @todo r=bird: Delete stateTo when appropriate. */
[24279]1338 throw setError(E_FAIL,
1339 tr("Could not copy the state file '%s' to '%s' (%Rrc)"),
1340 stateFrom.raw(),
1341 stateTo.raw(),
1342 vrc);
[24920]1343 }
[24279]1344 }
1345 }
1346 catch (HRESULT hrc)
1347 {
[24920]1348 LogThisFunc(("Caught %Rhrc [%s]\n", hrc, Global::stringifyMachineState(mData->mMachineState) ));
1349 if ( mSnapshotData.mLastState != mData->mMachineState
1350 && ( mSnapshotData.mLastState == MachineState_Running
1351 ? mData->mMachineState == MachineState_LiveSnapshotting
1352 : mData->mMachineState == MachineState_Saving)
1353 )
1354 setMachineState(mSnapshotData.mLastState);
1355
[24279]1356 pSnapshot->uninit();
1357 pSnapshot.setNull();
[24920]1358 mSnapshotData.mLastState = MachineState_Null;
1359 mSnapshotData.mSnapshot.setNull();
1360
[24279]1361 rc = hrc;
1362 }
1363
[24920]1364 if (fTakingSnapshotOnline && SUCCEEDED(rc))
[24279]1365 strStateFilePath.cloneTo(aStateFilePath);
1366 else
1367 *aStateFilePath = NULL;
1368
[24920]1369 LogFlowThisFunc(("LEAVE - %Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
[24279]1370 return rc;
1371}
1372
1373/**
[24299]1374 * Implementation for IInternalMachineControl::beginTakingSnapshot().
1375 *
1376 * Called by the Console when it's done saving the VM state into the snapshot
1377 * (if online) and reconfiguring the hard disks. See BeginTakingSnapshot() above.
1378 *
1379 * This also gets called if the console part of snapshotting failed after the
1380 * BeginTakingSnapshot() call, to clean up the server side.
1381 *
[24279]1382 * @note Locks this object for writing.
[24299]1383 *
1384 * @param aSuccess Whether Console was successful with the client-side snapshot things.
1385 * @return
[24279]1386 */
1387STDMETHODIMP SessionMachine::EndTakingSnapshot(BOOL aSuccess)
1388{
1389 LogFlowThisFunc(("\n"));
1390
1391 AutoCaller autoCaller(this);
1392 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
1393
[25310]1394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[24279]1395
[24301]1396 AssertReturn( !aSuccess
1397 || ( ( mData->mMachineState == MachineState_Saving
1398 || mData->mMachineState == MachineState_LiveSnapshotting)
1399 && mSnapshotData.mLastState != MachineState_Null
1400 && !mSnapshotData.mSnapshot.isNull()
1401 )
1402 , E_FAIL);
[24279]1403
1404 /*
1405 * Restore the state we had when BeginTakingSnapshot() was called,
1406 * Console::fntTakeSnapshotWorker restores its local copy when we return.
[24920]1407 * If the state was Running, then let Console::fntTakeSnapshotWorker do it
1408 * all to avoid races.
[24279]1409 */
1410 if ( mData->mMachineState != mSnapshotData.mLastState
1411 && mSnapshotData.mLastState != MachineState_Running)
1412 setMachineState(mSnapshotData.mLastState);
1413
1414 return endTakingSnapshot(aSuccess);
1415}
1416
1417/**
[24299]1418 * Internal helper method to finalize taking a snapshot. Gets called from
1419 * SessionMachine::EndTakingSnapshot() to finalize the server-side
1420 * parts of snapshotting.
1421 *
1422 * This also gets called from SessionMachine::uninit() if an untaken
1423 * snapshot needs cleaning up.
1424 *
1425 * Expected to be called after completing *all* the tasks related to
1426 * taking the snapshot, either successfully or unsuccessfilly.
1427 *
1428 * @param aSuccess TRUE if the snapshot has been taken successfully.
1429 *
1430 * @note Locks this objects for writing.
[24279]1431 */
[24299]1432HRESULT SessionMachine::endTakingSnapshot(BOOL aSuccess)
[24279]1433{
1434 LogFlowThisFuncEnter();
1435
1436 AutoCaller autoCaller(this);
1437 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
1438
[25310]1439 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
[24299]1440 // saveSettings needs VirtualBox lock
[24279]1441
[24299]1442 AssertReturn(!mSnapshotData.mSnapshot.isNull(), E_FAIL);
[24279]1443
[24299]1444 MultiResult rc(S_OK);
[24279]1445
[24299]1446 ComObjPtr<Snapshot> pOldFirstSnap = mData->mFirstSnapshot;
1447 ComObjPtr<Snapshot> pOldCurrentSnap = mData->mCurrentSnapshot;
[24279]1448
[24299]1449 bool fOnline = Global::IsOnline(mSnapshotData.mLastState);
[24279]1450
[24299]1451 if (aSuccess)
1452 {
1453 // new snapshot becomes the current one
1454 mData->mCurrentSnapshot = mSnapshotData.mSnapshot;
[24279]1455
[24299]1456 /* memorize the first snapshot if necessary */
1457 if (!mData->mFirstSnapshot)
1458 mData->mFirstSnapshot = mData->mCurrentSnapshot;
1459
1460 if (!fOnline)
1461 /* the machine was powered off or saved when taking a snapshot, so
1462 * reset the mCurrentStateModified flag */
1463 mData->mCurrentStateModified = FALSE;
1464
1465 rc = saveSettings();
[24279]1466 }
1467
[24299]1468 if (aSuccess && SUCCEEDED(rc))
1469 {
1470 /* associate old hard disks with the snapshot and do locking/unlocking*/
1471 fixupMedia(true /* aCommit */, fOnline);
[24279]1472
[24299]1473 /* inform callbacks */
1474 mParent->onSnapshotTaken(mData->mUuid,
1475 mSnapshotData.mSnapshot->getId());
1476 }
1477 else
[24279]1478 {
[24299]1479 /* delete all differencing hard disks created (this will also attach
1480 * their parents back by rolling back mMediaData) */
1481 fixupMedia(false /* aCommit */);
[24279]1482
[24299]1483 mData->mFirstSnapshot = pOldFirstSnap; // might have been changed above
1484 mData->mCurrentSnapshot = pOldCurrentSnap; // might have been changed above
[24279]1485
[24299]1486 /* delete the saved state file (it might have been already created) */
1487 if (mSnapshotData.mSnapshot->stateFilePath().length())
1488 RTFileDelete(mSnapshotData.mSnapshot->stateFilePath().c_str());
[24279]1489
[24299]1490 mSnapshotData.mSnapshot->uninit();
1491 }
[24279]1492
[24299]1493 /* clear out the snapshot data */
1494 mSnapshotData.mLastState = MachineState_Null;
1495 mSnapshotData.mSnapshot.setNull();
1496
[24279]1497 LogFlowThisFuncLeave();
[24299]1498 return rc;
[24279]1499}
1500
[24299]1501////////////////////////////////////////////////////////////////////////////////
1502//
1503// RestoreSnapshot methods (SessionMachine and related tasks)
1504//
1505////////////////////////////////////////////////////////////////////////////////
1506
[24279]1507/**
[24299]1508 * Implementation for IInternalMachineControl::restoreSnapshot().
1509 *
1510 * Gets called from Console::RestoreSnapshot(), and that's basically the
1511 * only thing Console does. Restoring a snapshot happens entirely on the
1512 * server side since the machine cannot be running.
1513 *
1514 * This creates a new thread that does the work and returns a progress
1515 * object to the client which is then returned to the caller of
1516 * Console::RestoreSnapshot().
1517 *
1518 * Actual work then takes place in RestoreSnapshotTask::handler().
1519 *
1520 * @note Locks this + children objects for writing!
1521 *
1522 * @param aInitiator in: rhe console on which Console::RestoreSnapshot was called.
1523 * @param aSnapshot in: the snapshot to restore.
1524 * @param aMachineState in: client-side machine state.
1525 * @param aProgress out: progress object to monitor restore thread.
1526 * @return
[24279]1527 */
1528STDMETHODIMP SessionMachine::RestoreSnapshot(IConsole *aInitiator,
1529 ISnapshot *aSnapshot,
1530 MachineState_T *aMachineState,
1531 IProgress **aProgress)
1532{
1533 LogFlowThisFuncEnter();
1534
1535 AssertReturn(aInitiator, E_INVALIDARG);
1536 AssertReturn(aSnapshot && aMachineState && aProgress, E_POINTER);
1537
1538 AutoCaller autoCaller(this);
1539 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
1540
[25310]1541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[24279]1542
[24299]1543 // machine must not be running
[24279]1544 ComAssertRet(!Global::IsOnlineOrTransient(mData->mMachineState),
1545 E_FAIL);
1546
1547 ComObjPtr<Snapshot> pSnapshot(static_cast<Snapshot*>(aSnapshot));
[25152]1548 ComObjPtr<SnapshotMachine> pSnapMachine = pSnapshot->getSnapshotMachine();
[24279]1549
[24299]1550 // create a progress object. The number of operations is:
1551 // 1 (preparing) + # of hard disks + 1 (if we need to copy the saved state file) */
[24279]1552 LogFlowThisFunc(("Going thru snapshot machine attachments to determine progress setup\n"));
1553
1554 ULONG ulOpCount = 1; // one for preparations
1555 ULONG ulTotalWeight = 1; // one for preparations
[24345]1556 for (MediaData::AttachmentList::iterator it = pSnapMachine->mMediaData->mAttachments.begin();
1557 it != pSnapMachine->mMediaData->mAttachments.end();
[24279]1558 ++it)
1559 {
1560 ComObjPtr<MediumAttachment> &pAttach = *it;
[25310]1561 AutoReadLock attachLock(pAttach COMMA_LOCKVAL_SRC_POS);
[24989]1562 if (pAttach->getType() == DeviceType_HardDisk)
[24279]1563 {
1564 ++ulOpCount;
1565 ++ulTotalWeight; // assume one MB weight for each differencing hard disk to manage
[24989]1566 Assert(pAttach->getMedium());
1567 LogFlowThisFunc(("op %d: considering hard disk attachment %s\n", ulOpCount, pAttach->getMedium()->getName().c_str()));
[24279]1568 }
1569 }
1570
1571 ULONG ulStateFileSizeMB = 0;
1572 if (pSnapshot->stateFilePath().length())
1573 {
1574 ++ulOpCount; // one for the saved state
1575
1576 uint64_t ullSize;
1577 int irc = RTFileQuerySize(pSnapshot->stateFilePath().c_str(), &ullSize);
1578 if (!RT_SUCCESS(irc))
1579 // if we can't access the file here, then we'll be doomed later also, so fail right away
1580 setError(E_FAIL, tr("Cannot access state file '%s', runtime error, %Rra"), pSnapshot->stateFilePath().c_str(), irc);
1581 if (ullSize == 0) // avoid division by zero
1582 ullSize = _1M;
1583
1584 ulStateFileSizeMB = (ULONG)(ullSize / _1M);
1585 LogFlowThisFunc(("op %d: saved state file '%s' has %RI64 bytes (%d MB)\n",
1586 ulOpCount, pSnapshot->stateFilePath().raw(), ullSize, ulStateFileSizeMB));
1587
1588 ulTotalWeight += ulStateFileSizeMB;
1589 }
1590
[24345]1591 ComObjPtr<Progress> pProgress;
1592 pProgress.createObject();
1593 pProgress->init(mParent, aInitiator,
1594 BstrFmt(tr("Restoring snapshot '%s'"), pSnapshot->getName().c_str()),
1595 FALSE /* aCancelable */,
1596 ulOpCount,
1597 ulTotalWeight,
1598 Bstr(tr("Restoring machine settings")),
1599 1);
[24279]1600
1601 /* create and start the task on a separate thread (note that it will not
1602 * start working until we release alock) */
[24298]1603 RestoreSnapshotTask *task = new RestoreSnapshotTask(this,
[24345]1604 pProgress,
[24298]1605 pSnapshot,
1606 ulStateFileSizeMB);
[24279]1607 int vrc = RTThreadCreate(NULL,
1608 taskHandler,
1609 (void*)task,
1610 0,
1611 RTTHREADTYPE_MAIN_WORKER,
1612 0,
1613 "RestoreSnap");
1614 if (RT_FAILURE(vrc))
1615 {
1616 delete task;
1617 ComAssertRCRet(vrc, E_FAIL);
1618 }
1619
1620 /* set the proper machine state (note: after creating a Task instance) */
1621 setMachineState(MachineState_RestoringSnapshot);
1622
1623 /* return the progress to the caller */
[24345]1624 pProgress.queryInterfaceTo(aProgress);
[24279]1625
1626 /* return the new state to the caller */
1627 *aMachineState = mData->mMachineState;
1628
1629 LogFlowThisFuncLeave();
1630
1631 return S_OK;
1632}
1633
[24299]1634/**
1635 * Worker method for the restore snapshot thread created by SessionMachine::RestoreSnapshot().
1636 * This method gets called indirectly through SessionMachine::taskHandler() which then
1637 * calls RestoreSnapshotTask::handler().
1638 *
1639 * The RestoreSnapshotTask contains the progress object returned to the console by
1640 * SessionMachine::RestoreSnapshot, through which progress and results are reported.
1641 *
1642 * @note Locks mParent + this object for writing.
1643 *
1644 * @param aTask Task data.
1645 */
1646void SessionMachine::restoreSnapshotHandler(RestoreSnapshotTask &aTask)
[24279]1647{
[24299]1648 LogFlowThisFuncEnter();
[24279]1649
[24299]1650 AutoCaller autoCaller(this);
[24279]1651
[24299]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.pProgress->notifyComplete(E_FAIL,
1658 COM_IIDOF(IMachine),
1659 getComponentName(),
1660 tr("The session has been accidentally closed"));
[24279]1661
[24299]1662 LogFlowThisFuncLeave();
1663 return;
1664 }
1665
1666 /* saveSettings() needs mParent lock */
[25310]1667 AutoWriteLock vboxLock(mParent COMMA_LOCKVAL_SRC_POS);
[24299]1668
1669 /* @todo We don't need mParent lock so far so unlock() it. Better is to
1670 * provide an AutoWriteLock argument that lets create a non-locking
1671 * instance */
[25279]1672 vboxLock.release();
[24299]1673
[25310]1674 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[24299]1675
1676 /* discard all current changes to mUserData (name, OSType etc.) (note that
1677 * the machine is powered off, so there is no need to inform the direct
1678 * session) */
1679 if (isModified())
1680 rollback(false /* aNotify */);
1681
1682 HRESULT rc = S_OK;
1683
1684 bool stateRestored = false;
1685
1686 try
1687 {
1688 /* discard the saved state file if the machine was Saved prior to this
1689 * operation */
1690 if (aTask.machineStateBackup == MachineState_Saved)
1691 {
1692 Assert(!mSSData->mStateFilePath.isEmpty());
1693 RTFileDelete(mSSData->mStateFilePath.c_str());
1694 mSSData->mStateFilePath.setNull();
1695 aTask.modifyBackedUpState(MachineState_PoweredOff);
1696 rc = saveStateSettings(SaveSTS_StateFilePath);
[25149]1697 if (FAILED(rc)) throw rc;
[24299]1698 }
1699
1700 RTTIMESPEC snapshotTimeStamp;
1701 RTTimeSpecSetMilli(&snapshotTimeStamp, 0);
1702
1703 {
[25310]1704 AutoReadLock snapshotLock(aTask.pSnapshot COMMA_LOCKVAL_SRC_POS);
[24299]1705
1706 /* remember the timestamp of the snapshot we're restoring from */
1707 snapshotTimeStamp = aTask.pSnapshot->getTimeStamp();
1708
1709 ComPtr<SnapshotMachine> pSnapshotMachine(aTask.pSnapshot->getSnapshotMachine());
1710
1711 /* copy all hardware data from the snapshot */
1712 copyFrom(pSnapshotMachine);
1713
1714 LogFlowThisFunc(("Restoring hard disks from the snapshot...\n"));
1715
1716 /* restore the attachments from the snapshot */
1717 mMediaData.backup();
1718 mMediaData->mAttachments = pSnapshotMachine->mMediaData->mAttachments;
1719
1720 /* leave the locks before the potentially lengthy operation */
[25279]1721 snapshotLock.release();
[24299]1722 alock.leave();
1723
1724 rc = createImplicitDiffs(mUserData->mSnapshotFolderFull,
1725 aTask.pProgress,
1726 1,
1727 false /* aOnline */);
[25149]1728 if (FAILED(rc)) throw rc;
[24299]1729
1730 alock.enter();
[25279]1731 snapshotLock.acquire();
[24299]1732
1733 /* Note: on success, current (old) hard disks will be
1734 * deassociated/deleted on #commit() called from #saveSettings() at
1735 * the end. On failure, newly created implicit diffs will be
1736 * deleted by #rollback() at the end. */
1737
1738 /* should not have a saved state file associated at this point */
1739 Assert(mSSData->mStateFilePath.isEmpty());
1740
1741 if (!aTask.pSnapshot->stateFilePath().isEmpty())
1742 {
1743 Utf8Str snapStateFilePath = aTask.pSnapshot->stateFilePath();
1744
1745 Utf8Str stateFilePath = Utf8StrFmt("%ls%c{%RTuuid}.sav",
1746 mUserData->mSnapshotFolderFull.raw(),
1747 RTPATH_DELIMITER,
1748 mData->mUuid.raw());
1749
1750 LogFlowThisFunc(("Copying saved state file from '%s' to '%s'...\n",
1751 snapStateFilePath.raw(), stateFilePath.raw()));
1752
1753 aTask.pProgress->SetNextOperation(Bstr(tr("Restoring the execution state")),
1754 aTask.m_ulStateFileSizeMB); // weight
1755
1756 /* leave the lock before the potentially lengthy operation */
[25279]1757 snapshotLock.release();
[24299]1758 alock.leave();
1759
1760 /* copy the state file */
1761 int vrc = RTFileCopyEx(snapStateFilePath.c_str(),
1762 stateFilePath.c_str(),
1763 0,
1764 progressCallback,
[24556]1765 static_cast<IProgress*>(aTask.pProgress));
[24299]1766
1767 alock.enter();
[25279]1768 snapshotLock.acquire();
[24299]1769
1770 if (RT_SUCCESS(vrc))
1771 mSSData->mStateFilePath = stateFilePath;
1772 else
1773 throw setError(E_FAIL,
1774 tr("Could not copy the state file '%s' to '%s' (%Rrc)"),
1775 snapStateFilePath.raw(),
1776 stateFilePath.raw(),
1777 vrc);
1778 }
1779
1780 LogFlowThisFunc(("Setting new current snapshot {%RTuuid}\n", aTask.pSnapshot->getId().raw()));
1781 /* make the snapshot we restored from the current snapshot */
1782 mData->mCurrentSnapshot = aTask.pSnapshot;
1783 }
1784
1785 /* grab differencing hard disks from the old attachments that will
1786 * become unused and need to be auto-deleted */
1787
1788 std::list< ComObjPtr<MediumAttachment> > llDiffAttachmentsToDelete;
1789
1790 for (MediaData::AttachmentList::const_iterator it = mMediaData.backedUpData()->mAttachments.begin();
1791 it != mMediaData.backedUpData()->mAttachments.end();
1792 ++it)
1793 {
1794 ComObjPtr<MediumAttachment> pAttach = *it;
[24989]1795 ComObjPtr<Medium> pMedium = pAttach->getMedium();
[24299]1796
1797 /* while the hard disk is attached, the number of children or the
1798 * parent cannot change, so no lock */
1799 if ( !pMedium.isNull()
[24989]1800 && pAttach->getType() == DeviceType_HardDisk
1801 && !pMedium->getParent().isNull()
1802 && pMedium->getChildren().size() == 0
[24299]1803 )
1804 {
[24989]1805 LogFlowThisFunc(("Picked differencing image '%s' for deletion\n", pMedium->getName().raw()));
[24299]1806
1807 llDiffAttachmentsToDelete.push_back(pAttach);
1808 }
1809 }
1810
1811 int saveFlags = 0;
1812
1813 /* @todo saveSettings() below needs a VirtualBox write lock and we need
1814 * to leave this object's lock to do this to follow the {parent-child}
1815 * locking rule. This is the last chance to do that while we are still
1816 * in a protective state which allows us to temporarily leave the lock*/
[25279]1817 alock.release();
1818 vboxLock.acquire();
1819 alock.acquire();
[24299]1820
1821 /* we have already discarded the current state, so set the execution
1822 * state accordingly no matter of the discard snapshot result */
1823 if (!mSSData->mStateFilePath.isEmpty())
1824 setMachineState(MachineState_Saved);
1825 else
1826 setMachineState(MachineState_PoweredOff);
1827
1828 updateMachineStateOnClient();
1829 stateRestored = true;
1830
1831 /* assign the timestamp from the snapshot */
1832 Assert(RTTimeSpecGetMilli (&snapshotTimeStamp) != 0);
1833 mData->mLastStateChange = snapshotTimeStamp;
1834
1835 // detach the current-state diffs that we detected above and build a list of
1836 // images to delete _after_ saveSettings()
1837
[25151]1838 MediaList llDiffsToDelete;
[24299]1839
1840 for (std::list< ComObjPtr<MediumAttachment> >::iterator it = llDiffAttachmentsToDelete.begin();
1841 it != llDiffAttachmentsToDelete.end();
1842 ++it)
1843 {
1844 ComObjPtr<MediumAttachment> pAttach = *it; // guaranteed to have only attachments where medium != NULL
[24989]1845 ComObjPtr<Medium> pMedium = pAttach->getMedium();
[24299]1846
[25310]1847 AutoWriteLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
[24299]1848
[24989]1849 LogFlowThisFunc(("Detaching old current state in differencing image '%s'\n", pMedium->getName().raw()));
[24299]1850
[24458]1851 // Normally we "detach" the medium by removing the attachment object
1852 // from the current machine data; saveSettings() below would then
[24447]1853 // compare the current machine data with the one in the backup
[24458]1854 // and actually call Medium::detachFrom(). But that works only half
1855 // the time in our case so instead we force a detachment here:
1856 // remove from machine data
[24299]1857 mMediaData->mAttachments.remove(pAttach);
[24458]1858 // remove it from the backup or else saveSettings will try to detach
1859 // it again and assert
1860 mMediaData.backedUpData()->mAttachments.remove(pAttach);
1861 // then clean up backrefs
1862 pMedium->detachFrom(mData->mUuid);
[24299]1863
1864 llDiffsToDelete.push_back(pMedium);
1865 }
1866
1867 // save all settings, reset the modified flag and commit;
1868 rc = saveSettings(SaveS_ResetCurStateModified | saveFlags);
[25149]1869 if (FAILED(rc)) throw rc;
1870
[24299]1871 // from here on we cannot roll back on failure any more
1872
[25151]1873 for (MediaList::iterator it = llDiffsToDelete.begin();
[24299]1874 it != llDiffsToDelete.end();
1875 ++it)
1876 {
1877 ComObjPtr<Medium> &pMedium = *it;
[24989]1878 LogFlowThisFunc(("Deleting old current state in differencing image '%s'\n", pMedium->getName().raw()));
[24299]1879
1880 HRESULT rc2 = pMedium->deleteStorageAndWait();
1881 // ignore errors here because we cannot roll back after saveSettings() above
1882 if (SUCCEEDED(rc2))
1883 pMedium->uninit();
1884 }
1885 }
1886 catch (HRESULT aRC)
1887 {
1888 rc = aRC;
1889 }
1890
1891 if (FAILED(rc))
1892 {
1893 /* preserve existing error info */
1894 ErrorInfoKeeper eik;
1895
1896 /* undo all changes on failure */
1897 rollback(false /* aNotify */);
1898
1899 if (!stateRestored)
1900 {
1901 /* restore the machine state */
1902 setMachineState(aTask.machineStateBackup);
1903 updateMachineStateOnClient();
1904 }
1905 }
1906
1907 /* set the result (this will try to fetch current error info on failure) */
1908 aTask.pProgress->notifyComplete(rc);
1909
1910 if (SUCCEEDED(rc))
1911 mParent->onSnapshotDeleted(mData->mUuid, Guid());
1912
1913 LogFlowThisFunc(("Done restoring snapshot (rc=%08X)\n", rc));
1914
1915 LogFlowThisFuncLeave();
[24279]1916}
1917
[24299]1918////////////////////////////////////////////////////////////////////////////////
1919//
1920// DeleteSnapshot methods (SessionMachine and related tasks)
1921//
1922////////////////////////////////////////////////////////////////////////////////
1923
[24279]1924/**
[24299]1925 * Implementation for IInternalMachineControl::deleteSnapshot().
[24279]1926 *
[24299]1927 * Gets called from Console::DeleteSnapshot(), and that's basically the
1928 * only thing Console does. Deleting a snapshot happens entirely on the
1929 * server side since the machine cannot be running.
[24279]1930 *
[24299]1931 * This creates a new thread that does the work and returns a progress
1932 * object to the client which is then returned to the caller of
1933 * Console::DeleteSnapshot().
[24279]1934 *
[24299]1935 * Actual work then takes place in DeleteSnapshotTask::handler().
1936 *
1937 * @note Locks mParent + this + children objects for writing!
[24279]1938 */
[24299]1939STDMETHODIMP SessionMachine::DeleteSnapshot(IConsole *aInitiator,
1940 IN_BSTR aId,
1941 MachineState_T *aMachineState,
1942 IProgress **aProgress)
[24279]1943{
1944 LogFlowThisFuncEnter();
1945
[24299]1946 Guid id(aId);
1947 AssertReturn(aInitiator && !id.isEmpty(), E_INVALIDARG);
1948 AssertReturn(aMachineState && aProgress, E_POINTER);
1949
[24279]1950 AutoCaller autoCaller(this);
1951 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
1952
[24299]1953 /* saveSettings() needs mParent lock */
[25310]1954 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
[24279]1955
[24299]1956 // machine must not be running
1957 ComAssertRet(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
[24279]1958
[25310]1959 AutoWriteLock treeLock(snapshotsTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
[24279]1960
[24345]1961 ComObjPtr<Snapshot> pSnapshot;
1962 HRESULT rc = findSnapshot(id, pSnapshot, true /* aSetError */);
[25149]1963 if (FAILED(rc)) return rc;
[24279]1964
[25310]1965 AutoWriteLock snapshotLock(pSnapshot COMMA_LOCKVAL_SRC_POS);
[24279]1966
[24345]1967 size_t childrenCount = pSnapshot->getChildrenCount();
[24299]1968 if (childrenCount > 1)
1969 return setError(VBOX_E_INVALID_OBJECT_STATE,
1970 tr("Snapshot '%s' of the machine '%ls' cannot be deleted. because it has %d child snapshots, which is more than the one snapshot allowed for deletion"),
[24345]1971 pSnapshot->getName().c_str(),
[24299]1972 mUserData->mName.raw(),
1973 childrenCount);
1974
1975 /* If the snapshot being discarded is the current one, ensure current
1976 * settings are committed and saved.
1977 */
[24345]1978 if (pSnapshot == mData->mCurrentSnapshot)
[24279]1979 {
[24299]1980 if (isModified())
1981 {
1982 rc = saveSettings();
[25149]1983 if (FAILED(rc)) return rc;
[24299]1984 }
1985 }
[24279]1986
[25152]1987 ComObjPtr<SnapshotMachine> pSnapMachine = pSnapshot->getSnapshotMachine();
[24345]1988
[24299]1989 /* create a progress object. The number of operations is:
[24345]1990 * 1 (preparing) + 1 if the snapshot is online + # of normal hard disks
[24299]1991 */
[24345]1992 LogFlowThisFunc(("Going thru snapshot machine attachments to determine progress setup\n"));
[24279]1993
[24345]1994 ULONG ulOpCount = 1; // one for preparations
1995 ULONG ulTotalWeight = 1; // one for preparations
1996
1997 if (pSnapshot->stateFilePath().length())
1998 {
1999 ++ulOpCount;
2000 ++ulTotalWeight; // assume 1 MB for deleting the state file
2001 }
2002
2003 // count normal hard disks and add their sizes to the weight
2004 for (MediaData::AttachmentList::iterator it = pSnapMachine->mMediaData->mAttachments.begin();
2005 it != pSnapMachine->mMediaData->mAttachments.end();
2006 ++it)
2007 {
2008 ComObjPtr<MediumAttachment> &pAttach = *it;
[25310]2009 AutoReadLock attachLock(pAttach COMMA_LOCKVAL_SRC_POS);
[24989]2010 if (pAttach->getType() == DeviceType_HardDisk)
[24345]2011 {
[24989]2012 ComObjPtr<Medium> pHD = pAttach->getMedium();
2013 Assert(pHD);
[25310]2014 AutoReadLock mlock(pHD COMMA_LOCKVAL_SRC_POS);
[24989]2015 if (pHD->getType() == MediumType_Normal)
[24345]2016 {
2017 ++ulOpCount;
[24992]2018 ulTotalWeight += (ULONG)(pHD->getSize() / _1M);
[24345]2019 }
[24989]2020 LogFlowThisFunc(("op %d: considering hard disk attachment %s\n", ulOpCount, pHD->getName().c_str()));
[24345]2021 }
2022 }
2023
2024 ComObjPtr<Progress> pProgress;
2025 pProgress.createObject();
2026 pProgress->init(mParent, aInitiator,
2027 BstrFmt(tr("Deleting snapshot '%s'"), pSnapshot->getName().c_str()),
2028 FALSE /* aCancelable */,
2029 ulOpCount,
2030 ulTotalWeight,
2031 Bstr(tr("Setting up")),
2032 1);
2033
[24299]2034 /* create and start the task on a separate thread */
[24345]2035 DeleteSnapshotTask *task = new DeleteSnapshotTask(this, pProgress, pSnapshot);
[24299]2036 int vrc = RTThreadCreate(NULL,
2037 taskHandler,
2038 (void*)task,
2039 0,
2040 RTTHREADTYPE_MAIN_WORKER,
2041 0,
2042 "DeleteSnapshot");
2043 if (RT_FAILURE(vrc))
[24279]2044 {
[24299]2045 delete task;
2046 return E_FAIL;
[24279]2047 }
2048
[24299]2049 /* set the proper machine state (note: after creating a Task instance) */
2050 setMachineState(MachineState_DeletingSnapshot);
[24279]2051
[24299]2052 /* return the progress to the caller */
[24345]2053 pProgress.queryInterfaceTo(aProgress);
[24279]2054
[24299]2055 /* return the new state to the caller */
2056 *aMachineState = mData->mMachineState;
[24279]2057
[24299]2058 LogFlowThisFuncLeave();
[24279]2059
[24299]2060 return S_OK;
[24279]2061}
2062
2063/**
2064 * Helper struct for SessionMachine::deleteSnapshotHandler().
2065 */
2066struct MediumDiscardRec
2067{
[24298]2068 MediumDiscardRec()
2069 : chain(NULL)
2070 {}
[24279]2071
[24298]2072 MediumDiscardRec(const ComObjPtr<Medium> &aHd,
2073 Medium::MergeChain *aChain = NULL)
2074 : hd(aHd),
2075 chain(aChain)
2076 {}
[24279]2077
[24298]2078 MediumDiscardRec(const ComObjPtr<Medium> &aHd,
2079 Medium::MergeChain *aChain,
2080 const ComObjPtr<Medium> &aReplaceHd,
2081 const ComObjPtr<MediumAttachment> &aReplaceHda,
2082 const Guid &aSnapshotId)
2083 : hd(aHd),
[24299]2084 chain(aChain),
[24298]2085 replaceHd(aReplaceHd),
2086 replaceHda(aReplaceHda),
2087 snapshotId(aSnapshotId)
2088 {}
[24279]2089
2090 ComObjPtr<Medium> hd;
2091 Medium::MergeChain *chain;
2092 /* these are for the replace hard disk case: */
2093 ComObjPtr<Medium> replaceHd;
2094 ComObjPtr<MediumAttachment> replaceHda;
2095 Guid snapshotId;
2096};
2097
2098typedef std::list <MediumDiscardRec> MediumDiscardRecList;
2099
2100/**
[24299]2101 * Worker method for the delete snapshot thread created by SessionMachine::DeleteSnapshot().
2102 * This method gets called indirectly through SessionMachine::taskHandler() which then
2103 * calls DeleteSnapshotTask::handler().
[24279]2104 *
[24299]2105 * The DeleteSnapshotTask contains the progress object returned to the console by
2106 * SessionMachine::DeleteSnapshot, through which progress and results are reported.
[24279]2107 *
2108 * @note Locks mParent + this + child objects for writing!
[24299]2109 *
2110 * @param aTask Task data.
[24279]2111 */
2112void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask)
2113{
2114 LogFlowThisFuncEnter();
2115
2116 AutoCaller autoCaller(this);
2117
2118 LogFlowThisFunc(("state=%d\n", autoCaller.state()));
2119 if (!autoCaller.isOk())
2120 {
2121 /* we might have been uninitialized because the session was accidentally
2122 * closed by the client, so don't assert */
[24298]2123 aTask.pProgress->notifyComplete(E_FAIL,
2124 COM_IIDOF(IMachine),
2125 getComponentName(),
2126 tr("The session has been accidentally closed"));
[24279]2127 LogFlowThisFuncLeave();
2128 return;
2129 }
2130
2131 /* Locking order: */
2132 AutoMultiWriteLock3 alock(this->lockHandle(),
2133 this->snapshotsTreeLockHandle(),
[25310]2134 aTask.pSnapshot->lockHandle()
2135 COMMA_LOCKVAL_SRC_POS);
[24279]2136
[25152]2137 ComObjPtr<SnapshotMachine> pSnapMachine = aTask.pSnapshot->getSnapshotMachine();
[24279]2138 /* no need to lock the snapshot machine since it is const by definiton */
2139
2140 HRESULT rc = S_OK;
2141
2142 /* save the snapshot ID (for callbacks) */
[24989]2143 Guid snapshotId1 = aTask.pSnapshot->getId();
[24279]2144
2145 MediumDiscardRecList toDiscard;
2146
2147 bool settingsChanged = false;
2148
2149 try
2150 {
2151 /* first pass: */
2152 LogFlowThisFunc(("1: Checking hard disk merge prerequisites...\n"));
2153
[24433]2154 // go thru the attachments of the snapshot machine
2155 // (the media in here point to the disk states _before_ the snapshot
2156 // was taken, i.e. the state we're restoring to; for each such
2157 // medium, we will need to merge it with its one and only child (the
2158 // diff image holding the changes written after the snapshot was taken)
[24345]2159 for (MediaData::AttachmentList::iterator it = pSnapMachine->mMediaData->mAttachments.begin();
2160 it != pSnapMachine->mMediaData->mAttachments.end();
2161 ++it)
[24279]2162 {
[24345]2163 ComObjPtr<MediumAttachment> &pAttach = *it;
[25310]2164 AutoReadLock attachLock(pAttach COMMA_LOCKVAL_SRC_POS);
[24989]2165 if (pAttach->getType() == DeviceType_HardDisk)
[24345]2166 {
[24989]2167 Assert(pAttach->getMedium());
2168 ComObjPtr<Medium> pHD = pAttach->getMedium();
[24433]2169 // do not lock, prepareDiscared() has a write lock which will hang otherwise
[24279]2170
[24509]2171#ifdef DEBUG
2172 pHD->dumpBackRefs();
2173#endif
2174
[24345]2175 Medium::MergeChain *chain = NULL;
[24279]2176
[24433]2177 // needs to be discarded (merged with the child if any), check prerequisites
[24345]2178 rc = pHD->prepareDiscard(chain);
[25149]2179 if (FAILED(rc)) throw rc;
[24279]2180
[24433]2181 // for simplicity, we merge pHd onto its child (forward merge), not the
2182 // other way round, because that saves us from updating the attachments
2183 // for the machine that follows the snapshot (next snapshot or real machine),
2184 // unless it's a base image:
2185
[24989]2186 if ( pHD->getParent().isNull()
[24433]2187 && chain != NULL
2188 )
[24345]2189 {
[24433]2190 // parent is null -> this disk is a base hard disk: we will
2191 // then do a backward merge, i.e. merge its only child onto
2192 // the base disk; prepareDiscard() does necessary checks.
2193 // So here we need then to update the attachment that refers
2194 // to the child and have it point to the parent instead
[24279]2195
[24345]2196 /* The below assert would be nice but I don't want to move
[24433]2197 * Medium::MergeChain to the header just for that
2198 * Assert (!chain->isForward()); */
[24279]2199
[24433]2200 // prepareDiscard() should have raised an error already
2201 // if there was more than one child
[24989]2202 Assert(pHD->getChildren().size() == 1);
[24279]2203
[24989]2204 ComObjPtr<Medium> pReplaceHD = pHD->getChildren().front();
[24279]2205
[24433]2206 const Guid *pReplaceMachineId = pReplaceHD->getFirstMachineBackrefId();
[24572]2207 NOREF(pReplaceMachineId);
[24433]2208 Assert(pReplaceMachineId);
2209 Assert(*pReplaceMachineId == mData->mUuid);
[24279]2210
[24345]2211 Guid snapshotId;
[24433]2212 const Guid *pSnapshotId = pReplaceHD->getFirstMachineBackrefSnapshotId();
[24345]2213 if (pSnapshotId)
2214 snapshotId = *pSnapshotId;
[24279]2215
[24345]2216 HRESULT rc2 = S_OK;
[24279]2217
[25279]2218 attachLock.release();
[24433]2219
2220 // First we must detach the child (otherwise mergeTo() called
2221 // by discard() will assert because it will be going to delete
2222 // the child), so adjust the backreferences:
2223 // 1) detach the first child hard disk
2224 rc2 = pReplaceHD->detachFrom(mData->mUuid, snapshotId);
[24345]2225 AssertComRC(rc2);
[24433]2226 // 2) attach to machine and snapshot
[24345]2227 rc2 = pHD->attachTo(mData->mUuid, snapshotId);
2228 AssertComRC(rc2);
[24279]2229
[24345]2230 /* replace the hard disk in the attachment object */
2231 if (snapshotId.isEmpty())
2232 {
2233 /* in current state */
[24433]2234 AssertBreak(pAttach = findAttachment(mMediaData->mAttachments, pReplaceHD));
[24345]2235 }
2236 else
2237 {
2238 /* in snapshot */
2239 ComObjPtr<Snapshot> snapshot;
2240 rc2 = findSnapshot(snapshotId, snapshot);
2241 AssertComRC(rc2);
[24279]2242
[24345]2243 /* don't lock the snapshot; cannot be modified outside */
2244 MediaData::AttachmentList &snapAtts = snapshot->getSnapshotMachine()->mMediaData->mAttachments;
[24433]2245 AssertBreak(pAttach = findAttachment(snapAtts, pReplaceHD));
[24345]2246 }
[24279]2247
[25310]2248 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
[24345]2249 pAttach->updateMedium(pHD, false /* aImplicit */);
[24279]2250
[24345]2251 toDiscard.push_back(MediumDiscardRec(pHD,
2252 chain,
[24433]2253 pReplaceHD,
[24345]2254 pAttach,
2255 snapshotId));
2256 continue;
[24279]2257 }
2258
[24345]2259 toDiscard.push_back(MediumDiscardRec(pHD, chain));
[24279]2260 }
2261 }
2262
2263 /* Now we checked that we can successfully merge all normal hard disks
2264 * (unless a runtime error like end-of-disc happens). Prior to
2265 * performing the actual merge, we want to discard the snapshot itself
2266 * and remove it from the XML file to make sure that a possible merge
2267 * ruintime error will not make this snapshot inconsistent because of
2268 * the partially merged or corrupted hard disks */
2269
2270 /* second pass: */
2271 LogFlowThisFunc(("2: Discarding snapshot...\n"));
2272
2273 {
[25152]2274 ComObjPtr<Snapshot> parentSnapshot = aTask.pSnapshot->getParent();
[24298]2275 Utf8Str stateFilePath = aTask.pSnapshot->stateFilePath();
[24279]2276
2277 /* Note that discarding the snapshot will deassociate it from the
2278 * hard disks which will allow the merge+delete operation for them*/
[24298]2279 aTask.pSnapshot->beginDiscard();
2280 aTask.pSnapshot->uninit();
[24279]2281
2282 rc = saveAllSnapshots();
[25149]2283 if (FAILED(rc)) throw rc;
[24279]2284
2285 /// @todo (dmik)
2286 // if we implement some warning mechanism later, we'll have
2287 // to return a warning if the state file path cannot be deleted
[24291]2288 if (!stateFilePath.isEmpty())
[24279]2289 {
[24298]2290 aTask.pProgress->SetNextOperation(Bstr(tr("Discarding the execution state")),
2291 1); // weight
[24279]2292
[24291]2293 RTFileDelete(stateFilePath.c_str());
[24279]2294 }
2295
2296 /// @todo NEWMEDIA to provide a good level of fauilt tolerance, we
2297 /// should restore the shapshot in the snapshot tree if
2298 /// saveSnapshotSettings fails. Actually, we may call
2299 /// #saveSnapshotSettings() with a special flag that will tell it to
2300 /// skip the given snapshot as if it would have been discarded and
2301 /// only actually discard it if the save operation succeeds.
2302 }
2303
2304 /* here we come when we've irrevesibly discarded the snapshot which
2305 * means that the VM settigns (our relevant changes to mData) need to be
2306 * saved too */
2307 /// @todo NEWMEDIA maybe save everything in one operation in place of
2308 /// saveSnapshotSettings() above
2309 settingsChanged = true;
2310
2311 /* third pass: */
2312 LogFlowThisFunc(("3: Performing actual hard disk merging...\n"));
2313
2314 /* leave the locks before the potentially lengthy operation */
2315 alock.leave();
2316
2317 /// @todo NEWMEDIA turn the following errors into warnings because the
2318 /// snapshot itself has been already deleted (and interpret these
2319 /// warnings properly on the GUI side)
2320
2321 for (MediumDiscardRecList::iterator it = toDiscard.begin();
2322 it != toDiscard.end();)
2323 {
[24345]2324 rc = it->hd->discard(aTask.pProgress,
[24992]2325 (ULONG)(it->hd->getSize() / _1M), // weight
[24345]2326 it->chain);
[25149]2327 if (FAILED(rc)) throw rc;
[24279]2328
2329 /* prevent from calling cancelDiscard() */
[24345]2330 it = toDiscard.erase(it);
[24279]2331 }
2332
[24345]2333 LogFlowThisFunc(("Entering locks again...\n"));
[24279]2334 alock.enter();
[24345]2335 LogFlowThisFunc(("Entered locks OK\n"));
[24279]2336 }
2337 catch (HRESULT aRC) { rc = aRC; }
2338
2339 if (FAILED(rc))
2340 {
2341 HRESULT rc2 = S_OK;
2342
2343 /* un-prepare the remaining hard disks */
2344 for (MediumDiscardRecList::const_iterator it = toDiscard.begin();
2345 it != toDiscard.end(); ++it)
2346 {
2347 it->hd->cancelDiscard (it->chain);
2348
2349 if (!it->replaceHd.isNull())
2350 {
2351 /* undo hard disk replacement */
2352
[24989]2353 rc2 = it->replaceHd->attachTo(mData->mUuid, it->snapshotId);
[24279]2354 AssertComRC(rc2);
2355
2356 rc2 = it->hd->detachFrom (mData->mUuid, it->snapshotId);
2357 AssertComRC(rc2);
2358
[25310]2359 AutoWriteLock attLock(it->replaceHda COMMA_LOCKVAL_SRC_POS);
[24279]2360 it->replaceHda->updateMedium(it->replaceHd, false /* aImplicit */);
2361 }
2362 }
2363 }
2364
[25279]2365 alock.release();
[24354]2366
2367 // whether we were successful or not, we need to set the machine
2368 // state and save the machine settings;
[24279]2369 {
[24354]2370 // preserve existing error info so that the result can
2371 // be properly reported to the progress object below
[24298]2372 ErrorInfoKeeper eik;
[24279]2373
[24354]2374 // restore the machine state that was saved when the
2375 // task was started
[24298]2376 setMachineState(aTask.machineStateBackup);
2377 updateMachineStateOnClient();
[24279]2378
[24298]2379 if (settingsChanged)
[24354]2380 {
2381 // saveSettings needs VirtualBox write lock in addition to our own
2382 // (parent -> child locking order!)
[25310]2383 AutoWriteLock vboxLock(mParent COMMA_LOCKVAL_SRC_POS);
[25279]2384 alock.acquire();
[24354]2385
[24298]2386 saveSettings(SaveS_InformCallbacksAnyway);
[24354]2387 }
[24279]2388 }
2389
[24354]2390 // report the result (this will try to fetch current error info on failure)
2391 aTask.pProgress->notifyComplete(rc);
2392
[24279]2393 if (SUCCEEDED(rc))
[24989]2394 mParent->onSnapshotDeleted(mData->mUuid, snapshotId1);
[24279]2395
[24298]2396 LogFlowThisFunc(("Done deleting snapshot (rc=%08X)\n", rc));
[24279]2397 LogFlowThisFuncLeave();
2398}
2399
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use