VirtualBox

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

Last change on this file since 22875 was 22778, checked in by vboxsync, 15 years ago

Main: fix infinite recursion when renaming machines with snapshots

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