VirtualBox

Changeset 50991 in vbox for trunk/src/VBox/Storage/VD.cpp


Ignore:
Timestamp:
Apr 8, 2014 10:45:31 AM (11 years ago)
Author:
vboxsync
Message:

Storage: Add support for filter plugins

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Storage/VD.cpp

    r50988 r50991  
    194194
    195195/**
     196 * VD filter instance.
     197 */
     198typedef struct VDFILTER
     199{
     200    /** Pointer to the previous filter. */
     201    struct VDFILTER   *pPrev;
     202    /** Pointer to the next filter. */
     203    struct VDFILTER   *pNext;
     204    /** Opaque VD filter backend instance data. */
     205    void              *pvBackendData;
     206    /** Pointer to the filter backend interface. */
     207    PCVDFILTERBACKEND  pBackend;
     208} VDFILTER;
     209/** Pointer to a VD filter instance. */
     210typedef VDFILTER *PVDFILTER;
     211
     212/**
    196213 * VBox HDD Container main structure, private part.
    197214 */
     
    262279    /** Pointer to the discard state if any. */
    263280    PVDDISCARDSTATE        pDiscard;
     281
     282    /** Pointer to the first filter in the chain. */
     283    PVDFILTER              pFilterHead;
     284    /** Pointer to the last filter in the chain. */
     285    PVDFILTER              pFilterTail;
    264286};
    265287
     
    357379            /** Override for the parent image to start reading from. */
    358380            PVDIMAGE             pImageParentOverride;
     381            /** Original offset of the transfer - required for filtering read requests. */
     382            uint64_t             uOffsetXferOrig;
     383            /** Original size of the transfer - required for fitlering read requests. */
     384            size_t               cbXferOrig;
    359385        } Io;
    360386        /** Discard requests. */
     
    598624};
    599625
     626/** Number of supported filter backends. */
     627static unsigned g_cFilterBackends = 0;
     628/** Array of pointers to the filters backends. */
     629static PCVDFILTERBACKEND *g_apFilterBackends = NULL;
     630
    600631/** Forward declaration of the async discard helper. */
    601632static int vdDiscardHelperAsync(PVDIOCTX pIoCtx);
     
    652683
    653684/**
     685 * Add several filter bakends.
     686 *
     687 * @returns VBox status code.
     688 * @param   ppBackends    Array of filter backends to add.
     689 * @param   cBackends     Number of backends to add.
     690 */
     691static int vdAddFilterBackends(PCVDFILTERBACKEND *ppBackends, unsigned cBackends)
     692{
     693    PCVDFILTERBACKEND *pTmp = (PCVDFILTERBACKEND *)RTMemRealloc(g_apFilterBackends,
     694           (g_cFilterBackends + cBackends) * sizeof(PCVDFILTERBACKEND));
     695    if (RT_UNLIKELY(!pTmp))
     696        return VERR_NO_MEMORY;
     697    g_apFilterBackends = pTmp;
     698    memcpy(&g_apFilterBackends[g_cFilterBackends], ppBackends, cBackends * sizeof(PCVDFILTERBACKEND));
     699    g_cFilterBackends += cBackends;
     700    return VINF_SUCCESS;
     701}
     702
     703/**
     704 * Add a single filter backend to the list of supported filters.
     705 *
     706 * @returns VBox status code.
     707 * @param   pBackend    The backend to add.
     708 */
     709DECLINLINE(int) vdAddFilterBackend(PCVDFILTERBACKEND pBackend)
     710{
     711    return vdAddFilterBackends(&pBackend, 1);
     712}
     713
     714/**
    654715 * internal: issue error message.
    655716 */
     
    756817
    757818/**
     819 * internal: find filter backend.
     820 */
     821static int vdFindFilterBackend(const char *pszFilter, PCVDFILTERBACKEND *ppBackend)
     822{
     823    int rc = VINF_SUCCESS;
     824    PCVDFILTERBACKEND pBackend = NULL;
     825
     826    for (unsigned i = 0; i < g_cFilterBackends; i++)
     827    {
     828        if (!RTStrICmp(pszFilter, g_apFilterBackends[i]->pszBackendName))
     829        {
     830            pBackend = g_apFilterBackends[i];
     831            break;
     832        }
     833    }
     834    *ppBackend = pBackend;
     835    return rc;
     836}
     837
     838
     839/**
    758840 * internal: add image structure to the end of images list.
    759841 */
     
    804886
    805887/**
     888 * internal: add filter structure to the end of filter list.
     889 */
     890static void vdAddFilterToList(PVBOXHDD pDisk, PVDFILTER pFilter)
     891{
     892    pFilter->pPrev = NULL;
     893    pFilter->pNext = NULL;
     894
     895    if (pDisk->pFilterHead)
     896    {
     897        pFilter->pPrev = pDisk->pFilterTail;
     898        pDisk->pFilterTail->pNext = pFilter;
     899        pDisk->pFilterTail = pFilter;
     900    }
     901    else
     902    {
     903        pDisk->pFilterHead = pFilter;
     904        pDisk->pFilterTail = pFilter;
     905    }
     906}
     907
     908/**
     909 * internal: Remove last filter structure from the filter list.
     910 */
     911static void vdRemoveFilterFromList(PVBOXHDD pDisk, PVDFILTER pFilter)
     912{
     913    Assert(pDisk->pFilterHead != NULL && pDisk->pFilterTail != NULL);
     914
     915    if (pFilter->pPrev)
     916        pFilter->pPrev->pNext = pFilter->pNext;
     917    else
     918        pDisk->pFilterHead = pFilter->pNext;
     919
     920    if (pFilter->pNext)
     921        pFilter->pNext->pPrev = pFilter->pPrev;
     922    else
     923        pDisk->pFilterTail = pFilter->pPrev;
     924
     925    pFilter->pPrev = NULL;
     926    pFilter->pNext = NULL;
     927}
     928
     929/**
    806930 * internal: find image by index into the images list.
    807931 */
     
    817941    }
    818942    return pImage;
     943}
     944
     945/**
     946 * Applies the filter chain to the given write request.
     947 *
     948 * @returns VBox status code.
     949 * @param   pDisk    The HDD container.
     950 * @param   uOffset  The start offset of the write.
     951 * @param   cbWrite  Number of bytes to write.
     952 * @param   pIoCtx   The I/O context associated with the request.
     953 */
     954static int vdFilterChainApplyWrite(PVBOXHDD pDisk, uint64_t uOffset, size_t cbWrite,
     955                                   PVDIOCTX pIoCtx)
     956{
     957    int rc = VINF_SUCCESS;
     958
     959    if (pDisk->pFilterHead)
     960    {
     961        PVDFILTER pFilterCurr = pDisk->pFilterHead;
     962
     963        do
     964        {
     965            rc = pFilterCurr->pBackend->pfnFilterWrite(pFilterCurr->pvBackendData, uOffset, cbWrite, pIoCtx);
     966            /* Reset S/G buffer for the next filter. */
     967            RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
     968
     969            pFilterCurr = pFilterCurr->pNext;
     970        } while (   RT_SUCCESS(rc)
     971                 && pFilterCurr);
     972    }
     973
     974    return rc;
     975}
     976
     977/**
     978 * Applies the filter chain to the given read request.
     979 *
     980 * @returns VBox status code.
     981 * @param   pDisk    The HDD container.
     982 * @param   uOffset  The start offset of the read.
     983 * @param   cbRead   Number of bytes read.
     984 * @param   pIoCtx   The I/O context associated with the request.
     985 */
     986static int vdFilterChainApplyRead(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRead,
     987                                  PVDIOCTX pIoCtx)
     988{
     989    int rc = VINF_SUCCESS;
     990
     991    if (pDisk->pFilterHead)
     992    {
     993        PVDFILTER pFilterCurr = pDisk->pFilterHead;
     994
     995        do
     996        {
     997            rc = pFilterCurr->pBackend->pfnFilterRead(pFilterCurr->pvBackendData, uOffset, cbRead, pIoCtx);
     998            /* Reset S/G buffer for the next filter. */
     999            RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
     1000
     1001            pFilterCurr = pFilterCurr->pNext;
     1002        } while (   RT_SUCCESS(rc)
     1003                 && pFilterCurr);
     1004    }
     1005
     1006    return rc;
     1007}
     1008
     1009DECLINLINE(void) vdIoCtxRootComplete(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
     1010{
     1011    if (   RT_SUCCESS(pIoCtx->rcReq)
     1012        && pIoCtx->enmTxDir == VDIOCTXTXDIR_READ)
     1013        pIoCtx->rcReq = vdFilterChainApplyRead(pDisk, pIoCtx->Req.Io.uOffsetXferOrig,
     1014                                               pIoCtx->Req.Io.cbXferOrig, pIoCtx);
     1015
     1016    pIoCtx->Type.Root.pfnComplete(pIoCtx->Type.Root.pvUser1,
     1017                                  pIoCtx->Type.Root.pvUser2,
     1018                                  pIoCtx->rcReq);
    8191019}
    8201020
     
    8361036    pIoCtx->Req.Io.cbBufClear     = 0;
    8371037    pIoCtx->Req.Io.pImageParentOverride = NULL;
     1038    pIoCtx->Req.Io.uOffsetXferOrig      = uOffset;
     1039    pIoCtx->Req.Io.cbXferOrig           = cbTransfer;
    8381040    pIoCtx->cDataTransfersPending = 0;
    8391041    pIoCtx->cMetaTransfersPending = 0;
     
    12911493DECLINLINE(void) vdIoTaskFree(PVBOXHDD pDisk, PVDIOTASK pIoTask)
    12921494{
    1293 //#ifdef DEBUG
     1495#ifdef DEBUG
    12941496    memset(pIoTask, 0xff, sizeof(VDIOTASK));
    1295 //#endif
     1497#endif
    12961498    RTMemCacheFree(pDisk->hMemCacheIoTask, pIoTask);
    12971499}
     
    15371739            LogFlowFunc(("Waiting I/O context completed pTmp=%#p\n", pTmp));
    15381740            vdThreadFinishWrite(pDisk);
    1539             pTmp->Type.Root.pfnComplete(pTmp->Type.Root.pvUser1,
    1540                                         pTmp->Type.Root.pvUser2,
    1541                                         pTmp->rcReq);
     1741            vdIoCtxRootComplete(pDisk, pTmp);
    15421742            vdIoCtxFree(pDisk, pTmp);
    15431743        }
     
    15941794            LogFlowFunc(("Waiting I/O context completed pTmp=%#p\n", pTmp));
    15951795            vdThreadFinishWrite(pDisk);
    1596             pTmp->Type.Root.pfnComplete(pTmp->Type.Root.pvUser1,
    1597                                         pTmp->Type.Root.pvUser2,
    1598                                         pTmp->rcReq);
     1796            vdIoCtxRootComplete(pDisk, pTmp);
    15991797            vdIoCtxFree(pDisk, pTmp);
    16001798        }
     
    19822180    IoCtx.Type.Root.pvUser2     = hEventComplete;
    19832181    rc = vdIoCtxProcessSync(&IoCtx, hEventComplete);
     2182    if (RT_SUCCESS(rc))
     2183        rc = vdFilterChainApplyRead(pDisk, uOffset, cbRead, &IoCtx);
    19842184
    19852185    RTSemEventDestroy(hEventComplete);
     
    20792279    IoCtx.Type.Root.pvUser1     = pDisk;
    20802280    IoCtx.Type.Root.pvUser2     = hEventComplete;
    2081     rc = vdIoCtxProcessSync(&IoCtx, hEventComplete);
     2281    /* Apply write filter chain here. */
     2282    rc = vdFilterChainApplyWrite(pDisk, uOffset, cbWrite, &IoCtx);
     2283    if (RT_SUCCESS(rc))
     2284        rc = vdIoCtxProcessSync(&IoCtx, hEventComplete);
    20822285
    20832286    RTSemEventDestroy(hEventComplete);
     
    32303433
    32313434/**
     3435 * @copydoc VDPLUGIN::pfnRegisterFilter
     3436 */
     3437static DECLCALLBACK(int) vdPluginRegisterFilter(void *pvUser, PCVDFILTERBACKEND pBackend)
     3438{
     3439    int rc = VINF_SUCCESS;
     3440
     3441    if (pBackend->cbSize == sizeof(VDFILTERBACKEND))
     3442        vdAddFilterBackend(pBackend);
     3443    else
     3444    {
     3445        LogFunc(("ignored plugin: pBackend->cbSize=%d rc=%Rrc\n", pBackend->cbSize));
     3446        rc = VERR_IGNORED;
     3447    }
     3448
     3449    return rc;
     3450}
     3451
     3452/**
    32323453 * Adds a plugin to the list of loaded plugins.
    32333454 *
     
    33323553            PFNVDPLUGINLOAD pfnVDPluginLoad = NULL;
    33333554
    3334             BackendRegister.pfnRegisterImage = vdPluginRegisterImage;
    3335             BackendRegister.pfnRegisterCache = vdPluginRegisterCache;
    3336             /** @todo: Filter plugins. */
     3555            BackendRegister.pfnRegisterImage  = vdPluginRegisterImage;
     3556            BackendRegister.pfnRegisterCache  = vdPluginRegisterCache;
     3557            BackendRegister.pfnRegisterFilter = vdPluginRegisterFilter;
    33373558
    33383559            rc = RTLdrGetSymbol(hPlugin, VD_PLUGIN_LOAD_NAME, (void**)&pfnVDPluginLoad);
     
    35803801                {
    35813802                    LogFlowFunc(("Parent I/O context completed pIoCtxParent=%#p rcReq=%Rrc\n", pIoCtxParent, pIoCtxParent->rcReq));
    3582                     pIoCtxParent->Type.Root.pfnComplete(pIoCtxParent->Type.Root.pvUser1,
    3583                                                         pIoCtxParent->Type.Root.pvUser2,
    3584                                                         pIoCtxParent->rcReq);
     3803                    vdIoCtxRootComplete(pDisk, pIoCtxParent);
    35853804                    vdThreadFinishWrite(pDisk);
    35863805                    vdIoCtxFree(pDisk, pIoCtxParent);
     
    36103829
    36113830                LogFlowFunc(("I/O context completed pIoCtx=%#p rcReq=%Rrc\n", pIoCtx, pIoCtx->rcReq));
    3612                 pIoCtx->Type.Root.pfnComplete(pIoCtx->Type.Root.pvUser1,
    3613                                               pIoCtx->Type.Root.pvUser2,
    3614                                               pIoCtx->rcReq);
     3831                vdIoCtxRootComplete(pDisk, pIoCtx);
    36153832            }
    36163833
     
    51805397            pDisk->hMemCacheIoCtx          = NIL_RTMEMCACHE;
    51815398            pDisk->hMemCacheIoTask         = NIL_RTMEMCACHE;
     5399            pDisk->pFilterHead             = NULL;
     5400            pDisk->pFilterTail             = NULL;
    51825401
    51835402            /* Create the I/O ctx cache */
     
    52375456
    52385457        rc = VDCloseAll(pDisk);
     5458        int rc2 = VDFilterRemoveAll(pDisk);
     5459        if (RT_SUCCESS(rc))
     5460            rc = rc2;
     5461
    52395462        RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
    52405463        RTMemCacheDestroy(pDisk->hMemCacheIoTask);
     
    58976120            RTMemFree(pCache);
    58986121        }
     6122    }
     6123
     6124    LogFlowFunc(("returns %Rrc\n", rc));
     6125    return rc;
     6126}
     6127
     6128/**
     6129 * Adds a filter to the disk.
     6130 *
     6131 * @returns VBox status code.
     6132 * @param   pDisk           Pointer to the HDD container which should use the filter.
     6133 * @param   pszFilter       Name of the filter backend to use (case insensitive).
     6134 * @param   pVDIfsFilter    Pointer to the per-filter VD interface list.
     6135 */
     6136VBOXDDU_DECL(int) VDFilterAdd(PVBOXHDD pDisk, const char *pszFilter,
     6137                              PVDINTERFACE pVDIfsFilter)
     6138{
     6139    int rc = VINF_SUCCESS;
     6140    int rc2;
     6141    bool fLockWrite = false;
     6142    PVDFILTER pFilter = NULL;
     6143
     6144    LogFlowFunc(("pDisk=%#p pszFilter=\"%s\" pVDIfsFilter=%#p\n",
     6145                 pDisk, pszFilter, pVDIfsFilter));
     6146
     6147    do
     6148    {
     6149        /* sanity check */
     6150        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
     6151        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
     6152
     6153        /* Check arguments. */
     6154        AssertMsgBreakStmt(VALID_PTR(pszFilter) && *pszFilter,
     6155                           ("pszFilter=%#p \"%s\"\n", pszFilter, pszFilter),
     6156                           rc = VERR_INVALID_PARAMETER);
     6157
     6158        /* Set up image descriptor. */
     6159        pFilter = (PVDFILTER)RTMemAllocZ(sizeof(VDFILTER));
     6160        if (!pFilter)
     6161        {
     6162            rc = VERR_NO_MEMORY;
     6163            break;
     6164        }
     6165
     6166        rc = vdFindFilterBackend(pszFilter, &pFilter->pBackend);
     6167        if (RT_FAILURE(rc))
     6168            break;
     6169        if (!pFilter->pBackend)
     6170        {
     6171            rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
     6172                         N_("VD: unknown filter backend name '%s'"), pszFilter);
     6173            break;
     6174        }
     6175
     6176        rc = pFilter->pBackend->pfnCreate(pDisk->pVDIfsDisk, pVDIfsFilter,
     6177                                          &pFilter->pvBackendData);
     6178
     6179        /* If the open in read-write mode failed, retry in read-only mode. */
     6180        if (RT_FAILURE(rc))
     6181        {
     6182            rc = vdError(pDisk, rc, RT_SRC_POS,
     6183                         N_("VD: error %Rrc creating filter '%s'"), rc, pszFilter);
     6184            break;
     6185        }
     6186
     6187        /* Lock disk for writing, as we modify pDisk information below. */
     6188        rc2 = vdThreadStartWrite(pDisk);
     6189        AssertRC(rc2);
     6190        fLockWrite = true;
     6191
     6192        /* Add filter to chain. */
     6193        vdAddFilterToList(pDisk, pFilter);
     6194    } while (0);
     6195
     6196    if (RT_UNLIKELY(fLockWrite))
     6197    {
     6198        rc2 = vdThreadFinishWrite(pDisk);
     6199        AssertRC(rc2);
     6200    }
     6201
     6202    if (RT_FAILURE(rc))
     6203    {
     6204        if (pFilter)
     6205            RTMemFree(pFilter);
    58996206    }
    59006207
     
    78908197
    78918198/**
     8199 * Removes the last added filter in the HDD container.
     8200 *
     8201 * @return  VBox status code.
     8202 * @retval  VERR_VD_NOT_OPENED if no filter is present for the disk.
     8203 * @param   pDisk           Pointer to HDD container.
     8204 */
     8205VBOXDDU_DECL(int) VDFilterRemove(PVBOXHDD pDisk)
     8206{
     8207    int rc = VINF_SUCCESS;
     8208    int rc2;
     8209    bool fLockWrite = false;
     8210    PVDFILTER pFilter = NULL;
     8211
     8212    LogFlowFunc(("pDisk=%#p\n", pDisk));
     8213
     8214    do
     8215    {
     8216        /* sanity check */
     8217        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
     8218        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
     8219
     8220        rc2 = vdThreadStartWrite(pDisk);
     8221        AssertRC(rc2);
     8222        fLockWrite = true;
     8223
     8224        AssertPtrBreakStmt(pDisk->pFilterHead, rc = VERR_VD_NOT_OPENED);
     8225
     8226        pFilter = pDisk->pFilterTail;
     8227        vdRemoveFilterFromList(pDisk, pFilter);
     8228
     8229        pFilter->pBackend->pfnDestroy(pFilter->pvBackendData);
     8230        RTMemFree(pFilter);
     8231    } while (0);
     8232
     8233    if (RT_LIKELY(fLockWrite))
     8234    {
     8235        rc2 = vdThreadFinishWrite(pDisk);
     8236        AssertRC(rc2);
     8237    }
     8238
     8239    LogFlowFunc(("returns %Rrc\n", rc));
     8240    return rc;
     8241}
     8242
     8243/**
    78928244 * Closes all opened image files in HDD container.
    78938245 *
     
    79418293        }
    79428294        Assert(!VALID_PTR(pDisk->pLast));
     8295    } while (0);
     8296
     8297    if (RT_UNLIKELY(fLockWrite))
     8298    {
     8299        rc2 = vdThreadFinishWrite(pDisk);
     8300        AssertRC(rc2);
     8301    }
     8302
     8303    LogFlowFunc(("returns %Rrc\n", rc));
     8304    return rc;
     8305}
     8306
     8307/**
     8308 * Removes all filters of the given HDD container.
     8309 *
     8310 * @return  VBox status code.
     8311 * @param   pDisk           Pointer to HDD container.
     8312 */
     8313VBOXDDU_DECL(int) VDFilterRemoveAll(PVBOXHDD pDisk)
     8314{
     8315    int rc = VINF_SUCCESS;
     8316    int rc2;
     8317    bool fLockWrite = false;
     8318
     8319    LogFlowFunc(("pDisk=%#p\n", pDisk));
     8320    do
     8321    {
     8322        /* sanity check */
     8323        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
     8324        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
     8325
     8326        /* Lock the entire operation. */
     8327        rc2 = vdThreadStartWrite(pDisk);
     8328        AssertRC(rc2);
     8329        fLockWrite = true;
     8330
     8331        PVDFILTER pFilter = pDisk->pFilterTail;
     8332        while (VALID_PTR(pFilter))
     8333        {
     8334            PVDFILTER pPrev = pFilter->pPrev;
     8335            vdRemoveFilterFromList(pDisk, pFilter);
     8336
     8337            rc2 = pFilter->pBackend->pfnDestroy(pFilter->pvBackendData);
     8338            if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
     8339                rc = rc2;
     8340            /* Free remaining resources related to the image. */
     8341            RTMemFree(pFilter);
     8342            pFilter = pPrev;
     8343        }
     8344        Assert(!VALID_PTR(pDisk->pFilterTail));
    79438345    } while (0);
    79448346
     
    963510037        {
    963610038            rc = VERR_NO_MEMORY;
     10039            break;
     10040        }
     10041
     10042        /* Apply write filter chain here. */
     10043        rc = vdFilterChainApplyWrite(pDisk, uOffset, cbWrite, pIoCtx);
     10044        if (RT_FAILURE(rc))
     10045        {
     10046            vdIoCtxFree(pDisk, pIoCtx);
    963710047            break;
    963810048        }
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette