VirtualBox

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

Last change on this file since 107700 was 107596, checked in by vboxsync, 5 weeks ago

Storage/VD.cpp: Some parfait warning fixes about unused assignments und unread variables, bugref:3409

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