SnapshotImpl.cpp revision 5959d7e4b51fd1cc749cfe16e35d8c7798c46a57
/** @file
*
* VirtualBox COM class implementation
*/
/*
* Copyright (C) 2006-2007 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
#include "SnapshotImpl.h"
#include "MachineImpl.h"
#include "Logging.h"
#include <iprt/path.h>
#include <VBox/param.h>
#include <VBox/err.h>
#include <algorithm>
// constructor / destructor
////////////////////////////////////////////////////////////////////////////////
Snapshot::Data::Data()
{
RTTimeSpecSetMilli (&mTimeStamp, 0);
};
Snapshot::Data::~Data()
{
};
HRESULT Snapshot::FinalConstruct()
{
LogFlowMember (("Snapshot::FinalConstruct()\n"));
return S_OK;
}
void Snapshot::FinalRelease()
{
LogFlowMember (("Snapshot::FinalRelease()\n"));
uninit();
}
/**
* Initializes the instance
*
* @param aId id of the snapshot
* @param aName name of the snapshot
* @param aDescription name of the snapshot (NULL if no description)
* @param aTimeStamp timestamp of the snapshot, in ms since 1970-01-01 UTC
* @param aMachine machine associated with this snapshot
* @param aParent parent snapshot (NULL if no parent)
*/
HRESULT Snapshot::init (const Guid &aId, IN_BSTR aName, IN_BSTR aDescription,
RTTIMESPEC aTimeStamp, SnapshotMachine *aMachine,
Snapshot *aParent)
{
LogFlowMember (("Snapshot::init(aParent=%p)\n", aParent));
ComAssertRet (!aId.isEmpty() && aName && aMachine, E_INVALIDARG);
AutoWriteLock alock (this);
ComAssertRet (!isReady(), E_FAIL);
mParent = aParent;
mData.mId = aId;
mData.mName = aName;
mData.mDescription = aDescription;
mData.mTimeStamp = aTimeStamp;
mData.mMachine = aMachine;
if (aParent)
aParent->addDependentChild (this);
setReady (true);
return S_OK;
}
/**
* Uninitializes the instance and sets the ready flag to FALSE.
* Called either from FinalRelease(), by the parent when it gets destroyed,
* or by a third party when it decides this object is no more valid.
*/
void Snapshot::uninit()
{
LogFlowMember (("Snapshot::uninit()\n"));
AutoWriteLock alock (this);
LogFlowMember (("Snapshot::uninit(): isReady=%d\n", isReady()));
if (!isReady())
return;
// uninit all children
uninitDependentChildren();
setReady (false);
if (mParent)
{
alock.leave();
mParent->removeDependentChild (this);
alock.enter();
mParent.setNull();
}
if (mData.mMachine)
{
mData.mMachine->uninit();
mData.mMachine.setNull();
}
}
/**
* Discards the current snapshot by removing it from the tree of snapshots
* and reparenting its children.
* This method also calls #uninit() in case of success.
*/
void Snapshot::discard()
{
LogFlowMember (("Snapshot::discard()\n"));
AutoWriteLock alock (this);
AssertReturn (isReady(), (void) 0);
{
AutoWriteLock chLock (childrenLock ());
AssertReturn (!!mParent || children().size() <= 1, (void) 0);
for (SnapshotList::const_iterator it = children().begin();
it != children().end(); ++ it)
{
ComObjPtr <Snapshot> child = *it;
AutoWriteLock childLock (child);
// reparent the child
child->mParent = mParent;
if (mParent)
mParent->addDependentChild (child);
}
}
// detach all our children to avoid their uninit in #uninit()
removeDependentChildren();
// finalize uninitialization
uninit();
}
// ISnapshot methods
////////////////////////////////////////////////////////////////////////////////
STDMETHODIMP Snapshot::COMGETTER(Id) (BSTR *aId)
{
CheckComArgOutPointerValid(aId);
AutoWriteLock alock (this);
CHECK_READY();
mData.mId.toUtf16().cloneTo (aId);
return S_OK;
}
STDMETHODIMP Snapshot::COMGETTER(Name) (BSTR *aName)
{
CheckComArgOutPointerValid(aName);
AutoWriteLock alock (this);
CHECK_READY();
mData.mName.cloneTo (aName);
return S_OK;
}
/**
* @note Locks this object for writing, then calls Machine::onSnapshotChange()
* (see its lock requirements).
*/
STDMETHODIMP Snapshot::COMSETTER(Name) (IN_BSTR aName)
{
CheckComArgNotNull(aName);
AutoWriteLock alock (this);
CHECK_READY();
if (mData.mName != aName)
{
mData.mName = aName;
alock.leave(); /* Important! (child->parent locks are forbidden) */
return mData.mMachine->onSnapshotChange (this);
}
return S_OK;
}
STDMETHODIMP Snapshot::COMGETTER(Description) (BSTR *aDescription)
{
CheckComArgOutPointerValid(aDescription);
AutoWriteLock alock (this);
CHECK_READY();
mData.mDescription.cloneTo (aDescription);
return S_OK;
}
STDMETHODIMP Snapshot::COMSETTER(Description) (IN_BSTR aDescription)
{
CheckComArgNotNull(aDescription);
AutoWriteLock alock (this);
CHECK_READY();
if (mData.mDescription != aDescription)
{
mData.mDescription = aDescription;
alock.leave(); /* Important! (child->parent locks are forbidden) */
return mData.mMachine->onSnapshotChange (this);
}
return S_OK;
}
STDMETHODIMP Snapshot::COMGETTER(TimeStamp) (LONG64 *aTimeStamp)
{
CheckComArgOutPointerValid(aTimeStamp);
AutoWriteLock alock (this);
CHECK_READY();
*aTimeStamp = RTTimeSpecGetMilli (&mData.mTimeStamp);
return S_OK;
}
STDMETHODIMP Snapshot::COMGETTER(Online) (BOOL *aOnline)
{
CheckComArgOutPointerValid(aOnline);
AutoWriteLock alock (this);
CHECK_READY();
*aOnline = !stateFilePath().isNull();
return S_OK;
}
STDMETHODIMP Snapshot::COMGETTER(Machine) (IMachine **aMachine)
{
CheckComArgOutPointerValid(aMachine);
AutoWriteLock alock (this);
CHECK_READY();
mData.mMachine.queryInterfaceTo (aMachine);
return S_OK;
}
STDMETHODIMP Snapshot::COMGETTER(Parent) (ISnapshot **aParent)
{
CheckComArgOutPointerValid(aParent);
AutoWriteLock alock (this);
CHECK_READY();
mParent.queryInterfaceTo (aParent);
return S_OK;
}
STDMETHODIMP Snapshot::COMGETTER(Children) (ComSafeArrayOut (ISnapshot *, aChildren))
{
CheckComArgOutSafeArrayPointerValid(aChildren);
AutoWriteLock alock (this);
CHECK_READY();
AutoWriteLock chLock (childrenLock ());
SafeIfaceArray <ISnapshot> collection (children());
collection.detachTo (ComSafeArrayOutArg (aChildren));
return S_OK;
}
// public methods only for internal purposes
////////////////////////////////////////////////////////////////////////////////
/**
* @note
* Must be called from under the object's lock!
*/
const Bstr &Snapshot::stateFilePath() const
{
return mData.mMachine->mSSData->mStateFilePath;
}
/**
* Returns the number of children of this snapshot, including grand-children,
* etc.
*/
ULONG Snapshot::descendantCount()
{
AutoWriteLock alock(this);
AssertReturn (isReady(), 0);
AutoWriteLock chLock (childrenLock ());
ULONG count = (ULONG)children().size();
for (SnapshotList::const_iterator it = children().begin();
it != children().end(); ++ it)
{
count += (*it)->descendantCount();
}
return count;
}
/**
* Searches for a snapshot with the given ID among children, grand-children,
* etc. of this snapshot. This snapshot itself is also included in the search.
*/
ComObjPtr <Snapshot> Snapshot::findChildOrSelf (IN_GUID aId)
{
ComObjPtr <Snapshot> child;
AutoWriteLock alock (this);
AssertReturn (isReady(), child);
if (mData.mId == aId)
child = this;
else
{
AutoWriteLock chLock (childrenLock ());
for (SnapshotList::const_iterator it = children().begin();
!child && it != children().end(); ++ it)
{
child = (*it)->findChildOrSelf (aId);
}
}
return child;
}
/**
* Searches for a first snapshot with the given name among children,
* grand-children, etc. of this snapshot. This snapshot itself is also included
* in the search.
*/
ComObjPtr <Snapshot> Snapshot::findChildOrSelf (IN_BSTR aName)
{
ComObjPtr <Snapshot> child;
AssertReturn (aName, child);
AutoWriteLock alock (this);
AssertReturn (isReady(), child);
if (mData.mName == aName)
child = this;
else
{
AutoWriteLock chLock (childrenLock ());
for (SnapshotList::const_iterator it = children().begin();
!child && it != children().end(); ++ it)
{
child = (*it)->findChildOrSelf (aName);
}
}
return child;
}
/**
* Checks if the specified path change affects the saved state file path of
* this snapshot or any of its (grand-)children and updates it accordingly.
*
* Intended to be called by Machine::openConfigLoader() only.
*
* @param aOldPath old path (full)
* @param aNewPath new path (full)
*
* @note Locks this object + children for writing.
*/
void Snapshot::updateSavedStatePaths (const char *aOldPath, const char *aNewPath)
{
LogFlowThisFunc (("aOldPath={%s} aNewPath={%s}\n", aOldPath, aNewPath));
AssertReturnVoid (aOldPath);
AssertReturnVoid (aNewPath);
AutoWriteLock alock (this);
AssertReturnVoid (isReady());
Utf8Str path = mData.mMachine->mSSData->mStateFilePath;
LogFlowThisFunc (("Snap[%ls].statePath={%s}\n", mData.mName.raw(), path.raw()));
/* state file may be NULL (for offline snapshots) */
if ( path.length()
&& RTPathStartsWith(path, aOldPath)
)
{
path = Utf8StrFmt ("%s%s", aNewPath, path.raw() + strlen (aOldPath));
mData.mMachine->mSSData->mStateFilePath = path;
LogFlowThisFunc (("-> updated: {%s}\n", path.raw()));
}
AutoWriteLock chLock (childrenLock ());
for (SnapshotList::const_iterator it = children().begin();
it != children().end(); ++ it)
{
(*it)->updateSavedStatePaths (aOldPath, aNewPath);
}
}
/* vi: set tabstop=4 shiftwidth=4 expandtab: */