diff options
Diffstat (limited to 'src/VBox/Storage')
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, - ¶llelsHeader, sizeof(parallelsHeader), NULL); + ¶llelsHeader, 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, ¶llelsHeader, - 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 } }; |