~ubuntu-branches/ubuntu/oneiric/virtualbox/oneiric-updates

« back to all changes in this revision

Viewing changes to src/VBox/Storage/VD.cpp

  • Committer: Package Import Robot
  • Author(s): Felix Geyer
  • Date: 2011-09-02 11:50:47 UTC
  • mfrom: (3.1.4 sid)
  • Revision ID: package-import@ubuntu.com-20110902115047-kfhmsikrpydgyoji
Tags: 4.1.2-dfsg-1ubuntu1
* Merge from Debian unstable, remaining changes:
  - Add Apport hook.
    - debian/virtualbox.files/source_virtualbox.py
    - debian/virtualbox.install
  - Drop *-source packages.
  - Add vboxguest modalias the to the package control field.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id: VD.cpp 37329 2011-06-06 15:43:39Z vboxsync $ */
 
1
/* $Id: VD.cpp 38449 2011-08-14 10:53:13Z vboxsync $ */
2
2
/** @file
3
3
 * VBoxHDD - VBox HDD Container implementation.
4
4
 */
808
808
}
809
809
 
810
810
/**
811
 
 * internal: read the specified amount of data in whatever blocks the backend
812
 
 * will give us.
 
811
 * Extended version of vdReadHelper(), implementing certain optimizations
 
812
 * for image cloning.
 
813
 *
 
814
 * @returns VBox status code.
 
815
 * @param   pDisk                   The disk to read from.
 
816
 * @param   pImage                  The image to start reading from.
 
817
 * @param   pImageParentOverride    The parent image to read from
 
818
 *                                  if the starting image returns a free block.
 
819
 *                                  If NULL is passed the real parent of the image
 
820
 *                                  in the chain is used.
 
821
 * @param   uOffset                 Offset in the disk to start reading from.
 
822
 * @param   pvBuf                   Where to store the read data.
 
823
 * @param   cbRead                  How much to read.
 
824
 * @param   fZeroFreeBlocks         Flag whether free blocks should be zeroed.
 
825
 *                                  If false and no image has data for sepcified
 
826
 *                                  range VERR_VD_BLOCK_FREE is returned.
 
827
 *                                  Note that unallocated blocks are still zeroed
 
828
 *                                  if at least one image has valid data for a part
 
829
 *                                  of the range.
 
830
 * @param   fUpdateCache            Flag whether to update the attached cache if
 
831
 *                                  available.
 
832
 * @param   cImagesRead             Number of images in the chain to read until
 
833
 *                                  the read is cut off. A value of 0 disables the cut off.
813
834
 */
814
 
static int vdReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
815
 
                        uint64_t uOffset, void *pvBuf, size_t cbRead,
816
 
                        bool fZeroFreeBlocks, bool fUpdateCache)
 
835
static int vdReadHelperEx(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
 
836
                          uint64_t uOffset, void *pvBuf, size_t cbRead,
 
837
                          bool fZeroFreeBlocks, bool fUpdateCache, unsigned cImagesRead)
817
838
{
818
839
    int rc = VINF_SUCCESS;
819
840
    size_t cbThisRead;
836
857
 
837
858
            if (rc == VERR_VD_BLOCK_FREE)
838
859
            {
839
 
                rc = vdDiskReadHelper(pDisk, pImage, pImageParentOverride,
840
 
                                      uOffset, pvBuf, cbThisRead, &cbThisRead);
 
860
                rc = vdDiskReadHelper(pDisk, pImage, NULL, uOffset, pvBuf, cbThisRead,
 
861
                                      &cbThisRead);
841
862
 
842
863
                /* If the read was successful, write the data back into the cache. */
843
864
                if (   RT_SUCCESS(rc)
861
882
                                          uOffset, pvBuf, cbThisRead,
862
883
                                          &cbThisRead);
863
884
 
864
 
            if (rc == VERR_VD_BLOCK_FREE)
 
885
            if (   rc == VERR_VD_BLOCK_FREE
 
886
                && cImagesRead != 1)
865
887
            {
 
888
                unsigned cImagesToProcess = cImagesRead;
 
889
 
866
890
                for (PVDIMAGE pCurrImage = pImageParentOverride ? pImageParentOverride : pImage->pPrev;
867
891
                     pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
868
892
                     pCurrImage = pCurrImage->pPrev)
870
894
                    rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
871
895
                                                      uOffset, pvBuf, cbThisRead,
872
896
                                                      &cbThisRead);
 
897
                    if (cImagesToProcess == 1)
 
898
                        break;
 
899
                    else if (cImagesToProcess > 0)
 
900
                        cImagesToProcess--;
873
901
                }
874
902
            }
875
903
        }
905
933
    return (!fZeroFreeBlocks && fAllFree) ? VERR_VD_BLOCK_FREE : rc;
