VirtualBox

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

Last change on this file since 74795 was 73097, checked in by vboxsync, 6 years ago

*: Made RT_UOFFSETOF, RT_OFFSETOF, RT_UOFFSETOF_ADD and RT_OFFSETOF_ADD work like builtin_offsetof() and require compile time resolvable requests, adding RT_UOFFSETOF_DYN for the dynamic questions that can only be answered at runtime.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use