VirtualBox

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

Last change on this file since 106129 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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