906
934
}
907
935
 
 
936
/**
 
937
 * internal: read the specified amount of data in whatever blocks the backend
 
938
 * will give us.
 
939
 */
 
940
static int vdReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, uint64_t uOffset,
 
941
                        void *pvBuf, size_t cbRead, bool fUpdateCache)
 
942
{
 
943
    return vdReadHelperEx(pDisk, pImage, NULL, uOffset, pvBuf, cbRead,
 
944
                          true /* fZeroFreeBlocks */, fUpdateCache, 0);
 
945
}
 
946
 
908
947
DECLINLINE(PVDIOCTX) vdIoCtxAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
909
948
                                  uint64_t uOffset, size_t cbTransfer,
910
949
                                  PVDIMAGE pImageStart,
1376
1415
                        size_t cbRead)
1377
1416
{
1378
1417
    PVDPARENTSTATEDESC pParentState = (PVDPARENTSTATEDESC)pvUser;
1379
 
    return vdReadHelper(pParentState->pDisk, pParentState->pImage, NULL, uOffset,
1380
 
                        pvBuf, cbRead, true /* fZeroFreeBlocks */,
1381
 
                        false /* fUpdateCache */);
 
1418
    return vdReadHelper(pParentState->pDisk, pParentState->pImage, uOffset,
 
1419
                        pvBuf, cbRead, false /* fUpdateCache */);
1382
1420
}
1383
1421
 
1384
1422
/**
1446
1484
         * Updating the cache doesn't make sense here because
1447
1485
         * this will be done after the complete block was written.
1448
1486
         */
1449
 
        rc = vdReadHelper(pDisk, pImage, pImageParentOverride,
1450
 
                          uOffset - cbPreRead, pvTmp, cbPreRead,
1451
 
                          true /* fZeroFreeBlocks*/,
1452
 
                          false /* fUpdateCache */);
 
1487
        rc = vdReadHelperEx(pDisk, pImage, pImageParentOverride,
 
1488
                            uOffset - cbPreRead, pvTmp, cbPreRead,
 
1489
                            true /* fZeroFreeBlocks*/,
 
1490
                            false /* fUpdateCache */, 0);
1453
1491
        if (RT_FAILURE(rc))
1454
1492
            return rc;
1455
1493
    }
1483
1521
            memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
1484
1522
                   (char *)pvBuf + cbThisWrite, cbWriteCopy);
1485
1523
        if (cbReadImage)
