VirtualBox

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

Last change on this file since 21945 was 21878, checked in by vboxsync, 15 years ago

Main: coding style: have Main obey the standard VirtualBox coding style rules (no functional changes)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.2 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 id;
51 Bstr name;
52 Bstr description;
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 IN_BSTR aName,
87 IN_BSTR aDescription,
88 RTTIMESPEC aTimeStamp,
89 SnapshotMachine *aMachine,
90 Snapshot *aParent)
91{
92 LogFlowMember (("Snapshot::init(aParent=%p)\n", aParent));
93
94 ComAssertRet (!aId.isEmpty() && aName && 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->id = aId;
108 m->name = aName;
109 m->description = 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->id.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->name.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 AutoWriteLock alock(this);
284
285 if (m->name != aName)
286 {
287 m->name = aName;
288
289 alock.leave(); /* Important! (child->parent locks are forbidden) */
290
291 return m->pMachine->onSnapshotChange(this);
292 }
293
294 return S_OK;
295}
296
297STDMETHODIMP Snapshot::COMGETTER(Description) (BSTR *aDescription)
298{
299 CheckComArgOutPointerValid(aDescription);
300
301 AutoCaller autoCaller(this);
302 CheckComRCReturnRC(autoCaller.rc());
303
304 AutoReadLock alock(this);
305
306 m->description.cloneTo(aDescription);
307 return S_OK;
308}
309
310STDMETHODIMP Snapshot::COMSETTER(Description) (IN_BSTR aDescription)
311{
312 CheckComArgNotNull(aDescription);
313
314 AutoCaller autoCaller(this);
315 CheckComRCReturnRC(autoCaller.rc());
316
317 AutoWriteLock alock(this);
318
319 if (m->description != aDescription)
320 {
321 m->description = aDescription;
322
323 alock.leave(); /* Important! (child->parent locks are forbidden) */
324
325 return m->pMachine->onSnapshotChange(this);
326 }
327
328 return S_OK;
329}
330
331STDMETHODIMP Snapshot::COMGETTER(TimeStamp) (LONG64 *aTimeStamp)
332{
333 CheckComArgOutPointerValid(aTimeStamp);
334
335 AutoCaller autoCaller(this);
336 CheckComRCReturnRC(autoCaller.rc());
337
338 AutoReadLock alock(this);
339
340 *aTimeStamp = RTTimeSpecGetMilli(&m->timeStamp);
341 return S_OK;
342}
343
344STDMETHODIMP Snapshot::COMGETTER(Online) (BOOL *aOnline)
345{
346 CheckComArgOutPointerValid(aOnline);
347
348 AutoCaller autoCaller(this);
349 CheckComRCReturnRC(autoCaller.rc());
350
351 AutoReadLock alock(this);
352
353 *aOnline = !stateFilePath().isNull();
354 return S_OK;
355}
356
357STDMETHODIMP Snapshot::COMGETTER(Machine) (IMachine **aMachine)
358{
359 CheckComArgOutPointerValid(aMachine);
360
361 AutoCaller autoCaller(this);
362 CheckComRCReturnRC(autoCaller.rc());
363
364 AutoReadLock alock(this);
365
366 m->pMachine.queryInterfaceTo(aMachine);
367 return S_OK;
368}
369
370STDMETHODIMP Snapshot::COMGETTER(Parent) (ISnapshot **aParent)
371{
372 CheckComArgOutPointerValid(aParent);
373
374 AutoCaller autoCaller(this);
375 CheckComRCReturnRC(autoCaller.rc());
376
377 AutoReadLock alock(this);
378
379 mParent.queryInterfaceTo(aParent);
380 return S_OK;
381}
382
383STDMETHODIMP Snapshot::COMGETTER(Children) (ComSafeArrayOut(ISnapshot *, aChildren))
384{
385 CheckComArgOutSafeArrayPointerValid(aChildren);
386
387 AutoCaller autoCaller(this);
388 CheckComRCReturnRC(autoCaller.rc());
389
390 AutoReadLock alock(m->pMachine->snapshotsTreeLockHandle());
391 AutoReadLock block(this->lockHandle());
392
393 SafeIfaceArray<ISnapshot> collection(m->llChildren);
394 collection.detachTo(ComSafeArrayOutArg(aChildren));
395
396 return S_OK;
397}
398
399// public methods only for internal purposes
400////////////////////////////////////////////////////////////////////////////////
401
402/**
403 * @note
404 * Must be called from under the object's lock!
405 */
406const Bstr& Snapshot::stateFilePath() const
407{
408 return m->pMachine->mSSData->mStateFilePath;
409}
410
411/**
412 * Returns the number of direct child snapshots, without grandchildren.
413 * Does not recurse.
414 * @return
415 */
416ULONG Snapshot::getChildrenCount()
417{
418 AutoCaller autoCaller(this);
419 AssertComRC(autoCaller.rc());
420
421 AutoReadLock treeLock(m->pMachine->snapshotsTreeLockHandle());
422 return (ULONG)m->llChildren.size();
423}
424
425/**
426 * Implementation method for getAllChildrenCount() so we request the
427 * tree lock only once before recursing. Don't call directly.
428 * @return
429 */
430ULONG Snapshot::getAllChildrenCountImpl()
431{
432 AutoCaller autoCaller(this);
433 AssertComRC(autoCaller.rc());
434
435 ULONG count = (ULONG)m->llChildren.size();
436 for (SnapshotsList::const_iterator it = m->llChildren.begin();
437 it != m->llChildren.end();
438 ++it)
439 {
440 count += (*it)->getAllChildrenCountImpl();
441 }
442
443 return count;
444}
445
446/**
447 * Returns the number of child snapshots including all grandchildren.
448 * Recurses into the snapshots tree.
449 * @return
450 */
451ULONG Snapshot::getAllChildrenCount()
452{
453 AutoCaller autoCaller(this);
454 AssertComRC(autoCaller.rc());
455
456 AutoReadLock treeLock(m->pMachine->snapshotsTreeLockHandle());
457 return getAllChildrenCountImpl();
458}
459
460/**
461 * Returns the SnapshotMachine that this snapshot belongs to.
462 * Caller must hold the snapshot's object lock!
463 * @return
464 */
465ComPtr<SnapshotMachine> Snapshot::getSnapshotMachine()
466{
467 return (SnapshotMachine*)m->pMachine;
468}
469
470/**
471 * Returns the UUID of this snapshot.
472 * Caller must hold the snapshot's object lock!
473 * @return
474 */
475Guid Snapshot::getId() const
476{
477 return m->id;
478}
479
480/**
481 * Returns the name of this snapshot.
482 * Caller must hold the snapshot's object lock!
483 * @return
484 */
485Bstr Snapshot::getName() const
486{
487 return m->name;
488}
489
490/**
491 * Returns the time stamp of this snapshot.
492 * Caller must hold the snapshot's object lock!
493 * @return
494 */
495RTTIMESPEC Snapshot::getTimeStamp() const
496{
497 return m->timeStamp;
498}
499
500/**
501 * Searches for a snapshot with the given ID among children, grand-children,
502 * etc. of this snapshot. This snapshot itself is also included in the search.
503 * Caller must hold the snapshots tree lock!
504 */
505ComObjPtr<Snapshot> Snapshot::findChildOrSelf(IN_GUID aId)
506{
507 ComObjPtr<Snapshot> child;
508
509 AutoCaller autoCaller(this);
510 AssertComRC(autoCaller.rc());
511
512 AutoReadLock alock(this);
513
514 if (m->id == aId)
515 child = this;
516 else
517 {
518 alock.unlock();
519 for (SnapshotsList::const_iterator it = m->llChildren.begin();
520 it != m->llChildren.end();
521 ++it)
522 {
523 if ((child = (*it)->findChildOrSelf(aId)))
524 break;
525 }
526 }
527
528 return child;
529}
530
531/**
532 * Searches for a first snapshot with the given name among children,
533 * grand-children, etc. of this snapshot. This snapshot itself is also included
534 * in the search.
535 * Caller must hold the snapshots tree lock!
536 */
537ComObjPtr<Snapshot> Snapshot::findChildOrSelf(IN_BSTR aName)
538{
539 ComObjPtr<Snapshot> child;
540 AssertReturn(aName, child);
541
542 AutoCaller autoCaller(this);
543 AssertComRC(autoCaller.rc());
544
545 AutoReadLock alock(this);
546
547 if (m->name == aName)
548 child = this;
549 else
550 {
551 alock.unlock();
552 for (SnapshotsList::const_iterator it = m->llChildren.begin();
553 it != m->llChildren.end();
554 ++it)
555 {
556 if ((child = (*it)->findChildOrSelf(aName)))
557 break;
558 }
559 }
560
561 return child;
562}
563
564/**
565 * Internal implementation for Snapshot::updateSavedStatePaths (below).
566 * @param aOldPath
567 * @param aNewPath
568 */
569void Snapshot::updateSavedStatePathsImpl(const char *aOldPath, const char *aNewPath)
570{
571 AutoWriteLock alock(this);
572
573 Utf8Str path = m->pMachine->mSSData->mStateFilePath;
574 LogFlowThisFunc(("Snap[%ls].statePath={%s}\n", m->name.raw(), path.raw()));
575
576 /* state file may be NULL (for offline snapshots) */
577 if ( path.length()
578 && RTPathStartsWith(path, aOldPath)
579 )
580 {
581 path = Utf8StrFmt ("%s%s", aNewPath, path.raw() + strlen (aOldPath));
582 m->pMachine->mSSData->mStateFilePath = path;
583
584 LogFlowThisFunc(("-> updated: {%s}\n", path.raw()));
585 }
586
587 for (SnapshotsList::const_iterator it = m->llChildren.begin();
588 it != m->llChildren.end();
589 ++it)
590 {
591 updateSavedStatePathsImpl(aOldPath, aNewPath);
592 }
593}
594
595/**
596 * Checks if the specified path change affects the saved state file path of
597 * this snapshot or any of its (grand-)children and updates it accordingly.
598 *
599 * Intended to be called by Machine::openConfigLoader() only.
600 *
601 * @param aOldPath old path (full)
602 * @param aNewPath new path (full)
603 *
604 * @note Locks this object + children for writing.
605 */
606void Snapshot::updateSavedStatePaths(const char *aOldPath, const char *aNewPath)
607{
608 LogFlowThisFunc(("aOldPath={%s} aNewPath={%s}\n", aOldPath, aNewPath));
609
610 AssertReturnVoid(aOldPath);
611 AssertReturnVoid(aNewPath);
612
613 AutoCaller autoCaller(this);
614 AssertComRC(autoCaller.rc());
615
616 AutoWriteLock chLock(m->pMachine->snapshotsTreeLockHandle());
617 // call the implementation under the tree lock
618 updateSavedStatePathsImpl(aOldPath, aNewPath);
619}
620
621/**
622 * Internal implementation for Snapshot::saveSnapshot (below).
623 * @param aNode
624 * @param aAttrsOnly
625 * @return
626 */
627HRESULT Snapshot::saveSnapshotImpl(settings::Key &aNode, bool aAttrsOnly)
628{
629 using namespace settings;
630
631 AutoReadLock alock(this);
632
633 /* uuid (required) */
634 if (!aAttrsOnly)
635 aNode.setValue<Guid>("uuid", m->id);
636
637 /* name (required) */
638 aNode.setValue<Bstr>("name", m->name);
639
640 /* timeStamp (required) */
641 aNode.setValue<RTTIMESPEC>("timeStamp", m->timeStamp);
642
643 /* Description node (optional) */
644 if (!m->description.isNull())
645 {
646 Key descNode = aNode.createKey("Description");
647 descNode.setKeyValue<Bstr>(m->description);
648 }
649 else
650 {
651 Key descNode = aNode.findKey ("Description");
652 if (!descNode.isNull())
653 descNode.zap();
654 }
655
656 if (aAttrsOnly)
657 return S_OK;
658
659 /* stateFile (optional) */
660 if (stateFilePath())
661 {
662 /* try to make the file name relative to the settings file dir */
663 Utf8Str strStateFilePath = stateFilePath();
664 m->pMachine->calculateRelativePath(strStateFilePath, strStateFilePath);
665 aNode.setStringValue ("stateFile", strStateFilePath);
666 }
667
668 {
669 ComObjPtr<SnapshotMachine> snapshotMachine = m->pMachine;
670
671 /* save hardware */
672 {
673 Key hwNode = aNode.createKey ("Hardware");
674 HRESULT rc = snapshotMachine->saveHardware(hwNode);
675 CheckComRCReturnRC(rc);
676 }
677
678 /* save hard disks. */
679 {
680 Key storageNode = aNode.createKey ("StorageControllers");
681 HRESULT rc = snapshotMachine->saveStorageControllers(storageNode);
682 CheckComRCReturnRC(rc);
683 }
684 }
685
686 alock.unlock();
687
688 if (m->llChildren.size())
689 {
690 Key snapshotsNode = aNode.createKey ("Snapshots");
691
692 HRESULT rc = S_OK;
693
694 for (SnapshotsList::const_iterator it = m->llChildren.begin();
695 it != m->llChildren.end();
696 ++it)
697 {
698 Key snapshotNode = snapshotsNode.createKey("Snapshot");
699 rc = (*it)->saveSnapshotImpl(snapshotNode, aAttrsOnly);
700 CheckComRCReturnRC(rc);
701 }
702 }
703
704 return S_OK;
705}
706
707/**
708 * Saves the given snapshot and all its children (unless \a aAttrsOnly is true).
709 * It is assumed that the given node is empty (unless \a aAttrsOnly is true).
710 *
711 * @param aNode <Snapshot> node to save the snapshot to.
712 * @param aSnapshot Snapshot to save.
713 * @param aAttrsOnly If true, only updatge user-changeable attrs.
714 */
715HRESULT Snapshot::saveSnapshot(settings::Key &aNode, bool aAttrsOnly)
716{
717 AssertReturn(!aNode.isNull(), E_INVALIDARG);
718
719 AutoWriteLock listLock(m->pMachine->snapshotsTreeLockHandle());
720
721 return saveSnapshotImpl(aNode, aAttrsOnly);
722}
723
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