VCICache.cpp revision 681fd85cc7cd49e9cf66a917d6ae9ff36eb7d9e9
/* $Id$ */
/** @file
* VCICacheCore - VirtualBox Cache Image, Core Code.
*/
/*
* Copyright (C) 2006-2010 Oracle Corporation
*
* 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.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <VBox/vd-cache-plugin.h>
/*******************************************************************************
* On disk data structures *
*******************************************************************************/
/** @note All structures which are written to the disk are written in camel case
* and packed. */
/** Block size used internally, because we cache sectors the smallest unit we
* have to care about is 512 bytes. */
#define VCI_BLOCK_SIZE 512
#define VCI_BYTE2BLOCK(u) ((u) >> 9)
/**
* The VCI header - at the beginning of the file.
*
* All entries a stored in little endian order.
*/
#pragma pack(1)
typedef struct VciHdr
{
/** The signature to identify a cache image. */
/** Version of the layout of metadata in the cache. */
/** Maximum size of the cache file in blocks.
* This includes all metadata. */
/** Flag indicating whether the cache was closed cleanly. */
/** Cache type. */
/** Offset of the B+-Tree root in the image in blocks. */
/** Offset of the block allocation bitmap in blocks. */
/** Size of the block allocation bitmap in blocks. */
/** UUID of the image. */
/** Modification UUID for the cache. */
/** Reserved for future use. */
#pragma pack()
/** VCI signature to identify a valid image. */
/** Current version we support. */
/** Value for an unclean cache shutdown. */
/** Value for a clean cache shutdown. */
/** Cache type: Dynamic image growing to the maximum value. */
/** Cache type: Fixed image, space is preallocated. */
/**
* On disk representation of an extent describing a range of cached data.
*
* All entries a stored in little endian order.
*/
#pragma pack(1)
typedef struct VciCacheExtent
{
/** Block address of the previous extent in the LRU list. */
/** Block address of the next extent in the LRU list. */
/** Flags (for compression, encryption etc.) - currently unused and should be always 0. */
/** Reserved */
/** First block of cached data the extent represents. */
/** Number of blocks the extent represents. */
/** First block in the image where the data is stored. */
#pragma pack()
/**
* On disk representation of an internal node.
*
* All entries a stored in little endian order.
*/
#pragma pack(1)
typedef struct VciTreeNodeInternal
{
/** First block of cached data the internal node represents. */
/** Number of blocks the internal node represents. */
/** Block address in the image where the next node in the tree is stored. */
#pragma pack()
/**
* On-disk representation of a node in the B+-Tree.
*
* All entries a stored in little endian order.
*/
#pragma pack(1)
typedef struct VciTreeNode
{
/** Type of the node (root, internal, leaf). */
/** Data in the node. */
} VciTreeNode, *PVciTreeNode;
#pragma pack()
/** Node type: Internal node containing links to other nodes (VciTreeNodeInternal). */
/** Node type: Leaf of the tree (VciCacheExtent). */
/** Number of cache extents described by one node. */
/** Number of internal nodes managed by one tree node. */
/**
* VCI block bitmap header.
*
* All entries a stored in little endian order.
*/
#pragma pack(1)
typedef struct VciBlkMap
{
/** Magic of the block bitmap. */
/** Version of the block bitmap. */
/** Number of blocks this block map manages. */
/** Number of free blocks. */
/** Number of blocks allocated for metadata. */
/** Number of blocks allocated for actual cached data. */
/** Reserved for future use. */
} VciBlkMap, *PVciBlkMap;
#pragma pack()
/** The magic which identifies a block map. */
/** Current version. */
/** Block bitmap entry */
typedef uint8_t VciBlkMapEnt;
/*******************************************************************************
* Constants And Macros, Structures and Typedefs *
*******************************************************************************/
/**
* Block range descriptor.
*/
typedef struct VCIBLKRANGEDESC
{
/** Previous entry in the list. */
struct VCIBLKRANGEDESC *pPrev;
/** Next entry in the list. */
struct VCIBLKRANGEDESC *pNext;
/** Start address of the range. */
/** Number of blocks in the range. */
/** Flag whether the range is free or allocated. */
bool fFree;
/**
* Block map for the cache image - in memory structure.
*/
typedef struct VCIBLKMAP
{
/** Number of blocks the map manages. */
/** Number of blocks allocated for metadata. */
/** Number of blocks allocated for actual cached data. */
/** Number of free blocks. */
/** Pointer to the head of the block range list. */
/** Pointer to the tail of the block range list. */
} VCIBLKMAP;
/** Pointer to a block map. */
typedef VCIBLKMAP *PVCIBLKMAP;
/**
* B+-Tree node header.
*/
typedef struct VCITREENODE
{
/** Type of the node (VCI_TREE_NODE_TYPE_*). */
/** Block address where the node is stored. */
/** Pointer to the parent. */
struct VCITREENODE *pParent;
} VCITREENODE, *PVCITREENODE;
/**
* B+-Tree node pointer.
*/
typedef struct VCITREENODEPTR
{
/** Flag whether the node is in memory or still on the disk. */
bool fInMemory;
/** Type dependent data. */
union
{
/** Pointer to a in memory node. */
/** Start block address of the node. */
} u;
/**
* Internal node.
*/
typedef struct VCINODEINTERNAL
{
/** First block of cached data the internal node represents. */
/** Number of blocks the internal node represents. */
/** Pointer to the child node. */
/**
* A in memory internal B+-tree node.
*/
typedef struct VCITREENODEINT
{
/** Node core. */
/** Number of used nodes. */
unsigned cUsedNodes;
/** Array of internal nodes. */
/**
* A in memory cache extent.
*/
typedef struct VCICACHEEXTENT
{
/** First block of cached data the extent represents. */
/** Number of blocks the extent represents. */
/** First block in the image where the data is stored. */
/**
* A in memory leaf B+-tree node.
*/
typedef struct VCITREENODELEAF
{
/** Node core. */
/** Number of used nodes. */
unsigned cUsedNodes;
/** The extents in the node. */
/**
* VCI image data structure.
*/
typedef struct VCICACHE
{
/** Image name. */
const char *pszFilename;
/** Storage handle. */
/** I/O interface. */
/** Async I/O interface callbacks. */
/** Pointer to the per-disk VD interface list. */
/** Pointer to the per-image VD interface list. */
/** Error callback. */
/** Opaque data for error callback. */
/** Open flags passed by VBoxHD layer. */
unsigned uOpenFlags;
/** Image flags defined during creation or determined during open. */
unsigned uImageFlags;
/** Total size of the image. */
/** Offset of the B+-Tree in the image in bytes. */
/** Pointer to the root node of the B+-Tree. */
/** Offset to the block allocation bitmap in bytes. */
/** Block map. */
/** No block free in bitmap error code. */
#define VERR_VCI_NO_BLOCKS_FREE (-65536)
/*******************************************************************************
* Static Variables *
*******************************************************************************/
/** NULL-terminated array of supported file extensions. */
static const char *const s_apszVciFileExtensions[] =
{
"vci",
};
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
/**
* Internal: signal an error to the frontend.
*/
const char *pszFormat, ...)
{
if (pCache->pInterfaceError)
return rc;
}
/**
* Internal: signal an informational message to the frontend.
*/
{
int rc = VINF_SUCCESS;
if (pCache->pInterfaceError)
return rc;
}
{
}
{
}
{
}
{
}
{
}
{
}
{
}
{
}
{
}
{
}
{
cbRead);
}
void *pvCompleteUser)
{
}
void *pvCompleteUser)
{
}
/**
* Internal. Flush image data to disk.
*/
{
int rc = VINF_SUCCESS;
return rc;
}
/**
* Internal. Free all allocated space for representing an image except pCache,
* and optionally delete the image from disk.
*/
{
int rc = VINF_SUCCESS;
/* Freeing a never allocated image (e.g. because the open failed) is
* not signalled as an error. After all nothing bad happens. */
if (pCache)
{
{
/* No point updating the file that is deleted anyway. */
if (!fDelete)
}
}
return rc;
}
/**
* Creates a new block map which can manage the given number of blocks.
*
* The size of the bitmap is aligned to the VCI block size.
*
* @returns VBox status code.
* @param cBlocks The number of blocks the bitmap can manage.
* @param ppBlkMap Where to store the pointer to the block bitmap.
* @param pcbBlkMap Where to store the size of the block bitmap in blocks
* needed on the disk.
*/
{
int rc = VINF_SUCCESS;
{
pBlkMap->cBlocksAllocMeta = 0;
pBlkMap->cBlocksAllocData = 0;
pFree->offAddrStart = 0;
}
else
{
if (pBlkMap)
if (pFree)
rc = VERR_NO_MEMORY;
}
return rc;
}
/**
* Frees a block map.
*
* @returns nothing.
* @param pBlkMap The block bitmap to destroy.
*/
{
while (pRangeCur)
{
}
LogFlowFunc(("returns\n"));
}
/**
* Loads the block map from the specified medium and creates all necessary
* in memory structures to manage used and free blocks.
*
* @returns VBox status code.
* @param pStorage Storage handle to read the block bitmap from.
* @param offBlkMap Start of the block bitmap in blocks.
* @param cBlkMap Size of the block bitmap on the disk in blocks.
* @param ppBlkMap Where to store the block bitmap on success.
*/
static int vciBlkMapLoad(PVCICACHE pStorage, uint64_t offBlkMap, uint32_t cBlkMap, PVCIBLKMAP *ppBlkMap)
{
int rc = VINF_SUCCESS;
LogFlowFunc(("pStorage=%#p offBlkMap=%llu cBlkMap=%u ppBlkMap=%#p\n",
{
if (RT_SUCCESS(rc))
{
{
if (pBlkMap)
{
/* Load the bitmap and construct the range list. */
uint32_t cBlocksFree = 0;
uint32_t cBlocksAllocated = 0;
if (pRangeCur)
{
uint32_t cBlocksRead = 0;
if (RT_SUCCESS(rc))
{
pRangeCur->offAddrStart = 0;
}
while ( RT_SUCCESS(rc)
&& cBlocksLeft)
{
int iBit = 0;
while (cBits)
{
{
/* Check for the first set bit. */
}
else
{
/* Check for the first free bit. */
}
if (iBit == -1)
{
/* No change. */
cBits = 0;
}
else
{
/* Create a new range descriptor. */
if (!pRangeNew)
{
rc = VERR_NO_MEMORY;
break;
}
}
}
offBlkMap += cBlocksRead;
if ( RT_SUCCESS(rc)
&& cBlocksLeft)
{
/* Read next chunk. */
}
}
}
else
rc = VERR_NO_MEMORY;
if (RT_SUCCESS(rc))
{
LogFlowFunc(("return success\n"));
return VINF_SUCCESS;
}
else
}
else
rc = VERR_NO_MEMORY;
}
else
}
else if (RT_SUCCESS(rc))
}
else
return rc;
}
/**
* Saves the block map in the cache image. All necessary on disk structures
* are written.
*
* @returns VBox status code.
* @param pBlkMap The block bitmap to save.
* @param pStorage Where the block bitmap should be written to.
* @param offBlkMap Start of the block bitmap in blocks.
* @param cBlkMap Size of the block bitmap on the disk in blocks.
*/
static int vciBlkMapSave(PVCIBLKMAP pBlkMap, PVCICACHE pStorage, uint64_t offBlkMap, uint32_t cBlkMap)
{
int rc = VINF_SUCCESS;
LogFlowFunc(("pBlkMap=%#p pStorage=%#p offBlkMap=%llu cBlkMap=%u\n",
/* Make sure the number of blocks allocated for us match our expectations. */
{
/* Setup the header */
if (RT_SUCCESS(rc))
{
unsigned iBit = 0;
/* Write the descriptor ranges. */
while (pCur)
{
while (cBlocks)
{
else
iBit += cBlocksMax;
cBlocks -= cBlocksMax;
{
/* Buffer is full, write to file and reset. */
if (RT_FAILURE(rc))
break;
iBit = 0;
}
}
}
}
}
else
return rc;
}
/**
* Finds the range block describing the given block address.
*
* @returns Pointer to the block range descriptor or NULL if none could be found.
* @param pBlkMap The block bitmap to search on.
* @param offBlockAddr The block address to search for.
*/
{
while ( pBlk
return pBlk;
}
/**
* Allocates the given number of blocks in the bitmap and returns the start block address.
*
* @returns VBox status code.
* @param pBlkMap The block bitmap to allocate the blocks from.
* @param cBlocks How many blocks to allocate.
* @param poffBlockAddr Where to store the start address of the allocated region.
*/
{
int rc = VINF_SUCCESS;
LogFlowFunc(("pBlkMap=%#p cBlocks=%u poffBlockAddr=%#p\n",
while (pCur)
{
{
if ( !pBestFit
{
/* Stop searching if the size is matching exactly. */
break;
}
}
}
if (pBestFit)
{
{
/* Create a new free block. */
if (pFree)
{
/* Link into the list. */
}
else
{
rc = VERR_NO_MEMORY;
}
}
}
else
return rc;
}
/**
* Try to extend the space of an already allocated block.
*
* @returns VBox status code.
* @param pBlkMap The block bitmap to allocate the blocks from.
* @param cBlocksNew How many blocks the extended block should have.
* @param offBlockAddrOld The start address of the block to reallocate.
* @param poffBlockAddr Where to store the start address of the allocated region.
*/
{
int rc = VINF_SUCCESS;
LogFlowFunc(("pBlkMap=%#p cBlocksNew=%u offBlockAddrOld=%llu poffBlockAddr=%#p\n",
AssertMsgFailed(("Implement\n"));
return rc;
}
/**
* Frees a range of blocks.
*
* @returns nothing.
* @param pBlkMap The block bitmap.
* @param offBlockAddr Address of the first block to free.
* @param cBlocks How many blocks to free.
*/
{
LogFlowFunc(("pBlkMap=%#p offBlockAddr=%llu cBlocks=%u\n",
while (cBlocks)
{
/* Easy case, the whole block is freed. */
{
/* Check if it is possible to merge free blocks. */
{
else
}
/* Now the one to the right. */
{
else
}
}
else
{
/* The block is intersecting. */
AssertMsgFailed(("TODO\n"));
}
}
LogFlowFunc(("returns\n"));
}
/**
* Converts a tree node from the image to the in memory structure.
*
* @returns Pointer to the in memory tree node.
* @param offBlockAddrNode Block address of the node.
* @param pNodeImage Pointer to the image representation of the node.
*/
{
{
if (pLeaf)
{
{
pExtent++;
pLeaf->cUsedNodes++;
}
}
}
{
if (pInt)
{
{
pIntImage++;
pInt->cUsedNodes++;
}
}
}
else
if (pNode)
return pNode;
}
/**
* Looks up the cache extent for the given virtual block address.
*
* @returns Pointer to the cache extent or NULL if none could be found.
* @param pCache The cache image instance.
* @param offBlockOffset The block offset to search for.
*/
{
int rc = VINF_SUCCESS;
while ( RT_SUCCESS(rc)
&& pNodeCur
{
/* Search for the correct internal node. */
unsigned idxMin = 0;
{
/* Determine the search direction. */
{
/* Search left from the current extent. */
}
{
/* Search right from the current extent. */
}
else
{
/* The block lies in the node, stop searching. */
else
{
/* Read from disk and add to the tree. */
if (pNodeNew)
{
/* Link to the parent. */
}
else
rc = VERR_NO_MEMORY;
}
break;
}
}
}
if ( RT_SUCCESS(rc)
&& pNodeCur)
{
/* Search the range. */
unsigned idxMin = 0;
{
/* Determine the search direction. */
{
/* Search left from the current extent. */
}
{
/* Search right from the current extent. */
}
else
{
/* We found the extent, stop searching. */
break;
}
}
}
return pExtent;
}
/**
* Internal: Open an image, constructing all necessary data structures.
*/
{
int rc;
if (pCache->pInterfaceError)
/* Get I/O interface. */
/*
* Open the image.
*/
false /* fCreate */));
if (RT_FAILURE(rc))
{
/* Do NOT signal an appropriate error here, as the VD layer has the
* choice of retrying the open if it failed. */
goto out;
}
{
goto out;
}
if (RT_FAILURE(rc))
{
goto out;
}
{
/* Load the block map. */
if (RT_SUCCESS(rc))
{
/* Load the first tree node. */
if (RT_SUCCESS(rc))
{
rc = VERR_NO_MEMORY;
}
}
}
else
out:
if (RT_FAILURE(rc))
vciFreeImage(pCache, false);
return rc;
}
/**
* Internal: Create a vci image.
*/
unsigned uImageFlags, const char *pszComment,
void *pvUser, unsigned uPercentStart,
unsigned uPercentSpan)
{
int rc;
if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
{
rc = vciError(pCache, VERR_VD_RAW_INVALID_TYPE, RT_SRC_POS, N_("VCI: cannot create diff image '%s'"), pCache->pszFilename);
return rc;
}
if (pCache->pInterfaceError)
do
{
/* Create image file. */
true /* fCreate */));
if (RT_FAILURE(rc))
{
break;
}
/* Allocate block bitmap. */
if (RT_FAILURE(rc))
{
rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot create block bitmap '%s'"), pCache->pszFilename);
break;
}
/*
* Allocate space for the header in the block bitmap.
* Because the block map is empty the header has to start at block 0
*/
if (RT_FAILURE(rc))
{
rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot allocate space for header in block bitmap '%s'"), pCache->pszFilename);
break;
}
/*
* Allocate space for the block map itself.
*/
if (RT_FAILURE(rc))
{
rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot allocate space for block map in block map '%s'"), pCache->pszFilename);
break;
}
/*
* Allocate space for the tree root node.
*/
uint64_t offTreeRoot = 0;
if (RT_FAILURE(rc))
{
rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot allocate space for block map in block map '%s'"), pCache->pszFilename);
break;
}
/*
* Allocate the in memory root node.
*/
{
rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot allocate B+-Tree root pointer '%s'"), pCache->pszFilename);
break;
}
/* Rest remains 0 as the tree is still empty. */
/*
* Now that we are here we have all the basic structures and know where to place them in the image.
* It's time to write it now.
*/
/* Setup the header. */
if (RT_FAILURE(rc))
{
break;
}
if (RT_FAILURE(rc))
{
break;
}
/* Setup the root tree. */
if (RT_FAILURE(rc))
{
break;
}
if (RT_FAILURE(rc))
{
break;
}
} while (0);
if (RT_FAILURE(rc))
return rc;
}
/** @copydoc VDCACHEBACKEND::pfnProbe */
{
int rc = VINF_SUCCESS;
/* Get I/O interface. */
false /* fCreate */),
&pStorage);
if (RT_FAILURE(rc))
goto out;
{
goto out;
}
rc = pInterfaceIOCallbacks->pfnReadSync(pInterfaceIO->pvUser, pStorage, 0, &Hdr, sizeof(Hdr), NULL);
if (RT_FAILURE(rc))
{
goto out;
}
rc = VINF_SUCCESS;
else
out:
return rc;
}
/** @copydoc VDCACHEBACKEND::pfnOpen */
void **ppBackendData)
{
LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
int rc;
/* Check open flags. All valid flags are supported. */
if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
{
goto out;
}
/* Check remaining arguments. */
if ( !VALID_PTR(pszFilename)
|| !*pszFilename)
{
goto out;
}
if (!pCache)
{
rc = VERR_NO_MEMORY;
goto out;
}
if (RT_SUCCESS(rc))
*ppBackendData = pCache;
else
out:
return rc;
}
/** @copydoc VDCACHEBACKEND::pfnCreate */
unsigned uImageFlags, const char *pszComment,
unsigned uPercentStart, unsigned uPercentSpan,
{
LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p ppBackendData=%#p",
pszFilename, cbSize, uImageFlags, pszComment, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, ppBackendData));
int rc;
if (pIfProgress)
{
if (pCbProgress)
}
/* Check open flags. All valid flags are supported. */
if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
{
goto out;
}
/* Check remaining arguments. */
if ( !VALID_PTR(pszFilename)
|| !*pszFilename)
{
goto out;
}
if (!pCache)
{
rc = VERR_NO_MEMORY;
goto out;
}
if (RT_SUCCESS(rc))
{
* image is opened in read-only mode if the caller requested that. */
if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
{
vciFreeImage(pCache, false);
if (RT_FAILURE(rc))
{
goto out;
}
}
*ppBackendData = pCache;
}
else
out:
return rc;
}
/** @copydoc VDCACHEBACKEND::pfnClose */
{
int rc;
return rc;
}
/** @copydoc VDCACHEBACKEND::pfnRead */
{
LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
int rc = VINF_SUCCESS;
if (pExtent)
{
}
else
{
/** @todo Best fit to check whether we have cached data later and set
* pcbActuallyRead accordingly. */
}
if (pcbActuallyRead)
out:
return rc;
}
/** @copydoc VDCACHEBACKEND::pfnWrite */
{
LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p\n",
int rc = VINF_SUCCESS;
out:
return rc;
}
/** @copydoc VDCACHEBACKEND::pfnFlush */
static int vciFlush(void *pBackendData)
{
int rc = VINF_SUCCESS;
return rc;
}
/** @copydoc VDCACHEBACKEND::pfnGetVersion */
static unsigned vciGetVersion(void *pBackendData)
{
if (pCache)
return 1;
else
return 0;
}
/** @copydoc VDCACHEBACKEND::pfnGetSize */
{
return cb;
}
/** @copydoc VDCACHEBACKEND::pfnGetFileSize */
{
if (pCache)
{
{
if (RT_SUCCESS(rc))
}
}
return cb;
}
/** @copydoc VDCACHEBACKEND::pfnGetImageFlags */
static unsigned vciGetImageFlags(void *pBackendData)
{
unsigned uImageFlags;
if (pCache)
else
uImageFlags = 0;
return uImageFlags;
}
/** @copydoc VDCACHEBACKEND::pfnGetOpenFlags */
static unsigned vciGetOpenFlags(void *pBackendData)
{
unsigned uOpenFlags;
if (pCache)
else
uOpenFlags = 0;
return uOpenFlags;
}
/** @copydoc VDCACHEBACKEND::pfnSetOpenFlags */
{
int rc;
/* Image must be opened and the new flags must be valid. Just readonly and
* info flags are supported. */
{
goto out;
}
/* Implement this operation via reopening the image. */
if (RT_FAILURE(rc))
goto out;
out:
return rc;
}
/** @copydoc VDCACHEBACKEND::pfnGetComment */
{
LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
int rc;
if (pCache)
else
return rc;
}
/** @copydoc VDCACHEBACKEND::pfnSetComment */
{
int rc;
if (pCache)
{
else
}
else
out:
return rc;
}
/** @copydoc VDCACHEBACKEND::pfnGetUuid */
{
int rc;
if (pCache)
else
return rc;
}
/** @copydoc VDCACHEBACKEND::pfnSetUuid */
{
int rc;
if (pCache)
{
else
}
else
return rc;
}
/** @copydoc VDCACHEBACKEND::pfnGetModificationUuid */
{
int rc;
if (pCache)
else
return rc;
}
/** @copydoc VDCACHEBACKEND::pfnSetModificationUuid */
{
int rc;
if (pCache)
{
else
}
else
return rc;
}
/** @copydoc VDCACHEBACKEND::pfnDump */
static void vciDump(void *pBackendData)
{
}
/** @copydoc VDCACHEBACKEND::pfnAsyncRead */
{
int rc = VERR_NOT_SUPPORTED;
return rc;
}
/** @copydoc VDCACHEBACKEND::pfnAsyncWrite */
{
int rc = VERR_NOT_SUPPORTED;
return rc;
}
/** @copydoc VDCACHEBACKEND::pfnAsyncFlush */
{
int rc = VERR_NOT_SUPPORTED;
return rc;
}
{
/* pszBackendName */
"vci",
/* cbSize */
sizeof(VDCACHEBACKEND),
/* uBackendCaps */
/* papszFileExtensions */
/* paConfigInfo */
NULL,
/* hPlugin */
/* pfnProbe */
/* pfnOpen */
/* pfnCreate */
/* pfnClose */
/* pfnRead */
/* pfnWrite */
/* pfnFlush */
/* pfnGetVersion */
/* pfnGetSize */
/* pfnGetFileSize */
/* pfnGetImageFlags */
/* pfnGetOpenFlags */
/* pfnSetOpenFlags */
/* pfnGetComment */
/* pfnSetComment */
/* pfnGetUuid */
/* pfnSetUuid */
/* pfnGetModificationUuid */
/* pfnSetModificationUuid */
/* pfnDump */
/* pfnAsyncRead */
/* pfnAsyncWrite */
/* pfnAsyncFlush */
/* pfnComposeLocation */
NULL,
/* pfnComposeName */
};