1486
 
            rc = vdReadHelper(pDisk, pImage, pImageParentOverride,
1487
 
                              uOffset + cbThisWrite + cbWriteCopy,
1488
 
                              (char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy,
1489
 
                              cbReadImage, true /* fZeroFreeBlocks */,
1490
 
                              false /* fUpdateCache */);
 
1524
            rc = vdReadHelperEx(pDisk, pImage, pImageParentOverride,
 
1525
                                uOffset + cbThisWrite + cbWriteCopy,
 
1526
                                (char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy,
 
1527
                                cbReadImage, true /* fZeroFreeBlocks */,
 
1528
                                false /* fUpdateCache */, 0);
1491
1529
        if (RT_FAILURE(rc))
1492
1530
            return rc;
1493
1531
        /* Zero out the remainder of this block. Will never be visible, as this
1520
1558
                                  uint64_t uOffset, size_t cbWrite,
1521
1559
                                  size_t cbThisWrite, size_t cbPreRead,
1522
1560
                                  size_t cbPostRead, const void *pvBuf,
1523
 
                                  void *pvTmp)
 
1561
                                  void *pvTmp, unsigned cImagesRead)
1524
1562
{
1525
1563
    size_t cbFill = 0;
1526
1564
    size_t cbWriteCopy = 0;
1546
1584
 
1547
1585
    /* Read the entire data of the block so that we can compare whether it will
1548
1586
     * be modified by the write or not. */
1549
 
    rc = vdReadHelper(pDisk, pImage, pImageParentOverride, uOffset - cbPreRead, pvTmp,
1550
 
                      cbPreRead + cbThisWrite + cbPostRead - cbFill,
1551
 
                      true /* fZeroFreeBlocks */,
1552
 
                      false /* fUpdateCache */);
 
1587
    rc = vdReadHelperEx(pDisk, pImage, pImageParentOverride, uOffset - cbPreRead, pvTmp,
 
1588
                        cbPreRead + cbThisWrite + cbPostRead - cbFill,
 
1589
                        true /* fZeroFreeBlocks */, false /* fUpdateCache */,
 
1590
                        cImagesRead);
1553
1591
    if (RT_FAILURE(rc))
1554
1592
        return rc;
1555
1593
 
1595
1633
 * internal: write buffer to the image, taking care of block boundaries and
1596
1634
 * write optimizations.
1597
1635
 */
1598
 
static int vdWriteHelper(PVBOXHDD pDisk, PVDIMAGE pImage,
1599
 
                         PVDIMAGE pImageParentOverride, uint64_t uOffset,
1600
 
                         const void *pvBuf, size_t cbWrite,
1601
 
                         bool fUpdateCache)
 
1636
static int vdWriteHelperEx(PVBOXHDD pDisk, PVDIMAGE pImage,
 
1637
                           PVDIMAGE pImageParentOverride, uint64_t uOffset,
 
1638
                           const void *pvBuf, size_t cbWrite,
 
1639
                           bool fUpdateCache, unsigned cImagesRead)
1602
1640
{
1603
1641
    int rc;
1604
1642
    unsigned fWrite;
1635
1673
                rc = vdWriteHelperOptimized(pDisk, pImage, pImageParentOverride,
1636
1674
                                            uOffsetCur, cbWriteCur,
1637
1675
                                            cbThisWrite, cbPreRead, cbPostRead,
1638
 
                                            pcvBufCur, pvTmp);
 
1676
                                            pcvBufCur, pvTmp, cImagesRead);
1639
1677
            }
1640
1678
            else
1641
1679
            {
1668
1706
}
1669
1707
 
1670
1708
/**
 
1709
 * internal: write buffer to the image, taking care of block boundaries and
 
1710
 * write optimizations.
 
1711
 */
 
1712
static int vdWriteHelper(PVBOXHDD pDisk, PVDIMAGE pImage, uint64_t uOffset,
 
1713
                         const void *pvBuf, size_t cbWrite, bool fUpdateCache)
 
1714
{
 
1715
    return vdWriteHelperEx(pDisk, pImage, NULL, uOffset, pvBuf, cbWrite,
 
1716
                           fUpdateCache, 0);
 
1717
}
 
1718
 
 
1719
/**
 
1720
 * Internal: Copies the content of one disk to another one applying optimizations
 
1721
 * to speed up the copy process if possible.
 
1722
 */
 
1723
static int vdCopyHelper(PVBOXHDD pDiskFrom, PVDIMAGE pImageFrom, PVBOXHDD pDiskTo,
 
1724
                        uint64_t cbSize, unsigned cImagesFromRead, unsigned cImagesToRead,
 
1725
                        bool fSuppressRedundantIo,
 
1726
                        PVDINTERFACE pIfProgress, PVDINTERFACEPROGRESS pCbProgress,
 
1727
                        PVDINTERFACE pDstIfProgress, PVDINTERFACEPROGRESS pDstCbProgress)
 
1728
{
 
1729
    int rc = VINF_SUCCESS;
 
1730
    int rc2;
 
1731
    uint64_t uOffset = 0;
 
1732
    uint64_t cbRemaining = cbSize;
 
1733
    void *pvBuf = NULL;
 
1734
    bool fLockReadFrom = false;
 
1735
    bool fLockWriteTo = false;
 
1736
    bool fBlockwiseCopy = fSuppressRedundantIo || (cImagesFromRead > 0);
 
1737
    unsigned uProgressOld = 0;
 
1738
 
 
1739
    LogFlowFunc(("pDiskFrom=%#p pImageFrom=%#p pDiskTo=%#p cbSize=%llu cImagesFromRead=%u cImagesToRead=%u fSuppressRedundantIo=%RTbool pIfProgress=%#p pCbProgress=%#p pDstIfProgress=%#p pDstCbProgress=%#p\n",
 
1740
                 pDiskFrom, pImageFrom, pDiskTo, cbSize, cImagesFromRead, cImagesToRead, fSuppressRedundantIo, pIfProgress, pCbProgress, pDstIfProgress, pDstCbProgress));
 
1741
 
 
1742
    /* Allocate tmp buffer. */
 
1743
    pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
 
1744
    if (!pvBuf)
 
1745
        return rc;
 
1746
 
 
1747
    do
 
1748
    {
 
1749
        size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
 
1750
 
 
1751
        /* Note that we don't attempt to synchronize cross-disk accesses.
 
1752
         * It wouldn't be very difficult to do, just the lock order would
 
1753
         * need to be defined somehow to prevent deadlocks. Postpone such
 
1754
         * magic as there is no use case for this. */
 
1755
 
 
1756
        rc2 = vdThreadStartRead(pDiskFrom);
 
1757
        AssertRC(rc2);
 
1758
        fLockReadFrom = true;
 
1759
 
 
1760
        if (fBlockwiseCopy)
 
1761
        {
 
1762
            /* Read the source data. */
 
1763
            rc = pImageFrom->Backend->pfnRead(pImageFrom->pBackendData,
 
1764
                                              uOffset, pvBuf, cbThisRead,
 
1765
                                              &cbThisRead);
 
1766
 
 
1767
            if (   rc == VERR_VD_BLOCK_FREE
 
1768
                && cImagesFromRead != 1)
 
1769
            {
 
1770
                unsigned cImagesToProcess = cImagesFromRead;
 
1771
 
 
1772
                for (PVDIMAGE pCurrImage = pImageFrom->pPrev;
 
1773
                     pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
 
1774
                     pCurrImage = pCurrImage->pPrev)
 
1775
                {
 
1776
                    rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
 
1777
                                                      uOffset, pvBuf, cbThisRead,
 
1778
                                                      &cbThisRead);
 
1779
                    if (cImagesToProcess == 1)
 
1780
                        break;
 
1781
                    else if (cImagesToProcess > 0)
 
1782
                        cImagesToProcess--;
 
1783
                }
 
1784
            }
 
1785
        }
 
1786
        else
 
1787
            rc = vdReadHelper(pDiskFrom, pImageFrom, uOffset, pvBuf, cbThisRead,
 
1788
                              false /* fUpdateCache */);
 
1789
 
 
1790
        if (RT_FAILURE(rc) && rc != VERR_VD_BLOCK_FREE)
 
1791
            break;
 
1792
 
 
1793
        rc2 = vdThreadFinishRead(pDiskFrom);
 
1794
        AssertRC(rc2);
 
1795
        fLockReadFrom = false;
 
1796
 
 
1797
        if (rc != VERR_VD_BLOCK_FREE)
 
1798
        {
 
1799
            rc2 = vdThreadStartWrite(pDiskTo);
 
1800
            AssertRC(rc2);
 
1801
            fLockWriteTo = true;
 
1802
 
 
1803
            /* Only do collapsed I/O if we are copying the data blockwise. */
 
1804
            rc = vdWriteHelperEx(pDiskTo, pDiskTo->pLast, NULL, uOffset, pvBuf,
 
1805
                                 cbThisRead, false /* fUpdateCache */,
 
1806
                                 fBlockwiseCopy ? cImagesToRead : 0);
 
1807
            if (RT_FAILURE(rc))
 
1808
                break;
 
1809
 
 
1810
            rc2 = vdThreadFinishWrite(pDiskTo);
 
1811
            AssertRC(rc2);
 
1812
            fLockWriteTo = false;
 
1813
        }
 
1814
        else /* Don't propagate the error to the outside */
 
1815
            rc = VINF_SUCCESS;
 
1816
 
 
1817
        uOffset += cbThisRead;
 
1818
        cbRemaining -= cbThisRead;
 
1819
 
 
1820
        unsigned uProgressNew = uOffset * 99 / cbSize;
 
1821
        if (uProgressNew != uProgressOld)
 
1822
        {
 
1823
            uProgressOld = uProgressNew;
 
1824
 
 
1825
            if (pCbProgress && pCbProgress->pfnProgress)
 
1826
            {
 
1827
                rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
 
1828
                                              uProgressOld);
 
1829
                if (RT_FAILURE(rc))
 
1830
                    break;
 
1831
            }
 
1832
            if (pDstCbProgress && pDstCbProgress->pfnProgress)
 
1833
            {
 
1834
                rc = pDstCbProgress->pfnProgress(pDstIfProgress->pvUser,
 
1835
                                                 uProgressOld);
 
1836
                if (RT_FAILURE(rc))
 
1837
                    break;
 
1838
            }
 
1839
        }
 
1840
    } while (uOffset < cbSize);
 
