811
* internal: read the specified amount of data in whatever blocks the backend
811
* Extended version of vdReadHelper(), implementing certain optimizations
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
830
* @param fUpdateCache Flag whether to update the attached cache if
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.
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)
818
839
int rc = VINF_SUCCESS;
819
840
size_t cbThisRead;
905
933
return (!fZeroFreeBlocks && fAllFree) ? VERR_VD_BLOCK_FREE : rc;
937
* internal: read the specified amount of data in whatever blocks the backend
940
static int vdReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, uint64_t uOffset,
941
void *pvBuf, size_t cbRead, bool fUpdateCache)
943
return vdReadHelperEx(pDisk, pImage, NULL, uOffset, pvBuf, cbRead,
944
true /* fZeroFreeBlocks */, fUpdateCache, 0);
908
947
DECLINLINE(PVDIOCTX) vdIoCtxAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
909
948
uint64_t uOffset, size_t cbTransfer,
910
949
PVDIMAGE pImageStart,
1446
1484
* Updating the cache doesn't make sense here because
1447
1485
* this will be done after the complete block was written.
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))
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))
1493
1531
/* Zero out the remainder of this block. Will never be visible, as this
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 */,
1553
1591
if (RT_FAILURE(rc))
1709
* internal: write buffer to the image, taking care of block boundaries and
1710
* write optimizations.
1712
static int vdWriteHelper(PVBOXHDD pDisk, PVDIMAGE pImage, uint64_t uOffset,
1713
const void *pvBuf, size_t cbWrite, bool fUpdateCache)
1715
return vdWriteHelperEx(pDisk, pImage, NULL, uOffset, pvBuf, cbWrite,
1720
* Internal: Copies the content of one disk to another one applying optimizations
1721
* to speed up the copy process if possible.
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)
1729
int rc = VINF_SUCCESS;
1731
uint64_t uOffset = 0;
1732
uint64_t cbRemaining = cbSize;
1734
bool fLockReadFrom = false;
1735
bool fLockWriteTo = false;
1736
bool fBlockwiseCopy = fSuppressRedundantIo || (cImagesFromRead > 0);
1737
unsigned uProgressOld = 0;
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));
1742
/* Allocate tmp buffer. */
1743
pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
1749
size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
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. */
1756
rc2 = vdThreadStartRead(pDiskFrom);
1758
fLockReadFrom = true;
1762
/* Read the source data. */
1763
rc = pImageFrom->Backend->pfnRead(pImageFrom->pBackendData,
1764
uOffset, pvBuf, cbThisRead,
1767
if ( rc == VERR_VD_BLOCK_FREE
1768
&& cImagesFromRead != 1)
1770
unsigned cImagesToProcess = cImagesFromRead;
1772
for (PVDIMAGE pCurrImage = pImageFrom->pPrev;
1773
pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
1774
pCurrImage = pCurrImage->pPrev)
1776
rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
1777
uOffset, pvBuf, cbThisRead,
1779
if (cImagesToProcess == 1)
1781
else if (cImagesToProcess > 0)
1787
rc = vdReadHelper(pDiskFrom, pImageFrom, uOffset, pvBuf, cbThisRead,
1788
false /* fUpdateCache */);
1790
if (RT_FAILURE(rc) && rc != VERR_VD_BLOCK_FREE)
1793
rc2 = vdThreadFinishRead(pDiskFrom);
1795
fLockReadFrom = false;
1797
if (rc != VERR_VD_BLOCK_FREE)
1799
rc2 = vdThreadStartWrite(pDiskTo);
1801
fLockWriteTo = true;
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);
1810
rc2 = vdThreadFinishWrite(pDiskTo);
1812
fLockWriteTo = false;
1814
else /* Don't propagate the error to the outside */
1817
uOffset += cbThisRead;
1818
cbRemaining -= cbThisRead;
1820
unsigned uProgressNew = uOffset * 99 / cbSize;
1821
if (uProgressNew != uProgressOld)
1823
uProgressOld = uProgressNew;
1825
if (pCbProgress && pCbProgress->pfnProgress)
1827
rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
1832
if (pDstCbProgress && pDstCbProgress->pfnProgress)
1834
rc = pDstCbProgress->pfnProgress(pDstIfProgress->pvUser,
1840
} while (uOffset < cbSize);
1846
rc2 = vdThreadFinishRead(pDiskFrom);
1852
rc2 = vdThreadFinishWrite(pDiskTo);
1856
LogFlowFunc(("returns rc=%Rrc\n", rc));
1671
1861
* Flush helper async version.
1673
1863
static int vdSetModifiedHelperAsync(PVDIOCTX pIoCtx)
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.
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.
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.
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)
5814
6011
int rc = VINF_SUCCESS;
5816
6013
bool fLockReadFrom = false, fLockWriteFrom = false, fLockWriteTo = false;
5818
6014
PVDIMAGE pImageTo = NULL;
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));
5823
6019
PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
5824
6020
VDINTERFACETYPE_PROGRESS);
6008
6210
fLockWriteTo = false;
6010
/* Allocate tmp buffer. */
6011
pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
6014
rc = VERR_NO_MEMORY;
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;
6218
if (nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN)
6219
cImagesFromReadBack = 0;
6222
if (nImage == VD_LAST_IMAGE)
6223
cImagesFromReadBack = pDiskFrom->cImages - nImageFromSame - 1;
6225
cImagesFromReadBack = nImage - nImageFromSame;
6228
if (nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
6229
cImagesToReadBack = 0;
6231
cImagesToReadBack = pDiskTo->cImages - nImageToSame - 1;
6022
6233
/* Copy the data. */
6023
uint64_t uOffset = 0;
6024
uint64_t cbRemaining = cbSize;
6028
size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
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. */
6035
rc2 = vdThreadStartRead(pDiskFrom);
6037
fLockReadFrom = true;
6040
* Updating the cache doesn't make any sense
6041
* as we are looping once through the image.
6043
rc = vdReadHelper(pDiskFrom, pImageFrom, NULL, uOffset, pvBuf,
6044
cbThisRead, fRegularRead,
6045
false /* fUpdateCache */);
6046
if (RT_FAILURE(rc) && rc != VERR_VD_BLOCK_FREE)
6049
rc2 = vdThreadFinishRead(pDiskFrom);
6051
fLockReadFrom = false;
6053
if (rc != VERR_VD_BLOCK_FREE)
6055
rc2 = vdThreadStartWrite(pDiskTo);
6057
fLockWriteTo = true;
6059
rc = vdWriteHelper(pDiskTo, pImageTo, NULL, uOffset, pvBuf,
6060
cbThisRead, false /* fUpdateCache */);
6064
rc2 = vdThreadFinishWrite(pDiskTo);
6066
fLockWriteTo = false;
6068
else /* Don't propagate the error to the outside */
6071
uOffset += cbThisRead;
6072
cbRemaining -= cbThisRead;
6074
if (pCbProgress && pCbProgress->pfnProgress)
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);
6084
if (pDstCbProgress && pDstCbProgress->pfnProgress)
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);
6094
} while (uOffset < cbSize);
6234
rc = vdCopyHelper(pDiskFrom, pImageFrom, pDiskTo, cbSize,
6235
cImagesFromReadBack, cImagesToReadBack,
6236
fSuppressRedundantIo, pIfProgress, pCbProgress,
6237
pDstIfProgress, pDstCbProgress);
6096
6239
if (RT_SUCCESS(rc))
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.
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.
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)
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);
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