VirtualBox

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

Last change on this file since 98102 was 97494, checked in by vboxsync, 2 years ago

Storage/VD: Fall back to synchronous I/O even for async requests if the async I/O interface is not available. Fixes virtio-scsi failing with the host cache enabled because it will submit concurrent requests from different threads which means some of the synchronous requests get converted to asynchronous requests due to disk lock contention, ticketref:19717

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette