VirtualBox

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

Last change on this file since 99960 was 99739, checked in by vboxsync, 19 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 339.1 KB
Line 
1/* $Id: VD.cpp 99739 2023-05-11 01:01:08Z vboxsync $ */
2/** @file
3 * VD - Virtual disk container implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_VD
33#include <VBox/vd.h>
34#include <VBox/err.h>
35#include <VBox/sup.h>
36#include <VBox/log.h>
37
38#include <iprt/alloc.h>
39#include <iprt/assert.h>
40#include <iprt/uuid.h>
41#include <iprt/file.h>
42#include <iprt/string.h>
43#include <iprt/asm.h>
44#include <iprt/param.h>
45#include <iprt/path.h>
46#include <iprt/sg.h>
47#include <iprt/semaphore.h>
48#include <iprt/vector.h>
49
50#include "VDInternal.h"
51
52/** Buffer size used for merging images. */
53#define VD_MERGE_BUFFER_SIZE (16 * _1M)
54
55/** Maximum number of segments in one I/O task. */
56#define VD_IO_TASK_SEGMENTS_MAX 64
57
58/** Threshold after not recently used blocks are removed from the list. */
59#define VD_DISCARD_REMOVE_THRESHOLD (10 * _1M) /** @todo experiment */
60
61/**
62 * VD async I/O interface storage descriptor.
63 */
64typedef struct VDIIOFALLBACKSTORAGE
65{
66 /** File handle. */
67 RTFILE File;
68 /** Completion callback. */
69 PFNVDCOMPLETED pfnCompleted;
70 /** Thread for async access. */
71 RTTHREAD ThreadAsync;
72} VDIIOFALLBACKSTORAGE, *PVDIIOFALLBACKSTORAGE;
73
74/**
75 * uModified bit flags.
76 */
77#define VD_IMAGE_MODIFIED_FLAG RT_BIT(0)
78#define VD_IMAGE_MODIFIED_FIRST RT_BIT(1)
79#define VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE RT_BIT(2)
80
81
82# define VD_IS_LOCKED(a_pDisk) \
83 do \
84 { \
85 NOREF(a_pDisk); \
86 AssertMsg((a_pDisk)->fLocked, \
87 ("Lock not held\n"));\
88 } while(0)
89
90/**
91 * VBox parent read descriptor, used internally for compaction.
92 */
93typedef struct VDPARENTSTATEDESC
94{
95 /** Pointer to disk descriptor. */
96 PVDISK pDisk;
97 /** Pointer to image descriptor. */
98 PVDIMAGE pImage;
99} VDPARENTSTATEDESC, *PVDPARENTSTATEDESC;
100
101/**
102 * Transfer direction.
103 */
104typedef enum VDIOCTXTXDIR
105{
106 /** Read */
107 VDIOCTXTXDIR_READ = 0,
108 /** Write */
109 VDIOCTXTXDIR_WRITE,
110 /** Flush */
111 VDIOCTXTXDIR_FLUSH,
112 /** Discard */
113 VDIOCTXTXDIR_DISCARD,
114 /** 32bit hack */
115 VDIOCTXTXDIR_32BIT_HACK = 0x7fffffff
116} VDIOCTXTXDIR, *PVDIOCTXTXDIR;
117
118/** Transfer function */
119typedef DECLCALLBACKTYPE(int, FNVDIOCTXTRANSFER ,(PVDIOCTX pIoCtx));
120/** Pointer to a transfer function. */
121typedef FNVDIOCTXTRANSFER *PFNVDIOCTXTRANSFER;
122
123/**
124 * I/O context
125 */
126typedef struct VDIOCTX
127{
128 /** Pointer to the next I/O context. */
129 struct VDIOCTX * volatile pIoCtxNext;
130 /** Disk this is request is for. */
131 PVDISK pDisk;
132 /** Return code. */
133 int rcReq;
134 /** Various flags for the I/O context. */
135 uint32_t fFlags;
136 /** Number of data transfers currently pending. */
137 volatile uint32_t cDataTransfersPending;
138 /** How many meta data transfers are pending. */
139 volatile uint32_t cMetaTransfersPending;
140 /** Flag whether the request finished */
141 volatile bool fComplete;
142 /** Temporary allocated memory which is freed
143 * when the context completes. */
144 void *pvAllocation;
145 /** Transfer function. */
146 PFNVDIOCTXTRANSFER pfnIoCtxTransfer;
147 /** Next transfer part after the current one completed. */
148 PFNVDIOCTXTRANSFER pfnIoCtxTransferNext;
149 /** Transfer direction */
150 VDIOCTXTXDIR enmTxDir;
151 /** Request type dependent data. */
152 union
153 {
154 /** I/O request (read/write). */
155 struct
156 {
157 /** Number of bytes left until this context completes. */
158 volatile uint32_t cbTransferLeft;
159 /** Current offset */
160 volatile uint64_t uOffset;
161 /** Number of bytes to transfer */
162 volatile size_t cbTransfer;
163 /** Current image in the chain. */
164 PVDIMAGE pImageCur;
165 /** Start image to read from. pImageCur is reset to this
166 * value after it reached the first image in the chain. */
167 PVDIMAGE pImageStart;
168 /** S/G buffer */
169 RTSGBUF SgBuf;
170 /** Number of bytes to clear in the buffer before the current read. */
171 size_t cbBufClear;
172 /** Number of images to read. */
173 unsigned cImagesRead;
174 /** Override for the parent image to start reading from. */
175 PVDIMAGE pImageParentOverride;
176 /** Original offset of the transfer - required for filtering read requests. */
177 uint64_t uOffsetXferOrig;
178 /** Original size of the transfer - required for fitlering read requests. */
179 size_t cbXferOrig;
180 } Io;
181 /** Discard requests. */
182 struct
183 {
184 /** Pointer to the range descriptor array. */
185 PCRTRANGE paRanges;
186 /** Number of ranges in the array. */
187 unsigned cRanges;
188 /** Range descriptor index which is processed. */
189 unsigned idxRange;
190 /** Start offset to discard currently. */
191 uint64_t offCur;
192 /** How many bytes left to discard in the current range. */
193 size_t cbDiscardLeft;
194 /** How many bytes to discard in the current block (<= cbDiscardLeft). */
195 size_t cbThisDiscard;
196 /** Discard block handled currently. */
197 PVDDISCARDBLOCK pBlock;
198 } Discard;
199 } Req;
200 /** Parent I/O context if any. Sets the type of the context (root/child) */
201 PVDIOCTX pIoCtxParent;
202 /** Type dependent data (root/child) */
203 union
204 {
205 /** Root data */
206 struct
207 {
208 /** Completion callback */
209 PFNVDASYNCTRANSFERCOMPLETE pfnComplete;
210 /** User argument 1 passed on completion. */
211 void *pvUser1;
212 /** User argument 2 passed on completion. */
213 void *pvUser2;
214 } Root;
215 /** Child data */
216 struct
217 {
218 /** Saved start offset */
219 uint64_t uOffsetSaved;
220 /** Saved transfer size */
221 size_t cbTransferLeftSaved;
222 /** Number of bytes transferred from the parent if this context completes. */
223 size_t cbTransferParent;
224 /** Number of bytes to pre read */
225 size_t cbPreRead;
226 /** Number of bytes to post read. */
227 size_t cbPostRead;
228 /** Number of bytes to write left in the parent. */
229 size_t cbWriteParent;
230 /** Write type dependent data. */
231 union
232 {
233 /** Optimized */
234 struct
235 {
236 /** Bytes to fill to satisfy the block size. Not part of the virtual disk. */
237 size_t cbFill;
238 /** Bytes to copy instead of reading from the parent */
239 size_t cbWriteCopy;
240 /** Bytes to read from the image. */
241 size_t cbReadImage;
242 } Optimized;
243 } Write;
244 } Child;
245 } Type;
246} VDIOCTX;
247
248/** Default flags for an I/O context, i.e. unblocked and async. */
249#define VDIOCTX_FLAGS_DEFAULT (0)
250/** Flag whether the context is blocked. */
251#define VDIOCTX_FLAGS_BLOCKED RT_BIT_32(0)
252/** Flag whether the I/O context is using synchronous I/O. */
253#define VDIOCTX_FLAGS_SYNC RT_BIT_32(1)
254/** Flag whether the read should update the cache. */
255#define VDIOCTX_FLAGS_READ_UPDATE_CACHE RT_BIT_32(2)
256/** Flag whether free blocks should be zeroed.
257 * If false and no image has data for sepcified
258 * range VERR_VD_BLOCK_FREE is returned for the I/O context.
259 * Note that unallocated blocks are still zeroed
260 * if at least one image has valid data for a part
261 * of the range.
262 */
263#define VDIOCTX_FLAGS_ZERO_FREE_BLOCKS RT_BIT_32(3)
264/** Don't free the I/O context when complete because
265 * it was alloacted elsewhere (stack, ...). */
266#define VDIOCTX_FLAGS_DONT_FREE RT_BIT_32(4)
267/** Don't set the modified flag for this I/O context when writing. */
268#define VDIOCTX_FLAGS_DONT_SET_MODIFIED_FLAG RT_BIT_32(5)
269/** The write filter was applied already and shouldn't be applied a second time.
270 * Used at the beginning of vdWriteHelperAsync() because it might be called
271 * multiple times.
272 */
273#define VDIOCTX_FLAGS_WRITE_FILTER_APPLIED RT_BIT_32(6)
274
275/** NIL I/O context pointer value. */
276#define NIL_VDIOCTX ((PVDIOCTX)0)
277
278/**
279 * List node for deferred I/O contexts.
280 */
281typedef struct VDIOCTXDEFERRED
282{
283 /** Node in the list of deferred requests.
284 * A request can be deferred if the image is growing
285 * and the request accesses the same range or if
286 * the backend needs to read or write metadata from the disk
287 * before it can continue. */
288 RTLISTNODE NodeDeferred;
289 /** I/O context this entry points to. */
290 PVDIOCTX pIoCtx;
291} VDIOCTXDEFERRED, *PVDIOCTXDEFERRED;
292
293/**
294 * I/O task.
295 */
296typedef struct VDIOTASK
297{
298 /** Next I/O task waiting in the list. */
299 struct VDIOTASK * volatile pNext;
300 /** Storage this task belongs to. */
301 PVDIOSTORAGE pIoStorage;
302 /** Optional completion callback. */
303 PFNVDXFERCOMPLETED pfnComplete;
304 /** Opaque user data. */
305 void *pvUser;
306 /** Completion status code for the task. */
307 int rcReq;
308 /** Flag whether this is a meta data transfer. */
309 bool fMeta;
310 /** Type dependent data. */
311 union
312 {
313 /** User data transfer. */
314 struct
315 {
316 /** Number of bytes this task transferred. */
317 uint32_t cbTransfer;
318 /** Pointer to the I/O context the task belongs. */
319 PVDIOCTX pIoCtx;
320 } User;
321 /** Meta data transfer. */
322 struct
323 {
324 /** Meta transfer this task is for. */
325 PVDMETAXFER pMetaXfer;
326 } Meta;
327 } Type;
328} VDIOTASK;
329
330/**
331 * Storage handle.
332 */
333typedef struct VDIOSTORAGE
334{
335 /** Image I/O state this storage handle belongs to. */
336 PVDIO pVDIo;
337 /** AVL tree for pending async metadata transfers. */
338 PAVLRFOFFTREE pTreeMetaXfers;
339 /** Storage handle */
340 void *pStorage;
341} VDIOSTORAGE;
342
343/**
344 * Metadata transfer.
345 *
346 * @note This entry can't be freed if either the list is not empty or
347 * the reference counter is not 0.
348 * The assumption is that the backends don't need to read huge amounts of
349 * metadata to complete a transfer so the additional memory overhead should
350 * be relatively small.
351 */
352typedef struct VDMETAXFER
353{
354 /** AVL core for fast search (the file offset is the key) */
355 AVLRFOFFNODECORE Core;
356 /** I/O storage for this transfer. */
357 PVDIOSTORAGE pIoStorage;
358 /** Flags. */
359 uint32_t fFlags;
360 /** List of I/O contexts waiting for this metadata transfer to complete. */
361 RTLISTNODE ListIoCtxWaiting;
362 /** Number of references to this entry. */
363 unsigned cRefs;
364 /** Size of the data stored with this entry. */
365 size_t cbMeta;
366 /** Shadow buffer which is used in case a write is still active and other
367 * writes update the shadow buffer. */
368 uint8_t *pbDataShw;
369 /** List of I/O contexts updating the shadow buffer while there is a write
370 * in progress. */
371 RTLISTNODE ListIoCtxShwWrites;
372 /** Data stored - variable size. */
373 uint8_t abData[1];
374} VDMETAXFER;
375
376/**
377 * The transfer direction for the metadata.
378 */
379#define VDMETAXFER_TXDIR_MASK 0x3
380#define VDMETAXFER_TXDIR_NONE 0x0
381#define VDMETAXFER_TXDIR_WRITE 0x1
382#define VDMETAXFER_TXDIR_READ 0x2
383#define VDMETAXFER_TXDIR_FLUSH 0x3
384#define VDMETAXFER_TXDIR_GET(flags) ((flags) & VDMETAXFER_TXDIR_MASK)
385#define VDMETAXFER_TXDIR_SET(flags, dir) ((flags) = (flags & ~VDMETAXFER_TXDIR_MASK) | (dir))
386
387/** Forward declaration of the async discard helper. */
388static DECLCALLBACK(int) vdDiscardHelperAsync(PVDIOCTX pIoCtx);
389static DECLCALLBACK(int) vdWriteHelperAsync(PVDIOCTX pIoCtx);
390static void vdDiskProcessBlockedIoCtx(PVDISK pDisk);
391static int vdDiskUnlock(PVDISK pDisk, PVDIOCTX pIoCtxRc);
392static DECLCALLBACK(void) vdIoCtxSyncComplete(void *pvUser1, void *pvUser2, int rcReq);
393
394/**
395 * internal: issue error message.
396 */
397static int vdError(PVDISK pDisk, int rc, RT_SRC_POS_DECL,
398 const char *pszFormat, ...)
399{
400 va_list va;
401 va_start(va, pszFormat);
402 if (pDisk->pInterfaceError)
403 pDisk->pInterfaceError->pfnError(pDisk->pInterfaceError->Core.pvUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
404 va_end(va);
405 return rc;
406}
407
408/**
409 * internal: thread synchronization, start read.
410 */
411DECLINLINE(int) vdThreadStartRead(PVDISK pDisk)
412{
413 int rc = VINF_SUCCESS;
414 if (RT_UNLIKELY(pDisk->pInterfaceThreadSync))
415 rc = pDisk->pInterfaceThreadSync->pfnStartRead(pDisk->pInterfaceThreadSync->Core.pvUser);
416 return rc;
417}
418
419/**
420 * internal: thread synchronization, finish read.
421 */
422DECLINLINE(int) vdThreadFinishRead(PVDISK pDisk)
423{
424 int rc = VINF_SUCCESS;
425 if (RT_UNLIKELY(pDisk->pInterfaceThreadSync))
426 rc = pDisk->pInterfaceThreadSync->pfnFinishRead(pDisk->pInterfaceThreadSync->Core.pvUser);
427 return rc;
428}
429
430/**
431 * internal: thread synchronization, start write.
432 */
433DECLINLINE(int) vdThreadStartWrite(PVDISK pDisk)
434{
435 int rc = VINF_SUCCESS;
436 if (RT_UNLIKELY(pDisk->pInterfaceThreadSync))
437 rc = pDisk->pInterfaceThreadSync->pfnStartWrite(pDisk->pInterfaceThreadSync->Core.pvUser);
438 return rc;
439}
440
441/**
442 * internal: thread synchronization, finish write.
443 */
444DECLINLINE(int) vdThreadFinishWrite(PVDISK pDisk)
445{
446 int rc = VINF_SUCCESS;
447 if (RT_UNLIKELY(pDisk->pInterfaceThreadSync))
448 rc = pDisk->pInterfaceThreadSync->pfnFinishWrite(pDisk->pInterfaceThreadSync->Core.pvUser);
449 return rc;
450}
451
452/**
453 * internal: add image structure to the end of images list.
454 */
455static void vdAddImageToList(PVDISK pDisk, PVDIMAGE pImage)
456{
457 pImage->pPrev = NULL;
458 pImage->pNext = NULL;
459
460 if (pDisk->pBase)
461 {
462 Assert(pDisk->cImages > 0);
463 pImage->pPrev = pDisk->pLast;
464 pDisk->pLast->pNext = pImage;
465 pDisk->pLast = pImage;
466 }
467 else
468 {
469 Assert(pDisk->cImages == 0);
470 pDisk->pBase = pImage;
471 pDisk->pLast = pImage;
472 }
473
474 pDisk->cImages++;
475}
476
477/**
478 * internal: remove image structure from the images list.
479 */
480static void vdRemoveImageFromList(PVDISK pDisk, PVDIMAGE pImage)
481{
482 Assert(pDisk->cImages > 0);
483
484 if (pImage->pPrev)
485 pImage->pPrev->pNext = pImage->pNext;
486 else
487 pDisk->pBase = pImage->pNext;
488
489 if (pImage->pNext)
490 pImage->pNext->pPrev = pImage->pPrev;
491 else
492 pDisk->pLast = pImage->pPrev;
493
494 pImage->pPrev = NULL;
495 pImage->pNext = NULL;
496
497 pDisk->cImages--;
498}
499
500/**
501 * Release a referene to the filter decrementing the counter and destroying the filter
502 * when the counter reaches zero.
503 *
504 * @returns The new reference count.
505 * @param pFilter The filter to release.
506 */
507static uint32_t vdFilterRelease(PVDFILTER pFilter)
508{
509 uint32_t cRefs = ASMAtomicDecU32(&pFilter->cRefs);
510 if (!cRefs)
511 {
512 pFilter->pBackend->pfnDestroy(pFilter->pvBackendData);
513 RTMemFree(pFilter);
514 }
515
516 return cRefs;
517}
518
519/**
520 * Increments the reference counter of the given filter.
521 *
522 * @return The new reference count.
523 * @param pFilter The filter.
524 */
525static uint32_t vdFilterRetain(PVDFILTER pFilter)
526{
527 return ASMAtomicIncU32(&pFilter->cRefs);
528}
529
530/**
531 * internal: find image by index into the images list.
532 */
533static PVDIMAGE vdGetImageByNumber(PVDISK pDisk, unsigned nImage)
534{
535 PVDIMAGE pImage = pDisk->pBase;
536 if (nImage == VD_LAST_IMAGE)
537 return pDisk->pLast;
538 while (pImage && nImage)
539 {
540 pImage = pImage->pNext;
541 nImage--;
542 }
543 return pImage;
544}
545
546/**
547 * Creates a new region list from the given one converting to match the flags if necessary.
548 *
549 * @returns VBox status code.
550 * @param pRegionList The region list to convert from.
551 * @param fFlags The flags for the new region list.
552 * @param ppRegionList Where to store the new region list on success.
553 */
554static int vdRegionListConv(PCVDREGIONLIST pRegionList, uint32_t fFlags, PPVDREGIONLIST ppRegionList)
555{
556 int rc = VINF_SUCCESS;
557 PVDREGIONLIST pRegionListNew = (PVDREGIONLIST)RTMemDup(pRegionList,
558 RT_UOFFSETOF_DYN(VDREGIONLIST, aRegions[pRegionList->cRegions]));
559 if (RT_LIKELY(pRegionListNew))
560 {
561 /* Do we have to convert anything? */
562 if (pRegionList->fFlags != fFlags)
563 {
564 uint64_t offRegionNext = 0;
565
566 pRegionListNew->fFlags = fFlags;
567 for (unsigned i = 0; i < pRegionListNew->cRegions; i++)
568 {
569 PVDREGIONDESC pRegion = &pRegionListNew->aRegions[i];
570
571 if ( (fFlags & VD_REGION_LIST_F_LOC_SIZE_BLOCKS)
572 && !(pRegionList->fFlags & VD_REGION_LIST_F_LOC_SIZE_BLOCKS))
573 {
574 Assert(!(pRegion->cRegionBlocksOrBytes % pRegion->cbBlock));
575
576 /* Convert from bytes to logical blocks. */
577 pRegion->offRegion = offRegionNext;
578 pRegion->cRegionBlocksOrBytes = pRegion->cRegionBlocksOrBytes / pRegion->cbBlock;
579 offRegionNext += pRegion->cRegionBlocksOrBytes;
580 }
581 else
582 {
583 /* Convert from logical blocks to bytes. */
584 pRegion->offRegion = offRegionNext;
585 pRegion->cRegionBlocksOrBytes = pRegion->cRegionBlocksOrBytes * pRegion->cbBlock;
586 offRegionNext += pRegion->cRegionBlocksOrBytes;
587 }
588 }
589 }
590
591 *ppRegionList = pRegionListNew;
592 }
593 else
594 rc = VERR_NO_MEMORY;
595
596 return rc;
597}
598
599/**
600 * Returns the virtual size of the image in bytes.
601 *
602 * @returns Size of the given image in bytes.
603 * @param pImage The image to get the size from.
604 */
605static uint64_t vdImageGetSize(PVDIMAGE pImage)
606{
607 uint64_t cbImage = 0;
608
609 if (pImage->cbImage == VD_IMAGE_SIZE_UNINITIALIZED)
610 {
611 PCVDREGIONLIST pRegionList = NULL;
612 int rc = pImage->Backend->pfnQueryRegions(pImage->pBackendData, &pRegionList);
613 if (RT_SUCCESS(rc))
614 {
615 if (pRegionList->fFlags & VD_REGION_LIST_F_LOC_SIZE_BLOCKS)
616 {
617 PVDREGIONLIST pRegionListConv = NULL;
618 rc = vdRegionListConv(pRegionList, 0, &pRegionListConv);
619 if (RT_SUCCESS(rc))
620 {
621 for (uint32_t i = 0; i < pRegionListConv->cRegions; i++)
622 cbImage += pRegionListConv->aRegions[i].cRegionBlocksOrBytes;
623
624 VDRegionListFree(pRegionListConv);
625 }
626 }
627 else
628 for (uint32_t i = 0; i < pRegionList->cRegions; i++)
629 cbImage += pRegionList->aRegions[i].cRegionBlocksOrBytes;
630
631 AssertPtr(pImage->Backend->pfnRegionListRelease);
632 pImage->Backend->pfnRegionListRelease(pImage->pBackendData, pRegionList);
633 pImage->cbImage = cbImage; /* Cache the value. */
634 }
635 }
636 else
637 cbImage = pImage->cbImage;
638
639 return cbImage;
640}
641
642/**
643 * Applies the filter chain to the given write request.
644 *
645 * @returns VBox status code.
646 * @param pDisk The HDD container.
647 * @param uOffset The start offset of the write.
648 * @param cbWrite Number of bytes to write.
649 * @param pIoCtx The I/O context associated with the request.
650 */
651static int vdFilterChainApplyWrite(PVDISK pDisk, uint64_t uOffset, size_t cbWrite,
652 PVDIOCTX pIoCtx)
653{
654 int rc = VINF_SUCCESS;
655
656 VD_IS_LOCKED(pDisk);
657
658 PVDFILTER pFilter;
659 RTListForEach(&pDisk->ListFilterChainWrite, pFilter, VDFILTER, ListNodeChainWrite)
660 {
661 rc = pFilter->pBackend->pfnFilterWrite(pFilter->pvBackendData, uOffset, cbWrite, pIoCtx);
662 if (RT_FAILURE(rc))
663 break;
664 /* Reset S/G buffer for the next filter. */
665 RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
666 }
667
668 return rc;
669}
670
671/**
672 * Applies the filter chain to the given read request.
673 *
674 * @returns VBox status code.
675 * @param pDisk The HDD container.
676 * @param uOffset The start offset of the read.
677 * @param cbRead Number of bytes read.
678 * @param pIoCtx The I/O context associated with the request.
679 */
680static int vdFilterChainApplyRead(PVDISK pDisk, uint64_t uOffset, size_t cbRead,
681 PVDIOCTX pIoCtx)
682{
683 int rc = VINF_SUCCESS;
684
685 VD_IS_LOCKED(pDisk);
686
687 /* Reset buffer before starting. */
688 RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
689
690 PVDFILTER pFilter;
691 RTListForEach(&pDisk->ListFilterChainRead, pFilter, VDFILTER, ListNodeChainRead)
692 {
693 rc = pFilter->pBackend->pfnFilterRead(pFilter->pvBackendData, uOffset, cbRead, pIoCtx);
694 if (RT_FAILURE(rc))
695 break;
696 /* Reset S/G buffer for the next filter. */
697 RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
698 }
699
700 return rc;
701}
702
703DECLINLINE(void) vdIoCtxRootComplete(PVDISK pDisk, PVDIOCTX pIoCtx)
704{
705 if ( RT_SUCCESS(pIoCtx->rcReq)
706 && pIoCtx->enmTxDir == VDIOCTXTXDIR_READ)
707 pIoCtx->rcReq = vdFilterChainApplyRead(pDisk, pIoCtx->Req.Io.uOffsetXferOrig,
708 pIoCtx->Req.Io.cbXferOrig, pIoCtx);
709
710 pIoCtx->Type.Root.pfnComplete(pIoCtx->Type.Root.pvUser1,
711 pIoCtx->Type.Root.pvUser2,
712 pIoCtx->rcReq);
713}
714
715/**
716 * Initialize the structure members of a given I/O context.
717 */
718DECLINLINE(void) vdIoCtxInit(PVDIOCTX pIoCtx, PVDISK pDisk, VDIOCTXTXDIR enmTxDir,
719 uint64_t uOffset, size_t cbTransfer, PVDIMAGE pImageStart,
720 PCRTSGBUF pSgBuf, void *pvAllocation,
721 PFNVDIOCTXTRANSFER pfnIoCtxTransfer, uint32_t fFlags)
722{
723 pIoCtx->pDisk = pDisk;
724 pIoCtx->enmTxDir = enmTxDir;
725 pIoCtx->Req.Io.cbTransferLeft = (uint32_t)cbTransfer; Assert((uint32_t)cbTransfer == cbTransfer);
726 pIoCtx->Req.Io.uOffset = uOffset;
727 pIoCtx->Req.Io.cbTransfer = cbTransfer;
728 pIoCtx->Req.Io.pImageStart = pImageStart;
729 pIoCtx->Req.Io.pImageCur = pImageStart;
730 pIoCtx->Req.Io.cbBufClear = 0;
731 pIoCtx->Req.Io.pImageParentOverride = NULL;
732 pIoCtx->Req.Io.uOffsetXferOrig = uOffset;
733 pIoCtx->Req.Io.cbXferOrig = cbTransfer;
734 pIoCtx->cDataTransfersPending = 0;
735 pIoCtx->cMetaTransfersPending = 0;
736 pIoCtx->fComplete = false;
737 pIoCtx->fFlags = fFlags;
738 pIoCtx->pvAllocation = pvAllocation;
739 pIoCtx->pfnIoCtxTransfer = pfnIoCtxTransfer;
740 pIoCtx->pfnIoCtxTransferNext = NULL;
741 pIoCtx->rcReq = VINF_SUCCESS;
742 pIoCtx->pIoCtxParent = NULL;
743
744 /* There is no S/G list for a flush request. */
745 if ( enmTxDir != VDIOCTXTXDIR_FLUSH
746 && enmTxDir != VDIOCTXTXDIR_DISCARD)
747 RTSgBufClone(&pIoCtx->Req.Io.SgBuf, pSgBuf);
748 else
749 memset(&pIoCtx->Req.Io.SgBuf, 0, sizeof(RTSGBUF));
750}
751
752/**
753 * Internal: Tries to read the desired range from the given cache.
754 *
755 * @returns VBox status code.
756 * @retval VERR_VD_BLOCK_FREE if the block is not in the cache.
757 * pcbRead will be set to the number of bytes not in the cache.
758 * Everything thereafter might be in the cache.
759 * @param pCache The cache to read from.
760 * @param uOffset Offset of the virtual disk to read.
761 * @param cbRead How much to read.
762 * @param pIoCtx The I/O context to read into.
763 * @param pcbRead Where to store the number of bytes actually read.
764 * On success this indicates the number of bytes read from the cache.
765 * If VERR_VD_BLOCK_FREE is returned this gives the number of bytes
766 * which are not in the cache.
767 * In both cases everything beyond this value
768 * might or might not be in the cache.
769 */
770static int vdCacheReadHelper(PVDCACHE pCache, uint64_t uOffset,
771 size_t cbRead, PVDIOCTX pIoCtx, size_t *pcbRead)
772{
773 int rc = VINF_SUCCESS;
774
775 LogFlowFunc(("pCache=%#p uOffset=%llu pIoCtx=%p cbRead=%zu pcbRead=%#p\n",
776 pCache, uOffset, pIoCtx, cbRead, pcbRead));
777
778 AssertPtr(pCache);
779 AssertPtr(pcbRead);
780
781 rc = pCache->Backend->pfnRead(pCache->pBackendData, uOffset, cbRead,
782 pIoCtx, pcbRead);
783
784 LogFlowFunc(("returns rc=%Rrc pcbRead=%zu\n", rc, *pcbRead));
785 return rc;
786}
787
788/**
789 * Internal: Writes data for the given block into the cache.
790 *
791 * @returns VBox status code.
792 * @param pCache The cache to write to.
793 * @param uOffset Offset of the virtual disk to write to the cache.
794 * @param cbWrite How much to write.
795 * @param pIoCtx The I/O context to write from.
796 * @param pcbWritten How much data could be written, optional.
797 */
798static int vdCacheWriteHelper(PVDCACHE pCache, uint64_t uOffset, size_t cbWrite,
799 PVDIOCTX pIoCtx, size_t *pcbWritten)
800{
801 int rc = VINF_SUCCESS;
802
803 LogFlowFunc(("pCache=%#p uOffset=%llu pIoCtx=%p cbWrite=%zu pcbWritten=%#p\n",
804 pCache, uOffset, pIoCtx, cbWrite, pcbWritten));
805
806 AssertPtr(pCache);
807 AssertPtr(pIoCtx);
808 Assert(cbWrite > 0);
809
810 if (pcbWritten)
811 rc = pCache->Backend->pfnWrite(pCache->pBackendData, uOffset, cbWrite,
812 pIoCtx, pcbWritten);
813 else
814 {
815 size_t cbWritten = 0;
816
817 do
818 {
819 rc = pCache->Backend->pfnWrite(pCache->pBackendData, uOffset, cbWrite,
820 pIoCtx, &cbWritten);
821 uOffset += cbWritten;
822 cbWrite -= cbWritten;
823 } while ( cbWrite
824 && ( RT_SUCCESS(rc)
825 || rc == VERR_VD_ASYNC_IO_IN_PROGRESS));
826 }
827
828 LogFlowFunc(("returns rc=%Rrc pcbWritten=%zu\n",
829 rc, pcbWritten ? *pcbWritten : cbWrite));
830 return rc;
831}
832
833/**
834 * Creates a new empty discard state.
835 *
836 * @returns Pointer to the new discard state or NULL if out of memory.
837 */
838static PVDDISCARDSTATE vdDiscardStateCreate(void)
839{
840 PVDDISCARDSTATE pDiscard = (PVDDISCARDSTATE)RTMemAllocZ(sizeof(VDDISCARDSTATE));
841
842 if (pDiscard)
843 {
844 RTListInit(&pDiscard->ListLru);
845 pDiscard->pTreeBlocks = (PAVLRU64TREE)RTMemAllocZ(sizeof(AVLRU64TREE));
846 if (!pDiscard->pTreeBlocks)
847 {
848 RTMemFree(pDiscard);
849 pDiscard = NULL;
850 }
851 }
852
853 return pDiscard;
854}
855
856/**
857 * Removes the least recently used blocks from the waiting list until
858 * the new value is reached.
859 *
860 * @returns VBox status code.
861 * @param pDisk VD disk container.
862 * @param pDiscard The discard state.
863 * @param cbDiscardingNew How many bytes should be waiting on success.
864 * The number of bytes waiting can be less.
865 */
866static int vdDiscardRemoveBlocks(PVDISK pDisk, PVDDISCARDSTATE pDiscard, size_t cbDiscardingNew)
867{
868 int rc = VINF_SUCCESS;
869
870 LogFlowFunc(("pDisk=%#p pDiscard=%#p cbDiscardingNew=%zu\n",
871 pDisk, pDiscard, cbDiscardingNew));
872
873 while (pDiscard->cbDiscarding > cbDiscardingNew)
874 {
875 PVDDISCARDBLOCK pBlock = RTListGetLast(&pDiscard->ListLru, VDDISCARDBLOCK, NodeLru);
876
877 Assert(!RTListIsEmpty(&pDiscard->ListLru));
878
879 /* Go over the allocation bitmap and mark all discarded sectors as unused. */
880 uint64_t offStart = pBlock->Core.Key;
881 uint32_t idxStart = 0;
882 size_t cbLeft = pBlock->cbDiscard;
883 bool fAllocated = ASMBitTest(pBlock->pbmAllocated, idxStart);
884 uint32_t cSectors = (uint32_t)(pBlock->cbDiscard / 512);
885
886 while (cbLeft > 0)
887 {
888 int32_t idxEnd;
889 size_t cbThis = cbLeft;
890
891 if (fAllocated)
892 {
893 /* Check for the first unallocated bit. */
894 idxEnd = ASMBitNextClear(pBlock->pbmAllocated, cSectors, idxStart);
895 if (idxEnd != -1)
896 {
897 cbThis = (idxEnd - idxStart) * 512;
898 fAllocated = false;
899 }
900 }
901 else
902 {
903 /* Mark as unused and check for the first set bit. */
904 idxEnd = ASMBitNextSet(pBlock->pbmAllocated, cSectors, idxStart);
905 if (idxEnd != -1)
906 cbThis = (idxEnd - idxStart) * 512;
907
908
909 VDIOCTX IoCtx;
910 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_DISCARD, 0, 0, NULL,
911 NULL, NULL, NULL, VDIOCTX_FLAGS_SYNC);
912 rc = pDisk->pLast->Backend->pfnDiscard(pDisk->pLast->pBackendData,
913 &IoCtx, offStart, cbThis, NULL,
914 NULL, &cbThis, NULL,
915 VD_DISCARD_MARK_UNUSED);
916 if (RT_FAILURE(rc))
917 break;
918
919 fAllocated = true;
920 }
921
922 idxStart = idxEnd;
923 offStart += cbThis;
924 cbLeft -= cbThis;
925 }
926
927 if (RT_FAILURE(rc))
928 break;
929
930 PVDDISCARDBLOCK pBlockRemove = (PVDDISCARDBLOCK)RTAvlrU64RangeRemove(pDiscard->pTreeBlocks, pBlock->Core.Key);
931 Assert(pBlockRemove == pBlock); NOREF(pBlockRemove);
932 RTListNodeRemove(&pBlock->NodeLru);
933
934 pDiscard->cbDiscarding -= pBlock->cbDiscard;
935 RTMemFree(pBlock->pbmAllocated);
936 RTMemFree(pBlock);
937 }
938
939 Assert(RT_FAILURE(rc) || pDiscard->cbDiscarding <= cbDiscardingNew);
940
941 LogFlowFunc(("returns rc=%Rrc\n", rc));
942 return rc;
943}
944
945/**
946 * Destroys the current discard state, writing any waiting blocks to the image.
947 *
948 * @returns VBox status code.
949 * @param pDisk VD disk container.
950 */
951static int vdDiscardStateDestroy(PVDISK pDisk)
952{
953 int rc = VINF_SUCCESS;
954
955 if (pDisk->pDiscard)
956 {
957 rc = vdDiscardRemoveBlocks(pDisk, pDisk->pDiscard, 0 /* Remove all blocks. */);
958 AssertRC(rc);
959 RTMemFree(pDisk->pDiscard->pTreeBlocks);
960 RTMemFree(pDisk->pDiscard);
961 pDisk->pDiscard = NULL;
962 }
963
964 return rc;
965}
966
967/**
968 * Marks the given range as allocated in the image.
969 * Required if there are discards in progress and a write to a block which can get discarded
970 * is written to.
971 *
972 * @returns VBox status code.
973 * @param pDisk VD container data.
974 * @param uOffset First byte to mark as allocated.
975 * @param cbRange Number of bytes to mark as allocated.
976 */
977static int vdDiscardSetRangeAllocated(PVDISK pDisk, uint64_t uOffset, size_t cbRange)
978{
979 PVDDISCARDSTATE pDiscard = pDisk->pDiscard;
980 int rc = VINF_SUCCESS;
981
982 if (pDiscard)
983 {
984 do
985 {
986 size_t cbThisRange = cbRange;
987 PVDDISCARDBLOCK pBlock = (PVDDISCARDBLOCK)RTAvlrU64RangeGet(pDiscard->pTreeBlocks, uOffset);
988
989 if (pBlock)
990 {
991 int32_t idxStart, idxEnd;
992
993 Assert(!(cbThisRange % 512));
994 Assert(!((uOffset - pBlock->Core.Key) % 512));
995
996 cbThisRange = RT_MIN(cbThisRange, pBlock->Core.KeyLast - uOffset + 1);
997
998 idxStart = (uOffset - pBlock->Core.Key) / 512;
999 idxEnd = idxStart + (int32_t)(cbThisRange / 512);
1000 ASMBitSetRange(pBlock->pbmAllocated, idxStart, idxEnd);
1001 }
1002 else
1003 {
1004 pBlock = (PVDDISCARDBLOCK)RTAvlrU64GetBestFit(pDiscard->pTreeBlocks, uOffset, true);
1005 if (pBlock)
1006 cbThisRange = RT_MIN(cbThisRange, pBlock->Core.Key - uOffset);
1007 }
1008
1009 Assert(cbRange >= cbThisRange);
1010
1011 uOffset += cbThisRange;
1012 cbRange -= cbThisRange;
1013 } while (cbRange != 0);
1014 }
1015
1016 return rc;
1017}
1018
1019DECLINLINE(PVDIOCTX) vdIoCtxAlloc(PVDISK pDisk, VDIOCTXTXDIR enmTxDir,
1020 uint64_t uOffset, size_t cbTransfer,
1021 PVDIMAGE pImageStart,PCRTSGBUF pSgBuf,
1022 void *pvAllocation, PFNVDIOCTXTRANSFER pfnIoCtxTransfer,
1023 uint32_t fFlags)
1024{
1025 PVDIOCTX pIoCtx = NULL;
1026
1027 pIoCtx = (PVDIOCTX)RTMemCacheAlloc(pDisk->hMemCacheIoCtx);
1028 if (RT_LIKELY(pIoCtx))
1029 {
1030 vdIoCtxInit(pIoCtx, pDisk, enmTxDir, uOffset, cbTransfer, pImageStart,
1031 pSgBuf, pvAllocation, pfnIoCtxTransfer, fFlags);
1032 }
1033
1034 return pIoCtx;
1035}
1036
1037DECLINLINE(PVDIOCTX) vdIoCtxRootAlloc(PVDISK pDisk, VDIOCTXTXDIR enmTxDir,
1038 uint64_t uOffset, size_t cbTransfer,
1039 PVDIMAGE pImageStart, PCRTSGBUF pSgBuf,
1040 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
1041 void *pvUser1, void *pvUser2,
1042 void *pvAllocation,
1043 PFNVDIOCTXTRANSFER pfnIoCtxTransfer,
1044 uint32_t fFlags)
1045{
1046 PVDIOCTX pIoCtx = vdIoCtxAlloc(pDisk, enmTxDir, uOffset, cbTransfer, pImageStart,
1047 pSgBuf, pvAllocation, pfnIoCtxTransfer, fFlags);
1048
1049 if (RT_LIKELY(pIoCtx))
1050 {
1051 pIoCtx->pIoCtxParent = NULL;
1052 pIoCtx->Type.Root.pfnComplete = pfnComplete;
1053 pIoCtx->Type.Root.pvUser1 = pvUser1;
1054 pIoCtx->Type.Root.pvUser2 = pvUser2;
1055 }
1056
1057 LogFlow(("Allocated root I/O context %#p\n", pIoCtx));
1058 return pIoCtx;
1059}
1060
1061DECLINLINE(void) vdIoCtxDiscardInit(PVDIOCTX pIoCtx, PVDISK pDisk, PCRTRANGE paRanges,
1062 unsigned cRanges, PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
1063 void *pvUser1, void *pvUser2, void *pvAllocation,
1064 PFNVDIOCTXTRANSFER pfnIoCtxTransfer, uint32_t fFlags)
1065{
1066 pIoCtx->pIoCtxNext = NULL;
1067 pIoCtx->pDisk = pDisk;
1068 pIoCtx->enmTxDir = VDIOCTXTXDIR_DISCARD;
1069 pIoCtx->cDataTransfersPending = 0;
1070 pIoCtx->cMetaTransfersPending = 0;
1071 pIoCtx->fComplete = false;
1072 pIoCtx->fFlags = fFlags;
1073 pIoCtx->pvAllocation = pvAllocation;
1074 pIoCtx->pfnIoCtxTransfer = pfnIoCtxTransfer;
1075 pIoCtx->pfnIoCtxTransferNext = NULL;
1076 pIoCtx->rcReq = VINF_SUCCESS;
1077 pIoCtx->Req.Discard.paRanges = paRanges;
1078 pIoCtx->Req.Discard.cRanges = cRanges;
1079 pIoCtx->Req.Discard.idxRange = 0;
1080 pIoCtx->Req.Discard.cbDiscardLeft = 0;
1081 pIoCtx->Req.Discard.offCur = 0;
1082 pIoCtx->Req.Discard.cbThisDiscard = 0;
1083
1084 pIoCtx->pIoCtxParent = NULL;
1085 pIoCtx->Type.Root.pfnComplete = pfnComplete;
1086 pIoCtx->Type.Root.pvUser1 = pvUser1;
1087 pIoCtx->Type.Root.pvUser2 = pvUser2;
1088}
1089
1090DECLINLINE(PVDIOCTX) vdIoCtxDiscardAlloc(PVDISK pDisk, PCRTRANGE paRanges,
1091 unsigned cRanges,
1092 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
1093 void *pvUser1, void *pvUser2,
1094 void *pvAllocation,
1095 PFNVDIOCTXTRANSFER pfnIoCtxTransfer,
1096 uint32_t fFlags)
1097{
1098 PVDIOCTX pIoCtx = NULL;
1099
1100 pIoCtx = (PVDIOCTX)RTMemCacheAlloc(pDisk->hMemCacheIoCtx);
1101 if (RT_LIKELY(pIoCtx))
1102 {
1103 vdIoCtxDiscardInit(pIoCtx, pDisk, paRanges, cRanges, pfnComplete, pvUser1,
1104 pvUser2, pvAllocation, pfnIoCtxTransfer, fFlags);
1105 }
1106
1107 LogFlow(("Allocated discard I/O context %#p\n", pIoCtx));
1108 return pIoCtx;
1109}
1110
1111DECLINLINE(PVDIOCTX) vdIoCtxChildAlloc(PVDISK pDisk, VDIOCTXTXDIR enmTxDir,
1112 uint64_t uOffset, size_t cbTransfer,
1113 PVDIMAGE pImageStart, PCRTSGBUF pSgBuf,
1114 PVDIOCTX pIoCtxParent, size_t cbTransferParent,
1115 size_t cbWriteParent, void *pvAllocation,
1116 PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
1117{
1118 PVDIOCTX pIoCtx = vdIoCtxAlloc(pDisk, enmTxDir, uOffset, cbTransfer, pImageStart,
1119 pSgBuf, pvAllocation, pfnIoCtxTransfer, pIoCtxParent->fFlags & ~VDIOCTX_FLAGS_DONT_FREE);
1120
1121 AssertPtr(pIoCtxParent);
1122 Assert(!pIoCtxParent->pIoCtxParent);
1123
1124 if (RT_LIKELY(pIoCtx))
1125 {
1126 pIoCtx->pIoCtxParent = pIoCtxParent;
1127 pIoCtx->Type.Child.uOffsetSaved = uOffset;
1128 pIoCtx->Type.Child.cbTransferLeftSaved = cbTransfer;
1129 pIoCtx->Type.Child.cbTransferParent = cbTransferParent;
1130 pIoCtx->Type.Child.cbWriteParent = cbWriteParent;
1131 }
1132
1133 LogFlow(("Allocated child I/O context %#p\n", pIoCtx));
1134 return pIoCtx;
1135}
1136
1137DECLINLINE(PVDIOTASK) vdIoTaskUserAlloc(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser, PVDIOCTX pIoCtx, uint32_t cbTransfer)
1138{
1139 PVDIOTASK pIoTask = NULL;
1140
1141 pIoTask = (PVDIOTASK)RTMemCacheAlloc(pIoStorage->pVDIo->pDisk->hMemCacheIoTask);
1142 if (pIoTask)
1143 {
1144 pIoTask->pIoStorage = pIoStorage;
1145 pIoTask->pfnComplete = pfnComplete;
1146 pIoTask->pvUser = pvUser;
1147 pIoTask->fMeta = false;
1148 pIoTask->Type.User.cbTransfer = cbTransfer;
1149 pIoTask->Type.User.pIoCtx = pIoCtx;
1150 }
1151
1152 return pIoTask;
1153}
1154
1155DECLINLINE(PVDIOTASK) vdIoTaskMetaAlloc(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser, PVDMETAXFER pMetaXfer)
1156{
1157 PVDIOTASK pIoTask = NULL;
1158
1159 pIoTask = (PVDIOTASK)RTMemCacheAlloc(pIoStorage->pVDIo->pDisk->hMemCacheIoTask);
1160 if (pIoTask)
1161 {
1162 pIoTask->pIoStorage = pIoStorage;
1163 pIoTask->pfnComplete = pfnComplete;
1164 pIoTask->pvUser = pvUser;
1165 pIoTask->fMeta = true;
1166 pIoTask->Type.Meta.pMetaXfer = pMetaXfer;
1167 }
1168
1169 return pIoTask;
1170}
1171
1172DECLINLINE(void) vdIoCtxFree(PVDISK pDisk, PVDIOCTX pIoCtx)
1173{
1174 Log(("Freeing I/O context %#p\n", pIoCtx));
1175
1176 if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_DONT_FREE))
1177 {
1178 if (pIoCtx->pvAllocation)
1179 RTMemFree(pIoCtx->pvAllocation);
1180#ifdef DEBUG
1181 memset(&pIoCtx->pDisk, 0xff, sizeof(void *));
1182#endif
1183 RTMemCacheFree(pDisk->hMemCacheIoCtx, pIoCtx);
1184 }
1185}
1186
1187DECLINLINE(void) vdIoTaskFree(PVDISK pDisk, PVDIOTASK pIoTask)
1188{
1189#ifdef DEBUG
1190 memset(pIoTask, 0xff, sizeof(VDIOTASK));
1191#endif
1192 RTMemCacheFree(pDisk->hMemCacheIoTask, pIoTask);
1193}
1194
1195DECLINLINE(void) vdIoCtxChildReset(PVDIOCTX pIoCtx)
1196{
1197 AssertPtr(pIoCtx->pIoCtxParent);
1198
1199 RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
1200 pIoCtx->Req.Io.uOffset = pIoCtx->Type.Child.uOffsetSaved;
1201 pIoCtx->Req.Io.cbTransferLeft = (uint32_t)pIoCtx->Type.Child.cbTransferLeftSaved;
1202 Assert((uint32_t)pIoCtx->Type.Child.cbTransferLeftSaved == pIoCtx->Type.Child.cbTransferLeftSaved);
1203}
1204
1205DECLINLINE(PVDMETAXFER) vdMetaXferAlloc(PVDIOSTORAGE pIoStorage, uint64_t uOffset, size_t cb)
1206{
1207 PVDMETAXFER pMetaXfer = (PVDMETAXFER)RTMemAlloc(RT_UOFFSETOF_DYN(VDMETAXFER, abData[cb]));
1208
1209 if (RT_LIKELY(pMetaXfer))
1210 {
1211 pMetaXfer->Core.Key = uOffset;
1212 pMetaXfer->Core.KeyLast = uOffset + cb - 1;
1213 pMetaXfer->fFlags = VDMETAXFER_TXDIR_NONE;
1214 pMetaXfer->cbMeta = cb;
1215 pMetaXfer->pIoStorage = pIoStorage;
1216 pMetaXfer->cRefs = 0;
1217 pMetaXfer->pbDataShw = NULL;
1218 RTListInit(&pMetaXfer->ListIoCtxWaiting);
1219 RTListInit(&pMetaXfer->ListIoCtxShwWrites);
1220 }
1221 return pMetaXfer;
1222}
1223
1224DECLINLINE(void) vdIoCtxAddToWaitingList(volatile PVDIOCTX *ppList, PVDIOCTX pIoCtx)
1225{
1226 /* Put it on the waiting list. */
1227 PVDIOCTX pNext = ASMAtomicUoReadPtrT(ppList, PVDIOCTX);
1228 PVDIOCTX pHeadOld;
1229 pIoCtx->pIoCtxNext = pNext;
1230 while (!ASMAtomicCmpXchgExPtr(ppList, pIoCtx, pNext, &pHeadOld))
1231 {
1232 pNext = pHeadOld;
1233 Assert(pNext != pIoCtx);
1234 pIoCtx->pIoCtxNext = pNext;
1235 ASMNopPause();
1236 }
1237}
1238
1239DECLINLINE(void) vdIoCtxDefer(PVDISK pDisk, PVDIOCTX pIoCtx)
1240{
1241 LogFlowFunc(("Deferring I/O context pIoCtx=%#p\n", pIoCtx));
1242
1243 Assert(!pIoCtx->pIoCtxParent && !(pIoCtx->fFlags & VDIOCTX_FLAGS_BLOCKED));
1244 pIoCtx->fFlags |= VDIOCTX_FLAGS_BLOCKED;
1245 vdIoCtxAddToWaitingList(&pDisk->pIoCtxBlockedHead, pIoCtx);
1246}
1247
1248static size_t vdIoCtxCopy(PVDIOCTX pIoCtxDst, PVDIOCTX pIoCtxSrc, size_t cbData)
1249{
1250 return RTSgBufCopy(&pIoCtxDst->Req.Io.SgBuf, &pIoCtxSrc->Req.Io.SgBuf, cbData);
1251}
1252
1253#if 0 /* unused */
1254static int vdIoCtxCmp(PVDIOCTX pIoCtx1, PVDIOCTX pIoCtx2, size_t cbData)
1255{
1256 return RTSgBufCmp(&pIoCtx1->Req.Io.SgBuf, &pIoCtx2->Req.Io.SgBuf, cbData);
1257}
1258#endif
1259
1260static size_t vdIoCtxCopyTo(PVDIOCTX pIoCtx, const uint8_t *pbData, size_t cbData)
1261{
1262 return RTSgBufCopyFromBuf(&pIoCtx->Req.Io.SgBuf, pbData, cbData);
1263}
1264
1265static size_t vdIoCtxCopyFrom(PVDIOCTX pIoCtx, uint8_t *pbData, size_t cbData)
1266{
1267 return RTSgBufCopyToBuf(&pIoCtx->Req.Io.SgBuf, pbData, cbData);
1268}
1269
1270static size_t vdIoCtxSet(PVDIOCTX pIoCtx, uint8_t ch, size_t cbData)
1271{
1272 return RTSgBufSet(&pIoCtx->Req.Io.SgBuf, ch, cbData);
1273}
1274
1275/**
1276 * Returns whether the given I/O context has completed.
1277 *
1278 * @returns Flag whether the I/O context is complete.
1279 * @param pIoCtx The I/O context to check.
1280 */
1281DECLINLINE(bool) vdIoCtxIsComplete(PVDIOCTX pIoCtx)
1282{
1283 if ( !pIoCtx->cMetaTransfersPending
1284 && !pIoCtx->cDataTransfersPending
1285 && !pIoCtx->pfnIoCtxTransfer)
1286 return true;
1287
1288 /*
1289 * We complete the I/O context in case of an error
1290 * if there is no I/O task pending.
1291 */
1292 if ( RT_FAILURE(pIoCtx->rcReq)
1293 && !pIoCtx->cMetaTransfersPending
1294 && !pIoCtx->cDataTransfersPending)
1295 return true;
1296
1297 return false;
1298}
1299
1300/**
1301 * Returns whether the given I/O context is blocked due to a metadata transfer
1302 * or because the backend blocked it.
1303 *
1304 * @returns Flag whether the I/O context is blocked.
1305 * @param pIoCtx The I/O context to check.
1306 */
1307DECLINLINE(bool) vdIoCtxIsBlocked(PVDIOCTX pIoCtx)
1308{
1309 /* Don't change anything if there is a metadata transfer pending or we are blocked. */
1310 if ( pIoCtx->cMetaTransfersPending
1311 || (pIoCtx->fFlags & VDIOCTX_FLAGS_BLOCKED))
1312 return true;
1313
1314 return false;
1315}
1316
1317/**
1318 * Process the I/O context, core method which assumes that the I/O context
1319 * acquired the lock.
1320 *
1321 * @returns VBox status code.
1322 * @param pIoCtx I/O context to process.
1323 */
1324static int vdIoCtxProcessLocked(PVDIOCTX pIoCtx)
1325{
1326 int rc = VINF_SUCCESS;
1327
1328 VD_IS_LOCKED(pIoCtx->pDisk);
1329
1330 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
1331
1332 if (!vdIoCtxIsComplete(pIoCtx))
1333 {
1334 if (!vdIoCtxIsBlocked(pIoCtx))
1335 {
1336 if (pIoCtx->pfnIoCtxTransfer)
1337 {
1338 /* Call the transfer function advancing to the next while there is no error. */
1339 while ( pIoCtx->pfnIoCtxTransfer
1340 && !pIoCtx->cMetaTransfersPending
1341 && RT_SUCCESS(rc))
1342 {
1343 LogFlowFunc(("calling transfer function %#p\n", pIoCtx->pfnIoCtxTransfer));
1344 rc = pIoCtx->pfnIoCtxTransfer(pIoCtx);
1345
1346 /* Advance to the next part of the transfer if the current one succeeded. */
1347 if (RT_SUCCESS(rc))
1348 {
1349 pIoCtx->pfnIoCtxTransfer = pIoCtx->pfnIoCtxTransferNext;
1350 pIoCtx->pfnIoCtxTransferNext = NULL;
1351 }
1352 }
1353 }
1354
1355 if ( RT_SUCCESS(rc)
1356 && !pIoCtx->cMetaTransfersPending
1357 && !pIoCtx->cDataTransfersPending
1358 && !(pIoCtx->fFlags & VDIOCTX_FLAGS_BLOCKED))
1359 rc = VINF_VD_ASYNC_IO_FINISHED;
1360 else if ( RT_SUCCESS(rc)
1361 || rc == VERR_VD_NOT_ENOUGH_METADATA
1362 || rc == VERR_VD_IOCTX_HALT)
1363 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1364 else if ( RT_FAILURE(rc)
1365 && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
1366 {
1367 ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rc, VINF_SUCCESS);
1368
1369 /*
1370 * The I/O context completed if we have an error and there is no data
1371 * or meta data transfer pending.
1372 */
1373 if ( !pIoCtx->cMetaTransfersPending
1374 && !pIoCtx->cDataTransfersPending)
1375 rc = VINF_VD_ASYNC_IO_FINISHED;
1376 else
1377 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1378 }
1379 }
1380 else
1381 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1382 }
1383 else
1384 rc = VINF_VD_ASYNC_IO_FINISHED;
1385
1386 LogFlowFunc(("pIoCtx=%#p rc=%Rrc cDataTransfersPending=%u cMetaTransfersPending=%u fComplete=%RTbool\n",
1387 pIoCtx, rc, pIoCtx->cDataTransfersPending, pIoCtx->cMetaTransfersPending,
1388 pIoCtx->fComplete));
1389
1390 return rc;
1391}
1392
1393/**
1394 * Processes the list of waiting I/O contexts.
1395 *
1396 * @returns VBox status code, only valid if pIoCtxRc is not NULL, treat as void
1397 * function otherwise.
1398 * @param pDisk The disk structure.
1399 * @param pIoCtxRc An I/O context handle which waits on the list. When processed
1400 * The status code is returned. NULL if there is no I/O context
1401 * to return the status code for.
1402 */
1403static int vdDiskProcessWaitingIoCtx(PVDISK pDisk, PVDIOCTX pIoCtxRc)
1404{
1405 int rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1406
1407 LogFlowFunc(("pDisk=%#p pIoCtxRc=%#p\n", pDisk, pIoCtxRc));
1408
1409 VD_IS_LOCKED(pDisk);
1410
1411 /* Get the waiting list and process it in FIFO order. */
1412 PVDIOCTX pIoCtxHead = ASMAtomicXchgPtrT(&pDisk->pIoCtxHead, NULL, PVDIOCTX);
1413
1414 /* Reverse it. */
1415 PVDIOCTX pCur = pIoCtxHead;
1416 pIoCtxHead = NULL;
1417 while (pCur)
1418 {
1419 PVDIOCTX pInsert = pCur;
1420 pCur = pCur->pIoCtxNext;
1421 pInsert->pIoCtxNext = pIoCtxHead;
1422 pIoCtxHead = pInsert;
1423 }
1424
1425 /* Process now. */
1426 pCur = pIoCtxHead;
1427 while (pCur)
1428 {
1429 int rcTmp;
1430 PVDIOCTX pTmp = pCur;
1431
1432 pCur = pCur->pIoCtxNext;
1433 pTmp->pIoCtxNext = NULL;
1434
1435 /*
1436 * Need to clear the sync flag here if there is a new I/O context
1437 * with it set and the context is not given in pIoCtxRc.
1438 * This happens most likely on a different thread and that one shouldn't
1439 * process the context synchronously.
1440 *
1441 * The thread who issued the context will wait on the event semaphore
1442 * anyway which is signalled when the completion handler is called.
1443 */
1444 if ( pTmp->fFlags & VDIOCTX_FLAGS_SYNC
1445 && pTmp != pIoCtxRc)
1446 pTmp->fFlags &= ~VDIOCTX_FLAGS_SYNC;
1447
1448 rcTmp = vdIoCtxProcessLocked(pTmp);
1449 if (pTmp == pIoCtxRc)
1450 {
1451 if ( rcTmp == VINF_VD_ASYNC_IO_FINISHED
1452 && RT_SUCCESS(pTmp->rcReq)
1453 && pTmp->enmTxDir == VDIOCTXTXDIR_READ)
1454 {
1455 int rc2 = vdFilterChainApplyRead(pDisk, pTmp->Req.Io.uOffsetXferOrig,
1456 pTmp->Req.Io.cbXferOrig, pTmp);
1457 if (RT_FAILURE(rc2))
1458 rcTmp = rc2;
1459 }
1460
1461 /* The given I/O context was processed, pass the return code to the caller. */
1462 if ( rcTmp == VINF_VD_ASYNC_IO_FINISHED
1463 && (pTmp->fFlags & VDIOCTX_FLAGS_SYNC))
1464 rc = pTmp->rcReq;
1465 else
1466 rc = rcTmp;
1467 }
1468 else if ( rcTmp == VINF_VD_ASYNC_IO_FINISHED
1469 && ASMAtomicCmpXchgBool(&pTmp->fComplete, true, false))
1470 {
1471 LogFlowFunc(("Waiting I/O context completed pTmp=%#p\n", pTmp));
1472 vdThreadFinishWrite(pDisk);
1473
1474 bool fFreeCtx = RT_BOOL(!(pTmp->fFlags & VDIOCTX_FLAGS_DONT_FREE));
1475 vdIoCtxRootComplete(pDisk, pTmp);
1476
1477 if (fFreeCtx)
1478 vdIoCtxFree(pDisk, pTmp);
1479 }
1480 }
1481
1482 LogFlowFunc(("returns rc=%Rrc\n", rc));
1483 return rc;
1484}
1485
1486/**
1487 * Processes the list of blocked I/O contexts.
1488 *
1489 * @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,
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 cbSize=%llu cImagesFromRead=%u cImagesToRead=%u fSuppressRedundantIo=%RTbool pIfProgress=%#p pDstIfProgress=%#p\n",
2071 pDiskFrom, pImageFrom, pDiskTo, 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, pDiskTo->pLast, 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 = (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 = (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 if (fFlush)
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, cbWrite, NULL);
4074 if (RT_SUCCESS(rc))
4075 {
4076 Assert(pIoCtx->Req.Io.cbTransferLeft >= cbWrite);
4077 ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, (uint32_t)cbWrite);
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 pImage->VDIo.pInterfaceIo = &pImage->VDIo.VDIfIo;
5543 }
5544
5545 /* Set up the internal I/O interface. */
5546 AssertBreakStmt(!VDIfIoIntGet(pVDIfsImage), rc = VERR_INVALID_PARAMETER);
5547 vdIfIoIntCallbacksSetup(&pImage->VDIo.VDIfIoInt);
5548 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
5549 &pImage->VDIo, sizeof(VDINTERFACEIOINT), &pImage->pVDIfsImage);
5550 AssertRC(rc);
5551
5552 pImage->uOpenFlags = uOpenFlags & (VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_DISCARD | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS);
5553 pImage->VDIo.fIgnoreFlush = (uOpenFlags & VD_OPEN_FLAGS_IGNORE_FLUSH) != 0;
5554 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
5555 uOpenFlags & ~(VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS),
5556 pDisk->pVDIfsDisk,
5557 pImage->pVDIfsImage,
5558 pDisk->enmType,
5559 &pImage->pBackendData);
5560 /*
5561 * If the image is corrupted and there is a repair method try to repair it
5562 * first if it was openend in read-write mode and open again afterwards.
5563 */
5564 if ( RT_UNLIKELY(rc == VERR_VD_IMAGE_CORRUPTED)
5565 && !(uOpenFlags & VD_OPEN_FLAGS_READONLY)
5566 && pImage->Backend->pfnRepair)
5567 {
5568 rc = pImage->Backend->pfnRepair(pszFilename, pDisk->pVDIfsDisk, pImage->pVDIfsImage, 0 /* fFlags */);
5569 if (RT_SUCCESS(rc))
5570 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
5571 uOpenFlags & ~(VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS),
5572 pDisk->pVDIfsDisk,
5573 pImage->pVDIfsImage,
5574 pDisk->enmType,
5575 &pImage->pBackendData);
5576 else
5577 {
5578 rc = vdError(pDisk, rc, RT_SRC_POS,
5579 N_("VD: error %Rrc repairing corrupted image file '%s'"), rc, pszFilename);
5580 break;
5581 }
5582 }
5583 else if (RT_UNLIKELY(rc == VERR_VD_IMAGE_CORRUPTED))
5584 {
5585 rc = vdError(pDisk, rc, RT_SRC_POS,
5586 N_("VD: Image file '%s' is corrupted and can't be opened"), pszFilename);
5587 break;
5588 }
5589
5590 /* If the open in read-write mode failed, retry in read-only mode. */
5591 if (RT_FAILURE(rc))
5592 {
5593 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
5594 && ( rc == VERR_ACCESS_DENIED
5595 || rc == VERR_PERMISSION_DENIED
5596 || rc == VERR_WRITE_PROTECT
5597 || rc == VERR_SHARING_VIOLATION
5598 || rc == VERR_FILE_LOCK_FAILED))
5599 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
5600 (uOpenFlags & ~(VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS))
5601 | VD_OPEN_FLAGS_READONLY,
5602 pDisk->pVDIfsDisk,
5603 pImage->pVDIfsImage,
5604 pDisk->enmType,
5605 &pImage->pBackendData);
5606 if (RT_FAILURE(rc))
5607 {
5608 rc = vdError(pDisk, rc, RT_SRC_POS,
5609 N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
5610 break;
5611 }
5612 }
5613
5614 /* Lock disk for writing, as we modify pDisk information below. */
5615 rc2 = vdThreadStartWrite(pDisk);
5616 AssertRC(rc2);
5617 fLockWrite = true;
5618
5619 pImage->VDIo.pBackendData = pImage->pBackendData;
5620
5621 /* Check image type. As the image itself has only partial knowledge
5622 * whether it's a base image or not, this info is derived here. The
5623 * base image can be fixed or normal, all others must be normal or
5624 * diff images. Some image formats don't distinguish between normal
5625 * and diff images, so this must be corrected here. */
5626 unsigned uImageFlags;
5627 uImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pBackendData);
5628 if (RT_FAILURE(rc))
5629 uImageFlags = VD_IMAGE_FLAGS_NONE;
5630 if ( RT_SUCCESS(rc)
5631 && !(uOpenFlags & VD_OPEN_FLAGS_INFO))
5632 {
5633 if ( pDisk->cImages == 0
5634 && (uImageFlags & VD_IMAGE_FLAGS_DIFF))
5635 {
5636 rc = VERR_VD_INVALID_TYPE;
5637 break;
5638 }
5639 else if (pDisk->cImages != 0)
5640 {
5641 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
5642 {
5643 rc = VERR_VD_INVALID_TYPE;
5644 break;
5645 }
5646 else
5647 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
5648 }
5649 }
5650
5651 /* Ensure we always get correct diff information, even if the backend
5652 * doesn't actually have a stored flag for this. It must not return
5653 * bogus information for the parent UUID if it is not a diff image. */
5654 RTUUID parentUuid;
5655 RTUuidClear(&parentUuid);
5656 rc2 = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, &parentUuid);
5657 if (RT_SUCCESS(rc2) && !RTUuidIsNull(&parentUuid))
5658 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
5659
5660 pImage->uImageFlags = uImageFlags;
5661
5662 /* Force sane optimization settings. It's not worth avoiding writes
5663 * to fixed size images. The overhead would have almost no payback. */
5664 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
5665 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
5666
5667 /** @todo optionally check UUIDs */
5668
5669 /* Cache disk information. */
5670 pDisk->cbSize = vdImageGetSize(pImage);
5671
5672 /* Cache PCHS geometry. */
5673 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
5674 &pDisk->PCHSGeometry);
5675 if (RT_FAILURE(rc2))
5676 {
5677 pDisk->PCHSGeometry.cCylinders = 0;
5678 pDisk->PCHSGeometry.cHeads = 0;
5679 pDisk->PCHSGeometry.cSectors = 0;
5680 }
5681 else
5682 {
5683 /* Make sure the PCHS geometry is properly clipped. */
5684 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
5685 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
5686 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
5687 }
5688
5689 /* Cache LCHS geometry. */
5690 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
5691 &pDisk->LCHSGeometry);
5692 if (RT_FAILURE(rc2))
5693 {
5694 pDisk->LCHSGeometry.cCylinders = 0;
5695 pDisk->LCHSGeometry.cHeads = 0;
5696 pDisk->LCHSGeometry.cSectors = 0;
5697 }
5698 else
5699 {
5700 /* Make sure the LCHS geometry is properly clipped. */
5701 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
5702 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
5703 }
5704
5705 if (pDisk->cImages != 0)
5706 {
5707 /* Switch previous image to read-only mode. */
5708 unsigned uOpenFlagsPrevImg;
5709 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
5710 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
5711 {
5712 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
5713 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
5714 }
5715 }
5716
5717 if (RT_SUCCESS(rc))
5718 {
5719 /* Image successfully opened, make it the last image. */
5720 vdAddImageToList(pDisk, pImage);
5721 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
5722 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
5723 }
5724 else
5725 {
5726 /* Error detected, but image opened. Close image. */
5727 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
5728 AssertRC(rc2);
5729 pImage->pBackendData = NULL;
5730 }
5731 } while (0);
5732
5733 if (RT_UNLIKELY(fLockWrite))
5734 {
5735 rc2 = vdThreadFinishWrite(pDisk);
5736 AssertRC(rc2);
5737 }
5738
5739 if (RT_FAILURE(rc))
5740 {
5741 if (pImage)
5742 {
5743 if (pImage->pszFilename)
5744 RTStrFree(pImage->pszFilename);
5745 RTMemFree(pImage);
5746 }
5747 }
5748
5749 LogFlowFunc(("returns %Rrc\n", rc));
5750 return rc;
5751}
5752
5753
5754VBOXDDU_DECL(int) VDCacheOpen(PVDISK pDisk, const char *pszBackend,
5755 const char *pszFilename, unsigned uOpenFlags,
5756 PVDINTERFACE pVDIfsCache)
5757{
5758 int rc = VINF_SUCCESS;
5759 int rc2;
5760 bool fLockWrite = false;
5761 PVDCACHE pCache = NULL;
5762
5763 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsCache=%#p\n",
5764 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsCache));
5765
5766 /* sanity check */
5767 AssertPtrReturn(pDisk, VERR_INVALID_PARAMETER);
5768 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5769
5770 /* Check arguments. */
5771 AssertPtrReturn(pszBackend, VERR_INVALID_POINTER);
5772 AssertReturn(*pszBackend != '\0', VERR_INVALID_PARAMETER);
5773 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
5774 AssertReturn(*pszFilename != '\0', VERR_INVALID_PARAMETER);
5775 AssertMsgReturn((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0, ("uOpenFlags=%#x\n", uOpenFlags),
5776 VERR_INVALID_PARAMETER);
5777
5778 do
5779 {
5780 /* Set up image descriptor. */
5781 pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
5782 if (!pCache)
5783 {
5784 rc = VERR_NO_MEMORY;
5785 break;
5786 }
5787 pCache->pszFilename = RTStrDup(pszFilename);
5788 if (!pCache->pszFilename)
5789 {
5790 rc = VERR_NO_MEMORY;
5791 break;
5792 }
5793
5794 pCache->VDIo.pDisk = pDisk;
5795 pCache->pVDIfsCache = pVDIfsCache;
5796
5797 rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
5798 if (RT_FAILURE(rc))
5799 break;
5800 if (!pCache->Backend)
5801 {
5802 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
5803 N_("VD: unknown backend name '%s'"), pszBackend);
5804 break;
5805 }
5806
5807 /* Set up the I/O interface. */
5808 pCache->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsCache);
5809 if (!pCache->VDIo.pInterfaceIo)
5810 {
5811 vdIfIoFallbackCallbacksSetup(&pCache->VDIo.VDIfIo);
5812 rc = VDInterfaceAdd(&pCache->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
5813 pDisk, sizeof(VDINTERFACEIO), &pVDIfsCache);
5814 pCache->VDIo.pInterfaceIo = &pCache->VDIo.VDIfIo;
5815 }
5816
5817 /* Set up the internal I/O interface. */
5818 AssertBreakStmt(!VDIfIoIntGet(pVDIfsCache), rc = VERR_INVALID_PARAMETER);
5819 vdIfIoIntCallbacksSetup(&pCache->VDIo.VDIfIoInt);
5820 rc = VDInterfaceAdd(&pCache->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
5821 &pCache->VDIo, sizeof(VDINTERFACEIOINT), &pCache->pVDIfsCache);
5822 AssertRC(rc);
5823
5824 pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
5825 rc = pCache->Backend->pfnOpen(pCache->pszFilename,
5826 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
5827 pDisk->pVDIfsDisk,
5828 pCache->pVDIfsCache,
5829 &pCache->pBackendData);
5830 /* If the open in read-write mode failed, retry in read-only mode. */
5831 if (RT_FAILURE(rc))
5832 {
5833 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
5834 && ( rc == VERR_ACCESS_DENIED
5835 || rc == VERR_PERMISSION_DENIED
5836 || rc == VERR_WRITE_PROTECT
5837 || rc == VERR_SHARING_VIOLATION
5838 || rc == VERR_FILE_LOCK_FAILED))
5839 rc = pCache->Backend->pfnOpen(pCache->pszFilename,
5840 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
5841 | VD_OPEN_FLAGS_READONLY,
5842 pDisk->pVDIfsDisk,
5843 pCache->pVDIfsCache,
5844 &pCache->pBackendData);
5845 if (RT_FAILURE(rc))
5846 {
5847 rc = vdError(pDisk, rc, RT_SRC_POS,
5848 N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
5849 break;
5850 }
5851 }
5852
5853 /* Lock disk for writing, as we modify pDisk information below. */
5854 rc2 = vdThreadStartWrite(pDisk);
5855 AssertRC(rc2);
5856 fLockWrite = true;
5857
5858 /*
5859 * Check that the modification UUID of the cache and last image
5860 * match. If not the image was modified in-between without the cache.
5861 * The cache might contain stale data.
5862 */
5863 RTUUID UuidImage, UuidCache;
5864
5865 rc = pCache->Backend->pfnGetModificationUuid(pCache->pBackendData,
5866 &UuidCache);
5867 if (RT_SUCCESS(rc))
5868 {
5869 rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
5870 &UuidImage);
5871 if (RT_SUCCESS(rc))
5872 {
5873 if (RTUuidCompare(&UuidImage, &UuidCache))
5874 rc = VERR_VD_CACHE_NOT_UP_TO_DATE;
5875 }
5876 }
5877
5878 /*
5879 * We assume that the user knows what he is doing if one of the images
5880 * doesn't support the modification uuid.
5881 */
5882 if (rc == VERR_NOT_SUPPORTED)
5883 rc = VINF_SUCCESS;
5884
5885 if (RT_SUCCESS(rc))
5886 {
5887 /* Cache successfully opened, make it the current one. */
5888 if (!pDisk->pCache)
5889 pDisk->pCache = pCache;
5890 else
5891 rc = VERR_VD_CACHE_ALREADY_EXISTS;
5892 }
5893
5894 if (RT_FAILURE(rc))
5895 {
5896 /* Error detected, but image opened. Close image. */
5897 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
5898 AssertRC(rc2);
5899 pCache->pBackendData = NULL;
5900 }
5901 } while (0);
5902
5903 if (RT_UNLIKELY(fLockWrite))
5904 {
5905 rc2 = vdThreadFinishWrite(pDisk);
5906 AssertRC(rc2);
5907 }
5908
5909 if (RT_FAILURE(rc))
5910 {
5911 if (pCache)
5912 {
5913 if (pCache->pszFilename)
5914 RTStrFree(pCache->pszFilename);
5915 RTMemFree(pCache);
5916 }
5917 }
5918
5919 LogFlowFunc(("returns %Rrc\n", rc));
5920 return rc;
5921}
5922
5923
5924VBOXDDU_DECL(int) VDFilterAdd(PVDISK pDisk, const char *pszFilter, uint32_t fFlags,
5925 PVDINTERFACE pVDIfsFilter)
5926{
5927 int rc = VINF_SUCCESS;
5928 int rc2;
5929 bool fLockWrite = false;
5930 PVDFILTER pFilter = NULL;
5931
5932 LogFlowFunc(("pDisk=%#p pszFilter=\"%s\" pVDIfsFilter=%#p\n",
5933 pDisk, pszFilter, pVDIfsFilter));
5934
5935 /* sanity check */
5936 AssertPtrReturn(pDisk, VERR_INVALID_PARAMETER);
5937 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5938
5939 /* Check arguments. */
5940 AssertPtrReturn(pszFilter, VERR_INVALID_POINTER);
5941 AssertReturn(*pszFilter != '\0', VERR_INVALID_PARAMETER);
5942 AssertMsgReturn(!(fFlags & ~VD_FILTER_FLAGS_MASK), ("Invalid flags set (fFlags=%#x)\n", fFlags),
5943 VERR_INVALID_PARAMETER);
5944
5945 do
5946 {
5947 /* Set up image descriptor. */
5948 pFilter = (PVDFILTER)RTMemAllocZ(sizeof(VDFILTER));
5949 if (!pFilter)
5950 {
5951 rc = VERR_NO_MEMORY;
5952 break;
5953 }
5954
5955 rc = vdFindFilterBackend(pszFilter, &pFilter->pBackend);
5956 if (RT_FAILURE(rc))
5957 break;
5958 if (!pFilter->pBackend)
5959 {
5960 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
5961 N_("VD: unknown filter backend name '%s'"), pszFilter);
5962 break;
5963 }
5964
5965 pFilter->VDIo.pDisk = pDisk;
5966 pFilter->pVDIfsFilter = pVDIfsFilter;
5967
5968 /* Set up the internal I/O interface. */
5969 AssertBreakStmt(!VDIfIoIntGet(pVDIfsFilter), rc = VERR_INVALID_PARAMETER);
5970 vdIfIoIntCallbacksSetup(&pFilter->VDIo.VDIfIoInt);
5971 rc = VDInterfaceAdd(&pFilter->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
5972 &pFilter->VDIo, sizeof(VDINTERFACEIOINT), &pFilter->pVDIfsFilter);
5973 AssertRC(rc);
5974
5975 rc = pFilter->pBackend->pfnCreate(pDisk->pVDIfsDisk, fFlags & VD_FILTER_FLAGS_INFO,
5976 pFilter->pVDIfsFilter, &pFilter->pvBackendData);
5977 if (RT_FAILURE(rc))
5978 break;
5979
5980 /* Lock disk for writing, as we modify pDisk information below. */
5981 rc2 = vdThreadStartWrite(pDisk);
5982 AssertRC(rc2);
5983 fLockWrite = true;
5984
5985 /* Add filter to chains. */
5986 if (fFlags & VD_FILTER_FLAGS_WRITE)
5987 {
5988 RTListAppend(&pDisk->ListFilterChainWrite, &pFilter->ListNodeChainWrite);
5989 vdFilterRetain(pFilter);
5990 }
5991
5992 if (fFlags & VD_FILTER_FLAGS_READ)
5993 {
5994 RTListAppend(&pDisk->ListFilterChainRead, &pFilter->ListNodeChainRead);
5995 vdFilterRetain(pFilter);
5996 }
5997 } while (0);
5998
5999 if (RT_UNLIKELY(fLockWrite))
6000 {
6001 rc2 = vdThreadFinishWrite(pDisk);
6002 AssertRC(rc2);
6003 }
6004
6005 if (RT_FAILURE(rc))
6006 {
6007 if (pFilter)
6008 RTMemFree(pFilter);
6009 }
6010
6011 LogFlowFunc(("returns %Rrc\n", rc));
6012 return rc;
6013}
6014
6015
6016VBOXDDU_DECL(int) VDCreateBase(PVDISK pDisk, const char *pszBackend,
6017 const char *pszFilename, uint64_t cbSize,
6018 unsigned uImageFlags, const char *pszComment,
6019 PCVDGEOMETRY pPCHSGeometry,
6020 PCVDGEOMETRY pLCHSGeometry,
6021 PCRTUUID pUuid, unsigned uOpenFlags,
6022 PVDINTERFACE pVDIfsImage,
6023 PVDINTERFACE pVDIfsOperation)
6024{
6025 int rc = VINF_SUCCESS;
6026 int rc2;
6027 bool fLockWrite = false, fLockRead = false;
6028 PVDIMAGE pImage = NULL;
6029 RTUUID uuid;
6030
6031 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",
6032 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment,
6033 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
6034 pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
6035 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, pUuid,
6036 uOpenFlags, pVDIfsImage, pVDIfsOperation));
6037
6038 /* sanity check */
6039 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
6040 AssertMsgReturn(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature),
6041 VERR_INVALID_MAGIC);
6042
6043 /* Check arguments. */
6044 AssertPtrReturn(pszBackend, VERR_INVALID_POINTER);
6045 AssertReturn(*pszBackend != '\0', VERR_INVALID_PARAMETER);
6046 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
6047 AssertReturn(*pszFilename != '\0', VERR_INVALID_PARAMETER);
6048 AssertMsgReturn(cbSize || (uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK), ("cbSize=%llu\n", cbSize),
6049 VERR_INVALID_PARAMETER);
6050 if (cbSize % 512 && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK))
6051 {
6052 rc = vdError(pDisk, VERR_VD_INVALID_SIZE, RT_SRC_POS,
6053 N_("VD: The given disk size %llu is not aligned on a sector boundary (512 bytes)"), cbSize);
6054 LogFlowFunc(("returns %Rrc\n", rc));
6055 return rc;
6056 }
6057 AssertMsgReturn( ((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0)
6058 || ((uImageFlags & (VD_IMAGE_FLAGS_FIXED | VD_IMAGE_FLAGS_DIFF)) != VD_IMAGE_FLAGS_FIXED),
6059 ("uImageFlags=%#x\n", uImageFlags),
6060 VERR_INVALID_PARAMETER);
6061 AssertMsgReturn( !(uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK)
6062 || !(uImageFlags & ~(VD_VMDK_IMAGE_FLAGS_RAWDISK | VD_IMAGE_FLAGS_FIXED)),
6063 ("uImageFlags=%#x\n", uImageFlags),
6064 VERR_INVALID_PARAMETER);
6065 /* The PCHS geometry fields may be 0 to leave it for later. */
6066 AssertPtrReturn(pPCHSGeometry, VERR_INVALID_PARAMETER);
6067 AssertMsgReturn( pPCHSGeometry->cHeads <= 16
6068 && pPCHSGeometry->cSectors <= 63,
6069 ("PCHS=%u/%u/%u\n", pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors),
6070 VERR_INVALID_PARAMETER);
6071 /* The LCHS geometry fields may be 0 to leave it to later autodetection. */
6072 AssertPtrReturn(pLCHSGeometry, VERR_INVALID_POINTER);
6073 AssertMsgReturn( pLCHSGeometry->cHeads <= 255
6074 && pLCHSGeometry->cSectors <= 63,
6075 ("LCHS=%u/%u/%u\n", pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors),
6076 VERR_INVALID_PARAMETER);
6077 /* The UUID may be NULL. */
6078 AssertPtrNullReturn(pUuid, VERR_INVALID_POINTER);
6079 AssertMsgReturn((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0, ("uOpenFlags=%#x\n", uOpenFlags),
6080 VERR_INVALID_PARAMETER);
6081
6082 AssertPtrNullReturn(pVDIfsOperation, VERR_INVALID_PARAMETER);
6083 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
6084
6085 do
6086 {
6087 /* Check state. Needs a temporary read lock. Holding the write lock
6088 * all the time would be blocking other activities for too long. */
6089 rc2 = vdThreadStartRead(pDisk);
6090 AssertRC(rc2);
6091 fLockRead = true;
6092 AssertMsgBreakStmt(pDisk->cImages == 0,
6093 ("Create base image cannot be done with other images open\n"),
6094 rc = VERR_VD_INVALID_STATE);
6095 rc2 = vdThreadFinishRead(pDisk);
6096 AssertRC(rc2);
6097 fLockRead = false;
6098
6099 /* Set up image descriptor. */
6100 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
6101 if (!pImage)
6102 {
6103 rc = VERR_NO_MEMORY;
6104 break;
6105 }
6106 pImage->pszFilename = RTStrDup(pszFilename);
6107 if (!pImage->pszFilename)
6108 {
6109 rc = VERR_NO_MEMORY;
6110 break;
6111 }
6112 pImage->cbImage = VD_IMAGE_SIZE_UNINITIALIZED;
6113 pImage->VDIo.pDisk = pDisk;
6114 pImage->pVDIfsImage = pVDIfsImage;
6115
6116 /* Set up the I/O interface. */
6117 pImage->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsImage);
6118 if (!pImage->VDIo.pInterfaceIo)
6119 {
6120 vdIfIoFallbackCallbacksSetup(&pImage->VDIo.VDIfIo);
6121 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
6122 pDisk, sizeof(VDINTERFACEIO), &pVDIfsImage);
6123 pImage->VDIo.pInterfaceIo = &pImage->VDIo.VDIfIo;
6124 }
6125
6126 /* Set up the internal I/O interface. */
6127 AssertBreakStmt(!VDIfIoIntGet(pVDIfsImage), rc = VERR_INVALID_PARAMETER);
6128 vdIfIoIntCallbacksSetup(&pImage->VDIo.VDIfIoInt);
6129 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
6130 &pImage->VDIo, sizeof(VDINTERFACEIOINT), &pImage->pVDIfsImage);
6131 AssertRC(rc);
6132
6133 rc = vdFindImageBackend(pszBackend, &pImage->Backend);
6134 if (RT_FAILURE(rc))
6135 break;
6136 if (!pImage->Backend)
6137 {
6138 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6139 N_("VD: unknown backend name '%s'"), pszBackend);
6140 break;
6141 }
6142 if (!(pImage->Backend->uBackendCaps & ( VD_CAP_CREATE_FIXED
6143 | VD_CAP_CREATE_DYNAMIC)))
6144 {
6145 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6146 N_("VD: backend '%s' cannot create base images"), pszBackend);
6147 break;
6148 }
6149 if ( ( (uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G)
6150 && !(pImage->Backend->uBackendCaps & VD_CAP_CREATE_SPLIT_2G))
6151 || ( (uImageFlags & ( VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED
6152 | VD_VMDK_IMAGE_FLAGS_RAWDISK))
6153 && RTStrICmp(pszBackend, "VMDK")))
6154 {
6155 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6156 N_("VD: backend '%s' does not support the selected image variant"), pszBackend);
6157 break;
6158 }
6159
6160 /* Create UUID if the caller didn't specify one. */
6161 if (!pUuid)
6162 {
6163 rc = RTUuidCreate(&uuid);
6164 if (RT_FAILURE(rc))
6165 {
6166 rc = vdError(pDisk, rc, RT_SRC_POS,
6167 N_("VD: cannot generate UUID for image '%s'"),
6168 pszFilename);
6169 break;
6170 }
6171 pUuid = &uuid;
6172 }
6173
6174 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
6175 uImageFlags &= ~VD_IMAGE_FLAGS_DIFF;
6176 pImage->VDIo.fIgnoreFlush = (uOpenFlags & VD_OPEN_FLAGS_IGNORE_FLUSH) != 0;
6177 rc = pImage->Backend->pfnCreate(pImage->pszFilename, cbSize,
6178 uImageFlags, pszComment, pPCHSGeometry,
6179 pLCHSGeometry, pUuid,
6180 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
6181 0, 99,
6182 pDisk->pVDIfsDisk,
6183 pImage->pVDIfsImage,
6184 pVDIfsOperation,
6185 pDisk->enmType,
6186 &pImage->pBackendData);
6187
6188 if (RT_SUCCESS(rc))
6189 {
6190 pImage->VDIo.pBackendData = pImage->pBackendData;
6191 pImage->uImageFlags = uImageFlags;
6192
6193 /* Force sane optimization settings. It's not worth avoiding writes
6194 * to fixed size images. The overhead would have almost no payback. */
6195 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
6196 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
6197
6198 /* Lock disk for writing, as we modify pDisk information below. */
6199 rc2 = vdThreadStartWrite(pDisk);
6200 AssertRC(rc2);
6201 fLockWrite = true;
6202
6203 /** @todo optionally check UUIDs */
6204
6205 /* Re-check state, as the lock wasn't held and another image
6206 * creation call could have been done by another thread. */
6207 AssertMsgStmt(pDisk->cImages == 0,
6208 ("Create base image cannot be done with other images open\n"),
6209 rc = VERR_VD_INVALID_STATE);
6210 }
6211
6212 if (RT_SUCCESS(rc))
6213 {
6214 /* Cache disk information. */
6215 pDisk->cbSize = vdImageGetSize(pImage);
6216
6217 /* Cache PCHS geometry. */
6218 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
6219 &pDisk->PCHSGeometry);
6220 if (RT_FAILURE(rc2))
6221 {
6222 pDisk->PCHSGeometry.cCylinders = 0;
6223 pDisk->PCHSGeometry.cHeads = 0;
6224 pDisk->PCHSGeometry.cSectors = 0;
6225 }
6226 else
6227 {
6228 /* Make sure the CHS geometry is properly clipped. */
6229 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
6230 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
6231 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
6232 }
6233
6234 /* Cache LCHS geometry. */
6235 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
6236 &pDisk->LCHSGeometry);
6237 if (RT_FAILURE(rc2))
6238 {
6239 pDisk->LCHSGeometry.cCylinders = 0;
6240 pDisk->LCHSGeometry.cHeads = 0;
6241 pDisk->LCHSGeometry.cSectors = 0;
6242 }
6243 else
6244 {
6245 /* Make sure the CHS geometry is properly clipped. */
6246 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
6247 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
6248 }
6249
6250 /* Image successfully opened, make it the last image. */
6251 vdAddImageToList(pDisk, pImage);
6252 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
6253 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
6254 }
6255 else
6256 {
6257 /* Error detected, image may or may not be opened. Close and delete
6258 * image if it was opened. */
6259 if (pImage->pBackendData)
6260 {
6261 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
6262 AssertRC(rc2);
6263 pImage->pBackendData = NULL;
6264 }
6265 }
6266 } while (0);
6267
6268 if (RT_UNLIKELY(fLockWrite))
6269 {
6270 rc2 = vdThreadFinishWrite(pDisk);
6271 AssertRC(rc2);
6272 }
6273 else if (RT_UNLIKELY(fLockRead))
6274 {
6275 rc2 = vdThreadFinishRead(pDisk);
6276 AssertRC(rc2);
6277 }
6278
6279 if (RT_FAILURE(rc))
6280 {
6281 if (pImage)
6282 {
6283 if (pImage->pszFilename)
6284 RTStrFree(pImage->pszFilename);
6285 RTMemFree(pImage);
6286 }
6287 }
6288
6289 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
6290 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
6291
6292 LogFlowFunc(("returns %Rrc\n", rc));
6293 return rc;
6294}
6295
6296
6297VBOXDDU_DECL(int) VDCreateDiff(PVDISK pDisk, const char *pszBackend,
6298 const char *pszFilename, unsigned uImageFlags,
6299 const char *pszComment, PCRTUUID pUuid,
6300 PCRTUUID pParentUuid, unsigned uOpenFlags,
6301 PVDINTERFACE pVDIfsImage,
6302 PVDINTERFACE pVDIfsOperation)
6303{
6304 int rc = VINF_SUCCESS;
6305 int rc2;
6306 bool fLockWrite = false, fLockRead = false;
6307 PVDIMAGE pImage = NULL;
6308 RTUUID uuid;
6309
6310 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
6311 pDisk, pszBackend, pszFilename, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsImage, pVDIfsOperation));
6312
6313 /* sanity check */
6314 AssertPtrReturn(pDisk, VERR_INVALID_PARAMETER);
6315 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6316
6317 /* Check arguments. */
6318 AssertPtrReturn(pszBackend, VERR_INVALID_POINTER);
6319 AssertReturn(*pszBackend != '\0', VERR_INVALID_PARAMETER);
6320 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
6321 AssertReturn(*pszFilename != '\0', VERR_INVALID_PARAMETER);
6322 AssertMsgReturn((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0, ("uImageFlags=%#x\n", uImageFlags),
6323 VERR_INVALID_PARAMETER);
6324 /* The UUID may be NULL. */
6325 AssertPtrNullReturn(pUuid, VERR_INVALID_POINTER);
6326 /* The parent UUID may be NULL. */
6327 AssertPtrNullReturn(pParentUuid, VERR_INVALID_POINTER);
6328 AssertMsgReturn((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0, ("uOpenFlags=%#x\n", uOpenFlags),
6329 VERR_INVALID_PARAMETER);
6330
6331 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
6332 do
6333 {
6334 /* Check state. Needs a temporary read lock. Holding the write lock
6335 * all the time would be blocking other activities for too long. */
6336 rc2 = vdThreadStartRead(pDisk);
6337 AssertRC(rc2);
6338 fLockRead = true;
6339 AssertMsgBreakStmt(pDisk->cImages != 0,
6340 ("Create diff image cannot be done without other images open\n"),
6341 rc = VERR_VD_INVALID_STATE);
6342 rc2 = vdThreadFinishRead(pDisk);
6343 AssertRC(rc2);
6344 fLockRead = false;
6345
6346 /*
6347 * Destroy the current discard state first which might still have pending blocks
6348 * for the currently opened image which will be switched to readonly mode.
6349 */
6350 /* Lock disk for writing, as we modify pDisk information below. */
6351 rc2 = vdThreadStartWrite(pDisk);
6352 AssertRC(rc2);
6353 fLockWrite = true;
6354 rc = vdDiscardStateDestroy(pDisk);
6355 if (RT_FAILURE(rc))
6356 break;
6357 rc2 = vdThreadFinishWrite(pDisk);
6358 AssertRC(rc2);
6359 fLockWrite = false;
6360
6361 /* Set up image descriptor. */
6362 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
6363 if (!pImage)
6364 {
6365 rc = VERR_NO_MEMORY;
6366 break;
6367 }
6368 pImage->pszFilename = RTStrDup(pszFilename);
6369 if (!pImage->pszFilename)
6370 {
6371 rc = VERR_NO_MEMORY;
6372 break;
6373 }
6374
6375 rc = vdFindImageBackend(pszBackend, &pImage->Backend);
6376 if (RT_FAILURE(rc))
6377 break;
6378 if (!pImage->Backend)
6379 {
6380 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6381 N_("VD: unknown backend name '%s'"), pszBackend);
6382 break;
6383 }
6384 if ( !(pImage->Backend->uBackendCaps & VD_CAP_DIFF)
6385 || !(pImage->Backend->uBackendCaps & ( VD_CAP_CREATE_FIXED
6386 | VD_CAP_CREATE_DYNAMIC)))
6387 {
6388 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6389 N_("VD: backend '%s' cannot create diff images"), pszBackend);
6390 break;
6391 }
6392
6393 pImage->cbImage = VD_IMAGE_SIZE_UNINITIALIZED;
6394 pImage->VDIo.pDisk = pDisk;
6395 pImage->pVDIfsImage = pVDIfsImage;
6396
6397 /* Set up the I/O interface. */
6398 pImage->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsImage);
6399 if (!pImage->VDIo.pInterfaceIo)
6400 {
6401 vdIfIoFallbackCallbacksSetup(&pImage->VDIo.VDIfIo);
6402 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
6403 pDisk, sizeof(VDINTERFACEIO), &pVDIfsImage);
6404 pImage->VDIo.pInterfaceIo = &pImage->VDIo.VDIfIo;
6405 }
6406
6407 /* Set up the internal I/O interface. */
6408 AssertBreakStmt(!VDIfIoIntGet(pVDIfsImage), rc = VERR_INVALID_PARAMETER);
6409 vdIfIoIntCallbacksSetup(&pImage->VDIo.VDIfIoInt);
6410 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
6411 &pImage->VDIo, sizeof(VDINTERFACEIOINT), &pImage->pVDIfsImage);
6412 AssertRC(rc);
6413
6414 /* Create UUID if the caller didn't specify one. */
6415 if (!pUuid)
6416 {
6417 rc = RTUuidCreate(&uuid);
6418 if (RT_FAILURE(rc))
6419 {
6420 rc = vdError(pDisk, rc, RT_SRC_POS,
6421 N_("VD: cannot generate UUID for image '%s'"),
6422 pszFilename);
6423 break;
6424 }
6425 pUuid = &uuid;
6426 }
6427
6428 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
6429 pImage->VDIo.fIgnoreFlush = (uOpenFlags & VD_OPEN_FLAGS_IGNORE_FLUSH) != 0;
6430 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
6431 rc = pImage->Backend->pfnCreate(pImage->pszFilename, pDisk->cbSize,
6432 uImageFlags | VD_IMAGE_FLAGS_DIFF,
6433 pszComment, &pDisk->PCHSGeometry,
6434 &pDisk->LCHSGeometry, pUuid,
6435 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
6436 0, 99,
6437 pDisk->pVDIfsDisk,
6438 pImage->pVDIfsImage,
6439 pVDIfsOperation,
6440 pDisk->enmType,
6441 &pImage->pBackendData);
6442
6443 if (RT_SUCCESS(rc))
6444 {
6445 pImage->VDIo.pBackendData = pImage->pBackendData;
6446 pImage->uImageFlags = uImageFlags;
6447
6448 /* Lock disk for writing, as we modify pDisk information below. */
6449 rc2 = vdThreadStartWrite(pDisk);
6450 AssertRC(rc2);
6451 fLockWrite = true;
6452
6453 /* Switch previous image to read-only mode. */
6454 unsigned uOpenFlagsPrevImg;
6455 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
6456 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
6457 {
6458 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
6459 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
6460 }
6461
6462 /** @todo optionally check UUIDs */
6463
6464 /* Re-check state, as the lock wasn't held and another image
6465 * creation call could have been done by another thread. */
6466 AssertMsgStmt(pDisk->cImages != 0,
6467 ("Create diff image cannot be done without other images open\n"),
6468 rc = VERR_VD_INVALID_STATE);
6469 }
6470
6471 if (RT_SUCCESS(rc))
6472 {
6473 RTUUID Uuid;
6474 RTTIMESPEC ts;
6475
6476 if (pParentUuid && !RTUuidIsNull(pParentUuid))
6477 {
6478 Uuid = *pParentUuid;
6479 pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
6480 }
6481 else
6482 {
6483 rc2 = pDisk->pLast->Backend->pfnGetUuid(pDisk->pLast->pBackendData,
6484 &Uuid);
6485 if (RT_SUCCESS(rc2))
6486 pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
6487 }
6488 rc2 = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
6489 &Uuid);
6490 if (RT_SUCCESS(rc2))
6491 pImage->Backend->pfnSetParentModificationUuid(pImage->pBackendData,
6492 &Uuid);
6493 if (pDisk->pLast->Backend->pfnGetTimestamp)
6494 rc2 = pDisk->pLast->Backend->pfnGetTimestamp(pDisk->pLast->pBackendData,
6495 &ts);
6496 else
6497 rc2 = VERR_NOT_IMPLEMENTED;
6498 if (RT_SUCCESS(rc2) && pImage->Backend->pfnSetParentTimestamp)
6499 pImage->Backend->pfnSetParentTimestamp(pImage->pBackendData, &ts);
6500
6501 if (pImage->Backend->pfnSetParentFilename)
6502 rc2 = pImage->Backend->pfnSetParentFilename(pImage->pBackendData, pDisk->pLast->pszFilename);
6503 }
6504
6505 if (RT_SUCCESS(rc))
6506 {
6507 /* Image successfully opened, make it the last image. */
6508 vdAddImageToList(pDisk, pImage);
6509 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
6510 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
6511 }
6512 else
6513 {
6514 /* Error detected, but image opened. Close and delete image. */
6515 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
6516 AssertRC(rc2);
6517 pImage->pBackendData = NULL;
6518 }
6519 } while (0);
6520
6521 if (RT_UNLIKELY(fLockWrite))
6522 {
6523 rc2 = vdThreadFinishWrite(pDisk);
6524 AssertRC(rc2);
6525 }
6526 else if (RT_UNLIKELY(fLockRead))
6527 {
6528 rc2 = vdThreadFinishRead(pDisk);
6529 AssertRC(rc2);
6530 }
6531
6532 if (RT_FAILURE(rc))
6533 {
6534 if (pImage)
6535 {
6536 if (pImage->pszFilename)
6537 RTStrFree(pImage->pszFilename);
6538 RTMemFree(pImage);
6539 }
6540 }
6541
6542 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
6543 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
6544
6545 LogFlowFunc(("returns %Rrc\n", rc));
6546 return rc;
6547}
6548
6549
6550VBOXDDU_DECL(int) VDCreateCache(PVDISK pDisk, const char *pszBackend,
6551 const char *pszFilename, uint64_t cbSize,
6552 unsigned uImageFlags, const char *pszComment,
6553 PCRTUUID pUuid, unsigned uOpenFlags,
6554 PVDINTERFACE pVDIfsCache, PVDINTERFACE pVDIfsOperation)
6555{
6556 int rc = VINF_SUCCESS;
6557 int rc2;
6558 bool fLockWrite = false, fLockRead = false;
6559 PVDCACHE pCache = NULL;
6560 RTUUID uuid;
6561
6562 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
6563 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsCache, pVDIfsOperation));
6564
6565 /* sanity check */
6566 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
6567 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6568
6569 /* Check arguments. */
6570 AssertPtrReturn(pszBackend, VERR_INVALID_POINTER);
6571 AssertReturn(*pszBackend, VERR_INVALID_PARAMETER);
6572 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
6573 AssertReturn(*pszFilename != '\0', VERR_INVALID_PARAMETER);
6574 AssertReturn(cbSize > 0, VERR_INVALID_PARAMETER);
6575 AssertMsgReturn((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0, ("uImageFlags=%#x\n", uImageFlags),
6576 VERR_INVALID_PARAMETER);
6577 /* The UUID may be NULL. */
6578 AssertPtrNullReturn(pUuid, VERR_INVALID_POINTER);
6579 AssertMsgReturn((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0, ("uOpenFlags=%#x\n", uOpenFlags),
6580 VERR_INVALID_PARAMETER);
6581
6582 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
6583
6584 do
6585 {
6586 /* Check state. Needs a temporary read lock. Holding the write lock
6587 * all the time would be blocking other activities for too long. */
6588 rc2 = vdThreadStartRead(pDisk);
6589 AssertRC(rc2);
6590 fLockRead = true;
6591 AssertMsgBreakStmt(!pDisk->pCache,
6592 ("Create cache image cannot be done with a cache already attached\n"),
6593 rc = VERR_VD_CACHE_ALREADY_EXISTS);
6594 rc2 = vdThreadFinishRead(pDisk);
6595 AssertRC(rc2);
6596 fLockRead = false;
6597
6598 /* Set up image descriptor. */
6599 pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
6600 if (!pCache)
6601 {
6602 rc = VERR_NO_MEMORY;
6603 break;
6604 }
6605 pCache->pszFilename = RTStrDup(pszFilename);
6606 if (!pCache->pszFilename)
6607 {
6608 rc = VERR_NO_MEMORY;
6609 break;
6610 }
6611
6612 rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
6613 if (RT_FAILURE(rc))
6614 break;
6615 if (!pCache->Backend)
6616 {
6617 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6618 N_("VD: unknown backend name '%s'"), pszBackend);
6619 break;
6620 }
6621
6622 pCache->VDIo.pDisk = pDisk;
6623 pCache->pVDIfsCache = pVDIfsCache;
6624
6625 /* Set up the I/O interface. */
6626 pCache->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsCache);
6627 if (!pCache->VDIo.pInterfaceIo)
6628 {
6629 vdIfIoFallbackCallbacksSetup(&pCache->VDIo.VDIfIo);
6630 rc = VDInterfaceAdd(&pCache->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
6631 pDisk, sizeof(VDINTERFACEIO), &pVDIfsCache);
6632 pCache->VDIo.pInterfaceIo = &pCache->VDIo.VDIfIo;
6633 }
6634
6635 /* Set up the internal I/O interface. */
6636 AssertBreakStmt(!VDIfIoIntGet(pVDIfsCache), rc = VERR_INVALID_PARAMETER);
6637 vdIfIoIntCallbacksSetup(&pCache->VDIo.VDIfIoInt);
6638 rc = VDInterfaceAdd(&pCache->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
6639 &pCache->VDIo, sizeof(VDINTERFACEIOINT), &pCache->pVDIfsCache);
6640 AssertRC(rc);
6641
6642 /* Create UUID if the caller didn't specify one. */
6643 if (!pUuid)
6644 {
6645 rc = RTUuidCreate(&uuid);
6646 if (RT_FAILURE(rc))
6647 {
6648 rc = vdError(pDisk, rc, RT_SRC_POS,
6649 N_("VD: cannot generate UUID for image '%s'"),
6650 pszFilename);
6651 break;
6652 }
6653 pUuid = &uuid;
6654 }
6655
6656 pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
6657 pCache->VDIo.fIgnoreFlush = (uOpenFlags & VD_OPEN_FLAGS_IGNORE_FLUSH) != 0;
6658 rc = pCache->Backend->pfnCreate(pCache->pszFilename, cbSize,
6659 uImageFlags,
6660 pszComment, pUuid,
6661 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
6662 0, 99,
6663 pDisk->pVDIfsDisk,
6664 pCache->pVDIfsCache,
6665 pVDIfsOperation,
6666 &pCache->pBackendData);
6667
6668 if (RT_SUCCESS(rc))
6669 {
6670 /* Lock disk for writing, as we modify pDisk information below. */
6671 rc2 = vdThreadStartWrite(pDisk);
6672 AssertRC(rc2);
6673 fLockWrite = true;
6674
6675 pCache->VDIo.pBackendData = pCache->pBackendData;
6676
6677 /* Re-check state, as the lock wasn't held and another image
6678 * creation call could have been done by another thread. */
6679 AssertMsgStmt(!pDisk->pCache,
6680 ("Create cache image cannot be done with another cache open\n"),
6681 rc = VERR_VD_CACHE_ALREADY_EXISTS);
6682 }
6683
6684 if ( RT_SUCCESS(rc)
6685 && pDisk->pLast)
6686 {
6687 RTUUID UuidModification;
6688
6689 /* Set same modification Uuid as the last image. */
6690 rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
6691 &UuidModification);
6692 if (RT_SUCCESS(rc))
6693 {
6694 rc = pCache->Backend->pfnSetModificationUuid(pCache->pBackendData,
6695 &UuidModification);
6696 }
6697
6698 if (rc == VERR_NOT_SUPPORTED)
6699 rc = VINF_SUCCESS;
6700 }
6701
6702 if (RT_SUCCESS(rc))
6703 {
6704 /* Cache successfully created. */
6705 pDisk->pCache = pCache;
6706 }
6707 else
6708 {
6709 /* Error detected, but image opened. Close and delete image. */
6710 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, true);
6711 AssertRC(rc2);
6712 pCache->pBackendData = NULL;
6713 }
6714 } while (0);
6715
6716 if (RT_UNLIKELY(fLockWrite))
6717 {
6718 rc2 = vdThreadFinishWrite(pDisk);
6719 AssertRC(rc2);
6720 }
6721 else if (RT_UNLIKELY(fLockRead))
6722 {
6723 rc2 = vdThreadFinishRead(pDisk);
6724 AssertRC(rc2);
6725 }
6726
6727 if (RT_FAILURE(rc))
6728 {
6729 if (pCache)
6730 {
6731 if (pCache->pszFilename)
6732 RTStrFree(pCache->pszFilename);
6733 RTMemFree(pCache);
6734 }
6735 }
6736
6737 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
6738 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
6739
6740 LogFlowFunc(("returns %Rrc\n", rc));
6741 return rc;
6742}
6743
6744
6745VBOXDDU_DECL(int) VDMerge(PVDISK pDisk, unsigned nImageFrom,
6746 unsigned nImageTo, PVDINTERFACE pVDIfsOperation)
6747{
6748 int rc = VINF_SUCCESS;
6749 int rc2;
6750 bool fLockWrite = false, fLockRead = false;
6751 void *pvBuf = NULL;
6752
6753 LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pVDIfsOperation=%#p\n",
6754 pDisk, nImageFrom, nImageTo, pVDIfsOperation));
6755
6756 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
6757
6758 do
6759 {
6760 /* sanity check */
6761 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6762 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6763
6764 /* For simplicity reasons lock for writing as the image reopen below
6765 * might need it. After all the reopen is usually needed. */
6766 rc2 = vdThreadStartWrite(pDisk);
6767 AssertRC(rc2);
6768 fLockWrite = true;
6769 PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
6770 PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
6771 if (!pImageFrom || !pImageTo)
6772 {
6773 rc = VERR_VD_IMAGE_NOT_FOUND;
6774 break;
6775 }
6776 AssertBreakStmt(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
6777
6778 /* Make sure destination image is writable. */
6779 unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
6780 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
6781 {
6782 /*
6783 * Clear skip consistency checks because the image is made writable now and
6784 * skipping consistency checks is only possible for readonly images.
6785 */
6786 uOpenFlags &= ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS);
6787 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
6788 uOpenFlags);
6789 if (RT_FAILURE(rc))
6790 break;
6791 }
6792
6793 /* Get size of destination image. */
6794 uint64_t cbSize = vdImageGetSize(pImageTo);
6795 rc2 = vdThreadFinishWrite(pDisk);
6796 AssertRC(rc2);
6797 fLockWrite = false;
6798
6799 /* Allocate tmp buffer. */
6800 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
6801 if (!pvBuf)
6802 {
6803 rc = VERR_NO_MEMORY;
6804 break;
6805 }
6806
6807 /* Merging is done directly on the images itself. This potentially
6808 * causes trouble if the disk is full in the middle of operation. */
6809 if (nImageFrom < nImageTo)
6810 {
6811 /* Merge parent state into child. This means writing all not
6812 * allocated blocks in the destination image which are allocated in
6813 * the images to be merged. */
6814 uint64_t uOffset = 0;
6815 uint64_t cbRemaining = cbSize;
6816
6817 do
6818 {
6819 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
6820 RTSGSEG SegmentBuf;
6821 RTSGBUF SgBuf;
6822 VDIOCTX IoCtx;
6823
6824 SegmentBuf.pvSeg = pvBuf;
6825 SegmentBuf.cbSeg = VD_MERGE_BUFFER_SIZE;
6826 RTSgBufInit(&SgBuf, &SegmentBuf, 1);
6827 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_READ, 0, 0, NULL,
6828 &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
6829
6830 /* Need to hold the write lock during a read-write operation. */
6831 rc2 = vdThreadStartWrite(pDisk);
6832 AssertRC(rc2);
6833 fLockWrite = true;
6834
6835 rc = pImageTo->Backend->pfnRead(pImageTo->pBackendData,
6836 uOffset, cbThisRead,
6837 &IoCtx, &cbThisRead);
6838 if (rc == VERR_VD_BLOCK_FREE)
6839 {
6840 /* Search for image with allocated block. Do not attempt to
6841 * read more than the previous reads marked as valid.
6842 * Otherwise this would return stale data when different
6843 * block sizes are used for the images. */
6844 for (PVDIMAGE pCurrImage = pImageTo->pPrev;
6845 pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VERR_VD_BLOCK_FREE;
6846 pCurrImage = pCurrImage->pPrev)
6847 {
6848 /*
6849 * Skip reading when offset exceeds image size which can happen when the target is
6850 * bigger than the source.
6851 */
6852 uint64_t cbImage = vdImageGetSize(pCurrImage);
6853 if (uOffset < cbImage)
6854 {
6855 cbThisRead = RT_MIN(cbThisRead, cbImage - uOffset);
6856 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
6857 uOffset, cbThisRead,
6858 &IoCtx, &cbThisRead);
6859 }
6860 else
6861 rc = VERR_VD_BLOCK_FREE;
6862 }
6863
6864 if (rc != VERR_VD_BLOCK_FREE)
6865 {
6866 if (RT_FAILURE(rc))
6867 break;
6868 /* Updating the cache is required because this might be a live merge. */
6869 rc = vdWriteHelperEx(pDisk, pImageTo, pImageFrom->pPrev,
6870 uOffset, pvBuf, cbThisRead,
6871 VDIOCTX_FLAGS_READ_UPDATE_CACHE, 0);
6872 if (RT_FAILURE(rc))
6873 break;
6874 }
6875 else
6876 rc = VINF_SUCCESS;
6877 }
6878 else if (RT_FAILURE(rc))
6879 break;
6880
6881 rc2 = vdThreadFinishWrite(pDisk);
6882 AssertRC(rc2);
6883 fLockWrite = false;
6884
6885 uOffset += cbThisRead;
6886 cbRemaining -= cbThisRead;
6887
6888 if (pIfProgress && pIfProgress->pfnProgress)
6889 {
6890 /** @todo r=klaus: this can update the progress to the same
6891 * percentage over and over again if the image format makes
6892 * relatively small increments. */
6893 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
6894 uOffset * 99 / cbSize);
6895 if (RT_FAILURE(rc))
6896 break;
6897 }
6898 } while (uOffset < cbSize);
6899 }
6900 else
6901 {
6902 /*
6903 * We may need to update the parent uuid of the child coming after
6904 * the last image to be merged. We have to reopen it read/write.
6905 *
6906 * This is done before we do the actual merge to prevent an
6907 * inconsistent chain if the mode change fails for some reason.
6908 */
6909 if (pImageFrom->pNext)
6910 {
6911 PVDIMAGE pImageChild = pImageFrom->pNext;
6912
6913 /* Take the write lock. */
6914 rc2 = vdThreadStartWrite(pDisk);
6915 AssertRC(rc2);
6916 fLockWrite = true;
6917
6918 /* We need to open the image in read/write mode. */
6919 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
6920
6921 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
6922 {
6923 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
6924 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
6925 uOpenFlags);
6926 if (RT_FAILURE(rc))
6927 break;
6928 }
6929
6930 rc2 = vdThreadFinishWrite(pDisk);
6931 AssertRC(rc2);
6932 fLockWrite = false;
6933 }
6934
6935 /* If the merge is from the last image we have to relay all writes
6936 * to the merge destination as well, so that concurrent writes
6937 * (in case of a live merge) are handled correctly. */
6938 if (!pImageFrom->pNext)
6939 {
6940 /* Take the write lock. */
6941 rc2 = vdThreadStartWrite(pDisk);
6942 AssertRC(rc2);
6943 fLockWrite = true;
6944
6945 pDisk->pImageRelay = pImageTo;
6946
6947 rc2 = vdThreadFinishWrite(pDisk);
6948 AssertRC(rc2);
6949 fLockWrite = false;
6950 }
6951
6952 /* Merge child state into parent. This means writing all blocks
6953 * which are allocated in the image up to the source image to the
6954 * destination image. */
6955 unsigned uProgressOld = 0;
6956 uint64_t uOffset = 0;
6957 uint64_t cbRemaining = cbSize;
6958 do
6959 {
6960 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
6961 RTSGSEG SegmentBuf;
6962 RTSGBUF SgBuf;
6963 VDIOCTX IoCtx;
6964
6965 rc = VERR_VD_BLOCK_FREE;
6966
6967 SegmentBuf.pvSeg = pvBuf;
6968 SegmentBuf.cbSeg = VD_MERGE_BUFFER_SIZE;
6969 RTSgBufInit(&SgBuf, &SegmentBuf, 1);
6970 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_READ, 0, 0, NULL,
6971 &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
6972
6973 /* Need to hold the write lock during a read-write operation. */
6974 rc2 = vdThreadStartWrite(pDisk);
6975 AssertRC(rc2);
6976 fLockWrite = true;
6977
6978 /* Search for image with allocated block. Do not attempt to
6979 * read more than the previous reads marked as valid. Otherwise
6980 * this would return stale data when different block sizes are
6981 * used for the images. */
6982 for (PVDIMAGE pCurrImage = pImageFrom;
6983 pCurrImage != NULL && pCurrImage != pImageTo && rc == VERR_VD_BLOCK_FREE;
6984 pCurrImage = pCurrImage->pPrev)
6985 {
6986 /*
6987 * Skip reading when offset exceeds image size which can happen when the target is
6988 * bigger than the source.
6989 */
6990 uint64_t cbImage = vdImageGetSize(pCurrImage);
6991 if (uOffset < cbImage)
6992 {
6993 cbThisRead = RT_MIN(cbThisRead, cbImage - uOffset);
6994 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
6995 uOffset, cbThisRead,
6996 &IoCtx, &cbThisRead);
6997 }
6998 else
6999 rc = VERR_VD_BLOCK_FREE;
7000 }
7001
7002 if (rc != VERR_VD_BLOCK_FREE)
7003 {
7004 if (RT_FAILURE(rc))
7005 break;
7006 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
7007 cbThisRead, VDIOCTX_FLAGS_READ_UPDATE_CACHE);
7008 if (RT_FAILURE(rc))
7009 break;
7010 }
7011 else
7012 rc = VINF_SUCCESS;
7013
7014 rc2 = vdThreadFinishWrite(pDisk);
7015 AssertRC(rc2);
7016 fLockWrite = false;
7017
7018 uOffset += cbThisRead;
7019 cbRemaining -= cbThisRead;
7020
7021 unsigned uProgressNew = uOffset * 99 / cbSize;
7022 if (uProgressNew != uProgressOld)
7023 {
7024 uProgressOld = uProgressNew;
7025
7026 if (pIfProgress && pIfProgress->pfnProgress)
7027 {
7028 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
7029 uProgressOld);
7030 if (RT_FAILURE(rc))
7031 break;
7032 }
7033 }
7034
7035 } while (uOffset < cbSize);
7036
7037 /* In case we set up a "write proxy" image above we must clear
7038 * this again now to prevent stray writes. Failure or not. */
7039 if (!pImageFrom->pNext)
7040 {
7041 /* Take the write lock. */
7042 rc2 = vdThreadStartWrite(pDisk);
7043 AssertRC(rc2);
7044 fLockWrite = true;
7045
7046 pDisk->pImageRelay = NULL;
7047
7048 rc2 = vdThreadFinishWrite(pDisk);
7049 AssertRC(rc2);
7050 fLockWrite = false;
7051 }
7052 }
7053
7054 /*
7055 * Leave in case of an error to avoid corrupted data in the image chain
7056 * (includes cancelling the operation by the user).
7057 */
7058 if (RT_FAILURE(rc))
7059 break;
7060
7061 /* Need to hold the write lock while finishing the merge. */
7062 rc2 = vdThreadStartWrite(pDisk);
7063 AssertRC(rc2);
7064 fLockWrite = true;
7065
7066 /* Update parent UUID so that image chain is consistent.
7067 * The two attempts work around the problem that some backends
7068 * (e.g. iSCSI) do not support UUIDs, so we exploit the fact that
7069 * so far there can only be one such image in the chain. */
7070 /** @todo needs a better long-term solution, passing the UUID
7071 * knowledge from the caller or some such */
7072 RTUUID Uuid;
7073 PVDIMAGE pImageChild = NULL;
7074 if (nImageFrom < nImageTo)
7075 {
7076 if (pImageFrom->pPrev)
7077 {
7078 /* plan A: ask the parent itself for its UUID */
7079 rc = pImageFrom->pPrev->Backend->pfnGetUuid(pImageFrom->pPrev->pBackendData,
7080 &Uuid);
7081 if (RT_FAILURE(rc))
7082 {
7083 /* plan B: ask the child of the parent for parent UUID */
7084 rc = pImageFrom->Backend->pfnGetParentUuid(pImageFrom->pBackendData,
7085 &Uuid);
7086 }
7087 AssertRC(rc);
7088 }
7089 else
7090 RTUuidClear(&Uuid);
7091 rc = pImageTo->Backend->pfnSetParentUuid(pImageTo->pBackendData,
7092 &Uuid);
7093 AssertRC(rc);
7094 }
7095 else
7096 {
7097 /* Update the parent uuid of the child of the last merged image. */
7098 if (pImageFrom->pNext)
7099 {
7100 /* plan A: ask the parent itself for its UUID */
7101 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pBackendData,
7102 &Uuid);
7103 if (RT_FAILURE(rc))
7104 {
7105 /* plan B: ask the child of the parent for parent UUID */
7106 rc = pImageTo->pNext->Backend->pfnGetParentUuid(pImageTo->pNext->pBackendData,
7107 &Uuid);
7108 }
7109 AssertRC(rc);
7110
7111 rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext->pBackendData,
7112 &Uuid);
7113 AssertRC(rc);
7114
7115 pImageChild = pImageFrom->pNext;
7116 }
7117 }
7118
7119 /* Delete the no longer needed images. */
7120 PVDIMAGE pImg = pImageFrom, pTmp;
7121 while (pImg != pImageTo)
7122 {
7123 if (nImageFrom < nImageTo)
7124 pTmp = pImg->pNext;
7125 else
7126 pTmp = pImg->pPrev;
7127 vdRemoveImageFromList(pDisk, pImg);
7128 pImg->Backend->pfnClose(pImg->pBackendData, true);
7129 RTStrFree(pImg->pszFilename);
7130 RTMemFree(pImg);
7131 pImg = pTmp;
7132 }
7133
7134 /* Make sure destination image is back to read only if necessary. */
7135 if (pImageTo != pDisk->pLast)
7136 {
7137 uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
7138 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
7139 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
7140 uOpenFlags);
7141 if (RT_FAILURE(rc))
7142 break;
7143 }
7144
7145 /*
7146 * Make sure the child is readonly
7147 * for the child -> parent merge direction
7148 * if necessary.
7149 */
7150 if ( nImageFrom > nImageTo
7151 && pImageChild
7152 && pImageChild != pDisk->pLast)
7153 {
7154 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
7155 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
7156 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
7157 uOpenFlags);
7158 if (RT_FAILURE(rc))
7159 break;
7160 }
7161 } while (0);
7162
7163 if (RT_UNLIKELY(fLockWrite))
7164 {
7165 rc2 = vdThreadFinishWrite(pDisk);
7166 AssertRC(rc2);
7167 }
7168 else if (RT_UNLIKELY(fLockRead))
7169 {
7170 rc2 = vdThreadFinishRead(pDisk);
7171 AssertRC(rc2);
7172 }
7173
7174 if (pvBuf)
7175 RTMemTmpFree(pvBuf);
7176
7177 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
7178 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
7179
7180 LogFlowFunc(("returns %Rrc\n", rc));
7181 return rc;
7182}
7183
7184
7185VBOXDDU_DECL(int) VDCopyEx(PVDISK pDiskFrom, unsigned nImage, PVDISK pDiskTo,
7186 const char *pszBackend, const char *pszFilename,
7187 bool fMoveByRename, uint64_t cbSize,
7188 unsigned nImageFromSame, unsigned nImageToSame,
7189 unsigned uImageFlags, PCRTUUID pDstUuid,
7190 unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
7191 PVDINTERFACE pDstVDIfsImage,
7192 PVDINTERFACE pDstVDIfsOperation)
7193{
7194 int rc = VINF_SUCCESS;
7195 int rc2;
7196 bool fLockReadFrom = false, fLockWriteFrom = false, fLockWriteTo = false;
7197 PVDIMAGE pImageTo = NULL;
7198
7199 LogFlowFunc(("pDiskFrom=%#p nImage=%u pDiskTo=%#p pszBackend=\"%s\" pszFilename=\"%s\" fMoveByRename=%d cbSize=%llu nImageFromSame=%u nImageToSame=%u uImageFlags=%#x pDstUuid=%#p uOpenFlags=%#x pVDIfsOperation=%#p pDstVDIfsImage=%#p pDstVDIfsOperation=%#p\n",
7200 pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, nImageFromSame, nImageToSame, uImageFlags, pDstUuid, uOpenFlags, pVDIfsOperation, pDstVDIfsImage, pDstVDIfsOperation));
7201
7202 /* Check arguments. */
7203 AssertReturn(pDiskFrom, VERR_INVALID_POINTER);
7204 AssertMsg(pDiskFrom->u32Signature == VDISK_SIGNATURE,
7205 ("u32Signature=%08x\n", pDiskFrom->u32Signature));
7206
7207 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
7208 PVDINTERFACEPROGRESS pDstIfProgress = VDIfProgressGet(pDstVDIfsOperation);
7209
7210 do {
7211 rc2 = vdThreadStartRead(pDiskFrom);
7212 AssertRC(rc2);
7213 fLockReadFrom = true;
7214 PVDIMAGE pImageFrom = vdGetImageByNumber(pDiskFrom, nImage);
7215 AssertPtrBreakStmt(pImageFrom, rc = VERR_VD_IMAGE_NOT_FOUND);
7216 AssertPtrBreakStmt(pDiskTo, rc = VERR_INVALID_POINTER);
7217 AssertMsg(pDiskTo->u32Signature == VDISK_SIGNATURE,
7218 ("u32Signature=%08x\n", pDiskTo->u32Signature));
7219 AssertMsgBreakStmt( (nImageFromSame < nImage || nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN)
7220 && (nImageToSame < pDiskTo->cImages || nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
7221 && ( (nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN && nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
7222 || (nImageFromSame != VD_IMAGE_CONTENT_UNKNOWN && nImageToSame != VD_IMAGE_CONTENT_UNKNOWN)),
7223 ("nImageFromSame=%u nImageToSame=%u\n", nImageFromSame, nImageToSame),
7224 rc = VERR_INVALID_PARAMETER);
7225
7226 /* Move the image. */
7227 if (pDiskFrom == pDiskTo)
7228 {
7229 /* Rename only works when backends are the same, are file based
7230 * and the rename method is implemented. */
7231 if ( fMoveByRename
7232 && !RTStrICmp(pszBackend, pImageFrom->Backend->pszBackendName)
7233 && pImageFrom->Backend->uBackendCaps & VD_CAP_FILE
7234 && pImageFrom->Backend->pfnRename)
7235 {
7236 rc2 = vdThreadFinishRead(pDiskFrom);
7237 AssertRC(rc2);
7238 fLockReadFrom = false;
7239
7240 rc2 = vdThreadStartWrite(pDiskFrom);
7241 AssertRC(rc2);
7242 fLockWriteFrom = true;
7243 rc = pImageFrom->Backend->pfnRename(pImageFrom->pBackendData, pszFilename ? pszFilename : pImageFrom->pszFilename);
7244 break;
7245 }
7246
7247 /** @todo Moving (including shrinking/growing) of the image is
7248 * requested, but the rename attempt failed or it wasn't possible.
7249 * Must now copy image to temp location. */
7250 AssertReleaseMsgFailed(("VDCopy: moving by copy/delete not implemented\n"));
7251 }
7252
7253 /* pszFilename is allowed to be NULL, as this indicates copy to the existing image. */
7254 if (pszFilename)
7255 {
7256 AssertPtrBreakStmt(pszFilename, rc = VERR_INVALID_POINTER);
7257 AssertBreakStmt(*pszFilename != '\0', rc = VERR_INVALID_PARAMETER);
7258 }
7259
7260 uint64_t cbSizeFrom;
7261 cbSizeFrom = vdImageGetSize(pImageFrom);
7262 if (cbSizeFrom == 0)
7263 {
7264 rc = VERR_VD_VALUE_NOT_FOUND;
7265 break;
7266 }
7267
7268 VDGEOMETRY PCHSGeometryFrom = {0, 0, 0};
7269 VDGEOMETRY LCHSGeometryFrom = {0, 0, 0};
7270 pImageFrom->Backend->pfnGetPCHSGeometry(pImageFrom->pBackendData, &PCHSGeometryFrom);
7271 pImageFrom->Backend->pfnGetLCHSGeometry(pImageFrom->pBackendData, &LCHSGeometryFrom);
7272
7273 RTUUID ImageUuid, ImageModificationUuid;
7274 if (pDiskFrom != pDiskTo)
7275 {
7276 if (pDstUuid)
7277 ImageUuid = *pDstUuid;
7278 else
7279 RTUuidCreate(&ImageUuid);
7280 }
7281 else
7282 {
7283 rc = pImageFrom->Backend->pfnGetUuid(pImageFrom->pBackendData, &ImageUuid);
7284 if (RT_FAILURE(rc))
7285 RTUuidCreate(&ImageUuid);
7286 }
7287 rc = pImageFrom->Backend->pfnGetModificationUuid(pImageFrom->pBackendData, &ImageModificationUuid);
7288 if (RT_FAILURE(rc))
7289 RTUuidClear(&ImageModificationUuid);
7290
7291 char szComment[1024];
7292 rc = pImageFrom->Backend->pfnGetComment(pImageFrom->pBackendData, szComment, sizeof(szComment));
7293 if (RT_FAILURE(rc))
7294 szComment[0] = '\0';
7295 else
7296 szComment[sizeof(szComment) - 1] = '\0';
7297
7298 rc2 = vdThreadFinishRead(pDiskFrom);
7299 AssertRC(rc2);
7300 fLockReadFrom = false;
7301
7302 rc2 = vdThreadStartRead(pDiskTo);
7303 AssertRC(rc2);
7304 unsigned cImagesTo = pDiskTo->cImages;
7305 rc2 = vdThreadFinishRead(pDiskTo);
7306 AssertRC(rc2);
7307
7308 if (pszFilename)
7309 {
7310 if (cbSize == 0)
7311 cbSize = cbSizeFrom;
7312
7313 /* Create destination image with the properties of source image. */
7314 /** @todo replace the VDCreateDiff/VDCreateBase calls by direct
7315 * calls to the backend. Unifies the code and reduces the API
7316 * dependencies. Would also make the synchronization explicit. */
7317 if (cImagesTo > 0)
7318 {
7319 rc = VDCreateDiff(pDiskTo, pszBackend, pszFilename,
7320 uImageFlags, szComment, &ImageUuid,
7321 NULL /* pParentUuid */,
7322 uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
7323 pDstVDIfsImage, NULL);
7324
7325 rc2 = vdThreadStartWrite(pDiskTo);
7326 AssertRC(rc2);
7327 fLockWriteTo = true;
7328 } else {
7329 /** @todo hack to force creation of a fixed image for
7330 * the RAW backend, which can't handle anything else. */
7331 if (!RTStrICmp(pszBackend, "RAW"))
7332 uImageFlags |= VD_IMAGE_FLAGS_FIXED;
7333
7334 vdFixupPCHSGeometry(&PCHSGeometryFrom, cbSize);
7335 vdFixupLCHSGeometry(&LCHSGeometryFrom, cbSize);
7336
7337 rc = VDCreateBase(pDiskTo, pszBackend, pszFilename, cbSize,
7338 uImageFlags, szComment,
7339 &PCHSGeometryFrom, &LCHSGeometryFrom,
7340 NULL, uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
7341 pDstVDIfsImage, NULL);
7342
7343 rc2 = vdThreadStartWrite(pDiskTo);
7344 AssertRC(rc2);
7345 fLockWriteTo = true;
7346
7347 if (RT_SUCCESS(rc) && !RTUuidIsNull(&ImageUuid))
7348 pDiskTo->pLast->Backend->pfnSetUuid(pDiskTo->pLast->pBackendData, &ImageUuid);
7349 }
7350 if (RT_FAILURE(rc))
7351 break;
7352
7353 pImageTo = pDiskTo->pLast;
7354 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
7355
7356 cbSize = RT_MIN(cbSize, cbSizeFrom);
7357 }
7358 else
7359 {
7360 pImageTo = pDiskTo->pLast;
7361 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
7362
7363 uint64_t cbSizeTo;
7364 cbSizeTo = vdImageGetSize(pImageTo);
7365 if (cbSizeTo == 0)
7366 {
7367 rc = VERR_VD_VALUE_NOT_FOUND;
7368 break;
7369 }
7370
7371 if (cbSize == 0)
7372 cbSize = RT_MIN(cbSizeFrom, cbSizeTo);
7373
7374 vdFixupPCHSGeometry(&PCHSGeometryFrom, cbSize);
7375 vdFixupLCHSGeometry(&LCHSGeometryFrom, cbSize);
7376
7377 /* Update the geometry in the destination image. */
7378 pImageTo->Backend->pfnSetPCHSGeometry(pImageTo->pBackendData, &PCHSGeometryFrom);
7379 pImageTo->Backend->pfnSetLCHSGeometry(pImageTo->pBackendData, &LCHSGeometryFrom);
7380 }
7381
7382 rc2 = vdThreadFinishWrite(pDiskTo);
7383 AssertRC(rc2);
7384 fLockWriteTo = false;
7385
7386 /* Whether we can take the optimized copy path (false) or not.
7387 * Don't optimize if the image existed or if it is a child image. */
7388 bool fSuppressRedundantIo = ( !(pszFilename == NULL || cImagesTo > 0)
7389 || (nImageToSame != VD_IMAGE_CONTENT_UNKNOWN));
7390 unsigned cImagesFromReadBack, cImagesToReadBack;
7391
7392 if (nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN)
7393 cImagesFromReadBack = 0;
7394 else
7395 {
7396 if (nImage == VD_LAST_IMAGE)
7397 cImagesFromReadBack = pDiskFrom->cImages - nImageFromSame - 1;
7398 else
7399 cImagesFromReadBack = nImage - nImageFromSame;
7400 }
7401
7402 if (nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
7403 cImagesToReadBack = 0;
7404 else
7405 cImagesToReadBack = pDiskTo->cImages - nImageToSame - 1;
7406
7407 /* Copy the data. */
7408 rc = vdCopyHelper(pDiskFrom, pImageFrom, pDiskTo, cbSize,
7409 cImagesFromReadBack, cImagesToReadBack,
7410 fSuppressRedundantIo, pIfProgress, pDstIfProgress);
7411
7412 if (RT_SUCCESS(rc))
7413 {
7414 rc2 = vdThreadStartWrite(pDiskTo);
7415 AssertRC(rc2);
7416 fLockWriteTo = true;
7417
7418 /* Only set modification UUID if it is non-null, since the source
7419 * backend might not provide a valid modification UUID. */
7420 if (!RTUuidIsNull(&ImageModificationUuid))
7421 pImageTo->Backend->pfnSetModificationUuid(pImageTo->pBackendData, &ImageModificationUuid);
7422
7423 /* Set the requested open flags if they differ from the value
7424 * required for creating the image and copying the contents. */
7425 if ( pImageTo && pszFilename
7426 && uOpenFlags != (uOpenFlags & ~VD_OPEN_FLAGS_READONLY))
7427 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
7428 uOpenFlags);
7429 }
7430 } while (0);
7431
7432 if (RT_FAILURE(rc) && pImageTo && pszFilename)
7433 {
7434 /* Take the write lock only if it is not taken. Not worth making the
7435 * above code even more complicated. */
7436 if (RT_UNLIKELY(!fLockWriteTo))
7437 {
7438 rc2 = vdThreadStartWrite(pDiskTo);
7439 AssertRC(rc2);
7440 fLockWriteTo = true;
7441 }
7442 /* Error detected, but new image created. Remove image from list. */
7443 vdRemoveImageFromList(pDiskTo, pImageTo);
7444
7445 /* Close and delete image. */
7446 rc2 = pImageTo->Backend->pfnClose(pImageTo->pBackendData, true);
7447 AssertRC(rc2);
7448 pImageTo->pBackendData = NULL;
7449
7450 /* Free remaining resources. */
7451 if (pImageTo->pszFilename)
7452 RTStrFree(pImageTo->pszFilename);
7453
7454 RTMemFree(pImageTo);
7455 }
7456
7457 if (RT_UNLIKELY(fLockWriteTo))
7458 {
7459 rc2 = vdThreadFinishWrite(pDiskTo);
7460 AssertRC(rc2);
7461 }
7462 if (RT_UNLIKELY(fLockWriteFrom))
7463 {
7464 rc2 = vdThreadFinishWrite(pDiskFrom);
7465 AssertRC(rc2);
7466 }
7467 else if (RT_UNLIKELY(fLockReadFrom))
7468 {
7469 rc2 = vdThreadFinishRead(pDiskFrom);
7470 AssertRC(rc2);
7471 }
7472
7473 if (RT_SUCCESS(rc))
7474 {
7475 if (pIfProgress && pIfProgress->pfnProgress)
7476 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
7477 if (pDstIfProgress && pDstIfProgress->pfnProgress)
7478 pDstIfProgress->pfnProgress(pDstIfProgress->Core.pvUser, 100);
7479 }
7480
7481 LogFlowFunc(("returns %Rrc\n", rc));
7482 return rc;
7483}
7484
7485
7486VBOXDDU_DECL(int) VDCopy(PVDISK pDiskFrom, unsigned nImage, PVDISK pDiskTo,
7487 const char *pszBackend, const char *pszFilename,
7488 bool fMoveByRename, uint64_t cbSize,
7489 unsigned uImageFlags, PCRTUUID pDstUuid,
7490 unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
7491 PVDINTERFACE pDstVDIfsImage,
7492 PVDINTERFACE pDstVDIfsOperation)
7493{
7494 return VDCopyEx(pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename,
7495 cbSize, VD_IMAGE_CONTENT_UNKNOWN, VD_IMAGE_CONTENT_UNKNOWN,
7496 uImageFlags, pDstUuid, uOpenFlags, pVDIfsOperation,
7497 pDstVDIfsImage, pDstVDIfsOperation);
7498}
7499
7500
7501VBOXDDU_DECL(int) VDCompact(PVDISK pDisk, unsigned nImage,
7502 PVDINTERFACE pVDIfsOperation)
7503{
7504 int rc = VINF_SUCCESS;
7505 int rc2;
7506 bool fLockRead = false, fLockWrite = false;
7507 void *pvBuf = NULL;
7508 void *pvTmp = NULL;
7509
7510 LogFlowFunc(("pDisk=%#p nImage=%u pVDIfsOperation=%#p\n",
7511 pDisk, nImage, pVDIfsOperation));
7512 /* Check arguments. */
7513 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
7514 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE,
7515 ("u32Signature=%08x\n", pDisk->u32Signature));
7516
7517 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
7518
7519 do {
7520 rc2 = vdThreadStartRead(pDisk);
7521 AssertRC(rc2);
7522 fLockRead = true;
7523
7524 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7525 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7526
7527 /* If there is no compact callback for not file based backends then
7528 * the backend doesn't need compaction. No need to make much fuss about
7529 * this. For file based ones signal this as not yet supported. */
7530 if (!pImage->Backend->pfnCompact)
7531 {
7532 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
7533 rc = VERR_NOT_SUPPORTED;
7534 else
7535 rc = VINF_SUCCESS;
7536 break;
7537 }
7538
7539 /* Insert interface for reading parent state into per-operation list,
7540 * if there is a parent image. */
7541 VDINTERFACEPARENTSTATE VDIfParent;
7542 VDPARENTSTATEDESC ParentUser;
7543 if (pImage->pPrev)
7544 {
7545 VDIfParent.pfnParentRead = vdParentRead;
7546 ParentUser.pDisk = pDisk;
7547 ParentUser.pImage = pImage->pPrev;
7548 rc = VDInterfaceAdd(&VDIfParent.Core, "VDCompact_ParentState", VDINTERFACETYPE_PARENTSTATE,
7549 &ParentUser, sizeof(VDINTERFACEPARENTSTATE), &pVDIfsOperation);
7550 AssertRC(rc);
7551 }
7552
7553 rc2 = vdThreadFinishRead(pDisk);
7554 AssertRC(rc2);
7555 fLockRead = false;
7556
7557 rc2 = vdThreadStartWrite(pDisk);
7558 AssertRC(rc2);
7559 fLockWrite = true;
7560
7561 rc = pImage->Backend->pfnCompact(pImage->pBackendData,
7562 0, 99,
7563 pDisk->pVDIfsDisk,
7564 pImage->pVDIfsImage,
7565 pVDIfsOperation);
7566 } while (0);
7567
7568 if (RT_UNLIKELY(fLockWrite))
7569 {
7570 rc2 = vdThreadFinishWrite(pDisk);
7571 AssertRC(rc2);
7572 }
7573 else if (RT_UNLIKELY(fLockRead))
7574 {
7575 rc2 = vdThreadFinishRead(pDisk);
7576 AssertRC(rc2);
7577 }
7578
7579 if (pvBuf)
7580 RTMemTmpFree(pvBuf);
7581 if (pvTmp)
7582 RTMemTmpFree(pvTmp);
7583
7584 if (RT_SUCCESS(rc))
7585 {
7586 if (pIfProgress && pIfProgress->pfnProgress)
7587 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
7588 }
7589
7590 LogFlowFunc(("returns %Rrc\n", rc));
7591 return rc;
7592}
7593
7594
7595VBOXDDU_DECL(int) VDResize(PVDISK pDisk, uint64_t cbSize,
7596 PCVDGEOMETRY pPCHSGeometry,
7597 PCVDGEOMETRY pLCHSGeometry,
7598 PVDINTERFACE pVDIfsOperation)
7599{
7600 /** @todo r=klaus resizing was designed to be part of VDCopy, so having a separate function is not desirable. */
7601 int rc = VINF_SUCCESS;
7602 int rc2;
7603 bool fLockRead = false, fLockWrite = false;
7604
7605 LogFlowFunc(("pDisk=%#p cbSize=%llu pVDIfsOperation=%#p\n",
7606 pDisk, cbSize, pVDIfsOperation));
7607 /* Check arguments. */
7608 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
7609 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE,
7610 ("u32Signature=%08x\n", pDisk->u32Signature));
7611
7612 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
7613
7614 do {
7615 rc2 = vdThreadStartRead(pDisk);
7616 AssertRC(rc2);
7617 fLockRead = true;
7618
7619 /* Must have at least one image in the chain, will resize last. */
7620 AssertMsgBreakStmt(pDisk->cImages >= 1, ("cImages=%u\n", pDisk->cImages),
7621 rc = VERR_NOT_SUPPORTED);
7622
7623 PVDIMAGE pImage = pDisk->pLast;
7624
7625 /* If there is no compact callback for not file based backends then
7626 * the backend doesn't need compaction. No need to make much fuss about
7627 * this. For file based ones signal this as not yet supported. */
7628 if (!pImage->Backend->pfnResize)
7629 {
7630 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
7631 rc = VERR_NOT_SUPPORTED;
7632 else
7633 rc = VINF_SUCCESS;
7634 break;
7635 }
7636
7637 rc2 = vdThreadFinishRead(pDisk);
7638 AssertRC(rc2);
7639 fLockRead = false;
7640
7641 rc2 = vdThreadStartWrite(pDisk);
7642 AssertRC(rc2);
7643 fLockWrite = true;
7644
7645 VDGEOMETRY PCHSGeometryOld;
7646 VDGEOMETRY LCHSGeometryOld;
7647 PCVDGEOMETRY pPCHSGeometryNew;
7648 PCVDGEOMETRY pLCHSGeometryNew;
7649
7650 if (pPCHSGeometry->cCylinders == 0)
7651 {
7652 /* Auto-detect marker, calculate new value ourself. */
7653 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData, &PCHSGeometryOld);
7654 if (RT_SUCCESS(rc) && (PCHSGeometryOld.cCylinders != 0))
7655 PCHSGeometryOld.cCylinders = RT_MIN(cbSize / 512 / PCHSGeometryOld.cHeads / PCHSGeometryOld.cSectors, 16383);
7656 else if (rc == VERR_VD_GEOMETRY_NOT_SET)
7657 rc = VINF_SUCCESS;
7658
7659 pPCHSGeometryNew = &PCHSGeometryOld;
7660 }
7661 else
7662 pPCHSGeometryNew = pPCHSGeometry;
7663
7664 if (pLCHSGeometry->cCylinders == 0)
7665 {
7666 /* Auto-detect marker, calculate new value ourself. */
7667 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData, &LCHSGeometryOld);
7668 if (RT_SUCCESS(rc) && (LCHSGeometryOld.cCylinders != 0))
7669 LCHSGeometryOld.cCylinders = cbSize / 512 / LCHSGeometryOld.cHeads / LCHSGeometryOld.cSectors;
7670 else if (rc == VERR_VD_GEOMETRY_NOT_SET)
7671 rc = VINF_SUCCESS;
7672
7673 pLCHSGeometryNew = &LCHSGeometryOld;
7674 }
7675 else
7676 pLCHSGeometryNew = pLCHSGeometry;
7677
7678 if (RT_SUCCESS(rc))
7679 rc = pImage->Backend->pfnResize(pImage->pBackendData,
7680 cbSize,
7681 pPCHSGeometryNew,
7682 pLCHSGeometryNew,
7683 0, 99,
7684 pDisk->pVDIfsDisk,
7685 pImage->pVDIfsImage,
7686 pVDIfsOperation);
7687 /* Mark the image size as uninitialized so it gets recalculated the next time. */
7688 if (RT_SUCCESS(rc))
7689 pImage->cbImage = VD_IMAGE_SIZE_UNINITIALIZED;
7690 } while (0);
7691
7692 if (RT_UNLIKELY(fLockWrite))
7693 {
7694 rc2 = vdThreadFinishWrite(pDisk);
7695 AssertRC(rc2);
7696 }
7697 else if (RT_UNLIKELY(fLockRead))
7698 {
7699 rc2 = vdThreadFinishRead(pDisk);
7700 AssertRC(rc2);
7701 }
7702
7703 if (RT_SUCCESS(rc))
7704 {
7705 if (pIfProgress && pIfProgress->pfnProgress)
7706 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
7707
7708 pDisk->cbSize = cbSize;
7709 }
7710
7711 LogFlowFunc(("returns %Rrc\n", rc));
7712 return rc;
7713}
7714
7715VBOXDDU_DECL(int) VDPrepareWithFilters(PVDISK pDisk, PVDINTERFACE pVDIfsOperation)
7716{
7717 int rc = VINF_SUCCESS;
7718 int rc2;
7719 bool fLockRead = false, fLockWrite = false;
7720
7721 LogFlowFunc(("pDisk=%#p pVDIfsOperation=%#p\n", pDisk, pVDIfsOperation));
7722 /* Check arguments. */
7723 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
7724 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE,
7725 ("u32Signature=%08x\n", pDisk->u32Signature));
7726
7727 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
7728
7729 do {
7730 rc2 = vdThreadStartRead(pDisk);
7731 AssertRC(rc2);
7732 fLockRead = true;
7733
7734 /* Must have at least one image in the chain. */
7735 AssertMsgBreakStmt(pDisk->cImages >= 1, ("cImages=%u\n", pDisk->cImages),
7736 rc = VERR_VD_NOT_OPENED);
7737
7738 unsigned uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
7739 AssertMsgBreakStmt(!(uOpenFlags & VD_OPEN_FLAGS_READONLY),
7740 ("Last image should be read write"),
7741 rc = VERR_VD_IMAGE_READ_ONLY);
7742
7743 rc2 = vdThreadFinishRead(pDisk);
7744 AssertRC(rc2);
7745 fLockRead = false;
7746
7747 rc2 = vdThreadStartWrite(pDisk);
7748 AssertRC(rc2);
7749 fLockWrite = true;
7750
7751 /*
7752 * Open all images in the chain in read write mode first to avoid running
7753 * into an error in the middle of the process.
7754 */
7755 PVDIMAGE pImage = pDisk->pBase;
7756
7757 while (pImage)
7758 {
7759 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
7760 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
7761 {
7762 /*
7763 * Clear skip consistency checks because the image is made writable now and
7764 * skipping consistency checks is only possible for readonly images.
7765 */
7766 uOpenFlags &= ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS);
7767 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData, uOpenFlags);
7768 if (RT_FAILURE(rc))
7769 break;
7770 }
7771 pImage = pImage->pNext;
7772 }
7773
7774 if (RT_SUCCESS(rc))
7775 {
7776 unsigned cImgCur = 0;
7777 unsigned uPercentStart = 0;
7778 unsigned uPercentSpan = 100 / pDisk->cImages - 1;
7779
7780 /* Allocate tmp buffer. */
7781 void *pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
7782 if (!pvBuf)
7783 {
7784 rc = VERR_NO_MEMORY;
7785 break;
7786 }
7787
7788 pImage = pDisk->pBase;
7789 pDisk->fLocked = true;
7790
7791 while ( pImage
7792 && RT_SUCCESS(rc))
7793 {
7794 /* Get size of image. */
7795 uint64_t cbSize = vdImageGetSize(pImage);
7796 uint64_t cbSizeFile = pImage->Backend->pfnGetFileSize(pImage->pBackendData);
7797 uint64_t cbFileWritten = 0;
7798 uint64_t uOffset = 0;
7799 uint64_t cbRemaining = cbSize;
7800
7801 do
7802 {
7803 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
7804 RTSGSEG SegmentBuf;
7805 RTSGBUF SgBuf;
7806 VDIOCTX IoCtx;
7807
7808 SegmentBuf.pvSeg = pvBuf;
7809 SegmentBuf.cbSeg = VD_MERGE_BUFFER_SIZE;
7810 RTSgBufInit(&SgBuf, &SegmentBuf, 1);
7811 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_READ, 0, 0, NULL,
7812 &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
7813
7814 rc = pImage->Backend->pfnRead(pImage->pBackendData, uOffset,
7815 cbThisRead, &IoCtx, &cbThisRead);
7816 if (rc != VERR_VD_BLOCK_FREE)
7817 {
7818 if (RT_FAILURE(rc))
7819 break;
7820
7821 /* Apply filter chains. */
7822 rc = vdFilterChainApplyRead(pDisk, uOffset, cbThisRead, &IoCtx);
7823 if (RT_FAILURE(rc))
7824 break;
7825
7826 rc = vdFilterChainApplyWrite(pDisk, uOffset, cbThisRead, &IoCtx);
7827 if (RT_FAILURE(rc))
7828 break;
7829
7830 RTSgBufReset(&SgBuf);
7831 size_t cbThisWrite = 0;
7832 size_t cbPreRead = 0;
7833 size_t cbPostRead = 0;
7834 rc = pImage->Backend->pfnWrite(pImage->pBackendData, uOffset,
7835 cbThisRead, &IoCtx, &cbThisWrite,
7836 &cbPreRead, &cbPostRead, 0);
7837 if (RT_FAILURE(rc))
7838 break;
7839 Assert(cbThisWrite == cbThisRead);
7840 cbFileWritten += cbThisWrite;
7841 }
7842 else
7843 rc = VINF_SUCCESS;
7844
7845 uOffset += cbThisRead;
7846 cbRemaining -= cbThisRead;
7847
7848 if (pIfProgress && pIfProgress->pfnProgress)
7849 {
7850 rc2 = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
7851 uPercentStart + cbFileWritten * uPercentSpan / cbSizeFile);
7852 AssertRC(rc2); /* Cancelling this operation without leaving an inconsistent state is not possible. */
7853 }
7854 } while (uOffset < cbSize);
7855
7856 pImage = pImage->pNext;
7857 cImgCur++;
7858 uPercentStart += uPercentSpan;
7859 }
7860
7861 pDisk->fLocked = false;
7862 if (pvBuf)
7863 RTMemTmpFree(pvBuf);
7864 }
7865
7866 /* Change images except last one back to readonly. */
7867 pImage = pDisk->pBase;
7868 while ( pImage != pDisk->pLast
7869 && pImage)
7870 {
7871 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
7872 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
7873 rc2 = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData, uOpenFlags);
7874 if (RT_FAILURE(rc2))
7875 {
7876 if (RT_SUCCESS(rc))
7877 rc = rc2;
7878 break;
7879 }
7880 pImage = pImage->pNext;
7881 }
7882 } while (0);
7883
7884 if (RT_UNLIKELY(fLockWrite))
7885 {
7886 rc2 = vdThreadFinishWrite(pDisk);
7887 AssertRC(rc2);
7888 }
7889 else if (RT_UNLIKELY(fLockRead))
7890 {
7891 rc2 = vdThreadFinishRead(pDisk);
7892 AssertRC(rc2);
7893 }
7894
7895 if ( RT_SUCCESS(rc)
7896 && pIfProgress
7897 && pIfProgress->pfnProgress)
7898 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
7899
7900 LogFlowFunc(("returns %Rrc\n", rc));
7901 return rc;
7902}
7903
7904
7905VBOXDDU_DECL(int) VDClose(PVDISK pDisk, bool fDelete)
7906{
7907 int rc = VINF_SUCCESS;
7908 int rc2;
7909 bool fLockWrite = false;
7910
7911 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
7912 do
7913 {
7914 /* sanity check */
7915 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7916 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7917
7918 /* Not worth splitting this up into a read lock phase and write
7919 * lock phase, as closing an image is a relatively fast operation
7920 * dominated by the part which needs the write lock. */
7921 rc2 = vdThreadStartWrite(pDisk);
7922 AssertRC(rc2);
7923 fLockWrite = true;
7924
7925 PVDIMAGE pImage = pDisk->pLast;
7926 if (!pImage)
7927 {
7928 rc = VERR_VD_NOT_OPENED;
7929 break;
7930 }
7931
7932 /* Destroy the current discard state first which might still have pending blocks. */
7933 rc = vdDiscardStateDestroy(pDisk);
7934 if (RT_FAILURE(rc))
7935 break;
7936
7937 unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
7938 /* Remove image from list of opened images. */
7939 vdRemoveImageFromList(pDisk, pImage);
7940 /* Close (and optionally delete) image. */
7941 rc = pImage->Backend->pfnClose(pImage->pBackendData, fDelete);
7942 /* Free remaining resources related to the image. */
7943 RTStrFree(pImage->pszFilename);
7944 RTMemFree(pImage);
7945
7946 pImage = pDisk->pLast;
7947 if (!pImage)
7948 break;
7949
7950 /* If disk was previously in read/write mode, make sure it will stay
7951 * like this (if possible) after closing this image. Set the open flags
7952 * accordingly. */
7953 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
7954 {
7955 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
7956 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
7957 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData, uOpenFlags);
7958 }
7959
7960 /* Cache disk information. */
7961 pDisk->cbSize = vdImageGetSize(pImage);
7962
7963 /* Cache PCHS geometry. */
7964 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
7965 &pDisk->PCHSGeometry);
7966 if (RT_FAILURE(rc2))
7967 {
7968 pDisk->PCHSGeometry.cCylinders = 0;
7969 pDisk->PCHSGeometry.cHeads = 0;
7970 pDisk->PCHSGeometry.cSectors = 0;
7971 }
7972 else
7973 {
7974 /* Make sure the PCHS geometry is properly clipped. */
7975 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
7976 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
7977 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
7978 }
7979
7980 /* Cache LCHS geometry. */
7981 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
7982 &pDisk->LCHSGeometry);
7983 if (RT_FAILURE(rc2))
7984 {
7985 pDisk->LCHSGeometry.cCylinders = 0;
7986 pDisk->LCHSGeometry.cHeads = 0;
7987 pDisk->LCHSGeometry.cSectors = 0;
7988 }
7989 else
7990 {
7991 /* Make sure the LCHS geometry is properly clipped. */
7992 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
7993 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
7994 }
7995 } while (0);
7996
7997 if (RT_UNLIKELY(fLockWrite))
7998 {
7999 rc2 = vdThreadFinishWrite(pDisk);
8000 AssertRC(rc2);
8001 }
8002
8003 LogFlowFunc(("returns %Rrc\n", rc));
8004 return rc;
8005}
8006
8007
8008VBOXDDU_DECL(int) VDCacheClose(PVDISK pDisk, bool fDelete)
8009{
8010 int rc = VINF_SUCCESS;
8011 int rc2;
8012 bool fLockWrite = false;
8013 PVDCACHE pCache = NULL;
8014
8015 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
8016
8017 do
8018 {
8019 /* sanity check */
8020 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8021 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8022
8023 rc2 = vdThreadStartWrite(pDisk);
8024 AssertRC(rc2);
8025 fLockWrite = true;
8026
8027 AssertPtrBreakStmt(pDisk->pCache, rc = VERR_VD_CACHE_NOT_FOUND);
8028
8029 pCache = pDisk->pCache;
8030 pDisk->pCache = NULL;
8031
8032 pCache->Backend->pfnClose(pCache->pBackendData, fDelete);
8033 if (pCache->pszFilename)
8034 RTStrFree(pCache->pszFilename);
8035 RTMemFree(pCache);
8036 } while (0);
8037
8038 if (RT_LIKELY(fLockWrite))
8039 {
8040 rc2 = vdThreadFinishWrite(pDisk);
8041 AssertRC(rc2);
8042 }
8043
8044 LogFlowFunc(("returns %Rrc\n", rc));
8045 return rc;
8046}
8047
8048VBOXDDU_DECL(int) VDFilterRemove(PVDISK pDisk, uint32_t fFlags)
8049{
8050 int rc = VINF_SUCCESS;
8051 int rc2;
8052 bool fLockWrite = false;
8053 PVDFILTER pFilter = NULL;
8054
8055 LogFlowFunc(("pDisk=%#p\n", pDisk));
8056
8057 do
8058 {
8059 /* sanity check */
8060 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8061 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8062
8063 AssertMsgBreakStmt(!(fFlags & ~VD_FILTER_FLAGS_MASK),
8064 ("Invalid flags set (fFlags=%#x)\n", fFlags),
8065 rc = VERR_INVALID_PARAMETER);
8066
8067 rc2 = vdThreadStartWrite(pDisk);
8068 AssertRC(rc2);
8069 fLockWrite = true;
8070
8071 if (fFlags & VD_FILTER_FLAGS_WRITE)
8072 {
8073 AssertBreakStmt(!RTListIsEmpty(&pDisk->ListFilterChainWrite), rc = VERR_VD_NOT_OPENED);
8074 pFilter = RTListGetLast(&pDisk->ListFilterChainWrite, VDFILTER, ListNodeChainWrite);
8075 AssertPtr(pFilter);
8076 RTListNodeRemove(&pFilter->ListNodeChainWrite);
8077 vdFilterRelease(pFilter);
8078 }
8079
8080 if (fFlags & VD_FILTER_FLAGS_READ)
8081 {
8082 AssertBreakStmt(!RTListIsEmpty(&pDisk->ListFilterChainRead), rc = VERR_VD_NOT_OPENED);
8083 pFilter = RTListGetLast(&pDisk->ListFilterChainRead, VDFILTER, ListNodeChainRead);
8084 AssertPtr(pFilter);
8085 RTListNodeRemove(&pFilter->ListNodeChainRead);
8086 vdFilterRelease(pFilter);
8087 }
8088 } while (0);
8089
8090 if (RT_LIKELY(fLockWrite))
8091 {
8092 rc2 = vdThreadFinishWrite(pDisk);
8093 AssertRC(rc2);
8094 }
8095
8096 LogFlowFunc(("returns %Rrc\n", rc));
8097 return rc;
8098}
8099
8100
8101VBOXDDU_DECL(int) VDCloseAll(PVDISK pDisk)
8102{
8103 int rc = VINF_SUCCESS;
8104
8105 LogFlowFunc(("pDisk=%#p\n", pDisk));
8106 /* sanity check */
8107 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
8108 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8109
8110 /* Lock the entire operation. */
8111 int rc2 = vdThreadStartWrite(pDisk);
8112 AssertRC(rc2);
8113
8114 PVDCACHE pCache = pDisk->pCache;
8115 if (pCache)
8116 {
8117 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
8118 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
8119 rc = rc2;
8120
8121 if (pCache->pszFilename)
8122 RTStrFree(pCache->pszFilename);
8123 RTMemFree(pCache);
8124 }
8125
8126 PVDIMAGE pImage = pDisk->pLast;
8127 while (RT_VALID_PTR(pImage))
8128 {
8129 PVDIMAGE pPrev = pImage->pPrev;
8130 /* Remove image from list of opened images. */
8131 vdRemoveImageFromList(pDisk, pImage);
8132 /* Close image. */
8133 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
8134 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
8135 rc = rc2;
8136 /* Free remaining resources related to the image. */
8137 RTStrFree(pImage->pszFilename);
8138 RTMemFree(pImage);
8139 pImage = pPrev;
8140 }
8141 Assert(!RT_VALID_PTR(pDisk->pLast));
8142
8143 rc2 = vdThreadFinishWrite(pDisk);
8144 AssertRC(rc2);
8145
8146 LogFlowFunc(("returns %Rrc\n", rc));
8147 return rc;
8148}
8149
8150
8151VBOXDDU_DECL(int) VDFilterRemoveAll(PVDISK pDisk)
8152{
8153 LogFlowFunc(("pDisk=%#p\n", pDisk));
8154 /* sanity check */
8155 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
8156 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8157
8158 /* Lock the entire operation. */
8159 int rc2 = vdThreadStartWrite(pDisk);
8160 AssertRC(rc2);
8161
8162 PVDFILTER pFilter, pFilterNext;
8163 RTListForEachSafe(&pDisk->ListFilterChainWrite, pFilter, pFilterNext, VDFILTER, ListNodeChainWrite)
8164 {
8165 RTListNodeRemove(&pFilter->ListNodeChainWrite);
8166 vdFilterRelease(pFilter);
8167 }
8168
8169 RTListForEachSafe(&pDisk->ListFilterChainRead, pFilter, pFilterNext, VDFILTER, ListNodeChainRead)
8170 {
8171 RTListNodeRemove(&pFilter->ListNodeChainRead);
8172 vdFilterRelease(pFilter);
8173 }
8174 Assert(RTListIsEmpty(&pDisk->ListFilterChainRead));
8175 Assert(RTListIsEmpty(&pDisk->ListFilterChainWrite));
8176
8177 rc2 = vdThreadFinishWrite(pDisk);
8178 AssertRC(rc2);
8179
8180 LogFlowFunc(("returns %Rrc\n", VINF_SUCCESS));
8181 return VINF_SUCCESS;
8182}
8183
8184
8185VBOXDDU_DECL(int) VDRead(PVDISK pDisk, uint64_t uOffset, void *pvBuf,
8186 size_t cbRead)
8187{
8188 int rc = VINF_SUCCESS;
8189 int rc2;
8190 bool fLockRead = false;
8191
8192 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
8193 pDisk, uOffset, pvBuf, cbRead));
8194 /* sanity check */
8195 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
8196 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8197
8198 /* Check arguments. */
8199 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
8200 AssertReturn(cbRead > 0, VERR_INVALID_PARAMETER);
8201
8202 do
8203 {
8204 rc2 = vdThreadStartRead(pDisk);
8205 AssertRC(rc2);
8206 fLockRead = true;
8207
8208 AssertMsgBreakStmt( uOffset < pDisk->cbSize
8209 && cbRead <= pDisk->cbSize - uOffset,
8210 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
8211 uOffset, cbRead, pDisk->cbSize),
8212 rc = VERR_INVALID_PARAMETER);
8213
8214 PVDIMAGE pImage = pDisk->pLast;
8215 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
8216
8217 if (uOffset + cbRead > pDisk->cbSize)
8218 {
8219 /* Floppy images might be smaller than the standard expected by
8220 the floppy controller code. So, we won't fail here. */
8221 AssertMsgBreakStmt(pDisk->enmType == VDTYPE_FLOPPY,
8222 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
8223 uOffset, cbRead, pDisk->cbSize),
8224 rc = VERR_EOF);
8225 memset(pvBuf, 0xf6, cbRead); /* f6h = format.com filler byte */
8226 if (uOffset >= pDisk->cbSize)
8227 break;
8228 cbRead = pDisk->cbSize - uOffset;
8229 }
8230
8231 rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead,
8232 true /* fUpdateCache */);
8233 } while (0);
8234
8235 if (RT_UNLIKELY(fLockRead))
8236 {
8237 rc2 = vdThreadFinishRead(pDisk);
8238 AssertRC(rc2);
8239 }
8240
8241 LogFlowFunc(("returns %Rrc\n", rc));
8242 return rc;
8243}
8244
8245
8246VBOXDDU_DECL(int) VDWrite(PVDISK pDisk, uint64_t uOffset, const void *pvBuf,
8247 size_t cbWrite)
8248{
8249 int rc = VINF_SUCCESS;
8250 int rc2;
8251
8252 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
8253 pDisk, uOffset, pvBuf, cbWrite));
8254 /* sanity check */
8255 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
8256 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8257
8258 /* Check arguments. */
8259 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
8260 AssertReturn(cbWrite > 0, VERR_INVALID_PARAMETER);
8261
8262 do
8263 {
8264 rc2 = vdThreadStartWrite(pDisk);
8265 AssertRC(rc2);
8266
8267 AssertMsgBreakStmt( uOffset < pDisk->cbSize
8268 && cbWrite <= pDisk->cbSize - uOffset,
8269 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
8270 uOffset, cbWrite, pDisk->cbSize),
8271 rc = VERR_INVALID_PARAMETER);
8272
8273 PVDIMAGE pImage = pDisk->pLast;
8274 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
8275
8276 vdSetModifiedFlag(pDisk);
8277 rc = vdWriteHelper(pDisk, pImage, uOffset, pvBuf, cbWrite,
8278 VDIOCTX_FLAGS_READ_UPDATE_CACHE);
8279 if (RT_FAILURE(rc))
8280 break;
8281
8282 /* If there is a merge (in the direction towards a parent) running
8283 * concurrently then we have to also "relay" the write to this parent,
8284 * as the merge position might be already past the position where
8285 * this write is going. The "context" of the write can come from the
8286 * natural chain, since merging either already did or will take care
8287 * of the "other" content which is might be needed to fill the block
8288 * to a full allocation size. The cache doesn't need to be touched
8289 * as this write is covered by the previous one. */
8290 if (RT_UNLIKELY(pDisk->pImageRelay))
8291 rc = vdWriteHelper(pDisk, pDisk->pImageRelay, uOffset,
8292 pvBuf, cbWrite, VDIOCTX_FLAGS_DEFAULT);
8293 } while (0);
8294
8295 rc2 = vdThreadFinishWrite(pDisk);
8296 AssertRC(rc2);
8297
8298 LogFlowFunc(("returns %Rrc\n", rc));
8299 return rc;
8300}
8301
8302
8303VBOXDDU_DECL(int) VDFlush(PVDISK pDisk)
8304{
8305 int rc = VINF_SUCCESS;
8306 int rc2;
8307
8308 LogFlowFunc(("pDisk=%#p\n", pDisk));
8309 /* sanity check */
8310 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
8311 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8312
8313 do
8314 {
8315 rc2 = vdThreadStartWrite(pDisk);
8316 AssertRC(rc2);
8317
8318 PVDIMAGE pImage = pDisk->pLast;
8319 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
8320
8321 VDIOCTX IoCtx;
8322 RTSEMEVENT hEventComplete = NIL_RTSEMEVENT;
8323
8324 rc = RTSemEventCreate(&hEventComplete);
8325 if (RT_FAILURE(rc))
8326 break;
8327
8328 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_FLUSH, 0, 0, pImage, NULL,
8329 NULL, vdFlushHelperAsync, VDIOCTX_FLAGS_SYNC | VDIOCTX_FLAGS_DONT_FREE);
8330
8331 IoCtx.Type.Root.pfnComplete = vdIoCtxSyncComplete;
8332 IoCtx.Type.Root.pvUser1 = pDisk;
8333 IoCtx.Type.Root.pvUser2 = hEventComplete;
8334 rc = vdIoCtxProcessSync(&IoCtx, hEventComplete);
8335
8336 RTSemEventDestroy(hEventComplete);
8337 } while (0);
8338
8339 rc2 = vdThreadFinishWrite(pDisk);
8340 AssertRC(rc2);
8341
8342 LogFlowFunc(("returns %Rrc\n", rc));
8343 return rc;
8344}
8345
8346
8347VBOXDDU_DECL(unsigned) VDGetCount(PVDISK pDisk)
8348{
8349 LogFlowFunc(("pDisk=%#p\n", pDisk));
8350
8351 /* sanity check */
8352 AssertPtrReturn(pDisk, 0);
8353 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8354
8355 int rc2 = vdThreadStartRead(pDisk);
8356 AssertRC(rc2);
8357
8358 unsigned cImages = pDisk->cImages;
8359
8360 rc2 = vdThreadFinishRead(pDisk);
8361 AssertRC(rc2);
8362
8363 LogFlowFunc(("returns %u\n", cImages));
8364 return cImages;
8365}
8366
8367
8368VBOXDDU_DECL(bool) VDIsReadOnly(PVDISK pDisk)
8369{
8370 LogFlowFunc(("pDisk=%#p\n", pDisk));
8371 /* sanity check */
8372 AssertPtrReturn(pDisk, true);
8373 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8374
8375 int rc2 = vdThreadStartRead(pDisk);
8376 AssertRC(rc2);
8377
8378 bool fReadOnly = true;
8379 PVDIMAGE pImage = pDisk->pLast;
8380 AssertPtr(pImage);
8381 if (pImage)
8382 {
8383 unsigned uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
8384 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
8385 }
8386
8387 rc2 = vdThreadFinishRead(pDisk);
8388 AssertRC(rc2);
8389
8390 LogFlowFunc(("returns %d\n", fReadOnly));
8391 return fReadOnly;
8392}
8393
8394
8395VBOXDDU_DECL(uint32_t) VDGetSectorSize(PVDISK pDisk, unsigned nImage)
8396{
8397 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
8398 /* sanity check */
8399 AssertPtrReturn(pDisk, 0);
8400 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8401
8402 /* Do the job. */
8403 int rc2 = vdThreadStartRead(pDisk);
8404 AssertRC(rc2);
8405
8406 uint64_t cbSector = 0;
8407 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8408 AssertPtr(pImage);
8409 if (pImage)
8410 {
8411 PCVDREGIONLIST pRegionList = NULL;
8412 int rc = pImage->Backend->pfnQueryRegions(pImage->pBackendData, &pRegionList);
8413 if (RT_SUCCESS(rc))
8414 {
8415 AssertMsg(pRegionList->cRegions == 1, ("%u\n", pRegionList->cRegions));
8416 if (pRegionList->cRegions == 1)
8417 {
8418 cbSector = pRegionList->aRegions[0].cbBlock;
8419
8420 AssertPtr(pImage->Backend->pfnRegionListRelease);
8421 pImage->Backend->pfnRegionListRelease(pImage->pBackendData, pRegionList);
8422 }
8423 }
8424 }
8425
8426 rc2 = vdThreadFinishRead(pDisk);
8427 AssertRC(rc2);
8428
8429 LogFlowFunc(("returns %u\n", cbSector));
8430 return cbSector;
8431}
8432
8433
8434VBOXDDU_DECL(uint64_t) VDGetSize(PVDISK pDisk, unsigned nImage)
8435{
8436 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
8437 /* sanity check */
8438 AssertPtrReturn(pDisk, 0);
8439 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8440
8441 /* Do the job. */
8442 int rc2 = vdThreadStartRead(pDisk);
8443 AssertRC(rc2);
8444
8445 uint64_t cbSize;
8446 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8447 AssertPtr(pImage);
8448 if (pImage)
8449 cbSize = vdImageGetSize(pImage);
8450 else
8451 cbSize = 0;
8452
8453 rc2 = vdThreadFinishRead(pDisk);
8454 AssertRC(rc2);
8455
8456 LogFlowFunc(("returns %llu (%#RX64)\n", cbSize, cbSize));
8457 return cbSize;
8458}
8459
8460
8461VBOXDDU_DECL(uint64_t) VDGetFileSize(PVDISK pDisk, unsigned nImage)
8462{
8463 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
8464
8465 /* sanity check */
8466 AssertPtrReturn(pDisk, 0);
8467 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8468
8469 int rc2 = vdThreadStartRead(pDisk);
8470 AssertRC(rc2);
8471
8472 uint64_t cbSize = 0;
8473 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8474 AssertPtr(pImage);
8475 if (pImage)
8476 cbSize = pImage->Backend->pfnGetFileSize(pImage->pBackendData);
8477
8478 rc2 = vdThreadFinishRead(pDisk);
8479 AssertRC(rc2);
8480
8481 LogFlowFunc(("returns %llu (%#RX64)\n", cbSize, cbSize));
8482 return cbSize;
8483}
8484
8485
8486VBOXDDU_DECL(int) VDGetPCHSGeometry(PVDISK pDisk, unsigned nImage,
8487 PVDGEOMETRY pPCHSGeometry)
8488{
8489 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
8490 pDisk, nImage, pPCHSGeometry));
8491 /* sanity check */
8492 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
8493 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8494
8495 /* Check arguments. */
8496 AssertPtrReturn(pPCHSGeometry, VERR_INVALID_POINTER);
8497
8498 int rc2 = vdThreadStartRead(pDisk);
8499 AssertRC(rc2);
8500
8501 int rc;
8502 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8503 AssertPtr(pImage);
8504 if (pImage)
8505 {
8506 if (pImage == pDisk->pLast)
8507 {
8508 /* Use cached information if possible. */
8509 if (pDisk->PCHSGeometry.cCylinders != 0)
8510 {
8511 *pPCHSGeometry = pDisk->PCHSGeometry;
8512 rc = VINF_SUCCESS;
8513 }
8514 else
8515 rc = VERR_VD_GEOMETRY_NOT_SET;
8516 }
8517 else
8518 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData, pPCHSGeometry);
8519 }
8520 else
8521 rc = VERR_VD_IMAGE_NOT_FOUND;
8522
8523 rc2 = vdThreadFinishRead(pDisk);
8524 AssertRC(rc2);
8525
8526 LogFlowFunc(("%Rrc (PCHS=%u/%u/%u)\n", rc,
8527 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
8528 pDisk->PCHSGeometry.cSectors));
8529 return rc;
8530}
8531
8532
8533VBOXDDU_DECL(int) VDSetPCHSGeometry(PVDISK pDisk, unsigned nImage,
8534 PCVDGEOMETRY pPCHSGeometry)
8535{
8536 int rc = VINF_SUCCESS;
8537 int rc2;
8538
8539 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
8540 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
8541 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
8542 /* sanity check */
8543 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
8544 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8545
8546 /* Check arguments. */
8547 AssertPtrReturn(pPCHSGeometry, VERR_INVALID_POINTER);
8548 AssertMsgReturn( pPCHSGeometry->cHeads <= 16
8549 && pPCHSGeometry->cSectors <= 63,
8550 ("PCHS=%u/%u/%u\n", pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors),
8551 VERR_INVALID_PARAMETER);
8552 do
8553 {
8554 rc2 = vdThreadStartWrite(pDisk);
8555 AssertRC(rc2);
8556
8557 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8558 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8559
8560 if (pImage == pDisk->pLast)
8561 {
8562 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
8563 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
8564 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
8565 {
8566 /* Only update geometry if it is changed. Avoids similar checks
8567 * in every backend. Most of the time the new geometry is set
8568 * to the previous values, so no need to go through the hassle
8569 * of updating an image which could be opened in read-only mode
8570 * right now. */
8571 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
8572 pPCHSGeometry);
8573
8574 /* Cache new geometry values in any case. */
8575 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
8576 &pDisk->PCHSGeometry);
8577 if (RT_FAILURE(rc2))
8578 {
8579 pDisk->PCHSGeometry.cCylinders = 0;
8580 pDisk->PCHSGeometry.cHeads = 0;
8581 pDisk->PCHSGeometry.cSectors = 0;
8582 }
8583 else
8584 {
8585 /* Make sure the CHS geometry is properly clipped. */
8586 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
8587 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
8588 }
8589 }
8590 }
8591 else
8592 {
8593 VDGEOMETRY PCHS;
8594 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
8595 &PCHS);
8596 if ( RT_FAILURE(rc)
8597 || pPCHSGeometry->cCylinders != PCHS.cCylinders
8598 || pPCHSGeometry->cHeads != PCHS.cHeads
8599 || pPCHSGeometry->cSectors != PCHS.cSectors)
8600 {
8601 /* Only update geometry if it is changed. Avoids similar checks
8602 * in every backend. Most of the time the new geometry is set
8603 * to the previous values, so no need to go through the hassle
8604 * of updating an image which could be opened in read-only mode
8605 * right now. */
8606 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
8607 pPCHSGeometry);
8608 }
8609 }
8610 } while (0);
8611
8612 rc2 = vdThreadFinishWrite(pDisk);
8613 AssertRC(rc2);
8614
8615 LogFlowFunc(("returns %Rrc\n", rc));
8616 return rc;
8617}
8618
8619
8620VBOXDDU_DECL(int) VDGetLCHSGeometry(PVDISK pDisk, unsigned nImage,
8621 PVDGEOMETRY pLCHSGeometry)
8622{
8623 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
8624 pDisk, nImage, pLCHSGeometry));
8625 /* sanity check */
8626 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
8627 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8628
8629 /* Check arguments. */
8630 AssertPtrReturn(pLCHSGeometry, VERR_INVALID_POINTER);
8631
8632 int rc2 = vdThreadStartRead(pDisk);
8633 AssertRC(rc2);
8634
8635 int rc = VINF_SUCCESS;
8636 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8637 AssertPtr(pImage);
8638 if (pImage)
8639 {
8640 if (pImage == pDisk->pLast)
8641 {
8642 /* Use cached information if possible. */
8643 if (pDisk->LCHSGeometry.cCylinders != 0)
8644 *pLCHSGeometry = pDisk->LCHSGeometry;
8645 else
8646 rc = VERR_VD_GEOMETRY_NOT_SET;
8647 }
8648 else
8649 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData, pLCHSGeometry);
8650 }
8651 else
8652 rc = VERR_VD_IMAGE_NOT_FOUND;
8653
8654 rc2 = vdThreadFinishRead(pDisk);
8655 AssertRC(rc2);
8656
8657 LogFlowFunc((": %Rrc (LCHS=%u/%u/%u)\n", rc,
8658 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
8659 pDisk->LCHSGeometry.cSectors));
8660 return rc;
8661}
8662
8663
8664VBOXDDU_DECL(int) VDSetLCHSGeometry(PVDISK pDisk, unsigned nImage,
8665 PCVDGEOMETRY pLCHSGeometry)
8666{
8667 int rc = VINF_SUCCESS;
8668 int rc2;
8669
8670 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
8671 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
8672 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
8673 /* sanity check */
8674 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
8675 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8676
8677 /* Check arguments. */
8678 AssertPtrReturn(pLCHSGeometry, VERR_INVALID_POINTER);
8679 AssertMsgReturn( pLCHSGeometry->cHeads <= 255
8680 && pLCHSGeometry->cSectors <= 63,
8681 ("LCHS=%u/%u/%u\n", pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors),
8682 VERR_INVALID_PARAMETER);
8683
8684 do
8685 {
8686 rc2 = vdThreadStartWrite(pDisk);
8687 AssertRC(rc2);
8688
8689 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8690 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8691
8692 if (pImage == pDisk->pLast)
8693 {
8694 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
8695 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
8696 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
8697 {
8698 /* Only update geometry if it is changed. Avoids similar checks
8699 * in every backend. Most of the time the new geometry is set
8700 * to the previous values, so no need to go through the hassle
8701 * of updating an image which could be opened in read-only mode
8702 * right now. */
8703 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
8704 pLCHSGeometry);
8705
8706 /* Cache new geometry values in any case. */
8707 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
8708 &pDisk->LCHSGeometry);
8709 if (RT_FAILURE(rc2))
8710 {
8711 pDisk->LCHSGeometry.cCylinders = 0;
8712 pDisk->LCHSGeometry.cHeads = 0;
8713 pDisk->LCHSGeometry.cSectors = 0;
8714 }
8715 else
8716 {
8717 /* Make sure the CHS geometry is properly clipped. */
8718 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
8719 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
8720 }
8721 }
8722 }
8723 else
8724 {
8725 VDGEOMETRY LCHS;
8726 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
8727 &LCHS);
8728 if ( RT_FAILURE(rc)
8729 || pLCHSGeometry->cCylinders != LCHS.cCylinders
8730 || pLCHSGeometry->cHeads != LCHS.cHeads
8731 || pLCHSGeometry->cSectors != LCHS.cSectors)
8732 {
8733 /* Only update geometry if it is changed. Avoids similar checks
8734 * in every backend. Most of the time the new geometry is set
8735 * to the previous values, so no need to go through the hassle
8736 * of updating an image which could be opened in read-only mode
8737 * right now. */
8738 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
8739 pLCHSGeometry);
8740 }
8741 }
8742 } while (0);
8743
8744 rc2 = vdThreadFinishWrite(pDisk);
8745 AssertRC(rc2);
8746
8747 LogFlowFunc(("returns %Rrc\n", rc));
8748 return rc;
8749}
8750
8751
8752VBOXDDU_DECL(int) VDQueryRegions(PVDISK pDisk, unsigned nImage, uint32_t fFlags,
8753 PPVDREGIONLIST ppRegionList)
8754{
8755 LogFlowFunc(("pDisk=%#p nImage=%u fFlags=%#x ppRegionList=%#p\n",
8756 pDisk, nImage, fFlags, ppRegionList));
8757 /* sanity check */
8758 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
8759 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8760
8761 /* Check arguments. */
8762 AssertPtrReturn(ppRegionList, VERR_INVALID_POINTER);
8763
8764 int rc2 = vdThreadStartRead(pDisk);
8765 AssertRC(rc2);
8766
8767 int rc;
8768 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8769 AssertPtr(pImage);
8770 if (pImage)
8771 {
8772 PCVDREGIONLIST pRegionList = NULL;
8773 rc = pImage->Backend->pfnQueryRegions(pImage->pBackendData, &pRegionList);
8774 if (RT_SUCCESS(rc))
8775 {
8776 rc = vdRegionListConv(pRegionList, fFlags, ppRegionList);
8777
8778 AssertPtr(pImage->Backend->pfnRegionListRelease);
8779 pImage->Backend->pfnRegionListRelease(pImage->pBackendData, pRegionList);
8780 }
8781 }
8782 else
8783 rc = VERR_VD_IMAGE_NOT_FOUND;
8784
8785 rc2 = vdThreadFinishRead(pDisk);
8786 AssertRC(rc2);
8787
8788 LogFlowFunc((": %Rrc\n", rc));
8789 return rc;
8790}
8791
8792
8793VBOXDDU_DECL(void) VDRegionListFree(PVDREGIONLIST pRegionList)
8794{
8795 RTMemFree(pRegionList);
8796}
8797
8798
8799VBOXDDU_DECL(int) VDGetVersion(PVDISK pDisk, unsigned nImage,
8800 unsigned *puVersion)
8801{
8802 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
8803 pDisk, nImage, puVersion));
8804 /* sanity check */
8805 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
8806 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8807
8808 /* Check arguments. */
8809 AssertPtrReturn(puVersion, VERR_INVALID_POINTER);
8810
8811 int rc2 = vdThreadStartRead(pDisk);
8812 AssertRC(rc2);
8813
8814 int rc = VINF_SUCCESS;
8815 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8816 AssertPtr(pImage);
8817 if (pImage)
8818 *puVersion = pImage->Backend->pfnGetVersion(pImage->pBackendData);
8819 else
8820 rc = VERR_VD_IMAGE_NOT_FOUND;
8821
8822 rc2 = vdThreadFinishRead(pDisk);
8823 AssertRC(rc2);
8824
8825 LogFlowFunc(("returns %Rrc uVersion=%#x\n", rc, *puVersion));
8826 return rc;
8827}
8828
8829
8830VBOXDDU_DECL(int) VDBackendInfoSingle(PVDISK pDisk, unsigned nImage,
8831 PVDBACKENDINFO pBackendInfo)
8832{
8833 int rc = VINF_SUCCESS;
8834
8835 LogFlowFunc(("pDisk=%#p nImage=%u pBackendInfo=%#p\n",
8836 pDisk, nImage, pBackendInfo));
8837 /* sanity check */
8838 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
8839 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8840
8841 /* Check arguments. */
8842 AssertPtrReturn(pBackendInfo, VERR_INVALID_POINTER);
8843
8844 /* Do the job. */
8845 int rc2 = vdThreadStartRead(pDisk);
8846 AssertRC(rc2);
8847
8848 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8849 AssertPtr(pImage);
8850 if (pImage)
8851 {
8852 pBackendInfo->pszBackend = pImage->Backend->pszBackendName;
8853 pBackendInfo->uBackendCaps = pImage->Backend->uBackendCaps;
8854 pBackendInfo->paFileExtensions = pImage->Backend->paFileExtensions;
8855 pBackendInfo->paConfigInfo = pImage->Backend->paConfigInfo;
8856 }
8857 else
8858 rc = VERR_VD_IMAGE_NOT_FOUND;
8859
8860 rc2 = vdThreadFinishRead(pDisk);
8861 AssertRC(rc2);
8862
8863 LogFlowFunc(("returns %Rrc\n", rc));
8864 return rc;
8865}
8866
8867
8868VBOXDDU_DECL(int) VDGetImageFlags(PVDISK pDisk, unsigned nImage,
8869 unsigned *puImageFlags)
8870{
8871 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
8872 pDisk, nImage, puImageFlags));
8873 /* sanity check */
8874 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
8875 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8876
8877 /* Check arguments. */
8878 AssertPtrReturn(puImageFlags, VERR_INVALID_POINTER);
8879
8880 /* Do the job. */
8881 int rc2 = vdThreadStartRead(pDisk);
8882 AssertRC(rc2);
8883
8884 int rc = VINF_SUCCESS;
8885 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8886 AssertPtr(pImage);
8887 if (pImage)
8888 *puImageFlags = pImage->uImageFlags;
8889 else
8890 rc = VERR_VD_IMAGE_NOT_FOUND;
8891
8892 rc2 = vdThreadFinishRead(pDisk);
8893 AssertRC(rc2);
8894
8895 LogFlowFunc(("returns %Rrc uImageFlags=%#x\n", rc, *puImageFlags));
8896 return rc;
8897}
8898
8899
8900VBOXDDU_DECL(int) VDGetOpenFlags(PVDISK pDisk, unsigned nImage,
8901 unsigned *puOpenFlags)
8902{
8903 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
8904 pDisk, nImage, puOpenFlags));
8905 /* sanity check */
8906 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
8907 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8908
8909 /* Check arguments. */
8910 AssertPtrReturn(puOpenFlags, VERR_INVALID_POINTER);
8911
8912 /* Do the job. */
8913 int rc2 = vdThreadStartRead(pDisk);
8914 AssertRC(rc2);
8915
8916 int rc = VINF_SUCCESS;
8917 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8918 AssertPtr(pImage);
8919 if (pImage)
8920 *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
8921 else
8922 rc = VERR_VD_IMAGE_NOT_FOUND;
8923
8924 rc2 = vdThreadFinishRead(pDisk);
8925 AssertRC(rc2);
8926
8927 LogFlowFunc(("returns %Rrc uOpenFlags=%#x\n", rc, *puOpenFlags));
8928 return rc;
8929}
8930
8931
8932VBOXDDU_DECL(int) VDSetOpenFlags(PVDISK pDisk, unsigned nImage,
8933 unsigned uOpenFlags)
8934{
8935 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
8936 /* sanity check */
8937 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
8938 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8939
8940 /* Check arguments. */
8941 AssertMsgReturn((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0, ("uOpenFlags=%#x\n", uOpenFlags),
8942 VERR_INVALID_PARAMETER);
8943
8944 /* Do the job. */
8945 int rc2 = vdThreadStartWrite(pDisk);
8946 AssertRC(rc2);
8947
8948 /* Destroy any discard state because the image might be changed to readonly mode. */
8949 int rc = vdDiscardStateDestroy(pDisk);
8950 if (RT_SUCCESS(rc))
8951 {
8952 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8953 AssertPtr(pImage);
8954 if (pImage)
8955 {
8956 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData,
8957 uOpenFlags & ~(VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_IGNORE_FLUSH
8958 | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS));
8959 if (RT_SUCCESS(rc))
8960 pImage->uOpenFlags = uOpenFlags & (VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_DISCARD | VD_OPEN_FLAGS_IGNORE_FLUSH
8961 | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS);
8962 }
8963 else
8964 rc = VERR_VD_IMAGE_NOT_FOUND;
8965 }
8966
8967 rc2 = vdThreadFinishWrite(pDisk);
8968 AssertRC(rc2);
8969
8970 LogFlowFunc(("returns %Rrc\n", rc));
8971 return rc;
8972}
8973
8974
8975VBOXDDU_DECL(int) VDGetFilename(PVDISK pDisk, unsigned nImage,
8976 char *pszFilename, unsigned cbFilename)
8977{
8978 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
8979 pDisk, nImage, pszFilename, cbFilename));
8980 /* sanity check */
8981 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
8982 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8983
8984 /* Check arguments. */
8985 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
8986 AssertReturn(cbFilename > 0, VERR_INVALID_PARAMETER);
8987
8988 /* Do the job. */
8989 int rc2 = vdThreadStartRead(pDisk);
8990 AssertRC(rc2);
8991
8992 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8993 int rc;
8994 if (pImage)
8995 rc = RTStrCopy(pszFilename, cbFilename, pImage->pszFilename);
8996 else
8997 rc = VERR_VD_IMAGE_NOT_FOUND;
8998
8999 rc2 = vdThreadFinishRead(pDisk);
9000 AssertRC(rc2);
9001
9002 LogFlowFunc(("returns %Rrc, pszFilename=\"%s\"\n", rc, pszFilename));
9003 return rc;
9004}
9005
9006
9007VBOXDDU_DECL(int) VDGetComment(PVDISK pDisk, unsigned nImage,
9008 char *pszComment, unsigned cbComment)
9009{
9010 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
9011 pDisk, nImage, pszComment, cbComment));
9012 /* sanity check */
9013 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
9014 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9015
9016 /* Check arguments. */
9017 AssertPtrReturn(pszComment, VERR_INVALID_POINTER);
9018 AssertReturn(cbComment > 0, VERR_INVALID_PARAMETER);
9019
9020 /* Do the job. */
9021 int rc2 = vdThreadStartRead(pDisk);
9022 AssertRC(rc2);
9023
9024 int rc;
9025 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9026 AssertPtr(pImage);
9027 if (pImage)
9028 rc = pImage->Backend->pfnGetComment(pImage->pBackendData, pszComment, cbComment);
9029 else
9030 rc = VERR_VD_IMAGE_NOT_FOUND;
9031
9032 rc2 = vdThreadFinishRead(pDisk);
9033 AssertRC(rc2);
9034
9035 LogFlowFunc(("returns %Rrc, pszComment=\"%s\"\n", rc, pszComment));
9036 return rc;
9037}
9038
9039
9040VBOXDDU_DECL(int) VDSetComment(PVDISK pDisk, unsigned nImage,
9041 const char *pszComment)
9042{
9043 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
9044 pDisk, nImage, pszComment, pszComment));
9045 /* sanity check */
9046 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
9047 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9048
9049 /* Check arguments. */
9050 AssertPtrNullReturn(pszComment, VERR_INVALID_POINTER);
9051
9052 /* Do the job. */
9053 int rc2 = vdThreadStartWrite(pDisk);
9054 AssertRC(rc2);
9055
9056 int rc;
9057 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9058 AssertPtr(pImage);
9059 if (pImage)
9060 rc = pImage->Backend->pfnSetComment(pImage->pBackendData, pszComment);
9061 else
9062 rc = VERR_VD_IMAGE_NOT_FOUND;
9063
9064 rc2 = vdThreadFinishWrite(pDisk);
9065 AssertRC(rc2);
9066
9067 LogFlowFunc(("returns %Rrc\n", rc));
9068 return rc;
9069}
9070
9071
9072VBOXDDU_DECL(int) VDGetUuid(PVDISK pDisk, unsigned nImage, PRTUUID pUuid)
9073{
9074 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
9075 /* sanity check */
9076 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
9077 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9078
9079 /* Check arguments. */
9080 AssertPtrReturn(pUuid, VERR_INVALID_POINTER);
9081
9082 /* Do the job. */
9083 int rc2 = vdThreadStartRead(pDisk);
9084 AssertRC(rc2);
9085
9086 int rc;
9087 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9088 AssertPtr(pImage);
9089 if (pImage)
9090 rc = pImage->Backend->pfnGetUuid(pImage->pBackendData, pUuid);
9091 else
9092 rc = VERR_VD_IMAGE_NOT_FOUND;
9093
9094 rc2 = vdThreadFinishRead(pDisk);
9095 AssertRC(rc2);
9096
9097 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
9098 return rc;
9099}
9100
9101
9102VBOXDDU_DECL(int) VDSetUuid(PVDISK pDisk, unsigned nImage, PCRTUUID pUuid)
9103{
9104 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
9105 pDisk, nImage, pUuid, pUuid));
9106 /* sanity check */
9107 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
9108 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9109
9110 /* Check arguments. */
9111 RTUUID Uuid;
9112 if (pUuid)
9113 AssertPtrReturn(pUuid, VERR_INVALID_POINTER);
9114 else
9115 {
9116 int rc = RTUuidCreate(&Uuid);
9117 AssertRCReturn(rc, rc);
9118 pUuid = &Uuid;
9119 }
9120
9121 /* Do the job. */
9122 int rc2 = vdThreadStartWrite(pDisk);
9123 AssertRC(rc2);
9124
9125 int rc;
9126 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9127 AssertPtr(pImage);
9128 if (pImage)
9129 rc = pImage->Backend->pfnSetUuid(pImage->pBackendData, pUuid);
9130 else
9131 rc = VERR_VD_IMAGE_NOT_FOUND;
9132
9133 rc2 = vdThreadFinishWrite(pDisk);
9134 AssertRC(rc2);
9135
9136 LogFlowFunc(("returns %Rrc\n", rc));
9137 return rc;
9138}
9139
9140
9141VBOXDDU_DECL(int) VDGetModificationUuid(PVDISK pDisk, unsigned nImage, PRTUUID pUuid)
9142{
9143 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
9144 /* sanity check */
9145 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
9146 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9147
9148 /* Check arguments. */
9149 AssertPtrReturn(pUuid, VERR_INVALID_POINTER);
9150
9151 /* Do the job. */
9152 int rc2 = vdThreadStartRead(pDisk);
9153 AssertRC(rc2);
9154
9155 int rc;
9156 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9157 AssertPtr(pImage);
9158 if (pImage)
9159 rc = pImage->Backend->pfnGetModificationUuid(pImage->pBackendData, pUuid);
9160 else
9161 rc = VERR_VD_IMAGE_NOT_FOUND;
9162
9163 rc2 = vdThreadFinishRead(pDisk);
9164 AssertRC(rc2);
9165
9166 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
9167 return rc;
9168}
9169
9170
9171VBOXDDU_DECL(int) VDSetModificationUuid(PVDISK pDisk, unsigned nImage, PCRTUUID pUuid)
9172{
9173 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
9174 pDisk, nImage, pUuid, pUuid));
9175 /* sanity check */
9176 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
9177 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9178
9179 /* Check arguments. */
9180 RTUUID Uuid;
9181 if (pUuid)
9182 AssertPtrReturn(pUuid, VERR_INVALID_POINTER);
9183 else
9184 {
9185 int rc = RTUuidCreate(&Uuid);
9186 AssertRCReturn(rc, rc);
9187 pUuid = &Uuid;
9188 }
9189
9190 /* Do the job. */
9191 int rc2 = vdThreadStartWrite(pDisk);
9192 AssertRC(rc2);
9193
9194 int rc;
9195 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9196 if (pImage)
9197 rc = pImage->Backend->pfnSetModificationUuid(pImage->pBackendData, pUuid);
9198 else
9199 rc = VERR_VD_IMAGE_NOT_FOUND;
9200
9201 rc2 = vdThreadFinishWrite(pDisk);
9202 AssertRC(rc2);
9203
9204 LogFlowFunc(("returns %Rrc\n", rc));
9205 return rc;
9206}
9207
9208
9209VBOXDDU_DECL(int) VDGetParentUuid(PVDISK pDisk, unsigned nImage,
9210 PRTUUID pUuid)
9211{
9212 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
9213 /* sanity check */
9214 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
9215 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9216
9217 /* Check arguments. */
9218 AssertPtrReturn(pUuid, VERR_INVALID_POINTER);
9219
9220 /* Do the job. */
9221 int rc2 = vdThreadStartRead(pDisk);
9222 AssertRC(rc2);
9223
9224 int rc;
9225 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9226 AssertPtr(pImage);
9227 if (pImage)
9228 rc = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, pUuid);
9229 else
9230 rc = VERR_VD_IMAGE_NOT_FOUND;
9231
9232 rc2 = vdThreadFinishRead(pDisk);
9233 AssertRC(rc2);
9234
9235 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
9236 return rc;
9237}
9238
9239
9240VBOXDDU_DECL(int) VDSetParentUuid(PVDISK pDisk, unsigned nImage,
9241 PCRTUUID pUuid)
9242{
9243 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
9244 pDisk, nImage, pUuid, pUuid));
9245 /* sanity check */
9246 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
9247 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9248
9249 /* Check arguments. */
9250 RTUUID Uuid;
9251 if (pUuid)
9252 AssertPtrReturn(pUuid, VERR_INVALID_POINTER);
9253 else
9254 {
9255 int rc = RTUuidCreate(&Uuid);
9256 AssertRCReturn(rc, rc);
9257 pUuid = &Uuid;
9258 }
9259
9260 /* Do the job. */
9261 int rc2 = vdThreadStartWrite(pDisk);
9262 AssertRC(rc2);
9263
9264 int rc;
9265 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9266 AssertPtr(pImage);
9267 if (pImage)
9268 rc = pImage->Backend->pfnSetParentUuid(pImage->pBackendData, pUuid);
9269 else
9270 rc = VERR_VD_IMAGE_NOT_FOUND;
9271
9272 rc2 = vdThreadFinishWrite(pDisk);
9273 AssertRC(rc2);
9274
9275 LogFlowFunc(("returns %Rrc\n", rc));
9276 return rc;
9277}
9278
9279
9280VBOXDDU_DECL(void) VDDumpImages(PVDISK pDisk)
9281{
9282 /* sanity check */
9283 AssertPtrReturnVoid(pDisk);
9284 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9285
9286 AssertPtrReturnVoid(pDisk->pInterfaceError);
9287 if (!RT_VALID_PTR(pDisk->pInterfaceError->pfnMessage))
9288 pDisk->pInterfaceError->pfnMessage = vdLogMessage;
9289
9290 int rc2 = vdThreadStartRead(pDisk);
9291 AssertRC(rc2);
9292
9293 vdMessageWrapper(pDisk, "--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
9294 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
9295 {
9296 vdMessageWrapper(pDisk, "Dumping VD image \"%s\" (Backend=%s)\n",
9297 pImage->pszFilename, pImage->Backend->pszBackendName);
9298 pImage->Backend->pfnDump(pImage->pBackendData);
9299 }
9300
9301 rc2 = vdThreadFinishRead(pDisk);
9302 AssertRC(rc2);
9303}
9304
9305
9306VBOXDDU_DECL(int) VDDiscardRanges(PVDISK pDisk, PCRTRANGE paRanges, unsigned cRanges)
9307{
9308 int rc;
9309 int rc2;
9310
9311 LogFlowFunc(("pDisk=%#p paRanges=%#p cRanges=%u\n",
9312 pDisk, paRanges, cRanges));
9313 /* sanity check */
9314 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
9315 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9316
9317 /* Check arguments. */
9318 AssertReturn(cRanges > 0, VERR_INVALID_PARAMETER);
9319 AssertPtrReturn(paRanges, VERR_INVALID_POINTER);
9320
9321 do
9322 {
9323 rc2 = vdThreadStartWrite(pDisk);
9324 AssertRC(rc2);
9325
9326 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
9327
9328 AssertMsgBreakStmt(pDisk->pLast->uOpenFlags & VD_OPEN_FLAGS_DISCARD,
9329 ("Discarding not supported\n"),
9330 rc = VERR_NOT_SUPPORTED);
9331
9332 VDIOCTX IoCtx;
9333 RTSEMEVENT hEventComplete = NIL_RTSEMEVENT;
9334
9335 rc = RTSemEventCreate(&hEventComplete);
9336 if (RT_FAILURE(rc))
9337 break;
9338
9339 vdIoCtxDiscardInit(&IoCtx, pDisk, paRanges, cRanges,
9340 vdIoCtxSyncComplete, pDisk, hEventComplete, NULL,
9341 vdDiscardHelperAsync, VDIOCTX_FLAGS_SYNC | VDIOCTX_FLAGS_DONT_FREE);
9342 rc = vdIoCtxProcessSync(&IoCtx, hEventComplete);
9343
9344 RTSemEventDestroy(hEventComplete);
9345 } while (0);
9346
9347 rc2 = vdThreadFinishWrite(pDisk);
9348 AssertRC(rc2);
9349
9350 LogFlowFunc(("returns %Rrc\n", rc));
9351 return rc;
9352}
9353
9354
9355VBOXDDU_DECL(int) VDAsyncRead(PVDISK pDisk, uint64_t uOffset, size_t cbRead,
9356 PCRTSGBUF pSgBuf,
9357 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
9358 void *pvUser1, void *pvUser2)
9359{
9360 int rc = VERR_VD_BLOCK_FREE;
9361 int rc2;
9362 PVDIOCTX pIoCtx = NULL;
9363
9364 LogFlowFunc(("pDisk=%#p uOffset=%llu pSgBuf=%#p cbRead=%zu pvUser1=%#p pvUser2=%#p\n",
9365 pDisk, uOffset, pSgBuf, cbRead, pvUser1, pvUser2));
9366
9367 /* sanity check */
9368 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
9369 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9370
9371 /* Check arguments. */
9372 AssertReturn(cbRead > 0, VERR_INVALID_PARAMETER);
9373 AssertPtrReturn(pSgBuf, VERR_INVALID_POINTER);
9374
9375 do
9376 {
9377 rc2 = vdThreadStartRead(pDisk);
9378 AssertRC(rc2);
9379
9380 AssertMsgBreakStmt( uOffset < pDisk->cbSize
9381 && cbRead <= pDisk->cbSize - uOffset,
9382 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
9383 uOffset, cbRead, pDisk->cbSize),
9384 rc = VERR_INVALID_PARAMETER);
9385 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
9386
9387 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_READ, uOffset,
9388 cbRead, pDisk->pLast, pSgBuf,
9389 pfnComplete, pvUser1, pvUser2,
9390 NULL, vdReadHelperAsync,
9391 VDIOCTX_FLAGS_ZERO_FREE_BLOCKS);
9392 if (!pIoCtx)
9393 {
9394 rc = VERR_NO_MEMORY;
9395 break;
9396 }
9397
9398 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
9399 if (rc == VINF_VD_ASYNC_IO_FINISHED)
9400 {
9401 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
9402 vdIoCtxFree(pDisk, pIoCtx);
9403 else
9404 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
9405 }
9406 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
9407 vdIoCtxFree(pDisk, pIoCtx);
9408
9409 } while (0);
9410
9411 if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
9412 {
9413 rc2 = vdThreadFinishRead(pDisk);
9414 AssertRC(rc2);
9415 }
9416
9417 LogFlowFunc(("returns %Rrc\n", rc));
9418 return rc;
9419}
9420
9421
9422VBOXDDU_DECL(int) VDAsyncWrite(PVDISK pDisk, uint64_t uOffset, size_t cbWrite,
9423 PCRTSGBUF pSgBuf,
9424 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
9425 void *pvUser1, void *pvUser2)
9426{
9427 int rc;
9428 int rc2;
9429 PVDIOCTX pIoCtx = NULL;
9430
9431 LogFlowFunc(("pDisk=%#p uOffset=%llu pSgBuf=%#p cbWrite=%zu pvUser1=%#p pvUser2=%#p\n",
9432 pDisk, uOffset, pSgBuf, cbWrite, pvUser1, pvUser2));
9433 /* sanity check */
9434 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
9435 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9436
9437 /* Check arguments. */
9438 AssertReturn(cbWrite > 0, VERR_INVALID_PARAMETER);
9439 AssertPtrReturn(pSgBuf, VERR_INVALID_POINTER);
9440
9441 do
9442 {
9443 rc2 = vdThreadStartWrite(pDisk);
9444 AssertRC(rc2);
9445
9446 AssertMsgBreakStmt( uOffset < pDisk->cbSize
9447 && cbWrite <= pDisk->cbSize - uOffset,
9448 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
9449 uOffset, cbWrite, pDisk->cbSize),
9450 rc = VERR_INVALID_PARAMETER);
9451 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
9452
9453 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_WRITE, uOffset,
9454 cbWrite, pDisk->pLast, pSgBuf,
9455 pfnComplete, pvUser1, pvUser2,
9456 NULL, vdWriteHelperAsync,
9457 VDIOCTX_FLAGS_DEFAULT);
9458 if (!pIoCtx)
9459 {
9460 rc = VERR_NO_MEMORY;
9461 break;
9462 }
9463
9464 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
9465 if (rc == VINF_VD_ASYNC_IO_FINISHED)
9466 {
9467 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
9468 vdIoCtxFree(pDisk, pIoCtx);
9469 else
9470 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
9471 }
9472 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
9473 vdIoCtxFree(pDisk, pIoCtx);
9474 } while (0);
9475
9476 if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
9477 {
9478 rc2 = vdThreadFinishWrite(pDisk);
9479 AssertRC(rc2);
9480 }
9481
9482 LogFlowFunc(("returns %Rrc\n", rc));
9483 return rc;
9484}
9485
9486
9487VBOXDDU_DECL(int) VDAsyncFlush(PVDISK pDisk, PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
9488 void *pvUser1, void *pvUser2)
9489{
9490 int rc;
9491 int rc2;
9492 PVDIOCTX pIoCtx = NULL;
9493
9494 LogFlowFunc(("pDisk=%#p\n", pDisk));
9495 /* sanity check */
9496 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
9497 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9498
9499 do
9500 {
9501 rc2 = vdThreadStartWrite(pDisk);
9502 AssertRC(rc2);
9503
9504 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
9505
9506 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_FLUSH, 0,
9507 0, pDisk->pLast, NULL,
9508 pfnComplete, pvUser1, pvUser2,
9509 NULL, vdFlushHelperAsync,
9510 VDIOCTX_FLAGS_DEFAULT);
9511 if (!pIoCtx)
9512 {
9513 rc = VERR_NO_MEMORY;
9514 break;
9515 }
9516
9517 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
9518 if (rc == VINF_VD_ASYNC_IO_FINISHED)
9519 {
9520 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
9521 vdIoCtxFree(pDisk, pIoCtx);
9522 else
9523 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
9524 }
9525 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
9526 vdIoCtxFree(pDisk, pIoCtx);
9527 } while (0);
9528
9529 if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
9530 {
9531 rc2 = vdThreadFinishWrite(pDisk);
9532 AssertRC(rc2);
9533 }
9534
9535 LogFlowFunc(("returns %Rrc\n", rc));
9536 return rc;
9537}
9538
9539VBOXDDU_DECL(int) VDAsyncDiscardRanges(PVDISK pDisk, PCRTRANGE paRanges, unsigned cRanges,
9540 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
9541 void *pvUser1, void *pvUser2)
9542{
9543 int rc;
9544 int rc2;
9545 PVDIOCTX pIoCtx = NULL;
9546
9547 LogFlowFunc(("pDisk=%#p\n", pDisk));
9548 /* sanity check */
9549 AssertPtrReturn(pDisk, VERR_INVALID_POINTER);
9550 AssertMsg(pDisk->u32Signature == VDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9551
9552 do
9553 {
9554 rc2 = vdThreadStartWrite(pDisk);
9555 AssertRC(rc2);
9556
9557 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
9558
9559 pIoCtx = vdIoCtxDiscardAlloc(pDisk, paRanges, cRanges,
9560 pfnComplete, pvUser1, pvUser2, NULL,
9561 vdDiscardHelperAsync,
9562 VDIOCTX_FLAGS_DEFAULT);
9563 if (!pIoCtx)
9564 {
9565 rc = VERR_NO_MEMORY;
9566 break;
9567 }
9568
9569 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
9570 if (rc == VINF_VD_ASYNC_IO_FINISHED)
9571 {
9572 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
9573 vdIoCtxFree(pDisk, pIoCtx);
9574 else
9575 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
9576 }
9577 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
9578 vdIoCtxFree(pDisk, pIoCtx);
9579 } while (0);
9580
9581 if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
9582 {
9583 rc2 = vdThreadFinishWrite(pDisk);
9584 AssertRC(rc2);
9585 }
9586
9587 LogFlowFunc(("returns %Rrc\n", rc));
9588 return rc;
9589}
9590
9591VBOXDDU_DECL(int) VDRepair(PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
9592 const char *pszFilename, const char *pszBackend,
9593 uint32_t fFlags)
9594{
9595 int rc = VERR_NOT_SUPPORTED;
9596 PCVDIMAGEBACKEND pBackend = NULL;
9597 VDINTERFACEIOINT VDIfIoInt;
9598 VDINTERFACEIO VDIfIoFallback;
9599 PVDINTERFACEIO pInterfaceIo;
9600
9601 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
9602 /* Check arguments. */
9603 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
9604 AssertReturn(*pszFilename != '\0', VERR_INVALID_PARAMETER);
9605 AssertPtrReturn(pszBackend, VERR_INVALID_POINTER);
9606 AssertMsgReturn((fFlags & ~VD_REPAIR_FLAGS_MASK) == 0, ("fFlags=%#x\n", fFlags),
9607 VERR_INVALID_PARAMETER);
9608
9609 pInterfaceIo = VDIfIoGet(pVDIfsImage);
9610 if (!pInterfaceIo)
9611 {
9612 /*
9613 * Caller doesn't provide an I/O interface, create our own using the
9614 * native file API.
9615 */
9616 vdIfIoFallbackCallbacksSetup(&VDIfIoFallback);
9617 pInterfaceIo = &VDIfIoFallback;
9618 }
9619
9620 /* Set up the internal I/O interface. */
9621 AssertReturn(!VDIfIoIntGet(pVDIfsImage), VERR_INVALID_PARAMETER);
9622 VDIfIoInt.pfnOpen = vdIOIntOpenLimited;
9623 VDIfIoInt.pfnClose = vdIOIntCloseLimited;
9624 VDIfIoInt.pfnDelete = vdIOIntDeleteLimited;
9625 VDIfIoInt.pfnMove = vdIOIntMoveLimited;
9626 VDIfIoInt.pfnGetFreeSpace = vdIOIntGetFreeSpaceLimited;
9627 VDIfIoInt.pfnGetModificationTime = vdIOIntGetModificationTimeLimited;
9628 VDIfIoInt.pfnGetSize = vdIOIntGetSizeLimited;
9629 VDIfIoInt.pfnSetSize = vdIOIntSetSizeLimited;
9630 VDIfIoInt.pfnReadUser = vdIOIntReadUserLimited;
9631 VDIfIoInt.pfnWriteUser = vdIOIntWriteUserLimited;
9632 VDIfIoInt.pfnReadMeta = vdIOIntReadMetaLimited;
9633 VDIfIoInt.pfnWriteMeta = vdIOIntWriteMetaLimited;
9634 VDIfIoInt.pfnFlush = vdIOIntFlushLimited;
9635 rc = VDInterfaceAdd(&VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
9636 pInterfaceIo, sizeof(VDINTERFACEIOINT), &pVDIfsImage);
9637 AssertRC(rc);
9638
9639 rc = vdFindImageBackend(pszBackend, &pBackend);
9640 if (RT_SUCCESS(rc))
9641 {
9642 if (pBackend->pfnRepair)
9643 rc = pBackend->pfnRepair(pszFilename, pVDIfsDisk, pVDIfsImage, fFlags);
9644 else
9645 rc = VERR_VD_IMAGE_REPAIR_NOT_SUPPORTED;
9646 }
9647
9648 LogFlowFunc(("returns %Rrc\n", rc));
9649 return rc;
9650}
9651
9652
9653/*
9654 * generic plugin functions
9655 */
9656
9657/**
9658 * @interface_method_impl{VDIMAGEBACKEND,pfnComposeLocation}
9659 */
9660DECLCALLBACK(int) genericFileComposeLocation(PVDINTERFACE pConfig, char **pszLocation)
9661{
9662 RT_NOREF1(pConfig);
9663 *pszLocation = NULL;
9664 return VINF_SUCCESS;
9665}
9666
9667/**
9668 * @interface_method_impl{VDIMAGEBACKEND,pfnComposeName}
9669 */
9670DECLCALLBACK(int) genericFileComposeName(PVDINTERFACE pConfig, char **pszName)
9671{
9672 RT_NOREF1(pConfig);
9673 *pszName = NULL;
9674 return VINF_SUCCESS;
9675}
9676
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