VirtualBox

Changeset 96832 in vbox for trunk/src/VBox/Storage/VMDK.cpp


Ignore:
Timestamp:
Sep 22, 2022 7:02:09 PM (2 years ago)
Author:
vboxsync
Message:

Storage: added resize functionality for VMDK monolithicFlat and twoGbMaxExtentFlat bugref:8707

File:
1 edited

Legend:

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

    r96407 r96832  
    146146 */
    147147#define VMDK_SPARSE_MAGICNUMBER 0x564d444b /* 'V' 'M' 'D' 'K' */
     148/** VMDK sector size in bytes. */
     149#define VMDK_SECTOR_SIZE 512
     150/** Max string buffer size for uint64_t with null term */
     151#define UINT64_MAX_BUFF_SIZE 21
     152/** Grain directory entry size in bytes */
     153#define VMDK_GRAIN_DIR_ENTRY_SIZE 4
     154/** Grain table size in bytes */
     155#define VMDK_GRAIN_TABLE_SIZE 2048
    148156/**
    149157 * VMDK hosted binary extent header. The "Sparse" is a total misnomer, as
     
    12381246 */
    12391247static int vmdkCreateGrainDirectory(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
    1240                                     uint64_t uStartSector, bool fPreAlloc)
     1248                                    uint64_t uStartSector, bool fPreAlloc,
     1249                                    bool fExisting = false)
    12411250{
    12421251    int rc = VINF_SUCCESS;
     
    12711280        cbOverhead = RT_ALIGN_64(cbOverhead,
    12721281                                 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
    1273         rc = vdIfIoIntFileSetSize(pImage->pIfIo, pExtent->pFile->pStorage, cbOverhead);
     1282
     1283        if (!fExisting)
     1284            rc = vdIfIoIntFileSetSize(pImage->pIfIo, pExtent->pFile->pStorage, cbOverhead);
    12741285    }
    12751286    if (RT_SUCCESS(rc))
     
    28062817        pImage->pExtents = pExtents;
    28072818        pImage->cExtents = cExtents;
     2819    }
     2820    else
     2821        rc = VERR_NO_MEMORY;
     2822    return rc;
     2823}
     2824/**
     2825 * Internal: allocate and describes an additional, file-backed extent
     2826 * for the given size. Preserves original extents.
     2827 */
     2828static int vmdkAddFileBackedExtent(PVMDKIMAGE pImage, uint64_t cbSize)
     2829{
     2830    int rc = VINF_SUCCESS;
     2831    PVMDKEXTENT pNewExtents = (PVMDKEXTENT)RTMemAllocZ((pImage->cExtents + 1) * sizeof(VMDKEXTENT));
     2832    if (pNewExtents)
     2833    {
     2834        memcpy(pNewExtents, pImage->pExtents, pImage->cExtents * sizeof(VMDKEXTENT));
     2835        PVMDKEXTENT pExtent = &pNewExtents[pImage->cExtents];
     2836
     2837        pExtent->pFile = NULL;
     2838        pExtent->pszBasename = NULL;
     2839        pExtent->pszFullname = NULL;
     2840        pExtent->pGD = NULL;
     2841        pExtent->pRGD = NULL;
     2842        pExtent->pDescData = NULL;
     2843        pExtent->uVersion = 1;
     2844        pExtent->uCompression = VMDK_COMPRESSION_NONE;
     2845        pExtent->uExtent = pImage->cExtents;
     2846        pExtent->pImage = pImage;
     2847        pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize);
     2848        pExtent->enmType = VMDKETYPE_FLAT;
     2849        pExtent->enmAccess = VMDKACCESS_READWRITE;
     2850        pExtent->uSectorOffset = 0;
     2851       
     2852        char *pszBasenameSubstr = RTPathFilename(pImage->pszFilename);
     2853        AssertPtr(pszBasenameSubstr);
     2854
     2855        char *pszBasenameSuff = RTPathSuffix(pszBasenameSubstr);
     2856        char *pszBasenameBase = RTStrDup(pszBasenameSubstr);
     2857        RTPathStripSuffix(pszBasenameBase);
     2858        char *pszTmp;
     2859        size_t cbTmp;
     2860
     2861        if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
     2862            RTStrAPrintf(&pszTmp, "%s-f%03d%s", pszBasenameBase,
     2863                         pExtent->uExtent + 1, pszBasenameSuff);
     2864        else
     2865            RTStrAPrintf(&pszTmp, "%s-s%03d%s", pszBasenameBase, pExtent->uExtent + 1,
     2866                         pszBasenameSuff);
     2867
     2868        RTStrFree(pszBasenameBase);
     2869        if (!pszTmp)
     2870            return VERR_NO_STR_MEMORY;
     2871        cbTmp = strlen(pszTmp) + 1;
     2872        char *pszBasename = (char *)RTMemTmpAlloc(cbTmp);
     2873        if (!pszBasename)
     2874        {
     2875            RTStrFree(pszTmp);
     2876            return VERR_NO_MEMORY;
     2877        }
     2878
     2879        memcpy(pszBasename, pszTmp, cbTmp);
     2880        RTStrFree(pszTmp);
     2881
     2882        pExtent->pszBasename = pszBasename;
     2883
     2884        char *pszBasedirectory = RTStrDup(pImage->pszFilename);
     2885        if (!pszBasedirectory)
     2886            return VERR_NO_STR_MEMORY;
     2887        RTPathStripFilename(pszBasedirectory);
     2888        char *pszFullname = RTPathJoinA(pszBasedirectory, pExtent->pszBasename);
     2889        RTStrFree(pszBasedirectory);
     2890        if (!pszFullname)
     2891            return VERR_NO_STR_MEMORY;
     2892        pExtent->pszFullname = pszFullname;
     2893
     2894        /* Create file for extent. */
     2895        rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszBasename, pExtent->pszFullname,
     2896                          VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
     2897                                                     true /* fCreate */));
     2898        if (RT_FAILURE(rc))
     2899            return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pExtent->pszFullname);
     2900
     2901        rc = vmdkDescExtInsert(pImage, &pImage->Descriptor, pExtent->enmAccess,
     2902                               pExtent->cNominalSectors, pExtent->enmType,
     2903                               pExtent->pszBasename, pExtent->uSectorOffset);
     2904        if (RT_FAILURE(rc))
     2905            return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not insert the extent list into descriptor in '%s'"), pImage->pszFilename);
     2906
     2907        rc = vdIfIoIntFileSetAllocationSize(pImage->pIfIo, pExtent->pFile->pStorage, cbSize,
     2908                                            0 /* fFlags */, NULL, 0, 0);
     2909
     2910        if (RT_FAILURE(rc))
     2911            return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not set size of new file '%s'"), pExtent->pszFullname);
     2912
     2913        pImage->pExtents = pNewExtents;
     2914        pImage->cExtents++;
    28082915    }
    28092916    else
     
    74007507    vdIfErrorMessage(pImage->pIfError, "Header: uuidParentModification={%RTuuid}\n", &pImage->ParentModificationUuid);
    74017508}
     7509
     7510static int vmdkRepaceExtentSize(PVMDKIMAGE pImage, unsigned line, uint64_t cSectorsOld,
     7511                                uint64_t cSectorsNew)
     7512{
     7513    char * szOldExtentSectors = (char *)RTMemAlloc(UINT64_MAX_BUFF_SIZE);
     7514    if (!szOldExtentSectors)
     7515        return VERR_NO_MEMORY;
     7516
     7517    int cbWritten = RTStrPrintf2(szOldExtentSectors, UINT64_MAX_BUFF_SIZE, "%llu", cSectorsOld);
     7518    if (cbWritten <= 0 || cbWritten > UINT64_MAX_BUFF_SIZE)
     7519    {
     7520        RTMemFree(szOldExtentSectors);
     7521        szOldExtentSectors = NULL;
     7522
     7523        return VERR_BUFFER_OVERFLOW;
     7524    }
     7525   
     7526    char * szNewExtentSectors = (char *)RTMemAlloc(UINT64_MAX_BUFF_SIZE);
     7527    if (!szNewExtentSectors)
     7528        return VERR_NO_MEMORY;
     7529
     7530    cbWritten = RTStrPrintf2(szNewExtentSectors, UINT64_MAX_BUFF_SIZE, "%llu", cSectorsNew);
     7531    if (cbWritten <= 0 || cbWritten > UINT64_MAX_BUFF_SIZE)
     7532    {
     7533        RTMemFree(szOldExtentSectors);
     7534        szOldExtentSectors = NULL;
     7535
     7536        RTMemFree(szNewExtentSectors);
     7537        szNewExtentSectors = NULL;
     7538
     7539        return VERR_BUFFER_OVERFLOW;
     7540    }
     7541
     7542    char * szNewExtentLine = vmdkStrReplace(pImage->Descriptor.aLines[line],
     7543                                            szOldExtentSectors,
     7544                                            szNewExtentSectors);
     7545
     7546    RTMemFree(szOldExtentSectors);
     7547    szOldExtentSectors = NULL;
     7548
     7549    RTMemFree(szNewExtentSectors);
     7550    szNewExtentSectors = NULL;
     7551
     7552    if (!szNewExtentLine)
     7553        return VERR_INVALID_PARAMETER;
     7554
     7555    pImage->Descriptor.aLines[line] = szNewExtentLine;
     7556
     7557    return VINF_SUCCESS;
     7558}
     7559
     7560/** @copydoc VDIMAGEBACKEND::pfnResize */
     7561static DECLCALLBACK(int) vmdkResize(void *pBackendData, uint64_t cbSize,
     7562                                   PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
     7563                                   unsigned uPercentStart, unsigned uPercentSpan,
     7564                                   PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
     7565                                   PVDINTERFACE pVDIfsOperation)
     7566{
     7567    // Establish variables and objects needed
     7568    int rc = VINF_SUCCESS;
     7569    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
     7570    unsigned uImageFlags = pImage->uImageFlags;
     7571    PVMDKEXTENT pExtent = &pImage->pExtents[0];
     7572
     7573    uint64_t cSectorsNew = cbSize / VMDK_SECTOR_SIZE;   /** < New number of sectors in the image after the resize */
     7574    if (cbSize % VMDK_SECTOR_SIZE)
     7575        cSectorsNew++;
     7576
     7577    uint64_t cSectorsOld = pImage->cbSize / VMDK_SECTOR_SIZE; /** < Number of sectors before the resize. Only for FLAT images. */
     7578    if (pImage->cbSize % VMDK_SECTOR_SIZE)
     7579        cSectorsOld++;
     7580    unsigned cExtents = pImage->cExtents;
     7581
     7582    /* Check size is within min/max bounds. */
     7583    if ( !(uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK)
     7584        && (   !cbSize
     7585            || (!(uImageFlags & VD_IMAGE_FLAGS_FIXED) && cbSize >= _1T * 256 - _64K)) )
     7586        return VERR_VD_INVALID_SIZE;
     7587
     7588    /*
     7589     * Making the image smaller is not supported at the moment.
     7590     */
     7591    /** @todo implement making the image smaller, it is the responsibility of
     7592     * the user to know what he's doing. */
     7593    if (cbSize < pImage->cbSize)
     7594        rc = VERR_VD_SHRINK_NOT_SUPPORTED;
     7595    else if (cbSize > pImage->cbSize)
     7596    {
     7597        /**
     7598         * monolithicFlat. FIXED flag and not split up into 2 GB parts.
     7599         */
     7600        if ((uImageFlags & VD_IMAGE_FLAGS_FIXED) && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G))
     7601        {
     7602            /** Required space in bytes for the extent after the resize. */
     7603            uint64_t cbSectorSpaceNew = cSectorsNew * VMDK_SECTOR_SIZE;
     7604            pExtent = &pImage->pExtents[0];
     7605
     7606            rc = vdIfIoIntFileSetAllocationSize(pImage->pIfIo, pExtent->pFile->pStorage, cbSectorSpaceNew,
     7607                                                0 /* fFlags */, NULL,
     7608                                                uPercentStart, uPercentSpan);
     7609            if (RT_FAILURE(rc))
     7610                return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not set size of new file '%s'"), pExtent->pszFullname);
     7611
     7612            rc = vmdkRepaceExtentSize(pImage, pImage->Descriptor.uFirstExtent, cSectorsOld, cSectorsNew);
     7613            if (RT_FAILURE(rc))
     7614                return rc;
     7615        }
     7616
     7617        /**
     7618         * twoGbMaxExtentFlat. FIXED flag and SPLIT into 2 GB parts.
     7619         */
     7620        if ((uImageFlags & VD_IMAGE_FLAGS_FIXED) && (uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G))
     7621        {
     7622            /* Check to see how much space remains in last extent */
     7623            bool fSpaceAvailible = false;
     7624            uint64_t cLastExtentRemSectors = cSectorsOld % VMDK_BYTE2SECTOR(VMDK_2G_SPLIT_SIZE);
     7625            if (cLastExtentRemSectors)
     7626                fSpaceAvailible = true;
     7627
     7628            uint64_t cSectorsNeeded = cSectorsNew - cSectorsOld;
     7629            if (fSpaceAvailible && cSectorsNeeded + cLastExtentRemSectors <= VMDK_BYTE2SECTOR(VMDK_2G_SPLIT_SIZE))
     7630            {
     7631                pExtent = &pImage->pExtents[cExtents - 1];
     7632                rc = vdIfIoIntFileSetAllocationSize(pImage->pIfIo, pExtent->pFile->pStorage,
     7633                                                    VMDK_SECTOR2BYTE(cSectorsNeeded + cLastExtentRemSectors),
     7634                                                    0 /* fFlags */, NULL, uPercentStart, uPercentSpan);
     7635                if (RT_FAILURE(rc))
     7636                    return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not set size of new file '%s'"), pExtent->pszFullname);
     7637           
     7638                rc = vmdkRepaceExtentSize(pImage, pImage->Descriptor.uFirstExtent + cExtents - 1,
     7639                                          pExtent->cNominalSectors, cSectorsNeeded + cLastExtentRemSectors);
     7640                if (RT_FAILURE(rc))
     7641                    return rc;
     7642            }
     7643            else
     7644            {
     7645                if (fSpaceAvailible)
     7646                {
     7647                    pExtent = &pImage->pExtents[cExtents - 1];
     7648                    rc = vdIfIoIntFileSetAllocationSize(pImage->pIfIo, pExtent->pFile->pStorage, VMDK_2G_SPLIT_SIZE,
     7649                                                        0 /* fFlags */, NULL,
     7650                                                        uPercentStart, uPercentSpan);
     7651                    if (RT_FAILURE(rc))
     7652                        return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not set size of new file '%s'"), pExtent->pszFullname);
     7653
     7654                    cSectorsNeeded = cSectorsNeeded - VMDK_BYTE2SECTOR(VMDK_2G_SPLIT_SIZE) + cLastExtentRemSectors;
     7655                   
     7656                    rc = vmdkRepaceExtentSize(pImage, pImage->Descriptor.uFirstExtent + cExtents - 1,
     7657                                              pExtent->cNominalSectors, VMDK_BYTE2SECTOR(VMDK_2G_SPLIT_SIZE));
     7658                    if (RT_FAILURE(rc))
     7659                        return rc;
     7660                }
     7661
     7662                unsigned cNewExtents = VMDK_SECTOR2BYTE(cSectorsNeeded) / VMDK_2G_SPLIT_SIZE;
     7663                if (cNewExtents % VMDK_2G_SPLIT_SIZE || cNewExtents < VMDK_2G_SPLIT_SIZE)
     7664                    cNewExtents++;
     7665
     7666                for (unsigned i = cExtents;
     7667                     i < cExtents + cNewExtents && cSectorsNeeded >= VMDK_BYTE2SECTOR(VMDK_2G_SPLIT_SIZE);
     7668                     i++)
     7669                {
     7670                    rc = vmdkAddFileBackedExtent(pImage, VMDK_2G_SPLIT_SIZE);
     7671                    if (RT_FAILURE(rc))
     7672                        return rc;
     7673
     7674                    pExtent = &pImage->pExtents[i];
     7675
     7676                    pExtent->cSectors = VMDK_BYTE2SECTOR(VMDK_2G_SPLIT_SIZE);
     7677                    cSectorsNeeded -= VMDK_BYTE2SECTOR(VMDK_2G_SPLIT_SIZE);
     7678                }
     7679
     7680                if (cSectorsNeeded)
     7681                {
     7682                    rc = vmdkAddFileBackedExtent(pImage, VMDK_SECTOR2BYTE(cSectorsNeeded));
     7683                    if (RT_FAILURE(rc))
     7684                        return rc;
     7685                }
     7686            }
     7687        }
     7688
     7689        /* Successful resize. Update metadata */
     7690        if (RT_SUCCESS(rc))
     7691        {
     7692            /* Update size and new block count. */
     7693            pImage->cbSize = cbSize;
     7694            /** @todo r=jack: update cExtents if needed */
     7695            pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize);
     7696
     7697            /* Update geometry. */
     7698            pImage->PCHSGeometry = *pPCHSGeometry;
     7699            pImage->LCHSGeometry = *pLCHSGeometry;
     7700        }
     7701
     7702        /* Update header information in base image file. */
     7703        rc = vmdkWriteDescriptor(pImage, NULL);
     7704
     7705        if (RT_FAILURE(rc))
     7706            return rc;
     7707
     7708        rc = vmdkFlushImage(pImage, NULL);
     7709
     7710        if (RT_FAILURE(rc))
     7711            return rc;
     7712    }
     7713    /* Same size doesn't change the image at all. */
     7714
     7715    LogFlowFunc(("returns %Rrc\n", rc));
     7716    return rc;
     7717}
     7718
    74027719const VDIMAGEBACKEND g_VmdkBackend =
    74037720{
     
    74937810    NULL,
    74947811    /* pfnResize */
    7495     NULL,
     7812    vmdkResize,
    74967813    /* pfnRepair */
    74977814    NULL,
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