VirtualBox

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

Last change on this file since 80074 was 79965, checked in by vboxsync, 5 years ago

Storage: Added a desired format parameter to VDGetFormat() so Main can pass along the device type. Bumped the RAW backend down after the CUE and VISO to prevent (impossible) mixups of tiny files. Extended rawProbe() to look for a valid ISO-9660/UDF descriptor sequence on the image if the caller doesn't say it should be a floppy or hdd, only if that fail go by the extension. Note! the pfnProbe callback should probably get a score return parameter to indicate how confient the backend is about the probe result, as this would prevent the RAW backend from accidentally grabbing images which belongs to a plug-in. bugref:9151

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

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