VirtualBox

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

Last change on this file since 23702 was 23341, checked in by vboxsync, 15 years ago

Main: opimization for r52856

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.0 KB
Line 
1/** @file
2 *
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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
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.
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.
20 */
21
22#include "SnapshotImpl.h"
23
24#include "MachineImpl.h"
25#include "Logging.h"
26
27#include <iprt/path.h>
28#include <VBox/param.h>
29#include <VBox/err.h>
30
31#include <list>
32
33#include <VBox/settings.h>
34
35////////////////////////////////////////////////////////////////////////////////
36//
37// Snapshot private data definition
38//
39////////////////////////////////////////////////////////////////////////////////
40
41typedef std::list< ComObjPtr<Snapshot> > SnapshotsList;
42
43struct Snapshot::Data
44{
45 Data()
46 {
47 RTTimeSpecSetMilli(&timeStamp, 0);
48 };
49
50 ~Data()
51 {}
52
53 Guid uuid;
54 Utf8Str strName;
55 Utf8Str strDescription;
56 RTTIMESPEC timeStamp;
57 ComObjPtr<SnapshotMachine> pMachine;
58
59 SnapshotsList llChildren; // protected by VirtualBox::snapshotTreeLockHandle()
60};
61
62////////////////////////////////////////////////////////////////////////////////
63//
64// Constructor / destructor
65//
66////////////////////////////////////////////////////////////////////////////////
67
68HRESULT Snapshot::FinalConstruct()
69{
70 LogFlowMember (("Snapshot::FinalConstruct()\n"));
71 return S_OK;
72}
73
74void Snapshot::FinalRelease()
75{
76 LogFlowMember (("Snapshot::FinalRelease()\n"));
77 uninit();
78}
79
80/**
81 * Initializes the instance
82 *
83 * @param aId id of the snapshot
84 * @param aName name of the snapshot
85 * @param aDescription name of the snapshot (NULL if no description)
86 * @param aTimeStamp timestamp of the snapshot, in ms since 1970-01-01 UTC
87 * @param aMachine machine associated with this snapshot
88 * @param aParent parent snapshot (NULL if no parent)
89 */
90HRESULT Snapshot::init(VirtualBox *aVirtualBox,
91 const Guid &aId,
92 const Utf8Str &aName,
93 const Utf8Str &aDescription,
94 const RTTIMESPEC &aTimeStamp,
95 SnapshotMachine *aMachine,
96 Snapshot *aParent)
97{
98 LogFlowMember(("Snapshot::init(uuid: %s, aParent->uuid=%s)\n", aId.toString().c_str(), (aParent) ? aParent->m->uuid.toString().c_str() : ""));
99
100 ComAssertRet (!aId.isEmpty() && !aName.isEmpty() && aMachine, E_INVALIDARG);
101
102 /* Enclose the state transition NotReady->InInit->Ready */
103 AutoInitSpan autoInitSpan(this);
104 AssertReturn(autoInitSpan.isOk(), E_FAIL);
105
106 m = new Data;
107
108 /* share parent weakly */
109 unconst(mVirtualBox) = aVirtualBox;
110
111 mParent = aParent;
112
113 m->uuid = aId;
114 m->strName = aName;
115 m->strDescription = aDescription;
116 m->timeStamp = aTimeStamp;
117 m->pMachine = aMachine;
118
119 if (aParent)
120 aParent->m->llChildren.push_back(this);
121
122 /* Confirm a successful initialization when it's the case */
123 autoInitSpan.setSucceeded();
124
125 return S_OK;
126}
127
128/**
129 * Uninitializes the instance and sets the ready flag to FALSE.
130 * Called either from FinalRelease(), by the parent when it gets destroyed,
131 * or by a third party when it decides this object is no more valid.
132 */
133void Snapshot::uninit()
134{
135 LogFlowMember (("Snapshot::uninit()\n"));
136
137 /* Enclose the state transition Ready->InUninit->NotReady */
138 AutoUninitSpan autoUninitSpan(this);
139 if (autoUninitSpan.uninitDone())
140 return;
141
142 // uninit all children
143 SnapshotsList::iterator it;
144 for (it = m->llChildren.begin();
145 it != m->llChildren.end();
146 ++it)
147 {
148 Snapshot *pChild = *it;
149 pChild->mParent.setNull();
150 pChild->uninit();
151 }
152 m->llChildren.clear(); // this unsets all the ComPtrs and probably calls delete
153
154 if (mParent)
155 {
156 SnapshotsList &llParent = mParent->m->llChildren;
157 for (it = llParent.begin();
158 it != llParent.end();
159 ++it)
160 {
161 Snapshot *pParentsChild = *it;
162 if (this == pParentsChild)
163 {
164 llParent.erase(it);
165 break;
166 }
167 }
168
169 mParent.setNull();
170 }
171
172 if (m->pMachine)
173 {
174 m->pMachine->uninit();
175 m->pMachine.setNull();
176 }
177
178 delete m;
179 m = NULL;
180}
181
182/**
183 * Discards the current snapshot by removing it from the tree of snapshots
184 * and reparenting its children.
185 *
186 * After this, the caller must call uninit() on the snapshot. We can't call
187 * that from here because if we do, the AutoUninitSpan waits forever for
188 * the number of callers to become 0 (it is 1 because of the AutoCaller in here).
189 *
190 * NOTE: this does NOT lock the snapshot, it is assumed that the caller has
191 * locked a) the machine and b) the snapshots tree in write mode!
192 */
193void Snapshot::beginDiscard()
194{
195 AutoCaller autoCaller(this);
196 if (FAILED(autoCaller.rc()))
197 return;
198
199 /* for now, the snapshot must have only one child when discarded,
200 * or no children at all */
201 AssertReturnVoid(m->llChildren.size() <= 1);
202
203 ComObjPtr<Snapshot> parentSnapshot = parent();
204
205 /// @todo (dmik):
206 // when we introduce clones later, discarding the snapshot
207 // will affect the current and first snapshots of clones, if they are
208 // direct children of this snapshot. So we will need to lock machines
209 // associated with child snapshots as well and update mCurrentSnapshot
210 // and/or mFirstSnapshot fields.
211
212 if (this == m->pMachine->mData->mCurrentSnapshot)
213 {
214 m->pMachine->mData->mCurrentSnapshot = parentSnapshot;
215
216 /* we've changed the base of the current state so mark it as
217 * modified as it no longer guaranteed to be its copy */
218 m->pMachine->mData->mCurrentStateModified = TRUE;
219 }
220
221 if (this == m->pMachine->mData->mFirstSnapshot)
222 {
223 if (m->llChildren.size() == 1)
224 {
225 ComObjPtr<Snapshot> childSnapshot = m->llChildren.front();
226 m->pMachine->mData->mFirstSnapshot = childSnapshot;
227 }
228 else
229 m->pMachine->mData->mFirstSnapshot.setNull();
230 }
231
232 // reparent our children
233 for (SnapshotsList::const_iterator it = m->llChildren.begin();
234 it != m->llChildren.end();
235 ++it)
236 {
237 ComObjPtr<Snapshot> child = *it;
238 AutoWriteLock childLock(child);
239
240 child->mParent = mParent;
241 if (mParent)
242 mParent->m->llChildren.push_back(child);
243 }
244
245 // clear our own children list (since we reparented the children)
246 m->llChildren.clear();
247}
248
249////////////////////////////////////////////////////////////////////////////////
250//
251// ISnapshot public methods
252//
253////////////////////////////////////////////////////////////////////////////////
254
255STDMETHODIMP Snapshot::COMGETTER(Id) (BSTR *aId)
256{
257 CheckComArgOutPointerValid(aId);
258
259 AutoCaller autoCaller(this);
260 CheckComRCReturnRC(autoCaller.rc());
261
262 AutoReadLock alock(this);
263
264 m->uuid.toUtf16().cloneTo(aId);
265 return S_OK;
266}
267
268STDMETHODIMP Snapshot::COMGETTER(Name) (BSTR *aName)
269{
270 CheckComArgOutPointerValid(aName);
271
272 AutoCaller autoCaller(this);
273 CheckComRCReturnRC(autoCaller.rc());
274
275 AutoReadLock alock(this);
276
277 m->strName.cloneTo(aName);
278 return S_OK;
279}
280
281/**
282 * @note Locks this object for writing, then calls Machine::onSnapshotChange()
283 * (see its lock requirements).
284 */
285STDMETHODIMP Snapshot::COMSETTER(Name)(IN_BSTR aName)
286{
287 CheckComArgNotNull(aName);
288
289 AutoCaller autoCaller(this);
290 CheckComRCReturnRC(autoCaller.rc());
291
292 Utf8Str strName(aName);
293
294 AutoWriteLock alock(this);
295
296 if (m->strName != strName)
297 {
298 m->strName = strName;
299
300 alock.leave(); /* Important! (child->parent locks are forbidden) */
301
302 return m->pMachine->onSnapshotChange(this);
303 }
304
305 return S_OK;
306}
307
308STDMETHODIMP Snapshot::COMGETTER(Description) (BSTR *aDescription)
309{
310 CheckComArgOutPointerValid(aDescription);
311
312 AutoCaller autoCaller(this);
313 CheckComRCReturnRC(autoCaller.rc());
314
315 AutoReadLock alock(this);
316
317 m->strDescription.cloneTo(aDescription);
318 return S_OK;
319}
320
321STDMETHODIMP Snapshot::COMSETTER(Description) (IN_BSTR aDescription)
322{
323 CheckComArgNotNull(aDescription);
324
325 AutoCaller autoCaller(this);
326 CheckComRCReturnRC(autoCaller.rc());
327
328 Utf8Str strDescription(aDescription);
329
330 AutoWriteLock alock(this);
331
332 if (m->strDescription != strDescription)
333 {
334 m->strDescription = strDescription;
335
336 alock.leave(); /* Important! (child->parent locks are forbidden) */
337
338 return m->pMachine->onSnapshotChange(this);
339 }
340
341 return S_OK;
342}
343
344STDMETHODIMP Snapshot::COMGETTER(TimeStamp) (LONG64 *aTimeStamp)
345{
346 CheckComArgOutPointerValid(aTimeStamp);
347
348 AutoCaller autoCaller(this);
349 CheckComRCReturnRC(autoCaller.rc());
350
351 AutoReadLock alock(this);
352
353 *aTimeStamp = RTTimeSpecGetMilli(&m->timeStamp);
354 return S_OK;
355}
356
357STDMETHODIMP Snapshot::COMGETTER(Online)(BOOL *aOnline)
358{
359 CheckComArgOutPointerValid(aOnline);
360
361 AutoCaller autoCaller(this);
362 CheckComRCReturnRC(autoCaller.rc());
363
364 AutoReadLock alock(this);
365
366 *aOnline = !stateFilePath().isNull();
367 return S_OK;
368}
369
370STDMETHODIMP Snapshot::COMGETTER(Machine) (IMachine **aMachine)
371{
372 CheckComArgOutPointerValid(aMachine);
373
374 AutoCaller autoCaller(this);
375 CheckComRCReturnRC(autoCaller.rc());
376
377 AutoReadLock alock(this);
378
379 m->pMachine.queryInterfaceTo(aMachine);
380 return S_OK;
381}
382
383STDMETHODIMP Snapshot::COMGETTER(Parent) (ISnapshot **aParent)
384{
385 CheckComArgOutPointerValid(aParent);
386
387 AutoCaller autoCaller(this);
388 CheckComRCReturnRC(autoCaller.rc());
389
390 AutoReadLock alock(this);
391
392 mParent.queryInterfaceTo(aParent);
393 return S_OK;
394}
395
396STDMETHODIMP Snapshot::COMGETTER(Children) (ComSafeArrayOut(ISnapshot *, aChildren))
397{
398 CheckComArgOutSafeArrayPointerValid(aChildren);
399
400 AutoCaller autoCaller(this);
401 CheckComRCReturnRC(autoCaller.rc());
402
403 AutoReadLock alock(m->pMachine->snapshotsTreeLockHandle());
404 AutoReadLock block(this->lockHandle());
405
406 SafeIfaceArray<ISnapshot> collection(m->llChildren);
407 collection.detachTo(ComSafeArrayOutArg(aChildren));
408
409 return S_OK;
410}
411
412// public methods only for internal purposes
413////////////////////////////////////////////////////////////////////////////////
414
415/**
416 * @note
417 * Must be called from under the object's lock!
418 */
419const Bstr& Snapshot::stateFilePath() const
420{
421 return m->pMachine->mSSData->mStateFilePath;
422}
423
424/**
425 * Returns the number of direct child snapshots, without grandchildren.
426 * Does not recurse.
427 * @return
428 */
429ULONG Snapshot::getChildrenCount()
430{
431 AutoCaller autoCaller(this);
432 AssertComRC(autoCaller.rc());
433
434 AutoReadLock treeLock(m->pMachine->snapshotsTreeLockHandle());
435 return (ULONG)m->llChildren.size();
436}
437
438/**
439 * Implementation method for getAllChildrenCount() so we request the
440 * tree lock only once before recursing. Don't call directly.
441 * @return
442 */
443ULONG Snapshot::getAllChildrenCountImpl()
444{
445 AutoCaller autoCaller(this);
446 AssertComRC(autoCaller.rc());
447
448 ULONG count = (ULONG)m->llChildren.size();
449 for (SnapshotsList::const_iterator it = m->llChildren.begin();
450 it != m->llChildren.end();
451 ++it)
452 {
453 count += (*it)->getAllChildrenCountImpl();
454 }
455
456 return count;
457}
458
459/**
460 * Returns the number of child snapshots including all grandchildren.
461 * Recurses into the snapshots tree.
462 * @return
463 */
464ULONG Snapshot::getAllChildrenCount()
465{
466 AutoCaller autoCaller(this);
467 AssertComRC(autoCaller.rc());
468
469 AutoReadLock treeLock(m->pMachine->snapshotsTreeLockHandle());
470 return getAllChildrenCountImpl();
471}
472
473/**
474 * Returns the SnapshotMachine that this snapshot belongs to.
475 * Caller must hold the snapshot's object lock!
476 * @return
477 */
478ComPtr<SnapshotMachine> Snapshot::getSnapshotMachine()
479{
480 return (SnapshotMachine*)m->pMachine;
481}
482
483/**
484 * Returns the UUID of this snapshot.
485 * Caller must hold the snapshot's object lock!
486 * @return
487 */
488Guid Snapshot::getId() const
489{
490 return m->uuid;
491}
492
493/**
494 * Returns the name of this snapshot.
495 * Caller must hold the snapshot's object lock!
496 * @return
497 */
498const Utf8Str& Snapshot::getName() const
499{
500 return m->strName;
501}
502
503/**
504 * Returns the time stamp of this snapshot.
505 * Caller must hold the snapshot's object lock!
506 * @return
507 */
508RTTIMESPEC Snapshot::getTimeStamp() const
509{
510 return m->timeStamp;
511}
512
513/**
514 * Searches for a snapshot with the given ID among children, grand-children,
515 * etc. of this snapshot. This snapshot itself is also included in the search.
516 * Caller must hold the snapshots tree lock!
517 */
518ComObjPtr<Snapshot> Snapshot::findChildOrSelf(IN_GUID aId)
519{
520 ComObjPtr<Snapshot> child;
521
522 AutoCaller autoCaller(this);
523 AssertComRC(autoCaller.rc());
524
525 AutoReadLock alock(this);
526
527 if (m->uuid == aId)
528 child = this;
529 else
530 {
531 alock.unlock();
532 for (SnapshotsList::const_iterator it = m->llChildren.begin();
533 it != m->llChildren.end();
534 ++it)
535 {
536 if ((child = (*it)->findChildOrSelf(aId)))
537 break;
538 }
539 }
540
541 return child;
542}
543
544/**
545 * Searches for a first snapshot with the given name among children,
546 * grand-children, etc. of this snapshot. This snapshot itself is also included
547 * in the search.
548 * Caller must hold the snapshots tree lock!
549 */
550ComObjPtr<Snapshot> Snapshot::findChildOrSelf(const Utf8Str &aName)
551{
552 ComObjPtr<Snapshot> child;
553 AssertReturn(!aName.isEmpty(), child);
554
555 AutoCaller autoCaller(this);
556 AssertComRC(autoCaller.rc());
557
558 AutoReadLock alock (this);
559
560 if (m->strName == aName)
561 child = this;
562 else
563 {
564 alock.unlock();
565 for (SnapshotsList::const_iterator it = m->llChildren.begin();
566 it != m->llChildren.end();
567 ++it)
568 {
569 if ((child = (*it)->findChildOrSelf(aName)))
570 break;
571 }
572 }
573
574 return child;
575}
576
577/**
578 * Internal implementation for Snapshot::updateSavedStatePaths (below).
579 * @param aOldPath
580 * @param aNewPath
581 */
582void Snapshot::updateSavedStatePathsImpl(const char *aOldPath, const char *aNewPath)
583{
584 AutoWriteLock alock(this);
585
586 Utf8Str path = m->pMachine->mSSData->mStateFilePath;
587 LogFlowThisFunc(("Snap[%s].statePath={%s}\n", m->strName.c_str(), path.c_str()));
588
589 /* state file may be NULL (for offline snapshots) */
590 if ( path.length()
591 && RTPathStartsWith(path.c_str(), aOldPath)
592 )
593 {
594 path = Utf8StrFmt ("%s%s", aNewPath, path.raw() + strlen (aOldPath));
595 m->pMachine->mSSData->mStateFilePath = path;
596
597 LogFlowThisFunc(("-> updated: {%s}\n", path.raw()));
598 }
599
600 for (SnapshotsList::const_iterator it = m->llChildren.begin();
601 it != m->llChildren.end();
602 ++it)
603 {
604 Snapshot *pChild = *it;
605 pChild->updateSavedStatePathsImpl(aOldPath, aNewPath);
606 }
607}
608
609/**
610 * Checks if the specified path change affects the saved state file path of
611 * this snapshot or any of its (grand-)children and updates it accordingly.
612 *
613 * Intended to be called by Machine::openConfigLoader() only.
614 *
615 * @param aOldPath old path (full)
616 * @param aNewPath new path (full)
617 *
618 * @note Locks this object + children for writing.
619 */
620void Snapshot::updateSavedStatePaths(const char *aOldPath, const char *aNewPath)
621{
622 LogFlowThisFunc(("aOldPath={%s} aNewPath={%s}\n", aOldPath, aNewPath));
623
624 AssertReturnVoid(aOldPath);
625 AssertReturnVoid(aNewPath);
626
627 AutoCaller autoCaller(this);
628 AssertComRC(autoCaller.rc());
629
630 AutoWriteLock chLock(m->pMachine->snapshotsTreeLockHandle());
631 // call the implementation under the tree lock
632 updateSavedStatePathsImpl(aOldPath, aNewPath);
633}
634
635/**
636 * Internal implementation for Snapshot::saveSnapshot (below).
637 * @param aNode
638 * @param aAttrsOnly
639 * @return
640 */
641HRESULT Snapshot::saveSnapshotImpl(settings::Snapshot &data, bool aAttrsOnly)
642{
643 AutoReadLock alock(this);
644
645 data.uuid = m->uuid;
646 data.strName = m->strName;
647 data.timestamp = m->timeStamp;
648 data.strDescription = m->strDescription;
649
650 if (aAttrsOnly)
651 return S_OK;
652
653 /* stateFile (optional) */
654 if (stateFilePath())
655 {
656 /* try to make the file name relative to the settings file dir */
657 Utf8Str strStateFilePath = stateFilePath();
658 m->pMachine->calculateRelativePath(strStateFilePath, strStateFilePath);
659 data.strStateFile = strStateFilePath;
660 }
661 else
662 data.strStateFile.setNull();
663
664 HRESULT rc = m->pMachine->saveHardware(data.hardware);
665 CheckComRCReturnRC (rc);
666
667 rc = m->pMachine->saveStorageControllers(data.storage);
668 CheckComRCReturnRC (rc);
669
670 alock.unlock();
671
672 data.llChildSnapshots.clear();
673
674 if (m->llChildren.size())
675 {
676 for (SnapshotsList::const_iterator it = m->llChildren.begin();
677 it != m->llChildren.end();
678 ++it)
679 {
680 settings::Snapshot snap;
681 rc = (*it)->saveSnapshotImpl(snap, aAttrsOnly);
682 CheckComRCReturnRC (rc);
683
684 data.llChildSnapshots.push_back(snap);
685 }
686 }
687
688 return S_OK;
689}
690
691/**
692 * Saves the given snapshot and all its children (unless \a aAttrsOnly is true).
693 * It is assumed that the given node is empty (unless \a aAttrsOnly is true).
694 *
695 * @param aNode <Snapshot> node to save the snapshot to.
696 * @param aSnapshot Snapshot to save.
697 * @param aAttrsOnly If true, only updatge user-changeable attrs.
698 */
699HRESULT Snapshot::saveSnapshot(settings::Snapshot &data, bool aAttrsOnly)
700{
701 AutoWriteLock listLock(m->pMachine->snapshotsTreeLockHandle());
702
703 return saveSnapshotImpl(data, aAttrsOnly);
704}
705
Note: See TracBrowser for help on using the repository browser.

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