1841
 
 
1842
    RTMemFree(pvBuf);
 
1843
 
 
1844
    if (fLockReadFrom)
 
1845
    {
 
1846
        rc2 = vdThreadFinishRead(pDiskFrom);
 
1847
        AssertRC(rc2);
 
1848
    }
 
1849
 
 
1850
    if (fLockWriteTo)
 
1851
    {
 
1852
        rc2 = vdThreadFinishWrite(pDiskTo);
 
1853
        AssertRC(rc2);
 
1854
    }
 
1855
 
 
1856
    LogFlowFunc(("returns rc=%Rrc\n", rc));
 
1857
    return rc;
 
1858
}
 
1859
 
 
1860
/**
1671
1861
 * Flush helper async version.
1672
1862
 */
1673
1863
static int vdSetModifiedHelperAsync(PVDIOCTX pIoCtx)
2572
2762
    PVBOXHDD pDisk = pIoCtx->pDisk;
2573
2763
    int rc = VINF_SUCCESS;
2574
2764
 
 
2765
    VD_THREAD_IS_CRITSECT_OWNER(pDisk);
 
2766
 
2575
2767
    if (RT_FAILURE(rcReq))
2576
2768
        ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rcReq, VINF_SUCCESS);
2577
2769
 
2629
2821
                }
2630
2822
 
2631
2823
                /* Process any pending writes if the current request didn't caused another growing. */
2632
 
                RTCritSectEnter(&pDisk->CritSect);
2633
 
 
2634
2824
                if (   !RTListIsEmpty(&pDisk->ListWriteLocked)
2635
2825
                    && !vdIoCtxIsDiskLockOwner(pDisk, pIoCtx))
