VirtualBox

Changeset 82595 in vbox for trunk/src


Ignore:
Timestamp:
Dec 16, 2019 5:58:40 PM (5 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
135480
Message:

Storage/QCOW: Implement readonly support for compressed clusters in v2 images

File:
1 edited

Legend:

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

    r79965 r82595  
    3131#include <iprt/path.h>
    3232#include <iprt/list.h>
     33#include <iprt/zip.h>
    3334
    3435#include "VDBackends.h"
     
    250251    /** Cluster size in bytes. */
    251252    uint32_t            cbCluster;
     253    /** Number of bits in the virtual offset used as the cluster offset. */
     254    uint32_t            cClusterBits;
     255    /** Bitmask to extract the offset from a compressed cluster descriptor. */
     256    uint64_t            fMaskCompressedClusterOffset;
     257    /** Bitmask to extract the sector count from a compressed cluster descriptor. */
     258    uint64_t            fMaskCompressedClusterSectors;
     259    /** Number of bits to shift the sector count to the right to get the final value. */
     260    uint32_t            cBitsShiftRCompressedClusterSectors;
    252261    /** Number of entries in the L1 table. */
    253262    uint32_t            cL1TableEntries;
     
    287296    /** Number of bits to shift to get the L2 index. */
    288297    uint32_t            cL2Shift;
     298
     299    /** Size of compressed cluster buffer. */
     300    size_t              cbCompCluster;
     301    /** Compressed cluster buffer. */
     302    void                *pvCompCluster;
     303    /** Buffer to hold the uncompressed data. */
     304    void                *pvCluster;
    289305
    290306    /** Pointer to the L2 table we are currently allocating
     
    860876static int qcowConvertToImageOffset(PQCOWIMAGE pImage, PVDIOCTX pIoCtx,
    861877                                    uint32_t idxL1, uint32_t idxL2,
    862                                     uint32_t offCluster, uint64_t *poffImage)
     878                                    uint32_t offCluster, uint64_t *poffImage,
     879                                    bool *pfCompressed, size_t *pcbCompressed)
    863880{
    864881    int rc = VERR_VD_BLOCK_FREE;
     
    886903                {
    887904                    if (RT_UNLIKELY(off & QCOW_V2_COMPRESSED_FLAG))
    888                         rc = VERR_NOT_SUPPORTED;
     905                    {
     906                        size_t cCompressedClusterSectors = ((off & pImage->fMaskCompressedClusterSectors) >> pImage->cBitsShiftRCompressedClusterSectors);
     907                        uint64_t offImage = off & pImage->fMaskCompressedClusterOffset;
     908
     909                        *pfCompressed  = true;
     910                        *poffImage     = offImage;
     911                        *pcbCompressed = (cCompressedClusterSectors + 1) * 512 - (offImage & 511ULL);
     912                    }
    889913                    else
     914                    {
    890915                        off &= QCOW_V2_TBL_OFFSET_MASK;
     916
     917                        *pfCompressed = false;
     918                        *poffImage = off + offCluster;
     919                    }
    891920                }
    892921                else
    893922                {
    894923                    if (RT_UNLIKELY(off & QCOW_V1_COMPRESSED_FLAG))
    895                         rc = VERR_NOT_SUPPORTED;
     924                    {
     925                        size_t cCompressedClusterSectors = (off & pImage->fMaskCompressedClusterSectors) >> pImage->cBitsShiftRCompressedClusterSectors;
     926
     927                        *pfCompressed  = true;
     928                        *poffImage     = off & pImage->fMaskCompressedClusterOffset;
     929                        *pcbCompressed = cCompressedClusterSectors * 512; /* Only additional sectors */
     930                        /* Add remaining bytes of the sector the offset starts in. */
     931                        *pcbCompressed += 512 - RT_ALIGN_64(*poffImage, 512) - *poffImage;
     932                    }
    896933                    else
     934                    {
    897935                        off &= ~QCOW_V1_COMPRESSED_FLAG;
     936
     937                        *pfCompressed = false;
     938                        *poffImage = off + offCluster;
     939                    }
    898940                }
    899 
    900                 *poffImage = off + offCluster;
    901941            }
    902942            else
     
    10311071            RTStrFree(pImage->pszBackingFilename);
    10321072            pImage->pszBackingFilename = NULL;
     1073        }
     1074
     1075        if (pImage->pvCompCluster)
     1076        {
     1077            RTMemFree(pImage->pvCompCluster);
     1078            pImage->pvCompCluster = NULL;
     1079            pImage->cbCompCluster = 0;
     1080        }
     1081
     1082        if (pImage->pvCluster)
     1083        {
     1084            RTMemFree(pImage->pvCluster);
     1085            pImage->pvCluster = NULL;
    10331086        }
    10341087
     
    11561209                                pImage->MTime              = Header.Version.v1.u32MTime;
    11571210                                pImage->cbSize             = Header.Version.v1.u64Size;
     1211                                pImage->cClusterBits       = Header.Version.v1.u8ClusterBits;
    11581212                                pImage->cbCluster          = RT_BIT_32(Header.Version.v1.u8ClusterBits);
    11591213                                pImage->cL2TableEntries    = RT_BIT_32(Header.Version.v1.u8L2Bits);
     
    11851239                                pImage->cbBackingFilename     = Header.Version.v2.u32BackingFileSize;
    11861240                                pImage->cbSize                = Header.Version.v2.u64Size;
     1241                                pImage->cClusterBits          = Header.Version.v2.u32ClusterBits;
    11871242                                pImage->cbCluster             = RT_BIT_32(Header.Version.v2.u32ClusterBits);
    11881243                                pImage->cL2TableEntries       = pImage->cbCluster / sizeof(uint64_t);
     
    11931248                                pImage->cbRefcountTable       = qcowCluster2Byte(pImage, Header.Version.v2.u32RefcountTableClusters);
    11941249                                pImage->cRefcountTableEntries = pImage->cbRefcountTable / sizeof(uint64_t);
     1250
     1251                                /* Init the masks to extract offset and sector count from a compressed cluster descriptor. */
     1252                                uint32_t cBitsCompressedClusterOffset = 62 - (pImage->cClusterBits - 8);
     1253                                pImage->fMaskCompressedClusterOffset  = RT_BIT_64(cBitsCompressedClusterOffset) - 1;
     1254                                pImage->fMaskCompressedClusterSectors = (RT_BIT_64(62) - 1) & ~pImage->fMaskCompressedClusterOffset;
     1255                                pImage->cBitsShiftRCompressedClusterSectors = cBitsCompressedClusterOffset;
    11951256
    11961257                                if (Header.u32Version == 3)
     
    15751636}
    15761637
     1638/**
     1639 * Reads a compressed cluster, inflates it and copies the amount of data requested
     1640 * into the given I/O context.
     1641 *
     1642 * @returns VBox status code.
     1643 * @param   pImage              The image instance data.
     1644 * @param   pIoCtx              The I/O context.
     1645 * @param   offCluster          Where to start reading in the uncompressed cluster.
     1646 * @param   cbToRead            How much to read in the uncomrpessed cluster.
     1647 * @param   offFile             Offset where the compressed cluster is stored in the image.
     1648 * @param   cbCompressedCluster Size of the comrpessed cluster in bytes.
     1649 */
     1650static int qcowReadCompressedCluster(PQCOWIMAGE pImage, PVDIOCTX pIoCtx,
     1651                                     uint32_t offCluster, size_t cbToRead,
     1652                                     uint64_t offFile, size_t cbCompressedCluster)
     1653{
     1654    int rc = VINF_SUCCESS;
     1655
     1656    AssertReturn(!(pImage->uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO), VERR_NOT_SUPPORTED); /* Only synchronous I/O supported so far. */
     1657
     1658    if (cbCompressedCluster > pImage->cbCompCluster)
     1659    {
     1660        void *pvCompClusterNew = RTMemRealloc(pImage->pvCompCluster, cbCompressedCluster);
     1661        if (RT_LIKELY(pvCompClusterNew))
     1662        {
     1663            pImage->pvCompCluster = pvCompClusterNew;
     1664            pImage->cbCompCluster = cbCompressedCluster;
     1665        }
     1666        else
     1667            rc = VERR_NO_MEMORY;
     1668    }
     1669
     1670    if (RT_SUCCESS(rc))
     1671    {
     1672        rc = vdIfIoIntFileReadMeta(pImage->pIfIo, pImage->pStorage,
     1673                                   offFile, pImage->pvCompCluster,
     1674                                   cbCompressedCluster, NULL,
     1675                                   NULL, NULL, NULL);
     1676        if (RT_SUCCESS(rc))
     1677        {
     1678            if (!pImage->pvCluster)
     1679            {
     1680                pImage->pvCluster = RTMemAllocZ(pImage->cbCluster);
     1681                if (!pImage->pvCluster)
     1682                    rc = VERR_NO_MEMORY;
     1683            }
     1684
     1685            if (RT_SUCCESS(rc))
     1686            {
     1687                size_t cbDecomp = 0;
     1688
     1689                rc = RTZipBlockDecompress(RTZIPTYPE_ZLIB_NO_HEADER, 0 /*fFlags*/,
     1690                                          pImage->pvCompCluster, cbCompressedCluster, NULL,
     1691                                          pImage->pvCluster, pImage->cbCluster, &cbDecomp);
     1692                if (RT_SUCCESS(rc))
     1693                {
     1694                    Assert(cbDecomp == pImage->cbCluster);
     1695                    vdIfIoIntIoCtxCopyTo(pImage->pIfIo, pIoCtx,
     1696                                         (uint8_t *)pImage->pvCluster + offCluster,
     1697                                         cbToRead);
     1698                }
     1699            }
     1700        }
     1701    }
     1702
     1703    return rc;
     1704}
     1705
    15771706/** @copydoc VDIMAGEBACKEND::pfnProbe */
    15781707static DECLCALLBACK(int) qcowProbe(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
     
    17731902
    17741903static DECLCALLBACK(int) qcowRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
    1775                     PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
     1904                                  PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
    17761905{
    17771906    LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
     
    17961925
    17971926    /* Get offset in image. */
    1798     rc = qcowConvertToImageOffset(pImage, pIoCtx, idxL1, idxL2, offCluster, &offFile);
     1927    bool fCompressedCluster = false;
     1928    size_t cbCompressedCluster = 0;
     1929    rc = qcowConvertToImageOffset(pImage, pIoCtx, idxL1, idxL2, offCluster,
     1930                                  &offFile, &fCompressedCluster, &cbCompressedCluster);
    17991931    if (RT_SUCCESS(rc))
    1800         rc = vdIfIoIntFileReadUser(pImage->pIfIo, pImage->pStorage, offFile,
    1801                                    pIoCtx, cbToRead);
     1932    {
     1933        if (!fCompressedCluster)
     1934            rc = vdIfIoIntFileReadUser(pImage->pIfIo, pImage->pStorage, offFile,
     1935                                       pIoCtx, cbToRead);
     1936        else
     1937            rc = qcowReadCompressedCluster(pImage, pIoCtx, offCluster, cbToRead, offFile, cbCompressedCluster);
     1938    }
    18021939
    18031940    if (   (   RT_SUCCESS(rc)
     
    18121949
    18131950static DECLCALLBACK(int) qcowWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
    1814                      PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
    1815                      size_t *pcbPostRead, unsigned fWrite)
     1951                                   PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
     1952                                   size_t *pcbPostRead, unsigned fWrite)
    18161953{
    18171954    LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
     
    18401977
    18411978        /* Get offset in image. */
    1842         rc = qcowConvertToImageOffset(pImage, pIoCtx, idxL1, idxL2, offCluster, &offImage);
     1979        bool fCompressedCluster = false;
     1980        size_t cbCompressedCluster = 0;
     1981        rc = qcowConvertToImageOffset(pImage, pIoCtx, idxL1, idxL2, offCluster,
     1982                                      &offImage, &fCompressedCluster, &cbCompressedCluster);
    18431983        if (RT_SUCCESS(rc))
    1844             rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
    1845                                         offImage, pIoCtx, cbToWrite, NULL, NULL);
     1984        {
     1985            if (!fCompressedCluster)
     1986                rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
     1987                                            offImage, pIoCtx, cbToWrite, NULL, NULL);
     1988            else
     1989                rc = VERR_NOT_SUPPORTED; /** @todo Support writing compressed clusters */
     1990        }
    18461991        else if (rc == VERR_VD_BLOCK_FREE)
    18471992        {
Note: See TracChangeset for help on using the changeset viewer.

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