summaryrefslogtreecommitdiff
path: root/src/VBox/Storage
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Storage')
-rw-r--r--src/VBox/Storage/DMG.cpp663
-rw-r--r--src/VBox/Storage/Debug/VDDbgIoLog.cpp4
-rw-r--r--src/VBox/Storage/ISCSI.cpp1237
-rw-r--r--src/VBox/Storage/Makefile.kmk1
-rw-r--r--src/VBox/Storage/Parallels.cpp342
-rw-r--r--src/VBox/Storage/QCOW.cpp670
-rw-r--r--src/VBox/Storage/QED.cpp598
-rw-r--r--src/VBox/Storage/RAW.cpp194
-rw-r--r--src/VBox/Storage/VCICache.cpp93
-rw-r--r--src/VBox/Storage/VD.cpp3115
-rw-r--r--src/VBox/Storage/VDI.cpp742
-rw-r--r--src/VBox/Storage/VDICore.h8
-rw-r--r--src/VBox/Storage/VDIfVfs.cpp413
-rw-r--r--src/VBox/Storage/VHD.cpp960
-rw-r--r--src/VBox/Storage/VHDX.cpp158
-rw-r--r--src/VBox/Storage/VMDK.cpp1413
-rw-r--r--src/VBox/Storage/testcase/Makefile.kmk49
-rw-r--r--src/VBox/Storage/testcase/VDDefs.h38
-rw-r--r--src/VBox/Storage/testcase/VDIoBackend.cpp283
-rw-r--r--src/VBox/Storage/testcase/VDIoBackend.h90
-rw-r--r--src/VBox/Storage/testcase/VDIoBackendMem.cpp31
-rw-r--r--src/VBox/Storage/testcase/VDIoBackendMem.h20
-rw-r--r--src/VBox/Storage/testcase/VDScript.cpp2733
-rw-r--r--src/VBox/Storage/testcase/VDScript.h153
-rw-r--r--src/VBox/Storage/testcase/VDScriptAst.cpp343
-rw-r--r--src/VBox/Storage/testcase/VDScriptAst.h452
-rw-r--r--src/VBox/Storage/testcase/VDScriptChecker.cpp31
-rw-r--r--src/VBox/Storage/testcase/VDScriptInternal.h107
-rw-r--r--src/VBox/Storage/testcase/VDScriptInterp.cpp1054
-rw-r--r--src/VBox/Storage/testcase/VDScriptStack.h142
-rw-r--r--src/VBox/Storage/testcase/tstVD-2.cpp2
-rw-r--r--src/VBox/Storage/testcase/tstVD.cpp2
-rw-r--r--src/VBox/Storage/testcase/tstVDCompact.vd125
-rw-r--r--src/VBox/Storage/testcase/tstVDCopy.cpp2
-rw-r--r--src/VBox/Storage/testcase/tstVDCopy.vd175
-rw-r--r--src/VBox/Storage/testcase/tstVDDiscard.vd114
-rw-r--r--src/VBox/Storage/testcase/tstVDIo.cpp1860
-rw-r--r--src/VBox/Storage/testcase/tstVDIo.vd159
-rw-r--r--src/VBox/Storage/testcase/tstVDResize.vd32
-rw-r--r--src/VBox/Storage/testcase/tstVDShareable.cpp2
-rw-r--r--src/VBox/Storage/testcase/tstVDShareable.vd108
-rw-r--r--src/VBox/Storage/testcase/tstVDSnap.cpp39
-rw-r--r--src/VBox/Storage/testcase/vbox-img.cpp180
43 files changed, 11120 insertions, 7817 deletions
diff --git a/src/VBox/Storage/DMG.cpp b/src/VBox/Storage/DMG.cpp
index 39fe1587..b5f22f0f 100644
--- a/src/VBox/Storage/DMG.cpp
+++ b/src/VBox/Storage/DMG.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2010 Oracle Corporation
+ * Copyright (C) 2010-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -20,19 +20,30 @@
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_VD_DMG
#include <VBox/vd-plugin.h>
+#include <VBox/vd-ifs.h>
#include <VBox/log.h>
#include <VBox/err.h>
-#include <iprt/assert.h>
+
#include <iprt/asm.h>
-#include <iprt/mem.h>
+#include <iprt/alloca.h>
+#include <iprt/assert.h>
+#include <iprt/base64.h>
#include <iprt/ctype.h>
+#include <iprt/mem.h>
#include <iprt/string.h>
-#include <iprt/base64.h>
#include <iprt/zip.h>
+#include <iprt/formats/xar.h>
+
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
+#if 0
+/** @def VBOX_WITH_DIRECT_XAR_ACCESS
+ * When defined, we will use RTVfs to access the XAR file instead of going
+ * the slightly longer way thru the VFS -> VD wrapper. */
+# define VBOX_WITH_DIRECT_XAR_ACCESS
+#endif
/** Sector size, multiply with all sector counts to get number of bytes. */
#define DMG_SECTOR_SIZE 512
@@ -310,8 +321,16 @@ typedef struct DMGIMAGE
PVDINTERFACE pVDIfsImage;
/** Error interface. */
PVDINTERFACEERROR pIfError;
- /** I/O interface. */
- PVDINTERFACEIOINT pIfIo;
+ /** I/O interface - careful accessing this because of hDmgFileInXar. */
+ PVDINTERFACEIOINT pIfIoXxx;
+
+
+ /** The VFS file handle for a DMG within a XAR archive. */
+ RTVFSFILE hDmgFileInXar;
+ /** XAR file system stream handle.
+ * Sitting on this isn't really necessary, but insurance against the XAR code
+ * changes making back references from child objects to the stream itself. */
+ RTVFSFSSTREAM hXarFss;
/** Flags the image was opened with. */
uint32_t uOpenFlags;
@@ -392,9 +411,6 @@ typedef struct DMGINFLATESTATE
} \
} while (0)
-/** VBoxDMG: Unable to parse the XML. */
-#define VERR_VD_DMG_XML_PARSE_ERROR (-3280)
-
/*******************************************************************************
* Static Variables *
@@ -420,6 +436,73 @@ static void dmgUdifCkSumHost2FileEndian(PDMGUDIFCKSUM pCkSum);
static void dmgUdifCkSumFile2HostEndian(PDMGUDIFCKSUM pCkSum);
static bool dmgUdifCkSumIsValid(PCDMGUDIFCKSUM pCkSum, const char *pszPrefix);
+
+
+/**
+ * vdIfIoIntFileReadSync / RTVfsFileReadAt wrapper.
+ */
+static int dmgWrapFileReadSync(PDMGIMAGE pThis, RTFOFF off, void *pvBuf, size_t cbToRead)
+{
+ int rc;
+ if (pThis->hDmgFileInXar == NIL_RTVFSFILE)
+ rc = vdIfIoIntFileReadSync(pThis->pIfIoXxx, pThis->pStorage, off, pvBuf, cbToRead);
+ else
+ rc = RTVfsFileReadAt(pThis->hDmgFileInXar, off, pvBuf, cbToRead, NULL);
+ return rc;
+}
+
+/**
+ * vdIfIoIntFileReadUser / RTVfsFileReadAt wrapper.
+ */
+static int dmgWrapFileReadUser(PDMGIMAGE pThis, RTFOFF off, PVDIOCTX pIoCtx, size_t cbToRead)
+{
+ int rc;
+ if (pThis->hDmgFileInXar == NIL_RTVFSFILE)
+ rc = vdIfIoIntFileReadUser(pThis->pIfIoXxx, pThis->pStorage, off, pIoCtx, cbToRead);
+ else
+ {
+ /*
+ * Alloate a temporary buffer on the stack or heap and use
+ * vdIfIoIntIoCtxCopyTo to work the context.
+ *
+ * The I/O context stuff seems too complicated and undocument that I'm
+ * not going to bother trying to implement this efficiently right now.
+ */
+ void *pvFree = NULL;
+ void *pvBuf;
+ if (cbToRead < _32K)
+ pvBuf = alloca(cbToRead);
+ else
+ pvFree = pvBuf = RTMemTmpAlloc(cbToRead);
+ if (pvBuf)
+ {
+ rc = RTVfsFileReadAt(pThis->hDmgFileInXar, off, pvBuf, cbToRead, NULL);
+ if (RT_SUCCESS(rc))
+ vdIfIoIntIoCtxCopyTo(pThis->pIfIoXxx, pIoCtx, pvBuf, cbToRead);
+ if (pvFree)
+ RTMemTmpFree(pvFree);
+ }
+ else
+ rc = VERR_NO_TMP_MEMORY;
+ }
+ return rc;
+}
+
+/**
+ * vdIfIoIntFileGetSize / RTVfsFileGetSize wrapper.
+ */
+static int dmgWrapFileGetSize(PDMGIMAGE pThis, uint64_t *pcbFile)
+{
+ int rc;
+ if (pThis->hDmgFileInXar == NIL_RTVFSFILE)
+ rc = vdIfIoIntFileGetSize(pThis->pIfIoXxx, pThis->pStorage, pcbFile);
+ else
+ rc = RTVfsFileGetSize(pThis->hDmgFileInXar, pcbFile);
+ return rc;
+}
+
+
+
static DECLCALLBACK(int) dmgFileInflateHelper(void *pvUser, void *pvBuf, size_t cbBuf, size_t *pcbBuf)
{
DMGINFLATESTATE *pInflateState = (DMGINFLATESTATE *)pvUser;
@@ -434,10 +517,7 @@ static DECLCALLBACK(int) dmgFileInflateHelper(void *pvUser, void *pvBuf, size_t
return VINF_SUCCESS;
}
cbBuf = RT_MIN(cbBuf, pInflateState->cbSize);
- int rc = vdIfIoIntFileReadSync(pInflateState->pImage->pIfIo,
- pInflateState->pImage->pStorage,
- pInflateState->uFileOffset,
- pvBuf, cbBuf, NULL);
+ int rc = dmgWrapFileReadSync(pInflateState->pImage, pInflateState->uFileOffset, pvBuf, cbBuf);
if (RT_FAILURE(rc))
return rc;
pInflateState->uFileOffset += cbBuf;
@@ -712,10 +792,11 @@ static int dmgFlushImage(PDMGIMAGE pThis)
{
int rc = VINF_SUCCESS;
- if ( pThis->pStorage
+ if ( pThis
+ && (pThis->pStorage || pThis->hDmgFileInXar != NIL_RTVFSFILE)
&& !(pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY))
{
- /* @todo handle writable files, update checksums etc. */
+ /** @todo handle writable files, update checksums etc. */
}
return rc;
@@ -734,13 +815,19 @@ static int dmgFreeImage(PDMGIMAGE pThis, bool fDelete)
* not signalled as an error. After all nothing bad happens. */
if (pThis)
{
+ RTVfsFileRelease(pThis->hDmgFileInXar);
+ pThis->hDmgFileInXar = NIL_RTVFSFILE;
+
+ RTVfsFsStrmRelease(pThis->hXarFss);
+ pThis->hXarFss = NIL_RTVFSFSSTREAM;
+
if (pThis->pStorage)
{
/* No point updating the file that is deleted anyway. */
if (!fDelete)
dmgFlushImage(pThis);
- vdIfIoIntFileClose(pThis->pIfIo, pThis->pStorage);
+ rc = vdIfIoIntFileClose(pThis->pIfIoXxx, pThis->pStorage);
pThis->pStorage = NULL;
}
@@ -765,7 +852,7 @@ static int dmgFreeImage(PDMGIMAGE pThis, bool fDelete)
}
if (fDelete && pThis->pszFilename)
- vdIfIoIntFileDelete(pThis->pIfIo, pThis->pszFilename);
+ vdIfIoIntFileDelete(pThis->pIfIoXxx, pThis->pszFilename);
if (pThis->pvDecompExtent)
{
@@ -773,7 +860,6 @@ static int dmgFreeImage(PDMGIMAGE pThis, bool fDelete)
pThis->pvDecompExtent = NULL;
pThis->cbDecompExtent = 0;
}
-
}
LogFlowFunc(("returns %Rrc\n", rc));
@@ -1356,6 +1442,103 @@ static int dmgBlkxParse(PDMGIMAGE pThis, PDMGBLKX pBlkx)
return rc;
}
+
+/**
+ * Worker for dmgOpenImage that tries to open a DMG inside a XAR file.
+ *
+ * We'll select the first .dmg inside the archive that we can get a file
+ * interface to.
+ *
+ * @returns VBox status code.
+ * @param fOpen Flags for defining the open type.
+ * @param pVDIfIoInt The internal VD I/O interface to use.
+ * @param pvStorage The storage pointer that goes with @a pVDIfsIo.
+ * @param pszFilename The input filename, optional.
+ * @param phXarFss Where to return the XAR file system stream handle on
+ * success
+ * @param phDmgFileInXar Where to return the VFS handle to the DMG file
+ * within the XAR image on success.
+ *
+ * @remarks Not using the PDMGIMAGE structure directly here because the function
+ * is being in serveral places.
+ */
+static int dmgOpenImageWithinXar(uint32_t fOpen, PVDINTERFACEIOINT pVDIfIoInt, void *pvStorage, const char *pszFilename,
+ PRTVFSFSSTREAM phXarFss, PRTVFSFILE phDmgFileInXar)
+{
+ /*
+ * Open the XAR file stream.
+ */
+ RTVFSFILE hVfsFile;
+#ifdef VBOX_WITH_DIRECT_XAR_ACCESS
+ int rc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
+#else
+ int rc = VDIfCreateVfsFile(NULL, pVDIfIoInt, pvStorage, fOpen, &hVfsFile);
+#endif
+ if (RT_FAILURE(rc))
+ return rc;
+
+ RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(hVfsFile);
+ RTVfsFileRelease(hVfsFile);
+
+ RTVFSFSSTREAM hXarFss;
+ rc = RTZipXarFsStreamFromIoStream(hVfsIos, 0 /*fFlags*/, &hXarFss);
+ RTVfsIoStrmRelease(hVfsIos);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Look for a DMG in the stream that we can use.
+ */
+ for (;;)
+ {
+ char *pszName;
+ RTVFSOBJTYPE enmType;
+ RTVFSOBJ hVfsObj;
+ rc = RTVfsFsStrmNext(hXarFss, &pszName, &enmType, &hVfsObj);
+ if (RT_FAILURE(rc))
+ break;
+
+ /* It must be a file object so it can be seeked, this also implies that
+ it's uncompressed. Then it must have the .dmg suffix. */
+ if (enmType == RTVFSOBJTYPE_FILE)
+ {
+ size_t cchName = strlen(pszName);
+ const char *pszSuff = pszName + cchName - 4;
+ if ( cchName >= 4
+ && pszSuff[0] == '.'
+ && (pszSuff[1] == 'd' || pszSuff[1] == 'D')
+ && (pszSuff[2] == 'm' || pszSuff[2] == 'M')
+ && (pszSuff[3] == 'g' || pszSuff[3] == 'G'))
+ {
+ RTVFSFILE hDmgFileInXar = RTVfsObjToFile(hVfsObj);
+ AssertBreakStmt(hDmgFileInXar != NIL_RTVFSFILE, rc = VERR_INTERNAL_ERROR_3);
+
+ if (pszFilename)
+ DMG_PRINTF(("DMG: Using '%s' within XAR file '%s'...\n", pszName, pszFilename));
+ *phXarFss = hXarFss;
+ *phDmgFileInXar = hDmgFileInXar;
+
+ RTStrFree(pszName);
+ RTVfsObjRelease(hVfsObj);
+
+ return VINF_SUCCESS;
+ }
+ }
+
+ /* Release the current return values. */
+ RTStrFree(pszName);
+ RTVfsObjRelease(hVfsObj);
+ }
+
+ /* Not found or some kind of error. */
+ RTVfsFsStrmRelease(hXarFss);
+ if (rc == VERR_EOF)
+ rc = VERR_VD_DMG_NOT_FOUND_INSIDE_XAR;
+ AssertStmt(RT_FAILURE_NP(rc), rc = VERR_INTERNAL_ERROR_4);
+ return rc;
+}
+
+
/**
* Worker for dmgOpen that reads in and validates all the necessary
* structures from the image.
@@ -1369,12 +1552,13 @@ static int dmgOpenImage(PDMGIMAGE pThis, unsigned uOpenFlags)
pThis->uOpenFlags = uOpenFlags;
pThis->pIfError = VDIfErrorGet(pThis->pVDIfsDisk);
- pThis->pIfIo = VDIfIoIntGet(pThis->pVDIfsImage);
- AssertPtrReturn(pThis->pIfIo, VERR_INVALID_PARAMETER);
+ pThis->pIfIoXxx = VDIfIoIntGet(pThis->pVDIfsImage);
+ pThis->hDmgFileInXar = NIL_RTVFSFILE;
+ pThis->hXarFss = NIL_RTVFSFSSTREAM;
+ AssertPtrReturn(pThis->pIfIoXxx, VERR_INVALID_PARAMETER);
- int rc = vdIfIoIntFileOpen(pThis->pIfIo, pThis->pszFilename,
- VDOpenFlagsToFileOpenFlags(uOpenFlags,
- false /* fCreate */),
+ int rc = vdIfIoIntFileOpen(pThis->pIfIoXxx, pThis->pszFilename,
+ VDOpenFlagsToFileOpenFlags(uOpenFlags, false /* fCreate */),
&pThis->pStorage);
if (RT_FAILURE(rc))
{
@@ -1384,16 +1568,47 @@ static int dmgOpenImage(PDMGIMAGE pThis, unsigned uOpenFlags)
}
/*
+ * Check for XAR archive.
+ */
+ uint32_t u32XarMagic;
+ rc = dmgWrapFileReadSync(pThis, 0, &u32XarMagic, sizeof(u32XarMagic));
+ if (RT_FAILURE(rc))
+ return rc;
+ if (u32XarMagic == XAR_HEADER_MAGIC)
+ {
+ rc = dmgOpenImageWithinXar(VDOpenFlagsToFileOpenFlags(uOpenFlags, false /* fCreate */),
+ pThis->pIfIoXxx,
+ pThis->pStorage,
+ pThis->pszFilename,
+ &pThis->hXarFss, &pThis->hDmgFileInXar);
+ if (RT_FAILURE(rc))
+ return rc;
+#ifdef VBOX_WITH_DIRECT_XAR_ACCESS
+ vdIfIoIntFileClose(pThis->pIfIoXxx, pThis->pStorage);
+ pThis->pStorage = NULL;
+#endif
+ }
+#if 0 /* This is for testing whether the VFS wrappers actually works. */
+ else
+ {
+ rc = RTVfsFileOpenNormal(pThis->pszFilename, VDOpenFlagsToFileOpenFlags(uOpenFlags, false /* fCreate */),
+ &pThis->hDmgFileInXar);
+ if (RT_FAILURE(rc))
+ return rc;
+ vdIfIoIntFileClose(pThis->pIfIoXxx, pThis->pStorage);
+ pThis->pStorage = NULL;
+ }
+#endif
+
+ /*
* Read the footer.
*/
- rc = vdIfIoIntFileGetSize(pThis->pIfIo, pThis->pStorage, &pThis->cbFile);
+ rc = dmgWrapFileGetSize(pThis, &pThis->cbFile);
if (RT_FAILURE(rc))
return rc;
if (pThis->cbFile < 1024)
return VERR_VD_DMG_INVALID_HEADER;
- rc = vdIfIoIntFileReadSync(pThis->pIfIo, pThis->pStorage,
- pThis->cbFile - sizeof(pThis->Ftr),
- &pThis->Ftr, sizeof(pThis->Ftr), NULL);
+ rc = dmgWrapFileReadSync(pThis, pThis->cbFile - sizeof(pThis->Ftr), &pThis->Ftr, sizeof(pThis->Ftr));
if (RT_FAILURE(rc))
return rc;
dmgUdifFtrFile2HostEndian(&pThis->Ftr);
@@ -1423,8 +1638,7 @@ static int dmgOpenImage(PDMGIMAGE pThis, unsigned uOpenFlags)
char *pszXml = (char *)RTMemAlloc(cchXml + 1);
if (!pszXml)
return VERR_NO_MEMORY;
- rc = vdIfIoIntFileReadSync(pThis->pIfIo, pThis->pStorage, pThis->Ftr.offXml,
- pszXml, cchXml, NULL);
+ rc = dmgWrapFileReadSync(pThis, pThis->Ftr.offXml, pszXml, cchXml);
if (RT_SUCCESS(rc))
{
pszXml[cchXml] = '\0';
@@ -1488,94 +1702,112 @@ static int dmgOpenImage(PDMGIMAGE pThis, unsigned uOpenFlags)
}
-/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
-static int dmgCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
- PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnCheckIfValid} */
+static DECLCALLBACK(int) dmgCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
+ PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
{
LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p penmType=%#p\n",
pszFilename, pVDIfsDisk, pVDIfsImage, penmType));
- int rc;
- PVDIOSTORAGE pStorage;
- uint64_t cbFile, offFtr = 0;
- DMGUDIF Ftr;
PVDINTERFACEIOINT pIfIo = VDIfIoIntGet(pVDIfsImage);
AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
/*
- * Open the file and read the footer.
+ * Open the file and check for XAR.
*/
- rc = vdIfIoIntFileOpen(pIfIo, pszFilename,
- VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_READONLY,
- false /* fCreate */),
- &pStorage);
- if (RT_SUCCESS(rc))
- rc = vdIfIoIntFileGetSize(pIfIo, pStorage, &cbFile);
- if (RT_SUCCESS(rc))
+ PVDIOSTORAGE pStorage = NULL;
+ int rc = vdIfIoIntFileOpen(pIfIo, pszFilename,
+ VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_READONLY, false /* fCreate */),
+ &pStorage);
+ if (RT_FAILURE(rc))
{
- offFtr = cbFile - sizeof(Ftr);
- rc = vdIfIoIntFileReadSync(pIfIo, pStorage, offFtr, &Ftr, sizeof(Ftr), NULL);
+ LogFlowFunc(("returns %Rrc (error opening file)\n", rc));
+ return rc;
}
- else
+
+ /*
+ * Check for XAR file.
+ */
+ RTVFSFSSTREAM hXarFss = NIL_RTVFSFSSTREAM;
+ RTVFSFILE hDmgFileInXar = NIL_RTVFSFILE;
+ uint32_t u32XarMagic;
+ rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0, &u32XarMagic, sizeof(u32XarMagic));
+ if ( RT_SUCCESS(rc)
+ && u32XarMagic == XAR_HEADER_MAGIC)
{
- vdIfIoIntFileClose(pIfIo, pStorage);
- rc = VERR_VD_DMG_INVALID_HEADER;
+ rc = dmgOpenImageWithinXar(RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE,
+ pIfIo, pStorage, pszFilename,
+ &hXarFss, &hDmgFileInXar);
+ if (RT_FAILURE(rc))
+ return rc;
}
+ /*
+ * Read the DMG footer.
+ */
+ uint64_t cbFile;
+ if (hDmgFileInXar == NIL_RTVFSFILE)
+ rc = vdIfIoIntFileGetSize(pIfIo, pStorage, &cbFile);
+ else
+ rc = RTVfsFileGetSize(hDmgFileInXar, &cbFile);
if (RT_SUCCESS(rc))
{
- /*
- * Do we recognize this stuff? Does it look valid?
- */
- if ( Ftr.u32Magic == RT_H2BE_U32(DMGUDIF_MAGIC)
- && Ftr.u32Version == RT_H2BE_U32(DMGUDIF_VER_CURRENT)
- && Ftr.cbFooter == RT_H2BE_U32(sizeof(Ftr)))
+ DMGUDIF Ftr;
+ uint64_t offFtr = cbFile - sizeof(Ftr);
+ if (hDmgFileInXar == NIL_RTVFSFILE)
+ rc = vdIfIoIntFileReadSync(pIfIo, pStorage, offFtr, &Ftr, sizeof(Ftr));
+ else
+ rc = RTVfsFileReadAt(hDmgFileInXar, offFtr, &Ftr, sizeof(Ftr), NULL);
+ if (RT_SUCCESS(rc))
{
- dmgUdifFtrFile2HostEndian(&Ftr);
- if (dmgUdifFtrIsValid(&Ftr, offFtr))
+ /*
+ * Do we recognize this stuff? Does it look valid?
+ */
+ if ( Ftr.u32Magic == RT_H2BE_U32_C(DMGUDIF_MAGIC)
+ && Ftr.u32Version == RT_H2BE_U32_C(DMGUDIF_VER_CURRENT)
+ && Ftr.cbFooter == RT_H2BE_U32_C(sizeof(Ftr)))
{
- rc = VINF_SUCCESS;
- *penmType = VDTYPE_DVD;
+ dmgUdifFtrFile2HostEndian(&Ftr);
+ if (dmgUdifFtrIsValid(&Ftr, offFtr))
+ {
+ rc = VINF_SUCCESS;
+ *penmType = VDTYPE_DVD;
+ }
+ else
+ {
+ DMG_PRINTF(("Bad DMG: '%s' offFtr=%RTfoff\n", pszFilename, offFtr));
+ rc = VERR_VD_DMG_INVALID_HEADER;
+ }
}
else
- {
- DMG_PRINTF(("Bad DMG: '%s' offFtr=%RTfoff\n", pszFilename, offFtr));
rc = VERR_VD_DMG_INVALID_HEADER;
- }
}
- else
- rc = VERR_VD_DMG_INVALID_HEADER;
}
+ else
+ rc = VERR_VD_DMG_INVALID_HEADER;
+ /* Clean up. */
+ RTVfsFileRelease(hDmgFileInXar);
+ RTVfsFsStrmRelease(hXarFss);
vdIfIoIntFileClose(pIfIo, pStorage);
LogFlowFunc(("returns %Rrc\n", rc));
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnOpen */
-static int dmgOpen(const char *pszFilename, unsigned uOpenFlags,
- PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
- VDTYPE enmType, void **ppBackendData)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnOpen} */
+static DECLCALLBACK(int) dmgOpen(const char *pszFilename, unsigned uOpenFlags,
+ PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+ VDTYPE enmType, void **ppBackendData)
{
LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
- int rc = VINF_SUCCESS;
- PDMGIMAGE pThis;
/* Check open flags. All valid flags are (in principle) supported. */
- if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
- {
- rc = VERR_INVALID_PARAMETER;
- goto out;
- }
+ AssertReturn(!(uOpenFlags & ~VD_OPEN_FLAGS_MASK), VERR_INVALID_PARAMETER);
/* Check remaining arguments. */
- if ( !VALID_PTR(pszFilename)
- || !*pszFilename)
- {
- rc = VERR_INVALID_PARAMETER;
- goto out;
- }
+ AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
+ AssertReturn(*pszFilename, VERR_INVALID_PARAMETER);
/*
* Reject combinations we don't currently support.
@@ -1587,45 +1819,42 @@ static int dmgOpen(const char *pszFilename, unsigned uOpenFlags,
if ( !(uOpenFlags & VD_OPEN_FLAGS_READONLY)
|| (uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO))
{
- rc = VERR_NOT_SUPPORTED;
- goto out;
+ LogFlowFunc(("Unsupported flag(s): %#x\n", uOpenFlags));
+ return VERR_INVALID_PARAMETER;
}
/*
* Create the basic instance data structure and open the file,
* then hand it over to a worker function that does all the rest.
*/
- pThis = (PDMGIMAGE)RTMemAllocZ(sizeof(*pThis));
- if (!pThis)
+ int rc = VERR_NO_MEMORY;
+ PDMGIMAGE pThis = (PDMGIMAGE)RTMemAllocZ(sizeof(*pThis));
+ if (pThis)
{
- rc = VERR_NO_MEMORY;
- goto out;
- }
+ pThis->pszFilename = pszFilename;
+ pThis->pStorage = NULL;
+ pThis->pVDIfsDisk = pVDIfsDisk;
+ pThis->pVDIfsImage = pVDIfsImage;
- pThis->pszFilename = pszFilename;
- pThis->pStorage = NULL;
- pThis->pVDIfsDisk = pVDIfsDisk;
- pThis->pVDIfsImage = pVDIfsImage;
-
- rc = dmgOpenImage(pThis, uOpenFlags);
- if (RT_SUCCESS(rc))
- *ppBackendData = pThis;
- else
- RTMemFree(pThis);
+ rc = dmgOpenImage(pThis, uOpenFlags);
+ if (RT_SUCCESS(rc))
+ *ppBackendData = pThis;
+ else
+ RTMemFree(pThis);
+ }
-out:
LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnCreate */
-static int dmgCreate(const char *pszFilename, uint64_t cbSize,
- unsigned uImageFlags, const char *pszComment,
- PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
- PCRTUUID pUuid, unsigned uOpenFlags,
- unsigned uPercentStart, unsigned uPercentSpan,
- PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
- PVDINTERFACE pVDIfsOperation, void **ppBackendData)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnCreate} */
+static DECLCALLBACK(int) dmgCreate(const char *pszFilename, uint64_t cbSize,
+ unsigned uImageFlags, const char *pszComment,
+ PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
+ PCRTUUID pUuid, unsigned uOpenFlags,
+ unsigned uPercentStart, unsigned uPercentSpan,
+ PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+ PVDINTERFACE pVDIfsOperation, void **ppBackendData)
{
LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p ppBackendData=%#p", pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, ppBackendData));
int rc = VERR_NOT_SUPPORTED;
@@ -1634,7 +1863,7 @@ static int dmgCreate(const char *pszFilename, uint64_t cbSize,
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnRename */
+/** @interface_method_impl{VBOXHDDBACKEND,pfnRename} */
static int dmgRename(void *pBackendData, const char *pszFilename)
{
LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
@@ -1644,25 +1873,25 @@ static int dmgRename(void *pBackendData, const char *pszFilename)
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnClose */
-static int dmgClose(void *pBackendData, bool fDelete)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnClose} */
+static DECLCALLBACK(int) dmgClose(void *pBackendData, bool fDelete)
{
LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
- int rc;
- rc = dmgFreeImage(pThis, fDelete);
+ int rc = dmgFreeImage(pThis, fDelete);
RTMemFree(pThis);
LogFlowFunc(("returns %Rrc\n", rc));
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnRead */
-static int dmgRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
- size_t cbToRead, size_t *pcbActuallyRead)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnRead} */
+static DECLCALLBACK(int) dmgRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
+ PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
+ LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
+ pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
PDMGEXTENT pExtent = NULL;
int rc = VINF_SUCCESS;
@@ -1674,8 +1903,8 @@ static int dmgRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
if ( uOffset + cbToRead > pThis->cbSize
|| cbToRead == 0)
{
- rc = VERR_INVALID_PARAMETER;
- goto out;
+ LogFlowFunc(("returns VERR_INVALID_PARAMETER\n"));
+ return VERR_INVALID_PARAMETER;
}
pExtent = dmgExtentGetFromOffset(pThis, DMG_BYTE2BLOCK(uOffset));
@@ -1691,14 +1920,12 @@ static int dmgRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
{
case DMGEXTENTTYPE_RAW:
{
- rc = vdIfIoIntFileReadSync(pThis->pIfIo, pThis->pStorage,
- pExtent->offFileStart + DMG_BLOCK2BYTE(uExtentRel),
- pvBuf, cbToRead, NULL);
+ rc = dmgWrapFileReadUser(pThis, pExtent->offFileStart + DMG_BLOCK2BYTE(uExtentRel), pIoCtx, cbToRead);
break;
}
case DMGEXTENTTYPE_ZERO:
{
- memset(pvBuf, 0, cbToRead);
+ vdIfIoIntIoCtxSet(pThis->pIfIoXxx, pIoCtx, 0, cbToRead);
break;
}
case DMGEXTENTTYPE_COMP_ZLIB:
@@ -1728,7 +1955,9 @@ static int dmgRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
}
if (RT_SUCCESS(rc))
- memcpy(pvBuf, (uint8_t *)pThis->pvDecompExtent + DMG_BLOCK2BYTE(uExtentRel), cbToRead);
+ vdIfIoIntIoCtxCopyTo(pThis->pIfIoXxx, pIoCtx,
+ (uint8_t *)pThis->pvDecompExtent + DMG_BLOCK2BYTE(uExtentRel),
+ cbToRead);
break;
}
default:
@@ -1741,18 +1970,17 @@ static int dmgRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
else
rc = VERR_INVALID_PARAMETER;
-out:
LogFlowFunc(("returns %Rrc\n", rc));
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnWrite */
-static int dmgWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
- size_t cbToWrite, size_t *pcbWriteProcess,
- size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnWrite} */
+static DECLCALLBACK(int) dmgWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
+ PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
+ size_t *pcbPostRead, unsigned fWrite)
{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
- pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
+ LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
+ pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
int rc = VERR_NOT_IMPLEMENTED;
@@ -1760,21 +1988,17 @@ static int dmgWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
Assert(uOffset % 512 == 0);
Assert(cbToWrite % 512 == 0);
- if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
- {
+ if (!(pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+ AssertMsgFailed(("Not implemented\n"));
+ else
rc = VERR_VD_IMAGE_READ_ONLY;
- goto out;
- }
-
- AssertMsgFailed(("Not implemented\n"));
-out:
LogFlowFunc(("returns %Rrc\n", rc));
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnFlush */
-static int dmgFlush(void *pBackendData)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnFlush} */
+static DECLCALLBACK(int) dmgFlush(void *pBackendData, PVDIOCTX pIoCtx)
{
LogFlowFunc(("pBackendData=%#p\n", pBackendData));
PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
@@ -1788,8 +2012,8 @@ static int dmgFlush(void *pBackendData)
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
-static unsigned dmgGetVersion(void *pBackendData)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnGetVersion} */
+static DECLCALLBACK(unsigned) dmgGetVersion(void *pBackendData)
{
LogFlowFunc(("pBackendData=%#p\n", pBackendData));
PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
@@ -1802,8 +2026,24 @@ static unsigned dmgGetVersion(void *pBackendData)
return 0;
}
-/** @copydoc VBOXHDDBACKEND::pfnGetSize */
-static uint64_t dmgGetSize(void *pBackendData)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnGetSectorSize} */
+static DECLCALLBACK(uint32_t) dmgGetSectorSize(void *pBackendData)
+{
+ LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+ PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+ uint32_t cb = 0;
+
+ AssertPtr(pThis);
+
+ if (pThis && (pThis->pStorage || pThis->hDmgFileInXar != NIL_RTVFSFILE))
+ cb = 2048;
+
+ LogFlowFunc(("returns %u\n", cb));
+ return cb;
+}
+
+/** @interface_method_impl{VBOXHDDBACKEND,pfnGetSize} */
+static DECLCALLBACK(uint64_t) dmgGetSize(void *pBackendData)
{
LogFlowFunc(("pBackendData=%#p\n", pBackendData));
PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
@@ -1811,15 +2051,15 @@ static uint64_t dmgGetSize(void *pBackendData)
AssertPtr(pThis);
- if (pThis && pThis->pStorage)
+ if (pThis && (pThis->pStorage || pThis->hDmgFileInXar != NIL_RTVFSFILE))
cb = pThis->cbSize;
LogFlowFunc(("returns %llu\n", cb));
return cb;
}
-/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
-static uint64_t dmgGetFileSize(void *pBackendData)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnGetFileSize} */
+static DECLCALLBACK(uint64_t) dmgGetFileSize(void *pBackendData)
{
LogFlowFunc(("pBackendData=%#p\n", pBackendData));
PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
@@ -1827,23 +2067,20 @@ static uint64_t dmgGetFileSize(void *pBackendData)
AssertPtr(pThis);
- if (pThis)
+ if (pThis && (pThis->pStorage || pThis->hDmgFileInXar != NIL_RTVFSFILE))
{
uint64_t cbFile;
- if (pThis->pStorage)
- {
- int rc = vdIfIoIntFileGetSize(pThis->pIfIo, pThis->pStorage, &cbFile);
- if (RT_SUCCESS(rc))
- cb = cbFile;
- }
+ int rc = dmgWrapFileGetSize(pThis, &cbFile);
+ if (RT_SUCCESS(rc))
+ cb = cbFile;
}
LogFlowFunc(("returns %lld\n", cb));
return cb;
}
-/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
-static int dmgGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnGetPCHSGeometry} */
+static DECLCALLBACK(int) dmgGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
{
LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
@@ -1868,8 +2105,8 @@ static int dmgGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
-static int dmgSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnSetPCHSGeometry} */
+static DECLCALLBACK(int) dmgSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
{
LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
@@ -1880,25 +2117,23 @@ static int dmgSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
if (pThis)
{
- if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+ if (!(pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY))
{
- rc = VERR_VD_IMAGE_READ_ONLY;
- goto out;
+ pThis->PCHSGeometry = *pPCHSGeometry;
+ rc = VINF_SUCCESS;
}
-
- pThis->PCHSGeometry = *pPCHSGeometry;
- rc = VINF_SUCCESS;
+ else
+ rc = VERR_VD_IMAGE_READ_ONLY;
}
else
rc = VERR_VD_NOT_OPENED;
-out:
LogFlowFunc(("returns %Rrc\n", rc));
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
-static int dmgGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnGetLCHSGeometry} */
+static DECLCALLBACK(int) dmgGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
{
LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
@@ -1923,8 +2158,8 @@ static int dmgGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
-static int dmgSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnSetLCHSGeometry} */
+static DECLCALLBACK(int) dmgSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
{
LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
@@ -1935,25 +2170,23 @@ static int dmgSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
if (pThis)
{
- if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+ if (!(pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY))
{
- rc = VERR_VD_IMAGE_READ_ONLY;
- goto out;
+ pThis->LCHSGeometry = *pLCHSGeometry;
+ rc = VINF_SUCCESS;
}
-
- pThis->LCHSGeometry = *pLCHSGeometry;
- rc = VINF_SUCCESS;
+ else
+ rc = VERR_VD_IMAGE_READ_ONLY;
}
else
rc = VERR_VD_NOT_OPENED;
-out:
LogFlowFunc(("returns %Rrc\n", rc));
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
-static unsigned dmgGetImageFlags(void *pBackendData)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnGetImageFlags} */
+static DECLCALLBACK(unsigned) dmgGetImageFlags(void *pBackendData)
{
LogFlowFunc(("pBackendData=%#p\n", pBackendData));
PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
@@ -1970,8 +2203,8 @@ static unsigned dmgGetImageFlags(void *pBackendData)
return uImageFlags;
}
-/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
-static unsigned dmgGetOpenFlags(void *pBackendData)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnGetOpenFlags} */
+static DECLCALLBACK(unsigned) dmgGetOpenFlags(void *pBackendData)
{
LogFlowFunc(("pBackendData=%#p\n", pBackendData));
PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
@@ -1988,15 +2221,17 @@ static unsigned dmgGetOpenFlags(void *pBackendData)
return uOpenFlags;
}
-/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
-static int dmgSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnSetOpenFlags} */
+static DECLCALLBACK(int) dmgSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
{
LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
int rc;
/* Image must be opened and the new flags must be valid. */
- if (!pThis || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL)))
+ if (!pThis || (uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
+ | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL
+ | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
{
rc = VERR_INVALID_PARAMETER;
goto out;
@@ -2013,9 +2248,8 @@ out:
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnGetComment */
-static int dmgGetComment(void *pBackendData, char *pszComment,
- size_t cbComment)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnGetComment} */
+static DECLCALLBACK(int) dmgGetComment(void *pBackendData, char *pszComment, size_t cbComment)
{
LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
@@ -2032,8 +2266,8 @@ static int dmgGetComment(void *pBackendData, char *pszComment,
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnSetComment */
-static int dmgSetComment(void *pBackendData, const char *pszComment)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnSetComment} */
+static DECLCALLBACK(int) dmgSetComment(void *pBackendData, const char *pszComment)
{
LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
PDMGIMAGE pImage = (PDMGIMAGE)pBackendData;
@@ -2055,8 +2289,8 @@ static int dmgSetComment(void *pBackendData, const char *pszComment)
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
-static int dmgGetUuid(void *pBackendData, PRTUUID pUuid)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnGetUuid} */
+static DECLCALLBACK(int) dmgGetUuid(void *pBackendData, PRTUUID pUuid)
{
LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
@@ -2073,8 +2307,8 @@ static int dmgGetUuid(void *pBackendData, PRTUUID pUuid)
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
-static int dmgSetUuid(void *pBackendData, PCRTUUID pUuid)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnSetUuid} */
+static DECLCALLBACK(int) dmgSetUuid(void *pBackendData, PCRTUUID pUuid)
{
LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
@@ -2097,8 +2331,8 @@ static int dmgSetUuid(void *pBackendData, PCRTUUID pUuid)
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
-static int dmgGetModificationUuid(void *pBackendData, PRTUUID pUuid)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnGetModificationUuid} */
+static DECLCALLBACK(int) dmgGetModificationUuid(void *pBackendData, PRTUUID pUuid)
{
LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
@@ -2115,8 +2349,8 @@ static int dmgGetModificationUuid(void *pBackendData, PRTUUID pUuid)
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
-static int dmgSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnSetModificationUuid} */
+static DECLCALLBACK(int) dmgSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
{
LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
@@ -2138,8 +2372,8 @@ static int dmgSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
-static int dmgGetParentUuid(void *pBackendData, PRTUUID pUuid)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnGetParentUuid} */
+static DECLCALLBACK(int) dmgGetParentUuid(void *pBackendData, PRTUUID pUuid)
{
LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
@@ -2156,8 +2390,8 @@ static int dmgGetParentUuid(void *pBackendData, PRTUUID pUuid)
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
-static int dmgSetParentUuid(void *pBackendData, PCRTUUID pUuid)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnSetParentUuid} */
+static DECLCALLBACK(int) dmgSetParentUuid(void *pBackendData, PCRTUUID pUuid)
{
LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
@@ -2179,8 +2413,8 @@ static int dmgSetParentUuid(void *pBackendData, PCRTUUID pUuid)
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
-static int dmgGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnGetParentModificationUuid} */
+static DECLCALLBACK(int) dmgGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
{
LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
@@ -2197,8 +2431,8 @@ static int dmgGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
-static int dmgSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnSetParentModificationUuid} */
+static DECLCALLBACK(int) dmgSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
{
LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
@@ -2220,18 +2454,18 @@ static int dmgSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnDump */
-static void dmgDump(void *pBackendData)
+/** @interface_method_impl{VBOXHDDBACKEND,pfnDump} */
+static DECLCALLBACK(void) dmgDump(void *pBackendData)
{
PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
AssertPtr(pThis);
if (pThis)
{
- vdIfErrorMessage(pThis->pIfError, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cbSector=%llu\n",
+ vdIfErrorMessage(pThis->pIfError, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cSectors=%llu\n",
pThis->PCHSGeometry.cCylinders, pThis->PCHSGeometry.cHeads, pThis->PCHSGeometry.cSectors,
pThis->LCHSGeometry.cCylinders, pThis->LCHSGeometry.cHeads, pThis->LCHSGeometry.cSectors,
- pThis->cbSize / 512);
+ pThis->cbSize / DMG_SECTOR_SIZE);
}
}
@@ -2266,8 +2500,12 @@ VBOXHDDBACKEND g_DmgBackend =
dmgWrite,
/* pfnFlush */
dmgFlush,
+ /* pfnDiscard */
+ NULL,
/* pfnGetVersion */
dmgGetVersion,
+ /* pfnGetSectorSize */
+ dmgGetSectorSize,
/* pfnGetSize */
dmgGetSize,
/* pfnGetFileSize */
@@ -2318,12 +2556,6 @@ VBOXHDDBACKEND g_DmgBackend =
NULL,
/* pfnSetParentFilename */
NULL,
- /* pfnAsyncRead */
- NULL,
- /* pfnAsyncWrite */
- NULL,
- /* pfnAsyncFlush */
- NULL,
/* pfnComposeLocation */
genericFileComposeLocation,
/* pfnComposeName */
@@ -2332,10 +2564,7 @@ VBOXHDDBACKEND g_DmgBackend =
NULL,
/* pfnResize */
NULL,
- /* pfnDiscard */
- NULL,
- /* pfnAsyncDiscard */
- NULL,
/* pfnRepair */
NULL
};
+
diff --git a/src/VBox/Storage/Debug/VDDbgIoLog.cpp b/src/VBox/Storage/Debug/VDDbgIoLog.cpp
index 956b368a..1aec115b 100644
--- a/src/VBox/Storage/Debug/VDDbgIoLog.cpp
+++ b/src/VBox/Storage/Debug/VDDbgIoLog.cpp
@@ -5,7 +5,7 @@
*/
/*
- * Copyright (C) 2011 Oracle Corporation
+ * Copyright (C) 2011-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -226,7 +226,7 @@ static int vddbgIoLoggerHeaderUpdate(PVDIOLOGGERINT pIoLogger)
* Writes data from the given S/G buffer into the I/O log.
*
* @returns VBox status code.
- * @param pIoLogger The I/O logger to use.
+ * @param pIoLogger The I/O logger to use.
* @param off The start offset in the log to write to.
* @param pSgBuf The S/G buffer to write.
* @param cbSgBuf How much data to write.
diff --git a/src/VBox/Storage/ISCSI.cpp b/src/VBox/Storage/ISCSI.cpp
index 76b24444..2ed7f4f3 100644
--- a/src/VBox/Storage/ISCSI.cpp
+++ b/src/VBox/Storage/ISCSI.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2011 Oracle Corporation
+ * Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -256,7 +256,7 @@ typedef enum ISCSISTATE
} ISCSISTATE;
/**
- * iSCSI PDU send flags (and maybe more in the future). */
+ * iSCSI PDU send/receive flags (and maybe more in the future). */
typedef enum ISCSIPDUFLAGS
{
/** No special flags */
@@ -335,6 +335,8 @@ typedef struct ISCSIIMAGE *PISCSIIMAGE;
*/
typedef struct SCSIREQ
{
+ /** I/O context associated with this request. */
+ PVDIOCTX pIoCtx;
/** Transfer direction. */
SCSIXFER enmXfer;
/** Length of command block. */
@@ -348,10 +350,12 @@ typedef struct SCSIREQ
size_t cbSense;
/** Completion status of the command. */
uint8_t status;
- /** Pointer to command block. */
- void *pvCDB;
- /** Pointer to sense buffer. */
- void *pvSense;
+ /** The CDB. */
+ uint8_t abCDB[16];
+ /** The sense buffer. */
+ uint8_t abSense[96];
+ /** Status code to return if we got sense data. */
+ int rcSense;
/** Pointer to the Initiator2Target S/G list. */
PRTSGSEG paI2TSegs;
/** Number of entries in the I2T S/G list. */
@@ -362,38 +366,16 @@ typedef struct SCSIREQ
unsigned cT2ISegs;
/** S/G buffer for the target to initiator bits. */
RTSGBUF SgBufT2I;
-} SCSIREQ, *PSCSIREQ;
-
-/**
- * Async request structure holding all necessary data for
- * request processing.
- */
-typedef struct SCSIREQASYNC
-{
- /** I/O context associated with this request. */
- PVDIOCTX pIoCtx;
- /** Pointer to the SCSI request structure. */
- PSCSIREQ pScsiReq;
- /** The CDB. */
- uint8_t abCDB[16];
- /** The sense buffer. */
- uint8_t abSense[96];
- /** Status code to return if we got sense data. */
- int rcSense;
/** Number of retries if the command completes with sense
* data before we return with an error.
*/
unsigned cSenseRetries;
- /** The number of entries in the I2T S/G list. */
- unsigned cI2TSegs;
- /** The number of entries in the T2I S/G list. */
- unsigned cT2ISegs;
/** The S/G list - variable in size.
* This array holds both the I2T and T2I segments.
* The I2T segments are first and the T2I are second.
*/
RTSGSEG aSegs[1];
-} SCSIREQASYNC, *PSCSIREQASYNC;
+} SCSIREQ, *PSCSIREQ;
typedef enum ISCSICMDTYPE
{
@@ -451,15 +433,15 @@ typedef struct ISCSICMD
struct
{
/** The SCSI request to process. */
- PSCSIREQ pScsiReq;
+ PSCSIREQ pScsiReq;
} ScsiReq;
/** Call a function in the I/O thread. */
struct
{
/** The method to execute. */
- PFNISCSIEXEC pfnExec;
+ PFNISCSIEXEC pfnExec;
/** User data. */
- void *pvUser;
+ void *pvUser;
} Exec;
} CmdType;
} ISCSICMD, *PISCSICMD;
@@ -671,7 +653,7 @@ static const VDCONFIGINFO s_iscsiConfigInfo[] =
/* iSCSI low-level functions (only to be used from the iSCSI high-level functions). */
static uint32_t iscsiNewITT(PISCSIIMAGE pImage);
static int iscsiSendPDU(PISCSIIMAGE pImage, PISCSIREQ paReq, uint32_t cnReq, uint32_t uFlags);
-static int iscsiRecvPDU(PISCSIIMAGE pImage, uint32_t itt, PISCSIRES paRes, uint32_t cnRes);
+static int iscsiRecvPDU(PISCSIIMAGE pImage, uint32_t itt, PISCSIRES paRes, uint32_t cnRes, uint32_t fFlags);
static int iscsiRecvPDUAsync(PISCSIIMAGE pImage);
static int iscsiSendPDUAsync(PISCSIIMAGE pImage);
static int iscsiValidatePDU(PISCSIRES paRes, uint32_t cnRes);
@@ -1138,7 +1120,10 @@ static int iscsiTransportOpen(PISCSIIMAGE pImage)
rc = VERR_NO_MEMORY;
else
{
- memcpy(pImage->pszHostname, pImage->pszTargetAddress, cbHostname);
+ if (pImage->pszTargetAddress[0] == '[')
+ memcpy(pImage->pszHostname, pImage->pszTargetAddress + 1, cbHostname);
+ else
+ memcpy(pImage->pszHostname, pImage->pszTargetAddress, cbHostname);
pImage->pszHostname[cbHostname] = '\0';
if (pcszPort != NULL)
{
@@ -1204,6 +1189,7 @@ static int iscsiAttach(void *pvUser)
uint32_t cnISCSIRes;
ISCSIRES aISCSIRes[2];
uint32_t aResBHS[12];
+ unsigned cRetries = 5;
char *pszNext;
PISCSIIMAGE pImage = (PISCSIIMAGE)pvUser;
@@ -1240,6 +1226,20 @@ static int iscsiAttach(void *pvUser)
iscsiTransportClose(pImage);
restart:
+ if (!cRetries)
+ {
+ /*
+ * Prevent the iSCSI initiator to go into normal state if we are here,
+ * even if there is no error code set.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ AssertMsgFailed(("Success status code set while out of retries\n"));
+ rc = VERR_IPE_UNEXPECTED_STATUS;
+ }
+ goto out;
+ }
+
if (!iscsiIsClientConnected(pImage))
{
rc = iscsiTransportOpen(pImage);
@@ -1379,8 +1379,17 @@ restart:
aISCSIRes[cnISCSIRes].cbSeg = sizeof(bBuf);
cnISCSIRes++;
- rc = iscsiRecvPDU(pImage, itt, aISCSIRes, cnISCSIRes);
- if (RT_FAILURE(rc))
+ rc = iscsiRecvPDU(pImage, itt, aISCSIRes, cnISCSIRes, ISCSIPDU_NO_REATTACH);
+ if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
+ {
+ /*
+ * We lost connection to the target while receiving the answer,
+ * start from the beginning.
+ */
+ cRetries--;
+ goto restart;
+ }
+ else if (RT_FAILURE(rc))
break;
/** @todo collect partial login responses with Continue bit set. */
Assert(aISCSIRes[0].pvSeg == aResBHS);
@@ -1716,7 +1725,7 @@ static int iscsiDetach(void *pvUser)
aISCSIRes.pvSeg = aResBHS;
aISCSIRes.cbSeg = sizeof(aResBHS);
- rc = iscsiRecvPDU(pImage, itt, &aISCSIRes, 1);
+ rc = iscsiRecvPDU(pImage, itt, &aISCSIRes, 1, ISCSIPDU_NO_REATTACH);
if (RT_SUCCESS(rc))
{
if (RT_N2H_U32(aResBHS[0]) != (ISCSI_FINAL_BIT | ISCSIOP_LOGOUT_RES))
@@ -1818,7 +1827,7 @@ static int iscsiCommand(PISCSIIMAGE pImage, PSCSIREQ pRequest)
aReqBHS[5] = RT_H2N_U32(cbData);
aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
- memcpy(aReqBHS + 8, pRequest->pvCDB, pRequest->cbCDB);
+ memcpy(aReqBHS + 8, pRequest->abCDB, pRequest->cbCDB);
pImage->CmdSN++;
aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
@@ -1877,7 +1886,7 @@ static int iscsiCommand(PISCSIIMAGE pImage, PSCSIREQ pRequest)
aISCSIRes[cnISCSIRes].cbSeg = sizeof(aStatus);
cnISCSIRes++;
- rc = iscsiRecvPDU(pImage, itt, aISCSIRes, cnISCSIRes);
+ rc = iscsiRecvPDU(pImage, itt, aISCSIRes, cnISCSIRes, ISCSIPDU_DEFAULT);
if (RT_FAILURE(rc))
break;
@@ -1910,13 +1919,13 @@ static int iscsiCommand(PISCSIIMAGE pImage, PSCSIREQ pRequest)
}
/* Truncate sense data if it doesn't fit into the buffer. */
pRequest->cbSense = RT_MIN(cbStat, pRequest->cbSense);
- memcpy(pRequest->pvSense,
+ memcpy(pRequest->abSense,
((const char *)aISCSIRes[1].pvSeg) + 2,
RT_MIN(aISCSIRes[1].cbSeg - 2, pRequest->cbSense));
if ( cnISCSIRes > 2 && aISCSIRes[2].cbSeg
&& (ssize_t)pRequest->cbSense - aISCSIRes[1].cbSeg + 2 > 0)
{
- memcpy((char *)pRequest->pvSense + aISCSIRes[1].cbSeg - 2,
+ memcpy((char *)pRequest->abSense + aISCSIRes[1].cbSeg - 2,
aISCSIRes[2].pvSeg,
pRequest->cbSense - aISCSIRes[1].cbSeg + 2);
}
@@ -2056,8 +2065,10 @@ static int iscsiSendPDU(PISCSIIMAGE pImage, PISCSIREQ paReq, uint32_t cnReq,
* @param pImage The iSCSI connection state to be used.
* @param paRes Pointer to array of iSCSI response sections.
* @param cnRes Number of valid iSCSI response sections in the array.
+ * @param fRecvFlags PDU receive flags.
*/
-static int iscsiRecvPDU(PISCSIIMAGE pImage, uint32_t itt, PISCSIRES paRes, uint32_t cnRes)
+static int iscsiRecvPDU(PISCSIIMAGE pImage, uint32_t itt, PISCSIRES paRes, uint32_t cnRes,
+ uint32_t fRecvFlags)
{
int rc = VINF_SUCCESS;
ISCSIRES aResBuf;
@@ -2078,19 +2089,21 @@ static int iscsiRecvPDU(PISCSIIMAGE pImage, uint32_t itt, PISCSIRES paRes, uint3
* try to restart by re-sending the original request (if any).
* This also handles the connection reestablishment (login etc.). */
RTThreadSleep(500);
- if (pImage->state != ISCSISTATE_IN_LOGIN)
+ if ( pImage->state != ISCSISTATE_IN_LOGIN
+ && !(fRecvFlags & ISCSIPDU_NO_REATTACH))
{
/* Attempt to re-login when a connection fails, but only when not
* currently logging in. */
rc = iscsiAttach(pImage);
if (RT_FAILURE(rc))
break;
- }
- if (pImage->paCurrReq != NULL)
- {
- rc = iscsiSendPDU(pImage, pImage->paCurrReq, pImage->cnCurrReq, ISCSIPDU_DEFAULT);
- if (RT_FAILURE(rc))
- break;
+
+ if (pImage->paCurrReq != NULL)
+ {
+ rc = iscsiSendPDU(pImage, pImage->paCurrReq, pImage->cnCurrReq, ISCSIPDU_DEFAULT);
+ if (RT_FAILURE(rc))
+ break;
+ }
}
}
else
@@ -2568,6 +2581,8 @@ static int iscsiValidatePDU(PISCSIRES paRes, uint32_t cnRes)
&& (hw0 & ( ISCSI_BI_READ_RESIDUAL_OVFL_BIT | ISCSI_BI_READ_RESIDUAL_UNFL_BIT
| ISCSI_RESIDUAL_OVFL_BIT))))
return VERR_PARSE_ERROR;
+ else
+ LogFlowFunc(("good SCSI response, first word %#08x\n", RT_N2H_U32(pcrgResBHS[0])));
break;
case ISCSIOP_LOGIN_RES:
/* Login responses must not contain contradicting transit and continue bits. */
@@ -2667,10 +2682,10 @@ static int iscsiPDUTxPrepare(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd)
paReqBHS[2] = RT_H2N_U32(pImage->LUN >> 32);
paReqBHS[3] = RT_H2N_U32(pImage->LUN & 0xffffffff);
paReqBHS[4] = pIScsiCmd->Itt;
- paReqBHS[5] = RT_H2N_U32(cbData);
+ paReqBHS[5] = RT_H2N_U32((uint32_t)cbData); Assert((uint32_t)cbData == cbData);
paReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
paReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
- memcpy(paReqBHS + 8, pScsiReq->pvCDB, pScsiReq->cbCDB);
+ memcpy(paReqBHS + 8, pScsiReq->abCDB, pScsiReq->cbCDB);
pIScsiPDU->CmdSN = pImage->CmdSN;
pImage->CmdSN++;
@@ -2783,7 +2798,7 @@ static int iscsiRecvPDUUpdateRequest(PISCSIIMAGE pImage, PISCSIRES paRes, uint32
{
/* Truncate sense data if it doesn't fit into the buffer. */
pScsiReq->cbSense = RT_MIN(cbStat, pScsiReq->cbSense);
- memcpy(pScsiReq->pvSense, (uint8_t *)pvSense + 2,
+ memcpy(pScsiReq->abSense, (uint8_t *)pvSense + 2,
RT_MIN(paRes[0].cbSeg - ISCSI_BHS_SIZE - 2, pScsiReq->cbSense));
}
}
@@ -3613,25 +3628,24 @@ static void iscsiCommandAsyncComplete(PISCSIIMAGE pImage, int rcReq, void *pvUse
{
bool fComplete = true;
size_t cbTransfered = 0;
- PSCSIREQASYNC pReqAsync = (PSCSIREQASYNC)pvUser;
- PSCSIREQ pScsiReq = pReqAsync->pScsiReq;
+ PSCSIREQ pScsiReq = (PSCSIREQ)pvUser;
if ( RT_SUCCESS(rcReq)
&& pScsiReq->cbSense > 0)
{
/* Try again if possible. */
- if (pReqAsync->cSenseRetries > 0)
+ if (pScsiReq->cSenseRetries > 0)
{
- pReqAsync->cSenseRetries--;
- pScsiReq->cbSense = sizeof(pReqAsync->abSense);
- int rc = iscsiCommandAsync(pImage, pScsiReq, iscsiCommandAsyncComplete, pReqAsync);
+ pScsiReq->cSenseRetries--;
+ pScsiReq->cbSense = sizeof(pScsiReq->abSense);
+ int rc = iscsiCommandAsync(pImage, pScsiReq, iscsiCommandAsyncComplete, pScsiReq);
if (RT_SUCCESS(rc))
fComplete = false;
else
- rcReq = pReqAsync->rcSense;
+ rcReq = pScsiReq->rcSense;
}
else
- rcReq = pReqAsync->rcSense;
+ rcReq = pScsiReq->rcSense;
}
if (fComplete)
@@ -3645,11 +3659,10 @@ static void iscsiCommandAsyncComplete(PISCSIIMAGE pImage, int rcReq, void *pvUse
/* Continue I/O context. */
pImage->pIfIo->pfnIoCtxCompleted(pImage->pIfIo->Core.pvUser,
- pReqAsync->pIoCtx, rcReq,
+ pScsiReq->pIoCtx, rcReq,
cbTransfered);
RTMemFree(pScsiReq);
- RTMemFree(pReqAsync);
}
}
@@ -4056,43 +4069,39 @@ static int iscsiOpenImage(PISCSIIMAGE pImage, unsigned uOpenFlags)
SCSIREQ sr;
RTSGSEG DataSeg;
- uint8_t sense[96];
uint8_t data8[8];
uint8_t data12[12];
/*
* Inquire available LUNs - purely dummy request.
*/
- uint8_t CDB_rlun[12];
uint8_t rlundata[16];
- CDB_rlun[0] = SCSI_REPORT_LUNS;
- CDB_rlun[1] = 0; /* reserved */
- CDB_rlun[2] = 0; /* reserved */
- CDB_rlun[3] = 0; /* reserved */
- CDB_rlun[4] = 0; /* reserved */
- CDB_rlun[5] = 0; /* reserved */
- CDB_rlun[6] = sizeof(rlundata) >> 24;
- CDB_rlun[7] = (sizeof(rlundata) >> 16) & 0xff;
- CDB_rlun[8] = (sizeof(rlundata) >> 8) & 0xff;
- CDB_rlun[9] = sizeof(rlundata) & 0xff;
- CDB_rlun[10] = 0; /* reserved */
- CDB_rlun[11] = 0; /* control */
+ RT_ZERO(sr.abCDB);
+ sr.abCDB[0] = SCSI_REPORT_LUNS;
+ sr.abCDB[1] = 0; /* reserved */
+ sr.abCDB[2] = 0; /* reserved */
+ sr.abCDB[3] = 0; /* reserved */
+ sr.abCDB[4] = 0; /* reserved */
+ sr.abCDB[5] = 0; /* reserved */
+ sr.abCDB[6] = sizeof(rlundata) >> 24;
+ sr.abCDB[7] = (sizeof(rlundata) >> 16) & 0xff;
+ sr.abCDB[8] = (sizeof(rlundata) >> 8) & 0xff;
+ sr.abCDB[9] = sizeof(rlundata) & 0xff;
+ sr.abCDB[10] = 0; /* reserved */
+ sr.abCDB[11] = 0; /* control */
DataSeg.pvSeg = rlundata;
DataSeg.cbSeg = sizeof(rlundata);
- sr.enmXfer = SCSIXFER_FROM_TARGET;
- sr.cbCDB = sizeof(CDB_rlun);
- sr.pvCDB = CDB_rlun;
+ sr.enmXfer = SCSIXFER_FROM_TARGET;
+ sr.cbCDB = 12;
sr.cbI2TData = 0;
sr.paI2TSegs = NULL;
sr.cI2TSegs = 0;
sr.cbT2IData = DataSeg.cbSeg;
sr.paT2ISegs = &DataSeg;
sr.cT2ISegs = 1;
- sr.cbSense = sizeof(sense);
- sr.pvSense = sense;
-
+ sr.cbSense = sizeof(sr.abSense);
rc = iscsiCommandSync(pImage, &sr, false, VERR_INVALID_STATE);
if (RT_FAILURE(rc))
{
@@ -4103,29 +4112,26 @@ static int iscsiOpenImage(PISCSIIMAGE pImage, unsigned uOpenFlags)
/*
* Inquire device characteristics - no tapes, scanners etc., please.
*/
- uint8_t CDB_inq[6];
- CDB_inq[0] = SCSI_INQUIRY;
- CDB_inq[1] = 0; /* reserved */
- CDB_inq[2] = 0; /* reserved */
- CDB_inq[3] = 0; /* reserved */
- CDB_inq[4] = sizeof(data8);
- CDB_inq[5] = 0; /* control */
+ RT_ZERO(sr.abCDB);
+ sr.abCDB[0] = SCSI_INQUIRY;
+ sr.abCDB[1] = 0; /* reserved */
+ sr.abCDB[2] = 0; /* reserved */
+ sr.abCDB[3] = 0; /* reserved */
+ sr.abCDB[4] = sizeof(data8);
+ sr.abCDB[5] = 0; /* control */
DataSeg.pvSeg = data8;
DataSeg.cbSeg = sizeof(data8);
- sr.enmXfer = SCSIXFER_FROM_TARGET;
- sr.cbCDB = sizeof(CDB_inq);
- sr.pvCDB = CDB_inq;
+ sr.enmXfer = SCSIXFER_FROM_TARGET;
+ sr.cbCDB = 6;
sr.cbI2TData = 0;
sr.paI2TSegs = NULL;
sr.cI2TSegs = 0;
sr.cbT2IData = DataSeg.cbSeg;
sr.paT2ISegs = &DataSeg;
sr.cT2ISegs = 1;
- sr.cbSense = sizeof(sense);
- sr.pvSense = sense;
-
+ sr.cbSense = sizeof(sr.abSense);
rc = iscsiCommandSync(pImage, &sr, true /* fRetry */, VERR_INVALID_STATE);
if (RT_SUCCESS(rc))
{
@@ -4162,31 +4168,27 @@ static int iscsiOpenImage(PISCSIIMAGE pImage, unsigned uOpenFlags)
* Query write disable bit in the device specific parameter entry in the
* mode parameter header. Refuse read/write opening of read only disks.
*/
-
- uint8_t CDB_ms[6];
uint8_t data4[4];
- CDB_ms[0] = SCSI_MODE_SENSE_6;
- CDB_ms[1] = 0; /* dbd=0/reserved */
- CDB_ms[2] = 0x3f; /* pc=0/page code=0x3f, ask for all pages */
- CDB_ms[3] = 0; /* subpage code=0, return everything in page_0 format */
- CDB_ms[4] = sizeof(data4); /* allocation length=4 */
- CDB_ms[5] = 0; /* control */
+ RT_ZERO(sr.abCDB);
+ sr.abCDB[0] = SCSI_MODE_SENSE_6;
+ sr.abCDB[1] = 0; /* dbd=0/reserved */
+ sr.abCDB[2] = 0x3f; /* pc=0/page code=0x3f, ask for all pages */
+ sr.abCDB[3] = 0; /* subpage code=0, return everything in page_0 format */
+ sr.abCDB[4] = sizeof(data4); /* allocation length=4 */
+ sr.abCDB[5] = 0; /* control */
DataSeg.pvSeg = data4;
DataSeg.cbSeg = sizeof(data4);
- sr.enmXfer = SCSIXFER_FROM_TARGET;
- sr.cbCDB = sizeof(CDB_ms);
- sr.pvCDB = CDB_ms;
+ sr.enmXfer = SCSIXFER_FROM_TARGET;
+ sr.cbCDB = 6;
sr.cbI2TData = 0;
sr.paI2TSegs = NULL;
sr.cI2TSegs = 0;
sr.cbT2IData = DataSeg.cbSeg;
sr.paT2ISegs = &DataSeg;
sr.cT2ISegs = 1;
- sr.cbSense = sizeof(sense);
- sr.pvSense = sense;
-
+ sr.cbSense = sizeof(sr.abSense);
rc = iscsiCommandSync(pImage, &sr, true /* fRetry */, VERR_INVALID_STATE);
if (RT_SUCCESS(rc))
{
@@ -4205,90 +4207,165 @@ static int iscsiOpenImage(PISCSIIMAGE pImage, unsigned uOpenFlags)
/*
* Determine sector size and capacity of the volume immediately.
*/
- uint8_t CDB_cap[16];
-
RT_ZERO(data12);
- RT_ZERO(CDB_cap);
- CDB_cap[0] = SCSI_SERVICE_ACTION_IN_16;
- CDB_cap[1] = SCSI_SVC_ACTION_IN_READ_CAPACITY_16; /* subcommand */
- CDB_cap[10+3] = sizeof(data12); /* allocation length (dword) */
+ RT_ZERO(sr.abCDB);
+ sr.abCDB[0] = SCSI_SERVICE_ACTION_IN_16;
+ sr.abCDB[1] = SCSI_SVC_ACTION_IN_READ_CAPACITY_16; /* subcommand */
+ sr.abCDB[10+3] = sizeof(data12); /* allocation length (dword) */
DataSeg.pvSeg = data12;
DataSeg.cbSeg = sizeof(data12);
- sr.enmXfer = SCSIXFER_FROM_TARGET;
- sr.cbCDB = sizeof(CDB_cap);
- sr.pvCDB = CDB_cap;
+ sr.enmXfer = SCSIXFER_FROM_TARGET;
+ sr.cbCDB = 16;
sr.cbI2TData = 0;
sr.paI2TSegs = NULL;
sr.cI2TSegs = 0;
sr.cbT2IData = DataSeg.cbSeg;
sr.paT2ISegs = &DataSeg;
sr.cT2ISegs = 1;
- sr.cbSense = sizeof(sense);
- sr.pvSense = sense;
+ sr.cbSense = sizeof(sr.abSense);
rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
- if ( RT_SUCCESS(rc)
- && sr.status == SCSI_STATUS_OK)
+ if (RT_SUCCESS(rc))
{
- pImage->cVolume = RT_BE2H_U64(*(uint64_t *)&data12[0]);
- pImage->cVolume++;
- pImage->cbSector = RT_BE2H_U32(*(uint32_t *)&data12[8]);
- pImage->cbSize = pImage->cVolume * pImage->cbSector;
- if (pImage->cVolume == 0 || pImage->cbSector != 512 || pImage->cbSize < pImage->cVolume)
+ bool fEnd = false;
+ uint8_t cMaxRetries = 10;
+ do
{
- rc = vdIfError(pImage->pIfError, VERR_VD_ISCSI_INVALID_TYPE,
- RT_SRC_POS, N_("iSCSI: target address %s, target name %s, SCSI LUN %lld reports media sector count=%llu sector size=%u"),
- pImage->pszTargetAddress, pImage->pszTargetName,
- pImage->LUN, pImage->cVolume, pImage->cbSector);
- }
+ switch (sr.status)
+ {
+ case SCSI_STATUS_OK:
+ {
+ pImage->cVolume = RT_BE2H_U64(*(uint64_t *)&data12[0]);
+ pImage->cVolume++;
+ pImage->cbSector = RT_BE2H_U32(*(uint32_t *)&data12[8]);
+ pImage->cbSize = pImage->cVolume * pImage->cbSector;
+ if (pImage->cVolume == 0 || pImage->cbSize < pImage->cVolume)
+ {
+ rc = vdIfError(pImage->pIfError, VERR_VD_ISCSI_INVALID_TYPE,
+ RT_SRC_POS, N_("iSCSI: target address %s, target name %s, SCSI LUN %lld reports media sector count=%llu sector size=%u"),
+ pImage->pszTargetAddress, pImage->pszTargetName,
+ pImage->LUN, pImage->cVolume, pImage->cbSector);
+ }
+ fEnd = true;
+ break;
+ }
+ case SCSI_STATUS_CHECK_CONDITION:
+ {
+ if((sr.abSense[2] & 0x0f) == SCSI_SENSE_UNIT_ATTENTION)
+ {
+ if( sr.abSense[12] == SCSI_ASC_POWER_ON_RESET_BUS_DEVICE_RESET_OCCURRED
+ && sr.abSense[13] == SCSI_ASCQ_POWER_ON_RESET_BUS_DEVICE_RESET_OCCURRED)
+ {
+/** @todo for future: prepare and send command "REQUEST SENSE" which will
+return the status of target and will clear any unit attention condition that it reports */
+ rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
+ if (RT_FAILURE(rc))
+ fEnd = true;
+ cMaxRetries--;
+ break;
+
+ }
+ }
+ break;
+ }
+ default:
+ {
+ rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
+ if (RT_FAILURE(rc))
+ fEnd = true;
+ cMaxRetries--;
+ break;
+ }
+ }
+ if (!cMaxRetries)
+ fEnd = true;
+ } while(!fEnd);
}
else
{
- uint8_t CDB_capfb[10];
-
RT_ZERO(data8);
- CDB_capfb[0] = SCSI_READ_CAPACITY;
- CDB_capfb[1] = 0; /* reserved */
- CDB_capfb[2] = 0; /* reserved */
- CDB_capfb[3] = 0; /* reserved */
- CDB_capfb[4] = 0; /* reserved */
- CDB_capfb[5] = 0; /* reserved */
- CDB_capfb[6] = 0; /* reserved */
- CDB_capfb[7] = 0; /* reserved */
- CDB_capfb[8] = 0; /* reserved */
- CDB_capfb[9] = 0; /* control */
+ sr.abCDB[0] = SCSI_READ_CAPACITY;
+ sr.abCDB[1] = 0; /* reserved */
+ sr.abCDB[2] = 0; /* reserved */
+ sr.abCDB[3] = 0; /* reserved */
+ sr.abCDB[4] = 0; /* reserved */
+ sr.abCDB[5] = 0; /* reserved */
+ sr.abCDB[6] = 0; /* reserved */
+ sr.abCDB[7] = 0; /* reserved */
+ sr.abCDB[8] = 0; /* reserved */
+ sr.abCDB[9] = 0; /* control */
DataSeg.pvSeg = data8;
DataSeg.cbSeg = sizeof(data8);
- sr.enmXfer = SCSIXFER_FROM_TARGET;
- sr.cbCDB = sizeof(CDB_capfb);
- sr.pvCDB = CDB_capfb;
+ sr.enmXfer = SCSIXFER_FROM_TARGET;
+ sr.cbCDB = 10;
sr.cbI2TData = 0;
sr.paI2TSegs = NULL;
sr.cI2TSegs = 0;
sr.cbT2IData = DataSeg.cbSeg;
sr.paT2ISegs = &DataSeg;
sr.cT2ISegs = 1;
- sr.cbSense = sizeof(sense);
- sr.pvSense = sense;
-
+ sr.cbSense = sizeof(sr.abSense);
rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
if (RT_SUCCESS(rc))
{
- pImage->cVolume = (data8[0] << 24) | (data8[1] << 16) | (data8[2] << 8) | data8[3];
- pImage->cVolume++;
- pImage->cbSector = (data8[4] << 24) | (data8[5] << 16) | (data8[6] << 8) | data8[7];
- pImage->cbSize = pImage->cVolume * pImage->cbSector;
- if (pImage->cVolume == 0 || pImage->cbSector != 512)
+ bool fEnd = false;
+ uint8_t cMaxRetries = 10;
+ do
{
- rc = vdIfError(pImage->pIfError, VERR_VD_ISCSI_INVALID_TYPE,
- RT_SRC_POS, N_("iSCSI: fallback capacity detectio for target address %s, target name %s, SCSI LUN %lld reports media sector count=%llu sector size=%u"),
- pImage->pszTargetAddress, pImage->pszTargetName,
- pImage->LUN, pImage->cVolume, pImage->cbSector);
- }
+ switch (sr.status)
+ {
+ case SCSI_STATUS_OK:
+ {
+ pImage->cVolume = (data8[0] << 24) | (data8[1] << 16) | (data8[2] << 8) | data8[3];
+ pImage->cVolume++;
+ pImage->cbSector = (data8[4] << 24) | (data8[5] << 16) | (data8[6] << 8) | data8[7];
+ pImage->cbSize = pImage->cVolume * pImage->cbSector;
+ if (pImage->cVolume == 0)
+ {
+ rc = vdIfError(pImage->pIfError, VERR_VD_ISCSI_INVALID_TYPE,
+ RT_SRC_POS, N_("iSCSI: fallback capacity detection for target address %s, target name %s, SCSI LUN %lld reports media sector count=%llu sector size=%u"),
+ pImage->pszTargetAddress, pImage->pszTargetName,
+ pImage->LUN, pImage->cVolume, pImage->cbSector);
+ }
+
+ fEnd = true;
+ break;
+ }
+ case SCSI_STATUS_CHECK_CONDITION:
+ {
+ if((sr.abSense[2] & 0x0f) == SCSI_SENSE_UNIT_ATTENTION)
+ {
+ if( sr.abSense[12] == SCSI_ASC_POWER_ON_RESET_BUS_DEVICE_RESET_OCCURRED
+ && sr.abSense[13] == SCSI_ASCQ_POWER_ON_RESET_BUS_DEVICE_RESET_OCCURRED)
+ {
+ /** @todo for future: prepare and send command "REQUEST SENSE" which will
+ return the status of target and will clear any unit attention condition that it reports */
+ rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
+ if (RT_FAILURE(rc))
+ fEnd = true;
+ cMaxRetries--;
+ break;
+
+ }
+ }
+ break;
+ }
+ default:
+ {
+ rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
+ if (RT_FAILURE(rc))
+ fEnd = true;
+ cMaxRetries--;
+ break;
+ }
+ }
+ if (!cMaxRetries)
+ fEnd = true;
+ } while(!fEnd);
}
else
{
@@ -4305,30 +4382,27 @@ static int iscsiOpenImage(PISCSIIMAGE pImage, unsigned uOpenFlags)
* to do it again.
*/
uint8_t aCachingModePage[32];
- uint8_t aCDBModeSense6[6];
memset(aCachingModePage, '\0', sizeof(aCachingModePage));
- aCDBModeSense6[0] = SCSI_MODE_SENSE_6;
- aCDBModeSense6[1] = 0;
- aCDBModeSense6[2] = (0x00 << 6) | (0x08 & 0x3f); /* Current values and caching mode page */
- aCDBModeSense6[3] = 0; /* Sub page code. */
- aCDBModeSense6[4] = sizeof(aCachingModePage) & 0xff;
- aCDBModeSense6[5] = 0;
+ sr.abCDB[0] = SCSI_MODE_SENSE_6;
+ sr.abCDB[1] = 0;
+ sr.abCDB[2] = (0x00 << 6) | (0x08 & 0x3f); /* Current values and caching mode page */
+ sr.abCDB[3] = 0; /* Sub page code. */
+ sr.abCDB[4] = sizeof(aCachingModePage) & 0xff;
+ sr.abCDB[5] = 0;
DataSeg.pvSeg = aCachingModePage;
DataSeg.cbSeg = sizeof(aCachingModePage);
- sr.enmXfer = SCSIXFER_FROM_TARGET;
- sr.cbCDB = sizeof(aCDBModeSense6);
- sr.pvCDB = aCDBModeSense6;
+ sr.enmXfer = SCSIXFER_FROM_TARGET;
+ sr.cbCDB = 6;
sr.cbI2TData = 0;
sr.paI2TSegs = NULL;
sr.cI2TSegs = 0;
sr.cbT2IData = DataSeg.cbSeg;
sr.paT2ISegs = &DataSeg;
sr.cT2ISegs = 1;
- sr.cbSense = sizeof(sense);
- sr.pvSense = sense;
+ sr.cbSense = sizeof(sr.abSense);
rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
if ( RT_SUCCESS(rc)
&& (sr.status == SCSI_STATUS_OK)
@@ -4352,29 +4426,26 @@ static int iscsiOpenImage(PISCSIIMAGE pImage, unsigned uOpenFlags)
ASMBitSet(&aCachingModePage[Offset + 2], 2);
ASMBitClear(&aCachingModePage[Offset + 2], 0);
- uint8_t aCDBCaching[6];
- aCDBCaching[0] = SCSI_MODE_SELECT_6;
- aCDBCaching[1] = 0; /* Don't write the page into NV RAM. */
- aCDBCaching[2] = 0;
- aCDBCaching[3] = 0;
- aCDBCaching[4] = sizeof(aCachingModePage) & 0xff;
- aCDBCaching[5] = 0;
+ sr.abCDB[0] = SCSI_MODE_SELECT_6;
+ sr.abCDB[1] = 0; /* Don't write the page into NV RAM. */
+ sr.abCDB[2] = 0;
+ sr.abCDB[3] = 0;
+ sr.abCDB[4] = sizeof(aCachingModePage) & 0xff;
+ sr.abCDB[5] = 0;
DataSeg.pvSeg = aCachingModePage;
DataSeg.cbSeg = sizeof(aCachingModePage);
- sr.enmXfer = SCSIXFER_TO_TARGET;
- sr.cbCDB = sizeof(aCDBCaching);
- sr.pvCDB = aCDBCaching;
+ sr.enmXfer = SCSIXFER_TO_TARGET;
+ sr.cbCDB = 6;
sr.cbI2TData = DataSeg.cbSeg;
sr.paI2TSegs = &DataSeg;
sr.cI2TSegs = 1;
sr.cbT2IData = 0;
sr.paT2ISegs = NULL;
sr.cT2ISegs = 0;
- sr.cbSense = sizeof(sense);
- sr.pvSense = sense;
- sr.status = 0;
+ sr.cbSense = sizeof(sr.abSense);
+ sr.status = 0;
rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
if ( RT_SUCCESS(rc)
&& (sr.status == SCSI_STATUS_OK))
@@ -4386,7 +4457,7 @@ static int iscsiOpenImage(PISCSIIMAGE pImage, unsigned uOpenFlags)
/* Log failures but continue. */
LogRel(("iSCSI: Could not enable read and write cache of target %s, rc=%Rrc status=%#x\n",
pImage->pszTargetName, rc, sr.status));
- LogRel(("iSCSI: Sense:\n%.*Rhxd\n", sr.cbSense, sense));
+ LogRel(("iSCSI: Sense:\n%.*Rhxd\n", sr.cbSense, sr.abSense));
rc = VINF_SUCCESS;
}
}
@@ -4394,8 +4465,8 @@ static int iscsiOpenImage(PISCSIIMAGE pImage, unsigned uOpenFlags)
else
{
/* Log errors but continue. */
- LogRel(("iSCSI: Could not check write cache of target %s, rc=%Rrc, got mode page %#x\n", pImage->pszTargetName, rc,aCachingModePage[0] & 0x3f));
- LogRel(("iSCSI: Sense:\n%.*Rhxd\n", sr.cbSense, sense));
+ LogRel(("iSCSI: Could not check write cache of target %s, rc=%Rrc, got mode page %#x\n", pImage->pszTargetName, rc, aCachingModePage[0] & 0x3f));
+ LogRel(("iSCSI: Sense:\n%.*Rhxd\n", sr.cbSense, sr.abSense));
rc = VINF_SUCCESS;
}
@@ -4515,251 +4586,312 @@ static int iscsiClose(void *pBackendData, bool fDelete)
}
/** @copydoc VBOXHDDBACKEND::pfnRead */
-static int iscsiRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
- size_t cbToRead, size_t *pcbActuallyRead)
+static int iscsiRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
+ PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
{
- /** @todo reinstate logging of the target everywhere - dropped temporarily */
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
- uint64_t lba;
- uint16_t tls;
- int rc;
-
- Assert(pImage);
- Assert(uOffset % 512 == 0);
- Assert(cbToRead % 512 == 0);
+ int rc = VINF_SUCCESS;
- Assert(pImage->cbSector);
- AssertPtr(pvBuf);
+ LogFlowFunc(("pBackendData=%p uOffset=%#llx pIoCtx=%#p cbToRead=%u pcbActuallyRead=%p\n",
+ pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
if ( uOffset + cbToRead > pImage->cbSize
|| cbToRead == 0)
- {
- rc = VERR_INVALID_PARAMETER;
- goto out;
- }
+ return VERR_INVALID_PARAMETER;
/*
* Clip read size to a value which is supported by the target.
*/
cbToRead = RT_MIN(cbToRead, pImage->cbRecvDataLength);
- lba = uOffset / pImage->cbSector;
- tls = (uint16_t)(cbToRead / pImage->cbSector);
- SCSIREQ sr;
- RTSGSEG T2ISeg;
- size_t cbCDB;
- uint8_t abCDB[16];
- uint8_t sense[96];
+ unsigned cT2ISegs = 0;
+ size_t cbSegs = 0;
- if (pImage->cVolume < _4G)
- {
- cbCDB = 10;
- abCDB[0] = SCSI_READ_10;
- abCDB[1] = 0; /* reserved */
- abCDB[2] = (lba >> 24) & 0xff;
- abCDB[3] = (lba >> 16) & 0xff;
- abCDB[4] = (lba >> 8) & 0xff;
- abCDB[5] = lba & 0xff;
- abCDB[6] = 0; /* reserved */
- abCDB[7] = (tls >> 8) & 0xff;
- abCDB[8] = tls & 0xff;
- abCDB[9] = 0; /* control */
- }
- else
+ /* Get the number of segments. */
+ cbSegs = pImage->pIfIo->pfnIoCtxSegArrayCreate(pImage->pIfIo->Core.pvUser, pIoCtx,
+ NULL, &cT2ISegs, cbToRead);
+ Assert(cbSegs == cbToRead);
+
+ PSCSIREQ pReq = (PSCSIREQ)RTMemAllocZ(RT_OFFSETOF(SCSIREQ, aSegs[cT2ISegs]));
+ if (RT_LIKELY(pReq))
{
- cbCDB = 16;
- abCDB[0] = SCSI_READ_16;
- abCDB[1] = 0; /* reserved */
- abCDB[2] = (lba >> 56) & 0xff;
- abCDB[3] = (lba >> 48) & 0xff;
- abCDB[4] = (lba >> 40) & 0xff;
- abCDB[5] = (lba >> 32) & 0xff;
- abCDB[6] = (lba >> 24) & 0xff;
- abCDB[7] = (lba >> 16) & 0xff;
- abCDB[8] = (lba >> 8) & 0xff;
- abCDB[9] = lba & 0xff;
- abCDB[10] = 0; /* tls unused */
- abCDB[11] = 0; /* tls unused */
- abCDB[12] = (tls >> 8) & 0xff;
- abCDB[13] = tls & 0xff;
- abCDB[14] = 0; /* reserved */
- abCDB[15] = 0; /* reserved */
- }
+ uint64_t lba;
+ uint16_t tls;
+ uint8_t *pbCDB = &pReq->abCDB[0];
+ size_t cbCDB;
- T2ISeg.pvSeg = pvBuf;
- T2ISeg.cbSeg = cbToRead;
+ lba = uOffset / pImage->cbSector;
+ tls = (uint16_t)(cbToRead / pImage->cbSector);
- sr.enmXfer = SCSIXFER_FROM_TARGET;
- sr.cbCDB = cbCDB;
- sr.pvCDB = abCDB;
- sr.cbI2TData = 0;
- sr.paI2TSegs = NULL;
- sr.cI2TSegs = 0;
- sr.cbT2IData = cbToRead;
- sr.paT2ISegs = &T2ISeg;
- sr.cT2ISegs = 1;
- sr.cbSense = sizeof(sense);
- sr.pvSense = sense;
+ cbSegs = pImage->pIfIo->pfnIoCtxSegArrayCreate(pImage->pIfIo->Core.pvUser, pIoCtx,
+ &pReq->aSegs[0],
+ &cT2ISegs, cbToRead);
+ Assert(cbSegs == cbToRead);
- rc = iscsiCommandSync(pImage, &sr, true, VERR_READ_ERROR);
- if (RT_FAILURE(rc))
- {
- LogFlow(("iscsiCommandSync(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
- *pcbActuallyRead = 0;
+ if (pImage->cVolume < _4G)
+ {
+ cbCDB = 10;
+ pbCDB[0] = SCSI_READ_10;
+ pbCDB[1] = 0; /* reserved */
+ pbCDB[2] = (lba >> 24) & 0xff;
+ pbCDB[3] = (lba >> 16) & 0xff;
+ pbCDB[4] = (lba >> 8) & 0xff;
+ pbCDB[5] = lba & 0xff;
+ pbCDB[6] = 0; /* reserved */
+ pbCDB[7] = (tls >> 8) & 0xff;
+ pbCDB[8] = tls & 0xff;
+ pbCDB[9] = 0; /* control */
+ }
+ else
+ {
+ cbCDB = 16;
+ pbCDB[0] = SCSI_READ_16;
+ pbCDB[1] = 0; /* reserved */
+ pbCDB[2] = (lba >> 56) & 0xff;
+ pbCDB[3] = (lba >> 48) & 0xff;
+ pbCDB[4] = (lba >> 40) & 0xff;
+ pbCDB[5] = (lba >> 32) & 0xff;
+ pbCDB[6] = (lba >> 24) & 0xff;
+ pbCDB[7] = (lba >> 16) & 0xff;
+ pbCDB[8] = (lba >> 8) & 0xff;
+ pbCDB[9] = lba & 0xff;
+ pbCDB[10] = 0; /* tls unused */
+ pbCDB[11] = 0; /* tls unused */
+ pbCDB[12] = (tls >> 8) & 0xff;
+ pbCDB[13] = tls & 0xff;
+ pbCDB[14] = 0; /* reserved */
+ pbCDB[15] = 0; /* reserved */
+ }
+
+ pReq->enmXfer = SCSIXFER_FROM_TARGET;
+ pReq->cbCDB = cbCDB;
+ pReq->cbI2TData = 0;
+ pReq->paI2TSegs = NULL;
+ pReq->cI2TSegs = 0;
+ pReq->cbT2IData = cbToRead;
+ pReq->paT2ISegs = &pReq->aSegs[pReq->cI2TSegs];
+ pReq->cT2ISegs = pReq->cT2ISegs;
+ pReq->cbSense = sizeof(pReq->abSense);
+ pReq->cT2ISegs = cT2ISegs;
+ pReq->pIoCtx = pIoCtx;
+ pReq->cSenseRetries = 10;
+ pReq->rcSense = VERR_READ_ERROR;
+
+ if (vdIfIoIntIoCtxIsSynchronous(pImage->pIfIo, pIoCtx))
+ {
+ rc = iscsiCommandSync(pImage, pReq, true, VERR_READ_ERROR);
+ if (RT_FAILURE(rc))
+ {
+ LogFlow(("iscsiCommandSync(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
+ *pcbActuallyRead = 0;
+ }
+ else
+ *pcbActuallyRead = pReq->cbT2IData;
+ }
+ else
+ {
+ rc = iscsiCommandAsync(pImage, pReq, iscsiCommandAsyncComplete, pReq);
+ if (RT_FAILURE(rc))
+ AssertMsgFailed(("iscsiCommandAsync(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
+ else
+ {
+ *pcbActuallyRead = cbToRead;
+ return VERR_VD_IOCTX_HALT; /* Halt the I/O context until further notification from the I/O thread. */
+ }
+ }
+
+ RTMemFree(pReq);
}
else
- *pcbActuallyRead = sr.cbT2IData;
+ rc = VERR_NO_MEMORY;
-out:
- LogFlowFunc(("returns %Rrc\n", rc));
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnWrite */
-static int iscsiWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
- size_t cbToWrite, size_t *pcbWriteProcess,
- size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
+static int iscsiWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
+ PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
+ size_t *pcbPostRead, unsigned fWrite)
{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
+ LogFlowFunc(("pBackendData=%p uOffset=%llu pIoCtx=%#p cbToWrite=%u pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p fWrite=%u\n",
+ pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite));
PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
- uint64_t lba;
- uint16_t tls;
- int rc;
+ int rc = VINF_SUCCESS;
- Assert(pImage);
+ AssertPtr(pImage);
Assert(uOffset % 512 == 0);
Assert(cbToWrite % 512 == 0);
- Assert(pImage->cbSector);
- Assert(pvBuf);
-
- if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
- {
- rc = VERR_VD_IMAGE_READ_ONLY;
- goto out;
- }
-
- *pcbPreRead = 0;
- *pcbPostRead = 0;
+ if (uOffset + cbToWrite > pImage->cbSize)
+ return VERR_INVALID_PARAMETER;
/*
- * Clip write size to a value which is supported by the target.
+ * Clip read size to a value which is supported by the target.
*/
cbToWrite = RT_MIN(cbToWrite, pImage->cbSendDataLength);
- lba = uOffset / pImage->cbSector;
- tls = (uint16_t)(cbToWrite / pImage->cbSector);
- SCSIREQ sr;
- RTSGSEG I2TSeg;
- size_t cbCDB;
- uint8_t abCDB[16];
- uint8_t sense[96];
+ unsigned cI2TSegs = 0;
+ size_t cbSegs = 0;
- if (pImage->cVolume < _4G)
- {
- cbCDB = 10;
- abCDB[0] = SCSI_WRITE_10;
- abCDB[1] = 0; /* reserved */
- abCDB[2] = (lba >> 24) & 0xff;
- abCDB[3] = (lba >> 16) & 0xff;
- abCDB[4] = (lba >> 8) & 0xff;
- abCDB[5] = lba & 0xff;
- abCDB[6] = 0; /* reserved */
- abCDB[7] = (tls >> 8) & 0xff;
- abCDB[8] = tls & 0xff;
- abCDB[9] = 0; /* control */
- }
- else
- {
- cbCDB = 16;
- abCDB[0] = SCSI_WRITE_16;
- abCDB[1] = 0; /* reserved */
- abCDB[2] = (lba >> 56) & 0xff;
- abCDB[3] = (lba >> 48) & 0xff;
- abCDB[4] = (lba >> 40) & 0xff;
- abCDB[5] = (lba >> 32) & 0xff;
- abCDB[6] = (lba >> 24) & 0xff;
- abCDB[7] = (lba >> 16) & 0xff;
- abCDB[8] = (lba >> 8) & 0xff;
- abCDB[9] = lba & 0xff;
- abCDB[10] = 0; /* tls unused */
- abCDB[11] = 0; /* tls unused */
- abCDB[12] = (tls >> 8) & 0xff;
- abCDB[13] = tls & 0xff;
- abCDB[14] = 0; /* reserved */
- abCDB[15] = 0; /* reserved */
- }
+ /* Get the number of segments. */
+ cbSegs = pImage->pIfIo->pfnIoCtxSegArrayCreate(pImage->pIfIo->Core.pvUser, pIoCtx,
+ NULL, &cI2TSegs, cbToWrite);
+ Assert(cbSegs == cbToWrite);
- I2TSeg.pvSeg = (void *)pvBuf;
- I2TSeg.cbSeg = cbToWrite;
-
- sr.enmXfer = SCSIXFER_TO_TARGET;
- sr.cbCDB = cbCDB;
- sr.pvCDB = abCDB;
- sr.cbI2TData = cbToWrite;
- sr.paI2TSegs = &I2TSeg;
- sr.cI2TSegs = 1;
- sr.cbT2IData = 0;
- sr.paT2ISegs = NULL;
- sr.cT2ISegs = 0;
- sr.cbSense = sizeof(sense);
- sr.pvSense = sense;
-
- rc = iscsiCommandSync(pImage, &sr, true, VERR_WRITE_ERROR);
- if (RT_FAILURE(rc))
+ PSCSIREQ pReq = (PSCSIREQ)RTMemAllocZ(RT_OFFSETOF(SCSIREQ, aSegs[cI2TSegs]));
+ if (RT_LIKELY(pReq))
{
- LogFlow(("iscsiCommandSync(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
- *pcbWriteProcess = 0;
+ uint64_t lba;
+ uint16_t tls;
+ uint8_t *pbCDB = &pReq->abCDB[0];
+ size_t cbCDB;
+
+ lba = uOffset / pImage->cbSector;
+ tls = (uint16_t)(cbToWrite / pImage->cbSector);
+
+ cbSegs = pImage->pIfIo->pfnIoCtxSegArrayCreate(pImage->pIfIo->Core.pvUser, pIoCtx,
+ &pReq->aSegs[0],
+ &cI2TSegs, cbToWrite);
+ Assert(cbSegs == cbToWrite);
+
+ if (pImage->cVolume < _4G)
+ {
+ cbCDB = 10;
+ pbCDB[0] = SCSI_WRITE_10;
+ pbCDB[1] = 0; /* reserved */
+ pbCDB[2] = (lba >> 24) & 0xff;
+ pbCDB[3] = (lba >> 16) & 0xff;
+ pbCDB[4] = (lba >> 8) & 0xff;
+ pbCDB[5] = lba & 0xff;
+ pbCDB[6] = 0; /* reserved */
+ pbCDB[7] = (tls >> 8) & 0xff;
+ pbCDB[8] = tls & 0xff;
+ pbCDB[9] = 0; /* control */
+ }
+ else
+ {
+ cbCDB = 16;
+ pbCDB[0] = SCSI_WRITE_16;
+ pbCDB[1] = 0; /* reserved */
+ pbCDB[2] = (lba >> 56) & 0xff;
+ pbCDB[3] = (lba >> 48) & 0xff;
+ pbCDB[4] = (lba >> 40) & 0xff;
+ pbCDB[5] = (lba >> 32) & 0xff;
+ pbCDB[6] = (lba >> 24) & 0xff;
+ pbCDB[7] = (lba >> 16) & 0xff;
+ pbCDB[8] = (lba >> 8) & 0xff;
+ pbCDB[9] = lba & 0xff;
+ pbCDB[10] = 0; /* tls unused */
+ pbCDB[11] = 0; /* tls unused */
+ pbCDB[12] = (tls >> 8) & 0xff;
+ pbCDB[13] = tls & 0xff;
+ pbCDB[14] = 0; /* reserved */
+ pbCDB[15] = 0; /* reserved */
+ }
+
+ pReq->enmXfer = SCSIXFER_TO_TARGET;
+ pReq->cbCDB = cbCDB;
+ pReq->cbI2TData = cbToWrite;
+ pReq->paI2TSegs = &pReq->aSegs[0];
+ pReq->cI2TSegs = cI2TSegs;
+ pReq->cbT2IData = 0;
+ pReq->paT2ISegs = NULL;
+ pReq->cT2ISegs = 0;
+ pReq->cbSense = sizeof(pReq->abSense);
+ pReq->pIoCtx = pIoCtx;
+ pReq->cSenseRetries = 10;
+ pReq->rcSense = VERR_WRITE_ERROR;
+
+ if (vdIfIoIntIoCtxIsSynchronous(pImage->pIfIo, pIoCtx))
+ {
+ rc = iscsiCommandSync(pImage, pReq, true, VERR_WRITE_ERROR);
+ if (RT_FAILURE(rc))
+ {
+ LogFlow(("iscsiCommandSync(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
+ *pcbWriteProcess = 0;
+ }
+ else
+ *pcbWriteProcess = cbToWrite;
+ }
+ else
+ {
+ rc = iscsiCommandAsync(pImage, pReq, iscsiCommandAsyncComplete, pReq);
+ if (RT_FAILURE(rc))
+ AssertMsgFailed(("iscsiCommandAsync(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
+ else
+ {
+ *pcbWriteProcess = cbToWrite;
+ return VERR_VD_IOCTX_HALT; /* Halt the I/O context until further notification from the I/O thread. */
+ }
+ }
+
+ RTMemFree(pReq);
}
else
- *pcbWriteProcess = cbToWrite;
+ rc = VERR_NO_MEMORY;
-out:
- LogFlowFunc(("returns %Rrc\n", rc));
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnFlush */
-static int iscsiFlush(void *pBackendData)
+static int iscsiFlush(void *pBackendData, PVDIOCTX pIoCtx)
{
- LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+ LogFlowFunc(("pBackendData=%p pIoCtx=%#p\n", pBackendData, pIoCtx));
PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
- int rc;
+ int rc = VINF_SUCCESS;
- Assert(pImage);
+ PSCSIREQ pReq = (PSCSIREQ)RTMemAllocZ(sizeof(SCSIREQ));
+ if (RT_LIKELY(pReq))
+ {
+ uint8_t *pbCDB = &pReq->abCDB[0];
+
+ pbCDB[0] = SCSI_SYNCHRONIZE_CACHE;
+ pbCDB[1] = 0; /* reserved */
+ pbCDB[2] = 0; /* reserved */
+ pbCDB[3] = 0; /* reserved */
+ pbCDB[4] = 0; /* reserved */
+ pbCDB[5] = 0; /* reserved */
+ pbCDB[6] = 0; /* reserved */
+ pbCDB[7] = 0; /* reserved */
+ pbCDB[8] = 0; /* reserved */
+ pbCDB[9] = 0; /* control */
+
+ pReq->enmXfer = SCSIXFER_NONE;
+ pReq->cbCDB = 10;
+ pReq->cbI2TData = 0;
+ pReq->paI2TSegs = NULL;
+ pReq->cI2TSegs = 0;
+ pReq->cbT2IData = 0;
+ pReq->paT2ISegs = NULL;
+ pReq->cT2ISegs = 0;
+ pReq->cbSense = sizeof(pReq->abSense);
+ pReq->pIoCtx = pIoCtx;
+ pReq->cSenseRetries = 0;
+ pReq->rcSense = VINF_SUCCESS;
+
+ if (vdIfIoIntIoCtxIsSynchronous(pImage->pIfIo, pIoCtx))
+ {
+ rc = iscsiCommandSync(pImage, pReq, false, VINF_SUCCESS);
+ if (RT_FAILURE(rc))
+ AssertMsgFailed(("iscsiCommand(%s) -> %Rrc\n", pImage->pszTargetName, rc));
+ }
+ else
+ {
+ rc = iscsiCommandAsync(pImage, pReq, iscsiCommandAsyncComplete, pReq);
+ if (RT_FAILURE(rc))
+ AssertMsgFailed(("iscsiCommand(%s) -> %Rrc\n", pImage->pszTargetName, rc));
+ else
+ return VERR_VD_IOCTX_HALT; /* Halt the I/O context until further notification from the I/O thread. */
+ }
- SCSIREQ sr;
- uint8_t abCDB[10];
- uint8_t sense[96];
-
- abCDB[0] = SCSI_SYNCHRONIZE_CACHE;
- abCDB[1] = 0; /* reserved */
- abCDB[2] = 0; /* LBA 0 */
- abCDB[3] = 0; /* LBA 0 */
- abCDB[4] = 0; /* LBA 0 */
- abCDB[5] = 0; /* LBA 0 */
- abCDB[6] = 0; /* reserved */
- abCDB[7] = 0; /* transfer everything to disk */
- abCDB[8] = 0; /* transfer everything to disk */
- abCDB[9] = 0; /* control */
-
- sr.enmXfer = SCSIXFER_NONE;
- sr.cbCDB = sizeof(abCDB);
- sr.pvCDB = abCDB;
- sr.cbI2TData = 0;
- sr.paI2TSegs = NULL;
- sr.cI2TSegs = 0;
- sr.cbT2IData = 0;
- sr.paT2ISegs = NULL;
- sr.cT2ISegs = 0;
- sr.cbSense = sizeof(sense);
- sr.pvSense = sense;
+ RTMemFree(pReq);
+ }
+ else
+ rc = VERR_NO_MEMORY;
- rc = iscsiCommandSync(pImage, &sr, false, VINF_SUCCESS);
- if (RT_FAILURE(rc))
- AssertMsgFailed(("iscsiCommand(%s) -> %Rrc\n", pImage->pszTargetName, rc));
- LogFlowFunc(("returns %Rrc\n", rc));
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
return rc;
}
@@ -4775,6 +4907,20 @@ static unsigned iscsiGetVersion(void *pBackendData)
return 0;
}
+/** @copydoc VBOXHDDBACKEND::pfnGetSectorSize */
+static uint32_t iscsiGetSectorSize(void *pBackendData)
+{
+ LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+ PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+
+ Assert(pImage);
+
+ if (pImage)
+ return pImage->cbSector;
+ else
+ return 0;
+}
+
/** @copydoc VBOXHDDBACKEND::pfnGetSize */
static uint64_t iscsiGetSize(void *pBackendData)
{
@@ -4934,7 +5080,9 @@ static int iscsiSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
int rc;
/* Image must be opened and the new flags must be valid. */
- if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL)))
+ if (!pImage || (uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
+ | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE
+ | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
{
rc = VERR_INVALID_PARAMETER;
goto out;
@@ -5184,313 +5332,6 @@ static void iscsiDump(void *pBackendData)
}
}
-/** @copydoc VBOXHDDBACKEND::pfnAsyncRead */
-static int iscsiAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
- PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
-{
- LogFlowFunc(("pBackendData=%p uOffset=%#llx pIoCtx=%#p cbToRead=%u pcbActuallyRead=%p\n",
- pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
- PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
- int rc = VINF_SUCCESS;
-
- if (uOffset + cbToRead > pImage->cbSize)
- return VERR_INVALID_PARAMETER;
-
- /*
- * Clip read size to a value which is supported by the target.
- */
- cbToRead = RT_MIN(cbToRead, pImage->cbRecvDataLength);
-
- unsigned cT2ISegs = 0;
- size_t cbSegs = 0;
-
- /* Get the number of segments. */
- cbSegs = pImage->pIfIo->pfnIoCtxSegArrayCreate(pImage->pIfIo->Core.pvUser, pIoCtx,
- NULL, &cT2ISegs, cbToRead);
- Assert(cbSegs == cbToRead);
-
- PSCSIREQASYNC pReqAsync = (PSCSIREQASYNC)RTMemAllocZ(RT_OFFSETOF(SCSIREQASYNC, aSegs[cT2ISegs]));
- if (RT_LIKELY(pReqAsync))
- {
- PSCSIREQ pReq = (PSCSIREQ)RTMemAllocZ(sizeof(SCSIREQ));
- if (pReq)
- {
- uint64_t lba;
- uint16_t tls;
- uint8_t *pbCDB = &pReqAsync->abCDB[0];
- size_t cbCDB;
-
- lba = uOffset / pImage->cbSector;
- tls = (uint16_t)(cbToRead / pImage->cbSector);
-
- cbSegs = pImage->pIfIo->pfnIoCtxSegArrayCreate(pImage->pIfIo->Core.pvUser, pIoCtx,
- &pReqAsync->aSegs[0],
- &cT2ISegs, cbToRead);
- Assert(cbSegs == cbToRead);
- pReqAsync->cT2ISegs = cT2ISegs;
- pReqAsync->pIoCtx = pIoCtx;
- pReqAsync->pScsiReq = pReq;
- pReqAsync->cSenseRetries = 10;
- pReqAsync->rcSense = VERR_READ_ERROR;
-
- if (pImage->cVolume < _4G)
- {
- cbCDB = 10;
- pbCDB[0] = SCSI_READ_10;
- pbCDB[1] = 0; /* reserved */
- pbCDB[2] = (lba >> 24) & 0xff;
- pbCDB[3] = (lba >> 16) & 0xff;
- pbCDB[4] = (lba >> 8) & 0xff;
- pbCDB[5] = lba & 0xff;
- pbCDB[6] = 0; /* reserved */
- pbCDB[7] = (tls >> 8) & 0xff;
- pbCDB[8] = tls & 0xff;
- pbCDB[9] = 0; /* control */
- }
- else
- {
- cbCDB = 16;
- pbCDB[0] = SCSI_READ_16;
- pbCDB[1] = 0; /* reserved */
- pbCDB[2] = (lba >> 56) & 0xff;
- pbCDB[3] = (lba >> 48) & 0xff;
- pbCDB[4] = (lba >> 40) & 0xff;
- pbCDB[5] = (lba >> 32) & 0xff;
- pbCDB[6] = (lba >> 24) & 0xff;
- pbCDB[7] = (lba >> 16) & 0xff;
- pbCDB[8] = (lba >> 8) & 0xff;
- pbCDB[9] = lba & 0xff;
- pbCDB[10] = 0; /* tls unused */
- pbCDB[11] = 0; /* tls unused */
- pbCDB[12] = (tls >> 8) & 0xff;
- pbCDB[13] = tls & 0xff;
- pbCDB[14] = 0; /* reserved */
- pbCDB[15] = 0; /* reserved */
- }
-
- pReq->enmXfer = SCSIXFER_FROM_TARGET;
- pReq->cbCDB = cbCDB;
- pReq->pvCDB = pReqAsync->abCDB;
- pReq->cbI2TData = 0;
- pReq->paI2TSegs = NULL;
- pReq->cI2TSegs = 0;
- pReq->cbT2IData = cbToRead;
- pReq->paT2ISegs = &pReqAsync->aSegs[pReqAsync->cI2TSegs];
- pReq->cT2ISegs = pReqAsync->cT2ISegs;
- pReq->cbSense = sizeof(pReqAsync->abSense);
- pReq->pvSense = pReqAsync->abSense;
-
- rc = iscsiCommandAsync(pImage, pReq, iscsiCommandAsyncComplete, pReqAsync);
- if (RT_FAILURE(rc))
- AssertMsgFailed(("iscsiCommand(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
- else
- {
- *pcbActuallyRead = cbToRead;
- return VERR_VD_IOCTX_HALT; /* Halt the I/O context until further notification from the I/O thread. */
- }
-
- RTMemFree(pReq);
- }
- else
- rc = VERR_NO_MEMORY;
-
- RTMemFree(pReqAsync);
- }
- else
- rc = VERR_NO_MEMORY;
-
- LogFlowFunc(("returns rc=%Rrc\n", rc));
- return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncWrite */
-static int iscsiAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
- PVDIOCTX pIoCtx,
- size_t *pcbWriteProcess, size_t *pcbPreRead,
- size_t *pcbPostRead, unsigned fWrite)
-{
- LogFlowFunc(("pBackendData=%p uOffset=%llu pIoCtx=%#p cbToWrite=%u pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p fWrite=%u\n",
- pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite));
- PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
- int rc = VINF_SUCCESS;
-
- AssertPtr(pImage);
- Assert(uOffset % 512 == 0);
- Assert(cbToWrite % 512 == 0);
-
- if (uOffset + cbToWrite > pImage->cbSize)
- return VERR_INVALID_PARAMETER;
-
- /*
- * Clip read size to a value which is supported by the target.
- */
- cbToWrite = RT_MIN(cbToWrite, pImage->cbSendDataLength);
-
- unsigned cI2TSegs = 0;
- size_t cbSegs = 0;
-
- /* Get the number of segments. */
- cbSegs = pImage->pIfIo->pfnIoCtxSegArrayCreate(pImage->pIfIo->Core.pvUser, pIoCtx,
- NULL, &cI2TSegs, cbToWrite);
- Assert(cbSegs == cbToWrite);
-
- PSCSIREQASYNC pReqAsync = (PSCSIREQASYNC)RTMemAllocZ(RT_OFFSETOF(SCSIREQASYNC, aSegs[cI2TSegs]));
- if (RT_LIKELY(pReqAsync))
- {
- PSCSIREQ pReq = (PSCSIREQ)RTMemAllocZ(sizeof(SCSIREQ));
- if (pReq)
- {
- uint64_t lba;
- uint16_t tls;
- uint8_t *pbCDB = &pReqAsync->abCDB[0];
- size_t cbCDB;
-
- lba = uOffset / pImage->cbSector;
- tls = (uint16_t)(cbToWrite / pImage->cbSector);
-
- cbSegs = pImage->pIfIo->pfnIoCtxSegArrayCreate(pImage->pIfIo->Core.pvUser, pIoCtx,
- &pReqAsync->aSegs[0],
- &cI2TSegs, cbToWrite);
- Assert(cbSegs == cbToWrite);
- pReqAsync->cI2TSegs = cI2TSegs;
- pReqAsync->pIoCtx = pIoCtx;
- pReqAsync->pScsiReq = pReq;
- pReqAsync->cSenseRetries = 10;
- pReqAsync->rcSense = VERR_WRITE_ERROR;
-
- if (pImage->cVolume < _4G)
- {
- cbCDB = 10;
- pbCDB[0] = SCSI_WRITE_10;
- pbCDB[1] = 0; /* reserved */
- pbCDB[2] = (lba >> 24) & 0xff;
- pbCDB[3] = (lba >> 16) & 0xff;
- pbCDB[4] = (lba >> 8) & 0xff;
- pbCDB[5] = lba & 0xff;
- pbCDB[6] = 0; /* reserved */
- pbCDB[7] = (tls >> 8) & 0xff;
- pbCDB[8] = tls & 0xff;
- pbCDB[9] = 0; /* control */
- }
- else
- {
- cbCDB = 16;
- pbCDB[0] = SCSI_WRITE_16;
- pbCDB[1] = 0; /* reserved */
- pbCDB[2] = (lba >> 56) & 0xff;
- pbCDB[3] = (lba >> 48) & 0xff;
- pbCDB[4] = (lba >> 40) & 0xff;
- pbCDB[5] = (lba >> 32) & 0xff;
- pbCDB[6] = (lba >> 24) & 0xff;
- pbCDB[7] = (lba >> 16) & 0xff;
- pbCDB[8] = (lba >> 8) & 0xff;
- pbCDB[9] = lba & 0xff;
- pbCDB[10] = 0; /* tls unused */
- pbCDB[11] = 0; /* tls unused */
- pbCDB[12] = (tls >> 8) & 0xff;
- pbCDB[13] = tls & 0xff;
- pbCDB[14] = 0; /* reserved */
- pbCDB[15] = 0; /* reserved */
- }
-
- pReq->enmXfer = SCSIXFER_TO_TARGET;
- pReq->cbCDB = cbCDB;
- pReq->pvCDB = pReqAsync->abCDB;
- pReq->cbI2TData = cbToWrite;
- pReq->paI2TSegs = &pReqAsync->aSegs[0];
- pReq->cI2TSegs = pReqAsync->cI2TSegs;
- pReq->cbT2IData = 0;
- pReq->paT2ISegs = NULL;
- pReq->cT2ISegs = 0;
- pReq->cbSense = sizeof(pReqAsync->abSense);
- pReq->pvSense = pReqAsync->abSense;
-
- rc = iscsiCommandAsync(pImage, pReq, iscsiCommandAsyncComplete, pReqAsync);
- if (RT_FAILURE(rc))
- AssertMsgFailed(("iscsiCommand(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
- else
- {
- *pcbWriteProcess = cbToWrite;
- return VERR_VD_IOCTX_HALT; /* Halt the I/O context until further notification from the I/O thread. */
- }
-
- RTMemFree(pReq);
- }
- else
- rc = VERR_NO_MEMORY;
-
- RTMemFree(pReqAsync);
- }
- else
- rc = VERR_NO_MEMORY;
-
- LogFlowFunc(("returns rc=%Rrc\n", rc));
- return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncFlush */
-static int iscsiAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
-{
- LogFlowFunc(("pBackendData=%p pIoCtx=%#p\n", pBackendData, pIoCtx));
- PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
- int rc = VINF_SUCCESS;
-
- PSCSIREQASYNC pReqAsync = (PSCSIREQASYNC)RTMemAllocZ(sizeof(SCSIREQASYNC));
- if (RT_LIKELY(pReqAsync))
- {
- PSCSIREQ pReq = (PSCSIREQ)RTMemAllocZ(sizeof(SCSIREQ));
- if (pReq)
- {
- uint8_t *pbCDB = &pReqAsync->abCDB[0];
-
- pReqAsync->pIoCtx = pIoCtx;
- pReqAsync->pScsiReq = pReq;
- pReqAsync->cSenseRetries = 0;
- pReqAsync->rcSense = VINF_SUCCESS;
-
- pbCDB[0] = SCSI_SYNCHRONIZE_CACHE;
- pbCDB[1] = 0; /* reserved */
- pbCDB[2] = 0; /* reserved */
- pbCDB[3] = 0; /* reserved */
- pbCDB[4] = 0; /* reserved */
- pbCDB[5] = 0; /* reserved */
- pbCDB[6] = 0; /* reserved */
- pbCDB[7] = 0; /* reserved */
- pbCDB[8] = 0; /* reserved */
- pbCDB[9] = 0; /* control */
-
- pReq->enmXfer = SCSIXFER_NONE;
- pReq->cbCDB = 10;
- pReq->pvCDB = pReqAsync->abCDB;
- pReq->cbI2TData = 0;
- pReq->paI2TSegs = NULL;
- pReq->cI2TSegs = 0;
- pReq->cbT2IData = 0;
- pReq->paT2ISegs = NULL;
- pReq->cT2ISegs = 0;
- pReq->cbSense = sizeof(pReqAsync->abSense);
- pReq->pvSense = pReqAsync->abSense;
-
- rc = iscsiCommandAsync(pImage, pReq, iscsiCommandAsyncComplete, pReqAsync);
- if (RT_FAILURE(rc))
- AssertMsgFailed(("iscsiCommand(%s) -> %Rrc\n", pImage->pszTargetName, rc));
- else
- return VERR_VD_IOCTX_HALT; /* Halt the I/O context until further notification from the I/O thread. */
-
- RTMemFree(pReq);
- }
- else
- rc = VERR_NO_MEMORY;
-
- RTMemFree(pReqAsync);
- }
- else
- rc = VERR_NO_MEMORY;
-
- LogFlowFunc(("returns rc=%Rrc\n", rc));
- return rc;
-}
-
/** @copydoc VBOXHDDBACKEND::pfnComposeLocation */
static int iscsiComposeLocation(PVDINTERFACE pConfig, char **pszLocation)
{
@@ -5578,8 +5419,12 @@ VBOXHDDBACKEND g_ISCSIBackend =
iscsiWrite,
/* pfnFlush */
iscsiFlush,
+ /* pfnDiscard */
+ NULL,
/* pfnGetVersion */
iscsiGetVersion,
+ /* pfnGetSectorSize */
+ iscsiGetSectorSize,
/* pfnGetSize */
iscsiGetSize,
/* pfnGetFileSize */
@@ -5630,12 +5475,6 @@ VBOXHDDBACKEND g_ISCSIBackend =
NULL,
/* pfnSetParentFilename */
NULL,
- /* pfnAsyncRead */
- iscsiAsyncRead,
- /* pfnAsyncWrite */
- iscsiAsyncWrite,
- /* pfnAsyncFlush */
- iscsiAsyncFlush,
/* pfnComposeLocation */
iscsiComposeLocation,
/* pfnComposeName */
@@ -5644,10 +5483,6 @@ VBOXHDDBACKEND g_ISCSIBackend =
NULL,
/* pfnResize */
NULL,
- /* pfnDiscard */
- NULL,
- /* pfnAsyncDiscard */
- NULL,
/* pfnRepair */
NULL
};
diff --git a/src/VBox/Storage/Makefile.kmk b/src/VBox/Storage/Makefile.kmk
index cac546e5..825482d7 100644
--- a/src/VBox/Storage/Makefile.kmk
+++ b/src/VBox/Storage/Makefile.kmk
@@ -37,6 +37,7 @@ StorageLib_DEFS = IN_VBOXDDU
StorageLib_SOURCES = \
VD.cpp \
VDVfs.cpp \
+ VDIfVfs.cpp \
VDI.cpp \
VMDK.cpp \
VHD.cpp \
diff --git a/src/VBox/Storage/Parallels.cpp b/src/VBox/Storage/Parallels.cpp
index d0393871..622fb835 100644
--- a/src/VBox/Storage/Parallels.cpp
+++ b/src/VBox/Storage/Parallels.cpp
@@ -5,7 +5,7 @@
*/
/*
- * Copyright (C) 2006-2010 Oracle Corporation
+ * Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -127,8 +127,7 @@ static int parallelsFlushImage(PPARALLELSIMAGE pImage)
/* Write the allocation bitmap to the file. */
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
sizeof(ParallelsHeader), pImage->pAllocationBitmap,
- pImage->cAllocationBitmapEntries * sizeof(uint32_t),
- NULL);
+ pImage->cAllocationBitmapEntries * sizeof(uint32_t));
if (RT_FAILURE(rc))
return rc;
}
@@ -158,7 +157,7 @@ static int parallelsFreeImage(PPARALLELSIMAGE pImage, bool fDelete)
if (!fDelete)
parallelsFlushImage(pImage);
- vdIfIoIntFileClose(pImage->pIfIo, pImage->pStorage);
+ rc = vdIfIoIntFileClose(pImage->pIfIo, pImage->pStorage);
pImage->pStorage = NULL;
}
@@ -182,6 +181,7 @@ static int parallelsOpenImage(PPARALLELSIMAGE pImage, unsigned uOpenFlags)
pImage->pIfError = VDIfErrorGet(pImage->pVDIfsDisk);
pImage->pIfIo = VDIfIoIntGet(pImage->pVDIfsImage);
+ pImage->uOpenFlags = uOpenFlags;
AssertPtrReturn(pImage->pIfIo, VERR_INVALID_PARAMETER);
rc = vdIfIoIntFileOpen(pImage->pIfIo, pImage->pszFilename,
@@ -197,7 +197,7 @@ static int parallelsOpenImage(PPARALLELSIMAGE pImage, unsigned uOpenFlags)
AssertMsg(pImage->cbFileCurrent % 512 == 0, ("File size is not a multiple of 512\n"));
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, 0,
- &parallelsHeader, sizeof(parallelsHeader), NULL);
+ &parallelsHeader, sizeof(parallelsHeader));
if (RT_FAILURE(rc))
goto out;
@@ -247,8 +247,7 @@ static int parallelsOpenImage(PPARALLELSIMAGE pImage, unsigned uOpenFlags)
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
sizeof(ParallelsHeader), pImage->pAllocationBitmap,
- pImage->cAllocationBitmapEntries * sizeof(uint32_t),
- NULL);
+ pImage->cAllocationBitmapEntries * sizeof(uint32_t));
if (RT_FAILURE(rc))
goto out;
@@ -341,7 +340,7 @@ static int parallelsCreateImage(PPARALLELSIMAGE pImage, uint64_t cbSize,
rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage, pImage->cbFileCurrent);
if (RT_SUCCESS(rc))
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, 0,
- &Header, sizeof(Header), NULL);
+ &Header, sizeof(Header));
if (RT_SUCCESS(rc))
rc = parallelsFlushImage(pImage); /* Writes the allocation bitmap. */
}
@@ -374,7 +373,7 @@ static int parallelsCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDis
return rc;
rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0, &parallelsHeader,
- sizeof(ParallelsHeader), NULL);
+ sizeof(ParallelsHeader));
if (RT_SUCCESS(rc))
{
if ( !memcmp(parallelsHeader.HeaderIdentifier, PARALLELS_HEADER_MAGIC, 16)
@@ -603,161 +602,144 @@ static int parallelsClose(void *pBackendData, bool fDelete)
}
/** @copydoc VBOXHDDBACKEND::pfnRead */
-static int parallelsRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
- size_t cbToRead, size_t *pcbActuallyRead)
+static int parallelsRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
+ PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
- pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
- PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+ LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
+ pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
int rc = VINF_SUCCESS;
+ PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+ uint64_t uSector;
+ uint64_t uOffsetInFile;
+ uint32_t iIndexInAllocationTable;
AssertPtr(pImage);
Assert(uOffset % 512 == 0);
Assert(cbToRead % 512 == 0);
if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
- rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, uOffset, pvBuf, cbToRead, NULL);
+ rc = vdIfIoIntFileReadUser(pImage->pIfIo, pImage->pStorage, uOffset,
+ pIoCtx, cbToRead);
else
{
- uint64_t uSector;
- uint32_t iIndexInAllocationTable;
-
/* Calculate offset in the real file. */
uSector = uOffset / 512;
-
/* One chunk in the file is always one track big. */
iIndexInAllocationTable = (uint32_t)(uSector / pImage->PCHSGeometry.cSectors);
uSector = uSector % pImage->PCHSGeometry.cSectors;
- Assert(iIndexInAllocationTable < pImage->cAllocationBitmapEntries);
-
cbToRead = RT_MIN(cbToRead, (pImage->PCHSGeometry.cSectors - uSector)*512);
- LogFlowFunc(("AllocationBitmap[%u]=%u uSector=%u cbToRead=%zu cAllocationBitmapEntries=%u\n",
- iIndexInAllocationTable, pImage->pAllocationBitmap[iIndexInAllocationTable],
- uSector, cbToRead, pImage->cAllocationBitmapEntries));
-
if (pImage->pAllocationBitmap[iIndexInAllocationTable] == 0)
rc = VERR_VD_BLOCK_FREE;
else
{
- uint64_t uOffsetInFile = ((uint64_t)pImage->pAllocationBitmap[iIndexInAllocationTable] + uSector) * 512;
-
- LogFlowFunc(("uOffsetInFile=%llu\n", uOffsetInFile));
- rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, uOffsetInFile,
- pvBuf, cbToRead, NULL);
+ uOffsetInFile = (pImage->pAllocationBitmap[iIndexInAllocationTable] + uSector) * 512;
+ rc = vdIfIoIntFileReadUser(pImage->pIfIo, pImage->pStorage, uOffsetInFile,
+ pIoCtx, cbToRead);
}
}
- if ( ( RT_SUCCESS(rc)
- || rc == VERR_VD_BLOCK_FREE)
- && pcbActuallyRead)
- *pcbActuallyRead = cbToRead;
+ *pcbActuallyRead = cbToRead;
LogFlowFunc(("returns %Rrc\n", rc));
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnWrite */
-static int parallelsWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
- size_t cbToWrite, size_t *pcbWriteProcess,
- size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
+static int parallelsWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
+ PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
+ size_t *pcbPostRead, unsigned fWrite)
{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p\n",
- pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess));
- PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+ LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p\n",
+ pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess));
int rc = VINF_SUCCESS;
+ PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+ uint64_t uSector;
+ uint64_t uOffsetInFile;
+ uint32_t iIndexInAllocationTable;
AssertPtr(pImage);
Assert(uOffset % 512 == 0);
Assert(cbToWrite % 512 == 0);
if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, uOffset,
- pvBuf, cbToWrite, NULL);
+ rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage, uOffset,
+ pIoCtx, cbToWrite, NULL, NULL);
else
{
- uint64_t uSector;
- uint64_t uOffsetInFile;
- uint32_t iIndexInAllocationTable;
-
/* Calculate offset in the real file. */
uSector = uOffset / 512;
/* One chunk in the file is always one track big. */
iIndexInAllocationTable = (uint32_t)(uSector / pImage->PCHSGeometry.cSectors);
uSector = uSector % pImage->PCHSGeometry.cSectors;
- Assert(iIndexInAllocationTable < pImage->cAllocationBitmapEntries);
-
cbToWrite = RT_MIN(cbToWrite, (pImage->PCHSGeometry.cSectors - uSector)*512);
- LogFlowFunc(("AllocationBitmap[%u]=%u uSector=%u cbToWrite=%zu cAllocationBitmapEntries=%u\n",
- iIndexInAllocationTable, pImage->pAllocationBitmap[iIndexInAllocationTable],
- uSector, cbToWrite, pImage->cAllocationBitmapEntries));
-
if (pImage->pAllocationBitmap[iIndexInAllocationTable] == 0)
{
- if ( cbToWrite == pImage->PCHSGeometry.cSectors * 512
- && !(fWrite & VD_WRITE_NO_ALLOC))
+ if (fWrite & VD_WRITE_NO_ALLOC)
{
- /* Stay on the safe side. Do not run the risk of confusing the higher
- * level, as that can be pretty lethal to image consistency. */
- *pcbPreRead = 0;
- *pcbPostRead = 0;
-
- /* Allocate new chunk in the file. */
- AssertMsg(pImage->cbFileCurrent % 512 == 0, ("File size is not a multiple of 512\n"));
- pImage->pAllocationBitmap[iIndexInAllocationTable] = (uint32_t)(pImage->cbFileCurrent / 512);
- pImage->cbFileCurrent += pImage->PCHSGeometry.cSectors * 512;
- pImage->fAllocationBitmapChanged = true;
-
- uOffsetInFile = (uint64_t)pImage->pAllocationBitmap[iIndexInAllocationTable] * 512;
-
- LogFlowFunc(("uOffsetInFile=%llu\n", uOffsetInFile));
-
- /*
- * Write the new block at the current end of the file.
- */
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
- uOffsetInFile, pvBuf, cbToWrite, NULL);
+ *pcbPreRead = uSector * 512;
+ *pcbPostRead = pImage->PCHSGeometry.cSectors * 512 - cbToWrite - *pcbPreRead;
+
+ if (pcbWriteProcess)
+ *pcbWriteProcess = cbToWrite;
+ return VERR_VD_BLOCK_FREE;
}
- else
+
+ /* Allocate new chunk in the file. */
+ Assert(uSector == 0);
+ AssertMsg(pImage->cbFileCurrent % 512 == 0, ("File size is not a multiple of 512\n"));
+ pImage->pAllocationBitmap[iIndexInAllocationTable] = (uint32_t)(pImage->cbFileCurrent / 512);
+ pImage->cbFileCurrent += pImage->PCHSGeometry.cSectors * 512;
+ pImage->fAllocationBitmapChanged = true;
+ uOffsetInFile = (uint64_t)pImage->pAllocationBitmap[iIndexInAllocationTable] * 512;
+
+ /*
+ * Write the new block at the current end of the file.
+ */
+ rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
+ uOffsetInFile, pIoCtx, cbToWrite, NULL, NULL);
+ if (RT_SUCCESS(rc) || (rc == VERR_VD_ASYNC_IO_IN_PROGRESS))
{
- /* Trying to do a partial write to an unallocated cluster. Don't do
- * anything except letting the upper layer know what to do. */
- *pcbPreRead = uSector * 512;
- *pcbPostRead = (pImage->PCHSGeometry.cSectors * 512) - cbToWrite - *pcbPreRead;
- rc = VERR_VD_BLOCK_FREE;
+ /* Write the changed allocation bitmap entry. */
+ /** @todo: Error handling. */
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
+ sizeof(ParallelsHeader) + iIndexInAllocationTable * sizeof(uint32_t),
+ &pImage->pAllocationBitmap[iIndexInAllocationTable],
+ sizeof(uint32_t), pIoCtx,
+ NULL, NULL);
}
+
+ *pcbPreRead = 0;
+ *pcbPostRead = 0;
}
else
{
- uOffsetInFile = ((uint64_t)pImage->pAllocationBitmap[iIndexInAllocationTable] + uSector) * 512;
-
- LogFlowFunc(("uOffsetInFile=%llu\n", uOffsetInFile));
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, uOffsetInFile,
- pvBuf, cbToWrite, NULL);
+ uOffsetInFile = (pImage->pAllocationBitmap[iIndexInAllocationTable] + uSector) * 512;
+ rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
+ uOffsetInFile, pIoCtx, cbToWrite, NULL, NULL);
}
}
if (pcbWriteProcess)
*pcbWriteProcess = cbToWrite;
-out:
LogFlowFunc(("returns %Rrc\n", rc));
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnFlush */
-static int parallelsFlush(void *pBackendData)
+static int parallelsFlush(void *pBackendData, PVDIOCTX pIoCtx)
{
- LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+ int rc = VINF_SUCCESS;
PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
- int rc;
- AssertPtr(pImage);
+ LogFlowFunc(("pImage=#%p\n", pImage));
- rc = parallelsFlushImage(pImage);
+ /* Flush the file, everything is up to date already. */
+ rc = vdIfIoIntFileFlush(pImage->pIfIo, pImage->pStorage, pIoCtx, NULL, NULL);
LogFlowFunc(("returns %Rrc\n", rc));
return rc;
@@ -777,6 +759,22 @@ static unsigned parallelsGetVersion(void *pBackendData)
return 0;
}
+/** @copydoc VBOXHDDBACKEND::pfnGetSectorSize */
+static uint32_t parallelsGetSectorSize(void *pBackendData)
+{
+ LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+ PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+ uint32_t cb = 0;
+
+ AssertPtr(pImage);
+
+ if (pImage && pImage->pStorage)
+ cb = 512;
+
+ LogFlowFunc(("returns %llu\n", cb));
+ return cb;
+}
+
/** @copydoc VBOXHDDBACKEND::pfnGetSize */
static uint64_t parallelsGetSize(void *pBackendData)
{
@@ -965,7 +963,9 @@ static int parallelsSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
int rc;
/* Image must be opened and the new flags must be valid. */
- if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_ASYNC_IO)))
+ if (!pImage || (uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
+ | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE
+ | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
{
rc = VERR_INVALID_PARAMETER;
goto out;
@@ -1200,152 +1200,6 @@ static void parallelsDump(void *pBackendData)
}
}
-/** @copydoc VBOXHDDBACKEND::pfnAsyncRead */
-static int parallelsAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
- PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
-{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
- pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
- int rc = VINF_SUCCESS;
- PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
- uint64_t uSector;
- uint64_t uOffsetInFile;
- uint32_t iIndexInAllocationTable;
-
- AssertPtr(pImage);
- Assert(uOffset % 512 == 0);
- Assert(cbToRead % 512 == 0);
-
- if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
- rc = vdIfIoIntFileReadUserAsync(pImage->pIfIo, pImage->pStorage, uOffset,
- pIoCtx, cbToRead);
- else
- {
- /* Calculate offset in the real file. */
- uSector = uOffset / 512;
- /* One chunk in the file is always one track big. */
- iIndexInAllocationTable = (uint32_t)(uSector / pImage->PCHSGeometry.cSectors);
- uSector = uSector % pImage->PCHSGeometry.cSectors;
-
- cbToRead = RT_MIN(cbToRead, (pImage->PCHSGeometry.cSectors - uSector)*512);
-
- if (pImage->pAllocationBitmap[iIndexInAllocationTable] == 0)
- {
- rc = VERR_VD_BLOCK_FREE;
- }
- else
- {
- uOffsetInFile = (pImage->pAllocationBitmap[iIndexInAllocationTable] + uSector) * 512;
- rc = vdIfIoIntFileReadUserAsync(pImage->pIfIo, pImage->pStorage, uOffsetInFile,
- pIoCtx, cbToRead);
- }
- }
-
- *pcbActuallyRead = cbToRead;
-
- LogFlowFunc(("returns %Rrc\n", rc));
- return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncWrite */
-static int parallelsAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
- PVDIOCTX pIoCtx,
- size_t *pcbWriteProcess, size_t *pcbPreRead,
- size_t *pcbPostRead, unsigned fWrite)
-{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p\n",
- pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess));
- int rc = VINF_SUCCESS;
- PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
- uint64_t uSector;
- uint64_t uOffsetInFile;
- uint32_t iIndexInAllocationTable;
-
- AssertPtr(pImage);
- Assert(uOffset % 512 == 0);
- Assert(cbToWrite % 512 == 0);
-
- if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
- rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pImage->pStorage, uOffset,
- pIoCtx, cbToWrite, NULL, NULL);
- else
- {
- /* Calculate offset in the real file. */
- uSector = uOffset / 512;
- /* One chunk in the file is always one track big. */
- iIndexInAllocationTable = (uint32_t)(uSector / pImage->PCHSGeometry.cSectors);
- uSector = uSector % pImage->PCHSGeometry.cSectors;
-
- cbToWrite = RT_MIN(cbToWrite, (pImage->PCHSGeometry.cSectors - uSector)*512);
-
- if (pImage->pAllocationBitmap[iIndexInAllocationTable] == 0)
- {
- if (fWrite & VD_WRITE_NO_ALLOC)
- {
- *pcbPreRead = uSector * 512;
- *pcbPostRead = pImage->PCHSGeometry.cSectors * 512 - cbToWrite - *pcbPreRead;
-
- if (pcbWriteProcess)
- *pcbWriteProcess = cbToWrite;
- return VERR_VD_BLOCK_FREE;
- }
-
- /* Allocate new chunk in the file. */
- Assert(uSector == 0);
- AssertMsg(pImage->cbFileCurrent % 512 == 0, ("File size is not a multiple of 512\n"));
- pImage->pAllocationBitmap[iIndexInAllocationTable] = (uint32_t)(pImage->cbFileCurrent / 512);
- pImage->cbFileCurrent += pImage->PCHSGeometry.cSectors * 512;
- pImage->fAllocationBitmapChanged = true;
- uOffsetInFile = (uint64_t)pImage->pAllocationBitmap[iIndexInAllocationTable] * 512;
-
- /*
- * Write the new block at the current end of the file.
- */
- rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pImage->pStorage,
- uOffsetInFile, pIoCtx, cbToWrite, NULL, NULL);
- if (RT_SUCCESS(rc) || (rc == VERR_VD_ASYNC_IO_IN_PROGRESS))
- {
- /* Write the changed allocation bitmap entry. */
- /** @todo: Error handling. */
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
- sizeof(ParallelsHeader) + iIndexInAllocationTable * sizeof(uint32_t),
- &pImage->pAllocationBitmap[iIndexInAllocationTable],
- sizeof(uint32_t), pIoCtx,
- NULL, NULL);
- }
-
- *pcbPreRead = 0;
- *pcbPostRead = 0;
- }
- else
- {
- uOffsetInFile = (pImage->pAllocationBitmap[iIndexInAllocationTable] + uSector) * 512;
- rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pImage->pStorage,
- uOffsetInFile, pIoCtx, cbToWrite, NULL, NULL);
- }
- }
-
- if (pcbWriteProcess)
- *pcbWriteProcess = cbToWrite;
-
- LogFlowFunc(("returns %Rrc\n", rc));
- return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncFlush */
-static int parallelsAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
-{
- int rc = VINF_SUCCESS;
- PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-
- LogFlowFunc(("pImage=#%p\n", pImage));
-
- /* Flush the file, everything is up to date already. */
- rc = vdIfIoIntFileFlushAsync(pImage->pIfIo, pImage->pStorage, pIoCtx, NULL, NULL);
-
- LogFlowFunc(("returns %Rrc\n", rc));
- return rc;
-}
VBOXHDDBACKEND g_ParallelsBackend =
@@ -1378,8 +1232,12 @@ VBOXHDDBACKEND g_ParallelsBackend =
parallelsWrite,
/* pfnFlush */
parallelsFlush,
+ /* pfnDiscard */
+ NULL,
/* pfnGetVersion */
parallelsGetVersion,
+ /* pfnGetSectorSize */
+ parallelsGetSectorSize,
/* pfnGetSize */
parallelsGetSize,
/* pfnGetFileSize */
@@ -1430,12 +1288,6 @@ VBOXHDDBACKEND g_ParallelsBackend =
NULL,
/* pfnSetParentFilename */
NULL,
- /* pfnAsyncRead */
- parallelsAsyncRead,
- /* pfnAsyncWrite */
- parallelsAsyncWrite,
- /* pfnAsyncFlush */
- parallelsAsyncFlush,
/* pfnComposeLocation */
genericFileComposeLocation,
/* pfnComposeName */
@@ -1443,5 +1295,7 @@ VBOXHDDBACKEND g_ParallelsBackend =
/* pfnCompact */
NULL,
/* pfnResize */
+ NULL,
+ /* pfnRepair */
NULL
};
diff --git a/src/VBox/Storage/QCOW.cpp b/src/VBox/Storage/QCOW.cpp
index 9572e1dc..c90d1558 100644
--- a/src/VBox/Storage/QCOW.cpp
+++ b/src/VBox/Storage/QCOW.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2011 Oracle Corporation
+ * Copyright (C) 2011-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -683,64 +683,12 @@ static void qcowL2TblCacheEntryInsert(PQCOWIMAGE pImage, PQCOWL2CACHEENTRY pL2En
*
* @returns VBox status code.
* @param pImage Image instance data.
- * @param offL2Tbl The offset of the L2 table in the image.
- * @param ppL2Entry Where to store the L2 table on success.
- */
-static int qcowL2TblCacheFetch(PQCOWIMAGE pImage, uint64_t offL2Tbl, PQCOWL2CACHEENTRY *ppL2Entry)
-{
- int rc = VINF_SUCCESS;
-
- LogFlowFunc(("pImage=%#p offL2Tbl=%llu ppL2Entry=%#p\n", pImage, offL2Tbl, ppL2Entry));
-
- /* Try to fetch the L2 table from the cache first. */
- PQCOWL2CACHEENTRY pL2Entry = qcowL2TblCacheRetain(pImage, offL2Tbl);
- if (!pL2Entry)
- {
- LogFlowFunc(("Reading L2 table from image\n"));
- pL2Entry = qcowL2TblCacheEntryAlloc(pImage);
-
- if (pL2Entry)
- {
- /* Read from the image. */
- pL2Entry->offL2Tbl = offL2Tbl;
- rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, offL2Tbl,
- pL2Entry->paL2Tbl, pImage->cbL2Table, NULL);
- if (RT_SUCCESS(rc))
- {
-#if defined(RT_LITTLE_ENDIAN)
- qcowTableConvertToHostEndianess(pL2Entry->paL2Tbl, pImage->cL2TableEntries);
-#endif
- qcowL2TblCacheEntryInsert(pImage, pL2Entry);
- }
- else
- {
- qcowL2TblCacheEntryRelease(pL2Entry);
- qcowL2TblCacheEntryFree(pImage, pL2Entry);
- }
- }
- else
- rc = VERR_NO_MEMORY;
- }
-
- if (RT_SUCCESS(rc))
- *ppL2Entry = pL2Entry;
-
- LogFlowFunc(("returns rc=%Rrc\n", rc));
- return rc;
-}
-
-/**
- * Fetches the L2 from the given offset trying the LRU cache first and
- * reading it from the image after a cache miss - version for async I/O.
- *
- * @returns VBox status code.
- * @param pImage Image instance data.
* @param pIoCtx The I/O context.
* @param offL2Tbl The offset of the L2 table in the image.
* @param ppL2Entry Where to store the L2 table on success.
*/
-static int qcowL2TblCacheFetchAsync(PQCOWIMAGE pImage, PVDIOCTX pIoCtx,
- uint64_t offL2Tbl, PQCOWL2CACHEENTRY *ppL2Entry)
+static int qcowL2TblCacheFetch(PQCOWIMAGE pImage, PVDIOCTX pIoCtx, uint64_t offL2Tbl,
+ PQCOWL2CACHEENTRY *ppL2Entry)
{
int rc = VINF_SUCCESS;
@@ -756,10 +704,10 @@ static int qcowL2TblCacheFetchAsync(PQCOWIMAGE pImage, PVDIOCTX pIoCtx,
PVDMETAXFER pMetaXfer;
pL2Entry->offL2Tbl = offL2Tbl;
- rc = vdIfIoIntFileReadMetaAsync(pImage->pIfIo, pImage->pStorage,
- offL2Tbl, pL2Entry->paL2Tbl,
- pImage->cbL2Table, pIoCtx,
- &pMetaXfer, NULL, NULL);
+ rc = vdIfIoIntFileReadMeta(pImage->pIfIo, pImage->pStorage,
+ offL2Tbl, pL2Entry->paL2Tbl,
+ pImage->cbL2Table, pIoCtx,
+ &pMetaXfer, NULL, NULL);
if (RT_SUCCESS(rc))
{
vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
@@ -875,79 +823,15 @@ DECLINLINE(uint64_t) qcowClusterAllocate(PQCOWIMAGE pImage, uint32_t cClusters)
* @returns VBox status code.
* VERR_VD_BLOCK_FREE if the cluster is not yet allocated.
* @param pImage The image instance data.
- * @param idxL1 The L1 index.
- * @param idxL2 The L2 index.
- * @param offCluster Offset inside the cluster.
- * @param poffImage Where to store the image offset on success;
- */
-static int qcowConvertToImageOffset(PQCOWIMAGE pImage, uint32_t idxL1, uint32_t idxL2,
- uint32_t offCluster, uint64_t *poffImage)
-{
- int rc = VERR_VD_BLOCK_FREE;
- LogFlowFunc(("pImage=%#p idxL1=%u idxL2=%u offCluster=%u poffImage=%#p\n",
- pImage, idxL1, idxL2, offCluster, poffImage));
-
- AssertReturn(idxL1 < pImage->cL1TableEntries, VERR_INVALID_PARAMETER);
- AssertReturn(idxL2 < pImage->cL2TableEntries, VERR_INVALID_PARAMETER);
-
- if (pImage->paL1Table[idxL1])
- {
- PQCOWL2CACHEENTRY pL2Entry;
-
- rc = qcowL2TblCacheFetch(pImage, pImage->paL1Table[idxL1], &pL2Entry);
- if (RT_SUCCESS(rc))
- {
- LogFlowFunc(("cluster start offset %llu\n", pL2Entry->paL2Tbl[idxL2]));
- /* Get real file offset. */
- if (pL2Entry->paL2Tbl[idxL2])
- {
- uint64_t off = pL2Entry->paL2Tbl[idxL2];
-
- /* Strip flags */
- if (pImage->uVersion == 2)
- {
- if (RT_UNLIKELY(off & QCOW_V2_COMPRESSED_FLAG))
- rc = VERR_NOT_SUPPORTED;
- else
- off &= ~(QCOW_V2_COMPRESSED_FLAG | QCOW_V2_COPIED_FLAG);
- }
- else
- {
- if (RT_UNLIKELY(off & QCOW_V1_COMPRESSED_FLAG))
- rc = VERR_NOT_SUPPORTED;
- else
- off &= ~QCOW_V1_COMPRESSED_FLAG;
- }
-
- *poffImage = off + offCluster;
- }
- else
- rc = VERR_VD_BLOCK_FREE;
-
- qcowL2TblCacheEntryRelease(pL2Entry);
- }
- }
-
- LogFlowFunc(("returns rc=%Rrc\n", rc));
- return rc;
-}
-
-/**
- * Returns the real image offset for a given cluster or an error if the cluster is not
- * yet allocated- version for async I/O.
- *
- * @returns VBox status code.
- * VERR_VD_BLOCK_FREE if the cluster is not yet allocated.
- * @param pImage The image instance data.
* @param pIoCtx The I/O context.
* @param idxL1 The L1 index.
* @param idxL2 The L2 index.
* @param offCluster Offset inside the cluster.
* @param poffImage Where to store the image offset on success;
*/
-static int qcowConvertToImageOffsetAsync(PQCOWIMAGE pImage, PVDIOCTX pIoCtx,
- uint32_t idxL1, uint32_t idxL2,
- uint32_t offCluster, uint64_t *poffImage)
+static int qcowConvertToImageOffset(PQCOWIMAGE pImage, PVDIOCTX pIoCtx,
+ uint32_t idxL1, uint32_t idxL2,
+ uint32_t offCluster, uint64_t *poffImage)
{
int rc = VERR_VD_BLOCK_FREE;
@@ -958,8 +842,7 @@ static int qcowConvertToImageOffsetAsync(PQCOWIMAGE pImage, PVDIOCTX pIoCtx,
{
PQCOWL2CACHEENTRY pL2Entry;
- rc = qcowL2TblCacheFetchAsync(pImage, pIoCtx, pImage->paL1Table[idxL1],
- &pL2Entry);
+ rc = qcowL2TblCacheFetch(pImage, pIoCtx, pImage->paL1Table[idxL1], &pL2Entry);
if (RT_SUCCESS(rc))
{
/* Get real file offset. */
@@ -1017,7 +900,7 @@ static int qcowFlushImage(PQCOWIMAGE pImage)
pImage->cL1TableEntries);
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
pImage->offL1Table, paL1TblImg,
- pImage->cbL1Table, NULL);
+ pImage->cbL1Table);
RTMemFree(paL1TblImg);
}
else
@@ -1025,7 +908,7 @@ static int qcowFlushImage(PQCOWIMAGE pImage)
#else
/* Write L1 table directly. */
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, pImage->offL1Table,
- pImage->paL1Table, pImage->cbL1Table, NULL);
+ pImage->paL1Table, pImage->cbL1Table);
#endif
if (RT_SUCCESS(rc))
{
@@ -1033,7 +916,7 @@ static int qcowFlushImage(PQCOWIMAGE pImage)
size_t cbHeader = 0;
qcowHdrConvertFromHostEndianess(pImage, &Header, &cbHeader);
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, 0, &Header,
- cbHeader, NULL);
+ cbHeader);
if (RT_SUCCESS(rc))
rc = vdIfIoIntFileFlushSync(pImage->pIfIo, pImage->pStorage);
}
@@ -1064,30 +947,30 @@ static int qcowFlushImageAsync(PQCOWIMAGE pImage, PVDIOCTX pIoCtx)
{
qcowTableConvertFromHostEndianess(paL1TblImg, pImage->paL1Table,
pImage->cL1TableEntries);
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
- pImage->offL1Table, paL1TblImg,
- pImage->cbL1Table, pIoCtx, NULL, NULL);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
+ pImage->offL1Table, paL1TblImg,
+ pImage->cbL1Table, pIoCtx, NULL, NULL);
RTMemFree(paL1TblImg);
}
else
rc = VERR_NO_MEMORY;
#else
/* Write L1 table directly. */
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
- pImage->offL1Table, pImage->paL1Table,
- pImage->cbL1Table, pIoCtx, NULL, NULL);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
+ pImage->offL1Table, pImage->paL1Table,
+ pImage->cbL1Table, pIoCtx, NULL, NULL);
#endif
if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
{
/* Write header. */
size_t cbHeader = 0;
qcowHdrConvertFromHostEndianess(pImage, &Header, &cbHeader);
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
- 0, &Header, cbHeader,
- pIoCtx, NULL, NULL);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
+ 0, &Header, cbHeader,
+ pIoCtx, NULL, NULL);
if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
- rc = vdIfIoIntFileFlushAsync(pImage->pIfIo, pImage->pStorage,
- pIoCtx, NULL, NULL);
+ rc = vdIfIoIntFileFlush(pImage->pIfIo, pImage->pStorage,
+ pIoCtx, NULL, NULL);
}
}
@@ -1112,7 +995,7 @@ static int qcowFreeImage(PQCOWIMAGE pImage, bool fDelete)
if (!fDelete)
qcowFlushImage(pImage);
- vdIfIoIntFileClose(pImage->pIfIo, pImage->pStorage);
+ rc = vdIfIoIntFileClose(pImage->pIfIo, pImage->pStorage);
pImage->pStorage = NULL;
}
@@ -1120,7 +1003,10 @@ static int qcowFreeImage(PQCOWIMAGE pImage, bool fDelete)
RTMemFree(pImage->paL1Table);
if (pImage->pszBackingFilename)
+ {
RTMemFree(pImage->pszBackingFilename);
+ pImage->pszBackingFilename = NULL;
+ }
qcowL2TblCacheDestroy(pImage);
@@ -1166,7 +1052,7 @@ static int qcowOpenImage(PQCOWIMAGE pImage, unsigned uOpenFlags)
goto out;
if (cbFile > sizeof(Header))
{
- rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, 0, &Header, sizeof(Header), NULL);
+ rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, 0, &Header, sizeof(Header));
if ( RT_SUCCESS(rc)
&& qcowHdrConvertToHostEndianess(&Header))
{
@@ -1241,12 +1127,12 @@ static int qcowOpenImage(PQCOWIMAGE pImage, unsigned uOpenFlags)
&& pImage->offBackingFilename)
{
/* Load backing filename from image. */
- pImage->pszFilename = (char *)RTMemAllocZ(pImage->cbBackingFilename + 1); /* +1 for \0 terminator. */
- if (pImage->pszFilename)
+ pImage->pszBackingFilename = (char *)RTMemAllocZ(pImage->cbBackingFilename + 1); /* +1 for \0 terminator. */
+ if (pImage->pszBackingFilename)
{
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
pImage->offBackingFilename, pImage->pszBackingFilename,
- pImage->cbBackingFilename, NULL);
+ pImage->cbBackingFilename);
}
else
rc = VERR_NO_MEMORY;
@@ -1263,7 +1149,7 @@ static int qcowOpenImage(PQCOWIMAGE pImage, unsigned uOpenFlags)
{
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
pImage->offRefcountTable, pImage->paRefcountTable,
- pImage->cbRefcountTable, NULL);
+ pImage->cbRefcountTable);
if (RT_SUCCESS(rc))
qcowTableConvertToHostEndianess(pImage->paRefcountTable,
pImage->cRefcountTableEntries);
@@ -1289,7 +1175,7 @@ static int qcowOpenImage(PQCOWIMAGE pImage, unsigned uOpenFlags)
/* Read from the image. */
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
pImage->offL1Table, pImage->paL1Table,
- pImage->cbL1Table, NULL);
+ pImage->cbL1Table);
if (RT_SUCCESS(rc))
qcowTableConvertToHostEndianess(pImage->paL1Table, pImage->cL1TableEntries);
else
@@ -1468,10 +1354,10 @@ static DECLCALLBACK(int) qcowAsyncClusterAllocUpdate(void *pBackendData, PVDIOCT
/* Update the link in the on disk L1 table now. */
pClusterAlloc->enmAllocState = QCOWCLUSTERASYNCALLOCSTATE_L2_LINK;
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
- pImage->offL1Table + pClusterAlloc->idxL1*sizeof(uint64_t),
- &offUpdateLe, sizeof(uint64_t), pIoCtx,
- qcowAsyncClusterAllocUpdate, pClusterAlloc);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
+ pImage->offL1Table + pClusterAlloc->idxL1*sizeof(uint64_t),
+ &offUpdateLe, sizeof(uint64_t), pIoCtx,
+ qcowAsyncClusterAllocUpdate, pClusterAlloc);
if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
break;
else if (RT_FAILURE(rc))
@@ -1496,9 +1382,9 @@ static DECLCALLBACK(int) qcowAsyncClusterAllocUpdate(void *pBackendData, PVDIOCT
pClusterAlloc->offClusterNew = offData;
/* Write data. */
- rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pImage->pStorage,
- offData, pIoCtx, pClusterAlloc->cbToWrite,
- qcowAsyncClusterAllocUpdate, pClusterAlloc);
+ rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
+ offData, pIoCtx, pClusterAlloc->cbToWrite,
+ qcowAsyncClusterAllocUpdate, pClusterAlloc);
if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
break;
else if (RT_FAILURE(rc))
@@ -1515,10 +1401,10 @@ static DECLCALLBACK(int) qcowAsyncClusterAllocUpdate(void *pBackendData, PVDIOCT
pClusterAlloc->enmAllocState = QCOWCLUSTERASYNCALLOCSTATE_USER_LINK;
/* Link L2 table and update it. */
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
- pImage->paL1Table[pClusterAlloc->idxL1] + pClusterAlloc->idxL2*sizeof(uint64_t),
- &offUpdateLe, sizeof(uint64_t), pIoCtx,
- qcowAsyncClusterAllocUpdate, pClusterAlloc);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
+ pImage->paL1Table[pClusterAlloc->idxL1] + pClusterAlloc->idxL2*sizeof(uint64_t),
+ &offUpdateLe, sizeof(uint64_t), pIoCtx,
+ qcowAsyncClusterAllocUpdate, pClusterAlloc);
if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
break;
else if (RT_FAILURE(rc))
@@ -1580,7 +1466,7 @@ static int qcowCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
{
QCowHeader Header;
- rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0, &Header, sizeof(Header), NULL);
+ rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0, &Header, sizeof(Header));
if ( RT_SUCCESS(rc)
&& qcowHdrConvertToHostEndianess(&Header))
{
@@ -1786,12 +1672,11 @@ static int qcowClose(void *pBackendData, bool fDelete)
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnRead */
-static int qcowRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
- size_t cbToRead, size_t *pcbActuallyRead)
+static int qcowRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
+ PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
- pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
+ LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
+ pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
PQCOWIMAGE pImage = (PQCOWIMAGE)pBackendData;
uint32_t offCluster = 0;
uint32_t idxL1 = 0;
@@ -1803,6 +1688,12 @@ static int qcowRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
Assert(uOffset % 512 == 0);
Assert(cbToRead % 512 == 0);
+ if (!VALID_PTR(pIoCtx) || !cbToRead)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+
if ( uOffset + cbToRead > pImage->cbSize
|| cbToRead == 0)
{
@@ -1811,21 +1702,19 @@ static int qcowRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
}
qcowConvertLogicalOffset(pImage, uOffset, &idxL1, &idxL2, &offCluster);
- LogFlowFunc(("idxL1=%u idxL2=%u offCluster=%u\n", idxL1, idxL2, offCluster));
/* Clip read size to remain in the cluster. */
cbToRead = RT_MIN(cbToRead, pImage->cbCluster - offCluster);
/* Get offset in image. */
- rc = qcowConvertToImageOffset(pImage, idxL1, idxL2, offCluster, &offFile);
+ rc = qcowConvertToImageOffset(pImage, pIoCtx, idxL1, idxL2, offCluster, &offFile);
if (RT_SUCCESS(rc))
- {
- LogFlowFunc(("offFile=%llu\n", offFile));
- rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, offFile,
- pvBuf, cbToRead, NULL);
- }
+ rc = vdIfIoIntFileReadUser(pImage->pIfIo, pImage->pStorage, offFile,
+ pIoCtx, cbToRead);
- if ( (RT_SUCCESS(rc) || rc == VERR_VD_BLOCK_FREE)
+ if ( ( RT_SUCCESS(rc)
+ || rc == VERR_VD_BLOCK_FREE
+ || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
&& pcbActuallyRead)
*pcbActuallyRead = cbToRead;
@@ -1834,23 +1723,22 @@ out:
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnWrite */
-static int qcowWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
- size_t cbToWrite, size_t *pcbWriteProcess,
- size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
+static int qcowWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
+ PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
+ size_t *pcbPostRead, unsigned fWrite)
{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
- pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
+ LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
+ pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
PQCOWIMAGE pImage = (PQCOWIMAGE)pBackendData;
uint32_t offCluster = 0;
uint32_t idxL1 = 0;
uint32_t idxL2 = 0;
uint64_t offImage = 0;
- int rc;
+ int rc = VINF_SUCCESS;
AssertPtr(pImage);
- Assert(uOffset % 512 == 0);
- Assert(cbToWrite % 512 == 0);
+ Assert(!(uOffset % 512));
+ Assert(!(cbToWrite % 512));
if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
{
@@ -1858,6 +1746,12 @@ static int qcowWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
goto out;
}
+ if (!VALID_PTR(pIoCtx) || !cbToWrite)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+
if ( uOffset + cbToWrite > pImage->cbSize
|| cbToWrite == 0)
{
@@ -1873,10 +1767,10 @@ static int qcowWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
Assert(!(cbToWrite % 512));
/* Get offset in image. */
- rc = qcowConvertToImageOffset(pImage, idxL1, idxL2, offCluster, &offImage);
+ rc = qcowConvertToImageOffset(pImage, pIoCtx, idxL1, idxL2, offCluster, &offImage);
if (RT_SUCCESS(rc))
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, offImage,
- pvBuf, cbToWrite, NULL);
+ rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
+ offImage, pIoCtx, cbToWrite, NULL, NULL);
else if (rc == VERR_VD_BLOCK_FREE)
{
if ( cbToWrite == pImage->cbCluster
@@ -1895,59 +1789,97 @@ static int qcowWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
/* Check if we have to allocate a new cluster for L2 tables. */
if (!pImage->paL1Table[idxL1])
{
- uint64_t offL2Tbl = qcowClusterAllocate(pImage, qcowByte2Cluster(pImage, pImage->cbL2Table));
+ uint64_t offL2Tbl;
+ PQCOWCLUSTERASYNCALLOC pL2ClusterAlloc = NULL;
+
+ /* Allocate new async cluster allocation state. */
+ pL2ClusterAlloc = (PQCOWCLUSTERASYNCALLOC)RTMemAllocZ(sizeof(QCOWCLUSTERASYNCALLOC));
+ if (RT_UNLIKELY(!pL2ClusterAlloc))
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
pL2Entry = qcowL2TblCacheEntryAlloc(pImage);
if (!pL2Entry)
{
rc = VERR_NO_MEMORY;
+ RTMemFree(pL2ClusterAlloc);
break;
}
+ offL2Tbl = qcowClusterAllocate(pImage, qcowByte2Cluster(pImage, pImage->cbL2Table));
pL2Entry->offL2Tbl = offL2Tbl;
memset(pL2Entry->paL2Tbl, 0, pImage->cbL2Table);
- qcowL2TblCacheEntryInsert(pImage, pL2Entry);
+
+ pL2ClusterAlloc->enmAllocState = QCOWCLUSTERASYNCALLOCSTATE_L2_ALLOC;
+ pL2ClusterAlloc->offNextClusterOld = offL2Tbl;
+ pL2ClusterAlloc->offClusterNew = offL2Tbl;
+ pL2ClusterAlloc->idxL1 = idxL1;
+ pL2ClusterAlloc->idxL2 = idxL2;
+ pL2ClusterAlloc->cbToWrite = cbToWrite;
+ pL2ClusterAlloc->pL2Entry = pL2Entry;
/*
* Write the L2 table first and link to the L1 table afterwards.
* If something unexpected happens the worst case which can happen
* is a leak of some clusters.
*/
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, offL2Tbl,
- pL2Entry->paL2Tbl, pImage->cbL2Table, NULL);
- if (RT_FAILURE(rc))
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
+ offL2Tbl, pL2Entry->paL2Tbl, pImage->cbL2Table, pIoCtx,
+ qcowAsyncClusterAllocUpdate, pL2ClusterAlloc);
+ if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
break;
-
- /* Write the L1 link now. */
- pImage->paL1Table[idxL1] = offL2Tbl;
- idxUpdateLe = RT_H2BE_U64(offL2Tbl);
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
- pImage->offL1Table + idxL1*sizeof(uint64_t),
- &idxUpdateLe, sizeof(uint64_t), NULL);
- if (RT_FAILURE(rc))
+ else if (RT_FAILURE(rc))
+ {
+ RTMemFree(pL2ClusterAlloc);
+ qcowL2TblCacheEntryFree(pImage, pL2Entry);
break;
+ }
+
+ rc = qcowAsyncClusterAllocUpdate(pImage, pIoCtx, pL2ClusterAlloc, rc);
}
else
- rc = qcowL2TblCacheFetch(pImage, pImage->paL1Table[idxL1], &pL2Entry);
-
- if (RT_SUCCESS(rc))
{
- /* Allocate new cluster for the data. */
- uint64_t offData = qcowClusterAllocate(pImage, 1);
+ rc = qcowL2TblCacheFetch(pImage, pIoCtx, pImage->paL1Table[idxL1],
+ &pL2Entry);
+ if (RT_SUCCESS(rc))
+ {
+ PQCOWCLUSTERASYNCALLOC pDataClusterAlloc = NULL;
- /* Write data. */
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
- offData, pvBuf, cbToWrite, NULL);
- if (RT_FAILURE(rc))
- break;
+ /* Allocate new async cluster allocation state. */
+ pDataClusterAlloc = (PQCOWCLUSTERASYNCALLOC)RTMemAllocZ(sizeof(QCOWCLUSTERASYNCALLOC));
+ if (RT_UNLIKELY(!pDataClusterAlloc))
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
- /* Link L2 table and update it. */
- pL2Entry->paL2Tbl[idxL2] = offData;
- idxUpdateLe = RT_H2BE_U64(offData);
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
- pImage->paL1Table[idxL1] + idxL2*sizeof(uint64_t),
- &idxUpdateLe, sizeof(uint64_t), NULL);
- qcowL2TblCacheEntryRelease(pL2Entry);
+ /* Allocate new cluster for the data. */
+ uint64_t offData = qcowClusterAllocate(pImage, 1);
+
+ pDataClusterAlloc->enmAllocState = QCOWCLUSTERASYNCALLOCSTATE_USER_ALLOC;
+ pDataClusterAlloc->offNextClusterOld = offData;
+ pDataClusterAlloc->offClusterNew = offData;
+ pDataClusterAlloc->idxL1 = idxL1;
+ pDataClusterAlloc->idxL2 = idxL2;
+ pDataClusterAlloc->cbToWrite = cbToWrite;
+ pDataClusterAlloc->pL2Entry = pL2Entry;
+
+ /* Write data. */
+ rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
+ offData, pIoCtx, cbToWrite,
+ qcowAsyncClusterAllocUpdate, pDataClusterAlloc);
+ if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+ break;
+ else if (RT_FAILURE(rc))
+ {
+ RTMemFree(pDataClusterAlloc);
+ break;
+ }
+
+ rc = qcowAsyncClusterAllocUpdate(pImage, pIoCtx, pDataClusterAlloc, rc);
+ }
}
} while (0);
@@ -1967,19 +1899,25 @@ static int qcowWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
if (pcbWriteProcess)
*pcbWriteProcess = cbToWrite;
+
out:
LogFlowFunc(("returns %Rrc\n", rc));
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnFlush */
-static int qcowFlush(void *pBackendData)
+static int qcowFlush(void *pBackendData, PVDIOCTX pIoCtx)
{
LogFlowFunc(("pBackendData=%#p\n", pBackendData));
PQCOWIMAGE pImage = (PQCOWIMAGE)pBackendData;
- int rc;
+ int rc = VINF_SUCCESS;
+
+ Assert(pImage);
+
+ if (VALID_PTR(pIoCtx))
+ rc = qcowFlushImageAsync(pImage, pIoCtx);
+ else
+ rc = VERR_INVALID_PARAMETER;
- rc = qcowFlushImage(pImage);
LogFlowFunc(("returns %Rrc\n", rc));
return rc;
}
@@ -1998,6 +1936,22 @@ static unsigned qcowGetVersion(void *pBackendData)
return 0;
}
+/** @copydoc VBOXHDDBACKEND::pfnGetSectorSize */
+static uint32_t qcowGetSectorSize(void *pBackendData)
+{
+ LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+ PQCOWIMAGE pImage = (PQCOWIMAGE)pBackendData;
+ uint32_t cb = 0;
+
+ AssertPtr(pImage);
+
+ if (pImage && pImage->pStorage)
+ cb = 512;
+
+ LogFlowFunc(("returns %u\n", cb));
+ return cb;
+}
+
/** @copydoc VBOXHDDBACKEND::pfnGetSize */
static uint64_t qcowGetSize(void *pBackendData)
{
@@ -2194,7 +2148,8 @@ static int qcowSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
int rc;
/* Image must be opened and the new flags must be valid. */
- if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO)))
+ if (!pImage || (uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
+ | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
{
rc = VERR_INVALID_PARAMETER;
goto out;
@@ -2441,7 +2396,7 @@ static int qcowGetParentFilename(void *pBackendData, char **ppszParentFilename)
AssertPtr(pImage);
if (pImage)
- if (pImage->pszFilename)
+ if (pImage->pszBackingFilename)
*ppszParentFilename = RTStrDup(pImage->pszBackingFilename);
else
rc = VERR_NOT_SUPPORTED;
@@ -2482,7 +2437,7 @@ static int qcowSetParentFilename(void *pBackendData, const char *pszParentFilena
Assert((offData & UINT32_MAX) == offData);
pImage->offBackingFilename = (uint32_t)offData;
- pImage->cbBackingFilename = strlen(pszParentFilename);
+ pImage->cbBackingFilename = (uint32_t)strlen(pszParentFilename);
rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage,
offData + pImage->cbCluster);
}
@@ -2491,8 +2446,7 @@ static int qcowSetParentFilename(void *pBackendData, const char *pszParentFilena
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
pImage->offBackingFilename,
pImage->pszBackingFilename,
- strlen(pImage->pszBackingFilename),
- NULL);
+ strlen(pImage->pszBackingFilename));
}
}
}
@@ -2503,259 +2457,7 @@ static int qcowSetParentFilename(void *pBackendData, const char *pszParentFilena
return rc;
}
-static int qcowAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
- PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
-{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
- pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
- PQCOWIMAGE pImage = (PQCOWIMAGE)pBackendData;
- uint32_t offCluster = 0;
- uint32_t idxL1 = 0;
- uint32_t idxL2 = 0;
- uint64_t offFile = 0;
- int rc;
-
- AssertPtr(pImage);
- Assert(uOffset % 512 == 0);
- Assert(cbToRead % 512 == 0);
-
- if (!VALID_PTR(pIoCtx) || !cbToRead)
- {
- rc = VERR_INVALID_PARAMETER;
- goto out;
- }
-
- if ( uOffset + cbToRead > pImage->cbSize
- || cbToRead == 0)
- {
- rc = VERR_INVALID_PARAMETER;
- goto out;
- }
-
- qcowConvertLogicalOffset(pImage, uOffset, &idxL1, &idxL2, &offCluster);
-
- /* Clip read size to remain in the cluster. */
- cbToRead = RT_MIN(cbToRead, pImage->cbCluster - offCluster);
-
- /* Get offset in image. */
- rc = qcowConvertToImageOffsetAsync(pImage, pIoCtx, idxL1, idxL2, offCluster,
- &offFile);
- if (RT_SUCCESS(rc))
- rc = vdIfIoIntFileReadUserAsync(pImage->pIfIo, pImage->pStorage, offFile,
- pIoCtx, cbToRead);
-
- if ( ( RT_SUCCESS(rc)
- || rc == VERR_VD_BLOCK_FREE
- || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
- && pcbActuallyRead)
- *pcbActuallyRead = cbToRead;
-
-out:
- LogFlowFunc(("returns %Rrc\n", rc));
- return rc;
-}
-
-static int qcowAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
- PVDIOCTX pIoCtx,
- size_t *pcbWriteProcess, size_t *pcbPreRead,
- size_t *pcbPostRead, unsigned fWrite)
-{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
- pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
- PQCOWIMAGE pImage = (PQCOWIMAGE)pBackendData;
- uint32_t offCluster = 0;
- uint32_t idxL1 = 0;
- uint32_t idxL2 = 0;
- uint64_t offImage = 0;
- int rc = VINF_SUCCESS;
-
- AssertPtr(pImage);
- Assert(!(uOffset % 512));
- Assert(!(cbToWrite % 512));
-
- if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
- {
- rc = VERR_VD_IMAGE_READ_ONLY;
- goto out;
- }
-
- if (!VALID_PTR(pIoCtx) || !cbToWrite)
- {
- rc = VERR_INVALID_PARAMETER;
- goto out;
- }
-
- if ( uOffset + cbToWrite > pImage->cbSize
- || cbToWrite == 0)
- {
- rc = VERR_INVALID_PARAMETER;
- goto out;
- }
-
- /* Convert offset to L1, L2 index and cluster offset. */
- qcowConvertLogicalOffset(pImage, uOffset, &idxL1, &idxL2, &offCluster);
-
- /* Clip write size to remain in the cluster. */
- cbToWrite = RT_MIN(cbToWrite, pImage->cbCluster - offCluster);
- Assert(!(cbToWrite % 512));
-
- /* Get offset in image. */
- rc = qcowConvertToImageOffsetAsync(pImage, pIoCtx, idxL1, idxL2, offCluster,
- &offImage);
- if (RT_SUCCESS(rc))
- rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pImage->pStorage,
- offImage, pIoCtx, cbToWrite, NULL, NULL);
- else if (rc == VERR_VD_BLOCK_FREE)
- {
- if ( cbToWrite == pImage->cbCluster
- && !(fWrite & VD_WRITE_NO_ALLOC))
- {
- PQCOWL2CACHEENTRY pL2Entry = NULL;
-
- /* Full cluster write to previously unallocated cluster.
- * Allocate cluster and write data. */
- Assert(!offCluster);
-
- do
- {
- uint64_t idxUpdateLe = 0;
-
- /* Check if we have to allocate a new cluster for L2 tables. */
- if (!pImage->paL1Table[idxL1])
- {
- uint64_t offL2Tbl;
- PQCOWCLUSTERASYNCALLOC pL2ClusterAlloc = NULL;
-
- /* Allocate new async cluster allocation state. */
- pL2ClusterAlloc = (PQCOWCLUSTERASYNCALLOC)RTMemAllocZ(sizeof(QCOWCLUSTERASYNCALLOC));
- if (RT_UNLIKELY(!pL2ClusterAlloc))
- {
- rc = VERR_NO_MEMORY;
- break;
- }
-
- pL2Entry = qcowL2TblCacheEntryAlloc(pImage);
- if (!pL2Entry)
- {
- rc = VERR_NO_MEMORY;
- RTMemFree(pL2ClusterAlloc);
- break;
- }
-
- offL2Tbl = qcowClusterAllocate(pImage, qcowByte2Cluster(pImage, pImage->cbL2Table));
- pL2Entry->offL2Tbl = offL2Tbl;
- memset(pL2Entry->paL2Tbl, 0, pImage->cbL2Table);
-
- pL2ClusterAlloc->enmAllocState = QCOWCLUSTERASYNCALLOCSTATE_L2_ALLOC;
- pL2ClusterAlloc->offNextClusterOld = offL2Tbl;
- pL2ClusterAlloc->offClusterNew = offL2Tbl;
- pL2ClusterAlloc->idxL1 = idxL1;
- pL2ClusterAlloc->idxL2 = idxL2;
- pL2ClusterAlloc->cbToWrite = cbToWrite;
- pL2ClusterAlloc->pL2Entry = pL2Entry;
-
- /*
- * Write the L2 table first and link to the L1 table afterwards.
- * If something unexpected happens the worst case which can happen
- * is a leak of some clusters.
- */
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
- offL2Tbl, pL2Entry->paL2Tbl, pImage->cbL2Table, pIoCtx,
- qcowAsyncClusterAllocUpdate, pL2ClusterAlloc);
- if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
- break;
- else if (RT_FAILURE(rc))
- {
- RTMemFree(pL2ClusterAlloc);
- qcowL2TblCacheEntryFree(pImage, pL2Entry);
- break;
- }
-
- rc = qcowAsyncClusterAllocUpdate(pImage, pIoCtx, pL2ClusterAlloc, rc);
- }
- else
- {
- rc = qcowL2TblCacheFetchAsync(pImage, pIoCtx, pImage->paL1Table[idxL1],
- &pL2Entry);
-
- if (RT_SUCCESS(rc))
- {
- PQCOWCLUSTERASYNCALLOC pDataClusterAlloc = NULL;
-
- /* Allocate new async cluster allocation state. */
- pDataClusterAlloc = (PQCOWCLUSTERASYNCALLOC)RTMemAllocZ(sizeof(QCOWCLUSTERASYNCALLOC));
- if (RT_UNLIKELY(!pDataClusterAlloc))
- {
- rc = VERR_NO_MEMORY;
- break;
- }
-
- /* Allocate new cluster for the data. */
- uint64_t offData = qcowClusterAllocate(pImage, 1);
-
- pDataClusterAlloc->enmAllocState = QCOWCLUSTERASYNCALLOCSTATE_USER_ALLOC;
- pDataClusterAlloc->offNextClusterOld = offData;
- pDataClusterAlloc->offClusterNew = offData;
- pDataClusterAlloc->idxL1 = idxL1;
- pDataClusterAlloc->idxL2 = idxL2;
- pDataClusterAlloc->cbToWrite = cbToWrite;
- pDataClusterAlloc->pL2Entry = pL2Entry;
- /* Write data. */
- rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pImage->pStorage,
- offData, pIoCtx, cbToWrite,
- qcowAsyncClusterAllocUpdate, pDataClusterAlloc);
- if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
- break;
- else if (RT_FAILURE(rc))
- {
- RTMemFree(pDataClusterAlloc);
- break;
- }
-
- rc = qcowAsyncClusterAllocUpdate(pImage, pIoCtx, pDataClusterAlloc, rc);
- }
- }
-
- } while (0);
-
- *pcbPreRead = 0;
- *pcbPostRead = 0;
- }
- else
- {
- /* Trying to do a partial write to an unallocated cluster. Don't do
- * anything except letting the upper layer know what to do. */
- *pcbPreRead = offCluster;
- *pcbPostRead = pImage->cbCluster - cbToWrite - *pcbPreRead;
- }
- }
-
- if (pcbWriteProcess)
- *pcbWriteProcess = cbToWrite;
-
-
-out:
- LogFlowFunc(("returns %Rrc\n", rc));
- return rc;
-}
-
-static int qcowAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
-{
- LogFlowFunc(("pBackendData=%#p\n", pBackendData));
- PQCOWIMAGE pImage = (PQCOWIMAGE)pBackendData;
- int rc = VINF_SUCCESS;
-
- Assert(pImage);
-
- if (VALID_PTR(pIoCtx))
- rc = qcowFlushImageAsync(pImage, pIoCtx);
- else
- rc = VERR_INVALID_PARAMETER;
-
- LogFlowFunc(("returns %Rrc\n", rc));
- return rc;
-}
VBOXHDDBACKEND g_QCowBackend =
{
@@ -2787,8 +2489,12 @@ VBOXHDDBACKEND g_QCowBackend =
qcowWrite,
/* pfnFlush */
qcowFlush,
+ /* pfnDiscard */
+ NULL,
/* pfnGetVersion */
qcowGetVersion,
+ /* pfnGetSectorSize */
+ qcowGetSectorSize,
/* pfnGetSize */
qcowGetSize,
/* pfnGetFileSize */
@@ -2839,12 +2545,6 @@ VBOXHDDBACKEND g_QCowBackend =
qcowGetParentFilename,
/* pfnSetParentFilename */
qcowSetParentFilename,
- /* pfnAsyncRead */
- qcowAsyncRead,
- /* pfnAsyncWrite */
- qcowAsyncWrite,
- /* pfnAsyncFlush */
- qcowAsyncFlush,
/* pfnComposeLocation */
genericFileComposeLocation,
/* pfnComposeName */
@@ -2853,10 +2553,6 @@ VBOXHDDBACKEND g_QCowBackend =
NULL,
/* pfnResize */
NULL,
- /* pfnDiscard */
- NULL,
- /* pfnAsyncDiscard */
- NULL,
/* pfnRepair */
NULL
};
diff --git a/src/VBox/Storage/QED.cpp b/src/VBox/Storage/QED.cpp
index f27957d0..681a8a56 100644
--- a/src/VBox/Storage/QED.cpp
+++ b/src/VBox/Storage/QED.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2011 Oracle Corporation
+ * Copyright (C) 2011-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -580,7 +580,7 @@ static int qedL2TblCacheFetch(PQEDIMAGE pImage, uint64_t offL2Tbl, PQEDL2CACHEEN
/* Read from the image. */
pL2Entry->offL2Tbl = offL2Tbl;
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, offL2Tbl,
- pL2Entry->paL2Tbl, pImage->cbTable, NULL);
+ pL2Entry->paL2Tbl, pImage->cbTable);
if (RT_SUCCESS(rc))
{
#if defined(RT_BIG_ENDIAN)
@@ -632,10 +632,10 @@ static int qedL2TblCacheFetchAsync(PQEDIMAGE pImage, PVDIOCTX pIoCtx,
PVDMETAXFER pMetaXfer;
pL2Entry->offL2Tbl = offL2Tbl;
- rc = vdIfIoIntFileReadMetaAsync(pImage->pIfIo, pImage->pStorage,
- offL2Tbl, pL2Entry->paL2Tbl,
- pImage->cbTable, pIoCtx,
- &pMetaXfer, NULL, NULL);
+ rc = vdIfIoIntFileReadMeta(pImage->pIfIo, pImage->pStorage,
+ offL2Tbl, pL2Entry->paL2Tbl,
+ pImage->cbTable, pIoCtx,
+ &pMetaXfer, NULL, NULL);
if (RT_SUCCESS(rc))
{
vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
@@ -702,7 +702,7 @@ static void qedTableMasksInit(PQEDIMAGE pImage)
}
/**
- * Converts a given logical offset into the
+ * Converts a given logical offset into the
*
* @returns nothing.
* @param pImage The image instance data.
@@ -771,59 +771,15 @@ DECLINLINE(uint64_t) qedClusterAllocate(PQEDIMAGE pImage, uint32_t cClusters)
* @returns VBox status code.
* VERR_VD_BLOCK_FREE if the cluster is not yet allocated.
* @param pImage The image instance data.
- * @param idxL1 The L1 index.
- * @param idxL2 The L2 index.
- * @param offCluster Offset inside the cluster.
- * @param poffImage Where to store the image offset on success;
- */
-static int qedConvertToImageOffset(PQEDIMAGE pImage, uint32_t idxL1, uint32_t idxL2,
- uint32_t offCluster, uint64_t *poffImage)
-{
- int rc = VERR_VD_BLOCK_FREE;
- LogFlowFunc(("pImage=%#p idxL1=%u idxL2=%u offCluster=%u poffImage=%#p\n",
- pImage, idxL1, idxL2, offCluster, poffImage));
-
- AssertReturn(idxL1 < pImage->cTableEntries, VERR_INVALID_PARAMETER);
- AssertReturn(idxL2 < pImage->cTableEntries, VERR_INVALID_PARAMETER);
-
- if (pImage->paL1Table[idxL1])
- {
- PQEDL2CACHEENTRY pL2Entry;
-
- rc = qedL2TblCacheFetch(pImage, pImage->paL1Table[idxL1], &pL2Entry);
- if (RT_SUCCESS(rc))
- {
- LogFlowFunc(("cluster start offset %llu\n", pL2Entry->paL2Tbl[idxL2]));
- /* Get real file offset. */
- if (pL2Entry->paL2Tbl[idxL2])
- *poffImage = pL2Entry->paL2Tbl[idxL2] + offCluster;
- else
- rc = VERR_VD_BLOCK_FREE;
-
- qedL2TblCacheEntryRelease(pL2Entry);
- }
- }
-
- LogFlowFunc(("returns rc=%Rrc\n", rc));
- return rc;
-}
-
-/**
- * Returns the real image offset for a given cluster or an error if the cluster is not
- * yet allocated- version for async I/O.
- *
- * @returns VBox status code.
- * VERR_VD_BLOCK_FREE if the cluster is not yet allocated.
- * @param pImage The image instance data.
* @param pIoCtx The I/O context.
* @param idxL1 The L1 index.
* @param idxL2 The L2 index.
* @param offCluster Offset inside the cluster.
* @param poffImage Where to store the image offset on success;
*/
-static int qedConvertToImageOffsetAsync(PQEDIMAGE pImage, PVDIOCTX pIoCtx,
- uint32_t idxL1, uint32_t idxL2,
- uint32_t offCluster, uint64_t *poffImage)
+static int qedConvertToImageOffset(PQEDIMAGE pImage, PVDIOCTX pIoCtx,
+ uint32_t idxL1, uint32_t idxL2,
+ uint32_t offCluster, uint64_t *poffImage)
{
int rc = VERR_VD_BLOCK_FREE;
@@ -881,14 +837,14 @@ static int qedFlushImage(PQEDIMAGE pImage)
#else
/* Write L1 table directly. */
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, pImage->offL1Table,
- pImage->paL1Table, pImage->cbTable, NULL);
+ pImage->paL1Table, pImage->cbTable);
#endif
if (RT_SUCCESS(rc))
{
/* Write header. */
qedHdrConvertFromHostEndianess(pImage, &Header);
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, 0, &Header,
- sizeof(Header), NULL);
+ sizeof(Header));
if (RT_SUCCESS(rc))
rc = vdIfIoIntFileFlushSync(pImage->pIfIo, pImage->pStorage);
}
@@ -920,29 +876,29 @@ static int qedFlushImageAsync(PQEDIMAGE pImage, PVDIOCTX pIoCtx)
{
qedTableConvertFromHostEndianess(paL1TblImg, pImage->paL1Table,
pImage->cTableEntries);
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
- pImage->offL1Table, paL1TblImg,
- pImage->cbTable, pIoCtx, NULL, NULL);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
+ pImage->offL1Table, paL1TblImg,
+ pImage->cbTable, pIoCtx, NULL, NULL);
RTMemFree(paL1TblImg);
}
else
rc = VERR_NO_MEMORY;
#else
/* Write L1 table directly. */
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
- pImage->offL1Table, pImage->paL1Table,
- pImage->cbTable, pIoCtx, NULL, NULL);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
+ pImage->offL1Table, pImage->paL1Table,
+ pImage->cbTable, pIoCtx, NULL, NULL);
#endif
if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
{
/* Write header. */
qedHdrConvertFromHostEndianess(pImage, &Header);
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
- 0, &Header, sizeof(Header),
- pIoCtx, NULL, NULL);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
+ 0, &Header, sizeof(Header),
+ pIoCtx, NULL, NULL);
if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
- rc = vdIfIoIntFileFlushAsync(pImage->pIfIo, pImage->pStorage,
- pIoCtx, NULL, NULL);
+ rc = vdIfIoIntFileFlush(pImage->pIfIo, pImage->pStorage,
+ pIoCtx, NULL, NULL);
}
}
@@ -1076,7 +1032,7 @@ static int qedCheckImage(PQEDIMAGE pImage, PQedHeader pHeader)
/* Read L1 table. */
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
- pHeader->u64OffL1Table, paL1Tbl, cbTable, NULL);
+ pHeader->u64OffL1Table, paL1Tbl, cbTable);
if (RT_FAILURE(rc))
{
rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
@@ -1120,7 +1076,7 @@ static int qedCheckImage(PQEDIMAGE pImage, PQedHeader pHeader)
/* Read the linked L2 table and check it. */
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
- paL1Tbl[iL1], paL2Tbl, cbTable, NULL);
+ paL1Tbl[iL1], paL2Tbl, cbTable);
if (RT_FAILURE(rc))
{
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
@@ -1185,7 +1141,7 @@ static int qedFreeImage(PQEDIMAGE pImage, bool fDelete)
if (!fDelete)
qedFlushImage(pImage);
- vdIfIoIntFileClose(pImage->pIfIo, pImage->pStorage);
+ rc = vdIfIoIntFileClose(pImage->pIfIo, pImage->pStorage);
pImage->pStorage = NULL;
}
@@ -1193,7 +1149,10 @@ static int qedFreeImage(PQEDIMAGE pImage, bool fDelete)
RTMemFree(pImage->paL1Table);
if (pImage->pszBackingFilename)
+ {
RTMemFree(pImage->pszBackingFilename);
+ pImage->pszBackingFilename = NULL;
+ }
qedL2TblCacheDestroy(pImage);
@@ -1239,7 +1198,7 @@ static int qedOpenImage(PQEDIMAGE pImage, unsigned uOpenFlags)
goto out;
if (cbFile > sizeof(Header))
{
- rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, 0, &Header, sizeof(Header), NULL);
+ rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, 0, &Header, sizeof(Header));
if ( RT_SUCCESS(rc)
&& qedHdrConvertToHostEndianess(&Header))
{
@@ -1261,14 +1220,14 @@ static int qedOpenImage(PQEDIMAGE pImage, unsigned uOpenFlags)
&& (Header.u64FeatureFlags & QED_FEATURE_BACKING_FILE))
{
/* Load backing filename from image. */
- pImage->pszFilename = (char *)RTMemAllocZ(Header.u32BackingFilenameSize + 1); /* +1 for \0 terminator. */
- if (pImage->pszFilename)
+ pImage->pszBackingFilename = (char *)RTMemAllocZ(Header.u32BackingFilenameSize + 1); /* +1 for \0 terminator. */
+ if (pImage->pszBackingFilename)
{
pImage->cbBackingFilename = Header.u32BackingFilenameSize;
pImage->offBackingFilename = Header.u32OffBackingFilename;
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
Header.u32OffBackingFilename, pImage->pszBackingFilename,
- Header.u32BackingFilenameSize, NULL);
+ Header.u32BackingFilenameSize);
}
else
rc = VERR_NO_MEMORY;
@@ -1291,7 +1250,7 @@ static int qedOpenImage(PQEDIMAGE pImage, unsigned uOpenFlags)
/* Read from the image. */
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
pImage->offL1Table, pImage->paL1Table,
- pImage->cbTable, NULL);
+ pImage->cbTable);
if (RT_SUCCESS(rc))
{
qedTableConvertToHostEndianess(pImage->paL1Table, pImage->cTableEntries);
@@ -1482,10 +1441,10 @@ static DECLCALLBACK(int) qedAsyncClusterAllocUpdate(void *pBackendData, PVDIOCTX
/* Update the link in the on disk L1 table now. */
pClusterAlloc->enmAllocState = QEDCLUSTERASYNCALLOCSTATE_L2_LINK;
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
- pImage->offL1Table + pClusterAlloc->idxL1*sizeof(uint64_t),
- &offUpdateLe, sizeof(uint64_t), pIoCtx,
- qedAsyncClusterAllocUpdate, pClusterAlloc);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
+ pImage->offL1Table + pClusterAlloc->idxL1*sizeof(uint64_t),
+ &offUpdateLe, sizeof(uint64_t), pIoCtx,
+ qedAsyncClusterAllocUpdate, pClusterAlloc);
if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
break;
else if (RT_FAILURE(rc))
@@ -1510,9 +1469,9 @@ static DECLCALLBACK(int) qedAsyncClusterAllocUpdate(void *pBackendData, PVDIOCTX
pClusterAlloc->offClusterNew = offData;
/* Write data. */
- rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pImage->pStorage,
- offData, pIoCtx, pClusterAlloc->cbToWrite,
- qedAsyncClusterAllocUpdate, pClusterAlloc);
+ rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
+ offData, pIoCtx, pClusterAlloc->cbToWrite,
+ qedAsyncClusterAllocUpdate, pClusterAlloc);
if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
break;
else if (RT_FAILURE(rc))
@@ -1529,10 +1488,10 @@ static DECLCALLBACK(int) qedAsyncClusterAllocUpdate(void *pBackendData, PVDIOCTX
pClusterAlloc->enmAllocState = QEDCLUSTERASYNCALLOCSTATE_USER_LINK;
/* Link L2 table and update it. */
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
- pImage->paL1Table[pClusterAlloc->idxL1] + pClusterAlloc->idxL2*sizeof(uint64_t),
- &offUpdateLe, sizeof(uint64_t), pIoCtx,
- qedAsyncClusterAllocUpdate, pClusterAlloc);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
+ pImage->paL1Table[pClusterAlloc->idxL1] + pClusterAlloc->idxL2*sizeof(uint64_t),
+ &offUpdateLe, sizeof(uint64_t), pIoCtx,
+ qedAsyncClusterAllocUpdate, pClusterAlloc);
if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
break;
else if (RT_FAILURE(rc))
@@ -1594,7 +1553,7 @@ static int qedCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
{
QedHeader Header;
- rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0, &Header, sizeof(Header), NULL);
+ rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0, &Header, sizeof(Header));
if ( RT_SUCCESS(rc)
&& qedHdrConvertToHostEndianess(&Header))
{
@@ -1800,12 +1759,11 @@ static int qedClose(void *pBackendData, bool fDelete)
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnRead */
-static int qedRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
- size_t cbToRead, size_t *pcbActuallyRead)
+static int qedRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
+ PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
- pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
+ LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
+ pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
uint32_t offCluster = 0;
uint32_t idxL1 = 0;
@@ -1817,6 +1775,12 @@ static int qedRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
Assert(uOffset % 512 == 0);
Assert(cbToRead % 512 == 0);
+ if (!VALID_PTR(pIoCtx) || !cbToRead)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+
if ( uOffset + cbToRead > pImage->cbSize
|| cbToRead == 0)
{
@@ -1825,21 +1789,19 @@ static int qedRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
}
qedConvertLogicalOffset(pImage, uOffset, &idxL1, &idxL2, &offCluster);
- LogFlowFunc(("idxL1=%u idxL2=%u offCluster=%u\n", idxL1, idxL2, offCluster));
/* Clip read size to remain in the cluster. */
cbToRead = RT_MIN(cbToRead, pImage->cbCluster - offCluster);
/* Get offset in image. */
- rc = qedConvertToImageOffset(pImage, idxL1, idxL2, offCluster, &offFile);
+ rc = qedConvertToImageOffset(pImage, pIoCtx, idxL1, idxL2, offCluster, &offFile);
if (RT_SUCCESS(rc))
- {
- LogFlowFunc(("offFile=%llu\n", offFile));
- rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, offFile,
- pvBuf, cbToRead, NULL);
- }
+ rc = vdIfIoIntFileReadUser(pImage->pIfIo, pImage->pStorage, offFile,
+ pIoCtx, cbToRead);
- if ( (RT_SUCCESS(rc) || rc == VERR_VD_BLOCK_FREE)
+ if ( ( RT_SUCCESS(rc)
+ || rc == VERR_VD_BLOCK_FREE
+ || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
&& pcbActuallyRead)
*pcbActuallyRead = cbToRead;
@@ -1848,23 +1810,22 @@ out:
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnWrite */
-static int qedWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
- size_t cbToWrite, size_t *pcbWriteProcess,
- size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
+static int qedWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
+ PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
+ size_t *pcbPostRead, unsigned fWrite)
{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
- pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
+ LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
+ pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
uint32_t offCluster = 0;
uint32_t idxL1 = 0;
uint32_t idxL2 = 0;
uint64_t offImage = 0;
- int rc;
+ int rc = VINF_SUCCESS;
AssertPtr(pImage);
- Assert(uOffset % 512 == 0);
- Assert(cbToWrite % 512 == 0);
+ Assert(!(uOffset % 512));
+ Assert(!(cbToWrite % 512));
if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
{
@@ -1872,6 +1833,12 @@ static int qedWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
goto out;
}
+ if (!VALID_PTR(pIoCtx) || !cbToWrite)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+
if ( uOffset + cbToWrite > pImage->cbSize
|| cbToWrite == 0)
{
@@ -1887,10 +1854,10 @@ static int qedWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
Assert(!(cbToWrite % 512));
/* Get offset in image. */
- rc = qedConvertToImageOffset(pImage, idxL1, idxL2, offCluster, &offImage);
+ rc = qedConvertToImageOffset(pImage, pIoCtx, idxL1, idxL2, offCluster, &offImage);
if (RT_SUCCESS(rc))
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, offImage,
- pvBuf, cbToWrite, NULL);
+ rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
+ offImage, pIoCtx, cbToWrite, NULL, NULL);
else if (rc == VERR_VD_BLOCK_FREE)
{
if ( cbToWrite == pImage->cbCluster
@@ -1909,59 +1876,98 @@ static int qedWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
/* Check if we have to allocate a new cluster for L2 tables. */
if (!pImage->paL1Table[idxL1])
{
- uint64_t offL2Tbl = qedClusterAllocate(pImage, qedByte2Cluster(pImage, pImage->cbTable));
+ uint64_t offL2Tbl;
+ PQEDCLUSTERASYNCALLOC pL2ClusterAlloc = NULL;
+
+ /* Allocate new async cluster allocation state. */
+ pL2ClusterAlloc = (PQEDCLUSTERASYNCALLOC)RTMemAllocZ(sizeof(QEDCLUSTERASYNCALLOC));
+ if (RT_UNLIKELY(!pL2ClusterAlloc))
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
pL2Entry = qedL2TblCacheEntryAlloc(pImage);
if (!pL2Entry)
{
rc = VERR_NO_MEMORY;
+ RTMemFree(pL2ClusterAlloc);
break;
}
+ offL2Tbl = qedClusterAllocate(pImage, qedByte2Cluster(pImage, pImage->cbTable));
pL2Entry->offL2Tbl = offL2Tbl;
memset(pL2Entry->paL2Tbl, 0, pImage->cbTable);
- qedL2TblCacheEntryInsert(pImage, pL2Entry);
+
+ pL2ClusterAlloc->enmAllocState = QEDCLUSTERASYNCALLOCSTATE_L2_ALLOC;
+ pL2ClusterAlloc->cbImageOld = offL2Tbl;
+ pL2ClusterAlloc->offClusterNew = offL2Tbl;
+ pL2ClusterAlloc->idxL1 = idxL1;
+ pL2ClusterAlloc->idxL2 = idxL2;
+ pL2ClusterAlloc->cbToWrite = cbToWrite;
+ pL2ClusterAlloc->pL2Entry = pL2Entry;
/*
* Write the L2 table first and link to the L1 table afterwards.
* If something unexpected happens the worst case which can happen
* is a leak of some clusters.
*/
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, offL2Tbl,
- pL2Entry->paL2Tbl, pImage->cbTable, NULL);
- if (RT_FAILURE(rc))
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
+ offL2Tbl, pL2Entry->paL2Tbl, pImage->cbTable, pIoCtx,
+ qedAsyncClusterAllocUpdate, pL2ClusterAlloc);
+ if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
break;
-
- /* Write the L1 link now. */
- pImage->paL1Table[idxL1] = offL2Tbl;
- idxUpdateLe = RT_H2LE_U64(offL2Tbl);
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
- pImage->offL1Table + idxL1*sizeof(uint64_t),
- &idxUpdateLe, sizeof(uint64_t), NULL);
- if (RT_FAILURE(rc))
+ else if (RT_FAILURE(rc))
+ {
+ RTMemFree(pL2ClusterAlloc);
+ qedL2TblCacheEntryFree(pImage, pL2Entry);
break;
+ }
+
+ rc = qedAsyncClusterAllocUpdate(pImage, pIoCtx, pL2ClusterAlloc, rc);
}
else
- rc = qedL2TblCacheFetch(pImage, pImage->paL1Table[idxL1], &pL2Entry);
-
- if (RT_SUCCESS(rc))
{
- /* Allocate new cluster for the data. */
- uint64_t offData = qedClusterAllocate(pImage, 1);
+ rc = qedL2TblCacheFetchAsync(pImage, pIoCtx, pImage->paL1Table[idxL1],
+ &pL2Entry);
- /* Write data. */
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
- offData, pvBuf, cbToWrite, NULL);
- if (RT_FAILURE(rc))
- break;
+ if (RT_SUCCESS(rc))
+ {
+ PQEDCLUSTERASYNCALLOC pDataClusterAlloc = NULL;
- /* Link L2 table and update it. */
- pL2Entry->paL2Tbl[idxL2] = offData;
- idxUpdateLe = RT_H2LE_U64(offData);
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
- pImage->paL1Table[idxL1] + idxL2*sizeof(uint64_t),
- &idxUpdateLe, sizeof(uint64_t), NULL);
- qedL2TblCacheEntryRelease(pL2Entry);
+ /* Allocate new async cluster allocation state. */
+ pDataClusterAlloc = (PQEDCLUSTERASYNCALLOC)RTMemAllocZ(sizeof(QEDCLUSTERASYNCALLOC));
+ if (RT_UNLIKELY(!pDataClusterAlloc))
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+
+ /* Allocate new cluster for the data. */
+ uint64_t offData = qedClusterAllocate(pImage, 1);
+
+ pDataClusterAlloc->enmAllocState = QEDCLUSTERASYNCALLOCSTATE_USER_ALLOC;
+ pDataClusterAlloc->cbImageOld = offData;
+ pDataClusterAlloc->offClusterNew = offData;
+ pDataClusterAlloc->idxL1 = idxL1;
+ pDataClusterAlloc->idxL2 = idxL2;
+ pDataClusterAlloc->cbToWrite = cbToWrite;
+ pDataClusterAlloc->pL2Entry = pL2Entry;
+
+ /* Write data. */
+ rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
+ offData, pIoCtx, cbToWrite,
+ qedAsyncClusterAllocUpdate, pDataClusterAlloc);
+ if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+ break;
+ else if (RT_FAILURE(rc))
+ {
+ RTMemFree(pDataClusterAlloc);
+ break;
+ }
+
+ rc = qedAsyncClusterAllocUpdate(pImage, pIoCtx, pDataClusterAlloc, rc);
+ }
}
} while (0);
@@ -1981,19 +1987,25 @@ static int qedWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
if (pcbWriteProcess)
*pcbWriteProcess = cbToWrite;
+
out:
LogFlowFunc(("returns %Rrc\n", rc));
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnFlush */
-static int qedFlush(void *pBackendData)
+static int qedFlush(void *pBackendData, PVDIOCTX pIoCtx)
{
LogFlowFunc(("pBackendData=%#p\n", pBackendData));
PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
- int rc;
+ int rc = VINF_SUCCESS;
+
+ Assert(pImage);
+
+ if (VALID_PTR(pIoCtx))
+ rc = qedFlushImageAsync(pImage, pIoCtx);
+ else
+ rc = VERR_INVALID_PARAMETER;
- rc = qedFlushImage(pImage);
LogFlowFunc(("returns %Rrc\n", rc));
return rc;
}
@@ -2012,6 +2024,22 @@ static unsigned qedGetVersion(void *pBackendData)
return 0;
}
+/** @copydoc VBOXHDDBACKEND::pfnGetSectorSize */
+static uint32_t qedGetSectorSize(void *pBackendData)
+{
+ LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+ PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
+ uint32_t cb = 0;
+
+ AssertPtr(pImage);
+
+ if (pImage && pImage->pStorage)
+ cb = 512;
+
+ LogFlowFunc(("returns %u\n", cb));
+ return cb;
+}
+
/** @copydoc VBOXHDDBACKEND::pfnGetSize */
static uint64_t qedGetSize(void *pBackendData)
{
@@ -2208,7 +2236,8 @@ static int qedSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
int rc;
/* Image must be opened and the new flags must be valid. */
- if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO)))
+ if (!pImage || (uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
+ | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
{
rc = VERR_INVALID_PARAMETER;
goto out;
@@ -2455,7 +2484,7 @@ static int qedGetParentFilename(void *pBackendData, char **ppszParentFilename)
AssertPtr(pImage);
if (pImage)
- if (pImage->pszFilename)
+ if (pImage->pszBackingFilename)
*ppszParentFilename = RTStrDup(pImage->pszBackingFilename);
else
rc = VERR_NOT_SUPPORTED;
@@ -2496,7 +2525,7 @@ static int qedSetParentFilename(void *pBackendData, const char *pszParentFilenam
Assert((offData & UINT32_MAX) == offData);
pImage->offBackingFilename = (uint32_t)offData;
- pImage->cbBackingFilename = strlen(pszParentFilename);
+ pImage->cbBackingFilename = (uint32_t)strlen(pszParentFilename);
rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage,
offData + pImage->cbCluster);
}
@@ -2505,8 +2534,7 @@ static int qedSetParentFilename(void *pBackendData, const char *pszParentFilenam
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
pImage->offBackingFilename,
pImage->pszBackingFilename,
- strlen(pImage->pszBackingFilename),
- NULL);
+ strlen(pImage->pszBackingFilename));
}
}
}
@@ -2517,260 +2545,6 @@ static int qedSetParentFilename(void *pBackendData, const char *pszParentFilenam
return rc;
}
-static int qedAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
- PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
-{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
- pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
- PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
- uint32_t offCluster = 0;
- uint32_t idxL1 = 0;
- uint32_t idxL2 = 0;
- uint64_t offFile = 0;
- int rc;
-
- AssertPtr(pImage);
- Assert(uOffset % 512 == 0);
- Assert(cbToRead % 512 == 0);
-
- if (!VALID_PTR(pIoCtx) || !cbToRead)
- {
- rc = VERR_INVALID_PARAMETER;
- goto out;
- }
-
- if ( uOffset + cbToRead > pImage->cbSize
- || cbToRead == 0)
- {
- rc = VERR_INVALID_PARAMETER;
- goto out;
- }
-
- qedConvertLogicalOffset(pImage, uOffset, &idxL1, &idxL2, &offCluster);
-
- /* Clip read size to remain in the cluster. */
- cbToRead = RT_MIN(cbToRead, pImage->cbCluster - offCluster);
-
- /* Get offset in image. */
- rc = qedConvertToImageOffsetAsync(pImage, pIoCtx, idxL1, idxL2, offCluster,
- &offFile);
- if (RT_SUCCESS(rc))
- rc = vdIfIoIntFileReadUserAsync(pImage->pIfIo, pImage->pStorage, offFile,
- pIoCtx, cbToRead);
-
- if ( ( RT_SUCCESS(rc)
- || rc == VERR_VD_BLOCK_FREE
- || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
- && pcbActuallyRead)
- *pcbActuallyRead = cbToRead;
-
-out:
- LogFlowFunc(("returns %Rrc\n", rc));
- return rc;
-}
-
-static int qedAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
- PVDIOCTX pIoCtx,
- size_t *pcbWriteProcess, size_t *pcbPreRead,
- size_t *pcbPostRead, unsigned fWrite)
-{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
- pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
- PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
- uint32_t offCluster = 0;
- uint32_t idxL1 = 0;
- uint32_t idxL2 = 0;
- uint64_t offImage = 0;
- int rc = VINF_SUCCESS;
-
- AssertPtr(pImage);
- Assert(!(uOffset % 512));
- Assert(!(cbToWrite % 512));
-
- if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
- {
- rc = VERR_VD_IMAGE_READ_ONLY;
- goto out;
- }
-
- if (!VALID_PTR(pIoCtx) || !cbToWrite)
- {
- rc = VERR_INVALID_PARAMETER;
- goto out;
- }
-
- if ( uOffset + cbToWrite > pImage->cbSize
- || cbToWrite == 0)
- {
- rc = VERR_INVALID_PARAMETER;
- goto out;
- }
-
- /* Convert offset to L1, L2 index and cluster offset. */
- qedConvertLogicalOffset(pImage, uOffset, &idxL1, &idxL2, &offCluster);
-
- /* Clip write size to remain in the cluster. */
- cbToWrite = RT_MIN(cbToWrite, pImage->cbCluster - offCluster);
- Assert(!(cbToWrite % 512));
-
- /* Get offset in image. */
- rc = qedConvertToImageOffsetAsync(pImage, pIoCtx, idxL1, idxL2, offCluster,
- &offImage);
- if (RT_SUCCESS(rc))
- rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pImage->pStorage,
- offImage, pIoCtx, cbToWrite, NULL, NULL);
- else if (rc == VERR_VD_BLOCK_FREE)
- {
- if ( cbToWrite == pImage->cbCluster
- && !(fWrite & VD_WRITE_NO_ALLOC))
- {
- PQEDL2CACHEENTRY pL2Entry = NULL;
-
- /* Full cluster write to previously unallocated cluster.
- * Allocate cluster and write data. */
- Assert(!offCluster);
-
- do
- {
- uint64_t idxUpdateLe = 0;
-
- /* Check if we have to allocate a new cluster for L2 tables. */
- if (!pImage->paL1Table[idxL1])
- {
- uint64_t offL2Tbl;
- PQEDCLUSTERASYNCALLOC pL2ClusterAlloc = NULL;
-
- /* Allocate new async cluster allocation state. */
- pL2ClusterAlloc = (PQEDCLUSTERASYNCALLOC)RTMemAllocZ(sizeof(QEDCLUSTERASYNCALLOC));
- if (RT_UNLIKELY(!pL2ClusterAlloc))
- {
- rc = VERR_NO_MEMORY;
- break;
- }
-
- pL2Entry = qedL2TblCacheEntryAlloc(pImage);
- if (!pL2Entry)
- {
- rc = VERR_NO_MEMORY;
- RTMemFree(pL2ClusterAlloc);
- break;
- }
-
- offL2Tbl = qedClusterAllocate(pImage, qedByte2Cluster(pImage, pImage->cbTable));
- pL2Entry->offL2Tbl = offL2Tbl;
- memset(pL2Entry->paL2Tbl, 0, pImage->cbTable);
-
- pL2ClusterAlloc->enmAllocState = QEDCLUSTERASYNCALLOCSTATE_L2_ALLOC;
- pL2ClusterAlloc->cbImageOld = offL2Tbl;
- pL2ClusterAlloc->offClusterNew = offL2Tbl;
- pL2ClusterAlloc->idxL1 = idxL1;
- pL2ClusterAlloc->idxL2 = idxL2;
- pL2ClusterAlloc->cbToWrite = cbToWrite;
- pL2ClusterAlloc->pL2Entry = pL2Entry;
-
- /*
- * Write the L2 table first and link to the L1 table afterwards.
- * If something unexpected happens the worst case which can happen
- * is a leak of some clusters.
- */
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
- offL2Tbl, pL2Entry->paL2Tbl, pImage->cbTable, pIoCtx,
- qedAsyncClusterAllocUpdate, pL2ClusterAlloc);
- if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
- break;
- else if (RT_FAILURE(rc))
- {
- RTMemFree(pL2ClusterAlloc);
- qedL2TblCacheEntryFree(pImage, pL2Entry);
- break;
- }
-
- rc = qedAsyncClusterAllocUpdate(pImage, pIoCtx, pL2ClusterAlloc, rc);
- }
- else
- {
- rc = qedL2TblCacheFetchAsync(pImage, pIoCtx, pImage->paL1Table[idxL1],
- &pL2Entry);
-
- if (RT_SUCCESS(rc))
- {
- PQEDCLUSTERASYNCALLOC pDataClusterAlloc = NULL;
-
- /* Allocate new async cluster allocation state. */
- pDataClusterAlloc = (PQEDCLUSTERASYNCALLOC)RTMemAllocZ(sizeof(QEDCLUSTERASYNCALLOC));
- if (RT_UNLIKELY(!pDataClusterAlloc))
- {
- rc = VERR_NO_MEMORY;
- break;
- }
-
- /* Allocate new cluster for the data. */
- uint64_t offData = qedClusterAllocate(pImage, 1);
-
- pDataClusterAlloc->enmAllocState = QEDCLUSTERASYNCALLOCSTATE_USER_ALLOC;
- pDataClusterAlloc->cbImageOld = offData;
- pDataClusterAlloc->offClusterNew = offData;
- pDataClusterAlloc->idxL1 = idxL1;
- pDataClusterAlloc->idxL2 = idxL2;
- pDataClusterAlloc->cbToWrite = cbToWrite;
- pDataClusterAlloc->pL2Entry = pL2Entry;
-
- /* Write data. */
- rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pImage->pStorage,
- offData, pIoCtx, cbToWrite,
- qedAsyncClusterAllocUpdate, pDataClusterAlloc);
- if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
- break;
- else if (RT_FAILURE(rc))
- {
- RTMemFree(pDataClusterAlloc);
- break;
- }
-
- rc = qedAsyncClusterAllocUpdate(pImage, pIoCtx, pDataClusterAlloc, rc);
- }
- }
-
- } while (0);
-
- *pcbPreRead = 0;
- *pcbPostRead = 0;
- }
- else
- {
- /* Trying to do a partial write to an unallocated cluster. Don't do
- * anything except letting the upper layer know what to do. */
- *pcbPreRead = offCluster;
- *pcbPostRead = pImage->cbCluster - cbToWrite - *pcbPreRead;
- }
- }
-
- if (pcbWriteProcess)
- *pcbWriteProcess = cbToWrite;
-
-
-out:
- LogFlowFunc(("returns %Rrc\n", rc));
- return rc;
-}
-
-static int qedAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
-{
- LogFlowFunc(("pBackendData=%#p\n", pBackendData));
- PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
- int rc = VINF_SUCCESS;
-
- Assert(pImage);
-
- if (VALID_PTR(pIoCtx))
- rc = qedFlushImageAsync(pImage, pIoCtx);
- else
- rc = VERR_INVALID_PARAMETER;
-
- LogFlowFunc(("returns %Rrc\n", rc));
- return rc;
-}
-
/** @copydoc VBOXHDDBACKEND::pfnResize */
static int qedResize(void *pBackendData, uint64_t cbSize,
PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
@@ -2853,8 +2627,12 @@ VBOXHDDBACKEND g_QedBackend =
qedWrite,
/* pfnFlush */
qedFlush,
+ /* pfnDiscard */
+ NULL,
/* pfnGetVersion */
qedGetVersion,
+ /* pfnGetSectorSize */
+ qedGetSectorSize,
/* pfnGetSize */
qedGetSize,
/* pfnGetFileSize */
@@ -2905,12 +2683,6 @@ VBOXHDDBACKEND g_QedBackend =
qedGetParentFilename,
/* pfnSetParentFilename */
qedSetParentFilename,
- /* pfnAsyncRead */
- qedAsyncRead,
- /* pfnAsyncWrite */
- qedAsyncWrite,
- /* pfnAsyncFlush */
- qedAsyncFlush,
/* pfnComposeLocation */
genericFileComposeLocation,
/* pfnComposeName */
@@ -2919,10 +2691,6 @@ VBOXHDDBACKEND g_QedBackend =
NULL,
/* pfnResize */
qedResize,
- /* pfnDiscard */
- NULL,
- /* pfnAsyncDiscard */
- NULL,
/* pfnRepair */
NULL
};
diff --git a/src/VBox/Storage/RAW.cpp b/src/VBox/Storage/RAW.cpp
index 801e1cea..705dc5e8 100644
--- a/src/VBox/Storage/RAW.cpp
+++ b/src/VBox/Storage/RAW.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2010 Oracle Corporation
+ * Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -64,7 +64,8 @@ typedef struct RAWIMAGE
VDGEOMETRY PCHSGeometry;
/** Logical geometry of this image. */
VDGEOMETRY LCHSGeometry;
-
+ /** Sector size of the image. */
+ uint32_t cbSector;
} RAWIMAGE, *PRAWIMAGE;
@@ -86,6 +87,7 @@ static const VDFILEEXTENSION s_aRawFileExtensions[] =
{"img", VDTYPE_FLOPPY},
{"ima", VDTYPE_FLOPPY},
{"dsk", VDTYPE_FLOPPY},
+ {"flp", VDTYPE_FLOPPY},
{"vfd", VDTYPE_FLOPPY},
{NULL, VDTYPE_INVALID}
};
@@ -146,7 +148,7 @@ static int rawFreeImage(PRAWIMAGE pImage, bool fDelete)
RAW_FILL_SIZE);
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
- uOff, pvBuf, cbChunk, NULL);
+ uOff, pvBuf, cbChunk);
if (RT_FAILURE(rc))
goto out;
@@ -159,7 +161,7 @@ out:
rawFlushImage(pImage);
}
- vdIfIoIntFileClose(pImage->pIfIo, pImage->pStorage);
+ rc = vdIfIoIntFileClose(pImage->pIfIo, pImage->pStorage);
pImage->pStorage = NULL;
}
@@ -299,7 +301,7 @@ static int rawCreateImage(PRAWIMAGE pImage, uint64_t cbSize,
unsigned cbChunk = (unsigned)RT_MIN(cbSize, RAW_FILL_SIZE);
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, uOff,
- pvBuf, cbChunk, NULL);
+ pvBuf, cbChunk);
if (RT_FAILURE(rc))
{
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("Raw: writing block failed for '%s'"), pImage->pszFilename);
@@ -392,6 +394,7 @@ static int rawCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
else if ( !RTStrICmp(pszExtension, ".img")
|| !RTStrICmp(pszExtension, ".ima")
|| !RTStrICmp(pszExtension, ".dsk")
+ || !RTStrICmp(pszExtension, ".flp")
|| !RTStrICmp(pszExtension, ".vfd")) /* Floppy images */
{
if (!(cbFile % 512) && cbFile <= RAW_MAX_FLOPPY_IMG_SIZE)
@@ -454,7 +457,13 @@ static int rawOpen(const char *pszFilename, unsigned uOpenFlags,
rc = rawOpenImage(pImage, uOpenFlags);
if (RT_SUCCESS(rc))
+ {
+ if (enmType == VDTYPE_DVD)
+ pImage->cbSector = 2048;
+ else
+ pImage->cbSector = 512;
*ppBackendData = pImage;
+ }
else
RTMemFree(pImage);
@@ -601,97 +610,50 @@ static int rawClose(void *pBackendData, bool fDelete)
}
/** @copydoc VBOXHDDBACKEND::pfnRead */
-static int rawRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
- size_t cbToRead, size_t *pcbActuallyRead)
+static int rawRead(void *pBackendData, uint64_t uOffset, size_t cbRead,
+ PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
+ int rc = VINF_SUCCESS;
PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
- int rc;
-
- AssertPtr(pImage);
- Assert(uOffset % 512 == 0);
- Assert(cbToRead % 512 == 0);
-
- if ( uOffset + cbToRead > pImage->cbSize
- || cbToRead == 0)
- {
- rc = VERR_INVALID_PARAMETER;
- goto out;
- }
- /* For sequential access do not allow to go back. */
- if ( pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL
- && uOffset < pImage->offAccess)
- {
- rc = VERR_INVALID_PARAMETER;
- goto out;
- }
-
- rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, uOffset, pvBuf,
- cbToRead, NULL);
- pImage->offAccess = uOffset + cbToRead;
- if (pcbActuallyRead)
- *pcbActuallyRead = cbToRead;
+ rc = vdIfIoIntFileReadUser(pImage->pIfIo, pImage->pStorage, uOffset,
+ pIoCtx, cbRead);
+ if (RT_SUCCESS(rc))
+ *pcbActuallyRead = cbRead;
-out:
- LogFlowFunc(("returns %Rrc\n", rc));
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnWrite */
-static int rawWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
- size_t cbToWrite, size_t *pcbWriteProcess,
- size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
+static int rawWrite(void *pBackendData, uint64_t uOffset, size_t cbWrite,
+ PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
+ size_t *pcbPostRead, unsigned fWrite)
{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
+ int rc = VINF_SUCCESS;
PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
- int rc;
-
- AssertPtr(pImage);
- Assert(uOffset % 512 == 0);
- Assert(cbToWrite % 512 == 0);
- if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
- {
- rc = VERR_VD_IMAGE_READ_ONLY;
- goto out;
- }
-
- if ( uOffset + cbToWrite > pImage->cbSize
- || cbToWrite == 0)
- {
- rc = VERR_INVALID_PARAMETER;
- goto out;
- }
-
- /* For sequential access do not allow to go back. */
- if ( pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL
- && uOffset < pImage->offAccess)
+ rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage, uOffset,
+ pIoCtx, cbWrite, NULL, NULL);
+ if (RT_SUCCESS(rc))
{
- rc = VERR_INVALID_PARAMETER;
- goto out;
+ *pcbWriteProcess = cbWrite;
+ *pcbPostRead = 0;
+ *pcbPreRead = 0;
}
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, uOffset, pvBuf,
- cbToWrite, NULL);
- pImage->offAccess = uOffset + cbToWrite;
- if (pcbWriteProcess)
- *pcbWriteProcess = cbToWrite;
-
-out:
- LogFlowFunc(("returns %Rrc\n", rc));
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnFlush */
-static int rawFlush(void *pBackendData)
+static int rawFlush(void *pBackendData, PVDIOCTX pIoCtx)
{
- LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+ int rc = VINF_SUCCESS;
PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
- int rc;
- rc = rawFlushImage(pImage);
- LogFlowFunc(("returns %Rrc\n", rc));
+ if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+ rc = vdIfIoIntFileFlush(pImage->pIfIo, pImage->pStorage, pIoCtx,
+ NULL, NULL);
+
return rc;
}
@@ -710,6 +672,22 @@ static unsigned rawGetVersion(void *pBackendData)
}
/** @copydoc VBOXHDDBACKEND::pfnGetSize */
+static uint32_t rawGetSectorSize(void *pBackendData)
+{
+ LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+ PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+ uint32_t cb = 0;
+
+ AssertPtr(pImage);
+
+ if (pImage && pImage->pStorage)
+ cb = pImage->cbSector;
+
+ LogFlowFunc(("returns %u\n", cb));
+ return cb;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetSize */
static uint64_t rawGetSize(void *pBackendData)
{
LogFlowFunc(("pBackendData=%#p\n", pBackendData));
@@ -905,7 +883,9 @@ static int rawSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
int rc;
/* Image must be opened and the new flags must be valid. */
- if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL)))
+ if (!pImage || (uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
+ | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE
+ | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
{
rc = VERR_INVALID_PARAMETER;
goto out;
@@ -1144,54 +1124,6 @@ static void rawDump(void *pBackendData)
}
}
-/** @copydoc VBOXHDDBACKEND::pfnAsyncRead */
-static int rawAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbRead,
- PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
-{
- int rc = VINF_SUCCESS;
- PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-
- rc = vdIfIoIntFileReadUserAsync(pImage->pIfIo, pImage->pStorage, uOffset,
- pIoCtx, cbRead);
- if (RT_SUCCESS(rc))
- *pcbActuallyRead = cbRead;
-
- return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncWrite */
-static int rawAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbWrite,
- PVDIOCTX pIoCtx,
- size_t *pcbWriteProcess, size_t *pcbPreRead,
- size_t *pcbPostRead, unsigned fWrite)
-{
- int rc = VINF_SUCCESS;
- PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-
- rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pImage->pStorage, uOffset,
- pIoCtx, cbWrite, NULL, NULL);
- if (RT_SUCCESS(rc))
- {
- *pcbWriteProcess = cbWrite;
- *pcbPostRead = 0;
- *pcbPreRead = 0;
- }
-
- return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncFlush */
-static int rawAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
-{
- int rc = VINF_SUCCESS;
- PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-
- if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
- rc = vdIfIoIntFileFlushAsync(pImage->pIfIo, pImage->pStorage, pIoCtx,
- NULL, NULL);
-
- return rc;
-}
VBOXHDDBACKEND g_RawBackend =
@@ -1224,8 +1156,12 @@ VBOXHDDBACKEND g_RawBackend =
rawWrite,
/* pfnFlush */
rawFlush,
+ /* pfnDiscard */
+ NULL,
/* pfnGetVersion */
rawGetVersion,
+ /* pfnGetSectorSize */
+ rawGetSectorSize,
/* pfnGetSize */
rawGetSize,
/* pfnGetFileSize */
@@ -1276,12 +1212,6 @@ VBOXHDDBACKEND g_RawBackend =
NULL,
/* pfnSetParentFilename */
NULL,
- /* pfnAsyncRead */
- rawAsyncRead,
- /* pfnAsyncWrite */
- rawAsyncWrite,
- /* pfnAsyncFlush */
- rawAsyncFlush,
/* pfnComposeLocation */
genericFileComposeLocation,
/* pfnComposeName */
@@ -1290,10 +1220,6 @@ VBOXHDDBACKEND g_RawBackend =
NULL,
/* pfnResize */
NULL,
- /* pfnDiscard */
- NULL,
- /* pfnAsyncDiscard */
- NULL,
/* pfnRepair */
NULL
};
diff --git a/src/VBox/Storage/VCICache.cpp b/src/VBox/Storage/VCICache.cpp
index 1cc792ea..393aab1f 100644
--- a/src/VBox/Storage/VCICache.cpp
+++ b/src/VBox/Storage/VCICache.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2010 Oracle Corporation
+ * Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -535,7 +535,7 @@ static int vciBlkMapLoad(PVCICACHE pStorage, uint64_t offBlkMap, uint32_t cBlkMa
cBlkMap -= VCI_BYTE2BLOCK(sizeof(VciBlkMap));
rc = vdIfIoIntFileReadSync(pStorage->pIfIo, pStorage->pStorage, offBlkMap,
- &BlkMap, VCI_BYTE2BLOCK(sizeof(VciBlkMap)), NULL);
+ &BlkMap, VCI_BYTE2BLOCK(sizeof(VciBlkMap)));
if (RT_SUCCESS(rc))
{
offBlkMap += VCI_BYTE2BLOCK(sizeof(VciBlkMap));
@@ -574,7 +574,7 @@ static int vciBlkMapLoad(PVCICACHE pStorage, uint64_t offBlkMap, uint32_t cBlkMa
cBlocksRead = RT_MIN(VCI_BYTE2BLOCK(sizeof(abBitmapBuffer)), cBlocksLeft);
rc = vdIfIoIntFileReadSync(pStorage->pIfIo, pStorage->pStorage,
offBlkMap, abBitmapBuffer,
- cBlocksRead, NULL);
+ cBlocksRead);
if (RT_SUCCESS(rc))
{
@@ -647,7 +647,7 @@ static int vciBlkMapLoad(PVCICACHE pStorage, uint64_t offBlkMap, uint32_t cBlkMa
/* Read next chunk. */
cBlocksRead = RT_MIN(VCI_BYTE2BLOCK(sizeof(abBitmapBuffer)), cBlocksLeft);
rc = vdIfIoIntFileReadSync(pStorage->pIfIo, pStorage->pStorage,
- offBlkMap, abBitmapBuffer, cBlocksRead, NULL);
+ offBlkMap, abBitmapBuffer, cBlocksRead);
}
}
}
@@ -711,7 +711,7 @@ static int vciBlkMapSave(PVCIBLKMAP pBlkMap, PVCICACHE pStorage, uint64_t offBlk
BlkMap.cBlocksAllocData = RT_H2LE_U32(pBlkMap->cBlocksAllocData);
rc = vdIfIoIntFileWriteSync(pStorage->pIfIo, pStorage->pStorage, offBlkMap,
- &BlkMap, VCI_BYTE2BLOCK(sizeof(VciBlkMap)), NULL);
+ &BlkMap, VCI_BYTE2BLOCK(sizeof(VciBlkMap)));
if (RT_SUCCESS(rc))
{
uint8_t abBitmapBuffer[16*_1K];
@@ -742,7 +742,7 @@ static int vciBlkMapSave(PVCIBLKMAP pBlkMap, PVCICACHE pStorage, uint64_t offBlk
/* Buffer is full, write to file and reset. */
rc = vdIfIoIntFileWriteSync(pStorage->pIfIo, pStorage->pStorage,
offBlkMap, abBitmapBuffer,
- VCI_BYTE2BLOCK(sizeof(abBitmapBuffer)), NULL);
+ VCI_BYTE2BLOCK(sizeof(abBitmapBuffer)));
if (RT_FAILURE(rc))
break;
@@ -758,7 +758,7 @@ static int vciBlkMapSave(PVCIBLKMAP pBlkMap, PVCICACHE pStorage, uint64_t offBlk
if (RT_SUCCESS(rc) && iBit)
rc = vdIfIoIntFileWriteSync(pStorage->pIfIo, pStorage->pStorage,
- offBlkMap, abBitmapBuffer, VCI_BYTE2BLOCK(iBit / 8), NULL);
+ offBlkMap, abBitmapBuffer, VCI_BYTE2BLOCK(iBit / 8));
}
}
else
@@ -1109,7 +1109,7 @@ static PVCICACHEEXTENT vciCacheExtentLookup(PVCICACHE pCache, uint64_t offBlockO
/* Read from disk and add to the tree. */
rc = vdIfIoIntFileReadSync(pCache->pIfIo, pCache->pStorage,
VCI_BLOCK2BYTE(pInt->PtrChild.u.offAddrBlockNode),
- &NodeTree, sizeof(NodeTree), NULL);
+ &NodeTree, sizeof(NodeTree));
AssertRC(rc);
pNodeNew = vciTreeNodeImage2Host(pInt->PtrChild.u.offAddrBlockNode, &NodeTree);
@@ -1182,7 +1182,7 @@ static PVCICACHEEXTENT vciCacheExtentLookup(PVCICACHE pCache, uint64_t offBlockO
while (pInt)
{
-
+
}
}
}
@@ -1228,7 +1228,7 @@ static int vciOpenImage(PVCICACHE pCache, unsigned uOpenFlags)
}
rc = vdIfIoIntFileReadSync(pCache->pIfIo, pCache->pStorage, 0, &Hdr,
- VCI_BYTE2BLOCK(sizeof(Hdr)), NULL);
+ VCI_BYTE2BLOCK(sizeof(Hdr)));
if (RT_FAILURE(rc))
{
rc = VERR_VD_GEN_INVALID_HEADER;
@@ -1258,7 +1258,7 @@ static int vciOpenImage(PVCICACHE pCache, unsigned uOpenFlags)
rc = vdIfIoIntFileReadSync(pCache->pIfIo, pCache->pStorage,
pCache->offTreeRoot, &RootNode,
- VCI_BYTE2BLOCK(sizeof(VciTreeNode)), NULL);
+ VCI_BYTE2BLOCK(sizeof(VciTreeNode)));
if (RT_SUCCESS(rc))
{
pCache->pRoot = vciTreeNodeImage2Host(pCache->offTreeRoot, &RootNode);
@@ -1393,7 +1393,7 @@ static int vciCreateImage(PVCICACHE pCache, uint64_t cbSize,
Hdr.cBlkMap = RT_H2LE_U32(cBlkMap);
rc = vdIfIoIntFileWriteSync(pCache->pIfIo, pCache->pStorage, offHdr, &Hdr,
- VCI_BYTE2BLOCK(sizeof(VciHdr)), NULL);
+ VCI_BYTE2BLOCK(sizeof(VciHdr)));
if (RT_FAILURE(rc))
{
rc = vdIfError(pCache->pIfError, rc, RT_SRC_POS, N_("VCI: cannot write header '%s'"), pCache->pszFilename);
@@ -1412,7 +1412,7 @@ static int vciCreateImage(PVCICACHE pCache, uint64_t cbSize,
NodeRoot.u8Type = RT_H2LE_U32(VCI_TREE_NODE_TYPE_LEAF);
rc = vdIfIoIntFileWriteSync(pCache->pIfIo, pCache->pStorage, offTreeRoot,
- &NodeRoot, VCI_BYTE2BLOCK(sizeof(VciTreeNode)), NULL);
+ &NodeRoot, VCI_BYTE2BLOCK(sizeof(VciTreeNode)));
if (RT_FAILURE(rc))
{
rc = vdIfError(pCache->pIfError, rc, RT_SRC_POS, N_("VCI: cannot write root node '%s'"), pCache->pszFilename);
@@ -1466,7 +1466,7 @@ static int vciProbe(const char *pszFilename, PVDINTERFACE pVDIfsCache,
goto out;
}
- rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0, &Hdr, sizeof(Hdr), NULL);
+ rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0, &Hdr, sizeof(Hdr));
if (RT_FAILURE(rc))
{
rc = VERR_VD_GEN_INVALID_HEADER;
@@ -1631,10 +1631,11 @@ static int vciClose(void *pBackendData, bool fDelete)
}
/** @copydoc VDCACHEBACKEND::pfnRead */
-static int vciRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
- size_t cbToRead, size_t *pcbActuallyRead)
+static int vciRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
+ PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
+ LogFlowFunc(("pBackendData=%#p uOffset=%llu cbToRead=%zu pIoCtx=%#p pcbActuallyRead=%#p\n",
+ pBackendData, uOffset, cbToRead, pIoCtx, pcbActuallyRead));
PVCICACHE pCache = (PVCICACHE)pBackendData;
int rc = VINF_SUCCESS;
PVCICACHEEXTENT pExtent;
@@ -1651,9 +1652,9 @@ static int vciRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
uint64_t offRead = offBlockAddr - pExtent->u64BlockOffset;
cBlocksToRead = RT_MIN(cBlocksToRead, pExtent->u32Blocks - offRead);
- rc = vdIfIoIntFileReadSync(pCache->pIfIo, pCache->pStorage,
+ rc = vdIfIoIntFileReadUser(pCache->pIfIo, pCache->pStorage,
pExtent->u64BlockAddr + offRead,
- pvBuf, cBlocksToRead, NULL);
+ pIoCtx, cBlocksToRead);
}
else
{
@@ -1665,22 +1666,20 @@ static int vciRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
if (pcbActuallyRead)
*pcbActuallyRead = VCI_BLOCK2BYTE(cBlocksToRead);
-out:
LogFlowFunc(("returns %Rrc\n", rc));
return rc;
}
/** @copydoc VDCACHEBACKEND::pfnWrite */
-static int vciWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
- size_t cbToWrite, size_t *pcbWriteProcess)
+static int vciWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
+ PVDIOCTX pIoCtx, size_t *pcbWriteProcess)
{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p\n",
- pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess));
+ LogFlowFunc(("pBackendData=%#p uOffset=%llu cbToWrite=%zu pIoCtx=%#p pcbWriteProcess=%#p\n",
+ pBackendData, uOffset, cbToWrite, pIoCtx, pcbWriteProcess));
PVCICACHE pCache = (PVCICACHE)pBackendData;
int rc = VINF_SUCCESS;
uint64_t cBlocksToWrite = VCI_BYTE2BLOCK(cbToWrite);
uint64_t offBlockAddr = VCI_BYTE2BLOCK(uOffset);
- PVCICACHEEXTENT pExtent;
AssertPtr(pCache);
Assert(uOffset % 512 == 0);
@@ -1688,17 +1687,17 @@ static int vciWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
while (cBlocksToWrite)
{
-
+
}
*pcbWriteProcess = cbToWrite; /** @todo: Implement. */
-out:
+
LogFlowFunc(("returns %Rrc\n", rc));
return rc;
}
/** @copydoc VDCACHEBACKEND::pfnFlush */
-static int vciFlush(void *pBackendData)
+static int vciFlush(void *pBackendData, PVDIOCTX pIoCtx)
{
LogFlowFunc(("pBackendData=%#p\n", pBackendData));
PVCICACHE pCache = (PVCICACHE)pBackendData;
@@ -1863,7 +1862,6 @@ static int vciSetComment(void *pBackendData, const char *pszComment)
else
rc = VERR_VD_NOT_OPENED;
-out:
LogFlowFunc(("returns %Rrc\n", rc));
return rc;
}
@@ -1957,35 +1955,6 @@ static void vciDump(void *pBackendData)
NOREF(pBackendData);
}
-/** @copydoc VDCACHEBACKEND::pfnAsyncRead */
-static int vciAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbRead,
- PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
-{
- int rc = VERR_NOT_SUPPORTED;
- PVCICACHE pCache = (PVCICACHE)pBackendData;
-
- return rc;
-}
-
-/** @copydoc VDCACHEBACKEND::pfnAsyncWrite */
-static int vciAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbWrite,
- PVDIOCTX pIoCtx, size_t *pcbWriteProcess)
-{
- int rc = VERR_NOT_SUPPORTED;
- PVCICACHE pCache = (PVCICACHE)pBackendData;
-
- return rc;
-}
-
-/** @copydoc VDCACHEBACKEND::pfnAsyncFlush */
-static int vciAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
-{
- int rc = VERR_NOT_SUPPORTED;
- PVCICACHE pCache = (PVCICACHE)pBackendData;
-
- return rc;
-}
-
VDCACHEBACKEND g_VciCacheBackend =
{
@@ -2015,6 +1984,8 @@ VDCACHEBACKEND g_VciCacheBackend =
vciWrite,
/* pfnFlush */
vciFlush,
+ /* pfnDiscard */
+ NULL,
/* pfnGetVersion */
vciGetVersion,
/* pfnGetSize */
@@ -2041,12 +2012,6 @@ VDCACHEBACKEND g_VciCacheBackend =
vciSetModificationUuid,
/* pfnDump */
vciDump,
- /* pfnAsyncRead */
- vciAsyncRead,
- /* pfnAsyncWrite */
- vciAsyncWrite,
- /* pfnAsyncFlush */
- vciAsyncFlush,
/* pfnComposeLocation */
NULL,
/* pfnComposeName */
diff --git a/src/VBox/Storage/VD.cpp b/src/VBox/Storage/VD.cpp
index a4ae99e5..5632e133 100644
--- a/src/VBox/Storage/VD.cpp
+++ b/src/VBox/Storage/VD.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2012 Oracle Corporation
+ * Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -36,9 +36,9 @@
#include <iprt/param.h>
#include <iprt/memcache.h>
#include <iprt/sg.h>
-#include <iprt/critsect.h>
#include <iprt/list.h>
#include <iprt/avl.h>
+#include <iprt/semaphore.h>
#include <VBox/vd-plugin.h>
#include <VBox/vd-cache-plugin.h>
@@ -97,6 +97,9 @@ typedef struct VDIO
bool fIgnoreFlush;
} VDIO, *PVDIO;
+/** Forward declaration of an I/O task */
+typedef struct VDIOTASK *PVDIOTASK;
+
/**
* VBox HDD Container image descriptor.
*/
@@ -234,31 +237,41 @@ struct VBOXHDD
RTMEMCACHE hMemCacheIoCtx;
/** Memory cache for I/O tasks. */
RTMEMCACHE hMemCacheIoTask;
- /** Critical section protecting the disk against concurrent access. */
- RTCRITSECT CritSect;
- /** Head of queued I/O contexts - LIFO order. */
- volatile PVDIOCTX pIoCtxHead;
- /** Flag whether the disk is currently locked by growing write or a flush
- * request. Other flush or growing write requests need to wait until
- * the current one completes.
- */
+ /** An I/O context is currently using the disk structures
+ * Every I/O context must be placed on one of the lists below. */
volatile bool fLocked;
- /** List of waiting requests. - Protected by the critical section. */
- RTLISTNODE ListWriteLocked;
- /** I/O context which locked the disk. */
- PVDIOCTX pIoCtxLockOwner;
+ /** Head of pending I/O tasks waiting for completion - LIFO order. */
+ volatile PVDIOTASK pIoTasksPendingHead;
+ /** Head of newly queued I/O contexts - LIFO order. */
+ volatile PVDIOCTX pIoCtxHead;
+ /** Head of halted I/O contexts which are given back to generic
+ * disk framework by the backend. - LIFO order. */
+ volatile PVDIOCTX pIoCtxHaltedHead;
+
+ /** Head of blocked I/O contexts, processed only
+ * after pIoCtxLockOwner was freed - LIFO order. */
+ volatile PVDIOCTX pIoCtxBlockedHead;
+ /** I/O context which locked the disk for a growing write or flush request.
+ * Other flush or growing write requests need to wait until
+ * the current one completes. - NIL_VDIOCTX if unlocked. */
+ volatile PVDIOCTX pIoCtxLockOwner;
/** Pointer to the L2 disk cache if any. */
PVDCACHE pCache;
/** Pointer to the discard state if any. */
PVDDISCARDSTATE pDiscard;
+
+ /** Event semaphore for synchronous I/O. */
+ RTSEMEVENT hEventSemSyncIo;
+ /** Status code of the last synchronous I/O request. */
+ int rcSync;
};
-# define VD_THREAD_IS_CRITSECT_OWNER(Disk) \
+# define VD_IS_LOCKED(a_pDisk) \
do \
{ \
- AssertMsg(RTCritSectIsOwner(&Disk->CritSect), \
- ("Thread does not own critical section\n"));\
+ AssertMsg(a_pDisk->fLocked, \
+ ("Lock not held\n"));\
} while(0)
/**
@@ -305,8 +318,8 @@ typedef struct VDIOCTX
PVBOXHDD pDisk;
/** Return code. */
int rcReq;
- /** Flag whether the I/O context is blocked because it is in the growing list. */
- bool fBlocked;
+ /** Various flags for the I/O context. */
+ uint32_t fFlags;
/** Number of data transfers currently pending. */
volatile uint32_t cDataTransfersPending;
/** How many meta data transfers are pending. */
@@ -341,6 +354,12 @@ typedef struct VDIOCTX
PVDIMAGE pImageStart;
/** S/G buffer */
RTSGBUF SgBuf;
+ /** Number of bytes to clear in the buffer before the current read. */
+ size_t cbBufClear;
+ /** Number of images to read. */
+ unsigned cImagesRead;
+ /** Override for the parent image to start reading from. */
+ PVDIMAGE pImageParentOverride;
} Io;
/** Discard requests. */
struct
@@ -409,6 +428,31 @@ typedef struct VDIOCTX
} Type;
} VDIOCTX;
+/** Default flags for an I/O context, i.e. unblocked and async. */
+#define VDIOCTX_FLAGS_DEFAULT (0)
+/** Flag whether the context is blocked. */
+#define VDIOCTX_FLAGS_BLOCKED RT_BIT_32(0)
+/** Flag whether the I/O context is using synchronous I/O. */
+#define VDIOCTX_FLAGS_SYNC RT_BIT_32(1)
+/** Flag whether the read should update the cache. */
+#define VDIOCTX_FLAGS_READ_UPDATE_CACHE RT_BIT_32(2)
+/** Flag whether free blocks should be zeroed.
+ * If false and no image has data for sepcified
+ * range VERR_VD_BLOCK_FREE is returned for the I/O context.
+ * Note that unallocated blocks are still zeroed
+ * if at least one image has valid data for a part
+ * of the range.
+ */
+#define VDIOCTX_FLAGS_ZERO_FREE_BLOCKS RT_BIT_32(3)
+/** Don't free the I/O context when complete because
+ * it was alloacted elsewhere (stack, ...). */
+#define VDIOCTX_FLAGS_DONT_FREE RT_BIT_32(4)
+/* Don't set the modified flag for this I/O context when writing. */
+#define VDIOCTX_FLAGS_DONT_SET_MODIFIED_FLAG RT_BIT_32(5)
+
+/** NIL I/O context pointer value. */
+#define NIL_VDIOCTX ((PVDIOCTX)0)
+
/**
* List node for deferred I/O contexts.
*/
@@ -429,12 +473,16 @@ typedef struct VDIOCTXDEFERRED
*/
typedef struct VDIOTASK
{
+ /** Next I/O task waiting in the list. */
+ struct VDIOTASK * volatile pNext;
/** Storage this task belongs to. */
PVDIOSTORAGE pIoStorage;
/** Optional completion callback. */
PFNVDXFERCOMPLETED pfnComplete;
/** Opaque user data. */
void *pvUser;
+ /** Completion status code for the task. */
+ int rcReq;
/** Flag whether this is a meta data transfer. */
bool fMeta;
/** Type dependent data. */
@@ -455,7 +503,7 @@ typedef struct VDIOTASK
PVDMETAXFER pMetaXfer;
} Meta;
} Type;
-} VDIOTASK, *PVDIOTASK;
+} VDIOTASK;
/**
* Storage handle.
@@ -549,6 +597,10 @@ static PVDCACHEBACKEND aStaticCacheBackends[] =
/** Forward declaration of the async discard helper. */
static int vdDiscardHelperAsync(PVDIOCTX pIoCtx);
+static int vdWriteHelperAsync(PVDIOCTX pIoCtx);
+static void vdDiskProcessBlockedIoCtx(PVBOXHDD pDisk);
+static int vdDiskUnlock(PVBOXHDD pDisk, PVDIOCTX pIoCtxRc);
+static DECLCALLBACK(void) vdIoCtxSyncComplete(void *pvUser1, void *pvUser2, int rcReq);
/**
* internal: add several backends.
@@ -765,6 +817,41 @@ static PVDIMAGE vdGetImageByNumber(PVBOXHDD pDisk, unsigned nImage)
}
/**
+ * Initialize the structure members of a given I/O context.
+ */
+DECLINLINE(void) vdIoCtxInit(PVDIOCTX pIoCtx, PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
+ uint64_t uOffset, size_t cbTransfer, PVDIMAGE pImageStart,
+ PCRTSGBUF pcSgBuf, void *pvAllocation,
+ PFNVDIOCTXTRANSFER pfnIoCtxTransfer, uint32_t fFlags)
+{
+ pIoCtx->pDisk = pDisk;
+ pIoCtx->enmTxDir = enmTxDir;
+ pIoCtx->Req.Io.cbTransferLeft = (uint32_t)cbTransfer; Assert((uint32_t)cbTransfer == cbTransfer);
+ pIoCtx->Req.Io.uOffset = uOffset;
+ pIoCtx->Req.Io.cbTransfer = cbTransfer;
+ pIoCtx->Req.Io.pImageStart = pImageStart;
+ pIoCtx->Req.Io.pImageCur = pImageStart;
+ pIoCtx->Req.Io.cbBufClear = 0;
+ pIoCtx->Req.Io.pImageParentOverride = NULL;
+ pIoCtx->cDataTransfersPending = 0;
+ pIoCtx->cMetaTransfersPending = 0;
+ pIoCtx->fComplete = false;
+ pIoCtx->fFlags = fFlags;
+ pIoCtx->pvAllocation = pvAllocation;
+ pIoCtx->pfnIoCtxTransfer = pfnIoCtxTransfer;
+ pIoCtx->pfnIoCtxTransferNext = NULL;
+ pIoCtx->rcReq = VINF_SUCCESS;
+ pIoCtx->pIoCtxParent = NULL;
+
+ /* There is no S/G list for a flush request. */
+ if ( enmTxDir != VDIOCTXTXDIR_FLUSH
+ && enmTxDir != VDIOCTXTXDIR_DISCARD)
+ RTSgBufClone(&pIoCtx->Req.Io.SgBuf, pcSgBuf);
+ else
+ memset(&pIoCtx->Req.Io.SgBuf, 0, sizeof(RTSGBUF));
+}
+
+/**
* Internal: Tries to read the desired range from the given cache.
*
* @returns VBox status code.
@@ -773,8 +860,8 @@ static PVDIMAGE vdGetImageByNumber(PVBOXHDD pDisk, unsigned nImage)
* Everything thereafter might be in the cache.
* @param pCache The cache to read from.
* @param uOffset Offset of the virtual disk to read.
- * @param pvBuf Where to store the read data.
* @param cbRead How much to read.
+ * @param pIoCtx The I/O context to read into.
* @param pcbRead Where to store the number of bytes actually read.
* On success this indicates the number of bytes read from the cache.
* If VERR_VD_BLOCK_FREE is returned this gives the number of bytes
@@ -783,18 +870,18 @@ static PVDIMAGE vdGetImageByNumber(PVBOXHDD pDisk, unsigned nImage)
* might or might not be in the cache.
*/
static int vdCacheReadHelper(PVDCACHE pCache, uint64_t uOffset,
- void *pvBuf, size_t cbRead, size_t *pcbRead)
+ size_t cbRead, PVDIOCTX pIoCtx, size_t *pcbRead)
{
int rc = VINF_SUCCESS;
- LogFlowFunc(("pCache=%#p uOffset=%llu pvBuf=%#p cbRead=%zu pcbRead=%#p\n",
- pCache, uOffset, pvBuf, cbRead, pcbRead));
+ LogFlowFunc(("pCache=%#p uOffset=%llu pIoCtx=%p cbRead=%zu pcbRead=%#p\n",
+ pCache, uOffset, pIoCtx, cbRead, pcbRead));
AssertPtr(pCache);
AssertPtr(pcbRead);
- rc = pCache->Backend->pfnRead(pCache->pBackendData, uOffset, pvBuf,
- cbRead, pcbRead);
+ rc = pCache->Backend->pfnRead(pCache->pBackendData, uOffset, cbRead,
+ pIoCtx, pcbRead);
LogFlowFunc(("returns rc=%Rrc pcbRead=%zu\n", rc, *pcbRead));
return rc;
@@ -806,38 +893,38 @@ static int vdCacheReadHelper(PVDCACHE pCache, uint64_t uOffset,
* @returns VBox status code.
* @param pCache The cache to write to.
* @param uOffset Offset of the virtual disk to write to the cache.
- * @param pcvBuf The data to write.
* @param cbWrite How much to write.
+ * @param pIoCtx The I/O context to ẃrite from.
* @param pcbWritten How much data could be written, optional.
*/
-static int vdCacheWriteHelper(PVDCACHE pCache, uint64_t uOffset, const void *pcvBuf,
- size_t cbWrite, size_t *pcbWritten)
+static int vdCacheWriteHelper(PVDCACHE pCache, uint64_t uOffset, size_t cbWrite,
+ PVDIOCTX pIoCtx, size_t *pcbWritten)
{
int rc = VINF_SUCCESS;
- LogFlowFunc(("pCache=%#p uOffset=%llu pvBuf=%#p cbWrite=%zu pcbWritten=%#p\n",
- pCache, uOffset, pcvBuf, cbWrite, pcbWritten));
+ LogFlowFunc(("pCache=%#p uOffset=%llu pIoCtx=%p cbWrite=%zu pcbWritten=%#p\n",
+ pCache, uOffset, pIoCtx, cbWrite, pcbWritten));
AssertPtr(pCache);
- AssertPtr(pcvBuf);
+ AssertPtr(pIoCtx);
Assert(cbWrite > 0);
if (pcbWritten)
- rc = pCache->Backend->pfnWrite(pCache->pBackendData, uOffset, pcvBuf,
- cbWrite, pcbWritten);
+ rc = pCache->Backend->pfnWrite(pCache->pBackendData, uOffset, cbWrite,
+ pIoCtx, pcbWritten);
else
{
size_t cbWritten = 0;
do
{
- rc = pCache->Backend->pfnWrite(pCache->pBackendData, uOffset, pcvBuf,
- cbWrite, &cbWritten);
+ rc = pCache->Backend->pfnWrite(pCache->pBackendData, uOffset, cbWrite,
+ pIoCtx, &cbWritten);
uOffset += cbWritten;
- pcvBuf = (char *)pcvBuf + cbWritten;
cbWrite -= cbWritten;
} while ( cbWrite
- && RT_SUCCESS(rc));
+ && ( RT_SUCCESS(rc)
+ || rc == VERR_VD_ASYNC_IO_IN_PROGRESS));
}
LogFlowFunc(("returns rc=%Rrc pcbWritten=%zu\n",
@@ -846,185 +933,6 @@ static int vdCacheWriteHelper(PVDCACHE pCache, uint64_t uOffset, const void *pcv
}
/**
- * Internal: Reads a given amount of data from the image chain of the disk.
- **/
-static int vdDiskReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
- uint64_t uOffset, void *pvBuf, size_t cbRead, size_t *pcbThisRead)
-{
- int rc = VINF_SUCCESS;
- size_t cbThisRead = cbRead;
-
- AssertPtr(pcbThisRead);
-
- *pcbThisRead = 0;
-
- /*
- * Try to read from the given image.
- * If the block is not allocated read from override chain if present.
- */
- rc = pImage->Backend->pfnRead(pImage->pBackendData,
- uOffset, pvBuf, cbThisRead,
- &cbThisRead);
-
- if (rc == VERR_VD_BLOCK_FREE)
- {
- for (PVDIMAGE pCurrImage = pImageParentOverride ? pImageParentOverride : pImage->pPrev;
- pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
- pCurrImage = pCurrImage->pPrev)
- {
- rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
- uOffset, pvBuf, cbThisRead,
- &cbThisRead);
- }
- }
-
- if (RT_SUCCESS(rc) || rc == VERR_VD_BLOCK_FREE)
- *pcbThisRead = cbThisRead;
-
- return rc;
-}
-
-/**
- * Extended version of vdReadHelper(), implementing certain optimizations
- * for image cloning.
- *
- * @returns VBox status code.
- * @param pDisk The disk to read from.
- * @param pImage The image to start reading from.
- * @param pImageParentOverride The parent image to read from
- * if the starting image returns a free block.
- * If NULL is passed the real parent of the image
- * in the chain is used.
- * @param uOffset Offset in the disk to start reading from.
- * @param pvBuf Where to store the read data.
- * @param cbRead How much to read.
- * @param fZeroFreeBlocks Flag whether free blocks should be zeroed.
- * If false and no image has data for sepcified
- * range VERR_VD_BLOCK_FREE is returned.
- * Note that unallocated blocks are still zeroed
- * if at least one image has valid data for a part
- * of the range.
- * @param fUpdateCache Flag whether to update the attached cache if
- * available.
- * @param cImagesRead Number of images in the chain to read until
- * the read is cut off. A value of 0 disables the cut off.
- */
-static int vdReadHelperEx(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
- uint64_t uOffset, void *pvBuf, size_t cbRead,
- bool fZeroFreeBlocks, bool fUpdateCache, unsigned cImagesRead)
-{
- int rc = VINF_SUCCESS;
- size_t cbThisRead;
- bool fAllFree = true;
- size_t cbBufClear = 0;
-
- /* Loop until all read. */
- do
- {
- /* Search for image with allocated block. Do not attempt to read more
- * than the previous reads marked as valid. Otherwise this would return
- * stale data when different block sizes are used for the images. */
- cbThisRead = cbRead;
-
- if ( pDisk->pCache
- && !pImageParentOverride)
- {
- rc = vdCacheReadHelper(pDisk->pCache, uOffset, pvBuf,
- cbThisRead, &cbThisRead);
-
- if (rc == VERR_VD_BLOCK_FREE)
- {
- rc = vdDiskReadHelper(pDisk, pImage, NULL, uOffset, pvBuf, cbThisRead,
- &cbThisRead);
-
- /* If the read was successful, write the data back into the cache. */
- if ( RT_SUCCESS(rc)
- && fUpdateCache)
- {
- rc = vdCacheWriteHelper(pDisk->pCache, uOffset, pvBuf,
- cbThisRead, NULL);
- }
- }
- }
- else
- {
- /** @todo can be be replaced by vdDiskReadHelper if it proves to be reliable,
- * don't want to be responsible for data corruption...
- */
- /*
- * Try to read from the given image.
- * If the block is not allocated read from override chain if present.
- */
- rc = pImage->Backend->pfnRead(pImage->pBackendData,
- uOffset, pvBuf, cbThisRead,
- &cbThisRead);
-
- if ( rc == VERR_VD_BLOCK_FREE
- && cImagesRead != 1)
- {
- unsigned cImagesToProcess = cImagesRead;
-
- for (PVDIMAGE pCurrImage = pImageParentOverride ? pImageParentOverride : pImage->pPrev;
- pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
- pCurrImage = pCurrImage->pPrev)
- {
- rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
- uOffset, pvBuf, cbThisRead,
- &cbThisRead);
- if (cImagesToProcess == 1)
- break;
- else if (cImagesToProcess > 0)
- cImagesToProcess--;
- }
- }
- }
-
- /* No image in the chain contains the data for the block. */
- if (rc == VERR_VD_BLOCK_FREE)
- {
- /* Fill the free space with 0 if we are told to do so
- * or a previous read returned valid data. */
- if (fZeroFreeBlocks || !fAllFree)
- memset(pvBuf, '\0', cbThisRead);
- else
- cbBufClear += cbThisRead;
-
- if (pImage->uOpenFlags & VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS)
- rc = VINF_VD_NEW_ZEROED_BLOCK;
- else
- rc = VINF_SUCCESS;
- }
- else if (RT_SUCCESS(rc))
- {
- /* First not free block, fill the space before with 0. */
- if (!fZeroFreeBlocks)
- {
- memset((char *)pvBuf - cbBufClear, '\0', cbBufClear);
- cbBufClear = 0;
- fAllFree = false;
- }
- }
-
- cbRead -= cbThisRead;
- uOffset += cbThisRead;
- pvBuf = (char *)pvBuf + cbThisRead;
- } while (cbRead != 0 && RT_SUCCESS(rc));
-
- return (!fZeroFreeBlocks && fAllFree) ? VERR_VD_BLOCK_FREE : rc;
-}
-
-/**
- * internal: read the specified amount of data in whatever blocks the backend
- * will give us.
- */
-static int vdReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, uint64_t uOffset,
- void *pvBuf, size_t cbRead, bool fUpdateCache)
-{
- return vdReadHelperEx(pDisk, pImage, NULL, uOffset, pvBuf, cbRead,
- true /* fZeroFreeBlocks */, fUpdateCache, 0);
-}
-
-/**
* Creates a new empty discard state.
*
* @returns Pointer to the new discard state or NULL if out of memory.
@@ -1075,7 +983,7 @@ static int vdDiscardRemoveBlocks(PVBOXHDD pDisk, PVDDISCARDSTATE pDiscard, size_
uint32_t idxStart = 0;
size_t cbLeft = pBlock->cbDiscard;
bool fAllocated = ASMBitTest(pBlock->pbmAllocated, idxStart);
- uint32_t cSectors = pBlock->cbDiscard / 512;
+ uint32_t cSectors = (uint32_t)(pBlock->cbDiscard / 512);
while (cbLeft > 0)
{
@@ -1099,9 +1007,14 @@ static int vdDiscardRemoveBlocks(PVBOXHDD pDisk, PVDDISCARDSTATE pDiscard, size_
if (idxEnd != -1)
cbThis = (idxEnd - idxStart) * 512;
- rc = pDisk->pLast->Backend->pfnDiscard(pDisk->pLast->pBackendData, offStart,
- cbThis, NULL, NULL, &cbThis,
- NULL, VD_DISCARD_MARK_UNUSED);
+
+ VDIOCTX IoCtx;
+ vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_DISCARD, 0, 0, NULL,
+ NULL, NULL, NULL, VDIOCTX_FLAGS_SYNC);
+ rc = pDisk->pLast->Backend->pfnDiscard(pDisk->pLast->pBackendData,
+ &IoCtx, offStart, cbThis, NULL,
+ NULL, &cbThis, NULL,
+ VD_DISCARD_MARK_UNUSED);
if (RT_FAILURE(rc))
break;
@@ -1154,167 +1067,6 @@ static int vdDiscardStateDestroy(PVBOXHDD pDisk)
}
/**
- * Discards the given range from the underlying block.
- *
- * @returns VBox status code.
- * @param pDisk VD container data.
- * @param offStart Where to start discarding.
- * @param cbDiscard How many bytes to discard.
- */
-static int vdDiscardRange(PVBOXHDD pDisk, PVDDISCARDSTATE pDiscard, uint64_t offStart, size_t cbDiscard)
-{
- int rc = VINF_SUCCESS;
-
- LogFlowFunc(("pDisk=%#p pDiscard=%#p offStart=%llu cbDiscard=%zu\n",
- pDisk, pDiscard, offStart, cbDiscard));
-
- do
- {
- size_t cbThisDiscard;
-
- /* Look for a matching block in the AVL tree first. */
- PVDDISCARDBLOCK pBlock = (PVDDISCARDBLOCK)RTAvlrU64GetBestFit(pDiscard->pTreeBlocks, offStart, false);
- if (!pBlock || pBlock->Core.KeyLast < offStart)
- {
- void *pbmAllocated = NULL;
- size_t cbPreAllocated, cbPostAllocated;
- PVDDISCARDBLOCK pBlockAbove = (PVDDISCARDBLOCK)RTAvlrU64GetBestFit(pDiscard->pTreeBlocks, offStart, true);
-
- /* Clip range to remain in the current block. */
- if (pBlockAbove)
- cbThisDiscard = RT_MIN(cbDiscard, pBlockAbove->Core.KeyLast - offStart + 1);
- else
- cbThisDiscard = cbDiscard;
-
- Assert(!(cbThisDiscard % 512));
-
- /* No block found, try to discard using the backend first. */
- rc = pDisk->pLast->Backend->pfnDiscard(pDisk->pLast->pBackendData, offStart,
- cbThisDiscard, &cbPreAllocated,
- &cbPostAllocated, &cbThisDiscard,
- &pbmAllocated, 0);
- if (rc == VERR_VD_DISCARD_ALIGNMENT_NOT_MET)
- {
- /* Create new discard block. */
- pBlock = (PVDDISCARDBLOCK)RTMemAllocZ(sizeof(VDDISCARDBLOCK));
- if (pBlock)
- {
- pBlock->Core.Key = offStart - cbPreAllocated;
- pBlock->Core.KeyLast = offStart + cbThisDiscard + cbPostAllocated - 1;
- pBlock->cbDiscard = cbPreAllocated + cbThisDiscard + cbPostAllocated;
- pBlock->pbmAllocated = pbmAllocated;
- bool fInserted = RTAvlrU64Insert(pDiscard->pTreeBlocks, &pBlock->Core);
- Assert(fInserted);
-
- RTListPrepend(&pDiscard->ListLru, &pBlock->NodeLru);
- pDiscard->cbDiscarding += pBlock->cbDiscard;
- if (pDiscard->cbDiscarding > VD_DISCARD_REMOVE_THRESHOLD)
- rc = vdDiscardRemoveBlocks(pDisk, pDiscard, VD_DISCARD_REMOVE_THRESHOLD);
- else
- rc = VINF_SUCCESS;
- }
- else
- {
- RTMemFree(pbmAllocated);
- rc = VERR_NO_MEMORY;
- }
- }
- }
- else
- {
- /* Range lies partly in the block, update allocation bitmap. */
- int32_t idxStart, idxEnd;
-
- cbThisDiscard = RT_MIN(cbDiscard, pBlock->Core.KeyLast - offStart + 1);
-
- AssertPtr(pBlock);
-
- Assert(!(cbThisDiscard % 512));
- Assert(!((offStart - pBlock->Core.Key) % 512));
-
- idxStart = (offStart - pBlock->Core.Key) / 512;
- idxEnd = idxStart + (cbThisDiscard / 512);
-
- ASMBitClearRange(pBlock->pbmAllocated, idxStart, idxEnd);
-
- /* Call the backend to discard the block if it is completely unallocated now. */
- if (ASMBitFirstSet((volatile void *)pBlock->pbmAllocated, pBlock->cbDiscard / 512) == -1)
- {
- size_t cbPreAllocated, cbPostAllocated, cbActuallyDiscarded;
-
- rc = pDisk->pLast->Backend->pfnDiscard(pDisk->pLast->pBackendData, pBlock->Core.Key,
- pBlock->cbDiscard, &cbPreAllocated,
- &cbPostAllocated, &cbActuallyDiscarded,
- NULL, 0);
- Assert(rc != VERR_VD_DISCARD_ALIGNMENT_NOT_MET);
- Assert(!cbPreAllocated);
- Assert(!cbPostAllocated);
- Assert(cbActuallyDiscarded == pBlock->cbDiscard || RT_FAILURE(rc));
-
- /* Remove the block on success. */
- if (RT_SUCCESS(rc))
- {
- PVDDISCARDBLOCK pBlockRemove = (PVDDISCARDBLOCK)RTAvlrU64RangeRemove(pDiscard->pTreeBlocks, pBlock->Core.Key);
- Assert(pBlockRemove == pBlock);
-
- pDiscard->cbDiscarding -= pBlock->cbDiscard;
- RTListNodeRemove(&pBlock->NodeLru);
- RTMemFree(pBlock->pbmAllocated);
- RTMemFree(pBlock);
- }
- }
- else
- {
- RTListNodeRemove(&pBlock->NodeLru);
- RTListPrepend(&pDiscard->ListLru, &pBlock->NodeLru);
- rc = VINF_SUCCESS;
- }
- }
-
- Assert(cbDiscard >= cbThisDiscard);
-
- cbDiscard -= cbThisDiscard;
- offStart += cbThisDiscard;
- } while (cbDiscard != 0 && RT_SUCCESS(rc));
-
- LogFlowFunc(("returns rc=%Rrc\n", rc));
- return rc;
-}
-
-/**
- * Discard helper.
- *
- * @returns VBox status code.
- * @param pDisk VD container data.
- * @param paRanges The array of ranges to discard.
- * @param cRanges The number of ranges in the array.
- */
-static int vdDiscardHelper(PVBOXHDD pDisk, PCRTRANGE paRanges, unsigned cRanges)
-{
- int rc = VINF_SUCCESS;
- PVDDISCARDSTATE pDiscard = pDisk->pDiscard;
-
- if (RT_UNLIKELY(!pDiscard))
- {
- pDiscard = vdDiscardStateCreate();
- if (!pDiscard)
- return VERR_NO_MEMORY;
-
- pDisk->pDiscard = pDiscard;
- }
-
- /* Go over the range array and discard individual blocks. */
- for (unsigned i = 0; i < cRanges; i++)
- {
- rc = vdDiscardRange(pDisk, pDiscard, paRanges[i].offStart, paRanges[i].cbRange);
- if (RT_FAILURE(rc))
- break;
- }
-
- return rc;
-}
-
-/**
* Marks the given range as allocated in the image.
* Required if there are discards in progress and a write to a block which can get discarded
* is written to.
@@ -1346,7 +1098,7 @@ static int vdDiscardSetRangeAllocated(PVBOXHDD pDisk, uint64_t uOffset, size_t c
cbThisRange = RT_MIN(cbThisRange, pBlock->Core.KeyLast - uOffset + 1);
idxStart = (uOffset - pBlock->Core.Key) / 512;
- idxEnd = idxStart + (cbThisRange / 512);
+ idxEnd = idxStart + (int32_t)(cbThisRange / 512);
ASMBitSetRange(pBlock->pbmAllocated, idxStart, idxEnd);
}
else
@@ -1368,36 +1120,17 @@ static int vdDiscardSetRangeAllocated(PVBOXHDD pDisk, uint64_t uOffset, size_t c
DECLINLINE(PVDIOCTX) vdIoCtxAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
uint64_t uOffset, size_t cbTransfer,
- PVDIMAGE pImageStart,
- PCRTSGBUF pcSgBuf, void *pvAllocation,
- PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
+ PVDIMAGE pImageStart,PCRTSGBUF pcSgBuf,
+ void *pvAllocation, PFNVDIOCTXTRANSFER pfnIoCtxTransfer,
+ uint32_t fFlags)
{
PVDIOCTX pIoCtx = NULL;
pIoCtx = (PVDIOCTX)RTMemCacheAlloc(pDisk->hMemCacheIoCtx);
if (RT_LIKELY(pIoCtx))
{
- pIoCtx->pDisk = pDisk;
- pIoCtx->enmTxDir = enmTxDir;
- pIoCtx->Req.Io.cbTransferLeft = cbTransfer;
- pIoCtx->Req.Io.uOffset = uOffset;
- pIoCtx->Req.Io.cbTransfer = cbTransfer;
- pIoCtx->Req.Io.pImageStart = pImageStart;
- pIoCtx->Req.Io.pImageCur = pImageStart;
- pIoCtx->cDataTransfersPending = 0;
- pIoCtx->cMetaTransfersPending = 0;
- pIoCtx->fComplete = false;
- pIoCtx->fBlocked = false;
- pIoCtx->pvAllocation = pvAllocation;
- pIoCtx->pfnIoCtxTransfer = pfnIoCtxTransfer;
- pIoCtx->pfnIoCtxTransferNext = NULL;
- pIoCtx->rcReq = VINF_SUCCESS;
-
- /* There is no S/G list for a flush request. */
- if (enmTxDir != VDIOCTXTXDIR_FLUSH)
- RTSgBufClone(&pIoCtx->Req.Io.SgBuf, pcSgBuf);
- else
- memset(&pIoCtx->Req.Io.SgBuf, 0, sizeof(RTSGBUF));
+ vdIoCtxInit(pIoCtx, pDisk, enmTxDir, uOffset, cbTransfer, pImageStart,
+ pcSgBuf, pvAllocation, pfnIoCtxTransfer, fFlags);
}
return pIoCtx;
@@ -1409,10 +1142,11 @@ DECLINLINE(PVDIOCTX) vdIoCtxRootAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
void *pvUser1, void *pvUser2,
void *pvAllocation,
- PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
+ PFNVDIOCTXTRANSFER pfnIoCtxTransfer,
+ uint32_t fFlags)
{
PVDIOCTX pIoCtx = vdIoCtxAlloc(pDisk, enmTxDir, uOffset, cbTransfer, pImageStart,
- pcSgBuf, pvAllocation, pfnIoCtxTransfer);
+ pcSgBuf, pvAllocation, pfnIoCtxTransfer, fFlags);
if (RT_LIKELY(pIoCtx))
{
@@ -1431,7 +1165,8 @@ DECLINLINE(PVDIOCTX) vdIoCtxDiscardAlloc(PVBOXHDD pDisk, PCRTRANGE paRanges,
PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
void *pvUser1, void *pvUser2,
void *pvAllocation,
- PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
+ PFNVDIOCTXTRANSFER pfnIoCtxTransfer,
+ uint32_t fFlags)
{
PVDIOCTX pIoCtx = NULL;
@@ -1444,7 +1179,7 @@ DECLINLINE(PVDIOCTX) vdIoCtxDiscardAlloc(PVBOXHDD pDisk, PCRTRANGE paRanges,
pIoCtx->cDataTransfersPending = 0;
pIoCtx->cMetaTransfersPending = 0;
pIoCtx->fComplete = false;
- pIoCtx->fBlocked = false;
+ pIoCtx->fFlags = fFlags;
pIoCtx->pvAllocation = pvAllocation;
pIoCtx->pfnIoCtxTransfer = pfnIoCtxTransfer;
pIoCtx->pfnIoCtxTransferNext = NULL;
@@ -1474,7 +1209,7 @@ DECLINLINE(PVDIOCTX) vdIoCtxChildAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
{
PVDIOCTX pIoCtx = vdIoCtxAlloc(pDisk, enmTxDir, uOffset, cbTransfer, pImageStart,
- pcSgBuf, pvAllocation, pfnIoCtxTransfer);
+ pcSgBuf, pvAllocation, pfnIoCtxTransfer, pIoCtxParent->fFlags & ~VDIOCTX_FLAGS_DONT_FREE);
AssertPtr(pIoCtxParent);
Assert(!pIoCtxParent->pIoCtxParent);
@@ -1529,17 +1264,24 @@ DECLINLINE(PVDIOTASK) vdIoTaskMetaAlloc(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLE
DECLINLINE(void) vdIoCtxFree(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
{
- LogFlow(("Freeing I/O context %#p\n", pIoCtx));
- if (pIoCtx->pvAllocation)
- RTMemFree(pIoCtx->pvAllocation);
+ Log(("Freeing I/O context %#p\n", pIoCtx));
+
+ if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_DONT_FREE))
+ {
+ if (pIoCtx->pvAllocation)
+ RTMemFree(pIoCtx->pvAllocation);
#ifdef DEBUG
- memset(pIoCtx, 0xff, sizeof(VDIOCTX));
+ memset(&pIoCtx->pDisk, 0xff, sizeof(void *));
#endif
- RTMemCacheFree(pDisk->hMemCacheIoCtx, pIoCtx);
+ RTMemCacheFree(pDisk->hMemCacheIoCtx, pIoCtx);
+ }
}
DECLINLINE(void) vdIoTaskFree(PVBOXHDD pDisk, PVDIOTASK pIoTask)
{
+//#ifdef DEBUG
+ memset(pIoTask, 0xff, sizeof(VDIOTASK));
+//#endif
RTMemCacheFree(pDisk->hMemCacheIoTask, pIoTask);
}
@@ -1549,7 +1291,8 @@ DECLINLINE(void) vdIoCtxChildReset(PVDIOCTX pIoCtx)
RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
pIoCtx->Req.Io.uOffset = pIoCtx->Type.Child.uOffsetSaved;
- pIoCtx->Req.Io.cbTransferLeft = pIoCtx->Type.Child.cbTransferLeftSaved;
+ pIoCtx->Req.Io.cbTransferLeft = (uint32_t)pIoCtx->Type.Child.cbTransferLeftSaved;
+ Assert((uint32_t)pIoCtx->Type.Child.cbTransferLeftSaved == pIoCtx->Type.Child.cbTransferLeftSaved);
}
DECLINLINE(PVDMETAXFER) vdMetaXferAlloc(PVDIOSTORAGE pIoStorage, uint64_t uOffset, size_t cb)
@@ -1569,22 +1312,28 @@ DECLINLINE(PVDMETAXFER) vdMetaXferAlloc(PVDIOSTORAGE pIoStorage, uint64_t uOffse
return pMetaXfer;
}
-DECLINLINE(int) vdIoCtxDefer(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
+DECLINLINE(void) vdIoCtxAddToWaitingList(volatile PVDIOCTX *ppList, PVDIOCTX pIoCtx)
{
- PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
-
- if (!pDeferred)
- return VERR_NO_MEMORY;
+ /* Put it on the waiting list. */
+ PVDIOCTX pNext = ASMAtomicUoReadPtrT(ppList, PVDIOCTX);
+ PVDIOCTX pHeadOld;
+ pIoCtx->pIoCtxNext = pNext;
+ while (!ASMAtomicCmpXchgExPtr(ppList, pIoCtx, pNext, &pHeadOld))
+ {
+ pNext = pHeadOld;
+ Assert(pNext != pIoCtx);
+ pIoCtx->pIoCtxNext = pNext;
+ ASMNopPause();
+ }
+}
+DECLINLINE(void) vdIoCtxDefer(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
+{
LogFlowFunc(("Deferring write pIoCtx=%#p\n", pIoCtx));
- Assert(!pIoCtx->pIoCtxParent && !pIoCtx->fBlocked);
-
- RTListInit(&pDeferred->NodeDeferred);
- pDeferred->pIoCtx = pIoCtx;
- RTListAppend(&pDisk->ListWriteLocked, &pDeferred->NodeDeferred);
- pIoCtx->fBlocked = true;
- return VINF_SUCCESS;
+ Assert(!pIoCtx->pIoCtxParent && !(pIoCtx->fFlags & VDIOCTX_FLAGS_BLOCKED));
+ pIoCtx->fFlags |= VDIOCTX_FLAGS_BLOCKED;
+ vdIoCtxAddToWaitingList(&pDisk->pIoCtxBlockedHead, pIoCtx);
}
static size_t vdIoCtxCopy(PVDIOCTX pIoCtxDst, PVDIOCTX pIoCtxSrc, size_t cbData)
@@ -1597,15 +1346,14 @@ static int vdIoCtxCmp(PVDIOCTX pIoCtx1, PVDIOCTX pIoCtx2, size_t cbData)
return RTSgBufCmp(&pIoCtx1->Req.Io.SgBuf, &pIoCtx2->Req.Io.SgBuf, cbData);
}
-static size_t vdIoCtxCopyTo(PVDIOCTX pIoCtx, uint8_t *pbData, size_t cbData)
+static size_t vdIoCtxCopyTo(PVDIOCTX pIoCtx, const uint8_t *pbData, size_t cbData)
{
- return RTSgBufCopyToBuf(&pIoCtx->Req.Io.SgBuf, pbData, cbData);
+ return RTSgBufCopyFromBuf(&pIoCtx->Req.Io.SgBuf, pbData, cbData);
}
-
static size_t vdIoCtxCopyFrom(PVDIOCTX pIoCtx, uint8_t *pbData, size_t cbData)
{
- return RTSgBufCopyFromBuf(&pIoCtx->Req.Io.SgBuf, pbData, cbData);
+ return RTSgBufCopyToBuf(&pIoCtx->Req.Io.SgBuf, pbData, cbData);
}
static size_t vdIoCtxSet(PVDIOCTX pIoCtx, uint8_t ch, size_t cbData)
@@ -1614,8 +1362,8 @@ static size_t vdIoCtxSet(PVDIOCTX pIoCtx, uint8_t ch, size_t cbData)
}
/**
- * Process the I/O context, core method which assumes that the critsect is acquired
- * by the calling thread.
+ * Process the I/O context, core method which assumes that the I/O context
+ * acquired the lock.
*
* @returns VBox status code.
* @param pIoCtx I/O context to process.
@@ -1624,7 +1372,7 @@ static int vdIoCtxProcessLocked(PVDIOCTX pIoCtx)
{
int rc = VINF_SUCCESS;
- VD_THREAD_IS_CRITSECT_OWNER(pIoCtx->pDisk);
+ VD_IS_LOCKED(pIoCtx->pDisk);
LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
@@ -1650,7 +1398,7 @@ static int vdIoCtxProcessLocked(PVDIOCTX pIoCtx)
/* Don't change anything if there is a metadata transfer pending or we are blocked. */
if ( pIoCtx->cMetaTransfersPending
- || pIoCtx->fBlocked)
+ || (pIoCtx->fFlags & VDIOCTX_FLAGS_BLOCKED))
{
rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
goto out;
@@ -1683,18 +1431,23 @@ static int vdIoCtxProcessLocked(PVDIOCTX pIoCtx)
|| rc == VERR_VD_NOT_ENOUGH_METADATA
|| rc == VERR_VD_IOCTX_HALT)
rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
- else if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
+ else if ( RT_FAILURE(rc)
+ && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
{
ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rc, VINF_SUCCESS);
- /*
- * The I/O context completed if we have an error and there is no data
- * or meta data transfer pending.
- */
- if ( !pIoCtx->cMetaTransfersPending
- && !pIoCtx->cDataTransfersPending)
- rc = VINF_VD_ASYNC_IO_FINISHED;
- else
- rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
+
+ if (rc != VERR_DISK_FULL)
+ {
+ /*
+ * The I/O context completed if we have an error and there is no data
+ * or meta data transfer pending.
+ */
+ if ( !pIoCtx->cMetaTransfersPending
+ && !pIoCtx->cDataTransfersPending)
+ rc = VINF_VD_ASYNC_IO_FINISHED;
+ else
+ rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
+ }
}
out:
@@ -1720,7 +1473,7 @@ static int vdDiskProcessWaitingIoCtx(PVBOXHDD pDisk, PVDIOCTX pIoCtxRc)
LogFlowFunc(("pDisk=%#p pIoCtxRc=%#p\n", pDisk, pIoCtxRc));
- VD_THREAD_IS_CRITSECT_OWNER(pDisk);
+ VD_IS_LOCKED(pDisk);
/* Get the waiting list and process it in FIFO order. */
PVDIOCTX pIoCtxHead = ASMAtomicXchgPtrT(&pDisk->pIoCtxHead, NULL, PVDIOCTX);
@@ -1746,6 +1499,19 @@ static int vdDiskProcessWaitingIoCtx(PVBOXHDD pDisk, PVDIOCTX pIoCtxRc)
pCur = pCur->pIoCtxNext;
pTmp->pIoCtxNext = NULL;
+ /*
+ * Need to clear the sync flag here if there is a new I/O context
+ * with it set and the context is not given in pIoCtxRc.
+ * This happens most likely on a different thread and that one shouldn't
+ * process the context synchronously.
+ *
+ * The thread who issued the context will wait on the event semaphore
+ * anyway which is signalled when the completion handler is called.
+ */
+ if ( pTmp->fFlags & VDIOCTX_FLAGS_SYNC
+ && pTmp != pIoCtxRc)
+ pTmp->fFlags &= ~VDIOCTX_FLAGS_SYNC;
+
rcTmp = vdIoCtxProcessLocked(pTmp);
if (pTmp == pIoCtxRc)
{
@@ -1764,64 +1530,73 @@ static int vdDiskProcessWaitingIoCtx(PVBOXHDD pDisk, PVDIOCTX pIoCtxRc)
}
}
+ /*
+ * vdIoCtxProcessLocked() never returns VINF_SUCCESS.
+ * If the status code is still set and a valid I/O context was given
+ * it was not found on the list (another thread cleared it already).
+ * Return I/O in progress status code in that case.
+ */
+ if (rc == VINF_SUCCESS && pIoCtxRc)
+ rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
+
LogFlowFunc(("returns rc=%Rrc\n", rc));
return rc;
}
/**
- * Leaves the critical section of the disk processing waiting I/O contexts.
+ * Processes the list of blocked I/O contexts.
*
- * @returns VBox status code.
- * @param pDisk The disk to unlock.
- * @param pIoCtxRc An I/O context handle which waits on the list. When processed
- * The status code is returned. NULL if there is no I/O context
- * to return the status code for.
+ * @returns nothing.
+ * @param pDisk The disk structure.
*/
-static int vdDiskCritSectLeave(PVBOXHDD pDisk, PVDIOCTX pIoCtxRc)
+static void vdDiskProcessBlockedIoCtx(PVBOXHDD pDisk)
{
- int rc = VINF_SUCCESS;
+ LogFlowFunc(("pDisk=%#p\n", pDisk));
- LogFlowFunc(("pDisk=%#p pIoCtxRc=%#p\n", pDisk, pIoCtxRc));
+ VD_IS_LOCKED(pDisk);
- VD_THREAD_IS_CRITSECT_OWNER(pDisk);
+ /* Get the waiting list and process it in FIFO order. */
+ PVDIOCTX pIoCtxHead = ASMAtomicXchgPtrT(&pDisk->pIoCtxBlockedHead, NULL, PVDIOCTX);
- rc = vdDiskProcessWaitingIoCtx(pDisk, pIoCtxRc);
- RTCritSectLeave(&pDisk->CritSect);
+ /* Reverse it. */
+ PVDIOCTX pCur = pIoCtxHead;
+ pIoCtxHead = NULL;
+ while (pCur)
+ {
+ PVDIOCTX pInsert = pCur;
+ pCur = pCur->pIoCtxNext;
+ pInsert->pIoCtxNext = pIoCtxHead;
+ pIoCtxHead = pInsert;
+ }
- /*
- * We have to check for new waiting contexts here. It is possible that
- * another thread has queued another one while process waiting contexts
- * and because we still held the lock it was appended to the waiting list.
- *
- * @note Don't overwrite rc here because this might result in loosing
- * the status code of the given I/O context.
- */
- while (ASMAtomicReadPtrT(&pDisk->pIoCtxHead, PVDIOCTX) != NULL)
+ /* Process now. */
+ pCur = pIoCtxHead;
+ while (pCur)
{
- int rc2 = RTCritSectTryEnter(&pDisk->CritSect);
+ int rc;
+ PVDIOCTX pTmp = pCur;
- if (RT_SUCCESS(rc2))
- {
- /*
- * Don't pass status codes for any I/O context here. The context must hae been
- * in the first run.
- */
- vdDiskProcessWaitingIoCtx(pDisk, NULL);
- RTCritSectLeave(&pDisk->CritSect);
- }
- else
+ pCur = pCur->pIoCtxNext;
+ pTmp->pIoCtxNext = NULL;
+
+ Assert(!pTmp->pIoCtxParent);
+ Assert(pTmp->fFlags & VDIOCTX_FLAGS_BLOCKED);
+ pTmp->fFlags &= ~VDIOCTX_FLAGS_BLOCKED;
+
+ rc = vdIoCtxProcessLocked(pTmp);
+ if ( rc == VINF_VD_ASYNC_IO_FINISHED
+ && ASMAtomicCmpXchgBool(&pTmp->fComplete, true, false))
{
- /*
- * Another thread is holding the lock already and will process the list
- * whewn leaving the lock, nothing left to do for us.
- */
- Assert(rc2 == VERR_SEM_BUSY);
- break;
+ LogFlowFunc(("Waiting I/O context completed pTmp=%#p\n", pTmp));
+ vdThreadFinishWrite(pDisk);
+ pTmp->Type.Root.pfnComplete(pTmp->Type.Root.pvUser1,
+ pTmp->Type.Root.pvUser2,
+ pTmp->rcReq);
+ vdIoCtxFree(pDisk, pTmp);
}
}
- LogFlowFunc(("returns rc=%Rrc\n", rc));
- return rc;
+ LogFlowFunc(("returns\n"));
}
/**
@@ -1836,31 +1611,20 @@ static int vdIoCtxProcessTryLockDefer(PVDIOCTX pIoCtx)
int rc = VINF_SUCCESS;
PVBOXHDD pDisk = pIoCtx->pDisk;
- LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
+ Log(("Defer pIoCtx=%#p\n", pIoCtx));
/* Put it on the waiting list first. */
- PVDIOCTX pNext = ASMAtomicUoReadPtrT(&pDisk->pIoCtxHead, PVDIOCTX);
- PVDIOCTX pHeadOld;
- pIoCtx->pIoCtxNext = pNext;
- while (!ASMAtomicCmpXchgExPtr(&pDisk->pIoCtxHead, pIoCtx, pNext, &pHeadOld))
- {
- pNext = pHeadOld;
- Assert(pNext != pIoCtx);
- pIoCtx->pIoCtxNext = pNext;
- ASMNopPause();
- }
+ vdIoCtxAddToWaitingList(&pDisk->pIoCtxHead, pIoCtx);
- rc = RTCritSectTryEnter(&pDisk->CritSect);
- if (RT_SUCCESS(rc))
+ if (ASMAtomicCmpXchgBool(&pDisk->fLocked, true, false))
{
/* Leave it again, the context will be processed just before leaving the lock. */
- LogFlowFunc(("Successfully acquired the critical section\n"));
- rc = vdDiskCritSectLeave(pDisk, pIoCtx);
+ LogFlowFunc(("Successfully acquired the lock\n"));
+ rc = vdDiskUnlock(pDisk, pIoCtx);
}
else
{
- AssertMsg(rc == VERR_SEM_BUSY, ("Invalid return code %Rrc\n", rc));
- LogFlowFunc(("Critical section is busy\n"));
+ LogFlowFunc(("Lock is held\n"));
rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
}
@@ -1868,113 +1632,123 @@ static int vdIoCtxProcessTryLockDefer(PVDIOCTX pIoCtx)
}
/**
- * Wrapper for vdIoCtxProcessLocked() which acquires the lock before.
+ * Process the I/O context in a synchronous manner, waiting
+ * for it to complete.
*
- * @returns VBox status code.
- * @param pIoCtx I/O context to process.
+ * @returns VBox status code of the completed request.
+ * @param pIoCtx The sync I/O context.
*/
-static int vdIoCtxProcess(PVDIOCTX pIoCtx)
+static int vdIoCtxProcessSync(PVDIOCTX pIoCtx)
{
int rc = VINF_SUCCESS;
PVBOXHDD pDisk = pIoCtx->pDisk;
- LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
+ LogFlowFunc(("pIoCtx=%p\n", pIoCtx));
+
+ AssertMsg(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC,
+ ("I/O context is not marked as synchronous\n"));
+
+ rc = vdIoCtxProcessTryLockDefer(pIoCtx);
+ if (rc == VINF_VD_ASYNC_IO_FINISHED)
+ rc = VINF_SUCCESS;
- RTCritSectEnter(&pDisk->CritSect);
- rc = vdIoCtxProcessLocked(pIoCtx);
- vdDiskCritSectLeave(pDisk, NULL);
+ if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+ {
+ rc = RTSemEventWait(pDisk->hEventSemSyncIo, RT_INDEFINITE_WAIT);
+ AssertRC(rc);
+
+ rc = pDisk->rcSync;
+ }
+ else /* Success or error. */
+ {
+ rc = pIoCtx->rcReq;
+ vdIoCtxFree(pDisk, pIoCtx);
+ }
return rc;
}
DECLINLINE(bool) vdIoCtxIsDiskLockOwner(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
{
- return pDisk->fLocked
- && pDisk->pIoCtxLockOwner == pIoCtx;
+ return pDisk->pIoCtxLockOwner == pIoCtx;
}
static int vdIoCtxLockDisk(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
{
int rc = VINF_SUCCESS;
+ VD_IS_LOCKED(pDisk);
+
LogFlowFunc(("pDisk=%#p pIoCtx=%#p\n", pDisk, pIoCtx));
- if (!ASMAtomicCmpXchgBool(&pDisk->fLocked, true, false))
+ if (!ASMAtomicCmpXchgPtr(&pDisk->pIoCtxLockOwner, pIoCtx, NIL_VDIOCTX))
{
Assert(pDisk->pIoCtxLockOwner != pIoCtx); /* No nesting allowed. */
-
- rc = vdIoCtxDefer(pDisk, pIoCtx);
- if (RT_SUCCESS(rc))
- rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
- }
- else
- {
- Assert(!pDisk->pIoCtxLockOwner);
- pDisk->pIoCtxLockOwner = pIoCtx;
+ vdIoCtxDefer(pDisk, pIoCtx);
+ rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
}
LogFlowFunc(("returns -> %Rrc\n", rc));
return rc;
}
-static void vdIoCtxUnlockDisk(PVBOXHDD pDisk, PVDIOCTX pIoCtx, bool fProcessDeferredReqs)
+static void vdIoCtxUnlockDisk(PVBOXHDD pDisk, PVDIOCTX pIoCtx, bool fProcessBlockedReqs)
{
- LogFlowFunc(("pDisk=%#p pIoCtx=%#p fProcessDeferredReqs=%RTbool\n",
- pDisk, pIoCtx, fProcessDeferredReqs));
+ LogFlowFunc(("pDisk=%#p pIoCtx=%#p fProcessBlockedReqs=%RTbool\n",
+ pDisk, pIoCtx, fProcessBlockedReqs));
+
+ VD_IS_LOCKED(pDisk);
LogFlow(("Unlocking disk lock owner is %#p\n", pDisk->pIoCtxLockOwner));
- Assert(pDisk->fLocked);
Assert(pDisk->pIoCtxLockOwner == pIoCtx);
- pDisk->pIoCtxLockOwner = NULL;
- ASMAtomicXchgBool(&pDisk->fLocked, false);
+ ASMAtomicXchgPtrT(&pDisk->pIoCtxLockOwner, NIL_VDIOCTX, PVDIOCTX);
- if (fProcessDeferredReqs)
+ if (fProcessBlockedReqs)
{
- /* Process any pending writes if the current request didn't caused another growing. */
- RTCritSectEnter(&pDisk->CritSect);
-
- if (!RTListIsEmpty(&pDisk->ListWriteLocked))
- {
- RTLISTNODE ListTmp;
-
- RTListMove(&ListTmp, &pDisk->ListWriteLocked);
- vdDiskCritSectLeave(pDisk, NULL);
+ /* Process any blocked writes if the current request didn't caused another growing. */
+ vdDiskProcessBlockedIoCtx(pDisk);
+ }
- /* Process the list. */
- do
- {
- int rc;
- PVDIOCTXDEFERRED pDeferred = RTListGetFirst(&ListTmp, VDIOCTXDEFERRED, NodeDeferred);
- PVDIOCTX pIoCtxWait = pDeferred->pIoCtx;
+ LogFlowFunc(("returns\n"));
+}
- AssertPtr(pIoCtxWait);
+/**
+ * Internal: Reads a given amount of data from the image chain of the disk.
+ **/
+static int vdDiskReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
+ uint64_t uOffset, size_t cbRead, PVDIOCTX pIoCtx, size_t *pcbThisRead)
+{
+ int rc = VINF_SUCCESS;
+ size_t cbThisRead = cbRead;
- RTListNodeRemove(&pDeferred->NodeDeferred);
- RTMemFree(pDeferred);
+ AssertPtr(pcbThisRead);
- Assert(!pIoCtxWait->pIoCtxParent);
+ *pcbThisRead = 0;
- pIoCtxWait->fBlocked = false;
- LogFlowFunc(("Processing waiting I/O context pIoCtxWait=%#p\n", pIoCtxWait));
+ /*
+ * Try to read from the given image.
+ * If the block is not allocated read from override chain if present.
+ */
+ rc = pImage->Backend->pfnRead(pImage->pBackendData,
+ uOffset, cbThisRead, pIoCtx,
+ &cbThisRead);
- rc = vdIoCtxProcess(pIoCtxWait);
- if ( rc == VINF_VD_ASYNC_IO_FINISHED
- && ASMAtomicCmpXchgBool(&pIoCtxWait->fComplete, true, false))
- {
- LogFlowFunc(("Waiting I/O context completed pIoCtxWait=%#p\n", pIoCtxWait));
- vdThreadFinishWrite(pDisk);
- pIoCtxWait->Type.Root.pfnComplete(pIoCtxWait->Type.Root.pvUser1,
- pIoCtxWait->Type.Root.pvUser2,
- pIoCtxWait->rcReq);
- vdIoCtxFree(pDisk, pIoCtxWait);
- }
- } while (!RTListIsEmpty(&ListTmp));
+ if (rc == VERR_VD_BLOCK_FREE)
+ {
+ for (PVDIMAGE pCurrImage = pImageParentOverride ? pImageParentOverride : pImage->pPrev;
+ pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
+ pCurrImage = pCurrImage->pPrev)
+ {
+ rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
+ uOffset, cbThisRead, pIoCtx,
+ &cbThisRead);
}
- else
- vdDiskCritSectLeave(pDisk, NULL);
}
- LogFlowFunc(("returns\n"));
+ if (RT_SUCCESS(rc) || rc == VERR_VD_BLOCK_FREE)
+ *pcbThisRead = cbThisRead;
+
+ return rc;
}
/**
@@ -1984,9 +1758,12 @@ static void vdIoCtxUnlockDisk(PVBOXHDD pDisk, PVDIOCTX pIoCtx, bool fProcessDefe
static int vdReadHelperAsync(PVDIOCTX pIoCtx)
{
int rc;
- size_t cbToRead = pIoCtx->Req.Io.cbTransfer;
- uint64_t uOffset = pIoCtx->Req.Io.uOffset;
- PVDIMAGE pCurrImage = pIoCtx->Req.Io.pImageCur;;
+ PVBOXHDD pDisk = pIoCtx->pDisk;
+ size_t cbToRead = pIoCtx->Req.Io.cbTransfer;
+ uint64_t uOffset = pIoCtx->Req.Io.uOffset;
+ PVDIMAGE pCurrImage = pIoCtx->Req.Io.pImageCur;
+ PVDIMAGE pImageParentOverride = pIoCtx->Req.Io.pImageParentOverride;
+ unsigned cImagesRead = pIoCtx->Req.Io.cImagesRead;
size_t cbThisRead;
/* Loop until all reads started or we have a backend which needs to read metadata. */
@@ -1997,23 +1774,57 @@ static int vdReadHelperAsync(PVDIOCTX pIoCtx)
* stale data when different block sizes are used for the images. */
cbThisRead = cbToRead;
- /*
- * Try to read from the given image.
- * If the block is not allocated read from override chain if present.
- */
- rc = pCurrImage->Backend->pfnAsyncRead(pCurrImage->pBackendData,
- uOffset, cbThisRead,
- pIoCtx, &cbThisRead);
+ if ( pDisk->pCache
+ && !pImageParentOverride)
+ {
+ rc = vdCacheReadHelper(pDisk->pCache, uOffset, cbThisRead,
+ pIoCtx, &cbThisRead);
+ if (rc == VERR_VD_BLOCK_FREE)
+ {
+ rc = vdDiskReadHelper(pDisk, pCurrImage, NULL, uOffset, cbThisRead,
+ pIoCtx, &cbThisRead);
- if (rc == VERR_VD_BLOCK_FREE)
+ /* If the read was successful, write the data back into the cache. */
+ if ( RT_SUCCESS(rc)
+ && pIoCtx->fFlags & VDIOCTX_FLAGS_READ_UPDATE_CACHE)
+ {
+ rc = vdCacheWriteHelper(pDisk->pCache, uOffset, cbThisRead,
+ pIoCtx, NULL);
+ }
+ }
+ }
+ else
{
- while ( pCurrImage->pPrev != NULL
- && rc == VERR_VD_BLOCK_FREE)
+
+ /*
+ * Try to read from the given image.
+ * If the block is not allocated read from override chain if present.
+ */
+ rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
+ uOffset, cbThisRead, pIoCtx,
+ &cbThisRead);
+
+ if ( rc == VERR_VD_BLOCK_FREE
+ && cImagesRead != 1)
{
- pCurrImage = pCurrImage->pPrev;
- rc = pCurrImage->Backend->pfnAsyncRead(pCurrImage->pBackendData,
- uOffset, cbThisRead,
- pIoCtx, &cbThisRead);
+ unsigned cImagesToProcess = cImagesRead;
+
+ pCurrImage = pImageParentOverride ? pImageParentOverride : pCurrImage->pPrev;
+ pIoCtx->Req.Io.pImageParentOverride = NULL;
+
+ while (pCurrImage && rc == VERR_VD_BLOCK_FREE)
+ {
+ rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
+ uOffset, cbThisRead,
+ pIoCtx, &cbThisRead);
+ if (cImagesToProcess == 1)
+ break;
+ else if (cImagesToProcess > 0)
+ cImagesToProcess--;
+
+ if (rc == VERR_VD_BLOCK_FREE)
+ pCurrImage = pCurrImage->pPrev;
+ }
}
}
@@ -2021,17 +1832,41 @@ static int vdReadHelperAsync(PVDIOCTX pIoCtx)
if (rc == VERR_VD_BLOCK_FREE)
{
/* No image in the chain contains the data for the block. */
- vdIoCtxSet(pIoCtx, '\0', cbThisRead);
- ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbThisRead);
- rc = VINF_SUCCESS;
+ ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, (uint32_t)cbThisRead); Assert(cbThisRead == (uint32_t)cbThisRead);
+
+ /* Fill the free space with 0 if we are told to do so
+ * or a previous read returned valid data. */
+ if (pIoCtx->fFlags & VDIOCTX_FLAGS_ZERO_FREE_BLOCKS)
+ vdIoCtxSet(pIoCtx, '\0', cbThisRead);
+ else
+ pIoCtx->Req.Io.cbBufClear += cbThisRead;
+
+ if (pIoCtx->Req.Io.pImageCur->uOpenFlags & VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS)
+ rc = VINF_VD_NEW_ZEROED_BLOCK;
+ else
+ rc = VINF_SUCCESS;
}
- else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
- rc = VINF_SUCCESS;
else if (rc == VERR_VD_IOCTX_HALT)
{
uOffset += cbThisRead;
cbToRead -= cbThisRead;
- pIoCtx->fBlocked = true;
+ pIoCtx->fFlags |= VDIOCTX_FLAGS_BLOCKED;
+ }
+ else if ( RT_SUCCESS(rc)
+ || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+ {
+ /* First not free block, fill the space before with 0. */
+ if ( pIoCtx->Req.Io.cbBufClear
+ && !(pIoCtx->fFlags & VDIOCTX_FLAGS_ZERO_FREE_BLOCKS))
+ {
+ RTSGBUF SgBuf;
+ RTSgBufClone(&SgBuf, &pIoCtx->Req.Io.SgBuf);
+ RTSgBufReset(&SgBuf);
+ RTSgBufSet(&SgBuf, 0, pIoCtx->Req.Io.cbBufClear);
+ pIoCtx->Req.Io.cbBufClear = 0;
+ pIoCtx->fFlags |= VDIOCTX_FLAGS_ZERO_FREE_BLOCKS;
+ }
+ rc = VINF_SUCCESS;
}
if (RT_FAILURE(rc))
@@ -2051,7 +1886,9 @@ static int vdReadHelperAsync(PVDIOCTX pIoCtx)
pIoCtx->Req.Io.pImageCur = pCurrImage ? pCurrImage : pIoCtx->Req.Io.pImageStart;
}
- return rc;
+ return (!(pIoCtx->fFlags & VDIOCTX_FLAGS_ZERO_FREE_BLOCKS))
+ ? VERR_VD_BLOCK_FREE
+ : rc;
}
/**
@@ -2061,8 +1898,93 @@ static int vdParentRead(void *pvUser, uint64_t uOffset, void *pvBuf,
size_t cbRead)
{
PVDPARENTSTATEDESC pParentState = (PVDPARENTSTATEDESC)pvUser;
- return vdReadHelper(pParentState->pDisk, pParentState->pImage, uOffset,
- pvBuf, cbRead, false /* fUpdateCache */);
+
+ /** @todo
+ * Only used for compaction so far which is not possible to mix with async I/O.
+ * Needs to be changed if we want to support online compaction of images.
+ */
+ bool fLocked = ASMAtomicXchgBool(&pParentState->pDisk->fLocked, true);
+ AssertMsgReturn(!fLocked,
+ ("Calling synchronous parent read while another thread holds the disk lock\n"),
+ VERR_VD_INVALID_STATE);
+
+ /* Fake an I/O context. */
+ RTSGSEG Segment;
+ RTSGBUF SgBuf;
+ VDIOCTX IoCtx;
+
+ Segment.pvSeg = pvBuf;
+ Segment.cbSeg = cbRead;
+ RTSgBufInit(&SgBuf, &Segment, 1);
+ vdIoCtxInit(&IoCtx, pParentState->pDisk, VDIOCTXTXDIR_READ, uOffset, cbRead, pParentState->pImage,
+ &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC | VDIOCTX_FLAGS_ZERO_FREE_BLOCKS);
+ int rc = vdReadHelperAsync(&IoCtx);
+ ASMAtomicXchgBool(&pParentState->pDisk->fLocked, false);
+ return rc;
+}
+
+/**
+ * Extended version of vdReadHelper(), implementing certain optimizations
+ * for image cloning.
+ *
+ * @returns VBox status code.
+ * @param pDisk The disk to read from.
+ * @param pImage The image to start reading from.
+ * @param pImageParentOverride The parent image to read from
+ * if the starting image returns a free block.
+ * If NULL is passed the real parent of the image
+ * in the chain is used.
+ * @param uOffset Offset in the disk to start reading from.
+ * @param pvBuf Where to store the read data.
+ * @param cbRead How much to read.
+ * @param fZeroFreeBlocks Flag whether free blocks should be zeroed.
+ * If false and no image has data for sepcified
+ * range VERR_VD_BLOCK_FREE is returned.
+ * Note that unallocated blocks are still zeroed
+ * if at least one image has valid data for a part
+ * of the range.
+ * @param fUpdateCache Flag whether to update the attached cache if
+ * available.
+ * @param cImagesRead Number of images in the chain to read until
+ * the read is cut off. A value of 0 disables the cut off.
+ */
+static int vdReadHelperEx(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
+ uint64_t uOffset, void *pvBuf, size_t cbRead,
+ bool fZeroFreeBlocks, bool fUpdateCache, unsigned cImagesRead)
+{
+ uint32_t fFlags = VDIOCTX_FLAGS_SYNC | VDIOCTX_FLAGS_DONT_FREE;
+ RTSGSEG Segment;
+ RTSGBUF SgBuf;
+ VDIOCTX IoCtx;
+
+ if (fZeroFreeBlocks)
+ fFlags |= VDIOCTX_FLAGS_ZERO_FREE_BLOCKS;
+ if (fUpdateCache)
+ fFlags |= VDIOCTX_FLAGS_READ_UPDATE_CACHE;
+
+ Segment.pvSeg = pvBuf;
+ Segment.cbSeg = cbRead;
+ RTSgBufInit(&SgBuf, &Segment, 1);
+ vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_READ, uOffset, cbRead, pImage, &SgBuf,
+ NULL, vdReadHelperAsync, fFlags);
+
+ IoCtx.Req.Io.pImageParentOverride = pImageParentOverride;
+ IoCtx.Req.Io.cImagesRead = cImagesRead;
+ IoCtx.Type.Root.pfnComplete = vdIoCtxSyncComplete;
+ IoCtx.Type.Root.pvUser1 = pDisk;
+ IoCtx.Type.Root.pvUser2 = NULL;
+ return vdIoCtxProcessSync(&IoCtx);
+}
+
+/**
+ * internal: read the specified amount of data in whatever blocks the backend
+ * will give us.
+ */
+static int vdReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, uint64_t uOffset,
+ void *pvBuf, size_t cbRead, bool fUpdateCache)
+{
+ return vdReadHelperEx(pDisk, pImage, NULL, uOffset, pvBuf, cbRead,
+ true /* fZeroFreeBlocks */, fUpdateCache, 0);
}
/**
@@ -2104,175 +2026,13 @@ static void vdSetModifiedFlag(PVBOXHDD pDisk)
vdResetModifiedFlag(pDisk);
if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
- pDisk->pLast->Backend->pfnFlush(pDisk->pLast->pBackendData);
- }
-}
-
-/**
- * internal: write a complete block (only used for diff images), taking the
- * remaining data from parent images. This implementation does not optimize
- * anything (except that it tries to read only that portions from parent
- * images that are really needed).
- */
-static int vdWriteHelperStandard(PVBOXHDD pDisk, PVDIMAGE pImage,
- PVDIMAGE pImageParentOverride,
- uint64_t uOffset, size_t cbWrite,
- size_t cbThisWrite, size_t cbPreRead,
- size_t cbPostRead, const void *pvBuf,
- void *pvTmp)
-{
- int rc = VINF_SUCCESS;
-
- /* Read the data that goes before the write to fill the block. */
- if (cbPreRead)
- {
- /*
- * Updating the cache doesn't make sense here because
- * this will be done after the complete block was written.
- */
- rc = vdReadHelperEx(pDisk, pImage, pImageParentOverride,
- uOffset - cbPreRead, pvTmp, cbPreRead,
- true /* fZeroFreeBlocks*/,
- false /* fUpdateCache */, 0);
- if (RT_FAILURE(rc))
- return rc;
- }
-
- /* Copy the data to the right place in the buffer. */
- memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
-
- /* Read the data that goes after the write to fill the block. */
- if (cbPostRead)
- {
- /* If we have data to be written, use that instead of reading
- * data from the image. */
- size_t cbWriteCopy;
- if (cbWrite > cbThisWrite)
- cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
- else
- cbWriteCopy = 0;
- /* Figure out how much we cannot read from the image, because
- * the last block to write might exceed the nominal size of the
- * image for technical reasons. */
- size_t cbFill;
- if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
- cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
- else
- cbFill = 0;
- /* The rest must be read from the image. */
- size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
-
- /* Now assemble the remaining data. */
- if (cbWriteCopy)
- memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
- (char *)pvBuf + cbThisWrite, cbWriteCopy);
- if (cbReadImage)
- rc = vdReadHelperEx(pDisk, pImage, pImageParentOverride,
- uOffset + cbThisWrite + cbWriteCopy,
- (char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy,
- cbReadImage, true /* fZeroFreeBlocks */,
- false /* fUpdateCache */, 0);
- if (RT_FAILURE(rc))
- return rc;
- /* Zero out the remainder of this block. Will never be visible, as this
- * is beyond the limit of the image. */
- if (cbFill)
- memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
- '\0', cbFill);
- }
-
- /* Write the full block to the virtual disk. */
- rc = pImage->Backend->pfnWrite(pImage->pBackendData,
- uOffset - cbPreRead, pvTmp,
- cbPreRead + cbThisWrite + cbPostRead,
- NULL, &cbPreRead, &cbPostRead, 0);
- Assert(rc != VERR_VD_BLOCK_FREE);
- Assert(cbPreRead == 0);
- Assert(cbPostRead == 0);
-
- return rc;
-}
-
-/**
- * internal: write a complete block (only used for diff images), taking the
- * remaining data from parent images. This implementation optimizes out writes
- * that do not change the data relative to the state as of the parent images.
- * All backends which support differential/growing images support this.
- */
-static int vdWriteHelperOptimized(PVBOXHDD pDisk, PVDIMAGE pImage,
- PVDIMAGE pImageParentOverride,
- uint64_t uOffset, size_t cbWrite,
- size_t cbThisWrite, size_t cbPreRead,
- size_t cbPostRead, const void *pvBuf,
- void *pvTmp, unsigned cImagesRead)
-{
- size_t cbFill = 0;
- size_t cbWriteCopy = 0;
- size_t cbReadImage = 0;
- int rc;
-
- if (cbPostRead)
- {
- /* Figure out how much we cannot read from the image, because
- * the last block to write might exceed the nominal size of the
- * image for technical reasons. */
- if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
- cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
-
- /* If we have data to be written, use that instead of reading
- * data from the image. */
- if (cbWrite > cbThisWrite)
- cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
-
- /* The rest must be read from the image. */
- cbReadImage = cbPostRead - cbWriteCopy - cbFill;
- }
-
- /* Read the entire data of the block so that we can compare whether it will
- * be modified by the write or not. */
- rc = vdReadHelperEx(pDisk, pImage, pImageParentOverride, uOffset - cbPreRead, pvTmp,
- cbPreRead + cbThisWrite + cbPostRead - cbFill,
- true /* fZeroFreeBlocks */, false /* fUpdateCache */,
- cImagesRead);
- if (RT_FAILURE(rc))
- return rc;
-
- /* Check if the write would modify anything in this block. */
- if ( !memcmp((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite)
- && (!cbWriteCopy || !memcmp((char *)pvTmp + cbPreRead + cbThisWrite,
- (char *)pvBuf + cbThisWrite, cbWriteCopy)))
- {
- /* Block is completely unchanged, so no need to write anything. */
- return VINF_SUCCESS;
- }
-
- /* Copy the data to the right place in the buffer. */
- memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
-
- /* Handle the data that goes after the write to fill the block. */
- if (cbPostRead)
- {
- /* Now assemble the remaining data. */
- if (cbWriteCopy)
- memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
- (char *)pvBuf + cbThisWrite, cbWriteCopy);
- /* Zero out the remainder of this block. Will never be visible, as this
- * is beyond the limit of the image. */
- if (cbFill)
- memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
- '\0', cbFill);
+ {
+ VDIOCTX IoCtx;
+ vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_FLUSH, 0, 0, NULL,
+ NULL, NULL, NULL, VDIOCTX_FLAGS_SYNC);
+ pDisk->pLast->Backend->pfnFlush(pDisk->pLast->pBackendData, &IoCtx);
+ }
}
-
- /* Write the full block to the virtual disk. */
- rc = pImage->Backend->pfnWrite(pImage->pBackendData,
- uOffset - cbPreRead, pvTmp,
- cbPreRead + cbThisWrite + cbPostRead,
- NULL, &cbPreRead, &cbPostRead, 0);
- Assert(rc != VERR_VD_BLOCK_FREE);
- Assert(cbPreRead == 0);
- Assert(cbPostRead == 0);
-
- return rc;
}
/**
@@ -2282,76 +2042,27 @@ static int vdWriteHelperOptimized(PVBOXHDD pDisk, PVDIMAGE pImage,
static int vdWriteHelperEx(PVBOXHDD pDisk, PVDIMAGE pImage,
PVDIMAGE pImageParentOverride, uint64_t uOffset,
const void *pvBuf, size_t cbWrite,
- bool fUpdateCache, unsigned cImagesRead)
+ uint32_t fFlags, unsigned cImagesRead)
{
- int rc;
- unsigned fWrite;
- size_t cbThisWrite;
- size_t cbPreRead, cbPostRead;
- uint64_t uOffsetCur = uOffset;
- size_t cbWriteCur = cbWrite;
- const void *pcvBufCur = pvBuf;
+ RTSGSEG Segment;
+ RTSGBUF SgBuf;
+ VDIOCTX IoCtx;
- /* Loop until all written. */
- do
- {
- /* Try to write the possibly partial block to the last opened image.
- * This works when the block is already allocated in this image or
- * if it is a full-block write (and allocation isn't suppressed below).
- * For image formats which don't support zero blocks, it's beneficial
- * to avoid unnecessarily allocating unchanged blocks. This prevents
- * unwanted expanding of images. VMDK is an example. */
- cbThisWrite = cbWriteCur;
- fWrite = (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
- ? 0 : VD_WRITE_NO_ALLOC;
- rc = pImage->Backend->pfnWrite(pImage->pBackendData, uOffsetCur, pcvBufCur,
- cbThisWrite, &cbThisWrite, &cbPreRead,
- &cbPostRead, fWrite);
- if (rc == VERR_VD_BLOCK_FREE)
- {
- void *pvTmp = RTMemTmpAlloc(cbPreRead + cbThisWrite + cbPostRead);
- AssertBreakStmt(VALID_PTR(pvTmp), rc = VERR_NO_MEMORY);
+ fFlags |= VDIOCTX_FLAGS_SYNC | VDIOCTX_FLAGS_DONT_FREE;
- if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME))
- {
- /* Optimized write, suppress writing to a so far unallocated
- * block if the data is in fact not changed. */
- rc = vdWriteHelperOptimized(pDisk, pImage, pImageParentOverride,
- uOffsetCur, cbWriteCur,
- cbThisWrite, cbPreRead, cbPostRead,
- pcvBufCur, pvTmp, cImagesRead);
- }
- else
- {
- /* Normal write, not optimized in any way. The block will
- * be written no matter what. This will usually (unless the
- * backend has some further optimization enabled) cause the
- * block to be allocated. */
- rc = vdWriteHelperStandard(pDisk, pImage, pImageParentOverride,
- uOffsetCur, cbWriteCur,
- cbThisWrite, cbPreRead, cbPostRead,
- pcvBufCur, pvTmp);
- }
- RTMemTmpFree(pvTmp);
- if (RT_FAILURE(rc))
- break;
- }
-
- cbWriteCur -= cbThisWrite;
- uOffsetCur += cbThisWrite;
- pcvBufCur = (char *)pcvBufCur + cbThisWrite;
- } while (cbWriteCur != 0 && RT_SUCCESS(rc));
+ Segment.pvSeg = (void *)pvBuf;
+ Segment.cbSeg = cbWrite;
+ RTSgBufInit(&SgBuf, &Segment, 1);
+ vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_WRITE, uOffset, cbWrite, pImage, &SgBuf,
+ NULL, vdWriteHelperAsync, fFlags);
- /* Update the cache on success */
- if ( RT_SUCCESS(rc)
- && pDisk->pCache
- && fUpdateCache)
- rc = vdCacheWriteHelper(pDisk->pCache, uOffset, pvBuf, cbWrite, NULL);
-
- if (RT_SUCCESS(rc))
- rc = vdDiscardSetRangeAllocated(pDisk, uOffset, cbWrite);
-
- return rc;
+ IoCtx.Req.Io.pImageParentOverride = pImageParentOverride;
+ IoCtx.Req.Io.cImagesRead = cImagesRead;
+ IoCtx.pIoCtxParent = NULL;
+ IoCtx.Type.Root.pfnComplete = vdIoCtxSyncComplete;
+ IoCtx.Type.Root.pvUser1 = pDisk;
+ IoCtx.Type.Root.pvUser2 = NULL;
+ return vdIoCtxProcessSync(&IoCtx);
}
/**
@@ -2359,10 +2070,10 @@ static int vdWriteHelperEx(PVBOXHDD pDisk, PVDIMAGE pImage,
* write optimizations.
*/
static int vdWriteHelper(PVBOXHDD pDisk, PVDIMAGE pImage, uint64_t uOffset,
- const void *pvBuf, size_t cbWrite, bool fUpdateCache)
+ const void *pvBuf, size_t cbWrite, uint32_t fFlags)
{
return vdWriteHelperEx(pDisk, pImage, NULL, uOffset, pvBuf, cbWrite,
- fUpdateCache, 0);
+ fFlags, 0);
}
/**
@@ -2407,9 +2118,19 @@ static int vdCopyHelper(PVBOXHDD pDiskFrom, PVDIMAGE pImageFrom, PVBOXHDD pDiskT
if (fBlockwiseCopy)
{
+ RTSGSEG SegmentBuf;
+ RTSGBUF SgBuf;
+ VDIOCTX IoCtx;
+
+ SegmentBuf.pvSeg = pvBuf;
+ SegmentBuf.cbSeg = VD_MERGE_BUFFER_SIZE;
+ RTSgBufInit(&SgBuf, &SegmentBuf, 1);
+ vdIoCtxInit(&IoCtx, pDiskFrom, VDIOCTXTXDIR_READ, 0, 0, NULL,
+ &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
+
/* Read the source data. */
rc = pImageFrom->Backend->pfnRead(pImageFrom->pBackendData,
- uOffset, pvBuf, cbThisRead,
+ uOffset, cbThisRead, &IoCtx,
&cbThisRead);
if ( rc == VERR_VD_BLOCK_FREE
@@ -2422,8 +2143,8 @@ static int vdCopyHelper(PVBOXHDD pDiskFrom, PVDIMAGE pImageFrom, PVBOXHDD pDiskT
pCurrImage = pCurrImage->pPrev)
{
rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
- uOffset, pvBuf, cbThisRead,
- &cbThisRead);
+ uOffset, cbThisRead,
+ &IoCtx, &cbThisRead);
if (cImagesToProcess == 1)
break;
else if (cImagesToProcess > 0)
@@ -2450,7 +2171,7 @@ static int vdCopyHelper(PVBOXHDD pDiskFrom, PVDIMAGE pImageFrom, PVBOXHDD pDiskT
/* Only do collapsed I/O if we are copying the data blockwise. */
rc = vdWriteHelperEx(pDiskTo, pDiskTo->pLast, NULL, uOffset, pvBuf,
- cbThisRead, false /* fUpdateCache */,
+ cbThisRead, VDIOCTX_FLAGS_DONT_SET_MODIFIED_FLAG /* fFlags */,
fBlockwiseCopy ? cImagesToRead : 0);
if (RT_FAILURE(rc))
break;
@@ -2514,7 +2235,7 @@ static int vdSetModifiedHelperAsync(PVDIOCTX pIoCtx)
PVBOXHDD pDisk = pIoCtx->pDisk;
PVDIMAGE pImage = pIoCtx->Req.Io.pImageCur;
- rc = pImage->Backend->pfnAsyncFlush(pImage->pBackendData, pIoCtx);
+ rc = pImage->Backend->pfnFlush(pImage->pBackendData, pIoCtx);
if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
rc = VINF_SUCCESS;
@@ -2528,6 +2249,8 @@ static int vdSetModifiedFlagAsync(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
{
int rc = VINF_SUCCESS;
+ VD_IS_LOCKED(pDisk);
+
pDisk->uModified |= VD_IMAGE_MODIFIED_FLAG;
if (pDisk->uModified & VD_IMAGE_MODIFIED_FIRST)
{
@@ -2548,7 +2271,7 @@ static int vdSetModifiedFlagAsync(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
if (pIoCtxFlush)
{
- rc = vdIoCtxProcess(pIoCtxFlush);
+ rc = vdIoCtxProcessLocked(pIoCtxFlush);
if (rc == VINF_VD_ASYNC_IO_FINISHED)
{
vdIoCtxUnlockDisk(pDisk, pIoCtx, false /* fProcessDeferredReqs */);
@@ -2557,7 +2280,7 @@ static int vdSetModifiedFlagAsync(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
{
ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
- pIoCtx->fBlocked = true;
+ pIoCtx->fFlags |= VDIOCTX_FLAGS_BLOCKED;
}
else /* Another error */
vdIoCtxFree(pDisk, pIoCtxFlush);
@@ -2571,101 +2294,7 @@ static int vdSetModifiedFlagAsync(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
return rc;
}
-/**
- * internal: write a complete block (only used for diff images), taking the
- * remaining data from parent images. This implementation does not optimize
- * anything (except that it tries to read only that portions from parent
- * images that are really needed) - async version.
- */
-static int vdWriteHelperStandardAsync(PVDIOCTX pIoCtx)
-{
- int rc = VINF_SUCCESS;
-
-#if 0
-
- /* Read the data that goes before the write to fill the block. */
- if (cbPreRead)
- {
- rc = vdReadHelperAsync(pIoCtxDst);
- if (RT_FAILURE(rc))
- return rc;
- }
-
- /* Copy the data to the right place in the buffer. */
- vdIoCtxCopy(pIoCtxDst, pIoCtxSrc, cbThisWrite);
-
- /* Read the data that goes after the write to fill the block. */
- if (cbPostRead)
- {
- /* If we have data to be written, use that instead of reading
- * data from the image. */
- size_t cbWriteCopy;
- if (cbWrite > cbThisWrite)
- cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
- else
- cbWriteCopy = 0;
- /* Figure out how much we cannot read from the image, because
- * the last block to write might exceed the nominal size of the
- * image for technical reasons. */
- size_t cbFill;
- if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
- cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
- else
- cbFill = 0;
- /* The rest must be read from the image. */
- size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
-
- /* Now assemble the remaining data. */
- if (cbWriteCopy)
- {
- vdIoCtxCopy(pIoCtxDst, pIoCtxSrc, cbWriteCopy);
- ASMAtomicSubU32(&pIoCtxDst->cbTransferLeft, cbWriteCopy);
- }
-
- if (cbReadImage)
- rc = vdReadHelperAsync(pDisk, pImage, pImageParentOverride, pIoCtxDst,
- uOffset + cbThisWrite + cbWriteCopy,
- cbReadImage);
- if (RT_FAILURE(rc))
- return rc;
- /* Zero out the remainder of this block. Will never be visible, as this
- * is beyond the limit of the image. */
- if (cbFill)
- {
- vdIoCtxSet(pIoCtxDst, '\0', cbFill);
- ASMAtomicSubU32(&pIoCtxDst->cbTransferLeft, cbFill);
- }
- }
-
- if ( !pIoCtxDst->cbTransferLeft
- && !pIoCtxDst->cMetaTransfersPending
- && ASMAtomicCmpXchgBool(&pIoCtxDst->fComplete, true, false))
- {
- /* Write the full block to the virtual disk. */
- vdIoCtxChildReset(pIoCtxDst);
- rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData,
- uOffset - cbPreRead,
- cbPreRead + cbThisWrite + cbPostRead,
- pIoCtxDst,
- NULL, &cbPreRead, &cbPostRead, 0);
- Assert(rc != VERR_VD_BLOCK_FREE);
- Assert(cbPreRead == 0);
- Assert(cbPostRead == 0);
- }
- else
- {
- LogFlow(("cbTransferLeft=%u cMetaTransfersPending=%u fComplete=%RTbool\n",
- pIoCtxDst->cbTransferLeft, pIoCtxDst->cMetaTransfersPending,
- pIoCtxDst->fComplete));
- rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
- }
-
- return rc;
-#endif
- return VERR_NOT_IMPLEMENTED;
-}
-
-static int vdWriteHelperOptimizedCommitAsync(PVDIOCTX pIoCtx)
+static int vdWriteHelperCommitAsync(PVDIOCTX pIoCtx)
{
int rc = VINF_SUCCESS;
PVDIMAGE pImage = pIoCtx->Req.Io.pImageStart;
@@ -2674,10 +2303,10 @@ static int vdWriteHelperOptimizedCommitAsync(PVDIOCTX pIoCtx)
size_t cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
- rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData,
- pIoCtx->Req.Io.uOffset - cbPreRead,
- cbPreRead + cbThisWrite + cbPostRead,
- pIoCtx, NULL, &cbPreRead, &cbPostRead, 0);
+ rc = pImage->Backend->pfnWrite(pImage->pBackendData,
+ pIoCtx->Req.Io.uOffset - cbPreRead,
+ cbPreRead + cbThisWrite + cbPostRead,
+ pIoCtx, NULL, &cbPreRead, &cbPostRead, 0);
Assert(rc != VERR_VD_BLOCK_FREE);
Assert(rc == VERR_VD_NOT_ENOUGH_METADATA || cbPreRead == 0);
Assert(rc == VERR_VD_NOT_ENOUGH_METADATA || cbPostRead == 0);
@@ -2685,7 +2314,7 @@ static int vdWriteHelperOptimizedCommitAsync(PVDIOCTX pIoCtx)
rc = VINF_SUCCESS;
else if (rc == VERR_VD_IOCTX_HALT)
{
- pIoCtx->fBlocked = true;
+ pIoCtx->fFlags |= VDIOCTX_FLAGS_BLOCKED;
rc = VINF_SUCCESS;
}
@@ -2766,7 +2395,7 @@ static int vdWriteHelperOptimizedCmpAndWriteAsync(PVDIOCTX pIoCtx)
/* Write the full block to the virtual disk. */
RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
- pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedCommitAsync;
+ pIoCtx->pfnIoCtxTransferNext = vdWriteHelperCommitAsync;
return rc;
}
@@ -2777,7 +2406,10 @@ static int vdWriteHelperOptimizedPreReadAsync(PVDIOCTX pIoCtx)
LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
- if (pIoCtx->Req.Io.cbTransferLeft)
+ pIoCtx->fFlags |= VDIOCTX_FLAGS_ZERO_FREE_BLOCKS;
+
+ if ( pIoCtx->Req.Io.cbTransferLeft
+ && !pIoCtx->cDataTransfersPending)
rc = vdReadHelperAsync(pIoCtx);
if ( RT_SUCCESS(rc)
@@ -2836,7 +2468,8 @@ static int vdWriteHelperOptimizedAsync(PVDIOCTX pIoCtx)
/* Read the entire data of the block so that we can compare whether it will
* be modified by the write or not. */
- pIoCtx->Req.Io.cbTransferLeft = cbPreRead + cbThisWrite + cbPostRead - cbFill;
+ size_t cbTmp = cbPreRead + cbThisWrite + cbPostRead - cbFill; Assert(cbTmp == (uint32_t)cbTmp);
+ pIoCtx->Req.Io.cbTransferLeft = (uint32_t)cbTmp;
pIoCtx->Req.Io.cbTransfer = pIoCtx->Req.Io.cbTransferLeft;
pIoCtx->Req.Io.uOffset -= cbPreRead;
@@ -2845,6 +2478,141 @@ static int vdWriteHelperOptimizedAsync(PVDIOCTX pIoCtx)
return VINF_SUCCESS;
}
+static int vdWriteHelperStandardAssemble(PVDIOCTX pIoCtx)
+{
+ int rc = VINF_SUCCESS;
+ size_t cbPostRead = pIoCtx->Type.Child.cbPostRead;
+ size_t cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
+ PVDIOCTX pIoCtxParent = pIoCtx->pIoCtxParent;
+
+ LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
+
+ vdIoCtxCopy(pIoCtx, pIoCtxParent, cbThisWrite);
+ if (cbPostRead)
+ {
+ size_t cbFill = pIoCtx->Type.Child.Write.Optimized.cbFill;
+ size_t cbWriteCopy = pIoCtx->Type.Child.Write.Optimized.cbWriteCopy;
+ size_t cbReadImage = pIoCtx->Type.Child.Write.Optimized.cbReadImage;
+
+ /* Now assemble the remaining data. */
+ if (cbWriteCopy)
+ {
+ /*
+ * The S/G buffer of the parent needs to be cloned because
+ * it is not allowed to modify the state.
+ */
+ RTSGBUF SgBufParentTmp;
+
+ RTSgBufClone(&SgBufParentTmp, &pIoCtxParent->Req.Io.SgBuf);
+ RTSgBufCopy(&pIoCtx->Req.Io.SgBuf, &SgBufParentTmp, cbWriteCopy);
+ }
+
+ /* Zero out the remainder of this block. Will never be visible, as this
+ * is beyond the limit of the image. */
+ if (cbFill)
+ {
+ RTSgBufAdvance(&pIoCtx->Req.Io.SgBuf, cbReadImage);
+ vdIoCtxSet(pIoCtx, '\0', cbFill);
+ }
+
+ if (cbReadImage)
+ {
+ /* Read remaining data. */
+ }
+ else
+ {
+ /* Write the full block to the virtual disk. */
+ RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
+ pIoCtx->pfnIoCtxTransferNext = vdWriteHelperCommitAsync;
+ }
+ }
+ else
+ {
+ /* Write the full block to the virtual disk. */
+ RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
+ pIoCtx->pfnIoCtxTransferNext = vdWriteHelperCommitAsync;
+ }
+
+ return rc;
+}
+
+static int vdWriteHelperStandardPreReadAsync(PVDIOCTX pIoCtx)
+{
+ int rc = VINF_SUCCESS;
+
+ LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
+
+ pIoCtx->fFlags |= VDIOCTX_FLAGS_ZERO_FREE_BLOCKS;
+
+ if (pIoCtx->Req.Io.cbTransferLeft)
+ rc = vdReadHelperAsync(pIoCtx);
+
+ if ( RT_SUCCESS(rc)
+ && ( pIoCtx->Req.Io.cbTransferLeft
+ || pIoCtx->cMetaTransfersPending))
+ rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
+ else
+ pIoCtx->pfnIoCtxTransferNext = vdWriteHelperStandardAssemble;
+
+ return rc;
+}
+
+static int vdWriteHelperStandardAsync(PVDIOCTX pIoCtx)
+{
+ PVBOXHDD pDisk = pIoCtx->pDisk;
+ uint64_t uOffset = pIoCtx->Type.Child.uOffsetSaved;
+ size_t cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
+ size_t cbPreRead = pIoCtx->Type.Child.cbPreRead;
+ size_t cbPostRead = pIoCtx->Type.Child.cbPostRead;
+ size_t cbWrite = pIoCtx->Type.Child.cbWriteParent;
+ size_t cbFill = 0;
+ size_t cbWriteCopy = 0;
+ size_t cbReadImage = 0;
+
+ LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
+
+ AssertPtr(pIoCtx->pIoCtxParent);
+ Assert(!pIoCtx->pIoCtxParent->pIoCtxParent);
+
+ /* Calculate the amount of data to read that goes after the write to fill the block. */
+ if (cbPostRead)
+ {
+ /* If we have data to be written, use that instead of reading
+ * data from the image. */
+ cbWriteCopy;
+ if (cbWrite > cbThisWrite)
+ cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
+
+ /* Figure out how much we cannot read from the image, because
+ * the last block to write might exceed the nominal size of the
+ * image for technical reasons. */
+ if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
+ cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
+
+ /* The rest must be read from the image. */
+ cbReadImage = cbPostRead - cbWriteCopy - cbFill;
+ }
+
+ pIoCtx->Type.Child.Write.Optimized.cbFill = cbFill;
+ pIoCtx->Type.Child.Write.Optimized.cbWriteCopy = cbWriteCopy;
+ pIoCtx->Type.Child.Write.Optimized.cbReadImage = cbReadImage;
+
+ /* Next step */
+ if (cbPreRead)
+ {
+ pIoCtx->pfnIoCtxTransferNext = vdWriteHelperStandardPreReadAsync;
+
+ /* Read the data that goes before the write to fill the block. */
+ pIoCtx->Req.Io.cbTransferLeft = (uint32_t)cbPreRead; Assert(cbPreRead == (uint32_t)cbPreRead);
+ pIoCtx->Req.Io.cbTransfer = pIoCtx->Req.Io.cbTransferLeft;
+ pIoCtx->Req.Io.uOffset -= cbPreRead;
+ }
+ else
+ pIoCtx->pfnIoCtxTransferNext = vdWriteHelperStandardAssemble;
+
+ return VINF_SUCCESS;
+}
+
/**
* internal: write buffer to the image, taking care of block boundaries and
* write optimizations - async version.
@@ -2860,9 +2628,12 @@ static int vdWriteHelperAsync(PVDIOCTX pIoCtx)
size_t cbThisWrite;
size_t cbPreRead, cbPostRead;
- rc = vdSetModifiedFlagAsync(pDisk, pIoCtx);
- if (RT_FAILURE(rc)) /* Includes I/O in progress. */
- return rc;
+ if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_DONT_SET_MODIFIED_FLAG))
+ {
+ rc = vdSetModifiedFlagAsync(pDisk, pIoCtx);
+ if (RT_FAILURE(rc)) /* Includes I/O in progress. */
+ return rc;
+ }
rc = vdDiscardSetRangeAllocated(pDisk, uOffset, cbWrite);
if (RT_FAILURE(rc))
@@ -2880,7 +2651,7 @@ static int vdWriteHelperAsync(PVDIOCTX pIoCtx)
cbThisWrite = cbWrite;
fWrite = (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
? 0 : VD_WRITE_NO_ALLOC;
- rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData, uOffset,
+ rc = pImage->Backend->pfnWrite(pImage->pBackendData, uOffset,
cbThisWrite, pIoCtx,
&cbThisWrite, &cbPreRead,
&cbPostRead, fWrite);
@@ -2923,9 +2694,10 @@ static int vdWriteHelperAsync(PVDIOCTX pIoCtx)
pIoCtxWrite->Type.Child.cbPreRead = cbPreRead;
pIoCtxWrite->Type.Child.cbPostRead = cbPostRead;
+ pIoCtxWrite->Req.Io.pImageParentOverride = pIoCtx->Req.Io.pImageParentOverride;
/* Process the write request */
- rc = vdIoCtxProcess(pIoCtxWrite);
+ rc = vdIoCtxProcessLocked(pIoCtxWrite);
if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
{
@@ -2937,7 +2709,8 @@ static int vdWriteHelperAsync(PVDIOCTX pIoCtx)
{
LogFlow(("Child write request completed\n"));
Assert(pIoCtx->Req.Io.cbTransferLeft >= cbThisWrite);
- ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbThisWrite);
+ Assert(cbThisWrite == (uint32_t)cbThisWrite);
+ ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, (uint32_t)cbThisWrite);
vdIoCtxUnlockDisk(pDisk, pIoCtx, false /* fProcessDeferredReqs*/ );
vdIoCtxFree(pDisk, pIoCtxWrite);
@@ -2947,7 +2720,7 @@ static int vdWriteHelperAsync(PVDIOCTX pIoCtx)
{
LogFlow(("Child write pending\n"));
ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
- pIoCtx->fBlocked = true;
+ pIoCtx->fFlags |= VDIOCTX_FLAGS_BLOCKED;
rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
cbWrite -= cbThisWrite;
uOffset += cbThisWrite;
@@ -2965,7 +2738,7 @@ static int vdWriteHelperAsync(PVDIOCTX pIoCtx)
{
cbWrite -= cbThisWrite;
uOffset += cbThisWrite;
- pIoCtx->fBlocked = true;
+ pIoCtx->fFlags |= VDIOCTX_FLAGS_BLOCKED;
break;
}
else if (rc == VERR_VD_NOT_ENOUGH_METADATA)
@@ -3007,11 +2780,24 @@ static int vdFlushHelperAsync(PVDIOCTX pIoCtx)
if (RT_SUCCESS(rc))
{
vdResetModifiedFlag(pDisk);
- rc = pImage->Backend->pfnAsyncFlush(pImage->pBackendData, pIoCtx);
- if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+ rc = pImage->Backend->pfnFlush(pImage->pBackendData, pIoCtx);
+ if ( ( RT_SUCCESS(rc)
+ || rc == VERR_VD_ASYNC_IO_IN_PROGRESS
+ || rc == VERR_VD_IOCTX_HALT)
+ && pDisk->pCache)
+ {
+ rc = pDisk->pCache->Backend->pfnFlush(pDisk->pCache->pBackendData, pIoCtx);
+ if ( RT_SUCCESS(rc)
+ || ( rc != VERR_VD_ASYNC_IO_IN_PROGRESS
+ && rc != VERR_VD_IOCTX_HALT))
+ vdIoCtxUnlockDisk(pDisk, pIoCtx, true /* fProcessBlockedReqs */);
+ else if (rc != VERR_VD_IOCTX_HALT)
+ rc = VINF_SUCCESS;
+ }
+ else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
rc = VINF_SUCCESS;
- else if (rc == VINF_VD_ASYNC_IO_FINISHED)
- vdIoCtxUnlockDisk(pDisk, pIoCtx, true /* fProcessDeferredReqs */);
+ else if (rc != VERR_VD_IOCTX_HALT)/* Some other error. */
+ vdIoCtxUnlockDisk(pDisk, pIoCtx, true /* fProcessBlockedReqs */);
}
return rc;
@@ -3036,7 +2822,7 @@ static int vdDiscardWholeBlockAsync(PVDIOCTX pIoCtx)
AssertPtr(pBlock);
- rc = pDisk->pLast->Backend->pfnAsyncDiscard(pDisk->pLast->pBackendData, pIoCtx,
+ rc = pDisk->pLast->Backend->pfnDiscard(pDisk->pLast->pBackendData, pIoCtx,
pBlock->Core.Key, pBlock->cbDiscard,
&cbPreAllocated, &cbPostAllocated,
&cbActuallyDiscarded, NULL, 0);
@@ -3094,7 +2880,7 @@ static int vdDiscardRemoveBlocksAsync(PVBOXHDD pDisk, PVDIOCTX pIoCtx, size_t cb
uint32_t idxStart = 0;
size_t cbLeft = pBlock->cbDiscard;
bool fAllocated = ASMBitTest(pBlock->pbmAllocated, idxStart);
- uint32_t cSectors = pBlock->cbDiscard / 512;
+ uint32_t cSectors = (uint32_t)(pBlock->cbDiscard / 512);
while (cbLeft > 0)
{
@@ -3118,7 +2904,7 @@ static int vdDiscardRemoveBlocksAsync(PVBOXHDD pDisk, PVDIOCTX pIoCtx, size_t cb
if (idxEnd != -1)
cbThis = (idxEnd - idxStart) * 512;
- rc = pDisk->pLast->Backend->pfnAsyncDiscard(pDisk->pLast->pBackendData, pIoCtx,
+ rc = pDisk->pLast->Backend->pfnDiscard(pDisk->pLast->pBackendData, pIoCtx,
offStart, cbThis, NULL, NULL, &cbThis,
NULL, VD_DISCARD_MARK_UNUSED);
if ( RT_FAILURE(rc)
@@ -3175,7 +2961,7 @@ static int vdDiscardCurrentRangeAsync(PVDIOCTX pIoCtx)
LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
/* No block found, try to discard using the backend first. */
- rc = pDisk->pLast->Backend->pfnAsyncDiscard(pDisk->pLast->pBackendData, pIoCtx,
+ rc = pDisk->pLast->Backend->pfnDiscard(pDisk->pLast->pBackendData, pIoCtx,
offStart, cbThisDiscard, &cbPreAllocated,
&cbPostAllocated, &cbThisDiscard,
&pbmAllocated, 0);
@@ -3310,7 +3096,7 @@ static int vdDiscardHelperAsync(PVDIOCTX pIoCtx)
Assert(!((offStart - pBlock->Core.Key) % 512));
idxStart = (offStart - pBlock->Core.Key) / 512;
- idxEnd = idxStart + (cbThisDiscard / 512);
+ idxEnd = idxStart + (int32_t)(cbThisDiscard / 512);
ASMBitClearRange(pBlock->pbmAllocated, idxStart, idxEnd);
@@ -3318,7 +3104,7 @@ static int vdDiscardHelperAsync(PVDIOCTX pIoCtx)
offStart += cbThisDiscard;
/* Call the backend to discard the block if it is completely unallocated now. */
- if (ASMBitFirstSet((volatile void *)pBlock->pbmAllocated, pBlock->cbDiscard / 512) == -1)
+ if (ASMBitFirstSet((volatile void *)pBlock->pbmAllocated, (uint32_t)(pBlock->cbDiscard / 512)) == -1)
{
pIoCtx->Req.Discard.pBlock = pBlock;
pIoCtx->pfnIoCtxTransferNext = vdDiscardWholeBlockAsync;
@@ -3745,15 +3531,15 @@ static int vdIoCtxContinue(PVDIOCTX pIoCtx, int rcReq)
PVBOXHDD pDisk = pIoCtx->pDisk;
int rc = VINF_SUCCESS;
- VD_THREAD_IS_CRITSECT_OWNER(pDisk);
+ VD_IS_LOCKED(pDisk);
if (RT_FAILURE(rcReq))
ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rcReq, VINF_SUCCESS);
- if (!pIoCtx->fBlocked)
+ if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_BLOCKED))
{
/* Continue the transfer */
- rc = vdIoCtxProcess(pIoCtx);
+ rc = vdIoCtxProcessLocked(pIoCtx);
if ( rc == VINF_VD_ASYNC_IO_FINISHED
&& ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
@@ -3776,7 +3562,7 @@ static int vdIoCtxContinue(PVDIOCTX pIoCtx, int rcReq)
/* Update the parent state. */
Assert(pIoCtxParent->Req.Io.cbTransferLeft >= pIoCtx->Type.Child.cbTransferParent);
- ASMAtomicSubU32(&pIoCtxParent->Req.Io.cbTransferLeft, pIoCtx->Type.Child.cbTransferParent);
+ ASMAtomicSubU32(&pIoCtxParent->Req.Io.cbTransferLeft, (uint32_t)pIoCtx->Type.Child.cbTransferParent);
}
else
Assert(pIoCtx->enmTxDir == VDIOCTXTXDIR_FLUSH);
@@ -3788,75 +3574,29 @@ static int vdIoCtxContinue(PVDIOCTX pIoCtx, int rcReq)
vdIoCtxUnlockDisk(pDisk, pIoCtxParent, false /* fProcessDeferredReqs */);
/* Unblock the parent */
- pIoCtxParent->fBlocked = false;
+ pIoCtxParent->fFlags &= ~VDIOCTX_FLAGS_BLOCKED;
- rc = vdIoCtxProcess(pIoCtxParent);
+ rc = vdIoCtxProcessLocked(pIoCtxParent);
if ( rc == VINF_VD_ASYNC_IO_FINISHED
&& ASMAtomicCmpXchgBool(&pIoCtxParent->fComplete, true, false))
{
- RTCritSectLeave(&pDisk->CritSect);
LogFlowFunc(("Parent I/O context completed pIoCtxParent=%#p rcReq=%Rrc\n", pIoCtxParent, pIoCtxParent->rcReq));
pIoCtxParent->Type.Root.pfnComplete(pIoCtxParent->Type.Root.pvUser1,
pIoCtxParent->Type.Root.pvUser2,
pIoCtxParent->rcReq);
vdThreadFinishWrite(pDisk);
vdIoCtxFree(pDisk, pIoCtxParent);
- RTCritSectEnter(&pDisk->CritSect);
+ vdDiskProcessBlockedIoCtx(pDisk);
}
-
- /* Process any pending writes if the current request didn't caused another growing. */
- if ( !RTListIsEmpty(&pDisk->ListWriteLocked)
- && !vdIoCtxIsDiskLockOwner(pDisk, pIoCtx))
+ else if (!vdIoCtxIsDiskLockOwner(pDisk, pIoCtx))
{
- RTLISTNODE ListTmp;
-
- LogFlowFunc(("Before: pNext=%#p pPrev=%#p\n", pDisk->ListWriteLocked.pNext,
- pDisk->ListWriteLocked.pPrev));
-
- RTListMove(&ListTmp, &pDisk->ListWriteLocked);
-
- LogFlowFunc(("After: pNext=%#p pPrev=%#p\n", pDisk->ListWriteLocked.pNext,
- pDisk->ListWriteLocked.pPrev));
-
- RTCritSectLeave(&pDisk->CritSect);
-
- /* Process the list. */
- do
- {
- PVDIOCTXDEFERRED pDeferred = RTListGetFirst(&ListTmp, VDIOCTXDEFERRED, NodeDeferred);
- PVDIOCTX pIoCtxWait = pDeferred->pIoCtx;
-
- AssertPtr(pIoCtxWait);
-
- RTListNodeRemove(&pDeferred->NodeDeferred);
- RTMemFree(pDeferred);
-
- Assert(!pIoCtxWait->pIoCtxParent);
-
- pIoCtxWait->fBlocked = false;
- LogFlowFunc(("Processing waiting I/O context pIoCtxWait=%#p\n", pIoCtxWait));
-
- rc = vdIoCtxProcess(pIoCtxWait);
- if ( rc == VINF_VD_ASYNC_IO_FINISHED
- && ASMAtomicCmpXchgBool(&pIoCtxWait->fComplete, true, false))
- {
- LogFlowFunc(("Waiting I/O context completed pIoCtxWait=%#p\n", pIoCtxWait));
- vdThreadFinishWrite(pDisk);
- pIoCtxWait->Type.Root.pfnComplete(pIoCtxWait->Type.Root.pvUser1,
- pIoCtxWait->Type.Root.pvUser2,
- pIoCtxWait->rcReq);
- vdIoCtxFree(pDisk, pIoCtxWait);
- }
- } while (!RTListIsEmpty(&ListTmp));
-
- RTCritSectEnter(&pDisk->CritSect);
+ /* Process any pending writes if the current request didn't caused another growing. */
+ vdDiskProcessBlockedIoCtx(pDisk);
}
}
else
{
- RTCritSectLeave(&pDisk->CritSect);
-
if (pIoCtx->enmTxDir == VDIOCTXTXDIR_FLUSH)
{
vdIoCtxUnlockDisk(pDisk, pIoCtx, true /* fProcessDerredReqs */);
@@ -3875,7 +3615,6 @@ static int vdIoCtxContinue(PVDIOCTX pIoCtx, int rcReq)
pIoCtx->Type.Root.pfnComplete(pIoCtx->Type.Root.pvUser1,
pIoCtx->Type.Root.pvUser2,
pIoCtx->rcReq);
- RTCritSectEnter(&pDisk->CritSect);
}
vdIoCtxFree(pDisk, pIoCtx);
@@ -3899,9 +3638,10 @@ static int vdUserXferCompleted(PVDIOSTORAGE pIoStorage, PVDIOCTX pIoCtx,
LogFlowFunc(("pIoStorage=%#p pIoCtx=%#p pfnComplete=%#p pvUser=%#p cbTransfer=%zu rcReq=%Rrc\n",
pIoStorage, pIoCtx, pfnComplete, pvUser, cbTransfer, rcReq));
- RTCritSectEnter(&pDisk->CritSect);
+ VD_IS_LOCKED(pDisk);
+
Assert(pIoCtx->Req.Io.cbTransferLeft >= cbTransfer);
- ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbTransfer);
+ ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, (uint32_t)cbTransfer); Assert(cbTransfer == (uint32_t)cbTransfer);
ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
if (pfnComplete)
@@ -3912,8 +3652,6 @@ static int vdUserXferCompleted(PVDIOSTORAGE pIoStorage, PVDIOCTX pIoCtx,
else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
rc = VINF_SUCCESS;
- vdDiskCritSectLeave(pDisk, NULL);
-
return rc;
}
@@ -3930,7 +3668,8 @@ static int vdMetaXferCompleted(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnCo
LogFlowFunc(("pIoStorage=%#p pfnComplete=%#p pvUser=%#p pMetaXfer=%#p rcReq=%Rrc\n",
pIoStorage, pfnComplete, pvUser, pMetaXfer, rcReq));
- RTCritSectEnter(&pDisk->CritSect);
+ VD_IS_LOCKED(pDisk);
+
fFlush = VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_FLUSH;
VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
@@ -3997,33 +3736,187 @@ static int vdMetaXferCompleted(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnCo
else if (fFlush)
RTMemFree(pMetaXfer);
- vdDiskCritSectLeave(pDisk, NULL);
-
return VINF_SUCCESS;
}
-static int vdIOIntReqCompleted(void *pvUser, int rcReq)
+/**
+ * Processes a list of waiting I/O tasks. The disk lock must be held by caller.
+ *
+ * @returns nothing.
+ * @param pDisk The disk to process the list for.
+ */
+static void vdIoTaskProcessWaitingList(PVBOXHDD pDisk)
+{
+ LogFlowFunc(("pDisk=%#p\n", pDisk));
+
+ VD_IS_LOCKED(pDisk);
+
+ PVDIOTASK pHead = ASMAtomicXchgPtrT(&pDisk->pIoTasksPendingHead, NULL, PVDIOTASK);
+
+ Log(("I/O task list cleared\n"));
+
+ /* Reverse order. */
+ PVDIOTASK pCur = pHead;
+ pHead = NULL;
+ while (pCur)
+ {
+ PVDIOTASK pInsert = pCur;
+ pCur = pCur->pNext;
+ pInsert->pNext = pHead;
+ pHead = pInsert;
+ }
+
+ while (pHead)
+ {
+ PVDIOSTORAGE pIoStorage = pHead->pIoStorage;
+
+ if (!pHead->fMeta)
+ vdUserXferCompleted(pIoStorage, pHead->Type.User.pIoCtx,
+ pHead->pfnComplete, pHead->pvUser,
+ pHead->Type.User.cbTransfer, pHead->rcReq);
+ else
+ vdMetaXferCompleted(pIoStorage, pHead->pfnComplete, pHead->pvUser,
+ pHead->Type.Meta.pMetaXfer, pHead->rcReq);
+
+ pCur = pHead;
+ pHead = pHead->pNext;
+ vdIoTaskFree(pDisk, pCur);
+ }
+}
+
+/**
+ * Process any I/O context on the halted list.
+ *
+ * @returns nothing.
+ * @param pDisk The disk.
+ */
+static void vdIoCtxProcessHaltedList(PVBOXHDD pDisk)
+{
+ LogFlowFunc(("pDisk=%#p\n", pDisk));
+
+ VD_IS_LOCKED(pDisk);
+
+ /* Get the waiting list and process it in FIFO order. */
+ PVDIOCTX pIoCtxHead = ASMAtomicXchgPtrT(&pDisk->pIoCtxHaltedHead, NULL, PVDIOCTX);
+
+ /* Reverse it. */
+ PVDIOCTX pCur = pIoCtxHead;
+ pIoCtxHead = NULL;
+ while (pCur)
+ {
+ PVDIOCTX pInsert = pCur;
+ pCur = pCur->pIoCtxNext;
+ pInsert->pIoCtxNext = pIoCtxHead;
+ pIoCtxHead = pInsert;
+ }
+
+ /* Process now. */
+ pCur = pIoCtxHead;
+ while (pCur)
+ {
+ PVDIOCTX pTmp = pCur;
+
+ pCur = pCur->pIoCtxNext;
+ pTmp->pIoCtxNext = NULL;
+
+ /* Continue */
+ pTmp->fFlags &= ~VDIOCTX_FLAGS_BLOCKED;
+ vdIoCtxContinue(pTmp, pTmp->rcReq);
+ }
+}
+
+/**
+ * Unlock the disk and process pending tasks.
+ *
+ * @returns VBox status code.
+ * @param pDisk The disk to unlock.
+ */
+static int vdDiskUnlock(PVBOXHDD pDisk, PVDIOCTX pIoCtxRc)
{
int rc = VINF_SUCCESS;
- PVDIOTASK pIoTask = (PVDIOTASK)pvUser;
- PVDIOSTORAGE pIoStorage = pIoTask->pIoStorage;
- LogFlowFunc(("Task completed pIoTask=%#p\n", pIoTask));
+ VD_IS_LOCKED(pDisk);
- if (!pIoTask->fMeta)
- rc = vdUserXferCompleted(pIoStorage, pIoTask->Type.User.pIoCtx,
- pIoTask->pfnComplete, pIoTask->pvUser,
- pIoTask->Type.User.cbTransfer, rcReq);
- else
- rc = vdMetaXferCompleted(pIoStorage, pIoTask->pfnComplete, pIoTask->pvUser,
- pIoTask->Type.Meta.pMetaXfer, rcReq);
+ /*
+ * Process the list of waiting I/O tasks first
+ * because they might complete I/O contexts.
+ * Same for the list of halted I/O contexts.
+ * Afterwards comes the list of new I/O contexts.
+ */
+ vdIoTaskProcessWaitingList(pDisk);
+ vdIoCtxProcessHaltedList(pDisk);
+ rc = vdDiskProcessWaitingIoCtx(pDisk, pIoCtxRc);
+ ASMAtomicXchgBool(&pDisk->fLocked, false);
- vdIoTaskFree(pIoStorage->pVDIo->pDisk, pIoTask);
+ /*
+ * Need to check for new I/O tasks and waiting I/O contexts now
+ * again as other threads might added them while we processed
+ * previous lists.
+ */
+ while ( ASMAtomicUoReadPtrT(&pDisk->pIoCtxHead, PVDIOCTX) != NULL
+ || ASMAtomicUoReadPtrT(&pDisk->pIoTasksPendingHead, PVDIOTASK) != NULL
+ || ASMAtomicUoReadPtrT(&pDisk->pIoCtxHaltedHead, PVDIOCTX) != NULL)
+ {
+ /* Try lock disk again. */
+ if (ASMAtomicCmpXchgBool(&pDisk->fLocked, true, false))
+ {
+ vdIoTaskProcessWaitingList(pDisk);
+ vdIoCtxProcessHaltedList(pDisk);
+ vdDiskProcessWaitingIoCtx(pDisk, NULL);
+ ASMAtomicXchgBool(&pDisk->fLocked, false);
+ }
+ else /* Let the other thread everything when he unlocks the disk. */
+ break;
+ }
return rc;
}
/**
+ * Try to lock the disk to complete pressing of the I/O task.
+ * The completion is deferred if the disk is locked already.
+ *
+ * @returns nothing.
+ * @param pIoTask The I/O task to complete.
+ */
+static void vdXferTryLockDiskDeferIoTask(PVDIOTASK pIoTask)
+{
+ PVDIOSTORAGE pIoStorage = pIoTask->pIoStorage;
+ PVBOXHDD pDisk = pIoStorage->pVDIo->pDisk;
+
+ Log(("Deferring I/O task pIoTask=%p\n", pIoTask));
+
+ /* Put it on the waiting list. */
+ PVDIOTASK pNext = ASMAtomicUoReadPtrT(&pDisk->pIoTasksPendingHead, PVDIOTASK);
+ PVDIOTASK pHeadOld;
+ pIoTask->pNext = pNext;
+ while (!ASMAtomicCmpXchgExPtr(&pDisk->pIoTasksPendingHead, pIoTask, pNext, &pHeadOld))
+ {
+ pNext = pHeadOld;
+ Assert(pNext != pIoTask);
+ pIoTask->pNext = pNext;
+ ASMNopPause();
+ }
+
+ if (ASMAtomicCmpXchgBool(&pDisk->fLocked, true, false))
+ {
+ /* Release disk lock, it will take care of processing all lists. */
+ vdDiskUnlock(pDisk, NULL);
+ }
+}
+
+static int vdIOIntReqCompleted(void *pvUser, int rcReq)
+{
+ PVDIOTASK pIoTask = (PVDIOTASK)pvUser;
+
+ LogFlowFunc(("Task completed pIoTask=%#p\n", pIoTask));
+
+ pIoTask->rcReq = rcReq;
+ vdXferTryLockDiskDeferIoTask(pIoTask);
+ return VINF_SUCCESS;
+}
+
+/**
* VD I/O interface callback for opening a file.
*/
static int vdIOIntOpen(void *pvUser, const char *pszLocation,
@@ -4068,16 +3961,15 @@ static int vdIOIntTreeMetaXferDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
static int vdIOIntClose(void *pvUser, PVDIOSTORAGE pIoStorage)
{
- PVDIO pVDIo = (PVDIO)pvUser;
-
- int rc = pVDIo->pInterfaceIo->pfnClose(pVDIo->pInterfaceIo->Core.pvUser,
- pIoStorage->pStorage);
- AssertRC(rc);
+ int rc = VINF_SUCCESS;
+ PVDIO pVDIo = (PVDIO)pvUser;
+ /* We free everything here, even if closing the file failed for some reason. */
+ rc = pVDIo->pInterfaceIo->pfnClose(pVDIo->pInterfaceIo->Core.pvUser, pIoStorage->pStorage);
RTAvlrFileOffsetDestroy(pIoStorage->pTreeMetaXfers, vdIOIntTreeMetaXferDestroy, NULL);
RTMemFree(pIoStorage->pTreeMetaXfers);
RTMemFree(pIoStorage);
- return VINF_SUCCESS;
+ return rc;
}
static int vdIOIntDelete(void *pvUser, const char *pcszFilename)
@@ -4127,41 +4019,8 @@ static int vdIOIntSetSize(void *pvUser, PVDIOSTORAGE pIoStorage,
pIoStorage->pStorage, cbSize);
}
-static int vdIOIntWriteSync(void *pvUser, PVDIOSTORAGE pIoStorage,
- uint64_t uOffset, const void *pvBuf,
- size_t cbWrite, size_t *pcbWritten)
-{
- PVDIO pVDIo = (PVDIO)pvUser;
- return pVDIo->pInterfaceIo->pfnWriteSync(pVDIo->pInterfaceIo->Core.pvUser,
- pIoStorage->pStorage, uOffset,
- pvBuf, cbWrite, pcbWritten);
-}
-
-static int vdIOIntReadSync(void *pvUser, PVDIOSTORAGE pIoStorage,
- uint64_t uOffset, void *pvBuf, size_t cbRead,
- size_t *pcbRead)
-{
- PVDIO pVDIo = (PVDIO)pvUser;
- return pVDIo->pInterfaceIo->pfnReadSync(pVDIo->pInterfaceIo->Core.pvUser,
- pIoStorage->pStorage, uOffset,
- pvBuf, cbRead, pcbRead);
-}
-
-static int vdIOIntFlushSync(void *pvUser, PVDIOSTORAGE pIoStorage)
-{
- int rc = VINF_SUCCESS;
- PVDIO pVDIo = (PVDIO)pvUser;
-
- if (!pVDIo->fIgnoreFlush)
- rc = pVDIo->pInterfaceIo->pfnFlushSync(pVDIo->pInterfaceIo->Core.pvUser,
- pIoStorage->pStorage);
-
- return rc;
-}
-
-static int vdIOIntReadUserAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
- uint64_t uOffset, PVDIOCTX pIoCtx,
- size_t cbRead)
+static int vdIOIntReadUser(void *pvUser, PVDIOSTORAGE pIoStorage, uint64_t uOffset,
+ PVDIOCTX pIoCtx, size_t cbRead)
{
int rc = VINF_SUCCESS;
PVDIO pVDIo = (PVDIO)pvUser;
@@ -4170,70 +4029,96 @@ static int vdIOIntReadUserAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pIoCtx=%#p cbRead=%u\n",
pvUser, pIoStorage, uOffset, pIoCtx, cbRead));
- VD_THREAD_IS_CRITSECT_OWNER(pDisk);
+ /** @todo: Enable check for sync I/O later. */
+ if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
+ VD_IS_LOCKED(pDisk);
Assert(cbRead > 0);
- /* Build the S/G array and spawn a new I/O task */
- while (cbRead)
+ if (pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC)
{
- RTSGSEG aSeg[VD_IO_TASK_SEGMENTS_MAX];
- unsigned cSegments = VD_IO_TASK_SEGMENTS_MAX;
- size_t cbTaskRead = 0;
+ RTSGSEG Seg;
+ unsigned cSegments = 1;
+ size_t cbTaskRead = 0;
- cbTaskRead = RTSgBufSegArrayCreate(&pIoCtx->Req.Io.SgBuf, aSeg, &cSegments, cbRead);
+ /* Synchronous I/O contexts only have one buffer segment. */
+ AssertMsgReturn(pIoCtx->Req.Io.SgBuf.cSegs == 1,
+ ("Invalid number of buffer segments for synchronous I/O context"),
+ VERR_INVALID_PARAMETER);
+
+ cbTaskRead = RTSgBufSegArrayCreate(&pIoCtx->Req.Io.SgBuf, &Seg, &cSegments, cbRead);
+ Assert(cbRead == cbTaskRead);
+ Assert(cSegments == 1);
+ rc = pVDIo->pInterfaceIo->pfnReadSync(pVDIo->pInterfaceIo->Core.pvUser,
+ pIoStorage->pStorage, uOffset,
+ Seg.pvSeg, cbRead, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(cbRead == (uint32_t)cbRead);
+ ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, (uint32_t)cbRead);
+ }
+ }
+ else
+ {
+ /* Build the S/G array and spawn a new I/O task */
+ while (cbRead)
+ {
+ RTSGSEG aSeg[VD_IO_TASK_SEGMENTS_MAX];
+ unsigned cSegments = VD_IO_TASK_SEGMENTS_MAX;
+ size_t cbTaskRead = RTSgBufSegArrayCreate(&pIoCtx->Req.Io.SgBuf, aSeg, &cSegments, cbRead);
- Assert(cSegments > 0);
- Assert(cbTaskRead > 0);
- AssertMsg(cbTaskRead <= cbRead, ("Invalid number of bytes to read\n"));
+ Assert(cSegments > 0);
+ Assert(cbTaskRead > 0);
+ AssertMsg(cbTaskRead <= cbRead, ("Invalid number of bytes to read\n"));
- LogFlow(("Reading %u bytes into %u segments\n", cbTaskRead, cSegments));
+ LogFlow(("Reading %u bytes into %u segments\n", cbTaskRead, cSegments));
#ifdef RT_STRICT
- for (unsigned i = 0; i < cSegments; i++)
- AssertMsg(aSeg[i].pvSeg && !(aSeg[i].cbSeg % 512),
- ("Segment %u is invalid\n", i));
+ for (unsigned i = 0; i < cSegments; i++)
+ AssertMsg(aSeg[i].pvSeg && !(aSeg[i].cbSeg % 512),
+ ("Segment %u is invalid\n", i));
#endif
- PVDIOTASK pIoTask = vdIoTaskUserAlloc(pIoStorage, NULL, NULL, pIoCtx, cbTaskRead);
+ Assert(cbTaskRead == (uint32_t)cbTaskRead);
+ PVDIOTASK pIoTask = vdIoTaskUserAlloc(pIoStorage, NULL, NULL, pIoCtx, (uint32_t)cbTaskRead);
- if (!pIoTask)
- return VERR_NO_MEMORY;
+ if (!pIoTask)
+ return VERR_NO_MEMORY;
- ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
+ ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
- void *pvTask;
- rc = pVDIo->pInterfaceIo->pfnReadAsync(pVDIo->pInterfaceIo->Core.pvUser,
- pIoStorage->pStorage, uOffset,
- aSeg, cSegments, cbTaskRead, pIoTask,
- &pvTask);
- if (RT_SUCCESS(rc))
- {
- AssertMsg(cbTaskRead <= pIoCtx->Req.Io.cbTransferLeft, ("Impossible!\n"));
- ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbTaskRead);
- ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
- vdIoTaskFree(pDisk, pIoTask);
- }
- else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
- {
- ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
- vdIoTaskFree(pDisk, pIoTask);
- break;
- }
+ void *pvTask;
+ Log(("Spawning pIoTask=%p pIoCtx=%p\n", pIoTask, pIoCtx));
+ rc = pVDIo->pInterfaceIo->pfnReadAsync(pVDIo->pInterfaceIo->Core.pvUser,
+ pIoStorage->pStorage, uOffset,
+ aSeg, cSegments, cbTaskRead, pIoTask,
+ &pvTask);
+ if (RT_SUCCESS(rc))
+ {
+ AssertMsg(cbTaskRead <= pIoCtx->Req.Io.cbTransferLeft, ("Impossible!\n"));
+ ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, (uint32_t)cbTaskRead);
+ ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
+ vdIoTaskFree(pDisk, pIoTask);
+ }
+ else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
+ {
+ ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
+ vdIoTaskFree(pDisk, pIoTask);
+ break;
+ }
- uOffset += cbTaskRead;
- cbRead -= cbTaskRead;
+ uOffset += cbTaskRead;
+ cbRead -= cbTaskRead;
+ }
}
LogFlowFunc(("returns rc=%Rrc\n", rc));
return rc;
}
-static int vdIOIntWriteUserAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
- uint64_t uOffset, PVDIOCTX pIoCtx,
- size_t cbWrite,
- PFNVDXFERCOMPLETED pfnComplete,
- void *pvCompleteUser)
+static int vdIOIntWriteUser(void *pvUser, PVDIOSTORAGE pIoStorage, uint64_t uOffset,
+ PVDIOCTX pIoCtx, size_t cbWrite, PFNVDXFERCOMPLETED pfnComplete,
+ void *pvCompleteUser)
{
int rc = VINF_SUCCESS;
PVDIO pVDIo = (PVDIO)pvUser;
@@ -4242,70 +4127,99 @@ static int vdIOIntWriteUserAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pIoCtx=%#p cbWrite=%u\n",
pvUser, pIoStorage, uOffset, pIoCtx, cbWrite));
- VD_THREAD_IS_CRITSECT_OWNER(pDisk);
+ /** @todo: Enable check for sync I/O later. */
+ if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
+ VD_IS_LOCKED(pDisk);
Assert(cbWrite > 0);
- /* Build the S/G array and spawn a new I/O task */
- while (cbWrite)
+ if (pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC)
{
- RTSGSEG aSeg[VD_IO_TASK_SEGMENTS_MAX];
- unsigned cSegments = VD_IO_TASK_SEGMENTS_MAX;
- size_t cbTaskWrite = 0;
+ RTSGSEG Seg;
+ unsigned cSegments = 1;
+ size_t cbTaskWrite = 0;
- cbTaskWrite = RTSgBufSegArrayCreate(&pIoCtx->Req.Io.SgBuf, aSeg, &cSegments, cbWrite);
+ /* Synchronous I/O contexts only have one buffer segment. */
+ AssertMsgReturn(pIoCtx->Req.Io.SgBuf.cSegs == 1,
+ ("Invalid number of buffer segments for synchronous I/O context"),
+ VERR_INVALID_PARAMETER);
- Assert(cSegments > 0);
- Assert(cbTaskWrite > 0);
- AssertMsg(cbTaskWrite <= cbWrite, ("Invalid number of bytes to write\n"));
+ cbTaskWrite = RTSgBufSegArrayCreate(&pIoCtx->Req.Io.SgBuf, &Seg, &cSegments, cbWrite);
+ Assert(cbWrite == cbTaskWrite);
+ Assert(cSegments == 1);
+ rc = pVDIo->pInterfaceIo->pfnWriteSync(pVDIo->pInterfaceIo->Core.pvUser,
+ pIoStorage->pStorage, uOffset,
+ Seg.pvSeg, cbWrite, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(pIoCtx->Req.Io.cbTransferLeft >= cbWrite);
+ ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, (uint32_t)cbWrite);
+ }
+ }
+ else
+ {
+ /* Build the S/G array and spawn a new I/O task */
+ while (cbWrite)
+ {
+ RTSGSEG aSeg[VD_IO_TASK_SEGMENTS_MAX];
+ unsigned cSegments = VD_IO_TASK_SEGMENTS_MAX;
+ size_t cbTaskWrite = 0;
- LogFlow(("Writing %u bytes from %u segments\n", cbTaskWrite, cSegments));
+ cbTaskWrite = RTSgBufSegArrayCreate(&pIoCtx->Req.Io.SgBuf, aSeg, &cSegments, cbWrite);
+
+ Assert(cSegments > 0);
+ Assert(cbTaskWrite > 0);
+ AssertMsg(cbTaskWrite <= cbWrite, ("Invalid number of bytes to write\n"));
+
+ LogFlow(("Writing %u bytes from %u segments\n", cbTaskWrite, cSegments));
#ifdef DEBUG
- for (unsigned i = 0; i < cSegments; i++)
- AssertMsg(aSeg[i].pvSeg && !(aSeg[i].cbSeg % 512),
- ("Segment %u is invalid\n", i));
+ for (unsigned i = 0; i < cSegments; i++)
+ AssertMsg(aSeg[i].pvSeg && !(aSeg[i].cbSeg % 512),
+ ("Segment %u is invalid\n", i));
#endif
- PVDIOTASK pIoTask = vdIoTaskUserAlloc(pIoStorage, pfnComplete, pvCompleteUser, pIoCtx, cbTaskWrite);
+ Assert(cbTaskWrite == (uint32_t)cbTaskWrite);
+ PVDIOTASK pIoTask = vdIoTaskUserAlloc(pIoStorage, pfnComplete, pvCompleteUser, pIoCtx, (uint32_t)cbTaskWrite);
- if (!pIoTask)
- return VERR_NO_MEMORY;
+ if (!pIoTask)
+ return VERR_NO_MEMORY;
- ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
+ ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
- void *pvTask;
- rc = pVDIo->pInterfaceIo->pfnWriteAsync(pVDIo->pInterfaceIo->Core.pvUser,
- pIoStorage->pStorage,
- uOffset, aSeg, cSegments,
- cbTaskWrite, pIoTask, &pvTask);
- if (RT_SUCCESS(rc))
- {
- AssertMsg(cbTaskWrite <= pIoCtx->Req.Io.cbTransferLeft, ("Impossible!\n"));
- ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbTaskWrite);
- ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
- vdIoTaskFree(pDisk, pIoTask);
- }
- else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
- {
- ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
- vdIoTaskFree(pDisk, pIoTask);
- break;
- }
+ void *pvTask;
+ Log(("Spawning pIoTask=%p pIoCtx=%p\n", pIoTask, pIoCtx));
+ rc = pVDIo->pInterfaceIo->pfnWriteAsync(pVDIo->pInterfaceIo->Core.pvUser,
+ pIoStorage->pStorage,
+ uOffset, aSeg, cSegments,
+ cbTaskWrite, pIoTask, &pvTask);
+ if (RT_SUCCESS(rc))
+ {
+ AssertMsg(cbTaskWrite <= pIoCtx->Req.Io.cbTransferLeft, ("Impossible!\n"));
+ ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, (uint32_t)cbTaskWrite);
+ ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
+ vdIoTaskFree(pDisk, pIoTask);
+ }
+ else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
+ {
+ ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
+ vdIoTaskFree(pDisk, pIoTask);
+ break;
+ }
- uOffset += cbTaskWrite;
- cbWrite -= cbTaskWrite;
+ uOffset += cbTaskWrite;
+ cbWrite -= cbTaskWrite;
+ }
}
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
return rc;
}
-static int vdIOIntReadMetaAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
- uint64_t uOffset, void *pvBuf,
- size_t cbRead, PVDIOCTX pIoCtx,
- PPVDMETAXFER ppMetaXfer,
- PFNVDXFERCOMPLETED pfnComplete,
- void *pvCompleteUser)
+static int vdIOIntReadMeta(void *pvUser, PVDIOSTORAGE pIoStorage, uint64_t uOffset,
+ void *pvBuf, size_t cbRead, PVDIOCTX pIoCtx,
+ PPVDMETAXFER ppMetaXfer, PFNVDXFERCOMPLETED pfnComplete,
+ void *pvCompleteUser)
{
PVDIO pVDIo = (PVDIO)pvUser;
PVBOXHDD pDisk = pVDIo->pDisk;
@@ -4318,91 +4232,112 @@ static int vdIOIntReadMetaAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pvBuf=%#p cbRead=%u\n",
pvUser, pIoStorage, uOffset, pvBuf, cbRead));
- VD_THREAD_IS_CRITSECT_OWNER(pDisk);
+ AssertMsgReturn( pIoCtx
+ || (!ppMetaXfer && !pfnComplete && !pvCompleteUser),
+ ("A synchronous metadata read is requested but the parameters are wrong\n"),
+ VERR_INVALID_POINTER);
- pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGet(pIoStorage->pTreeMetaXfers, uOffset);
- if (!pMetaXfer)
+ /** @todo: Enable check for sync I/O later. */
+ if ( pIoCtx
+ && !(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
+ VD_IS_LOCKED(pDisk);
+
+ if ( !pIoCtx
+ || pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC)
{
+ /* Handle synchronous metadata I/O. */
+ /** @todo: Integrate with metadata transfers below. */
+ rc = pVDIo->pInterfaceIo->pfnReadSync(pVDIo->pInterfaceIo->Core.pvUser,
+ pIoStorage->pStorage, uOffset,
+ pvBuf, cbRead, NULL);
+ if (ppMetaXfer)
+ *ppMetaXfer = NULL;
+ }
+ else
+ {
+ pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGet(pIoStorage->pTreeMetaXfers, uOffset);
+ if (!pMetaXfer)
+ {
#ifdef RT_STRICT
- pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGetBestFit(pIoStorage->pTreeMetaXfers, uOffset, false /* fAbove */);
- AssertMsg(!pMetaXfer || (pMetaXfer->Core.Key + (RTFOFF)pMetaXfer->cbMeta <= (RTFOFF)uOffset),
- ("Overlapping meta transfers!\n"));
+ pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGetBestFit(pIoStorage->pTreeMetaXfers, uOffset, false /* fAbove */);
+ AssertMsg(!pMetaXfer || (pMetaXfer->Core.Key + (RTFOFF)pMetaXfer->cbMeta <= (RTFOFF)uOffset),
+ ("Overlapping meta transfers!\n"));
#endif
- /* Allocate a new meta transfer. */
- pMetaXfer = vdMetaXferAlloc(pIoStorage, uOffset, cbRead);
- if (!pMetaXfer)
- return VERR_NO_MEMORY;
+ /* Allocate a new meta transfer. */
+ pMetaXfer = vdMetaXferAlloc(pIoStorage, uOffset, cbRead);
+ if (!pMetaXfer)
+ return VERR_NO_MEMORY;
- pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvCompleteUser, pMetaXfer);
- if (!pIoTask)
- {
- RTMemFree(pMetaXfer);
- return VERR_NO_MEMORY;
- }
+ pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvCompleteUser, pMetaXfer);
+ if (!pIoTask)
+ {
+ RTMemFree(pMetaXfer);
+ return VERR_NO_MEMORY;
+ }
- Seg.cbSeg = cbRead;
- Seg.pvSeg = pMetaXfer->abData;
+ Seg.cbSeg = cbRead;
+ Seg.pvSeg = pMetaXfer->abData;
- VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_READ);
- rc = pVDIo->pInterfaceIo->pfnReadAsync(pVDIo->pInterfaceIo->Core.pvUser,
- pIoStorage->pStorage,
- uOffset, &Seg, 1,
- cbRead, pIoTask, &pvTask);
+ VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_READ);
+ rc = pVDIo->pInterfaceIo->pfnReadAsync(pVDIo->pInterfaceIo->Core.pvUser,
+ pIoStorage->pStorage,
+ uOffset, &Seg, 1,
+ cbRead, pIoTask, &pvTask);
- if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
- {
- bool fInserted = RTAvlrFileOffsetInsert(pIoStorage->pTreeMetaXfers, &pMetaXfer->Core);
- Assert(fInserted);
- }
- else
- RTMemFree(pMetaXfer);
+ if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+ {
+ bool fInserted = RTAvlrFileOffsetInsert(pIoStorage->pTreeMetaXfers, &pMetaXfer->Core);
+ Assert(fInserted);
+ }
+ else
+ RTMemFree(pMetaXfer);
- if (RT_SUCCESS(rc))
- {
- VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
- vdIoTaskFree(pDisk, pIoTask);
+ if (RT_SUCCESS(rc))
+ {
+ VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
+ vdIoTaskFree(pDisk, pIoTask);
+ }
+ else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS && !pfnComplete)
+ rc = VERR_VD_NOT_ENOUGH_METADATA;
}
- else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS && !pfnComplete)
- rc = VERR_VD_NOT_ENOUGH_METADATA;
- }
- Assert(VALID_PTR(pMetaXfer) || RT_FAILURE(rc));
+ Assert(VALID_PTR(pMetaXfer) || RT_FAILURE(rc));
- if (RT_SUCCESS(rc) || rc == VERR_VD_NOT_ENOUGH_METADATA || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
- {
- /* If it is pending add the request to the list. */
- if (VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_READ)
+ if (RT_SUCCESS(rc) || rc == VERR_VD_NOT_ENOUGH_METADATA || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
{
- PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
- AssertPtr(pDeferred);
+ /* If it is pending add the request to the list. */
+ if (VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_READ)
+ {
+ PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
+ AssertPtr(pDeferred);
- RTListInit(&pDeferred->NodeDeferred);
- pDeferred->pIoCtx = pIoCtx;
+ RTListInit(&pDeferred->NodeDeferred);
+ pDeferred->pIoCtx = pIoCtx;
- ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
- RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
- rc = VERR_VD_NOT_ENOUGH_METADATA;
- }
- else
- {
- /* Transfer the data. */
- pMetaXfer->cRefs++;
- Assert(pMetaXfer->cbMeta >= cbRead);
- Assert(pMetaXfer->Core.Key == (RTFOFF)uOffset);
- memcpy(pvBuf, pMetaXfer->abData, cbRead);
- *ppMetaXfer = pMetaXfer;
+ ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
+ RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
+ rc = VERR_VD_NOT_ENOUGH_METADATA;
+ }
+ else
+ {
+ /* Transfer the data. */
+ pMetaXfer->cRefs++;
+ Assert(pMetaXfer->cbMeta >= cbRead);
+ Assert(pMetaXfer->Core.Key == (RTFOFF)uOffset);
+ memcpy(pvBuf, pMetaXfer->abData, cbRead);
+ *ppMetaXfer = pMetaXfer;
+ }
}
}
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
return rc;
}
-static int vdIOIntWriteMetaAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
- uint64_t uOffset, void *pvBuf,
- size_t cbWrite, PVDIOCTX pIoCtx,
- PFNVDXFERCOMPLETED pfnComplete,
- void *pvCompleteUser)
+static int vdIOIntWriteMeta(void *pvUser, PVDIOSTORAGE pIoStorage, uint64_t uOffset,
+ const void *pvBuf, size_t cbWrite, PVDIOCTX pIoCtx,
+ PFNVDXFERCOMPLETED pfnComplete, void *pvCompleteUser)
{
PVDIO pVDIo = (PVDIO)pvUser;
PVBOXHDD pDisk = pVDIo->pDisk;
@@ -4416,79 +4351,100 @@ static int vdIOIntWriteMetaAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pvBuf=%#p cbWrite=%u\n",
pvUser, pIoStorage, uOffset, pvBuf, cbWrite));
- VD_THREAD_IS_CRITSECT_OWNER(pDisk);
+ AssertMsgReturn( pIoCtx
+ || (!pfnComplete && !pvCompleteUser),
+ ("A synchronous metadata write is requested but the parameters are wrong\n"),
+ VERR_INVALID_POINTER);
- pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGet(pIoStorage->pTreeMetaXfers, uOffset);
- if (!pMetaXfer)
+ /** @todo: Enable check for sync I/O later. */
+ if ( pIoCtx
+ && !(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
+ VD_IS_LOCKED(pDisk);
+
+ if ( !pIoCtx
+ || pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC)
{
- /* Allocate a new meta transfer. */
- pMetaXfer = vdMetaXferAlloc(pIoStorage, uOffset, cbWrite);
- if (!pMetaXfer)
- return VERR_NO_MEMORY;
+ /* Handle synchronous metadata I/O. */
+ /** @todo: Integrate with metadata transfers below. */
+ rc = pVDIo->pInterfaceIo->pfnWriteSync(pVDIo->pInterfaceIo->Core.pvUser,
+ pIoStorage->pStorage, uOffset,
+ pvBuf, cbWrite, NULL);
}
else
{
- Assert(pMetaXfer->cbMeta >= cbWrite);
- Assert(pMetaXfer->Core.Key == (RTFOFF)uOffset);
- fInTree = true;
- }
+ pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGet(pIoStorage->pTreeMetaXfers, uOffset);
+ if (!pMetaXfer)
+ {
+ /* Allocate a new meta transfer. */
+ pMetaXfer = vdMetaXferAlloc(pIoStorage, uOffset, cbWrite);
+ if (!pMetaXfer)
+ return VERR_NO_MEMORY;
+ }
+ else
+ {
+ Assert(pMetaXfer->cbMeta >= cbWrite);
+ Assert(pMetaXfer->Core.Key == (RTFOFF)uOffset);
+ fInTree = true;
+ }
- Assert(VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE);
+ Assert(VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE);
- pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvCompleteUser, pMetaXfer);
- if (!pIoTask)
- {
- RTMemFree(pMetaXfer);
- return VERR_NO_MEMORY;
- }
+ pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvCompleteUser, pMetaXfer);
+ if (!pIoTask)
+ {
+ RTMemFree(pMetaXfer);
+ return VERR_NO_MEMORY;
+ }
- memcpy(pMetaXfer->abData, pvBuf, cbWrite);
- Seg.cbSeg = cbWrite;
- Seg.pvSeg = pMetaXfer->abData;
+ memcpy(pMetaXfer->abData, pvBuf, cbWrite);
+ Seg.cbSeg = cbWrite;
+ Seg.pvSeg = pMetaXfer->abData;
- ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
+ ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
- VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_WRITE);
- rc = pVDIo->pInterfaceIo->pfnWriteAsync(pVDIo->pInterfaceIo->Core.pvUser,
- pIoStorage->pStorage,
- uOffset, &Seg, 1, cbWrite, pIoTask,
- &pvTask);
- if (RT_SUCCESS(rc))
- {
- VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
- ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
- vdIoTaskFree(pDisk, pIoTask);
- if (fInTree && !pMetaXfer->cRefs)
+ VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_WRITE);
+ rc = pVDIo->pInterfaceIo->pfnWriteAsync(pVDIo->pInterfaceIo->Core.pvUser,
+ pIoStorage->pStorage,
+ uOffset, &Seg, 1, cbWrite, pIoTask,
+ &pvTask);
+ if (RT_SUCCESS(rc))
{
- LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
- bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
- AssertMsg(fRemoved, ("Metadata transfer wasn't removed\n"));
- RTMemFree(pMetaXfer);
- pMetaXfer = NULL;
+ VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
+ ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
+ vdIoTaskFree(pDisk, pIoTask);
+ if (fInTree && !pMetaXfer->cRefs)
+ {
+ LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
+ bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
+ AssertMsg(fRemoved, ("Metadata transfer wasn't removed\n"));
+ RTMemFree(pMetaXfer);
+ pMetaXfer = NULL;
+ }
}
- }
- else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
- {
- PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
- AssertPtr(pDeferred);
+ else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+ {
+ PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
+ AssertPtr(pDeferred);
- RTListInit(&pDeferred->NodeDeferred);
- pDeferred->pIoCtx = pIoCtx;
+ RTListInit(&pDeferred->NodeDeferred);
+ pDeferred->pIoCtx = pIoCtx;
+
+ if (!fInTree)
+ {
+ bool fInserted = RTAvlrFileOffsetInsert(pIoStorage->pTreeMetaXfers, &pMetaXfer->Core);
+ Assert(fInserted);
+ }
- if (!fInTree)
+ RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
+ }
+ else
{
- bool fInserted = RTAvlrFileOffsetInsert(pIoStorage->pTreeMetaXfers, &pMetaXfer->Core);
- Assert(fInserted);
+ RTMemFree(pMetaXfer);
+ pMetaXfer = NULL;
}
-
- RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
- }
- else
- {
- RTMemFree(pMetaXfer);
- pMetaXfer = NULL;
}
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
return rc;
}
@@ -4496,9 +4452,18 @@ static void vdIOIntMetaXferRelease(void *pvUser, PVDMETAXFER pMetaXfer)
{
PVDIO pVDIo = (PVDIO)pvUser;
PVBOXHDD pDisk = pVDIo->pDisk;
- PVDIOSTORAGE pIoStorage = pMetaXfer->pIoStorage;
+ PVDIOSTORAGE pIoStorage;
- VD_THREAD_IS_CRITSECT_OWNER(pDisk);
+ /*
+ * It is possible that we get called with a NULL metadata xfer handle
+ * for synchronous I/O. Just exit.
+ */
+ if (!pMetaXfer)
+ return;
+
+ pIoStorage = pMetaXfer->pIoStorage;
+
+ VD_IS_LOCKED(pDisk);
Assert( VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE
|| VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_WRITE);
@@ -4518,9 +4483,8 @@ static void vdIOIntMetaXferRelease(void *pvUser, PVDMETAXFER pMetaXfer)
}
}
-static int vdIOIntFlushAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
- PVDIOCTX pIoCtx, PFNVDXFERCOMPLETED pfnComplete,
- void *pvCompleteUser)
+static int vdIOIntFlush(void *pvUser, PVDIOSTORAGE pIoStorage, PVDIOCTX pIoCtx,
+ PFNVDXFERCOMPLETED pfnComplete, void *pvCompleteUser)
{
PVDIO pVDIo = (PVDIO)pvUser;
PVBOXHDD pDisk = pVDIo->pDisk;
@@ -4529,66 +4493,89 @@ static int vdIOIntFlushAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
PVDMETAXFER pMetaXfer = NULL;
void *pvTask = NULL;
- VD_THREAD_IS_CRITSECT_OWNER(pDisk);
-
LogFlowFunc(("pvUser=%#p pIoStorage=%#p pIoCtx=%#p\n",
pvUser, pIoStorage, pIoCtx));
+ AssertMsgReturn( pIoCtx
+ || (!pfnComplete && !pvCompleteUser),
+ ("A synchronous metadata write is requested but the parameters are wrong\n"),
+ VERR_INVALID_POINTER);
+
+ /** @todo: Enable check for sync I/O later. */
+ if ( pIoCtx
+ && !(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
+ VD_IS_LOCKED(pDisk);
+
if (pVDIo->fIgnoreFlush)
return VINF_SUCCESS;
- /* Allocate a new meta transfer. */
- pMetaXfer = vdMetaXferAlloc(pIoStorage, 0, 0);
- if (!pMetaXfer)
- return VERR_NO_MEMORY;
-
- pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvUser, pMetaXfer);
- if (!pIoTask)
+ if ( !pIoCtx
+ || pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC)
{
- RTMemFree(pMetaXfer);
- return VERR_NO_MEMORY;
+ /* Handle synchronous flushes. */
+ /** @todo: Integrate with metadata transfers below. */
+ rc = pVDIo->pInterfaceIo->pfnFlushSync(pVDIo->pInterfaceIo->Core.pvUser,
+ pIoStorage->pStorage);
}
+ else
+ {
+ /* Allocate a new meta transfer. */
+ pMetaXfer = vdMetaXferAlloc(pIoStorage, 0, 0);
+ if (!pMetaXfer)
+ return VERR_NO_MEMORY;
- ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
+ pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvUser, pMetaXfer);
+ if (!pIoTask)
+ {
+ RTMemFree(pMetaXfer);
+ return VERR_NO_MEMORY;
+ }
- PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
- AssertPtr(pDeferred);
+ ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
- RTListInit(&pDeferred->NodeDeferred);
- pDeferred->pIoCtx = pIoCtx;
+ PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
+ AssertPtr(pDeferred);
- RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
- VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_FLUSH);
- rc = pVDIo->pInterfaceIo->pfnFlushAsync(pVDIo->pInterfaceIo->Core.pvUser,
- pIoStorage->pStorage,
- pIoTask, &pvTask);
- if (RT_SUCCESS(rc))
- {
- VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
- ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
- vdIoTaskFree(pDisk, pIoTask);
- RTMemFree(pDeferred);
- RTMemFree(pMetaXfer);
+ RTListInit(&pDeferred->NodeDeferred);
+ pDeferred->pIoCtx = pIoCtx;
+
+ RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
+ VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_FLUSH);
+ rc = pVDIo->pInterfaceIo->pfnFlushAsync(pVDIo->pInterfaceIo->Core.pvUser,
+ pIoStorage->pStorage,
+ pIoTask, &pvTask);
+ if (RT_SUCCESS(rc))
+ {
+ VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
+ ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
+ vdIoTaskFree(pDisk, pIoTask);
+ RTMemFree(pDeferred);
+ RTMemFree(pMetaXfer);
+ }
+ else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
+ RTMemFree(pMetaXfer);
}
- else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
- RTMemFree(pMetaXfer);
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
return rc;
}
static size_t vdIOIntIoCtxCopyTo(void *pvUser, PVDIOCTX pIoCtx,
- void *pvBuf, size_t cbBuf)
+ const void *pvBuf, size_t cbBuf)
{
PVDIO pVDIo = (PVDIO)pvUser;
PVBOXHDD pDisk = pVDIo->pDisk;
size_t cbCopied = 0;
- VD_THREAD_IS_CRITSECT_OWNER(pDisk);
+ /** @todo: Enable check for sync I/O later. */
+ if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
+ VD_IS_LOCKED(pDisk);
cbCopied = vdIoCtxCopyTo(pIoCtx, (uint8_t *)pvBuf, cbBuf);
Assert(cbCopied == cbBuf);
- ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbCopied);
+ /// @todo Assert(pIoCtx->Req.Io.cbTransferLeft >= cbCopied); - triggers with vdCopyHelper/dmgRead.
+ ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, (uint32_t)cbCopied);
return cbCopied;
}
@@ -4600,12 +4587,15 @@ static size_t vdIOIntIoCtxCopyFrom(void *pvUser, PVDIOCTX pIoCtx,
PVBOXHDD pDisk = pVDIo->pDisk;
size_t cbCopied = 0;
- VD_THREAD_IS_CRITSECT_OWNER(pDisk);
+ /** @todo: Enable check for sync I/O later. */
+ if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
+ VD_IS_LOCKED(pDisk);
cbCopied = vdIoCtxCopyFrom(pIoCtx, (uint8_t *)pvBuf, cbBuf);
Assert(cbCopied == cbBuf);
- ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbCopied);
+ /// @todo Assert(pIoCtx->Req.Io.cbTransferLeft > cbCopied); - triggers with vdCopyHelper/dmgRead.
+ ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, (uint32_t)cbCopied);
return cbCopied;
}
@@ -4616,12 +4606,15 @@ static size_t vdIOIntIoCtxSet(void *pvUser, PVDIOCTX pIoCtx, int ch, size_t cb)
PVBOXHDD pDisk = pVDIo->pDisk;
size_t cbSet = 0;
- VD_THREAD_IS_CRITSECT_OWNER(pDisk);
+ /** @todo: Enable check for sync I/O later. */
+ if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
+ VD_IS_LOCKED(pDisk);
cbSet = vdIoCtxSet(pIoCtx, ch, cb);
Assert(cbSet == cb);
- ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbSet);
+ /// @todo Assert(pIoCtx->Req.Io.cbTransferLeft >= cbSet); - triggers with vdCopyHelper/dmgRead.
+ ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, (uint32_t)cbSet);
return cbSet;
}
@@ -4634,7 +4627,9 @@ static size_t vdIOIntIoCtxSegArrayCreate(void *pvUser, PVDIOCTX pIoCtx,
PVBOXHDD pDisk = pVDIo->pDisk;
size_t cbCreated = 0;
- VD_THREAD_IS_CRITSECT_OWNER(pDisk);
+ /** @todo: Enable check for sync I/O later. */
+ if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
+ VD_IS_LOCKED(pDisk);
cbCreated = RTSgBufSegArrayCreate(&pIoCtx->Req.Io.SgBuf, paSeg, pcSeg, cbData);
Assert(!paSeg || cbData == cbCreated);
@@ -4648,30 +4643,54 @@ static void vdIOIntIoCtxCompleted(void *pvUser, PVDIOCTX pIoCtx, int rcReq,
PVDIO pVDIo = (PVDIO)pvUser;
PVBOXHDD pDisk = pVDIo->pDisk;
+ LogFlowFunc(("pvUser=%#p pIoCtx=%#p rcReq=%Rrc cbCompleted=%zu\n",
+ pvUser, pIoCtx, rcReq, cbCompleted));
+
/*
* Grab the disk critical section to avoid races with other threads which
* might still modify the I/O context.
* Example is that iSCSI is doing an asynchronous write but calls us already
* while the other thread is still hanging in vdWriteHelperAsync and couldn't update
- * the fBlocked state yet.
+ * the blocked state yet.
* It can overwrite the state to true before we call vdIoCtxContinue and the
* the request would hang indefinite.
*/
- int rc = RTCritSectEnter(&pDisk->CritSect);
- AssertRC(rc);
+ ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rcReq, VINF_SUCCESS);
+ Assert(pIoCtx->Req.Io.cbTransferLeft >= cbCompleted);
+ ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, (uint32_t)cbCompleted);
- /* Continue */
- pIoCtx->fBlocked = false;
- ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbCompleted);
-
- /* Clear the pointer to next transfer function in case we have nothing to transfer anymore.
- * @todo: Find a better way to prevent vdIoCtxContinue from calling the read/write helper again. */
+ /* Set next transfer function if the current one finished.
+ * @todo: Find a better way to prevent vdIoCtxContinue from calling the current helper again. */
if (!pIoCtx->Req.Io.cbTransferLeft)
- pIoCtx->pfnIoCtxTransfer = NULL;
+ {
+ pIoCtx->pfnIoCtxTransfer = pIoCtx->pfnIoCtxTransferNext;
+ pIoCtx->pfnIoCtxTransferNext = NULL;
+ }
- vdIoCtxContinue(pIoCtx, rcReq);
+ vdIoCtxAddToWaitingList(&pDisk->pIoCtxHaltedHead, pIoCtx);
+ if (ASMAtomicCmpXchgBool(&pDisk->fLocked, true, false))
+ {
+ /* Immediately drop the lock again, it will take care of processing the list. */
+ vdDiskUnlock(pDisk, NULL);
+ }
+}
- vdDiskCritSectLeave(pDisk, NULL);
+static DECLCALLBACK(bool) vdIOIntIoCtxIsSynchronous(void *pvUser, PVDIOCTX pIoCtx)
+{
+ NOREF(pvUser);
+ return !!(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC);
+}
+
+static DECLCALLBACK(bool) vdIOIntIoCtxIsZero(void *pvUser, PVDIOCTX pIoCtx, size_t cbCheck,
+ bool fAdvance)
+{
+ NOREF(pvUser);
+
+ bool fIsZero = RTSgBufIsZero(&pIoCtx->Req.Io.SgBuf, cbCheck);
+ if (fIsZero && fAdvance)
+ RTSgBufAdvance(&pIoCtx->Req.Io.SgBuf, cbCheck);
+
+ return fIsZero;
}
/**
@@ -4700,10 +4719,9 @@ static int vdIOIntCloseLimited(void *pvUser, PVDIOSTORAGE pIoStorage)
{
PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
int rc = pInterfaceIo->pfnClose(NULL, pIoStorage->pStorage);
- AssertRC(rc);
RTMemFree(pIoStorage);
- return VINF_SUCCESS;
+ return rc;
}
static int vdIOIntDeleteLimited(void *pvUser, const char *pcszFilename)
@@ -4748,26 +4766,85 @@ static int vdIOIntSetSizeLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
return pInterfaceIo->pfnSetSize(NULL, pIoStorage->pStorage, cbSize);
}
-static int vdIOIntWriteSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
- uint64_t uOffset, const void *pvBuf,
- size_t cbWrite, size_t *pcbWritten)
+static int vdIOIntWriteUserLimited(void *pvUser, PVDIOSTORAGE pStorage,
+ uint64_t uOffset, PVDIOCTX pIoCtx,
+ size_t cbWrite,
+ PFNVDXFERCOMPLETED pfnComplete,
+ void *pvCompleteUser)
+{
+ NOREF(pvUser);
+ NOREF(pStorage);
+ NOREF(uOffset);
+ NOREF(pIoCtx);
+ NOREF(cbWrite);
+ NOREF(pfnComplete);
+ NOREF(pvCompleteUser);
+ AssertMsgFailedReturn(("This needs to be implemented when called\n"), VERR_NOT_IMPLEMENTED);
+}
+
+static int vdIOIntReadUserLimited(void *pvUser, PVDIOSTORAGE pStorage,
+ uint64_t uOffset, PVDIOCTX pIoCtx,
+ size_t cbRead)
+{
+ NOREF(pvUser);
+ NOREF(pStorage);
+ NOREF(uOffset);
+ NOREF(pIoCtx);
+ NOREF(cbRead);
+ AssertMsgFailedReturn(("This needs to be implemented when called\n"), VERR_NOT_IMPLEMENTED);
+}
+
+static int vdIOIntWriteMetaLimited(void *pvUser, PVDIOSTORAGE pStorage,
+ uint64_t uOffset, const void *pvBuffer,
+ size_t cbBuffer, PVDIOCTX pIoCtx,
+ PFNVDXFERCOMPLETED pfnComplete,
+ void *pvCompleteUser)
{
PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
- return pInterfaceIo->pfnWriteSync(NULL, pIoStorage->pStorage, uOffset, pvBuf, cbWrite, pcbWritten);
+
+ AssertMsgReturn(!pIoCtx && !pfnComplete && !pvCompleteUser,
+ ("Async I/O not implemented for the limited interface"),
+ VERR_NOT_SUPPORTED);
+
+ return pInterfaceIo->pfnWriteSync(NULL, pStorage->pStorage, uOffset, pvBuffer, cbBuffer, NULL);
}
-static int vdIOIntReadSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
- uint64_t uOffset, void *pvBuf, size_t cbRead,
- size_t *pcbRead)
+static int vdIOIntReadMetaLimited(void *pvUser, PVDIOSTORAGE pStorage,
+ uint64_t uOffset, void *pvBuffer,
+ size_t cbBuffer, PVDIOCTX pIoCtx,
+ PPVDMETAXFER ppMetaXfer,
+ PFNVDXFERCOMPLETED pfnComplete,
+ void *pvCompleteUser)
{
PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
- return pInterfaceIo->pfnReadSync(NULL, pIoStorage->pStorage, uOffset, pvBuf, cbRead, pcbRead);
+
+ AssertMsgReturn(!pIoCtx && !ppMetaXfer && !pfnComplete && !pvCompleteUser,
+ ("Async I/O not implemented for the limited interface"),
+ VERR_NOT_SUPPORTED);
+
+ return pInterfaceIo->pfnReadSync(NULL, pStorage->pStorage, uOffset, pvBuffer, cbBuffer, NULL);
+}
+
+static int vdIOIntMetaXferReleaseLimited(void *pvUser, PVDMETAXFER pMetaXfer)
+{
+ /* This is a NOP in this case. */
+ NOREF(pvUser);
+ NOREF(pMetaXfer);
+ return VINF_SUCCESS;
}
-static int vdIOIntFlushSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage)
+static int vdIOIntFlushLimited(void *pvUser, PVDIOSTORAGE pStorage,
+ PVDIOCTX pIoCtx,
+ PFNVDXFERCOMPLETED pfnComplete,
+ void *pvCompleteUser)
{
PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
- return pInterfaceIo->pfnFlushSync(NULL, pIoStorage->pStorage);
+
+ AssertMsgReturn(!pIoCtx && !pfnComplete && !pvCompleteUser,
+ ("Async I/O not implemented for the limited interface"),
+ VERR_NOT_SUPPORTED);
+
+ return pInterfaceIo->pfnFlushSync(NULL, pStorage->pStorage);
}
/**
@@ -4886,20 +4963,30 @@ static void vdIfIoIntCallbacksSetup(PVDINTERFACEIOINT pIfIoInt)
pIfIoInt->pfnGetModificationTime = vdIOIntGetModificationTime;
pIfIoInt->pfnGetSize = vdIOIntGetSize;
pIfIoInt->pfnSetSize = vdIOIntSetSize;
- pIfIoInt->pfnReadSync = vdIOIntReadSync;
- pIfIoInt->pfnWriteSync = vdIOIntWriteSync;
- pIfIoInt->pfnFlushSync = vdIOIntFlushSync;
- pIfIoInt->pfnReadUserAsync = vdIOIntReadUserAsync;
- pIfIoInt->pfnWriteUserAsync = vdIOIntWriteUserAsync;
- pIfIoInt->pfnReadMetaAsync = vdIOIntReadMetaAsync;
- pIfIoInt->pfnWriteMetaAsync = vdIOIntWriteMetaAsync;
+ pIfIoInt->pfnReadUser = vdIOIntReadUser;
+ pIfIoInt->pfnWriteUser = vdIOIntWriteUser;
+ pIfIoInt->pfnReadMeta = vdIOIntReadMeta;
+ pIfIoInt->pfnWriteMeta = vdIOIntWriteMeta;
pIfIoInt->pfnMetaXferRelease = vdIOIntMetaXferRelease;
- pIfIoInt->pfnFlushAsync = vdIOIntFlushAsync;
+ pIfIoInt->pfnFlush = vdIOIntFlush;
pIfIoInt->pfnIoCtxCopyFrom = vdIOIntIoCtxCopyFrom;
pIfIoInt->pfnIoCtxCopyTo = vdIOIntIoCtxCopyTo;
pIfIoInt->pfnIoCtxSet = vdIOIntIoCtxSet;
pIfIoInt->pfnIoCtxSegArrayCreate = vdIOIntIoCtxSegArrayCreate;
pIfIoInt->pfnIoCtxCompleted = vdIOIntIoCtxCompleted;
+ pIfIoInt->pfnIoCtxIsSynchronous = vdIOIntIoCtxIsSynchronous;
+ pIfIoInt->pfnIoCtxIsZero = vdIOIntIoCtxIsZero;
+}
+
+/**
+ * Internally used completion handler for synchronous I/O contexts.
+ */
+static DECLCALLBACK(void) vdIoCtxSyncComplete(void *pvUser1, void *pvUser2, int rcReq)
+{
+ PVBOXHDD pDisk = (PVBOXHDD)pvUser1;
+
+ pDisk->rcSync = rcReq;
+ RTSemEventSignal(pDisk->hEventSemSyncIo);
}
/**
@@ -5077,54 +5164,43 @@ VBOXDDU_DECL(int) VDCreate(PVDINTERFACE pVDIfsDisk, VDTYPE enmType, PVBOXHDD *pp
pDisk = (PVBOXHDD)RTMemAllocZ(sizeof(VBOXHDD));
if (pDisk)
{
- pDisk->u32Signature = VBOXHDDDISK_SIGNATURE;
- pDisk->enmType = enmType;
- pDisk->cImages = 0;
- pDisk->pBase = NULL;
- pDisk->pLast = NULL;
- pDisk->cbSize = 0;
+ pDisk->u32Signature = VBOXHDDDISK_SIGNATURE;
+ pDisk->enmType = enmType;
+ pDisk->cImages = 0;
+ pDisk->pBase = NULL;
+ pDisk->pLast = NULL;
+ pDisk->cbSize = 0;
pDisk->PCHSGeometry.cCylinders = 0;
pDisk->PCHSGeometry.cHeads = 0;
pDisk->PCHSGeometry.cSectors = 0;
pDisk->LCHSGeometry.cCylinders = 0;
pDisk->LCHSGeometry.cHeads = 0;
pDisk->LCHSGeometry.cSectors = 0;
- pDisk->pVDIfsDisk = pVDIfsDisk;
- pDisk->pInterfaceError = NULL;
- pDisk->pInterfaceThreadSync = NULL;
- pDisk->fLocked = false;
- pDisk->pIoCtxLockOwner = NULL;
- pDisk->pIoCtxHead = NULL;
- RTListInit(&pDisk->ListWriteLocked);
+ pDisk->pVDIfsDisk = pVDIfsDisk;
+ pDisk->pInterfaceError = NULL;
+ pDisk->pInterfaceThreadSync = NULL;
+ pDisk->pIoCtxLockOwner = NULL;
+ pDisk->pIoCtxHead = NULL;
+ pDisk->fLocked = false;
+ pDisk->hEventSemSyncIo = NIL_RTSEMEVENT;
+ pDisk->hMemCacheIoCtx = NIL_RTMEMCACHE;
+ pDisk->hMemCacheIoTask = NIL_RTMEMCACHE;
+
+ rc = RTSemEventCreate(&pDisk->hEventSemSyncIo);
+ if (RT_FAILURE(rc))
+ break;
/* Create the I/O ctx cache */
rc = RTMemCacheCreate(&pDisk->hMemCacheIoCtx, sizeof(VDIOCTX), 0, UINT32_MAX,
NULL, NULL, NULL, 0);
if (RT_FAILURE(rc))
- {
- RTMemFree(pDisk);
break;
- }
/* Create the I/O task cache */
rc = RTMemCacheCreate(&pDisk->hMemCacheIoTask, sizeof(VDIOTASK), 0, UINT32_MAX,
NULL, NULL, NULL, 0);
if (RT_FAILURE(rc))
- {
- RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
- RTMemFree(pDisk);
break;
- }
-
- /* Create critical section. */
- rc = RTCritSectInit(&pDisk->CritSect);
- if (RT_FAILURE(rc))
- {
- RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
- RTMemCacheDestroy(pDisk->hMemCacheIoTask);
- RTMemFree(pDisk);
- break;
- }
pDisk->pInterfaceError = VDIfErrorGet(pVDIfsDisk);
pDisk->pInterfaceThreadSync = VDIfThreadSyncGet(pVDIfsDisk);
@@ -5138,6 +5214,17 @@ VBOXDDU_DECL(int) VDCreate(PVDINTERFACE pVDIfsDisk, VDTYPE enmType, PVBOXHDD *pp
}
} while (0);
+ if ( RT_FAILURE(rc)
+ && pDisk)
+ {
+ if (pDisk->hEventSemSyncIo != NIL_RTSEMEVENT)
+ RTSemEventDestroy(pDisk->hEventSemSyncIo);
+ if (pDisk->hMemCacheIoCtx != NIL_RTMEMCACHE)
+ RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
+ if (pDisk->hMemCacheIoTask != NIL_RTMEMCACHE)
+ RTMemCacheDestroy(pDisk->hMemCacheIoTask);
+ }
+
LogFlowFunc(("returns %Rrc (pDisk=%#p)\n", rc, pDisk));
return rc;
}
@@ -5158,10 +5245,12 @@ VBOXDDU_DECL(int) VDDestroy(PVBOXHDD pDisk)
/* sanity check */
AssertPtrBreak(pDisk);
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+ Assert(!pDisk->fLocked);
+
rc = VDCloseAll(pDisk);
- RTCritSectDelete(&pDisk->CritSect);
RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
RTMemCacheDestroy(pDisk->hMemCacheIoTask);
+ RTSemEventDestroy(pDisk->hEventSemSyncIo);
RTMemFree(pDisk);
} while (0);
LogFlowFunc(("returns %Rrc\n", rc));
@@ -5225,14 +5314,11 @@ VBOXDDU_DECL(int) VDGetFormat(PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
VDIfIoInt.pfnGetModificationTime = vdIOIntGetModificationTimeLimited;
VDIfIoInt.pfnGetSize = vdIOIntGetSizeLimited;
VDIfIoInt.pfnSetSize = vdIOIntSetSizeLimited;
- VDIfIoInt.pfnReadSync = vdIOIntReadSyncLimited;
- VDIfIoInt.pfnWriteSync = vdIOIntWriteSyncLimited;
- VDIfIoInt.pfnFlushSync = vdIOIntFlushSyncLimited;
- VDIfIoInt.pfnReadUserAsync = NULL;
- VDIfIoInt.pfnWriteUserAsync = NULL;
- VDIfIoInt.pfnReadMetaAsync = NULL;
- VDIfIoInt.pfnWriteMetaAsync = NULL;
- VDIfIoInt.pfnFlushAsync = NULL;
+ VDIfIoInt.pfnReadUser = vdIOIntReadUserLimited;
+ VDIfIoInt.pfnWriteUser = vdIOIntWriteUserLimited;
+ VDIfIoInt.pfnReadMeta = vdIOIntReadMetaLimited;
+ VDIfIoInt.pfnWriteMeta = vdIOIntWriteMetaLimited;
+ VDIfIoInt.pfnFlush = vdIOIntFlushLimited;
rc = VDInterfaceAdd(&VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
pInterfaceIo, sizeof(VDINTERFACEIOINT), &pVDIfsImage);
AssertRC(rc);
@@ -5358,6 +5444,10 @@ VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszBackend,
AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
("uOpenFlags=%#x\n", uOpenFlags),
rc = VERR_INVALID_PARAMETER);
+ AssertMsgBreakStmt( !(uOpenFlags & VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)
+ || (uOpenFlags & VD_OPEN_FLAGS_READONLY),
+ ("uOpenFlags=%#x\n", uOpenFlags),
+ rc = VERR_INVALID_PARAMETER);
/*
* Destroy the current discard state first which might still have pending blocks
@@ -5450,6 +5540,36 @@ VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszBackend,
pImage->pVDIfsImage,
pDisk->enmType,
&pImage->pBackendData);
+ /*
+ * If the image is corrupted and there is a repair method try to repair it
+ * first if it was openend in read-write mode and open again afterwards.
+ */
+ if ( RT_UNLIKELY(rc == VERR_VD_IMAGE_CORRUPTED)
+ && !(uOpenFlags & VD_OPEN_FLAGS_READONLY)
+ && pImage->Backend->pfnRepair)
+ {
+ rc = pImage->Backend->pfnRepair(pszFilename, pDisk->pVDIfsDisk, pImage->pVDIfsImage, 0 /* fFlags */);
+ if (RT_SUCCESS(rc))
+ rc = pImage->Backend->pfnOpen(pImage->pszFilename,
+ uOpenFlags & ~(VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS),
+ pDisk->pVDIfsDisk,
+ pImage->pVDIfsImage,
+ pDisk->enmType,
+ &pImage->pBackendData);
+ else
+ {
+ rc = vdError(pDisk, rc, RT_SRC_POS,
+ N_("VD: error %Rrc repairing corrupted image file '%s'"), rc, pszFilename);
+ break;
+ }
+ }
+ else if (RT_UNLIKELY(rc == VERR_VD_IMAGE_CORRUPTED))
+ {
+ rc = vdError(pDisk, rc, RT_SRC_POS,
+ N_("VD: Image file '%s' is corrupted and can't be opened"), pszFilename);
+ break;
+ }
+
/* If the open in read-write mode failed, retry in read-only mode. */
if (RT_FAILURE(rc))
{
@@ -6619,7 +6739,11 @@ VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
{
- uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
+ /*
+ * Clear skip consistency checks because the image is made writable now and
+ * skipping consistency checks is only possible for readonly images.
+ */
+ uOpenFlags &= ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS);
rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
uOpenFlags);
if (RT_FAILURE(rc))
@@ -6652,6 +6776,15 @@ VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
do
{
size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
+ RTSGSEG SegmentBuf;
+ RTSGBUF SgBuf;
+ VDIOCTX IoCtx;
+
+ SegmentBuf.pvSeg = pvBuf;
+ SegmentBuf.cbSeg = VD_MERGE_BUFFER_SIZE;
+ RTSgBufInit(&SgBuf, &SegmentBuf, 1);
+ vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_READ, 0, 0, NULL,
+ &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
/* Need to hold the write lock during a read-write operation. */
rc2 = vdThreadStartWrite(pDisk);
@@ -6659,8 +6792,8 @@ VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
fLockWrite = true;
rc = pImageTo->Backend->pfnRead(pImageTo->pBackendData,
- uOffset, pvBuf, cbThisRead,
- &cbThisRead);
+ uOffset, cbThisRead,
+ &IoCtx, &cbThisRead);
if (rc == VERR_VD_BLOCK_FREE)
{
/* Search for image with allocated block. Do not attempt to
@@ -6672,9 +6805,8 @@ VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
pCurrImage = pCurrImage->pPrev)
{
rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
- uOffset, pvBuf,
- cbThisRead,
- &cbThisRead);
+ uOffset, cbThisRead,
+ &IoCtx, &cbThisRead);
}
if (rc != VERR_VD_BLOCK_FREE)
@@ -6684,7 +6816,7 @@ VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
/* Updating the cache is required because this might be a live merge. */
rc = vdWriteHelperEx(pDisk, pImageTo, pImageFrom->pPrev,
uOffset, pvBuf, cbThisRead,
- true /* fUpdateCache */, 0);
+ VDIOCTX_FLAGS_READ_UPDATE_CACHE, 0);
if (RT_FAILURE(rc))
break;
}
@@ -6773,8 +6905,18 @@ VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
do
{
size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
+ RTSGSEG SegmentBuf;
+ RTSGBUF SgBuf;
+ VDIOCTX IoCtx;
+
rc = VERR_VD_BLOCK_FREE;
+ SegmentBuf.pvSeg = pvBuf;
+ SegmentBuf.cbSeg = VD_MERGE_BUFFER_SIZE;
+ RTSgBufInit(&SgBuf, &SegmentBuf, 1);
+ vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_READ, 0, 0, NULL,
+ &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
+
/* Need to hold the write lock during a read-write operation. */
rc2 = vdThreadStartWrite(pDisk);
AssertRC(rc2);
@@ -6789,8 +6931,8 @@ VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
pCurrImage = pCurrImage->pPrev)
{
rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
- uOffset, pvBuf,
- cbThisRead, &cbThisRead);
+ uOffset, cbThisRead,
+ &IoCtx, &cbThisRead);
}
if (rc != VERR_VD_BLOCK_FREE)
@@ -6798,7 +6940,7 @@ VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
if (RT_FAILURE(rc))
break;
rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
- cbThisRead, true /* fUpdateCache */);
+ cbThisRead, VDIOCTX_FLAGS_READ_UPDATE_CACHE);
if (RT_FAILURE(rc))
break;
}
@@ -6853,15 +6995,27 @@ VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
AssertRC(rc2);
fLockWrite = true;
- /* Update parent UUID so that image chain is consistent. */
+ /* Update parent UUID so that image chain is consistent.
+ * The two attempts work around the problem that some backends
+ * (e.g. iSCSI) do not support UUIDs, so we exploit the fact that
+ * so far there can only be one such image in the chain. */
+ /** @todo needs a better long-term solution, passing the UUID
+ * knowledge from the caller or some such */
RTUUID Uuid;
PVDIMAGE pImageChild = NULL;
if (nImageFrom < nImageTo)
{
if (pImageFrom->pPrev)
{
+ /* plan A: ask the parent itself for its UUID */
rc = pImageFrom->pPrev->Backend->pfnGetUuid(pImageFrom->pPrev->pBackendData,
&Uuid);
+ if (RT_FAILURE(rc))
+ {
+ /* plan B: ask the child of the parent for parent UUID */
+ rc = pImageFrom->Backend->pfnGetParentUuid(pImageFrom->pBackendData,
+ &Uuid);
+ }
AssertRC(rc);
}
else
@@ -6875,8 +7029,15 @@ VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
/* Update the parent uuid of the child of the last merged image. */
if (pImageFrom->pNext)
{
+ /* plan A: ask the parent itself for its UUID */
rc = pImageTo->Backend->pfnGetUuid(pImageTo->pBackendData,
&Uuid);
+ if (RT_FAILURE(rc))
+ {
+ /* plan B: ask the child of the parent for parent UUID */
+ rc = pImageTo->pNext->Backend->pfnGetParentUuid(pImageTo->pNext->pBackendData,
+ &Uuid);
+ }
AssertRC(rc);
rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext->pBackendData,
@@ -7486,11 +7647,11 @@ VBOXDDU_DECL(int) VDResize(PVBOXHDD pDisk, uint64_t cbSize,
AssertRC(rc2);
fLockRead = true;
- /* Not supported if the disk has child images attached. */
- AssertMsgBreakStmt(pDisk->cImages == 1, ("cImages=%u\n", pDisk->cImages),
+ /* Must have at least one image in the chain, will resize last. */
+ AssertMsgBreakStmt(pDisk->cImages >= 1, ("cImages=%u\n", pDisk->cImages),
rc = VERR_NOT_SUPPORTED);
- PVDIMAGE pImage = pDisk->pBase;
+ PVDIMAGE pImage = pDisk->pLast;
/* If there is no compact callback for not file based backends then
* the backend doesn't need compaction. No need to make much fuss about
@@ -7571,6 +7732,8 @@ VBOXDDU_DECL(int) VDResize(PVBOXHDD pDisk, uint64_t cbSize,
{
if (pIfProgress && pIfProgress->pfnProgress)
pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
+
+ pDisk->cbSize = cbSize;
}
LogFlowFunc(("returns %Rrc\n", rc));
@@ -7839,14 +8002,23 @@ VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
AssertRC(rc2);
fLockRead = true;
- AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
- ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
- uOffset, cbRead, pDisk->cbSize),
- rc = VERR_INVALID_PARAMETER);
-
PVDIMAGE pImage = pDisk->pLast;
AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
+ if (uOffset + cbRead > pDisk->cbSize)
+ {
+ /* Floppy images might be smaller than the standard expected by
+ the floppy controller code. So, we won't fail here. */
+ AssertMsgBreakStmt(pDisk->enmType == VDTYPE_FLOPPY,
+ ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
+ uOffset, cbRead, pDisk->cbSize),
+ rc = VERR_EOF);
+ memset(pvBuf, 0xf6, cbRead); /* f6h = format.com filler byte */
+ if (uOffset >= pDisk->cbSize)
+ break;
+ cbRead = pDisk->cbSize - uOffset;
+ }
+
rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead,
true /* fUpdateCache */);
} while (0);
@@ -7909,7 +8081,7 @@ VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
vdSetModifiedFlag(pDisk);
rc = vdWriteHelper(pDisk, pImage, uOffset, pvBuf, cbWrite,
- true /* fUpdateCache */);
+ VDIOCTX_FLAGS_READ_UPDATE_CACHE);
if (RT_FAILURE(rc))
break;
@@ -7923,7 +8095,7 @@ VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
* as this write is covered by the previous one. */
if (RT_UNLIKELY(pDisk->pImageRelay))
rc = vdWriteHelper(pDisk, pDisk->pImageRelay, uOffset,
- pvBuf, cbWrite, false /* fUpdateCache */);
+ pvBuf, cbWrite, VDIOCTX_FLAGS_DEFAULT);
} while (0);
if (RT_UNLIKELY(fLockWrite))
@@ -7963,12 +8135,19 @@ VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
PVDIMAGE pImage = pDisk->pLast;
AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
- vdResetModifiedFlag(pDisk);
- rc = pImage->Backend->pfnFlush(pImage->pBackendData);
+ PVDIOCTX pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_FLUSH, 0,
+ 0, pDisk->pLast, NULL,
+ vdIoCtxSyncComplete, pDisk, NULL,
+ NULL, vdFlushHelperAsync,
+ VDIOCTX_FLAGS_SYNC);
- if ( RT_SUCCESS(rc)
- && pDisk->pCache)
- rc = pDisk->pCache->Backend->pfnFlush(pDisk->pCache->pBackendData);
+ if (!pIoCtx)
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+
+ rc = vdIoCtxProcessSync(pIoCtx);
} while (0);
if (RT_UNLIKELY(fLockWrite))
@@ -8060,6 +8239,46 @@ VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
}
/**
+ * Get sector size of an image in HDD container.
+ *
+ * @return Virtual disk sector size in bytes.
+ * @return 0 if image with specified number was not opened.
+ * @param pDisk Pointer to HDD container.
+ * @param nImage Image number, counts from 0. 0 is always base image of container.
+ */
+VBOXDDU_DECL(uint32_t) VDGetSectorSize(PVBOXHDD pDisk, unsigned nImage)
+{
+ uint64_t cbSector;
+ int rc2;
+ bool fLockRead = false;
+
+ LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
+ do
+ {
+ /* sanity check */
+ AssertPtrBreakStmt(pDisk, cbSector = 0);
+ AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+ rc2 = vdThreadStartRead(pDisk);
+ AssertRC(rc2);
+ fLockRead = true;
+
+ PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
+ AssertPtrBreakStmt(pImage, cbSector = 0);
+ cbSector = pImage->Backend->pfnGetSectorSize(pImage->pBackendData);
+ } while (0);
+
+ if (RT_UNLIKELY(fLockRead))
+ {
+ rc2 = vdThreadFinishRead(pDisk);
+ AssertRC(rc2);
+ }
+
+ LogFlowFunc(("returns %u\n", cbSector));
+ return cbSector;
+}
+
+/**
* Get total capacity of an image in HDD container.
*
* @returns Virtual disk size in bytes.
@@ -9282,8 +9501,17 @@ VBOXDDU_DECL(int) VDDiscardRanges(PVBOXHDD pDisk, PCRTRANGE paRanges, unsigned c
("Discarding not supported\n"),
rc = VERR_NOT_SUPPORTED);
- vdSetModifiedFlag(pDisk);
- rc = vdDiscardHelper(pDisk, paRanges, cRanges);
+ PVDIOCTX pIoCtx = vdIoCtxDiscardAlloc(pDisk, paRanges, cRanges,
+ vdIoCtxSyncComplete, pDisk, NULL, NULL,
+ vdDiscardHelperAsync,
+ VDIOCTX_FLAGS_SYNC);
+ if (!pIoCtx)
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+
+ rc = vdIoCtxProcessSync(pIoCtx);
} while (0);
if (RT_UNLIKELY(fLockWrite))
@@ -9337,18 +9565,15 @@ VBOXDDU_DECL(int) VDAsyncRead(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRead,
pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_READ, uOffset,
cbRead, pDisk->pLast, pcSgBuf,
pfnComplete, pvUser1, pvUser2,
- NULL, vdReadHelperAsync);
+ NULL, vdReadHelperAsync,
+ VDIOCTX_FLAGS_ZERO_FREE_BLOCKS);
if (!pIoCtx)
{
rc = VERR_NO_MEMORY;
break;
}
-#if 0
rc = vdIoCtxProcessTryLockDefer(pIoCtx);
-#else
- rc = vdIoCtxProcess(pIoCtx);
-#endif
if (rc == VINF_VD_ASYNC_IO_FINISHED)
{
if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
@@ -9412,18 +9637,15 @@ VBOXDDU_DECL(int) VDAsyncWrite(PVBOXHDD pDisk, uint64_t uOffset, size_t cbWrite,
pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_WRITE, uOffset,
cbWrite, pDisk->pLast, pcSgBuf,
pfnComplete, pvUser1, pvUser2,
- NULL, vdWriteHelperAsync);
+ NULL, vdWriteHelperAsync,
+ VDIOCTX_FLAGS_DEFAULT);
if (!pIoCtx)
{
rc = VERR_NO_MEMORY;
break;
}
-#if 0
rc = vdIoCtxProcessTryLockDefer(pIoCtx);
-#else
- rc = vdIoCtxProcess(pIoCtx);
-#endif
if (rc == VINF_VD_ASYNC_IO_FINISHED)
{
if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
@@ -9472,18 +9694,15 @@ VBOXDDU_DECL(int) VDAsyncFlush(PVBOXHDD pDisk, PFNVDASYNCTRANSFERCOMPLETE pfnCom
pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_FLUSH, 0,
0, pDisk->pLast, NULL,
pfnComplete, pvUser1, pvUser2,
- NULL, vdFlushHelperAsync);
+ NULL, vdFlushHelperAsync,
+ VDIOCTX_FLAGS_DEFAULT);
if (!pIoCtx)
{
rc = VERR_NO_MEMORY;
break;
}
-#if 0
rc = vdIoCtxProcessTryLockDefer(pIoCtx);
-#else
- rc = vdIoCtxProcess(pIoCtx);
-#endif
if (rc == VINF_VD_ASYNC_IO_FINISHED)
{
if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
@@ -9531,18 +9750,15 @@ VBOXDDU_DECL(int) VDAsyncDiscardRanges(PVBOXHDD pDisk, PCRTRANGE paRanges, unsig
pIoCtx = vdIoCtxDiscardAlloc(pDisk, paRanges, cRanges,
pfnComplete, pvUser1, pvUser2, NULL,
- vdDiscardHelperAsync);
+ vdDiscardHelperAsync,
+ VDIOCTX_FLAGS_DEFAULT);
if (!pIoCtx)
{
rc = VERR_NO_MEMORY;
break;
}
-#if 0
rc = vdIoCtxProcessTryLockDefer(pIoCtx);
-#else
- rc = vdIoCtxProcess(pIoCtx);
-#endif
if (rc == VINF_VD_ASYNC_IO_FINISHED)
{
if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
@@ -9608,14 +9824,11 @@ VBOXDDU_DECL(int) VDRepair(PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
VDIfIoInt.pfnGetModificationTime = vdIOIntGetModificationTimeLimited;
VDIfIoInt.pfnGetSize = vdIOIntGetSizeLimited;
VDIfIoInt.pfnSetSize = vdIOIntSetSizeLimited;
- VDIfIoInt.pfnReadSync = vdIOIntReadSyncLimited;
- VDIfIoInt.pfnWriteSync = vdIOIntWriteSyncLimited;
- VDIfIoInt.pfnFlushSync = vdIOIntFlushSyncLimited;
- VDIfIoInt.pfnReadUserAsync = NULL;
- VDIfIoInt.pfnWriteUserAsync = NULL;
- VDIfIoInt.pfnReadMetaAsync = NULL;
- VDIfIoInt.pfnWriteMetaAsync = NULL;
- VDIfIoInt.pfnFlushAsync = NULL;
+ VDIfIoInt.pfnReadUser = vdIOIntReadUserLimited;
+ VDIfIoInt.pfnWriteUser = vdIOIntWriteUserLimited;
+ VDIfIoInt.pfnReadMeta = vdIOIntReadMetaLimited;
+ VDIfIoInt.pfnWriteMeta = vdIOIntWriteMetaLimited;
+ VDIfIoInt.pfnFlush = vdIOIntFlushLimited;
rc = VDInterfaceAdd(&VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
pInterfaceIo, sizeof(VDINTERFACEIOINT), &pVDIfsImage);
AssertRC(rc);
diff --git a/src/VBox/Storage/VDI.cpp b/src/VBox/Storage/VDI.cpp
index f20f1874..7cd47c24 100644
--- a/src/VBox/Storage/VDI.cpp
+++ b/src/VBox/Storage/VDI.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2011 Oracle Corporation
+ * Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -102,6 +102,7 @@ static void vdiConvGeometryEndianess(VDIECONV enmConv, PVDIDISKGEOMETRY pDiskGeo
static void vdiConvHeaderEndianessV0(VDIECONV enmConv, PVDIHEADER0 pHdrConv,
PVDIHEADER0 pHdr)
{
+ memmove(pHdrConv->szComment, pHdr->szComment, sizeof(pHdr->szComment));
pHdrConv->u32Type = SET_ENDIAN_U32(enmConv, pHdr->u32Type);
pHdrConv->fFlags = SET_ENDIAN_U32(enmConv, pHdr->fFlags);
vdiConvGeometryEndianess(enmConv, &pHdrConv->LegacyGeometry, &pHdr->LegacyGeometry);
@@ -124,6 +125,7 @@ static void vdiConvHeaderEndianessV0(VDIECONV enmConv, PVDIHEADER0 pHdrConv,
static void vdiConvHeaderEndianessV1(VDIECONV enmConv, PVDIHEADER1 pHdrConv,
PVDIHEADER1 pHdr)
{
+ memmove(pHdrConv->szComment, pHdr->szComment, sizeof(pHdr->szComment));
pHdrConv->cbHeader = SET_ENDIAN_U32(enmConv, pHdr->cbHeader);
pHdrConv->u32Type = SET_ENDIAN_U32(enmConv, pHdr->u32Type);
pHdrConv->fFlags = SET_ENDIAN_U32(enmConv, pHdr->fFlags);
@@ -152,6 +154,7 @@ static void vdiConvHeaderEndianessV1(VDIECONV enmConv, PVDIHEADER1 pHdrConv,
static void vdiConvHeaderEndianessV1p(VDIECONV enmConv, PVDIHEADER1PLUS pHdrConv,
PVDIHEADER1PLUS pHdr)
{
+ memmove(pHdrConv->szComment, pHdr->szComment, sizeof(pHdr->szComment));
pHdrConv->cbHeader = SET_ENDIAN_U32(enmConv, pHdr->cbHeader);
pHdrConv->u32Type = SET_ENDIAN_U32(enmConv, pHdr->u32Type);
pHdrConv->fFlags = SET_ENDIAN_U32(enmConv, pHdr->fFlags);
@@ -221,7 +224,7 @@ static int vdiFreeImage(PVDIIMAGEDESC pImage, bool fDelete)
if (!fDelete)
vdiFlushImage(pImage);
- vdIfIoIntFileClose(pImage->pIfIo, pImage->pStorage);
+ rc = vdIfIoIntFileClose(pImage->pIfIo, pImage->pStorage);
pImage->pStorage = NULL;
}
@@ -325,7 +328,8 @@ static unsigned vdiTranslateVDI2ImageFlags(VDIIMAGETYPE enmType)
*/
static void vdiInitHeader(PVDIHEADER pHeader, uint32_t uImageFlags,
const char *pszComment, uint64_t cbDisk,
- uint32_t cbBlock, uint32_t cbBlockExtra)
+ uint32_t cbBlock, uint32_t cbBlockExtra,
+ uint32_t cbDataAlign)
{
pHeader->uVersion = VDI_IMAGE_VERSION;
pHeader->u.v1plus.cbHeader = sizeof(VDIHEADER1PLUS);
@@ -359,8 +363,8 @@ static void vdiInitHeader(PVDIHEADER pHeader, uint32_t uImageFlags,
pHeader->u.v1plus.cBlocksAllocated = 0;
/* Init offsets. */
- pHeader->u.v1plus.offBlocks = RT_ALIGN_32(sizeof(VDIPREHEADER) + sizeof(VDIHEADER1PLUS), VDI_DATA_ALIGN);
- pHeader->u.v1plus.offData = RT_ALIGN_32(pHeader->u.v1plus.offBlocks + (pHeader->u.v1plus.cBlocks * sizeof(VDIIMAGEBLOCKPOINTER)), VDI_DATA_ALIGN);
+ pHeader->u.v1plus.offBlocks = RT_ALIGN_32(sizeof(VDIPREHEADER) + sizeof(VDIHEADER1PLUS), cbDataAlign);
+ pHeader->u.v1plus.offData = RT_ALIGN_32(pHeader->u.v1plus.offBlocks + (pHeader->u.v1plus.cBlocks * sizeof(VDIIMAGEBLOCKPOINTER)), cbDataAlign);
/* Init uuids. */
RTUuidCreate(&pHeader->u.v1plus.uuidCreate);
@@ -520,12 +524,13 @@ static int vdiCreateImage(PVDIIMAGEDESC pImage, uint64_t cbSize,
PCVDGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
unsigned uOpenFlags, PFNVDPROGRESS pfnProgress,
void *pvUser, unsigned uPercentStart,
- unsigned uPercentSpan)
+ unsigned uPercentSpan, PVDINTERFACECONFIG pIfCfg)
{
int rc;
uint64_t cbTotal;
uint64_t cbFill;
uint64_t uOff;
+ uint32_t cbDataAlign = VDI_DATA_ALIGN;
AssertPtr(pPCHSGeometry);
AssertPtr(pLCHSGeometry);
@@ -543,8 +548,20 @@ static int vdiCreateImage(PVDIIMAGEDESC pImage, uint64_t cbSize,
goto out;
}
+ if (pIfCfg)
+ {
+ rc = VDCFGQueryU32Def(pIfCfg, "DataAlignment", &cbDataAlign, VDI_DATA_ALIGN);
+ if (RT_FAILURE(rc))
+ {
+ rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
+ N_("VDI: Getting data alignment for '%s' failed (%Rrc)"), pImage->pszFilename);
+ goto out;
+ }
+ }
+
vdiInitPreHeader(&pImage->PreHeader);
- vdiInitHeader(&pImage->Header, uImageFlags, pszComment, cbSize, VDI_IMAGE_DEFAULT_BLOCK_SIZE, 0);
+ vdiInitHeader(&pImage->Header, uImageFlags, pszComment, cbSize, VDI_IMAGE_DEFAULT_BLOCK_SIZE, 0,
+ cbDataAlign);
/* Save PCHS geometry. Not much work, and makes the flow of information
* quite a bit clearer - relying on the higher level isn't obvious. */
pImage->PCHSGeometry = *pPCHSGeometry;
@@ -639,7 +656,7 @@ static int vdiCreateImage(PVDIIMAGEDESC pImage, uint64_t cbSize,
VDIPREHEADER PreHeader;
vdiConvPreHeaderEndianess(VDIECONV_H2F, &PreHeader, &pImage->PreHeader);
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, 0,
- &PreHeader, sizeof(PreHeader), NULL);
+ &PreHeader, sizeof(PreHeader));
if (RT_FAILURE(rc))
{
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VDI: writing pre-header failed for '%s'"),
@@ -651,7 +668,7 @@ static int vdiCreateImage(PVDIIMAGEDESC pImage, uint64_t cbSize,
VDIHEADER1PLUS Hdr;
vdiConvHeaderEndianessV1p(VDIECONV_H2F, &Hdr, &pImage->Header.u.v1plus);
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, sizeof(pImage->PreHeader),
- &Hdr, sizeof(Hdr), NULL);
+ &Hdr, sizeof(Hdr));
if (RT_FAILURE(rc))
{
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VDI: writing header failed for '%s'"),
@@ -661,8 +678,7 @@ static int vdiCreateImage(PVDIIMAGEDESC pImage, uint64_t cbSize,
vdiConvBlocksEndianess(VDIECONV_H2F, pImage->paBlocks, getImageBlocks(&pImage->Header));
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, pImage->offStartBlocks, pImage->paBlocks,
- getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER),
- NULL);
+ getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER));
vdiConvBlocksEndianess(VDIECONV_F2H, pImage->paBlocks, getImageBlocks(&pImage->Header));
if (RT_FAILURE(rc))
{
@@ -697,7 +713,7 @@ static int vdiCreateImage(PVDIIMAGEDESC pImage, uint64_t cbSize,
unsigned cbChunk = (unsigned)RT_MIN(cbFill, cbBuf);
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, pImage->offStartData + uOff,
- pvBuf, cbChunk, NULL);
+ pvBuf, cbChunk);
if (RT_FAILURE(rc))
{
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VDI: writing block failed for '%s'"), pImage->pszFilename);
@@ -766,7 +782,7 @@ static int vdiOpenImage(PVDIIMAGEDESC pImage, unsigned uOpenFlags)
/* Read pre-header. */
VDIPREHEADER PreHeader;
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, 0,
- &PreHeader, sizeof(PreHeader), NULL);
+ &PreHeader, sizeof(PreHeader));
if (RT_FAILURE(rc))
{
vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VDI: error reading pre-header in '%s'"), pImage->pszFilename);
@@ -787,8 +803,7 @@ static int vdiOpenImage(PVDIIMAGEDESC pImage, unsigned uOpenFlags)
{
case 0:
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, sizeof(pImage->PreHeader),
- &pImage->Header.u.v0, sizeof(pImage->Header.u.v0),
- NULL);
+ &pImage->Header.u.v0, sizeof(pImage->Header.u.v0));
if (RT_FAILURE(rc))
{
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VDI: error reading v0 header in '%s'"), pImage->pszFilename);
@@ -798,8 +813,7 @@ static int vdiOpenImage(PVDIIMAGEDESC pImage, unsigned uOpenFlags)
break;
case 1:
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, sizeof(pImage->PreHeader),
- &pImage->Header.u.v1, sizeof(pImage->Header.u.v1),
- NULL);
+ &pImage->Header.u.v1, sizeof(pImage->Header.u.v1));
if (RT_FAILURE(rc))
{
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VDI: error reading v1 header in '%s'"), pImage->pszFilename);
@@ -825,7 +839,7 @@ static int vdiOpenImage(PVDIIMAGEDESC pImage, unsigned uOpenFlags)
/* Read the actual VDI 1.1+ header completely. */
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, sizeof(pImage->PreHeader),
&pImage->Header.u.v1plus,
- sizeof(pImage->Header.u.v1plus), NULL);
+ sizeof(pImage->Header.u.v1plus));
if (RT_FAILURE(rc))
{
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VDI: error reading v1.1+ header in '%s'"), pImage->pszFilename);
@@ -859,8 +873,7 @@ static int vdiOpenImage(PVDIIMAGEDESC pImage, unsigned uOpenFlags)
/* Read blocks array. */
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, pImage->offStartBlocks, pImage->paBlocks,
- getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER),
- NULL);
+ getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER));
if (RT_FAILURE(rc))
{
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VDI: Error reading the block table in '%s'"), pImage->pszFilename);
@@ -930,7 +943,7 @@ static int vdiUpdateHeader(PVDIIMAGEDESC pImage)
VDIHEADER0 Hdr;
vdiConvHeaderEndianessV0(VDIECONV_H2F, &Hdr, &pImage->Header.u.v0);
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, sizeof(VDIPREHEADER),
- &Hdr, sizeof(Hdr), NULL);
+ &Hdr, sizeof(Hdr));
break;
}
case 1:
@@ -939,14 +952,14 @@ static int vdiUpdateHeader(PVDIIMAGEDESC pImage)
VDIHEADER1 Hdr;
vdiConvHeaderEndianessV1(VDIECONV_H2F, &Hdr, &pImage->Header.u.v1);
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, sizeof(VDIPREHEADER),
- &Hdr, sizeof(Hdr), NULL);
+ &Hdr, sizeof(Hdr));
}
else
{
VDIHEADER1PLUS Hdr;
vdiConvHeaderEndianessV1p(VDIECONV_H2F, &Hdr, &pImage->Header.u.v1plus);
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, sizeof(VDIPREHEADER),
- &Hdr, sizeof(Hdr), NULL);
+ &Hdr, sizeof(Hdr));
}
break;
default:
@@ -969,9 +982,9 @@ static int vdiUpdateHeaderAsync(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx)
{
VDIHEADER0 Hdr;
vdiConvHeaderEndianessV0(VDIECONV_H2F, &Hdr, &pImage->Header.u.v0);
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
- sizeof(VDIPREHEADER), &Hdr, sizeof(Hdr),
- pIoCtx, NULL, NULL);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
+ sizeof(VDIPREHEADER), &Hdr, sizeof(Hdr),
+ pIoCtx, NULL, NULL);
break;
}
case 1:
@@ -979,17 +992,17 @@ static int vdiUpdateHeaderAsync(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx)
{
VDIHEADER1 Hdr;
vdiConvHeaderEndianessV1(VDIECONV_H2F, &Hdr, &pImage->Header.u.v1);
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
- sizeof(VDIPREHEADER), &Hdr, sizeof(Hdr),
- pIoCtx, NULL, NULL);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
+ sizeof(VDIPREHEADER), &Hdr, sizeof(Hdr),
+ pIoCtx, NULL, NULL);
}
else
{
VDIHEADER1PLUS Hdr;
vdiConvHeaderEndianessV1p(VDIECONV_H2F, &Hdr, &pImage->Header.u.v1plus);
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
- sizeof(VDIPREHEADER), &Hdr, sizeof(Hdr),
- pIoCtx, NULL, NULL);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
+ sizeof(VDIPREHEADER), &Hdr, sizeof(Hdr),
+ pIoCtx, NULL, NULL);
}
break;
default:
@@ -1014,8 +1027,7 @@ static int vdiUpdateBlockInfo(PVDIIMAGEDESC pImage, unsigned uBlock)
VDIIMAGEBLOCKPOINTER ptrBlock = RT_H2LE_U32(pImage->paBlocks[uBlock]);
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
pImage->offStartBlocks + uBlock * sizeof(VDIIMAGEBLOCKPOINTER),
- &ptrBlock, sizeof(VDIIMAGEBLOCKPOINTER),
- NULL);
+ &ptrBlock, sizeof(VDIIMAGEBLOCKPOINTER));
AssertMsgRC(rc, ("vdiUpdateBlockInfo failed to update block=%u, filename=\"%s\", rc=%Rrc\n",
uBlock, pImage->pszFilename, rc));
}
@@ -1038,10 +1050,10 @@ static int vdiUpdateBlockInfoAsync(PVDIIMAGEDESC pImage, unsigned uBlock,
{
/* write only one block pointer. */
VDIIMAGEBLOCKPOINTER ptrBlock = RT_H2LE_U32(pImage->paBlocks[uBlock]);
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
- pImage->offStartBlocks + uBlock * sizeof(VDIIMAGEBLOCKPOINTER),
- &ptrBlock, sizeof(VDIIMAGEBLOCKPOINTER),
- pIoCtx, NULL, NULL);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
+ pImage->offStartBlocks + uBlock * sizeof(VDIIMAGEBLOCKPOINTER),
+ &ptrBlock, sizeof(VDIIMAGEBLOCKPOINTER),
+ pIoCtx, NULL, NULL);
AssertMsg(RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS,
("vdiUpdateBlockInfo failed to update block=%u, filename=\"%s\", rc=%Rrc\n",
uBlock, pImage->pszFilename, rc));
@@ -1052,7 +1064,7 @@ static int vdiUpdateBlockInfoAsync(PVDIIMAGEDESC pImage, unsigned uBlock,
/**
* Internal: Flush the image file to disk - async version.
*/
-static int vdiFlushImageAsync(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx)
+static int vdiFlushImageIoCtx(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx)
{
int rc = VINF_SUCCESS;
@@ -1063,7 +1075,7 @@ static int vdiFlushImageAsync(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx)
AssertMsg(RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS,
("vdiUpdateHeaderAsync() failed, filename=\"%s\", rc=%Rrc\n",
pImage->pszFilename, rc));
- rc = vdIfIoIntFileFlushAsync(pImage->pIfIo, pImage->pStorage, pIoCtx, NULL, NULL);
+ rc = vdIfIoIntFileFlush(pImage->pIfIo, pImage->pStorage, pIoCtx, NULL, NULL);
AssertMsg(RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS,
("Flushing data to disk failed rc=%Rrc\n", rc));
}
@@ -1072,81 +1084,6 @@ static int vdiFlushImageAsync(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx)
}
/**
- * Internal: Discard a whole block from the image filling the created hole with
- * data from another block.
- *
- * @returns VBox status code.
- * @param pImage VDI image instance data.
- * @param uBlock The block to discard.
- * @param pvBlock Memory to use for the I/O.
- */
-static int vdiDiscardBlock(PVDIIMAGEDESC pImage, unsigned uBlock, void *pvBlock)
-{
- int rc = VINF_SUCCESS;
- uint64_t cbImage;
- unsigned idxLastBlock = getImageBlocksAllocated(&pImage->Header) - 1;
- unsigned uBlockLast = pImage->paBlocksRev[idxLastBlock];
- VDIIMAGEBLOCKPOINTER ptrBlockDiscard = pImage->paBlocks[uBlock];
-
- LogFlowFunc(("pImage=%#p uBlock=%u pvBlock=%#p\n",
- pImage, uBlock, pvBlock));
-
- pImage->paBlocksRev[idxLastBlock] = VDI_IMAGE_BLOCK_FREE;
-
- do
- {
- /*
- * The block is empty, remove it.
- * Read the last block of the image first.
- */
- if (idxLastBlock != ptrBlockDiscard)
- {
- uint64_t u64Offset;
-
- LogFlowFunc(("Moving block [%u]=%u into [%u]=%u\n",
- uBlockLast, idxLastBlock, uBlock, pImage->paBlocks[uBlock]));
-
- u64Offset = (uint64_t)idxLastBlock * pImage->cbTotalBlockData + pImage->offStartData;
- rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, u64Offset,
- pvBlock, pImage->cbTotalBlockData, NULL);
- if (RT_FAILURE(rc))
- break;
-
- /* Write to the now unallocated block. */
- u64Offset = (uint64_t)ptrBlockDiscard * pImage->cbTotalBlockData + pImage->offStartData;
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, u64Offset,
- pvBlock, pImage->cbTotalBlockData, NULL);
- if (RT_FAILURE(rc))
- break;
-
- /* Update block and reverse block tables. */
- pImage->paBlocks[uBlockLast] = ptrBlockDiscard;
- pImage->paBlocksRev[ptrBlockDiscard] = uBlockLast;
- rc = vdiUpdateBlockInfo(pImage, uBlockLast);
- if (RT_FAILURE(rc))
- break;
- }
- else
- LogFlowFunc(("Discard last block [%u]=%u\n", uBlock, pImage->paBlocks[uBlock]));
-
- pImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
-
- /* Update the block pointers. */
- setImageBlocksAllocated(&pImage->Header, idxLastBlock);
- rc = vdiUpdateBlockInfo(pImage, uBlock);
- if (RT_FAILURE(rc))
- break;
-
- pImage->cbImage -= pImage->cbTotalBlockData;
- LogFlowFunc(("Set new size %llu\n", pImage->cbImage));
- rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage, pImage->cbImage);
- } while (0);
-
- LogFlowFunc(("returns rc=%Rrc\n", rc));
- return rc;
-}
-
-/**
* Completion callback for meta/userdata reads or writes.
*
* @return VBox status code.
@@ -1169,9 +1106,9 @@ static DECLCALLBACK(int) vdiDiscardBlockAsyncUpdate(void *pBackendData, PVDIOCTX
{
PVDMETAXFER pMetaXfer;
uint64_t u64Offset = (uint64_t)pDiscardAsync->idxLastBlock * pImage->cbTotalBlockData + pImage->offStartData;
- rc = vdIfIoIntFileReadMetaAsync(pImage->pIfIo, pImage->pStorage, u64Offset,
- pDiscardAsync->pvBlock, pImage->cbTotalBlockData, pIoCtx,
- &pMetaXfer, vdiDiscardBlockAsyncUpdate, pDiscardAsync);
+ rc = vdIfIoIntFileReadMeta(pImage->pIfIo, pImage->pStorage, u64Offset,
+ pDiscardAsync->pvBlock, pImage->cbTotalBlockData, pIoCtx,
+ &pMetaXfer, vdiDiscardBlockAsyncUpdate, pDiscardAsync);
if (RT_FAILURE(rc))
break;
@@ -1183,9 +1120,9 @@ static DECLCALLBACK(int) vdiDiscardBlockAsyncUpdate(void *pBackendData, PVDIOCTX
{
/* Block read complete. Write to the new location (discarded block). */
uint64_t u64Offset = (uint64_t)pDiscardAsync->ptrBlockDiscard * pImage->cbTotalBlockData + pImage->offStartData;
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage, u64Offset,
- pDiscardAsync->pvBlock, pImage->cbTotalBlockData, pIoCtx,
- vdiDiscardBlockAsyncUpdate, pDiscardAsync);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage, u64Offset,
+ pDiscardAsync->pvBlock, pImage->cbTotalBlockData, pIoCtx,
+ vdiDiscardBlockAsyncUpdate, pDiscardAsync);
pDiscardAsync->enmState = VDIBLOCKDISCARDSTATE_UPDATE_METADATA;
if (RT_FAILURE(rc))
@@ -1194,7 +1131,6 @@ static DECLCALLBACK(int) vdiDiscardBlockAsyncUpdate(void *pBackendData, PVDIOCTX
case VDIBLOCKDISCARDSTATE_UPDATE_METADATA:
{
int rc2;
- uint64_t cbImage;
/* Block write complete. Update metadata. */
pImage->paBlocksRev[pDiscardAsync->idxLastBlock] = VDI_IMAGE_BLOCK_FREE;
@@ -1303,8 +1239,9 @@ static int vdiDiscardBlockAsync(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx,
*/
static void *vdiAllocationBitmapCreate(void *pvData, size_t cbData)
{
- unsigned cSectors = cbData / 512;
- unsigned uSectorCur = 0;
+ Assert(cbData <= UINT32_MAX / 8);
+ uint32_t cSectors = (uint32_t)(cbData / 512);
+ uint32_t uSectorCur = 0;
void *pbmAllocationBitmap = NULL;
Assert(!(cbData % 512));
@@ -1316,7 +1253,7 @@ static void *vdiAllocationBitmapCreate(void *pvData, size_t cbData)
while (uSectorCur < cSectors)
{
- int idxSet = ASMBitFirstSet((uint8_t *)pvData + uSectorCur * 512, cbData * 8);
+ int idxSet = ASMBitFirstSet((uint8_t *)pvData + uSectorCur * 512, (uint32_t)cbData * 8);
if (idxSet != -1)
{
@@ -1343,7 +1280,7 @@ static void *vdiAllocationBitmapCreate(void *pvData, size_t cbData)
* @param pvUser Opaque user data passed during a read/write request.
* @param rcReq Status code for the completed request.
*/
-static DECLCALLBACK(int) vdiAsyncBlockAllocUpdate(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
+static DECLCALLBACK(int) vdiBlockAllocUpdate(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
{
int rc = VINF_SUCCESS;
PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
@@ -1475,6 +1412,8 @@ static int vdiCreate(const char *pszFilename, uint64_t cbSize,
pvUser = pIfProgress->Core.pvUser;
}
+ PVDINTERFACECONFIG pIfCfg = VDIfConfigGet(pVDIfsOperation);
+
/* Check the image flags. */
if ((uImageFlags & ~VD_VDI_IMAGE_FLAGS_MASK) != 0)
{
@@ -1491,7 +1430,6 @@ static int vdiCreate(const char *pszFilename, uint64_t cbSize,
/* Check size. Maximum 4PB-3M. No tricks with adjusting the 1M block size
* so far, which would extend the size. */
- cbSize = RT_ALIGN_64(cbSize, _1M);
if ( !cbSize
|| cbSize >= _1P * 4 - _1M * 3)
{
@@ -1524,7 +1462,8 @@ static int vdiCreate(const char *pszFilename, uint64_t cbSize,
rc = vdiCreateImage(pImage, cbSize, uImageFlags, pszComment,
pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags,
- pfnProgress, pvUser, uPercentStart, uPercentSpan);
+ pfnProgress, pvUser, uPercentStart, uPercentSpan,
+ pIfCfg);
if (RT_SUCCESS(rc))
{
/* So far the image is opened in read/write mode. Make sure the
@@ -1567,27 +1506,29 @@ static int vdiRename(void *pBackendData, const char *pszFilename)
}
/* Close the image. */
- vdiFreeImage(pImage, false);
-
- /* Rename the file. */
- rc = vdIfIoIntFileMove(pImage->pIfIo, pImage->pszFilename, pszFilename, 0);
- if (RT_FAILURE(rc))
+ rc = vdiFreeImage(pImage, false);
+ if (RT_SUCCESS(rc))
{
- /* The move failed, try to reopen the original image. */
- int rc2 = vdiOpenImage(pImage, pImage->uOpenFlags);
- if (RT_FAILURE(rc2))
- rc = rc2;
+ /* Rename the file. */
+ rc = vdIfIoIntFileMove(pImage->pIfIo, pImage->pszFilename, pszFilename, 0);
+ if (RT_FAILURE(rc))
+ {
+ /* The move failed, try to reopen the original image. */
+ int rc2 = vdiOpenImage(pImage, pImage->uOpenFlags);
+ if (RT_FAILURE(rc2))
+ rc = rc2;
- goto out;
- }
+ goto out;
+ }
- /* Update pImage with the new information. */
- pImage->pszFilename = pszFilename;
+ /* Update pImage with the new information. */
+ pImage->pszFilename = pszFilename;
- /* Open the new image. */
- rc = vdiOpenImage(pImage, pImage->uOpenFlags);
- if (RT_FAILURE(rc))
- goto out;
+ /* Open the new image. */
+ rc = vdiOpenImage(pImage, pImage->uOpenFlags);
+ if (RT_FAILURE(rc))
+ goto out;
+ }
out:
LogFlowFunc(("returns %Rrc\n", rc));
@@ -1608,11 +1549,11 @@ static int vdiClose(void *pBackendData, bool fDelete)
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnRead */
-static int vdiRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
- size_t cbToRead, size_t *pcbActuallyRead)
+static int vdiRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
+ PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
+ LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
+ pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
unsigned uBlock;
unsigned offRead;
@@ -1623,7 +1564,7 @@ static int vdiRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
Assert(!(cbToRead % 512));
if ( uOffset + cbToRead > getImageDiskSize(&pImage->Header)
- || !VALID_PTR(pvBuf)
+ || !VALID_PTR(pIoCtx)
|| !cbToRead)
{
rc = VERR_INVALID_PARAMETER;
@@ -1642,7 +1583,11 @@ static int vdiRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
rc = VERR_VD_BLOCK_FREE;
else if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
{
- memset(pvBuf, 0, cbToRead);
+ size_t cbSet;
+
+ cbSet = vdIfIoIntIoCtxSet(pImage->pIfIo, pIoCtx, 0, cbToRead);
+ Assert(cbSet == cbToRead);
+
rc = VINF_SUCCESS;
}
else
@@ -1652,13 +1597,13 @@ static int vdiRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
+ (pImage->offStartData + pImage->offStartBlockData + offRead);
if (u64Offset + cbToRead <= pImage->cbImage)
- rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, u64Offset,
- pvBuf, cbToRead, NULL);
+ rc = vdIfIoIntFileReadUser(pImage->pIfIo, pImage->pStorage, u64Offset,
+ pIoCtx, cbToRead);
else
{
LogRel(("VDI: Out of range access (%llu) in image %s, image size %llu\n",
u64Offset, pImage->pszFilename, pImage->cbImage));
- memset(pvBuf, 0, cbToRead);
+ vdIfIoIntIoCtxSet(pImage->pIfIo, pIoCtx, 0, cbToRead);
rc = VERR_VD_READ_OUT_OF_RANGE;
}
}
@@ -1671,12 +1616,12 @@ out:
return rc;
}
-/**@copydoc VBOXHDDBACKEND::pfnWrite */
-static int vdiWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
- size_t cbToWrite, size_t *pcbWriteProcess,
- size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
+static int vdiWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
+ PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
+ size_t *pcbPostRead, unsigned fWrite)
{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
+ LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
+ pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
unsigned uBlock;
unsigned offWrite;
@@ -1692,7 +1637,7 @@ static int vdiWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
goto out;
}
- if (!VALID_PTR(pvBuf) || !cbToWrite)
+ if (!VALID_PTR(pIoCtx) || !cbToWrite)
{
rc = VERR_INVALID_PARAMETER;
goto out;
@@ -1723,9 +1668,7 @@ static int vdiWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
* either a zero block or a block which hasn't been used so far
* (which also means that it's a zero block. Don't need to write
* anything to this block if the data consists of just zeroes. */
- Assert(!(cbToWrite % 4));
- Assert(cbToWrite * 8 <= UINT32_MAX);
- if (ASMBitFirstSet((volatile void *)pvBuf, (uint32_t)cbToWrite * 8) == -1)
+ if (vdIfIoIntIoCtxIsZero(pImage->pIfIo, pIoCtx, cbToWrite, true))
{
pImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
*pcbPreRead = 0;
@@ -1740,27 +1683,35 @@ static int vdiWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
/* Full block write to previously unallocated block.
* Allocate block and write data. */
Assert(!offWrite);
+ PVDIASYNCBLOCKALLOC pBlockAlloc = (PVDIASYNCBLOCKALLOC)RTMemAllocZ(sizeof(VDIASYNCBLOCKALLOC));
+ if (!pBlockAlloc)
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+
unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
uint64_t u64Offset = (uint64_t)cBlocksAllocated * pImage->cbTotalBlockData
+ (pImage->offStartData + pImage->offStartBlockData);
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
- u64Offset, pvBuf, cbToWrite, NULL);
- if (RT_FAILURE(rc))
- goto out;
- pImage->paBlocks[uBlock] = cBlocksAllocated;
-
- if (pImage->paBlocksRev)
- pImage->paBlocksRev[cBlocksAllocated] = uBlock;
- setImageBlocksAllocated(&pImage->Header, cBlocksAllocated + 1);
-
- rc = vdiUpdateBlockInfo(pImage, uBlock);
- if (RT_FAILURE(rc))
- goto out;
+ pBlockAlloc->cBlocksAllocated = cBlocksAllocated;
+ pBlockAlloc->uBlock = uBlock;
- pImage->cbImage += cbToWrite;
*pcbPreRead = 0;
*pcbPostRead = 0;
+
+ rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
+ u64Offset, pIoCtx, cbToWrite,
+ vdiBlockAllocUpdate, pBlockAlloc);
+ if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+ break;
+ else if (RT_FAILURE(rc))
+ {
+ RTMemFree(pBlockAlloc);
+ break;
+ }
+
+ rc = vdiBlockAllocUpdate(pImage, pIoCtx, pBlockAlloc, rc);
}
else
{
@@ -1776,8 +1727,8 @@ static int vdiWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
/* Block present in image file, write relevant data. */
uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData
+ (pImage->offStartData + pImage->offStartBlockData + offWrite);
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, u64Offset,
- pvBuf, cbToWrite, NULL);
+ rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
+ u64Offset, pIoCtx, cbToWrite, NULL, NULL);
}
} while (0);
@@ -1789,8 +1740,7 @@ out:
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnFlush */
-static int vdiFlush(void *pBackendData)
+static int vdiFlush(void *pBackendData, PVDIOCTX pIoCtx)
{
LogFlowFunc(("pBackendData=%#p\n", pBackendData));
PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
@@ -1798,7 +1748,7 @@ static int vdiFlush(void *pBackendData)
Assert(pImage);
- vdiFlushImage(pImage);
+ rc = vdiFlushImageIoCtx(pImage, pIoCtx);
LogFlowFunc(("returns %Rrc\n", rc));
return rc;
}
@@ -1821,6 +1771,22 @@ static unsigned vdiGetVersion(void *pBackendData)
return uVersion;
}
+/** @copydoc VBOXHDDBACKEND::pfnGetSectorSize */
+static uint32_t vdiGetSectorSize(void *pBackendData)
+{
+ LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+ PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+ uint64_t cbSector = 0;
+
+ AssertPtr(pImage);
+
+ if (pImage && pImage->pStorage)
+ cbSector = 512;
+
+ LogFlowFunc(("returns %zu\n", cbSector));
+ return cbSector;
+}
+
/** @copydoc VBOXHDDBACKEND::pfnGetSize */
static uint64_t vdiGetSize(void *pBackendData)
{
@@ -2036,7 +2002,10 @@ static int vdiSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
const char *pszFilename;
/* Image must be opened and the new flags must be valid. */
- if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_DISCARD)))
+ if (!pImage || (uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
+ | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE
+ | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_DISCARD
+ | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
{
rc = VERR_INVALID_PARAMETER;
goto out;
@@ -2416,213 +2385,6 @@ static void vdiDump(void *pBackendData)
}
}
-static int vdiAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
- PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
-{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
- pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
- PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
- unsigned uBlock;
- unsigned offRead;
- int rc;
-
- AssertPtr(pImage);
- Assert(!(uOffset % 512));
- Assert(!(cbToRead % 512));
-
- if ( uOffset + cbToRead > getImageDiskSize(&pImage->Header)
- || !VALID_PTR(pIoCtx)
- || !cbToRead)
- {
- rc = VERR_INVALID_PARAMETER;
- goto out;
- }
-
- /* Calculate starting block number and offset inside it. */
- uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
- offRead = (unsigned)uOffset & pImage->uBlockMask;
-
- /* Clip read range to at most the rest of the block. */
- cbToRead = RT_MIN(cbToRead, getImageBlockSize(&pImage->Header) - offRead);
- Assert(!(cbToRead % 512));
-
- if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
- rc = VERR_VD_BLOCK_FREE;
- else if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
- {
- size_t cbSet;
-
- cbSet = vdIfIoIntIoCtxSet(pImage->pIfIo, pIoCtx, 0, cbToRead);
- Assert(cbSet == cbToRead);
-
- rc = VINF_SUCCESS;
- }
- else
- {
- /* Block present in image file, read relevant data. */
- uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData
- + (pImage->offStartData + pImage->offStartBlockData + offRead);
-
- if (u64Offset + cbToRead <= pImage->cbImage)
- rc = vdIfIoIntFileReadUserAsync(pImage->pIfIo, pImage->pStorage, u64Offset,
- pIoCtx, cbToRead);
- else
- {
- LogRel(("VDI: Out of range access (%llu) in image %s, image size %llu\n",
- u64Offset, pImage->pszFilename, pImage->cbImage));
- vdIfIoIntIoCtxSet(pImage->pIfIo, pIoCtx, 0, cbToRead);
- rc = VERR_VD_READ_OUT_OF_RANGE;
- }
- }
-
- if (pcbActuallyRead)
- *pcbActuallyRead = cbToRead;
-
-out:
- LogFlowFunc(("returns %Rrc\n", rc));
- return rc;
-}
-
-static int vdiAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
- PVDIOCTX pIoCtx,
- size_t *pcbWriteProcess, size_t *pcbPreRead,
- size_t *pcbPostRead, unsigned fWrite)
-{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
- pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
- PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
- unsigned uBlock;
- unsigned offWrite;
- int rc = VINF_SUCCESS;
-
- AssertPtr(pImage);
- Assert(!(uOffset % 512));
- Assert(!(cbToWrite % 512));
-
- if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
- {
- rc = VERR_VD_IMAGE_READ_ONLY;
- goto out;
- }
-
- if (!VALID_PTR(pIoCtx) || !cbToWrite)
- {
- rc = VERR_INVALID_PARAMETER;
- goto out;
- }
-
- /* No size check here, will do that later. For dynamic images which are
- * not multiples of the block size in length, this would prevent writing to
- * the last block. */
-
- /* Calculate starting block number and offset inside it. */
- uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
- offWrite = (unsigned)uOffset & pImage->uBlockMask;
-
- /* Clip write range to at most the rest of the block. */
- cbToWrite = RT_MIN(cbToWrite, getImageBlockSize(&pImage->Header) - offWrite);
- Assert(!(cbToWrite % 512));
-
- do
- {
- if (!IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
- {
- /* Block is either free or zero. */
- if ( !(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_ZEROES)
- && ( pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO
- || cbToWrite == getImageBlockSize(&pImage->Header)))
- {
-#if 0 /** @todo Provide interface to check an I/O context for a specific value */
- /* If the destination block is unallocated at this point, it's
- * either a zero block or a block which hasn't been used so far
- * (which also means that it's a zero block. Don't need to write
- * anything to this block if the data consists of just zeroes. */
- Assert(!(cbToWrite % 4));
- Assert(cbToWrite * 8 <= UINT32_MAX);
- if (ASMBitFirstSet((volatile void *)pvBuf, (uint32_t)cbToWrite * 8) == -1)
- {
- pImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
- break;
- }
-#endif
- }
-
- if ( cbToWrite == getImageBlockSize(&pImage->Header)
- && !(fWrite & VD_WRITE_NO_ALLOC))
- {
- /* Full block write to previously unallocated block.
- * Allocate block and write data. */
- Assert(!offWrite);
- PVDIASYNCBLOCKALLOC pBlockAlloc = (PVDIASYNCBLOCKALLOC)RTMemAllocZ(sizeof(VDIASYNCBLOCKALLOC));
- if (!pBlockAlloc)
- {
- rc = VERR_NO_MEMORY;
- break;
- }
-
- unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
- uint64_t u64Offset = (uint64_t)cBlocksAllocated * pImage->cbTotalBlockData
- + (pImage->offStartData + pImage->offStartBlockData);
-
- pBlockAlloc->cBlocksAllocated = cBlocksAllocated;
- pBlockAlloc->uBlock = uBlock;
-
- *pcbPreRead = 0;
- *pcbPostRead = 0;
-
- rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pImage->pStorage,
- u64Offset, pIoCtx, cbToWrite,
- vdiAsyncBlockAllocUpdate, pBlockAlloc);
- if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
- break;
- else if (RT_FAILURE(rc))
- {
- RTMemFree(pBlockAlloc);
- break;
- }
-
- rc = vdiAsyncBlockAllocUpdate(pImage, pIoCtx, pBlockAlloc, rc);
- }
- else
- {
- /* Trying to do a partial write to an unallocated block. Don't do
- * anything except letting the upper layer know what to do. */
- *pcbPreRead = offWrite % getImageBlockSize(&pImage->Header);
- *pcbPostRead = getImageBlockSize(&pImage->Header) - cbToWrite - *pcbPreRead;
- rc = VERR_VD_BLOCK_FREE;
- }
- }
- else
- {
- /* Block present in image file, write relevant data. */
- uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData
- + (pImage->offStartData + pImage->offStartBlockData + offWrite);
- rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pImage->pStorage,
- u64Offset, pIoCtx, cbToWrite, NULL, NULL);
- }
- } while (0);
-
- if (pcbWriteProcess)
- *pcbWriteProcess = cbToWrite;
-
-out:
- LogFlowFunc(("returns %Rrc\n", rc));
- return rc;
-}
-
-static int vdiAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
-{
- LogFlowFunc(("pBackendData=%#p\n", pBackendData));
- PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
- int rc = VINF_SUCCESS;
-
- Assert(pImage);
-
- rc = vdiFlushImageAsync(pImage, pIoCtx);
- LogFlowFunc(("returns %Rrc\n", rc));
- return rc;
-}
-
/** @copydoc VBOXHDDBACKEND::pfnCompact */
static int vdiCompact(void *pBackendData, unsigned uPercentStart,
unsigned uPercentSpan, PVDINTERFACE pVDIfsDisk,
@@ -2731,7 +2493,7 @@ static int vdiCompact(void *pBackendData, unsigned uPercentStart,
/* Block present in image file, read relevant data. */
uint64_t u64Offset = (uint64_t)ptrBlock * pImage->cbTotalBlockData
+ (pImage->offStartData + pImage->offStartBlockData);
- rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, u64Offset, pvTmp, cbBlock, NULL);
+ rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, u64Offset, pvTmp, cbBlock);
if (RT_FAILURE(rc))
break;
@@ -2816,11 +2578,11 @@ static int vdiCompact(void *pBackendData, unsigned uPercentStart,
uint64_t u64Offset = (uint64_t)uBlockUsedPos * pImage->cbTotalBlockData
+ (pImage->offStartData + pImage->offStartBlockData);
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, u64Offset,
- pvTmp, cbBlock, NULL);
+ pvTmp, cbBlock);
u64Offset = (uint64_t)i * pImage->cbTotalBlockData
+ (pImage->offStartData + pImage->offStartBlockData);
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, u64Offset,
- pvTmp, cbBlock, NULL);
+ pvTmp, cbBlock);
pImage->paBlocks[uBlockData] = i;
setImageBlocksAllocated(&pImage->Header, cBlocksAllocated - cBlocksMoved);
rc = vdiUpdateBlockInfo(pImage, uBlockData);
@@ -2931,8 +2693,6 @@ static int vdiResize(void *pBackendData, uint64_t cbSize,
void *pvBuf = NULL, *pvZero = NULL;
do
{
- VDIIMAGEBLOCKPOINTER uBlock = 0;
-
/* Allocate data buffer. */
pvBuf = RTMemAllocZ(pImage->cbTotalBlockData);
if (!pvBuf)
@@ -2954,12 +2714,12 @@ static int vdiResize(void *pBackendData, uint64_t cbSize,
/* Search the index in the block table. */
for (unsigned idxBlock = 0; idxBlock < cBlocksOld; idxBlock++)
{
- if (pImage->paBlocks[idxBlock] == uBlock)
+ if (!pImage->paBlocks[idxBlock])
{
/* Read data and append to the end of the image. */
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
offStartDataNew, pvBuf,
- pImage->cbTotalBlockData, NULL);
+ pImage->cbTotalBlockData);
if (RT_FAILURE(rc))
break;
@@ -2970,14 +2730,14 @@ static int vdiResize(void *pBackendData, uint64_t cbSize,
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
offBlockAppend, pvBuf,
- pImage->cbTotalBlockData, NULL);
+ pImage->cbTotalBlockData);
if (RT_FAILURE(rc))
break;
/* Zero out the old block area. */
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
offStartDataNew, pvZero,
- pImage->cbTotalBlockData, NULL);
+ pImage->cbTotalBlockData);
if (RT_FAILURE(rc))
break;
@@ -3006,7 +2766,6 @@ static int vdiResize(void *pBackendData, uint64_t cbSize,
if (RT_FAILURE(rc))
break;
- uBlock++;
offStartDataNew += pImage->cbTotalBlockData;
}
} while (0);
@@ -3045,7 +2804,7 @@ static int vdiResize(void *pBackendData, uint64_t cbSize,
/* Write the block array before updating the rest. */
vdiConvBlocksEndianess(VDIECONV_H2F, pImage->paBlocks, cBlocksNew);
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, pImage->offStartBlocks,
- pImage->paBlocks, cbBlockspaceNew, NULL);
+ pImage->paBlocks, cbBlockspaceNew);
vdiConvBlocksEndianess(VDIECONV_F2H, pImage->paBlocks, cBlocksNew);
if (RT_SUCCESS(rc))
@@ -3055,6 +2814,7 @@ static int vdiResize(void *pBackendData, uint64_t cbSize,
setImageBlocks(&pImage->Header, cBlocksNew);
/* Update geometry. */
pImage->PCHSGeometry = *pPCHSGeometry;
+ pImage->cbImage = cbSize;
PVDIDISKGEOMETRY pGeometry = getImageLCHSGeometry(&pImage->Header);
if (pGeometry)
@@ -3076,138 +2836,8 @@ static int vdiResize(void *pBackendData, uint64_t cbSize,
return rc;
}
-
/** @copydoc VBOXHDDBACKEND::pfnDiscard */
-static DECLCALLBACK(int) vdiDiscard(void *pBackendData,
- uint64_t uOffset, size_t cbDiscard,
- size_t *pcbPreAllocated,
- size_t *pcbPostAllocated,
- size_t *pcbActuallyDiscarded,
- void **ppbmAllocationBitmap,
- unsigned fDiscard)
-{
- PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
- unsigned uBlock;
- unsigned offDiscard;
- int rc = VINF_SUCCESS;
- void *pvBlock = NULL;
-
- LogFlowFunc(("pBackendData=%#p uOffset=%llu cbDiscard=%zu pcbPreAllocated=%#p pcbPostAllocated=%#p pcbActuallyDiscarded=%#p ppbmAllocationBitmap=%#p fDiscard=%#x\n",
- pBackendData, uOffset, cbDiscard, pcbPreAllocated, pcbPostAllocated, pcbActuallyDiscarded, ppbmAllocationBitmap, fDiscard));
-
- AssertPtr(pImage);
- Assert(!(uOffset % 512));
- Assert(!(cbDiscard % 512));
-
- AssertMsgReturn(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
- ("Image is readonly\n"), VERR_VD_IMAGE_READ_ONLY);
- AssertMsgReturn( uOffset + cbDiscard <= getImageDiskSize(&pImage->Header)
- && cbDiscard,
- ("Invalid parameters uOffset=%llu cbDiscard=%zu\n",
- uOffset, cbDiscard),
- VERR_INVALID_PARAMETER);
-
- do
- {
- AssertMsgBreakStmt(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
- ("Image is opened readonly\n"),
- rc = VERR_VD_IMAGE_READ_ONLY);
-
- AssertMsgBreakStmt(cbDiscard,
- ("cbDiscard=%u\n", cbDiscard),
- rc = VERR_INVALID_PARAMETER);
-
- /* Calculate starting block number and offset inside it. */
- uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
- offDiscard = (unsigned)uOffset & pImage->uBlockMask;
-
- /* Clip range to at most the rest of the block. */
- cbDiscard = RT_MIN(cbDiscard, getImageBlockSize(&pImage->Header) - offDiscard);
- Assert(!(cbDiscard % 512));
-
- if (pcbPreAllocated)
- *pcbPreAllocated = 0;
-
- if (pcbPostAllocated)
- *pcbPostAllocated = 0;
-
- if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
- {
- uint8_t *pbBlockData;
- size_t cbPreAllocated, cbPostAllocated;
-
- cbPreAllocated = offDiscard % getImageBlockSize(&pImage->Header);
- cbPostAllocated = getImageBlockSize(&pImage->Header) - cbDiscard - cbPreAllocated;
-
- /* Read the block data. */
- pvBlock = RTMemAlloc(pImage->cbTotalBlockData);
- if (!pvBlock)
- {
- rc = VERR_NO_MEMORY;
- break;
- }
-
- if (!cbPreAllocated && !cbPostAllocated)
- {
- /*
- * Discarding a whole block, don't check for allocated sectors.
- * It is possible to just remove the whole block which avoids
- * one read and checking the whole block for data.
- */
- rc = vdiDiscardBlock(pImage, uBlock, pvBlock);
- }
- else
- {
- /* Read data. */
- pbBlockData = (uint8_t *)pvBlock + pImage->offStartBlockData;
-
- uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData + pImage->offStartData;
- rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, u64Offset,
- pvBlock, pImage->cbTotalBlockData, NULL);
- if (RT_FAILURE(rc))
- break;
-
- /* Clear data. */
- memset(pbBlockData + offDiscard , 0, cbDiscard);
-
- Assert(!(cbDiscard % 4));
- Assert(cbDiscard * 8 <= UINT32_MAX);
- if (ASMBitFirstSet((volatile void *)pbBlockData, getImageBlockSize(&pImage->Header) * 8) == -1)
- rc = vdiDiscardBlock(pImage, uBlock, pvBlock);
- else if (fDiscard & VD_DISCARD_MARK_UNUSED)
- {
- /* Write changed data to the image. */
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, u64Offset + offDiscard,
- pbBlockData + offDiscard, cbDiscard, NULL);
- }
- else
- {
- /* Block has data, create allocation bitmap. */
- *pcbPreAllocated = cbPreAllocated;
- *pcbPostAllocated = cbPostAllocated;
- *ppbmAllocationBitmap = vdiAllocationBitmapCreate(pbBlockData, getImageBlockSize(&pImage->Header));
- if (RT_UNLIKELY(!*ppbmAllocationBitmap))
- rc = VERR_NO_MEMORY;
- else
- rc = VERR_VD_DISCARD_ALIGNMENT_NOT_MET;
- }
- } /* if: no complete block discarded */
- } /* if: Block is allocated. */
- /* else: nothing to do. */
- } while (0);
-
- if (pcbActuallyDiscarded)
- *pcbActuallyDiscarded = cbDiscard;
-
- if (pvBlock)
- RTMemFree(pvBlock);
-
- LogFlowFunc(("returns %Rrc\n", rc));
- return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnDiscard */
-static DECLCALLBACK(int) vdiAsyncDiscard(void *pBackendData, PVDIOCTX pIoCtx,
+static DECLCALLBACK(int) vdiDiscard(void *pBackendData, PVDIOCTX pIoCtx,
uint64_t uOffset, size_t cbDiscard,
size_t *pcbPreAllocated,
size_t *pcbPostAllocated,
@@ -3291,9 +2921,9 @@ static DECLCALLBACK(int) vdiAsyncDiscard(void *pBackendData, PVDIOCTX pIoCtx,
memset(pvBlock, 0, cbDiscard);
uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData + pImage->offStartData + offDiscard;
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
- u64Offset, pvBlock, cbDiscard, pIoCtx,
- NULL, NULL);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
+ u64Offset, pvBlock, cbDiscard, pIoCtx,
+ NULL, NULL);
RTMemFree(pvBlock);
}
else
@@ -3306,9 +2936,9 @@ static DECLCALLBACK(int) vdiAsyncDiscard(void *pBackendData, PVDIOCTX pIoCtx,
pbBlockData = (uint8_t *)pvBlock + pImage->offStartBlockData;
uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData + pImage->offStartData;
- rc = vdIfIoIntFileReadMetaAsync(pImage->pIfIo, pImage->pStorage, u64Offset,
- pbBlockData, pImage->cbTotalBlockData,
- pIoCtx, &pMetaXfer, NULL, NULL);
+ rc = vdIfIoIntFileReadMeta(pImage->pIfIo, pImage->pStorage, u64Offset,
+ pbBlockData, pImage->cbTotalBlockData,
+ pIoCtx, &pMetaXfer, NULL, NULL);
if (RT_FAILURE(rc))
{
RTMemFree(pvBlock);
@@ -3394,7 +3024,7 @@ static DECLCALLBACK(int) vdiRepair(const char *pszFilename, PVDINTERFACE pVDIfsD
}
/* Read pre-header. */
- rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0, &PreHdr, sizeof(PreHdr), NULL);
+ rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0, &PreHdr, sizeof(PreHdr));
if (RT_FAILURE(rc))
{
rc = vdIfError(pIfError, rc, RT_SRC_POS, N_("VDI: Error reading pre-header in '%s'"), pszFilename);
@@ -3415,8 +3045,7 @@ static DECLCALLBACK(int) vdiRepair(const char *pszFilename, PVDINTERFACE pVDIfsD
{
case 0:
rc = vdIfIoIntFileReadSync(pIfIo, pStorage, sizeof(PreHdr),
- &Hdr.u.v0, sizeof(Hdr.u.v0),
- NULL);
+ &Hdr.u.v0, sizeof(Hdr.u.v0));
if (RT_FAILURE(rc))
rc = vdIfError(pIfError, rc, RT_SRC_POS, N_("VDI: error reading v0 header in '%s'"),
pszFilename);
@@ -3424,7 +3053,7 @@ static DECLCALLBACK(int) vdiRepair(const char *pszFilename, PVDINTERFACE pVDIfsD
break;
case 1:
rc = vdIfIoIntFileReadSync(pIfIo, pStorage, sizeof(PreHdr),
- &Hdr.u.v1, sizeof(Hdr.u.v1), NULL);
+ &Hdr.u.v1, sizeof(Hdr.u.v1));
if (RT_FAILURE(rc))
{
rc = vdIfError(pIfError, rc, RT_SRC_POS, N_("VDI: error reading v1 header in '%s'"),
@@ -3435,8 +3064,7 @@ static DECLCALLBACK(int) vdiRepair(const char *pszFilename, PVDINTERFACE pVDIfsD
{
/* Read the VDI 1.1+ header completely. */
rc = vdIfIoIntFileReadSync(pIfIo, pStorage, sizeof(PreHdr),
- &Hdr.u.v1plus, sizeof(Hdr.u.v1plus),
- NULL);
+ &Hdr.u.v1plus, sizeof(Hdr.u.v1plus));
if (RT_FAILURE(rc))
rc = vdIfError(pIfError, rc, RT_SRC_POS, N_("VDI: error reading v1.1+ header in '%s'"),
pszFilename);
@@ -3480,8 +3108,7 @@ static DECLCALLBACK(int) vdiRepair(const char *pszFilename, PVDINTERFACE pVDIfsD
/* Read blocks array. */
rc = vdIfIoIntFileReadSync(pIfIo, pStorage, offStartBlocks, paBlocks,
- getImageBlocks(&Hdr) * sizeof(VDIIMAGEBLOCKPOINTER),
- NULL);
+ getImageBlocks(&Hdr) * sizeof(VDIIMAGEBLOCKPOINTER));
if (RT_FAILURE(rc))
{
rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
@@ -3536,8 +3163,7 @@ static DECLCALLBACK(int) vdiRepair(const char *pszFilename, PVDINTERFACE pVDIfsD
vdiConvBlocksEndianess(VDIECONV_H2F, paBlocks, getImageBlocks(&Hdr));
rc = vdIfIoIntFileWriteSync(pIfIo, pStorage, offStartBlocks, paBlocks,
- getImageBlocks(&Hdr) * sizeof(VDIIMAGEBLOCKPOINTER),
- NULL);
+ getImageBlocks(&Hdr) * sizeof(VDIIMAGEBLOCKPOINTER));
if (RT_FAILURE(rc))
{
rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
@@ -3557,7 +3183,11 @@ static DECLCALLBACK(int) vdiRepair(const char *pszFilename, PVDINTERFACE pVDIfsD
RTMemFree(pu32BlockBitmap);
if (pStorage)
- vdIfIoIntFileClose(pIfIo, pStorage);
+ {
+ int rc2 = vdIfIoIntFileClose(pIfIo, pStorage);
+ if (RT_SUCCESS(rc))
+ rc = rc2; /* Propagate error code only if repairing was successful. */
+ }
LogFlowFunc(("returns %Rrc\n", rc));
return rc;
@@ -3594,8 +3224,12 @@ VBOXHDDBACKEND g_VDIBackend =
vdiWrite,
/* pfnFlush */
vdiFlush,
+ /* pfnDiscard */
+ vdiDiscard,
/* pfnGetVersion */
vdiGetVersion,
+ /* pfnGetSectorSize */
+ vdiGetSectorSize,
/* pfnGetSize */
vdiGetSize,
/* pfnGetFileSize */
@@ -3646,12 +3280,6 @@ VBOXHDDBACKEND g_VDIBackend =
NULL,
/* pfnSetParentFilename */
NULL,
- /* pfnAsyncRead */
- vdiAsyncRead,
- /* pfnAsyncWrite */
- vdiAsyncWrite,
- /* pfnAsyncFlush */
- vdiAsyncFlush,
/* pfnComposeLocation */
genericFileComposeLocation,
/* pfnComposeName */
@@ -3660,10 +3288,6 @@ VBOXHDDBACKEND g_VDIBackend =
vdiCompact,
/* pfnResize */
vdiResize,
- /* pfnDiscard */
- vdiDiscard,
- /* pfnAsyncDiscard */
- vdiAsyncDiscard,
/* pfnRepair */
vdiRepair
};
diff --git a/src/VBox/Storage/VDICore.h b/src/VBox/Storage/VDICore.h
index 4931adb1..035e1f4f 100644
--- a/src/VBox/Storage/VDICore.h
+++ b/src/VBox/Storage/VDICore.h
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2011 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -236,9 +236,9 @@ typedef struct VDIHEADER
/**
* File alignment boundary for both the block array and data area. Should be
* at least the size of a physical sector on disk for performance reasons.
- * With the growing market share of disks with 4K sectors this needs to be
- * bumped, and maybe again later. */
-#define VDI_DATA_ALIGN _4K
+ * Bumped to 1MB because SSDs tend to have 8kb per page so we don't have to worry
+ * about proper alignment in the near future again. */
+#define VDI_DATA_ALIGN _1M
/** Block 'pointer'. */
typedef uint32_t VDIIMAGEBLOCKPOINTER;
diff --git a/src/VBox/Storage/VDIfVfs.cpp b/src/VBox/Storage/VDIfVfs.cpp
new file mode 100644
index 00000000..5fbd8a94
--- /dev/null
+++ b/src/VBox/Storage/VDIfVfs.cpp
@@ -0,0 +1,413 @@
+/* $Id: VDIfVfs.cpp $ */
+/** @file
+ * Virtual Disk Image (VDI), I/O interface to IPRT VFS I/O stream glue.
+ */
+
+/*
+ * Copyright (C) 2012-2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <iprt/types.h>
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/err.h>
+#include <iprt/asm.h>
+#include <iprt/string.h>
+#include <iprt/file.h>
+#include <iprt/sg.h>
+#include <iprt/vfslowlevel.h>
+#include <iprt/poll.h>
+#include <VBox/vd.h>
+#include <VBox/vd-ifs-internal.h>
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+
+/**
+ * The internal data of an VD I/O to VFS file or I/O stream wrapper.
+ */
+typedef struct VDIFVFSIOSFILE
+{
+ /** The VD I/O interface we prefer wrap.
+ * Can be NULL, in which case pVDIfsIoInt must be valid. */
+ PVDINTERFACEIO pVDIfsIo;
+ /** The VD I/O interface we alternatively can wrap.
+ Can be NULL, in which case pVDIfsIo must be valid. */
+ PVDINTERFACEIOINT pVDIfsIoInt;
+ /** User pointer to pass to the VD I/O interface methods. */
+ PVDIOSTORAGE pStorage;
+ /** The current stream position. */
+ RTFOFF offCurPos;
+} VDIFVFSIOSFILE;
+/** Pointer to a the internal data of a DVM volume file. */
+typedef VDIFVFSIOSFILE *PVDIFVFSIOSFILE;
+
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) vdIfVfsIos_Close(void *pvThis)
+{
+ /* We don't close anything. */
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) vdIfVfsIos_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ NOREF(pvThis);
+ NOREF(pObjInfo);
+ NOREF(enmAddAttr);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
+ */
+static DECLCALLBACK(int) vdIfVfsIos_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
+{
+ PVDIFVFSIOSFILE pThis = (PVDIFVFSIOSFILE)pvThis;
+ Assert(pSgBuf->cSegs == 1); NOREF(fBlocking);
+ Assert(off >= -1);
+
+ /*
+ * This may end up being a little more complicated, esp. wrt VERR_EOF.
+ */
+ if (off == -1)
+ off = pThis->offCurPos;
+ int rc;
+ if (pThis->pVDIfsIo)
+ rc = vdIfIoFileReadSync(pThis->pVDIfsIo, pThis->pStorage, off, pSgBuf[0].pvSegCur, pSgBuf->paSegs[0].cbSeg, pcbRead);
+ else
+ {
+ rc = vdIfIoIntFileReadSync(pThis->pVDIfsIoInt, (PVDIOSTORAGE)pThis->pStorage, off, pSgBuf[0].pvSegCur, pSgBuf->paSegs[0].cbSeg);
+ if (pcbRead)
+ *pcbRead = RT_SUCCESS(rc) ? pSgBuf->paSegs[0].cbSeg : 0;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbAdvance = pcbRead ? *pcbRead : pSgBuf->paSegs[0].cbSeg;
+ pThis->offCurPos = off + cbAdvance;
+ if (pcbRead && !cbAdvance)
+ rc = VINF_EOF;
+ }
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
+ */
+static DECLCALLBACK(int) vdIfVfsIos_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
+{
+ PVDIFVFSIOSFILE pThis = (PVDIFVFSIOSFILE)pvThis;
+ Assert(pSgBuf->cSegs == 1); NOREF(fBlocking);
+ Assert(off >= -1);
+
+ /*
+ * This may end up being a little more complicated, esp. wrt VERR_EOF.
+ */
+ if (off == -1)
+ off = pThis->offCurPos;
+ int rc;
+ if (pThis->pVDIfsIo)
+ rc = vdIfIoFileWriteSync(pThis->pVDIfsIo, pThis->pStorage, off, pSgBuf[0].pvSegCur, pSgBuf->paSegs[0].cbSeg, pcbWritten);
+ else
+ {
+ rc = vdIfIoIntFileWriteSync(pThis->pVDIfsIoInt, pThis->pStorage, off, pSgBuf[0].pvSegCur, pSgBuf->paSegs[0].cbSeg);
+ if (pcbWritten)
+ *pcbWritten = RT_SUCCESS(rc) ? pSgBuf->paSegs[0].cbSeg : 0;
+ }
+ if (RT_SUCCESS(rc))
+ pThis->offCurPos = off + (pcbWritten ? *pcbWritten : pSgBuf->paSegs[0].cbSeg);
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
+ */
+static DECLCALLBACK(int) vdIfVfsIos_Flush(void *pvThis)
+{
+ PVDIFVFSIOSFILE pThis = (PVDIFVFSIOSFILE)pvThis;
+ int rc;
+ if (pThis->pVDIfsIo)
+ rc = vdIfIoFileFlushSync(pThis->pVDIfsIo, pThis->pStorage);
+ else
+ rc = vdIfIoIntFileFlushSync(pThis->pVDIfsIoInt, pThis->pStorage);
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
+ */
+static DECLCALLBACK(int) vdIfVfsIos_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
+ uint32_t *pfRetEvents)
+{
+ NOREF(pvThis);
+ int rc;
+ if (fEvents != RTPOLL_EVT_ERROR)
+ {
+ *pfRetEvents = fEvents & ~RTPOLL_EVT_ERROR;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = RTVfsUtilDummyPollOne(fEvents, cMillies, fIntr, pfRetEvents);
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
+ */
+static DECLCALLBACK(int) vdIfVfsIos_Tell(void *pvThis, PRTFOFF poffActual)
+{
+ PVDIFVFSIOSFILE pThis = (PVDIFVFSIOSFILE)pvThis;
+ *poffActual = pThis->offCurPos;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * VFS I/O stream operations for a VD file or stream.
+ */
+DECL_HIDDEN_CONST(const RTVFSIOSTREAMOPS) g_vdIfVfsIosOps =
+{
+ { /* Obj */
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_IO_STREAM,
+ "VDIfIos",
+ vdIfVfsIos_Close,
+ vdIfVfsIos_QueryInfo,
+ RTVFSOBJOPS_VERSION
+ },
+ RTVFSIOSTREAMOPS_VERSION,
+ RTVFSIOSTREAMOPS_FEAT_NO_SG,
+ vdIfVfsIos_Read,
+ vdIfVfsIos_Write,
+ vdIfVfsIos_Flush,
+ vdIfVfsIos_PollOne,
+ vdIfVfsIos_Tell,
+ NULL /*Skip*/,
+ NULL /*ZeroFill*/,
+ RTVFSIOSTREAMOPS_VERSION,
+
+};
+
+VBOXDDU_DECL(int) VDIfCreateVfsStream(PVDINTERFACEIO pVDIfsIo, void *pvStorage, uint32_t fFlags, PRTVFSIOSTREAM phVfsIos)
+{
+ AssertPtrReturn(pVDIfsIo, VERR_INVALID_HANDLE);
+ AssertPtrReturn(phVfsIos, VERR_INVALID_POINTER);
+
+ /*
+ * Create the volume file.
+ */
+ RTVFSIOSTREAM hVfsIos;
+ PVDIFVFSIOSFILE pThis;
+ int rc = RTVfsNewIoStream(&g_vdIfVfsIosOps, sizeof(*pThis), fFlags,
+ NIL_RTVFS, NIL_RTVFSLOCK, &hVfsIos, (void **)&pThis);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->pVDIfsIo = pVDIfsIo;
+ pThis->pVDIfsIoInt = NULL;
+ pThis->pStorage = (PVDIOSTORAGE)pvStorage;
+ pThis->offCurPos = 0;
+
+ *phVfsIos = hVfsIos;
+ return VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
+ */
+static DECLCALLBACK(int) vdIfVfsFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
+{
+ NOREF(pvThis);
+ NOREF(fMode);
+ NOREF(fMask);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
+ */
+static DECLCALLBACK(int) vdIfVfsFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
+{
+ NOREF(pvThis);
+ NOREF(pAccessTime);
+ NOREF(pModificationTime);
+ NOREF(pChangeTime);
+ NOREF(pBirthTime);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
+ */
+static DECLCALLBACK(int) vdIfVfsFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
+{
+ NOREF(pvThis);
+ NOREF(uid);
+ NOREF(gid);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
+ */
+static DECLCALLBACK(int) vdIfVfsFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
+{
+ PVDIFVFSIOSFILE pThis = (PVDIFVFSIOSFILE)pvThis;
+
+ uint64_t cbFile;
+ int rc;
+ if (pThis->pVDIfsIo)
+ rc = vdIfIoFileGetSize(pThis->pVDIfsIo, pThis->pStorage, &cbFile);
+ else
+ rc = vdIfIoIntFileGetSize(pThis->pVDIfsIoInt, pThis->pStorage, &cbFile);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (cbFile >= (uint64_t)RTFOFF_MAX)
+ cbFile = RTFOFF_MAX;
+
+ /* Recalculate the request to RTFILE_SEEK_BEGIN. */
+ switch (uMethod)
+ {
+ case RTFILE_SEEK_BEGIN:
+ break;
+ case RTFILE_SEEK_CURRENT:
+ offSeek += pThis->offCurPos;
+ break;
+ case RTFILE_SEEK_END:
+ offSeek = cbFile + offSeek;
+ break;
+ default:
+ AssertFailedReturn(VERR_INVALID_PARAMETER);
+ }
+
+ /* Do limit checks. */
+ if (offSeek < 0)
+ offSeek = 0;
+ else if (offSeek > (RTFOFF)cbFile)
+ offSeek = cbFile;
+
+ /* Apply and return. */
+ pThis->offCurPos = offSeek;
+ if (poffActual)
+ *poffActual = offSeek;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
+ */
+static DECLCALLBACK(int) vdIfVfsFile_QuerySize(void *pvThis, uint64_t *pcbFile)
+{
+ PVDIFVFSIOSFILE pThis = (PVDIFVFSIOSFILE)pvThis;
+ int rc;
+ if (pThis->pVDIfsIo)
+ rc = vdIfIoFileGetSize(pThis->pVDIfsIo, pThis->pStorage, pcbFile);
+ else
+ rc = vdIfIoIntFileGetSize(pThis->pVDIfsIoInt, pThis->pStorage, pcbFile);
+ return rc;
+}
+
+
+
+/**
+ * VFS file operations for a VD file.
+ */
+DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_vdIfVfsFileOps =
+{
+ { /* I/O stream */
+ { /* Obj */
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_FILE,
+ "VDIfFile",
+ vdIfVfsIos_Close,
+ vdIfVfsIos_QueryInfo,
+ RTVFSOBJOPS_VERSION
+ },
+ RTVFSIOSTREAMOPS_VERSION,
+ RTVFSIOSTREAMOPS_FEAT_NO_SG,
+ vdIfVfsIos_Read,
+ vdIfVfsIos_Write,
+ vdIfVfsIos_Flush,
+ vdIfVfsIos_PollOne,
+ vdIfVfsIos_Tell,
+ NULL /*Skip*/,
+ NULL /*ZeroFill*/,
+ RTVFSIOSTREAMOPS_VERSION,
+ },
+ RTVFSFILEOPS_VERSION,
+ 0,
+ { /* ObjSet */
+ RTVFSOBJSETOPS_VERSION,
+ RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
+ vdIfVfsFile_SetMode,
+ vdIfVfsFile_SetTimes,
+ vdIfVfsFile_SetOwner,
+ RTVFSOBJSETOPS_VERSION
+ },
+ vdIfVfsFile_Seek,
+ vdIfVfsFile_QuerySize,
+ RTVFSFILEOPS_VERSION,
+};
+
+
+VBOXDDU_DECL(int) VDIfCreateVfsFile(PVDINTERFACEIO pVDIfs, struct VDINTERFACEIOINT *pVDIfsInt, void *pvStorage, uint32_t fFlags, PRTVFSFILE phVfsFile)
+{
+ AssertReturn((pVDIfs != NULL) != (pVDIfsInt != NULL), VERR_INVALID_PARAMETER); /* Exactly one needs to be specified. */
+ AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER);
+
+ /*
+ * Create the volume file.
+ */
+ RTVFSFILE hVfsFile;
+ PVDIFVFSIOSFILE pThis;
+ int rc = RTVfsNewFile(&g_vdIfVfsFileOps, sizeof(*pThis), fFlags,
+ NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFile, (void **)&pThis);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->pVDIfsIo = pVDIfs;
+ pThis->pVDIfsIoInt = pVDIfsInt;
+ pThis->pStorage = (PVDIOSTORAGE)pvStorage;
+ pThis->offCurPos = 0;
+
+ *phVfsFile = hVfsFile;
+ return VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/Storage/VHD.cpp b/src/VBox/Storage/VHD.cpp
index fc3a17b5..c563a267 100644
--- a/src/VBox/Storage/VHD.cpp
+++ b/src/VBox/Storage/VHD.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2010 Oracle Corporation
+ * Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -298,43 +298,74 @@ out:
*/
static int vhdLocatorUpdate(PVHDIMAGE pImage, PVHDPLE pLocator, const char *pszFilename)
{
- int rc;
- uint32_t cb, cbMaxLen = RT_BE2H_U32(pLocator->u32DataSpace) * VHD_SECTOR_SIZE;
+ int rc = VINF_SUCCESS;
+ uint32_t cb, cbMaxLen = RT_BE2H_U32(pLocator->u32DataSpace);
void *pvBuf = RTMemTmpAllocZ(cbMaxLen);
char *pszTmp;
if (!pvBuf)
- {
- rc = VERR_NO_MEMORY;
- goto out;
- }
+ return VERR_NO_MEMORY;
switch (RT_BE2H_U32(pLocator->u32Code))
{
case VHD_PLATFORM_CODE_WI2R:
- /* Update plain relative name. */
- cb = (uint32_t)strlen(pszFilename);
- if (cb > cbMaxLen)
+ {
+ if (RTPathStartsWithRoot(pszFilename))
{
- rc = VERR_FILENAME_TOO_LONG;
- goto out;
+ /* Convert to relative path. */
+ char szPath[RTPATH_MAX];
+ rc = RTPathCalcRelative(szPath, sizeof(szPath), pImage->pszFilename,
+ pszFilename);
+ if (RT_SUCCESS(rc))
+ {
+ /* Update plain relative name. */
+ cb = (uint32_t)strlen(szPath);
+ if (cb > cbMaxLen)
+ {
+ rc = VERR_FILENAME_TOO_LONG;
+ break;
+ }
+ memcpy(pvBuf, szPath, cb);
+ }
+ }
+ else
+ {
+ /* Update plain relative name. */
+ cb = (uint32_t)strlen(pszFilename);
+ if (cb > cbMaxLen)
+ {
+ rc = VERR_FILENAME_TOO_LONG;
+ break;
+ }
+ memcpy(pvBuf, pszFilename, cb);
}
- memcpy(pvBuf, pszFilename, cb);
pLocator->u32DataLength = RT_H2BE_U32(cb);
break;
+ }
case VHD_PLATFORM_CODE_WI2K:
/* Update plain absolute name. */
rc = RTPathAbs(pszFilename, (char *)pvBuf, cbMaxLen);
- if (RT_FAILURE(rc))
- goto out;
- pLocator->u32DataLength = RT_H2BE_U32((uint32_t)strlen((const char *)pvBuf));
+ if (RT_SUCCESS(rc))
+ pLocator->u32DataLength = RT_H2BE_U32((uint32_t)strlen((const char *)pvBuf));
break;
case VHD_PLATFORM_CODE_W2RU:
- /* Update unicode relative name. */
- rc = vhdFilenameToUtf16(pszFilename, (uint16_t *)pvBuf, cbMaxLen, &cb, false);
- if (RT_FAILURE(rc))
- goto out;
- pLocator->u32DataLength = RT_H2BE_U32(cb);
+ if (RTPathStartsWithRoot(pszFilename))
+ {
+ /* Convert to relative path. */
+ char szPath[RTPATH_MAX];
+ rc = RTPathCalcRelative(szPath, sizeof(szPath), pImage->pszFilename,
+ pszFilename);
+ if (RT_SUCCESS(rc))
+ rc = vhdFilenameToUtf16(szPath, (uint16_t *)pvBuf, cbMaxLen, &cb, false);
+ }
+ else
+ {
+ /* Update unicode relative name. */
+ rc = vhdFilenameToUtf16(pszFilename, (uint16_t *)pvBuf, cbMaxLen, &cb, false);
+ }
+
+ if (RT_SUCCESS(rc))
+ pLocator->u32DataLength = RT_H2BE_U32(cb);
break;
case VHD_PLATFORM_CODE_W2KU:
/* Update unicode absolute name. */
@@ -342,30 +373,29 @@ static int vhdLocatorUpdate(PVHDIMAGE pImage, PVHDPLE pLocator, const char *pszF
if (!pszTmp)
{
rc = VERR_NO_MEMORY;
- goto out;
+ break;
}
rc = RTPathAbs(pszFilename, pszTmp, cbMaxLen);
if (RT_FAILURE(rc))
{
RTMemTmpFree(pszTmp);
- goto out;
+ break;
}
rc = vhdFilenameToUtf16(pszTmp, (uint16_t *)pvBuf, cbMaxLen, &cb, false);
RTMemTmpFree(pszTmp);
- if (RT_FAILURE(rc))
- goto out;
- pLocator->u32DataLength = RT_H2BE_U32(cb);
+ if (RT_SUCCESS(rc))
+ pLocator->u32DataLength = RT_H2BE_U32(cb);
break;
default:
rc = VERR_NOT_IMPLEMENTED;
- goto out;
+ break;
}
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
- RT_BE2H_U64(pLocator->u64DataOffset),
- pvBuf, RT_BE2H_U32(pLocator->u32DataSpace) * VHD_SECTOR_SIZE,
- NULL);
-out:
+ if (RT_SUCCESS(rc))
+ rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
+ RT_BE2H_U64(pLocator->u64DataOffset),
+ pvBuf, cb);
+
if (pvBuf)
RTMemTmpFree(pvBuf);
return rc;
@@ -383,7 +413,7 @@ static int vhdDynamicHeaderUpdate(PVHDIMAGE pImage)
return VERR_VD_NOT_OPENED;
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
- pImage->u64DataOffset, &ddh, sizeof(ddh), NULL);
+ pImage->u64DataOffset, &ddh, sizeof(ddh));
if (RT_FAILURE(rc))
return rc;
if (memcmp(ddh.Cookie, VHD_DYNAMIC_DISK_HEADER_COOKIE, VHD_DYNAMIC_DISK_HEADER_COOKIE_SIZE) != 0)
@@ -433,7 +463,7 @@ static int vhdDynamicHeaderUpdate(PVHDIMAGE pImage)
ddh.Checksum = 0;
ddh.Checksum = RT_H2BE_U32(vhdChecksum(&ddh, sizeof(ddh)));
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
- pImage->u64DataOffset, &ddh, sizeof(ddh), NULL);
+ pImage->u64DataOffset, &ddh, sizeof(ddh));
return rc;
}
@@ -455,12 +485,12 @@ static int vhdUpdateFooter(PVHDIMAGE pImage)
if (pImage->pBlockAllocationTable)
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, 0,
- &pImage->vhdFooterCopy, sizeof(VHDFooter), NULL);
+ &pImage->vhdFooterCopy, sizeof(VHDFooter));
if (RT_SUCCESS(rc))
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
pImage->uCurrentEndOfFile, &pImage->vhdFooterCopy,
- sizeof(VHDFooter), NULL);
+ sizeof(VHDFooter));
return rc;
}
@@ -496,7 +526,7 @@ static int vhdFlushImage(PVHDIMAGE pImage)
* Write the block allocation table after the copy of the disk footer and the dynamic disk header.
*/
vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, pImage->uBlockAllocationTableOffset,
- pBlockAllocationTableToWrite, cbBlockAllocationTableToWrite, NULL);
+ pBlockAllocationTableToWrite, cbBlockAllocationTableToWrite);
if (pImage->fDynHdrNeedsUpdate)
rc = vhdDynamicHeaderUpdate(pImage);
RTMemFree(pBlockAllocationTableToWrite);
@@ -529,7 +559,7 @@ static int vhdFreeImage(PVHDIMAGE pImage, bool fDelete)
if (!fDelete)
vhdFlushImage(pImage);
- vdIfIoIntFileClose(pImage->pIfIo, pImage->pStorage);
+ rc = vdIfIoIntFileClose(pImage->pIfIo, pImage->pStorage);
pImage->pStorage = NULL;
}
@@ -550,7 +580,7 @@ static int vhdFreeImage(PVHDIMAGE pImage, bool fDelete)
}
if (fDelete && pImage->pszFilename)
- rc = vdIfIoIntFileDelete(pImage->pIfIo, pImage->pszFilename);
+ vdIfIoIntFileDelete(pImage->pIfIo, pImage->pszFilename);
}
LogFlowFunc(("returns %Rrc\n", rc));
@@ -615,11 +645,11 @@ static int vhdAsyncExpansionComplete(PVHDIMAGE pImage, PVDIOCTX pIoCtx, PVHDIMAG
* do anything if this fails. */
if (uStatus == VHDIMAGEEXPAND_STEP_SUCCESS)
{
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
- pImage->uBlockAllocationTableOffset
- + pExpand->idxBatAllocated * sizeof(uint32_t),
- &pImage->pBlockAllocationTable[pExpand->idxBatAllocated],
- sizeof(uint32_t), pIoCtx, NULL, NULL);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
+ pImage->uBlockAllocationTableOffset
+ + pExpand->idxBatAllocated * sizeof(uint32_t),
+ &pImage->pBlockAllocationTable[pExpand->idxBatAllocated],
+ sizeof(uint32_t), pIoCtx, NULL, NULL);
fIoInProgress |= rc == VERR_VD_ASYNC_IO_IN_PROGRESS;
}
}
@@ -632,10 +662,10 @@ static int vhdAsyncExpansionComplete(PVHDIMAGE pImage, PVDIOCTX pIoCtx, PVHDIMAG
AssertRC(rc);
pImage->uCurrentEndOfFile = pExpand->cbEofOld;
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
- pImage->uCurrentEndOfFile,
- &pImage->vhdFooterCopy, sizeof(VHDFooter),
- pIoCtx, NULL, NULL);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
+ pImage->uCurrentEndOfFile,
+ &pImage->vhdFooterCopy, sizeof(VHDFooter),
+ pIoCtx, NULL, NULL);
fIoInProgress |= rc == VERR_VD_ASYNC_IO_IN_PROGRESS;
}
@@ -695,8 +725,7 @@ static int vhdLoadDynamicDisk(PVHDIMAGE pImage, uint64_t uDynamicDiskHeaderOffse
* Read the dynamic disk header.
*/
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, uDynamicDiskHeaderOffset,
- &vhdDynamicDiskHeader, sizeof(VHDDynamicDiskHeader),
- NULL);
+ &vhdDynamicDiskHeader, sizeof(VHDDynamicDiskHeader));
if (memcmp(vhdDynamicDiskHeader.Cookie, VHD_DYNAMIC_DISK_HEADER_COOKIE, VHD_DYNAMIC_DISK_HEADER_COOKIE_SIZE))
return VERR_INVALID_PARAMETER;
@@ -737,8 +766,7 @@ static int vhdLoadDynamicDisk(PVHDIMAGE pImage, uint64_t uDynamicDiskHeaderOffse
pImage->uBlockAllocationTableOffset = uBlockAllocationTableOffset;
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
uBlockAllocationTableOffset, pBlockAllocationTable,
- pImage->cBlockAllocationTableEntries * sizeof(uint32_t),
- NULL);
+ pImage->cBlockAllocationTableEntries * sizeof(uint32_t));
/*
* Because the offset entries inside the allocation table are stored big endian
@@ -791,9 +819,29 @@ static int vhdOpenImage(PVHDIMAGE pImage, unsigned uOpenFlags)
pImage->uCurrentEndOfFile = FileSize - sizeof(VHDFooter);
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, pImage->uCurrentEndOfFile,
- &vhdFooter, sizeof(VHDFooter), NULL);
- if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
- return VERR_VD_VHD_INVALID_HEADER;
+ &vhdFooter, sizeof(VHDFooter));
+ if (RT_SUCCESS(rc))
+ {
+ if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
+ {
+ /*
+ * There is also a backup header at the beginning in case the image got corrupted.
+ * Such corrupted images are detected here to let the open handler repair it later.
+ */
+ rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, 0,
+ &vhdFooter, sizeof(VHDFooter));
+ if (RT_SUCCESS(rc))
+ {
+ if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
+ rc = VERR_VD_VHD_INVALID_HEADER;
+ else
+ rc = VERR_VD_IMAGE_CORRUPTED;
+ }
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ return rc;
switch (RT_BE2H_U32(vhdFooter.DiskType))
{
@@ -938,29 +986,26 @@ static void vhdSetDiskGeometry(PVHDIMAGE pImage, uint64_t cbSize)
static uint32_t vhdAllocateParentLocators(PVHDIMAGE pImage, VHDDynamicDiskHeader *pDDH, uint64_t u64Offset)
{
PVHDPLE pLocator = pDDH->ParentLocatorEntry;
- /* Relative Windows path. */
- pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_WI2R);
- pLocator->u32DataSpace = RT_H2BE_U32(VHD_RELATIVE_MAX_PATH / VHD_SECTOR_SIZE);
- pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
- u64Offset += VHD_RELATIVE_MAX_PATH;
- pLocator++;
- /* Absolute Windows path. */
- pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_WI2K);
- pLocator->u32DataSpace = RT_H2BE_U32(VHD_ABSOLUTE_MAX_PATH / VHD_SECTOR_SIZE);
+
+ /*
+ * The VHD spec states that the DataSpace field holds the number of sectors
+ * required to store the parent locator path.
+ * As it turned out VPC and Hyper-V store the amount of bytes reserved for the
+ * path and not the number of sectors.
+ */
+
+ /* Unicode absolute Windows path. */
+ pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_W2KU);
+ pLocator->u32DataSpace = RT_H2BE_U32(VHD_ABSOLUTE_MAX_PATH * sizeof(RTUTF16));
pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
- u64Offset += VHD_ABSOLUTE_MAX_PATH;
pLocator++;
+ u64Offset += VHD_ABSOLUTE_MAX_PATH * sizeof(RTUTF16);
/* Unicode relative Windows path. */
pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_W2RU);
- pLocator->u32DataSpace = RT_H2BE_U32(VHD_RELATIVE_MAX_PATH * sizeof(RTUTF16) / VHD_SECTOR_SIZE);
+ pLocator->u32DataSpace = RT_H2BE_U32(VHD_RELATIVE_MAX_PATH * sizeof(RTUTF16));
pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
u64Offset += VHD_RELATIVE_MAX_PATH * sizeof(RTUTF16);
- pLocator++;
- /* Unicode absolute Windows path. */
- pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_W2KU);
- pLocator->u32DataSpace = RT_H2BE_U32(VHD_ABSOLUTE_MAX_PATH * sizeof(RTUTF16) / VHD_SECTOR_SIZE);
- pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
- return u64Offset + VHD_ABSOLUTE_MAX_PATH * sizeof(RTUTF16);
+ return u64Offset;
}
/**
@@ -1013,7 +1058,7 @@ static int vhdCreateDynamicImage(PVHDIMAGE pImage, uint64_t cbSize)
return vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS, N_("VHD: cannot set the file size for '%s'"), pImage->pszFilename);
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, 0, pvTmp,
- pImage->uCurrentEndOfFile + sizeof(VHDFooter), NULL);
+ pImage->uCurrentEndOfFile + sizeof(VHDFooter));
if (RT_FAILURE(rc))
{
RTMemTmpFree(pvTmp);
@@ -1034,15 +1079,14 @@ static int vhdCreateDynamicImage(PVHDIMAGE pImage, uint64_t cbSize)
DynamicDiskHeader.Checksum = RT_H2BE_U32(vhdChecksum(&DynamicDiskHeader, sizeof(DynamicDiskHeader)));
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, sizeof(VHDFooter),
- &DynamicDiskHeader, sizeof(DynamicDiskHeader), NULL);
+ &DynamicDiskHeader, sizeof(DynamicDiskHeader));
if (RT_FAILURE(rc))
return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot write dynamic disk header to image '%s'"), pImage->pszFilename);
/* Write BAT. */
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, pImage->uBlockAllocationTableOffset,
pImage->pBlockAllocationTable,
- pImage->cBlockAllocationTableEntries * sizeof(uint32_t),
- NULL);
+ pImage->cBlockAllocationTableEntries * sizeof(uint32_t));
if (RT_FAILURE(rc))
return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot write BAT to image '%s'"), pImage->pszFilename);
@@ -1162,7 +1206,7 @@ static int vhdCreateImage(PVHDIMAGE pImage, uint64_t cbSize,
/* Store the footer */
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, pImage->uCurrentEndOfFile,
- &Footer, sizeof(Footer), NULL);
+ &Footer, sizeof(Footer));
if (RT_FAILURE(rc))
{
vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot write footer to image '%s'"), pImage->pszFilename);
@@ -1173,7 +1217,7 @@ static int vhdCreateImage(PVHDIMAGE pImage, uint64_t cbSize,
if (!(uImageFlags & VD_IMAGE_FLAGS_FIXED))
{
/* Write the copy of the footer. */
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, 0, &Footer, sizeof(Footer), NULL);
+ rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, 0, &Footer, sizeof(Footer));
if (RT_FAILURE(rc))
{
vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot write a copy of footer to image '%s'"), pImage->pszFilename);
@@ -1220,14 +1264,26 @@ static int vhdCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
}
rc = vdIfIoIntFileReadSync(pIfIo, pStorage, cbFile - sizeof(VHDFooter),
- &vhdFooter, sizeof(VHDFooter), NULL);
- if (RT_FAILURE(rc) || (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0))
- rc = VERR_VD_VHD_INVALID_HEADER;
- else
+ &vhdFooter, sizeof(VHDFooter));
+ if (RT_SUCCESS(rc))
{
- *penmType = VDTYPE_HDD;
- rc = VINF_SUCCESS;
+ if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
+ {
+ /*
+ * There is also a backup header at the beginning in case the image got corrupted.
+ * Such corrupted images are detected here to let the open handler repair it later.
+ */
+ rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0, &vhdFooter, sizeof(VHDFooter));
+ if ( RT_FAILURE(rc)
+ || (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0))
+ rc = VERR_VD_VHD_INVALID_HEADER;
+ }
+
+ if (RT_SUCCESS(rc))
+ *penmType = VDTYPE_HDD;
}
+ else
+ rc = VERR_VD_VHD_INVALID_HEADER;
vdIfIoIntFileClose(pIfIo, pStorage);
@@ -1422,18 +1478,16 @@ static int vhdClose(void *pBackendData, bool fDelete)
}
/** @copydoc VBOXHDDBACKEND::pfnRead */
-static int vhdRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
- size_t cbBuf, size_t *pcbActuallyRead)
+static int vhdRead(void *pBackendData, uint64_t uOffset, size_t cbRead,
+ PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
{
- LogFlowFunc(("pBackendData=%p uOffset=%#llu pvBuf=%p cbBuf=%u pcbActuallyRead=%p\n", pBackendData, uOffset, pvBuf, cbBuf, pcbActuallyRead));
PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
int rc = VINF_SUCCESS;
- if (uOffset + cbBuf > pImage->cbSize)
- {
- rc = VERR_INVALID_PARAMETER;
- goto out;
- }
+ LogFlowFunc(("pBackendData=%p uOffset=%#llx pIoCtx=%#p cbRead=%u pcbActuallyRead=%p\n", pBackendData, uOffset, pIoCtx, cbRead, pcbActuallyRead));
+
+ if (uOffset + cbRead > pImage->cbSize)
+ return VERR_INVALID_PARAMETER;
/*
* If we have a dynamic disk image, we need to find the data block and sector to read.
@@ -1453,7 +1507,7 @@ static int vhdRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
/*
* Clip read range to remain in this data block.
*/
- cbBuf = RT_MIN(cbBuf, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
+ cbRead = RT_MIN(cbRead, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
/*
* If the block is not allocated the content of the entry is ~0
@@ -1463,17 +1517,20 @@ static int vhdRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
else
{
uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
- LogFlowFunc(("uVhdOffset=%llu cbBuf=%u\n", uVhdOffset, cbBuf));
+ LogFlowFunc(("uVhdOffset=%llu cbRead=%u\n", uVhdOffset, cbRead));
/* Read in the block's bitmap. */
- rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
+ PVDMETAXFER pMetaXfer;
+ rc = vdIfIoIntFileReadMeta(pImage->pIfIo, pImage->pStorage,
((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
- NULL);
+ pIoCtx, &pMetaXfer, NULL, NULL);
+
if (RT_SUCCESS(rc))
{
uint32_t cSectors = 0;
+ vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
if (vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
{
cBATEntryIndex++;
@@ -1484,18 +1541,18 @@ static int vhdRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
* can from child. Note that only sectors that are marked dirty
* must be read from child.
*/
- while ( (cSectors < (cbBuf / VHD_SECTOR_SIZE))
+ while ( (cSectors < (cbRead / VHD_SECTOR_SIZE))
&& vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
{
cBATEntryIndex++;
cSectors++;
}
- cbBuf = cSectors * VHD_SECTOR_SIZE;
+ cbRead = cSectors * VHD_SECTOR_SIZE;
- LogFlowFunc(("uVhdOffset=%llu cbBuf=%u\n", uVhdOffset, cbBuf));
- rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
- uVhdOffset, pvBuf, cbBuf, NULL);
+ LogFlowFunc(("uVhdOffset=%llu cbRead=%u\n", uVhdOffset, cbRead));
+ rc = vdIfIoIntFileReadUser(pImage->pIfIo, pImage->pStorage,
+ uVhdOffset, pIoCtx, cbRead);
}
else
{
@@ -1510,56 +1567,46 @@ static int vhdRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
cBATEntryIndex++;
cSectors = 1;
- while ( (cSectors < (cbBuf / VHD_SECTOR_SIZE))
+ while ( (cSectors < (cbRead / VHD_SECTOR_SIZE))
&& !vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
{
cBATEntryIndex++;
cSectors++;
}
- cbBuf = cSectors * VHD_SECTOR_SIZE;
- LogFunc(("Sectors free: uVhdOffset=%llu cbBuf=%u\n", uVhdOffset, cbBuf));
+ cbRead = cSectors * VHD_SECTOR_SIZE;
+ LogFunc(("Sectors free: uVhdOffset=%llu cbRead=%u\n", uVhdOffset, cbRead));
rc = VERR_VD_BLOCK_FREE;
}
}
else
- AssertMsgFailed(("Reading block bitmap failed rc=%Rrc\n", rc));
+ AssertMsg(rc == VERR_VD_NOT_ENOUGH_METADATA, ("Reading block bitmap failed rc=%Rrc\n", rc));
}
}
else
- rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, uOffset, pvBuf, cbBuf, NULL);
+ rc = vdIfIoIntFileReadUser(pImage->pIfIo, pImage->pStorage, uOffset, pIoCtx, cbRead);
- if ( RT_SUCCESS(rc)
- || rc == VERR_VD_BLOCK_FREE)
- {
- if (pcbActuallyRead)
- *pcbActuallyRead = cbBuf;
-
- Log2(("vhdRead: off=%#llx pvBuf=%p cbBuf=%d\n"
- "%.*Rhxd\n",
- uOffset, pvBuf, cbBuf, cbBuf, pvBuf));
- }
+ if (pcbActuallyRead)
+ *pcbActuallyRead = cbRead;
-out:
- LogFlowFunc(("returns %Rrc\n", rc));
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnWrite */
-static int vhdWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
- size_t cbBuf, size_t *pcbWriteProcess,
- size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
+static int vhdWrite(void *pBackendData, uint64_t uOffset, size_t cbWrite,
+ PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
+ size_t *pcbPostRead, unsigned fWrite)
{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbBuf=%zu pcbWriteProcess=%#p\n", pBackendData, uOffset, pvBuf, cbBuf, pcbWriteProcess));
PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
int rc = VINF_SUCCESS;
- LogFlowFunc(("pBackendData=%p uOffset=%llu pvBuf=%p cbBuf=%u pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p fWrite=%u\n",
- pBackendData, uOffset, pvBuf, cbBuf, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite));
+ LogFlowFunc(("pBackendData=%p uOffset=%llu pIoCtx=%#p cbWrite=%u pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p fWrite=%u\n",
+ pBackendData, uOffset, pIoCtx, cbWrite, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite));
AssertPtr(pImage);
Assert(uOffset % VHD_SECTOR_SIZE == 0);
- Assert(cbBuf % VHD_SECTOR_SIZE == 0);
+ Assert(cbWrite % VHD_SECTOR_SIZE == 0);
if (pImage->pBlockAllocationTable)
{
@@ -1574,7 +1621,7 @@ static int vhdWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
/*
* Clip write range.
*/
- cbBuf = RT_MIN(cbBuf, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
+ cbWrite = RT_MIN(cbWrite, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
/*
* If the block is not allocated the content of the entry is ~0
@@ -1591,105 +1638,202 @@ static int vhdWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
if (fWrite & VD_WRITE_NO_ALLOC)
{
*pcbPreRead = cBATEntryIndex * VHD_SECTOR_SIZE;
- *pcbPostRead = pImage->cSectorsPerDataBlock * VHD_SECTOR_SIZE - cbBuf - *pcbPreRead;
+ *pcbPostRead = pImage->cSectorsPerDataBlock * VHD_SECTOR_SIZE - cbWrite - *pcbPreRead;
if (pcbWriteProcess)
- *pcbWriteProcess = cbBuf;
- rc = VERR_VD_BLOCK_FREE;
- goto out;
+ *pcbWriteProcess = cbWrite;
+ return VERR_VD_BLOCK_FREE;
}
- size_t cbNewBlock = pImage->cbDataBlock + (pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE);
- uint8_t *pNewBlock = (uint8_t *)RTMemAllocZ(cbNewBlock);
+ PVHDIMAGEEXPAND pExpand = (PVHDIMAGEEXPAND)RTMemAllocZ(RT_OFFSETOF(VHDIMAGEEXPAND, au8Bitmap[pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE]));
+ bool fIoInProgress = false;
- if (!pNewBlock)
+ if (!pExpand)
+ return VERR_NO_MEMORY;
+
+ pExpand->cbEofOld = pImage->uCurrentEndOfFile;
+ pExpand->idxBatAllocated = cBlockAllocationTableEntry;
+ pExpand->idxBlockBe = RT_H2BE_U32(pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE);
+
+ /* Set the bits for all sectors having been written. */
+ for (uint32_t iSector = 0; iSector < (cbWrite / VHD_SECTOR_SIZE); iSector++)
{
- rc = VERR_NO_MEMORY;
- goto out;
+ /* No need to check for a changed value because this is an initial write. */
+ vhdBlockBitmapSectorSet(pImage, pExpand->au8Bitmap, cBATEntryIndex);
+ cBATEntryIndex++;
}
- /*
- * Write the new block at the current end of the file.
- */
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, pImage->uCurrentEndOfFile,
- pNewBlock, cbNewBlock, NULL);
- AssertRC(rc);
+ do
+ {
+ /*
+ * Start with the sector bitmap.
+ */
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
+ pImage->uCurrentEndOfFile,
+ pExpand->au8Bitmap,
+ pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE, pIoCtx,
+ vhdAsyncExpansionDataBlockBitmapComplete,
+ pExpand);
+ if (RT_SUCCESS(rc))
+ VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
+ else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+ fIoInProgress = true;
+ else
+ {
+ VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
+ VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
+ VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
+ VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
+ break;
+ }
- /*
- * Set the new end of the file and link the new block into the BAT.
- */
- pImage->pBlockAllocationTable[cBlockAllocationTableEntry] = pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE;
- pImage->uCurrentEndOfFile += cbNewBlock;
- RTMemFree(pNewBlock);
- /* Write the updated BAT and the footer to remain in a consistent state. */
- rc = vhdFlushImage(pImage);
- AssertRC(rc);
- }
+ /*
+ * Write the new block at the current end of the file.
+ */
+ rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
+ pImage->uCurrentEndOfFile + pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE,
+ pIoCtx, cbWrite,
+ vhdAsyncExpansionDataComplete,
+ pExpand);
+ if (RT_SUCCESS(rc))
+ VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
+ else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+ fIoInProgress = true;
+ else
+ {
+ VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
+ VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
+ VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
+ break;
+ }
- /*
- * Calculate the real offset in the file.
- */
- uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
+ /*
+ * Write entry in the BAT.
+ */
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
+ pImage->uBlockAllocationTableOffset + cBlockAllocationTableEntry * sizeof(uint32_t),
+ &pExpand->idxBlockBe, sizeof(uint32_t), pIoCtx,
+ vhdAsyncExpansionBatUpdateComplete,
+ pExpand);
+ if (RT_SUCCESS(rc))
+ VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
+ else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+ fIoInProgress = true;
+ else
+ {
+ VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
+ VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
+ break;
+ }
- /* Write data. */
- vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, uVhdOffset,
- pvBuf, cbBuf, NULL);
+ /*
+ * Set the new end of the file and link the new block into the BAT.
+ */
+ pImage->pBlockAllocationTable[cBlockAllocationTableEntry] = pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE;
+ pImage->uCurrentEndOfFile += pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE + pImage->cbDataBlock;
- /* Read in the block's bitmap. */
- rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
- ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
- pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
- NULL);
- if (RT_SUCCESS(rc))
+ /* Update the footer. */
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
+ pImage->uCurrentEndOfFile,
+ &pImage->vhdFooterCopy,
+ sizeof(VHDFooter), pIoCtx,
+ vhdAsyncExpansionFooterUpdateComplete,
+ pExpand);
+ if (RT_SUCCESS(rc))
+ VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
+ else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+ fIoInProgress = true;
+ else
+ {
+ VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
+ break;
+ }
+
+ } while (0);
+
+ if (!fIoInProgress)
+ vhdAsyncExpansionComplete(pImage, pIoCtx, pExpand);
+ else
+ rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
+ }
+ else
{
- bool fChanged = false;
+ /*
+ * Calculate the real offset in the file.
+ */
+ uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
- /* Set the bits for all sectors having been written. */
- for (uint32_t iSector = 0; iSector < (cbBuf / VHD_SECTOR_SIZE); iSector++)
+ /* Read in the block's bitmap. */
+ PVDMETAXFER pMetaXfer;
+ rc = vdIfIoIntFileReadMeta(pImage->pIfIo, pImage->pStorage,
+ ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
+ pImage->pu8Bitmap,
+ pImage->cbDataBlockBitmap, pIoCtx,
+ &pMetaXfer, NULL, NULL);
+ if (RT_SUCCESS(rc))
{
- fChanged |= vhdBlockBitmapSectorSet(pImage, pImage->pu8Bitmap, cBATEntryIndex);
- cBATEntryIndex++;
- }
+ vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
- if (fChanged)
- {
- /* Write the bitmap back. */
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
- ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
- pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
- NULL);
+ /* Write data. */
+ rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
+ uVhdOffset, pIoCtx, cbWrite,
+ NULL, NULL);
+ if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+ {
+ bool fChanged = false;
+
+ /* Set the bits for all sectors having been written. */
+ for (uint32_t iSector = 0; iSector < (cbWrite / VHD_SECTOR_SIZE); iSector++)
+ {
+ fChanged |= vhdBlockBitmapSectorSet(pImage, pImage->pu8Bitmap, cBATEntryIndex);
+ cBATEntryIndex++;
+ }
+
+ /* Only write the bitmap if it was changed. */
+ if (fChanged)
+ {
+ /*
+ * Write the bitmap back.
+ *
+ * @note We don't have a completion callback here because we
+ * can't do anything if the write fails for some reason.
+ * The error will propagated to the device/guest
+ * by the generic VD layer already and we don't need
+ * to rollback anything here.
+ */
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
+ ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
+ pImage->pu8Bitmap,
+ pImage->cbDataBlockBitmap,
+ pIoCtx, NULL, NULL);
+ }
+ }
}
}
}
else
- {
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, uOffset, pvBuf, cbBuf, NULL);
- }
+ rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
+ uOffset, pIoCtx, cbWrite, NULL, NULL);
if (pcbWriteProcess)
- *pcbWriteProcess = cbBuf;
+ *pcbWriteProcess = cbWrite;
/* Stay on the safe side. Do not run the risk of confusing the higher
* level, as that can be pretty lethal to image consistency. */
*pcbPreRead = 0;
*pcbPostRead = 0;
-out:
- LogFlowFunc(("returns %Rrc\n", rc));
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnFlush */
-static int vhdFlush(void *pBackendData)
+static int vhdFlush(void *pBackendData, PVDIOCTX pIoCtx)
{
- LogFlowFunc(("pBackendData=%#p", pBackendData));
PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
- int rc;
- rc = vhdFlushImage(pImage);
- LogFlowFunc(("returns %Rrc\n", rc));
- return rc;
+ /* No need to write anything here. Data is always updated on a write. */
+ return vdIfIoIntFileFlush(pImage->pIfIo, pImage->pStorage, pIoCtx, NULL, NULL);
}
/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
@@ -1708,6 +1852,22 @@ static unsigned vhdGetVersion(void *pBackendData)
return ver;
}
+/** @copydoc VBOXHDDBACKEND::pfnGetSectorSize */
+static uint32_t vhdGetSectorSize(void *pBackendData)
+{
+ LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+ PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+ uint32_t cb = 0;
+
+ AssertPtr(pImage);
+
+ if (pImage)
+ cb = 512;
+
+ LogFlowFunc(("returns %zu\n", cb));
+ return cb;
+}
+
/** @copydoc VBOXHDDBACKEND::pfnGetSize */
static uint64_t vhdGetSize(void *pBackendData)
{
@@ -1891,7 +2051,9 @@ static int vhdSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
int rc;
/* Image must be opened and the new flags must be valid. */
- if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL)))
+ if (!pImage || (uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
+ | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE
+ | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
{
rc = VERR_INVALID_PARAMETER;
goto out;
@@ -2255,368 +2417,6 @@ static int vhdSetParentFilename(void *pBackendData, const char *pszParentFilenam
return rc;
}
-/** @copydoc VBOXHDDBACKEND::pfnAsyncRead */
-static int vhdAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbRead,
- PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
-{
- PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
- int rc = VINF_SUCCESS;
-
- LogFlowFunc(("pBackendData=%p uOffset=%#llx pIoCtx=%#p cbRead=%u pcbActuallyRead=%p\n", pBackendData, uOffset, pIoCtx, cbRead, pcbActuallyRead));
-
- if (uOffset + cbRead > pImage->cbSize)
- return VERR_INVALID_PARAMETER;
-
- /*
- * If we have a dynamic disk image, we need to find the data block and sector to read.
- */
- if (pImage->pBlockAllocationTable)
- {
- /*
- * Get the data block first.
- */
- uint32_t cBlockAllocationTableEntry = (uOffset / VHD_SECTOR_SIZE) / pImage->cSectorsPerDataBlock;
- uint32_t cBATEntryIndex = (uOffset / VHD_SECTOR_SIZE) % pImage->cSectorsPerDataBlock;
- uint64_t uVhdOffset;
-
- LogFlowFunc(("cBlockAllocationTableEntry=%u cBatEntryIndex=%u\n", cBlockAllocationTableEntry, cBATEntryIndex));
- LogFlowFunc(("BlockAllocationEntry=%u\n", pImage->pBlockAllocationTable[cBlockAllocationTableEntry]));
-
- /*
- * Clip read range to remain in this data block.
- */
- cbRead = RT_MIN(cbRead, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
-
- /*
- * If the block is not allocated the content of the entry is ~0
- */
- if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
- rc = VERR_VD_BLOCK_FREE;
- else
- {
- uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
- LogFlowFunc(("uVhdOffset=%llu cbRead=%u\n", uVhdOffset, cbRead));
-
- /* Read in the block's bitmap. */
- PVDMETAXFER pMetaXfer;
- rc = vdIfIoIntFileReadMetaAsync(pImage->pIfIo, pImage->pStorage,
- ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
- pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
- pIoCtx, &pMetaXfer, NULL, NULL);
-
- if (RT_SUCCESS(rc))
- {
- uint32_t cSectors = 0;
-
- vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
- if (vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
- {
- cBATEntryIndex++;
- cSectors = 1;
-
- /*
- * The first sector being read is marked dirty, read as much as we
- * can from child. Note that only sectors that are marked dirty
- * must be read from child.
- */
- while ( (cSectors < (cbRead / VHD_SECTOR_SIZE))
- && vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
- {
- cBATEntryIndex++;
- cSectors++;
- }
-
- cbRead = cSectors * VHD_SECTOR_SIZE;
-
- LogFlowFunc(("uVhdOffset=%llu cbRead=%u\n", uVhdOffset, cbRead));
- rc = vdIfIoIntFileReadUserAsync(pImage->pIfIo, pImage->pStorage,
- uVhdOffset, pIoCtx, cbRead);
- }
- else
- {
- /*
- * The first sector being read is marked clean, so we should read from
- * our parent instead, but only as much as there are the following
- * clean sectors, because the block may still contain dirty sectors
- * further on. We just need to compute the number of clean sectors
- * and pass it to our caller along with the notification that they
- * should be read from the parent.
- */
- cBATEntryIndex++;
- cSectors = 1;
-
- while ( (cSectors < (cbRead / VHD_SECTOR_SIZE))
- && !vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
- {
- cBATEntryIndex++;
- cSectors++;
- }
-
- cbRead = cSectors * VHD_SECTOR_SIZE;
- LogFunc(("Sectors free: uVhdOffset=%llu cbRead=%u\n", uVhdOffset, cbRead));
- rc = VERR_VD_BLOCK_FREE;
- }
- }
- else
- AssertMsg(rc == VERR_VD_NOT_ENOUGH_METADATA, ("Reading block bitmap failed rc=%Rrc\n", rc));
- }
- }
- else
- rc = vdIfIoIntFileReadUserAsync(pImage->pIfIo, pImage->pStorage, uOffset, pIoCtx, cbRead);
-
- if (pcbActuallyRead)
- *pcbActuallyRead = cbRead;
-
- LogFlowFunc(("returns rc=%Rrc\n", rc));
- return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncWrite */
-static int vhdAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbWrite,
- PVDIOCTX pIoCtx,
- size_t *pcbWriteProcess, size_t *pcbPreRead,
- size_t *pcbPostRead, unsigned fWrite)
-{
- PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
- int rc = VINF_SUCCESS;
-
- LogFlowFunc(("pBackendData=%p uOffset=%llu pIoCtx=%#p cbWrite=%u pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p fWrite=%u\n",
- pBackendData, uOffset, pIoCtx, cbWrite, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite));
-
- AssertPtr(pImage);
- Assert(uOffset % VHD_SECTOR_SIZE == 0);
- Assert(cbWrite % VHD_SECTOR_SIZE == 0);
-
- if (pImage->pBlockAllocationTable)
- {
- /*
- * Get the data block first.
- */
- uint32_t cSector = uOffset / VHD_SECTOR_SIZE;
- uint32_t cBlockAllocationTableEntry = cSector / pImage->cSectorsPerDataBlock;
- uint32_t cBATEntryIndex = cSector % pImage->cSectorsPerDataBlock;
- uint64_t uVhdOffset;
-
- /*
- * Clip write range.
- */
- cbWrite = RT_MIN(cbWrite, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
-
- /*
- * If the block is not allocated the content of the entry is ~0
- * and we need to allocate a new block. Note that while blocks are
- * allocated with a relatively big granularity, each sector has its
- * own bitmap entry, indicating whether it has been written or not.
- * So that means for the purposes of the higher level that the
- * granularity is invisible. This means there's no need to return
- * VERR_VD_BLOCK_FREE unless the block hasn't been allocated yet.
- */
- if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
- {
- /* Check if the block allocation should be suppressed. */
- if (fWrite & VD_WRITE_NO_ALLOC)
- {
- *pcbPreRead = cBATEntryIndex * VHD_SECTOR_SIZE;
- *pcbPostRead = pImage->cSectorsPerDataBlock * VHD_SECTOR_SIZE - cbWrite - *pcbPreRead;
-
- if (pcbWriteProcess)
- *pcbWriteProcess = cbWrite;
- return VERR_VD_BLOCK_FREE;
- }
-
- PVHDIMAGEEXPAND pExpand = (PVHDIMAGEEXPAND)RTMemAllocZ(RT_OFFSETOF(VHDIMAGEEXPAND, au8Bitmap[pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE]));
- bool fIoInProgress = false;
-
- if (!pExpand)
- return VERR_NO_MEMORY;
-
- pExpand->cbEofOld = pImage->uCurrentEndOfFile;
- pExpand->idxBatAllocated = cBlockAllocationTableEntry;
- pExpand->idxBlockBe = RT_H2BE_U32(pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE);
-
- /* Set the bits for all sectors having been written. */
- for (uint32_t iSector = 0; iSector < (cbWrite / VHD_SECTOR_SIZE); iSector++)
- {
- /* No need to check for a changed value because this is an initial write. */
- vhdBlockBitmapSectorSet(pImage, pExpand->au8Bitmap, cBATEntryIndex);
- cBATEntryIndex++;
- }
-
- do
- {
- /*
- * Start with the sector bitmap.
- */
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
- pImage->uCurrentEndOfFile,
- pExpand->au8Bitmap,
- pImage->cbDataBlockBitmap, pIoCtx,
- vhdAsyncExpansionDataBlockBitmapComplete,
- pExpand);
- if (RT_SUCCESS(rc))
- VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
- else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
- fIoInProgress = true;
- else
- {
- VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
- VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
- VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
- VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
- break;
- }
-
-
- /*
- * Write the new block at the current end of the file.
- */
- rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pImage->pStorage,
- pImage->uCurrentEndOfFile + pImage->cbDataBlockBitmap,
- pIoCtx, cbWrite,
- vhdAsyncExpansionDataComplete,
- pExpand);
- if (RT_SUCCESS(rc))
- VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
- else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
- fIoInProgress = true;
- else
- {
- VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
- VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
- VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
- break;
- }
-
- /*
- * Write entry in the BAT.
- */
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
- pImage->uBlockAllocationTableOffset + cBlockAllocationTableEntry * sizeof(uint32_t),
- &pExpand->idxBlockBe,
- sizeof(uint32_t), pIoCtx,
- vhdAsyncExpansionBatUpdateComplete,
- pExpand);
- if (RT_SUCCESS(rc))
- VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
- else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
- fIoInProgress = true;
- else
- {
- VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
- VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
- break;
- }
-
- /*
- * Set the new end of the file and link the new block into the BAT.
- */
- pImage->pBlockAllocationTable[cBlockAllocationTableEntry] = pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE;
- pImage->uCurrentEndOfFile += pImage->cbDataBlockBitmap + pImage->cbDataBlock;
-
- /* Update the footer. */
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
- pImage->uCurrentEndOfFile,
- &pImage->vhdFooterCopy,
- sizeof(VHDFooter), pIoCtx,
- vhdAsyncExpansionFooterUpdateComplete,
- pExpand);
- if (RT_SUCCESS(rc))
- VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
- else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
- fIoInProgress = true;
- else
- {
- VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
- break;
- }
-
- } while (0);
-
- if (!fIoInProgress)
- vhdAsyncExpansionComplete(pImage, pIoCtx, pExpand);
- else
- rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
- }
- else
- {
- /*
- * Calculate the real offset in the file.
- */
- uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
-
- /* Read in the block's bitmap. */
- PVDMETAXFER pMetaXfer;
- rc = vdIfIoIntFileReadMetaAsync(pImage->pIfIo, pImage->pStorage,
- ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
- pImage->pu8Bitmap,
- pImage->cbDataBlockBitmap, pIoCtx,
- &pMetaXfer, NULL, NULL);
- if (RT_SUCCESS(rc))
- {
- vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
-
- /* Write data. */
- rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pImage->pStorage,
- uVhdOffset, pIoCtx, cbWrite,
- NULL, NULL);
- if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
- {
- bool fChanged = false;
-
- /* Set the bits for all sectors having been written. */
- for (uint32_t iSector = 0; iSector < (cbWrite / VHD_SECTOR_SIZE); iSector++)
- {
- fChanged |= vhdBlockBitmapSectorSet(pImage, pImage->pu8Bitmap, cBATEntryIndex);
- cBATEntryIndex++;
- }
-
- /* Only write the bitmap if it was changed. */
- if (fChanged)
- {
- /*
- * Write the bitmap back.
- *
- * @note We don't have a completion callback here because we
- * can't do anything if the write fails for some reason.
- * The error will propagated to the device/guest
- * by the generic VD layer already and we don't need
- * to rollback anything here.
- */
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
- ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
- pImage->pu8Bitmap,
- pImage->cbDataBlockBitmap,
- pIoCtx, NULL, NULL);
- }
- }
- }
- }
- }
- else
- rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pImage->pStorage,
- uOffset, pIoCtx, cbWrite, NULL, NULL);
-
- if (pcbWriteProcess)
- *pcbWriteProcess = cbWrite;
-
- /* Stay on the safe side. Do not run the risk of confusing the higher
- * level, as that can be pretty lethal to image consistency. */
- *pcbPreRead = 0;
- *pcbPostRead = 0;
-
- return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncFlush */
-static int vhdAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
-{
- PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-
- /* No need to write anything here. Data is always updated on a write. */
- return vdIfIoIntFileFlushAsync(pImage->pIfIo, pImage->pStorage,
- pIoCtx, NULL, NULL);
-}
-
/** @copydoc VBOXHDDBACKEND::pfnCompact */
static int vhdCompact(void *pBackendData, unsigned uPercentStart,
unsigned uPercentSpan, PVDINTERFACE pVDIfsDisk,
@@ -2662,7 +2462,7 @@ static int vhdCompact(void *pBackendData, unsigned uPercentStart,
if (pfnParentRead)
{
pvParent = RTMemTmpAlloc(pImage->cbDataBlock);
- AssertBreakStmt(VALID_PTR(pvBuf), rc = VERR_NO_MEMORY);
+ AssertBreakStmt(VALID_PTR(pvParent), rc = VERR_NO_MEMORY);
}
pvBuf = RTMemTmpAlloc(pImage->cbDataBlock);
AssertBreakStmt(VALID_PTR(pvBuf), rc = VERR_NO_MEMORY);
@@ -2727,7 +2527,7 @@ static int vhdCompact(void *pBackendData, unsigned uPercentStart,
/* Block present in image file, read relevant data. */
uint64_t u64Offset = ((uint64_t)paBat[i] + pImage->cDataBlockBitmapSectors) * VHD_SECTOR_SIZE;
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
- u64Offset, pvBuf, pImage->cbDataBlock, NULL);
+ u64Offset, pvBuf, pImage->cbDataBlock);
if (RT_FAILURE(rc))
break;
@@ -2791,14 +2591,14 @@ static int vhdCompact(void *pBackendData, unsigned uPercentStart,
uint64_t u64Offset = (uint64_t)uBlockUsedPos * cbBlock
+ (offBlocksStart * VHD_SECTOR_SIZE);
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
- u64Offset, pvBuf, cbBlock, NULL);
+ u64Offset, pvBuf, cbBlock);
if (RT_FAILURE(rc))
break;
u64Offset = (uint64_t)i * cbBlock
+ (offBlocksStart * VHD_SECTOR_SIZE);
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
- u64Offset, pvBuf, cbBlock, NULL);
+ u64Offset, pvBuf, cbBlock);
if (RT_FAILURE(rc))
break;
@@ -2953,18 +2753,18 @@ static int vhdResize(void *pBackendData, uint64_t cbSize,
{
/* Read data and append to the end of the image. */
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
- offStartDataNew, pvBuf, cbBlock, NULL);
+ offStartDataNew, pvBuf, cbBlock);
if (RT_FAILURE(rc))
break;
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
- pImage->uCurrentEndOfFile, pvBuf, cbBlock, NULL);
+ pImage->uCurrentEndOfFile, pvBuf, cbBlock);
if (RT_FAILURE(rc))
break;
/* Zero out the old block area. */
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
- offStartDataNew, pvZero, cbBlock, NULL);
+ offStartDataNew, pvZero, cbBlock);
if (RT_FAILURE(rc))
break;
@@ -3015,7 +2815,7 @@ static int vhdResize(void *pBackendData, uint64_t cbSize,
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
pImage->uBlockAllocationTableOffset,
pImage->pBlockAllocationTable,
- cBlocksNew * sizeof(uint32_t), NULL);
+ cBlocksNew * sizeof(uint32_t));
}
if (RT_SUCCESS(rc))
@@ -3032,7 +2832,7 @@ static int vhdResize(void *pBackendData, uint64_t cbSize,
/* Update header information in base image file. */
pImage->fDynHdrNeedsUpdate = true;
- vhdFlush(pImage);
+ vhdFlushImage(pImage);
}
/* Same size doesn't change the image at all. */
@@ -3099,7 +2899,7 @@ static DECLCALLBACK(int) vhdRepair(const char *pszFilename, PVDINTERFACE pVDIfsD
}
rc = vdIfIoIntFileReadSync(pIfIo, pStorage, cbFile - sizeof(VHDFooter),
- &vhdFooter, sizeof(VHDFooter), NULL);
+ &vhdFooter, sizeof(VHDFooter));
if (RT_FAILURE(rc))
{
rc = vdIfError(pIfError, rc, RT_SRC_POS, "Failed to read footer of image");
@@ -3110,7 +2910,7 @@ static DECLCALLBACK(int) vhdRepair(const char *pszFilename, PVDINTERFACE pVDIfsD
{
/* Dynamic images have a backup at the beginning of the image. */
rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0,
- &vhdFooter, sizeof(VHDFooter), NULL);
+ &vhdFooter, sizeof(VHDFooter));
if (RT_FAILURE(rc))
{
rc = vdIfError(pIfError, rc, RT_SRC_POS, "Failed to read header of image");
@@ -3185,7 +2985,7 @@ static DECLCALLBACK(int) vhdRepair(const char *pszFilename, PVDINTERFACE pVDIfsD
}
rc = vdIfIoIntFileReadSync(pIfIo, pStorage, offDynamicDiskHeader,
- &dynamicDiskHeader, sizeof(VHDDynamicDiskHeader), NULL);
+ &dynamicDiskHeader, sizeof(VHDDynamicDiskHeader));
if (RT_FAILURE(rc))
{
rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
@@ -3232,7 +3032,7 @@ static DECLCALLBACK(int) vhdRepair(const char *pszFilename, PVDINTERFACE pVDIfsD
}
rc = vdIfIoIntFileReadSync(pIfIo, pStorage, offBat, paBat,
- cBatEntries * sizeof(uint32_t), NULL);
+ cBatEntries * sizeof(uint32_t));
if (RT_FAILURE(rc))
{
rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
@@ -3285,7 +3085,7 @@ static DECLCALLBACK(int) vhdRepair(const char *pszFilename, PVDINTERFACE pVDIfsD
}
if ( paBat[i] != UINT32_C(0xffffffff)
- && ASMBitTestAndSet(pu32BlockBitmap, (paBat[i] - idxMinBlock) / (cbBlock / VHD_SECTOR_SIZE)))
+ && ASMBitTestAndSet(pu32BlockBitmap, (uint32_t)((paBat[i] - idxMinBlock) / (cbBlock / VHD_SECTOR_SIZE))))
{
vdIfErrorMessage(pIfError, "Entry %u points to an already referenced data block, clearing\n",
i);
@@ -3309,7 +3109,7 @@ static DECLCALLBACK(int) vhdRepair(const char *pszFilename, PVDINTERFACE pVDIfsD
vdIfErrorMessage(pIfError, "Writing repaired block allocation table...\n");
rc = vdIfIoIntFileWriteSync(pIfIo, pStorage, offBat, paBat,
- cBatEntries * sizeof(uint32_t), NULL);
+ cBatEntries * sizeof(uint32_t));
if (RT_FAILURE(rc))
{
rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
@@ -3325,7 +3125,7 @@ static DECLCALLBACK(int) vhdRepair(const char *pszFilename, PVDINTERFACE pVDIfsD
vdIfErrorMessage(pIfError, "Writing repaired dynamic disk header...\n");
rc = vdIfIoIntFileWriteSync(pIfIo, pStorage, offDynamicDiskHeader, &dynamicDiskHeader,
- sizeof(VHDDynamicDiskHeader), NULL);
+ sizeof(VHDDynamicDiskHeader));
if (RT_FAILURE(rc))
{
rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
@@ -3343,7 +3143,7 @@ static DECLCALLBACK(int) vhdRepair(const char *pszFilename, PVDINTERFACE pVDIfsD
{
/* Write backup at image beginning. */
rc = vdIfIoIntFileWriteSync(pIfIo, pStorage, 0, &vhdFooter,
- sizeof(VHDFooter), NULL);
+ sizeof(VHDFooter));
if (RT_FAILURE(rc))
{
rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
@@ -3354,7 +3154,7 @@ static DECLCALLBACK(int) vhdRepair(const char *pszFilename, PVDINTERFACE pVDIfsD
}
rc = vdIfIoIntFileWriteSync(pIfIo, pStorage, offFooter, &vhdFooter,
- sizeof(VHDFooter), NULL);
+ sizeof(VHDFooter));
if (RT_FAILURE(rc))
{
rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
@@ -3375,7 +3175,11 @@ static DECLCALLBACK(int) vhdRepair(const char *pszFilename, PVDINTERFACE pVDIfsD
RTMemFree(pu32BlockBitmap);
if (pStorage)
- vdIfIoIntFileClose(pIfIo, pStorage);
+ {
+ int rc2 = vdIfIoIntFileClose(pIfIo, pStorage);
+ if (RT_SUCCESS(rc))
+ rc = rc2; /* Propagate status code only when repairing the image was successful. */
+ }
LogFlowFunc(("returns %Rrc\n", rc));
return rc;
@@ -3414,8 +3218,12 @@ VBOXHDDBACKEND g_VhdBackend =
vhdWrite,
/* pfnFlush */
vhdFlush,
+ /* pfnDiscard */
+ NULL,
/* pfnGetVersion */
vhdGetVersion,
+ /* pfnGetSectorSize */
+ vhdGetSectorSize,
/* pfnGetSize */
vhdGetSize,
/* pfnGetFileSize */
@@ -3466,12 +3274,6 @@ VBOXHDDBACKEND g_VhdBackend =
vhdGetParentFilename,
/* pfnSetParentFilename */
vhdSetParentFilename,
- /* pfnAsyncRead */
- vhdAsyncRead,
- /* pfnAsyncWrite */
- vhdAsyncWrite,
- /* pfnAsyncFlush */
- vhdAsyncFlush,
/* pfnComposeLocation */
genericFileComposeLocation,
/* pfnComposeName */
@@ -3480,10 +3282,6 @@ VBOXHDDBACKEND g_VhdBackend =
vhdCompact,
/* pfnResize */
vhdResize,
- /* pfnDiscard */
- NULL,
- /* pfnAsyncDiscard */
- NULL,
/* pfnRepair */
vhdRepair
};
diff --git a/src/VBox/Storage/VHDX.cpp b/src/VBox/Storage/VHDX.cpp
index 035f3d04..35d99e8d 100644
--- a/src/VBox/Storage/VHDX.cpp
+++ b/src/VBox/Storage/VHDX.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2012 Oracle Corporation
+ * Copyright (C) 2012-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -510,40 +510,40 @@ typedef struct VHDXMETADATAITEMPROPS
typedef struct VHDXIMAGE
{
/** Image name. */
- const char *pszFilename;
+ const char *pszFilename;
/** Storage handle. */
- PVDIOSTORAGE pStorage;
+ PVDIOSTORAGE pStorage;
/** Pointer to the per-disk VD interface list. */
- PVDINTERFACE pVDIfsDisk;
+ PVDINTERFACE pVDIfsDisk;
/** Pointer to the per-image VD interface list. */
- PVDINTERFACE pVDIfsImage;
+ PVDINTERFACE pVDIfsImage;
/** Error interface. */
- PVDINTERFACEERROR pIfError;
+ PVDINTERFACEERROR pIfError;
/** I/O interface. */
- PVDINTERFACEIOINT pIfIo;
+ PVDINTERFACEIOINT pIfIo;
/** Open flags passed by VBoxHD layer. */
- unsigned uOpenFlags;
+ unsigned uOpenFlags;
/** Image flags defined during creation or determined during open. */
- unsigned uImageFlags;
+ unsigned uImageFlags;
/** Version of the VHDX image format. */
- unsigned uVersion;
+ unsigned uVersion;
/** Total size of the image. */
- uint64_t cbSize;
+ uint64_t cbSize;
/** Logical sector size of the image. */
- size_t cbLogicalSector;
+ uint32_t cbLogicalSector;
/** Block size of the image. */
- size_t cbBlock;
+ size_t cbBlock;
/** Physical geometry of this image. */
- VDGEOMETRY PCHSGeometry;
+ VDGEOMETRY PCHSGeometry;
/** Logical geometry of this image. */
- VDGEOMETRY LCHSGeometry;
+ VDGEOMETRY LCHSGeometry;
/** The BAT. */
- PVhdxBatEntry paBat;
+ PVhdxBatEntry paBat;
/** Chunk ratio. */
- uint32_t uChunkRatio;
+ uint32_t uChunkRatio;
} VHDXIMAGE, *PVHDXIMAGE;
@@ -586,7 +586,7 @@ static const VHDXMETADATAITEMPROPS s_aVhdxMetadataItemProps[] =
{VHDX_METADATA_TBL_ENTRY_ITEM_VDISK_SIZE, false, true, true, VHDXMETADATAITEM_VDISK_SIZE},
{VHDX_METADATA_TBL_ENTRY_ITEM_PAGE83_DATA, false, true, true, VHDXMETADATAITEM_PAGE83_DATA},
{VHDX_METADATA_TBL_ENTRY_ITEM_LOG_SECT_SIZE, false, true, true, VHDXMETADATAITEM_LOGICAL_SECTOR_SIZE},
- {VHDX_METADATA_TBL_ENTRY_ITEM_PHYS_SECT_SIZE, false, true, false, VHDXMETADATAITEM_PHYSICAL_SECTOR_SIZE},
+ {VHDX_METADATA_TBL_ENTRY_ITEM_PHYS_SECT_SIZE, false, true, true, VHDXMETADATAITEM_PHYSICAL_SECTOR_SIZE},
{VHDX_METADATA_TBL_ENTRY_ITEM_PARENT_LOCATOR, false, false, true, VHDXMETADATAITEM_PARENT_LOCATOR}
};
@@ -970,7 +970,7 @@ static int vhdxFreeImage(PVHDXIMAGE pImage, bool fDelete)
{
if (pImage->pStorage)
{
- vdIfIoIntFileClose(pImage->pIfIo, pImage->pStorage);
+ rc = vdIfIoIntFileClose(pImage->pIfIo, pImage->pStorage);
pImage->pStorage = NULL;
}
@@ -1055,7 +1055,7 @@ static int vhdxFindAndLoadCurrentHeader(PVHDXIMAGE pImage)
{
/* Read the first header. */
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, VHDX_HEADER1_OFFSET,
- pHdr1, sizeof(*pHdr1), NULL);
+ pHdr1, sizeof(*pHdr1));
if (RT_SUCCESS(rc))
{
vhdxConvHeaderEndianess(VHDXECONV_F2H, pHdr1, pHdr1);
@@ -1063,7 +1063,7 @@ static int vhdxFindAndLoadCurrentHeader(PVHDXIMAGE pImage)
/* Validate checksum. */
u32ChkSumSaved = pHdr1->u32Checksum;
pHdr1->u32Checksum = 0;
- //u32ChkSum = RTCrc32C(pHdr1, sizeof(*pHdr1));
+ //u32ChkSum = RTCrc32C(pHdr1, RT_OFFSETOF(VhdxHeader, u8Reserved[502]));
if ( pHdr1->u32Signature == VHDX_HEADER_SIGNATURE
/*&& u32ChkSum == u32ChkSumSaved*/)
@@ -1072,7 +1072,7 @@ static int vhdxFindAndLoadCurrentHeader(PVHDXIMAGE pImage)
/* Try to read the second header in any case (even if reading the first failed). */
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, VHDX_HEADER2_OFFSET,
- pHdr2, sizeof(*pHdr2), NULL);
+ pHdr2, sizeof(*pHdr2));
if (RT_SUCCESS(rc))
{
vhdxConvHeaderEndianess(VHDXECONV_F2H, pHdr2, pHdr2);
@@ -1080,7 +1080,7 @@ static int vhdxFindAndLoadCurrentHeader(PVHDXIMAGE pImage)
/* Validate checksum. */
u32ChkSumSaved = pHdr2->u32Checksum;
pHdr2->u32Checksum = 0;
- //u32ChkSum = RTCrc32C(pHdr2, sizeof(*pHdr2));
+ //u32ChkSum = RTCrc32C(pHdr2, RT_OFFSETOF(VhdxHeader, u8Reserved[502]));
if ( pHdr2->u32Signature == VHDX_HEADER_SIGNATURE
/*&& u32ChkSum == u32ChkSumSaved*/)
@@ -1143,8 +1143,11 @@ static int vhdxLoadBatRegion(PVHDXIMAGE pImage, uint64_t offRegion,
LogFlowFunc(("pImage=%#p\n", pImage));
/* Calculate required values first. */
- uChunkRatio = (RT_BIT_64(23) * pImage->cbLogicalSector) / pImage->cbBlock;
- cDataBlocks = pImage->cbSize / pImage->cbBlock;
+ uint64_t uChunkRatio64 = (RT_BIT_64(23) * pImage->cbLogicalSector) / pImage->cbBlock;
+ uChunkRatio = (uint32_t)uChunkRatio64; Assert(uChunkRatio == uChunkRatio64);
+ uint64_t cDataBlocks64 = pImage->cbSize / pImage->cbBlock;
+ cDataBlocks = (uint32_t)cDataBlocks64; Assert(cDataBlocks == cDataBlocks64);
+
if (pImage->cbSize % pImage->cbBlock)
cDataBlocks++;
@@ -1165,7 +1168,7 @@ static int vhdxLoadBatRegion(PVHDXIMAGE pImage, uint64_t offRegion,
if (paBatEntries)
{
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, offRegion,
- paBatEntries, cbBatEntries, NULL);
+ paBatEntries, cbBatEntries);
if (RT_SUCCESS(rc))
{
vhdxConvBatTableEndianess(VHDXECONV_F2H, paBatEntries, paBatEntries,
@@ -1177,6 +1180,12 @@ static int vhdxLoadBatRegion(PVHDXIMAGE pImage, uint64_t offRegion,
if ( i != 0
&& (i % uChunkRatio) == 0)
{
+/**
+ * Disabled the verification because there are images out there with the sector bitmap
+ * marked as present. The entry is never accessed and the image is readonly anyway,
+ * so no harm done.
+ */
+#if 0
/* Sector bitmap block. */
if ( VHDX_BAT_ENTRY_GET_STATE(paBatEntries[i].u64BatEntry)
!= VHDX_BAT_ENTRY_SB_BLOCK_NOT_PRESENT)
@@ -1186,6 +1195,7 @@ static int vhdxLoadBatRegion(PVHDXIMAGE pImage, uint64_t offRegion,
i, pImage->pszFilename);
break;
}
+#endif
}
else
{
@@ -1253,7 +1263,7 @@ static int vhdxLoadFileParametersMetadata(PVHDXIMAGE pImage, uint64_t offItem, s
VhdxFileParameters FileParameters;
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, offItem,
- &FileParameters, sizeof(FileParameters), NULL);
+ &FileParameters, sizeof(FileParameters));
if (RT_SUCCESS(rc))
{
vhdxConvFileParamsEndianess(VHDXECONV_F2H, &FileParameters, &FileParameters);
@@ -1298,7 +1308,7 @@ static int vhdxLoadVDiskSizeMetadata(PVHDXIMAGE pImage, uint64_t offItem, size_t
VhdxVDiskSize VDiskSize;
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, offItem,
- &VDiskSize, sizeof(VDiskSize), NULL);
+ &VDiskSize, sizeof(VDiskSize));
if (RT_SUCCESS(rc))
{
vhdxConvVDiskSizeEndianess(VHDXECONV_F2H, &VDiskSize, &VDiskSize);
@@ -1337,7 +1347,7 @@ static int vhdxLoadVDiskLogSectorSizeMetadata(PVHDXIMAGE pImage, uint64_t offIte
VhdxVDiskLogicalSectorSize VDiskLogSectSize;
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, offItem,
- &VDiskLogSectSize, sizeof(VDiskLogSectSize), NULL);
+ &VDiskLogSectSize, sizeof(VDiskLogSectSize));
if (RT_SUCCESS(rc))
{
vhdxConvVDiskLogSectSizeEndianess(VHDXECONV_F2H, &VDiskLogSectSize,
@@ -1372,7 +1382,7 @@ static int vhdxLoadMetadataRegion(PVHDXIMAGE pImage, uint64_t offRegion,
/* Load the header first. */
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, offRegion,
- &MetadataTblHdr, sizeof(MetadataTblHdr), NULL);
+ &MetadataTblHdr, sizeof(MetadataTblHdr));
if (RT_SUCCESS(rc))
{
vhdxConvMetadataTblHdrEndianess(VHDXECONV_F2H, &MetadataTblHdr, &MetadataTblHdr);
@@ -1402,7 +1412,7 @@ static int vhdxLoadMetadataRegion(PVHDXIMAGE pImage, uint64_t offRegion,
VhdxMetadataTblEntry MetadataTblEntry;
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, offMetadataTblEntry,
- &MetadataTblEntry, sizeof(MetadataTblEntry), NULL);
+ &MetadataTblEntry, sizeof(MetadataTblEntry));
if (RT_FAILURE(rc))
{
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
@@ -1419,6 +1429,12 @@ static int vhdxLoadMetadataRegion(PVHDXIMAGE pImage, uint64_t offRegion,
if (!RTUuidCompareStr(&MetadataTblEntry.UuidItem,
s_aVhdxMetadataItemProps[idxProp].pszItemUuid))
{
+ /*
+ * Check for specification violations and bail out, except
+ * for the required flag of the physical sector size metadata item.
+ * Early images had the required flag not set opposed to the specification.
+ * We don't want to brerak those images.
+ */
if ( !!(MetadataTblEntry.u32Flags & VHDX_METADATA_TBL_ENTRY_FLAGS_IS_USER)
!= s_aVhdxMetadataItemProps[idxProp].fIsUser)
rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
@@ -1429,8 +1445,9 @@ static int vhdxLoadMetadataRegion(PVHDXIMAGE pImage, uint64_t offRegion,
rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
"VHDX: Virtual disk flag of metadata item does not meet expectations \'%s\'",
pImage->pszFilename);
- else if ( !!(MetadataTblEntry.u32Flags & VHDX_METADATA_TBL_ENTRY_FLAGS_IS_REQUIRED)
- != s_aVhdxMetadataItemProps[idxProp].fIsRequired)
+ else if ( !!(MetadataTblEntry.u32Flags & VHDX_METADATA_TBL_ENTRY_FLAGS_IS_REQUIRED)
+ != s_aVhdxMetadataItemProps[idxProp].fIsRequired
+ && (s_aVhdxMetadataItemProps[idxProp].enmMetadataItem != VHDXMETADATAITEM_PHYSICAL_SECTOR_SIZE))
rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
"VHDX: Required flag of metadata item does not meet expectations \'%s\'",
pImage->pszFilename);
@@ -1533,7 +1550,7 @@ static int vhdxLoadRegionTable(PVHDXIMAGE pImage)
if (pbRegionTbl)
{
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, VHDX_REGION_TBL_HDR_OFFSET,
- pbRegionTbl, VHDX_REGION_TBL_SIZE_MAX, NULL);
+ pbRegionTbl, VHDX_REGION_TBL_SIZE_MAX);
if (RT_SUCCESS(rc))
{
PVhdxRegionTblHdr pRegionTblHdr;
@@ -1574,8 +1591,9 @@ static int vhdxLoadRegionTable(PVHDXIMAGE pImage)
{
/* Parse the region table entries. */
PVhdxRegionTblEntry pRegTblEntry = (PVhdxRegionTblEntry)(pbRegionTbl + sizeof(VhdxRegionTblHdr));
- VhdxRegionTblEntry RegTblEntryBat; /**<< BAT region table entry. */
+ VhdxRegionTblEntry RegTblEntryBat; /* BAT region table entry. */
bool fBatRegPresent = false;
+ RT_ZERO(RegTblEntryBat); /* Maybe uninitialized, gcc. */
for (unsigned i = 0; i < RegionTblHdr.u32EntryCount; i++)
{
@@ -1663,11 +1681,9 @@ static int vhdxOpenImage(PVHDXIMAGE pImage, unsigned uOpenFlags)
pImage->pIfIo = VDIfIoIntGet(pImage->pVDIfsImage);
AssertPtrReturn(pImage->pIfIo, VERR_INVALID_PARAMETER);
-#if 0
/* Refuse write access, it is not implemented so far. */
if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
return VERR_NOT_SUPPORTED;
-#endif
/*
* Open the image.
@@ -1687,7 +1703,7 @@ static int vhdxOpenImage(PVHDXIMAGE pImage, unsigned uOpenFlags)
if (cbFile > sizeof(FileIdentifier))
{
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, VHDX_FILE_IDENTIFIER_OFFSET,
- &FileIdentifier, sizeof(FileIdentifier), NULL);
+ &FileIdentifier, sizeof(FileIdentifier));
if (RT_SUCCESS(rc))
{
vhdxConvFileIdentifierEndianess(VHDXECONV_F2H, &FileIdentifier,
@@ -1747,7 +1763,7 @@ static int vhdxCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
if (cbFile > sizeof(FileIdentifier))
{
rc = vdIfIoIntFileReadSync(pIfIo, pStorage, VHDX_FILE_IDENTIFIER_OFFSET,
- &FileIdentifier, sizeof(FileIdentifier), NULL);
+ &FileIdentifier, sizeof(FileIdentifier));
if (RT_SUCCESS(rc))
{
vhdxConvFileIdentifierEndianess(VHDXECONV_F2H, &FileIdentifier,
@@ -1865,10 +1881,11 @@ static int vhdxClose(void *pBackendData, bool fDelete)
}
/** @copydoc VBOXHDDBACKEND::pfnRead */
-static int vhdxRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
- size_t cbToRead, size_t *pcbActuallyRead)
+static int vhdxRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
+ PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
+ LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
+ pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
int rc = VINF_SUCCESS;
@@ -1881,7 +1898,7 @@ static int vhdxRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
rc = VERR_INVALID_PARAMETER;
else
{
- uint32_t idxBat = uOffset / pImage->cbBlock;
+ uint32_t idxBat = (uint32_t)(uOffset / pImage->cbBlock); Assert(idxBat == uOffset / pImage->cbBlock);
uint32_t offRead = uOffset % pImage->cbBlock;
uint64_t uBatEntry;
@@ -1897,14 +1914,14 @@ static int vhdxRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
case VHDX_BAT_ENTRY_PAYLOAD_BLOCK_ZERO:
case VHDX_BAT_ENTRY_PAYLOAD_BLOCK_UNMAPPED:
{
- memset(pvBuf, 0, cbToRead);
+ vdIfIoIntIoCtxSet(pImage->pIfIo, pIoCtx, 0, cbToRead);
break;
}
case VHDX_BAT_ENTRY_PAYLOAD_BLOCK_FULLY_PRESENT:
{
uint64_t offFile = VHDX_BAT_ENTRY_GET_FILE_OFFSET(uBatEntry) + offRead;
- rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, offFile,
- pvBuf, cbToRead, NULL);
+ rc = vdIfIoIntFileReadUser(pImage->pIfIo, pImage->pStorage, offFile,
+ pIoCtx, cbToRead);
break;
}
case VHDX_BAT_ENTRY_PAYLOAD_BLOCK_PARTIALLY_PRESENT:
@@ -1922,12 +1939,12 @@ static int vhdxRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
}
/** @copydoc VBOXHDDBACKEND::pfnWrite */
-static int vhdxWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
- size_t cbToWrite, size_t *pcbWriteProcess,
- size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
+static int vhdxWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
+ PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
+ size_t *pcbPostRead, unsigned fWrite)
{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
- pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
+ LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
+ pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
int rc;
@@ -1948,9 +1965,9 @@ static int vhdxWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
}
/** @copydoc VBOXHDDBACKEND::pfnFlush */
-static int vhdxFlush(void *pBackendData)
+static int vhdxFlush(void *pBackendData, PVDIOCTX pIoCtx)
{
- LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+ LogFlowFunc(("pBackendData=%#p pIoCtx=%#p\n", pBackendData, pIoCtx));
PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
int rc;
@@ -1977,6 +1994,22 @@ static unsigned vhdxGetVersion(void *pBackendData)
return 0;
}
+/** @copydoc VBOXHDDBACKEND::pfnGetSectorSize */
+static uint32_t vhdxGetSectorSize(void *pBackendData)
+{
+ LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+ PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
+ uint32_t cb = 0;
+
+ AssertPtr(pImage);
+
+ if (pImage && pImage->pStorage)
+ cb = pImage->cbLogicalSector;
+
+ LogFlowFunc(("returns %u\n", cb));
+ return cb;
+}
+
/** @copydoc VBOXHDDBACKEND::pfnGetSize */
static uint64_t vhdxGetSize(void *pBackendData)
{
@@ -2064,7 +2097,6 @@ static int vhdxSetPCHSGeometry(void *pBackendData,
else
rc = VERR_VD_NOT_OPENED;
-out:
LogFlowFunc(("returns %Rrc\n", rc));
return rc;
}
@@ -2161,7 +2193,7 @@ static int vhdxSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
int rc = VINF_SUCCESS;
/* Image must be opened and the new flags must be valid. */
- if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO)))
+ if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
rc = VERR_INVALID_PARAMETER;
else
{
@@ -2390,7 +2422,7 @@ static void vhdxDump(void *pBackendData)
AssertPtr(pImage);
if (pImage)
{
- vdIfErrorMessage(pImage->pIfError, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cbSector=%zu\n",
+ vdIfErrorMessage(pImage->pIfError, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cbSector=%u\n",
pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors,
pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors,
pImage->cbLogicalSector);
@@ -2428,8 +2460,12 @@ VBOXHDDBACKEND g_VhdxBackend =
vhdxWrite,
/* pfnFlush */
vhdxFlush,
+ /* pfnDiscard */
+ NULL,
/* pfnGetVersion */
vhdxGetVersion,
+ /* pfnGetSectorSize */
+ vhdxGetSectorSize,
/* pfnGetSize */
vhdxGetSize,
/* pfnGetFileSize */
@@ -2480,12 +2516,6 @@ VBOXHDDBACKEND g_VhdxBackend =
NULL,
/* pfnSetParentFilename */
NULL,
- /* pfnAsyncRead */
- NULL,
- /* pfnAsyncWrite */
- NULL,
- /* pfnAsyncFlush */
- NULL,
/* pfnComposeLocation */
genericFileComposeLocation,
/* pfnComposeName */
@@ -2494,10 +2524,6 @@ VBOXHDDBACKEND g_VhdxBackend =
NULL,
/* pfnResize */
NULL,
- /* pfnDiscard */
- NULL,
- /* pfnAsyncDiscard */
- NULL,
/* pfnRepair */
NULL
};
diff --git a/src/VBox/Storage/VMDK.cpp b/src/VBox/Storage/VMDK.cpp
index 19d83a2c..edc6733e 100644
--- a/src/VBox/Storage/VMDK.cpp
+++ b/src/VBox/Storage/VMDK.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2011 Oracle Corporation
+ * Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -232,8 +232,6 @@ typedef struct VMDKFILE
const char *pszFilename;
/** File open flags for consistency checking. */
unsigned fOpen;
- /** Flag whether this file has been opened for async I/O. */
- bool fAsyncIO;
/** Handle for sync/async file abstraction.*/
PVDIOSTORAGE pStorage;
/** Reference counter. */
@@ -525,22 +523,23 @@ static const VDFILEEXTENSION s_aVmdkFileExtensions[] =
*******************************************************************************/
static void vmdkFreeStreamBuffers(PVMDKEXTENT pExtent);
-static void vmdkFreeExtentData(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
- bool fDelete);
+static int vmdkFreeExtentData(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
+ bool fDelete);
static int vmdkCreateExtents(PVMDKIMAGE pImage, unsigned cExtents);
-static int vmdkFlushImage(PVMDKIMAGE pImage);
+static int vmdkFlushImage(PVMDKIMAGE pImage, PVDIOCTX pIoCtx);
static int vmdkSetImageComment(PVMDKIMAGE pImage, const char *pszComment);
static int vmdkFreeImage(PVMDKIMAGE pImage, bool fDelete);
-static int vmdkAllocGrainAsyncComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq);
+static int vmdkAllocGrainComplete(void *pBackendData, PVDIOCTX pIoCtx,
+ void *pvUser, int rcReq);
/**
* Internal: open a file (using a file descriptor cache to ensure each file
* is only opened once - anything else can cause locking problems).
*/
static int vmdkFileOpen(PVMDKIMAGE pImage, PVMDKFILE *ppVmdkFile,
- const char *pszFilename, uint32_t fOpen, bool fAsyncIO)
+ const char *pszFilename, uint32_t fOpen)
{
int rc = VINF_SUCCESS;
PVMDKFILE pVmdkFile;
@@ -576,7 +575,6 @@ static int vmdkFileOpen(PVMDKIMAGE pImage, PVMDKFILE *ppVmdkFile,
return VERR_NO_MEMORY;
}
pVmdkFile->fOpen = fOpen;
- pVmdkFile->fAsyncIO = fAsyncIO;
rc = vdIfIoIntFileOpen(pImage->pIfIo, pszFilename, fOpen,
&pVmdkFile->pStorage);
@@ -679,79 +677,75 @@ DECLINLINE(int) vmdkFileInflateSync(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
size_t cbToRead, const void *pcvMarker,
uint64_t *puLBA, uint32_t *pcbMarkerData)
{
- if (pExtent->pFile->fAsyncIO)
+ int rc;
+ PRTZIPDECOMP pZip = NULL;
+ VMDKMARKER *pMarker = (VMDKMARKER *)pExtent->pvCompGrain;
+ size_t cbCompSize, cbActuallyRead;
+
+ if (!pcvMarker)
{
- AssertMsgFailed(("TODO\n"));
- return VERR_NOT_SUPPORTED;
+ rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
+ uOffset, pMarker, RT_OFFSETOF(VMDKMARKER, uType));
+ if (RT_FAILURE(rc))
+ return rc;
}
else
{
- int rc;
- PRTZIPDECOMP pZip = NULL;
- VMDKMARKER *pMarker = (VMDKMARKER *)pExtent->pvCompGrain;
- size_t cbCompSize, cbActuallyRead;
-
- if (!pcvMarker)
- {
- rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
- uOffset, pMarker, RT_OFFSETOF(VMDKMARKER, uType),
- NULL);
- if (RT_FAILURE(rc))
- return rc;
- }
- else
- memcpy(pMarker, pcvMarker, RT_OFFSETOF(VMDKMARKER, uType));
+ memcpy(pMarker, pcvMarker, RT_OFFSETOF(VMDKMARKER, uType));
+ /* pcvMarker endianness has already been partially transformed, fix it */
+ pMarker->uSector = RT_H2LE_U64(pMarker->uSector);
+ pMarker->cbSize = RT_H2LE_U32(pMarker->cbSize);
+ }
- cbCompSize = RT_LE2H_U32(pMarker->cbSize);
- if (cbCompSize == 0)
- {
- AssertMsgFailed(("VMDK: corrupted marker\n"));
- return VERR_VD_VMDK_INVALID_FORMAT;
- }
+ cbCompSize = RT_LE2H_U32(pMarker->cbSize);
+ if (cbCompSize == 0)
+ {
+ AssertMsgFailed(("VMDK: corrupted marker\n"));
+ return VERR_VD_VMDK_INVALID_FORMAT;
+ }
- /* Sanity check - the expansion ratio should be much less than 2. */
- Assert(cbCompSize < 2 * cbToRead);
- if (cbCompSize >= 2 * cbToRead)
- return VERR_VD_VMDK_INVALID_FORMAT;
+ /* Sanity check - the expansion ratio should be much less than 2. */
+ Assert(cbCompSize < 2 * cbToRead);
+ if (cbCompSize >= 2 * cbToRead)
+ return VERR_VD_VMDK_INVALID_FORMAT;
- /* Compressed grain marker. Data follows immediately. */
- rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
- uOffset + RT_OFFSETOF(VMDKMARKER, uType),
- (uint8_t *)pExtent->pvCompGrain
+ /* Compressed grain marker. Data follows immediately. */
+ rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
+ uOffset + RT_OFFSETOF(VMDKMARKER, uType),
+ (uint8_t *)pExtent->pvCompGrain
+ + RT_OFFSETOF(VMDKMARKER, uType),
+ RT_ALIGN_Z( cbCompSize
+ + RT_OFFSETOF(VMDKMARKER, uType),
+ 512)
+ - RT_OFFSETOF(VMDKMARKER, uType));
+
+ if (puLBA)
+ *puLBA = RT_LE2H_U64(pMarker->uSector);
+ if (pcbMarkerData)
+ *pcbMarkerData = RT_ALIGN( cbCompSize
+ RT_OFFSETOF(VMDKMARKER, uType),
- RT_ALIGN_Z( cbCompSize
- + RT_OFFSETOF(VMDKMARKER, uType),
- 512)
- - RT_OFFSETOF(VMDKMARKER, uType), NULL);
-
- if (puLBA)
- *puLBA = RT_LE2H_U64(pMarker->uSector);
- if (pcbMarkerData)
- *pcbMarkerData = RT_ALIGN( cbCompSize
- + RT_OFFSETOF(VMDKMARKER, uType),
- 512);
+ 512);
- VMDKCOMPRESSIO InflateState;
- InflateState.pImage = pImage;
- InflateState.iOffset = -1;
- InflateState.cbCompGrain = cbCompSize + RT_OFFSETOF(VMDKMARKER, uType);
- InflateState.pvCompGrain = pExtent->pvCompGrain;
+ VMDKCOMPRESSIO InflateState;
+ InflateState.pImage = pImage;
+ InflateState.iOffset = -1;
+ InflateState.cbCompGrain = cbCompSize + RT_OFFSETOF(VMDKMARKER, uType);
+ InflateState.pvCompGrain = pExtent->pvCompGrain;
- rc = RTZipDecompCreate(&pZip, &InflateState, vmdkFileInflateHelper);
- if (RT_FAILURE(rc))
- return rc;
- rc = RTZipDecompress(pZip, pvBuf, cbToRead, &cbActuallyRead);
- RTZipDecompDestroy(pZip);
- if (RT_FAILURE(rc))
- {
- if (rc == VERR_ZIP_CORRUPTED)
- rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: Compressed image is corrupted '%s'"), pExtent->pszFullname);
- return rc;
- }
- if (cbActuallyRead != cbToRead)
- rc = VERR_VD_VMDK_INVALID_FORMAT;
+ rc = RTZipDecompCreate(&pZip, &InflateState, vmdkFileInflateHelper);
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = RTZipDecompress(pZip, pvBuf, cbToRead, &cbActuallyRead);
+ RTZipDecompDestroy(pZip);
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_ZIP_CORRUPTED)
+ rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: Compressed image is corrupted '%s'"), pExtent->pszFullname);
return rc;
}
+ if (cbActuallyRead != cbToRead)
+ rc = VERR_VD_VMDK_INVALID_FORMAT;
+ return rc;
}
static DECLCALLBACK(int) vmdkFileDeflateHelper(void *pvUser, const void *pvBuf, size_t cbBuf)
@@ -784,60 +778,52 @@ DECLINLINE(int) vmdkFileDeflateSync(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
size_t cbToWrite, uint64_t uLBA,
uint32_t *pcbMarkerData)
{
- if (pExtent->pFile->fAsyncIO)
- {
- AssertMsgFailed(("TODO\n"));
- return VERR_NOT_SUPPORTED;
- }
- else
- {
- int rc;
- PRTZIPCOMP pZip = NULL;
- VMDKCOMPRESSIO DeflateState;
+ int rc;
+ PRTZIPCOMP pZip = NULL;
+ VMDKCOMPRESSIO DeflateState;
- DeflateState.pImage = pImage;
- DeflateState.iOffset = -1;
- DeflateState.cbCompGrain = pExtent->cbCompGrain;
- DeflateState.pvCompGrain = pExtent->pvCompGrain;
+ DeflateState.pImage = pImage;
+ DeflateState.iOffset = -1;
+ DeflateState.cbCompGrain = pExtent->cbCompGrain;
+ DeflateState.pvCompGrain = pExtent->pvCompGrain;
- rc = RTZipCompCreate(&pZip, &DeflateState, vmdkFileDeflateHelper,
- RTZIPTYPE_ZLIB, RTZIPLEVEL_DEFAULT);
- if (RT_FAILURE(rc))
- return rc;
- rc = RTZipCompress(pZip, pvBuf, cbToWrite);
- if (RT_SUCCESS(rc))
- rc = RTZipCompFinish(pZip);
- RTZipCompDestroy(pZip);
- if (RT_SUCCESS(rc))
- {
- Assert( DeflateState.iOffset > 0
- && (size_t)DeflateState.iOffset <= DeflateState.cbCompGrain);
+ rc = RTZipCompCreate(&pZip, &DeflateState, vmdkFileDeflateHelper,
+ RTZIPTYPE_ZLIB, RTZIPLEVEL_DEFAULT);
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = RTZipCompress(pZip, pvBuf, cbToWrite);
+ if (RT_SUCCESS(rc))
+ rc = RTZipCompFinish(pZip);
+ RTZipCompDestroy(pZip);
+ if (RT_SUCCESS(rc))
+ {
+ Assert( DeflateState.iOffset > 0
+ && (size_t)DeflateState.iOffset <= DeflateState.cbCompGrain);
- /* pad with zeroes to get to a full sector size */
- uint32_t uSize = DeflateState.iOffset;
- if (uSize % 512)
- {
- uint32_t uSizeAlign = RT_ALIGN(uSize, 512);
- memset((uint8_t *)pExtent->pvCompGrain + uSize, '\0',
- uSizeAlign - uSize);
- uSize = uSizeAlign;
- }
+ /* pad with zeroes to get to a full sector size */
+ uint32_t uSize = DeflateState.iOffset;
+ if (uSize % 512)
+ {
+ uint32_t uSizeAlign = RT_ALIGN(uSize, 512);
+ memset((uint8_t *)pExtent->pvCompGrain + uSize, '\0',
+ uSizeAlign - uSize);
+ uSize = uSizeAlign;
+ }
- if (pcbMarkerData)
- *pcbMarkerData = uSize;
+ if (pcbMarkerData)
+ *pcbMarkerData = uSize;
- /* Compressed grain marker. Data follows immediately. */
- VMDKMARKER *pMarker = (VMDKMARKER *)pExtent->pvCompGrain;
- pMarker->uSector = RT_H2LE_U64(uLBA);
- pMarker->cbSize = RT_H2LE_U32( DeflateState.iOffset
- - RT_OFFSETOF(VMDKMARKER, uType));
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
- uOffset, pMarker, uSize, NULL);
- if (RT_FAILURE(rc))
- return rc;
- }
- return rc;
+ /* Compressed grain marker. Data follows immediately. */
+ VMDKMARKER *pMarker = (VMDKMARKER *)pExtent->pvCompGrain;
+ pMarker->uSector = RT_H2LE_U64(uLBA);
+ pMarker->cbSize = RT_H2LE_U32( DeflateState.iOffset
+ - RT_OFFSETOF(VMDKMARKER, uType));
+ rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
+ uOffset, pMarker, uSize);
+ if (RT_FAILURE(rc))
+ return rc;
}
+ return rc;
}
@@ -1048,7 +1034,7 @@ out:
static int vmdkReadGrainDirectory(PVMDKIMAGE pImage, PVMDKEXTENT pExtent)
{
int rc = VINF_SUCCESS;
- unsigned i;
+ size_t i;
uint32_t *pGDTmp, *pRGDTmp;
size_t cbGD = pExtent->cGDEntries * sizeof(uint32_t);
@@ -1070,7 +1056,7 @@ static int vmdkReadGrainDirectory(PVMDKIMAGE pImage, PVMDKEXTENT pExtent)
* but in reality they are not compressed. */
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
VMDK_SECTOR2BYTE(pExtent->uSectorGD),
- pExtent->pGD, cbGD, NULL);
+ pExtent->pGD, cbGD);
AssertRC(rc);
if (RT_FAILURE(rc))
{
@@ -1080,13 +1066,14 @@ static int vmdkReadGrainDirectory(PVMDKIMAGE pImage, PVMDKEXTENT pExtent)
for (i = 0, pGDTmp = pExtent->pGD; i < pExtent->cGDEntries; i++, pGDTmp++)
*pGDTmp = RT_LE2H_U32(*pGDTmp);
- if (pExtent->uSectorRGD)
+ if ( pExtent->uSectorRGD
+ && !(pImage->uOpenFlags & VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS))
{
/* The VMDK 1.1 spec seems to talk about compressed grain directories,
* but in reality they are not compressed. */
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
VMDK_SECTOR2BYTE(pExtent->uSectorRGD),
- pExtent->pRGD, cbGD, NULL);
+ pExtent->pRGD, cbGD);
AssertRC(rc);
if (RT_FAILURE(rc))
{
@@ -1117,7 +1104,7 @@ static int vmdkReadGrainDirectory(PVMDKIMAGE pImage, PVMDKEXTENT pExtent)
{
uint32_t uGTStart = *pGDTmp;
uint32_t uRGTStart = *pRGDTmp;
- uint32_t cbGTRead = cbGT;
+ size_t cbGTRead = cbGT;
/* If no grain table is allocated skip the entry. */
if (*pGDTmp == 0 && *pRGDTmp == 0)
@@ -1206,7 +1193,7 @@ static int vmdkReadGrainDirectory(PVMDKIMAGE pImage, PVMDKEXTENT pExtent)
* but in reality they are not compressed. */
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
VMDK_SECTOR2BYTE(uGTStart),
- pTmpGT1, cbGTRead, NULL);
+ pTmpGT1, cbGTRead);
if (RT_FAILURE(rc))
{
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
@@ -1217,7 +1204,7 @@ static int vmdkReadGrainDirectory(PVMDKIMAGE pImage, PVMDKEXTENT pExtent)
* but in reality they are not compressed. */
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
VMDK_SECTOR2BYTE(uRGTStart),
- pTmpGT2, cbGTRead, NULL);
+ pTmpGT2, cbGTRead);
if (RT_FAILURE(rc))
{
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
@@ -1324,7 +1311,7 @@ static int vmdkCreateGrainDirectory(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
/* Write the redundant grain directory entry to disk. */
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
VMDK_SECTOR2BYTE(pExtent->uSectorRGD) + i * sizeof(uGTSectorLE),
- &uGTSectorLE, sizeof(uGTSectorLE), NULL);
+ &uGTSectorLE, sizeof(uGTSectorLE));
if (RT_FAILURE(rc))
{
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write new redundant grain directory entry in '%s'"), pExtent->pszFullname);
@@ -1342,7 +1329,7 @@ static int vmdkCreateGrainDirectory(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
/* Write the grain directory entry to disk. */
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
VMDK_SECTOR2BYTE(pExtent->uSectorGD) + i * sizeof(uGTSectorLE),
- &uGTSectorLE, sizeof(uGTSectorLE), NULL);
+ &uGTSectorLE, sizeof(uGTSectorLE));
if (RT_FAILURE(rc))
{
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write new grain directory entry in '%s'"), pExtent->pszFullname);
@@ -2363,7 +2350,7 @@ static int vmdkDescriptorPrepare(PVMDKIMAGE pImage, uint64_t cbLimit,
*/
size_t cbDescriptor = cbLimit ? cbLimit : 4 * _1K;
char *pszDescriptor = (char *)RTMemAllocZ(cbDescriptor);
- unsigned offDescriptor = 0;
+ size_t offDescriptor = 0;
if (!pszDescriptor)
return VERR_NO_MEMORY;
@@ -2424,67 +2411,13 @@ static int vmdkDescriptorPrepare(PVMDKIMAGE pImage, uint64_t cbLimit,
/**
* Internal: write/update the descriptor part of the image.
*/
-static int vmdkWriteDescriptor(PVMDKIMAGE pImage)
-{
- int rc = VINF_SUCCESS;
- uint64_t cbLimit;
- uint64_t uOffset;
- PVMDKFILE pDescFile;
- void *pvDescriptor;
- size_t cbDescriptor;
-
- if (pImage->pDescData)
- {
- /* Separate descriptor file. */
- uOffset = 0;
- cbLimit = 0;
- pDescFile = pImage->pFile;
- }
- else
- {
- /* Embedded descriptor file. */
- uOffset = VMDK_SECTOR2BYTE(pImage->pExtents[0].uDescriptorSector);
- cbLimit = VMDK_SECTOR2BYTE(pImage->pExtents[0].cDescriptorSectors);
- pDescFile = pImage->pExtents[0].pFile;
- }
- /* Bail out if there is no file to write to. */
- if (pDescFile == NULL)
- return VERR_INVALID_PARAMETER;
-
- rc = vmdkDescriptorPrepare(pImage, cbLimit, &pvDescriptor, &cbDescriptor);
- if (RT_SUCCESS(rc))
- {
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pDescFile->pStorage, uOffset,
- pvDescriptor, cbLimit ? cbLimit : cbDescriptor, NULL);
- if (RT_FAILURE(rc))
- rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error writing descriptor in '%s'"), pImage->pszFilename);
-
- if (RT_SUCCESS(rc) && !cbLimit)
- {
- rc = vdIfIoIntFileSetSize(pImage->pIfIo, pDescFile->pStorage, cbDescriptor);
- if (RT_FAILURE(rc))
- rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error truncating descriptor in '%s'"), pImage->pszFilename);
- }
-
- if (RT_SUCCESS(rc))
- pImage->Descriptor.fDirty = false;
-
- RTMemFree(pvDescriptor);
- }
-
- return rc;
-}
-
-/**
- * Internal: write/update the descriptor part of the image - async version.
- */
-static int vmdkWriteDescriptorAsync(PVMDKIMAGE pImage, PVDIOCTX pIoCtx)
+static int vmdkWriteDescriptor(PVMDKIMAGE pImage, PVDIOCTX pIoCtx)
{
int rc = VINF_SUCCESS;
uint64_t cbLimit;
uint64_t uOffset;
PVMDKFILE pDescFile;
- void *pvDescriptor;
+ void *pvDescriptor = NULL;
size_t cbDescriptor;
if (pImage->pDescData)
@@ -2508,10 +2441,10 @@ static int vmdkWriteDescriptorAsync(PVMDKIMAGE pImage, PVDIOCTX pIoCtx)
rc = vmdkDescriptorPrepare(pImage, cbLimit, &pvDescriptor, &cbDescriptor);
if (RT_SUCCESS(rc))
{
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pDescFile->pStorage,
- uOffset, pvDescriptor,
- cbLimit ? cbLimit : cbDescriptor,
- pIoCtx, NULL, NULL);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pDescFile->pStorage,
+ uOffset, pvDescriptor,
+ cbLimit ? cbLimit : cbDescriptor,
+ pIoCtx, NULL, NULL);
if ( RT_FAILURE(rc)
&& rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error writing descriptor in '%s'"), pImage->pszFilename);
@@ -2527,7 +2460,8 @@ static int vmdkWriteDescriptorAsync(PVMDKIMAGE pImage, PVDIOCTX pIoCtx)
if (RT_SUCCESS(rc))
pImage->Descriptor.fDirty = false;
- RTMemFree(pvDescriptor);
+ if (pvDescriptor)
+ RTMemFree(pvDescriptor);
return rc;
}
@@ -2574,7 +2508,7 @@ static int vmdkReadBinaryMetaExtent(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
if (!fMagicAlreadyRead)
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage, 0,
- &Header, sizeof(Header), NULL);
+ &Header, sizeof(Header));
else
{
Header.magicNumber = RT_H2LE_U32(VMDK_SPARSE_MAGICNUMBER);
@@ -2582,8 +2516,7 @@ static int vmdkReadBinaryMetaExtent(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
RT_OFFSETOF(SparseExtentHeader, version),
&Header.version,
sizeof(Header)
- - RT_OFFSETOF(SparseExtentHeader, version),
- NULL);
+ - RT_OFFSETOF(SparseExtentHeader, version));
}
AssertRC(rc);
if (RT_FAILURE(rc))
@@ -2623,7 +2556,7 @@ static int vmdkReadBinaryMetaExtent(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
/* Read the footer, which comes before the end-of-stream marker. */
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
cbFile - 2*512, &Header,
- sizeof(Header), NULL);
+ sizeof(Header));
AssertRC(rc);
if (RT_FAILURE(rc))
{
@@ -2776,70 +2709,7 @@ out:
* Internal: write/update the metadata for a sparse extent.
*/
static int vmdkWriteMetaSparseExtent(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
- uint64_t uOffset)
-{
- SparseExtentHeader Header;
-
- memset(&Header, '\0', sizeof(Header));
- Header.magicNumber = RT_H2LE_U32(VMDK_SPARSE_MAGICNUMBER);
- Header.version = RT_H2LE_U32(pExtent->uVersion);
- Header.flags = RT_H2LE_U32(RT_BIT(0));
- if (pExtent->pRGD)
- Header.flags |= RT_H2LE_U32(RT_BIT(1));
- if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
- Header.flags |= RT_H2LE_U32(RT_BIT(16) | RT_BIT(17));
- Header.capacity = RT_H2LE_U64(pExtent->cSectors);
- Header.grainSize = RT_H2LE_U64(pExtent->cSectorsPerGrain);
- Header.descriptorOffset = RT_H2LE_U64(pExtent->uDescriptorSector);
- Header.descriptorSize = RT_H2LE_U64(pExtent->cDescriptorSectors);
- Header.numGTEsPerGT = RT_H2LE_U32(pExtent->cGTEntries);
- if (pExtent->fFooter && uOffset == 0)
- {
- if (pExtent->pRGD)
- {
- Assert(pExtent->uSectorRGD);
- Header.rgdOffset = RT_H2LE_U64(VMDK_GD_AT_END);
- Header.gdOffset = RT_H2LE_U64(VMDK_GD_AT_END);
- }
- else
- {
- Header.gdOffset = RT_H2LE_U64(VMDK_GD_AT_END);
- }
- }
- else
- {
- if (pExtent->pRGD)
- {
- Assert(pExtent->uSectorRGD);
- Header.rgdOffset = RT_H2LE_U64(pExtent->uSectorRGD);
- Header.gdOffset = RT_H2LE_U64(pExtent->uSectorGD);
- }
- else
- {
- Header.gdOffset = RT_H2LE_U64(pExtent->uSectorGD);
- }
- }
- Header.overHead = RT_H2LE_U64(pExtent->cOverheadSectors);
- Header.uncleanShutdown = pExtent->fUncleanShutdown;
- Header.singleEndLineChar = '\n';
- Header.nonEndLineChar = ' ';
- Header.doubleEndLineChar1 = '\r';
- Header.doubleEndLineChar2 = '\n';
- Header.compressAlgorithm = RT_H2LE_U16(pExtent->uCompression);
-
- int rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
- uOffset, &Header, sizeof(Header), NULL);
- AssertRC(rc);
- if (RT_FAILURE(rc))
- rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error writing extent header in '%s'"), pExtent->pszFullname);
- return rc;
-}
-
-/**
- * Internal: write/update the metadata for a sparse extent - async version.
- */
-static int vmdkWriteMetaSparseExtentAsync(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
- uint64_t uOffset, PVDIOCTX pIoCtx)
+ uint64_t uOffset, PVDIOCTX pIoCtx)
{
SparseExtentHeader Header;
@@ -2890,9 +2760,9 @@ static int vmdkWriteMetaSparseExtentAsync(PVMDKIMAGE pImage, PVMDKEXTENT pExtent
Header.doubleEndLineChar2 = '\n';
Header.compressAlgorithm = RT_H2LE_U16(pExtent->uCompression);
- int rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pExtent->pFile->pStorage,
- uOffset, &Header, sizeof(Header),
- pIoCtx, NULL, NULL);
+ int rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pExtent->pFile->pStorage,
+ uOffset, &Header, sizeof(Header),
+ pIoCtx, NULL, NULL);
if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error writing extent header in '%s'"), pExtent->pszFullname);
return rc;
@@ -2910,7 +2780,7 @@ static int vmdkReadMetaESXSparseExtent(PVMDKEXTENT pExtent)
uint64_t cSectorsPerGDE;
int rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage, 0,
- &Header, sizeof(Header), NULL);
+ &Header, sizeof(Header));
AssertRC(rc);
if (RT_FAILURE(rc))
{
@@ -2991,10 +2861,17 @@ static void vmdkFreeStreamBuffers(PVMDKEXTENT pExtent)
/**
* Internal: free the memory used by the extent data structure, optionally
* deleting the referenced files.
+ *
+ * @returns VBox status code.
+ * @param pImage Pointer to the image instance data.
+ * @param pExtent The extent to free.
+ * @param fDelete Flag whether to delete the backing storage.
*/
-static void vmdkFreeExtentData(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
- bool fDelete)
+static int vmdkFreeExtentData(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
+ bool fDelete)
{
+ int rc = VINF_SUCCESS;
+
vmdkFreeGrainDirectory(pExtent);
if (pExtent->pDescData)
{
@@ -3004,10 +2881,10 @@ static void vmdkFreeExtentData(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
if (pExtent->pFile != NULL)
{
/* Do not delete raw extents, these have full and base names equal. */
- vmdkFileClose(pImage, &pExtent->pFile,
- fDelete
- && pExtent->pszFullname
- && strcmp(pExtent->pszFullname, pExtent->pszBasename));
+ rc =vmdkFileClose(pImage, &pExtent->pFile,
+ fDelete
+ && pExtent->pszFullname
+ && strcmp(pExtent->pszFullname, pExtent->pszBasename));
}
if (pExtent->pszBasename)
{
@@ -3020,6 +2897,8 @@ static void vmdkFreeExtentData(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
pExtent->pszFullname = NULL;
}
vmdkFreeStreamBuffers(pExtent);
+
+ return rc;
}
/**
@@ -3111,8 +2990,7 @@ static int vmdkOpenImage(PVMDKIMAGE pImage, unsigned uOpenFlags)
*/
rc = vmdkFileOpen(pImage, &pFile, pImage->pszFilename,
- VDOpenFlagsToFileOpenFlags(uOpenFlags, false /* fCreate */),
- false /* fAsyncIO */);
+ VDOpenFlagsToFileOpenFlags(uOpenFlags, false /* fCreate */));
if (RT_FAILURE(rc))
{
/* Do NOT signal an appropriate error here, as the VD layer has the
@@ -3123,7 +3001,7 @@ static int vmdkOpenImage(PVMDKIMAGE pImage, unsigned uOpenFlags)
/* Read magic (if present). */
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pFile->pStorage, 0,
- &u32Magic, sizeof(u32Magic), NULL);
+ &u32Magic, sizeof(u32Magic));
if (RT_FAILURE(rc))
{
vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error reading the magic number in '%s'"), pImage->pszFilename);
@@ -3181,7 +3059,7 @@ static int vmdkOpenImage(PVMDKIMAGE pImage, unsigned uOpenFlags)
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
VMDK_SECTOR2BYTE(pExtent->uDescriptorSector),
pExtent->pDescData,
- VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors), NULL);
+ VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors));
AssertRC(rc);
if (RT_FAILURE(rc))
{
@@ -3248,17 +3126,17 @@ static int vmdkOpenImage(PVMDKIMAGE pImage, unsigned uOpenFlags)
/* Don't reread the place where the magic would live in a sparse
* image if it's a descriptor based one. */
memcpy(pImage->pDescData, &u32Magic, sizeof(u32Magic));
- size_t cbRead;
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pFile->pStorage, sizeof(u32Magic),
pImage->pDescData + sizeof(u32Magic),
RT_MIN(pImage->cbDescAlloc - sizeof(u32Magic),
- cbFileSize - sizeof(u32Magic)),
- &cbRead);
+ cbFileSize - sizeof(u32Magic)));
if (RT_FAILURE(rc))
{
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: read error for descriptor in '%s'"), pImage->pszFilename);
goto out;
}
+
+#if 0 /** @todo: Revisit */
cbRead += sizeof(u32Magic);
if (cbRead == pImage->cbDescAlloc)
{
@@ -3267,6 +3145,7 @@ static int vmdkOpenImage(PVMDKIMAGE pImage, unsigned uOpenFlags)
rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: cannot read descriptor in '%s'"), pImage->pszFilename);
goto out;
}
+#endif
rc = vmdkParseDescriptor(pImage, pImage->pDescData,
pImage->cbDescAlloc);
@@ -3351,8 +3230,7 @@ static int vmdkOpenImage(PVMDKIMAGE pImage, unsigned uOpenFlags)
case VMDKETYPE_HOSTED_SPARSE:
rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
VDOpenFlagsToFileOpenFlags(uOpenFlags,
- false /* fCreate */),
- false /* fAsyncIO */);
+ false /* fCreate */));
if (RT_FAILURE(rc))
{
/* Do NOT signal an appropriate error here, as the VD
@@ -3379,8 +3257,7 @@ static int vmdkOpenImage(PVMDKIMAGE pImage, unsigned uOpenFlags)
case VMDKETYPE_FLAT:
rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
VDOpenFlagsToFileOpenFlags(uOpenFlags,
- false /* fCreate */),
- true /* fAsyncIO */);
+ false /* fCreate */));
if (RT_FAILURE(rc))
{
/* Do NOT signal an appropriate error here, as the VD
@@ -3417,7 +3294,7 @@ static int vmdkOpenImage(PVMDKIMAGE pImage, unsigned uOpenFlags)
}
/* Update the image metadata now in case has changed. */
- rc = vmdkFlushImage(pImage);
+ rc = vmdkFlushImage(pImage, NULL);
if (RT_FAILURE(rc))
goto out;
@@ -3482,8 +3359,7 @@ static int vmdkCreateRawImage(PVMDKIMAGE pImage, const PVBOXHDDRAW pRaw,
/* Create raw disk descriptor file. */
rc = vmdkFileOpen(pImage, &pImage->pFile, pImage->pszFilename,
VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
- true /* fCreate */),
- false /* fAsyncIO */);
+ true /* fCreate */));
if (RT_FAILURE(rc))
return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pImage->pszFilename);
@@ -3507,8 +3383,7 @@ static int vmdkCreateRawImage(PVMDKIMAGE pImage, const PVBOXHDDRAW pRaw,
/* Open flat image, the raw disk. */
rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
- false /* fCreate */),
- false /* fAsyncIO */);
+ false /* fCreate */));
if (RT_FAILURE(rc))
return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not open raw disk file '%s'"), pExtent->pszFullname);
}
@@ -3545,8 +3420,7 @@ static int vmdkCreateRawImage(PVMDKIMAGE pImage, const PVBOXHDDRAW pRaw,
/* Create raw partition descriptor file. */
rc = vmdkFileOpen(pImage, &pImage->pFile, pImage->pszFilename,
VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
- true /* fCreate */),
- false /* fAsyncIO */);
+ true /* fCreate */));
if (RT_FAILURE(rc))
return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pImage->pszFilename);
@@ -3617,14 +3491,13 @@ static int vmdkCreateRawImage(PVMDKIMAGE pImage, const PVBOXHDDRAW pRaw,
/* Create partition table flat image. */
rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
- true /* fCreate */),
- false /* fAsyncIO */);
+ true /* fCreate */));
if (RT_FAILURE(rc))
return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new partition data file '%s'"), pExtent->pszFullname);
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
VMDK_SECTOR2BYTE(uPartOffset),
pPart->pvPartitionData,
- pPart->cbData, NULL);
+ pPart->cbData);
if (RT_FAILURE(rc))
return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not write partition data to '%s'"), pExtent->pszFullname);
uPartOffset += VMDK_BYTE2SECTOR(pPart->cbData);
@@ -3653,8 +3526,7 @@ static int vmdkCreateRawImage(PVMDKIMAGE pImage, const PVBOXHDDRAW pRaw,
/* Open flat image, the raw partition. */
rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
- false /* fCreate */),
- false /* fAsyncIO */);
+ false /* fCreate */));
if (RT_FAILURE(rc))
return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not open raw partition file '%s'"), pExtent->pszFullname);
}
@@ -3727,8 +3599,7 @@ static int vmdkCreateRegularImage(PVMDKIMAGE pImage, uint64_t cbSize,
{
rc = vmdkFileOpen(pImage, &pImage->pFile, pImage->pszFilename,
VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
- true /* fCreate */),
- false /* fAsyncIO */);
+ true /* fCreate */));
if (RT_FAILURE(rc))
return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new sparse descriptor file '%s'"), pImage->pszFilename);
}
@@ -3798,8 +3669,7 @@ static int vmdkCreateRegularImage(PVMDKIMAGE pImage, uint64_t cbSize,
/* Create file for extent. */
rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
- true /* fCreate */),
- false /* fAsyncIO */);
+ true /* fCreate */));
if (RT_FAILURE(rc))
return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pExtent->pszFullname);
if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
@@ -3828,7 +3698,7 @@ static int vmdkCreateRegularImage(PVMDKIMAGE pImage, uint64_t cbSize,
unsigned cbChunk = (unsigned)RT_MIN(cbExtent, cbBuf);
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
- uOff, pvBuf, cbChunk, NULL);
+ uOff, pvBuf, cbChunk);
if (RT_FAILURE(rc))
{
RTMemFree(pvBuf);
@@ -3997,8 +3867,7 @@ static int vmdkCreateStreamImage(PVMDKIMAGE pImage, uint64_t cbSize,
rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
true /* fCreate */)
- & ~RTFILE_O_READ,
- false /* fAsyncIO */);
+ & ~RTFILE_O_READ);
if (RT_FAILURE(rc))
return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pExtent->pszFullname);
@@ -4203,14 +4072,14 @@ static int vmdkCreateImage(PVMDKIMAGE pImage, uint64_t cbSize,
* information explicitly. */
pImage->pExtents[0].cDescriptorSectors = VMDK_BYTE2SECTOR(RT_ALIGN_64( pImage->Descriptor.aLines[pImage->Descriptor.cLines]
- pImage->Descriptor.aLines[0], 512));
- rc = vmdkWriteMetaSparseExtent(pImage, &pImage->pExtents[0], 0);
+ rc = vmdkWriteMetaSparseExtent(pImage, &pImage->pExtents[0], 0, NULL);
if (RT_FAILURE(rc))
{
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write VMDK header in '%s'"), pImage->pszFilename);
goto out;
}
- rc = vmdkWriteDescriptor(pImage);
+ rc = vmdkWriteDescriptor(pImage, NULL);
if (RT_FAILURE(rc))
{
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write VMDK descriptor in '%s'"), pImage->pszFilename);
@@ -4218,7 +4087,7 @@ static int vmdkCreateImage(PVMDKIMAGE pImage, uint64_t cbSize,
}
}
else
- rc = vmdkFlushImage(pImage);
+ rc = vmdkFlushImage(pImage, NULL);
out:
if (RT_SUCCESS(rc) && pfnProgress)
@@ -4307,7 +4176,7 @@ static int vmdkStreamFlushGT(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
pMarker->uSector = RT_H2LE_U64(VMDK_BYTE2SECTOR((uint64_t)pExtent->cGTEntries * sizeof(uint32_t)));
pMarker->uType = RT_H2LE_U32(VMDK_MARKER_GT);
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage, uFileOffset,
- aMarker, sizeof(aMarker), NULL);
+ aMarker, sizeof(aMarker));
AssertRC(rc);
uFileOffset += 512;
@@ -4326,8 +4195,7 @@ static int vmdkStreamFlushGT(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage, uFileOffset,
&pImage->pGTCache->aGTCache[i].aGTData[0],
- VMDK_GT_CACHELINE_SIZE * sizeof(uint32_t),
- NULL);
+ VMDK_GT_CACHELINE_SIZE * sizeof(uint32_t));
uFileOffset += VMDK_GT_CACHELINE_SIZE * sizeof(uint32_t);
if (RT_FAILURE(rc))
break;
@@ -4415,7 +4283,7 @@ static int vmdkFreeImage(PVMDKIMAGE pImage, bool fDelete)
pMarker->uSector = VMDK_BYTE2SECTOR(RT_ALIGN_64(RT_H2LE_U64((uint64_t)pExtent->cGDEntries * sizeof(uint32_t)), 512));
pMarker->uType = RT_H2LE_U32(VMDK_MARKER_GD);
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage, uFileOffset,
- aMarker, sizeof(aMarker), NULL);
+ aMarker, sizeof(aMarker));
AssertRC(rc);
uFileOffset += 512;
@@ -4426,8 +4294,7 @@ static int vmdkFreeImage(PVMDKIMAGE pImage, bool fDelete)
*pGDTmp = RT_H2LE_U32(*pGDTmp);
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
uFileOffset, pExtent->pGD,
- pExtent->cGDEntries * sizeof(uint32_t),
- NULL);
+ pExtent->cGDEntries * sizeof(uint32_t));
AssertRC(rc);
pExtent->uSectorGD = VMDK_BYTE2SECTOR(uFileOffset);
@@ -4441,35 +4308,45 @@ static int vmdkFreeImage(PVMDKIMAGE pImage, bool fDelete)
pMarker->uSector = VMDK_BYTE2SECTOR(512);
pMarker->uType = RT_H2LE_U32(VMDK_MARKER_FOOTER);
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
- uFileOffset, aMarker, sizeof(aMarker), NULL);
+ uFileOffset, aMarker, sizeof(aMarker));
AssertRC(rc);
uFileOffset += 512;
- rc = vmdkWriteMetaSparseExtent(pImage, pExtent, uFileOffset);
+ rc = vmdkWriteMetaSparseExtent(pImage, pExtent, uFileOffset, NULL);
AssertRC(rc);
uFileOffset += 512;
/* End-of-stream marker. */
memset(pMarker, '\0', sizeof(aMarker));
rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
- uFileOffset, aMarker, sizeof(aMarker), NULL);
+ uFileOffset, aMarker, sizeof(aMarker));
AssertRC(rc);
}
}
else
- vmdkFlushImage(pImage);
+ vmdkFlushImage(pImage, NULL);
if (pImage->pExtents != NULL)
{
for (unsigned i = 0 ; i < pImage->cExtents; i++)
- vmdkFreeExtentData(pImage, &pImage->pExtents[i], fDelete);
+ {
+ int rc2 = vmdkFreeExtentData(pImage, &pImage->pExtents[i], fDelete);
+ if (RT_SUCCESS(rc))
+ rc = rc2; /* Propogate any error when closing the file. */
+ }
RTMemFree(pImage->pExtents);
pImage->pExtents = NULL;
}
pImage->cExtents = 0;
if (pImage->pFile != NULL)
- vmdkFileClose(pImage, &pImage->pFile, fDelete);
- vmdkFileCheckAllClose(pImage);
+ {
+ int rc2 = vmdkFileClose(pImage, &pImage->pFile, fDelete);
+ if (RT_SUCCESS(rc))
+ rc = rc2; /* Propogate any error when closing the file. */
+ }
+ int rc2 = vmdkFileCheckAllClose(pImage);
+ if (RT_SUCCESS(rc))
+ rc = rc2; /* Propogate any error when closing the file. */
if (pImage->pGTCache)
{
@@ -4490,7 +4367,7 @@ static int vmdkFreeImage(PVMDKIMAGE pImage, bool fDelete)
/**
* Internal. Flush image data (and metadata) to disk.
*/
-static int vmdkFlushImage(PVMDKIMAGE pImage)
+static int vmdkFlushImage(PVMDKIMAGE pImage, PVDIOCTX pIoCtx)
{
PVMDKEXTENT pExtent;
int rc = VINF_SUCCESS;
@@ -4498,7 +4375,7 @@ static int vmdkFlushImage(PVMDKIMAGE pImage)
/* Update descriptor if changed. */
if (pImage->Descriptor.fDirty)
{
- rc = vmdkWriteDescriptor(pImage);
+ rc = vmdkWriteDescriptor(pImage, pIoCtx);
if (RT_FAILURE(rc))
goto out;
}
@@ -4513,7 +4390,7 @@ static int vmdkFlushImage(PVMDKIMAGE pImage)
case VMDKETYPE_HOSTED_SPARSE:
if (!pExtent->fFooter)
{
- rc = vmdkWriteMetaSparseExtent(pImage, pExtent, 0);
+ rc = vmdkWriteMetaSparseExtent(pImage, pExtent, 0, pIoCtx);
if (RT_FAILURE(rc))
goto out;
}
@@ -4526,7 +4403,7 @@ static int vmdkFlushImage(PVMDKIMAGE pImage)
break;
uFileOffset = RT_ALIGN_64(uFileOffset, 512);
rc = vmdkWriteMetaSparseExtent(pImage, pExtent,
- uFileOffset);
+ uFileOffset, pIoCtx);
if (RT_FAILURE(rc))
goto out;
}
@@ -4559,7 +4436,8 @@ static int vmdkFlushImage(PVMDKIMAGE pImage)
if ( pExtent->pFile != NULL
&& !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
&& !(pExtent->pszBasename[0] == RTPATH_SLASH))
- rc = vdIfIoIntFileFlushSync(pImage->pIfIo, pExtent->pFile->pStorage);
+ rc = vdIfIoIntFileFlush(pImage->pIfIo, pExtent->pFile->pStorage, pIoCtx,
+ NULL, NULL);
break;
case VMDKETYPE_ZERO:
/* No need to do anything for this extent. */
@@ -4617,8 +4495,9 @@ static uint32_t vmdkGTCacheHash(PVMDKGTCACHE pCache, uint64_t uSector,
* Internal. Get sector number in the extent file from the relative sector
* number in the extent.
*/
-static int vmdkGetSector(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
- uint64_t uSector, uint64_t *puExtentSector)
+static int vmdkGetSector(PVMDKIMAGE pImage, PVDIOCTX pIoCtx,
+ PVMDKEXTENT pExtent, uint64_t uSector,
+ uint64_t *puExtentSector)
{
PVMDKGTCACHE pCache = pImage->pGTCache;
uint64_t uGDIndex, uGTSector, uGTBlock;
@@ -4658,63 +4537,10 @@ static int vmdkGetSector(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
|| pGTCacheEntry->uGTBlock != uGTBlock)
{
/* Cache miss, fetch data from disk. */
- rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
- VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
- aGTDataTmp, sizeof(aGTDataTmp), NULL);
- if (RT_FAILURE(rc))
- return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot read grain table entry in '%s'"), pExtent->pszFullname);
- pGTCacheEntry->uExtent = pExtent->uExtent;
- pGTCacheEntry->uGTBlock = uGTBlock;
- for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
- pGTCacheEntry->aGTData[i] = RT_LE2H_U32(aGTDataTmp[i]);
- }
- uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE;
- uint32_t uGrainSector = pGTCacheEntry->aGTData[uGTBlockIndex];
- if (uGrainSector)
- *puExtentSector = uGrainSector + uSector % pExtent->cSectorsPerGrain;
- else
- *puExtentSector = 0;
- return VINF_SUCCESS;
-}
-
-/**
- * Internal. Get sector number in the extent file from the relative sector
- * number in the extent - version for async access.
- */
-static int vmdkGetSectorAsync(PVMDKIMAGE pImage, PVDIOCTX pIoCtx,
- PVMDKEXTENT pExtent, uint64_t uSector,
- uint64_t *puExtentSector)
-{
- PVMDKGTCACHE pCache = pImage->pGTCache;
- uint64_t uGDIndex, uGTSector, uGTBlock;
- uint32_t uGTHash, uGTBlockIndex;
- PVMDKGTCACHEENTRY pGTCacheEntry;
- uint32_t aGTDataTmp[VMDK_GT_CACHELINE_SIZE];
- int rc;
-
- uGDIndex = uSector / pExtent->cSectorsPerGDE;
- if (uGDIndex >= pExtent->cGDEntries)
- return VERR_OUT_OF_RANGE;
- uGTSector = pExtent->pGD[uGDIndex];
- if (!uGTSector)
- {
- /* There is no grain table referenced by this grain directory
- * entry. So there is absolutely no data in this area. */
- *puExtentSector = 0;
- return VINF_SUCCESS;
- }
-
- uGTBlock = uSector / (pExtent->cSectorsPerGrain * VMDK_GT_CACHELINE_SIZE);
- uGTHash = vmdkGTCacheHash(pCache, uGTBlock, pExtent->uExtent);
- pGTCacheEntry = &pCache->aGTCache[uGTHash];
- if ( pGTCacheEntry->uExtent != pExtent->uExtent
- || pGTCacheEntry->uGTBlock != uGTBlock)
- {
- /* Cache miss, fetch data from disk. */
PVDMETAXFER pMetaXfer;
- rc = vdIfIoIntFileReadMetaAsync(pImage->pIfIo, pExtent->pFile->pStorage,
- VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
- aGTDataTmp, sizeof(aGTDataTmp), pIoCtx, &pMetaXfer, NULL, NULL);
+ rc = vdIfIoIntFileReadMeta(pImage->pIfIo, pExtent->pFile->pStorage,
+ VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
+ aGTDataTmp, sizeof(aGTDataTmp), pIoCtx, &pMetaXfer, NULL, NULL);
if (RT_FAILURE(rc))
return rc;
/* We can release the metadata transfer immediately. */
@@ -4734,234 +4560,18 @@ static int vmdkGetSectorAsync(PVMDKIMAGE pImage, PVDIOCTX pIoCtx,
}
/**
- * Internal. Allocates a new grain table (if necessary), writes the grain
- * and updates the grain table. The cache is also updated by this operation.
- * This is separate from vmdkGetSector, because that should be as fast as
- * possible. Most code from vmdkGetSector also appears here.
- */
-static int vmdkAllocGrain(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
- uint64_t uSector, const void *pvBuf,
- uint64_t cbWrite)
-{
- PVMDKGTCACHE pCache = pImage->pGTCache;
- uint64_t uGDIndex, uGTSector, uRGTSector, uGTBlock;
- uint64_t uFileOffset;
- uint32_t uGTHash, uGTBlockIndex;
- PVMDKGTCACHEENTRY pGTCacheEntry;
- uint32_t aGTDataTmp[VMDK_GT_CACHELINE_SIZE];
- int rc;
-
- uGDIndex = uSector / pExtent->cSectorsPerGDE;
- if (uGDIndex >= pExtent->cGDEntries)
- return VERR_OUT_OF_RANGE;
- uGTSector = pExtent->pGD[uGDIndex];
- if (pExtent->pRGD)
- uRGTSector = pExtent->pRGD[uGDIndex];
- else
- uRGTSector = 0; /**< avoid compiler warning */
- if (!uGTSector)
- {
- /* There is no grain table referenced by this grain directory
- * entry. So there is absolutely no data in this area. Allocate
- * a new grain table and put the reference to it in the GDs. */
- uFileOffset = pExtent->uAppendPosition;
- if (!uFileOffset)
- return VERR_INTERNAL_ERROR;
- Assert(!(uFileOffset % 512));
- uFileOffset = RT_ALIGN_64(uFileOffset, 512);
- uGTSector = VMDK_BYTE2SECTOR(uFileOffset);
-
- pExtent->uAppendPosition += pExtent->cGTEntries * sizeof(uint32_t);
-
- /* Normally the grain table is preallocated for hosted sparse extents
- * that support more than 32 bit sector numbers. So this shouldn't
- * ever happen on a valid extent. */
- if (uGTSector > UINT32_MAX)
- return VERR_VD_VMDK_INVALID_HEADER;
-
- /* Write grain table by writing the required number of grain table
- * cache chunks. Avoids dynamic memory allocation, but is a bit
- * slower. But as this is a pretty infrequently occurring case it
- * should be acceptable. */
- memset(aGTDataTmp, '\0', sizeof(aGTDataTmp));
- for (unsigned i = 0;
- i < pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE;
- i++)
- {
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
- VMDK_SECTOR2BYTE(uGTSector) + i * sizeof(aGTDataTmp),
- aGTDataTmp, sizeof(aGTDataTmp), NULL);
- if (RT_FAILURE(rc))
- return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write grain table allocation in '%s'"), pExtent->pszFullname);
- }
- pExtent->uAppendPosition = RT_ALIGN_64( pExtent->uAppendPosition
- + pExtent->cGTEntries * sizeof(uint32_t),
- 512);
-
- if (pExtent->pRGD)
- {
- AssertReturn(!uRGTSector, VERR_VD_VMDK_INVALID_HEADER);
- uFileOffset = pExtent->uAppendPosition;
- if (!uFileOffset)
- return VERR_INTERNAL_ERROR;
- Assert(!(uFileOffset % 512));
- uRGTSector = VMDK_BYTE2SECTOR(uFileOffset);
-
- pExtent->uAppendPosition += pExtent->cGTEntries * sizeof(uint32_t);
-
- /* Normally the redundant grain table is preallocated for hosted
- * sparse extents that support more than 32 bit sector numbers. So
- * this shouldn't ever happen on a valid extent. */
- if (uRGTSector > UINT32_MAX)
- return VERR_VD_VMDK_INVALID_HEADER;
-
- /* Write backup grain table by writing the required number of grain
- * table cache chunks. Avoids dynamic memory allocation, but is a
- * bit slower. But as this is a pretty infrequently occurring case
- * it should be acceptable. */
- for (unsigned i = 0;
- i < pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE;
- i++)
- {
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
- VMDK_SECTOR2BYTE(uRGTSector) + i * sizeof(aGTDataTmp),
- aGTDataTmp, sizeof(aGTDataTmp), NULL);
- if (RT_FAILURE(rc))
- return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain table allocation in '%s'"), pExtent->pszFullname);
- }
-
- pExtent->uAppendPosition = pExtent->uAppendPosition
- + pExtent->cGTEntries * sizeof(uint32_t);
- }
-
- /* Update the grain directory on disk (doing it before writing the
- * grain table will result in a garbled extent if the operation is
- * aborted for some reason. Otherwise the worst that can happen is
- * some unused sectors in the extent. */
- uint32_t uGTSectorLE = RT_H2LE_U64(uGTSector);
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
- VMDK_SECTOR2BYTE(pExtent->uSectorGD) + uGDIndex * sizeof(uGTSectorLE),
- &uGTSectorLE, sizeof(uGTSectorLE), NULL);
- if (RT_FAILURE(rc))
- return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write grain directory entry in '%s'"), pExtent->pszFullname);
- if (pExtent->pRGD)
- {
- uint32_t uRGTSectorLE = RT_H2LE_U64(uRGTSector);
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
- VMDK_SECTOR2BYTE(pExtent->uSectorRGD) + uGDIndex * sizeof(uRGTSectorLE),
- &uRGTSectorLE, sizeof(uRGTSectorLE), NULL);
- if (RT_FAILURE(rc))
- return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain directory entry in '%s'"), pExtent->pszFullname);
- }
-
- /* As the final step update the in-memory copy of the GDs. */
- pExtent->pGD[uGDIndex] = uGTSector;
- if (pExtent->pRGD)
- pExtent->pRGD[uGDIndex] = uRGTSector;
- }
-
- uFileOffset = pExtent->uAppendPosition;
- if (!uFileOffset)
- return VERR_INTERNAL_ERROR;
- Assert(!(uFileOffset % 512));
-
- /* Write the data. Always a full grain, or we're in big trouble. */
- if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
- {
- if (cbWrite != VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
- return vdIfError(pImage->pIfError, VERR_INTERNAL_ERROR, RT_SRC_POS, N_("VMDK: not enough data for a compressed data block in '%s'"), pExtent->pszFullname);
-
- /* Invalidate cache, just in case some code incorrectly allows mixing
- * of reads and writes. Normally shouldn't be needed. */
- pExtent->uGrainSectorAbs = 0;
-
- /* Write compressed data block and the markers. */
- uint32_t cbGrain = 0;
- rc = vmdkFileDeflateSync(pImage, pExtent, uFileOffset,
- pvBuf, cbWrite, uSector, &cbGrain);
- if (RT_FAILURE(rc))
- {
- AssertRC(rc);
- return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write allocated compressed data block in '%s'"), pExtent->pszFullname);
- }
- pExtent->uLastGrainAccess = uSector / pExtent->cSectorsPerGrain;
- pExtent->uAppendPosition += cbGrain;
- }
- else
- {
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
- uFileOffset, pvBuf, cbWrite, NULL);
- if (RT_FAILURE(rc))
- return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write allocated data block in '%s'"), pExtent->pszFullname);
- pExtent->uAppendPosition += cbWrite;
- }
-
- /* Update the grain table (and the cache). */
- uGTBlock = uSector / (pExtent->cSectorsPerGrain * VMDK_GT_CACHELINE_SIZE);
- uGTHash = vmdkGTCacheHash(pCache, uGTBlock, pExtent->uExtent);
- pGTCacheEntry = &pCache->aGTCache[uGTHash];
- if ( pGTCacheEntry->uExtent != pExtent->uExtent
- || pGTCacheEntry->uGTBlock != uGTBlock)
- {
- /* Cache miss, fetch data from disk. */
- rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
- VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
- aGTDataTmp, sizeof(aGTDataTmp), NULL);
- if (RT_FAILURE(rc))
- return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot read allocated grain table entry in '%s'"), pExtent->pszFullname);
- pGTCacheEntry->uExtent = pExtent->uExtent;
- pGTCacheEntry->uGTBlock = uGTBlock;
- for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
- pGTCacheEntry->aGTData[i] = RT_LE2H_U32(aGTDataTmp[i]);
- }
- else
- {
- /* Cache hit. Convert grain table block back to disk format, otherwise
- * the code below will write garbage for all but the updated entry. */
- for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
- aGTDataTmp[i] = RT_H2LE_U32(pGTCacheEntry->aGTData[i]);
- }
- uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE;
- aGTDataTmp[uGTBlockIndex] = RT_H2LE_U32(VMDK_BYTE2SECTOR(uFileOffset));
- pGTCacheEntry->aGTData[uGTBlockIndex] = VMDK_BYTE2SECTOR(uFileOffset);
- /* Update grain table on disk. */
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
- VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
- aGTDataTmp, sizeof(aGTDataTmp), NULL);
- if (RT_FAILURE(rc))
- return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write updated grain table in '%s'"), pExtent->pszFullname);
- if (pExtent->pRGD)
- {
- /* Update backup grain table on disk. */
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
- VMDK_SECTOR2BYTE(uRGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
- aGTDataTmp, sizeof(aGTDataTmp), NULL);
- if (RT_FAILURE(rc))
- return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write updated backup grain table in '%s'"), pExtent->pszFullname);
- }
-#ifdef VBOX_WITH_VMDK_ESX
- if (RT_SUCCESS(rc) && pExtent->enmType == VMDKETYPE_ESX_SPARSE)
- {
- pExtent->uFreeSector = uGTSector + VMDK_BYTE2SECTOR(cbWrite);
- pExtent->fMetaDirty = true;
- }
-#endif /* VBOX_WITH_VMDK_ESX */
- return rc;
-}
-
-/**
* Internal. Writes the grain and also if necessary the grain tables.
* Uses the grain table cache as a true grain table.
*/
static int vmdkStreamAllocGrain(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
- uint64_t uSector, const void *pvBuf,
+ uint64_t uSector, PVDIOCTX pIoCtx,
uint64_t cbWrite)
{
uint32_t uGrain;
uint32_t uGDEntry, uLastGDEntry;
uint32_t cbGrain = 0;
uint32_t uCacheLine, uCacheEntry;
- const void *pData = pvBuf;
+ const void *pData;
int rc;
/* Very strict requirements: always write at least one full grain, with
@@ -4992,7 +4602,7 @@ static int vmdkStreamAllocGrain(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
/* Zero byte write optimization. Since we don't tell VBoxHDD that we need
* to allocate something, we also need to detect the situation ourself. */
if ( !(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_ZEROES)
- && ASMBitFirstSet((volatile void *)pvBuf, (uint32_t)cbWrite * 8) == -1)
+ && vdIfIoIntIoCtxIsZero(pImage->pIfIo, pIoCtx, cbWrite, true /* fAdvance */))
return VINF_SUCCESS;
if (uGDEntry != uLastGDEntry)
@@ -5029,11 +4639,22 @@ static int vmdkStreamAllocGrain(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
if (cbWrite != VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
{
- memcpy(pExtent->pvGrain, pvBuf, cbWrite);
+ vdIfIoIntIoCtxCopyFrom(pImage->pIfIo, pIoCtx, pExtent->pvGrain, cbWrite);
memset((char *)pExtent->pvGrain + cbWrite, '\0',
VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) - cbWrite);
pData = pExtent->pvGrain;
}
+ else
+ {
+ RTSGSEG Segment;
+ unsigned cSegments = 1;
+ size_t cbSeg = 0;
+
+ cbSeg = vdIfIoIntIoCtxSegArrayCreate(pImage->pIfIo, pIoCtx, &Segment,
+ &cSegments, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
+ Assert(cbSeg == VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
+ pData = Segment.pvSeg;
+ }
rc = vmdkFileDeflateSync(pImage, pExtent, uFileOffset, pData,
VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain),
uSector, &cbGrain);
@@ -5050,11 +4671,10 @@ static int vmdkStreamAllocGrain(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
}
/**
- * Internal: Updates the grain table during a async grain allocation.
+ * Internal: Updates the grain table during grain allocation.
*/
-static int vmdkAllocGrainAsyncGTUpdate(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
- PVDIOCTX pIoCtx,
- PVMDKGRAINALLOCASYNC pGrainAlloc)
+static int vmdkAllocGrainGTUpdate(PVMDKIMAGE pImage, PVMDKEXTENT pExtent, PVDIOCTX pIoCtx,
+ PVMDKGRAINALLOCASYNC pGrainAlloc)
{
int rc = VINF_SUCCESS;
PVMDKGTCACHE pCache = pImage->pGTCache;
@@ -5081,10 +4701,10 @@ static int vmdkAllocGrainAsyncGTUpdate(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
/* Cache miss, fetch data from disk. */
LogFlow(("Cache miss, fetch data from disk\n"));
PVDMETAXFER pMetaXfer = NULL;
- rc = vdIfIoIntFileReadMetaAsync(pImage->pIfIo, pExtent->pFile->pStorage,
- VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
- aGTDataTmp, sizeof(aGTDataTmp), pIoCtx,
- &pMetaXfer, vmdkAllocGrainAsyncComplete, pGrainAlloc);
+ rc = vdIfIoIntFileReadMeta(pImage->pIfIo, pExtent->pFile->pStorage,
+ VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
+ aGTDataTmp, sizeof(aGTDataTmp), pIoCtx,
+ &pMetaXfer, vmdkAllocGrainComplete, pGrainAlloc);
if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
{
pGrainAlloc->cIoXfersPending++;
@@ -5113,10 +4733,10 @@ static int vmdkAllocGrainAsyncGTUpdate(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
aGTDataTmp[uGTBlockIndex] = RT_H2LE_U32(VMDK_BYTE2SECTOR(pGrainAlloc->uGrainOffset));
pGTCacheEntry->aGTData[uGTBlockIndex] = VMDK_BYTE2SECTOR(pGrainAlloc->uGrainOffset);
/* Update grain table on disk. */
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pExtent->pFile->pStorage,
- VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
- aGTDataTmp, sizeof(aGTDataTmp), pIoCtx,
- vmdkAllocGrainAsyncComplete, pGrainAlloc);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pExtent->pFile->pStorage,
+ VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
+ aGTDataTmp, sizeof(aGTDataTmp), pIoCtx,
+ vmdkAllocGrainComplete, pGrainAlloc);
if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
pGrainAlloc->cIoXfersPending++;
else if (RT_FAILURE(rc))
@@ -5124,10 +4744,10 @@ static int vmdkAllocGrainAsyncGTUpdate(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
if (pExtent->pRGD)
{
/* Update backup grain table on disk. */
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pExtent->pFile->pStorage,
- VMDK_SECTOR2BYTE(uRGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
- aGTDataTmp, sizeof(aGTDataTmp), pIoCtx,
- vmdkAllocGrainAsyncComplete, pGrainAlloc);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pExtent->pFile->pStorage,
+ VMDK_SECTOR2BYTE(uRGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
+ aGTDataTmp, sizeof(aGTDataTmp), pIoCtx,
+ vmdkAllocGrainComplete, pGrainAlloc);
if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
pGrainAlloc->cIoXfersPending++;
else if (RT_FAILURE(rc))
@@ -5149,7 +4769,7 @@ static int vmdkAllocGrainAsyncGTUpdate(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
/**
* Internal - complete the grain allocation by updating disk grain table if required.
*/
-static int vmdkAllocGrainAsyncComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
+static int vmdkAllocGrainComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
{
int rc = VINF_SUCCESS;
PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
@@ -5161,8 +4781,7 @@ static int vmdkAllocGrainAsyncComplete(void *pBackendData, PVDIOCTX pIoCtx, void
pGrainAlloc->cIoXfersPending--;
if (!pGrainAlloc->cIoXfersPending && pGrainAlloc->fGTUpdateNeeded)
- rc = vmdkAllocGrainAsyncGTUpdate(pImage, pGrainAlloc->pExtent,
- pIoCtx, pGrainAlloc);
+ rc = vmdkAllocGrainGTUpdate(pImage, pGrainAlloc->pExtent, pIoCtx, pGrainAlloc);
if (!pGrainAlloc->cIoXfersPending)
{
@@ -5175,11 +4794,10 @@ static int vmdkAllocGrainAsyncComplete(void *pBackendData, PVDIOCTX pIoCtx, void
}
/**
- * Internal. Allocates a new grain table (if necessary) - async version.
+ * Internal. Allocates a new grain table (if necessary).
*/
-static int vmdkAllocGrainAsync(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
- PVDIOCTX pIoCtx, uint64_t uSector,
- uint64_t cbWrite)
+static int vmdkAllocGrain(PVMDKIMAGE pImage, PVMDKEXTENT pExtent, PVDIOCTX pIoCtx,
+ uint64_t uSector, uint64_t cbWrite)
{
PVMDKGTCACHE pCache = pImage->pGTCache;
uint64_t uGDIndex, uGTSector, uRGTSector;
@@ -5190,8 +4808,6 @@ static int vmdkAllocGrainAsync(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
LogFlowFunc(("pCache=%#p pExtent=%#p pIoCtx=%#p uSector=%llu cbWrite=%llu\n",
pCache, pExtent, pIoCtx, uSector, cbWrite));
- AssertReturn(!(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED), VERR_NOT_SUPPORTED);
-
pGrainAlloc = (PVMDKGRAINALLOCASYNC)RTMemAllocZ(sizeof(VMDKGRAINALLOCASYNC));
if (!pGrainAlloc)
return VERR_NO_MEMORY;
@@ -5241,10 +4857,10 @@ static int vmdkAllocGrainAsync(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
return VERR_NO_MEMORY;
memset(paGTDataTmp, '\0', cbGTDataTmp);
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pExtent->pFile->pStorage,
- VMDK_SECTOR2BYTE(uGTSector),
- paGTDataTmp, cbGTDataTmp, pIoCtx,
- vmdkAllocGrainAsyncComplete, pGrainAlloc);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pExtent->pFile->pStorage,
+ VMDK_SECTOR2BYTE(uGTSector),
+ paGTDataTmp, cbGTDataTmp, pIoCtx,
+ vmdkAllocGrainComplete, pGrainAlloc);
if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
pGrainAlloc->cIoXfersPending++;
else if (RT_FAILURE(rc))
@@ -5276,10 +4892,10 @@ static int vmdkAllocGrainAsync(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
/* Write grain table by writing the required number of grain table
* cache chunks. Allocate memory dynamically here or we flood the
* metadata cache with very small entries. */
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pExtent->pFile->pStorage,
- VMDK_SECTOR2BYTE(uRGTSector),
- paGTDataTmp, cbGTDataTmp, pIoCtx,
- vmdkAllocGrainAsyncComplete, pGrainAlloc);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pExtent->pFile->pStorage,
+ VMDK_SECTOR2BYTE(uRGTSector),
+ paGTDataTmp, cbGTDataTmp, pIoCtx,
+ vmdkAllocGrainComplete, pGrainAlloc);
if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
pGrainAlloc->cIoXfersPending++;
else if (RT_FAILURE(rc))
@@ -5298,10 +4914,10 @@ static int vmdkAllocGrainAsync(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
* aborted for some reason. Otherwise the worst that can happen is
* some unused sectors in the extent. */
uint32_t uGTSectorLE = RT_H2LE_U64(uGTSector);
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pExtent->pFile->pStorage,
- VMDK_SECTOR2BYTE(pExtent->uSectorGD) + uGDIndex * sizeof(uGTSectorLE),
- &uGTSectorLE, sizeof(uGTSectorLE), pIoCtx,
- vmdkAllocGrainAsyncComplete, pGrainAlloc);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pExtent->pFile->pStorage,
+ VMDK_SECTOR2BYTE(pExtent->uSectorGD) + uGDIndex * sizeof(uGTSectorLE),
+ &uGTSectorLE, sizeof(uGTSectorLE), pIoCtx,
+ vmdkAllocGrainComplete, pGrainAlloc);
if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
pGrainAlloc->cIoXfersPending++;
else if (RT_FAILURE(rc))
@@ -5309,10 +4925,10 @@ static int vmdkAllocGrainAsync(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
if (pExtent->pRGD)
{
uint32_t uRGTSectorLE = RT_H2LE_U64(uRGTSector);
- rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pExtent->pFile->pStorage,
- VMDK_SECTOR2BYTE(pExtent->uSectorRGD) + uGDIndex * sizeof(uGTSectorLE),
- &uRGTSectorLE, sizeof(uRGTSectorLE), pIoCtx,
- vmdkAllocGrainAsyncComplete, pGrainAlloc);
+ rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pExtent->pFile->pStorage,
+ VMDK_SECTOR2BYTE(pExtent->uSectorRGD) + uGDIndex * sizeof(uGTSectorLE),
+ &uRGTSectorLE, sizeof(uRGTSectorLE), pIoCtx,
+ vmdkAllocGrainComplete, pGrainAlloc);
if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
pGrainAlloc->cIoXfersPending++;
else if (RT_FAILURE(rc))
@@ -5336,18 +4952,54 @@ static int vmdkAllocGrainAsync(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
pGrainAlloc->uGrainOffset = uFileOffset;
- /* Write the data. Always a full grain, or we're in big trouble. */
- rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pExtent->pFile->pStorage,
- uFileOffset, pIoCtx, cbWrite,
- vmdkAllocGrainAsyncComplete, pGrainAlloc);
- if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
- pGrainAlloc->cIoXfersPending++;
- else if (RT_FAILURE(rc))
- return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write allocated data block in '%s'"), pExtent->pszFullname);
+ if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+ {
+ AssertMsgReturn(vdIfIoIntIoCtxIsSynchronous(pImage->pIfIo, pIoCtx),
+ ("Accesses to stream optimized images must be synchronous\n"),
+ VERR_INVALID_STATE);
- pExtent->uAppendPosition += cbWrite;
+ if (cbWrite != VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
+ return vdIfError(pImage->pIfError, VERR_INTERNAL_ERROR, RT_SRC_POS, N_("VMDK: not enough data for a compressed data block in '%s'"), pExtent->pszFullname);
- rc = vmdkAllocGrainAsyncGTUpdate(pImage, pExtent, pIoCtx, pGrainAlloc);
+ /* Invalidate cache, just in case some code incorrectly allows mixing
+ * of reads and writes. Normally shouldn't be needed. */
+ pExtent->uGrainSectorAbs = 0;
+
+ /* Write compressed data block and the markers. */
+ uint32_t cbGrain = 0;
+ size_t cbSeg = 0;
+ RTSGSEG Segment;
+ unsigned cSegments = 1;
+
+ cbSeg = vdIfIoIntIoCtxSegArrayCreate(pImage->pIfIo, pIoCtx, &Segment,
+ &cSegments, cbWrite);
+ Assert(cbSeg == cbWrite);
+
+ rc = vmdkFileDeflateSync(pImage, pExtent, uFileOffset,
+ Segment.pvSeg, cbWrite, uSector, &cbGrain);
+ if (RT_FAILURE(rc))
+ {
+ AssertRC(rc);
+ return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write allocated compressed data block in '%s'"), pExtent->pszFullname);
+ }
+ pExtent->uLastGrainAccess = uSector / pExtent->cSectorsPerGrain;
+ pExtent->uAppendPosition += cbGrain;
+ }
+ else
+ {
+ /* Write the data. Always a full grain, or we're in big trouble. */
+ rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pExtent->pFile->pStorage,
+ uFileOffset, pIoCtx, cbWrite,
+ vmdkAllocGrainComplete, pGrainAlloc);
+ if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+ pGrainAlloc->cIoXfersPending++;
+ else if (RT_FAILURE(rc))
+ return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write allocated data block in '%s'"), pExtent->pszFullname);
+
+ pExtent->uAppendPosition += cbWrite;
+ }
+
+ rc = vmdkAllocGrainGTUpdate(pImage, pExtent, pIoCtx, pGrainAlloc);
if (!pGrainAlloc->cIoXfersPending)
{
@@ -5365,13 +5017,17 @@ static int vmdkAllocGrainAsync(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
* grains (hoping that they are in sequence).
*/
static int vmdkStreamReadSequential(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
- uint64_t uSector, void *pvBuf,
+ uint64_t uSector, PVDIOCTX pIoCtx,
uint64_t cbRead)
{
int rc;
- LogFlowFunc(("pImage=%#p pExtent=%#p uSector=%llu pvBuf=%#p cbRead=%llu\n",
- pImage, pExtent, uSector, pvBuf, cbRead));
+ LogFlowFunc(("pImage=%#p pExtent=%#p uSector=%llu pIoCtx=%#p cbRead=%llu\n",
+ pImage, pExtent, uSector, pIoCtx, cbRead));
+
+ AssertMsgReturn(vdIfIoIntIoCtxIsSynchronous(pImage->pIfIo, pIoCtx),
+ ("Async I/O not supported for sequential stream optimized images\n"),
+ VERR_INVALID_STATE);
/* Do not allow to go back. */
uint32_t uGrain = uSector / pExtent->cSectorsPerGrain;
@@ -5400,8 +5056,7 @@ static int vmdkStreamReadSequential(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
RT_ZERO(Marker);
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
VMDK_SECTOR2BYTE(uGrainSectorAbs),
- &Marker, RT_OFFSETOF(VMDKMARKER, uType),
- NULL);
+ &Marker, RT_OFFSETOF(VMDKMARKER, uType));
if (RT_FAILURE(rc))
return rc;
Marker.uSector = RT_LE2H_U64(Marker.uSector);
@@ -5413,8 +5068,7 @@ static int vmdkStreamReadSequential(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
VMDK_SECTOR2BYTE(uGrainSectorAbs)
+ RT_OFFSETOF(VMDKMARKER, uType),
- &Marker.uType, sizeof(Marker.uType),
- NULL);
+ &Marker.uType, sizeof(Marker.uType));
if (RT_FAILURE(rc))
return rc;
Marker.uType = RT_LE2H_U32(Marker.uType);
@@ -5430,7 +5084,7 @@ static int vmdkStreamReadSequential(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
VMDK_SECTOR2BYTE(uGrainSectorAbs)
+ 511,
- &Marker.uSector, 1, NULL);
+ &Marker.uSector, 1);
break;
case VMDK_MARKER_GT:
uGrainSectorAbs += 1 + VMDK_BYTE2SECTOR(pExtent->cGTEntries * sizeof(uint32_t));
@@ -5507,9 +5161,9 @@ static int vmdkStreamReadSequential(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
}
uint32_t uSectorInGrain = uSector % pExtent->cSectorsPerGrain;
- memcpy(pvBuf,
- (uint8_t *)pExtent->pvGrain + VMDK_SECTOR2BYTE(uSectorInGrain),
- cbRead);
+ vdIfIoIntIoCtxCopyTo(pImage->pIfIo, pIoCtx,
+ (uint8_t *)pExtent->pvGrain + VMDK_SECTOR2BYTE(uSectorInGrain),
+ cbRead);
LogFlowFunc(("returns VINF_SUCCESS\n"));
return VINF_SUCCESS;
}
@@ -5869,7 +5523,7 @@ static int vmdkRename(void *pBackendData, const char *pszFilename)
/* Make sure the descriptor gets written back. */
pImage->Descriptor.fDirty = true;
/* Flush the descriptor now, in case it is embedded. */
- vmdkFlushImage(pImage);
+ vmdkFlushImage(pImage, NULL);
/* Close and rename/move extents. */
for (i = 0; i < cExtents; i++)
@@ -5881,7 +5535,10 @@ static int vmdkRename(void *pBackendData, const char *pszFilename)
if (!apszNewName[i])
goto rollback;
/* Close the extent file. */
- vmdkFileClose(pImage, &pExtent->pFile, false);
+ rc = vmdkFileClose(pImage, &pExtent->pFile, false);
+ if (RT_FAILURE(rc))
+ goto rollback;
+
/* Rename the extent file. */
rc = vdIfIoIntFileMove(pImage->pIfIo, pExtent->pszFullname, apszNewName[i], 0);
if (RT_FAILURE(rc))
@@ -5890,7 +5547,9 @@ static int vmdkRename(void *pBackendData, const char *pszFilename)
apszOldName[i] = RTStrDup(pExtent->pszFullname);
}
/* Release all old stuff. */
- vmdkFreeImage(pImage, false);
+ rc = vmdkFreeImage(pImage, false);
+ if (RT_FAILURE(rc))
+ goto rollback;
fImageFreed = true;
@@ -5942,8 +5601,7 @@ rollback:
PVMDKFILE pFile;
rrc = vmdkFileOpen(pImage, &pFile, pszOldDescName,
VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_NORMAL,
- false /* fCreate */),
- false /* fAsyncIO */);
+ false /* fCreate */));
AssertRC(rrc);
if (fEmbeddedDesc)
{
@@ -5959,7 +5617,7 @@ rollback:
pImage->pFile = pFile;
}
pImage->Descriptor = DescriptorCopy;
- vmdkWriteDescriptor(pImage);
+ vmdkWriteDescriptor(pImage, NULL);
vmdkFileClose(pImage, &pFile, false);
/* Get rid of the stuff we implanted. */
pImage->pExtents = NULL;
@@ -6025,10 +5683,11 @@ static int vmdkClose(void *pBackendData, bool fDelete)
}
/** @copydoc VBOXHDDBACKEND::pfnRead */
-static int vmdkRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
- size_t cbToRead, size_t *pcbActuallyRead)
+static int vmdkRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
+ PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
+ LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
+ pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
PVMDKEXTENT pExtent;
uint64_t uSectorExtentRel;
@@ -6068,8 +5727,7 @@ static int vmdkRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
#ifdef VBOX_WITH_VMDK_ESX
case VMDKETYPE_ESX_SPARSE:
#endif /* VBOX_WITH_VMDK_ESX */
- rc = vmdkGetSector(pImage, pExtent, uSectorExtentRel,
- &uSectorExtentAbs);
+ rc = vmdkGetSector(pImage, pIoCtx, pExtent, uSectorExtentRel, &uSectorExtentAbs);
if (RT_FAILURE(rc))
goto out;
/* Clip read range to at most the rest of the grain. */
@@ -6084,17 +5742,20 @@ static int vmdkRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
else
rc = vmdkStreamReadSequential(pImage, pExtent,
uSectorExtentRel,
- pvBuf, cbToRead);
+ pIoCtx, cbToRead);
}
else
{
if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
{
+ AssertMsg(vdIfIoIntIoCtxIsSynchronous(pImage->pIfIo, pIoCtx),
+ ("Async I/O is not supported for stream optimized VMDK's\n"));
+
uint32_t uSectorInGrain = uSectorExtentRel % pExtent->cSectorsPerGrain;
uSectorExtentAbs -= uSectorInGrain;
- uint64_t uLBA;
if (pExtent->uGrainSectorAbs != uSectorExtentAbs)
{
+ uint64_t uLBA = 0; /* gcc maybe uninitialized */
rc = vmdkFileInflateSync(pImage, pExtent,
VMDK_SECTOR2BYTE(uSectorExtentAbs),
pExtent->pvGrain,
@@ -6110,24 +5771,30 @@ static int vmdkRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
pExtent->uGrain = uSectorExtentRel / pExtent->cSectorsPerGrain;
Assert(uLBA == uSectorExtentRel);
}
- memcpy(pvBuf, (uint8_t *)pExtent->pvGrain + VMDK_SECTOR2BYTE(uSectorInGrain), cbToRead);
+ vdIfIoIntIoCtxCopyTo(pImage->pIfIo, pIoCtx,
+ (uint8_t *)pExtent->pvGrain
+ + VMDK_SECTOR2BYTE(uSectorInGrain),
+ cbToRead);
}
else
- {
- rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
+ rc = vdIfIoIntFileReadUser(pImage->pIfIo, pExtent->pFile->pStorage,
VMDK_SECTOR2BYTE(uSectorExtentAbs),
- pvBuf, cbToRead, NULL);
- }
+ pIoCtx, cbToRead);
}
break;
case VMDKETYPE_VMFS:
case VMDKETYPE_FLAT:
- rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
+ rc = vdIfIoIntFileReadUser(pImage->pIfIo, pExtent->pFile->pStorage,
VMDK_SECTOR2BYTE(uSectorExtentRel),
- pvBuf, cbToRead, NULL);
+ pIoCtx, cbToRead);
break;
case VMDKETYPE_ZERO:
- memset(pvBuf, '\0', cbToRead);
+ size_t cbSet;
+
+ cbSet = vdIfIoIntIoCtxSet(pImage->pIfIo, pIoCtx, 0, cbToRead);
+ Assert(cbSet == cbToRead);
+
+ rc = VINF_SUCCESS;
break;
}
if (pcbActuallyRead)
@@ -6139,11 +5806,12 @@ out:
}
/** @copydoc VBOXHDDBACKEND::pfnWrite */
-static int vmdkWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
- size_t cbToWrite, size_t *pcbWriteProcess,
- size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
+static int vmdkWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
+ PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
+ size_t *pcbPostRead, unsigned fWrite)
{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
+ LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
+ pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
PVMDKEXTENT pExtent;
uint64_t uSectorExtentRel;
@@ -6195,8 +5863,7 @@ static int vmdkWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
#ifdef VBOX_WITH_VMDK_ESX
case VMDKETYPE_ESX_SPARSE:
#endif /* VBOX_WITH_VMDK_ESX */
- rc = vmdkGetSector(pImage, pExtent, uSectorExtentRel,
- &uSectorExtentAbs);
+ rc = vmdkGetSector(pImage, pIoCtx, pExtent, uSectorExtentRel, &uSectorExtentAbs);
if (RT_FAILURE(rc))
goto out;
/* Clip write range to at most the rest of the grain. */
@@ -6214,13 +5881,12 @@ static int vmdkWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
if (cbToWrite == VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
{
/* Full block write to a previously unallocated block.
- * Check if the caller wants feedback. */
+ * Check if the caller wants to avoid the automatic alloc. */
if (!(fWrite & VD_WRITE_NO_ALLOC))
{
- /* Allocate GT and store the grain. */
- rc = vmdkAllocGrain(pImage, pExtent,
- uSectorExtentRel,
- pvBuf, cbToWrite);
+ /* Allocate GT and find out where to store the grain. */
+ rc = vmdkAllocGrain(pImage, pExtent, pIoCtx,
+ uSectorExtentRel, cbToWrite);
}
else
rc = VERR_VD_BLOCK_FREE;
@@ -6240,7 +5906,7 @@ static int vmdkWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
{
rc = vmdkStreamAllocGrain(pImage, pExtent,
uSectorExtentRel,
- pvBuf, cbToWrite);
+ pIoCtx, cbToWrite);
}
}
else
@@ -6256,9 +5922,10 @@ static int vmdkWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
}
else
{
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
+ Assert(!(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED));
+ rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pExtent->pFile->pStorage,
VMDK_SECTOR2BYTE(uSectorExtentAbs),
- pvBuf, cbToWrite, NULL);
+ pIoCtx, cbToWrite, NULL, NULL);
}
}
break;
@@ -6266,9 +5933,9 @@ static int vmdkWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
case VMDKETYPE_FLAT:
/* Clip write range to remain in this extent. */
cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
- rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
+ rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pExtent->pFile->pStorage,
VMDK_SECTOR2BYTE(uSectorExtentRel),
- pvBuf, cbToWrite, NULL);
+ pIoCtx, cbToWrite, NULL, NULL);
break;
case VMDKETYPE_ZERO:
/* Clip write range to remain in this extent. */
@@ -6285,19 +5952,11 @@ out:
}
/** @copydoc VBOXHDDBACKEND::pfnFlush */
-static int vmdkFlush(void *pBackendData)
+static int vmdkFlush(void *pBackendData, PVDIOCTX pIoCtx)
{
- LogFlowFunc(("pBackendData=%#p\n", pBackendData));
PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
- int rc = VINF_SUCCESS;
- AssertPtr(pImage);
-
- if (!(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
- rc = vmdkFlushImage(pImage);
-
- LogFlowFunc(("returns %Rrc\n", rc));
- return rc;
+ return vmdkFlushImage(pImage, pIoCtx);
}
/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
@@ -6314,6 +5973,20 @@ static unsigned vmdkGetVersion(void *pBackendData)
return 0;
}
+/** @copydoc VBOXHDDBACKEND::pfnGetSectorSize */
+static uint32_t vmdkGetSectorSize(void *pBackendData)
+{
+ LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+ PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+
+ AssertPtr(pImage);
+
+ if (pImage)
+ return 512;
+ else
+ return 0;
+}
+
/** @copydoc VBOXHDDBACKEND::pfnGetSize */
static uint64_t vmdkGetSize(void *pBackendData)
{
@@ -6529,7 +6202,9 @@ static int vmdkSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
int rc;
/* Image must be opened and the new flags must be valid. */
- if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL)))
+ if (!pImage || (uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
+ | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE
+ | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
{
rc = VERR_INVALID_PARAMETER;
goto out;
@@ -6542,12 +6217,13 @@ static int vmdkSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
rc = VINF_SUCCESS;
else
rc = VERR_INVALID_PARAMETER;
- goto out;
}
-
- /* Implement this operation via reopening the image. */
- vmdkFreeImage(pImage, false);
- rc = vmdkOpenImage(pImage, uOpenFlags);
+ else
+ {
+ /* Implement this operation via reopening the image. */
+ vmdkFreeImage(pImage, false);
+ rc = vmdkOpenImage(pImage, uOpenFlags);
+ }
out:
LogFlowFunc(("returns %Rrc\n", rc));
@@ -6871,307 +6547,6 @@ static void vmdkDump(void *pBackendData)
}
}
-/** @copydoc VBOXHDDBACKEND::pfnAsyncRead */
-static int vmdkAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbRead,
- PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
-{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
- pBackendData, uOffset, pIoCtx, cbRead, pcbActuallyRead));
- PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
- PVMDKEXTENT pExtent;
- uint64_t uSectorExtentRel;
- uint64_t uSectorExtentAbs;
- int rc;
-
- AssertPtr(pImage);
- Assert(uOffset % 512 == 0);
- Assert(cbRead % 512 == 0);
-
- if ( uOffset + cbRead > pImage->cbSize
- || cbRead == 0)
- {
- rc = VERR_INVALID_PARAMETER;
- goto out;
- }
-
- rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
- &pExtent, &uSectorExtentRel);
- if (RT_FAILURE(rc))
- goto out;
-
- /* Check access permissions as defined in the extent descriptor. */
- if (pExtent->enmAccess == VMDKACCESS_NOACCESS)
- {
- rc = VERR_VD_VMDK_INVALID_STATE;
- goto out;
- }
-
- /* Clip read range to remain in this extent. */
- cbRead = RT_MIN(cbRead, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
-
- /* Handle the read according to the current extent type. */
- switch (pExtent->enmType)
- {
- case VMDKETYPE_HOSTED_SPARSE:
-#ifdef VBOX_WITH_VMDK_ESX
- case VMDKETYPE_ESX_SPARSE:
-#endif /* VBOX_WITH_VMDK_ESX */
- rc = vmdkGetSectorAsync(pImage, pIoCtx, pExtent,
- uSectorExtentRel, &uSectorExtentAbs);
- if (RT_FAILURE(rc))
- goto out;
- /* Clip read range to at most the rest of the grain. */
- cbRead = RT_MIN(cbRead, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSectorExtentRel % pExtent->cSectorsPerGrain));
- Assert(!(cbRead % 512));
- if (uSectorExtentAbs == 0)
- rc = VERR_VD_BLOCK_FREE;
- else
- {
- AssertMsg(!(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED), ("Async I/O is not supported for stream optimized VMDK's\n"));
- rc = vdIfIoIntFileReadUserAsync(pImage->pIfIo, pExtent->pFile->pStorage,
- VMDK_SECTOR2BYTE(uSectorExtentAbs),
- pIoCtx, cbRead);
- }
- break;
- case VMDKETYPE_VMFS:
- case VMDKETYPE_FLAT:
- rc = vdIfIoIntFileReadUserAsync(pImage->pIfIo, pExtent->pFile->pStorage,
- VMDK_SECTOR2BYTE(uSectorExtentRel),
- pIoCtx, cbRead);
- break;
- case VMDKETYPE_ZERO:
- size_t cbSet;
-
- cbSet = vdIfIoIntIoCtxSet(pImage->pIfIo, pIoCtx, 0, cbRead);
- Assert(cbSet == cbRead);
-
- rc = VINF_SUCCESS;
- break;
- }
- if (pcbActuallyRead)
- *pcbActuallyRead = cbRead;
-
-out:
- LogFlowFunc(("returns %Rrc\n", rc));
- return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncWrite */
-static int vmdkAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbWrite,
- PVDIOCTX pIoCtx,
- size_t *pcbWriteProcess, size_t *pcbPreRead,
- size_t *pcbPostRead, unsigned fWrite)
-{
- LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
- pBackendData, uOffset, pIoCtx, cbWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
- PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
- PVMDKEXTENT pExtent;
- uint64_t uSectorExtentRel;
- uint64_t uSectorExtentAbs;
- int rc;
-
- AssertPtr(pImage);
- Assert(uOffset % 512 == 0);
- Assert(cbWrite % 512 == 0);
-
- if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
- {
- rc = VERR_VD_IMAGE_READ_ONLY;
- goto out;
- }
-
- if (cbWrite == 0)
- {
- rc = VERR_INVALID_PARAMETER;
- goto out;
- }
-
- /* No size check here, will do that later when the extent is located.
- * There are sparse images out there which according to the spec are
- * invalid, because the total size is not a multiple of the grain size.
- * Also for sparse images which are stitched together in odd ways (not at
- * grain boundaries, and with the nominal size not being a multiple of the
- * grain size), this would prevent writing to the last grain. */
-
- rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
- &pExtent, &uSectorExtentRel);
- if (RT_FAILURE(rc))
- goto out;
-
- /* Check access permissions as defined in the extent descriptor. */
- if (pExtent->enmAccess != VMDKACCESS_READWRITE)
- {
- rc = VERR_VD_VMDK_INVALID_STATE;
- goto out;
- }
-
- /* Handle the write according to the current extent type. */
- switch (pExtent->enmType)
- {
- case VMDKETYPE_HOSTED_SPARSE:
-#ifdef VBOX_WITH_VMDK_ESX
- case VMDKETYPE_ESX_SPARSE:
-#endif /* VBOX_WITH_VMDK_ESX */
- rc = vmdkGetSectorAsync(pImage, pIoCtx, pExtent, uSectorExtentRel,
- &uSectorExtentAbs);
- if (RT_FAILURE(rc))
- goto out;
- /* Clip write range to at most the rest of the grain. */
- cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSectorExtentRel % pExtent->cSectorsPerGrain));
- if ( pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED
- && uSectorExtentRel < (uint64_t)pExtent->uLastGrainAccess * pExtent->cSectorsPerGrain)
- {
- rc = VERR_VD_VMDK_INVALID_WRITE;
- goto out;
- }
- if (uSectorExtentAbs == 0)
- {
- if (cbWrite == VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
- {
- /* Full block write to a previously unallocated block.
- * Check if the caller wants to avoid the automatic alloc. */
- if (!(fWrite & VD_WRITE_NO_ALLOC))
- {
- /* Allocate GT and find out where to store the grain. */
- rc = vmdkAllocGrainAsync(pImage, pExtent, pIoCtx,
- uSectorExtentRel, cbWrite);
- }
- else
- rc = VERR_VD_BLOCK_FREE;
- *pcbPreRead = 0;
- *pcbPostRead = 0;
- }
- else
- {
- /* Clip write range to remain in this extent. */
- cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
- *pcbPreRead = VMDK_SECTOR2BYTE(uSectorExtentRel % pExtent->cSectorsPerGrain);
- *pcbPostRead = VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) - cbWrite - *pcbPreRead;
- rc = VERR_VD_BLOCK_FREE;
- }
- }
- else
- {
- Assert(!(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED));
- rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pExtent->pFile->pStorage,
- VMDK_SECTOR2BYTE(uSectorExtentAbs),
- pIoCtx, cbWrite, NULL, NULL);
- }
- break;
- case VMDKETYPE_VMFS:
- case VMDKETYPE_FLAT:
- /* Clip write range to remain in this extent. */
- cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
- rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pExtent->pFile->pStorage,
- VMDK_SECTOR2BYTE(uSectorExtentRel),
- pIoCtx, cbWrite, NULL, NULL);
- break;
- case VMDKETYPE_ZERO:
- /* Clip write range to remain in this extent. */
- cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
- break;
- }
-
- if (pcbWriteProcess)
- *pcbWriteProcess = cbWrite;
-
-out:
- LogFlowFunc(("returns %Rrc\n", rc));
- return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncFlush */
-static int vmdkAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
-{
- PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
- PVMDKEXTENT pExtent;
- int rc = VINF_SUCCESS;
-
- /* Update descriptor if changed. */
- /** @todo: The descriptor is never updated because
- * it remains unchanged during normal operation (only vmdkRename updates it).
- * So this part is actually not tested so far and requires testing as soon
- * as the descriptor might change during async I/O.
- */
- if (pImage->Descriptor.fDirty)
- {
- rc = vmdkWriteDescriptorAsync(pImage, pIoCtx);
- if ( RT_FAILURE(rc)
- && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
- goto out;
- }
-
- for (unsigned i = 0; i < pImage->cExtents; i++)
- {
- pExtent = &pImage->pExtents[i];
- if (pExtent->pFile != NULL && pExtent->fMetaDirty)
- {
- switch (pExtent->enmType)
- {
- case VMDKETYPE_HOSTED_SPARSE:
-#ifdef VBOX_WITH_VMDK_ESX
- case VMDKETYPE_ESX_SPARSE:
-#endif /* VBOX_WITH_VMDK_ESX */
- rc = vmdkWriteMetaSparseExtentAsync(pImage, pExtent, 0, pIoCtx);
- if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
- goto out;
- if (pExtent->fFooter)
- {
- uint64_t uFileOffset = pExtent->uAppendPosition;
- if (!uFileOffset)
- {
- rc = VERR_INTERNAL_ERROR;
- goto out;
- }
- uFileOffset = RT_ALIGN_64(uFileOffset, 512);
- rc = vmdkWriteMetaSparseExtent(pImage, pExtent, uFileOffset);
- if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
- goto out;
- }
- break;
- case VMDKETYPE_VMFS:
- case VMDKETYPE_FLAT:
- /* Nothing to do. */
- break;
- case VMDKETYPE_ZERO:
- default:
- AssertMsgFailed(("extent with type %d marked as dirty\n",
- pExtent->enmType));
- break;
- }
- }
- switch (pExtent->enmType)
- {
- case VMDKETYPE_HOSTED_SPARSE:
-#ifdef VBOX_WITH_VMDK_ESX
- case VMDKETYPE_ESX_SPARSE:
-#endif /* VBOX_WITH_VMDK_ESX */
- case VMDKETYPE_VMFS:
- case VMDKETYPE_FLAT:
- /*
- * Don't ignore block devices like in the sync case
- * (they have an absolute path).
- * We might have unwritten data in the writeback cache and
- * the async I/O manager will handle these requests properly
- * even if the block device doesn't support these requests.
- */
- if ( pExtent->pFile != NULL
- && !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
- rc = vdIfIoIntFileFlushAsync(pImage->pIfIo, pExtent->pFile->pStorage,
- pIoCtx, NULL, NULL);
- break;
- case VMDKETYPE_ZERO:
- /* No need to do anything for this extent. */
- break;
- default:
- AssertMsgFailed(("unknown extent type %d\n", pExtent->enmType));
- break;
- }
- }
-
-out:
- return rc;
-}
VBOXHDDBACKEND g_VmdkBackend =
@@ -7206,8 +6581,12 @@ VBOXHDDBACKEND g_VmdkBackend =
vmdkWrite,
/* pfnFlush */
vmdkFlush,
+ /* pfnDiscard */
+ NULL,
/* pfnGetVersion */
vmdkGetVersion,
+ /* pfnGetSectorSize */
+ vmdkGetSectorSize,
/* pfnGetSize */
vmdkGetSize,
/* pfnGetFileSize */
@@ -7258,12 +6637,6 @@ VBOXHDDBACKEND g_VmdkBackend =
NULL,
/* pfnSetParentFilename */
NULL,
- /* pfnAsyncRead */
- vmdkAsyncRead,
- /* pfnAsyncWrite */
- vmdkAsyncWrite,
- /* pfnAsyncFlush */
- vmdkAsyncFlush,
/* pfnComposeLocation */
genericFileComposeLocation,
/* pfnComposeName */
@@ -7272,10 +6645,6 @@ VBOXHDDBACKEND g_VmdkBackend =
NULL,
/* pfnResize */
NULL,
- /* pfnDiscard */
- NULL,
- /* pfnAsyncDiscard */
- NULL,
/* pfnRepair */
NULL
};
diff --git a/src/VBox/Storage/testcase/Makefile.kmk b/src/VBox/Storage/testcase/Makefile.kmk
index 3d0f2b31..9d44e45c 100644
--- a/src/VBox/Storage/testcase/Makefile.kmk
+++ b/src/VBox/Storage/testcase/Makefile.kmk
@@ -22,7 +22,7 @@ include $(KBUILD_PATH)/subheader.kmk
# vditool - useful too for manipulating VDIs, but now pretty obsolete and
# probably will go away soon. Testcase only now.
#
-ifdef VBOX_WITH_TESTCASES
+if defined(VBOX_WITH_TESTCASES) || defined(VBOX_WITH_VDITOOL)
PROGRAMS += vditool
vditool_TEMPLATE = VBOXR3TSTEXE
vditool_LIBS = $(LIB_DDU)
@@ -33,7 +33,7 @@ endif
# Basic testcases for the VD code.
#
ifdef VBOX_WITH_TESTCASES
- PROGRAMS += tstVD tstVD-2 tstVDCopy tstVDSnap tstVDShareable vbox-img
+ PROGRAMS += tstVD tstVD-2 tstVDCopy tstVDSnap tstVDShareable
tstVD_TEMPLATE = VBOXR3TSTEXE
tstVD_SOURCES = tstVD.cpp
@@ -52,9 +52,14 @@ ifdef VBOX_WITH_TESTCASES
tstVDIo_TEMPLATE = VBOXR3TSTEXE
tstVDIo_SOURCES = tstVDIo.cpp \
+ VDIoBackend.cpp \
VDIoBackendMem.cpp \
VDMemDisk.cpp \
- VDIoRnd.cpp
+ VDIoRnd.cpp \
+ VDScript.cpp \
+ VDScriptAst.cpp \
+ VDScriptChecker.cpp \
+ VDScriptInterp.cpp
tstVDIo_LIBS = \
$(LIB_DDU) \
$(PATH_STAGE_LIB)/StorageDbgLib$(VBOX_SUFF_LIB)
@@ -70,27 +75,32 @@ ifdef VBOX_WITH_TESTCASES
tstVDSnap_TEMPLATE = VBOXR3TSTEXE
tstVDSnap_LIBS = $(LIB_DDU)
tstVDSnap_SOURCES = tstVDSnap.cpp
+endif
+
+if defined(VBOX_WITH_TESTCASES) || defined(VBOX_WITH_VBOX_IMG)
+ PROGRAMS += vbox-img
#
# vbox-img - static because it migth be used as at standalone tool.
#
- vbox-img_TEMPLATE = VBOXR3STATIC
- vbox-img_DEFS += IN_VBOXDDU IN_VBOXDDU_STATIC VBOX_HDD_NO_DYNAMIC_BACKENDS IN_RT_R3
+ vbox-img_TEMPLATE = VBoxR3Static
+ vbox-img_DEFS += IN_VBOXDDU IN_VBOXDDU_STATIC VBOX_HDD_NO_DYNAMIC_BACKENDS
vbox-img_SOURCES = \
vbox-img.cpp \
- $(VBOX_PATH_STORAGE_SRC)/VD.cpp \
- $(VBOX_PATH_STORAGE_SRC)/VDVfs.cpp \
- $(VBOX_PATH_STORAGE_SRC)/VDI.cpp \
- $(VBOX_PATH_STORAGE_SRC)/VMDK.cpp \
- $(VBOX_PATH_STORAGE_SRC)/VHD.cpp \
- $(VBOX_PATH_STORAGE_SRC)/DMG.cpp \
- $(VBOX_PATH_STORAGE_SRC)/Parallels.cpp \
- $(VBOX_PATH_STORAGE_SRC)/ISCSI.cpp \
- $(VBOX_PATH_STORAGE_SRC)/RAW.cpp \
- $(VBOX_PATH_STORAGE_SRC)/QED.cpp \
- $(VBOX_PATH_STORAGE_SRC)/QCOW.cpp \
- $(VBOX_PATH_STORAGE_SRC)/VHDX.cpp \
- $(VBOX_PATH_STORAGE_SRC)/VCICache.cpp
+ ../VD.cpp \
+ ../VDVfs.cpp \
+ ../VDI.cpp \
+ ../VMDK.cpp \
+ ../VHD.cpp \
+ ../DMG.cpp \
+ ../Parallels.cpp \
+ ../ISCSI.cpp \
+ ../RAW.cpp \
+ ../QED.cpp \
+ ../QCOW.cpp \
+ ../VHDX.cpp \
+ ../VCICache.cpp \
+ ../VDIfVfs.cpp
vbox-img_LIBS = \
$(VBOX_LIB_RUNTIME_STATIC)
if1of ($(KBUILD_TARGET),os2 win)
@@ -104,6 +114,9 @@ ifdef VBOX_WITH_TESTCASES
endif
ifeq ($(KBUILD_TARGET),linux)
vbox-img_LIBS += crypt
+ ifdef SDK_VBOX_LIBXML2_LIBS
+ vbox-img_LIBS += xml2
+ endif
else if1of ($(KBUILD_TARGET),darwin freebsd)
vbox-img_LIBS += iconv
else ifeq ($(KBUILD_TARGET),win)
diff --git a/src/VBox/Storage/testcase/VDDefs.h b/src/VBox/Storage/testcase/VDDefs.h
new file mode 100644
index 00000000..729a8fff
--- /dev/null
+++ b/src/VBox/Storage/testcase/VDDefs.h
@@ -0,0 +1,38 @@
+/** $Id: VDDefs.h $ */
+/** @file
+ *
+ * VBox HDD container test utility, common definitions.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#ifndef __VDDefs_h__
+#define __VDDefs_h__
+
+#include <iprt/sg.h>
+
+/**
+ * I/O transfer direction.
+ */
+typedef enum VDIOTXDIR
+{
+ /** Read. */
+ VDIOTXDIR_READ = 0,
+ /** Write. */
+ VDIOTXDIR_WRITE,
+ /** Flush. */
+ VDIOTXDIR_FLUSH,
+ /** Invalid. */
+ VDIOTXDIR_INVALID
+} VDIOTXDIR;
+
+#endif /* __VDDefs_h__ */
diff --git a/src/VBox/Storage/testcase/VDIoBackend.cpp b/src/VBox/Storage/testcase/VDIoBackend.cpp
new file mode 100644
index 00000000..6458d7df
--- /dev/null
+++ b/src/VBox/Storage/testcase/VDIoBackend.cpp
@@ -0,0 +1,283 @@
+/** $Id: VDIoBackend.cpp $ */
+/** @file
+ *
+ * VBox HDD container test utility, I/O backend API
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#define LOGGROUP LOGGROUP_DEFAULT /** @todo: Log group */
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/mem.h>
+#include <iprt/file.h>
+#include <iprt/aiomgr.h>
+#include <iprt/string.h>
+
+#include "VDIoBackend.h"
+#include "VDMemDisk.h"
+#include "VDIoBackendMem.h"
+
+typedef struct VDIOBACKEND
+{
+ /** Memory I/O backend handle. */
+ PVDIOBACKENDMEM pIoMem;
+ /** Async I/O manager. */
+ RTAIOMGR hAioMgr;
+ /** Users of the memory backend. */
+ volatile uint32_t cRefsIoMem;
+ /** Users of the file backend. */
+ volatile uint32_t cRefsFile;
+} VDIOBACKEND;
+
+typedef struct VDIOSTORAGE
+{
+ /** Pointer to the I/O backend parent. */
+ PVDIOBACKEND pIoBackend;
+ /** Completion callback. */
+ PFNVDIOCOMPLETE pfnComplete;
+ /** Flag whether this storage is backed by a file or memory.disk. */
+ bool fMemory;
+ /** Type dependent data. */
+ union
+ {
+ /** Memory disk handle. */
+ PVDMEMDISK pMemDisk;
+ struct
+ {
+ /** file handle. */
+ RTFILE hFile;
+ /** I/O manager file handle. */
+ RTAIOMGRFILE hAioMgrFile;
+ } File;
+ } u;
+} VDIOSTORAGE;
+
+static DECLCALLBACK(void) vdIoBackendFileIoComplete(RTAIOMGRFILE hAioMgrFile, int rcReq, void *pvUser)
+{
+ PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTAioMgrFileGetUser(hAioMgrFile);
+ pIoStorage->pfnComplete(pvUser, rcReq);
+}
+
+int VDIoBackendCreate(PPVDIOBACKEND ppIoBackend)
+{
+ int rc = VINF_SUCCESS;
+ PVDIOBACKEND pIoBackend;
+
+ pIoBackend = (PVDIOBACKEND)RTMemAllocZ(sizeof(VDIOBACKEND));
+ if (pIoBackend)
+ {
+ pIoBackend->hAioMgr = NIL_RTAIOMGR;
+ *ppIoBackend = pIoBackend;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+void VDIoBackendDestroy(PVDIOBACKEND pIoBackend)
+{
+ if (pIoBackend->pIoMem)
+ VDIoBackendMemDestroy(pIoBackend->pIoMem);
+ if (pIoBackend->hAioMgr)
+ RTAioMgrRelease(pIoBackend->hAioMgr);
+ RTMemFree(pIoBackend);
+}
+
+int VDIoBackendStorageCreate(PVDIOBACKEND pIoBackend, const char *pszBackend,
+ const char *pszName, PFNVDIOCOMPLETE pfnComplete,
+ PPVDIOSTORAGE ppIoStorage)
+{
+ int rc = VINF_SUCCESS;
+ PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTMemAllocZ(sizeof(VDIOSTORAGE));
+
+ if (pIoStorage)
+ {
+ pIoStorage->pIoBackend = pIoBackend;
+ pIoStorage->pfnComplete = pfnComplete;
+ if (!strcmp(pszBackend, "memory"))
+ {
+ pIoStorage->fMemory = true;
+ rc = VDMemDiskCreate(&pIoStorage->u.pMemDisk, 0 /* Growing */);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t cRefs = ASMAtomicIncU32(&pIoBackend->cRefsIoMem);
+ if ( cRefs == 1
+ && !pIoBackend->pIoMem)
+ {
+ rc = VDIoBackendMemCreate(&pIoBackend->pIoMem);
+ if (RT_FAILURE(rc))
+ VDMemDiskDestroy(pIoStorage->u.pMemDisk);
+ }
+ }
+ }
+ else if (!strcmp(pszBackend, "file"))
+ {
+ pIoStorage->fMemory = false;
+ uint32_t cRefs = ASMAtomicIncU32(&pIoBackend->cRefsFile);
+ if ( cRefs == 1
+ && pIoBackend->hAioMgr == NIL_RTAIOMGR)
+ rc = RTAioMgrCreate(&pIoBackend->hAioMgr, 1024);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Create file. */
+ rc = RTFileOpen(&pIoStorage->u.File.hFile, pszName,
+ RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_ASYNC_IO | RTFILE_O_NO_CACHE | RTFILE_O_DENY_NONE);
+ if (RT_SUCCESS(rc))
+ {
+ /* Create file handle for I/O manager. */
+ rc = RTAioMgrFileCreate(pIoBackend->hAioMgr, pIoStorage->u.File.hFile,
+ vdIoBackendFileIoComplete, pIoStorage,
+ &pIoStorage->u.File.hAioMgrFile);
+ if (RT_FAILURE(rc))
+ RTFileClose(pIoStorage->u.File.hFile);
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ ASMAtomicDecU32(&pIoBackend->cRefsFile);
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+
+ if (RT_FAILURE(rc))
+ RTMemFree(pIoStorage);
+ else
+ *ppIoStorage = pIoStorage;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+void VDIoBackendStorageDestroy(PVDIOSTORAGE pIoStorage)
+{
+ if (pIoStorage->fMemory)
+ {
+ VDMemDiskDestroy(pIoStorage->u.pMemDisk);
+ ASMAtomicDecU32(&pIoStorage->pIoBackend->cRefsIoMem);
+ }
+ else
+ {
+ RTAioMgrFileRelease(pIoStorage->u.File.hAioMgrFile);
+ RTFileClose(pIoStorage->u.File.hFile);
+ ASMAtomicDecU32(&pIoStorage->pIoBackend->cRefsFile);
+ }
+ RTMemFree(pIoStorage);
+}
+
+int VDIoBackendTransfer(PVDIOSTORAGE pIoStorage, VDIOTXDIR enmTxDir, uint64_t off,
+ size_t cbTransfer, PRTSGBUF pSgBuf, void *pvUser, bool fSync)
+{
+ int rc = VINF_SUCCESS;
+
+ if (pIoStorage->fMemory)
+ {
+ if (!fSync)
+ {
+ rc = VDIoBackendMemTransfer(pIoStorage->pIoBackend->pIoMem, pIoStorage->u.pMemDisk,
+ enmTxDir, off, cbTransfer, pSgBuf, pIoStorage->pfnComplete,
+ pvUser);
+ }
+ else
+ {
+ switch (enmTxDir)
+ {
+ case VDIOTXDIR_READ:
+ rc = VDMemDiskRead(pIoStorage->u.pMemDisk, off, cbTransfer, pSgBuf);
+ break;
+ case VDIOTXDIR_WRITE:
+ rc = VDMemDiskWrite(pIoStorage->u.pMemDisk, off, cbTransfer, pSgBuf);
+ break;
+ case VDIOTXDIR_FLUSH:
+ break;
+ default:
+ AssertMsgFailed(("Invalid transfer type %d\n", enmTxDir));
+ }
+ }
+ }
+ else
+ {
+ if (!fSync)
+ {
+ switch (enmTxDir)
+ {
+ case VDIOTXDIR_READ:
+ rc = RTAioMgrFileRead(pIoStorage->u.File.hAioMgrFile, off, pSgBuf, cbTransfer, pvUser);
+ break;
+ case VDIOTXDIR_WRITE:
+ rc = RTAioMgrFileWrite(pIoStorage->u.File.hAioMgrFile, off, pSgBuf, cbTransfer, pvUser);
+ break;
+ case VDIOTXDIR_FLUSH:
+ rc = RTAioMgrFileFlush(pIoStorage->u.File.hAioMgrFile, pvUser);
+ break;
+ default:
+ AssertMsgFailed(("Invalid transfer type %d\n", enmTxDir));
+ }
+ if (rc == VERR_FILE_AIO_IN_PROGRESS)
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ switch (enmTxDir)
+ {
+ case VDIOTXDIR_READ:
+ rc = RTFileSgReadAt(pIoStorage->u.File.hFile, off, pSgBuf, cbTransfer, NULL);
+ break;
+ case VDIOTXDIR_WRITE:
+ rc = RTFileSgWriteAt(pIoStorage->u.File.hFile, off, pSgBuf, cbTransfer, NULL);
+ break;
+ case VDIOTXDIR_FLUSH:
+ rc = RTFileFlush(pIoStorage->u.File.hFile);
+ break;
+ default:
+ AssertMsgFailed(("Invalid transfer type %d\n", enmTxDir));
+ }
+ }
+ }
+
+ return rc;
+}
+
+int VDIoBackendStorageSetSize(PVDIOSTORAGE pIoStorage, uint64_t cbSize)
+{
+ int rc = VINF_SUCCESS;
+
+ if (pIoStorage->fMemory)
+ {
+ rc = VDMemDiskSetSize(pIoStorage->u.pMemDisk, cbSize);
+ }
+ else
+ rc = RTFileSetSize(pIoStorage->u.File.hFile, cbSize);
+
+ return rc;
+}
+
+int VDIoBackendStorageGetSize(PVDIOSTORAGE pIoStorage, uint64_t *pcbSize)
+{
+ int rc = VINF_SUCCESS;
+
+ if (pIoStorage->fMemory)
+ {
+ rc = VDMemDiskGetSize(pIoStorage->u.pMemDisk, pcbSize);
+ }
+ else
+ rc = RTFileGetSize(pIoStorage->u.File.hFile, pcbSize);
+
+ return rc;
+}
+
diff --git a/src/VBox/Storage/testcase/VDIoBackend.h b/src/VBox/Storage/testcase/VDIoBackend.h
new file mode 100644
index 00000000..bec1bb7d
--- /dev/null
+++ b/src/VBox/Storage/testcase/VDIoBackend.h
@@ -0,0 +1,90 @@
+/** $Id: VDIoBackend.h $ */
+/** @file
+ *
+ * VBox HDD container test utility, async I/O backend
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#ifndef __VDIoBackend_h__
+#define __VDIoBackend_h__
+
+#include <iprt/sg.h>
+
+#include "VDDefs.h"
+
+/** I/O backend handle. */
+typedef struct VDIOBACKEND *PVDIOBACKEND;
+/** Pointer to a I/O backend handle. */
+typedef PVDIOBACKEND *PPVDIOBACKEND;
+
+/** Storage handle. */
+typedef struct VDIOSTORAGE *PVDIOSTORAGE;
+/** Pointer to a storage handle. */
+typedef PVDIOSTORAGE *PPVDIOSTORAGE;
+
+/**
+ * Completion handler.
+ *
+ * @returns nothing.
+ * @param pvUser Opaque user data.
+ * @param rcReq Completion code for the request.
+ */
+typedef DECLCALLBACK(int) FNVDIOCOMPLETE(void *pvUser, int rcReq);
+/** Pointer to a completion handler. */
+typedef FNVDIOCOMPLETE *PFNVDIOCOMPLETE;
+
+/**
+ * Creates a new memory I/O backend.
+ *
+ * @returns IPRT status code.
+ *
+ * @param ppIoBackend Where to store the handle on success.
+ */
+int VDIoBackendCreate(PPVDIOBACKEND ppIoBackend);
+
+/**
+ * Destroys a memory I/O backend.
+ *
+ * @returns nothing.
+ *
+ * @param pIoBackend The backend to destroy.
+ */
+void VDIoBackendDestroy(PVDIOBACKEND pIoBackend);
+
+int VDIoBackendStorageCreate(PVDIOBACKEND pIoBackend, const char *pszBackend,
+ const char *pszName, PFNVDIOCOMPLETE pfnComplete,
+ PPVDIOSTORAGE ppIoStorage);
+
+void VDIoBackendStorageDestroy(PVDIOSTORAGE pIoStorage);
+
+int VDIoBackendStorageSetSize(PVDIOSTORAGE pIoStorage, uint64_t cbSize);
+
+int VDIoBackendStorageGetSize(PVDIOSTORAGE pIoStorage, uint64_t *pcbSize);
+
+/**
+ * Enqueues a new I/O request.
+ *
+ * @returns IPRT status code.
+ *
+ * @param pIoStorage Storage handle.
+ * @param enmTxDir The transfer direction.
+ * @param off Start offset of the transfer.
+ * @param cbTransfer Size of the transfer.
+ * @param pSgBuf S/G buffer to use.
+ * @param pvUser Opaque user data.
+ * @param fSync Flag whether to wait for the operation to complete.
+ */
+int VDIoBackendTransfer(PVDIOSTORAGE pIoStorage, VDIOTXDIR enmTxDir, uint64_t off,
+ size_t cbTransfer, PRTSGBUF pSgBuf, void *pvUser, bool fSync);
+
+#endif /* __VDIoBackendMem_h__ */
diff --git a/src/VBox/Storage/testcase/VDIoBackendMem.cpp b/src/VBox/Storage/testcase/VDIoBackendMem.cpp
index a3ab1793..b572fe77 100644
--- a/src/VBox/Storage/testcase/VDIoBackendMem.cpp
+++ b/src/VBox/Storage/testcase/VDIoBackendMem.cpp
@@ -5,7 +5,7 @@
*/
/*
- * Copyright (C) 2011 Oracle Corporation
+ * Copyright (C) 2011-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -43,13 +43,13 @@ typedef struct VDIOBACKENDREQ
uint64_t off;
/** Size of the transfer. */
size_t cbTransfer;
- /** Number of segments in the array. */
- unsigned cSegs;
/** Completion handler to call. */
PFNVDIOCOMPLETE pfnComplete;
/** Opaque user data. */
void *pvUser;
- /** Segment array - variable in size */
+ /** S/G buffer. */
+ RTSGBUF SgBuf;
+ /** Segment array - variable size. */
RTSGSEG aSegs[1];
} VDIOBACKENDREQ, *PVDIOBACKENDREQ;
@@ -142,15 +142,19 @@ int VDIoBackendMemDestroy(PVDIOBACKENDMEM pIoBackend)
}
int VDIoBackendMemTransfer(PVDIOBACKENDMEM pIoBackend, PVDMEMDISK pMemDisk,
- VDIOTXDIR enmTxDir, uint64_t off, size_t cbTransfer, PCRTSGSEG paSegs,
- unsigned cSegs, PFNVDIOCOMPLETE pfnComplete, void *pvUser)
+ VDIOTXDIR enmTxDir, uint64_t off, size_t cbTransfer,
+ PRTSGBUF pSgBuf, PFNVDIOCOMPLETE pfnComplete, void *pvUser)
{
PVDIOBACKENDREQ pReq = NULL;
PPVDIOBACKENDREQ ppReq = NULL;
size_t cbData;
+ unsigned cSegs = 0;
LogFlowFunc(("Queuing request\n"));
+ if (enmTxDir != VDIOTXDIR_FLUSH)
+ RTSgBufSegArrayCreate(pSgBuf, NULL, &cSegs, cbTransfer);
+
pReq = (PVDIOBACKENDREQ)RTMemAlloc(RT_OFFSETOF(VDIOBACKENDREQ, aSegs[cSegs]));
if (!pReq)
return VERR_NO_MEMORY;
@@ -167,13 +171,12 @@ int VDIoBackendMemTransfer(PVDIOBACKENDMEM pIoBackend, PVDMEMDISK pMemDisk,
pReq->cbTransfer = cbTransfer;
pReq->off = off;
pReq->pMemDisk = pMemDisk;
- pReq->cSegs = cSegs;
pReq->pfnComplete = pfnComplete;
pReq->pvUser = pvUser;
- for (unsigned i = 0; i < cSegs; i++)
+ if (enmTxDir != VDIOTXDIR_FLUSH)
{
- pReq->aSegs[i].pvSeg = paSegs[i].pvSeg;
- pReq->aSegs[i].cbSeg = paSegs[i].cbSeg;
+ RTSgBufSegArrayCreate(pSgBuf, &pReq->aSegs[0], &cSegs, cbTransfer);
+ RTSgBufInit(&pReq->SgBuf, pReq->aSegs, cSegs);
}
*ppReq = pReq;
@@ -225,16 +228,12 @@ static int vdIoBackendMemThread(RTTHREAD hThread, void *pvUser)
{
case VDIOTXDIR_READ:
{
- RTSGBUF SgBuf;
- RTSgBufInit(&SgBuf, pReq->aSegs, pReq->cSegs);
- rcReq = VDMemDiskRead(pReq->pMemDisk, pReq->off, pReq->cbTransfer, &SgBuf);
+ rcReq = VDMemDiskRead(pReq->pMemDisk, pReq->off, pReq->cbTransfer, &pReq->SgBuf);
break;
}
case VDIOTXDIR_WRITE:
{
- RTSGBUF SgBuf;
- RTSgBufInit(&SgBuf, pReq->aSegs, pReq->cSegs);
- rcReq = VDMemDiskWrite(pReq->pMemDisk, pReq->off, pReq->cbTransfer, &SgBuf);
+ rcReq = VDMemDiskWrite(pReq->pMemDisk, pReq->off, pReq->cbTransfer, &pReq->SgBuf);
break;
}
case VDIOTXDIR_FLUSH:
diff --git a/src/VBox/Storage/testcase/VDIoBackendMem.h b/src/VBox/Storage/testcase/VDIoBackendMem.h
index 89b16dc1..3e1f5b84 100644
--- a/src/VBox/Storage/testcase/VDIoBackendMem.h
+++ b/src/VBox/Storage/testcase/VDIoBackendMem.h
@@ -20,20 +20,7 @@
#include <iprt/sg.h>
-/**
- * I/O transfer direction.
- */
-typedef enum VDIOTXDIR
-{
- /** Read. */
- VDIOTXDIR_READ = 0,
- /** Write. */
- VDIOTXDIR_WRITE,
- /** Flush. */
- VDIOTXDIR_FLUSH,
- /** Invalid. */
- VDIOTXDIR_INVALID
-} VDIOTXDIR;
+#include "VDDefs.h"
/** Memory backend handle. */
typedef struct VDIOBACKENDMEM *PVDIOBACKENDMEM;
@@ -85,8 +72,7 @@ int VDIoBackendMemDestroy(PVDIOBACKENDMEM pIoBackend);
* @param pvUser Opaque user data.
*/
int VDIoBackendMemTransfer(PVDIOBACKENDMEM pIoBackend, PVDMEMDISK pMemDisk,
- VDIOTXDIR enmTxDir, uint64_t off, size_t cbTransfer, PCRTSGSEG paSegs,
- unsigned cSegs,
- PFNVDIOCOMPLETE pfnComplete, void *pvUser);
+ VDIOTXDIR enmTxDir, uint64_t off, size_t cbTransfer,
+ PRTSGBUF pSgBuf, PFNVDIOCOMPLETE pfnComplete, void *pvUser);
#endif /* __VDIoBackendMem_h__ */
diff --git a/src/VBox/Storage/testcase/VDScript.cpp b/src/VBox/Storage/testcase/VDScript.cpp
new file mode 100644
index 00000000..63786311
--- /dev/null
+++ b/src/VBox/Storage/testcase/VDScript.cpp
@@ -0,0 +1,2733 @@
+/** $Id: VDScript.cpp $ */
+/** @file
+ *
+ * VBox HDD container test utility - scripting engine.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/** @page pg_vd_script VDScript - Simple scripting language for VD I/O testing.
+ *
+ * This component implements a very simple scripting language to make testing the VD
+ * library more flexible and testcases faster to implement without the need to recompile
+ * everything after it changed.
+ * The language is a small subset of the C language. It doesn't support unions, structs,
+ * global variables, typedefed types or pointers (yet). It also adds a boolean and a string type.
+ * Strings are immutable and only to print messages from the script.
+ * There are also not the default types like int or unsigned because theire ranges are architecture
+ * dependent. Instead VDScript uses uint8_t, int8_t, ... as primitive types.
+ *
+ * Why inventing a completely new language?
+ *
+ * Well it is not a completely new language to start with, it is a subset of C and the
+ * language can be extended later on to reach the full C language later on.
+ * Second, there is no static typed scripting language I like which could be implemented
+ * and finally because I can ;)
+ * The code implementing the scripting engine is designed to be easily incorporated into other
+ * code. Could be used as a scripting language for the VBox debugger for example or in the scm
+ * tool to automatically rewrite C code using the AST VDSCript generates...
+ *
+ * The syntax of VDSCript is derived from the C syntax. The syntax of C in BNF was taken
+ * from: http://www.csci.csusb.edu/dick/samples/c.syntax.html
+ * and: http://slps.github.com/zoo/c/iso-9899-tc3.html
+ * and: http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf
+ */
+#define LOGGROUP LOGGROUP_DEFAULT
+#include <iprt/string.h>
+#include <iprt/list.h>
+#include <iprt/mem.h>
+#include <iprt/ctype.h>
+#include <iprt/stream.h>
+
+#include <VBox/log.h>
+
+#include "VDScriptAst.h"
+#include "VDScriptInternal.h"
+
+/**
+ * VD script token class.
+ */
+typedef enum VDTOKENCLASS
+{
+ /** Invalid. */
+ VDTOKENCLASS_INVALID = 0,
+ /** Identifier class. */
+ VDTOKENCLASS_IDENTIFIER,
+ /** Numerical constant. */
+ VDTOKENCLASS_NUMCONST,
+ /** String constant. */
+ VDTOKENCLASS_STRINGCONST,
+ /** Operators */
+ VDTOKENCLASS_OPERATORS,
+ /** Reserved keyword */
+ VDTOKENCLASS_KEYWORD,
+ /** Punctuator */
+ VDTOKENCLASS_PUNCTUATOR,
+ /** End of stream */
+ VDTOKENCLASS_EOS,
+ /** 32bit hack. */
+ VDTOKENCLASS_32BIT_HACK = 0x7fffffff
+} VDTOKENCLASS;
+/** Pointer to a token class. */
+typedef VDTOKENCLASS *PVDTOKENCLASS;
+
+/**
+ * Keyword types.
+ */
+typedef enum VDSCRIPTTOKENKEYWORD
+{
+ VDSCRIPTTOKENKEYWORD_INVALID = 0,
+ VDSCRIPTTOKENKEYWORD_CONTINUE,
+ VDSCRIPTTOKENKEYWORD_DEFAULT,
+ VDSCRIPTTOKENKEYWORD_RETURN,
+ VDSCRIPTTOKENKEYWORD_SWITCH,
+ VDSCRIPTTOKENKEYWORD_WHILE,
+ VDSCRIPTTOKENKEYWORD_BREAK,
+ VDSCRIPTTOKENKEYWORD_FALSE,
+ VDSCRIPTTOKENKEYWORD_TRUE,
+ VDSCRIPTTOKENKEYWORD_ELSE,
+ VDSCRIPTTOKENKEYWORD_CASE,
+ VDSCRIPTTOKENKEYWORD_FOR,
+ VDSCRIPTTOKENKEYWORD_IF,
+ VDSCRIPTTOKENKEYWORD_DO,
+ VDSCRIPTTOKENKEYWORD_32BIT_HACK = 0x7fffffff
+} VDSCRIPTTOKENKEYWORD;
+/** Pointer to a keyword type. */
+typedef VDSCRIPTTOKENKEYWORD *PVDSCRIPTTOKENKEYWORD;
+
+/**
+ * VD script token.
+ */
+typedef struct VDSCRIPTTOKEN
+{
+ /** Token class. */
+ VDTOKENCLASS enmClass;
+ /** Token position in the source buffer. */
+ VDSRCPOS Pos;
+ /** Data based on the token class. */
+ union
+ {
+ /** Identifier. */
+ struct
+ {
+ /** Pointer to the start of the identifier. */
+ const char *pszIde;
+ /** Number of characters for the identifier excluding the null terminator. */
+ size_t cchIde;
+ } Ide;
+ /** Numerical constant. */
+ struct
+ {
+ uint64_t u64;
+ } NumConst;
+ /** String constant */
+ struct
+ {
+ /** Pointer to the start of the string constant. */
+ const char *pszString;
+ /** Number of characters of the string, including the null terminator. */
+ size_t cchString;
+ } StringConst;
+ /** Operator */
+ struct
+ {
+ /** The operator string. */
+ char aszOp[4]; /** Maximum of 3 for >>= + null terminator. */
+ } Operator;
+ /** Keyword. */
+ struct
+ {
+ /** The keyword type. */
+ VDSCRIPTTOKENKEYWORD enmKeyword;
+ } Keyword;
+ /** Punctuator. */
+ struct
+ {
+ /** The punctuator in question. */
+ char chPunctuator;
+ } Punctuator;
+ } Class;
+} VDSCRIPTTOKEN;
+/** Pointer to a script token. */
+typedef VDSCRIPTTOKEN *PVDSCRIPTTOKEN;
+/** Pointer to a const script token. */
+typedef const VDSCRIPTTOKEN *PCVDSCRIPTTOKEN;
+
+/**
+ * Tokenizer state.
+ */
+typedef struct VDTOKENIZER
+{
+ /** Char buffer to read from. */
+ const char *pszInput;
+ /** Current position ininput buffer. */
+ VDSRCPOS Pos;
+ /** Token 1. */
+ VDSCRIPTTOKEN Token1;
+ /** Token 2. */
+ VDSCRIPTTOKEN Token2;
+ /** Pointer to the current active token. */
+ PVDSCRIPTTOKEN pTokenCurr;
+ /** The next token in the input stream (used for peeking). */
+ PVDSCRIPTTOKEN pTokenNext;
+} VDTOKENIZER;
+
+/**
+ * Operators entry.
+ */
+typedef struct VDSCRIPTOP
+{
+ /** Operator string. */
+ const char *pszOp;
+ /** Size of the operator in characters without zero terminator. */
+ size_t cchOp;
+} VDSCRIPTOP;
+/** Pointer to a script operator. */
+typedef VDSCRIPTOP *PVDSCRIPTOP;
+
+/**
+ * Known operators array, sort from higest character count to lowest.
+ */
+static VDSCRIPTOP g_aScriptOps[] =
+{
+ {">>=", 3},
+ {"<<=", 3},
+ {"+=", 2},
+ {"-=", 2},
+ {"/=", 2},
+ {"%=", 2},
+ {"&=", 2},
+ {"|=", 2},
+ {"^=", 2},
+ {"&&", 2},
+ {"||", 2},
+ {"<<", 2},
+ {">>", 2},
+ {"++", 2},
+ {"--", 2},
+ {"==", 2},
+ {"!=", 2},
+ {">=", 2},
+ {"<=", 2},
+ {"=", 1},
+ {"+", 1},
+ {"-", 1},
+ {"*", 1},
+ {"/", 1},
+ {"%", 1},
+ {"|", 1},
+ {"&", 1},
+ {"^", 1},
+ {"<", 1},
+ {">", 1},
+ {"!", 1},
+ {"~", 1}
+};
+
+/**
+ * Known punctuators.
+ */
+static VDSCRIPTOP g_aScriptPunctuators[] =
+{
+ {"(", 1},
+ {")", 1},
+ {"{", 1},
+ {"}", 1},
+ {",", 1},
+ {";", 1},
+};
+
+/**
+ * Keyword entry.
+ */
+typedef struct VDSCRIPTKEYWORD
+{
+ /** Keyword string. */
+ const char *pszKeyword;
+ /** Size of the string in characters without zero terminator. */
+ size_t cchKeyword;
+ /** Keyword type. */
+ VDSCRIPTTOKENKEYWORD enmKeyword;
+} VDSCRIPTKEYWORD;
+/** */
+typedef VDSCRIPTKEYWORD *PVDSCRIPTKEYWORD;
+
+/**
+ * Known keywords.
+ */
+static VDSCRIPTKEYWORD g_aKeywords[] =
+{
+ {"continue", 8, VDSCRIPTTOKENKEYWORD_CONTINUE},
+ {"default", 7, VDSCRIPTTOKENKEYWORD_DEFAULT},
+ {"return", 6, VDSCRIPTTOKENKEYWORD_RETURN},
+ {"switch", 6, VDSCRIPTTOKENKEYWORD_SWITCH},
+ {"while", 5, VDSCRIPTTOKENKEYWORD_WHILE},
+ {"break", 5, VDSCRIPTTOKENKEYWORD_BREAK},
+ {"false", 5, VDSCRIPTTOKENKEYWORD_FALSE},
+ {"true", 4, VDSCRIPTTOKENKEYWORD_TRUE},
+ {"else", 4, VDSCRIPTTOKENKEYWORD_ELSE},
+ {"case", 4, VDSCRIPTTOKENKEYWORD_CASE},
+ {"for", 3, VDSCRIPTTOKENKEYWORD_FOR},
+ {"if", 2, VDSCRIPTTOKENKEYWORD_IF},
+ {"do", 2, VDSCRIPTTOKENKEYWORD_DO}
+};
+
+static int vdScriptParseCompoundStatement(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTMT *ppAstNodeCompound);
+static int vdScriptParseStatement(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTMT *ppAstNodeStmt);
+static int vdScriptParseExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr);
+static int vdScriptParseAssignmentExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr);
+
+/**
+ * Returns whether the tokenizer reached the end of the stream.
+ *
+ * @returns true if the tokenizer reached the end of stream marker
+ * false otherwise.
+ * @param pTokenizer The tokenizer state.
+ */
+DECLINLINE(bool) vdScriptTokenizerIsEos(PVDTOKENIZER pTokenizer)
+{
+ return *pTokenizer->pszInput == '\0';
+}
+
+/**
+ * Skip one character in the input stream.
+ *
+ * @returns nothing.
+ * @param pTokenizer The tokenizer state.
+ */
+DECLINLINE(void) vdScriptTokenizerSkipCh(PVDTOKENIZER pTokenizer)
+{
+ pTokenizer->pszInput++;
+ pTokenizer->Pos.iChStart++;
+ pTokenizer->Pos.iChEnd++;
+}
+
+/**
+ * Returns the next char in the input buffer without advancing it.
+ *
+ * @returns Next character in the input buffer.
+ * @param pTokenizer The tokenizer state.
+ */
+DECLINLINE(char) vdScriptTokenizerPeekCh(PVDTOKENIZER pTokenizer)
+{
+ return vdScriptTokenizerIsEos(pTokenizer)
+ ? '\0'
+ : *(pTokenizer->pszInput + 1);
+}
+
+/**
+ * Returns the next character in the input buffer advancing the internal
+ * position.
+ *
+ * @returns Next character in the stream.
+ * @param pTokenizer The tokenizer state.
+ */
+DECLINLINE(char) vdScriptTokenizerGetCh(PVDTOKENIZER pTokenizer)
+{
+ char ch;
+
+ if (vdScriptTokenizerIsEos(pTokenizer))
+ ch = '\0';
+ else
+ ch = *pTokenizer->pszInput;
+
+ return ch;
+}
+
+/**
+ * Sets a new line for the tokenizer.
+ *
+ * @returns nothing.
+ * @param pTokenizer The tokenizer state.
+ */
+DECLINLINE(void) vdScriptTokenizerNewLine(PVDTOKENIZER pTokenizer, unsigned cSkip)
+{
+ pTokenizer->pszInput += cSkip;
+ pTokenizer->Pos.iLine++;
+ pTokenizer->Pos.iChStart = 1;
+ pTokenizer->Pos.iChEnd = 1;
+}
+
+/**
+ * Checks whether the current position in the input stream is a new line
+ * and skips it.
+ *
+ * @returns Flag whether there was a new line at the current position
+ * in the input buffer.
+ * @param pTokenizer The tokenizer state.
+ */
+DECLINLINE(bool) vdScriptTokenizerIsSkipNewLine(PVDTOKENIZER pTokenizer)
+{
+ bool fNewline = true;
+
+ if ( vdScriptTokenizerGetCh(pTokenizer) == '\r'
+ && vdScriptTokenizerPeekCh(pTokenizer) == '\n')
+ vdScriptTokenizerNewLine(pTokenizer, 2);
+ else if (vdScriptTokenizerGetCh(pTokenizer) == '\n')
+ vdScriptTokenizerNewLine(pTokenizer, 1);
+ else
+ fNewline = false;
+
+ return fNewline;
+}
+
+/**
+ * Skips a multi line comment.
+ *
+ * @returns nothing.
+ * @param pTokenizer The tokenizer state.
+ */
+DECLINLINE(void) vdScriptTokenizerSkipComment(PVDTOKENIZER pTokenizer)
+{
+ while ( !vdScriptTokenizerIsEos(pTokenizer)
+ && ( vdScriptTokenizerGetCh(pTokenizer) != '*'
+ || vdScriptTokenizerPeekCh(pTokenizer) != '/'))
+ {
+ if (!vdScriptTokenizerIsSkipNewLine(pTokenizer))
+ vdScriptTokenizerSkipCh(pTokenizer);
+ }
+
+ if (!vdScriptTokenizerIsEos(pTokenizer))
+ vdScriptTokenizerSkipCh(pTokenizer);
+ if (!vdScriptTokenizerIsEos(pTokenizer))
+ vdScriptTokenizerSkipCh(pTokenizer);
+}
+
+/**
+ * Skip all whitespace starting from the current input buffer position.
+ * Skips all present comments too.
+ *
+ * @returns nothing.
+ * @param pTokenizer The tokenizer state.
+ */
+DECLINLINE(void) vdScriptTokenizerSkipWhitespace(PVDTOKENIZER pTokenizer)
+{
+ while (!vdScriptTokenizerIsEos(pTokenizer))
+ {
+ while ( vdScriptTokenizerGetCh(pTokenizer) == ' '
+ || vdScriptTokenizerGetCh(pTokenizer) == '\t')
+ vdScriptTokenizerSkipCh(pTokenizer);
+
+ if ( !vdScriptTokenizerIsEos(pTokenizer)
+ && !vdScriptTokenizerIsSkipNewLine(pTokenizer))
+ {
+ if ( vdScriptTokenizerGetCh(pTokenizer) == '/'
+ && vdScriptTokenizerPeekCh(pTokenizer) == '*')
+ {
+ vdScriptTokenizerSkipCh(pTokenizer);
+ vdScriptTokenizerSkipCh(pTokenizer);
+ vdScriptTokenizerSkipComment(pTokenizer);
+ }
+ else
+ break; /* Skipped everything, next is some real content. */
+ }
+ }
+}
+
+/**
+ * Get an identifier token from the tokenizer.
+ *
+ * @returns nothing.
+ * @param pTokenizer The tokenizer state.
+ * @param pToken The uninitialized token.
+ */
+static void vdScriptTokenizerGetIdeOrKeyword(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken)
+{
+ char ch;
+ size_t cchIde = 0;
+ bool fIsKeyword = false;
+ const char *pszIde = pTokenizer->pszInput;
+
+ pToken->Pos = pTokenizer->Pos;
+
+ Assert(RT_C_IS_ALPHA(*pszIde) || *pszIde == '_' );
+
+ do
+ {
+ cchIde++;
+ vdScriptTokenizerSkipCh(pTokenizer);
+ ch = vdScriptTokenizerGetCh(pTokenizer);
+ }
+ while (RT_C_IS_ALNUM(ch) || ch == '_');
+
+ /* Check whether we got an identifier or an reserved keyword. */
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aKeywords); i++)
+ {
+ if (!RTStrNCmp(g_aKeywords[i].pszKeyword, pszIde, g_aKeywords[i].cchKeyword))
+ {
+ fIsKeyword = true;
+ pToken->enmClass = VDTOKENCLASS_KEYWORD;
+ pToken->Class.Keyword.enmKeyword = g_aKeywords[i].enmKeyword;
+ break;
+ }
+ }
+
+ if (!fIsKeyword)
+ {
+ pToken->enmClass = VDTOKENCLASS_IDENTIFIER;
+ pToken->Class.Ide.pszIde = pszIde;
+ pToken->Class.Ide.cchIde = cchIde;
+ }
+ pToken->Pos.iChEnd += cchIde;
+}
+
+/**
+ * Get a numerical constant from the tokenizer.
+ *
+ * @returns nothing.
+ * @param pTokenizer The tokenizer state.
+ * @param pToken The uninitialized token.
+ */
+static void vdScriptTokenizerGetNumberConst(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken)
+{
+ unsigned uBase = 10;
+ char *pszNext = NULL;
+
+ Assert(RT_C_IS_DIGIT(vdScriptTokenizerGetCh(pTokenizer)));
+
+ /* Let RTStrToUInt64Ex() do all the work, looks C compliant :). */
+ pToken->enmClass = VDTOKENCLASS_NUMCONST;
+ int rc = RTStrToUInt64Ex(pTokenizer->pszInput, &pszNext, 0, &pToken->Class.NumConst.u64);
+ Assert(RT_SUCCESS(rc) || rc == VWRN_TRAILING_CHARS || rc == VWRN_TRAILING_SPACES);
+ /** @todo: Handle number to big, throw a warning */
+
+ unsigned cchNumber = pszNext - pTokenizer->pszInput;
+ for (unsigned i = 0; i < cchNumber; i++)
+ vdScriptTokenizerSkipCh(pTokenizer);
+
+ /* Check for a supported suffix, supported are K|M|G. */
+ if (vdScriptTokenizerGetCh(pTokenizer) == 'K')
+ {
+ pToken->Class.NumConst.u64 *= _1K;
+ vdScriptTokenizerSkipCh(pTokenizer);
+ }
+ else if (vdScriptTokenizerGetCh(pTokenizer) == 'M')
+ {
+ pToken->Class.NumConst.u64 *= _1M;
+ vdScriptTokenizerSkipCh(pTokenizer);
+ }
+ else if (vdScriptTokenizerGetCh(pTokenizer) == 'G')
+ {
+ pToken->Class.NumConst.u64 *= _1G;
+ vdScriptTokenizerSkipCh(pTokenizer);
+ }
+}
+
+/**
+ * Parses a string constant.
+ *
+ * @returns nothing.
+ * @param pTokenizer The tokenizer state.
+ * @param pToken The uninitialized token.
+ *
+ * @remarks: No escape sequences allowed at this time.
+ */
+static void vdScriptTokenizerGetStringConst(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken)
+{
+ size_t cchStr = 0;
+
+ Assert(vdScriptTokenizerGetCh(pTokenizer) == '\"');
+ vdScriptTokenizerSkipCh(pTokenizer); /* Skip " */
+
+ pToken->enmClass = VDTOKENCLASS_STRINGCONST;
+ pToken->Pos = pTokenizer->Pos;
+ pToken->Class.StringConst.pszString = pTokenizer->pszInput;
+
+ while (vdScriptTokenizerGetCh(pTokenizer) != '\"')
+ {
+ cchStr++;
+ vdScriptTokenizerSkipCh(pTokenizer);
+ }
+
+ vdScriptTokenizerSkipCh(pTokenizer); /* Skip closing " */
+
+ pToken->Class.StringConst.cchString = cchStr;
+ pToken->Pos.iChEnd += cchStr;
+}
+
+/**
+ * Get the end of stream token.
+ *
+ * @returns nothing.
+ * @param pTokenizer The tokenizer state.
+ * @param pToken The uninitialized token.
+ */
+static void vdScriptTokenizerGetEos(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken)
+{
+ Assert(vdScriptTokenizerGetCh(pTokenizer) == '\0');
+
+ pToken->enmClass = VDTOKENCLASS_EOS;
+ pToken->Pos = pTokenizer->Pos;
+}
+
+/**
+ * Get operator or punctuator token.
+ *
+ * @returns nothing.
+ * @param pTokenizer The tokenizer state.
+ * @param pToken The uninitialized token.
+ */
+static void vdScriptTokenizerGetOperatorOrPunctuator(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken)
+{
+ bool fOpFound = false;
+
+ pToken->enmClass = VDTOKENCLASS_INVALID;
+ pToken->Pos = pTokenizer->Pos;
+
+ /*
+ * Use table based approach here, not the fastest solution but enough for our purpose
+ * for now.
+ */
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aScriptOps); i++)
+ {
+ if (!RTStrNCmp(g_aScriptOps[i].pszOp, pTokenizer->pszInput, g_aScriptOps[i].cchOp))
+ {
+ memset(pToken->Class.Operator.aszOp, 0, sizeof(pToken->Class.Operator.aszOp));
+
+ int rc = RTStrCopy(pToken->Class.Operator.aszOp, sizeof(pToken->Class.Operator.aszOp), g_aScriptOps[i].pszOp);
+ AssertRC(rc);
+
+ pToken->enmClass = VDTOKENCLASS_OPERATORS;
+ pToken->Pos.iChEnd += g_aScriptOps[i].cchOp;
+
+ /** @todo: Make this prettier. */
+ for (unsigned j = 0; j < g_aScriptOps[i].cchOp; j++)
+ vdScriptTokenizerSkipCh(pTokenizer);
+ fOpFound = true;
+ break;
+ }
+ }
+
+ if (!fOpFound)
+ {
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aScriptPunctuators); i++)
+ {
+ if (!RTStrNCmp(g_aScriptPunctuators[i].pszOp, pTokenizer->pszInput, g_aScriptPunctuators[i].cchOp))
+ {
+ pToken->Pos.iChEnd += g_aScriptPunctuators[i].cchOp;
+ pToken->enmClass = VDTOKENCLASS_PUNCTUATOR;
+ pToken->Class.Punctuator.chPunctuator = *g_aScriptPunctuators[i].pszOp;
+
+ vdScriptTokenizerSkipCh(pTokenizer);
+ fOpFound = true;
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * Read the next token from the tokenizer stream.
+ *
+ * @returns nothing.
+ * @param pTokenizer The tokenizer to read from.
+ * @param pToken Uninitialized token to fill the token data into.
+ */
+static void vdScriptTokenizerReadNextToken(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken)
+{
+ /* Skip all eventually existing whitespace, newlines and comments first. */
+ vdScriptTokenizerSkipWhitespace(pTokenizer);
+
+ char ch = vdScriptTokenizerGetCh(pTokenizer);
+ if (RT_C_IS_ALPHA(ch) || ch == '_')
+ vdScriptTokenizerGetIdeOrKeyword(pTokenizer, pToken);
+ else if (RT_C_IS_DIGIT(ch))
+ vdScriptTokenizerGetNumberConst(pTokenizer, pToken);
+ else if (ch == '\"')
+ vdScriptTokenizerGetStringConst(pTokenizer, pToken);
+ else if (ch == '\0')
+ vdScriptTokenizerGetEos(pTokenizer, pToken);
+ else
+ vdScriptTokenizerGetOperatorOrPunctuator(pTokenizer, pToken);
+}
+
+/**
+ * Create a new tokenizer.
+ *
+ * @returns Pointer to the new tokenizer state on success.
+ * NULL if out of memory.
+ * @param pszInput The input to create the tokenizer for.
+ */
+static PVDTOKENIZER vdScriptTokenizerCreate(const char *pszInput)
+{
+ PVDTOKENIZER pTokenizer = (PVDTOKENIZER)RTMemAllocZ(sizeof(VDTOKENIZER));
+ if (pTokenizer)
+ {
+ pTokenizer->pszInput = pszInput;
+ pTokenizer->Pos.iLine = 1;
+ pTokenizer->Pos.iChStart = 1;
+ pTokenizer->Pos.iChEnd = 1;
+ pTokenizer->pTokenCurr = &pTokenizer->Token1;
+ pTokenizer->pTokenNext = &pTokenizer->Token2;
+ /* Fill the tokenizer with two first tokens. */
+ vdScriptTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenCurr);
+ vdScriptTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenNext);
+ }
+
+ return pTokenizer;
+}
+
+/**
+ * Destroys a given tokenizer state.
+ *
+ * @returns nothing.
+ * @param pTokenizer The tokenizer to destroy.
+ */
+static void vdScriptTokenizerDestroy(PVDTOKENIZER pTokenizer)
+{
+ RTMemFree(pTokenizer);
+}
+
+/**
+ * Get the current token in the input stream.
+ *
+ * @returns Pointer to the next token in the stream.
+ * @param pTokenizer The tokenizer to destroy.
+ */
+DECLINLINE(PCVDSCRIPTTOKEN) vdScriptTokenizerGetToken(PVDTOKENIZER pTokenizer)
+{
+ return pTokenizer->pTokenCurr;
+}
+
+/**
+ * Get the class of the current token.
+ *
+ * @returns Class of the current token.
+ * @param pTokenizer The tokenizer state.
+ */
+DECLINLINE(VDTOKENCLASS) vdScriptTokenizerGetTokenClass(PVDTOKENIZER pTokenizer)
+{
+ return pTokenizer->pTokenCurr->enmClass;
+}
+
+/**
+ * Returns the token class of the next token in the stream.
+ *
+ * @returns Token class of the next token.
+ * @param pTokenizer The tokenizer state.
+ */
+DECLINLINE(VDTOKENCLASS) vdScriptTokenizerPeekNextClass(PVDTOKENIZER pTokenizer)
+{
+ return pTokenizer->pTokenNext->enmClass;
+}
+
+/**
+ * Consume the current token advancing to the next in the stream.
+ *
+ * @returns nothing.
+ * @param pTokenizer The tokenizer state.
+ */
+static void vdScriptTokenizerConsume(PVDTOKENIZER pTokenizer)
+{
+ PVDSCRIPTTOKEN pTokenTmp = pTokenizer->pTokenCurr;
+
+ /* Switch next token to current token and read in the next token. */
+ pTokenizer->pTokenCurr = pTokenizer->pTokenNext;
+ pTokenizer->pTokenNext = pTokenTmp;
+ vdScriptTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenNext);
+}
+
+/**
+ * Check whether the next token in the input stream is a punctuator and matches the given
+ * character.
+ *
+ * @returns true if the token matched.
+ * false otherwise.
+ * @param pTokenizer The tokenizer state.
+ * @param chCheck The punctuator to check against.
+ */
+static bool vdScriptTokenizerIsPunctuatorEqual(PVDTOKENIZER pTokenizer, char chCheck)
+{
+ PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pTokenizer);
+
+ if ( pToken->enmClass == VDTOKENCLASS_PUNCTUATOR
+ && pToken->Class.Punctuator.chPunctuator == chCheck)
+ return true;
+
+ return false;
+}
+
+/**
+ * Check whether the next token in the input stream is a punctuator and matches the given
+ * character and skips it.
+ *
+ * @returns true if the token matched and was skipped.
+ * false otherwise.
+ * @param pTokenizer The tokenizer state.
+ * @param chCheck The punctuator to check against.
+ */
+static bool vdScriptTokenizerSkipIfIsPunctuatorEqual(PVDTOKENIZER pTokenizer, char chCheck)
+{
+ bool fEqual = vdScriptTokenizerIsPunctuatorEqual(pTokenizer, chCheck);
+ if (fEqual)
+ vdScriptTokenizerConsume(pTokenizer);
+
+ return fEqual;
+}
+
+/**
+ * Check whether the next token in the input stream is a keyword and matches the given
+ * keyword.
+ *
+ * @returns true if the token matched.
+ * false otherwise.
+ * @param pTokenizer The tokenizer state.
+ * @param enmKey The keyword to check against.
+ */
+static bool vdScriptTokenizerIsKeywordEqual(PVDTOKENIZER pTokenizer, VDSCRIPTTOKENKEYWORD enmKeyword)
+{
+ PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pTokenizer);
+
+ if ( pToken->enmClass == VDTOKENCLASS_KEYWORD
+ && pToken->Class.Keyword.enmKeyword == enmKeyword)
+ return true;
+
+ return false;
+}
+
+/**
+ * Check whether the next token in the input stream is a keyword and matches the given
+ * keyword and skips it.
+ *
+ * @returns true if the token matched and was skipped.
+ * false otherwise.
+ * @param pTokenizer The tokenizer state.
+ * @param enmKey The keyword to check against.
+ */
+static bool vdScriptTokenizerSkipIfIsKeywordEqual(PVDTOKENIZER pTokenizer, VDSCRIPTTOKENKEYWORD enmKeyword)
+{
+ bool fEqual = vdScriptTokenizerIsKeywordEqual(pTokenizer, enmKeyword);
+ if (fEqual)
+ vdScriptTokenizerConsume(pTokenizer);
+
+ return fEqual;
+}
+
+/**
+ * Check whether the next token in the input stream is a keyword and matches the given
+ * keyword.
+ *
+ * @returns true if the token matched.
+ * false otherwise.
+ * @param pTokenizer The tokenizer state.
+ * @param pszOp The operation to check against.
+ */
+static bool vdScriptTokenizerIsOperatorEqual(PVDTOKENIZER pTokenizer, const char *pszOp)
+{
+ PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pTokenizer);
+
+ if ( pToken->enmClass == VDTOKENCLASS_OPERATORS
+ && !RTStrCmp(pToken->Class.Operator.aszOp, pszOp))
+ return true;
+
+ return false;
+}
+
+/**
+ * Check whether the next token in the input stream is an operator and matches the given
+ * keyword and skips it.
+ *
+ * @returns true if the token matched and was skipped.
+ * false otherwise.
+ * @param pTokenizer The tokenizer state.
+ * @param pszOp The operation to check against.
+ */
+static bool vdScriptTokenizerSkipIfIsOperatorEqual(PVDTOKENIZER pTokenizer, const char *pszOp)
+{
+ bool fEqual = vdScriptTokenizerIsOperatorEqual(pTokenizer, pszOp);
+ if (fEqual)
+ vdScriptTokenizerConsume(pTokenizer);
+
+ return fEqual;
+}
+
+/**
+ * Record an error while parsing.
+ *
+ * @returns VBox status code passed.
+ */
+static int vdScriptParserError(PVDSCRIPTCTXINT pThis, int rc, RT_SRC_POS_DECL, const char *pszFmt, ...)
+{
+ NOREF(pThis);
+ NOREF(pszFmt);
+ RTPrintf(pszFmt);
+ return rc;
+}
+
+/**
+ * Puts the next identifier AST node on the stack.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeIde Where to store the identifier AST node on success.
+ */
+static int vdScriptParseIde(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTIDE *ppAstNodeIde)
+{
+ int rc = VINF_SUCCESS;
+ PCVDSCRIPTTOKEN pToken;
+
+ LogFlowFunc(("pThis=%p ppAstNodeIde=%p\n", pThis, ppAstNodeIde));
+
+ pToken = vdScriptTokenizerGetToken(pThis->pTokenizer);
+ if (pToken->enmClass != VDTOKENCLASS_IDENTIFIER)
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected identifer got...\n");
+ else
+ {
+ /* Create new AST node and push onto stack. */
+ PVDSCRIPTASTIDE pAstNodeIde = vdScriptAstNodeIdeAlloc(pToken->Class.Ide.cchIde);
+ if (pAstNodeIde)
+ {
+ rc = RTStrCopyEx(pAstNodeIde->aszIde, pToken->Class.Ide.cchIde + 1, pToken->Class.Ide.pszIde, pToken->Class.Ide.cchIde);
+ AssertRC(rc);
+ pAstNodeIde->cchIde = pToken->Class.Ide.cchIde;
+
+ *ppAstNodeIde = pAstNodeIde;
+ vdScriptTokenizerConsume(pThis->pTokenizer);
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating identifier AST node\n");
+ }
+
+ LogFlowFunc(("returns %Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a primary expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the primary expression on success.
+ */
+static int vdScriptParsePrimaryExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
+ {
+ rc = vdScriptParseExpression(pThis, ppAstNodeExpr);
+ if (RT_SUCCESS(rc)
+ && !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
+ }
+ else
+ {
+ PVDSCRIPTASTEXPR pExpr = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExpr)
+ {
+ if (vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_IDENTIFIER)
+ {
+ PVDSCRIPTASTIDE pIde = NULL;
+ rc = vdScriptParseIde(pThis, &pIde);
+ if (RT_SUCCESS(rc))
+ {
+ pExpr->enmType = VDSCRIPTEXPRTYPE_PRIMARY_IDENTIFIER;
+ pExpr->pIde = pIde;
+ }
+ }
+ else if (vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_NUMCONST)
+ {
+ PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pThis->pTokenizer);
+ pExpr->enmType = VDSCRIPTEXPRTYPE_PRIMARY_NUMCONST;
+ pExpr->u64 = pToken->Class.NumConst.u64;
+ vdScriptTokenizerConsume(pThis->pTokenizer);
+ }
+ else if (vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_STRINGCONST)
+ {
+ PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pThis->pTokenizer);
+ pExpr->enmType = VDSCRIPTEXPRTYPE_PRIMARY_STRINGCONST;
+ pExpr->pszStr = RTStrDupN(pToken->Class.StringConst.pszString, pToken->Class.StringConst.cchString);
+ vdScriptTokenizerConsume(pThis->pTokenizer);
+
+ if (!pExpr->pszStr)
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating string\n");
+ }
+ else if (vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_KEYWORD)
+ {
+ PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pThis->pTokenizer);
+ pExpr->enmType = VDSCRIPTEXPRTYPE_PRIMARY_BOOLEAN;
+
+ if (pToken->Class.Keyword.enmKeyword == VDSCRIPTTOKENKEYWORD_TRUE)
+ pExpr->f = true;
+ else if (pToken->Class.Keyword.enmKeyword == VDSCRIPTTOKENKEYWORD_FALSE)
+ pExpr->f = false;
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Unexpected keyword, expected true or false\n");
+ vdScriptTokenizerConsume(pThis->pTokenizer);
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\" | identifier | constant | string, got ...\n");
+
+ if (RT_FAILURE(rc))
+ vdScriptAstNodeFree(&pExpr->Core);
+ else
+ *ppAstNodeExpr = pExpr;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse an argument list for a function call.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param pFnCall The function call AST node.
+ */
+static int vdScriptParseFnCallArgumentList(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR pFnCall)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseAssignmentExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ RTListAppend(&pFnCall->FnCall.ListArgs, &pExpr->Core.ListNode);
+ while (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ','))
+ {
+ rc = vdScriptParseAssignmentExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ RTListAppend(&pFnCall->FnCall.ListArgs, &pExpr->Core.ListNode);
+ else
+ break;
+ }
+ if ( RT_SUCCESS(rc)
+ && !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a postfix expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * postfix-expression:
+ * primary-expression
+ * postfix-expression ( argument-expression )
+ * postfix-expression ++
+ * postfix-expression --
+ */
+static int vdScriptParsePostfixExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParsePrimaryExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ while (true)
+ {
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+
+ if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "++"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ {
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_POSTFIX_INCREMENT;
+ pExprNew->pExpr = pExpr;
+ pExpr = pExprNew;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "--"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ {
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_POSTFIX_DECREMENT;
+ pExprNew->pExpr = pExpr;
+ pExpr = pExprNew;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ {
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_POSTFIX_FNCALL;
+ RTListInit(&pExprNew->FnCall.ListArgs);
+ if (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
+ rc = vdScriptParseFnCallArgumentList(pThis, pExprNew);
+ pExprNew->FnCall.pFnIde = pExpr;
+ pExpr = pExprNew;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else
+ break;
+
+ if (RT_FAILURE(rc))
+ break;
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExpr;
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse an unary expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * unary-expression:
+ * postfix-expression
+ * ++ unary-expression
+ * -- unary-expression
+ * + unary-expression
+ * - unary-expression
+ * ~ unary-expression
+ * ! unary-expression
+ */
+static int vdScriptParseUnaryExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+ PVDSCRIPTASTEXPR pExprTop = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ while (true)
+ {
+ bool fQuit = false;
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+
+ if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "++"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_UNARY_INCREMENT;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "--"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_UNARY_DECREMENT;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "+"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_UNARY_POSSIGN;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "-"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_UNARY_NEGSIGN;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "~"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_UNARY_INVERT;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "!"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_UNARY_NEGATE;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else
+ {
+ /* Must be a postfix expression. */
+ rc = vdScriptParsePostfixExpression(pThis, &pExprNew);
+ fQuit = true;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ if (!pExprTop)
+ {
+ pExprTop = pExprNew;
+ pExpr = pExprNew;
+ }
+ else
+ {
+ pExpr->pExpr = pExprNew;
+ pExpr = pExprNew;
+ }
+ if (fQuit)
+ break;
+ }
+ else
+ break;
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExprTop;
+ else if (pExprTop)
+ vdScriptAstNodeFree(&pExprTop->Core);
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a multiplicative expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * multiplicative-expression:
+ * unary-expression
+ * multiplicative-expression * unary-expression
+ * multiplicative-expression / unary-expression
+ * multiplicative-expression % unary-expression
+ */
+static int vdScriptParseMultiplicativeExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseUnaryExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+ while (RT_SUCCESS(rc))
+ {
+ if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "*"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_MULTIPLICATION;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "/"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_DIVISION;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "%"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_MODULUS;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else
+ break;
+
+ pExprNew->BinaryOp.pLeftExpr = pExpr;
+ pExpr = pExprNew;
+ rc = vdScriptParseUnaryExpression(pThis, &pExprNew);
+ if (RT_SUCCESS(rc))
+ pExpr->BinaryOp.pRightExpr = pExprNew;
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExpr;
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a additive expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * additive-expression:
+ * multiplicative-expression
+ * additive-expression + multiplicative-expression
+ * additive-expression - multiplicative-expression
+ */
+static int vdScriptParseAdditiveExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseMultiplicativeExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+ while (RT_SUCCESS(rc))
+ {
+ if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "+"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_ADDITION;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "-"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_SUBTRACTION;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else
+ break;
+
+ pExprNew->BinaryOp.pLeftExpr = pExpr;
+ pExpr = pExprNew;
+ rc = vdScriptParseMultiplicativeExpression(pThis, &pExprNew);
+ if (RT_SUCCESS(rc))
+ pExpr->BinaryOp.pRightExpr = pExprNew;
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExpr;
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a shift expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * shift-expression:
+ * additive-expression
+ * shift-expression << additive-expression
+ * shift-expression >> additive-expression
+ */
+static int vdScriptParseShiftExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseAdditiveExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+ while (RT_SUCCESS(rc))
+ {
+ if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "<<"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_LSL;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ">>"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_LSR;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else
+ break;
+
+ pExprNew->BinaryOp.pLeftExpr = pExpr;
+ pExpr = pExprNew;
+ rc = vdScriptParseAdditiveExpression(pThis, &pExprNew);
+ if (RT_SUCCESS(rc))
+ pExpr->BinaryOp.pRightExpr = pExprNew;
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExpr;
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a relational expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * relational-expression:
+ * shift-expression
+ * relational-expression < shift-expression
+ * relational-expression > shift-expression
+ * relational-expression >= shift-expression
+ * relational-expression <= shift-expression
+ */
+static int vdScriptParseRelationalExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseShiftExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+ while (RT_SUCCESS(rc))
+ {
+ if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "<"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_LOWER;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ">"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_HIGHER;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ">="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_HIGHEREQUAL;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "<="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_LOWEREQUAL;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else
+ break;
+
+ pExprNew->BinaryOp.pLeftExpr = pExpr;
+ pExpr = pExprNew;
+ rc = vdScriptParseShiftExpression(pThis, &pExprNew);
+ if (RT_SUCCESS(rc))
+ pExpr->BinaryOp.pRightExpr = pExprNew;
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExpr;
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a equality expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * equality-expression:
+ * relational-expression
+ * equality-expression == relational-expression
+ * equality-expression != relational-expression
+ */
+static int vdScriptParseEqualityExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseRelationalExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+ while (RT_SUCCESS(rc))
+ {
+ if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "=="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_EQUAL;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "!="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_NOTEQUAL;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else
+ break;
+
+ pExprNew->BinaryOp.pLeftExpr = pExpr;
+ pExpr = pExprNew;
+ rc = vdScriptParseRelationalExpression(pThis, &pExprNew);
+ if (RT_SUCCESS(rc))
+ pExpr->BinaryOp.pRightExpr = pExprNew;
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExpr;
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a bitwise and expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * and-expression:
+ * equality-expression
+ * and-expression & equality-expression
+ */
+static int vdScriptParseBitwiseAndExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseEqualityExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+ while ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "&"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ {
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_EQUAL;
+ pExprNew->BinaryOp.pLeftExpr = pExpr;
+ pExpr = pExprNew;
+ rc = vdScriptParseEqualityExpression(pThis, &pExprNew);
+ if (RT_SUCCESS(rc))
+ pExpr->BinaryOp.pRightExpr = pExprNew;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExpr;
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a bitwise xor expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * xor-expression:
+ * and-expression
+ * xor-expression ^ equality-expression
+ */
+static int vdScriptParseBitwiseXorExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseBitwiseAndExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+ while ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "^"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ {
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_BITWISE_XOR;
+ pExprNew->BinaryOp.pLeftExpr = pExpr;
+ pExpr = pExprNew;
+ rc = vdScriptParseBitwiseAndExpression(pThis, &pExprNew);
+ if (RT_SUCCESS(rc))
+ pExpr->BinaryOp.pRightExpr = pExprNew;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExpr;
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a bitwise or expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * or-expression:
+ * xor-expression
+ * or-expression | xor-expression
+ */
+static int vdScriptParseBitwiseOrExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseBitwiseXorExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+ while ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "|"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ {
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_BITWISE_OR;
+ pExprNew->BinaryOp.pLeftExpr = pExpr;
+ pExpr = pExprNew;
+ rc = vdScriptParseBitwiseXorExpression(pThis, &pExprNew);
+ if (RT_SUCCESS(rc))
+ pExpr->BinaryOp.pRightExpr = pExprNew;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExpr;
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a logical and expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * logical-and-expression:
+ * or-expression
+ * logical-and-expression | or-expression
+ */
+static int vdScriptParseLogicalAndExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseBitwiseOrExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+ while ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "&&"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ {
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_LOGICAL_AND;
+ pExprNew->BinaryOp.pLeftExpr = pExpr;
+ pExpr = pExprNew;
+ rc = vdScriptParseBitwiseOrExpression(pThis, &pExprNew);
+ if (RT_SUCCESS(rc))
+ pExpr->BinaryOp.pRightExpr = pExprNew;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExpr;
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a logical or expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * logical-or-expression:
+ * logical-and-expression
+ * logical-or-expression | logical-and-expression
+ */
+static int vdScriptParseLogicalOrExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseLogicalAndExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+ while ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "||"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ {
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_LOGICAL_OR;
+ pExprNew->BinaryOp.pLeftExpr = pExpr;
+ pExpr = pExprNew;
+ rc = vdScriptParseLogicalAndExpression(pThis, &pExprNew);
+ if (RT_SUCCESS(rc))
+ pExpr->BinaryOp.pRightExpr = pExprNew;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExpr;
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a conditional expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note: VDScript doesn't support logical-or-expression ? expression : conditional-expression
+ * so a conditional expression is equal to a logical-or-expression.
+ */
+static int vdScriptParseCondExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ return vdScriptParseLogicalOrExpression(pThis, ppAstNodeExpr);
+}
+
+/**
+ * Parse an assignment expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * assignment-expression:
+ * conditional-expression
+ * unary-expression assignment-operator assignment-expression
+ */
+static int vdScriptParseAssignmentExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseLogicalOrExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+ while (RT_SUCCESS(rc))
+ {
+ if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "*="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_MULT;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "/="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_DIV;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "%="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_MOD;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "+="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_ADD;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "-="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_SUB;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "<<="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_LSL;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ">>="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_LSR;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "&="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_AND;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "^="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_XOR;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "|="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_OR;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else
+ break;
+
+ pExprNew->BinaryOp.pLeftExpr = pExpr;
+ pExpr = pExprNew;
+ rc = vdScriptParseLogicalOrExpression(pThis, &pExprNew);
+ if (RT_SUCCESS(rc))
+ pExpr->BinaryOp.pRightExpr = pExprNew;
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExpr;
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse an expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * expression:
+ * assignment-expression
+ * expression , assignment-expression
+ */
+static int vdScriptParseExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pAssignExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseAssignmentExpression(pThis, &pAssignExpr);
+ if ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ','))
+ {
+ PVDSCRIPTASTEXPR pListAssignExpr = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pListAssignExpr)
+ {
+ pListAssignExpr->enmType = VDSCRIPTEXPRTYPE_ASSIGNMENT_LIST;
+ RTListInit(&pListAssignExpr->ListExpr);
+ RTListAppend(&pListAssignExpr->ListExpr, &pAssignExpr->Core.ListNode);
+ do
+ {
+ rc = vdScriptParseAssignmentExpression(pThis, &pAssignExpr);
+ if (RT_SUCCESS(rc))
+ RTListAppend(&pListAssignExpr->ListExpr, &pAssignExpr->Core.ListNode);
+ } while ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ','));
+
+ if (RT_FAILURE(rc))
+ vdScriptAstNodeFree(&pListAssignExpr->Core);
+ else
+ *ppAstNodeExpr = pListAssignExpr;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pAssignExpr;
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse an if statement.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param pAstNodeIf Uninitialized if AST node.
+ *
+ * @note The caller skipped the "if" token already.
+ */
+static int vdScriptParseIf(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTIF pAstNodeIf)
+{
+ int rc = VINF_SUCCESS;
+
+ if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
+ {
+ PVDSCRIPTASTEXPR pCondExpr = NULL;
+ rc = vdScriptParseExpression(pThis, &pCondExpr);
+ if (RT_SUCCESS(rc))
+ {
+ if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
+ {
+ PVDSCRIPTASTSTMT pStmt = NULL;
+ PVDSCRIPTASTSTMT pElseStmt = NULL;
+ rc = vdScriptParseStatement(pThis, &pStmt);
+ if ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_ELSE))
+ rc = vdScriptParseStatement(pThis, &pElseStmt);
+
+ if (RT_SUCCESS(rc))
+ {
+ pAstNodeIf->pCond = pCondExpr;
+ pAstNodeIf->pTrueStmt = pStmt;
+ pAstNodeIf->pElseStmt = pElseStmt;
+ }
+ else if (pStmt)
+ vdScriptAstNodeFree(&pStmt->Core);
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
+
+ if (RT_FAILURE(rc))
+ vdScriptAstNodeFree(&pCondExpr->Core);
+ }
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n");
+
+ return rc;
+}
+
+/**
+ * Parse a switch statement.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param pAstNodeSwitch Uninitialized switch AST node.
+ *
+ * @note The caller skipped the "switch" token already.
+ */
+static int vdScriptParseSwitch(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSWITCH pAstNodeSwitch)
+{
+ int rc = VINF_SUCCESS;
+
+ if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
+ {
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ rc = vdScriptParseExpression(pThis, &pExpr);
+ if ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
+ {
+ PVDSCRIPTASTSTMT pStmt = NULL;
+ rc = vdScriptParseStatement(pThis, &pStmt);
+ if (RT_SUCCESS(rc))
+ {
+ pAstNodeSwitch->pCond = pExpr;
+ pAstNodeSwitch->pStmt = pStmt;
+ }
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+ else if (RT_SUCCESS(rc))
+ {
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n");
+
+ return rc;
+}
+
+/**
+ * Parse a while or do ... while statement.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param pAstNodeWhile Uninitialized while AST node.
+ *
+ * @note The caller skipped the "while" or "do" token already.
+ */
+static int vdScriptParseWhile(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTWHILE pAstNodeWhile, bool fDoWhile)
+{
+ int rc = VINF_SUCCESS;
+
+ pAstNodeWhile->fDoWhile = fDoWhile;
+
+ if (fDoWhile)
+ {
+ PVDSCRIPTASTSTMT pStmt = NULL;
+ rc = vdScriptParseStatement(pThis, &pStmt);
+ if ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_WHILE))
+ {
+ if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
+ {
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ rc = vdScriptParseExpression(pThis, &pExpr);
+ if ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
+ {
+ if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';'))
+ {
+ pAstNodeWhile->pCond = pExpr;
+ pAstNodeWhile->pStmt = pStmt;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n");
+ }
+ else if (RT_SUCCESS(rc))
+ {
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n");
+ }
+ else if (RT_SUCCESS(rc))
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"while\", got ...\n");
+
+ if ( RT_FAILURE(rc)
+ && pStmt)
+ vdScriptAstNodeFree(&pStmt->Core);
+ }
+ else
+ {
+ if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
+ {
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ rc = vdScriptParseExpression(pThis, &pExpr);
+ if ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
+ {
+ PVDSCRIPTASTSTMT pStmt = NULL;
+ rc = vdScriptParseStatement(pThis, &pStmt);
+ if (RT_SUCCESS(rc))
+ {
+ pAstNodeWhile->pCond = pExpr;
+ pAstNodeWhile->pStmt = pStmt;
+ }
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+ else if (RT_SUCCESS(rc))
+ {
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n");
+ }
+
+ return rc;
+}
+
+/**
+ * Parse a for statement.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param pAstNodeFor Uninitialized for AST node.
+ *
+ * @note The caller skipped the "for" token already.
+ */
+static int vdScriptParseFor(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTFOR pAstNodeFor)
+{
+ int rc = VINF_SUCCESS;
+
+ if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
+ {
+ PVDSCRIPTASTEXPR pExprStart = NULL;
+ PVDSCRIPTASTEXPR pExprCond = NULL;
+ PVDSCRIPTASTEXPR pExpr3 = NULL;
+
+ rc = vdScriptParseExpression(pThis, &pExprStart);
+ if ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';'))
+ {
+ rc = vdScriptParseExpression(pThis, &pExprCond);
+ if ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';'))
+ {
+ rc = vdScriptParseExpression(pThis, &pExpr3);
+ if ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
+ {
+ PVDSCRIPTASTSTMT pStmt = NULL;
+ rc = vdScriptParseStatement(pThis, &pStmt);
+ if (RT_SUCCESS(rc))
+ {
+ pAstNodeFor->pExprStart = pExprStart;
+ pAstNodeFor->pExprCond = pExprCond;
+ pAstNodeFor->pExpr3 = pExpr3;
+ pAstNodeFor->pStmt = pStmt;
+ }
+ }
+ }
+ else if (RT_SUCCESS(rc))
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n");
+ }
+ else if (RT_SUCCESS(rc))
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n");
+
+ if (RT_FAILURE(rc))
+ {
+ if (pExprStart)
+ vdScriptAstNodeFree(&pExprStart->Core);
+ if (pExprCond)
+ vdScriptAstNodeFree(&pExprCond->Core);
+ if (pExpr3)
+ vdScriptAstNodeFree(&pExpr3->Core);
+ }
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n");
+
+ return rc;
+}
+
+/**
+ * Parse a declaration.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeDecl Where to store the declaration AST node on success.
+ */
+static int vdScriptParseDeclaration(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTDECL *ppAstNodeDecl)
+{
+ int rc = VERR_NOT_IMPLEMENTED;
+ return rc;
+}
+
+/**
+ * Parse a statement.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeStmt Where to store the statement AST node on success.
+ */
+static int vdScriptParseStatement(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTMT *ppAstNodeStmt)
+{
+ int rc = VINF_SUCCESS;
+
+ /* Shortcut for a new compound statement. */
+ if (vdScriptTokenizerIsPunctuatorEqual(pThis->pTokenizer, '{'))
+ rc = vdScriptParseCompoundStatement(pThis, ppAstNodeStmt);
+ else
+ {
+ PVDSCRIPTASTSTMT pAstNodeStmt = (PVDSCRIPTASTSTMT)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_STATEMENT);
+
+ if (pAstNodeStmt)
+ {
+
+ if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_DEFAULT))
+ {
+ if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ':'))
+ {
+ PVDSCRIPTASTSTMT pAstNodeStmtDef = NULL;
+ pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_DEFAULT;
+ rc = vdScriptParseStatement(pThis, &pAstNodeStmtDef);
+ if (RT_SUCCESS(rc))
+ pAstNodeStmt->pStmt = pAstNodeStmtDef;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \":\", got ...\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_CASE))
+ {
+ PVDSCRIPTASTEXPR pAstNodeExpr = NULL;
+ rc = vdScriptParseCondExpression(pThis, &pAstNodeExpr);
+ if (RT_SUCCESS(rc))
+ {
+ if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ':'))
+ {
+ PVDSCRIPTASTSTMT pAstNodeCaseStmt = NULL;
+ rc = vdScriptParseStatement(pThis, &pAstNodeCaseStmt);
+ if (RT_SUCCESS(rc))
+ {
+ pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_CASE;
+ pAstNodeStmt->Case.pExpr = pAstNodeExpr;
+ pAstNodeStmt->Case.pStmt = pAstNodeCaseStmt;
+ }
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \":\", got ...\n");
+
+ if (RT_FAILURE(rc))
+ vdScriptAstNodeFree(&pAstNodeExpr->Core);
+ }
+ }
+ else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_IF))
+ {
+ pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_IF;
+ rc = vdScriptParseIf(pThis, &pAstNodeStmt->If);
+ }
+ else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_SWITCH))
+ {
+ pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_SWITCH;
+ rc = vdScriptParseSwitch(pThis, &pAstNodeStmt->Switch);
+ }
+ else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_WHILE))
+ {
+ pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_WHILE;
+ rc = vdScriptParseWhile(pThis, &pAstNodeStmt->While, false /* fDoWhile */);
+ }
+ else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_DO))
+ {
+ pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_WHILE;
+ rc = vdScriptParseWhile(pThis, &pAstNodeStmt->While, true /* fDoWhile */);
+ }
+ else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_FOR))
+ {
+ pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_FOR;
+ rc = vdScriptParseFor(pThis, &pAstNodeStmt->For);
+ }
+ else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_CONTINUE))
+ {
+ pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_CONTINUE;
+ if (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';'))
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_BREAK))
+ {
+ pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_BREAK;
+ if (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';'))
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_RETURN))
+ {
+ pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_RETURN;
+ if (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';'))
+ {
+ rc = vdScriptParseExpression(pThis, &pAstNodeStmt->pExpr);
+ if ( RT_SUCCESS(rc)
+ && !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';'))
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n");
+ }
+ else
+ pAstNodeStmt->pExpr = NULL; /* No expression for return. */
+ }
+ else
+ {
+ /* Must be an expression. */
+ pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_EXPRESSION;
+ rc = vdScriptParseExpression(pThis, &pAstNodeStmt->pExpr);
+ if ( RT_SUCCESS(rc)
+ && !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';'))
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n");
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeStmt = pAstNodeStmt;
+ else
+ vdScriptAstNodeFree(&pAstNodeStmt->Core);
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory creating statement node\n");
+ }
+
+ return rc;
+}
+
+/**
+ * Parses a compound statement.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeCompound Where to store the compound AST node on success.
+ */
+static int vdScriptParseCompoundStatement(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTMT *ppAstNodeCompound)
+{
+ int rc = VINF_SUCCESS;
+
+ if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '{'))
+ {
+ PVDSCRIPTASTSTMT pAstNodeCompound = (PVDSCRIPTASTSTMT)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_STATEMENT);
+ if (pAstNodeCompound)
+ {
+ pAstNodeCompound->enmStmtType = VDSCRIPTSTMTTYPE_COMPOUND;
+ RTListInit(&pAstNodeCompound->Compound.ListDecls);
+ RTListInit(&pAstNodeCompound->Compound.ListStmts);
+ while (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '}'))
+ {
+ /*
+ * Check whether we have a declaration or a statement.
+ * For now we assume that 2 identifier tokens specify a declaration
+ * (type + variable name). Having two consecutive identifers is not possible
+ * for a statement.
+ */
+ if ( vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_IDENTIFIER
+ && vdScriptTokenizerPeekNextClass(pThis->pTokenizer) == VDTOKENCLASS_IDENTIFIER)
+ {
+ PVDSCRIPTASTDECL pAstNodeDecl = NULL;
+ rc = vdScriptParseDeclaration(pThis, &pAstNodeDecl);
+ if (RT_SUCCESS(rc))
+ RTListAppend(&pAstNodeCompound->Compound.ListDecls, &pAstNodeDecl->Core.ListNode);
+ }
+ else
+ {
+ PVDSCRIPTASTSTMT pAstNodeStmt = NULL;
+ rc = vdScriptParseStatement(pThis, &pAstNodeStmt);
+ if (RT_SUCCESS(rc))
+ RTListAppend(&pAstNodeCompound->Compound.ListStmts, &pAstNodeStmt->Core.ListNode);
+ }
+
+ if (RT_FAILURE(rc))
+ break;
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeCompound = pAstNodeCompound;
+ else
+ vdScriptAstNodeFree(&pAstNodeCompound->Core);
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory creating compound statement node\n");
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"{\" got...\n");
+
+
+ return rc;
+}
+
+/**
+ * Parses a function definition from the given tokenizer.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ */
+static int vdScriptParseAddFnDef(PVDSCRIPTCTXINT pThis)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTIDE pRetType = NULL;
+ PVDSCRIPTASTIDE pFnIde = NULL;
+
+ LogFlowFunc(("pThis=%p\n", pThis));
+
+ /* Put return type on the stack. */
+ rc = vdScriptParseIde(pThis, &pRetType);
+ if (RT_SUCCESS(rc))
+ {
+ /* Function name */
+ rc = vdScriptParseIde(pThis, &pFnIde);
+ if (RT_SUCCESS(rc))
+ {
+ if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
+ {
+ PVDSCRIPTASTFN pAstNodeFn = (PVDSCRIPTASTFN)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_FUNCTION);
+
+ if (pAstNodeFn)
+ {
+ pAstNodeFn->pFnIde = pFnIde;
+ pAstNodeFn->pRetType = pRetType;
+ RTListInit(&pAstNodeFn->ListArgs);
+
+ pFnIde = NULL;
+ pRetType = NULL;
+
+ /* Parse parameter list, create empty parameter list AST node and put it on the stack. */
+ while (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
+ {
+ PVDSCRIPTASTIDE pArgType = NULL;
+ PVDSCRIPTASTIDE pArgIde = NULL;
+ /* Parse two identifiers, first one is the type, second the name. */
+ rc = vdScriptParseIde(pThis, &pArgType);
+ if (RT_SUCCESS(rc))
+ rc = vdScriptParseIde(pThis, &pArgIde);
+
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTFNARG pAstNodeFnArg = (PVDSCRIPTASTFNARG)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_FUNCTIONARG);
+ if (pAstNodeFnArg)
+ {
+ pAstNodeFnArg->pArgIde = pArgIde;
+ pAstNodeFnArg->pType = pArgType;
+ RTListAppend(&pAstNodeFn->ListArgs, &pAstNodeFnArg->Core.ListNode);
+ pAstNodeFn->cArgs++;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating function argument AST node\n");
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ if (pArgType)
+ vdScriptAstNodeFree(&pArgType->Core);
+ if (pArgIde)
+ vdScriptAstNodeFree(&pArgIde->Core);
+ }
+
+ if ( !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ',')
+ && !vdScriptTokenizerIsPunctuatorEqual(pThis->pTokenizer, ')'))
+ {
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \",\" or \")\" got...\n");
+ break;
+ }
+ }
+
+ /* Parse the compound or statement block now. */
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTSTMT pAstCompound = NULL;
+
+ rc = vdScriptParseCompoundStatement(pThis, &pAstCompound);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Link compound statement block to function AST node and add it to the
+ * list of functions.
+ */
+ pAstNodeFn->pCompoundStmts = pAstCompound;
+ RTListAppend(&pThis->ListAst, &pAstNodeFn->Core.ListNode);
+
+ PVDSCRIPTFN pFn = (PVDSCRIPTFN)RTMemAllocZ(sizeof(VDSCRIPTFN));
+ if (pFn)
+ {
+ pFn->Core.pszString = pAstNodeFn->pFnIde->aszIde;
+ pFn->Core.cchString = strlen(pFn->Core.pszString);
+ pFn->fExternal = false;
+ pFn->Type.Internal.pAstFn = pAstNodeFn;
+ /** @todo: Parameters. */
+ RTStrSpaceInsert(&pThis->hStrSpaceFn, &pFn->Core);
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory allocating memory for function\n");
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ vdScriptAstNodeFree(&pAstNodeFn->Core);
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating function AST node\n");
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\" got...\n");
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ if (pRetType)
+ vdScriptAstNodeFree(&pRetType->Core);
+ if (pFnIde)
+ vdScriptAstNodeFree(&pFnIde->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parses the script from the given tokenizer.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ */
+static int vdScriptParseFromTokenizer(PVDSCRIPTCTXINT pThis)
+{
+ int rc = VINF_SUCCESS;
+
+ LogFlowFunc(("pThis=%p\n", pThis));
+
+ /* This is a very very simple LL(1) parser, don't expect much from it for now :). */
+ while ( RT_SUCCESS(rc)
+ && !vdScriptTokenizerIsEos(pThis->pTokenizer))
+ rc = vdScriptParseAddFnDef(pThis);
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+DECLHIDDEN(int) VDScriptCtxCreate(PVDSCRIPTCTX phScriptCtx)
+{
+ int rc = VINF_SUCCESS;
+
+ LogFlowFunc(("phScriptCtx=%p\n", phScriptCtx));
+
+ AssertPtrReturn(phScriptCtx, VERR_INVALID_POINTER);
+
+ PVDSCRIPTCTXINT pThis = (PVDSCRIPTCTXINT)RTMemAllocZ(sizeof(VDSCRIPTCTXINT));
+ if (pThis)
+ {
+ pThis->hStrSpaceFn = NULL;
+ RTListInit(&pThis->ListAst);
+ *phScriptCtx = pThis;
+ }
+ else
+ rc = VINF_SUCCESS;
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+static int vdScriptCtxDestroyFnSpace(PRTSTRSPACECORE pStr, void *pvUser)
+{
+ NOREF(pvUser);
+
+ /*
+ * Just free the whole structure, the AST for internal functions will be
+ * destroyed later.
+ */
+ RTMemFree(pStr);
+ return VINF_SUCCESS;
+}
+
+DECLHIDDEN(void) VDScriptCtxDestroy(VDSCRIPTCTX hScriptCtx)
+{
+ PVDSCRIPTCTXINT pThis = hScriptCtx;
+
+ AssertPtrReturnVoid(pThis);
+
+ LogFlowFunc(("hScriptCtx=%p\n", pThis));
+
+ RTStrSpaceDestroy(&pThis->hStrSpaceFn, vdScriptCtxDestroyFnSpace, NULL);
+
+ /** @todo: Go through the list and destroy all ASTs. */
+ RTMemFree(pThis);
+}
+
+DECLHIDDEN(int) VDScriptCtxCallbacksRegister(VDSCRIPTCTX hScriptCtx, PCVDSCRIPTCALLBACK paCallbacks,
+ unsigned cCallbacks, void *pvUser)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTCTXINT pThis = hScriptCtx;
+
+ LogFlowFunc(("hScriptCtx=%p paCallbacks=%p cCallbacks=%u pvUser=%p\n",
+ pThis, paCallbacks, cCallbacks, pvUser));
+
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertPtrReturn(paCallbacks, VERR_INVALID_POINTER);
+ AssertReturn(cCallbacks > 0, VERR_INVALID_PARAMETER);
+
+ /** @todo: Unregister already registered callbacks in case of an error. */
+ do
+ {
+ PVDSCRIPTFN pFn = NULL;
+
+ if (RTStrSpaceGet(&pThis->hStrSpaceFn, paCallbacks->pszFnName))
+ {
+ rc = VERR_DUPLICATE;
+ break;
+ }
+
+ pFn = (PVDSCRIPTFN)RTMemAllocZ(RT_OFFSETOF(VDSCRIPTFN, aenmArgTypes[paCallbacks->cArgs]));
+ if (!pFn)
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+
+ /** @todo: Validate argument and returns types. */
+ pFn->Core.pszString = paCallbacks->pszFnName;
+ pFn->Core.cchString = strlen(pFn->Core.pszString);
+ pFn->fExternal = true;
+ pFn->Type.External.pfnCallback = paCallbacks->pfnCallback;
+ pFn->Type.External.pvUser = pvUser;
+ pFn->enmTypeRetn = paCallbacks->enmTypeReturn;
+ pFn->cArgs = paCallbacks->cArgs;
+
+ for (unsigned i = 0; i < paCallbacks->cArgs; i++)
+ pFn->aenmArgTypes[i] = paCallbacks->paArgs[i];
+
+ RTStrSpaceInsert(&pThis->hStrSpaceFn, &pFn->Core);
+ cCallbacks--;
+ paCallbacks++;
+ }
+ while (cCallbacks);
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+DECLHIDDEN(int) VDScriptCtxLoadScript(VDSCRIPTCTX hScriptCtx, const char *pszScript)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTCTXINT pThis = hScriptCtx;
+
+ LogFlowFunc(("hScriptCtx=%p pszScript=%p\n", pThis, pszScript));
+
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszScript, VERR_INVALID_POINTER);
+
+ PVDTOKENIZER pTokenizer = vdScriptTokenizerCreate(pszScript);
+ if (pTokenizer)
+ {
+ pThis->pTokenizer = pTokenizer;
+ rc = vdScriptParseFromTokenizer(pThis);
+ pThis->pTokenizer = NULL;
+ RTMemFree(pTokenizer);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+DECLHIDDEN(int) VDScriptCtxCallFn(VDSCRIPTCTX hScriptCtx, const char *pszFnCall,
+ PVDSCRIPTARG paArgs, unsigned cArgs)
+{
+ PVDSCRIPTCTXINT pThis = hScriptCtx;
+ VDSCRIPTARG Ret;
+ return vdScriptCtxInterprete(pThis, pszFnCall, paArgs, cArgs, &Ret);
+}
diff --git a/src/VBox/Storage/testcase/VDScript.h b/src/VBox/Storage/testcase/VDScript.h
new file mode 100644
index 00000000..f1731dfd
--- /dev/null
+++ b/src/VBox/Storage/testcase/VDScript.h
@@ -0,0 +1,153 @@
+/** @file
+ *
+ * VBox HDD container test utility - scripting engine.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#ifndef _VDScript_h__
+#define _VDScript_h__
+
+/** Handle to the scripting context. */
+typedef struct VDSCRIPTCTXINT *VDSCRIPTCTX;
+/** Pointer to a scripting context handle. */
+typedef VDSCRIPTCTX *PVDSCRIPTCTX;
+
+/**
+ * Supprted primitive types in the scripting engine.
+ */
+typedef enum VDSCRIPTTYPE
+{
+ /** Invalid type, do not use. */
+ VDSCRIPTTYPE_INVALID = 0,
+ /** void type, used for no return value of methods. */
+ VDSCRIPTTYPE_VOID,
+ /** unsigned 8bit integer. */
+ VDSCRIPTTYPE_UINT8,
+ VDSCRIPTTYPE_INT8,
+ VDSCRIPTTYPE_UINT16,
+ VDSCRIPTTYPE_INT16,
+ VDSCRIPTTYPE_UINT32,
+ VDSCRIPTTYPE_INT32,
+ VDSCRIPTTYPE_UINT64,
+ VDSCRIPTTYPE_INT64,
+ VDSCRIPTTYPE_STRING,
+ VDSCRIPTTYPE_BOOL,
+ /** As usual, the 32bit blowup hack. */
+ VDSCRIPTTYPE_32BIT_HACK = 0x7fffffff
+} VDSCRIPTTYPE;
+/** Pointer to a type. */
+typedef VDSCRIPTTYPE *PVDSCRIPTTYPE;
+/** Pointer to a const type. */
+typedef const VDSCRIPTTYPE *PCVDSCRIPTTYPE;
+
+/**
+ * Script argument.
+ */
+typedef struct VDSCRIPTARG
+{
+ /** Type of the argument. */
+ VDSCRIPTTYPE enmType;
+ /** Value */
+ union
+ {
+ uint8_t u8;
+ int8_t i8;
+ uint16_t u16;
+ int16_t i16;
+ uint32_t u32;
+ int32_t i32;
+ uint64_t u64;
+ int64_t i64;
+ const char *psz;
+ bool f;
+ };
+} VDSCRIPTARG;
+/** Pointer to an argument. */
+typedef VDSCRIPTARG *PVDSCRIPTARG;
+
+/** Script callback. */
+typedef DECLCALLBACK(int) FNVDSCRIPTCALLBACK(PVDSCRIPTARG paScriptArgs, void *pvUser);
+/** Pointer to a script callback. */
+typedef FNVDSCRIPTCALLBACK *PFNVDSCRIPTCALLBACK;
+
+/**
+ * Callback registration structure.
+ */
+typedef struct VDSCRIPTCALLBACK
+{
+ /** The function name. */
+ const char *pszFnName;
+ /** The return type of the function. */
+ VDSCRIPTTYPE enmTypeReturn;
+ /** Pointer to the array of argument types. */
+ PCVDSCRIPTTYPE paArgs;
+ /** Number of arguments this method takes. */
+ unsigned cArgs;
+ /** The callback handler. */
+ PFNVDSCRIPTCALLBACK pfnCallback;
+} VDSCRIPTCALLBACK;
+/** Pointer to a callback register entry. */
+typedef VDSCRIPTCALLBACK *PVDSCRIPTCALLBACK;
+/** Pointer to a const callback register entry. */
+typedef const VDSCRIPTCALLBACK *PCVDSCRIPTCALLBACK;
+
+/**
+ * Create a new scripting context.
+ *
+ * @returns VBox status code.
+ * @param phScriptCtx Where to store the scripting context on success.
+ */
+DECLHIDDEN(int) VDScriptCtxCreate(PVDSCRIPTCTX phScriptCtx);
+
+/**
+ * Destroys the given scripting context.
+ *
+ * @returns nothing.
+ * @param hScriptCtx The script context to destroy.
+ */
+DECLHIDDEN(void) VDScriptCtxDestroy(VDSCRIPTCTX hScriptCtx);
+
+/**
+ * Register callbacks for the scripting context.
+ *
+ * @returns VBox status code.
+ * @param hScriptCtx The script context handle.
+ * @param paCallbacks Pointer to the callbacks to register.
+ * @param cCallbacks Number of callbacks in the array.
+ * @param pvUser Opaque user data to pass on the callback invocation.
+ */
+DECLHIDDEN(int) VDScriptCtxCallbacksRegister(VDSCRIPTCTX hScriptCtx, PCVDSCRIPTCALLBACK paCallbacks,
+ unsigned cCallbacks, void *pvUser);
+
+/**
+ * Load a given script into the context.
+ *
+ * @returns VBox status code.
+ * @param hScriptCtx The script context handle.
+ * @param pszScript Pointer to the char buffer containing the script.
+ */
+DECLHIDDEN(int) VDScriptCtxLoadScript(VDSCRIPTCTX hScriptCtx, const char *pszScript);
+
+/**
+ * Execute a given method in the script context.
+ *
+ * @returns VBox status code.
+ * @param hScriptCtx The script context handle.
+ * @param pszFnCall The method to call.
+ * @param paArgs Pointer to arguments to pass.
+ * @param cArgs Number of arguments.
+ */
+DECLHIDDEN(int) VDScriptCtxCallFn(VDSCRIPTCTX hScriptCtx, const char *pszFnCall,
+ PVDSCRIPTARG paArgs, unsigned cArgs);
+
+#endif /* _VDScript_h__ */
diff --git a/src/VBox/Storage/testcase/VDScriptAst.cpp b/src/VBox/Storage/testcase/VDScriptAst.cpp
new file mode 100644
index 00000000..8b3d0011
--- /dev/null
+++ b/src/VBox/Storage/testcase/VDScriptAst.cpp
@@ -0,0 +1,343 @@
+/** $Id: VDScriptAst.cpp $ */
+/** @file
+ *
+ * VBox HDD container test utility - scripting engine AST node related functions.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#define LOGGROUP LOGGROUP_DEFAULT
+#include <iprt/list.h>
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+
+#include <VBox/log.h>
+
+#include "VDScriptAst.h"
+
+/**
+ * Put all child nodes of the given expression AST node onto the given to free list.
+ *
+ * @returns nothing.
+ * @param pList The free list to append everything to.
+ * @param pAstNode The expression node to free.
+ */
+static void vdScriptAstNodeExpressionPutOnFreeList(PRTLISTANCHOR pList, PVDSCRIPTASTCORE pAstNode)
+{
+ AssertMsgReturnVoid(pAstNode->enmClass == VDSCRIPTASTCLASS_EXPRESSION,
+ ("Given AST node is not a statement\n"));
+
+ PVDSCRIPTASTEXPR pExpr = (PVDSCRIPTASTEXPR)pAstNode;
+ switch (pExpr->enmType)
+ {
+ case VDSCRIPTEXPRTYPE_PRIMARY_NUMCONST:
+ case VDSCRIPTEXPRTYPE_PRIMARY_BOOLEAN:
+ break;
+ case VDSCRIPTEXPRTYPE_PRIMARY_STRINGCONST:
+ RTStrFree((char *)pExpr->pszStr);
+ break;
+ case VDSCRIPTEXPRTYPE_PRIMARY_IDENTIFIER:
+ {
+ RTListAppend(pList, &pExpr->pIde->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTEXPRTYPE_ASSIGNMENT_LIST:
+ {
+ while (!RTListIsEmpty(&pExpr->ListExpr))
+ {
+ PVDSCRIPTASTCORE pNode = RTListGetFirst(&pExpr->ListExpr, VDSCRIPTASTCORE, ListNode);
+ RTListNodeRemove(&pNode->ListNode);
+ RTListAppend(pList, &pNode->ListNode);
+ }
+ break;
+ }
+ case VDSCRIPTEXPRTYPE_POSTFIX_FNCALL:
+ {
+ RTListAppend(pList, &pExpr->FnCall.pFnIde->Core.ListNode);
+ while (!RTListIsEmpty(&pExpr->FnCall.ListArgs))
+ {
+ PVDSCRIPTASTCORE pNode = RTListGetFirst(&pExpr->FnCall.ListArgs, VDSCRIPTASTCORE, ListNode);
+ RTListNodeRemove(&pNode->ListNode);
+ RTListAppend(pList, &pNode->ListNode);
+ }
+ break;
+ }
+ case VDSCRIPTEXPRTYPE_POSTFIX_INCREMENT:
+ case VDSCRIPTEXPRTYPE_POSTFIX_DECREMENT:
+ case VDSCRIPTEXPRTYPE_UNARY_INCREMENT:
+ case VDSCRIPTEXPRTYPE_UNARY_DECREMENT:
+ case VDSCRIPTEXPRTYPE_UNARY_POSSIGN:
+ case VDSCRIPTEXPRTYPE_UNARY_NEGSIGN:
+ case VDSCRIPTEXPRTYPE_UNARY_INVERT:
+ case VDSCRIPTEXPRTYPE_UNARY_NEGATE:
+ {
+ RTListAppend(pList, &pExpr->pExpr->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTEXPRTYPE_MULTIPLICATION:
+ case VDSCRIPTEXPRTYPE_DIVISION:
+ case VDSCRIPTEXPRTYPE_MODULUS:
+ case VDSCRIPTEXPRTYPE_ADDITION:
+ case VDSCRIPTEXPRTYPE_SUBTRACTION:
+ case VDSCRIPTEXPRTYPE_LSR:
+ case VDSCRIPTEXPRTYPE_LSL:
+ case VDSCRIPTEXPRTYPE_LOWER:
+ case VDSCRIPTEXPRTYPE_HIGHER:
+ case VDSCRIPTEXPRTYPE_LOWEREQUAL:
+ case VDSCRIPTEXPRTYPE_HIGHEREQUAL:
+ case VDSCRIPTEXPRTYPE_EQUAL:
+ case VDSCRIPTEXPRTYPE_NOTEQUAL:
+ case VDSCRIPTEXPRTYPE_BITWISE_AND:
+ case VDSCRIPTEXPRTYPE_BITWISE_XOR:
+ case VDSCRIPTEXPRTYPE_BITWISE_OR:
+ case VDSCRIPTEXPRTYPE_LOGICAL_AND:
+ case VDSCRIPTEXPRTYPE_LOGICAL_OR:
+ case VDSCRIPTEXPRTYPE_ASSIGN:
+ case VDSCRIPTEXPRTYPE_ASSIGN_MULT:
+ case VDSCRIPTEXPRTYPE_ASSIGN_DIV:
+ case VDSCRIPTEXPRTYPE_ASSIGN_MOD:
+ case VDSCRIPTEXPRTYPE_ASSIGN_ADD:
+ case VDSCRIPTEXPRTYPE_ASSIGN_SUB:
+ case VDSCRIPTEXPRTYPE_ASSIGN_LSL:
+ case VDSCRIPTEXPRTYPE_ASSIGN_LSR:
+ case VDSCRIPTEXPRTYPE_ASSIGN_AND:
+ case VDSCRIPTEXPRTYPE_ASSIGN_XOR:
+ case VDSCRIPTEXPRTYPE_ASSIGN_OR:
+ {
+ RTListAppend(pList, &pExpr->BinaryOp.pLeftExpr->Core.ListNode);
+ RTListAppend(pList, &pExpr->BinaryOp.pRightExpr->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTEXPRTYPE_INVALID:
+ default:
+ AssertMsgFailedReturnVoid(("Invalid AST node expression type %d\n",
+ pExpr->enmType));
+ }
+}
+
+/**
+ * Free a given statement AST node and put everything on the given to free list.
+ *
+ * @returns nothing.
+ * @param pList The free list to append everything to.
+ * @param pAstNode The statement node to free.
+ */
+static void vdScriptAstNodeStatmentPutOnFreeList(PRTLISTANCHOR pList, PVDSCRIPTASTCORE pAstNode)
+{
+ AssertMsgReturnVoid(pAstNode->enmClass == VDSCRIPTASTCLASS_STATEMENT,
+ ("Given AST node is not a statement\n"));
+
+ PVDSCRIPTASTSTMT pStmt = (PVDSCRIPTASTSTMT)pAstNode;
+ switch (pStmt->enmStmtType)
+ {
+ case VDSCRIPTSTMTTYPE_COMPOUND:
+ {
+ /* Put all declarations on the to free list. */
+ while (!RTListIsEmpty(&pStmt->Compound.ListDecls))
+ {
+ PVDSCRIPTASTCORE pNode = RTListGetFirst(&pStmt->Compound.ListDecls, VDSCRIPTASTCORE, ListNode);
+ RTListNodeRemove(&pNode->ListNode);
+ RTListAppend(pList, &pNode->ListNode);
+ }
+
+ /* Put all statements on the to free list. */
+ while (!RTListIsEmpty(&pStmt->Compound.ListStmts))
+ {
+ PVDSCRIPTASTCORE pNode = RTListGetFirst(&pStmt->Compound.ListDecls, VDSCRIPTASTCORE, ListNode);
+ RTListNodeRemove(&pNode->ListNode);
+ RTListAppend(pList, &pNode->ListNode);
+ }
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_EXPRESSION:
+ {
+ if (pStmt->pExpr)
+ RTListAppend(pList, &pStmt->pExpr->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_IF:
+ {
+ RTListAppend(pList, &pStmt->If.pCond->Core.ListNode);
+ RTListAppend(pList, &pStmt->If.pTrueStmt->Core.ListNode);
+ if (pStmt->If.pElseStmt)
+ RTListAppend(pList, &pStmt->If.pElseStmt->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_SWITCH:
+ {
+ RTListAppend(pList, &pStmt->Switch.pCond->Core.ListNode);
+ RTListAppend(pList, &pStmt->Switch.pStmt->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_WHILE:
+ {
+ RTListAppend(pList, &pStmt->While.pCond->Core.ListNode);
+ RTListAppend(pList, &pStmt->While.pStmt->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_FOR:
+ {
+ RTListAppend(pList, &pStmt->For.pExprStart->Core.ListNode);
+ RTListAppend(pList, &pStmt->For.pExprCond->Core.ListNode);
+ RTListAppend(pList, &pStmt->For.pExpr3->Core.ListNode);
+ RTListAppend(pList, &pStmt->For.pStmt->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_RETURN:
+ {
+ if (pStmt->pExpr)
+ RTListAppend(pList, &pStmt->pExpr->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_CASE:
+ {
+ RTListAppend(pList, &pStmt->Case.pExpr->Core.ListNode);
+ RTListAppend(pList, &pStmt->Case.pStmt->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_DEFAULT:
+ {
+ RTListAppend(pList, &pStmt->Case.pStmt->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_CONTINUE:
+ case VDSCRIPTSTMTTYPE_BREAK:
+ break;
+ case VDSCRIPTSTMTTYPE_INVALID:
+ default:
+ AssertMsgFailedReturnVoid(("Invalid AST node statement type %d\n",
+ pStmt->enmStmtType));
+ }
+}
+
+DECLHIDDEN(void) vdScriptAstNodeFree(PVDSCRIPTASTCORE pAstNode)
+{
+ RTLISTANCHOR ListFree;
+
+ /*
+ * The node is not allowed to be part of a list because we need it
+ * for the nodes to free list.
+ */
+ Assert(RTListIsEmpty(&pAstNode->ListNode));
+ RTListInit(&ListFree);
+ RTListAppend(&ListFree, &pAstNode->ListNode);
+
+ do
+ {
+ pAstNode = RTListGetFirst(&ListFree, VDSCRIPTASTCORE, ListNode);
+ RTListNodeRemove(&pAstNode->ListNode);
+
+ switch (pAstNode->enmClass)
+ {
+ case VDSCRIPTASTCLASS_FUNCTION:
+ {
+ PVDSCRIPTASTFN pFn = (PVDSCRIPTASTFN)pAstNode;
+
+ if (pFn->pRetType)
+ RTListAppend(&ListFree, &pFn->pRetType->Core.ListNode);
+ if (pFn->pFnIde)
+ RTListAppend(&ListFree, &pFn->pFnIde->Core.ListNode);
+
+ /* Put argument list on the to free list. */
+ while (!RTListIsEmpty(&pFn->ListArgs))
+ {
+ PVDSCRIPTASTCORE pArg = RTListGetFirst(&pFn->ListArgs, VDSCRIPTASTCORE, ListNode);
+ RTListNodeRemove(&pArg->ListNode);
+ RTListAppend(&ListFree, &pArg->ListNode);
+ }
+
+ /* Put compound statement onto the list. */
+ RTListAppend(&ListFree, &pFn->pCompoundStmts->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTASTCLASS_FUNCTIONARG:
+ {
+ PVDSCRIPTASTFNARG pAstNodeArg = (PVDSCRIPTASTFNARG)pAstNode;
+ if (pAstNodeArg->pType)
+ RTListAppend(&ListFree, &pAstNodeArg->pType->Core.ListNode);
+ if (pAstNodeArg->pArgIde)
+ RTListAppend(&ListFree, &pAstNodeArg->pArgIde->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTASTCLASS_IDENTIFIER:
+ break;
+ case VDSCRIPTASTCLASS_DECLARATION:
+ break;
+ case VDSCRIPTASTCLASS_STATEMENT:
+ {
+ vdScriptAstNodeStatmentPutOnFreeList(&ListFree, pAstNode);
+ break;
+ }
+ case VDSCRIPTASTCLASS_EXPRESSION:
+ {
+ vdScriptAstNodeExpressionPutOnFreeList(&ListFree, pAstNode);
+ break;
+ }
+ case VDSCRIPTASTCLASS_INVALID:
+ default:
+ AssertMsgFailedReturnVoid(("Invalid AST node class given %d\n", pAstNode->enmClass));
+ }
+
+ RTMemFree(pAstNode);
+ } while (!RTListIsEmpty(&ListFree));
+
+}
+
+DECLHIDDEN(PVDSCRIPTASTCORE) vdScriptAstNodeAlloc(VDSCRIPTASTCLASS enmClass)
+{
+ size_t cbAlloc = 0;
+
+ switch (enmClass)
+ {
+ case VDSCRIPTASTCLASS_FUNCTION:
+ cbAlloc = sizeof(VDSCRIPTASTFN);
+ break;
+ case VDSCRIPTASTCLASS_FUNCTIONARG:
+ cbAlloc = sizeof(VDSCRIPTASTFNARG);
+ break;
+ case VDSCRIPTASTCLASS_DECLARATION:
+ cbAlloc = sizeof(VDSCRIPTASTDECL);
+ break;
+ case VDSCRIPTASTCLASS_STATEMENT:
+ cbAlloc = sizeof(VDSCRIPTASTSTMT);
+ break;
+ case VDSCRIPTASTCLASS_EXPRESSION:
+ cbAlloc = sizeof(VDSCRIPTASTEXPR);
+ break;
+ case VDSCRIPTASTCLASS_IDENTIFIER:
+ case VDSCRIPTASTCLASS_INVALID:
+ default:
+ AssertMsgFailedReturn(("Invalid AST node class given %d\n", enmClass), NULL);
+ }
+
+ PVDSCRIPTASTCORE pAstNode = (PVDSCRIPTASTCORE)RTMemAllocZ(cbAlloc);
+ if (pAstNode)
+ {
+ pAstNode->enmClass = enmClass;
+ RTListInit(&pAstNode->ListNode);
+ }
+
+ return pAstNode;
+}
+
+DECLHIDDEN(PVDSCRIPTASTIDE) vdScriptAstNodeIdeAlloc(unsigned cchIde)
+{
+ PVDSCRIPTASTIDE pAstNode = (PVDSCRIPTASTIDE)RTMemAllocZ(RT_OFFSETOF(VDSCRIPTASTIDE, aszIde[cchIde + 1]));
+ if (pAstNode)
+ {
+ pAstNode->Core.enmClass = VDSCRIPTASTCLASS_IDENTIFIER;
+ RTListInit(&pAstNode->Core.ListNode);
+ }
+
+ return pAstNode;
+}
diff --git a/src/VBox/Storage/testcase/VDScriptAst.h b/src/VBox/Storage/testcase/VDScriptAst.h
new file mode 100644
index 00000000..3f324094
--- /dev/null
+++ b/src/VBox/Storage/testcase/VDScriptAst.h
@@ -0,0 +1,452 @@
+/** @file
+ *
+ * VBox HDD container test utility - scripting engine, AST related structures.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#ifndef _VDScriptAst_h__
+#define _VDScriptAst_h__
+
+#include <iprt/list.h>
+
+/**
+ * Position information.
+ */
+typedef struct VDSRCPOS
+{
+ /** Line in the source. */
+ unsigned iLine;
+ /** Current start character .*/
+ unsigned iChStart;
+ /** Current end character. */
+ unsigned iChEnd;
+} VDSRCPOS;
+/** Pointer to a source position. */
+typedef struct VDSRCPOS *PVDSRCPOS;
+
+/**
+ * AST node classes.
+ */
+typedef enum VDSCRIPTASTCLASS
+{
+ /** Invalid. */
+ VDSCRIPTASTCLASS_INVALID = 0,
+ /** Function node. */
+ VDSCRIPTASTCLASS_FUNCTION,
+ /** Function argument. */
+ VDSCRIPTASTCLASS_FUNCTIONARG,
+ /** Identifier node. */
+ VDSCRIPTASTCLASS_IDENTIFIER,
+ /** Declaration node. */
+ VDSCRIPTASTCLASS_DECLARATION,
+ /** Statement node. */
+ VDSCRIPTASTCLASS_STATEMENT,
+ /** Expression node. */
+ VDSCRIPTASTCLASS_EXPRESSION,
+ /** 32bit blowup. */
+ VDSCRIPTASTCLASS_32BIT_HACK = 0x7fffffff
+} VDSCRIPTASTCLASS;
+/** Pointer to an AST node class. */
+typedef VDSCRIPTASTCLASS *PVDSCRIPTASTCLASS;
+
+/**
+ * Core AST structure.
+ */
+typedef struct VDSCRIPTASTCORE
+{
+ /** The node class, used for verification. */
+ VDSCRIPTASTCLASS enmClass;
+ /** List which might be used. */
+ RTLISTNODE ListNode;
+ /** Position in the source file of this node. */
+ VDSRCPOS Pos;
+} VDSCRIPTASTCORE;
+/** Pointer to an AST core structure. */
+typedef VDSCRIPTASTCORE *PVDSCRIPTASTCORE;
+
+/** Pointer to an statement node - forward declaration. */
+typedef struct VDSCRIPTASTSTMT *PVDSCRIPTASTSTMT;
+/** Pointer to an expression node - forward declaration. */
+typedef struct VDSCRIPTASTEXPR *PVDSCRIPTASTEXPR;
+
+/**
+ * AST identifier node.
+ */
+typedef struct VDSCRIPTASTIDE
+{
+ /** Core structure. */
+ VDSCRIPTASTCORE Core;
+ /** Number of characters in the identifier, excluding the zero terminator. */
+ unsigned cchIde;
+ /** Identifier, variable size. */
+ char aszIde[1];
+} VDSCRIPTASTIDE;
+/** Pointer to an identifer node. */
+typedef VDSCRIPTASTIDE *PVDSCRIPTASTIDE;
+
+/**
+ * AST declaration node.
+ */
+typedef struct VDSCRIPTASTDECL
+{
+ /** Core structure. */
+ VDSCRIPTASTCORE Core;
+ /** @todo */
+} VDSCRIPTASTDECL;
+/** Pointer to an declaration node. */
+typedef VDSCRIPTASTDECL *PVDSCRIPTASTDECL;
+
+/**
+ * Expression types.
+ */
+typedef enum VDSCRIPTEXPRTYPE
+{
+ /** Invalid. */
+ VDSCRIPTEXPRTYPE_INVALID = 0,
+ /** Numerical constant. */
+ VDSCRIPTEXPRTYPE_PRIMARY_NUMCONST,
+ /** String constant. */
+ VDSCRIPTEXPRTYPE_PRIMARY_STRINGCONST,
+ /** Boolean constant. */
+ VDSCRIPTEXPRTYPE_PRIMARY_BOOLEAN,
+ /** Identifier. */
+ VDSCRIPTEXPRTYPE_PRIMARY_IDENTIFIER,
+ /** List of assignment expressions as in a = b = c = ... . */
+ VDSCRIPTEXPRTYPE_ASSIGNMENT_LIST,
+ /** Postfix increment expression. */
+ VDSCRIPTEXPRTYPE_POSTFIX_INCREMENT,
+ /** Postfix decrement expression. */
+ VDSCRIPTEXPRTYPE_POSTFIX_DECREMENT,
+ /** Postfix function call expression. */
+ VDSCRIPTEXPRTYPE_POSTFIX_FNCALL,
+ /** Unary increment expression. */
+ VDSCRIPTEXPRTYPE_UNARY_INCREMENT,
+ /** Unary decrement expression. */
+ VDSCRIPTEXPRTYPE_UNARY_DECREMENT,
+ /** Unary positive sign expression. */
+ VDSCRIPTEXPRTYPE_UNARY_POSSIGN,
+ /** Unary negtive sign expression. */
+ VDSCRIPTEXPRTYPE_UNARY_NEGSIGN,
+ /** Unary invert expression. */
+ VDSCRIPTEXPRTYPE_UNARY_INVERT,
+ /** Unary negate expression. */
+ VDSCRIPTEXPRTYPE_UNARY_NEGATE,
+ /** Multiplicative expression. */
+ VDSCRIPTEXPRTYPE_MULTIPLICATION,
+ /** Division expression. */
+ VDSCRIPTEXPRTYPE_DIVISION,
+ /** Modulus expression. */
+ VDSCRIPTEXPRTYPE_MODULUS,
+ /** Addition expression. */
+ VDSCRIPTEXPRTYPE_ADDITION,
+ /** Subtraction expression. */
+ VDSCRIPTEXPRTYPE_SUBTRACTION,
+ /** Logical shift right. */
+ VDSCRIPTEXPRTYPE_LSR,
+ /** Logical shift left. */
+ VDSCRIPTEXPRTYPE_LSL,
+ /** Lower than expression */
+ VDSCRIPTEXPRTYPE_LOWER,
+ /** Higher than expression */
+ VDSCRIPTEXPRTYPE_HIGHER,
+ /** Lower or equal than expression */
+ VDSCRIPTEXPRTYPE_LOWEREQUAL,
+ /** Higher or equal than expression */
+ VDSCRIPTEXPRTYPE_HIGHEREQUAL,
+ /** Equals expression */
+ VDSCRIPTEXPRTYPE_EQUAL,
+ /** Not equal expression */
+ VDSCRIPTEXPRTYPE_NOTEQUAL,
+ /** Bitwise and expression */
+ VDSCRIPTEXPRTYPE_BITWISE_AND,
+ /** Bitwise xor expression */
+ VDSCRIPTEXPRTYPE_BITWISE_XOR,
+ /** Bitwise or expression */
+ VDSCRIPTEXPRTYPE_BITWISE_OR,
+ /** Logical and expression */
+ VDSCRIPTEXPRTYPE_LOGICAL_AND,
+ /** Logical or expression */
+ VDSCRIPTEXPRTYPE_LOGICAL_OR,
+ /** Assign expression */
+ VDSCRIPTEXPRTYPE_ASSIGN,
+ /** Multiplicative assign expression */
+ VDSCRIPTEXPRTYPE_ASSIGN_MULT,
+ /** Division assign expression */
+ VDSCRIPTEXPRTYPE_ASSIGN_DIV,
+ /** Modulus assign expression */
+ VDSCRIPTEXPRTYPE_ASSIGN_MOD,
+ /** Additive assign expression */
+ VDSCRIPTEXPRTYPE_ASSIGN_ADD,
+ /** Subtractive assign expression */
+ VDSCRIPTEXPRTYPE_ASSIGN_SUB,
+ /** Bitwise left shift assign expression */
+ VDSCRIPTEXPRTYPE_ASSIGN_LSL,
+ /** Bitwise right shift assign expression */
+ VDSCRIPTEXPRTYPE_ASSIGN_LSR,
+ /** Bitwise and assign expression */
+ VDSCRIPTEXPRTYPE_ASSIGN_AND,
+ /** Bitwise xor assign expression */
+ VDSCRIPTEXPRTYPE_ASSIGN_XOR,
+ /** Bitwise or assign expression */
+ VDSCRIPTEXPRTYPE_ASSIGN_OR,
+ /** 32bit hack. */
+ VDSCRIPTEXPRTYPE_32BIT_HACK = 0x7fffffff
+} VDSCRIPTEXPRTYPE;
+/** Pointer to an expression type. */
+typedef VDSCRIPTEXPRTYPE *PVDSCRIPTEXPRTYPE;
+
+/**
+ * AST expression node.
+ */
+typedef struct VDSCRIPTASTEXPR
+{
+ /** Core structure. */
+ VDSCRIPTASTCORE Core;
+ /** Expression type. */
+ VDSCRIPTEXPRTYPE enmType;
+ /** Expression type dependent data. */
+ union
+ {
+ /** Numerical constant. */
+ uint64_t u64;
+ /** Primary identifier. */
+ PVDSCRIPTASTIDE pIde;
+ /** String literal */
+ const char *pszStr;
+ /** Boolean constant. */
+ bool f;
+ /** List of expressions - VDSCRIPTASTEXPR. */
+ RTLISTANCHOR ListExpr;
+ /** Pointer to another expression. */
+ PVDSCRIPTASTEXPR pExpr;
+ /** Function call expression. */
+ struct
+ {
+ /** Other postfix expression used as the identifier for the function. */
+ PVDSCRIPTASTEXPR pFnIde;
+ /** Argument list if existing. */
+ RTLISTANCHOR ListArgs;
+ } FnCall;
+ /** Binary operation. */
+ struct
+ {
+ /** Left operator. */
+ PVDSCRIPTASTEXPR pLeftExpr;
+ /** Right operator. */
+ PVDSCRIPTASTEXPR pRightExpr;
+ } BinaryOp;
+ };
+} VDSCRIPTASTEXPR;
+
+/**
+ * AST if node.
+ */
+typedef struct VDSCRIPTASTIF
+{
+ /** Conditional expression. */
+ PVDSCRIPTASTEXPR pCond;
+ /** The true branch */
+ PVDSCRIPTASTSTMT pTrueStmt;
+ /** The else branch, can be NULL if no else branch. */
+ PVDSCRIPTASTSTMT pElseStmt;
+} VDSCRIPTASTIF;
+/** Pointer to an expression node. */
+typedef VDSCRIPTASTIF *PVDSCRIPTASTIF;
+
+/**
+ * AST switch node.
+ */
+typedef struct VDSCRIPTASTSWITCH
+{
+ /** Conditional expression. */
+ PVDSCRIPTASTEXPR pCond;
+ /** The statement to follow. */
+ PVDSCRIPTASTSTMT pStmt;
+} VDSCRIPTASTSWITCH;
+/** Pointer to an expression node. */
+typedef VDSCRIPTASTSWITCH *PVDSCRIPTASTSWITCH;
+
+/**
+ * AST while or do ... while node.
+ */
+typedef struct VDSCRIPTASTWHILE
+{
+ /** Flag whether this is a do while loop. */
+ bool fDoWhile;
+ /** Conditional expression. */
+ PVDSCRIPTASTEXPR pCond;
+ /** The statement to follow. */
+ PVDSCRIPTASTSTMT pStmt;
+} VDSCRIPTASTWHILE;
+/** Pointer to an expression node. */
+typedef VDSCRIPTASTWHILE *PVDSCRIPTASTWHILE;
+
+/**
+ * AST for node.
+ */
+typedef struct VDSCRIPTASTFOR
+{
+ /** Initializer expression. */
+ PVDSCRIPTASTEXPR pExprStart;
+ /** The exit condition. */
+ PVDSCRIPTASTEXPR pExprCond;
+ /** The third expression (normally used to increase/decrease loop variable). */
+ PVDSCRIPTASTEXPR pExpr3;
+ /** The for loop body. */
+ PVDSCRIPTASTSTMT pStmt;
+} VDSCRIPTASTFOR;
+/** Pointer to an expression node. */
+typedef VDSCRIPTASTFOR *PVDSCRIPTASTFOR;
+
+/**
+ * Statement types.
+ */
+typedef enum VDSCRIPTSTMTTYPE
+{
+ /** Invalid. */
+ VDSCRIPTSTMTTYPE_INVALID = 0,
+ /** Compound statement. */
+ VDSCRIPTSTMTTYPE_COMPOUND,
+ /** Expression statement. */
+ VDSCRIPTSTMTTYPE_EXPRESSION,
+ /** if statement. */
+ VDSCRIPTSTMTTYPE_IF,
+ /** switch statement. */
+ VDSCRIPTSTMTTYPE_SWITCH,
+ /** while statement. */
+ VDSCRIPTSTMTTYPE_WHILE,
+ /** for statement. */
+ VDSCRIPTSTMTTYPE_FOR,
+ /** continue statement. */
+ VDSCRIPTSTMTTYPE_CONTINUE,
+ /** break statement. */
+ VDSCRIPTSTMTTYPE_BREAK,
+ /** return statement. */
+ VDSCRIPTSTMTTYPE_RETURN,
+ /** case statement. */
+ VDSCRIPTSTMTTYPE_CASE,
+ /** default statement. */
+ VDSCRIPTSTMTTYPE_DEFAULT,
+ /** 32bit hack. */
+ VDSCRIPTSTMTTYPE_32BIT_HACK = 0x7fffffff
+} VDSCRIPTSTMTTYPE;
+/** Pointer to a statement type. */
+typedef VDSCRIPTSTMTTYPE *PVDSCRIPTSTMTTYPE;
+
+/**
+ * AST statement node.
+ */
+typedef struct VDSCRIPTASTSTMT
+{
+ /** Core structure. */
+ VDSCRIPTASTCORE Core;
+ /** Statement type */
+ VDSCRIPTSTMTTYPE enmStmtType;
+ /** Statement type dependent data. */
+ union
+ {
+ /** Compound statement. */
+ struct
+ {
+ /** List of declarations - VDSCRIPTASTDECL. */
+ RTLISTANCHOR ListDecls;
+ /** List of statements - VDSCRIPTASTSTMT. */
+ RTLISTANCHOR ListStmts;
+ } Compound;
+ /** case, default statement. */
+ struct
+ {
+ /** Pointer to the expression. */
+ PVDSCRIPTASTEXPR pExpr;
+ /** Pointer to the statement. */
+ PVDSCRIPTASTSTMT pStmt;
+ } Case;
+ /** "if" statement. */
+ VDSCRIPTASTIF If;
+ /** "switch" statement. */
+ VDSCRIPTASTSWITCH Switch;
+ /** "while" or "do ... while" loop. */
+ VDSCRIPTASTWHILE While;
+ /** "for" loop. */
+ VDSCRIPTASTFOR For;
+ /** Pointer to another statement. */
+ PVDSCRIPTASTSTMT pStmt;
+ /** Expression statement. */
+ PVDSCRIPTASTEXPR pExpr;
+ };
+} VDSCRIPTASTSTMT;
+
+/**
+ * AST node for one function argument.
+ */
+typedef struct VDSCRIPTASTFNARG
+{
+ /** Core structure. */
+ VDSCRIPTASTCORE Core;
+ /** Identifier describing the type of the argument. */
+ PVDSCRIPTASTIDE pType;
+ /** The name of the argument. */
+ PVDSCRIPTASTIDE pArgIde;
+} VDSCRIPTASTFNARG;
+/** Pointer to a AST function argument node. */
+typedef VDSCRIPTASTFNARG *PVDSCRIPTASTFNARG;
+
+/**
+ * AST node describing a function.
+ */
+typedef struct VDSCRIPTASTFN
+{
+ /** Core structure. */
+ VDSCRIPTASTCORE Core;
+ /** Identifier describing the return type. */
+ PVDSCRIPTASTIDE pRetType;
+ /** Name of the function. */
+ PVDSCRIPTASTIDE pFnIde;
+ /** Number of arguments in the list. */
+ unsigned cArgs;
+ /** Argument list - VDSCRIPTASTFNARG. */
+ RTLISTANCHOR ListArgs;
+ /** Compound statement node. */
+ PVDSCRIPTASTSTMT pCompoundStmts;
+} VDSCRIPTASTFN;
+/** Pointer to a function AST node. */
+typedef VDSCRIPTASTFN *PVDSCRIPTASTFN;
+
+/**
+ * Free the given AST node and all subsequent nodes pointed to
+ * by the given node.
+ *
+ * @returns nothing.
+ * @param pAstNode The AST node to free.
+ */
+DECLHIDDEN(void) vdScriptAstNodeFree(PVDSCRIPTASTCORE pAstNode);
+
+/**
+ * Allocate a non variable in size AST node of the given class.
+ *
+ * @returns Pointer to the allocated node.
+ * NULL if out of memory.
+ * @param enmClass The class of the AST node.
+ */
+DECLHIDDEN(PVDSCRIPTASTCORE) vdScriptAstNodeAlloc(VDSCRIPTASTCLASS enmClass);
+
+/**
+ * Allocate a IDE node which can hold the given number of characters.
+ *
+ * @returns Pointer to the allocated node.
+ * NULL if out of memory.
+ * @param cchIde Number of characters which can be stored in the node.
+ */
+DECLHIDDEN(PVDSCRIPTASTIDE) vdScriptAstNodeIdeAlloc(unsigned cchIde);
+
+#endif /* _VDScriptAst_h__ */
diff --git a/src/VBox/Storage/testcase/VDScriptChecker.cpp b/src/VBox/Storage/testcase/VDScriptChecker.cpp
new file mode 100644
index 00000000..42dfd51a
--- /dev/null
+++ b/src/VBox/Storage/testcase/VDScriptChecker.cpp
@@ -0,0 +1,31 @@
+/** $Id: VDScriptChecker.cpp $ */
+/** @file
+ *
+ * VBox HDD container test utility - scripting engine, type and context checker.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#define LOGGROUP LOGGROUP_DEFAULT
+#include <iprt/list.h>
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+
+#include <VBox/log.h>
+
+#include "VDScriptAst.h"
+#include "VDScriptInternal.h"
+
+DECLHIDDEN(int) vdScriptCtxCheck(PVDSCRIPTCTXINT pThis)
+{
+ return VERR_NOT_IMPLEMENTED;
+}
diff --git a/src/VBox/Storage/testcase/VDScriptInternal.h b/src/VBox/Storage/testcase/VDScriptInternal.h
new file mode 100644
index 00000000..9e1b78a2
--- /dev/null
+++ b/src/VBox/Storage/testcase/VDScriptInternal.h
@@ -0,0 +1,107 @@
+/** @file
+ *
+ * VBox HDD container test utility - scripting engine, internal script structures.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#ifndef _VDScriptInternal_h__
+#define _VDScriptInternal_h__
+
+#include <iprt/list.h>
+#include <iprt/string.h>
+
+#include "VDScript.h"
+
+/**
+ * Script function which can be called.
+ */
+typedef struct VDSCRIPTFN
+{
+ /** String space core. */
+ RTSTRSPACECORE Core;
+ /** Flag whether function is defined in the source or was
+ * registered from the outside. */
+ bool fExternal;
+ /** Flag dependent data. */
+ union
+ {
+ /** Data for functions defined in the source. */
+ struct
+ {
+ /** Pointer to the AST defining the function. */
+ PVDSCRIPTASTFN pAstFn;
+ } Internal;
+ /** Data for external defined functions. */
+ struct
+ {
+ /** Callback function. */
+ PFNVDSCRIPTCALLBACK pfnCallback;
+ /** Opaque user data. */
+ void *pvUser;
+ } External;
+ } Type;
+ /** Return type of the function. */
+ VDSCRIPTTYPE enmTypeRetn;
+ /** Number of arguments the function takes. */
+ unsigned cArgs;
+ /** Variable sized array of argument types. */
+ VDSCRIPTTYPE aenmArgTypes[1];
+} VDSCRIPTFN;
+/** Pointer to a script function registration structure. */
+typedef VDSCRIPTFN *PVDSCRIPTFN;
+
+/** Pointer to a tokenize state. */
+typedef struct VDTOKENIZER *PVDTOKENIZER;
+
+/**
+ * Script context.
+ */
+typedef struct VDSCRIPTCTXINT
+{
+ /** String space of external registered and source defined functions. */
+ RTSTRSPACE hStrSpaceFn;
+ /** List of ASTs for functions - VDSCRIPTASTFN. */
+ RTLISTANCHOR ListAst;
+ /** Pointer to the current tokenizer state. */
+ PVDTOKENIZER pTokenizer;
+} VDSCRIPTCTXINT;
+/** Pointer to a script context. */
+typedef VDSCRIPTCTXINT *PVDSCRIPTCTXINT;
+
+/**
+ * Check the context for type correctness.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ */
+DECLHIDDEN(int) vdScriptCtxCheck(PVDSCRIPTCTXINT pThis);
+
+/**
+ * Interprete a given function AST. The executed functions
+ * must be type correct, otherwise the behavior is undefined
+ * (Will assert in debug builds).
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param pszFn The function name to interprete.
+ * @param paArgs Arguments to pass to the function.
+ * @param cArgs Number of arguments.
+ * @param pRet Where to store the return value on success.
+ *
+ * @note: The AST is not modified in any way during the interpretation process.
+ */
+DECLHIDDEN(int) vdScriptCtxInterprete(PVDSCRIPTCTXINT pThis, const char *pszFn,
+ PVDSCRIPTARG paArgs, unsigned cArgs,
+ PVDSCRIPTARG pRet);
+
+#endif /* _VDScriptInternal_h__ */
diff --git a/src/VBox/Storage/testcase/VDScriptInterp.cpp b/src/VBox/Storage/testcase/VDScriptInterp.cpp
new file mode 100644
index 00000000..27f2bd12
--- /dev/null
+++ b/src/VBox/Storage/testcase/VDScriptInterp.cpp
@@ -0,0 +1,1054 @@
+/** $Id: VDScriptInterp.cpp $ */
+/** @file
+ *
+ * VBox HDD container test utility - scripting engine, interpreter.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#define LOGGROUP LOGGROUP_DEFAULT
+#include <iprt/list.h>
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+
+#include <VBox/log.h>
+
+#include "VDScriptAst.h"
+#include "VDScriptStack.h"
+#include "VDScriptInternal.h"
+
+/**
+ * Interpreter variable.
+ */
+typedef struct VDSCRIPTINTERPVAR
+{
+ /** String space core. */
+ RTSTRSPACECORE Core;
+ /** Value. */
+ VDSCRIPTARG Value;
+} VDSCRIPTINTERPVAR;
+/** Pointer to an interpreter variable. */
+typedef VDSCRIPTINTERPVAR *PVDSCRIPTINTERPVAR;
+
+/**
+ * Block scope.
+ */
+typedef struct VDSCRIPTINTERPSCOPE
+{
+ /** Pointer to the enclosing scope if available. */
+ struct VDSCRIPTINTERPSCOPE *pParent;
+ /** String space of declared variables in this scope. */
+ RTSTRSPACE hStrSpaceVar;
+} VDSCRIPTINTERPSCOPE;
+/** Pointer to a scope block. */
+typedef VDSCRIPTINTERPSCOPE *PVDSCRIPTINTERPSCOPE;
+
+/**
+ * Function call.
+ */
+typedef struct VDSCRIPTINTERPFNCALL
+{
+ /** Pointer to the caller of this function. */
+ struct VDSCRIPTINTERPFNCALL *pCaller;
+ /** Root scope of this function. */
+ VDSCRIPTINTERPSCOPE ScopeRoot;
+ /** Current scope in this function. */
+ PVDSCRIPTINTERPSCOPE pScopeCurr;
+} VDSCRIPTINTERPFNCALL;
+/** Pointer to a function call. */
+typedef VDSCRIPTINTERPFNCALL *PVDSCRIPTINTERPFNCALL;
+
+/**
+ * Interpreter context.
+ */
+typedef struct VDSCRIPTINTERPCTX
+{
+ /** Pointer to the script context. */
+ PVDSCRIPTCTXINT pScriptCtx;
+ /** Current function call entry. */
+ PVDSCRIPTINTERPFNCALL pFnCallCurr;
+ /** Stack of calculated values. */
+ VDSCRIPTSTACK StackValues;
+ /** Evaluation control stack. */
+ VDSCRIPTSTACK StackCtrl;
+} VDSCRIPTINTERPCTX;
+/** Pointer to an interpreter context. */
+typedef VDSCRIPTINTERPCTX *PVDSCRIPTINTERPCTX;
+
+/**
+ * Interpreter control type.
+ */
+typedef enum VDSCRIPTINTERPCTRLTYPE
+{
+ VDSCRIPTINTERPCTRLTYPE_INVALID = 0,
+ /** Function call to evaluate now, all values are computed
+ * and are stored on the value stack.
+ */
+ VDSCRIPTINTERPCTRLTYPE_FN_CALL,
+ /** Cleanup the function call, deleting the scope and restoring the previous one. */
+ VDSCRIPTINTERPCTRLTYPE_FN_CALL_CLEANUP,
+ /** If statement to evaluate now, the guard is on the stack. */
+ VDSCRIPTINTERPCTRLTYPE_IF,
+ /** While or for statement. */
+ VDSCRIPTINTERPCTRLTYPE_LOOP,
+ /** switch statement. */
+ VDSCRIPTINTERPCTRLTYPE_SWITCH,
+ /** Compound statement. */
+ VDSCRIPTINTERPCTRLTYPE_COMPOUND,
+ /** 32bit blowup. */
+ VDSCRIPTINTERPCTRLTYPE_32BIT_HACK = 0x7fffffff
+} VDSCRIPTINTERPCTRLTYPE;
+/** Pointer to a control type. */
+typedef VDSCRIPTINTERPCTRLTYPE *PVDSCRIPTINTERPCTRLTYPE;
+
+/**
+ * Interpreter stack control entry.
+ */
+typedef struct VDSCRIPTINTERPCTRL
+{
+ /** Flag whether this entry points to an AST node to evaluate. */
+ bool fEvalAst;
+ /** Flag dependent data. */
+ union
+ {
+ /** Pointer to the AST node to interprete. */
+ PVDSCRIPTASTCORE pAstNode;
+ /** Interpreter control structure. */
+ struct
+ {
+ /** Type of control. */
+ VDSCRIPTINTERPCTRLTYPE enmCtrlType;
+ /** Function call data. */
+ struct
+ {
+ /** Function to call. */
+ PVDSCRIPTFN pFn;
+ } FnCall;
+ /** Compound statement. */
+ struct
+ {
+ /** The compound statement node. */
+ PVDSCRIPTASTSTMT pStmtCompound;
+ /** Current statement evaluated. */
+ PVDSCRIPTASTSTMT pStmtCurr;
+ } Compound;
+ /** Pointer to an AST node. */
+ PVDSCRIPTASTCORE pAstNode;
+ } Ctrl;
+ };
+} VDSCRIPTINTERPCTRL;
+/** Pointer to an exec stack control entry. */
+typedef VDSCRIPTINTERPCTRL *PVDSCRIPTINTERPCTRL;
+
+/**
+ * Record an error while interpreting.
+ *
+ * @returns VBox status code passed.
+ * @param pThis The interpreter context.
+ * @param rc The status code to record.
+ * @param RT_SRC_POS Position in the source code.
+ * @param pszFmt Format string.
+ */
+static int vdScriptInterpreterError(PVDSCRIPTINTERPCTX pThis, int rc, RT_SRC_POS_DECL, const char *pszFmt, ...)
+{
+ RTPrintf(pszFmt);
+ return rc;
+}
+
+/**
+ * Pops the topmost value from the value stack.
+ *
+ * @returns nothing.
+ * @param pThis The interpreter context.
+ * @param pVal Where to store the value.
+ */
+DECLINLINE(void) vdScriptInterpreterPopValue(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTARG pVal)
+{
+ PVDSCRIPTARG pValStack = (PVDSCRIPTARG)vdScriptStackGetUsed(&pThis->StackValues);
+ if (!pValStack)
+ {
+ RT_ZERO(*pVal);
+ AssertPtrReturnVoid(pValStack);
+ }
+
+ *pVal = *pValStack;
+ vdScriptStackPop(&pThis->StackValues);
+}
+
+/**
+ * Pushes a given value onto the value stack.
+ */
+DECLINLINE(int) vdScriptInterpreterPushValue(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTARG pVal)
+{
+ PVDSCRIPTARG pValStack = (PVDSCRIPTARG)vdScriptStackGetUnused(&pThis->StackValues);
+ if (!pValStack)
+ return vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory pushing a value on the value stack");
+
+ *pValStack = *pVal;
+ vdScriptStackPush(&pThis->StackValues);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Pushes an AST node onto the control stack.
+ *
+ * @returns VBox status code.
+ * @param pThis The interpreter context.
+ * @param enmCtrlType The control entry type.
+ */
+DECLINLINE(int) vdScriptInterpreterPushAstEntry(PVDSCRIPTINTERPCTX pThis,
+ PVDSCRIPTASTCORE pAstNode)
+{
+ PVDSCRIPTINTERPCTRL pCtrl = NULL;
+
+ pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
+
+ if (pCtrl)
+ {
+ pCtrl->fEvalAst = true;
+ pCtrl->pAstNode = pAstNode;
+ vdScriptStackPush(&pThis->StackCtrl);
+ return VINF_SUCCESS;
+ }
+
+ return vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
+}
+
+/**
+ * Pushes a control entry without additional data onto the stack.
+ *
+ * @returns VBox status code.
+ * @param pThis The interpreter context.
+ * @param enmCtrlType The control entry type.
+ */
+DECLINLINE(int) vdScriptInterpreterPushNonDataCtrlEntry(PVDSCRIPTINTERPCTX pThis,
+ VDSCRIPTINTERPCTRLTYPE enmCtrlType)
+{
+ PVDSCRIPTINTERPCTRL pCtrl = NULL;
+
+ pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
+ if (pCtrl)
+ {
+ pCtrl->fEvalAst = false;
+ pCtrl->Ctrl.enmCtrlType = enmCtrlType;
+ vdScriptStackPush(&pThis->StackCtrl);
+ return VINF_SUCCESS;
+ }
+
+ return vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
+}
+
+/**
+ * Pushes a compound statement control entry onto the stack.
+ *
+ * @returns VBox status code.
+ * @param pThis The interpreter context.
+ * @param pStmtFirst The first statement of the compound statement
+ */
+DECLINLINE(int) vdScriptInterpreterPushCompoundCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt)
+{
+ PVDSCRIPTINTERPCTRL pCtrl = NULL;
+
+ pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
+ if (pCtrl)
+ {
+ pCtrl->fEvalAst = false;
+ pCtrl->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_COMPOUND;
+ pCtrl->Ctrl.Compound.pStmtCompound = pStmt;
+ pCtrl->Ctrl.Compound.pStmtCurr = RTListGetFirst(&pStmt->Compound.ListStmts, VDSCRIPTASTSTMT, Core.ListNode);
+ vdScriptStackPush(&pThis->StackCtrl);
+ return VINF_SUCCESS;
+ }
+
+ return vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
+}
+
+/**
+ * Pushes a while statement control entry onto the stack.
+ *
+ * @returns VBox status code.
+ * @param pThis The interpreter context.
+ * @param pStmt The while statement.
+ */
+DECLINLINE(int) vdScriptInterpreterPushWhileCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTINTERPCTRL pCtrl = NULL;
+
+ pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
+ if (pCtrl)
+ {
+ pCtrl->fEvalAst = false;
+ pCtrl->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_LOOP;
+ pCtrl->Ctrl.pAstNode = &pStmt->Core;
+ vdScriptStackPush(&pThis->StackCtrl);
+
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->While.pCond->Core);
+ if ( RT_SUCCESS(rc)
+ && pStmt->While.fDoWhile)
+ {
+ /* Push the statement to execute for do ... while loops because they run at least once. */
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->While.pStmt->Core);
+ if (RT_FAILURE(rc))
+ {
+ /* Cleanup while control statement and AST node. */
+ vdScriptStackPop(&pThis->StackCtrl);
+ vdScriptStackPop(&pThis->StackCtrl);
+ }
+ }
+ else if (RT_FAILURE(rc))
+ vdScriptStackPop(&pThis->StackCtrl); /* Cleanup the while control statement. */
+ }
+ else
+ rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
+
+ return rc;
+}
+
+/**
+ * Pushes an if statement control entry onto the stack.
+ *
+ * @returns VBox status code.
+ * @param pThis The interpreter context.
+ * @param pStmt The if statement.
+ */
+static int vdScriptInterpreterPushIfCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTINTERPCTRL pCtrl = NULL;
+
+ pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
+ if (pCtrl)
+ {
+ pCtrl->fEvalAst = false;
+ pCtrl->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_IF;
+ pCtrl->Ctrl.pAstNode = &pStmt->Core;
+ vdScriptStackPush(&pThis->StackCtrl);
+
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->If.pCond->Core);
+ }
+ else
+ rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
+
+ return rc;
+}
+
+/**
+ * Pushes a for statement control entry onto the stack.
+ *
+ * @returns VBox status code.
+ * @param pThis The interpreter context.
+ * @param pStmt The while statement.
+ */
+DECLINLINE(int) vdScriptInterpreterPushForCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTINTERPCTRL pCtrl = NULL;
+
+ pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
+ if (pCtrl)
+ {
+ pCtrl->fEvalAst = false;
+ pCtrl->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_LOOP;
+ pCtrl->Ctrl.pAstNode = &pStmt->Core;
+ vdScriptStackPush(&pThis->StackCtrl);
+
+ /* Push the conditional first and the the initializer .*/
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->For.pExprCond->Core);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->For.pExprStart->Core);
+ if (RT_FAILURE(rc))
+ vdScriptStackPop(&pThis->StackCtrl);
+ }
+ }
+ else
+ rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
+
+ return rc;
+}
+
+/**
+ * Destroy variable string space callback.
+ */
+static DECLCALLBACK(int) vdScriptInterpreterVarSpaceDestroy(PRTSTRSPACECORE pStr, void *pvUser)
+{
+ RTMemFree(pStr);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Setsup a new scope in the current function call.
+ *
+ * @returns VBox status code.
+ * @param pThis The interpreter context.
+ */
+static int vdScriptInterpreterScopeCreate(PVDSCRIPTINTERPCTX pThis)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTINTERPSCOPE pScope = (PVDSCRIPTINTERPSCOPE)RTMemAllocZ(sizeof(VDSCRIPTINTERPSCOPE));
+ if (pScope)
+ {
+ pScope->pParent = pThis->pFnCallCurr->pScopeCurr;
+ pScope->hStrSpaceVar = NULL;
+ pThis->pFnCallCurr->pScopeCurr = pScope;
+ }
+ else
+ rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory allocating new scope");
+
+ return rc;
+}
+
+/**
+ * Destroys the current scope.
+ *
+ * @returns nothing.
+ * @param pThis The interpreter context.
+ */
+static void vdScriptInterpreterScopeDestroyCurr(PVDSCRIPTINTERPCTX pThis)
+{
+ AssertMsgReturnVoid(pThis->pFnCallCurr->pScopeCurr != &pThis->pFnCallCurr->ScopeRoot,
+ ("Current scope is root scope of function call\n"));
+
+ PVDSCRIPTINTERPSCOPE pScope = pThis->pFnCallCurr->pScopeCurr;
+ pThis->pFnCallCurr->pScopeCurr = pScope->pParent;
+ RTStrSpaceDestroy(&pScope->hStrSpaceVar, vdScriptInterpreterVarSpaceDestroy, NULL);
+ RTMemFree(pScope);
+}
+
+/**
+ * Get the content of the given variable identifier from the current or parent scope.
+ */
+static PVDSCRIPTINTERPVAR vdScriptInterpreterGetVar(PVDSCRIPTINTERPCTX pThis, const char *pszVar)
+{
+ PVDSCRIPTINTERPSCOPE pScopeCurr = pThis->pFnCallCurr->pScopeCurr;
+ PVDSCRIPTINTERPVAR pVar = NULL;
+
+ while ( !pVar
+ && pScopeCurr)
+ {
+ pVar = (PVDSCRIPTINTERPVAR)RTStrSpaceGet(&pScopeCurr->hStrSpaceVar, pszVar);
+ if (pVar)
+ break;
+ pScopeCurr = pScopeCurr->pParent;
+ }
+
+
+ return pVar;
+}
+
+/**
+ * Evaluate an expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The interpreter context.
+ * @param pExpr The expression to evaluate.
+ */
+static int vdScriptInterpreterEvaluateExpression(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTEXPR pExpr)
+{
+ int rc = VINF_SUCCESS;
+
+ switch (pExpr->enmType)
+ {
+ case VDSCRIPTEXPRTYPE_PRIMARY_NUMCONST:
+ {
+ /* Push the numerical constant on the value stack. */
+ VDSCRIPTARG NumConst;
+ NumConst.enmType = VDSCRIPTTYPE_UINT64;
+ NumConst.u64 = pExpr->u64;
+ rc = vdScriptInterpreterPushValue(pThis, &NumConst);
+ break;
+ }
+ case VDSCRIPTEXPRTYPE_PRIMARY_STRINGCONST:
+ {
+ /* Push the string literal on the value stack. */
+ VDSCRIPTARG StringConst;
+ StringConst.enmType = VDSCRIPTTYPE_STRING;
+ StringConst.psz = pExpr->pszStr;
+ rc = vdScriptInterpreterPushValue(pThis, &StringConst);
+ break;
+ }
+ case VDSCRIPTEXPRTYPE_PRIMARY_BOOLEAN:
+ {
+ VDSCRIPTARG BoolConst;
+ BoolConst.enmType = VDSCRIPTTYPE_BOOL;
+ BoolConst.f = pExpr->f;
+ rc = vdScriptInterpreterPushValue(pThis, &BoolConst);
+ break;
+ }
+ case VDSCRIPTEXPRTYPE_PRIMARY_IDENTIFIER:
+ {
+ /* Look it up and push the value onto the value stack. */
+ PVDSCRIPTINTERPVAR pVar = vdScriptInterpreterGetVar(pThis, pExpr->pIde->aszIde);
+
+ AssertPtrReturn(pVar, VERR_IPE_UNINITIALIZED_STATUS);
+ rc = vdScriptInterpreterPushValue(pThis, &pVar->Value);
+ break;
+ }
+ case VDSCRIPTEXPRTYPE_POSTFIX_INCREMENT:
+ case VDSCRIPTEXPRTYPE_POSTFIX_DECREMENT:
+ AssertMsgFailed(("TODO\n"));
+ case VDSCRIPTEXPRTYPE_POSTFIX_FNCALL:
+ {
+ PVDSCRIPTFN pFn = (PVDSCRIPTFN)RTStrSpaceGet(&pThis->pScriptCtx->hStrSpaceFn, pExpr->FnCall.pFnIde->pIde->aszIde);
+ if (pFn)
+ {
+ /* Push a function call control entry on the stack. */
+ PVDSCRIPTINTERPCTRL pCtrlFn = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
+ if (pCtrlFn)
+ {
+ pCtrlFn->fEvalAst = false;
+ pCtrlFn->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_FN_CALL;
+ pCtrlFn->Ctrl.FnCall.pFn = pFn;
+ vdScriptStackPush(&pThis->StackCtrl);
+
+ /* Push parameter expressions on the stack. */
+ PVDSCRIPTASTEXPR pArg = RTListGetFirst(&pExpr->FnCall.ListArgs, VDSCRIPTASTEXPR, Core.ListNode);
+ while (pArg)
+ {
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pArg->Core);
+ if (RT_FAILURE(rc))
+ break;
+ pArg = RTListGetNext(&pExpr->FnCall.ListArgs, pArg, VDSCRIPTASTEXPR, Core.ListNode);
+ }
+ }
+ }
+ else
+ AssertMsgFailed(("Invalid program given, unknown function: %s\n", pExpr->FnCall.pFnIde->pIde->aszIde));
+ break;
+ }
+ case VDSCRIPTEXPRTYPE_UNARY_INCREMENT:
+ case VDSCRIPTEXPRTYPE_UNARY_DECREMENT:
+ case VDSCRIPTEXPRTYPE_UNARY_POSSIGN:
+ case VDSCRIPTEXPRTYPE_UNARY_NEGSIGN:
+ case VDSCRIPTEXPRTYPE_UNARY_INVERT:
+ case VDSCRIPTEXPRTYPE_UNARY_NEGATE:
+ case VDSCRIPTEXPRTYPE_MULTIPLICATION:
+ case VDSCRIPTEXPRTYPE_DIVISION:
+ case VDSCRIPTEXPRTYPE_MODULUS:
+ case VDSCRIPTEXPRTYPE_ADDITION:
+ case VDSCRIPTEXPRTYPE_SUBTRACTION:
+ case VDSCRIPTEXPRTYPE_LSR:
+ case VDSCRIPTEXPRTYPE_LSL:
+ case VDSCRIPTEXPRTYPE_LOWER:
+ case VDSCRIPTEXPRTYPE_HIGHER:
+ case VDSCRIPTEXPRTYPE_LOWEREQUAL:
+ case VDSCRIPTEXPRTYPE_HIGHEREQUAL:
+ case VDSCRIPTEXPRTYPE_EQUAL:
+ case VDSCRIPTEXPRTYPE_NOTEQUAL:
+ case VDSCRIPTEXPRTYPE_BITWISE_AND:
+ case VDSCRIPTEXPRTYPE_BITWISE_XOR:
+ case VDSCRIPTEXPRTYPE_BITWISE_OR:
+ case VDSCRIPTEXPRTYPE_LOGICAL_AND:
+ case VDSCRIPTEXPRTYPE_LOGICAL_OR:
+ case VDSCRIPTEXPRTYPE_ASSIGN:
+ case VDSCRIPTEXPRTYPE_ASSIGN_MULT:
+ case VDSCRIPTEXPRTYPE_ASSIGN_DIV:
+ case VDSCRIPTEXPRTYPE_ASSIGN_MOD:
+ case VDSCRIPTEXPRTYPE_ASSIGN_ADD:
+ case VDSCRIPTEXPRTYPE_ASSIGN_SUB:
+ case VDSCRIPTEXPRTYPE_ASSIGN_LSL:
+ case VDSCRIPTEXPRTYPE_ASSIGN_LSR:
+ case VDSCRIPTEXPRTYPE_ASSIGN_AND:
+ case VDSCRIPTEXPRTYPE_ASSIGN_XOR:
+ case VDSCRIPTEXPRTYPE_ASSIGN_OR:
+ case VDSCRIPTEXPRTYPE_ASSIGNMENT_LIST:
+ AssertMsgFailed(("TODO\n"));
+ default:
+ AssertMsgFailed(("Invalid expression type: %d\n", pExpr->enmType));
+ }
+ return rc;
+}
+
+/**
+ * Evaluate a statement.
+ *
+ * @returns VBox status code.
+ * @param pThis The interpreter context.
+ * @param pStmt The statement to evaluate.
+ */
+static int vdScriptInterpreterEvaluateStatement(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt)
+{
+ int rc = VINF_SUCCESS;
+
+ switch (pStmt->enmStmtType)
+ {
+ case VDSCRIPTSTMTTYPE_COMPOUND:
+ {
+ /* Setup new scope. */
+ rc = vdScriptInterpreterScopeCreate(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo: Declarations */
+ rc = vdScriptInterpreterPushCompoundCtrlEntry(pThis, pStmt);
+ }
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_EXPRESSION:
+ {
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->pExpr->Core);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_IF:
+ {
+ rc = vdScriptInterpreterPushIfCtrlEntry(pThis, pStmt);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_SWITCH:
+ AssertMsgFailed(("TODO\n"));
+ break;
+ case VDSCRIPTSTMTTYPE_WHILE:
+ {
+ rc = vdScriptInterpreterPushWhileCtrlEntry(pThis, pStmt);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_RETURN:
+ {
+ /* Walk up the control stack until we reach a function cleanup entry. */
+ PVDSCRIPTINTERPCTRL pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
+ while ( pCtrl
+ && ( pCtrl->fEvalAst
+ || pCtrl->Ctrl.enmCtrlType != VDSCRIPTINTERPCTRLTYPE_FN_CALL_CLEANUP))
+ {
+ /* Cleanup up any compound statement scope. */
+ if ( !pCtrl->fEvalAst
+ && pCtrl->Ctrl.enmCtrlType == VDSCRIPTINTERPCTRLTYPE_COMPOUND)
+ vdScriptInterpreterScopeDestroyCurr(pThis);
+
+ vdScriptStackPop(&pThis->StackCtrl);
+ pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
+ }
+ AssertMsg(VALID_PTR(pCtrl), ("Incorrect program, return outside of function\n"));
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_FOR:
+ {
+ rc = vdScriptInterpreterPushForCtrlEntry(pThis, pStmt);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_CONTINUE:
+ {
+ /* Remove everything up to a loop control entry. */
+ PVDSCRIPTINTERPCTRL pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
+ while ( pCtrl
+ && ( pCtrl->fEvalAst
+ || pCtrl->Ctrl.enmCtrlType != VDSCRIPTINTERPCTRLTYPE_LOOP))
+ {
+ /* Cleanup up any compound statement scope. */
+ if ( !pCtrl->fEvalAst
+ && pCtrl->Ctrl.enmCtrlType == VDSCRIPTINTERPCTRLTYPE_COMPOUND)
+ vdScriptInterpreterScopeDestroyCurr(pThis);
+
+ vdScriptStackPop(&pThis->StackCtrl);
+ pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
+ }
+ AssertMsg(VALID_PTR(pCtrl), ("Incorrect program, continue outside of loop\n"));
+
+ /* Put the conditionals for while and for loops onto the control stack again. */
+ PVDSCRIPTASTSTMT pLoopStmt = (PVDSCRIPTASTSTMT)pCtrl->Ctrl.pAstNode;
+
+ AssertMsg( pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_WHILE
+ || pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_FOR,
+ ("Invalid statement type, must be for or while loop\n"));
+
+ if (pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_FOR)
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->For.pExprCond->Core);
+ else if (!pLoopStmt->While.fDoWhile)
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->While.pCond->Core);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_BREAK:
+ {
+ /* Remove everything including the loop control statement. */
+ PVDSCRIPTINTERPCTRL pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
+ while ( pCtrl
+ && ( pCtrl->fEvalAst
+ || pCtrl->Ctrl.enmCtrlType != VDSCRIPTINTERPCTRLTYPE_LOOP))
+ {
+ /* Cleanup up any compound statement scope. */
+ if ( !pCtrl->fEvalAst
+ && pCtrl->Ctrl.enmCtrlType == VDSCRIPTINTERPCTRLTYPE_COMPOUND)
+ vdScriptInterpreterScopeDestroyCurr(pThis);
+
+ vdScriptStackPop(&pThis->StackCtrl);
+ pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
+ }
+ AssertMsg(VALID_PTR(pCtrl), ("Incorrect program, break outside of loop\n"));
+ vdScriptStackPop(&pThis->StackCtrl); /* Remove loop control statement. */
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_CASE:
+ case VDSCRIPTSTMTTYPE_DEFAULT:
+ AssertMsgFailed(("TODO\n"));
+ break;
+ default:
+ AssertMsgFailed(("Invalid statement type: %d\n", pStmt->enmStmtType));
+ }
+
+ return rc;
+}
+
+/**
+ * Evaluates the given AST node.
+ *
+ * @returns VBox statuse code.
+ * @param pThis The interpreter context.
+ * @param pAstNode The AST node to interpret.
+ */
+static int vdScriptInterpreterEvaluateAst(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTCORE pAstNode)
+{
+ int rc = VERR_NOT_IMPLEMENTED;
+
+ switch (pAstNode->enmClass)
+ {
+ case VDSCRIPTASTCLASS_DECLARATION:
+ {
+ AssertMsgFailed(("TODO\n"));
+ break;
+ }
+ case VDSCRIPTASTCLASS_STATEMENT:
+ {
+ rc = vdScriptInterpreterEvaluateStatement(pThis, (PVDSCRIPTASTSTMT)pAstNode);
+ break;
+ }
+ case VDSCRIPTASTCLASS_EXPRESSION:
+ {
+ rc = vdScriptInterpreterEvaluateExpression(pThis, (PVDSCRIPTASTEXPR)pAstNode);
+ break;
+ }
+ /* These should never ever appear here. */
+ case VDSCRIPTASTCLASS_IDENTIFIER:
+ case VDSCRIPTASTCLASS_FUNCTION:
+ case VDSCRIPTASTCLASS_FUNCTIONARG:
+ case VDSCRIPTASTCLASS_INVALID:
+ default:
+ AssertMsgFailed(("Invalid AST node class: %d\n", pAstNode->enmClass));
+ }
+
+ return rc;
+}
+
+/**
+ * Evaluate a function call.
+ *
+ * @returns VBox status code.
+ * @param pThis The interpreter context.
+ * @param pFn The function execute.
+ */
+static int vdScriptInterpreterFnCall(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTFN pFn)
+{
+ int rc = VINF_SUCCESS;
+
+ if (!pFn->fExternal)
+ {
+ PVDSCRIPTASTFN pAstFn = pFn->Type.Internal.pAstFn;
+
+ /* Add function call cleanup marker on the stack first. */
+ rc = vdScriptInterpreterPushNonDataCtrlEntry(pThis, VDSCRIPTINTERPCTRLTYPE_FN_CALL_CLEANUP);
+ if (RT_SUCCESS(rc))
+ {
+ /* Create function call frame and set it up. */
+ PVDSCRIPTINTERPFNCALL pFnCall = (PVDSCRIPTINTERPFNCALL)RTMemAllocZ(sizeof(VDSCRIPTINTERPFNCALL));
+ if (pFnCall)
+ {
+ pFnCall->pCaller = pThis->pFnCallCurr;
+ pFnCall->ScopeRoot.pParent = NULL;
+ pFnCall->ScopeRoot.hStrSpaceVar = NULL;
+ pFnCall->pScopeCurr = &pFnCall->ScopeRoot;
+
+ /* Add the variables, remember order. The first variable in the argument has the value at the top of the value stack. */
+ PVDSCRIPTASTFNARG pArg = RTListGetFirst(&pAstFn->ListArgs, VDSCRIPTASTFNARG, Core.ListNode);
+ for (unsigned i = 0; i < pAstFn->cArgs; i++)
+ {
+ PVDSCRIPTINTERPVAR pVar = (PVDSCRIPTINTERPVAR)RTMemAllocZ(sizeof(VDSCRIPTINTERPVAR));
+ if (pVar)
+ {
+ pVar->Core.pszString = pArg->pArgIde->aszIde;
+ pVar->Core.cchString = pArg->pArgIde->cchIde;
+ vdScriptInterpreterPopValue(pThis, &pVar->Value);
+ bool fInserted = RTStrSpaceInsert(&pFnCall->ScopeRoot.hStrSpaceVar, &pVar->Core);
+ Assert(fInserted);
+ }
+ else
+ {
+ rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory creating a variable");
+ break;
+ }
+ pArg = RTListGetNext(&pAstFn->ListArgs, pArg, VDSCRIPTASTFNARG, Core.ListNode);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Push compount statement on the control stack and make the newly created
+ * call frame the current one.
+ */
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pAstFn->pCompoundStmts->Core);
+ if (RT_SUCCESS(rc))
+ pThis->pFnCallCurr = pFnCall;
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ RTStrSpaceDestroy(&pFnCall->ScopeRoot.hStrSpaceVar, vdScriptInterpreterVarSpaceDestroy, NULL);
+ RTMemFree(pFnCall);
+ }
+ }
+ else
+ rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory creating a call frame");
+ }
+ }
+ else
+ {
+ /* External function call, build the argument list. */
+ if (pFn->cArgs)
+ {
+ PVDSCRIPTARG paArgs = (PVDSCRIPTARG)RTMemAllocZ(pFn->cArgs * sizeof(VDSCRIPTARG));
+ if (paArgs)
+ {
+ for (unsigned i = 0; i < pFn->cArgs; i++)
+ vdScriptInterpreterPopValue(pThis, &paArgs[i]);
+
+ rc = pFn->Type.External.pfnCallback(paArgs, pFn->Type.External.pvUser);
+ RTMemFree(paArgs);
+ }
+ else
+ rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS,
+ "Out of memory creating argument array for external function call");
+ }
+ else
+ rc = pFn->Type.External.pfnCallback(NULL, pFn->Type.External.pvUser);
+ }
+
+ return rc;
+}
+
+/**
+ * Evaluate interpreter control statement.
+ *
+ * @returns VBox status code.
+ * @param pThis The interpreter context.
+ * @param pCtrl The control entry to evaluate.
+ */
+static int vdScriptInterpreterEvaluateCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTINTERPCTRL pCtrl)
+{
+ int rc = VINF_SUCCESS;
+
+ Assert(!pCtrl->fEvalAst);
+ switch (pCtrl->Ctrl.enmCtrlType)
+ {
+ case VDSCRIPTINTERPCTRLTYPE_FN_CALL:
+ {
+ PVDSCRIPTFN pFn = pCtrl->Ctrl.FnCall.pFn;
+
+ vdScriptStackPop(&pThis->StackCtrl);
+ rc = vdScriptInterpreterFnCall(pThis, pFn);
+ break;
+ }
+ case VDSCRIPTINTERPCTRLTYPE_FN_CALL_CLEANUP:
+ {
+ vdScriptStackPop(&pThis->StackCtrl);
+
+ /* Delete function call entry. */
+ AssertPtr(pThis->pFnCallCurr);
+ PVDSCRIPTINTERPFNCALL pFnCallFree = pThis->pFnCallCurr;
+
+ pThis->pFnCallCurr = pFnCallFree->pCaller;
+ Assert(pFnCallFree->pScopeCurr == &pFnCallFree->ScopeRoot);
+ RTStrSpaceDestroy(&pFnCallFree->ScopeRoot.hStrSpaceVar, vdScriptInterpreterVarSpaceDestroy, NULL);
+ RTMemFree(pFnCallFree);
+ break;
+ }
+ case VDSCRIPTINTERPCTRLTYPE_COMPOUND:
+ {
+ if (!pCtrl->Ctrl.Compound.pStmtCurr)
+ {
+ /* Evaluated last statement, cleanup and remove the control statement from the stack. */
+ vdScriptInterpreterScopeDestroyCurr(pThis);
+ vdScriptStackPop(&pThis->StackCtrl);
+ }
+ else
+ {
+ /* Push the current statement onto the control stack and move on. */
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pCtrl->Ctrl.Compound.pStmtCurr->Core);
+ if (RT_SUCCESS(rc))
+ {
+ pCtrl->Ctrl.Compound.pStmtCurr = RTListGetNext(&pCtrl->Ctrl.Compound.pStmtCompound->Compound.ListStmts,
+ pCtrl->Ctrl.Compound.pStmtCurr, VDSCRIPTASTSTMT, Core.ListNode);
+ }
+ }
+ break;
+ }
+ case VDSCRIPTINTERPCTRLTYPE_LOOP:
+ {
+ PVDSCRIPTASTSTMT pLoopStmt = (PVDSCRIPTASTSTMT)pCtrl->Ctrl.pAstNode;
+
+ /* Check whether the condition passed. */
+ VDSCRIPTARG Cond;
+ vdScriptInterpreterPopValue(pThis, &Cond);
+ AssertMsg(Cond.enmType == VDSCRIPTTYPE_BOOL,
+ ("Value on stack is not of boolean type\n"));
+
+ if (Cond.f)
+ {
+ /* Execute the loop another round. */
+ if (pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_WHILE)
+ {
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->While.pCond->Core);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->While.pStmt->Core);
+ if (RT_FAILURE(rc))
+ vdScriptStackPop(&pThis->StackCtrl);
+ }
+ }
+ else
+ {
+ AssertMsg(pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_FOR,
+ ("Not a for statement\n"));
+
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->For.pExprCond->Core);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->For.pExpr3->Core);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->For.pStmt->Core);
+ if (RT_FAILURE(rc))
+ vdScriptStackPop(&pThis->StackCtrl);
+ }
+
+ if (RT_FAILURE(rc))
+ vdScriptStackPop(&pThis->StackCtrl);
+ }
+ }
+ }
+ else
+ vdScriptStackPop(&pThis->StackCtrl); /* Remove loop control statement. */
+ break;
+ }
+ case VDSCRIPTINTERPCTRLTYPE_IF:
+ {
+ PVDSCRIPTASTSTMT pIfStmt = (PVDSCRIPTASTSTMT)pCtrl->Ctrl.pAstNode;
+
+ vdScriptStackPop(&pThis->StackCtrl); /* Remove if control statement. */
+
+ /* Check whether the condition passed. */
+ VDSCRIPTARG Cond;
+ vdScriptInterpreterPopValue(pThis, &Cond);
+ AssertMsg(Cond.enmType == VDSCRIPTTYPE_BOOL,
+ ("Value on stack is not of boolean type\n"));
+
+ if (Cond.f)
+ {
+ /* Execute the true branch. */
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pIfStmt->If.pTrueStmt->Core);
+ }
+ else if (pIfStmt->If.pElseStmt)
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pIfStmt->If.pElseStmt->Core);
+
+ break;
+ }
+ default:
+ AssertMsgFailed(("Invalid evaluation control type on the stack: %d\n",
+ pCtrl->Ctrl.enmCtrlType));
+ }
+
+ return rc;
+}
+
+/**
+ * The interpreter evaluation core loop.
+ *
+ * @returns VBox status code.
+ * @param pThis The interpreter context.
+ */
+static int vdScriptInterpreterEvaluate(PVDSCRIPTINTERPCTX pThis)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTINTERPCTRL pCtrl = NULL;
+
+ pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
+ while (pCtrl)
+ {
+ if (pCtrl->fEvalAst)
+ {
+ PVDSCRIPTASTCORE pAstNode = pCtrl->pAstNode;
+ vdScriptStackPop(&pThis->StackCtrl);
+
+ rc = vdScriptInterpreterEvaluateAst(pThis, pAstNode);
+ }
+ else
+ rc = vdScriptInterpreterEvaluateCtrlEntry(pThis, pCtrl);
+
+ pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
+ }
+
+ return rc;
+}
+
+DECLHIDDEN(int) vdScriptCtxInterprete(PVDSCRIPTCTXINT pThis, const char *pszFn,
+ PVDSCRIPTARG paArgs, unsigned cArgs,
+ PVDSCRIPTARG pRet)
+{
+ int rc = VINF_SUCCESS;
+ VDSCRIPTINTERPCTX InterpCtx;
+ PVDSCRIPTFN pFn = NULL;
+
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszFn, VERR_INVALID_POINTER);
+ AssertReturn( (!cArgs && !paArgs)
+ || (cArgs && paArgs), VERR_INVALID_PARAMETER);
+
+ InterpCtx.pScriptCtx = pThis;
+ InterpCtx.pFnCallCurr = NULL;
+ vdScriptStackInit(&InterpCtx.StackValues, sizeof(VDSCRIPTARG));
+ vdScriptStackInit(&InterpCtx.StackCtrl, sizeof(VDSCRIPTINTERPCTRL));
+
+ pFn = (PVDSCRIPTFN)RTStrSpaceGet(&pThis->hStrSpaceFn, pszFn);
+ if (pFn)
+ {
+ if (cArgs == pFn->cArgs)
+ {
+ /* Push the arguments onto the stack. */
+ /** @todo: Check expected and given argument types. */
+ for (unsigned i = 0; i < cArgs; i++)
+ {
+ PVDSCRIPTARG pArg = (PVDSCRIPTARG)vdScriptStackGetUnused(&InterpCtx.StackValues);
+ *pArg = paArgs[i];
+ vdScriptStackPush(&InterpCtx.StackValues);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Setup function call frame and parameters. */
+ rc = vdScriptInterpreterFnCall(&InterpCtx, pFn);
+ if (RT_SUCCESS(rc))
+ {
+ /* Run the interpreter. */
+ rc = vdScriptInterpreterEvaluate(&InterpCtx);
+ vdScriptStackDestroy(&InterpCtx.StackValues);
+ vdScriptStackDestroy(&InterpCtx.StackCtrl);
+ }
+ }
+ }
+ else
+ rc = vdScriptInterpreterError(&InterpCtx, VERR_INVALID_PARAMETER, RT_SRC_POS, "Invalid number of parameters, expected %d got %d", pFn->cArgs, cArgs);
+ }
+ else
+ rc = vdScriptInterpreterError(&InterpCtx, VERR_NOT_FOUND, RT_SRC_POS, "Function with identifier \"%s\" not found", pszFn);
+
+
+ return rc;
+}
diff --git a/src/VBox/Storage/testcase/VDScriptStack.h b/src/VBox/Storage/testcase/VDScriptStack.h
new file mode 100644
index 00000000..93eb1c36
--- /dev/null
+++ b/src/VBox/Storage/testcase/VDScriptStack.h
@@ -0,0 +1,142 @@
+/** @file
+ *
+ * VBox HDD container test utility - scripting engine, internal stack implementation.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#ifndef _VDScriptStack_h__
+#define _VDScriptStack_h__
+
+#include <iprt/list.h>
+#include <iprt/string.h>
+
+#include "VDScript.h"
+
+/**
+ * Stack structure.
+ */
+typedef struct VDSCRIPTSTACK
+{
+ /** Size of one stack element. */
+ size_t cbStackEntry;
+ /** Stack memory. */
+ void *pvStack;
+ /** Number of elements on the stack. */
+ unsigned cOnStack;
+ /** Maximum number of elements the stack can hold. */
+ unsigned cOnStackMax;
+} VDSCRIPTSTACK;
+/** Pointer to a stack. */
+typedef VDSCRIPTSTACK *PVDSCRIPTSTACK;
+
+/**
+ * Init the stack structure.
+ *
+ * @returns nothing.
+ * @param pStack The stack to initialize.
+ * @param cbStackEntry The size of one stack entry.
+ */
+DECLINLINE(void) vdScriptStackInit(PVDSCRIPTSTACK pStack, size_t cbStackEntry)
+{
+ pStack->cbStackEntry = cbStackEntry;
+ pStack->pvStack = NULL;
+ pStack->cOnStack = 0;
+ pStack->cOnStackMax = 0;
+}
+
+/**
+ * Destroys the given stack freeing all memory.
+ *
+ * @returns nothing.
+ * @param pStack The stack to destroy.
+ */
+DECLINLINE(void) vdScriptStackDestroy(PVDSCRIPTSTACK pStack)
+{
+ if (pStack->pvStack)
+ RTMemFree(pStack->pvStack);
+ pStack->cbStackEntry = 0;
+ pStack->pvStack = NULL;
+ pStack->cOnStack = 0;
+ pStack->cOnStackMax = 0;
+}
+
+/**
+ * Gets the topmost unused stack entry.
+ *
+ * @returns Pointer to the first unused entry.
+ * NULL if there is no room left and increasing the stack failed.
+ * @param pStack The stack.
+ */
+DECLINLINE(void *)vdScriptStackGetUnused(PVDSCRIPTSTACK pStack)
+{
+ void *pvElem = NULL;
+
+ if (pStack->cOnStack >= pStack->cOnStackMax)
+ {
+ unsigned cOnStackMaxNew = pStack->cOnStackMax + 10;
+ void *pvStackNew = NULL;
+
+ /* Try to increase stack space. */
+ pvStackNew = RTMemRealloc(pStack->pvStack, cOnStackMaxNew * pStack->cbStackEntry);
+ if (pvStackNew)
+ {
+ pStack->pvStack = pvStackNew;
+ pStack->cOnStackMax = cOnStackMaxNew;
+ }
+
+ }
+
+ if (pStack->cOnStack < pStack->cOnStackMax)
+ pvElem = (char *)pStack->pvStack + pStack->cOnStack * pStack->cbStackEntry;
+
+ return pvElem;
+}
+
+/**
+ * Gets the topmost used entry on the stack.
+ *
+ * @returns Pointer to the first used entry
+ * or NULL if the stack is empty.
+ * @param pStack The stack.
+ */
+DECLINLINE(void *)vdScriptStackGetUsed(PVDSCRIPTSTACK pStack)
+{
+ if (!pStack->cOnStack)
+ return NULL;
+ else
+ return (char *)pStack->pvStack + (pStack->cOnStack - 1) * pStack->cbStackEntry;
+}
+
+/**
+ * Increases the used element count for the given stack.
+ *
+ * @returns nothing.
+ * @param pStack The stack.
+ */
+DECLINLINE(void) vdScriptStackPush(PVDSCRIPTSTACK pStack)
+{
+ pStack->cOnStack++;
+}
+
+/**
+ * Decreases the used element count for the given stack.
+ *
+ * @returns nothing.
+ * @param pStack The stack.
+ */
+DECLINLINE(void) vdScriptStackPop(PVDSCRIPTSTACK pStack)
+{
+ pStack->cOnStack--;
+}
+
+#endif /* _VDScriptStack_h__ */
diff --git a/src/VBox/Storage/testcase/tstVD-2.cpp b/src/VBox/Storage/testcase/tstVD-2.cpp
index 13821789..7e81ffc9 100644
--- a/src/VBox/Storage/testcase/tstVD-2.cpp
+++ b/src/VBox/Storage/testcase/tstVD-2.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2007 Oracle Corporation
+ * Copyright (C) 2006-2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
diff --git a/src/VBox/Storage/testcase/tstVD.cpp b/src/VBox/Storage/testcase/tstVD.cpp
index c858e96c..953b5c34 100644
--- a/src/VBox/Storage/testcase/tstVD.cpp
+++ b/src/VBox/Storage/testcase/tstVD.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2010 Oracle Corporation
+ * Copyright (C) 2006-2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
diff --git a/src/VBox/Storage/testcase/tstVDCompact.vd b/src/VBox/Storage/testcase/tstVDCompact.vd
index 9d511baa..95532d23 100644
--- a/src/VBox/Storage/testcase/tstVDCompact.vd
+++ b/src/VBox/Storage/testcase/tstVDCompact.vd
@@ -1,77 +1,62 @@
-# $Id: tstVDCompact.vd $
-#
-# Storage: Testcase for compacting disks.
-#
+/* $Id: tstVDCompact.vd $ */
+/**
+ * Storage: Testcase for compacting disks.
+ */
-#
-# Copyright (C) 2011 Oracle Corporation
-#
-# This file is part of VirtualBox Open Source Edition (OSE), as
-# available from http://www.virtualbox.org. This file is free software;
-# you can redistribute it and/or modify it under the terms of the GNU
-# General Public License (GPL) as published by the Free Software
-# Foundation, in version 2 as it comes in the "COPYING" file of the
-# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
-# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
-#
+/*
+ * Copyright (C) 2011 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
-# Init I/O RNG for generating random data for writes
-iorngcreate size=10M mode=manual seed=1234567890
+void tstCompact(string strMsg, string strBackend)
+{
+ print(strMsg);
-# Create zero pattern
-iopatterncreatefromnumber name=zero size=1M pattern=0
+ /* Create disk containers, read verification is on. */
+ createdisk("disk", true);
+ create("disk", "base", "tstCompact.disk", "dynamic", strBackend, 200M, false);
-print msg=Testing_VDI
-# Create disk containers, read verification is on.
-createdisk name=disk verify=yes
-# Create the disk.
-create disk=disk mode=base name=tstCompact.vdi type=dynamic backend=VDI size=200M
-# Fill the disk with random data
-io disk=disk async=no mode=seq blocksize=64k off=0-200M size=200M writes=100
-# Read the data to verify it once.
-io disk=disk async=no mode=seq blocksize=64k off=0-200M size=200M writes=0
-# Fill a part with 0's
-io disk=disk async=no mode=seq blocksize=64k off=100M-150M size=50M writes=100 pattern=zero
-# Now compact
-compact disk=disk image=0
-# Read again to verify that the content hasn't changed
-io disk=disk async=no mode=seq blocksize=64k off=0-200M size=200M writes=0
-# Fill everything with 0
-io disk=disk async=no mode=seq blocksize=64k off=0M-200M size=200M writes=100 pattern=zero
-# Now compact
-compact disk=disk image=0
-# Read again to verify that the content hasn't changed
-io disk=disk async=no mode=seq blocksize=64k off=0-200M size=200M writes=0
-# Cleanup
-close disk=disk mode=single delete=yes
-destroydisk name=disk
+ /* Fill the disk with random data. */
+ io("disk", false, 1, "seq", 64K, 0, 200M, 200M, 100, "none");
+ /* Read the data to verify it once. */
+ io("disk", false, 1, "seq", 64K, 0, 200M, 200M, 0, "none");
+ /* Fill a part with 0's. */
+ io("disk", false, 1, "seq", 64K, 100M, 150M, 50M, 100, "zero");
-print msg=Testing_VHD
-# Create disk containers, read verification is on.
-createdisk name=disk verify=yes
-# Create the disk.
-create disk=disk mode=base name=tstCompact.vhd type=dynamic backend=VHD size=200M
-# Fill the disk with random data
-io disk=disk async=no mode=seq blocksize=64k off=0-200M size=200M writes=100
-# Read the data to verify it once.
-io disk=disk async=no mode=seq blocksize=64k off=0-200M size=200M writes=0
-# Fill a part with 0's
-io disk=disk async=no mode=seq blocksize=64k off=100M-150M size=50M writes=100 pattern=zero
-# Now compact
-compact disk=disk image=0
-# Read again to verify that the content hasn't changed
-io disk=disk async=no mode=seq blocksize=64k off=0-200M size=200M writes=0
-# Fill everything with 0
-io disk=disk async=no mode=seq blocksize=64k off=0M-200M size=200M writes=100 pattern=zero
-# Now compact
-compact disk=disk image=0
-# Read again to verify that the content hasn't changed
-io disk=disk async=no mode=seq blocksize=64k off=0-200M size=200M writes=0
-# Cleanup
-close disk=disk mode=single delete=yes
-destroydisk name=disk
+ /* Now compact. */
+ compact("disk", 0);
+ /* Read again to verify that the content hasn't changed. */
+ io("disk", false, 1, "seq", 64K, 0, 200M, 200M, 0, "none");
+ /* Fill everything with 0. */
+ io("disk", false, 1, "seq", 64K, 0, 200M, 200M, 100, "zero");
+ /* Now compact again. */
+ compact("disk", 0);
+ /* Read again to verify that the content hasn't changed. */
+ io("disk", false, 1, "seq", 64K, 0, 200M, 200M, 0, "none");
-# Destroy RNG and pattern
-iopatterndestroy name=zero
-iorngdestroy
+ close("disk", "single", true);
+ destroydisk("disk");
+}
+void main()
+{
+ /* Init I/O RNG for generating random data for writes. */
+ iorngcreate(10M, "manual", 1234567890);
+
+ /* Create zero pattern */
+ iopatterncreatefromnumber("zero", 1M, 0);
+
+ tstCompact("Testing VDI", "VDI");
+ tstCompact("Testing VHD", "VHD");
+
+ /* Destroy RNG and pattern */
+ iopatterndestroy("zero");
+ iorngdestroy();
+}
diff --git a/src/VBox/Storage/testcase/tstVDCopy.cpp b/src/VBox/Storage/testcase/tstVDCopy.cpp
index ef8a37e2..6d73560b 100644
--- a/src/VBox/Storage/testcase/tstVDCopy.cpp
+++ b/src/VBox/Storage/testcase/tstVDCopy.cpp
@@ -5,7 +5,7 @@
*/
/*
- * Copyright (C) 2006-2010 Oracle Corporation
+ * Copyright (C) 2006-2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
diff --git a/src/VBox/Storage/testcase/tstVDCopy.vd b/src/VBox/Storage/testcase/tstVDCopy.vd
index cd4c97f3..9cd4e034 100644
--- a/src/VBox/Storage/testcase/tstVDCopy.vd
+++ b/src/VBox/Storage/testcase/tstVDCopy.vd
@@ -1,89 +1,86 @@
-# $Id: tstVDCopy.vd $
-#
-# Storage: Testcase for VDCopy with snapshots and optimizations.
-#
-
-#
-# Copyright (C) 2011 Oracle Corporation
-#
-# This file is part of VirtualBox Open Source Edition (OSE), as
-# available from http://www.virtualbox.org. This file is free software;
-# you can redistribute it and/or modify it under the terms of the GNU
-# General Public License (GPL) as published by the Free Software
-# Foundation, in version 2 as it comes in the "COPYING" file of the
-# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
-# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
-#
-
-# Init I/O RNG for generating random data for writes
-iorngcreate size=10M mode=manual seed=1234567890
-
-# Create source disk and fill data
-print msg=Creating_Source_Disk
- createdisk name=source verify=no
- create disk=source mode=base name=source_base.vdi type=dynamic backend=VDI size=1G
- io disk=source async=no mode=rnd blocksize=64k off=0-512M size=256M writes=100
-
-# Create 1st snapshot
-print msg=Creating_First_Diff
- create disk=source mode=diff name=source_diff1.vdi type=dynamic backend=VDI size=1G
- io disk=source async=no mode=rnd blocksize=64k off=512M-1G size=256M writes=50
-
-# Create 2nd snapshot
-print msg=Creating_Second_Diff
- create disk=source mode=diff name=source_diff2.vdi type=dynamic backend=VDI size=1G
- io disk=source async=no mode=rnd blocksize=1M off=0M-1G size=45M writes=100
-
-print msg=Creating_Third_Diff
- create disk=source mode=diff name=source_diff3.vdi type=dynamic backend=VDI size=1G
- io disk=source async=no mode=rnd blocksize=1M off=0M-1G size=45M writes=100
-
-print msg=Creating_Fourth_Diff
- create disk=source mode=diff name=source_diff4.vdi type=dynamic backend=VDI size=1G
- io disk=source async=no mode=rnd blocksize=1M off=0M-1G size=45M writes=100
-
-# Create destination disk
-print msg=Creating_Destination_Disk
- createdisk name=dest verify=no
-
-# Copy base image
-print msg=Copying_Base_Image
- copy diskfrom=source diskto=dest imagefrom=0 backend=VDI filename=dest_base.vdi
-
-print msg=Copying_First_Diff_optimized
- copy diskfrom=source diskto=dest imagefrom=1 backend=VDI filename=dest_diff1.vdi fromsame=0 tosame=0
-
-print msg=Copying_Second_Diff_optimized
- copy diskfrom=source diskto=dest imagefrom=2 backend=VDI filename=dest_diff2.vdi fromsame=1 tosame=1
- copy diskfrom=source diskto=dest imagefrom=3 backend=VDI filename=dest_diff3.vdi fromsame=2 tosame=2
- copy diskfrom=source diskto=dest imagefrom=4 backend=VDI filename=dest_diff4.vdi fromsame=3 tosame=3
-
-print msg=Comparing_Disks
- comparedisks disk1=source disk2=dest
-
-printfilesize disk=source image=0
-printfilesize disk=source image=1
-printfilesize disk=source image=2
-printfilesize disk=source image=3
-printfilesize disk=source image=4
-
-printfilesize disk=dest image=0
-printfilesize disk=dest image=1
-printfilesize disk=dest image=2
-printfilesize disk=dest image=3
-printfilesize disk=dest image=4
-
-# Cleanup
-print msg=Cleaning_up
- close disk=dest mode=single delete=yes
- close disk=dest mode=single delete=yes
- close disk=dest mode=single delete=yes
-
- close disk=source mode=single delete=yes
- close disk=source mode=single delete=yes
- close disk=source mode=single delete=yes
- destroydisk name=source
- destroydisk name=dest
-
-iorngdestroy
-
+/* $Id: tstVDCopy.vd $ */
+/**
+ * Storage: Testcase for VDCopy with snapshots and optimizations.
+ */
+
+/*
+ * Copyright (C) 2011 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+void main()
+{
+ /* Init I/O RNG for generating random data for writes. */
+ iorngcreate(10M, "manual", 1234567890);
+
+ /* Create source disk and fill data. */
+ print("Creating Source Disk");
+ createdisk("source", false);
+ create("source", "base", "source_base.vdi", "dynamic", "VDI", 1G, false);
+ io("source", false, 1, "rnd", 64K, 0, 512M, 256M, 100, "none");
+
+ print("Creating first diff");
+ create("source", "diff", "source_diff1.vdi", "dynamic", "VDI", 1G, false);
+ io("source", false, 1, "rnd", 64K, 512M, 1G, 256M, 50, "none");
+
+ print("Creating second diff");
+ create("source", "diff", "source_diff2.vdi", "dynamic", "VDI", 1G, false);
+ io("source", false, 1, "rnd", 1M, 0, 1G, 45M, 100, "none");
+
+ print("Creating third diff");
+ create("source", "diff", "source_diff3.vdi", "dynamic", "VDI", 1G, false);
+ io("source", false, 1, "rnd", 1M, 0, 1G, 45M, 100, "none");
+
+ print("Creating fourth diff");
+ create("source", "diff", "source_diff4.vdi", "dynamic", "VDI", 1G, false);
+ io("source", false, 1, "rnd", 1M, 0, 1G, 45M, 100, "none");
+
+ print("Creating destination disk");
+ createdisk("dest", false);
+
+ print("Copying base image");
+ copy("source", "dest", 0, "VDI", "dest_base.vdi", false, 0, 0xffffffff, 0xffffffff); /* Image content unknown */
+
+ print("Copying first diff optimized");
+ copy("source", "dest", 1, "VDI", "dest_diff1.vdi", false, 0, 0, 0);
+
+ print("Copying other diffs optimized");
+ copy("source", "dest", 2, "VDI", "dest_diff1.vdi", false, 0, 1, 1);
+ copy("source", "dest", 3, "VDI", "dest_diff1.vdi", false, 0, 2, 2);
+ copy("source", "dest", 4, "VDI", "dest_diff1.vdi", false, 0, 3, 3);
+
+ print("Comparing_Disks");
+ comparedisks("source", "dest");
+
+ printfilesize("source", 0);
+ printfilesize("source", 1);
+ printfilesize("source", 2);
+ printfilesize("source", 3);
+ printfilesize("source", 4);
+
+ printfilesize("dest", 0);
+ printfilesize("dest", 1);
+ printfilesize("dest", 2);
+ printfilesize("dest", 3);
+ printfilesize("dest", 4);
+
+ print("Cleaning up");
+ close("dest", "single", true);
+ close("dest", "single", true);
+ close("dest", "single", true);
+
+ close("source", "single", true);
+ close("source", "single", true);
+ close("source", "single", true);
+ destroydisk("source");
+ destroydisk("dest");
+
+ iorngdestroy();
+}
diff --git a/src/VBox/Storage/testcase/tstVDDiscard.vd b/src/VBox/Storage/testcase/tstVDDiscard.vd
index 7ac1a0ad..56fadf58 100644
--- a/src/VBox/Storage/testcase/tstVDDiscard.vd
+++ b/src/VBox/Storage/testcase/tstVDDiscard.vd
@@ -1,55 +1,59 @@
-# $Id: tstVDDiscard.vd $
-#
-# Storage: Testcase for discarding data in a disk.
-#
-
-#
-# Copyright (C) 2011 Oracle Corporation
-#
-# This file is part of VirtualBox Open Source Edition (OSE), as
-# available from http://www.virtualbox.org. This file is free software;
-# you can redistribute it and/or modify it under the terms of the GNU
-# General Public License (GPL) as published by the Free Software
-# Foundation, in version 2 as it comes in the "COPYING" file of the
-# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
-# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
-#
-
-# Init I/O RNG for generating random data for writes
-iorngcreate size=10M mode=manual seed=1234567890
-
-print msg=Testing_VDI
-# Create disk containers, read verification is on.
-createdisk name=disk verify=yes
-# Create the disk.
-create disk=disk mode=base name=tstCompact.vdi type=dynamic backend=VDI size=2G
-# Fill the disk with random data
-io disk=disk async=no mode=seq blocksize=64k off=0-2G size=2G writes=100
-# Read the data to verify it once.
-io disk=disk async=no mode=seq blocksize=64k off=0-2G size=2G writes=0
-close disk=disk mode=single delete=no
-
-open disk=disk name=tstCompact.vdi backend=VDI async=yes discard=yes
-printfilesize disk=disk image=0
-discard async=yes disk=disk ranges=6,0M,512k,1M,512k,2M,512k,3M,512k,4M,512k,5M,512k
-discard async=yes disk=disk ranges=6,6M,512k,7M,512k,8M,512k,9M,512k,10M,512k,11M,512k
-discard async=yes disk=disk ranges=1,512k,512k
-printfilesize disk=disk image=0
-
-print msg=Discard_whole_block
-discard async=yes disk=disk ranges=1,20M,1M
-printfilesize disk=disk image=0
-
-print msg=Split_Discard
-discard async=yes disk=disk ranges=1,21M,512k
-printfilesize disk=disk image=0
-discard async=yes disk=disk ranges=1,22016k,512k
-printfilesize disk=disk image=0
-
-# Cleanup
-close disk=disk mode=single delete=yes
-destroydisk name=disk
-
-# Destroy RNG and pattern
-iorngdestroy
-
+/* $Id: tstVDDiscard.vd $ */
+/**
+ * Storage: Testcase for discarding data in a disk.
+ */
+
+/*
+ * Copyright (C) 2011 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+void main()
+{
+ /* Init I/O RNG for generating random data for writes. */
+ iorngcreate(10M, "manual", 1234567890);
+
+ print("Testing VDI");
+
+ /* Create disk containers, read verification is on. */
+ createdisk("disk", true /* fVerify */);
+ /* Create the disk. */
+ create("disk", "base", "tstCompact.vdi", "dynamic", "VDI", 2G, false /* fIgnoreFlush */);
+ /* Fill the disk with random data */
+ io("disk", false, 1, "seq", 64K, 0, 2G, 2G, 100, "none");
+ /* Read the data to verify it once. */
+ io("disk", false, 1, "seq", 64K, 0, 2G, 2G, 0, "none");
+ close("disk", "single", false);
+
+ open("disk", "tstCompact.vdi", "VDI", true, false, false, true, false);
+ printfilesize("disk", 0);
+ discard("disk", true, "6,0M,512K,1M,512K,2M,512K,3M,512K,4M,512K,5M,512K");
+ discard("disk", true, "6,6M,512K,7M,512K,8M,512K,9M,512K,10M,512K,11M,512K");
+ discard("disk", true, "1,512K,512K");
+ discard("disk", false, "1,1024K,64K");
+ printfilesize("disk", 0);
+
+ print("Discard whole block");
+ discard("disk", true, "1,20M,1M");
+ printfilesize("disk", 0);
+
+ print("Split Discard");
+ discard("disk", true, "1,21M,512K");
+ printfilesize("disk", 0);
+ discard("disk", true, "1,22016K,512K");
+ printfilesize("disk", 0);
+
+ /* Cleanup */
+ close("disk", "single", true);
+ destroydisk("disk");
+
+ /* Destroy RNG and pattern */
+ iorngdestroy();
+}
diff --git a/src/VBox/Storage/testcase/tstVDIo.cpp b/src/VBox/Storage/testcase/tstVDIo.cpp
index d492c50a..9422f477 100644
--- a/src/VBox/Storage/testcase/tstVDIo.cpp
+++ b/src/VBox/Storage/testcase/tstVDIo.cpp
@@ -5,7 +5,7 @@
*/
/*
- * Copyright (C) 2011 Oracle Corporation
+ * Copyright (C) 2011-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -34,9 +34,11 @@
#include <iprt/critsect.h>
#include "VDMemDisk.h"
-#include "VDIoBackendMem.h"
+#include "VDIoBackend.h"
#include "VDIoRnd.h"
+#include "VDScript.h"
+
/**
* A virtual file backed by memory.
*/
@@ -46,8 +48,8 @@ typedef struct VDFILE
RTLISTNODE Node;
/** Name of the file. */
char *pszName;
- /** Memory file baking the file. */
- PVDMEMDISK pMemDisk;
+ /** Storage backing the file. */
+ PVDIOSTORAGE pIoStorage;
/** Flag whether the file is read locked. */
bool fReadLock;
/** Flag whether the file is write locked. */
@@ -124,8 +126,8 @@ typedef struct VDTESTGLOB
RTLISTNODE ListFiles;
/** Head of the pattern list. */
RTLISTNODE ListPatterns;
- /** Memory I/O backend. */
- PVDIOBACKENDMEM pIoBackend;
+ /** I/O backend, common data. */
+ PVDIOBACKEND pIoBackend;
/** Error interface. */
VDINTERFACEERROR VDIfError;
/** Pointer to the per disk interface list. */
@@ -136,6 +138,8 @@ typedef struct VDTESTGLOB
PVDINTERFACE pInterfacesImages;
/** I/O RNG handle. */
PVDIORND pIoRnd;
+ /** Current storage backend to use. */
+ char *pszIoBackend;
} VDTESTGLOB, *PVDTESTGLOB;
/**
@@ -213,369 +217,279 @@ typedef struct VDIOTEST
} u;
} VDIOTEST, *PVDIOTEST;
-/**
- * Argument types.
- */
-typedef enum VDSCRIPTARGTYPE
-{
- /** Argument is a string. */
- VDSCRIPTARGTYPE_STRING = 0,
- /** Argument is a 64bit unsigned number. */
- VDSCRIPTARGTYPE_UNSIGNED_NUMBER,
- /** Argument is a 64bit signed number. */
- VDSCRIPTARGTYPE_SIGNED_NUMBER,
- /** Arugment is a unsigned 64bit range */
- VDSCRIPTARGTYPE_UNSIGNED_RANGE,
- /** Arugment is a boolean. */
- VDSCRIPTARGTYPE_BOOL
-} VDSCRIPTARGTYPE;
-
-/**
- * Script argument.
- */
-typedef struct VDSCRIPTARG
-{
- /** Argument identifier. */
- char chId;
- /** Type of the argument. */
- VDSCRIPTARGTYPE enmType;
- /** Type depndent data. */
- union
- {
- /** String. */
- const char *pcszString;
- /** Bool. */
- bool fFlag;
- /** unsigned number. */
- uint64_t u64;
- /** Signed number. */
- int64_t i64;
- /** Unsigned range. */
- struct
- {
- uint64_t Start;
- uint64_t End;
- } Range;
- } u;
-} VDSCRIPTARG, *PVDSCRIPTARG;
-
-/** Script action handler. */
-typedef DECLCALLBACK(int) FNVDSCRIPTACTION(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-/** Pointer to a script action handler. */
-typedef FNVDSCRIPTACTION *PFNVDSCRIPTACTION;
-
-/**
- * Script argument descriptor.
- */
-typedef struct VDSCRIPTARGDESC
-{
- /** Name of the arugment. */
- const char *pcszName;
- /** Identifier for the argument. */
- char chId;
- /** Type of the argument. */
- VDSCRIPTARGTYPE enmType;
- /** Flags */
- uint32_t fFlags;
-} VDSCRIPTARGDESC, *PVDSCRIPTARGDESC;
-/** Pointer to a const script argument descriptor. */
-typedef const VDSCRIPTARGDESC *PCVDSCRIPTARGDESC;
-
-/** Flag whether the argument is mandatory. */
-#define VDSCRIPTARGDESC_FLAG_MANDATORY RT_BIT(0)
-/** Flag whether the number can have a size suffix (K|M|G) */
-#define VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX RT_BIT(1)
-
-/**
- * Script action.
- */
-typedef struct VDSCRIPTACTION
-{
- /** Action name. */
- const char *pcszAction;
- /** Pointer to the arguments. */
- const PCVDSCRIPTARGDESC paArgDesc;
- /** Number of arugments in the array. */
- unsigned cArgDescs;
- /** Pointer to the action handler. */
- PFNVDSCRIPTACTION pfnHandler;
-} VDSCRIPTACTION, *PVDSCRIPTACTION;
-
-typedef const VDSCRIPTACTION *PCVDSCRIPTACTION;
-
-static DECLCALLBACK(int) vdScriptHandlerCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerFlush(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerMerge(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerCompact(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerDiscard(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerCopy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerClose(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerPrintFileSize(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerIoLogReplay(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromNumber(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromFile(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerIoPatternDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerSleep(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerPrintMsg(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerShowStatistics(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerResetStatistics(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
+static DECLCALLBACK(int) vdScriptHandlerCreate(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerOpen(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerIo(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerFlush(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerMerge(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerCompact(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerDiscard(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerCopy(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerClose(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerPrintFileSize(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerIoLogReplay(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromNumber(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromFile(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerIoPatternDestroy(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerSleep(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerPrintMsg(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerShowStatistics(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerResetStatistics(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerResize(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerSetFileBackend(PVDSCRIPTARG paScriptArgs, void *pvUser);
/* create action */
-const VDSCRIPTARGDESC g_aArgCreate[] =
-{
- /* pcszName chId enmType fFlags */
- {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"type", 't', VDSCRIPTARGTYPE_STRING, 0},
- {"backend", 'b', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"size", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
- {"ignoreflush", 'f', VDSCRIPTARGTYPE_BOOL, 0}
+const VDSCRIPTTYPE g_aArgCreate[] =
+{
+ VDSCRIPTTYPE_STRING,
+ VDSCRIPTTYPE_STRING,
+ VDSCRIPTTYPE_STRING,
+ VDSCRIPTTYPE_STRING,
+ VDSCRIPTTYPE_STRING,
+ VDSCRIPTTYPE_UINT64,
+ VDSCRIPTTYPE_BOOL
};
/* open action */
-const VDSCRIPTARGDESC g_aArgOpen[] =
-{
- /* pcszName chId enmType fFlags */
- {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"backend", 'b', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"async", 'a', VDSCRIPTARGTYPE_BOOL, 0},
- {"shareable", 's', VDSCRIPTARGTYPE_BOOL, 0},
- {"readonly", 'r', VDSCRIPTARGTYPE_BOOL, 0},
- {"discard", 'i', VDSCRIPTARGTYPE_BOOL, 0},
- {"ignoreflush", 'f', VDSCRIPTARGTYPE_BOOL, 0},
+const VDSCRIPTTYPE g_aArgOpen[] =
+{
+ VDSCRIPTTYPE_STRING, /* disk */
+ VDSCRIPTTYPE_STRING, /* name */
+ VDSCRIPTTYPE_STRING, /* backend */
+ VDSCRIPTTYPE_BOOL, /* async */
+ VDSCRIPTTYPE_BOOL, /* shareable */
+ VDSCRIPTTYPE_BOOL, /* readonly */
+ VDSCRIPTTYPE_BOOL, /* discard */
+ VDSCRIPTTYPE_BOOL /* ignoreflush */
};
/* I/O action */
-const VDSCRIPTARGDESC g_aArgIo[] =
-{
- /* pcszName chId enmType fFlags */
- {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"async", 'a', VDSCRIPTARGTYPE_BOOL, 0},
- {"max-reqs", 'l', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, 0},
- {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"size", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
- {"blocksize", 'b', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
- {"off", 'o', VDSCRIPTARGTYPE_UNSIGNED_RANGE, VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
- {"writes", 'w', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"pattern", 'p', VDSCRIPTARGTYPE_STRING, 0},
+const VDSCRIPTTYPE g_aArgIo[] =
+{
+ VDSCRIPTTYPE_STRING, /* disk */
+ VDSCRIPTTYPE_BOOL, /* async */
+ VDSCRIPTTYPE_UINT32, /* max-reqs */
+ VDSCRIPTTYPE_STRING, /* mode */
+ VDSCRIPTTYPE_UINT64, /* size */
+ VDSCRIPTTYPE_UINT64, /* blocksize */
+ VDSCRIPTTYPE_UINT64, /* offStart */
+ VDSCRIPTTYPE_UINT64, /* offEnd */
+ VDSCRIPTTYPE_UINT32, /* writes */
+ VDSCRIPTTYPE_STRING /* pattern */
};
/* flush action */
-const VDSCRIPTARGDESC g_aArgFlush[] =
+const VDSCRIPTTYPE g_aArgFlush[] =
{
- /* pcszName chId enmType fFlags */
- {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"async", 'a', VDSCRIPTARGTYPE_BOOL, 0}
+ VDSCRIPTTYPE_STRING, /* disk */
+ VDSCRIPTTYPE_BOOL /* async */
};
/* merge action */
-const VDSCRIPTARGDESC g_aArgMerge[] =
+const VDSCRIPTTYPE g_aArgMerge[] =
{
- /* pcszName chId enmType fFlags */
- {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"from", 'f', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"to", 't', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY},
+ VDSCRIPTTYPE_STRING, /* disk */
+ VDSCRIPTTYPE_UINT32, /* from */
+ VDSCRIPTTYPE_UINT32 /* to */
};
/* Compact a disk */
-const VDSCRIPTARGDESC g_aArgCompact[] =
+const VDSCRIPTTYPE g_aArgCompact[] =
{
- /* pcszName chId enmType fFlags */
- {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"image", 'i', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY},
+ VDSCRIPTTYPE_STRING, /* disk */
+ VDSCRIPTTYPE_UINT32 /* image */
};
/* Discard a part of a disk */
-const VDSCRIPTARGDESC g_aArgDiscard[] =
+const VDSCRIPTTYPE g_aArgDiscard[] =
{
- /* pcszName chId enmType fFlags */
- {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"async", 'a', VDSCRIPTARGTYPE_BOOL, 0},
- {"ranges", 'r', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}
+ VDSCRIPTTYPE_STRING, /* disk */
+ VDSCRIPTTYPE_BOOL, /* async */
+ VDSCRIPTTYPE_STRING /* ranges */
};
/* Compact a disk */
-const VDSCRIPTARGDESC g_aArgCopy[] =
-{
- /* pcszName chId enmType fFlags */
- {"diskfrom", 's', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"diskto", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"imagefrom", 'i', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"backend", 'b', VDSCRIPTARGTYPE_STRING, 0},
- {"filename", 'f', VDSCRIPTARGTYPE_STRING, 0},
- {"movebyrename", 'm', VDSCRIPTARGTYPE_BOOL, 0},
- {"size", 'z', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, 0},
- {"fromsame", 'o', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, 0},
- {"tosame", 't', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, 0}
+const VDSCRIPTTYPE g_aArgCopy[] =
+{
+ VDSCRIPTTYPE_STRING, /* diskfrom */
+ VDSCRIPTTYPE_STRING, /* diskto */
+ VDSCRIPTTYPE_UINT32, /* imagefrom */
+ VDSCRIPTTYPE_STRING, /* backend */
+ VDSCRIPTTYPE_STRING, /* filename */
+ VDSCRIPTTYPE_BOOL, /* movebyrename */
+ VDSCRIPTTYPE_UINT64, /* size */
+ VDSCRIPTTYPE_UINT32, /* fromsame */
+ VDSCRIPTTYPE_UINT32 /* tosame */
};
/* close action */
-const VDSCRIPTARGDESC g_aArgClose[] =
+const VDSCRIPTTYPE g_aArgClose[] =
{
- /* pcszName chId enmType fFlags */
- {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"delete", 'r', VDSCRIPTARGTYPE_BOOL, VDSCRIPTARGDESC_FLAG_MANDATORY}
+ VDSCRIPTTYPE_STRING, /* disk */
+ VDSCRIPTTYPE_STRING, /* mode */
+ VDSCRIPTTYPE_BOOL /* delete */
};
/* print file size action */
-const VDSCRIPTARGDESC g_aArgPrintFileSize[] =
+const VDSCRIPTTYPE g_aArgPrintFileSize[] =
{
- /* pcszName chId enmType fFlags */
- {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"image", 'i', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY}
+ VDSCRIPTTYPE_STRING, /* disk */
+ VDSCRIPTTYPE_UINT32 /* image */
};
/* print file size action */
-const VDSCRIPTARGDESC g_aArgIoLogReplay[] =
+const VDSCRIPTTYPE g_aArgIoLogReplay[] =
{
- /* pcszName chId enmType fFlags */
- {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"iolog", 'i', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}
+ VDSCRIPTTYPE_STRING, /* disk */
+ VDSCRIPTTYPE_STRING /* iolog */
};
/* I/O RNG create action */
-const VDSCRIPTARGDESC g_aArgIoRngCreate[] =
+const VDSCRIPTTYPE g_aArgIoRngCreate[] =
{
- /* pcszName chId enmType fFlags */
- {"size", 'd', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
- {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"seed", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, 0}
+ VDSCRIPTTYPE_UINT32, /* size */
+ VDSCRIPTTYPE_STRING, /* mode */
+ VDSCRIPTTYPE_UINT32, /* seed */
};
/* I/O pattern create action */
-const VDSCRIPTARGDESC g_aArgIoPatternCreateFromNumber[] =
+const VDSCRIPTTYPE g_aArgIoPatternCreateFromNumber[] =
{
- /* pcszName chId enmType fFlags */
- {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"size", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
- {"pattern", 'p', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY},
+ VDSCRIPTTYPE_STRING, /* name */
+ VDSCRIPTTYPE_UINT32, /* size */
+ VDSCRIPTTYPE_UINT32 /* pattern */
};
/* I/O pattern create action */
-const VDSCRIPTARGDESC g_aArgIoPatternCreateFromFile[] =
+const VDSCRIPTTYPE g_aArgIoPatternCreateFromFile[] =
{
- /* pcszName chId enmType fFlags */
- {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"file", 'f', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
+ VDSCRIPTTYPE_STRING, /* name */
+ VDSCRIPTTYPE_STRING /* file */
};
/* I/O pattern destroy action */
-const VDSCRIPTARGDESC g_aArgIoPatternDestroy[] =
+const VDSCRIPTTYPE g_aArgIoPatternDestroy[] =
{
- /* pcszName chId enmType fFlags */
- {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}
+ VDSCRIPTTYPE_STRING /* name */
};
/* Sleep */
-const VDSCRIPTARGDESC g_aArgSleep[] =
+const VDSCRIPTTYPE g_aArgSleep[] =
{
- /* pcszName chId enmType fFlags */
- {"time", 't', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY}
+ VDSCRIPTTYPE_UINT32 /* time */
};
/* Dump memory file */
-const VDSCRIPTARGDESC g_aArgDumpFile[] =
+const VDSCRIPTTYPE g_aArgDumpFile[] =
{
- /* pcszName chId enmType fFlags */
- {"file", 'f', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"path", 'p', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}
+ VDSCRIPTTYPE_STRING, /* file */
+ VDSCRIPTTYPE_STRING /* path */
};
/* Create virtual disk handle */
-const VDSCRIPTARGDESC g_aArgCreateDisk[] =
+const VDSCRIPTTYPE g_aArgCreateDisk[] =
{
- /* pcszName chId enmType fFlags */
- {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"verify", 'v', VDSCRIPTARGTYPE_BOOL, 0}
+ VDSCRIPTTYPE_STRING, /* name */
+ VDSCRIPTTYPE_BOOL /* verify */
};
/* Create virtual disk handle */
-const VDSCRIPTARGDESC g_aArgDestroyDisk[] =
+const VDSCRIPTTYPE g_aArgDestroyDisk[] =
{
- /* pcszName chId enmType fFlags */
- {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}
+ VDSCRIPTTYPE_STRING /* name */
};
/* Compare virtual disks */
-const VDSCRIPTARGDESC g_aArgCompareDisks[] =
+const VDSCRIPTTYPE g_aArgCompareDisks[] =
{
- /* pcszName chId enmType fFlags */
- {"disk1", '1', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"disk2", '2', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}
+ VDSCRIPTTYPE_STRING, /* disk1 */
+ VDSCRIPTTYPE_STRING /* disk2 */
};
/* Dump disk info */
-const VDSCRIPTARGDESC g_aArgDumpDiskInfo[] =
+const VDSCRIPTTYPE g_aArgDumpDiskInfo[] =
{
- /* pcszName chId enmType fFlags */
- {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
+ VDSCRIPTTYPE_STRING /* disk */
};
/* Print message */
-const VDSCRIPTARGDESC g_aArgPrintMsg[] =
+const VDSCRIPTTYPE g_aArgPrintMsg[] =
{
- /* pcszName chId enmType fFlags */
- {"msg", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
+ VDSCRIPTTYPE_STRING /* msg */
};
/* Show statistics */
-const VDSCRIPTARGDESC g_aArgShowStatistics[] =
+const VDSCRIPTTYPE g_aArgShowStatistics[] =
{
- /* pcszName chId enmType fFlags */
- {"file", 'f', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
+ VDSCRIPTTYPE_STRING /* file */
};
/* Reset statistics */
-const VDSCRIPTARGDESC g_aArgResetStatistics[] =
+const VDSCRIPTTYPE g_aArgResetStatistics[] =
{
- /* pcszName chId enmType fFlags */
- {"file", 'f', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
+ VDSCRIPTTYPE_STRING /* file */
};
-const VDSCRIPTACTION g_aScriptActions[] =
-{
- /* pcszAction paArgDesc cArgDescs pfnHandler */
- {"create", g_aArgCreate, RT_ELEMENTS(g_aArgCreate), vdScriptHandlerCreate},
- {"open", g_aArgOpen, RT_ELEMENTS(g_aArgOpen), vdScriptHandlerOpen},
- {"io", g_aArgIo, RT_ELEMENTS(g_aArgIo), vdScriptHandlerIo},
- {"flush", g_aArgFlush, RT_ELEMENTS(g_aArgFlush), vdScriptHandlerFlush},
- {"close", g_aArgClose, RT_ELEMENTS(g_aArgClose), vdScriptHandlerClose},
- {"printfilesize", g_aArgPrintFileSize, RT_ELEMENTS(g_aArgPrintFileSize), vdScriptHandlerPrintFileSize},
- {"ioreplay", g_aArgIoLogReplay, RT_ELEMENTS(g_aArgIoLogReplay), vdScriptHandlerIoLogReplay},
- {"merge", g_aArgMerge, RT_ELEMENTS(g_aArgMerge), vdScriptHandlerMerge},
- {"compact", g_aArgCompact, RT_ELEMENTS(g_aArgCompact), vdScriptHandlerCompact},
- {"discard", g_aArgDiscard, RT_ELEMENTS(g_aArgDiscard), vdScriptHandlerDiscard},
- {"copy", g_aArgCopy, RT_ELEMENTS(g_aArgCopy), vdScriptHandlerCopy},
- {"iorngcreate", g_aArgIoRngCreate, RT_ELEMENTS(g_aArgIoRngCreate), vdScriptHandlerIoRngCreate},
- {"iorngdestroy", NULL, 0, vdScriptHandlerIoRngDestroy},
- {"iopatterncreatefromnumber", g_aArgIoPatternCreateFromNumber, RT_ELEMENTS(g_aArgIoPatternCreateFromNumber), vdScriptHandlerIoPatternCreateFromNumber},
- {"iopatterncreatefromfile", g_aArgIoPatternCreateFromFile, RT_ELEMENTS(g_aArgIoPatternCreateFromFile), vdScriptHandlerIoPatternCreateFromFile},
- {"iopatterndestroy", g_aArgIoPatternDestroy, RT_ELEMENTS(g_aArgIoPatternDestroy), vdScriptHandlerIoPatternDestroy},
- {"sleep", g_aArgSleep, RT_ELEMENTS(g_aArgSleep), vdScriptHandlerSleep},
- {"dumpfile", g_aArgDumpFile, RT_ELEMENTS(g_aArgDumpFile), vdScriptHandlerDumpFile},
- {"createdisk", g_aArgCreateDisk, RT_ELEMENTS(g_aArgCreateDisk), vdScriptHandlerCreateDisk},
- {"destroydisk", g_aArgDestroyDisk, RT_ELEMENTS(g_aArgDestroyDisk), vdScriptHandlerDestroyDisk},
- {"comparedisks", g_aArgCompareDisks, RT_ELEMENTS(g_aArgCompareDisks), vdScriptHandlerCompareDisks},
- {"dumpdiskinfo", g_aArgDumpDiskInfo, RT_ELEMENTS(g_aArgDumpDiskInfo), vdScriptHandlerDumpDiskInfo},
- {"print", g_aArgPrintMsg, RT_ELEMENTS(g_aArgPrintMsg), vdScriptHandlerPrintMsg},
- {"showstatistics", g_aArgShowStatistics, RT_ELEMENTS(g_aArgShowStatistics), vdScriptHandlerShowStatistics},
- {"resetstatistics", g_aArgResetStatistics, RT_ELEMENTS(g_aArgResetStatistics), vdScriptHandlerResetStatistics}
+/* Resize disk. */
+const VDSCRIPTTYPE g_aArgResize[] =
+{
+ VDSCRIPTTYPE_STRING, /* disk */
+ VDSCRIPTTYPE_UINT64 /* size */
+};
+
+/* Set file backend. */
+const VDSCRIPTTYPE g_aArgSetFileBackend[] =
+{
+ VDSCRIPTTYPE_STRING /* new file backend */
+};
+
+const VDSCRIPTCALLBACK g_aScriptActions[] =
+{
+ /* pcszFnName enmTypeReturn paArgDesc cArgDescs pfnHandler */
+ {"create", VDSCRIPTTYPE_VOID, g_aArgCreate, RT_ELEMENTS(g_aArgCreate), vdScriptHandlerCreate},
+ {"open", VDSCRIPTTYPE_VOID, g_aArgOpen, RT_ELEMENTS(g_aArgOpen), vdScriptHandlerOpen},
+ {"io", VDSCRIPTTYPE_VOID, g_aArgIo, RT_ELEMENTS(g_aArgIo), vdScriptHandlerIo},
+ {"flush", VDSCRIPTTYPE_VOID, g_aArgFlush, RT_ELEMENTS(g_aArgFlush), vdScriptHandlerFlush},
+ {"close", VDSCRIPTTYPE_VOID, g_aArgClose, RT_ELEMENTS(g_aArgClose), vdScriptHandlerClose},
+ {"printfilesize", VDSCRIPTTYPE_VOID, g_aArgPrintFileSize, RT_ELEMENTS(g_aArgPrintFileSize), vdScriptHandlerPrintFileSize},
+ {"ioreplay", VDSCRIPTTYPE_VOID, g_aArgIoLogReplay, RT_ELEMENTS(g_aArgIoLogReplay), vdScriptHandlerIoLogReplay},
+ {"merge", VDSCRIPTTYPE_VOID, g_aArgMerge, RT_ELEMENTS(g_aArgMerge), vdScriptHandlerMerge},
+ {"compact", VDSCRIPTTYPE_VOID, g_aArgCompact, RT_ELEMENTS(g_aArgCompact), vdScriptHandlerCompact},
+ {"discard", VDSCRIPTTYPE_VOID, g_aArgDiscard, RT_ELEMENTS(g_aArgDiscard), vdScriptHandlerDiscard},
+ {"copy", VDSCRIPTTYPE_VOID, g_aArgCopy, RT_ELEMENTS(g_aArgCopy), vdScriptHandlerCopy},
+ {"iorngcreate", VDSCRIPTTYPE_VOID, g_aArgIoRngCreate, RT_ELEMENTS(g_aArgIoRngCreate), vdScriptHandlerIoRngCreate},
+ {"iorngdestroy", VDSCRIPTTYPE_VOID, NULL, 0, vdScriptHandlerIoRngDestroy},
+ {"iopatterncreatefromnumber", VDSCRIPTTYPE_VOID, g_aArgIoPatternCreateFromNumber, RT_ELEMENTS(g_aArgIoPatternCreateFromNumber), vdScriptHandlerIoPatternCreateFromNumber},
+ {"iopatterncreatefromfile", VDSCRIPTTYPE_VOID, g_aArgIoPatternCreateFromFile, RT_ELEMENTS(g_aArgIoPatternCreateFromFile), vdScriptHandlerIoPatternCreateFromFile},
+ {"iopatterndestroy", VDSCRIPTTYPE_VOID, g_aArgIoPatternDestroy, RT_ELEMENTS(g_aArgIoPatternDestroy), vdScriptHandlerIoPatternDestroy},
+ {"sleep", VDSCRIPTTYPE_VOID, g_aArgSleep, RT_ELEMENTS(g_aArgSleep), vdScriptHandlerSleep},
+ {"dumpfile", VDSCRIPTTYPE_VOID, g_aArgDumpFile, RT_ELEMENTS(g_aArgDumpFile), vdScriptHandlerDumpFile},
+ {"createdisk", VDSCRIPTTYPE_VOID, g_aArgCreateDisk, RT_ELEMENTS(g_aArgCreateDisk), vdScriptHandlerCreateDisk},
+ {"destroydisk", VDSCRIPTTYPE_VOID, g_aArgDestroyDisk, RT_ELEMENTS(g_aArgDestroyDisk), vdScriptHandlerDestroyDisk},
+ {"comparedisks", VDSCRIPTTYPE_VOID, g_aArgCompareDisks, RT_ELEMENTS(g_aArgCompareDisks), vdScriptHandlerCompareDisks},
+ {"dumpdiskinfo", VDSCRIPTTYPE_VOID, g_aArgDumpDiskInfo, RT_ELEMENTS(g_aArgDumpDiskInfo), vdScriptHandlerDumpDiskInfo},
+ {"print", VDSCRIPTTYPE_VOID, g_aArgPrintMsg, RT_ELEMENTS(g_aArgPrintMsg), vdScriptHandlerPrintMsg},
+ {"showstatistics", VDSCRIPTTYPE_VOID, g_aArgShowStatistics, RT_ELEMENTS(g_aArgShowStatistics), vdScriptHandlerShowStatistics},
+ {"resetstatistics", VDSCRIPTTYPE_VOID, g_aArgResetStatistics, RT_ELEMENTS(g_aArgResetStatistics), vdScriptHandlerResetStatistics},
+ {"resize", VDSCRIPTTYPE_VOID, g_aArgResize, RT_ELEMENTS(g_aArgResize), vdScriptHandlerResize},
+ {"setfilebackend", VDSCRIPTTYPE_VOID, g_aArgSetFileBackend, RT_ELEMENTS(g_aArgSetFileBackend), vdScriptHandlerSetFileBackend},
};
const unsigned g_cScriptActions = RT_ELEMENTS(g_aScriptActions);
+static DECLCALLBACK(int) vdScriptCallbackPrint(PVDSCRIPTARG paScriptArgs, void *pvUser)
+{
+ RTPrintf(paScriptArgs[0].psz);
+ return VINF_SUCCESS;
+}
+
static void tstVDError(void *pvUser, int rc, RT_SRC_POS_DECL,
const char *pszFormat, va_list va)
{
@@ -605,9 +519,10 @@ static PVDPATTERN tstVDIoGetPatternByName(PVDTESTGLOB pGlob, const char *pcszNam
static PVDPATTERN tstVDIoPatternCreate(const char *pcszName, size_t cbPattern);
static int tstVDIoPatternGetBuffer(PVDPATTERN pPattern, void **ppv, size_t cb);
-static DECLCALLBACK(int) vdScriptHandlerCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerCreate(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
uint64_t cbSize = 0;
const char *pcszBackend = NULL;
const char *pcszImage = NULL;
@@ -616,69 +531,31 @@ static DECLCALLBACK(int) vdScriptHandlerCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG p
bool fBase = false;
bool fDynamic = true;
bool fIgnoreFlush = false;
+ PVDIOBACKEND pIoBackend = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
+ pcszDisk = paScriptArgs[0].psz;
+ if (!RTStrICmp(paScriptArgs[1].psz, "base"))
+ fBase = true;
+ else if (!RTStrICmp(paScriptArgs[1].psz, "diff"))
+ fBase = false;
+ else
{
- switch (paScriptArgs[i].chId)
- {
- case 'd':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'm':
- {
- if (!RTStrICmp(paScriptArgs[i].u.pcszString, "base"))
- fBase = true;
- else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "diff"))
- fBase = false;
- else
- {
- RTPrintf("Invalid image mode '%s' given\n", paScriptArgs[i].u.pcszString);
- rc = VERR_INVALID_PARAMETER;
- }
- break;
- }
- case 'n':
- {
- pcszImage = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'b':
- {
- pcszBackend = paScriptArgs[i].u.pcszString;
- break;
- }
- case 's':
- {
- cbSize = paScriptArgs[i].u.u64;
- break;
- }
- case 't':
- {
- if (!RTStrICmp(paScriptArgs[i].u.pcszString, "fixed"))
- fDynamic = false;
- else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "dynamic"))
- fDynamic = true;
- else
- {
- RTPrintf("Invalid image type '%s' given\n", paScriptArgs[i].u.pcszString);
- rc = VERR_INVALID_PARAMETER;
- }
- break;
- }
- case 'f':
- {
- fIgnoreFlush = paScriptArgs[i].u.fFlag;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
-
- if (RT_FAILURE(rc))
- break;
+ RTPrintf("Invalid image mode '%s' given\n", paScriptArgs[1].psz);
+ rc = VERR_INVALID_PARAMETER;
}
+ pcszImage = paScriptArgs[2].psz;
+ if (!RTStrICmp(paScriptArgs[3].psz, "fixed"))
+ fDynamic = false;
+ else if (!RTStrICmp(paScriptArgs[3].psz, "dynamic"))
+ fDynamic = true;
+ else
+ {
+ RTPrintf("Invalid image type '%s' given\n", paScriptArgs[3].psz);
+ rc = VERR_INVALID_PARAMETER;
+ }
+ pcszBackend = paScriptArgs[4].psz;
+ cbSize = paScriptArgs[5].u64;
+ fIgnoreFlush = paScriptArgs[6].f;
if (RT_SUCCESS(rc))
{
@@ -709,9 +586,10 @@ static DECLCALLBACK(int) vdScriptHandlerCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG p
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerOpen(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszBackend = NULL;
const char *pcszImage = NULL;
const char *pcszDisk = NULL;
@@ -722,52 +600,13 @@ static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paS
bool fDiscard = false;
bool fIgnoreFlush = false;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'd':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'n':
- {
- pcszImage = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'b':
- {
- pcszBackend = paScriptArgs[i].u.pcszString;
- break;
- }
- case 's':
- {
- fShareable = paScriptArgs[i].u.fFlag;
- break;
- }
- case 'r':
- {
- fReadonly = paScriptArgs[i].u.fFlag;
- break;
- }
- case 'a':
- {
- fAsyncIo = paScriptArgs[i].u.fFlag;
- break;
- }
- case 'i':
- {
- fDiscard = paScriptArgs[i].u.fFlag;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
-
- if (RT_FAILURE(rc))
- break;
- }
+ pcszDisk = paScriptArgs[0].psz;
+ pcszImage = paScriptArgs[1].psz;
+ pcszBackend = paScriptArgs[2].psz;
+ fShareable = paScriptArgs[3].f;
+ fReadonly = paScriptArgs[4].f;
+ fAsyncIo = paScriptArgs[5].f;
+ fDiscard = paScriptArgs[6].f;
if (RT_SUCCESS(rc))
{
@@ -796,9 +635,10 @@ static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paS
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerIo(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
bool fAsync = false;
bool fRandomAcc = false;
uint64_t cbIo = 0;
@@ -814,71 +654,24 @@ static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScr
PVDDISK pDisk = NULL;
PVDPATTERN pPattern = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
+ pcszDisk = paScriptArgs[0].psz;
+ fAsync = paScriptArgs[1].f;
+ cMaxReqs = paScriptArgs[2].u64;
+ if (!RTStrICmp(paScriptArgs[3].psz, "seq"))
+ fRandomAcc = false;
+ else if (!RTStrICmp(paScriptArgs[3].psz, "rnd"))
+ fRandomAcc = true;
+ else
{
- switch (paScriptArgs[i].chId)
- {
- case 'd':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'a':
- {
- fAsync = paScriptArgs[i].u.fFlag;
- break;
- }
- case 'l':
- {
- cMaxReqs = paScriptArgs[i].u.u64;
- break;
- }
- case 'm':
- {
- if (!RTStrICmp(paScriptArgs[i].u.pcszString, "seq"))
- fRandomAcc = false;
- else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "rnd"))
- fRandomAcc = true;
- else
- {
- RTPrintf("Invalid access mode '%s'\n", paScriptArgs[i].u.pcszString);
- rc = VERR_INVALID_PARAMETER;
- }
- break;
- }
- case 's':
- {
- cbIo = paScriptArgs[i].u.u64;
- break;
- }
- case 'b':
- {
- cbBlkSize = paScriptArgs[i].u.u64;
- break;
- }
- case 'o':
- {
- offStart = paScriptArgs[i].u.Range.Start;
- offEnd = paScriptArgs[i].u.Range.End;
- break;
- }
- case 'w':
- {
- uWriteChance = (uint8_t)paScriptArgs[i].u.u64;
- break;
- }
- case 'p':
- {
- pcszPattern = paScriptArgs[i].u.pcszString;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
-
- if (RT_FAILURE(rc))
- break;
+ RTPrintf("Invalid access mode '%s'\n", paScriptArgs[3].psz);
+ rc = VERR_INVALID_PARAMETER;
}
+ cbBlkSize = paScriptArgs[4].u64;
+ offStart = paScriptArgs[5].u64;
+ offEnd = paScriptArgs[6].u64;
+ cbIo = paScriptArgs[7].u64;
+ uWriteChance = (uint8_t)paScriptArgs[8].u64;
+ pcszPattern = paScriptArgs[9].psz;
if ( RT_SUCCESS(rc)
&& fAsync
@@ -907,7 +700,7 @@ static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScr
}
if ( RT_SUCCESS(rc)
- && pcszPattern)
+ && RTStrCmp(pcszPattern, "none"))
{
pPattern = tstVDIoGetPatternByName(pGlob, pcszPattern);
if (!pPattern)
@@ -1144,35 +937,16 @@ static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScr
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerFlush(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerFlush(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
bool fAsync = false;
const char *pcszDisk = NULL;
PVDDISK pDisk = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'd':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'a':
- {
- fAsync = paScriptArgs[i].u.fFlag;
- break;
- }
-
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
-
- if (RT_FAILURE(rc))
- break;
- }
+ pcszDisk = paScriptArgs[0].psz;
+ fAsync = paScriptArgs[1].f;
if (RT_SUCCESS(rc))
{
@@ -1210,137 +984,70 @@ static DECLCALLBACK(int) vdScriptHandlerFlush(PVDTESTGLOB pGlob, PVDSCRIPTARG pa
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerMerge(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerMerge(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszDisk = NULL;
PVDDISK pDisk = NULL;
unsigned nImageFrom = 0;
unsigned nImageTo = 0;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'd':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'f':
- {
- nImageFrom = (unsigned)paScriptArgs[i].u.u64;
- break;
- }
- case 't':
- {
- nImageTo = (unsigned)paScriptArgs[i].u.u64;
- break;
- }
-
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
+ pcszDisk = paScriptArgs[0].psz;
+ nImageFrom = paScriptArgs[1].u32;
+ nImageTo = paScriptArgs[2].u32;
- if (RT_FAILURE(rc))
- break;
- }
-
- if (RT_SUCCESS(rc))
+ pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
+ if (!pDisk)
+ rc = VERR_NOT_FOUND;
+ else
{
- pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
- if (!pDisk)
- rc = VERR_NOT_FOUND;
- else
- {
- /** @todo: Provide progress interface to test that cancelation
- * doesn't corrupt the data.
- */
- rc = VDMerge(pDisk->pVD, nImageFrom, nImageTo, NULL);
- }
+ /** @todo: Provide progress interface to test that cancelation
+ * doesn't corrupt the data.
+ */
+ rc = VDMerge(pDisk->pVD, nImageFrom, nImageTo, NULL);
}
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerCompact(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerCompact(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszDisk = NULL;
PVDDISK pDisk = NULL;
unsigned nImage = 0;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'd':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'i':
- {
- nImage = (unsigned)paScriptArgs[i].u.u64;
- break;
- }
-
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
-
- if (RT_FAILURE(rc))
- break;
- }
+ pcszDisk = paScriptArgs[0].psz;
+ nImage = paScriptArgs[1].u32;
- if (RT_SUCCESS(rc))
+ pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
+ if (!pDisk)
+ rc = VERR_NOT_FOUND;
+ else
{
- pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
- if (!pDisk)
- rc = VERR_NOT_FOUND;
- else
- {
- /** @todo: Provide progress interface to test that cancelation
- * doesn't corrupt the data.
- */
- rc = VDCompact(pDisk->pVD, nImage, NULL);
- }
+ /** @todo: Provide progress interface to test that cancelation
+ * doesn't corrupt the data.
+ */
+ rc = VDCompact(pDisk->pVD, nImage, NULL);
}
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerDiscard(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerDiscard(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszDisk = NULL;
PVDDISK pDisk = NULL;
bool fAsync = false;
const char *pcszRanges = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'd':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'a':
- {
- fAsync = paScriptArgs[i].u.fFlag;
- break;
- }
- case 'r':
- {
- pcszRanges = paScriptArgs[i].u.pcszString;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ pcszDisk = paScriptArgs[0].psz;
+ fAsync = paScriptArgs[1].f;
+ pcszRanges = paScriptArgs[2].psz;
pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
if (!pDisk)
@@ -1546,9 +1253,10 @@ static DECLCALLBACK(int) vdScriptHandlerDiscard(PVDTESTGLOB pGlob, PVDSCRIPTARG
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerCopy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerCopy(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszDiskFrom = NULL;
const char *pcszDiskTo = NULL;
PVDDISK pDiskFrom = NULL;
@@ -1561,130 +1269,56 @@ static DECLCALLBACK(int) vdScriptHandlerCopy(PVDTESTGLOB pGlob, PVDSCRIPTARG paS
unsigned nImageFromSame = VD_IMAGE_CONTENT_UNKNOWN;
unsigned nImageToSame = VD_IMAGE_CONTENT_UNKNOWN;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 's':
- {
- pcszDiskFrom = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'd':
- {
- pcszDiskTo = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'i':
- {
- nImageFrom = (unsigned)paScriptArgs[i].u.u64;
- break;
- }
- case 'b':
- {
- pcszBackend = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'f':
- {
- pcszFilename = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'm':
- {
- fMoveByRename = paScriptArgs[i].u.fFlag;
- break;
- }
- case 'z':
- {
- cbSize = paScriptArgs[i].u.u64;
- break;
- }
- case 'o':
- {
- nImageFromSame = (unsigned)paScriptArgs[i].u.u64;
- break;
- }
- case 't':
- {
- nImageToSame = (unsigned)paScriptArgs[i].u.u64;
- break;
- }
-
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
-
- if (RT_FAILURE(rc))
- break;
- }
-
- if (RT_SUCCESS(rc))
+ pcszDiskFrom = paScriptArgs[0].psz;
+ pcszDiskTo = paScriptArgs[1].psz;
+ nImageFrom = paScriptArgs[2].u32;
+ pcszBackend = paScriptArgs[3].psz;
+ pcszFilename = paScriptArgs[4].psz;
+ fMoveByRename = paScriptArgs[5].f;
+ cbSize = paScriptArgs[6].u64;
+ nImageFromSame = paScriptArgs[7].u32;
+ nImageToSame = paScriptArgs[8].u32;
+
+ pDiskFrom = tstVDIoGetDiskByName(pGlob, pcszDiskFrom);
+ pDiskTo = tstVDIoGetDiskByName(pGlob, pcszDiskTo);
+ if (!pDiskFrom || !pDiskTo)
+ rc = VERR_NOT_FOUND;
+ else
{
- pDiskFrom = tstVDIoGetDiskByName(pGlob, pcszDiskFrom);
- pDiskTo = tstVDIoGetDiskByName(pGlob, pcszDiskTo);
- if (!pDiskFrom || !pDiskTo)
- rc = VERR_NOT_FOUND;
- else
- {
- /** @todo: Provide progress interface to test that cancelation
- * works as intended.
- */
- rc = VDCopyEx(pDiskFrom->pVD, nImageFrom, pDiskTo->pVD, pcszBackend, pcszFilename,
- fMoveByRename, cbSize, nImageFromSame, nImageToSame,
- VD_IMAGE_FLAGS_NONE, NULL, VD_OPEN_FLAGS_ASYNC_IO,
- NULL, pGlob->pInterfacesImages, NULL);
- }
+ /** @todo: Provide progress interface to test that cancelation
+ * works as intended.
+ */
+ rc = VDCopyEx(pDiskFrom->pVD, nImageFrom, pDiskTo->pVD, pcszBackend, pcszFilename,
+ fMoveByRename, cbSize, nImageFromSame, nImageToSame,
+ VD_IMAGE_FLAGS_NONE, NULL, VD_OPEN_FLAGS_ASYNC_IO,
+ NULL, pGlob->pInterfacesImages, NULL);
}
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerClose(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerClose(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
bool fAll = false;
bool fDelete = false;
const char *pcszDisk = NULL;
PVDDISK pDisk = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
+ pcszDisk = paScriptArgs[0].psz;
+ if (!RTStrICmp(paScriptArgs[1].psz, "all"))
+ fAll = true;
+ else if (!RTStrICmp(paScriptArgs[1].psz, "single"))
+ fAll = false;
+ else
{
- switch (paScriptArgs[i].chId)
- {
- case 'd':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'm':
- {
- if (!RTStrICmp(paScriptArgs[i].u.pcszString, "all"))
- fAll = true;
- else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "single"))
- fAll = false;
- else
- {
- RTPrintf("Invalid mode '%s' given\n", paScriptArgs[i].u.pcszString);
- rc = VERR_INVALID_PARAMETER;
- }
- break;
- }
- case 'r':
- {
- fDelete = paScriptArgs[i].u.fFlag;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
-
- if (RT_FAILURE(rc))
- break;
+ RTPrintf("Invalid mode '%s' given\n", paScriptArgs[1].psz);
+ rc = VERR_INVALID_PARAMETER;
}
+ fDelete = paScriptArgs[2].f;
- if ( RT_SUCCESS(rc)
- && fAll
+ if ( fAll
&& fDelete)
{
RTPrintf("mode=all doesn't work with delete=yes\n");
@@ -1708,31 +1342,16 @@ static DECLCALLBACK(int) vdScriptHandlerClose(PVDTESTGLOB pGlob, PVDSCRIPTARG pa
}
-static DECLCALLBACK(int) vdScriptHandlerPrintFileSize(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerPrintFileSize(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszDisk = NULL;
PVDDISK pDisk = NULL;
unsigned nImage = 0;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'd':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'i':
- {
- nImage = (unsigned)paScriptArgs[i].u.u64;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ pcszDisk = paScriptArgs[0].psz;
+ nImage = paScriptArgs[1].u32;
pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
if (pDisk)
@@ -1744,31 +1363,16 @@ static DECLCALLBACK(int) vdScriptHandlerPrintFileSize(PVDTESTGLOB pGlob, PVDSCRI
}
-static DECLCALLBACK(int) vdScriptHandlerIoLogReplay(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerIoLogReplay(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszDisk = NULL;
PVDDISK pDisk = NULL;
const char *pcszIoLog = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'd':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'i':
- {
- pcszIoLog = paScriptArgs[i].u.pcszString;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ pcszDisk = paScriptArgs[0].psz;
+ pcszIoLog = paScriptArgs[1].psz;
pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
if (pDisk)
@@ -1911,36 +1515,17 @@ static DECLCALLBACK(int) vdScriptHandlerIoLogReplay(PVDTESTGLOB pGlob, PVDSCRIPT
}
-static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
size_t cbPattern = 0;
uint64_t uSeed = 0;
const char *pcszSeeder = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'd':
- {
- cbPattern = paScriptArgs[i].u.u64;
- break;
- }
- case 's':
- {
- uSeed = paScriptArgs[i].u.u64;
- break;
- }
- case 'm':
- {
- pcszSeeder = paScriptArgs[i].u.pcszString;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ cbPattern = paScriptArgs[0].u64;
+ pcszSeeder = paScriptArgs[1].psz;
+ uSeed = paScriptArgs[2].u64;
if (pGlob->pIoRnd)
{
@@ -1973,8 +1558,10 @@ static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDTESTGLOB pGlob, PVDSCRIPT
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
+
if (pGlob->pIoRnd)
{
VDIoRndDestroy(pGlob->pIoRnd);
@@ -1986,36 +1573,17 @@ static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDTESTGLOB pGlob, PVDSCRIP
return VINF_SUCCESS;
}
-static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromNumber(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromNumber(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
size_t cbPattern = 0;
const char *pcszName = NULL;
uint64_t u64Pattern = 0;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'n':
- {
- pcszName = paScriptArgs[i].u.pcszString;
- break;
- }
- case 's':
- {
- cbPattern = paScriptArgs[i].u.u64;
- break;
- }
- case 'p':
- {
- u64Pattern = paScriptArgs[i].u.u64;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ pcszName = paScriptArgs[0].psz;
+ cbPattern = paScriptArgs[1].u64;
+ u64Pattern = paScriptArgs[2].u64;
PVDPATTERN pPattern = tstVDIoGetPatternByName(pGlob, pcszName);
if (!pPattern)
@@ -2045,30 +1613,15 @@ static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromNumber(PVDTESTGLOB pG
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromFile(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromFile(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszName = NULL;
const char *pcszFile = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'n':
- {
- pcszName = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'f':
- {
- pcszFile = paScriptArgs[i].u.pcszString;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ pcszName = paScriptArgs[0].psz;
+ pcszFile = paScriptArgs[1].psz;
PVDPATTERN pPattern = tstVDIoGetPatternByName(pGlob, pcszName);
if (!pPattern)
@@ -2107,24 +1660,13 @@ static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromFile(PVDTESTGLOB pGlo
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerIoPatternDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerIoPatternDestroy(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszName = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'n':
- {
- pcszName = paScriptArgs[i].u.pcszString;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ pcszName = paScriptArgs[0].psz;
PVDPATTERN pPattern = tstVDIoGetPatternByName(pGlob, pcszName);
if (pPattern)
@@ -2140,53 +1682,24 @@ static DECLCALLBACK(int) vdScriptHandlerIoPatternDestroy(PVDTESTGLOB pGlob, PVDS
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerSleep(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerSleep(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
- uint64_t cMillies = 0;
-
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 't':
- {
- cMillies = paScriptArgs[i].u.u64;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ uint64_t cMillies = paScriptArgs[0].u64;
rc = RTThreadSleep(cMillies);
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszFile = NULL;
const char *pcszPathToDump = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'f':
- {
- pcszFile = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'p':
- {
- pcszPathToDump = paScriptArgs[i].u.pcszString;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ pcszFile = paScriptArgs[0].psz;
+ pcszPathToDump = paScriptArgs[1].psz;
/* Check for the file. */
PVDFILE pIt = NULL;
@@ -2203,7 +1716,8 @@ static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDTESTGLOB pGlob, PVDSCRIPTARG
if (fFound)
{
RTPrintf("Dumping memory file %s to %s, this might take some time\n", pcszFile, pcszPathToDump);
- rc = VDMemDiskWriteToFile(pIt->pMemDisk, pcszPathToDump);
+ //rc = VDMemDiskWriteToFile(pIt->pIo, pcszPathToDump);
+ rc = VERR_NOT_IMPLEMENTED;
}
else
rc = VERR_FILE_NOT_FOUND;
@@ -2211,31 +1725,16 @@ static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDTESTGLOB pGlob, PVDSCRIPTARG
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszDisk = NULL;
PVDDISK pDisk = NULL;
bool fVerify = false;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'n':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'v':
- {
- fVerify = paScriptArgs[i].u.fFlag;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ pcszDisk = paScriptArgs[0].psz;
+ fVerify = paScriptArgs[1].f;
pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
if (pDisk)
@@ -2290,25 +1789,14 @@ static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDTESTGLOB pGlob, PVDSCRIPTA
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszDisk = NULL;
PVDDISK pDisk = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'n':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ pcszDisk = paScriptArgs[0].psz;
pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
if (pDisk)
@@ -2329,32 +1817,17 @@ static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDTESTGLOB pGlob, PVDSCRIPT
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszDisk1 = NULL;
PVDDISK pDisk1 = NULL;
const char *pcszDisk2 = NULL;
PVDDISK pDisk2 = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case '1':
- {
- pcszDisk1 = paScriptArgs[i].u.pcszString;
- break;
- }
- case '2':
- {
- pcszDisk2 = paScriptArgs[i].u.pcszString;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ pcszDisk1 = paScriptArgs[0].psz;
+ pcszDisk2 = paScriptArgs[1].psz;
pDisk1 = tstVDIoGetDiskByName(pGlob, pcszDisk1);
pDisk2 = tstVDIoGetDiskByName(pGlob, pcszDisk2);
@@ -2420,25 +1893,14 @@ static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDTESTGLOB pGlob, PVDSCRIP
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszDisk = NULL;
PVDDISK pDisk = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'd':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ pcszDisk = paScriptArgs[0].psz;
pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
@@ -2450,30 +1912,17 @@ static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDTESTGLOB pGlob, PVDSCRIP
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerPrintMsg(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerPrintMsg(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
- RTPrintf("%s\n", paScriptArgs[0].u.pcszString);
+ RTPrintf("%s\n", paScriptArgs[0].psz);
return VINF_SUCCESS;
}
-static DECLCALLBACK(int) vdScriptHandlerShowStatistics(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerShowStatistics(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
- const char *pcszFile = NULL;
-
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'f':
- {
- pcszFile = paScriptArgs[i].u.pcszString;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
+ const char *pcszFile = paScriptArgs[0].psz;
/* Check for the file. */
PVDFILE pIt = NULL;
@@ -2502,24 +1951,11 @@ static DECLCALLBACK(int) vdScriptHandlerShowStatistics(PVDTESTGLOB pGlob, PVDSCR
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerResetStatistics(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerResetStatistics(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
- const char *pcszFile = NULL;
-
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'f':
- {
- pcszFile = paScriptArgs[i].u.pcszString;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
+ const char *pcszFile = paScriptArgs[0].psz;
/* Check for the file. */
PVDFILE pIt = NULL;
@@ -2549,6 +1985,39 @@ static DECLCALLBACK(int) vdScriptHandlerResetStatistics(PVDTESTGLOB pGlob, PVDSC
return rc;
}
+static DECLCALLBACK(int) vdScriptHandlerResize(PVDSCRIPTARG paScriptArgs, void *pvUser)
+{
+ int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
+ const char *pcszDisk = paScriptArgs[0].psz;
+ uint64_t cbDiskNew = 0;
+ PVDDISK pDisk = NULL;
+
+ pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
+ if (pDisk)
+ {
+ rc = VDResize(pDisk->pVD, cbDiskNew, &pDisk->PhysGeom, &pDisk->LogicalGeom, NULL);
+ }
+ else
+ rc = VERR_NOT_FOUND;
+
+ return rc;
+}
+
+static DECLCALLBACK(int) vdScriptHandlerSetFileBackend(PVDSCRIPTARG paScriptArgs, void *pvUser)
+{
+ int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
+ const char *pcszBackend = paScriptArgs[0].psz;
+
+ RTStrFree(pGlob->pszIoBackend);
+ pGlob->pszIoBackend = RTStrDup(pcszBackend);
+ if (!pGlob->pszIoBackend)
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
static DECLCALLBACK(int) tstVDIoFileOpen(void *pvUser, const char *pszLocation,
uint32_t fOpen,
PFNVDCOMPLETED pfnCompleted,
@@ -2583,7 +2052,7 @@ static DECLCALLBACK(int) tstVDIoFileOpen(void *pvUser, const char *pszLocation,
{
/* If the file exists delete the memory disk. */
if (fFound)
- rc = VDMemDiskSetSize(pIt->pMemDisk, 0);
+ rc = VDIoBackendStorageSetSize(pIt->pIoStorage, 0);
else
{
/* Create completey new. */
@@ -2594,7 +2063,8 @@ static DECLCALLBACK(int) tstVDIoFileOpen(void *pvUser, const char *pszLocation,
if (pIt->pszName)
{
- rc = VDMemDiskCreate(&pIt->pMemDisk, 0);
+ rc = VDIoBackendStorageCreate(pGlob->pIoBackend, pGlob->pszIoBackend,
+ pszLocation, pfnCompleted, &pIt->pIoStorage);
}
else
rc = VERR_NO_MEMORY;
@@ -2605,11 +2075,11 @@ static DECLCALLBACK(int) tstVDIoFileOpen(void *pvUser, const char *pszLocation,
RTStrFree(pIt->pszName);
RTMemFree(pIt);
}
+ else
+ RTListAppend(&pGlob->ListFiles, &pIt->Node);
}
else
rc = VERR_NO_MEMORY;
-
- RTListAppend(&pGlob->ListFiles, &pIt->Node);
}
}
else if ((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN)
@@ -2673,7 +2143,7 @@ static DECLCALLBACK(int) tstVDIoFileDelete(void *pvUser, const char *pcszFilenam
if (fFound)
{
RTListNodeRemove(&pIt->Node);
- VDMemDiskDestroy(pIt->pMemDisk);
+ VDIoBackendStorageDestroy(pIt->pIoStorage);
RTStrFree(pIt->pszName);
RTMemFree(pIt);
}
@@ -2737,14 +2207,14 @@ static DECLCALLBACK(int) tstVDIoFileGetSize(void *pvUser, void *pStorage, uint64
{
PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
- return VDMemDiskGetSize(pIoStorage->pFile->pMemDisk, pcbSize);
+ return VDIoBackendStorageGetSize(pIoStorage->pFile->pIoStorage, pcbSize);
}
static DECLCALLBACK(int) tstVDIoFileSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
{
PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
- return VDMemDiskSetSize(pIoStorage->pFile->pMemDisk, cbSize);
+ return VDIoBackendStorageSetSize(pIoStorage->pFile->pIoStorage, cbSize);
}
static DECLCALLBACK(int) tstVDIoFileWriteSync(void *pvUser, void *pStorage, uint64_t uOffset,
@@ -2759,7 +2229,8 @@ static DECLCALLBACK(int) tstVDIoFileWriteSync(void *pvUser, void *pStorage, uint
Seg.pvSeg = (void *)pvBuffer;
Seg.cbSeg = cbBuffer;
RTSgBufInit(&SgBuf, &Seg, 1);
- rc = VDMemDiskWrite(pIoStorage->pFile->pMemDisk, uOffset, cbBuffer, &SgBuf);
+ rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_WRITE, uOffset,
+ cbBuffer, &SgBuf, NULL, true /* fSync */);
if (RT_SUCCESS(rc))
{
pIoStorage->pFile->cWrites++;
@@ -2782,7 +2253,8 @@ static DECLCALLBACK(int) tstVDIoFileReadSync(void *pvUser, void *pStorage, uint6
Seg.pvSeg = pvBuffer;
Seg.cbSeg = cbBuffer;
RTSgBufInit(&SgBuf, &Seg, 1);
- rc = VDMemDiskRead(pIoStorage->pFile->pMemDisk, uOffset, cbBuffer, &SgBuf);
+ rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_READ, uOffset,
+ cbBuffer, &SgBuf, NULL, true /* fSync */);
if (RT_SUCCESS(rc))
{
pIoStorage->pFile->cReads++;
@@ -2796,8 +2268,10 @@ static DECLCALLBACK(int) tstVDIoFileReadSync(void *pvUser, void *pStorage, uint6
static DECLCALLBACK(int) tstVDIoFileFlushSync(void *pvUser, void *pStorage)
{
PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
+ int rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_FLUSH, 0,
+ 0, NULL, NULL, true /* fSync */);
pIoStorage->pFile->cFlushes++;
- return VINF_SUCCESS;
+ return rc;
}
static DECLCALLBACK(int) tstVDIoFileReadAsync(void *pvUser, void *pStorage, uint64_t uOffset,
@@ -2808,9 +2282,11 @@ static DECLCALLBACK(int) tstVDIoFileReadAsync(void *pvUser, void *pStorage, uint
int rc = VINF_SUCCESS;
PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
+ RTSGBUF SgBuf;
- rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pIoStorage->pFile->pMemDisk, VDIOTXDIR_READ, uOffset,
- cbRead, paSegments, cSegments, pIoStorage->pfnComplete, pvCompletion);
+ RTSgBufInit(&SgBuf, paSegments, cSegments);
+ rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_READ, uOffset,
+ cbRead, &SgBuf, pvCompletion, false /* fSync */);
if (RT_SUCCESS(rc))
{
pIoStorage->pFile->cAsyncReads++;
@@ -2828,9 +2304,11 @@ static DECLCALLBACK(int) tstVDIoFileWriteAsync(void *pvUser, void *pStorage, uin
int rc = VINF_SUCCESS;
PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
+ RTSGBUF SgBuf;
- rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pIoStorage->pFile->pMemDisk, VDIOTXDIR_WRITE, uOffset,
- cbWrite, paSegments, cSegments, pIoStorage->pfnComplete, pvCompletion);
+ RTSgBufInit(&SgBuf, paSegments, cSegments);
+ rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_WRITE, uOffset,
+ cbWrite, &SgBuf, pvCompletion, false /* fSync */);
if (RT_SUCCESS(rc))
{
pIoStorage->pFile->cAsyncWrites++;
@@ -2847,8 +2325,8 @@ static DECLCALLBACK(int) tstVDIoFileFlushAsync(void *pvUser, void *pStorage, voi
PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
- rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pIoStorage->pFile->pMemDisk, VDIOTXDIR_FLUSH, 0,
- 0, NULL, 0, pIoStorage->pfnComplete, pvCompletion);
+ rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_FLUSH, 0,
+ 0, NULL, pvCompletion, false /* fSync */);
if (RT_SUCCESS(rc))
{
pIoStorage->pFile->cAsyncFlushes++;
@@ -3169,454 +2647,7 @@ static int tstVDIoPatternGetBuffer(PVDPATTERN pPattern, void **ppv, size_t cb)
}
/**
- * Skips the characters until the given character is reached.
- *
- * @returns Start of the string with the given character
- * or NULL if the string ended before.
- *
- * @param psz The string to skip.
- * @param ch The character.
- */
-static char *tstVDIoScriptSkipUntil(char *psz, char ch)
-{
- while ( *psz != '\0'
- && *psz != ch)
- psz++;
-
- return psz;
-}
-
-/**
- * Skips the spaces of the current string.
- *
- * @returns Start of the string with a non space character
- * or NULL if the string ended before.
- *
- * @param psz The string to skip.
- */
-static char *tstVDIoScriptSkipSpace(char *psz)
-{
- while ( *psz != '\0'
- && RT_C_IS_SPACE(*psz))
- psz++;
-
- return psz;
-}
-
-/**
- * Skips all characters until a space is reached of the current
- * string.
- *
- * @returns Start of the string with a space character
- * or NULL if the string ended before.
- *
- * @param psz The string to skip.
- */
-static char *tstVDIoScriptSkipNonSpace(char *psz)
-{
- while ( *psz != '\0'
- && !RT_C_IS_SPACE(*psz))
- psz++;
-
- return psz;
-}
-
-/**
- * Returns true if the first character of the given string
- * contains a character marking a line end (comment or \0
- * terminator).
- *
- * @returns true if the line contains no more characters of
- * interest and false otherwise.
- *
- * @param psz The string to check for.
- */
-static bool tstVDIoIsLineEnd(const char *psz)
-{
- return *psz == '\0' || *psz == '#';
-}
-
-/**
- * Parses one argument name, value pair.
- *
- * @returns IPRT status code.
- *
- * @param pVDScriptAction Script action.
- * @param pcszName Argument name.
- * @param pcszValue Argument value.
- * @param pScriptArg Where to fill in the parsed
- * argument.
- * @param pfMandatory Where to store whether the argument
- * is mandatory.
- */
-static int tstVDIoScriptArgumentParse(PCVDSCRIPTACTION pVDScriptAction, const char *pcszName,
- const char *pcszValue, PVDSCRIPTARG pScriptArg, bool *pfMandatory)
-{
- int rc = VERR_NOT_FOUND;
-
- for (unsigned i = 0; i < pVDScriptAction->cArgDescs; i++)
- {
- if (!RTStrCmp(pVDScriptAction->paArgDesc[i].pcszName, pcszName))
- {
- rc = VINF_SUCCESS;
-
- switch (pVDScriptAction->paArgDesc[i].enmType)
- {
- case VDSCRIPTARGTYPE_BOOL:
- {
- pScriptArg->enmType = VDSCRIPTARGTYPE_BOOL;
- if (!RTStrICmp(pcszValue, "yes") || !RTStrICmp(pcszValue, "on"))
- pScriptArg->u.fFlag = true;
- else if (!RTStrICmp(pcszValue, "no") || !RTStrICmp(pcszValue, "off"))
- pScriptArg->u.fFlag = false;
- else
- {
- RTPrintf("Boolean argument malformed '%s'\n", pcszValue);
- rc = VERR_INVALID_PARAMETER;
- }
- break;
- }
- case VDSCRIPTARGTYPE_SIGNED_NUMBER:
- {
- pScriptArg->enmType = VDSCRIPTARGTYPE_SIGNED_NUMBER;
- AssertMsgFailed(("todo\n"));
- break;
- }
- case VDSCRIPTARGTYPE_STRING:
- {
- pScriptArg->enmType = VDSCRIPTARGTYPE_STRING;
- pScriptArg->u.pcszString = pcszValue;
- break;
- }
- case VDSCRIPTARGTYPE_UNSIGNED_NUMBER:
- {
- char *pszSuffix = NULL;
-
- pScriptArg->enmType = VDSCRIPTARGTYPE_UNSIGNED_NUMBER;
- rc = RTStrToUInt64Ex(pcszValue, &pszSuffix, 10, &pScriptArg->u.u64);
- if (rc == VWRN_TRAILING_CHARS)
- {
- switch (*pszSuffix)
- {
- case 'k':
- case 'K':
- {
- pScriptArg->u.u64 *= _1K;
- break;
- }
- case 'm':
- case 'M':
- {
- pScriptArg->u.u64 *= _1M;
- break;
- }
- case 'g':
- case 'G':
- {
- pScriptArg->u.u64 *= _1G;
- break;
- }
- default:
- {
- RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
- rc = VERR_INVALID_PARAMETER;
- }
- }
- if (rc != VERR_INVALID_PARAMETER)
- rc = VINF_SUCCESS;
- }
-
- break;
- }
- case VDSCRIPTARGTYPE_UNSIGNED_RANGE:
- {
- char *pszSuffix = NULL;
-
- pScriptArg->enmType = VDSCRIPTARGTYPE_UNSIGNED_RANGE;
- rc = RTStrToUInt64Ex(pcszValue, &pszSuffix, 10, &pScriptArg->u.Range.Start);
- if (rc == VWRN_TRAILING_CHARS)
- {
- if (*pszSuffix != '-')
- {
- switch (*pszSuffix)
- {
- case 'k':
- case 'K':
- {
- pScriptArg->u.u64 *= _1K;
- break;
- }
- case 'm':
- case 'M':
- {
- pScriptArg->u.u64 *= _1M;
- break;
- }
- case 'g':
- case 'G':
- {
- pScriptArg->u.u64 *= _1G;
- break;
- }
- default:
- {
- RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
- rc = VERR_INVALID_PARAMETER;
- }
- }
- if (RT_SUCCESS(rc))
- pszSuffix++;
- }
-
- if (*pszSuffix == '-')
- {
- pszSuffix++;
- rc = RTStrToUInt64Ex(pszSuffix, &pszSuffix, 10, &pScriptArg->u.Range.End);
- if (rc == VWRN_TRAILING_CHARS)
- {
- switch (*pszSuffix)
- {
- case 'k':
- case 'K':
- {
- pScriptArg->u.Range.End *= _1K;
- break;
- }
- case 'm':
- case 'M':
- {
- pScriptArg->u.Range.End *= _1M;
- break;
- }
- case 'g':
- case 'G':
- {
- pScriptArg->u.Range.End *= _1G;
- break;
- }
- default:
- {
- RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
- rc = VERR_INVALID_PARAMETER;
- }
- }
- }
- }
- else
- rc = VERR_INVALID_PARAMETER;
- }
- else
- rc = VERR_INVALID_PARAMETER;
-
- if (rc == VERR_INVALID_PARAMETER)
- RTPrintf("Invalid range format\n");
- break;
- }
- default:
- AssertMsgFailed(("Invalid script argument type\n"));
- }
-
- if (RT_SUCCESS(rc))
- {
- pScriptArg->chId = pVDScriptAction->paArgDesc[i].chId;
- *pfMandatory = !!(pVDScriptAction->paArgDesc[i].fFlags & VDSCRIPTARGDESC_FLAG_MANDATORY);
- }
- break;
- }
- }
-
- if (rc == VERR_NOT_FOUND)
- RTPrintf("Argument '%s' not found\n", pcszName);
-
- return rc;
-}
-
-/**
- * Parses the arguments of a action in the script.
- *
- * @returns IPRT status code.
- *
- * @param psz Argument string.
- * @param pVDScriptAction The script action to parses
- * arguments for.
- * @param paScriptArgs Where to store the arguments.
- * @param pcScriptArgs Where to store the actual number of
- * arguments parsed.
- */
-static int tstVDIoScriptArgumentListParse(char *psz, PCVDSCRIPTACTION pVDScriptAction, PVDSCRIPTARG paScriptArgs, unsigned *pcScriptArgs)
-{
- int rc = VINF_SUCCESS;
- unsigned cMandatoryArgsReq = 0;
- unsigned cScriptArgs = 0;
-
- /* Count the number of mandatory arguments first. */
- for (unsigned i = 0; i < pVDScriptAction->cArgDescs; i++)
- if (pVDScriptAction->paArgDesc[i].fFlags & VDSCRIPTARGDESC_FLAG_MANDATORY)
- cMandatoryArgsReq++;
-
- /* One argument is given in the form name=value. */
- *pcScriptArgs = 0;
-
- while ( psz
- && !tstVDIoIsLineEnd(psz))
- {
- const char *pcszName = psz;
-
- psz = tstVDIoScriptSkipUntil(psz, '=');
- if (!tstVDIoIsLineEnd(psz))
- {
- *psz = '\0'; /* Overwrite */
- psz++;
- const char *pcszValue = psz;
-
- psz = tstVDIoScriptSkipNonSpace(psz);
- if (!tstVDIoIsLineEnd(psz))
- {
- *psz = '\0'; /* Overwrite */
- psz++;
- psz = tstVDIoScriptSkipSpace(psz);
- }
-
- pcszValue = tstVDIoScriptSkipSpace((char *)pcszValue);
- if (*pcszValue == '\0')
- {
- RTPrintf("Value missing for argument '%s'\n", pcszName);
- rc = VERR_INVALID_STATE;
- break;
- }
-
- /* We have the name and value pair now. */
- bool fMandatory = false; /* Shut up gcc */
- rc = tstVDIoScriptArgumentParse(pVDScriptAction, pcszName, pcszValue, &paScriptArgs[cScriptArgs], &fMandatory);
- if (RT_SUCCESS(rc))
- {
- if (fMandatory)
- cMandatoryArgsReq--;
- cScriptArgs++;
- }
- }
- else
- {
- RTPrintf("Argument in invalid form\n");
- rc = VERR_INVALID_STATE;
- break;
- }
- }
-
- if ( RT_SUCCESS(rc)
- && cMandatoryArgsReq)
- {
- /* No arguments anymore but there are still mandatory arguments left. */
- RTPrintf("There are %u arguments missing for script action '%s\n", pVDScriptAction->pcszAction);
- rc = VERR_INVALID_STATE;
- }
-
- if (RT_SUCCESS(rc))
- *pcScriptArgs = cScriptArgs;
-
- return rc;
-}
-
-/**
- * Executes the script pointed to by the given stream.
- *
- * @returns IPRT status code.
- *
- * @param pStrm The stream handle of the script.
- * @param pGlob Global test data.
- */
-static int tstVDIoScriptExecute(PRTSTREAM pStrm, PVDTESTGLOB pGlob)
-{
- int rc = VINF_SUCCESS;
- char abBuffer[0x1000]; /* Current assumption that a line is never longer than 4096 bytes. */
- PVDSCRIPTARG paScriptArgs = NULL;
- unsigned cScriptArgsMax = 0;
-
- do
- {
- memset(abBuffer, 0, sizeof(abBuffer));
- rc = RTStrmGetLine(pStrm, abBuffer, sizeof(abBuffer));
- if (RT_SUCCESS(rc))
- {
- const char *pcszAction = NULL;
- char *psz = abBuffer;
-
- /* Skip space */
- psz = tstVDIoScriptSkipSpace(psz);
- if (!tstVDIoIsLineEnd(psz))
- {
- PCVDSCRIPTACTION pVDScriptAction = NULL;
-
- /* Get the action name. */
- pcszAction = psz;
-
- psz = tstVDIoScriptSkipNonSpace(psz);
- if (!tstVDIoIsLineEnd(psz))
- {
- Assert(RT_C_IS_SPACE(*psz));
- *psz++ = '\0';
- }
-
- /* Find the action. */
- for (unsigned i = 0; i < g_cScriptActions; i++)
- {
- if (!RTStrCmp(pcszAction, g_aScriptActions[i].pcszAction))
- {
- pVDScriptAction = &g_aScriptActions[i];
- break;
- }
- }
-
- if (pVDScriptAction)
- {
- /* Parse arguments. */
- if (cScriptArgsMax < pVDScriptAction->cArgDescs)
- {
- /* Increase arguments array. */
- if (paScriptArgs)
- RTMemFree(paScriptArgs);
-
- cScriptArgsMax = pVDScriptAction->cArgDescs;
- paScriptArgs = (PVDSCRIPTARG)RTMemAllocZ(cScriptArgsMax * sizeof(VDSCRIPTARG));
- }
-
- if (paScriptArgs)
- {
- unsigned cScriptArgs;
-
- rc = tstVDIoScriptArgumentListParse(psz, pVDScriptAction, paScriptArgs, &cScriptArgs);
- if (RT_SUCCESS(rc))
- {
- /* Execute the handler. */
- rc = pVDScriptAction->pfnHandler(pGlob, paScriptArgs, cScriptArgs);
- }
- }
- else
- {
- RTPrintf("Out of memory while allocating argument array for script action %s\n", pcszAction);
- rc = VERR_NO_MEMORY;
- }
- }
- else
- {
- RTPrintf("Script action %s is not known\n", pcszAction);
- rc = VERR_NOT_FOUND;
- }
- }
- /* else empty line, just continue */
- }
- } while(RT_SUCCESS(rc));
-
- if (rc == VERR_EOF)
- {
- RTPrintf("Successfully executed I/O script\n");
- rc = VINF_SUCCESS;
- }
- return rc;
-}
-
-/**
- * Executes the given I/O script.
+ * Executes the given I/O script using the new scripting engine.
*
* @returns nothing.
*
@@ -3625,17 +2656,28 @@ static int tstVDIoScriptExecute(PRTSTREAM pStrm, PVDTESTGLOB pGlob)
static void tstVDIoScriptRun(const char *pcszFilename)
{
int rc = VINF_SUCCESS;
- PRTSTREAM pScriptStrm; /**< Stream of the script file. */
VDTESTGLOB GlobTest; /**< Global test data. */
+ void *pvFile = NULL;
+ size_t cbFile = 0;
memset(&GlobTest, 0, sizeof(VDTESTGLOB));
RTListInit(&GlobTest.ListFiles);
RTListInit(&GlobTest.ListDisks);
RTListInit(&GlobTest.ListPatterns);
+ GlobTest.pszIoBackend = RTStrDup("memory");
+ if (!GlobTest.pszIoBackend)
+ {
+ RTPrintf("Out of memory allocating I/O backend string\n");
+ return;
+ }
- rc = RTStrmOpen(pcszFilename, "r", &pScriptStrm);
+ rc = RTFileReadAll(pcszFilename, &pvFile, &cbFile);
if (RT_SUCCESS(rc))
{
+ char *pszScript = RTStrDupN((char *)pvFile, cbFile);
+ RTFileReadAllFree(pvFile, cbFile);
+
+ AssertPtr(pszScript);
/* Init global test data. */
GlobTest.VDIfError.pfnError = tstVDError;
GlobTest.VDIfError.pfnMessage = tstVDMessage;
@@ -3664,24 +2706,34 @@ static void tstVDIoScriptRun(const char *pcszFilename)
AssertRC(rc);
/* Init I/O backend. */
- rc = VDIoBackendMemCreate(&GlobTest.pIoBackend);
+ rc = VDIoBackendCreate(&GlobTest.pIoBackend);
if (RT_SUCCESS(rc))
{
- /* Execute the script. */
- rc = tstVDIoScriptExecute(pScriptStrm, &GlobTest);
- if (RT_FAILURE(rc))
+ VDSCRIPTCTX hScriptCtx = NULL;
+ rc = VDScriptCtxCreate(&hScriptCtx);
+ if (RT_SUCCESS(rc))
{
- RTPrintf("Executing the script stream failed rc=%Rrc\n", rc);
+ rc = VDScriptCtxCallbacksRegister(hScriptCtx, g_aScriptActions, g_cScriptActions, &GlobTest);
+ AssertRC(rc);
+
+ rc = VDScriptCtxLoadScript(hScriptCtx, pszScript);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("Loading the script failed rc=%Rrc\n", rc);
+ }
+ else
+ rc = VDScriptCtxCallFn(hScriptCtx, "main", NULL, 0);
+ VDScriptCtxDestroy(hScriptCtx);
}
- VDIoBackendMemDestroy(GlobTest.pIoBackend);
+ VDIoBackendDestroy(GlobTest.pIoBackend);
}
else
RTPrintf("Creating the I/O backend failed rc=%Rrc\n");
-
- RTStrmClose(pScriptStrm);
}
else
RTPrintf("Opening script failed rc=%Rrc\n", rc);
+
+ RTStrFree(GlobTest.pszIoBackend);
}
/**
diff --git a/src/VBox/Storage/testcase/tstVDIo.vd b/src/VBox/Storage/testcase/tstVDIo.vd
index b808ffa3..0074be68 100644
--- a/src/VBox/Storage/testcase/tstVDIo.vd
+++ b/src/VBox/Storage/testcase/tstVDIo.vd
@@ -1,108 +1,53 @@
-# $Id: tstVDIo.vd $
-#
-# Storage: Simple I/O testing for most backends.
-#
-
-#
-# Copyright (C) 2011 Oracle Corporation
-#
-# This file is part of VirtualBox Open Source Edition (OSE), as
-# available from http://www.virtualbox.org. This file is free software;
-# you can redistribute it and/or modify it under the terms of the GNU
-# General Public License (GPL) as published by the Free Software
-# Foundation, in version 2 as it comes in the "COPYING" file of the
-# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
-# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
-#
-
-# Init I/O RNG for generating random data for writes
-iorngcreate size=10M mode=manual seed=1234567890
-
-# VMDK disk
-print msg=Testing_VMDK
-createdisk name=test verify=yes
-create disk=test mode=base name=tstShared.vmdk type=dynamic backend=VMDK size=200M
-io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=100
-io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=0
-create disk=test mode=diff name=tstShared2.vmdk type=dynamic backend=VMDK size=200M
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50
-create disk=test mode=diff name=tstShared3.vmdk type=dynamic backend=VMDK size=200M
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50
-close disk=test mode=single delete=yes
-close disk=test mode=single delete=yes
-close disk=test mode=single delete=yes
-destroydisk name=test
-
-# VDI disk
-print msg=Testing_VDI
-createdisk name=test verify=yes
-create disk=test mode=base name=tstShared.vdi type=dynamic backend=VDI size=200M
-io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=100
-io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=0
-create disk=test mode=diff name=tstShared2.vdi type=dynamic backend=VDI size=200M
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50
-create disk=test mode=diff name=tstShared3.vdi type=dynamic backend=VDI size=200M
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50
-close disk=test mode=single delete=yes
-close disk=test mode=single delete=yes
-close disk=test mode=single delete=yes
-destroydisk name=test
-
-# VHD disk
-print msg=Testing_VHD
-createdisk name=test verify=yes
-create disk=test mode=base name=tstShared.vhd type=dynamic backend=VHD size=200M
-io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=100
-io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=0
-create disk=test mode=diff name=tstShared2.vhd type=dynamic backend=VHD size=200M
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50
-create disk=test mode=diff name=tstShared3.vhd type=dynamic backend=VHD size=200M
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50
-close disk=test mode=single delete=yes
-close disk=test mode=single delete=yes
-close disk=test mode=single delete=yes
-destroydisk name=test
-
-# Parallels disk
-print msg=Testing_Parallels
-createdisk name=test verify=yes
-create disk=test mode=base name=tstShared.hdd type=dynamic backend=Parallels size=200M
-io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=100
-io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=0
-create disk=test mode=diff name=tstShared2.hdd type=dynamic backend=Parallels size=200M
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50
-create disk=test mode=diff name=tstShared3.hdd type=dynamic backend=Parallels size=200M
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50
-close disk=test mode=single delete=yes
-close disk=test mode=single delete=yes
-close disk=test mode=single delete=yes
-destroydisk name=test
-
-# QED disk
-print msg=Testing_QED
-createdisk name=test verify=yes
-create disk=test mode=base name=tstShared.qed type=dynamic backend=QED size=200M
-io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=100
-io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=0
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=0
-close disk=test mode=single delete=no
-open disk=test name=tstShared.qed backend=QED async=yes
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=0
-destroydisk name=test
-
-# QCOW disk
-print msg=Testing_QCOW
-createdisk name=test verify=yes
-create disk=test mode=base name=tstShared.qed type=dynamic backend=QCOW size=200M
-io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=100
-io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=0
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=0
-close disk=test mode=single delete=no
-open disk=test name=tstShared.qed backend=QCOW async=yes
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=0
-destroydisk name=test
-
-iorngdestroy
+/* $Id: tstVDIo.vd $ */
+/**
+ * Storage: Simple I/O testing for most backends.
+ */
+
+/*
+ * Copyright (C) 2011-2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+void tstIo(string strMessage, string strBackend)
+{
+ print(strMessage);
+ createdisk("test", true /* fVerify */);
+ create("test", "base", "tst.disk", "dynamic", strBackend, 200M, false /* fIgnoreFlush */);
+ io("test", true, 32, "seq", 64K, 0, 200M, 200M, 100, "none");
+ io("test", false, 1, "seq", 64K, 0, 200M, 200M, 100, "none");
+ io("test", true, 32, "seq", 64K, 0, 200M, 200M, 0, "none");
+ io("test", false, 1, "seq", 64K, 0, 200M, 200M, 0, "none");
+ create("test", "diff", "tst2.disk", "dynamic", strBackend, 200M, false /* fIgnoreFlush */);
+ io("test", true, 32, "rnd", 64K, 0, 200M, 200M, 50, "none");
+ io("test", false, 1, "rnd", 64K, 0, 200M, 200M, 50, "none");
+ create("test", "diff", "tst3.disk", "dynamic", strBackend, 200M, false /* fIgnoreFlush */);
+ io("test", true, 32, "rnd", 64K, 0, 200M, 200M, 50, "none");
+ io("test", false, 1, "rnd", 64K, 0, 200M, 200M, 50, "none");
+ close("test", "single", true /* fDelete */);
+ close("test", "single", true /* fDelete */);
+ close("test", "single", true /* fDelete */);
+ destroydisk("test");
+}
+
+void main()
+{
+ /* Init I/O RNG for generating random data for writes */
+ iorngcreate(10M, "manual", 1234567890);
+
+ tstIo("Testing VDI", "VDI");
+ tstIo("Testing VMDK", "VMDK");
+ tstIo("Testing VHD", "VHD");
+ tstIo("Testing Parallels", "Parallels");
+ tstIo("Testing QED", "QED");
+ tstIo("Testing QCOW", "QCOW");
+
+ iorngdestroy();
+}
diff --git a/src/VBox/Storage/testcase/tstVDResize.vd b/src/VBox/Storage/testcase/tstVDResize.vd
new file mode 100644
index 00000000..0a3dcf40
--- /dev/null
+++ b/src/VBox/Storage/testcase/tstVDResize.vd
@@ -0,0 +1,32 @@
+/* $Id: tstVDResize.vd $ */
+/**
+ * Storage: Resize testing for VDI.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+void main()
+{
+ /* Init I/O RNG for generating random data for writes. */
+ iorngcreate(10M, "manual", 1234567890);
+
+ print("Testing VDI");
+ createdisk("test", true);
+ create("test", "base", "tst.vdi", "dynamic", "VDI", 1T, false);
+ io("test", false, 1, "seq", 64K, 255G, 257G, 2G, 100, "none");
+ resize("test", 1331200M);
+ io("test", false, 1, "seq", 64K, 255G, 257G, 2G, 0, "none");
+ destroydisk("test");
+
+ iorngdestroy();
+}
diff --git a/src/VBox/Storage/testcase/tstVDShareable.cpp b/src/VBox/Storage/testcase/tstVDShareable.cpp
index 98957fb7..834f6a96 100644
--- a/src/VBox/Storage/testcase/tstVDShareable.cpp
+++ b/src/VBox/Storage/testcase/tstVDShareable.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2010 Oracle Corporation
+ * Copyright (C) 2010-2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
diff --git a/src/VBox/Storage/testcase/tstVDShareable.vd b/src/VBox/Storage/testcase/tstVDShareable.vd
index b2f5cd0e..7cbe4e2b 100644
--- a/src/VBox/Storage/testcase/tstVDShareable.vd
+++ b/src/VBox/Storage/testcase/tstVDShareable.vd
@@ -1,53 +1,55 @@
-# $Id: tstVDShareable.vd $
-#
-# Storage: Testcase for shareable disks.
-#
-
-#
-# Copyright (C) 2011 Oracle Corporation
-#
-# This file is part of VirtualBox Open Source Edition (OSE), as
-# available from http://www.virtualbox.org. This file is free software;
-# you can redistribute it and/or modify it under the terms of the GNU
-# General Public License (GPL) as published by the Free Software
-# Foundation, in version 2 as it comes in the "COPYING" file of the
-# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
-# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
-#
-
-# Init I/O RNG for generating random data for writes
-iorngcreate size=10M mode=manual seed=1234567890
-
-# Create disk containers.
-createdisk name=shared1
-createdisk name=shared2
-
-# Create the disk and close it.
-create disk=shared1 mode=base name=tstShared.vdi type=fixed backend=VDI size=20M
-close disk=shared1 mode=all delete=no
-
-# Open the disk with sharing enabled.
-open disk=shared1 name=tstShared.vdi backend=VDI shareable=yes
-open disk=shared2 name=tstShared.vdi backend=VDI shareable=yes
-
-# Write to one disk and verify that the other disk can see the content.
-io disk=shared1 async=yes max-reqs=32 mode=seq blocksize=64k off=0-20M size=20M writes=100
-comparedisks disk1=shared1 disk2=shared2
-
-# Write to the second disk and verify that the first can see the content.
-io disk=shared2 async=yes max-reqs=64 mode=seq blocksize=8k off=0-20M size=20M writes=50
-comparedisks disk1=shared1 disk2=shared2
-
-# Close but don't delete yet.
-close disk=shared1 mode=all delete=no
-close disk=shared2 mode=all delete=no
-
-# Open and delete
-open disk=shared1 name=tstShared.vdi backend=VDI shareable=no
-close disk=shared1 mode=single delete=yes
-
-# Cleanup
-destroydisk name=shared1
-destroydisk name=shared2
-iorngdestroy
-
+/* $Id: tstVDShareable.vd $ */
+/**
+ * Storage: Testcase for shareable disks.
+ */
+
+/*
+ * Copyright (C) 2011 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+void main()
+{
+ /* Init I/O RNG for generating random data for writes. */
+ iorngcreate(10M, "manual", 1234567890);
+
+ /* Create disk containers. */
+ createdisk("shared1", false);
+ createdisk("shared2", false);
+
+ /* Create the disk and close it. */
+ create("shared1", "base", "tstShared.vdi", "fixed", "VDI", 20M, false);
+ close("shared1", "all", false);
+
+ /* Open the disk with sharing enabled. */
+ open("shared1", "tstShared.vdi", "VDI", true /* fAsync */, true /* fShareable */, false, false, false);
+ open("shared2", "tstShared.vdi", "VDI", true /* fAsync */, true /* fShareable */, false, false, false);
+
+ /* Write to one disk and verify that the other disk can see the content. */
+ io("shared1", true, 32, "seq", 64K, 0, 20M, 20M, 100, "none");
+ comparedisks("shared1", "shared2");
+
+ /* Write to the second disk and verify that the first can see the content. */
+ io("shared2", true, 64, "seq", 8K, 0, 20M, 20M, 50, "none");
+ comparedisks("shared1", "shared2");
+
+ /* Close but don't delete yet. */
+ close("shared1", "all", false);
+ close("shared2", "all", false);
+
+ /* Open and delete. */
+ open("shared1", "tstShared.vdi", "VDI", false /* fAsync */, false /* fShareable */, false, false, false);
+ close("shared1", "single", true);
+
+ /* Cleanup */
+ destroydisk("shared1");
+ destroydisk("shared2");
+ iorngdestroy();
+}
diff --git a/src/VBox/Storage/testcase/tstVDSnap.cpp b/src/VBox/Storage/testcase/tstVDSnap.cpp
index 83313e1f..d32d3b55 100644
--- a/src/VBox/Storage/testcase/tstVDSnap.cpp
+++ b/src/VBox/Storage/testcase/tstVDSnap.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2010 Oracle Corporation
+ * Copyright (C) 2010-2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -229,6 +229,21 @@ static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest)
unsigned cDiffs = 0;
unsigned idDiff = 0; /* Diff ID counter for the filename */
+ /* Delete all images from a previous run. */
+ RTFileDelete(pTest->pcszBaseImage);
+ for (unsigned i = 0; i < pTest->cIterations; i++)
+ {
+ char *pszDiffFilename = NULL;
+
+ rc = RTStrAPrintf(&pszDiffFilename, "tstVDSnapDiff%u.%s", i, pTest->pcszDiffSuff);
+ if (RT_SUCCESS(rc))
+ {
+ if (RTFileExists(pszDiffFilename))
+ RTFileDelete(pszDiffFilename);
+ RTStrFree(pszDiffFilename);
+ }
+ }
+
/* Create the virtual disk test data */
pbTestPattern = (uint8_t *)RTMemAlloc(pTest->cbTestPattern);
@@ -260,10 +275,22 @@ static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest)
if (pbTestPattern) \
RTMemFree(pbTestPattern); \
VDDestroy(pVD); \
+ g_cErrors++; \
return rc; \
} \
} while (0)
+#define CHECK_BREAK(str) \
+ do \
+ { \
+ RTPrintf("%s rc=%Rrc\n", str, rc); \
+ if (RT_FAILURE(rc)) \
+ { \
+ g_cErrors++; \
+ break; \
+ } \
+ } while (0)
+
/* Create error interface. */
/* Create error interface. */
VDIfError.pfnError = tstVDError;
@@ -292,7 +319,7 @@ static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest)
{
/* Write */
rc = tstVDSnapWrite(pVD, paDiskSeg, cDiskSegments, cbDisk, fInit);
- CHECK("tstVDSnapWrite()");
+ CHECK_BREAK("tstVDSnapWrite()");
fInit = false;
@@ -313,7 +340,7 @@ static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest)
rc = VDCreateDiff(pVD, pTest->pcszBackend, pszDiffFilename,
VD_IMAGE_FLAGS_NONE, "Test diff image", NULL, NULL,
VD_OPEN_FLAGS_NORMAL, NULL, NULL);
- CHECK("VDCreateDiff()");
+ CHECK_BREAK("VDCreateDiff()");
RTStrFree(pszDiffFilename);
VDDumpImages(pVD);
@@ -333,7 +360,7 @@ static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest)
rc = VDMerge(pVD, uStartMerge, uEndMerge, NULL);
else
rc = VDMerge(pVD, uEndMerge, uStartMerge, NULL);
- CHECK("VDMerge()");
+ CHECK_BREAK("VDMerge()");
cDiffs -= uEndMerge - uStartMerge;
@@ -351,7 +378,7 @@ static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest)
/* Now compare the result with our test pattern */
rc = tstVDSnapReadVerify(pVD, paDiskSeg, cDiskSegments, cbDisk);
- CHECK("tstVDSnapReadVerify()");
+ CHECK_BREAK("tstVDSnapReadVerify()");
}
cIteration++;
}
@@ -372,7 +399,7 @@ static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest)
RTStrFree(pszDiffFilename);
}
#undef CHECK
- return 0;
+ return rc;
}
int main(int argc, char *argv[])
diff --git a/src/VBox/Storage/testcase/vbox-img.cpp b/src/VBox/Storage/testcase/vbox-img.cpp
index 8fb9e0bf..7d3f9a48 100644
--- a/src/VBox/Storage/testcase/vbox-img.cpp
+++ b/src/VBox/Storage/testcase/vbox-img.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2010-2011 Oracle Corporation
+ * Copyright (C) 2010-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -64,10 +64,13 @@ static void printUsage(PRTSTREAM pStrm)
" --size <size in bytes>\n"
" [--format VDI|VMDK|VHD] (default: VDI)\n"
" [--variant Standard,Fixed,Split2G,Stream,ESX]\n"
+ " [--dataalignment <alignment in bytes>]\n"
"\n"
" repair --filename <filename>\n"
" [--dry-run]\n"
- " [--format VDI|VMDK|VHD] (default: autodetect)\n",
+ " [--format VDI|VMDK|VHD] (default: autodetect)\n"
+ "\n"
+ " clearcomment --filename <filename>\n",
g_pszProgName);
}
@@ -260,8 +263,8 @@ int handleSetUUID(HandlerArg *a)
if (RT_FAILURE(rc))
return errorRuntime("Cannot create the virtual disk container: %Rrc\n", rc);
-
- rc = VDOpen(pVD, pszFormat, pszFilename, VD_OPEN_FLAGS_NORMAL, NULL);
+ /* Open in info mode to be able to open diff images without their parent. */
+ rc = VDOpen(pVD, pszFormat, pszFilename, VD_OPEN_FLAGS_INFO, NULL);
if (RT_FAILURE(rc))
return errorRuntime("Cannot open the virtual disk image \"%s\": %Rrc\n",
pszFilename, rc);
@@ -430,21 +433,22 @@ static int convInRead(void *pvUser, void *pStorage, uint64_t uOffset,
if (pFS->offBuffer == UINT64_MAX)
{
/* Repeat reading until buffer is full or EOF. */
- size_t cbSumRead = 0, cbRead;
- uint8_t *pTmp = (uint8_t *)&pFS->abBuffer[0];
+ size_t cbRead;
+ size_t cbSumRead = 0;
+ uint8_t *pbTmp = (uint8_t *)&pFS->abBuffer[0];
size_t cbTmp = sizeof(pFS->abBuffer);
do
{
- rc = RTFileRead(pFS->file, pTmp, cbTmp, &cbRead);
+ rc = RTFileRead(pFS->file, pbTmp, cbTmp, &cbRead);
if (RT_FAILURE(rc))
return rc;
- pTmp += cbRead;
+ pbTmp += cbRead;
cbTmp -= cbRead;
cbSumRead += cbRead;
} while (cbTmp && cbRead);
pFS->offBuffer = 0;
- pFS->cbBuffer = cbSumRead;
+ pFS->cbBuffer = (uint32_t)cbSumRead;
if (!cbSumRead && !pcbRead) /* Caller can't handle partial reads. */
return VERR_EOF;
}
@@ -464,25 +468,26 @@ static int convInRead(void *pvUser, void *pStorage, uint64_t uOffset,
}
/* Repeat reading until buffer is full or EOF. */
- size_t cbSumRead = 0, cbRead;
- uint8_t *pTmp = (uint8_t *)&pFS->abBuffer[0];
+ size_t cbRead;
+ size_t cbSumRead = 0;
+ uint8_t *pbTmp = (uint8_t *)&pFS->abBuffer[0];
size_t cbTmp = sizeof(pFS->abBuffer);
do
{
- rc = RTFileRead(pFS->file, pTmp, cbTmp, &cbRead);
+ rc = RTFileRead(pFS->file, pbTmp, cbTmp, &cbRead);
if (RT_FAILURE(rc))
return rc;
- pTmp += cbRead;
+ pbTmp += cbRead;
cbTmp -= cbRead;
cbSumRead += cbRead;
} while (cbTmp && cbRead);
pFS->offBuffer += pFS->cbBuffer;
- pFS->cbBuffer = cbSumRead;
+ pFS->cbBuffer = (uint32_t)cbSumRead;
}
- uint32_t cbThisRead = RT_MIN(cbBuffer,
- pFS->cbBuffer - uOffset % sizeof(pFS->abBuffer));
+ uint32_t cbThisRead = (uint32_t)RT_MIN(cbBuffer,
+ pFS->cbBuffer - uOffset % sizeof(pFS->abBuffer));
memcpy(pvBuffer, &pFS->abBuffer[uOffset % sizeof(pFS->abBuffer)],
cbThisRead);
uOffset += cbThisRead;
@@ -654,8 +659,8 @@ static int convOutWrite(void *pvUser, void *pStorage, uint64_t uOffset,
pFS->cbBuffer = 0;
}
- uint32_t cbThisWrite = RT_MIN(cbBuffer,
- sizeof(pFS->abBuffer) - uOffset % sizeof(pFS->abBuffer));
+ uint32_t cbThisWrite = (uint32_t)RT_MIN(cbBuffer,
+ sizeof(pFS->abBuffer) - uOffset % sizeof(pFS->abBuffer));
memcpy(&pFS->abBuffer[uOffset % sizeof(pFS->abBuffer)], pvBuffer,
cbThisWrite);
uOffset += cbThisWrite;
@@ -954,7 +959,7 @@ int handleInfo(HandlerArg *a)
return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
/* Open the image */
- rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_INFO, NULL);
+ rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_READONLY, NULL);
if (RT_FAILURE(rc))
return errorRuntime("Error while opening the image: %Rrc\n", rc);
@@ -1296,6 +1301,42 @@ int handleCreateCache(HandlerArg *a)
return rc;
}
+static DECLCALLBACK(bool) vdIfCfgCreateBaseAreKeysValid(void *pvUser, const char *pszzValid)
+{
+ return VINF_SUCCESS; /** @todo: Implement. */
+}
+
+static DECLCALLBACK(int) vdIfCfgCreateBaseQuerySize(void *pvUser, const char *pszName, size_t *pcbValue)
+{
+ AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
+
+ AssertPtrReturn(pvUser, VERR_GENERAL_FAILURE);
+
+ if (RTStrCmp(pszName, "DataAlignment"))
+ return VERR_CFGM_VALUE_NOT_FOUND;
+
+ *pcbValue = strlen((const char *)pvUser) + 1 /* include terminator */;
+
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) vdIfCfgCreateBaseQuery(void *pvUser, const char *pszName, char *pszValue, size_t cchValue)
+{
+ AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
+
+ AssertPtrReturn(pvUser, VERR_GENERAL_FAILURE);
+
+ if (RTStrCmp(pszName, "DataAlignment"))
+ return VERR_CFGM_VALUE_NOT_FOUND;
+
+ if (strlen((const char *)pvUser) >= cchValue)
+ return VERR_CFGM_NOT_ENOUGH_SPACE;
+
+ memcpy(pszValue, pvUser, strlen((const char *)pvUser) + 1);
+
+ return VINF_SUCCESS;
+
+}
int handleCreateBase(HandlerArg *a)
{
@@ -1306,7 +1347,10 @@ int handleCreateBase(HandlerArg *a)
const char *pszVariant = NULL;
unsigned uImageFlags = VD_IMAGE_FLAGS_NONE;
uint64_t cbSize = 0;
+ const char *pszDataAlignment = NULL;
VDGEOMETRY LCHSGeometry, PCHSGeometry;
+ PVDINTERFACE pVDIfsOperation = NULL;
+ VDINTERFACECONFIG vdIfCfg;
memset(&LCHSGeometry, 0, sizeof(VDGEOMETRY));
memset(&PCHSGeometry, 0, sizeof(VDGEOMETRY));
@@ -1314,10 +1358,11 @@ int handleCreateBase(HandlerArg *a)
/* Parse the command line. */
static const RTGETOPTDEF s_aOptions[] =
{
- { "--filename", 'f', RTGETOPT_REQ_STRING },
- { "--size", 's', RTGETOPT_REQ_UINT64 },
- { "--format", 'b', RTGETOPT_REQ_STRING },
- { "--variant", 'v', RTGETOPT_REQ_STRING }
+ { "--filename", 'f', RTGETOPT_REQ_STRING },
+ { "--size", 's', RTGETOPT_REQ_UINT64 },
+ { "--format", 'b', RTGETOPT_REQ_STRING },
+ { "--variant", 'v', RTGETOPT_REQ_STRING },
+ { "--dataalignment", 'a', RTGETOPT_REQ_STRING }
};
int ch;
RTGETOPTUNION ValueUnion;
@@ -1343,6 +1388,10 @@ int handleCreateBase(HandlerArg *a)
pszVariant = ValueUnion.psz;
break;
+ case 'a': // --dataalignment
+ pszDataAlignment = ValueUnion.psz;
+ break;
+
default:
ch = RTGetOptPrintError(ch, &ValueUnion);
printUsage(g_pStdErr);
@@ -1364,6 +1413,16 @@ int handleCreateBase(HandlerArg *a)
return errorSyntax("Invalid variant %s given\n", pszVariant);
}
+ /* Setup the config interface if required. */
+ if (pszDataAlignment)
+ {
+ vdIfCfg.pfnAreKeysValid = vdIfCfgCreateBaseAreKeysValid;
+ vdIfCfg.pfnQuerySize = vdIfCfgCreateBaseQuerySize;
+ vdIfCfg.pfnQuery = vdIfCfgCreateBaseQuery;
+ VDInterfaceAdd(&vdIfCfg.Core, "Config", VDINTERFACETYPE_CONFIG, (void *)pszDataAlignment,
+ sizeof(vdIfCfg), &pVDIfsOperation);
+ }
+
/* just try it */
rc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
if (RT_FAILURE(rc))
@@ -1371,7 +1430,7 @@ int handleCreateBase(HandlerArg *a)
rc = VDCreateBase(pDisk, pszBackend, pszFilename, cbSize, uImageFlags,
NULL, &PCHSGeometry, &LCHSGeometry, NULL, VD_OPEN_FLAGS_NORMAL,
- NULL, NULL);
+ NULL, pVDIfsOperation);
if (RT_FAILURE(rc))
return errorRuntime("Error while creating the virtual disk: %Rrc\n", rc);
@@ -1448,6 +1507,64 @@ int handleRepair(HandlerArg *a)
}
+int handleClearComment(HandlerArg *a)
+{
+ int rc = VINF_SUCCESS;
+ PVBOXHDD pDisk = NULL;
+ const char *pszFilename = NULL;
+ bool fDryRun = false;
+
+ /* Parse the command line. */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--filename", 'f', RTGETOPT_REQ_STRING }
+ };
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case 'f': // --filename
+ pszFilename = ValueUnion.psz;
+ break;
+
+ default:
+ ch = RTGetOptPrintError(ch, &ValueUnion);
+ printUsage(g_pStdErr);
+ return ch;
+ }
+ }
+
+ /* Check for mandatory parameters. */
+ if (!pszFilename)
+ return errorSyntax("Mandatory --filename option missing\n");
+
+ /* just try it */
+ char *pszFormat = NULL;
+ VDTYPE enmType = VDTYPE_INVALID;
+ rc = VDGetFormat(NULL, NULL, pszFilename, &pszFormat, &enmType);
+ if (RT_FAILURE(rc))
+ return errorSyntax("Format autodetect failed: %Rrc\n", rc);
+
+ rc = VDCreate(pVDIfs, enmType, &pDisk);
+ if (RT_FAILURE(rc))
+ return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
+
+ /* Open the image */
+ rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_INFO, NULL);
+ if (RT_FAILURE(rc))
+ return errorRuntime("Error while opening the image: %Rrc\n", rc);
+
+ VDSetComment(pDisk, 0, NULL);
+
+ VDDestroy(pDisk);
+ return rc;
+}
+
+
int main(int argc, char *argv[])
{
int exitcode = 0;
@@ -1530,13 +1647,14 @@ int main(int argc, char *argv[])
int (*handler)(HandlerArg *a);
} s_commandHandlers[] =
{
- { "setuuid", handleSetUUID },
- { "convert", handleConvert },
- { "info", handleInfo },
- { "compact", handleCompact },
- { "createcache", handleCreateCache },
- { "createbase", handleCreateBase },
- { "repair", handleRepair },
+ { "setuuid", handleSetUUID },
+ { "convert", handleConvert },
+ { "info", handleInfo },
+ { "compact", handleCompact },
+ { "createcache", handleCreateCache },
+ { "createbase", handleCreateBase },
+ { "repair", handleRepair },
+ { "clearcomment", handleClearComment },
{ NULL, NULL }
};