2636
2826
                {
2666
2856
                        if (   rc == VINF_VD_ASYNC_IO_FINISHED
2667
2857
                            && ASMAtomicCmpXchgBool(&pIoCtxWait->fComplete, true, false))
2668
2858
                        {
2669
 
                            RTCritSectLeave(&pDisk->CritSect);
2670
2859
                            LogFlowFunc(("Waiting I/O context completed pIoCtxWait=%#p\n", pIoCtxWait));
2671
2860
                            vdThreadFinishWrite(pDisk);
2672
2861
                            pIoCtxWait->Type.Root.pfnComplete(pIoCtxWait->Type.Root.pvUser1,
2673
2862
                                                              pIoCtxWait->Type.Root.pvUser2,
2674
2863
                                                              pIoCtxWait->rcReq);
2675
2864
                            vdIoCtxFree(pDisk, pIoCtxWait);
2676
 
                            RTCritSectEnter(&pDisk->CritSect);
2677
2865
                        }
2678
2866
                    } while (!RTListIsEmpty(&ListTmp));
 
2867
 
 
2868
                    RTCritSectEnter(&pDisk->CritSect);
2679
2869
                }
2680
 
                else
2681
 
                    RTCritSectLeave(&pDisk->CritSect);
2682
2870
            }
2683
2871
            else
2684
2872
            {
5502
5690
                        if (RT_FAILURE(rc))
5503
5691
                            break;
5504
5692
                        /* Updating the cache is required because this might be a live merge. */
5505
 
                        rc = vdWriteHelper(pDisk, pImageTo, pImageFrom->pPrev,
5506
 
                                           uOffset, pvBuf, cbThisRead,
5507
 
                                           true /* fUpdateCache */);
 
5693
                        rc = vdWriteHelperEx(pDisk, pImageTo, pImageFrom->pPrev,
 
5694
                                             uOffset, pvBuf, cbThisRead,
 
5695
                                             true /* fUpdateCache */, 0);
5508
5696
                        if (RT_FAILURE(rc))
5509
5697
                            break;
5510
5698
                    }
5617
5805
                {
5618
5806
                    if (RT_FAILURE(rc))
5619
5807
                        break;
5620
 
                    rc = vdWriteHelper(pDisk, pImageTo, NULL, uOffset, pvBuf,
 
5808
                    rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
5621
5809
                                       cbThisRead, true /* fUpdateCache */);
5622
5810
                    if (RT_FAILURE(rc))
5623
5811
                        break;
5773
5961
}
5774
5962
 
5775
5963
/**
5776
 
 * Copies an image from one HDD container to another.
 
5964
 * Copies an image from one HDD container to another - extended version.
5777
5965
 * The copy is opened in the target HDD container.
5778
5966
 * It is possible to convert between different image formats, because the
5779
5967
 * backend for the destination may be different from the source.
5782
5970
 * The source container is unchanged if the move operation fails, otherwise
5783
5971
 * the image at the new location is opened in the same way as the old one was.
5784
5972
 *
5785
 
 * @returns VBox status code.
5786
 
 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
 
5973
 * @note The read/write accesses across disks are not synchronized, just the
 
5974
 * accesses to each disk. Once there is a use case which requires a defined
 
5975
 * read/write behavior in this situation this needs to be extended.
 
5976
 *
 
5977
 * @return  VBox status code.
 
5978
 * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5787
5979
 * @param   pDiskFrom       Pointer to source HDD container.
5788
5980
 * @param   nImage          Image number, counts from 0. 0 is always base image of container.
5789
5981
 * @param   pDiskTo         Pointer to destination HDD container.
5790
 
 * @param   pszBackend      Name of the image file backend to use.
5791
 
 * @param   pszFilename     New name of the image (may be NULL if pDiskFrom == pDiskTo).
 
5982
 * @param   pszBackend      Name of the image file backend to use (may be NULL to use the same as the source, case insensitive).
 
5983
 * @param   pszFilename     New name of the image (may be NULL to specify that the
 
5984
 *                          copy destination is the destination container, or
 
5985
 *                          if pDiskFrom == pDiskTo, i.e. when moving).
5792
5986
 * @param   fMoveByRename   If true, attempt to perform a move by renaming (if successful the new size is ignored).
5793
5987
 * @param   cbSize          New image size (0 means leave unchanged).
 
5988
 * @param   nImageSameFrom  todo
 
5989
 * @param   nImageSameTo    todo
5794
5990
 * @param   uImageFlags     Flags specifying special destination image features.
5795
5991
 * @param   pDstUuid        New UUID of the destination image. If NULL, a new UUID is created.
5796
5992
 *                          This parameter is used if and only if a true copy is created.
5797
 
 *                          In all rename/move cases the UUIDs are copied over.
 
5993
 *                          In all rename/move cases or copy to existing image cases the modification UUIDs are copied over.
5798
5994
 * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
5799
5995
 *                          Only used if the destination image is created.
5800
5996
 * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
5801
5997
 * @param   pDstVDIfsImage  Pointer to the per-image VD interface list, for the
5802
5998
 *                          destination image.
5803
 
 * @param   pDstVDIfsOperation Pointer to the per-image VD interface list,
5804
 
 *                          for the destination image.
 
5999
 * @param   pDstVDIfsOperation Pointer to the per-operation VD interface list,
 
6000
 *                          for the destination operation.
5805
6001
 */
