HardDiskImpl.cpp revision 68a4ee3a31a0807abd03eae881c1bbaf4d42ee6d
/** @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;
* 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 "HardDiskImpl.h"
#include "ProgressImpl.h"
#include "VirtualBoxImpl.h"
#include "SystemPropertiesImpl.h"
#include "Logging.h"
#include <iprt/cpputils.h>
#include <algorithm>
#define CHECK_BUSY() \
do { \
if (isBusy()) \
return setError (E_UNEXPECTED, \
tr ("Hard disk '%ls' is being used by another task"), \
} while (0)
#define CHECK_BUSY_AND_READERS() \
do { \
return setError (E_UNEXPECTED, \
tr ("Hard disk '%ls' is being used by another task"), \
} while (0)
/** Task structure for asynchronous VDI operations */
struct VDITask
{
, vdi (i)
, progress (p)
{}
/* for CreateDynamic, CreateStatic */
/* for CloneToImage */
};
/**
* Progress callback handler for VDI operations.
*
* @param uPercent Completetion precentage (0-100).
* @param pvUser Pointer to the Progress instance.
*/
{
/* update the progress object, capping it at 99% as the final percent
* is used for additional operations like setting the UUIDs and similar. */
if (progress)
return VINF_SUCCESS;
}
////////////////////////////////////////////////////////////////////////////////
// HardDisk class
////////////////////////////////////////////////////////////////////////////////
// constructor / destructor
////////////////////////////////////////////////////////////////////////////////
/** Shold be called by subclasses from #FinalConstruct() */
{
mRegistered = FALSE;
mBusy = false;
mReaders = 0;
return S_OK;
}
/**
* Shold be called by subclasses from #FinalRelease().
* Uninitializes this object by calling #uninit() if it's not yet done.
*/
void HardDisk::FinalRelease()
{
uninit();
}
// protected initializer/uninitializer for internal purposes only
////////////////////////////////////////////////////////////////////////////////
/**
* Initializes the hard disk object.
*
* Subclasses should call this or any other #init() method from their
* init() implementations.
*
* @note
* This method doesn't do |isReady()| check and doesn't call
* |setReady (true)| on success!
* @note
* This method must be called from under the object's lock!
*/
{
if (!aParent)
aVirtualBox->addDependentChild (this);
else
aParent->addDependentChild (this);
return S_OK;
}
/**
* Uninitializes the instance.
* Subclasses should call this from their uninit() implementations.
* The readiness flag must be true on input and will be set to false
* on output.
*
* @param alock this object's autolock
*
* @note
* Using mParent and mVirtualBox members after this method returns
* is forbidden.
*/
{
LogFlowThisFunc (("\n"));
/* uninit all children */
setReady (false);
if (mParent)
mParent->removeDependentChild (this);
else
{
mVirtualBox->removeDependentChild (this);
}
}
// IHardDisk properties
/////////////////////////////////////////////////////////////////////////////
{
if (!aId)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
if (!aStorageType)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
if (!aLocation)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
if (!aType)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
AutoWriteLock alock (this);
CHECK_READY();
if (mRegistered)
tr ("You cannot change the type of the registered hard disk '%ls'"),
/* return silently if nothing to do */
return S_OK;
tr ("Currently, changing the type of VMDK hard disks is not allowed"));
tr ("Currently, changing the type of iSCSI hard disks is not allowed"));
/// @todo (dmik) later: allow to change the type on any registered hard disk
// depending on whether it is attached or not, has children etc.
// Don't forget to save hdd configuration afterwards.
return S_OK;
}
{
if (!aParent)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
if (!aChildren)
return E_POINTER;
AutoReadLock lock(this);
CHECK_READY();
return S_OK;
}
{
if (!aRoot)
return E_POINTER;
AutoReadLock lock(this);
CHECK_READY();
return S_OK;
}
{
if (!aAccessible)
return E_POINTER;
AutoWriteLock alock (this);
CHECK_READY();
return rc;
return S_OK;
}
{
if (!aAllAccessible)
return E_POINTER;
AutoWriteLock alock (this);
CHECK_READY();
if (mParent)
{
/* check the accessibility state of all ancestors */
while (parent)
{
break;
if (!*aAllAccessible)
break;
}
return rc;
}
return rc;
return S_OK;
}
{
if (!aLastAccessError)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
if (!aMachineId)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
if (!aSnapshotId)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
return E_INVALIDARG;
return E_POINTER;
AutoWriteLock alock (this);
CHECK_READY();
CHECK_BUSY();
tr ("Cloning differencing VDI images is not yet supported ('%ls')"),
/* create a project object */
FALSE /* aCancelable */);
/* create an imageless resulting object */
/* append the default path if only a name is given */
{
if (!RTPathHavePath (fp))
{
}
}
/* set the desired path */
/* ensure the directory exists */
{
if (!RTDirExists (imageDir))
{
if (VBOX_FAILURE (vrc))
{
tr ("Could not create a directory '%s' "
"to store the image file (%Vrc)"),
}
}
}
/* mark as busy (being created)
* (VDI task thread will unmark it) */
/* fill in a VDI task data */
/* increase readers until finished
* (VDI task thread will decrease them) */
addReader();
/* create the hard disk creation thread, pass operation data */
(void *) task, 0, RTTHREADTYPE_MAIN_HEAVY_WORKER,
0, "VDITask");
if (VBOX_FAILURE (vrc))
{
delete task;
return E_FAIL;
}
/* return interfaces to the caller */
return S_OK;
}
// public methods for internal purposes only
/////////////////////////////////////////////////////////////////////////////
/**
* Returns the very first (grand-) parent of this hard disk or the hard
* disk itself, if it doesn't have a parent.
*
* @note
* Must be called from under the object's lock
*/
{
return root;
}
/**
* Attempts to mark the hard disk as registered.
* Must be always called by every reimplementation.
* Only VirtualBox can call this method.
*
* @param aRegistered true to set registered and false to set unregistered
*/
{
AutoWriteLock alock (this);
CHECK_READY();
if (aRegistered)
{
if (mRegistered)
tr ("Hard disk '%ls' is already registered"),
CHECK_BUSY();
}
else
{
if (!mRegistered)
tr ("Hard disk '%ls' is already unregistered"),
if (!mMachineId.isEmpty())
tr ("Hard disk '%ls' is attached to a virtual machine with UUID {%s}"),
tr ("Hard disk '%ls' has %d differencing hard disks based on it"),
}
return S_OK;
}
/**
* Checks basic accessibility of this hard disk only (w/o parents).
* Must be always called by every HardDisk::getAccessible() reimplementation
* in the first place.
*
* When @a aCheckBusy is true, this method checks that mBusy = false (and
* returns an appropriate error if not). This lets reimplementations
* successfully call addReader() after getBaseAccessible() succeeds to
* reference the disk and protect it from being modified or deleted before
* the remaining check steps are done. Note that in this case, the
* reimplementation must enter the object lock before calling this method and
* must not leave it before calling addReader() to avoid race condition.
*
* When @a aCheckReaders is true, this method checks that mReaders = 0 (and
* returns an appropriate error if not). When set to true together with
* @a aCheckBusy, this lets reimplementations successfully call setBusy() after
* getBaseAccessible() succeeds to lock the disk and make sure nobody is
* referencing it until the remaining check steps are done. Note that in this
* case, the reimplementation must enter the object lock before calling this
* method and must not leave it before calling setBusy() to avoid race
* condition.
*
* @param aAccessError On output, a null string indicates the hard disk is
* accessible, otherwise contains a message describing
* the reason of inaccessibility.
* @param aCheckBusy Whether to do the busy check or not.
* @param aCheckReaders Whether to do readers check or not.
*/
bool aCheckBusy /* = false */,
bool aCheckReaders /* = false */)
{
AutoReadLock alock (this);
CHECK_READY();
if (aCheckBusy)
{
if (mBusy)
{
tr ("Hard disk '%ls' is being exclusively used by another task"),
return S_OK;
}
}
if (aCheckReaders)
{
if (mReaders > 0)
{
tr ("Hard disk '%ls' is being used by another task (%d readers)"),
return S_OK;
}
}
return S_OK;
}
/**
* Returns true if the set of properties that makes this object unique
* is equal to the same set of properties in the given object.
*/
{
AutoReadLock alock (this);
if (!isReady())
return false;
/// @todo (dmik) besides UUID, we temporarily use toString() to uniquely
// identify objects. This is ok for VDIs but may be not good for iSCSI,
// so it will need a reimp of this method.
}
/**
* Marks this hard disk as busy.
* A busy hard disk cannot have readers and its properties (UUID, description)
* cannot be externally modified.
*/
{
AutoWriteLock alock (this);
AssertReturnVoid (isReady());
mBusy = true;
}
/**
* Clears the busy flag previously set by #setBusy().
*/
{
AutoWriteLock alock (this);
AssertReturnVoid (isReady());
mBusy = false;
}
/**
* Increases the number of readers of this hard disk.
* A hard disk that have readers cannot be marked as busy (and vice versa)
* and its properties (UUID, description) cannot be externally modified.
*/
{
AutoWriteLock alock (this);
AssertReturnVoid (isReady());
++ mReaders;
}
/**
* Decreases the number of readers of this hard disk.
*/
void HardDisk::releaseReader()
{
AutoWriteLock alock (this);
AssertReturnVoid (isReady());
-- mReaders;
}
/**
* Increases the number of readers on all ancestors of this hard disk.
*/
void HardDisk::addReaderOnAncestors()
{
AutoWriteLock alock (this);
AssertReturnVoid (isReady());
if (mParent)
{
}
}
/**
* Decreases the number of readers on all ancestors of this hard disk.
*/
void HardDisk::releaseReaderOnAncestors()
{
AutoWriteLock alock (this);
AssertReturnVoid (isReady());
if (mParent)
{
mParent->releaseReader();
}
}
/**
* Returns true if this hard disk has children not belonging to the same
* machine.
*/
bool HardDisk::hasForeignChildren()
{
AutoReadLock alock (this);
AssertReturn (isReady(), false);
/* check all children */
++ it)
{
return true;
}
return false;
}
/**
* Marks this hard disk and all its children as busy.
* Used for merge operations.
* Returns a meaningful error info on failure.
*/
{
AutoWriteLock alock (this);
++ it)
{
{
/* reset the busy flag of all previous children */
}
else
}
mBusy = true;
return S_OK;
}
/**
* Clears the busy flag of this hard disk and all its children.
* An opposite to #setBusyWithChildren.
*/
void HardDisk::clearBusyWithChildren()
{
AutoWriteLock alock (this);
AssertReturn (isReady(), (void) 0);
AssertReturn (mBusy == true, (void) 0);
++ it)
{
}
mBusy = false;
}
/**
* Checks that this hard disk and all its direct children are accessible.
*
* @note Locks this object for writing.
*/
{
/* getAccessible() needs a write lock */
AutoWriteLock alock (this);
return rc;
++ it)
{
return rc;
}
return rc;
}
/**
* Checks that this hard disk and all its descendants are consistent.
* For now, the consistency means that:
*
* 1) every differencing image is associated with a registered machine
* 2) every root image that has differencing children is associated with
* a registered machine.
*
* This method is used by the VirtualBox constructor after loading all hard
* disks and all machines.
*/
{
AutoReadLock alock (this);
if (isDifferencing())
{
mMachineId.isEmpty());
if (mMachineId.isEmpty())
tr ("Differencing hard disk '%ls' is not associated with "
"any registered virtual machine or snapshot"),
}
{
if (mMachineId.isEmpty())
tr ("Hard disk '%ls' is not associated with any registered "
"virtual machine or snapshot, but has differencing child "
"hard disks based on it"),
}
++ it)
{
}
return rc;
}
/**
* Creates a differencing hard disk for this hard disk and returns the
* created hard disk object to the caller.
*
* The created differencing hard disk is automatically added to the list of
* children of this hard disk object and registered within VirtualBox.
* The specified progress object (if not NULL) receives the percentage
* of the operation completion. However, it is responsibility of the caller to
* call Progress::notifyComplete() after this method returns.
*
* @param aFolder folder where to create the differencing disk
* (must be a full path)
* @param aMachineId machine ID the new hard disk will belong to
* @param aHardDisk resulting hard disk object
* @param aProgress progress object to run during copy operation
* (may be NULL)
*
* @note
* Must be NOT called from under locks of other objects that need external
* access dirung this method execurion!
*/
{
E_FAIL);
AutoWriteLock alock (this);
CHECK_READY();
/* try to make the path relative to the vbox home dir */
const char *filePathToRel = filePathTo;
{
}
/* first ensure the directory exists */
{
if (!RTDirExists (dir))
{
if (VBOX_FAILURE (vrc))
{
tr ("Could not create a directory '%s' "
"to store the image file (%Vrc)"),
}
}
}
/* call storage type specific diff creation method */
vdi.createObject();
TRUE /* aRegistered */);
/* associate the created hard disk with the given machine */
return S_OK;
}
/**
* Checks if the given change of \a aOldPath to \a aNewPath affects the path
* of this hard disk or any of its children and updates it if necessary (by
* calling #updatePath()). Intended to be called only by
* VirtualBox::updateSettings() if a machine's name change causes directory
* renaming that affects this image.
*
* @param aOldPath old path (full)
* @param aNewPath new path (full)
*
* @note Locks this object and all children for writing.
*/
{
AutoWriteLock alock (this);
AssertReturnVoid (isReady());
/* update paths of all children */
++ it)
{
}
}
/**
* Helper method that deduces a hard disk object type to create from
* the location string format and from the contents of the resource
* pointed to by the location string.
*
* Currently, the location string must be a file path which is
* passed to the HVirtualDiskImage or HVMDKImage initializer in
* attempt to create a hard disk object.
*
* @param aVirtualBox
* @param aLocation
* @param hardDisk
*
* @return
*/
/* static */
{
/* null and empty strings are not allowed locations */
static const struct
{
const char *ext;
}
storageTypes[] =
{
/* try the plugin format first if there is no extension match */
/* then try the rest */
{ HardDiskStorageType_VMDKImage, ".vmdk" },
{ HardDiskStorageType_VirtualDiskImage, ".vdi" },
{ HardDiskStorageType_VHDImage, ".vhd" },
};
/* try to guess the probe order by extension */
bool haveFirst = false;
{
if (storageTypes [i].ext &&
{
first = i;
haveFirst = true;
break;
}
}
{
switch (storageTypes [j].type)
{
{
obj.createObject();
FALSE /* aRegistered */);
{
return rc;
}
break;
}
{
obj.createObject();
FALSE /* aRegistered */);
{
return rc;
}
break;
}
{
obj.createObject();
FALSE /* aRegistered */);
{
return rc;
}
break;
}
{
obj.createObject();
FALSE /* aRegistered */);
{
return rc;
}
break;
}
default:
{
}
}
/* remember the error of the matching class */
{
}
}
if (haveFirst)
{
/* firstErr will restore the error info upon destruction */
return firstRC;
}
/* There was no exact extension match; chances are high that an error we
* got after probing is useless. Use a generic error message instead. */
tr ("Could not recognize the format of the hard disk '%ls'. "
"Either the given format is not supported or hard disk data "
"is corrupt"),
}
// protected methods
/////////////////////////////////////////////////////////////////////////////
/**
* Loads the base settings of the hard disk from the given node, registers
* it and loads and registers all child hard disks as HVirtualDiskImage
* instances.
*
* Subclasses must call this method in their init() or loadSettings() methods
* *after* they load specific parts of data (at least, necessary to let
* toString() function correctly), in order to be properly loaded from the
* settings file and registered.
*
* @param aHDNode <HardDisk> node when #isDifferencing() = false, or
* <DiffHardDisk> node otherwise.
*
* @note
* Must be called from under the object's lock
*/
{
using namespace settings;
/* required */
if (!isDifferencing())
{
/* type required for <HardDisk> nodes only */
else
E_FAIL);
}
else
/* load all children */
{
vdi.createObject();
}
return rc;
}
/**
* Saves the base settings of the hard disk to the given node
* and saves all child hard disks as <DiffHardDisk> nodes.
*
* Subclasses must call this method in their saveSettings() methods
* in order to be properly saved to the settings file.
*
* @param aHDNode <HardDisk> node when #isDifferencing() = false, or
* <DiffHardDisk> node otherwise.
*
* @note
* Must be called from under the object's read lock
*/
{
using namespace settings;
/* uuid (required) */
if (!isDifferencing())
{
/* type (required) */
switch (mType)
{
case HardDiskType_Normal:
type = "normal";
break;
case HardDiskType_Immutable:
type = "immutable";
break;
type = "writethrough";
break;
}
}
/* save all children */
++ it)
{
{
}
}
return S_OK;
}
////////////////////////////////////////////////////////////////////////////////
// HVirtualDiskImage class
////////////////////////////////////////////////////////////////////////////////
// constructor / destructor
////////////////////////////////////////////////////////////////////////////////
{
return rc;
mState = NotCreated;
mStateCheckWaiters = 0;
mSize = 0;
mActualSize = 0;
return S_OK;
}
void HVirtualDiskImage::FinalRelease()
{
HardDisk::FinalRelease();
}
// public initializer/uninitializer for internal purposes only
////////////////////////////////////////////////////////////////////////////////
// public methods for internal purposes only
/////////////////////////////////////////////////////////////////////////////
/**
* Initializes the VDI hard disk object by reading its properties from
* the given configuration node. The created hard disk will be marked as
* registered on success.
*
* @param aHDNode <HardDisk> or <DiffHardDisk> node.
* @param aVDINode <VirtualDiskImage> node.
*/
{
using namespace settings;
LogFlowThisFunc (("\n"));
AutoWriteLock alock (this);
do
{
/* set ready to let protectedUninit() be called on failure */
setReady (true);
/* filePath (required) */
/* load basic settings and children */
mRegistered = TRUE;
/* Don't call queryInformation() for registered hard disks to
* prevent the calling thread (i.e. the VirtualBox server startup
* thread) from an unexpected freeze. The vital mId property (UUID)
* is read from the registry file in loadSettings(). To get the rest,
* the user will have to call COMGETTER(Accessible) manually. */
}
while (0);
uninit();
return rc;
}
/**
* Initializes the VDI hard disk object using the given image file name.
*
* @param aVirtualBox VirtualBox parent.
* @param aParent Parent hard disk.
* @param aFilePath Path to the image file, or @c NULL to create an
* image-less object.
* @param aRegistered Whether to mark this disk as registered or not
* (ignored when @a aFilePath is @c NULL, assuming @c FALSE)
*/
{
LogFlowThisFunc (("aFilePath='%ls', aRegistered=%d\n",
aFilePath, aRegistered));
AutoWriteLock alock (this);
do
{
/* set ready to let protectedUninit() be called on failure */
setReady (true);
{
/* Call queryInformation() anyway (even if it will block), because
* it is the only way to get the UUID of the existing VDI and
* initialize the vital mId property. */
{
/* We are constructing a new HVirtualDiskImage object. If there
* is a fatal accessibility error (we cannot read image UUID),
* we have to fail. We do so even on non-fatal errors as well,
* because it's not worth to keep going with the inaccessible
* image from the very beginning (when nothing else depends on
* it yet). */
}
}
else
{
mRegistered = FALSE;
mState = NotCreated;
}
}
while (0);
uninit();
return rc;
}
/**
* 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 HVirtualDiskImage::uninit()
{
LogFlowThisFunc (("\n"));
AutoWriteLock alock (this);
if (!isReady())
return;
}
// IHardDisk properties
////////////////////////////////////////////////////////////////////////////////
{
if (!aDescription)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
AutoWriteLock alock (this);
CHECK_READY();
{
if (VBOX_FAILURE (vrc))
tr ("Could not change the description of the VDI hard disk '%ls' "
"(%Vrc)"),
}
return S_OK;
}
{
if (!aSize)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
/* only a non-differencing image knows the logical size */
if (isDifferencing())
return S_OK;
}
{
if (!aActualSize)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
// IVirtualDiskImage properties
////////////////////////////////////////////////////////////////////////////////
{
if (!aFilePath)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
AutoWriteLock alock (this);
CHECK_READY();
if (mState != NotCreated)
return setError (E_ACCESSDENIED,
tr ("Cannot change the file path of the existing hard disk '%ls'"),
/* append the default path if only a name is given */
{
if (!RTPathHavePath (fp))
{
}
}
return setFilePath (path);
}
{
if (!aCreated)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
// IVirtualDiskImage methods
/////////////////////////////////////////////////////////////////////////////
{
if (!aProgress)
return E_POINTER;
AutoWriteLock alock (this);
CHECK_READY();
}
{
if (!aProgress)
return E_POINTER;
AutoWriteLock alock (this);
CHECK_READY();
}
{
AutoWriteLock alock (this);
CHECK_READY();
if (mRegistered)
return setError (E_ACCESSDENIED,
tr ("Cannot delete an image of the registered hard disk image '%ls"),
mFilePathFull.raw());
if (mState == NotCreated)
tr ("Hard disk image has been already deleted or never created"));
mState = NotCreated;
/* reset the fields */
mSize = 0;
mActualSize = 0;
return S_OK;
}
/////////////////////////////////////////////////////////////////////////////
/**
* Attempts to mark the hard disk as registered.
* Only VirtualBox can call this method.
*/
{
AutoWriteLock alock (this);
CHECK_READY();
if (aRegistered)
{
if (mState == NotCreated)
tr ("Image file '%ls' is not yet created for this hard disk"),
mFilePathFull.raw());
if (isDifferencing())
tr ("Hard disk '%ls' is differencing and cannot be unregistered "
"explicitly"),
mFilePathFull.raw());
}
else
{
}
}
/**
* Checks accessibility of this hard disk image only (w/o parents).
*
* @param aAccessError on output, a null string indicates the hard disk is
* accessible, otherwise contains a message describing
* the reason of inaccessibility.
*
* @note Locks this object for writing.
*/
{
/* queryInformation() needs a write lock */
AutoWriteLock alock (this);
CHECK_READY();
if (mStateCheckSem != NIL_RTSEMEVENTMULTI)
{
/* An accessibility check in progress on some other thread,
* wait for it to finish. */
if (mStateCheckWaiters == 0)
{
}
/* don't touch aAccessError, it has been already set */
return S_OK;
}
/* check the basic accessibility */
return rc;
{
return queryInformation (&aAccessError);
}
mFilePathFull.raw());
return S_OK;
}
/**
* Saves hard disk settings to the specified storage node and saves
* all children to the specified hard disk node
*
* @param aHDNode <HardDisk> or <DiffHardDisk> node.
* @param aStorageNode <VirtualDiskImage> node.
*/
{
AutoReadLock alock (this);
CHECK_READY();
/* filePath (required) */
/* save basic settings and children */
}
/**
* Checks if the given change of \a aOldPath to \a aNewPath affects the path
* of this hard disk and updates it if necessary to reflect the new location.
* Intended to be from HardDisk::updatePaths().
*
* @param aOldPath old path (full)
* @param aNewPath new path (full)
*
* @note Locks this object for writing.
*/
{
AutoWriteLock alock (this);
AssertReturnVoid (isReady());
{
LogFlowThisFunc (("-> updated: full={%s} short={%s}\n",
}
}
/**
* Returns the string representation of this hard disk.
* When \a aShort is false, returns the full image file path.
* Otherwise, returns the image file name only.
*
* @param aShort if true, a short representation is returned
*
* @note Locks this object for reading.
*/
{
AutoReadLock alock (this);
if (!aShort)
return mFilePathFull;
else
{
}
}
/**
* Creates a clone of this hard disk by storing hard disk data in the given
* VDI file.
*
* If the operation fails, @a aDeleteTarget will be set to @c true unless the
* failure happened because the target file already existed.
*
* @param aId UUID to assign to the created image.
* @param aTargetPath VDI file where the cloned image is to be to stored.
* @param aProgress progress object to run during operation.
* @param aDeleteTarget Whether it is recommended to delete target on
* failure or not.
*/
{
/* normally, the target file should be deleted on error */
aDeleteTarget = true;
AutoWriteLock alock (this);
/// @todo (dmik) cloning of differencing images is not yet supported
if (mState == NotCreated)
tr ("Source hard disk image '%s' is not yet created"),
filePathFull.raw());
addReader();
/* We don't want to delete existing user files */
if (vrc == VERR_ALREADY_EXISTS)
aDeleteTarget = false;
if (VBOX_SUCCESS (vrc))
if (VBOX_FAILURE (vrc))
tr ("Could not copy the hard disk image '%s' to '%s' (%Vrc)"),
return S_OK;
}
/**
* Creates a new differencing image for this hard disk with the given
* VDI file name.
*
* @param aId UUID to assign to the created image
* @param aTargetPath VDI file where to store the created differencing image
* @param aProgress progress object to run during operation
* (can be NULL)
*/
{
AutoWriteLock alock (this);
addReader();
/* update the UUID to correspond to the file name */
if (VBOX_SUCCESS (vrc))
if (VBOX_FAILURE (vrc))
tr ("Could not create a differencing hard disk '%s' (%Vrc)"),
return S_OK;
}
/**
* Copies the image file of this hard disk to a separate VDI file (with an
* unique creation UUID) and creates a new hard disk object for the copied
* image. The copy will be created as a child of this hard disk's parent
* (so that this hard disk must be a differencing one).
*
* The specified progress object (if not NULL) receives the percentage
* of the operation completion. However, it is responsibility of the caller to
* call Progress::notifyComplete() after this method returns.
*
* @param aFolder folder where to create a copy (must be a full path)
* @param aMachineId machine ID the new hard disk will belong to
* @param aHardDisk resulting hard disk object
* @param aProgress progress object to run during copy operation
* (may be NULL)
*
* @note
* Must be NOT called from under locks of other objects that need external
* access dirung this method execurion!
*/
{
E_FAIL);
AutoWriteLock alock (this);
CHECK_READY();
/* try to make the path relative to the vbox home dir */
const char *filePathToRel = filePathTo;
{
}
/* first ensure the directory exists */
{
if (!RTDirExists (dir))
{
if (VBOX_FAILURE (vrc))
{
tr ("Could not create a directory '%s' "
"to store the image file (%Vrc)"),
}
}
}
/* get modification and parent UUIDs of this image */
if (VBOX_SUCCESS (vrc))
&parentUuid, &parentModUuid);
// update the UUID of the copy to correspond to the file name
// and copy all other UUIDs from this image
if (VBOX_SUCCESS (vrc))
&parentUuid, &parentModUuid);
if (VBOX_FAILURE (vrc))
tr ("Could not copy the hard disk image '%s' to '%s' (%Vrc)"),
vdi.createObject();
TRUE /* aRegistered */);
return rc;
/* associate the created hard disk with the given machine */
return rc;
return S_OK;
}
/**
* Merges this child image to its parent image and updates the parent UUID
* of all children of this image (to point to this image's parent).
* It's a responsibility of the caller to unregister and uninitialize
* the merged image on success.
*
* This method is intended to be called on a worker thread (the operation
* can be time consuming).
*
* The specified progress object (if not NULL) receives the percentage
* of the operation completion. However, it is responsibility of the caller to
* call Progress::notifyComplete() after this method returns.
*
* @param aProgress progress object to run during copy operation
* (may be NULL)
*
* @note
* This method expects that both this hard disk and the paret hard disk
* are marked as busy using #setBusyWithChildren() prior to calling it!
* Busy flags of both hard disks will be cleared by this method
* on a successful return. In case of failure, #clearBusyWithChildren()
* must be called on a parent.
*
* @note
* Must be NOT called from under locks of other objects that need external
* access dirung this method execurion!
*/
{
AutoWriteLock alock (this);
CHECK_READY();
("non VDI storage types are not yet supported!"), E_FAIL);
parentLock.leave();
parentLock.enter();
if (VBOX_FAILURE (vrc))
tr ("Could not merge the hard disk image '%ls' to "
"its parent image '%ls' (%Vrc)"),
{
{
/* reparent the child */
if (mParent)
/* change the parent UUID in the image as well */
if (VBOX_FAILURE (vrc))
{
tr ("Could not access the hard disk image '%ls' (%Vrc)"),
break;
}
if (VBOX_FAILURE (vrc))
{
tr ("Could not update parent UUID of the hard disk image "
"'%ls' (%Vrc)"),
break;
}
}
return rc;
}
/* detach all our children to avoid their uninit in #uninit() */
clearBusy();
return S_OK;
}
/**
* Merges this image to all its child images, updates the parent UUID
* of all children of this image (to point to this image's parent).
* It's a responsibility of the caller to unregister and uninitialize
* the merged image on success.
*
* This method is intended to be called on a worker thread (the operation
* can be time consuming).
*
* The specified progress object (if not NULL) receives the percentage
* of the operation completion. However, it is responsibility of the caller to
* call Progress::notifyComplete() after this method returns.
*
* @param aProgress progress object to run during copy operation
* (may be NULL)
*
* @note
* This method expects that both this hard disk and all children
* are marked as busy using setBusyWithChildren() prior to calling it!
* Busy flags of all affected hard disks will be cleared by this method
* on a successful return. In case of failure, #clearBusyWithChildren()
* must be called for this hard disk.
*
* @note
* Must be NOT called from under locks of other objects that need external
* access dirung this method execurion!
*/
{
AutoWriteLock alock (this);
CHECK_READY();
/* this must be a diff image */
("non VDI storage types are not yet supported!"), E_FAIL);
{
/* iterate over a copy since we will modify the list */
{
if (VBOX_FAILURE (vrc))
{
tr ("Could not merge the hard disk image '%ls' to "
"its parent image '%ls' (%Vrc)"),
break;
}
/* reparent the child */
if (mParent)
/* change the parent UUID in the image as well */
if (VBOX_FAILURE (vrc))
{
tr ("Could not access the hard disk image '%ls' (%Vrc)"),
break;
}
if (VBOX_FAILURE (vrc))
{
tr ("Could not update parent UUID of the hard disk image "
"'%ls' (%Vrc)"),
break;
}
/* detach child to avoid its uninit in #uninit() */
/* remove the busy flag */
}
return rc;
}
clearBusy();
return S_OK;
}
/**
* Deletes and recreates the differencing hard disk image from scratch.
* The file name and UUID remain the same.
*/
{
AutoWriteLock alock (this);
CHECK_READY();
("non-VDI storage types are not yet supported!"), E_FAIL);
if (VBOX_FAILURE (vrc))
tr ("Could not delete the image file '%s' (%Vrc)"),
/* update the UUID to correspond to the file name */
if (VBOX_SUCCESS (vrc))
if (VBOX_FAILURE (vrc))
tr ("Could not create a differencing hard disk '%s' (%Vrc)"),
return S_OK;
}
{
AutoWriteLock alock (this);
CHECK_READY();
tr ("Could not delete the image file '%ls' (%Vrc)"),
return S_OK;
}
// private methods
/////////////////////////////////////////////////////////////////////////////
/**
* Helper to set a new file path.
* Resolves a path relatively to the Virtual Box home directory.
*
* @note
* Must be called from under the object's lock!
*/
{
{
/* get the full file name */
char filePathFull [RTPATH_MAX];
filePathFull, sizeof (filePathFull));
if (VBOX_FAILURE (vrc))
}
else
{
}
return S_OK;
}
/**
* Helper to query information about the VDI hard disk.
*
* @param aAccessError not used when NULL, otherwise see #getAccessible()
*
* @note Must be called from under the object's write lock, only after
* CHECK_BUSY_AND_READERS() succeeds.
*/
{
/* create a lock object to completely release it later */
AutoWriteLock alock (this);
int vrc = VINF_SUCCESS;
/* lazily create a semaphore */
/* Reference the disk to prevent any concurrent modifications
* after releasing the lock below (to unblock getters before
* a lengthy operation). */
addReader();
/* VBoxVHDD management interface needs to be optimized: we're opening a
* file three times in a raw to get three bits of information. */
do
{
/* check the image file */
if (VBOX_FAILURE (vrc))
break;
{
/* check that the actual UUID of the image matches the stored UUID */
{
errMsg = Utf8StrFmt (
tr ("Actual UUID {%Vuuid} of the hard disk image '%s' doesn't "
"match UUID {%Vuuid} stored in the registry"),
break;
}
}
else
{
/* assgn an UUID read from the image file */
}
if (mParent)
{
/* check parent UUID */
{
errMsg = Utf8StrFmt (
tr ("UUID {%Vuuid} of the parent image '%ls' stored in "
"the hard disk image file '%s' doesn't match "
"UUID {%Vuuid} stored in the registry"),
break;
}
}
{
errMsg = Utf8StrFmt (
tr ("Hard disk image '%s' is a differencing image that is linked "
"to a hard disk with UUID {%Vuuid} and cannot be used "
"directly as a base hard disk"),
break;
}
{
if (VBOX_SUCCESS (vrc))
{
if (VBOX_SUCCESS (vrc))
mActualSize = size;
RTFileClose (file);
}
if (VBOX_FAILURE (vrc))
break;
}
if (!mParent)
{
/* query logical size only for non-differencing images */
if (VBOX_SUCCESS (vrc))
{
/* convert to MBytes */
}
if (VBOX_FAILURE (vrc))
break;
}
}
while (0);
/* enter the lock again */
/* remove the reference */
{
LogWarningFunc (("'%ls' is not accessible "
"(rc=%08X, vrc=%Vrc, errMsg='%ls')\n",
if (aAccessError)
{
*aAccessError = errMsg;
else if (VBOX_FAILURE (vrc))
*aAccessError = Utf8StrFmt (
tr ("Could not access hard disk image '%ls' (%Vrc)"),
}
/* downgrade to not accessible */
}
else
{
if (aAccessError)
aAccessError->setNull();
mState = Accessible;
}
/* inform waiters if there are any */
if (mStateCheckWaiters > 0)
{
}
else
{
/* delete the semaphore ourselves */
}
return rc;
}
/**
* Helper to create hard disk images.
*
* @param aSize size in MB
* @param aDynamic dynamic or fixed image
* @param aProgress address of IProgress pointer to return
*/
{
AutoWriteLock alock (this);
if (mState != NotCreated)
return setError (E_ACCESSDENIED,
tr ("Hard disk image '%ls' is already created"),
mFilePathFull.raw());
if (!mFilePathFull)
return setError (E_ACCESSDENIED,
tr ("Cannot create a hard disk image using an empty (null) file path"),
mFilePathFull.raw());
/* first ensure the directory exists */
{
if (!RTDirExists (imageDir))
{
if (VBOX_FAILURE (vrc))
{
tr ("Could not create a directory '%s' "
"to store the image file (%Vrc)"),
}
}
}
/* check whether the given file exists or not */
if (vrc != VERR_FILE_NOT_FOUND)
{
if (VBOX_SUCCESS (vrc))
RTFileClose (file);
switch(vrc)
{
case VINF_SUCCESS:
tr ("Image file '%ls' already exists"),
mFilePathFull.raw());
default:
tr ("Invalid image file path '%ls' (%Vrc)"),
}
}
/* check VDI size limits */
{
return setError (E_INVALIDARG,
tr ("Invalid VDI size: %llu MB (must be in range [1, %llu] MB)"),
aSize, maxVDISize);
}
/* create a project object */
{
: tr ("Creating a fixed-size hard disk");
}
/* mark as busy (being created)
* (VDI task thread will unmark it) */
setBusy();
/* fill in VDI task data */
: VDITask::CreateStatic,
this, progress);
/* create the hard disk creation thread, pass operation data */
RTTHREADTYPE_MAIN_HEAVY_WORKER, 0, "VDITask");
if (VBOX_FAILURE (vrc))
{
clearBusy();
delete task;
}
else
{
/* get one interface for the caller */
}
return rc;
}
/* static */
{
{
case VDITask::CloneToImage: break;
default: AssertFailedReturn (VERR_GENERAL_FAILURE); break;
}
bool deleteTarget = true;
{
/// @todo (dmik) check locks
/* release reader added in HardDisk::CloneToImage() */
}
else
{
/* We don't want to delete existing user files */
if (vrc == VERR_ALREADY_EXISTS)
deleteTarget = false;
{
/* we have a non-null UUID, update the created image */
}
if (VBOX_FAILURE (vrc))
{
errorMsg = Utf8StrFmt (
tr ("Falied to create a hard disk image '%ls' (%Vrc)"),
}
}
/* clear busy set in in HardDisk::CloneToImage() or
* in HVirtualDiskImage::createImage() */
{
/* update VDI data fields */
/* we want to deliver the access check result to the caller
* immediately, before he calls HardDisk::GetAccssible() himself. */
errMsg);
else
}
else
{
/* delete the target file so we don't have orphaned files */
if (deleteTarget)
/* complete the progress object */
if (errorMsg)
errorMsg);
else
}
delete task;
return VINF_SUCCESS;
}
////////////////////////////////////////////////////////////////////////////////
// HISCSIHardDisk class
////////////////////////////////////////////////////////////////////////////////
// constructor / destructor
////////////////////////////////////////////////////////////////////////////////
{
return rc;
mSize = 0;
mActualSize = 0;
mPort = 0;
mLun = 0;
return S_OK;
}
void HISCSIHardDisk::FinalRelease()
{
HardDisk::FinalRelease();
}
// public initializer/uninitializer for internal purposes only
////////////////////////////////////////////////////////////////////////////////
// public methods for internal purposes only
/////////////////////////////////////////////////////////////////////////////
/**
* Initializes the iSCSI hard disk object by reading its properties from
* the given configuration node. The created hard disk will be marked as
* registered on success.
*
* @param aHDNode <HardDisk> node.
* @param aVDINod <ISCSIHardDisk> node.
*/
{
using namespace settings;
LogFlowThisFunc (("\n"));
AutoWriteLock alock (this);
do
{
/* set ready to let protectedUninit() be called on failure */
setReady (true);
/* server (required) */
/* target (required) */
/* port (optional) */
/* lun (optional) */
/* userName (optional) */
/* password (optional) */
LogFlowThisFunc (("'iscsi:%ls:%hu@%ls/%ls:%llu'\n",
mLun));
/* load basic settings and children */
if (mType != HardDiskType_Writethrough)
{
tr ("Currently, non-Writethrough iSCSI hard disks are not "
"allowed ('%ls')"),
break;
}
mRegistered = TRUE;
}
while (0);
uninit();
return rc;
}
/**
* Initializes the iSCSI hard disk object using default values for all
* properties. The created hard disk will NOT be marked as registered.
*/
{
LogFlowThisFunc (("\n"));
AutoWriteLock alock (this);
do
{
/* set ready to let protectedUninit() be called on failure */
setReady (true);
/* we have to generate a new UUID */
/* currently, all iSCSI hard disks are writethrough */
mRegistered = FALSE;
}
while (0);
uninit();
return rc;
}
/**
* 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 HISCSIHardDisk::uninit()
{
LogFlowThisFunc (("\n"));
AutoWriteLock alock (this);
if (!isReady())
return;
}
// IHardDisk properties
////////////////////////////////////////////////////////////////////////////////
{
if (!aDescription)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
AutoWriteLock alock (this);
CHECK_READY();
if (mDescription != aDescription)
{
if (mRegistered)
return mVirtualBox->saveSettings();
}
return S_OK;
}
{
if (!aSize)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
if (!aActualSize)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
// IISCSIHardDisk properties
////////////////////////////////////////////////////////////////////////////////
{
if (!aServer)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
return E_INVALIDARG;
AutoWriteLock alock (this);
CHECK_READY();
{
if (mRegistered)
return mVirtualBox->saveSettings();
}
return S_OK;
}
{
if (!aPort)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
AutoWriteLock alock (this);
CHECK_READY();
{
if (mRegistered)
return mVirtualBox->saveSettings();
}
return S_OK;
}
{
if (!aTarget)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
return E_INVALIDARG;
AutoWriteLock alock (this);
CHECK_READY();
{
if (mRegistered)
return mVirtualBox->saveSettings();
}
return S_OK;
}
{
if (!aLun)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
AutoWriteLock alock (this);
CHECK_READY();
{
if (mRegistered)
return mVirtualBox->saveSettings();
}
return S_OK;
}
{
if (!aUserName)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
AutoWriteLock alock (this);
CHECK_READY();
{
if (mRegistered)
return mVirtualBox->saveSettings();
}
return S_OK;
}
{
if (!aPassword)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
AutoWriteLock alock (this);
CHECK_READY();
{
if (mRegistered)
return mVirtualBox->saveSettings();
}
return S_OK;
}
/////////////////////////////////////////////////////////////////////////////
/**
* Attempts to mark the hard disk as registered.
* Only VirtualBox can call this method.
*/
{
AutoWriteLock alock (this);
CHECK_READY();
if (aRegistered)
{
tr ("iSCSI Hard disk has no server or target defined"));
}
else
{
}
}
/**
* Checks accessibility of this iSCSI hard disk.
*
* @note Locks this object for writing.
*/
{
/* queryInformation() needs a write lock */
AutoWriteLock alock (this);
CHECK_READY();
/* check the basic accessibility */
return rc;
return queryInformation (aAccessError);
}
/**
* Saves hard disk settings to the specified storage node and saves
* all children to the specified hard disk node
*
* @param aHDNode <HardDisk>.
* @param aStorageNode <ISCSIHardDisk> node.
*/
{
AutoReadLock alock (this);
CHECK_READY();
/* server (required) */
/* target (required) */
/* port (optional, defaults to 0) */
/* lun (optional, force 0x format to conform to XML Schema!) */
/* userName (optional) */
/* password (optional) */
/* save basic settings and children */
}
/**
* Returns the string representation of this hard disk.
* When \a aShort is false, returns the full image file path.
* Otherwise, returns the image file name only.
*
* @param aShort if true, a short representation is returned
*
* @note Locks this object for reading.
*/
{
AutoReadLock alock (this);
if (!aShort)
{
/* the format is iscsi:[<user@>]<server>[:<port>]/<target>[:<lun>] */
}
else
{
}
return str;
}
/**
* Creates a clone of this hard disk by storing hard disk data in the given
* VDI file.
*
* If the operation fails, @a aDeleteTarget will be set to @c true unless the
* failure happened because the target file already existed.
*
* @param aId UUID to assign to the created image.
* @param aTargetPath VDI file where the cloned image is to be to stored.
* @param aProgress progress object to run during operation.
* @param aDeleteTarget Whether it is recommended to delete target on
* failure or not.
*/
{
ComAssertMsgFailed (("Not implemented"));
return E_NOTIMPL;
// AssertReturn (isBusy() == false, E_FAIL);
// addReader();
// releaseReader();
}
/**
* Creates a new differencing image for this hard disk with the given
* VDI file name.
*
* @param aId UUID to assign to the created image
* @param aTargetPath VDI file where to store the created differencing image
* @param aProgress progress object to run during operation
* (can be NULL)
*/
{
ComAssertMsgFailed (("Not implemented"));
return E_NOTIMPL;
// AssertReturn (isBusy() == false, E_FAIL);
// addReader();
// releaseReader();
}
// private methods
/////////////////////////////////////////////////////////////////////////////
/**
* Helper to query information about the iSCSI hard disk.
*
* @param aAccessError see #getAccessible()
*
* @note Must be called from under the object's write lock, only after
* CHECK_BUSY_AND_READERS() succeeds.
*/
{
/* create a lock object to completely release it later */
AutoWriteLock alock (this);
/// @todo (dmik) query info about this iSCSI disk,
// set mSize and mActualSize,
// or set aAccessError in case of failure
return S_OK;
}
////////////////////////////////////////////////////////////////////////////////
// HVMDKImage class
////////////////////////////////////////////////////////////////////////////////
// constructor / destructor
////////////////////////////////////////////////////////////////////////////////
{
return rc;
mState = NotCreated;
mStateCheckWaiters = 0;
mSize = 0;
mActualSize = 0;
/* Create supported error interface. */
&mInterfaceErrorCallbacks, this, &mVDInterfaces);
/* initialize the container */
return S_OK;
}
void HVMDKImage::FinalRelease()
{
if (mContainer != NULL)
HardDisk::FinalRelease();
}
// public initializer/uninitializer for internal purposes only
////////////////////////////////////////////////////////////////////////////////
// public methods for internal purposes only
/////////////////////////////////////////////////////////////////////////////
/**
* Initializes the VMDK hard disk object by reading its properties from
* the given configuration node. The created hard disk will be marked as
* registered on success.
*
* @param aHDNode <HardDisk> node.
* @param aVMDKNode <VirtualDiskImage> node.
*/
{
using namespace settings;
LogFlowThisFunc (("\n"));
AutoWriteLock alock (this);
do
{
/* set ready to let protectedUninit() be called on failure */
setReady (true);
/* filePath (required) */
/* load basic settings and children */
if (mType != HardDiskType_Writethrough)
{
tr ("Currently, non-Writethrough VMDK images are not "
"allowed ('%ls')"),
break;
}
mRegistered = TRUE;
/* Don't call queryInformation() for registered hard disks to
* prevent the calling thread (i.e. the VirtualBox server startup
* thread) from an unexpected freeze. The vital mId property (UUID)
* is read from the registry file in loadSettings(). To get the rest,
* the user will have to call COMGETTER(Accessible) manually. */
}
while (0);
uninit();
return rc;
}
/**
* Initializes the VMDK hard disk object using the given image file name.
*
* @param aVirtualBox VirtualBox parent.
* @param aParent Currently, must always be @c NULL.
* @param aFilePath Path to the image file, or @c NULL to create an
* image-less object.
* @param aRegistered Whether to mark this disk as registered or not
* (ignored when @a aFilePath is @c NULL, assuming @c FALSE)
*/
{
AutoWriteLock alock (this);
do
{
/* set ready to let protectedUninit() be called on failure */
setReady (true);
/* currently, all VMDK hard disks are writethrough */
{
/* Call queryInformation() anyway (even if it will block), because
* it is the only way to get the UUID of the existing VDI and
* initialize the vital mId property. */
{
/* We are constructing a new HVirtualDiskImage object. If there
* is a fatal accessibility error (we cannot read image UUID),
* we have to fail. We do so even on non-fatal errors as well,
* because it's not worth to keep going with the inaccessible
* image from the very beginning (when nothing else depends on
* it yet). */
}
}
else
{
mRegistered = FALSE;
mState = NotCreated;
}
}
while (0);
uninit();
return rc;
}
/**
* 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 HVMDKImage::uninit()
{
LogFlowThisFunc (("\n"));
AutoWriteLock alock (this);
if (!isReady())
return;
}
// IHardDisk properties
////////////////////////////////////////////////////////////////////////////////
{
if (!aDescription)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
AutoWriteLock alock (this);
CHECK_READY();
return E_NOTIMPL;
/// @todo (r=dmik) implement
//
// if (mState >= Created)
// {
// int vrc = VDISetImageComment (Utf8Str (mFilePathFull), Utf8Str (aDescription));
// if (VBOX_FAILURE (vrc))
// return setError (E_FAIL,
// tr ("Could not change the description of the VDI hard disk '%ls' "
// "(%Vrc)"),
// toString().raw(), vrc);
// }
//
// mDescription = aDescription;
// return S_OK;
}
{
if (!aSize)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
/// @todo (r=dmik) will need this if we add support for differencing VMDKs
//
// /* only a non-differencing image knows the logical size */
// if (isDifferencing())
// return root()->COMGETTER(Size) (aSize);
return S_OK;
}
{
if (!aActualSize)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
// IVirtualDiskImage properties
////////////////////////////////////////////////////////////////////////////////
{
if (!aFilePath)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
AutoWriteLock alock (this);
CHECK_READY();
if (mState != NotCreated)
return setError (E_ACCESSDENIED,
tr ("Cannot change the file path of the existing hard disk '%ls'"),
/* append the default path if only a name is given */
{
if (!RTPathHavePath (fp))
{
}
}
return setFilePath (path);
}
{
if (!aCreated)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
// IVMDKImage methods
/////////////////////////////////////////////////////////////////////////////
{
if (!aProgress)
return E_POINTER;
AutoWriteLock alock (this);
CHECK_READY();
}
{
if (!aProgress)
return E_POINTER;
AutoWriteLock alock (this);
CHECK_READY();
}
{
AutoWriteLock alock (this);
CHECK_READY();
return E_NOTIMPL;
/// @todo (r=dmik) later
// We will need to parse the file in order to delete all related delta and
// sparse images etc. We may also want to obey the .vmdk.lck file
// which is (as far as I understood) created when the VMware VM is
// running or saved etc.
//
// if (mRegistered)
// return setError (E_ACCESSDENIED,
// tr ("Cannot delete an image of the registered hard disk image '%ls"),
// mFilePathFull.raw());
// if (mState == NotCreated)
// return setError (E_FAIL,
// tr ("Hard disk image has been already deleted or never created"));
//
// int vrc = RTFileDelete (Utf8Str (mFilePathFull));
// if (VBOX_FAILURE (vrc))
// return setError (E_FAIL, tr ("Could not delete the image file '%ls' (%Vrc)"),
// mFilePathFull.raw(), vrc);
//
// mState = NotCreated;
//
// /* reset the fields */
// mSize = 0;
// mActualSize = 0;
//
// return S_OK;
}
/////////////////////////////////////////////////////////////////////////////
/**
* Attempts to mark the hard disk as registered.
* Only VirtualBox can call this method.
*/
{
AutoWriteLock alock (this);
CHECK_READY();
if (aRegistered)
{
if (mState == NotCreated)
tr ("Image file '%ls' is not yet created for this hard disk"),
mFilePathFull.raw());
/// @todo (r=dmik) will need this if we add support for differencing VMDKs
// if (isDifferencing())
// return setError (E_FAIL,
// tr ("Hard disk '%ls' is differencing and cannot be unregistered "
// "explicitly"),
// mFilePathFull.raw());
}
else
{
}
}
/**
* Checks accessibility of this hard disk image only (w/o parents).
*
* @param aAccessError on output, a null string indicates the hard disk is
* accessible, otherwise contains a message describing
* the reason of inaccessibility.
*
* @note Locks this object for writing.
*/
{
/* queryInformation() needs a write lock */
AutoWriteLock alock (this);
CHECK_READY();
if (mStateCheckSem != NIL_RTSEMEVENTMULTI)
{
/* An accessibility check in progress on some other thread,
* wait for it to finish. */
if (mStateCheckWaiters == 0)
{
}
/* don't touch aAccessError, it has been already set */
return S_OK;
}
/* check the basic accessibility */
return rc;
{
return queryInformation (&aAccessError);
}
mFilePathFull.raw());
return S_OK;
}
/**
* Saves hard disk settings to the specified storage node and saves
* all children to the specified hard disk node
*
* @param aHDNode <HardDisk> or <DiffHardDisk> node.
* @param aStorageNode <VirtualDiskImage> node.
*/
{
AutoReadLock alock (this);
CHECK_READY();
/* filePath (required) */
/* save basic settings and children */
}
/**
* Checks if the given change of \a aOldPath to \a aNewPath affects the path
* of this hard disk and updates it if necessary to reflect the new location.
* Intended to be from HardDisk::updatePaths().
*
* @param aOldPath old path (full)
* @param aNewPath new path (full)
*
* @note Locks this object for writing.
*/
{
AutoWriteLock alock (this);
AssertReturnVoid (isReady());
{
LogFlowThisFunc (("-> updated: full={%s} short={%s}\n",
}
}
/**
* Returns the string representation of this hard disk.
* When \a aShort is false, returns the full image file path.
* Otherwise, returns the image file name only.
*
* @param aShort if true, a short representation is returned
*
* @note Locks this object for reading.
*/
{
AutoReadLock alock (this);
if (!aShort)
return mFilePathFull;
else
{
}
}
/**
* Creates a clone of this hard disk by storing hard disk data in the given
* VDI file.
*
* If the operation fails, @a aDeleteTarget will be set to @c true unless the
* failure happened because the target file already existed.
*
* @param aId UUID to assign to the created image.
* @param aTargetPath VDI file where the cloned image is to be to stored.
* @param aProgress progress object to run during operation.
* @param aDeleteTarget Whether it is recommended to delete target on
* failure or not.
*/
{
ComAssertMsgFailed (("Not implemented"));
return E_NOTIMPL;
/// @todo (r=dmik) will need this if we add support for differencing VMDKs
// Use code from HVirtualDiskImage::cloneToImage as an example.
}
/**
* Creates a new differencing image for this hard disk with the given
* VDI file name.
*
* @param aId UUID to assign to the created image
* @param aTargetPath VDI file where to store the created differencing image
* @param aProgress progress object to run during operation
* (can be NULL)
*/
{
ComAssertMsgFailed (("Not implemented"));
return E_NOTIMPL;
/// @todo (r=dmik) will need this if we add support for differencing VMDKs
// Use code from HVirtualDiskImage::createDiffImage as an example.
}
// private methods
/////////////////////////////////////////////////////////////////////////////
/**
* Helper to set a new file path.
* Resolves a path relatively to the Virtual Box home directory.
*
* @note
* Must be called from under the object's lock!
*/
{
{
/* get the full file name */
char filePathFull [RTPATH_MAX];
filePathFull, sizeof (filePathFull));
if (VBOX_FAILURE (vrc))
}
else
{
}
return S_OK;
}
/**
* Helper to query information about the VDI hard disk.
*
* @param aAccessError not used when NULL, otherwise see #getAccessible()
*
* @note Must be called from under the object's lock, only after
* CHECK_BUSY_AND_READERS() succeeds.
*/
{
/* create a lock object to completely release it later */
AutoWriteLock alock (this);
int vrc = VINF_SUCCESS;
/* lazily create a semaphore */
/* Reference the disk to prevent any concurrent modifications
* after releasing the lock below (to unblock getters before
* a lengthy operation). */
addReader();
/* VBoxVHDD management interface needs to be optimized: we're opening a
* file three times in a raw to get three bits of information. */
/* reset any previous error report from VDError() */
do
{
/// @todo changed from VD_OPEN_FLAGS_READONLY to VD_OPEN_FLAGS_NORMAL,
/// because otherwise registering a VMDK which so far has no UUID will
/// yield a null UUID. It cannot be added to a VMDK opened readonly,
/// obviously. This of course changes locking behavior, but for now
/// this is acceptable. A better solution needs to be found later.
if (VBOX_FAILURE (vrc))
break;
if (VBOX_FAILURE (vrc))
break;
if (VBOX_FAILURE (vrc))
break;
{
/* check that the actual UUID of the image matches the stored UUID */
{
errMsg = Utf8StrFmt (
tr ("Actual UUID {%Vuuid} of the hard disk image '%s' doesn't "
"match UUID {%Vuuid} stored in the registry"),
break;
}
}
else
{
/* assgn an UUID read from the image file */
}
if (mParent)
{
/* check parent UUID */
{
errMsg = Utf8StrFmt (
tr ("UUID {%Vuuid} of the parent image '%ls' stored in "
"the hard disk image file '%s' doesn't match "
"UUID {%Vuuid} stored in the registry"),
break;
}
}
{
errMsg = Utf8StrFmt (
tr ("Hard disk image '%s' is a differencing image that is linked "
"to a hard disk with UUID {%Vuuid} and cannot be used "
"directly as a base hard disk"),
break;
}
/* get actual file size */
/// @todo is there a direct method in RT?
{
if (VBOX_SUCCESS (vrc))
{
if (VBOX_SUCCESS (vrc))
mActualSize = size;
RTFileClose (file);
}
if (VBOX_FAILURE (vrc))
break;
}
/* query logical size only for non-differencing images */
if (!mParent)
{
/* convert to MBytes */
}
}
while (0);
/* enter the lock again */
/* remove the reference */
{
LogWarningFunc (("'%ls' is not accessible "
"(rc=%08X, vrc=%Vrc, errMsg='%ls', mLastVDError='%s')\n",
if (aAccessError)
{
*aAccessError = errMsg;
else if (!mLastVDError.isNull())
else if (VBOX_FAILURE (vrc))
*aAccessError = Utf8StrFmt (
tr ("Could not access hard disk image '%ls' (%Vrc)"),
}
/* downgrade to not accessible */
}
else
{
if (aAccessError)
aAccessError->setNull();
mState = Accessible;
}
/* inform waiters if there are any */
if (mStateCheckWaiters > 0)
{
}
else
{
/* delete the semaphore ourselves */
}
/* cleanup the last error report from VDError() */
return rc;
}
/**
* Helper to create hard disk images.
*
* @param aSize size in MB
* @param aDynamic dynamic or fixed image
* @param aProgress address of IProgress pointer to return
*/
{
ComAssertMsgFailed (("Not implemented"));
return E_NOTIMPL;
/// @todo (r=dmik) later
// Use code from HVirtualDiskImage::createImage as an example.
}
/* static */
{
AssertMsgFailed (("Not implemented"));
return VERR_GENERAL_FAILURE;
/// @todo (r=dmik) later
// Use code from HVirtualDiskImage::VDITaskThread as an example.
}
/* static */
{
/// @todo pass the error message to the operation initiator
if (VBOX_FAILURE (rc))
else
}
////////////////////////////////////////////////////////////////////////////////
// HCustomHardDisk class
////////////////////////////////////////////////////////////////////////////////
// constructor / destructor
////////////////////////////////////////////////////////////////////////////////
{
return rc;
mState = NotCreated;
mStateCheckWaiters = 0;
mSize = 0;
mActualSize = 0;
mContainer = NULL;
/* Create supported error interface. */
&mInterfaceErrorCallbacks, this, &mVDInterfaces);
return S_OK;
}
void HCustomHardDisk::FinalRelease()
{
if (mContainer != NULL)
HardDisk::FinalRelease();
}
// public initializer/uninitializer for internal purposes only
////////////////////////////////////////////////////////////////////////////////
// public methods for internal purposes only
/////////////////////////////////////////////////////////////////////////////
/**
* Initializes the custom hard disk object by reading its properties from
* the given configuration node. The created hard disk will be marked as
* registered on success.
*
* @param aHDNode <HardDisk> node.
* @param aCustomNode <VirtualDiskImage> node.
*/
{
using namespace settings;
LogFlowThisFunc (("\n"));
AutoWriteLock alock (this);
int vrc = VINF_SUCCESS;
do
{
/* set ready to let protectedUninit() be called on failure */
setReady (true);
/* location (required) */
/* format (required) */
/* initialize the container */
if (VBOX_FAILURE (vrc))
{
if (mLastVDError.isEmpty())
tr ("Unknown format '%ls' of the custom "
"hard disk '%ls' (%Vrc)"),
else
break;
}
/* load basic settings and children */
if (mType != HardDiskType_Writethrough)
{
tr ("Currently, non-Writethrough custom hard disks "
"are not allowed ('%ls')"),
break;
}
mRegistered = TRUE;
/* Don't call queryInformation() for registered hard disks to
* prevent the calling thread (i.e. the VirtualBox server startup
* thread) from an unexpected freeze. The vital mId property (UUID)
* is read from the registry file in loadSettings(). To get the rest,
* the user will have to call COMGETTER(Accessible) manually. */
}
while (0);
uninit();
return rc;
}
/**
* Initializes the custom hard disk object using the given image file name.
*
* @param aVirtualBox VirtualBox parent.
* @param aParent Currently, must always be @c NULL.
* @param aLocation Location of the virtual disk, or @c NULL to create an
* image-less object.
* @param aRegistered Whether to mark this disk as registered or not
* (ignored when @a aLocation is @c NULL, assuming @c FALSE)
*/
{
AutoWriteLock alock (this);
do
{
/* set ready to let protectedUninit() be called on failure */
setReady (true);
/* currently, all custom hard disks are writethrough */
{
if (VBOX_FAILURE(vrc))
{
tr ("Cannot recognize the format of the custom "
"hard disk '%ls' (%Vrc)"),
break;
}
/* initialize the container */
/* the format has been already checked for presence at this point */
/* Call queryInformation() anyway (even if it will block), because
* it is the only way to get the UUID of the existing VDI and
* initialize the vital mId property. */
{
/* We are constructing a new HVirtualDiskImage object. If there
* is a fatal accessibility error (we cannot read image UUID),
* we have to fail. We do so even on non-fatal errors as well,
* because it's not worth to keep going with the inaccessible
* image from the very beginning (when nothing else depends on
* it yet). */
}
}
else
{
mRegistered = FALSE;
mState = NotCreated;
}
}
while (0);
uninit();
return rc;
}
/**
* 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 HCustomHardDisk::uninit()
{
LogFlowThisFunc (("\n"));
AutoWriteLock alock (this);
if (!isReady())
return;
}
// IHardDisk properties
////////////////////////////////////////////////////////////////////////////////
{
if (!aDescription)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
AutoWriteLock alock (this);
CHECK_READY();
return E_NOTIMPL;
}
{
if (!aSize)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
if (!aActualSize)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
// ICustomHardDisk properties
////////////////////////////////////////////////////////////////////////////////
{
if (!aLocation)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
AutoWriteLock alock (this);
CHECK_READY();
if (mState != NotCreated)
return setError (E_ACCESSDENIED,
tr ("Cannot change the file path of the existing hard disk '%ls'"),
/// @todo currently, we assume that location is always a file path for
/// all custom hard disks. This is not generally correct, and needs to be
/// parametrized in the VD plugin interface.
/* append the default path if only a name is given */
{
if (!RTPathHavePath (fp))
{
}
}
return setLocation (path);
}
{
if (!aCreated)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
if (!aFormat)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
// ICustomHardDisk methods
/////////////////////////////////////////////////////////////////////////////
{
if (!aProgress)
return E_POINTER;
AutoWriteLock alock (this);
CHECK_READY();
}
{
if (!aProgress)
return E_POINTER;
AutoWriteLock alock (this);
CHECK_READY();
}
{
AutoWriteLock alock (this);
CHECK_READY();
return E_NOTIMPL;
/// @todo later
}
/////////////////////////////////////////////////////////////////////////////
/**
* Attempts to mark the hard disk as registered.
* Only VirtualBox can call this method.
*/
{
AutoWriteLock alock (this);
CHECK_READY();
if (aRegistered)
{
if (mState == NotCreated)
tr ("Storage location '%ls' is not yet created for this hard disk"),
mLocationFull.raw());
}
else
{
}
}
/**
* Checks accessibility of this hard disk image only (w/o parents).
*
* @param aAccessError on output, a null string indicates the hard disk is
* accessible, otherwise contains a message describing
* the reason of inaccessibility.
*
* @note Locks this object for writing.
*/
{
/* queryInformation() needs a write lock */
AutoWriteLock alock (this);
CHECK_READY();
if (mStateCheckSem != NIL_RTSEMEVENTMULTI)
{
/* An accessibility check in progress on some other thread,
* wait for it to finish. */
if (mStateCheckWaiters == 0)
{
}
/* don't touch aAccessError, it has been already set */
return S_OK;
}
/* check the basic accessibility */
return rc;
{
return queryInformation (&aAccessError);
}
mLocationFull.raw());
return S_OK;
}
/**
* Saves hard disk settings to the specified storage node and saves
* all children to the specified hard disk node
*
* @param aHDNode <HardDisk> or <DiffHardDisk> node.
* @param aStorageNode <VirtualDiskImage> node.
*/
{
AutoReadLock alock (this);
CHECK_READY();
/* location (required) */
/* format (required) */
/* save basic settings and children */
}
/**
* Returns the string representation of this hard disk.
* When \a aShort is false, returns the full image file path.
* Otherwise, returns the image file name only.
*
* @param aShort if true, a short representation is returned
*
* @note Locks this object for reading.
*/
{
AutoReadLock alock (this);
/// @todo currently, we assume that location is always a file path for
/// all custom hard disks. This is not generally correct, and needs to be
/// parametrized in the VD plugin interface.
if (!aShort)
return mLocationFull;
else
{
}
}
/**
* Creates a clone of this hard disk by storing hard disk data in the given
* VDI file.
*
* If the operation fails, @a aDeleteTarget will be set to @c true unless the
* failure happened because the target file already existed.
*
* @param aId UUID to assign to the created image.
* @param aTargetPath VDI file where the cloned image is to be to stored.
* @param aProgress progress object to run during operation.
* @param aDeleteTarget Whether it is recommended to delete target on
* failure or not.
*/
{
ComAssertMsgFailed (("Not implemented"));
return E_NOTIMPL;
}
/**
* Creates a new differencing image for this hard disk with the given
* VDI file name.
*
* @param aId UUID to assign to the created image
* @param aTargetPath VDI file where to store the created differencing image
* @param aProgress progress object to run during operation
* (can be NULL)
*/
{
ComAssertMsgFailed (("Not implemented"));
return E_NOTIMPL;
}
// private methods
/////////////////////////////////////////////////////////////////////////////
/**
* Helper to set a new location.
*
* @note
* Must be called from under the object's lock!
*/
{
/// @todo currently, we assume that location is always a file path for
/// all custom hard disks. This is not generally correct, and needs to be
/// parametrized in the VD plugin interface.
{
/* get the full file name */
char locationFull [RTPATH_MAX];
locationFull, sizeof (locationFull));
if (VBOX_FAILURE (vrc))
}
else
{
}
return S_OK;
}
/**
* Helper to query information about the custom hard disk.
*
* @param aAccessError not used when NULL, otherwise see #getAccessible()
*
* @note Must be called from under the object's lock, only after
* CHECK_BUSY_AND_READERS() succeeds.
*/
{
/* create a lock object to completely release it later */
AutoWriteLock alock (this);
int vrc = VINF_SUCCESS;
/* lazily create a semaphore */
/* Reference the disk to prevent any concurrent modifications
* after releasing the lock below (to unblock getters before
* a lengthy operation). */
addReader();
/* VBoxVHDD management interface needs to be optimized: we're opening a
* file three times in a raw to get three bits of information. */
/* reset any previous error report from VDError() */
do
{
if (VBOX_FAILURE (vrc))
break;
break;
{
/* check that the actual UUID of the image matches the stored UUID */
{
errMsg = Utf8StrFmt (
tr ("Actual UUID {%Vuuid} of the hard disk image '%s' doesn't "
"match UUID {%Vuuid} stored in the registry"),
break;
}
}
else
{
/* assign an UUID read from the image file */
if (VBOX_SUCCESS(vrc))
else
{
/* Create a UUID on our own. */
if (VBOX_FAILURE(vrc))
break;
}
}
if (VBOX_FAILURE (vrc))
break;
if (mParent)
{
/* check parent UUID */
{
errMsg = Utf8StrFmt (
tr ("UUID {%Vuuid} of the parent image '%ls' stored in "
"the hard disk image file '%s' doesn't match "
"UUID {%Vuuid} stored in the registry"),
break;
}
}
{
errMsg = Utf8StrFmt (
tr ("Hard disk image '%s' is a differencing image that is linked "
"to a hard disk with UUID {%Vuuid} and cannot be used "
"directly as a base hard disk"),
break;
}
/* get actual file size */
/// @todo is there a direct method in RT?
{
if (VBOX_SUCCESS (vrc))
{
if (VBOX_SUCCESS (vrc))
mActualSize = size;
RTFileClose (file);
}
if (VBOX_FAILURE (vrc))
break;
}
/* query logical size only for non-differencing images */
if (!mParent)
{
/* convert to MBytes */
}
}
while (0);
/* enter the lock again */
/* remove the reference */
{
LogWarningFunc (("'%ls' is not accessible "
"(rc=%08X, vrc=%Vrc, errMsg='%ls')\n",
if (aAccessError)
{
*aAccessError = errMsg;
else if (!mLastVDError.isNull())
else if (VBOX_FAILURE (vrc))
*aAccessError = Utf8StrFmt (
tr ("Could not access hard disk '%ls' (%Vrc)"),
}
/* downgrade to not accessible */
}
else
{
if (aAccessError)
aAccessError->setNull();
mState = Accessible;
}
/* inform waiters if there are any */
if (mStateCheckWaiters > 0)
{
}
else
{
/* delete the semaphore ourselves */
}
/* cleanup the last error report from VDError() */
return rc;
}
/**
* Helper to create hard disk images.
*
* @param aSize size in MB
* @param aDynamic dynamic or fixed image
* @param aProgress address of IProgress pointer to return
*/
{
ComAssertMsgFailed (("Not implemented"));
return E_NOTIMPL;
}
/* static */
{
AssertMsgFailed (("Not implemented"));
return VERR_GENERAL_FAILURE;
}
/* static */
{
/// @todo pass the error message to the operation initiator
if (VBOX_FAILURE (rc))
else
}
////////////////////////////////////////////////////////////////////////////////
// HVHDImage class
////////////////////////////////////////////////////////////////////////////////
// constructor / destructor
////////////////////////////////////////////////////////////////////////////////
{
return rc;
mState = NotCreated;
mStateCheckWaiters = 0;
mSize = 0;
mActualSize = 0;
/* Create supported error interface. */
&mInterfaceErrorCallbacks, this, &mVDInterfaces);
/* initialize the container */
return S_OK;
}
void HVHDImage::FinalRelease()
{
if (mContainer != NULL)
HardDisk::FinalRelease();
}
// public initializer/uninitializer for internal purposes only
////////////////////////////////////////////////////////////////////////////////
// public methods for internal purposes only
/////////////////////////////////////////////////////////////////////////////
/**
* Initializes the VHD hard disk object by reading its properties from
* the given configuration node. The created hard disk will be marked as
* registered on success.
*
* @param aHDNode <HardDisk> node
* @param aVHDNode <VirtualDiskImage> node
*/
{
LogFlowThisFunc (("\n"));
AutoWriteLock alock (this);
do
{
/* set ready to let protectedUninit() be called on failure */
setReady (true);
/* filePath (required) */
/* load basic settings and children */
if (mType != HardDiskType_Writethrough)
{
tr ("Currently, non-Writethrough VHD images are not "
"allowed ('%ls')"),
break;
}
mRegistered = TRUE;
/* Don't call queryInformation() for registered hard disks to
* prevent the calling thread (i.e. the VirtualBox server startup
* thread) from an unexpected freeze. The vital mId property (UUID)
* is read from the registry file in loadSettings(). To get the rest,
* the user will have to call COMGETTER(Accessible) manually. */
}
while (0);
uninit();
return rc;
}
/**
* Initializes the VHD hard disk object using the given image file name.
*
* @param aVirtualBox VirtualBox parent.
* @param aParent Currently, must always be @c NULL.
* @param aFilePath Path to the image file, or @c NULL to create an
* image-less object.
* @param aRegistered Whether to mark this disk as registered or not
* (ignored when @a aFilePath is @c NULL, assuming @c FALSE)
*/
{
AutoWriteLock alock (this);
do
{
/* set ready to let protectedUninit() be called on failure */
setReady (true);
/* currently, all VHD hard disks are writethrough */
{
/* Call queryInformation() anyway (even if it will block), because
* it is the only way to get the UUID of the existing VDI and
* initialize the vital mId property. */
{
/* We are constructing a new HVirtualDiskImage object. If there
* is a fatal accessibility error (we cannot read image UUID),
* we have to fail. We do so even on non-fatal errors as well,
* because it's not worth to keep going with the inaccessible
* image from the very beginning (when nothing else depends on
* it yet). */
}
}
else
{
mRegistered = FALSE;
mState = NotCreated;
}
}
while (0);
uninit();
return rc;
}
/**
* 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.
*/
{
LogFlowThisFunc (("\n"));
AutoWriteLock alock (this);
if (!isReady())
return;
}
// IHardDisk properties
////////////////////////////////////////////////////////////////////////////////
{
if (!aDescription)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
AutoWriteLock alock (this);
CHECK_READY();
return E_NOTIMPL;
/// @todo implement
//
// if (mState >= Created)
// {
// int vrc = VDISetImageComment (Utf8Str (mFilePathFull), Utf8Str (aDescription));
// if (VBOX_FAILURE (vrc))
// return setError (E_FAIL,
// tr ("Could not change the description of the VDI hard disk '%ls' "
// "(%Vrc)"),
// toString().raw(), vrc);
// }
//
// mDescription = aDescription;
// return S_OK;
}
{
if (!aSize)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
/// @todo will need this if we add suppord for differencing VMDKs
//
// /* only a non-differencing image knows the logical size */
// if (isDifferencing())
// return root()->COMGETTER(Size) (aSize);
return S_OK;
}
{
if (!aActualSize)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
// IVirtualDiskImage properties
////////////////////////////////////////////////////////////////////////////////
{
if (!aFilePath)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
{
AutoWriteLock alock (this);
CHECK_READY();
if (mState != NotCreated)
return setError (E_ACCESSDENIED,
tr ("Cannot change the file path of the existing hard disk '%ls'"),
/* append the default path if only a name is given */
{
if (!RTPathHavePath (fp))
{
}
}
return setFilePath (path);
}
{
if (!aCreated)
return E_POINTER;
AutoReadLock alock (this);
CHECK_READY();
return S_OK;
}
// IVHDImage methods
/////////////////////////////////////////////////////////////////////////////
{
if (!aProgress)
return E_POINTER;
AutoWriteLock alock (this);
CHECK_READY();
}
{
if (!aProgress)
return E_POINTER;
AutoWriteLock alock (this);
CHECK_READY();
}
{
AutoWriteLock alock (this);
CHECK_READY();
return E_NOTIMPL;
/// @todo later
// We will need to parse the file in order to delete all related delta and
// sparse images etc. We may also want to obey the .vmdk.lck file
// which is (as far as I understood) created when the VMware VM is
// running or saved etc.
//
// if (mRegistered)
// return setError (E_ACCESSDENIED,
// tr ("Cannot delete an image of the registered hard disk image '%ls"),
// mFilePathFull.raw());
// if (mState == NotCreated)
// return setError (E_FAIL,
// tr ("Hard disk image has been already deleted or never created"));
//
// int vrc = RTFileDelete (Utf8Str (mFilePathFull));
// if (VBOX_FAILURE (vrc))
// return setError (E_FAIL, tr ("Could not delete the image file '%ls' (%Vrc)"),
// mFilePathFull.raw(), vrc);
//
// mState = NotCreated;
//
// /* reset the fields */
// mSize = 0;
// mActualSize = 0;
//
// return S_OK;
}
/////////////////////////////////////////////////////////////////////////////
/**
* Attempts to mark the hard disk as registered.
* Only VirtualBox can call this method.
*/
{
AutoWriteLock alock (this);
CHECK_READY();
if (aRegistered)
{
if (mState == NotCreated)
tr ("Image file '%ls' is not yet created for this hard disk"),
mFilePathFull.raw());
/// @todo will need this if we add suppord for differencing VHDs
// if (isDifferencing())
// return setError (E_FAIL,
// tr ("Hard disk '%ls' is differencing and cannot be unregistered "
// "explicitly"),
// mFilePathFull.raw());
}
else
{
}
}
/**
* Checks accessibility of this hard disk image only (w/o parents).
*
* @param aAccessError on output, a null string indicates the hard disk is
* accessible, otherwise contains a message describing
* the reason of inaccessibility.
*
* @note Locks this object for writing.
*/
{
/* queryInformation() needs a write lock */
AutoWriteLock alock (this);
CHECK_READY();
if (mStateCheckSem != NIL_RTSEMEVENTMULTI)
{
/* An accessibility check in progress on some other thread,
* wait for it to finish. */
if (mStateCheckWaiters == 0)
{
}
/* don't touch aAccessError, it has been already set */
return S_OK;
}
/* check the basic accessibility */
return rc;
{
return queryInformation (&aAccessError);
}
mFilePathFull.raw());
return S_OK;
}
/**
* Saves hard disk settings to the specified storage node and saves
* all children to the specified hard disk node
*
* @param aHDNode <HardDisk> or <DiffHardDisk> node
* @param aStorageNode <VirtualDiskImage> node
*/
{
AutoReadLock alock (this);
CHECK_READY();
/* filePath (required) */
/* save basic settings and children */
}
/**
* Checks if the given change of \a aOldPath to \a aNewPath affects the path
* of this hard disk and updates it if necessary to reflect the new location.
* Intended to be from HardDisk::updatePaths().
*
* @param aOldPath old path (full)
* @param aNewPath new path (full)
*
* @note Locks this object for writing.
*/
{
AutoWriteLock alock (this);
AssertReturnVoid (isReady());
{
LogFlowThisFunc (("-> updated: full={%s} short={%s}\n",
}
}
/**
* Returns the string representation of this hard disk.
* When \a aShort is false, returns the full image file path.
* Otherwise, returns the image file name only.
*
* @param aShort if true, a short representation is returned
*
* @note Locks this object for reading.
*/
{
AutoReadLock alock (this);
if (!aShort)
return mFilePathFull;
else
{
}
}
/**
* Creates a clone of this hard disk by storing hard disk data in the given
* VDI file.
*
* If the operation fails, @a aDeleteTarget will be set to @c true unless the
* failure happened because the target file already existed.
*
* @param aId UUID to assign to the created image.
* @param aTargetPath VDI file where the cloned image is to be to stored.
* @param aProgress progress object to run during operation.
* @param aDeleteTarget Whether it is recommended to delete target on
* failure or not.
*/
{
ComAssertMsgFailed (("Not implemented"));
return E_NOTIMPL;
/// @todo will need this if we add suppord for differencing VHDs
// Use code from HVirtualDiskImage::cloneToImage as an example.
}
/**
* Creates a new differencing image for this hard disk with the given
* VDI file name.
*
* @param aId UUID to assign to the created image
* @param aTargetPath VDI file where to store the created differencing image
* @param aProgress progress object to run during operation
* (can be NULL)
*/
{
ComAssertMsgFailed (("Not implemented"));
return E_NOTIMPL;
/// @todo will need this if we add suppord for differencing VHDs
// Use code from HVirtualDiskImage::createDiffImage as an example.
}
// private methods
/////////////////////////////////////////////////////////////////////////////
/**
* Helper to set a new file path.
* Resolves a path relatively to the Virtual Box home directory.
*
* @note
* Must be called from under the object's lock!
*/
{
{
/* get the full file name */
char filePathFull [RTPATH_MAX];
filePathFull, sizeof (filePathFull));
if (VBOX_FAILURE (vrc))
}
else
{
}
return S_OK;
}
/**
* Helper to query information about the VDI hard disk.
*
* @param aAccessError not used when NULL, otherwise see #getAccessible()
*
* @note Must be called from under the object's lock, only after
* CHECK_BUSY_AND_READERS() succeeds.
*/
{
/* create a lock object to completely release it later */
AutoWriteLock alock (this);
int vrc = VINF_SUCCESS;
/* lazily create a semaphore */
/* Reference the disk to prevent any concurrent modifications
* after releasing the lock below (to unblock getters before
* a lengthy operation). */
addReader();
/* VBoxVHDD management interface needs to be optimized: we're opening a
* file three times in a raw to get three bits of information. */
/* reset any previous error report from VDError() */
do
{
/// @todo changed from VD_OPEN_FLAGS_READONLY to VD_OPEN_FLAGS_NORMAL,
/// because otherwise registering a VHD which so far has no UUID will
/// yield a null UUID. It cannot be added to a VHD opened readonly,
/// obviously. This of course changes locking behavior, but for now
/// this is acceptable. A better solution needs to be found later.
if (VBOX_FAILURE (vrc))
break;
if (VBOX_FAILURE (vrc))
break;
if (VBOX_FAILURE (vrc))
break;
{
/* check that the actual UUID of the image matches the stored UUID */
{
errMsg = Utf8StrFmt (
tr ("Actual UUID {%Vuuid} of the hard disk image '%s' doesn't "
"match UUID {%Vuuid} stored in the registry"),
break;
}
}
else
{
/* assgn an UUID read from the image file */
}
if (mParent)
{
/* check parent UUID */
{
errMsg = Utf8StrFmt (
tr ("UUID {%Vuuid} of the parent image '%ls' stored in "
"the hard disk image file '%s' doesn't match "
"UUID {%Vuuid} stored in the registry"),
break;
}
}
{
errMsg = Utf8StrFmt (
tr ("Hard disk image '%s' is a differencing image that is linked "
"to a hard disk with UUID {%Vuuid} and cannot be used "
"directly as a base hard disk"),
break;
}
/* get actual file size */
/// @todo is there a direct method in RT?
{
if (VBOX_SUCCESS (vrc))
{
if (VBOX_SUCCESS (vrc))
mActualSize = size;
RTFileClose (file);
}
if (VBOX_FAILURE (vrc))
break;
}
/* query logical size only for non-differencing images */
if (!mParent)
{
/* convert to MBytes */
}
}
while (0);
/* enter the lock again */
/* remove the reference */
{
LogWarningFunc (("'%ls' is not accessible "
"(rc=%08X, vrc=%Vrc, errMsg='%ls')\n",
if (aAccessError)
{
*aAccessError = errMsg;
else if (!mLastVDError.isNull())
else if (VBOX_FAILURE (vrc))
*aAccessError = Utf8StrFmt (
tr ("Could not access hard disk image '%ls' (%Vrc)"),
}
/* downgrade to not accessible */
}
else
{
if (aAccessError)
aAccessError->setNull();
mState = Accessible;
}
/* inform waiters if there are any */
if (mStateCheckWaiters > 0)
{
}
else
{
/* delete the semaphore ourselves */
}
/* cleanup the last error report from VDError() */
return rc;
}
/**
* Helper to create hard disk images.
*
* @param aSize size in MB
* @param aDynamic dynamic or fixed image
* @param aProgress address of IProgress pointer to return
*/
{
ComAssertMsgFailed (("Not implemented"));
return E_NOTIMPL;
/// @todo later
// Use code from HVirtualDiskImage::createImage as an example.
}
/* static */
{
AssertMsgFailed (("Not implemented"));
return VERR_GENERAL_FAILURE;
/// @todo later
// Use code from HVirtualDiskImage::VDITaskThread as an example.
}
/* static */
{
/// @todo pass the error message to the operation initiator
if (VBOX_FAILURE (rc))
else
}