VirtualBox

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

Last change on this file since 75373 was 75373, checked in by vboxsync, 7 years ago

Main: bugref:6598: Added ability to merge mediums with different sizes in the offline mode

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

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