5806
 
VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
5807
 
                         const char *pszBackend, const char *pszFilename,
5808
 
                         bool fMoveByRename, uint64_t cbSize,
5809
 
                         unsigned uImageFlags, PCRTUUID pDstUuid,
5810
 
                         unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
5811
 
                         PVDINTERFACE pDstVDIfsImage,
5812
 
                         PVDINTERFACE pDstVDIfsOperation)
 
6002
VBOXDDU_DECL(int) VDCopyEx(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
 
6003
                           const char *pszBackend, const char *pszFilename,
 
6004
                           bool fMoveByRename, uint64_t cbSize,
 
6005
                           unsigned nImageFromSame, unsigned nImageToSame,
 
6006
                           unsigned uImageFlags, PCRTUUID pDstUuid,
 
6007
                           unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
 
6008
                           PVDINTERFACE pDstVDIfsImage,
 
6009
                           PVDINTERFACE pDstVDIfsOperation)
5813
6010
{
5814
6011
    int rc = VINF_SUCCESS;
5815
6012
    int rc2;
5816
6013
    bool fLockReadFrom = false, fLockWriteFrom = false, fLockWriteTo = false;
5817
 
    void *pvBuf = NULL;
5818
6014
    PVDIMAGE pImageTo = NULL;
5819
6015
 
5820
 
    LogFlowFunc(("pDiskFrom=%#p nImage=%u pDiskTo=%#p pszBackend=\"%s\" pszFilename=\"%s\" fMoveByRename=%d cbSize=%llu uImageFlags=%#x pDstUuid=%#p uOpenFlags=%#x pVDIfsOperation=%#p pDstVDIfsImage=%#p pDstVDIfsOperation=%#p\n",
5821
 
                 pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, uImageFlags, pDstUuid, uOpenFlags, pVDIfsOperation, pDstVDIfsImage, pDstVDIfsOperation));
 
6016
    LogFlowFunc(("pDiskFrom=%#p nImage=%u pDiskTo=%#p pszBackend=\"%s\" pszFilename=\"%s\" fMoveByRename=%d cbSize=%llu nImageFromSame=%u nImageToSame=%u uImageFlags=%#x pDstUuid=%#p uOpenFlags=%#x pVDIfsOperation=%#p pDstVDIfsImage=%#p pDstVDIfsOperation=%#p\n",
 
6017
                 pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, nImageFromSame, nImageToSame, uImageFlags, pDstUuid, uOpenFlags, pVDIfsOperation, pDstVDIfsImage, pDstVDIfsOperation));
5822
6018
 
5823
6019
    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
5824
6020
                                              VDINTERFACETYPE_PROGRESS);
5848
6044
                           rc = VERR_INVALID_PARAMETER);
5849
6045
        AssertMsg(pDiskTo->u32Signature == VBOXHDDDISK_SIGNATURE,
5850
6046
                  ("u32Signature=%08x\n", pDiskTo->u32Signature));
 
