VirtualBox

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

Last change on this file since 96445 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

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