VirtualBox

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

Last change on this file since 98296 was 98103, checked in by vboxsync, 22 months ago

Copyright year updates by scm.

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