6047
        AssertMsgBreakStmt(   (nImageFromSame < nImage || nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN)
 
6048
                           && (nImageToSame < pDiskTo->cImages || nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
 
6049
                           && (   (nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN && nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
 
6050
                               || (nImageFromSame != VD_IMAGE_CONTENT_UNKNOWN && nImageToSame != VD_IMAGE_CONTENT_UNKNOWN)),
 
6051
                           ("nImageFromSame=%u nImageToSame=%u\n", nImageFromSame, nImageToSame),
 
6052
                           rc = VERR_INVALID_PARAMETER);
5851
6053
 
5852
6054
        /* Move the image. */
5853
6055
        if (pDiskFrom == pDiskTo)
6007
6209
        AssertRC(rc2);
6008
6210
        fLockWriteTo = false;
6009
6211
 
6010
 
        /* Allocate tmp buffer. */
6011
 
        pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
6012
 
        if (!pvBuf)
6013
 
        {
6014
 
            rc = VERR_NO_MEMORY;
6015
 
            break;
6016
 
        }
6017
 
 
6018
6212
        /* Whether we can take the optimized copy path (false) or not.
6019
6213
         * Don't optimize if the image existed or if it is a child image. */
6020
 
        bool fRegularRead = (pszFilename == NULL) || (cImagesTo > 0);
 
6214
        bool fSuppressRedundantIo = (   !(pszFilename == NULL || cImagesTo > 0)
 
6215
                                     || (nImageToSame != VD_IMAGE_CONTENT_UNKNOWN));
 
6216
        unsigned cImagesFromReadBack, cImagesToReadBack;
 
6217
 
 
6218
        if (nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN)
 
6219
            cImagesFromReadBack = 0;
 
6220
        else
 
6221
        {
 
6222
            if (nImage == VD_LAST_IMAGE)
 
6223
                cImagesFromReadBack = pDiskFrom->cImages - nImageFromSame - 1;
 
6224
            else
 
6225
                cImagesFromReadBack = nImage - nImageFromSame;
 
6226
        }
 
6227
 
 
6228
        if (nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
 
6229
            cImagesToReadBack = 0;
 
6230
        else
 
6231
            cImagesToReadBack = pDiskTo->cImages - nImageToSame - 1;
6021
6232
 
6022
6233
        /* Copy the data. */
6023
 
        uint64_t uOffset = 0;
6024
 
        uint64_t cbRemaining = cbSize;
6025
 
 
6026
 
        do
6027
 
        {
6028
 
            size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
6029
 
 
6030
 
            /* Note that we don't attempt to synchronize cross-disk accesses.
6031
 
             * It wouldn't be very difficult to do, just the lock order would
6032
 
             * need to be defined somehow to prevent deadlocks. Postpone such
6033
 
             * magic as there is no use case for this. */
6034
 
 
6035
 
            rc2 = vdThreadStartRead(pDiskFrom);
6036
 
            AssertRC(rc2);
6037
 
            fLockReadFrom = true;
6038
 
 
6039
 
            /*
6040
 
             * Updating the cache doesn't make any sense
6041
 
             * as we are looping once through the image.
6042
 
             */
6043
 
            rc = vdReadHelper(pDiskFrom, pImageFrom, NULL, uOffset, pvBuf,
6044
 
                              cbThisRead, fRegularRead,
6045
 
                              false /* fUpdateCache */);
6046
 
            if (RT_FAILURE(rc) && rc != VERR_VD_BLOCK_FREE)
6047
 
                break;
6048
 
 
6049
 
            rc2 = vdThreadFinishRead(pDiskFrom);
6050
 
            AssertRC(rc2);
6051
 
            fLockReadFrom = false;
6052
 
 
6053
 
            if (rc != VERR_VD_BLOCK_FREE)
6054
 
            {
6055
 
                rc2 = vdThreadStartWrite(pDiskTo);
6056
 
                AssertRC(rc2);
6057
 
                fLockWriteTo = true;
6058
 
 
6059
 
                rc = vdWriteHelper(pDiskTo, pImageTo, NULL, uOffset, pvBuf,
6060
 
                                   cbThisRead, false /* fUpdateCache */);
6061
 
                if (RT_FAILURE(rc))
6062
 
                    break;
6063
 
 
6064
 
                rc2 = vdThreadFinishWrite(pDiskTo);
6065
 
                AssertRC(rc2);
6066
 
                fLockWriteTo = false;
6067
 
            }
6068
 
            else /* Don't propagate the error to the outside */
6069
 
                rc = VINF_SUCCESS;
6070
 
 
6071
 
            uOffset += cbThisRead;
6072
 
            cbRemaining -= cbThisRead;
6073
 
 
6074
 
            if (pCbProgress && pCbProgress->pfnProgress)
6075
 
            {
6076
 
                /** @todo r=klaus: this can update the progress to the same
6077
 
                 * percentage over and over again if the image format makes
6078
 
                 * relatively small increments. */
6079
 
                rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
6080
 
                                              uOffset * 99 / cbSize);
6081
 
                if (RT_FAILURE(rc))
6082
 
                    break;
6083
 
            }
6084
 
            if (pDstCbProgress && pDstCbProgress->pfnProgress)
6085
 
            {
6086
 
                /** @todo r=klaus: this can update the progress to the same
6087
 
                 * percentage over and over again if the image format makes
6088
 
                 * relatively small increments. */
6089
 
                rc = pDstCbProgress->pfnProgress(pDstIfProgress->pvUser,
6090
 
                                                 uOffset * 99 / cbSize);
6091
 
                if (RT_FAILURE(rc))
6092
 
                    break;
6093
 
            }
6094
 
        } while (uOffset < cbSize);
 
6234
        rc = vdCopyHelper(pDiskFrom, pImageFrom, pDiskTo, cbSize,
 
6235
                          cImagesFromReadBack, cImagesToReadBack,
 
6236
                          fSuppressRedundantIo, pIfProgress, pCbProgress,
 
6237
                          pDstIfProgress, pDstCbProgress);
6095
6238
 
6096
6239
        if (RT_SUCCESS(rc))
6097
6240
        {
6154
6297
        AssertRC(rc2);
6155
6298
    }
6156
6299
 
6157
 
    if (pvBuf)
6158
 
        RTMemTmpFree(pvBuf);
6159
 
 
6160
6300
    if (RT_SUCCESS(rc))
6161
6301
    {
6162
6302
        if (pCbProgress && pCbProgress->pfnProgress)
6170
6310
}
6171
6311
 
6172
6312
/**
 
6313
 * Copies an image from one HDD container to another.
 
6314
 * The copy is opened in the target HDD container.
 
6315
 * It is possible to convert between different image formats, because the
 
6316
 * backend for the destination may be different from the source.
 
6317
 * If both the source and destination reference the same HDD container,
 
6318
 * then the image is moved (by copying/deleting or renaming) to the new location.
 
6319
 * The source container is unchanged if the move operation fails, otherwise
 
6320
 * the image at the new location is opened in the same way as the old one was.
 
6321
 *
 
6322
 * @returns VBox status code.
 
6323
 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
 
6324
 * @param   pDiskFrom       Pointer to source HDD container.
 
6325
 * @param   nImage          Image number, counts from 0. 0 is always base image of container.
 
6326
 * @param   pDiskTo         Pointer to destination HDD container.
 
6327
 * @param   pszBackend      Name of the image file backend to use.
 
6328
 * @param   pszFilename     New name of the image (may be NULL if pDiskFrom == pDiskTo).
 
6329
 * @param   fMoveByRename   If true, attempt to perform a move by renaming (if successful the new size is ignored).
 
6330
 * @param   cbSize          New image size (0 means leave unchanged).
 
6331
 * @param   uImageFlags     Flags specifying special destination image features.
 
6332
 * @param   pDstUuid        New UUID of the destination image. If NULL, a new UUID is created.
 
6333
 *                          This parameter is used if and only if a true copy is created.
 
6334
 *                          In all rename/move cases the UUIDs are copied over.
 
6335
 * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
 
6336
 *                          Only used if the destination image is created.
 
6337
 * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
 
6338
 * @param   pDstVDIfsImage  Pointer to the per-image VD interface list, for the
 
6339
 *                          destination image.
 
6340
 * @param   pDstVDIfsOperation Pointer to the per-image VD interface list,
 
6341
 *                          for the destination image.
 
6342
 */
 
6343
VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
 
6344
                         const char *pszBackend, const char *pszFilename,
 
6345
                         bool fMoveByRename, uint64_t cbSize,
 
6346
                         unsigned uImageFlags, PCRTUUID pDstUuid,
 
6347
                         unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
 
6348
                         PVDINTERFACE pDstVDIfsImage,
 
6349
                         PVDINTERFACE pDstVDIfsOperation)
 
6350
{
 
6351
    return VDCopyEx(pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename,
 
6352
                    cbSize, VD_IMAGE_CONTENT_UNKNOWN, VD_IMAGE_CONTENT_UNKNOWN,
 
6353
                    uImageFlags, pDstUuid, uOpenFlags, pVDIfsOperation,
 
6354
                    pDstVDIfsImage, pDstVDIfsOperation);
 
6355
}
 
6356
 
 
6357
/**
6173
6358
 * Optimizes the storage consumption of an image. Typically the unused blocks
6174
6359
 * have to be wiped with zeroes to achieve a substantial reduced storage use.
6175
6360
 * Another optimization done is reordering the image blocks, which can provide
6685
6870
        PVDIMAGE pImage = pDisk->pLast;
6686
6871
        AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
6687
6872
 
6688
 
        rc = vdReadHelper(pDisk, pImage, NULL, uOffset, pvBuf, cbRead,
6689
 
                          true /* fZeroFreeBlocks */,
 
6873
        rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead,
6690
6874
                          true /* fUpdateCache */);
6691
6875
    } while (0);
6692
6876
 
6747
6931
        AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
6748
6932
 
6749
6933
        vdSetModifiedFlag(pDisk);
6750
 
        rc = vdWriteHelper(pDisk, pImage, NULL, uOffset, pvBuf, cbWrite,
 
6934
        rc = vdWriteHelper(pDisk, pImage, uOffset, pvBuf, cbWrite,
6751
6935
                           true /* fUpdateCache */);
6752
6936
        if (RT_FAILURE(rc))
6753
6937
            break;
6761
6945
         * to a full allocation size. The cache doesn't need to be touched
6762
6946
         * as this write is covered by the previous one. */
6763
6947
        if (RT_UNLIKELY(pDisk->pImageRelay))
6764
 
            rc = vdWriteHelper(pDisk, pDisk->pImageRelay, NULL, uOffset,
 
6948
            rc = vdWriteHelper(pDisk, pDisk->pImageRelay, uOffset,
6765
6949
                               pvBuf, cbWrite, false /* fUpdateCache */);
6766
6950
    } while (0);
6767
6951