VirtualBox

source: vbox/trunk/src/VBox/Storage/VD.cpp@ 66378

Last change on this file since 66378 was 66378, checked in by vboxsync, 8 years ago

Storage/VD: Some cleanup (part 1), move the code related to plugin and backend management into a separate file to make navigation in VD.cpp easier

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 374.5 KB
Line 
1/* $Id: VD.cpp 66378 2017-03-31 11:20:50Z vboxsync $ */
2/** @file
3 * VD - Virtual disk container implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2017 Oracle Corporation
8 *
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.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_VD
23#include <VBox/vd.h>
24#include <VBox/err.h>
25#include <VBox/sup.h>
26#include <VBox/log.h>
27
28#include <iprt/alloc.h>
29#include <iprt/assert.h>
30#include <iprt/uuid.h>
31#include <iprt/file.h>
32#include <iprt/string.h>
33#include <iprt/asm.h>
34#include <iprt/param.h>
35#include <iprt/path.h>
36#include <iprt/sg.h>
37#include <iprt/semaphore.h>
38
39#include "VDInternal.h"
40
41/** Buffer size used for merging images. */
42#define VD_MERGE_BUFFER_SIZE (16 * _1M)
43
44/** Maximum number of segments in one I/O task. */
45#define VD_IO_TASK_SEGMENTS_MAX 64
46
47/** Threshold after not recently used blocks are removed from the list. */
48#define VD_DISCARD_REMOVE_THRESHOLD (10 * _1M) /** @todo experiment */
49
50/**
51 * VD async I/O interface storage descriptor.
52 */
53typedef struct VDIIOFALLBACKSTORAGE
54{
55 /** File handle. */
56 RTFILE File;
57 /** Completion callback. */
58 PFNVDCOMPLETED pfnCompleted;
59 /** Thread for async access. */
60 RTTHREAD ThreadAsync;
61} VDIIOFALLBACKSTORAGE, *PVDIIOFALLBACKSTORAGE;
62
63/**
64 * uModified bit flags.
65 */
66#define VD_IMAGE_MODIFIED_FLAG RT_BIT(0)
67#define VD_IMAGE_MODIFIED_FIRST RT_BIT(1)
68#define VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE RT_BIT(2)
69
70
71# define VD_IS_LOCKED(a_pDisk) \
72 do \
73 { \
74 NOREF(a_pDisk); \
75 AssertMsg((a_pDisk)->fLocked, \
76 ("Lock not held\n"));\
77 } while(0)
78
79/**
80 * VBox parent read descriptor, used internally for compaction.
81 */
82typedef struct VDPARENTSTATEDESC
83{
84 /** Pointer to disk descriptor. */
85 PVDISK pDisk;
86 /** Pointer to image descriptor. */
87 PVDIMAGE pImage;
88} VDPARENTSTATEDESC, *PVDPARENTSTATEDESC;
89
90/**
91 * Transfer direction.
92 */
93typedef enum VDIOCTXTXDIR
94{
95 /** Read */
96 VDIOCTXTXDIR_READ = 0,
97 /** Write */
98 VDIOCTXTXDIR_WRITE,
99 /** Flush */
100 VDIOCTXTXDIR_FLUSH,
101 /** Discard */
102 VDIOCTXTXDIR_DISCARD,
103 /** 32bit hack */
104 VDIOCTXTXDIR_32BIT_HACK = 0x7fffffff
105} VDIOCTXTXDIR, *PVDIOCTXTXDIR;
106
107/** Transfer function */
108typedef DECLCALLBACK(int) FNVDIOCTXTRANSFER (PVDIOCTX pIoCtx);
109/** Pointer to a transfer function. */
110typedef FNVDIOCTXTRANSFER *PFNVDIOCTXTRANSFER;
111
112/**
113 * I/O context
114 */
115typedef struct VDIOCTX
116{
117 /** Pointer to the next I/O context. */
118 struct VDIOCTX * volatile pIoCtxNext;
119 /** Disk this is request is for. */
120 PVDISK pDisk;
121 /** Return code. */
122 int rcReq;
123 /** Various flags for the I/O context. */
124 uint32_t fFlags;
125 /** Number of data transfers currently pending. */
126 volatile uint32_t cDataTransfersPending;
127 /** How many meta data transfers are pending. */
128 volatile uint32_t cMetaTransfersPending;
129 /** Flag whether the request finished */
130 volatile bool fComplete;
131 /** Temporary allocated memory which is freed
132 * when the context completes. */
133 void *pvAllocation;
134 /** Transfer function. */
135 PFNVDIOCTXTRANSFER pfnIoCtxTransfer;
136 /** Next transfer part after the current one completed. */
137 PFNVDIOCTXTRANSFER pfnIoCtxTransferNext;
138 /** Transfer direction */
139 VDIOCTXTXDIR enmTxDir;
140 /** Request type dependent data. */
141 union
142 {
143 /** I/O request (read/write). */
144 struct
145 {
146 /** Number of bytes left until this context completes. */
147 volatile uint32_t cbTransferLeft;
148 /** Current offset */
149 volatile uint64_t uOffset;
150 /** Number of bytes to transfer */
151 volatile size_t cbTransfer;
152 /** Current image in the chain. */
153 PVDIMAGE pImageCur;
154 /** Start image to read from. pImageCur is reset to this
155 * value after it reached the first image in the chain. */
156 PVDIMAGE pImageStart;
157 /** S/G buffer */
158 RTSGBUF SgBuf;
159 /** Number of bytes to clear in the buffer before the current read. */
160 size_t cbBufClear;
161 /** Number of images to read. */
162 unsigned cImagesRead;
163 /** Override for the parent image to start reading from. */
164 PVDIMAGE pImageParentOverride;
165 /** Original offset of the transfer - required for filtering read requests. */
166 uint64_t uOffsetXferOrig;
167 /** Original size of the transfer - required for fitlering read requests. */
168 size_t cbXferOrig;
169 } Io;
170 /** Discard requests. */
171 struct
172 {
173 /** Pointer to the range descriptor array. */
174 PCRTRANGE paRanges;
175 /** Number of ranges in the array. */
176 unsigned cRanges;
177 /** Range descriptor index which is processed. */
178 unsigned idxRange;
179 /** Start offset to discard currently. */
180 uint64_t offCur;
181 /** How many bytes left to discard in the current range. */
182 size_t cbDiscardLeft;
183 /** How many bytes to discard in the current block (<= cbDiscardLeft). */
184 size_t cbThisDiscard;
185 /** Discard block handled currently. */
186 PVDDISCARDBLOCK pBlock;
187 } Discard;
188 } Req;
189 /** Parent I/O context if any. Sets the type of the context (root/child) */
190 PVDIOCTX pIoCtxParent;
191 /** Type dependent data (root/child) */
192 union
193 {
194 /** Root data */
195 struct
196 {
197 /** Completion callback */
198 PFNVDASYNCTRANSFERCOMPLETE pfnComplete;
199 /** User argument 1 passed on completion. */
200 void *pvUser1;
201 /** User argument 2 passed on completion. */
202 void *pvUser2;
203 } Root;
204 /** Child data */
205 struct
206 {
207 /** Saved start offset */
208 uint64_t uOffsetSaved;
209 /** Saved transfer size */
210 size_t cbTransferLeftSaved;
211 /** Number of bytes transferred from the parent if this context completes. */
212 size_t cbTransferParent;
213 /** Number of bytes to pre read */
214 size_t cbPreRead;
215 /** Number of bytes to post read. */
216 size_t cbPostRead;
217 /** Number of bytes to write left in the parent. */
218 size_t cbWriteParent;
219 /** Write type dependent data. */
220 union
221 {
222 /** Optimized */
223 struct
224 {
225 /** Bytes to fill to satisfy the block size. Not part of the virtual disk. */
226 size_t cbFill;
227 /** Bytes to copy instead of reading from the parent */
228 size_t cbWriteCopy;
229 /** Bytes to read from the image. */
230 size_t cbReadImage;
231 } Optimized;
232 } Write;
233 } Child;
234 } Type;
235} VDIOCTX;
236
237/** Default flags for an I/O context, i.e. unblocked and async. */
238#define VDIOCTX_FLAGS_DEFAULT (0)
239/** Flag whether the context is blocked. */
240#define VDIOCTX_FLAGS_BLOCKED RT_BIT_32(0)
241/** Flag whether the I/O context is using synchronous I/O. */
242#define VDIOCTX_FLAGS_SYNC RT_BIT_32(1)
243/** Flag whether the read should update the cache. */
244#define VDIOCTX_FLAGS_READ_UPDATE_CACHE RT_BIT_32(2)
245/** Flag whether free blocks should be zeroed.
246 * If false and no image has data for sepcified
247 * range VERR_VD_BLOCK_FREE is returned for the I/O context.
248 * Note that unallocated blocks are still zeroed
249 * if at least one image has valid data for a part
250 * of the range.
251 */
252#define VDIOCTX_FLAGS_ZERO_FREE_BLOCKS RT_BIT_32(3)
253/** Don't free the I/O context when complete because
254 * it was alloacted elsewhere (stack, ...). */
255#define VDIOCTX_FLAGS_DONT_FREE RT_BIT_32(4)
256/** Don't set the modified flag for this I/O context when writing. */
257#define VDIOCTX_FLAGS_DONT_SET_MODIFIED_FLAG RT_BIT_32(5)
258/** The write filter was applied already and shouldn't be applied a second time.
259 * Used at the beginning of vdWriteHelperAsync() because it might be called
260 * multiple times.
261 */
262#define VDIOCTX_FLAGS_WRITE_FILTER_APPLIED RT_BIT_32(6)
263
264/** NIL I/O context pointer value. */
265#define NIL_VDIOCTX ((PVDIOCTX)0)
266
267/**
268 * List node for deferred I/O contexts.
269 */
270typedef struct VDIOCTXDEFERRED
271{
272 /** Node in the list of deferred requests.
273 * A request can be deferred if the image is growing
274 * and the request accesses the same range or if
275 * the backend needs to read or write metadata from the disk
276 * before it can continue. */
277 RTLISTNODE NodeDeferred;
278 /** I/O context this entry points to. */
279 PVDIOCTX pIoCtx;
280} VDIOCTXDEFERRED, *PVDIOCTXDEFERRED;
281
282/**
283 * I/O task.
284 */
285typedef struct VDIOTASK
286{
287 /** Next I/O task waiting in the list. */
288 struct VDIOTASK * volatile pNext;
289 /** Storage this task belongs to. */
290 PVDIOSTORAGE pIoStorage;
291 /** Optional completion callback. */
292 PFNVDXFERCOMPLETED pfnComplete;
293 /** Opaque user data. */
294 void *pvUser;
295 /** Completion status code for the task. */
296 int rcReq;
297 /** Flag whether this is a meta data transfer. */
298 bool fMeta;
299 /** Type dependent data. */
300 union
301 {
302 /** User data transfer. */
303 struct
304 {
305 /** Number of bytes this task transferred. */
306 uint32_t cbTransfer;
307 /** Pointer to the I/O context the task belongs. */
308 PVDIOCTX pIoCtx;
309 } User;
310 /** Meta data transfer. */
311 struct
312 {
313 /** Meta transfer this task is for. */
314 PVDMETAXFER pMetaXfer;
315 } Meta;
316 } Type;
317} VDIOTASK;
318
319/**
320 * Storage handle.
321 */
322typedef struct VDIOSTORAGE
323{
324 /** Image I/O state this storage handle belongs to. */
325 PVDIO pVDIo;
326 /** AVL tree for pending async metadata transfers. */
327 PAVLRFOFFTREE pTreeMetaXfers;
328 /** Storage handle */
329 void *pStorage;
330} VDIOSTORAGE;
331
332/**
333 * Metadata transfer.
334 *
335 * @note This entry can't be freed if either the list is not empty or
336 * the reference counter is not 0.
337 * The assumption is that the backends don't need to read huge amounts of
338 * metadata to complete a transfer so the additional memory overhead should
339 * be relatively small.
340 */
341typedef struct VDMETAXFER
342{
343 /** AVL core for fast search (the file offset is the key) */
344 AVLRFOFFNODECORE Core;
345 /** I/O storage for this transfer. */
346 PVDIOSTORAGE pIoStorage;
347 /** Flags. */
348 uint32_t fFlags;
349 /** List of I/O contexts waiting for this metadata transfer to complete. */
350 RTLISTNODE ListIoCtxWaiting;
351 /** Number of references to this entry. */
352 unsigned cRefs;
353 /** Size of the data stored with this entry. */
354 size_t cbMeta;
355 /** Shadow buffer which is used in case a write is still active and other
356 * writes update the shadow buffer. */
357 uint8_t *pbDataShw;
358 /** List of I/O contexts updating the shadow buffer while there is a write
359 * in progress. */
360 RTLISTNODE ListIoCtxShwWrites;
361 /** Data stored - variable size. */
362 uint8_t abData[1];
363} VDMETAXFER;
364
365/**
366 * The transfer direction for the metadata.
367 */
368#define VDMETAXFER_TXDIR_MASK 0x3
369#define VDMETAXFER_TXDIR_NONE 0x0
370#define VDMETAXFER_TXDIR_WRITE 0x1
371#define VDMETAXFER_TXDIR_READ 0x2
372#define VDMETAXFER_TXDIR_FLUSH 0x3
373#define VDMETAXFER_TXDIR_GET(flags) ((flags) & VDMETAXFER_TXDIR_MASK)
374#define VDMETAXFER_TXDIR_SET(flags, dir) ((flags) = (flags & ~VDMETAXFER_TXDIR_MASK) | (dir))
375
376/** Forward declaration of the async discard helper. */
377static DECLCALLBACK(int) vdDiscardHelperAsync(PVDIOCTX pIoCtx);
378static DECLCALLBACK(int) vdWriteHelperAsync(PVDIOCTX pIoCtx);
379static void vdDiskProcessBlockedIoCtx(PVDISK pDisk);
380static int vdDiskUnlock(PVDISK pDisk, PVDIOCTX pIoCtxRc);
381static DECLCALLBACK(void) vdIoCtxSyncComplete(void *pvUser1, void *pvUser2, int rcReq);
382
383/**
384 * internal: issue error message.
385 */
386static int vdError(PVDISK pDisk, int rc, RT_SRC_POS_DECL,
387 const char *pszFormat, ...)
388{
389 va_list va;
390 va_start(va, pszFormat);
391 if (pDisk->pInterfaceError)
392 pDisk->pInterfaceError->pfnError(pDisk->pInterfaceError->Core.pvUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
393 va_end(va);
394 return rc;
395}
396
397/**
398 * internal: thread synchronization, start read.
399 */
400DECLINLINE(int) vdThreadStartRead(PVDISK pDisk)
401{
402 int rc = VINF_SUCCESS;
403 if (RT_UNLIKELY(pDisk->pInterfaceThreadSync))
404 rc = pDisk->pInterfaceThreadSync->pfnStartRead(pDisk->pInterfaceThreadSync->Core.pvUser);
405 return rc;
406}
407
408/**
409 * internal: thread synchronization, finish read.
410 */
411DECLINLINE(int) vdThreadFinishRead(PVDISK pDisk)
412{
413 int rc = VINF_SUCCESS;
414 if (RT_UNLIKELY(pDisk->pInterfaceThreadSync))
415 rc = pDisk->pInterfaceThreadSync->pfnFinishRead(pDisk->pInterfaceThreadSync->Core.pvUser);
416 return rc;
417}
418
419/**
420 * internal: thread synchronization, start write.
421 */
422DECLINLINE(int) vdThreadStartWrite(PVDISK pDisk)
423{
424 int rc = VINF_SUCCESS;
425 if (RT_UNLIKELY(pDisk->pInterfaceThreadSync))
426 rc = pDisk->pInterfaceThreadSync->pfnStartWrite(pDisk->pInterfaceThreadSync->Core.pvUser);
427 return rc;
428}
429
430/**
431 * internal: thread synchronization, finish write.
432 */
433DECLINLINE(int) vdThreadFinishWrite(PVDISK pDisk)
434{
435 int rc = VINF_SUCCESS;
436 if (RT_UNLIKELY(pDisk->pInterfaceThreadSync))
437 rc = pDisk->pInterfaceThreadSync->pfnFinishWrite(pDisk->pInterfaceThreadSync->Core.pvUser);
438 return rc;
439}
440
441/**
442 * internal: add image structure to the end of images list.
443 */
444static void vdAddImageToList(PVDISK pDisk, PVDIMAGE pImage)
445{
446 pImage->pPrev = NULL;
447 pImage->pNext = NULL;
448
449 if (pDisk->pBase)
450 {
451 Assert(pDisk->cImages > 0);
452 pImage->pPrev = pDisk->pLast;
453 pDisk->pLast->pNext = pImage;
454 pDisk->pLast = pImage;
455 }
456 else
457 {
458 Assert(pDisk->cImages == 0);
459 pDisk->pBase = pImage;
460 pDisk->pLast = pImage;
461 }
462
463 pDisk->cImages++;
464}
465
466/**
467 * internal: remove image structure from the images list.
468 */
469static void vdRemoveImageFromList(PVDISK pDisk, PVDIMAGE pImage)
470{
471 Assert(pDisk->cImages > 0);
472
473 if (pImage->pPrev)
474 pImage->pPrev->pNext = pImage->pNext;
475 else
476 pDisk->pBase = pImage->pNext;
477
478 if (pImage->pNext)
479 pImage->pNext->pPrev = pImage->pPrev;
480 else
481 pDisk->pLast = pImage->pPrev;
482
483 pImage->pPrev = NULL;
484 pImage->pNext = NULL;
485
486 pDisk->cImages--;
487}
488
489/**
490 * Release a referene to the filter decrementing the counter and destroying the filter
491 * when the counter reaches zero.
492 *
493 * @returns The new reference count.
494 * @param pFilter The filter to release.
495 */
496static uint32_t vdFilterRelease(PVDFILTER pFilter)
497{
498 uint32_t cRefs = ASMAtomicDecU32(&pFilter->cRefs);
499 if (!cRefs)
500 {
501 pFilter->pBackend->pfnDestroy(pFilter->pvBackendData);
502 RTMemFree(pFilter);
503 }
504
505 return cRefs;
506}
507
508/**
509 * Increments the reference counter of the given filter.
510 *
511 * @return The new reference count.
512 * @param pFilter The filter.
513 */
514static uint32_t vdFilterRetain(PVDFILTER pFilter)
515{
516 return ASMAtomicIncU32(&pFilter->cRefs);
517}
518
519/**
520 * internal: find image by index into the images list.
521 */
522static PVDIMAGE vdGetImageByNumber(PVDISK pDisk, unsigned nImage)
523{
524 PVDIMAGE pImage = pDisk->pBase;
525 if (nImage == VD_LAST_IMAGE)
526 return pDisk->pLast;
527 while (pImage && nImage)
528 {
529 pImage = pImage->pNext;
530 nImage--;
531 }
532 return pImage;
533}
534
535/**
536 * Applies the filter chain to the given write request.
537 *
538 * @returns VBox status code.
539 * @param pDisk The HDD container.
540 * @param uOffset The start offset of the write.
541 * @param cbWrite Number of bytes to write.
542 * @param pIoCtx The I/O context associated with the request.
543 */
544static int vdFilterChainApplyWrite(PVDISK pDisk, uint64_t uOffset, size_t cbWrite,
545 PVDIOCTX pIoCtx)
546{
547 int rc = VINF_SUCCESS;
548
549 VD_IS_LOCKED(pDisk);
550
551 PVDFILTER pFilter;
552 RTListForEach(&pDisk->ListFilterChainWrite, pFilter, VDFILTER, ListNodeChainWrite)
553 {
554 rc = pFilter->pBackend->pfnFilterWrite(pFilter->pvBackendData, uOffset, cbWrite, pIoCtx);
555 if (RT_FAILURE(rc))
556 break;
557 /* Reset S/G buffer for the next filter. */
558 RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
559 }
560
561 return rc;
562}
563
564/**
565 * Applies the filter chain to the given read request.
566 *
567 * @returns VBox status code.
568 * @param pDisk The HDD container.
569 * @param uOffset The start offset of the read.
570 * @param cbRead Number of bytes read.
571 * @param pIoCtx The I/O context associated with the request.
572 */
573static int vdFilterChainApplyRead(PVDISK pDisk, uint64_t uOffset, size_t cbRead,
574 PVDIOCTX pIoCtx)
575{
576 int rc = VINF_SUCCESS;
577
578 VD_IS_LOCKED(pDisk);
579
580 /* Reset buffer before starting. */
581 RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
582
583 PVDFILTER pFilter;
584 RTListForEach(&pDisk->ListFilterChainRead, pFilter, VDFILTER, ListNodeChainRead)
585 {
586 rc = pFilter->pBackend->pfnFilterRead(pFilter->pvBackendData, uOffset, cbRead, pIoCtx);
587 if (RT_FAILURE(rc))
588 break;
589 /* Reset S/G buffer for the next filter. */
590 RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
591 }
592
593 return rc;
594}
595
596DECLINLINE(void) vdIoCtxRootComplete(PVDISK pDisk, PVDIOCTX pIoCtx)
597{
598 if ( RT_SUCCESS(pIoCtx->rcReq)
599 && pIoCtx->enmTxDir == VDIOCTXTXDIR_READ)
600 pIoCtx->rcReq = vdFilterChainApplyRead(pDisk, pIoCtx->Req.Io.uOffsetXferOrig,
601 pIoCtx->Req.Io.cbXferOrig, pIoCtx);
602
603 pIoCtx->Type.Root.pfnComplete(pIoCtx->Type.Root.pvUser1,
604 pIoCtx->Type.Root.pvUser2,
605 pIoCtx->rcReq);
606}
607
608/**
609 * Initialize the structure members of a given I/O context.
610 */
611DECLINLINE(void) vdIoCtxInit(PVDIOCTX pIoCtx, PVDISK pDisk, VDIOCTXTXDIR enmTxDir,
612 uint64_t uOffset, size_t cbTransfer, PVDIMAGE pImageStart,
613 PCRTSGBUF pcSgBuf, void *pvAllocation,
614 PFNVDIOCTXTRANSFER pfnIoCtxTransfer, uint32_t fFlags)
615{
616 pIoCtx->pDisk = pDisk;
617 pIoCtx->enmTxDir = enmTxDir;
618 pIoCtx->Req.Io.cbTransferLeft = (uint32_t)cbTransfer; Assert((uint32_t)cbTransfer == cbTransfer);
619 pIoCtx->Req.Io.uOffset = uOffset;
620 pIoCtx->Req.Io.cbTransfer = cbTransfer;
621 pIoCtx->Req.Io.pImageStart = pImageStart;
622 pIoCtx->Req.Io.pImageCur = pImageStart;
623 pIoCtx->Req.Io.cbBufClear = 0;
624 pIoCtx->Req.Io.pImageParentOverride = NULL;
625 pIoCtx->Req.Io.uOffsetXferOrig = uOffset;
626 pIoCtx->Req.Io.cbXferOrig = cbTransfer;
627 pIoCtx->cDataTransfersPending = 0;
628 pIoCtx->cMetaTransfersPending = 0;
629 pIoCtx->fComplete = false;
630 pIoCtx->fFlags = fFlags;
631 pIoCtx->pvAllocation = pvAllocation;
632 pIoCtx->pfnIoCtxTransfer = pfnIoCtxTransfer;
633 pIoCtx->pfnIoCtxTransferNext = NULL;
634 pIoCtx->rcReq = VINF_SUCCESS;
635 pIoCtx->pIoCtxParent = NULL;
636
637 /* There is no S/G list for a flush request. */
638 if ( enmTxDir != VDIOCTXTXDIR_FLUSH
639 && enmTxDir != VDIOCTXTXDIR_DISCARD)
640 RTSgBufClone(&pIoCtx->Req.Io.SgBuf, pcSgBuf);
641 else
642 memset(&pIoCtx->Req.Io.SgBuf, 0, sizeof(RTSGBUF));
643}
644
645/**
646 * Internal: Tries to read the desired range from the given cache.
647 *
648 * @returns VBox status code.
649 * @retval VERR_VD_BLOCK_FREE if the block is not in the cache.
650 * pcbRead will be set to the number of bytes not in the cache.
651 * Everything thereafter might be in the cache.
652 * @param pCache The cache to read from.
653 * @param uOffset Offset of the virtual disk to read.
654 * @param cbRead How much to read.
655 * @param pIoCtx The I/O context to read into.
656 * @param pcbRead Where to store the number of bytes actually read.
657 * On success this indicates the number of bytes read from the cache.
658 * If VERR_VD_BLOCK_FREE is returned this gives the number of bytes
659 * which are not in the cache.
660 * In both cases everything beyond this value
661 * might or might not be in the cache.
662 */
663static int vdCacheReadHelper(PVDCACHE pCache, uint64_t uOffset,
664 size_t cbRead, PVDIOCTX pIoCtx, size_t *pcbRead)
665{
666 int rc = VINF_SUCCESS;
667
668 LogFlowFunc(("pCache=%#p uOffset=%llu pIoCtx=%p cbRead=%zu pcbRead=%#p\n",
669 pCache, uOffset, pIoCtx, cbRead, pcbRead));
670
671 AssertPtr(pCache);
672 AssertPtr(pcbRead);
673
674 rc = pCache->Backend->pfnRead(pCache->pBackendData, uOffset, cbRead,
675 pIoCtx, pcbRead);
676
677 LogFlowFunc(("returns rc=%Rrc pcbRead=%zu\n", rc, *pcbRead));
678 return rc;
679}
680
681/**
682 * Internal: Writes data for the given block into the cache.
683 *
684 * @returns VBox status code.
685 * @param pCache The cache to write to.
686 * @param uOffset Offset of the virtual disk to write to the cache.
687 * @param cbWrite How much to write.
688 * @param pIoCtx The I/O context to write from.
689 * @param pcbWritten How much data could be written, optional.
690 */
691static int vdCacheWriteHelper(PVDCACHE pCache, uint64_t uOffset, size_t cbWrite,
692 PVDIOCTX pIoCtx, size_t *pcbWritten)
693{
694 int rc = VINF_SUCCESS;
695
696 LogFlowFunc(("pCache=%#p uOffset=%llu pIoCtx=%p cbWrite=%zu pcbWritten=%#p\n",
697 pCache, uOffset, pIoCtx, cbWrite, pcbWritten));
698
699 AssertPtr(pCache);
700 AssertPtr(pIoCtx);
701 Assert(cbWrite > 0);
702
703 if (pcbWritten)
704 rc = pCache->Backend->pfnWrite(pCache->pBackendData, uOffset, cbWrite,
705 pIoCtx, pcbWritten);
706 else
707 {
708 size_t cbWritten = 0;
709
710 do
711 {
712 rc = pCache->Backend->pfnWrite(pCache->pBackendData, uOffset, cbWrite,
713 pIoCtx, &cbWritten);
714 uOffset += cbWritten;
715 cbWrite -= cbWritten;
716 } while ( cbWrite
717 && ( RT_SUCCESS(rc)
718 || rc == VERR_VD_ASYNC_IO_IN_PROGRESS));
719 }
720
721 LogFlowFunc(("returns rc=%Rrc pcbWritten=%zu\n",
722 rc, pcbWritten ? *pcbWritten : cbWrite));
723 return rc;
724}
725
726/**
727 * Creates a new empty discard state.
728 *
729 * @returns Pointer to the new discard state or NULL if out of memory.
730 */
731static PVDDISCARDSTATE vdDiscardStateCreate(void)
732{
733 PVDDISCARDSTATE pDiscard = (PVDDISCARDSTATE)RTMemAllocZ(sizeof(VDDISCARDSTATE));
734
735 if (pDiscard)
736 {
737 RTListInit(&pDiscard->ListLru);
738 pDiscard->pTreeBlocks = (PAVLRU64TREE)RTMemAllocZ(sizeof(AVLRU64TREE));
739 if (!pDiscard->pTreeBlocks)
740 {
741 RTMemFree(pDiscard);
742 pDiscard = NULL;
743 }
744 }
745
746 return pDiscard;
747}
748
749/**
750 * Removes the least recently used blocks from the waiting list until
751 * the new value is reached.
752 *
753 * @returns VBox status code.
754 * @param pDisk VD disk container.
755 * @param pDiscard The discard state.
756 * @param cbDiscardingNew How many bytes should be waiting on success.
757 * The number of bytes waiting can be less.
758 */
759static int vdDiscardRemoveBlocks(PVDISK pDisk, PVDDISCARDSTATE pDiscard, size_t cbDiscardingNew)
760{
761 int rc = VINF_SUCCESS;
762
763 LogFlowFunc(("pDisk=%#p pDiscard=%#p cbDiscardingNew=%zu\n",
764 pDisk, pDiscard, cbDiscardingNew));
765
766 while (pDiscard->cbDiscarding > cbDiscardingNew)
767 {
768 PVDDISCARDBLOCK pBlock = RTListGetLast(&pDiscard->ListLru, VDDISCARDBLOCK, NodeLru);
769
770 Assert(!RTListIsEmpty(&pDiscard->ListLru));
771
772 /* Go over the allocation bitmap and mark all discarded sectors as unused. */
773 uint64_t offStart = pBlock->Core.Key;
774 uint32_t idxStart = 0;
775 size_t cbLeft = pBlock->cbDiscard;
776 bool fAllocated = ASMBitTest(pBlock->pbmAllocated, idxStart);
777 uint32_t cSectors = (uint32_t)(pBlock->cbDiscard / 512);
778
779 while (cbLeft > 0)
780 {
781 int32_t idxEnd;
782 size_t cbThis = cbLeft;
783
784 if (fAllocated)
785 {
786 /* Check for the first unallocated bit. */
787 idxEnd = ASMBitNextClear(pBlock->pbmAllocated, cSectors, idxStart);
788 if (idxEnd != -1)
789 {
790 cbThis = (idxEnd - idxStart) * 512;
791 fAllocated = false;
792 }
793 }
794 else
795 {
796 /* Mark as unused and check for the first set bit. */
797 idxEnd = ASMBitNextSet(pBlock->pbmAllocated, cSectors, idxStart);
798 if (idxEnd != -1)
799 cbThis = (idxEnd - idxStart) * 512;
800
801
802 VDIOCTX IoCtx;
803 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_DISCARD, 0, 0, NULL,
804 NULL, NULL, NULL, VDIOCTX_FLAGS_SYNC);
805 rc = pDisk->pLast->Backend->pfnDiscard(pDisk->pLast->pBackendData,
806 &IoCtx, offStart, cbThis, NULL,
807 NULL, &cbThis, NULL,
808 VD_DISCARD_MARK_UNUSED);
809 if (RT_FAILURE(rc))
810 break;
811
812 fAllocated = true;
813 }
814
815 idxStart = idxEnd;
816 offStart += cbThis;
817 cbLeft -= cbThis;
818 }
819
820 if (RT_FAILURE(rc))
821 break;
822
823 PVDDISCARDBLOCK pBlockRemove = (PVDDISCARDBLOCK)RTAvlrU64RangeRemove(pDiscard->pTreeBlocks, pBlock->Core.Key);
824 Assert(pBlockRemove == pBlock); NOREF(pBlockRemove);
825 RTListNodeRemove(&pBlock->NodeLru);
826
827 pDiscard->cbDiscarding -= pBlock->cbDiscard;
828 RTMemFree(pBlock->pbmAllocated);
829 RTMemFree(pBlock);
830 }
831
832 Assert(RT_FAILURE(rc) || pDiscard->cbDiscarding <= cbDiscardingNew);
833
834 LogFlowFunc(("returns rc=%Rrc\n", rc));
835 return rc;
836}
837
838/**
839 * Destroys the current discard state, writing any waiting blocks to the image.
840 *
841 * @returns VBox status code.
842 * @param pDisk VD disk container.
843 */
844static int vdDiscardStateDestroy(PVDISK pDisk)
845{
846 int rc = VINF_SUCCESS;
847
848 if (pDisk->pDiscard)
849 {
850 rc = vdDiscardRemoveBlocks(pDisk, pDisk->pDiscard, 0 /* Remove all blocks. */);
851 AssertRC(rc);
852 RTMemFree(pDisk->pDiscard->pTreeBlocks);
853 RTMemFree(pDisk->pDiscard);
854 pDisk->pDiscard = NULL;
855 }
856
857 return rc;
858}
859
860/**
861 * Marks the given range as allocated in the image.
862 * Required if there are discards in progress and a write to a block which can get discarded
863 * is written to.
864 *
865 * @returns VBox status code.
866 * @param pDisk VD container data.
867 * @param uOffset First byte to mark as allocated.
868 * @param cbRange Number of bytes to mark as allocated.
869 */
870static int vdDiscardSetRangeAllocated(PVDISK pDisk, uint64_t uOffset, size_t cbRange)
871{
872 PVDDISCARDSTATE pDiscard = pDisk->pDiscard;
873 int rc = VINF_SUCCESS;
874
875 if (pDiscard)
876 {
877 do
878 {
879 size_t cbThisRange = cbRange;
880 PVDDISCARDBLOCK pBlock = (PVDDISCARDBLOCK)RTAvlrU64RangeGet(pDiscard->pTreeBlocks, uOffset);
881
882 if (pBlock)
883 {
884 int32_t idxStart, idxEnd;
885
886 Assert(!(cbThisRange % 512));
887 Assert(!((uOffset - pBlock->Core.Key) % 512));
888
889 cbThisRange = RT_MIN(cbThisRange, pBlock->Core.KeyLast - uOffset + 1);
890
891 idxStart = (uOffset - pBlock->Core.Key) / 512;
892 idxEnd = idxStart + (int32_t)(cbThisRange / 512);
893 ASMBitSetRange(pBlock->pbmAllocated, idxStart, idxEnd);
894 }
895 else
896 {
897 pBlock = (PVDDISCARDBLOCK)RTAvlrU64GetBestFit(pDiscard->pTreeBlocks, uOffset, true);
898 if (pBlock)
899 cbThisRange = RT_MIN(cbThisRange, pBlock->Core.Key - uOffset);
900 }
901
902 Assert(cbRange >= cbThisRange);
903
904 uOffset += cbThisRange;
905 cbRange -= cbThisRange;
906 } while (cbRange != 0);
907 }
908
909 return rc;
910}
911
912DECLINLINE(PVDIOCTX) vdIoCtxAlloc(PVDISK pDisk, VDIOCTXTXDIR enmTxDir,
913 uint64_t uOffset, size_t cbTransfer,
914 PVDIMAGE pImageStart,PCRTSGBUF pcSgBuf,
915 void *pvAllocation, PFNVDIOCTXTRANSFER pfnIoCtxTransfer,
916 uint32_t fFlags)
917{
918 PVDIOCTX pIoCtx = NULL;
919
920 pIoCtx = (PVDIOCTX)RTMemCacheAlloc(pDisk->hMemCacheIoCtx);
921 if (RT_LIKELY(pIoCtx))
922 {
923 vdIoCtxInit(pIoCtx, pDisk, enmTxDir, uOffset, cbTransfer, pImageStart,
924 pcSgBuf, pvAllocation, pfnIoCtxTransfer, fFlags);
925 }
926
927 return pIoCtx;
928}
929
930DECLINLINE(PVDIOCTX) vdIoCtxRootAlloc(PVDISK pDisk, VDIOCTXTXDIR enmTxDir,
931 uint64_t uOffset, size_t cbTransfer,
932 PVDIMAGE pImageStart, PCRTSGBUF pcSgBuf,
933 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
934 void *pvUser1, void *pvUser2,
935 void *pvAllocation,
936 PFNVDIOCTXTRANSFER pfnIoCtxTransfer,
937 uint32_t fFlags)
938{
939 PVDIOCTX pIoCtx = vdIoCtxAlloc(pDisk, enmTxDir, uOffset, cbTransfer, pImageStart,
940 pcSgBuf, pvAllocation, pfnIoCtxTransfer, fFlags);
941
942 if (RT_LIKELY(pIoCtx))
943 {
944 pIoCtx->pIoCtxParent = NULL;
945 pIoCtx->Type.Root.pfnComplete = pfnComplete;
946 pIoCtx->Type.Root.pvUser1 = pvUser1;
947 pIoCtx->Type.Root.pvUser2 = pvUser2;
948 }
949
950 LogFlow(("Allocated root I/O context %#p\n", pIoCtx));
951 return pIoCtx;
952}
953
954DECLINLINE(void) vdIoCtxDiscardInit(PVDIOCTX pIoCtx, PVDISK pDisk, PCRTRANGE paRanges,
955 unsigned cRanges, PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
956 void *pvUser1, void *pvUser2, void *pvAllocation,
957 PFNVDIOCTXTRANSFER pfnIoCtxTransfer, uint32_t fFlags)
958{
959 pIoCtx->pIoCtxNext = NULL;
960 pIoCtx->pDisk = pDisk;
961 pIoCtx->enmTxDir = VDIOCTXTXDIR_DISCARD;
962 pIoCtx->cDataTransfersPending = 0;
963 pIoCtx->cMetaTransfersPending = 0;
964 pIoCtx->fComplete = false;
965 pIoCtx->fFlags = fFlags;
966 pIoCtx->pvAllocation = pvAllocation;
967 pIoCtx->pfnIoCtxTransfer = pfnIoCtxTransfer;
968 pIoCtx->pfnIoCtxTransferNext = NULL;
969 pIoCtx->rcReq = VINF_SUCCESS;
970 pIoCtx->Req.Discard.paRanges = paRanges;
971 pIoCtx->Req.Discard.cRanges = cRanges;
972 pIoCtx->Req.Discard.idxRange = 0;
973 pIoCtx->Req.Discard.cbDiscardLeft = 0;
974 pIoCtx->Req.Discard.offCur = 0;
975 pIoCtx->Req.Discard.cbThisDiscard = 0;
976
977 pIoCtx->pIoCtxParent = NULL;
978 pIoCtx->Type.Root.pfnComplete = pfnComplete;
979 pIoCtx->Type.Root.pvUser1 = pvUser1;
980 pIoCtx->Type.Root.pvUser2 = pvUser2;
981}
982
983DECLINLINE(PVDIOCTX) vdIoCtxDiscardAlloc(PVDISK pDisk, PCRTRANGE paRanges,
984 unsigned cRanges,
985 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
986 void *pvUser1, void *pvUser2,
987 void *pvAllocation,
988 PFNVDIOCTXTRANSFER pfnIoCtxTransfer,
989 uint32_t fFlags)
990{
991 PVDIOCTX pIoCtx = NULL;
992
993 pIoCtx = (PVDIOCTX)RTMemCacheAlloc(pDisk->hMemCacheIoCtx);
994 if (RT_LIKELY(pIoCtx))
995 {
996 vdIoCtxDiscardInit(pIoCtx, pDisk, paRanges, cRanges, pfnComplete, pvUser1,
997 pvUser2, pvAllocation, pfnIoCtxTransfer, fFlags);
998 }
999
1000 LogFlow(("Allocated discard I/O context %#p\n", pIoCtx));
1001 return pIoCtx;
1002}
1003
1004DECLINLINE(PVDIOCTX) vdIoCtxChildAlloc(PVDISK pDisk, VDIOCTXTXDIR enmTxDir,
1005 uint64_t uOffset, size_t cbTransfer,
1006 PVDIMAGE pImageStart, PCRTSGBUF pcSgBuf,
1007 PVDIOCTX pIoCtxParent, size_t cbTransferParent,
1008 size_t cbWriteParent, void *pvAllocation,
1009 PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
1010{
1011 PVDIOCTX pIoCtx = vdIoCtxAlloc(pDisk, enmTxDir, uOffset, cbTransfer, pImageStart,
1012 pcSgBuf, pvAllocation, pfnIoCtxTransfer, pIoCtxParent->fFlags & ~VDIOCTX_FLAGS_DONT_FREE);
1013
1014 AssertPtr(pIoCtxParent);
1015 Assert(!pIoCtxParent->pIoCtxParent);
1016
1017 if (RT_LIKELY(pIoCtx))
1018 {
1019 pIoCtx->pIoCtxParent = pIoCtxParent;
1020 pIoCtx->Type.Child.uOffsetSaved = uOffset;
1021 pIoCtx->Type.Child.cbTransferLeftSaved = cbTransfer;
1022 pIoCtx->Type.Child.cbTransferParent = cbTransferParent;
1023 pIoCtx->Type.Child.cbWriteParent = cbWriteParent;
1024 }
1025
1026 LogFlow(("Allocated child I/O context %#p\n", pIoCtx));
1027 return pIoCtx;
1028}
1029
1030DECLINLINE(PVDIOTASK) vdIoTaskUserAlloc(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser, PVDIOCTX pIoCtx, uint32_t cbTransfer)
1031{
1032 PVDIOTASK pIoTask = NULL;
1033
1034 pIoTask = (PVDIOTASK)RTMemCacheAlloc(pIoStorage->pVDIo->pDisk->hMemCacheIoTask);
1035 if (pIoTask)
1036 {
1037 pIoTask->pIoStorage = pIoStorage;
1038 pIoTask->pfnComplete = pfnComplete;
1039 pIoTask->pvUser = pvUser;
1040 pIoTask->fMeta = false;
1041 pIoTask->Type.User.cbTransfer = cbTransfer;
1042 pIoTask->Type.User.pIoCtx = pIoCtx;
1043 }
1044
1045 return pIoTask;
1046}
1047
1048DECLINLINE(PVDIOTASK) vdIoTaskMetaAlloc(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser, PVDMETAXFER pMetaXfer)
1049{
1050 PVDIOTASK pIoTask = NULL;
1051
1052 pIoTask = (PVDIOTASK)RTMemCacheAlloc(pIoStorage->pVDIo->pDisk->hMemCacheIoTask);
1053 if (pIoTask)
1054 {
1055 pIoTask->pIoStorage = pIoStorage;
1056 pIoTask->pfnComplete = pfnComplete;
1057 pIoTask->pvUser = pvUser;
1058 pIoTask->fMeta = true;
1059 pIoTask->Type.Meta.pMetaXfer = pMetaXfer;
1060 }
1061
1062 return pIoTask;
1063}
1064
1065DECLINLINE(void) vdIoCtxFree(PVDISK pDisk, PVDIOCTX pIoCtx)
1066{
1067 Log(("Freeing I/O context %#p\n", pIoCtx));
1068
1069 if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_DONT_FREE))
1070 {
1071 if (pIoCtx->pvAllocation)
1072 RTMemFree(pIoCtx->pvAllocation);
1073#ifdef DEBUG
1074 memset(&pIoCtx->pDisk, 0xff, sizeof(void *));
1075#endif
1076 RTMemCacheFree(pDisk->hMemCacheIoCtx, pIoCtx);
1077 }
1078}
1079
1080DECLINLINE(void) vdIoTaskFree(PVDISK pDisk, PVDIOTASK pIoTask)
1081{
1082#ifdef DEBUG
1083 memset(pIoTask, 0xff, sizeof(VDIOTASK));
1084#endif
1085 RTMemCacheFree(pDisk->hMemCacheIoTask, pIoTask);
1086}
1087
1088DECLINLINE(void) vdIoCtxChildReset(PVDIOCTX pIoCtx)
1089{
1090 AssertPtr(pIoCtx->pIoCtxParent);
1091
1092 RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
1093 pIoCtx->Req.Io.uOffset = pIoCtx->Type.Child.uOffsetSaved;
1094 pIoCtx->Req.Io.cbTransferLeft = (uint32_t)pIoCtx->Type.Child.cbTransferLeftSaved;
1095 Assert((uint32_t)pIoCtx->Type.Child.cbTransferLeftSaved == pIoCtx->Type.Child.cbTransferLeftSaved);
1096}
1097
1098DECLINLINE(PVDMETAXFER) vdMetaXferAlloc(PVDIOSTORAGE pIoStorage, uint64_t uOffset, size_t cb)
1099{
1100 PVDMETAXFER pMetaXfer = (PVDMETAXFER)RTMemAlloc(RT_OFFSETOF(VDMETAXFER, abData[cb]));
1101
1102 if (RT_LIKELY(pMetaXfer))
1103 {
1104 pMetaXfer->Core.Key = uOffset;
1105 pMetaXfer->Core.KeyLast = uOffset + cb - 1;
1106 pMetaXfer->fFlags = VDMETAXFER_TXDIR_NONE;
1107 pMetaXfer->cbMeta = cb;
1108 pMetaXfer->pIoStorage = pIoStorage;
1109 pMetaXfer->cRefs = 0;
1110 pMetaXfer->pbDataShw = NULL;
1111 RTListInit(&pMetaXfer->ListIoCtxWaiting);
1112 RTListInit(&pMetaXfer->ListIoCtxShwWrites);
1113 }
1114 return pMetaXfer;
1115}
1116
1117DECLINLINE(void) vdIoCtxAddToWaitingList(volatile PVDIOCTX *ppList, PVDIOCTX pIoCtx)
1118{
1119 /* Put it on the waiting list. */
1120 PVDIOCTX pNext = ASMAtomicUoReadPtrT(ppList, PVDIOCTX);
1121 PVDIOCTX pHeadOld;
1122 pIoCtx->pIoCtxNext = pNext;
1123 while (!ASMAtomicCmpXchgExPtr(ppList, pIoCtx, pNext, &pHeadOld))
1124 {
1125 pNext = pHeadOld;
1126 Assert(pNext != pIoCtx);
1127 pIoCtx->pIoCtxNext = pNext;
1128 ASMNopPause();
1129 }
1130}
1131
1132DECLINLINE(void) vdIoCtxDefer(PVDISK pDisk, PVDIOCTX pIoCtx)
1133{
1134 LogFlowFunc(("Deferring I/O context pIoCtx=%#p\n", pIoCtx));
1135
1136 Assert(!pIoCtx->pIoCtxParent && !(pIoCtx->fFlags & VDIOCTX_FLAGS_BLOCKED));
1137 pIoCtx->fFlags |= VDIOCTX_FLAGS_BLOCKED;
1138 vdIoCtxAddToWaitingList(&pDisk->pIoCtxBlockedHead, pIoCtx);
1139}
1140
1141static size_t vdIoCtxCopy(PVDIOCTX pIoCtxDst, PVDIOCTX pIoCtxSrc, size_t cbData)
1142{
1143 return RTSgBufCopy(&pIoCtxDst->Req.Io.SgBuf, &pIoCtxSrc->Req.Io.SgBuf, cbData);
1144}
1145
1146#if 0 /* unused */
1147static int vdIoCtxCmp(PVDIOCTX pIoCtx1, PVDIOCTX pIoCtx2, size_t cbData)
1148{
1149 return RTSgBufCmp(&pIoCtx1->Req.Io.SgBuf, &pIoCtx2->Req.Io.SgBuf, cbData);
1150}
1151#endif
1152
1153static size_t vdIoCtxCopyTo(PVDIOCTX pIoCtx, const uint8_t *pbData, size_t cbData)
1154{
1155 return RTSgBufCopyFromBuf(&pIoCtx->Req.Io.SgBuf, pbData, cbData);
1156}
1157
1158static size_t vdIoCtxCopyFrom(PVDIOCTX pIoCtx, uint8_t *pbData, size_t cbData)
1159{
1160 return RTSgBufCopyToBuf(&pIoCtx->Req.Io.SgBuf, pbData, cbData);
1161}
1162
1163static size_t vdIoCtxSet(PVDIOCTX pIoCtx, uint8_t ch, size_t cbData)
1164{
1165 return RTSgBufSet(&pIoCtx->Req.Io.SgBuf, ch, cbData);
1166}
1167
1168/**
1169 * Returns whether the given I/O context has completed.
1170 *
1171 * @returns Flag whether the I/O context is complete.
1172 * @param pIoCtx The I/O context to check.
1173 */
1174DECLINLINE(bool) vdIoCtxIsComplete(PVDIOCTX pIoCtx)
1175{
1176 if ( !pIoCtx->cMetaTransfersPending
1177 && !pIoCtx->cDataTransfersPending
1178 && !pIoCtx->pfnIoCtxTransfer)
1179 return true;
1180
1181 /*
1182 * We complete the I/O context in case of an error
1183 * if there is no I/O task pending.
1184 */
1185 if ( RT_FAILURE(pIoCtx->rcReq)
1186 && !pIoCtx->cMetaTransfersPending
1187 && !pIoCtx->cDataTransfersPending)
1188 return true;
1189
1190 return false;
1191}
1192
1193/**
1194 * Returns whether the given I/O context is blocked due to a metadata transfer
1195 * or because the backend blocked it.
1196 *
1197 * @returns Flag whether the I/O context is blocked.
1198 * @param pIoCtx The I/O context to check.
1199 */
1200DECLINLINE(bool) vdIoCtxIsBlocked(PVDIOCTX pIoCtx)
1201{
1202 /* Don't change anything if there is a metadata transfer pending or we are blocked. */
1203 if ( pIoCtx->cMetaTransfersPending
1204 || (pIoCtx->fFlags & VDIOCTX_FLAGS_BLOCKED))
1205 return true;
1206
1207 return false;
1208}
1209
1210/**
1211 * Process the I/O context, core method which assumes that the I/O context
1212 * acquired the lock.
1213 *
1214 * @returns VBox status code.
1215 * @param pIoCtx I/O context to process.
1216 */
1217static int vdIoCtxProcessLocked(PVDIOCTX pIoCtx)
1218{
1219 int rc = VINF_SUCCESS;
1220
1221 VD_IS_LOCKED(pIoCtx->pDisk);
1222
1223 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
1224
1225 if (!vdIoCtxIsComplete(pIoCtx))
1226 {
1227 if (!vdIoCtxIsBlocked(pIoCtx))
1228 {
1229 if (pIoCtx->pfnIoCtxTransfer)
1230 {
1231 /* Call the transfer function advancing to the next while there is no error. */
1232 while ( pIoCtx->pfnIoCtxTransfer
1233 && !pIoCtx->cMetaTransfersPending
1234 && RT_SUCCESS(rc))
1235 {
1236 LogFlowFunc(("calling transfer function %#p\n", pIoCtx->pfnIoCtxTransfer));
1237 rc = pIoCtx->pfnIoCtxTransfer(pIoCtx);
1238
1239 /* Advance to the next part of the transfer if the current one succeeded. */
1240 if (RT_SUCCESS(rc))
1241 {
1242 pIoCtx->pfnIoCtxTransfer = pIoCtx->pfnIoCtxTransferNext;
1243 pIoCtx->pfnIoCtxTransferNext = NULL;
1244 }
1245 }
1246 }
1247
1248 if ( RT_SUCCESS(rc)
1249 && !pIoCtx->cMetaTransfersPending
1250 && !pIoCtx->cDataTransfersPending
1251 && !(pIoCtx->fFlags & VDIOCTX_FLAGS_BLOCKED))
1252 rc = VINF_VD_ASYNC_IO_FINISHED;
1253 else if ( RT_SUCCESS(rc)
1254 || rc == VERR_VD_NOT_ENOUGH_METADATA
1255 || rc == VERR_VD_IOCTX_HALT)
1256 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1257 else if ( RT_FAILURE(rc)
1258 && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
1259 {
1260 ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rc, VINF_SUCCESS);
1261
1262 /*
1263 * The I/O context completed if we have an error and there is no data
1264 * or meta data transfer pending.
1265 */
1266 if ( !pIoCtx->cMetaTransfersPending
1267 && !pIoCtx->cDataTransfersPending)
1268 rc = VINF_VD_ASYNC_IO_FINISHED;
1269 else
1270 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1271 }
1272 }
1273 else
1274 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1275 }
1276 else
1277 rc = VINF_VD_ASYNC_IO_FINISHED;
1278
1279 LogFlowFunc(("pIoCtx=%#p rc=%Rrc cDataTransfersPending=%u cMetaTransfersPending=%u fComplete=%RTbool\n",
1280 pIoCtx, rc, pIoCtx->cDataTransfersPending, pIoCtx->cMetaTransfersPending,
1281 pIoCtx->fComplete));
1282
1283 return rc;
1284}
1285
1286/**
1287 * Processes the list of waiting I/O contexts.
1288 *
1289 * @returns VBox status code, only valid if pIoCtxRc is not NULL, treat as void
1290 * function otherwise.
1291 * @param pDisk The disk structure.
1292 * @param pIoCtxRc An I/O context handle which waits on the list. When processed
1293 * The status code is returned. NULL if there is no I/O context
1294 * to return the status code for.
1295 */
1296static int vdDiskProcessWaitingIoCtx(PVDISK pDisk, PVDIOCTX pIoCtxRc)
1297{
1298 int rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1299
1300 LogFlowFunc(("pDisk=%#p pIoCtxRc=%#p\n", pDisk, pIoCtxRc));
1301
1302 VD_IS_LOCKED(pDisk);
1303
1304 /* Get the waiting list and process it in FIFO order. */
1305 PVDIOCTX pIoCtxHead = ASMAtomicXchgPtrT(&pDisk->pIoCtxHead, NULL, PVDIOCTX);
1306
1307 /* Reverse it. */
1308 PVDIOCTX pCur = pIoCtxHead;
1309 pIoCtxHead = NULL;
1310 while (pCur)
1311 {
1312 PVDIOCTX pInsert = pCur;
1313 pCur = pCur->pIoCtxNext;
1314 pInsert->pIoCtxNext = pIoCtxHead;
1315 pIoCtxHead = pInsert;
1316 }
1317
1318 /* Process now. */
1319 pCur = pIoCtxHead;
1320 while (pCur)
1321 {
1322 int rcTmp;
1323 PVDIOCTX pTmp = pCur;
1324
1325 pCur = pCur->pIoCtxNext;
1326 pTmp->pIoCtxNext = NULL;
1327
1328 /*
1329 * Need to clear the sync flag here if there is a new I/O context
1330 * with it set and the context is not given in pIoCtxRc.
1331 * This happens most likely on a different thread and that one shouldn't
1332 * process the context synchronously.
1333 *
1334 * The thread who issued the context will wait on the event semaphore
1335 * anyway which is signalled when the completion handler is called.
1336 */
1337 if ( pTmp->fFlags & VDIOCTX_FLAGS_SYNC
1338 && pTmp != pIoCtxRc)
1339 pTmp->fFlags &= ~VDIOCTX_FLAGS_SYNC;
1340
1341 rcTmp = vdIoCtxProcessLocked(pTmp);
1342 if (pTmp == pIoCtxRc)
1343 {
1344 if ( rcTmp == VINF_VD_ASYNC_IO_FINISHED
1345 && RT_SUCCESS(pTmp->rcReq)
1346 && pTmp->enmTxDir == VDIOCTXTXDIR_READ)
1347 {
1348 int rc2 = vdFilterChainApplyRead(pDisk, pTmp->Req.Io.uOffsetXferOrig,
1349 pTmp->Req.Io.cbXferOrig, pTmp);
1350 if (RT_FAILURE(rc2))
1351 rcTmp = rc2;
1352 }
1353
1354 /* The given I/O context was processed, pass the return code to the caller. */
1355 if ( rcTmp == VINF_VD_ASYNC_IO_FINISHED
1356 && (pTmp->fFlags & VDIOCTX_FLAGS_SYNC))
1357 rc = pTmp->rcReq;
1358 else
1359 rc = rcTmp;
1360 }
1361 else if ( rcTmp == VINF_VD_ASYNC_IO_FINISHED
1362 && ASMAtomicCmpXchgBool(&pTmp->fComplete, true, false))
1363 {
1364 LogFlowFunc(("Waiting I/O context completed pTmp=%#p\n", pTmp));
1365 vdThreadFinishWrite(pDisk);
1366 vdIoCtxRootComplete(pDisk, pTmp);
1367 vdIoCtxFree(pDisk, pTmp);
1368 }
1369 }
1370
1371 LogFlowFunc(("returns rc=%Rrc\n", rc));
1372 return rc;
1373}
1374
1375/**
1376 * Processes the list of blocked I/O contexts.
1377 *
1378 * @returns nothing.
1379 * @param pDisk The disk structure.
1380 */
1381static void vdDiskProcessBlockedIoCtx(PVDISK pDisk)
1382{
1383 LogFlowFunc(("pDisk=%#p\n", pDisk));
1384
1385 VD_IS_LOCKED(pDisk);
1386
1387 /* Get the waiting list and process it in FIFO order. */
1388 PVDIOCTX pIoCtxHead = ASMAtomicXchgPtrT(&pDisk->pIoCtxBlockedHead, NULL, PVDIOCTX);
1389
1390 /* Reverse it. */
1391 PVDIOCTX pCur = pIoCtxHead;
1392 pIoCtxHead = NULL;
1393 while (pCur)
1394 {
1395 PVDIOCTX pInsert = pCur;
1396 pCur = pCur->pIoCtxNext;
1397 pInsert->pIoCtxNext = pIoCtxHead;
1398 pIoCtxHead = pInsert;
1399 }
1400
1401 /* Process now. */
1402 pCur = pIoCtxHead;
1403 while (pCur)
1404 {
1405 int rc;
1406 PVDIOCTX pTmp = pCur;
1407
1408 pCur = pCur->pIoCtxNext;
1409 pTmp->pIoCtxNext = NULL;
1410
1411 Assert(!pTmp->pIoCtxParent);
1412 Assert(pTmp->fFlags & VDIOCTX_FLAGS_BLOCKED);
1413 pTmp->fFlags &= ~VDIOCTX_FLAGS_BLOCKED;
1414
1415 rc = vdIoCtxProcessLocked(pTmp);
1416 if ( rc == VINF_VD_ASYNC_IO_FINISHED
1417 && ASMAtomicCmpXchgBool(&pTmp->fComplete, true, false))
1418 {
1419 LogFlowFunc(("Waiting I/O context completed pTmp=%#p\n", pTmp));
1420 vdThreadFinishWrite(pDisk);
1421 vdIoCtxRootComplete(pDisk, pTmp);
1422 vdIoCtxFree(pDisk, pTmp);
1423 }
1424 }
1425
1426 LogFlowFunc(("returns\n"));
1427}
1428
1429/**
1430 * Processes the I/O context trying to lock the criticial section.
1431 * The context is deferred if the critical section is busy.
1432 *
1433 * @returns VBox status code.
1434 * @param pIoCtx The I/O context to process.
1435 */
1436static int vdIoCtxProcessTryLockDefer(PVDIOCTX pIoCtx)
1437{
1438 int rc = VINF_SUCCESS;
1439 PVDISK pDisk = pIoCtx->pDisk;
1440
1441 Log(("Defer pIoCtx=%#p\n", pIoCtx));
1442
1443 /* Put it on the waiting list first. */
1444 vdIoCtxAddToWaitingList(&pDisk->pIoCtxHead, pIoCtx);
1445
1446 if (ASMAtomicCmpXchgBool(&pDisk->fLocked, true, false))
1447 {
1448 /* Leave it again, the context will be processed just before leaving the lock. */
1449 LogFlowFunc(("Successfully acquired the lock\n"));
1450 rc = vdDiskUnlock(pDisk, pIoCtx);
1451 }
1452 else
1453 {
1454 LogFlowFunc(("Lock is held\n"));
1455 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1456 }
1457
1458 return rc;
1459}
1460
1461/**
1462 * Process the I/O context in a synchronous manner, waiting
1463 * for it to complete.
1464 *
1465 * @returns VBox status code of the completed request.
1466 * @param pIoCtx The sync I/O context.
1467 * @param hEventComplete Event sempahore to wait on for completion.
1468 */
1469static int vdIoCtxProcessSync(PVDIOCTX pIoCtx, RTSEMEVENT hEventComplete)
1470{
1471 int rc = VINF_SUCCESS;
1472 PVDISK pDisk = pIoCtx->pDisk;
1473
1474 LogFlowFunc(("pIoCtx=%p\n", pIoCtx));
1475
1476 AssertMsg(pIoCtx->fFlags & (VDIOCTX_FLAGS_SYNC | VDIOCTX_FLAGS_DONT_FREE),
1477 ("I/O context is not marked as synchronous\n"));
1478
1479 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
1480 if (rc == VINF_VD_ASYNC_IO_FINISHED)
1481 rc = VINF_SUCCESS;
1482
1483 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1484 {
1485 rc = RTSemEventWait(hEventComplete, RT_INDEFINITE_WAIT);
1486 AssertRC(rc);
1487 }
1488
1489 rc = pIoCtx->rcReq;
1490 vdIoCtxFree(pDisk, pIoCtx);
1491
1492 return rc;
1493}
1494
1495DECLINLINE(bool) vdIoCtxIsDiskLockOwner(PVDISK pDisk, PVDIOCTX pIoCtx)
1496{
1497 return pDisk->pIoCtxLockOwner == pIoCtx;
1498}
1499
1500static int vdIoCtxLockDisk(PVDISK pDisk, PVDIOCTX pIoCtx)
1501{
1502 int rc = VINF_SUCCESS;
1503
1504 VD_IS_LOCKED(pDisk);
1505
1506 LogFlowFunc(("pDisk=%#p pIoCtx=%#p\n", pDisk, pIoCtx));
1507
1508 if (!ASMAtomicCmpXchgPtr(&pDisk->pIoCtxLockOwner, pIoCtx, NIL_VDIOCTX))
1509 {
1510 Assert(pDisk->pIoCtxLockOwner != pIoCtx); /* No nesting allowed. */
1511 vdIoCtxDefer(pDisk, pIoCtx);
1512 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1513 }
1514
1515 LogFlowFunc(("returns -> %Rrc\n", rc));
1516 return rc;
1517}
1518
1519static void vdIoCtxUnlockDisk(PVDISK pDisk, PVDIOCTX pIoCtx, bool fProcessBlockedReqs)
1520{
1521 RT_NOREF1(pIoCtx);
1522 LogFlowFunc(("pDisk=%#p pIoCtx=%#p fProcessBlockedReqs=%RTbool\n",
1523 pDisk, pIoCtx, fProcessBlockedReqs));
1524
1525 VD_IS_LOCKED(pDisk);
1526
1527 LogFlow(("Unlocking disk lock owner is %#p\n", pDisk->pIoCtxLockOwner));
1528 Assert(pDisk->pIoCtxLockOwner == pIoCtx);
1529 ASMAtomicXchgPtrT(&pDisk->pIoCtxLockOwner, NIL_VDIOCTX, PVDIOCTX);
1530
1531 if (fProcessBlockedReqs)
1532 {
1533 /* Process any blocked writes if the current request didn't caused another growing. */
1534 vdDiskProcessBlockedIoCtx(pDisk);
1535 }
1536
1537 LogFlowFunc(("returns\n"));
1538}
1539
1540/**
1541 * Internal: Reads a given amount of data from the image chain of the disk.
1542 **/
1543static int vdDiskReadHelper(PVDISK pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
1544 uint64_t uOffset, size_t cbRead, PVDIOCTX pIoCtx, size_t *pcbThisRead)
1545{
1546 RT_NOREF1(pDisk);
1547 int rc = VINF_SUCCESS;
1548 size_t cbThisRead = cbRead;
1549
1550 AssertPtr(pcbThisRead);
1551
1552 *pcbThisRead = 0;
1553
1554 /*
1555 * Try to read from the given image.
1556 * If the block is not allocated read from override chain if present.
1557 */
1558 rc = pImage->Backend->pfnRead(pImage->pBackendData,
1559 uOffset, cbThisRead, pIoCtx,
1560 &cbThisRead);
1561
1562 if (rc == VERR_VD_BLOCK_FREE)
1563 {
1564 for (PVDIMAGE pCurrImage = pImageParentOverride ? pImageParentOverride : pImage->pPrev;
1565 pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
1566 pCurrImage = pCurrImage->pPrev)
1567 {
1568 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
1569 uOffset, cbThisRead, pIoCtx,
1570 &cbThisRead);
1571 }
1572 }
1573
1574 if (RT_SUCCESS(rc) || rc == VERR_VD_BLOCK_FREE)
1575 *pcbThisRead = cbThisRead;
1576
1577 return rc;
1578}
1579
1580/**
1581 * internal: read the specified amount of data in whatever blocks the backend
1582 * will give us - async version.
1583 */
1584static DECLCALLBACK(int) vdReadHelperAsync(PVDIOCTX pIoCtx)
1585{
1586 int rc;
1587 PVDISK pDisk = pIoCtx->pDisk;
1588 size_t cbToRead = pIoCtx->Req.Io.cbTransfer;
1589 uint64_t uOffset = pIoCtx->Req.Io.uOffset;
1590 PVDIMAGE pCurrImage = pIoCtx->Req.Io.pImageCur;
1591 PVDIMAGE pImageParentOverride = pIoCtx->Req.Io.pImageParentOverride;
1592 unsigned cImagesRead = pIoCtx->Req.Io.cImagesRead;
1593 size_t cbThisRead;
1594
1595 /*
1596 * Check whether there is a full block write in progress which was not allocated.
1597 * Defer I/O if the range interferes but only if it does not belong to the
1598 * write doing the allocation.
1599 */
1600 if ( pDisk->pIoCtxLockOwner != NIL_VDIOCTX
1601 && uOffset >= pDisk->uOffsetStartLocked
1602 && uOffset < pDisk->uOffsetEndLocked
1603 && ( !pIoCtx->pIoCtxParent
1604 || pIoCtx->pIoCtxParent != pDisk->pIoCtxLockOwner))
1605 {
1606 Log(("Interferring read while allocating a new block => deferring read\n"));
1607 vdIoCtxDefer(pDisk, pIoCtx);
1608 return VERR_VD_ASYNC_IO_IN_PROGRESS;
1609 }
1610
1611 /* Loop until all reads started or we have a backend which needs to read metadata. */
1612 do
1613 {
1614 /* Search for image with allocated block. Do not attempt to read more
1615 * than the previous reads marked as valid. Otherwise this would return
1616 * stale data when different block sizes are used for the images. */
1617 cbThisRead = cbToRead;
1618
1619 if ( pDisk->pCache
1620 && !pImageParentOverride)
1621 {
1622 rc = vdCacheReadHelper(pDisk->pCache, uOffset, cbThisRead,
1623 pIoCtx, &cbThisRead);
1624 if (rc == VERR_VD_BLOCK_FREE)
1625 {
1626 rc = vdDiskReadHelper(pDisk, pCurrImage, NULL, uOffset, cbThisRead,
1627 pIoCtx, &cbThisRead);
1628
1629 /* If the read was successful, write the data back into the cache. */
1630 if ( RT_SUCCESS(rc)
1631 && pIoCtx->fFlags & VDIOCTX_FLAGS_READ_UPDATE_CACHE)
1632 {
1633 rc = vdCacheWriteHelper(pDisk->pCache, uOffset, cbThisRead,
1634 pIoCtx, NULL);
1635 }
1636 }
1637 }
1638 else
1639 {
1640 /*
1641 * Try to read from the given image.
1642 * If the block is not allocated read from override chain if present.
1643 */
1644 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
1645 uOffset, cbThisRead, pIoCtx,
1646 &cbThisRead);
1647
1648 if ( rc == VERR_VD_BLOCK_FREE
1649 && cImagesRead != 1)
1650 {
1651 unsigned cImagesToProcess = cImagesRead;
1652
1653 pCurrImage = pImageParentOverride ? pImageParentOverride : pCurrImage->pPrev;
1654 pIoCtx->Req.Io.pImageParentOverride = NULL;
1655
1656 while (pCurrImage && rc == VERR_VD_BLOCK_FREE)
1657 {
1658 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
1659 uOffset, cbThisRead,
1660 pIoCtx, &cbThisRead);
1661 if (cImagesToProcess == 1)
1662 break;
1663 else if (cImagesToProcess > 0)
1664 cImagesToProcess--;
1665
1666 if (rc == VERR_VD_BLOCK_FREE)
1667 pCurrImage = pCurrImage->pPrev;
1668 }
1669 }
1670 }
1671
1672 /* The task state will be updated on success already, don't do it here!. */
1673 if (rc == VERR_VD_BLOCK_FREE)
1674 {
1675 /* No image in the chain contains the data for the block. */
1676 ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, (uint32_t)cbThisRead); Assert(cbThisRead == (uint32_t)cbThisRead);
1677
1678 /* Fill the free space with 0 if we are told to do so
1679 * or a previous read returned valid data. */
1680 if (pIoCtx->fFlags & VDIOCTX_FLAGS_ZERO_FREE_BLOCKS)
1681 vdIoCtxSet(pIoCtx, '\0', cbThisRead);
1682 else
1683 pIoCtx->Req.Io.cbBufClear += cbThisRead;
1684
1685 if (pIoCtx->Req.Io.pImageCur->uOpenFlags & VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS)
1686 rc = VINF_VD_NEW_ZEROED_BLOCK;
1687 else
1688 rc = VINF_SUCCESS;
1689 }
1690 else if (rc == VERR_VD_IOCTX_HALT)
1691 {
1692 uOffset += cbThisRead;
1693 cbToRead -= cbThisRead;
1694 pIoCtx->fFlags |= VDIOCTX_FLAGS_BLOCKED;
1695 }
1696 else if ( RT_SUCCESS(rc)
1697 || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1698 {
1699 /* First not free block, fill the space before with 0. */
1700 if ( pIoCtx->Req.Io.cbBufClear
1701 && !(pIoCtx->fFlags & VDIOCTX_FLAGS_ZERO_FREE_BLOCKS))
1702 {
1703 RTSGBUF SgBuf;
1704 RTSgBufClone(&SgBuf, &pIoCtx->Req.Io.SgBuf);
1705 RTSgBufReset(&SgBuf);
1706 RTSgBufSet(&SgBuf, 0, pIoCtx->Req.Io.cbBufClear);
1707 pIoCtx->Req.Io.cbBufClear = 0;
1708 pIoCtx->fFlags |= VDIOCTX_FLAGS_ZERO_FREE_BLOCKS;
1709 }
1710 rc = VINF_SUCCESS;
1711 }
1712
1713 if (RT_FAILURE(rc))
1714 break;
1715
1716 cbToRead -= cbThisRead;
1717 uOffset += cbThisRead;
1718 pCurrImage = pIoCtx->Req.Io.pImageStart; /* Start with the highest image in the chain. */
1719 } while (cbToRead != 0 && RT_SUCCESS(rc));
1720
1721 if ( rc == VERR_VD_NOT_ENOUGH_METADATA
1722 || rc == VERR_VD_IOCTX_HALT)
1723 {
1724 /* Save the current state. */
1725 pIoCtx->Req.Io.uOffset = uOffset;
1726 pIoCtx->Req.Io.cbTransfer = cbToRead;
1727 pIoCtx->Req.Io.pImageCur = pCurrImage ? pCurrImage : pIoCtx->Req.Io.pImageStart;
1728 }
1729
1730 return (!(pIoCtx->fFlags & VDIOCTX_FLAGS_ZERO_FREE_BLOCKS))
1731 ? VERR_VD_BLOCK_FREE
1732 : rc;
1733}
1734
1735/**
1736 * internal: parent image read wrapper for compacting.
1737 */
1738static DECLCALLBACK(int) vdParentRead(void *pvUser, uint64_t uOffset, void *pvBuf,
1739 size_t cbRead)
1740{
1741 PVDPARENTSTATEDESC pParentState = (PVDPARENTSTATEDESC)pvUser;
1742
1743 /** @todo
1744 * Only used for compaction so far which is not possible to mix with async I/O.
1745 * Needs to be changed if we want to support online compaction of images.
1746 */
1747 bool fLocked = ASMAtomicXchgBool(&pParentState->pDisk->fLocked, true);
1748 AssertMsgReturn(!fLocked,
1749 ("Calling synchronous parent read while another thread holds the disk lock\n"),
1750 VERR_VD_INVALID_STATE);
1751
1752 /* Fake an I/O context. */
1753 RTSGSEG Segment;
1754 RTSGBUF SgBuf;
1755 VDIOCTX IoCtx;
1756
1757 Segment.pvSeg = pvBuf;
1758 Segment.cbSeg = cbRead;
1759 RTSgBufInit(&SgBuf, &Segment, 1);
1760 vdIoCtxInit(&IoCtx, pParentState->pDisk, VDIOCTXTXDIR_READ, uOffset, cbRead, pParentState->pImage,
1761 &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC | VDIOCTX_FLAGS_ZERO_FREE_BLOCKS);
1762 int rc = vdReadHelperAsync(&IoCtx);
1763 ASMAtomicXchgBool(&pParentState->pDisk->fLocked, false);
1764 return rc;
1765}
1766
1767/**
1768 * Extended version of vdReadHelper(), implementing certain optimizations
1769 * for image cloning.
1770 *
1771 * @returns VBox status code.
1772 * @param pDisk The disk to read from.
1773 * @param pImage The image to start reading from.
1774 * @param pImageParentOverride The parent image to read from
1775 * if the starting image returns a free block.
1776 * If NULL is passed the real parent of the image
1777 * in the chain is used.
1778 * @param uOffset Offset in the disk to start reading from.
1779 * @param pvBuf Where to store the read data.
1780 * @param cbRead How much to read.
1781 * @param fZeroFreeBlocks Flag whether free blocks should be zeroed.
1782 * If false and no image has data for sepcified
1783 * range VERR_VD_BLOCK_FREE is returned.
1784 * Note that unallocated blocks are still zeroed
1785 * if at least one image has valid data for a part
1786 * of the range.
1787 * @param fUpdateCache Flag whether to update the attached cache if
1788 * available.
1789 * @param cImagesRead Number of images in the chain to read until
1790 * the read is cut off. A value of 0 disables the cut off.
1791 */
1792static int vdReadHelperEx(PVDISK pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
1793 uint64_t uOffset, void *pvBuf, size_t cbRead,
1794 bool fZeroFreeBlocks, bool fUpdateCache, unsigned cImagesRead)
1795{
1796 int rc = VINF_SUCCESS;
1797 uint32_t fFlags = VDIOCTX_FLAGS_SYNC | VDIOCTX_FLAGS_DONT_FREE;
1798 RTSGSEG Segment;
1799 RTSGBUF SgBuf;
1800 VDIOCTX IoCtx;
1801 RTSEMEVENT hEventComplete = NIL_RTSEMEVENT;
1802
1803 rc = RTSemEventCreate(&hEventComplete);
1804 if (RT_FAILURE(rc))
1805 return rc;
1806
1807 if (fZeroFreeBlocks)
1808 fFlags |= VDIOCTX_FLAGS_ZERO_FREE_BLOCKS;
1809 if (fUpdateCache)
1810 fFlags |= VDIOCTX_FLAGS_READ_UPDATE_CACHE;
1811
1812 Segment.pvSeg = pvBuf;
1813 Segment.cbSeg = cbRead;
1814 RTSgBufInit(&SgBuf, &Segment, 1);
1815 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_READ, uOffset, cbRead, pImage, &SgBuf,
1816 NULL, vdReadHelperAsync, fFlags);
1817
1818 IoCtx.Req.Io.pImageParentOverride = pImageParentOverride;
1819 IoCtx.Req.Io.cImagesRead = cImagesRead;
1820 IoCtx.Type.Root.pfnComplete = vdIoCtxSyncComplete;
1821 IoCtx.Type.Root.pvUser1 = pDisk;
1822 IoCtx.Type.Root.pvUser2 = hEventComplete;
1823 rc = vdIoCtxProcessSync(&IoCtx, hEventComplete);
1824 RTSemEventDestroy(hEventComplete);
1825 return rc;
1826}
1827
1828/**
1829 * internal: read the specified amount of data in whatever blocks the backend
1830 * will give us.
1831 */
1832static int vdReadHelper(PVDISK pDisk, PVDIMAGE pImage, uint64_t uOffset,
1833 void *pvBuf, size_t cbRead, bool fUpdateCache)
1834{
1835 return vdReadHelperEx(pDisk, pImage, NULL, uOffset, pvBuf, cbRead,
1836 true /* fZeroFreeBlocks */, fUpdateCache, 0);
1837}
1838
1839/**
1840 * internal: mark the disk as not modified.
1841 */
1842static void vdResetModifiedFlag(PVDISK pDisk)
1843{
1844 if (pDisk->uModified & VD_IMAGE_MODIFIED_FLAG)
1845 {
1846 /* generate new last-modified uuid */
1847 if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
1848 {
1849 RTUUID Uuid;
1850
1851 RTUuidCreate(&Uuid);
1852 pDisk->pLast->Backend->pfnSetModificationUuid(pDisk->pLast->pBackendData,
1853 &Uuid);
1854
1855 if (pDisk->pCache)
1856 pDisk->pCache->Backend->pfnSetModificationUuid(pDisk->pCache->pBackendData,
1857 &Uuid);
1858 }
1859
1860 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FLAG;
1861 }
1862}
1863
1864/**
1865 * internal: mark the disk as modified.
1866 */
1867static void vdSetModifiedFlag(PVDISK pDisk)
1868{
1869 pDisk->uModified |= VD_IMAGE_MODIFIED_FLAG;
1870 if (pDisk->uModified & VD_IMAGE_MODIFIED_FIRST)
1871 {
1872 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FIRST;
1873
1874 /* First modify, so create a UUID and ensure it's written to disk. */
1875 vdResetModifiedFlag(pDisk);
1876
1877 if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
1878 {
1879 VDIOCTX IoCtx;
1880 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_FLUSH, 0, 0, NULL,
1881 NULL, NULL, NULL, VDIOCTX_FLAGS_SYNC);
1882 pDisk->pLast->Backend->pfnFlush(pDisk->pLast->pBackendData, &IoCtx);
1883 }
1884 }
1885}
1886
1887/**
1888 * internal: write buffer to the image, taking care of block boundaries and
1889 * write optimizations.
1890 */
1891static int vdWriteHelperEx(PVDISK pDisk, PVDIMAGE pImage,
1892 PVDIMAGE pImageParentOverride, uint64_t uOffset,
1893 const void *pvBuf, size_t cbWrite,
1894 uint32_t fFlags, unsigned cImagesRead)
1895{
1896 int rc = VINF_SUCCESS;
1897 RTSGSEG Segment;
1898 RTSGBUF SgBuf;
1899 VDIOCTX IoCtx;
1900 RTSEMEVENT hEventComplete = NIL_RTSEMEVENT;
1901
1902 rc = RTSemEventCreate(&hEventComplete);
1903 if (RT_FAILURE(rc))
1904 return rc;
1905
1906 fFlags |= VDIOCTX_FLAGS_SYNC | VDIOCTX_FLAGS_DONT_FREE;
1907
1908 Segment.pvSeg = (void *)pvBuf;
1909 Segment.cbSeg = cbWrite;
1910 RTSgBufInit(&SgBuf, &Segment, 1);
1911 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_WRITE, uOffset, cbWrite, pImage, &SgBuf,
1912 NULL, vdWriteHelperAsync, fFlags);
1913
1914 IoCtx.Req.Io.pImageParentOverride = pImageParentOverride;
1915 IoCtx.Req.Io.cImagesRead = cImagesRead;
1916 IoCtx.pIoCtxParent = NULL;
1917 IoCtx.Type.Root.pfnComplete = vdIoCtxSyncComplete;
1918 IoCtx.Type.Root.pvUser1 = pDisk;
1919 IoCtx.Type.Root.pvUser2 = hEventComplete;
1920 if (RT_SUCCESS(rc))
1921 rc = vdIoCtxProcessSync(&IoCtx, hEventComplete);
1922
1923 RTSemEventDestroy(hEventComplete);
1924 return rc;
1925}
1926
1927/**
1928 * internal: write buffer to the image, taking care of block boundaries and
1929 * write optimizations.
1930 */
1931static int vdWriteHelper(PVDISK pDisk, PVDIMAGE pImage, uint64_t uOffset,
1932 const void *pvBuf, size_t cbWrite, uint32_t fFlags)
1933{
1934 return vdWriteHelperEx(pDisk, pImage, NULL, uOffset, pvBuf, cbWrite,
1935 fFlags, 0);
1936}
1937
1938/**
1939 * Internal: Copies the content of one disk to another one applying optimizations
1940 * to speed up the copy process if possible.
1941 */
1942static int vdCopyHelper(PVDISK pDiskFrom, PVDIMAGE pImageFrom, PVDISK pDiskTo,
1943 uint64_t cbSize, unsigned cImagesFromRead, unsigned cImagesToRead,
1944 bool fSuppressRedundantIo, PVDINTERFACEPROGRESS pIfProgress,
1945 PVDINTERFACEPROGRESS pDstIfProgress)
1946{
1947 int rc = VINF_SUCCESS;
1948 int rc2;
1949 uint64_t uOffset = 0;
1950 uint64_t cbRemaining = cbSize;
1951 void *pvBuf = NULL;
1952 bool fLockReadFrom = false;
1953 bool fLockWriteTo = false;
1954 bool fBlockwiseCopy = false;
1955 unsigned uProgressOld = 0;
1956
1957 LogFlowFunc(("pDiskFrom=%#p pImageFrom=%#p pDiskTo=%#p cbSize=%llu cImagesFromRead=%u cImagesToRead=%u fSuppressRedundantIo=%RTbool pIfProgress=%#p pDstIfProgress=%#p\n",
1958 pDiskFrom, pImageFrom, pDiskTo, cbSize, cImagesFromRead, cImagesToRead, fSuppressRedundantIo, pDstIfProgress, pDstIfProgress));
1959
1960 if ( (fSuppressRedundantIo || (cImagesFromRead > 0))
1961 && RTListIsEmpty(&pDiskFrom->ListFilterChainRead))
1962 fBlockwiseCopy = true;
1963
1964 /* Allocate tmp buffer. */
1965 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
1966 if (!pvBuf)
1967 return rc;
1968
1969 do
1970 {
1971 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
1972
1973 /* Note that we don't attempt to synchronize cross-disk accesses.
1974 * It wouldn't be very difficult to do, just the lock order would
1975 * need to be defined somehow to prevent deadlocks. Postpone such
1976 * magic as there is no use case for this. */
1977
1978 rc2 = vdThreadStartRead(pDiskFrom);
1979 AssertRC(rc2);
1980 fLockReadFrom = true;
1981
1982 if (fBlockwiseCopy)
1983 {
1984 RTSGSEG SegmentBuf;
1985 RTSGBUF SgBuf;
1986 VDIOCTX IoCtx;
1987
1988 SegmentBuf.pvSeg = pvBuf;
1989 SegmentBuf.cbSeg = VD_MERGE_BUFFER_SIZE;
1990 RTSgBufInit(&SgBuf, &SegmentBuf, 1);
1991 vdIoCtxInit(&IoCtx, pDiskFrom, VDIOCTXTXDIR_READ, 0, 0, NULL,
1992 &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
1993
1994 /* Read the source data. */
1995 rc = pImageFrom->Backend->pfnRead(pImageFrom->pBackendData,
1996 uOffset, cbThisRead, &IoCtx,
1997 &cbThisRead);
1998
1999 if ( rc == VERR_VD_BLOCK_FREE
2000 && cImagesFromRead != 1)
2001 {
2002 unsigned cImagesToProcess = cImagesFromRead;
2003
2004 for (PVDIMAGE pCurrImage = pImageFrom->pPrev;
2005 pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
2006 pCurrImage = pCurrImage->pPrev)
2007 {
2008 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
2009 uOffset, cbThisRead,
2010 &IoCtx, &cbThisRead);
2011 if (cImagesToProcess == 1)
2012 break;
2013 else if (cImagesToProcess > 0)
2014 cImagesToProcess--;
2015 }
2016 }
2017 }
2018 else
2019 rc = vdReadHelper(pDiskFrom, pImageFrom, uOffset, pvBuf, cbThisRead,
2020 false /* fUpdateCache */);
2021
2022 if (RT_FAILURE(rc) && rc != VERR_VD_BLOCK_FREE)
2023 break;
2024
2025 rc2 = vdThreadFinishRead(pDiskFrom);
2026 AssertRC(rc2);
2027 fLockReadFrom = false;
2028
2029 if (rc != VERR_VD_BLOCK_FREE)
2030 {
2031 rc2 = vdThreadStartWrite(pDiskTo);
2032 AssertRC(rc2);
2033 fLockWriteTo = true;
2034
2035 /* Only do collapsed I/O if we are copying the data blockwise. */
2036 rc = vdWriteHelperEx(pDiskTo, pDiskTo->pLast, NULL, uOffset, pvBuf,
2037 cbThisRead, VDIOCTX_FLAGS_DONT_SET_MODIFIED_FLAG /* fFlags */,
2038 fBlockwiseCopy ? cImagesToRead : 0);
2039 if (RT_FAILURE(rc))
2040 break;
2041
2042 rc2 = vdThreadFinishWrite(pDiskTo);
2043 AssertRC(rc2);
2044 fLockWriteTo = false;
2045 }
2046 else /* Don't propagate the error to the outside */
2047 rc = VINF_SUCCESS;
2048
2049 uOffset += cbThisRead;
2050 cbRemaining -= cbThisRead;
2051
2052 unsigned uProgressNew = uOffset * 99 / cbSize;
2053 if (uProgressNew != uProgressOld)
2054 {
2055 uProgressOld = uProgressNew;
2056
2057 if (pIfProgress && pIfProgress->pfnProgress)
2058 {
2059 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
2060 uProgressOld);
2061 if (RT_FAILURE(rc))
2062 break;
2063 }
2064 if (pDstIfProgress && pDstIfProgress->pfnProgress)
2065 {
2066 rc = pDstIfProgress->pfnProgress(pDstIfProgress->Core.pvUser,
2067 uProgressOld);
2068 if (RT_FAILURE(rc))
2069 break;
2070 }
2071 }
2072 } while (uOffset < cbSize);
2073
2074 RTMemFree(pvBuf);
2075
2076 if (fLockReadFrom)
2077 {
2078 rc2 = vdThreadFinishRead(pDiskFrom);
2079 AssertRC(rc2);
2080 }
2081
2082 if (fLockWriteTo)
2083 {
2084 rc2 = vdThreadFinishWrite(pDiskTo);
2085 AssertRC(rc2);
2086 }
2087
2088 LogFlowFunc(("returns rc=%Rrc\n", rc));
2089 return rc;
2090}
2091
2092/**
2093 * Flush helper async version.
2094 */
2095static DECLCALLBACK(int) vdSetModifiedHelperAsync(PVDIOCTX pIoCtx)
2096{
2097 int rc = VINF_SUCCESS;
2098 PVDIMAGE pImage = pIoCtx->Req.Io.pImageCur;
2099
2100 rc = pImage->Backend->pfnFlush(pImage->pBackendData, pIoCtx);
2101 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2102 rc = VINF_SUCCESS;
2103
2104 return rc;
2105}
2106
2107/**
2108 * internal: mark the disk as modified - async version.
2109 */
2110static int vdSetModifiedFlagAsync(PVDISK pDisk, PVDIOCTX pIoCtx)
2111{
2112 int rc = VINF_SUCCESS;
2113
2114 VD_IS_LOCKED(pDisk);
2115
2116 pDisk->uModified |= VD_IMAGE_MODIFIED_FLAG;
2117 if (pDisk->uModified & VD_IMAGE_MODIFIED_FIRST)
2118 {
2119 rc = vdIoCtxLockDisk(pDisk, pIoCtx);
2120 if (RT_SUCCESS(rc))
2121 {
2122 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FIRST;
2123
2124 /* First modify, so create a UUID and ensure it's written to disk. */
2125 vdResetModifiedFlag(pDisk);
2126
2127 if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
2128 {
2129 PVDIOCTX pIoCtxFlush = vdIoCtxChildAlloc(pDisk, VDIOCTXTXDIR_FLUSH,
2130 0, 0, pDisk->pLast,
2131 NULL, pIoCtx, 0, 0, NULL,
2132 vdSetModifiedHelperAsync);
2133
2134 if (pIoCtxFlush)
2135 {
2136 rc = vdIoCtxProcessLocked(pIoCtxFlush);
2137 if (rc == VINF_VD_ASYNC_IO_FINISHED)
2138 {
2139 vdIoCtxUnlockDisk(pDisk, pIoCtx, false /* fProcessDeferredReqs */);
2140 vdIoCtxFree(pDisk, pIoCtxFlush);
2141 }
2142 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2143 {
2144 ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
2145 pIoCtx->fFlags |= VDIOCTX_FLAGS_BLOCKED;
2146 }
2147 else /* Another error */
2148 vdIoCtxFree(pDisk, pIoCtxFlush);
2149 }
2150 else
2151 rc = VERR_NO_MEMORY;
2152 }
2153 }
2154 }
2155
2156 return rc;
2157}
2158
2159static DECLCALLBACK(int) vdWriteHelperCommitAsync(PVDIOCTX pIoCtx)
2160{
2161 int rc = VINF_SUCCESS;
2162 PVDIMAGE pImage = pIoCtx->Req.Io.pImageStart;
2163 size_t cbPreRead = pIoCtx->Type.Child.cbPreRead;
2164 size_t cbPostRead = pIoCtx->Type.Child.cbPostRead;
2165 size_t cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
2166
2167 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
2168 rc = pImage->Backend->pfnWrite(pImage->pBackendData,
2169 pIoCtx->Req.Io.uOffset - cbPreRead,
2170 cbPreRead + cbThisWrite + cbPostRead,
2171 pIoCtx, NULL, &cbPreRead, &cbPostRead, 0);
2172 Assert(rc != VERR_VD_BLOCK_FREE);
2173 Assert(rc == VERR_VD_NOT_ENOUGH_METADATA || cbPreRead == 0);
2174 Assert(rc == VERR_VD_NOT_ENOUGH_METADATA || cbPostRead == 0);
2175 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2176 rc = VINF_SUCCESS;
2177 else if (rc == VERR_VD_IOCTX_HALT)
2178 {
2179 pIoCtx->fFlags |= VDIOCTX_FLAGS_BLOCKED;
2180 rc = VINF_SUCCESS;
2181 }
2182
2183 LogFlowFunc(("returns rc=%Rrc\n", rc));
2184 return rc;
2185}
2186
2187static DECLCALLBACK(int) vdWriteHelperOptimizedCmpAndWriteAsync(PVDIOCTX pIoCtx)
2188{
2189 int rc = VINF_SUCCESS;
2190 size_t cbThisWrite = 0;
2191 size_t cbPreRead = pIoCtx->Type.Child.cbPreRead;
2192 size_t cbPostRead = pIoCtx->Type.Child.cbPostRead;
2193 size_t cbWriteCopy = pIoCtx->Type.Child.Write.Optimized.cbWriteCopy;
2194 size_t cbFill = pIoCtx->Type.Child.Write.Optimized.cbFill;
2195 size_t cbReadImage = pIoCtx->Type.Child.Write.Optimized.cbReadImage;
2196 PVDIOCTX pIoCtxParent = pIoCtx->pIoCtxParent;
2197
2198 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
2199
2200 AssertPtr(pIoCtxParent);
2201 Assert(!pIoCtxParent->pIoCtxParent);
2202 Assert(!pIoCtx->Req.Io.cbTransferLeft && !pIoCtx->cMetaTransfersPending);
2203
2204 vdIoCtxChildReset(pIoCtx);
2205 cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
2206 RTSgBufAdvance(&pIoCtx->Req.Io.SgBuf, cbPreRead);
2207
2208 /* Check if the write would modify anything in this block. */
2209 if (!RTSgBufCmp(&pIoCtx->Req.Io.SgBuf, &pIoCtxParent->Req.Io.SgBuf, cbThisWrite))
2210 {
2211 RTSGBUF SgBufSrcTmp;
2212
2213 RTSgBufClone(&SgBufSrcTmp, &pIoCtxParent->Req.Io.SgBuf);
2214 RTSgBufAdvance(&SgBufSrcTmp, cbThisWrite);
2215 RTSgBufAdvance(&pIoCtx->Req.Io.SgBuf, cbThisWrite);
2216
2217 if (!cbWriteCopy || !RTSgBufCmp(&pIoCtx->Req.Io.SgBuf, &SgBufSrcTmp, cbWriteCopy))
2218 {
2219 /* Block is completely unchanged, so no need to write anything. */
2220 LogFlowFunc(("Block didn't changed\n"));
2221 ASMAtomicWriteU32(&pIoCtx->Req.Io.cbTransferLeft, 0);
2222 RTSgBufAdvance(&pIoCtxParent->Req.Io.SgBuf, cbThisWrite);
2223 return VINF_VD_ASYNC_IO_FINISHED;
2224 }
2225 }
2226
2227 /* Copy the data to the right place in the buffer. */
2228 RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
2229 RTSgBufAdvance(&pIoCtx->Req.Io.SgBuf, cbPreRead);
2230 vdIoCtxCopy(pIoCtx, pIoCtxParent, cbThisWrite);
2231
2232 /* Handle the data that goes after the write to fill the block. */
2233 if (cbPostRead)
2234 {
2235 /* Now assemble the remaining data. */
2236 if (cbWriteCopy)
2237 {
2238 /*
2239 * The S/G buffer of the parent needs to be cloned because
2240 * it is not allowed to modify the state.
2241 */
2242 RTSGBUF SgBufParentTmp;
2243
2244 RTSgBufClone(&SgBufParentTmp, &pIoCtxParent->Req.Io.SgBuf);
2245 RTSgBufCopy(&pIoCtx->Req.Io.SgBuf, &SgBufParentTmp, cbWriteCopy);
2246 }
2247
2248 /* Zero out the remainder of this block. Will never be visible, as this
2249 * is beyond the limit of the image. */
2250 if (cbFill)
2251 {
2252 RTSgBufAdvance(&pIoCtx->Req.Io.SgBuf, cbReadImage);
2253 vdIoCtxSet(pIoCtx, '\0', cbFill);
2254 }
2255 }
2256
2257 /* Write the full block to the virtual disk. */
2258 RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
2259 pIoCtx->pfnIoCtxTransferNext = vdWriteHelperCommitAsync;
2260
2261 return rc;
2262}
2263
2264static DECLCALLBACK(int) vdWriteHelperOptimizedPreReadAsync(PVDIOCTX pIoCtx)
2265{
2266 int rc = VINF_SUCCESS;
2267
2268 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
2269
2270 pIoCtx->fFlags |= VDIOCTX_FLAGS_ZERO_FREE_BLOCKS;
2271
2272 if ( pIoCtx->Req.Io.cbTransferLeft
2273 && !pIoCtx->cDataTransfersPending)
2274 rc = vdReadHelperAsync(pIoCtx);
2275
2276 if ( ( RT_SUCCESS(rc)
2277 || (rc == VERR_VD_ASYNC_IO_IN_PROGRESS))
2278 && ( pIoCtx->Req.Io.cbTransferLeft
2279 || pIoCtx->cMetaTransfersPending))
2280 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2281 else
2282 pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedCmpAndWriteAsync;
2283
2284 return rc;
2285}
2286
2287/**
2288 * internal: write a complete block (only used for diff images), taking the
2289 * remaining data from parent images. This implementation optimizes out writes
2290 * that do not change the data relative to the state as of the parent images.
2291 * All backends which support differential/growing images support this - async version.
2292 */
2293static DECLCALLBACK(int) vdWriteHelperOptimizedAsync(PVDIOCTX pIoCtx)
2294{
2295 PVDISK pDisk = pIoCtx->pDisk;
2296 uint64_t uOffset = pIoCtx->Type.Child.uOffsetSaved;
2297 size_t cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
2298 size_t cbPreRead = pIoCtx->Type.Child.cbPreRead;
2299 size_t cbPostRead = pIoCtx->Type.Child.cbPostRead;
2300 size_t cbWrite = pIoCtx->Type.Child.cbWriteParent;
2301 size_t cbFill = 0;
2302 size_t cbWriteCopy = 0;
2303 size_t cbReadImage = 0;
2304
2305 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
2306
2307 AssertPtr(pIoCtx->pIoCtxParent);
2308 Assert(!pIoCtx->pIoCtxParent->pIoCtxParent);
2309
2310 if (cbPostRead)
2311 {
2312 /* Figure out how much we cannot read from the image, because
2313 * the last block to write might exceed the nominal size of the
2314 * image for technical reasons. */
2315 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
2316 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
2317
2318 /* If we have data to be written, use that instead of reading
2319 * data from the image. */
2320 if (cbWrite > cbThisWrite)
2321 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
2322
2323 /* The rest must be read from the image. */
2324 cbReadImage = cbPostRead - cbWriteCopy - cbFill;
2325 }
2326
2327 pIoCtx->Type.Child.Write.Optimized.cbFill = cbFill;
2328 pIoCtx->Type.Child.Write.Optimized.cbWriteCopy = cbWriteCopy;
2329 pIoCtx->Type.Child.Write.Optimized.cbReadImage = cbReadImage;
2330
2331 /* Read the entire data of the block so that we can compare whether it will
2332 * be modified by the write or not. */
2333 size_t cbTmp = cbPreRead + cbThisWrite + cbPostRead - cbFill; Assert(cbTmp == (uint32_t)cbTmp);
2334 pIoCtx->Req.Io.cbTransferLeft = (uint32_t)cbTmp;
2335 pIoCtx->Req.Io.cbTransfer = pIoCtx->Req.Io.cbTransferLeft;
2336 pIoCtx->Req.Io.uOffset -= cbPreRead;
2337
2338 /* Next step */
2339 pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedPreReadAsync;
2340 return VINF_SUCCESS;
2341}
2342
2343static DECLCALLBACK(int) vdWriteHelperStandardReadImageAsync(PVDIOCTX pIoCtx)
2344{
2345 int rc = VINF_SUCCESS;
2346
2347 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
2348
2349 pIoCtx->fFlags |= VDIOCTX_FLAGS_ZERO_FREE_BLOCKS;
2350
2351 if ( pIoCtx->Req.Io.cbTransferLeft
2352 && !pIoCtx->cDataTransfersPending)
2353 rc = vdReadHelperAsync(pIoCtx);
2354
2355 if ( RT_SUCCESS(rc)
2356 && ( pIoCtx->Req.Io.cbTransferLeft
2357 || pIoCtx->cMetaTransfersPending))
2358 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2359 else
2360 {
2361 size_t cbFill = pIoCtx->Type.Child.Write.Optimized.cbFill;
2362
2363 /* Zero out the remainder of this block. Will never be visible, as this
2364 * is beyond the limit of the image. */
2365 if (cbFill)
2366 vdIoCtxSet(pIoCtx, '\0', cbFill);
2367
2368 /* Write the full block to the virtual disk. */
2369 RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
2370
2371 vdIoCtxChildReset(pIoCtx);
2372 pIoCtx->pfnIoCtxTransferNext = vdWriteHelperCommitAsync;
2373 }
2374
2375 return rc;
2376}
2377
2378static DECLCALLBACK(int) vdWriteHelperStandardAssemble(PVDIOCTX pIoCtx)
2379{
2380 int rc = VINF_SUCCESS;
2381 size_t cbPostRead = pIoCtx->Type.Child.cbPostRead;
2382 size_t cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
2383 PVDIOCTX pIoCtxParent = pIoCtx->pIoCtxParent;
2384
2385 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
2386
2387 vdIoCtxCopy(pIoCtx, pIoCtxParent, cbThisWrite);
2388 if (cbPostRead)
2389 {
2390 size_t cbFill = pIoCtx->Type.Child.Write.Optimized.cbFill;
2391 size_t cbWriteCopy = pIoCtx->Type.Child.Write.Optimized.cbWriteCopy;
2392 size_t cbReadImage = pIoCtx->Type.Child.Write.Optimized.cbReadImage;
2393
2394 /* Now assemble the remaining data. */
2395 if (cbWriteCopy)
2396 {
2397 /*
2398 * The S/G buffer of the parent needs to be cloned because
2399 * it is not allowed to modify the state.
2400 */
2401 RTSGBUF SgBufParentTmp;
2402
2403 RTSgBufClone(&SgBufParentTmp, &pIoCtxParent->Req.Io.SgBuf);
2404 RTSgBufCopy(&pIoCtx->Req.Io.SgBuf, &SgBufParentTmp, cbWriteCopy);
2405 }
2406
2407 if (cbReadImage)
2408 {
2409 /* Read remaining data. */
2410 pIoCtx->pfnIoCtxTransferNext = vdWriteHelperStandardReadImageAsync;
2411
2412 /* Read the data that goes before the write to fill the block. */
2413 pIoCtx->Req.Io.cbTransferLeft = (uint32_t)cbReadImage; Assert(cbReadImage == (uint32_t)cbReadImage);
2414 pIoCtx->Req.Io.cbTransfer = pIoCtx->Req.Io.cbTransferLeft;
2415 pIoCtx->Req.Io.uOffset += cbWriteCopy;
2416 }
2417 else
2418 {
2419 /* Zero out the remainder of this block. Will never be visible, as this
2420 * is beyond the limit of the image. */
2421 if (cbFill)
2422 vdIoCtxSet(pIoCtx, '\0', cbFill);
2423
2424 /* Write the full block to the virtual disk. */
2425 RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
2426 vdIoCtxChildReset(pIoCtx);
2427 pIoCtx->pfnIoCtxTransferNext = vdWriteHelperCommitAsync;
2428 }
2429 }
2430 else
2431 {
2432 /* Write the full block to the virtual disk. */
2433 RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
2434 vdIoCtxChildReset(pIoCtx);
2435 pIoCtx->pfnIoCtxTransferNext = vdWriteHelperCommitAsync;
2436 }
2437
2438 return rc;
2439}
2440
2441static DECLCALLBACK(int) vdWriteHelperStandardPreReadAsync(PVDIOCTX pIoCtx)
2442{
2443 int rc = VINF_SUCCESS;
2444
2445 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
2446
2447 pIoCtx->fFlags |= VDIOCTX_FLAGS_ZERO_FREE_BLOCKS;
2448
2449 if ( pIoCtx->Req.Io.cbTransferLeft
2450 && !pIoCtx->cDataTransfersPending)
2451 rc = vdReadHelperAsync(pIoCtx);
2452
2453 if ( RT_SUCCESS(rc)
2454 && ( pIoCtx->Req.Io.cbTransferLeft
2455 || pIoCtx->cMetaTransfersPending))
2456 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2457 else
2458 pIoCtx->pfnIoCtxTransferNext = vdWriteHelperStandardAssemble;
2459
2460 return rc;
2461}
2462
2463static DECLCALLBACK(int) vdWriteHelperStandardAsync(PVDIOCTX pIoCtx)
2464{
2465 PVDISK pDisk = pIoCtx->pDisk;
2466 uint64_t uOffset = pIoCtx->Type.Child.uOffsetSaved;
2467 size_t cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
2468 size_t cbPreRead = pIoCtx->Type.Child.cbPreRead;
2469 size_t cbPostRead = pIoCtx->Type.Child.cbPostRead;
2470 size_t cbWrite = pIoCtx->Type.Child.cbWriteParent;
2471 size_t cbFill = 0;
2472 size_t cbWriteCopy = 0;
2473 size_t cbReadImage = 0;
2474
2475 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
2476
2477 AssertPtr(pIoCtx->pIoCtxParent);
2478 Assert(!pIoCtx->pIoCtxParent->pIoCtxParent);
2479
2480 /* Calculate the amount of data to read that goes after the write to fill the block. */
2481 if (cbPostRead)
2482 {
2483 /* If we have data to be written, use that instead of reading
2484 * data from the image. */
2485 if (cbWrite > cbThisWrite)
2486 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
2487 else
2488 cbWriteCopy = 0;
2489
2490 /* Figure out how much we cannot read from the image, because
2491 * the last block to write might exceed the nominal size of the
2492 * image for technical reasons. */
2493 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
2494 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
2495
2496 /* The rest must be read from the image. */
2497 cbReadImage = cbPostRead - cbWriteCopy - cbFill;
2498 }
2499
2500 pIoCtx->Type.Child.Write.Optimized.cbFill = cbFill;
2501 pIoCtx->Type.Child.Write.Optimized.cbWriteCopy = cbWriteCopy;
2502 pIoCtx->Type.Child.Write.Optimized.cbReadImage = cbReadImage;
2503
2504 /* Next step */
2505 if (cbPreRead)
2506 {
2507 pIoCtx->pfnIoCtxTransferNext = vdWriteHelperStandardPreReadAsync;
2508
2509 /* Read the data that goes before the write to fill the block. */
2510 pIoCtx->Req.Io.cbTransferLeft = (uint32_t)cbPreRead; Assert(cbPreRead == (uint32_t)cbPreRead);
2511 pIoCtx->Req.Io.cbTransfer = pIoCtx->Req.Io.cbTransferLeft;
2512 pIoCtx->Req.Io.uOffset -= cbPreRead;
2513 }
2514 else
2515 pIoCtx->pfnIoCtxTransferNext = vdWriteHelperStandardAssemble;
2516
2517 return VINF_SUCCESS;
2518}
2519
2520/**
2521 * internal: write buffer to the image, taking care of block boundaries and
2522 * write optimizations - async version.
2523 */
2524static DECLCALLBACK(int) vdWriteHelperAsync(PVDIOCTX pIoCtx)
2525{
2526 int rc;
2527 size_t cbWrite = pIoCtx->Req.Io.cbTransfer;
2528 uint64_t uOffset = pIoCtx->Req.Io.uOffset;
2529 PVDIMAGE pImage = pIoCtx->Req.Io.pImageCur;
2530 PVDISK pDisk = pIoCtx->pDisk;
2531 unsigned fWrite;
2532 size_t cbThisWrite;
2533 size_t cbPreRead, cbPostRead;
2534
2535 /* Apply write filter chain here if it was not done already. */
2536 if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_WRITE_FILTER_APPLIED))
2537 {
2538 rc = vdFilterChainApplyWrite(pDisk, uOffset, cbWrite, pIoCtx);
2539 if (RT_FAILURE(rc))
2540 return rc;
2541 pIoCtx->fFlags |= VDIOCTX_FLAGS_WRITE_FILTER_APPLIED;
2542 }
2543
2544 if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_DONT_SET_MODIFIED_FLAG))
2545 {
2546 rc = vdSetModifiedFlagAsync(pDisk, pIoCtx);
2547 if (RT_FAILURE(rc)) /* Includes I/O in progress. */
2548 return rc;
2549 }
2550
2551 rc = vdDiscardSetRangeAllocated(pDisk, uOffset, cbWrite);
2552 if (RT_FAILURE(rc))
2553 return rc;
2554
2555 /* Loop until all written. */
2556 do
2557 {
2558 /* Try to write the possibly partial block to the last opened image.
2559 * This works when the block is already allocated in this image or
2560 * if it is a full-block write (and allocation isn't suppressed below).
2561 * For image formats which don't support zero blocks, it's beneficial
2562 * to avoid unnecessarily allocating unchanged blocks. This prevents
2563 * unwanted expanding of images. VMDK is an example. */
2564 cbThisWrite = cbWrite;
2565
2566 /*
2567 * Check whether there is a full block write in progress which was not allocated.
2568 * Defer I/O if the range interferes.
2569 */
2570 if ( pDisk->pIoCtxLockOwner != NIL_VDIOCTX
2571 && uOffset >= pDisk->uOffsetStartLocked
2572 && uOffset < pDisk->uOffsetEndLocked)
2573 {
2574 Log(("Interferring write while allocating a new block => deferring write\n"));
2575 vdIoCtxDefer(pDisk, pIoCtx);
2576 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2577 break;
2578 }
2579
2580 fWrite = (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
2581 ? 0 : VD_WRITE_NO_ALLOC;
2582 rc = pImage->Backend->pfnWrite(pImage->pBackendData, uOffset, cbThisWrite,
2583 pIoCtx, &cbThisWrite, &cbPreRead, &cbPostRead,
2584 fWrite);
2585 if (rc == VERR_VD_BLOCK_FREE)
2586 {
2587 /* Lock the disk .*/
2588 rc = vdIoCtxLockDisk(pDisk, pIoCtx);
2589 if (RT_SUCCESS(rc))
2590 {
2591 /*
2592 * Allocate segment and buffer in one go.
2593 * A bit hackish but avoids the need to allocate memory twice.
2594 */
2595 PRTSGBUF pTmp = (PRTSGBUF)RTMemAlloc(cbPreRead + cbThisWrite + cbPostRead + sizeof(RTSGSEG) + sizeof(RTSGBUF));
2596 AssertBreakStmt(pTmp, rc = VERR_NO_MEMORY);
2597 PRTSGSEG pSeg = (PRTSGSEG)(pTmp + 1);
2598
2599 pSeg->pvSeg = pSeg + 1;
2600 pSeg->cbSeg = cbPreRead + cbThisWrite + cbPostRead;
2601 RTSgBufInit(pTmp, pSeg, 1);
2602
2603 PVDIOCTX pIoCtxWrite = vdIoCtxChildAlloc(pDisk, VDIOCTXTXDIR_WRITE,
2604 uOffset, pSeg->cbSeg, pImage,
2605 pTmp,
2606 pIoCtx, cbThisWrite,
2607 cbWrite,
2608 pTmp,
2609 (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
2610 ? vdWriteHelperStandardAsync
2611 : vdWriteHelperOptimizedAsync);
2612 if (!VALID_PTR(pIoCtxWrite))
2613 {
2614 RTMemTmpFree(pTmp);
2615 rc = VERR_NO_MEMORY;
2616 break;
2617 }
2618
2619 LogFlowFunc(("Disk is growing because of pIoCtx=%#p pIoCtxWrite=%#p\n",
2620 pIoCtx, pIoCtxWrite));
2621
2622 /* Save the current range for the growing operation to check for intersecting requests later. */
2623 pDisk->uOffsetStartLocked = uOffset - cbPreRead;
2624 pDisk->uOffsetEndLocked = uOffset + cbThisWrite + cbPostRead;
2625
2626 pIoCtxWrite->Type.Child.cbPreRead = cbPreRead;
2627 pIoCtxWrite->Type.Child.cbPostRead = cbPostRead;
2628 pIoCtxWrite->Req.Io.pImageParentOverride = pIoCtx->Req.Io.pImageParentOverride;
2629
2630 /* Process the write request */
2631 rc = vdIoCtxProcessLocked(pIoCtxWrite);
2632
2633 if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
2634 {
2635 vdIoCtxUnlockDisk(pDisk, pIoCtx, false /* fProcessDeferredReqs*/ );
2636 vdIoCtxFree(pDisk, pIoCtxWrite);
2637 break;
2638 }
2639 else if ( rc == VINF_VD_ASYNC_IO_FINISHED
2640 && ASMAtomicCmpXchgBool(&pIoCtxWrite->fComplete, true, false))
2641 {
2642 LogFlow(("Child write request completed\n"));
2643 Assert(pIoCtx->Req.Io.cbTransferLeft >= cbThisWrite);
2644 Assert(cbThisWrite == (uint32_t)cbThisWrite);
2645 rc = pIoCtxWrite->rcReq;
2646 ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, (uint32_t)cbThisWrite);
2647 vdIoCtxUnlockDisk(pDisk, pIoCtx, false /* fProcessDeferredReqs*/ );
2648 vdIoCtxFree(pDisk, pIoCtxWrite);
2649 }
2650 else
2651 {
2652 LogFlow(("Child write pending\n"));
2653 ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
2654 pIoCtx->fFlags |= VDIOCTX_FLAGS_BLOCKED;
2655 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2656 cbWrite -= cbThisWrite;
2657 uOffset += cbThisWrite;
2658 break;
2659 }
2660 }
2661 else
2662 {
2663 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2664 break;
2665 }
2666 }
2667
2668 if (rc == VERR_VD_IOCTX_HALT)
2669 {
2670 cbWrite -= cbThisWrite;
2671 uOffset += cbThisWrite;
2672 pIoCtx->fFlags |= VDIOCTX_FLAGS_BLOCKED;
2673 break;
2674 }
2675 else if (rc == VERR_VD_NOT_ENOUGH_METADATA)
2676 break;
2677
2678 cbWrite -= cbThisWrite;
2679 uOffset += cbThisWrite;
2680 } while (cbWrite != 0 && (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS));
2681
2682 if ( rc == VERR_VD_ASYNC_IO_IN_PROGRESS
2683 || rc == VERR_VD_NOT_ENOUGH_METADATA
2684 || rc == VERR_VD_IOCTX_HALT)
2685 {
2686 /*
2687 * Tell the caller that we don't need to go back here because all
2688 * writes are initiated.
2689 */
2690 if ( !cbWrite
2691 && rc != VERR_VD_IOCTX_HALT)
2692 rc = VINF_SUCCESS;
2693
2694 pIoCtx->Req.Io.uOffset = uOffset;
2695 pIoCtx->Req.Io.cbTransfer = cbWrite;
2696 }
2697
2698 return rc;
2699}
2700
2701/**
2702 * Flush helper async version.
2703 */
2704static DECLCALLBACK(int) vdFlushHelperAsync(PVDIOCTX pIoCtx)
2705{
2706 int rc = VINF_SUCCESS;
2707 PVDISK pDisk = pIoCtx->pDisk;
2708 PVDIMAGE pImage = pIoCtx->Req.Io.pImageCur;
2709
2710 rc = vdIoCtxLockDisk(pDisk, pIoCtx);
2711 if (RT_SUCCESS(rc))
2712 {
2713 /* Mark the whole disk as locked. */
2714 pDisk->uOffsetStartLocked = 0;
2715 pDisk->uOffsetEndLocked = UINT64_C(0xffffffffffffffff);
2716
2717 vdResetModifiedFlag(pDisk);
2718 rc = pImage->Backend->pfnFlush(pImage->pBackendData, pIoCtx);
2719 if ( ( RT_SUCCESS(rc)
2720 || rc == VERR_VD_ASYNC_IO_IN_PROGRESS
2721 || rc == VERR_VD_IOCTX_HALT)
2722 && pDisk->pCache)
2723 {
2724 rc = pDisk->pCache->Backend->pfnFlush(pDisk->pCache->pBackendData, pIoCtx);
2725 if ( RT_SUCCESS(rc)
2726 || ( rc != VERR_VD_ASYNC_IO_IN_PROGRESS
2727 && rc != VERR_VD_IOCTX_HALT))
2728 vdIoCtxUnlockDisk(pDisk, pIoCtx, true /* fProcessBlockedReqs */);
2729 else if (rc != VERR_VD_IOCTX_HALT)
2730 rc = VINF_SUCCESS;
2731 }
2732 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2733 rc = VINF_SUCCESS;
2734 else if (rc != VERR_VD_IOCTX_HALT)/* Some other error. */
2735 vdIoCtxUnlockDisk(pDisk, pIoCtx, true /* fProcessBlockedReqs */);
2736 }
2737
2738 return rc;
2739}
2740
2741/**
2742 * Async discard helper - discards a whole block which is recorded in the block
2743 * tree.
2744 *
2745 * @returns VBox status code.
2746 * @param pIoCtx The I/O context to operate on.
2747 */
2748static DECLCALLBACK(int) vdDiscardWholeBlockAsync(PVDIOCTX pIoCtx)
2749{
2750 int rc = VINF_SUCCESS;
2751 PVDISK pDisk = pIoCtx->pDisk;
2752 PVDDISCARDSTATE pDiscard = pDisk->pDiscard;
2753 PVDDISCARDBLOCK pBlock = pIoCtx->Req.Discard.pBlock;
2754 size_t cbPreAllocated, cbPostAllocated, cbActuallyDiscarded;
2755
2756 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
2757
2758 AssertPtr(pBlock);
2759
2760 rc = pDisk->pLast->Backend->pfnDiscard(pDisk->pLast->pBackendData, pIoCtx,
2761 pBlock->Core.Key, pBlock->cbDiscard,
2762 &cbPreAllocated, &cbPostAllocated,
2763 &cbActuallyDiscarded, NULL, 0);
2764 Assert(rc != VERR_VD_DISCARD_ALIGNMENT_NOT_MET);
2765 Assert(!cbPreAllocated);
2766 Assert(!cbPostAllocated);
2767 Assert(cbActuallyDiscarded == pBlock->cbDiscard || RT_FAILURE(rc));
2768
2769 /* Remove the block on success. */
2770 if ( RT_SUCCESS(rc)
2771 || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2772 {
2773 PVDDISCARDBLOCK pBlockRemove = (PVDDISCARDBLOCK)RTAvlrU64RangeRemove(pDiscard->pTreeBlocks, pBlock->Core.Key);
2774 Assert(pBlockRemove == pBlock); RT_NOREF1(pBlockRemove);
2775
2776 pDiscard->cbDiscarding -= pBlock->cbDiscard;
2777 RTListNodeRemove(&pBlock->NodeLru);
2778 RTMemFree(pBlock->pbmAllocated);
2779 RTMemFree(pBlock);
2780 pIoCtx->Req.Discard.pBlock = NULL;/* Safety precaution. */
2781 pIoCtx->pfnIoCtxTransferNext = vdDiscardHelperAsync; /* Next part. */
2782 rc = VINF_SUCCESS;
2783 }
2784
2785 LogFlowFunc(("returns rc=%Rrc\n", rc));
2786 return rc;
2787}
2788
2789/**
2790 * Removes the least recently used blocks from the waiting list until
2791 * the new value is reached - version for async I/O.
2792 *
2793 * @returns VBox status code.
2794 * @param pDisk VD disk container.
2795 * @param pIoCtx The I/O context associated with this discard operation.
2796 * @param cbDiscardingNew How many bytes should be waiting on success.
2797 * The number of bytes waiting can be less.
2798 */
2799static int vdDiscardRemoveBlocksAsync(PVDISK pDisk, PVDIOCTX pIoCtx, size_t cbDiscardingNew)
2800{
2801 int rc = VINF_SUCCESS;
2802 PVDDISCARDSTATE pDiscard = pDisk->pDiscard;
2803
2804 LogFlowFunc(("pDisk=%#p pDiscard=%#p cbDiscardingNew=%zu\n",
2805 pDisk, pDiscard, cbDiscardingNew));
2806
2807 while (pDiscard->cbDiscarding > cbDiscardingNew)
2808 {
2809 PVDDISCARDBLOCK pBlock = RTListGetLast(&pDiscard->ListLru, VDDISCARDBLOCK, NodeLru);
2810
2811 Assert(!RTListIsEmpty(&pDiscard->ListLru));
2812
2813 /* Go over the allocation bitmap and mark all discarded sectors as unused. */
2814 uint64_t offStart = pBlock->Core.Key;
2815 uint32_t idxStart = 0;
2816 size_t cbLeft = pBlock->cbDiscard;
2817 bool fAllocated = ASMBitTest(pBlock->pbmAllocated, idxStart);
2818 uint32_t cSectors = (uint32_t)(pBlock->cbDiscard / 512);
2819
2820 while (cbLeft > 0)
2821 {
2822 int32_t idxEnd;
2823 size_t cbThis = cbLeft;
2824
2825 if (fAllocated)
2826 {
2827 /* Check for the first unallocated bit. */
2828 idxEnd = ASMBitNextClear(pBlock->pbmAllocated, cSectors, idxStart);
2829 if (idxEnd != -1)
2830 {
2831 cbThis = (idxEnd - idxStart) * 512;
2832 fAllocated = false;
2833 }
2834 }
2835 else
2836 {
2837 /* Mark as unused and check for the first set bit. */
2838 idxEnd = ASMBitNextSet(pBlock->pbmAllocated, cSectors, idxStart);
2839 if (idxEnd != -1)
2840 cbThis = (idxEnd - idxStart) * 512;
2841
2842 rc = pDisk->pLast->Backend->pfnDiscard(pDisk->pLast->pBackendData, pIoCtx,
2843 offStart, cbThis, NULL, NULL, &cbThis,
2844 NULL, VD_DISCARD_MARK_UNUSED);
2845 if ( RT_FAILURE(rc)
2846 && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
2847 break;
2848
2849 fAllocated = true;
2850 }
2851
2852 idxStart = idxEnd;
2853 offStart += cbThis;
2854 cbLeft -= cbThis;
2855 }
2856
2857 if ( RT_FAILURE(rc)
2858 && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
2859 break;
2860
2861 PVDDISCARDBLOCK pBlockRemove = (PVDDISCARDBLOCK)RTAvlrU64RangeRemove(pDiscard->pTreeBlocks, pBlock->Core.Key);
2862 Assert(pBlockRemove == pBlock); NOREF(pBlockRemove);
2863 RTListNodeRemove(&pBlock->NodeLru);
2864
2865 pDiscard->cbDiscarding -= pBlock->cbDiscard;
2866 RTMemFree(pBlock->pbmAllocated);
2867 RTMemFree(pBlock);
2868 }
2869
2870 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2871 rc = VINF_SUCCESS;
2872
2873 Assert(RT_FAILURE(rc) || pDiscard->cbDiscarding <= cbDiscardingNew);
2874
2875 LogFlowFunc(("returns rc=%Rrc\n", rc));
2876 return rc;
2877}
2878
2879/**
2880 * Async discard helper - discards the current range if there is no matching
2881 * block in the tree.
2882 *
2883 * @returns VBox status code.
2884 * @param pIoCtx The I/O context to operate on.
2885 */
2886static DECLCALLBACK(int) vdDiscardCurrentRangeAsync(PVDIOCTX pIoCtx)
2887{
2888 PVDISK pDisk = pIoCtx->pDisk;
2889 PVDDISCARDSTATE pDiscard = pDisk->pDiscard;
2890 uint64_t offStart = pIoCtx->Req.Discard.offCur;
2891 size_t cbThisDiscard = pIoCtx->Req.Discard.cbThisDiscard;
2892 void *pbmAllocated = NULL;
2893 size_t cbPreAllocated, cbPostAllocated;
2894 int rc = VINF_SUCCESS;
2895
2896 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
2897
2898 /* No block found, try to discard using the backend first. */
2899 rc = pDisk->pLast->Backend->pfnDiscard(pDisk->pLast->pBackendData, pIoCtx,
2900 offStart, cbThisDiscard, &cbPreAllocated,
2901 &cbPostAllocated, &cbThisDiscard,
2902 &pbmAllocated, 0);
2903 if (rc == VERR_VD_DISCARD_ALIGNMENT_NOT_MET)
2904 {
2905 /* Create new discard block. */
2906 PVDDISCARDBLOCK pBlock = (PVDDISCARDBLOCK)RTMemAllocZ(sizeof(VDDISCARDBLOCK));
2907 if (pBlock)
2908 {
2909 pBlock->Core.Key = offStart - cbPreAllocated;
2910 pBlock->Core.KeyLast = offStart + cbThisDiscard + cbPostAllocated - 1;
2911 pBlock->cbDiscard = cbPreAllocated + cbThisDiscard + cbPostAllocated;
2912 pBlock->pbmAllocated = pbmAllocated;
2913 bool fInserted = RTAvlrU64Insert(pDiscard->pTreeBlocks, &pBlock->Core);
2914 Assert(fInserted); NOREF(fInserted);
2915
2916 RTListPrepend(&pDiscard->ListLru, &pBlock->NodeLru);
2917 pDiscard->cbDiscarding += pBlock->cbDiscard;
2918
2919 Assert(pIoCtx->Req.Discard.cbDiscardLeft >= cbThisDiscard);
2920 pIoCtx->Req.Discard.cbDiscardLeft -= cbThisDiscard;
2921 pIoCtx->Req.Discard.offCur += cbThisDiscard;
2922 pIoCtx->Req.Discard.cbThisDiscard = cbThisDiscard;
2923
2924 if (pDiscard->cbDiscarding > VD_DISCARD_REMOVE_THRESHOLD)
2925 rc = vdDiscardRemoveBlocksAsync(pDisk, pIoCtx, VD_DISCARD_REMOVE_THRESHOLD);
2926 else
2927 rc = VINF_SUCCESS;
2928
2929 if (RT_SUCCESS(rc))
2930 pIoCtx->pfnIoCtxTransferNext = vdDiscardHelperAsync; /* Next part. */
2931 }
2932 else
2933 {
2934 RTMemFree(pbmAllocated);
2935 rc = VERR_NO_MEMORY;
2936 }
2937 }
2938 else if ( RT_SUCCESS(rc)
2939 || rc == VERR_VD_ASYNC_IO_IN_PROGRESS) /* Save state and andvance to next range. */
2940 {
2941 Assert(pIoCtx->Req.Discard.cbDiscardLeft >= cbThisDiscard);
2942 pIoCtx->Req.Discard.cbDiscardLeft -= cbThisDiscard;
2943 pIoCtx->Req.Discard.offCur += cbThisDiscard;
2944 pIoCtx->Req.Discard.cbThisDiscard = cbThisDiscard;
2945 pIoCtx->pfnIoCtxTransferNext = vdDiscardHelperAsync;
2946 rc = VINF_SUCCESS;
2947 }
2948
2949 LogFlowFunc(("returns rc=%Rrc\n", rc));
2950 return rc;
2951}
2952
2953/**
2954 * Async discard helper - entry point.
2955 *
2956 * @returns VBox status code.
2957 * @param pIoCtx The I/O context to operate on.
2958 */
2959static DECLCALLBACK(int) vdDiscardHelperAsync(PVDIOCTX pIoCtx)
2960{
2961 int rc = VINF_SUCCESS;
2962 PVDISK pDisk = pIoCtx->pDisk;
2963 PCRTRANGE paRanges = pIoCtx->Req.Discard.paRanges;
2964 unsigned cRanges = pIoCtx->Req.Discard.cRanges;
2965 PVDDISCARDSTATE pDiscard = pDisk->pDiscard;
2966
2967 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
2968
2969 /* Check if the I/O context processed all ranges. */
2970 if ( pIoCtx->Req.Discard.idxRange == cRanges
2971 && !pIoCtx->Req.Discard.cbDiscardLeft)
2972 {
2973 LogFlowFunc(("All ranges discarded, completing\n"));
2974 vdIoCtxUnlockDisk(pDisk, pIoCtx, true /* fProcessDeferredReqs*/);
2975 return VINF_SUCCESS;
2976 }
2977
2978 if (pDisk->pIoCtxLockOwner != pIoCtx)
2979 rc = vdIoCtxLockDisk(pDisk, pIoCtx);
2980
2981 if (RT_SUCCESS(rc))
2982 {
2983 uint64_t offStart = pIoCtx->Req.Discard.offCur;
2984 size_t cbDiscardLeft = pIoCtx->Req.Discard.cbDiscardLeft;
2985 size_t cbThisDiscard;
2986
2987 pDisk->uOffsetStartLocked = offStart;
2988 pDisk->uOffsetEndLocked = offStart + cbDiscardLeft;
2989
2990 if (RT_UNLIKELY(!pDiscard))
2991 {
2992 pDiscard = vdDiscardStateCreate();
2993 if (!pDiscard)
2994 return VERR_NO_MEMORY;
2995
2996 pDisk->pDiscard = pDiscard;
2997 }
2998
2999 if (!pIoCtx->Req.Discard.cbDiscardLeft)
3000 {
3001 offStart = paRanges[pIoCtx->Req.Discard.idxRange].offStart;
3002 cbDiscardLeft = paRanges[pIoCtx->Req.Discard.idxRange].cbRange;
3003 LogFlowFunc(("New range descriptor loaded (%u) offStart=%llu cbDiscard=%zu\n",
3004 pIoCtx->Req.Discard.idxRange, offStart, cbDiscardLeft));
3005 pIoCtx->Req.Discard.idxRange++;
3006 }
3007
3008 /* Look for a matching block in the AVL tree first. */
3009 PVDDISCARDBLOCK pBlock = (PVDDISCARDBLOCK)RTAvlrU64GetBestFit(pDiscard->pTreeBlocks, offStart, false);
3010 if (!pBlock || pBlock->Core.KeyLast < offStart)
3011 {
3012 PVDDISCARDBLOCK pBlockAbove = (PVDDISCARDBLOCK)RTAvlrU64GetBestFit(pDiscard->pTreeBlocks, offStart, true);
3013
3014 /* Clip range to remain in the current block. */
3015 if (pBlockAbove)
3016 cbThisDiscard = RT_MIN(cbDiscardLeft, pBlockAbove->Core.KeyLast - offStart + 1);
3017 else
3018 cbThisDiscard = cbDiscardLeft;
3019
3020 Assert(!(cbThisDiscard % 512));
3021 pIoCtx->Req.Discard.pBlock = NULL;
3022 pIoCtx->pfnIoCtxTransferNext = vdDiscardCurrentRangeAsync;
3023 }
3024 else
3025 {
3026 /* Range lies partly in the block, update allocation bitmap. */
3027 int32_t idxStart, idxEnd;
3028
3029 cbThisDiscard = RT_MIN(cbDiscardLeft, pBlock->Core.KeyLast - offStart + 1);
3030
3031 AssertPtr(pBlock);
3032
3033 Assert(!(cbThisDiscard % 512));
3034 Assert(!((offStart - pBlock->Core.Key) % 512));
3035
3036 idxStart = (offStart - pBlock->Core.Key) / 512;
3037 idxEnd = idxStart + (int32_t)(cbThisDiscard / 512);
3038
3039 ASMBitClearRange(pBlock->pbmAllocated, idxStart, idxEnd);
3040
3041 cbDiscardLeft -= cbThisDiscard;
3042 offStart += cbThisDiscard;
3043
3044 /* Call the backend to discard the block if it is completely unallocated now. */
3045 if (ASMBitFirstSet((volatile void *)pBlock->pbmAllocated, (uint32_t)(pBlock->cbDiscard / 512)) == -1)
3046 {
3047 pIoCtx->Req.Discard.pBlock = pBlock;
3048 pIoCtx->pfnIoCtxTransferNext = vdDiscardWholeBlockAsync;
3049 rc = VINF_SUCCESS;
3050 }
3051 else
3052 {
3053 RTListNodeRemove(&pBlock->NodeLru);
3054 RTListPrepend(&pDiscard->ListLru, &pBlock->NodeLru);
3055
3056 /* Start with next range. */
3057 pIoCtx->pfnIoCtxTransferNext = vdDiscardHelperAsync;
3058 rc = VINF_SUCCESS;
3059 }
3060 }
3061
3062 /* Save state in the context. */
3063 pIoCtx->Req.Discard.offCur = offStart;
3064 pIoCtx->Req.Discard.cbDiscardLeft = cbDiscardLeft;
3065 pIoCtx->Req.Discard.cbThisDiscard = cbThisDiscard;
3066 }
3067
3068 LogFlowFunc(("returns rc=%Rrc\n", rc));
3069 return rc;
3070}
3071
3072/**
3073 * VD async I/O interface open callback.
3074 */
3075static DECLCALLBACK(int) vdIOOpenFallback(void *pvUser, const char *pszLocation,
3076 uint32_t fOpen, PFNVDCOMPLETED pfnCompleted,
3077 void **ppStorage)
3078{
3079 RT_NOREF1(pvUser);
3080 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)RTMemAllocZ(sizeof(VDIIOFALLBACKSTORAGE));
3081
3082 if (!pStorage)
3083 return VERR_NO_MEMORY;
3084
3085 pStorage->pfnCompleted = pfnCompleted;
3086
3087 /* Open the file. */
3088 int rc = RTFileOpen(&pStorage->File, pszLocation, fOpen);
3089 if (RT_SUCCESS(rc))
3090 {
3091 *ppStorage = pStorage;
3092 return VINF_SUCCESS;
3093 }
3094
3095 RTMemFree(pStorage);
3096 return rc;
3097}
3098
3099/**
3100 * VD async I/O interface close callback.
3101 */
3102static DECLCALLBACK(int) vdIOCloseFallback(void *pvUser, void *pvStorage)
3103{
3104 RT_NOREF1(pvUser);
3105 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
3106
3107 RTFileClose(pStorage->File);
3108 RTMemFree(pStorage);
3109 return VINF_SUCCESS;
3110}
3111
3112static DECLCALLBACK(int) vdIODeleteFallback(void *pvUser, const char *pcszFilename)
3113{
3114 RT_NOREF1(pvUser);
3115 return RTFileDelete(pcszFilename);
3116}
3117
3118static DECLCALLBACK(int) vdIOMoveFallback(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
3119{
3120 RT_NOREF1(pvUser);
3121 return RTFileMove(pcszSrc, pcszDst, fMove);
3122}
3123
3124static DECLCALLBACK(int) vdIOGetFreeSpaceFallback(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
3125{
3126 RT_NOREF1(pvUser);
3127 return RTFsQuerySizes(pcszFilename, NULL, pcbFreeSpace, NULL, NULL);
3128}
3129
3130static DECLCALLBACK(int) vdIOGetModificationTimeFallback(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
3131{
3132 RT_NOREF1(pvUser);
3133 RTFSOBJINFO info;
3134 int rc = RTPathQueryInfo(pcszFilename, &info, RTFSOBJATTRADD_NOTHING);
3135 if (RT_SUCCESS(rc))
3136 *pModificationTime = info.ModificationTime;
3137 return rc;
3138}
3139
3140/**
3141 * VD async I/O interface callback for retrieving the file size.
3142 */
3143static DECLCALLBACK(int) vdIOGetSizeFallback(void *pvUser, void *pvStorage, uint64_t *pcbSize)
3144{
3145 RT_NOREF1(pvUser);
3146 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
3147
3148 return RTFileGetSize(pStorage->File, pcbSize);
3149}
3150
3151/**
3152 * VD async I/O interface callback for setting the file size.
3153 */
3154static DECLCALLBACK(int) vdIOSetSizeFallback(void *pvUser, void *pvStorage, uint64_t cbSize)
3155{
3156 RT_NOREF1(pvUser);
3157 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
3158
3159 return RTFileSetSize(pStorage->File, cbSize);
3160}
3161
3162/**
3163 * VD async I/O interface callback for setting the file allocation size.
3164 */
3165static DECLCALLBACK(int) vdIOSetAllocationSizeFallback(void *pvUser, void *pvStorage, uint64_t cbSize,
3166 uint32_t fFlags)
3167{
3168 RT_NOREF2(pvUser, fFlags);
3169 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
3170
3171 return RTFileSetAllocationSize(pStorage->File, cbSize, RTFILE_ALLOC_SIZE_F_DEFAULT);
3172}
3173
3174/**
3175 * VD async I/O interface callback for a synchronous write to the file.
3176 */
3177static DECLCALLBACK(int) vdIOWriteSyncFallback(void *pvUser, void *pvStorage, uint64_t uOffset,
3178 const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
3179{
3180 RT_NOREF1(pvUser);
3181 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
3182
3183 return RTFileWriteAt(pStorage->File, uOffset, pvBuf, cbWrite, pcbWritten);
3184}
3185
3186/**
3187 * VD async I/O interface callback for a synchronous read from the file.
3188 */
3189static DECLCALLBACK(int) vdIOReadSyncFallback(void *pvUser, void *pvStorage, uint64_t uOffset,
3190 void *pvBuf, size_t cbRead, size_t *pcbRead)
3191{
3192 RT_NOREF1(pvUser);
3193 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
3194
3195 return RTFileReadAt(pStorage->File, uOffset, pvBuf, cbRead, pcbRead);
3196}
3197
3198/**
3199 * VD async I/O interface callback for a synchronous flush of the file data.
3200 */
3201static DECLCALLBACK(int) vdIOFlushSyncFallback(void *pvUser, void *pvStorage)
3202{
3203 RT_NOREF1(pvUser);
3204 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
3205
3206 return RTFileFlush(pStorage->File);
3207}
3208
3209/**
3210 * VD async I/O interface callback for a asynchronous read from the file.
3211 */
3212static DECLCALLBACK(int) vdIOReadAsyncFallback(void *pvUser, void *pStorage, uint64_t uOffset,
3213 PCRTSGSEG paSegments, size_t cSegments,
3214 size_t cbRead, void *pvCompletion,
3215 void **ppTask)
3216{
3217 RT_NOREF8(pvUser, pStorage, uOffset, paSegments, cSegments, cbRead, pvCompletion, ppTask);
3218 return VERR_NOT_IMPLEMENTED;
3219}
3220
3221/**
3222 * VD async I/O interface callback for a asynchronous write to the file.
3223 */
3224static DECLCALLBACK(int) vdIOWriteAsyncFallback(void *pvUser, void *pStorage, uint64_t uOffset,
3225 PCRTSGSEG paSegments, size_t cSegments,
3226 size_t cbWrite, void *pvCompletion,
3227 void **ppTask)
3228{
3229 RT_NOREF8(pvUser, pStorage, uOffset, paSegments, cSegments, cbWrite, pvCompletion, ppTask);
3230 return VERR_NOT_IMPLEMENTED;
3231}
3232
3233/**
3234 * VD async I/O interface callback for a asynchronous flush of the file data.
3235 */
3236static DECLCALLBACK(int) vdIOFlushAsyncFallback(void *pvUser, void *pStorage,
3237 void *pvCompletion, void **ppTask)
3238{
3239 RT_NOREF4(pvUser, pStorage, pvCompletion, ppTask);
3240 return VERR_NOT_IMPLEMENTED;
3241}
3242
3243/**
3244 * Internal - Continues an I/O context after
3245 * it was halted because of an active transfer.
3246 */
3247static int vdIoCtxContinue(PVDIOCTX pIoCtx, int rcReq)
3248{
3249 PVDISK pDisk = pIoCtx->pDisk;
3250 int rc = VINF_SUCCESS;
3251
3252 VD_IS_LOCKED(pDisk);
3253
3254 if (RT_FAILURE(rcReq))
3255 ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rcReq, VINF_SUCCESS);
3256
3257 if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_BLOCKED))
3258 {
3259 /* Continue the transfer */
3260 rc = vdIoCtxProcessLocked(pIoCtx);
3261
3262 if ( rc == VINF_VD_ASYNC_IO_FINISHED
3263 && ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
3264 {
3265 LogFlowFunc(("I/O context completed pIoCtx=%#p\n", pIoCtx));
3266 if (pIoCtx->pIoCtxParent)
3267 {
3268 PVDIOCTX pIoCtxParent = pIoCtx->pIoCtxParent;
3269
3270 Assert(!pIoCtxParent->pIoCtxParent);
3271 if (RT_FAILURE(pIoCtx->rcReq))
3272 ASMAtomicCmpXchgS32(&pIoCtxParent->rcReq, pIoCtx->rcReq, VINF_SUCCESS);
3273
3274 ASMAtomicDecU32(&pIoCtxParent->cDataTransfersPending);
3275
3276 if (pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE)
3277 {
3278 LogFlowFunc(("I/O context transferred %u bytes for the parent pIoCtxParent=%p\n",
3279 pIoCtx->Type.Child.cbTransferParent, pIoCtxParent));
3280
3281 /* Update the parent state. */
3282 Assert(pIoCtxParent->Req.Io.cbTransferLeft >= pIoCtx->Type.Child.cbTransferParent);
3283 ASMAtomicSubU32(&pIoCtxParent->Req.Io.cbTransferLeft, (uint32_t)pIoCtx->Type.Child.cbTransferParent);
3284 }
3285 else
3286 Assert(pIoCtx->enmTxDir == VDIOCTXTXDIR_FLUSH);
3287
3288 /*
3289 * A completed child write means that we finished growing the image.
3290 * We have to process any pending writes now.
3291 */
3292 vdIoCtxUnlockDisk(pDisk, pIoCtxParent, false /* fProcessDeferredReqs */);
3293
3294 /* Unblock the parent */
3295 pIoCtxParent->fFlags &= ~VDIOCTX_FLAGS_BLOCKED;
3296
3297 rc = vdIoCtxProcessLocked(pIoCtxParent);
3298
3299 if ( rc == VINF_VD_ASYNC_IO_FINISHED
3300 && ASMAtomicCmpXchgBool(&pIoCtxParent->fComplete, true, false))
3301 {
3302 LogFlowFunc(("Parent I/O context completed pIoCtxParent=%#p rcReq=%Rrc\n", pIoCtxParent, pIoCtxParent->rcReq));
3303 vdIoCtxRootComplete(pDisk, pIoCtxParent);
3304 vdThreadFinishWrite(pDisk);
3305 vdIoCtxFree(pDisk, pIoCtxParent);
3306 vdDiskProcessBlockedIoCtx(pDisk);
3307 }
3308 else if (!vdIoCtxIsDiskLockOwner(pDisk, pIoCtx))
3309 {
3310 /* Process any pending writes if the current request didn't caused another growing. */
3311 vdDiskProcessBlockedIoCtx(pDisk);
3312 }
3313 }
3314 else
3315 {
3316 if (pIoCtx->enmTxDir == VDIOCTXTXDIR_FLUSH)
3317 {
3318 vdIoCtxUnlockDisk(pDisk, pIoCtx, true /* fProcessDerredReqs */);
3319 vdThreadFinishWrite(pDisk);
3320 }
3321 else if ( pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE
3322 || pIoCtx->enmTxDir == VDIOCTXTXDIR_DISCARD)
3323 vdThreadFinishWrite(pDisk);
3324 else
3325 {
3326 Assert(pIoCtx->enmTxDir == VDIOCTXTXDIR_READ);
3327 vdThreadFinishRead(pDisk);
3328 }
3329
3330 LogFlowFunc(("I/O context completed pIoCtx=%#p rcReq=%Rrc\n", pIoCtx, pIoCtx->rcReq));
3331 vdIoCtxRootComplete(pDisk, pIoCtx);
3332 }
3333
3334 vdIoCtxFree(pDisk, pIoCtx);
3335 }
3336 }
3337
3338 return VINF_SUCCESS;
3339}
3340
3341/**
3342 * Internal - Called when user transfer completed.
3343 */
3344static int vdUserXferCompleted(PVDIOSTORAGE pIoStorage, PVDIOCTX pIoCtx,
3345 PFNVDXFERCOMPLETED pfnComplete, void *pvUser,
3346 size_t cbTransfer, int rcReq)
3347{
3348 int rc = VINF_SUCCESS;
3349 PVDISK pDisk = pIoCtx->pDisk;
3350
3351 LogFlowFunc(("pIoStorage=%#p pIoCtx=%#p pfnComplete=%#p pvUser=%#p cbTransfer=%zu rcReq=%Rrc\n",
3352 pIoStorage, pIoCtx, pfnComplete, pvUser, cbTransfer, rcReq));
3353
3354 VD_IS_LOCKED(pDisk);
3355
3356 Assert(pIoCtx->Req.Io.cbTransferLeft >= cbTransfer);
3357 ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, (uint32_t)cbTransfer); Assert(cbTransfer == (uint32_t)cbTransfer);
3358 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
3359
3360 if (pfnComplete)
3361 rc = pfnComplete(pIoStorage->pVDIo->pBackendData, pIoCtx, pvUser, rcReq);
3362
3363 if (RT_SUCCESS(rc))
3364 rc = vdIoCtxContinue(pIoCtx, rcReq);
3365 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
3366 rc = VINF_SUCCESS;
3367
3368 return rc;
3369}
3370
3371static void vdIoCtxContinueDeferredList(PVDIOSTORAGE pIoStorage, PRTLISTANCHOR pListWaiting,
3372 PFNVDXFERCOMPLETED pfnComplete, void *pvUser, int rcReq)
3373{
3374 LogFlowFunc(("pIoStorage=%#p pListWaiting=%#p pfnComplete=%#p pvUser=%#p rcReq=%Rrc\n",
3375 pIoStorage, pListWaiting, pfnComplete, pvUser, rcReq));
3376
3377 /* Go through the waiting list and continue the I/O contexts. */
3378 while (!RTListIsEmpty(pListWaiting))
3379 {
3380 int rc = VINF_SUCCESS;
3381 PVDIOCTXDEFERRED pDeferred = RTListGetFirst(pListWaiting, VDIOCTXDEFERRED, NodeDeferred);
3382 PVDIOCTX pIoCtx = pDeferred->pIoCtx;
3383 RTListNodeRemove(&pDeferred->NodeDeferred);
3384
3385 RTMemFree(pDeferred);
3386 ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
3387
3388 if (pfnComplete)
3389 rc = pfnComplete(pIoStorage->pVDIo->pBackendData, pIoCtx, pvUser, rcReq);
3390
3391 LogFlow(("Completion callback for I/O context %#p returned %Rrc\n", pIoCtx, rc));
3392
3393 if (RT_SUCCESS(rc))
3394 {
3395 rc = vdIoCtxContinue(pIoCtx, rcReq);
3396 AssertRC(rc);
3397 }
3398 else
3399 Assert(rc == VERR_VD_ASYNC_IO_IN_PROGRESS);
3400 }
3401}
3402
3403/**
3404 * Internal - Called when a meta transfer completed.
3405 */
3406static int vdMetaXferCompleted(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser,
3407 PVDMETAXFER pMetaXfer, int rcReq)
3408{
3409 PVDISK pDisk = pIoStorage->pVDIo->pDisk;
3410 RTLISTNODE ListIoCtxWaiting;
3411 bool fFlush;
3412
3413 LogFlowFunc(("pIoStorage=%#p pfnComplete=%#p pvUser=%#p pMetaXfer=%#p rcReq=%Rrc\n",
3414 pIoStorage, pfnComplete, pvUser, pMetaXfer, rcReq));
3415
3416 VD_IS_LOCKED(pDisk);
3417
3418 fFlush = VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_FLUSH;
3419
3420 if (!fFlush)
3421 {
3422 RTListMove(&ListIoCtxWaiting, &pMetaXfer->ListIoCtxWaiting);
3423
3424 if (RT_FAILURE(rcReq))
3425 {
3426 /* Remove from the AVL tree. */
3427 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
3428 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
3429 Assert(fRemoved); NOREF(fRemoved);
3430 /* If this was a write check if there is a shadow buffer with updated data. */
3431 if (pMetaXfer->pbDataShw)
3432 {
3433 Assert(VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_WRITE);
3434 Assert(!RTListIsEmpty(&pMetaXfer->ListIoCtxShwWrites));
3435 RTListConcatenate(&ListIoCtxWaiting, &pMetaXfer->ListIoCtxShwWrites);
3436 RTMemFree(pMetaXfer->pbDataShw);
3437 pMetaXfer->pbDataShw = NULL;
3438 }
3439 RTMemFree(pMetaXfer);
3440 }
3441 else
3442 {
3443 /* Increase the reference counter to make sure it doesn't go away before the last context is processed. */
3444 pMetaXfer->cRefs++;
3445 }
3446 }
3447 else
3448 RTListMove(&ListIoCtxWaiting, &pMetaXfer->ListIoCtxWaiting);
3449
3450 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
3451 vdIoCtxContinueDeferredList(pIoStorage, &ListIoCtxWaiting, pfnComplete, pvUser, rcReq);
3452
3453 /*
3454 * If there is a shadow buffer and the previous write was successful update with the
3455 * new data and trigger a new write.
3456 */
3457 if ( pMetaXfer->pbDataShw
3458 && RT_SUCCESS(rcReq)
3459 && VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE)
3460 {
3461 LogFlowFunc(("pMetaXfer=%#p Updating from shadow buffer and triggering new write\n", pMetaXfer));
3462 memcpy(pMetaXfer->abData, pMetaXfer->pbDataShw, pMetaXfer->cbMeta);
3463 RTMemFree(pMetaXfer->pbDataShw);
3464 pMetaXfer->pbDataShw = NULL;
3465 Assert(!RTListIsEmpty(&pMetaXfer->ListIoCtxShwWrites));
3466
3467 /* Setup a new I/O write. */
3468 PVDIOTASK pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvUser, pMetaXfer);
3469 if (RT_LIKELY(pIoTask))
3470 {
3471 void *pvTask = NULL;
3472 RTSGSEG Seg;
3473
3474 Seg.cbSeg = pMetaXfer->cbMeta;
3475 Seg.pvSeg = pMetaXfer->abData;
3476
3477 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_WRITE);
3478 rcReq = pIoStorage->pVDIo->pInterfaceIo->pfnWriteAsync(pIoStorage->pVDIo->pInterfaceIo->Core.pvUser,
3479 pIoStorage->pStorage,
3480 pMetaXfer->Core.Key, &Seg, 1,
3481 pMetaXfer->cbMeta, pIoTask,
3482 &pvTask);
3483 if ( RT_SUCCESS(rcReq)
3484 || rcReq != VERR_VD_ASYNC_IO_IN_PROGRESS)
3485 {
3486 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
3487 vdIoTaskFree(pDisk, pIoTask);
3488 }
3489 else
3490 RTListMove(&pMetaXfer->ListIoCtxWaiting, &pMetaXfer->ListIoCtxShwWrites);
3491 }
3492 else
3493 rcReq = VERR_NO_MEMORY;
3494
3495 /* Cleanup if there was an error or the request completed already. */
3496 if (rcReq != VERR_VD_ASYNC_IO_IN_PROGRESS)
3497 vdIoCtxContinueDeferredList(pIoStorage, &pMetaXfer->ListIoCtxShwWrites, pfnComplete, pvUser, rcReq);
3498 }
3499
3500 /* Remove if not used anymore. */
3501 if (!fFlush)
3502 {
3503 pMetaXfer->cRefs--;
3504 if (!pMetaXfer->cRefs && RTListIsEmpty(&pMetaXfer->ListIoCtxWaiting))
3505 {
3506 /* Remove from the AVL tree. */
3507 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
3508 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
3509 Assert(fRemoved); NOREF(fRemoved);
3510 RTMemFree(pMetaXfer);
3511 }
3512 }
3513 else if (fFlush)
3514 RTMemFree(pMetaXfer);
3515
3516 return VINF_SUCCESS;
3517}
3518
3519/**
3520 * Processes a list of waiting I/O tasks. The disk lock must be held by caller.
3521 *
3522 * @returns nothing.
3523 * @param pDisk The disk to process the list for.
3524 */
3525static void vdIoTaskProcessWaitingList(PVDISK pDisk)
3526{
3527 LogFlowFunc(("pDisk=%#p\n", pDisk));
3528
3529 VD_IS_LOCKED(pDisk);
3530
3531 PVDIOTASK pHead = ASMAtomicXchgPtrT(&pDisk->pIoTasksPendingHead, NULL, PVDIOTASK);
3532
3533 Log(("I/O task list cleared\n"));
3534
3535 /* Reverse order. */
3536 PVDIOTASK pCur = pHead;
3537 pHead = NULL;
3538 while (pCur)
3539 {
3540 PVDIOTASK pInsert = pCur;
3541 pCur = pCur->pNext;
3542 pInsert->pNext = pHead;
3543 pHead = pInsert;
3544 }
3545
3546 while (pHead)
3547 {
3548 PVDIOSTORAGE pIoStorage = pHead->pIoStorage;
3549
3550 if (!pHead->fMeta)
3551 vdUserXferCompleted(pIoStorage, pHead->Type.User.pIoCtx,
3552 pHead->pfnComplete, pHead->pvUser,
3553 pHead->Type.User.cbTransfer, pHead->rcReq);
3554 else
3555 vdMetaXferCompleted(pIoStorage, pHead->pfnComplete, pHead->pvUser,
3556 pHead->Type.Meta.pMetaXfer, pHead->rcReq);
3557
3558 pCur = pHead;
3559 pHead = pHead->pNext;
3560 vdIoTaskFree(pDisk, pCur);
3561 }
3562}
3563
3564/**
3565 * Process any I/O context on the halted list.
3566 *
3567 * @returns nothing.
3568 * @param pDisk The disk.
3569 */
3570static void vdIoCtxProcessHaltedList(PVDISK pDisk)
3571{
3572 LogFlowFunc(("pDisk=%#p\n", pDisk));
3573
3574 VD_IS_LOCKED(pDisk);
3575
3576 /* Get the waiting list and process it in FIFO order. */
3577 PVDIOCTX pIoCtxHead = ASMAtomicXchgPtrT(&pDisk->pIoCtxHaltedHead, NULL, PVDIOCTX);
3578
3579 /* Reverse it. */
3580 PVDIOCTX pCur = pIoCtxHead;
3581 pIoCtxHead = NULL;
3582 while (pCur)
3583 {
3584 PVDIOCTX pInsert = pCur;
3585 pCur = pCur->pIoCtxNext;
3586 pInsert->pIoCtxNext = pIoCtxHead;
3587 pIoCtxHead = pInsert;
3588 }
3589
3590 /* Process now. */
3591 pCur = pIoCtxHead;
3592 while (pCur)
3593 {
3594 PVDIOCTX pTmp = pCur;
3595
3596 pCur = pCur->pIoCtxNext;
3597 pTmp->pIoCtxNext = NULL;
3598
3599 /* Continue */
3600 pTmp->fFlags &= ~VDIOCTX_FLAGS_BLOCKED;
3601 vdIoCtxContinue(pTmp, pTmp->rcReq);
3602 }
3603}
3604
3605/**
3606 * Unlock the disk and process pending tasks.
3607 *
3608 * @returns VBox status code.
3609 * @param pDisk The disk to unlock.
3610 * @param pIoCtxRc The I/O context to get the status code from, optional.
3611 */
3612static int vdDiskUnlock(PVDISK pDisk, PVDIOCTX pIoCtxRc)
3613{
3614 int rc = VINF_SUCCESS;
3615
3616 VD_IS_LOCKED(pDisk);
3617
3618 /*
3619 * Process the list of waiting I/O tasks first
3620 * because they might complete I/O contexts.
3621 * Same for the list of halted I/O contexts.
3622 * Afterwards comes the list of new I/O contexts.
3623 */
3624 vdIoTaskProcessWaitingList(pDisk);
3625 vdIoCtxProcessHaltedList(pDisk);
3626 rc = vdDiskProcessWaitingIoCtx(pDisk, pIoCtxRc);
3627 ASMAtomicXchgBool(&pDisk->fLocked, false);
3628
3629 /*
3630 * Need to check for new I/O tasks and waiting I/O contexts now
3631 * again as other threads might added them while we processed
3632 * previous lists.
3633 */
3634 while ( ASMAtomicUoReadPtrT(&pDisk->pIoCtxHead, PVDIOCTX) != NULL
3635 || ASMAtomicUoReadPtrT(&pDisk->pIoTasksPendingHead, PVDIOTASK) != NULL
3636 || ASMAtomicUoReadPtrT(&pDisk->pIoCtxHaltedHead, PVDIOCTX) != NULL)
3637 {
3638 /* Try lock disk again. */
3639 if (ASMAtomicCmpXchgBool(&pDisk->fLocked, true, false))
3640 {
3641 vdIoTaskProcessWaitingList(pDisk);
3642 vdIoCtxProcessHaltedList(pDisk);
3643 vdDiskProcessWaitingIoCtx(pDisk, NULL);
3644 ASMAtomicXchgBool(&pDisk->fLocked, false);
3645 }
3646 else /* Let the other thread everything when he unlocks the disk. */
3647 break;
3648 }
3649
3650 return rc;
3651}
3652
3653/**
3654 * Try to lock the disk to complete pressing of the I/O task.
3655 * The completion is deferred if the disk is locked already.
3656 *
3657 * @returns nothing.
3658 * @param pIoTask The I/O task to complete.
3659 */
3660static void vdXferTryLockDiskDeferIoTask(PVDIOTASK pIoTask)
3661{
3662 PVDIOSTORAGE pIoStorage = pIoTask->pIoStorage;
3663 PVDISK pDisk = pIoStorage->pVDIo->pDisk;
3664
3665 Log(("Deferring I/O task pIoTask=%p\n", pIoTask));
3666
3667 /* Put it on the waiting list. */
3668 PVDIOTASK pNext = ASMAtomicUoReadPtrT(&pDisk->pIoTasksPendingHead, PVDIOTASK);
3669 PVDIOTASK pHeadOld;
3670 pIoTask->pNext = pNext;
3671 while (!ASMAtomicCmpXchgExPtr(&pDisk->pIoTasksPendingHead, pIoTask, pNext, &pHeadOld))
3672 {
3673 pNext = pHeadOld;
3674 Assert(pNext != pIoTask);
3675 pIoTask->pNext = pNext;
3676 ASMNopPause();
3677 }
3678
3679 if (ASMAtomicCmpXchgBool(&pDisk->fLocked, true, false))
3680 {
3681 /* Release disk lock, it will take care of processing all lists. */
3682 vdDiskUnlock(pDisk, NULL);
3683 }
3684}
3685
3686static DECLCALLBACK(int) vdIOIntReqCompleted(void *pvUser, int rcReq)
3687{
3688 PVDIOTASK pIoTask = (PVDIOTASK)pvUser;
3689
3690 LogFlowFunc(("Task completed pIoTask=%#p\n", pIoTask));
3691
3692 pIoTask->rcReq = rcReq;
3693 vdXferTryLockDiskDeferIoTask(pIoTask);
3694 return VINF_SUCCESS;
3695}
3696
3697/**
3698 * VD I/O interface callback for opening a file.
3699 */
3700static DECLCALLBACK(int) vdIOIntOpen(void *pvUser, const char *pszLocation,
3701 unsigned uOpenFlags, PPVDIOSTORAGE ppIoStorage)
3702{
3703 int rc = VINF_SUCCESS;
3704 PVDIO pVDIo = (PVDIO)pvUser;
3705 PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTMemAllocZ(sizeof(VDIOSTORAGE));
3706
3707 if (!pIoStorage)
3708 return VERR_NO_MEMORY;
3709
3710 /* Create the AVl tree. */
3711 pIoStorage->pTreeMetaXfers = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
3712 if (pIoStorage->pTreeMetaXfers)
3713 {
3714 rc = pVDIo->pInterfaceIo->pfnOpen(pVDIo->pInterfaceIo->Core.pvUser,
3715 pszLocation, uOpenFlags,
3716 vdIOIntReqCompleted,
3717 &pIoStorage->pStorage);
3718 if (RT_SUCCESS(rc))
3719 {
3720 pIoStorage->pVDIo = pVDIo;
3721 *ppIoStorage = pIoStorage;
3722 return VINF_SUCCESS;
3723 }
3724
3725 RTMemFree(pIoStorage->pTreeMetaXfers);
3726 }
3727 else
3728 rc = VERR_NO_MEMORY;
3729
3730 RTMemFree(pIoStorage);
3731 return rc;
3732}
3733
3734static DECLCALLBACK(int) vdIOIntTreeMetaXferDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
3735{
3736 RT_NOREF2(pNode, pvUser);
3737 AssertMsgFailed(("Tree should be empty at this point!\n"));
3738 return VINF_SUCCESS;
3739}
3740
3741static DECLCALLBACK(int) vdIOIntClose(void *pvUser, PVDIOSTORAGE pIoStorage)
3742{
3743 int rc = VINF_SUCCESS;
3744 PVDIO pVDIo = (PVDIO)pvUser;
3745
3746 /* We free everything here, even if closing the file failed for some reason. */
3747 rc = pVDIo->pInterfaceIo->pfnClose(pVDIo->pInterfaceIo->Core.pvUser, pIoStorage->pStorage);
3748 RTAvlrFileOffsetDestroy(pIoStorage->pTreeMetaXfers, vdIOIntTreeMetaXferDestroy, NULL);
3749 RTMemFree(pIoStorage->pTreeMetaXfers);
3750 RTMemFree(pIoStorage);
3751 return rc;
3752}
3753
3754static DECLCALLBACK(int) vdIOIntDelete(void *pvUser, const char *pcszFilename)
3755{
3756 PVDIO pVDIo = (PVDIO)pvUser;
3757 return pVDIo->pInterfaceIo->pfnDelete(pVDIo->pInterfaceIo->Core.pvUser,
3758 pcszFilename);
3759}
3760
3761static DECLCALLBACK(int) vdIOIntMove(void *pvUser, const char *pcszSrc, const char *pcszDst,
3762 unsigned fMove)
3763{
3764 PVDIO pVDIo = (PVDIO)pvUser;
3765 return pVDIo->pInterfaceIo->pfnMove(pVDIo->pInterfaceIo->Core.pvUser,
3766 pcszSrc, pcszDst, fMove);
3767}
3768
3769static DECLCALLBACK(int) vdIOIntGetFreeSpace(void *pvUser, const char *pcszFilename,
3770 int64_t *pcbFreeSpace)
3771{
3772 PVDIO pVDIo = (PVDIO)pvUser;
3773 return pVDIo->pInterfaceIo->pfnGetFreeSpace(pVDIo->pInterfaceIo->Core.pvUser,
3774 pcszFilename, pcbFreeSpace);
3775}
3776
3777static DECLCALLBACK(int) vdIOIntGetModificationTime(void *pvUser, const char *pcszFilename,
3778 PRTTIMESPEC pModificationTime)
3779{
3780 PVDIO pVDIo = (PVDIO)pvUser;
3781 return pVDIo->pInterfaceIo->pfnGetModificationTime(pVDIo->pInterfaceIo->Core.pvUser,
3782 pcszFilename, pModificationTime);
3783}
3784
3785static DECLCALLBACK(int) vdIOIntGetSize(void *pvUser, PVDIOSTORAGE pIoStorage,
3786 uint64_t *pcbSize)
3787{
3788 PVDIO pVDIo = (PVDIO)pvUser;
3789 return pVDIo->pInterfaceIo->pfnGetSize(pVDIo->pInterfaceIo->Core.pvUser,
3790 pIoStorage->pStorage, pcbSize);
3791}
3792
3793static DECLCALLBACK(int) vdIOIntSetSize(void *pvUser, PVDIOSTORAGE pIoStorage,
3794 uint64_t cbSize)
3795{
3796 PVDIO pVDIo = (PVDIO)pvUser;
3797 return pVDIo->pInterfaceIo->pfnSetSize(pVDIo->pInterfaceIo->Core.pvUser,
3798 pIoStorage->pStorage, cbSize);
3799}
3800
3801static DECLCALLBACK(int) vdIOIntSetAllocationSize(void *pvUser, PVDIOSTORAGE pIoStorage,
3802 uint64_t cbSize, uint32_t fFlags,
3803 PVDINTERFACEPROGRESS pIfProgress,
3804 unsigned uPercentStart, unsigned uPercentSpan)
3805{
3806 PVDIO pVDIo = (PVDIO)pvUser;
3807 int rc = pVDIo->pInterfaceIo->pfnSetAllocationSize(pVDIo->pInterfaceIo->Core.pvUser,
3808 pIoStorage->pStorage, cbSize, fFlags);
3809 if (rc == VERR_NOT_SUPPORTED)
3810 {
3811 /* Fallback if the underlying medium does not support optimized storage allocation. */
3812 uint64_t cbSizeCur = 0;
3813 rc = pVDIo->pInterfaceIo->pfnGetSize(pVDIo->pInterfaceIo->Core.pvUser,
3814 pIoStorage->pStorage, &cbSizeCur);
3815 if (RT_SUCCESS(rc))
3816 {
3817 if (cbSizeCur < cbSize)
3818 {
3819 const size_t cbBuf = 128 * _1K;
3820 void *pvBuf = RTMemTmpAllocZ(cbBuf);
3821 if (RT_LIKELY(pvBuf))
3822 {
3823 uint64_t cbFill = cbSize - cbSizeCur;
3824 uint64_t uOff = 0;
3825
3826 /* Write data to all blocks. */
3827 while ( uOff < cbFill
3828 && RT_SUCCESS(rc))
3829 {
3830 size_t cbChunk = (size_t)RT_MIN(cbFill - uOff, cbBuf);
3831
3832 rc = pVDIo->pInterfaceIo->pfnWriteSync(pVDIo->pInterfaceIo->Core.pvUser,
3833 pIoStorage->pStorage, cbSizeCur + uOff,
3834 pvBuf, cbChunk, NULL);
3835 if (RT_SUCCESS(rc))
3836 {
3837 uOff += cbChunk;
3838
3839 rc = vdIfProgress(pIfProgress, uPercentStart + uOff * uPercentSpan / cbFill);
3840 }
3841 }
3842
3843 RTMemTmpFree(pvBuf);
3844 }
3845 else
3846 rc = VERR_NO_MEMORY;
3847 }
3848 else if (cbSizeCur > cbSize)
3849 rc = pVDIo->pInterfaceIo->pfnSetSize(pVDIo->pInterfaceIo->Core.pvUser,
3850 pIoStorage->pStorage, cbSize);
3851 }
3852 }
3853
3854 if (RT_SUCCESS(rc))
3855 rc = vdIfProgress(pIfProgress, uPercentStart + uPercentSpan);
3856
3857 return rc;
3858}
3859
3860static DECLCALLBACK(int) vdIOIntReadUser(void *pvUser, PVDIOSTORAGE pIoStorage, uint64_t uOffset,
3861 PVDIOCTX pIoCtx, size_t cbRead)
3862{
3863 int rc = VINF_SUCCESS;
3864 PVDIO pVDIo = (PVDIO)pvUser;
3865 PVDISK pDisk = pVDIo->pDisk;
3866
3867 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pIoCtx=%#p cbRead=%u\n",
3868 pvUser, pIoStorage, uOffset, pIoCtx, cbRead));
3869
3870 /** @todo Enable check for sync I/O later. */
3871 if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
3872 VD_IS_LOCKED(pDisk);
3873
3874 Assert(cbRead > 0);
3875
3876 if (pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC)
3877 {
3878 RTSGSEG Seg;
3879 unsigned cSegments = 1;
3880 size_t cbTaskRead = 0;
3881
3882 /* Synchronous I/O contexts only have one buffer segment. */
3883 AssertMsgReturn(pIoCtx->Req.Io.SgBuf.cSegs == 1,
3884 ("Invalid number of buffer segments for synchronous I/O context"),
3885 VERR_INVALID_PARAMETER);
3886
3887 cbTaskRead = RTSgBufSegArrayCreate(&pIoCtx->Req.Io.SgBuf, &Seg, &cSegments, cbRead);
3888 Assert(cbRead == cbTaskRead);
3889 Assert(cSegments == 1);
3890 rc = pVDIo->pInterfaceIo->pfnReadSync(pVDIo->pInterfaceIo->Core.pvUser,
3891 pIoStorage->pStorage, uOffset,
3892 Seg.pvSeg, cbRead, NULL);
3893 if (RT_SUCCESS(rc))
3894 {
3895 Assert(cbRead == (uint32_t)cbRead);
3896 ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, (uint32_t)cbRead);
3897 }
3898 }
3899 else
3900 {
3901 /* Build the S/G array and spawn a new I/O task */
3902 while (cbRead)
3903 {
3904 RTSGSEG aSeg[VD_IO_TASK_SEGMENTS_MAX];
3905 unsigned cSegments = VD_IO_TASK_SEGMENTS_MAX;
3906 size_t cbTaskRead = RTSgBufSegArrayCreate(&pIoCtx->Req.Io.SgBuf, aSeg, &cSegments, cbRead);
3907
3908 Assert(cSegments > 0);
3909 Assert(cbTaskRead > 0);
3910 AssertMsg(cbTaskRead <= cbRead, ("Invalid number of bytes to read\n"));
3911
3912 LogFlow(("Reading %u bytes into %u segments\n", cbTaskRead, cSegments));
3913
3914#ifdef RT_STRICT
3915 for (unsigned i = 0; i < cSegments; i++)
3916 AssertMsg(aSeg[i].pvSeg && !(aSeg[i].cbSeg % 512),
3917 ("Segment %u is invalid\n", i));
3918#endif
3919
3920 Assert(cbTaskRead == (uint32_t)cbTaskRead);
3921 PVDIOTASK pIoTask = vdIoTaskUserAlloc(pIoStorage, NULL, NULL, pIoCtx, (uint32_t)cbTaskRead);
3922
3923 if (!pIoTask)
3924 return VERR_NO_MEMORY;
3925
3926 ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
3927
3928 void *pvTask;
3929 Log(("Spawning pIoTask=%p pIoCtx=%p\n", pIoTask, pIoCtx));
3930 rc = pVDIo->pInterfaceIo->pfnReadAsync(pVDIo->pInterfaceIo->Core.pvUser,
3931 pIoStorage->pStorage, uOffset,
3932 aSeg, cSegments, cbTaskRead, pIoTask,
3933 &pvTask);
3934 if (RT_SUCCESS(rc))
3935 {
3936 AssertMsg(cbTaskRead <= pIoCtx->Req.Io.cbTransferLeft, ("Impossible!\n"));
3937 ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, (uint32_t)cbTaskRead);
3938 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
3939 vdIoTaskFree(pDisk, pIoTask);
3940 }
3941 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
3942 {
3943 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
3944 vdIoTaskFree(pDisk, pIoTask);
3945 break;
3946 }
3947
3948 uOffset += cbTaskRead;
3949 cbRead -= cbTaskRead;
3950 }
3951 }
3952
3953 LogFlowFunc(("returns rc=%Rrc\n", rc));
3954 return rc;
3955}
3956
3957static DECLCALLBACK(int) vdIOIntWriteUser(void *pvUser, PVDIOSTORAGE pIoStorage, uint64_t uOffset,
3958 PVDIOCTX pIoCtx, size_t cbWrite, PFNVDXFERCOMPLETED pfnComplete,
3959 void *pvCompleteUser)
3960{
3961 int rc = VINF_SUCCESS;
3962 PVDIO pVDIo = (PVDIO)pvUser;
3963 PVDISK pDisk = pVDIo->pDisk;
3964
3965 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pIoCtx=%#p cbWrite=%u\n",
3966 pvUser, pIoStorage, uOffset, pIoCtx, cbWrite));
3967
3968 /** @todo Enable check for sync I/O later. */
3969 if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
3970 VD_IS_LOCKED(pDisk);
3971
3972 Assert(cbWrite > 0);
3973
3974 if (pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC)
3975 {
3976 RTSGSEG Seg;
3977 unsigned cSegments = 1;
3978 size_t cbTaskWrite = 0;
3979
3980 /* Synchronous I/O contexts only have one buffer segment. */
3981 AssertMsgReturn(pIoCtx->Req.Io.SgBuf.cSegs == 1,
3982 ("Invalid number of buffer segments for synchronous I/O context"),
3983 VERR_INVALID_PARAMETER);
3984
3985 cbTaskWrite = RTSgBufSegArrayCreate(&pIoCtx->Req.Io.SgBuf, &Seg, &cSegments, cbWrite);
3986 Assert(cbWrite == cbTaskWrite);
3987 Assert(cSegments == 1);
3988 rc = pVDIo->pInterfaceIo->pfnWriteSync(pVDIo->pInterfaceIo->Core.pvUser,
3989 pIoStorage->pStorage, uOffset,
3990 Seg.pvSeg, cbWrite, NULL);
3991 if (RT_SUCCESS(rc))
3992 {
3993 Assert(pIoCtx->Req.Io.cbTransferLeft >= cbWrite);
3994 ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, (uint32_t)cbWrite);
3995 }
3996 }
3997 else
3998 {
3999 /* Build the S/G array and spawn a new I/O task */
4000 while (cbWrite)
4001 {
4002 RTSGSEG aSeg[VD_IO_TASK_SEGMENTS_MAX];
4003 unsigned cSegments = VD_IO_TASK_SEGMENTS_MAX;
4004 size_t cbTaskWrite = 0;
4005
4006 cbTaskWrite = RTSgBufSegArrayCreate(&pIoCtx->Req.Io.SgBuf, aSeg, &cSegments, cbWrite);
4007
4008 Assert(cSegments > 0);
4009 Assert(cbTaskWrite > 0);
4010 AssertMsg(cbTaskWrite <= cbWrite, ("Invalid number of bytes to write\n"));
4011
4012 LogFlow(("Writing %u bytes from %u segments\n", cbTaskWrite, cSegments));
4013
4014#ifdef DEBUG
4015 for (unsigned i = 0; i < cSegments; i++)
4016 AssertMsg(aSeg[i].pvSeg && !(aSeg[i].cbSeg % 512),
4017 ("Segment %u is invalid\n", i));
4018#endif
4019
4020 Assert(cbTaskWrite == (uint32_t)cbTaskWrite);
4021 PVDIOTASK pIoTask = vdIoTaskUserAlloc(pIoStorage, pfnComplete, pvCompleteUser, pIoCtx, (uint32_t)cbTaskWrite);
4022
4023 if (!pIoTask)
4024 return VERR_NO_MEMORY;
4025
4026 ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
4027
4028 void *pvTask;
4029 Log(("Spawning pIoTask=%p pIoCtx=%p\n", pIoTask, pIoCtx));
4030 rc = pVDIo->pInterfaceIo->pfnWriteAsync(pVDIo->pInterfaceIo->Core.pvUser,
4031 pIoStorage->pStorage,
4032 uOffset, aSeg, cSegments,
4033 cbTaskWrite, pIoTask, &pvTask);
4034 if (RT_SUCCESS(rc))
4035 {
4036 AssertMsg(cbTaskWrite <= pIoCtx->Req.Io.cbTransferLeft, ("Impossible!\n"));
4037 ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, (uint32_t)cbTaskWrite);
4038 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
4039 vdIoTaskFree(pDisk, pIoTask);
4040 }
4041 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
4042 {
4043 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
4044 vdIoTaskFree(pDisk, pIoTask);
4045 break;
4046 }
4047
4048 uOffset += cbTaskWrite;
4049 cbWrite -= cbTaskWrite;
4050 }
4051 }
4052
4053 LogFlowFunc(("returns rc=%Rrc\n", rc));
4054 return rc;
4055}
4056
4057static DECLCALLBACK(int) vdIOIntReadMeta(void *pvUser, PVDIOSTORAGE pIoStorage, uint64_t uOffset,
4058 void *pvBuf, size_t cbRead, PVDIOCTX pIoCtx,
4059 PPVDMETAXFER ppMetaXfer, PFNVDXFERCOMPLETED pfnComplete,
4060 void *pvCompleteUser)
4061{
4062 PVDIO pVDIo = (PVDIO)pvUser;
4063 PVDISK pDisk = pVDIo->pDisk;
4064 int rc = VINF_SUCCESS;
4065 RTSGSEG Seg;
4066 PVDIOTASK pIoTask;
4067 PVDMETAXFER pMetaXfer = NULL;
4068 void *pvTask = NULL;
4069
4070 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pvBuf=%#p cbRead=%u\n",
4071 pvUser, pIoStorage, uOffset, pvBuf, cbRead));
4072
4073 AssertMsgReturn( pIoCtx
4074 || (!ppMetaXfer && !pfnComplete && !pvCompleteUser),
4075 ("A synchronous metadata read is requested but the parameters are wrong\n"),
4076 VERR_INVALID_POINTER);
4077
4078 /** @todo Enable check for sync I/O later. */
4079 if ( pIoCtx
4080 && !(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
4081 VD_IS_LOCKED(pDisk);
4082
4083 if ( !pIoCtx
4084 || pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC)
4085 {
4086 /* Handle synchronous metadata I/O. */
4087 /** @todo Integrate with metadata transfers below. */
4088 rc = pVDIo->pInterfaceIo->pfnReadSync(pVDIo->pInterfaceIo->Core.pvUser,
4089 pIoStorage->pStorage, uOffset,
4090 pvBuf, cbRead, NULL);
4091 if (ppMetaXfer)
4092 *ppMetaXfer = NULL;
4093 }
4094 else
4095 {
4096 pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGet(pIoStorage->pTreeMetaXfers, uOffset);
4097 if (!pMetaXfer)
4098 {
4099#ifdef RT_STRICT
4100 pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGetBestFit(pIoStorage->pTreeMetaXfers, uOffset, false /* fAbove */);
4101 AssertMsg(!pMetaXfer || (pMetaXfer->Core.Key + (RTFOFF)pMetaXfer->cbMeta <= (RTFOFF)uOffset),
4102 ("Overlapping meta transfers!\n"));
4103#endif
4104
4105 /* Allocate a new meta transfer. */
4106 pMetaXfer = vdMetaXferAlloc(pIoStorage, uOffset, cbRead);
4107 if (!pMetaXfer)
4108 return VERR_NO_MEMORY;
4109
4110 pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvCompleteUser, pMetaXfer);
4111 if (!pIoTask)
4112 {
4113 RTMemFree(pMetaXfer);
4114 return VERR_NO_MEMORY;
4115 }
4116
4117 Seg.cbSeg = cbRead;
4118 Seg.pvSeg = pMetaXfer->abData;
4119
4120 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_READ);
4121 rc = pVDIo->pInterfaceIo->pfnReadAsync(pVDIo->pInterfaceIo->Core.pvUser,
4122 pIoStorage->pStorage,
4123 uOffset, &Seg, 1,
4124 cbRead, pIoTask, &pvTask);
4125
4126 if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
4127 {
4128 bool fInserted = RTAvlrFileOffsetInsert(pIoStorage->pTreeMetaXfers, &pMetaXfer->Core);
4129 Assert(fInserted); NOREF(fInserted);
4130 }
4131 else
4132 RTMemFree(pMetaXfer);
4133
4134 if (RT_SUCCESS(rc))
4135 {
4136 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
4137 vdIoTaskFree(pDisk, pIoTask);
4138 }
4139 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS && !pfnComplete)
4140 rc = VERR_VD_NOT_ENOUGH_METADATA;
4141 }
4142
4143 Assert(VALID_PTR(pMetaXfer) || RT_FAILURE(rc));
4144
4145 if (RT_SUCCESS(rc) || rc == VERR_VD_NOT_ENOUGH_METADATA || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
4146 {
4147 /* If it is pending add the request to the list. */
4148 if (VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_READ)
4149 {
4150 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
4151 AssertPtr(pDeferred);
4152
4153 RTListInit(&pDeferred->NodeDeferred);
4154 pDeferred->pIoCtx = pIoCtx;
4155
4156 ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
4157 RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
4158 rc = VERR_VD_NOT_ENOUGH_METADATA;
4159 }
4160 else
4161 {
4162 /* Transfer the data. */
4163 pMetaXfer->cRefs++;
4164 Assert(pMetaXfer->cbMeta >= cbRead);
4165 Assert(pMetaXfer->Core.Key == (RTFOFF)uOffset);
4166 if (pMetaXfer->pbDataShw)
4167 memcpy(pvBuf, pMetaXfer->pbDataShw, cbRead);
4168 else
4169 memcpy(pvBuf, pMetaXfer->abData, cbRead);
4170 *ppMetaXfer = pMetaXfer;
4171 }
4172 }
4173 }
4174
4175 LogFlowFunc(("returns rc=%Rrc\n", rc));
4176 return rc;
4177}
4178
4179static DECLCALLBACK(int) vdIOIntWriteMeta(void *pvUser, PVDIOSTORAGE pIoStorage, uint64_t uOffset,
4180 const void *pvBuf, size_t cbWrite, PVDIOCTX pIoCtx,
4181 PFNVDXFERCOMPLETED pfnComplete, void *pvCompleteUser)
4182{
4183 PVDIO pVDIo = (PVDIO)pvUser;
4184 PVDISK pDisk = pVDIo->pDisk;
4185 int rc = VINF_SUCCESS;
4186 RTSGSEG Seg;
4187 PVDIOTASK pIoTask;
4188 PVDMETAXFER pMetaXfer = NULL;
4189 bool fInTree = false;
4190 void *pvTask = NULL;
4191
4192 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pvBuf=%#p cbWrite=%u\n",
4193 pvUser, pIoStorage, uOffset, pvBuf, cbWrite));
4194
4195 AssertMsgReturn( pIoCtx
4196 || (!pfnComplete && !pvCompleteUser),
4197 ("A synchronous metadata write is requested but the parameters are wrong\n"),
4198 VERR_INVALID_POINTER);
4199
4200 /** @todo Enable check for sync I/O later. */
4201 if ( pIoCtx
4202 && !(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
4203 VD_IS_LOCKED(pDisk);
4204
4205 if ( !pIoCtx
4206 || pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC)
4207 {
4208 /* Handle synchronous metadata I/O. */
4209 /** @todo Integrate with metadata transfers below. */
4210 rc = pVDIo->pInterfaceIo->pfnWriteSync(pVDIo->pInterfaceIo->Core.pvUser,
4211 pIoStorage->pStorage, uOffset,
4212 pvBuf, cbWrite, NULL);
4213 }
4214 else
4215 {
4216 pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGet(pIoStorage->pTreeMetaXfers, uOffset);
4217 if (!pMetaXfer)
4218 {
4219 /* Allocate a new meta transfer. */
4220 pMetaXfer = vdMetaXferAlloc(pIoStorage, uOffset, cbWrite);
4221 if (!pMetaXfer)
4222 return VERR_NO_MEMORY;
4223 }
4224 else
4225 {
4226 Assert(pMetaXfer->cbMeta >= cbWrite);
4227 Assert(pMetaXfer->Core.Key == (RTFOFF)uOffset);
4228 fInTree = true;
4229 }
4230
4231 if (VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE)
4232 {
4233 pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvCompleteUser, pMetaXfer);
4234 if (!pIoTask)
4235 {
4236 RTMemFree(pMetaXfer);
4237 return VERR_NO_MEMORY;
4238 }
4239
4240 memcpy(pMetaXfer->abData, pvBuf, cbWrite);
4241 Seg.cbSeg = cbWrite;
4242 Seg.pvSeg = pMetaXfer->abData;
4243
4244 ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
4245
4246 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_WRITE);
4247 rc = pVDIo->pInterfaceIo->pfnWriteAsync(pVDIo->pInterfaceIo->Core.pvUser,
4248 pIoStorage->pStorage,
4249 uOffset, &Seg, 1, cbWrite, pIoTask,
4250 &pvTask);
4251 if (RT_SUCCESS(rc))
4252 {
4253 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
4254 ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
4255 vdIoTaskFree(pDisk, pIoTask);
4256 if (fInTree && !pMetaXfer->cRefs)
4257 {
4258 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
4259 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
4260 AssertMsg(fRemoved, ("Metadata transfer wasn't removed\n")); NOREF(fRemoved);
4261 RTMemFree(pMetaXfer);
4262 pMetaXfer = NULL;
4263 }
4264 }
4265 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
4266 {
4267 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
4268 AssertPtr(pDeferred);
4269
4270 RTListInit(&pDeferred->NodeDeferred);
4271 pDeferred->pIoCtx = pIoCtx;
4272
4273 if (!fInTree)
4274 {
4275 bool fInserted = RTAvlrFileOffsetInsert(pIoStorage->pTreeMetaXfers, &pMetaXfer->Core);
4276 Assert(fInserted); NOREF(fInserted);
4277 }
4278
4279 RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
4280 }
4281 else
4282 {
4283 RTMemFree(pMetaXfer);
4284 pMetaXfer = NULL;
4285 }
4286 }
4287 else
4288 {
4289 /* I/O is in progress, update shadow buffer and add to waiting list. */
4290 Assert(VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_WRITE);
4291 if (!pMetaXfer->pbDataShw)
4292 {
4293 /* Allocate shadow buffer and set initial state. */
4294 LogFlowFunc(("pMetaXfer=%#p Creating shadow buffer\n", pMetaXfer));
4295 pMetaXfer->pbDataShw = (uint8_t *)RTMemAlloc(pMetaXfer->cbMeta);
4296 if (RT_LIKELY(pMetaXfer->pbDataShw))
4297 memcpy(pMetaXfer->pbDataShw, pMetaXfer->abData, pMetaXfer->cbMeta);
4298 else
4299 rc = VERR_NO_MEMORY;
4300 }
4301
4302 if (RT_SUCCESS(rc))
4303 {
4304 /* Update with written data and append to waiting list. */
4305 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
4306 if (pDeferred)
4307 {
4308 LogFlowFunc(("pMetaXfer=%#p Updating shadow buffer\n", pMetaXfer));
4309
4310 RTListInit(&pDeferred->NodeDeferred);
4311 pDeferred->pIoCtx = pIoCtx;
4312 ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
4313 memcpy(pMetaXfer->pbDataShw, pvBuf, cbWrite);
4314 RTListAppend(&pMetaXfer->ListIoCtxShwWrites, &pDeferred->NodeDeferred);
4315 }
4316 else
4317 {
4318 /*
4319 * Free shadow buffer if there is no one depending on it, i.e.
4320 * we just allocated it.
4321 */
4322 if (RTListIsEmpty(&pMetaXfer->ListIoCtxShwWrites))
4323 {
4324 RTMemFree(pMetaXfer->pbDataShw);
4325 pMetaXfer->pbDataShw = NULL;
4326 }
4327 rc = VERR_NO_MEMORY;
4328 }
4329 }
4330 }
4331 }
4332
4333 LogFlowFunc(("returns rc=%Rrc\n", rc));
4334 return rc;
4335}
4336
4337static DECLCALLBACK(void) vdIOIntMetaXferRelease(void *pvUser, PVDMETAXFER pMetaXfer)
4338{
4339 PVDIO pVDIo = (PVDIO)pvUser;
4340 PVDISK pDisk = pVDIo->pDisk;
4341 PVDIOSTORAGE pIoStorage;
4342
4343 /*
4344 * It is possible that we get called with a NULL metadata xfer handle
4345 * for synchronous I/O. Just exit.
4346 */
4347 if (!pMetaXfer)
4348 return;
4349
4350 pIoStorage = pMetaXfer->pIoStorage;
4351
4352 VD_IS_LOCKED(pDisk);
4353
4354 Assert( VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE
4355 || VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_WRITE);
4356 Assert(pMetaXfer->cRefs > 0);
4357
4358 pMetaXfer->cRefs--;
4359 if ( !pMetaXfer->cRefs
4360 && RTListIsEmpty(&pMetaXfer->ListIoCtxWaiting)
4361 && VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE)
4362 {
4363 /* Free the meta data entry. */
4364 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
4365 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
4366 AssertMsg(fRemoved, ("Metadata transfer wasn't removed\n")); NOREF(fRemoved);
4367
4368 RTMemFree(pMetaXfer);
4369 }
4370}
4371
4372static DECLCALLBACK(int) vdIOIntFlush(void *pvUser, PVDIOSTORAGE pIoStorage, PVDIOCTX pIoCtx,
4373 PFNVDXFERCOMPLETED pfnComplete, void *pvCompleteUser)
4374{
4375 PVDIO pVDIo = (PVDIO)pvUser;
4376 PVDISK pDisk = pVDIo->pDisk;
4377 int rc = VINF_SUCCESS;
4378 PVDIOTASK pIoTask;
4379 PVDMETAXFER pMetaXfer = NULL;
4380 void *pvTask = NULL;
4381
4382 LogFlowFunc(("pvUser=%#p pIoStorage=%#p pIoCtx=%#p\n",
4383 pvUser, pIoStorage, pIoCtx));
4384
4385 AssertMsgReturn( pIoCtx
4386 || (!pfnComplete && !pvCompleteUser),
4387 ("A synchronous metadata write is requested but the parameters are wrong\n"),
4388 VERR_INVALID_POINTER);
4389
4390 /** @todo Enable check for sync I/O later. */
4391 if ( pIoCtx
4392 && !(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
4393 VD_IS_LOCKED(pDisk);
4394
4395 if (pVDIo->fIgnoreFlush)
4396 return VINF_SUCCESS;
4397
4398 if ( !pIoCtx
4399 || pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC)
4400 {
4401 /* Handle synchronous flushes. */
4402 /** @todo Integrate with metadata transfers below. */
4403 rc = pVDIo->pInterfaceIo->pfnFlushSync(pVDIo->pInterfaceIo->Core.pvUser,
4404 pIoStorage->pStorage);
4405 }
4406 else
4407 {
4408 /* Allocate a new meta transfer. */
4409 pMetaXfer = vdMetaXferAlloc(pIoStorage, 0, 0);
4410 if (!pMetaXfer)
4411 return VERR_NO_MEMORY;
4412
4413 pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvUser, pMetaXfer);
4414 if (!pIoTask)
4415 {
4416 RTMemFree(pMetaXfer);
4417 return VERR_NO_MEMORY;
4418 }
4419
4420 ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
4421
4422 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
4423 AssertPtr(pDeferred);
4424
4425 RTListInit(&pDeferred->NodeDeferred);
4426 pDeferred->pIoCtx = pIoCtx;
4427
4428 RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
4429 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_FLUSH);
4430 rc = pVDIo->pInterfaceIo->pfnFlushAsync(pVDIo->pInterfaceIo->Core.pvUser,
4431 pIoStorage->pStorage,
4432 pIoTask, &pvTask);
4433 if (RT_SUCCESS(rc))
4434 {
4435 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
4436 ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
4437 vdIoTaskFree(pDisk, pIoTask);
4438 RTMemFree(pDeferred);
4439 RTMemFree(pMetaXfer);
4440 }
4441 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
4442 RTMemFree(pMetaXfer);
4443 }
4444
4445 LogFlowFunc(("returns rc=%Rrc\n", rc));
4446 return rc;
4447}
4448
4449static DECLCALLBACK(size_t) vdIOIntIoCtxCopyTo(void *pvUser, PVDIOCTX pIoCtx,
4450 const void *pvBuf, size_t cbBuf)
4451{
4452 PVDIO pVDIo = (PVDIO)pvUser;
4453 PVDISK pDisk = pVDIo->pDisk;
4454 size_t cbCopied = 0;
4455
4456 /** @todo Enable check for sync I/O later. */
4457 if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
4458 VD_IS_LOCKED(pDisk);
4459
4460 cbCopied = vdIoCtxCopyTo(pIoCtx, (uint8_t *)pvBuf, cbBuf);
4461 Assert(cbCopied == cbBuf);
4462
4463 /// @todo Assert(pIoCtx->Req.Io.cbTransferLeft >= cbCopied); - triggers with vdCopyHelper/dmgRead.
4464 ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, (uint32_t)cbCopied);
4465
4466 return cbCopied;
4467}
4468
4469static DECLCALLBACK(size_t) vdIOIntIoCtxCopyFrom(void *pvUser, PVDIOCTX pIoCtx,
4470 void *pvBuf, size_t cbBuf)
4471{
4472 PVDIO pVDIo = (PVDIO)pvUser;
4473 PVDISK pDisk = pVDIo->pDisk;
4474 size_t cbCopied = 0;
4475
4476 /** @todo Enable check for sync I/O later. */
4477 if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
4478 VD_IS_LOCKED(pDisk);
4479
4480 cbCopied = vdIoCtxCopyFrom(pIoCtx, (uint8_t *)pvBuf, cbBuf);
4481 Assert(cbCopied == cbBuf);
4482
4483 /// @todo Assert(pIoCtx->Req.Io.cbTransferLeft > cbCopied); - triggers with vdCopyHelper/dmgRead.
4484 ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, (uint32_t)cbCopied);
4485
4486 return cbCopied;
4487}
4488
4489static DECLCALLBACK(size_t) vdIOIntIoCtxSet(void *pvUser, PVDIOCTX pIoCtx, int ch, size_t cb)
4490{
4491 PVDIO pVDIo = (PVDIO)pvUser;
4492 PVDISK pDisk = pVDIo->pDisk;
4493 size_t cbSet = 0;
4494
4495 /** @todo Enable check for sync I/O later. */
4496 if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
4497 VD_IS_LOCKED(pDisk);
4498
4499 cbSet = vdIoCtxSet(pIoCtx, ch, cb);
4500 Assert(cbSet == cb);
4501
4502 /// @todo Assert(pIoCtx->Req.Io.cbTransferLeft >= cbSet); - triggers with vdCopyHelper/dmgRead.
4503 ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, (uint32_t)cbSet);
4504
4505 return cbSet;
4506}
4507
4508static DECLCALLBACK(size_t) vdIOIntIoCtxSegArrayCreate(void *pvUser, PVDIOCTX pIoCtx,
4509 PRTSGSEG paSeg, unsigned *pcSeg,
4510 size_t cbData)
4511{
4512 PVDIO pVDIo = (PVDIO)pvUser;
4513 PVDISK pDisk = pVDIo->pDisk;
4514 size_t cbCreated = 0;
4515
4516 /** @todo It is possible that this gets called from a filter plugin
4517 * outside of the disk lock. Refine assertion or remove completely. */
4518#if 0
4519 /** @todo Enable check for sync I/O later. */
4520 if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
4521 VD_IS_LOCKED(pDisk);
4522#else
4523 NOREF(pDisk);
4524#endif
4525
4526 cbCreated = RTSgBufSegArrayCreate(&pIoCtx->Req.Io.SgBuf, paSeg, pcSeg, cbData);
4527 Assert(!paSeg || cbData == cbCreated);
4528
4529 return cbCreated;
4530}
4531
4532static DECLCALLBACK(void) vdIOIntIoCtxCompleted(void *pvUser, PVDIOCTX pIoCtx, int rcReq,
4533 size_t cbCompleted)
4534{
4535 PVDIO pVDIo = (PVDIO)pvUser;
4536 PVDISK pDisk = pVDIo->pDisk;
4537
4538 LogFlowFunc(("pvUser=%#p pIoCtx=%#p rcReq=%Rrc cbCompleted=%zu\n",
4539 pvUser, pIoCtx, rcReq, cbCompleted));
4540
4541 /*
4542 * Grab the disk critical section to avoid races with other threads which
4543 * might still modify the I/O context.
4544 * Example is that iSCSI is doing an asynchronous write but calls us already
4545 * while the other thread is still hanging in vdWriteHelperAsync and couldn't update
4546 * the blocked state yet.
4547 * It can overwrite the state to true before we call vdIoCtxContinue and the
4548 * the request would hang indefinite.
4549 */
4550 ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rcReq, VINF_SUCCESS);
4551 Assert(pIoCtx->Req.Io.cbTransferLeft >= cbCompleted);
4552 ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, (uint32_t)cbCompleted);
4553
4554 /* Set next transfer function if the current one finished.
4555 * @todo: Find a better way to prevent vdIoCtxContinue from calling the current helper again. */
4556 if (!pIoCtx->Req.Io.cbTransferLeft)
4557 {
4558 pIoCtx->pfnIoCtxTransfer = pIoCtx->pfnIoCtxTransferNext;
4559 pIoCtx->pfnIoCtxTransferNext = NULL;
4560 }
4561
4562 vdIoCtxAddToWaitingList(&pDisk->pIoCtxHaltedHead, pIoCtx);
4563 if (ASMAtomicCmpXchgBool(&pDisk->fLocked, true, false))
4564 {
4565 /* Immediately drop the lock again, it will take care of processing the list. */
4566 vdDiskUnlock(pDisk, NULL);
4567 }
4568}
4569
4570static DECLCALLBACK(bool) vdIOIntIoCtxIsSynchronous(void *pvUser, PVDIOCTX pIoCtx)
4571{
4572 NOREF(pvUser);
4573 return !!(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC);
4574}
4575
4576static DECLCALLBACK(bool) vdIOIntIoCtxIsZero(void *pvUser, PVDIOCTX pIoCtx, size_t cbCheck,
4577 bool fAdvance)
4578{
4579 NOREF(pvUser);
4580
4581 bool fIsZero = RTSgBufIsZero(&pIoCtx->Req.Io.SgBuf, cbCheck);
4582 if (fIsZero && fAdvance)
4583 RTSgBufAdvance(&pIoCtx->Req.Io.SgBuf, cbCheck);
4584
4585 return fIsZero;
4586}
4587
4588static DECLCALLBACK(size_t) vdIOIntIoCtxGetDataUnitSize(void *pvUser, PVDIOCTX pIoCtx)
4589{
4590 RT_NOREF1(pIoCtx);
4591 PVDIO pVDIo = (PVDIO)pvUser;
4592 PVDISK pDisk = pVDIo->pDisk;
4593
4594 PVDIMAGE pImage = vdGetImageByNumber(pDisk, VD_LAST_IMAGE);
4595 AssertPtrReturn(pImage, 0);
4596 return pImage->Backend->pfnGetSectorSize(pImage->pBackendData);
4597}
4598
4599/**
4600 * VD I/O interface callback for opening a file (limited version for VDGetFormat).
4601 */
4602static DECLCALLBACK(int) vdIOIntOpenLimited(void *pvUser, const char *pszLocation,
4603 uint32_t fOpen, PPVDIOSTORAGE ppIoStorage)
4604{
4605 int rc = VINF_SUCCESS;
4606 PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
4607 PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTMemAllocZ(sizeof(VDIOSTORAGE));
4608
4609 if (!pIoStorage)
4610 return VERR_NO_MEMORY;
4611
4612 rc = pInterfaceIo->pfnOpen(NULL, pszLocation, fOpen, NULL, &pIoStorage->pStorage);
4613 if (RT_SUCCESS(rc))
4614 *ppIoStorage = pIoStorage;
4615 else
4616 RTMemFree(pIoStorage);
4617
4618 return rc;
4619}
4620
4621static DECLCALLBACK(int) vdIOIntCloseLimited(void *pvUser, PVDIOSTORAGE pIoStorage)
4622{
4623 PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
4624 int rc = pInterfaceIo->pfnClose(NULL, pIoStorage->pStorage);
4625
4626 RTMemFree(pIoStorage);
4627 return rc;
4628}
4629
4630static DECLCALLBACK(int) vdIOIntDeleteLimited(void *pvUser, const char *pcszFilename)
4631{
4632 PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
4633 return pInterfaceIo->pfnDelete(NULL, pcszFilename);
4634}
4635
4636static DECLCALLBACK(int) vdIOIntMoveLimited(void *pvUser, const char *pcszSrc,
4637 const char *pcszDst, unsigned fMove)
4638{
4639 PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
4640 return pInterfaceIo->pfnMove(NULL, pcszSrc, pcszDst, fMove);
4641}
4642
4643static DECLCALLBACK(int) vdIOIntGetFreeSpaceLimited(void *pvUser, const char *pcszFilename,
4644 int64_t *pcbFreeSpace)
4645{
4646 PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
4647 return pInterfaceIo->pfnGetFreeSpace(NULL, pcszFilename, pcbFreeSpace);
4648}
4649
4650static DECLCALLBACK(int) vdIOIntGetModificationTimeLimited(void *pvUser,
4651 const char *pcszFilename,
4652 PRTTIMESPEC pModificationTime)
4653{
4654 PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
4655 return pInterfaceIo->pfnGetModificationTime(NULL, pcszFilename, pModificationTime);
4656}
4657
4658static DECLCALLBACK(int) vdIOIntGetSizeLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
4659 uint64_t *pcbSize)
4660{
4661 PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
4662 return pInterfaceIo->pfnGetSize(NULL, pIoStorage->pStorage, pcbSize);
4663}
4664
4665static DECLCALLBACK(int) vdIOIntSetSizeLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
4666 uint64_t cbSize)
4667{
4668 PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
4669 return pInterfaceIo->pfnSetSize(NULL, pIoStorage->pStorage, cbSize);
4670}
4671
4672static DECLCALLBACK(int) vdIOIntWriteUserLimited(void *pvUser, PVDIOSTORAGE pStorage,
4673 uint64_t uOffset, PVDIOCTX pIoCtx,
4674 size_t cbWrite,
4675 PFNVDXFERCOMPLETED pfnComplete,
4676 void *pvCompleteUser)
4677{
4678 NOREF(pvUser);
4679 NOREF(pStorage);
4680 NOREF(uOffset);
4681 NOREF(pIoCtx);
4682 NOREF(cbWrite);
4683 NOREF(pfnComplete);
4684 NOREF(pvCompleteUser);
4685 AssertMsgFailedReturn(("This needs to be implemented when called\n"), VERR_NOT_IMPLEMENTED);
4686}
4687
4688static DECLCALLBACK(int) vdIOIntReadUserLimited(void *pvUser, PVDIOSTORAGE pStorage,
4689 uint64_t uOffset, PVDIOCTX pIoCtx,
4690 size_t cbRead)
4691{
4692 NOREF(pvUser);
4693 NOREF(pStorage);
4694 NOREF(uOffset);
4695 NOREF(pIoCtx);
4696 NOREF(cbRead);
4697 AssertMsgFailedReturn(("This needs to be implemented when called\n"), VERR_NOT_IMPLEMENTED);
4698}
4699
4700static DECLCALLBACK(int) vdIOIntWriteMetaLimited(void *pvUser, PVDIOSTORAGE pStorage,
4701 uint64_t uOffset, const void *pvBuffer,
4702 size_t cbBuffer, PVDIOCTX pIoCtx,
4703 PFNVDXFERCOMPLETED pfnComplete,
4704 void *pvCompleteUser)
4705{
4706 PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
4707
4708 AssertMsgReturn(!pIoCtx && !pfnComplete && !pvCompleteUser,
4709 ("Async I/O not implemented for the limited interface"),
4710 VERR_NOT_SUPPORTED);
4711
4712 return pInterfaceIo->pfnWriteSync(NULL, pStorage->pStorage, uOffset, pvBuffer, cbBuffer, NULL);
4713}
4714
4715static DECLCALLBACK(int) vdIOIntReadMetaLimited(void *pvUser, PVDIOSTORAGE pStorage,
4716 uint64_t uOffset, void *pvBuffer,
4717 size_t cbBuffer, PVDIOCTX pIoCtx,
4718 PPVDMETAXFER ppMetaXfer,
4719 PFNVDXFERCOMPLETED pfnComplete,
4720 void *pvCompleteUser)
4721{
4722 PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
4723
4724 AssertMsgReturn(!pIoCtx && !ppMetaXfer && !pfnComplete && !pvCompleteUser,
4725 ("Async I/O not implemented for the limited interface"),
4726 VERR_NOT_SUPPORTED);
4727
4728 return pInterfaceIo->pfnReadSync(NULL, pStorage->pStorage, uOffset, pvBuffer, cbBuffer, NULL);
4729}
4730
4731#if 0 /* unsed */
4732static int vdIOIntMetaXferReleaseLimited(void *pvUser, PVDMETAXFER pMetaXfer)
4733{
4734 /* This is a NOP in this case. */
4735 NOREF(pvUser);
4736 NOREF(pMetaXfer);
4737 return VINF_SUCCESS;
4738}
4739#endif
4740
4741static DECLCALLBACK(int) vdIOIntFlushLimited(void *pvUser, PVDIOSTORAGE pStorage,
4742 PVDIOCTX pIoCtx,
4743 PFNVDXFERCOMPLETED pfnComplete,
4744 void *pvCompleteUser)
4745{
4746 PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
4747
4748 AssertMsgReturn(!pIoCtx && !pfnComplete && !pvCompleteUser,
4749 ("Async I/O not implemented for the limited interface"),
4750 VERR_NOT_SUPPORTED);
4751
4752 return pInterfaceIo->pfnFlushSync(NULL, pStorage->pStorage);
4753}
4754
4755/**
4756 * internal: send output to the log (unconditionally).
4757 */
4758static DECLCALLBACK(int) vdLogMessage(void *pvUser, const char *pszFormat, va_list args)
4759{
4760 NOREF(pvUser);
4761 RTLogPrintfV(pszFormat, args);
4762 return VINF_SUCCESS;
4763}
4764
4765DECLINLINE(int) vdMessageWrapper(PVDISK pDisk, const char *pszFormat, ...)
4766{
4767 va_list va;
4768 va_start(va, pszFormat);
4769 int rc = pDisk->pInterfaceError->pfnMessage(pDisk->pInterfaceError->Core.pvUser,
4770 pszFormat, va);
4771 va_end(va);
4772 return rc;
4773}
4774
4775
4776/**
4777 * internal: adjust PCHS geometry
4778 */
4779static void vdFixupPCHSGeometry(PVDGEOMETRY pPCHS, uint64_t cbSize)
4780{
4781 /* Fix broken PCHS geometry. Can happen for two reasons: either the backend
4782 * mixes up PCHS and LCHS, or the application used to create the source
4783 * image has put garbage in it. Additionally, if the PCHS geometry covers
4784 * more than the image size, set it back to the default. */
4785 if ( pPCHS->cHeads > 16
4786 || pPCHS->cSectors > 63
4787 || pPCHS->cCylinders == 0
4788 || (uint64_t)pPCHS->cHeads * pPCHS->cSectors * pPCHS->cCylinders * 512 > cbSize)
4789 {
4790 Assert(!(RT_MIN(cbSize / 512 / 16 / 63, 16383) - (uint32_t)RT_MIN(cbSize / 512 / 16 / 63, 16383)));
4791 pPCHS->cCylinders = (uint32_t)RT_MIN(cbSize / 512 / 16 / 63, 16383);
4792 pPCHS->cHeads = 16;
4793 pPCHS->cSectors = 63;
4794 }
4795}
4796
4797/**
4798 * internal: adjust PCHS geometry
4799 */
4800static void vdFixupLCHSGeometry(PVDGEOMETRY pLCHS, uint64_t cbSize)
4801{
4802 /* Fix broken LCHS geometry. Can happen for two reasons: either the backend
4803 * mixes up PCHS and LCHS, or the application used to create the source
4804 * image has put garbage in it. The fix in this case is to clear the LCHS
4805 * geometry to trigger autodetection when it is used next. If the geometry
4806 * already says "please autodetect" (cylinders=0) keep it. */
4807 if ( ( pLCHS->cHeads > 255
4808 || pLCHS->cHeads == 0
4809 || pLCHS->cSectors > 63
4810 || pLCHS->cSectors == 0)
4811 && pLCHS->cCylinders != 0)
4812 {
4813 pLCHS->cCylinders = 0;
4814 pLCHS->cHeads = 0;
4815 pLCHS->cSectors = 0;
4816 }
4817 /* Always recompute the number of cylinders stored in the LCHS
4818 * geometry if it isn't set to "autotedetect" at the moment.
4819 * This is very useful if the destination image size is
4820 * larger or smaller than the source image size. Do not modify
4821 * the number of heads and sectors. Windows guests hate it. */
4822 if ( pLCHS->cCylinders != 0
4823 && pLCHS->cHeads != 0 /* paranoia */
4824 && pLCHS->cSectors != 0 /* paranoia */)
4825 {
4826 Assert(!(RT_MIN(cbSize / 512 / pLCHS->cHeads / pLCHS->cSectors, 1024) - (uint32_t)RT_MIN(cbSize / 512 / pLCHS->cHeads / pLCHS->cSectors, 1024)));
4827 pLCHS->cCylinders = (uint32_t)RT_MIN(cbSize / 512 / pLCHS->cHeads / pLCHS->cSectors, 1024);
4828 }
4829}
4830
4831/**
4832 * Sets the I/O callbacks of the given interface to the fallback methods
4833 *
4834 * @returns nothing.
4835 * @param pIfIo The I/O interface to setup.
4836 */
4837static void vdIfIoFallbackCallbacksSetup(PVDINTERFACEIO pIfIo)
4838{
4839 pIfIo->pfnOpen = vdIOOpenFallback;
4840 pIfIo->pfnClose = vdIOCloseFallback;
4841 pIfIo->pfnDelete = vdIODeleteFallback;
4842 pIfIo->pfnMove = vdIOMoveFallback;
4843 pIfIo->pfnGetFreeSpace = vdIOGetFreeSpaceFallback;
4844 pIfIo->pfnGetModificationTime = vdIOGetModificationTimeFallback;
4845 pIfIo->pfnGetSize = vdIOGetSizeFallback;
4846 pIfIo->pfnSetSize = vdIOSetSizeFallback;
4847 pIfIo->pfnSetAllocationSize = vdIOSetAllocationSizeFallback;
4848 pIfIo->pfnReadSync = vdIOReadSyncFallback;
4849 pIfIo->pfnWriteSync = vdIOWriteSyncFallback;
4850 pIfIo->pfnFlushSync = vdIOFlushSyncFallback;
4851 pIfIo->pfnReadAsync = vdIOReadAsyncFallback;
4852 pIfIo->pfnWriteAsync = vdIOWriteAsyncFallback;
4853 pIfIo->pfnFlushAsync = vdIOFlushAsyncFallback;
4854}
4855
4856/**
4857 * Sets the internal I/O callbacks of the given interface.
4858 *
4859 * @returns nothing.
4860 * @param pIfIoInt The internal I/O interface to setup.
4861 */
4862static void vdIfIoIntCallbacksSetup(PVDINTERFACEIOINT pIfIoInt)
4863{
4864 pIfIoInt->pfnOpen = vdIOIntOpen;
4865 pIfIoInt->pfnClose = vdIOIntClose;
4866 pIfIoInt->pfnDelete = vdIOIntDelete;
4867 pIfIoInt->pfnMove = vdIOIntMove;
4868 pIfIoInt->pfnGetFreeSpace = vdIOIntGetFreeSpace;
4869 pIfIoInt->pfnGetModificationTime = vdIOIntGetModificationTime;
4870 pIfIoInt->pfnGetSize = vdIOIntGetSize;
4871 pIfIoInt->pfnSetSize = vdIOIntSetSize;
4872 pIfIoInt->pfnSetAllocationSize = vdIOIntSetAllocationSize;
4873 pIfIoInt->pfnReadUser = vdIOIntReadUser;
4874 pIfIoInt->pfnWriteUser = vdIOIntWriteUser;
4875 pIfIoInt->pfnReadMeta = vdIOIntReadMeta;
4876 pIfIoInt->pfnWriteMeta = vdIOIntWriteMeta;
4877 pIfIoInt->pfnMetaXferRelease = vdIOIntMetaXferRelease;
4878 pIfIoInt->pfnFlush = vdIOIntFlush;
4879 pIfIoInt->pfnIoCtxCopyFrom = vdIOIntIoCtxCopyFrom;
4880 pIfIoInt->pfnIoCtxCopyTo = vdIOIntIoCtxCopyTo;
4881 pIfIoInt->pfnIoCtxSet = vdIOIntIoCtxSet;
4882 pIfIoInt->pfnIoCtxSegArrayCreate = vdIOIntIoCtxSegArrayCreate;
4883 pIfIoInt->pfnIoCtxCompleted = vdIOIntIoCtxCompleted;
4884 pIfIoInt->pfnIoCtxIsSynchronous = vdIOIntIoCtxIsSynchronous;
4885 pIfIoInt->pfnIoCtxIsZero = vdIOIntIoCtxIsZero;
4886 pIfIoInt->pfnIoCtxGetDataUnitSize = vdIOIntIoCtxGetDataUnitSize;
4887}
4888
4889/**
4890 * Internally used completion handler for synchronous I/O contexts.
4891 */
4892static DECLCALLBACK(void) vdIoCtxSyncComplete(void *pvUser1, void *pvUser2, int rcReq)
4893{
4894 RT_NOREF2(pvUser1, rcReq);
4895 RTSEMEVENT hEvent = (RTSEMEVENT)pvUser2;
4896
4897 RTSemEventSignal(hEvent);
4898}
4899
4900/**
4901 * Creates a new region list from the given one converting to match the flags if necessary.
4902 *
4903 * @returns VBox status code.
4904 * @param pRegionList The region list to convert from.
4905 * @param fFlags The flags for the new region list.
4906 * @param ppRegionList Where to store the new region list on success.
4907 */
4908static int vdRegionListConv(PCVDREGIONLIST pRegionList, uint32_t fFlags, PPVDREGIONLIST ppRegionList)
4909{
4910 int rc = VINF_SUCCESS;
4911 PVDREGIONLIST pRegionListNew = (PVDREGIONLIST)RTMemDup(pRegionList, RT_UOFFSETOF(VDREGIONLIST, aRegions[pRegionList->cRegions]));
4912 if (RT_LIKELY(pRegionListNew))
4913 {
4914 /* Do we have to convert anything? */
4915 if (pRegionList->fFlags != fFlags)
4916 {
4917 uint64_t offRegionNext = 0;
4918
4919 pRegionListNew->fFlags = fFlags;
4920 for (unsigned i = 0; i < pRegionListNew->cRegions; i++)
4921 {
4922 PVDREGIONDESC pRegion = &pRegionListNew->aRegions[i];
4923
4924 if ( (fFlags & VD_REGION_LIST_F_LOC_SIZE_BLOCKS)
4925 && !(pRegionList->fFlags & VD_REGION_LIST_F_LOC_SIZE_BLOCKS))
4926 {
4927 Assert(!(pRegion->cRegionBlocksOrBytes % pRegion->cbBlock));
4928
4929 /* Convert from bytes to logical blocks. */
4930 pRegion->offRegion = offRegionNext;
4931 pRegion->cRegionBlocksOrBytes = pRegion->cRegionBlocksOrBytes / pRegion->cbBlock;
4932 offRegionNext += pRegion->cRegionBlocksOrBytes;
4933 }
4934 else
4935 {
4936 /* Convert from logical blocks to bytes. */
4937 pRegion->offRegion = offRegionNext;
4938 pRegion->cRegionBlocksOrBytes = pRegion->cRegionBlocksOrBytes * pRegion->cbBlock;
4939 offRegionNext += pRegion->cRegionBlocksOrBytes;
4940 }
4941 }
4942 }
4943
4944 *ppRegionList = pRegionListNew;
4945 }
4946 else
4947 rc = VERR_NO_MEMORY;
4948
4949 return rc;
4950}
4951
4952/**
4953 * Initializes HDD backends.
4954 *
4955 * @returns VBox status code.
4956 */
4957VBOXDDU_DECL(int) VDInit(void)
4958{
4959 int rc = vdPluginInit();
4960 LogRel(("VD: VDInit finished with %Rrc\n", rc));
4961 return rc;
4962}
4963
4964/**
4965 * Destroys loaded HDD backends.
4966 *
4967 * @returns VBox status code.
4968 */
4969VBOXDDU_DECL(int) VDShutdown(void)
4970{
4971 return vdPluginTerm();
4972}
4973
4974/**
4975 * Loads a single plugin given by filename.
4976 *
4977 * @returns VBox status code.
4978 * @param pszFilename The plugin filename to load.
4979 */
4980VBOXDDU_DECL(int) VDPluginLoadFromFilename(const char *pszFilename)
4981{
4982 if (!vdPluginIsInitialized())
4983 {
4984 int rc = VDInit();
4985 if (RT_FAILURE(rc))
4986 return rc;
4987 }
4988
4989 return vdPluginLoadFromFilename(pszFilename);
4990}
4991
4992/**
4993 * Load all plugins from a given path.
4994 *
4995 * @returns VBox statuse code.
4996 * @param pszPath The path to load plugins from.
4997 */
4998VBOXDDU_DECL(int) VDPluginLoadFromPath(const char *pszPath)
4999{
5000 if (!vdPluginIsInitialized())
5001 {
5002 int rc = VDInit();
5003 if (RT_FAILURE(rc))
5004 return rc;
5005 }
5006
5007 return vdPluginLoadFromPath(pszPath);
5008}
5009
5010/**
5011 * Unloads a single plugin given by filename.
5012 *
5013 * @returns VBox status code.
5014 * @param pszFilename The plugin filename to unload.
5015 */
5016VBOXDDU_DECL(int) VDPluginUnloadFromFilename(const char *pszFilename)
5017{
5018 if (!vdPluginIsInitialized())
5019 {
5020 int rc = VDInit();
5021 if (RT_FAILURE(rc))
5022 return rc;
5023 }
5024
5025 return vdPluginUnloadFromFilename(pszFilename);
5026}
5027
5028/**
5029 * Unload all plugins from a given path.
5030 *
5031 * @returns VBox statuse code.
5032 * @param pszPath The path to unload plugins from.
5033 */
5034VBOXDDU_DECL(int) VDPluginUnloadFromPath(const char *pszPath)
5035{
5036 if (!vdPluginIsInitialized())
5037 {
5038 int rc = VDInit();
5039 if (RT_FAILURE(rc))
5040 return rc;
5041 }
5042
5043 return vdPluginUnloadFromPath(pszPath);
5044}
5045
5046/**
5047 * Lists all HDD backends and their capabilities in a caller-provided buffer.
5048 *
5049 * @returns VBox status code.
5050 * VERR_BUFFER_OVERFLOW if not enough space is passed.
5051 * @param cEntriesAlloc Number of list entries available.
5052 * @param pEntries Pointer to array for the entries.
5053 * @param pcEntriesUsed Number of entries returned.
5054 */
5055VBOXDDU_DECL(int) VDBackendInfo(unsigned cEntriesAlloc, PVDBACKENDINFO pEntries,
5056 unsigned *pcEntriesUsed)
5057{
5058 int rc = VINF_SUCCESS;
5059
5060 LogFlowFunc(("cEntriesAlloc=%u pEntries=%#p pcEntriesUsed=%#p\n", cEntriesAlloc, pEntries, pcEntriesUsed));
5061 /* Check arguments. */
5062 AssertMsgReturn(cEntriesAlloc,
5063 ("cEntriesAlloc=%u\n", cEntriesAlloc),
5064 VERR_INVALID_PARAMETER);
5065 AssertMsgReturn(VALID_PTR(pEntries),
5066 ("pEntries=%#p\n", pEntries),
5067 VERR_INVALID_PARAMETER);
5068 AssertMsgReturn(VALID_PTR(pcEntriesUsed),
5069 ("pcEntriesUsed=%#p\n", pcEntriesUsed),
5070 VERR_INVALID_PARAMETER);
5071 if (!vdPluginIsInitialized())
5072 VDInit();
5073
5074 uint32_t cBackends = vdGetImageBackendCount();
5075 if (cEntriesAlloc < cBackends)
5076 {
5077 *pcEntriesUsed = cBackends;
5078 return VERR_BUFFER_OVERFLOW;
5079 }
5080
5081 for (unsigned i = 0; i < cBackends; i++)
5082 {
5083 PCVDIMAGEBACKEND pBackend;
5084 rc = vdQueryImageBackend(i, &pBackend);
5085 AssertRC(rc);
5086
5087 pEntries[i].pszBackend = pBackend->pszBackendName;
5088 pEntries[i].uBackendCaps = pBackend->uBackendCaps;
5089 pEntries[i].paFileExtensions = pBackend->paFileExtensions;
5090 pEntries[i].paConfigInfo = pBackend->paConfigInfo;
5091 pEntries[i].pfnComposeLocation = pBackend->pfnComposeLocation;
5092 pEntries[i].pfnComposeName = pBackend->pfnComposeName;
5093 }
5094
5095 LogFlowFunc(("returns %Rrc *pcEntriesUsed=%u\n", rc, cBackends));
5096 *pcEntriesUsed = cBackends;
5097 return rc;
5098}
5099
5100/**
5101 * Lists the capabilities of a backend identified by its name.
5102 *
5103 * @returns VBox status code.
5104 * @param pszBackend The backend name.
5105 * @param pEntry Pointer to an entry.
5106 */
5107VBOXDDU_DECL(int) VDBackendInfoOne(const char *pszBackend, PVDBACKENDINFO pEntry)
5108{
5109 LogFlowFunc(("pszBackend=%#p pEntry=%#p\n", pszBackend, pEntry));
5110 /* Check arguments. */
5111 AssertMsgReturn(VALID_PTR(pszBackend),
5112 ("pszBackend=%#p\n", pszBackend),
5113 VERR_INVALID_PARAMETER);
5114 AssertMsgReturn(VALID_PTR(pEntry),
5115 ("pEntry=%#p\n", pEntry),
5116 VERR_INVALID_PARAMETER);
5117 if (!vdPluginIsInitialized())
5118 VDInit();
5119
5120 PCVDIMAGEBACKEND pBackend;
5121 int rc = vdFindImageBackend(pszBackend, &pBackend);
5122 if (RT_SUCCESS(rc))
5123 {
5124 pEntry->pszBackend = pBackend->pszBackendName;
5125 pEntry->uBackendCaps = pBackend->uBackendCaps;
5126 pEntry->paFileExtensions = pBackend->paFileExtensions;
5127 pEntry->paConfigInfo = pBackend->paConfigInfo;
5128 }
5129
5130 return rc;
5131}
5132
5133/**
5134 * Lists all filters and their capabilities in a caller-provided buffer.
5135 *
5136 * @return VBox status code.
5137 * VERR_BUFFER_OVERFLOW if not enough space is passed.
5138 * @param cEntriesAlloc Number of list entries available.
5139 * @param pEntries Pointer to array for the entries.
5140 * @param pcEntriesUsed Number of entries returned.
5141 */
5142VBOXDDU_DECL(int) VDFilterInfo(unsigned cEntriesAlloc, PVDFILTERINFO pEntries,
5143 unsigned *pcEntriesUsed)
5144{
5145 int rc = VINF_SUCCESS;
5146
5147 LogFlowFunc(("cEntriesAlloc=%u pEntries=%#p pcEntriesUsed=%#p\n", cEntriesAlloc, pEntries, pcEntriesUsed));
5148 /* Check arguments. */
5149 AssertMsgReturn(cEntriesAlloc,
5150 ("cEntriesAlloc=%u\n", cEntriesAlloc),
5151 VERR_INVALID_PARAMETER);
5152 AssertMsgReturn(VALID_PTR(pEntries),
5153 ("pEntries=%#p\n", pEntries),
5154 VERR_INVALID_PARAMETER);
5155 AssertMsgReturn(VALID_PTR(pcEntriesUsed),
5156 ("pcEntriesUsed=%#p\n", pcEntriesUsed),
5157 VERR_INVALID_PARAMETER);
5158 if (!vdPluginIsInitialized())
5159 VDInit();
5160
5161 uint32_t cBackends = vdGetFilterBackendCount();
5162 if (cEntriesAlloc < cBackends)
5163 {
5164 *pcEntriesUsed = cBackends;
5165 return VERR_BUFFER_OVERFLOW;
5166 }
5167
5168 for (unsigned i = 0; i < cBackends; i++)
5169 {
5170 PCVDFILTERBACKEND pBackend;
5171 rc = vdQueryFilterBackend(i, &pBackend);
5172 pEntries[i].pszFilter = pBackend->pszBackendName;
5173 pEntries[i].paConfigInfo = pBackend->paConfigInfo;
5174 }
5175
5176 LogFlowFunc(("returns %Rrc *pcEntriesUsed=%u\n", rc, cBackends));
5177 *pcEntriesUsed = cBackends;
5178 return rc;
5179}
5180
5181/**
5182 * Lists the capabilities of a filter identified by its name.
5183 *
5184 * @return VBox status code.
5185 * @param pszFilter The filter name (case insensitive).
5186 * @param pEntry Pointer to an entry.
5187 */
5188VBOXDDU_DECL(int) VDFilterInfoOne(const char *pszFilter, PVDFILTERINFO pEntry)
5189{
5190 LogFlowFunc(("pszFilter=%#p pEntry=%#p\n", pszFilter, pEntry));
5191 /* Check arguments. */
5192 AssertMsgReturn(VALID_PTR(pszFilter),
5193 ("pszFilter=%#p\n", pszFilter),
5194 VERR_INVALID_PARAMETER);
5195 AssertMsgReturn(VALID_PTR(pEntry),
5196 ("pEntry=%#p\n", pEntry),
5197 VERR_INVALID_PARAMETER);
5198 if (!vdPluginIsInitialized())
5199 VDInit();
5200
5201 PCVDFILTERBACKEND pBackend;
5202 int rc = vdFindFilterBackend(pszFilter, &pBackend);
5203 if (RT_SUCCESS(rc))
5204 {
5205 pEntry->pszFilter = pBackend->pszBackendName;
5206 pEntry->paConfigInfo = pBackend->paConfigInfo;
5207 }
5208
5209 return rc;
5210}
5211
5212/**
5213 * Allocates and initializes an empty HDD container.
5214 * No image files are opened.
5215 *
5216 * @returns VBox status code.
5217 * @param pVDIfsDisk Pointer to the per-disk VD interface list.
5218 * @param enmType Type of the image container.
5219 * @param ppDisk Where to store the reference to HDD container.
5220 */
5221VBOXDDU_DECL(int) VDCreate(PVDINTERFACE pVDIfsDisk, VDTYPE enmType, PVDISK *ppDisk)
5222{
5223 int rc = VINF_SUCCESS;
5224 PVDISK pDisk = NULL;
5225
5226 LogFlowFunc(("pVDIfsDisk=%#p\n", pVDIfsDisk));
5227 do
5228 {
5229 /* Check arguments. */
5230 AssertMsgBreakStmt(VALID_PTR(ppDisk),
5231 ("ppDisk=%#p\n", ppDisk),
5232 rc = VERR_INVALID_PARAMETER);
5233
5234 pDisk = (PVDISK)RTMemAllocZ(sizeof(VDISK));
5235 if (pDisk)
5236 {
5237 pDisk->u32Signature = VDISK_SIGNATURE;
5238 pDisk->enmType = enmType;
5239 pDisk->cImages = 0;
5240 pDisk->pBase = NULL;
5241 pDisk->pLast = NULL;
5242 pDisk->cbSize = 0;
5243 pDisk->PCHSGeometry.cCylinders = 0;
5244 pDisk->PCHSGeometry.cHeads = 0;
5245 pDisk->PCHSGeometry.cSectors = 0;
5246 pDisk->LCHSGeometry.cCylinders = 0;
5247 pDisk->LCHSGeometry.cHeads = 0;
5248 pDisk->LCHSGeometry.cSectors = 0;
5249 pDisk->pVDIfsDisk = pVDIfsDisk;
5250 pDisk->pInterfaceError = NULL;
5251 pDisk->pInterfaceThreadSync = NULL;
5252 pDisk->pIoCtxLockOwner = NULL;
5253 pDisk->pIoCtxHead = NULL;
5254 pDisk->fLocked = false;
5255 pDisk->hMemCacheIoCtx = NIL_RTMEMCACHE;
5256 pDisk->hMemCacheIoTask = NIL_RTMEMCACHE;
5257 RTListInit(&pDisk->ListFilterChainWrite);
5258 RTListInit(&pDisk->ListFilterChainRead);
5259
5260 /* Create the I/O ctx cache */
5261 rc = RTMemCacheCreate(&pDisk->hMemCacheIoCtx, sizeof(VDIOCTX), 0, UINT32_MAX,
5262 NULL, NULL, NULL, 0);
5263 if (RT_FAILURE(rc))
5264 break;
5265
5266 /* Create the I/O task cache */
5267 rc = RTMemCacheCreate(&pDisk->hMemCacheIoTask, sizeof(VDIOTASK), 0, UINT32_MAX,
5268 NULL, NULL, NULL, 0);
5269 if (RT_FAILURE(rc))
5270 break;
5271
5272 pDisk->pInterfaceError = VDIfErrorGet(pVDIfsDisk);
5273 pDisk->pInterfaceThreadSync = VDIfThreadSyncGet(pVDIfsDisk);
5274
5275 *ppDisk = pDisk;
5276 }
5277 else
5278 {
5279 rc = VERR_NO_MEMORY;
5280 break;
5281 }
5282 } while (0);
5283
5284 if ( RT_FAILURE(rc)
5285 && pDisk)
5286 {
5287 if (pDisk->hMemCacheIoCtx != NIL_RTMEMCACHE)
5288 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
5289 if (pDisk->hMemCacheIoTask != NIL_RTMEMCACHE)
5290 RTMemCacheDestroy(pDisk->hMemCacheIoTask);
5291 }
5292
5293 LogFlowFunc(("returns %Rrc (pDisk=%#p)\n", rc, pDisk));
5294 return rc;
5295}
5296
5297/**
5298 * Destroys HDD container.
5299 * If container has opened image files they will be closed.
5300 *
5301 * @returns VBox status code.
5302 * @param pDisk Pointer to HDD container.
5303 */
5304VBOXDDU_DECL(int) VDDestroy(PVDISK pDisk)
5305{
5306 int rc = VINF_SUCCESS;
5307 LogFlowFunc(("pDisk=%#p\n", pDisk));
5308 do
5309 {
5310 /* sanity check */
5311 AssertPtrBreak(pDisk);
5312 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5313 Assert(!pDisk->fLocked);
5314
5315 rc = VDCloseAll(pDisk);
5316 int rc2 = VDFilterRemoveAll(pDisk);
5317 if (RT_SUCCESS(rc))
5318 rc = rc2;
5319
5320 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
5321 RTMemCacheDestroy(pDisk->hMemCacheIoTask);
5322 RTMemFree(pDisk);
5323 } while (0);
5324 LogFlowFunc(("returns %Rrc\n", rc));
5325 return rc;
5326}
5327
5328/**
5329 * Try to get the backend name which can use this image.
5330 *
5331 * @returns VBox status code.
5332 * VINF_SUCCESS if a plugin was found.
5333 * ppszFormat contains the string which can be used as backend name.
5334 * VERR_NOT_SUPPORTED if no backend was found.
5335 * @param pVDIfsDisk Pointer to the per-disk VD interface list.
5336 * @param pVDIfsImage Pointer to the per-image VD interface list.
5337 * @param pszFilename Name of the image file for which the backend is queried.
5338 * @param ppszFormat Receives pointer of the UTF-8 string which contains the format name.
5339 * The returned pointer must be freed using RTStrFree().
5340 */
5341VBOXDDU_DECL(int) VDGetFormat(PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
5342 const char *pszFilename, char **ppszFormat, VDTYPE *penmType)
5343{
5344 int rc = VERR_NOT_SUPPORTED;
5345 VDINTERFACEIOINT VDIfIoInt;
5346 VDINTERFACEIO VDIfIoFallback;
5347 PVDINTERFACEIO pInterfaceIo;
5348
5349 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
5350 /* Check arguments. */
5351 AssertMsgReturn(VALID_PTR(pszFilename) && *pszFilename,
5352 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
5353 VERR_INVALID_PARAMETER);
5354 AssertMsgReturn(VALID_PTR(ppszFormat),
5355 ("ppszFormat=%#p\n", ppszFormat),
5356 VERR_INVALID_PARAMETER);
5357 AssertMsgReturn(VALID_PTR(penmType),
5358 ("penmType=%#p\n", penmType),
5359 VERR_INVALID_PARAMETER);
5360
5361 if (!vdPluginIsInitialized())
5362 VDInit();
5363
5364 pInterfaceIo = VDIfIoGet(pVDIfsImage);
5365 if (!pInterfaceIo)
5366 {
5367 /*
5368 * Caller doesn't provide an I/O interface, create our own using the
5369 * native file API.
5370 */
5371 vdIfIoFallbackCallbacksSetup(&VDIfIoFallback);
5372 pInterfaceIo = &VDIfIoFallback;
5373 }
5374
5375 /* Set up the internal I/O interface. */
5376 AssertReturn(!VDIfIoIntGet(pVDIfsImage), VERR_INVALID_PARAMETER);
5377 VDIfIoInt.pfnOpen = vdIOIntOpenLimited;
5378 VDIfIoInt.pfnClose = vdIOIntCloseLimited;
5379 VDIfIoInt.pfnDelete = vdIOIntDeleteLimited;
5380 VDIfIoInt.pfnMove = vdIOIntMoveLimited;
5381 VDIfIoInt.pfnGetFreeSpace = vdIOIntGetFreeSpaceLimited;
5382 VDIfIoInt.pfnGetModificationTime = vdIOIntGetModificationTimeLimited;
5383 VDIfIoInt.pfnGetSize = vdIOIntGetSizeLimited;
5384 VDIfIoInt.pfnSetSize = vdIOIntSetSizeLimited;
5385 VDIfIoInt.pfnReadUser = vdIOIntReadUserLimited;
5386 VDIfIoInt.pfnWriteUser = vdIOIntWriteUserLimited;
5387 VDIfIoInt.pfnReadMeta = vdIOIntReadMetaLimited;
5388 VDIfIoInt.pfnWriteMeta = vdIOIntWriteMetaLimited;
5389 VDIfIoInt.pfnFlush = vdIOIntFlushLimited;
5390 rc = VDInterfaceAdd(&VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
5391 pInterfaceIo, sizeof(VDINTERFACEIOINT), &pVDIfsImage);
5392 AssertRC(rc);
5393
5394 /* Find the backend supporting this file format. */
5395 for (unsigned i = 0; i < vdGetImageBackendCount(); i++)
5396 {
5397 PCVDIMAGEBACKEND pBackend;
5398 rc = vdQueryImageBackend(i, &pBackend);
5399 AssertRC(rc);
5400
5401 if (pBackend->pfnProbe)
5402 {
5403 rc = pBackend->pfnProbe(pszFilename, pVDIfsDisk, pVDIfsImage, penmType);
5404 if ( RT_SUCCESS(rc)
5405 /* The correct backend has been found, but there is a small
5406 * incompatibility so that the file cannot be used. Stop here
5407 * and signal success - the actual open will of course fail,
5408 * but that will create a really sensible error message. */
5409 || ( rc != VERR_VD_GEN_INVALID_HEADER
5410 && rc != VERR_VD_VDI_INVALID_HEADER
5411 && rc != VERR_VD_VMDK_INVALID_HEADER
5412 && rc != VERR_VD_ISCSI_INVALID_HEADER
5413 && rc != VERR_VD_VHD_INVALID_HEADER
5414 && rc != VERR_VD_RAW_INVALID_HEADER
5415 && rc != VERR_VD_RAW_SIZE_MODULO_512
5416 && rc != VERR_VD_RAW_SIZE_MODULO_2048
5417 && rc != VERR_VD_RAW_SIZE_OPTICAL_TOO_SMALL
5418 && rc != VERR_VD_RAW_SIZE_FLOPPY_TOO_BIG
5419 && rc != VERR_VD_PARALLELS_INVALID_HEADER
5420 && rc != VERR_VD_DMG_INVALID_HEADER))
5421 {
5422 /* Copy the name into the new string. */
5423 char *pszFormat = RTStrDup(pBackend->pszBackendName);
5424 if (!pszFormat)
5425 {
5426 rc = VERR_NO_MEMORY;
5427 break;
5428 }
5429 *ppszFormat = pszFormat;
5430 /* Do not consider the typical file access errors as success,
5431 * which allows the caller to deal with such issues. */
5432 if ( rc != VERR_ACCESS_DENIED
5433 && rc != VERR_PATH_NOT_FOUND
5434 && rc != VERR_FILE_NOT_FOUND)
5435 rc = VINF_SUCCESS;
5436 break;
5437 }
5438 rc = VERR_NOT_SUPPORTED;
5439 }
5440 }
5441
5442 /* Try the cache backends. */
5443 if (rc == VERR_NOT_SUPPORTED)
5444 {
5445 for (unsigned i = 0; i < vdGetCacheBackendCount(); i++)
5446 {
5447 PCVDCACHEBACKEND pBackend;
5448 rc = vdQueryCacheBackend(i, &pBackend);
5449 AssertRC(rc);
5450
5451 if (pBackend->pfnProbe)
5452 {
5453 rc = pBackend->pfnProbe(pszFilename, pVDIfsDisk, pVDIfsImage);
5454 if ( RT_SUCCESS(rc)
5455 || (rc != VERR_VD_GEN_INVALID_HEADER))
5456 {
5457 /* Copy the name into the new string. */
5458 char *pszFormat = RTStrDup(pBackend->pszBackendName);
5459 if (!pszFormat)
5460 {
5461 rc = VERR_NO_MEMORY;
5462 break;
5463 }
5464 *ppszFormat = pszFormat;
5465 rc = VINF_SUCCESS;
5466 break;
5467 }
5468 rc = VERR_NOT_SUPPORTED;
5469 }
5470 }
5471 }
5472
5473 LogFlowFunc(("returns %Rrc *ppszFormat=\"%s\"\n", rc, *ppszFormat));
5474 return rc;
5475}
5476
5477/**
5478 * Opens an image file.
5479 *
5480 * The first opened image file in HDD container must have a base image type,
5481 * others (next opened images) must be a differencing or undo images.
5482 * Linkage is checked for differencing image to be in consistence with the previously opened image.
5483 * When another differencing image is opened and the last image was opened in read/write access
5484 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
5485 * other processes to use images in read-only mode too.
5486 *
5487 * Note that the image is opened in read-only mode if a read/write open is not possible.
5488 * Use VDIsReadOnly to check open mode.
5489 *
5490 * @returns VBox status code.
5491 * @param pDisk Pointer to HDD container.
5492 * @param pszBackend Name of the image file backend to use.
5493 * @param pszFilename Name of the image file to open.
5494 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
5495 * @param pVDIfsImage Pointer to the per-image VD interface list.
5496 */
5497VBOXDDU_DECL(int) VDOpen(PVDISK pDisk, const char *pszBackend,
5498 const char *pszFilename, unsigned uOpenFlags,
5499 PVDINTERFACE pVDIfsImage)
5500{
5501 int rc = VINF_SUCCESS;
5502 int rc2;
5503 bool fLockWrite = false;
5504 PVDIMAGE pImage = NULL;
5505
5506 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsImage=%#p\n",
5507 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsImage));
5508
5509 do
5510 {
5511 /* sanity check */
5512 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5513 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5514
5515 /* Check arguments. */
5516 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
5517 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
5518 rc = VERR_INVALID_PARAMETER);
5519 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
5520 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
5521 rc = VERR_INVALID_PARAMETER);
5522 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
5523 ("uOpenFlags=%#x\n", uOpenFlags),
5524 rc = VERR_INVALID_PARAMETER);
5525 AssertMsgBreakStmt( !(uOpenFlags & VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)
5526 || (uOpenFlags & VD_OPEN_FLAGS_READONLY),
5527 ("uOpenFlags=%#x\n", uOpenFlags),
5528 rc = VERR_INVALID_PARAMETER);
5529
5530 /*
5531 * Destroy the current discard state first which might still have pending blocks
5532 * for the currently opened image which will be switched to readonly mode.
5533 */
5534 /* Lock disk for writing, as we modify pDisk information below. */
5535 rc2 = vdThreadStartWrite(pDisk);
5536 AssertRC(rc2);
5537 fLockWrite = true;
5538 rc = vdDiscardStateDestroy(pDisk);
5539 if (RT_FAILURE(rc))
5540 break;
5541 rc2 = vdThreadFinishWrite(pDisk);
5542 AssertRC(rc2);
5543 fLockWrite = false;
5544
5545 /* Set up image descriptor. */
5546 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
5547 if (!pImage)
5548 {
5549 rc = VERR_NO_MEMORY;
5550 break;
5551 }
5552 pImage->pszFilename = RTStrDup(pszFilename);
5553 if (!pImage->pszFilename)
5554 {
5555 rc = VERR_NO_MEMORY;
5556 break;
5557 }
5558
5559 pImage->VDIo.pDisk = pDisk;
5560 pImage->pVDIfsImage = pVDIfsImage;
5561
5562 rc = vdFindImageBackend(pszBackend, &pImage->Backend);
5563 if (RT_FAILURE(rc))
5564 break;
5565 if (!pImage->Backend)
5566 {
5567 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
5568 N_("VD: unknown backend name '%s'"), pszBackend);
5569 break;
5570 }
5571
5572 /*
5573 * Fail if the backend can't do async I/O but the
5574 * flag is set.
5575 */
5576 if ( !(pImage->Backend->uBackendCaps & VD_CAP_ASYNC)
5577 && (uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO))
5578 {
5579 rc = vdError(pDisk, VERR_NOT_SUPPORTED, RT_SRC_POS,
5580 N_("VD: Backend '%s' does not support async I/O"), pszBackend);
5581 break;
5582 }
5583
5584 /*
5585 * Fail if the backend doesn't support the discard operation but the
5586 * flag is set.
5587 */
5588 if ( !(pImage->Backend->uBackendCaps & VD_CAP_DISCARD)
5589 && (uOpenFlags & VD_OPEN_FLAGS_DISCARD))
5590 {
5591 rc = vdError(pDisk, VERR_VD_DISCARD_NOT_SUPPORTED, RT_SRC_POS,
5592 N_("VD: Backend '%s' does not support discard"), pszBackend);
5593 break;
5594 }
5595
5596 /* Set up the I/O interface. */
5597 pImage->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsImage);
5598 if (!pImage->VDIo.pInterfaceIo)
5599 {
5600 vdIfIoFallbackCallbacksSetup(&pImage->VDIo.VDIfIo);
5601 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
5602 pDisk, sizeof(VDINTERFACEIO), &pVDIfsImage);
5603 pImage->VDIo.pInterfaceIo = &pImage->VDIo.VDIfIo;
5604 }
5605
5606 /* Set up the internal I/O interface. */
5607 AssertBreakStmt(!VDIfIoIntGet(pVDIfsImage), rc = VERR_INVALID_PARAMETER);
5608 vdIfIoIntCallbacksSetup(&pImage->VDIo.VDIfIoInt);
5609 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
5610 &pImage->VDIo, sizeof(VDINTERFACEIOINT), &pImage->pVDIfsImage);
5611 AssertRC(rc);
5612
5613 pImage->uOpenFlags = uOpenFlags & (VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_DISCARD | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS);
5614 pImage->VDIo.fIgnoreFlush = (uOpenFlags & VD_OPEN_FLAGS_IGNORE_FLUSH) != 0;
5615 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
5616 uOpenFlags & ~(VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS),
5617 pDisk->pVDIfsDisk,
5618 pImage->pVDIfsImage,
5619 pDisk->enmType,
5620 &pImage->pBackendData);
5621 /*
5622 * If the image is corrupted and there is a repair method try to repair it
5623 * first if it was openend in read-write mode and open again afterwards.
5624 */
5625 if ( RT_UNLIKELY(rc == VERR_VD_IMAGE_CORRUPTED)
5626 && !(uOpenFlags & VD_OPEN_FLAGS_READONLY)
5627 && pImage->Backend->pfnRepair)
5628 {
5629 rc = pImage->Backend->pfnRepair(pszFilename, pDisk->pVDIfsDisk, pImage->pVDIfsImage, 0 /* fFlags */);
5630 if (RT_SUCCESS(rc))
5631 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
5632 uOpenFlags & ~(VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS),
5633 pDisk->pVDIfsDisk,
5634 pImage->pVDIfsImage,
5635 pDisk->enmType,
5636 &pImage->pBackendData);
5637 else
5638 {
5639 rc = vdError(pDisk, rc, RT_SRC_POS,
5640 N_("VD: error %Rrc repairing corrupted image file '%s'"), rc, pszFilename);
5641 break;
5642 }
5643 }
5644 else if (RT_UNLIKELY(rc == VERR_VD_IMAGE_CORRUPTED))
5645 {
5646 rc = vdError(pDisk, rc, RT_SRC_POS,
5647 N_("VD: Image file '%s' is corrupted and can't be opened"), pszFilename);
5648 break;
5649 }
5650
5651 /* If the open in read-write mode failed, retry in read-only mode. */
5652 if (RT_FAILURE(rc))
5653 {
5654 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
5655 && ( rc == VERR_ACCESS_DENIED
5656 || rc == VERR_PERMISSION_DENIED
5657 || rc == VERR_WRITE_PROTECT
5658 || rc == VERR_SHARING_VIOLATION
5659 || rc == VERR_FILE_LOCK_FAILED))
5660 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
5661 (uOpenFlags & ~(VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS))
5662 | VD_OPEN_FLAGS_READONLY,
5663 pDisk->pVDIfsDisk,
5664 pImage->pVDIfsImage,
5665 pDisk->enmType,
5666 &pImage->pBackendData);
5667 if (RT_FAILURE(rc))
5668 {
5669 rc = vdError(pDisk, rc, RT_SRC_POS,
5670 N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
5671 break;
5672 }
5673 }
5674
5675 /* Lock disk for writing, as we modify pDisk information below. */
5676 rc2 = vdThreadStartWrite(pDisk);
5677 AssertRC(rc2);
5678 fLockWrite = true;
5679
5680 pImage->VDIo.pBackendData = pImage->pBackendData;
5681
5682 /* Check image type. As the image itself has only partial knowledge
5683 * whether it's a base image or not, this info is derived here. The
5684 * base image can be fixed or normal, all others must be normal or
5685 * diff images. Some image formats don't distinguish between normal
5686 * and diff images, so this must be corrected here. */
5687 unsigned uImageFlags;
5688 uImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pBackendData);
5689 if (RT_FAILURE(rc))
5690 uImageFlags = VD_IMAGE_FLAGS_NONE;
5691 if ( RT_SUCCESS(rc)
5692 && !(uOpenFlags & VD_OPEN_FLAGS_INFO))
5693 {
5694 if ( pDisk->cImages == 0
5695 && (uImageFlags & VD_IMAGE_FLAGS_DIFF))
5696 {
5697 rc = VERR_VD_INVALID_TYPE;
5698 break;
5699 }
5700 else if (pDisk->cImages != 0)
5701 {
5702 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
5703 {
5704 rc = VERR_VD_INVALID_TYPE;
5705 break;
5706 }
5707 else
5708 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
5709 }
5710 }
5711
5712 /* Ensure we always get correct diff information, even if the backend
5713 * doesn't actually have a stored flag for this. It must not return
5714 * bogus information for the parent UUID if it is not a diff image. */
5715 RTUUID parentUuid;
5716 RTUuidClear(&parentUuid);
5717 rc2 = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, &parentUuid);
5718 if (RT_SUCCESS(rc2) && !RTUuidIsNull(&parentUuid))
5719 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
5720
5721 pImage->uImageFlags = uImageFlags;
5722
5723 /* Force sane optimization settings. It's not worth avoiding writes
5724 * to fixed size images. The overhead would have almost no payback. */
5725 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
5726 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
5727
5728 /** @todo optionally check UUIDs */
5729
5730 /* Cache disk information. */
5731 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
5732
5733 /* Cache PCHS geometry. */
5734 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
5735 &pDisk->PCHSGeometry);
5736 if (RT_FAILURE(rc2))
5737 {
5738 pDisk->PCHSGeometry.cCylinders = 0;
5739 pDisk->PCHSGeometry.cHeads = 0;
5740 pDisk->PCHSGeometry.cSectors = 0;
5741 }
5742 else
5743 {
5744 /* Make sure the PCHS geometry is properly clipped. */
5745 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
5746 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
5747 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
5748 }
5749
5750 /* Cache LCHS geometry. */
5751 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
5752 &pDisk->LCHSGeometry);
5753 if (RT_FAILURE(rc2))
5754 {
5755 pDisk->LCHSGeometry.cCylinders = 0;
5756 pDisk->LCHSGeometry.cHeads = 0;
5757 pDisk->LCHSGeometry.cSectors = 0;
5758 }
5759 else
5760 {
5761 /* Make sure the LCHS geometry is properly clipped. */
5762 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
5763 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
5764 }
5765
5766 if (pDisk->cImages != 0)
5767 {
5768 /* Switch previous image to read-only mode. */
5769 unsigned uOpenFlagsPrevImg;
5770 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
5771 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
5772 {
5773 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
5774 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
5775 }
5776 }
5777
5778 if (RT_SUCCESS(rc))
5779 {
5780 /* Image successfully opened, make it the last image. */
5781 vdAddImageToList(pDisk, pImage);
5782 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
5783 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
5784 }
5785 else
5786 {
5787 /* Error detected, but image opened. Close image. */
5788 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
5789 AssertRC(rc2);
5790 pImage->pBackendData = NULL;
5791 }
5792 } while (0);
5793
5794 if (RT_UNLIKELY(fLockWrite))
5795 {
5796 rc2 = vdThreadFinishWrite(pDisk);
5797 AssertRC(rc2);
5798 }
5799
5800 if (RT_FAILURE(rc))
5801 {
5802 if (pImage)
5803 {
5804 if (pImage->pszFilename)
5805 RTStrFree(pImage->pszFilename);
5806 RTMemFree(pImage);
5807 }
5808 }
5809
5810 LogFlowFunc(("returns %Rrc\n", rc));
5811 return rc;
5812}
5813
5814/**
5815 * Opens a cache image.
5816 *
5817 * @return VBox status code.
5818 * @param pDisk Pointer to the HDD container which should use the cache image.
5819 * @param pszBackend Name of the cache file backend to use (case insensitive).
5820 * @param pszFilename Name of the cache image to open.
5821 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
5822 * @param pVDIfsCache Pointer to the per-cache VD interface list.
5823 */
5824VBOXDDU_DECL(int) VDCacheOpen(PVDISK pDisk, const char *pszBackend,
5825 const char *pszFilename, unsigned uOpenFlags,
5826 PVDINTERFACE pVDIfsCache)
5827{
5828 int rc = VINF_SUCCESS;
5829 int rc2;
5830 bool fLockWrite = false;
5831 PVDCACHE pCache = NULL;
5832
5833 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsCache=%#p\n",
5834 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsCache));
5835
5836 do
5837 {
5838 /* sanity check */
5839 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5840 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5841
5842 /* Check arguments. */
5843 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
5844 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
5845 rc = VERR_INVALID_PARAMETER);
5846 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
5847 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
5848 rc = VERR_INVALID_PARAMETER);
5849 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
5850 ("uOpenFlags=%#x\n", uOpenFlags),
5851 rc = VERR_INVALID_PARAMETER);
5852
5853 /* Set up image descriptor. */
5854 pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
5855 if (!pCache)
5856 {
5857 rc = VERR_NO_MEMORY;
5858 break;
5859 }
5860 pCache->pszFilename = RTStrDup(pszFilename);
5861 if (!pCache->pszFilename)
5862 {
5863 rc = VERR_NO_MEMORY;
5864 break;
5865 }
5866
5867 pCache->VDIo.pDisk = pDisk;
5868 pCache->pVDIfsCache = pVDIfsCache;
5869
5870 rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
5871 if (RT_FAILURE(rc))
5872 break;
5873 if (!pCache->Backend)
5874 {
5875 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
5876 N_("VD: unknown backend name '%s'"), pszBackend);
5877 break;
5878 }
5879
5880 /* Set up the I/O interface. */
5881 pCache->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsCache);
5882 if (!pCache->VDIo.pInterfaceIo)
5883 {
5884 vdIfIoFallbackCallbacksSetup(&pCache->VDIo.VDIfIo);
5885 rc = VDInterfaceAdd(&pCache->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
5886 pDisk, sizeof(VDINTERFACEIO), &pVDIfsCache);
5887 pCache->VDIo.pInterfaceIo = &pCache->VDIo.VDIfIo;
5888 }
5889
5890 /* Set up the internal I/O interface. */
5891 AssertBreakStmt(!VDIfIoIntGet(pVDIfsCache), rc = VERR_INVALID_PARAMETER);
5892 vdIfIoIntCallbacksSetup(&pCache->VDIo.VDIfIoInt);
5893 rc = VDInterfaceAdd(&pCache->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
5894 &pCache->VDIo, sizeof(VDINTERFACEIOINT), &pCache->pVDIfsCache);
5895 AssertRC(rc);
5896
5897 pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
5898 rc = pCache->Backend->pfnOpen(pCache->pszFilename,
5899 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
5900 pDisk->pVDIfsDisk,
5901 pCache->pVDIfsCache,
5902 &pCache->pBackendData);
5903 /* If the open in read-write mode failed, retry in read-only mode. */
5904 if (RT_FAILURE(rc))
5905 {
5906 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
5907 && ( rc == VERR_ACCESS_DENIED
5908 || rc == VERR_PERMISSION_DENIED
5909 || rc == VERR_WRITE_PROTECT
5910 || rc == VERR_SHARING_VIOLATION
5911 || rc == VERR_FILE_LOCK_FAILED))
5912 rc = pCache->Backend->pfnOpen(pCache->pszFilename,
5913 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
5914 | VD_OPEN_FLAGS_READONLY,
5915 pDisk->pVDIfsDisk,
5916 pCache->pVDIfsCache,
5917 &pCache->pBackendData);
5918 if (RT_FAILURE(rc))
5919 {
5920 rc = vdError(pDisk, rc, RT_SRC_POS,
5921 N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
5922 break;
5923 }
5924 }
5925
5926 /* Lock disk for writing, as we modify pDisk information below. */
5927 rc2 = vdThreadStartWrite(pDisk);
5928 AssertRC(rc2);
5929 fLockWrite = true;
5930
5931 /*
5932 * Check that the modification UUID of the cache and last image
5933 * match. If not the image was modified in-between without the cache.
5934 * The cache might contain stale data.
5935 */
5936 RTUUID UuidImage, UuidCache;
5937
5938 rc = pCache->Backend->pfnGetModificationUuid(pCache->pBackendData,
5939 &UuidCache);
5940 if (RT_SUCCESS(rc))
5941 {
5942 rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
5943 &UuidImage);
5944 if (RT_SUCCESS(rc))
5945 {
5946 if (RTUuidCompare(&UuidImage, &UuidCache))
5947 rc = VERR_VD_CACHE_NOT_UP_TO_DATE;
5948 }
5949 }
5950
5951 /*
5952 * We assume that the user knows what he is doing if one of the images
5953 * doesn't support the modification uuid.
5954 */
5955 if (rc == VERR_NOT_SUPPORTED)
5956 rc = VINF_SUCCESS;
5957
5958 if (RT_SUCCESS(rc))
5959 {
5960 /* Cache successfully opened, make it the current one. */
5961 if (!pDisk->pCache)
5962 pDisk->pCache = pCache;
5963 else
5964 rc = VERR_VD_CACHE_ALREADY_EXISTS;
5965 }
5966
5967 if (RT_FAILURE(rc))
5968 {
5969 /* Error detected, but image opened. Close image. */
5970 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
5971 AssertRC(rc2);
5972 pCache->pBackendData = NULL;
5973 }
5974 } while (0);
5975
5976 if (RT_UNLIKELY(fLockWrite))
5977 {
5978 rc2 = vdThreadFinishWrite(pDisk);
5979 AssertRC(rc2);
5980 }
5981
5982 if (RT_FAILURE(rc))
5983 {
5984 if (pCache)
5985 {
5986 if (pCache->pszFilename)
5987 RTStrFree(pCache->pszFilename);
5988 RTMemFree(pCache);
5989 }
5990 }
5991
5992 LogFlowFunc(("returns %Rrc\n", rc));
5993 return rc;
5994}
5995
5996VBOXDDU_DECL(int) VDFilterAdd(PVDISK pDisk, const char *pszFilter, uint32_t fFlags,
5997 PVDINTERFACE pVDIfsFilter)
5998{
5999 int rc = VINF_SUCCESS;
6000 int rc2;
6001 bool fLockWrite = false;
6002 PVDFILTER pFilter = NULL;
6003
6004 LogFlowFunc(("pDisk=%#p pszFilter=\"%s\" pVDIfsFilter=%#p\n",
6005 pDisk, pszFilter, pVDIfsFilter));
6006
6007 do
6008 {
6009 /* sanity check */
6010 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6011 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6012
6013 /* Check arguments. */
6014 AssertMsgBreakStmt(VALID_PTR(pszFilter) && *pszFilter,
6015 ("pszFilter=%#p \"%s\"\n", pszFilter, pszFilter),
6016 rc = VERR_INVALID_PARAMETER);
6017
6018 AssertMsgBreakStmt(!(fFlags & ~VD_FILTER_FLAGS_MASK),
6019 ("Invalid flags set (fFlags=%#x)\n", fFlags),
6020 rc = VERR_INVALID_PARAMETER);
6021
6022 /* Set up image descriptor. */
6023 pFilter = (PVDFILTER)RTMemAllocZ(sizeof(VDFILTER));
6024 if (!pFilter)
6025 {
6026 rc = VERR_NO_MEMORY;
6027 break;
6028 }
6029
6030 rc = vdFindFilterBackend(pszFilter, &pFilter->pBackend);
6031 if (RT_FAILURE(rc))
6032 break;
6033 if (!pFilter->pBackend)
6034 {
6035 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6036 N_("VD: unknown filter backend name '%s'"), pszFilter);
6037 break;
6038 }
6039
6040 pFilter->VDIo.pDisk = pDisk;
6041 pFilter->pVDIfsFilter = pVDIfsFilter;
6042
6043 /* Set up the internal I/O interface. */
6044 AssertBreakStmt(!VDIfIoIntGet(pVDIfsFilter), rc = VERR_INVALID_PARAMETER);
6045 vdIfIoIntCallbacksSetup(&pFilter->VDIo.VDIfIoInt);
6046 rc = VDInterfaceAdd(&pFilter->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
6047 &pFilter->VDIo, sizeof(VDINTERFACEIOINT), &pFilter->pVDIfsFilter);
6048 AssertRC(rc);
6049
6050 rc = pFilter->pBackend->pfnCreate(pDisk->pVDIfsDisk, fFlags & VD_FILTER_FLAGS_INFO,
6051 pFilter->pVDIfsFilter, &pFilter->pvBackendData);
6052 if (RT_FAILURE(rc))
6053 break;
6054
6055 /* Lock disk for writing, as we modify pDisk information below. */
6056 rc2 = vdThreadStartWrite(pDisk);
6057 AssertRC(rc2);
6058 fLockWrite = true;
6059
6060 /* Add filter to chains. */
6061 if (fFlags & VD_FILTER_FLAGS_WRITE)
6062 {
6063 RTListAppend(&pDisk->ListFilterChainWrite, &pFilter->ListNodeChainWrite);
6064 vdFilterRetain(pFilter);
6065 }
6066
6067 if (fFlags & VD_FILTER_FLAGS_READ)
6068 {
6069 RTListAppend(&pDisk->ListFilterChainRead, &pFilter->ListNodeChainRead);
6070 vdFilterRetain(pFilter);
6071 }
6072 } while (0);
6073
6074 if (RT_UNLIKELY(fLockWrite))
6075 {
6076 rc2 = vdThreadFinishWrite(pDisk);
6077 AssertRC(rc2);
6078 }
6079
6080 if (RT_FAILURE(rc))
6081 {
6082 if (pFilter)
6083 RTMemFree(pFilter);
6084 }
6085
6086 LogFlowFunc(("returns %Rrc\n", rc));
6087 return rc;
6088}
6089
6090/**
6091 * Creates and opens a new base image file.
6092 *
6093 * @returns VBox status code.
6094 * @param pDisk Pointer to HDD container.
6095 * @param pszBackend Name of the image file backend to use.
6096 * @param pszFilename Name of the image file to create.
6097 * @param cbSize Image size in bytes.
6098 * @param uImageFlags Flags specifying special image features.
6099 * @param pszComment Pointer to image comment. NULL is ok.
6100 * @param pPCHSGeometry Pointer to physical disk geometry <= (16383,16,63). Not NULL.
6101 * @param pLCHSGeometry Pointer to logical disk geometry <= (x,255,63). Not NULL.
6102 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
6103 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
6104 * @param pVDIfsImage Pointer to the per-image VD interface list.
6105 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
6106 */
6107VBOXDDU_DECL(int) VDCreateBase(PVDISK pDisk, const char *pszBackend,
6108 const char *pszFilename, uint64_t cbSize,
6109 unsigned uImageFlags, const char *pszComment,
6110 PCVDGEOMETRY pPCHSGeometry,
6111 PCVDGEOMETRY pLCHSGeometry,
6112 PCRTUUID pUuid, unsigned uOpenFlags,
6113 PVDINTERFACE pVDIfsImage,
6114 PVDINTERFACE pVDIfsOperation)
6115{
6116 int rc = VINF_SUCCESS;
6117 int rc2;
6118 bool fLockWrite = false, fLockRead = false;
6119 PVDIMAGE pImage = NULL;
6120 RTUUID uuid;
6121
6122 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",
6123 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment,
6124 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
6125 pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
6126 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, pUuid,
6127 uOpenFlags, pVDIfsImage, pVDIfsOperation));
6128
6129 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
6130
6131 do
6132 {
6133 /* sanity check */
6134 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6135 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6136
6137 /* Check arguments. */
6138 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
6139 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
6140 rc = VERR_INVALID_PARAMETER);
6141 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
6142 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
6143 rc = VERR_INVALID_PARAMETER);
6144 AssertMsgBreakStmt(cbSize,
6145 ("cbSize=%llu\n", cbSize),
6146 rc = VERR_INVALID_PARAMETER);
6147 if (cbSize % 512)
6148 {
6149 rc = vdError(pDisk, VERR_VD_INVALID_SIZE, RT_SRC_POS,
6150 N_("VD: The given disk size %llu is not aligned on a sector boundary (512 bytes)"), cbSize);
6151 break;
6152 }
6153 AssertMsgBreakStmt( ((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0)
6154 || ((uImageFlags & (VD_IMAGE_FLAGS_FIXED | VD_IMAGE_FLAGS_DIFF)) != VD_IMAGE_FLAGS_FIXED),
6155 ("uImageFlags=%#x\n", uImageFlags),
6156 rc = VERR_INVALID_PARAMETER);
6157 /* The PCHS geometry fields may be 0 to leave it for later. */
6158 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
6159 && pPCHSGeometry->cHeads <= 16
6160 && pPCHSGeometry->cSectors <= 63,
6161 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
6162 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
6163 pPCHSGeometry->cSectors),
6164 rc = VERR_INVALID_PARAMETER);
6165 /* The LCHS geometry fields may be 0 to leave it to later autodetection. */
6166 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
6167 && pLCHSGeometry->cHeads <= 255
6168 && pLCHSGeometry->cSectors <= 63,
6169 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
6170 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
6171 pLCHSGeometry->cSectors),
6172 rc = VERR_INVALID_PARAMETER);
6173 /* The UUID may be NULL. */
6174 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
6175 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
6176 rc = VERR_INVALID_PARAMETER);
6177 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
6178 ("uOpenFlags=%#x\n", uOpenFlags),
6179 rc = VERR_INVALID_PARAMETER);
6180
6181 /* Check state. Needs a temporary read lock. Holding the write lock
6182 * all the time would be blocking other activities for too long. */
6183 rc2 = vdThreadStartRead(pDisk);
6184 AssertRC(rc2);
6185 fLockRead = true;
6186 AssertMsgBreakStmt(pDisk->cImages == 0,
6187 ("Create base image cannot be done with other images open\n"),
6188 rc = VERR_VD_INVALID_STATE);
6189 rc2 = vdThreadFinishRead(pDisk);
6190 AssertRC(rc2);
6191 fLockRead = false;
6192
6193 /* Set up image descriptor. */
6194 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
6195 if (!pImage)
6196 {
6197 rc = VERR_NO_MEMORY;
6198 break;
6199 }
6200 pImage->pszFilename = RTStrDup(pszFilename);
6201 if (!pImage->pszFilename)
6202 {
6203 rc = VERR_NO_MEMORY;
6204 break;
6205 }
6206 pImage->VDIo.pDisk = pDisk;
6207 pImage->pVDIfsImage = pVDIfsImage;
6208
6209 /* Set up the I/O interface. */
6210 pImage->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsImage);
6211 if (!pImage->VDIo.pInterfaceIo)
6212 {
6213 vdIfIoFallbackCallbacksSetup(&pImage->VDIo.VDIfIo);
6214 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
6215 pDisk, sizeof(VDINTERFACEIO), &pVDIfsImage);
6216 pImage->VDIo.pInterfaceIo = &pImage->VDIo.VDIfIo;
6217 }
6218
6219 /* Set up the internal I/O interface. */
6220 AssertBreakStmt(!VDIfIoIntGet(pVDIfsImage), rc = VERR_INVALID_PARAMETER);
6221 vdIfIoIntCallbacksSetup(&pImage->VDIo.VDIfIoInt);
6222 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
6223 &pImage->VDIo, sizeof(VDINTERFACEIOINT), &pImage->pVDIfsImage);
6224 AssertRC(rc);
6225
6226 rc = vdFindImageBackend(pszBackend, &pImage->Backend);
6227 if (RT_FAILURE(rc))
6228 break;
6229 if (!pImage->Backend)
6230 {
6231 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6232 N_("VD: unknown backend name '%s'"), pszBackend);
6233 break;
6234 }
6235 if (!(pImage->Backend->uBackendCaps & ( VD_CAP_CREATE_FIXED
6236 | VD_CAP_CREATE_DYNAMIC)))
6237 {
6238 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6239 N_("VD: backend '%s' cannot create base images"), pszBackend);
6240 break;
6241 }
6242 if ( ( (uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G)
6243 && !(pImage->Backend->uBackendCaps & VD_CAP_CREATE_SPLIT_2G))
6244 || ( (uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
6245 && RTStrICmp(pszBackend, "VMDK")))
6246 {
6247 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6248 N_("VD: backend '%s' does not support the selected image variant"), pszBackend);
6249 break;
6250 }
6251
6252 /* Create UUID if the caller didn't specify one. */
6253 if (!pUuid)
6254 {
6255 rc = RTUuidCreate(&uuid);
6256 if (RT_FAILURE(rc))
6257 {
6258 rc = vdError(pDisk, rc, RT_SRC_POS,
6259 N_("VD: cannot generate UUID for image '%s'"),
6260 pszFilename);
6261 break;
6262 }
6263 pUuid = &uuid;
6264 }
6265
6266 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
6267 uImageFlags &= ~VD_IMAGE_FLAGS_DIFF;
6268 pImage->VDIo.fIgnoreFlush = (uOpenFlags & VD_OPEN_FLAGS_IGNORE_FLUSH) != 0;
6269 rc = pImage->Backend->pfnCreate(pImage->pszFilename, cbSize,
6270 uImageFlags, pszComment, pPCHSGeometry,
6271 pLCHSGeometry, pUuid,
6272 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
6273 0, 99,
6274 pDisk->pVDIfsDisk,
6275 pImage->pVDIfsImage,
6276 pVDIfsOperation,
6277 pDisk->enmType,
6278 &pImage->pBackendData);
6279
6280 if (RT_SUCCESS(rc))
6281 {
6282 pImage->VDIo.pBackendData = pImage->pBackendData;
6283 pImage->uImageFlags = uImageFlags;
6284
6285 /* Force sane optimization settings. It's not worth avoiding writes
6286 * to fixed size images. The overhead would have almost no payback. */
6287 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
6288 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
6289
6290 /* Lock disk for writing, as we modify pDisk information below. */
6291 rc2 = vdThreadStartWrite(pDisk);
6292 AssertRC(rc2);
6293 fLockWrite = true;
6294
6295 /** @todo optionally check UUIDs */
6296
6297 /* Re-check state, as the lock wasn't held and another image
6298 * creation call could have been done by another thread. */
6299 AssertMsgStmt(pDisk->cImages == 0,
6300 ("Create base image cannot be done with other images open\n"),
6301 rc = VERR_VD_INVALID_STATE);
6302 }
6303
6304 if (RT_SUCCESS(rc))
6305 {
6306 /* Cache disk information. */
6307 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
6308
6309 /* Cache PCHS geometry. */
6310 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
6311 &pDisk->PCHSGeometry);
6312 if (RT_FAILURE(rc2))
6313 {
6314 pDisk->PCHSGeometry.cCylinders = 0;
6315 pDisk->PCHSGeometry.cHeads = 0;
6316 pDisk->PCHSGeometry.cSectors = 0;
6317 }
6318 else
6319 {
6320 /* Make sure the CHS geometry is properly clipped. */
6321 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
6322 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
6323 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
6324 }
6325
6326 /* Cache LCHS geometry. */
6327 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
6328 &pDisk->LCHSGeometry);
6329 if (RT_FAILURE(rc2))
6330 {
6331 pDisk->LCHSGeometry.cCylinders = 0;
6332 pDisk->LCHSGeometry.cHeads = 0;
6333 pDisk->LCHSGeometry.cSectors = 0;
6334 }
6335 else
6336 {
6337 /* Make sure the CHS geometry is properly clipped. */
6338 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
6339 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
6340 }
6341
6342 /* Image successfully opened, make it the last image. */
6343 vdAddImageToList(pDisk, pImage);
6344 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
6345 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
6346 }
6347 else
6348 {
6349 /* Error detected, image may or may not be opened. Close and delete
6350 * image if it was opened. */
6351 if (pImage->pBackendData)
6352 {
6353 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
6354 AssertRC(rc2);
6355 pImage->pBackendData = NULL;
6356 }
6357 }
6358 } while (0);
6359
6360 if (RT_UNLIKELY(fLockWrite))
6361 {
6362 rc2 = vdThreadFinishWrite(pDisk);
6363 AssertRC(rc2);
6364 }
6365 else if (RT_UNLIKELY(fLockRead))
6366 {
6367 rc2 = vdThreadFinishRead(pDisk);
6368 AssertRC(rc2);
6369 }
6370
6371 if (RT_FAILURE(rc))
6372 {
6373 if (pImage)
6374 {
6375 if (pImage->pszFilename)
6376 RTStrFree(pImage->pszFilename);
6377 RTMemFree(pImage);
6378 }
6379 }
6380
6381 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
6382 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
6383
6384 LogFlowFunc(("returns %Rrc\n", rc));
6385 return rc;
6386}
6387
6388/**
6389 * Creates and opens a new differencing image file in HDD container.
6390 * See comments for VDOpen function about differencing images.
6391 *
6392 * @returns VBox status code.
6393 * @param pDisk Pointer to HDD container.
6394 * @param pszBackend Name of the image file backend to use.
6395 * @param pszFilename Name of the differencing image file to create.
6396 * @param uImageFlags Flags specifying special image features.
6397 * @param pszComment Pointer to image comment. NULL is ok.
6398 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
6399 * @param pParentUuid New parent UUID of the image. If NULL, the UUID is queried automatically.
6400 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
6401 * @param pVDIfsImage Pointer to the per-image VD interface list.
6402 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
6403 */
6404VBOXDDU_DECL(int) VDCreateDiff(PVDISK pDisk, const char *pszBackend,
6405 const char *pszFilename, unsigned uImageFlags,
6406 const char *pszComment, PCRTUUID pUuid,
6407 PCRTUUID pParentUuid, unsigned uOpenFlags,
6408 PVDINTERFACE pVDIfsImage,
6409 PVDINTERFACE pVDIfsOperation)
6410{
6411 int rc = VINF_SUCCESS;
6412 int rc2;
6413 bool fLockWrite = false, fLockRead = false;
6414 PVDIMAGE pImage = NULL;
6415 RTUUID uuid;
6416
6417 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
6418 pDisk, pszBackend, pszFilename, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsImage, pVDIfsOperation));
6419
6420 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
6421
6422 do
6423 {
6424 /* sanity check */
6425 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6426 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6427
6428 /* Check arguments. */
6429 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
6430 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
6431 rc = VERR_INVALID_PARAMETER);
6432 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
6433 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
6434 rc = VERR_INVALID_PARAMETER);
6435 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
6436 ("uImageFlags=%#x\n", uImageFlags),
6437 rc = VERR_INVALID_PARAMETER);
6438 /* The UUID may be NULL. */
6439 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
6440 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
6441 rc = VERR_INVALID_PARAMETER);
6442 /* The parent UUID may be NULL. */
6443 AssertMsgBreakStmt(pParentUuid == NULL || VALID_PTR(pParentUuid),
6444 ("pParentUuid=%#p ParentUUID=%RTuuid\n", pParentUuid, pParentUuid),
6445 rc = VERR_INVALID_PARAMETER);
6446 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
6447 ("uOpenFlags=%#x\n", uOpenFlags),
6448 rc = VERR_INVALID_PARAMETER);
6449
6450 /* Check state. Needs a temporary read lock. Holding the write lock
6451 * all the time would be blocking other activities for too long. */
6452 rc2 = vdThreadStartRead(pDisk);
6453 AssertRC(rc2);
6454 fLockRead = true;
6455 AssertMsgBreakStmt(pDisk->cImages != 0,
6456 ("Create diff image cannot be done without other images open\n"),
6457 rc = VERR_VD_INVALID_STATE);
6458 rc2 = vdThreadFinishRead(pDisk);
6459 AssertRC(rc2);
6460 fLockRead = false;
6461
6462 /*
6463 * Destroy the current discard state first which might still have pending blocks
6464 * for the currently opened image which will be switched to readonly mode.
6465 */
6466 /* Lock disk for writing, as we modify pDisk information below. */
6467 rc2 = vdThreadStartWrite(pDisk);
6468 AssertRC(rc2);
6469 fLockWrite = true;
6470 rc = vdDiscardStateDestroy(pDisk);
6471 if (RT_FAILURE(rc))
6472 break;
6473 rc2 = vdThreadFinishWrite(pDisk);
6474 AssertRC(rc2);
6475 fLockWrite = false;
6476
6477 /* Set up image descriptor. */
6478 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
6479 if (!pImage)
6480 {
6481 rc = VERR_NO_MEMORY;
6482 break;
6483 }
6484 pImage->pszFilename = RTStrDup(pszFilename);
6485 if (!pImage->pszFilename)
6486 {
6487 rc = VERR_NO_MEMORY;
6488 break;
6489 }
6490
6491 rc = vdFindImageBackend(pszBackend, &pImage->Backend);
6492 if (RT_FAILURE(rc))
6493 break;
6494 if (!pImage->Backend)
6495 {
6496 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6497 N_("VD: unknown backend name '%s'"), pszBackend);
6498 break;
6499 }
6500 if ( !(pImage->Backend->uBackendCaps & VD_CAP_DIFF)
6501 || !(pImage->Backend->uBackendCaps & ( VD_CAP_CREATE_FIXED
6502 | VD_CAP_CREATE_DYNAMIC)))
6503 {
6504 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6505 N_("VD: backend '%s' cannot create diff images"), pszBackend);
6506 break;
6507 }
6508
6509 pImage->VDIo.pDisk = pDisk;
6510 pImage->pVDIfsImage = pVDIfsImage;
6511
6512 /* Set up the I/O interface. */
6513 pImage->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsImage);
6514 if (!pImage->VDIo.pInterfaceIo)
6515 {
6516 vdIfIoFallbackCallbacksSetup(&pImage->VDIo.VDIfIo);
6517 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
6518 pDisk, sizeof(VDINTERFACEIO), &pVDIfsImage);
6519 pImage->VDIo.pInterfaceIo = &pImage->VDIo.VDIfIo;
6520 }
6521
6522 /* Set up the internal I/O interface. */
6523 AssertBreakStmt(!VDIfIoIntGet(pVDIfsImage), rc = VERR_INVALID_PARAMETER);
6524 vdIfIoIntCallbacksSetup(&pImage->VDIo.VDIfIoInt);
6525 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
6526 &pImage->VDIo, sizeof(VDINTERFACEIOINT), &pImage->pVDIfsImage);
6527 AssertRC(rc);
6528
6529 /* Create UUID if the caller didn't specify one. */
6530 if (!pUuid)
6531 {
6532 rc = RTUuidCreate(&uuid);
6533 if (RT_FAILURE(rc))
6534 {
6535 rc = vdError(pDisk, rc, RT_SRC_POS,
6536 N_("VD: cannot generate UUID for image '%s'"),
6537 pszFilename);
6538 break;
6539 }
6540 pUuid = &uuid;
6541 }
6542
6543 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
6544 pImage->VDIo.fIgnoreFlush = (uOpenFlags & VD_OPEN_FLAGS_IGNORE_FLUSH) != 0;
6545 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
6546 rc = pImage->Backend->pfnCreate(pImage->pszFilename, pDisk->cbSize,
6547 uImageFlags | VD_IMAGE_FLAGS_DIFF,
6548 pszComment, &pDisk->PCHSGeometry,
6549 &pDisk->LCHSGeometry, pUuid,
6550 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
6551 0, 99,
6552 pDisk->pVDIfsDisk,
6553 pImage->pVDIfsImage,
6554 pVDIfsOperation,
6555 pDisk->enmType,
6556 &pImage->pBackendData);
6557
6558 if (RT_SUCCESS(rc))
6559 {
6560 pImage->VDIo.pBackendData = pImage->pBackendData;
6561 pImage->uImageFlags = uImageFlags;
6562
6563 /* Lock disk for writing, as we modify pDisk information below. */
6564 rc2 = vdThreadStartWrite(pDisk);
6565 AssertRC(rc2);
6566 fLockWrite = true;
6567
6568 /* Switch previous image to read-only mode. */
6569 unsigned uOpenFlagsPrevImg;
6570 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
6571 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
6572 {
6573 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
6574 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
6575 }
6576
6577 /** @todo optionally check UUIDs */
6578
6579 /* Re-check state, as the lock wasn't held and another image
6580 * creation call could have been done by another thread. */
6581 AssertMsgStmt(pDisk->cImages != 0,
6582 ("Create diff image cannot be done without other images open\n"),
6583 rc = VERR_VD_INVALID_STATE);
6584 }
6585
6586 if (RT_SUCCESS(rc))
6587 {
6588 RTUUID Uuid;
6589 RTTIMESPEC ts;
6590
6591 if (pParentUuid && !RTUuidIsNull(pParentUuid))
6592 {
6593 Uuid = *pParentUuid;
6594 pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
6595 }
6596 else
6597 {
6598 rc2 = pDisk->pLast->Backend->pfnGetUuid(pDisk->pLast->pBackendData,
6599 &Uuid);
6600 if (RT_SUCCESS(rc2))
6601 pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
6602 }
6603 rc2 = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
6604 &Uuid);
6605 if (RT_SUCCESS(rc2))
6606 pImage->Backend->pfnSetParentModificationUuid(pImage->pBackendData,
6607 &Uuid);
6608 if (pDisk->pLast->Backend->pfnGetTimestamp)
6609 rc2 = pDisk->pLast->Backend->pfnGetTimestamp(pDisk->pLast->pBackendData,
6610 &ts);
6611 else
6612 rc2 = VERR_NOT_IMPLEMENTED;
6613 if (RT_SUCCESS(rc2) && pImage->Backend->pfnSetParentTimestamp)
6614 pImage->Backend->pfnSetParentTimestamp(pImage->pBackendData, &ts);
6615
6616 if (pImage->Backend->pfnSetParentFilename)
6617 rc2 = pImage->Backend->pfnSetParentFilename(pImage->pBackendData, pDisk->pLast->pszFilename);
6618 }
6619
6620 if (RT_SUCCESS(rc))
6621 {
6622 /* Image successfully opened, make it the last image. */
6623 vdAddImageToList(pDisk, pImage);
6624 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
6625 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
6626 }
6627 else
6628 {
6629 /* Error detected, but image opened. Close and delete image. */
6630 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
6631 AssertRC(rc2);
6632 pImage->pBackendData = NULL;
6633 }
6634 } while (0);
6635
6636 if (RT_UNLIKELY(fLockWrite))
6637 {
6638 rc2 = vdThreadFinishWrite(pDisk);
6639 AssertRC(rc2);
6640 }
6641 else if (RT_UNLIKELY(fLockRead))
6642 {
6643 rc2 = vdThreadFinishRead(pDisk);
6644 AssertRC(rc2);
6645 }
6646
6647 if (RT_FAILURE(rc))
6648 {
6649 if (pImage)
6650 {
6651 if (pImage->pszFilename)
6652 RTStrFree(pImage->pszFilename);
6653 RTMemFree(pImage);
6654 }
6655 }
6656
6657 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
6658 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
6659
6660 LogFlowFunc(("returns %Rrc\n", rc));
6661 return rc;
6662}
6663
6664
6665/**
6666 * Creates and opens new cache image file in HDD container.
6667 *
6668 * @return VBox status code.
6669 * @param pDisk Name of the cache file backend to use (case insensitive).
6670 * @param pszFilename Name of the differencing cache file to create.
6671 * @param cbSize Maximum size of the cache.
6672 * @param uImageFlags Flags specifying special cache features.
6673 * @param pszComment Pointer to image comment. NULL is ok.
6674 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
6675 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
6676 * @param pVDIfsCache Pointer to the per-cache VD interface list.
6677 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
6678 */
6679VBOXDDU_DECL(int) VDCreateCache(PVDISK pDisk, const char *pszBackend,
6680 const char *pszFilename, uint64_t cbSize,
6681 unsigned uImageFlags, const char *pszComment,
6682 PCRTUUID pUuid, unsigned uOpenFlags,
6683 PVDINTERFACE pVDIfsCache, PVDINTERFACE pVDIfsOperation)
6684{
6685 int rc = VINF_SUCCESS;
6686 int rc2;
6687 bool fLockWrite = false, fLockRead = false;
6688 PVDCACHE pCache = NULL;
6689 RTUUID uuid;
6690
6691 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
6692 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsCache, pVDIfsOperation));
6693
6694 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
6695
6696 do
6697 {
6698 /* sanity check */
6699 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6700 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6701
6702 /* Check arguments. */
6703 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
6704 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
6705 rc = VERR_INVALID_PARAMETER);
6706 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
6707 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
6708 rc = VERR_INVALID_PARAMETER);
6709 AssertMsgBreakStmt(cbSize,
6710 ("cbSize=%llu\n", cbSize),
6711 rc = VERR_INVALID_PARAMETER);
6712 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
6713 ("uImageFlags=%#x\n", uImageFlags),
6714 rc = VERR_INVALID_PARAMETER);
6715 /* The UUID may be NULL. */
6716 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
6717 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
6718 rc = VERR_INVALID_PARAMETER);
6719 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
6720 ("uOpenFlags=%#x\n", uOpenFlags),
6721 rc = VERR_INVALID_PARAMETER);
6722
6723 /* Check state. Needs a temporary read lock. Holding the write lock
6724 * all the time would be blocking other activities for too long. */
6725 rc2 = vdThreadStartRead(pDisk);
6726 AssertRC(rc2);
6727 fLockRead = true;
6728 AssertMsgBreakStmt(!pDisk->pCache,
6729 ("Create cache image cannot be done with a cache already attached\n"),
6730 rc = VERR_VD_CACHE_ALREADY_EXISTS);
6731 rc2 = vdThreadFinishRead(pDisk);
6732 AssertRC(rc2);
6733 fLockRead = false;
6734
6735 /* Set up image descriptor. */
6736 pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
6737 if (!pCache)
6738 {
6739 rc = VERR_NO_MEMORY;
6740 break;
6741 }
6742 pCache->pszFilename = RTStrDup(pszFilename);
6743 if (!pCache->pszFilename)
6744 {
6745 rc = VERR_NO_MEMORY;
6746 break;
6747 }
6748
6749 rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
6750 if (RT_FAILURE(rc))
6751 break;
6752 if (!pCache->Backend)
6753 {
6754 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6755 N_("VD: unknown backend name '%s'"), pszBackend);
6756 break;
6757 }
6758
6759 pCache->VDIo.pDisk = pDisk;
6760 pCache->pVDIfsCache = pVDIfsCache;
6761
6762 /* Set up the I/O interface. */
6763 pCache->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsCache);
6764 if (!pCache->VDIo.pInterfaceIo)
6765 {
6766 vdIfIoFallbackCallbacksSetup(&pCache->VDIo.VDIfIo);
6767 rc = VDInterfaceAdd(&pCache->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
6768 pDisk, sizeof(VDINTERFACEIO), &pVDIfsCache);
6769 pCache->VDIo.pInterfaceIo = &pCache->VDIo.VDIfIo;
6770 }
6771
6772 /* Set up the internal I/O interface. */
6773 AssertBreakStmt(!VDIfIoIntGet(pVDIfsCache), rc = VERR_INVALID_PARAMETER);
6774 vdIfIoIntCallbacksSetup(&pCache->VDIo.VDIfIoInt);
6775 rc = VDInterfaceAdd(&pCache->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
6776 &pCache->VDIo, sizeof(VDINTERFACEIOINT), &pCache->pVDIfsCache);
6777 AssertRC(rc);
6778
6779 /* Create UUID if the caller didn't specify one. */
6780 if (!pUuid)
6781 {
6782 rc = RTUuidCreate(&uuid);
6783 if (RT_FAILURE(rc))
6784 {
6785 rc = vdError(pDisk, rc, RT_SRC_POS,
6786 N_("VD: cannot generate UUID for image '%s'"),
6787 pszFilename);
6788 break;
6789 }
6790 pUuid = &uuid;
6791 }
6792
6793 pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
6794 pCache->VDIo.fIgnoreFlush = (uOpenFlags & VD_OPEN_FLAGS_IGNORE_FLUSH) != 0;
6795 rc = pCache->Backend->pfnCreate(pCache->pszFilename, cbSize,
6796 uImageFlags,
6797 pszComment, pUuid,
6798 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
6799 0, 99,
6800 pDisk->pVDIfsDisk,
6801 pCache->pVDIfsCache,
6802 pVDIfsOperation,
6803 &pCache->pBackendData);
6804
6805 if (RT_SUCCESS(rc))
6806 {
6807 /* Lock disk for writing, as we modify pDisk information below. */
6808 rc2 = vdThreadStartWrite(pDisk);
6809 AssertRC(rc2);
6810 fLockWrite = true;
6811
6812 pCache->VDIo.pBackendData = pCache->pBackendData;
6813
6814 /* Re-check state, as the lock wasn't held and another image
6815 * creation call could have been done by another thread. */
6816 AssertMsgStmt(!pDisk->pCache,
6817 ("Create cache image cannot be done with another cache open\n"),
6818 rc = VERR_VD_CACHE_ALREADY_EXISTS);
6819 }
6820
6821 if ( RT_SUCCESS(rc)
6822 && pDisk->pLast)
6823 {
6824 RTUUID UuidModification;
6825
6826 /* Set same modification Uuid as the last image. */
6827 rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
6828 &UuidModification);
6829 if (RT_SUCCESS(rc))
6830 {
6831 rc = pCache->Backend->pfnSetModificationUuid(pCache->pBackendData,
6832 &UuidModification);
6833 }
6834
6835 if (rc == VERR_NOT_SUPPORTED)
6836 rc = VINF_SUCCESS;
6837 }
6838
6839 if (RT_SUCCESS(rc))
6840 {
6841 /* Cache successfully created. */
6842 pDisk->pCache = pCache;
6843 }
6844 else
6845 {
6846 /* Error detected, but image opened. Close and delete image. */
6847 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, true);
6848 AssertRC(rc2);
6849 pCache->pBackendData = NULL;
6850 }
6851 } while (0);
6852
6853 if (RT_UNLIKELY(fLockWrite))
6854 {
6855 rc2 = vdThreadFinishWrite(pDisk);
6856 AssertRC(rc2);
6857 }
6858 else if (RT_UNLIKELY(fLockRead))
6859 {
6860 rc2 = vdThreadFinishRead(pDisk);
6861 AssertRC(rc2);
6862 }
6863
6864 if (RT_FAILURE(rc))
6865 {
6866 if (pCache)
6867 {
6868 if (pCache->pszFilename)
6869 RTStrFree(pCache->pszFilename);
6870 RTMemFree(pCache);
6871 }
6872 }
6873
6874 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
6875 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
6876
6877 LogFlowFunc(("returns %Rrc\n", rc));
6878 return rc;
6879}
6880
6881/**
6882 * Merges two images (not necessarily with direct parent/child relationship).
6883 * As a side effect the source image and potentially the other images which
6884 * are also merged to the destination are deleted from both the disk and the
6885 * images in the HDD container.
6886 *
6887 * @returns VBox status code.
6888 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6889 * @param pDisk Pointer to HDD container.
6890 * @param nImageFrom Name of the image file to merge from.
6891 * @param nImageTo Name of the image file to merge to.
6892 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
6893 */
6894VBOXDDU_DECL(int) VDMerge(PVDISK pDisk, unsigned nImageFrom,
6895 unsigned nImageTo, PVDINTERFACE pVDIfsOperation)
6896{
6897 int rc = VINF_SUCCESS;
6898 int rc2;
6899 bool fLockWrite = false, fLockRead = false;
6900 void *pvBuf = NULL;
6901
6902 LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pVDIfsOperation=%#p\n",
6903 pDisk, nImageFrom, nImageTo, pVDIfsOperation));
6904
6905 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
6906
6907 do
6908 {
6909 /* sanity check */
6910 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6911 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6912
6913 /* For simplicity reasons lock for writing as the image reopen below
6914 * might need it. After all the reopen is usually needed. */
6915 rc2 = vdThreadStartWrite(pDisk);
6916 AssertRC(rc2);
6917 fLockWrite = true;
6918 PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
6919 PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
6920 if (!pImageFrom || !pImageTo)
6921 {
6922 rc = VERR_VD_IMAGE_NOT_FOUND;
6923 break;
6924 }
6925 AssertBreakStmt(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
6926
6927 /* Make sure destination image is writable. */
6928 unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
6929 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
6930 {
6931 /*
6932 * Clear skip consistency checks because the image is made writable now and
6933 * skipping consistency checks is only possible for readonly images.
6934 */
6935 uOpenFlags &= ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS);
6936 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
6937 uOpenFlags);
6938 if (RT_FAILURE(rc))
6939 break;
6940 }
6941
6942 /* Get size of destination image. */
6943 uint64_t cbSize = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
6944 rc2 = vdThreadFinishWrite(pDisk);
6945 AssertRC(rc2);
6946 fLockWrite = false;
6947
6948 /* Allocate tmp buffer. */
6949 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
6950 if (!pvBuf)
6951 {
6952 rc = VERR_NO_MEMORY;
6953 break;
6954 }
6955
6956 /* Merging is done directly on the images itself. This potentially
6957 * causes trouble if the disk is full in the middle of operation. */
6958 if (nImageFrom < nImageTo)
6959 {
6960 /* Merge parent state into child. This means writing all not
6961 * allocated blocks in the destination image which are allocated in
6962 * the images to be merged. */
6963 uint64_t uOffset = 0;
6964 uint64_t cbRemaining = cbSize;
6965 do
6966 {
6967 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
6968 RTSGSEG SegmentBuf;
6969 RTSGBUF SgBuf;
6970 VDIOCTX IoCtx;
6971
6972 SegmentBuf.pvSeg = pvBuf;
6973 SegmentBuf.cbSeg = VD_MERGE_BUFFER_SIZE;
6974 RTSgBufInit(&SgBuf, &SegmentBuf, 1);
6975 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_READ, 0, 0, NULL,
6976 &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
6977
6978 /* Need to hold the write lock during a read-write operation. */
6979 rc2 = vdThreadStartWrite(pDisk);
6980 AssertRC(rc2);
6981 fLockWrite = true;
6982
6983 rc = pImageTo->Backend->pfnRead(pImageTo->pBackendData,
6984 uOffset, cbThisRead,
6985 &IoCtx, &cbThisRead);
6986 if (rc == VERR_VD_BLOCK_FREE)
6987 {
6988 /* Search for image with allocated block. Do not attempt to
6989 * read more than the previous reads marked as valid.
6990 * Otherwise this would return stale data when different
6991 * block sizes are used for the images. */
6992 for (PVDIMAGE pCurrImage = pImageTo->pPrev;
6993 pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VERR_VD_BLOCK_FREE;
6994 pCurrImage = pCurrImage->pPrev)
6995 {
6996 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
6997 uOffset, cbThisRead,
6998 &IoCtx, &cbThisRead);
6999 }
7000
7001 if (rc != VERR_VD_BLOCK_FREE)
7002 {
7003 if (RT_FAILURE(rc))
7004 break;
7005 /* Updating the cache is required because this might be a live merge. */
7006 rc = vdWriteHelperEx(pDisk, pImageTo, pImageFrom->pPrev,
7007 uOffset, pvBuf, cbThisRead,
7008 VDIOCTX_FLAGS_READ_UPDATE_CACHE, 0);
7009 if (RT_FAILURE(rc))
7010 break;
7011 }
7012 else
7013 rc = VINF_SUCCESS;
7014 }
7015 else if (RT_FAILURE(rc))
7016 break;
7017
7018 rc2 = vdThreadFinishWrite(pDisk);
7019 AssertRC(rc2);
7020 fLockWrite = false;
7021
7022 uOffset += cbThisRead;
7023 cbRemaining -= cbThisRead;
7024
7025 if (pIfProgress && pIfProgress->pfnProgress)
7026 {
7027 /** @todo r=klaus: this can update the progress to the same
7028 * percentage over and over again if the image format makes
7029 * relatively small increments. */
7030 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
7031 uOffset * 99 / cbSize);
7032 if (RT_FAILURE(rc))
7033 break;
7034 }
7035 } while (uOffset < cbSize);
7036 }
7037 else
7038 {
7039 /*
7040 * We may need to update the parent uuid of the child coming after
7041 * the last image to be merged. We have to reopen it read/write.
7042 *
7043 * This is done before we do the actual merge to prevent an
7044 * inconsistent chain if the mode change fails for some reason.
7045 */
7046 if (pImageFrom->pNext)
7047 {
7048 PVDIMAGE pImageChild = pImageFrom->pNext;
7049
7050 /* Take the write lock. */
7051 rc2 = vdThreadStartWrite(pDisk);
7052 AssertRC(rc2);
7053 fLockWrite = true;
7054
7055 /* We need to open the image in read/write mode. */
7056 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
7057
7058 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
7059 {
7060 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
7061 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
7062 uOpenFlags);
7063 if (RT_FAILURE(rc))
7064 break;
7065 }
7066
7067 rc2 = vdThreadFinishWrite(pDisk);
7068 AssertRC(rc2);
7069 fLockWrite = false;
7070 }
7071
7072 /* If the merge is from the last image we have to relay all writes
7073 * to the merge destination as well, so that concurrent writes
7074 * (in case of a live merge) are handled correctly. */
7075 if (!pImageFrom->pNext)
7076 {
7077 /* Take the write lock. */
7078 rc2 = vdThreadStartWrite(pDisk);
7079 AssertRC(rc2);
7080 fLockWrite = true;
7081
7082 pDisk->pImageRelay = pImageTo;
7083
7084 rc2 = vdThreadFinishWrite(pDisk);
7085 AssertRC(rc2);
7086 fLockWrite = false;
7087 }
7088
7089 /* Merge child state into parent. This means writing all blocks
7090 * which are allocated in the image up to the source image to the
7091 * destination image. */
7092 uint64_t uOffset = 0;
7093 uint64_t cbRemaining = cbSize;
7094 do
7095 {
7096 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
7097 RTSGSEG SegmentBuf;
7098 RTSGBUF SgBuf;
7099 VDIOCTX IoCtx;
7100
7101 rc = VERR_VD_BLOCK_FREE;
7102
7103 SegmentBuf.pvSeg = pvBuf;
7104 SegmentBuf.cbSeg = VD_MERGE_BUFFER_SIZE;
7105 RTSgBufInit(&SgBuf, &SegmentBuf, 1);
7106 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_READ, 0, 0, NULL,
7107 &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
7108
7109 /* Need to hold the write lock during a read-write operation. */
7110 rc2 = vdThreadStartWrite(pDisk);
7111 AssertRC(rc2);
7112 fLockWrite = true;
7113
7114 /* Search for image with allocated block. Do not attempt to
7115 * read more than the previous reads marked as valid. Otherwise
7116 * this would return stale data when different block sizes are
7117 * used for the images. */
7118 for (PVDIMAGE pCurrImage = pImageFrom;
7119 pCurrImage != NULL && pCurrImage != pImageTo && rc == VERR_VD_BLOCK_FREE;
7120 pCurrImage = pCurrImage->pPrev)
7121 {
7122 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
7123 uOffset, cbThisRead,
7124 &IoCtx, &cbThisRead);
7125 }
7126
7127 if (rc != VERR_VD_BLOCK_FREE)
7128 {
7129 if (RT_FAILURE(rc))
7130 break;
7131 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
7132 cbThisRead, VDIOCTX_FLAGS_READ_UPDATE_CACHE);
7133 if (RT_FAILURE(rc))
7134 break;
7135 }
7136 else
7137 rc = VINF_SUCCESS;
7138
7139 rc2 = vdThreadFinishWrite(pDisk);
7140 AssertRC(rc2);
7141 fLockWrite = false;
7142
7143 uOffset += cbThisRead;
7144 cbRemaining -= cbThisRead;
7145
7146 if (pIfProgress && pIfProgress->pfnProgress)
7147 {
7148 /** @todo r=klaus: this can update the progress to the same
7149 * percentage over and over again if the image format makes
7150 * relatively small increments. */
7151 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
7152 uOffset * 99 / cbSize);
7153 if (RT_FAILURE(rc))
7154 break;
7155 }
7156 } while (uOffset < cbSize);
7157
7158 /* In case we set up a "write proxy" image above we must clear
7159 * this again now to prevent stray writes. Failure or not. */
7160 if (!pImageFrom->pNext)
7161 {
7162 /* Take the write lock. */
7163 rc2 = vdThreadStartWrite(pDisk);
7164 AssertRC(rc2);
7165 fLockWrite = true;
7166
7167 pDisk->pImageRelay = NULL;
7168
7169 rc2 = vdThreadFinishWrite(pDisk);
7170 AssertRC(rc2);
7171 fLockWrite = false;
7172 }
7173 }
7174
7175 /*
7176 * Leave in case of an error to avoid corrupted data in the image chain
7177 * (includes cancelling the operation by the user).
7178 */
7179 if (RT_FAILURE(rc))
7180 break;
7181
7182 /* Need to hold the write lock while finishing the merge. */
7183 rc2 = vdThreadStartWrite(pDisk);
7184 AssertRC(rc2);
7185 fLockWrite = true;
7186
7187 /* Update parent UUID so that image chain is consistent.
7188 * The two attempts work around the problem that some backends
7189 * (e.g. iSCSI) do not support UUIDs, so we exploit the fact that
7190 * so far there can only be one such image in the chain. */
7191 /** @todo needs a better long-term solution, passing the UUID
7192 * knowledge from the caller or some such */
7193 RTUUID Uuid;
7194 PVDIMAGE pImageChild = NULL;
7195 if (nImageFrom < nImageTo)
7196 {
7197 if (pImageFrom->pPrev)
7198 {
7199 /* plan A: ask the parent itself for its UUID */
7200 rc = pImageFrom->pPrev->Backend->pfnGetUuid(pImageFrom->pPrev->pBackendData,
7201 &Uuid);
7202 if (RT_FAILURE(rc))
7203 {
7204 /* plan B: ask the child of the parent for parent UUID */
7205 rc = pImageFrom->Backend->pfnGetParentUuid(pImageFrom->pBackendData,
7206 &Uuid);
7207 }
7208 AssertRC(rc);
7209 }
7210 else
7211 RTUuidClear(&Uuid);
7212 rc = pImageTo->Backend->pfnSetParentUuid(pImageTo->pBackendData,
7213 &Uuid);
7214 AssertRC(rc);
7215 }
7216 else
7217 {
7218 /* Update the parent uuid of the child of the last merged image. */
7219 if (pImageFrom->pNext)
7220 {
7221 /* plan A: ask the parent itself for its UUID */
7222 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pBackendData,
7223 &Uuid);
7224 if (RT_FAILURE(rc))
7225 {
7226 /* plan B: ask the child of the parent for parent UUID */
7227 rc = pImageTo->pNext->Backend->pfnGetParentUuid(pImageTo->pNext->pBackendData,
7228 &Uuid);
7229 }
7230 AssertRC(rc);
7231
7232 rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext->pBackendData,
7233 &Uuid);
7234 AssertRC(rc);
7235
7236 pImageChild = pImageFrom->pNext;
7237 }
7238 }
7239
7240 /* Delete the no longer needed images. */
7241 PVDIMAGE pImg = pImageFrom, pTmp;
7242 while (pImg != pImageTo)
7243 {
7244 if (nImageFrom < nImageTo)
7245 pTmp = pImg->pNext;
7246 else
7247 pTmp = pImg->pPrev;
7248 vdRemoveImageFromList(pDisk, pImg);
7249 pImg->Backend->pfnClose(pImg->pBackendData, true);
7250 RTMemFree(pImg->pszFilename);
7251 RTMemFree(pImg);
7252 pImg = pTmp;
7253 }
7254
7255 /* Make sure destination image is back to read only if necessary. */
7256 if (pImageTo != pDisk->pLast)
7257 {
7258 uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
7259 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
7260 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
7261 uOpenFlags);
7262 if (RT_FAILURE(rc))
7263 break;
7264 }
7265
7266 /*
7267 * Make sure the child is readonly
7268 * for the child -> parent merge direction
7269 * if necessary.
7270 */
7271 if ( nImageFrom > nImageTo
7272 && pImageChild
7273 && pImageChild != pDisk->pLast)
7274 {
7275 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
7276 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
7277 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
7278 uOpenFlags);
7279 if (RT_FAILURE(rc))
7280 break;
7281 }
7282 } while (0);
7283
7284 if (RT_UNLIKELY(fLockWrite))
7285 {
7286 rc2 = vdThreadFinishWrite(pDisk);
7287 AssertRC(rc2);
7288 }
7289 else if (RT_UNLIKELY(fLockRead))
7290 {
7291 rc2 = vdThreadFinishRead(pDisk);
7292 AssertRC(rc2);
7293 }
7294
7295 if (pvBuf)
7296 RTMemTmpFree(pvBuf);
7297
7298 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
7299 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
7300
7301 LogFlowFunc(("returns %Rrc\n", rc));
7302 return rc;
7303}
7304
7305/**
7306 * Copies an image from one HDD container to another - extended version.
7307 * The copy is opened in the target HDD container.
7308 * It is possible to convert between different image formats, because the
7309 * backend for the destination may be different from the source.
7310 * If both the source and destination reference the same HDD container,
7311 * then the image is moved (by copying/deleting or renaming) to the new location.
7312 * The source container is unchanged if the move operation fails, otherwise
7313 * the image at the new location is opened in the same way as the old one was.
7314 *
7315 * @note The read/write accesses across disks are not synchronized, just the
7316 * accesses to each disk. Once there is a use case which requires a defined
7317 * read/write behavior in this situation this needs to be extended.
7318 *
7319 * @returns VBox status code.
7320 * @retval VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7321 * @param pDiskFrom Pointer to source HDD container.
7322 * @param nImage Image number, counts from 0. 0 is always base image of container.
7323 * @param pDiskTo Pointer to destination HDD container.
7324 * @param pszBackend Name of the image file backend to use (may be NULL to use the same as the source, case insensitive).
7325 * @param pszFilename New name of the image (may be NULL to specify that the
7326 * copy destination is the destination container, or
7327 * if pDiskFrom == pDiskTo, i.e. when moving).
7328 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
7329 * @param cbSize New image size (0 means leave unchanged).
7330 * @param nImageFromSame todo
7331 * @param nImageToSame todo
7332 * @param uImageFlags Flags specifying special destination image features.
7333 * @param pDstUuid New UUID of the destination image. If NULL, a new UUID is created.
7334 * This parameter is used if and only if a true copy is created.
7335 * In all rename/move cases or copy to existing image cases the modification UUIDs are copied over.
7336 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
7337 * Only used if the destination image is created.
7338 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
7339 * @param pDstVDIfsImage Pointer to the per-image VD interface list, for the
7340 * destination image.
7341 * @param pDstVDIfsOperation Pointer to the per-operation VD interface list,
7342 * for the destination operation.
7343 */
7344VBOXDDU_DECL(int) VDCopyEx(PVDISK pDiskFrom, unsigned nImage, PVDISK pDiskTo,
7345 const char *pszBackend, const char *pszFilename,
7346 bool fMoveByRename, uint64_t cbSize,
7347 unsigned nImageFromSame, unsigned nImageToSame,
7348 unsigned uImageFlags, PCRTUUID pDstUuid,
7349 unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
7350 PVDINTERFACE pDstVDIfsImage,
7351 PVDINTERFACE pDstVDIfsOperation)
7352{
7353 int rc = VINF_SUCCESS;
7354 int rc2;
7355 bool fLockReadFrom = false, fLockWriteFrom = false, fLockWriteTo = false;
7356 PVDIMAGE pImageTo = NULL;
7357
7358 LogFlowFunc(("pDiskFrom=%#p nImage=%u pDiskTo=%#p pszBackend=\"%s\" pszFilename=\"%s\" fMoveByRename=%d cbSize=%llu nImageFromSame=%u nImageToSame=%u uImageFlags=%#x pDstUuid=%#p uOpenFlags=%#x pVDIfsOperation=%#p pDstVDIfsImage=%#p pDstVDIfsOperation=%#p\n",
7359 pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, nImageFromSame, nImageToSame, uImageFlags, pDstUuid, uOpenFlags, pVDIfsOperation, pDstVDIfsImage, pDstVDIfsOperation));
7360
7361 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
7362 PVDINTERFACEPROGRESS pDstIfProgress = VDIfProgressGet(pDstVDIfsOperation);
7363
7364 do {
7365 /* Check arguments. */
7366 AssertMsgBreakStmt(VALID_PTR(pDiskFrom), ("pDiskFrom=%#p\n", pDiskFrom),
7367 rc = VERR_INVALID_PARAMETER);
7368 AssertMsg(pDiskFrom->u32Signature == VDISK_SIGNATURE,
7369 ("u32Signature=%08x\n", pDiskFrom->u32Signature));
7370
7371 rc2 = vdThreadStartRead(pDiskFrom);
7372 AssertRC(rc2);
7373 fLockReadFrom = true;
7374 PVDIMAGE pImageFrom = vdGetImageByNumber(pDiskFrom, nImage);
7375 AssertPtrBreakStmt(pImageFrom, rc = VERR_VD_IMAGE_NOT_FOUND);
7376 AssertMsgBreakStmt(VALID_PTR(pDiskTo), ("pDiskTo=%#p\n", pDiskTo),
7377 rc = VERR_INVALID_PARAMETER);
7378 AssertMsg(pDiskTo->u32Signature == VDISK_SIGNATURE,
7379 ("u32Signature=%08x\n", pDiskTo->u32Signature));
7380 AssertMsgBreakStmt( (nImageFromSame < nImage || nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN)
7381 && (nImageToSame < pDiskTo->cImages || nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
7382 && ( (nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN && nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
7383 || (nImageFromSame != VD_IMAGE_CONTENT_UNKNOWN && nImageToSame != VD_IMAGE_CONTENT_UNKNOWN)),
7384 ("nImageFromSame=%u nImageToSame=%u\n", nImageFromSame, nImageToSame),
7385 rc = VERR_INVALID_PARAMETER);
7386
7387 /* Move the image. */
7388 if (pDiskFrom == pDiskTo)
7389 {
7390 /* Rename only works when backends are the same, are file based
7391 * and the rename method is implemented. */
7392 if ( fMoveByRename
7393 && !RTStrICmp(pszBackend, pImageFrom->Backend->pszBackendName)
7394 && pImageFrom->Backend->uBackendCaps & VD_CAP_FILE
7395 && pImageFrom->Backend->pfnRename)
7396 {
7397 rc2 = vdThreadFinishRead(pDiskFrom);
7398 AssertRC(rc2);
7399 fLockReadFrom = false;
7400
7401 rc2 = vdThreadStartWrite(pDiskFrom);
7402 AssertRC(rc2);
7403 fLockWriteFrom = true;
7404 rc = pImageFrom->Backend->pfnRename(pImageFrom->pBackendData, pszFilename ? pszFilename : pImageFrom->pszFilename);
7405 break;
7406 }
7407
7408 /** @todo Moving (including shrinking/growing) of the image is
7409 * requested, but the rename attempt failed or it wasn't possible.
7410 * Must now copy image to temp location. */
7411 AssertReleaseMsgFailed(("VDCopy: moving by copy/delete not implemented\n"));
7412 }
7413
7414 /* pszFilename is allowed to be NULL, as this indicates copy to the existing image. */
7415 AssertMsgBreakStmt(pszFilename == NULL || (VALID_PTR(pszFilename) && *pszFilename),
7416 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
7417 rc = VERR_INVALID_PARAMETER);
7418
7419 uint64_t cbSizeFrom;
7420 cbSizeFrom = pImageFrom->Backend->pfnGetSize(pImageFrom->pBackendData);
7421 if (cbSizeFrom == 0)
7422 {
7423 rc = VERR_VD_VALUE_NOT_FOUND;
7424 break;
7425 }
7426
7427 VDGEOMETRY PCHSGeometryFrom = {0, 0, 0};
7428 VDGEOMETRY LCHSGeometryFrom = {0, 0, 0};
7429 pImageFrom->Backend->pfnGetPCHSGeometry(pImageFrom->pBackendData, &PCHSGeometryFrom);
7430 pImageFrom->Backend->pfnGetLCHSGeometry(pImageFrom->pBackendData, &LCHSGeometryFrom);
7431
7432 RTUUID ImageUuid, ImageModificationUuid;
7433 if (pDiskFrom != pDiskTo)
7434 {
7435 if (pDstUuid)
7436 ImageUuid = *pDstUuid;
7437 else
7438 RTUuidCreate(&ImageUuid);
7439 }
7440 else
7441 {
7442 rc = pImageFrom->Backend->pfnGetUuid(pImageFrom->pBackendData, &ImageUuid);
7443 if (RT_FAILURE(rc))
7444 RTUuidCreate(&ImageUuid);
7445 }
7446 rc = pImageFrom->Backend->pfnGetModificationUuid(pImageFrom->pBackendData, &ImageModificationUuid);
7447 if (RT_FAILURE(rc))
7448 RTUuidClear(&ImageModificationUuid);
7449
7450 char szComment[1024];
7451 rc = pImageFrom->Backend->pfnGetComment(pImageFrom->pBackendData, szComment, sizeof(szComment));
7452 if (RT_FAILURE(rc))
7453 szComment[0] = '\0';
7454 else
7455 szComment[sizeof(szComment) - 1] = '\0';
7456
7457 rc2 = vdThreadFinishRead(pDiskFrom);
7458 AssertRC(rc2);
7459 fLockReadFrom = false;
7460
7461 rc2 = vdThreadStartRead(pDiskTo);
7462 AssertRC(rc2);
7463 unsigned cImagesTo = pDiskTo->cImages;
7464 rc2 = vdThreadFinishRead(pDiskTo);
7465 AssertRC(rc2);
7466
7467 if (pszFilename)
7468 {
7469 if (cbSize == 0)
7470 cbSize = cbSizeFrom;
7471
7472 /* Create destination image with the properties of source image. */
7473 /** @todo replace the VDCreateDiff/VDCreateBase calls by direct
7474 * calls to the backend. Unifies the code and reduces the API
7475 * dependencies. Would also make the synchronization explicit. */
7476 if (cImagesTo > 0)
7477 {
7478 rc = VDCreateDiff(pDiskTo, pszBackend, pszFilename,
7479 uImageFlags, szComment, &ImageUuid,
7480 NULL /* pParentUuid */,
7481 uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
7482 pDstVDIfsImage, NULL);
7483
7484 rc2 = vdThreadStartWrite(pDiskTo);
7485 AssertRC(rc2);
7486 fLockWriteTo = true;
7487 } else {
7488 /** @todo hack to force creation of a fixed image for
7489 * the RAW backend, which can't handle anything else. */
7490 if (!RTStrICmp(pszBackend, "RAW"))
7491 uImageFlags |= VD_IMAGE_FLAGS_FIXED;
7492
7493 vdFixupPCHSGeometry(&PCHSGeometryFrom, cbSize);
7494 vdFixupLCHSGeometry(&LCHSGeometryFrom, cbSize);
7495
7496 rc = VDCreateBase(pDiskTo, pszBackend, pszFilename, cbSize,
7497 uImageFlags, szComment,
7498 &PCHSGeometryFrom, &LCHSGeometryFrom,
7499 NULL, uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
7500 pDstVDIfsImage, NULL);
7501
7502 rc2 = vdThreadStartWrite(pDiskTo);
7503 AssertRC(rc2);
7504 fLockWriteTo = true;
7505
7506 if (RT_SUCCESS(rc) && !RTUuidIsNull(&ImageUuid))
7507 pDiskTo->pLast->Backend->pfnSetUuid(pDiskTo->pLast->pBackendData, &ImageUuid);
7508 }
7509 if (RT_FAILURE(rc))
7510 break;
7511
7512 pImageTo = pDiskTo->pLast;
7513 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
7514
7515 cbSize = RT_MIN(cbSize, cbSizeFrom);
7516 }
7517 else
7518 {
7519 pImageTo = pDiskTo->pLast;
7520 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
7521
7522 uint64_t cbSizeTo;
7523 cbSizeTo = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
7524 if (cbSizeTo == 0)
7525 {
7526 rc = VERR_VD_VALUE_NOT_FOUND;
7527 break;
7528 }
7529
7530 if (cbSize == 0)
7531 cbSize = RT_MIN(cbSizeFrom, cbSizeTo);
7532
7533 vdFixupPCHSGeometry(&PCHSGeometryFrom, cbSize);
7534 vdFixupLCHSGeometry(&LCHSGeometryFrom, cbSize);
7535
7536 /* Update the geometry in the destination image. */
7537 pImageTo->Backend->pfnSetPCHSGeometry(pImageTo->pBackendData, &PCHSGeometryFrom);
7538 pImageTo->Backend->pfnSetLCHSGeometry(pImageTo->pBackendData, &LCHSGeometryFrom);
7539 }
7540
7541 rc2 = vdThreadFinishWrite(pDiskTo);
7542 AssertRC(rc2);
7543 fLockWriteTo = false;
7544
7545 /* Whether we can take the optimized copy path (false) or not.
7546 * Don't optimize if the image existed or if it is a child image. */
7547 bool fSuppressRedundantIo = ( !(pszFilename == NULL || cImagesTo > 0)
7548 || (nImageToSame != VD_IMAGE_CONTENT_UNKNOWN));
7549 unsigned cImagesFromReadBack, cImagesToReadBack;
7550
7551 if (nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN)
7552 cImagesFromReadBack = 0;
7553 else
7554 {
7555 if (nImage == VD_LAST_IMAGE)
7556 cImagesFromReadBack = pDiskFrom->cImages - nImageFromSame - 1;
7557 else
7558 cImagesFromReadBack = nImage - nImageFromSame;
7559 }
7560
7561 if (nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
7562 cImagesToReadBack = 0;
7563 else
7564 cImagesToReadBack = pDiskTo->cImages - nImageToSame - 1;
7565
7566 /* Copy the data. */
7567 rc = vdCopyHelper(pDiskFrom, pImageFrom, pDiskTo, cbSize,
7568 cImagesFromReadBack, cImagesToReadBack,
7569 fSuppressRedundantIo, pIfProgress, pDstIfProgress);
7570
7571 if (RT_SUCCESS(rc))
7572 {
7573 rc2 = vdThreadStartWrite(pDiskTo);
7574 AssertRC(rc2);
7575 fLockWriteTo = true;
7576
7577 /* Only set modification UUID if it is non-null, since the source
7578 * backend might not provide a valid modification UUID. */
7579 if (!RTUuidIsNull(&ImageModificationUuid))
7580 pImageTo->Backend->pfnSetModificationUuid(pImageTo->pBackendData, &ImageModificationUuid);
7581
7582 /* Set the requested open flags if they differ from the value
7583 * required for creating the image and copying the contents. */
7584 if ( pImageTo && pszFilename
7585 && uOpenFlags != (uOpenFlags & ~VD_OPEN_FLAGS_READONLY))
7586 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
7587 uOpenFlags);
7588 }
7589 } while (0);
7590
7591 if (RT_FAILURE(rc) && pImageTo && pszFilename)
7592 {
7593 /* Take the write lock only if it is not taken. Not worth making the
7594 * above code even more complicated. */
7595 if (RT_UNLIKELY(!fLockWriteTo))
7596 {
7597 rc2 = vdThreadStartWrite(pDiskTo);
7598 AssertRC(rc2);
7599 fLockWriteTo = true;
7600 }
7601 /* Error detected, but new image created. Remove image from list. */
7602 vdRemoveImageFromList(pDiskTo, pImageTo);
7603
7604 /* Close and delete image. */
7605 rc2 = pImageTo->Backend->pfnClose(pImageTo->pBackendData, true);
7606 AssertRC(rc2);
7607 pImageTo->pBackendData = NULL;
7608
7609 /* Free remaining resources. */
7610 if (pImageTo->pszFilename)
7611 RTStrFree(pImageTo->pszFilename);
7612
7613 RTMemFree(pImageTo);
7614 }
7615
7616 if (RT_UNLIKELY(fLockWriteTo))
7617 {
7618 rc2 = vdThreadFinishWrite(pDiskTo);
7619 AssertRC(rc2);
7620 }
7621 if (RT_UNLIKELY(fLockWriteFrom))
7622 {
7623 rc2 = vdThreadFinishWrite(pDiskFrom);
7624 AssertRC(rc2);
7625 }
7626 else if (RT_UNLIKELY(fLockReadFrom))
7627 {
7628 rc2 = vdThreadFinishRead(pDiskFrom);
7629 AssertRC(rc2);
7630 }
7631
7632 if (RT_SUCCESS(rc))
7633 {
7634 if (pIfProgress && pIfProgress->pfnProgress)
7635 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
7636 if (pDstIfProgress && pDstIfProgress->pfnProgress)
7637 pDstIfProgress->pfnProgress(pDstIfProgress->Core.pvUser, 100);
7638 }
7639
7640 LogFlowFunc(("returns %Rrc\n", rc));
7641 return rc;
7642}
7643
7644/**
7645 * Copies an image from one HDD container to another.
7646 * The copy is opened in the target HDD container.
7647 * It is possible to convert between different image formats, because the
7648 * backend for the destination may be different from the source.
7649 * If both the source and destination reference the same HDD container,
7650 * then the image is moved (by copying/deleting or renaming) to the new location.
7651 * The source container is unchanged if the move operation fails, otherwise
7652 * the image at the new location is opened in the same way as the old one was.
7653 *
7654 * @returns VBox status code.
7655 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7656 * @param pDiskFrom Pointer to source HDD container.
7657 * @param nImage Image number, counts from 0. 0 is always base image of container.
7658 * @param pDiskTo Pointer to destination HDD container.
7659 * @param pszBackend Name of the image file backend to use.
7660 * @param pszFilename New name of the image (may be NULL if pDiskFrom == pDiskTo).
7661 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
7662 * @param cbSize New image size (0 means leave unchanged).
7663 * @param uImageFlags Flags specifying special destination image features.
7664 * @param pDstUuid New UUID of the destination image. If NULL, a new UUID is created.
7665 * This parameter is used if and only if a true copy is created.
7666 * In all rename/move cases the UUIDs are copied over.
7667 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
7668 * Only used if the destination image is created.
7669 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
7670 * @param pDstVDIfsImage Pointer to the per-image VD interface list, for the
7671 * destination image.
7672 * @param pDstVDIfsOperation Pointer to the per-image VD interface list,
7673 * for the destination image.
7674 */
7675VBOXDDU_DECL(int) VDCopy(PVDISK pDiskFrom, unsigned nImage, PVDISK pDiskTo,
7676 const char *pszBackend, const char *pszFilename,
7677 bool fMoveByRename, uint64_t cbSize,
7678 unsigned uImageFlags, PCRTUUID pDstUuid,
7679 unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
7680 PVDINTERFACE pDstVDIfsImage,
7681 PVDINTERFACE pDstVDIfsOperation)
7682{
7683 return VDCopyEx(pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename,
7684 cbSize, VD_IMAGE_CONTENT_UNKNOWN, VD_IMAGE_CONTENT_UNKNOWN,
7685 uImageFlags, pDstUuid, uOpenFlags, pVDIfsOperation,
7686 pDstVDIfsImage, pDstVDIfsOperation);
7687}
7688
7689/**
7690 * Optimizes the storage consumption of an image. Typically the unused blocks
7691 * have to be wiped with zeroes to achieve a substantial reduced storage use.
7692 * Another optimization done is reordering the image blocks, which can provide
7693 * a significant performance boost, as reads and writes tend to use less random
7694 * file offsets.
7695 *
7696 * @return VBox status code.
7697 * @return VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7698 * @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
7699 * @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
7700 * the code for this isn't implemented yet.
7701 * @param pDisk Pointer to HDD container.
7702 * @param nImage Image number, counts from 0. 0 is always base image of container.
7703 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
7704 */
7705VBOXDDU_DECL(int) VDCompact(PVDISK pDisk, unsigned nImage,
7706 PVDINTERFACE pVDIfsOperation)
7707{
7708 int rc = VINF_SUCCESS;
7709 int rc2;
7710 bool fLockRead = false, fLockWrite = false;
7711 void *pvBuf = NULL;
7712 void *pvTmp = NULL;
7713
7714 LogFlowFunc(("pDisk=%#p nImage=%u pVDIfsOperation=%#p\n",
7715 pDisk, nImage, pVDIfsOperation));
7716
7717 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
7718
7719 do {
7720 /* Check arguments. */
7721 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
7722 rc = VERR_INVALID_PARAMETER);
7723 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE,
7724 ("u32Signature=%08x\n", pDisk->u32Signature));
7725
7726 rc2 = vdThreadStartRead(pDisk);
7727 AssertRC(rc2);
7728 fLockRead = true;
7729
7730 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7731 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7732
7733 /* If there is no compact callback for not file based backends then
7734 * the backend doesn't need compaction. No need to make much fuss about
7735 * this. For file based ones signal this as not yet supported. */
7736 if (!pImage->Backend->pfnCompact)
7737 {
7738 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
7739 rc = VERR_NOT_SUPPORTED;
7740 else
7741 rc = VINF_SUCCESS;
7742 break;
7743 }
7744
7745 /* Insert interface for reading parent state into per-operation list,
7746 * if there is a parent image. */
7747 VDINTERFACEPARENTSTATE VDIfParent;
7748 VDPARENTSTATEDESC ParentUser;
7749 if (pImage->pPrev)
7750 {
7751 VDIfParent.pfnParentRead = vdParentRead;
7752 ParentUser.pDisk = pDisk;
7753 ParentUser.pImage = pImage->pPrev;
7754 rc = VDInterfaceAdd(&VDIfParent.Core, "VDCompact_ParentState", VDINTERFACETYPE_PARENTSTATE,
7755 &ParentUser, sizeof(VDINTERFACEPARENTSTATE), &pVDIfsOperation);
7756 AssertRC(rc);
7757 }
7758
7759 rc2 = vdThreadFinishRead(pDisk);
7760 AssertRC(rc2);
7761 fLockRead = false;
7762
7763 rc2 = vdThreadStartWrite(pDisk);
7764 AssertRC(rc2);
7765 fLockWrite = true;
7766
7767 rc = pImage->Backend->pfnCompact(pImage->pBackendData,
7768 0, 99,
7769 pDisk->pVDIfsDisk,
7770 pImage->pVDIfsImage,
7771 pVDIfsOperation);
7772 } while (0);
7773
7774 if (RT_UNLIKELY(fLockWrite))
7775 {
7776 rc2 = vdThreadFinishWrite(pDisk);
7777 AssertRC(rc2);
7778 }
7779 else if (RT_UNLIKELY(fLockRead))
7780 {
7781 rc2 = vdThreadFinishRead(pDisk);
7782 AssertRC(rc2);
7783 }
7784
7785 if (pvBuf)
7786 RTMemTmpFree(pvBuf);
7787 if (pvTmp)
7788 RTMemTmpFree(pvTmp);
7789
7790 if (RT_SUCCESS(rc))
7791 {
7792 if (pIfProgress && pIfProgress->pfnProgress)
7793 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
7794 }
7795
7796 LogFlowFunc(("returns %Rrc\n", rc));
7797 return rc;
7798}
7799
7800/**
7801 * Resizes the given disk image to the given size.
7802 *
7803 * @return VBox status
7804 * @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
7805 * @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
7806 *
7807 * @param pDisk Pointer to the HDD container.
7808 * @param cbSize New size of the image.
7809 * @param pPCHSGeometry Pointer to the new physical disk geometry <= (16383,16,63). Not NULL.
7810 * @param pLCHSGeometry Pointer to the new logical disk geometry <= (x,255,63). Not NULL.
7811 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
7812 */
7813VBOXDDU_DECL(int) VDResize(PVDISK pDisk, uint64_t cbSize,
7814 PCVDGEOMETRY pPCHSGeometry,
7815 PCVDGEOMETRY pLCHSGeometry,
7816 PVDINTERFACE pVDIfsOperation)
7817{
7818 /** @todo r=klaus resizing was designed to be part of VDCopy, so having a separate function is not desirable. */
7819 int rc = VINF_SUCCESS;
7820 int rc2;
7821 bool fLockRead = false, fLockWrite = false;
7822
7823 LogFlowFunc(("pDisk=%#p cbSize=%llu pVDIfsOperation=%#p\n",
7824 pDisk, cbSize, pVDIfsOperation));
7825
7826 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
7827
7828 do {
7829 /* Check arguments. */
7830 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
7831 rc = VERR_INVALID_PARAMETER);
7832 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE,
7833 ("u32Signature=%08x\n", pDisk->u32Signature));
7834
7835 rc2 = vdThreadStartRead(pDisk);
7836 AssertRC(rc2);
7837 fLockRead = true;
7838
7839 /* Must have at least one image in the chain, will resize last. */
7840 AssertMsgBreakStmt(pDisk->cImages >= 1, ("cImages=%u\n", pDisk->cImages),
7841 rc = VERR_NOT_SUPPORTED);
7842
7843 PVDIMAGE pImage = pDisk->pLast;
7844
7845 /* If there is no compact callback for not file based backends then
7846 * the backend doesn't need compaction. No need to make much fuss about
7847 * this. For file based ones signal this as not yet supported. */
7848 if (!pImage->Backend->pfnResize)
7849 {
7850 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
7851 rc = VERR_NOT_SUPPORTED;
7852 else
7853 rc = VINF_SUCCESS;
7854 break;
7855 }
7856
7857 rc2 = vdThreadFinishRead(pDisk);
7858 AssertRC(rc2);
7859 fLockRead = false;
7860
7861 rc2 = vdThreadStartWrite(pDisk);
7862 AssertRC(rc2);
7863 fLockWrite = true;
7864
7865 VDGEOMETRY PCHSGeometryOld;
7866 VDGEOMETRY LCHSGeometryOld;
7867 PCVDGEOMETRY pPCHSGeometryNew;
7868 PCVDGEOMETRY pLCHSGeometryNew;
7869
7870 if (pPCHSGeometry->cCylinders == 0)
7871 {
7872 /* Auto-detect marker, calculate new value ourself. */
7873 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData, &PCHSGeometryOld);
7874 if (RT_SUCCESS(rc) && (PCHSGeometryOld.cCylinders != 0))
7875 PCHSGeometryOld.cCylinders = RT_MIN(cbSize / 512 / PCHSGeometryOld.cHeads / PCHSGeometryOld.cSectors, 16383);
7876 else if (rc == VERR_VD_GEOMETRY_NOT_SET)
7877 rc = VINF_SUCCESS;
7878
7879 pPCHSGeometryNew = &PCHSGeometryOld;
7880 }
7881 else
7882 pPCHSGeometryNew = pPCHSGeometry;
7883
7884 if (pLCHSGeometry->cCylinders == 0)
7885 {
7886 /* Auto-detect marker, calculate new value ourself. */
7887 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData, &LCHSGeometryOld);
7888 if (RT_SUCCESS(rc) && (LCHSGeometryOld.cCylinders != 0))
7889 LCHSGeometryOld.cCylinders = cbSize / 512 / LCHSGeometryOld.cHeads / LCHSGeometryOld.cSectors;
7890 else if (rc == VERR_VD_GEOMETRY_NOT_SET)
7891 rc = VINF_SUCCESS;
7892
7893 pLCHSGeometryNew = &LCHSGeometryOld;
7894 }
7895 else
7896 pLCHSGeometryNew = pLCHSGeometry;
7897
7898 if (RT_SUCCESS(rc))
7899 rc = pImage->Backend->pfnResize(pImage->pBackendData,
7900 cbSize,
7901 pPCHSGeometryNew,
7902 pLCHSGeometryNew,
7903 0, 99,
7904 pDisk->pVDIfsDisk,
7905 pImage->pVDIfsImage,
7906 pVDIfsOperation);
7907 } while (0);
7908
7909 if (RT_UNLIKELY(fLockWrite))
7910 {
7911 rc2 = vdThreadFinishWrite(pDisk);
7912 AssertRC(rc2);
7913 }
7914 else if (RT_UNLIKELY(fLockRead))
7915 {
7916 rc2 = vdThreadFinishRead(pDisk);
7917 AssertRC(rc2);
7918 }
7919
7920 if (RT_SUCCESS(rc))
7921 {
7922 if (pIfProgress && pIfProgress->pfnProgress)
7923 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
7924
7925 pDisk->cbSize = cbSize;
7926 }
7927
7928 LogFlowFunc(("returns %Rrc\n", rc));
7929 return rc;
7930}
7931
7932VBOXDDU_DECL(int) VDPrepareWithFilters(PVDISK pDisk, PVDINTERFACE pVDIfsOperation)
7933{
7934 int rc = VINF_SUCCESS;
7935 int rc2;
7936 bool fLockRead = false, fLockWrite = false;
7937
7938 LogFlowFunc(("pDisk=%#p pVDIfsOperation=%#p\n", pDisk, pVDIfsOperation));
7939
7940 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
7941
7942 do {
7943 /* Check arguments. */
7944 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
7945 rc = VERR_INVALID_PARAMETER);
7946 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE,
7947 ("u32Signature=%08x\n", pDisk->u32Signature));
7948
7949 rc2 = vdThreadStartRead(pDisk);
7950 AssertRC(rc2);
7951 fLockRead = true;
7952
7953 /* Must have at least one image in the chain. */
7954 AssertMsgBreakStmt(pDisk->cImages >= 1, ("cImages=%u\n", pDisk->cImages),
7955 rc = VERR_VD_NOT_OPENED);
7956
7957 unsigned uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
7958 AssertMsgBreakStmt(!(uOpenFlags & VD_OPEN_FLAGS_READONLY),
7959 ("Last image should be read write"),
7960 rc = VERR_VD_IMAGE_READ_ONLY);
7961
7962 rc2 = vdThreadFinishRead(pDisk);
7963 AssertRC(rc2);
7964 fLockRead = false;
7965
7966 rc2 = vdThreadStartWrite(pDisk);
7967 AssertRC(rc2);
7968 fLockWrite = true;
7969
7970 /*
7971 * Open all images in the chain in read write mode first to avoid running
7972 * into an error in the middle of the process.
7973 */
7974 PVDIMAGE pImage = pDisk->pBase;
7975
7976 while (pImage)
7977 {
7978 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
7979 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
7980 {
7981 /*
7982 * Clear skip consistency checks because the image is made writable now and
7983 * skipping consistency checks is only possible for readonly images.
7984 */
7985 uOpenFlags &= ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS);
7986 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData, uOpenFlags);
7987 if (RT_FAILURE(rc))
7988 break;
7989 }
7990 pImage = pImage->pNext;
7991 }
7992
7993 if (RT_SUCCESS(rc))
7994 {
7995 unsigned cImgCur = 0;
7996 unsigned uPercentStart = 0;
7997 unsigned uPercentSpan = 100 / pDisk->cImages - 1;
7998
7999 /* Allocate tmp buffer. */
8000 void *pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
8001 if (!pvBuf)
8002 {
8003 rc = VERR_NO_MEMORY;
8004 break;
8005 }
8006
8007 pImage = pDisk->pBase;
8008 pDisk->fLocked = true;
8009
8010 while ( pImage
8011 && RT_SUCCESS(rc))
8012 {
8013 /* Get size of image. */
8014 uint64_t cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
8015 uint64_t cbSizeFile = pImage->Backend->pfnGetFileSize(pImage->pBackendData);
8016 uint64_t cbFileWritten = 0;
8017 uint64_t uOffset = 0;
8018 uint64_t cbRemaining = cbSize;
8019
8020 do
8021 {
8022 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
8023 RTSGSEG SegmentBuf;
8024 RTSGBUF SgBuf;
8025 VDIOCTX IoCtx;
8026
8027 SegmentBuf.pvSeg = pvBuf;
8028 SegmentBuf.cbSeg = VD_MERGE_BUFFER_SIZE;
8029 RTSgBufInit(&SgBuf, &SegmentBuf, 1);
8030 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_READ, 0, 0, NULL,
8031 &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
8032
8033 rc = pImage->Backend->pfnRead(pImage->pBackendData, uOffset,
8034 cbThisRead, &IoCtx, &cbThisRead);
8035 if (rc != VERR_VD_BLOCK_FREE)
8036 {
8037 if (RT_FAILURE(rc))
8038 break;
8039
8040 /* Apply filter chains. */
8041 rc = vdFilterChainApplyRead(pDisk, uOffset, cbThisRead, &IoCtx);
8042 if (RT_FAILURE(rc))
8043 break;
8044
8045 rc = vdFilterChainApplyWrite(pDisk, uOffset, cbThisRead, &IoCtx);
8046 if (RT_FAILURE(rc))
8047 break;
8048
8049 RTSgBufReset(&SgBuf);
8050 size_t cbThisWrite = 0;
8051 size_t cbPreRead = 0;
8052 size_t cbPostRead = 0;
8053 rc = pImage->Backend->pfnWrite(pImage->pBackendData, uOffset,
8054 cbThisRead, &IoCtx, &cbThisWrite,
8055 &cbPreRead, &cbPostRead, 0);
8056 if (RT_FAILURE(rc))
8057 break;
8058 Assert(cbThisWrite == cbThisRead);
8059 cbFileWritten += cbThisWrite;
8060 }
8061 else
8062 rc = VINF_SUCCESS;
8063
8064 uOffset += cbThisRead;
8065 cbRemaining -= cbThisRead;
8066
8067 if (pIfProgress && pIfProgress->pfnProgress)
8068 {
8069 rc2 = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
8070 uPercentStart + cbFileWritten * uPercentSpan / cbSizeFile);
8071 AssertRC(rc2); /* Cancelling this operation without leaving an inconsistent state is not possible. */
8072 }
8073 } while (uOffset < cbSize);
8074
8075 pImage = pImage->pNext;
8076 cImgCur++;
8077 uPercentStart += uPercentSpan;
8078 }
8079
8080 pDisk->fLocked = false;
8081 if (pvBuf)
8082 RTMemTmpFree(pvBuf);
8083 }
8084
8085 /* Change images except last one back to readonly. */
8086 pImage = pDisk->pBase;
8087 while ( pImage != pDisk->pLast
8088 && pImage)
8089 {
8090 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
8091 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
8092 rc2 = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData, uOpenFlags);
8093 if (RT_FAILURE(rc2))
8094 {
8095 if (RT_SUCCESS(rc))
8096 rc = rc2;
8097 break;
8098 }
8099 pImage = pImage->pNext;
8100 }
8101 } while (0);
8102
8103 if (RT_UNLIKELY(fLockWrite))
8104 {
8105 rc2 = vdThreadFinishWrite(pDisk);
8106 AssertRC(rc2);
8107 }
8108 else if (RT_UNLIKELY(fLockRead))
8109 {
8110 rc2 = vdThreadFinishRead(pDisk);
8111 AssertRC(rc2);
8112 }
8113
8114 if ( RT_SUCCESS(rc)
8115 && pIfProgress
8116 && pIfProgress->pfnProgress)
8117 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
8118
8119 LogFlowFunc(("returns %Rrc\n", rc));
8120 return rc;
8121}
8122
8123/**
8124 * Closes the last opened image file in HDD container.
8125 * If previous image file was opened in read-only mode (the normal case) and
8126 * the last opened image is in read-write mode then the previous image will be
8127 * reopened in read/write mode.
8128 *
8129 * @returns VBox status code.
8130 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
8131 * @param pDisk Pointer to HDD container.
8132 * @param fDelete If true, delete the image from the host disk.
8133 */
8134VBOXDDU_DECL(int) VDClose(PVDISK pDisk, bool fDelete)
8135{
8136 int rc = VINF_SUCCESS;
8137 int rc2;
8138 bool fLockWrite = false;
8139
8140 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
8141 do
8142 {
8143 /* sanity check */
8144 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8145 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8146
8147 /* Not worth splitting this up into a read lock phase and write
8148 * lock phase, as closing an image is a relatively fast operation
8149 * dominated by the part which needs the write lock. */
8150 rc2 = vdThreadStartWrite(pDisk);
8151 AssertRC(rc2);
8152 fLockWrite = true;
8153
8154 PVDIMAGE pImage = pDisk->pLast;
8155 if (!pImage)
8156 {
8157 rc = VERR_VD_NOT_OPENED;
8158 break;
8159 }
8160
8161 /* Destroy the current discard state first which might still have pending blocks. */
8162 rc = vdDiscardStateDestroy(pDisk);
8163 if (RT_FAILURE(rc))
8164 break;
8165
8166 unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
8167 /* Remove image from list of opened images. */
8168 vdRemoveImageFromList(pDisk, pImage);
8169 /* Close (and optionally delete) image. */
8170 rc = pImage->Backend->pfnClose(pImage->pBackendData, fDelete);
8171 /* Free remaining resources related to the image. */
8172 RTStrFree(pImage->pszFilename);
8173 RTMemFree(pImage);
8174
8175 pImage = pDisk->pLast;
8176 if (!pImage)
8177 break;
8178
8179 /* If disk was previously in read/write mode, make sure it will stay
8180 * like this (if possible) after closing this image. Set the open flags
8181 * accordingly. */
8182 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
8183 {
8184 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
8185 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
8186 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData, uOpenFlags);
8187 }
8188
8189 /* Cache disk information. */
8190 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
8191
8192 /* Cache PCHS geometry. */
8193 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
8194 &pDisk->PCHSGeometry);
8195 if (RT_FAILURE(rc2))
8196 {
8197 pDisk->PCHSGeometry.cCylinders = 0;
8198 pDisk->PCHSGeometry.cHeads = 0;
8199 pDisk->PCHSGeometry.cSectors = 0;
8200 }
8201 else
8202 {
8203 /* Make sure the PCHS geometry is properly clipped. */
8204 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
8205 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
8206 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
8207 }
8208
8209 /* Cache LCHS geometry. */
8210 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
8211 &pDisk->LCHSGeometry);
8212 if (RT_FAILURE(rc2))
8213 {
8214 pDisk->LCHSGeometry.cCylinders = 0;
8215 pDisk->LCHSGeometry.cHeads = 0;
8216 pDisk->LCHSGeometry.cSectors = 0;
8217 }
8218 else
8219 {
8220 /* Make sure the LCHS geometry is properly clipped. */
8221 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
8222 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
8223 }
8224 } while (0);
8225
8226 if (RT_UNLIKELY(fLockWrite))
8227 {
8228 rc2 = vdThreadFinishWrite(pDisk);
8229 AssertRC(rc2);
8230 }
8231
8232 LogFlowFunc(("returns %Rrc\n", rc));
8233 return rc;
8234}
8235
8236/**
8237 * Closes the currently opened cache image file in HDD container.
8238 *
8239 * @return VBox status code.
8240 * @return VERR_VD_NOT_OPENED if no cache is opened in HDD container.
8241 * @param pDisk Pointer to HDD container.
8242 * @param fDelete If true, delete the image from the host disk.
8243 */
8244VBOXDDU_DECL(int) VDCacheClose(PVDISK pDisk, bool fDelete)
8245{
8246 int rc = VINF_SUCCESS;
8247 int rc2;
8248 bool fLockWrite = false;
8249 PVDCACHE pCache = NULL;
8250
8251 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
8252
8253 do
8254 {
8255 /* sanity check */
8256 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8257 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8258
8259 rc2 = vdThreadStartWrite(pDisk);
8260 AssertRC(rc2);
8261 fLockWrite = true;
8262
8263 AssertPtrBreakStmt(pDisk->pCache, rc = VERR_VD_CACHE_NOT_FOUND);
8264
8265 pCache = pDisk->pCache;
8266 pDisk->pCache = NULL;
8267
8268 pCache->Backend->pfnClose(pCache->pBackendData, fDelete);
8269 if (pCache->pszFilename)
8270 RTStrFree(pCache->pszFilename);
8271 RTMemFree(pCache);
8272 } while (0);
8273
8274 if (RT_LIKELY(fLockWrite))
8275 {
8276 rc2 = vdThreadFinishWrite(pDisk);
8277 AssertRC(rc2);
8278 }
8279
8280 LogFlowFunc(("returns %Rrc\n", rc));
8281 return rc;
8282}
8283
8284VBOXDDU_DECL(int) VDFilterRemove(PVDISK pDisk, uint32_t fFlags)
8285{
8286 int rc = VINF_SUCCESS;
8287 int rc2;
8288 bool fLockWrite = false;
8289 PVDFILTER pFilter = NULL;
8290
8291 LogFlowFunc(("pDisk=%#p\n", pDisk));
8292
8293 do
8294 {
8295 /* sanity check */
8296 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8297 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8298
8299 AssertMsgBreakStmt(!(fFlags & ~VD_FILTER_FLAGS_MASK),
8300 ("Invalid flags set (fFlags=%#x)\n", fFlags),
8301 rc = VERR_INVALID_PARAMETER);
8302
8303 rc2 = vdThreadStartWrite(pDisk);
8304 AssertRC(rc2);
8305 fLockWrite = true;
8306
8307 if (fFlags & VD_FILTER_FLAGS_WRITE)
8308 {
8309 AssertBreakStmt(!RTListIsEmpty(&pDisk->ListFilterChainWrite), rc = VERR_VD_NOT_OPENED);
8310 pFilter = RTListGetLast(&pDisk->ListFilterChainWrite, VDFILTER, ListNodeChainWrite);
8311 AssertPtr(pFilter);
8312 RTListNodeRemove(&pFilter->ListNodeChainWrite);
8313 vdFilterRelease(pFilter);
8314 }
8315
8316 if (fFlags & VD_FILTER_FLAGS_READ)
8317 {
8318 AssertBreakStmt(!RTListIsEmpty(&pDisk->ListFilterChainRead), rc = VERR_VD_NOT_OPENED);
8319 pFilter = RTListGetLast(&pDisk->ListFilterChainRead, VDFILTER, ListNodeChainRead);
8320 AssertPtr(pFilter);
8321 RTListNodeRemove(&pFilter->ListNodeChainRead);
8322 vdFilterRelease(pFilter);
8323 }
8324 } while (0);
8325
8326 if (RT_LIKELY(fLockWrite))
8327 {
8328 rc2 = vdThreadFinishWrite(pDisk);
8329 AssertRC(rc2);
8330 }
8331
8332 LogFlowFunc(("returns %Rrc\n", rc));
8333 return rc;
8334}
8335
8336/**
8337 * Closes all opened image files in HDD container.
8338 *
8339 * @returns VBox status code.
8340 * @param pDisk Pointer to HDD container.
8341 */
8342VBOXDDU_DECL(int) VDCloseAll(PVDISK pDisk)
8343{
8344 int rc = VINF_SUCCESS;
8345 int rc2;
8346 bool fLockWrite = false;
8347
8348 LogFlowFunc(("pDisk=%#p\n", pDisk));
8349 do
8350 {
8351 /* sanity check */
8352 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8353 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8354
8355 /* Lock the entire operation. */
8356 rc2 = vdThreadStartWrite(pDisk);
8357 AssertRC(rc2);
8358 fLockWrite = true;
8359
8360 PVDCACHE pCache = pDisk->pCache;
8361 if (pCache)
8362 {
8363 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
8364 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
8365 rc = rc2;
8366
8367 if (pCache->pszFilename)
8368 RTStrFree(pCache->pszFilename);
8369 RTMemFree(pCache);
8370 }
8371
8372 PVDIMAGE pImage = pDisk->pLast;
8373 while (VALID_PTR(pImage))
8374 {
8375 PVDIMAGE pPrev = pImage->pPrev;
8376 /* Remove image from list of opened images. */
8377 vdRemoveImageFromList(pDisk, pImage);
8378 /* Close image. */
8379 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
8380 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
8381 rc = rc2;
8382 /* Free remaining resources related to the image. */
8383 RTStrFree(pImage->pszFilename);
8384 RTMemFree(pImage);
8385 pImage = pPrev;
8386 }
8387 Assert(!VALID_PTR(pDisk->pLast));
8388 } while (0);
8389
8390 if (RT_UNLIKELY(fLockWrite))
8391 {
8392 rc2 = vdThreadFinishWrite(pDisk);
8393 AssertRC(rc2);
8394 }
8395
8396 LogFlowFunc(("returns %Rrc\n", rc));
8397 return rc;
8398}
8399
8400/**
8401 * Removes all filters of the given HDD container.
8402 *
8403 * @return VBox status code.
8404 * @param pDisk Pointer to HDD container.
8405 */
8406VBOXDDU_DECL(int) VDFilterRemoveAll(PVDISK pDisk)
8407{
8408 int rc = VINF_SUCCESS;
8409 int rc2;
8410 bool fLockWrite = false;
8411
8412 LogFlowFunc(("pDisk=%#p\n", pDisk));
8413 do
8414 {
8415 /* sanity check */
8416 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8417 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8418
8419 /* Lock the entire operation. */
8420 rc2 = vdThreadStartWrite(pDisk);
8421 AssertRC(rc2);
8422 fLockWrite = true;
8423
8424 PVDFILTER pFilter, pFilterNext;
8425 RTListForEachSafe(&pDisk->ListFilterChainWrite, pFilter, pFilterNext, VDFILTER, ListNodeChainWrite)
8426 {
8427 RTListNodeRemove(&pFilter->ListNodeChainWrite);
8428 vdFilterRelease(pFilter);
8429 }
8430
8431 RTListForEachSafe(&pDisk->ListFilterChainRead, pFilter, pFilterNext, VDFILTER, ListNodeChainRead)
8432 {
8433 RTListNodeRemove(&pFilter->ListNodeChainRead);
8434 vdFilterRelease(pFilter);
8435 }
8436 Assert(RTListIsEmpty(&pDisk->ListFilterChainRead));
8437 Assert(RTListIsEmpty(&pDisk->ListFilterChainWrite));
8438 } while (0);
8439
8440 if (RT_UNLIKELY(fLockWrite))
8441 {
8442 rc2 = vdThreadFinishWrite(pDisk);
8443 AssertRC(rc2);
8444 }
8445
8446 LogFlowFunc(("returns %Rrc\n", rc));
8447 return rc;
8448}
8449
8450/**
8451 * Read data from virtual HDD.
8452 *
8453 * @returns VBox status code.
8454 * @retval VERR_VD_NOT_OPENED if no image is opened in HDD container.
8455 * @param pDisk Pointer to HDD container.
8456 * @param uOffset Offset of first reading byte from start of disk.
8457 * @param pvBuf Pointer to buffer for reading data.
8458 * @param cbRead Number of bytes to read.
8459 */
8460VBOXDDU_DECL(int) VDRead(PVDISK pDisk, uint64_t uOffset, void *pvBuf,
8461 size_t cbRead)
8462{
8463 int rc = VINF_SUCCESS;
8464 int rc2;
8465 bool fLockRead = false;
8466
8467 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
8468 pDisk, uOffset, pvBuf, cbRead));
8469 do
8470 {
8471 /* sanity check */
8472 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8473 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8474
8475 /* Check arguments. */
8476 AssertMsgBreakStmt(VALID_PTR(pvBuf),
8477 ("pvBuf=%#p\n", pvBuf),
8478 rc = VERR_INVALID_PARAMETER);
8479 AssertMsgBreakStmt(cbRead,
8480 ("cbRead=%zu\n", cbRead),
8481 rc = VERR_INVALID_PARAMETER);
8482
8483 rc2 = vdThreadStartRead(pDisk);
8484 AssertRC(rc2);
8485 fLockRead = true;
8486
8487 PVDIMAGE pImage = pDisk->pLast;
8488 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
8489
8490 if (uOffset + cbRead > pDisk->cbSize)
8491 {
8492 /* Floppy images might be smaller than the standard expected by
8493 the floppy controller code. So, we won't fail here. */
8494 AssertMsgBreakStmt(pDisk->enmType == VDTYPE_FLOPPY,
8495 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
8496 uOffset, cbRead, pDisk->cbSize),
8497 rc = VERR_EOF);
8498 memset(pvBuf, 0xf6, cbRead); /* f6h = format.com filler byte */
8499 if (uOffset >= pDisk->cbSize)
8500 break;
8501 cbRead = pDisk->cbSize - uOffset;
8502 }
8503
8504 rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead,
8505 true /* fUpdateCache */);
8506 } while (0);
8507
8508 if (RT_UNLIKELY(fLockRead))
8509 {
8510 rc2 = vdThreadFinishRead(pDisk);
8511 AssertRC(rc2);
8512 }
8513
8514 LogFlowFunc(("returns %Rrc\n", rc));
8515 return rc;
8516}
8517
8518/**
8519 * Write data to virtual HDD.
8520 *
8521 * @returns VBox status code.
8522 * @retval VERR_VD_NOT_OPENED if no image is opened in HDD container.
8523 * @param pDisk Pointer to HDD container.
8524 * @param uOffset Offset of the first byte being
8525 * written from start of disk.
8526 * @param pvBuf Pointer to buffer for writing data.
8527 * @param cbWrite Number of bytes to write.
8528 */
8529VBOXDDU_DECL(int) VDWrite(PVDISK pDisk, uint64_t uOffset, const void *pvBuf,
8530 size_t cbWrite)
8531{
8532 int rc = VINF_SUCCESS;
8533 int rc2;
8534 bool fLockWrite = false;
8535
8536 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
8537 pDisk, uOffset, pvBuf, cbWrite));
8538 do
8539 {
8540 /* sanity check */
8541 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8542 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8543
8544 /* Check arguments. */
8545 AssertMsgBreakStmt(VALID_PTR(pvBuf),
8546 ("pvBuf=%#p\n", pvBuf),
8547 rc = VERR_INVALID_PARAMETER);
8548 AssertMsgBreakStmt(cbWrite,
8549 ("cbWrite=%zu\n", cbWrite),
8550 rc = VERR_INVALID_PARAMETER);
8551
8552 rc2 = vdThreadStartWrite(pDisk);
8553 AssertRC(rc2);
8554 fLockWrite = true;
8555
8556 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
8557 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
8558 uOffset, cbWrite, pDisk->cbSize),
8559 rc = VERR_INVALID_PARAMETER);
8560
8561 PVDIMAGE pImage = pDisk->pLast;
8562 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
8563
8564 vdSetModifiedFlag(pDisk);
8565 rc = vdWriteHelper(pDisk, pImage, uOffset, pvBuf, cbWrite,
8566 VDIOCTX_FLAGS_READ_UPDATE_CACHE);
8567 if (RT_FAILURE(rc))
8568 break;
8569
8570 /* If there is a merge (in the direction towards a parent) running
8571 * concurrently then we have to also "relay" the write to this parent,
8572 * as the merge position might be already past the position where
8573 * this write is going. The "context" of the write can come from the
8574 * natural chain, since merging either already did or will take care
8575 * of the "other" content which is might be needed to fill the block
8576 * to a full allocation size. The cache doesn't need to be touched
8577 * as this write is covered by the previous one. */
8578 if (RT_UNLIKELY(pDisk->pImageRelay))
8579 rc = vdWriteHelper(pDisk, pDisk->pImageRelay, uOffset,
8580 pvBuf, cbWrite, VDIOCTX_FLAGS_DEFAULT);
8581 } while (0);
8582
8583 if (RT_UNLIKELY(fLockWrite))
8584 {
8585 rc2 = vdThreadFinishWrite(pDisk);
8586 AssertRC(rc2);
8587 }
8588
8589 LogFlowFunc(("returns %Rrc\n", rc));
8590 return rc;
8591}
8592
8593/**
8594 * Make sure the on disk representation of a virtual HDD is up to date.
8595 *
8596 * @returns VBox status code.
8597 * @retval VERR_VD_NOT_OPENED if no image is opened in HDD container.
8598 * @param pDisk Pointer to HDD container.
8599 */
8600VBOXDDU_DECL(int) VDFlush(PVDISK pDisk)
8601{
8602 int rc = VINF_SUCCESS;
8603 int rc2;
8604 bool fLockWrite = false;
8605
8606 LogFlowFunc(("pDisk=%#p\n", pDisk));
8607 do
8608 {
8609 /* sanity check */
8610 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8611 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8612
8613 rc2 = vdThreadStartWrite(pDisk);
8614 AssertRC(rc2);
8615 fLockWrite = true;
8616
8617 PVDIMAGE pImage = pDisk->pLast;
8618 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
8619
8620 VDIOCTX IoCtx;
8621 RTSEMEVENT hEventComplete = NIL_RTSEMEVENT;
8622
8623 rc = RTSemEventCreate(&hEventComplete);
8624 if (RT_FAILURE(rc))
8625 break;
8626
8627 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_FLUSH, 0, 0, pImage, NULL,
8628 NULL, vdFlushHelperAsync, VDIOCTX_FLAGS_SYNC | VDIOCTX_FLAGS_DONT_FREE);
8629
8630 IoCtx.Type.Root.pfnComplete = vdIoCtxSyncComplete;
8631 IoCtx.Type.Root.pvUser1 = pDisk;
8632 IoCtx.Type.Root.pvUser2 = hEventComplete;
8633 rc = vdIoCtxProcessSync(&IoCtx, hEventComplete);
8634
8635 RTSemEventDestroy(hEventComplete);
8636 } while (0);
8637
8638 if (RT_UNLIKELY(fLockWrite))
8639 {
8640 rc2 = vdThreadFinishWrite(pDisk);
8641 AssertRC(rc2);
8642 }
8643
8644 LogFlowFunc(("returns %Rrc\n", rc));
8645 return rc;
8646}
8647
8648/**
8649 * Get number of opened images in HDD container.
8650 *
8651 * @returns Number of opened images for HDD container. 0 if no images have been opened.
8652 * @param pDisk Pointer to HDD container.
8653 */
8654VBOXDDU_DECL(unsigned) VDGetCount(PVDISK pDisk)
8655{
8656 unsigned cImages;
8657 int rc2;
8658 bool fLockRead = false;
8659
8660 LogFlowFunc(("pDisk=%#p\n", pDisk));
8661 do
8662 {
8663 /* sanity check */
8664 AssertPtrBreakStmt(pDisk, cImages = 0);
8665 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8666
8667 rc2 = vdThreadStartRead(pDisk);
8668 AssertRC(rc2);
8669 fLockRead = true;
8670
8671 cImages = pDisk->cImages;
8672 } while (0);
8673
8674 if (RT_UNLIKELY(fLockRead))
8675 {
8676 rc2 = vdThreadFinishRead(pDisk);
8677 AssertRC(rc2);
8678 }
8679
8680 LogFlowFunc(("returns %u\n", cImages));
8681 return cImages;
8682}
8683
8684/**
8685 * Get read/write mode of HDD container.
8686 *
8687 * @returns Virtual disk ReadOnly status.
8688 * @returns true if no image is opened in HDD container.
8689 * @param pDisk Pointer to HDD container.
8690 */
8691VBOXDDU_DECL(bool) VDIsReadOnly(PVDISK pDisk)
8692{
8693 bool fReadOnly;
8694 int rc2;
8695 bool fLockRead = false;
8696
8697 LogFlowFunc(("pDisk=%#p\n", pDisk));
8698 do
8699 {
8700 /* sanity check */
8701 AssertPtrBreakStmt(pDisk, fReadOnly = false);
8702 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8703
8704 rc2 = vdThreadStartRead(pDisk);
8705 AssertRC(rc2);
8706 fLockRead = true;
8707
8708 PVDIMAGE pImage = pDisk->pLast;
8709 AssertPtrBreakStmt(pImage, fReadOnly = true);
8710
8711 unsigned uOpenFlags;
8712 uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
8713 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
8714 } while (0);
8715
8716 if (RT_UNLIKELY(fLockRead))
8717 {
8718 rc2 = vdThreadFinishRead(pDisk);
8719 AssertRC(rc2);
8720 }
8721
8722 LogFlowFunc(("returns %d\n", fReadOnly));
8723 return fReadOnly;
8724}
8725
8726/**
8727 * Get sector size of an image in HDD container.
8728 *
8729 * @return Virtual disk sector size in bytes.
8730 * @return 0 if image with specified number was not opened.
8731 * @param pDisk Pointer to HDD container.
8732 * @param nImage Image number, counts from 0. 0 is always base image of container.
8733 */
8734VBOXDDU_DECL(uint32_t) VDGetSectorSize(PVDISK pDisk, unsigned nImage)
8735{
8736 uint64_t cbSector;
8737 int rc2;
8738 bool fLockRead = false;
8739
8740 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
8741 do
8742 {
8743 /* sanity check */
8744 AssertPtrBreakStmt(pDisk, cbSector = 0);
8745 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8746
8747 rc2 = vdThreadStartRead(pDisk);
8748 AssertRC(rc2);
8749 fLockRead = true;
8750
8751 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8752 AssertPtrBreakStmt(pImage, cbSector = 0);
8753 cbSector = pImage->Backend->pfnGetSectorSize(pImage->pBackendData);
8754 } while (0);
8755
8756 if (RT_UNLIKELY(fLockRead))
8757 {
8758 rc2 = vdThreadFinishRead(pDisk);
8759 AssertRC(rc2);
8760 }
8761
8762 LogFlowFunc(("returns %u\n", cbSector));
8763 return cbSector;
8764}
8765
8766/**
8767 * Get total capacity of an image in HDD container.
8768 *
8769 * @returns Virtual disk size in bytes.
8770 * @returns 0 if no image with specified number was not opened.
8771 * @param pDisk Pointer to HDD container.
8772 * @param nImage Image number, counts from 0. 0 is always base image of container.
8773 */
8774VBOXDDU_DECL(uint64_t) VDGetSize(PVDISK pDisk, unsigned nImage)
8775{
8776 uint64_t cbSize;
8777 int rc2;
8778 bool fLockRead = false;
8779
8780 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
8781 do
8782 {
8783 /* sanity check */
8784 AssertPtrBreakStmt(pDisk, cbSize = 0);
8785 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8786
8787 rc2 = vdThreadStartRead(pDisk);
8788 AssertRC(rc2);
8789 fLockRead = true;
8790
8791 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8792 AssertPtrBreakStmt(pImage, cbSize = 0);
8793 cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
8794 } while (0);
8795
8796 if (RT_UNLIKELY(fLockRead))
8797 {
8798 rc2 = vdThreadFinishRead(pDisk);
8799 AssertRC(rc2);
8800 }
8801
8802 LogFlowFunc(("returns %llu\n", cbSize));
8803 return cbSize;
8804}
8805
8806/**
8807 * Get total file size of an image in HDD container.
8808 *
8809 * @returns Virtual disk size in bytes.
8810 * @returns 0 if no image is opened in HDD container.
8811 * @param pDisk Pointer to HDD container.
8812 * @param nImage Image number, counts from 0. 0 is always base image of container.
8813 */
8814VBOXDDU_DECL(uint64_t) VDGetFileSize(PVDISK pDisk, unsigned nImage)
8815{
8816 uint64_t cbSize;
8817 int rc2;
8818 bool fLockRead = false;
8819
8820 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
8821 do
8822 {
8823 /* sanity check */
8824 AssertPtrBreakStmt(pDisk, cbSize = 0);
8825 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8826
8827 rc2 = vdThreadStartRead(pDisk);
8828 AssertRC(rc2);
8829 fLockRead = true;
8830
8831 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8832 AssertPtrBreakStmt(pImage, cbSize = 0);
8833 cbSize = pImage->Backend->pfnGetFileSize(pImage->pBackendData);
8834 } while (0);
8835
8836 if (RT_UNLIKELY(fLockRead))
8837 {
8838 rc2 = vdThreadFinishRead(pDisk);
8839 AssertRC(rc2);
8840 }
8841
8842 LogFlowFunc(("returns %llu\n", cbSize));
8843 return cbSize;
8844}
8845
8846/**
8847 * Get virtual disk PCHS geometry stored in HDD container.
8848 *
8849 * @returns VBox status code.
8850 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
8851 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
8852 * @param pDisk Pointer to HDD container.
8853 * @param nImage Image number, counts from 0. 0 is always base image of container.
8854 * @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
8855 */
8856VBOXDDU_DECL(int) VDGetPCHSGeometry(PVDISK pDisk, unsigned nImage,
8857 PVDGEOMETRY pPCHSGeometry)
8858{
8859 int rc = VINF_SUCCESS;
8860 int rc2;
8861 bool fLockRead = false;
8862
8863 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
8864 pDisk, nImage, pPCHSGeometry));
8865 do
8866 {
8867 /* sanity check */
8868 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8869 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8870
8871 /* Check arguments. */
8872 AssertMsgBreakStmt(VALID_PTR(pPCHSGeometry),
8873 ("pPCHSGeometry=%#p\n", pPCHSGeometry),
8874 rc = VERR_INVALID_PARAMETER);
8875
8876 rc2 = vdThreadStartRead(pDisk);
8877 AssertRC(rc2);
8878 fLockRead = true;
8879
8880 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8881 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8882
8883 if (pImage == pDisk->pLast)
8884 {
8885 /* Use cached information if possible. */
8886 if (pDisk->PCHSGeometry.cCylinders != 0)
8887 *pPCHSGeometry = pDisk->PCHSGeometry;
8888 else
8889 rc = VERR_VD_GEOMETRY_NOT_SET;
8890 }
8891 else
8892 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
8893 pPCHSGeometry);
8894 } while (0);
8895
8896 if (RT_UNLIKELY(fLockRead))
8897 {
8898 rc2 = vdThreadFinishRead(pDisk);
8899 AssertRC(rc2);
8900 }
8901
8902 LogFlowFunc(("%Rrc (PCHS=%u/%u/%u)\n", rc,
8903 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
8904 pDisk->PCHSGeometry.cSectors));
8905 return rc;
8906}
8907
8908/**
8909 * Store virtual disk PCHS geometry in HDD container.
8910 *
8911 * Note that in case of unrecoverable error all images in HDD container will be closed.
8912 *
8913 * @returns VBox status code.
8914 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
8915 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
8916 * @param pDisk Pointer to HDD container.
8917 * @param nImage Image number, counts from 0. 0 is always base image of container.
8918 * @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
8919 */
8920VBOXDDU_DECL(int) VDSetPCHSGeometry(PVDISK pDisk, unsigned nImage,
8921 PCVDGEOMETRY pPCHSGeometry)
8922{
8923 int rc = VINF_SUCCESS;
8924 int rc2;
8925 bool fLockWrite = false;
8926
8927 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
8928 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
8929 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
8930 do
8931 {
8932 /* sanity check */
8933 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8934 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8935
8936 /* Check arguments. */
8937 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
8938 && pPCHSGeometry->cHeads <= 16
8939 && pPCHSGeometry->cSectors <= 63,
8940 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
8941 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
8942 pPCHSGeometry->cSectors),
8943 rc = VERR_INVALID_PARAMETER);
8944
8945 rc2 = vdThreadStartWrite(pDisk);
8946 AssertRC(rc2);
8947 fLockWrite = true;
8948
8949 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8950 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8951
8952 if (pImage == pDisk->pLast)
8953 {
8954 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
8955 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
8956 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
8957 {
8958 /* Only update geometry if it is changed. Avoids similar checks
8959 * in every backend. Most of the time the new geometry is set
8960 * to the previous values, so no need to go through the hassle
8961 * of updating an image which could be opened in read-only mode
8962 * right now. */
8963 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
8964 pPCHSGeometry);
8965
8966 /* Cache new geometry values in any case. */
8967 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
8968 &pDisk->PCHSGeometry);
8969 if (RT_FAILURE(rc2))
8970 {
8971 pDisk->PCHSGeometry.cCylinders = 0;
8972 pDisk->PCHSGeometry.cHeads = 0;
8973 pDisk->PCHSGeometry.cSectors = 0;
8974 }
8975 else
8976 {
8977 /* Make sure the CHS geometry is properly clipped. */
8978 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
8979 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
8980 }
8981 }
8982 }
8983 else
8984 {
8985 VDGEOMETRY PCHS;
8986 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
8987 &PCHS);
8988 if ( RT_FAILURE(rc)
8989 || pPCHSGeometry->cCylinders != PCHS.cCylinders
8990 || pPCHSGeometry->cHeads != PCHS.cHeads
8991 || pPCHSGeometry->cSectors != PCHS.cSectors)
8992 {
8993 /* Only update geometry if it is changed. Avoids similar checks
8994 * in every backend. Most of the time the new geometry is set
8995 * to the previous values, so no need to go through the hassle
8996 * of updating an image which could be opened in read-only mode
8997 * right now. */
8998 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
8999 pPCHSGeometry);
9000 }
9001 }
9002 } while (0);
9003
9004 if (RT_UNLIKELY(fLockWrite))
9005 {
9006 rc2 = vdThreadFinishWrite(pDisk);
9007 AssertRC(rc2);
9008 }
9009
9010 LogFlowFunc(("returns %Rrc\n", rc));
9011 return rc;
9012}
9013
9014/**
9015 * Get virtual disk LCHS geometry stored in HDD container.
9016 *
9017 * @returns VBox status code.
9018 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9019 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
9020 * @param pDisk Pointer to HDD container.
9021 * @param nImage Image number, counts from 0. 0 is always base image of container.
9022 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
9023 */
9024VBOXDDU_DECL(int) VDGetLCHSGeometry(PVDISK pDisk, unsigned nImage,
9025 PVDGEOMETRY pLCHSGeometry)
9026{
9027 int rc = VINF_SUCCESS;
9028 int rc2;
9029 bool fLockRead = false;
9030
9031 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
9032 pDisk, nImage, pLCHSGeometry));
9033 do
9034 {
9035 /* sanity check */
9036 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9037 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9038
9039 /* Check arguments. */
9040 AssertMsgBreakStmt(VALID_PTR(pLCHSGeometry),
9041 ("pLCHSGeometry=%#p\n", pLCHSGeometry),
9042 rc = VERR_INVALID_PARAMETER);
9043
9044 rc2 = vdThreadStartRead(pDisk);
9045 AssertRC(rc2);
9046 fLockRead = true;
9047
9048 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9049 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9050
9051 if (pImage == pDisk->pLast)
9052 {
9053 /* Use cached information if possible. */
9054 if (pDisk->LCHSGeometry.cCylinders != 0)
9055 *pLCHSGeometry = pDisk->LCHSGeometry;
9056 else
9057 rc = VERR_VD_GEOMETRY_NOT_SET;
9058 }
9059 else
9060 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
9061 pLCHSGeometry);
9062 } while (0);
9063
9064 if (RT_UNLIKELY(fLockRead))
9065 {
9066 rc2 = vdThreadFinishRead(pDisk);
9067 AssertRC(rc2);
9068 }
9069
9070 LogFlowFunc((": %Rrc (LCHS=%u/%u/%u)\n", rc,
9071 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
9072 pDisk->LCHSGeometry.cSectors));
9073 return rc;
9074}
9075
9076/**
9077 * Store virtual disk LCHS geometry in HDD container.
9078 *
9079 * Note that in case of unrecoverable error all images in HDD container will be closed.
9080 *
9081 * @returns VBox status code.
9082 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9083 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
9084 * @param pDisk Pointer to HDD container.
9085 * @param nImage Image number, counts from 0. 0 is always base image of container.
9086 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
9087 */
9088VBOXDDU_DECL(int) VDSetLCHSGeometry(PVDISK pDisk, unsigned nImage,
9089 PCVDGEOMETRY pLCHSGeometry)
9090{
9091 int rc = VINF_SUCCESS;
9092 int rc2;
9093 bool fLockWrite = false;
9094
9095 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
9096 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
9097 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
9098 do
9099 {
9100 /* sanity check */
9101 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9102 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9103
9104 /* Check arguments. */
9105 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
9106 && pLCHSGeometry->cHeads <= 255
9107 && pLCHSGeometry->cSectors <= 63,
9108 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
9109 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
9110 pLCHSGeometry->cSectors),
9111 rc = VERR_INVALID_PARAMETER);
9112
9113 rc2 = vdThreadStartWrite(pDisk);
9114 AssertRC(rc2);
9115 fLockWrite = true;
9116
9117 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9118 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9119
9120 if (pImage == pDisk->pLast)
9121 {
9122 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
9123 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
9124 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
9125 {
9126 /* Only update geometry if it is changed. Avoids similar checks
9127 * in every backend. Most of the time the new geometry is set
9128 * to the previous values, so no need to go through the hassle
9129 * of updating an image which could be opened in read-only mode
9130 * right now. */
9131 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
9132 pLCHSGeometry);
9133
9134 /* Cache new geometry values in any case. */
9135 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
9136 &pDisk->LCHSGeometry);
9137 if (RT_FAILURE(rc2))
9138 {
9139 pDisk->LCHSGeometry.cCylinders = 0;
9140 pDisk->LCHSGeometry.cHeads = 0;
9141 pDisk->LCHSGeometry.cSectors = 0;
9142 }
9143 else
9144 {
9145 /* Make sure the CHS geometry is properly clipped. */
9146 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
9147 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
9148 }
9149 }
9150 }
9151 else
9152 {
9153 VDGEOMETRY LCHS;
9154 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
9155 &LCHS);
9156 if ( RT_FAILURE(rc)
9157 || pLCHSGeometry->cCylinders != LCHS.cCylinders
9158 || pLCHSGeometry->cHeads != LCHS.cHeads
9159 || pLCHSGeometry->cSectors != LCHS.cSectors)
9160 {
9161 /* Only update geometry if it is changed. Avoids similar checks
9162 * in every backend. Most of the time the new geometry is set
9163 * to the previous values, so no need to go through the hassle
9164 * of updating an image which could be opened in read-only mode
9165 * right now. */
9166 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
9167 pLCHSGeometry);
9168 }
9169 }
9170 } while (0);
9171
9172 if (RT_UNLIKELY(fLockWrite))
9173 {
9174 rc2 = vdThreadFinishWrite(pDisk);
9175 AssertRC(rc2);
9176 }
9177
9178 LogFlowFunc(("returns %Rrc\n", rc));
9179 return rc;
9180}
9181
9182/**
9183 * Queries the available regions of an image in the given VD container.
9184 *
9185 * @return VBox status code.
9186 * @retval VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9187 * @retval VERR_NOT_SUPPORTED if the image backend doesn't support region lists.
9188 * @param pDisk Pointer to HDD container.
9189 * @param nImage Image number, counts from 0. 0 is always base image of container.
9190 * @param fFlags Combination of VD_REGION_LIST_F_* flags.
9191 * @param ppRegionList Where to store the pointer to the region list on success, must be freed
9192 * with VDRegionListFree().
9193 */
9194VBOXDDU_DECL(int) VDQueryRegions(PVDISK pDisk, unsigned nImage, uint32_t fFlags,
9195 PPVDREGIONLIST ppRegionList)
9196{
9197 int rc = VINF_SUCCESS;
9198 int rc2;
9199 bool fLockRead = false;
9200
9201 LogFlowFunc(("pDisk=%#p nImage=%u fFlags=%#x ppRegionList=%#p\n",
9202 pDisk, nImage, fFlags, ppRegionList));
9203 do
9204 {
9205 /* sanity check */
9206 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9207 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9208
9209 /* Check arguments. */
9210 AssertMsgBreakStmt(VALID_PTR(ppRegionList),
9211 ("ppRegionList=%#p\n", ppRegionList),
9212 rc = VERR_INVALID_PARAMETER);
9213
9214 rc2 = vdThreadStartRead(pDisk);
9215 AssertRC(rc2);
9216 fLockRead = true;
9217
9218 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9219 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9220
9221 if (pImage->Backend->pfnQueryRegions)
9222 {
9223 PCVDREGIONLIST pRegionList = NULL;
9224 rc = pImage->Backend->pfnQueryRegions(pImage->pBackendData, &pRegionList);
9225 if (RT_SUCCESS(rc))
9226 {
9227 rc = vdRegionListConv(pRegionList, fFlags, ppRegionList);
9228
9229 AssertPtr(pImage->Backend->pfnRegionListRelease);
9230 pImage->Backend->pfnRegionListRelease(pImage->pBackendData, pRegionList);
9231 }
9232 }
9233 else
9234 rc = VERR_NOT_SUPPORTED;
9235
9236 if (rc == VERR_NOT_SUPPORTED)
9237 {
9238 /*
9239 * Create a list with a single region containing the data gathered from the
9240 * image and sector size.
9241 */
9242 PVDREGIONLIST pRegionList = (PVDREGIONLIST)RTMemAllocZ(RT_UOFFSETOF(VDREGIONLIST, aRegions[1]));
9243 if (RT_LIKELY(pRegionList))
9244 {
9245 uint32_t cbSector = pImage->Backend->pfnGetSectorSize(pImage->pBackendData);
9246 uint64_t cbImage = pImage->Backend->pfnGetSize(pImage->pBackendData);
9247
9248 pRegionList->cRegions = 1;
9249 pRegionList->fFlags = fFlags;
9250
9251 /*
9252 * Single region starting at the first byte/block covering the whole image,
9253 * block size equals sector size and contains no metadata.
9254 */
9255 PVDREGIONDESC pRegion = &pRegionList->aRegions[0];
9256 pRegion->offRegion = 0; /* Disk start. */
9257 pRegion->cbBlock = cbSector;
9258 pRegion->enmDataForm = VDREGIONDATAFORM_RAW;
9259 pRegion->enmMetadataForm = VDREGIONMETADATAFORM_NONE;
9260 pRegion->cbData = cbSector;
9261 pRegion->cbMetadata = 0;
9262 if (fFlags & VD_REGION_LIST_F_LOC_SIZE_BLOCKS)
9263 pRegion->cRegionBlocksOrBytes = cbImage / cbSector;
9264 else
9265 pRegion->cRegionBlocksOrBytes = cbImage;
9266
9267 *ppRegionList = pRegionList;
9268 rc = VINF_SUCCESS;
9269 }
9270 else
9271 rc = VERR_NO_MEMORY;
9272 }
9273 } while (0);
9274
9275 if (RT_UNLIKELY(fLockRead))
9276 {
9277 rc2 = vdThreadFinishRead(pDisk);
9278 AssertRC(rc2);
9279 }
9280
9281 LogFlowFunc((": %Rrc\n", rc));
9282 return rc;
9283}
9284
9285/**
9286 * Frees a region list previously queried with VDQueryRegions().
9287 *
9288 * @return nothing.
9289 * @param pRegionList The region list to free.
9290 */
9291VBOXDDU_DECL(void) VDRegionListFree(PVDREGIONLIST pRegionList)
9292{
9293 RTMemFree(pRegionList);
9294}
9295
9296/**
9297 * Get version of image in HDD container.
9298 *
9299 * @returns VBox status code.
9300 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9301 * @param pDisk Pointer to HDD container.
9302 * @param nImage Image number, counts from 0. 0 is always base image of container.
9303 * @param puVersion Where to store the image version.
9304 */
9305VBOXDDU_DECL(int) VDGetVersion(PVDISK pDisk, unsigned nImage,
9306 unsigned *puVersion)
9307{
9308 int rc = VINF_SUCCESS;
9309 int rc2;
9310 bool fLockRead = false;
9311
9312 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
9313 pDisk, nImage, puVersion));
9314 do
9315 {
9316 /* sanity check */
9317 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9318 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9319
9320 /* Check arguments. */
9321 AssertMsgBreakStmt(VALID_PTR(puVersion),
9322 ("puVersion=%#p\n", puVersion),
9323 rc = VERR_INVALID_PARAMETER);
9324
9325 rc2 = vdThreadStartRead(pDisk);
9326 AssertRC(rc2);
9327 fLockRead = true;
9328
9329 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9330 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9331
9332 *puVersion = pImage->Backend->pfnGetVersion(pImage->pBackendData);
9333 } while (0);
9334
9335 if (RT_UNLIKELY(fLockRead))
9336 {
9337 rc2 = vdThreadFinishRead(pDisk);
9338 AssertRC(rc2);
9339 }
9340
9341 LogFlowFunc(("returns %Rrc uVersion=%#x\n", rc, *puVersion));
9342 return rc;
9343}
9344
9345/**
9346 * List the capabilities of image backend in HDD container.
9347 *
9348 * @returns VBox status code.
9349 * @retval VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9350 * @param pDisk Pointer to the HDD container.
9351 * @param nImage Image number, counts from 0. 0 is always base image of container.
9352 * @param pBackendInfo Where to store the backend information.
9353 */
9354VBOXDDU_DECL(int) VDBackendInfoSingle(PVDISK pDisk, unsigned nImage,
9355 PVDBACKENDINFO pBackendInfo)
9356{
9357 int rc = VINF_SUCCESS;
9358 int rc2;
9359 bool fLockRead = false;
9360
9361 LogFlowFunc(("pDisk=%#p nImage=%u pBackendInfo=%#p\n",
9362 pDisk, nImage, pBackendInfo));
9363 do
9364 {
9365 /* sanity check */
9366 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9367 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9368
9369 /* Check arguments. */
9370 AssertMsgBreakStmt(VALID_PTR(pBackendInfo),
9371 ("pBackendInfo=%#p\n", pBackendInfo),
9372 rc = VERR_INVALID_PARAMETER);
9373
9374 rc2 = vdThreadStartRead(pDisk);
9375 AssertRC(rc2);
9376 fLockRead = true;
9377
9378 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9379 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9380
9381 pBackendInfo->pszBackend = pImage->Backend->pszBackendName;
9382 pBackendInfo->uBackendCaps = pImage->Backend->uBackendCaps;
9383 pBackendInfo->paFileExtensions = pImage->Backend->paFileExtensions;
9384 pBackendInfo->paConfigInfo = pImage->Backend->paConfigInfo;
9385 } while (0);
9386
9387 if (RT_UNLIKELY(fLockRead))
9388 {
9389 rc2 = vdThreadFinishRead(pDisk);
9390 AssertRC(rc2);
9391 }
9392
9393 LogFlowFunc(("returns %Rrc\n", rc));
9394 return rc;
9395}
9396
9397/**
9398 * Get flags of image in HDD container.
9399 *
9400 * @returns VBox status code.
9401 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9402 * @param pDisk Pointer to HDD container.
9403 * @param nImage Image number, counts from 0. 0 is always base image of container.
9404 * @param puImageFlags Where to store the image flags.
9405 */
9406VBOXDDU_DECL(int) VDGetImageFlags(PVDISK pDisk, unsigned nImage,
9407 unsigned *puImageFlags)
9408{
9409 int rc = VINF_SUCCESS;
9410 int rc2;
9411 bool fLockRead = false;
9412
9413 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
9414 pDisk, nImage, puImageFlags));
9415 do
9416 {
9417 /* sanity check */
9418 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9419 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9420
9421 /* Check arguments. */
9422 AssertMsgBreakStmt(VALID_PTR(puImageFlags),
9423 ("puImageFlags=%#p\n", puImageFlags),
9424 rc = VERR_INVALID_PARAMETER);
9425
9426 rc2 = vdThreadStartRead(pDisk);
9427 AssertRC(rc2);
9428 fLockRead = true;
9429
9430 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9431 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9432
9433 *puImageFlags = pImage->uImageFlags;
9434 } while (0);
9435
9436 if (RT_UNLIKELY(fLockRead))
9437 {
9438 rc2 = vdThreadFinishRead(pDisk);
9439 AssertRC(rc2);
9440 }
9441
9442 LogFlowFunc(("returns %Rrc uImageFlags=%#x\n", rc, *puImageFlags));
9443 return rc;
9444}
9445
9446/**
9447 * Get open flags of image in HDD container.
9448 *
9449 * @returns VBox status code.
9450 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9451 * @param pDisk Pointer to HDD container.
9452 * @param nImage Image number, counts from 0. 0 is always base image of container.
9453 * @param puOpenFlags Where to store the image open flags.
9454 */
9455VBOXDDU_DECL(int) VDGetOpenFlags(PVDISK pDisk, unsigned nImage,
9456 unsigned *puOpenFlags)
9457{
9458 int rc = VINF_SUCCESS;
9459 int rc2;
9460 bool fLockRead = false;
9461
9462 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
9463 pDisk, nImage, puOpenFlags));
9464 do
9465 {
9466 /* sanity check */
9467 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9468 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9469
9470 /* Check arguments. */
9471 AssertMsgBreakStmt(VALID_PTR(puOpenFlags),
9472 ("puOpenFlags=%#p\n", puOpenFlags),
9473 rc = VERR_INVALID_PARAMETER);
9474
9475 rc2 = vdThreadStartRead(pDisk);
9476 AssertRC(rc2);
9477 fLockRead = true;
9478
9479 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9480 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9481
9482 *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
9483 } while (0);
9484
9485 if (RT_UNLIKELY(fLockRead))
9486 {
9487 rc2 = vdThreadFinishRead(pDisk);
9488 AssertRC(rc2);
9489 }
9490
9491 LogFlowFunc(("returns %Rrc uOpenFlags=%#x\n", rc, *puOpenFlags));
9492 return rc;
9493}
9494
9495/**
9496 * Set open flags of image in HDD container.
9497 * This operation may cause file locking changes and/or files being reopened.
9498 * Note that in case of unrecoverable error all images in HDD container will be closed.
9499 *
9500 * @returns VBox status code.
9501 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9502 * @param pDisk Pointer to HDD container.
9503 * @param nImage Image number, counts from 0. 0 is always base image of container.
9504 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
9505 */
9506VBOXDDU_DECL(int) VDSetOpenFlags(PVDISK pDisk, unsigned nImage,
9507 unsigned uOpenFlags)
9508{
9509 int rc;
9510 int rc2;
9511 bool fLockWrite = false;
9512
9513 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
9514 do
9515 {
9516 /* sanity check */
9517 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9518 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9519
9520 /* Check arguments. */
9521 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
9522 ("uOpenFlags=%#x\n", uOpenFlags),
9523 rc = VERR_INVALID_PARAMETER);
9524
9525 rc2 = vdThreadStartWrite(pDisk);
9526 AssertRC(rc2);
9527 fLockWrite = true;
9528
9529 /* Destroy any discard state because the image might be changed to readonly mode. */
9530 rc = vdDiscardStateDestroy(pDisk);
9531 if (RT_FAILURE(rc))
9532 break;
9533
9534 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9535 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9536
9537 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData,
9538 uOpenFlags & ~(VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS));
9539 if (RT_SUCCESS(rc))
9540 pImage->uOpenFlags = uOpenFlags & (VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_DISCARD | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS);
9541 } while (0);
9542
9543 if (RT_UNLIKELY(fLockWrite))
9544 {
9545 rc2 = vdThreadFinishWrite(pDisk);
9546 AssertRC(rc2);
9547 }
9548
9549 LogFlowFunc(("returns %Rrc\n", rc));
9550 return rc;
9551}
9552
9553/**
9554 * Get base filename of image in HDD container. Some image formats use
9555 * other filenames as well, so don't use this for anything but informational
9556 * purposes.
9557 *
9558 * @returns VBox status code.
9559 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9560 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
9561 * @param pDisk Pointer to HDD container.
9562 * @param nImage Image number, counts from 0. 0 is always base image of container.
9563 * @param pszFilename Where to store the image file name.
9564 * @param cbFilename Size of buffer pszFilename points to.
9565 */
9566VBOXDDU_DECL(int) VDGetFilename(PVDISK pDisk, unsigned nImage,
9567 char *pszFilename, unsigned cbFilename)
9568{
9569 int rc;
9570 int rc2;
9571 bool fLockRead = false;
9572
9573 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
9574 pDisk, nImage, pszFilename, cbFilename));
9575 do
9576 {
9577 /* sanity check */
9578 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9579 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9580
9581 /* Check arguments. */
9582 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
9583 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
9584 rc = VERR_INVALID_PARAMETER);
9585 AssertMsgBreakStmt(cbFilename,
9586 ("cbFilename=%u\n", cbFilename),
9587 rc = VERR_INVALID_PARAMETER);
9588
9589 rc2 = vdThreadStartRead(pDisk);
9590 AssertRC(rc2);
9591 fLockRead = true;
9592
9593 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9594 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9595
9596 size_t cb = strlen(pImage->pszFilename);
9597 if (cb <= cbFilename)
9598 {
9599 strcpy(pszFilename, pImage->pszFilename);
9600 rc = VINF_SUCCESS;
9601 }
9602 else
9603 {
9604 strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
9605 pszFilename[cbFilename - 1] = '\0';
9606 rc = VERR_BUFFER_OVERFLOW;
9607 }
9608 } while (0);
9609
9610 if (RT_UNLIKELY(fLockRead))
9611 {
9612 rc2 = vdThreadFinishRead(pDisk);
9613 AssertRC(rc2);
9614 }
9615
9616 LogFlowFunc(("returns %Rrc, pszFilename=\"%s\"\n", rc, pszFilename));
9617 return rc;
9618}
9619
9620/**
9621 * Get the comment line of image in HDD container.
9622 *
9623 * @returns VBox status code.
9624 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9625 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
9626 * @param pDisk Pointer to HDD container.
9627 * @param nImage Image number, counts from 0. 0 is always base image of container.
9628 * @param pszComment Where to store the comment string of image. NULL is ok.
9629 * @param cbComment The size of pszComment buffer. 0 is ok.
9630 */
9631VBOXDDU_DECL(int) VDGetComment(PVDISK pDisk, unsigned nImage,
9632 char *pszComment, unsigned cbComment)
9633{
9634 int rc;
9635 int rc2;
9636 bool fLockRead = false;
9637
9638 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
9639 pDisk, nImage, pszComment, cbComment));
9640 do
9641 {
9642 /* sanity check */
9643 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9644 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9645
9646 /* Check arguments. */
9647 AssertMsgBreakStmt(VALID_PTR(pszComment),
9648 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
9649 rc = VERR_INVALID_PARAMETER);
9650 AssertMsgBreakStmt(cbComment,
9651 ("cbComment=%u\n", cbComment),
9652 rc = VERR_INVALID_PARAMETER);
9653
9654 rc2 = vdThreadStartRead(pDisk);
9655 AssertRC(rc2);
9656 fLockRead = true;
9657
9658 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9659 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9660
9661 rc = pImage->Backend->pfnGetComment(pImage->pBackendData, pszComment,
9662 cbComment);
9663 } while (0);
9664
9665 if (RT_UNLIKELY(fLockRead))
9666 {
9667 rc2 = vdThreadFinishRead(pDisk);
9668 AssertRC(rc2);
9669 }
9670
9671 LogFlowFunc(("returns %Rrc, pszComment=\"%s\"\n", rc, pszComment));
9672 return rc;
9673}
9674
9675/**
9676 * Changes the comment line of image in HDD container.
9677 *
9678 * @returns VBox status code.
9679 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9680 * @param pDisk Pointer to HDD container.
9681 * @param nImage Image number, counts from 0. 0 is always base image of container.
9682 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
9683 */
9684VBOXDDU_DECL(int) VDSetComment(PVDISK pDisk, unsigned nImage,
9685 const char *pszComment)
9686{
9687 int rc;
9688 int rc2;
9689 bool fLockWrite = false;
9690
9691 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
9692 pDisk, nImage, pszComment, pszComment));
9693 do
9694 {
9695 /* sanity check */
9696 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9697 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9698
9699 /* Check arguments. */
9700 AssertMsgBreakStmt(VALID_PTR(pszComment) || pszComment == NULL,
9701 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
9702 rc = VERR_INVALID_PARAMETER);
9703
9704 rc2 = vdThreadStartWrite(pDisk);
9705 AssertRC(rc2);
9706 fLockWrite = true;
9707
9708 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9709 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9710
9711 rc = pImage->Backend->pfnSetComment(pImage->pBackendData, pszComment);
9712 } while (0);
9713
9714 if (RT_UNLIKELY(fLockWrite))
9715 {
9716 rc2 = vdThreadFinishWrite(pDisk);
9717 AssertRC(rc2);
9718 }
9719
9720 LogFlowFunc(("returns %Rrc\n", rc));
9721 return rc;
9722}
9723
9724
9725/**
9726 * Get UUID of image in HDD container.
9727 *
9728 * @returns VBox status code.
9729 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9730 * @param pDisk Pointer to HDD container.
9731 * @param nImage Image number, counts from 0. 0 is always base image of container.
9732 * @param pUuid Where to store the image creation UUID.
9733 */
9734VBOXDDU_DECL(int) VDGetUuid(PVDISK pDisk, unsigned nImage, PRTUUID pUuid)
9735{
9736 int rc;
9737 int rc2;
9738 bool fLockRead = false;
9739
9740 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
9741 do
9742 {
9743 /* sanity check */
9744 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9745 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9746
9747 /* Check arguments. */
9748 AssertMsgBreakStmt(VALID_PTR(pUuid),
9749 ("pUuid=%#p\n", pUuid),
9750 rc = VERR_INVALID_PARAMETER);
9751
9752 rc2 = vdThreadStartRead(pDisk);
9753 AssertRC(rc2);
9754 fLockRead = true;
9755
9756 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9757 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9758
9759 rc = pImage->Backend->pfnGetUuid(pImage->pBackendData, pUuid);
9760 } while (0);
9761
9762 if (RT_UNLIKELY(fLockRead))
9763 {
9764 rc2 = vdThreadFinishRead(pDisk);
9765 AssertRC(rc2);
9766 }
9767
9768 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
9769 return rc;
9770}
9771
9772/**
9773 * Set the image's UUID. Should not be used by normal applications.
9774 *
9775 * @returns VBox status code.
9776 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9777 * @param pDisk Pointer to HDD container.
9778 * @param nImage Image number, counts from 0. 0 is always base image of container.
9779 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
9780 */
9781VBOXDDU_DECL(int) VDSetUuid(PVDISK pDisk, unsigned nImage, PCRTUUID pUuid)
9782{
9783 int rc;
9784 int rc2;
9785 bool fLockWrite = false;
9786
9787 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
9788 pDisk, nImage, pUuid, pUuid));
9789 do
9790 {
9791 /* sanity check */
9792 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9793 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9794
9795 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
9796 ("pUuid=%#p\n", pUuid),
9797 rc = VERR_INVALID_PARAMETER);
9798
9799 rc2 = vdThreadStartWrite(pDisk);
9800 AssertRC(rc2);
9801 fLockWrite = true;
9802
9803 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9804 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9805
9806 RTUUID Uuid;
9807 if (!pUuid)
9808 {
9809 RTUuidCreate(&Uuid);
9810 pUuid = &Uuid;
9811 }
9812 rc = pImage->Backend->pfnSetUuid(pImage->pBackendData, pUuid);
9813 } while (0);
9814
9815 if (RT_UNLIKELY(fLockWrite))
9816 {
9817 rc2 = vdThreadFinishWrite(pDisk);
9818 AssertRC(rc2);
9819 }
9820
9821 LogFlowFunc(("returns %Rrc\n", rc));
9822 return rc;
9823}
9824
9825/**
9826 * Get last modification UUID of image in HDD container.
9827 *
9828 * @returns VBox status code.
9829 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9830 * @param pDisk Pointer to HDD container.
9831 * @param nImage Image number, counts from 0. 0 is always base image of container.
9832 * @param pUuid Where to store the image modification UUID.
9833 */
9834VBOXDDU_DECL(int) VDGetModificationUuid(PVDISK pDisk, unsigned nImage, PRTUUID pUuid)
9835{
9836 int rc = VINF_SUCCESS;
9837 int rc2;
9838 bool fLockRead = false;
9839
9840 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
9841 do
9842 {
9843 /* sanity check */
9844 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9845 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9846
9847 /* Check arguments. */
9848 AssertMsgBreakStmt(VALID_PTR(pUuid),
9849 ("pUuid=%#p\n", pUuid),
9850 rc = VERR_INVALID_PARAMETER);
9851
9852 rc2 = vdThreadStartRead(pDisk);
9853 AssertRC(rc2);
9854 fLockRead = true;
9855
9856 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9857 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9858
9859 rc = pImage->Backend->pfnGetModificationUuid(pImage->pBackendData,
9860 pUuid);
9861 } while (0);
9862
9863 if (RT_UNLIKELY(fLockRead))
9864 {
9865 rc2 = vdThreadFinishRead(pDisk);
9866 AssertRC(rc2);
9867 }
9868
9869 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
9870 return rc;
9871}
9872
9873/**
9874 * Set the image's last modification UUID. Should not be used by normal applications.
9875 *
9876 * @returns VBox status code.
9877 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9878 * @param pDisk Pointer to HDD container.
9879 * @param nImage Image number, counts from 0. 0 is always base image of container.
9880 * @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
9881 */
9882VBOXDDU_DECL(int) VDSetModificationUuid(PVDISK pDisk, unsigned nImage, PCRTUUID pUuid)
9883{
9884 int rc;
9885 int rc2;
9886 bool fLockWrite = false;
9887
9888 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
9889 pDisk, nImage, pUuid, pUuid));
9890 do
9891 {
9892 /* sanity check */
9893 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9894 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9895
9896 /* Check arguments. */
9897 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
9898 ("pUuid=%#p\n", pUuid),
9899 rc = VERR_INVALID_PARAMETER);
9900
9901 rc2 = vdThreadStartWrite(pDisk);
9902 AssertRC(rc2);
9903 fLockWrite = true;
9904
9905 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9906 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9907
9908 RTUUID Uuid;
9909 if (!pUuid)
9910 {
9911 RTUuidCreate(&Uuid);
9912 pUuid = &Uuid;
9913 }
9914 rc = pImage->Backend->pfnSetModificationUuid(pImage->pBackendData,
9915 pUuid);
9916 } while (0);
9917
9918 if (RT_UNLIKELY(fLockWrite))
9919 {
9920 rc2 = vdThreadFinishWrite(pDisk);
9921 AssertRC(rc2);
9922 }
9923
9924 LogFlowFunc(("returns %Rrc\n", rc));
9925 return rc;
9926}
9927
9928/**
9929 * Get parent UUID of image in HDD container.
9930 *
9931 * @returns VBox status code.
9932 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9933 * @param pDisk Pointer to HDD container.
9934 * @param nImage Image number, counts from 0. 0 is always base image of container.
9935 * @param pUuid Where to store the parent image UUID.
9936 */
9937VBOXDDU_DECL(int) VDGetParentUuid(PVDISK pDisk, unsigned nImage,
9938 PRTUUID pUuid)
9939{
9940 int rc = VINF_SUCCESS;
9941 int rc2;
9942 bool fLockRead = false;
9943
9944 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
9945 do
9946 {
9947 /* sanity check */
9948 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9949 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9950
9951 /* Check arguments. */
9952 AssertMsgBreakStmt(VALID_PTR(pUuid),
9953 ("pUuid=%#p\n", pUuid),
9954 rc = VERR_INVALID_PARAMETER);
9955
9956 rc2 = vdThreadStartRead(pDisk);
9957 AssertRC(rc2);
9958 fLockRead = true;
9959
9960 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9961 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9962
9963 rc = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, pUuid);
9964 } while (0);
9965
9966 if (RT_UNLIKELY(fLockRead))
9967 {
9968 rc2 = vdThreadFinishRead(pDisk);
9969 AssertRC(rc2);
9970 }
9971
9972 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
9973 return rc;
9974}
9975
9976/**
9977 * Set the image's parent UUID. Should not be used by normal applications.
9978 *
9979 * @returns VBox status code.
9980 * @param pDisk Pointer to HDD container.
9981 * @param nImage Image number, counts from 0. 0 is always base image of container.
9982 * @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
9983 */
9984VBOXDDU_DECL(int) VDSetParentUuid(PVDISK pDisk, unsigned nImage,
9985 PCRTUUID pUuid)
9986{
9987 int rc;
9988 int rc2;
9989 bool fLockWrite = false;
9990
9991 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
9992 pDisk, nImage, pUuid, pUuid));
9993 do
9994 {
9995 /* sanity check */
9996 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9997 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9998
9999 /* Check arguments. */
10000 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
10001 ("pUuid=%#p\n", pUuid),
10002 rc = VERR_INVALID_PARAMETER);
10003
10004 rc2 = vdThreadStartWrite(pDisk);
10005 AssertRC(rc2);
10006 fLockWrite = true;
10007
10008 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10009 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10010
10011 RTUUID Uuid;
10012 if (!pUuid)
10013 {
10014 RTUuidCreate(&Uuid);
10015 pUuid = &Uuid;
10016 }
10017 rc = pImage->Backend->pfnSetParentUuid(pImage->pBackendData, pUuid);
10018 } while (0);
10019
10020 if (RT_UNLIKELY(fLockWrite))
10021 {
10022 rc2 = vdThreadFinishWrite(pDisk);
10023 AssertRC(rc2);
10024 }
10025
10026 LogFlowFunc(("returns %Rrc\n", rc));
10027 return rc;
10028}
10029
10030
10031/**
10032 * Debug helper - dumps all opened images in HDD container into the log file.
10033 *
10034 * @param pDisk Pointer to HDD container.
10035 */
10036VBOXDDU_DECL(void) VDDumpImages(PVDISK pDisk)
10037{
10038 int rc2;
10039 bool fLockRead = false;
10040
10041 do
10042 {
10043 /* sanity check */
10044 AssertPtrBreak(pDisk);
10045 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10046
10047 if (!pDisk->pInterfaceError || !VALID_PTR(pDisk->pInterfaceError->pfnMessage))
10048 pDisk->pInterfaceError->pfnMessage = vdLogMessage;
10049
10050 rc2 = vdThreadStartRead(pDisk);
10051 AssertRC(rc2);
10052 fLockRead = true;
10053
10054 vdMessageWrapper(pDisk, "--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
10055 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
10056 {
10057 vdMessageWrapper(pDisk, "Dumping VD image \"%s\" (Backend=%s)\n",
10058 pImage->pszFilename, pImage->Backend->pszBackendName);
10059 pImage->Backend->pfnDump(pImage->pBackendData);
10060 }
10061 } while (0);
10062
10063 if (RT_UNLIKELY(fLockRead))
10064 {
10065 rc2 = vdThreadFinishRead(pDisk);
10066 AssertRC(rc2);
10067 }
10068}
10069
10070
10071VBOXDDU_DECL(int) VDDiscardRanges(PVDISK pDisk, PCRTRANGE paRanges, unsigned cRanges)
10072{
10073 int rc;
10074 int rc2;
10075 bool fLockWrite = false;
10076
10077 LogFlowFunc(("pDisk=%#p paRanges=%#p cRanges=%u\n",
10078 pDisk, paRanges, cRanges));
10079 do
10080 {
10081 /* sanity check */
10082 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10083 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10084
10085 /* Check arguments. */
10086 AssertMsgBreakStmt(cRanges,
10087 ("cRanges=%u\n", cRanges),
10088 rc = VERR_INVALID_PARAMETER);
10089 AssertMsgBreakStmt(VALID_PTR(paRanges),
10090 ("paRanges=%#p\n", paRanges),
10091 rc = VERR_INVALID_PARAMETER);
10092
10093 rc2 = vdThreadStartWrite(pDisk);
10094 AssertRC(rc2);
10095 fLockWrite = true;
10096
10097 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
10098
10099 AssertMsgBreakStmt(pDisk->pLast->uOpenFlags & VD_OPEN_FLAGS_DISCARD,
10100 ("Discarding not supported\n"),
10101 rc = VERR_NOT_SUPPORTED);
10102
10103 VDIOCTX IoCtx;
10104 RTSEMEVENT hEventComplete = NIL_RTSEMEVENT;
10105
10106 rc = RTSemEventCreate(&hEventComplete);
10107 if (RT_FAILURE(rc))
10108 break;
10109
10110 vdIoCtxDiscardInit(&IoCtx, pDisk, paRanges, cRanges,
10111 vdIoCtxSyncComplete, pDisk, hEventComplete, NULL,
10112 vdDiscardHelperAsync, VDIOCTX_FLAGS_SYNC | VDIOCTX_FLAGS_DONT_FREE);
10113 rc = vdIoCtxProcessSync(&IoCtx, hEventComplete);
10114
10115 RTSemEventDestroy(hEventComplete);
10116 } while (0);
10117
10118 if (RT_UNLIKELY(fLockWrite))
10119 {
10120 rc2 = vdThreadFinishWrite(pDisk);
10121 AssertRC(rc2);
10122 }
10123
10124 LogFlowFunc(("returns %Rrc\n", rc));
10125 return rc;
10126}
10127
10128
10129VBOXDDU_DECL(int) VDAsyncRead(PVDISK pDisk, uint64_t uOffset, size_t cbRead,
10130 PCRTSGBUF pcSgBuf,
10131 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
10132 void *pvUser1, void *pvUser2)
10133{
10134 int rc = VERR_VD_BLOCK_FREE;
10135 int rc2;
10136 bool fLockRead = false;
10137 PVDIOCTX pIoCtx = NULL;
10138
10139 LogFlowFunc(("pDisk=%#p uOffset=%llu pcSgBuf=%#p cbRead=%zu pvUser1=%#p pvUser2=%#p\n",
10140 pDisk, uOffset, pcSgBuf, cbRead, pvUser1, pvUser2));
10141
10142 do
10143 {
10144 /* sanity check */
10145 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10146 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10147
10148 /* Check arguments. */
10149 AssertMsgBreakStmt(cbRead,
10150 ("cbRead=%zu\n", cbRead),
10151 rc = VERR_INVALID_PARAMETER);
10152 AssertMsgBreakStmt(VALID_PTR(pcSgBuf),
10153 ("pcSgBuf=%#p\n", pcSgBuf),
10154 rc = VERR_INVALID_PARAMETER);
10155
10156 rc2 = vdThreadStartRead(pDisk);
10157 AssertRC(rc2);
10158 fLockRead = true;
10159
10160 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
10161 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
10162 uOffset, cbRead, pDisk->cbSize),
10163 rc = VERR_INVALID_PARAMETER);
10164 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
10165
10166 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_READ, uOffset,
10167 cbRead, pDisk->pLast, pcSgBuf,
10168 pfnComplete, pvUser1, pvUser2,
10169 NULL, vdReadHelperAsync,
10170 VDIOCTX_FLAGS_ZERO_FREE_BLOCKS);
10171 if (!pIoCtx)
10172 {
10173 rc = VERR_NO_MEMORY;
10174 break;
10175 }
10176
10177 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
10178 if (rc == VINF_VD_ASYNC_IO_FINISHED)
10179 {
10180 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
10181 vdIoCtxFree(pDisk, pIoCtx);
10182 else
10183 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
10184 }
10185 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
10186 vdIoCtxFree(pDisk, pIoCtx);
10187
10188 } while (0);
10189
10190 if (RT_UNLIKELY(fLockRead) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
10191 {
10192 rc2 = vdThreadFinishRead(pDisk);
10193 AssertRC(rc2);
10194 }
10195
10196 LogFlowFunc(("returns %Rrc\n", rc));
10197 return rc;
10198}
10199
10200
10201VBOXDDU_DECL(int) VDAsyncWrite(PVDISK pDisk, uint64_t uOffset, size_t cbWrite,
10202 PCRTSGBUF pcSgBuf,
10203 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
10204 void *pvUser1, void *pvUser2)
10205{
10206 int rc;
10207 int rc2;
10208 bool fLockWrite = false;
10209 PVDIOCTX pIoCtx = NULL;
10210
10211 LogFlowFunc(("pDisk=%#p uOffset=%llu cSgBuf=%#p cbWrite=%zu pvUser1=%#p pvUser2=%#p\n",
10212 pDisk, uOffset, pcSgBuf, cbWrite, pvUser1, pvUser2));
10213 do
10214 {
10215 /* sanity check */
10216 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10217 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10218
10219 /* Check arguments. */
10220 AssertMsgBreakStmt(cbWrite,
10221 ("cbWrite=%zu\n", cbWrite),
10222 rc = VERR_INVALID_PARAMETER);
10223 AssertMsgBreakStmt(VALID_PTR(pcSgBuf),
10224 ("pcSgBuf=%#p\n", pcSgBuf),
10225 rc = VERR_INVALID_PARAMETER);
10226
10227 rc2 = vdThreadStartWrite(pDisk);
10228 AssertRC(rc2);
10229 fLockWrite = true;
10230
10231 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
10232 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
10233 uOffset, cbWrite, pDisk->cbSize),
10234 rc = VERR_INVALID_PARAMETER);
10235 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
10236
10237 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_WRITE, uOffset,
10238 cbWrite, pDisk->pLast, pcSgBuf,
10239 pfnComplete, pvUser1, pvUser2,
10240 NULL, vdWriteHelperAsync,
10241 VDIOCTX_FLAGS_DEFAULT);
10242 if (!pIoCtx)
10243 {
10244 rc = VERR_NO_MEMORY;
10245 break;
10246 }
10247
10248 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
10249 if (rc == VINF_VD_ASYNC_IO_FINISHED)
10250 {
10251 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
10252 vdIoCtxFree(pDisk, pIoCtx);
10253 else
10254 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
10255 }
10256 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
10257 vdIoCtxFree(pDisk, pIoCtx);
10258 } while (0);
10259
10260 if (RT_UNLIKELY(fLockWrite) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
10261 {
10262 rc2 = vdThreadFinishWrite(pDisk);
10263 AssertRC(rc2);
10264 }
10265
10266 LogFlowFunc(("returns %Rrc\n", rc));
10267 return rc;
10268}
10269
10270
10271VBOXDDU_DECL(int) VDAsyncFlush(PVDISK pDisk, PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
10272 void *pvUser1, void *pvUser2)
10273{
10274 int rc;
10275 int rc2;
10276 bool fLockWrite = false;
10277 PVDIOCTX pIoCtx = NULL;
10278
10279 LogFlowFunc(("pDisk=%#p\n", pDisk));
10280
10281 do
10282 {
10283 /* sanity check */
10284 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10285 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10286
10287 rc2 = vdThreadStartWrite(pDisk);
10288 AssertRC(rc2);
10289 fLockWrite = true;
10290
10291 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
10292
10293 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_FLUSH, 0,
10294 0, pDisk->pLast, NULL,
10295 pfnComplete, pvUser1, pvUser2,
10296 NULL, vdFlushHelperAsync,
10297 VDIOCTX_FLAGS_DEFAULT);
10298 if (!pIoCtx)
10299 {
10300 rc = VERR_NO_MEMORY;
10301 break;
10302 }
10303
10304 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
10305 if (rc == VINF_VD_ASYNC_IO_FINISHED)
10306 {
10307 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
10308 vdIoCtxFree(pDisk, pIoCtx);
10309 else
10310 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
10311 }
10312 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
10313 vdIoCtxFree(pDisk, pIoCtx);
10314 } while (0);
10315
10316 if (RT_UNLIKELY(fLockWrite) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
10317 {
10318 rc2 = vdThreadFinishWrite(pDisk);
10319 AssertRC(rc2);
10320 }
10321
10322 LogFlowFunc(("returns %Rrc\n", rc));
10323 return rc;
10324}
10325
10326VBOXDDU_DECL(int) VDAsyncDiscardRanges(PVDISK pDisk, PCRTRANGE paRanges, unsigned cRanges,
10327 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
10328 void *pvUser1, void *pvUser2)
10329{
10330 int rc;
10331 int rc2;
10332 bool fLockWrite = false;
10333 PVDIOCTX pIoCtx = NULL;
10334
10335 LogFlowFunc(("pDisk=%#p\n", pDisk));
10336
10337 do
10338 {
10339 /* sanity check */
10340 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10341 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10342
10343 rc2 = vdThreadStartWrite(pDisk);
10344 AssertRC(rc2);
10345 fLockWrite = true;
10346
10347 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
10348
10349 pIoCtx = vdIoCtxDiscardAlloc(pDisk, paRanges, cRanges,
10350 pfnComplete, pvUser1, pvUser2, NULL,
10351 vdDiscardHelperAsync,
10352 VDIOCTX_FLAGS_DEFAULT);
10353 if (!pIoCtx)
10354 {
10355 rc = VERR_NO_MEMORY;
10356 break;
10357 }
10358
10359 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
10360 if (rc == VINF_VD_ASYNC_IO_FINISHED)
10361 {
10362 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
10363 vdIoCtxFree(pDisk, pIoCtx);
10364 else
10365 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
10366 }
10367 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
10368 vdIoCtxFree(pDisk, pIoCtx);
10369 } while (0);
10370
10371 if (RT_UNLIKELY(fLockWrite) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
10372 {
10373 rc2 = vdThreadFinishWrite(pDisk);
10374 AssertRC(rc2);
10375 }
10376
10377 LogFlowFunc(("returns %Rrc\n", rc));
10378 return rc;
10379}
10380
10381VBOXDDU_DECL(int) VDRepair(PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
10382 const char *pszFilename, const char *pszBackend,
10383 uint32_t fFlags)
10384{
10385 int rc = VERR_NOT_SUPPORTED;
10386 PCVDIMAGEBACKEND pBackend = NULL;
10387 VDINTERFACEIOINT VDIfIoInt;
10388 VDINTERFACEIO VDIfIoFallback;
10389 PVDINTERFACEIO pInterfaceIo;
10390
10391 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
10392 /* Check arguments. */
10393 AssertMsgReturn(VALID_PTR(pszFilename) && *pszFilename,
10394 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
10395 VERR_INVALID_PARAMETER);
10396 AssertMsgReturn(VALID_PTR(pszBackend),
10397 ("pszBackend=%#p\n", pszBackend),
10398 VERR_INVALID_PARAMETER);
10399 AssertMsgReturn((fFlags & ~VD_REPAIR_FLAGS_MASK) == 0,
10400 ("fFlags=%#x\n", fFlags),
10401 VERR_INVALID_PARAMETER);
10402
10403 pInterfaceIo = VDIfIoGet(pVDIfsImage);
10404 if (!pInterfaceIo)
10405 {
10406 /*
10407 * Caller doesn't provide an I/O interface, create our own using the
10408 * native file API.
10409 */
10410 vdIfIoFallbackCallbacksSetup(&VDIfIoFallback);
10411 pInterfaceIo = &VDIfIoFallback;
10412 }
10413
10414 /* Set up the internal I/O interface. */
10415 AssertReturn(!VDIfIoIntGet(pVDIfsImage), VERR_INVALID_PARAMETER);
10416 VDIfIoInt.pfnOpen = vdIOIntOpenLimited;
10417 VDIfIoInt.pfnClose = vdIOIntCloseLimited;
10418 VDIfIoInt.pfnDelete = vdIOIntDeleteLimited;
10419 VDIfIoInt.pfnMove = vdIOIntMoveLimited;
10420 VDIfIoInt.pfnGetFreeSpace = vdIOIntGetFreeSpaceLimited;
10421 VDIfIoInt.pfnGetModificationTime = vdIOIntGetModificationTimeLimited;
10422 VDIfIoInt.pfnGetSize = vdIOIntGetSizeLimited;
10423 VDIfIoInt.pfnSetSize = vdIOIntSetSizeLimited;
10424 VDIfIoInt.pfnReadUser = vdIOIntReadUserLimited;
10425 VDIfIoInt.pfnWriteUser = vdIOIntWriteUserLimited;
10426 VDIfIoInt.pfnReadMeta = vdIOIntReadMetaLimited;
10427 VDIfIoInt.pfnWriteMeta = vdIOIntWriteMetaLimited;
10428 VDIfIoInt.pfnFlush = vdIOIntFlushLimited;
10429 rc = VDInterfaceAdd(&VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
10430 pInterfaceIo, sizeof(VDINTERFACEIOINT), &pVDIfsImage);
10431 AssertRC(rc);
10432
10433 rc = vdFindImageBackend(pszBackend, &pBackend);
10434 if (RT_SUCCESS(rc))
10435 {
10436 if (pBackend->pfnRepair)
10437 rc = pBackend->pfnRepair(pszFilename, pVDIfsDisk, pVDIfsImage, fFlags);
10438 else
10439 rc = VERR_VD_IMAGE_REPAIR_NOT_SUPPORTED;
10440 }
10441
10442 LogFlowFunc(("returns %Rrc\n", rc));
10443 return rc;
10444}
10445
10446
10447/*
10448 * generic plugin functions
10449 */
10450
10451/**
10452 * @interface_method_impl{VDIMAGEBACKEND,pfnComposeLocation}
10453 */
10454DECLCALLBACK(int) genericFileComposeLocation(PVDINTERFACE pConfig, char **pszLocation)
10455{
10456 RT_NOREF1(pConfig);
10457 *pszLocation = NULL;
10458 return VINF_SUCCESS;
10459}
10460
10461/**
10462 * @interface_method_impl{VDIMAGEBACKEND,pfnComposeName}
10463 */
10464DECLCALLBACK(int) genericFileComposeName(PVDINTERFACE pConfig, char **pszName)
10465{
10466 RT_NOREF1(pConfig);
10467 *pszName = NULL;
10468 return VINF_SUCCESS;
10469}
10470
Note: See TracBrowser for help on using the repository browser.

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