1
/* $Id: VD.cpp 35321 2010-12-24 16:05:54Z vboxsync $ */
3
* VBoxHDD - VBox HDD Container implementation.
7
* Copyright (C) 2006-2010 Oracle Corporation
9
* This file is part of VirtualBox Open Source Edition (OSE), as
10
* available from http://www.virtualbox.org. This file is free software;
11
* you can redistribute it and/or modify it under the terms of the GNU
12
* General Public License (GPL) as published by the Free Software
13
* Foundation, in version 2 as it comes in the "COPYING" file of the
14
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18
/*******************************************************************************
20
*******************************************************************************/
21
#define LOG_GROUP LOG_GROUP_VD
27
#include <iprt/alloc.h>
28
#include <iprt/assert.h>
29
#include <iprt/uuid.h>
30
#include <iprt/file.h>
31
#include <iprt/string.h>
35
#include <iprt/path.h>
36
#include <iprt/param.h>
37
#include <iprt/memcache.h>
39
#include <iprt/critsect.h>
40
#include <iprt/list.h>
43
#include <VBox/vd-plugin.h>
44
#include <VBox/vd-cache-plugin.h>
46
#define VBOXHDDDISK_SIGNATURE 0x6f0e2a7d
48
/** Buffer size used for merging images. */
49
#define VD_MERGE_BUFFER_SIZE (16 * _1M)
51
/** Maximum number of segments in one I/O task. */
52
#define VD_IO_TASK_SEGMENTS_MAX 64
55
* VD async I/O interface storage descriptor.
57
typedef struct VDIIOFALLBACKSTORAGE
61
/** Completion callback. */
62
PFNVDCOMPLETED pfnCompleted;
63
/** Thread for async access. */
65
} VDIIOFALLBACKSTORAGE, *PVDIIOFALLBACKSTORAGE;
68
* Structure containing everything I/O related
69
* for the image and cache descriptors.
73
/** I/O interface to the upper layer. */
74
PVDINTERFACE pInterfaceIO;
75
/** I/O interface callback table. */
76
PVDINTERFACEIO pInterfaceIOCallbacks;
78
/** Per image internal I/O interface. */
81
/** Fallback I/O interface, only used if the caller doesn't provide it. */
84
/** Opaque backend data. */
86
/** Disk this image is part of */
91
* VBox HDD Container image descriptor.
93
typedef struct VDIMAGE
95
/** Link to parent image descriptor, if any. */
96
struct VDIMAGE *pPrev;
97
/** Link to child image descriptor, if any. */
98
struct VDIMAGE *pNext;
99
/** Container base filename. (UTF-8) */
101
/** Data managed by the backend which keeps the actual info. */
103
/** Cached sanitized image flags. */
104
unsigned uImageFlags;
105
/** Image open flags (only those handled generically in this code and which
106
* the backends will never ever see). */
109
/** Function pointers for the various backend methods. */
110
PCVBOXHDDBACKEND Backend;
111
/** Pointer to list of VD interfaces, per-image. */
112
PVDINTERFACE pVDIfsImage;
113
/** I/O related things. */
115
} VDIMAGE, *PVDIMAGE;
118
* uModified bit flags.
120
#define VD_IMAGE_MODIFIED_FLAG RT_BIT(0)
121
#define VD_IMAGE_MODIFIED_FIRST RT_BIT(1)
122
#define VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE RT_BIT(2)
126
* VBox HDD Cache image descriptor.
128
typedef struct VDCACHE
130
/** Cache base filename. (UTF-8) */
132
/** Data managed by the backend which keeps the actual info. */
134
/** Cached sanitized image flags. */
135
unsigned uImageFlags;
136
/** Image open flags (only those handled generically in this code and which
137
* the backends will never ever see). */
140
/** Function pointers for the various backend methods. */
141
PCVDCACHEBACKEND Backend;
143
/** Pointer to list of VD interfaces, per-cache. */
144
PVDINTERFACE pVDIfsCache;
145
/** I/O related things. */
147
} VDCACHE, *PVDCACHE;
150
* VBox HDD Container main structure, private part.
154
/** Structure signature (VBOXHDDDISK_SIGNATURE). */
155
uint32_t u32Signature;
160
/** Number of opened images. */
166
/** Last opened image in the chain.
167
* The same as pBase if only one image is used. */
170
/** If a merge to one of the parents is running this may be non-NULL
171
* to indicate to what image the writes should be additionally relayed. */
172
PVDIMAGE pImageRelay;
174
/** Flags representing the modification state. */
177
/** Cached size of this disk. */
179
/** Cached PCHS geometry for this disk. */
180
VDGEOMETRY PCHSGeometry;
181
/** Cached LCHS geometry for this disk. */
182
VDGEOMETRY LCHSGeometry;
184
/** Pointer to list of VD interfaces, per-disk. */
185
PVDINTERFACE pVDIfsDisk;
186
/** Pointer to the common interface structure for error reporting. */
187
PVDINTERFACE pInterfaceError;
188
/** Pointer to the error interface callbacks we use if available. */
189
PVDINTERFACEERROR pInterfaceErrorCallbacks;
191
/** Pointer to the optional thread synchronization interface. */
192
PVDINTERFACE pInterfaceThreadSync;
193
/** Pointer to the optional thread synchronization callbacks. */
194
PVDINTERFACETHREADSYNC pInterfaceThreadSyncCallbacks;
196
/** Internal I/O interface callback table for the images. */
197
VDINTERFACEIOINT VDIIOIntCallbacks;
199
/** Callback table for the fallback I/O interface. */
200
VDINTERFACEIO VDIIOCallbacks;
202
/** Memory cache for I/O contexts */
203
RTMEMCACHE hMemCacheIoCtx;
204
/** Memory cache for I/O tasks. */
205
RTMEMCACHE hMemCacheIoTask;
206
/** Critical section protecting the disk against concurrent access. */
208
/** Flag whether the disk is currently locked by growing write or a flush
209
* request. Other flush or growing write requests need to wait until
210
* the current one completes.
212
volatile bool fLocked;
213
/** List of waiting requests. - Protected by the critical section. */
214
RTLISTNODE ListWriteLocked;
216
/** Pointer to the L2 disk cache if any. */
220
# define VD_THREAD_IS_CRITSECT_OWNER(Disk) \
223
AssertMsg(RTCritSectIsOwner(&Disk->CritSect), \
224
("Thread does not own critical section\n"));\
228
* VBox parent read descriptor, used internally for compaction.
230
typedef struct VDPARENTSTATEDESC
232
/** Pointer to disk descriptor. */
234
/** Pointer to image descriptor. */
236
} VDPARENTSTATEDESC, *PVDPARENTSTATEDESC;
239
* Transfer direction.
241
typedef enum VDIOCTXTXDIR
244
VDIOCTXTXDIR_READ = 0,
250
VDIOCTXTXDIR_32BIT_HACK = 0x7fffffff
251
} VDIOCTXTXDIR, *PVDIOCTXTXDIR;
253
/** Transfer function */
254
typedef DECLCALLBACK(int) FNVDIOCTXTRANSFER (PVDIOCTX pIoCtx);
255
/** Pointer to a transfer function. */
256
typedef FNVDIOCTXTRANSFER *PFNVDIOCTXTRANSFER;
261
typedef struct VDIOCTX
263
/** Disk this is request is for. */
267
/** Transfer direction */
268
VDIOCTXTXDIR enmTxDir;
269
/** Number of bytes left until this context completes. */
270
volatile uint32_t cbTransferLeft;
271
/** Current offset */
272
volatile uint64_t uOffset;
273
/** Number of bytes to transfer */
274
volatile size_t cbTransfer;
275
/** Current image in the chain. */
277
/** Start image to read from. pImageCur is reset to this
278
* value after it reached the first image in the chain. */
279
PVDIMAGE pImageStart;
282
/** Flag whether the I/O context is blocked because it is in the growing list. */
284
/** Number of data transfers currently pending. */
285
volatile uint32_t cDataTransfersPending;
286
/** How many meta data transfers are pending. */
287
volatile uint32_t cMetaTransfersPending;
288
/** Flag whether the request finished */
289
volatile bool fComplete;
290
/** Temporary allocated memory which is freed
291
* when the context completes. */
293
/** Transfer function. */
294
PFNVDIOCTXTRANSFER pfnIoCtxTransfer;
295
/** Next transfer part after the current one completed. */
296
PFNVDIOCTXTRANSFER pfnIoCtxTransferNext;
297
/** Parent I/O context if any. Sets the type of the context (root/child) */
298
PVDIOCTX pIoCtxParent;
299
/** Type dependent data (root/child) */
305
/** Completion callback */
306
PFNVDASYNCTRANSFERCOMPLETE pfnComplete;
307
/** User argument 1 passed on completion. */
309
/** User argument 1 passed on completion. */
315
/** Saved start offset */
316
uint64_t uOffsetSaved;
317
/** Saved transfer size */
318
size_t cbTransferLeftSaved;
319
/** Number of bytes transferred from the parent if this context completes. */
320
size_t cbTransferParent;
321
/** Number of bytes to pre read */
323
/** Number of bytes to post read. */
325
/** Number of bytes to write left in the parent. */
326
size_t cbWriteParent;
327
/** Write type dependent data. */
333
/** Bytes to fill to satisfy the block size. Not part of the virtual disk. */
335
/** Bytes to copy instead of reading from the parent */
337
/** Bytes to read from the image. */
345
typedef struct VDIOCTXDEFERRED
347
/** Node in the list of deferred requests.
348
* A request can be deferred if the image is growing
349
* and the request accesses the same range or if
350
* the backend needs to read or write metadata from the disk
351
* before it can continue. */
352
RTLISTNODE NodeDeferred;
353
/** I/O context this entry points to. */
355
} VDIOCTXDEFERRED, *PVDIOCTXDEFERRED;
360
typedef struct VDIOTASK
362
/** Storage this task belongs to. */
363
PVDIOSTORAGE pIoStorage;
364
/** Optional completion callback. */
365
PFNVDXFERCOMPLETED pfnComplete;
366
/** Opaque user data. */
368
/** Flag whether this is a meta data transfer. */
370
/** Type dependent data. */
373
/** User data transfer. */
376
/** Number of bytes this task transferred. */
378
/** Pointer to the I/O context the task belongs. */
381
/** Meta data transfer. */
384
/** Meta transfer this task is for. */
385
PVDMETAXFER pMetaXfer;
388
} VDIOTASK, *PVDIOTASK;
393
typedef struct VDIOSTORAGE
395
/** Image I/O state this storage handle belongs to. */
397
/** AVL tree for pending async metadata transfers. */
398
PAVLRFOFFTREE pTreeMetaXfers;
399
/** Storage handle */
406
* @note This entry can't be freed if either the list is not empty or
407
* the reference counter is not 0.
408
* The assumption is that the backends don't need to read huge amounts of
409
* metadata to complete a transfer so the additional memory overhead should
410
* be relatively small.
412
typedef struct VDMETAXFER
414
/** AVL core for fast search (the file offset is the key) */
415
AVLRFOFFNODECORE Core;
416
/** I/O storage for this transfer. */
417
PVDIOSTORAGE pIoStorage;
420
/** List of I/O contexts waiting for this metadata transfer to complete. */
421
RTLISTNODE ListIoCtxWaiting;
422
/** Number of references to this entry. */
424
/** Size of the data stored with this entry. */
426
/** Data stored - variable size. */
431
* The transfer direction for the metadata.
433
#define VDMETAXFER_TXDIR_MASK 0x3
434
#define VDMETAXFER_TXDIR_NONE 0x0
435
#define VDMETAXFER_TXDIR_WRITE 0x1
436
#define VDMETAXFER_TXDIR_READ 0x2
437
#define VDMETAXFER_TXDIR_FLUSH 0x3
438
#define VDMETAXFER_TXDIR_GET(flags) ((flags) & VDMETAXFER_TXDIR_MASK)
439
#define VDMETAXFER_TXDIR_SET(flags, dir) ((flags) = (flags & ~VDMETAXFER_TXDIR_MASK) | (dir))
441
extern VBOXHDDBACKEND g_RawBackend;
442
extern VBOXHDDBACKEND g_VmdkBackend;
443
extern VBOXHDDBACKEND g_VDIBackend;
444
extern VBOXHDDBACKEND g_VhdBackend;
445
extern VBOXHDDBACKEND g_ParallelsBackend;
446
extern VBOXHDDBACKEND g_DmgBackend;
447
extern VBOXHDDBACKEND g_ISCSIBackend;
449
static unsigned g_cBackends = 0;
450
static PVBOXHDDBACKEND *g_apBackends = NULL;
451
static PVBOXHDDBACKEND aStaticBackends[] =
463
* Supported backends for the disk cache.
465
extern VDCACHEBACKEND g_VciCacheBackend;
467
static unsigned g_cCacheBackends = 0;
468
static PVDCACHEBACKEND *g_apCacheBackends = NULL;
469
static PVDCACHEBACKEND aStaticCacheBackends[] =
475
* internal: add several backends.
477
static int vdAddBackends(PVBOXHDDBACKEND *ppBackends, unsigned cBackends)
479
PVBOXHDDBACKEND *pTmp = (PVBOXHDDBACKEND*)RTMemRealloc(g_apBackends,
480
(g_cBackends + cBackends) * sizeof(PVBOXHDDBACKEND));
481
if (RT_UNLIKELY(!pTmp))
482
return VERR_NO_MEMORY;
484
memcpy(&g_apBackends[g_cBackends], ppBackends, cBackends * sizeof(PVBOXHDDBACKEND));
485
g_cBackends += cBackends;
490
* internal: add single backend.
492
DECLINLINE(int) vdAddBackend(PVBOXHDDBACKEND pBackend)
494
return vdAddBackends(&pBackend, 1);
498
* internal: add several cache backends.
500
static int vdAddCacheBackends(PVDCACHEBACKEND *ppBackends, unsigned cBackends)
502
PVDCACHEBACKEND *pTmp = (PVDCACHEBACKEND*)RTMemRealloc(g_apCacheBackends,
503
(g_cCacheBackends + cBackends) * sizeof(PVDCACHEBACKEND));
504
if (RT_UNLIKELY(!pTmp))
505
return VERR_NO_MEMORY;
506
g_apCacheBackends = pTmp;
507
memcpy(&g_apCacheBackends[g_cCacheBackends], ppBackends, cBackends * sizeof(PVDCACHEBACKEND));
508
g_cCacheBackends += cBackends;
513
* internal: add single cache backend.
515
DECLINLINE(int) vdAddCacheBackend(PVDCACHEBACKEND pBackend)
517
return vdAddCacheBackends(&pBackend, 1);
521
* internal: issue error message.
523
static int vdError(PVBOXHDD pDisk, int rc, RT_SRC_POS_DECL,
524
const char *pszFormat, ...)
527
va_start(va, pszFormat);
528
if (pDisk->pInterfaceErrorCallbacks)
529
pDisk->pInterfaceErrorCallbacks->pfnError(pDisk->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
535
* internal: thread synchronization, start read.
537
DECLINLINE(int) vdThreadStartRead(PVBOXHDD pDisk)
539
int rc = VINF_SUCCESS;
540
if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
541
rc = pDisk->pInterfaceThreadSyncCallbacks->pfnStartRead(pDisk->pInterfaceThreadSync->pvUser);
546
* internal: thread synchronization, finish read.
548
DECLINLINE(int) vdThreadFinishRead(PVBOXHDD pDisk)
550
int rc = VINF_SUCCESS;
551
if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
552
rc = pDisk->pInterfaceThreadSyncCallbacks->pfnFinishRead(pDisk->pInterfaceThreadSync->pvUser);
557
* internal: thread synchronization, start write.
559
DECLINLINE(int) vdThreadStartWrite(PVBOXHDD pDisk)
561
int rc = VINF_SUCCESS;
562
if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
563
rc = pDisk->pInterfaceThreadSyncCallbacks->pfnStartWrite(pDisk->pInterfaceThreadSync->pvUser);
568
* internal: thread synchronization, finish write.
570
DECLINLINE(int) vdThreadFinishWrite(PVBOXHDD pDisk)
572
int rc = VINF_SUCCESS;
573
if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
574
rc = pDisk->pInterfaceThreadSyncCallbacks->pfnFinishWrite(pDisk->pInterfaceThreadSync->pvUser);
579
* internal: find image format backend.
581
static int vdFindBackend(const char *pszBackend, PCVBOXHDDBACKEND *ppBackend)
583
int rc = VINF_SUCCESS;
584
PCVBOXHDDBACKEND pBackend = NULL;
589
for (unsigned i = 0; i < g_cBackends; i++)
591
if (!RTStrICmp(pszBackend, g_apBackends[i]->pszBackendName))
593
pBackend = g_apBackends[i];
597
*ppBackend = pBackend;
602
* internal: find cache format backend.
604
static int vdFindCacheBackend(const char *pszBackend, PCVDCACHEBACKEND *ppBackend)
606
int rc = VINF_SUCCESS;
607
PCVDCACHEBACKEND pBackend = NULL;
609
if (!g_apCacheBackends)
612
for (unsigned i = 0; i < g_cCacheBackends; i++)
614
if (!RTStrICmp(pszBackend, g_apCacheBackends[i]->pszBackendName))
616
pBackend = g_apCacheBackends[i];
620
*ppBackend = pBackend;
625
* internal: add image structure to the end of images list.
627
static void vdAddImageToList(PVBOXHDD pDisk, PVDIMAGE pImage)
629
pImage->pPrev = NULL;
630
pImage->pNext = NULL;
634
Assert(pDisk->cImages > 0);
635
pImage->pPrev = pDisk->pLast;
636
pDisk->pLast->pNext = pImage;
637
pDisk->pLast = pImage;
641
Assert(pDisk->cImages == 0);
642
pDisk->pBase = pImage;
643
pDisk->pLast = pImage;
650
* internal: remove image structure from the images list.
652
static void vdRemoveImageFromList(PVBOXHDD pDisk, PVDIMAGE pImage)
654
Assert(pDisk->cImages > 0);
657
pImage->pPrev->pNext = pImage->pNext;
659
pDisk->pBase = pImage->pNext;
662
pImage->pNext->pPrev = pImage->pPrev;
664
pDisk->pLast = pImage->pPrev;
666
pImage->pPrev = NULL;
667
pImage->pNext = NULL;
673
* internal: find image by index into the images list.
675
static PVDIMAGE vdGetImageByNumber(PVBOXHDD pDisk, unsigned nImage)
677
PVDIMAGE pImage = pDisk->pBase;
678
if (nImage == VD_LAST_IMAGE)
680
while (pImage && nImage)
682
pImage = pImage->pNext;
689
* Internal: Tries to read the desired range from the given cache.
691
* @returns VBox status code.
692
* @retval VERR_VD_BLOCK_FREE if the block is not in the cache.
693
* pcbRead will be set to the number of bytes not in the cache.
694
* Everything thereafter might be in the cache.
695
* @param pCache The cache to read from.
696
* @param uOffset Offset of the virtual disk to read.
697
* @param pvBuf Where to store the read data.
698
* @param cbRead How much to read.
699
* @param pcbRead Where to store the number of bytes actually read.
700
* On success this indicates the number of bytes read from the cache.
701
* If VERR_VD_BLOCK_FREE is returned this gives the number of bytes
702
* which are not in the cache.
703
* In both cases everything beyond this value
704
* might or might not be in the cache.
706
static int vdCacheReadHelper(PVDCACHE pCache, uint64_t uOffset,
707
void *pvBuf, size_t cbRead, size_t *pcbRead)
709
int rc = VINF_SUCCESS;
711
LogFlowFunc(("pCache=%#p uOffset=%llu pvBuf=%#p cbRead=%zu pcbRead=%#p\n",
712
pCache, uOffset, pvBuf, cbRead, pcbRead));
717
rc = pCache->Backend->pfnRead(pCache->pBackendData, uOffset, pvBuf,
720
LogFlowFunc(("returns rc=%Rrc pcbRead=%zu\n", rc, *pcbRead));
725
* Internal: Writes data for the given block into the cache.
727
* @returns VBox status code.
728
* @param pCache The cache to write to.
729
* @param uOffset Offset of the virtual disk to write to teh cache.
730
* @param pcvBuf The data to write.
731
* @param cbWrite How much to write.
732
* @param pcbWritten How much data could be written, optional.
734
static int vdCacheWriteHelper(PVDCACHE pCache, uint64_t uOffset, const void *pcvBuf,
735
size_t cbWrite, size_t *pcbWritten)
737
int rc = VINF_SUCCESS;
739
LogFlowFunc(("pCache=%#p uOffset=%llu pvBuf=%#p cbWrite=%zu pcbWritten=%#p\n",
740
pCache, uOffset, pcvBuf, cbWrite, pcbWritten));
747
rc = pCache->Backend->pfnWrite(pCache->pBackendData, uOffset, pcvBuf,
748
cbWrite, pcbWritten);
751
size_t cbWritten = 0;
755
rc = pCache->Backend->pfnWrite(pCache->pBackendData, uOffset, pcvBuf,
756
cbWrite, &cbWritten);
757
uOffset += cbWritten;
758
pcvBuf = (char *)pcvBuf + cbWritten;
759
cbWrite -= cbWritten;
764
LogFlowFunc(("returns rc=%Rrc pcbWritten=%zu\n",
765
rc, pcbWritten ? *pcbWritten : cbWrite));
770
* Internal: Reads a given amount of data from the image chain of the disk.
772
static int vdDiskReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
773
uint64_t uOffset, void *pvBuf, size_t cbRead, size_t *pcbThisRead)
775
int rc = VINF_SUCCESS;
776
size_t cbThisRead = cbRead;
778
AssertPtr(pcbThisRead);
783
* Try to read from the given image.
784
* If the block is not allocated read from override chain if present.
786
rc = pImage->Backend->pfnRead(pImage->pBackendData,
787
uOffset, pvBuf, cbThisRead,
790
if (rc == VERR_VD_BLOCK_FREE)
792
for (PVDIMAGE pCurrImage = pImageParentOverride ? pImageParentOverride : pImage->pPrev;
793
pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
794
pCurrImage = pCurrImage->pPrev)
796
rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
797
uOffset, pvBuf, cbThisRead,
802
if (RT_SUCCESS(rc) || rc == VERR_VD_BLOCK_FREE)
803
*pcbThisRead = cbThisRead;
809
* internal: read the specified amount of data in whatever blocks the backend
812
static int vdReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
813
uint64_t uOffset, void *pvBuf, size_t cbRead,
814
bool fZeroFreeBlocks, bool fUpdateCache)
816
int rc = VINF_SUCCESS;
818
bool fAllFree = true;
819
size_t cbBufClear = 0;
821
/* Loop until all read. */
824
/* Search for image with allocated block. Do not attempt to read more
825
* than the previous reads marked as valid. Otherwise this would return
826
* stale data when different block sizes are used for the images. */
830
&& !pImageParentOverride)
832
rc = vdCacheReadHelper(pDisk->pCache, uOffset, pvBuf,
833
cbThisRead, &cbThisRead);
835
if (rc == VERR_VD_BLOCK_FREE)
837
rc = vdDiskReadHelper(pDisk, pImage, pImageParentOverride,
838
uOffset, pvBuf, cbThisRead, &cbThisRead);
840
/* If the read was successful, write the data back into the cache. */
844
rc = vdCacheWriteHelper(pDisk->pCache, uOffset, pvBuf,
851
/** @todo can be be replaced by vdDiskReadHelper if it proves to be reliable,
852
* don't want to be responsible for data corruption...
855
* Try to read from the given image.
856
* If the block is not allocated read from override chain if present.
858
rc = pImage->Backend->pfnRead(pImage->pBackendData,
859
uOffset, pvBuf, cbThisRead,
862
if (rc == VERR_VD_BLOCK_FREE)
864
for (PVDIMAGE pCurrImage = pImageParentOverride ? pImageParentOverride : pImage->pPrev;
865
pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
866
pCurrImage = pCurrImage->pPrev)
868
rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
869
uOffset, pvBuf, cbThisRead,
875
/* No image in the chain contains the data for the block. */
876
if (rc == VERR_VD_BLOCK_FREE)
878
/* Fill the free space with 0 if we are told to do so
879
* or a previous read returned valid data. */
880
if (fZeroFreeBlocks || !fAllFree)
881
memset(pvBuf, '\0', cbThisRead);
883
cbBufClear += cbThisRead;
887
else if (RT_SUCCESS(rc))
889
/* First not free block, fill the space before with 0. */
890
if (!fZeroFreeBlocks)
892
memset((char *)pvBuf - cbBufClear, '\0', cbBufClear);
898
cbRead -= cbThisRead;
899
uOffset += cbThisRead;
900
pvBuf = (char *)pvBuf + cbThisRead;
901
} while (cbRead != 0 && RT_SUCCESS(rc));
903
return (!fZeroFreeBlocks && fAllFree) ? VERR_VD_BLOCK_FREE : rc;
906
DECLINLINE(PVDIOCTX) vdIoCtxAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
907
uint64_t uOffset, size_t cbTransfer,
908
PVDIMAGE pImageStart,
909
PCRTSGBUF pcSgBuf, void *pvAllocation,
910
PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
912
PVDIOCTX pIoCtx = NULL;
914
pIoCtx = (PVDIOCTX)RTMemCacheAlloc(pDisk->hMemCacheIoCtx);
915
if (RT_LIKELY(pIoCtx))
917
pIoCtx->pDisk = pDisk;
918
pIoCtx->enmTxDir = enmTxDir;
919
pIoCtx->cbTransferLeft = cbTransfer;
920
pIoCtx->uOffset = uOffset;
921
pIoCtx->cbTransfer = cbTransfer;
922
pIoCtx->pImageStart = pImageStart;
923
pIoCtx->pImageCur = pImageStart;
924
pIoCtx->cDataTransfersPending = 0;
925
pIoCtx->cMetaTransfersPending = 0;
926
pIoCtx->fComplete = false;
927
pIoCtx->fBlocked = false;
928
pIoCtx->pvAllocation = pvAllocation;
929
pIoCtx->pfnIoCtxTransfer = pfnIoCtxTransfer;
930
pIoCtx->pfnIoCtxTransferNext = NULL;
931
pIoCtx->rcReq = VINF_SUCCESS;
933
/* There is no S/G list for a flush request. */
934
if (enmTxDir != VDIOCTXTXDIR_FLUSH)
935
RTSgBufClone(&pIoCtx->SgBuf, pcSgBuf);
937
memset(&pIoCtx->SgBuf, 0, sizeof(RTSGBUF));
943
DECLINLINE(PVDIOCTX) vdIoCtxRootAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
944
uint64_t uOffset, size_t cbTransfer,
945
PVDIMAGE pImageStart, PCRTSGBUF pcSgBuf,
946
PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
947
void *pvUser1, void *pvUser2,
949
PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
951
PVDIOCTX pIoCtx = vdIoCtxAlloc(pDisk, enmTxDir, uOffset, cbTransfer, pImageStart,
952
pcSgBuf, pvAllocation, pfnIoCtxTransfer);
954
if (RT_LIKELY(pIoCtx))
956
pIoCtx->pIoCtxParent = NULL;
957
pIoCtx->Type.Root.pfnComplete = pfnComplete;
958
pIoCtx->Type.Root.pvUser1 = pvUser1;
959
pIoCtx->Type.Root.pvUser2 = pvUser2;
962
LogFlow(("Allocated root I/O context %#p\n", pIoCtx));
966
DECLINLINE(PVDIOCTX) vdIoCtxChildAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
967
uint64_t uOffset, size_t cbTransfer,
968
PVDIMAGE pImageStart, PCRTSGBUF pcSgBuf,
969
PVDIOCTX pIoCtxParent, size_t cbTransferParent,
970
size_t cbWriteParent, void *pvAllocation,
971
PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
973
PVDIOCTX pIoCtx = vdIoCtxAlloc(pDisk, enmTxDir, uOffset, cbTransfer, pImageStart,
974
pcSgBuf, pvAllocation, pfnIoCtxTransfer);
976
AssertPtr(pIoCtxParent);
977
Assert(!pIoCtxParent->pIoCtxParent);
979
if (RT_LIKELY(pIoCtx))
981
pIoCtx->pIoCtxParent = pIoCtxParent;
982
pIoCtx->Type.Child.uOffsetSaved = uOffset;
983
pIoCtx->Type.Child.cbTransferLeftSaved = cbTransfer;
984
pIoCtx->Type.Child.cbTransferParent = cbTransferParent;
985
pIoCtx->Type.Child.cbWriteParent = cbWriteParent;
988
LogFlow(("Allocated child I/O context %#p\n", pIoCtx));
992
DECLINLINE(PVDIOTASK) vdIoTaskUserAlloc(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser, PVDIOCTX pIoCtx, uint32_t cbTransfer)
994
PVDIOTASK pIoTask = NULL;
996
pIoTask = (PVDIOTASK)RTMemCacheAlloc(pIoStorage->pVDIo->pDisk->hMemCacheIoTask);
999
pIoTask->pIoStorage = pIoStorage;
1000
pIoTask->pfnComplete = pfnComplete;
1001
pIoTask->pvUser = pvUser;
1002
pIoTask->fMeta = false;
1003
pIoTask->Type.User.cbTransfer = cbTransfer;
1004
pIoTask->Type.User.pIoCtx = pIoCtx;
1010
DECLINLINE(PVDIOTASK) vdIoTaskMetaAlloc(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser, PVDMETAXFER pMetaXfer)
1012
PVDIOTASK pIoTask = NULL;
1014
pIoTask = (PVDIOTASK)RTMemCacheAlloc(pIoStorage->pVDIo->pDisk->hMemCacheIoTask);
1017
pIoTask->pIoStorage = pIoStorage;
1018
pIoTask->pfnComplete = pfnComplete;
1019
pIoTask->pvUser = pvUser;
1020
pIoTask->fMeta = true;
1021
pIoTask->Type.Meta.pMetaXfer = pMetaXfer;
1027
DECLINLINE(void) vdIoCtxFree(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
1029
LogFlow(("Freeing I/O context %#p\n", pIoCtx));
1030
if (pIoCtx->pvAllocation)
1031
RTMemFree(pIoCtx->pvAllocation);
1033
memset(pIoCtx, 0xff, sizeof(VDIOCTX));
1035
RTMemCacheFree(pDisk->hMemCacheIoCtx, pIoCtx);
1038
DECLINLINE(void) vdIoTaskFree(PVBOXHDD pDisk, PVDIOTASK pIoTask)
1040
RTMemCacheFree(pDisk->hMemCacheIoTask, pIoTask);
1043
DECLINLINE(void) vdIoCtxChildReset(PVDIOCTX pIoCtx)
1045
AssertPtr(pIoCtx->pIoCtxParent);
1047
RTSgBufReset(&pIoCtx->SgBuf);
1048
pIoCtx->uOffset = pIoCtx->Type.Child.uOffsetSaved;
1049
pIoCtx->cbTransferLeft = pIoCtx->Type.Child.cbTransferLeftSaved;
1052
DECLINLINE(PVDMETAXFER) vdMetaXferAlloc(PVDIOSTORAGE pIoStorage, uint64_t uOffset, size_t cb)
1054
PVDMETAXFER pMetaXfer = (PVDMETAXFER)RTMemAlloc(RT_OFFSETOF(VDMETAXFER, abData[cb]));
1056
if (RT_LIKELY(pMetaXfer))
1058
pMetaXfer->Core.Key = uOffset;
1059
pMetaXfer->Core.KeyLast = uOffset + cb - 1;
1060
pMetaXfer->fFlags = VDMETAXFER_TXDIR_NONE;
1061
pMetaXfer->cbMeta = cb;
1062
pMetaXfer->pIoStorage = pIoStorage;
1063
pMetaXfer->cRefs = 0;
1064
RTListInit(&pMetaXfer->ListIoCtxWaiting);
1069
DECLINLINE(int) vdIoCtxDefer(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
1071
PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
1074
return VERR_NO_MEMORY;
1076
LogFlowFunc(("Deferring write pIoCtx=%#p\n", pIoCtx));
1078
Assert(!pIoCtx->pIoCtxParent && !pIoCtx->fBlocked);
1080
RTListInit(&pDeferred->NodeDeferred);
1081
pDeferred->pIoCtx = pIoCtx;
1082
RTListAppend(&pDisk->ListWriteLocked, &pDeferred->NodeDeferred);
1083
pIoCtx->fBlocked = true;
1084
return VINF_SUCCESS;
1087
static size_t vdIoCtxCopy(PVDIOCTX pIoCtxDst, PVDIOCTX pIoCtxSrc, size_t cbData)
1089
return RTSgBufCopy(&pIoCtxDst->SgBuf, &pIoCtxSrc->SgBuf, cbData);
1092
static int vdIoCtxCmp(PVDIOCTX pIoCtx1, PVDIOCTX pIoCtx2, size_t cbData)
1094
return RTSgBufCmp(&pIoCtx1->SgBuf, &pIoCtx2->SgBuf, cbData);
1097
static size_t vdIoCtxCopyTo(PVDIOCTX pIoCtx, uint8_t *pbData, size_t cbData)
1099
return RTSgBufCopyToBuf(&pIoCtx->SgBuf, pbData, cbData);
1103
static size_t vdIoCtxCopyFrom(PVDIOCTX pIoCtx, uint8_t *pbData, size_t cbData)
1105
return RTSgBufCopyFromBuf(&pIoCtx->SgBuf, pbData, cbData);
1108
static size_t vdIoCtxSet(PVDIOCTX pIoCtx, uint8_t ch, size_t cbData)
1110
return RTSgBufSet(&pIoCtx->SgBuf, ch, cbData);
1113
static int vdIoCtxProcess(PVDIOCTX pIoCtx)
1115
int rc = VINF_SUCCESS;
1116
PVBOXHDD pDisk = pIoCtx->pDisk;
1118
LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
1120
RTCritSectEnter(&pDisk->CritSect);
1122
if ( !pIoCtx->cbTransferLeft
1123
&& !pIoCtx->cMetaTransfersPending
1124
&& !pIoCtx->cDataTransfersPending
1125
&& !pIoCtx->pfnIoCtxTransfer)
1127
rc = VINF_VD_ASYNC_IO_FINISHED;
1132
* We complete the I/O context in case of an error
1133
* if there is no I/O task pending.
1135
if ( RT_FAILURE(pIoCtx->rcReq)
1136
&& !pIoCtx->cMetaTransfersPending
1137
&& !pIoCtx->cDataTransfersPending)
1139
rc = VINF_VD_ASYNC_IO_FINISHED;
1143
/* Don't change anything if there is a metadata transfer pending or we are blocked. */
1144
if ( pIoCtx->cMetaTransfersPending
1145
|| pIoCtx->fBlocked)
1147
rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1151
if (pIoCtx->pfnIoCtxTransfer)
1153
/* Call the transfer function advancing to the next while there is no error. */
1154
while ( pIoCtx->pfnIoCtxTransfer
1157
LogFlowFunc(("calling transfer function %#p\n", pIoCtx->pfnIoCtxTransfer));
1158
rc = pIoCtx->pfnIoCtxTransfer(pIoCtx);
1160
/* Advance to the next part of the transfer if the current one succeeded. */
1163
pIoCtx->pfnIoCtxTransfer = pIoCtx->pfnIoCtxTransferNext;
1164
pIoCtx->pfnIoCtxTransferNext = NULL;
1170
&& !pIoCtx->cbTransferLeft
1171
&& !pIoCtx->cMetaTransfersPending
1172
&& !pIoCtx->cDataTransfersPending)
1173
rc = VINF_VD_ASYNC_IO_FINISHED;
1174
else if ( RT_SUCCESS(rc)
1175
|| rc == VERR_VD_NOT_ENOUGH_METADATA
1176
|| rc == VERR_VD_IOCTX_HALT)
1177
rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1178
else if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
1180
ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rc, VINF_SUCCESS);
1182
* The I/O context completed if we have an error and there is no data
1183
* or meta data transfer pending.
1185
if ( !pIoCtx->cMetaTransfersPending
1186
&& !pIoCtx->cDataTransfersPending)
1187
rc = VINF_VD_ASYNC_IO_FINISHED;
1189
rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1193
RTCritSectLeave(&pDisk->CritSect);
1195
LogFlowFunc(("pIoCtx=%#p rc=%Rrc cbTransferLeft=%u cMetaTransfersPending=%u fComplete=%RTbool\n",
1196
pIoCtx, rc, pIoCtx->cbTransferLeft, pIoCtx->cMetaTransfersPending,
1197
pIoCtx->fComplete));
1202
static int vdIoCtxLockDisk(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
1204
int rc = VINF_SUCCESS;
1206
if (!ASMAtomicCmpXchgBool(&pDisk->fLocked, true, false))
1208
rc = vdIoCtxDefer(pDisk, pIoCtx);
1210
rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1216
static void vdIoCtxUnlockDisk(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
1218
Assert(pDisk->fLocked);
1219
ASMAtomicXchgBool(&pDisk->fLocked, false);
1221
/* Process any pending writes if the current request didn't caused another growing. */
1222
RTCritSectEnter(&pDisk->CritSect);
1224
if (!RTListIsEmpty(&pDisk->ListWriteLocked))
1228
RTListMove(&ListTmp, &pDisk->ListWriteLocked);
1229
RTCritSectLeave(&pDisk->CritSect);
1231
/* Process the list. */
1235
PVDIOCTXDEFERRED pDeferred = RTListGetFirst(&ListTmp, VDIOCTXDEFERRED, NodeDeferred);
1236
PVDIOCTX pIoCtxWait = pDeferred->pIoCtx;
1238
AssertPtr(pIoCtxWait);
1240
RTListNodeRemove(&pDeferred->NodeDeferred);
1241
RTMemFree(pDeferred);
1243
Assert(!pIoCtxWait->pIoCtxParent);
1245
pIoCtxWait->fBlocked = false;
1246
LogFlowFunc(("Processing waiting I/O context pIoCtxWait=%#p\n", pIoCtxWait));
1248
rc = vdIoCtxProcess(pIoCtxWait);
1249
if ( rc == VINF_VD_ASYNC_IO_FINISHED
1250
&& ASMAtomicCmpXchgBool(&pIoCtxWait->fComplete, true, false))
1252
LogFlowFunc(("Waiting I/O context completed pIoCtxWait=%#p\n", pIoCtxWait));
1253
vdThreadFinishWrite(pDisk);
1254
pIoCtxWait->Type.Root.pfnComplete(pIoCtxWait->Type.Root.pvUser1,
1255
pIoCtxWait->Type.Root.pvUser2,
1257
vdIoCtxFree(pDisk, pIoCtxWait);
1259
} while (!RTListIsEmpty(&ListTmp));
1262
RTCritSectLeave(&pDisk->CritSect);
1266
* internal: read the specified amount of data in whatever blocks the backend
1267
* will give us - async version.
1269
static int vdReadHelperAsync(PVDIOCTX pIoCtx)
1272
size_t cbToRead = pIoCtx->cbTransfer;
1273
uint64_t uOffset = pIoCtx->uOffset;
1274
PVDIMAGE pCurrImage = NULL;
1277
/* Loop until all reads started or we have a backend which needs to read metadata. */
1280
pCurrImage = pIoCtx->pImageCur;
1282
/* Search for image with allocated block. Do not attempt to read more
1283
* than the previous reads marked as valid. Otherwise this would return
1284
* stale data when different block sizes are used for the images. */
1285
cbThisRead = cbToRead;
1288
* Try to read from the given image.
1289
* If the block is not allocated read from override chain if present.
1291
rc = pCurrImage->Backend->pfnAsyncRead(pCurrImage->pBackendData,
1292
uOffset, cbThisRead,
1293
pIoCtx, &cbThisRead);
1295
if (rc == VERR_VD_BLOCK_FREE)
1297
for (pCurrImage = pCurrImage->pPrev;
1298
pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
1299
pCurrImage = pCurrImage->pPrev)
1301
rc = pCurrImage->Backend->pfnAsyncRead(pCurrImage->pBackendData,
1302
uOffset, cbThisRead,
1303
pIoCtx, &cbThisRead);
1307
/* The task state will be updated on success already, don't do it here!. */
1308
if (rc == VERR_VD_BLOCK_FREE)
1310
/* No image in the chain contains the data for the block. */
1311
vdIoCtxSet(pIoCtx, '\0', cbThisRead);
1312
ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbThisRead);
1315
else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1317
else if (rc == VERR_VD_IOCTX_HALT)
1319
uOffset += cbThisRead;
1320
cbToRead -= cbThisRead;
1321
pIoCtx->fBlocked = true;
1327
cbToRead -= cbThisRead;
1328
uOffset += cbThisRead;
1329
} while (cbToRead != 0 && RT_SUCCESS(rc));
1331
if ( rc == VERR_VD_NOT_ENOUGH_METADATA
1332
|| rc == VERR_VD_IOCTX_HALT)
1334
/* Save the current state. */
1335
pIoCtx->uOffset = uOffset;
1336
pIoCtx->cbTransfer = cbToRead;
1337
pIoCtx->pImageCur = pCurrImage ? pCurrImage : pIoCtx->pImageStart;
1344
* internal: parent image read wrapper for compacting.
1346
static int vdParentRead(void *pvUser, uint64_t uOffset, void *pvBuf,
1349
PVDPARENTSTATEDESC pParentState = (PVDPARENTSTATEDESC)pvUser;
1350
return vdReadHelper(pParentState->pDisk, pParentState->pImage, NULL, uOffset,
1351
pvBuf, cbRead, true /* fZeroFreeBlocks */,
1352
false /* fUpdateCache */);
1356
* internal: mark the disk as not modified.
1358
static void vdResetModifiedFlag(PVBOXHDD pDisk)
1360
if (pDisk->uModified & VD_IMAGE_MODIFIED_FLAG)
1362
/* generate new last-modified uuid */
1363
if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
1367
RTUuidCreate(&Uuid);
1368
pDisk->pLast->Backend->pfnSetModificationUuid(pDisk->pLast->pBackendData,
1372
pDisk->pCache->Backend->pfnSetModificationUuid(pDisk->pCache->pBackendData,
1376
pDisk->uModified &= ~VD_IMAGE_MODIFIED_FLAG;
1381
* internal: mark the disk as modified.
1383
static void vdSetModifiedFlag(PVBOXHDD pDisk)
1385
pDisk->uModified |= VD_IMAGE_MODIFIED_FLAG;
1386
if (pDisk->uModified & VD_IMAGE_MODIFIED_FIRST)
1388
pDisk->uModified &= ~VD_IMAGE_MODIFIED_FIRST;
1390
/* First modify, so create a UUID and ensure it's written to disk. */
1391
vdResetModifiedFlag(pDisk);
1393
if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
1394
pDisk->pLast->Backend->pfnFlush(pDisk->pLast->pBackendData);
1399
* internal: write a complete block (only used for diff images), taking the
1400
* remaining data from parent images. This implementation does not optimize
1401
* anything (except that it tries to read only that portions from parent
1402
* images that are really needed).
1404
static int vdWriteHelperStandard(PVBOXHDD pDisk, PVDIMAGE pImage,
1405
PVDIMAGE pImageParentOverride,
1406
uint64_t uOffset, size_t cbWrite,
1407
size_t cbThisWrite, size_t cbPreRead,
1408
size_t cbPostRead, const void *pvBuf,
1411
int rc = VINF_SUCCESS;
1413
/* Read the data that goes before the write to fill the block. */
1417
* Updating the cache doesn't make sense here because
1418
* this will be done after the complete block was written.
1420
rc = vdReadHelper(pDisk, pImage, pImageParentOverride,
1421
uOffset - cbPreRead, pvTmp, cbPreRead,
1422
true /* fZeroFreeBlocks*/,
1423
false /* fUpdateCache */);
1428
/* Copy the data to the right place in the buffer. */
1429
memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
1431
/* Read the data that goes after the write to fill the block. */
1434
/* If we have data to be written, use that instead of reading
1435
* data from the image. */
1437
if (cbWrite > cbThisWrite)
1438
cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
1441
/* Figure out how much we cannot read from the image, because
1442
* the last block to write might exceed the nominal size of the
1443
* image for technical reasons. */
1445
if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
1446
cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
1449
/* The rest must be read from the image. */
1450
size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
1452
/* Now assemble the remaining data. */
1454
memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
1455
(char *)pvBuf + cbThisWrite, cbWriteCopy);
1457
rc = vdReadHelper(pDisk, pImage, pImageParentOverride,
1458
uOffset + cbThisWrite + cbWriteCopy,
1459
(char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy,
1460
cbReadImage, true /* fZeroFreeBlocks */,
1461
false /* fUpdateCache */);
1464
/* Zero out the remainder of this block. Will never be visible, as this
1465
* is beyond the limit of the image. */
1467
memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
1471
/* Write the full block to the virtual disk. */
1472
rc = pImage->Backend->pfnWrite(pImage->pBackendData,
1473
uOffset - cbPreRead, pvTmp,
1474
cbPreRead + cbThisWrite + cbPostRead,
1475
NULL, &cbPreRead, &cbPostRead, 0);
1476
Assert(rc != VERR_VD_BLOCK_FREE);
1477
Assert(cbPreRead == 0);
1478
Assert(cbPostRead == 0);
1484
* internal: write a complete block (only used for diff images), taking the
1485
* remaining data from parent images. This implementation optimizes out writes
1486
* that do not change the data relative to the state as of the parent images.
1487
* All backends which support differential/growing images support this.
1489
static int vdWriteHelperOptimized(PVBOXHDD pDisk, PVDIMAGE pImage,
1490
PVDIMAGE pImageParentOverride,
1491
uint64_t uOffset, size_t cbWrite,
1492
size_t cbThisWrite, size_t cbPreRead,
1493
size_t cbPostRead, const void *pvBuf,
1497
size_t cbWriteCopy = 0;
1498
size_t cbReadImage = 0;
1503
/* Figure out how much we cannot read from the image, because
1504
* the last block to write might exceed the nominal size of the
1505
* image for technical reasons. */
1506
if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
1507
cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
1509
/* If we have data to be written, use that instead of reading
1510
* data from the image. */
1511
if (cbWrite > cbThisWrite)
1512
cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
1514
/* The rest must be read from the image. */
1515
cbReadImage = cbPostRead - cbWriteCopy - cbFill;
1518
/* Read the entire data of the block so that we can compare whether it will
1519
* be modified by the write or not. */
1520
rc = vdReadHelper(pDisk, pImage, pImageParentOverride, uOffset - cbPreRead, pvTmp,
1521
cbPreRead + cbThisWrite + cbPostRead - cbFill,
1522
true /* fZeroFreeBlocks */,
1523
false /* fUpdateCache */);
1527
/* Check if the write would modify anything in this block. */
1528
if ( !memcmp((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite)
1529
&& (!cbWriteCopy || !memcmp((char *)pvTmp + cbPreRead + cbThisWrite,
1530
(char *)pvBuf + cbThisWrite, cbWriteCopy)))
1532
/* Block is completely unchanged, so no need to write anything. */
1533
return VINF_SUCCESS;
1536
/* Copy the data to the right place in the buffer. */
1537
memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
1539
/* Handle the data that goes after the write to fill the block. */
1542
/* Now assemble the remaining data. */
1544
memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
1545
(char *)pvBuf + cbThisWrite, cbWriteCopy);
1546
/* Zero out the remainder of this block. Will never be visible, as this
1547
* is beyond the limit of the image. */
1549
memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
1553
/* Write the full block to the virtual disk. */
1554
rc = pImage->Backend->pfnWrite(pImage->pBackendData,
1555
uOffset - cbPreRead, pvTmp,
1556
cbPreRead + cbThisWrite + cbPostRead,
1557
NULL, &cbPreRead, &cbPostRead, 0);
1558
Assert(rc != VERR_VD_BLOCK_FREE);
1559
Assert(cbPreRead == 0);
1560
Assert(cbPostRead == 0);
1566
* internal: write buffer to the image, taking care of block boundaries and
1567
* write optimizations.
1569
static int vdWriteHelper(PVBOXHDD pDisk, PVDIMAGE pImage,
1570
PVDIMAGE pImageParentOverride, uint64_t uOffset,
1571
const void *pvBuf, size_t cbWrite,
1577
size_t cbPreRead, cbPostRead;
1578
uint64_t uOffsetCur = uOffset;
1579
size_t cbWriteCur = cbWrite;
1580
const void *pcvBufCur = pvBuf;
1582
/* Loop until all written. */
1585
/* Try to write the possibly partial block to the last opened image.
1586
* This works when the block is already allocated in this image or
1587
* if it is a full-block write (and allocation isn't suppressed below).
1588
* For image formats which don't support zero blocks, it's beneficial
1589
* to avoid unnecessarily allocating unchanged blocks. This prevents
1590
* unwanted expanding of images. VMDK is an example. */
1591
cbThisWrite = cbWriteCur;
1592
fWrite = (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
1593
? 0 : VD_WRITE_NO_ALLOC;
1594
rc = pImage->Backend->pfnWrite(pImage->pBackendData, uOffsetCur, pcvBufCur,
1595
cbThisWrite, &cbThisWrite, &cbPreRead,
1596
&cbPostRead, fWrite);
1597
if (rc == VERR_VD_BLOCK_FREE)
1599
void *pvTmp = RTMemTmpAlloc(cbPreRead + cbThisWrite + cbPostRead);
1600
AssertBreakStmt(VALID_PTR(pvTmp), rc = VERR_NO_MEMORY);
1602
if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME))
1604
/* Optimized write, suppress writing to a so far unallocated
1605
* block if the data is in fact not changed. */
1606
rc = vdWriteHelperOptimized(pDisk, pImage, pImageParentOverride,
1607
uOffsetCur, cbWriteCur,
1608
cbThisWrite, cbPreRead, cbPostRead,
1613
/* Normal write, not optimized in any way. The block will
1614
* be written no matter what. This will usually (unless the
1615
* backend has some further optimization enabled) cause the
1616
* block to be allocated. */
1617
rc = vdWriteHelperStandard(pDisk, pImage, pImageParentOverride,
1618
uOffsetCur, cbWriteCur,
1619
cbThisWrite, cbPreRead, cbPostRead,
1622
RTMemTmpFree(pvTmp);
1627
cbWriteCur -= cbThisWrite;
1628
uOffsetCur += cbThisWrite;
1629
pcvBufCur = (char *)pcvBufCur + cbThisWrite;
1630
} while (cbWriteCur != 0 && RT_SUCCESS(rc));
1632
/* Update the cache on success */
1636
rc = vdCacheWriteHelper(pDisk->pCache, uOffset, pvBuf, cbWrite, NULL);
1642
* internal: write a complete block (only used for diff images), taking the
1643
* remaining data from parent images. This implementation does not optimize
1644
* anything (except that it tries to read only that portions from parent
1645
* images that are really needed) - async version.
1647
static int vdWriteHelperStandardAsync(PVDIOCTX pIoCtx)
1649
int rc = VINF_SUCCESS;
1653
/* Read the data that goes before the write to fill the block. */
1656
rc = vdReadHelperAsync(pIoCtxDst);
1661
/* Copy the data to the right place in the buffer. */
1662
vdIoCtxCopy(pIoCtxDst, pIoCtxSrc, cbThisWrite);
1664
/* Read the data that goes after the write to fill the block. */
1667
/* If we have data to be written, use that instead of reading
1668
* data from the image. */
1670
if (cbWrite > cbThisWrite)
1671
cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
1674
/* Figure out how much we cannot read from the image, because
1675
* the last block to write might exceed the nominal size of the
1676
* image for technical reasons. */
1678
if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
1679
cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
1682
/* The rest must be read from the image. */
1683
size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
1685
/* Now assemble the remaining data. */
1688
vdIoCtxCopy(pIoCtxDst, pIoCtxSrc, cbWriteCopy);
1689
ASMAtomicSubU32(&pIoCtxDst->cbTransferLeft, cbWriteCopy);
1693
rc = vdReadHelperAsync(pDisk, pImage, pImageParentOverride, pIoCtxDst,
1694
uOffset + cbThisWrite + cbWriteCopy,
1698
/* Zero out the remainder of this block. Will never be visible, as this
1699
* is beyond the limit of the image. */
1702
vdIoCtxSet(pIoCtxDst, '\0', cbFill);
1703
ASMAtomicSubU32(&pIoCtxDst->cbTransferLeft, cbFill);
1707
if ( !pIoCtxDst->cbTransferLeft
1708
&& !pIoCtxDst->cMetaTransfersPending
1709
&& ASMAtomicCmpXchgBool(&pIoCtxDst->fComplete, true, false))
1711
/* Write the full block to the virtual disk. */
1712
vdIoCtxChildReset(pIoCtxDst);
1713
rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData,
1714
uOffset - cbPreRead,
1715
cbPreRead + cbThisWrite + cbPostRead,
1717
NULL, &cbPreRead, &cbPostRead, 0);
1718
Assert(rc != VERR_VD_BLOCK_FREE);
1719
Assert(cbPreRead == 0);
1720
Assert(cbPostRead == 0);
1724
LogFlow(("cbTransferLeft=%u cMetaTransfersPending=%u fComplete=%RTbool\n",
1725
pIoCtxDst->cbTransferLeft, pIoCtxDst->cMetaTransfersPending,
1726
pIoCtxDst->fComplete));
1727
rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1732
return VERR_NOT_IMPLEMENTED;
1735
static int vdWriteHelperOptimizedCommitAsync(PVDIOCTX pIoCtx)
1737
int rc = VINF_SUCCESS;
1738
PVDIMAGE pImage = pIoCtx->pImageCur;
1739
size_t cbPreRead = pIoCtx->Type.Child.cbPreRead;
1740
size_t cbPostRead = pIoCtx->Type.Child.cbPostRead;
1741
size_t cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
1743
LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
1744
rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData,
1745
pIoCtx->uOffset - cbPreRead,
1746
cbPreRead + cbThisWrite + cbPostRead,
1747
pIoCtx, NULL, &cbPreRead, &cbPostRead, 0);
1748
Assert(rc != VERR_VD_BLOCK_FREE);
1749
Assert(rc == VERR_VD_NOT_ENOUGH_METADATA || cbPreRead == 0);
1750
Assert(rc == VERR_VD_NOT_ENOUGH_METADATA || cbPostRead == 0);
1751
if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1753
else if (rc == VERR_VD_IOCTX_HALT)
1755
pIoCtx->fBlocked = true;
1759
LogFlowFunc(("returns rc=%Rrc\n", rc));
1763
static int vdWriteHelperOptimizedCmpAndWriteAsync(PVDIOCTX pIoCtx)
1765
int rc = VINF_SUCCESS;
1766
PVDIMAGE pImage = pIoCtx->pImageCur;
1767
size_t cbThisWrite = 0;
1768
size_t cbPreRead = pIoCtx->Type.Child.cbPreRead;
1769
size_t cbPostRead = pIoCtx->Type.Child.cbPostRead;
1770
size_t cbWriteCopy = pIoCtx->Type.Child.Write.Optimized.cbWriteCopy;
1771
size_t cbFill = pIoCtx->Type.Child.Write.Optimized.cbFill;
1772
size_t cbReadImage = pIoCtx->Type.Child.Write.Optimized.cbReadImage;
1773
PVDIOCTX pIoCtxParent = pIoCtx->pIoCtxParent;
1775
LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
1777
AssertPtr(pIoCtxParent);
1778
Assert(!pIoCtxParent->pIoCtxParent);
1779
Assert(!pIoCtx->cbTransferLeft && !pIoCtx->cMetaTransfersPending);
1781
vdIoCtxChildReset(pIoCtx);
1782
cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
1783
RTSgBufAdvance(&pIoCtx->SgBuf, cbPreRead);
1785
/* Check if the write would modify anything in this block. */
1786
if (!RTSgBufCmp(&pIoCtx->SgBuf, &pIoCtxParent->SgBuf, cbThisWrite))
1788
RTSGBUF SgBufSrcTmp;
1790
RTSgBufClone(&SgBufSrcTmp, &pIoCtxParent->SgBuf);
1791
RTSgBufAdvance(&SgBufSrcTmp, cbThisWrite);
1792
RTSgBufAdvance(&pIoCtx->SgBuf, cbThisWrite);
1794
if (!cbWriteCopy || !RTSgBufCmp(&pIoCtx->SgBuf, &SgBufSrcTmp, cbWriteCopy))
1796
/* Block is completely unchanged, so no need to write anything. */
1797
LogFlowFunc(("Block didn't changed\n"));
1798
ASMAtomicWriteU32(&pIoCtx->cbTransferLeft, 0);
1799
RTSgBufAdvance(&pIoCtxParent->SgBuf, cbThisWrite);
1800
return VINF_VD_ASYNC_IO_FINISHED;
1804
/* Copy the data to the right place in the buffer. */
1805
RTSgBufReset(&pIoCtx->SgBuf);
1806
RTSgBufAdvance(&pIoCtx->SgBuf, cbPreRead);
1807
vdIoCtxCopy(pIoCtx, pIoCtxParent, cbThisWrite);
1809
/* Handle the data that goes after the write to fill the block. */
1812
/* Now assemble the remaining data. */
1816
* The S/G buffer of the parent needs to be cloned because
1817
* it is not allowed to modify the state.
1819
RTSGBUF SgBufParentTmp;
1821
RTSgBufClone(&SgBufParentTmp, &pIoCtxParent->SgBuf);
1822
RTSgBufCopy(&pIoCtx->SgBuf, &SgBufParentTmp, cbWriteCopy);
1825
/* Zero out the remainder of this block. Will never be visible, as this
1826
* is beyond the limit of the image. */
1829
RTSgBufAdvance(&pIoCtx->SgBuf, cbReadImage);
1830
vdIoCtxSet(pIoCtx, '\0', cbFill);
1834
/* Write the full block to the virtual disk. */
1835
RTSgBufReset(&pIoCtx->SgBuf);
1836
pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedCommitAsync;
1841
static int vdWriteHelperOptimizedPreReadAsync(PVDIOCTX pIoCtx)
1843
int rc = VINF_SUCCESS;
1845
LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
1847
if (pIoCtx->cbTransferLeft)
1848
rc = vdReadHelperAsync(pIoCtx);
1851
&& ( pIoCtx->cbTransferLeft
1852
|| pIoCtx->cMetaTransfersPending))
1853
rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1855
pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedCmpAndWriteAsync;
1861
* internal: write a complete block (only used for diff images), taking the
1862
* remaining data from parent images. This implementation optimizes out writes
1863
* that do not change the data relative to the state as of the parent images.
1864
* All backends which support differential/growing images support this - async version.
1866
static int vdWriteHelperOptimizedAsync(PVDIOCTX pIoCtx)
1868
PVBOXHDD pDisk = pIoCtx->pDisk;
1869
uint64_t uOffset = pIoCtx->Type.Child.uOffsetSaved;
1870
size_t cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
1871
size_t cbPreRead = pIoCtx->Type.Child.cbPreRead;
1872
size_t cbPostRead = pIoCtx->Type.Child.cbPostRead;
1873
size_t cbWrite = pIoCtx->Type.Child.cbWriteParent;
1875
size_t cbWriteCopy = 0;
1876
size_t cbReadImage = 0;
1878
LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
1880
AssertPtr(pIoCtx->pIoCtxParent);
1881
Assert(!pIoCtx->pIoCtxParent->pIoCtxParent);
1885
/* Figure out how much we cannot read from the image, because
1886
* the last block to write might exceed the nominal size of the
1887
* image for technical reasons. */
1888
if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
1889
cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
1891
/* If we have data to be written, use that instead of reading
1892
* data from the image. */
1893
if (cbWrite > cbThisWrite)
1894
cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
1896
/* The rest must be read from the image. */
1897
cbReadImage = cbPostRead - cbWriteCopy - cbFill;
1900
pIoCtx->Type.Child.Write.Optimized.cbFill = cbFill;
1901
pIoCtx->Type.Child.Write.Optimized.cbWriteCopy = cbWriteCopy;
1902
pIoCtx->Type.Child.Write.Optimized.cbReadImage = cbReadImage;
1904
/* Read the entire data of the block so that we can compare whether it will
1905
* be modified by the write or not. */
1906
pIoCtx->cbTransferLeft = cbPreRead + cbThisWrite + cbPostRead - cbFill;
1907
pIoCtx->cbTransfer = pIoCtx->cbTransferLeft;
1908
pIoCtx->uOffset -= cbPreRead;
1911
pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedPreReadAsync;
1912
return VINF_SUCCESS;
1916
* internal: write buffer to the image, taking care of block boundaries and
1917
* write optimizations - async version.
1919
static int vdWriteHelperAsync(PVDIOCTX pIoCtx)
1922
size_t cbWrite = pIoCtx->cbTransfer;
1923
uint64_t uOffset = pIoCtx->uOffset;
1924
PVDIMAGE pImage = pIoCtx->pImageCur;
1925
PVBOXHDD pDisk = pIoCtx->pDisk;
1928
size_t cbPreRead, cbPostRead;
1930
/* Loop until all written. */
1933
/* Try to write the possibly partial block to the last opened image.
1934
* This works when the block is already allocated in this image or
1935
* if it is a full-block write (and allocation isn't suppressed below).
1936
* For image formats which don't support zero blocks, it's beneficial
1937
* to avoid unnecessarily allocating unchanged blocks. This prevents
1938
* unwanted expanding of images. VMDK is an example. */
1939
cbThisWrite = cbWrite;
1940
fWrite = (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
1941
? 0 : VD_WRITE_NO_ALLOC;
1942
rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData, uOffset,
1943
cbThisWrite, pIoCtx,
1944
&cbThisWrite, &cbPreRead,
1945
&cbPostRead, fWrite);
1946
if (rc == VERR_VD_BLOCK_FREE)
1948
/* Lock the disk .*/
1949
rc = vdIoCtxLockDisk(pDisk, pIoCtx);
1953
* Allocate segment and buffer in one go.
1954
* A bit hackish but avoids the need to allocate memory twice.
1956
PRTSGBUF pTmp = (PRTSGBUF)RTMemAlloc(cbPreRead + cbThisWrite + cbPostRead + sizeof(RTSGSEG) + sizeof(RTSGBUF));
1957
AssertBreakStmt(VALID_PTR(pTmp), rc = VERR_NO_MEMORY);
1958
PRTSGSEG pSeg = (PRTSGSEG)(pTmp + 1);
1960
pSeg->pvSeg = pSeg + 1;
1961
pSeg->cbSeg = cbPreRead + cbThisWrite + cbPostRead;
1962
RTSgBufInit(pTmp, pSeg, 1);
1964
PVDIOCTX pIoCtxWrite = vdIoCtxChildAlloc(pDisk, VDIOCTXTXDIR_WRITE,
1965
uOffset, pSeg->cbSeg, pImage,
1967
pIoCtx, cbThisWrite,
1970
(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
1971
? vdWriteHelperStandardAsync
1972
: vdWriteHelperOptimizedAsync);
1973
if (!VALID_PTR(pIoCtxWrite))
1976
rc = VERR_NO_MEMORY;
1980
LogFlowFunc(("Disk is growing because of pIoCtx=%#p pIoCtxWrite=%#p\n",
1981
pIoCtx, pIoCtxWrite));
1983
pIoCtxWrite->Type.Child.cbPreRead = cbPreRead;
1984
pIoCtxWrite->Type.Child.cbPostRead = cbPostRead;
1986
/* Process the write request */
1987
rc = vdIoCtxProcess(pIoCtxWrite);
1989
if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
1991
vdIoCtxFree(pDisk, pIoCtxWrite);
1994
else if ( rc == VINF_VD_ASYNC_IO_FINISHED
1995
&& ASMAtomicCmpXchgBool(&pIoCtxWrite->fComplete, true, false))
1997
LogFlow(("Child write request completed\n"));
1998
Assert(pIoCtx->cbTransferLeft >= cbThisWrite);
1999
ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbThisWrite);
2000
ASMAtomicWriteBool(&pDisk->fLocked, false);
2001
vdIoCtxFree(pDisk, pIoCtxWrite);
2007
LogFlow(("Child write pending\n"));
2008
pIoCtx->fBlocked = true;
2009
rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2010
cbWrite -= cbThisWrite;
2011
uOffset += cbThisWrite;
2017
rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2022
if (rc == VERR_VD_IOCTX_HALT)
2024
cbWrite -= cbThisWrite;
2025
uOffset += cbThisWrite;
2026
pIoCtx->fBlocked = true;
2029
else if (rc == VERR_VD_NOT_ENOUGH_METADATA)
2032
cbWrite -= cbThisWrite;
2033
uOffset += cbThisWrite;
2034
} while (cbWrite != 0 && (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS));
2036
if ( rc == VERR_VD_ASYNC_IO_IN_PROGRESS
2037
|| rc == VERR_VD_NOT_ENOUGH_METADATA
2038
|| rc == VERR_VD_IOCTX_HALT)
2041
* Tell the caller that we don't need to go back here because all
2042
* writes are initiated.
2047
pIoCtx->uOffset = uOffset;
2048
pIoCtx->cbTransfer = cbWrite;
2055
* Flush helper async version.
2057
static int vdFlushHelperAsync(PVDIOCTX pIoCtx)
2059
int rc = VINF_SUCCESS;
2060
PVBOXHDD pDisk = pIoCtx->pDisk;
2061
PVDIMAGE pImage = pIoCtx->pImageCur;
2063
rc = vdIoCtxLockDisk(pDisk, pIoCtx);
2066
vdResetModifiedFlag(pDisk);
2067
rc = pImage->Backend->pfnAsyncFlush(pImage->pBackendData, pIoCtx);
2068
if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2076
* internal: scans plugin directory and loads the backends have been found.
2078
static int vdLoadDynamicBackends()
2080
#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
2081
int rc = VINF_SUCCESS;
2082
PRTDIR pPluginDir = NULL;
2084
/* Enumerate plugin backends. */
2085
char szPath[RTPATH_MAX];
2086
rc = RTPathAppPrivateArch(szPath, sizeof(szPath));
2090
/* To get all entries with VBoxHDD as prefix. */
2091
char *pszPluginFilter = RTPathJoinA(szPath, VBOX_HDDFORMAT_PLUGIN_PREFIX "*");
2092
if (!pszPluginFilter)
2093
return VERR_NO_STR_MEMORY;
2095
PRTDIRENTRYEX pPluginDirEntry = NULL;
2096
size_t cbPluginDirEntry = sizeof(RTDIRENTRYEX);
2097
/* The plugins are in the same directory as the other shared libs. */
2098
rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT);
2101
/* On Windows the above immediately signals that there are no
2102
* files matching, while on other platforms enumerating the
2103
* files below fails. Either way: no plugins. */
2107
pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
2108
if (!pPluginDirEntry)
2110
rc = VERR_NO_MEMORY;
2114
while ((rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK)) != VERR_NO_MORE_FILES)
2116
RTLDRMOD hPlugin = NIL_RTLDRMOD;
2117
PFNVBOXHDDFORMATLOAD pfnHDDFormatLoad = NULL;
2118
PVBOXHDDBACKEND pBackend = NULL;
2119
char *pszPluginPath = NULL;
2121
if (rc == VERR_BUFFER_OVERFLOW)
2123
/* allocate new buffer. */
2124
RTMemFree(pPluginDirEntry);
2125
pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbPluginDirEntry);
2126
if (!pPluginDirEntry)
2128
rc = VERR_NO_MEMORY;
2132
rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
2136
else if (RT_FAILURE(rc))
2139
/* We got the new entry. */
2140
if (!RTFS_IS_FILE(pPluginDirEntry->Info.Attr.fMode))
2143
/* Prepend the path to the libraries. */
2144
pszPluginPath = RTPathJoinA(szPath, pPluginDirEntry->szName);
2147
rc = VERR_NO_STR_MEMORY;
2151
rc = SUPR3HardenedLdrLoadPlugIn(pszPluginPath, &hPlugin, NULL);
2154
rc = RTLdrGetSymbol(hPlugin, VBOX_HDDFORMAT_LOAD_NAME, (void**)&pfnHDDFormatLoad);
2155
if (RT_FAILURE(rc) || !pfnHDDFormatLoad)
2157
LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnHDDFormat=%#p\n", VBOX_HDDFORMAT_LOAD_NAME, pPluginDirEntry->szName, rc, pfnHDDFormatLoad));
2159
rc = VERR_SYMBOL_NOT_FOUND;
2164
/* Get the function table. */
2165
rc = pfnHDDFormatLoad(&pBackend);
2166
if (RT_SUCCESS(rc) && pBackend->cbSize == sizeof(VBOXHDDBACKEND))
2168
pBackend->hPlugin = hPlugin;
2169
vdAddBackend(pBackend);
2172
LogFunc(("ignored plugin '%s': pBackend->cbSize=%d rc=%Rrc\n", pszPluginPath, pBackend->cbSize, rc));
2175
LogFunc(("ignored plugin '%s': rc=%Rrc\n", pszPluginPath, rc));
2178
RTLdrClose(hPlugin);
2180
RTStrFree(pszPluginPath);
2183
if (rc == VERR_NO_MORE_FILES)
2185
RTStrFree(pszPluginFilter);
2186
if (pPluginDirEntry)
2187
RTMemFree(pPluginDirEntry);
2189
RTDirClose(pPluginDir);
2192
return VINF_SUCCESS;
2197
* internal: scans plugin directory and loads the cache backends have been found.
2199
static int vdLoadDynamicCacheBackends()
2201
#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
2202
int rc = VINF_SUCCESS;
2203
PRTDIR pPluginDir = NULL;
2205
/* Enumerate plugin backends. */
2206
char szPath[RTPATH_MAX];
2207
rc = RTPathAppPrivateArch(szPath, sizeof(szPath));
2211
/* To get all entries with VBoxHDD as prefix. */
2212
char *pszPluginFilter = RTPathJoinA(szPath, VD_CACHEFORMAT_PLUGIN_PREFIX "*");
2213
if (!pszPluginFilter)
2215
rc = VERR_NO_STR_MEMORY;
2219
PRTDIRENTRYEX pPluginDirEntry = NULL;
2220
size_t cbPluginDirEntry = sizeof(RTDIRENTRYEX);
2221
/* The plugins are in the same directory as the other shared libs. */
2222
rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT);
2225
/* On Windows the above immediately signals that there are no
2226
* files matching, while on other platforms enumerating the
2227
* files below fails. Either way: no plugins. */
2231
pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
2232
if (!pPluginDirEntry)
2234
rc = VERR_NO_MEMORY;
2238
while ((rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK)) != VERR_NO_MORE_FILES)
2240
RTLDRMOD hPlugin = NIL_RTLDRMOD;
2241
PFNVDCACHEFORMATLOAD pfnVDCacheLoad = NULL;
2242
PVDCACHEBACKEND pBackend = NULL;
2243
char *pszPluginPath = NULL;
2245
if (rc == VERR_BUFFER_OVERFLOW)
2247
/* allocate new buffer. */
2248
RTMemFree(pPluginDirEntry);
2249
pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbPluginDirEntry);
2250
if (!pPluginDirEntry)
2252
rc = VERR_NO_MEMORY;
2256
rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
2260
else if (RT_FAILURE(rc))
2263
/* We got the new entry. */
2264
if (!RTFS_IS_FILE(pPluginDirEntry->Info.Attr.fMode))
2267
/* Prepend the path to the libraries. */
2268
pszPluginPath = RTPathJoinA(szPath, pPluginDirEntry->szName);
2271
rc = VERR_NO_STR_MEMORY;
2275
rc = SUPR3HardenedLdrLoadPlugIn(pszPluginPath, &hPlugin, NULL);
2278
rc = RTLdrGetSymbol(hPlugin, VD_CACHEFORMAT_LOAD_NAME, (void**)&pfnVDCacheLoad);
2279
if (RT_FAILURE(rc) || !pfnVDCacheLoad)
2281
LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnVDCacheLoad=%#p\n",
2282
VD_CACHEFORMAT_LOAD_NAME, pPluginDirEntry->szName, rc, pfnVDCacheLoad));
2284
rc = VERR_SYMBOL_NOT_FOUND;
2289
/* Get the function table. */
2290
rc = pfnVDCacheLoad(&pBackend);
2291
if (RT_SUCCESS(rc) && pBackend->cbSize == sizeof(VDCACHEBACKEND))
2293
pBackend->hPlugin = hPlugin;
2294
vdAddCacheBackend(pBackend);
2297
LogFunc(("ignored plugin '%s': pBackend->cbSize=%d rc=%Rrc\n", pszPluginPath, pBackend->cbSize, rc));
2300
LogFunc(("ignored plugin '%s': rc=%Rrc\n", pszPluginPath, rc));
2303
RTLdrClose(hPlugin);
2305
RTStrFree(pszPluginPath);
2308
if (rc == VERR_NO_MORE_FILES)
2310
RTStrFree(pszPluginFilter);
2311
if (pPluginDirEntry)
2312
RTMemFree(pPluginDirEntry);
2314
RTDirClose(pPluginDir);
2317
return VINF_SUCCESS;
2322
* VD async I/O interface open callback.
2324
static int vdIOOpenFallback(void *pvUser, const char *pszLocation,
2325
uint32_t fOpen, PFNVDCOMPLETED pfnCompleted,
2328
PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)RTMemAllocZ(sizeof(VDIIOFALLBACKSTORAGE));
2331
return VERR_NO_MEMORY;
2333
pStorage->pfnCompleted = pfnCompleted;
2335
/* Open the file. */
2336
int rc = RTFileOpen(&pStorage->File, pszLocation, fOpen);
2339
*ppStorage = pStorage;
2340
return VINF_SUCCESS;
2343
RTMemFree(pStorage);
2348
* VD async I/O interface close callback.
2350
static int vdIOCloseFallback(void *pvUser, void *pvStorage)
2352
PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
2354
RTFileClose(pStorage->File);
2355
RTMemFree(pStorage);
2356
return VINF_SUCCESS;
2359
static int vdIODeleteFallback(void *pvUser, const char *pcszFilename)
2361
return RTFileDelete(pcszFilename);
2364
static int vdIOMoveFallback(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
2366
return RTFileMove(pcszSrc, pcszDst, fMove);
2369
static int vdIOGetFreeSpaceFallback(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
2371
return RTFsQuerySizes(pcszFilename, NULL, pcbFreeSpace, NULL, NULL);
2374
static int vdIOGetModificationTimeFallback(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
2377
int rc = RTPathQueryInfo(pcszFilename, &info, RTFSOBJATTRADD_NOTHING);
2379
*pModificationTime = info.ModificationTime;
2384
* VD async I/O interface callback for retrieving the file size.
2386
static int vdIOGetSizeFallback(void *pvUser, void *pvStorage, uint64_t *pcbSize)
2388
PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
2390
return RTFileGetSize(pStorage->File, pcbSize);
2394
* VD async I/O interface callback for setting the file size.
2396
static int vdIOSetSizeFallback(void *pvUser, void *pvStorage, uint64_t cbSize)
2398
PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
2400
return RTFileSetSize(pStorage->File, cbSize);
2404
* VD async I/O interface callback for a synchronous write to the file.
2406
static int vdIOWriteSyncFallback(void *pvUser, void *pvStorage, uint64_t uOffset,
2407
const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
2409
PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
2411
return RTFileWriteAt(pStorage->File, uOffset, pvBuf, cbWrite, pcbWritten);
2415
* VD async I/O interface callback for a synchronous read from the file.
2417
static int vdIOReadSyncFallback(void *pvUser, void *pvStorage, uint64_t uOffset,
2418
void *pvBuf, size_t cbRead, size_t *pcbRead)
2420
PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
2422
return RTFileReadAt(pStorage->File, uOffset, pvBuf, cbRead, pcbRead);
2426
* VD async I/O interface callback for a synchronous flush of the file data.
2428
static int vdIOFlushSyncFallback(void *pvUser, void *pvStorage)
2430
PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
2432
return RTFileFlush(pStorage->File);
2436
* VD async I/O interface callback for a asynchronous read from the file.
2438
static int vdIOReadAsyncFallback(void *pvUser, void *pStorage, uint64_t uOffset,
2439
PCRTSGSEG paSegments, size_t cSegments,
2440
size_t cbRead, void *pvCompletion,
2443
return VERR_NOT_IMPLEMENTED;
2447
* VD async I/O interface callback for a asynchronous write to the file.
2449
static int vdIOWriteAsyncFallback(void *pvUser, void *pStorage, uint64_t uOffset,
2450
PCRTSGSEG paSegments, size_t cSegments,
2451
size_t cbWrite, void *pvCompletion,
2454
return VERR_NOT_IMPLEMENTED;
2458
* VD async I/O interface callback for a asynchronous flush of the file data.
2460
static int vdIOFlushAsyncFallback(void *pvUser, void *pStorage,
2461
void *pvCompletion, void **ppTask)
2463
return VERR_NOT_IMPLEMENTED;
2467
* Internal - Continues an I/O context after
2468
* it was halted because of an active transfer.
2470
static int vdIoCtxContinue(PVDIOCTX pIoCtx, int rcReq)
2472
PVBOXHDD pDisk = pIoCtx->pDisk;
2473
int rc = VINF_SUCCESS;
2475
if (RT_FAILURE(rcReq))
2476
ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rcReq, VINF_SUCCESS);
2478
if (!pIoCtx->fBlocked)
2480
/* Continue the transfer */
2481
rc = vdIoCtxProcess(pIoCtx);
2483
if ( rc == VINF_VD_ASYNC_IO_FINISHED
2484
&& ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
2486
LogFlowFunc(("I/O context completed pIoCtx=%#p\n", pIoCtx));
2487
if (pIoCtx->pIoCtxParent)
2489
PVDIOCTX pIoCtxParent = pIoCtx->pIoCtxParent;
2491
LogFlowFunc(("I/O context transferred %u bytes for the parent pIoCtxParent=%p\n",
2492
pIoCtx->Type.Child.cbTransferParent, pIoCtxParent));
2494
/* Update the parent state. */
2495
Assert(!pIoCtxParent->pIoCtxParent);
2496
Assert(pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE);
2497
Assert(pIoCtxParent->cbTransferLeft >= pIoCtx->Type.Child.cbTransferParent);
2498
ASMAtomicSubU32(&pIoCtxParent->cbTransferLeft, pIoCtx->Type.Child.cbTransferParent);
2500
if (RT_FAILURE(pIoCtx->rcReq))
2501
ASMAtomicCmpXchgS32(&pIoCtxParent->rcReq, pIoCtx->rcReq, VINF_SUCCESS);
2504
* A completed child write means that we finished growing the image.
2505
* We have to process any pending writes now.
2507
Assert(pDisk->fLocked);
2508
ASMAtomicWriteBool(&pDisk->fLocked, false);
2510
/* Unblock the parent */
2511
pIoCtxParent->fBlocked = false;
2513
rc = vdIoCtxProcess(pIoCtxParent);
2515
if ( rc == VINF_VD_ASYNC_IO_FINISHED
2516
&& ASMAtomicCmpXchgBool(&pIoCtxParent->fComplete, true, false))
2518
LogFlowFunc(("Parent I/O context completed pIoCtxParent=%#p rcReq=%Rrc\n", pIoCtxParent, pIoCtxParent->rcReq));
2519
pIoCtxParent->Type.Root.pfnComplete(pIoCtxParent->Type.Root.pvUser1,
2520
pIoCtxParent->Type.Root.pvUser2,
2521
pIoCtxParent->rcReq);
2522
vdThreadFinishWrite(pDisk);
2523
vdIoCtxFree(pDisk, pIoCtxParent);
2526
/* Process any pending writes if the current request didn't caused another growing. */
2527
RTCritSectEnter(&pDisk->CritSect);
2529
if (!RTListIsEmpty(&pDisk->ListWriteLocked) && !pDisk->fLocked)
2533
LogFlowFunc(("Before: pNext=%#p pPrev=%#p\n", pDisk->ListWriteLocked.pNext,
2534
pDisk->ListWriteLocked.pPrev));
2536
RTListMove(&ListTmp, &pDisk->ListWriteLocked);
2538
LogFlowFunc(("After: pNext=%#p pPrev=%#p\n", pDisk->ListWriteLocked.pNext,
2539
pDisk->ListWriteLocked.pPrev));
2541
RTCritSectLeave(&pDisk->CritSect);
2543
/* Process the list. */
2546
PVDIOCTXDEFERRED pDeferred = RTListGetFirst(&ListTmp, VDIOCTXDEFERRED, NodeDeferred);
2547
PVDIOCTX pIoCtxWait = pDeferred->pIoCtx;
2549
AssertPtr(pIoCtxWait);
2551
RTListNodeRemove(&pDeferred->NodeDeferred);
2552
RTMemFree(pDeferred);
2554
Assert(!pIoCtxWait->pIoCtxParent);
2556
pIoCtxWait->fBlocked = false;
2557
LogFlowFunc(("Processing waiting I/O context pIoCtxWait=%#p\n", pIoCtxWait));
2559
rc = vdIoCtxProcess(pIoCtxWait);
2560
if ( rc == VINF_VD_ASYNC_IO_FINISHED
2561
&& ASMAtomicCmpXchgBool(&pIoCtxWait->fComplete, true, false))
2563
LogFlowFunc(("Waiting I/O context completed pIoCtxWait=%#p\n", pIoCtxWait));
2564
vdThreadFinishWrite(pDisk);
2565
pIoCtxWait->Type.Root.pfnComplete(pIoCtxWait->Type.Root.pvUser1,
2566
pIoCtxWait->Type.Root.pvUser2,
2568
vdIoCtxFree(pDisk, pIoCtxWait);
2570
} while (!RTListIsEmpty(&ListTmp));
2573
RTCritSectLeave(&pDisk->CritSect);
2577
if (pIoCtx->enmTxDir == VDIOCTXTXDIR_FLUSH)
2579
vdIoCtxUnlockDisk(pDisk, pIoCtx);
2580
vdThreadFinishWrite(pDisk);
2582
else if (pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE)
2583
vdThreadFinishWrite(pDisk);
2586
Assert(pIoCtx->enmTxDir == VDIOCTXTXDIR_READ);
2587
vdThreadFinishRead(pDisk);
2590
LogFlowFunc(("I/O context completed pIoCtx=%#p rcReq=%Rrc\n", pIoCtx, pIoCtx->rcReq));
2591
pIoCtx->Type.Root.pfnComplete(pIoCtx->Type.Root.pvUser1,
2592
pIoCtx->Type.Root.pvUser2,
2596
vdIoCtxFree(pDisk, pIoCtx);
2600
return VINF_SUCCESS;
2604
* Internal - Called when user transfer completed.
2606
static int vdUserXferCompleted(PVDIOSTORAGE pIoStorage, PVDIOCTX pIoCtx,
2607
PFNVDXFERCOMPLETED pfnComplete, void *pvUser,
2608
size_t cbTransfer, int rcReq)
2610
int rc = VINF_SUCCESS;
2611
bool fIoCtxContinue = true;
2612
PVBOXHDD pDisk = pIoCtx->pDisk;
2614
LogFlowFunc(("pIoStorage=%#p pIoCtx=%#p pfnComplete=%#p pvUser=%#p cbTransfer=%zu rcReq=%Rrc\n",
2615
pIoStorage, pIoCtx, pfnComplete, pvUser, cbTransfer, rcReq));
2617
Assert(pIoCtx->cbTransferLeft >= cbTransfer);
2618
ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTransfer);
2619
ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
2623
RTCritSectEnter(&pDisk->CritSect);
2624
rc = pfnComplete(pIoStorage->pVDIo->pBackendData, pIoCtx, pvUser, rcReq);
2625
RTCritSectLeave(&pDisk->CritSect);
2629
rc = vdIoCtxContinue(pIoCtx, rcReq);
2630
else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2637
* Internal - Called when a meta transfer completed.
2639
static int vdMetaXferCompleted(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser,
2640
PVDMETAXFER pMetaXfer, int rcReq)
2642
PVBOXHDD pDisk = pIoStorage->pVDIo->pDisk;
2643
RTLISTNODE ListIoCtxWaiting;
2646
LogFlowFunc(("pIoStorage=%#p pfnComplete=%#p pvUser=%#p pMetaXfer=%#p rcReq=%Rrc\n",
2647
pIoStorage, pfnComplete, pvUser, pMetaXfer, rcReq));
2649
RTCritSectEnter(&pDisk->CritSect);
2650
fFlush = VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_FLUSH;
2651
VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
2655
RTListMove(&ListIoCtxWaiting, &pMetaXfer->ListIoCtxWaiting);
2657
if (RT_FAILURE(rcReq))
2659
/* Remove from the AVL tree. */
2660
LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
2661
bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
2663
RTMemFree(pMetaXfer);
2667
/* Increase the reference counter to make sure it doesn't go away before the last context is processed. */
2672
RTListMove(&ListIoCtxWaiting, &pMetaXfer->ListIoCtxWaiting);
2673
RTCritSectLeave(&pDisk->CritSect);
2675
/* Go through the waiting list and continue the I/O contexts. */
2676
while (!RTListIsEmpty(&ListIoCtxWaiting))
2678
int rc = VINF_SUCCESS;
2679
bool fContinue = true;
2680
PVDIOCTXDEFERRED pDeferred = RTListGetFirst(&ListIoCtxWaiting, VDIOCTXDEFERRED, NodeDeferred);
2681
PVDIOCTX pIoCtx = pDeferred->pIoCtx;
2682
RTListNodeRemove(&pDeferred->NodeDeferred);
2684
RTMemFree(pDeferred);
2685
ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
2689
RTCritSectEnter(&pDisk->CritSect);
2690
rc = pfnComplete(pIoStorage->pVDIo->pBackendData, pIoCtx, pvUser, rcReq);
2691
RTCritSectLeave(&pDisk->CritSect);
2694
LogFlow(("Completion callback for I/O context %#p returned %Rrc\n", pIoCtx, rc));
2698
rc = vdIoCtxContinue(pIoCtx, rcReq);
2702
Assert(rc == VERR_VD_ASYNC_IO_IN_PROGRESS);
2705
/* Remove if not used anymore. */
2706
if (RT_SUCCESS(rcReq) && !fFlush)
2708
RTCritSectEnter(&pDisk->CritSect);
2710
if (!pMetaXfer->cRefs && RTListIsEmpty(&pMetaXfer->ListIoCtxWaiting))
2712
/* Remove from the AVL tree. */
2713
LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
2714
bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
2716
RTMemFree(pMetaXfer);
2718
RTCritSectLeave(&pDisk->CritSect);
2721
RTMemFree(pMetaXfer);
2723
return VINF_SUCCESS;
2726
static int vdIOIntReqCompleted(void *pvUser, int rcReq)
2728
int rc = VINF_SUCCESS;
2729
PVDIOTASK pIoTask = (PVDIOTASK)pvUser;
2730
PVDIOSTORAGE pIoStorage = pIoTask->pIoStorage;
2732
LogFlowFunc(("Task completed pIoTask=%#p\n", pIoTask));
2734
if (!pIoTask->fMeta)
2735
rc = vdUserXferCompleted(pIoStorage, pIoTask->Type.User.pIoCtx,
2736
pIoTask->pfnComplete, pIoTask->pvUser,
2737
pIoTask->Type.User.cbTransfer, rcReq);
2739
rc = vdMetaXferCompleted(pIoStorage, pIoTask->pfnComplete, pIoTask->pvUser,
2740
pIoTask->Type.Meta.pMetaXfer, rcReq);
2742
vdIoTaskFree(pIoStorage->pVDIo->pDisk, pIoTask);
2748
* VD I/O interface callback for opening a file.
2750
static int vdIOIntOpen(void *pvUser, const char *pszLocation,
2751
unsigned uOpenFlags, PPVDIOSTORAGE ppIoStorage)
2753
int rc = VINF_SUCCESS;
2754
PVDIO pVDIo = (PVDIO)pvUser;
2755
PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTMemAllocZ(sizeof(VDIOSTORAGE));
2758
return VERR_NO_MEMORY;
2760
/* Create the AVl tree. */
2761
pIoStorage->pTreeMetaXfers = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
2762
if (pIoStorage->pTreeMetaXfers)
2764
rc = pVDIo->pInterfaceIOCallbacks->pfnOpen(pVDIo->pInterfaceIO->pvUser,
2765
pszLocation, uOpenFlags,
2766
vdIOIntReqCompleted,
2767
&pIoStorage->pStorage);
2770
pIoStorage->pVDIo = pVDIo;
2771
*ppIoStorage = pIoStorage;
2772
return VINF_SUCCESS;
2775
RTMemFree(pIoStorage->pTreeMetaXfers);
2778
rc = VERR_NO_MEMORY;
2780
RTMemFree(pIoStorage);
2784
static int vdIOIntTreeMetaXferDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
2786
AssertMsgFailed(("Tree should be empty at this point!\n"));
2787
return VINF_SUCCESS;
2790
static int vdIOIntClose(void *pvUser, PVDIOSTORAGE pIoStorage)
2792
PVDIO pVDIo = (PVDIO)pvUser;
2794
int rc = pVDIo->pInterfaceIOCallbacks->pfnClose(pVDIo->pInterfaceIO->pvUser,
2795
pIoStorage->pStorage);
2798
RTAvlrFileOffsetDestroy(pIoStorage->pTreeMetaXfers, vdIOIntTreeMetaXferDestroy, NULL);
2799
RTMemFree(pIoStorage->pTreeMetaXfers);
2800
RTMemFree(pIoStorage);
2801
return VINF_SUCCESS;
2804
static int vdIOIntDelete(void *pvUser, const char *pcszFilename)
2806
PVDIO pVDIo = (PVDIO)pvUser;
2807
return pVDIo->pInterfaceIOCallbacks->pfnDelete(pVDIo->pInterfaceIO->pvUser,
2811
static int vdIOIntMove(void *pvUser, const char *pcszSrc, const char *pcszDst,
2814
PVDIO pVDIo = (PVDIO)pvUser;
2815
return pVDIo->pInterfaceIOCallbacks->pfnMove(pVDIo->pInterfaceIO->pvUser,
2816
pcszSrc, pcszDst, fMove);
2819
static int vdIOIntGetFreeSpace(void *pvUser, const char *pcszFilename,
2820
int64_t *pcbFreeSpace)
2822
PVDIO pVDIo = (PVDIO)pvUser;
2823
return pVDIo->pInterfaceIOCallbacks->pfnGetFreeSpace(pVDIo->pInterfaceIO->pvUser,
2828
static int vdIOIntGetModificationTime(void *pvUser, const char *pcszFilename,
2829
PRTTIMESPEC pModificationTime)
2831
PVDIO pVDIo = (PVDIO)pvUser;
2832
return pVDIo->pInterfaceIOCallbacks->pfnGetModificationTime(pVDIo->pInterfaceIO->pvUser,
2837
static int vdIOIntGetSize(void *pvUser, PVDIOSTORAGE pIoStorage,
2840
PVDIO pVDIo = (PVDIO)pvUser;
2841
return pVDIo->pInterfaceIOCallbacks->pfnGetSize(pVDIo->pInterfaceIO->pvUser,
2842
pIoStorage->pStorage,
2846
static int vdIOIntSetSize(void *pvUser, PVDIOSTORAGE pIoStorage,
2849
PVDIO pVDIo = (PVDIO)pvUser;
2851
return pVDIo->pInterfaceIOCallbacks->pfnSetSize(pVDIo->pInterfaceIO->pvUser,
2852
pIoStorage->pStorage,
2856
static int vdIOIntWriteSync(void *pvUser, PVDIOSTORAGE pIoStorage,
2857
uint64_t uOffset, const void *pvBuf,
2858
size_t cbWrite, size_t *pcbWritten)
2860
PVDIO pVDIo = (PVDIO)pvUser;
2862
return pVDIo->pInterfaceIOCallbacks->pfnWriteSync(pVDIo->pInterfaceIO->pvUser,
2863
pIoStorage->pStorage,
2864
uOffset, pvBuf, cbWrite,
2868
static int vdIOIntReadSync(void *pvUser, PVDIOSTORAGE pIoStorage,
2869
uint64_t uOffset, void *pvBuf, size_t cbRead,
2872
PVDIO pVDIo = (PVDIO)pvUser;
2873
return pVDIo->pInterfaceIOCallbacks->pfnReadSync(pVDIo->pInterfaceIO->pvUser,
2874
pIoStorage->pStorage,
2875
uOffset, pvBuf, cbRead,
2879
static int vdIOIntFlushSync(void *pvUser, PVDIOSTORAGE pIoStorage)
2881
PVDIO pVDIo = (PVDIO)pvUser;
2882
return pVDIo->pInterfaceIOCallbacks->pfnFlushSync(pVDIo->pInterfaceIO->pvUser,
2883
pIoStorage->pStorage);
2886
static int vdIOIntReadUserAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
2887
uint64_t uOffset, PVDIOCTX pIoCtx,
2890
int rc = VINF_SUCCESS;
2891
PVDIO pVDIo = (PVDIO)pvUser;
2892
PVBOXHDD pDisk = pVDIo->pDisk;
2894
LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pIoCtx=%#p cbRead=%u\n",
2895
pvUser, pIoStorage, uOffset, pIoCtx, cbRead));
2897
VD_THREAD_IS_CRITSECT_OWNER(pDisk);
2899
/* Build the S/G array and spawn a new I/O task */
2902
RTSGSEG aSeg[VD_IO_TASK_SEGMENTS_MAX];
2903
unsigned cSegments = VD_IO_TASK_SEGMENTS_MAX;
2904
size_t cbTaskRead = 0;
2906
cbTaskRead = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, aSeg, &cSegments, cbRead);
2908
AssertMsg(cbTaskRead <= cbRead, ("Invalid number of bytes to read\n"));
2910
LogFlow(("Reading %u bytes into %u segments\n", cbTaskRead, cSegments));
2913
for (unsigned i = 0; i < cSegments; i++)
2914
AssertMsg(aSeg[i].pvSeg && !(aSeg[i].cbSeg % 512),
2915
("Segment %u is invalid\n", i));
2918
PVDIOTASK pIoTask = vdIoTaskUserAlloc(pIoStorage, NULL, NULL, pIoCtx, cbTaskRead);
2921
return VERR_NO_MEMORY;
2923
ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
2926
rc = pVDIo->pInterfaceIOCallbacks->pfnReadAsync(pVDIo->pInterfaceIO->pvUser,
2927
pIoStorage->pStorage,
2928
uOffset, aSeg, cSegments,
2929
cbTaskRead, pIoTask,
2933
AssertMsg(cbTaskRead <= pIoCtx->cbTransferLeft, ("Impossible!\n"));
2934
ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTaskRead);
2935
ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
2936
vdIoTaskFree(pDisk, pIoTask);
2938
else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
2940
ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
2944
uOffset += cbTaskRead;
2945
cbRead -= cbTaskRead;
2948
LogFlowFunc(("returns rc=%Rrc\n", rc));
2952
static int vdIOIntWriteUserAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
2953
uint64_t uOffset, PVDIOCTX pIoCtx,
2955
PFNVDXFERCOMPLETED pfnComplete,
2956
void *pvCompleteUser)
2958
int rc = VINF_SUCCESS;
2959
PVDIO pVDIo = (PVDIO)pvUser;
2960
PVBOXHDD pDisk = pVDIo->pDisk;
2962
LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pIoCtx=%#p cbWrite=%u\n",
2963
pvUser, pIoStorage, uOffset, pIoCtx, cbWrite));
2965
VD_THREAD_IS_CRITSECT_OWNER(pDisk);
2967
/* Build the S/G array and spawn a new I/O task */
2970
RTSGSEG aSeg[VD_IO_TASK_SEGMENTS_MAX];
2971
unsigned cSegments = VD_IO_TASK_SEGMENTS_MAX;
2972
size_t cbTaskWrite = 0;
2974
cbTaskWrite = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, aSeg, &cSegments, cbWrite);
2976
AssertMsg(cbTaskWrite <= cbWrite, ("Invalid number of bytes to write\n"));
2978
LogFlow(("Writing %u bytes from %u segments\n", cbTaskWrite, cSegments));
2981
for (unsigned i = 0; i < cSegments; i++)
2982
AssertMsg(aSeg[i].pvSeg && !(aSeg[i].cbSeg % 512),
2983
("Segment %u is invalid\n", i));
2986
PVDIOTASK pIoTask = vdIoTaskUserAlloc(pIoStorage, pfnComplete, pvCompleteUser, pIoCtx, cbTaskWrite);
2989
return VERR_NO_MEMORY;
2991
ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
2994
rc = pVDIo->pInterfaceIOCallbacks->pfnWriteAsync(pVDIo->pInterfaceIO->pvUser,
2995
pIoStorage->pStorage,
2996
uOffset, aSeg, cSegments,
2997
cbTaskWrite, pIoTask,
3001
AssertMsg(cbTaskWrite <= pIoCtx->cbTransferLeft, ("Impossible!\n"));
3002
ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTaskWrite);
3003
ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
3004
vdIoTaskFree(pDisk, pIoTask);
3006
else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
3008
ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
3012
uOffset += cbTaskWrite;
3013
cbWrite -= cbTaskWrite;
3019
static int vdIOIntReadMetaAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
3020
uint64_t uOffset, void *pvBuf,
3021
size_t cbRead, PVDIOCTX pIoCtx,
3022
PPVDMETAXFER ppMetaXfer,
3023
PFNVDXFERCOMPLETED pfnComplete,
3024
void *pvCompleteUser)
3026
PVDIO pVDIo = (PVDIO)pvUser;
3027
PVBOXHDD pDisk = pVDIo->pDisk;
3028
int rc = VINF_SUCCESS;
3031
PVDMETAXFER pMetaXfer = NULL;
3032
void *pvTask = NULL;
3034
LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pvBuf=%#p cbRead=%u\n",
3035
pvUser, pIoStorage, uOffset, pvBuf, cbRead));
3037
VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3039
pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGet(pIoStorage->pTreeMetaXfers, uOffset);
3043
pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGetBestFit(pIoStorage->pTreeMetaXfers, uOffset, false /* fAbove */);
3044
AssertMsg(!pMetaXfer || (pMetaXfer->Core.Key + (RTFOFF)pMetaXfer->cbMeta <= (RTFOFF)uOffset),
3045
("Overlapping meta transfers!\n"));
3048
/* Allocate a new meta transfer. */
3049
pMetaXfer = vdMetaXferAlloc(pIoStorage, uOffset, cbRead);
3051
return VERR_NO_MEMORY;
3053
pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvCompleteUser, pMetaXfer);
3056
RTMemFree(pMetaXfer);
3057
return VERR_NO_MEMORY;
3061
Seg.pvSeg = pMetaXfer->abData;
3063
VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_READ);
3064
rc = pVDIo->pInterfaceIOCallbacks->pfnReadAsync(pVDIo->pInterfaceIO->pvUser,
3065
pIoStorage->pStorage,
3070
if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
3072
bool fInserted = RTAvlrFileOffsetInsert(pIoStorage->pTreeMetaXfers, &pMetaXfer->Core);
3076
RTMemFree(pMetaXfer);
3080
VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
3081
vdIoTaskFree(pDisk, pIoTask);
3083
else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS && !pfnComplete)
3084
rc = VERR_VD_NOT_ENOUGH_METADATA;
3087
Assert(VALID_PTR(pMetaXfer) || RT_FAILURE(rc));
3089
if (RT_SUCCESS(rc) || rc == VERR_VD_NOT_ENOUGH_METADATA || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
3091
/* If it is pending add the request to the list. */
3092
if (VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_READ)
3094
PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
3095
AssertPtr(pDeferred);
3097
RTListInit(&pDeferred->NodeDeferred);
3098
pDeferred->pIoCtx = pIoCtx;
3100
ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
3101
RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
3102
rc = VERR_VD_NOT_ENOUGH_METADATA;
3106
/* Transfer the data. */
3108
Assert(pMetaXfer->cbMeta >= cbRead);
3109
Assert(pMetaXfer->Core.Key == (RTFOFF)uOffset);
3110
memcpy(pvBuf, pMetaXfer->abData, cbRead);
3111
*ppMetaXfer = pMetaXfer;
3118
static int vdIOIntWriteMetaAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
3119
uint64_t uOffset, void *pvBuf,
3120
size_t cbWrite, PVDIOCTX pIoCtx,
3121
PFNVDXFERCOMPLETED pfnComplete,
3122
void *pvCompleteUser)
3124
PVDIO pVDIo = (PVDIO)pvUser;
3125
PVBOXHDD pDisk = pVDIo->pDisk;
3126
int rc = VINF_SUCCESS;
3129
PVDMETAXFER pMetaXfer = NULL;
3130
bool fInTree = false;
3131
void *pvTask = NULL;
3133
LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pvBuf=%#p cbWrite=%u\n",
3134
pvUser, pIoStorage, uOffset, pvBuf, cbWrite));
3136
VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3138
pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGet(pIoStorage->pTreeMetaXfers, uOffset);
3141
/* Allocate a new meta transfer. */
3142
pMetaXfer = vdMetaXferAlloc(pIoStorage, uOffset, cbWrite);
3144
return VERR_NO_MEMORY;
3148
Assert(pMetaXfer->cbMeta >= cbWrite);
3149
Assert(pMetaXfer->Core.Key == (RTFOFF)uOffset);
3153
Assert(VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE);
3155
pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvCompleteUser, pMetaXfer);
3158
RTMemFree(pMetaXfer);
3159
return VERR_NO_MEMORY;
3162
memcpy(pMetaXfer->abData, pvBuf, cbWrite);
3163
Seg.cbSeg = cbWrite;
3164
Seg.pvSeg = pMetaXfer->abData;
3166
ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
3168
VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_WRITE);
3169
rc = pVDIo->pInterfaceIOCallbacks->pfnWriteAsync(pVDIo->pInterfaceIO->pvUser,
3170
pIoStorage->pStorage,
3176
VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
3177
ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
3178
vdIoTaskFree(pDisk, pIoTask);
3179
if (fInTree && !pMetaXfer->cRefs)
3181
LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
3182
bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
3183
AssertMsg(fRemoved, ("Metadata transfer wasn't removed\n"));
3184
RTMemFree(pMetaXfer);
3188
else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
3190
PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
3191
AssertPtr(pDeferred);
3193
RTListInit(&pDeferred->NodeDeferred);
3194
pDeferred->pIoCtx = pIoCtx;
3198
bool fInserted = RTAvlrFileOffsetInsert(pIoStorage->pTreeMetaXfers, &pMetaXfer->Core);
3202
RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
3206
RTMemFree(pMetaXfer);
3213
static void vdIOIntMetaXferRelease(void *pvUser, PVDMETAXFER pMetaXfer)
3215
PVDIO pVDIo = (PVDIO)pvUser;
3216
PVBOXHDD pDisk = pVDIo->pDisk;
3217
PVDIOSTORAGE pIoStorage = pMetaXfer->pIoStorage;
3219
VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3221
Assert( VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE
3222
|| VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_WRITE);
3223
Assert(pMetaXfer->cRefs > 0);
3226
if ( !pMetaXfer->cRefs
3227
&& RTListIsEmpty(&pMetaXfer->ListIoCtxWaiting)
3228
&& VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE)
3230
/* Free the meta data entry. */
3231
LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
3232
bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
3233
AssertMsg(fRemoved, ("Metadata transfer wasn't removed\n"));
3235
RTMemFree(pMetaXfer);
3239
static int vdIOIntFlushAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
3240
PVDIOCTX pIoCtx, PFNVDXFERCOMPLETED pfnComplete,
3241
void *pvCompleteUser)
3243
PVDIO pVDIo = (PVDIO)pvUser;
3244
PVBOXHDD pDisk = pVDIo->pDisk;
3245
int rc = VINF_SUCCESS;
3247
PVDMETAXFER pMetaXfer = NULL;
3248
void *pvTask = NULL;
3250
VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3252
LogFlowFunc(("pvUser=%#p pIoStorage=%#p pIoCtx=%#p\n",
3253
pvUser, pIoStorage, pIoCtx));
3255
/* Allocate a new meta transfer. */
3256
pMetaXfer = vdMetaXferAlloc(pIoStorage, 0, 0);
3258
return VERR_NO_MEMORY;
3260
pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvUser, pMetaXfer);
3263
RTMemFree(pMetaXfer);
3264
return VERR_NO_MEMORY;
3267
ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
3269
PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
3270
AssertPtr(pDeferred);
3272
RTListInit(&pDeferred->NodeDeferred);
3273
pDeferred->pIoCtx = pIoCtx;
3275
RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
3276
VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_FLUSH);
3277
rc = pVDIo->pInterfaceIOCallbacks->pfnFlushAsync(pVDIo->pInterfaceIO->pvUser,
3278
pIoStorage->pStorage,
3282
VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
3283
ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
3284
vdIoTaskFree(pDisk, pIoTask);
3285
RTMemFree(pDeferred);
3286
RTMemFree(pMetaXfer);
3288
else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
3289
RTMemFree(pMetaXfer);
3294
static size_t vdIOIntIoCtxCopyTo(void *pvUser, PVDIOCTX pIoCtx,
3295
void *pvBuf, size_t cbBuf)
3297
PVDIO pVDIo = (PVDIO)pvUser;
3298
PVBOXHDD pDisk = pVDIo->pDisk;
3299
size_t cbCopied = 0;
3301
VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3303
cbCopied = vdIoCtxCopyTo(pIoCtx, (uint8_t *)pvBuf, cbBuf);
3304
Assert(cbCopied == cbBuf);
3306
ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCopied);
3311
static size_t vdIOIntIoCtxCopyFrom(void *pvUser, PVDIOCTX pIoCtx,
3312
void *pvBuf, size_t cbBuf)
3314
PVDIO pVDIo = (PVDIO)pvUser;
3315
PVBOXHDD pDisk = pVDIo->pDisk;
3316
size_t cbCopied = 0;
3318
VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3320
cbCopied = vdIoCtxCopyFrom(pIoCtx, (uint8_t *)pvBuf, cbBuf);
3321
Assert(cbCopied == cbBuf);
3323
ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCopied);
3328
static size_t vdIOIntIoCtxSet(void *pvUser, PVDIOCTX pIoCtx, int ch, size_t cb)
3330
PVDIO pVDIo = (PVDIO)pvUser;
3331
PVBOXHDD pDisk = pVDIo->pDisk;
3334
VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3336
cbSet = vdIoCtxSet(pIoCtx, ch, cb);
3337
Assert(cbSet == cb);
3339
ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbSet);
3344
static size_t vdIOIntIoCtxSegArrayCreate(void *pvUser, PVDIOCTX pIoCtx,
3345
PRTSGSEG paSeg, unsigned *pcSeg,
3348
PVDIO pVDIo = (PVDIO)pvUser;
3349
PVBOXHDD pDisk = pVDIo->pDisk;
3350
size_t cbCreated = 0;
3352
VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3354
cbCreated = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, paSeg, pcSeg, cbData);
3355
Assert(!paSeg || cbData == cbCreated);
3360
static void vdIOIntIoCtxCompleted(void *pvUser, PVDIOCTX pIoCtx, int rcReq,
3364
pIoCtx->fBlocked = false;
3365
ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCompleted);
3367
/* Clear the pointer to next transfer function in case we have nothing to transfer anymore.
3368
* @todo: Find a better way to prevent vdIoCtxContinue from calling the read/write helper again. */
3369
if (!pIoCtx->cbTransferLeft)
3370
pIoCtx->pfnIoCtxTransfer = NULL;
3372
vdIoCtxContinue(pIoCtx, rcReq);
3376
* VD I/O interface callback for opening a file (limited version for VDGetFormat).
3378
static int vdIOIntOpenLimited(void *pvUser, const char *pszLocation,
3379
uint32_t fOpen, PPVDIOSTORAGE ppIoStorage)
3381
int rc = VINF_SUCCESS;
3382
PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3383
PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTMemAllocZ(sizeof(VDIOSTORAGE));
3386
return VERR_NO_MEMORY;
3388
rc = pInterfaceIOCallbacks->pfnOpen(NULL, pszLocation, fOpen,
3389
NULL, &pIoStorage->pStorage);
3391
*ppIoStorage = pIoStorage;
3393
RTMemFree(pIoStorage);
3398
static int vdIOIntCloseLimited(void *pvUser, PVDIOSTORAGE pIoStorage)
3400
PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3401
int rc = pInterfaceIOCallbacks->pfnClose(NULL, pIoStorage->pStorage);
3404
RTMemFree(pIoStorage);
3405
return VINF_SUCCESS;
3408
static int vdIOIntDeleteLimited(void *pvUser, const char *pcszFilename)
3410
PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3411
return pInterfaceIOCallbacks->pfnDelete(NULL, pcszFilename);
3414
static int vdIOIntMoveLimited(void *pvUser, const char *pcszSrc,
3415
const char *pcszDst, unsigned fMove)
3417
PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3418
return pInterfaceIOCallbacks->pfnMove(NULL, pcszSrc, pcszDst, fMove);
3421
static int vdIOIntGetFreeSpaceLimited(void *pvUser, const char *pcszFilename,
3422
int64_t *pcbFreeSpace)
3424
PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3425
return pInterfaceIOCallbacks->pfnGetFreeSpace(NULL, pcszFilename, pcbFreeSpace);
3428
static int vdIOIntGetModificationTimeLimited(void *pvUser,
3429
const char *pcszFilename,
3430
PRTTIMESPEC pModificationTime)
3432
PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3433
return pInterfaceIOCallbacks->pfnGetModificationTime(NULL, pcszFilename, pModificationTime);
3436
static int vdIOIntGetSizeLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
3439
PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3440
return pInterfaceIOCallbacks->pfnGetSize(NULL, pIoStorage->pStorage, pcbSize);
3443
static int vdIOIntSetSizeLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
3446
PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3447
return pInterfaceIOCallbacks->pfnSetSize(NULL, pIoStorage->pStorage, cbSize);
3450
static int vdIOIntWriteSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
3451
uint64_t uOffset, const void *pvBuf,
3452
size_t cbWrite, size_t *pcbWritten)
3454
PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3455
return pInterfaceIOCallbacks->pfnWriteSync(NULL, pIoStorage->pStorage, uOffset, pvBuf, cbWrite, pcbWritten);
3458
static int vdIOIntReadSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
3459
uint64_t uOffset, void *pvBuf, size_t cbRead,
3462
PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3463
return pInterfaceIOCallbacks->pfnReadSync(NULL, pIoStorage->pStorage, uOffset, pvBuf, cbRead, pcbRead);
3466
static int vdIOIntFlushSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage)
3468
PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3469
return pInterfaceIOCallbacks->pfnFlushSync(NULL, pIoStorage->pStorage);
3473
* internal: send output to the log (unconditionally).
3475
int vdLogMessage(void *pvUser, const char *pszFormat, va_list args)
3478
RTLogPrintfV(pszFormat, args);
3479
return VINF_SUCCESS;
3482
DECLINLINE(int) vdMessageWrapper(PVBOXHDD pDisk, const char *pszFormat, ...)
3485
va_start(va, pszFormat);
3486
int rc = pDisk->pInterfaceErrorCallbacks->pfnMessage(pDisk->pInterfaceError->pvUser,
3494
* internal: adjust PCHS geometry
3496
static void vdFixupPCHSGeometry(PVDGEOMETRY pPCHS, uint64_t cbSize)
3498
/* Fix broken PCHS geometry. Can happen for two reasons: either the backend
3499
* mixes up PCHS and LCHS, or the application used to create the source
3500
* image has put garbage in it. Additionally, if the PCHS geometry covers
3501
* more than the image size, set it back to the default. */
3502
if ( pPCHS->cHeads > 16
3503
|| pPCHS->cSectors > 63
3504
|| pPCHS->cCylinders == 0
3505
|| (uint64_t)pPCHS->cHeads * pPCHS->cSectors * pPCHS->cCylinders * 512 > cbSize)
3507
Assert(!(RT_MIN(cbSize / 512 / 16 / 63, 16383) - (uint32_t)RT_MIN(cbSize / 512 / 16 / 63, 16383)));
3508
pPCHS->cCylinders = (uint32_t)RT_MIN(cbSize / 512 / 16 / 63, 16383);
3510
pPCHS->cSectors = 63;
3515
* internal: adjust PCHS geometry
3517
static void vdFixupLCHSGeometry(PVDGEOMETRY pLCHS, uint64_t cbSize)
3519
/* Fix broken LCHS geometry. Can happen for two reasons: either the backend
3520
* mixes up PCHS and LCHS, or the application used to create the source
3521
* image has put garbage in it. The fix in this case is to clear the LCHS
3522
* geometry to trigger autodetection when it is used next. If the geometry
3523
* already says "please autodetect" (cylinders=0) keep it. */
3524
if ( ( pLCHS->cHeads > 255
3525
|| pLCHS->cHeads == 0
3526
|| pLCHS->cSectors > 63
3527
|| pLCHS->cSectors == 0)
3528
&& pLCHS->cCylinders != 0)
3530
pLCHS->cCylinders = 0;
3532
pLCHS->cSectors = 0;
3534
/* Always recompute the number of cylinders stored in the LCHS
3535
* geometry if it isn't set to "autotedetect" at the moment.
3536
* This is very useful if the destination image size is
3537
* larger or smaller than the source image size. Do not modify
3538
* the number of heads and sectors. Windows guests hate it. */
3539
if ( pLCHS->cCylinders != 0
3540
&& pLCHS->cHeads != 0 /* paranoia */
3541
&& pLCHS->cSectors != 0 /* paranoia */)
3543
Assert(!(RT_MIN(cbSize / 512 / pLCHS->cHeads / pLCHS->cSectors, 1024) - (uint32_t)RT_MIN(cbSize / 512 / pLCHS->cHeads / pLCHS->cSectors, 1024)));
3544
pLCHS->cCylinders = (uint32_t)RT_MIN(cbSize / 512 / pLCHS->cHeads / pLCHS->cSectors, 1024);
3549
* Initializes HDD backends.
3551
* @returns VBox status code.
3553
VBOXDDU_DECL(int) VDInit(void)
3555
int rc = vdAddBackends(aStaticBackends, RT_ELEMENTS(aStaticBackends));
3558
rc = vdAddCacheBackends(aStaticCacheBackends, RT_ELEMENTS(aStaticCacheBackends));
3561
rc = vdLoadDynamicBackends();
3563
rc = vdLoadDynamicCacheBackends();
3566
LogRel(("VDInit finished\n"));
3571
* Destroys loaded HDD backends.
3573
* @returns VBox status code.
3575
VBOXDDU_DECL(int) VDShutdown(void)
3577
PVBOXHDDBACKEND *pBackends = g_apBackends;
3578
PVDCACHEBACKEND *pCacheBackends = g_apCacheBackends;
3579
unsigned cBackends = g_cBackends;
3582
return VERR_INTERNAL_ERROR;
3585
g_apBackends = NULL;
3587
#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
3588
for (unsigned i = 0; i < cBackends; i++)
3589
if (pBackends[i]->hPlugin != NIL_RTLDRMOD)
3590
RTLdrClose(pBackends[i]->hPlugin);
3593
/* Clear the supported cache backends. */
3594
cBackends = g_cCacheBackends;
3595
g_cCacheBackends = 0;
3596
g_apCacheBackends = NULL;
3598
#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
3599
for (unsigned i = 0; i < cBackends; i++)
3600
if (pCacheBackends[i]->hPlugin != NIL_RTLDRMOD)
3601
RTLdrClose(pCacheBackends[i]->hPlugin);
3605
RTMemFree(pCacheBackends);
3606
RTMemFree(pBackends);
3607
return VINF_SUCCESS;
3612
* Lists all HDD backends and their capabilities in a caller-provided buffer.
3614
* @returns VBox status code.
3615
* VERR_BUFFER_OVERFLOW if not enough space is passed.
3616
* @param cEntriesAlloc Number of list entries available.
3617
* @param pEntries Pointer to array for the entries.
3618
* @param pcEntriesUsed Number of entries returned.
3620
VBOXDDU_DECL(int) VDBackendInfo(unsigned cEntriesAlloc, PVDBACKENDINFO pEntries,
3621
unsigned *pcEntriesUsed)
3623
int rc = VINF_SUCCESS;
3624
PRTDIR pPluginDir = NULL;
3625
unsigned cEntries = 0;
3627
LogFlowFunc(("cEntriesAlloc=%u pEntries=%#p pcEntriesUsed=%#p\n", cEntriesAlloc, pEntries, pcEntriesUsed));
3628
/* Check arguments. */
3629
AssertMsgReturn(cEntriesAlloc,
3630
("cEntriesAlloc=%u\n", cEntriesAlloc),
3631
VERR_INVALID_PARAMETER);
3632
AssertMsgReturn(VALID_PTR(pEntries),
3633
("pEntries=%#p\n", pEntries),
3634
VERR_INVALID_PARAMETER);
3635
AssertMsgReturn(VALID_PTR(pcEntriesUsed),
3636
("pcEntriesUsed=%#p\n", pcEntriesUsed),
3637
VERR_INVALID_PARAMETER);
3641
if (cEntriesAlloc < g_cBackends)
3643
*pcEntriesUsed = g_cBackends;
3644
return VERR_BUFFER_OVERFLOW;
3647
for (unsigned i = 0; i < g_cBackends; i++)
3649
pEntries[i].pszBackend = g_apBackends[i]->pszBackendName;
3650
pEntries[i].uBackendCaps = g_apBackends[i]->uBackendCaps;
3651
pEntries[i].paFileExtensions = g_apBackends[i]->paFileExtensions;
3652
pEntries[i].paConfigInfo = g_apBackends[i]->paConfigInfo;
3653
pEntries[i].pfnComposeLocation = g_apBackends[i]->pfnComposeLocation;
3654
pEntries[i].pfnComposeName = g_apBackends[i]->pfnComposeName;
3657
LogFlowFunc(("returns %Rrc *pcEntriesUsed=%u\n", rc, cEntries));
3658
*pcEntriesUsed = g_cBackends;
3663
* Lists the capabilities of a backend identified by its name.
3665
* @returns VBox status code.
3666
* @param pszBackend The backend name.
3667
* @param pEntries Pointer to an entry.
3669
VBOXDDU_DECL(int) VDBackendInfoOne(const char *pszBackend, PVDBACKENDINFO pEntry)
3671
LogFlowFunc(("pszBackend=%#p pEntry=%#p\n", pszBackend, pEntry));
3672
/* Check arguments. */
3673
AssertMsgReturn(VALID_PTR(pszBackend),
3674
("pszBackend=%#p\n", pszBackend),
3675
VERR_INVALID_PARAMETER);
3676
AssertMsgReturn(VALID_PTR(pEntry),
3677
("pEntry=%#p\n", pEntry),
3678
VERR_INVALID_PARAMETER);
3682
/* Go through loaded backends. */
3683
for (unsigned i = 0; i < g_cBackends; i++)
3685
if (!RTStrICmp(pszBackend, g_apBackends[i]->pszBackendName))
3687
pEntry->pszBackend = g_apBackends[i]->pszBackendName;
3688
pEntry->uBackendCaps = g_apBackends[i]->uBackendCaps;
3689
pEntry->paFileExtensions = g_apBackends[i]->paFileExtensions;
3690
pEntry->paConfigInfo = g_apBackends[i]->paConfigInfo;
3691
return VINF_SUCCESS;
3695
return VERR_NOT_FOUND;
3699
* Allocates and initializes an empty HDD container.
3700
* No image files are opened.
3702
* @returns VBox status code.
3703
* @param pVDIfsDisk Pointer to the per-disk VD interface list.
3704
* @param enmType Type of the image container.
3705
* @param ppDisk Where to store the reference to HDD container.
3707
VBOXDDU_DECL(int) VDCreate(PVDINTERFACE pVDIfsDisk, VDTYPE enmType, PVBOXHDD *ppDisk)
3709
int rc = VINF_SUCCESS;
3710
PVBOXHDD pDisk = NULL;
3712
LogFlowFunc(("pVDIfsDisk=%#p\n", pVDIfsDisk));
3715
/* Check arguments. */
3716
AssertMsgBreakStmt(VALID_PTR(ppDisk),
3717
("ppDisk=%#p\n", ppDisk),
3718
rc = VERR_INVALID_PARAMETER);
3720
pDisk = (PVBOXHDD)RTMemAllocZ(sizeof(VBOXHDD));
3723
pDisk->u32Signature = VBOXHDDDISK_SIGNATURE;
3724
pDisk->enmType = enmType;
3726
pDisk->pBase = NULL;
3727
pDisk->pLast = NULL;
3729
pDisk->PCHSGeometry.cCylinders = 0;
3730
pDisk->PCHSGeometry.cHeads = 0;
3731
pDisk->PCHSGeometry.cSectors = 0;
3732
pDisk->LCHSGeometry.cCylinders = 0;
3733
pDisk->LCHSGeometry.cHeads = 0;
3734
pDisk->LCHSGeometry.cSectors = 0;
3735
pDisk->pVDIfsDisk = pVDIfsDisk;
3736
pDisk->pInterfaceError = NULL;
3737
pDisk->pInterfaceErrorCallbacks = NULL;
3738
pDisk->pInterfaceThreadSync = NULL;
3739
pDisk->pInterfaceThreadSyncCallbacks = NULL;
3740
pDisk->fLocked = false;
3741
RTListInit(&pDisk->ListWriteLocked);
3743
/* Create the I/O ctx cache */
3744
rc = RTMemCacheCreate(&pDisk->hMemCacheIoCtx, sizeof(VDIOCTX), 0, UINT32_MAX,
3745
NULL, NULL, NULL, 0);
3752
/* Create the I/O task cache */
3753
rc = RTMemCacheCreate(&pDisk->hMemCacheIoTask, sizeof(VDIOTASK), 0, UINT32_MAX,
3754
NULL, NULL, NULL, 0);
3757
RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
3762
/* Create critical section. */
3763
rc = RTCritSectInit(&pDisk->CritSect);
3766
RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
3767
RTMemCacheDestroy(pDisk->hMemCacheIoTask);
3772
pDisk->pInterfaceError = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_ERROR);
3773
if (pDisk->pInterfaceError)
3774
pDisk->pInterfaceErrorCallbacks = VDGetInterfaceError(pDisk->pInterfaceError);
3776
pDisk->pInterfaceThreadSync = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_THREADSYNC);
3777
if (pDisk->pInterfaceThreadSync)
3778
pDisk->pInterfaceThreadSyncCallbacks = VDGetInterfaceThreadSync(pDisk->pInterfaceThreadSync);
3780
/* Create fallback I/O callback table */
3781
pDisk->VDIIOCallbacks.cbSize = sizeof(VDINTERFACEIO);
3782
pDisk->VDIIOCallbacks.enmInterface = VDINTERFACETYPE_IO;
3783
pDisk->VDIIOCallbacks.pfnOpen = vdIOOpenFallback;
3784
pDisk->VDIIOCallbacks.pfnClose = vdIOCloseFallback;
3785
pDisk->VDIIOCallbacks.pfnDelete = vdIODeleteFallback;
3786
pDisk->VDIIOCallbacks.pfnMove = vdIOMoveFallback;
3787
pDisk->VDIIOCallbacks.pfnGetFreeSpace = vdIOGetFreeSpaceFallback;
3788
pDisk->VDIIOCallbacks.pfnGetModificationTime = vdIOGetModificationTimeFallback;
3789
pDisk->VDIIOCallbacks.pfnGetSize = vdIOGetSizeFallback;
3790
pDisk->VDIIOCallbacks.pfnSetSize = vdIOSetSizeFallback;
3791
pDisk->VDIIOCallbacks.pfnReadSync = vdIOReadSyncFallback;
3792
pDisk->VDIIOCallbacks.pfnWriteSync = vdIOWriteSyncFallback;
3793
pDisk->VDIIOCallbacks.pfnFlushSync = vdIOFlushSyncFallback;
3794
pDisk->VDIIOCallbacks.pfnReadAsync = vdIOReadAsyncFallback;
3795
pDisk->VDIIOCallbacks.pfnWriteAsync = vdIOWriteAsyncFallback;
3796
pDisk->VDIIOCallbacks.pfnFlushAsync = vdIOFlushAsyncFallback;
3799
* Create the internal I/O callback table.
3800
* The interface is per-image but no need to duplicate the
3801
* callback table every time.
3803
pDisk->VDIIOIntCallbacks.cbSize = sizeof(VDINTERFACEIOINT);
3804
pDisk->VDIIOIntCallbacks.enmInterface = VDINTERFACETYPE_IOINT;
3805
pDisk->VDIIOIntCallbacks.pfnOpen = vdIOIntOpen;
3806
pDisk->VDIIOIntCallbacks.pfnClose = vdIOIntClose;
3807
pDisk->VDIIOIntCallbacks.pfnDelete = vdIOIntDelete;
3808
pDisk->VDIIOIntCallbacks.pfnMove = vdIOIntMove;
3809
pDisk->VDIIOIntCallbacks.pfnGetFreeSpace = vdIOIntGetFreeSpace;
3810
pDisk->VDIIOIntCallbacks.pfnGetModificationTime = vdIOIntGetModificationTime;
3811
pDisk->VDIIOIntCallbacks.pfnGetSize = vdIOIntGetSize;
3812
pDisk->VDIIOIntCallbacks.pfnSetSize = vdIOIntSetSize;
3813
pDisk->VDIIOIntCallbacks.pfnReadSync = vdIOIntReadSync;
3814
pDisk->VDIIOIntCallbacks.pfnWriteSync = vdIOIntWriteSync;
3815
pDisk->VDIIOIntCallbacks.pfnFlushSync = vdIOIntFlushSync;
3816
pDisk->VDIIOIntCallbacks.pfnReadUserAsync = vdIOIntReadUserAsync;
3817
pDisk->VDIIOIntCallbacks.pfnWriteUserAsync = vdIOIntWriteUserAsync;
3818
pDisk->VDIIOIntCallbacks.pfnReadMetaAsync = vdIOIntReadMetaAsync;
3819
pDisk->VDIIOIntCallbacks.pfnWriteMetaAsync = vdIOIntWriteMetaAsync;
3820
pDisk->VDIIOIntCallbacks.pfnMetaXferRelease = vdIOIntMetaXferRelease;
3821
pDisk->VDIIOIntCallbacks.pfnFlushAsync = vdIOIntFlushAsync;
3822
pDisk->VDIIOIntCallbacks.pfnIoCtxCopyFrom = vdIOIntIoCtxCopyFrom;
3823
pDisk->VDIIOIntCallbacks.pfnIoCtxCopyTo = vdIOIntIoCtxCopyTo;
3824
pDisk->VDIIOIntCallbacks.pfnIoCtxSet = vdIOIntIoCtxSet;
3825
pDisk->VDIIOIntCallbacks.pfnIoCtxSegArrayCreate = vdIOIntIoCtxSegArrayCreate;
3826
pDisk->VDIIOIntCallbacks.pfnIoCtxCompleted = vdIOIntIoCtxCompleted;
3832
rc = VERR_NO_MEMORY;
3837
LogFlowFunc(("returns %Rrc (pDisk=%#p)\n", rc, pDisk));
3842
* Destroys HDD container.
3843
* If container has opened image files they will be closed.
3845
* @param pDisk Pointer to HDD container.
3847
VBOXDDU_DECL(void) VDDestroy(PVBOXHDD pDisk)
3849
LogFlowFunc(("pDisk=%#p\n", pDisk));
3853
AssertPtrBreak(pDisk);
3854
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3856
RTCritSectDelete(&pDisk->CritSect);
3857
RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
3858
RTMemCacheDestroy(pDisk->hMemCacheIoTask);
3861
LogFlowFunc(("returns\n"));
3865
* Try to get the backend name which can use this image.
3867
* @returns VBox status code.
3868
* VINF_SUCCESS if a plugin was found.
3869
* ppszFormat contains the string which can be used as backend name.
3870
* VERR_NOT_SUPPORTED if no backend was found.
3871
* @param pVDIfsDisk Pointer to the per-disk VD interface list.
3872
* @param pVDIfsImage Pointer to the per-image VD interface list.
3873
* @param pszFilename Name of the image file for which the backend is queried.
3874
* @param ppszFormat Receives pointer of the UTF-8 string which contains the format name.
3875
* The returned pointer must be freed using RTStrFree().
3877
VBOXDDU_DECL(int) VDGetFormat(PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
3878
const char *pszFilename, char **ppszFormat, VDTYPE *penmType)
3880
int rc = VERR_NOT_SUPPORTED;
3881
VDINTERFACEIOINT VDIIOIntCallbacks;
3882
VDINTERFACE VDIIOInt;
3883
VDINTERFACEIO VDIIOCallbacksFallback;
3884
PVDINTERFACE pInterfaceIO;
3885
PVDINTERFACEIO pInterfaceIOCallbacks;
3887
LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
3888
/* Check arguments. */
3889
AssertMsgReturn(VALID_PTR(pszFilename) && *pszFilename,
3890
("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
3891
VERR_INVALID_PARAMETER);
3892
AssertMsgReturn(VALID_PTR(ppszFormat),
3893
("ppszFormat=%#p\n", ppszFormat),
3894
VERR_INVALID_PARAMETER);
3895
AssertMsgReturn(VALID_PTR(ppszFormat),
3896
("penmType=%#p\n", penmType),
3897
VERR_INVALID_PARAMETER);
3902
pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
3906
* Caller doesn't provide an I/O interface, create our own using the
3909
VDIIOCallbacksFallback.cbSize = sizeof(VDINTERFACEIO);
3910
VDIIOCallbacksFallback.enmInterface = VDINTERFACETYPE_IO;
3911
VDIIOCallbacksFallback.pfnOpen = vdIOOpenFallback;
3912
VDIIOCallbacksFallback.pfnClose = vdIOCloseFallback;
3913
VDIIOCallbacksFallback.pfnDelete = vdIODeleteFallback;
3914
VDIIOCallbacksFallback.pfnMove = vdIOMoveFallback;
3915
VDIIOCallbacksFallback.pfnGetFreeSpace = vdIOGetFreeSpaceFallback;
3916
VDIIOCallbacksFallback.pfnGetModificationTime = vdIOGetModificationTimeFallback;
3917
VDIIOCallbacksFallback.pfnGetSize = vdIOGetSizeFallback;
3918
VDIIOCallbacksFallback.pfnSetSize = vdIOSetSizeFallback;
3919
VDIIOCallbacksFallback.pfnReadSync = vdIOReadSyncFallback;
3920
VDIIOCallbacksFallback.pfnWriteSync = vdIOWriteSyncFallback;
3921
VDIIOCallbacksFallback.pfnFlushSync = vdIOFlushSyncFallback;
3922
pInterfaceIOCallbacks = &VDIIOCallbacksFallback;
3925
pInterfaceIOCallbacks = VDGetInterfaceIO(pInterfaceIO);
3927
/* Set up the internal I/O interface. */
3928
AssertReturn(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
3929
VERR_INVALID_PARAMETER);
3930
VDIIOIntCallbacks.cbSize = sizeof(VDINTERFACEIOINT);
3931
VDIIOIntCallbacks.enmInterface = VDINTERFACETYPE_IOINT;
3932
VDIIOIntCallbacks.pfnOpen = vdIOIntOpenLimited;
3933
VDIIOIntCallbacks.pfnClose = vdIOIntCloseLimited;
3934
VDIIOIntCallbacks.pfnDelete = vdIOIntDeleteLimited;
3935
VDIIOIntCallbacks.pfnMove = vdIOIntMoveLimited;
3936
VDIIOIntCallbacks.pfnGetFreeSpace = vdIOIntGetFreeSpaceLimited;
3937
VDIIOIntCallbacks.pfnGetModificationTime = vdIOIntGetModificationTimeLimited;
3938
VDIIOIntCallbacks.pfnGetSize = vdIOIntGetSizeLimited;
3939
VDIIOIntCallbacks.pfnSetSize = vdIOIntSetSizeLimited;
3940
VDIIOIntCallbacks.pfnReadSync = vdIOIntReadSyncLimited;
3941
VDIIOIntCallbacks.pfnWriteSync = vdIOIntWriteSyncLimited;
3942
VDIIOIntCallbacks.pfnFlushSync = vdIOIntFlushSyncLimited;
3943
VDIIOIntCallbacks.pfnReadUserAsync = NULL;
3944
VDIIOIntCallbacks.pfnWriteUserAsync = NULL;
3945
VDIIOIntCallbacks.pfnReadMetaAsync = NULL;
3946
VDIIOIntCallbacks.pfnWriteMetaAsync = NULL;
3947
VDIIOIntCallbacks.pfnFlushAsync = NULL;
3948
rc = VDInterfaceAdd(&VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
3949
&VDIIOIntCallbacks, pInterfaceIOCallbacks, &pVDIfsImage);
3952
/* Find the backend supporting this file format. */
3953
for (unsigned i = 0; i < g_cBackends; i++)
3955
if (g_apBackends[i]->pfnCheckIfValid)
3957
rc = g_apBackends[i]->pfnCheckIfValid(pszFilename, pVDIfsDisk,
3958
pVDIfsImage, penmType);
3960
/* The correct backend has been found, but there is a small
3961
* incompatibility so that the file cannot be used. Stop here
3962
* and signal success - the actual open will of course fail,
3963
* but that will create a really sensible error message. */
3964
|| ( rc != VERR_VD_GEN_INVALID_HEADER
3965
&& rc != VERR_VD_VDI_INVALID_HEADER
3966
&& rc != VERR_VD_VMDK_INVALID_HEADER
3967
&& rc != VERR_VD_ISCSI_INVALID_HEADER
3968
&& rc != VERR_VD_VHD_INVALID_HEADER
3969
&& rc != VERR_VD_RAW_INVALID_HEADER
3970
&& rc != VERR_VD_PARALLELS_INVALID_HEADER
3971
&& rc != VERR_VD_DMG_INVALID_HEADER))
3973
/* Copy the name into the new string. */
3974
char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
3977
rc = VERR_NO_MEMORY;
3980
*ppszFormat = pszFormat;
3984
rc = VERR_NOT_SUPPORTED;
3988
/* Try the cache backends. */
3989
if (rc == VERR_NOT_SUPPORTED)
3991
for (unsigned i = 0; i < g_cCacheBackends; i++)
3993
if (g_apCacheBackends[i]->pfnProbe)
3995
rc = g_apCacheBackends[i]->pfnProbe(pszFilename, pVDIfsDisk,
3998
|| (rc != VERR_VD_GEN_INVALID_HEADER))
4000
/* Copy the name into the new string. */
4001
char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
4004
rc = VERR_NO_MEMORY;
4007
*ppszFormat = pszFormat;
4011
rc = VERR_NOT_SUPPORTED;
4016
LogFlowFunc(("returns %Rrc *ppszFormat=\"%s\"\n", rc, *ppszFormat));
4021
* Opens an image file.
4023
* The first opened image file in HDD container must have a base image type,
4024
* others (next opened images) must be a differencing or undo images.
4025
* Linkage is checked for differencing image to be in consistence with the previously opened image.
4026
* When another differencing image is opened and the last image was opened in read/write access
4027
* mode, then the last image is reopened in read-only with deny write sharing mode. This allows
4028
* other processes to use images in read-only mode too.
4030
* Note that the image is opened in read-only mode if a read/write open is not possible.
4031
* Use VDIsReadOnly to check open mode.
4033
* @returns VBox status code.
4034
* @param pDisk Pointer to HDD container.
4035
* @param pszBackend Name of the image file backend to use.
4036
* @param pszFilename Name of the image file to open.
4037
* @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
4038
* @param pVDIfsImage Pointer to the per-image VD interface list.
4040
VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszBackend,
4041
const char *pszFilename, unsigned uOpenFlags,
4042
PVDINTERFACE pVDIfsImage)
4044
int rc = VINF_SUCCESS;
4046
bool fLockWrite = false;
4047
PVDIMAGE pImage = NULL;
4049
LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsImage=%#p\n",
4050
pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsImage));
4055
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4056
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4058
/* Check arguments. */
4059
AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
4060
("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
4061
rc = VERR_INVALID_PARAMETER);
4062
AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
4063
("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4064
rc = VERR_INVALID_PARAMETER);
4065
AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
4066
("uOpenFlags=%#x\n", uOpenFlags),
4067
rc = VERR_INVALID_PARAMETER);
4069
/* Set up image descriptor. */
4070
pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
4073
rc = VERR_NO_MEMORY;
4076
pImage->pszFilename = RTStrDup(pszFilename);
4077
if (!pImage->pszFilename)
4079
rc = VERR_NO_MEMORY;
4083
pImage->VDIo.pDisk = pDisk;
4084
pImage->pVDIfsImage = pVDIfsImage;
4086
rc = vdFindBackend(pszBackend, &pImage->Backend);
4089
if (!pImage->Backend)
4091
rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4092
N_("VD: unknown backend name '%s'"), pszBackend);
4096
/* Set up the I/O interface. */
4097
pImage->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
4098
if (pImage->VDIo.pInterfaceIO)
4099
pImage->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->VDIo.pInterfaceIO);
4102
rc = VDInterfaceAdd(&pImage->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
4103
&pDisk->VDIIOCallbacks, pDisk, &pVDIfsImage);
4104
pImage->VDIo.pInterfaceIO = &pImage->VDIo.VDIIO;
4105
pImage->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
4108
/* Set up the internal I/O interface. */
4109
AssertBreakStmt(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
4110
rc = VERR_INVALID_PARAMETER);
4111
rc = VDInterfaceAdd(&pImage->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
4112
&pDisk->VDIIOIntCallbacks, &pImage->VDIo, &pImage->pVDIfsImage);
4115
pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
4116
rc = pImage->Backend->pfnOpen(pImage->pszFilename,
4117
uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
4119
pImage->pVDIfsImage,
4121
&pImage->pBackendData);
4122
/* If the open in read-write mode failed, retry in read-only mode. */
4125
if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
4126
&& ( rc == VERR_ACCESS_DENIED
4127
|| rc == VERR_PERMISSION_DENIED
4128
|| rc == VERR_WRITE_PROTECT
4129
|| rc == VERR_SHARING_VIOLATION
4130
|| rc == VERR_FILE_LOCK_FAILED))
4131
rc = pImage->Backend->pfnOpen(pImage->pszFilename,
4132
(uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
4133
| VD_OPEN_FLAGS_READONLY,
4135
pImage->pVDIfsImage,
4137
&pImage->pBackendData);
4140
rc = vdError(pDisk, rc, RT_SRC_POS,
4141
N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
4146
/* Lock disk for writing, as we modify pDisk information below. */
4147
rc2 = vdThreadStartWrite(pDisk);
4151
pImage->VDIo.pBackendData = pImage->pBackendData;
4153
/* Check image type. As the image itself has only partial knowledge
4154
* whether it's a base image or not, this info is derived here. The
4155
* base image can be fixed or normal, all others must be normal or
4156
* diff images. Some image formats don't distinguish between normal
4157
* and diff images, so this must be corrected here. */
4158
unsigned uImageFlags;
4159
uImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pBackendData);
4161
uImageFlags = VD_IMAGE_FLAGS_NONE;
4163
&& !(uOpenFlags & VD_OPEN_FLAGS_INFO))
4165
if ( pDisk->cImages == 0
4166
&& (uImageFlags & VD_IMAGE_FLAGS_DIFF))
4168
rc = VERR_VD_INVALID_TYPE;
4171
else if (pDisk->cImages != 0)
4173
if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
4175
rc = VERR_VD_INVALID_TYPE;
4179
uImageFlags |= VD_IMAGE_FLAGS_DIFF;
4183
/* Ensure we always get correct diff information, even if the backend
4184
* doesn't actually have a stored flag for this. It must not return
4185
* bogus information for the parent UUID if it is not a diff image. */
4187
RTUuidClear(&parentUuid);
4188
rc2 = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, &parentUuid);
4189
if (RT_SUCCESS(rc2) && !RTUuidIsNull(&parentUuid))
4190
uImageFlags |= VD_IMAGE_FLAGS_DIFF;
4192
pImage->uImageFlags = uImageFlags;
4194
/* Force sane optimization settings. It's not worth avoiding writes
4195
* to fixed size images. The overhead would have almost no payback. */
4196
if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
4197
pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
4199
/** @todo optionally check UUIDs */
4201
/* Cache disk information. */
4202
pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
4204
/* Cache PCHS geometry. */
4205
rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
4206
&pDisk->PCHSGeometry);
4207
if (RT_FAILURE(rc2))
4209
pDisk->PCHSGeometry.cCylinders = 0;
4210
pDisk->PCHSGeometry.cHeads = 0;
4211
pDisk->PCHSGeometry.cSectors = 0;
4215
/* Make sure the PCHS geometry is properly clipped. */
4216
pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
4217
pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
4218
pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
4221
/* Cache LCHS geometry. */
4222
rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
4223
&pDisk->LCHSGeometry);
4224
if (RT_FAILURE(rc2))
4226
pDisk->LCHSGeometry.cCylinders = 0;
4227
pDisk->LCHSGeometry.cHeads = 0;
4228
pDisk->LCHSGeometry.cSectors = 0;
4232
/* Make sure the LCHS geometry is properly clipped. */
4233
pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
4234
pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
4237
if (pDisk->cImages != 0)
4239
/* Switch previous image to read-only mode. */
4240
unsigned uOpenFlagsPrevImg;
4241
uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
4242
if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
4244
uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
4245
rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
4251
/* Image successfully opened, make it the last image. */
4252
vdAddImageToList(pDisk, pImage);
4253
if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
4254
pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
4258
/* Error detected, but image opened. Close image. */
4259
rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
4261
pImage->pBackendData = NULL;
4265
if (RT_UNLIKELY(fLockWrite))
4267
rc2 = vdThreadFinishWrite(pDisk);
4275
if (pImage->pszFilename)
4276
RTStrFree(pImage->pszFilename);
4281
LogFlowFunc(("returns %Rrc\n", rc));
4286
* Opens a cache image.
4288
* @return VBox status code.
4289
* @param pDisk Pointer to the HDD container which should use the cache image.
4290
* @param pszBackend Name of the cache file backend to use (case insensitive).
4291
* @param pszFilename Name of the cache image to open.
4292
* @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
4293
* @param pVDIfsCache Pointer to the per-cache VD interface list.
4295
VBOXDDU_DECL(int) VDCacheOpen(PVBOXHDD pDisk, const char *pszBackend,
4296
const char *pszFilename, unsigned uOpenFlags,
4297
PVDINTERFACE pVDIfsCache)
4299
int rc = VINF_SUCCESS;
4301
bool fLockWrite = false;
4302
PVDCACHE pCache = NULL;
4304
LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsCache=%#p\n",
4305
pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsCache));
4310
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4311
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4313
/* Check arguments. */
4314
AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
4315
("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
4316
rc = VERR_INVALID_PARAMETER);
4317
AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
4318
("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4319
rc = VERR_INVALID_PARAMETER);
4320
AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
4321
("uOpenFlags=%#x\n", uOpenFlags),
4322
rc = VERR_INVALID_PARAMETER);
4324
/* Set up image descriptor. */
4325
pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
4328
rc = VERR_NO_MEMORY;
4331
pCache->pszFilename = RTStrDup(pszFilename);
4332
if (!pCache->pszFilename)
4334
rc = VERR_NO_MEMORY;
4338
pCache->VDIo.pDisk = pDisk;
4339
pCache->pVDIfsCache = pVDIfsCache;
4341
rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
4344
if (!pCache->Backend)
4346
rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4347
N_("VD: unknown backend name '%s'"), pszBackend);
4351
/* Set up the I/O interface. */
4352
pCache->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IO);
4353
if (pCache->VDIo.pInterfaceIO)
4354
pCache->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pCache->VDIo.pInterfaceIO);
4357
rc = VDInterfaceAdd(&pCache->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
4358
&pDisk->VDIIOCallbacks, pDisk, &pVDIfsCache);
4359
pCache->VDIo.pInterfaceIO = &pCache->VDIo.VDIIO;
4360
pCache->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
4363
/* Set up the internal I/O interface. */
4364
AssertBreakStmt(!VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IOINT),
4365
rc = VERR_INVALID_PARAMETER);
4366
rc = VDInterfaceAdd(&pCache->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
4367
&pDisk->VDIIOIntCallbacks, &pCache->VDIo, &pCache->pVDIfsCache);
4370
pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
4371
rc = pCache->Backend->pfnOpen(pCache->pszFilename,
4372
uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
4374
pCache->pVDIfsCache,
4375
&pCache->pBackendData);
4376
/* If the open in read-write mode failed, retry in read-only mode. */
4379
if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
4380
&& ( rc == VERR_ACCESS_DENIED
4381
|| rc == VERR_PERMISSION_DENIED
4382
|| rc == VERR_WRITE_PROTECT
4383
|| rc == VERR_SHARING_VIOLATION
4384
|| rc == VERR_FILE_LOCK_FAILED))
4385
rc = pCache->Backend->pfnOpen(pCache->pszFilename,
4386
(uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
4387
| VD_OPEN_FLAGS_READONLY,
4389
pCache->pVDIfsCache,
4390
&pCache->pBackendData);
4393
rc = vdError(pDisk, rc, RT_SRC_POS,
4394
N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
4399
/* Lock disk for writing, as we modify pDisk information below. */
4400
rc2 = vdThreadStartWrite(pDisk);
4405
* Check that the modification UUID of the cache and last image
4406
* match. If not the image was modified in-between without the cache.
4407
* The cache might contain stale data.
4409
RTUUID UuidImage, UuidCache;
4411
rc = pCache->Backend->pfnGetModificationUuid(pCache->pBackendData,
4415
rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
4419
if (RTUuidCompare(&UuidImage, &UuidCache))
4420
rc = VERR_VD_CACHE_NOT_UP_TO_DATE;
4425
* We assume that the user knows what he is doing if one of the images
4426
* doesn't support the modification uuid.
4428
if (rc == VERR_NOT_SUPPORTED)
4433
/* Cache successfully opened, make it the current one. */
4435
pDisk->pCache = pCache;
4437
rc = VERR_VD_CACHE_ALREADY_EXISTS;
4442
/* Error detected, but image opened. Close image. */
4443
rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
4445
pCache->pBackendData = NULL;
4449
if (RT_UNLIKELY(fLockWrite))
4451
rc2 = vdThreadFinishWrite(pDisk);
4459
if (pCache->pszFilename)
4460
RTStrFree(pCache->pszFilename);
4465
LogFlowFunc(("returns %Rrc\n", rc));
4470
* Creates and opens a new base image file.
4472
* @returns VBox status code.
4473
* @param pDisk Pointer to HDD container.
4474
* @param pszBackend Name of the image file backend to use.
4475
* @param pszFilename Name of the image file to create.
4476
* @param cbSize Image size in bytes.
4477
* @param uImageFlags Flags specifying special image features.
4478
* @param pszComment Pointer to image comment. NULL is ok.
4479
* @param pPCHSGeometry Pointer to physical disk geometry <= (16383,16,63). Not NULL.
4480
* @param pLCHSGeometry Pointer to logical disk geometry <= (x,255,63). Not NULL.
4481
* @param pUuid New UUID of the image. If NULL, a new UUID is created.
4482
* @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
4483
* @param pVDIfsImage Pointer to the per-image VD interface list.
4484
* @param pVDIfsOperation Pointer to the per-operation VD interface list.
4486
VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszBackend,
4487
const char *pszFilename, uint64_t cbSize,
4488
unsigned uImageFlags, const char *pszComment,
4489
PCVDGEOMETRY pPCHSGeometry,
4490
PCVDGEOMETRY pLCHSGeometry,
4491
PCRTUUID pUuid, unsigned uOpenFlags,
4492
PVDINTERFACE pVDIfsImage,
4493
PVDINTERFACE pVDIfsOperation)
4495
int rc = VINF_SUCCESS;
4497
bool fLockWrite = false, fLockRead = false;
4498
PVDIMAGE pImage = NULL;
4501
LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" PCHS=%u/%u/%u LCHS=%u/%u/%u Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
4502
pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment,
4503
pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
4504
pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
4505
pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, pUuid,
4506
uOpenFlags, pVDIfsImage, pVDIfsOperation));
4508
PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
4509
VDINTERFACETYPE_PROGRESS);
4510
PVDINTERFACEPROGRESS pCbProgress = NULL;
4512
pCbProgress = VDGetInterfaceProgress(pIfProgress);
4517
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4518
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4520
/* Check arguments. */
4521
AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
4522
("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
4523
rc = VERR_INVALID_PARAMETER);
4524
AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
4525
("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4526
rc = VERR_INVALID_PARAMETER);
4527
AssertMsgBreakStmt(cbSize,
4528
("cbSize=%llu\n", cbSize),
4529
rc = VERR_INVALID_PARAMETER);
4530
AssertMsgBreakStmt( ((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0)
4531
|| ((uImageFlags & (VD_IMAGE_FLAGS_FIXED | VD_IMAGE_FLAGS_DIFF)) != VD_IMAGE_FLAGS_FIXED),
4532
("uImageFlags=%#x\n", uImageFlags),
4533
rc = VERR_INVALID_PARAMETER);
4534
/* The PCHS geometry fields may be 0 to leave it for later. */
4535
AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
4536
&& pPCHSGeometry->cHeads <= 16
4537
&& pPCHSGeometry->cSectors <= 63,
4538
("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
4539
pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
4540
pPCHSGeometry->cSectors),
4541
rc = VERR_INVALID_PARAMETER);
4542
/* The LCHS geometry fields may be 0 to leave it to later autodetection. */
4543
AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
4544
&& pLCHSGeometry->cHeads <= 255
4545
&& pLCHSGeometry->cSectors <= 63,
4546
("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
4547
pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
4548
pLCHSGeometry->cSectors),
4549
rc = VERR_INVALID_PARAMETER);
4550
/* The UUID may be NULL. */
4551
AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
4552
("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
4553
rc = VERR_INVALID_PARAMETER);
4554
AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
4555
("uOpenFlags=%#x\n", uOpenFlags),
4556
rc = VERR_INVALID_PARAMETER);
4558
/* Check state. Needs a temporary read lock. Holding the write lock
4559
* all the time would be blocking other activities for too long. */
4560
rc2 = vdThreadStartRead(pDisk);
4563
AssertMsgBreakStmt(pDisk->cImages == 0,
4564
("Create base image cannot be done with other images open\n"),
4565
rc = VERR_VD_INVALID_STATE);
4566
rc2 = vdThreadFinishRead(pDisk);
4570
/* Set up image descriptor. */
4571
pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
4574
rc = VERR_NO_MEMORY;
4577
pImage->pszFilename = RTStrDup(pszFilename);
4578
if (!pImage->pszFilename)
4580
rc = VERR_NO_MEMORY;
4583
pImage->VDIo.pDisk = pDisk;
4584
pImage->pVDIfsImage = pVDIfsImage;
4586
/* Set up the I/O interface. */
4587
pImage->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
4588
if (pImage->VDIo.pInterfaceIO)
4589
pImage->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->VDIo.pInterfaceIO);
4592
rc = VDInterfaceAdd(&pImage->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
4593
&pDisk->VDIIOCallbacks, pDisk, &pVDIfsImage);
4594
pImage->VDIo.pInterfaceIO = &pImage->VDIo.VDIIO;
4595
pImage->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
4598
/* Set up the internal I/O interface. */
4599
AssertBreakStmt(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
4600
rc = VERR_INVALID_PARAMETER);
4601
rc = VDInterfaceAdd(&pImage->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
4602
&pDisk->VDIIOIntCallbacks, &pImage->VDIo, &pImage->pVDIfsImage);
4605
rc = vdFindBackend(pszBackend, &pImage->Backend);
4608
if (!pImage->Backend)
4610
rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4611
N_("VD: unknown backend name '%s'"), pszBackend);
4614
if (!(pImage->Backend->uBackendCaps & ( VD_CAP_CREATE_FIXED
4615
| VD_CAP_CREATE_DYNAMIC)))
4617
rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4618
N_("VD: backend '%s' cannot create base images"), pszBackend);
4622
/* Create UUID if the caller didn't specify one. */
4625
rc = RTUuidCreate(&uuid);
4628
rc = vdError(pDisk, rc, RT_SRC_POS,
4629
N_("VD: cannot generate UUID for image '%s'"),
4636
pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
4637
uImageFlags &= ~VD_IMAGE_FLAGS_DIFF;
4638
rc = pImage->Backend->pfnCreate(pImage->pszFilename, cbSize,
4639
uImageFlags, pszComment, pPCHSGeometry,
4640
pLCHSGeometry, pUuid,
4641
uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
4644
pImage->pVDIfsImage,
4646
&pImage->pBackendData);
4650
pImage->VDIo.pBackendData = pImage->pBackendData;
4651
pImage->uImageFlags = uImageFlags;
4653
/* Force sane optimization settings. It's not worth avoiding writes
4654
* to fixed size images. The overhead would have almost no payback. */
4655
if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
4656
pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
4658
/* Lock disk for writing, as we modify pDisk information below. */
4659
rc2 = vdThreadStartWrite(pDisk);
4663
/** @todo optionally check UUIDs */
4665
/* Re-check state, as the lock wasn't held and another image
4666
* creation call could have been done by another thread. */
4667
AssertMsgStmt(pDisk->cImages == 0,
4668
("Create base image cannot be done with other images open\n"),
4669
rc = VERR_VD_INVALID_STATE);
4674
/* Cache disk information. */
4675
pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
4677
/* Cache PCHS geometry. */
4678
rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
4679
&pDisk->PCHSGeometry);
4680
if (RT_FAILURE(rc2))
4682
pDisk->PCHSGeometry.cCylinders = 0;
4683
pDisk->PCHSGeometry.cHeads = 0;
4684
pDisk->PCHSGeometry.cSectors = 0;
4688
/* Make sure the CHS geometry is properly clipped. */
4689
pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
4690
pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
4691
pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
4694
/* Cache LCHS geometry. */
4695
rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
4696
&pDisk->LCHSGeometry);
4697
if (RT_FAILURE(rc2))
4699
pDisk->LCHSGeometry.cCylinders = 0;
4700
pDisk->LCHSGeometry.cHeads = 0;
4701
pDisk->LCHSGeometry.cSectors = 0;
4705
/* Make sure the CHS geometry is properly clipped. */
4706
pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
4707
pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
4710
/* Image successfully opened, make it the last image. */
4711
vdAddImageToList(pDisk, pImage);
4712
if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
4713
pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
4717
/* Error detected, image may or may not be opened. Close and delete
4718
* image if it was opened. */
4719
if (pImage->pBackendData)
4721
rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
4723
pImage->pBackendData = NULL;
4728
if (RT_UNLIKELY(fLockWrite))
4730
rc2 = vdThreadFinishWrite(pDisk);
4733
else if (RT_UNLIKELY(fLockRead))
4735
rc2 = vdThreadFinishRead(pDisk);
4743
if (pImage->pszFilename)
4744
RTStrFree(pImage->pszFilename);
4749
if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
4750
pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
4752
LogFlowFunc(("returns %Rrc\n", rc));
4757
* Creates and opens a new differencing image file in HDD container.
4758
* See comments for VDOpen function about differencing images.
4760
* @returns VBox status code.
4761
* @param pDisk Pointer to HDD container.
4762
* @param pszBackend Name of the image file backend to use.
4763
* @param pszFilename Name of the differencing image file to create.
4764
* @param uImageFlags Flags specifying special image features.
4765
* @param pszComment Pointer to image comment. NULL is ok.
4766
* @param pUuid New UUID of the image. If NULL, a new UUID is created.
4767
* @param pParentUuid New parent UUID of the image. If NULL, the UUID is queried automatically.
4768
* @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
4769
* @param pVDIfsImage Pointer to the per-image VD interface list.
4770
* @param pVDIfsOperation Pointer to the per-operation VD interface list.
4772
VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszBackend,
4773
const char *pszFilename, unsigned uImageFlags,
4774
const char *pszComment, PCRTUUID pUuid,
4775
PCRTUUID pParentUuid, unsigned uOpenFlags,
4776
PVDINTERFACE pVDIfsImage,
4777
PVDINTERFACE pVDIfsOperation)
4779
int rc = VINF_SUCCESS;
4781
bool fLockWrite = false, fLockRead = false;
4782
PVDIMAGE pImage = NULL;
4785
LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
4786
pDisk, pszBackend, pszFilename, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsImage, pVDIfsOperation));
4788
PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
4789
VDINTERFACETYPE_PROGRESS);
4790
PVDINTERFACEPROGRESS pCbProgress = NULL;
4792
pCbProgress = VDGetInterfaceProgress(pIfProgress);
4797
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4798
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4800
/* Check arguments. */
4801
AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
4802
("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
4803
rc = VERR_INVALID_PARAMETER);
4804
AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
4805
("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4806
rc = VERR_INVALID_PARAMETER);
4807
AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
4808
("uImageFlags=%#x\n", uImageFlags),
4809
rc = VERR_INVALID_PARAMETER);
4810
/* The UUID may be NULL. */
4811
AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
4812
("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
4813
rc = VERR_INVALID_PARAMETER);
4814
/* The parent UUID may be NULL. */
4815
AssertMsgBreakStmt(pParentUuid == NULL || VALID_PTR(pParentUuid),
4816
("pParentUuid=%#p ParentUUID=%RTuuid\n", pParentUuid, pParentUuid),
4817
rc = VERR_INVALID_PARAMETER);
4818
AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
4819
("uOpenFlags=%#x\n", uOpenFlags),
4820
rc = VERR_INVALID_PARAMETER);
4822
/* Check state. Needs a temporary read lock. Holding the write lock
4823
* all the time would be blocking other activities for too long. */
4824
rc2 = vdThreadStartRead(pDisk);
4827
AssertMsgBreakStmt(pDisk->cImages != 0,
4828
("Create diff image cannot be done without other images open\n"),
4829
rc = VERR_VD_INVALID_STATE);
4830
rc2 = vdThreadFinishRead(pDisk);
4834
/* Set up image descriptor. */
4835
pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
4838
rc = VERR_NO_MEMORY;
4841
pImage->pszFilename = RTStrDup(pszFilename);
4842
if (!pImage->pszFilename)
4844
rc = VERR_NO_MEMORY;
4848
rc = vdFindBackend(pszBackend, &pImage->Backend);
4851
if (!pImage->Backend)
4853
rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4854
N_("VD: unknown backend name '%s'"), pszBackend);
4857
if ( !(pImage->Backend->uBackendCaps & VD_CAP_DIFF)
4858
|| !(pImage->Backend->uBackendCaps & ( VD_CAP_CREATE_FIXED
4859
| VD_CAP_CREATE_DYNAMIC)))
4861
rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4862
N_("VD: backend '%s' cannot create diff images"), pszBackend);
4866
pImage->VDIo.pDisk = pDisk;
4867
pImage->pVDIfsImage = pVDIfsImage;
4869
/* Set up the I/O interface. */
4870
pImage->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
4871
if (pImage->VDIo.pInterfaceIO)
4872
pImage->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->VDIo.pInterfaceIO);
4875
rc = VDInterfaceAdd(&pImage->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
4876
&pDisk->VDIIOCallbacks, pDisk, &pVDIfsImage);
4877
pImage->VDIo.pInterfaceIO = &pImage->VDIo.VDIIO;
4878
pImage->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
4881
/* Set up the internal I/O interface. */
4882
AssertBreakStmt(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
4883
rc = VERR_INVALID_PARAMETER);
4884
rc = VDInterfaceAdd(&pImage->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
4885
&pDisk->VDIIOIntCallbacks, &pImage->VDIo, &pImage->pVDIfsImage);
4888
/* Create UUID if the caller didn't specify one. */
4891
rc = RTUuidCreate(&uuid);
4894
rc = vdError(pDisk, rc, RT_SRC_POS,
4895
N_("VD: cannot generate UUID for image '%s'"),
4902
pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
4903
uImageFlags |= VD_IMAGE_FLAGS_DIFF;
4904
rc = pImage->Backend->pfnCreate(pImage->pszFilename, pDisk->cbSize,
4905
uImageFlags | VD_IMAGE_FLAGS_DIFF,
4906
pszComment, &pDisk->PCHSGeometry,
4907
&pDisk->LCHSGeometry, pUuid,
4908
uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
4911
pImage->pVDIfsImage,
4913
&pImage->pBackendData);
4917
pImage->VDIo.pBackendData = pImage->pBackendData;
4918
pImage->uImageFlags = uImageFlags;
4920
/* Lock disk for writing, as we modify pDisk information below. */
4921
rc2 = vdThreadStartWrite(pDisk);
4925
/* Switch previous image to read-only mode. */
4926
unsigned uOpenFlagsPrevImg;
4927
uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
4928
if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
4930
uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
4931
rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
4934
/** @todo optionally check UUIDs */
4936
/* Re-check state, as the lock wasn't held and another image
4937
* creation call could have been done by another thread. */
4938
AssertMsgStmt(pDisk->cImages != 0,
4939
("Create diff image cannot be done without other images open\n"),
4940
rc = VERR_VD_INVALID_STATE);
4948
if (pParentUuid && !RTUuidIsNull(pParentUuid))
4950
Uuid = *pParentUuid;
4951
pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
4955
rc2 = pDisk->pLast->Backend->pfnGetUuid(pDisk->pLast->pBackendData,
4957
if (RT_SUCCESS(rc2))
4958
pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
4960
rc2 = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
4962
if (RT_SUCCESS(rc2))
4963
pImage->Backend->pfnSetParentModificationUuid(pImage->pBackendData,
4965
if (pDisk->pLast->Backend->pfnGetTimeStamp)
4966
rc2 = pDisk->pLast->Backend->pfnGetTimeStamp(pDisk->pLast->pBackendData,
4969
rc2 = VERR_NOT_IMPLEMENTED;
4970
if (RT_SUCCESS(rc2) && pImage->Backend->pfnSetParentTimeStamp)
4971
pImage->Backend->pfnSetParentTimeStamp(pImage->pBackendData, &ts);
4973
if (pImage->Backend->pfnSetParentFilename)
4974
rc2 = pImage->Backend->pfnSetParentFilename(pImage->pBackendData, pDisk->pLast->pszFilename);
4979
/* Image successfully opened, make it the last image. */
4980
vdAddImageToList(pDisk, pImage);
4981
if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
4982
pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
4986
/* Error detected, but image opened. Close and delete image. */
4987
rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
4989
pImage->pBackendData = NULL;
4993
if (RT_UNLIKELY(fLockWrite))
4995
rc2 = vdThreadFinishWrite(pDisk);
4998
else if (RT_UNLIKELY(fLockRead))
5000
rc2 = vdThreadFinishRead(pDisk);
5008
if (pImage->pszFilename)
5009
RTStrFree(pImage->pszFilename);
5014
if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
5015
pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
5017
LogFlowFunc(("returns %Rrc\n", rc));
5023
* Creates and opens new cache image file in HDD container.
5025
* @return VBox status code.
5026
* @param pDisk Name of the cache file backend to use (case insensitive).
5027
* @param pszFilename Name of the differencing cache file to create.
5028
* @param cbSize Maximum size of the cache.
5029
* @param uImageFlags Flags specifying special cache features.
5030
* @param pszComment Pointer to image comment. NULL is ok.
5031
* @param pUuid New UUID of the image. If NULL, a new UUID is created.
5032
* @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
5033
* @param pVDIfsCache Pointer to the per-cache VD interface list.
5034
* @param pVDIfsOperation Pointer to the per-operation VD interface list.
5036
VBOXDDU_DECL(int) VDCreateCache(PVBOXHDD pDisk, const char *pszBackend,
5037
const char *pszFilename, uint64_t cbSize,
5038
unsigned uImageFlags, const char *pszComment,
5039
PCRTUUID pUuid, unsigned uOpenFlags,
5040
PVDINTERFACE pVDIfsCache, PVDINTERFACE pVDIfsOperation)
5042
int rc = VINF_SUCCESS;
5044
bool fLockWrite = false, fLockRead = false;
5045
PVDCACHE pCache = NULL;
5048
LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
5049
pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsCache, pVDIfsOperation));
5051
PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
5052
VDINTERFACETYPE_PROGRESS);
5053
PVDINTERFACEPROGRESS pCbProgress = NULL;
5055
pCbProgress = VDGetInterfaceProgress(pIfProgress);
5060
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5061
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5063
/* Check arguments. */
5064
AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
5065
("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
5066
rc = VERR_INVALID_PARAMETER);
5067
AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
5068
("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
5069
rc = VERR_INVALID_PARAMETER);
5070
AssertMsgBreakStmt(cbSize,
5071
("cbSize=%llu\n", cbSize),
5072
rc = VERR_INVALID_PARAMETER);
5073
AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
5074
("uImageFlags=%#x\n", uImageFlags),
5075
rc = VERR_INVALID_PARAMETER);
5076
/* The UUID may be NULL. */
5077
AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
5078
("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
5079
rc = VERR_INVALID_PARAMETER);
5080
AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
5081
("uOpenFlags=%#x\n", uOpenFlags),
5082
rc = VERR_INVALID_PARAMETER);
5084
/* Check state. Needs a temporary read lock. Holding the write lock
5085
* all the time would be blocking other activities for too long. */
5086
rc2 = vdThreadStartRead(pDisk);
5089
AssertMsgBreakStmt(!pDisk->pCache,
5090
("Create cache image cannot be done with a cache already attached\n"),
5091
rc = VERR_VD_CACHE_ALREADY_EXISTS);
5092
rc2 = vdThreadFinishRead(pDisk);
5096
/* Set up image descriptor. */
5097
pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
5100
rc = VERR_NO_MEMORY;
5103
pCache->pszFilename = RTStrDup(pszFilename);
5104
if (!pCache->pszFilename)
5106
rc = VERR_NO_MEMORY;
5110
rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
5113
if (!pCache->Backend)
5115
rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
5116
N_("VD: unknown backend name '%s'"), pszBackend);
5120
pCache->VDIo.pDisk = pDisk;
5121
pCache->pVDIfsCache = pVDIfsCache;
5123
/* Set up the I/O interface. */
5124
pCache->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IO);
5125
if (pCache->VDIo.pInterfaceIO)
5126
pCache->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pCache->VDIo.pInterfaceIO);
5129
rc = VDInterfaceAdd(&pCache->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
5130
&pDisk->VDIIOCallbacks, pDisk, &pVDIfsCache);
5131
pCache->VDIo.pInterfaceIO = &pCache->VDIo.VDIIO;
5132
pCache->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
5135
/* Set up the internal I/O interface. */
5136
AssertBreakStmt(!VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IOINT),
5137
rc = VERR_INVALID_PARAMETER);
5138
rc = VDInterfaceAdd(&pCache->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
5139
&pDisk->VDIIOIntCallbacks, &pCache->VDIo, &pCache->pVDIfsCache);
5142
/* Create UUID if the caller didn't specify one. */
5145
rc = RTUuidCreate(&uuid);
5148
rc = vdError(pDisk, rc, RT_SRC_POS,
5149
N_("VD: cannot generate UUID for image '%s'"),
5156
pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
5157
rc = pCache->Backend->pfnCreate(pCache->pszFilename, cbSize,
5160
uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
5163
pCache->pVDIfsCache,
5165
&pCache->pBackendData);
5169
/* Lock disk for writing, as we modify pDisk information below. */
5170
rc2 = vdThreadStartWrite(pDisk);
5174
pCache->VDIo.pBackendData = pCache->pBackendData;
5176
/* Re-check state, as the lock wasn't held and another image
5177
* creation call could have been done by another thread. */
5178
AssertMsgStmt(!pDisk->pCache,
5179
("Create cache image cannot be done with another cache open\n"),
5180
rc = VERR_VD_CACHE_ALREADY_EXISTS);
5186
RTUUID UuidModification;
5188
/* Set same modification Uuid as the last image. */
5189
rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
5193
rc = pCache->Backend->pfnSetModificationUuid(pCache->pBackendData,
5197
if (rc == VERR_NOT_SUPPORTED)
5203
/* Cache successfully created. */
5204
pDisk->pCache = pCache;
5208
/* Error detected, but image opened. Close and delete image. */
5209
rc2 = pCache->Backend->pfnClose(pCache->pBackendData, true);
5211
pCache->pBackendData = NULL;
5215
if (RT_UNLIKELY(fLockWrite))
5217
rc2 = vdThreadFinishWrite(pDisk);
5220
else if (RT_UNLIKELY(fLockRead))
5222
rc2 = vdThreadFinishRead(pDisk);
5230
if (pCache->pszFilename)
5231
RTStrFree(pCache->pszFilename);
5236
if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
5237
pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
5239
LogFlowFunc(("returns %Rrc\n", rc));
5244
* Merges two images (not necessarily with direct parent/child relationship).
5245
* As a side effect the source image and potentially the other images which
5246
* are also merged to the destination are deleted from both the disk and the
5247
* images in the HDD container.
5249
* @returns VBox status code.
5250
* @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5251
* @param pDisk Pointer to HDD container.
5252
* @param nImageFrom Name of the image file to merge from.
5253
* @param nImageTo Name of the image file to merge to.
5254
* @param pVDIfsOperation Pointer to the per-operation VD interface list.
5256
VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
5257
unsigned nImageTo, PVDINTERFACE pVDIfsOperation)
5259
int rc = VINF_SUCCESS;
5261
bool fLockWrite = false, fLockRead = false;
5264
LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pVDIfsOperation=%#p\n",
5265
pDisk, nImageFrom, nImageTo, pVDIfsOperation));
5267
PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
5268
VDINTERFACETYPE_PROGRESS);
5269
PVDINTERFACEPROGRESS pCbProgress = NULL;
5271
pCbProgress = VDGetInterfaceProgress(pIfProgress);
5276
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5277
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5279
/* For simplicity reasons lock for writing as the image reopen below
5280
* might need it. After all the reopen is usually needed. */
5281
rc2 = vdThreadStartWrite(pDisk);
5284
PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
5285
PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
5286
if (!pImageFrom || !pImageTo)
5288
rc = VERR_VD_IMAGE_NOT_FOUND;
5291
AssertBreakStmt(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
5293
/* Make sure destination image is writable. */
5294
unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
5295
if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
5297
uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
5298
rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
5304
/* Get size of destination image. */
5305
uint64_t cbSize = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
5306
rc2 = vdThreadFinishWrite(pDisk);
5310
/* Allocate tmp buffer. */
5311
pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
5314
rc = VERR_NO_MEMORY;
5318
/* Merging is done directly on the images itself. This potentially
5319
* causes trouble if the disk is full in the middle of operation. */
5320
if (nImageFrom < nImageTo)
5322
/* Merge parent state into child. This means writing all not
5323
* allocated blocks in the destination image which are allocated in
5324
* the images to be merged. */
5325
uint64_t uOffset = 0;
5326
uint64_t cbRemaining = cbSize;
5329
size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
5331
/* Need to hold the write lock during a read-write operation. */
5332
rc2 = vdThreadStartWrite(pDisk);
5336
rc = pImageTo->Backend->pfnRead(pImageTo->pBackendData,
5337
uOffset, pvBuf, cbThisRead,
5339
if (rc == VERR_VD_BLOCK_FREE)
5341
/* Search for image with allocated block. Do not attempt to
5342
* read more than the previous reads marked as valid.
5343
* Otherwise this would return stale data when different
5344
* block sizes are used for the images. */
5345
for (PVDIMAGE pCurrImage = pImageTo->pPrev;
5346
pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VERR_VD_BLOCK_FREE;
5347
pCurrImage = pCurrImage->pPrev)
5349
rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
5355
if (rc != VERR_VD_BLOCK_FREE)
5359
/* Updating the cache is required because this might be a live merge. */
5360
rc = vdWriteHelper(pDisk, pImageTo, pImageFrom->pPrev,
5361
uOffset, pvBuf, cbThisRead,
5362
true /* fUpdateCache */);
5369
else if (RT_FAILURE(rc))
5372
rc2 = vdThreadFinishWrite(pDisk);
5376
uOffset += cbThisRead;
5377
cbRemaining -= cbThisRead;
5379
if (pCbProgress && pCbProgress->pfnProgress)
5381
/** @todo r=klaus: this can update the progress to the same
5382
* percentage over and over again if the image format makes
5383
* relatively small increments. */
5384
rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
5385
uOffset * 99 / cbSize);
5389
} while (uOffset < cbSize);
5394
* We may need to update the parent uuid of the child coming after
5395
* the last image to be merged. We have to reopen it read/write.
5397
* This is done before we do the actual merge to prevent an
5398
* inconsistent chain if the mode change fails for some reason.
5400
if (pImageFrom->pNext)
5402
PVDIMAGE pImageChild = pImageFrom->pNext;
5404
/* Take the write lock. */
5405
rc2 = vdThreadStartWrite(pDisk);
5409
/* We need to open the image in read/write mode. */
5410
uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
5412
if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
5414
uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
5415
rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
5421
rc2 = vdThreadFinishWrite(pDisk);
5426
/* If the merge is from the last image we have to relay all writes
5427
* to the merge destination as well, so that concurrent writes
5428
* (in case of a live merge) are handled correctly. */
5429
if (!pImageFrom->pNext)
5431
/* Take the write lock. */
5432
rc2 = vdThreadStartWrite(pDisk);
5436
pDisk->pImageRelay = pImageTo;
5438
rc2 = vdThreadFinishWrite(pDisk);
5443
/* Merge child state into parent. This means writing all blocks
5444
* which are allocated in the image up to the source image to the
5445
* destination image. */
5446
uint64_t uOffset = 0;
5447
uint64_t cbRemaining = cbSize;
5450
size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
5451
rc = VERR_VD_BLOCK_FREE;
5453
/* Need to hold the write lock during a read-write operation. */
5454
rc2 = vdThreadStartWrite(pDisk);
5458
/* Search for image with allocated block. Do not attempt to
5459
* read more than the previous reads marked as valid. Otherwise
5460
* this would return stale data when different block sizes are
5461
* used for the images. */
5462
for (PVDIMAGE pCurrImage = pImageFrom;
5463
pCurrImage != NULL && pCurrImage != pImageTo && rc == VERR_VD_BLOCK_FREE;
5464
pCurrImage = pCurrImage->pPrev)
5466
rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
5468
cbThisRead, &cbThisRead);
5471
if (rc != VERR_VD_BLOCK_FREE)
5475
rc = vdWriteHelper(pDisk, pImageTo, NULL, uOffset, pvBuf,
5476
cbThisRead, true /* fUpdateCache */);
5483
rc2 = vdThreadFinishWrite(pDisk);
5487
uOffset += cbThisRead;
5488
cbRemaining -= cbThisRead;
5490
if (pCbProgress && pCbProgress->pfnProgress)
5492
/** @todo r=klaus: this can update the progress to the same
5493
* percentage over and over again if the image format makes
5494
* relatively small increments. */
5495
rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
5496
uOffset * 99 / cbSize);
5500
} while (uOffset < cbSize);
5502
/* In case we set up a "write proxy" image above we must clear
5503
* this again now to prevent stray writes. Failure or not. */
5504
if (!pImageFrom->pNext)
5506
/* Take the write lock. */
5507
rc2 = vdThreadStartWrite(pDisk);
5511
pDisk->pImageRelay = NULL;
5513
rc2 = vdThreadFinishWrite(pDisk);
5520
* Leave in case of an error to avoid corrupted data in the image chain
5521
* (includes cancelling the operation by the user).
5526
/* Need to hold the write lock while finishing the merge. */
5527
rc2 = vdThreadStartWrite(pDisk);
5531
/* Update parent UUID so that image chain is consistent. */
5533
PVDIMAGE pImageChild = NULL;
5534
if (nImageFrom < nImageTo)
5536
if (pImageFrom->pPrev)
5538
rc = pImageFrom->pPrev->Backend->pfnGetUuid(pImageFrom->pPrev->pBackendData,
5544
rc = pImageTo->Backend->pfnSetParentUuid(pImageTo->pBackendData,
5550
/* Update the parent uuid of the child of the last merged image. */
5551
if (pImageFrom->pNext)
5553
rc = pImageTo->Backend->pfnGetUuid(pImageTo->pBackendData,
5557
rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext->pBackendData,
5561
pImageChild = pImageFrom->pNext;
5565
/* Delete the no longer needed images. */
5566
PVDIMAGE pImg = pImageFrom, pTmp;
5567
while (pImg != pImageTo)
5569
if (nImageFrom < nImageTo)
5573
vdRemoveImageFromList(pDisk, pImg);
5574
pImg->Backend->pfnClose(pImg->pBackendData, true);
5575
RTMemFree(pImg->pszFilename);
5580
/* Make sure destination image is back to read only if necessary. */
5581
if (pImageTo != pDisk->pLast)
5583
uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
5584
uOpenFlags |= VD_OPEN_FLAGS_READONLY;
5585
rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
5592
* Make sure the child is readonly
5593
* for the child -> parent merge direction
5596
if ( nImageFrom > nImageTo
5598
&& pImageChild != pDisk->pLast)
5600
uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
5601
uOpenFlags |= VD_OPEN_FLAGS_READONLY;
5602
rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
5609
if (RT_UNLIKELY(fLockWrite))
5611
rc2 = vdThreadFinishWrite(pDisk);
5614
else if (RT_UNLIKELY(fLockRead))
5616
rc2 = vdThreadFinishRead(pDisk);
5621
RTMemTmpFree(pvBuf);
5623
if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
5624
pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
5626
LogFlowFunc(("returns %Rrc\n", rc));
5631
* Copies an image from one HDD container to another.
5632
* The copy is opened in the target HDD container.
5633
* It is possible to convert between different image formats, because the
5634
* backend for the destination may be different from the source.
5635
* If both the source and destination reference the same HDD container,
5636
* then the image is moved (by copying/deleting or renaming) to the new location.
5637
* The source container is unchanged if the move operation fails, otherwise
5638
* the image at the new location is opened in the same way as the old one was.
5640
* @returns VBox status code.
5641
* @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5642
* @param pDiskFrom Pointer to source HDD container.
5643
* @param nImage Image number, counts from 0. 0 is always base image of container.
5644
* @param pDiskTo Pointer to destination HDD container.
5645
* @param pszBackend Name of the image file backend to use.
5646
* @param pszFilename New name of the image (may be NULL if pDiskFrom == pDiskTo).
5647
* @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
5648
* @param cbSize New image size (0 means leave unchanged).
5649
* @param uImageFlags Flags specifying special destination image features.
5650
* @param pDstUuid New UUID of the destination image. If NULL, a new UUID is created.
5651
* This parameter is used if and only if a true copy is created.
5652
* In all rename/move cases the UUIDs are copied over.
5653
* @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
5654
* Only used if the destination image is created.
5655
* @param pVDIfsOperation Pointer to the per-operation VD interface list.
5656
* @param pDstVDIfsImage Pointer to the per-image VD interface list, for the
5657
* destination image.
5658
* @param pDstVDIfsOperation Pointer to the per-image VD interface list,
5659
* for the destination image.
5661
VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
5662
const char *pszBackend, const char *pszFilename,
5663
bool fMoveByRename, uint64_t cbSize,
5664
unsigned uImageFlags, PCRTUUID pDstUuid,
5665
unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
5666
PVDINTERFACE pDstVDIfsImage,
5667
PVDINTERFACE pDstVDIfsOperation)
5669
int rc = VINF_SUCCESS;
5671
bool fLockReadFrom = false, fLockWriteFrom = false, fLockWriteTo = false;
5673
PVDIMAGE pImageTo = NULL;
5675
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",
5676
pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, uImageFlags, pDstUuid, uOpenFlags, pVDIfsOperation, pDstVDIfsImage, pDstVDIfsOperation));
5678
PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
5679
VDINTERFACETYPE_PROGRESS);
5680
PVDINTERFACEPROGRESS pCbProgress = NULL;
5682
pCbProgress = VDGetInterfaceProgress(pIfProgress);
5684
PVDINTERFACE pDstIfProgress = VDInterfaceGet(pDstVDIfsOperation,
5685
VDINTERFACETYPE_PROGRESS);
5686
PVDINTERFACEPROGRESS pDstCbProgress = NULL;
5688
pDstCbProgress = VDGetInterfaceProgress(pDstIfProgress);
5691
/* Check arguments. */
5692
AssertMsgBreakStmt(VALID_PTR(pDiskFrom), ("pDiskFrom=%#p\n", pDiskFrom),
5693
rc = VERR_INVALID_PARAMETER);
5694
AssertMsg(pDiskFrom->u32Signature == VBOXHDDDISK_SIGNATURE,
5695
("u32Signature=%08x\n", pDiskFrom->u32Signature));
5697
rc2 = vdThreadStartRead(pDiskFrom);
5699
fLockReadFrom = true;
5700
PVDIMAGE pImageFrom = vdGetImageByNumber(pDiskFrom, nImage);
5701
AssertPtrBreakStmt(pImageFrom, rc = VERR_VD_IMAGE_NOT_FOUND);
5702
AssertMsgBreakStmt(VALID_PTR(pDiskTo), ("pDiskTo=%#p\n", pDiskTo),
5703
rc = VERR_INVALID_PARAMETER);
5704
AssertMsg(pDiskTo->u32Signature == VBOXHDDDISK_SIGNATURE,
5705
("u32Signature=%08x\n", pDiskTo->u32Signature));
5707
/* Move the image. */
5708
if (pDiskFrom == pDiskTo)
5710
/* Rename only works when backends are the same, are file based
5711
* and the rename method is implemented. */
5713
&& !RTStrICmp(pszBackend, pImageFrom->Backend->pszBackendName)
5714
&& pImageFrom->Backend->uBackendCaps & VD_CAP_FILE
5715
&& pImageFrom->Backend->pfnRename)
5717
rc2 = vdThreadFinishRead(pDiskFrom);
5719
fLockReadFrom = false;
5721
rc2 = vdThreadStartWrite(pDiskFrom);
5723
fLockWriteFrom = true;
5724
rc = pImageFrom->Backend->pfnRename(pImageFrom->pBackendData, pszFilename ? pszFilename : pImageFrom->pszFilename);
5728
/** @todo Moving (including shrinking/growing) of the image is
5729
* requested, but the rename attempt failed or it wasn't possible.
5730
* Must now copy image to temp location. */
5731
AssertReleaseMsgFailed(("VDCopy: moving by copy/delete not implemented\n"));
5734
/* pszFilename is allowed to be NULL, as this indicates copy to the existing image. */
5735
AssertMsgBreakStmt(pszFilename == NULL || (VALID_PTR(pszFilename) && *pszFilename),
5736
("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
5737
rc = VERR_INVALID_PARAMETER);
5739
uint64_t cbSizeFrom;
5740
cbSizeFrom = pImageFrom->Backend->pfnGetSize(pImageFrom->pBackendData);
5741
if (cbSizeFrom == 0)
5743
rc = VERR_VD_VALUE_NOT_FOUND;
5747
VDGEOMETRY PCHSGeometryFrom = {0, 0, 0};
5748
VDGEOMETRY LCHSGeometryFrom = {0, 0, 0};
5749
pImageFrom->Backend->pfnGetPCHSGeometry(pImageFrom->pBackendData, &PCHSGeometryFrom);
5750
pImageFrom->Backend->pfnGetLCHSGeometry(pImageFrom->pBackendData, &LCHSGeometryFrom);
5752
RTUUID ImageUuid, ImageModificationUuid;
5753
if (pDiskFrom != pDiskTo)
5756
ImageUuid = *pDstUuid;
5758
RTUuidCreate(&ImageUuid);
5762
rc = pImageFrom->Backend->pfnGetUuid(pImageFrom->pBackendData, &ImageUuid);
5764
RTUuidCreate(&ImageUuid);
5766
rc = pImageFrom->Backend->pfnGetModificationUuid(pImageFrom->pBackendData, &ImageModificationUuid);
5768
RTUuidClear(&ImageModificationUuid);
5770
char szComment[1024];
5771
rc = pImageFrom->Backend->pfnGetComment(pImageFrom->pBackendData, szComment, sizeof(szComment));
5773
szComment[0] = '\0';
5775
szComment[sizeof(szComment) - 1] = '\0';
5777
rc2 = vdThreadFinishRead(pDiskFrom);
5779
fLockReadFrom = false;
5781
rc2 = vdThreadStartRead(pDiskTo);
5783
unsigned cImagesTo = pDiskTo->cImages;
5784
rc2 = vdThreadFinishRead(pDiskTo);
5790
cbSize = cbSizeFrom;
5792
/* Create destination image with the properties of source image. */
5793
/** @todo replace the VDCreateDiff/VDCreateBase calls by direct
5794
* calls to the backend. Unifies the code and reduces the API
5795
* dependencies. Would also make the synchronization explicit. */
5798
rc = VDCreateDiff(pDiskTo, pszBackend, pszFilename,
5799
uImageFlags, szComment, &ImageUuid,
5800
NULL /* pParentUuid */,
5801
uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
5802
pDstVDIfsImage, NULL);
5804
rc2 = vdThreadStartWrite(pDiskTo);
5806
fLockWriteTo = true;
5808
/** @todo hack to force creation of a fixed image for
5809
* the RAW backend, which can't handle anything else. */
5810
if (!RTStrICmp(pszBackend, "RAW"))
5811
uImageFlags |= VD_IMAGE_FLAGS_FIXED;
5813
vdFixupPCHSGeometry(&PCHSGeometryFrom, cbSize);
5814
vdFixupLCHSGeometry(&LCHSGeometryFrom, cbSize);
5816
rc = VDCreateBase(pDiskTo, pszBackend, pszFilename, cbSize,
5817
uImageFlags, szComment,
5818
&PCHSGeometryFrom, &LCHSGeometryFrom,
5819
NULL, uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
5820
pDstVDIfsImage, NULL);
5822
rc2 = vdThreadStartWrite(pDiskTo);
5824
fLockWriteTo = true;
5826
if (RT_SUCCESS(rc) && !RTUuidIsNull(&ImageUuid))
5827
pDiskTo->pLast->Backend->pfnSetUuid(pDiskTo->pLast->pBackendData, &ImageUuid);
5832
pImageTo = pDiskTo->pLast;
5833
AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
5835
cbSize = RT_MIN(cbSize, cbSizeFrom);
5839
pImageTo = pDiskTo->pLast;
5840
AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
5843
cbSizeTo = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
5846
rc = VERR_VD_VALUE_NOT_FOUND;
5851
cbSize = RT_MIN(cbSizeFrom, cbSizeTo);
5853
vdFixupPCHSGeometry(&PCHSGeometryFrom, cbSize);
5854
vdFixupLCHSGeometry(&LCHSGeometryFrom, cbSize);
5856
/* Update the geometry in the destination image. */
5857
pImageTo->Backend->pfnSetPCHSGeometry(pImageTo->pBackendData, &PCHSGeometryFrom);
5858
pImageTo->Backend->pfnSetLCHSGeometry(pImageTo->pBackendData, &LCHSGeometryFrom);
5861
rc2 = vdThreadFinishWrite(pDiskTo);
5863
fLockWriteTo = false;
5865
/* Allocate tmp buffer. */
5866
pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
5869
rc = VERR_NO_MEMORY;
5873
/* Whether we can take the optimized copy path (false) or not.
5874
* Don't optimize if the image existed or if it is a child image. */
5875
bool fRegularRead = (pszFilename == NULL) || (cImagesTo > 0);
5877
/* Copy the data. */
5878
uint64_t uOffset = 0;
5879
uint64_t cbRemaining = cbSize;
5883
size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
5885
/* Note that we don't attempt to synchronize cross-disk accesses.
5886
* It wouldn't be very difficult to do, just the lock order would
5887
* need to be defined somehow to prevent deadlocks. Postpone such
5888
* magic as there is no use case for this. */
5890
rc2 = vdThreadStartRead(pDiskFrom);
5892
fLockReadFrom = true;
5895
* Updating the cache doesn't make any sense
5896
* as we are looping once through the image.
5898
rc = vdReadHelper(pDiskFrom, pImageFrom, NULL, uOffset, pvBuf,
5899
cbThisRead, fRegularRead,
5900
false /* fUpdateCache */);
5901
if (RT_FAILURE(rc) && rc != VERR_VD_BLOCK_FREE)
5904
rc2 = vdThreadFinishRead(pDiskFrom);
5906
fLockReadFrom = false;
5908
if (rc != VERR_VD_BLOCK_FREE)
5910
rc2 = vdThreadStartWrite(pDiskTo);
5912
fLockWriteTo = true;
5914
rc = vdWriteHelper(pDiskTo, pImageTo, NULL, uOffset, pvBuf,
5915
cbThisRead, false /* fUpdateCache */);
5919
rc2 = vdThreadFinishWrite(pDiskTo);
5921
fLockWriteTo = false;
5923
else /* Don't propagate the error to the outside */
5926
uOffset += cbThisRead;
5927
cbRemaining -= cbThisRead;
5929
if (pCbProgress && pCbProgress->pfnProgress)
5931
/** @todo r=klaus: this can update the progress to the same
5932
* percentage over and over again if the image format makes
5933
* relatively small increments. */
5934
rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
5935
uOffset * 99 / cbSize);
5939
if (pDstCbProgress && pDstCbProgress->pfnProgress)
5941
/** @todo r=klaus: this can update the progress to the same
5942
* percentage over and over again if the image format makes
5943
* relatively small increments. */
5944
rc = pDstCbProgress->pfnProgress(pDstIfProgress->pvUser,
5945
uOffset * 99 / cbSize);
5949
} while (uOffset < cbSize);
5953
rc2 = vdThreadStartWrite(pDiskTo);
5955
fLockWriteTo = true;
5957
/* Only set modification UUID if it is non-null, since the source
5958
* backend might not provide a valid modification UUID. */
5959
if (!RTUuidIsNull(&ImageModificationUuid))
5960
pImageTo->Backend->pfnSetModificationUuid(pImageTo->pBackendData, &ImageModificationUuid);
5962
/* Set the requested open flags if they differ from the value
5963
* required for creating the image and copying the contents. */
5964
if ( pImageTo && pszFilename
5965
&& uOpenFlags != (uOpenFlags & ~VD_OPEN_FLAGS_READONLY))
5966
rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
5971
if (RT_FAILURE(rc) && pImageTo && pszFilename)
5973
/* Take the write lock only if it is not taken. Not worth making the
5974
* above code even more complicated. */
5975
if (RT_UNLIKELY(!fLockWriteTo))
5977
rc2 = vdThreadStartWrite(pDiskTo);
5979
fLockWriteTo = true;
5981
/* Error detected, but new image created. Remove image from list. */
5982
vdRemoveImageFromList(pDiskTo, pImageTo);
5984
/* Close and delete image. */
5985
rc2 = pImageTo->Backend->pfnClose(pImageTo->pBackendData, true);
5987
pImageTo->pBackendData = NULL;
5989
/* Free remaining resources. */
5990
if (pImageTo->pszFilename)
5991
RTStrFree(pImageTo->pszFilename);
5993
RTMemFree(pImageTo);
5996
if (RT_UNLIKELY(fLockWriteTo))
5998
rc2 = vdThreadFinishWrite(pDiskTo);
6001
if (RT_UNLIKELY(fLockWriteFrom))
6003
rc2 = vdThreadFinishWrite(pDiskFrom);
6006
else if (RT_UNLIKELY(fLockReadFrom))
6008
rc2 = vdThreadFinishRead(pDiskFrom);
6013
RTMemTmpFree(pvBuf);
6017
if (pCbProgress && pCbProgress->pfnProgress)
6018
pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
6019
if (pDstCbProgress && pDstCbProgress->pfnProgress)
6020
pDstCbProgress->pfnProgress(pDstIfProgress->pvUser, 100);
6023
LogFlowFunc(("returns %Rrc\n", rc));
6028
* Optimizes the storage consumption of an image. Typically the unused blocks
6029
* have to be wiped with zeroes to achieve a substantial reduced storage use.
6030
* Another optimization done is reordering the image blocks, which can provide
6031
* a significant performance boost, as reads and writes tend to use less random
6034
* @return VBox status code.
6035
* @return VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6036
* @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
6037
* @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
6038
* the code for this isn't implemented yet.
6039
* @param pDisk Pointer to HDD container.
6040
* @param nImage Image number, counts from 0. 0 is always base image of container.
6041
* @param pVDIfsOperation Pointer to the per-operation VD interface list.
6043
VBOXDDU_DECL(int) VDCompact(PVBOXHDD pDisk, unsigned nImage,
6044
PVDINTERFACE pVDIfsOperation)
6046
int rc = VINF_SUCCESS;
6048
bool fLockRead = false, fLockWrite = false;
6052
LogFlowFunc(("pDisk=%#p nImage=%u pVDIfsOperation=%#p\n",
6053
pDisk, nImage, pVDIfsOperation));
6055
PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
6056
VDINTERFACETYPE_PROGRESS);
6057
PVDINTERFACEPROGRESS pCbProgress = NULL;
6059
pCbProgress = VDGetInterfaceProgress(pIfProgress);
6062
/* Check arguments. */
6063
AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
6064
rc = VERR_INVALID_PARAMETER);
6065
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
6066
("u32Signature=%08x\n", pDisk->u32Signature));
6068
rc2 = vdThreadStartRead(pDisk);
6072
PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6073
AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
6075
/* If there is no compact callback for not file based backends then
6076
* the backend doesn't need compaction. No need to make much fuss about
6077
* this. For file based ones signal this as not yet supported. */
6078
if (!pImage->Backend->pfnCompact)
6080
if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
6081
rc = VERR_NOT_SUPPORTED;
6087
/* Insert interface for reading parent state into per-operation list,
6088
* if there is a parent image. */
6089
VDINTERFACE IfOpParent;
6090
VDINTERFACEPARENTSTATE ParentCb;
6091
VDPARENTSTATEDESC ParentUser;
6094
ParentCb.cbSize = sizeof(ParentCb);
6095
ParentCb.enmInterface = VDINTERFACETYPE_PARENTSTATE;
6096
ParentCb.pfnParentRead = vdParentRead;
6097
ParentUser.pDisk = pDisk;
6098
ParentUser.pImage = pImage->pPrev;
6099
rc = VDInterfaceAdd(&IfOpParent, "VDCompact_ParentState", VDINTERFACETYPE_PARENTSTATE,
6100
&ParentCb, &ParentUser, &pVDIfsOperation);
6104
rc2 = vdThreadFinishRead(pDisk);
6108
rc2 = vdThreadStartWrite(pDisk);
6112
rc = pImage->Backend->pfnCompact(pImage->pBackendData,
6115
pImage->pVDIfsImage,
6119
if (RT_UNLIKELY(fLockWrite))
6121
rc2 = vdThreadFinishWrite(pDisk);
6124
else if (RT_UNLIKELY(fLockRead))
6126
rc2 = vdThreadFinishRead(pDisk);
6131
RTMemTmpFree(pvBuf);
6133
RTMemTmpFree(pvTmp);
6137
if (pCbProgress && pCbProgress->pfnProgress)
6138
pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
6141
LogFlowFunc(("returns %Rrc\n", rc));
6146
* Resizes the the given disk image to the given size.
6148
* @return VBox status
6149
* @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
6150
* @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
6152
* @param pDisk Pointer to the HDD container.
6153
* @param cbSize New size of the image.
6154
* @param pPCHSGeometry Pointer to the new physical disk geometry <= (16383,16,63). Not NULL.
6155
* @param pLCHSGeometry Pointer to the new logical disk geometry <= (x,255,63). Not NULL.
6156
* @param pVDIfsOperation Pointer to the per-operation VD interface list.
6158
VBOXDDU_DECL(int) VDResize(PVBOXHDD pDisk, uint64_t cbSize,
6159
PCVDGEOMETRY pPCHSGeometry,
6160
PCVDGEOMETRY pLCHSGeometry,
6161
PVDINTERFACE pVDIfsOperation)
6163
/** @todo r=klaus resizing was designed to be part of VDCopy, so having a separate function is not desirable. */
6164
int rc = VINF_SUCCESS;
6166
bool fLockRead = false, fLockWrite = false;
6168
LogFlowFunc(("pDisk=%#p cbSize=%llu pVDIfsOperation=%#p\n",
6169
pDisk, cbSize, pVDIfsOperation));
6171
PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
6172
VDINTERFACETYPE_PROGRESS);
6173
PVDINTERFACEPROGRESS pCbProgress = NULL;
6175
pCbProgress = VDGetInterfaceProgress(pIfProgress);
6178
/* Check arguments. */
6179
AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
6180
rc = VERR_INVALID_PARAMETER);
6181
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
6182
("u32Signature=%08x\n", pDisk->u32Signature));
6184
rc2 = vdThreadStartRead(pDisk);
6188
/* Not supported if the disk has child images attached. */
6189
AssertMsgBreakStmt(pDisk->cImages == 1, ("cImages=%u\n", pDisk->cImages),
6190
rc = VERR_NOT_SUPPORTED);
6192
PVDIMAGE pImage = pDisk->pBase;
6194
/* If there is no compact callback for not file based backends then
6195
* the backend doesn't need compaction. No need to make much fuss about
6196
* this. For file based ones signal this as not yet supported. */
6197
if (!pImage->Backend->pfnResize)
6199
if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
6200
rc = VERR_NOT_SUPPORTED;
6206
rc2 = vdThreadFinishRead(pDisk);
6210
rc2 = vdThreadStartWrite(pDisk);
6214
VDGEOMETRY PCHSGeometryOld;
6215
VDGEOMETRY LCHSGeometryOld;
6216
PCVDGEOMETRY pPCHSGeometryNew;
6217
PCVDGEOMETRY pLCHSGeometryNew;
6219
if (pPCHSGeometry->cCylinders == 0)
6221
/* Auto-detect marker, calculate new value ourself. */
6222
rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData, &PCHSGeometryOld);
6223
if (RT_SUCCESS(rc) && (PCHSGeometryOld.cCylinders != 0))
6224
PCHSGeometryOld.cCylinders = RT_MIN(cbSize / 512 / PCHSGeometryOld.cHeads / PCHSGeometryOld.cSectors, 16383);
6225
else if (rc == VERR_VD_GEOMETRY_NOT_SET)
6228
pPCHSGeometryNew = &PCHSGeometryOld;
6231
pPCHSGeometryNew = pPCHSGeometry;
6233
if (pLCHSGeometry->cCylinders == 0)
6235
/* Auto-detect marker, calculate new value ourself. */
6236
rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData, &LCHSGeometryOld);
6237
if (RT_SUCCESS(rc) && (LCHSGeometryOld.cCylinders != 0))
6238
LCHSGeometryOld.cCylinders = cbSize / 512 / LCHSGeometryOld.cHeads / LCHSGeometryOld.cSectors;
6239
else if (rc == VERR_VD_GEOMETRY_NOT_SET)
6242
pLCHSGeometryNew = &LCHSGeometryOld;
6245
pLCHSGeometryNew = pLCHSGeometry;
6248
rc = pImage->Backend->pfnResize(pImage->pBackendData,
6254
pImage->pVDIfsImage,
6258
if (RT_UNLIKELY(fLockWrite))
6260
rc2 = vdThreadFinishWrite(pDisk);
6263
else if (RT_UNLIKELY(fLockRead))
6265
rc2 = vdThreadFinishRead(pDisk);
6271
if (pCbProgress && pCbProgress->pfnProgress)
6272
pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
6275
LogFlowFunc(("returns %Rrc\n", rc));
6280
* Closes the last opened image file in HDD container.
6281
* If previous image file was opened in read-only mode (the normal case) and
6282
* the last opened image is in read-write mode then the previous image will be
6283
* reopened in read/write mode.
6285
* @returns VBox status code.
6286
* @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
6287
* @param pDisk Pointer to HDD container.
6288
* @param fDelete If true, delete the image from the host disk.
6290
VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
6292
int rc = VINF_SUCCESS;
6294
bool fLockWrite = false;
6296
LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
6300
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6301
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6303
/* Not worth splitting this up into a read lock phase and write
6304
* lock phase, as closing an image is a relatively fast operation
6305
* dominated by the part which needs the write lock. */
6306
rc2 = vdThreadStartWrite(pDisk);
6310
PVDIMAGE pImage = pDisk->pLast;
6313
rc = VERR_VD_NOT_OPENED;
6316
unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
6317
/* Remove image from list of opened images. */
6318
vdRemoveImageFromList(pDisk, pImage);
6319
/* Close (and optionally delete) image. */
6320
rc = pImage->Backend->pfnClose(pImage->pBackendData, fDelete);
6321
/* Free remaining resources related to the image. */
6322
RTStrFree(pImage->pszFilename);
6325
pImage = pDisk->pLast;
6329
/* If disk was previously in read/write mode, make sure it will stay
6330
* like this (if possible) after closing this image. Set the open flags
6332
if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
6334
uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
6335
uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
6336
rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData, uOpenFlags);
6339
/* Cache disk information. */
6340
pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
6342
/* Cache PCHS geometry. */
6343
rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
6344
&pDisk->PCHSGeometry);
6345
if (RT_FAILURE(rc2))
6347
pDisk->PCHSGeometry.cCylinders = 0;
6348
pDisk->PCHSGeometry.cHeads = 0;
6349
pDisk->PCHSGeometry.cSectors = 0;
6353
/* Make sure the PCHS geometry is properly clipped. */
6354
pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
6355
pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
6356
pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
6359
/* Cache LCHS geometry. */
6360
rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
6361
&pDisk->LCHSGeometry);
6362
if (RT_FAILURE(rc2))
6364
pDisk->LCHSGeometry.cCylinders = 0;
6365
pDisk->LCHSGeometry.cHeads = 0;
6366
pDisk->LCHSGeometry.cSectors = 0;
6370
/* Make sure the LCHS geometry is properly clipped. */
6371
pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
6372
pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
6376
if (RT_UNLIKELY(fLockWrite))
6378
rc2 = vdThreadFinishWrite(pDisk);
6382
LogFlowFunc(("returns %Rrc\n", rc));
6387
* Closes the currently opened cache image file in HDD container.
6389
* @return VBox status code.
6390
* @return VERR_VD_NOT_OPENED if no cache is opened in HDD container.
6391
* @param pDisk Pointer to HDD container.
6392
* @param fDelete If true, delete the image from the host disk.
6394
VBOXDDU_DECL(int) VDCacheClose(PVBOXHDD pDisk, bool fDelete)
6396
int rc = VINF_SUCCESS;
6398
bool fLockWrite = false;
6399
PVDCACHE pCache = NULL;
6401
LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
6406
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6407
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6409
rc2 = vdThreadStartWrite(pDisk);
6413
AssertPtrBreakStmt(pDisk->pCache, rc = VERR_VD_CACHE_NOT_FOUND);
6415
pCache = pDisk->pCache;
6416
pDisk->pCache = NULL;
6418
pCache->Backend->pfnClose(pCache->pBackendData, fDelete);
6419
if (pCache->pszFilename)
6420
RTStrFree(pCache->pszFilename);
6424
if (RT_LIKELY(fLockWrite))
6426
rc2 = vdThreadFinishWrite(pDisk);
6430
LogFlowFunc(("returns %Rrc\n", rc));
6435
* Closes all opened image files in HDD container.
6437
* @returns VBox status code.
6438
* @param pDisk Pointer to HDD container.
6440
VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
6442
int rc = VINF_SUCCESS;
6444
bool fLockWrite = false;
6446
LogFlowFunc(("pDisk=%#p\n", pDisk));
6450
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6451
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6453
/* Lock the entire operation. */
6454
rc2 = vdThreadStartWrite(pDisk);
6458
PVDCACHE pCache = pDisk->pCache;
6461
rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
6462
if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
6465
if (pCache->pszFilename)
6466
RTStrFree(pCache->pszFilename);
6470
PVDIMAGE pImage = pDisk->pLast;
6471
while (VALID_PTR(pImage))
6473
PVDIMAGE pPrev = pImage->pPrev;
6474
/* Remove image from list of opened images. */
6475
vdRemoveImageFromList(pDisk, pImage);
6477
rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
6478
if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
6480
/* Free remaining resources related to the image. */
6481
RTStrFree(pImage->pszFilename);
6485
Assert(!VALID_PTR(pDisk->pLast));
6488
if (RT_UNLIKELY(fLockWrite))
6490
rc2 = vdThreadFinishWrite(pDisk);
6494
LogFlowFunc(("returns %Rrc\n", rc));
6499
* Read data from virtual HDD.
6501
* @returns VBox status code.
6502
* @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
6503
* @param pDisk Pointer to HDD container.
6504
* @param uOffset Offset of first reading byte from start of disk.
6505
* @param pvBuf Pointer to buffer for reading data.
6506
* @param cbRead Number of bytes to read.
6508
VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
6511
int rc = VINF_SUCCESS;
6513
bool fLockRead = false;
6515
LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
6516
pDisk, uOffset, pvBuf, cbRead));
6520
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6521
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6523
/* Check arguments. */
6524
AssertMsgBreakStmt(VALID_PTR(pvBuf),
6525
("pvBuf=%#p\n", pvBuf),
6526
rc = VERR_INVALID_PARAMETER);
6527
AssertMsgBreakStmt(cbRead,
6528
("cbRead=%zu\n", cbRead),
6529
rc = VERR_INVALID_PARAMETER);
6531
rc2 = vdThreadStartRead(pDisk);
6535
AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
6536
("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
6537
uOffset, cbRead, pDisk->cbSize),
6538
rc = VERR_INVALID_PARAMETER);
6540
PVDIMAGE pImage = pDisk->pLast;
6541
AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
6543
rc = vdReadHelper(pDisk, pImage, NULL, uOffset, pvBuf, cbRead,
6544
true /* fZeroFreeBlocks */,
6545
true /* fUpdateCache */);
6548
if (RT_UNLIKELY(fLockRead))
6550
rc2 = vdThreadFinishRead(pDisk);
6554
LogFlowFunc(("returns %Rrc\n", rc));
6559
* Write data to virtual HDD.
6561
* @returns VBox status code.
6562
* @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
6563
* @param pDisk Pointer to HDD container.
6564
* @param uOffset Offset of the first byte being
6565
* written from start of disk.
6566
* @param pvBuf Pointer to buffer for writing data.
6567
* @param cbWrite Number of bytes to write.
6569
VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
6572
int rc = VINF_SUCCESS;
6574
bool fLockWrite = false;
6576
LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
6577
pDisk, uOffset, pvBuf, cbWrite));
6581
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6582
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6584
/* Check arguments. */
6585
AssertMsgBreakStmt(VALID_PTR(pvBuf),
6586
("pvBuf=%#p\n", pvBuf),
6587
rc = VERR_INVALID_PARAMETER);
6588
AssertMsgBreakStmt(cbWrite,
6589
("cbWrite=%zu\n", cbWrite),
6590
rc = VERR_INVALID_PARAMETER);
6592
rc2 = vdThreadStartWrite(pDisk);
6596
AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
6597
("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
6598
uOffset, cbWrite, pDisk->cbSize),
6599
rc = VERR_INVALID_PARAMETER);
6601
PVDIMAGE pImage = pDisk->pLast;
6602
AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
6604
vdSetModifiedFlag(pDisk);
6605
rc = vdWriteHelper(pDisk, pImage, NULL, uOffset, pvBuf, cbWrite,
6606
true /* fUpdateCache */);
6610
/* If there is a merge (in the direction towards a parent) running
6611
* concurrently then we have to also "relay" the write to this parent,
6612
* as the merge position might be already past the position where
6613
* this write is going. The "context" of the write can come from the
6614
* natural chain, since merging either already did or will take care
6615
* of the "other" content which is might be needed to fill the block
6616
* to a full allocation size. The cache doesn't need to be touched
6617
* as this write is covered by the previous one. */
6618
if (RT_UNLIKELY(pDisk->pImageRelay))
6619
rc = vdWriteHelper(pDisk, pDisk->pImageRelay, NULL, uOffset,
6620
pvBuf, cbWrite, false /* fUpdateCache */);
6623
if (RT_UNLIKELY(fLockWrite))
6625
rc2 = vdThreadFinishWrite(pDisk);
6629
LogFlowFunc(("returns %Rrc\n", rc));
6634
* Make sure the on disk representation of a virtual HDD is up to date.
6636
* @returns VBox status code.
6637
* @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
6638
* @param pDisk Pointer to HDD container.
6640
VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
6642
int rc = VINF_SUCCESS;
6644
bool fLockWrite = false;
6646
LogFlowFunc(("pDisk=%#p\n", pDisk));
6650
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6651
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6653
rc2 = vdThreadStartWrite(pDisk);
6657
PVDIMAGE pImage = pDisk->pLast;
6658
AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
6660
vdResetModifiedFlag(pDisk);
6661
rc = pImage->Backend->pfnFlush(pImage->pBackendData);
6665
rc = pDisk->pCache->Backend->pfnFlush(pDisk->pCache->pBackendData);
6668
if (RT_UNLIKELY(fLockWrite))
6670
rc2 = vdThreadFinishWrite(pDisk);
6674
LogFlowFunc(("returns %Rrc\n", rc));
6679
* Get number of opened images in HDD container.
6681
* @returns Number of opened images for HDD container. 0 if no images have been opened.
6682
* @param pDisk Pointer to HDD container.
6684
VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
6688
bool fLockRead = false;
6690
LogFlowFunc(("pDisk=%#p\n", pDisk));
6694
AssertPtrBreakStmt(pDisk, cImages = 0);
6695
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6697
rc2 = vdThreadStartRead(pDisk);
6701
cImages = pDisk->cImages;
6704
if (RT_UNLIKELY(fLockRead))
6706
rc2 = vdThreadFinishRead(pDisk);
6710
LogFlowFunc(("returns %u\n", cImages));
6715
* Get read/write mode of HDD container.
6717
* @returns Virtual disk ReadOnly status.
6718
* @returns true if no image is opened in HDD container.
6719
* @param pDisk Pointer to HDD container.
6721
VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
6725
bool fLockRead = false;
6727
LogFlowFunc(("pDisk=%#p\n", pDisk));
6731
AssertPtrBreakStmt(pDisk, fReadOnly = false);
6732
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6734
rc2 = vdThreadStartRead(pDisk);
6738
PVDIMAGE pImage = pDisk->pLast;
6739
AssertPtrBreakStmt(pImage, fReadOnly = true);
6741
unsigned uOpenFlags;
6742
uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
6743
fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
6746
if (RT_UNLIKELY(fLockRead))
6748
rc2 = vdThreadFinishRead(pDisk);
6752
LogFlowFunc(("returns %d\n", fReadOnly));
6757
* Get total capacity of an image in HDD container.
6759
* @returns Virtual disk size in bytes.
6760
* @returns 0 if no image with specified number was not opened.
6761
* @param pDisk Pointer to HDD container.
6762
* @param nImage Image number, counts from 0. 0 is always base image of container.
6764
VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
6768
bool fLockRead = false;
6770
LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
6774
AssertPtrBreakStmt(pDisk, cbSize = 0);
6775
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6777
rc2 = vdThreadStartRead(pDisk);
6781
PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6782
AssertPtrBreakStmt(pImage, cbSize = 0);
6783
cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
6786
if (RT_UNLIKELY(fLockRead))
6788
rc2 = vdThreadFinishRead(pDisk);
6792
LogFlowFunc(("returns %llu\n", cbSize));
6797
* Get total file size of an image in HDD container.
6799
* @returns Virtual disk size in bytes.
6800
* @returns 0 if no image is opened in HDD container.
6801
* @param pDisk Pointer to HDD container.
6802
* @param nImage Image number, counts from 0. 0 is always base image of container.
6804
VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
6808
bool fLockRead = false;
6810
LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
6814
AssertPtrBreakStmt(pDisk, cbSize = 0);
6815
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6817
rc2 = vdThreadStartRead(pDisk);
6821
PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6822
AssertPtrBreakStmt(pImage, cbSize = 0);
6823
cbSize = pImage->Backend->pfnGetFileSize(pImage->pBackendData);
6826
if (RT_UNLIKELY(fLockRead))
6828
rc2 = vdThreadFinishRead(pDisk);
6832
LogFlowFunc(("returns %llu\n", cbSize));
6837
* Get virtual disk PCHS geometry stored in HDD container.
6839
* @returns VBox status code.
6840
* @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6841
* @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
6842
* @param pDisk Pointer to HDD container.
6843
* @param nImage Image number, counts from 0. 0 is always base image of container.
6844
* @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
6846
VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
6847
PVDGEOMETRY pPCHSGeometry)
6849
int rc = VINF_SUCCESS;
6851
bool fLockRead = false;
6853
LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
6854
pDisk, nImage, pPCHSGeometry));
6858
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6859
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6861
/* Check arguments. */
6862
AssertMsgBreakStmt(VALID_PTR(pPCHSGeometry),
6863
("pPCHSGeometry=%#p\n", pPCHSGeometry),
6864
rc = VERR_INVALID_PARAMETER);
6866
rc2 = vdThreadStartRead(pDisk);
6870
PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6871
AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
6873
if (pImage == pDisk->pLast)
6875
/* Use cached information if possible. */
6876
if (pDisk->PCHSGeometry.cCylinders != 0)
6877
*pPCHSGeometry = pDisk->PCHSGeometry;
6879
rc = VERR_VD_GEOMETRY_NOT_SET;
6882
rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
6886
if (RT_UNLIKELY(fLockRead))
6888
rc2 = vdThreadFinishRead(pDisk);
6892
LogFlowFunc(("%Rrc (PCHS=%u/%u/%u)\n", rc,
6893
pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
6894
pDisk->PCHSGeometry.cSectors));
6899
* Store virtual disk PCHS geometry in HDD container.
6901
* Note that in case of unrecoverable error all images in HDD container will be closed.
6903
* @returns VBox status code.
6904
* @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6905
* @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
6906
* @param pDisk Pointer to HDD container.
6907
* @param nImage Image number, counts from 0. 0 is always base image of container.
6908
* @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
6910
VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
6911
PCVDGEOMETRY pPCHSGeometry)
6913
int rc = VINF_SUCCESS;
6915
bool fLockWrite = false;
6917
LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
6918
pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
6919
pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
6923
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6924
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6926
/* Check arguments. */
6927
AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
6928
&& pPCHSGeometry->cHeads <= 16
6929
&& pPCHSGeometry->cSectors <= 63,
6930
("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
6931
pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
6932
pPCHSGeometry->cSectors),
6933
rc = VERR_INVALID_PARAMETER);
6935
rc2 = vdThreadStartWrite(pDisk);
6939
PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6940
AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
6942
if (pImage == pDisk->pLast)
6944
if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
6945
|| pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
6946
|| pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
6948
/* Only update geometry if it is changed. Avoids similar checks
6949
* in every backend. Most of the time the new geometry is set
6950
* to the previous values, so no need to go through the hassle
6951
* of updating an image which could be opened in read-only mode
6953
rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
6956
/* Cache new geometry values in any case. */
6957
rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
6958
&pDisk->PCHSGeometry);
6959
if (RT_FAILURE(rc2))
6961
pDisk->PCHSGeometry.cCylinders = 0;
6962
pDisk->PCHSGeometry.cHeads = 0;
6963
pDisk->PCHSGeometry.cSectors = 0;
6967
/* Make sure the CHS geometry is properly clipped. */
6968
pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
6969
pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
6976
rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
6979
|| pPCHSGeometry->cCylinders != PCHS.cCylinders
6980
|| pPCHSGeometry->cHeads != PCHS.cHeads
6981
|| pPCHSGeometry->cSectors != PCHS.cSectors)
6983
/* Only update geometry if it is changed. Avoids similar checks
6984
* in every backend. Most of the time the new geometry is set
6985
* to the previous values, so no need to go through the hassle
6986
* of updating an image which could be opened in read-only mode
6988
rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
6994
if (RT_UNLIKELY(fLockWrite))
6996
rc2 = vdThreadFinishWrite(pDisk);
7000
LogFlowFunc(("returns %Rrc\n", rc));
7005
* Get virtual disk LCHS geometry stored in HDD container.
7007
* @returns VBox status code.
7008
* @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7009
* @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
7010
* @param pDisk Pointer to HDD container.
7011
* @param nImage Image number, counts from 0. 0 is always base image of container.
7012
* @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
7014
VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
7015
PVDGEOMETRY pLCHSGeometry)
7017
int rc = VINF_SUCCESS;
7019
bool fLockRead = false;
7021
LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
7022
pDisk, nImage, pLCHSGeometry));
7026
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7027
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7029
/* Check arguments. */
7030
AssertMsgBreakStmt(VALID_PTR(pLCHSGeometry),
7031
("pLCHSGeometry=%#p\n", pLCHSGeometry),
7032
rc = VERR_INVALID_PARAMETER);
7034
rc2 = vdThreadStartRead(pDisk);
7038
PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7039
AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7041
if (pImage == pDisk->pLast)
7043
/* Use cached information if possible. */
7044
if (pDisk->LCHSGeometry.cCylinders != 0)
7045
*pLCHSGeometry = pDisk->LCHSGeometry;
7047
rc = VERR_VD_GEOMETRY_NOT_SET;
7050
rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
7054
if (RT_UNLIKELY(fLockRead))
7056
rc2 = vdThreadFinishRead(pDisk);
7060
LogFlowFunc((": %Rrc (LCHS=%u/%u/%u)\n", rc,
7061
pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
7062
pDisk->LCHSGeometry.cSectors));
7067
* Store virtual disk LCHS geometry in HDD container.
7069
* Note that in case of unrecoverable error all images in HDD container will be closed.
7071
* @returns VBox status code.
7072
* @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7073
* @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
7074
* @param pDisk Pointer to HDD container.
7075
* @param nImage Image number, counts from 0. 0 is always base image of container.
7076
* @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
7078
VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
7079
PCVDGEOMETRY pLCHSGeometry)
7081
int rc = VINF_SUCCESS;
7083
bool fLockWrite = false;
7085
LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
7086
pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
7087
pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
7091
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7092
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7094
/* Check arguments. */
7095
AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
7096
&& pLCHSGeometry->cHeads <= 255
7097
&& pLCHSGeometry->cSectors <= 63,
7098
("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
7099
pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
7100
pLCHSGeometry->cSectors),
7101
rc = VERR_INVALID_PARAMETER);
7103
rc2 = vdThreadStartWrite(pDisk);
7107
PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7108
AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7110
if (pImage == pDisk->pLast)
7112
if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
7113
|| pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
7114
|| pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
7116
/* Only update geometry if it is changed. Avoids similar checks
7117
* in every backend. Most of the time the new geometry is set
7118
* to the previous values, so no need to go through the hassle
7119
* of updating an image which could be opened in read-only mode
7121
rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
7124
/* Cache new geometry values in any case. */
7125
rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
7126
&pDisk->LCHSGeometry);
7127
if (RT_FAILURE(rc2))
7129
pDisk->LCHSGeometry.cCylinders = 0;
7130
pDisk->LCHSGeometry.cHeads = 0;
7131
pDisk->LCHSGeometry.cSectors = 0;
7135
/* Make sure the CHS geometry is properly clipped. */
7136
pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
7137
pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
7144
rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
7147
|| pLCHSGeometry->cCylinders != LCHS.cCylinders
7148
|| pLCHSGeometry->cHeads != LCHS.cHeads
7149
|| pLCHSGeometry->cSectors != LCHS.cSectors)
7151
/* Only update geometry if it is changed. Avoids similar checks
7152
* in every backend. Most of the time the new geometry is set
7153
* to the previous values, so no need to go through the hassle
7154
* of updating an image which could be opened in read-only mode
7156
rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
7162
if (RT_UNLIKELY(fLockWrite))
7164
rc2 = vdThreadFinishWrite(pDisk);
7168
LogFlowFunc(("returns %Rrc\n", rc));
7173
* Get version of image in HDD container.
7175
* @returns VBox status code.
7176
* @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7177
* @param pDisk Pointer to HDD container.
7178
* @param nImage Image number, counts from 0. 0 is always base image of container.
7179
* @param puVersion Where to store the image version.
7181
VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
7182
unsigned *puVersion)
7184
int rc = VINF_SUCCESS;
7186
bool fLockRead = false;
7188
LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
7189
pDisk, nImage, puVersion));
7193
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7194
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7196
/* Check arguments. */
7197
AssertMsgBreakStmt(VALID_PTR(puVersion),
7198
("puVersion=%#p\n", puVersion),
7199
rc = VERR_INVALID_PARAMETER);
7201
rc2 = vdThreadStartRead(pDisk);
7205
PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7206
AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7208
*puVersion = pImage->Backend->pfnGetVersion(pImage->pBackendData);
7211
if (RT_UNLIKELY(fLockRead))
7213
rc2 = vdThreadFinishRead(pDisk);
7217
LogFlowFunc(("returns %Rrc uVersion=%#x\n", rc, *puVersion));
7222
* List the capabilities of image backend in HDD container.
7224
* @returns VBox status code.
7225
* @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7226
* @param pDisk Pointer to the HDD container.
7227
* @param nImage Image number, counts from 0. 0 is always base image of container.
7228
* @param pbackendInfo Where to store the backend information.
7230
VBOXDDU_DECL(int) VDBackendInfoSingle(PVBOXHDD pDisk, unsigned nImage,
7231
PVDBACKENDINFO pBackendInfo)
7233
int rc = VINF_SUCCESS;
7235
bool fLockRead = false;
7237
LogFlowFunc(("pDisk=%#p nImage=%u pBackendInfo=%#p\n",
7238
pDisk, nImage, pBackendInfo));
7242
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7243
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7245
/* Check arguments. */
7246
AssertMsgBreakStmt(VALID_PTR(pBackendInfo),
7247
("pBackendInfo=%#p\n", pBackendInfo),
7248
rc = VERR_INVALID_PARAMETER);
7250
rc2 = vdThreadStartRead(pDisk);
7254
PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7255
AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7257
pBackendInfo->pszBackend = pImage->Backend->pszBackendName;
7258
pBackendInfo->uBackendCaps = pImage->Backend->uBackendCaps;
7259
pBackendInfo->paFileExtensions = pImage->Backend->paFileExtensions;
7260
pBackendInfo->paConfigInfo = pImage->Backend->paConfigInfo;
7263
if (RT_UNLIKELY(fLockRead))
7265
rc2 = vdThreadFinishRead(pDisk);
7269
LogFlowFunc(("returns %Rrc\n", rc));
7274
* Get flags of image in HDD container.
7276
* @returns VBox status code.
7277
* @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7278
* @param pDisk Pointer to HDD container.
7279
* @param nImage Image number, counts from 0. 0 is always base image of container.
7280
* @param puImageFlags Where to store the image flags.
7282
VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
7283
unsigned *puImageFlags)
7285
int rc = VINF_SUCCESS;
7287
bool fLockRead = false;
7289
LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
7290
pDisk, nImage, puImageFlags));
7294
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7295
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7297
/* Check arguments. */
7298
AssertMsgBreakStmt(VALID_PTR(puImageFlags),
7299
("puImageFlags=%#p\n", puImageFlags),
7300
rc = VERR_INVALID_PARAMETER);
7302
rc2 = vdThreadStartRead(pDisk);
7306
PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7307
AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7309
*puImageFlags = pImage->uImageFlags;
7312
if (RT_UNLIKELY(fLockRead))
7314
rc2 = vdThreadFinishRead(pDisk);
7318
LogFlowFunc(("returns %Rrc uImageFlags=%#x\n", rc, *puImageFlags));
7323
* Get open flags of image in HDD container.
7325
* @returns VBox status code.
7326
* @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7327
* @param pDisk Pointer to HDD container.
7328
* @param nImage Image number, counts from 0. 0 is always base image of container.
7329
* @param puOpenFlags Where to store the image open flags.
7331
VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
7332
unsigned *puOpenFlags)
7334
int rc = VINF_SUCCESS;
7336
bool fLockRead = false;
7338
LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
7339
pDisk, nImage, puOpenFlags));
7343
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7344
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7346
/* Check arguments. */
7347
AssertMsgBreakStmt(VALID_PTR(puOpenFlags),
7348
("puOpenFlags=%#p\n", puOpenFlags),
7349
rc = VERR_INVALID_PARAMETER);
7351
rc2 = vdThreadStartRead(pDisk);
7355
PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7356
AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7358
*puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
7361
if (RT_UNLIKELY(fLockRead))
7363
rc2 = vdThreadFinishRead(pDisk);
7367
LogFlowFunc(("returns %Rrc uOpenFlags=%#x\n", rc, *puOpenFlags));
7372
* Set open flags of image in HDD container.
7373
* This operation may cause file locking changes and/or files being reopened.
7374
* Note that in case of unrecoverable error all images in HDD container will be closed.
7376
* @returns VBox status code.
7377
* @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7378
* @param pDisk Pointer to HDD container.
7379
* @param nImage Image number, counts from 0. 0 is always base image of container.
7380
* @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
7382
VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
7383
unsigned uOpenFlags)
7387
bool fLockWrite = false;
7389
LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
7393
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7394
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7396
/* Check arguments. */
7397
AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
7398
("uOpenFlags=%#x\n", uOpenFlags),
7399
rc = VERR_INVALID_PARAMETER);
7401
rc2 = vdThreadStartWrite(pDisk);
7405
PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7406
AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7408
rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData,
7412
if (RT_UNLIKELY(fLockWrite))
7414
rc2 = vdThreadFinishWrite(pDisk);
7418
LogFlowFunc(("returns %Rrc\n", rc));
7423
* Get base filename of image in HDD container. Some image formats use
7424
* other filenames as well, so don't use this for anything but informational
7427
* @returns VBox status code.
7428
* @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7429
* @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
7430
* @param pDisk Pointer to HDD container.
7431
* @param nImage Image number, counts from 0. 0 is always base image of container.
7432
* @param pszFilename Where to store the image file name.
7433
* @param cbFilename Size of buffer pszFilename points to.
7435
VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
7436
char *pszFilename, unsigned cbFilename)
7440
bool fLockRead = false;
7442
LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
7443
pDisk, nImage, pszFilename, cbFilename));
7447
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7448
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7450
/* Check arguments. */
7451
AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
7452
("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
7453
rc = VERR_INVALID_PARAMETER);
7454
AssertMsgBreakStmt(cbFilename,
7455
("cbFilename=%u\n", cbFilename),
7456
rc = VERR_INVALID_PARAMETER);
7458
rc2 = vdThreadStartRead(pDisk);
7462
PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7463
AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7465
size_t cb = strlen(pImage->pszFilename);
7466
if (cb <= cbFilename)
7468
strcpy(pszFilename, pImage->pszFilename);
7473
strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
7474
pszFilename[cbFilename - 1] = '\0';
7475
rc = VERR_BUFFER_OVERFLOW;
7479
if (RT_UNLIKELY(fLockRead))
7481
rc2 = vdThreadFinishRead(pDisk);
7485
LogFlowFunc(("returns %Rrc, pszFilename=\"%s\"\n", rc, pszFilename));
7490
* Get the comment line of image in HDD container.
7492
* @returns VBox status code.
7493
* @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7494
* @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
7495
* @param pDisk Pointer to HDD container.
7496
* @param nImage Image number, counts from 0. 0 is always base image of container.
7497
* @param pszComment Where to store the comment string of image. NULL is ok.
7498
* @param cbComment The size of pszComment buffer. 0 is ok.
7500
VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
7501
char *pszComment, unsigned cbComment)
7505
bool fLockRead = false;
7507
LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
7508
pDisk, nImage, pszComment, cbComment));
7512
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7513
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7515
/* Check arguments. */
7516
AssertMsgBreakStmt(VALID_PTR(pszComment),
7517
("pszComment=%#p \"%s\"\n", pszComment, pszComment),
7518
rc = VERR_INVALID_PARAMETER);
7519
AssertMsgBreakStmt(cbComment,
7520
("cbComment=%u\n", cbComment),
7521
rc = VERR_INVALID_PARAMETER);
7523
rc2 = vdThreadStartRead(pDisk);
7527
PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7528
AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7530
rc = pImage->Backend->pfnGetComment(pImage->pBackendData, pszComment,
7534
if (RT_UNLIKELY(fLockRead))
7536
rc2 = vdThreadFinishRead(pDisk);
7540
LogFlowFunc(("returns %Rrc, pszComment=\"%s\"\n", rc, pszComment));
7545
* Changes the comment line of image in HDD container.
7547
* @returns VBox status code.
7548
* @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7549
* @param pDisk Pointer to HDD container.
7550
* @param nImage Image number, counts from 0. 0 is always base image of container.
7551
* @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
7553
VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
7554
const char *pszComment)
7558
bool fLockWrite = false;
7560
LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
7561
pDisk, nImage, pszComment, pszComment));
7565
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7566
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7568
/* Check arguments. */
7569
AssertMsgBreakStmt(VALID_PTR(pszComment) || pszComment == NULL,
7570
("pszComment=%#p \"%s\"\n", pszComment, pszComment),
7571
rc = VERR_INVALID_PARAMETER);
7573
rc2 = vdThreadStartWrite(pDisk);
7577
PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7578
AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7580
rc = pImage->Backend->pfnSetComment(pImage->pBackendData, pszComment);
7583
if (RT_UNLIKELY(fLockWrite))
7585
rc2 = vdThreadFinishWrite(pDisk);
7589
LogFlowFunc(("returns %Rrc\n", rc));
7595
* Get UUID of image in HDD container.
7597
* @returns VBox status code.
7598
* @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7599
* @param pDisk Pointer to HDD container.
7600
* @param nImage Image number, counts from 0. 0 is always base image of container.
7601
* @param pUuid Where to store the image creation UUID.
7603
VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
7607
bool fLockRead = false;
7609
LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
7613
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7614
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7616
/* Check arguments. */
7617
AssertMsgBreakStmt(VALID_PTR(pUuid),
7618
("pUuid=%#p\n", pUuid),
7619
rc = VERR_INVALID_PARAMETER);
7621
rc2 = vdThreadStartRead(pDisk);
7625
PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7626
AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7628
rc = pImage->Backend->pfnGetUuid(pImage->pBackendData, pUuid);
7631
if (RT_UNLIKELY(fLockRead))
7633
rc2 = vdThreadFinishRead(pDisk);
7637
LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
7642
* Set the image's UUID. Should not be used by normal applications.
7644
* @returns VBox status code.
7645
* @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7646
* @param pDisk Pointer to HDD container.
7647
* @param nImage Image number, counts from 0. 0 is always base image of container.
7648
* @param pUuid New UUID of the image. If NULL, a new UUID is created.
7650
VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
7654
bool fLockWrite = false;
7656
LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
7657
pDisk, nImage, pUuid, pUuid));
7661
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7662
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7664
AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
7665
("pUuid=%#p\n", pUuid),
7666
rc = VERR_INVALID_PARAMETER);
7668
rc2 = vdThreadStartWrite(pDisk);
7672
PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7673
AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7678
RTUuidCreate(&Uuid);
7681
rc = pImage->Backend->pfnSetUuid(pImage->pBackendData, pUuid);
7684
if (RT_UNLIKELY(fLockWrite))
7686
rc2 = vdThreadFinishWrite(pDisk);
7690
LogFlowFunc(("returns %Rrc\n", rc));
7695
* Get last modification UUID of image in HDD container.
7697
* @returns VBox status code.
7698
* @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7699
* @param pDisk Pointer to HDD container.
7700
* @param nImage Image number, counts from 0. 0 is always base image of container.
7701
* @param pUuid Where to store the image modification UUID.
7703
VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
7705
int rc = VINF_SUCCESS;
7707
bool fLockRead = false;
7709
LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
7713
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7714
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7716
/* Check arguments. */
7717
AssertMsgBreakStmt(VALID_PTR(pUuid),
7718
("pUuid=%#p\n", pUuid),
7719
rc = VERR_INVALID_PARAMETER);
7721
rc2 = vdThreadStartRead(pDisk);
7725
PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7726
AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7728
rc = pImage->Backend->pfnGetModificationUuid(pImage->pBackendData,
7732
if (RT_UNLIKELY(fLockRead))
7734
rc2 = vdThreadFinishRead(pDisk);
7738
LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
7743
* Set the image's last modification UUID. Should not be used by normal applications.
7745
* @returns VBox status code.
7746
* @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7747
* @param pDisk Pointer to HDD container.
7748
* @param nImage Image number, counts from 0. 0 is always base image of container.
7749
* @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
7751
VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
7755
bool fLockWrite = false;
7757
LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
7758
pDisk, nImage, pUuid, pUuid));
7762
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7763
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7765
/* Check arguments. */
7766
AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
7767
("pUuid=%#p\n", pUuid),
7768
rc = VERR_INVALID_PARAMETER);
7770
rc2 = vdThreadStartWrite(pDisk);
7774
PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7775
AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7780
RTUuidCreate(&Uuid);
7783
rc = pImage->Backend->pfnSetModificationUuid(pImage->pBackendData,
7787
if (RT_UNLIKELY(fLockWrite))
7789
rc2 = vdThreadFinishWrite(pDisk);
7793
LogFlowFunc(("returns %Rrc\n", rc));
7798
* Get parent UUID of image in HDD container.
7800
* @returns VBox status code.
7801
* @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7802
* @param pDisk Pointer to HDD container.
7803
* @param nImage Image number, counts from 0. 0 is always base image of container.
7804
* @param pUuid Where to store the parent image UUID.
7806
VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
7809
int rc = VINF_SUCCESS;
7811
bool fLockRead = false;
7813
LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
7817
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7818
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7820
/* Check arguments. */
7821
AssertMsgBreakStmt(VALID_PTR(pUuid),
7822
("pUuid=%#p\n", pUuid),
7823
rc = VERR_INVALID_PARAMETER);
7825
rc2 = vdThreadStartRead(pDisk);
7829
PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7830
AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7832
rc = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, pUuid);
7835
if (RT_UNLIKELY(fLockRead))
7837
rc2 = vdThreadFinishRead(pDisk);
7841
LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
7846
* Set the image's parent UUID. Should not be used by normal applications.
7848
* @returns VBox status code.
7849
* @param pDisk Pointer to HDD container.
7850
* @param nImage Image number, counts from 0. 0 is always base image of container.
7851
* @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
7853
VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
7858
bool fLockWrite = false;
7860
LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
7861
pDisk, nImage, pUuid, pUuid));
7865
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7866
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7868
/* Check arguments. */
7869
AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
7870
("pUuid=%#p\n", pUuid),
7871
rc = VERR_INVALID_PARAMETER);
7873
rc2 = vdThreadStartWrite(pDisk);
7877
PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7878
AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7883
RTUuidCreate(&Uuid);
7886
rc = pImage->Backend->pfnSetParentUuid(pImage->pBackendData, pUuid);
7889
if (RT_UNLIKELY(fLockWrite))
7891
rc2 = vdThreadFinishWrite(pDisk);
7895
LogFlowFunc(("returns %Rrc\n", rc));
7901
* Debug helper - dumps all opened images in HDD container into the log file.
7903
* @param pDisk Pointer to HDD container.
7905
VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
7908
bool fLockRead = false;
7913
AssertPtrBreak(pDisk);
7914
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7916
if (!pDisk->pInterfaceErrorCallbacks || !VALID_PTR(pDisk->pInterfaceErrorCallbacks->pfnMessage))
7917
pDisk->pInterfaceErrorCallbacks->pfnMessage = vdLogMessage;
7919
rc2 = vdThreadStartRead(pDisk);
7923
vdMessageWrapper(pDisk, "--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
7924
for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
7926
vdMessageWrapper(pDisk, "Dumping VD image \"%s\" (Backend=%s)\n",
7927
pImage->pszFilename, pImage->Backend->pszBackendName);
7928
pImage->Backend->pfnDump(pImage->pBackendData);
7932
if (RT_UNLIKELY(fLockRead))
7934
rc2 = vdThreadFinishRead(pDisk);
7940
* Query if asynchronous operations are supported for this disk.
7942
* @returns VBox status code.
7943
* @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7944
* @param pDisk Pointer to the HDD container.
7945
* @param nImage Image number, counts from 0. 0 is always base image of container.
7946
* @param pfAIOSupported Where to store if async IO is supported.
7948
VBOXDDU_DECL(int) VDImageIsAsyncIOSupported(PVBOXHDD pDisk, unsigned nImage, bool *pfAIOSupported)
7950
int rc = VINF_SUCCESS;
7952
bool fLockRead = false;
7954
LogFlowFunc(("pDisk=%#p nImage=%u pfAIOSupported=%#p\n", pDisk, nImage, pfAIOSupported));
7958
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7959
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7961
/* Check arguments. */
7962
AssertMsgBreakStmt(VALID_PTR(pfAIOSupported),
7963
("pfAIOSupported=%#p\n", pfAIOSupported),
7964
rc = VERR_INVALID_PARAMETER);
7966
rc2 = vdThreadStartRead(pDisk);
7970
PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7971
AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7973
if (pImage->Backend->uBackendCaps & VD_CAP_ASYNC)
7974
*pfAIOSupported = pImage->Backend->pfnIsAsyncIOSupported(pImage->pBackendData);
7976
*pfAIOSupported = false;
7979
if (RT_UNLIKELY(fLockRead))
7981
rc2 = vdThreadFinishRead(pDisk);
7985
LogFlowFunc(("returns %Rrc, fAIOSupported=%u\n", rc, *pfAIOSupported));
7990
VBOXDDU_DECL(int) VDAsyncRead(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRead,
7992
PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
7993
void *pvUser1, void *pvUser2)
7995
int rc = VERR_VD_BLOCK_FREE;
7997
bool fLockRead = false;
7998
PVDIOCTX pIoCtx = NULL;
8000
LogFlowFunc(("pDisk=%#p uOffset=%llu pcSgBuf=%#p cbRead=%zu pvUser1=%#p pvUser2=%#p\n",
8001
pDisk, uOffset, pcSgBuf, cbRead, pvUser1, pvUser2));
8006
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8007
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8009
/* Check arguments. */
8010
AssertMsgBreakStmt(cbRead,
8011
("cbRead=%zu\n", cbRead),
8012
rc = VERR_INVALID_PARAMETER);
8013
AssertMsgBreakStmt(VALID_PTR(pcSgBuf),
8014
("pcSgBuf=%#p\n", pcSgBuf),
8015
rc = VERR_INVALID_PARAMETER);
8017
rc2 = vdThreadStartRead(pDisk);
8021
AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
8022
("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
8023
uOffset, cbRead, pDisk->cbSize),
8024
rc = VERR_INVALID_PARAMETER);
8025
AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
8027
pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_READ, uOffset,
8028
cbRead, pDisk->pLast, pcSgBuf,
8029
pfnComplete, pvUser1, pvUser2,
8030
NULL, vdReadHelperAsync);
8033
rc = VERR_NO_MEMORY;
8037
rc = vdIoCtxProcess(pIoCtx);
8038
if (rc == VINF_VD_ASYNC_IO_FINISHED)
8040
if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
8041
vdIoCtxFree(pDisk, pIoCtx);
8043
rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
8045
else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
8046
vdIoCtxFree(pDisk, pIoCtx);
8050
if (RT_UNLIKELY(fLockRead) && ( rc == VINF_VD_ASYNC_IO_FINISHED
8051
|| rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
8053
rc2 = vdThreadFinishRead(pDisk);
8057
LogFlowFunc(("returns %Rrc\n", rc));
8062
VBOXDDU_DECL(int) VDAsyncWrite(PVBOXHDD pDisk, uint64_t uOffset, size_t cbWrite,
8064
PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
8065
void *pvUser1, void *pvUser2)
8069
bool fLockWrite = false;
8070
PVDIOCTX pIoCtx = NULL;
8072
LogFlowFunc(("pDisk=%#p uOffset=%llu cSgBuf=%#p cbWrite=%zu pvUser1=%#p pvUser2=%#p\n",
8073
pDisk, uOffset, pcSgBuf, cbWrite, pvUser1, pvUser2));
8077
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8078
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8080
/* Check arguments. */
8081
AssertMsgBreakStmt(cbWrite,
8082
("cbWrite=%zu\n", cbWrite),
8083
rc = VERR_INVALID_PARAMETER);
8084
AssertMsgBreakStmt(VALID_PTR(pcSgBuf),
8085
("pcSgBuf=%#p\n", pcSgBuf),
8086
rc = VERR_INVALID_PARAMETER);
8088
rc2 = vdThreadStartWrite(pDisk);
8092
AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
8093
("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
8094
uOffset, cbWrite, pDisk->cbSize),
8095
rc = VERR_INVALID_PARAMETER);
8096
AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
8098
pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_WRITE, uOffset,
8099
cbWrite, pDisk->pLast, pcSgBuf,
8100
pfnComplete, pvUser1, pvUser2,
8101
NULL, vdWriteHelperAsync);
8104
rc = VERR_NO_MEMORY;
8108
rc = vdIoCtxProcess(pIoCtx);
8109
if (rc == VINF_VD_ASYNC_IO_FINISHED)
8111
if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
8112
vdIoCtxFree(pDisk, pIoCtx);
8114
rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
8116
else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
8117
vdIoCtxFree(pDisk, pIoCtx);
8120
if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
8121
|| rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
8123
rc2 = vdThreadFinishWrite(pDisk);
8127
LogFlowFunc(("returns %Rrc\n", rc));
8132
VBOXDDU_DECL(int) VDAsyncFlush(PVBOXHDD pDisk, PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
8133
void *pvUser1, void *pvUser2)
8137
bool fLockWrite = false;
8138
PVDIOCTX pIoCtx = NULL;
8140
LogFlowFunc(("pDisk=%#p\n", pDisk));
8145
AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8146
AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8148
rc2 = vdThreadStartWrite(pDisk);
8152
AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
8154
pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_FLUSH, 0,
8155
0, pDisk->pLast, NULL,
8156
pfnComplete, pvUser1, pvUser2,
8157
NULL, vdFlushHelperAsync);
8160
rc = VERR_NO_MEMORY;
8164
rc = vdIoCtxProcess(pIoCtx);
8165
if (rc == VINF_VD_ASYNC_IO_FINISHED)
8167
if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
8168
vdIoCtxFree(pDisk, pIoCtx);
8170
rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
8172
else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
8173
vdIoCtxFree(pDisk, pIoCtx);
8176
if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
8177
|| rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
8179
rc2 = vdThreadFinishWrite(pDisk);
8183
LogFlowFunc(("returns %Rrc\n", rc));